Use new virtual IO api
[sxemacs] / src / print.c
index a0b49a8..d1d3c2a 100644 (file)
@@ -103,6 +103,8 @@ FILE *termscript;           /* Stdio stream being used for copy of all output.  */
 \f
 int stdout_needs_newline;
 
+void debug_backtrace(void);
+
 static void
 std_handle_out_external(FILE * stream, Lisp_Object lstream,
                        const Extbyte * extptr, Extcount extlen,
@@ -117,11 +119,8 @@ std_handle_out_external(FILE * stream, Lisp_Object lstream,
                return;
        }
        if (stream) {
-               {
-                       fwrite(extptr, 1, extlen, stream);
-                       if (must_flush)
-                               fflush(stream);
-               }
+               fwrite(extptr, 1, extlen, stream);
+               if (must_flush) fflush(stream);
        } else
                Lstream_write(XLSTREAM(lstream), extptr, extlen);
 
@@ -130,10 +129,84 @@ std_handle_out_external(FILE * stream, Lisp_Object lstream,
                        fwrite(extptr, 1, extlen, termscript);
                        fflush(termscript);
                }
-               stdout_needs_newline = extlen ? (extptr[extlen - 1] != '\n') : 1;
+               stdout_needs_newline = extptr[extlen - 1] != '\n';
        }
 }
 
+
+#define SXE_VSNPRINT_VA(ret__,sbuf__,buf__,size__,spec__,tries__,type__,fmt__,args__) \
+       do {                                                            \
+               --tries__;                                              \
+               ret__ = vsnprintf((char*)buf__,size__,fmt__,args__);    \
+               if ( ret__ == 0 ) {                                     \
+                       /* Nothing to write */                          \
+                       break;                                          \
+               } else if ( ret__ < 0 ) {                               \
+                       XMALLOC_UNBIND(buf__,size__,spec__);            \
+                       size__ *= 2;                                    \
+                       XMALLOC_OR_ALLOCA(buf__,size__,type__);         \
+               } else if ( (size_t)ret__ > (size_t)size__ ) {          \
+                   /* We need more space, so we need to allocate it */ \
+                       XMALLOC_UNBIND(buf__,size__,spec__);            \
+                       size__ = ret__ + 1;                             \
+                       XMALLOC_OR_ALLOCA(buf__,size__,type__);         \
+                       ret__ = -1;                                     \
+               }                                                       \
+       } while( ret__ < 0 && tries__ > 0 )
+
+
+int write_fmt_str(Lisp_Object stream, const char* fmt, ...)
+{
+       char   *kludge;
+       va_list args;
+       int     bufsize, retval, tries = 3;
+       /* write_fmt_str is used for small prints usually... */
+       char    buffer[64+1];
+       int speccount = specpdl_depth();
+
+       va_start(args, fmt);
+       kludge = buffer;
+       bufsize = sizeof(buffer);
+
+       SXE_VSNPRINT_VA(retval,buffer,kludge,bufsize,speccount,tries,char,fmt,args);
+
+       if (retval>0)
+               write_c_string(kludge,stream);
+
+       XMALLOC_UNBIND(kludge, bufsize, speccount);
+       va_end(args);
+
+       if (retval < 0)
+               error("Error attempting to write write format string '%s'",
+                     fmt);
+       return retval;
+}
+
+int write_fmt_string(Lisp_Object stream, const char *fmt, ...)
+{
+       char   *kludge;
+       va_list args;
+       int     bufsize, retval, tries = 3;
+       /* write_va is used for small prints usually... */
+       char    buffer[128+1];
+       int speccount = specpdl_depth();
+
+       va_start(args, fmt);
+       kludge = buffer;
+       bufsize = sizeof(buffer);
+
+       SXE_VSNPRINT_VA(retval,buffer,kludge,bufsize,speccount,tries,char,fmt,args);
+       if (retval>0)
+               write_c_string(kludge,stream);
+       XMALLOC_UNBIND(kludge, bufsize, speccount);
+       va_end(args);
+
+       if (retval < 0)
+               error("Error attempting to write write format string '%s'",
+                     fmt);
+       return retval;
+}
+
 /* #### The following function should be replaced a call to the
    emacs_doprnt_*() functions.  This is the only way to ensure that
    I18N3 works properly (many implementations of the *printf()
@@ -151,48 +224,50 @@ std_handle_out_external(FILE * stream, Lisp_Object lstream,
 
 static int std_handle_out_va(FILE * stream, const char *fmt, va_list args)
 {
-       Bufbyte buffer[16384],
-               *kludge = buffer;
-       Extbyte *extptr = NULL;
-       Extcount extlen = 0;
-       int     retval, 
-               bufsize = sizeof(buffer), 
-               tries = 3;
-       int speccount = specpdl_depth();
+       int      retval, tries = 3;
+       size_t   bufsize;
+       int      use_fprintf;
+       Bufbyte *kludge;
+       Bufbyte  buffer[1024]; /* Tax stack lightly, used to be 16KiB */
+       int      speccount = specpdl_depth();
 
