mirror of
https://github.com/rxrbln/t2sde.git
synced 2025-05-09 04:31:26 +08:00
512 lines
13 KiB
C
512 lines
13 KiB
C
/*
|
|
* --- T2-COPYRIGHT-NOTE-BEGIN ---
|
|
* This copyright note is auto-generated by ./scripts/Create-CopyPatch.
|
|
*
|
|
* T2 SDE: misc/luabash/bash/luabash.c
|
|
* Copyright (C) 2019 The T2 SDE Project
|
|
*
|
|
* More information can be found in the files COPYING and README.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; version 2 of the License. A copy of the
|
|
* GNU General Public License can be found in the file COPYING.
|
|
* --- T2-COPYRIGHT-NOTE-END ---
|
|
*/
|
|
|
|
// compile with:
|
|
// gcc -shared -fPIC -Wall -o luabash.so luabash.c -llua -lm -Wall -ldl
|
|
|
|
/* Some declarations copied from bash-3.1 headers */
|
|
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
|
|
#include "lua.h"
|
|
#include "lauxlib.h"
|
|
#include "lualib.h"
|
|
|
|
|
|
#define EXECUTION_SUCCESS 0
|
|
#define EXECUTION_FAILURE 1
|
|
|
|
typedef struct word_desc {
|
|
char *word;
|
|
int flags;
|
|
} WORD_DESC;
|
|
|
|
typedef struct word_list {
|
|
struct word_list *next;
|
|
WORD_DESC *word;
|
|
} WORD_LIST;
|
|
|
|
typedef int sh_builtin_func_t(WORD_LIST *);
|
|
|
|
#define BUILTIN_ENABLED 0x1
|
|
#define STATIC_BUILTIN 0x4 /* This builtin is not dynamically loaded. */
|
|
#define SPECIAL_BUILTIN 0x8 /* This is a Posix `special' builtin. */
|
|
|
|
struct builtin {
|
|
char *name;
|
|
sh_builtin_func_t *function;
|
|
int flags;
|
|
char * const *long_doc;
|
|
const char *short_doc;
|
|
char *handle;
|
|
};
|
|
|
|
struct variable;
|
|
typedef intmax_t arrayind_t;
|
|
|
|
typedef struct variable *sh_var_value_func_t(struct variable *);
|
|
typedef struct variable *sh_var_assign_func_t(struct variable *, char *, arrayind_t);
|
|
|
|
typedef struct variable {
|
|
char *name;
|
|
char *value;
|
|
char *exportstr;
|
|
sh_var_value_func_t *dynamic_value;
|
|
sh_var_assign_func_t *assign_func;
|
|
int attributes;
|
|
int context;
|
|
} SHELL_VAR;
|
|
|
|
|
|
typedef struct redirect {
|
|
int data; // this is not quite true, but we don't need 'em here...
|
|
} REDIRECT;
|
|
|
|
struct simple_com;
|
|
struct function_def;
|
|
|
|
/* Command Types: */
|
|
enum command_type { cm_for, cm_case, cm_while, cm_if, cm_simple, cm_select,
|
|
cm_connection, cm_function_def, cm_until, cm_group,
|
|
cm_arith, cm_cond, cm_arith_for, cm_subshell };
|
|
|
|
typedef struct command {
|
|
enum command_type type; /* FOR CASE WHILE IF CONNECTION or SIMPLE. */
|
|
int flags; /* Flags controlling execution environment. */
|
|
int line; /* line number the command starts on */
|
|
REDIRECT *redirects; /* Special redirects for FOR CASE, etc. */
|
|
union { // SNIP !!! we only need two of those structs here
|
|
struct simple_com *Simple;
|
|
struct function_def *Function_def;
|
|
} value;
|
|
} COMMAND;
|
|
|
|
|
|
/*************** 3.x definitions ********************/
|
|
|
|
/* The "simple" command. Just a collection of words and redirects. */
|
|
typedef struct simple_com3 {
|
|
int flags; /* See description of CMD flags. */
|
|
int line; /* line number the command starts on */
|
|
WORD_LIST *words; /* The program name, the arguments,
|
|
variable assignments, etc. */
|
|
REDIRECT *redirects; /* Redirections to perform. */
|
|
} SIMPLE_COM3;
|
|
|
|
|
|
/* The "function definition" command. */
|
|
typedef struct function_def3 {
|
|
int flags; /* See description of CMD flags. */
|
|
int line; /* Line number the function def starts on. */
|
|
WORD_DESC *name; /* The name of the function. */
|
|
COMMAND *command; /* The parsed execution tree. */
|
|
char *source_file; /* file in which function was defined, if any */
|
|
} FUNCTION_DEF3;
|
|
|
|
|
|
/*************** 2.x definitions ********************/
|
|
|
|
|
|
/* The "simple" command. Just a collection of words and redirects. */
|
|
typedef struct simple_com2 {
|
|
int flags; /* See description of CMD flags. */
|
|
WORD_LIST *words; /* The program name, the arguments,
|
|
variable assignments, etc. */
|
|
REDIRECT *redirects; /* Redirections to perform. */
|
|
int line; /* line number the command starts on */
|
|
} SIMPLE_COM2;
|
|
|
|
|
|
/* The "function definition" command. */
|
|
typedef struct function_def2 {
|
|
int flags; /* See description of CMD flags. */
|
|
WORD_DESC *name; /* The name of the function. */
|
|
COMMAND *command; /* The parsed execution tree. */
|
|
int line; /* Line number the function def starts on. */
|
|
} FUNCTION_DEF2;
|
|
|
|
/****************************************************/
|
|
|
|
typedef struct simple_com {
|
|
union {
|
|
SIMPLE_COM2 v2;
|
|
SIMPLE_COM3 v3;
|
|
}version;
|
|
} SIMPLE_COM;
|
|
|
|
|
|
typedef struct function_def {
|
|
union {
|
|
FUNCTION_DEF2 v2;
|
|
FUNCTION_DEF3 v3;
|
|
}version;
|
|
} FUNCTION_DEF;
|
|
|
|
|
|
typedef enum { bash_version_v2, bash_version_v3, bash_version_unknown } bash_flavor;
|
|
static bash_flavor current_flavor=bash_version_unknown;
|
|
|
|
SIMPLE_COM* newSimpleCom(WORD_LIST *words)
|
|
{
|
|
SIMPLE_COM* ret=(SIMPLE_COM*) malloc(sizeof(SIMPLE_COM));
|
|
switch (current_flavor) {
|
|
case bash_version_v2:
|
|
ret->version.v2.flags=0;
|
|
ret->version.v2.line=0;
|
|
ret->version.v2.redirects=NULL;
|
|
ret->version.v2.words=words;
|
|
break;
|
|
case bash_version_v3:
|
|
ret->version.v3.flags=0;
|
|
ret->version.v3.line=0;
|
|
ret->version.v3.redirects=NULL;
|
|
ret->version.v3.words=words;
|
|
break;
|
|
default: // somethings wrong here...
|
|
return 0;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
/****************************************************/
|
|
|
|
#ifndef __P
|
|
#define __P(proto) proto
|
|
#endif
|
|
|
|
extern WORD_DESC *make_word __P((const char *));
|
|
extern COMMAND *make_command __P((enum command_type, SIMPLE_COM *));
|
|
extern COMMAND *make_function_def __P((WORD_DESC *, COMMAND *, int, int));
|
|
extern int execute_command __P((COMMAND *));
|
|
|
|
extern SHELL_VAR* find_variable(const char *);
|
|
extern SHELL_VAR* bind_variable(const char *, const char *);
|
|
extern SHELL_VAR* bind_function __P((const char *, COMMAND *));
|
|
extern SHELL_VAR** all_shell_variables __P((void));
|
|
#define value_cell(var) ((var)->value)
|
|
|
|
extern int num_shell_builtins; /* Number of shell builtins. */
|
|
extern struct builtin static_shell_builtins[];
|
|
extern struct builtin* shell_builtins;
|
|
extern void initialize_shell_builtins __P((void));
|
|
extern struct builtin* builtin_address_internal __P((char *, int));
|
|
|
|
|
|
extern void add_alias __P((const char *, const char *));
|
|
|
|
|
|
#ifdef DO_DYNAMIC_REGISTRATION
|
|
|
|
/* dynamic registration, not needed anymore.
|
|
* want to keep that code, one never knows if we might need it again!
|
|
*
|
|
* Valentin.
|
|
*/
|
|
|
|
static int inject_new_builtin (struct builtin* b)
|
|
{
|
|
struct builtin* old_builtin;
|
|
|
|
b->flags |= STATIC_BUILTIN;
|
|
/*if (flags & SPECIAL)
|
|
b->flags |= SPECIAL_BUILTIN;*/
|
|
|
|
if ( (old_builtin = builtin_address_internal (b->name, 1)) != 0 ) {
|
|
memcpy ((void*) old_builtin, b, sizeof (struct builtin));
|
|
} else {
|
|
int total = num_shell_builtins + 1;
|
|
int size = (total + 1) * sizeof (struct builtin);
|
|
|
|
struct builtin* new_shell_builtins = (struct builtin*) malloc (size);
|
|
memcpy ((void*) new_shell_builtins, (void*) shell_builtins,
|
|
num_shell_builtins * sizeof (struct builtin));
|
|
memcpy ((void*) &new_shell_builtins[num_shell_builtins], (void*)b, sizeof (struct builtin));
|
|
new_shell_builtins[total].name = (char *)0;
|
|
new_shell_builtins[total].function = (sh_builtin_func_t *)0;
|
|
new_shell_builtins[total].flags = 0;
|
|
|
|
if (shell_builtins != static_shell_builtins)
|
|
free (shell_builtins);
|
|
|
|
shell_builtins = new_shell_builtins;
|
|
num_shell_builtins = total;
|
|
initialize_shell_builtins ();
|
|
}
|
|
|
|
return EXECUTION_SUCCESS;
|
|
}
|
|
|
|
#endif
|
|
|
|
/* helpers to work with variables, thanks to clifford */
|
|
|
|
static const char *getvar(const char *name)
|
|
{
|
|
SHELL_VAR *var;
|
|
var = find_variable(name);
|
|
return var ? value_cell(var) : 0;
|
|
}
|
|
|
|
#define setvar(a,b) bind_variable(a,b)
|
|
|
|
|
|
/* lua bash wrapper */
|
|
|
|
#define LUABASH_VERSION "lua bash wrapper 0.0.3 (C) 2006 - 2019 Valentin Ziegler & Rene Rebe"
|
|
|
|
#define LUA_ERRMSG fprintf(stderr, "lua bash error: %s\n", lua_tostring(L, -1))
|
|
#define LUA_ERRMSG_FAIL {LUA_ERRMSG; return EXECUTION_FAILURE;}
|
|
|
|
char* luabash_doc[] = {
|
|
LUABASH_VERSION,
|
|
"usage:",
|
|
"\tinit",
|
|
"\tload <lua chunk>",
|
|
"\tcall <lua function> [arguments]",
|
|
(char*) 0
|
|
};
|
|
|
|
static int initialized=0;
|
|
static lua_State* L;
|
|
|
|
static void print_usage()
|
|
{
|
|
int i=0;
|
|
while(luabash_doc[i])
|
|
fprintf(stderr, "%s\n", luabash_doc[i++]);
|
|
}
|
|
|
|
static int register_function (lua_State *L)
|
|
{
|
|
const char* fnname = luaL_checkstring(L, 1);
|
|
|
|
// old code using aliases
|
|
//const char* fmt="luabash call %s ";
|
|
//char* fullname=(char*) malloc(strlen(fmt)+strlen(fnname));
|
|
//sprintf(fullname, fmt, fnname);
|
|
//add_alias(fnname, fullname);
|
|
|
|
WORD_LIST* wluabash=(WORD_LIST*) malloc(sizeof(WORD_LIST));
|
|
WORD_LIST* wcall=(WORD_LIST*) malloc(sizeof(WORD_LIST));
|
|
WORD_LIST* wfnname=(WORD_LIST*) malloc(sizeof(WORD_LIST));
|
|
WORD_LIST* warguments=(WORD_LIST*) malloc(sizeof(WORD_LIST));
|
|
wluabash->next = wcall;
|
|
wcall->next=wfnname;
|
|
wfnname->next=warguments;
|
|
warguments->next=0;
|
|
wluabash->word=make_word("luabash");
|
|
wcall->word=make_word("call");
|
|
wfnname->word=make_word(fnname);
|
|
warguments->word=make_word("$@");
|
|
|
|
SIMPLE_COM* call_luabash=newSimpleCom(wluabash);
|
|
|
|
COMMAND* function_body=make_command(cm_simple, call_luabash);
|
|
bind_function(fnname,function_body);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int get_variable (lua_State *L)
|
|
{
|
|
const char* vname = luaL_checkstring(L, 1);
|
|
lua_pushstring(L, getvar(vname));
|
|
return 1;
|
|
}
|
|
|
|
static int set_variable (lua_State *L)
|
|
{
|
|
const char* vname = luaL_checkstring(L, 1);
|
|
const char* vvalue = luaL_checkstring(L, 2);
|
|
setvar(vname, vvalue);
|
|
return 0;
|
|
}
|
|
|
|
static int call_bashfunction (lua_State *L)
|
|
{
|
|
int no_args=lua_gettop(L);
|
|
int retval=0;
|
|
int i;
|
|
|
|
WORD_LIST* list=0;
|
|
WORD_LIST* start=0;
|
|
|
|
for (i=0; i<no_args; i++) {
|
|
const char* string=luaL_checkstring(L, i+1);
|
|
if (list) {
|
|
list->next=(WORD_LIST*) malloc(sizeof(WORD_LIST));
|
|
list=list -> next;
|
|
} else {
|
|
list=(WORD_LIST*) malloc(sizeof(WORD_LIST));
|
|
start=list;
|
|
}
|
|
list->word=make_word(string);
|
|
list->next=0;
|
|
}
|
|
|
|
if (!list)
|
|
retval=127;
|
|
else {
|
|
SIMPLE_COM* cmd=newSimpleCom(start);
|
|
retval=execute_command(make_command(cm_simple, cmd));
|
|
}
|
|
lua_pushinteger(L,retval);
|
|
return 1;
|
|
}
|
|
|
|
static int get_environment (lua_State *L)
|
|
{
|
|
SHELL_VAR** list=all_shell_variables();
|
|
int i=0;
|
|
|
|
lua_newtable(L);
|
|
while(list && list[i]) {
|
|
const char* key=list[i]->name;
|
|
const char* val=list[i]->value;
|
|
lua_pushstring(L,val);
|
|
lua_setfield(L,-2,key);
|
|
i++;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static const luaL_Reg bashlib[] = {
|
|
{"register", register_function},
|
|
{"getVariable",get_variable},
|
|
{"setVariable",set_variable},
|
|
{"getEnvironment",get_environment},
|
|
{"call",call_bashfunction},
|
|
{NULL, NULL}
|
|
};
|
|
|
|
static int init_luabash()
|
|
{
|
|
if (!initialized) {
|
|
const char* bash_version=getvar("BASH_VERSION");
|
|
if (!bash_version)
|
|
return EXECUTION_FAILURE;
|
|
|
|
if (bash_version[0]=='2')
|
|
current_flavor=bash_version_v2;
|
|
else if (bash_version[0]=='3' ||
|
|
bash_version[0]=='4' ||
|
|
bash_version[0]=='5')
|
|
current_flavor=bash_version_v3;
|
|
|
|
if (current_flavor==bash_version_unknown) {
|
|
fprintf(stderr, "lua bash error: NYI bash version\n");
|
|
return EXECUTION_FAILURE;
|
|
}
|
|
|
|
L=luaL_newstate ();
|
|
if (!L) {
|
|
fprintf(stderr, "lua bash error: failed to initialize lua state\n");
|
|
return EXECUTION_FAILURE;
|
|
}
|
|
|
|
luaL_openlibs(L);
|
|
luaL_register(L, "bash", bashlib);
|
|
|
|
initialized=1;
|
|
}
|
|
|
|
return EXECUTION_SUCCESS;
|
|
}
|
|
|
|
static int load_chunk(const char* filename)
|
|
{
|
|
int result=init_luabash();
|
|
if (result != EXECUTION_SUCCESS)
|
|
return result;
|
|
|
|
if (luaL_loadfile(L, filename) || lua_pcall(L, 0, 0, 0))
|
|
LUA_ERRMSG_FAIL;
|
|
|
|
return EXECUTION_SUCCESS;
|
|
}
|
|
|
|
static int call_function(const char* func, WORD_LIST *argv)
|
|
{
|
|
int i;
|
|
|
|
if (!initialized) {
|
|
fprintf(stderr, "lua bash error: not initialized yet!\n");
|
|
return EXECUTION_FAILURE;
|
|
}
|
|
|
|
lua_getglobal(L, func);
|
|
|
|
int argc = 0;
|
|
while (argv) {
|
|
if (argv->word->word) {
|
|
lua_pushstring(L, argv->word->word);
|
|
++argc;
|
|
}
|
|
argv = argv->next;
|
|
}
|
|
|
|
if (lua_pcall(L, argc, 1, 0))
|
|
LUA_ERRMSG_FAIL;
|
|
|
|
switch (lua_type(L, -1)) {
|
|
case LUA_TNUMBER: return lua_tonumber(L,-1);
|
|
case LUA_TBOOLEAN: return lua_toboolean(L,-1) ? EXECUTION_SUCCESS : EXECUTION_FAILURE;
|
|
case LUA_TNIL: return EXECUTION_SUCCESS;
|
|
}
|
|
|
|
i = lua_type(L,-1);
|
|
printf ("lua bash: unexpected type '%s' returned\n", lua_typename(L,i));
|
|
|
|
return EXECUTION_SUCCESS;
|
|
}
|
|
|
|
|
|
int luabash_builtin(WORD_LIST *list)
|
|
{
|
|
if (list && (list->word->word)) {
|
|
char* command=list->word->word;
|
|
if (strcmp(command, "init") == 0) {
|
|
return init_luabash();
|
|
} else if (strcmp(command, "load") == 0) {
|
|
list=list->next;
|
|
if (list && (list->word->word))
|
|
return load_chunk(list->word->word);
|
|
} else if (strcmp(command, "call") == 0) {
|
|
list=list->next;
|
|
if (list && (list->word->word))
|
|
return call_function(list->word->word, list->next);
|
|
}
|
|
}
|
|
|
|
print_usage();
|
|
return EXECUTION_FAILURE;
|
|
}
|
|
|
|
|
|
struct builtin luabash_struct = {
|
|
"luabash",
|
|
&luabash_builtin,
|
|
BUILTIN_ENABLED,
|
|
luabash_doc,
|
|
LUABASH_VERSION,
|
|
0
|
|
};
|