Zsh Mailing List Archive
Messages sorted by:
Reverse Date,
Date,
Thread,
Author
Re: [bug-report] ztrftime and datetime strftime
- X-seq: zsh-workers 19168
- From: Peter Stephenson <pws@xxxxxxxxxxxxxxxxxxxxxxxx>
- To: Zsh hackers list <zsh-workers@xxxxxxxxxx>
- Subject: Re: [bug-report] ztrftime and datetime strftime
- Date: Mon, 06 Oct 2003 23:42:02 +0100
- In-reply-to: "Stephane CHAZELAS"'s message of "Sat, 04 Oct 2003 14:46:24 +0200." <20031004124624.GA3640@xxxxxxxxxxxxxxxxxx>
- Mailing-list: contact zsh-workers-help@xxxxxxxxxx; run by ezmlm
Stephane CHAZELAS wrote:
> [message posted to Peter Stephenson and zsh-workers ML, even if
> it will probably never show up on the ML for a reason I don't
> know of]
>
> There are several problems with the Src/utils.c ztrftime function:
>
> It always returns 0.
I can't work out what it was originally supposed to do. This must be an
age-old oversight. I've made it do what strftime() does --- hope this
works with strftimes other than Linux.
> When calling libc strftime, if the buffer is too small, the
> error is not reported. buffer overflows are not checked
> (PS1="%D{${(l:10000::%d:)}}" segfaults)
Fixed as a consequence.
> In Src/Modules/datetime.c, it is called as:
>
> <<
> bufsize = strlen(argv[0]) * 2;
>
> for (x=1;x<4;x++) {
> buffer = zrealloc(buffer, bufsize * x);
> size = ztrftime(buffer, bufsize * x, argv[0], t);
> if (size) x = 4;
> }
> >>
Hmm. I don't know what the `size' variable's doing and why x=4 is used
to end the loop, so I've just removed those.
I presume it was just an oversight that buffer wasn't being freed.
> So, it is always called 3 times as ztrftime always returns 0.
>
> strftime %c $SECS
>
> returns an empty string as %c expansion is more that 12 char
> long.
I made the increase exponential. It works for my locale, but I haven't
done any calculations to check the range.
> Src/prompt.c is also wrong:
>
> for(t0=80; ; t0*=2) {
> addbufspc(t0);
> if(ztrftime(bp, t0, tmfmt, tm) != t0)
> break;
> }
This *really* makes me wonder what the interface was supposed to
be... I've just removed the `!= t0'.
I also discovered that `print -P %D{%}' was causing a pointer overrun in
ztrftime which could cause garbage to be printed.
None of this gives me a warm and fuzzy feeling about the internals.
Please test again...
Index: Src/prompt.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/prompt.c,v
retrieving revision 1.11
diff -u -r1.11 prompt.c
--- Src/prompt.c 27 Jan 2003 16:39:25 -0000 1.11
+++ Src/prompt.c 6 Oct 2003 22:37:50 -0000
@@ -500,18 +500,23 @@
tmfmt = "%m/%d/%y";
break;
case 'D':
- if (fm[1] == '{') /*}*/ {
+ if (fm[1] == '{' /*}*/) {
for (ss = fm + 2; *ss && *ss != /*{*/ '}'; ss++)
if(*ss == '\\' && ss[1])
ss++;
dd = tmfmt = tmbuf = zalloc(ss - fm);
- for (ss = fm + 2; *ss && *ss != /*{*/ '}'; ss++) {
+ for (ss = fm + 2; *ss && *ss != /*{*/ '}';
+ ss++) {
if(*ss == '\\' && ss[1])
ss++;
*dd++ = *ss;
}
*dd = 0;
fm = ss - !*ss;
+ if (!*tmfmt) {
+ free(tmbuf);
+ continue;
+ }
} else
tmfmt = "%y-%m-%d";
break;
@@ -523,7 +528,7 @@
tm = localtime(&timet);
for(t0=80; ; t0*=2) {
addbufspc(t0);
- if(ztrftime(bp, t0, tmfmt, tm) != t0)
+ if (ztrftime(bp, t0, tmfmt, tm))
break;
}
bp += strlen(bp);
Index: Src/utils.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/utils.c,v
retrieving revision 1.54
diff -u -r1.54 utils.c
--- Src/utils.c 24 Sep 2003 14:55:33 -0000 1.54
+++ Src/utils.c 6 Oct 2003 22:38:07 -0000
@@ -1685,20 +1685,42 @@
}
}
+/*
+ * 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 @@
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 @@
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 @@
*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 @@
*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;
}
/**/
Index: Src/Modules/datetime.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/Modules/datetime.c,v
retrieving revision 1.4
diff -u -r1.4 datetime.c
--- Src/Modules/datetime.c 28 Sep 2003 16:27:00 -0000 1.4
+++ Src/Modules/datetime.c 6 Oct 2003 22:38:07 -0000
@@ -35,10 +35,9 @@
bin_strftime(char *nam, char **argv, Options ops, int func)
{
int bufsize, x;
- char *endptr = NULL, *buffer = NULL;
+ char *endptr = NULL, *buffer;
time_t secs;
struct tm *t;
- int size;
secs = (time_t)strtoul(argv[1], &endptr, 10);
if (secs == ULONG_MAX) {
@@ -51,15 +50,17 @@
t = localtime(&secs);
bufsize = strlen(argv[0]) * 2;
+ buffer = zalloc(bufsize);
- for (x=1;x<4;x++) {
- buffer = zrealloc(buffer, bufsize * x);
- size = ztrftime(buffer, bufsize * x, argv[0], t);
- if (size) x = 4;
+ for (x=0; x < 4; x++) {
+ if (ztrftime(buffer, bufsize, argv[0], t))
+ break;
+ buffer = zrealloc(buffer, bufsize *= 2);
}
printf("%s\n", buffer);
-
+ zfree(buffer, bufsize);
+
return 0;
}
--
Peter Stephenson <pws@xxxxxxxxxxxxxxxxxxxxxxxx>
Work: pws@xxxxxxx
Web: http://www.pwstephenson.fsnet.co.uk
Messages sorted by:
Reverse Date,
Date,
Thread,
Author