Implement RLang.spp for templated scripting ##lang (#17067)

Co-authored-by: pancake <pancake@nopcode.org>
This commit is contained in:
pancake 2020-07-01 14:14:18 +02:00 committed by GitHub
parent 4b35fd9f19
commit 13e1636d97
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 565 additions and 32 deletions

View File

@ -5,7 +5,4 @@ OBJS=lang.o
R2DEPS=r_util r_cons
include ../rules.mk
pop:
make
r2 -c '#!rust' -
CFLAGS+=-I$(SHLR)/spp

View File

@ -9,6 +9,7 @@ R_LIB_VERSION(r_lang);
#include "p/vala.c" // hardcoded
#include "p/rust.c" // hardcoded
#include "p/zig.c" // hardcoded
#include "p/spp.c" // hardcoded
#include "p/c.c" // hardcoded
#include "p/lib.c"
#if __UNIX__
@ -49,6 +50,7 @@ R_API RLang *r_lang_new(void) {
r_lang_add (lang, &r_lang_plugin_vala);
r_lang_add (lang, &r_lang_plugin_rust);
r_lang_add (lang, &r_lang_plugin_zig);
r_lang_add (lang, &r_lang_plugin_spp);
r_lang_add (lang, &r_lang_plugin_pipe);
r_lang_add (lang, &r_lang_plugin_lib);
@ -255,6 +257,7 @@ R_API int r_lang_prompt(RLang *lang) {
/* foo */
for (;;) {
r_cons_flush ();
snprintf (buf, sizeof (buf)-1, "%s> ", lang->cur->name);
r_line_set_prompt (buf);
#if 0

View File

@ -8,12 +8,15 @@ r_lang_sources = [
#'p/rust.c',
#'p/vala.c'
#'p/zig.c',
#'p/spp.c',
]
spp_inc = [platform_inc, include_directories('../../shlr/spp')]
r_lang = library('r_lang', r_lang_sources,
include_directories: [platform_inc],
include_directories: [platform_inc, spp_inc],
c_args: library_cflags,
dependencies: [r_util_dep, r_cons_dep],
dependencies: [r_util_dep, spp_dep, r_cons_dep],
install: true,
implicit_include_directories: false,
soversion: r2_libversion

48
libr/lang/p/spp.c Normal file
View File

@ -0,0 +1,48 @@
/* radare - LGPL - Copyright 2020 pancake */
#include "r_lib.h"
#include "r_core.h"
#include "r_lang.h"
#define USE_R2 1
static RLang *Glang = NULL;
#include <spp.h>
#include "spp_r2.inc"
static int lang_spp_init(RLang *l) {
Glang = l;
return true;
}
static int lang_spp_run(RLang *lang, const char *code, int len) {
Output out;
out.fout = NULL;
out.cout = r_strbuf_new (NULL);
r_strbuf_init (out.cout);
spp_proc_set (&spp_r2_proc, NULL, 0);
char *c = strdup (code);
spp_eval (c, &out);
free (c);
char *data = r_strbuf_drain (out.cout);
r_cons_printf ("%s\n", data);
free (data);
return true;
}
static int lang_spp_file(RLang *lang, const char *file) {
size_t len;
char *code = r_file_slurp (file, &len);
int res = lang_spp_run (lang, code, len);
free (code);
return res;
}
static RLangPlugin r_lang_plugin_spp = {
.name = "spp",
.ext = "spp",
.license = "MIT",
.desc = "SPP template programs",
.run = lang_spp_run,
.init = (void*)lang_spp_init,
.run_file = (void*)lang_spp_file,
};

442
libr/lang/p/spp_r2.inc Normal file
View File

@ -0,0 +1,442 @@
/* SPP-R2, a template programming language with r2 integration */
#include <r_util.h>
static char *spp_r2_var_get(char *var) {
return r_sys_getenv (var);
}
static int spp_r2_var_set(const char *var, const char *val) {
return r_sys_setenv (var, val);
}
static TAG_CALLBACK(spp_r2_set) {
char *eq, *val = "";
if (!state->echo[state->ifl]) {
return 0;
}
for (eq = buf; eq[0]; eq++) {
switch (eq[0]) {
case '-':
case '.':
eq[0] = '_';
break;
}
}
eq = strchr (buf, ' ');
if (eq) {
*eq = '\0';
val = eq + 1;
}
if (spp_r2_var_set (buf, val) == -1) {
fprintf (stderr, "Invalid variable name '%s' at line %d\n", buf, state->lineno);
}
return 0;
}
static TAG_CALLBACK(spp_r2_get) {
char *var;
if (!state->echo[state->ifl]) {
return 0;
}
var = spp_r2_var_get (buf);
if (var) {
r_cons_printf ("%s", var);
}
return 0;
}
static TAG_CALLBACK(spp_r2_getrandom) {
int max;
if (!state->echo[state->ifl]) {
return 0;
}
// XXX srsly? this is pretty bad random
srandom (r_sys_getpid ()); // TODO: change this to be portable
max = atoi (buf);
if (max > 0) {
max = (int)(rand () % max);
}
r_cons_printf ("%d", max);
return 0;
}
static TAG_CALLBACK(spp_r2_add) {
char res[32];
char *var, *eq = strchr (buf, ' ');
int ret = 0;
if (!state->echo[state->ifl]) {
return 0;
}
if (eq) {
*eq = '\0';
var = spp_r2_var_get (buf);
if (var) {
ret = atoi (var);
}
ret += atoi (eq + 1);
snprintf (res, sizeof (res), "%d", ret);
r_sys_setenv (buf, res);
} else {
/* syntax error */
}
return 0;
}
static TAG_CALLBACK(spp_r2_sub) {
char *eq = strchr (buf, ' ');
char *var;
int ret = 0;
if (!state->echo[state->ifl]) {
return 0;
}
if (eq) {
*eq = '\0';
var = spp_r2_var_get (buf);
ret = var? atoi (var): 0;
ret -= atoi (eq + 1);
r_sys_setenv (buf, eq + 1);
} else {
/* syntax error */
}
return ret;
}
static TAG_CALLBACK(spp_r2_trace) {
if (state->echo[state->ifl]) {
fprintf (stderr, "%.1000s\n", buf);
}
return 0;
}
/* TODO: deprecate */
static TAG_CALLBACK(spp_r2_echo) {
if (state->echo[state->ifl]) {
r_cons_printf ("%s", buf);
}
// TODO: add variable replacement here?? not necessary, done by {{get}}
return 0;
}
static TAG_CALLBACK(spp_r2_error) {
if (!state->echo[state->ifl]) {
return 0;
}
fprintf (stderr, "ERROR: %s (line=%d)\n", buf, state->lineno);
return -1;
}
static TAG_CALLBACK(spp_r2_warning) {
if (!state->echo[state->ifl]) {
return 0;
}
fprintf (stderr, "WARNING: %s (line=%d)\n", buf, state->lineno);
return 0;
}
static TAG_CALLBACK(spp_r2_r2) {
char *str = Glang->cmd_str (Glang->user, buf);
if (str) {
r_cons_printf ("%s", str);
free (str);
}
return 0;
}
static TAG_CALLBACK(spp_r2_system) {
if (!state->echo[state->ifl]) {
return 0;
}
char *str = r_sys_cmd_str (buf, NULL, NULL);
if (str) {
r_cons_printf ("%s", str);
free (str);
}
return 0;
}
static TAG_CALLBACK(spp_r2_include) {
if (!state->echo[state->ifl]) {
return 0;
}
char *incdir = getenv ("SPP_INCDIR");
if (incdir) {
char *b = strdup (incdir);
if (!b) {
return 0;
}
char *p = realloc (b, strlen (b) + strlen (buf) + 3);
if (p) {
b = p;
strcat (b, "/");
strcat (b, buf);
spp_file (b, out);
}
free (b);
} else {
spp_file (buf, out);
}
return 0;
}
static TAG_CALLBACK(spp_r2_if) {
char *var = spp_r2_var_get (buf);
state->echo[state->ifl + 1] = (var && *var != '0' && *var != '\0') ? 1 : 0;
return 1;
}
/* {{ ifeq $path / }} */
static TAG_CALLBACK(spp_r2_ifeq) {
char *value = buf;
char *eq = strchr (buf, ' ');
if (eq) {
*eq = '\0';
value = spp_r2_var_get (value);
if (value && !strcmp (value, eq + 1)) {
state->echo[state->ifl + 1] = 1;
} else {
state->echo[state->ifl + 1] = 0;
}
} else {
value = spp_r2_var_get (buf);
if (!value || !*value) {
state->echo[state->ifl + 1] = 1;
} else {
state->echo[state->ifl + 1] = 0;
}
}
return 1;
}
static TAG_CALLBACK(spp_r2_hex) {
size_t i;
for (i = 0; buf[i]; i++) {
if (isdigit (buf[i])) {
int ch, b = buf[i + 2];
buf[i + 2] = '\0';
sscanf (buf + i, "%02x", &ch);
r_cons_printf ("%c", ch & 0xff);
buf[i + 2] = b;
buf = buf + 2;
}
}
return 0;
}
static TAG_CALLBACK(spp_r2_grepline) {
FILE *fd;
char b[1024];
int line;
if (!state->echo[state->ifl]) {
return 1;
}
char *ptr = strchr (buf, ' ');
if (ptr) {
*ptr = '\0';
fd = fopen (buf, "r");
line = atoi (ptr+1);
if (fd) {
while (!feof (fd) && line--) {
if (!fgets (b, 1023, fd)) {
break;
}
}
fclose (fd);
r_cons_printf ("%s", b);
} else {
eprintf ("Unable to open '%s'\n", buf);
}
}
return 0;
}
static TAG_CALLBACK(spp_r2_else) {
state->echo[state->ifl] = state->echo[state->ifl] ? 0 : 1;
return 0;
}
static TAG_CALLBACK(spp_r2_ifnot) {
spp_r2_if (state, out, buf);
spp_r2_else (state, out, buf);
return 1;
}
static TAG_CALLBACK(spp_r2_ifin) {
char *var, *ptr;
if (!state->echo[state->ifl]) {
return 1;
}
ptr = strchr (buf, ' ');
state->echo[state->ifl + 1] = 0;
if (ptr) {
*ptr='\0';
var = getenv (buf);
if (strstr (ptr + 1, var)) {
state->echo[state->ifl + 1] = 1;
}
}
return 1;
}
static TAG_CALLBACK(spp_r2_endif) {
return -1;
}
static TAG_CALLBACK(spp_r2_default) {
if (!state->echo[state->ifl]) {
return 0;
}
if (buf[-1] != ';') { /* commented tag */
fprintf (stderr, "WARNING: invalid command: '%s' at line %d\n", buf, state->lineno);
}
return 0;
}
#if HAVE_SYSTEM
static FILE *spp_r2_pipe_fd = NULL;
#endif
static TAG_CALLBACK(spp_r2_pipe) {
#if HAVE_SYSTEM
spp_r2_pipe_fd = popen (buf, "w");
#endif
return 0;
}
static char *spp_r2_switch_str = NULL;
static TAG_CALLBACK(spp_r2_switch) {
char *var = spp_r2_var_get (buf);
if (var) {
spp_r2_switch_str = strdup (var);
} else {
spp_r2_switch_str = strdup ("");
}
return 1;
}
static TAG_CALLBACK(spp_r2_case) {
state->echo[state->ifl] = strcmp (buf, spp_r2_switch_str)?0:1;
return 0;
}
static TAG_CALLBACK(spp_r2_endswitch) {
free (spp_r2_switch_str);
spp_r2_switch_str = NULL;
return -1;
}
static TAG_CALLBACK(spp_r2_endpipe) {
#if HAVE_SYSTEM
/* TODO: Get output here */
int ret = 0, len = 0;
int outlen = 4096;
char *str = (char *)malloc (4096);
char *tstr;
if (!str) {
return 0;
}
do {
len += ret;
ret = fread (str + len, 1, 1023, spp_r2_pipe_fd);
if (ret + 1024 > outlen) {
outlen += 4096;
tstr = realloc (str, outlen);
if (!tstr) {
fprintf (stderr, "Out of memory.\n");
break;
}
str = tstr;
}
} while (ret > 0);
str[len] = '\0';
r_cons_printf ("%s", str);
if (spp_r2_pipe_fd) {
pclose (spp_r2_pipe_fd);
}
spp_r2_pipe_fd = NULL;
free (str);
#endif
return 0;
}
static PUT_CALLBACK(spp_r2_fputs) {
#if HAVE_SYSTEM
if (spp_r2_pipe_fd) {
fprintf (spp_r2_pipe_fd, "%s", buf);
} else
#endif
{
r_cons_printf ("%s", buf);
}
return 0;
}
static SppTag spp_r2_tags[] = {
{ "get", spp_r2_get },
{ "hex", spp_r2_hex },
{ "getrandom", spp_r2_getrandom },
{ "grepline", spp_r2_grepline },
{ "set", spp_r2_set },
{ "add", spp_r2_add },
{ "sub", spp_r2_sub },
{ "switch", spp_r2_switch },
{ "case", spp_r2_case },
{ "endswitch", spp_r2_endswitch },
{ "echo", spp_r2_echo },
{ "error", spp_r2_error },
{ "warning", spp_r2_warning },
{ "trace", spp_r2_trace },
{ "ifin", spp_r2_ifin },
{ "ifnot", spp_r2_ifnot },
{ "ifeq", spp_r2_ifeq },
{ "if", spp_r2_if },
{ "else", spp_r2_else },
{ "endif", spp_r2_endif },
{ "pipe", spp_r2_pipe },
{ "endpipe", spp_r2_endpipe },
{ "include", spp_r2_include },
{ "system", spp_r2_system },
{ "r2", spp_r2_r2 },
{ NULL, spp_r2_default },
{ NULL }
};
static ARG_CALLBACK(spp_r2_arg_i) {
r_sys_setenv ("SPP_INCDIR", arg);
return 0;
}
static ARG_CALLBACK(spp_r2_arg_d) {
/* TODO: Handle error */
char *eq = strchr (arg, '=');
if (eq) {
*eq = '\0';
spp_r2_var_set (arg, eq+1);
} else {
spp_r2_var_set (arg, "");
}
return 0;
}
static struct Arg spp_r2_args[] = {
{ "-I", "add include directory", 1, spp_r2_arg_i },
{ "-D", "define value of variable", 1, spp_r2_arg_d },
{ NULL }
};
DLL_LOCAL SppProc spp_r2_proc = {
.name = "sppr2",
.tags = (struct Tag **)spp_r2_tags,
.args = (struct Arg **)spp_r2_args,
.token = " ",
.eof = NULL,
.tag_pre = "{",
.tag_post = "}",
.chop = 1,
.fputs = spp_r2_fputs,
.multiline = NULL,
.default_echo = 1,
.tag_begin = 0,
};

View File

@ -83,7 +83,7 @@ r_util_sources = [
'annotated_code.c'
]
r_util_deps = [ldl, mth, pth, utl, sdb_dep, zlib_dep, platform_deps]
r_util_deps = [ldl, mth, spp_dep, pth, utl, sdb_dep, zlib_dep, platform_deps]
if host_machine.system().startswith('freebsd') or host_machine.system().startswith('netbsd')
# backtrace_symbols_fd requires -lexecinfo
r_util_deps += [cc.find_library('execinfo')]

View File

@ -1,5 +1,6 @@
SPPPATH=../../shlr/spp/
CFLAGS+=-DHAVE_FORK=$(HAVE_FORK)
CFLAGS+=-I../../shlr
SPP_OBJS=spp.o
SPPOBJS=$(addprefix ${SPPPATH},${SPP_OBJS})
OBJS+=$(SPPOBJS)

View File

@ -261,6 +261,7 @@ io.zip
io.r2k
io.ar
io.rbuf
lang.spp
lang.vala
parse.6502_pseudo
parse.arm_pseudo

View File

@ -37,6 +37,7 @@ test:
done
install:
mkdir -p ${BINDIR}
${INSTALL_BIN} ${BIN} ${BINDIR}
uninstall:

View File

@ -87,7 +87,7 @@ static TAG_CALLBACK(spp_getrandom) {
return 0;
}
// XXX srsly? this is pretty bad random
srandom (r_sys_getpid()); // TODO: change this to be portable
srandom (r_sys_getpid ()); // TODO: change this to be portable
max = atoi (buf);
if (max > 0) {
max = (int)(rand () % max);

View File

@ -148,10 +148,13 @@ err_r_sys_get_env:
#endif
}
unsigned int r_sys_getpid() {
#if __WINDOWS__
return (unsigned int)GetCurrentProcessId();
int r_sys_getpid() {
#if __UNIX__
return getpid();
#elif __WINDOWS__
return GetCurrentProcessId();
#else
return (unsigned int)getpid();
#warning r_sys_getpid not implemented for this platform
return -1;
#endif
}

View File

@ -22,7 +22,7 @@ void r_strbuf_fini(SStrBuf *sb);
void r_strbuf_init(SStrBuf *sb);
int r_sys_setenv(const char *key, const char *value);
char *r_sys_getenv(const char *key);
unsigned int r_sys_getpid();
int r_sys_getpid(void);
#endif
#endif

View File

@ -5,7 +5,8 @@
#include "config.h"
S_API int spp_run(char *buf, Output *out) {
int i, ret = 0;
size_t i;
int ret = 0;
char *tok;
D fprintf (stderr, "SPP_RUN(%s)\n", buf);
@ -101,6 +102,13 @@ int do_fputs(Output *out, char *str) {
return printed;
}
S_API void spp_proc_eval(SppProc *p, char *buf, Output *out) {
SppProc *op = proc;
proc = p;
spp_eval (buf, out);
proc = op;
}
S_API void spp_eval(char *buf, Output *out) {
char *ptr, *ptr2;
char *ptrr = NULL;
@ -294,21 +302,24 @@ S_API void spp_proc_list() {
}
}
S_API void spp_proc_set(struct Proc *p, char *arg, int fail) {
int i, j;
if (arg)
for (j = 0; procs[j]; j++) {
if (!strcmp (procs[j]->name, arg)) {
proc = procs[j];
D printf ("SET PROC:(%s)(%s)\n", arg, proc->name);
break;
S_API void spp_proc_set(SppProc *p, const char *arg, int fail) {
size_t i;
bool found = false;
if (arg) {
for (i = 0; procs[i]; i++) {
if (!strcmp (procs[i]->name, arg)) {
proc = procs[i];
found = true;
D printf ("SET PROC:(%s)(%s)\n", arg, proc->name);
break;
}
}
}
if (arg && *arg && !procs[j] && fail) {
if (arg && *arg && !procs[i] && fail) {
fprintf (stderr, "Invalid preprocessor name '%s'\n", arg);
return;
}
if (!proc) {
if (!found || !proc) {
proc = p;
}
if (proc) {
@ -318,7 +329,7 @@ S_API void spp_proc_set(struct Proc *p, char *arg, int fail) {
proc->state.echo[i] = proc->default_echo;
}
//args = (struct Arg*)proc->args;
tags = (struct Tag*)proc->tags;
tags = (SppTag*)proc->tags;
}
}

View File

@ -119,19 +119,19 @@ typedef struct SppBuf {
#define PUT_CALLBACK(x) int x (Output *out, char *buf)
#define IS_SPACE(x) ((x==' ')||(x=='\t')||(x=='\r')||(x=='\n'))
struct Tag {
typedef struct Tag {
const char *name;
TAG_CALLBACK((*callback));
};
} SppTag;
struct Arg {
typedef struct Arg {
const char *flag;
const char *desc;
int has_arg;
ARG_CALLBACK((*callback));
};
} SppArg;
struct Proc {
typedef struct Proc {
const char *name;
struct Tag **tags;
struct Arg **args;
@ -146,15 +146,16 @@ struct Proc {
int default_echo;
SppState state;
SppBuf buf;
};
} SppProc;
S_API int spp_file(const char *file, Output *out);
S_API int spp_run(char *buf, Output *out);
S_API void spp_eval(char *buf, Output *out);
S_API void spp_proc_eval(SppProc *p, char *buf, Output *out);
S_API void spp_io(FILE *in, Output *out);
S_API void spp_proc_list(void);
S_API void spp_proc_list_kw(void);
S_API void spp_proc_set(struct Proc *p, char *arg, int fail);
S_API void spp_proc_set(SppProc *p, const char *arg, int fail);
#if DEBUG
#define D if (1)

View File

@ -40,6 +40,12 @@ RebuildIOSDebug() {
fi
}
RebuildSpp() {
Rebuild shlr/spp
Rebuild libr/util
Rebuild libr/lang
}
RebuildJava() {
Rebuild shlr/java
Rebuild libr/asm

View File

@ -17,3 +17,16 @@ EXPECT=<<EOF
hello world
EOF
RUN
NAME=.spp
FILE=-
CMDS=<<EOF
. scripts/r2test.spp
EOF
EXPECT=<<EOF
Test
hello world
Eron
EOF
RUN

3
test/scripts/r2test.spp Normal file
View File

@ -0,0 +1,3 @@
Test
{r2 ?e hello world}
Eron