| /* $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); |
| } |