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