--- a/editors/awk.c +++ b/editors/awk.c @@ -53,9 +53,14 @@ typedef struct chain_s { } chain; /* Function */ +typedef var *(*awk_cfunc)(var *res, var *args, int nargs); typedef struct func_s { unsigned nargs; + enum { AWKFUNC, CFUNC } type; + union { + awk_cfunc cfunc; struct chain_s body; + } x; } func; /* I/O stream */ @@ -1395,7 +1400,8 @@ static void parse_program(char *p) next_token(TC_FUNCTION); g_pos++; f = newfunc(t_string); - f->body.first = NULL; + f->type = AWKFUNC; + f->x.body.first = NULL; f->nargs = 0; while (next_token(TC_VARIABLE | TC_SEQTERM) & TC_VARIABLE) { v = findvar(ahash, t_string); @@ -1404,7 +1410,7 @@ static void parse_program(char *p) if (next_token(TC_COMMA | TC_SEQTERM) & TC_SEQTERM) break; } - seq = &(f->body); + seq = &(f->x.body); chain_group(); clear_array(ahash); @@ -2367,7 +2373,8 @@ static var *evaluate(node *op, var *res) break; case XC( OC_FUNC ): - if (!op->r.f->body.first) + if ((op->r.f->type == AWKFUNC) && + !op->r.f->x.body.first) syntax_error(EMSG_UNDEF_FUNC); X.v = R.v = nvalloc(op->r.f->nargs+1); @@ -2384,7 +2391,10 @@ static var *evaluate(node *op, var *res) fnargs = X.v; L.s = g_progname; - res = evaluate(op->r.f->body.first, res); + if (op->r.f->type == AWKFUNC) + res = evaluate(op->r.f->x.body.first, res); + else if (op->r.f->type == CFUNC) + res = op->r.f->x.cfunc(res, fnargs, op->r.f->nargs); g_progname = L.s; nvfree(fnargs); @@ -2747,6 +2757,143 @@ static rstream *next_input_file(void) #undef files_happen } +/* read the contents of an entire file */ +static char *get_file(const char *fname) +{ + FILE *F; + char *s = NULL; + int i, j, flen; + + F = fopen(fname, "r"); + if (!F) { + return NULL; + } + + if (fseek(F, 0, SEEK_END) == 0) { + flen = ftell(F); + s = (char *)xmalloc(flen+4); + fseek(F, 0, SEEK_SET); + i = 1 + fread(s+1, 1, flen, F); + } else { + for (i=j=1; j>0; i+=j) { + s = (char *)xrealloc(s, i+4096); + j = fread(s+i, 1, 4094, F); + } + } + + s[i] = '\0'; + fclose(F); + return s; +} + + +/* parse_include(): + * + * taken from parse_program from awk.c + * END{} is not parsed here, and BEGIN{} is executed immediately + */ +static void parse_include(char *p) +{ + uint32_t tclass; + chain *initseq = NULL; + chain tmp; + func *f; + var *v, *tv; + + tv = nvalloc(1); + memset(&tmp, 0, sizeof(tmp)); + g_pos = p; + t_lineno = 1; + while ((tclass = next_token(TC_EOF | TC_OPSEQ | + TC_OPTERM | TC_BEGIN | TC_FUNCDECL)) != TC_EOF) { + if (tclass & TC_OPTERM) + continue; + + seq = &tmp; + if (tclass & TC_BEGIN) { + initseq = xzalloc(sizeof(chain)); + seq = initseq; + chain_group(); + } else if (tclass & TC_FUNCDECL) { + next_token(TC_FUNCTION); + g_pos++; + f = newfunc(t_string); + f->type = AWKFUNC; + f->x.body.first = NULL; + f->nargs = 0; + while (next_token(TC_VARIABLE | TC_SEQTERM) & TC_VARIABLE) { + v = findvar(ahash, t_string); + v->x.aidx = (f->nargs)++; + + if (next_token(TC_COMMA | TC_SEQTERM) & TC_SEQTERM) + break; + } + seq = &(f->x.body); + chain_group(); + clear_array(ahash); + } + } + if (initseq && initseq->first) + tv = evaluate(initseq->first, tv); + nvfree(tv); +} + + +/* include an awk file and run its BEGIN{} section */ +static xhash *includes = NULL; +static void include_file(const char *filename) +{ + char *s; + var *v; + int oldlnr = g_lineno; + const char *oldprg = g_progname; + + if (!includes) + includes = hash_init(); + + /* find out if the file has been included already */ + v = findvar(includes, filename); + if (istrue(v)) + return; + setvar_s(v, "1"); + + /* read include file */ + s = get_file(filename); + if (!s) { + fprintf(stderr, "Could not open file.\n"); + return; + } + g_lineno = 1; + g_progname = xstrdup(filename); + parse_include(s+1); + free(s); + g_lineno = oldlnr; + g_progname = oldprg; +} + +static var *include(var *res, var *args, int nargs) +{ + const char *s; + + nargs = nargs; /* shut up, gcc */ + s = getvar_s(args); + if (s && (strlen(s) > 0)) + include_file(s); + + return res; +} + +/* registers a global c function for the awk interpreter */ +static void register_cfunc(const char *name, awk_cfunc cfunc, int nargs) +{ + func *f; + + f = newfunc(name); + f->type = CFUNC; + f->x.cfunc = cfunc; + f->nargs = nargs; +} + int awk_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int awk_main(int argc, char **argv) { @@ -2812,6 +2959,9 @@ int awk_main(int argc, char **argv) *s1 = '='; } } + + register_cfunc("include", include, 1); + opt_complementary = "v::f::"; /* -v and -f can occur multiple times */ opt = getopt32(argv, "F:v:f:W:", &opt_F, &list_v, &list_f, &opt_W); argv += optind;