Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

Re: mkdir builtin and $'\0'



On Tue, 18 Aug 2015 14:24:58 +0100
Peter Stephenson <p.stephenson@xxxxxxxxxxx> wrote:
> On Tue, 18 Aug 2015 13:55:19 +0100
> Stephane Chazelas <stephane.chazelas@xxxxxxxxx> wrote:
> > 2015-08-18 12:27:55 +0100, Peter Stephenson:
> > > On Tue, 18 Aug 2015 12:11:34 +0100
> > > Stephane Chazelas <stephane.chazelas@xxxxxxxxx> wrote:
> > > > $ strftime $'%Y\0%m\0%d' 0 | sed -n l
> > > > 1970\203 01\203 01$
> > > 
> > > This should fix that one.
> > [...]
> > 
> > Thanks, though it seems to have broken some internationalisation
> > support.
> 
> That was Ismail's problem, too... in fact, if I'd looked above, I'd have
> seen that buffer isn't actually metafied at that point, duh.  So I'll
> need to look further at what's causing you're original probalem ---
> looks like something metafied has crept in.  I'll back off the original
> change.

OK, so the answer at the moment is that ztrftime() assumes everything is
unmetafied.  That's inevitable with the output coming out from
strftime(), so what we pass in needs to be consistent.  However,
actually in every case fmt is passed in still metafied.  So we should
unmetafy it and handle appropriately internally.

For example:

% print -P $'%D{%Y\0%m\0%d}' 0 | sed -n l
2015\203 08\203 18 0$

The caller then needs to metafy where appropriate, so we need to use the
length returned from ztrftime().

This seems to work for strftime and primt -P %D and the tests pass.

One unconnected drive-by unmeta if sched output --- the ztrftime there
was OK because the input is a fixed string and the output is going
straight to printf --- although that means it doesn't handled embedded
NULLs which probably isn't a big issue in this case.

pws

diff --git a/Src/Builtins/sched.c b/Src/Builtins/sched.c
index bcf7661..5d5dac6 100644
--- a/Src/Builtins/sched.c
+++ b/Src/Builtins/sched.c
@@ -220,7 +220,8 @@ bin_sched(char *nam, char **argv, UNUSED(Options ops), UNUSED(int func))
 		endstr = "-- ";
 	    else
 		endstr = "";
