Add environment variable access with $NAME syntax
Lexes $NAME as TK_ENVVAR, compiles reads as __getenv("NAME") calls
and writes ($NAME = expr) as __setenv("NAME", expr) calls. Runtime
functions wrap getenv/setenv/unsetenv with automatic tostring coercion.
This commit is contained in:
39
linit.c
39
linit.c
@@ -13,12 +13,14 @@
|
|||||||
|
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include "lua.h"
|
#include "lua.h"
|
||||||
|
|
||||||
#include "lualib.h"
|
#include "lualib.h"
|
||||||
#include "lauxlib.h"
|
#include "lauxlib.h"
|
||||||
#include "llimits.h"
|
#include "llimits.h"
|
||||||
|
#include "lcmd.h"
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -40,30 +42,39 @@ static const luaL_Reg stdlibs[] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/*
|
static int luaB_getenv (lua_State *L) {
|
||||||
** Stub __command function for backtick command syntax.
|
const char *name = luaL_checkstring(L, 1);
|
||||||
** Returns a table {code=0, stdout=cmdstring, stderr=""}.
|
const char *val = getenv(name);
|
||||||
** The real implementation will be provided by issue #03.
|
if (val == NULL)
|
||||||
*/
|
lua_pushnil(L);
|
||||||
static int luaB_command (lua_State *L) {
|
else
|
||||||
const char *cmd = luaL_checkstring(L, 1);
|
lua_pushstring(L, val);
|
||||||
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");
|
|
||||||
return 1;
|
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).
|
** Register shell built-in globals (called after standard libraries).
|
||||||
*/
|
*/
|
||||||
static void opencommand (lua_State *L) {
|
static void opencommand (lua_State *L) {
|
||||||
lua_pushcfunction(L, luaB_command);
|
lua_pushcfunction(L, luaB_command);
|
||||||
lua_setglobal(L, "__command");
|
lua_setglobal(L, "__command");
|
||||||
|
lua_pushcfunction(L, luaB_getenv);
|
||||||
|
lua_setglobal(L, "__getenv");
|
||||||
|
lua_pushcfunction(L, luaB_setenv);
|
||||||
|
lua_setglobal(L, "__setenv");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
17
llex.c
17
llex.c
@@ -50,7 +50,7 @@ static const char *const luaX_tokens [] = {
|
|||||||
"//", "..", "...", "==", ">=", "<=", "~=",
|
"//", "..", "...", "==", ">=", "<=", "~=",
|
||||||
"<<", ">>", "::", "<eof>",
|
"<<", ">>", "::", "<eof>",
|
||||||
"<number>", "<integer>", "<name>", "<string>",
|
"<number>", "<integer>", "<name>", "<string>",
|
||||||
"<command>", "<command_interp>"
|
"<command>", "<command_interp>", "<envvar>"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -108,6 +108,9 @@ static const char *txtToken (LexState *ls, int token) {
|
|||||||
case TK_FLT: case TK_INT:
|
case TK_FLT: case TK_INT:
|
||||||
save(ls, '\0');
|
save(ls, '\0');
|
||||||
return luaO_pushfstring(ls->L, "'%s'", luaZ_buffer(ls->buff));
|
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:
|
default:
|
||||||
return luaX_token2str(ls, token);
|
return luaX_token2str(ls, token);
|
||||||
}
|
}
|
||||||
@@ -633,6 +636,18 @@ static int llex (LexState *ls, SemInfo *seminfo) {
|
|||||||
case '`': { /* explicit command `ls -l` with ${} interpolation */
|
case '`': { /* explicit command `ls -l` with ${} interpolation */
|
||||||
return read_command(ls, seminfo);
|
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 */
|
case '.': { /* '.', '..', '...', or number */
|
||||||
save_and_next(ls);
|
save_and_next(ls);
|
||||||
if (check_next1(ls, '.')) {
|
if (check_next1(ls, '.')) {
|
||||||
|
|||||||
3
llex.h
3
llex.h
@@ -41,7 +41,8 @@ enum RESERVED {
|
|||||||
TK_DBCOLON, TK_EOS,
|
TK_DBCOLON, TK_EOS,
|
||||||
TK_FLT, TK_INT, TK_NAME, TK_STRING,
|
TK_FLT, TK_INT, TK_NAME, TK_STRING,
|
||||||
TK_COMMAND,
|
TK_COMMAND,
|
||||||
TK_COMMAND_INTERP
|
TK_COMMAND_INTERP,
|
||||||
|
TK_ENVVAR
|
||||||
};
|
};
|
||||||
|
|
||||||
/* number of reserved words */
|
/* number of reserved words */
|
||||||
|
|||||||
52
lparser.c
52
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) {
|
static void primaryexp (LexState *ls, expdesc *v) {
|
||||||
/* primaryexp -> NAME | '(' expr ')' | COMMAND */
|
/* primaryexp -> NAME | '(' expr ')' | COMMAND | ENVVAR */
|
||||||
switch (ls->t.token) {
|
switch (ls->t.token) {
|
||||||
case '(': {
|
case '(': {
|
||||||
int line = ls->linenumber;
|
int line = ls->linenumber;
|
||||||
@@ -1321,6 +1338,11 @@ static void primaryexp (LexState *ls, expdesc *v) {
|
|||||||
commandexp(ls, v);
|
commandexp(ls, v);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
case TK_ENVVAR: {
|
||||||
|
codeenvget(ls, v, ls->t.seminfo.ts);
|
||||||
|
luaX_next(ls);
|
||||||
|
return;
|
||||||
|
}
|
||||||
default: {
|
default: {
|
||||||
luaX_syntaxerror(ls, "unexpected symbol");
|
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) {
|
static void statement (LexState *ls) {
|
||||||
int line = ls->linenumber; /* may be needed for error messages */
|
int line = ls->linenumber; /* may be needed for error messages */
|
||||||
enterlevel(ls);
|
enterlevel(ls);
|
||||||
@@ -2225,6 +2271,10 @@ static void statement (LexState *ls) {
|
|||||||
gotostat(ls, line);
|
gotostat(ls, line);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case TK_ENVVAR: { /* stat -> '$' NAME '=' expr */
|
||||||
|
envstat(ls);
|
||||||
|
break;
|
||||||
|
}
|
||||||
#if defined(LUA_COMPAT_GLOBAL)
|
#if defined(LUA_COMPAT_GLOBAL)
|
||||||
case TK_NAME: {
|
case TK_NAME: {
|
||||||
/* compatibility code to parse global keyword when "global"
|
/* compatibility code to parse global keyword when "global"
|
||||||
|
|||||||
Reference in New Issue
Block a user