diff options
Diffstat (limited to 'dotfiles/system/.zsh/modules/Src/parse.c')
| -rw-r--r-- | dotfiles/system/.zsh/modules/Src/parse.c | 3977 | 
1 files changed, 3977 insertions, 0 deletions
| diff --git a/dotfiles/system/.zsh/modules/Src/parse.c b/dotfiles/system/.zsh/modules/Src/parse.c new file mode 100644 index 0000000..83383f1 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Src/parse.c @@ -0,0 +1,3977 @@ +/* + * parse.c - parser + * + * 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" + +/* != 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; +  +/* + * 1 if we are about to read a case pattern + * -1 if we are not quite sure + * 0 otherwise + */ +  +/**/ +int incasepat; +  +/* != 0 if we just read a newline */ +  +/**/ +int isnewlin; + +/* != 0 if we are after a for keyword */ + +/**/ +int infor; + +/* != 0 if we are after a repeat keyword; if it's nonzero it's a 1-based index + * of the current token from the last-seen command position */ + +/**/ +int inrepeat_; /* trailing underscore because of name clash with Zle/zle_vi.c */ + +/* != 0 if parsing arguments of typeset etc. */ + +/**/ +mod_export int intypeset; + +/* 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);				\ +	herrflush();				\ +	if (noerrs != 2)			\ +	    errflag |= ERRFLAG_ERROR;		\ +	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 + *     - for the extended form {var}>... where the fd is assigned + *       to var, there is an extra item to contain var + * + *   WC_ASSIGN + *     - data contains type (scalar, array) and number of array-elements + *     - followed by name and value + *     Note variant for WC_TYPESET assignments: WC_ASSIGN_INC indicates + *     a name with no equals, not an =+ which isn't valid here. + * + *   WC_SIMPLE + *     - data contains the number of arguments (plus command) + *     - followed by strings + * + *   WC_TYPESET + *     Variant of WC_SIMPLE used when TYPESET reserved word found. + *     - data contains the number of string arguments (plus command) + *     - followed by strings + *     - followed by number of assignments + *     - followed by assignments if non-zero number. + * + *   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 TESTAND (;|), + *       data is offset to next case + *     - each OR/AND/TESTAND 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 `cmplx', 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 + +/* save parse context */ + +/**/ +void +parse_context_save(struct parse_stack *ps, int toplevel) +{ +    (void)toplevel; + +    ps->incmdpos = incmdpos; +    ps->aliasspaceflag = aliasspaceflag; +    ps->incond = incond; +    ps->inredir = inredir; +    ps->incasepat = incasepat; +    ps->isnewlin = isnewlin; +    ps->infor = infor; +    ps->inrepeat_ = inrepeat_; +    ps->intypeset = intypeset; + +    ps->hdocs = hdocs; +    ps->eclen = eclen; +    ps->ecused = ecused; +    ps->ecnpats = ecnpats; +    ps->ecbuf = ecbuf; +    ps->ecstrs = ecstrs; +    ps->ecsoffs = ecsoffs; +    ps->ecssub = ecssub; +    ps->ecnfunc = ecnfunc; +    ecbuf = NULL; +    hdocs = NULL; +} + +/* restore parse context */ + +/**/ +void +parse_context_restore(const struct parse_stack *ps, int toplevel) +{ +    (void)toplevel; + +    if (ecbuf) +	zfree(ecbuf, eclen); + +    incmdpos = ps->incmdpos; +    aliasspaceflag = ps->aliasspaceflag; +    incond = ps->incond; +    inredir = ps->inredir; +    incasepat = ps->incasepat; +    isnewlin = ps->isnewlin; +    infor = ps->infor; +    inrepeat_ = ps->inrepeat_; +    intypeset = ps->intypeset; + +    hdocs = ps->hdocs; +    eclen = ps->eclen; +    ecused = ps->ecused; +    ecnpats = ps->ecnpats; +    ecbuf = ps->ecbuf; +    ecstrs = ps->ecstrs; +    ecsoffs = ps->ecsoffs; +    ecssub = ps->ecssub; +    ecnfunc = ps->ecnfunc; + +    errflag &= ~ERRFLAG_ERROR; +} + +/* 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; + +    return ecused++; +} + +/* 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; + +    unsigned val = hasher(s); + +    if ((l = strlen(s) + 1) && l <= 4) { +	t = has_token(s); +	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 = (((signed)p->hashval) - ((signed)val))) && !(cmp = strcmp(p->str, s))) { +		return p->offs; +            } +	    pp = (cmp < 0 ? &(p->left) : &(p->right)); +	} + +        t = has_token(s); + +	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; +        p->hashval = val; +	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) + + +/**/ +mod_export void +init_parse_status(void) +{ +    /* +     * These variables are currently declared by the parser, so we +     * initialise them here.  Possibly they are more naturally declared +     * by the lexical anaylser; however, as they are used for signalling +     * between the two it's a bit ambiguous.  We clear them when +     * using the lexical analyser for strings as well as here. +     */ +    incasepat = incond = inredir = infor = intypeset = 0; +    inrepeat_ = 0; +    incmdpos = 1; +} + +/* Initialise wordcode buffer. */ + +/**/ +void +init_parse(void) +{ +    queue_signals(); + +    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; + +    init_parse_status(); + +    unqueue_signals(); +} + +/* Build eprog. */ + +/* careful: copy_ecstr is from arg1 to arg2, unlike memcpy */ + +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(int heap) +{ +    Eprog ret; +    int l; + +    queue_signals(); + +    ecadd(WCB_END()); + +    ret = heap ? (Eprog) zhalloc(sizeof(*ret)) : (Eprog) zalloc(sizeof(*ret)); +    ret->len = ((ecnpats * sizeof(Patprog)) + +		(ecused * sizeof(wordcode)) + +		ecsoffs); +    ret->npats = ecnpats; +    ret->nref = heap ? -1 : 1; +    ret->pats = heap ? (Patprog *) zhalloc(ret->len) : +	(Patprog *) zshcalloc(ret->len); +    ret->prog = (Wordcode) (ret->pats + ecnpats); +    ret->strs = (char *) (ret->prog + ecused); +    ret->shf = NULL; +    ret->flags = heap ? EF_HEAP : EF_REAL; +    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; + +    unqueue_signals(); + +    return ret; +} + +/**/ +mod_export int +empty_eprog(Eprog p) +{ +    return (!p || !p->prog || *p->prog == WCB_END()); +} + +static void +clear_hdocs(void) +{ +    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 ] + * + * cmdsubst indicates our event is part of a command-style + * substitution terminated by the token indicationg, usual closing + * parenthesis.  In other cases endtok is ENDINPUT. + */ + +/**/ +Eprog +parse_event(int endtok) +{ +    tok = ENDINPUT; +    incmdpos = 1; +    aliasspaceflag = 0; +    zshlex(); +    init_parse(); + +    if (!par_event(endtok)) { +        clear_hdocs(); +        return NULL; +    } +    if (endtok != ENDINPUT) { +	/* don't need to build an eprog for this */ +	return &dummy_eprog; +    } +    return bld_eprog(1); +} + +/**/ +int +par_event(int endtok) +{ +    int r = 0, p, c = 0; + +    while (tok == SEPER) { +	if (isnewlin > 0 && endtok == ENDINPUT) +	    return 0; +	zshlex(); +    } +    if (tok == ENDINPUT) +	return 0; +    if (tok == endtok) +	return 1; + +    p = ecadd(0); + +    if (par_sublist(&c)) { +	if (tok == ENDINPUT || tok == endtok) { +	    set_list_code(p, Z_SYNC, c); +	    r = 1; +	} else if (tok == SEPER) { +	    set_list_code(p, Z_SYNC, c); +	    if (isnewlin <= 0 || endtok != ENDINPUT) +		zshlex(); +	    r = 1; +	} else if (tok == AMPER) { +	    set_list_code(p, Z_ASYNC, c); +	    zshlex(); +	    r = 1; +	} else if (tok == AMPERBANG) { +	    set_list_code(p, (Z_ASYNC | Z_DISOWN), c); +	    zshlex(); +	    r = 1; +	} +    } +    if (!r) { +	tok = LEXERR; +	if (errflag) { +	    yyerror(0); +	    ecused--; +	    return 0; +	} +	yyerror(1); +	herrflush(); +	if (noerrs != 2) +	    errflag |= ERRFLAG_ERROR; +	ecused--; +	return 0; +    } else { +	int oec = ecused; + +	if (!par_event(endtok)) { +	    ecused = oec; +	    ecbuf[p] |= wc_bdata(Z_END); +	    return errflag ? 0 : 1; +	} +    } +    return 1; +} + +/**/ +mod_export Eprog +parse_list(void) +{ +    int c = 0; + +    tok = ENDINPUT; +    init_parse(); +    zshlex(); +    par_list(&c); +    if (tok != ENDINPUT) { +        clear_hdocs(); +	tok = LEXERR; +	yyerror(0); +	return NULL; +    } +    return bld_eprog(1); +} + +/* + * This entry point is only used for bin_test, our attempt to + * provide compatibility with /bin/[ and /bin/test.  Hence + * at this point condlex should always be set to testlex. + */ + +/**/ +mod_export Eprog +parse_cond(void) +{ +    init_parse(); + +    if (!par_cond()) { +        clear_hdocs(); +	return NULL; +    } +    return bld_eprog(1); +} + +/* 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 cmplx) +{ +    if (!cmplx && (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 cmplx) +{ +    if (cmplx) +	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 void +par_list(int *cmplx) +{ +    int p, lp = -1, c; + + rec: + +    while (tok == SEPER) +	zshlex(); + +    p = ecadd(0); +    c = 0; + +    if (par_sublist(&c)) { +	*cmplx |= c; +	if (tok == SEPER || tok == AMPER || tok == AMPERBANG) { +	    if (tok != SEPER) +		*cmplx = 1; +	    set_list_code(p, ((tok == SEPER) ? Z_SYNC : +			      (tok == AMPER) ? Z_ASYNC : +			      (Z_ASYNC | Z_DISOWN)), c); +	    incmdpos = 1; +	    do { +		zshlex(); +	    } while (tok == SEPER); +	    lp = p; +	    goto rec; +	} else +	    set_list_code(p, (Z_SYNC | Z_END), c); +    } else { +	ecused--; +	if (lp >= 0) +	    ecbuf[lp] |= wc_bdata(Z_END); +    } +} + +/**/ +static void +par_list1(int *cmplx) +{ +    int p = ecadd(0), c = 0; + +    if (par_sublist(&c)) { +	set_list_code(p, (Z_SYNC | Z_END), c); +	*cmplx |= c; +    } else +	ecused--; +} + +/* + * sublist	: sublist2 [ ( DBAR | DAMPER ) { SEPER } sublist ] + */ + +/**/ +static int +par_sublist(int *cmplx) +{ +    int f, p, c = 0; + +    p = ecadd(0); + +    if ((f = par_sublist2(&c)) != -1) { +	int e = ecused; + +	*cmplx |= c; +	if (tok == DBAR || tok == DAMPER) { +	    enum lextok qtok = tok; +	    int sl; + +	    cmdpush(tok == DBAR ? CS_CMDOR : CS_CMDAND); +	    zshlex(); +	    while (tok == SEPER) +		zshlex(); +	    sl = par_sublist(cmplx); +	    set_sublist_code(p, (sl ? (qtok == DBAR ? +				       WC_SUBLIST_OR : WC_SUBLIST_AND) : +				 WC_SUBLIST_END), +			     f, (e - 1 - p), c); +	    cmdpop(); +	} else { +	    if (tok == AMPER || tok == AMPERBANG) { +		c = 1; +		*cmplx |= c; +	    }		 +	    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 *cmplx) +{ +    int f = 0; + +    if (tok == COPROC) { +	*cmplx = 1; +	f |= WC_SUBLIST_COPROC; +	zshlex(); +    } else if (tok == BANG) { +	*cmplx = 1; +	f |= WC_SUBLIST_NOT; +	zshlex(); +    } +    if (!par_pline(cmplx) && !f) +	return -1; + +    return f; +} + +/* + * pline	: cmd [ ( BAR | BARAMP ) { SEPER } pline ] + */ + +/**/ +static int +par_pline(int *cmplx) +{ +    int p; +    zlong line = toklineno; + +    p = ecadd(0); + +    if (!par_cmd(cmplx, 0)) { +	ecused--; +	return 0; +    } +    if (tok == BAR) { +	*cmplx = 1; +	cmdpush(CS_PIPE); +	zshlex(); +	while (tok == SEPER) +	    zshlex(); +	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(cmplx)) { +	    tok = LEXERR; +	} +	cmdpop(); +	return 1; +    } else if (tok == BARAMP) { +	int r; + +	for (r = p + 1; wc_code(ecbuf[r]) == WC_REDIR; +	     r += WC_REDIR_WORDS(ecbuf[r])); + +	ecispace(r, 3); +	ecbuf[r] = WCB_REDIR(REDIR_MERGEOUT); +	ecbuf[r + 1] = 2; +	ecbuf[r + 2] = ecstrcode("1"); + +	*cmplx = 1; +	cmdpush(CS_ERRPIPE); +	zshlex(); +	while (tok == SEPER) +	    zshlex(); +	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(cmplx)) { +	    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 } + * + * zsh_construct is passed through to par_subsh(), q.v. + */ + +/**/ +static int +par_cmd(int *cmplx, int zsh_construct) +{ +    int r, nr = 0; + +    r = ecused; + +    if (IS_REDIROP(tok)) { +	*cmplx = 1; +	while (IS_REDIROP(tok)) { +	    nr += par_redir(&r, NULL); +	} +    } +    switch (tok) { +    case FOR: +	cmdpush(CS_FOR); +	par_for(cmplx); +	cmdpop(); +	break; +    case FOREACH: +	cmdpush(CS_FOREACH); +	par_for(cmplx); +	cmdpop(); +	break; +    case SELECT: +	*cmplx = 1; +	cmdpush(CS_SELECT); +	par_for(cmplx); +	cmdpop(); +	break; +    case CASE: +	cmdpush(CS_CASE); +	par_case(cmplx); +	cmdpop(); +	break; +    case IF: +	par_if(cmplx); +	break; +    case WHILE: +	cmdpush(CS_WHILE); +	par_while(cmplx); +	cmdpop(); +	break; +    case UNTIL: +	cmdpush(CS_UNTIL); +	par_while(cmplx); +	cmdpop(); +	break; +    case REPEAT: +	cmdpush(CS_REPEAT); +	par_repeat(cmplx); +	cmdpop(); +	break; +    case INPAR: +	*cmplx = 1; +	cmdpush(CS_SUBSH); +	par_subsh(cmplx, zsh_construct); +	cmdpop(); +	break; +    case INBRACE: +	cmdpush(CS_CURSH); +	par_subsh(cmplx, zsh_construct); +	cmdpop(); +	break; +    case FUNC: +	cmdpush(CS_FUNCDEF); +	par_funcdef(cmplx); +	cmdpop(); +	break; +    case DINBRACK: +	cmdpush(CS_COND); +	par_dinbrack(); +	cmdpop(); +	break; +    case DINPAR: +	ecadd(WCB_ARITH()); +	ecstr(tokstr); +	zshlex(); +	break; +    case TIME: +	{ +	    static int inpartime = 0; + +	    if (!inpartime) { +		*cmplx = 1; +		inpartime = 1; +		par_time(); +		inpartime = 0; +		break; +	    } +	} +	tok = STRING; +	/* fall through */ +    default: +	{ +	    int sr; + +	    if (!(sr = par_simple(cmplx, nr))) { +		if (!nr) +		    return 0; +	    } else { +		/* Take account of redirections */ +		if (sr > 1) { +		    *cmplx = 1; +		    r += sr - 1; +		} +	    } +	} +	break; +    } +    if (IS_REDIROP(tok)) { +	*cmplx = 1; +	while (IS_REDIROP(tok)) +	    (void)par_redir(&r, NULL); +    } +    incmdpos = 1; +    incasepat = 0; +    incond = 0; +    intypeset = 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 *cmplx) +{ +    int oecused = ecused, csh = (tok == FOREACH), p, sel = (tok == SELECT); +    int type; + +    p = ecadd(0); + +    incmdpos = 0; +    infor = tok == FOR ? 2 : 0; +    zshlex(); +    if (tok == DINPAR) { +	zshlex(); +	if (tok != DINPAR) +	    YYERRORV(oecused); +	ecstr(tokstr); +	zshlex(); +	if (tok != DINPAR) +	    YYERRORV(oecused); +	ecstr(tokstr); +	zshlex(); +	if (tok != DOUTPAR) +	    YYERRORV(oecused); +	ecstr(tokstr); +	infor = 0; +	incmdpos = 1; +	zshlex(); +	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); +	    zshlex(); +	    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) +	    zshlex(); +        if (tok == STRING && !strcmp(tokstr, "in")) { +	    incmdpos = 0; +	    zshlex(); +	    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; +	    zshlex(); +	    np = ecadd(0); +	    n = par_nl_wordlist(); +	    if (tok != OUTPAR) +		YYERRORV(oecused); +	    ecbuf[np] = n; +	    incmdpos = 1; +	    zshlex(); +	    type = (sel ? WC_SELECT_LIST : WC_FOR_LIST); +	} else +	    type = (sel ? WC_SELECT_PPARAM : WC_FOR_PPARAM); +    } +    incmdpos = 1; +    while (tok == SEPER) +	zshlex(); +    if (tok == DOLOOP) { +	zshlex(); +	par_save_list(cmplx); +	if (tok != DONE) +	    YYERRORV(oecused); +	incmdpos = 0; +	zshlex(); +    } else if (tok == INBRACE) { +	zshlex(); +	par_save_list(cmplx); +	if (tok != OUTBRACE) +	    YYERRORV(oecused); +	incmdpos = 0; +	zshlex(); +    } else if (csh || isset(CSHJUNKIELOOPS)) { +	par_save_list(cmplx); +	if (tok != ZEND) +	    YYERRORV(oecused); +	incmdpos = 0; +	zshlex(); +    } else if (unset(SHORTLOOPS)) { +	YYERRORV(oecused); +    } else +	par_save_list1(cmplx); + +    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 | SEMIBAR ] } +				{ SEPER } ( "esac" | OUTBRACE ) + */ + +/**/ +static void +par_case(int *cmplx) +{ +    int oecused = ecused, brflag, p, pp, palts, type, nalts; +    int ona, onc; + +    p = ecadd(0); + +    incmdpos = 0; +    zshlex(); +    if (tok != STRING) +	YYERRORV(oecused); +    ecstr(tokstr); + +    incmdpos = 1; +    ona = noaliases; +    onc = nocorrect; +    noaliases = nocorrect = 1; +    zshlex(); +    while (tok == SEPER) +	zshlex(); +    if (!(tok == STRING && !strcmp(tokstr, "in")) && tok != INBRACE) +    { +	noaliases = ona; +	nocorrect = onc; +	YYERRORV(oecused); +    } +    brflag = (tok == INBRACE); +    incasepat = 1; +    incmdpos = 0; +    noaliases = ona; +    nocorrect = onc; +    zshlex(); + +    for (;;) { +	char *str; +	int skip_zshlex; + +	while (tok == SEPER) +	    zshlex(); +	if (tok == OUTBRACE) +	    break; +	if (tok == INPAR) +	    zshlex(); +	if (tok == BAR) { +	    str = dupstring(""); +	    skip_zshlex = 1; +	} else { +	    if (tok != STRING) +		YYERRORV(oecused); +	    if (!strcmp(tokstr, "esac")) +		break; +	    str = dupstring(tokstr); +	    skip_zshlex = 0; +	} +	type = WC_CASE_OR; +	pp = ecadd(0); +	palts = ecadd(0); +	nalts = 0; +	/* +	 * Hack here. +	 * +	 * [Pause for astonished hubbub to subside.] +	 * +	 * The next token we get may be +	 * - ")" or "|" if we're looking at an honest-to-god +	 *   "case" pattern, either because there's no opening +	 *   parenthesis, or because SH_GLOB is set and we +	 *   managed to grab an initial "(" to mark the start +	 *   of the case pattern. +	 * - Something else --- we don't care what --- because +	 *   we're parsing a complete "(...)" as a complete +	 *   zsh pattern.  In that case, we treat this as a +	 *   single instance of a case pattern but we pretend +	 *   we're doing proper case parsing --- in which the +	 *   parentheses and bar are in different words from +	 *   the string, so may be separated by whitespace. +	 *   So we quietly massage the whitespace and hope +	 *   no one noticed.  This is horrible, but it's +	 *   unfortunately too difficult to combine traditional +	 *   zsh patterns with a properly parsed case pattern +	 *   without generating incompatibilities which aren't +	 *   all that popular (I've discovered). +	 * - We can also end up with something other than ")" or "|" +	 *   just because we're looking at garbage. +	 * +	 * Because of the second case, what happens next might +	 * be the start of the command after the pattern, so we +	 * need to treat it as in command position.  Luckily +	 * this doesn't affect our ability to match a | or ) as +	 * these are valid on command lines. +	 */ +	incasepat = -1; +	incmdpos = 1; +	if (!skip_zshlex) +	    zshlex(); +	for (;;) { +	    if (tok == OUTPAR) { +		ecstr(str); +		ecadd(ecnpats++); +		nalts++; + +		incasepat = 0; +		incmdpos = 1; +		zshlex(); +		break; +	    } else if (tok == BAR) { +		ecstr(str); +		ecadd(ecnpats++); +		nalts++; + +		incasepat = 1; +		incmdpos = 0; +	    } else { +		if (!nalts && str[0] == Inpar) { +		    int pct = 0, sl; +		    char *s; + +		    for (s = str; *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); +		    ecstr(str); +		    ecadd(ecnpats++); +		    nalts++; +		    break; +		} +		YYERRORV(oecused); +	    } + +	    zshlex(); +	    switch (tok) { +	    case STRING: +		/* Normal case */ +		str = dupstring(tokstr); +		zshlex(); +		break; + +	    case OUTPAR: +	    case BAR: +		/* Empty string */ +		str = dupstring(""); +		break; + +	    default: +		/* Oops. */ +		YYERRORV(oecused); +		break; +	    } +	} +	incasepat = 0; +	par_save_list(cmplx); +	if (tok == SEMIAMP) +	    type = WC_CASE_AND; +	else if (tok == SEMIBAR) +	    type = WC_CASE_TESTAND; +	ecbuf[pp] = WCB_CASE(type, ecused - 1 - pp); +	ecbuf[palts] = nalts; +	if ((tok == ESAC && !brflag) || (tok == OUTBRACE && brflag)) +	    break; +	if (tok != DSEMI && tok != SEMIAMP && tok != SEMIBAR) +	    YYERRORV(oecused); +	incasepat = 1; +	incmdpos = 0; +	zshlex(); +    } +    incmdpos = 1; +    incasepat = 0; +    zshlex(); + +    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 *cmplx) +{ +    int oecused = ecused, p, pp, type, usebrace = 0; +    enum lextok xtok; +    unsigned char nc; + +    p = ecadd(0); + +    for (;;) { +	xtok = tok; +	cmdpush(xtok == IF ? CS_IF : CS_ELIF); +	if (xtok == FI) { +	    incmdpos = 0; +	    zshlex(); +	    break; +	} +	zshlex(); +	if (xtok == ELSE) +	    break; +	while (tok == SEPER) +	    zshlex(); +	if (!(xtok == IF || xtok == ELIF)) { +	    cmdpop(); +	    YYERRORV(oecused); +	} +	pp = ecadd(0); +	type = (xtok == IF ? WC_IF_IF : WC_IF_ELIF); +	par_save_list(cmplx); +	incmdpos = 1; +	if (tok == ENDINPUT) { +	    cmdpop(); +	    YYERRORV(oecused); +	} +	while (tok == SEPER) +	    zshlex(); +	xtok = FI; +	nc = cmdstack[cmdsp - 1] == CS_IF ? CS_IFTHEN : CS_ELIFTHEN; +	if (tok == THEN) { +	    usebrace = 0; +	    cmdpop(); +	    cmdpush(nc); +	    zshlex(); +	    par_save_list(cmplx); +	    ecbuf[pp] = WCB_IF(type, ecused - 1 - pp); +	    incmdpos = 1; +	    cmdpop(); +	} else if (tok == INBRACE) { +	    usebrace = 1; +	    cmdpop(); +	    cmdpush(nc); +	    zshlex(); +	    par_save_list(cmplx); +	    if (tok != OUTBRACE) { +		cmdpop(); +		YYERRORV(oecused); +	    } +	    ecbuf[pp] = WCB_IF(type, ecused - 1 - pp); +	    /* command word (else) allowed to follow immediately */ +	    zshlex(); +	    incmdpos = 1; +	    if (tok == SEPER) +		break; +	    cmdpop(); +	} else if (unset(SHORTLOOPS)) { +	    cmdpop(); +	    YYERRORV(oecused); +	} else { +	    cmdpop(); +	    cmdpush(nc); +	    par_save_list1(cmplx); +	    ecbuf[pp] = WCB_IF(type, ecused - 1 - pp); +	    incmdpos = 1; +	    break; +	} +    } +    cmdpop(); +    if (xtok == ELSE || tok == ELSE) { +	pp = ecadd(0); +	cmdpush(CS_ELSE); +	while (tok == SEPER) +	    zshlex(); +	if (tok == INBRACE && usebrace) { +	    zshlex(); +	    par_save_list(cmplx); +	    if (tok != OUTBRACE) { +		cmdpop(); +		YYERRORV(oecused); +	    } +	} else { +	    par_save_list(cmplx); +	    if (tok != FI) { +		cmdpop(); +		YYERRORV(oecused); +	    } +	} +	incmdpos = 0; +	ecbuf[pp] = WCB_IF(WC_IF_ELSE, ecused - 1 - pp); +	zshlex(); +	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 *cmplx) +{ +    int oecused = ecused, p; +    int type = (tok == UNTIL ? WC_WHILE_UNTIL : WC_WHILE_WHILE); + +    p = ecadd(0); +    zshlex(); +    par_save_list(cmplx); +    incmdpos = 1; +    while (tok == SEPER) +	zshlex(); +    if (tok == DOLOOP) { +	zshlex(); +	par_save_list(cmplx); +	if (tok != DONE) +	    YYERRORV(oecused); +	incmdpos = 0; +	zshlex(); +    } else if (tok == INBRACE) { +	zshlex(); +	par_save_list(cmplx); +	if (tok != OUTBRACE) +	    YYERRORV(oecused); +	incmdpos = 0; +	zshlex(); +    } else if (isset(CSHJUNKIELOOPS)) { +	par_save_list(cmplx); +	if (tok != ZEND) +	    YYERRORV(oecused); +	zshlex(); +    } else if (unset(SHORTLOOPS)) { +	YYERRORV(oecused); +    } else +	par_save_list1(cmplx); + +    ecbuf[p] = WCB_WHILE(type, ecused - 1 - p); +} + +/* + * repeat	: REPEAT STRING { SEPER } ( DO list DONE | list1 ) + */ + +/**/ +static void +par_repeat(int *cmplx) +{ +    /* ### what to do about inrepeat_ here? */ +    int oecused = ecused, p; + +    p = ecadd(0); + +    incmdpos = 0; +    zshlex(); +    if (tok != STRING) +	YYERRORV(oecused); +    ecstr(tokstr); +    incmdpos = 1; +    zshlex(); +    while (tok == SEPER) +	zshlex(); +    if (tok == DOLOOP) { +	zshlex(); +	par_save_list(cmplx); +	if (tok != DONE) +	    YYERRORV(oecused); +	incmdpos = 0; +	zshlex(); +    } else if (tok == INBRACE) { +	zshlex(); +	par_save_list(cmplx); +	if (tok != OUTBRACE) +	    YYERRORV(oecused); +	incmdpos = 0; +	zshlex(); +    } else if (isset(CSHJUNKIELOOPS)) { +	par_save_list(cmplx); +	if (tok != ZEND) +	    YYERRORV(oecused); +	zshlex(); +    } else if (unset(SHORTLOOPS)) { +	YYERRORV(oecused); +    } else +	par_save_list1(cmplx); + +    ecbuf[p] = WCB_REPEAT(ecused - 1 - p); +} + +/* + * subsh	: INPAR list OUTPAR | + *                INBRACE list OUTBRACE [ "always" INBRACE list OUTBRACE ] + * + * With zsh_construct non-zero, we're doing a zsh special in which + * the following token is not considered in command position.  This + * is used for arguments of anonymous functions. + */ + +/**/ +static void +par_subsh(int *cmplx, int zsh_construct) +{ +    enum lextok otok = tok; +    int oecused = ecused, p, pp; + +    p = ecadd(0); +    /* Extra word only needed for always block */ +    pp = ecadd(0); +    zshlex(); +    par_list(cmplx); +    ecadd(WCB_END()); +    if (tok != ((otok == INPAR) ? OUTPAR : OUTBRACE)) +	YYERRORV(oecused); +    incmdpos = !zsh_construct; +    zshlex(); + +    /* 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 { +	    zshlex(); +	} while (tok == SEPER); + +	if (tok != INBRACE) +	    YYERRORV(oecused); +	cmdpop(); +	cmdpush(CS_ALWAYS); + +	zshlex(); +	par_save_list(cmplx); +	while (tok == SEPER) +	    zshlex(); + +	incmdpos = 1; + +	if (tok != OUTBRACE) +	    YYERRORV(oecused); +	zshlex(); +	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(int *cmplx) +{ +    int oecused = ecused, num = 0, onp, p, c = 0; +    int so, oecssub = ecssub; +    zlong oldlineno = lineno; + +    lineno = 0; +    nocorrect = 1; +    incmdpos = 0; +    zshlex(); + +    p = ecadd(0); +    ecadd(0); + +    while (tok == STRING) { +	if ((*tokstr == Inbrace || *tokstr == '{') && +	    !tokstr[1]) { +	    tok = INBRACE; +	    break; +	} +	ecstr(tokstr); +	num++; +	zshlex(); +    } +    ecadd(0); +    ecadd(0); +    ecadd(0); + +    nocorrect = 0; +    incmdpos = 1; +    if (tok == INOUTPAR) +	zshlex(); +    while (tok == SEPER) +	zshlex(); + +    ecnfunc++; +    ecssub = so = ecsoffs; +    onp = ecnpats; +    ecnpats = 0; + +    if (tok == INBRACE) { +	zshlex(); +	par_list(&c); +	if (tok != OUTBRACE) { +	    lineno += oldlineno; +	    ecnpats = onp; +	    ecssub = oecssub; +	    YYERRORV(oecused); +	} +	if (num == 0) { +	    /* Anonymous function, possibly with arguments */ +	    incmdpos = 0; +	} +	zshlex(); +    } 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; + +    ecnpats = onp; +    ecssub = oecssub; +    ecnfunc++; + +    ecbuf[p] = WCB_FUNCDEF(ecused - 1 - p); + +    if (num == 0) { +	/* Unnamed function */ +	int parg = ecadd(0); +	ecadd(0); +	while (tok == STRING) { +	    ecstr(tokstr); +	    num++; +	    zshlex(); +	} +	if (num > 0) +	    *cmplx = 1; +	ecbuf[parg] = ecused - parg; /*?*/ +	ecbuf[parg+1] = num; +    } +    lineno += oldlineno; +} + +/* + * time	: TIME sublist2 + */ + +/**/ +static void +par_time(void) +{ +    int p, f, c = 0; + +    zshlex(); + +    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; +    zshlex(); +    par_cond(); +    if (tok != DOUTBRACK) +	YYERRORV(oecused); +    incond = 0; +    incmdpos = 1; +    zshlex(); +} + +/* + * simple	: { COMMAND | EXEC | NOGLOB | NOCORRECT | DASH } +					{ STRING | ENVSTRING | ENVARRAY wordlist OUTPAR | redir } +					[ INOUTPAR { SEPER } ( list1 | INBRACE list OUTBRACE ) ] + * + * Returns 0 if no code, else 1 plus the number of code words + * used up by redirections. + */ + +/**/ +static int +par_simple(int *cmplx, int nr) +{ +    int oecused = ecused, isnull = 1, r, argc = 0, p, isfunc = 0, sr = 0; +    int c = *cmplx, nrediradd, assignments = 0, ppost = 0, is_typeset = 0; +    char *hasalias = input_hasalias(); +    wordcode postassigns = 0; + +    r = ecused; +    for (;;) { +	if (tok == NOCORRECT) { +	    *cmplx = c = 1; +	    nocorrect = 1; +	} else if (tok == ENVSTRING) { +	    char *ptr, *name, *str; + +	    name = tokstr; +	    for (ptr = tokstr; +		 *ptr && *ptr != Inbrack && *ptr != '=' && *ptr != '+'; +	         ptr++); +	    if (*ptr == Inbrack) skipparens(Inbrack, Outbrack, &ptr); +	    if (*ptr == '+') { +	    	*ptr++ = '\0'; +	    	ecadd(WCB_ASSIGN(WC_ASSIGN_SCALAR, WC_ASSIGN_INC, 0)); +	    } else +		ecadd(WCB_ASSIGN(WC_ASSIGN_SCALAR, WC_ASSIGN_NEW, 0)); + +	    if (*ptr == '=') { +		*ptr = '\0'; +		str = ptr + 1; +	    } else +		equalsplit(tokstr, &str); +	    for (ptr = str; *ptr; ptr++) { +		/* +		 * We can't treat this as "simple" if it contains +		 * expansions that require process subsitution, since then +		 * we need process handling. +		 */ +		if (ptr[1] == Inpar && +		    (*ptr == Equals || *ptr == Inang || *ptr == OutangProc)) { +		    *cmplx = 1; +		    break; +		} +	    } +	    ecstr(name); +	    ecstr(str); +	    isnull = 0; +	    assignments = 1; +	} else if (tok == ENVARRAY) { +	    int oldcmdpos = incmdpos, n, type2; + +	    /* +	     * We consider array setting cmplx because it can +	     * contain process substitutions, which need a valid job. +	     */ +	    *cmplx = c = 1; +	    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); +	    zshlex(); +	    n = par_nl_wordlist(); +	    ecbuf[p] = WCB_ASSIGN(WC_ASSIGN_ARRAY, type2, n); +	    cmdpop(); +	    if (tok != OUTPAR) +		YYERROR(oecused); +	    incmdpos = oldcmdpos; +	    isnull = 0; +	    assignments = 1; +	} else if (IS_REDIROP(tok)) { +	    *cmplx = c = 1; +	    nr += par_redir(&r, NULL); +	    continue; +	} else +	    break; +	zshlex(); +	if (!hasalias) +	    hasalias = input_hasalias(); +    } +    if (tok == AMPER || tok == AMPERBANG) +	YYERROR(oecused); + +    p = ecadd(WCB_SIMPLE(0)); + +    for (;;) { +	if (tok == STRING || tok == TYPESET) { +	    int redir_var = 0; + +	    *cmplx = 1; +	    incmdpos = 0; + +	    if (tok == TYPESET) +		intypeset = is_typeset = 1; + +	    if (!isset(IGNOREBRACES) && *tokstr == Inbrace) +	    { +		/* Look for redirs of the form {var}>file etc. */ +		char *eptr = tokstr + strlen(tokstr) - 1; +		char *ptr = eptr; + +		if (*ptr == Outbrace && ptr > tokstr + 1) +		{ +		    if (itype_end(tokstr+1, IIDENT, 0) >= ptr) +		    { +			char *toksave = tokstr; +			char *idstring = dupstrpfx(tokstr+1, eptr-tokstr-1); +			redir_var = 1; +			zshlex(); +			if (!hasalias) +			    hasalias = input_hasalias(); + +			if (IS_REDIROP(tok) && tokfd == -1) +			{ +			    *cmplx = c = 1; +			    nrediradd = par_redir(&r, idstring); +			    p += nrediradd; +			    sr += nrediradd; +			} +			else +			{ +			    ecstr(toksave); +			    argc++; +			} +		    } +		} +	    } + +	    if (!redir_var) +	    { +		if (postassigns) { +		    /* +		     * We're in the variable part of a typeset, +		     * but this doesn't have an assignment. +		     * We'll parse it as if it does, but mark +		     * it specially with WC_ASSIGN_INC. +		     */ +		    postassigns++; +		    ecadd(WCB_ASSIGN(WC_ASSIGN_SCALAR, WC_ASSIGN_INC, 0)); +		    ecstr(tokstr); +		    ecstr("");	/* TBD can possibly optimise out */ +		} else { +		    ecstr(tokstr); +		    argc++; +		} +		zshlex(); +		if (!hasalias) +		    hasalias = input_hasalias(); +	    } +	} else if (IS_REDIROP(tok)) { +	    *cmplx = c = 1; +	    nrediradd = par_redir(&r, NULL); +	    p += nrediradd; +	    if (ppost) +		ppost += nrediradd; +	    sr += nrediradd; +	} else if (tok == ENVSTRING) { +	    char *ptr, *name, *str; + +	    if (!postassigns++) +		ppost = ecadd(0); + +	    name = tokstr; +	    for (ptr = tokstr; *ptr && *ptr != Inbrack && *ptr != '=' && *ptr != '+'; +	         ptr++); +	    if (*ptr == Inbrack) skipparens(Inbrack, Outbrack, &ptr); +	    ecadd(WCB_ASSIGN(WC_ASSIGN_SCALAR, WC_ASSIGN_NEW, 0)); + +	    if (*ptr == '=') { +		*ptr = '\0'; +		str = ptr + 1; +	    } else +		equalsplit(tokstr, &str); +	    ecstr(name); +	    ecstr(str); +	    zshlex(); +	    if (!hasalias) +		hasalias = input_hasalias(); +	} else if (tok == ENVARRAY) { +	    int n, parr; + +	    if (!postassigns++) +		ppost = ecadd(0); + +	    parr = ecadd(0); +	    ecstr(tokstr); +	    cmdpush(CS_ARRAY); +	    /* +	     * Careful here: this must be the typeset case, +	     * but we need to tell the lexer not to look +	     * for assignments until we've finished the +	     * present one. +	     */ +	    intypeset = 0; +	    zshlex(); +	    n = par_nl_wordlist(); +	    ecbuf[parr] = WCB_ASSIGN(WC_ASSIGN_ARRAY, WC_ASSIGN_NEW, n); +	    cmdpop(); +	    intypeset = 1; +	    if (tok != OUTPAR) +		YYERROR(oecused); +	    zshlex(); +	} else if (tok == INOUTPAR) { +	    zlong oldlineno = lineno; +	    int onp, so, oecssub = ecssub; + +	    /* Error if too many function definitions at once */ +	    if (!isset(MULTIFUNCDEF) && argc > 1) +		YYERROR(oecused); +	    /* Error if preceding assignments */ +	    if (assignments || postassigns) +		YYERROR(oecused); +	    if (hasalias && !isset(ALIASFUNCDEF) && argc && +		hasalias != input_hasalias()) { +		zwarn("defining function based on alias `%s'", hasalias); +		YYERROR(oecused); +	    } + +	    *cmplx = c; +	    lineno = 0; +	    incmdpos = 1; +	    cmdpush(CS_FUNCDEF); +	    zshlex(); +	    while (tok == SEPER) +		zshlex(); + +	    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; + +		zshlex(); +		par_list(&c); +		if (tok != OUTBRACE) { +		    cmdpop(); +		    lineno += oldlineno; +		    ecnpats = onp; +		    ecssub = oecssub; +		    YYERROR(oecused); +		} +		if (argc == 0) { +		    /* Anonymous function, possibly with arguments */ +		    incmdpos = 0; +		} +		zshlex(); +	    } else { +		int ll, sl, c = 0; + +		ll = ecadd(0); +		sl = ecadd(0); +		(void)ecadd(WCB_PIPE(WC_PIPE_END, 0)); + +		if (!par_cmd(&c, argc == 0)) { +		    cmdpop(); +		    YYERROR(oecused); +		} +		if (argc == 0) { +		    /* +		     * Anonymous function, possibly with arguments. +		     * N.B. for cmplx structures in particular +		     * ( ... ) we rely on lower level code doing this +		     * to get the immediately following word (the +		     * first token after the ")" has already been +		     * read). +		     */ +		    incmdpos = 0; +		} + +		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; + +	    ecnpats = onp; +	    ecssub = oecssub; +	    ecnfunc++; + +	    ecbuf[p] = WCB_FUNCDEF(ecused - 1 - p); + +	    if (argc == 0) { +		/* Unnamed function */ +		int parg = ecadd(0); +		ecadd(0); +		while (tok == STRING || IS_REDIROP(tok)) { +		    if (tok == STRING) +		    { +			ecstr(tokstr); +			argc++; +			zshlex(); +		    } else { +			*cmplx = c = 1; +			nrediradd = par_redir(&r, NULL); +			p += nrediradd; +			if (ppost) +			    ppost += nrediradd; +			sr += nrediradd; +			parg += nrediradd; +		    } +		} +		if (argc > 0) +		    *cmplx = 1; +		ecbuf[parg] = ecused - parg; /*?*/ +		ecbuf[parg+1] = argc; +	    } +	    lineno += oldlineno; + +	    isfunc = 1; +	    isnull = 0; +	    break; +	} else +	    break; +	isnull = 0; +    } +    if (isnull && !(sr + nr)) { +	ecused = p; +	return 0; +    } +    incmdpos = 1; +    intypeset = 0; + +    if (!isfunc) { +	if (is_typeset) { +	    ecbuf[p] = WCB_TYPESET(argc); +	    if (postassigns) +		ecbuf[ppost] = postassigns; +	    else +		ecadd(0); +	} else +	    ecbuf[p] = WCB_SIMPLE(argc); +    } + +    return sr + 1; +} + +/* + * redir	: ( OUTANG | ... | TRINANG ) STRING + * + * Return number of code words required for redirection + */ + +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 int +par_redir(int *rp, char *idstring) +{ +    int r = *rp, type, fd1, oldcmdpos, oldnc, ncodes; +    char *name; + +    oldcmdpos = incmdpos; +    incmdpos = 0; +    oldnc = nocorrect; +    if (tok != INANG && tok != INOUTANG) +	nocorrect = 1; +    type = redirtab[tok - OUTANG]; +    fd1 = tokfd; +    zshlex(); +    if (tok != STRING && tok != ENVSTRING) +	YYERROR(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; +	int htype = type; + +	/* +	 * Add two here for the string to remember the HERE +	 * terminator in raw and munged form. +	 */ +	if (idstring) +	{ +	    type |= REDIR_VARID_MASK; +	    ncodes = 6; +	} +	else +	    ncodes = 5; + +	/* If we ever to change the number of codes, we have to change +	 * the definition of WC_REDIR_WORDS. */ +	ecispace(r, ncodes); +	*rp = r + ncodes; +	ecbuf[r] = WCB_REDIR(type | REDIR_FROM_HEREDOC_MASK); +	ecbuf[r + 1] = fd1; + +	/* +	 * r + 2: the HERE string we recover +	 * r + 3: the HERE document terminator, raw +	 * r + 4: the HERE document terminator, munged +	 */ +	if (idstring) +	    ecbuf[r + 5] = ecstrcode(idstring); + +	for (hd = &hdocs; *hd; hd = &(*hd)->next) +	    ; +	*hd = zalloc(sizeof(struct heredocs)); +	(*hd)->next = NULL; +	(*hd)->type = htype; +	(*hd)->pc = r; +	(*hd)->str = tokstr; + +	zshlex(); +	return ncodes; +    } +    case REDIR_WRITE: +    case REDIR_WRITENOW: +	if (tokstr[0] == OutangProc && tokstr[1] == Inpar) +	    /* > >(...) */ +	    type = REDIR_OUTPIPE; +	else if (tokstr[0] == Inang && tokstr[1] == Inpar) +	    YYERROR(ecused); +	break; +    case REDIR_READ: +	if (tokstr[0] == Inang && tokstr[1] == Inpar) +	    /* < <(...) */ +	    type = REDIR_INPIPE; +	else if (tokstr[0] == OutangProc && tokstr[1] == Inpar) +	    YYERROR(ecused); +	break; +    case REDIR_READWRITE: +	if ((tokstr[0] == Inang || tokstr[0] == OutangProc) && +	    tokstr[1] == Inpar) +	    type = tokstr[0] == Inang ? REDIR_INPIPE : REDIR_OUTPIPE; +	break; +    } +    zshlex(); + +    /* If we ever to change the number of codes, we have to change +     * the definition of WC_REDIR_WORDS. */ +    if (idstring) +    { +	type |= REDIR_VARID_MASK; +	ncodes = 4; +    } +    else +	ncodes = 3; + +    ecispace(r, ncodes); +    *rp = r + ncodes; +    ecbuf[r] = WCB_REDIR(type); +    ecbuf[r + 1] = fd1; +    ecbuf[r + 2] = ecstrcode(name); +    if (idstring) +	ecbuf[r + 3] = ecstrcode(idstring); + +    return ncodes; +} + +/**/ +void +setheredoc(int pc, int type, char *str, char *termstr, char *munged_termstr) +{ +    ecbuf[pc] = WCB_REDIR(type | REDIR_FROM_HEREDOC_MASK); +    ecbuf[pc + 2] = ecstrcode(str); +    ecbuf[pc + 3] = ecstrcode(termstr); +    ecbuf[pc + 4] = ecstrcode(munged_termstr); +} + +/* + * wordlist	: { STRING } + */ + +/**/ +static int +par_wordlist(void) +{ +    int num = 0; +    while (tok == STRING) { +	ecstr(tokstr); +	num++; +	zshlex(); +    } +    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++; +	} +	zshlex(); +    } +    return num; +} + +/* + * condlex is zshlex for normal parsing, but is altered to allow + * the test builtin to use par_cond. + */ + +/**/ +void (*condlex) _((void)) = zshlex; + +/* + * cond	: cond_1 { SEPER } [ DBAR { SEPER } cond ] + */ + +#define COND_SEP() (tok == SEPER && condlex != testlex && *zshlextext != ';') + +/**/ +static int +par_cond(void) +{ +    int p = ecused, r; + +    r = par_cond_1(); +    while (COND_SEP()) +	condlex(); +    if (tok == DBAR) { +	condlex(); +	while (COND_SEP()) +	    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 (COND_SEP()) +	condlex(); +    if (tok == DAMPER) { +	condlex(); +	while (COND_SEP()) +	    condlex(); +	ecispace(p, 1); +	par_cond_1(); +	ecbuf[p] = WCB_COND(COND_AND, ecused - 1 - p); +	return 1; +    } +    return r; +} + +/* + * Return 1 if condition matches.  This also works for non-elided options. + * + * input is test string, may begin - or Dash. + * cond is condition following the -. + */ +static int check_cond(const char *input, const char *cond) +{ +    if (!IS_DASH(input[0])) +	return 0; +    return !strcmp(input + 1, cond); +} + +/* + * 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; +    int n_testargs = (condlex == testlex) ? arrlen(testargs) + 1 : 0; + +    if (n_testargs) { +	/* See the description of test in POSIX 1003.2 */ +	if (tok == NULLTOK) +	    /* no arguments: false */ +	    return par_cond_double(dupstring("-n"), dupstring("")); +	if (n_testargs == 1) { +	    /* one argument: [ foo ] is equivalent to [ -n foo ] */ +	    s1 = tokstr; +	    condlex(); +	    /* ksh behavior: [ -t ] means [ -t 1 ]; bash disagrees */ +	    if (unset(POSIXBUILTINS) && check_cond(s1, "t")) +		return par_cond_double(s1, dupstring("1")); +	    return par_cond_double(dupstring("-n"), s1); +	} +	if (n_testargs > 2) { +	    /* 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, "!=") || +		(IS_DASH(**testargs) && get_cond_num(*testargs + 1) >= 0)) { +		s1 = tokstr; +		condlex(); +		s2 = tokstr; +		condlex(); +		s3 = tokstr; +		condlex(); +		return par_cond_triple(s1, s2, s3); +	    } +	} +	/* +	 * We fall through here on any non-numeric infix operator +	 * or any other time there are at least two arguments. +	 */ +    } else +	while (COND_SEP()) +	    condlex(); +    if (tok == BANG) { +	/* +	 * In "test" compatibility mode, "! -a ..." and "! -o ..." +	 * are treated as "[string] [and] ..." and "[string] [or] ...". +	 */ +	if (!(n_testargs > 1 && (check_cond(*testargs, "a") || +				 check_cond(*testargs, "o")))) +	{ +	    condlex(); +	    ecadd(WCB_COND(COND_NOT, 0)); +	    return par_cond_2(); +	} +    } +    if (tok == INPAR) { +	int r; + +	condlex(); +	while (COND_SEP()) +	    condlex(); +	r = par_cond(); +	while (COND_SEP()) +	    condlex(); +	if (tok != OUTPAR) +	    YYERROR(ecused); +	condlex(); +	return r; +    } +    s1 = tokstr; +    dble = (s1 && IS_DASH(*s1) +	    && (!n_testargs +		|| strspn(s1+1, "abcdefghknoprstuvwxzLONGS") == 1) +	    && !s1[2]); +    if (tok != STRING) { +	/* Check first argument for [[ STRING ]] re-interpretation */ +	if (s1 /* tok != DOUTBRACK && tok != DAMPER && tok != DBAR */ +	    && tok != LEXERR && (!dble || n_testargs)) { +	    do condlex(); while (COND_SEP()); +	    return par_cond_double(dupstring("-n"), s1); +	} else +	    YYERROR(ecused); +    } +    condlex(); +    if (n_testargs == 2 && tok != STRING && tokstr && IS_DASH(s1[0])) { +	/* +	 * Something like "test -z" followed by a token. +	 * We'll turn the token into a string (we've also +	 * checked it does have a string representation). +	 */ +	tok = STRING; +    } else +	while (COND_SEP()) +	    condlex(); +    if (tok == INANG || tok == OUTANG) { +	enum lextok xtok = tok; +	do condlex(); while (COND_SEP()); +	if (tok != STRING) +	    YYERROR(ecused); +	s3 = tokstr; +	do condlex(); while (COND_SEP()); +	ecadd(WCB_COND((xtok == INANG ? COND_STRLT : COND_STRGTR), 0)); +	ecstr(s1); +	ecstr(s3); +	return 1; +    } +    if (tok != STRING) { +	/* +	 * Check second argument in case semantics e.g. [ = -a = ] +	 * mean we have to go back and fix up the first one +	 */ +	if (tok != LEXERR) { +	    if (!dble || n_testargs) +		return par_cond_double(dupstring("-n"), s1); +	    else +		return par_cond_multi(s1, newlinklist()); +	} else +	    YYERROR(ecused); +    } +    s2 = tokstr; +    if (!n_testargs) +	dble = (s2 && IS_DASH(*s2) && !s2[2]); +    incond++;			/* parentheses do globbing */ +    do condlex(); while (COND_SEP()); +    incond--;			/* parentheses do grouping */ +    if (tok == STRING && !dble) { +	s3 = tokstr; +	do condlex(); while (COND_SEP()); +	if (tok == STRING) { +	    LinkList l = newlinklist(); + +	    addlinknode(l, s2); +	    addlinknode(l, s3); + +	    while (tok == STRING) { +		addlinknode(l, tokstr); +		do condlex(); while (COND_SEP()); +	    } +	    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 (!IS_DASH(a[0]) || !a[1]) +	COND_ERROR("parse error: condition expected: %s", a); +    else if (!a[2] && strspn(a+1, "abcdefgknoprstuvwxzhLONGS") == 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]) { +	ecadd(WCB_COND(COND_STREQ, 0)); +	ecstr(a); +	ecstr(c); +	ecadd(ecnpats++); +    } else if ((b[0] == Equals || b[0] == '=') && +	       (b[1] == Equals || b[1] == '=') && !b[2]) { +	ecadd(WCB_COND(COND_STRDEQ, 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] == Equals || b[0] == '=') && +               (b[1] == '~' || b[1] == Tilde) && !b[2]) { +        /* We become an implicit COND_MODI but do not provide the first +	 * item, it's skipped */ +	ecadd(WCB_COND(COND_REGEX, 0)); +	ecstr(a); +	ecstr(c); +    } else if (IS_DASH(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 (IS_DASH(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 (!IS_DASH(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(zshlextext))) +	untokenize(t); + +    for (t0 = 0; t0 != 20; t0++) +	if (!t || !t[t0] || t[t0] == '\n') +	    break; +    if (!(histdone & HISTFLAG_NOEXEC) && !(errflag & ERRFLAG_INT)) { +	if (t0 == 20) +	    zwarn("parse error near `%l...'", t, 20); +	else if (t0) +	    zwarn("parse error near `%l'", t, t0); +	else +	    zwarn("parse error"); +    } +    if (!noerr && noerrs != 2) +	errflag |= ERRFLAG_ERROR; +} + +/* + * Duplicate a programme list, on the heap if heap is 1, else + * in permanent storage. + * + * Be careful in case p is the Eprog for a function which will + * later be autoloaded.  The shf element of the returned Eprog + * must be set appropriately by the caller.  (Normally we create + * the Eprog in this case by using mkautofn.) + */ + +/**/ +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(zsh_funcnest >=0 && p->nref > zsh_funcnest + 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 *tokflag) +{ +    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 (tokflag) +	*tokflag = (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 *tokflag) +{ +    static char buf[4]; +    wordcode c = *pc; + +    if (c == 6 || c == 7) { +	if (tokflag) +	    *tokflag = (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 (tokflag) +	    *tokflag = (c & 1); +	return buf; +    } else { +	if (tokflag) +	    *tokflag = (c & 1); +	return p->strs + (c >> 2); +    } +} + +/**/ +char ** +ecgetarr(Estate s, int num, int dup, int *tokflag) +{ +    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 (tokflag) +	*tokflag = tf; + +    return ret; +} + +/**/ +LinkList +ecgetlist(Estate s, int num, int dup, int *tokflag) +{ +    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 (tokflag) +	    *tokflag = tf; +	return ret; +    } +    if (tokflag) +	*tokflag = 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); +	if (WC_REDIR_FROM_HEREDOC(code)) { +	    r->flags = REDIRF_FROM_HEREDOC; +	    r->here_terminator = ecgetstr(s, EC_DUP, NULL); +	    r->munged_here_terminator = ecgetstr(s, EC_DUP, NULL); +	} else { +	    r->flags = 0; +	    r->here_terminator = NULL; +	    r->munged_here_terminator = NULL; +	} +	if (WC_REDIR_VARID(code)) +	    r->varid = ecgetstr(s, EC_DUP, NULL); +	else +	    r->varid = NULL; + +	addlinknode(ret, r); + +	code = *s->pc++; +    } +    s->pc--; + +    return ret; +} + +/* + * Copy the consecutive set of redirections in the state at s. + * Return NULL if none, else an Eprog consisting only of the + * redirections from permanently allocated memory. + * + * s is left in the state ready for whatever follows the redirections. + */ + +/**/ +Eprog +eccopyredirs(Estate s) +{ +    Wordcode pc = s->pc; +    wordcode code = *pc; +    int ncode, ncodes = 0, r; + +    if (wc_code(code) != WC_REDIR) +	return NULL; + +    init_parse(); + +    while (wc_code(code) == WC_REDIR) { +#ifdef DEBUG +	int type = WC_REDIR_TYPE(code); +#endif + +	DPUTS(type == REDIR_HEREDOC || type == REDIR_HEREDOCDASH, +	      "unexpanded here document"); + +	if (WC_REDIR_FROM_HEREDOC(code)) +	    ncode = 5; +	else +	    ncode = 3; +	if (WC_REDIR_VARID(code)) +	    ncode++; +	pc += ncode; +	ncodes += ncode; +	code = *pc; +    } +    r = ecused; +    ecispace(r, ncodes); + +    code = *s->pc; +    while (wc_code(code) == WC_REDIR) { +	s->pc++; + +	ecbuf[r++] = code; +	/* fd1 */ +	ecbuf[r++] = *s->pc++; +	/* name or HERE string */ +	/* No DUP needed as we'll copy into Eprog immediately below */ +	ecbuf[r++] = ecstrcode(ecgetstr(s, EC_NODUP, NULL)); +	if (WC_REDIR_FROM_HEREDOC(code)) +	{ +	    /* terminator, raw */ +	    ecbuf[r++] = ecstrcode(ecgetstr(s, EC_NODUP, NULL)); +	    /* terminator, munged */ +	    ecbuf[r++] = ecstrcode(ecgetstr(s, EC_NODUP, NULL)); +	} +	if (WC_REDIR_VARID(code)) +	    ecbuf[r++] = ecstrcode(ecgetstr(s, EC_NODUP, NULL)); + +	code = *s->pc; +    } + +    /* bld_eprog() appends a useful WC_END marker */ +    return bld_eprog(0); +} + +/**/ +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; +} + +/**/ +int +bin_zcompile(char *nam, char **args, Options ops, UNUSED(int func)) +{ +    int map, flags, ret; +    char *dump; + +    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"); +	return 1; +    } +    if ((OPT_ISSET(ops,'c') || OPT_ISSET(ops,'a')) && isset(KSHAUTOLOAD)) +	zwarnnam(nam, "functions will use zsh style autoloading"); + +    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"); +	    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"); +	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; +} + +/* 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 = 1; +    wordcode buf[FD_PRELEN + 1]; + +    if ((fd = open(name, O_RDONLY)) < 0) { +	if (err) +	    zwarnnam(nam, "can't open zwc file: %s", name); +	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)) || +	strcmp(fdversion(buf), ZSH_VERSION)) { +	if (err) { +	    if (!v) { +		zwarnnam(nam, "zwc file has wrong version (zsh-%s): %s", +			 fdversion(buf), name); +	    } else +		zwarnnam(nam, "invalid zwc file: %s" , name); +	} +	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); +		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); +	    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); + +    memset(pre, 0, sizeof(wordcode) * FD_PRELEN); + +    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_loop(dfd, (char *)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_loop(dfd, (char *)&head, sizeof(head)); +	    tmp = strlen(n) + 1; +	    write_loop(dfd, n, tmp); +	    if ((tmp &= (sizeof(wordcode) - 1))) +		write_loop(dfd, (char *)&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_loop(dfd, (char *)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); +	return 1; +    } +    progs = newlinklist(); +    noaliases = ali; + +    for (hlen = FD_PRELEN, tlen = 0; *files; files++) { +	struct stat st; + +	if (check_cond(*files, "k")) { +	    flags = (flags & ~(FDHF_KSHLOAD | FDHF_ZSHLOAD)) | FDHF_KSHLOAD; +	    continue; +	} else if (check_cond(*files, "z")) { +	    flags = (flags & ~(FDHF_KSHLOAD | FDHF_ZSHLOAD)) | FDHF_ZSHLOAD; +	    continue; +	} +	if ((fd = open(*files, O_RDONLY)) < 0 || +	    fstat(fd, &st) != 0 || !S_ISREG(st.st_mode) || +	    (flen = lseek(fd, 0, 2)) == -1) { +	    if (fd >= 0) +		close(fd); +	    close(dfd); +	    zwarnnam(nam, "can't open file: %s", *files); +	    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); +	    noaliases = ona; +	    unlink(dump); +	    return 1; +	} +	close(fd); +	file = metafy(file, flen, META_REALLOC); + +	if (!(prog = parse_string(file, 1)) || errflag) { +	    errflag &= ~ERRFLAG_ERROR; +	    close(dfd); +	    zfree(file, flen); +	    zwarnnam(nam, "can't read file: %s", *files); +	    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->node.flags & PM_UNDEFINED) { +	int ona = noaliases; + +	if (!(what & 2)) { +	    zwarnnam(nam, "function is not loaded: %s", shf->node.nam); +	    return 1; +	} +	noaliases = (shf->node.flags & PM_UNALIASED); +	if (!(prog = getfpfunc(shf->node.nam, NULL, NULL, NULL, 0)) || +	    prog == &dummy_eprog) { +	    noaliases = ona; +	    zwarnnam(nam, "can't load function: %s", shf->node.nam); +	    return 1; +	} +	if (prog->dump) +	    prog = dupeprog(prog, 1); +	noaliases = ona; +    } else { +	if (!(what & 1)) { +	    zwarnnam(nam, "function is already loaded: %s", shf->node.nam); +	    return 1; +	} +	prog = dupeprog(shf->funcdef, 1); +    } +    wcf = (WCFunc) zhalloc(sizeof(*wcf)); +    wcf->name = shf->node.nam; +    wcf->prog = prog; +    wcf->flags = ((prog->flags & EF_RUN) ? FDHF_KSHLOAD : FDHF_ZSHLOAD); +    addlinknode(progs, wcf); +    addlinknode(names, shf->node.nam); + +    *hlen += ((sizeof(struct fdhead) / sizeof(wordcode)) + +	      ((strlen(shf->node.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); +	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 &= ~ERRFLAG_ERROR; +		    close(dfd); +		    unlink(dump); +		    return 1; +		} +    } else if (match) { +	char *pat; +	Patprog pprog; +	int i; +	HashNode hn; + +	for (; *names; names++) { +	    tokenize(pat = dupstring(*names)); +	    /* Signal-safe here, caller queues signals */ +	    if (!(pprog = patcompile(pat, PAT_STATIC, NULL))) { +		zwarnnam(nam, "bad pattern: %s", *names); +		close(dfd); +		unlink(dump); +		return 1; +	    } +	    for (i = 0; i < shfunctab->hsize; i++) +		for (hn = shfunctab->nodes[i]; hn; hn = hn->next) +		    if (!linknodebydatum(lnames, hn->nam) && +			pattry(pprog, hn->nam) && +			cur_add_func(nam, (Shfunc) hn, lnames, progs, +				     &hlen, &tlen, what)) { +			errflag &= ~ERRFLAG_ERROR; +			close(dfd); +			unlink(dump); +			return 1; +		    } +	} +    } else { +	for (; *names; names++) { +	    if (errflag || +		!(shf = (Shfunc) shfunctab->getnode(shfunctab, *names))) { +		zwarnnam(nam, "unknown function: %s", *names); +		errflag &= ~ERRFLAG_ERROR; +		close(dfd); +		unlink(dump); +		return 1; +	    } +	    if (cur_add_func(nam, shf, lnames, progs, &hlen, &tlen, what)) { +		errflag &= ~ERRFLAG_ERROR; +		close(dfd); +		unlink(dump); +		return 1; +	    } +	} +    } +    if (empty(progs)) { +	zwarnnam(nam, "no functions"); +	errflag &= ~ERRFLAG_ERROR; +	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) +{ +    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 (fd == -1) +	return; + +    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; +#ifdef FD_CLOEXEC +    fcntl(fd, F_SETFD, FD_CLOEXEC); +#endif +    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) (!!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, int test_only) +{ +    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, test_only); +	unqueue_signals(); +	return prog; +    } +    dig = dyncat(path, FD_EXT); +    wc = dyncat(file, FD_EXT); + +    rd = zwcstat(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. */ +    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, test_only))) { +	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, test_only))) { +	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++; +    else +	tail = file; + +    if (strsfx(FD_EXT, file)) { +	queue_signals(); +	prog = check_dump_file(file, NULL, tail, NULL, 0); +	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, 0))) { +	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 test_only) +{ +    int isrec = 0; +    Wordcode d; +    FDHead h; +    FuncDump f; +    struct stat lsbuf; + +    if (!sbuf) { +	if (zwcstat(file, &lsbuf)) +	    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. */ +	if (test_only) +	{ +	    /* This is all we need.  Just return dummy. */ +	    return &dummy_eprog; +	} + +#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++; +} + +/**/ +static void +freedump(FuncDump f) +{ +    munmap((void *) f->addr, f->len); +    zclose(f->fd); +    zsfree(f->filename); +    zfree(f, sizeof(*f)); +} + +/* 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; +	    freedump(f); +	} +    } +} + +#ifndef FD_CLOEXEC +/**/ +mod_export void +closedumps(void) +{ +    while (dumps) { +	FuncDump p = dumps->next; +	freedump(dumps); +	dumps = p; +    } +} +#endif + +#else + +void +incrdumpcount(FuncDump f) +{ +} + +void +decrdumpcount(FuncDump f) +{ +} + +#ifndef FD_CLOEXEC +/**/ +mod_export void +closedumps(void) +{ +} +#endif + +#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->node.flags = on; +	shf->funcdef = mkautofn(shf); +	shf->sticky = NULL; +	shfunctab->addnode(shfunctab, ztrdup(fdname(n) + fdhtail(n)), shf); +	if (OPT_ISSET(ops,'X') && eval_autoload(shf, shf->node.nam, ops, func)) +	    ret = 1; +    } +    return ret; +} | 
