1
0
Fork 0
mirror of git://git.code.sf.net/p/zsh/code synced 2025-09-01 09:41:44 +02:00
zsh/Src/parse.c
2000-02-25 15:15:52 +00:00

2195 lines
42 KiB
C

/*
* 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;
/* != 0 if we are in the middle of a [[ ... ]] */
/**/
mod_export int incond;
/* != 0 if we are after a redirection (for ctxtlex only) */
/**/
mod_export int inredir;
/* != 0 if we are about to read a case pattern */
/**/
int incasepat;
/* != 0 if we just read a newline */
/**/
int isnewlin;
/* != 0 if we are after a for keyword */
/**/
int infor;
/* list of here-documents */
/**/
struct heredocs *hdocs;
#define YYERROR(O) { tok = LEXERR; ecused = (O); return 0; }
#define YYERRORV(O) { tok = LEXERR; ecused = (O); return; }
#define COND_ERROR(X,Y) do { \
zwarn(X,Y,0); \
herrflush(); \
if (noerrs != 2) \
errflag = 1; \
YYERROR(ecused) \
} while(0)
/*
* Word code.
*
* For now we simply post-process the syntax tree produced by the
* parser. We compile it into a struct eprog. Some day the parser
* above should be changed to emit the word code directly.
*
* Word code layout:
*
* WC_END
* - end of program code
*
* WC_LIST
* - data contains type (sync, ...)
* - follwed by code for this list
* - if not (type & Z_END), followed by next WC_LIST
*
* WC_SUBLIST
* - data contains type (&&, ||, END) and flags (coprog, not)
* - followed by code for sublist
* - if not (type == END), followed by next WC_SUBLIST
*
* WC_PIPE
* - data contains type (end, mid) and LINENO
* - if not (type == END), followed by offset to next WC_PIPE
* - followed by command
* - if not (type == END), followed by next WC_PIPE
*
* WC_REDIR
* - must precede command-code (or WC_ASSIGN)
* - data contains type (<, >, ...)
* - followed by fd1 and name from struct redir
*
* WC_ASSIGN
* - data contains type (scalar, array) and number of array-elements
* - followed by name and value
*
* WC_SIMPLE
* - data contains the number of arguments (plus command)
* - followed by strings
*
* WC_SUBSH
* - data unused
* - followed by list
*
* WC_CURSH
* - data unused
* - followed by list
*
* WC_TIMED
* - data contains type (followed by pipe or not)
* - if (type == PIPE), followed by pipe
*
* WC_FUNCDEF
* - data contains offset to after body-strings
* - followed by number of names
* - followed by names
* - followed by number of codes for body
* - followed by number of patterns for body
* - follwoed by codes for body
* - followed by strings for body
*
* WC_FOR
* - data contains type (list, ...) and offset to after body
* - if (type == COND), followed by init, cond, advance expressions
* - else if (type == PPARAM), followed by param name
* - else if (type == LIST), followed by param name, num strings, strings
* - followed by body
*
* WC_SELECT
* - data contains type (list, ...) and offset to after body
* - if (type == PPARAM), followed by param name
* - else if (type == LIST), followed by param name, num strings, strings
* - followed by body
*
* WC_WHILE
* - data contains type (while, until) and ofsset to after body
* - followed by condition
* - followed by body
*
* WC_REPEAT
* - data contains offset to after body
* - followed by number-string
* - followed by body
*
* WC_CASE
* - first CASE is always of type HEAD, data contains offset to esac
* - after that CASEs of type OR (;;) and AND (;&), data is offset to
* next case
* - each OR/AND case is followed by pattern, pattern-number, list
*
* WC_IF
* - first IF is of type HEAD, data contains offset to fi
* - after that IFs of type IF, ELIF, ELSE, data is offset to next
* - each non-HEAD is followed by condition (only IF, ELIF) and body
*
* WC_COND
* - data contains type
* - if (type == AND/OR), data contains offset to after this one,
* followed by two CONDs
* - else if (type == NOT), followed by COND
* - else if (type == MOD), followed by name and strings
* - else if (type == MODI), followed by name, left, right
* - else if (type == STR[N]EQ), followed by left, right, pattern-number
* - else if (has two args) followed by left, right
* - else followed by string
*
* WC_ARITH
* - followed by string (there's only one)
*
* WC_AUTOFN
* - only used by the autoload builtin
*
* Lists and sublists may also be simplified, indicated by the presence
* of the Z_SIMPLE or WC_SUBLIST_SIMPLE flags. In this case they are only
* followed by a slot containing the line number, not by a WC_SUBLIST or
* WC_PIPE, respectively. The real advantage of simplified lists and
* sublists is that they can be executed faster, see exec.c. In the
* parser, the test if a list can be simplified is done quite simply
* by passing a int* around which gets set to non-zero if the thing
* just parsed is `complex', i.e. may need to be run by forking or
* some such.
*
* In each of the above, strings are encoded as one word code. For empty
* strings this is the bit pattern 11x, the lowest bit is non-zero if the
* string contains tokens and zero otherwise (this is true for the other
* ways to encode strings, too). For short strings (one to three
* characters), this is the marker 01x with the 24 bits above that
* containing the characters. Longer strings are encoded as the offset
* into the strs character array stored in the eprog struct shifted by
* two and ored with the bit pattern 0x.
* The ecstr() function that adds the code for a string uses a simple
* list of strings already added so that long strings are encoded only
* once.
*
* Note also that in the eprog struct the pattern, code, and string
* arrays all point to the same memory block.
*
*
* To make things even faster in future versions, we could not only
* test if the strings contain tokens, but instead what kind of
* expansions need to be done on strings. In the execution code we
* could then use these flags for a specialized version of prefork()
* to avoid a lot of string parsing and some more string duplication.
*/
/**/
int eclen, ecused, ecfree, ecnpats;
/**/
Wordcode ecbuf;
/**/
Eccstr ecstrs;
/**/
int ecsoffs;
/* Make at least n bytes free (aligned to sizeof(wordcode)). */
static int
ecspace(int n)
{
n = (n + sizeof(wordcode) - 1) / sizeof(wordcode);
if (ecfree < n) {
int a = (n > 256 ? n : 256);
ecbuf = (Wordcode) hrealloc((char *) ecbuf, eclen * sizeof(wordcode),
(eclen + a) * sizeof(wordcode));
eclen += a;
ecfree += a;
}
ecused += n;
ecfree -= n;
return ecused - 1;
}
/* Insert n free code-slots at position p. */
static void
ecispace(int p, int n)
{
int m;
if (ecfree < n) {
int a = (n > 256 ? n : 256);
ecbuf = (Wordcode) hrealloc((char *) ecbuf, eclen * sizeof(wordcode),
(eclen + a) * sizeof(wordcode));
eclen += a;
ecfree += a;
}
if ((m = ecused - p) > 0)
memmove(ecbuf + p + n, ecbuf + p, m * sizeof(wordcode));
ecused += n;
}
/* Add one wordcode. */
static int
ecadd(wordcode c)
{
if (ecfree < 1) {
ecbuf = (Wordcode) hrealloc((char *) ecbuf, eclen * sizeof(wordcode),
(eclen + 256) * sizeof(wordcode));
eclen += 256;
ecfree += 256;
}
ecbuf[ecused] = c;
ecused++;
ecfree--;
return ecused - 1;
}
/* Delete a wordcode. */
static void
ecdel(int p)
{
int n = ecused - p - 1;
if (n > 0)
memmove(ecbuf + p, ecbuf + p + 1, n * sizeof(wordcode));
ecused--;
}
/* Build the wordcode for a string. */
static wordcode
ecstrcode(char *s)
{
int l, t = has_token(s);
if ((l = strlen(s) + 1) && l <= 4) {
wordcode c = (t ? 3 : 2);
switch (l) {
case 4: c |= ((wordcode) STOUC(s[2])) << 19;
case 3: c |= ((wordcode) STOUC(s[1])) << 11;
case 2: c |= ((wordcode) STOUC(s[0])) << 3; break;
case 1: c = (t ? 7 : 6); break;
}
return c;
} else {
Eccstr p, q = NULL;
for (p = ecstrs; p; q = p, p = p->next)
if (!strcmp(s, p->str))
return p->offs;
p = (Eccstr) zhalloc(sizeof(*p));
p->next = NULL;
if (q)
q->next = p;
else
ecstrs = p;
p->offs = (ecsoffs << 2) | (t ? 1 : 0);
p->str = s;
ecsoffs += l;
return p->offs;
}
}
static int
ecstr(char *s)
{
return ecadd(ecstrcode(s));
}
#define par_save_list(C) \
do { \
int eu = ecused; \
par_list(C); \
if (eu == ecused) ecadd(WCB_END()); \
} while (0)
#define par_save_list1(C) \
do { \
int eu = ecused; \
par_list1(C); \
if (eu == ecused) ecadd(WCB_END()); \
} while (0)
/* Initialise wordcode buffer. */
static void
init_parse(void)
{
ecbuf = (Wordcode) zhalloc((eclen = ecfree = 256) * sizeof(wordcode));
ecused = 0;
ecstrs = NULL;
ecsoffs = ecnpats = 0;
}
/* Build eprog. */
static Eprog
bld_eprog(void)
{
Eprog ret;
Eccstr p;
char *q;
int l;
ecadd(WCB_END());
ret = (Eprog) zhalloc(sizeof(*ret));
ret->len = ((ecnpats * sizeof(Patprog)) +
(ecused * sizeof(wordcode)) +
ecsoffs);
ret->npats = ecnpats;
ret->pats = (Patprog *) zhalloc(ret->len);
ret->prog = (Wordcode) (ret->pats + ecnpats);
ret->strs = (char *) (ret->prog + ecused);
ret->shf = NULL;
ret->heap = 1;
for (l = 0; l < ecnpats; l++)
ret->pats[l] = dummy_patprog1;
memcpy(ret->prog, ecbuf, ecused * sizeof(wordcode));
for (p = ecstrs, q = ret->strs; p; p = p->next, q += l) {
l = strlen(p->str) + 1;
memcpy(q, p->str, l);
}
return ret;
}
/*
* event : ENDINPUT
* | SEPER
* | sublist [ SEPER | AMPER | AMPERBANG ]
*/
/**/
Eprog
parse_event(void)
{
tok = ENDINPUT;
incmdpos = 1;
yylex();
init_parse();
return ((par_event()) ? bld_eprog() : NULL);
}
/**/
static int
par_event(void)
{
int r = 0, p, c = 0;
while (tok == SEPER) {
if (isnewlin > 0)
return 0;
yylex();
}
if (tok == ENDINPUT)
return 0;
p = ecadd(0);
if (par_sublist(&c)) {
if (tok == ENDINPUT) {
set_list_code(p, Z_SYNC, c);
r = 1;
} else if (tok == SEPER) {
set_list_code(p, Z_SYNC, c);
if (isnewlin <= 0)
yylex();
r = 1;
} else if (tok == AMPER) {
set_list_code(p, Z_ASYNC, c);
yylex();
r = 1;
} else if (tok == AMPERBANG) {
set_list_code(p, (Z_ASYNC | Z_DISOWN), c);
yylex();
r = 1;
}
}
if (!r) {
if (errflag) {
yyerror(0);
ecused--;
return 0;
}
yyerror(1);
herrflush();
if (noerrs != 2)
errflag = 1;
ecused--;
return 0;
} else {
int oec = ecused;
par_event();
if (ecused == oec)
ecbuf[p] |= wc_bdata(Z_END);
}
return 1;
}
/**/
mod_export Eprog
parse_list(void)
{
int c = 0;
tok = ENDINPUT;
incmdpos = 1;
yylex();
init_parse();
par_list(&c);
#if 0
if (tok == LEXERR)
#endif
if (tok != ENDINPUT) {
yyerror(0);
return NULL;
}
return bld_eprog();
}
/**/
mod_export Eprog
parse_cond(void)
{
init_parse();
if (!par_cond())
return NULL;
return bld_eprog();
}
/* This adds a list wordcode. The important bit about this is that it also
* tries to optimise this to a Z_SIMPLE list code. */
/**/
static void
set_list_code(int p, int type, int complex)
{
if (!complex && (type == Z_SYNC || type == (Z_SYNC | Z_END)) &&
WC_SUBLIST_TYPE(ecbuf[p + 1]) == WC_SUBLIST_END) {
int ispipe = !(WC_SUBLIST_FLAGS(ecbuf[p + 1]) & WC_SUBLIST_SIMPLE);
ecbuf[p] = WCB_LIST((type | Z_SIMPLE), ecused - 2 - p);
ecdel(p + 1);
if (ispipe)
ecbuf[p + 1] = WC_PIPE_LINENO(ecbuf[p + 1]);
} else
ecbuf[p] = WCB_LIST(type, 0);
}
/* The same for sublists. */
/**/
static void
set_sublist_code(int p, int type, int flags, int skip, int complex)
{
if (complex)
ecbuf[p] = WCB_SUBLIST(type, flags, skip);
else {
ecbuf[p] = WCB_SUBLIST(type, (flags | WC_SUBLIST_SIMPLE), skip);
ecbuf[p + 1] = WC_PIPE_LINENO(ecbuf[p + 1]);
}
}
/*
* list : { SEPER } [ sublist [ { SEPER | AMPER | AMPERBANG } list ] ]
*/
/**/
static int
par_list(int *complex)
{
int p, lp = -1, c;
rec:
while (tok == SEPER)
yylex();
p = ecadd(0);
c = 0;
if (par_sublist(&c)) {
*complex |= c;
if (tok == SEPER || tok == AMPER || tok == AMPERBANG) {
if (tok != SEPER)
*complex = 1;
set_list_code(p, ((tok == SEPER) ? Z_SYNC :
(tok == AMPER) ? Z_ASYNC :
(Z_ASYNC | Z_DISOWN)), c);
incmdpos = 1;
do {
yylex();
} while (tok == SEPER);
lp = p;
goto rec;
} else
set_list_code(p, (Z_SYNC | Z_END), c);
return 1;
} else {
ecused--;
if (lp >= 0) {
ecbuf[lp] |= wc_bdata(Z_END);
return 1;
}
return 0;
}
}
/**/
static int
par_list1(int *complex)
{
int p = ecadd(0), c = 0;
if (par_sublist(&c)) {
set_list_code(p, (Z_SYNC | Z_END), c);
*complex |= c;
return 1;
} else {
ecused--;
return 0;
}
}
/*
* sublist : sublist2 [ ( DBAR | DAMPER ) { SEPER } sublist ]
*/
/**/
static int
par_sublist(int *complex)
{
int f, p, c = 0;
p = ecadd(0);
if ((f = par_sublist2(&c)) != -1) {
int e = ecused;
*complex |= c;
if (tok == DBAR || tok == DAMPER) {
int qtok = tok, sl;
cmdpush(tok == DBAR ? CS_CMDOR : CS_CMDAND);
yylex();
while (tok == SEPER)
yylex();
sl = par_sublist(complex);
set_sublist_code(p, (sl ? (qtok == DBAR ?
WC_SUBLIST_OR : WC_SUBLIST_AND) :
WC_SUBLIST_END),
f, (e - 1 - p), c);
cmdpop();
} else
set_sublist_code(p, WC_SUBLIST_END, f, (e - 1 - p), c);
return 1;
} else {
ecused--;
return 0;
}
}
/*
* sublist2 : [ COPROC | BANG ] pline
*/
/**/
static int
par_sublist2(int *complex)
{
int f = 0;
if (tok == COPROC) {
*complex = 1;
f |= WC_SUBLIST_COPROC;
yylex();
} else if (tok == BANG) {
*complex = 1;
f |= WC_SUBLIST_NOT;
yylex();
}
if (!par_pline(complex) && !f)
return -1;
return f;
}
/*
* pline : cmd [ ( BAR | BARAMP ) { SEPER } pline ]
*/
/**/
static int
par_pline(int *complex)
{
int p, line = lineno;
p = ecadd(0);
if (!par_cmd(complex)) {
ecused--;
return 0;
}
if (tok == BAR) {
*complex = 1;
cmdpush(CS_PIPE);
yylex();
while (tok == SEPER)
yylex();
ecbuf[p] = WCB_PIPE(WC_PIPE_MID, (line >= 0 ? line + 1 : 0));
ecispace(p + 1, 1);
ecbuf[p + 1] = ecused - 1 - p;
par_pline(complex);
cmdpop();
return 1;
} else if (tok == BARAMP) {
int r;
for (r = p + 1; wc_code(ecbuf[r]) == WC_REDIR; r += 3);
ecispace(r, 3);
p += 3;
ecbuf[r] = WCB_REDIR(MERGEOUT);
ecbuf[r + 1] = 2;
ecbuf[r + 2] = ecstrcode("1");
*complex = 1;
cmdpush(CS_ERRPIPE);
yylex();
ecbuf[p] = WCB_PIPE(WC_PIPE_MID, (line >= 0 ? line + 1 : 0));
ecispace(p + 1, 1);
ecbuf[p + 1] = ecused - 1 - p;
par_pline(complex);
cmdpop();
return 1;
} else {
ecbuf[p] = WCB_PIPE(WC_PIPE_END, (line >= 0 ? line + 1 : 0));
return 1;
}
}
/*
* cmd : { redir } ( for | case | if | while | repeat |
* subsh | funcdef | time | dinbrack | dinpar | simple ) { redir }
*/
/**/
static int
par_cmd(int *complex)
{
int r, nr = 0;
r = ecused;
if (IS_REDIROP(tok)) {
*complex = 1;
while (IS_REDIROP(tok)) {
nr++;
par_redir(&r);
}
}
switch (tok) {
case FOR:
cmdpush(CS_FOR);
par_for(complex);
cmdpop();
break;
case FOREACH:
cmdpush(CS_FOREACH);
par_for(complex);
cmdpop();
break;
case SELECT:
*complex = 1;
cmdpush(CS_SELECT);
par_for(complex);
cmdpop();
break;
case CASE:
cmdpush(CS_CASE);
par_case(complex);
cmdpop();
break;
case IF:
par_if(complex);
break;
case WHILE:
cmdpush(CS_WHILE);
par_while(complex);
cmdpop();
break;
case UNTIL:
cmdpush(CS_UNTIL);
par_while(complex);
cmdpop();
break;
case REPEAT:
cmdpush(CS_REPEAT);
par_repeat(complex);
cmdpop();
break;
case INPAR:
*complex = 1;
cmdpush(CS_SUBSH);
par_subsh(complex);
cmdpop();
break;
case INBRACE:
cmdpush(CS_CURSH);
par_subsh(complex);
cmdpop();
break;
case FUNC:
cmdpush(CS_FUNCDEF);
par_funcdef();
cmdpop();
break;
case TIME:
*complex = 1;
par_time();
break;
case DINBRACK:
cmdpush(CS_COND);
par_dinbrack();
cmdpop();
break;
case DINPAR:
ecadd(WCB_ARITH());
ecstr(tokstr);
yylex();
break;
default:
{
int sr;
if (!(sr = par_simple(complex, nr))) {
if (!nr)
return 0;
} else {
/* Three codes per redirection. */
if (sr > 1) {
*complex = 1;
r += (sr - 1) * 3;
}
}
}
break;
}
if (IS_REDIROP(tok)) {
*complex = 1;
while (IS_REDIROP(tok))
par_redir(&r);
}
incmdpos = 1;
incasepat = 0;
incond = 0;
return 1;
}
/*
* for : ( FOR DINPAR expr SEMI expr SEMI expr DOUTPAR |
* ( FOR[EACH] | SELECT ) name ( "in" wordlist | INPAR wordlist OUTPAR ) )
* { SEPER } ( DO list DONE | INBRACE list OUTBRACE | list ZEND | list1 )
*/
/**/
static void
par_for(int *complex)
{
int oecused = ecused, csh = (tok == FOREACH), p, sel = (tok == SELECT);
int type;
p = ecadd(0);
incmdpos = 0;
infor = tok == FOR ? 2 : 0;
yylex();
if (tok == DINPAR) {
yylex();
if (tok != DINPAR)
YYERRORV(oecused);
ecstr(tokstr);
yylex();
if (tok != DINPAR)
YYERRORV(oecused);
ecstr(tokstr);
yylex();
if (tok != DOUTPAR)
YYERRORV(oecused);
ecstr(tokstr);
infor = 0;
incmdpos = 1;
yylex();
type = WC_FOR_COND;
} else {
infor = 0;
if (tok != STRING || !isident(tokstr))
YYERRORV(oecused);
ecstr(tokstr);
incmdpos = 1;
yylex();
if (tok == STRING && !strcmp(tokstr, "in")) {
int np, n;
incmdpos = 0;
yylex();
np = ecadd(0);
n = par_wordlist();
if (tok != SEPER)
YYERRORV(oecused);
ecbuf[np] = n;
type = (sel ? WC_SELECT_LIST : WC_FOR_LIST);
} else if (tok == INPAR) {
int np, n;
incmdpos = 0;
yylex();
np = ecadd(0);
n = par_nl_wordlist();
if (tok != OUTPAR)
YYERRORV(oecused);
ecbuf[np] = n;
incmdpos = 1;
yylex();
type = (sel ? WC_SELECT_LIST : WC_FOR_LIST);
} else
type = (sel ? WC_SELECT_PPARAM : WC_FOR_PPARAM);
}
incmdpos = 1;
while (tok == SEPER)
yylex();
if (tok == DO) {
yylex();
par_save_list(complex);
if (tok != DONE)
YYERRORV(oecused);
yylex();
} else if (tok == INBRACE) {
yylex();
par_save_list(complex);
if (tok != OUTBRACE)
YYERRORV(oecused);
yylex();
} else if (csh || isset(CSHJUNKIELOOPS)) {
par_save_list(complex);
if (tok != ZEND)
YYERRORV(oecused);
yylex();
} else if (unset(SHORTLOOPS)) {
YYERRORV(oecused);
} else
par_save_list1(complex);
ecbuf[p] = (sel ?
WCB_SELECT(type, ecused - 1 - p) :
WCB_FOR(type, ecused - 1 - p));
}
/*
* case : CASE STRING { SEPER } ( "in" | INBRACE )
{ { SEPER } STRING { BAR STRING } OUTPAR
list [ DSEMI | SEMIAMP ] }
{ SEPER } ( "esac" | OUTBRACE )
*/
/**/
static void
par_case(int *complex)
{
int oecused = ecused, brflag, p, pp, n = 1, type;
p = ecadd(0);
incmdpos = 0;
yylex();
if (tok != STRING)
YYERRORV(oecused);
ecstr(tokstr);
incmdpos = 1;
yylex();
while (tok == SEPER)
yylex();
if (!(tok == STRING && !strcmp(tokstr, "in")) && tok != INBRACE)
YYERRORV(oecused);
brflag = (tok == INBRACE);
incasepat = 1;
incmdpos = 0;
yylex();
for (;;) {
char *str;
while (tok == SEPER)
yylex();
if (tok == OUTBRACE)
break;
if (tok != STRING)
YYERRORV(oecused);
if (!strcmp(tokstr, "esac"))
break;
str = dupstring(tokstr);
incasepat = 0;
incmdpos = 1;
type = WC_CASE_OR;
for (;;) {
yylex();
if (tok == OUTPAR) {
incasepat = 0;
incmdpos = 1;
yylex();
break;
} else if (tok == BAR) {
char *str2;
int sl = strlen(str);
incasepat = 1;
incmdpos = 0;
str2 = hcalloc(sl + 2);
strcpy(str2, str);
str2[sl] = Bar;
str2[sl+1] = '\0';
str = str2;
} else {
int sl = strlen(str);
if (!sl || str[sl - 1] != Bar) {
/* POSIX allows (foo*) patterns */
int pct;
char *s;
for (s = str, pct = 0; *s; s++) {
if (*s == Inpar)
pct++;
if (!pct)
break;
if (pct == 1) {
if (*s == Bar || *s == Inpar)
while (iblank(s[1]))
chuck(s+1);
if (*s == Bar || *s == Outpar)
while (iblank(s[-1]) &&
(s < str + 1 || s[-2] != Meta))
chuck(--s);
}
if (*s == Outpar)
pct--;
}
if (*s || pct || s == str)
YYERRORV(oecused);
/* Simplify pattern by removing surrounding (...) */
sl = strlen(str);
DPUTS(*str != Inpar || str[sl - 1] != Outpar,
"BUG: strange case pattern");
str[sl - 1] = '\0';
chuck(str);
break;
} else {
char *str2;
if (tok != STRING)
YYERRORV(oecused);
str2 = hcalloc(sl + strlen(tokstr) + 1);
strcpy(str2, str);
strcpy(str2 + sl, tokstr);
str = str2;
}
}
}
pp = ecadd(0);
ecstr(str);
ecadd(ecnpats++);
par_save_list(complex);
n++;
if (tok == SEMIAMP)
type = WC_CASE_AND;
ecbuf[pp] = WCB_CASE(type, ecused - 1 - pp);
if ((tok == ESAC && !brflag) || (tok == OUTBRACE && brflag))
break;
if (tok != DSEMI && tok != SEMIAMP)
YYERRORV(oecused);
incasepat = 1;
incmdpos = 0;
yylex();
}
incmdpos = 1;
yylex();
ecbuf[p] = WCB_CASE(WC_CASE_HEAD, ecused - 1 - p);
}
/*
* if : { ( IF | ELIF ) { SEPER } ( INPAR list OUTPAR | list )
{ SEPER } ( THEN list | INBRACE list OUTBRACE | list1 ) }
[ FI | ELSE list FI | ELSE { SEPER } INBRACE list OUTBRACE ]
(you get the idea...?)
*/
/**/
static void
par_if(int *complex)
{
int oecused = ecused, xtok, p, pp, type, usebrace = 0;
unsigned char nc;
p = ecadd(0);
for (;;) {
xtok = tok;
cmdpush(xtok == IF ? CS_IF : CS_ELIF);
yylex();
if (xtok == FI)
break;
if (xtok == ELSE)
break;
while (tok == SEPER)
yylex();
if (!(xtok == IF || xtok == ELIF)) {
cmdpop();
YYERRORV(oecused);
}
pp = ecadd(0);
type = (xtok == IF ? WC_IF_IF : WC_IF_ELIF);
par_save_list(complex);
incmdpos = 1;
while (tok == SEPER)
yylex();
xtok = FI;
nc = cmdstack[cmdsp - 1] == CS_IF ? CS_IFTHEN : CS_ELIFTHEN;
if (tok == THEN) {
usebrace = 0;
cmdpop();
cmdpush(nc);
yylex();
par_save_list(complex);
ecbuf[pp] = WCB_IF(type, ecused - 1 - pp);
incmdpos = 1;
cmdpop();
} else if (tok == INBRACE) {
usebrace = 1;
cmdpop();
cmdpush(nc);
yylex();
par_save_list(complex);
if (tok != OUTBRACE) {
cmdpop();
YYERRORV(oecused);
}
ecbuf[pp] = WCB_IF(type, ecused - 1 - pp);
yylex();
incmdpos = 1;
if (tok == SEPER)
break;
cmdpop();
} else if (unset(SHORTLOOPS)) {
cmdpop();
YYERRORV(oecused);
} else {
cmdpop();
cmdpush(nc);
par_save_list1(complex);
ecbuf[pp] = WCB_IF(type, ecused - 1 - pp);
incmdpos = 1;
break;
}
}
cmdpop();
if (xtok == ELSE) {
pp = ecadd(0);
cmdpush(CS_ELSE);
while (tok == SEPER)
yylex();
if (tok == INBRACE && usebrace) {
yylex();
par_save_list(complex);
if (tok != OUTBRACE) {
cmdpop();
YYERRORV(oecused);
}
} else {
par_save_list(complex);
if (tok != FI) {
cmdpop();
YYERRORV(oecused);
}
}
ecbuf[pp] = WCB_IF(WC_IF_ELSE, ecused - 1 - pp);
yylex();
cmdpop();
}
ecbuf[p] = WCB_IF(WC_IF_HEAD, ecused - 1 - p);
}
/*
* while : ( WHILE | UNTIL ) ( INPAR list OUTPAR | list ) { SEPER }
( DO list DONE | INBRACE list OUTBRACE | list ZEND )
*/
/**/
static void
par_while(int *complex)
{
int oecused = ecused, p;
int type = (tok == UNTIL ? WC_WHILE_UNTIL : WC_WHILE_WHILE);
p = ecadd(0);
yylex();
par_save_list(complex);
incmdpos = 1;
while (tok == SEPER)
yylex();
if (tok == DO) {
yylex();
par_save_list(complex);
if (tok != DONE)
YYERRORV(oecused);
yylex();
} else if (tok == INBRACE) {
yylex();
par_save_list(complex);
if (tok != OUTBRACE)
YYERRORV(oecused);
yylex();
} else if (isset(CSHJUNKIELOOPS)) {
par_save_list(complex);
if (tok != ZEND)
YYERRORV(oecused);
yylex();
} else
YYERRORV(oecused);
ecbuf[p] = WCB_WHILE(type, ecused - 1 - p);
}
/*
* repeat : REPEAT STRING { SEPER } ( DO list DONE | list1 )
*/
/**/
static void
par_repeat(int *complex)
{
int oecused = ecused, p;
p = ecadd(0);
incmdpos = 0;
yylex();
if (tok != STRING)
YYERRORV(oecused);
ecstr(tokstr);
incmdpos = 1;
yylex();
while (tok == SEPER)
yylex();
if (tok == DO) {
yylex();
par_save_list(complex);
if (tok != DONE)
YYERRORV(oecused);
yylex();
} else if (tok == INBRACE) {
yylex();
par_save_list(complex);
if (tok != OUTBRACE)
YYERRORV(oecused);
yylex();
} else if (isset(CSHJUNKIELOOPS)) {
par_save_list(complex);
if (tok != ZEND)
YYERRORV(oecused);
yylex();
} else if (unset(SHORTLOOPS)) {
YYERRORV(oecused);
} else
par_save_list1(complex);
ecbuf[p] = WCB_REPEAT(ecused - 1 - p);
}
/*
* subsh : ( INPAR | INBRACE ) list ( OUTPAR | OUTBRACE )
*/
/**/
static void
par_subsh(int *complex)
{
int oecused = ecused, otok = tok;
ecadd(tok == INPAR ? WCB_SUBSH() : WCB_CURSH());
yylex();
par_save_list(complex);
if (tok != ((otok == INPAR) ? OUTPAR : OUTBRACE))
YYERRORV(oecused);
incmdpos = 1;
yylex();
}
/*
* funcdef : FUNCTION wordlist [ INOUTPAR ] { SEPER }
* ( list1 | INBRACE list OUTBRACE )
*/
/**/
static void
par_funcdef(void)
{
int oecused = ecused, oldlineno = lineno, num = 0, sbeg, onp, p, c = 0;
Eccstr ostrs;
lineno = 0;
nocorrect = 1;
incmdpos = 0;
yylex();
p = ecadd(0);
ecadd(0);
incmdpos = 1;
while (tok == STRING) {
if (*tokstr == Inbrace && !tokstr[1]) {
tok = INBRACE;
break;
}
ecstr(tokstr);
num++;
yylex();
}
ecadd(0);
ecadd(0);
nocorrect = 0;
if (tok == INOUTPAR)
yylex();
while (tok == SEPER)
yylex();
sbeg = ecsoffs;
ecsoffs = 0;
ostrs = ecstrs;
ecstrs = NULL;
onp = ecnpats;
ecnpats = 0;
if (tok == INBRACE) {
yylex();
par_list(&c);
if (tok != OUTBRACE) {
lineno += oldlineno;
ecsoffs = sbeg;
ecstrs = ostrs;
ecnpats = onp;
YYERRORV(oecused);
}
yylex();
} else if (unset(SHORTLOOPS)) {
lineno += oldlineno;
ecsoffs = sbeg;
ecstrs = ostrs;
ecnpats = onp;
YYERRORV(oecused);
} else
par_list1(&c);
ecadd(WCB_END());
ecbuf[p + num + 2] = ecused - num - p;
ecbuf[p + num + 3] = ecnpats;
ecbuf[p + 1] = num;
if (ecsoffs) {
int beg = ecused, l;
Eccstr sp;
char *sq;
ecspace(ecsoffs);
for (sp = ecstrs, sq = (char *) (ecbuf + beg); sp;
sp = sp->next, sq += l) {
l = strlen(sp->str) + 1;
memcpy(sq, sp->str, l);
}
}
lineno += oldlineno;
ecsoffs = sbeg;
ecstrs = ostrs;
ecnpats = onp;
ecbuf[p] = WCB_FUNCDEF(ecused - 1 - p);
}
/*
* time : TIME sublist2
*/
/**/
static void
par_time(void)
{
int p, f, c = 0;
yylex();
p = ecadd(0);
ecadd(0);
f = par_sublist2(&c);
ecbuf[p] = WCB_TIMED((p + 1 == ecused) ? WC_TIMED_EMPTY : WC_TIMED_PIPE);
set_sublist_code(p + 1, WC_SUBLIST_END, f, ecused - 2 - p, c);
}
/*
* dinbrack : DINBRACK cond DOUTBRACK
*/
/**/
static void
par_dinbrack(void)
{
int oecused = ecused;
incond = 1;
incmdpos = 0;
yylex();
par_cond();
if (tok != DOUTBRACK)
YYERRORV(oecused);
incond = 0;
incmdpos = 1;
yylex();
}
/*
* simple : { COMMAND | EXEC | NOGLOB | NOCORRECT | DASH }
{ STRING | ENVSTRING | ENVARRAY wordlist OUTPAR | redir }
[ INOUTPAR { SEPER } ( list1 | INBRACE list OUTBRACE ) ]
*/
/**/
static int
par_simple(int *complex, int nr)
{
int oecused = ecused, isnull = 1, r, argc = 0, p, isfunc = 0, sr = 0;
int c = *complex;
r = ecused;
for (;;) {
if (tok == NOCORRECT) {
*complex = c = 1;
nocorrect = 1;
} else if (tok == ENVSTRING) {
char *p, *name, *str;
ecadd(WCB_ASSIGN(WC_ASSIGN_SCALAR, 0));
name = tokstr;
for (p = tokstr; *p && *p != Inbrack && *p != '='; p++);
if (*p == Inbrack && !skipparens(Inbrack, Outbrack, &p) &&
*p == '=') {
*p = '\0';
str = p + 1;
} else
equalsplit(tokstr, &str);
ecstr(name);
ecstr(str);
isnull = 0;
} else if (tok == ENVARRAY) {
int oldcmdpos = incmdpos, n;
p = ecadd(0);
incmdpos = 0;
ecstr(tokstr);
cmdpush(CS_ARRAY);
yylex();
n = par_nl_wordlist();
ecbuf[p] = WCB_ASSIGN(WC_ASSIGN_ARRAY, n);
cmdpop();
if (tok != OUTPAR)
YYERROR(oecused);
incmdpos = oldcmdpos;
isnull = 0;
} else
break;
yylex();
}
if (tok == AMPER || tok == AMPERBANG)
YYERROR(oecused);
p = ecadd(WCB_SIMPLE(0));
for (;;) {
if (tok == STRING) {
*complex = 1;
incmdpos = 0;
ecstr(tokstr);
argc++;
yylex();
} else if (IS_REDIROP(tok)) {
*complex = c = 1;
par_redir(&r);
p += 3; /* 3 codes per redirection */
sr++;
} else if (tok == INOUTPAR) {
int oldlineno = lineno, sbeg, onp;
Eccstr ostrs;
*complex = c;
lineno = 0;
incmdpos = 1;
cmdpush(CS_FUNCDEF);
yylex();
while (tok == SEPER)
yylex();
ecispace(p + 1, 1);
ecbuf[p + 1] = argc;
ecadd(0);
ecadd(0);
sbeg = ecsoffs;
ecsoffs = 0;
ostrs = ecstrs;
ecstrs = NULL;
onp = ecnpats;
ecnpats = 0;
if (tok == INBRACE) {
int c = 0;
yylex();
par_list(&c);
if (tok != OUTBRACE) {
cmdpop();
lineno += oldlineno;
ecsoffs = sbeg;
ecstrs = ostrs;
ecnpats = onp;
YYERROR(oecused);
}
yylex();
} else {
int ll, sl, c = 0;
ll = ecadd(0);
sl = ecadd(0);
par_cmd(&c);
set_sublist_code(sl, WC_SUBLIST_END, 0, ecused - 1 - sl, c);
set_list_code(ll, (Z_SYNC | Z_END), c);
}
cmdpop();
ecadd(WCB_END());
ecbuf[p + argc + 2] = ecused - argc - p;
ecbuf[p + argc + 3] = ecnpats;
if (ecsoffs) {
int beg = ecused, l;
Eccstr sp;
char *sq;
ecspace(ecsoffs);
for (sp = ecstrs, sq = (char *) (ecbuf + beg); sp;
sp = sp->next, sq += l) {
l = strlen(sp->str) + 1;
memcpy(sq, sp->str, l);
}
}
lineno += oldlineno;
ecsoffs = sbeg;
ecstrs = ostrs;
ecnpats = onp;
ecbuf[p] = WCB_FUNCDEF(ecused - 1 - p);
isfunc = 1;
} else
break;
isnull = 0;
}
if (isnull && !(sr + nr)) {
ecused = p;
return 0;
}
incmdpos = 1;
if (!isfunc)
ecbuf[p] = WCB_SIMPLE(argc);
return sr + 1;
}
/*
* redir : ( OUTANG | ... | TRINANG ) STRING
*/
static int redirtab[TRINANG - OUTANG + 1] = {
WRITE,
WRITENOW,
APP,
APPNOW,
READ,
READWRITE,
HEREDOC,
HEREDOCDASH,
MERGEIN,
MERGEOUT,
ERRWRITE,
ERRWRITENOW,
ERRAPP,
ERRAPPNOW,
HERESTR,
};
/**/
static void
par_redir(int *rp)
{
int r = *rp, type, fd1, oldcmdpos, oldnc;
char *name;
oldcmdpos = incmdpos;
incmdpos = 0;
oldnc = nocorrect;
if (tok != INANG && tok != INOUTANG)
nocorrect = 1;
type = redirtab[tok - OUTANG];
fd1 = tokfd;
yylex();
if (tok != STRING && tok != ENVSTRING)
YYERRORV(ecused);
incmdpos = oldcmdpos;
nocorrect = oldnc;
/* assign default fd */
if (fd1 == -1)
fd1 = IS_READFD(type) ? 0 : 1;
name = tokstr;
switch (type) {
case HEREDOC:
case HEREDOCDASH: {
/* <<[-] name */
struct heredocs **hd;
for (hd = &hdocs; *hd; hd = &(*hd)->next);
*hd = zalloc(sizeof(struct heredocs));
(*hd)->next = NULL;
(*hd)->pc = ecbuf + r;
(*hd)->str = tokstr;
/* If we ever need more than three codes (or less), we have to change
* the factors in par_cmd() and par_simple(), too. */
ecispace(r, 3);
*rp = r + 3;
ecbuf[r] = WCB_REDIR(type);
ecbuf[r + 1] = fd1;
yylex();
return;
}
case WRITE:
case WRITENOW:
if (tokstr[0] == Outang && tokstr[1] == Inpar)
/* > >(...) */
type = OUTPIPE;
else if (tokstr[0] == Inang && tokstr[1] == Inpar)
YYERRORV(ecused);
break;
case READ:
if (tokstr[0] == Inang && tokstr[1] == Inpar)
/* < <(...) */
type = INPIPE;
else if (tokstr[0] == Outang && tokstr[1] == Inpar)
YYERRORV(ecused);
break;
case READWRITE:
if ((tokstr[0] == Inang || tokstr[0] == Outang) && tokstr[1] == Inpar)
type = tokstr[0] == Inang ? INPIPE : OUTPIPE;
break;
}
yylex();
/* If we ever need more than three codes (or less), we have to change
* the factors in par_cmd() and par_simple(), too. */
ecispace(r, 3);
*rp = r + 3;
ecbuf[r] = WCB_REDIR(type);
ecbuf[r + 1] = fd1;
ecbuf[r + 2] = ecstrcode(name);
}
/**/
void
setheredoc(Wordcode pc, int type, char *str)
{
pc[0] = WCB_REDIR(type);
pc[2] = ecstrcode(str);
}
/*
* wordlist : { STRING }
*/
/**/
static int
par_wordlist(void)
{
int num = 0;
while (tok == STRING) {
ecstr(tokstr);
num++;
yylex();
}
return num;
}
/*
* nl_wordlist : { STRING | SEPER }
*/
/**/
static int
par_nl_wordlist(void)
{
int num = 0;
while (tok == STRING || tok == SEPER) {
if (tok != SEPER) {
ecstr(tokstr);
num++;
}
yylex();
}
return num;
}
/*
* condlex is yylex for normal parsing, but is altered to allow
* the test builtin to use par_cond.
*/
/**/
void (*condlex) _((void)) = yylex;
/*
* cond : cond_1 { SEPER } [ DBAR { SEPER } cond ]
*/
/**/
static int
par_cond(void)
{
int p = ecused, r;
r = par_cond_1();
while (tok == SEPER)
condlex();
if (tok == DBAR) {
condlex();
while (tok == SEPER)
condlex();
ecispace(p, 1);
par_cond();
ecbuf[p] = WCB_COND(COND_OR, ecused - 1 - p);
return 1;
}
return r;
}
/*
* cond_1 : cond_2 { SEPER } [ DAMPER { SEPER } cond_1 ]
*/
/**/
static int
par_cond_1(void)
{
int r, p = ecused;
r = par_cond_2();
while (tok == SEPER)
condlex();
if (tok == DAMPER) {
condlex();
while (tok == SEPER)
condlex();
ecispace(p, 1);
par_cond_1();
ecbuf[p] = WCB_COND(COND_AND, ecused - 1 - p);
return 1;
}
return r;
}
/*
* cond_2 : BANG cond_2
| INPAR { SEPER } cond_2 { SEPER } OUTPAR
| STRING STRING STRING
| STRING STRING
| STRING ( INANG | OUTANG ) STRING
*/
/**/
static int
par_cond_2(void)
{
char *s1, *s2, *s3;
int dble = 0;
if (condlex == testlex) {
/* See the description of test in POSIX 1003.2 */
if (tok == NULLTOK)
/* no arguments: false */
return par_cond_double(dupstring("-n"), dupstring(""));
if (!*testargs) {
/* one argument: [ foo ] is equivalent to [ -n foo ] */
s1 = tokstr;
condlex();
return par_cond_double(dupstring("-n"), s1);
}
if (testargs[1] && !testargs[2]) {
/* three arguments: if the second argument is a binary operator, *
* perform that binary test on the first and the trird argument */
if (!strcmp(*testargs, "=") ||
!strcmp(*testargs, "==") ||
!strcmp(*testargs, "!=") ||
(**testargs == '-' && get_cond_num(*testargs + 1) >= 0)) {
s1 = tokstr;
condlex();
s2 = tokstr;
condlex();
s3 = tokstr;
condlex();
return par_cond_triple(s1, s2, s3);
}
}
}
if (tok == BANG) {
condlex();
ecadd(WCB_COND(COND_NOT, 0));
return par_cond_2();
}
if (tok == INPAR) {
int r;
condlex();
while (tok == SEPER)
condlex();
r = par_cond();
while (tok == SEPER)
condlex();
if (tok != OUTPAR)
YYERROR(ecused);
condlex();
return r;
}
if (tok != STRING) {
if (tok && tok != LEXERR && condlex == testlex) {
s1 = tokstr;
condlex();
return par_cond_double("-n", s1);
} else
YYERROR(ecused);
}
s1 = tokstr;
if (condlex == testlex)
dble = (*s1 == '-' && strspn(s1+1, "abcdefghknoprstuwxzLONGS") == 1
&& !s1[2]);
condlex();
if (tok == INANG || tok == OUTANG) {
int xtok = tok;
condlex();
if (tok != STRING)
YYERROR(ecused);
s3 = tokstr;
condlex();
ecadd(WCB_COND((xtok == INANG ? COND_STRLT : COND_STRGTR), 0));
ecstr(s1);
ecstr(s3);
return 1;
}
if (tok != STRING) {
if (tok != LEXERR && condlex == testlex) {
if (!dble)
return par_cond_double("-n", s1);
else if (!strcmp(s1, "-t"))
return par_cond_double(s1, "1");
} else
YYERROR(ecused);
}
s2 = tokstr;
incond++; /* parentheses do globbing */
condlex();
incond--; /* parentheses do grouping */
if (tok == STRING && !dble) {
s3 = tokstr;
condlex();
if (tok == STRING) {
LinkList l = newlinklist();
addlinknode(l, s2);
addlinknode(l, s3);
while (tok == STRING) {
addlinknode(l, tokstr);
condlex();
}
return par_cond_multi(s1, l);
} else
return par_cond_triple(s1, s2, s3);
} else
return par_cond_double(s1, s2);
}
/**/
static int
par_cond_double(char *a, char *b)
{
if (a[0] != '-' || !a[1])
COND_ERROR("parse error: condition expected: %s", a);
else if (!a[2] && strspn(a+1, "abcdefgknoprstuwxzhLONGS") == 1) {
ecadd(WCB_COND(a[1], 0));
ecstr(b);
} else {
ecadd(WCB_COND(COND_MOD, 1));
ecstr(a);
ecstr(b);
}
return 1;
}
/**/
static int
get_cond_num(char *tst)
{
static char *condstrs[] =
{
"nt", "ot", "ef", "eq", "ne", "lt", "gt", "le", "ge", NULL
};
int t0;
for (t0 = 0; condstrs[t0]; t0++)
if (!strcmp(condstrs[t0], tst))
return t0;
return -1;
}
/**/
static int
par_cond_triple(char *a, char *b, char *c)
{
int t0;
if ((b[0] == Equals || b[0] == '=') &&
(!b[1] || ((b[1] == Equals || b[1] == '=') && !b[2]))) {
ecadd(WCB_COND(COND_STREQ, 0));
ecstr(a);
ecstr(c);
ecadd(ecnpats++);
} else if (b[0] == '!' && (b[1] == Equals || b[1] == '=') && !b[2]) {
ecadd(WCB_COND(COND_STRNEQ, 0));
ecstr(a);
ecstr(c);
ecadd(ecnpats++);
} else if (b[0] == '-') {
if ((t0 = get_cond_num(b + 1)) > -1) {
ecadd(WCB_COND(t0 + COND_NT, 0));
ecstr(a);
ecstr(c);
} else {
ecadd(WCB_COND(COND_MODI, 0));
ecstr(b);
ecstr(a);
ecstr(c);
}
} else if (a[0] == '-' && a[1]) {
ecadd(WCB_COND(COND_MOD, 2));
ecstr(a);
ecstr(b);
ecstr(c);
} else
COND_ERROR("condition expected: %s", b);
return 1;
}
/**/
static int
par_cond_multi(char *a, LinkList l)
{
if (a[0] != '-' || !a[1])
COND_ERROR("condition expected: %s", a);
else {
LinkNode n;
ecadd(WCB_COND(COND_MOD, countlinknodes(l)));
ecstr(a);
for (n = firstnode(l); n; incnode(n))
ecstr((char *) getdata(n));
}
return 1;
}
/**/
static void
yyerror(int noerr)
{
int t0;
char *t;
if ((t = dupstring(yytext)))
untokenize(t);
for (t0 = 0; t0 != 20; t0++)
if (!t || !t[t0] || t[t0] == '\n')
break;
if (t0 == 20)
zwarn("parse error near `%l...'", t, 20);
else if (t0)
zwarn("parse error near `%l'", t, t0);
else
zwarn("parse error", NULL, 0);
if (!noerr && noerrs != 2)
errflag = 1;
}
/**/
mod_export Eprog
zdupeprog(Eprog p)
{
Eprog r;
int i;
Patprog *pp;
if (p == &dummy_eprog)
return p;
r = (Eprog) zalloc(sizeof(*r));
r->heap = 0;
r->len = p->len;
r->npats = p->npats;
pp = r->pats = (Patprog *) zcalloc(r->len);
r->prog = (Wordcode) (r->pats + r->npats);
r->strs = ((char *) r->prog) + (p->strs - ((char *) p->prog));
memcpy(r->prog, p->prog, r->len - (p->npats * sizeof(Patprog)));
r->shf = NULL;
for (i = r->npats; i--; pp++)
*pp = dummy_patprog1;
return r;
}
static LinkList eprog_free;
/**/
mod_export void
freeeprog(Eprog p)
{
if (p && p != &dummy_eprog)
zaddlinknode(eprog_free, p);
}
/**/
void
freeeprogs(void)
{
Eprog p;
int i;
Patprog *pp;
while ((p = (Eprog) getlinknode(eprog_free))) {
for (i = p->npats, pp = p->pats; i--; pp++)
freepatprog(*pp);
zfree(p->pats, p->len);
zfree(p, sizeof(*p));
}
}
/**/
char *
ecgetstr(Estate s, int dup, int *tok)
{
static char buf[4];
wordcode c = *s->pc++;
char *r;
if (c == 6 || c == 7)
r = "";
else if (c & 2) {
buf[0] = (char) ((c >> 3) & 0xff);
buf[1] = (char) ((c >> 11) & 0xff);
buf[2] = (char) ((c >> 19) & 0xff);
buf[3] = '\0';
r = dupstring(buf);
dup = EC_NODUP;
} else {
r = s->strs + (c >> 2);
}
if (tok)
*tok = (c & 1);
return ((dup == EC_DUP || (dup && (c & 1))) ? dupstring(r) : r);
}
/**/
char *
ecrawstr(Eprog p, Wordcode pc, int *tok)
{
static char buf[4];
wordcode c = *pc;
if (c == 6 || c == 7) {
if (tok)
*tok = (c & 1);
return "";
} else if (c & 2) {
buf[0] = (char) ((c >> 3) & 0xff);
buf[1] = (char) ((c >> 11) & 0xff);
buf[2] = (char) ((c >> 19) & 0xff);
buf[3] = '\0';
if (tok)
*tok = (c & 1);
return buf;
} else {
if (tok)
*tok = (c & 1);
return p->strs + (c >> 2);
}
}
/**/
char **
ecgetarr(Estate s, int num, int dup, int *tok)
{
char **ret, **rp;
int tf = 0, tmp = 0;
ret = rp = (char **) zhalloc((num + 1) * sizeof(char *));
while (num--) {
*rp++ = ecgetstr(s, dup, &tmp);
tf |= tmp;
}
*rp = NULL;
if (tok)
*tok = tf;
return ret;
}
/**/
LinkList
ecgetlist(Estate s, int num, int dup, int *tok)
{
if (num) {
LinkList ret;
int i, tf = 0, tmp = 0;
ret = newsizedlist(num);
for (i = 0; i < num; i++) {
setsizednode(ret, i, ecgetstr(s, dup, &tmp));
tf |= tmp;
}
if (tok)
*tok = tf;
return ret;
}
if (tok)
*tok = 0;
return NULL;
}
/**/
LinkList
ecgetredirs(Estate s)
{
LinkList ret = newlinklist();
wordcode code = *s->pc++;
while (wc_code(code) == WC_REDIR) {
Redir r = (Redir) zhalloc(sizeof(*r));
r->type = WC_REDIR_TYPE(code);
r->fd1 = *s->pc++;
r->name = ecgetstr(s, EC_DUP, NULL);
addlinknode(ret, r);
code = *s->pc++;
}
s->pc--;
return ret;
}
/**/
mod_export struct eprog dummy_eprog;
static wordcode dummy_eprog_code;
/**/
void
init_eprog(void)
{
dummy_eprog_code = WCB_END();
dummy_eprog.len = sizeof(wordcode);
dummy_eprog.prog = &dummy_eprog_code;
dummy_eprog.strs = NULL;
eprog_free = znewlinklist();
}