mirror of
git://git.code.sf.net/p/zsh/code
synced 2024-11-19 21:44:11 +01:00
88a37cc187
unposted: update version to 4.2.3-dev-1
684 lines
14 KiB
C
684 lines
14 KiB
C
/*
|
|
* loop.c - loop execution
|
|
*
|
|
* 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 "loop.pro"
|
|
|
|
/* # of nested loops we are in */
|
|
|
|
/**/
|
|
int loops;
|
|
|
|
/* # of continue levels */
|
|
|
|
/**/
|
|
mod_export int contflag;
|
|
|
|
/* # of break levels */
|
|
|
|
/**/
|
|
mod_export int breaks;
|
|
|
|
/**/
|
|
int
|
|
execfor(Estate state, int do_exec)
|
|
{
|
|
Wordcode end, loop;
|
|
wordcode code = state->pc[-1];
|
|
int iscond = (WC_FOR_TYPE(code) == WC_FOR_COND), ctok = 0, atok = 0;
|
|
int last = 0;
|
|
char *name, *str, *cond = NULL, *advance = NULL;
|
|
zlong val = 0;
|
|
LinkList vars = NULL, args = NULL;
|
|
|
|
end = state->pc + WC_FOR_SKIP(code);
|
|
|
|
if (iscond) {
|
|
str = dupstring(ecgetstr(state, EC_NODUP, NULL));
|
|
singsub(&str);
|
|
if (isset(XTRACE)) {
|
|
char *str2 = dupstring(str);
|
|
untokenize(str2);
|
|
printprompt4();
|
|
fprintf(xtrerr, "%s\n", str2);
|
|
fflush(xtrerr);
|
|
}
|
|
if (!errflag)
|
|
matheval(str);
|
|
if (errflag) {
|
|
state->pc = end;
|
|
return lastval = errflag;
|
|
}
|
|
cond = ecgetstr(state, EC_NODUP, &ctok);
|
|
advance = ecgetstr(state, EC_NODUP, &atok);
|
|
} else {
|
|
vars = ecgetlist(state, *state->pc++, EC_NODUP, NULL);
|
|
|
|
if (WC_FOR_TYPE(code) == WC_FOR_LIST) {
|
|
int htok = 0;
|
|
|
|
if (!(args = ecgetlist(state, *state->pc++, EC_DUPTOK, &htok))) {
|
|
state->pc = end;
|
|
return 0;
|
|
}
|
|
if (htok)
|
|
execsubst(args);
|
|
} else {
|
|
char **x;
|
|
|
|
args = newlinklist();
|
|
for (x = pparams; *x; x++)
|
|
addlinknode(args, dupstring(*x));
|
|
}
|
|
}
|
|
lastval = 0;
|
|
loops++;
|
|
pushheap();
|
|
cmdpush(CS_FOR);
|
|
loop = state->pc;
|
|
while (!last) {
|
|
if (iscond) {
|
|
if (ctok) {
|
|
str = dupstring(cond);
|
|
singsub(&str);
|
|
} else
|
|
str = cond;
|
|
if (!errflag) {
|
|
while (iblank(*str))
|
|
str++;
|
|
if (*str) {
|
|
if (isset(XTRACE)) {
|
|
printprompt4();
|
|
fprintf(xtrerr, "%s\n", str);
|
|
fflush(xtrerr);
|
|
}
|
|
val = mathevali(str);
|
|
} else
|
|
val = 1;
|
|
}
|
|
if (errflag) {
|
|
if (breaks)
|
|
breaks--;
|
|
lastval = 1;
|
|
break;
|
|
}
|
|
if (!val)
|
|
break;
|
|
} else {
|
|
LinkNode node;
|
|
int count = 0;
|
|
for (node = firstnode(vars); node; incnode(node))
|
|
{
|
|
name = (char *)getdata(node);
|
|
if (!args || !(str = (char *) ugetnode(args)))
|
|
{
|
|
if (count) {
|
|
str = "";
|
|
last = 1;
|
|
} else
|
|
break;
|
|
}
|
|
if (isset(XTRACE)) {
|
|
printprompt4();
|
|
fprintf(xtrerr, "%s=%s\n", name, str);
|
|
fflush(xtrerr);
|
|
}
|
|
setsparam(name, ztrdup(str));
|
|
count++;
|
|
}
|
|
if (!count)
|
|
break;
|
|
}
|
|
state->pc = loop;
|
|
execlist(state, 1, do_exec && args && empty(args));
|
|
if (breaks) {
|
|
breaks--;
|
|
if (breaks || !contflag)
|
|
break;
|
|
contflag = 0;
|
|
}
|
|
if (retflag)
|
|
break;
|
|
if (iscond && !errflag) {
|
|
if (atok) {
|
|
str = dupstring(advance);
|
|
singsub(&str);
|
|
} else
|
|
str = advance;
|
|
if (isset(XTRACE)) {
|
|
printprompt4();
|
|
fprintf(xtrerr, "%s\n", str);
|
|
fflush(xtrerr);
|
|
}
|
|
if (!errflag)
|
|
matheval(str);
|
|
}
|
|
if (errflag) {
|
|
if (breaks)
|
|
breaks--;
|
|
lastval = 1;
|
|
break;
|
|
}
|
|
freeheap();
|
|
}
|
|
popheap();
|
|
cmdpop();
|
|
loops--;
|
|
state->pc = end;
|
|
return lastval;
|
|
}
|
|
|
|
/**/
|
|
int
|
|
execselect(Estate state, UNUSED(int do_exec))
|
|
{
|
|
Wordcode end, loop;
|
|
wordcode code = state->pc[-1];
|
|
char *str, *s, *name;
|
|
LinkNode n;
|
|
int i, usezle;
|
|
FILE *inp;
|
|
size_t more;
|
|
LinkList args;
|
|
|
|
end = state->pc + WC_FOR_SKIP(code);
|
|
name = ecgetstr(state, EC_NODUP, NULL);
|
|
|
|
if (WC_SELECT_TYPE(code) == WC_SELECT_PPARAM) {
|
|
char **x;
|
|
|
|
args = newlinklist();
|
|
for (x = pparams; *x; x++)
|
|
addlinknode(args, dupstring(*x));
|
|
} else {
|
|
int htok = 0;
|
|
|
|
if (!(args = ecgetlist(state, *state->pc++, EC_DUPTOK, &htok))) {
|
|
state->pc = end;
|
|
return 0;
|
|
}
|
|
if (htok)
|
|
execsubst(args);
|
|
}
|
|
if (!args || empty(args)) {
|
|
state->pc = end;
|
|
return 1;
|
|
}
|
|
loops++;
|
|
lastval = 0;
|
|
pushheap();
|
|
cmdpush(CS_SELECT);
|
|
usezle = interact && SHTTY != -1 && isset(USEZLE);
|
|
inp = fdopen(dup(usezle ? SHTTY : 0), "r");
|
|
more = selectlist(args, 0);
|
|
loop = state->pc;
|
|
for (;;) {
|
|
for (;;) {
|
|
if (empty(bufstack)) {
|
|
if (usezle) {
|
|
int oef = errflag;
|
|
|
|
isfirstln = 1;
|
|
str = (char *)zlereadptr(&prompt3, NULL, 0, ZLCON_SELECT);
|
|
if (errflag)
|
|
str = NULL;
|
|
errflag = oef;
|
|
} else {
|
|
str = promptexpand(prompt3, 0, NULL, NULL);
|
|
zputs(str, stderr);
|
|
free(str);
|
|
fflush(stderr);
|
|
str = fgets(zalloc(256), 256, inp);
|
|
}
|
|
} else
|
|
str = (char *)getlinknode(bufstack);
|
|
if (!str || errflag) {
|
|
if (breaks)
|
|
breaks--;
|
|
fprintf(stderr, "\n");
|
|
fflush(stderr);
|
|
goto done;
|
|
}
|
|
if ((s = strchr(str, '\n')))
|
|
*s = '\0';
|
|
if (*str)
|
|
break;
|
|
more = selectlist(args, more);
|
|
}
|
|
setsparam("REPLY", ztrdup(str));
|
|
i = atoi(str);
|
|
if (!i)
|
|
str = "";
|
|
else {
|
|
for (i--, n = firstnode(args); n && i; incnode(n), i--);
|
|
if (n)
|
|
str = (char *) getdata(n);
|
|
else
|
|
str = "";
|
|
}
|
|
setsparam(name, ztrdup(str));
|
|
state->pc = loop;
|
|
execlist(state, 1, 0);
|
|
freeheap();
|
|
if (breaks) {
|
|
breaks--;
|
|
if (breaks || !contflag)
|
|
break;
|
|
contflag = 0;
|
|
}
|
|
if (retflag || errflag)
|
|
break;
|
|
}
|
|
done:
|
|
cmdpop();
|
|
popheap();
|
|
fclose(inp);
|
|
loops--;
|
|
state->pc = end;
|
|
return lastval;
|
|
}
|
|
|
|
/* And this is used to print select lists. */
|
|
|
|
/**/
|
|
size_t
|
|
selectlist(LinkList l, size_t start)
|
|
{
|
|
size_t longest = 1, fct, fw = 0, colsz, t0, t1, ct;
|
|
LinkNode n;
|
|
char **arr, **ap;
|
|
|
|
trashzleptr();
|
|
ct = countlinknodes(l);
|
|
ap = arr = (char **) zhalloc((countlinknodes(l) + 1) * sizeof(char **));
|
|
|
|
for (n = (LinkNode) firstnode(l); n; incnode(n))
|
|
*ap++ = (char *)getdata(n);
|
|
*ap = NULL;
|
|
for (ap = arr; *ap; ap++)
|
|
if (strlen(*ap) > longest)
|
|
longest = strlen(*ap);
|
|
t0 = ct;
|
|
longest++;
|
|
while (t0)
|
|
t0 /= 10, longest++;
|
|
/* to compensate for added ')' */
|
|
fct = (columns - 1) / (longest + 3);
|
|
if (fct == 0)
|
|
fct = 1;
|
|
else
|
|
fw = (columns - 1) / fct;
|
|
colsz = (ct + fct - 1) / fct;
|
|
for (t1 = start; t1 != colsz && t1 - start < lines - 2; t1++) {
|
|
ap = arr + t1;
|
|
do {
|
|
size_t t2 = strlen(*ap) + 2;
|
|
int t3;
|
|
|
|
fprintf(stderr, "%d) %s", t3 = ap - arr + 1, *ap);
|
|
while (t3)
|
|
t2++, t3 /= 10;
|
|
for (; t2 < fw; t2++)
|
|
fputc(' ', stderr);
|
|
for (t0 = colsz; t0 && *ap; t0--, ap++);
|
|
}
|
|
while (*ap);
|
|
fputc('\n', stderr);
|
|
}
|
|
|
|
/* Below is a simple attempt at doing it the Korn Way..
|
|
ap = arr;
|
|
t0 = 0;
|
|
do {
|
|
t0++;
|
|
fprintf(stderr,"%d) %s\n",t0,*ap);
|
|
ap++;
|
|
}
|
|
while (*ap);*/
|
|
fflush(stderr);
|
|
|
|
return t1 < colsz ? t1 : 0;
|
|
}
|
|
|
|
/**/
|
|
int
|
|
execwhile(Estate state, UNUSED(int do_exec))
|
|
{
|
|
Wordcode end, loop;
|
|
wordcode code = state->pc[-1];
|
|
int olderrexit, oldval, isuntil = (WC_WHILE_TYPE(code) == WC_WHILE_UNTIL);
|
|
|
|
end = state->pc + WC_WHILE_SKIP(code);
|
|
olderrexit = noerrexit;
|
|
oldval = 0;
|
|
pushheap();
|
|
cmdpush(isuntil ? CS_UNTIL : CS_WHILE);
|
|
loops++;
|
|
loop = state->pc;
|
|
|
|
if (loop[0] == WC_END && loop[1] == WC_END) {
|
|
|
|
/* This is an empty loop. Make sure the signal handler sets the
|
|
* flags and then just wait for someone hitting ^C. */
|
|
|
|
int old_simple_pline = simple_pline;
|
|
|
|
simple_pline = 1;
|
|
|
|
while (!breaks)
|
|
;
|
|
breaks--;
|
|
|
|
simple_pline = old_simple_pline;
|
|
} else
|
|
for (;;) {
|
|
state->pc = loop;
|
|
noerrexit = 1;
|
|
execlist(state, 1, 0);
|
|
noerrexit = olderrexit;
|
|
if (!((lastval == 0) ^ isuntil)) {
|
|
if (breaks)
|
|
breaks--;
|
|
lastval = oldval;
|
|
break;
|
|
}
|
|
if (retflag) {
|
|
lastval = oldval;
|
|
break;
|
|
}
|
|
execlist(state, 1, 0);
|
|
if (breaks) {
|
|
breaks--;
|
|
if (breaks || !contflag)
|
|
break;
|
|
contflag = 0;
|
|
}
|
|
if (errflag) {
|
|
lastval = 1;
|
|
break;
|
|
}
|
|
if (retflag)
|
|
break;
|
|
freeheap();
|
|
oldval = lastval;
|
|
}
|
|
cmdpop();
|
|
popheap();
|
|
loops--;
|
|
state->pc = end;
|
|
return lastval;
|
|
}
|
|
|
|
/**/
|
|
int
|
|
execrepeat(Estate state, UNUSED(int do_exec))
|
|
{
|
|
Wordcode end, loop;
|
|
wordcode code = state->pc[-1];
|
|
int count, htok = 0;
|
|
char *tmp;
|
|
|
|
end = state->pc + WC_REPEAT_SKIP(code);
|
|
|
|
lastval = 0;
|
|
tmp = ecgetstr(state, EC_DUPTOK, &htok);
|
|
if (htok)
|
|
singsub(&tmp);
|
|
count = atoi(tmp);
|
|
pushheap();
|
|
cmdpush(CS_REPEAT);
|
|
loops++;
|
|
loop = state->pc;
|
|
while (count-- > 0) {
|
|
state->pc = loop;
|
|
execlist(state, 1, 0);
|
|
freeheap();
|
|
if (breaks) {
|
|
breaks--;
|
|
if (breaks || !contflag)
|
|
break;
|
|
contflag = 0;
|
|
}
|
|
if (errflag) {
|
|
lastval = 1;
|
|
break;
|
|
}
|
|
if (retflag)
|
|
break;
|
|
}
|
|
cmdpop();
|
|
popheap();
|
|
loops--;
|
|
state->pc = end;
|
|
return lastval;
|
|
}
|
|
|
|
/**/
|
|
int
|
|
execif(Estate state, int do_exec)
|
|
{
|
|
Wordcode end, next;
|
|
wordcode code = state->pc[-1];
|
|
int olderrexit, s = 0, run = 0;
|
|
|
|
olderrexit = noerrexit;
|
|
end = state->pc + WC_IF_SKIP(code);
|
|
|
|
if (!noerrexit)
|
|
noerrexit = 1;
|
|
while (state->pc < end) {
|
|
code = *state->pc++;
|
|
if (wc_code(code) != WC_IF ||
|
|
(run = (WC_IF_TYPE(code) == WC_IF_ELSE))) {
|
|
if (run)
|
|
run = 2;
|
|
break;
|
|
}
|
|
next = state->pc + WC_IF_SKIP(code);
|
|
cmdpush(s ? CS_ELIF : CS_IF);
|
|
execlist(state, 1, 0);
|
|
cmdpop();
|
|
if (!lastval) {
|
|
run = 1;
|
|
break;
|
|
}
|
|
if (retflag)
|
|
break;
|
|
s = 1;
|
|
state->pc = next;
|
|
}
|
|
noerrexit = olderrexit;
|
|
|
|
if (run) {
|
|
cmdpush(run == 2 ? CS_ELSE : (s ? CS_ELIFTHEN : CS_IFTHEN));
|
|
execlist(state, 1, do_exec);
|
|
cmdpop();
|
|
} else
|
|
lastval = 0;
|
|
state->pc = end;
|
|
|
|
return lastval;
|
|
}
|
|
|
|
/**/
|
|
int
|
|
execcase(Estate state, int do_exec)
|
|
{
|
|
Wordcode end, next;
|
|
wordcode code = state->pc[-1];
|
|
char *word, *pat;
|
|
int npat, save;
|
|
Patprog *spprog, pprog;
|
|
|
|
end = state->pc + WC_CASE_SKIP(code);
|
|
|
|
word = ecgetstr(state, EC_DUP, NULL);
|
|
singsub(&word);
|
|
untokenize(word);
|
|
lastval = 0;
|
|
|
|
cmdpush(CS_CASE);
|
|
while (state->pc < end) {
|
|
code = *state->pc++;
|
|
if (wc_code(code) != WC_CASE)
|
|
break;
|
|
|
|
pat = NULL;
|
|
pprog = NULL;
|
|
save = 0;
|
|
npat = state->pc[1];
|
|
spprog = state->prog->pats + npat;
|
|
|
|
next = state->pc + WC_CASE_SKIP(code);
|
|
|
|
if (isset(XTRACE)) {
|
|
char *pat2, *opat;
|
|
|
|
pat = dupstring(opat = ecrawstr(state->prog, state->pc, NULL));
|
|
singsub(&pat);
|
|
save = (!(state->prog->flags & EF_HEAP) &&
|
|
!strcmp(pat, opat) && *spprog != dummy_patprog2);
|
|
|
|
pat2 = dupstring(pat);
|
|
untokenize(pat2);
|
|
printprompt4();
|
|
fprintf(xtrerr, "case %s (%s)\n", word, pat2);
|
|
fflush(xtrerr);
|
|
}
|
|
state->pc += 2;
|
|
|
|
if (*spprog != dummy_patprog1 && *spprog != dummy_patprog2)
|
|
pprog = *spprog;
|
|
|
|
if (!pprog) {
|
|
if (!pat) {
|
|
char *opat;
|
|
int htok = 0;
|
|
|
|
pat = dupstring(opat = ecrawstr(state->prog,
|
|
state->pc - 2, &htok));
|
|
if (htok)
|
|
singsub(&pat);
|
|
save = (!(state->prog->flags & EF_HEAP) &&
|
|
!strcmp(pat, opat) && *spprog != dummy_patprog2);
|
|
}
|
|
if (!(pprog = patcompile(pat, (save ? PAT_ZDUP : PAT_STATIC),
|
|
NULL)))
|
|
zerr("bad pattern: %s", pat, 0);
|
|
else if (save)
|
|
*spprog = pprog;
|
|
}
|
|
if (pprog && pattry(pprog, word)) {
|
|
execlist(state, 1, ((WC_CASE_TYPE(code) == WC_CASE_OR) &&
|
|
do_exec));
|
|
while (!retflag && wc_code(code) == WC_CASE &&
|
|
WC_CASE_TYPE(code) == WC_CASE_AND) {
|
|
state->pc = next;
|
|
code = *state->pc;
|
|
state->pc += 3;
|
|
next = state->pc + WC_CASE_SKIP(code) - 2;
|
|
execlist(state, 1, ((WC_CASE_TYPE(code) == WC_CASE_OR) &&
|
|
do_exec));
|
|
}
|
|
break;
|
|
} else
|
|
state->pc = next;
|
|
}
|
|
cmdpop();
|
|
|
|
state->pc = end;
|
|
|
|
return lastval;
|
|
}
|
|
|
|
/*
|
|
* Errflag from `try' block, may be reset in `always' block.
|
|
* Accessible from an integer parameter, so needs to be a zlong.
|
|
*/
|
|
|
|
/**/
|
|
zlong
|
|
try_errflag = -1;
|
|
|
|
/**/
|
|
int
|
|
exectry(Estate state, int do_exec)
|
|
{
|
|
Wordcode end, always;
|
|
int endval;
|
|
int save_retflag, save_breaks, save_loops, save_contflag;
|
|
zlong save_try_errflag;
|
|
|
|
end = state->pc + WC_TRY_SKIP(state->pc[-1]);
|
|
always = state->pc + 1 + WC_TRY_SKIP(*state->pc);
|
|
state->pc++;
|
|
pushheap();
|
|
cmdpush(CS_CURSH);
|
|
|
|
/* The :try clause */
|
|
execlist(state, 1, do_exec);
|
|
|
|
/* Don't record errflag here, may be reset. */
|
|
endval = lastval;
|
|
|
|
freeheap();
|
|
|
|
cmdpop();
|
|
cmdpush(CS_ALWAYS);
|
|
|
|
/* The always clause. */
|
|
save_try_errflag = try_errflag;
|
|
try_errflag = (zlong)errflag;
|
|
errflag = 0;
|
|
save_retflag = retflag;
|
|
retflag = 0;
|
|
save_breaks = breaks;
|
|
breaks = 0;
|
|
save_loops = loops;
|
|
loops = 0;
|
|
save_contflag = contflag;
|
|
contflag = 0;
|
|
|
|
state->pc = always;
|
|
execlist(state, 1, do_exec);
|
|
|
|
errflag = try_errflag ? 1 : 0;
|
|
try_errflag = save_try_errflag;
|
|
retflag = save_retflag;
|
|
breaks = save_breaks;
|
|
loops = save_loops;
|
|
contflag = save_contflag;
|
|
|
|
cmdpop();
|
|
popheap();
|
|
state->pc = end;
|
|
|
|
return endval;
|
|
}
|