summaryrefslogtreecommitdiff
path: root/src/doprnt.c
diff options
context:
space:
mode:
authorEli Zaretskii <eliz@gnu.org>2011-04-23 13:33:28 +0300
committerEli Zaretskii <eliz@gnu.org>2011-04-23 13:33:28 +0300
commite6c3da2065ac72cc4e1a2bef22d367cd75401892 (patch)
tree1f6fcbee1e12f63096d2221a89f5436b831862a9 /src/doprnt.c
parent4ffd0d6b569d252e4e807d4e9c9d6a5bd5b08640 (diff)
downloademacs-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.c106
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;
}