mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 03:27:39 +00:00
957c61cbbf
This change upgrades to GCC 12.3 and GNU binutils 2.42. The GNU linker appears to have changed things so that only a single de-duplicated str table is present in the binary, and it gets placed wherever the linker wants, regardless of what the linker script says. To cope with that we need to stop using .ident to embed licenses. As such, this change does significant work to revamp how third party licenses are defined in the codebase, using `.section .notice,"aR",@progbits`. This new GCC 12.3 toolchain has support for GNU indirect functions. It lets us support __target_clones__ for the first time. This is used for optimizing the performance of libc string functions such as strlen and friends so far on x86, by ensuring AVX systems favor a second codepath that uses VEX encoding. It shaves some latency off certain operations. It's a useful feature to have for scientific computing for the reasons explained by the test/libcxx/openmp_test.cc example which compiles for fifteen different microarchitectures. Thanks to the upgrades, it's now also possible to use newer instruction sets, such as AVX512FP16, VNNI. Cosmo now uses the %gs register on x86 by default for TLS. Doing it is helpful for any program that links `cosmo_dlopen()`. Such programs had to recompile their binaries at startup to change the TLS instructions. That's not great, since it means every page in the executable needs to be faulted. The work of rewriting TLS-related x86 opcodes, is moved to fixupobj.com instead. This is great news for MacOS x86 users, since we previously needed to morph the binary every time for that platform but now that's no longer necessary. The only platforms where we need fixup of TLS x86 opcodes at runtime are now Windows, OpenBSD, and NetBSD. On Windows we morph TLS to point deeper into the TIB, based on a TlsAlloc assignment, and on OpenBSD/NetBSD we morph %gs back into %fs since the kernels do not allow us to specify a value for the %gs register. OpenBSD users are now required to use APE Loader to run Cosmo binaries and assimilation is no longer possible. OpenBSD kernel needs to change to allow programs to specify a value for the %gs register, or it needs to stop marking executable pages loaded by the kernel as mimmutable(). This release fixes __constructor__, .ctor, .init_array, and lastly the .preinit_array so they behave the exact same way as glibc. We no longer use hex constants to define math.h symbols like M_PI.
602 lines
21 KiB
C
602 lines
21 KiB
C
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
|
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
|
╚──────────────────────────────────────────────────────────────────────────────╝
|
|
│ │
|
|
│ Lua │
|
|
│ Copyright © 2004-2021 Lua.org, PUC-Rio. │
|
|
│ │
|
|
│ Permission is hereby granted, free of charge, to any person obtaining │
|
|
│ a copy of this software and associated documentation files (the │
|
|
│ "Software"), to deal in the Software without restriction, including │
|
|
│ without limitation the rights to use, copy, modify, merge, publish, │
|
|
│ distribute, sublicense, and/or sell copies of the Software, and to │
|
|
│ permit persons to whom the Software is furnished to do so, subject to │
|
|
│ the following conditions: │
|
|
│ │
|
|
│ The above copyright notice and this permission notice shall be │
|
|
│ included in all copies or substantial portions of the Software. │
|
|
│ │
|
|
│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │
|
|
│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │
|
|
│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │
|
|
│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │
|
|
│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │
|
|
│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │
|
|
│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │
|
|
│ │
|
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
|
#define lobject_c
|
|
#define LUA_CORE
|
|
#include "libc/str/str.h"
|
|
#include "third_party/lua/lctype.h"
|
|
#include "third_party/lua/ldebug.h"
|
|
#include "third_party/lua/ldo.h"
|
|
#include "third_party/lua/lmem.h"
|
|
#include "third_party/lua/lobject.h"
|
|
#include "third_party/lua/lprefix.h"
|
|
#include "third_party/lua/lstate.h"
|
|
#include "third_party/lua/lstring.h"
|
|
#include "third_party/lua/lua.h"
|
|
#include "third_party/lua/lvm.h"
|
|
__static_yoink("lua_notice");
|
|
|
|
|
|
static lua_Integer intarith (lua_State *L, int op, lua_Integer v1,
|
|
lua_Integer v2) {
|
|
switch (op) {
|
|
case LUA_OPADD: return intop(+, v1, v2);
|
|
case LUA_OPSUB:return intop(-, v1, v2);
|
|
case LUA_OPMUL:return intop(*, v1, v2);
|
|
case LUA_OPMOD: return luaV_mod(L, v1, v2);
|
|
case LUA_OPIDIV: return luaV_idiv(L, v1, v2);
|
|
case LUA_OPBAND: return intop(&, v1, v2);
|
|
case LUA_OPBOR: return intop(|, v1, v2);
|
|
case LUA_OPBXOR: return intop(^, v1, v2);
|
|
case LUA_OPSHL: return luaV_shiftl(v1, v2);
|
|
case LUA_OPSHR: return luaV_shiftl(v1, -v2);
|
|
case LUA_OPUNM: return intop(-, 0, v1);
|
|
case LUA_OPBNOT: return intop(^, ~l_castS2U(0), v1);
|
|
default: lua_assert(0); return 0;
|
|
}
|
|
}
|
|
|
|
|
|
static lua_Number numarith (lua_State *L, int op, lua_Number v1,
|
|
lua_Number v2) {
|
|
switch (op) {
|
|
case LUA_OPADD: return luai_numadd(L, v1, v2);
|
|
case LUA_OPSUB: return luai_numsub(L, v1, v2);
|
|
case LUA_OPMUL: return luai_nummul(L, v1, v2);
|
|
case LUA_OPDIV: return luai_numdiv(L, v1, v2);
|
|
case LUA_OPPOW: return luai_numpow(L, v1, v2);
|
|
case LUA_OPIDIV: return luai_numidiv(L, v1, v2);
|
|
case LUA_OPUNM: return luai_numunm(L, v1);
|
|
case LUA_OPMOD: return luaV_modf(L, v1, v2);
|
|
default: lua_assert(0); return 0;
|
|
}
|
|
}
|
|
|
|
|
|
int luaO_rawarith (lua_State *L, int op, const TValue *p1, const TValue *p2,
|
|
TValue *res) {
|
|
switch (op) {
|
|
case LUA_OPBAND: case LUA_OPBOR: case LUA_OPBXOR:
|
|
case LUA_OPSHL: case LUA_OPSHR:
|
|
case LUA_OPBNOT: { /* operate only on integers */
|
|
lua_Integer i1; lua_Integer i2;
|
|
if (tointegerns(p1, &i1) && tointegerns(p2, &i2)) {
|
|
setivalue(res, intarith(L, op, i1, i2));
|
|
return 1;
|
|
}
|
|
else return 0; /* fail */
|
|
}
|
|
case LUA_OPDIV: case LUA_OPPOW: { /* operate only on floats */
|
|
lua_Number n1; lua_Number n2;
|
|
if (tonumberns(p1, n1) && tonumberns(p2, n2)) {
|
|
setfltvalue(res, numarith(L, op, n1, n2));
|
|
return 1;
|
|
}
|
|
else return 0; /* fail */
|
|
}
|
|
default: { /* other operations */
|
|
lua_Number n1; lua_Number n2;
|
|
if (ttisinteger(p1) && ttisinteger(p2)) {
|
|
setivalue(res, intarith(L, op, ivalue(p1), ivalue(p2)));
|
|
return 1;
|
|
}
|
|
else if (tonumberns(p1, n1) && tonumberns(p2, n2)) {
|
|
setfltvalue(res, numarith(L, op, n1, n2));
|
|
return 1;
|
|
}
|
|
else return 0; /* fail */
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void luaO_arith (lua_State *L, int op, const TValue *p1, const TValue *p2,
|
|
StkId res) {
|
|
if (!luaO_rawarith(L, op, p1, p2, s2v(res))) {
|
|
/* could not perform raw operation; try metamethod */
|
|
luaT_trybinTM(L, p1, p2, res, cast(TMS, (op - LUA_OPADD) + TM_ADD));
|
|
}
|
|
}
|
|
|
|
|
|
int luaO_hexavalue (int c) {
|
|
if (lisdigit(c)) return c - '0';
|
|
else return (ltolower(c) - 'a') + 10;
|
|
}
|
|
|
|
|
|
static int isneg (const char **s) {
|
|
if (**s == '-') { (*s)++; return 1; }
|
|
else if (**s == '+') (*s)++;
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
** {==================================================================
|
|
** Lua's implementation for 'lua_strx2number'
|
|
** ===================================================================
|
|
*/
|
|
|
|
#if !defined(lua_strx2number)
|
|
|
|
/* maximum number of significant digits to read (to avoid overflows
|
|
even with single floats) */
|
|
#define MAXSIGDIG 30
|
|
|
|
/*
|
|
** convert a hexadecimal numeric string to a number, following
|
|
** C99 specification for 'strtod'
|
|
*/
|
|
static lua_Number lua_strx2number (const char *s, char **endptr) {
|
|
int dot = lua_getlocaledecpoint();
|
|
lua_Number r = 0.0; /* result (accumulator) */
|
|
int sigdig = 0; /* number of significant digits */
|
|
int nosigdig = 0; /* number of non-significant digits */
|
|
int e = 0; /* exponent correction */
|
|
int neg; /* 1 if number is negative */
|
|
int hasdot = 0; /* true after seen a dot */
|
|
*endptr = cast_charp(s); /* nothing is valid yet */
|
|
while (lisspace(cast_uchar(*s))) s++; /* skip initial spaces */
|
|
neg = isneg(&s); /* check sign */
|
|
if (!(*s == '0' && (*(s + 1) == 'x' || *(s + 1) == 'X'))) /* check '0x' */
|
|
return 0.0; /* invalid format (no '0x') */
|
|
for (s += 2; ; s++) { /* skip '0x' and read numeral */
|
|
if (*s == dot) {
|
|
if (hasdot) break; /* second dot? stop loop */
|
|
else hasdot = 1;
|
|
}
|
|
else if (lisxdigit(cast_uchar(*s))) {
|
|
if (sigdig == 0 && *s == '0') /* non-significant digit (zero)? */
|
|
nosigdig++;
|
|
else if (++sigdig <= MAXSIGDIG) /* can read it without overflow? */
|
|
r = (r * cast_num(16.0)) + luaO_hexavalue(*s);
|
|
else e++; /* too many digits; ignore, but still count for exponent */
|
|
if (hasdot) e--; /* decimal digit? correct exponent */
|
|
}
|
|
else break; /* neither a dot nor a digit */
|
|
}
|
|
if (nosigdig + sigdig == 0) /* no digits? */
|
|
return 0.0; /* invalid format */
|
|
*endptr = cast_charp(s); /* valid up to here */
|
|
e *= 4; /* each digit multiplies/divides value by 2^4 */
|
|
if (*s == 'p' || *s == 'P') { /* exponent part? */
|
|
int exp1 = 0; /* exponent value */
|
|
int neg1; /* exponent sign */
|
|
s++; /* skip 'p' */
|
|
neg1 = isneg(&s); /* sign */
|
|
if (!lisdigit(cast_uchar(*s)))
|
|
return 0.0; /* invalid; must have at least one digit */
|
|
while (lisdigit(cast_uchar(*s))) /* read exponent */
|
|
exp1 = exp1 * 10 + *(s++) - '0';
|
|
if (neg1) exp1 = -exp1;
|
|
e += exp1;
|
|
*endptr = cast_charp(s); /* valid up to here */
|
|
}
|
|
if (neg) r = -r;
|
|
return l_mathop(ldexp)(r, e);
|
|
}
|
|
|
|
#endif
|
|
/* }====================================================== */
|
|
|
|
|
|
/* maximum length of a numeral to be converted to a number */
|
|
#if !defined (L_MAXLENNUM)
|
|
#define L_MAXLENNUM 200
|
|
#endif
|
|
|
|
/*
|
|
** Convert string 's' to a Lua number (put in 'result'). Return NULL on
|
|
** fail or the address of the ending '\0' on success. ('mode' == 'x')
|
|
** means a hexadecimal numeral.
|
|
*/
|
|
static const char *l_str2dloc (const char *s, lua_Number *result, int mode) {
|
|
char *endptr;
|
|
*result = (mode == 'x') ? lua_strx2number(s, &endptr) /* try to convert */
|
|
: lua_str2number(s, &endptr);
|
|
if (endptr == s) return NULL; /* nothing recognized? */
|
|
while (lisspace(cast_uchar(*endptr))) endptr++; /* skip trailing spaces */
|
|
return (*endptr == '\0') ? endptr : NULL; /* OK iff no trailing chars */
|
|
}
|
|
|
|
|
|
/*
|
|
** Convert string 's' to a Lua number (put in 'result') handling the
|
|
** current locale.
|
|
** This function accepts both the current locale or a dot as the radix
|
|
** mark. If the conversion fails, it may mean number has a dot but
|
|
** locale accepts something else. In that case, the code copies 's'
|
|
** to a buffer (because 's' is read-only), changes the dot to the
|
|
** current locale radix mark, and tries to convert again.
|
|
** The variable 'mode' checks for special characters in the string:
|
|
** - 'n' means 'inf' or 'nan' (which should be rejected)
|
|
** - 'x' means a hexadecimal numeral
|
|
** - '.' just optimizes the search for the common case (no special chars)
|
|
*/
|
|
static const char *l_str2d (const char *s, lua_Number *result) {
|
|
const char *endptr;
|
|
const char *pmode = strpbrk(s, ".xXnN"); /* look for special chars */
|
|
int mode = pmode ? ltolower(cast_uchar(*pmode)) : 0;
|
|
if (mode == 'n') /* reject 'inf' and 'nan' */
|
|
return NULL;
|
|
endptr = l_str2dloc(s, result, mode); /* try to convert */
|
|
if (endptr == NULL) { /* failed? may be a different locale */
|
|
char buff[L_MAXLENNUM + 1];
|
|
const char *pdot = strchr(s, '.');
|
|
if (pdot == NULL || strlen(s) > L_MAXLENNUM)
|
|
return NULL; /* string too long or no dot; fail */
|
|
strcpy(buff, s); /* copy string to buffer */
|
|
buff[pdot - s] = lua_getlocaledecpoint(); /* correct decimal point */
|
|
endptr = l_str2dloc(buff, result, mode); /* try again */
|
|
if (endptr != NULL)
|
|
endptr = s + (endptr - buff); /* make relative to 's' */
|
|
}
|
|
return endptr;
|
|
}
|
|
|
|
|
|
#define MAXBY10 cast(lua_Unsigned, LUA_MAXINTEGER / 10)
|
|
#define MAXBY8 cast(lua_Unsigned, LUA_MAXINTEGER / 8)
|
|
#define MAXLASTD cast_int(LUA_MAXINTEGER % 10)
|
|
#define MAXLASTD8 cast_int(LUA_MAXINTEGER % 8)
|
|
|
|
static const char *l_str2int (const char *s, lua_Integer *result) {
|
|
lua_Unsigned a = 0;
|
|
int empty = 1;
|
|
int neg;
|
|
while (lisspace(cast_uchar(*s))) s++; /* skip initial spaces */
|
|
neg = isneg(&s);
|
|
if (s[0] == '0' &&
|
|
(s[1] == 'x' || s[1] == 'X')) { /* hex? */
|
|
s += 2; /* skip '0x' */
|
|
for (; lisxdigit(cast_uchar(*s)); s++) {
|
|
a = a * 16 + luaO_hexavalue(*s);
|
|
empty = 0;
|
|
}
|
|
}
|
|
if (s[0] == '0' &&
|
|
(s[1] == 'b' || s[1] == 'B')) { /* [jart] binary */
|
|
s += 2; /* skip '0b' */
|
|
for (; lisbdigit(cast_uchar(*s)); s++) {
|
|
a = a * 2 + (*s - '0');
|
|
empty = 0;
|
|
}
|
|
}
|
|
else if (s[0] == '0') { /* [jart] octal is the best radix */
|
|
for (; lisdigit(cast_uchar(*s)); s++) {
|
|
int d = *s - '0';
|
|
if (a >= MAXBY8 && (a > MAXBY8 || d > MAXLASTD8 + neg)) /* overflow? */
|
|
return NULL; /* do not accept it (as integer) */
|
|
a = a * 8 + d;
|
|
empty = 0;
|
|
}
|
|
}
|
|
else { /* decimal */
|
|
for (; lisdigit(cast_uchar(*s)); s++) {
|
|
int d = *s - '0';
|
|
if (a >= MAXBY10 && (a > MAXBY10 || d > MAXLASTD + neg)) /* overflow? */
|
|
return NULL; /* do not accept it (as integer) */
|
|
a = a * 10 + d;
|
|
empty = 0;
|
|
}
|
|
}
|
|
while (lisspace(cast_uchar(*s))) s++; /* skip trailing spaces */
|
|
if (empty || *s != '\0') return NULL; /* something wrong in the numeral */
|
|
else {
|
|
*result = l_castU2S((neg) ? 0u - a : a);
|
|
return s;
|
|
}
|
|
}
|
|
|
|
|
|
size_t luaO_str2num (const char *s, TValue *o) {
|
|
lua_Integer i; lua_Number n;
|
|
const char *e;
|
|
if ((e = l_str2int(s, &i)) != NULL) { /* try as an integer */
|
|
setivalue(o, i);
|
|
}
|
|
else if ((e = l_str2d(s, &n)) != NULL) { /* else try as a float */
|
|
setfltvalue(o, n);
|
|
}
|
|
else
|
|
return 0; /* conversion failed */
|
|
return (e - s) + 1; /* success; return string size */
|
|
}
|
|
|
|
|
|
int luaO_utf8esc (char *buff, unsigned long x) {
|
|
int n = 1; /* number of bytes put in buffer (backwards) */
|
|
lua_assert(x <= 0x7FFFFFFFu);
|
|
if (x < 0x80) /* ascii? */
|
|
buff[UTF8BUFFSZ - 1] = cast_char(x);
|
|
else { /* need continuation bytes */
|
|
unsigned int mfb = 0x3f; /* maximum that fits in first byte */
|
|
do { /* add continuation bytes */
|
|
buff[UTF8BUFFSZ - (n++)] = cast_char(0x80 | (x & 0x3f));
|
|
x >>= 6; /* remove added bits */
|
|
mfb >>= 1; /* now there is one less bit available in first byte */
|
|
} while (x > mfb); /* still needs continuation byte? */
|
|
buff[UTF8BUFFSZ - n] = cast_char((~mfb << 1) | x); /* add first byte */
|
|
}
|
|
return n;
|
|
}
|
|
|
|
|
|
/*
|
|
** Maximum length of the conversion of a number to a string. Must be
|
|
** enough to accommodate both LUA_INTEGER_FMT and LUA_NUMBER_FMT.
|
|
** (For a long long int, this is 19 digits plus a sign and a final '\0',
|
|
** adding to 21. For a long double, it can go to a sign, 33 digits,
|
|
** the dot, an exponent letter, an exponent sign, 5 exponent digits,
|
|
** and a final '\0', adding to 43.)
|
|
*/
|
|
#define MAXNUMBER2STR 44
|
|
|
|
|
|
/*
|
|
** Convert a number object to a string, adding it to a buffer
|
|
*/
|
|
static int tostringbuff (TValue *obj, char *buff) {
|
|
int len;
|
|
lua_assert(ttisnumber(obj));
|
|
if (ttisinteger(obj))
|
|
len = lua_integer2str(buff, MAXNUMBER2STR, ivalue(obj));
|
|
else {
|
|
len = lua_number2str(buff, MAXNUMBER2STR, fltvalue(obj));
|
|
if (buff[strspn(buff, "-0123456789")] == '\0') { /* looks like an int? */
|
|
buff[len++] = lua_getlocaledecpoint();
|
|
buff[len++] = '0'; /* adds '.0' to result */
|
|
}
|
|
}
|
|
return len;
|
|
}
|
|
|
|
|
|
/*
|
|
** Convert a number object to a Lua string, replacing the value at 'obj'
|
|
*/
|
|
void luaO_tostring (lua_State *L, TValue *obj) {
|
|
char buff[MAXNUMBER2STR];
|
|
int len = tostringbuff(obj, buff);
|
|
setsvalue(L, obj, luaS_newlstr(L, buff, len));
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
** {==================================================================
|
|
** 'luaO_pushvfstring'
|
|
** ===================================================================
|
|
*/
|
|
|
|
/* size for buffer space used by 'luaO_pushvfstring' */
|
|
#define BUFVFS 200
|
|
|
|
/* buffer used by 'luaO_pushvfstring' */
|
|
typedef struct BuffFS {
|
|
lua_State *L;
|
|
int pushed; /* number of string pieces already on the stack */
|
|
int blen; /* length of partial string in 'space' */
|
|
char space[BUFVFS]; /* holds last part of the result */
|
|
} BuffFS;
|
|
|
|
|
|
/*
|
|
** Push given string to the stack, as part of the buffer, and
|
|
** join the partial strings in the stack into one.
|
|
*/
|
|
static void pushstr (BuffFS *buff, const char *str, size_t l) {
|
|
lua_State *L = buff->L;
|
|
setsvalue2s(L, L->top, luaS_newlstr(L, str, l));
|
|
L->top++; /* may use one extra slot */
|
|
buff->pushed++;
|
|
luaV_concat(L, buff->pushed); /* join partial results into one */
|
|
buff->pushed = 1;
|
|
}
|
|
|
|
|
|
/*
|
|
** empty the buffer space into the stack
|
|
*/
|
|
static void clearbuff (BuffFS *buff) {
|
|
pushstr(buff, buff->space, buff->blen); /* push buffer contents */
|
|
buff->blen = 0; /* space now is empty */
|
|
}
|
|
|
|
|
|
/*
|
|
** Get a space of size 'sz' in the buffer. If buffer has not enough
|
|
** space, empty it. 'sz' must fit in an empty buffer.
|
|
*/
|
|
static char *getbuff (BuffFS *buff, int sz) {
|
|
lua_assert(buff->blen <= BUFVFS); lua_assert(sz <= BUFVFS);
|
|
if (sz > BUFVFS - buff->blen) /* not enough space? */
|
|
clearbuff(buff);
|
|
return buff->space + buff->blen;
|
|
}
|
|
|
|
|
|
#define addsize(b,sz) ((b)->blen += (sz))
|
|
|
|
|
|
/*
|
|
** Add 'str' to the buffer. If string is larger than the buffer space,
|
|
** push the string directly to the stack.
|
|
*/
|
|
static void addstr2buff (BuffFS *buff, const char *str, size_t slen) {
|
|
if (slen <= BUFVFS) { /* does string fit into buffer? */
|
|
char *bf = getbuff(buff, cast_int(slen));
|
|
memcpy(bf, str, slen); /* add string to buffer */
|
|
addsize(buff, cast_int(slen));
|
|
}
|
|
else { /* string larger than buffer */
|
|
clearbuff(buff); /* string comes after buffer's content */
|
|
pushstr(buff, str, slen); /* push string */
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
** Add a number to the buffer.
|
|
*/
|
|
static void addnum2buff (BuffFS *buff, TValue *num) {
|
|
char *numbuff = getbuff(buff, MAXNUMBER2STR);
|
|
int len = tostringbuff(num, numbuff); /* format number into 'numbuff' */
|
|
addsize(buff, len);
|
|
}
|
|
|
|
|
|
/*
|
|
** this function handles only '%d', '%c', '%f', '%p', '%s', and '%%'
|
|
conventional formats, plus Lua-specific '%I' and '%U'
|
|
*/
|
|
const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) {
|
|
BuffFS buff; /* holds last part of the result */
|
|
const char *e; /* points to next '%' */
|
|
buff.pushed = buff.blen = 0;
|
|
buff.L = L;
|
|
while ((e = strchr(fmt, '%')) != NULL) {
|
|
addstr2buff(&buff, fmt, e - fmt); /* add 'fmt' up to '%' */
|
|
switch (*(e + 1)) { /* conversion specifier */
|
|
case 's': { /* zero-terminated string */
|
|
const char *s = va_arg(argp, char *);
|
|
if (s == NULL) s = "(null)";
|
|
addstr2buff(&buff, s, strlen(s));
|
|
break;
|
|
}
|
|
case 'c': { /* an 'int' as a character */
|
|
char c = cast_uchar(va_arg(argp, int));
|
|
addstr2buff(&buff, &c, sizeof(char));
|
|
break;
|
|
}
|
|
case 'd': { /* an 'int' */
|
|
TValue num;
|
|
setivalue(&num, va_arg(argp, int));
|
|
addnum2buff(&buff, &num);
|
|
break;
|
|
}
|
|
case 'I': { /* a 'lua_Integer' */
|
|
TValue num;
|
|
setivalue(&num, cast(lua_Integer, va_arg(argp, l_uacInt)));
|
|
addnum2buff(&buff, &num);
|
|
break;
|
|
}
|
|
case 'f': { /* a 'lua_Number' */
|
|
TValue num;
|
|
setfltvalue(&num, cast_num(va_arg(argp, l_uacNumber)));
|
|
addnum2buff(&buff, &num);
|
|
break;
|
|
}
|
|
case 'p': { /* a pointer */
|
|
const int sz = 3 * sizeof(void*) + 8; /* enough space for '%p' */
|
|
char *bf = getbuff(&buff, sz);
|
|
void *p = va_arg(argp, void *);
|
|
int len = lua_pointer2str(bf, sz, p);
|
|
addsize(&buff, len);
|
|
break;
|
|
}
|
|
case 'U': { /* a 'long' as a UTF-8 sequence */
|
|
char bf[UTF8BUFFSZ];
|
|
int len = luaO_utf8esc(bf, va_arg(argp, long));
|
|
addstr2buff(&buff, bf + UTF8BUFFSZ - len, len);
|
|
break;
|
|
}
|
|
case '%': {
|
|
addstr2buff(&buff, "%", 1);
|
|
break;
|
|
}
|
|
default: {
|
|
luaG_runerror(L, "invalid option '%%%c' to 'lua_pushfstring'",
|
|
*(e + 1));
|
|
}
|
|
}
|
|
fmt = e + 2; /* skip '%' and the specifier */
|
|
}
|
|
addstr2buff(&buff, fmt, strlen(fmt)); /* rest of 'fmt' */
|
|
clearbuff(&buff); /* empty buffer into the stack */
|
|
lua_assert(buff.pushed == 1);
|
|
return svalue(s2v(L->top - 1));
|
|
}
|
|
|
|
|
|
const char *luaO_pushfstring (lua_State *L, const char *fmt, ...) {
|
|
const char *msg;
|
|
va_list argp;
|
|
va_start(argp, fmt);
|
|
msg = luaO_pushvfstring(L, fmt, argp);
|
|
va_end(argp);
|
|
return msg;
|
|
}
|
|
|
|
/* }================================================================== */
|
|
|
|
|
|
#define RETS "..."
|
|
#define PRE "[string \""
|
|
#define POS "\"]"
|
|
|
|
#define addstr(a,b,l) ( memcpy(a,b,(l) * sizeof(char)), a += (l) )
|
|
|
|
void luaO_chunkid (char *out, const char *source, size_t srclen) {
|
|
size_t bufflen = LUA_IDSIZE; /* free space in buffer */
|
|
if (*source == '=') { /* 'literal' source */
|
|
if (srclen <= bufflen) /* small enough? */
|
|
memcpy(out, source + 1, srclen * sizeof(char));
|
|
else { /* truncate it */
|
|
addstr(out, source + 1, bufflen - 1);
|
|
*out = '\0';
|
|
}
|
|
}
|
|
else if (*source == '@') { /* file name */
|
|
if (srclen <= bufflen) /* small enough? */
|
|
memcpy(out, source + 1, srclen * sizeof(char));
|
|
else { /* add '...' before rest of name */
|
|
addstr(out, RETS, LL(RETS));
|
|
bufflen -= LL(RETS);
|
|
memcpy(out, source + 1 + srclen - bufflen, bufflen * sizeof(char));
|
|
}
|
|
}
|
|
else { /* string; format as [string "source"] */
|
|
const char *nl = strchr(source, '\n'); /* find first new line (if any) */
|
|
addstr(out, PRE, LL(PRE)); /* add prefix */
|
|
bufflen -= LL(PRE RETS POS) + 1; /* save space for prefix+suffix+'\0' */
|
|
if (srclen < bufflen && nl == NULL) { /* small one-line source? */
|
|
addstr(out, source, srclen); /* keep it */
|
|
}
|
|
else {
|
|
if (nl != NULL) srclen = nl - source; /* stop at first newline */
|
|
if (srclen > bufflen) srclen = bufflen;
|
|
addstr(out, source, srclen);
|
|
addstr(out, RETS, LL(RETS));
|
|
}
|
|
memcpy(out, POS, (LL(POS) + 1) * sizeof(char));
|
|
}
|
|
}
|
|
|