diff options
Diffstat (limited to 'src/lib')
-rw-r--r-- | src/lib/Makefile | 28 | ||||
-rw-r--r-- | src/lib/README | 4 | ||||
-rw-r--r-- | src/lib/liolib.c | 448 | ||||
-rw-r--r-- | src/lib/lmathlib.c | 213 | ||||
-rw-r--r-- | src/lib/lstrlib.c | 541 |
5 files changed, 1234 insertions, 0 deletions
diff --git a/src/lib/Makefile b/src/lib/Makefile new file mode 100644 index 00000000..70db660f --- /dev/null +++ b/src/lib/Makefile @@ -0,0 +1,28 @@ +# makefile for lua standard library + +LUA= ../.. + +include $(LUA)/config + +# actually only used in liolib.c +EXTRA_DEFS= $(POPEN) + +OBJS= liolib.o lmathlib.o lstrlib.o +SRCS= liolib.c lmathlib.c lstrlib.c + +T= $(LIB)/liblualib.a + +all: $T + +$T: $(OBJS) + $(AR) $@ $(OBJS) + $(RANLIB) $@ + +clean: + rm -f $(OBJS) $T + +co: + co -q -f -M $(SRCS) + +klean: clean + rm -f $(SRCS) diff --git a/src/lib/README b/src/lib/README new file mode 100644 index 00000000..e8e599c8 --- /dev/null +++ b/src/lib/README @@ -0,0 +1,4 @@ +This is the standard Lua library. +It is implemented entirely on top of the official Lua API as declared in lua.h, +using src/lauxlib.c, which contains several useful functions. +The code can be read as an example of how to export C functions to Lua. diff --git a/src/lib/liolib.c b/src/lib/liolib.c new file mode 100644 index 00000000..15ea6587 --- /dev/null +++ b/src/lib/liolib.c @@ -0,0 +1,448 @@ +/* +** $Id: liolib.c,v 1.21 1998/06/18 17:04:28 roberto Exp $ +** Standard I/O (and system) library +** See Copyright Notice in lua.h +*/ + + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#include "lauxlib.h" +#include "lua.h" +#include "luadebug.h" +#include "lualib.h" + + +#ifndef OLD_ANSI +#include <locale.h> +#else +#define setlocale(a,b) 0 +#define LC_ALL 0 +#define LC_COLLATE 0 +#define LC_CTYPE 0 +#define LC_MONETARY 0 +#define LC_NUMERIC 0 +#define LC_TIME 0 +#define strerror(e) "(no error message provided by operating system)" +#endif + + +#define CLOSEDTAG 2 +#define IOTAG 1 + +#define FIRSTARG 3 /* 1st and 2nd are upvalues */ + +#define FINPUT "_INPUT" +#define FOUTPUT "_OUTPUT" + + +#ifdef POPEN +FILE *popen(); +int pclose(); +#else +#define popen(x,y) NULL /* that is, popen always fails */ +#define pclose(x) (-1) +#endif + + +static int gettag (int i) +{ + return lua_getnumber(lua_getparam(i)); +} + + +static void pushresult (int i) +{ + if (i) + lua_pushuserdata(NULL); + else { + lua_pushnil(); + lua_pushstring(strerror(errno)); + } +} + + +static int ishandler (lua_Object f) +{ + if (lua_isuserdata(f)) { + if (lua_tag(f) == gettag(CLOSEDTAG)) + lua_error("cannot access a closed file"); + return lua_tag(f) == gettag(IOTAG); + } + else return 0; +} + +static FILE *getfile (char *name) +{ + lua_Object f = lua_getglobal(name); + if (!ishandler(f)) + luaL_verror("global variable `%.50s' is not a file handle", name); + return lua_getuserdata(f); +} + + +static FILE *getfileparam (char *name, int *arg) +{ + lua_Object f = lua_getparam(*arg); + if (ishandler(f)) { + (*arg)++; + return lua_getuserdata(f); + } + else + return getfile(name); +} + + +static void closefile (char *name) +{ + FILE *f = getfile(name); + if (f == stdin || f == stdout) return; + if (pclose(f) == -1) + fclose(f); + lua_pushobject(lua_getglobal(name)); + lua_settag(gettag(CLOSEDTAG)); +} + + +static void setfile (FILE *f, char *name, int tag) +{ + lua_pushusertag(f, tag); + lua_setglobal(name); +} + + +static void setreturn (FILE *f, char *name) +{ + int tag = gettag(IOTAG); + setfile(f, name, tag); + lua_pushusertag(f, tag); +} + + +static void io_readfrom (void) +{ + FILE *current; + lua_Object f = lua_getparam(FIRSTARG); + if (f == LUA_NOOBJECT) { + closefile(FINPUT); + current = stdin; + } + else if (lua_tag(f) == gettag(IOTAG)) + current = lua_getuserdata(f); + else { + char *s = luaL_check_string(FIRSTARG); + current = (*s == '|') ? popen(s+1, "r") : fopen(s, "r"); + if (current == NULL) { + pushresult(0); + return; + } + } + setreturn(current, FINPUT); +} + + +static void io_writeto (void) +{ + FILE *current; + lua_Object f = lua_getparam(FIRSTARG); + if (f == LUA_NOOBJECT) { + closefile(FOUTPUT); + current = stdout; + } + else if (lua_tag(f) == gettag(IOTAG)) + current = lua_getuserdata(f); + else { + char *s = luaL_check_string(FIRSTARG); + current = (*s == '|') ? popen(s+1,"w") : fopen(s,"w"); + if (current == NULL) { + pushresult(0); + return; + } + } + setreturn(current, FOUTPUT); +} + + +static void io_appendto (void) +{ + char *s = luaL_check_string(FIRSTARG); + FILE *fp = fopen (s, "a"); + if (fp != NULL) + setreturn(fp, FOUTPUT); + else + pushresult(0); +} + + +#define NEED_OTHER (EOF-1) /* just some flag different from EOF */ + + +static void read_until (FILE *f, int lim) { + int l = 0; + int c; + for (c = getc(f); c != EOF && c != lim; c = getc(f)) { + luaL_addchar(c); + l++; + } + if (l > 0 || c == lim) /* read anything? */ + lua_pushlstring(luaL_buffer(), l); +} + +static void io_read (void) { + int arg = FIRSTARG; + FILE *f = getfileparam(FINPUT, &arg); + char *p = luaL_opt_string(arg, NULL); + luaL_resetbuffer(); + if (p == NULL) /* default: read a line */ + read_until(f, '\n'); + else if (p[0] == '.' && p[1] == '*' && p[2] == 0) /* p = ".*" */ + read_until(f, EOF); + else { + int l = 0; /* number of chars read in buffer */ + int inskip = 0; /* to control {skips} */ + int c = NEED_OTHER; + while (*p) { + switch (*p) { + case '{': + inskip++; + p++; + continue; + case '}': + if (inskip == 0) + lua_error("unbalanced braces in read pattern"); + inskip--; + p++; + continue; + default: { + char *ep; /* get what is next */ + int m; /* match result */ + if (c == NEED_OTHER) c = getc(f); + if (c == EOF) { + luaI_singlematch(0, p, &ep); /* to set "ep" */ + m = 0; + } + else { + m = luaI_singlematch(c, p, &ep); + if (m) { + if (inskip == 0) { + luaL_addchar(c); + l++; + } + c = NEED_OTHER; + } + } + switch (*ep) { + case '*': /* repetition */ + if (!m) p = ep+1; /* else stay in (repeat) the same item */ + continue; + case '?': /* optional */ + p = ep+1; /* continues reading the pattern */ + continue; + default: + if (m) p = ep; /* continues reading the pattern */ + else + goto break_while; /* pattern fails */ + } + } + } + } break_while: + if (c >= 0) /* not EOF nor NEED_OTHER? */ + ungetc(c, f); + if (l > 0 || *p == 0) /* read something or did not fail? */ + lua_pushlstring(luaL_buffer(), l); + } +} + + +static void io_write (void) +{ + int arg = FIRSTARG; + FILE *f = getfileparam(FOUTPUT, &arg); + int status = 1; + char *s; + long l; + while ((s = luaL_opt_lstr(arg++, NULL, &l)) != NULL) + status = status && (fwrite(s, 1, l, f) == l); + pushresult(status); +} + + +static void io_execute (void) +{ + lua_pushnumber(system(luaL_check_string(1))); +} + + +static void io_remove (void) +{ + pushresult(remove(luaL_check_string(1)) == 0); +} + + +static void io_rename (void) +{ + pushresult(rename(luaL_check_string(1), + luaL_check_string(2)) == 0); +} + + +static void io_tmpname (void) +{ + lua_pushstring(tmpnam(NULL)); +} + + + +static void io_getenv (void) +{ + lua_pushstring(getenv(luaL_check_string(1))); /* if NULL push nil */ +} + + +static void io_clock (void) { + lua_pushnumber(((double)clock())/CLOCKS_PER_SEC); +} + + +static void io_date (void) +{ + time_t t; + struct tm *tm; + char *s = luaL_opt_string(1, "%c"); + char b[BUFSIZ]; + time(&t); tm = localtime(&t); + if (strftime(b,sizeof(b),s,tm)) + lua_pushstring(b); + else + lua_error("invalid `date' format"); +} + + +static void setloc (void) +{ + static int cat[] = {LC_ALL, LC_COLLATE, LC_CTYPE, LC_MONETARY, LC_NUMERIC, + LC_TIME}; + static char *catnames[] = {"all", "collate", "ctype", "monetary", + "numeric", "time", NULL}; + int op = luaL_findstring(luaL_opt_string(2, "all"), catnames); + luaL_arg_check(op != -1, 2, "invalid option"); + lua_pushstring(setlocale(cat[op], luaL_check_string(1))); +} + + +static void io_exit (void) +{ + lua_Object o = lua_getparam(1); + exit(lua_isnumber(o) ? (int)lua_getnumber(o) : 1); +} + + +static void io_debug (void) +{ + while (1) { + char buffer[250]; + fprintf(stderr, "lua_debug> "); + if (fgets(buffer, sizeof(buffer), stdin) == 0) return; + if (strcmp(buffer, "cont\n") == 0) return; + lua_dostring(buffer); + } +} + + +static void lua_printstack (FILE *f) +{ + int level = 1; /* skip level 0 (it's this function) */ + lua_Object func; + while ((func = lua_stackedfunction(level++)) != LUA_NOOBJECT) { + char *name; + int currentline; + char *filename; + int linedefined; + lua_funcinfo(func, &filename, &linedefined); + fprintf(f, (level==2) ? "Active Stack:\n\t" : "\t"); + switch (*lua_getobjname(func, &name)) { + case 'g': + fprintf(f, "function %s", name); + break; + case 't': + fprintf(f, "`%s' tag method", name); + break; + default: { + if (linedefined == 0) + fprintf(f, "main of %s", filename); + else if (linedefined < 0) + fprintf(f, "%s", filename); + else + fprintf(f, "function (%s:%d)", filename, linedefined); + filename = NULL; + } + } + if ((currentline = lua_currentline(func)) > 0) + fprintf(f, " at line %d", currentline); + if (filename) + fprintf(f, " [in file %s]", filename); + fprintf(f, "\n"); + } +} + + +static void errorfb (void) +{ + fprintf(stderr, "lua: %s\n", lua_getstring(lua_getparam(1))); + lua_printstack(stderr); +} + + + +static struct luaL_reg iolib[] = { +{"setlocale", setloc}, +{"execute", io_execute}, +{"remove", io_remove}, +{"rename", io_rename}, +{"tmpname", io_tmpname}, +{"getenv", io_getenv}, +{"date", io_date}, +{"clock", io_clock}, +{"exit", io_exit}, +{"debug", io_debug}, +{"print_stack", errorfb} +}; + +static struct luaL_reg iolibtag[] = { +{"readfrom", io_readfrom}, +{"writeto", io_writeto}, +{"appendto", io_appendto}, +{"read", io_read}, +{"write", io_write} +}; + +static void openwithtags (void) +{ + int iotag = lua_newtag(); + int closedtag = lua_newtag(); + int i; + for (i=0; i<sizeof(iolibtag)/sizeof(iolibtag[0]); i++) { + /* put both tags as upvalues for these functions */ + lua_pushnumber(iotag); + lua_pushnumber(closedtag); + lua_pushcclosure(iolibtag[i].func, 2); + lua_setglobal(iolibtag[i].name); + } + setfile(stdin, FINPUT, iotag); + setfile(stdout, FOUTPUT, iotag); + setfile(stdin, "_STDIN", iotag); + setfile(stdout, "_STDOUT", iotag); + setfile(stderr, "_STDERR", iotag); +} + +void lua_iolibopen (void) +{ + luaL_openlib(iolib, (sizeof(iolib)/sizeof(iolib[0]))); + openwithtags(); + lua_pushcfunction(errorfb); + lua_seterrormethod(); +} diff --git a/src/lib/lmathlib.c b/src/lib/lmathlib.c new file mode 100644 index 00000000..bdc534f5 --- /dev/null +++ b/src/lib/lmathlib.c @@ -0,0 +1,213 @@ +/* +** $Id: lmathlib.c,v 1.10 1998/06/19 16:14:09 roberto Exp $ +** Lua standard mathematical library +** See Copyright Notice in lua.h +*/ + + +#include <stdlib.h> +#include <math.h> + +#include "lauxlib.h" +#include "lua.h" +#include "lualib.h" + +#ifdef M_PI +#define PI M_PI +#else +#define PI ((double)3.14159265358979323846) +#endif + + +#define FROMRAD(a) ((a)*(180.0/PI)) +#define TORAD(a) ((a)*(PI/180.0)) + + +static void math_abs (void) +{ + double d = luaL_check_number(1); + if (d < 0) d = -d; + lua_pushnumber(d); +} + +static void math_sin (void) +{ + lua_pushnumber(sin(TORAD(luaL_check_number(1)))); +} + +static void math_cos (void) +{ + lua_pushnumber(cos(TORAD(luaL_check_number(1)))); +} + +static void math_tan (void) +{ + lua_pushnumber(tan(TORAD(luaL_check_number(1)))); +} + +static void math_asin (void) +{ + lua_pushnumber(FROMRAD(asin(luaL_check_number(1)))); +} + +static void math_acos (void) +{ + lua_pushnumber(FROMRAD(acos(luaL_check_number(1)))); +} + +static void math_atan (void) +{ + lua_pushnumber(FROMRAD(atan(luaL_check_number(1)))); +} + +static void math_atan2 (void) +{ + lua_pushnumber(FROMRAD(atan2(luaL_check_number(1), luaL_check_number(2)))); +} + +static void math_ceil (void) +{ + lua_pushnumber(ceil(luaL_check_number(1))); +} + +static void math_floor (void) +{ + lua_pushnumber(floor(luaL_check_number(1))); +} + +static void math_mod (void) +{ + lua_pushnumber(fmod(luaL_check_number(1), luaL_check_number(2))); +} + +static void math_sqrt (void) +{ + lua_pushnumber(sqrt(luaL_check_number(1))); +} + +static void math_pow (void) +{ + lua_pushnumber(pow(luaL_check_number(1), luaL_check_number(2))); +} + +static void math_log (void) +{ + lua_pushnumber(log(luaL_check_number(1))); +} + +static void math_log10 (void) +{ + lua_pushnumber(log10(luaL_check_number(1))); +} + +static void math_exp (void) +{ + lua_pushnumber(exp(luaL_check_number(1))); +} + +static void math_deg (void) +{ + lua_pushnumber(luaL_check_number(1)*(180.0/PI)); +} + +static void math_rad (void) +{ + lua_pushnumber(luaL_check_number(1)*(PI/180.0)); +} + +static void math_frexp (void) { + int e; + lua_pushnumber(frexp(luaL_check_number(1), &e)); + lua_pushnumber(e); +} + +static void math_ldexp (void) { + lua_pushnumber(ldexp(luaL_check_number(1), luaL_check_number(2))); +} + + + +static void math_min (void) +{ + int i = 1; + double dmin = luaL_check_number(i); + while (lua_getparam(++i) != LUA_NOOBJECT) { + double d = luaL_check_number(i); + if (d < dmin) + dmin = d; + } + lua_pushnumber(dmin); +} + + +static void math_max (void) +{ + int i = 1; + double dmax = luaL_check_number(i); + while (lua_getparam(++i) != LUA_NOOBJECT) { + double d = luaL_check_number(i); + if (d > dmax) + dmax = d; + } + lua_pushnumber(dmax); +} + + +static void math_random (void) +{ + /* the '%' is needed because on some systems (SunOS!) "rand()" may */ + /* return a value bigger than RAND_MAX... */ + double r = (double)(rand()%RAND_MAX) / (double)RAND_MAX; + double l = luaL_opt_number(1, 0); + if (l == 0) + lua_pushnumber(r); + else + lua_pushnumber((int)(r*l)+1); +} + + +static void math_randomseed (void) +{ + srand(luaL_check_number(1)); +} + + +static struct luaL_reg mathlib[] = { +{"abs", math_abs}, +{"sin", math_sin}, +{"cos", math_cos}, +{"tan", math_tan}, +{"asin", math_asin}, +{"acos", math_acos}, +{"atan", math_atan}, +{"atan2", math_atan2}, +{"ceil", math_ceil}, +{"floor", math_floor}, +{"mod", math_mod}, +{"frexp", math_frexp}, +{"ldexp", math_ldexp}, +{"sqrt", math_sqrt}, +{"min", math_min}, +{"max", math_max}, +{"log", math_log}, +{"log10", math_log10}, +{"exp", math_exp}, +{"deg", math_deg}, +{"rad", math_rad}, +{"random", math_random}, +{"randomseed", math_randomseed} +}; + +/* +** Open math library +*/ +void lua_mathlibopen (void) +{ + luaL_openlib(mathlib, (sizeof(mathlib)/sizeof(mathlib[0]))); + lua_pushstring("deg"); lua_setglobal("_TRIGMODE"); + lua_pushcfunction(math_pow); + lua_pushnumber(0); /* to get its tag */ + lua_settagmethod(lua_tag(lua_pop()), "pow"); + lua_pushnumber(PI); lua_setglobal("PI"); +} + diff --git a/src/lib/lstrlib.c b/src/lib/lstrlib.c new file mode 100644 index 00000000..dc79cc7e --- /dev/null +++ b/src/lib/lstrlib.c @@ -0,0 +1,541 @@ +/* +** $Id: lstrlib.c,v 1.18 1998/07/01 14:21:57 roberto Exp $ +** Standard library for strings and pattern-matching +** See Copyright Notice in lua.h +*/ + + +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "lauxlib.h" +#include "lua.h" +#include "lualib.h" + + + +static void addnchar (char *s, int n) +{ + char *b = luaL_openspace(n); + memcpy(b, s, n); + luaL_addsize(n); +} + + +static void str_len (void) +{ + long l; + luaL_check_lstr(1, &l); + lua_pushnumber(l); +} + + +static void closeandpush (void) +{ + lua_pushlstring(luaL_buffer(), luaL_getsize()); +} + + +static long posrelat (long pos, long len) +{ + /* relative string position: negative means back from end */ + return (pos>=0) ? pos : len+pos+1; +} + + +static void str_sub (void) +{ + long l; + char *s = luaL_check_lstr(1, &l); + long start = posrelat(luaL_check_number(2), l); + long end = posrelat(luaL_opt_number(3, -1), l); + if (1 <= start && start <= end && end <= l) + lua_pushlstring(s+start-1, end-start+1); + else lua_pushstring(""); +} + + +static void str_lower (void) +{ + long l; + int i; + char *s = luaL_check_lstr(1, &l); + luaL_resetbuffer(); + for (i=0; i<l; i++) + luaL_addchar(tolower((unsigned char)(s[i]))); + closeandpush(); +} + + +static void str_upper (void) +{ + long l; + int i; + char *s = luaL_check_lstr(1, &l); + luaL_resetbuffer(); + for (i=0; i<l; i++) + luaL_addchar(toupper((unsigned char)(s[i]))); + closeandpush(); +} + +static void str_rep (void) +{ + long l; + char *s = luaL_check_lstr(1, &l); + int n = (int)luaL_check_number(2); + luaL_resetbuffer(); + while (n-- > 0) + addnchar(s, l); + closeandpush(); +} + + +static void str_byte (void) +{ + long l; + char *s = luaL_check_lstr(1, &l); + long pos = posrelat(luaL_opt_number(2, 1), l); + luaL_arg_check(0<pos && pos<=l, 2, "out of range"); + lua_pushnumber((unsigned char)s[pos-1]); +} + +static void str_char (void) { + int i = 0; + luaL_resetbuffer(); + while (lua_getparam(++i) != LUA_NOOBJECT) { + double c = luaL_check_number(i); + luaL_arg_check((unsigned char)c == c, i, "invalid value"); + luaL_addchar((int)c); + } + closeandpush(); +} + + +/* +** ======================================================= +** PATTERN MATCHING +** ======================================================= +*/ + +#define MAX_CAPT 9 + +struct Capture { + int level; /* total number of captures (finished or unfinished) */ + char *src_end; /* end ('\0') of source string */ + struct { + char *init; + int len; /* -1 signals unfinished capture */ + } capture[MAX_CAPT]; +}; + + +#define ESC '%' +#define SPECIALS "^$*?.([%-" + + +static void push_captures (struct Capture *cap) +{ + int i; + for (i=0; i<cap->level; i++) + lua_pushlstring(cap->capture[i].init, cap->capture[i].len); +} + + +static int check_cap (int l, struct Capture *cap) +{ + l -= '1'; + if (!(0 <= l && l < cap->level && cap->capture[l].len != -1)) + lua_error("invalid capture index"); + return l; +} + + +static int capture_to_close (struct Capture *cap) +{ + int level = cap->level; + for (level--; level>=0; level--) + if (cap->capture[level].len == -1) return level; + lua_error("invalid pattern capture"); + return 0; /* to avoid warnings */ +} + + +static char *bracket_end (char *p) +{ + return (*p == 0) ? NULL : strchr((*p=='^') ? p+2 : p+1, ']'); +} + + +static int matchclass (int c, int cl) +{ + int res; + switch (tolower(cl)) { + case 'a' : res = isalpha(c); break; + case 'c' : res = iscntrl(c); break; + case 'd' : res = isdigit(c); break; + case 'l' : res = islower(c); break; + case 'p' : res = ispunct(c); break; + case 's' : res = isspace(c); break; + case 'u' : res = isupper(c); break; + case 'w' : res = isalnum(c); break; + case 'z' : res = (c == '\0'); break; + default: return (cl == c); + } + return (islower((unsigned char)cl) ? res : !res); +} + + +int luaI_singlematch (int c, char *p, char **ep) +{ + switch (*p) { + case '.': /* matches any char */ + *ep = p+1; + return 1; + case '\0': /* end of pattern; matches nothing */ + *ep = p; + return 0; + case ESC: + if (*(++p) == '\0') + luaL_verror("incorrect pattern (ends with `%c')", ESC); + *ep = p+1; + return matchclass(c, (unsigned char)*p); + case '[': { + char *end = bracket_end(p+1); + int sig = *(p+1) == '^' ? (p++, 0) : 1; + if (end == NULL) lua_error("incorrect pattern (missing `]')"); + *ep = end+1; + while (++p < end) { + if (*p == ESC) { + if (((p+1) < end) && matchclass(c, (unsigned char)*++p)) + return sig; + } + else if ((*(p+1) == '-') && (p+2 < end)) { + p+=2; + if ((unsigned char)*(p-2) <= c && c <= (unsigned char)*p) + return sig; + } + else if ((unsigned char)*p == c) return sig; + } + return !sig; + } + default: + *ep = p+1; + return ((unsigned char)*p == c); + } +} + + +static char *matchbalance (char *s, int b, int e, struct Capture *cap) +{ + if (*s != b) return NULL; + else { + int cont = 1; + while (++s < cap->src_end) { + if (*s == e) { + if (--cont == 0) return s+1; + } + else if (*s == b) cont++; + } + } + return NULL; /* string ends out of balance */ +} + + +static char *matchitem (char *s, char *p, struct Capture *cap, char **ep) +{ + if (*p == ESC) { + p++; + if (isdigit((unsigned char)*p)) { /* capture */ + int l = check_cap(*p, cap); + int len = cap->capture[l].len; + *ep = p+1; + if (cap->src_end-s >= len && memcmp(cap->capture[l].init, s, len) == 0) + return s+len; + else return NULL; + } + else if (*p == 'b') { /* balanced string */ + p++; + if (*p == 0 || *(p+1) == 0) + lua_error("unbalanced pattern"); + *ep = p+2; + return matchbalance(s, *p, *(p+1), cap); + } + else p--; /* and go through */ + } + /* "luaI_singlematch" sets "ep" (so must be called even when *s == 0) */ + return (luaI_singlematch((unsigned char)*s, p, ep) && s<cap->src_end) ? + s+1 : NULL; +} + + +static char *match (char *s, char *p, struct Capture *cap) +{ + init: /* using goto's to optimize tail recursion */ + switch (*p) { + case '(': { /* start capture */ + char *res; + if (cap->level >= MAX_CAPT) lua_error("too many captures"); + cap->capture[cap->level].init = s; + cap->capture[cap->level].len = -1; + cap->level++; + if ((res=match(s, p+1, cap)) == NULL) /* match failed? */ + cap->level--; /* undo capture */ + return res; + } + case ')': { /* end capture */ + int l = capture_to_close(cap); + char *res; + cap->capture[l].len = s - cap->capture[l].init; /* close capture */ + if ((res = match(s, p+1, cap)) == NULL) /* match failed? */ + cap->capture[l].len = -1; /* undo capture */ + return res; + } + case '\0': case '$': /* (possibly) end of pattern */ + if (*p == 0 || (*(p+1) == 0 && s == cap->src_end)) + return s; + /* else go through */ + default: { /* it is a pattern item */ + char *ep; /* get what is next */ + char *s1 = matchitem(s, p, cap, &ep); + switch (*ep) { + case '*': { /* repetition */ + char *res; + if (s1 && s1>s && ((res=match(s1, p, cap)) != NULL)) + return res; + p=ep+1; goto init; /* else return match(s, ep+1, cap); */ + } + case '?': { /* optional */ + char *res; + if (s1 && ((res=match(s1, ep+1, cap)) != NULL)) + return res; + p=ep+1; goto init; /* else return match(s, ep+1, cap); */ + } + case '-': { /* repetition */ + char *res; + if ((res = match(s, ep+1, cap)) != NULL) + return res; + else if (s1 && s1>s) { + s = s1; + goto init; /* return match(s1, p, cap); */ + } + else + return NULL; + } + default: + if (s1) { s=s1; p=ep; goto init; } /* return match(s1, ep, cap); */ + else return NULL; + } + } + } +} + + +static void str_find (void) +{ + long l; + char *s = luaL_check_lstr(1, &l); + char *p = luaL_check_string(2); + long init = posrelat(luaL_opt_number(3, 1), l) - 1; + struct Capture cap; + luaL_arg_check(0 <= init && init <= l, 3, "out of range"); + if (lua_getparam(4) != LUA_NOOBJECT || + strpbrk(p, SPECIALS) == NULL) { /* no special characters? */ + char *s2 = strstr(s+init, p); + if (s2) { + lua_pushnumber(s2-s+1); + lua_pushnumber(s2-s+strlen(p)); + return; + } + } + else { + int anchor = (*p == '^') ? (p++, 1) : 0; + char *s1=s+init; + cap.src_end = s+l; + do { + char *res; + cap.level = 0; + if ((res=match(s1, p, &cap)) != NULL) { + lua_pushnumber(s1-s+1); /* start */ + lua_pushnumber(res-s); /* end */ + push_captures(&cap); + return; + } + } while (s1++<cap.src_end && !anchor); + } + lua_pushnil(); /* if arrives here, it didn't find */ +} + + +static void add_s (lua_Object newp, struct Capture *cap) +{ + if (lua_isstring(newp)) { + char *news = lua_getstring(newp); + int l = lua_strlen(newp); + int i; + for (i=0; i<l; i++) { + if (news[i] != ESC) + luaL_addchar(news[i]); + else { + i++; /* skip ESC */ + if (!isdigit((unsigned char)news[i])) + luaL_addchar(news[i]); + else { + int level = check_cap(news[i], cap); + addnchar(cap->capture[level].init, cap->capture[level].len); + } + } + } + } + else { /* is a function */ + lua_Object res; + int status; + int oldbuff; + lua_beginblock(); + push_captures(cap); + /* function may use buffer, so save it and create a new one */ + oldbuff = luaL_newbuffer(0); + status = lua_callfunction(newp); + /* restore old buffer */ + luaL_oldbuffer(oldbuff); + if (status != 0) { + lua_endblock(); + lua_error(NULL); + } + res = lua_getresult(1); + if (lua_isstring(res)) + addnchar(lua_getstring(res), lua_strlen(res)); + lua_endblock(); + } +} + + +static void str_gsub (void) +{ + long srcl; + char *src = luaL_check_lstr(1, &srcl); + char *p = luaL_check_string(2); + lua_Object newp = lua_getparam(3); + int max_s = (int)luaL_opt_number(4, srcl+1); + int anchor = (*p == '^') ? (p++, 1) : 0; + int n = 0; + struct Capture cap; + luaL_arg_check(lua_isstring(newp) || lua_isfunction(newp), 3, + "string or function expected"); + luaL_resetbuffer(); + cap.src_end = src+srcl; + while (n < max_s) { + char *e; + cap.level = 0; + e = match(src, p, &cap); + if (e) { + n++; + add_s(newp, &cap); + } + if (e && e>src) /* non empty match? */ + src = e; /* skip it */ + else if (src < cap.src_end) + luaL_addchar(*src++); + else break; + if (anchor) break; + } + addnchar(src, cap.src_end-src); + closeandpush(); + lua_pushnumber(n); /* number of substitutions */ +} + + +static void luaI_addquoted (char *s) +{ + luaL_addchar('"'); + for (; *s; s++) { + if (strchr("\"\\\n", *s)) + luaL_addchar('\\'); + luaL_addchar(*s); + } + luaL_addchar('"'); +} + +#define MAX_FORMAT 200 + +static void str_format (void) +{ + int arg = 1; + char *strfrmt = luaL_check_string(arg); + struct Capture cap; + cap.src_end = strfrmt+strlen(strfrmt)+1; + luaL_resetbuffer(); + while (*strfrmt) { + if (*strfrmt != '%') + luaL_addchar(*strfrmt++); + else if (*++strfrmt == '%') + luaL_addchar(*strfrmt++); /* %% */ + else { /* format item */ + char form[MAX_FORMAT]; /* store the format ('%...') */ + char *buff; + char *initf = strfrmt; + form[0] = '%'; + cap.level = 0; + if (isdigit((unsigned char)initf[0]) && initf[1] == '$') { + arg = initf[0] - '0'; + initf += 2; /* skip the 'n$' */ + } + arg++; + strfrmt = match(initf, "[-+ #0]*(%d*)%.?(%d*)", &cap); + if (cap.capture[0].len > 2 || cap.capture[1].len > 2) /* < 100? */ + lua_error("invalid format (width or precision too long)"); + strncpy(form+1, initf, strfrmt-initf+1); /* +1 to include conversion */ + form[strfrmt-initf+2] = 0; + buff = luaL_openspace(1000); /* to store the formatted value */ + switch (*strfrmt++) { + case 'q': + luaI_addquoted(luaL_check_string(arg)); + continue; + case 's': { + char *s = luaL_check_string(arg); + buff = luaL_openspace(strlen(s)); + sprintf(buff, form, s); + break; + } + case 'c': case 'd': case 'i': + sprintf(buff, form, (int)luaL_check_number(arg)); + break; + case 'o': case 'u': case 'x': case 'X': + sprintf(buff, form, (unsigned int)luaL_check_number(arg)); + break; + case 'e': case 'E': case 'f': case 'g': case 'G': + sprintf(buff, form, luaL_check_number(arg)); + break; + default: /* also treat cases 'pnLlh' */ + lua_error("invalid option in `format'"); + } + luaL_addsize(strlen(buff)); + } + } + closeandpush(); /* push the result */ +} + + +static struct luaL_reg strlib[] = { +{"strlen", str_len}, +{"strsub", str_sub}, +{"strlower", str_lower}, +{"strupper", str_upper}, +{"strchar", str_char}, +{"strrep", str_rep}, +{"ascii", str_byte}, /* for compatibility */ +{"strbyte", str_byte}, +{"format", str_format}, +{"strfind", str_find}, +{"gsub", str_gsub} +}; + + +/* +** Open string library +*/ +void strlib_open (void) +{ + luaL_openlib(strlib, (sizeof(strlib)/sizeof(strlib[0]))); +} |