summaryrefslogtreecommitdiff
path: root/dotfiles/system/.zsh/modules/Src/parse.c
diff options
context:
space:
mode:
Diffstat (limited to 'dotfiles/system/.zsh/modules/Src/parse.c')
-rw-r--r--dotfiles/system/.zsh/modules/Src/parse.c3977
1 files changed, 0 insertions, 3977 deletions
diff --git a/dotfiles/system/.zsh/modules/Src/parse.c b/dotfiles/system/.zsh/modules/Src/parse.c
deleted file mode 100644
index 83383f1..0000000
--- a/dotfiles/system/.zsh/modules/Src/parse.c
+++ /dev/null
@@ -1,3977 +0,0 @@
-/*
- * 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;
-}