diff options
Diffstat (limited to 'Src/parse.c')
-rw-r--r-- | Src/parse.c | 2753 |
1 files changed, 2207 insertions, 546 deletions
diff --git a/Src/parse.c b/Src/parse.c index d42be2f2f..3ffed46d7 100644 --- a/Src/parse.c +++ b/Src/parse.c @@ -33,17 +33,17 @@ /* != 0 if we are about to read a command word */ /**/ -int incmdpos; +mod_export int incmdpos; /* != 0 if we are in the middle of a [[ ... ]] */ /**/ -int incond; +mod_export int incond; /* != 0 if we are after a redirection (for ctxtlex only) */ /**/ -int inredir; +mod_export int inredir; /* != 0 if we are about to read a case pattern */ @@ -65,107 +65,477 @@ int infor; /**/ struct heredocs *hdocs; -/* used in arrays of lists instead of NULL pointers */ - + +#define YYERROR(O) { tok = LEXERR; ecused = (O); return 0; } +#define YYERRORV(O) { tok = LEXERR; ecused = (O); return; } +#define COND_ERROR(X,Y) do { \ + zwarn(X,Y,0); \ + herrflush(); \ + if (noerrs != 2) \ + errflag = 1; \ + YYERROR(ecused) \ +} while(0) + + +/* + * Word code. + * + * For now we simply post-process the syntax tree produced by the + * parser. We compile it into a struct eprog. Some day the parser + * above should be changed to emit the word code directly. + * + * Word code layout: + * + * WC_END + * - end of program code + * + * WC_LIST + * - data contains type (sync, ...) + * - follwed by code for this list + * - if not (type & Z_END), followed by next WC_LIST + * + * WC_SUBLIST + * - data contains type (&&, ||, END) and flags (coprog, not) + * - followed by code for sublist + * - if not (type == END), followed by next WC_SUBLIST + * + * WC_PIPE + * - data contains type (end, mid) and LINENO + * - if not (type == END), followed by offset to next WC_PIPE + * - followed by command + * - if not (type == END), followed by next WC_PIPE + * + * WC_REDIR + * - must precede command-code (or WC_ASSIGN) + * - data contains type (<, >, ...) + * - followed by fd1 and name from struct redir + * + * WC_ASSIGN + * - data contains type (scalar, array) and number of array-elements + * - followed by name and value + * + * WC_SIMPLE + * - data contains the number of arguments (plus command) + * - followed by strings + * + * WC_SUBSH + * - data unused + * - followed by list + * + * WC_CURSH + * - data unused + * - followed by list + * + * WC_TIMED + * - data contains type (followed by pipe or not) + * - if (type == PIPE), followed by pipe + * + * WC_FUNCDEF + * - data contains offset to after body + * - followed by number of names + * - followed by names + * - followed by offset to first string + * - followed by length of string table + * - followed by number of patterns for body + * - follwoed by codes for body + * - followed by strings for body + * + * WC_FOR + * - data contains type (list, ...) and offset to after body + * - if (type == COND), followed by init, cond, advance expressions + * - else if (type == PPARAM), followed by param name + * - else if (type == LIST), followed by param name, num strings, strings + * - followed by body + * + * WC_SELECT + * - data contains type (list, ...) and offset to after body + * - if (type == PPARAM), followed by param name + * - else if (type == LIST), followed by param name, num strings, strings + * - followed by body + * + * WC_WHILE + * - data contains type (while, until) and ofsset to after body + * - followed by condition + * - followed by body + * + * WC_REPEAT + * - data contains offset to after body + * - followed by number-string + * - followed by body + * + * WC_CASE + * - first CASE is always of type HEAD, data contains offset to esac + * - after that CASEs of type OR (;;) and AND (;&), data is offset to + * next case + * - each OR/AND case is followed by pattern, pattern-number, list + * + * WC_IF + * - first IF is of type HEAD, data contains offset to fi + * - after that IFs of type IF, ELIF, ELSE, data is offset to next + * - each non-HEAD is followed by condition (only IF, ELIF) and body + * + * WC_COND + * - data contains type + * - if (type == AND/OR), data contains offset to after this one, + * followed by two CONDs + * - else if (type == NOT), followed by COND + * - else if (type == MOD), followed by name and strings + * - else if (type == MODI), followed by name, left, right + * - else if (type == STR[N]EQ), followed by left, right, pattern-number + * - else if (has two args) followed by left, right + * - else followed by string + * + * WC_ARITH + * - followed by string (there's only one) + * + * WC_AUTOFN + * - only used by the autoload builtin + * + * Lists and sublists may also be simplified, indicated by the presence + * of the Z_SIMPLE or WC_SUBLIST_SIMPLE flags. In this case they are only + * followed by a slot containing the line number, not by a WC_SUBLIST or + * WC_PIPE, respectively. The real advantage of simplified lists and + * sublists is that they can be executed faster, see exec.c. In the + * parser, the test if a list can be simplified is done quite simply + * by passing a int* around which gets set to non-zero if the thing + * just parsed is `complex', i.e. may need to be run by forking or + * some such. + * + * In each of the above, strings are encoded as one word code. For empty + * strings this is the bit pattern 11x, the lowest bit is non-zero if the + * string contains tokens and zero otherwise (this is true for the other + * ways to encode strings, too). For short strings (one to three + * characters), this is the marker 01x with the 24 bits above that + * containing the characters. Longer strings are encoded as the offset + * into the strs character array stored in the eprog struct shifted by + * two and ored with the bit pattern 0x. + * The ecstr() function that adds the code for a string uses a simple + * list of strings already added so that long strings are encoded only + * once. + * + * Note also that in the eprog struct the pattern, code, and string + * arrays all point to the same memory block. + * + * + * To make things even faster in future versions, we could not only + * test if the strings contain tokens, but instead what kind of + * expansions need to be done on strings. In the execution code we + * could then use these flags for a specialized version of prefork() + * to avoid a lot of string parsing and some more string duplication. + */ + +/**/ +int eclen, ecused, ecnpats; +/**/ +Wordcode ecbuf; +/**/ +Eccstr ecstrs; /**/ -struct list dummy_list; +int ecsoffs, ecssub, ecnfunc; -#define YYERROR { tok = LEXERR; return NULL; } -#define YYERRORV { tok = LEXERR; return; } -#define COND_ERROR(X,Y) do{herrflush();zerr(X,Y,0);YYERROR}while(0) +/* Adjust pointers in here-doc structs. */ -#define make_list() allocnode(N_LIST) -#define make_sublist() allocnode(N_SUBLIST) -#define make_pline() allocnode(N_PLINE) -#define make_cmd() allocnode(N_CMD) -#define make_forcmd() allocnode(N_FOR) -#define make_casecmd() allocnode(N_CASE) -#define make_ifcmd() allocnode(N_IF) -#define make_whilecmd() allocnode(N_WHILE) -#define make_varnode() allocnode(N_VARASG) -#define make_cond() allocnode(N_COND) +static void +ecadjusthere(int p, int d) +{ + struct heredocs *h; + + for (h = hdocs; h; h = h->next) + if (h->pc >= p) + h->pc += d; +} + +/* Insert n free code-slots at position p. */ + +static void +ecispace(int p, int n) +{ + int m; + + if ((eclen - ecused) < n) { + int a = (n > 256 ? n : 256); + + ecbuf = (Wordcode) hrealloc((char *) ecbuf, eclen * sizeof(wordcode), + (eclen + a) * sizeof(wordcode)); + eclen += a; + } + if ((m = ecused - p) > 0) + memmove(ecbuf + p + n, ecbuf + p, m * sizeof(wordcode)); + ecused += n; + ecadjusthere(p, n); +} + +/* Add one wordcode. */ + +static int +ecadd(wordcode c) +{ + if ((eclen - ecused) < 1) { + ecbuf = (Wordcode) hrealloc((char *) ecbuf, eclen * sizeof(wordcode), + (eclen + 256) * sizeof(wordcode)); + eclen += 256; + } + ecbuf[ecused] = c; + ecused++; + + return ecused - 1; +} + +/* Delete a wordcode. */ + +static void +ecdel(int p) +{ + int n = ecused - p - 1; + + if (n > 0) + memmove(ecbuf + p, ecbuf + p + 1, n * sizeof(wordcode)); + ecused--; + ecadjusthere(p, -1); +} + +/* Build the wordcode for a string. */ + +static wordcode +ecstrcode(char *s) +{ + int l, t = has_token(s); + + if ((l = strlen(s) + 1) && l <= 4) { + wordcode c = (t ? 3 : 2); + switch (l) { + case 4: c |= ((wordcode) STOUC(s[2])) << 19; + case 3: c |= ((wordcode) STOUC(s[1])) << 11; + case 2: c |= ((wordcode) STOUC(s[0])) << 3; break; + case 1: c = (t ? 7 : 6); break; + } + return c; + } else { + Eccstr p, q = NULL; + + for (p = ecstrs; p; q = p, p = p->next) + if (p->nfunc == ecnfunc && !strcmp(s, p->str)) + return p->offs; + + p = (Eccstr) zhalloc(sizeof(*p)); + p->next = NULL; + if (q) + q->next = p; + else + ecstrs = p; + p->offs = ((ecsoffs - ecssub) << 2) | (t ? 1 : 0); + p->str = s; + p->nfunc = ecnfunc; + ecsoffs += l; + + return p->offs; + } +} + +static int +ecstr(char *s) +{ + return ecadd(ecstrcode(s)); +} + + +#define par_save_list(C) \ + do { \ + int eu = ecused; \ + par_list(C); \ + if (eu == ecused) ecadd(WCB_END()); \ + } while (0) +#define par_save_list1(C) \ + do { \ + int eu = ecused; \ + par_list1(C); \ + if (eu == ecused) ecadd(WCB_END()); \ + } while (0) + + +/* Initialise wordcode buffer. */ + +static void +init_parse(void) +{ + ecbuf = (Wordcode) zhalloc((eclen = 256) * sizeof(wordcode)); + ecused = 0; + ecstrs = NULL; + ecsoffs = ecnpats = 0; + ecssub = 0; + ecnfunc = 0; +} + +/* Build eprog. */ + +static Eprog +bld_eprog(void) +{ + Eprog ret; + Eccstr p; + char *q; + int l; + + ecadd(WCB_END()); + + ret = (Eprog) zhalloc(sizeof(*ret)); + ret->len = ((ecnpats * sizeof(Patprog)) + + (ecused * sizeof(wordcode)) + + ecsoffs); + ret->npats = ecnpats; + ret->pats = (Patprog *) zhalloc(ret->len); + ret->prog = (Wordcode) (ret->pats + ecnpats); + ret->strs = (char *) (ret->prog + ecused); + ret->shf = NULL; + ret->flags = EF_HEAP; + ret->dump = NULL; + for (l = 0; l < ecnpats; l++) + ret->pats[l] = dummy_patprog1; + memcpy(ret->prog, ecbuf, ecused * sizeof(wordcode)); + for (p = ecstrs, q = ret->strs; p; p = p->next, q += l) { + l = strlen(p->str) + 1; + memcpy(q, p->str, l); + } + return ret; +} /* * event : ENDINPUT * | SEPER * | sublist [ SEPER | AMPER | AMPERBANG ] */ + /**/ -List +Eprog parse_event(void) { tok = ENDINPUT; incmdpos = 1; yylex(); - return par_event(); + init_parse(); + return ((par_event()) ? bld_eprog() : NULL); } /**/ -static List +static int par_event(void) { - Sublist sl; - List l = NULL; + int r = 0, p, c = 0; while (tok == SEPER) { if (isnewlin > 0) - return NULL; + return 0; yylex(); } if (tok == ENDINPUT) - return NULL; - if ((sl = par_sublist())) + return 0; + + p = ecadd(0); + + if (par_sublist(&c)) { if (tok == ENDINPUT) { - l = (List) make_list(); - l->type = Z_SYNC; - l->left = sl; + set_list_code(p, Z_SYNC, c); + r = 1; } else if (tok == SEPER) { - l = (List) make_list(); - l->type = Z_SYNC; - l->left = sl; + set_list_code(p, Z_SYNC, c); if (isnewlin <= 0) yylex(); + r = 1; } else if (tok == AMPER) { - l = (List) make_list(); - l->type = Z_ASYNC; - l->left = sl; + set_list_code(p, Z_ASYNC, c); yylex(); + r = 1; } else if (tok == AMPERBANG) { - l = (List) make_list(); - l->type = Z_ASYNC | Z_DISOWN; - l->left = sl; + set_list_code(p, (Z_ASYNC | Z_DISOWN), c); yylex(); - } else - l = NULL; - if (!l) { + r = 1; + } + } + if (!r) { if (errflag) { - yyerror(); - return NULL; + yyerror(0); + ecused--; + return 0; } + yyerror(1); herrflush(); - yyerror(); - return NULL; + if (noerrs != 2) + errflag = 1; + ecused--; + return 0; } else { - l->right = par_event(); + int oec = ecused; + + par_event(); + if (ecused == oec) + ecbuf[p] |= wc_bdata(Z_END); } - return l; + return 1; } /**/ -List +mod_export Eprog parse_list(void) { - List ret; + int c = 0; tok = ENDINPUT; incmdpos = 1; yylex(); - ret = par_list(); - if (tok == LEXERR) { - yyerror(); + init_parse(); + par_list(&c); +#if 0 + if (tok == LEXERR) +#endif + if (tok != ENDINPUT) { + yyerror(0); return NULL; } - return ret; + return bld_eprog(); +} + +/**/ +mod_export Eprog +parse_cond(void) +{ + init_parse(); + + if (!par_cond()) + return NULL; + + return bld_eprog(); +} + +/* This adds a list wordcode. The important bit about this is that it also + * tries to optimise this to a Z_SIMPLE list code. */ + +/**/ +static void +set_list_code(int p, int type, int complex) +{ + if (!complex && (type == Z_SYNC || type == (Z_SYNC | Z_END)) && + WC_SUBLIST_TYPE(ecbuf[p + 1]) == WC_SUBLIST_END) { + int ispipe = !(WC_SUBLIST_FLAGS(ecbuf[p + 1]) & WC_SUBLIST_SIMPLE); + ecbuf[p] = WCB_LIST((type | Z_SIMPLE), ecused - 2 - p); + ecdel(p + 1); + if (ispipe) + ecbuf[p + 1] = WC_PIPE_LINENO(ecbuf[p + 1]); + } else + ecbuf[p] = WCB_LIST(type, 0); +} + +/* The same for sublists. */ + +/**/ +static void +set_sublist_code(int p, int type, int flags, int skip, int complex) +{ + if (complex) + ecbuf[p] = WCB_SUBLIST(type, flags, skip); + else { + ecbuf[p] = WCB_SUBLIST(type, (flags | WC_SUBLIST_SIMPLE), skip); + ecbuf[p + 1] = WC_PIPE_LINENO(ecbuf[p + 1]); + } } /* @@ -173,46 +543,60 @@ parse_list(void) */ /**/ -static List -par_list(void) +static int +par_list(int *complex) { - Sublist sl; - List l = NULL; + int p, lp = -1, c; + + rec: while (tok == SEPER) yylex(); - if ((sl = par_sublist())) + + p = ecadd(0); + c = 0; + + if (par_sublist(&c)) { + *complex |= c; if (tok == SEPER || tok == AMPER || tok == AMPERBANG) { - l = (List) make_list(); - l->left = sl; - l->type = (tok == SEPER) ? Z_SYNC : - (tok == AMPER) ? Z_ASYNC : Z_ASYNC | Z_DISOWN; + if (tok != SEPER) + *complex = 1; + set_list_code(p, ((tok == SEPER) ? Z_SYNC : + (tok == AMPER) ? Z_ASYNC : + (Z_ASYNC | Z_DISOWN)), c); incmdpos = 1; do { yylex(); } while (tok == SEPER); - l->right = par_list(); - } else { - l = (List) make_list(); - l->left = sl; - l->type = Z_SYNC; + lp = p; + goto rec; + } else + set_list_code(p, (Z_SYNC | Z_END), c); + return 1; + } else { + ecused--; + if (lp >= 0) { + ecbuf[lp] |= wc_bdata(Z_END); + return 1; } - return l; + return 0; + } } /**/ -static List -par_list1(void) +static int +par_list1(int *complex) { - Sublist sl; - List l = NULL; + int p = ecadd(0), c = 0; - if ((sl = par_sublist())) { - l = (List) make_list(); - l->type = Z_SYNC; - l->left = sl; + if (par_sublist(&c)) { + set_list_code(p, (Z_SYNC | Z_END), c); + *complex |= c; + return 1; + } else { + ecused--; + return 0; } - return l; } /* @@ -220,24 +604,37 @@ par_list1(void) */ /**/ -static Sublist -par_sublist(void) +static int +par_sublist(int *complex) { - Sublist sl; + int f, p, c = 0; - if ((sl = par_sublist2())) + p = ecadd(0); + + if ((f = par_sublist2(&c)) != -1) { + int e = ecused; + + *complex |= c; if (tok == DBAR || tok == DAMPER) { - int qtok = tok; + int qtok = tok, sl; cmdpush(tok == DBAR ? CS_CMDOR : CS_CMDAND); yylex(); while (tok == SEPER) yylex(); - sl->right = par_sublist(); - sl->type = (qtok == DBAR) ? ORNEXT : ANDNEXT; + sl = par_sublist(complex); + set_sublist_code(p, (sl ? (qtok == DBAR ? + WC_SUBLIST_OR : WC_SUBLIST_AND) : + WC_SUBLIST_END), + f, (e - 1 - p), c); cmdpop(); - } - return sl; + } else + set_sublist_code(p, WC_SUBLIST_END, f, (e - 1 - p), c); + return 1; + } else { + ecused--; + return 0; + } } /* @@ -245,24 +642,24 @@ par_sublist(void) */ /**/ -static Sublist -par_sublist2(void) +static int +par_sublist2(int *complex) { - Sublist sl; - Pline p; + int f = 0; - sl = (Sublist) make_sublist(); if (tok == COPROC) { - sl->flags |= PFLAG_COPROC; + *complex = 1; + f |= WC_SUBLIST_COPROC; yylex(); } else if (tok == BANG) { - sl->flags |= PFLAG_NOT; + *complex = 1; + f |= WC_SUBLIST_NOT; yylex(); } - if (!(p = par_pline()) && !sl->flags) - return NULL; - sl->left = p; - return sl; + if (!par_pline(complex) && !f) + return -1; + + return f; } /* @@ -270,48 +667,53 @@ par_sublist2(void) */ /**/ -static Pline -par_pline(void) +static int +par_pline(int *complex) { - Cmd c; - Pline p, p2; + int p, line = lineno; - if (!(c = par_cmd())) - return NULL; + p = ecadd(0); + + if (!par_cmd(complex)) { + ecused--; + return 0; + } if (tok == BAR) { + *complex = 1; cmdpush(CS_PIPE); yylex(); while (tok == SEPER) yylex(); - p2 = par_pline(); + ecbuf[p] = WCB_PIPE(WC_PIPE_MID, (line >= 0 ? line + 1 : 0)); + ecispace(p + 1, 1); + ecbuf[p + 1] = ecused - 1 - p; + par_pline(complex); cmdpop(); - p = (Pline) make_pline(); - p->left = c; - p->right = p2; - p->type = PIPE; - return p; + return 1; } else if (tok == BARAMP) { - struct redir *rdr = (struct redir *)allocnode(N_REDIR); + int r; + + for (r = p + 1; wc_code(ecbuf[r]) == WC_REDIR; r += 3); - rdr->type = MERGEOUT; - rdr->fd1 = 2; - rdr->name = dupstring("1"); - addlinknode(c->redir, rdr); + ecispace(r, 3); + ecbuf[r] = WCB_REDIR(MERGEOUT); + ecbuf[r + 1] = 2; + ecbuf[r + 2] = ecstrcode("1"); + *complex = 1; cmdpush(CS_ERRPIPE); yylex(); - p2 = par_pline(); + while (tok == SEPER) + yylex(); + ecbuf[p] = WCB_PIPE(WC_PIPE_MID, (line >= 0 ? line + 1 : 0)); + ecispace(p + 1, 1); + ecbuf[p + 1] = ecused - 1 - p; + par_pline(complex); cmdpop(); - p = (Pline) make_pline(); - p->left = c; - p->right = p2; - p->type = PIPE; - return p; + return 1; } else { - p = (Pline) make_pline(); - p->left = c; - p->type = END; - return p; + ecbuf[p] = WCB_PIPE(WC_PIPE_END, (line >= 0 ? line + 1 : 0)); + return 1; } } @@ -321,96 +723,116 @@ par_pline(void) */ /**/ -static Cmd -par_cmd(void) +static int +par_cmd(int *complex) { - Cmd c; + int r, nr = 0; + + r = ecused; - c = (Cmd) make_cmd(); - c->lineno = lineno; - c->args = newlinklist(); - c->redir = newlinklist(); - c->vars = newlinklist(); - while (IS_REDIROP(tok)) - par_redir(c->redir); + if (IS_REDIROP(tok)) { + *complex = 1; + while (IS_REDIROP(tok)) { + nr++; + par_redir(&r); + } + } switch (tok) { case FOR: cmdpush(CS_FOR); - par_for(c); + par_for(complex); cmdpop(); break; case FOREACH: cmdpush(CS_FOREACH); - par_for(c); + par_for(complex); cmdpop(); break; case SELECT: + *complex = 1; cmdpush(CS_SELECT); - par_for(c); + par_for(complex); cmdpop(); break; case CASE: cmdpush(CS_CASE); - par_case(c); + par_case(complex); cmdpop(); break; case IF: - par_if(c); + par_if(complex); break; case WHILE: cmdpush(CS_WHILE); - par_while(c); + par_while(complex); cmdpop(); break; case UNTIL: cmdpush(CS_UNTIL); - par_while(c); + par_while(complex); cmdpop(); break; case REPEAT: cmdpush(CS_REPEAT); - par_repeat(c); + par_repeat(complex); cmdpop(); break; case INPAR: + *complex = 1; cmdpush(CS_SUBSH); - par_subsh(c); + par_subsh(complex); cmdpop(); break; case INBRACE: cmdpush(CS_CURSH); - par_subsh(c); + par_subsh(complex); cmdpop(); break; case FUNC: cmdpush(CS_FUNCDEF); - par_funcdef(c); + par_funcdef(); cmdpop(); break; case TIME: - par_time(c); + *complex = 1; + par_time(); break; case DINBRACK: cmdpush(CS_COND); - par_dinbrack(c); + par_dinbrack(); cmdpop(); break; case DINPAR: - c->type = CARITH; - addlinknode(c->args, tokstr); + ecadd(WCB_ARITH()); + ecstr(tokstr); yylex(); break; default: - if (!par_simple(c)) - return NULL; + { + int sr; + + if (!(sr = par_simple(complex, nr))) { + if (!nr) + return 0; + } else { + /* Three codes per redirection. */ + if (sr > 1) { + *complex = 1; + r += (sr - 1) * 3; + } + } + } break; } - while (IS_REDIROP(tok)) - par_redir(c->redir); + if (IS_REDIROP(tok)) { + *complex = 1; + while (IS_REDIROP(tok)) + par_redir(&r); + } incmdpos = 1; incasepat = 0; incond = 0; - return c; + return 1; } /* @@ -421,82 +843,95 @@ par_cmd(void) /**/ static void -par_for(Cmd c) +par_for(int *complex) { - Forcmd f; - int csh = (tok == FOREACH); + int oecused = ecused, csh = (tok == FOREACH), p, sel = (tok == SELECT); + int type; + + p = ecadd(0); - f = (Forcmd) make_forcmd(); - c->type = (tok == SELECT) ? CSELECT : CFOR; incmdpos = 0; infor = tok == FOR ? 2 : 0; yylex(); if (tok == DINPAR) { yylex(); if (tok != DINPAR) - YYERRORV; - f->name = tokstr; + YYERRORV(oecused); + ecstr(tokstr); yylex(); if (tok != DINPAR) - YYERRORV; - f->condition = tokstr; + YYERRORV(oecused); + ecstr(tokstr); yylex(); if (tok != DOUTPAR) - YYERRORV; - f->advance = tokstr; + YYERRORV(oecused); + ecstr(tokstr); infor = 0; incmdpos = 1; yylex(); + type = WC_FOR_COND; } else { infor = 0; if (tok != STRING || !isident(tokstr)) - YYERRORV; - f->name = tokstr; + YYERRORV(oecused); + ecstr(tokstr); incmdpos = 1; yylex(); if (tok == STRING && !strcmp(tokstr, "in")) { - f->inflag = 1; + int np, n; + incmdpos = 0; yylex(); - c->args = par_wordlist(); + np = ecadd(0); + n = par_wordlist(); if (tok != SEPER) - YYERRORV; + YYERRORV(oecused); + ecbuf[np] = n; + type = (sel ? WC_SELECT_LIST : WC_FOR_LIST); } else if (tok == INPAR) { - f->inflag = 1; + int np, n; + incmdpos = 0; yylex(); - c->args = par_nl_wordlist(); + np = ecadd(0); + n = par_nl_wordlist(); if (tok != OUTPAR) - YYERRORV; + YYERRORV(oecused); + ecbuf[np] = n; incmdpos = 1; yylex(); - } + type = (sel ? WC_SELECT_LIST : WC_FOR_LIST); + } else + type = (sel ? WC_SELECT_PPARAM : WC_FOR_PPARAM); } incmdpos = 1; while (tok == SEPER) yylex(); if (tok == DO) { yylex(); - f->list = par_list(); + par_save_list(complex); if (tok != DONE) - YYERRORV; + YYERRORV(oecused); yylex(); } else if (tok == INBRACE) { yylex(); - f->list = par_list(); + par_save_list(complex); if (tok != OUTBRACE) - YYERRORV; + YYERRORV(oecused); yylex(); } else if (csh || isset(CSHJUNKIELOOPS)) { - f->list = par_list(); + par_save_list(complex); if (tok != ZEND) - YYERRORV; + YYERRORV(oecused); yylex(); } else if (unset(SHORTLOOPS)) { - YYERRORV; + YYERRORV(oecused); } else - f->list = par_list1(); - c->u.forcmd = f; + par_save_list1(complex); + + ecbuf[p] = (sel ? + WCB_SELECT(type, ecused - 1 - p) : + WCB_FOR(type, ecused - 1 - p)); } /* @@ -508,35 +943,29 @@ par_for(Cmd c) /**/ static void -par_case(Cmd c) +par_case(int *complex) { - int brflag; - LinkList pats, lists; - int n = 1; - char **pp; - List *ll; - LinkNode no; - struct casecmd *cc; + int oecused = ecused, brflag, p, pp, n = 1, type; + + p = ecadd(0); - c->type = CCASE; incmdpos = 0; yylex(); if (tok != STRING) - YYERRORV; - pats = newlinklist(); - addlinknode(pats, tokstr); + YYERRORV(oecused); + ecstr(tokstr); + incmdpos = 1; yylex(); while (tok == SEPER) yylex(); if (!(tok == STRING && !strcmp(tokstr, "in")) && tok != INBRACE) - YYERRORV; + YYERRORV(oecused); brflag = (tok == INBRACE); incasepat = 1; incmdpos = 0; yylex(); - cc = c->u.casecmd = (struct casecmd *)make_casecmd(); - lists = newlinklist(); + for (;;) { char *str; @@ -545,14 +974,13 @@ par_case(Cmd c) if (tok == OUTBRACE) break; if (tok != STRING) - YYERRORV; + YYERRORV(oecused); if (!strcmp(tokstr, "esac")) break; - str = ncalloc(strlen(tokstr) + 2); - *str = ';'; - strcpy(str + 1, tokstr); + str = dupstring(tokstr); incasepat = 0; incmdpos = 1; + type = WC_CASE_OR; for (;;) { yylex(); if (tok == OUTPAR) { @@ -566,7 +994,7 @@ par_case(Cmd c) incasepat = 1; incmdpos = 0; - str2 = ncalloc(sl + 2); + str2 = hcalloc(sl + 2); strcpy(str2, str); str2[sl] = Bar; str2[sl+1] = '\0'; @@ -574,12 +1002,12 @@ par_case(Cmd c) } else { int sl = strlen(str); - if (str[sl - 1] != Bar) { + if (!sl || str[sl - 1] != Bar) { /* POSIX allows (foo*) patterns */ int pct; char *s; - for (s = str + 1, pct = 0; *s; s++) { + for (s = str, pct = 0; *s; s++) { if (*s == Inpar) pct++; if (!pct) @@ -590,54 +1018,53 @@ par_case(Cmd c) chuck(s+1); if (*s == Bar || *s == Outpar) while (iblank(s[-1]) && - (s < str+2 || s[-2] != Meta)) + (s < str + 1 || s[-2] != Meta)) chuck(--s); } if (*s == Outpar) pct--; } - if (*s || pct || s == str + 1) - YYERRORV; + if (*s || pct || s == str) + YYERRORV(oecused); + /* Simplify pattern by removing surrounding (...) */ + sl = strlen(str); + DPUTS(*str != Inpar || str[sl - 1] != Outpar, + "BUG: strange case pattern"); + str[sl - 1] = '\0'; + chuck(str); break; } else { char *str2; if (tok != STRING) - YYERRORV; - str2 = ncalloc(sl + strlen(tokstr) + 1); + YYERRORV(oecused); + str2 = hcalloc(sl + strlen(tokstr) + 1); strcpy(str2, str); strcpy(str2 + sl, tokstr); str = str2; } } } - addlinknode(pats, str); - addlinknode(lists, par_list()); + pp = ecadd(0); + ecstr(str); + ecadd(ecnpats++); + par_save_list(complex); n++; + if (tok == SEMIAMP) + type = WC_CASE_AND; + ecbuf[pp] = WCB_CASE(type, ecused - 1 - pp); if ((tok == ESAC && !brflag) || (tok == OUTBRACE && brflag)) break; - if(tok == SEMIAMP) - *str = '&'; - else if (tok != DSEMI) - YYERRORV; + if (tok != DSEMI && tok != SEMIAMP) + YYERRORV(oecused); incasepat = 1; incmdpos = 0; yylex(); } - incmdpos = 1; yylex(); - cc->pats = (char **)alloc((n + 1) * sizeof(char *)); - - for (pp = cc->pats, no = firstnode(pats); no; incnode(no)) - *pp++ = (char *)getdata(no); - *pp = NULL; - cc->lists = (List *) alloc((n + 1) * sizeof(List)); - for (ll = cc->lists, no = firstnode(lists); no; incnode(no), ll++) - if (!(*ll = (List) getdata(no))) - *ll = &dummy_list; - *ll = NULL; + ecbuf[p] = WCB_CASE(WC_CASE_HEAD, ecused - 1 - p); } /* @@ -649,20 +1076,13 @@ par_case(Cmd c) /**/ static void -par_if(Cmd c) +par_if(int *complex) { - struct ifcmd *i; - int xtok; + int oecused = ecused, xtok, p, pp, type, usebrace = 0; unsigned char nc; - LinkList ifsl, thensl; - LinkNode no; - int ni = 0, nt = 0, usebrace = 0; - List l, *ll; - ifsl = newlinklist(); - thensl = newlinklist(); + p = ecadd(0); - c->type = CIF; for (;;) { xtok = tok; cmdpush(xtok == IF ? CS_IF : CS_ELIF); @@ -675,10 +1095,11 @@ par_if(Cmd c) yylex(); if (!(xtok == IF || xtok == ELIF)) { cmdpop(); - YYERRORV; + YYERRORV(oecused); } - addlinknode(ifsl, par_list()); - ni++; + pp = ecadd(0); + type = (xtok == IF ? WC_IF_IF : WC_IF_ELIF); + par_save_list(complex); incmdpos = 1; while (tok == SEPER) yylex(); @@ -689,79 +1110,63 @@ par_if(Cmd c) cmdpop(); cmdpush(nc); yylex(); - addlinknode(thensl, par_list()); - nt++; + par_save_list(complex); + ecbuf[pp] = WCB_IF(type, ecused - 1 - pp); incmdpos = 1; cmdpop(); - } else { - if (tok == INBRACE) { - usebrace = 1; - cmdpop(); - cmdpush(nc); - yylex(); - l = par_list(); - if (tok != OUTBRACE) { - cmdpop(); - YYERRORV; - } - addlinknode(thensl, l); - nt++; - yylex(); - incmdpos = 1; - if (tok == SEPER) - break; - cmdpop(); - } else if (unset(SHORTLOOPS)) { - cmdpop(); - YYERRORV; - } else { + } else if (tok == INBRACE) { + usebrace = 1; + cmdpop(); + cmdpush(nc); + yylex(); + par_save_list(complex); + if (tok != OUTBRACE) { cmdpop(); - cmdpush(nc); - addlinknode(thensl, par_list1()); - nt++; - incmdpos = 1; - break; + YYERRORV(oecused); } + ecbuf[pp] = WCB_IF(type, ecused - 1 - pp); + yylex(); + incmdpos = 1; + if (tok == SEPER) + break; + cmdpop(); + } else if (unset(SHORTLOOPS)) { + cmdpop(); + YYERRORV(oecused); + } else { + cmdpop(); + cmdpush(nc); + par_save_list1(complex); + ecbuf[pp] = WCB_IF(type, ecused - 1 - pp); + incmdpos = 1; + break; } } cmdpop(); if (xtok == ELSE) { + pp = ecadd(0); cmdpush(CS_ELSE); while (tok == SEPER) yylex(); if (tok == INBRACE && usebrace) { yylex(); - l = par_list(); + par_save_list(complex); if (tok != OUTBRACE) { cmdpop(); - YYERRORV; + YYERRORV(oecused); } } else { - l = par_list(); + par_save_list(complex); if (tok != FI) { cmdpop(); - YYERRORV; + YYERRORV(oecused); } } - addlinknode(thensl, l); - nt++; + ecbuf[pp] = WCB_IF(WC_IF_ELSE, ecused - 1 - pp); yylex(); cmdpop(); } - i = (struct ifcmd *)make_ifcmd(); - i->ifls = (List *) alloc((ni + 1) * sizeof(List)); - i->thenls = (List *) alloc((nt + 1) * sizeof(List)); - - for (ll = i->ifls, no = firstnode(ifsl); no; incnode(no), ll++) - if (!(*ll = (List) getdata(no))) - *ll = &dummy_list; - *ll = NULL; - for (ll = i->thenls, no = firstnode(thensl); no; incnode(no), ll++) - if (!(*ll = (List) getdata(no))) - *ll = &dummy_list; - *ll = NULL; - - c->u.ifcmd = i; + ecbuf[p] = WCB_IF(WC_IF_HEAD, ecused - 1 - p); } /* @@ -771,37 +1176,38 @@ par_if(Cmd c) /**/ static void -par_while(Cmd c) +par_while(int *complex) { - struct whilecmd *w; + int oecused = ecused, p; + int type = (tok == UNTIL ? WC_WHILE_UNTIL : WC_WHILE_WHILE); - c->type = CWHILE; - w = c->u.whilecmd = (struct whilecmd *)make_whilecmd(); - w->cond = (tok == UNTIL); + p = ecadd(0); yylex(); - w->cont = par_list(); + par_save_list(complex); incmdpos = 1; while (tok == SEPER) yylex(); if (tok == DO) { yylex(); - w->loop = par_list(); + par_save_list(complex); if (tok != DONE) - YYERRORV; + YYERRORV(oecused); yylex(); } else if (tok == INBRACE) { yylex(); - w->loop = par_list(); + par_save_list(complex); if (tok != OUTBRACE) - YYERRORV; + YYERRORV(oecused); yylex(); } else if (isset(CSHJUNKIELOOPS)) { - w->loop = par_list(); + par_save_list(complex); if (tok != ZEND) - YYERRORV; + YYERRORV(oecused); yylex(); } else - YYERRORV; + YYERRORV(oecused); + + ecbuf[p] = WCB_WHILE(type, ecused - 1 - p); } /* @@ -810,39 +1216,44 @@ par_while(Cmd c) /**/ static void -par_repeat(Cmd c) +par_repeat(int *complex) { - c->type = CREPEAT; + int oecused = ecused, p; + + p = ecadd(0); + incmdpos = 0; yylex(); if (tok != STRING) - YYERRORV; - addlinknode(c->args, tokstr); + YYERRORV(oecused); + ecstr(tokstr); incmdpos = 1; yylex(); while (tok == SEPER) yylex(); if (tok == DO) { yylex(); - c->u.list = par_list(); + par_save_list(complex); if (tok != DONE) - YYERRORV; + YYERRORV(oecused); yylex(); } else if (tok == INBRACE) { yylex(); - c->u.list = par_list(); + par_save_list(complex); if (tok != OUTBRACE) - YYERRORV; + YYERRORV(oecused); yylex(); } else if (isset(CSHJUNKIELOOPS)) { - c->u.list = par_list(); + par_save_list(complex); if (tok != ZEND) - YYERRORV; + YYERRORV(oecused); yylex(); } else if (unset(SHORTLOOPS)) { - YYERRORV; + YYERRORV(oecused); } else - c->u.list = par_list1(); + par_save_list1(complex); + + ecbuf[p] = WCB_REPEAT(ecused - 1 - p); } /* @@ -851,13 +1262,18 @@ par_repeat(Cmd c) /**/ static void -par_subsh(Cmd c) +par_subsh(int *complex) { - c->type = (tok == INPAR) ? SUBSH : CURSH; + int oecused = ecused, otok = tok, p; + + p = ecadd(0); yylex(); - c->u.list = par_list(); - if (tok != ((c->type == SUBSH) ? OUTPAR : OUTBRACE)) - YYERRORV; + par_list(complex); + ecadd(WCB_END()); + if (tok != ((otok == INPAR) ? OUTPAR : OUTBRACE)) + YYERRORV(oecused); + ecbuf[p] = (otok == INPAR ? WCB_SUBSH(ecused - 1 - p) : + WCB_CURSH(ecused - 1 - p)); incmdpos = 1; yylex(); } @@ -869,37 +1285,74 @@ par_subsh(Cmd c) /**/ static void -par_funcdef(Cmd c) +par_funcdef(void) { + int oecused = ecused, oldlineno = lineno, num = 0, onp, p, c = 0; + int so, oecssub = ecssub; + + lineno = 0; nocorrect = 1; incmdpos = 0; yylex(); - c->type = FUNCDEF; - c->args = newlinklist(); + + p = ecadd(0); + ecadd(0); + incmdpos = 1; while (tok == STRING) { if (*tokstr == Inbrace && !tokstr[1]) { tok = INBRACE; break; } - addlinknode(c->args, tokstr); + ecstr(tokstr); + num++; yylex(); } + ecadd(0); + ecadd(0); + ecadd(0); + nocorrect = 0; if (tok == INOUTPAR) yylex(); while (tok == SEPER) yylex(); + + ecnfunc++; + ecssub = so = ecsoffs; + onp = ecnpats; + ecnpats = 0; + if (tok == INBRACE) { yylex(); - c->u.list = par_list(); - if (tok != OUTBRACE) - YYERRORV; + par_list(&c); + if (tok != OUTBRACE) { + lineno += oldlineno; + ecnpats = onp; + ecssub = oecssub; + YYERRORV(oecused); + } yylex(); } else if (unset(SHORTLOOPS)) { - YYERRORV; + lineno += oldlineno; + ecnpats = onp; + ecssub = oecssub; + YYERRORV(oecused); } else - c->u.list = par_list1(); + par_list1(&c); + + ecadd(WCB_END()); + ecbuf[p + num + 2] = so - oecssub; + ecbuf[p + num + 3] = ecsoffs - so; + ecbuf[p + num + 4] = ecnpats; + ecbuf[p + 1] = num; + + lineno += oldlineno; + ecnpats = onp; + ecssub = oecssub; + ecnfunc++; + + ecbuf[p] = WCB_FUNCDEF(ecused - 1 - p); } /* @@ -908,11 +1361,17 @@ par_funcdef(Cmd c) /**/ static void -par_time(Cmd c) +par_time(void) { + int p, f, c = 0; + yylex(); - c->type = ZCTIME; - c->u.pline = par_sublist2(); + + p = ecadd(0); + ecadd(0); + f = par_sublist2(&c); + ecbuf[p] = WCB_TIMED((p + 1 == ecused) ? WC_TIMED_EMPTY : WC_TIMED_PIPE); + set_sublist_code(p + 1, WC_SUBLIST_END, f, ecused - 2 - p, c); } /* @@ -921,15 +1380,16 @@ par_time(Cmd c) /**/ static void -par_dinbrack(Cmd c) +par_dinbrack(void) { - c->type = COND; + int oecused = ecused; + incond = 1; incmdpos = 0; yylex(); - c->u.cond = par_cond(); + par_cond(); if (tok != DOUTBRACK) - YYERRORV; + YYERRORV(oecused); incond = 0; incmdpos = 1; yylex(); @@ -942,77 +1402,290 @@ par_dinbrack(Cmd c) */ /**/ -static Cmd -par_simple(Cmd c) +static int +par_simple(int *complex, int nr) { - int isnull = 1; + int oecused = ecused, isnull = 1, r, argc = 0, p, isfunc = 0, sr = 0; + int c = *complex; - c->type = SIMPLE; + r = ecused; for (;;) { - if (tok == NOCORRECT) + if (tok == NOCORRECT) { + *complex = c = 1; nocorrect = 1; - else if (tok == ENVSTRING) { - struct varasg *v = (struct varasg *)make_varnode(); - - v->type = PM_SCALAR; - equalsplit(v->name = tokstr, &v->str); - addlinknode(c->vars, v); + } else if (tok == ENVSTRING) { + char *p, *name, *str; + + ecadd(WCB_ASSIGN(WC_ASSIGN_SCALAR, 0)); + name = tokstr; + for (p = tokstr; *p && *p != Inbrack && *p != '='; p++); + if (*p == Inbrack && !skipparens(Inbrack, Outbrack, &p) && + *p == '=') { + *p = '\0'; + str = p + 1; + } else + equalsplit(tokstr, &str); + ecstr(name); + ecstr(str); isnull = 0; } else if (tok == ENVARRAY) { - struct varasg *v = (struct varasg *)make_varnode(); - int oldcmdpos = incmdpos; + int oldcmdpos = incmdpos, n; - v->type = PM_ARRAY; + p = ecadd(0); incmdpos = 0; - v->name = tokstr; + ecstr(tokstr); cmdpush(CS_ARRAY); yylex(); - v->arr = par_nl_wordlist(); + n = par_nl_wordlist(); + ecbuf[p] = WCB_ASSIGN(WC_ASSIGN_ARRAY, n); cmdpop(); if (tok != OUTPAR) - YYERROR; + YYERROR(oecused); incmdpos = oldcmdpos; - addlinknode(c->vars, v); isnull = 0; } else break; yylex(); } if (tok == AMPER || tok == AMPERBANG) - YYERROR; + YYERROR(oecused); + + p = ecadd(WCB_SIMPLE(0)); + for (;;) { if (tok == STRING) { + *complex = 1; incmdpos = 0; - addlinknode(c->args, tokstr); + ecstr(tokstr); + argc++; yylex(); } else if (IS_REDIROP(tok)) { - par_redir(c->redir); + *complex = c = 1; + par_redir(&r); + p += 3; /* 3 codes per redirection */ + sr++; } else if (tok == INOUTPAR) { + int oldlineno = lineno, onp, so, oecssub = ecssub; + + *complex = c; + lineno = 0; incmdpos = 1; cmdpush(CS_FUNCDEF); yylex(); while (tok == SEPER) yylex(); + + ecispace(p + 1, 1); + ecbuf[p + 1] = argc; + ecadd(0); + ecadd(0); + ecadd(0); + + ecnfunc++; + ecssub = so = ecsoffs; + onp = ecnpats; + ecnpats = 0; + if (tok == INBRACE) { + int c = 0; + yylex(); - c->u.list = par_list(); + par_list(&c); if (tok != OUTBRACE) { cmdpop(); - YYERROR; + lineno += oldlineno; + ecnpats = onp; + ecssub = oecssub; + YYERROR(oecused); } yylex(); - } else - c->u.list = (List) expandstruct((struct node *) par_cmd(), N_LIST); + } else { + int ll, sl, c = 0; + + ll = ecadd(0); + sl = ecadd(0); + + par_cmd(&c); + + set_sublist_code(sl, WC_SUBLIST_END, 0, ecused - 1 - sl, c); + set_list_code(ll, (Z_SYNC | Z_END), c); + } cmdpop(); - c->type = FUNCDEF; + + ecadd(WCB_END()); + ecbuf[p + argc + 2] = so - oecssub; + ecbuf[p + argc + 3] = ecsoffs - so; + ecbuf[p + argc + 4] = ecnpats; + + lineno += oldlineno; + ecnpats = onp; + ecssub = oecssub; + ecnfunc++; + + ecbuf[p] = WCB_FUNCDEF(ecused - 1 - p); + + isfunc = 1; } else break; isnull = 0; } - if (isnull && empty(c->redir)) - return NULL; + if (isnull && !(sr + nr)) { + ecused = p; + return 0; + } incmdpos = 1; - return c; + + if (!isfunc) + ecbuf[p] = WCB_SIMPLE(argc); + + return sr + 1; +} + +/* + * redir : ( OUTANG | ... | TRINANG ) STRING + */ + +static int redirtab[TRINANG - OUTANG + 1] = { + WRITE, + WRITENOW, + APP, + APPNOW, + READ, + READWRITE, + HEREDOC, + HEREDOCDASH, + MERGEIN, + MERGEOUT, + ERRWRITE, + ERRWRITENOW, + ERRAPP, + ERRAPPNOW, + HERESTR, +}; + +/**/ +static void +par_redir(int *rp) +{ + int r = *rp, type, fd1, oldcmdpos, oldnc; + char *name; + + oldcmdpos = incmdpos; + incmdpos = 0; + oldnc = nocorrect; + if (tok != INANG && tok != INOUTANG) + nocorrect = 1; + type = redirtab[tok - OUTANG]; + fd1 = tokfd; + yylex(); + if (tok != STRING && tok != ENVSTRING) + YYERRORV(ecused); + incmdpos = oldcmdpos; + nocorrect = oldnc; + + /* assign default fd */ + if (fd1 == -1) + fd1 = IS_READFD(type) ? 0 : 1; + + name = tokstr; + + switch (type) { + case HEREDOC: + case HEREDOCDASH: { + /* <<[-] name */ + struct heredocs **hd; + + /* If we ever need more than three codes (or less), we have to change + * the factors in par_cmd() and par_simple(), too. */ + ecispace(r, 3); + *rp = r + 3; + ecbuf[r] = WCB_REDIR(type); + ecbuf[r + 1] = fd1; + + for (hd = &hdocs; *hd; hd = &(*hd)->next); + *hd = zalloc(sizeof(struct heredocs)); + (*hd)->next = NULL; + (*hd)->type = type; + (*hd)->pc = r; + (*hd)->str = tokstr; + + yylex(); + return; + } + case WRITE: + case WRITENOW: + if (tokstr[0] == Outang && tokstr[1] == Inpar) + /* > >(...) */ + type = OUTPIPE; + else if (tokstr[0] == Inang && tokstr[1] == Inpar) + YYERRORV(ecused); + break; + case READ: + if (tokstr[0] == Inang && tokstr[1] == Inpar) + /* < <(...) */ + type = INPIPE; + else if (tokstr[0] == Outang && tokstr[1] == Inpar) + YYERRORV(ecused); + break; + case READWRITE: + if ((tokstr[0] == Inang || tokstr[0] == Outang) && tokstr[1] == Inpar) + type = tokstr[0] == Inang ? INPIPE : OUTPIPE; + break; + } + yylex(); + + /* If we ever need more than three codes (or less), we have to change + * the factors in par_cmd() and par_simple(), too. */ + ecispace(r, 3); + *rp = r + 3; + ecbuf[r] = WCB_REDIR(type); + ecbuf[r + 1] = fd1; + ecbuf[r + 2] = ecstrcode(name); +} + +/**/ +void +setheredoc(int pc, int type, char *str) +{ + ecbuf[pc] = WCB_REDIR(type); + ecbuf[pc + 2] = ecstrcode(str); +} + +/* + * wordlist : { STRING } + */ + +/**/ +static int +par_wordlist(void) +{ + int num = 0; + while (tok == STRING) { + ecstr(tokstr); + num++; + yylex(); + } + return num; +} + +/* + * nl_wordlist : { STRING | SEPER } + */ + +/**/ +static int +par_nl_wordlist(void) +{ + int num = 0; + + while (tok == STRING || tok == SEPER) { + if (tok != SEPER) { + ecstr(tokstr); + num++; + } + yylex(); + } + return num; } /* @@ -1028,25 +1701,24 @@ void (*condlex) _((void)) = yylex; */ /**/ -Cond +static int par_cond(void) { - Cond c, c2; + int p = ecused, r; - c = par_cond_1(); + r = par_cond_1(); while (tok == SEPER) condlex(); if (tok == DBAR) { condlex(); while (tok == SEPER) condlex(); - c2 = (Cond) make_cond(); - c2->left = (void *) c; - c2->right = (void *) par_cond(); - c2->type = COND_OR; - return c2; + ecispace(p, 1); + par_cond(); + ecbuf[p] = WCB_COND(COND_OR, ecused - 1 - p); + return 1; } - return c; + return r; } /* @@ -1054,25 +1726,24 @@ par_cond(void) */ /**/ -static Cond +static int par_cond_1(void) { - Cond c, c2; + int r, p = ecused; - c = par_cond_2(); + r = par_cond_2(); while (tok == SEPER) condlex(); if (tok == DAMPER) { condlex(); while (tok == SEPER) condlex(); - c2 = (Cond) make_cond(); - c2->left = (void *) c; - c2->right = (void *) par_cond_1(); - c2->type = COND_AND; - return c2; + ecispace(p, 1); + par_cond_1(); + ecbuf[p] = WCB_COND(COND_AND, ecused - 1 - p); + return 1; } - return c; + return r; } /* @@ -1084,10 +1755,9 @@ par_cond_1(void) */ /**/ -static Cond +static int par_cond_2(void) { - Cond c, c2; char *s1, *s2, *s3; int dble = 0; @@ -1121,31 +1791,31 @@ par_cond_2(void) } if (tok == BANG) { condlex(); - c = par_cond_2(); - c2 = (Cond) make_cond(); - c2->left = (void *) c; - c2->type = COND_NOT; - return c2; + ecadd(WCB_COND(COND_NOT, 0)); + return par_cond_2(); } if (tok == INPAR) { + int r; + condlex(); while (tok == SEPER) condlex(); - c = par_cond(); + r = par_cond(); while (tok == SEPER) condlex(); if (tok != OUTPAR) - YYERROR; + YYERROR(ecused); condlex(); - return c; + return r; } - if (tok != STRING) + if (tok != STRING) { if (tok && tok != LEXERR && condlex == testlex) { s1 = tokstr; condlex(); return par_cond_double("-n", s1); } else - YYERROR; + YYERROR(ecused); + } s1 = tokstr; if (condlex == testlex) dble = (*s1 == '-' && strspn(s1+1, "abcdefghknoprstuwxzLONGS") == 1 @@ -1155,24 +1825,23 @@ par_cond_2(void) int xtok = tok; condlex(); if (tok != STRING) - YYERROR; + YYERROR(ecused); s3 = tokstr; condlex(); - c = (Cond) make_cond(); - c->left = (void *) s1; - c->right = (void *) s3; - c->type = (xtok == INANG) ? COND_STRLT : COND_STRGTR; - c->ntype = NT_SET(N_COND, NT_STR, NT_STR, 0, 0); - return c; + ecadd(WCB_COND((xtok == INANG ? COND_STRLT : COND_STRGTR), 0)); + ecstr(s1); + ecstr(s3); + return 1; } - if (tok != STRING) + if (tok != STRING) { if (tok != LEXERR && condlex == testlex) { if (!dble) return par_cond_double("-n", s1); else if (!strcmp(s1, "-t")) return par_cond_double(s1, "1"); } else - YYERROR; + YYERROR(ecused); + } s2 = tokstr; incond++; /* parentheses do globbing */ condlex(); @@ -1180,200 +1849,1192 @@ par_cond_2(void) if (tok == STRING && !dble) { s3 = tokstr; condlex(); - return par_cond_triple(s1, s2, s3); + if (tok == STRING) { + LinkList l = newlinklist(); + + addlinknode(l, s2); + addlinknode(l, s3); + + while (tok == STRING) { + addlinknode(l, tokstr); + condlex(); + } + return par_cond_multi(s1, l); + } else + return par_cond_triple(s1, s2, s3); } else return par_cond_double(s1, s2); } -/* - * redir : ( OUTANG | ... | TRINANG ) STRING - */ +/**/ +static int +par_cond_double(char *a, char *b) +{ + if (a[0] != '-' || !a[1]) + COND_ERROR("parse error: condition expected: %s", a); + else if (!a[2] && strspn(a+1, "abcdefgknoprstuwxzhLONGS") == 1) { + ecadd(WCB_COND(a[1], 0)); + ecstr(b); + } else { + ecadd(WCB_COND(COND_MOD, 1)); + ecstr(a); + ecstr(b); + } + return 1; +} -static int redirtab[TRINANG - OUTANG + 1] = { - WRITE, - WRITENOW, - APP, - APPNOW, - READ, - READWRITE, - HEREDOC, - HEREDOCDASH, - MERGEIN, - MERGEOUT, - ERRWRITE, - ERRWRITENOW, - ERRAPP, - ERRAPPNOW, - HERESTR, -}; +/**/ +static int +get_cond_num(char *tst) +{ + static char *condstrs[] = + { + "nt", "ot", "ef", "eq", "ne", "lt", "gt", "le", "ge", NULL + }; + int t0; + + for (t0 = 0; condstrs[t0]; t0++) + if (!strcmp(condstrs[t0], tst)) + return t0; + return -1; +} + +/**/ +static int +par_cond_triple(char *a, char *b, char *c) +{ + int t0; + + if ((b[0] == Equals || b[0] == '=') && + (!b[1] || ((b[1] == Equals || b[1] == '=') && !b[2]))) { + ecadd(WCB_COND(COND_STREQ, 0)); + ecstr(a); + ecstr(c); + ecadd(ecnpats++); + } else if (b[0] == '!' && (b[1] == Equals || b[1] == '=') && !b[2]) { + ecadd(WCB_COND(COND_STRNEQ, 0)); + ecstr(a); + ecstr(c); + ecadd(ecnpats++); + } else if (b[0] == '-') { + if ((t0 = get_cond_num(b + 1)) > -1) { + ecadd(WCB_COND(t0 + COND_NT, 0)); + ecstr(a); + ecstr(c); + } else { + ecadd(WCB_COND(COND_MODI, 0)); + ecstr(b); + ecstr(a); + ecstr(c); + } + } else if (a[0] == '-' && a[1]) { + ecadd(WCB_COND(COND_MOD, 2)); + ecstr(a); + ecstr(b); + ecstr(c); + } else + COND_ERROR("condition expected: %s", b); + + return 1; +} + +/**/ +static int +par_cond_multi(char *a, LinkList l) +{ + if (a[0] != '-' || !a[1]) + COND_ERROR("condition expected: %s", a); + else { + LinkNode n; + + ecadd(WCB_COND(COND_MOD, countlinknodes(l))); + ecstr(a); + for (n = firstnode(l); n; incnode(n)) + ecstr((char *) getdata(n)); + } + return 1; +} /**/ static void -par_redir(LinkList l) +yyerror(int noerr) { - struct redir *fn = (struct redir *)allocnode(N_REDIR); - int oldcmdpos, oldnc; + int t0; + char *t; - oldcmdpos = incmdpos; - incmdpos = 0; - oldnc = nocorrect; - if (tok != INANG && tok != INOUTANG) - nocorrect = 1; - fn->type = redirtab[tok - OUTANG]; - fn->fd1 = tokfd; - yylex(); - if (tok != STRING && tok != ENVSTRING) - YYERRORV; - incmdpos = oldcmdpos; - nocorrect = oldnc; + if ((t = dupstring(yytext))) + untokenize(t); - /* assign default fd */ - if (fn->fd1 == -1) - fn->fd1 = IS_READFD(fn->type) ? 0 : 1; + for (t0 = 0; t0 != 20; t0++) + if (!t || !t[t0] || t[t0] == '\n') + break; + if (t0 == 20) + zwarn("parse error near `%l...'", t, 20); + else if (t0) + zwarn("parse error near `%l'", t, t0); + else + zwarn("parse error", NULL, 0); + if (!noerr && noerrs != 2) + errflag = 1; +} - fn->name = tokstr; +/**/ +mod_export Eprog +dupeprog(Eprog p, int heap) +{ + Eprog r; + int i; + Patprog *pp; - switch (fn->type) { - case HEREDOC: - case HEREDOCDASH: { - /* <<[-] name */ - struct heredocs **hd; + if (p == &dummy_eprog) + return p; - for (hd = &hdocs; *hd; hd = &(*hd)->next); - *hd = zalloc(sizeof(struct heredocs)); - (*hd)->next = NULL; - (*hd)->rd = fn; - break; + r = (heap ? (Eprog) zhalloc(sizeof(*r)) : (Eprog) zalloc(sizeof(*r))); + r->flags = (heap ? EF_HEAP : EF_REAL) | (p->flags & EF_RUN); + r->dump = NULL; + r->len = p->len; + r->npats = p->npats; + pp = r->pats = (heap ? (Patprog *) hcalloc(r->len) : + (Patprog *) zcalloc(r->len)); + r->prog = (Wordcode) (r->pats + r->npats); + r->strs = ((char *) r->prog) + (p->strs - ((char *) p->prog)); + memcpy(r->prog, p->prog, r->len - (p->npats * sizeof(Patprog))); + r->shf = NULL; + + for (i = r->npats; i--; pp++) + *pp = dummy_patprog1; + + return r; +} + +static LinkList eprog_free; + +/**/ +mod_export void +freeeprog(Eprog p) +{ + if (p && p != &dummy_eprog) + zaddlinknode(eprog_free, p); +} + +/**/ +void +freeeprogs(void) +{ + Eprog p; + int i; + Patprog *pp; + + while ((p = (Eprog) getlinknode(eprog_free))) { + for (i = p->npats, pp = p->pats; i--; pp++) + freepatprog(*pp); + if (p->dump) { + decrdumpcount(p->dump); + zfree(p->pats, p->npats * sizeof(Patprog)); + } else + zfree(p->pats, p->len); + zfree(p, sizeof(*p)); } - case WRITE: - case WRITENOW: - if (tokstr[0] == Outang && tokstr[1] == Inpar) - /* > >(...) */ - fn->type = OUTPIPE; - else if (tokstr[0] == Inang && tokstr[1] == Inpar) - YYERRORV; - break; - case READ: - if (tokstr[0] == Inang && tokstr[1] == Inpar) - /* < <(...) */ - fn->type = INPIPE; - else if (tokstr[0] == Outang && tokstr[1] == Inpar) - YYERRORV; - break; - case READWRITE: - if ((tokstr[0] == Inang || tokstr[0] == Outang) && tokstr[1] == Inpar) - fn->type = tokstr[0] == Inang ? INPIPE : OUTPIPE; - break; +} + +/**/ +char * +ecgetstr(Estate s, int dup, int *tok) +{ + static char buf[4]; + wordcode c = *s->pc++; + char *r; + + if (c == 6 || c == 7) + r = ""; + else if (c & 2) { + buf[0] = (char) ((c >> 3) & 0xff); + buf[1] = (char) ((c >> 11) & 0xff); + buf[2] = (char) ((c >> 19) & 0xff); + buf[3] = '\0'; + r = dupstring(buf); + dup = EC_NODUP; + } else { + r = s->strs + (c >> 2); } - yylex(); - addlinknode(l, fn); + if (tok) + *tok = (c & 1); + + /*** Since function dump files are mapped read-only, avoiding to + * to duplicate strings when they don't contain tokens may fail + * when one of the many utility functions happens to write to + * one of the strings (without really modifying it). + * If that happens to you and you don't feel like debugging it, + * just change the line below to: + * + * return (dup ? dupstring(r) : r); + */ + + return ((dup == EC_DUP || (dup && (c & 1))) ? dupstring(r) : r); } -/* - * wordlist : { STRING } - */ +/**/ +char * +ecrawstr(Eprog p, Wordcode pc, int *tok) +{ + static char buf[4]; + wordcode c = *pc; + + if (c == 6 || c == 7) { + if (tok) + *tok = (c & 1); + return ""; + } else if (c & 2) { + buf[0] = (char) ((c >> 3) & 0xff); + buf[1] = (char) ((c >> 11) & 0xff); + buf[2] = (char) ((c >> 19) & 0xff); + buf[3] = '\0'; + if (tok) + *tok = (c & 1); + return buf; + } else { + if (tok) + *tok = (c & 1); + return p->strs + (c >> 2); + } +} /**/ -static LinkList -par_wordlist(void) +char ** +ecgetarr(Estate s, int num, int dup, int *tok) { - LinkList l; + char **ret, **rp; + int tf = 0, tmp = 0; - l = newlinklist(); - while (tok == STRING) { - addlinknode(l, tokstr); - yylex(); + ret = rp = (char **) zhalloc((num + 1) * sizeof(char *)); + + while (num--) { + *rp++ = ecgetstr(s, dup, &tmp); + tf |= tmp; } - return l; + *rp = NULL; + if (tok) + *tok = tf; + + return ret; } -/* - * nl_wordlist : { STRING | SEPER } +/**/ +LinkList +ecgetlist(Estate s, int num, int dup, int *tok) +{ + if (num) { + LinkList ret; + int i, tf = 0, tmp = 0; + + ret = newsizedlist(num); + for (i = 0; i < num; i++) { + setsizednode(ret, i, ecgetstr(s, dup, &tmp)); + tf |= tmp; + } + if (tok) + *tok = tf; + return ret; + } + if (tok) + *tok = 0; + return NULL; +} + +/**/ +LinkList +ecgetredirs(Estate s) +{ + LinkList ret = newlinklist(); + wordcode code = *s->pc++; + + while (wc_code(code) == WC_REDIR) { + Redir r = (Redir) zhalloc(sizeof(*r)); + + r->type = WC_REDIR_TYPE(code); + r->fd1 = *s->pc++; + r->name = ecgetstr(s, EC_DUP, NULL); + + addlinknode(ret, r); + + code = *s->pc++; + } + s->pc--; + + return ret; +} + +/**/ +mod_export struct eprog dummy_eprog; + +static wordcode dummy_eprog_code; + +/**/ +void +init_eprog(void) +{ + dummy_eprog_code = WCB_END(); + dummy_eprog.len = sizeof(wordcode); + dummy_eprog.prog = &dummy_eprog_code; + dummy_eprog.strs = NULL; + + eprog_free = znewlinklist(); +} + +/* Code for function dump files. + * + * Dump files consist of a header and the function bodies (the wordcode + * plus the string table) and that twice: once for the byte-order of the + * host the file was created on and once for the other byte-order. The + * header describes where the beginning of the `other' version is and it + * is up to the shell reading the file to decide which version it needs. + * This is done by checking if the first word is FD_MAGIC (then the + * shell reading the file has the same byte order as the one that created + * the file) or if it is FD_OMAGIC, then the `other' version has to be + * read. + * The header is the magic number, a word containing the flags (if the + * file should be mapped or read and if this header is the `other' one), + * the version string in a field of 40 characters and the descriptions + * for the functions in the dump file. + * Each description consists of a struct fdhead followed by the name, + * aligned to sizeof(wordcode) (i.e. 4 bytes). */ +#include "version.h" + +#define FD_EXT ".zwc" +#define FD_MINMAP 4096 + +#define FD_PRELEN 12 +#define FD_MAGIC 0x02030405 +#define FD_OMAGIC 0x05040302 + +#define FDF_MAP 1 +#define FDF_OTHER 2 + +typedef struct fdhead *FDHead; + +struct fdhead { + wordcode start; /* offset to function definition */ + wordcode len; /* length of wordcode/strings */ + wordcode npats; /* number of patterns needed */ + wordcode strs; /* offset to strings */ + wordcode hlen; /* header length (incl. name) */ + wordcode flags; /* flags and offset to name tail */ +}; + +#define fdheaderlen(f) (((Wordcode) (f))[FD_PRELEN]) + +#define fdmagic(f) (((Wordcode) (f))[0]) +#define fdsetbyte(f,i,v) \ + ((((unsigned char *) (((Wordcode) (f)) + 1))[i]) = ((unsigned char) (v))) +#define fdbyte(f,i) ((wordcode) (((unsigned char *) (((Wordcode) (f)) + 1))[i])) +#define fdflags(f) fdbyte(f, 0) +#define fdsetflags(f,v) fdsetbyte(f, 0, v) +#define fdother(f) (fdbyte(f, 1) + (fdbyte(f, 2) << 8) + (fdbyte(f, 3) << 16)) +#define fdsetother(f, o) \ + do { \ + fdsetbyte(f, 1, ((o) & 0xff)); \ + fdsetbyte(f, 2, (((o) >> 8) & 0xff)); \ + fdsetbyte(f, 3, (((o) >> 16) & 0xff)); \ + } while (0) +#define fdversion(f) ((char *) ((f) + 2)) + +#define firstfdhead(f) ((FDHead) (((Wordcode) (f)) + FD_PRELEN)) +#define nextfdhead(f) ((FDHead) (((Wordcode) (f)) + (f)->hlen)) + +#define fdhflags(f) (((FDHead) (f))->flags) +#define fdhtail(f) (((FDHead) (f))->flags >> 2) +#define fdhbldflags(f,t) ((f) | ((t) << 2)) + +#define FDHF_KSHLOAD 1 +#define FDHF_ZSHLOAD 2 + +#define fdname(f) ((char *) (((FDHead) (f)) + 1)) + +/* This is used when building wordcode files. */ + +typedef struct wcfunc *WCFunc; + +struct wcfunc { + char *name; + Eprog prog; + int flags; +}; + +/* Try to find the description for the given function name. */ + +static FDHead +dump_find_func(Wordcode h, char *name) +{ + FDHead n, e = (FDHead) (h + fdheaderlen(h)); + + for (n = firstfdhead(h); n < e; n = nextfdhead(n)) + if (!strcmp(name, fdname(n) + fdhtail(n))) + return n; + + return NULL; +} + /**/ -static LinkList -par_nl_wordlist(void) +int +bin_zcompile(char *nam, char **args, char *ops, int func) { - LinkList l; + int map, flags; + char *dump; + + if ((ops['k'] && ops['z']) || (ops['R'] && ops['M']) || + (ops['c'] && (ops['U'] || ops['k'] || ops['z'])) || + (!(ops['c'] || ops['a']) && ops['m'])) { + zwarnnam(nam, "illegal combination of options", NULL, 0); + return 1; + } + if ((ops['c'] || ops['a']) && isset(KSHAUTOLOAD)) + zwarnnam(nam, "functions will use zsh style autoloading", NULL, 0); - l = newlinklist(); - while (tok == STRING || tok == SEPER) { - if (tok != SEPER) - addlinknode(l, tokstr); - yylex(); + flags = (ops['k'] ? FDHF_KSHLOAD : + (ops['z'] ? FDHF_ZSHLOAD : 0)); + + if (ops['t']) { + Wordcode f; + + if (!*args) { + zwarnnam(nam, "too few arguments", NULL, 0); + return 1; + } + if (!(f = load_dump_header(*args)) && + !(f = load_dump_header(dyncat(*args, FD_EXT)))) { + zwarnnam(nam, "invalid dump file: %s", *args, 0); + return 1; + } + if (args[1]) { + for (args++; *args; args++) + if (!dump_find_func(f, *args)) + return 1; + return 0; + } else { + FDHead h, e = (FDHead) (f + fdheaderlen(f)); + + printf("function dump file (%s) for zsh-%s\n", + ((fdflags(f) & FDF_MAP) ? "mapped" : "read"), fdversion(f)); + for (h = firstfdhead(f); h < e; h = nextfdhead(h)) + printf("%s\n", fdname(h)); + return 0; + } + } + if (!*args) { + zwarnnam(nam, "too few arguments", NULL, 0); + return 1; } - return l; + map = (ops['M'] ? 2 : (ops['R'] ? 0 : 1)); + + if (!args[1] && !(ops['c'] || ops['a'])) + return build_dump(nam, dyncat(*args, FD_EXT), args, ops['U'], map, flags); + + dump = (strsfx(FD_EXT, *args) ? *args : dyncat(*args, FD_EXT)); + + return ((ops['c'] || ops['a']) ? + build_cur_dump(nam, dump, args + 1, ops['m'], map, + (ops['c'] ? 1 : 0) | (ops['a'] ? 2 : 0)) : + build_dump(nam, dump, args + 1, ops['U'], map, flags)); } +/* Load the header of a dump file. Returns NULL if the file isn't a + * valid dump file. */ + /**/ -static Cond -par_cond_double(char *a, char *b) +static Wordcode +load_dump_header(char *name) { - Cond n = (Cond) make_cond(); + int fd; + wordcode buf[FD_PRELEN + 1]; - if (a[0] != '-' || !a[1] || a[2]) - COND_ERROR("parse error: condition expected: %s", a); - n->left = (void *) b; - n->type = a[1]; - n->ntype = NT_SET(N_COND, NT_STR, NT_STR, 0, 0); - return n; + if ((fd = open(name, O_RDONLY)) < 0) + return NULL; + + if (read(fd, buf, (FD_PRELEN + 1) * sizeof(wordcode)) != + ((FD_PRELEN + 1) * sizeof(wordcode)) || + (fdmagic(buf) != FD_MAGIC && fdmagic(buf) != FD_OMAGIC) || + strcmp(ZSH_VERSION, fdversion(buf))) { + close(fd); + return NULL; + } else { + int len; + Wordcode head; + + if (fdmagic(buf) == FD_MAGIC) { + len = fdheaderlen(buf) * sizeof(wordcode); + head = (Wordcode) zhalloc(len); + } + else { + int o = fdother(buf); + + if (lseek(fd, o, 0) == -1 || + read(fd, buf, (FD_PRELEN + 1) * sizeof(wordcode)) != + ((FD_PRELEN + 1) * sizeof(wordcode))) { + close(fd); + return NULL; + } + len = fdheaderlen(buf) * sizeof(wordcode); + head = (Wordcode) zhalloc(len); + } + memcpy(head, buf, (FD_PRELEN + 1) * sizeof(wordcode)); + + if (read(fd, head + (FD_PRELEN + 1), + len - ((FD_PRELEN + 1) * sizeof(wordcode))) != + len - ((FD_PRELEN + 1) * sizeof(wordcode))) { + close(fd); + return NULL; + } + close(fd); + return head; + } +} + +/* Swap the bytes in a wordcode. */ + +static void +fdswap(Wordcode p, int n) +{ + wordcode c; + + for (; n--; p++) { + c = *p; + *p = (((c & 0xff) << 24) | + ((c & 0xff00) << 8) | + ((c & 0xff0000) >> 8) | + ((c & 0xff000000) >> 24)); + } +} + +/* Write a dump file. */ + +static void +write_dump(int dfd, LinkList progs, int map, int hlen, int tlen) +{ + LinkNode node; + WCFunc wcf; + int other = 0, ohlen, tmp; + wordcode pre[FD_PRELEN]; + char *tail, *n; + struct fdhead head; + Eprog prog; + + if (map == 1) + map = (tlen >= FD_MINMAP); + + for (ohlen = hlen; ; hlen = ohlen) { + fdmagic(pre) = (other ? FD_OMAGIC : FD_MAGIC); + fdsetflags(pre, ((map ? FDF_MAP : 0) | other)); + fdsetother(pre, tlen); + strcpy(fdversion(pre), ZSH_VERSION); + write(dfd, pre, FD_PRELEN * sizeof(wordcode)); + + for (node = firstnode(progs); node; incnode(node)) { + wcf = (WCFunc) getdata(node); + n = wcf->name; + prog = wcf->prog; + head.start = hlen; + hlen += (prog->len - (prog->npats * sizeof(Patprog)) + + sizeof(wordcode) - 1) / sizeof(wordcode); + head.len = prog->len - (prog->npats * sizeof(Patprog)); + head.npats = prog->npats; + head.strs = prog->strs - ((char *) prog->prog); + head.hlen = (sizeof(struct fdhead) / sizeof(wordcode)) + + (strlen(n) + sizeof(wordcode)) / sizeof(wordcode); + if ((tail = strrchr(n, '/'))) + tail++; + else + tail = n; + head.flags = fdhbldflags(wcf->flags, (tail - n)); + if (other) + fdswap((Wordcode) &head, sizeof(head) / sizeof(wordcode)); + write(dfd, &head, sizeof(head)); + tmp = strlen(n) + 1; + write(dfd, n, tmp); + if ((tmp &= (sizeof(wordcode) - 1))) + write(dfd, &head, sizeof(wordcode) - tmp); + } + for (node = firstnode(progs); node; incnode(node)) { + prog = ((WCFunc) getdata(node))->prog; + tmp = (prog->len - (prog->npats * sizeof(Patprog)) + + sizeof(wordcode) - 1) / sizeof(wordcode); + if (other) + fdswap(prog->prog, (((Wordcode) prog->strs) - prog->prog)); + write(dfd, prog->prog, tmp * sizeof(wordcode)); + } + if (other) + break; + other = FDF_OTHER; + } } /**/ static int -get_cond_num(char *tst) +build_dump(char *nam, char *dump, char **files, int ali, int map, int flags) { - static char *condstrs[] = - { - "nt", "ot", "ef", "eq", "ne", "lt", "gt", "le", "ge", NULL - }; - int t0; + int dfd, fd, hlen, tlen, flen, ona = noaliases; + LinkList progs; + char *file; + Eprog prog; + WCFunc wcf; + + if (!strsfx(FD_EXT, dump)) + dump = dyncat(dump, FD_EXT); + + if ((dfd = open(dump, O_WRONLY|O_CREAT, 0600)) < 0) { + zwarnnam(nam, "can't write dump file: %s", dump, 0); + return 1; + } + progs = newlinklist(); + noaliases = ali; + + for (hlen = FD_PRELEN, tlen = 0; *files; files++) { + if (!strcmp(*files, "-k")) { + flags = (flags & ~(FDHF_KSHLOAD | FDHF_ZSHLOAD)) | FDHF_KSHLOAD; + continue; + } else if (!strcmp(*files, "-z")) { + flags = (flags & ~(FDHF_KSHLOAD | FDHF_ZSHLOAD)) | FDHF_ZSHLOAD; + continue; + } + if ((fd = open(*files, O_RDONLY)) < 0 || + (flen = lseek(fd, 0, 2)) == -1) { + if (fd >= 0) + close(fd); + close(dfd); + zwarnnam(nam, "can't open file: %s", *files, 0); + noaliases = ona; + unlink(dump); + return 1; + } + file = (char *) zalloc(flen + 1); + file[flen] = '\0'; + lseek(fd, 0, 0); + if (read(fd, file, flen) != flen) { + close(fd); + close(dfd); + zfree(file, flen); + zwarnnam(nam, "can't read file: %s", *files, 0); + noaliases = ona; + unlink(dump); + return 1; + } + close(fd); + file = metafy(file, flen, META_REALLOC); + + if (!(prog = parse_string(file, 1)) || errflag) { + errflag = 0; + close(dfd); + zfree(file, flen); + zwarnnam(nam, "can't read file: %s", *files, 0); + noaliases = ona; + unlink(dump); + return 1; + } + zfree(file, flen); - for (t0 = 0; condstrs[t0]; t0++) - if (!strcmp(condstrs[t0], tst)) - return t0; - return -1; + wcf = (WCFunc) zhalloc(sizeof(*wcf)); + wcf->name = *files; + wcf->prog = prog; + wcf->flags = ((prog->flags & EF_RUN) ? FDHF_KSHLOAD : flags); + addlinknode(progs, wcf); + + flen = (strlen(*files) + sizeof(wordcode)) / sizeof(wordcode); + hlen += (sizeof(struct fdhead) / sizeof(wordcode)) + flen; + + tlen += (prog->len - (prog->npats * sizeof(Patprog)) + + sizeof(wordcode) - 1) / sizeof(wordcode); + } + noaliases = ona; + + tlen = (tlen + hlen) * sizeof(wordcode); + + write_dump(dfd, progs, map, hlen, tlen); + + close(dfd); + + return 0; +} + +static int +cur_add_func(char *nam, Shfunc shf, LinkList names, LinkList progs, + int *hlen, int *tlen, int what) +{ + Eprog prog; + WCFunc wcf; + + if (shf->flags & PM_UNDEFINED) { + int ona = noaliases; + + if (!(what & 2)) { + zwarnnam(nam, "function is not loaded: %s", shf->nam, 0); + return 1; + } + noaliases = (shf->flags & PM_UNALIASED); + if (!(prog = getfpfunc(shf->nam, NULL)) || prog == &dummy_eprog) { + noaliases = ona; + zwarnnam(nam, "can't load function: %s", shf->nam, 0); + return 1; + } + if (prog->dump) + prog = dupeprog(prog, 1); + noaliases = ona; + } else { + if (!(what & 1)) { + zwarnnam(nam, "function is already loaded: %s", shf->nam, 0); + return 1; + } + prog = dupeprog(shf->funcdef, 1); + } + wcf = (WCFunc) zhalloc(sizeof(*wcf)); + wcf->name = shf->nam; + wcf->prog = prog; + wcf->flags = ((prog->flags & EF_RUN) ? FDHF_KSHLOAD : FDHF_ZSHLOAD); + addlinknode(progs, wcf); + addlinknode(names, shf->nam); + + *hlen += ((sizeof(struct fdhead) / sizeof(wordcode)) + + ((strlen(shf->nam) + sizeof(wordcode)) / sizeof(wordcode))); + *tlen += (prog->len - (prog->npats * sizeof(Patprog)) + + sizeof(wordcode) - 1) / sizeof(wordcode); + + return 0; } /**/ -static Cond -par_cond_triple(char *a, char *b, char *c) +static int +build_cur_dump(char *nam, char *dump, char **names, int match, int map, + int what) { - Cond n = (Cond) make_cond(); - int t0; + int dfd, hlen, tlen; + LinkList progs, lnames; + Shfunc shf = NULL; - if ((b[0] == Equals || b[0] == '=') && - (!b[1] || ((b[1] == Equals || b[1] == '=') && !b[2]))) - n->type = COND_STREQ; - else if (b[0] == '!' && (b[1] == Equals || b[1] == '=') && !b[2]) - n->type = COND_STRNEQ; - else if (b[0] == '-') { - if ((t0 = get_cond_num(b + 1)) > -1) - n->type = t0 + COND_NT; - else - COND_ERROR("unrecognized condition: %s", b); + if (!strsfx(FD_EXT, dump)) + dump = dyncat(dump, FD_EXT); + + if ((dfd = open(dump, O_WRONLY|O_CREAT, 0600)) < 0) { + zwarnnam(nam, "can't write dump file: %s", dump, 0); + return 1; + } + progs = newlinklist(); + lnames = newlinklist(); + + hlen = FD_PRELEN; + tlen = 0; + + if (!*names) { + int i; + HashNode hn; + + for (i = 0; i < shfunctab->hsize; i++) + for (hn = shfunctab->nodes[i]; hn; hn = hn->next) + if (cur_add_func(nam, (Shfunc) hn, lnames, progs, + &hlen, &tlen, what)) { + errflag = 0; + close(dfd); + unlink(dump); + return 1; + } + } else if (match) { + char *pat; + Patprog pprog; + int i; + HashNode hn; + + for (; *names; names++) { + tokenize(pat = dupstring(*names)); + if (!(pprog = patcompile(pat, PAT_STATIC, NULL))) { + zwarnnam(nam, "bad pattern: %s", *names, 0); + close(dfd); + unlink(dump); + return 1; + } + for (i = 0; i < shfunctab->hsize; i++) + for (hn = shfunctab->nodes[i]; hn; hn = hn->next) + if (!listcontains(lnames, hn->nam) && + pattry(pprog, hn->nam) && + cur_add_func(nam, (Shfunc) hn, lnames, progs, + &hlen, &tlen, what)) { + errflag = 0; + close(dfd); + unlink(dump); + return 1; + } + } + } else { + for (; *names; names++) { + if (errflag || + !(shf = (Shfunc) shfunctab->getnode(shfunctab, *names))) { + zwarnnam(nam, "unknown function: %s", *names, 0); + errflag = 0; + close(dfd); + unlink(dump); + return 1; + } + if (cur_add_func(nam, shf, lnames, progs, &hlen, &tlen, what)) { + errflag = 0; + close(dfd); + unlink(dump); + return 1; + } + } + } + if (empty(progs)) { + zwarnnam(nam, "no functions", NULL, 0); + errflag = 0; + close(dfd); + unlink(dump); + return 1; + } + tlen = (tlen + hlen) * sizeof(wordcode); + + write_dump(dfd, progs, map, hlen, tlen); + + close(dfd); + + return 0; +} + +#if defined(HAVE_SYS_MMAN_H) && defined(HAVE_MMAP) && defined(HAVE_MUNMAP) + +#include <sys/mman.h> + +#if defined(MAP_SHARED) && defined(PROT_READ) + +#define USE_MMAP 1 + +#endif +#endif + +#ifdef USE_MMAP + +/* List of dump files mapped. */ + +static FuncDump dumps; + +/* Load a dump file (i.e. map it). */ + +static void +load_dump_file(char *dump, int other, int len) +{ + FuncDump d; + Wordcode addr; + int fd, off; + + if (other) { + static size_t pgsz = 0; + + if (!pgsz) { + +#ifdef _SC_PAGESIZE + pgsz = sysconf(_SC_PAGESIZE); /* SVR4 */ +#else +# ifdef _SC_PAGE_SIZE + pgsz = sysconf(_SC_PAGE_SIZE); /* HPUX */ +# else + pgsz = getpagesize(); +# endif +#endif + + pgsz--; + } + off = len & ~pgsz; } else - COND_ERROR("condition expected: %s", b); - n->left = (void *) a; - n->right = (void *) c; - n->ntype = NT_SET(N_COND, NT_STR, NT_STR, 0, 0); - return n; + off = 0; + + if ((fd = open(dump, O_RDONLY)) < 0) + return; + + fd = movefd(fd); + + if ((addr = (Wordcode) mmap(NULL, len, PROT_READ, MAP_SHARED, fd, off)) == + ((Wordcode) -1)) { + close(fd); + return; + } + d = (FuncDump) zalloc(sizeof(*d)); + d->next = dumps; + dumps = d; + d->name = ztrdup(dump); + d->fd = fd; + d->map = addr + (other ? (len - off) / sizeof(wordcode) : 0); + d->addr = addr; + d->len = len; + d->count = 0; } +#endif + +/* Try to load a function from one of the possible wordcode files for it. + * The first argument is a element of $fpath, the second one is the name + * of the function searched and the last one is the possible name for the + * uncompiled function file (<path>/<func>). */ + /**/ -static void -yyerror(void) +Eprog +try_dump_file(char *path, char *name, char *file, int *ksh) { - int t0; + Eprog prog; + struct stat std, stc, stn; + int rd, rc, rn; + char *dig, *wc; + + if (strsfx(FD_EXT, path)) + return check_dump_file(path, name, ksh); + + dig = dyncat(path, FD_EXT); + wc = dyncat(file, FD_EXT); + + rd = stat(dig, &std); + rc = stat(wc, &stc); + rn = stat(file, &stn); + + /* See if there is a digest file for the directory, it is younger than + * both the uncompiled function file and its compiled version (or they + * don't exist) and the digest file contains the definition for the + * function. */ + if (!rd && + (rc || std.st_mtime > stc.st_mtime) && + (rn || std.st_mtime > stn.st_mtime) && + (prog = check_dump_file(dig, name, ksh))) + return prog; + + /* No digest file. Now look for the per-function compiled file. */ + if (!rc && + (rn || stc.st_mtime > stn.st_mtime) && + (prog = check_dump_file(wc, name, ksh))) + return prog; + + /* No compiled file for the function. The caller (getfpfunc() will + * check if the directory contains the uncompiled file for it. */ + return NULL; +} - for (t0 = 0; t0 != 20; t0++) - if (!yytext || !yytext[t0] || yytext[t0] == '\n') - break; - if (t0 == 20) - zerr("parse error near `%l...'", yytext, 20); - else if (t0) - zerr("parse error near `%l'", yytext, t0); +/* Almost the same, but for sourced files. */ + +/**/ +Eprog +try_source_file(char *file) +{ + Eprog prog; + struct stat stc, stn; + int rc, rn; + char *wc, *tail; + + if ((tail = strrchr(file, '/'))) + tail++; else - zerr("parse error", NULL, 0); + tail = file; + + if (strsfx(FD_EXT, file)) + return check_dump_file(file, tail, NULL); + + wc = dyncat(file, FD_EXT); + + rc = stat(wc, &stc); + rn = stat(file, &stn); + + if (!rc && (rn || stc.st_mtime > stn.st_mtime) && + (prog = check_dump_file(wc, tail, NULL))) + return prog; + + return NULL; +} + +/* See if `file' names a wordcode dump file and that contains the + * definition for the function `name'. If so, return an eprog for it. */ + +/**/ +static Eprog +check_dump_file(char *file, char *name, int *ksh) +{ + int isrec = 0; + Wordcode d; + FDHead h; + FuncDump f; + +#ifdef USE_MMAP + + rec: + +#endif + + d = NULL; + +#ifdef USE_MMAP + + for (f = dumps; f; f = f->next) + if (!strcmp(file, f->name)) { + d = f->map; + break; + } + +#else + + f = NULL; + +#endif + + if (!f && (isrec || !(d = load_dump_header(file)))) + return NULL; + + if ((h = dump_find_func(d, name))) { + /* Found the name. If the file is already mapped, return the eprog, + * otherwise map it and just go up. */ + +#ifdef USE_MMAP + + if (f) { + Eprog prog = (Eprog) zalloc(sizeof(*prog)); + Patprog *pp; + int np; + + prog->flags = EF_MAP; + prog->len = h->len; + prog->npats = np = h->npats; + prog->pats = pp = (Patprog *) zalloc(np * sizeof(Patprog)); + prog->prog = f->map + h->start; + prog->strs = ((char *) prog->prog) + h->strs; + prog->shf = NULL; + prog->dump = f; + + incrdumpcount(f); + + while (np--) + *pp++ = dummy_patprog1; + + if (ksh) + *ksh = ((fdhflags(h) & FDHF_KSHLOAD) ? 2 : + ((fdhflags(h) & FDHF_ZSHLOAD) ? 0 : 1)); + + return prog; + } else if (fdflags(d) & FDF_MAP) { + load_dump_file(file, (fdflags(d) & FDF_OTHER), fdother(d)); + isrec = 1; + goto rec; + } else + +#endif + + { + Eprog prog; + Patprog *pp; + int np, fd, po = h->npats * sizeof(Patprog); + + if ((fd = open(file, O_RDONLY)) < 0 || + lseek(fd, ((h->start * sizeof(wordcode)) + + ((fdflags(d) & FDF_OTHER) ? fdother(d) : 0)), 0) < 0) { + if (fd >= 0) + close(fd); + return NULL; + } + d = (Wordcode) zalloc(h->len + po); + + if (read(fd, ((char *) d) + po, h->len) != h->len) { + close(fd); + zfree(d, h->len); + + return NULL; + } + close(fd); + + prog = (Eprog) zalloc(sizeof(*prog)); + + prog->flags = EF_REAL; + prog->len = h->len + po; + prog->npats = np = h->npats; + prog->pats = pp = (Patprog *) d; + prog->prog = (Wordcode) (((char *) d) + po); + prog->strs = ((char *) prog->prog) + h->strs; + prog->shf = NULL; + prog->dump = f; + + while (np--) + *pp++ = dummy_patprog1; + + if (ksh) + *ksh = ((fdhflags(h) & FDHF_KSHLOAD) ? 2 : + ((fdhflags(h) & FDHF_ZSHLOAD) ? 0 : 1)); + + return prog; + } + } + return NULL; +} + +#ifdef USE_MMAP + +/* Increment the reference counter for a dump file. */ + +/**/ +void +incrdumpcount(FuncDump f) +{ + f->count++; +} + +/* Decrement the reference counter for a dump file. If zero, unmap the file. */ + +/**/ +void +decrdumpcount(FuncDump f) +{ + f->count--; + if (!f->count) { + FuncDump p, q; + + for (q = NULL, p = dumps; p && p != f; q = p, p = p->next); + if (p) { + if (q) + q->next = p->next; + else + dumps = p->next; + munmap((void *) f->addr, f->len); + zclose(f->fd); + zsfree(f->name); + zfree(f, sizeof(*f)); + } + } +} + +#else + +void +incrdumpcount(FuncDump f) +{ +} + +void +decrdumpcount(FuncDump f) +{ +} + +#endif + +/**/ +int +dump_autoload(char *file, int on, char *ops, int func) +{ + Wordcode h; + FDHead n, e; + Shfunc shf; + int ret = 0; + + if (!strsfx(FD_EXT, file)) + file = dyncat(file, FD_EXT); + + if (!(h = load_dump_header(file))) + return 1; + + for (n = firstfdhead(h), e = (FDHead) (h + fdheaderlen(h)); n < e; + n = nextfdhead(n)) { + shf = (Shfunc) zcalloc(sizeof *shf); + shf->flags = on; + shf->funcdef = mkautofn(shf); + shfunctab->addnode(shfunctab, ztrdup(fdname(n) + fdhtail(n)), shf); + if (ops['X'] && eval_autoload(shf, shf->nam, ops, func)) + ret = 1; + } + return ret; } |