-       do {
-               assert(tries != 0);
-               retval = vsnprintf((char *)kludge, bufsize, fmt, args);
-               if ( retval == 0 ) {
-                       /* Nothing to write!! */
-                       return retval;
-               } else if ( retval < 0 ) {
-                       bufsize *= 2;
-                       XMALLOC_UNBIND(kludge, bufsize, speccount);
-                       XMALLOC_OR_ALLOCA(kludge,bufsize,Bufbyte);
-                       retval = 0;
-               } else if ( retval > bufsize ) {
-                       /* We need more space, so we need to allocate it 
-                        */
-                       bufsize = retval + 1;
-                       XMALLOC_OR_ALLOCA(kludge,bufsize,Bufbyte);
-                       retval = 0;
-               }
-       } while( retval == 0 );
+       bufsize = sizeof(buffer);
+       kludge = buffer;
 
-       extlen = retval;
+       SXE_VSNPRINT_VA(retval,buffer,kludge,bufsize,speccount,tries,Bufbyte,fmt,args);
 
-       if (initialized && !inhibit_non_essential_printing_operations && 
-           ! fatal_error_in_progress ) {
-               TO_EXTERNAL_FORMAT(DATA, (kludge, strlen((char *)kludge)),
-                                  ALLOCA, (extptr, extlen), Qnative);
-               std_handle_out_external(stream, Qnil, extptr, extlen, 1, 1);
-       } else if (fatal_error_in_progress || !inhibit_non_essential_printing_operations)
-               fprintf(stream,"%s",(char*)kludge);
+       if (retval == 0)
+               /* nothing to write */
+               return retval;
+
+       use_fprintf = ! initialized ||fatal_error_in_progress ||
+               inhibit_non_essential_printing_operations;
+
+       if (retval > 0) {
+               if (use_fprintf) {
+                       fprintf(stream,"%s",(char*)kludge);
+               } else {
+                       Extbyte  *extptr = NULL;
+                       Extcount extlen = retval;
+
+                       TO_EXTERNAL_FORMAT(DATA, (kludge, strlen((char *)kludge)),
+                                          ALLOCA, (extptr, extlen), Qnative);
+                       std_handle_out_external(stream, Qnil, extptr, extlen, 1, 1);
+               }
+       } else {
+               if (use_fprintf) {
+                       fprintf(stream,"Error attempting to write format string '%s'",
+                               fmt);
+               } else {
+                       const Extbyte *msg = "Error attempting to write format string";
+                       std_handle_out_external(stream, Qnil, msg, strlen(msg), 1, 1);
+               }
+       }
        XMALLOC_UNBIND(kludge, bufsize, speccount);
        return retval;
 }
 
+
 /* Output portably to stderr or its equivalent; call GETTEXT on the
    format string.  Automatically flush when done. */
 
@@ -217,10 +292,10 @@ int stdout_out(const char *fmt, ...)
        int retval;
        va_list args;
        va_start(args, fmt);
-       retval =
-           std_handle_out_va
-           (stdout, initialized
-            && !fatal_error_in_progress ? GETTEXT(fmt) : fmt, args);
+       retval = std_handle_out_va(stdout,
+                                  (initialized && !fatal_error_in_progress
+                                   ? GETTEXT(fmt) : fmt),
+                                  args);
        va_end(args);
        return retval;
 }
@@ -231,7 +306,10 @@ DOESNT_RETURN fatal(const char *fmt, ...)
        va_start(args, fmt);
 
        stderr_out("\nSXEmacs: ");
-       std_handle_out_va(stderr, GETTEXT(fmt), args);
+       std_handle_out_va(stderr,
+                         (initialized && !fatal_error_in_progress
+                          ? GETTEXT(fmt) : fmt),
+                         args);
        stderr_out("\n");
 
        va_end(args);
