mirror of
https://gitlab.com/embeddable-common-lisp/ecl.git
synced 2026-01-16 14:21:48 -08:00
New console streams for Windows.
This commit is contained in:
parent
c7953cc0f5
commit
b3e6bf772a
5 changed files with 239 additions and 35 deletions
|
|
@ -50,6 +50,15 @@ ECL 12.2.2:
|
|||
customize how these interrupts are caught. SET-INTERRUPT-HANDLER runs an
|
||||
implicit EXT:CATCH-SIGNAL.
|
||||
|
||||
* Windows:
|
||||
|
||||
- ECL guesses whether the input / output / error streams are consoles. If
|
||||
so, it sets up a special type of stream that copes with the deficiencies
|
||||
of read()/write() and similar functions on consoles -- namely that they
|
||||
may read or write a larger number of bytes than demanded because they
|
||||
translate the input/output to and from the corresponding codepage.
|
||||
(EXPERIMENTAL)
|
||||
|
||||
* Metaobject protocol:
|
||||
|
||||
- Implemented CLOS:COMPUTE-APPLICABLE-METHODS-USING-CLASSES.
|
||||
|
|
|
|||
|
|
@ -124,7 +124,8 @@ static cl_object si_simple_toplevel ()
|
|||
sentence = @read(3, Cnil, Cnil, OBJNULL);
|
||||
if (sentence == OBJNULL)
|
||||
@(return);
|
||||
ecl_prin1(si_eval_with_env(1, sentence), output);
|
||||
sentence = si_eval_with_env(1, sentence);
|
||||
ecl_prin1(sentence, output);
|
||||
}
|
||||
} CL_CATCH_ALL_END;
|
||||
}
|
||||
|
|
|
|||
248
src/c/file.d
248
src/c/file.d
|
|
@ -48,6 +48,7 @@
|
|||
# include <unistd.h>
|
||||
#elif defined(ECL_MS_WINDOWS_HOST)
|
||||
# include <winsock.h>
|
||||
# include <windows.h>
|
||||
# include <sys/stat.h>
|
||||
# define STDIN_FILENO 0
|
||||
# define STDOUT_FILENO 1
|
||||
|
|
@ -550,21 +551,21 @@ eformat_unread_char(cl_object strm, ecl_character c)
|
|||
unread_twice(strm);
|
||||
}
|
||||
{
|
||||
cl_object l = Cnil;
|
||||
unsigned char buffer[2*ENCODING_BUFFER_MAX_SIZE];
|
||||
int ndx = 0;
|
||||
cl_object l = strm->stream.byte_stack;
|
||||
cl_fixnum i = strm->stream.last_code[0];
|
||||
if (i != EOF) {
|
||||
ndx += strm->stream.encoder(strm, buffer, i);
|
||||
}
|
||||
i = strm->stream.last_code[1];
|
||||
if (i != EOF) {
|
||||
ndx += strm->stream.encoder(strm, buffer, i);
|
||||
ndx += strm->stream.encoder(strm, buffer+ndx, i);
|
||||
}
|
||||
while (ndx != 0) {
|
||||
l = CONS(MAKE_FIXNUM(buffer[--ndx]), l);
|
||||
}
|
||||
strm->stream.byte_stack = ecl_nconc(strm->stream.byte_stack, l);
|
||||
strm->stream.byte_stack = l;
|
||||
strm->stream.last_char = EOF;
|
||||
}
|
||||
}
|
||||
|
|
@ -573,6 +574,8 @@ static ecl_character
|
|||
eformat_read_char(cl_object strm)
|
||||
{
|
||||
ecl_character c = strm->stream.decoder(strm);
|
||||
if (c == strm->stream.eof_char)
|
||||
return EOF;
|
||||
if (c != EOF) {
|
||||
strm->stream.last_char = c;
|
||||
strm->stream.last_code[0] = c;
|
||||
|
|
@ -626,6 +629,7 @@ eformat_read_char_crlf(cl_object strm)
|
|||
if (c == ECL_CHAR_CODE_RETURN) {
|
||||
c = eformat_read_char(strm);
|
||||
if (c == ECL_CHAR_CODE_LINEFEED) {
|
||||
strm->stream.last_code[0] = ECL_CHAR_CODE_RETURN;
|
||||
strm->stream.last_code[1] = c;
|
||||
c = ECL_CHAR_CODE_NEWLINE;
|
||||
} else {
|
||||
|
|
@ -1442,6 +1446,7 @@ si_make_string_output_stream_from_string(cl_object s)
|
|||
strm->stream.byte_size = 32;
|
||||
}
|
||||
#endif
|
||||
strm->stream.eof_char = EOF;
|
||||
@(return strm)
|
||||
}
|
||||
|
||||
|
|
@ -2618,17 +2623,17 @@ safe_fclose(FILE *stream)
|
|||
static cl_index
|
||||
consume_byte_stack(cl_object strm, unsigned char *c, cl_index n)
|
||||
{
|
||||
cl_object l = strm->stream.byte_stack;
|
||||
cl_index out = 0;
|
||||
do {
|
||||
*c = fix(ECL_CONS_CAR(l));
|
||||
l = ECL_CONS_CDR(l);
|
||||
while (n) {
|
||||
cl_object l = strm->stream.byte_stack;
|
||||
if (l == Cnil)
|
||||
return out + strm->stream.ops->read_byte8(strm, c, n);
|
||||
*(c++) = fix(ECL_CONS_CAR(l));
|
||||
out++;
|
||||
c++;
|
||||
n--;
|
||||
} while (l != Cnil);
|
||||
strm->stream.byte_stack = Cnil;
|
||||
return out + strm->stream.ops->read_byte8(strm, c, n);
|
||||
strm->stream.byte_stack = l = ECL_CONS_CDR(l);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
static cl_index
|
||||
|
|
@ -2701,6 +2706,17 @@ io_file_listen(cl_object strm)
|
|||
return file_listen(IO_FILE_DESCRIPTOR(strm));
|
||||
}
|
||||
|
||||
#if defined(ECL_MS_WINDOWS_HOST)
|
||||
static int
|
||||
isaconsole(int i)
|
||||
{
|
||||
HANDLE h = (HANDLE)_get_osfhandle(i);
|
||||
DWORD mode;
|
||||
return !!GetConsoleMode(h, &mode);
|
||||
}
|
||||
#define isatty isaconsole
|
||||
#endif
|
||||
|
||||
static void
|
||||
io_file_clear_input(cl_object strm)
|
||||
{
|
||||
|
|
@ -3231,6 +3247,7 @@ si_stream_external_format_set(cl_object stream, cl_object format)
|
|||
case smm_input_wsock:
|
||||
case smm_output_wsock:
|
||||
case smm_io_wsock:
|
||||
case smm_io_wcon:
|
||||
#endif
|
||||
{
|
||||
cl_object elt_type = ecl_stream_element_type(stream);
|
||||
|
|
@ -3617,9 +3634,7 @@ const struct ecl_file_ops input_stream_ops = {
|
|||
static cl_index
|
||||
winsock_stream_read_byte8(cl_object strm, unsigned char *c, cl_index n)
|
||||
{
|
||||
cl_index out = 0;
|
||||
cl_index len = 0;
|
||||
cl_object l;
|
||||
|
||||
unlikely_if (strm->stream.byte_stack != Cnil) {
|
||||
return consume_byte_stack(strm, c, n);
|
||||
|
|
@ -3637,9 +3652,7 @@ winsock_stream_read_byte8(cl_object strm, unsigned char *c, cl_index n)
|
|||
ecl_enable_interrupts();
|
||||
}
|
||||
}
|
||||
return (out > 0)
|
||||
? (out + (len > 0 ? len : 0))
|
||||
: (len > 0) ? len : EOF;
|
||||
return (len > 0) ? len : EOF;
|
||||
}
|
||||
|
||||
static cl_index
|
||||
|
|
@ -3827,6 +3840,175 @@ const struct ecl_file_ops winsock_stream_input_ops = {
|
|||
};
|
||||
#endif
|
||||
|
||||
/**********************************************************************
|
||||
* WINCONSOLE STREAM
|
||||
*/
|
||||
|
||||
#if defined(ECL_MS_WINDOWS_HOST)
|
||||
|
||||
#define wcon_stream_element_type io_file_element_type
|
||||
|
||||
static cl_index
|
||||
wcon_stream_read_byte8(cl_object strm, unsigned char *c, cl_index n)
|
||||
{
|
||||
unlikely_if (strm->stream.byte_stack != Cnil) {
|
||||
return consume_byte_stack(strm, c, n);
|
||||
} else {
|
||||
cl_index len = 0;
|
||||
cl_env_ptr the_env = ecl_process_env();
|
||||
HANDLE h = (HANDLE)IO_FILE_DESCRIPTOR(strm);
|
||||
DWORD nchars;
|
||||
unsigned char aux[4];
|
||||
for (len = 0; len < n; ) {
|
||||
int i, ok;
|
||||
ecl_disable_interrupts_env(the_env);
|
||||
ok = ReadConsole(h, &aux, 1, &nchars, NULL);
|
||||
ecl_enable_interrupts_env(the_env);
|
||||
unlikely_if (!ok) {
|
||||
FEwin32_error("Cannot read from console", 0);
|
||||
}
|
||||
for (i = 0; i < nchars; i++) {
|
||||
if (len < n) {
|
||||
c[len++] = aux[i];
|
||||
} else {
|
||||
strm->stream.byte_stack =
|
||||
ecl_nconc(strm->stream.byte_stack,
|
||||
ecl_list1(MAKE_FIXNUM(aux[i])));
|
||||
}
|
||||
}
|
||||
}
|
||||
return (len > 0) ? len : EOF;
|
||||
}
|
||||
}
|
||||
|
||||
static cl_index
|
||||
wcon_stream_write_byte8(cl_object strm, unsigned char *c, cl_index n)
|
||||
{
|
||||
HANDLE h = (HANDLE)IO_FILE_DESCRIPTOR(strm);
|
||||
DWORD nchars;
|
||||
unlikely_if(!WriteConsole(h, c, n, &nchars, NULL)) {
|
||||
FEwin32_error("Cannot write to console.", 0);
|
||||
}
|
||||
return nchars;
|
||||
}
|
||||
|
||||
static int
|
||||
wcon_stream_listen(cl_object strm)
|
||||
{
|
||||
HANDLE h = (HANDLE)IO_FILE_DESCRIPTOR(strm);
|
||||
INPUT_RECORD aux;
|
||||
DWORD nevents;
|
||||
do {
|
||||
unlikely_if(!PeekConsoleInput(h, &aux, 1, &nevents))
|
||||
FEwin32_error("Cannot read from console.", 0);
|
||||
if (nevents == 0)
|
||||
return 0;
|
||||
if (aux.EventType == KEY_EVENT)
|
||||
return 1;
|
||||
unlikely_if(!ReadConsoleInput(h, &aux, 1, &nevents))
|
||||
FEwin32_error("Cannot read from console.", 0);
|
||||
} while (1);
|
||||
}
|
||||
|
||||
static void
|
||||
wcon_stream_clear_input(cl_object strm)
|
||||
{
|
||||
FlushConsoleInputBuffer((HANDLE)IO_FILE_DESCRIPTOR(strm));
|
||||
}
|
||||
|
||||
static void
|
||||
wcon_stream_force_output(cl_object strm)
|
||||
{
|
||||
DWORD nchars;
|
||||
WriteConsole((HANDLE)IO_FILE_DESCRIPTOR(strm), 0, 0, &nchars, NULL);
|
||||
}
|
||||
|
||||
const struct ecl_file_ops wcon_stream_io_ops = {
|
||||
wcon_stream_write_byte8,
|
||||
wcon_stream_read_byte8,
|
||||
|
||||
generic_write_byte,
|
||||
generic_read_byte,
|
||||
|
||||
eformat_read_char,
|
||||
eformat_write_char,
|
||||
eformat_unread_char,
|
||||
generic_peek_char,
|
||||
|
||||
generic_read_vector,
|
||||
generic_write_vector,
|
||||
|
||||
wcon_stream_listen,
|
||||
wcon_stream_clear_input,
|
||||
generic_void,
|
||||
wcon_stream_force_output,
|
||||
wcon_stream_force_output,
|
||||
|
||||
generic_always_true, /* input_p */
|
||||
generic_always_true, /* output_p */
|
||||
generic_always_false,
|
||||
wcon_stream_element_type,
|
||||
|
||||
not_a_file_stream,
|
||||
not_implemented_get_position,
|
||||
not_implemented_set_position,
|
||||
generic_column,
|
||||
|
||||
generic_close,
|
||||
};
|
||||
|
||||
#define CONTROL_Z 26
|
||||
|
||||
static cl_object
|
||||
maybe_make_windows_console_FILE(cl_object fname, FILE *f, enum ecl_smmode smm,
|
||||
cl_fixnum byte_size, int flags,
|
||||
cl_object external_format)
|
||||
{
|
||||
int desc = fileno(f);
|
||||
cl_object output;
|
||||
if (isatty(desc)) {
|
||||
output = ecl_make_stream_from_FILE
|
||||
(fname,
|
||||
(void*)_get_osfhandle(desc),
|
||||
smm_io_wcon,
|
||||
byte_size, flags,
|
||||
external_format);
|
||||
output->stream.eof_char = CONTROL_Z;
|
||||
} else {
|
||||
output = ecl_make_stream_from_FILE
|
||||
(fname, f, smm, byte_size, flags,
|
||||
external_format);
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
static cl_object
|
||||
maybe_make_windows_console_fd(cl_object fname, int desc, enum ecl_smmode smm,
|
||||
cl_fixnum byte_size, int flags,
|
||||
cl_object external_format)
|
||||
{
|
||||
cl_object output;
|
||||
if (isatty(desc)) {
|
||||
output = ecl_make_stream_from_FILE
|
||||
(fname,
|
||||
(void*)_get_osfhandle(desc),
|
||||
smm_io_wcon,
|
||||
byte_size, flags,
|
||||
external_format);
|
||||
output->stream.eof_char = CONTROL_Z;
|
||||
} else {
|
||||
output = ecl_make_stream_from_fd
|
||||
(fname, desc, smm,
|
||||
byte_size, flags,
|
||||
external_format);
|
||||
}
|
||||
return output;
|
||||
}
|
||||
#else
|
||||
#define maybe_make_windows_console_FILE ecl_make_stream_from_file
|
||||
#define maybe_make_windows_console_fd ecl_make_file_stream_from_fd
|
||||
#endif
|
||||
|
||||
cl_object
|
||||
si_set_buffering_mode(cl_object stream, cl_object buffer_mode_symbol)
|
||||
{
|
||||
|
|
@ -3889,6 +4071,9 @@ ecl_make_stream_from_FILE(cl_object fname, void *f, enum ecl_smmode smm,
|
|||
case smm_io_wsock:
|
||||
stream->stream.ops = duplicate_dispatch_table(&winsock_stream_io_ops);
|
||||
break;
|
||||
case smm_io_wcon:
|
||||
stream->stream.ops = duplicate_dispatch_table(&wcon_stream_io_ops);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
FEerror("Not a valid mode ~D for ecl_make_stream_from_FILE", 1, MAKE_FIXNUM(smm));
|
||||
|
|
@ -3922,13 +4107,14 @@ ecl_make_stream_from_fd(cl_object fname, int fd, enum ecl_smmode smm,
|
|||
case smm_input_wsock:
|
||||
case smm_output_wsock:
|
||||
case smm_io_wsock:
|
||||
case smm_io_wcon:
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
FEerror("make_stream: wrong mode", 0);
|
||||
}
|
||||
#if defined(ECL_WSOCK)
|
||||
if (smm == smm_input_wsock || smm == smm_output_wsock || smm == smm_io_wsock)
|
||||
if (smm == smm_input_wsock || smm == smm_output_wsock || smm == smm_io_wsock || smm == smm_io_wcon)
|
||||
fp = (FILE*)fd;
|
||||
else
|
||||
fp = safe_fdopen(fd, mode);
|
||||
|
|
@ -5389,22 +5575,22 @@ init_file(void)
|
|||
/* We choose C streams by default only when _not_ using threads.
|
||||
* The reason is that C streams block on I/O operations. */
|
||||
#if !defined(ECL_THREADS)
|
||||
standard_input = ecl_make_stream_from_FILE(make_constant_base_string("stdin"),
|
||||
stdin, smm_input, 8, flags, external_format);
|
||||
standard_output = ecl_make_stream_from_FILE(make_constant_base_string("stdout"),
|
||||
stdout, smm_output, 8, flags, external_format);
|
||||
error_output = ecl_make_stream_from_FILE(make_constant_base_string("stderr"),
|
||||
stderr, smm_output, 8, flags, external_format);
|
||||
standard_input = maybe_make_windows_console_FILE(make_constant_base_string("stdin"),
|
||||
stdin, smm_input, 8, flags, external_format);
|
||||
standard_output = maybe_make_windows_console_FILE(make_constant_base_string("stdout"),
|
||||
stdout, smm_output, 8, flags, external_format);
|
||||
error_output = maybe_make_windows_console_FILE(make_constant_base_string("stderr"),
|
||||
stderr, smm_output, 8, flags, external_format);
|
||||
#else
|
||||
standard_input = ecl_make_file_stream_from_fd(make_constant_base_string("stdin"),
|
||||
STDIN_FILENO, smm_input_file, 8, flags,
|
||||
external_format);
|
||||
standard_output = ecl_make_file_stream_from_fd(make_constant_base_string("stdout"),
|
||||
STDOUT_FILENO, smm_output_file, 8, flags,
|
||||
standard_input = maybe_make_windows_console_fd(make_constant_base_string("stdin"),
|
||||
STDIN_FILENO, smm_input_file, 8, flags,
|
||||
external_format);
|
||||
error_output = ecl_make_file_stream_from_fd(make_constant_base_string("stderr"),
|
||||
STDERR_FILENO, smm_output_file, 8, flags,
|
||||
external_format);
|
||||
standard_output = maybe_make_windows_console_fd(make_constant_base_string("stdout"),
|
||||
STDOUT_FILENO, smm_output_file, 8, flags,
|
||||
external_format);
|
||||
error_output = maybe_make_windows_console_fd(make_constant_base_string("stderr"),
|
||||
STDERR_FILENO, smm_output_file, 8, flags,
|
||||
external_format);
|
||||
#endif
|
||||
cl_core.standard_input = standard_input;
|
||||
ECL_SET(@'ext::+process-standard-input+', standard_input);
|
||||
|
|
|
|||
|
|
@ -217,6 +217,10 @@ write_stream(cl_object x, cl_object stream)
|
|||
prefix = "closed i/o win32 socket stream";
|
||||
tag = IO_STREAM_FILENAME(x);
|
||||
break;
|
||||
case smm_io_wcon:
|
||||
prefix = "closed i/o win32 console stream";
|
||||
tag = IO_STREAM_FILENAME(x);
|
||||
break;
|
||||
#endif
|
||||
case smm_io_file:
|
||||
prefix = "closed io file";
|
||||
|
|
|
|||
|
|
@ -543,9 +543,12 @@ enum ecl_smmode { /* stream mode */
|
|||
smm_string_output, /* string output */
|
||||
smm_probe, /* probe (only used in open_stream()) */
|
||||
#if defined(ECL_WSOCK)
|
||||
smm_input_wsock, /* input socket (Win32) */
|
||||
smm_output_wsock, /* output socket (Win32) */
|
||||
smm_io_wsock, /* input/output socket (Win32) */
|
||||
smm_input_wsock, /* input socket (Win32) */
|
||||
smm_output_wsock, /* output socket (Win32) */
|
||||
smm_io_wsock, /* input/output socket (Win32) */
|
||||
#endif
|
||||
#if defined(ECL_MS_WINDOWS_HOST)
|
||||
smm_io_wcon, /* windows console (Win32) */
|
||||
#endif
|
||||
smm_sequence_input, /* sequence input */
|
||||
smm_sequence_output /* sequence output */
|
||||
|
|
@ -645,6 +648,7 @@ struct ecl_stream {
|
|||
cl_eformat_decoder decoder;
|
||||
cl_object format_table;
|
||||
int flags; /* character table, flags, etc */
|
||||
ecl_character eof_char;
|
||||
};
|
||||
|
||||
struct ecl_random {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue