|
@@ -1090,13 +1090,31 @@ static int lua_number2strx (lua_State *L, char *buff, int sz,
|
|
|
|
|
|
|
|
|
|
/* valid flags in a format specification */
|
|
/* valid flags in a format specification */
|
|
-#if !defined(L_FMTFLAGS)
|
|
|
|
-#define L_FMTFLAGS "-+ #0"
|
|
|
|
|
|
+#if !defined(L_FMTFLAGSF)
|
|
|
|
+
|
|
|
|
+/* valid flags for a, A, e, E, f, F, g, and G conversions */
|
|
|
|
+#define L_FMTFLAGSF "-+#0 "
|
|
|
|
+
|
|
|
|
+/* valid flags for o, x, and X conversions */
|
|
|
|
+#define L_FMTFLAGSX "-#0"
|
|
|
|
+
|
|
|
|
+/* valid flags for d and i conversions */
|
|
|
|
+#define L_FMTFLAGSI "-+0 "
|
|
|
|
+
|
|
|
|
+/* valid flags for u conversions */
|
|
|
|
+#define L_FMTFLAGSU "-0"
|
|
|
|
+
|
|
|
|
+/* valid flags for c, p, and s conversions */
|
|
|
|
+#define L_FMTFLAGSC "-"
|
|
|
|
+
|
|
#endif
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/*
|
|
/*
|
|
-** maximum size of each format specification (such as "%-099.99d")
|
|
|
|
|
|
+** Maximum size of each format specification (such as "%-099.99d"):
|
|
|
|
+** Initial '%', flags (up to 5), width (2), period, precision (2),
|
|
|
|
+** length modifier (8), conversion specifier, and final '\0', plus some
|
|
|
|
+** extra.
|
|
*/
|
|
*/
|
|
#define MAX_FORMAT 32
|
|
#define MAX_FORMAT 32
|
|
|
|
|
|
@@ -1189,25 +1207,53 @@ static void addliteral (lua_State *L, luaL_Buffer *b, int arg) {
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
-static const char *scanformat (lua_State *L, const char *strfrmt, char *form) {
|
|
|
|
- const char *p = strfrmt;
|
|
|
|
- while (*p != '\0' && strchr(L_FMTFLAGS, *p) != NULL) p++; /* skip flags */
|
|
|
|
- if ((size_t)(p - strfrmt) >= sizeof(L_FMTFLAGS)/sizeof(char))
|
|
|
|
- luaL_error(L, "invalid format (repeated flags)");
|
|
|
|
- if (isdigit(uchar(*p))) p++; /* skip width */
|
|
|
|
- if (isdigit(uchar(*p))) p++; /* (2 digits at most) */
|
|
|
|
- if (*p == '.') {
|
|
|
|
- p++;
|
|
|
|
- if (isdigit(uchar(*p))) p++; /* skip precision */
|
|
|
|
- if (isdigit(uchar(*p))) p++; /* (2 digits at most) */
|
|
|
|
|
|
+static const char *get2digits (const char *s) {
|
|
|
|
+ if (isdigit(uchar(*s))) {
|
|
|
|
+ s++;
|
|
|
|
+ if (isdigit(uchar(*s))) s++; /* (2 digits at most) */
|
|
|
|
+ }
|
|
|
|
+ return s;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+** Chech whether a conversion specification is valid. When called,
|
|
|
|
+** first character in 'form' must be '%' and last character must
|
|
|
|
+** be a valid conversion specifier. 'flags' are the accepted flags;
|
|
|
|
+** 'precision' signals whether to accept a precision.
|
|
|
|
+*/
|
|
|
|
+static void checkformat (lua_State *L, const char *form, const char *flags,
|
|
|
|
+ int precision) {
|
|
|
|
+ const char *spec = form + 1; /* skip '%' */
|
|
|
|
+ spec += strspn(spec, flags); /* skip flags */
|
|
|
|
+ if (*spec != '0') { /* a width cannot start with '0' */
|
|
|
|
+ spec = get2digits(spec); /* skip width */
|
|
|
|
+ if (*spec == '.' && precision) {
|
|
|
|
+ spec++;
|
|
|
|
+ spec = get2digits(spec); /* skip precision */
|
|
|
|
+ }
|
|
}
|
|
}
|
|
- if (isdigit(uchar(*p)))
|
|
|
|
- luaL_error(L, "invalid format (width or precision too long)");
|
|
|
|
|
|
+ if (!isalpha(uchar(*spec))) /* did not go to the end? */
|
|
|
|
+ luaL_error(L, "invalid conversion specification: '%s'", form);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+** Get a conversion specification and copy it to 'form'.
|
|
|
|
+** Return the address of its last character.
|
|
|
|
+*/
|
|
|
|
+static const char *getformat (lua_State *L, const char *strfrmt,
|
|
|
|
+ char *form) {
|
|
|
|
+ /* spans flags, width, and precision ('0' is included as a flag) */
|
|
|
|
+ size_t len = strspn(strfrmt, L_FMTFLAGSF "123456789.");
|
|
|
|
+ len++; /* adds following character (should be the specifier) */
|
|
|
|
+ /* still needs space for '%', '\0', plus a length modifier */
|
|
|
|
+ if (len >= MAX_FORMAT - 10)
|
|
|
|
+ luaL_error(L, "invalid format (too long)");
|
|
*(form++) = '%';
|
|
*(form++) = '%';
|
|
- memcpy(form, strfrmt, ((p - strfrmt) + 1) * sizeof(char));
|
|
|
|
- form += (p - strfrmt) + 1;
|
|
|
|
- *form = '\0';
|
|
|
|
- return p;
|
|
|
|
|
|
+ memcpy(form, strfrmt, len * sizeof(char));
|
|
|
|
+ *(form + len) = '\0';
|
|
|
|
+ return strfrmt + len - 1;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -1230,6 +1276,7 @@ static int str_format (lua_State *L) {
|
|
size_t sfl;
|
|
size_t sfl;
|
|
const char *strfrmt = luaL_checklstring(L, arg, &sfl);
|
|
const char *strfrmt = luaL_checklstring(L, arg, &sfl);
|
|
const char *strfrmt_end = strfrmt+sfl;
|
|
const char *strfrmt_end = strfrmt+sfl;
|
|
|
|
+ const char *flags;
|
|
luaL_Buffer b;
|
|
luaL_Buffer b;
|
|
luaL_buffinit(L, &b);
|
|
luaL_buffinit(L, &b);
|
|
while (strfrmt < strfrmt_end) {
|
|
while (strfrmt < strfrmt_end) {
|
|
@@ -1239,25 +1286,35 @@ static int str_format (lua_State *L) {
|
|
luaL_addchar(&b, *strfrmt++); /* %% */
|
|
luaL_addchar(&b, *strfrmt++); /* %% */
|
|
else { /* format item */
|
|
else { /* format item */
|
|
char form[MAX_FORMAT]; /* to store the format ('%...') */
|
|
char form[MAX_FORMAT]; /* to store the format ('%...') */
|
|
- int maxitem = MAX_ITEM;
|
|
|
|
- char *buff = luaL_prepbuffsize(&b, maxitem); /* to put formatted item */
|
|
|
|
- int nb = 0; /* number of bytes in added item */
|
|
|
|
|
|
+ int maxitem = MAX_ITEM; /* maximum length for the result */
|
|
|
|
+ char *buff = luaL_prepbuffsize(&b, maxitem); /* to put result */
|
|
|
|
+ int nb = 0; /* number of bytes in result */
|
|
if (++arg > top)
|
|
if (++arg > top)
|
|
return luaL_argerror(L, arg, "no value");
|
|
return luaL_argerror(L, arg, "no value");
|
|
- strfrmt = scanformat(L, strfrmt, form);
|
|
|
|
|
|
+ strfrmt = getformat(L, strfrmt, form);
|
|
switch (*strfrmt++) {
|
|
switch (*strfrmt++) {
|
|
case 'c': {
|
|
case 'c': {
|
|
|
|
+ checkformat(L, form, L_FMTFLAGSC, 0);
|
|
nb = l_sprintf(buff, maxitem, form, (int)luaL_checkinteger(L, arg));
|
|
nb = l_sprintf(buff, maxitem, form, (int)luaL_checkinteger(L, arg));
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
case 'd': case 'i':
|
|
case 'd': case 'i':
|
|
- case 'o': case 'u': case 'x': case 'X': {
|
|
|
|
|
|
+ flags = L_FMTFLAGSI;
|
|
|
|
+ goto intcase;
|
|
|
|
+ case 'u':
|
|
|
|
+ flags = L_FMTFLAGSU;
|
|
|
|
+ goto intcase;
|
|
|
|
+ case 'o': case 'x': case 'X':
|
|
|
|
+ flags = L_FMTFLAGSX;
|
|
|
|
+ intcase: {
|
|
lua_Integer n = luaL_checkinteger(L, arg);
|
|
lua_Integer n = luaL_checkinteger(L, arg);
|
|
|
|
+ checkformat(L, form, flags, 1);
|
|
addlenmod(form, LUA_INTEGER_FRMLEN);
|
|
addlenmod(form, LUA_INTEGER_FRMLEN);
|
|
nb = l_sprintf(buff, maxitem, form, (LUAI_UACINT)n);
|
|
nb = l_sprintf(buff, maxitem, form, (LUAI_UACINT)n);
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
case 'a': case 'A':
|
|
case 'a': case 'A':
|
|
|
|
+ checkformat(L, form, L_FMTFLAGSF, 1);
|
|
addlenmod(form, LUA_NUMBER_FRMLEN);
|
|
addlenmod(form, LUA_NUMBER_FRMLEN);
|
|
nb = lua_number2strx(L, buff, maxitem, form,
|
|
nb = lua_number2strx(L, buff, maxitem, form,
|
|
luaL_checknumber(L, arg));
|
|
luaL_checknumber(L, arg));
|
|
@@ -1268,12 +1325,14 @@ static int str_format (lua_State *L) {
|
|
/* FALLTHROUGH */
|
|
/* FALLTHROUGH */
|
|
case 'e': case 'E': case 'g': case 'G': {
|
|
case 'e': case 'E': case 'g': case 'G': {
|
|
lua_Number n = luaL_checknumber(L, arg);
|
|
lua_Number n = luaL_checknumber(L, arg);
|
|
|
|
+ checkformat(L, form, L_FMTFLAGSF, 1);
|
|
addlenmod(form, LUA_NUMBER_FRMLEN);
|
|
addlenmod(form, LUA_NUMBER_FRMLEN);
|
|
nb = l_sprintf(buff, maxitem, form, (LUAI_UACNUMBER)n);
|
|
nb = l_sprintf(buff, maxitem, form, (LUAI_UACNUMBER)n);
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
case 'p': {
|
|
case 'p': {
|
|
const void *p = lua_topointer(L, arg);
|
|
const void *p = lua_topointer(L, arg);
|
|
|
|
+ checkformat(L, form, L_FMTFLAGSC, 0);
|
|
if (p == NULL) { /* avoid calling 'printf' with argument NULL */
|
|
if (p == NULL) { /* avoid calling 'printf' with argument NULL */
|
|
p = "(null)"; /* result */
|
|
p = "(null)"; /* result */
|
|
form[strlen(form) - 1] = 's'; /* format it as a string */
|
|
form[strlen(form) - 1] = 's'; /* format it as a string */
|
|
@@ -1294,7 +1353,8 @@ static int str_format (lua_State *L) {
|
|
luaL_addvalue(&b); /* keep entire string */
|
|
luaL_addvalue(&b); /* keep entire string */
|
|
else {
|
|
else {
|
|
luaL_argcheck(L, l == strlen(s), arg, "string contains zeros");
|
|
luaL_argcheck(L, l == strlen(s), arg, "string contains zeros");
|
|
- if (!strchr(form, '.') && l >= 100) {
|
|
|
|
|
|
+ checkformat(L, form, L_FMTFLAGSC, 1);
|
|
|
|
+ if (strchr(form, '.') == NULL && l >= 100) {
|
|
/* no precision and string is too long to be formatted */
|
|
/* no precision and string is too long to be formatted */
|
|
luaL_addvalue(&b); /* keep entire string */
|
|
luaL_addvalue(&b); /* keep entire string */
|
|
}
|
|
}
|