@@ -247,7 +325,7 @@ write_string_to_stdio_stream(FILE * stream, struct console *con,
                             Lisp_Object coding_system, int must_flush)
 {
        Extcount extlen;
-       const Extbyte *extptr;
+       const Extbyte *extptr = NULL;
 
        /* #### yuck! sometimes this function is called with string data,
           and the following call may gc. */
@@ -255,26 +333,32 @@ write_string_to_stdio_stream(FILE * stream, struct console *con,
                Bufbyte *puta = (Bufbyte *) alloca(len);
                memcpy(puta, str + offset, len);
 
-               if (initialized && !inhibit_non_essential_printing_operations)
+               if (initialized && !inhibit_non_essential_printing_operations) {
                        TO_EXTERNAL_FORMAT(DATA, (puta, len),
                                           ALLOCA, (extptr, extlen),
                                           coding_system);
-               else {
+               }
+               if( extptr == NULL ) {
                        extptr = (Extbyte *) puta;
                        extlen = (Bytecount) len;
                }
        }
 
+
        if (stream) {
                std_handle_out_external(stream, Qnil, extptr, extlen,
                                        stream == stdout
                                        || stream == stderr, must_flush);
-       } else {
+       } else if(con != NULL) {
                assert(CONSOLE_TTY_P(con));
                std_handle_out_external(0, CONSOLE_TTY_DATA(con)->outstream,
                                        extptr, extlen,
                                        CONSOLE_TTY_DATA(con)->is_stdio,
                                        must_flush);
+       } else {
+               error("Error attempting to write write '%s' with no stream nor console", str);
+               debug_backtrace();
+               abort();
        }
 }
 
@@ -483,22 +567,21 @@ void write_string_1(const Bufbyte * str, Bytecount size, Lisp_Object stream)
        output_string(stream, str, Qnil, 0, size);
 }
 
+
+void write_hex_ptr(void* value, Lisp_Object stream)
+{
+       char buf[sizeof(value)*2+1];
+       int n = snprintf(buf,sizeof(buf),"0x%p",value);
+       assert(n>=0 && (size_t)n<sizeof(buf));
+       write_c_string(buf,stream);
+}
+
 void write_c_string(const char *str, Lisp_Object stream)
 {
        /* This function can GC */
        write_string_1((const Bufbyte *)str, strlen(str), stream);
 }
 
