summaryrefslogtreecommitdiff
path: root/Src/utils.c
diff options
context:
space:
mode:
authorPeter Stephenson <pws@users.sourceforge.net>2003-10-06 22:46:24 +0000
committerPeter Stephenson <pws@users.sourceforge.net>2003-10-06 22:46:24 +0000
commited6a7ba60e87e1fd4b5c504d82c348e8dc415117 (patch)
tree97be429c165b5eff0b5ad4102a618deec333eac7 /Src/utils.c
parentbbc409eefece558f9b24ea6960e15c0c3468422a (diff)
downloadzsh-ed6a7ba60e87e1fd4b5c504d82c348e8dc415117.tar.gz
zsh-ed6a7ba60e87e1fd4b5c504d82c348e8dc415117.zip
19168: Various problems with size of buffers and pointer usage in ztrftime
Diffstat (limited to 'Src/utils.c')
-rw-r--r--Src/utils.c68
1 files changed, 59 insertions, 9 deletions
diff --git a/Src/utils.c b/Src/utils.c
index 56230fddb..536fe13e0 100644
--- a/Src/utils.c
+++ b/Src/utils.c
@@ -1685,20 +1685,42 @@ spckword(char **s, int hist, int cmd, int ask)
}
}
+/*
+ * Helper for ztrftime. Called with a pointer to the length left
+ * in the buffer, and a new string length to decrement from that.
+ * Returns 0 if the new length fits, 1 otherwise. We assume a terminating
+ * NUL and return 1 if that doesn't fit.
+ */
+
+/**/
+static int
+ztrftimebuf(int *bufsizeptr, int decr)
+{
+ if (*bufsizeptr <= decr)
+ return 1;
+ *bufsizeptr -= decr;
+ return 0;
+}
+
+/*
+ * Like the system function, this returns the number of characters
+ * copied, not including the terminating NUL. This may be zero
+ * if the string didn't fit.
+ */
+
/**/
mod_export int
ztrftime(char *buf, int bufsize, char *fmt, struct tm *tm)
{
- int hr12;
+ int hr12, decr;
#ifndef HAVE_STRFTIME
static char *astr[] =
{"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
static char *estr[] =
{"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul",
"Aug", "Sep", "Oct", "Nov", "Dec"};
-#else
- char *origbuf = buf;
#endif
+ char *origbuf = buf;
char tmp[3];
@@ -1707,6 +1729,14 @@ ztrftime(char *buf, int bufsize, char *fmt, struct tm *tm)
while (*fmt)
if (*fmt == '%') {
fmt++;
+ /*
+ * Assume this format will take up at least two
+ * characters. Not always true, but if that matters
+ * we are so close to the edge it's not a big deal.
+ * Fix up some longer cases specially when we get to them.
+ */
+ if (ztrftimebuf(&bufsize, 2))
+ return 0;
switch (*fmt++) {
case 'd':
*buf++ = '0' + tm->tm_mday / 10;
@@ -1734,9 +1764,10 @@ ztrftime(char *buf, int bufsize, char *fmt, struct tm *tm)
if (hr12 == 0)
hr12 = 12;
if (hr12 > 9)
- *buf++ = '1';
+ *buf++ = '1';
else if (fmt[-1] == 'l')
- *buf++ = ' ';
+ *buf++ = ' ';
+
*buf++ = '0' + (hr12 % 10);
break;
case 'm':
@@ -1755,11 +1786,20 @@ ztrftime(char *buf, int bufsize, char *fmt, struct tm *tm)
*buf++ = '0' + (tm->tm_year / 10) % 10;
*buf++ = '0' + tm->tm_year % 10;
break;
+ case '\0':
+ /* Guard against premature end of string */
+ *buf++ = '%';
+ fmt--;
+ break;
#ifndef HAVE_STRFTIME
case 'a':
+ if (ztrftimebuf(&bufsize, strlen(astr[tm->tm_wday]) - 2))
+ return 0;
strucpy(&buf, astr[tm->tm_wday]);
break;
case 'b':
+ if (ztrftimebuf(&bufsize, strlen(estr[tm->tm_mon]) - 2))
+ return 0;
strucpy(&buf, estr[tm->tm_mon]);
break;
case 'p':
@@ -1772,17 +1812,27 @@ ztrftime(char *buf, int bufsize, char *fmt, struct tm *tm)
*buf++ = fmt[-1];
#else
default:
+ /*
+ * Remember we've already allowed for two characters
+ * in the accounting in bufsize (but nowhere else).
+ */
*buf = '\0';
tmp[1] = fmt[-1];
- strftime(buf, bufsize - strlen(origbuf), tmp, tm);
- buf += strlen(buf);
+ if (!strftime(buf, bufsize + 2, tmp, tm))
+ return 0;
+ decr = strlen(buf);
+ buf += decr;
+ bufsize -= decr - 2;
#endif
break;
}
- } else
+ } else {
+ if (ztrftimebuf(&bufsize, 1))
+ return 0;
*buf++ = *fmt++;
+ }
*buf = '\0';
- return 0;
+ return buf - origbuf;
}
/**/