mirror of
https://github.com/zsh-users/zsh
synced 2024-11-02 09:47:59 +00:00
1109 lines
23 KiB
C
1109 lines
23 KiB
C
/*
|
|
* text.c - textual representations of syntax trees
|
|
*
|
|
* 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 "text.pro"
|
|
|
|
/*
|
|
* If non-zero, expand syntactically significant leading tabs in text
|
|
* to this number of spaces.
|
|
*
|
|
* If negative, don't output leading whitespace at all.
|
|
*/
|
|
|
|
/**/
|
|
int text_expand_tabs;
|
|
|
|
/*
|
|
* Binary operators in conditions.
|
|
* There order is tied to the order of the definitions COND_STREQ
|
|
* et seq. in zsh.h.
|
|
*/
|
|
static const char *cond_binary_ops[] = {
|
|
"=", "==", "!=", "<", ">", "-nt", "-ot", "-ef", "-eq",
|
|
"-ne", "-lt", "-gt", "-le", "-ge", "=~", NULL
|
|
};
|
|
|
|
static char *tptr, *tbuf, *tlim, *tpending;
|
|
static int tsiz, tindent, tnewlins, tjob;
|
|
|
|
/**/
|
|
int
|
|
is_cond_binary_op(const char *str)
|
|
{
|
|
const char **op;
|
|
for (op = cond_binary_ops; *op; op++)
|
|
{
|
|
if (!strcmp(str, *op))
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
dec_tindent(void)
|
|
{
|
|
DPUTS(tindent == 0, "attempting to decrement tindent below zero");
|
|
if (tindent > 0)
|
|
tindent--;
|
|
}
|
|
|
|
/*
|
|
* Add a pair of pending strings and a newline.
|
|
* This is used for here documents. It will be output when
|
|
* we have a lexically significant newline.
|
|
*
|
|
* This isn't that common and a multiple use on the same line is *very*
|
|
* uncommon; we don't try to optimise it.
|
|
*
|
|
* This is not used for job text; there we bear the inaccuracy
|
|
* of turning this into a here-string.
|
|
*/
|
|
static void
|
|
taddpending(char *str1, char *str2)
|
|
{
|
|
int len = strlen(str1) + strlen(str2) + 1;
|
|
|
|
/*
|
|
* We don't strip newlines from here-documents converted
|
|
* to here-strings, so no munging is required except to
|
|
* add a newline after the here-document terminator.
|
|
* However, because the job text doesn't automatically
|
|
* have a newline right at the end, we handle that
|
|
* specially.
|
|
*/
|
|
if (tpending) {
|
|
int oldlen = strlen(tpending);
|
|
tpending = zrealloc(tpending, len + oldlen + 1);
|
|
sprintf(tpending + oldlen, "\n%s%s", str1, str2);
|
|
} else {
|
|
tpending = (char *)zalloc(len);
|
|
sprintf(tpending, "%s%s", str1, str2);
|
|
}
|
|
}
|
|
|
|
/* Output the pending string where appropriate */
|
|
|
|
static void
|
|
tdopending(void)
|
|
{
|
|
if (tpending) {
|
|
taddchr('\n');
|
|
taddstr(tpending);
|
|
zsfree(tpending);
|
|
tpending = NULL;
|
|
}
|
|
}
|
|
|
|
/* add a character to the text buffer */
|
|
|
|
/**/
|
|
static void
|
|
taddchr(int c)
|
|
{
|
|
*tptr++ = c;
|
|
if (tptr == tlim) {
|
|
if (!tbuf) {
|
|
tptr--;
|
|
return;
|
|
}
|
|
tbuf = zrealloc(tbuf, tsiz *= 2);
|
|
tlim = tbuf + tsiz;
|
|
tptr = tbuf + tsiz / 2;
|
|
}
|
|
}
|
|
|
|
/* add a string to the text buffer */
|
|
|
|
/**/
|
|
static void
|
|
taddstr(const char *s)
|
|
{
|
|
int sl = strlen(s);
|
|
char c;
|
|
|
|
while (tptr + sl >= tlim) {
|
|
int x = tptr - tbuf;
|
|
|
|
if (!tbuf)
|
|
return;
|
|
tbuf = zrealloc(tbuf, tsiz *= 2);
|
|
tlim = tbuf + tsiz;
|
|
tptr = tbuf + x;
|
|
}
|
|
if (tnewlins) {
|
|
memcpy(tptr, s, sl);
|
|
tptr += sl;
|
|
} else
|
|
while ((c = *s++))
|
|
*tptr++ = (c == '\n' ? ' ' : c);
|
|
}
|
|
|
|
/**/
|
|
static void
|
|
taddlist(Estate state, int num)
|
|
{
|
|
if (num) {
|
|
while (num--) {
|
|
taddstr(ecgetstr(state, EC_NODUP, NULL));
|
|
taddchr(' ');
|
|
}
|
|
tptr--;
|
|
}
|
|
}
|
|
|
|
/* add an assignment */
|
|
|
|
static void
|
|
taddassign(wordcode code, Estate state, int typeset)
|
|
{
|
|
/* name */
|
|
taddstr(ecgetstr(state, EC_NODUP, NULL));
|
|
/* value... maybe */
|
|
if (WC_ASSIGN_TYPE2(code) == WC_ASSIGN_INC) {
|
|
if (typeset) {
|
|
/* dummy assignment --- just var name */
|
|
(void)ecgetstr(state, EC_NODUP, NULL);
|
|
taddchr(' ');
|
|
return;
|
|
}
|
|
taddchr('+');
|
|
}
|
|
taddchr('=');
|
|
if (WC_ASSIGN_TYPE(code) == WC_ASSIGN_ARRAY) {
|
|
taddchr('(');
|
|
taddlist(state, WC_ASSIGN_NUM(code));
|
|
taddstr(") ");
|
|
} else {
|
|
taddstr(ecgetstr(state, EC_NODUP, NULL));
|
|
taddchr(' ');
|
|
}
|
|
}
|
|
|
|
/* add a number of assignments from typeset */
|
|
|
|
/**/
|
|
static void
|
|
taddassignlist(Estate state, wordcode count)
|
|
{
|
|
if (count)
|
|
taddchr(' ');
|
|
while (count--) {
|
|
wordcode code = *state->pc++;
|
|
taddassign(code, state, 1);
|
|
}
|
|
}
|
|
|
|
/* add a newline, or something equivalent, to the text buffer */
|
|
|
|
/**/
|
|
static void
|
|
taddnl(int no_semicolon)
|
|
{
|
|
int t0;
|
|
|
|
if (tnewlins) {
|
|
tdopending();
|
|
taddchr('\n');
|
|
for (t0 = 0; t0 != tindent; t0++) {
|
|
if (text_expand_tabs >= 0) {
|
|
if (text_expand_tabs) {
|
|
int t1;
|
|
for (t1 = 0; t1 < text_expand_tabs; t1++)
|
|
taddchr(' ');
|
|
} else
|
|
taddchr('\t');
|
|
}
|
|
}
|
|
} else if (no_semicolon) {
|
|
taddstr(" ");
|
|
} else {
|
|
taddstr("; ");
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Output a tab that may be expanded as part of a leading set.
|
|
* Note this is not part of the text framework; it's for
|
|
* code that needs to output its own tabs that are to be
|
|
* consistent with those from getpermtext().
|
|
*
|
|
* Note these tabs are only expected to be useful at the
|
|
* start of the line, so we make no attempt to count columns.
|
|
*/
|
|
|
|
/**/
|
|
void
|
|
zoutputtab(FILE *outf)
|
|
{
|
|
if (text_expand_tabs < 0)
|
|
return;
|
|
if (text_expand_tabs) {
|
|
int i;
|
|
for (i = 0; i < text_expand_tabs; i++)
|
|
fputc(' ', outf);
|
|
} else
|
|
fputc('\t', outf);
|
|
}
|
|
|
|
/* get a permanent textual representation of n */
|
|
|
|
/**/
|
|
mod_export char *
|
|
getpermtext(Eprog prog, Wordcode c, int start_indent)
|
|
{
|
|
struct estate s;
|
|
|
|
queue_signals();
|
|
|
|
if (!c)
|
|
c = prog->prog;
|
|
|
|
useeprog(prog); /* mark as used */
|
|
|
|
s.prog = prog;
|
|
s.pc = c;
|
|
s.strs = prog->strs;
|
|
|
|
tindent = start_indent;
|
|
tnewlins = 1;
|
|
tbuf = (char *)zalloc(tsiz = 32);
|
|
tptr = tbuf;
|
|
tlim = tbuf + tsiz;
|
|
tjob = 0;
|
|
if (prog->len)
|
|
gettext2(&s);
|
|
*tptr = '\0';
|
|
freeeprog(prog); /* mark as unused */
|
|
untokenize(tbuf);
|
|
|
|
unqueue_signals();
|
|
|
|
return tbuf;
|
|
}
|
|
|
|
/* get a representation of n in a job text buffer */
|
|
|
|
/**/
|
|
char *
|
|
getjobtext(Eprog prog, Wordcode c)
|
|
{
|
|
static char jbuf[JOBTEXTSIZE];
|
|
|
|
struct estate s;
|
|
|
|
queue_signals();
|
|
|
|
if (!c)
|
|
c = prog->prog;
|
|
|
|
useeprog(prog); /* mark as used */
|
|
s.prog = prog;
|
|
s.pc = c;
|
|
s.strs = prog->strs;
|
|
|
|
tindent = 0;
|
|
tnewlins = 0;
|
|
tbuf = NULL;
|
|
tptr = jbuf;
|
|
tlim = tptr + JOBTEXTSIZE - 1;
|
|
tjob = 1;
|
|
gettext2(&s);
|
|
*tptr = '\0';
|
|
freeeprog(prog); /* mark as unused */
|
|
untokenize(jbuf);
|
|
|
|
unqueue_signals();
|
|
|
|
return jbuf;
|
|
}
|
|
|
|
/*
|
|
* gettext2() shows one way to walk through the word code without
|
|
* recursion. We start by reading a word code and executing the
|
|
* action for it. Some codes have sub-structures (like, e.g. WC_FOR)
|
|
* and require something to be done after the sub-structure has been
|
|
* handled. For these codes a tstack structure which describes what
|
|
* has to be done is pushed onto a stack. Codes without sub-structures
|
|
* arrange for the next structure being taken from the stack so that
|
|
* the action for it is executed instead of the one for the next
|
|
* word code. If the stack is empty at this point, we have handled
|
|
* the whole structure we were called for.
|
|
*/
|
|
|
|
typedef struct tstack *Tstack;
|
|
|
|
struct tstack {
|
|
Tstack prev;
|
|
wordcode code;
|
|
int pop;
|
|
union {
|
|
struct {
|
|
LinkList list;
|
|
} _redir;
|
|
struct {
|
|
char *strs;
|
|
Wordcode end;
|
|
int nargs;
|
|
} _funcdef;
|
|
struct {
|
|
Wordcode end;
|
|
} _case;
|
|
struct {
|
|
int cond;
|
|
Wordcode end;
|
|
} _if;
|
|
struct {
|
|
int par;
|
|
} _cond;
|
|
struct {
|
|
Wordcode end;
|
|
} _subsh;
|
|
} u;
|
|
};
|
|
|
|
static Tstack tstack, tfree;
|
|
|
|
static Tstack
|
|
tpush(wordcode code, int pop)
|
|
{
|
|
Tstack s;
|
|
|
|
if ((s = tfree))
|
|
tfree = s->prev;
|
|
else
|
|
s = (Tstack) zalloc(sizeof(*s));
|
|
|
|
s->prev = tstack;
|
|
tstack = s;
|
|
s->code = code;
|
|
s->pop = pop;
|
|
|
|
return s;
|
|
}
|
|
|
|
/**/
|
|
static void
|
|
gettext2(Estate state)
|
|
{
|
|
Tstack s, n;
|
|
int stack = 0;
|
|
wordcode code;
|
|
|
|
while (1) {
|
|
if (stack) {
|
|
if (!(s = tstack))
|
|
break;
|
|
if (s->pop) {
|
|
tstack = s->prev;
|
|
s->prev = tfree;
|
|
tfree = s;
|
|
}
|
|
code = s->code;
|
|
stack = 0;
|
|
} else {
|
|
s = NULL;
|
|
code = *state->pc++;
|
|
}
|
|
switch (wc_code(code)) {
|
|
case WC_LIST:
|
|
if (!s) {
|
|
s = tpush(code, (WC_LIST_TYPE(code) & Z_END));
|
|
stack = 0;
|
|
} else {
|
|
if (WC_LIST_TYPE(code) & Z_ASYNC) {
|
|
taddstr(" &");
|
|
if (WC_LIST_TYPE(code) & Z_DISOWN)
|
|
taddstr("|");
|
|
}
|
|
if (!(stack = (WC_LIST_TYPE(code) & Z_END))) {
|
|
if (tnewlins)
|
|
taddnl(0);
|
|
else
|
|
taddstr((WC_LIST_TYPE(code) & Z_ASYNC) ? " " : "; ");
|
|
s->code = *state->pc++;
|
|
s->pop = (WC_LIST_TYPE(s->code) & Z_END);
|
|
}
|
|
}
|
|
if (!stack && (WC_LIST_TYPE(s->code) & Z_SIMPLE))
|
|
state->pc++;
|
|
break;
|
|
case WC_SUBLIST:
|
|
if (!s) {
|
|
if (!(WC_SUBLIST_FLAGS(code) & WC_SUBLIST_SIMPLE) &&
|
|
wc_code(*state->pc) != WC_PIPE)
|
|
stack = -1;
|
|
if (WC_SUBLIST_FLAGS(code) & WC_SUBLIST_NOT)
|
|
taddstr(stack ? "!" : "! ");
|
|
if (WC_SUBLIST_FLAGS(code) & WC_SUBLIST_COPROC)
|
|
taddstr(stack ? "coproc" : "coproc ");
|
|
s = tpush(code, (WC_SUBLIST_TYPE(code) == WC_SUBLIST_END));
|
|
} else {
|
|
if (!(stack = (WC_SUBLIST_TYPE(code) == WC_SUBLIST_END))) {
|
|
taddstr((WC_SUBLIST_TYPE(code) == WC_SUBLIST_OR) ?
|
|
" || " : " && ");
|
|
s->code = *state->pc++;
|
|
s->pop = (WC_SUBLIST_TYPE(s->code) == WC_SUBLIST_END);
|
|
if (WC_SUBLIST_FLAGS(s->code) & WC_SUBLIST_NOT) {
|
|
if (WC_SUBLIST_SKIP(s->code) == 0)
|
|
stack = 1;
|
|
taddstr((stack || (!(WC_SUBLIST_FLAGS(s->code) &
|
|
WC_SUBLIST_SIMPLE) && wc_code(*state->pc) !=
|
|
WC_PIPE)) ? "!" : "! ");
|
|
}
|
|
if (WC_SUBLIST_FLAGS(s->code) & WC_SUBLIST_COPROC)
|
|
taddstr("coproc ");
|
|
}
|
|
}
|
|
if (stack < 1 && (WC_SUBLIST_FLAGS(s->code) & WC_SUBLIST_SIMPLE))
|
|
state->pc++;
|
|
break;
|
|
case WC_PIPE:
|
|
if (!s) {
|
|
tpush(code, (WC_PIPE_TYPE(code) == WC_PIPE_END));
|
|
if (WC_PIPE_TYPE(code) == WC_PIPE_MID)
|
|
state->pc++;
|
|
} else {
|
|
if (!(stack = (WC_PIPE_TYPE(code) == WC_PIPE_END))) {
|
|
taddstr(" | ");
|
|
s->code = *state->pc++;
|
|
if (!(s->pop = (WC_PIPE_TYPE(s->code) == WC_PIPE_END)))
|
|
state->pc++;
|
|
}
|
|
}
|
|
break;
|
|
case WC_REDIR:
|
|
if (!s) {
|
|
state->pc--;
|
|
n = tpush(code, 1);
|
|
n->u._redir.list = ecgetredirs(state);
|
|
} else {
|
|
getredirs(s->u._redir.list);
|
|
stack = 1;
|
|
}
|
|
break;
|
|
case WC_ASSIGN:
|
|
taddassign(code, state, 0);
|
|
break;
|
|
case WC_SIMPLE:
|
|
taddlist(state, WC_SIMPLE_ARGC(code));
|
|
stack = 1;
|
|
break;
|
|
case WC_TYPESET:
|
|
taddlist(state, WC_TYPESET_ARGC(code));
|
|
taddassignlist(state, *state->pc++);
|
|
stack = 1;
|
|
break;
|
|
case WC_SUBSH:
|
|
if (!s) {
|
|
taddstr("(");
|
|
tindent++;
|
|
taddnl(1);
|
|
n = tpush(code, 1);
|
|
n->u._subsh.end = state->pc + WC_SUBSH_SKIP(code);
|
|
/* skip word only use for try/always */
|
|
state->pc++;
|
|
} else {
|
|
state->pc = s->u._subsh.end;
|
|
dec_tindent();
|
|
/* semicolon is optional here but more standard */
|
|
taddnl(0);
|
|
taddstr(")");
|
|
stack = 1;
|
|
}
|
|
break;
|
|
case WC_CURSH:
|
|
if (!s) {
|
|
taddstr("{");
|
|
tindent++;
|
|
taddnl(1);
|
|
n = tpush(code, 1);
|
|
n->u._subsh.end = state->pc + WC_CURSH_SKIP(code);
|
|
/* skip word only use for try/always */
|
|
state->pc++;
|
|
} else {
|
|
state->pc = s->u._subsh.end;
|
|
dec_tindent();
|
|
/* semicolon is optional here but more standard */
|
|
taddnl(0);
|
|
taddstr("}");
|
|
stack = 1;
|
|
}
|
|
break;
|
|
case WC_TIMED:
|
|
if (!s) {
|
|
taddstr("time");
|
|
if (WC_TIMED_TYPE(code) == WC_TIMED_PIPE) {
|
|
taddchr(' ');
|
|
tindent++;
|
|
tpush(code, 1);
|
|
} else
|
|
stack = 1;
|
|
} else {
|
|
dec_tindent();
|
|
stack = 1;
|
|
}
|
|
break;
|
|
case WC_FUNCDEF:
|
|
if (!s) {
|
|
Wordcode p = state->pc;
|
|
Wordcode end = p + WC_FUNCDEF_SKIP(code);
|
|
int nargs = *state->pc++;
|
|
|
|
taddlist(state, nargs);
|
|
if (nargs)
|
|
taddstr(" ");
|
|
if (tjob) {
|
|
taddstr("() { ... }");
|
|
state->pc = end;
|
|
if (!nargs) {
|
|
/*
|
|
* Unnamed function.
|
|
* We're not going to pull any arguments off
|
|
* later, so skip them now...
|
|
*/
|
|
state->pc += *end;
|
|
}
|
|
stack = 1;
|
|
} else {
|
|
taddstr("() {");
|
|
tindent++;
|
|
taddnl(1);
|
|
n = tpush(code, 1);
|
|
n->u._funcdef.strs = state->strs;
|
|
n->u._funcdef.end = end;
|
|
n->u._funcdef.nargs = nargs;
|
|
state->strs += *state->pc;
|
|
state->pc += 3;
|
|
}
|
|
} else {
|
|
state->strs = s->u._funcdef.strs;
|
|
state->pc = s->u._funcdef.end;
|
|
dec_tindent();
|
|
taddnl(0);
|
|
taddstr("}");
|
|
if (s->u._funcdef.nargs == 0) {
|
|
/* Unnamed function with post-arguments */
|
|
int nargs;
|
|
s->u._funcdef.end += *state->pc++;
|
|
nargs = *state->pc++;
|
|
if (nargs) {
|
|
taddstr(" ");
|
|
taddlist(state, nargs);
|
|
}
|
|
state->pc = s->u._funcdef.end;
|
|
}
|
|
stack = 1;
|
|
}
|
|
break;
|
|
case WC_FOR:
|
|
if (!s) {
|
|
taddstr("for ");
|
|
if (WC_FOR_TYPE(code) == WC_FOR_COND) {
|
|
taddstr("((");
|
|
taddstr(ecgetstr(state, EC_NODUP, NULL));
|
|
taddstr("; ");
|
|
taddstr(ecgetstr(state, EC_NODUP, NULL));
|
|
taddstr("; ");
|
|
taddstr(ecgetstr(state, EC_NODUP, NULL));
|
|
taddstr(")) do");
|
|
} else {
|
|
taddlist(state, *state->pc++);
|
|
if (WC_FOR_TYPE(code) == WC_FOR_LIST) {
|
|
taddstr(" in ");
|
|
taddlist(state, *state->pc++);
|
|
}
|
|
taddnl(0);
|
|
taddstr("do");
|
|
}
|
|
tindent++;
|
|
taddnl(0);
|
|
tpush(code, 1);
|
|
} else {
|
|
dec_tindent();
|
|
taddnl(0);
|
|
taddstr("done");
|
|
stack = 1;
|
|
}
|
|
break;
|
|
case WC_SELECT:
|
|
if (!s) {
|
|
taddstr("select ");
|
|
taddstr(ecgetstr(state, EC_NODUP, NULL));
|
|
if (WC_SELECT_TYPE(code) == WC_SELECT_LIST) {
|
|
taddstr(" in ");
|
|
taddlist(state, *state->pc++);
|
|
}
|
|
taddnl(0);
|
|
taddstr("do");
|
|
taddnl(0);
|
|
tindent++;
|
|
tpush(code, 1);
|
|
} else {
|
|
dec_tindent();
|
|
taddnl(0);
|
|
taddstr("done");
|
|
stack = 1;
|
|
}
|
|
break;
|
|
case WC_WHILE:
|
|
if (!s) {
|
|
taddstr(WC_WHILE_TYPE(code) == WC_WHILE_UNTIL ?
|
|
"until " : "while ");
|
|
tindent++;
|
|
tpush(code, 0);
|
|
} else if (!s->pop) {
|
|
dec_tindent();
|
|
taddnl(0);
|
|
taddstr("do");
|
|
tindent++;
|
|
taddnl(0);
|
|
s->pop = 1;
|
|
} else {
|
|
dec_tindent();
|
|
taddnl(0);
|
|
taddstr("done");
|
|
stack = 1;
|
|
}
|
|
break;
|
|
case WC_REPEAT:
|
|
if (!s) {
|
|
taddstr("repeat ");
|
|
taddstr(ecgetstr(state, EC_NODUP, NULL));
|
|
taddnl(0);
|
|
taddstr("do");
|
|
tindent++;
|
|
taddnl(0);
|
|
tpush(code, 1);
|
|
} else {
|
|
dec_tindent();
|
|
taddnl(0);
|
|
taddstr("done");
|
|
stack = 1;
|
|
}
|
|
break;
|
|
case WC_CASE:
|
|
if (!s) {
|
|
Wordcode end = state->pc + WC_CASE_SKIP(code);
|
|
wordcode ialts;
|
|
|
|
taddstr("case ");
|
|
taddstr(ecgetstr(state, EC_NODUP, NULL));
|
|
taddstr(" in");
|
|
|
|
if (state->pc >= end) {
|
|
if (tnewlins)
|
|
taddnl(0);
|
|
else
|
|
taddchr(' ');
|
|
taddstr("esac");
|
|
stack = 1;
|
|
} else {
|
|
Wordcode prev_pc;
|
|
tindent++;
|
|
if (tnewlins)
|
|
taddnl(0);
|
|
else
|
|
taddchr(' ');
|
|
taddstr("(");
|
|
code = *state->pc++;
|
|
prev_pc = state->pc++;
|
|
ialts = *prev_pc;
|
|
while (ialts--) {
|
|
taddstr(ecgetstr(state, EC_NODUP, NULL));
|
|
state->pc++;
|
|
if (ialts)
|
|
taddstr(" | ");
|
|
}
|
|
taddstr(") ");
|
|
tindent++;
|
|
n = tpush(code, 0);
|
|
n->u._case.end = end;
|
|
n->pop = (prev_pc + WC_CASE_SKIP(code) >= end);
|
|
}
|
|
} else if (state->pc < s->u._case.end) {
|
|
Wordcode prev_pc;
|
|
wordcode ialts;
|
|
dec_tindent();
|
|
switch (WC_CASE_TYPE(code)) {
|
|
case WC_CASE_OR:
|
|
taddstr(" ;;");
|
|
break;
|
|
|
|
case WC_CASE_AND:
|
|
taddstr(" ;&");
|
|
break;
|
|
|
|
default:
|
|
taddstr(" ;|");
|
|
break;
|
|
}
|
|
if (tnewlins)
|
|
taddnl(0);
|
|
else
|
|
taddchr(' ');
|
|
taddstr("(");
|
|
code = *state->pc++;
|
|
prev_pc = state->pc++;
|
|
ialts = *prev_pc;
|
|
while (ialts--) {
|
|
taddstr(ecgetstr(state, EC_NODUP, NULL));
|
|
state->pc++;
|
|
if (ialts)
|
|
taddstr(" | ");
|
|
}
|
|
taddstr(") ");
|
|
tindent++;
|
|
s->code = code;
|
|
s->pop = (prev_pc + WC_CASE_SKIP(code) >=
|
|
s->u._case.end);
|
|
} else {
|
|
dec_tindent();
|
|
switch (WC_CASE_TYPE(code)) {
|
|
case WC_CASE_OR:
|
|
taddstr(" ;;");
|
|
break;
|
|
|
|
case WC_CASE_AND:
|
|
taddstr(" ;&");
|
|
break;
|
|
|
|
default:
|
|
taddstr(" ;|");
|
|
break;
|
|
}
|
|
dec_tindent();
|
|
if (tnewlins)
|
|
taddnl(0);
|
|
else
|
|
taddchr(' ');
|
|
taddstr("esac");
|
|
stack = 1;
|
|
}
|
|
break;
|
|
case WC_IF:
|
|
if (!s) {
|
|
Wordcode end = state->pc + WC_IF_SKIP(code);
|
|
|
|
taddstr("if ");
|
|
tindent++;
|
|
state->pc++;
|
|
|
|
n = tpush(code, 0);
|
|
n->u._if.end = end;
|
|
n->u._if.cond = 1;
|
|
} else if (s->pop) {
|
|
stack = 1;
|
|
} else if (s->u._if.cond) {
|
|
dec_tindent();
|
|
taddnl(0);
|
|
taddstr("then");
|
|
tindent++;
|
|
taddnl(0);
|
|
s->u._if.cond = 0;
|
|
} else if (state->pc < s->u._if.end) {
|
|
dec_tindent();
|
|
taddnl(0);
|
|
code = *state->pc++;
|
|
if (WC_IF_TYPE(code) == WC_IF_ELIF) {
|
|
taddstr("elif ");
|
|
tindent++;
|
|
s->u._if.cond = 1;
|
|
} else {
|
|
taddstr("else");
|
|
tindent++;
|
|
taddnl(0);
|
|
}
|
|
} else {
|
|
s->pop = 1;
|
|
dec_tindent();
|
|
taddnl(0);
|
|
taddstr("fi");
|
|
stack = 1;
|
|
}
|
|
break;
|
|
case WC_COND:
|
|
{
|
|
int ctype;
|
|
|
|
if (!s) {
|
|
taddstr("[[ ");
|
|
n = tpush(code, 1);
|
|
n->u._cond.par = 2;
|
|
} else if (s->u._cond.par == 2) {
|
|
taddstr(" ]]");
|
|
stack = 1;
|
|
break;
|
|
} else if (s->u._cond.par == 1) {
|
|
taddstr(" )");
|
|
stack = 1;
|
|
break;
|
|
} else if (WC_COND_TYPE(s->code) == COND_AND) {
|
|
taddstr(" && ");
|
|
code = *state->pc++;
|
|
if (WC_COND_TYPE(code) == COND_OR) {
|
|
taddstr("( ");
|
|
n = tpush(code, 1);
|
|
n->u._cond.par = 1;
|
|
}
|
|
} else if (WC_COND_TYPE(s->code) == COND_OR) {
|
|
taddstr(" || ");
|
|
code = *state->pc++;
|
|
if (WC_COND_TYPE(code) == COND_AND) {
|
|
taddstr("( ");
|
|
n = tpush(code, 1);
|
|
n->u._cond.par = 1;
|
|
}
|
|
}
|
|
while (!stack) {
|
|
switch ((ctype = WC_COND_TYPE(code))) {
|
|
case COND_NOT:
|
|
taddstr("! ");
|
|
code = *state->pc++;
|
|
if (WC_COND_TYPE(code) <= COND_OR) {
|
|
taddstr("( ");
|
|
n = tpush(code, 1);
|
|
n->u._cond.par = 1;
|
|
}
|
|
break;
|
|
case COND_AND:
|
|
n = tpush(code, 1);
|
|
n->u._cond.par = 0;
|
|
code = *state->pc++;
|
|
if (WC_COND_TYPE(code) == COND_OR) {
|
|
taddstr("( ");
|
|
n = tpush(code, 1);
|
|
n->u._cond.par = 1;
|
|
}
|
|
break;
|
|
case COND_OR:
|
|
n = tpush(code, 1);
|
|
n->u._cond.par = 0;
|
|
code = *state->pc++;
|
|
if (WC_COND_TYPE(code) == COND_AND) {
|
|
taddstr("( ");
|
|
n = tpush(code, 1);
|
|
n->u._cond.par = 1;
|
|
}
|
|
break;
|
|
case COND_MOD:
|
|
taddstr(ecgetstr(state, EC_NODUP, NULL));
|
|
taddchr(' ');
|
|
taddlist(state, WC_COND_SKIP(code));
|
|
stack = 1;
|
|
break;
|
|
case COND_MODI:
|
|
{
|
|
char *name = ecgetstr(state, EC_NODUP, NULL);
|
|
|
|
taddstr(ecgetstr(state, EC_NODUP, NULL));
|
|
taddchr(' ');
|
|
taddstr(name);
|
|
taddchr(' ');
|
|
taddstr(ecgetstr(state, EC_NODUP, NULL));
|
|
stack = 1;
|
|
}
|
|
break;
|
|
default:
|
|
if (ctype < COND_MOD) {
|
|
/* Binary test: `a = b' etc. */
|
|
taddstr(ecgetstr(state, EC_NODUP, NULL));
|
|
taddstr(" ");
|
|
taddstr(cond_binary_ops[ctype - COND_STREQ]);
|
|
taddstr(" ");
|
|
taddstr(ecgetstr(state, EC_NODUP, NULL));
|
|
if (ctype == COND_STREQ ||
|
|
ctype == COND_STRDEQ ||
|
|
ctype == COND_STRNEQ)
|
|
state->pc++;
|
|
} else {
|
|
/* Unary test: `-f foo' etc. */
|
|
char c2[4];
|
|
|
|
c2[0] = '-';
|
|
c2[1] = ctype;
|
|
c2[2] = ' ';
|
|
c2[3] = '\0';
|
|
taddstr(c2);
|
|
taddstr(ecgetstr(state, EC_NODUP, NULL));
|
|
}
|
|
stack = 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case WC_ARITH:
|
|
taddstr("((");
|
|
taddstr(ecgetstr(state, EC_NODUP, NULL));
|
|
taddstr("))");
|
|
stack = 1;
|
|
break;
|
|
case WC_AUTOFN:
|
|
taddstr("builtin autoload -X");
|
|
stack = 1;
|
|
break;
|
|
case WC_TRY:
|
|
if (!s) {
|
|
taddstr("{");
|
|
tindent++;
|
|
taddnl(0);
|
|
n = tpush(code, 0);
|
|
state->pc++;
|
|
/* this is the end of the try block alone */
|
|
n->u._subsh.end = state->pc + WC_CURSH_SKIP(state->pc[-1]);
|
|
} else if (!s->pop) {
|
|
state->pc = s->u._subsh.end;
|
|
dec_tindent();
|
|
taddnl(0);
|
|
taddstr("} always {");
|
|
tindent++;
|
|
taddnl(0);
|
|
s->pop = 1;
|
|
} else {
|
|
dec_tindent();
|
|
taddnl(0);
|
|
taddstr("}");
|
|
stack = 1;
|
|
}
|
|
break;
|
|
case WC_END:
|
|
stack = 1;
|
|
break;
|
|
default:
|
|
DPUTS(1, "unknown word code in gettext2()");
|
|
return;
|
|
}
|
|
}
|
|
tdopending();
|
|
}
|
|
|
|
/**/
|
|
void
|
|
getredirs(LinkList redirs)
|
|
{
|
|
LinkNode n;
|
|
static char *fstr[] =
|
|
{
|
|
">", ">|", ">>", ">>|", "&>", "&>|", "&>>", "&>>|", "<>", "<",
|
|
"<<", "<<-", "<<<", "<&", ">&", NULL /* >&- */, "<", ">"
|
|
};
|
|
|
|
queue_signals();
|
|
|
|
taddchr(' ');
|
|
for (n = firstnode(redirs); n; incnode(n)) {
|
|
Redir f = (Redir) getdata(n);
|
|
|
|
switch (f->type) {
|
|
case REDIR_WRITE:
|
|
case REDIR_WRITENOW:
|
|
case REDIR_APP:
|
|
case REDIR_APPNOW:
|
|
case REDIR_ERRWRITE:
|
|
case REDIR_ERRWRITENOW:
|
|
case REDIR_ERRAPP:
|
|
case REDIR_ERRAPPNOW:
|
|
case REDIR_READ:
|
|
case REDIR_READWRITE:
|
|
case REDIR_HERESTR:
|
|
case REDIR_MERGEIN:
|
|
case REDIR_MERGEOUT:
|
|
case REDIR_INPIPE:
|
|
case REDIR_OUTPIPE:
|
|
if (f->varid) {
|
|
taddchr('{');
|
|
taddstr(f->varid);
|
|
taddchr('}');
|
|
} else if (f->fd1 != (IS_READFD(f->type) ? 0 : 1))
|
|
taddchr('0' + f->fd1);
|
|
if (f->type == REDIR_HERESTR &&
|
|
(f->flags & REDIRF_FROM_HEREDOC)) {
|
|
if (tnewlins) {
|
|
/*
|
|
* Strings that came from here-documents are converted
|
|
* to here strings without quotation, so convert them
|
|
* back.
|
|
*/
|
|
taddstr(fstr[REDIR_HEREDOC]);
|
|
taddstr(f->here_terminator);
|
|
taddpending(f->name, f->munged_here_terminator);
|
|
} else {
|
|
int fnamelen, sav;
|
|
taddstr(fstr[REDIR_HERESTR]);
|
|
/*
|
|
* Just a quick and dirty representation.
|
|
* Remove a terminating newline, if any.
|
|
*/
|
|
fnamelen = strlen(f->name);
|
|
if (fnamelen > 0 && f->name[fnamelen-1] == '\n') {
|
|
sav = 1;
|
|
f->name[fnamelen-1] = '\0';
|
|
} else
|
|
sav = 0;
|
|
/*
|
|
* Strings that came from here-documents are converted
|
|
* to here strings without quotation, so add that
|
|
* now. If tokens are present we need to do double quoting.
|
|
*/
|
|
if (!has_token(f->name)) {
|
|
taddchr('\'');
|
|
taddstr(quotestring(f->name, QT_SINGLE));
|
|
taddchr('\'');
|
|
} else {
|
|
taddchr('"');
|
|
taddstr(quotestring(f->name, QT_DOUBLE));
|
|
taddchr('"');
|
|
}
|
|
if (sav)
|
|
f->name[fnamelen-1] = '\n';
|
|
}
|
|
} else {
|
|
taddstr(fstr[f->type]);
|
|
if (f->type != REDIR_MERGEIN && f->type != REDIR_MERGEOUT)
|
|
taddchr(' ');
|
|
taddstr(f->name);
|
|
}
|
|
taddchr(' ');
|
|
break;
|
|
#ifdef DEBUG
|
|
case REDIR_CLOSE:
|
|
DPUTS(1, "BUG: CLOSE in getredirs()");
|
|
taddchr(f->fd1 + '0');
|
|
taddstr(">&- ");
|
|
break;
|
|
default:
|
|
DPUTS(1, "BUG: unknown redirection in getredirs()");
|
|
#endif
|
|
}
|
|
}
|
|
tptr--;
|
|
|
|
unqueue_signals();
|
|
}
|