-static void write_fmt_string(Lisp_Object stream, const char *fmt, ...)
-{
-       va_list va;
-       char bigbuf[666];
-
-       va_start(va, fmt);
-       vsprintf(bigbuf, fmt, va);
-       va_end(va);
-       write_c_string(bigbuf, stream);
-}
 \f
 DEFUN("write-char", Fwrite_char, 1, 2, 0,      /*
 Output character CHARACTER to stream STREAM.
@@ -829,6 +912,8 @@ Display ERROR-OBJECT on STREAM in a user-friendly way.
 
 Lisp_Object Vfloat_output_format;
 
+void float_to_string(char *buf, fpfloat data, int maxlen);
+
 /*
  * This buffer should be at least as large as the max string size of the
  * largest float, printed in the biggest notation.  This is undoubtedly
@@ -840,20 +925,21 @@ Lisp_Object Vfloat_output_format;
  * I assume that IEEE-754 format numbers can take 329 bytes for the worst
  * case of -1e307 in 20d float_output_format. What is one to do (short of
  * re-writing _doprnt to be more sane)?
- *                     -wsr
+ *                     -wsr
  */
-void float_to_string(char *buf, fpfloat data)
+void float_to_string(char *buf, fpfloat data, int maxlen)
 {
        Bufbyte *cp, c;
-       int width;
+       int width, sz;
 
        if (NILP(Vfloat_output_format) || !STRINGP(Vfloat_output_format)) {
        lose:
 #if fpfloat_double_p
-               sprintf(buf, "%.16g", data);
+               sz = snprintf(buf, maxlen, "%.16g", data);
 #elif fpfloat_long_double_p
-               sprintf(buf, "%.16Lg", data);
+               sz = snprintf(buf, maxlen, "%.16Lg", data);
 #endif
+               assert(sz>=0 && sz<maxlen);
        } else {                        /* oink oink */
 
                /* Check that the spec we have is fully valid.
@@ -882,7 +968,9 @@ void float_to_string(char *buf, fpfloat data)
                if (cp[1] != 0)
                        goto lose;
 
-               sprintf(buf, (char *)XSTRING_DATA(Vfloat_output_format), data);
+               sz = snprintf(buf, maxlen,
+                             (char *)XSTRING_DATA(Vfloat_output_format), data);
+               assert(sz>=0 && sz < maxlen);
        }
 
        /* added by jwz: don't allow "1.0" to print as "1"; that destroys
@@ -893,14 +981,19 @@ void float_to_string(char *buf, fpfloat data)
        {
                Bufbyte *s = (Bufbyte *) buf;   /* don't use signed chars here!
                                                   isdigit() can't hack them! */
-               if (*s == '-')
+               if (*s == '-') {
                        s++;
+                       maxlen--;
+                       assert(maxlen>0);
+               }
                for (; *s; s++)
                        /* if there's a non-digit, then there is a decimal point, or
                           it's in exponential notation, both of which are ok. */
                        if (!isdigit(*s))
                                goto DONE_LABEL;
                /* otherwise, we need to hack it. */
+               maxlen-=2;
+               assert(maxlen>0);
                *s++ = '.';
                *s++ = '0';
                *s = 0;
@@ -909,6 +1002,7 @@ void float_to_string(char *buf, fpfloat data)
 
        /* Some machines print "0.4" as ".4".  I don't like that. */
        if (buf[0] == '.' || (buf[0] == '-' && buf[1] == '.')) {
+               assert(maxlen>0);
                int i;
                for (i = strlen(buf) + 1; i >= 0; i--)
                        buf[i + 1] = buf[i];
@@ -918,16 +1012,17 @@ void float_to_string(char *buf, fpfloat data)
 #endif                         /* HAVE_FPFLOAT */
 
 /* Print NUMBER to BUFFER.
-   This is equivalent to sprintf (buffer, "%ld", number), only much faster.
+   This is equivalent to snprintf (buffer, maxlen, "%ld", number), only much faster.
 
    BUFFER should accept 24 bytes.  This should suffice for the longest
    numbers on 64-bit machines, including the `-' sign and the trailing
    '\0'.  Returns a pointer to the trailing '\0'. */
-char *long_to_string(char *buffer, long number)
+char *long_to_string(char *buffer, long number, int maxlen)
 {
 #if (SIZEOF_LONG != 4) && (SIZEOF_LONG != 8)
        /* Huh? */
-       sprintf(buffer, "%ld", number);
+       int sz = snprintf(buffer, maxlen, "%ld", number);
+       assert(sz>=0 && sz < maxlen);
        return buffer + strlen(buffer);
 #else                          /* (SIZEOF_LONG == 4) || (SIZEOF_LONG == 8) */
        char *p = buffer;
@@ -937,10 +1032,16 @@ char *long_to_string(char *buffer, long number)
                *p++ = '-';
                number = -number;
        }
-#define FROB(figure) do {                                              \
-    if (force || number >= figure)                                     \
-      *p++ = number / figure + '0', number %= figure, force = 1;       \
-    } while (0)
+#define FROB(figure) \
+       do {                                                            \
+               if (force || number >= figure) {                        \
+                       *p++ = number / figure + '0';                   \
+                       number %= figure;                               \
+                       force = 1;                                      \
+                       --maxlen;                                       \
+                       assert(maxlen>0);                               \
+               }                                                       \
+       } while (0)
 #if SIZEOF_LONG == 8
        FROB(1000000000000000000L);
        FROB(100000000000000000L);
@@ -1129,27 +1230,23 @@ default_object_printer(Lisp_Object obj, Lisp_Object printcharfun,
                       int escapeflag)
 {
        struct lcrecord_header *header = (struct lcrecord_header *)XPNTR(obj);
-       char buf[200];
 
        if (print_readably)
                error("printing unreadable object #<%s 0x%x>",
                      LHEADER_IMPLEMENTATION(&header->lheader)->name,
                      header->uid);
 
-       sprintf(buf, "#<%s 0x%x>",
-               LHEADER_IMPLEMENTATION(&header->lheader)->name, header->uid);
-       write_c_string(buf, printcharfun);
+       write_fmt_string(printcharfun, "#<%s 0x%x>",
+                        LHEADER_IMPLEMENTATION(&header->lheader)->name, header->uid);
 }
 
 void
 internal_object_printer(Lisp_Object obj, Lisp_Object printcharfun,
                        int escapeflag)
 {
-       char buf[200];
-       sprintf(buf, "#<INTERNAL OBJECT (SXEmacs bug?) (%s) 0x%lx>",
-               XRECORD_LHEADER_IMPLEMENTATION(obj)->name,
-               (unsigned long)XPNTR(obj));
-       write_c_string(buf, printcharfun);
+       write_fmt_string(printcharfun, "#<INTERNAL OBJECT (SXEmacs bug?) (%s) 0x%lx>",
+                        XRECORD_LHEADER_IMPLEMENTATION(obj)->name,
+                        (unsigned long)XPNTR(obj));
 }
 
 enum printing_badness {
@@ -1164,23 +1261,27 @@ printing_major_badness(Lisp_Object printcharfun,
                       enum printing_badness badness)
 {
        char buf[666];
+       ssize_t len;
 
        switch (badness) {
        case BADNESS_INTEGER_OBJECT:
-               sprintf(buf, "%s %d object %ld", badness_string, type,
-                       (EMACS_INT) val);
+               len = snprintf(buf, sizeof(buf), "%s %d object %ld", badness_string, type,
+                              (EMACS_INT) val);
                break;
 
        case BADNESS_POINTER_OBJECT:
-               sprintf(buf, "%s %d object %p", badness_string, type, val);
+               len = snprintf(buf, sizeof(buf), "%s %d object %p", badness_string, type, val);
                break;
 
        case BADNESS_NO_TYPE:
-               sprintf(buf, "%s object %p", badness_string, val);
+               len = snprintf(buf, sizeof(buf), "%s object %p", badness_string, val);
                break;
        default:
+               len = snprintf(buf, sizeof(buf), "%s unknown badness %d",
+                              badness_string, badness);
                break;
        }
+       assert(len >= 0 && (size_t)len < sizeof(buf));
 
        /* Don't abort or signal if called from debug_print() or already
           crashing */
@@ -1222,12 +1323,12 @@ print_internal(Lisp_Object obj, Lisp_Object printcharfun, int escapeflag)
           output. */
 #endif
 
-        /* Try out custom printing */
-        if (UNLIKELY(!(bool)inhibit_autoloads && !(bool)nodumpfile) &&
+       /* Try out custom printing */
+       if (UNLIKELY(!(bool)inhibit_autoloads && !(bool)nodumpfile) &&
            !EQ(Qnil, Vcustom_object_printer) &&
-            !EQ(Qnil, apply1(Vcustom_object_printer,
-                             Fcons(obj, Fcons(printcharfun, Qnil))))) {
-                return;
+           !EQ(Qnil, apply1(Vcustom_object_printer,
+                            Fcons(obj, Fcons(printcharfun, Qnil))))) {
+               return;
        }
 
        /* Detect circularities and truncate them.
@@ -1238,7 +1339,7 @@ print_internal(Lisp_Object obj, Lisp_Object printcharfun, int escapeflag)
                        if (EQ(obj, being_printed[i])) {
                                char buf[32];
                                *buf = '#';
-                               long_to_string(buf + 1, i);
+                               long_to_string(buf + 1, i, sizeof(buf)-1);
                                write_c_string(buf, printcharfun);
                                return;
                        }
@@ -1257,7 +1358,7 @@ print_internal(Lisp_Object obj, Lisp_Object printcharfun, int escapeflag)
                /* ASCII Decimal representation uses 2.4 times as many bits as
                   machine binary.  */
                char buf[3 * sizeof(EMACS_INT) + 5];
-               long_to_string(buf, XINT(obj));
+               long_to_string(buf, XINT(obj),sizeof(buf));
                write_c_string(buf, printcharfun);
                break;
        }
@@ -1612,15 +1713,22 @@ to 0.
        Bufbyte str[MAX_EMCHAR_LEN];
        Bytecount len;
        int extlen;
-       const Extbyte *extptr;
+       const Extbyte *extptr = NULL;
 
        CHECK_CHAR_COERCE_INT(character);
        len = set_charptr_emchar(str, XCHAR(character));
        TO_EXTERNAL_FORMAT(DATA, (str, len),
                           ALLOCA, (extptr, extlen), Qterminal);
-       memcpy(alternate_do_string + alternate_do_pointer, extptr, extlen);
-       alternate_do_pointer += extlen;
-       alternate_do_string[alternate_do_pointer] = 0;
+       if ( extptr != NULL ) {
+               memcpy(alternate_do_string + alternate_do_pointer, extptr, extlen);
+               alternate_do_pointer += extlen;
+               alternate_do_string[alternate_do_pointer] = 0;
+       } else {
+               /* Better bad transcoding than nothing I guess... */
+               memcpy(alternate_do_string + alternate_do_pointer, str, len);
+               alternate_do_pointer += len;
+               alternate_do_string[alternate_do_pointer] = 0;
+       }
        return character;
 }
 
@@ -1639,8 +1747,8 @@ the output also will be logged to this file.
 */
       (char_or_string, stdout_p, device))
 {
-       FILE *file = 0;
-       struct console *con = 0;
+       FILE *file = NULL;
+       struct console *con = NULL;
 
        if (NILP(device)) {
                if (!NILP(stdout_p))
@@ -1756,7 +1864,6 @@ void debug_print(Lisp_Object debug_print_obj)
 
 /* Debugging kludge -- unbuffered */
 /* This function provided for the benefit of the debugger.  */
-void debug_backtrace(void);
 void debug_backtrace(void)
 {
        /* This function can GC */