/* $OpenBSD: tree.c,v 1.19 2008/08/11 21:50:35 jaredy Exp $ */ /*- * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 * Thorsten Glaser <tg@mirbsd.org> * * Provided that these terms and disclaimer and all copyright notices * are retained or reproduced in an accompanying document, permission * is granted to deal in this work without restriction, including un- * limited rights to use, publicly perform, distribute, sell, modify, * merge, give away, or sublicence. * * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to * the utmost extent permitted by applicable law, neither express nor * implied; without malicious intent or gross negligence. In no event * may a licensor, author or contributor be held liable for indirect, * direct, other damage, loss, or other issues arising in any way out * of dealing in the work, even if advised of the possibility of such * damage or existence of a defect, except proven that it results out * of said person's immediate fault when using the work as intended. */ #include "sh.h" __RCSID("$MirOS: src/bin/mksh/tree.c,v 1.30 2010/02/25 20:18:19 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) { const char **w; struct ioword **ioact; struct op *t1; Chain: if (t == NULL) return; switch (t->type) { case TCOM: if (t->vars) for (w = (const char **)t->vars; *w != NULL; ) fptreef(shf, indent, "%S ", *w++); else shf_puts("#no-vars# ", shf); if (t->args) for (w = t->args; *w != NULL; ) fptreef(shf, indent, "%S ", *w++); else shf_puts("#no-args# ", shf); 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: shf_puts("! ", shf); t = t->right; goto Chain; case TDBRACKET: { int i; shf_puts("[[", shf); for (i = 0; t->args[i]; i++) fptreef(shf, indent, " %S", t->args[i]); shf_puts(" ]] ", shf); break; } case TSELECT: fptreef(shf, indent, "select %s ", t->str); /* FALLTHROUGH */ case TFOR: if (t->type == TFOR) fptreef(shf, indent, "for %s ", t->str); if (t->vars != NULL) { shf_puts("in ", shf); for (w = (const char **)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 = (const char **)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: shf_puts("<botch>", shf); 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 && /* iop->delim[1] == '<' means here string */ (!iop->delim || iop->delim[1] != '<')) { 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) shf_fprintf(shf, "%d", iop->unit); switch (type) { case IOREAD: shf_puts("< ", shf); break; case IOHERE: shf_puts(flag & IOSKIP ? "<<-" : "<<", shf); break; case IOCAT: shf_puts(">> ", shf); break; case IOWRITE: shf_puts(flag & IOCLOB ? ">| " : "> ", shf); break; case IORDWR: shf_puts("<> ", shf); break; case IODUP: shf_puts(flag & IORDUP ? "<&" : ">&", shf); break; } /* name/delim are 0 when printing syntax errors */ if (type == IOHERE) { if (iop->delim) fptreef(shf, indent, "%s%S ", /* here string */ iop->delim[1] == '<' ? "" : " ", iop->delim); else tputc(' ', shf); } 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, quotelevel = 0; /* problems: * `...` -> $(...) * 'foo' -> "foo" * could change encoding to: * OQUOTE ["'] ... CQUOTE ["'] * COMSUB [(`] ...\0 (handle $ ` \ and maybe " in `...` case) */ while (1) switch (*wp++) { case EOS: return; case ADELIM: case CHAR: tputC(*wp++, shf); break; case QCHAR: c = *wp++; if (!quotelevel || (c == '"' || c == '`' || c == '$')) tputc('\\', shf); tputC(c, shf); break; case COMSUB: shf_puts("$(", shf); while (*wp != 0) tputC(*wp++, shf); tputc(')', shf); wp++; break; case EXPRSUB: shf_puts("$((", shf); while (*wp != 0) tputC(*wp++, shf); shf_puts("))", shf); wp++; break; case OQUOTE: quotelevel++; tputc('"', shf); break; case CQUOTE: if (quotelevel) quotelevel--; 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 int)); 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; const char **tw; char **rw; if (t == NULL) return (NULL); r = alloc(sizeof(struct op), ap); r->type = t->type; r->u.evalflags = t->u.evalflags; if (t->type == TCASE) r->str = wdcopy(t->str, ap); else strdupx(r->str, t->str, ap); if (t->vars == NULL) r->vars = NULL; else { for (tw = (const char **)t->vars; *tw++ != NULL; ) ; rw = r->vars = alloc((tw - (const char **)t->vars + 1) * sizeof(*tw), ap); for (tw = (const char **)t->vars; *tw != NULL; ) *rw++ = wdcopy(*tw++, ap); *rw = NULL; } if (t->args == NULL) r->args = NULL; else { for (tw = t->args; *tw++ != NULL; ) ; r->args = (const char **)(rw = 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 ADELIM: if (c == ADELIM) return (wp + 1); /* FALLTHROUGH */ 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_warningf( "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, bool keepq, bool make_magic) { 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} unless keepq */ while (1) switch (*wp++) { case EOS: return (shf_sclose(&shf)); /* null terminates */ case ADELIM: case CHAR: c = *wp++; if (make_magic && (ISMAGIC(c) || c == '[' || c == NOT || c == '-' || c == ']' || c == '*' || c == '?')) shf_putchar(MAGIC, &shf); shf_putchar(c, &shf); break; case QCHAR: c = *wp++; if (keepq && (c == '"' || c == '`' || c == '$' || c == '\\')) shf_putchar('\\', &shf); shf_putchar(c, &shf); break; case COMSUB: shf_puts("$(", &shf); while (*wp != 0) shf_putchar(*wp++, &shf); shf_putchar(')', &shf); break; case EXPRSUB: shf_puts("$((", &shf); while (*wp != 0) shf_putchar(*wp++, &shf); shf_puts("))", &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: if (make_magic) { shf_putchar(MAGIC, &shf); shf_putchar(*wp++ | 0x80, &shf); } else { shf_putchar(*wp++, &shf); shf_putchar('(', &shf); } break; case SPAT: if (make_magic) shf_putchar(MAGIC, &shf); shf_putchar('|', &shf); break; case CPAT: if (make_magic) shf_putchar(MAGIC, &shf); shf_putchar(')', &shf); break; } } static struct ioword ** iocopy(struct ioword **iow, Area *ap) { struct ioword **ior; int i; for (ior = iow; *ior++ != NULL; ) ; ior = alloc((ior - iow + 1) * sizeof(struct ioword *), ap); for (i = 0; iow[i] != NULL; i++) { struct ioword *p, *q; p = iow[i]; q = alloc(sizeof(struct ioword), 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) strdupx(q->heredoc, 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(t->str, ap); if (t->vars != NULL) { for (w = t->vars; *w != NULL; w++) afree(*w, ap); afree(t->vars, ap); } if (t->args != NULL) { union mksh_ccphack cw; /* XXX we assume the caller is right */ cw.ro = t->args; for (w = cw.rw; *w != NULL; w++) afree(*w, ap); afree(t->args, ap); } if (t->ioact != NULL) iofree(t->ioact, ap); tfree(t->left, ap); tfree(t->right, ap); afree(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(p->name, ap); if (p->delim != NULL) afree(p->delim, ap); if (p->heredoc != NULL) afree(p->heredoc, ap); afree(p, ap); } afree(iow, ap); }