-	    printf("%3d %s %s%s%s\n", sn, tbuf, flagstr, endstr, sch->cmd);
+	    printf("%3d %s %s%s%s\n", sn, tbuf, flagstr, endstr,
+		   unmeta(sch->cmd));
 	}
 	return 0;
     } else if (!argptr[1]) {
diff --git a/Src/Modules/datetime.c b/Src/Modules/datetime.c
index d941667..86c61cf 100644
--- a/Src/Modules/datetime.c
+++ b/Src/Modules/datetime.c
@@ -98,7 +98,7 @@ reverse_strftime(char *nam, char **argv, char *scalar, int quiet)
 static int
 output_strftime(char *nam, char **argv, Options ops, UNUSED(int func))
 {
-    int bufsize, x;
+    int bufsize, x, len;
     char *endptr = NULL, *scalar = NULL, *buffer;
     time_t secs;
     struct tm *t;
@@ -131,16 +131,19 @@ output_strftime(char *nam, char **argv, Options ops, UNUSED(int func))
     bufsize = strlen(argv[0]) * 8;
     buffer = zalloc(bufsize);
 
+    len = 0;
     for (x=0; x < 4; x++) {
-        if (ztrftime(buffer, bufsize, argv[0], t, 0L) >= 0)
+        if ((len = ztrftime(buffer, bufsize, argv[0], t, 0L)) >= 0)
 	    break;
 	buffer = zrealloc(buffer, bufsize *= 2);
     }
+    DPUTS(len < 0, "bad output from ztrftime");
 
     if (scalar) {
-	setsparam(scalar, metafy(buffer, -1, META_DUP));
+	setsparam(scalar, metafy(buffer, len, META_DUP));
     } else {
-	printf("%s\n", buffer);
+	fwrite(buffer, 1, len, stdout);
+	putchar('\n');
     }
     zfree(buffer, bufsize);
 
diff --git a/Src/Modules/stat.c b/Src/Modules/stat.c
index 6fc5389..3961771 100644
--- a/Src/Modules/stat.c
+++ b/Src/Modules/stat.c
@@ -197,8 +197,11 @@ stattimeprint(time_t tim, char *outbuf, int flags)
     }
     if (flags & STF_STRING) {
 	char *oend = outbuf + strlen(outbuf);
-	ztrftime(oend, 40, timefmt, (flags & STF_GMT) ? gmtime(&tim) :
-		 localtime(&tim), 0L);
+	/* Where the heck does "40" come from? */
+	int len = ztrftime(oend, 40, timefmt, (flags & STF_GMT) ? gmtime(&tim) :
+			   localtime(&tim), 0L);
+	if (len > 0)
+	    metafy(oend, len, META_NOALLOC);
 	if (flags & STF_RAW)
 	    strcat(oend, ")");
     }
diff --git a/Src/builtin.c b/Src/builtin.c
index 4a97a31..572a0dd 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -1783,9 +1783,12 @@ fclist(FILE *f, Options ops, zlong first, zlong last,
 	       command, if required */
 	    if (tdfmt != NULL) {
 		struct tm *ltm;
+		int len;
 		ltm = localtime(&ent->stim);
-		if (ztrftime(timebuf, 256, tdfmt, ltm, 0L))
-		    fprintf(f, "%s  ", timebuf);
+		if ((len = ztrftime(timebuf, 256, tdfmt, ltm, 0L)) >= 0) {
+		    fwrite(timebuf, 1, len, f);
+		    fprintf(f, "  ");
+		}
 	    }
 	    /* display the time taken by the command, if required */
 	    if (OPT_ISSET(ops,'D')) {
diff --git a/Src/prompt.c b/Src/prompt.c
index 9e8589d..be067ee 100644
--- a/Src/prompt.c
+++ b/Src/prompt.c
@@ -271,7 +271,7 @@ static int
 putpromptchar(int doprint, int endchar, unsigned int *txtchangep)
 {
     char *ss, *hostnam;
-    int t0, arg, test, sep, j, numjobs;
+    int t0, arg, test, sep, j, numjobs, len;
     struct tm *tm;
     struct timezone dummy_tz;
     struct timeval tv;
@@ -673,12 +673,14 @@ putpromptchar(int doprint, int endchar, unsigned int *txtchangep)
 		     */
 		    for(j = 0, t0 = strlen(tmfmt)*8; j < 3; j++, t0*=2) {
 			addbufspc(t0);
-			if (ztrftime(bv->bp, t0, tmfmt, tm, tv.tv_usec) >= 0)
+			if ((len = ztrftime(bv->bp, t0, tmfmt, tm, tv.tv_usec))
+			    >= 0)
 			    break;
 		    }
 		    /* There is enough room for this because addbufspc(t0)
 		     * allocates room for t0 * 2 bytes. */
-		    metafy(bv->bp, -1, META_NOALLOC);
+		    if (len >= 0)
+			metafy(bv->bp, len, META_NOALLOC);
 		    bv->bp += strlen(bv->bp);
 		    zsfree(tmbuf);
 		    break;
diff --git a/Src/utils.c b/Src/utils.c
index 236661a..20e01a2 100644
--- a/Src/utils.c
+++ b/Src/utils.c
@@ -2878,6 +2878,10 @@ ztrftimebuf(int *bufsizeptr, int decr)
  * not enough memory --- and return -1.  Not guaranteed to be portable,
  * since the strftime() interface doesn't make any guarantees about
  * the state of the buffer if it returns zero.
+ *
+ * fmt is metafied, but we need to unmetafy it on the fly to
+ * pass into strftime / combine with the output from strftime.
+ * The return value in buf is not metafied.
  */
 
 /**/
@@ -2898,8 +2902,14 @@ ztrftime(char *buf, int bufsize, char *fmt, struct tm *tm, long usec)
     char *origbuf = buf;
 
 
-    while (*fmt)
-	if (*fmt == '%') {
+    while (*fmt) {
+	if (*fmt == Meta) {
+	    int chr = fmt[1] ^ 32;
+	    if (ztrftimebuf(&bufsize, 1))
+		return -1;
+	    *buf++ = chr;
+	    fmt += 2;
+	} else if (*fmt == '%') {
 	    int strip;
 	    int digs = 3;
 
@@ -3083,8 +3093,21 @@ strftimehandling:
 		 */
 		{
 		    int size = fmt - fmtstart;
-		    char *tmp = zhalloc(size + 1);
+		    char *tmp, *last;
+		    tmp = zhalloc(size + 1);
 		    strncpy(tmp, fmtstart, size);
+		    last = fmt-1;
+		    if (*last == Meta) {
+			/*
+			 * This is for consistency in counting:
+			 * a metafiable character isn't actually
+			 * a valid strftime descriptor.
+			 *
+			 * Previous characters were explicitly checked,
+			 * so can't be metafied.
+			 */
+			*last = *++fmt ^ 32;
+		    }
 		    tmp[size] = '\0';
 		    *buf = '\1';
 		    if (!strftime(buf, bufsize + 2, tmp, tm))
@@ -3107,6 +3130,7 @@ strftimehandling:
 		return -1;
 	    *buf++ = *fmt++;
 	}
+    }
     *buf = '\0';
     return buf - origbuf;
 }
diff --git a/Src/watch.c b/Src/watch.c
index e1bdaa4..c804913 100644
--- a/Src/watch.c
+++ b/Src/watch.c
@@ -237,6 +237,7 @@ watchlog2(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt, int fini)
     time_t timet;
     struct tm *tm;
     char *fm2;
+    int len;
 # ifdef WATCH_UTMP_UT_HOST
     char *p;
     int i;
@@ -330,7 +331,9 @@ watchlog2(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt, int fini)
 		    }
 		    timet = getlogtime(u, inout);
 		    tm = localtime(&timet);
-		    ztrftime(buf, 40, fm2, tm, 0L);
+		    len = ztrftime(buf, 40, fm2, tm, 0L);
+		    if (len > 0)
+			metafy(buf, len, META_NOALLOC);
 		    printf("%s", (*buf == ' ') ? buf + 1 : buf);
 		    break;
 		case '%':



Messages sorted by: Reverse Date, Date, Thread, Author