X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/68be917ded75e3147180af2a0a51fc8c89d042d8..3c53a3cf83c218772d9bcfde4cd60c1face33e93:/src/doprnt.c diff --git a/src/doprnt.c b/src/doprnt.c index 5639a7eff3..3fd6222c9d 100644 --- a/src/doprnt.c +++ b/src/doprnt.c @@ -17,14 +17,38 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU Emacs; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ +the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ #include #include #include -extern long *xmalloc (), *xrealloc (); +#ifdef STDC_HEADERS +#include +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif + +#include "lisp.h" + +#ifndef DBL_MAX_10_EXP +#define DBL_MAX_10_EXP 308 /* IEEE double */ +#endif + +/* Since we use the macro CHAR_HEAD_P, we have to include this, but + don't have to include others because CHAR_HEAD_P does not contains + another macro. */ +#include "charset.h" + +static int doprnt1 (); /* Generate output from a format-spec FORMAT, terminated at position FORMAT_END. @@ -32,8 +56,11 @@ extern long *xmalloc (), *xrealloc (); If the output does not fit, truncate it to fit. Returns the number of characters stored into BUFFER. ARGS points to the vector of arguments, and NARGS says how many. - A double counts as two arguments. */ + A double counts as two arguments. + String arguments are passed as C strings. + Integers are passed as C integers. */ +int doprnt (buffer, bufsize, format, format_end, nargs, args) char *buffer; register int bufsize; @@ -41,16 +68,44 @@ doprnt (buffer, bufsize, format, format_end, nargs, args) char *format_end; int nargs; char **args; +{ + return doprnt1 (0, buffer, bufsize, format, format_end, nargs, args); +} + +/* Like doprnt except that strings in ARGS are passed + as Lisp_Object. */ + +int +doprnt_lisp (buffer, bufsize, format, format_end, nargs, args) + char *buffer; + register int bufsize; + char *format; + char *format_end; + int nargs; + char **args; +{ + return doprnt1 (1, buffer, bufsize, format, format_end, nargs, args); +} + +static int +doprnt1 (lispstrings, buffer, bufsize, format, format_end, nargs, args) + int lispstrings; + char *buffer; + register int bufsize; + char *format; + char *format_end; + int nargs; + char **args; { int cnt = 0; /* Number of arg to gobble next */ register char *fmt = format; /* Pointer into format string */ register char *bufptr = buffer; /* Pointer into output buffer.. */ /* Use this for sprintf unless we need something really big. */ - char tembuf[100]; + char tembuf[DBL_MAX_10_EXP + 100]; /* Size of sprintf_buffer. */ - int size_allocated = 100; + int size_allocated = sizeof (tembuf); /* Buffer to use for sprintf. Either tembuf or same as BIG_BUFFER. */ char *sprintf_buffer = tembuf; @@ -59,12 +114,11 @@ doprnt (buffer, bufsize, format, format_end, nargs, args) char *big_buffer = 0; register int tem; - char *string; + unsigned char *string; char fixed_buffer[20]; /* Default buffer for small formatting. */ char *fmtcpy; int minlen; - int size; /* Field width factor; e.g., %90d */ - char charbuf[2]; /* Used for %c. */ + unsigned char charbuf[5]; /* Used for %c. */ if (format_end == 0) format_end = format + strlen (format); @@ -81,31 +135,48 @@ doprnt (buffer, bufsize, format, format_end, nargs, args) { if (*fmt == '%') /* Check for a '%' character */ { - int size_bound; + int size_bound = 0; + int width; /* Columns occupied by STRING. */ fmt++; /* Copy this one %-spec into fmtcpy. */ - string = fmtcpy; + string = (unsigned char *)fmtcpy; *string++ = '%'; while (1) { *string++ = *fmt; - if (! (*fmt >= '0' && *fmt <= '9') - && *fmt != '-' && *fmt != ' '&& *fmt != '.') + if ('0' <= *fmt && *fmt <= '9') + { + /* Get an idea of how much space we might need. + This might be a field width or a precision; e.g. + %1.1000f and %1000.1f both might need 1000+ bytes. + Parse the width or precision, checking for overflow. */ + int n = *fmt - '0'; + while ('0' <= fmt[1] && fmt[1] <= '9') + { + if (n * 10 / 10 != n + || (n = n * 10 + (fmt[1] - '0')) < 0) + error ("Format width or precision too large"); + *string++ = *++fmt; + } + + if (size_bound < n) + size_bound = n; + } + else if (*fmt == '-' || *fmt == ' ' || *fmt == '.') + ; + else break; fmt++; } *string = 0; - /* Get an idea of how much space we might need. */ - size_bound = atoi (&fmtcpy[1]); - /* Avoid pitfall of negative "size" parameter ("%-200d"). */ - if (size_bound < 0) - size_bound = -size_bound; - size_bound += 50; + /* Make the size bound large enough to handle floating point formats + with large numbers. */ + size_bound += DBL_MAX_10_EXP + 50; - if (size_bound > (unsigned) (1 << (BITS_PER_INT - 1))) - error ("Format padding too large"); + if (size_bound < 0) + error ("Format width or precision too large"); /* Make sure we have that much. */ if (size_bound > size_allocated) @@ -141,7 +212,7 @@ doprnt (buffer, bufsize, format, format_end, nargs, args) abort (); sprintf (sprintf_buffer, fmtcpy, args[cnt++]); /* Now copy into final output, truncating as nec. */ - string = sprintf_buffer; + string = (unsigned char *)sprintf_buffer; goto doit; case 'f': @@ -155,7 +226,7 @@ doprnt (buffer, bufsize, format, format_end, nargs, args) u.half[1] = args[cnt++]; sprintf (sprintf_buffer, fmtcpy, u.d); /* Now copy into final output, truncating as nec. */ - string = sprintf_buffer; + string = (unsigned char *)sprintf_buffer; goto doit; } @@ -164,16 +235,34 @@ doprnt (buffer, bufsize, format, format_end, nargs, args) case 's': if (cnt == nargs) error ("not enough arguments for format string"); - string = args[cnt++]; if (fmtcpy[1] != 's') minlen = atoi (&fmtcpy[1]); + if (lispstrings) + { + string = ((struct Lisp_String *)args[cnt])->data; + tem = STRING_BYTES ((struct Lisp_String *)args[cnt]); + cnt++; + } + else + { + string = (unsigned char *)args[cnt++]; + tem = strlen (string); + } + width = strwidth (string, tem); + goto doit1; + /* Copy string into final output, truncating if no room. */ doit: - tem = strlen (string); + /* Coming here means STRING contains ASCII only. */ + width = tem = strlen (string); doit1: + /* We have already calculated: + TEM -- length of STRING, + WIDTH -- columns occupied by STRING when displayed, and + MINLEN -- minimum columns of the output. */ if (minlen > 0) { - while (minlen > tem && bufsize > 0) + while (minlen > width && bufsize > 0) { *bufptr++ = ' '; bufsize--; @@ -182,13 +271,21 @@ doprnt (buffer, bufsize, format, format_end, nargs, args) minlen = 0; } if (tem > bufsize) - tem = bufsize; - strncpy (bufptr, string, tem); + { + /* Truncate the string at character boundary. */ + tem = bufsize; + while (!CHAR_HEAD_P (string[tem - 1])) tem--; + bcopy (string, bufptr, tem); + /* We must calculate WIDTH again. */ + width = strwidth (bufptr, tem); + } + else + bcopy (string, bufptr, tem); bufptr += tem; bufsize -= tem; if (minlen < 0) { - while (minlen < - tem && bufsize > 0) + while (minlen < - width && bufsize > 0) { *bufptr++ = ' '; bufsize--; @@ -201,9 +298,11 @@ doprnt (buffer, bufsize, format, format_end, nargs, args) case 'c': if (cnt == nargs) error ("not enough arguments for format string"); - *charbuf = (EMACS_INT) args[cnt++]; + tem = CHAR_STRING ((int) (EMACS_INT) args[cnt], charbuf); string = charbuf; - tem = 1; + cnt++; + string[tem] = 0; + width = strwidth (string, tem); if (fmtcpy[1] != 'c') minlen = atoi (&fmtcpy[1]); goto doit1; @@ -212,8 +311,20 @@ doprnt (buffer, bufsize, format, format_end, nargs, args) fmt--; /* Drop thru and this % will be treated as normal */ } } - *bufptr++ = *fmt++; /* Just some characters; Copy 'em */ - bufsize--; + + { + /* Just some character; Copy it if the whole multi-byte form + fit in the buffer. */ + char *save_bufptr = bufptr; + + do { *bufptr++ = *fmt++; } + while (--bufsize > 0 && !CHAR_HEAD_P (*fmt)); + if (!CHAR_HEAD_P (*fmt)) + { + bufptr = save_bufptr; + break; + } + } }; /* If we had to malloc something, free it. */