diff options
author | Eli Zaretskii <eliz@gnu.org> | 2011-04-23 13:33:28 +0300 |
---|---|---|
committer | Eli Zaretskii <eliz@gnu.org> | 2011-04-23 13:33:28 +0300 |
commit | e6c3da2065ac72cc4e1a2bef22d367cd75401892 (patch) | |
tree | 1f6fcbee1e12f63096d2221a89f5436b831862a9 /src/doprnt.c | |
parent | 4ffd0d6b569d252e4e807d4e9c9d6a5bd5b08640 (diff) | |
download | emacs-e6c3da2065ac72cc4e1a2bef22d367cd75401892.tar.gz |
Fix doprnt so it could be used safely in `verror'. (Bug#8435)
src/doprnt.c: Include limits.h.
(SIZE_MAX): New macro.
(doprnt): Return a size_t value. 2nd arg is now size_t. Many
local variables are now size_t instead of int or unsigned.
Improve overflow protection. Support `l' modifier for integer
conversions. Support %l conversion. Don't assume an EMACS_INT
argument for integer conversions and for %c.
src/lisp.h (doprnt): Restore prototype.
src/makefile.w32-in ($(BLD)/callint.$(O)): Depend on
$(SRC)/character.h.
src/Makefile.in (base_obj): Add back doprnt.o.
src/deps.mk (doprnt.o): Add back prerequisites.
(callint.o): Depend on character.h.
src/eval.c (internal_lisp_condition_case): Include the handler
representation in the error message.
(verror): Call doprnt instead of vsnprintf. Fix an off-by-one bug
when breaking from the loop.
src/xdisp.c (vmessage): Call doprnt instead of vsnprintf.
src/callint.c (Fcall_interactively): When displaying error message
about invalid control letter, pass the character's codepoint, not
a pointer to its multibyte form. Improve display of the character
in octal and display also its hex code.
src/character.c (char_string): Use %x to display the (unsigned)
codepoint of an invalid character, to avoid displaying a bogus
negative value.
src/font.c (check_otf_features): Pass SDATA of SYMBOL_NAME to
`error', not SYMBOL_NAME itself.
src/coding.c (Fencode_sjis_char, Fencode_big5_char): Use %c for
character arguments to `error'.
src/charset.c (check_iso_charset_parameter): Fix incorrect argument
to `error' in error message about FINAL_CHAR argument. Make sure
FINAL_CHAR is a character, and use %c when it is passed as
argument to `error'.
Diffstat (limited to 'src/doprnt.c')
-rw-r--r-- | src/doprnt.c | 106 |
1 files changed, 72 insertions, 34 deletions
diff --git a/src/doprnt.c b/src/doprnt.c index 36eb272caae..f182529b801 100644 --- a/src/doprnt.c +++ b/src/doprnt.c @@ -30,6 +30,11 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ #include <unistd.h> +#include <limits.h> +#ifndef SIZE_MAX +# define SIZE_MAX ((size_t) -1) +#endif + #include "lisp.h" /* Since we use the macro CHAR_HEAD_P, we have to include this, but @@ -51,8 +56,8 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ String arguments are passed as C strings. Integers are passed as C integers. */ -EMACS_INT -doprnt (char *buffer, register int bufsize, const char *format, +size_t +doprnt (char *buffer, register size_t bufsize, const char *format, const char *format_end, va_list ap) { const char *fmt = format; /* Pointer into format string */ @@ -62,15 +67,15 @@ doprnt (char *buffer, register int bufsize, const char *format, char tembuf[DBL_MAX_10_EXP + 100]; /* Size of sprintf_buffer. */ - unsigned size_allocated = sizeof (tembuf); + size_t size_allocated = sizeof (tembuf); /* Buffer to use for sprintf. Either tembuf or same as BIG_BUFFER. */ char *sprintf_buffer = tembuf; /* Buffer we have got with malloc. */ - char *big_buffer = 0; + char *big_buffer = NULL; - register int tem; + register size_t tem; char *string; char fixed_buffer[20]; /* Default buffer for small formatting. */ char *fmtcpy; @@ -92,8 +97,9 @@ doprnt (char *buffer, register int bufsize, const char *format, { if (*fmt == '%') /* Check for a '%' character */ { - unsigned size_bound = 0; - EMACS_INT width; /* Columns occupied by STRING. */ + size_t size_bound = 0; + EMACS_INT width; /* Columns occupied by STRING on display. */ + int long_flag = 0; fmt++; /* Copy this one %-spec into fmtcpy. */ @@ -108,10 +114,11 @@ doprnt (char *buffer, register int bufsize, const char *format, 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. */ - unsigned n = *fmt - '0'; + size_t n = *fmt - '0'; while ('0' <= fmt[1] && fmt[1] <= '9') { - if (n * 10 + fmt[1] - '0' < n) + if (n >= SIZE_MAX / 10 + || n * 10 > SIZE_MAX - (fmt[1] - '0')) error ("Format width or precision too large"); n = n * 10 + fmt[1] - '0'; *string++ = *++fmt; @@ -122,6 +129,13 @@ doprnt (char *buffer, register int bufsize, const char *format, } else if (*fmt == '-' || *fmt == ' ' || *fmt == '.' || *fmt == '+') ; + else if (*fmt == 'l') + { + long_flag = 1; + if (!strchr ("dox", fmt[1])) + /* %l as conversion specifier, not as modifier. */ + break; + } else break; fmt++; @@ -130,7 +144,7 @@ doprnt (char *buffer, register int bufsize, const char *format, /* Make the size bound large enough to handle floating point formats with large numbers. */ - if (size_bound + DBL_MAX_10_EXP + 50 < size_bound) + if (size_bound > SIZE_MAX - DBL_MAX_10_EXP - 50) error ("Format width or precision too large"); size_bound += DBL_MAX_10_EXP + 50; @@ -151,23 +165,47 @@ doprnt (char *buffer, register int bufsize, const char *format, error ("Invalid format operation %%%c", fmt[-1]); /* case 'b': */ + case 'l': case 'd': + { + int i; + long l; + + if (long_flag) + { + l = va_arg(ap, long); + sprintf (sprintf_buffer, fmtcpy, l); + } + else + { + i = va_arg(ap, int); + sprintf (sprintf_buffer, fmtcpy, i); + } + /* Now copy into final output, truncating as necessary. */ + string = sprintf_buffer; + goto doit; + } + case 'o': case 'x': - if (sizeof (int) == sizeof (EMACS_INT)) - ; - else if (sizeof (long) == sizeof (EMACS_INT)) - /* Insert an `l' the right place. */ - string[1] = string[0], - string[0] = string[-1], - string[-1] = 'l', - string++; - else - abort (); - sprintf (sprintf_buffer, fmtcpy, va_arg(ap, char *)); - /* Now copy into final output, truncating as nec. */ - string = sprintf_buffer; - goto doit; + { + unsigned u; + unsigned long ul; + + if (long_flag) + { + ul = va_arg(ap, unsigned long); + sprintf (sprintf_buffer, fmtcpy, ul); + } + else + { + u = va_arg(ap, unsigned); + sprintf (sprintf_buffer, fmtcpy, u); + } + /* Now copy into final output, truncating as necessary. */ + string = sprintf_buffer; + goto doit; + } case 'f': case 'e': @@ -175,7 +213,7 @@ doprnt (char *buffer, register int bufsize, const char *format, { double d = va_arg(ap, double); sprintf (sprintf_buffer, fmtcpy, d); - /* Now copy into final output, truncating as nec. */ + /* Now copy into final output, truncating as necessary. */ string = sprintf_buffer; goto doit; } @@ -187,13 +225,18 @@ doprnt (char *buffer, register int bufsize, const char *format, minlen = atoi (&fmtcpy[1]); string = va_arg (ap, char *); tem = strlen (string); + if (tem > MOST_POSITIVE_FIXNUM) + error ("String for %%s or %%S format is too long"); width = strwidth (string, tem); goto doit1; /* Copy string into final output, truncating if no room. */ doit: /* Coming here means STRING contains ASCII only. */ - width = tem = strlen (string); + tem = strlen (string); + if (tem > MOST_POSITIVE_FIXNUM) + error ("Format width or precision too large"); + width = tem; doit1: /* We have already calculated: TEM -- length of STRING, @@ -236,13 +279,8 @@ doprnt (char *buffer, register int bufsize, const char *format, case 'c': { - /* Sometimes for %c we pass a char, which would widen - to int. Sometimes we pass XFASTINT() or XINT() - values, which would be EMACS_INT. Let's hope that - both are passed the same way, otherwise we'll need - to rewrite callers. */ - EMACS_INT chr = va_arg(ap, EMACS_INT); - tem = CHAR_STRING ((int) chr, (unsigned char *) charbuf); + int chr = va_arg(ap, int); + tem = CHAR_STRING (chr, (unsigned char *) charbuf); string = charbuf; string[tem] = 0; width = strwidth (string, tem); @@ -274,6 +312,6 @@ doprnt (char *buffer, register int bufsize, const char *format, /* If we had to malloc something, free it. */ xfree (big_buffer); - *bufptr = 0; /* Make sure our string end with a '\0' */ + *bufptr = 0; /* Make sure our string ends with a '\0' */ return bufptr - buffer; } |