| 1 | --- a/editors/awk.c |
| 2 | +++ b/editors/awk.c |
| 3 | @@ -1,3 +1,4 @@ |
| 4 | + |
| 5 | /* vi: set sw=4 ts=4: */ |
| 6 | /* |
| 7 | * awk implementation for busybox |
| 8 | @@ -81,9 +82,14 @@ typedef struct chain_s { |
| 9 | } chain; |
| 10 | |
| 11 | /* Function */ |
| 12 | +typedef var *(*awk_cfunc)(var *res, var *args, int nargs); |
| 13 | typedef struct func_s { |
| 14 | unsigned nargs; |
| 15 | + enum { AWKFUNC, CFUNC } type; |
| 16 | + union { |
| 17 | + awk_cfunc cfunc; |
| 18 | struct chain_s body; |
| 19 | + } x; |
| 20 | } func; |
| 21 | |
| 22 | /* I/O stream */ |
| 23 | @@ -1473,7 +1479,8 @@ static void parse_program(char *p) |
| 24 | next_token(TC_FUNCTION); |
| 25 | g_pos++; |
| 26 | f = newfunc(t_string); |
| 27 | - f->body.first = NULL; |
| 28 | + f->type = AWKFUNC; |
| 29 | + f->x.body.first = NULL; |
| 30 | f->nargs = 0; |
| 31 | while (next_token(TC_VARIABLE | TC_SEQTERM) & TC_VARIABLE) { |
| 32 | v = findvar(ahash, t_string); |
| 33 | @@ -1482,7 +1489,7 @@ static void parse_program(char *p) |
| 34 | if (next_token(TC_COMMA | TC_SEQTERM) & TC_SEQTERM) |
| 35 | break; |
| 36 | } |
| 37 | - seq = &f->body; |
| 38 | + seq = &f->x.body; |
| 39 | chain_group(); |
| 40 | clear_array(ahash); |
| 41 | |
| 42 | @@ -2580,7 +2587,8 @@ static var *evaluate(node *op, var *res) |
| 43 | var *vbeg, *v; |
| 44 | const char *sv_progname; |
| 45 | |
| 46 | - if (!op->r.f->body.first) |
| 47 | + if ((op->r.f->type == AWKFUNC) && |
| 48 | + !op->r.f->x.body.first) |
| 49 | syntax_error(EMSG_UNDEF_FUNC); |
| 50 | |
| 51 | vbeg = v = nvalloc(op->r.f->nargs + 1); |
| 52 | @@ -2597,7 +2605,10 @@ static var *evaluate(node *op, var *res) |
| 53 | fnargs = vbeg; |
| 54 | sv_progname = g_progname; |
| 55 | |
| 56 | - res = evaluate(op->r.f->body.first, res); |
| 57 | + if (op->r.f->type == AWKFUNC) |
| 58 | + res = evaluate(op->r.f->x.body.first, res); |
| 59 | + else if (op->r.f->type == CFUNC) |
| 60 | + res = op->r.f->x.cfunc(res, fnargs, op->r.f->nargs); |
| 61 | |
| 62 | g_progname = sv_progname; |
| 63 | nvfree(fnargs); |
| 64 | @@ -2991,6 +3002,143 @@ static rstream *next_input_file(void) |
| 65 | #undef files_happen |
| 66 | } |
| 67 | |
| 68 | +/* read the contents of an entire file */ |
| 69 | +static char *get_file(const char *fname) |
| 70 | +{ |
| 71 | + FILE *F; |
| 72 | + char *s = NULL; |
| 73 | + int i, j, flen; |
| 74 | + |
| 75 | + F = fopen(fname, "r"); |
| 76 | + if (!F) { |
| 77 | + return NULL; |
| 78 | + } |
| 79 | + |
| 80 | + if (fseek(F, 0, SEEK_END) == 0) { |
| 81 | + flen = ftell(F); |
| 82 | + s = (char *)xmalloc(flen+4); |
| 83 | + fseek(F, 0, SEEK_SET); |
| 84 | + i = 1 + fread(s+1, 1, flen, F); |
| 85 | + } else { |
| 86 | + for (i=j=1; j>0; i+=j) { |
| 87 | + s = (char *)xrealloc(s, i+4096); |
| 88 | + j = fread(s+i, 1, 4094, F); |
| 89 | + } |
| 90 | + } |
| 91 | + |
| 92 | + s[i] = '\0'; |
| 93 | + fclose(F); |
| 94 | + return s; |
| 95 | +} |
| 96 | + |
| 97 | + |
| 98 | +/* parse_include(): |
| 99 | + * |
| 100 | + * taken from parse_program from awk.c |
| 101 | + * END{} is not parsed here, and BEGIN{} is executed immediately |
| 102 | + */ |
| 103 | +static void parse_include(char *p) |
| 104 | +{ |
| 105 | + uint32_t tclass; |
| 106 | + chain *initseq = NULL; |
| 107 | + chain tmp; |
| 108 | + func *f; |
| 109 | + var *v, *tv; |
| 110 | + |
| 111 | + tv = nvalloc(1); |
| 112 | + memset(&tmp, 0, sizeof(tmp)); |
| 113 | + g_pos = p; |
| 114 | + t_lineno = 1; |
| 115 | + while ((tclass = next_token(TC_EOF | TC_OPSEQ | |
| 116 | + TC_OPTERM | TC_BEGIN | TC_FUNCDECL)) != TC_EOF) { |
| 117 | + if (tclass & TC_OPTERM) |
| 118 | + continue; |
| 119 | + |
| 120 | + seq = &tmp; |
| 121 | + if (tclass & TC_BEGIN) { |
| 122 | + initseq = xzalloc(sizeof(chain)); |
| 123 | + seq = initseq; |
| 124 | + chain_group(); |
| 125 | + } else if (tclass & TC_FUNCDECL) { |
| 126 | + next_token(TC_FUNCTION); |
| 127 | + g_pos++; |
| 128 | + f = newfunc(t_string); |
| 129 | + f->type = AWKFUNC; |
| 130 | + f->x.body.first = NULL; |
| 131 | + f->nargs = 0; |
| 132 | + while (next_token(TC_VARIABLE | TC_SEQTERM) & TC_VARIABLE) { |
| 133 | + v = findvar(ahash, t_string); |
| 134 | + v->x.aidx = (f->nargs)++; |
| 135 | + |
| 136 | + if (next_token(TC_COMMA | TC_SEQTERM) & TC_SEQTERM) |
| 137 | + break; |
| 138 | + } |
| 139 | + seq = &(f->x.body); |
| 140 | + chain_group(); |
| 141 | + clear_array(ahash); |
| 142 | + } |
| 143 | + } |
| 144 | + if (initseq && initseq->first) |
| 145 | + tv = evaluate(initseq->first, tv); |
| 146 | + nvfree(tv); |
| 147 | +} |
| 148 | + |
| 149 | + |
| 150 | +/* include an awk file and run its BEGIN{} section */ |
| 151 | +static xhash *includes = NULL; |
| 152 | +static void include_file(const char *filename) |
| 153 | +{ |
| 154 | + char *s; |
| 155 | + var *v; |
| 156 | + int oldlnr = g_lineno; |
| 157 | + const char *oldprg = g_progname; |
| 158 | + |
| 159 | + if (!includes) |
| 160 | + includes = hash_init(); |
| 161 | + |
| 162 | + /* find out if the file has been included already */ |
| 163 | + v = findvar(includes, filename); |
| 164 | + if (istrue(v)) |
| 165 | + return; |
| 166 | + setvar_s(v, "1"); |
| 167 | + |
| 168 | + /* read include file */ |
| 169 | + s = get_file(filename); |
| 170 | + if (!s) { |
| 171 | + fprintf(stderr, "Could not open file.\n"); |
| 172 | + return; |
| 173 | + } |
| 174 | + g_lineno = 1; |
| 175 | + g_progname = xstrdup(filename); |
| 176 | + parse_include(s+1); |
| 177 | + free(s); |
| 178 | + g_lineno = oldlnr; |
| 179 | + g_progname = oldprg; |
| 180 | +} |
| 181 | + |
| 182 | +static var *include(var *res, var *args, int nargs) |
| 183 | +{ |
| 184 | + const char *s; |
| 185 | + |
| 186 | + nargs = nargs; /* shut up, gcc */ |
| 187 | + s = getvar_s(args); |
| 188 | + if (s && (strlen(s) > 0)) |
| 189 | + include_file(s); |
| 190 | + |
| 191 | + return res; |
| 192 | +} |
| 193 | + |
| 194 | +/* registers a global c function for the awk interpreter */ |
| 195 | +static void register_cfunc(const char *name, awk_cfunc cfunc, int nargs) |
| 196 | +{ |
| 197 | + func *f; |
| 198 | + |
| 199 | + f = newfunc(name); |
| 200 | + f->type = CFUNC; |
| 201 | + f->x.cfunc = cfunc; |
| 202 | + f->nargs = nargs; |
| 203 | +} |
| 204 | + |
| 205 | int awk_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
| 206 | int awk_main(int argc, char **argv) |
| 207 | { |
| 208 | @@ -3056,6 +3204,9 @@ int awk_main(int argc, char **argv) |
| 209 | *s1 = '='; |
| 210 | } |
| 211 | } |
| 212 | + |
| 213 | + register_cfunc("include", include, 1); |
| 214 | + |
| 215 | opt_complementary = "v::f::"; /* -v and -f can occur multiple times */ |
| 216 | opt = getopt32(argv, "F:v:f:W:", &opt_F, &list_v, &list_f, &opt_W); |
| 217 | argv += optind; |
| 218 | |