shithub: lu9-lua

Download patch

ref: 494cc82554be6cb3ebd823177f47e8bbbb52439c
parent: e69cf9de6c04202e0ab133821dc2a41a71f219fb
author: kvik <kvik@a-b.xyz>
date: Mon Feb 22 11:27:13 EST 2021

los9lib: make os.date accept and output Lua rather than C strings

Lua wants the format string and formatted output to be Lua rather than C
strings, allowing embedded zero bytes to be formatted.
This means we can't use the fmtprint(2) routines to directly construct
the output, so we split the formatting into pieces and use a lua_Buffer
to construct the result Lua string.

This also fixes the '%%' escape.

--- a/los9lib.c
+++ b/los9lib.c
@@ -249,98 +249,110 @@
 	return 1;
 }
 
-static void
-l_fmttime(lua_State *L, char *fmt, Tm *tm)
+static char*
+datefmt1(lua_State *L, char **sp, Tm *tm)
 {
 	char *s, mod;
 	Fmt f;
 	
 	fmtstrinit(&f);
-	for(s = fmt; *s; s++){
-		if(s[0] != '%'){
-			fmtprint(&f, "%c", s[0]);
-			continue;
-		}
+	s = *sp;
+	if(s[0] == 'E' || s[0] == '0'){
+		mod = s[0];
 		s++;
-		if(s[0] == '%'){
-			fmtprint(&f, "%");
-			continue;
-		}
-		if(s[0] == 'E' || s[0] == '0'){
-			mod = s[0];
+	}
+	switch(s[0]){
+	case '%': fmtprint(&f, "%%"); break;
+	case 'n': fmtprint(&f, "\n"); break;
+	case 't': fmtprint(&f, "\t"); break;
+	
+	case 'y': fmtprint(&f, "%τ", tmfmt(tm, "YY")); break;
+	case 'Y': fmtprint(&f, "%τ", tmfmt(tm, "YYYY")); break;
+	
+	case 'm': fmtprint(&f, "%τ", tmfmt(tm, "MM")); break;
+	case 'b': fmtprint(&f, "%τ", tmfmt(tm, "MMM")); break;
+	case 'B': fmtprint(&f, "%τ", tmfmt(tm, "MMMM")); break;
+	
+	/* TODO: week of the year calculation */
+	case 'U': fmtprint(&f, "00"); break;
+	case 'W': fmtprint(&f, "01"); break;
+	
+	case 'j': fmtprint(&f, "%.3d", tm->yday + 1); break;
+	case 'd': fmtprint(&f, "%.2d", tm->mday); break;
+
+	case 'w': fmtprint(&f, "%τ", tmfmt(tm, "W")); break;
+	case 'a': fmtprint(&f, "%τ", tmfmt(tm, "WW")); break;
+	case 'A': fmtprint(&f, "%τ", tmfmt(tm, "WWW")); break;
+	
+	case 'H': fmtprint(&f, "%τ", tmfmt(tm, "hh")); break;
+	case 'I': fmtprint(&f, "%d", tm->hour % 12); break;
+	
+	case 'M': fmtprint(&f, "%τ", tmfmt(tm, "mm")); break;
+	
+	case 'S': fmtprint(&f, "%τ", tmfmt(tm, "ss")); break;
+
+	case 'c': case 'x': fmtprint(&f, "%τ", tmfmt(tm, nil)); break;
+	case 'X': fmtprint(&f, "%τ", tmfmt(tm, "hh[:]mm[:]ss")); break;
+	
+	case 'p': fmtprint(&f, "%τ", tmfmt(tm, "a")); break;
+	
+	case 'Z': fmtprint(&f, "%τ", tmfmt(tm, "Z")); break;
+	
+	default:
+		fmtprint(&f, "%%");
+		if(mod)
+			fmtprint(&f, "%c", mod);
+		fmtprint(&f, "%c", s[0]);
+		break;
+	}
+	*sp = ++s;
+	return fmtstrflush(&f);
+}
+
+static void
+datefmt(lua_State *L, char *fmt, long fmtlen, Tm *tm)
+{
+	char *s, *e, *p;
+	luaL_Buffer b;
+	
+	luaL_buffinit(L, &b);
+	s = fmt;
+	e = fmt + fmtlen;
+	while(s < e){
+		if(s[0] != '%')
+			luaL_addchar(&b, *s++);
+		else {
 			s++;
+			p = datefmt1(L, &s, tm);
+			luaL_addstring(&b, p);
+			free(p);
 		}
-		switch(s[0]){
-		case 'n': fmtprint(&f, "\n"); break;
-		case 't': fmtprint(&f, "\t"); break;
-		
-		case 'y': fmtprint(&f, "%τ", tmfmt(tm, "YY")); break;
-		case 'Y': fmtprint(&f, "%τ", tmfmt(tm, "YYYY")); break;
-		
-		case 'm': fmtprint(&f, "%τ", tmfmt(tm, "MM")); break;
-		case 'b': fmtprint(&f, "%τ", tmfmt(tm, "MMM")); break;
-		case 'B': fmtprint(&f, "%τ", tmfmt(tm, "MMMM")); break;
-		
-		/* TODO: week of the year calculation */
-		case 'U': fmtprint(&f, "00"); break;
-		case 'W': fmtprint(&f, "01"); break;
-		
-		case 'j': fmtprint(&f, "%.3d", tm->yday + 1); break;
-		case 'd': fmtprint(&f, "%.2d", tm->mday); break;
-
-		case 'w': fmtprint(&f, "%τ", tmfmt(tm, "W")); break;
-		case 'a': fmtprint(&f, "%τ", tmfmt(tm, "WW")); break;
-		case 'A': fmtprint(&f, "%τ", tmfmt(tm, "WWW")); break;
-		
-		case 'H': fmtprint(&f, "%τ", tmfmt(tm, "hh")); break;
-		case 'I': fmtprint(&f, "%d", tm->hour % 12); break;
-		
-		case 'M': fmtprint(&f, "%τ", tmfmt(tm, "mm")); break;
-		
-		case 'S': fmtprint(&f, "%τ", tmfmt(tm, "ss")); break;
-		
-		case 'c': case 'x': fmtprint(&f, "%τ", tmfmt(tm, nil)); break;
-		case 'X': fmtprint(&f, "%τ", tmfmt(tm, "hh[:]mm[:]ss")); break;
-		
-		case 'p': fmtprint(&f, "%τ", tmfmt(tm, "a")); break;
-		
-		case 'Z': fmtprint(&f, "%τ", tmfmt(tm, "Z")); break;
-		
-		default:
-			fmtprint(&f, "%%");
-			if(mod)
-				fmtprint(&f, "%c", mod);
-			fmtprint(&f, "%c", s[0]);
-			break;
-		}
-		mod = 0;
 	}
-	s = fmtstrflush(&f);
-	lua_pushstring(L, s);
-	free(s);
+	luaL_pushresult(&b);
 }
 
 static int
 os_date(lua_State *L)
 {
-	char *s;
+	char *fmt;
+	size_t fmtlen;
 	vlong t;
 	Tm tm, *tp;
 	
-	s = luaL_optstring(L, 1, "%c");
+	fmt = luaL_optlstring(L, 1, "%c", &fmtlen);
 	t = luaL_opt(L, luaL_checkinteger, 2, time(nil));
-	if(s[0] == '!'){
+	if(fmt[0] == '!'){
+		fmt++; fmtlen--;
 		tp = gmtime(t);
-		s++;
 	}else
 		tp = localtime(t);
 	if(tp == nil)
 		return luaL_error(L, "date result cannot be represented in this installation");
-	if(strcmp(s, "*t") == 0){
+	if(strcmp(fmt, "*t") == 0){
 		lua_createtable(L, 0, 9);
 		setallfields(L, tp);
 	}else
-		l_fmttime(L, s, tp);
+		datefmt(L, fmt, fmtlen, tp);
 	return 1;
 }