jcs
/detritus
/amendments
/31
util: Fix snprintf not truncating when the format string is too long
We were correctly stopping when the resulting buffer was becoming
longer than size, but if the input format string was longer than the
size, it was incorrectly copying the whole thing. This wasn't much
of a problem since passing a user-supplied format is dangerous anyway
but this needed fixing.
Since we weren't using vfprintf, drop the FILE mechanism and
implement bounds checking directly in vsnprintf. This is easier to
understand and may be a tad more performant.
jcs made amendment 31 about 1 year ago
--- util.c Sat Oct 26 21:10:37 2024
+++ util.c Mon Nov 11 22:29:36 2024
@@ -1986,21 +1986,14 @@ RestoreHiddenMenuBar(void)
bool
CommandPeriodPressed(void)
{
- KeyMap km;
- bool cmd_down;
+ EventRecord event;
- GetKeys(&km);
- cmd_down = (km[1] & (1 << 15)) != 0;
- if (cmd_down)
- ;//Debugger();
-#if 0
if (!GetNextEvent(keyDownMask | autoKeyMask, &event))
return false;
if ((event.modifiers & cmdKey) &&
((event.message & charCodeMask) == '.'))
return true;
-#endif
return false;
}
@@ -2131,346 +2124,306 @@ strsep(char **stringp, const char *delim)
return (NULL);
}
-static struct format {
- unsigned leftJustify : 1;
- unsigned forceSign : 1;
- unsigned altForm : 1;
- unsigned zeroPad : 1;
- unsigned havePrecision : 1;
- unsigned hSize : 1;
- unsigned lSize : 1;
- unsigned LSize : 1;
- char sign;
- char exponent;
- int fieldWidth;
- int precision;
-} default_format;
+static struct printf_format {
+ unsigned short leftJustify : 1;
+ unsigned short forceSign : 1;
+ unsigned short altForm : 1;
+ unsigned short zeroPad : 1;
+ unsigned short havePrecision : 1;
+ unsigned short hSize : 1;
+ unsigned short lSize : 1;
+ unsigned short LSize : 1;
+ char sign;
+ char exponent;
+ short fieldWidth;
+ short precision;
+} printf_default_format;
-short bounded_vfprintf(FILE *fp, const char *fmt, va_list arg);
-static int nullio(FILE *fp, int i);
+short
+snprintf(char *s, size_t size, const char *fmt, ...)
+{
+ return vsnprintf(s, size, fmt, __va(fmt));
+}
short
-bounded_vfprintf(FILE *fp, const char *fmt, va_list arg)
+vsnprintf(char *str, size_t size, const char *fmt, void *arg)
{
- register int c, i, j, nwritten = 0;
+ register short c, i, si, nwritten;
register unsigned long n;
long double x;
register char *s;
-#define VFPRINTF_BUFLEN 512
- static char buf[VFPRINTF_BUFLEN], *digits, *t;
- struct format F;
+#define VSNPRINTF_BUFLEN 512
+ static char buf[VSNPRINTF_BUFLEN], *digits, *t;
+ struct printf_format F;
+ nwritten = 0;
+
+ if (size == 0)
+ return 0;
+
for (c = *fmt; c; c = *++fmt) {
if (c != '%')
goto copy1;
- F = default_format;
-
- /* decode flags */
+ F = printf_default_format;
+
+ /* decode flags */
for (;;) {
c = *++fmt;
if (c == '-')
- F.leftJustify = TRUE;
+ F.leftJustify = true;
else if (c == '+')
- F.forceSign = TRUE;
+ F.forceSign = true;
else if (c == ' ')
F.sign = ' ';
else if (c == '#')
- F.altForm = TRUE;
+ F.altForm = true;
else if (c == '0')
- F.zeroPad = TRUE;
+ F.zeroPad = true;
else
break;
}
- /* decode field width */
-
+ /* decode field width */
if (c == '*') {
- if ((F.fieldWidth = va_arg(arg, int)) < 0) {
- F.leftJustify = TRUE;
+ if ((F.fieldWidth = va_arg(arg, short)) < 0) {
+ F.leftJustify = true;
F.fieldWidth = -F.fieldWidth;
}
c = *++fmt;
- }
- else {
+ } else {
for (; c >= '0' && c <= '9'; c = *++fmt)
F.fieldWidth = (10 * F.fieldWidth) + (c - '0');
}
- /* decode precision */
-
+ /* decode precision */
if (c == '.') {
if ((c = *++fmt) == '*') {
- F.precision = va_arg(arg, int);
+ F.precision = va_arg(arg, short);
c = *++fmt;
- }
- else {
+ } else {
for (; c >= '0' && c <= '9'; c = *++fmt)
F.precision = (10 * F.precision) + (c - '0');
}
if (F.precision >= 0)
- F.havePrecision = TRUE;
+ F.havePrecision = true;
}
- /* perform appropriate conversion */
-
- s = &buf[VFPRINTF_BUFLEN];
+ /* perform appropriate conversion */
+ s = &buf[VSNPRINTF_BUFLEN];
if (F.leftJustify)
- F.zeroPad = FALSE;
-conv: switch (c) {
-
- /* 'h' size modifier */
-
- case 'h':
- F.hSize = TRUE;
- c = *++fmt;
- goto conv;
-
- /* 'l' size modifier */
-
- case 'l':
- F.lSize = TRUE;
- c = *++fmt;
- goto conv;
-
- /* 'L' size modifier */
-
- case 'L':
- F.LSize = TRUE;
- c = *++fmt;
- goto conv;
-
- /* decimal (signed) */
-
- case 'd':
- case 'i':
- if (F.lSize)
- n = va_arg(arg, long);
- else
- n = va_arg(arg, int);
- if (F.hSize)
- n = (short) n;
- if ((long) n < 0) {
- n = -n;
- F.sign = '-';
+ F.zeroPad = false;
+conv:
+ switch (c) {
+ /* 'h' size modifier */
+ case 'h':
+ F.hSize = true;
+ c = *++fmt;
+ goto conv;
+
+ /* 'l' size modifier */
+ case 'l':
+ F.lSize = true;
+ c = *++fmt;
+ goto conv;
+
+ /* 'L' size modifier */
+ case 'L':
+ F.LSize = true;
+ c = *++fmt;
+ goto conv;
+
+ /* decimal (signed) */
+ case 'd':
+ case 'i':
+ if (F.lSize)
+ n = va_arg(arg, long);
+ else
+ n = va_arg(arg, short);
+ if (F.hSize)
+ n = (short)n;
+ if ((long)n < 0) {
+ n = -n;
+ F.sign = '-';
+ } else if (F.forceSign)
+ F.sign = '+';
+ goto decimal;
+
+ /* decimal (unsigned) */
+ case 'u':
+ if (F.lSize)
+ n = va_arg(arg, unsigned long);
+ else
+ n = va_arg(arg, unsigned short);
+ if (F.hSize)
+ n = (unsigned short)n;
+ F.sign = 0;
+ goto decimal;
+
+ /* decimal (common code) */
+ decimal:
+ if (!F.havePrecision) {
+ if (F.zeroPad) {
+ F.precision = F.fieldWidth;
+ if (F.sign)
+ --F.precision;
}
- else if (F.forceSign)
- F.sign = '+';
- goto decimal;
-
- /* decimal (unsigned) */
-
- case 'u':
- if (F.lSize)
- n = va_arg(arg, unsigned long);
- else
- n = va_arg(arg, unsigned int);
- if (F.hSize)
- n = (unsigned short) n;
- F.sign = 0;
- goto decimal;
-
- /* decimal (common code) */
-
- decimal:
- if (!F.havePrecision) {
- if (F.zeroPad) {
- F.precision = F.fieldWidth;
- if (F.sign)
- --F.precision;
- }
- if (F.precision < 1)
- F.precision = 1;
+ if (F.precision < 1)
+ F.precision = 1;
+ }
+ for (i = 0; n; n /= 10, i++)
+ *--s = n % 10 + '0';
+ for (; i < F.precision; i++)
+ *--s = '0';
+ if (F.sign) {
+ *--s = F.sign;
+ i++;
+ }
+ break;
+
+ /* octal (unsigned) */
+ case 'o':
+ if (F.lSize)
+ n = va_arg(arg, unsigned long);
+ else
+ n = va_arg(arg, unsigned short);
+ if (F.hSize)
+ n = (unsigned short)n;
+ if (!F.havePrecision) {
+ if (F.zeroPad)
+ F.precision = F.fieldWidth;
+ if (F.precision < 1)
+ F.precision = 1;
+ }
+ for (i = 0; n; n /= 8, i++)
+ *--s = n % 8 + '0';
+ if (F.altForm && i && *s != '0') {
+ *--s = '0';
+ i++;
+ }
+ for (; i < F.precision; i++)
+ *--s = '0';
+ break;
+
+ /* hexadecimal (unsigned) */
+ case 'p':
+ F.havePrecision = F.lSize = true;
+ F.precision = 8;
+ /* ... */
+ case 'X':
+ digits = "0123456789ABCDEF";
+ goto hexadecimal;
+ case 'x':
+ digits = "0123456789abcdef";
+ /* ... */
+ hexadecimal:
+ if (F.lSize)
+ n = va_arg(arg, unsigned long);
+ else
+ n = va_arg(arg, unsigned short);
+ if (F.hSize)
+ n = (unsigned short)n;
+ if (!F.havePrecision) {
+ if (F.zeroPad) {
+ F.precision = F.fieldWidth;
+ if (F.altForm)
+ F.precision -= 2;
}
- for (i = 0; n; n /= 10, i++)
- *--s = n % 10 + '0';
- for (; i < F.precision; i++)
- *--s = '0';
- if (F.sign) {
- *--s = F.sign;
- i++;
- }
- break;
-
- /* octal (unsigned) */
-
- case 'o':
- if (F.lSize)
- n = va_arg(arg, unsigned long);
- else
- n = va_arg(arg, unsigned int);
- if (F.hSize)
- n = (unsigned short) n;
- if (!F.havePrecision) {
- if (F.zeroPad)
- F.precision = F.fieldWidth;
- if (F.precision < 1)
- F.precision = 1;
- }
- for (i = 0; n; n /= 8, i++)
- *--s = n % 8 + '0';
- if (F.altForm && i && *s != '0') {
- *--s = '0';
- i++;
- }
- for (; i < F.precision; i++)
- *--s = '0';
- break;
-
- /* hexadecimal (unsigned) */
-
- case 'p':
- F.havePrecision = F.lSize = TRUE;
- F.precision = 8;
- /* ... */
- case 'X':
- digits = "0123456789ABCDEF";
- goto hexadecimal;
- case 'x':
- digits = "0123456789abcdef";
- /* ... */
- hexadecimal:
- if (F.lSize)
- n = va_arg(arg, unsigned long);
- else
- n = va_arg(arg, unsigned int);
- if (F.hSize)
- n = (unsigned short) n;
- if (!F.havePrecision) {
- if (F.zeroPad) {
- F.precision = F.fieldWidth;
- if (F.altForm)
- F.precision -= 2;
- }
- if (F.precision < 1)
- F.precision = 1;
- }
- for (i = 0; n; n /= 16, i++)
- *--s = digits[n % 16];
- for (; i < F.precision; i++)
- *--s = '0';
- if (F.altForm) {
- *--s = c;
- *--s = '0';
- i += 2;
- }
- break;
+ if (F.precision < 1)
+ F.precision = 1;
+ }
+ for (i = 0; n; n /= 16, i++)
+ *--s = digits[n % 16];
+ for (; i < F.precision; i++)
+ *--s = '0';
+ if (F.altForm) {
+ *--s = c;
+ *--s = '0';
+ i += 2;
+ }
+ break;
- /* character */
-
- case 'c':
- *--s = va_arg(arg, int);
- i = 1;
- break;
-
- /* string */
-
- case 's':
- s = va_arg(arg, char *);
- if (F.altForm) {
- i = (unsigned char) *s++;
- if (F.havePrecision && i > F.precision)
- i = F.precision;
- }
- else {
- if (!F.havePrecision)
- i = strlen(s);
- else if (t = memchr(s, '\0', F.precision))
- i = t - s;
- else
- i = F.precision;
- }
- break;
-
- /* store # bytes written so far */
-
- case 'n':
- s = va_arg(arg, void *);
- if (F.hSize)
- * (short *) s = nwritten;
- else if (F.lSize)
- * (long *) s = nwritten;
+ /* character */
+ case 'c':
+ *--s = va_arg(arg, short);
+ i = 1;
+ break;
+
+ /* string */
+ case 's':
+ s = va_arg(arg, char *);
+ if (F.altForm) {
+ i = (unsigned char)*s++;
+ if (F.havePrecision && i > F.precision)
+ i = F.precision;
+ } else {
+ if (!F.havePrecision)
+ i = strlen(s);
+ else if (t = memchr(s, '\0', F.precision))
+ i = t - s;
else
- * (int *) s = nwritten;
- continue;
+ i = F.precision;
+ }
+ break;
- /* oops - unknown conversion, abort */
-
- default:
- if (c != '%')
- goto done;
- copy1:
- putc(c, fp); /* disregard EOF */
- ++nwritten;
- continue;
+ /* store # bytes written so far */
+ case 'n':
+ s = va_arg(arg, void *);
+ if (F.lSize)
+ *(long *)s = nwritten;
+ else
+ *(short *)s = nwritten;
+ continue;
+
+ /* unknown conversion, abort */
+ default:
+ if (c != '%')
+ goto done;
+ copy1:
+ if (size) {
+ *str++ = c;
+ size--;
+ }
+ ++nwritten;
+ continue;
}
- /* pad on the left */
-
+ /* pad on the left */
if (i < F.fieldWidth && !F.leftJustify) {
do {
- putc(' ', fp); /* disregard EOF */
+ if (size) {
+ *str++ = ' ';
+ size--;
+ }
++nwritten;
} while (i < --F.fieldWidth);
}
- /* write the converted result */
-
- fwrite(s, 1, i, fp); /* disregard EOF */
+ /* write the converted result */
+ si = i > size ? size : i;
+ if (si) {
+ memcpy(str, s, si);
+ str += si;
+ size -= si;
+ }
nwritten += i;
- /* pad on the right */
-
+ /* pad on the right */
for (; i < F.fieldWidth; i++) {
- putc(' ', fp); /* disregard EOF */
+ if (size) {
+ *str++ = ' ';
+ size--;
+ }
++nwritten;
}
}
- /* all done! */
-
-done:
- return(nwritten);
-}
-
-short
-snprintf(char *s, size_t size, const char *fmt, ...)
-{
- return(vsnprintf(s, size, fmt, __va(fmt)));
-}
-
-static int
-nullio(FILE *fp, int i)
-{
- return(EOF);
-}
-
-short
-vsnprintf(char *s, size_t size, const char *fmt, void *p)
-{
- FILE f;
- int n;
- unsigned char zb;
+ /* all done! */
+ if (size)
+ *str = '\0';
+ else
+ str[-1] = '\0';
- memset(&f, 0, sizeof(f));
- f.refnum = -1;
- f.ptr = (unsigned char *)s;
- f.cnt = size;
- f.proc = nullio;
- f.dirty = 1;
-
- if (size == 0)
- zb = s[0];
-
- if ((n = bounded_vfprintf(&f, fmt, p)) >= 0) {
- if (n < size)
- s[n] = 0;
- else
- s[size - 1] = 0;
- }
-
- if (size == 0)
- s[0] = zb;
-
- return(n);
+done:
+ return nwritten;
}