t2sde/misc/luabash/bash/luabash.c
René Rebe 44646b8114 * added bash v5 to luabash API binding
git-svn-id: https://svn.exactcode.de/t2/trunk@47637 c5f82cb5-29bc-0310-9cd0-bff59a50e3bc
2019-02-09 21:09:09 +00:00

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
};