--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/openenvutils/commandshell/shell/src/parse.c Tue Feb 02 10:12:00 2010 +0200
@@ -0,0 +1,3296 @@
+// parse.c - parser
+//
+// © Portions Copyright (c) Symbian Software Ltd 2007. All rights reserved.
+//
+/*
+ * This file is part of zsh, the Z shell.
+ *
+ * Copyright (c) 1992-1997 Paul Falstad
+ * All rights reserved.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and to distribute modified versions of this software for any
+ * purpose, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * In no event shall Paul Falstad or the Zsh Development Group be liable
+ * to any party for direct, indirect, special, incidental, or consequential
+ * damages arising out of the use of this software and its documentation,
+ * even if Paul Falstad and the Zsh Development Group have been advised of
+ * the possibility of such damage.
+ *
+ * Paul Falstad and the Zsh Development Group specifically disclaim any
+ * warranties, including, but not limited to, the implied warranties of
+ * merchantability and fitness for a particular purpose. The software
+ * provided hereunder is on an "as is" basis, and Paul Falstad and the
+ * Zsh Development Group have no obligation to provide maintenance,
+ * support, updates, enhancements, or modifications.
+ *
+ */
+
+#include "zsh.mdh"
+#include "parse.pro"
+
+#ifdef __SYMBIAN32__
+#ifdef __WINSCW__
+#pragma warn_possunwant off
+#endif//__WINSCW__
+#endif//__SYMBIAN32__
+
+/* != 0 if we are about to read a command word */
+
+/**/
+mod_export int incmdpos;
+
+/**/
+int aliasspaceflag;
+
+/* != 0 if we are in the middle of a [[ ... ]] */
+
+/**/
+mod_export int incond;
+
+/* != 0 if we are after a redirection (for ctxtlex only) */
+
+/**/
+mod_export int inredir;
+
+/* != 0 if we are about to read a case pattern */
+
+/**/
+int incasepat;
+
+/* != 0 if we just read a newline */
+
+/**/
+int isnewlin;
+
+/* != 0 if we are after a for keyword */
+
+/**/
+int infor;
+
+/* list of here-documents */
+
+/**/
+struct heredocs *hdocs;
+
+
+#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.
+ *
+ * The parser now produces word code, reducing memory consumption compared
+ * to the nested structs we had before.
+ *
+ * Word code layout:
+ *
+ * WC_END
+ * - end of program code
+ *
+ * WC_LIST
+ * - data contains type (sync, ...)
+ * - followed 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
+ * - followed 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 offset 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 ecstrcode() function that adds the code for a string uses a simple
+ * binary tree 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;
+/**/
+int ecsoffs, ecssub, ecnfunc;
+
+#define EC_INIT_SIZE 256
+#define EC_DOUBLE_THRESHOLD 32768
+#define EC_INCREMENT 1024
+
+
+/* Adjust pointers in here-doc structs. */
+
+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 = (eclen < EC_DOUBLE_THRESHOLD ? eclen : EC_INCREMENT);
+
+ if (n > a) a = n;
+
+ ecbuf = (Wordcode) zrealloc((char *) ecbuf, (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) {
+ int a = (eclen < EC_DOUBLE_THRESHOLD ? eclen : EC_INCREMENT);
+
+ ecbuf = (Wordcode) zrealloc((char *) ecbuf, (eclen + a) * sizeof(wordcode));
+ eclen += a;
+ }
+ 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, *pp;
+ int cmp;
+
+ for (pp = &ecstrs; (p = *pp); ) {
+ if (!(cmp = p->nfunc - ecnfunc) && !(cmp = strcmp(p->str, s)))
+ return p->offs;
+ pp = (cmp < 0 ? &(p->left) : &(p->right));
+ }
+ p = *pp = (Eccstr) zhalloc(sizeof(*p));
+ p->left = p->right = 0;
+ p->offs = ((ecsoffs - ecssub) << 2) | (t ? 1 : 0);
+ p->aoffs = ecsoffs;
+ p->str = s;
+ p->nfunc = ecnfunc;
+ ecsoffs += l;
+
+ return p->offs;
+ }
+}
+
+#define ecstr(S) 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)
+{
+ if (ecbuf) zfree(ecbuf, eclen);
+
+ ecbuf = (Wordcode) zalloc((eclen = EC_INIT_SIZE) * sizeof(wordcode));
+ ecused = 0;
+ ecstrs = NULL;
+ ecsoffs = ecnpats = 0;
+ ecssub = 0;
+ ecnfunc = 0;
+}
+
+/* Build eprog. */
+
+static void
+copy_ecstr(Eccstr s, char *p)
+{
+ while (s) {
+ memcpy(p + s->aoffs, s->str, strlen(s->str) + 1);
+ copy_ecstr(s->left, p);
+ s = s->right;
+ }
+}
+
+static Eprog
+bld_eprog(void)
+{
+ Eprog ret;
+ int l;
+
+ ecadd(WCB_END());
+
+ ret = (Eprog) zhalloc(sizeof(*ret));
+ ret->len = ((ecnpats * sizeof(Patprog)) +
+ (ecused * sizeof(wordcode)) +
+ ecsoffs);
+ ret->npats = ecnpats;
+ ret->nref = -1; /* Eprog is on the heap */
+ 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));
+ copy_ecstr(ecstrs, ret->strs);
+
+ zfree(ecbuf, eclen);
+ ecbuf = NULL;
+
+ return ret;
+}
+
+/**/
+mod_export int
+empty_eprog(Eprog p)
+{
+ return (!p || !p->prog || *p->prog == WCB_END());
+}
+
+static void
+clear_hdocs()
+{
+ struct heredocs *p, *n;
+
+ for (p = hdocs; p; p = n) {
+ n = p->next;
+ zfree(p, sizeof(struct heredocs));
+ }
+ hdocs = NULL;
+}
+
+/*
+ * event : ENDINPUT
+ * | SEPER
+ * | sublist [ SEPER | AMPER | AMPERBANG ]
+ */
+
+/**/
+Eprog
+parse_event(void)
+{
+ tok = ENDINPUT;
+ incmdpos = 1;
+ aliasspaceflag = 0;
+ yylex();
+ init_parse();
+
+ if (!par_event()) {
+ clear_hdocs();
+ return NULL;
+ }
+ return bld_eprog();
+}
+
+/**/
+static int
+par_event(void)
+{
+ int r = 0, p, c = 0;
+
+ while (tok == SEPER) {
+ if (isnewlin > 0)
+ return 0;
+ yylex();
+ }
+ if (tok == ENDINPUT)
+ return 0;
+
+ p = ecadd(0);
+
+ if (par_sublist(&c)) {
+ if (tok == ENDINPUT) {
+ set_list_code(p, Z_SYNC, c);
+ r = 1;
+ } else if (tok == SEPER) {
+ set_list_code(p, Z_SYNC, c);
+ if (isnewlin <= 0)
+ yylex();
+ r = 1;
+ } else if (tok == AMPER) {
+ set_list_code(p, Z_ASYNC, c);
+ yylex();
+ r = 1;
+ } else if (tok == AMPERBANG) {
+ set_list_code(p, (Z_ASYNC | Z_DISOWN), c);
+ yylex();
+ r = 1;
+ }
+ }
+ if (!r) {
+ tok = LEXERR;
+ if (errflag) {
+ yyerror(0);
+ ecused--;
+ return 0;
+ }
+ yyerror(1);
+ herrflush();
+ if (noerrs != 2)
+ errflag = 1;
+ ecused--;
+ return 0;
+ } else {
+ int oec = ecused;
+
+ if (!par_event()) {
+ ecused = oec;
+ ecbuf[p] |= wc_bdata(Z_END);
+ }
+ }
+ return 1;
+}
+
+/**/
+mod_export Eprog
+parse_list(void)
+{
+ int c = 0;
+
+ tok = ENDINPUT;
+ incmdpos = 1;
+ yylex();
+ init_parse();
+ par_list(&c);
+ if (tok != ENDINPUT) {
+ clear_hdocs();
+ tok = LEXERR;
+ yyerror(0);
+ return NULL;
+ }
+ return bld_eprog();
+}
+
+/**/
+mod_export Eprog
+parse_cond(void)
+{
+ init_parse();
+
+ if (!par_cond()) {
+ clear_hdocs();
+ 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]);
+ }
+}
+
+/*
+ * list : { SEPER } [ sublist [ { SEPER | AMPER | AMPERBANG } list ] ]
+ */
+
+/**/
+static int
+par_list(int *complex)
+{
+ int p, lp = -1, c;
+
+ rec:
+
+ while (tok == SEPER)
+ yylex();
+
+ p = ecadd(0);
+ c = 0;
+
+ if (par_sublist(&c)) {
+ *complex |= c;
+ if (tok == SEPER || tok == AMPER || tok == AMPERBANG) {
+ 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);
+ 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 0;
+ }
+}
+
+/**/
+static int
+par_list1(int *complex)
+{
+ int p = ecadd(0), c = 0;
+
+ if (par_sublist(&c)) {
+ set_list_code(p, (Z_SYNC | Z_END), c);
+ *complex |= c;
+ return 1;
+ } else {
+ ecused--;
+ return 0;
+ }
+}
+
+/*
+ * sublist : sublist2 [ ( DBAR | DAMPER ) { SEPER } sublist ]
+ */
+
+/**/
+static int
+par_sublist(int *complex)
+{
+ int f, p, c = 0;
+
+ p = ecadd(0);
+
+ if ((f = par_sublist2(&c)) != -1) {
+ int e = ecused;
+
+ *complex |= c;
+ if (tok == DBAR || tok == DAMPER) {
+ int qtok = tok, sl;
+
+ cmdpush(tok == DBAR ? CS_CMDOR : CS_CMDAND);
+ yylex();
+ while (tok == SEPER)
+ yylex();
+ 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();
+ } else
+ set_sublist_code(p, WC_SUBLIST_END, f, (e - 1 - p), c);
+ return 1;
+ } else {
+ ecused--;
+ return 0;
+ }
+}
+
+/*
+ * sublist2 : [ COPROC | BANG ] pline
+ */
+
+/**/
+static int
+par_sublist2(int *complex)
+{
+ int f = 0;
+
+ if (tok == COPROC) {
+ *complex = 1;
+ f |= WC_SUBLIST_COPROC;
+ yylex();
+ } else if (tok == BANG) {
+ *complex = 1;
+ f |= WC_SUBLIST_NOT;
+ yylex();
+ }
+ if (!par_pline(complex) && !f)
+ return -1;
+
+ return f;
+}
+
+/*
+ * pline : cmd [ ( BAR | BARAMP ) { SEPER } pline ]
+ */
+
+/**/
+static int
+par_pline(int *complex)
+{
+ int p, line = lineno;
+
+ p = ecadd(0);
+
+ if (!par_cmd(complex)) {
+ ecused--;
+ return 0;
+ }
+ if (tok == BAR) {
+ *complex = 1;
+ cmdpush(CS_PIPE);
+ yylex();
+ 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;
+ if (!par_pline(complex)) {
+ tok = LEXERR;
+ }
+ cmdpop();
+ return 1;
+ } else if (tok == BARAMP) {
+ int r;
+
+ for (r = p + 1; wc_code(ecbuf[r]) == WC_REDIR; r += 3);
+
+ ecispace(r, 3);
+ ecbuf[r] = WCB_REDIR(REDIR_MERGEOUT);
+ ecbuf[r + 1] = 2;
+ ecbuf[r + 2] = ecstrcode("1");
+
+ *complex = 1;
+ cmdpush(CS_ERRPIPE);
+ yylex();
+ 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;
+ if (!par_pline(complex)) {
+ tok = LEXERR;
+ }
+ cmdpop();
+ return 1;
+ } else {
+ ecbuf[p] = WCB_PIPE(WC_PIPE_END, (line >= 0 ? line + 1 : 0));
+ return 1;
+ }
+}
+
+/*
+ * cmd : { redir } ( for | case | if | while | repeat |
+ * subsh | funcdef | time | dinbrack | dinpar | simple ) { redir }
+ */
+
+/**/
+static int
+par_cmd(int *complex)
+{
+ int r, nr = 0;
+
+ r = ecused;
+
+ if (IS_REDIROP(tok)) {
+ *complex = 1;
+ while (IS_REDIROP(tok)) {
+ nr++;
+ par_redir(&r);
+ }
+ }
+ switch (tok) {
+ case FOR:
+ cmdpush(CS_FOR);
+ par_for(complex);
+ cmdpop();
+ break;
+ case FOREACH:
+ cmdpush(CS_FOREACH);
+ par_for(complex);
+ cmdpop();
+ break;
+ case SELECT:
+ *complex = 1;
+ cmdpush(CS_SELECT);
+ par_for(complex);
+ cmdpop();
+ break;
+ case CASE:
+ cmdpush(CS_CASE);
+ par_case(complex);
+ cmdpop();
+ break;
+ case IF:
+ par_if(complex);
+ break;
+ case WHILE:
+ cmdpush(CS_WHILE);
+ par_while(complex);
+ cmdpop();
+ break;
+ case UNTIL:
+ cmdpush(CS_UNTIL);
+ par_while(complex);
+ cmdpop();
+ break;
+ case REPEAT:
+ cmdpush(CS_REPEAT);
+ par_repeat(complex);
+ cmdpop();
+ break;
+ case INPAR:
+ *complex = 1;
+ cmdpush(CS_SUBSH);
+ par_subsh(complex);
+ cmdpop();
+ break;
+ case INBRACE:
+ cmdpush(CS_CURSH);
+ par_subsh(complex);
+ cmdpop();
+ break;
+ case FUNC:
+ cmdpush(CS_FUNCDEF);
+ par_funcdef();
+ cmdpop();
+ break;
+ case DINBRACK:
+ cmdpush(CS_COND);
+ par_dinbrack();
+ cmdpop();
+ break;
+ case DINPAR:
+ ecadd(WCB_ARITH());
+ ecstr(tokstr);
+ yylex();
+ break;
+ case TIME:
+ {
+ static int inpartime = 0;
+
+ if (!inpartime) {
+ *complex = 1;
+ inpartime = 1;
+ par_time();
+ inpartime = 0;
+ break;
+ }
+ }
+ tok = STRING;
+ /* fall through */
+ default:
+ {
+ 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;
+ }
+ if (IS_REDIROP(tok)) {
+ *complex = 1;
+ while (IS_REDIROP(tok))
+ par_redir(&r);
+ }
+ incmdpos = 1;
+ incasepat = 0;
+ incond = 0;
+ return 1;
+}
+
+/*
+ * for : ( FOR DINPAR expr SEMI expr SEMI expr DOUTPAR |
+ * ( FOR[EACH] | SELECT ) name ( "in" wordlist | INPAR wordlist OUTPAR ) )
+ * { SEPER } ( DO list DONE | INBRACE list OUTBRACE | list ZEND | list1 )
+ */
+
+/**/
+static void
+par_for(int *complex)
+{
+ int oecused = ecused, csh = (tok == FOREACH), p, sel = (tok == SELECT);
+ int type;
+
+ p = ecadd(0);
+
+ incmdpos = 0;
+ infor = tok == FOR ? 2 : 0;
+ yylex();
+ if (tok == DINPAR) {
+ yylex();
+ if (tok != DINPAR)
+ YYERRORV(oecused);
+ ecstr(tokstr);
+ yylex();
+ if (tok != DINPAR)
+ YYERRORV(oecused);
+ ecstr(tokstr);
+ yylex();
+ if (tok != DOUTPAR)
+ YYERRORV(oecused);
+ ecstr(tokstr);
+ infor = 0;
+ incmdpos = 1;
+ yylex();
+ type = WC_FOR_COND;
+ } else {
+ int np = 0, n, posix_in, ona = noaliases, onc = nocorrect;
+ infor = 0;
+ if (tok != STRING || !isident(tokstr))
+ YYERRORV(oecused);
+ if (!sel)
+ np = ecadd(0);
+ n = 0;
+ incmdpos = 1;
+ noaliases = nocorrect = 1;
+ for (;;) {
+ n++;
+ ecstr(tokstr);
+ yylex();
+ if (tok != STRING || !strcmp(tokstr, "in") || sel)
+ break;
+ if (!isident(tokstr) || errflag)
+ {
+ noaliases = ona;
+ nocorrect = onc;
+ YYERRORV(oecused);
+ }
+ }
+ noaliases = ona;
+ nocorrect = onc;
+ if (!sel)
+ ecbuf[np] = n;
+ posix_in = isnewlin;
+ while (isnewlin)
+ yylex();
+ if (tok == STRING && !strcmp(tokstr, "in")) {
+ incmdpos = 0;
+ yylex();
+ np = ecadd(0);
+ n = par_wordlist();
+ if (tok != SEPER)
+ YYERRORV(oecused);
+ ecbuf[np] = n;
+ type = (sel ? WC_SELECT_LIST : WC_FOR_LIST);
+ } else if (!posix_in && tok == INPAR) {
+ incmdpos = 0;
+ yylex();
+ np = ecadd(0);
+ n = par_nl_wordlist();
+ if (tok != OUTPAR)
+ 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 == DOLOOP) {
+ yylex();
+ par_save_list(complex);
+ if (tok != DONE)
+ YYERRORV(oecused);
+ yylex();
+ } else if (tok == INBRACE) {
+ yylex();
+ par_save_list(complex);
+ if (tok != OUTBRACE)
+ YYERRORV(oecused);
+ yylex();
+ } else if (csh || isset(CSHJUNKIELOOPS)) {
+ par_save_list(complex);
+ if (tok != ZEND)
+ YYERRORV(oecused);
+ yylex();
+ } else if (unset(SHORTLOOPS)) {
+ YYERRORV(oecused);
+ } else
+ par_save_list1(complex);
+
+ ecbuf[p] = (sel ?
+ WCB_SELECT(type, ecused - 1 - p) :
+ WCB_FOR(type, ecused - 1 - p));
+}
+
+/*
+ * case : CASE STRING { SEPER } ( "in" | INBRACE )
+ { { SEPER } STRING { BAR STRING } OUTPAR
+ list [ DSEMI | SEMIAMP ] }
+ { SEPER } ( "esac" | OUTBRACE )
+ */
+
+/**/
+static void
+par_case(int *complex)
+{
+ int oecused = ecused, brflag, p, pp, n = 1, type;
+
+ p = ecadd(0);
+
+ incmdpos = 0;
+ yylex();
+ if (tok != STRING)
+ YYERRORV(oecused);
+ ecstr(tokstr);
+
+ incmdpos = 1;
+ yylex();
+ while (tok == SEPER)
+ yylex();
+ if (!(tok == STRING && !strcmp(tokstr, "in")) && tok != INBRACE)
+ YYERRORV(oecused);
+ brflag = (tok == INBRACE);
+ incasepat = 1;
+ incmdpos = 0;
+ yylex();
+
+ for (;;) {
+ char *str;
+
+ while (tok == SEPER)
+ yylex();
+ if (tok == OUTBRACE)
+ break;
+ if (tok == INPAR)
+ yylex();
+ if (tok != STRING)
+ YYERRORV(oecused);
+ if (!strcmp(tokstr, "esac"))
+ break;
+ str = dupstring(tokstr);
+ incasepat = 0;
+ incmdpos = 1;
+ type = WC_CASE_OR;
+ for (;;) {
+ yylex();
+ if (tok == OUTPAR) {
+ incasepat = 0;
+ incmdpos = 1;
+ yylex();
+ break;
+ } else if (tok == BAR) {
+ char *str2;
+ int sl = strlen(str);
+
+ incasepat = 1;
+ incmdpos = 0;
+ str2 = hcalloc(sl + 2);
+ strcpy(str2, str);
+ str2[sl] = Bar;
+ str2[sl+1] = '\0';
+ str = str2;
+ } else {
+ int sl = strlen(str);
+
+ if (!sl || str[sl - 1] != Bar) {
+ /* POSIX allows (foo*) patterns */
+ int pct;
+ char *s;
+
+ for (s = str, pct = 0; *s; s++) {
+ if (*s == Inpar)
+ pct++;
+ if (!pct)
+ break;
+ if (pct == 1) {
+ if (*s == Bar || *s == Inpar)
+ while (iblank(s[1]))
+ chuck(s+1);
+ if (*s == Bar || *s == Outpar)
+ while (iblank(s[-1]) &&
+ (s < str + 1 || s[-2] != Meta))
+ chuck(--s);
+ }
+ if (*s == Outpar)
+ pct--;
+ }
+ 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(oecused);
+ str2 = hcalloc(sl + strlen(tokstr) + 1);
+ strcpy(str2, str);
+ strcpy(str2 + sl, tokstr);
+ str = str2;
+ }
+ }
+ }
+ 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 != DSEMI && tok != SEMIAMP)
+ YYERRORV(oecused);
+ incasepat = 1;
+ incmdpos = 0;
+ yylex();
+ }
+ incmdpos = 1;
+ yylex();
+
+ ecbuf[p] = WCB_CASE(WC_CASE_HEAD, ecused - 1 - p);
+}
+
+/*
+ * if : { ( IF | ELIF ) { SEPER } ( INPAR list OUTPAR | list )
+ { SEPER } ( THEN list | INBRACE list OUTBRACE | list1 ) }
+ [ FI | ELSE list FI | ELSE { SEPER } INBRACE list OUTBRACE ]
+ (you get the idea...?)
+ */
+
+/**/
+static void
+par_if(int *complex)
+{
+ int oecused = ecused, xtok, p, pp, type, usebrace = 0;
+ unsigned char nc;
+
+ p = ecadd(0);
+
+ for (;;) {
+ xtok = tok;
+ cmdpush(xtok == IF ? CS_IF : CS_ELIF);
+ yylex();
+ if (xtok == FI)
+ break;
+ if (xtok == ELSE)
+ break;
+ while (tok == SEPER)
+ yylex();
+ if (!(xtok == IF || xtok == ELIF)) {
+ cmdpop();
+ YYERRORV(oecused);
+ }
+ pp = ecadd(0);
+ type = (xtok == IF ? WC_IF_IF : WC_IF_ELIF);
+ par_save_list(complex);
+ incmdpos = 1;
+ while (tok == SEPER)
+ yylex();
+ xtok = FI;
+ nc = cmdstack[cmdsp - 1] == CS_IF ? CS_IFTHEN : CS_ELIFTHEN;
+ if (tok == THEN) {
+ usebrace = 0;
+ cmdpop();
+ cmdpush(nc);
+ yylex();
+ 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();
+ par_save_list(complex);
+ if (tok != OUTBRACE) {
+ cmdpop();
+ 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();
+ par_save_list(complex);
+ if (tok != OUTBRACE) {
+ cmdpop();
+ YYERRORV(oecused);
+ }
+ } else {
+ par_save_list(complex);
+ if (tok != FI) {
+ cmdpop();
+ YYERRORV(oecused);
+ }
+ }
+ ecbuf[pp] = WCB_IF(WC_IF_ELSE, ecused - 1 - pp);
+ yylex();
+ cmdpop();
+ }
+ ecbuf[p] = WCB_IF(WC_IF_HEAD, ecused - 1 - p);
+}
+
+/*
+ * while : ( WHILE | UNTIL ) ( INPAR list OUTPAR | list ) { SEPER }
+ ( DO list DONE | INBRACE list OUTBRACE | list ZEND )
+ */
+
+/**/
+static void
+par_while(int *complex)
+{
+ int oecused = ecused, p;
+ int type = (tok == UNTIL ? WC_WHILE_UNTIL : WC_WHILE_WHILE);
+
+ p = ecadd(0);
+ yylex();
+ par_save_list(complex);
+ incmdpos = 1;
+ while (tok == SEPER)
+ yylex();
+ if (tok == DOLOOP) {
+ yylex();
+ par_save_list(complex);
+ if (tok != DONE)
+ YYERRORV(oecused);
+ yylex();
+ } else if (tok == INBRACE) {
+ yylex();
+ par_save_list(complex);
+ if (tok != OUTBRACE)
+ YYERRORV(oecused);
+ yylex();
+ } else if (isset(CSHJUNKIELOOPS)) {
+ par_save_list(complex);
+ if (tok != ZEND)
+ YYERRORV(oecused);
+ yylex();
+ } else
+ YYERRORV(oecused);
+
+ ecbuf[p] = WCB_WHILE(type, ecused - 1 - p);
+}
+
+/*
+ * repeat : REPEAT STRING { SEPER } ( DO list DONE | list1 )
+ */
+
+/**/
+static void
+par_repeat(int *complex)
+{
+ int oecused = ecused, p;
+
+ p = ecadd(0);
+
+ incmdpos = 0;
+ yylex();
+ if (tok != STRING)
+ YYERRORV(oecused);
+ ecstr(tokstr);
+ incmdpos = 1;
+ yylex();
+ while (tok == SEPER)
+ yylex();
+ if (tok == DOLOOP) {
+ yylex();
+ par_save_list(complex);
+ if (tok != DONE)
+ YYERRORV(oecused);
+ yylex();
+ } else if (tok == INBRACE) {
+ yylex();
+ par_save_list(complex);
+ if (tok != OUTBRACE)
+ YYERRORV(oecused);
+ yylex();
+ } else if (isset(CSHJUNKIELOOPS)) {
+ par_save_list(complex);
+ if (tok != ZEND)
+ YYERRORV(oecused);
+ yylex();
+ } else if (unset(SHORTLOOPS)) {
+ YYERRORV(oecused);
+ } else
+ par_save_list1(complex);
+
+ ecbuf[p] = WCB_REPEAT(ecused - 1 - p);
+}
+
+/*
+ * subsh : INPAR list OUTPAR |
+ * INBRACE list OUTBRACE [ "always" INBRACE list OUTBRACE ]
+ */
+
+/**/
+static void
+par_subsh(int *complex)
+{
+ int oecused = ecused, otok = tok, p, pp;
+
+ p = ecadd(0);
+ /* Extra word only needed for always block */
+ pp = ecadd(0);
+ yylex();
+ par_list(complex);
+ ecadd(WCB_END());
+ if (tok != ((otok == INPAR) ? OUTPAR : OUTBRACE))
+ YYERRORV(oecused);
+ incmdpos = 1;
+ yylex();
+
+ /* Optional always block. No intervening SEPERs allowed. */
+ if (otok == INBRACE && tok == STRING && !strcmp(tokstr, "always")) {
+ ecbuf[pp] = WCB_TRY(ecused - 1 - pp);
+ incmdpos = 1;
+ do {
+ yylex();
+ } while (tok == SEPER);
+
+ if (tok != INBRACE)
+ YYERRORV(oecused);
+ cmdpop();
+ cmdpush(CS_ALWAYS);
+
+ yylex();
+ par_save_list(complex);
+ while (tok == SEPER)
+ yylex();
+
+ incmdpos = 1;
+
+ if (tok != OUTBRACE)
+ YYERRORV(oecused);
+ yylex();
+ ecbuf[p] = WCB_TRY(ecused - 1 - p);
+ } else {
+ ecbuf[p] = (otok == INPAR ? WCB_SUBSH(ecused - 1 - p) :
+ WCB_CURSH(ecused - 1 - p));
+ }
+}
+
+/*
+ * funcdef : FUNCTION wordlist [ INOUTPAR ] { SEPER }
+ * ( list1 | INBRACE list OUTBRACE )
+ */
+
+/**/
+static void
+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();
+
+ p = ecadd(0);
+ ecadd(0);
+
+ incmdpos = 1;
+ while (tok == STRING) {
+ if (*tokstr == Inbrace && !tokstr[1]) {
+ tok = INBRACE;
+ break;
+ }
+ 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();
+ par_list(&c);
+ if (tok != OUTBRACE) {
+ lineno += oldlineno;
+ ecnpats = onp;
+ ecssub = oecssub;
+ YYERRORV(oecused);
+ }
+ yylex();
+ } else if (unset(SHORTLOOPS)) {
+ lineno += oldlineno;
+ ecnpats = onp;
+ ecssub = oecssub;
+ YYERRORV(oecused);
+ } else
+ 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);
+}
+
+/*
+ * time : TIME sublist2
+ */
+
+/**/
+static void
+par_time(void)
+{
+ int p, f, c = 0;
+
+ yylex();
+
+ p = ecadd(0);
+ ecadd(0);
+ if ((f = par_sublist2(&c)) < 0) {
+ ecused--;
+ ecbuf[p] = WCB_TIMED(WC_TIMED_EMPTY);
+ } else {
+ ecbuf[p] = WCB_TIMED(WC_TIMED_PIPE);
+ set_sublist_code(p + 1, WC_SUBLIST_END, f, ecused - 2 - p, c);
+ }
+}
+
+/*
+ * dinbrack : DINBRACK cond DOUTBRACK
+ */
+
+/**/
+static void
+par_dinbrack(void)
+{
+ int oecused = ecused;
+
+ incond = 1;
+ incmdpos = 0;
+ yylex();
+ par_cond();
+ if (tok != DOUTBRACK)
+ YYERRORV(oecused);
+ incond = 0;
+ incmdpos = 1;
+ yylex();
+}
+
+/*
+ * simple : { COMMAND | EXEC | NOGLOB | NOCORRECT | DASH }
+ { STRING | ENVSTRING | ENVARRAY wordlist OUTPAR | redir }
+ [ INOUTPAR { SEPER } ( list1 | INBRACE list OUTBRACE ) ]
+ */
+
+/**/
+static int
+par_simple(int *complex, int nr)
+{
+ int oecused = ecused, isnull = 1, r, argc = 0, p, isfunc = 0, sr = 0;
+ int c = *complex;
+
+ r = ecused;
+ for (;;) {
+ if (tok == NOCORRECT) {
+ *complex = c = 1;
+ nocorrect = 1;
+ } else if (tok == ENVSTRING) {
+ char *p, *name, *str;
+
+ name = tokstr;
+ for (p = tokstr; *p && *p != Inbrack && *p != '=' && *p != '+';
+ p++);
+ if (*p == Inbrack) skipparens(Inbrack, Outbrack, &p);
+ if (*p == '+') {
+ *p++ = '\0';
+ ecadd(WCB_ASSIGN(WC_ASSIGN_SCALAR, WC_ASSIGN_INC, 0));
+ } else
+ ecadd(WCB_ASSIGN(WC_ASSIGN_SCALAR, WC_ASSIGN_NEW, 0));
+
+ if (*p == '=') {
+ *p = '\0';
+ str = p + 1;
+ } else
+ equalsplit(tokstr, &str);
+ ecstr(name);
+ ecstr(str);
+ isnull = 0;
+ } else if (tok == ENVARRAY) {
+ int oldcmdpos = incmdpos, n, type2;
+
+ p = ecadd(0);
+ incmdpos = 0;
+ if ((type2 = strlen(tokstr) - 1) && tokstr[type2] == '+') {
+ tokstr[type2] = '\0';
+ type2 = WC_ASSIGN_INC;
+ } else
+ type2 = WC_ASSIGN_NEW;
+ ecstr(tokstr);
+ cmdpush(CS_ARRAY);
+ yylex();
+ n = par_nl_wordlist();
+ ecbuf[p] = WCB_ASSIGN(WC_ASSIGN_ARRAY, type2, n);
+ cmdpop();
+ if (tok != OUTPAR)
+ YYERROR(oecused);
+ incmdpos = oldcmdpos;
+ isnull = 0;
+ } else
+ break;
+ yylex();
+ }
+ if (tok == AMPER || tok == AMPERBANG)
+ YYERROR(oecused);
+
+ p = ecadd(WCB_SIMPLE(0));
+
+ for (;;) {
+ if (tok == STRING) {
+ *complex = 1;
+ incmdpos = 0;
+ ecstr(tokstr);
+ argc++;
+ yylex();
+ } else if (IS_REDIROP(tok)) {
+ *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();
+ par_list(&c);
+ if (tok != OUTBRACE) {
+ cmdpop();
+ lineno += oldlineno;
+ ecnpats = onp;
+ ecssub = oecssub;
+ YYERROR(oecused);
+ }
+ yylex();
+ } else {
+ int ll, sl, pl, c = 0;
+
+ ll = ecadd(0);
+ sl = ecadd(0);
+ pl = ecadd(WCB_PIPE(WC_PIPE_END, 0));
+
+ par_cmd(&c);
+ if (!c)
+ YYERROR(oecused);
+
+ set_sublist_code(sl, WC_SUBLIST_END, 0, ecused - 1 - sl, c);
+ set_list_code(ll, (Z_SYNC | Z_END), c);
+ }
+ cmdpop();
+
+ 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;
+ isnull = 0;
+ break;
+ } else
+ break;
+ isnull = 0;
+ }
+ if (isnull && !(sr + nr)) {
+ ecused = p;
+ return 0;
+ }
+ incmdpos = 1;
+
+ if (!isfunc)
+ ecbuf[p] = WCB_SIMPLE(argc);
+
+ return sr + 1;
+}
+
+/*
+ * redir : ( OUTANG | ... | TRINANG ) STRING
+ */
+
+static int redirtab[TRINANG - OUTANG + 1] = {
+ REDIR_WRITE,
+ REDIR_WRITENOW,
+ REDIR_APP,
+ REDIR_APPNOW,
+ REDIR_READ,
+ REDIR_READWRITE,
+ REDIR_HEREDOC,
+ REDIR_HEREDOCDASH,
+ REDIR_MERGEIN,
+ REDIR_MERGEOUT,
+ REDIR_ERRWRITE,
+ REDIR_ERRWRITENOW,
+ REDIR_ERRAPP,
+ REDIR_ERRAPPNOW,
+ REDIR_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 REDIR_HEREDOC:
+ case REDIR_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 REDIR_WRITE:
+ case REDIR_WRITENOW:
+ if (tokstr[0] == Outang && tokstr[1] == Inpar)
+ /* > >(...) */
+ type = REDIR_OUTPIPE;
+ else if (tokstr[0] == Inang && tokstr[1] == Inpar)
+ YYERRORV(ecused);
+ break;
+ case REDIR_READ:
+ if (tokstr[0] == Inang && tokstr[1] == Inpar)
+ /* < <(...) */
+ type = REDIR_INPIPE;
+ else if (tokstr[0] == Outang && tokstr[1] == Inpar)
+ YYERRORV(ecused);
+ break;
+ case REDIR_READWRITE:
+ if ((tokstr[0] == Inang || tokstr[0] == Outang) && tokstr[1] == Inpar)
+ type = tokstr[0] == Inang ? REDIR_INPIPE : REDIR_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;
+}
+
+/*
+ * condlex is yylex for normal parsing, but is altered to allow
+ * the test builtin to use par_cond.
+ */
+
+/**/
+void (*condlex) _((void)) = yylex;
+
+/*
+ * cond : cond_1 { SEPER } [ DBAR { SEPER } cond ]
+ */
+
+/**/
+static int
+par_cond(void)
+{
+ int p = ecused, r;
+
+ r = par_cond_1();
+ while (tok == SEPER)
+ condlex();
+ if (tok == DBAR) {
+ condlex();
+ while (tok == SEPER)
+ condlex();
+ ecispace(p, 1);
+ par_cond();
+ ecbuf[p] = WCB_COND(COND_OR, ecused - 1 - p);
+ return 1;
+ }
+ return r;
+}
+
+/*
+ * cond_1 : cond_2 { SEPER } [ DAMPER { SEPER } cond_1 ]
+ */
+
+/**/
+static int
+par_cond_1(void)
+{
+ int r, p = ecused;
+
+ r = par_cond_2();
+ while (tok == SEPER)
+ condlex();
+ if (tok == DAMPER) {
+ condlex();
+ while (tok == SEPER)
+ condlex();
+ ecispace(p, 1);
+ par_cond_1();
+ ecbuf[p] = WCB_COND(COND_AND, ecused - 1 - p);
+ return 1;
+ }
+ return r;
+}
+
+/*
+ * cond_2 : BANG cond_2
+ | INPAR { SEPER } cond_2 { SEPER } OUTPAR
+ | STRING STRING STRING
+ | STRING STRING
+ | STRING ( INANG | OUTANG ) STRING
+ */
+
+/**/
+static int
+par_cond_2(void)
+{
+ char *s1, *s2, *s3;
+ int dble = 0;
+
+ if (condlex == testlex) {
+ /* See the description of test in POSIX 1003.2 */
+ if (tok == NULLTOK)
+ /* no arguments: false */
+ return par_cond_double(dupstring("-n"), dupstring(""));
+ if (!*testargs) {
+ /* one argument: [ foo ] is equivalent to [ -n foo ] */
+ s1 = tokstr;
+ condlex();
+ return par_cond_double(dupstring("-n"), s1);
+ }
+ if (testargs[1]) {
+ /* three arguments: if the second argument is a binary operator, *
+ * perform that binary test on the first and the third argument */
+ if (!strcmp(*testargs, "=") ||
+ !strcmp(*testargs, "==") ||
+ !strcmp(*testargs, "!=") ||
+ (**testargs == '-' && get_cond_num(*testargs + 1) >= 0)) {
+ s1 = tokstr;
+ condlex();
+ s2 = tokstr;
+ condlex();
+ s3 = tokstr;
+ condlex();
+ return par_cond_triple(s1, s2, s3);
+ }
+ }
+ }
+ if (tok == BANG) {
+ condlex();
+ ecadd(WCB_COND(COND_NOT, 0));
+ return par_cond_2();
+ }
+ if (tok == INPAR) {
+ int r;
+
+ condlex();
+ while (tok == SEPER)
+ condlex();
+ r = par_cond();
+ while (tok == SEPER)
+ condlex();
+ if (tok != OUTPAR)
+ YYERROR(ecused);
+ condlex();
+ return r;
+ }
+ if (tok != STRING) {
+ if (tok && tok != LEXERR && condlex == testlex) {
+ s1 = tokstr;
+ condlex();
+ return par_cond_double("-n", s1);
+ } else
+ YYERROR(ecused);
+ }
+ s1 = tokstr;
+ if (condlex == testlex)
+ dble = (*s1 == '-' && strspn(s1+1, "abcdefghknoprstuwxzLONGS") == 1
+ && !s1[2]);
+ condlex();
+ if (tok == INANG || tok == OUTANG) {
+ int xtok = tok;
+ condlex();
+ if (tok != STRING)
+ YYERROR(ecused);
+ s3 = tokstr;
+ condlex();
+ ecadd(WCB_COND((xtok == INANG ? COND_STRLT : COND_STRGTR), 0));
+ ecstr(s1);
+ ecstr(s3);
+ return 1;
+ }
+ 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(ecused);
+ }
+ s2 = tokstr;
+ incond++; /* parentheses do globbing */
+ condlex();
+ incond--; /* parentheses do grouping */
+ if (tok == STRING && !dble) {
+ s3 = tokstr;
+ condlex();
+ 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);
+}
+
+/**/
+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
+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
+yyerror(int noerr)
+{
+ int t0;
+ char *t;
+
+ if ((t = dupstring(yytext)))
+ untokenize(t);
+
+ 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;
+}
+
+/**/
+mod_export Eprog
+dupeprog(Eprog p, int heap)
+{
+ Eprog r;
+ int i;
+ Patprog *pp;
+
+ if (p == &dummy_eprog)
+ return p;
+
+ 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;
+ /*
+ * If Eprog is on the heap, reference count is not valid.
+ * Otherwise, initialise reference count to 1 so that a freeeprog()
+ * will delete it if it is not in use.
+ */
+ r->nref = heap ? -1 : 1;
+ pp = r->pats = (heap ? (Patprog *) hcalloc(r->len) :
+ (Patprog *) zshcalloc(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;
+}
+
+
+/*
+ * Pair of functions to mark an Eprog as in use, and to delete it
+ * when it is no longer in use, by means of the reference count in
+ * then nref element.
+ *
+ * If nref is negative, the Eprog is on the heap and is never freed.
+ */
+
+/* Increase the reference count of an Eprog so it won't be deleted. */
+
+/**/
+mod_export void
+useeprog(Eprog p)
+{
+ if (p && p != &dummy_eprog && p->nref >= 0)
+ p->nref++;
+}
+
+/* Free an Eprog if we have finished with it */
+
+/**/
+mod_export void
+freeeprog(Eprog p)
+{
+ int i;
+ Patprog *pp;
+
+ if (p && p != &dummy_eprog) {
+ /* paranoia */
+ DPUTS(p->nref > 0 && (p->flags & EF_HEAP), "Heap EPROG has nref > 0");
+ DPUTS(p->nref < 0 && !(p->flags & EF_HEAP), "Real EPROG has nref < 0");
+ DPUTS(p->nref < -1, "Uninitialised EPROG nref");
+#ifdef MAX_FUNCTION_DEPTH
+ DPUTS(p->nref > MAX_FUNCTION_DEPTH + 10, "Overlarge EPROG nref");
+#endif
+ if (p->nref > 0 && !--p->nref) {
+ 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));
+ }
+ }
+}
+
+/**/
+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);
+ }
+ 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);
+}
+
+/**/
+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);
+ }
+}
+
+/**/
+char **
+ecgetarr(Estate s, int num, int dup, int *tok)
+{
+ char **ret, **rp;
+ int tf = 0, tmp = 0;
+
+ ret = rp = (char **) zhalloc((num + 1) * sizeof(char *));
+
+ while (num--) {
+ *rp++ = ecgetstr(s, dup, &tmp);
+ tf |= tmp;
+ }
+ *rp = NULL;
+ if (tok)
+ *tok = tf;
+
+ return ret;
+}
+
+/**/
+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;
+}
+
+/* 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.
+ *
+ * NOTES:
+ * - This layout has to be kept; everything after it may be changed.
+ * - When incompatible changes are made, the FD_MAGIC and FD_OMAGIC
+ * numbers have to be changed.
+ *
+ * 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 0x04050607
+#define FD_OMAGIC 0x07060504
+
+#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;
+}
+#ifndef __SYMBIAN32__
+/**/
+int
+bin_zcompile(char *nam, char **args, Options ops, UNUSED(int func))
+{
+ int map, flags, ret;
+ char *dump;
+#ifdef __SYMBIAN32__
+ func=func;
+#endif
+ if ((OPT_ISSET(ops,'k') && OPT_ISSET(ops,'z')) ||
+ (OPT_ISSET(ops,'R') && OPT_ISSET(ops,'M')) ||
+ (OPT_ISSET(ops,'c') &&
+ (OPT_ISSET(ops,'U') || OPT_ISSET(ops,'k') || OPT_ISSET(ops,'z'))) ||
+ (!(OPT_ISSET(ops,'c') || OPT_ISSET(ops,'a')) && OPT_ISSET(ops,'m'))) {
+ zwarnnam(nam, "illegal combination of options", NULL, 0);
+ return 1;
+ }
+ if ((OPT_ISSET(ops,'c') || OPT_ISSET(ops,'a')) && isset(KSHAUTOLOAD))
+ zwarnnam(nam, "functions will use zsh style autoloading", NULL, 0);
+
+ flags = (OPT_ISSET(ops,'k') ? FDHF_KSHLOAD :
+ (OPT_ISSET(ops,'z') ? FDHF_ZSHLOAD : 0));
+
+ if (OPT_ISSET(ops,'t')) {
+ Wordcode f;
+
+ if (!*args) {
+ zwarnnam(nam, "too few arguments", NULL, 0);
+ return 1;
+ }
+ if (!(f = load_dump_header(nam, (strsfx(FD_EXT, *args) ? *args :
+ dyncat(*args, FD_EXT)), 1)))
+ 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("zwc 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;
+ }
+ map = (OPT_ISSET(ops,'M') ? 2 : (OPT_ISSET(ops,'R') ? 0 : 1));
+
+ if (!args[1] && !(OPT_ISSET(ops,'c') || OPT_ISSET(ops,'a'))) {
+ queue_signals();
+ ret = build_dump(nam, dyncat(*args, FD_EXT), args, OPT_ISSET(ops,'U'),
+ map, flags);
+ unqueue_signals();
+ return ret;
+ }
+ dump = (strsfx(FD_EXT, *args) ? *args : dyncat(*args, FD_EXT));
+
+ queue_signals();
+ ret = ((OPT_ISSET(ops,'c') || OPT_ISSET(ops,'a')) ?
+ build_cur_dump(nam, dump, args + 1, OPT_ISSET(ops,'m'), map,
+ (OPT_ISSET(ops,'c') ? 1 : 0) |
+ (OPT_ISSET(ops,'a') ? 2 : 0)) :
+ build_dump(nam, dump, args + 1, OPT_ISSET(ops,'U'), map, flags));
+ unqueue_signals();
+
+ return ret;
+}
+#endif //__SYMBIAN32__
+
+/* Load the header of a dump file. Returns NULL if the file isn't a
+ * valid dump file. */
+
+/**/
+static Wordcode
+load_dump_header(char *nam, char *name, int err)
+{
+ int fd, v = 0;
+ wordcode buf[FD_PRELEN + 1];
+
+ if ((fd = open(name, O_RDONLY)) < 0) {
+ if (err)
+ zwarnnam(nam, "can't open zwc file: %s", name, 0);
+ return NULL;
+ }
+ if (read(fd, buf, (FD_PRELEN + 1) * sizeof(wordcode)) !=
+ ((FD_PRELEN + 1) * sizeof(wordcode)) ||
+ (v = (fdmagic(buf) != FD_MAGIC && fdmagic(buf) != FD_OMAGIC))) {
+ if (err) {
+ if (v) {
+ char msg[80];
+
+ sprintf(msg, "zwc file has wrong version (zsh-%s): %%s",
+ fdversion(buf));
+ zwarnnam(nam, msg , name, 0);
+ } else
+ zwarnnam(nam, "invalid zwc file: %s" , name, 0);
+ }
+ 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))) {
+ zwarnnam(nam, "invalid zwc file: %s" , name, 0);
+ close(fd);
+ return NULL;
+ }
+ len = fdheaderlen(buf) * sizeof(wordcode);
+ head = (Wordcode) zhalloc(len);
+ }
+ memcpy(head, buf, (FD_PRELEN + 1) * sizeof(wordcode));
+
+ len -= (FD_PRELEN + 1) * sizeof(wordcode);
+ if (read(fd, head + (FD_PRELEN + 1), len) != len) {
+ close(fd);
+ zwarnnam(nam, "invalid zwc file: %s" , name, 0);
+ 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
+build_dump(char *nam, char *dump, char **files, int ali, int map, int flags)
+{
+ 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);
+
+ unlink(dump);
+ if ((dfd = open(dump, O_WRONLY|O_CREAT, 0444)) < 0) {
+ zwarnnam(nam, "can't write zwc 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)) || 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);
+
+ 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 int
+build_cur_dump(char *nam, char *dump, char **names, int match, int map,
+ int what)
+{
+ int dfd, hlen, tlen;
+ LinkList progs, lnames;
+ Shfunc shf = NULL;
+
+ if (!strsfx(FD_EXT, dump))
+ dump = dyncat(dump, FD_EXT);
+
+ unlink(dump);
+ if ((dfd = open(dump, O_WRONLY|O_CREAT, 0444)) < 0) {
+ zwarnnam(nam, "can't write zwc 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;
+
+/**/
+static int
+zwcstat(char *filename, struct stat *buf, FuncDump dumps)
+{
+ if (stat(filename, buf)) {
+#ifdef HAVE_FSTAT
+ FuncDump f;
+
+ for (f = dumps; f; f = f->next) {
+ if (!strncmp(filename, f->filename, strlen(f->filename)) &&
+ !fstat(f->fd, buf))
+ return 0;
+ }
+#endif
+ return 1;
+ } else return 0;
+}
+
+/* Load a dump file (i.e. map it). */
+
+static void
+load_dump_file(char *dump, struct stat *sbuf, int other, int len)
+{
+ FuncDump d;
+ Wordcode addr;
+ int fd, off, mlen;
+
+ 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;
+ mlen = len + (len - off);
+ } else {
+ off = 0;
+ mlen = len;
+ }
+ if ((fd = open(dump, O_RDONLY)) < 0)
+ return;
+
+ fd = movefd(fd);
+
+ if ((addr = (Wordcode) mmap(NULL, mlen, PROT_READ, MAP_SHARED, fd, off)) ==
+ ((Wordcode) -1)) {
+ close(fd);
+ return;
+ }
+ d = (FuncDump) zalloc(sizeof(*d));
+ d->next = dumps;
+ dumps = d;
+ d->dev = sbuf->st_dev;
+ d->ino = sbuf->st_ino;
+ d->fd = fd;
+ d->map = addr + (other ? (len - off) / sizeof(wordcode) : 0);
+ d->addr = addr;
+ d->len = len;
+ d->count = 0;
+ d->filename = ztrdup(dump);
+}
+
+#else
+
+#define zwcstat(f, b, d) stat(f, b)
+
+#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>). */
+
+/**/
+Eprog
+try_dump_file(char *path, char *name, char *file, int *ksh)
+{
+ Eprog prog;
+ struct stat std, stc, stn;
+ int rd, rc, rn;
+ char *dig, *wc;
+
+ if (strsfx(FD_EXT, path)) {
+ queue_signals();
+ prog = check_dump_file(path, NULL, name, ksh);
+ unqueue_signals();
+ return prog;
+ }
+ dig = dyncat(path, FD_EXT);
+ wc = dyncat(file, FD_EXT);
+
+ rd = zwcstat(dig, &std, dumps);
+ 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. */
+ queue_signals();
+ if (!rd &&
+ (rc || std.st_mtime > stc.st_mtime) &&
+ (rn || std.st_mtime > stn.st_mtime) &&
+ (prog = check_dump_file(dig, &std, name, ksh))) {
+ unqueue_signals();
+ 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, &stc, name, ksh))) {
+ unqueue_signals();
+ return prog;
+ }
+ /* No compiled file for the function. The caller (getfpfunc() will
+ * check if the directory contains the uncompiled file for it. */
+ unqueue_signals();
+ return NULL;
+}
+
+/* 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 = strrchr(file, '\\')))
+ tail++;
+ else
+ tail = file;
+
+ if (strsfx(FD_EXT, file)) {
+ queue_signals();
+ prog = check_dump_file(file, NULL, tail, NULL);
+ unqueue_signals();
+ return prog;
+ }
+ wc = dyncat(file, FD_EXT);
+
+ rc = stat(wc, &stc);
+ rn = stat(file, &stn);
+
+ queue_signals();
+ if (!rc && (rn || stc.st_mtime > stn.st_mtime) &&
+ (prog = check_dump_file(wc, &stc, tail, NULL))) {
+ unqueue_signals();
+ return prog;
+ }
+ unqueue_signals();
+ 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, struct stat *sbuf, char *name, int *ksh)
+{
+ int isrec = 0;
+ Wordcode d;
+ FDHead h;
+ FuncDump f;
+ struct stat lsbuf;
+
+ if (!sbuf) {
+ if (zwcstat(file, &lsbuf, dumps))
+ return NULL;
+ sbuf = &lsbuf;
+ }
+
+#ifdef USE_MMAP
+
+ rec:
+
+#endif
+
+ d = NULL;
+
+#ifdef USE_MMAP
+
+ for (f = dumps; f; f = f->next)
+ if (f->dev == sbuf->st_dev && f->ino == sbuf->st_ino) {
+ d = f->map;
+ break;
+ }
+
+#else
+
+ f = NULL;
+
+#endif
+
+ if (!f && (isrec || !(d = load_dump_header(NULL, file, 0))))
+ 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->nref = 1; /* allocated from permanent storage */
+ 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, sbuf, (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) != (int)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->nref = 1; /* allocated from permanent storage */
+ 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->filename);
+ zfree(f, sizeof(*f));
+ }
+ }
+}
+
+/**/
+mod_export void
+closedumps(void)
+{
+ FuncDump p;
+
+ for (p = dumps; p; p = p->next)
+ zclose(p->fd);
+}
+
+#else
+
+void
+incrdumpcount(FuncDump f)
+{
+}
+
+void
+decrdumpcount(FuncDump f)
+{
+}
+
+/**/
+mod_export void
+closedumps(void)
+{
+}
+
+#endif
+
+/**/
+int
+dump_autoload(char *nam, char *file, int on, Options 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(nam, file, 1)))
+ return 1;
+
+ for (n = firstfdhead(h), e = (FDHead) (h + fdheaderlen(h)); n < e;
+ n = nextfdhead(n)) {
+ shf = (Shfunc) zshcalloc(sizeof *shf);
+ shf->flags = on;
+ shf->funcdef = mkautofn(shf);
+ shfunctab->addnode(shfunctab, ztrdup(fdname(n) + fdhtail(n)), shf);
+ if (OPT_ISSET(ops,'X') && eval_autoload(shf, shf->nam, ops, func))
+ ret = 1;
+ }
+ return ret;
+}
+