151d913115
main.c: In function 'main': main.c:208: warning: cast discards qualifiers from pointer target type main.c:329: warning: cast discards qualifiers from pointer target type no warnings at autoconf time left either; will take care of these two later (might revisit changes from this commit), maybe change declararion for the builtins to have their argv[] be const strings, and go through strict type and qualifier checking again. this'll further improve stability. XXX these changes might have introduced (more?) memory leaks, XXX someone who knows about these tools should verify with XXX automatic memory usage analysers (valgrind?) still passes testsuite
680 lines
13 KiB
C
680 lines
13 KiB
C
/* $OpenBSD: tree.c,v 1.18 2006/04/10 14:38:59 jaredy Exp $ */
|
|
|
|
#include "sh.h"
|
|
|
|
__RCSID("$MirOS: src/bin/mksh/tree.c,v 1.7 2006/11/12 14:58:16 tg Exp $");
|
|
|
|
#define INDENT 4
|
|
|
|
#define tputc(c, shf) shf_putchar(c, shf);
|
|
static void ptree(struct op *, int, struct shf *);
|
|
static void pioact(struct shf *, int, struct ioword *);
|
|
static void tputC(int, struct shf *);
|
|
static void tputS(char *, struct shf *);
|
|
static void vfptreef(struct shf *, int, const char *, va_list);
|
|
static struct ioword **iocopy(struct ioword **, Area *);
|
|
static void iofree(struct ioword **, Area *);
|
|
|
|
/*
|
|
* print a command tree
|
|
*/
|
|
|
|
static void
|
|
ptree(struct op *t, int indent, struct shf *shf)
|
|
{
|
|
char **w;
|
|
struct ioword **ioact;
|
|
struct op *t1;
|
|
|
|
Chain:
|
|
if (t == NULL)
|
|
return;
|
|
switch (t->type) {
|
|
case TCOM:
|
|
if (t->vars)
|
|
for (w = t->vars; *w != NULL; )
|
|
fptreef(shf, indent, "%S ", *w++);
|
|
else
|
|
fptreef(shf, indent, "#no-vars# ");
|
|
if (t->args)
|
|
for (w = t->args; *w != NULL; )
|
|
fptreef(shf, indent, "%S ", *w++);
|
|
else
|
|
fptreef(shf, indent, "#no-args# ");
|
|
break;
|
|
case TEXEC:
|
|
t = t->left;
|
|
goto Chain;
|
|
case TPAREN:
|
|
fptreef(shf, indent + 2, "( %T) ", t->left);
|
|
break;
|
|
case TPIPE:
|
|
fptreef(shf, indent, "%T| ", t->left);
|
|
t = t->right;
|
|
goto Chain;
|
|
case TLIST:
|
|
fptreef(shf, indent, "%T%;", t->left);
|
|
t = t->right;
|
|
goto Chain;
|
|
case TOR:
|
|
case TAND:
|
|
fptreef(shf, indent, "%T%s %T",
|
|
t->left, (t->type==TOR) ? "||" : "&&", t->right);
|
|
break;
|
|
case TBANG:
|
|
fptreef(shf, indent, "! ");
|
|
t = t->right;
|
|
goto Chain;
|
|
case TDBRACKET:
|
|
{
|
|
int i;
|
|
|
|
fptreef(shf, indent, "[[");
|
|
for (i = 0; t->args[i]; i++)
|
|
fptreef(shf, indent, " %S", t->args[i]);
|
|
fptreef(shf, indent, " ]] ");
|
|
break;
|
|
}
|
|
case TSELECT:
|
|
fptreef(shf, indent, "select %s ", t->str);
|
|
/* FALLTHRU */
|
|
case TFOR:
|
|
if (t->type == TFOR)
|
|
fptreef(shf, indent, "for %s ", t->str);
|
|
if (t->vars != NULL) {
|
|
fptreef(shf, indent, "in ");
|
|
for (w = t->vars; *w; )
|
|
fptreef(shf, indent, "%S ", *w++);
|
|
fptreef(shf, indent, "%;");
|
|
}
|
|
fptreef(shf, indent + INDENT, "do%N%T", t->left);
|
|
fptreef(shf, indent, "%;done ");
|
|
break;
|
|
case TCASE:
|
|
fptreef(shf, indent, "case %S in", t->str);
|
|
for (t1 = t->left; t1 != NULL; t1 = t1->right) {
|
|
fptreef(shf, indent, "%N(");
|
|
for (w = t1->vars; *w != NULL; w++)
|
|
fptreef(shf, indent, "%S%c", *w,
|
|
(w[1] != NULL) ? '|' : ')');
|
|
fptreef(shf, indent + INDENT, "%;%T%N;;", t1->left);
|
|
}
|
|
fptreef(shf, indent, "%Nesac ");
|
|
break;
|
|
case TIF:
|
|
case TELIF:
|
|
/* 3 == strlen("if ") */
|
|
fptreef(shf, indent + 3, "if %T", t->left);
|
|
for (;;) {
|
|
t = t->right;
|
|
if (t->left != NULL) {
|
|
fptreef(shf, indent, "%;");
|
|
fptreef(shf, indent + INDENT, "then%N%T",
|
|
t->left);
|
|
}
|
|
if (t->right == NULL || t->right->type != TELIF)
|
|
break;
|
|
t = t->right;
|
|
fptreef(shf, indent, "%;");
|
|
/* 5 == strlen("elif ") */
|
|
fptreef(shf, indent + 5, "elif %T", t->left);
|
|
}
|
|
if (t->right != NULL) {
|
|
fptreef(shf, indent, "%;");
|
|
fptreef(shf, indent + INDENT, "else%;%T", t->right);
|
|
}
|
|
fptreef(shf, indent, "%;fi ");
|
|
break;
|
|
case TWHILE:
|
|
case TUNTIL:
|
|
/* 6 == strlen("while"/"until") */
|
|
fptreef(shf, indent + 6, "%s %T",
|
|
(t->type==TWHILE) ? "while" : "until",
|
|
t->left);
|
|
fptreef(shf, indent, "%;do");
|
|
fptreef(shf, indent + INDENT, "%;%T", t->right);
|
|
fptreef(shf, indent, "%;done ");
|
|
break;
|
|
case TBRACE:
|
|
fptreef(shf, indent + INDENT, "{%;%T", t->left);
|
|
fptreef(shf, indent, "%;} ");
|
|
break;
|
|
case TCOPROC:
|
|
fptreef(shf, indent, "%T|& ", t->left);
|
|
break;
|
|
case TASYNC:
|
|
fptreef(shf, indent, "%T& ", t->left);
|
|
break;
|
|
case TFUNCT:
|
|
fptreef(shf, indent,
|
|
t->u.ksh_func ? "function %s %T" : "%s() %T",
|
|
t->str, t->left);
|
|
break;
|
|
case TTIME:
|
|
fptreef(shf, indent, "time %T", t->left);
|
|
break;
|
|
default:
|
|
fptreef(shf, indent, "<botch>");
|
|
break;
|
|
}
|
|
if ((ioact = t->ioact) != NULL) {
|
|
int need_nl = 0;
|
|
|
|
while (*ioact != NULL)
|
|
pioact(shf, indent, *ioact++);
|
|
/* Print here documents after everything else... */
|
|
for (ioact = t->ioact; *ioact != NULL; ) {
|
|
struct ioword *iop = *ioact++;
|
|
|
|
/* heredoc is 0 when tracing (set -x) */
|
|
if ((iop->flag & IOTYPE) == IOHERE && iop->heredoc) {
|
|
tputc('\n', shf);
|
|
shf_puts(iop->heredoc, shf);
|
|
fptreef(shf, indent, "%s",
|
|
evalstr(iop->delim, 0));
|
|
need_nl = 1;
|
|
}
|
|
}
|
|
/* Last delimiter must be followed by a newline (this often
|
|
* leads to an extra blank line, but its not worth worrying
|
|
* about)
|
|
*/
|
|
if (need_nl)
|
|
tputc('\n', shf);
|
|
}
|
|
}
|
|
|
|
static void
|
|
pioact(struct shf *shf, int indent, struct ioword *iop)
|
|
{
|
|
int flag = iop->flag;
|
|
int type = flag & IOTYPE;
|
|
int expected;
|
|
|
|
expected = (type == IOREAD || type == IORDWR || type == IOHERE) ? 0 :
|
|
(type == IOCAT || type == IOWRITE) ? 1 :
|
|
(type == IODUP && (iop->unit == !(flag & IORDUP))) ? iop->unit :
|
|
iop->unit + 1;
|
|
if (iop->unit != expected)
|
|
tputc('0' + iop->unit, shf);
|
|
|
|
switch (type) {
|
|
case IOREAD:
|
|
fptreef(shf, indent, "< ");
|
|
break;
|
|
case IOHERE:
|
|
if (flag&IOSKIP)
|
|
fptreef(shf, indent, "<<- ");
|
|
else
|
|
fptreef(shf, indent, "<< ");
|
|
break;
|
|
case IOCAT:
|
|
fptreef(shf, indent, ">> ");
|
|
break;
|
|
case IOWRITE:
|
|
if (flag&IOCLOB)
|
|
fptreef(shf, indent, ">| ");
|
|
else
|
|
fptreef(shf, indent, "> ");
|
|
break;
|
|
case IORDWR:
|
|
fptreef(shf, indent, "<> ");
|
|
break;
|
|
case IODUP:
|
|
if (flag & IORDUP)
|
|
fptreef(shf, indent, "<&");
|
|
else
|
|
fptreef(shf, indent, ">&");
|
|
break;
|
|
}
|
|
/* name/delim are 0 when printing syntax errors */
|
|
if (type == IOHERE) {
|
|
if (iop->delim)
|
|
fptreef(shf, indent, "%S ", iop->delim);
|
|
} else if (iop->name)
|
|
fptreef(shf, indent, (iop->flag & IONAMEXP) ? "%s " : "%S ",
|
|
iop->name);
|
|
}
|
|
|
|
|
|
/*
|
|
* variants of fputc, fputs for ptreef and snptreef
|
|
*/
|
|
|
|
static void
|
|
tputC(int c, struct shf *shf)
|
|
{
|
|
if ((c&0x60) == 0) { /* C0|C1 */
|
|
tputc((c&0x80) ? '$' : '^', shf);
|
|
tputc(((c&0x7F)|0x40), shf);
|
|
} else if ((c&0x7F) == 0x7F) { /* DEL */
|
|
tputc((c&0x80) ? '$' : '^', shf);
|
|
tputc('?', shf);
|
|
} else
|
|
tputc(c, shf);
|
|
}
|
|
|
|
static void
|
|
tputS(char *wp, struct shf *shf)
|
|
{
|
|
int c, quoted=0;
|
|
|
|
/* problems:
|
|
* `...` -> $(...)
|
|
* 'foo' -> "foo"
|
|
* could change encoding to:
|
|
* OQUOTE ["'] ... CQUOTE ["']
|
|
* COMSUB [(`] ...\0 (handle $ ` \ and maybe " in `...` case)
|
|
*/
|
|
while (1)
|
|
switch ((c = *wp++)) {
|
|
case EOS:
|
|
return;
|
|
case CHAR:
|
|
tputC(*wp++, shf);
|
|
break;
|
|
case QCHAR:
|
|
c = *wp++;
|
|
if (!quoted || (c == '"' || c == '`' || c == '$'))
|
|
tputc('\\', shf);
|
|
tputC(c, shf);
|
|
break;
|
|
case COMSUB:
|
|
tputc('$', shf);
|
|
tputc('(', shf);
|
|
while (*wp != 0)
|
|
tputC(*wp++, shf);
|
|
tputc(')', shf);
|
|
wp++;
|
|
break;
|
|
case EXPRSUB:
|
|
tputc('$', shf);
|
|
tputc('(', shf);
|
|
tputc('(', shf);
|
|
while (*wp != 0)
|
|
tputC(*wp++, shf);
|
|
tputc(')', shf);
|
|
tputc(')', shf);
|
|
wp++;
|
|
break;
|
|
case OQUOTE:
|
|
quoted = 1;
|
|
tputc('"', shf);
|
|
break;
|
|
case CQUOTE:
|
|
quoted = 0;
|
|
tputc('"', shf);
|
|
break;
|
|
case OSUBST:
|
|
tputc('$', shf);
|
|
if (*wp++ == '{')
|
|
tputc('{', shf);
|
|
while ((c = *wp++) != 0)
|
|
tputC(c, shf);
|
|
break;
|
|
case CSUBST:
|
|
if (*wp++ == '}')
|
|
tputc('}', shf);
|
|
break;
|
|
case OPAT:
|
|
tputc(*wp++, shf);
|
|
tputc('(', shf);
|
|
break;
|
|
case SPAT:
|
|
tputc('|', shf);
|
|
break;
|
|
case CPAT:
|
|
tputc(')', shf);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* this is the _only_ way to reliably handle
|
|
* variable args with an ANSI compiler
|
|
*/
|
|
/* VARARGS */
|
|
int
|
|
fptreef(struct shf *shf, int indent, const char *fmt, ...)
|
|
{
|
|
va_list va;
|
|
|
|
va_start(va, fmt);
|
|
|
|
vfptreef(shf, indent, fmt, va);
|
|
va_end(va);
|
|
return 0;
|
|
}
|
|
|
|
/* VARARGS */
|
|
char *
|
|
snptreef(char *s, int n, const char *fmt, ...)
|
|
{
|
|
va_list va;
|
|
struct shf shf;
|
|
|
|
shf_sopen(s, n, SHF_WR | (s ? 0 : SHF_DYNAMIC), &shf);
|
|
|
|
va_start(va, fmt);
|
|
vfptreef(&shf, 0, fmt, va);
|
|
va_end(va);
|
|
|
|
return shf_sclose(&shf); /* null terminates */
|
|
}
|
|
|
|
static void
|
|
vfptreef(struct shf *shf, int indent, const char *fmt, va_list va)
|
|
{
|
|
int c;
|
|
|
|
while ((c = *fmt++)) {
|
|
if (c == '%') {
|
|
switch ((c = *fmt++)) {
|
|
case 'c':
|
|
tputc(va_arg(va, int), shf);
|
|
break;
|
|
case 's':
|
|
shf_puts(va_arg(va, char *), shf);
|
|
break;
|
|
case 'S': /* word */
|
|
tputS(va_arg(va, char *), shf);
|
|
break;
|
|
case 'd': /* decimal */
|
|
shf_fprintf(shf, "%d", va_arg(va, int));
|
|
break;
|
|
case 'u': /* decimal */
|
|
shf_fprintf(shf, "%u", va_arg(va, unsigned));
|
|
break;
|
|
case 'T': /* format tree */
|
|
ptree(va_arg(va, struct op *), indent, shf);
|
|
break;
|
|
case ';': /* newline or ; */
|
|
case 'N': /* newline or space */
|
|
if (shf->flags & SHF_STRING) {
|
|
if (c == ';')
|
|
tputc(';', shf);
|
|
tputc(' ', shf);
|
|
} else {
|
|
int i;
|
|
|
|
tputc('\n', shf);
|
|
for (i = indent; i >= 8; i -= 8)
|
|
tputc('\t', shf);
|
|
for (; i > 0; --i)
|
|
tputc(' ', shf);
|
|
}
|
|
break;
|
|
case 'R':
|
|
pioact(shf, indent, va_arg(va, struct ioword *));
|
|
break;
|
|
default:
|
|
tputc(c, shf);
|
|
break;
|
|
}
|
|
} else
|
|
tputc(c, shf);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* copy tree (for function definition)
|
|
*/
|
|
|
|
struct op *
|
|
tcopy(struct op *t, Area *ap)
|
|
{
|
|
struct op *r;
|
|
char **tw, **rw;
|
|
|
|
if (t == NULL)
|
|
return NULL;
|
|
|
|
r = (struct op *) alloc(sizeof(struct op), ap);
|
|
|
|
r->type = t->type;
|
|
r->u.evalflags = t->u.evalflags;
|
|
|
|
r->str = t->type == TCASE ? wdcopy(t->str, ap) : str_save(t->str, ap);
|
|
|
|
if (t->vars == NULL)
|
|
r->vars = NULL;
|
|
else {
|
|
for (tw = t->vars; *tw++ != NULL; )
|
|
;
|
|
rw = r->vars = (char **)
|
|
alloc((tw - t->vars + 1) * sizeof(*tw), ap);
|
|
for (tw = t->vars; *tw != NULL; )
|
|
*rw++ = wdcopy(*tw++, ap);
|
|
*rw = NULL;
|
|
}
|
|
|
|
if (t->args == NULL)
|
|
r->args = NULL;
|
|
else {
|
|
for (tw = t->args; *tw++ != NULL; )
|
|
;
|
|
rw = r->args = (char **)
|
|
alloc((tw - t->args + 1) * sizeof(*tw), ap);
|
|
for (tw = t->args; *tw != NULL; )
|
|
*rw++ = wdcopy(*tw++, ap);
|
|
*rw = NULL;
|
|
}
|
|
|
|
r->ioact = (t->ioact == NULL) ? NULL : iocopy(t->ioact, ap);
|
|
|
|
r->left = tcopy(t->left, ap);
|
|
r->right = tcopy(t->right, ap);
|
|
r->lineno = t->lineno;
|
|
|
|
return r;
|
|
}
|
|
|
|
char *
|
|
wdcopy(const char *wp, Area *ap)
|
|
{
|
|
size_t len = wdscan(wp, EOS) - wp;
|
|
return memcpy(alloc(len, ap), wp, len);
|
|
}
|
|
|
|
/* return the position of prefix c in wp plus 1 */
|
|
const char *
|
|
wdscan(const char *wp, int c)
|
|
{
|
|
int nest = 0;
|
|
|
|
while (1)
|
|
switch (*wp++) {
|
|
case EOS:
|
|
return (wp);
|
|
case CHAR:
|
|
case QCHAR:
|
|
wp++;
|
|
break;
|
|
case COMSUB:
|
|
case EXPRSUB:
|
|
while (*wp++ != 0)
|
|
;
|
|
break;
|
|
case OQUOTE:
|
|
case CQUOTE:
|
|
break;
|
|
case OSUBST:
|
|
nest++;
|
|
while (*wp++ != '\0')
|
|
;
|
|
break;
|
|
case CSUBST:
|
|
wp++;
|
|
if (c == CSUBST && nest == 0)
|
|
return (wp);
|
|
nest--;
|
|
break;
|
|
case OPAT:
|
|
nest++;
|
|
wp++;
|
|
break;
|
|
case SPAT:
|
|
case CPAT:
|
|
if (c == wp[-1] && nest == 0)
|
|
return (wp);
|
|
if (wp[-1] == CPAT)
|
|
nest--;
|
|
break;
|
|
default:
|
|
internal_errorf(0,
|
|
"wdscan: unknown char 0x%x (carrying on)",
|
|
wp[-1]);
|
|
}
|
|
}
|
|
|
|
/* return a copy of wp without any of the mark up characters and
|
|
* with quote characters (" ' \) stripped.
|
|
* (string is allocated from ATEMP)
|
|
*/
|
|
char *
|
|
wdstrip(const char *wp)
|
|
{
|
|
struct shf shf;
|
|
int c;
|
|
|
|
shf_sopen(NULL, 32, SHF_WR | SHF_DYNAMIC, &shf);
|
|
|
|
/* problems:
|
|
* `...` -> $(...)
|
|
* x${foo:-"hi"} -> x${foo:-hi}
|
|
* x${foo:-'hi'} -> x${foo:-hi}
|
|
*/
|
|
while (1)
|
|
switch ((c = *wp++)) {
|
|
case EOS:
|
|
return shf_sclose(&shf); /* null terminates */
|
|
case CHAR:
|
|
case QCHAR:
|
|
shf_putchar(*wp++, &shf);
|
|
break;
|
|
case COMSUB:
|
|
shf_putchar('$', &shf);
|
|
shf_putchar('(', &shf);
|
|
while (*wp != 0)
|
|
shf_putchar(*wp++, &shf);
|
|
shf_putchar(')', &shf);
|
|
break;
|
|
case EXPRSUB:
|
|
shf_putchar('$', &shf);
|
|
shf_putchar('(', &shf);
|
|
shf_putchar('(', &shf);
|
|
while (*wp != 0)
|
|
shf_putchar(*wp++, &shf);
|
|
shf_putchar(')', &shf);
|
|
shf_putchar(')', &shf);
|
|
break;
|
|
case OQUOTE:
|
|
break;
|
|
case CQUOTE:
|
|
break;
|
|
case OSUBST:
|
|
shf_putchar('$', &shf);
|
|
if (*wp++ == '{')
|
|
shf_putchar('{', &shf);
|
|
while ((c = *wp++) != 0)
|
|
shf_putchar(c, &shf);
|
|
break;
|
|
case CSUBST:
|
|
if (*wp++ == '}')
|
|
shf_putchar('}', &shf);
|
|
break;
|
|
case OPAT:
|
|
shf_putchar(*wp++, &shf);
|
|
shf_putchar('(', &shf);
|
|
break;
|
|
case SPAT:
|
|
shf_putchar('|', &shf);
|
|
break;
|
|
case CPAT:
|
|
shf_putchar(')', &shf);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static struct ioword **
|
|
iocopy(struct ioword **iow, Area *ap)
|
|
{
|
|
struct ioword **ior;
|
|
int i;
|
|
|
|
for (ior = iow; *ior++ != NULL; )
|
|
;
|
|
ior = (struct ioword **) alloc((ior - iow + 1) * sizeof(*ior), ap);
|
|
|
|
for (i = 0; iow[i] != NULL; i++) {
|
|
struct ioword *p, *q;
|
|
|
|
p = iow[i];
|
|
q = (struct ioword *) alloc(sizeof(*p), ap);
|
|
ior[i] = q;
|
|
*q = *p;
|
|
if (p->name != NULL)
|
|
q->name = wdcopy(p->name, ap);
|
|
if (p->delim != NULL)
|
|
q->delim = wdcopy(p->delim, ap);
|
|
if (p->heredoc != NULL)
|
|
q->heredoc = str_save(p->heredoc, ap);
|
|
}
|
|
ior[i] = NULL;
|
|
|
|
return ior;
|
|
}
|
|
|
|
/*
|
|
* free tree (for function definition)
|
|
*/
|
|
|
|
void
|
|
tfree(struct op *t, Area *ap)
|
|
{
|
|
char **w;
|
|
|
|
if (t == NULL)
|
|
return;
|
|
|
|
if (t->str != NULL)
|
|
afree((void*)t->str, ap);
|
|
|
|
if (t->vars != NULL) {
|
|
for (w = t->vars; *w != NULL; w++)
|
|
afree((void*)*w, ap);
|
|
afree((void*)t->vars, ap);
|
|
}
|
|
|
|
if (t->args != NULL) {
|
|
for (w = t->args; *w != NULL; w++)
|
|
afree((void*)*w, ap);
|
|
afree((void*)t->args, ap);
|
|
}
|
|
|
|
if (t->ioact != NULL)
|
|
iofree(t->ioact, ap);
|
|
|
|
tfree(t->left, ap);
|
|
tfree(t->right, ap);
|
|
|
|
afree((void*)t, ap);
|
|
}
|
|
|
|
static void
|
|
iofree(struct ioword **iow, Area *ap)
|
|
{
|
|
struct ioword **iop;
|
|
struct ioword *p;
|
|
|
|
for (iop = iow; (p = *iop++) != NULL; ) {
|
|
if (p->name != NULL)
|
|
afree((void*)p->name, ap);
|
|
if (p->delim != NULL)
|
|
afree((void*)p->delim, ap);
|
|
if (p->heredoc != NULL)
|
|
afree((void*)p->heredoc, ap);
|
|
afree((void*)p, ap);
|
|
}
|
|
}
|