/* print.c Copyright (C) 1990-2007 Codemist Ltd */ /* * Printing, plus some file-related operations. */ /* * This code may be used and modified, and redistributed in binary * or source form, subject to the "CCL Public License", which should * accompany it. This license is a variant on the BSD license, and thus * permits use of code derived from this in either open and commercial * projects: but it does require that updates to this code be made * available back to the originators of the package. * Before merging other code in with this or linking this code * with other packages or libraries please check that the license terms * of the other material are compatible with those of this. */ /* Signature: 50be624f 12-Apr-2008 */ #include "headers.h" #ifdef COMMON #include "clsyms.h" #endif #ifdef SOCKETS #include "sockhdr.h" #endif FILE *spool_file = NULL; char spool_file_name[32]; int32_t terminal_column = 0; int32_t terminal_line_length = (int32_t)0x80000000; #ifdef HAVE_FWIN #define default_terminal_line_length fwin_linelength #else #define default_terminal_line_length 80 #endif /* * The next line is a clue to the unsafe nature of a Standard C library! * I want to implement "printf-like" functions of my own, but need to * process the characters others than via a normal (FILE *) object. So I * use vsprintf etc to place stuff in a buffer from where I can pass it on. * however usage such as * my_magic_printf("%s", ...) * can oh so easily generate unbounded amounts of stuff to overflow any * buffer I have. I allow space for VPRINTF_CHUNK chars so demand * discipline of myself in all uses... * * The 1999 C standard introduced vsnprintf and solves this worry! */ #define VPRINTF_CHUNK 256 void ensure_screen() { #ifdef SOCKETS if (socket_server != 0) flush_socket(); #endif #ifdef HAVE_FWIN fwin_ensure_screen(); #else fflush(stdout); #endif if (spool_file != NULL) fflush(spool_file); } void MS_CDECL term_printf(char *fmt, ...) { va_list a; char print_temp[VPRINTF_CHUNK], *p; int n; va_start(a, fmt); n = vsprintf(print_temp, fmt, a); p = print_temp; while (n-- > 0) char_to_terminal(*p++, 0); va_end(a); } void MS_CDECL stdout_printf(char *fmt, ...) { va_list a; char print_temp[VPRINTF_CHUNK], *p; int n; nil_as_base Lisp_Object stream = qvalue(standard_output); if (!is_stream(stream)) stream = qvalue(terminal_io); if (!is_stream(stream)) stream = lisp_terminal_io; va_start(a, fmt); n = vsprintf(print_temp, fmt, a); p = print_temp; while (n-- > 0) putc_stream(*p++, stream); va_end(a); } void MS_CDECL err_printf(char *fmt, ...) { va_list a; char print_temp[VPRINTF_CHUNK], *p; int n; nil_as_base Lisp_Object stream = qvalue(error_output); if (!is_stream(stream)) stream = qvalue(terminal_io); if (!is_stream(stream)) stream = lisp_terminal_io; va_start(a, fmt); n = vsprintf(print_temp, fmt, a); p = print_temp; while (n-- > 0) putc_stream(*p++, stream); va_end(a); } void MS_CDECL debug_printf(char *fmt, ...) { va_list a; char print_temp[VPRINTF_CHUNK], *p; int n; nil_as_base Lisp_Object stream = qvalue(debug_io); if (!is_stream(stream)) stream = qvalue(terminal_io); if (!is_stream(stream)) stream = lisp_terminal_io; va_start(a, fmt); n = vsprintf(print_temp, fmt, a); p = print_temp; while (n-- > 0) putc_stream(*p++, stream); va_end(a); } void MS_CDECL trace_printf(char *fmt, ...) { va_list a; char print_temp[VPRINTF_CHUNK], *p; int n; nil_as_base Lisp_Object stream = qvalue(trace_output); if (!is_stream(stream)) stream = qvalue(terminal_io); if (!is_stream(stream)) stream = lisp_terminal_io; va_start(a, fmt); n = vsprintf(print_temp, fmt, a); p = print_temp; while (n-- > 0) putc_stream(*p++, stream); va_end(a); } Lisp_Object Ltyo(Lisp_Object nil, Lisp_Object a) { /* * Print a character given its character code. NOTE that in earlier * versions of CSL this always printed to the standard output regardless * of what output stream was selected. Such a curious behaviour was * provided for use when magic characters sent to the standard output had * odd behaviour (eg caused graphics effects). Now tyo is a more * sensible function for use across all systems. To be generous it * accepts either a character or a numeric code. */ int c; Lisp_Object stream = qvalue(standard_output); CSL_IGNORE(nil); if (a == CHAR_EOF) return onevalue(a); else if (is_char(a)) c = (int)code_of_char(a); else if (is_fixnum(a)) c = (int)int_of_fixnum(a); else return aerror1("tyo", a); push(a); if (!is_stream(stream)) stream = qvalue(terminal_io); if (!is_stream(stream)) stream = lisp_terminal_io; putc_stream(c, stream); pop(a); errexit(); return onevalue(a); } int char_to_illegal(int c, Lisp_Object f) { Lisp_Object nil = C_nil; CSL_IGNORE(c); CSL_IGNORE(f); if (exception_pending()) return 1; aerror1("Attempt to write to an input stream or one that has been closed", stream_type(f)); return 1; } int char_from_illegal(Lisp_Object f) { Lisp_Object nil = C_nil; CSL_IGNORE(f); if (exception_pending()) return EOF; aerror1("Attempt to read from an output stream or one that has been closed", stream_type(f)); return EOF; } int32_t write_action_illegal(int32_t op, Lisp_Object f) { CSL_IGNORE(f); if (op == WRITE_GET_INFO+WRITE_IS_CONSOLE) return 0; if (op != WRITE_CLOSE) aerror1("Illegal operation on stream", cons_no_gc(fixnum_of_int(op >> 8), stream_type(f))); return 0; } int32_t write_action_file(int32_t op, Lisp_Object f) { int32_t w; switch (op & 0xf0000000) { case WRITE_CLOSE: if (stream_file(f) == NULL) op = 0; else op = fclose(stream_file(f)); set_stream_write_fn(f, char_to_illegal); set_stream_write_other(f, write_action_illegal); set_stream_read_fn(f, char_from_illegal); set_stream_read_other(f, read_action_illegal); set_stream_file(f, NULL); return op; case WRITE_FLUSH: return fflush(stream_file(f)); case WRITE_SET_LINELENGTH_DEFAULT: op = 80; /* drop through */ case WRITE_SET_LINELENGTH: w = stream_line_length(f); stream_line_length(f) = op & 0x07ffffff; return w; case WRITE_SET_COLUMN: w = stream_char_pos(f); stream_char_pos(f) = op & 0x07ffffff; return w; case WRITE_GET_INFO: switch (op & 0xff) { case WRITE_GET_LINE_LENGTH: return stream_line_length(f); case WRITE_GET_COLUMN: return stream_char_pos(f); case WRITE_IS_CONSOLE: return 0; default:return 0; } default: return 0; } } #if defined HAVE_POPEN || defined HAVE_FWIN int32_t write_action_pipe(int32_t op, Lisp_Object f) { int32_t w; if (op < 0) return -1; else switch (op & 0xf0000000) { case WRITE_CLOSE: my_pclose(stream_file(f)); set_stream_write_fn(f, char_to_illegal); set_stream_write_other(f, write_action_illegal); set_stream_file(f, NULL); return 0; case WRITE_FLUSH: return my_pipe_flush(stream_file(f)); case WRITE_SET_LINELENGTH_DEFAULT: op = 80; /* drop through */ case WRITE_SET_LINELENGTH: w = stream_line_length(f); stream_line_length(f) = op & 0x07ffffff; return w; case WRITE_SET_COLUMN: w = stream_char_pos(f); stream_char_pos(f) = op & 0x07ffffff; return w; case WRITE_GET_INFO: switch (op & 0xff) { case WRITE_GET_LINE_LENGTH: return stream_line_length(f); case WRITE_GET_COLUMN: return stream_char_pos(f); case WRITE_IS_CONSOLE: return 0; default:return 0; } default: return 0; } } #else int32_t write_action_pipe(int32_t op, Lisp_Object f) { CSL_IGNORE(op); CSL_IGNORE(f); return -1; } #endif int32_t write_action_terminal(int32_t op, Lisp_Object dummy) { int32_t w; CSL_IGNORE(dummy); if (op < 0) return -1; else switch (op & 0xf0000000) { case WRITE_CLOSE: return 0; /* I will never close the terminal stream */ case WRITE_FLUSH: ensure_screen(); return 0; case WRITE_SET_LINELENGTH_DEFAULT: w = terminal_line_length; terminal_line_length = 0x80000000; return w; case WRITE_SET_LINELENGTH: w = terminal_line_length; terminal_line_length = op & 0x07ffffff; return w; case WRITE_SET_COLUMN: w = terminal_column; terminal_column = op & 0x07ffffff; return w; case WRITE_GET_INFO: switch (op & 0xff) { case WRITE_GET_LINE_LENGTH: w = terminal_line_length; if (w == 0x80000000) w = default_terminal_line_length; return w; case WRITE_GET_COLUMN: return terminal_column; case WRITE_IS_CONSOLE: return 1; default:return 0; } default: return 0; } } #ifdef HAVE_LIBFOX int32_t write_action_math(int32_t op, Lisp_Object dummy) { CSL_IGNORE(dummy); if (op < 0) return -1; else switch (op & 0xf0000000) { case WRITE_CLOSE: return 0; /* I will never close the math stream */ case WRITE_FLUSH: /* not flushed using the normal protocol */ return 0; case WRITE_SET_LINELENGTH_DEFAULT: return 0x07ffffff; /* essentially unlimited linelength */ case WRITE_SET_LINELENGTH: return 0x07ffffff; case WRITE_SET_COLUMN: /* operation not really supported */ return 0; case WRITE_GET_INFO: switch (op & 0xff) { case WRITE_GET_LINE_LENGTH: return 0x07ffffff; case WRITE_GET_COLUMN: return 0; case WRITE_IS_CONSOLE: return 1; default:return 0; } default: return 0; } } int32_t write_action_spool(int32_t op, Lisp_Object dummy) { int32_t w; CSL_IGNORE(dummy); if (op < 0) return -1; else switch (op & 0xf0000000) { case WRITE_CLOSE: return 0; /* I will never close the spool stream this way */ case WRITE_FLUSH: if (spool_file != NULL) fflush(spool_file); return 0; /* * In many respects this behaves just like terminal output. */ case WRITE_SET_LINELENGTH_DEFAULT: w = terminal_line_length; terminal_line_length = 0x80000000; return w; case WRITE_SET_LINELENGTH: w = terminal_line_length; terminal_line_length = op & 0x07ffffff; return w; case WRITE_SET_COLUMN: w = terminal_column; terminal_column = op & 0x07ffffff; return w; case WRITE_GET_INFO: switch (op & 0xff) { case WRITE_GET_LINE_LENGTH: w = terminal_line_length; if (w == 0x80000000) w = default_terminal_line_length; return w; case WRITE_GET_COLUMN: return terminal_column; case WRITE_IS_CONSOLE: return 1; default:return 0; } default: return 0; } } #endif int32_t write_action_list(int32_t op, Lisp_Object f) { int32_t w; if (op < 0) return -1; else switch (op & 0xf0000000) { case WRITE_CLOSE: set_stream_write_fn(f, char_to_illegal); set_stream_write_other(f, write_action_illegal); set_stream_file(f, NULL); return 0; case WRITE_FLUSH: return 0; case WRITE_SET_LINELENGTH_DEFAULT: case WRITE_SET_LINELENGTH: return 0x03ffffff; case WRITE_SET_COLUMN: w = stream_char_pos(f); stream_char_pos(f) = op & 0x07ffffff; return w; case WRITE_GET_INFO: switch (op & 0xff) { case WRITE_GET_LINE_LENGTH: return 0x03ffffff; case WRITE_GET_COLUMN: return stream_char_pos(f); case WRITE_IS_CONSOLE: return 0; default:return 0; } default: return 0; } } Lisp_Object Lstreamp(Lisp_Object nil, Lisp_Object a) { return onevalue(Lispify_predicate(is_stream(a))); } Lisp_Object Lis_console(Lisp_Object nil, Lisp_Object a) { int r1, r2; if (!is_stream(a)) return onevalue(nil); r1 = other_write_action(WRITE_GET_INFO+WRITE_IS_CONSOLE, a); r2 = other_read_action(READ_IS_CONSOLE, a); return onevalue(Lispify_predicate(r1 || r2)); } Lisp_Object make_stream_handle(void) { Lisp_Object w = getvector(TAG_VECTOR, TYPE_STREAM, STREAM_SIZE), nil; errexit(); stream_type(w) = nil; stream_write_data(w) = nil; stream_read_data(w) = nil; set_stream_file(w, 0); set_stream_write_fn(w, char_to_illegal); set_stream_write_other(w, write_action_illegal); stream_line_length(w) = 80; stream_char_pos(w) = 0; set_stream_read_fn(w, char_from_illegal); set_stream_read_other(w, read_action_illegal); stream_pushed_char(w) = NOT_CHAR; return w; } #ifdef COMMON Lisp_Object MS_CDECL Lmake_broadcast_stream_n(Lisp_Object nil, int nargs, ...) { Lisp_Object r = nil, w, w1; va_list a; va_start(a, nargs); push_args(a, nargs); while (nargs > 1) { pop2(w, w1); nargs-=2; r = list2star(w1, w, r); errexitn(nargs); } while (nargs > 0) { pop(w); nargs--; r = cons(w, r); errexitn(nargs); } push(r); w = make_stream_handle(); pop(r); errexit(); set_stream_write_fn(w, char_to_broadcast); set_stream_write_other(w, write_action_broadcast); stream_write_data(w) = r; return onevalue(w); } Lisp_Object Lmake_broadcast_stream_1(Lisp_Object nil, Lisp_Object a) { return Lmake_broadcast_stream_n(nil, 1, a); } Lisp_Object Lmake_broadcast_stream_2(Lisp_Object nil, Lisp_Object a, Lisp_Object b) { return Lmake_broadcast_stream_n(nil, 2, a, b); } Lisp_Object MS_CDECL Lmake_concatenated_stream_n(Lisp_Object nil, int nargs, ...) { Lisp_Object r = nil, w, w1; va_list a; va_start(a, nargs); push_args(a, nargs); while (nargs > 1) { pop2(w, w1); nargs-=2; r = list2star(w1, w, r); errexitn(nargs); } while (nargs > 0) { pop(w); nargs--; r = cons(w, r); errexitn(nargs); } push(r); w = make_stream_handle(); pop(r); errexit(); set_stream_read_fn(w, char_from_concatenated); set_stream_read_other(w, read_action_concatenated); stream_read_data(w) = r; return onevalue(w); } Lisp_Object Lmake_concatenated_stream_1(Lisp_Object nil, Lisp_Object a) { return Lmake_concatenated_stream_n(nil, 1, a); } Lisp_Object Lmake_concatenated_stream_2(Lisp_Object nil, Lisp_Object a, Lisp_Object b) { return Lmake_concatenated_stream_n(nil, 2, a, b); } Lisp_Object Lmake_synonym_stream(Lisp_Object nil, Lisp_Object a) { Lisp_Object w; if (!is_symbol(a)) return aerror1("make-synonym-stream", a); push(a); w = make_stream_handle(); pop(a); errexit(); set_stream_write_fn(w, char_to_synonym); set_stream_write_other(w, write_action_synonym); stream_write_data(w) = a; set_stream_read_fn(w, char_from_synonym); set_stream_read_other(w, read_action_synonym); stream_read_data(w) = a; return onevalue(w); } Lisp_Object Lmake_two_way_stream(Lisp_Object nil, Lisp_Object a, Lisp_Object b) { Lisp_Object w; if (!is_symbol(a)) return aerror1("make-two-way-stream", a); if (!is_symbol(b)) return aerror1("make-two-way-stream", b); push2(a, b); w = make_stream_handle(); pop2(b, a); errexit(); set_stream_write_fn(w, char_to_synonym); set_stream_write_other(w, write_action_synonym); stream_write_data(w) = b; set_stream_read_fn(w, char_from_synonym); set_stream_read_other(w, read_action_synonym); stream_read_data(w) = a; return onevalue(w); } Lisp_Object Lmake_echo_stream(Lisp_Object nil, Lisp_Object a, Lisp_Object b) { Lisp_Object w; if (!is_symbol(a)) return aerror1("make-echo-stream", a); if (!is_symbol(b)) return aerror1("make-echo-stream", b); push2(a, b); w = make_stream_handle(); pop2(b, a); errexit(); set_stream_write_fn(w, char_to_synonym); set_stream_write_other(w, write_action_synonym); stream_write_data(w) = b; set_stream_read_fn(w, char_from_echo); set_stream_read_other(w, read_action_synonym); stream_read_data(w) = a; return onevalue(w); } Lisp_Object MS_CDECL Lmake_string_input_stream_n(Lisp_Object nil, int nargs, ...) { CSL_IGNORE(nil); CSL_IGNORE(nargs); return aerror("make-string-input-stream"); } Lisp_Object Lmake_string_input_stream_1(Lisp_Object nil, Lisp_Object a) { return Lmake_string_input_stream_n(nil, 1, a); } Lisp_Object Lmake_string_input_stream_2(Lisp_Object nil, Lisp_Object a, Lisp_Object b) { return Lmake_string_input_stream_n(nil, 2, a, b); } Lisp_Object MS_CDECL Lmake_string_output_stream(Lisp_Object nil, int nargs, ...) { Lisp_Object w; argcheck(nargs, 0, "make-string-output-stream"); w = make_stream_handle(); errexit(); set_stream_write_fn(w, code_to_list); set_stream_write_other(w, write_action_list); return onevalue(w); } Lisp_Object Lget_output_stream_string(Lisp_Object nil, Lisp_Object a) { Lisp_Object w; int32_t n, k; if (!is_stream(a)) return aerror1("get-output-stream-string", a); w = stream_write_data(a); n = stream_char_pos(a); stream_write_data(a) = nil; stream_char_pos(a) = 0; push(w); a = getvector(TAG_VECTOR, TYPE_STRING, CELL+n); pop(w); errexit(); k = (n + 3) & ~(int32_t)7; *(int32_t *)((char *)a + k + 4 - TAG_VECTOR) = 0; if (k != 0) *(int32_t *)((char *)a + k - TAG_VECTOR) = 0; while (n > 0) { n--; celt(a, n) = int_of_fixnum(qcar(w)); w = qcdr(w); } return a; } #endif /* COMMON */ /* * (make-function-stream 'fn) makes a stream where output just passes * characters to the given function. */ Lisp_Object Lmake_function_stream(Lisp_Object nil, Lisp_Object a) { Lisp_Object w; if (!is_symbol(a)) return aerror1("make-function-stream", a); push(a); w = make_stream_handle(); pop(a); errexit(); set_stream_write_fn(w, char_to_function); set_stream_write_other(w, write_action_list); stream_write_data(w) = a; return onevalue(w); } int char_to_terminal(int c, Lisp_Object dummy) { CSL_IGNORE(dummy); if (c == '\n' || c == '\f') terminal_column = 0; else terminal_column++; if (spool_file != NULL) { putc(c, spool_file); #ifdef DEBUG fflush(spool_file); #endif } if (procedural_output != NULL) return (*procedural_output)(c); #ifdef WINDOW_SYSTEM if (alternative_stdout != NULL) { putc(c, alternative_stdout); return 0; } #endif #ifdef HAVE_FWIN fwin_putchar(c); #else putchar(c); #endif return 0; /* indicate success */ } #ifdef HAVE_LIBFOX static int math_buffer_size, math_buffer_p; static char *math_buffer = NULL; int char_to_math(int c, Lisp_Object stream) { if (math_buffer == NULL) { math_buffer_size = 500; math_buffer = (char *)malloc(math_buffer_size); math_buffer_p = 0; if (math_buffer == NULL) return 1; /* failed */ } if (math_buffer_p == math_buffer_size-1) { math_buffer_size += 500; /* Grow the buffer */ math_buffer = (char *)realloc(math_buffer, math_buffer_size); /* * If I fail to extend the buffer then I will lose some initial part of * my output. Ugh! But (provided the memory situation improves!) things will * correct themselves when I next try to display a smaller expression. */ if (math_buffer == NULL) return 1; } math_buffer[math_buffer_p++] = c; math_buffer[math_buffer_p] = 0; return 0; } int char_to_spool(int c, Lisp_Object stream) { if (spool_file == NULL) return 1; if (c == '\n' || c == '\f') terminal_column = 0; else terminal_column++; putc(c, spool_file); return 0; } #endif int char_to_file(int c, Lisp_Object stream) { if (c == '\n' || c == '\f') stream_char_pos(stream) = 0; else stream_char_pos(stream)++; putc(c, stream_file(stream)); return 0; /* indicate success */ } int char_to_synonym(int c, Lisp_Object f) { f = qvalue(stream_write_data(f)); if (!is_stream(f)) return 1; return putc_stream(c, f); } int char_to_function(int c, Lisp_Object f) { Lisp_Object nil = C_nil; f = stream_write_data(f); /* name of the function to call */ (*qfn1(f))(qenv(f), pack_char(0, 0, c & 0xff)); errexit(); return 0; /* return 0 for success */ } int char_to_broadcast(int c, Lisp_Object f) { Lisp_Object l = stream_write_data(f); int r = 0; Lisp_Object nil = C_nil; while (consp(l)) { f = qcar(l); l = qcdr(l); if (!is_symbol(f)) continue; f = qvalue(f); if (!is_stream(f)) continue; push(l); r = r | putc_stream(c, f); pop(l); errexit(); } return r; } int32_t write_action_synonym(int32_t c, Lisp_Object f) { int r; Lisp_Object f1 = qvalue(stream_write_data(f)); if (!is_stream(f1)) return aerror1("attempt to act on", cons_no_gc(fixnum_of_int(c >> 8), f)); r = other_write_action(c, f1); if (c == WRITE_CLOSE) { set_stream_write_fn(f, char_to_illegal); set_stream_write_other(f, write_action_illegal); set_stream_file(f, NULL); } return r; } int32_t write_action_broadcast(int32_t c, Lisp_Object f) { int r = 0, r1; Lisp_Object l = stream_write_data(f), f1; Lisp_Object nil = C_nil; while (consp(l)) { f1 = qcar(l); l = qcdr(l); if (!is_symbol(f1)) continue; f1 = qvalue(f1); if (!is_stream(f1)) continue; push2(l, f); r1 = other_write_action(c, f1); pop2(f, l); errexit(); if (r == 0) r = r1; } if (c == WRITE_CLOSE) { set_stream_write_fn(f, char_to_illegal); set_stream_write_other(f, write_action_illegal); set_stream_file(f, NULL); } return r; } #if defined HAVE_POPEN || defined HAVE_FWIN int char_to_pipeout(int c, Lisp_Object stream) { if (c == '\n' || c == '\f') stream_char_pos(stream) = 0; else stream_char_pos(stream)++; my_pipe_putc(c, stream_file(stream)); return 0; /* indicate success */ } #else int char_to_pipeout(int c, Lisp_Object stream) { return char_to_illegal(c, stream); } #endif char *get_string_data(Lisp_Object name, char *why, int32_t *len) { Lisp_Object nil = C_nil; Header h; #ifdef COMMON if (complex_stringp(name)) { name = simplify_string(name); nil = C_nil; if (exception_pending()) return NULL; h = vechdr(name); } else #endif if (symbolp(name)) { name = get_pname(name); nil = C_nil; if (exception_pending()) return NULL; h = vechdr(name); } else if (!(is_vector(name))) { aerror1(why, name); return NULL; } else if (type_of_header(h = vechdr(name)) != TYPE_STRING) { aerror1(why, name); return NULL; } *len = length_of_header(h) - CELL; return &celt(name, 0); } static Lisp_Object Lfiledate(Lisp_Object nil, Lisp_Object name) { char filename[LONGEST_LEGAL_FILENAME], tt[32]; int32_t len; char *w = get_string_data(name, "filep", &len); errexit(); if (len >= sizeof(filename)) len = sizeof(filename); if (!file_exists(filename, w, (size_t)len, tt)) return onevalue(nil); tt[24] = 0; name = make_string(tt); errexit(); return onevalue(name); } static Lisp_Object Lfilep(Lisp_Object nil, Lisp_Object name) { name = Lfiledate(nil, name); errexit(); if (name != nil) name = lisp_true; return onevalue(name); } Lisp_Object MS_CDECL Ltmpnam(Lisp_Object nil, int nargs, ...) /* * Returns a string that is suitable for use as the name of a temporary * file. Note that this is generally NOT a comfortable thing to use, * since after tmpnam() has generated the name but before you get around * to doing anything with the file somebody else may do something that * interferes. As a result some C compilers issue a warning when they * see use of tmpnam() at all... Here the potential security issues are * just left for the user to think about! */ { char *s; Lisp_Object r; char tempdir[LONGEST_LEGAL_FILENAME]; #ifdef WIN32 DWORD len = GetTempPath(LONGEST_LEGAL_FILENAME, tempdir); argcheck(nargs, 0, "tmpnam"); if (len <= 0) return onevalue(nil); s = tempnam(tempdir, "CSL_t"); #else argcheck(nargs, 0, "tmpnam"); s = tmpnam(NULL); #endif if (s == NULL) return onevalue(nil); /* Sorry - can't do it */ /* * Ensure that file name only has Lisp-friendly characters in it! * THIS COULD PROBABLY BREAK THE IDEA THAT THE FILE NAME SHOULD BE * UNIQUE! But on the systems I have tried it seems OK! */ strcpy(tempdir, s); s = tempdir; while (*s != 0) s++; while (s != tempdir) { int c = *--s; if (c=='/' || c=='\\') break; if (!is_constituent(c)) *s = '_'; } r = make_string(tempdir); errexit(); return onevalue(r); } static int tmpSerial = 0; static char tempname[LONGEST_LEGAL_FILENAME]; char *CSLtmpnam(char *suffix, int32_t suffixlen) { time_t t0 = time(NULL); clock_t c0 = clock(); unsigned long taskid; char fname[LONGEST_LEGAL_FILENAME]; char tt[32]; char *s; #ifdef WIN32 DWORD len = GetTempPath(LONGEST_LEGAL_FILENAME, tempname); if (len <= 0) return NULL; /* * I want to avoid name clashes fairly often, so I will use the current * time of day and information about the current process as a seed for the * generated file-name so that (with luck) clashes are at least not * incredibly probable. I will also use my source of random numbers, which * adds variation that changes each time I call this function. */ taskid = (unsigned long)GetCurrentThreadId()*169 + (unsigned long)GetCurrentProcessId(); #else strcpy(tempname, "/tmp/"); taskid = (unsigned long)getpid()*169 + (unsigned long)getuid(); #endif taskid = 169*taskid + (unsigned long)t0; taskid = 169*taskid + (unsigned long)c0; taskid = 169 * taskid + tmpSerial++; /* * The information I have gathered thus far may not change terribly rapidly, * since the process id is static form any one instance of my code and the * clock may tick very slowly compared with the CPU's activity. */ for (;;) { unsigned long n; int i; /* * The next line reduces taskid modulo the largest prime under 2^32, which * may be a sensible thing to do of "unsigned long" had been a 64-bit * data type. */ n = taskid % 0xfffffffbUL; /* * At this stage I have at most 32-bits of information, derived from the * clock and process identification. I will combine in info from the * random number generator I have elsewhere in this code, and do that in * such a way that I can generate 8 characters of file-name. */ s = tempname + strlen(tempname); for (i=0; i<7; i++) { int d = (int)(n % 36); n = n / 36; if (i == 1) n ^= (unsigned long)Crand(); if (d < 10) d += '0'; else d += ('a' - 10); /* now 0-9 or 1-z */ *s++ = d; } n = n % 36; if (n < 10) *s++ = '0' + (int)n; else *s++ = 'a' + (int)(n - 10); if (suffix != NULL) { sprintf(s, ".%.*s", (int)suffixlen, suffix); } else *s = 0; /* * If the file whose name I have just invented already exists I need to * try again. I will count of the "random" sequence from Crand to propose * an alternative name for me. */ if (file_exists(fname, tempname, strlen(tempname), tt)) { taskid ^= n; continue; } break; } return tempname; } Lisp_Object MS_CDECL Ltmpnam1(Lisp_Object nil, Lisp_Object extn) /* * Returns a string that is suitable for use as the name of a temporary * file and that has the given extension. Note that this is generally NOT * a fully secure thing to use, since after tmpnam() has generated the * name but before you get around to doing anything with the file * somebody else may do something that interferes. */ { char *suffix; int32_t suffixlen; Lisp_Object r; suffix = get_string_data(extn, "tmpnam", &suffixlen); errexit(); suffix = CSLtmpnam(suffix, suffixlen); if (suffix == NULL) return onevalue(nil); r = make_string(suffix); errexit(); return onevalue(r); } #ifdef DEBUG FILE *myopen(char *f, char *m) { FILE *s = fopen(f, m); trace_printf("fopen(%s, %s) = %p\n", f, m, s); return s; } #define fopen(a, b) myopen(a, b) #endif /* * The Common Lisp keywords for OPEN are a horrid mess. I arrange to decode * the syntax of the keywords in a Lisp-coded wrapper function, and in that * code I will also fill in default values for any that needs same. I then * pack all the information into a single integer, which has several * sub-fields * * x x xx xxx 00 direction PROBE * x x xx xxx 01 INPUT * x x xx xxx 10 OUTPUT * x x xx xxx 11 IO * * x x xx 000 xx if-exists NIL * x x xx 001 xx overwrite * x x xx 010 xx append * x x xx 011 xx rename * x x xx 100 xx error * x x xx 101 xx (new-version) * x x xx 110 xx (supersede) * x x xx 111 xx (rename-and-delete) * * x x 00 xxx xx if-does-not-exist NIL * x x 01 xxx xx create * x x 10 xxx xx error * * x 0 xx xxx xx regular text file * x 1 xx xxx xx open for binary access * * 0 x xx xxx xx regular file * 1 x xx xxx xx open as a pipe */ #define DIRECTION_MASK 0x3 #define DIRECTION_PROBE 0x0 #define DIRECTION_INPUT 0x1 #define DIRECTION_OUTPUT 0x2 #define DIRECTION_IO 0x3 #define IF_EXISTS_MASK 0x1c #define IF_EXISTS_NIL 0x00 #define IF_EXISTS_OVERWRITE 0x04 #define IF_EXISTS_APPEND 0x08 #define IF_EXISTS_RENAME 0x0c #define IF_EXISTS_ERROR 0x10 #define IF_EXISTS_NEW_VERSION 0x14 #define IF_EXISTS_SUPERSEDE 0x18 #define IF_EXISTS_RENAME_AND_DELETE 0x1c #define IF_MISSING_MASK 0x60 #define IF_MISSING_NIL 0x00 #define IF_MISSING_CREATE 0x20 #define IF_MISSING_ERROR 0x40 #define OPEN_BINARY 0x80 #define OPEN_PIPE 0x100 Lisp_Object Lopen(Lisp_Object nil, Lisp_Object name, Lisp_Object dir) { FILE *file; Lisp_Object r; char filename[LONGEST_LEGAL_FILENAME], fn1[LONGEST_LEGAL_FILENAME]; int32_t len; char *w; int d; #if defined HAVE_POPEN || defined HAVE_FWIN CSLbool pipep = NO; #endif if (!is_fixnum(dir)) return aerror1("open", dir); d = (int)int_of_fixnum(dir); #ifdef SOCKETS /* * If I am working as a socket server I will prohibit operations that * could (easily) corrupt the local machine. Here I prevent anybody from * opening files for output. I also prevent use of pipes. */ if (socket_server != 0 && ((d & DIRECTION_MASK) == DIRECTION_OUTPUT || (d & DIRECTION_MASK) == DIRECTION_IO || (d & OPEN_PIPE) != 0)) return aerror1("open invalid in server mode", dir); #endif #ifdef DEBUG_OPENING_FILES trace_printf("Open file:"); switch (d & DIRECTION_MASK) { case DIRECTION_PROBE: trace_printf(" probe"); break; case DIRECTION_INPUT: trace_printf(" input"); break; case DIRECTION_OUTPUT:trace_printf(" output"); break; case DIRECTION_IO: trace_printf(" io"); break; } switch (d & IF_EXISTS_MASK) { case IF_EXISTS_NIL: trace_printf(" if-exists-nil"); break; case IF_EXISTS_OVERWRITE: trace_printf(" if-exists-overwrite"); break; case IF_EXISTS_APPEND: trace_printf(" if-exists-append"); break; case IF_EXISTS_RENAME: trace_printf(" if-exists-rename"); break; case IF_EXISTS_ERROR: trace_printf(" if-exists-error"); break; case IF_EXISTS_NEW_VERSION: trace_printf(" if-exists-new-version"); break; case IF_EXISTS_SUPERSEDE: trace_printf(" if-exists-supersede"); break; case IF_EXISTS_RENAME_AND_DELETE: trace_printf(" if-exists-r-and-d"); break; } switch (d & IF_MISSING_MASK) { case IF_MISSING_NIL: trace_printf(" if-missing-nil"); break; case IF_MISSING_CREATE: trace_printf(" if-missing-create"); break; case IF_MISSING_ERROR: trace_printf(" if-missing-error"); break; } if (d & OPEN_BINARY) trace_printf(" binary"); if (d & OPEN_PIPE) trace_printf(" pipe"); trace_printf("\n"); #endif w = get_string_data(name, "open", &len); errexit(); if (len >= sizeof(filename)) len = sizeof(filename); file = NULL; switch (d & (DIRECTION_MASK | OPEN_PIPE)) { case DIRECTION_PROBE: /* probe file - can not be used with pipes */ file = open_file(filename, w, (size_t)len, "r", NULL); if (file == NULL) { switch (d & IF_MISSING_MASK) { case IF_MISSING_NIL: return onevalue(nil); case IF_MISSING_ERROR: return error(1, err_open_failed, name); case IF_MISSING_CREATE: /* * I thing that people who go (open xxx :direction :probe * :if-does-not-exist :create) * are to be considered unduly enthusiastic, but I will still try to do what * they tell me to! */ file = open_file(filename, w, (size_t)len, "w", NULL); if (file == NULL) return error(1, err_open_failed, name); fclose(file); file = NULL; } } else { fclose(file); file = NULL; } break; /* Must then create a no-direction stream */ case DIRECTION_INPUT: file = open_file(filename, w, (size_t)len, (d & OPEN_BINARY ? "rb" : "r"), NULL); if (file == NULL) { switch (d & IF_MISSING_MASK) { case IF_MISSING_NIL: return onevalue(nil); case IF_MISSING_ERROR: return error(1, err_open_failed, name); case IF_MISSING_CREATE: file = open_file(filename, w, (size_t)len, "w", NULL); if (file == NULL) return error(1, err_open_failed, name); fclose(file); /* * I use fopen(xx,"w") to create the file, then close it again and re-open * for input, so that concurrent tasks can see the file now existing but * only open for reading. If opening the file I just created fails I will * give up. */ file = open_file(filename, w, (size_t)len, (d & OPEN_BINARY ? "rb" : "r"), NULL); if (file == NULL) return error(1, err_open_failed, name); break; } } break; /* if-exists ignored when opening for input */ case DIRECTION_OUTPUT: case DIRECTION_IO: /* * I will start by trying to open the file to see if it exists. By using * mode "r+" I will only open it if I am able to obtain write-access, and * in some cases I will then be able to make use of the file. The fact that * it will have been opened for IO not just output will not harm me. */ file = open_file(filename, w, (size_t)len, (d & OPEN_BINARY ? "r+b" : "r+"), NULL); if (file == NULL) switch (d & IF_MISSING_MASK) { case IF_MISSING_NIL: return onevalue(nil); case IF_MISSING_ERROR: return error(1, err_open_failed, name); case IF_MISSING_CREATE: break; /* usual case for output and IO files */ } else switch (d & IF_EXISTS_MASK) { case IF_EXISTS_NIL: fclose(file); return onevalue(nil); case IF_EXISTS_RENAME: /* * When I open a file with :if-exists :rename I will always rename to * a fixed target, "oldfile.bak". If the rename fails I will not worry too * much. I imagine some people would rather that the name I renamed to was * based on the original file-name, but that seems excessive to me. And I * would have little sympathy for users who relied on it! */ fclose(file); file = NULL; rename_file(filename, w, (size_t)len, fn1, "oldfile.bak", 11); break; case IF_EXISTS_ERROR: fclose(file); return error(1, err_open_failed, name); /* * Working through the standard C library the ideas of :new-version, * :supersede and :rename-and-delete seem rather odd, so I will just treat * them all as :new-version. */ case IF_EXISTS_SUPERSEDE: case IF_EXISTS_RENAME_AND_DELETE: case IF_EXISTS_NEW_VERSION: fclose(file); delete_file(filename, w, (size_t)len); file = NULL; break; case IF_EXISTS_OVERWRITE: break; case IF_EXISTS_APPEND: fseek(file, 0L, SEEK_END); break; } if (file == NULL) { file = open_file(filename, w, (size_t)len, (d & OPEN_BINARY ? "w+b" : "w+"), NULL); if (file == NULL) return error(1, err_open_failed, name); } break; case DIRECTION_OUTPUT | OPEN_PIPE: #if defined HAVE_POPEN || defined HAVE_FWIN pipep = YES; memcpy(filename, w, (size_t)len); filename[len] = 0; file = my_popen(filename, "w"); if (file == NULL) return error(1, err_pipe_failed, name); break; #else return aerror("pipes not available with this version of CSL"); #endif case DIRECTION_INPUT | OPEN_PIPE: case DIRECTION_IO | OPEN_PIPE: return aerror("reading from pipes is not supported in CCL\n"); } push(name); r = make_stream_handle(); pop(name); errexit(); stream_type(r) = name; set_stream_file(r, file); switch (d & (DIRECTION_MASK | OPEN_PIPE)) { case DIRECTION_INPUT: set_stream_read_fn(r, char_from_file); set_stream_read_other(r, read_action_file); break; #if defined HAVE_POPEN || defined HAVE_FWIN case DIRECTION_OUTPUT | OPEN_PIPE: set_stream_write_fn(r, char_to_pipeout); set_stream_write_other(r, write_action_pipe); break; #endif case DIRECTION_OUTPUT: set_stream_write_fn(r, char_to_file); set_stream_write_other(r, write_action_file); set_stream_read_other(r, read_action_output_file); break; case DIRECTION_IO: set_stream_read_fn(r, char_from_file); set_stream_read_other(r, read_action_output_file); set_stream_write_fn(r, char_to_file); set_stream_write_other(r, write_action_file); break; } return onevalue(r); } Lisp_Object Lwrs(Lisp_Object nil, Lisp_Object a) { Lisp_Object old = qvalue(standard_output); if (a == nil) a = qvalue(terminal_io); if (a == old) return onevalue(old); else if (!is_stream(a)) return aerror1("wrs", a); else if (stream_write_fn(a) == char_to_illegal) #ifdef COMMON a = qvalue(terminal_io); #else return aerror("wrs (closed or input file)"); /* closed file or input file */ #endif qvalue(standard_output) = a; return onevalue(old); } Lisp_Object Lclose(Lisp_Object nil, Lisp_Object a) { /* * I will not allow anybody to close the terminal streams */ if (a == nil || a == lisp_terminal_io) return onevalue(nil); else if (!is_stream(a)) return aerror1("close", a); if (a == qvalue(standard_input)) qvalue(standard_input) = lisp_terminal_io; else if (a == qvalue(standard_output)) qvalue(standard_output) = lisp_terminal_io; other_read_action(READ_CLOSE, a); other_write_action(WRITE_CLOSE, a); #ifdef COMMON return onevalue(lisp_true); #else return onevalue(nil); #endif } #ifdef HAVE_LIBFOX extern void *text; #endif Lisp_Object Lmath_display(Lisp_Object nil, Lisp_Object a) { /* * In all cases where maths display is not supported (ie if output is * not directly to a FOX window that has been built with SHOWMATH * option) this returns nil and does not do anything at all exciting. If there * is the possibility of maths output the cases supported here are: * * nil ) Enquire if maths display is available, return T if so; * or 0 ) * 1 Enquire if a spool file is present; * 2 Clear out maths display buffer ready to start a new line; * 3 Indicate that local maths buffer is now complete and pass * its contents (which may be several lines) to the front end * display engine. */ #ifdef HAVE_LIBFOX if (a == nil || a == fixnum_of_int(0)) /* test if showmath available */ { /* * Disable maths specials if output is NOT to the terminal. Observe that often * standard_output will be a synonym for direct terminal access. */ Lisp_Object std = qvalue(standard_output); /* * text is the FXTerminal object. If it is NULL that means that I had * selected non-windowed mode.... */ if (text == NULL) return onevalue(nil); /* * With CSL I have all these curious ways of ending up with standard output * redirected to elsewhere! In any such case I want this code to report "not * directly to a maths-aware window". */ if (alternative_stdout != NULL || procedural_output != NULL) return onevalue(nil); /* * I allow for synonym streams (which are probably only used in Common Lisp * mode). I do NOT allow for broadcast streams. I then check if the current * output stream would end up executing char_to_terminal to write a character. */ while (stream_write_fn(std) == char_to_synonym) std = stream_write_data(std); if (stream_write_fn(std) != char_to_terminal) return onevalue(nil); /* * Now I believe I am attached to a FOX screen that can display maths. */ return onevalue(lisp_true); } else if (a == fixnum_of_int(1)) /* test if spool file in use */ { /* * Note that I let this say TRUE if a spool file is in use regardless * of whether maths display is to be used... */ if (spool_file == NULL) return onevalue(nil); else return onevalue(lisp_true); } else if (a == fixnum_of_int(2)) /* clear out local buffer */ { math_buffer_p = 0; if (math_buffer != NULL) math_buffer[0] = 0; return onevalue(lisp_true); } else if (a == fixnum_of_int(3)) /* display local buffer */ { if (math_buffer == NULL || math_buffer[0]==0) return onevalue(nil); fwin_showmath(math_buffer); math_buffer_p = 0; math_buffer[0] = 0; return onevalue(lisp_true); } else #endif return onevalue(nil); /* bad arg, but just return nil */ } Lisp_Object Ltruename(Lisp_Object nil, Lisp_Object name) { char filename[LONGEST_LEGAL_FILENAME]; Lisp_Object truename; int32_t len; char *w = get_string_data(name, "truename", &len); errexit(); if (len >= sizeof(filename)) len = sizeof(filename); w = get_truename(filename,w,len); if (w == NULL) return aerror0(filename); truename = make_string(w); free(w); errexit(); return onevalue(truename); } Lisp_Object Lcreate_directory(Lisp_Object nil, Lisp_Object name) { char filename[LONGEST_LEGAL_FILENAME]; int32_t len; char *w; if (name == unset_var) return onevalue(nil); w = get_string_data(name, "create-directory", &len); errexit(); if (len >= sizeof(filename)) len = sizeof(filename); #ifdef SOCKETS if (socket_server != 0) return aerror("create-directory"); #endif len = create_directory(filename, w, (size_t)len); return onevalue(Lispify_predicate(len == 0)); } Lisp_Object Lfile_readable(Lisp_Object nil, Lisp_Object name) { char filename[LONGEST_LEGAL_FILENAME]; int32_t len; char *w = get_string_data(name, "file-readable", &len); errexit(); if (len >= sizeof(filename)) len = sizeof(filename); len = file_readable(filename, w, (size_t)len); return onevalue(Lispify_predicate(len)); } Lisp_Object Lchange_directory(Lisp_Object nil, Lisp_Object name) { char filename[LONGEST_LEGAL_FILENAME]; int32_t len; char *err; char *w; if (name == unset_var) return onevalue(nil); w = get_string_data(name, "change-directory", &len); errexit(); if (len >= sizeof(filename)) len = sizeof(filename); /* * At present I will permit change-directory in server mode. */ err = change_directory(filename, w, (size_t)len); if (err != NULL) aerror0(err); return onevalue(Lispify_predicate(err == NULL)); } Lisp_Object Lfile_writeable(Lisp_Object nil, Lisp_Object name) { char filename[LONGEST_LEGAL_FILENAME]; int32_t len; char *w; /* First check whether file exists */ if (Lfilep(nil,name) == nil) return nil; w = get_string_data(name, "file-writable", &len); errexit(); if (len >= sizeof(filename)) len = sizeof(filename); len = file_writeable(filename, w, (size_t)len); return onevalue(Lispify_predicate(len)); } Lisp_Object Ldelete_file(Lisp_Object nil, Lisp_Object name) { char filename[LONGEST_LEGAL_FILENAME]; int32_t len; char *w; if (name == unset_var) return onevalue(nil); w = get_string_data(name, "delete-file", &len); errexit(); if (len >= sizeof(filename)) len = sizeof(filename); #ifdef SOCKETS if (socket_server != 0) return aerror("delete-file"); #endif len = delete_file(filename, w, (size_t)len); return onevalue(Lispify_predicate(len == 0)); } /* Returns the length of a file in bytes */ Lisp_Object Lfile_length(Lisp_Object nil, Lisp_Object name) { char filename[LONGEST_LEGAL_FILENAME]; int32_t len; long size; char *w = get_string_data(name, "file-length", &len); errexit(); if (len >= sizeof(filename)) len = sizeof(filename); size = file_length(filename, w, (size_t)len); if (size < 0) return nil; else if (size < 268435456) return fixnum_of_int(size); else return make_one_word_bignum(size); } Lisp_Object Ldirectoryp(Lisp_Object nil, Lisp_Object name) { char filename[LONGEST_LEGAL_FILENAME]; int32_t len; char *w = get_string_data(name, "directoryp", &len); errexit(); if (len >= sizeof(filename)) len = sizeof(filename); len = directoryp(filename, w, (size_t)len); return onevalue(Lispify_predicate(len)); } Lisp_Object MS_CDECL Lget_current_directory(Lisp_Object nil, int nargs, ...) { char filename[LONGEST_LEGAL_FILENAME]; int len; Lisp_Object w; argcheck(nargs, 0, "get-current-directory"); len = get_current_directory(filename, LONGEST_LEGAL_FILENAME); if (len == 0) return onevalue(nil); w = make_string(filename); errexit(); return onevalue(w); } Lisp_Object MS_CDECL Luser_homedir_pathname(Lisp_Object nil, int nargs, ...) { char home[LONGEST_LEGAL_FILENAME]; int len; Lisp_Object w; argcheck(nargs, 0, "user-homedir-pathname") len = get_home_directory(home, LONGEST_LEGAL_FILENAME); if (len == 0) return onevalue(nil); w = make_string(home); errexit(); return onevalue(w); } Lisp_Object MS_CDECL Lget_lisp_directory(Lisp_Object nil, int nargs, ...) { char filename[LONGEST_LEGAL_FILENAME]; int len; Lisp_Object w; argcheck(nargs, 0, "get-lisp-directory"); strcpy(filename, standard_directory); len = strlen(filename); while (len-- > 0 && filename[len] != '/' && filename[len] != '\\'); if (len == 0) return onevalue(nil); filename[len] = 0; w = make_string(filename); errexit(); return onevalue(w); } Lisp_Object Lrename_file(Lisp_Object nil, Lisp_Object from, Lisp_Object to) { char from_name[LONGEST_LEGAL_FILENAME], to_name[LONGEST_LEGAL_FILENAME]; int32_t from_len, to_len; char *from_w, *to_w; if (from == unset_var) return onevalue(nil); if (to == unset_var) return onevalue(nil); #ifdef SOCKETS if (socket_server != 0) return aerror("rename-file"); #endif push(to); from_w = get_string_data(from, "rename-file", &from_len); pop(to); errexit(); if (from_len >= sizeof(from_name)) from_len = sizeof(from_name); from = (Lisp_Object)(from_w + TAG_VECTOR - CELL); push(from); to_w = get_string_data(to, "rename-file", &to_len); pop(from); from_w = &celt(from, 0); errexit(); if (to_len >= sizeof(to_name)) to_len = sizeof(to_name); to_len = rename_file(from_name, from_w, (size_t)from_len, to_name, to_w, (size_t)to_len); return onevalue(Lispify_predicate(to_len == 0)); } /* * This function is a call-back from the file-scanning routine. */ static void make_dir_list(char *name, int why, long int size) { Lisp_Object nil = C_nil, w; CSL_IGNORE(why); CSL_IGNORE(size); errexitv(); if (scan_leafstart >= (int)strlen(name)) return; w = make_string(name+scan_leafstart); errexitv(); w = cons(w, stack[0]); errexitv(); stack[0] = w; } Lisp_Object Llist_directory(Lisp_Object nil, Lisp_Object name) { Lisp_Object result; char filename[LONGEST_LEGAL_FILENAME]; int32_t len; char *w = get_string_data(name, "list-directory", &len); errexit(); if (len >= sizeof(filename)) len = sizeof(filename); push(nil); list_directory_members(filename, w, (size_t)len, make_dir_list); pop(result); errexit(); result = nreverse(result); errexit(); return onevalue(result); } /*****************************************************************************/ /* Printing. */ /*****************************************************************************/ int escaped_printing; /* * I should make WRS save tmprint_flag so that it always refers to * a setting of the stream currently in use, ie active_stream. That should * not be hard but I will do it later. @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ */ int tmprint_flag = 0; #define escape_yes 0x0001 /* make output re-readable */ #define escape_fold_down 0x0002 /* force lower case output */ #define escape_fold_up 0x0004 /* FORCE UPPER CASE OUTPUT */ #define escape_capitalize 0x0008 /* Force Capitalisation (!) */ #define escape_binary 0x0010 /* print format for numbers */ #define escape_octal 0x0020 /* (including bignums) */ #define escape_hex 0x0040 #define escape_nolinebreak 0x0080 /* use infinite line-length */ #define escape_hexwidth 0x3f00 /* 6 bits to specify width of hex/bin */ #define escape_width(n) (((n) & escape_hexwidth) >> 8) #define escape_checksum 0x4000 /* doing a checksum operation */ static void outprefix(CSLbool blankp, int32_t len) /* * This function takes most of the responsibility for splitting lines. * when called we are about to print an item with (len) characters. * If blankp is true we need to display a blank or newline before * the item. */ { nil_as_base int32_t line_length = other_write_action(WRITE_GET_INFO+WRITE_GET_LINE_LENGTH, active_stream); int32_t column = other_write_action(WRITE_GET_INFO+WRITE_GET_COLUMN, active_stream); if (blankp) len++; if (column+len > line_length && (escaped_printing & escape_nolinebreak) == 0 && !tmprint_flag) putc_stream('\n', active_stream); else if (blankp) putc_stream(' ', active_stream); } static Lisp_Object Lprint_precision(Lisp_Object nil, Lisp_Object a) { int32_t old = print_precision; if (a == nil) return onevalue(fixnum_of_int(old)); if (!is_fixnum(a)) return aerror1("print-precision", a); print_precision = int_of_fixnum(a); if (print_precision > 16) print_precision = 15; return onevalue(fixnum_of_int(old)); } static void prin_buf(char *buf, int blankp) { Lisp_Object nil = C_nil; int len = strlen(buf), i; outprefix(blankp, len); for (i=0; i<len; i++) { putc_stream(*buf++, active_stream); errexitv(); } } static int32_t local_gensym_count; void internal_prin(Lisp_Object u, int blankp) { Lisp_Object w, nil = C_nil; int32_t len, k; char my_buff[68]; #ifdef COMMON int bl = blankp & 2; /* * There is a fairly shameless FUDGE here. When I come to need to print * the package part of a symbol as in ppp:xxx (or even |)p(|::|.| if I * have names with silly characters in them) I will have a STRING that is the * name of the relevant package, but I want it displayed as if it was an * identifier. I achieve this by setting the "2" bit in blankp (which is * otherwise a simple boolean), and when this is detected I go and join the * code for printing symbols. But in that case I MUST have been passed * a (simple) string, or else things can collapse utterly. */ blankp &= 1; if (bl != 0) { w = u; push(u); goto tag_symbol; } restart: #endif if (--countdown < 0) deal_with_tick(); errexitv(); if (stack >= stacklimit) { u = reclaim(u, "stack", GC_STACK, 0); errexitv(); } switch ((int)u & TAG_BITS) { case TAG_CONS: #ifdef COMMON if (u == nil) /* BEWARE - nil is tagged as a cons cell */ { outprefix(blankp, 3); putc_stream('N', active_stream); putc_stream('I', active_stream); putc_stream('L', active_stream); return; } #endif if (u == 0) u = nil; /* Bug security here */ push(u); outprefix(blankp, 1); putc_stream('(', active_stream); errexitvn(1); internal_prin(qcar(stack[0]), 0); errexitvn(1); w = stack[0]; while (is_cons(w = qcdr(w))) { #ifdef COMMON if (w == nil) break; /* Again BEWARE the tag code of NIL */ #endif stack[0] = w; internal_prin(qcar(stack[0]), 1); errexitvn(1); w = stack[0]; } if (w != nil) { stack[0] = w; outprefix(YES, 1); putc_stream('.', active_stream); errexitvn(1); internal_prin(stack[0], 1); } popv(1); outprefix(NO, 1); putc_stream(')', active_stream); return; #ifdef COMMON case TAG_SFLOAT: { Float_union uu; uu.i = u - TAG_SFLOAT; sprintf(my_buff, "%#.6g", (double)uu.f); } goto float_print_tidyup; #endif case TAG_FIXNUM: if (escaped_printing & escape_hex) { int32_t v = int_of_fixnum(u); int width = escape_width(escaped_printing); int32_t mask; /* * The printing style adopted here for negative numbers follows that used in * the big number printing code. A prefix "~" stands for an infinite initial * string of 'f' digits, and what follows will be exactly one 'f' (just to * remind you) and then the remaining hex digits. E.g. -2 should display * as ~fe. Note that any fixnum will start off with 0xf in the top 4 of * 32 bits. If an explicit width had been specified then I want that many * charcters to be displayed, with full leading zeros etc. A width is taken as * minimum number of chars to be displayed, so a width of zero (or in fact 1) * would have the effect of no constraint. The width-specification field * only allows for the range 0 to 63, and that is just as well since I put * characters in a buffer (my_buff) which would almost fill up at the * widest... */ len = 0; if (v < 0) { mask = 0x0f000000; my_buff[len++] = '~'; width--; while ((v & mask) == mask && mask != 0) { v = v ^ (mask << 4); mask = mask >> 4; } k = 'f'; } else k = '0'; mask = 0xf; while ((v & mask) != v) { width--; mask = (mask<<4) | 0xf; } while (--width > 0) my_buff[len++] = (char)k; sprintf(&my_buff[len], "%lx", (long)v); } else if (escaped_printing & escape_octal) { int32_t v = int_of_fixnum(u); int width = escape_width(escaped_printing); int32_t mask; len = 0; if (v < 0) { mask = 0x38000000; my_buff[len++] = '~'; width--; while ((v & mask) == mask && mask != 0) { v = v ^ (mask << 3); mask = mask >> 3; } k = '7'; } else k = '0'; mask = 0x7; while ((v & mask) != v) { width--; mask = (mask<<3) | 0x7; } while (--width > 0) my_buff[len++] = (char)k; sprintf(&my_buff[len], "%lo", (long)v); } else if (escaped_printing & escape_binary) { int32_t v = int_of_fixnum(u); /* int width = escape_width(escaped_printing); */ uint32_t mask = 0x40000000; len = 0; if (v < 0) { while ((v & mask) == mask && mask != 0) { v = v ^ (mask << 1); mask = mask >> 1; } my_buff[len++] = '~'; k = '1'; } else k = '0'; /* * /* Width specifier not processed here (yet), sorry. */ mask = 0x80000000; while ((v & mask) == 0 && mask != 1) mask = mask >> 1; while (mask != 0) { my_buff[len++] = (v & mask) ? '1' : '0'; mask = mask >> 1; } my_buff[len] = 0; } else sprintf(my_buff, "%ld", (long)int_of_fixnum(u)); break; case TAG_ODDS: if (is_bps(u)) { Header h = *(Header *)(data_of_bps(u) - CELL); len = length_of_header(h) - CELL; push(u); outprefix(blankp, 3+2*len); putc_stream('#', active_stream); putc_stream('[', active_stream); for (k = 0; k < len; k++) { int ch = ((char *)data_of_bps(stack[0]))[k]; static char *hexdig = "0123456789abcdef"; /* * Code vectors are not ever going to be re-readable (huh - I suppose there * is no big reason why they should not be!) so I split them across multiple * lines if that seems useful. Anyway a reader for them could understand to * expect that. */ outprefix(NO, 2); #if defined DEMO_MODE || defined DEMO_BUILD putc_stream('?', active_stream); putc_stream('?', active_stream); #else putc_stream(hexdig[(ch >> 4) & 0xf], active_stream); putc_stream(hexdig[ch & 0xf], active_stream); #endif } popv(1); putc_stream(']', active_stream); return; } /* * A SPID is an object used internally by CSL in various places, and the * rules of the system are that it ought never to be visible to the user. * I print it here in case it arises because of a bug, or while I am testing. */ else if (is_spid(u)) { switch (u & 0xffff) { /* * The decoding of readable names for SPIDs here is somewhat over the top * except while somebdy is hard at work debugging.... */ case SPID_NIL: strcpy(my_buff, "SPID_NIL"); break; case SPID_FBIND: strcpy(my_buff, "SPID_FBIND"); break; case SPID_CATCH: strcpy(my_buff, "SPID_CATCH"); break; case SPID_PROTECT: strcpy(my_buff, "SPID_PROTECT"); break; case SPID_NOARG: strcpy(my_buff, "SPID_NOARG"); break; case SPID_HASH0: strcpy(my_buff, "SPID_HASH0"); break; case SPID_HASH1: strcpy(my_buff, "SPID_HASH1"); break; case SPID_GCMARK: strcpy(my_buff, "SPID_GCMARK"); break; case SPID_NOINPUT: strcpy(my_buff, "SPID_NOINPUT"); break; case SPID_ERROR: strcpy(my_buff, "SPID_ERROR"); break; case SPID_PVBIND: strcpy(my_buff, "SPID_PVBIND"); break; case SPID_NOPROP: strcpy(my_buff, "SPID_NOPROP"); break; case SPID_LIBRARY: u = (u >> 20) & 0xfff; /* * When I print the name of a library I will truncate the displayed name * to 30 characters. This is somewhat arbitrary (but MUST relate to the * size of my_buff), but will tend to keep output more compact. */ sprintf(my_buff, "#{%.30s}", fasl_paths[u]); break; default: sprintf(my_buff, "SPID_%lx", (long)((u >> 8) & 0x00ffffff)); break; } len = strlen(my_buff); outprefix(blankp, len); for (k=0; k<len; k++) putc_stream(my_buff[k], active_stream); return; } /* * Assume if is a CHAR here */ outprefix(blankp, escaped_printing & escape_yes ? 3 : 1); if (u != CHAR_EOF) /* I know that a char is immediate data and so does not need GC protection */ { if (escaped_printing & escape_yes) putc_stream('#', active_stream), putc_stream('\\', active_stream); putc_stream((int)code_of_char(u), active_stream); } return; case TAG_VECTOR: { Header h = vechdr(u); len = length_of_header(h) - CELL; /* counts in bytes */ push(u); #ifdef COMMON print_non_simple_string: #endif switch (type_of_header(h)) { case TYPE_STRING: { int32_t slen = 0; if (escaped_printing & escape_yes) { for (k = 0; k < len; k++) { int ch = celt(stack[0], k); if (ch == '"') slen += 2; #ifdef COMMON else if (ch == '\\') slen += 2; #endif else if (iscntrl(ch)) slen += 3; else slen += 1; } slen += 2; } else slen = len; outprefix(blankp, slen); /* * I will write out the fast, easy, common case here */ if (!(escaped_printing & (escape_yes | escape_fold_down | escape_fold_up | escape_capitalize))) { for (k = 0; k < len; k++) { int ch = celt(stack[0], k); putc_stream(ch, active_stream); } } else { if (escaped_printing & escape_yes) putc_stream('"', active_stream); for (k = 0; k < len; k++) { int ch = celt(stack[0], k); static char *hexdig = "0123456789abcdef"; #ifdef COMMON if ((escaped_printing & escape_yes) && (ch == '"' || ch == '\\')) { putc_stream('\\', active_stream); putc_stream(ch, active_stream); } #else if ((escaped_printing & escape_yes) && ch == '"') { putc_stream('"', active_stream); putc_stream('"', active_stream); } #endif else if (iscntrl(ch)) { putc_stream('\\', active_stream); putc_stream(hexdig[(ch >> 4) & 0xf], active_stream); putc_stream(hexdig[ch & 0xf], active_stream); } else { if (escaped_printing & escape_fold_down) ch = tolower(ch); else if (escaped_printing & escape_fold_up) ch = toupper(ch); /* Just For Now I Will Not Implement The Option To Capitalize Things */ putc_stream(ch, active_stream); } } } popv(1); if (escaped_printing & escape_yes) putc_stream('"', active_stream); } return; case TYPE_SP: pop(u); sprintf(my_buff, "#<closure: %p>", (void *)elt(u, 0)); goto print_my_buff; case TYPE_SPARE: pop(u); sprintf(my_buff, "#<encapsulated pointer: %p>", (void *)elt(u, 0)); goto print_my_buff; #ifdef COMMON case TYPE_BITVEC1: bl = 1; break; case TYPE_BITVEC2: bl = 2; break; case TYPE_BITVEC3: bl = 3; break; case TYPE_BITVEC4: bl = 4; break; case TYPE_BITVEC5: bl = 5; break; case TYPE_BITVEC6: bl = 6; break; case TYPE_BITVEC7: bl = 7; break; case TYPE_BITVEC8: bl = 8; break; #endif #ifndef COMMON case TYPE_STRUCTURE: pop(u); sprintf(my_buff, "[e-vector:%.8lx]", (long)(uint32_t)u); goto print_my_buff; #else case TYPE_STRUCTURE: if (elt(stack[0], 0) == package_symbol) { outprefix(blankp, 3); putc_stream('#', active_stream); putc_stream('P', active_stream); putc_stream(':', active_stream); pop(u); u = elt(u, 8); /* The name of the package */ blankp = 0; goto restart; } /* Drop through */ #endif case TYPE_ARRAY: #ifdef COMMON { Lisp_Object dims = elt(stack[0], 1); /* * I suppose that really I need to deal with non-simple bitvectors too. * And generally get Common Lisp style array printing "right". */ if (consp(dims) && !consp(qcdr(dims)) && elt(stack[0], 0) == string_char_sym) { len = int_of_fixnum(qcar(dims)); dims = elt(stack[0], 5); /* Fill pointer */ if (is_fixnum(dims)) len = int_of_fixnum(dims); stack[0] = elt(stack[0], 2); /* * The demand here is that the object within the non-simple-string was * a simple string, so I can restart printing to deal with it. This will * not support strings that were over-large so got represented in * chunks. Tough luck about that for now! */ h = TYPE_STRING; goto print_non_simple_string; } } /* Drop through */ #endif case TYPE_SIMPLE_VEC: case TYPE_HASH: { #ifndef COMMON if (type_of_header(h) == TYPE_SIMPLE_VEC) { outprefix(blankp, 1); putc_stream('[', active_stream); } else #endif if (type_of_header(h) == TYPE_STRUCTURE) { outprefix(blankp, 3); putc_stream('#', active_stream); putc_stream('S', active_stream); putc_stream('(', active_stream); } else if (type_of_header(h) == TYPE_HASH) { outprefix(blankp, 3); putc_stream('#', active_stream); putc_stream('H', active_stream); putc_stream('(', active_stream); } else { outprefix(blankp, 2); putc_stream('#', active_stream); putc_stream('(', active_stream); } #ifdef COMMON if (qvalue(print_array_sym) == nil) { putc_stream('.', active_stream); putc_stream('.', active_stream); putc_stream('.', active_stream); } else #endif for (k=0; k<len; k+=CELL) { Lisp_Object vv = *(Lisp_Object *) ((char *)stack[0] + (CELL - TAG_VECTOR) + k); internal_prin(vv, (k != 0) ? 1 : 0); errexitvn(1); } popv(1); outprefix(NO, 1); #ifndef COMMON if (type_of_header(h) == TYPE_SIMPLE_VEC) putc_stream(']', active_stream); else #endif putc_stream(')', active_stream); return; } case TYPE_MIXED1: /* An experimental addition to CSL */ case TYPE_MIXED2: case TYPE_MIXED3: case TYPE_STREAM: { outprefix(blankp, 3); putc_stream('#', active_stream); if (type_of_header(h) == TYPE_STREAM) putc_stream('F', active_stream); else if (type_of_header(h) == TYPE_MIXED1) putc_stream('1', active_stream); else if (type_of_header(h) == TYPE_MIXED2) putc_stream('2', active_stream); else putc_stream('3', active_stream); putc_stream('[', active_stream); #ifdef COMMON if (qvalue(print_array_sym) == nil) { putc_stream('.', active_stream); putc_stream('.', active_stream); putc_stream('.', active_stream); } else #endif { internal_prin(elt(stack[0], 0), 0); errexitvn(1); outprefix(NO, 1); internal_prin(elt(stack[0], 1), 1); errexitvn(1); outprefix(NO, 1); internal_prin(elt(stack[0], 2), 1); errexitvn(1); } for (k=3*CELL; k<len; k+=CELL) { sprintf(my_buff, "%.8lx", (long)*(Lisp_Object *) ((char *)stack[0] + (CELL - TAG_VECTOR) + k)); prin_buf(my_buff, YES); } popv(1); outprefix(NO, 1); putc_stream(']', active_stream); return; } case TYPE_VEC8: outprefix(blankp, 4); putc_stream('#', active_stream); putc_stream('V', active_stream); putc_stream('8', active_stream); putc_stream('(', active_stream); for (k=0; k<len; k++) { sprintf(my_buff, "%d", scelt(stack[0], k)); prin_buf(my_buff, k != 0); } outprefix(NO, 1); putc_stream(')', active_stream); popv(1); return; case TYPE_VEC16: outprefix(blankp, 5); putc_stream('#', active_stream); putc_stream('V', active_stream); putc_stream('1', active_stream); putc_stream('6', active_stream); putc_stream('(', active_stream); len = len >> 1; for (k=0; k<len; k++) { sprintf(my_buff, "%d", helt(stack[0], k)); prin_buf(my_buff, k != 0); } outprefix(NO, 1); putc_stream(')', active_stream); popv(1); return; case TYPE_VEC32: outprefix(blankp, 5); putc_stream('#', active_stream); putc_stream('V', active_stream); putc_stream('3', active_stream); putc_stream('2', active_stream); putc_stream('(', active_stream); len = len >> 2; /* /* I think that this is broken on 64-bit machines since then ielt fetches a 64-bit value.... Oh misery! But maybe it is not a VERY important part of Lisp so I can think about it later! */ for (k=0; k<len; k++) { sprintf(my_buff, "%ld", (long)ielt(stack[0], k)); prin_buf(my_buff, k != 0); } outprefix(NO, 1); putc_stream(')', active_stream); popv(1); return; case TYPE_FLOAT32: outprefix(blankp, 4); putc_stream('#', active_stream); putc_stream('F', active_stream); putc_stream('S', active_stream); putc_stream('(', active_stream); len = len >> 2; for (k=0; k<len; k++) { sprintf(my_buff, "%#.7g", (double)felt(stack[0], k)); prin_buf(my_buff, k != 0); } outprefix(NO, 1); putc_stream(')', active_stream); popv(1); return; case TYPE_FLOAT64: outprefix(blankp, 4); putc_stream('#', active_stream); putc_stream('F', active_stream); putc_stream('D', active_stream); putc_stream('(', active_stream); len = (len-CELL)/8; /* I will not worry about print-precision bugs here... */ for (k=0; k<len; k++) { sprintf(my_buff, "%#.*g", (int)print_precision, delt(stack[0], k)); prin_buf(my_buff, k != 0); } outprefix(NO, 1); putc_stream(')', active_stream); popv(1); return; default: goto error_case; } #ifdef COMMON /* Here for bit-vectors */ outprefix(blankp, 2+8*(len-1)+bl); putc_stream('#', active_stream), putc_stream('*', active_stream); { int z, q; for (k = 0; k < len-1; k++) { z = ucelt(stack[0], k); for (q=0; q<8; q++) { if (z & 1) putc_stream('1', active_stream); else putc_stream('0', active_stream); z >>= 1; } } if (len != 0) /* Empty bitvec */ { z = ucelt(stack[0], len-1); for (q=0; q<bl; q++) { if (z & 1) putc_stream('1', active_stream); else putc_stream('0', active_stream); z >>= 1; } } } popv(1); return; #endif } /* * It seems probable that I could never get here, but this "return" is * just in case, as a safety measure. */ popv(1); return; case TAG_SYMBOL: push(u); /* * When computing checksums with the "md5" function I count gensyms as being * purely local to the current expression. The strange effect is that * (md5 (gensym)) * always gives the same result, even though the gensyms involved are * different. But it is REASONABLE compatible with a view that I am forming * a digest of a printed representation and is needed if digests are to * be acceptably consistent across lisp images. */ if (escaped_printing & escape_checksum) { if ((qheader(u) & (SYM_CODEPTR+SYM_ANY_GENSYM)) == SYM_ANY_GENSYM) { Lisp_Object al = stream_write_data(active_stream); while (al != nil && qcar(qcar(al)) != u) al = qcdr(al); pop(u); if (al == nil) { al = acons(u, fixnum_of_int(local_gensym_count), stream_write_data(active_stream)); local_gensym_count++; if (exception_pending()) return; stream_write_data(active_stream) = al; } al = qcdr(qcar(al)); sprintf(my_buff, "#G%lx", (long)int_of_fixnum(al)); break; } } w = get_pname(u); /* allocates name for gensym if needbe */ u = stack[0]; #ifdef COMMON tag_symbol: #endif nil = C_nil; if (!exception_pending()) { Header h = vechdr(w); int32_t slen = 0; int raised = 0; #ifdef COMMON int pkgid = 0; /* No package marker needed */ /* * 0 no package marker needed * 1 display as #:xxx (ie as a gensym) * 2 display as :xxx (ie in keyword package) * 3 display as ppp:xxx (external in its home package) * 4 display as ppp::xxx (internal in its home package) */ if (escaped_printing & escape_yes) { if (!is_symbol(u)) pkgid = 0; /* Support for a HACK */ else if (qpackage(u) == nil) pkgid = 1; /* gensym */ else if (qpackage(u) == qvalue(keyword_package)) pkgid = 2; else if (qpackage(u) == CP) pkgid = 0; /* home is current */ else { pkgid = 3; k = packflags_(CP); if (k != 0 && k <= SYM_IN_PKG_COUNT) { k = ((int32_t)1) << (k+SYM_IN_PKG_SHIFT-1); if (k & qheader(u)) pkgid = 0; } else k = 0; if (pkgid != 0) { push(w); w = Lfind_symbol_1(nil, w); nil = C_nil; if (exception_pending()) { popv(2); return; } u = stack[-1]; if (mv_2 != nil && w == u) { pkgid = 0; /* * Here I update the cache it that keeps telling me that the symbol is * is "available" in the package that is current at present. I guess that * I need to clear this bit if I unintern or otherwise mess around with * package structures. */ qheader(u) |= k; } else if (qheader(u) & SYM_EXTERN_IN_HOME) pkgid = 3; else pkgid = 4; pop(w); } } } #endif len = length_of_header(h); /* counts in bytes */ /* * When I come to print things I will assume that I want them re-readable * with values of !*raise and !*lower as in effect when the printing took * place, and insert escape characters accordingly. I optimise the case * of printing without any effects... */ if (!(escaped_printing & (escape_yes | escape_fold_down | escape_fold_up | escape_capitalize))) { stack[0] = w; len -= CELL; #ifdef COMMON switch (pkgid) { case 1: outprefix(blankp, len+2); putc_stream('#', active_stream); putc_stream(':', active_stream); break; case 2: outprefix(blankp, len+1); putc_stream(':', active_stream); break; case 3: case 4: internal_prin(packname_(qpackage(u)), blankp | 2); putc_stream(':', active_stream); if (pkgid == 4) putc_stream(':', active_stream); break; default:outprefix(blankp, len); break; } #else outprefix(blankp, len); #endif for (k = 0; k < len; k++) { int ch = celt(stack[0], k); /* * Specially for the benefit of "tmprint.red" I arrange to switch off * line-wrapping if I have a "\x02" character but switch it back on after * "\x05". I should probably also restore things to a normal state on any * exception/backtrace. */ if (ch == 2) tmprint_flag = 1; putc_stream(ch, active_stream); if (ch == 5) tmprint_flag = 0; } } else { int extralen = 0; if (qvalue(lower_symbol) != nil) raised = -1; else if (qvalue(raise_symbol) != nil) raised = 1; stack[0] = w; len -= CELL; /* A really horrid case here - digits are special at the start of names! */ if (len > 0) { int ch = celt(stack[0], 0); if (escaped_printing & escape_yes && (isdigit(ch) #ifdef COMMON || (ch=='.') #else || (ch=='_') #endif )) extralen++; } for (k = 0; k < len; k++) { int ch = celt(stack[0], k); if (escaped_printing & escape_yes && !(escaped_printing & (escape_fold_down | escape_fold_up | escape_capitalize)) && #ifdef COMMON (ch=='.' || ch=='\\' || ch=='|') || #endif (!is_constituent(ch) || #ifdef COMMON (ch=='.' || ch=='\\' || ch=='|' || ch==':') || #endif (raised < 0 && isupper(ch)) || (raised > 0 && islower(ch)))) extralen++; slen++; } #ifdef COMMON /* * The |xxx| notation is where the "2" here comes from, but that does not * make full allowance for names with '\\' in them. Tough! */ if (extralen != 0) extralen = 2; switch (pkgid) { case 1: outprefix(blankp, slen+extralen+2); putc_stream('#', active_stream); putc_stream(':', active_stream); break; case 2: outprefix(blankp, slen+extralen+1); putc_stream(':', active_stream); break; case 3: case 4: internal_prin(packname_(qpackage(u)), blankp | 2); putc_stream(':', active_stream); if (pkgid == 4) putc_stream(':', active_stream); break; default:outprefix(blankp, len); break; } #else outprefix(blankp, slen+extralen); #endif #ifdef COMMON if (extralen != 0) putc_stream('|', active_stream); #endif if (len > 0) { int ch = celt(stack[0], 0); #ifdef COMMON if (ch == '\\' || ch=='|') putc_stream(ESCAPE_CHAR, active_stream); #else if (!is_constituent(ch) || isdigit(ch) || (ch == '_') || (!(escaped_printing & (escape_fold_down | escape_fold_up | escape_capitalize)) && ((raised < 0 && isupper(ch)) || (raised > 0 && islower(ch))))) putc_stream(ESCAPE_CHAR, active_stream); #endif if (escaped_printing & escape_fold_down) ch = tolower(ch); else if (escaped_printing & escape_fold_up) ch = toupper(ch); putc_stream(ch, active_stream); } for (k = 1; k < len; k++) { int ch = celt(stack[0], k); #ifdef COMMON if (ch == '\\' || ch=='|') putc_stream(ESCAPE_CHAR, active_stream); #else if (!(escaped_printing & (escape_fold_down | escape_fold_up | escape_capitalize)) && (!is_constituent(ch) || (raised < 0 && isupper(ch)) || (raised > 0 && islower(ch)))) putc_stream(ESCAPE_CHAR, active_stream); #endif if (escaped_printing & escape_fold_down) ch = tolower(ch); else if (escaped_printing & escape_fold_up) ch = toupper(ch); putc_stream(ch, active_stream); } #ifdef COMMON if (extralen != 0) putc_stream('|', active_stream); #endif } } popv(1); return; case TAG_BOXFLOAT: switch (type_of_header(flthdr(u))) { #ifdef COMMON case TYPE_SINGLE_FLOAT: sprintf(my_buff, "%#.7g", (double)single_float_val(u)); break; #endif case TYPE_DOUBLE_FLOAT: /* * Hexadecimal printing of floating point numbers is only provided for * here to help with nasty low-level debugging. The output will not be * directly re-readable. It is only provided for the (default) double- * precision numbers. Use (prinhex ..) to activate it. */ if (escaped_printing & escape_hex) { uint32_t *p = (uint32_t *)((char *)u + 1); int q = current_fp_rep & FP_WORD_ORDER; sprintf(my_buff, "{%.8lx/%.8lx:%#.8g}", (long)(uint32_t)p[1-q], (long)(uint32_t)p[q], double_float_val(u)); } else if (escaped_printing & escape_octal) { uint32_t *p = (uint32_t *)((char *)u + 1); int q = current_fp_rep & FP_WORD_ORDER; sprintf(my_buff, "{%.11lo/%.11lo:%#.8g}", (long)p[1-q], (long)p[q], double_float_val(u)); } else sprintf(my_buff, "%#.*g", (int)print_precision, double_float_val(u)); break; #ifdef COMMON case TYPE_LONG_FLOAT: sprintf(my_buff, "%#.17g", (double)long_float_val(u)); break; #endif default: sprintf(my_buff, "?%.8lx?", (long)(uint32_t)u); break; } /* * I want to trim off trailing zeros, but ensure I leave a digit after the * decimal point. Things are made more complicated by the presence of an * exponent. Note that the '#' in the format conversions should mean that * I ALWAYS have a '.' in the number that has been printed. However on some * systems this proves not to be the case - in particular IEEE infinities * (and maybe NaNs?) get displayed without a '.' in some environments where * they are supported. I also see that some C libraries in some of the cases * I generate above dump out nonsense like 0.0e+000 with unreasonably wide * exponents, so I will try to rationalise that sort of mess too. */ #ifdef COMMON float_print_tidyup: #endif { int i = 0, j, c; while ((c = my_buff[i]) != 0 && c != '.') i++; if (c == 0) break; /* No '.' found, so leave unaltered */ j = i+1; /* Find the end of the fraction (= end of number or start of exponent) */ while ((c = my_buff[j]) != 'e' && c != 0) j++; if (c == 'e') { /* check for leading zeros in an exponent component */ while (my_buff[j+1] == '+' || my_buff[j+1] == '0') { int m = j+1; for (;;) { if ((my_buff[m] = my_buff[m+1]) == 0) break; m++; } } if (my_buff[j+1] == '-') /* kill leading zeros after '-' */ { while (my_buff[j+2] == '0') { int m = j+2; for (;;) { if ((my_buff[m] = my_buff[m+1]) == 0) break; m++; } } if (my_buff[j+2] == 0) my_buff[j+1] = 0; } if (my_buff[j+1] == 0) my_buff[j] = 0; /* "e" now at end? */ } k = j - 1; if (k == i) /* no digits after the '.' - push in a '0' */ { int l = j; while (my_buff[l] != 0) l++; while (l >= j) { my_buff[l+1] = my_buff[l]; l--; } my_buff[j++] = '0'; } else /* Scan back past any trailing zeroes */ { i++; while (k > i && my_buff[k] == '0') k--; /* Copy data down to strip out the unnecessary '0' characters */ if (k != j-1) { k++; while ((my_buff[k++] = my_buff[j++]) != 0) /* nothing */ ; } } } /* * For my purposes I do not want to see "-0.0" - it causes muddle and loses * portability. I know that losing the information here removes a facility * from people but it also removes pain from naive users! */ if (strcmp(my_buff, "-0.0") == 0) strcpy(my_buff, "0.0"); break; case TAG_NUMBERS: if (is_bignum(u)) { if (escaped_printing & escape_hex) print_bighexoctbin(u, 16, escape_width(escaped_printing), blankp, (escaped_printing & escape_nolinebreak) || tmprint_flag); else if (escaped_printing & escape_octal) print_bighexoctbin(u, 8, escape_width(escaped_printing), blankp, (escaped_printing & escape_nolinebreak) || tmprint_flag); else if (escaped_printing & escape_binary) print_bighexoctbin(u, 2, escape_width(escaped_printing), blankp, (escaped_printing & escape_nolinebreak) || tmprint_flag); else print_bignum(u, blankp, (escaped_printing & escape_nolinebreak) || tmprint_flag); return; } #ifdef COMMON else if (is_ratio(u)) { push(u); /* * Here I have a line-break problem --- I do not measure the size of the * denominator, and hence may well split a line between numerator and * denominator. This would be HORRID. I guess that the correct recipe will * involve measuring the size of the denominator first... Let's not bother * just at the moment. */ internal_prin(numerator(stack[0]), blankp); outprefix(NO, 1); putc_stream('/', active_stream); pop(u); internal_prin(denominator(u), 0); return; } else if (is_complex(u)) { push(u); outprefix(blankp, 3); putc_stream('#', active_stream), putc_stream('C', active_stream); putc_stream('(', active_stream); nil = C_nil; if (exception_pending()) { popv(1); return; } internal_prin(real_part(stack[0]), 0); pop(u); internal_prin(imag_part(u), 1); outprefix(NO, 1); putc_stream(')', active_stream); return; } #endif /* Else drop through to treat as an error */ default: error_case: sprintf(my_buff, "?%.8lx?", (long)(uint32_t)u); break; } print_my_buff: { char *p = my_buff; int ch; outprefix(blankp, strlen(my_buff)); while ((ch = *p++) != 0) putc_stream(ch, active_stream); } return; } Lisp_Object prin(Lisp_Object u) { nil_as_base escaped_printing = escape_yes; push(u); active_stream = qvalue(standard_output); if (!is_stream(active_stream)) active_stream = qvalue(terminal_io); if (!is_stream(active_stream)) active_stream = lisp_terminal_io; internal_prin(u, 0); pop(u); return u; } void prin_to_terminal(Lisp_Object u) { Lisp_Object nil = C_nil; escaped_printing = escape_yes; active_stream = qvalue(terminal_io); if (!is_stream(active_stream)) active_stream = lisp_terminal_io; internal_prin(u, 0); ignore_exception(); ensure_screen(); /* * The various "prin_to_xxx()" functions here are generally used (only) for * diagnostic printing. So to try to keep interaction as smooth as possible * in such cases I arrange that the operating system (eg window manager) will * be polled rather soon... */ if (countdown > 5) countdown = 5; } void prin_to_stdout(Lisp_Object u) { Lisp_Object nil = C_nil; escaped_printing = escape_yes; active_stream = qvalue(standard_output); if (!is_stream(active_stream)) active_stream = lisp_standard_output; internal_prin(u, 0); ignore_exception(); ensure_screen(); if (countdown > 5) countdown = 5; } void prin_to_error(Lisp_Object u) { Lisp_Object nil = C_nil; escaped_printing = escape_yes; active_stream = qvalue(error_output); if (!is_stream(active_stream)) active_stream = lisp_error_output; internal_prin(u, 0); ignore_exception(); ensure_screen(); if (countdown > 5) countdown = 5; } void prin_to_trace(Lisp_Object u) { Lisp_Object nil = C_nil; escaped_printing = escape_yes; active_stream = qvalue(trace_output); if (!is_stream(active_stream)) active_stream = lisp_trace_output; internal_prin(u, 0); ignore_exception(); ensure_screen(); if (countdown > 5) countdown = 5; } void prin_to_debug(Lisp_Object u) { Lisp_Object nil = C_nil; escaped_printing = escape_yes; active_stream = qvalue(debug_io); if (!is_stream(active_stream)) active_stream = lisp_debug_io; internal_prin(u, 0); ignore_exception(); ensure_screen(); if (countdown > 5) countdown = 5; } void prin_to_query(Lisp_Object u) { Lisp_Object nil = C_nil; escaped_printing = escape_yes; active_stream = qvalue(query_io); if (!is_stream(active_stream)) active_stream = lisp_query_io; internal_prin(u, 0); ignore_exception(); ensure_screen(); if (countdown > 5) countdown = 5; } void loop_print_stdout(Lisp_Object o) { Lisp_Object nil = C_nil; int32_t sx = exit_reason; one_args *f; Lisp_Object lp = qvalue(traceprint_symbol); if (lp == nil || lp == unset_var) lp = prinl_symbol; if (!is_symbol(lp) || (f = qfn1(lp)) == undefined1) prin_to_stdout(o); else { CSLbool bad = NO; Lisp_Object env = qenv(lp); push2(lp, env); ifn1(lp) = (intptr_t)undefined1; /* To avoid recursion if it fails */ qenv(lp) = lp; /* make it an undefined function */ (*f)(env, o); nil = C_nil; if (exception_pending()) flip_exception(), bad = YES; pop2(env, lp); if (!bad) ifn1(lp) = (intptr_t)f, qenv(lp) = env; /* Restore if OK */ } exit_reason = sx; } void loop_print_error(Lisp_Object o) { nil_as_base Lisp_Object w = qvalue(standard_output); push(w); if (is_stream(qvalue(error_output))) qvalue(standard_output) = qvalue(error_output); loop_print_stdout(o); pop(w); qvalue(standard_output) = w; #ifdef COMMON /* * This is to help me debug in the face of low level system crashes */ if (spool_file) fflush(spool_file); #endif } void loop_print_trace(Lisp_Object o) { nil_as_base Lisp_Object w = qvalue(standard_output); push(w); if (is_stream(qvalue(trace_output))) qvalue(standard_output) = qvalue(trace_output); loop_print_stdout(o); pop(w); qvalue(standard_output) = w; #ifdef COMMON /* * This is to help me debug in the face of low level system crashes */ if (spool_file) fflush(spool_file); #endif } void loop_print_debug(Lisp_Object o) { nil_as_base Lisp_Object w = qvalue(standard_output); push(w); if (is_stream(qvalue(debug_io))) qvalue(standard_output) = qvalue(debug_io); loop_print_stdout(o); pop(w); qvalue(standard_output) = w; } void loop_print_query(Lisp_Object o) { nil_as_base Lisp_Object w = qvalue(standard_output); push(w); if (is_stream(qvalue(query_io))) qvalue(standard_output) = qvalue(query_io); loop_print_stdout(o); pop(w); qvalue(standard_output) = w; } void loop_print_terminal(Lisp_Object o) { nil_as_base Lisp_Object w = qvalue(standard_output); push(w); if (is_stream(qvalue(terminal_io))) qvalue(standard_output) = qvalue(terminal_io); loop_print_stdout(o); pop(w); qvalue(standard_output) = w; } Lisp_Object prinraw(Lisp_Object u) { Header h; int32_t len, i; char b[40], *p; nil_as_base push(u); active_stream = qvalue(standard_output); if (!is_stream(active_stream)) active_stream = qvalue(terminal_io); if (!is_stream(active_stream)) active_stream = lisp_terminal_io; if (is_fixnum(u)) { /* * The following line wants to print a long-long 64-bit value but the * format specifier %.16llx is not universally available, so I use two 32-bit * chunks. */ unsigned long long w = (unsigned long long)u; unsigned long long hi = w >> 32, lo = w; sprintf(b, "%.8x%.8x", (int)hi, (int)lo); for (p=b; *p!=0; p++) putc_stream(*p, active_stream); } else if (!is_numbers(u) || type_of_header(h = numhdr(u)) != TYPE_BIGNUM) { for (i=0; i<11; i++) putc_stream("<NotNumber>"[i], active_stream); } else { len = length_of_header(h); for (i=CELL; i<len; i+=4) { sprintf(b, "%.8x ", bignum_digits(u)[(i-CELL)/4]); for (p=b; *p!=0; p++) putc_stream(*p, active_stream); } } pop(u); return u; } static Lisp_Object prinhex(Lisp_Object u, int n) { nil_as_base escaped_printing = escape_yes+escape_hex+((n & 0x3f)<<8); push(u); active_stream = qvalue(standard_output); if (!is_stream(active_stream)) active_stream = qvalue(terminal_io); if (!is_stream(active_stream)) active_stream = lisp_terminal_io; internal_prin(u, 0); pop(u); return u; } static Lisp_Object prinoctal(Lisp_Object u, int n) { nil_as_base escaped_printing = escape_yes+escape_octal+((n & 0x3f)<<8); push(u); active_stream = qvalue(standard_output); if (!is_stream(active_stream)) active_stream = qvalue(terminal_io); if (!is_stream(active_stream)) active_stream = lisp_terminal_io; internal_prin(u, 0); pop(u); return u; } static Lisp_Object prinbinary(Lisp_Object u, int n) { nil_as_base escaped_printing = escape_yes+escape_binary+((n & 0x3f)<<8); push(u); active_stream = qvalue(standard_output); if (!is_stream(active_stream)) active_stream = qvalue(terminal_io); if (!is_stream(active_stream)) active_stream = lisp_terminal_io; internal_prin(u, 0); pop(u); return u; } Lisp_Object princ(Lisp_Object u) { nil_as_base escaped_printing = 0; push(u); active_stream = qvalue(standard_output); if (!is_stream(active_stream)) active_stream = qvalue(terminal_io); if (!is_stream(active_stream)) active_stream = lisp_terminal_io; internal_prin(u, 0); pop(u); return u; } Lisp_Object print(Lisp_Object u) { nil_as_base Lisp_Object stream = qvalue(standard_output); push(u); escaped_printing = escape_yes; if (!is_stream(stream)) stream = qvalue(terminal_io); if (!is_stream(stream)) stream = lisp_terminal_io; active_stream = stream; putc_stream('\n', stream); internal_prin(u, 0); pop(u); return u; } Lisp_Object printc(Lisp_Object u) { nil_as_base Lisp_Object stream = qvalue(standard_output); push(u); escaped_printing = 0; if (!is_stream(stream)) stream = qvalue(terminal_io); if (!is_stream(stream)) stream = lisp_terminal_io; active_stream = stream; putc_stream('\n', stream); internal_prin(u, 0); pop(u); return u; } void freshline_trace(void) { nil_as_base if (other_write_action(WRITE_GET_INFO+WRITE_GET_COLUMN, qvalue(trace_output)) != 0) putc_stream('\n', qvalue(trace_output)); } void freshline_debug(void) { nil_as_base if (other_write_action(WRITE_GET_INFO+WRITE_GET_COLUMN, qvalue(debug_io)) != 0) putc_stream('\n', qvalue(debug_io)); } int char_to_list(int c, Lisp_Object f) { Lisp_Object k, nil = C_nil; /* * return at once if a previous call raised an exception */ if (exception_pending()) return 1; k = elt(charvec, c & 0xff); if (k == nil) { celt(boffo, 0) = (char)c; push(f); /* * It could very well be that in Common Lisp I ought to generate a list of * character objects here. As it is I hand back symbols, but I do take care * that they are in the LISP package. */ k = iintern(boffo, 1, lisp_package, 0); pop(f); nil = C_nil; if (exception_pending()) return 1; elt(charvec, c & 0xff) = k; } push(f); k = cons(k, stream_write_data(f)); pop(f); nil = C_nil; if (!exception_pending()) { stream_write_data(f) = k; return 0; } else return 1; } static Lisp_Object explode(Lisp_Object u) { Lisp_Object nil = C_nil; stream_write_data(lisp_work_stream) = nil; set_stream_write_fn(lisp_work_stream, char_to_list); set_stream_write_other(lisp_work_stream, write_action_list); active_stream = lisp_work_stream; internal_prin(u, 0); errexit(); u = stream_write_data(lisp_work_stream); stream_write_data(lisp_work_stream) = nil; return nreverse(u); } static unsigned char checksum_buffer[64]; static int checksum_count; int char_to_checksum(int c, Lisp_Object f) { Lisp_Object nil = C_nil; /* * return at once if a previous call raised an exception */ CSL_IGNORE(f); if (exception_pending()) return 1; checksum_buffer[checksum_count++] = (char)c; if (checksum_count == sizeof(checksum_buffer)) { CSL_MD5_Update(checksum_buffer, sizeof(checksum_buffer)); checksum_count = 0; } return 0; } void checksum(Lisp_Object u) { Lisp_Object nil = C_nil; escaped_printing = escape_yes+escape_nolinebreak+escape_checksum; set_stream_write_fn(lisp_work_stream, char_to_checksum); set_stream_write_other(lisp_work_stream, write_action_list); /* sic */ active_stream = lisp_work_stream; CSL_MD5_Init(); local_gensym_count = checksum_count = 0; internal_prin(u, 0); if (exception_pending()) return; stream_write_data(lisp_work_stream) = nil; if (checksum_count != 0) CSL_MD5_Update(checksum_buffer, checksum_count); } int code_to_list(int c, Lisp_Object f) { Lisp_Object k, nil = C_nil; /* * return at once if a previous call raised an exception */ if (exception_pending()) return 1; k = fixnum_of_int((int32_t)c); push(f); k = cons(k, stream_write_data(f)); pop(f); nil = C_nil; if (!exception_pending()) { stream_write_data(f) = k; stream_char_pos(f)++; return 0; } else return 1; } static Lisp_Object exploden(Lisp_Object u) { Lisp_Object nil = C_nil; stream_write_data(lisp_work_stream) = nil; set_stream_write_fn(lisp_work_stream, code_to_list); set_stream_write_other(lisp_work_stream, write_action_list); active_stream = lisp_work_stream; internal_prin(u, 0); errexit(); u = stream_write_data(lisp_work_stream); stream_write_data(lisp_work_stream) = nil; return nreverse(u); } /* * To cope with the needs of windowed implementations I am (unilaterally) * altering the specification of the LINELENGTH function that I implement. * The new rules are: * (linelength nil) returns current width, always an integer * (linelength n) sets new with to n, returns old * (linelength T) sets new width to default for current stream, * and returns old. * the "old" value returned in the last two cases will often be the current * linelength as returnd by (linelength nil), but it CAN be the value T. * On some windowed systems after (linelength T) the value of (linelength nil) * will track changes that the user makes by re-sizing the main output * window on their screen. The linelength function inspects and sets * information for the current standard output stream, and separate * record is kept of the linelength associated with each stream. */ Lisp_Object Llinelength(Lisp_Object nil, Lisp_Object a) { int32_t oll; Lisp_Object stream = qvalue(standard_output); if (!is_stream(stream)) stream = qvalue(terminal_io); if (!is_stream(stream)) stream = lisp_terminal_io; if (a == nil) oll = other_write_action(WRITE_GET_INFO+WRITE_GET_LINE_LENGTH, stream); else if (a == lisp_true) oll = other_write_action(WRITE_SET_LINELENGTH_DEFAULT, stream); else if (!is_fixnum(a)) return aerror1("linelength", a); else { oll = int_of_fixnum(a); if (oll < 10) oll = 10; oll = other_write_action(WRITE_SET_LINELENGTH | oll, stream); } if (oll == 0x80000000) return onevalue(lisp_true); else return onevalue(fixnum_of_int(oll)); } static Lisp_Object MS_CDECL Llinelength0(Lisp_Object nil, int nargs, ...) { argcheck(nargs, 0, "linelength"); return Llinelength(nil, nil); } Lisp_Object MS_CDECL Lprint_imports(Lisp_Object nil, int nargs, ...) { char *p; const char *s; int i, ch; Lisp_Object stream; argcheck(nargs, 0, "print-imports"); stream = qvalue(standard_output); if (!is_stream(stream)) stream = qvalue(terminal_io); if (!is_stream(stream)) stream = lisp_terminal_io; s = fwin_full_program_name; i = strlen(s)-1; while (i>=0 && s[i]!='/' && s[i]!='\\') i--; s = s + (i + 1); for (i=0; (p=import_data[i])!=NULL; i++) { const char *w = s; putc_stream(' ', stream); while (*w != 0) putc_stream(*w++, stream); putc_stream('.', stream); while ((ch = *p++) != 0) putc_stream(ch, stream); putc_stream('\n', stream); } return onevalue(nil); } Lisp_Object MS_CDECL Lprint_csl_headers(Lisp_Object nil, int nargs, ...) { char *p; int i, ch; Lisp_Object stream; argcheck(nargs, 0, "print-csl-headers"); stream = qvalue(standard_output); if (!is_stream(stream)) stream = qvalue(terminal_io); if (!is_stream(stream)) stream = lisp_terminal_io; for (i=0; (p=csl_headers[i])!=NULL; i++) { while ((ch = *p++) != 0) putc_stream(ch, stream); putc_stream('\n', stream); } return onevalue(nil); } Lisp_Object MS_CDECL Lprint_config_header(Lisp_Object nil, int nargs, ...) { char *p; int i, ch; Lisp_Object stream; argcheck(nargs, 0, "print-config-header"); stream = qvalue(standard_output); if (!is_stream(stream)) stream = qvalue(terminal_io); if (!is_stream(stream)) stream = lisp_terminal_io; for (i=0; (p=config_header[i])!=NULL; i++) { while ((ch = *p++) != 0) putc_stream(ch, stream); putc_stream('\n', stream); } return onevalue(nil); } Lisp_Object Lprin(Lisp_Object nil, Lisp_Object a) { push(a); escaped_printing = escape_yes; active_stream = qvalue(standard_output); if (!is_stream(active_stream)) active_stream = qvalue(terminal_io); if (!is_stream(active_stream)) active_stream = lisp_terminal_io; internal_prin(a, 0); pop(a); errexit(); return onevalue(a); } static Lisp_Object Lprinraw(Lisp_Object nil, Lisp_Object a) { push(a); prinraw(a); pop(a); errexit(); return onevalue(a); } static Lisp_Object Lprinhex(Lisp_Object nil, Lisp_Object a) { push(a); prinhex(a, 0); pop(a); errexit(); return onevalue(a); } static Lisp_Object Lprinoctal(Lisp_Object nil, Lisp_Object a) { push(a); prinoctal(a, 0); pop(a); errexit(); return onevalue(a); } static Lisp_Object Lprinbinary(Lisp_Object nil, Lisp_Object a) { push(a); prinbinary(a, 0); pop(a); errexit(); return onevalue(a); } static Lisp_Object Lprinhex2(Lisp_Object nil, Lisp_Object a, Lisp_Object b) { if (!is_fixnum(b)) return aerror1("prinhex", b); push(a); prinhex(a, int_of_fixnum(b)); pop(a); errexit(); return onevalue(a); } static Lisp_Object Lprinoctal2(Lisp_Object nil, Lisp_Object a, Lisp_Object b) { if (!is_fixnum(b)) return aerror1("prinoctal", b); push(a); prinoctal(a, int_of_fixnum(b)); pop(a); errexit(); return onevalue(a); } static Lisp_Object Lprinbinary2(Lisp_Object nil, Lisp_Object a, Lisp_Object b) { if (!is_fixnum(b)) return aerror1("prinbinary", b); push(a); prinbinary(a, int_of_fixnum(b)); pop(a); errexit(); return onevalue(a); } Lisp_Object MS_CDECL Lposn(Lisp_Object nil, int nargs, ...) { CSL_IGNORE(nil); argcheck(nargs, 0, "posn"); return onevalue(fixnum_of_int((int32_t) other_write_action(WRITE_GET_INFO+WRITE_GET_COLUMN, qvalue(standard_output)))); } Lisp_Object Lposn_1(Lisp_Object nil, Lisp_Object stream) { CSL_IGNORE(nil); if (!is_stream(stream)) stream = qvalue(terminal_io); if (!is_stream(stream)) stream = lisp_terminal_io; return onevalue(fixnum_of_int((int32_t) other_write_action(WRITE_GET_INFO+WRITE_GET_COLUMN, stream))); } Lisp_Object MS_CDECL Llposn(Lisp_Object nil, int nargs, ...) { CSL_IGNORE(nil); argcheck(nargs, 0, "lposn"); return onevalue(fixnum_of_int(0)); } Lisp_Object Lpagelength(Lisp_Object nil, Lisp_Object a) { CSL_IGNORE(nil); return onevalue(a); } Lisp_Object Lprinc_upcase(Lisp_Object nil, Lisp_Object a) { CSL_IGNORE(nil); push(a); escaped_printing = escape_fold_up; active_stream = qvalue(standard_output); if (!is_stream(active_stream)) active_stream = qvalue(terminal_io); if (!is_stream(active_stream)) active_stream = lisp_terminal_io; internal_prin(a, 0); pop(a); errexit(); return onevalue(a); } Lisp_Object Lprinc_downcase(Lisp_Object nil, Lisp_Object a) { CSL_IGNORE(nil); push(a); escaped_printing = escape_fold_down; active_stream = qvalue(standard_output); if (!is_stream(active_stream)) active_stream = qvalue(terminal_io); if (!is_stream(active_stream)) active_stream = lisp_terminal_io; internal_prin(a, 0); pop(a); errexit(); return onevalue(a); } Lisp_Object Lprinc(Lisp_Object nil, Lisp_Object a) { CSL_IGNORE(nil); push(a); escaped_printing = 0; active_stream = qvalue(standard_output); if (!is_stream(active_stream)) active_stream = qvalue(terminal_io); if (!is_stream(active_stream)) active_stream = lisp_terminal_io; internal_prin(a, 0); pop(a); errexit(); return onevalue(a); } Lisp_Object Lprin2a(Lisp_Object nil, Lisp_Object a) { CSL_IGNORE(nil); push(a); escaped_printing = escape_nolinebreak; active_stream = qvalue(standard_output); if (!is_stream(active_stream)) active_stream = qvalue(terminal_io); if (!is_stream(active_stream)) active_stream = lisp_terminal_io; internal_prin(a, 0); pop(a); errexit(); return onevalue(a); } char memory_print_buffer[MAX_PROMPT_LENGTH]; int count_character(int c, Lisp_Object f) { int n = stream_char_pos(f); if (n < MAX_PROMPT_LENGTH-1) { memory_print_buffer[n] = (char)c; memory_print_buffer[n+1] = 0; } stream_char_pos(f) = n+1; return 0; /* indicate success */ } Lisp_Object Llengthc(Lisp_Object nil, Lisp_Object a) { CSL_IGNORE(nil); escaped_printing = escape_nolinebreak; set_stream_write_fn(lisp_work_stream, count_character); memory_print_buffer[0] = 0; set_stream_write_other(lisp_work_stream, write_action_list); stream_char_pos(lisp_work_stream) = 0; active_stream = lisp_work_stream; internal_prin(a, 0); errexit(); return onevalue(fixnum_of_int(stream_char_pos(lisp_work_stream))); } Lisp_Object Lprint(Lisp_Object nil, Lisp_Object a) { Lisp_Object stream = qvalue(standard_output); if (!is_stream(stream)) stream = qvalue(terminal_io); if (!is_stream(stream)) stream = lisp_terminal_io; push(a); #ifdef COMMON escaped_printing = escape_yes; active_stream = stream; putc_stream('\n', stream); internal_prin(a, 0); #else escaped_printing = escape_yes; active_stream = stream; internal_prin(a, 0); putc_stream('\n', active_stream); #endif pop(a); errexit(); return onevalue(a); } Lisp_Object Lprintc(Lisp_Object nil, Lisp_Object a) { Lisp_Object stream = qvalue(standard_output); CSL_IGNORE(nil); if (!is_stream(stream)) stream = qvalue(terminal_io); if (!is_stream(stream)) stream = lisp_terminal_io; push(a); #ifdef COMMON escaped_printing = 0; active_stream = stream; putc_stream('\n', stream); internal_prin(a, 0); #else escaped_printing = 0; active_stream = stream; internal_prin(a, 0); putc_stream('\n', active_stream); #endif pop(a); errexit(); return onevalue(a); } Lisp_Object MS_CDECL Lterpri(Lisp_Object nil, int nargs, ...) { Lisp_Object stream = qvalue(standard_output); argcheck(nargs, 0, "terpri"); if (!is_stream(stream)) stream = qvalue(terminal_io); if (!is_stream(stream)) stream = lisp_terminal_io; putc_stream('\n', stream); return onevalue(nil); } Lisp_Object MS_CDECL Lflush(Lisp_Object nil, int nargs, ...) { Lisp_Object stream = qvalue(standard_output); #ifdef COMMON argcheck(nargs, 0, "finish-output"); #else argcheck(nargs, 0, "flush"); #endif if (!is_stream(stream)) stream = qvalue(terminal_io); if (!is_stream(stream)) stream = lisp_terminal_io; other_write_action(WRITE_FLUSH, stream); return onevalue(nil); } Lisp_Object Lflush1(Lisp_Object nil, Lisp_Object stream) { if (!is_stream(stream)) stream = qvalue(terminal_io); if (!is_stream(stream)) stream = lisp_terminal_io; other_write_action(WRITE_FLUSH, stream); return onevalue(nil); } Lisp_Object Lttab(Lisp_Object nil, Lisp_Object a) { int32_t n; Lisp_Object stream = qvalue(standard_output); if (!is_fixnum(a)) return aerror1("ttab", a); n = int_of_fixnum(a); if (!is_stream(stream)) stream = qvalue(terminal_io); if (!is_stream(stream)) stream = lisp_terminal_io; active_stream = stream; while (other_write_action(WRITE_GET_INFO+WRITE_GET_COLUMN, stream) < n) putc_stream(' ', active_stream); return onevalue(nil); } Lisp_Object Lxtab(Lisp_Object nil, Lisp_Object a) { int32_t n; Lisp_Object stream = qvalue(standard_output); if (!is_fixnum(a)) return aerror1("xtab", a); n = int_of_fixnum(a); if (!is_stream(stream)) stream = qvalue(terminal_io); if (!is_stream(stream)) stream = lisp_terminal_io; active_stream = stream; while (n-- > 0) putc_stream(' ', active_stream); return onevalue(nil); } Lisp_Object MS_CDECL Leject(Lisp_Object nil, int nargs, ...) { Lisp_Object stream = qvalue(standard_output); argcheck(nargs, 0, "eject"); if (!is_stream(stream)) stream = qvalue(terminal_io); if (!is_stream(stream)) stream = lisp_terminal_io; putc_stream('\f', stream); return onevalue(nil); } Lisp_Object Lexplode(Lisp_Object nil, Lisp_Object a) { escaped_printing = escape_yes+escape_nolinebreak; a = explode(a); errexit(); return onevalue(a); } Lisp_Object Lexplodehex(Lisp_Object nil, Lisp_Object a) { escaped_printing = escape_yes+escape_hex+escape_nolinebreak; a = explode(a); errexit(); return onevalue(a); } Lisp_Object Lexplodeoctal(Lisp_Object nil, Lisp_Object a) { escaped_printing = escape_yes+escape_octal+escape_nolinebreak; a = explode(a); errexit(); return onevalue(a); } Lisp_Object Lexplodebinary(Lisp_Object nil, Lisp_Object a) { escaped_printing = escape_yes+escape_binary+escape_nolinebreak; a = explode(a); errexit(); return onevalue(a); } Lisp_Object Lexplodec(Lisp_Object nil, Lisp_Object a) { escaped_printing = escape_nolinebreak; a = explode(a); errexit(); return onevalue(a); } Lisp_Object Lexplode2lc(Lisp_Object nil, Lisp_Object a) { escaped_printing = escape_fold_down+escape_nolinebreak; a = explode(a); errexit(); return onevalue(a); } Lisp_Object Lexplode2uc(Lisp_Object nil, Lisp_Object a) { escaped_printing = escape_fold_up+escape_nolinebreak; a = explode(a); errexit(); return onevalue(a); } Lisp_Object Lexploden(Lisp_Object nil, Lisp_Object a) { escaped_printing = escape_yes+escape_nolinebreak; a = exploden(a); errexit(); return onevalue(a); } Lisp_Object Lexplodecn(Lisp_Object nil, Lisp_Object a) { escaped_printing = escape_nolinebreak; a = exploden(a); errexit(); return onevalue(a); } Lisp_Object Lexplode2lcn(Lisp_Object nil, Lisp_Object a) { escaped_printing = escape_fold_down+escape_nolinebreak; a = exploden(a); errexit(); return onevalue(a); } Lisp_Object Lexplode2ucn(Lisp_Object nil, Lisp_Object a) { escaped_printing = escape_fold_up+escape_nolinebreak; a = exploden(a); errexit(); return onevalue(a); } /* * Now a bunch of binary file access code, as required for the RAND simulation * package. Note that these are NOT smoothly integrated with the use of * variables like *standard-output* to hold file handles, but I will leave them * pending until other things are more stable... or until they are needed! */ static FILE *binary_outfile, *binary_infile; static FILE *binary_open(Lisp_Object nil, Lisp_Object name, char *dir, char *e) { FILE *file; char filename[LONGEST_LEGAL_FILENAME]; int32_t len; char *w = get_string_data(name, e, &len); nil = C_nil; if (exception_pending()) return NULL; if (len >= sizeof(filename)) len = sizeof(filename); file = open_file(filename, w, (size_t)len, dir, NULL); if (file == NULL) { error(1, err_open_failed, name); return NULL; } return file; } static Lisp_Object Lbinary_open_output(Lisp_Object nil, Lisp_Object name) { #ifdef SOCKETS if (socket_server != 0) return aerror("binary-open-output"); #endif binary_outfile = binary_open(nil, name, "wb", "binary_open_output"); errexit(); return onevalue(nil); } int binary_outchar(int c, Lisp_Object dummy) { CSL_IGNORE(dummy); if (binary_outfile == NULL) return 1; putc(c, binary_outfile); return 0; /* indicate success */ } static Lisp_Object Lbinary_prin1(Lisp_Object nil, Lisp_Object a) { push(a); escaped_printing = escape_yes; set_stream_write_fn(lisp_work_stream, binary_outchar); set_stream_write_other(lisp_work_stream, write_action_file); set_stream_file(lisp_work_stream, binary_outfile); active_stream = lisp_work_stream; internal_prin(a, 0); pop(a); errexit(); return onevalue(a); } static Lisp_Object Lbinary_princ(Lisp_Object nil, Lisp_Object a) { CSL_IGNORE(nil); escaped_printing = 0; push(a); set_stream_write_fn(lisp_work_stream, binary_outchar); set_stream_write_other(lisp_work_stream, write_action_file); set_stream_file(lisp_work_stream, binary_outfile); active_stream = lisp_work_stream; internal_prin(a, 0); pop(a); return a; } static Lisp_Object Lbinary_prinbyte(Lisp_Object nil, Lisp_Object a) { int x; if (binary_outfile == NULL) return onevalue(nil); if (!is_fixnum(a)) return aerror1("binary_prinbyte", a); x = (int)int_of_fixnum(a); putc(x, binary_outfile); return onevalue(nil); } static Lisp_Object Lbinary_prin2(Lisp_Object nil, Lisp_Object a) { uint32_t x; if (binary_outfile == NULL) return onevalue(nil); if (!is_fixnum(a)) return aerror1("binary_prin2", a); x = int_of_fixnum(a); putc((int)(x >> 8), binary_outfile); putc((int)x, binary_outfile); return onevalue(nil); } static Lisp_Object Lbinary_prin3(Lisp_Object nil, Lisp_Object a) { uint32_t x; if (binary_outfile == NULL) return onevalue(nil); if (!is_fixnum(a)) return aerror1("binary_prin3", a); x = int_of_fixnum(a); putc((int)(x >> 16), binary_outfile); putc((int)(x >> 8), binary_outfile); putc((int)x, binary_outfile); return onevalue(nil); } static Lisp_Object Lbinary_prinfloat(Lisp_Object nil, Lisp_Object a) { uint32_t *w, x; if (binary_outfile == NULL) return onevalue(nil); if (!is_float(a)) return aerror1("binary_prinfloat", a); w = (uint32_t *)&double_float_val(a); x = w[0]; putc((int)(x >> 24), binary_outfile); putc((int)(x >> 16), binary_outfile); putc((int)(x >> 8), binary_outfile); putc((int)x, binary_outfile); x = w[1]; putc((int)(x >> 24), binary_outfile); putc((int)(x >> 16), binary_outfile); putc((int)(x >> 8), binary_outfile); putc((int)x, binary_outfile); return onevalue(nil); } static Lisp_Object MS_CDECL Lbinary_terpri(Lisp_Object nil, int nargs, ...) { argcheck(nargs, 0, "binary_terpri"); if (binary_outfile != NULL) putc('\n', binary_outfile); return onevalue(nil); } static Lisp_Object MS_CDECL Lbinary_close_output(Lisp_Object nil, int nargs, ...) { argcheck(nargs, 0, "binary-close-output"); if (binary_outfile != NULL) { fclose(binary_outfile); binary_outfile = NULL; } return onevalue(nil); } static Lisp_Object Lbinary_open_input(Lisp_Object nil, Lisp_Object name) { Lisp_Object r; FILE *fh = binary_open(nil, name, "rb", "binary_open_input"); errexit(); r = make_stream_handle(); errexit(); set_stream_read_fn(r, char_from_file); set_stream_read_other(r, read_action_file); set_stream_file(r, fh); return onevalue(r); } static Lisp_Object Lbinary_select_input(Lisp_Object nil, Lisp_Object a) { if (!is_stream(a) || stream_file(a) == NULL || stream_write_fn(a) != 0) return aerror1("binary-select-input", a); /* closed file or output file */ binary_infile = stream_file(a); return onevalue(nil); } static Lisp_Object MS_CDECL Lbinary_readbyte(Lisp_Object nil, int nargs, ...) { CSL_IGNORE(nil); argcheck(nargs, 0, "binary-readbyte"); if (binary_infile == NULL) return onevalue(fixnum_of_int(-1)); return onevalue(fixnum_of_int((int32_t)getc(binary_infile) & 0xff)); } static Lisp_Object MS_CDECL Lbinary_read2(Lisp_Object nil, int nargs, ...) { CSL_IGNORE(nil); argcheck(nargs, 0, "binary-read2"); if (binary_infile == NULL) return onevalue(fixnum_of_int(-1)); { int32_t c1 = (int32_t)getc(binary_infile) & 0xff; int32_t c2 = (int32_t)getc(binary_infile) & 0xff; return onevalue(fixnum_of_int((c1<<8) | c2)); } } static Lisp_Object MS_CDECL Lbinary_read3(Lisp_Object nil, int nargs, ...) { CSL_IGNORE(nil); argcheck(nargs, 0, "binary-read3"); if (binary_infile == NULL) return onevalue(fixnum_of_int(-1)); { int32_t c1 = (int32_t)getc(binary_infile) & 0xff; int32_t c2 = (int32_t)getc(binary_infile) & 0xff; int32_t c3 = (int32_t)getc(binary_infile) & 0xff; return onevalue(fixnum_of_int((c1<<16) | (c2<<8) | c3)); } } static Lisp_Object MS_CDECL Lbinary_read4(Lisp_Object nil, int nargs, ...) { CSL_IGNORE(nil); argcheck(nargs, 0, "binary-read4"); if (binary_infile == NULL) return onevalue(fixnum_of_int(-1)); { int32_t c1 = (int32_t)getc(binary_infile) & 0xff; int32_t c2 = (int32_t)getc(binary_infile) & 0xff; int32_t c3 = (int32_t)getc(binary_infile) & 0xff; int32_t c4 = (int32_t)getc(binary_infile) & 0xff; int32_t r = (c1 << 24) | (c2 << 16) | (c3 << 8) | c4; return onevalue(fixnum_of_int(r)); } } static Lisp_Object MS_CDECL Lbinary_readfloat(Lisp_Object nil, int nargs, ...) { Lisp_Object r = make_boxfloat(0.0, TYPE_DOUBLE_FLOAT); uint32_t w; errexit(); argcheck(nargs, 0, "binary-readfloat"); if (binary_infile == NULL) return onevalue(r); w = (int32_t)getc(binary_infile) & 0xff; w = (w<<8) | ((int32_t)getc(binary_infile) & 0xff); w = (w<<8) | ((int32_t)getc(binary_infile) & 0xff); w = (w<<8) | ((int32_t)getc(binary_infile) & 0xff); ((uint32_t *)&double_float_val(r))[0] = w; w = (int32_t)getc(binary_infile) & 0xff; w = (w<<8) | ((int32_t)getc(binary_infile) & 0xff); w = (w<<8) | ((int32_t)getc(binary_infile) & 0xff); w = (w<<8) | ((int32_t)getc(binary_infile) & 0xff); ((uint32_t *)&double_float_val(r))[1] = w; return onevalue(r); } static Lisp_Object MS_CDECL Lbinary_close_input(Lisp_Object nil, int nargs, ...) { argcheck(nargs, 0, "binary-close-input"); if (binary_infile != NULL) { fclose(binary_infile); binary_infile = NULL; } return onevalue(nil); } /* * (open-library "file" dirn) opens a new library (for use with the * fasl mechanism etc). If dirn=nil (or not specified) the library is * opened for input only. If dirn is non-nil an attempt is made to open * the library so that it can be updated, and if it does not exist to start * with it is created. The resulting handle can be passed to close-library * or used in the variables input-libraries or output-library. */ static Lisp_Object Lopen_library(Lisp_Object nil, Lisp_Object file, Lisp_Object dirn) { char filename[LONGEST_LEGAL_FILENAME]; int32_t len; CSLbool forinput = (dirn==nil); int i; char *w = get_string_data(file, "open-library", &len); errexit(); if (len >= sizeof(filename)) len = sizeof(filename)-1; memcpy(filename, w, len); filename[len] = 0; for (i=0; i<number_of_fasl_paths; i++) { if (fasl_files[i] == NULL) goto found; } if (number_of_fasl_paths>=MAX_FASL_PATHS-1) return aerror("open-library (too many open libraries)"); number_of_fasl_paths++; found: fasl_files[i] = open_pds(filename, forinput); /* * allocating space using malloc() here is dodgy, because the matching * place in close-library does not do a corresponding free() operation. */ w = (char *)malloc(strlen(filename)+1); if (w == NULL) w = "Unknown file"; else strcpy(w, filename); fasl_paths[i] = w; return onevalue(SPID_LIBRARY + (((int32_t)i)<<20)); } static Lisp_Object Lopen_library_1(Lisp_Object nil, Lisp_Object file) { return Lopen_library(nil, file, nil); } static Lisp_Object Lclose_library(Lisp_Object nil, Lisp_Object lib) { if (!is_library(lib)) return aerror1("close-library", lib); finished_with(library_number(lib)); return onevalue(nil); } static Lisp_Object Llibrary_name(Lisp_Object nil, Lisp_Object lib) { Lisp_Object a; if (!is_library(lib)) return aerror1("library-name", lib); a = make_string(fasl_paths[library_number(lib)]); errexit(); return onevalue(a); } #ifdef CJAVA extern void process_java_file(FILE *file); static Lisp_Object Ljava(Lisp_Object nil, Lisp_Object name) { char filename[LONGEST_LEGAL_FILENAME]; int32_t len; FILE *file; char *w = get_string_data(name, "java", &len); nil = C_nil; if (exception_pending()) return nil; if (len >= sizeof(filename)) len = sizeof(filename); file = open_file(filename, w, (size_t)len, "rb", NULL); if (file == NULL) { error(1, err_open_failed, name); return NULL; } process_java_file(file); fclose(file); return onevalue(nil); } #endif #ifdef SOCKETS /* * If a Winsock function fails it leaves an error code that * WSAGetLastError() can retrieve. This function converts the numeric * codes to some printable text. Still cryptic, but maybe better than * the raw numbers! */ static char error_name[32]; char *WSAErrName(int i) { switch (i) { default: sprintf(error_name, "Socket error %d", i); return error_name; #ifdef WIN32 case WSAEINTR: return "WSAEINTR"; case WSAEBADF: return "WSAEBADF"; case WSAEACCES: return "WSAEACCES"; #ifdef WSAEDISCON case WSAEDISCON: return "WSAEDISCON"; #endif case WSAEFAULT: return "WSAEFAULT"; case WSAEINVAL: return "WSAEINVAL"; case WSAEMFILE: return "WSAEMFILE"; case WSAEWOULDBLOCK: return "WSAEWOULDBLOCK"; case WSAEINPROGRESS: return "WSAEINPROGRESS"; case WSAEALREADY: return "WSAEALREADY"; case WSAENOTSOCK: return "WSAENOTSOCK"; case WSAEDESTADDRREQ: return "WSAEDESTADDRREQ"; case WSAEMSGSIZE: return "WSAEMSGSIZE"; case WSAEPROTOTYPE: return "WSAEPROTOTYPE"; case WSAENOPROTOOPT: return "WSAENOPROTOOPT"; case WSAEPROTONOSUPPORT: return "WSAEPROTONOSUPPORT"; case WSAESOCKTNOSUPPORT: return "WSAESOCKTNOSUPPORT"; case WSAEOPNOTSUPP: return "WSAEOPNOTSUPP"; case WSAEPFNOSUPPORT: return "WSAEPFNOSUPPORT"; case WSAEAFNOSUPPORT: return "WSAEAFNOSUPPORT"; case WSAEADDRINUSE: return "WSAEADDRINUSE"; case WSAEADDRNOTAVAIL: return "WSAEADDRNOTAVAIL"; case WSAENETDOWN: return "WSAENETDOWN"; case WSAENETUNREACH: return "WSAENETUNREACH"; case WSAENETRESET: return "WSAENETRESET"; case WSAECONNABORTED: return "WSAECONNABORTED"; case WSAECONNRESET: return "WSAECONNRESET"; case WSAENOBUFS: return "WSAENOBUFS"; case WSAEISCONN: return "WSAEISCONN"; case WSAENOTCONN: return "WSAENOTCONN"; case WSAESHUTDOWN: return "WSAESHUTDOWN"; case WSAETOOMANYREFS: return "WSAETOOMANYREFS"; case WSAETIMEDOUT: return "WSAETIMEDOUT"; case WSAECONNREFUSED: return "WSAECONNREFUSED"; case WSAELOOP: return "WSAELOOP"; case WSAENAMETOOLONG: return "WSAENAMETOOLONG"; case WSAEHOSTDOWN: return "WSAEHOSTDOWN"; case WSAEHOSTUNREACH: return "WSAEHOSTUNREACH"; case WSASYSNOTREADY: return "WSASYSNOTREADY"; case WSAVERNOTSUPPORTED: return "WSAVERNOTSUPPORTED"; case WSANOTINITIALISED: return "WSANOTINITIALISED"; case WSAHOST_NOT_FOUND: return "WSAHOST_NOT_FOUND"; case WSATRY_AGAIN: return "WSATRY_AGAIN"; case WSANO_RECOVERY: return "WSANO_RECOVERY"; case WSANO_DATA: return "WSANO_DATA"; #else /* * When I run under Unix I display both the Unix and Windows form of the * error code. I guess that shows you which of those platforms is the one * I am doing initial development on! */ case EINTR: return "WSAEINTR/EINTR"; case EBADF: return "WSAEBADF/EBADF"; case EACCES: return "WSAEACCES/EACCES"; case EFAULT: return "WSAEFAULT/EFAULT"; case EINVAL: return "WSAEINVAL/EINVAL"; case EMFILE: return "WSAEMFILE/EMFILE"; case EWOULDBLOCK: return "WSAEWOULDBLOCK/EWOULDBLOCK"; case EINPROGRESS: return "WSAEINPROGRESS/EINPROGRESS"; case EALREADY: return "WSAEALREADY/EALREADY"; case ENOTSOCK: return "WSAENOTSOCK/ENOTSOCK"; case EDESTADDRREQ: return "WSAEDESTADDRREQ/EDESTADDRREQ"; case EMSGSIZE: return "WSAEMSGSIZE/EMSGSIZE"; case EPROTOTYPE: return "WSAEPROTOTYPE/EPROTOTYPE"; case ENOPROTOOPT: return "WSAENOPROTOOPT/ENOPROTOOPT"; case EPROTONOSUPPORT: return "WSAEPROTONOSUPPORT/EPROTONOSUPPORT"; case ESOCKTNOSUPPORT: return "WSAESOCKTNOSUPPORT/ESOCKTNOSUPPORT"; case EOPNOTSUPP: return "WSAEOPNOTSUPP/EOPNOTSUPP"; case EPFNOSUPPORT: return "WSAEPFNOSUPPORT/EPFNOSUPPORT"; case EAFNOSUPPORT: return "WSAEAFNOSUPPORT/EAFNOSUPPORT"; case EADDRINUSE: return "WSAEADDRINUSE/EADDRINUSE"; case EADDRNOTAVAIL: return "WSAEADDRNOTAVAIL/EADDRNOTAVAIL"; case ENETDOWN: return "WSAENETDOWN/ENETDOWN"; case ENETUNREACH: return "WSAENETUNREACH/ENETUNREACH"; case ENETRESET: return "WSAENETRESET/ENETRESET"; case ECONNABORTED: return "WSAECONNABORTED/ECONNABORTED"; case ECONNRESET: return "WSAECONNRESET/ECONNRESET"; case ENOBUFS: return "WSAENOBUFS/ENOBUFS"; case EISCONN: return "WSAEISCONN/EISCONN"; case ENOTCONN: return "WSAENOTCONN/ENOTCONN"; case ESHUTDOWN: return "WSAESHUTDOWN/ESHUTDOWN"; case ETOOMANYREFS: return "WSAETOOMANYREFS/ETOOMANYREFS"; case ETIMEDOUT: return "WSAETIMEDOUT/ETIMEDOUT"; case ECONNREFUSED: return "WSAECONNREFUSED/ECONNREFUSED"; case ELOOP: return "WSAELOOP/ELOOP"; case ENAMETOOLONG: return "WSAENAMETOOLONG/ENAMETOOLONG"; case EHOSTDOWN: return "WSAEHOSTDOWN/EHOSTDOWN"; case EHOSTUNREACH: return "WSAEHOSTUNREACH/EHOSTUNREACH"; case HOST_NOT_FOUND: return "WSAHOST_NOT_FOUND/HOST_NOT_FOUND"; case TRY_AGAIN: return "WSATRY_AGAIN/TRY_AGAIN"; case NO_RECOVERY: return "WSANO_RECOVERY/NO_RECOVERY"; #ifdef never /* * Duplicated EINTR, at least on Linux. */ case NO_DATA: return "WSANO_DATA/NO_DATA"; #endif #endif /* WIN32 */ } } int ensure_sockets_ready(void) { if (!sockets_ready) { #ifdef WIN32 /* * Under Windows the socket stuff is not automatically active, so some * system calls have to be made at the start of a run. I demand a * Winsock 1.1, and fail if that is not available. */ WSADATA wsadata; int i = WSAStartup(MAKEWORD(1,1), &wsadata); if (i) return i; /* Failed to start winsock for some reason */; if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1) { WSACleanup(); return 1; /* Version 1.1 of winsock needed */ } #endif sockets_ready = 1; } return 0; } #define SOCKET_BUFFER_SIZE 256 /* * A stream attached to a socket is represented by putting the socket handle * into the field that would otherwise hold a FILE. The stream_read_data * field then holds a string. The first 4 characters of this contain * two packed integers saying how much buffered data is available, * and then there is just a chunk of buffered text. */ int char_from_socket(Lisp_Object stream) { int ch = stream_pushed_char(stream); if (ch == NOT_CHAR) { Lisp_Object w = stream_read_data(stream); int32_t sb_data = ielt32(w, 0); int sb_start = sb_data & 0xffff, sb_end = (sb_data >> 16) & 0xffff; /* * Note use of ucelt in the next line even if char is a signed type. This * is because getc() etc are expected to return an UNSIGNED char cast to * an int. */ if (sb_start != sb_end) ch = ucelt(w, sb_start++); else { ch = recv((SOCKET)(intptr_t)stream_file(stream), &celt(w, 4), SOCKET_BUFFER_SIZE, 0); if (ch == 0) return EOF; if (ch == SOCKET_ERROR) { err_printf("socket read error (%s)\n", WSAErrName(WSAGetLastError())); return EOF; } sb_start = 5; sb_end = ch + 4; ch = ucelt(w, 4); } sb_data = sb_start | (sb_end << 16); ielt32(w, 0) = sb_data; return ch; } else stream_pushed_char(stream) = NOT_CHAR; return ch; } /* * Seek and tell will be just quiet no-ops on socket streams. */ int32_t read_action_socket(int32_t op, Lisp_Object f) { if (op < -1) return 0; else if (op <= 0xff) return (stream_pushed_char(f) = op); else switch (op) { case READ_CLOSE: if (stream_file(f) == NULL) op = 0; else op = closesocket((SOCKET)(intptr_t)stream_file(f)); set_stream_read_fn(f, char_from_illegal); set_stream_read_other(f, read_action_illegal); set_stream_file(f, NULL); stream_read_data(f) = C_nil; return op; case READ_FLUSH: stream_pushed_char(f) = NOT_CHAR; return 0; default: return 0; } } int fetch_response(char *buffer, Lisp_Object r) { int i; for (i = 0; i < LONGEST_LEGAL_FILENAME; i++) { int ch = char_from_socket(r); if (ch == EOF) return 1; buffer[i] = (char)ch; if (ch == 0x0a) { buffer[i] = 0; /* * The keys returned at the start of a response line are supposed to be * case insensitive, so I fold things to lower case right here. */ for (i=0; buffer[i]!=0 && buffer[i]!=' '; i++) buffer[i] = (char)tolower(buffer[i]); return 0; } } return 1; /* fail if response was over-long */ } static Lisp_Object Lopen_url(Lisp_Object nil, Lisp_Object url) { char filename[LONGEST_LEGAL_FILENAME], filename1[LONGEST_LEGAL_FILENAME], *p; char *user, *pass, *proto, *hostaddr, *port, *path; int nuser, npass, nproto, nhostaddr, nport, npath; int32_t len; struct hostent *host; long int hostnum; SOCKET s; int i, retcode, retry_count=0; Lisp_Object r; char *w = get_string_data(url, "open-url", &len); errexit(); start_again: if (len >= sizeof(filename)) len = sizeof(filename)-1; memcpy(filename, w, len); filename[len] = 0; /* * I want to parse the URL. I leave the result as a collection of * pointers (usually to the start of text within the URL itself, but * sometimes elsewhere, together with lengths of the substrings as found. */ user = pass = proto = hostaddr = port = path = " "; nuser = npass = nproto = nhostaddr = nport = npath = 0; p = filename; /* * If the start of the URL is of the form "xyz:" with xyz alphanumeric * then that is a protocol name, and I will force it into lower case. */ for (i=0; i<len; i++) if (!isalnum(p[i])) break; if (p[i] == ':') { proto = p; nproto = i; /* Could still be zero! */ p += i+1; len -= i+1; for (i=0; i<nproto; i++) proto[i] = (char)tolower(proto[i]); } /* * After any protocol specification I may have a host name, introduced * by "//". */ if (p[0] == '/' && p[1] == '/') { p += 2; len -= 2; /* * If the URL (sans protocol) contains a "@" then I will take it to be * in the form * user:password@hostaddr/... * and will split the user bit off. This will be particularly used in the * case of FTP requests. The password will be allowed to contain ":" and * "@" characters. Furthermore I will also allow the password to be * enclosed in quote marks ("), although since I scan for the "@" from * the right and for the ":" from the left these are not needed at all, * so if I notice them here all I have to do is to discard them! */ for (i=len-1; i>=0; i--) if (p[i] == '@') break; if (i >= 0) { user = p; p += i+1; len -= i+1; while (user[nuser] != ':' && user[nuser] != '@') nuser++; if (user[nuser] == ':') { pass = user+nuser+1; npass = i - nuser - 1; if (pass[0] == '"' && pass[npass-1] == '"') pass++, npass -= 2; } } /* * Now what is left is a host, port number and path, written as * hostaddr:port/... but note that the "/" should be treated as * part of the path-name. */ hostaddr = p; for (;;) { switch (hostaddr[nhostaddr]) { default: nhostaddr++; continue; case '/': p += nhostaddr; len -= nhostaddr; break; case 0: len = 0; break; case ':': /* port number given */ port = hostaddr+nhostaddr+1; for (;;) { switch (port[nport]) { default: nport++; continue; case '/': p += nhostaddr + nport + 1; len -= nhostaddr + nport + 1; break; case 0: len = 0; break; } break; } break; } break; } } path = p; npath = len; if (npath == 0) path = "/", npath = 1; /* Default path */ /* * If a protocol was not explicitly given I will try to deduce one from the * start of the name of the hostaddr. Failing that I will just use a default. */ if (nproto == 0) { if (strncmp(hostaddr, "www.", 4) == 0 || strncmp(hostaddr, "wwwcgi.", 7) == 0) { proto = "http"; nproto = 4; } else { proto = "ftp"; nproto = 3; } } /* * If the user gave an explicit port number I will try to use it. If the * port was not numeric I ignore it and drop down to trying to use * a default port based on the selected protocol. */ if (nport != 0) { int w; memcpy(filename1, port, nport); filename1[nport] = 0; if (sscanf(filename1, "%d", &w) == 1) nport = w; else nport = 0; } if (nport == 0) { if (nproto == 3 && memcmp(proto, "ftp", 3) == 0) nport = 21; else if (nproto == 4 && memcmp(proto, "http", 4) == 0) nport = 80; /* * Elsewhere I have code that can call on an external "scp" program to support * a secure-fetch scheme, but I will NOT include that here. */ else return aerror("Unknown protocol"); } /* * If no host-name was given then the object concerned is on the * local machine. This is a funny case maybe, but I will just chain * through and open it as an ordinary file (without regard to * protocol etc). */ if (nhostaddr == 0) { FILE *file = open_file(filename1, path, (size_t)npath, "r", NULL); if (file == NULL) return onevalue(nil); push(url); r = make_stream_handle(); pop(url); errexit(); stream_type(r) = url; set_stream_file(r, file); set_stream_read_fn(r, char_from_file); set_stream_read_other(r, read_action_file); return onevalue(r); } if (nproto == 3 && strcmp(proto, "ftp") == 0 && nuser == 0) { user = "anonymous"; nuser = strlen(user); if (npass == 0) { pass = "acn1@cam.ac.uk"; npass = strlen(pass); } } #ifdef DEBUG /* * The trace print here is not needed in the long term but certainly * helps while I am doing initial tests. */ trace_printf( "User <%.*s> Pass <%.*s> Proto <%.*s>\n" "Host <%.*s> Port <%d> Path <%.*s>\n", nuser, user, npass, pass, nproto, proto, nhostaddr, hostaddr, nport, npath, path); #endif if (ensure_sockets_ready() != 0) return nil; memcpy(filename1, hostaddr, nhostaddr); filename1[nhostaddr] = 0; /* I try to accept either "." form or named host specifications */ hostnum = inet_addr(filename1); if (hostnum == INADDR_NONE) { host = gethostbyname(filename1); if (host != NULL) hostnum = ((struct in_addr *)host->h_addr)->s_addr; } if (hostnum == INADDR_NONE) { err_printf("Host not found (%s)\n", WSAErrName(WSAGetLastError())); return onevalue(nil); } s = socket(PF_INET, SOCK_STREAM, 0); /* Make a new socket */ { struct sockaddr_in sin; memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = htons(nport); sin.sin_addr.s_addr = hostnum; /* * Because there can be quite tedious delays in network fetches I will * log that I am trying to make contact. */ trace_printf("Contacting %.*s...\n", nhostaddr, hostaddr); ensure_screen(); if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) == SOCKET_ERROR) { err_printf("connect failed %s\n", WSAErrName(WSAGetLastError())); closesocket(s); return onevalue(nil); } trace_printf("Connection created\n"); } sprintf(filename1, "GET %.*s HTTP/1.0\x0d\x0a\x0d\x0a", npath, path); /* MD addition from webcore.c*/ i = strlen(filename1); /* * Certainly if the Web server I am accessing is the one that comes as * standard with Windows NT I need to reassure it that I want the document * returned to me WHATEVER its media type is. If I do not add in the * line "Accept: *|*" the GET request will only allow me to fetch simple * text (?) * Note that above I write "*|*" where I only really mean a "/" in the * middle but where C comment conventions intrude! */ sprintf(&filename1[i], "Accept: */*\x0d\x0a\x0d\x0a"); if (send(s, filename1, strlen(filename1), 0) == SOCKET_ERROR) { err_printf("Send error (%s)\n", WSAErrName(WSAGetLastError())); closesocket(s); return onevalue(nil); } push(url); r = make_stream_handle(); pop(url); errexit(); stream_type(r) = url; push(r); url = getvector(TAG_VECTOR, TYPE_STRING, CELL+4+SOCKET_BUFFER_SIZE); pop(r); errexit(); ielt32(url, 0) = 0; stream_read_data(r) = url; set_stream_file(r, (FILE *)(intptr_t)s); set_stream_read_fn(r, char_from_socket); set_stream_read_other(r, read_action_socket); /* Now fetch the status line. */ if (fetch_response(filename1, r)) { err_printf("Error fetching status line from the server\n"); Lclose(nil,r); return onevalue(nil); } /* * I check if the first line returned is in the form "HTTP/n.n nnn " and if * it is not I assume that I have reached an HTTP/0.9 server and all the * text that comes back will be the body. */ { int major, minor; /* * I will not worry much about just which version of HTTP the system reports * that it is using, provided it says something! I expect to see the return * code as a three digit number. I verify that it is in the range 0 to 999 but * do not check for (and thus reject) illegal responses such as 0000200. */ if (sscanf(filename1, "http/%d.%d %d", &major, &minor, &retcode) != 3 || retcode < 0 || retcode > 999) { err_printf("Bad protocol specification returned\n"); err_printf(filename1); /* So I can see what did come back */ Lclose(nil,r); return onevalue(nil); } } /* * In this code I treat all unexpected responses as errors and I do not * attempt to continue. This is sometimes going to be overly pessimistic * and RFC1945 tells me that I should treat unidentified codes as the * n00 variant thereupon. */ switch (retcode) { default:retcode = 0; break; case 200: break; /* A success code for GET requests */ case 301: /* Redirection request */ case 302: do { if (fetch_response(filename1, r)) { err_printf("Unexpected response from the server\n"); retcode = 0; break; } if (filename1[0] == 0) { err_printf("Document has moved, but I can not trace it\n"); retcode = 0; break; } } while (memcmp(filename1, "location: ", 10) != 0); if (retcode == 0) break; /* * At present I take a somewhat simplistic view of redirection, and just * look for the first alternative URL and start my entire unpicking * process afresh from there. */ for (i = 10; filename1[i] == ' '; i++); w = &filename1[i]; while (filename1[i]!=' ' && filename1[i]!=0) i++; filename1[i] = 0; len = strlen(w); closesocket(s); if (++retry_count > 5) { err_printf("Apparent loop in redirection information\n"); retcode = 0; break; } goto start_again; break; case 401: err_printf("Authorisation required for this access\n"); retcode = 0; break; case 404: err_printf("Object not found\n"); retcode = 0; break; } if (retcode == 0) { Lclose(nil,r); return onevalue(nil); } /* * Skip further information returned by the server until a line containing * just the end-of-line marker is fetched */ do { for (i = 0; i < LONGEST_LEGAL_FILENAME; i++) { int ch = char_from_socket(r); if (ch == EOF) { err_printf("Error fetching additional info from the server\n"); Lclose(nil,r); return onevalue(nil); } if (ch == 0x0a) break; } } while (i > 1); return onevalue(r); } #endif /* SOCKETS */ int window_heading = 0; Lisp_Object Lwindow_heading2(Lisp_Object nil, Lisp_Object a, Lisp_Object b) { #ifdef HAVE_FWIN int32_t n, bit; char *s, txt[32]; if (is_fixnum(b)) n = int_of_fixnum(b); else n = 2; /* default to setting the right section */ if (is_vector(a) && type_of_header(vechdr(a)) == TYPE_STRING) { int32_t l = length_of_header(vechdr(a)); if (l > 30) l = 30; memcpy(txt, &celt(a, 0), l); txt[l] = 0; s = txt; } else if (b == 2) s = ""; else s = NULL; switch (n) { case 0: fwin_report_left(s); bit = 1; break; case 1: fwin_report_mid(s); bit = 2; break; default:fwin_report_right(s); bit = 4; break; } if (s == NULL || *s == 0) window_heading &= ~bit; else window_heading |= bit; #endif return onevalue(nil); } Lisp_Object Lwindow_heading1(Lisp_Object nil, Lisp_Object a) { return Lwindow_heading2(nil, a, nil); } setup_type const print_setup[] = { #ifdef CJAVA {"java", Ljava, too_many_1, wrong_no_1}, #endif #ifdef SOCKETS {"open-url", Lopen_url, too_many_1, wrong_no_1}, #endif {"window-heading", Lwindow_heading1, Lwindow_heading2, wrong_no_1}, {"eject", wrong_no_na, wrong_no_nb, Leject}, {"filep", Lfilep, too_many_1, wrong_no_1}, {"filedate", Lfiledate, too_many_1, wrong_no_1}, {"flush", Lflush1, wrong_no_nb, Lflush}, {"streamp", Lstreamp, too_many_1, wrong_no_1}, {"is-console", Lis_console, too_many_1, wrong_no_1}, {"lengthc", Llengthc, too_many_1, wrong_no_1}, {"linelength", Llinelength, too_many_1, Llinelength0}, {"lposn", wrong_no_na, wrong_no_nb, Llposn}, {"internal-open", too_few_2, Lopen, wrong_no_2}, {"open-library", Lopen_library_1, Lopen_library, wrong_no_2}, {"close-library", Lclose_library, too_many_1, wrong_no_1}, {"library-name", Llibrary_name, too_many_1, wrong_no_1}, {"create-directory", Lcreate_directory, too_many_1, wrong_no_1}, {"delete-file", Ldelete_file, too_many_1, wrong_no_1}, {"rename-file", too_few_2, Lrename_file, wrong_no_2}, {"file-readablep", Lfile_readable, too_many_1, wrong_no_1}, {"file-writeablep", Lfile_writeable, too_many_1, wrong_no_1}, {"directoryp", Ldirectoryp, too_many_1, wrong_no_1}, {"file-length", Lfile_length, too_many_1, wrong_no_1}, {"truename", Ltruename, too_many_1, wrong_no_1}, {"list-directory", Llist_directory, too_many_1, wrong_no_1}, {"chdir", Lchange_directory, too_many_1, wrong_no_1}, {"make-function-stream", Lmake_function_stream, too_many_1, wrong_no_1}, {"get-current-directory", wrong_no_na, wrong_no_nb, Lget_current_directory}, {"user-homedir-pathname", wrong_no_na, wrong_no_nb, Luser_homedir_pathname}, {"get-lisp-directory", wrong_no_na, wrong_no_nb, Lget_lisp_directory}, {"pagelength", Lpagelength, too_many_1, wrong_no_1}, {"posn", Lposn_1, wrong_no_nb, Lposn}, {"spaces", Lxtab, too_many_1, wrong_no_1}, {"terpri", wrong_no_na, wrong_no_nb, Lterpri}, {"tmpnam", Ltmpnam1, wrong_no_nb, Ltmpnam}, {"ttab", Lttab, too_many_1, wrong_no_1}, {"wrs", Lwrs, too_many_1, wrong_no_1}, {"xtab", Lxtab, too_many_1, wrong_no_1}, {"princ-upcase", Lprinc_upcase, too_many_1, wrong_no_1}, {"princ-downcase", Lprinc_downcase, too_many_1, wrong_no_1}, {"binary_open_output", Lbinary_open_output, too_many_1, wrong_no_1}, {"binary_prin1", Lbinary_prin1, too_many_1, wrong_no_1}, {"binary_princ", Lbinary_princ, too_many_1, wrong_no_1}, {"binary_prinbyte", Lbinary_prinbyte, too_many_1, wrong_no_1}, {"binary_prin2", Lbinary_prin2, too_many_1, wrong_no_1}, {"binary_prin3", Lbinary_prin3, too_many_1, wrong_no_1}, {"binary_prinfloat", Lbinary_prinfloat, too_many_1, wrong_no_1}, {"binary_terpri", wrong_no_na, wrong_no_nb, Lbinary_terpri}, {"binary_close_output", wrong_no_na, wrong_no_nb, Lbinary_close_output}, {"binary_open_input", Lbinary_open_input, too_many_1, wrong_no_1}, {"binary_select_input", Lbinary_select_input, too_many_1, wrong_no_1}, {"binary_readbyte", wrong_no_na, wrong_no_nb, Lbinary_readbyte}, {"binary_read2", wrong_no_na, wrong_no_nb, Lbinary_read2}, {"binary_read3", wrong_no_na, wrong_no_nb, Lbinary_read3}, {"binary_read4", wrong_no_na, wrong_no_nb, Lbinary_read4}, {"binary_readfloat", wrong_no_na, wrong_no_nb, Lbinary_readfloat}, {"binary_close_input", wrong_no_na, wrong_no_nb, Lbinary_close_input}, {"prinraw", Lprinraw, too_many_1, wrong_no_1}, {"prinhex", Lprinhex, Lprinhex2, wrong_no_1}, {"prinoctal", Lprinoctal, Lprinoctal2, wrong_no_1}, {"prinbinary", Lprinbinary, Lprinbinary2, wrong_no_1}, {"print-config-header", wrong_no_na, wrong_no_nb, Lprint_config_header}, {"print-csl-headers", wrong_no_na, wrong_no_nb, Lprint_csl_headers}, {"print-imports", wrong_no_na, wrong_no_nb, Lprint_imports}, {"math-display", Lmath_display, too_many_1, wrong_no_1}, #ifdef COMMON {"charpos", Lposn_1, wrong_no_nb, Lposn}, {"finish-output", Lflush1, wrong_no_nb, Lflush}, {"make-synonym-stream", Lmake_synonym_stream, too_many_1, wrong_no_1}, {"make-broadcast-stream", Lmake_broadcast_stream_1, Lmake_broadcast_stream_2, Lmake_broadcast_stream_n}, {"make-concatenated-stream",Lmake_concatenated_stream_1, Lmake_concatenated_stream_2, Lmake_concatenated_stream_n}, {"make-two-way-stream", too_few_2, Lmake_two_way_stream, wrong_no_2}, {"make-echo-stream", too_few_2, Lmake_echo_stream, wrong_no_2}, {"make-string-input-stream",Lmake_string_input_stream_1, Lmake_string_input_stream_2, Lmake_string_input_stream_n}, {"make-string-output-stream",wrong_no_na, wrong_no_nb, Lmake_string_output_stream}, {"get-output-stream-string",Lget_output_stream_string, too_many_1, wrong_no_1}, {"close", Lclose, too_many_1, wrong_no_1}, {"~tyo", Ltyo, too_many_1, wrong_no_1}, /* At least as a temporary measure I provide these in COMMON mode too */ {"explode", Lexplode, too_many_1, wrong_no_1}, {"explodec", Lexplodec, too_many_1, wrong_no_1}, {"explode2", Lexplodec, too_many_1, wrong_no_1}, {"explode2lc", Lexplode2lc, too_many_1, wrong_no_1}, {"exploden", Lexploden, too_many_1, wrong_no_1}, {"explodecn", Lexplodecn, too_many_1, wrong_no_1}, {"explode2n", Lexplodecn, too_many_1, wrong_no_1}, {"explode2lcn", Lexplode2lcn, too_many_1, wrong_no_1}, {"explodehex", Lexplodehex, too_many_1, wrong_no_1}, {"explodeoctal", Lexplodeoctal, too_many_1, wrong_no_1}, {"explodebinary", Lexplodebinary, too_many_1, wrong_no_1}, {"prin", Lprin, too_many_1, wrong_no_1}, {"prin1", Lprin, too_many_1, wrong_no_1}, {"princ", Lprinc, too_many_1, wrong_no_1}, {"prin2", Lprinc, too_many_1, wrong_no_1}, {"prin2a", Lprin2a, too_many_1, wrong_no_1}, {"print", Lprint, too_many_1, wrong_no_1}, {"printc", Lprintc, too_many_1, wrong_no_1}, {"set-print-precision", Lprint_precision, too_many_1, wrong_no_1}, #else {"close", Lclose, too_many_1, wrong_no_1}, {"explode", Lexplode, too_many_1, wrong_no_1}, {"explodec", Lexplodec, too_many_1, wrong_no_1}, {"explode2", Lexplodec, too_many_1, wrong_no_1}, {"explode2lc", Lexplode2lc, too_many_1, wrong_no_1}, {"explode2uc", Lexplode2uc, too_many_1, wrong_no_1}, {"exploden", Lexploden, too_many_1, wrong_no_1}, {"explodecn", Lexplodecn, too_many_1, wrong_no_1}, {"explode2n", Lexplodecn, too_many_1, wrong_no_1}, {"explode2lcn", Lexplode2lcn, too_many_1, wrong_no_1}, {"explode2ucn", Lexplode2ucn, too_many_1, wrong_no_1}, {"explodehex", Lexplodehex, too_many_1, wrong_no_1}, {"explodeoctal", Lexplodeoctal, too_many_1, wrong_no_1}, {"explodebinary", Lexplodebinary, too_many_1, wrong_no_1}, {"prin", Lprin, too_many_1, wrong_no_1}, {"prin1", Lprin, too_many_1, wrong_no_1}, {"princ", Lprinc, too_many_1, wrong_no_1}, {"prin2", Lprinc, too_many_1, wrong_no_1}, {"prin2a", Lprin2a, too_many_1, wrong_no_1}, {"print", Lprint, too_many_1, wrong_no_1}, {"printc", Lprintc, too_many_1, wrong_no_1}, {"set-print-precision", Lprint_precision, too_many_1, wrong_no_1}, {"tyo", Ltyo, too_many_1, wrong_no_1}, #endif {NULL, 0, 0, 0} }; /* end of print.c */