zsh/Src/loop.c

618 lines
13 KiB
C
Raw Normal View History

1999-04-15 18:05:38 +00:00
/*
* 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 */
/**/
2000-05-05 16:45:03 +00:00
mod_export int contflag;
1999-04-15 18:05:38 +00:00
/* # of break levels */
/**/
2000-04-01 20:49:47 +00:00
mod_export int breaks;
1999-04-15 18:05:38 +00:00
/**/
int
2000-04-01 20:49:47 +00:00
execfor(Estate state, int do_exec)
1999-04-15 18:05:38 +00:00
{
2000-04-01 20:49:47 +00:00
Wordcode end, loop;
wordcode code = state->pc[-1];
int iscond = (WC_FOR_TYPE(code) == WC_FOR_COND), ctok = 0, atok = 0;
2001-06-25 16:07:51 +00:00
int last = 0;
2000-04-01 20:49:47 +00:00
char *name, *str, *cond = NULL, *advance = NULL;
zlong val = 0;
2001-06-25 16:07:51 +00:00
LinkList vars = NULL, args = NULL;
2000-04-01 20:49:47 +00:00
end = state->pc + WC_FOR_SKIP(code);
1999-04-15 18:05:38 +00:00
2000-04-01 20:49:47 +00:00
if (iscond) {
2001-06-25 16:07:51 +00:00
str = dupstring(ecgetstr(state, EC_NODUP, NULL));
1999-04-15 18:05:38 +00:00
singsub(&str);
2000-04-01 20:49:47 +00:00
if (isset(XTRACE)) {
char *str2 = dupstring(str);
untokenize(str2);
printprompt4();
fprintf(xtrerr, "%s\n", str2);
fflush(xtrerr);
}
1999-04-15 18:05:38 +00:00
if (!errflag)
matheval(str);
2000-04-01 20:49:47 +00:00
if (errflag) {
state->pc = end;
1999-04-15 18:05:38 +00:00
return lastval = errflag;
2000-04-01 20:49:47 +00:00
}
cond = ecgetstr(state, EC_NODUP, &ctok);
advance = ecgetstr(state, EC_NODUP, &atok);
} else {
2001-06-25 16:07:51 +00:00
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;
1999-04-15 18:05:38 +00:00
2001-06-25 16:07:51 +00:00
args = newlinklist();
for (x = pparams; *x; x++)
addlinknode(args, dupstring(*x));
}
1999-04-15 18:05:38 +00:00
}
lastval = 0;
loops++;
pushheap();
2000-04-01 20:49:47 +00:00
cmdpush(CS_FOR);
loop = state->pc;
2001-06-25 16:07:51 +00:00
while (!last) {
2000-04-01 20:49:47 +00:00
if (iscond) {
if (ctok) {
str = dupstring(cond);
singsub(&str);
} else
str = cond;
1999-04-15 18:05:38 +00:00
if (!errflag) {
while (iblank(*str))
str++;
2000-04-01 20:49:47 +00:00
if (*str) {
if (isset(XTRACE)) {
printprompt4();
fprintf(xtrerr, "%s\n", str);
fflush(xtrerr);
}
val = mathevali(str);
} else
1999-04-15 18:05:38 +00:00
val = 1;
}
if (errflag) {
if (breaks)
breaks--;
lastval = 1;
break;
}
if (!val)
break;
} else {
2001-06-25 16:07:51 +00:00
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++;
2000-04-01 20:49:47 +00:00
}
2001-06-25 16:07:51 +00:00
if (!count)
break;
1999-04-15 18:05:38 +00:00
}
2000-04-01 20:49:47 +00:00
state->pc = loop;
execlist(state, 1, do_exec && args && empty(args));
1999-04-15 18:05:38 +00:00
if (breaks) {
breaks--;
if (breaks || !contflag)
break;
contflag = 0;
}
2000-04-01 20:49:47 +00:00
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);
}
1999-04-15 18:05:38 +00:00
if (!errflag)
matheval(str);
}
if (errflag) {
if (breaks)
breaks--;
lastval = 1;
break;
}
freeheap();
}
popheap();
2000-04-01 20:49:47 +00:00
cmdpop();
1999-04-15 18:05:38 +00:00
loops--;
2000-04-01 20:49:47 +00:00
state->pc = end;
1999-04-15 18:05:38 +00:00
return lastval;
}
/**/
int
2000-04-01 20:49:47 +00:00
execselect(Estate state, int do_exec)
1999-04-15 18:05:38 +00:00
{
2000-04-01 20:49:47 +00:00
Wordcode end, loop;
wordcode code = state->pc[-1];
char *str, *s, *name;
1999-04-15 18:05:38 +00:00
LinkNode n;
int i, usezle;
1999-04-15 18:05:38 +00:00
FILE *inp;
2000-04-01 20:49:47 +00:00
size_t more;
LinkList args;
end = state->pc + WC_FOR_SKIP(code);
name = ecgetstr(state, EC_NODUP, NULL);
1999-04-15 18:05:38 +00:00
2000-04-01 20:49:47 +00:00
if (WC_SELECT_TYPE(code) == WC_SELECT_PPARAM) {
1999-04-15 18:05:38 +00:00
char **x;
args = newlinklist();
for (x = pparams; *x; x++)
2000-04-01 20:49:47 +00:00
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);
1999-04-15 18:05:38 +00:00
}
2000-04-01 20:49:47 +00:00
if (!args || empty(args)) {
state->pc = end;
1999-04-15 18:05:38 +00:00
return 1;
2000-04-01 20:49:47 +00:00
}
1999-04-15 18:05:38 +00:00
loops++;
lastval = 0;
pushheap();
2000-04-01 20:49:47 +00:00
cmdpush(CS_SELECT);
usezle = interact && SHTTY != -1 && isset(USEZLE);
inp = fdopen(dup(usezle ? SHTTY : 0), "r");
more = selectlist(args, 0);
loop = state->pc;
1999-04-15 18:05:38 +00:00
for (;;) {
for (;;) {
if (empty(bufstack)) {
2000-04-01 20:49:47 +00:00
if (usezle) {
int oef = errflag;
1999-04-15 18:05:38 +00:00
isfirstln = 1;
str = (char *)zleread(prompt3, NULL, 0);
2000-04-01 20:49:47 +00:00
if (errflag)
str = NULL;
errflag = oef;
1999-04-15 18:05:38 +00:00
} 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;
2000-04-01 20:49:47 +00:00
more = selectlist(args, more);
1999-04-15 18:05:38 +00:00
}
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 = "";
}
2000-04-01 20:49:47 +00:00
setsparam(name, ztrdup(str));
state->pc = loop;
execlist(state, 1, 0);
1999-04-15 18:05:38 +00:00
freeheap();
if (breaks) {
breaks--;
if (breaks || !contflag)
break;
contflag = 0;
}
2000-04-01 20:49:47 +00:00
if (retflag || errflag)
1999-04-15 18:05:38 +00:00
break;
}
done:
2000-04-01 20:49:47 +00:00
cmdpop();
1999-04-15 18:05:38 +00:00
popheap();
fclose(inp);
loops--;
2000-04-01 20:49:47 +00:00
state->pc = end;
1999-04-15 18:05:38 +00:00
return lastval;
}
/* And this is used to print select lists. */
/**/
2000-04-01 20:49:47 +00:00
size_t
selectlist(LinkList l, size_t start)
1999-04-15 18:05:38 +00:00
{
size_t longest = 1, fct, fw = 0, colsz, t0, t1, ct;
LinkNode n;
char **arr, **ap;
trashzle();
ct = countlinknodes(l);
2000-04-01 20:49:47 +00:00
ap = arr = (char **) zhalloc((countlinknodes(l) + 1) * sizeof(char **));
1999-04-15 18:05:38 +00:00
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;
2000-04-01 20:49:47 +00:00
for (t1 = start; t1 != colsz && t1 - start < lines - 2; t1++) {
1999-04-15 18:05:38 +00:00
ap = arr + t1;
do {
int t2 = strlen(*ap) + 2, 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);
2000-04-01 20:49:47 +00:00
return t1 < colsz ? t1 : 0;
1999-04-15 18:05:38 +00:00
}
/**/
int
2000-04-01 20:49:47 +00:00
execwhile(Estate state, int do_exec)
1999-04-15 18:05:38 +00:00
{
2000-04-01 20:49:47 +00:00
Wordcode end, loop;
wordcode code = state->pc[-1];
int olderrexit, oldval, isuntil = (WC_WHILE_TYPE(code) == WC_WHILE_UNTIL);
1999-04-15 18:05:38 +00:00
2000-04-01 20:49:47 +00:00
end = state->pc + WC_WHILE_SKIP(code);
1999-04-15 18:05:38 +00:00
olderrexit = noerrexit;
oldval = 0;
pushheap();
2000-04-01 20:49:47 +00:00
cmdpush(isuntil ? CS_UNTIL : CS_WHILE);
1999-04-15 18:05:38 +00:00
loops++;
2000-04-01 20:49:47 +00:00
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;
}
2000-04-01 20:49:47 +00:00
cmdpop();
1999-04-15 18:05:38 +00:00
popheap();
loops--;
2000-04-01 20:49:47 +00:00
state->pc = end;
1999-04-15 18:05:38 +00:00
return lastval;
}
/**/
int
2000-04-01 20:49:47 +00:00
execrepeat(Estate state, int do_exec)
1999-04-15 18:05:38 +00:00
{
2000-04-01 20:49:47 +00:00
Wordcode end, loop;
wordcode code = state->pc[-1];
int count, htok = 0;
char *tmp;
end = state->pc + WC_REPEAT_SKIP(code);
1999-04-15 18:05:38 +00:00
lastval = 0;
2000-04-01 20:49:47 +00:00
tmp = ecgetstr(state, EC_DUPTOK, &htok);
if (htok)
singsub(&tmp);
count = atoi(tmp);
1999-04-15 18:05:38 +00:00
pushheap();
2000-04-01 20:49:47 +00:00
cmdpush(CS_REPEAT);
1999-04-15 18:05:38 +00:00
loops++;
2000-04-01 20:49:47 +00:00
loop = state->pc;
while (count-- > 0) {
state->pc = loop;
execlist(state, 1, 0);
1999-04-15 18:05:38 +00:00
freeheap();
if (breaks) {
breaks--;
if (breaks || !contflag)
break;
contflag = 0;
}
if (errflag) {
lastval = 1;
break;
}
2000-04-01 20:49:47 +00:00
if (retflag)
break;
1999-04-15 18:05:38 +00:00
}
2000-04-01 20:49:47 +00:00
cmdpop();
1999-04-15 18:05:38 +00:00
popheap();
loops--;
2000-04-01 20:49:47 +00:00
state->pc = end;
1999-04-15 18:05:38 +00:00
return lastval;
}
/**/
int
2000-04-01 20:49:47 +00:00
execif(Estate state, int do_exec)
1999-04-15 18:05:38 +00:00
{
2000-04-01 20:49:47 +00:00
Wordcode end, next;
wordcode code = state->pc[-1];
int olderrexit, s = 0, run = 0;
1999-04-15 18:05:38 +00:00
olderrexit = noerrexit;
2000-04-01 20:49:47 +00:00
end = state->pc + WC_IF_SKIP(code);
1999-04-15 18:05:38 +00:00
if (!noerrexit)
noerrexit = 1;
2000-04-01 20:49:47 +00:00
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;
1999-04-15 18:05:38 +00:00
break;
2000-04-01 20:49:47 +00:00
}
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;
1999-04-15 18:05:38 +00:00
}
noerrexit = olderrexit;
2000-04-01 20:49:47 +00:00
if (run) {
cmdpush(run == 2 ? CS_ELSE : (s ? CS_ELIFTHEN : CS_IFTHEN));
execlist(state, 1, do_exec);
cmdpop();
} else
1999-04-15 18:05:38 +00:00
lastval = 0;
2000-04-01 20:49:47 +00:00
state->pc = end;
1999-04-15 18:05:38 +00:00
return lastval;
}
/**/
int
2000-04-01 20:49:47 +00:00
execcase(Estate state, int do_exec)
1999-04-15 18:05:38 +00:00
{
2000-04-01 20:49:47 +00:00
Wordcode end, next;
wordcode code = state->pc[-1];
char *word, *pat;
int npat, save;
Patprog *spprog, pprog;
1999-04-15 18:05:38 +00:00
2000-04-01 20:49:47 +00:00
end = state->pc + WC_CASE_SKIP(code);
1999-04-15 18:05:38 +00:00
2000-04-01 20:49:47 +00:00
word = ecgetstr(state, EC_DUP, NULL);
1999-04-15 18:05:38 +00:00
singsub(&word);
untokenize(word);
lastval = 0;
2000-04-01 20:49:47 +00:00
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));
1999-04-15 18:05:38 +00:00
singsub(&pat);
2000-04-01 20:49:47 +00:00
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;
2000-04-01 20:49:47 +00:00
if (*spprog != dummy_patprog1 && *spprog != dummy_patprog2)
pprog = *spprog;
if (!pprog) {
if (!pat) {
char *opat;
int htok = 0;
pat = dupstring(opat = ecrawstr(state->prog,
2000-04-01 20:49:47 +00:00
state->pc - 2, &htok));
if (htok)
singsub(&pat);
save = (!(state->prog->flags & EF_HEAP) &&
!strcmp(pat, opat) && *spprog != dummy_patprog2);
1999-04-15 18:05:38 +00:00
}
2000-04-01 20:49:47 +00:00
if (!(pprog = patcompile(pat, (save ? PAT_ZDUP : PAT_STATIC),
NULL)))
zerr("bad pattern: %s", pat, 0);
else if (save)
*spprog = pprog;
1999-04-15 18:05:38 +00:00
}
2000-04-01 20:49:47 +00:00
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;
2000-06-17 17:05:53 +00:00
next = state->pc + WC_CASE_SKIP(code) - 2;
2000-04-01 20:49:47 +00:00
execlist(state, 1, ((WC_CASE_TYPE(code) == WC_CASE_OR) &&
do_exec));
}
break;
} else
state->pc = next;
1999-04-15 18:05:38 +00:00
}
2000-04-01 20:49:47 +00:00
cmdpop();
state->pc = end;
1999-04-15 18:05:38 +00:00
return lastval;
}