integer formats in 'string.format' may need to operate with larger-than-int

types
This commit is contained in:
Roberto Ierusalimschy 2005-12-15 16:53:34 -02:00
parent 43c61fc113
commit 9fbefdf69c
2 changed files with 58 additions and 21 deletions

View File

@ -1,5 +1,5 @@
/*
** $Id: lstrlib.c,v 1.126 2005/10/23 17:46:30 roberto Exp roberto $
** $Id: lstrlib.c,v 1.127 2005/10/26 13:28:19 roberto Exp roberto $
** Standard library for string operations and pattern-matching
** See Copyright Notice in lua.h
*/
@ -683,8 +683,13 @@ static int str_gsub (lua_State *L) {
/* maximum size of each formatted item (> len(format('%99.99f', -1e308))) */
#define MAX_ITEM 512
/* maximum size of each format specification (such as '%-099.99d') */
#define MAX_FORMAT 20
/* valid flags in a format specification */
#define FLAGS "-+ #0"
/*
** maximum size of each format specification (such as '%-099.99d')
** (+10 accounts for %99.99x plus margin of error)
*/
#define MAX_FORMAT (sizeof(FLAGS) + sizeof(LUA_INTFRMLEN) + 10)
static void addquoted (lua_State *L, luaL_Buffer *b, int arg) {
@ -712,30 +717,37 @@ static void addquoted (lua_State *L, luaL_Buffer *b, int arg) {
luaL_addchar(b, '"');
}
static const char *scanformat (lua_State *L, const char *strfrmt,
char *form, int *hasprecision) {
static const char *scanformat (lua_State *L, const char *strfrmt, char *form) {
const char *p = strfrmt;
while (strchr("-+ #0", *p)) p++; /* skip flags */
while (strchr(FLAGS, *p)) p++; /* skip flags */
if ((size_t)(p - strfrmt) >= sizeof(FLAGS))
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++;
*hasprecision = 1;
if (isdigit(uchar(*p))) p++; /* skip precision */
if (isdigit(uchar(*p))) p++; /* (2 digits at most) */
}
if (isdigit(uchar(*p)))
luaL_error(L, "invalid format (width or precision too long)");
if (p-strfrmt+2 > MAX_FORMAT) /* +2 to include `%' and the specifier */
luaL_error(L, "invalid format (too long)");
form[0] = L_ESC;
strncpy(form+1, strfrmt, p-strfrmt+1);
form[p-strfrmt+2] = 0;
*(form++) = '%';
strncpy(form, strfrmt, p - strfrmt + 1);
form += p - strfrmt + 1;
*form = '\0';
return p;
}
static void addintlen (char *form) {
int l = strlen(form);
char spec = form[l - 1];
strcpy(form + l - 1, LUA_INTFRMLEN);
form[l + sizeof(LUA_INTFRMLEN) - 2] = spec;
form[l + sizeof(LUA_INTFRMLEN) - 1] = '\0';
}
static int str_format (lua_State *L) {
int arg = 1;
size_t sfl;
@ -751,21 +763,26 @@ static int str_format (lua_State *L) {
else { /* format item */
char form[MAX_FORMAT]; /* to store the format (`%...') */
char buff[MAX_ITEM]; /* to store the formatted item */
int hasprecision = 0;
arg++;
strfrmt = scanformat(L, strfrmt, form, &hasprecision);
strfrmt = scanformat(L, strfrmt, form);
switch (*strfrmt++) {
case 'c': case 'd': case 'i': {
sprintf(buff, form, luaL_checkint(L, arg));
case 'c': {
sprintf(buff, form, (int)luaL_checknumber(L, arg));
break;
}
case 'd': case 'i': {
addintlen(form);
sprintf(buff, form, (LUA_INTFRM_T)luaL_checknumber(L, arg));
break;
}
case 'o': case 'u': case 'x': case 'X': {
sprintf(buff, form, (unsigned int)(luaL_checknumber(L, arg)));
addintlen(form);
sprintf(buff, form, (unsigned LUA_INTFRM_T)luaL_checknumber(L, arg));
break;
}
case 'e': case 'E': case 'f':
case 'g': case 'G': {
sprintf(buff, form, luaL_checknumber(L, arg));
sprintf(buff, form, (double)luaL_checknumber(L, arg));
break;
}
case 'q': {
@ -775,7 +792,7 @@ static int str_format (lua_State *L) {
case 's': {
size_t l;
const char *s = luaL_checklstring(L, arg, &l);
if (!hasprecision && l >= 100) {
if (!strchr(form, '.') && l >= 100) {
/* no precision and string is too long to be formatted;
keep original string */
lua_pushvalue(L, arg);

View File

@ -1,5 +1,5 @@
/*
** $Id: luaconf.h,v 1.74 2005/11/16 16:24:28 roberto Exp roberto $
** $Id: luaconf.h,v 1.75 2005/11/25 13:29:11 roberto Exp roberto $
** Configuration file for Lua
** See Copyright Notice in lua.h
*/
@ -708,6 +708,26 @@ union luai_Cast { double l_d; long l_l; };
#define luai_userstateyield(L,n) ((void)L)
/*
@@ LUA_INTFRMLEN is the length modifier for integer conversions
@* in 'string.fomat'.
@@ LUA_INTFRM_T is the integer type correspoding to the previous length
@* modifier.
** CHANGE them if your system supports long long or does not support long.
*/
#if defined(LUA_USELONGLONG)
#define LUA_INTFRMLEN "ll"
#define LUA_INTFRM_T long long
#else
#define LUA_INTFRMLEN "l"
#define LUA_INTFRM_T long
#endif
/* =================================================================== */