diff --git a/linit.c b/linit.c index e9955e97..725deb35 100644 --- a/linit.c +++ b/linit.c @@ -13,12 +13,14 @@ #include +#include #include "lua.h" #include "lualib.h" #include "lauxlib.h" #include "llimits.h" +#include "lcmd.h" /* @@ -40,30 +42,39 @@ static const luaL_Reg stdlibs[] = { }; -/* -** Stub __command function for backtick command syntax. -** Returns a table {code=0, stdout=cmdstring, stderr=""}. -** The real implementation will be provided by issue #03. -*/ -static int luaB_command (lua_State *L) { - const char *cmd = luaL_checkstring(L, 1); - lua_createtable(L, 0, 3); - lua_pushinteger(L, 0); - lua_setfield(L, -2, "code"); - lua_pushstring(L, cmd); - lua_setfield(L, -2, "stdout"); - lua_pushliteral(L, ""); - lua_setfield(L, -2, "stderr"); +static int luaB_getenv (lua_State *L) { + const char *name = luaL_checkstring(L, 1); + const char *val = getenv(name); + if (val == NULL) + lua_pushnil(L); + else + lua_pushstring(L, val); return 1; } +static int luaB_setenv (lua_State *L) { + const char *name = luaL_checkstring(L, 1); + if (lua_isnoneornil(L, 2)) + unsetenv(name); + else { + const char *val = luaL_tolstring(L, 2, NULL); + setenv(name, val, 1); + } + return 0; +} + + /* ** Register shell built-in globals (called after standard libraries). */ static void opencommand (lua_State *L) { lua_pushcfunction(L, luaB_command); lua_setglobal(L, "__command"); + lua_pushcfunction(L, luaB_getenv); + lua_setglobal(L, "__getenv"); + lua_pushcfunction(L, luaB_setenv); + lua_setglobal(L, "__setenv"); } diff --git a/llex.c b/llex.c index 37be9b96..fe1924de 100644 --- a/llex.c +++ b/llex.c @@ -50,7 +50,7 @@ static const char *const luaX_tokens [] = { "//", "..", "...", "==", ">=", "<=", "~=", "<<", ">>", "::", "", "", "", "", "", - "", "" + "", "", "" }; @@ -108,6 +108,9 @@ static const char *txtToken (LexState *ls, int token) { case TK_FLT: case TK_INT: save(ls, '\0'); return luaO_pushfstring(ls->L, "'%s'", luaZ_buffer(ls->buff)); + case TK_ENVVAR: + save(ls, '\0'); + return luaO_pushfstring(ls->L, "'$%s'", luaZ_buffer(ls->buff)); default: return luaX_token2str(ls, token); } @@ -633,6 +636,18 @@ static int llex (LexState *ls, SemInfo *seminfo) { case '`': { /* explicit command `ls -l` with ${} interpolation */ return read_command(ls, seminfo); } + case '$': { /* environment variable $NAME */ + next(ls); + if (lislalpha(ls->current)) { /* $NAME */ + do { + save_and_next(ls); + } while (lislalnum(ls->current)); + seminfo->ts = luaX_newstring(ls, luaZ_buffer(ls->buff), + luaZ_bufflen(ls->buff)); + return TK_ENVVAR; + } + else return '$'; /* bare $ as single-char token */ + } case '.': { /* '.', '..', '...', or number */ save_and_next(ls); if (check_next1(ls, '.')) { diff --git a/llex.h b/llex.h index 111e7a57..c8a74ebb 100644 --- a/llex.h +++ b/llex.h @@ -41,7 +41,8 @@ enum RESERVED { TK_DBCOLON, TK_EOS, TK_FLT, TK_INT, TK_NAME, TK_STRING, TK_COMMAND, - TK_COMMAND_INTERP + TK_COMMAND_INTERP, + TK_ENVVAR }; /* number of reserved words */ diff --git a/lparser.c b/lparser.c index 53c74fcd..ae88577a 100644 --- a/lparser.c +++ b/lparser.c @@ -1302,8 +1302,25 @@ static void commandexp (LexState *ls, expdesc *v) { } +static void codeenvget (LexState *ls, expdesc *v, TString *name) { + FuncState *fs = ls->fs; + int base, line; + expdesc func, arg; + TString *fname = luaX_newstring(ls, "__getenv", 8); + line = ls->linenumber; + buildglobal(ls, fname, &func); + luaK_exp2nextreg(fs, &func); + base = func.u.info; + codestring(&arg, name); + luaK_exp2nextreg(fs, &arg); + init_exp(v, VCALL, luaK_codeABC(fs, OP_CALL, base, 2, 2)); + luaK_fixline(fs, line); + fs->freereg = cast_byte(base + 1); +} + + static void primaryexp (LexState *ls, expdesc *v) { - /* primaryexp -> NAME | '(' expr ')' | COMMAND */ + /* primaryexp -> NAME | '(' expr ')' | COMMAND | ENVVAR */ switch (ls->t.token) { case '(': { int line = ls->linenumber; @@ -1321,6 +1338,11 @@ static void primaryexp (LexState *ls, expdesc *v) { commandexp(ls, v); return; } + case TK_ENVVAR: { + codeenvget(ls, v, ls->t.seminfo.ts); + luaX_next(ls); + return; + } default: { luaX_syntaxerror(ls, "unexpected symbol"); } @@ -2160,6 +2182,30 @@ static void retstat (LexState *ls) { } +static void envstat (LexState *ls) { + /* envstat -> '$' NAME '=' expr */ + FuncState *fs = ls->fs; + TString *name = ls->t.seminfo.ts; + int base, line; + expdesc func, arg, val; + TString *fname; + line = ls->linenumber; + luaX_next(ls); /* skip TK_ENVVAR */ + checknext(ls, '='); + fname = luaX_newstring(ls, "__setenv", 8); + buildglobal(ls, fname, &func); + luaK_exp2nextreg(fs, &func); + base = func.u.info; + codestring(&arg, name); + luaK_exp2nextreg(fs, &arg); + expr(ls, &val); + luaK_exp2nextreg(fs, &val); + init_exp(&val, VCALL, luaK_codeABC(fs, OP_CALL, base, 3, 1)); + luaK_fixline(fs, line); + fs->freereg = cast_byte(base); +} + + static void statement (LexState *ls) { int line = ls->linenumber; /* may be needed for error messages */ enterlevel(ls); @@ -2225,6 +2271,10 @@ static void statement (LexState *ls) { gotostat(ls, line); break; } + case TK_ENVVAR: { /* stat -> '$' NAME '=' expr */ + envstat(ls); + break; + } #if defined(LUA_COMPAT_GLOBAL) case TK_NAME: { /* compatibility code to parse global keyword when "global"