|
|
|
@ -1,5 +1,5 @@
|
|
|
|
|
/*
|
|
|
|
|
** $Id: lstrlib.c,v 1.259 2017/11/16 13:19:06 roberto Exp roberto $
|
|
|
|
|
** $Id: lstrlib.c $
|
|
|
|
|
** Standard library for string operations and pattern-matching
|
|
|
|
|
** See Copyright Notice in lua.h
|
|
|
|
|
*/
|
|
|
|
@ -60,23 +60,50 @@ static int str_len (lua_State *L) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* translate a relative string position: negative means back from end */
|
|
|
|
|
static lua_Integer posrelat (lua_Integer pos, size_t len) {
|
|
|
|
|
if (pos >= 0) return pos;
|
|
|
|
|
else if (0u - (size_t)pos > len) return 0;
|
|
|
|
|
else return (lua_Integer)len + pos + 1;
|
|
|
|
|
/*
|
|
|
|
|
** translate a relative initial string position
|
|
|
|
|
** (negative means back from end): clip result to [1, inf).
|
|
|
|
|
** The length of any string in Lua must fit in a lua_Integer,
|
|
|
|
|
** so there are no overflows in the casts.
|
|
|
|
|
** The inverted comparison avoids a possible overflow
|
|
|
|
|
** computing '-pos'.
|
|
|
|
|
*/
|
|
|
|
|
static size_t posrelatI (lua_Integer pos, size_t len) {
|
|
|
|
|
if (pos > 0)
|
|
|
|
|
return (size_t)pos;
|
|
|
|
|
else if (pos == 0)
|
|
|
|
|
return 1;
|
|
|
|
|
else if (pos < -(lua_Integer)len) /* inverted comparison */
|
|
|
|
|
return 1; /* clip to 1 */
|
|
|
|
|
else return len + (size_t)pos + 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
** Gets an optional ending string position from argument 'arg',
|
|
|
|
|
** with default value 'def'.
|
|
|
|
|
** Negative means back from end: clip result to [0, len]
|
|
|
|
|
*/
|
|
|
|
|
static size_t getendpos (lua_State *L, int arg, lua_Integer def,
|
|
|
|
|
size_t len) {
|
|
|
|
|
lua_Integer pos = luaL_optinteger(L, arg, def);
|
|
|
|
|
if (pos > (lua_Integer)len)
|
|
|
|
|
return len;
|
|
|
|
|
else if (pos >= 0)
|
|
|
|
|
return (size_t)pos;
|
|
|
|
|
else if (pos < -(lua_Integer)len)
|
|
|
|
|
return 0;
|
|
|
|
|
else return len + (size_t)pos + 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int str_sub (lua_State *L) {
|
|
|
|
|
size_t l;
|
|
|
|
|
const char *s = luaL_checklstring(L, 1, &l);
|
|
|
|
|
lua_Integer start = posrelat(luaL_checkinteger(L, 2), l);
|
|
|
|
|
lua_Integer end = posrelat(luaL_optinteger(L, 3, -1), l);
|
|
|
|
|
if (start < 1) start = 1;
|
|
|
|
|
if (end > (lua_Integer)l) end = l;
|
|
|
|
|
size_t start = posrelatI(luaL_checkinteger(L, 2), l);
|
|
|
|
|
size_t end = getendpos(L, 3, -1, l);
|
|
|
|
|
if (start <= end)
|
|
|
|
|
lua_pushlstring(L, s + start - 1, (size_t)(end - start) + 1);
|
|
|
|
|
lua_pushlstring(L, s + start - 1, (end - start) + 1);
|
|
|
|
|
else lua_pushliteral(L, "");
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
@ -149,13 +176,12 @@ static int str_rep (lua_State *L) {
|
|
|
|
|
static int str_byte (lua_State *L) {
|
|
|
|
|
size_t l;
|
|
|
|
|
const char *s = luaL_checklstring(L, 1, &l);
|
|
|
|
|
lua_Integer posi = posrelat(luaL_optinteger(L, 2, 1), l);
|
|
|
|
|
lua_Integer pose = posrelat(luaL_optinteger(L, 3, posi), l);
|
|
|
|
|
lua_Integer pi = luaL_optinteger(L, 2, 1);
|
|
|
|
|
size_t posi = posrelatI(pi, l);
|
|
|
|
|
size_t pose = getendpos(L, 3, pi, l);
|
|
|
|
|
int n, i;
|
|
|
|
|
if (posi < 1) posi = 1;
|
|
|
|
|
if (pose > (lua_Integer)l) pose = l;
|
|
|
|
|
if (posi > pose) return 0; /* empty interval; return no values */
|
|
|
|
|
if (pose - posi >= INT_MAX) /* arithmetic overflow? */
|
|
|
|
|
if (pose - posi >= (size_t)INT_MAX) /* arithmetic overflow? */
|
|
|
|
|
return luaL_error(L, "string slice too long");
|
|
|
|
|
n = (int)(pose - posi) + 1;
|
|
|
|
|
luaL_checkstack(L, n, "string slice too long");
|
|
|
|
@ -171,8 +197,8 @@ static int str_char (lua_State *L) {
|
|
|
|
|
luaL_Buffer b;
|
|
|
|
|
char *p = luaL_buffinitsize(L, &b, n);
|
|
|
|
|
for (i=1; i<=n; i++) {
|
|
|
|
|
lua_Integer c = luaL_checkinteger(L, i);
|
|
|
|
|
luaL_argcheck(L, uchar(c) == c, i, "value out of range");
|
|
|
|
|
lua_Unsigned c = (lua_Unsigned)luaL_checkinteger(L, i);
|
|
|
|
|
luaL_argcheck(L, c <= (lua_Unsigned)UCHAR_MAX, i, "value out of range");
|
|
|
|
|
p[i - 1] = uchar(c);
|
|
|
|
|
}
|
|
|
|
|
luaL_pushresultsize(&b, n);
|
|
|
|
@ -180,22 +206,38 @@ static int str_char (lua_State *L) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int writer (lua_State *L, const void *b, size_t size, void *B) {
|
|
|
|
|
(void)L;
|
|
|
|
|
luaL_addlstring((luaL_Buffer *) B, (const char *)b, size);
|
|
|
|
|
/*
|
|
|
|
|
** Buffer to store the result of 'string.dump'. It must be initialized
|
|
|
|
|
** after the call to 'lua_dump', to ensure that the function is on the
|
|
|
|
|
** top of the stack when 'lua_dump' is called. ('luaL_buffinit' might
|
|
|
|
|
** push stuff.)
|
|
|
|
|
*/
|
|
|
|
|
struct str_Writer {
|
|
|
|
|
int init; /* true iff buffer has been initialized */
|
|
|
|
|
luaL_Buffer B;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int writer (lua_State *L, const void *b, size_t size, void *ud) {
|
|
|
|
|
struct str_Writer *state = (struct str_Writer *)ud;
|
|
|
|
|
if (!state->init) {
|
|
|
|
|
state->init = 1;
|
|
|
|
|
luaL_buffinit(L, &state->B);
|
|
|
|
|
}
|
|
|
|
|
luaL_addlstring(&state->B, (const char *)b, size);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int str_dump (lua_State *L) {
|
|
|
|
|
luaL_Buffer b;
|
|
|
|
|
struct str_Writer state;
|
|
|
|
|
int strip = lua_toboolean(L, 2);
|
|
|
|
|
luaL_checktype(L, 1, LUA_TFUNCTION);
|
|
|
|
|
lua_settop(L, 1);
|
|
|
|
|
luaL_buffinit(L,&b);
|
|
|
|
|
if (lua_dump(L, writer, &b, strip) != 0)
|
|
|
|
|
lua_settop(L, 1); /* ensure function is on the top of the stack */
|
|
|
|
|
state.init = 0;
|
|
|
|
|
if (lua_dump(L, writer, &state, strip) != 0)
|
|
|
|
|
return luaL_error(L, "unable to dump given function");
|
|
|
|
|
luaL_pushresult(&b);
|
|
|
|
|
luaL_pushresult(&state.B);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -207,6 +249,17 @@ static int str_dump (lua_State *L) {
|
|
|
|
|
** =======================================================
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#if defined(LUA_NOCVTS2N) /* { */
|
|
|
|
|
|
|
|
|
|
/* no coercion from strings to numbers */
|
|
|
|
|
|
|
|
|
|
static const luaL_Reg stringmetamethods[] = {
|
|
|
|
|
{"__index", NULL}, /* placeholder */
|
|
|
|
|
{NULL, NULL}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#else /* }{ */
|
|
|
|
|
|
|
|
|
|
static int tonum (lua_State *L, int arg) {
|
|
|
|
|
if (lua_type(L, arg) == LUA_TNUMBER) { /* already a number? */
|
|
|
|
|
lua_pushvalue(L, arg);
|
|
|
|
@ -220,25 +273,6 @@ static int tonum (lua_State *L, int arg) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int toint (lua_State *L, int arg) {
|
|
|
|
|
if (!tonum(L, arg))
|
|
|
|
|
return 0; /* not coercible to a number */
|
|
|
|
|
else if (lua_isinteger(L, arg))
|
|
|
|
|
return 1; /* already an integer */
|
|
|
|
|
else { /* a float */
|
|
|
|
|
int ok;
|
|
|
|
|
lua_Integer n = lua_tointegerx(L, arg, &ok);
|
|
|
|
|
if (!ok)
|
|
|
|
|
return 0;
|
|
|
|
|
else {
|
|
|
|
|
lua_pop(L, 1); /* remove the float */
|
|
|
|
|
lua_pushinteger(L, n); /* push an integer */
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void trymt (lua_State *L, const char *mtname) {
|
|
|
|
|
lua_settop(L, 2); /* back to the original arguments */
|
|
|
|
|
if (lua_type(L, 2) == LUA_TSTRING || !luaL_getmetafield(L, 2, mtname))
|
|
|
|
@ -258,15 +292,6 @@ static int arith (lua_State *L, int op, const char *mtname) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int bitwise (lua_State *L, int op, const char *mtname) {
|
|
|
|
|
if (toint(L, 1) && toint(L, 2))
|
|
|
|
|
lua_arith(L, op); /* result will be on the top */
|
|
|
|
|
else
|
|
|
|
|
trymt(L, mtname);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int arith_add (lua_State *L) {
|
|
|
|
|
return arith(L, LUA_OPADD, "__add");
|
|
|
|
|
}
|
|
|
|
@ -299,30 +324,6 @@ static int arith_unm (lua_State *L) {
|
|
|
|
|
return arith(L, LUA_OPUNM, "__unm");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int bitwise_band (lua_State *L) {
|
|
|
|
|
return bitwise(L, LUA_OPBAND, "__band");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int bitwise_bor (lua_State *L) {
|
|
|
|
|
return bitwise(L, LUA_OPBOR, "__bor");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int bitwise_bxor (lua_State *L) {
|
|
|
|
|
return bitwise(L, LUA_OPBXOR, "__bxor");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int bitwise_shl (lua_State *L) {
|
|
|
|
|
return bitwise(L, LUA_OPSHL, "__shl");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int bitwise_shr (lua_State *L) {
|
|
|
|
|
return bitwise(L, LUA_OPSHR, "__shr");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int bitwise_bnot (lua_State *L) {
|
|
|
|
|
return bitwise(L, LUA_OPBNOT, "__bnot");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static const luaL_Reg stringmetamethods[] = {
|
|
|
|
|
{"__add", arith_add},
|
|
|
|
@ -333,16 +334,11 @@ static const luaL_Reg stringmetamethods[] = {
|
|
|
|
|
{"__div", arith_div},
|
|
|
|
|
{"__idiv", arith_idiv},
|
|
|
|
|
{"__unm", arith_unm},
|
|
|
|
|
{"__band", bitwise_band},
|
|
|
|
|
{"__bor", bitwise_bor},
|
|
|
|
|
{"__bxor", bitwise_bxor},
|
|
|
|
|
{"__shl", bitwise_shl},
|
|
|
|
|
{"__shr", bitwise_shr},
|
|
|
|
|
{"__bnot", bitwise_bnot},
|
|
|
|
|
{"__index", NULL}, /* placeholder */
|
|
|
|
|
{NULL, NULL}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#endif /* } */
|
|
|
|
|
|
|
|
|
|
/* }====================================================== */
|
|
|
|
|
|
|
|
|
@ -693,25 +689,46 @@ static const char *lmemfind (const char *s1, size_t l1,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void push_onecapture (MatchState *ms, int i, const char *s,
|
|
|
|
|
const char *e) {
|
|
|
|
|
/*
|
|
|
|
|
** get information about the i-th capture. If there are no captures
|
|
|
|
|
** and 'i==0', return information about the whole match, which
|
|
|
|
|
** is the range 's'..'e'. If the capture is a string, return
|
|
|
|
|
** its length and put its address in '*cap'. If it is an integer
|
|
|
|
|
** (a position), push it on the stack and return CAP_POSITION.
|
|
|
|
|
*/
|
|
|
|
|
static size_t get_onecapture (MatchState *ms, int i, const char *s,
|
|
|
|
|
const char *e, const char **cap) {
|
|
|
|
|
if (i >= ms->level) {
|
|
|
|
|
if (i == 0) /* ms->level == 0, too */
|
|
|
|
|
lua_pushlstring(ms->L, s, e - s); /* add whole match */
|
|
|
|
|
else
|
|
|
|
|
if (i != 0)
|
|
|
|
|
luaL_error(ms->L, "invalid capture index %%%d", i + 1);
|
|
|
|
|
*cap = s;
|
|
|
|
|
return e - s;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
ptrdiff_t l = ms->capture[i].len;
|
|
|
|
|
if (l == CAP_UNFINISHED) luaL_error(ms->L, "unfinished capture");
|
|
|
|
|
if (l == CAP_POSITION)
|
|
|
|
|
ptrdiff_t capl = ms->capture[i].len;
|
|
|
|
|
*cap = ms->capture[i].init;
|
|
|
|
|
if (capl == CAP_UNFINISHED)
|
|
|
|
|
luaL_error(ms->L, "unfinished capture");
|
|
|
|
|
else if (capl == CAP_POSITION)
|
|
|
|
|
lua_pushinteger(ms->L, (ms->capture[i].init - ms->src_init) + 1);
|
|
|
|
|
else
|
|
|
|
|
lua_pushlstring(ms->L, ms->capture[i].init, l);
|
|
|
|
|
return capl;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
** Push the i-th capture on the stack.
|
|
|
|
|
*/
|
|
|
|
|
static void push_onecapture (MatchState *ms, int i, const char *s,
|
|
|
|
|
const char *e) {
|
|
|
|
|
const char *cap;
|
|
|
|
|
ptrdiff_t l = get_onecapture(ms, i, s, e, &cap);
|
|
|
|
|
if (l != CAP_POSITION)
|
|
|
|
|
lua_pushlstring(ms->L, cap, l);
|
|
|
|
|
/* else position was already pushed */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int push_captures (MatchState *ms, const char *s, const char *e) {
|
|
|
|
|
int i;
|
|
|
|
|
int nlevels = (ms->level == 0 && s) ? 1 : ms->level;
|
|
|
|
@ -754,16 +771,15 @@ static int str_find_aux (lua_State *L, int find) {
|
|
|
|
|
size_t ls, lp;
|
|
|
|
|
const char *s = luaL_checklstring(L, 1, &ls);
|
|
|
|
|
const char *p = luaL_checklstring(L, 2, &lp);
|
|
|
|
|
lua_Integer init = posrelat(luaL_optinteger(L, 3, 1), ls);
|
|
|
|
|
if (init < 1) init = 1;
|
|
|
|
|
else if (init > (lua_Integer)ls + 1) { /* start after string's end? */
|
|
|
|
|
lua_pushnil(L); /* cannot find anything */
|
|
|
|
|
size_t init = posrelatI(luaL_optinteger(L, 3, 1), ls) - 1;
|
|
|
|
|
if (init > ls) { /* start after string's end? */
|
|
|
|
|
luaL_pushfail(L); /* cannot find anything */
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
/* explicit request or no special characters? */
|
|
|
|
|
if (find && (lua_toboolean(L, 4) || nospecials(p, lp))) {
|
|
|
|
|
/* do a plain search */
|
|
|
|
|
const char *s2 = lmemfind(s + init - 1, ls - (size_t)init + 1, p, lp);
|
|
|
|
|
const char *s2 = lmemfind(s + init, ls - init, p, lp);
|
|
|
|
|
if (s2) {
|
|
|
|
|
lua_pushinteger(L, (s2 - s) + 1);
|
|
|
|
|
lua_pushinteger(L, (s2 - s) + lp);
|
|
|
|
@ -772,7 +788,7 @@ static int str_find_aux (lua_State *L, int find) {
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
MatchState ms;
|
|
|
|
|
const char *s1 = s + init - 1;
|
|
|
|
|
const char *s1 = s + init;
|
|
|
|
|
int anchor = (*p == '^');
|
|
|
|
|
if (anchor) {
|
|
|
|
|
p++; lp--; /* skip anchor character */
|
|
|
|
@ -792,7 +808,7 @@ static int str_find_aux (lua_State *L, int find) {
|
|
|
|
|
}
|
|
|
|
|
} while (s1++ < ms.src_end && !anchor);
|
|
|
|
|
}
|
|
|
|
|
lua_pushnil(L); /* not found */
|
|
|
|
|
luaL_pushfail(L); /* not found */
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -836,11 +852,14 @@ static int gmatch (lua_State *L) {
|
|
|
|
|
size_t ls, lp;
|
|
|
|
|
const char *s = luaL_checklstring(L, 1, &ls);
|
|
|
|
|
const char *p = luaL_checklstring(L, 2, &lp);
|
|
|
|
|
size_t init = posrelatI(luaL_optinteger(L, 3, 1), ls) - 1;
|
|
|
|
|
GMatchState *gm;
|
|
|
|
|
lua_settop(L, 2); /* keep them on closure to avoid being collected */
|
|
|
|
|
lua_settop(L, 2); /* keep strings on closure to avoid being collected */
|
|
|
|
|
gm = (GMatchState *)lua_newuserdata(L, sizeof(GMatchState));
|
|
|
|
|
if (init > ls) /* start after string's end? */
|
|
|
|
|
init = ls + 1; /* avoid overflows in 's + init' */
|
|
|
|
|
prepstate(&gm->ms, L, s, ls, p, lp);
|
|
|
|
|
gm->src = s; gm->p = p; gm->lastmatch = NULL;
|
|
|
|
|
gm->src = s + init; gm->p = p; gm->lastmatch = NULL;
|
|
|
|
|
lua_pushcclosure(L, gmatch_aux, 3);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
@ -848,60 +867,72 @@ static int gmatch (lua_State *L) {
|
|
|
|
|
|
|
|
|
|
static void add_s (MatchState *ms, luaL_Buffer *b, const char *s,
|
|
|
|
|
const char *e) {
|
|
|
|
|
size_t l, i;
|
|
|
|
|
size_t l;
|
|
|
|
|
lua_State *L = ms->L;
|
|
|
|
|
const char *news = lua_tolstring(L, 3, &l);
|
|
|
|
|
for (i = 0; i < l; i++) {
|
|
|
|
|
if (news[i] != L_ESC)
|
|
|
|
|
luaL_addchar(b, news[i]);
|
|
|
|
|
else {
|
|
|
|
|
i++; /* skip ESC */
|
|
|
|
|
if (!isdigit(uchar(news[i]))) {
|
|
|
|
|
if (news[i] != L_ESC)
|
|
|
|
|
luaL_error(L, "invalid use of '%c' in replacement string", L_ESC);
|
|
|
|
|
luaL_addchar(b, news[i]);
|
|
|
|
|
}
|
|
|
|
|
else if (news[i] == '0')
|
|
|
|
|
luaL_addlstring(b, s, e - s);
|
|
|
|
|
else {
|
|
|
|
|
push_onecapture(ms, news[i] - '1', s, e);
|
|
|
|
|
luaL_tolstring(L, -1, NULL); /* if number, convert it to string */
|
|
|
|
|
lua_remove(L, -2); /* remove original value */
|
|
|
|
|
luaL_addvalue(b); /* add capture to accumulated result */
|
|
|
|
|
}
|
|
|
|
|
const char *p;
|
|
|
|
|
while ((p = (char *)memchr(news, L_ESC, l)) != NULL) {
|
|
|
|
|
luaL_addlstring(b, news, p - news);
|
|
|
|
|
p++; /* skip ESC */
|
|
|
|
|
if (*p == L_ESC) /* '%%' */
|
|
|
|
|
luaL_addchar(b, *p);
|
|
|
|
|
else if (*p == '0') /* '%0' */
|
|
|
|
|
luaL_addlstring(b, s, e - s);
|
|
|
|
|
else if (isdigit(uchar(*p))) { /* '%n' */
|
|
|
|
|
const char *cap;
|
|
|
|
|
ptrdiff_t resl = get_onecapture(ms, *p - '1', s, e, &cap);
|
|
|
|
|
if (resl == CAP_POSITION)
|
|
|
|
|
luaL_addvalue(b); /* add position to accumulated result */
|
|
|
|
|
else
|
|
|
|
|
luaL_addlstring(b, cap, resl);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
luaL_error(L, "invalid use of '%c' in replacement string", L_ESC);
|
|
|
|
|
l -= p + 1 - news;
|
|
|
|
|
news = p + 1;
|
|
|
|
|
}
|
|
|
|
|
luaL_addlstring(b, news, l);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void add_value (MatchState *ms, luaL_Buffer *b, const char *s,
|
|
|
|
|
const char *e, int tr) {
|
|
|
|
|
/*
|
|
|
|
|
** Add the replacement value to the string buffer 'b'.
|
|
|
|
|
** Return true if the original string was changed. (Function calls and
|
|
|
|
|
** table indexing resulting in nil or false do not change the subject.)
|
|
|
|
|
*/
|
|
|
|
|
static int add_value (MatchState *ms, luaL_Buffer *b, const char *s,
|
|
|
|
|
const char *e, int tr) {
|
|
|
|
|
lua_State *L = ms->L;
|
|
|
|
|
switch (tr) {
|
|
|
|
|
case LUA_TFUNCTION: {
|
|
|
|
|
case LUA_TFUNCTION: { /* call the function */
|
|
|
|
|
int n;
|
|
|
|
|
lua_pushvalue(L, 3);
|
|
|
|
|
n = push_captures(ms, s, e);
|
|
|
|
|
lua_call(L, n, 1);
|
|
|
|
|
lua_pushvalue(L, 3); /* push the function */
|
|
|
|
|
n = push_captures(ms, s, e); /* all captures as arguments */
|
|
|
|
|
lua_call(L, n, 1); /* call it */
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case LUA_TTABLE: {
|
|
|
|
|
push_onecapture(ms, 0, s, e);
|
|
|
|
|
case LUA_TTABLE: { /* index the table */
|
|
|
|
|
push_onecapture(ms, 0, s, e); /* first capture is the index */
|
|
|
|
|
lua_gettable(L, 3);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default: { /* LUA_TNUMBER or LUA_TSTRING */
|
|
|
|
|
add_s(ms, b, s, e);
|
|
|
|
|
return;
|
|
|
|
|
add_s(ms, b, s, e); /* add value to the buffer */
|
|
|
|
|
return 1; /* something changed */
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!lua_toboolean(L, -1)) { /* nil or false? */
|
|
|
|
|
lua_pop(L, 1);
|
|
|
|
|
lua_pushlstring(L, s, e - s); /* keep original text */
|
|
|
|
|
lua_pop(L, 1); /* remove value */
|
|
|
|
|
luaL_addlstring(b, s, e - s); /* keep original text */
|
|
|
|
|
return 0; /* no changes */
|
|
|
|
|
}
|
|
|
|
|
else if (!lua_isstring(L, -1))
|
|
|
|
|
luaL_error(L, "invalid replacement value (a %s)", luaL_typename(L, -1));
|
|
|
|
|
luaL_addvalue(b); /* add result to accumulator */
|
|
|
|
|
return luaL_error(L, "invalid replacement value (a %s)",
|
|
|
|
|
luaL_typename(L, -1));
|
|
|
|
|
else {
|
|
|
|
|
luaL_addvalue(b); /* add result to accumulator */
|
|
|
|
|
return 1; /* something changed */
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -914,11 +945,12 @@ static int str_gsub (lua_State *L) {
|
|
|
|
|
lua_Integer max_s = luaL_optinteger(L, 4, srcl + 1); /* max replacements */
|
|
|
|
|
int anchor = (*p == '^');
|
|
|
|
|
lua_Integer n = 0; /* replacement count */
|
|
|
|
|
int changed = 0; /* change flag */
|
|
|
|
|
MatchState ms;
|
|
|
|
|
luaL_Buffer b;
|
|
|
|
|
luaL_argcheck(L, tr == LUA_TNUMBER || tr == LUA_TSTRING ||
|
|
|
|
|
luaL_argexpected(L, tr == LUA_TNUMBER || tr == LUA_TSTRING ||
|
|
|
|
|
tr == LUA_TFUNCTION || tr == LUA_TTABLE, 3,
|
|
|
|
|
"string/function/table expected");
|
|
|
|
|
"string/function/table");
|
|
|
|
|
luaL_buffinit(L, &b);
|
|
|
|
|
if (anchor) {
|
|
|
|
|
p++; lp--; /* skip anchor character */
|
|
|
|
@ -929,7 +961,7 @@ static int str_gsub (lua_State *L) {
|
|
|
|
|
reprepstate(&ms); /* (re)prepare state for new match */
|
|
|
|
|
if ((e = match(&ms, src, p)) != NULL && e != lastmatch) { /* match? */
|
|
|
|
|
n++;
|
|
|
|
|
add_value(&ms, &b, src, e, tr); /* add replacement to buffer */
|
|
|
|
|
changed = add_value(&ms, &b, src, e, tr) | changed;
|
|
|
|
|
src = lastmatch = e;
|
|
|
|
|
}
|
|
|
|
|
else if (src < ms.src_end) /* otherwise, skip one character */
|
|
|
|
@ -937,8 +969,12 @@ static int str_gsub (lua_State *L) {
|
|
|
|
|
else break; /* end of subject */
|
|
|
|
|
if (anchor) break;
|
|
|
|
|
}
|
|
|
|
|
luaL_addlstring(&b, src, ms.src_end-src);
|
|
|
|
|
luaL_pushresult(&b);
|
|
|
|
|
if (!changed) /* no changes? */
|
|
|
|
|
lua_pushvalue(L, 1); /* return original string */
|
|
|
|
|
else { /* something changed */
|
|
|
|
|
luaL_addlstring(&b, src, ms.src_end-src);
|
|
|
|
|
luaL_pushresult(&b); /* create and return new string */
|
|
|
|
|
}
|
|
|
|
|
lua_pushinteger(L, n); /* number of substitutions */
|
|
|
|
|
return 2;
|
|
|
|
|
}
|
|
|
|
@ -959,8 +995,6 @@ static int str_gsub (lua_State *L) {
|
|
|
|
|
** Hexadecimal floating-point formatter
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <math.h>
|
|
|
|
|
|
|
|
|
|
#define SIZELENMOD (sizeof(LUA_NUMBER_FRMLEN)/sizeof(char))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -1053,7 +1087,10 @@ static int lua_number2strx (lua_State *L, char *buff, int sz,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* valid flags in a format specification */
|
|
|
|
|
#define FLAGS "-+ #0"
|
|
|
|
|
#if !defined(L_FMTFLAGS)
|
|
|
|
|
#define L_FMTFLAGS "-+ #0"
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
** maximum size of each format specification (such as "%-099.99d")
|
|
|
|
@ -1085,14 +1122,32 @@ static void addquoted (luaL_Buffer *b, const char *s, size_t len) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
** Ensures the 'buff' string uses a dot as the radix character.
|
|
|
|
|
** Serialize a floating-point number in such a way that it can be
|
|
|
|
|
** scanned back by Lua. Use hexadecimal format for "common" numbers
|
|
|
|
|
** (to preserve precision); inf, -inf, and NaN are handled separately.
|
|
|
|
|
** (NaN cannot be expressed as a numeral, so we write '(0/0)' for it.)
|
|
|
|
|
*/
|
|
|
|
|
static void checkdp (char *buff, int nb) {
|
|
|
|
|
if (memchr(buff, '.', nb) == NULL) { /* no dot? */
|
|
|
|
|
char point = lua_getlocaledecpoint(); /* try locale point */
|
|
|
|
|
char *ppoint = (char *)memchr(buff, point, nb);
|
|
|
|
|
if (ppoint) *ppoint = '.'; /* change it to a dot */
|
|
|
|
|
static int quotefloat (lua_State *L, char *buff, lua_Number n) {
|
|
|
|
|
const char *s; /* for the fixed representations */
|
|
|
|
|
if (n == (lua_Number)HUGE_VAL) /* inf? */
|
|
|
|
|
s = "1e9999";
|
|
|
|
|
else if (n == -(lua_Number)HUGE_VAL) /* -inf? */
|
|
|
|
|
s = "-1e9999";
|
|
|
|
|
else if (n != n) /* NaN? */
|
|
|
|
|
s = "(0/0)";
|
|
|
|
|
else { /* format number as hexadecimal */
|
|
|
|
|
int nb = lua_number2strx(L, buff, MAX_ITEM,
|
|
|
|
|
"%" LUA_NUMBER_FRMLEN "a", n);
|
|
|
|
|
/* ensures that 'buff' string uses a dot as the radix character */
|
|
|
|
|
if (memchr(buff, '.', nb) == NULL) { /* no dot? */
|
|
|
|
|
char point = lua_getlocaledecpoint(); /* try locale point */
|
|
|
|
|
char *ppoint = (char *)memchr(buff, point, nb);
|
|
|
|
|
if (ppoint) *ppoint = '.'; /* change it to a dot */
|
|
|
|
|
}
|
|
|
|
|
return nb;
|
|
|
|
|
}
|
|
|
|
|
/* for the fixed representations */
|
|
|
|
|
return l_sprintf(buff, MAX_ITEM, "%s", s);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -1107,15 +1162,12 @@ static void addliteral (lua_State *L, luaL_Buffer *b, int arg) {
|
|
|
|
|
case LUA_TNUMBER: {
|
|
|
|
|
char *buff = luaL_prepbuffsize(b, MAX_ITEM);
|
|
|
|
|
int nb;
|
|
|
|
|
if (!lua_isinteger(L, arg)) { /* float? */
|
|
|
|
|
lua_Number n = lua_tonumber(L, arg); /* write as hexa ('%a') */
|
|
|
|
|
nb = lua_number2strx(L, buff, MAX_ITEM, "%" LUA_NUMBER_FRMLEN "a", n);
|
|
|
|
|
checkdp(buff, nb); /* ensure it uses a dot */
|
|
|
|
|
}
|
|
|
|
|
if (!lua_isinteger(L, arg)) /* float? */
|
|
|
|
|
nb = quotefloat(L, buff, lua_tonumber(L, arg));
|
|
|
|
|
else { /* integers */
|
|
|
|
|
lua_Integer n = lua_tointeger(L, arg);
|
|
|
|
|
const char *format = (n == LUA_MININTEGER) /* corner case? */
|
|
|
|
|
? "0x%" LUA_INTEGER_FRMLEN "x" /* use hexa */
|
|
|
|
|
? "0x%" LUA_INTEGER_FRMLEN "x" /* use hex */
|
|
|
|
|
: LUA_INTEGER_FMT; /* else use default format */
|
|
|
|
|
nb = l_sprintf(buff, MAX_ITEM, format, (LUAI_UACINT)n);
|
|
|
|
|
}
|
|
|
|
@ -1136,8 +1188,8 @@ 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(FLAGS, *p) != NULL) p++; /* skip flags */
|
|
|
|
|
if ((size_t)(p - strfrmt) >= sizeof(FLAGS)/sizeof(char))
|
|
|
|
|
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) */
|
|
|
|
@ -1592,17 +1644,12 @@ static int str_packsize (lua_State *L) {
|
|
|
|
|
while (*fmt != '\0') {
|
|
|
|
|
int size, ntoalign;
|
|
|
|
|
KOption opt = getdetails(&h, totalsize, &fmt, &size, &ntoalign);
|
|
|
|
|
luaL_argcheck(L, opt != Kstring && opt != Kzstr, 1,
|
|
|
|
|
"variable-length format");
|
|
|
|
|
size += ntoalign; /* total space used by option */
|
|
|
|
|
luaL_argcheck(L, totalsize <= MAXSIZE - size, 1,
|
|
|
|
|
"format result too large");
|
|
|
|
|
totalsize += size;
|
|
|
|
|
switch (opt) {
|
|
|
|
|
case Kstring: /* strings with length count */
|
|
|
|
|
case Kzstr: /* zero-terminated string */
|
|
|
|
|
luaL_argerror(L, 1, "variable-length format");
|
|
|
|
|
/* call never return, but to avoid warnings: *//* FALLTHROUGH */
|
|
|
|
|
default: break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
lua_pushinteger(L, (lua_Integer)totalsize);
|
|
|
|
|
return 1;
|
|
|
|
@ -1648,15 +1695,15 @@ static int str_unpack (lua_State *L) {
|
|
|
|
|
const char *fmt = luaL_checkstring(L, 1);
|
|
|
|
|
size_t ld;
|
|
|
|
|
const char *data = luaL_checklstring(L, 2, &ld);
|
|
|
|
|
size_t pos = (size_t)posrelat(luaL_optinteger(L, 3, 1), ld) - 1;
|
|
|
|
|
size_t pos = posrelatI(luaL_optinteger(L, 3, 1), ld) - 1;
|
|
|
|
|
int n = 0; /* number of results */
|
|
|
|
|
luaL_argcheck(L, pos <= ld, 3, "initial position out of string");
|
|
|
|
|
initheader(L, &h);
|
|
|
|
|
while (*fmt != '\0') {
|
|
|
|
|
int size, ntoalign;
|
|
|
|
|
KOption opt = getdetails(&h, pos, &fmt, &size, &ntoalign);
|
|
|
|
|
if ((size_t)ntoalign + size > ~pos || pos + ntoalign + size > ld)
|
|
|
|
|
luaL_argerror(L, 2, "data string too short");
|
|
|
|
|
luaL_argcheck(L, (size_t)ntoalign + size <= ld - pos, 2,
|
|
|
|
|
"data string too short");
|
|
|
|
|
pos += ntoalign; /* skip alignment */
|
|
|
|
|
/* stack space for item + next position */
|
|
|
|
|
luaL_checkstack(L, 2, "too many results");
|
|
|
|
@ -1685,13 +1732,15 @@ static int str_unpack (lua_State *L) {
|
|
|
|
|
}
|
|
|
|
|
case Kstring: {
|
|
|
|
|
size_t len = (size_t)unpackint(L, data + pos, h.islittle, size, 0);
|
|
|
|
|
luaL_argcheck(L, pos + len + size <= ld, 2, "data string too short");
|
|
|
|
|
luaL_argcheck(L, len <= ld - pos - size, 2, "data string too short");
|
|
|
|
|
lua_pushlstring(L, data + pos + size, len);
|
|
|
|
|
pos += len; /* skip string */
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case Kzstr: {
|
|
|
|
|
size_t len = (int)strlen(data + pos);
|
|
|
|
|
luaL_argcheck(L, pos + len < ld, 2,
|
|
|
|
|
"unfinished string for format 'z'");
|
|
|
|
|
lua_pushlstring(L, data + pos, len);
|
|
|
|
|
pos += len + 1; /* skip string plus final '\0' */
|
|
|
|
|
break;
|
|
|
|
|