1 /* $OpenBSD: tree.c,v 1.20 2012/06/27 07:17:19 otto Exp $ */
4 * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
6 * Thorsten Glaser <tg@mirbsd.org>
8 * Provided that these terms and disclaimer and all copyright notices
9 * are retained or reproduced in an accompanying document, permission
10 * is granted to deal in this work without restriction, including un-
11 * limited rights to use, publicly perform, distribute, sell, modify,
12 * merge, give away, or sublicence.
14 * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
15 * the utmost extent permitted by applicable law, neither express nor
16 * implied; without malicious intent or gross negligence. In no event
17 * may a licensor, author or contributor be held liable for indirect,
18 * direct, other damage, loss, or other issues arising in any way out
19 * of dealing in the work, even if advised of the possibility of such
20 * damage or existence of a defect, except proven that it results out
21 * of said person's immediate fault when using the work as intended.
26 __RCSID("$MirOS: src/bin/mksh/tree.c,v 1.67 2012/12/04 01:10:35 tg Exp $");
30 static void ptree(struct op *, int, struct shf *);
31 static void pioact(struct shf *, int, struct ioword *);
32 static const char *wdvarput(struct shf *, const char *, int, int);
33 static void vfptreef(struct shf *, int, const char *, va_list);
34 static struct ioword **iocopy(struct ioword **, Area *);
35 static void iofree(struct ioword **, Area *);
37 /* "foo& ; bar" and "foo |& ; bar" are invalid */
38 static bool prevent_semicolon;
40 static const char Telif_pT[] = "elif %T";
43 * print a command tree
46 ptree(struct op *t, int indent, struct shf *shf)
49 struct ioword **ioact;
58 prevent_semicolon = false;
60 * special-case 'var=<<EOF' (rough; see
61 * exec.c:execute() for full code)
64 /* we have zero arguments, i.e. no programme to run */
66 /* we have exactly one variable assignment */
67 t->vars[0] != NULL && t->vars[1] == NULL &&
68 /* we have exactly one I/O redirection */
69 t->ioact != NULL && t->ioact[0] != NULL &&
70 t->ioact[1] == NULL &&
71 /* of type "here document" (or "here string") */
72 (t->ioact[0]->flag & IOTYPE) == IOHERE) {
73 fptreef(shf, indent, "%S", t->vars[0]);
78 w = (const char **)t->vars;
80 fptreef(shf, indent, "%S ", *w++);
82 shf_puts("#no-vars# ", shf);
86 fptreef(shf, indent, "%S ", *w++);
88 shf_puts("#no-args# ", shf);
94 fptreef(shf, indent + 2, "( %T) ", t->left);
97 fptreef(shf, indent, "%T| ", t->left);
101 fptreef(shf, indent, "%T%;", t->left);
106 fptreef(shf, indent, "%T%s %T",
107 t->left, (t->type == TOR) ? "||" : "&&", t->right);
111 prevent_semicolon = false;
118 fptreef(shf, indent, " %S", *w++);
119 shf_puts(" ]] ", shf);
123 fptreef(shf, indent, "%s %s ",
124 (t->type == TFOR) ? "for" : Tselect, t->str);
125 if (t->vars != NULL) {
126 shf_puts("in ", shf);
127 w = (const char **)t->vars;
129 fptreef(shf, indent, "%S ", *w++);
130 fptreef(shf, indent, "%;");
132 fptreef(shf, indent + INDENT, "do%N%T", t->left);
133 fptreef(shf, indent, "%;done ");
136 fptreef(shf, indent, "case %S in", t->str);
137 for (t1 = t->left; t1 != NULL; t1 = t1->right) {
138 fptreef(shf, indent, "%N(");
139 w = (const char **)t1->vars;
141 fptreef(shf, indent, "%S%c", *w,
142 (w[1] != NULL) ? '|' : ')');
145 fptreef(shf, indent + INDENT, "%N%T%N;%c", t1->left,
148 fptreef(shf, indent, "%Nesac ");
152 internal_errorf("TELIF in tree.c:ptree() unexpected");
162 fptreef(shf, indent, "%;");
164 /* 5 == strlen("elif ") */
165 fptreef(shf, indent + 5 - i, Telif_pT + i, t1->left);
167 if (t1->left != NULL) {
168 fptreef(shf, indent, "%;");
169 fptreef(shf, indent + INDENT, "%s%N%T",
172 } while (t1->right && t1->right->type == TELIF);
173 if (t1->right != NULL) {
174 fptreef(shf, indent, "%;");
175 fptreef(shf, indent + INDENT, "%s%N%T",
178 fptreef(shf, indent, "%;fi ");
182 /* 6 == strlen("while "/"until ") */
183 fptreef(shf, indent + 6, "%s %T",
184 (t->type == TWHILE) ? "while" : "until",
186 fptreef(shf, indent, "%;");
187 fptreef(shf, indent + INDENT, "do%N%T", t->right);
188 fptreef(shf, indent, "%;done ");
191 fptreef(shf, indent + INDENT, "{%N%T", t->left);
192 fptreef(shf, indent, "%;} ");
195 fptreef(shf, indent, "%T|& ", t->left);
196 prevent_semicolon = true;
199 fptreef(shf, indent, "%T& ", t->left);
200 prevent_semicolon = true;
203 fpFUNCTf(shf, indent, tobool(t->u.ksh_func), t->str, t->left);
206 fptreef(shf, indent, "%s %T", "time", t->left);
209 shf_puts("<botch>", shf);
210 prevent_semicolon = false;
213 if ((ioact = t->ioact) != NULL) {
214 bool need_nl = false;
216 while (*ioact != NULL)
217 pioact(shf, indent, *ioact++);
218 /* Print here documents after everything else... */
220 while (*ioact != NULL) {
221 struct ioword *iop = *ioact++;
223 /* heredoc is NULL when tracing (set -x) */
224 if ((iop->flag & (IOTYPE | IOHERESTR)) == IOHERE &&
227 shf_puts(iop->heredoc, shf);
228 fptreef(shf, indent, "%s",
229 iop->flag & IONDELIM ? "<<" :
230 evalstr(iop->delim, 0));
235 * Last delimiter must be followed by a newline (this
236 * often leads to an extra blank line, but it's not
237 * worth worrying about)
241 prevent_semicolon = true;
247 pioact(struct shf *shf, int indent, struct ioword *iop)
249 int flag = iop->flag;
250 int type = flag & IOTYPE;
253 expected = (type == IOREAD || type == IORDWR || type == IOHERE) ? 0 :
254 (type == IOCAT || type == IOWRITE) ? 1 :
255 (type == IODUP && (iop->unit == !(flag & IORDUP))) ? iop->unit :
257 if (iop->unit != expected)
258 shf_fprintf(shf, "%d", iop->unit);
265 shf_puts(flag & IOSKIP ? "<<-" : "<<", shf);
271 shf_puts(flag & IOCLOB ? ">|" : ">", shf);
277 shf_puts(flag & IORDUP ? "<&" : ">&", shf);
280 /* name/delim are NULL when printing syntax errors */
281 if (type == IOHERE) {
283 wdvarput(shf, iop->delim, 0, WDS_TPUTS);
284 if (iop->flag & IOHERESTR)
286 } else if (iop->name)
287 fptreef(shf, indent, (iop->flag & IONAMEXP) ? "%s " : "%S ",
289 prevent_semicolon = false;
292 /* variant of fputs for ptreef and wdstrip */
294 wdvarput(struct shf *shf, const char *wp, int quotelevel, int opmode)
303 * x${foo:-"hi"} -> x${foo:-hi} unless WDS_TPUTS
304 * x${foo:-'hi'} -> x${foo:-hi} unless WDS_KEEPQ
305 * could change encoding to:
306 * OQUOTE ["'] ... CQUOTE ["']
307 * COMSUB [(`] ...\0 (handle $ ` \ and maybe " in `...` case)
309 while (/* CONSTCOND */ 1)
316 if ((opmode & WDS_MAGIC) &&
317 (ISMAGIC(c) || c == '[' || c == '!' ||
318 c == '-' || c == ']' || c == '*' || c == '?'))
319 shf_putc(MAGIC, shf);
326 doq = (c == '"' || c == '`' || c == '$' || c == '\\');
327 if (opmode & WDS_TPUTS) {
331 if (!(opmode & WDS_KEEPQ))
343 while ((c = *wp++) != 0)
348 shf_puts("${ ", shf);
352 shf_puts("$((", shf);
356 if (opmode & WDS_TPUTS) {
362 if (opmode & WDS_TPUTS) {
372 while ((c = *wp++) != 0)
374 wp = wdvarput(shf, wp, 0, opmode);
381 if (opmode & WDS_MAGIC) {
382 shf_putc(MAGIC, shf);
383 shf_putchar(*wp++ | 0x80, shf);
385 shf_putchar(*wp++, shf);
394 if (opmode & WDS_MAGIC)
395 shf_putc(MAGIC, shf);
402 * this is the _only_ way to reliably handle
403 * variable args with an ANSI compiler
407 fptreef(struct shf *shf, int indent, const char *fmt, ...)
412 vfptreef(shf, indent, fmt, va);
418 snptreef(char *s, ssize_t n, const char *fmt, ...)
423 shf_sopen(s, n, SHF_WR | (s ? 0 : SHF_DYNAMIC), &shf);
426 vfptreef(&shf, 0, fmt, va);
429 /* shf_sclose NUL terminates */
430 return (shf_sclose(&shf));
434 vfptreef(struct shf *shf, int indent, const char *fmt, va_list va)
438 while ((c = *fmt++)) {
440 switch ((c = *fmt++)) {
442 /* character (octet, probably) */
443 shf_putchar(va_arg(va, int), shf);
447 shf_puts(va_arg(va, char *), shf);
451 wdvarput(shf, va_arg(va, char *), 0, WDS_TPUTS);
455 shf_fprintf(shf, "%d", va_arg(va, int));
458 /* unsigned decimal */
459 shf_fprintf(shf, "%u", va_arg(va, unsigned int));
463 ptree(va_arg(va, struct op *), indent, shf);
464 goto dont_trash_prevent_semicolon;
468 /* newline or space */
469 if (shf->flags & SHF_STRING) {
470 if (c == ';' && !prevent_semicolon)
487 /* I/O redirection */
488 pioact(shf, indent, va_arg(va, struct ioword *));
496 prevent_semicolon = false;
497 dont_trash_prevent_semicolon:
503 * copy tree (for function definition)
506 tcopy(struct op *t, Area *ap)
515 r = alloc(sizeof(struct op), ap);
518 r->u.evalflags = t->u.evalflags;
520 if (t->type == TCASE)
521 r->str = wdcopy(t->str, ap);
523 strdupx(r->str, t->str, ap);
528 tw = (const char **)t->vars;
531 rw = r->vars = alloc2(tw - (const char **)t->vars + 1,
533 tw = (const char **)t->vars;
535 *rw++ = wdcopy(*tw++, ap);
545 r->args = (const char **)(rw = alloc2(tw - t->args + 1,
549 *rw++ = wdcopy(*tw++, ap);
553 r->ioact = (t->ioact == NULL) ? NULL : iocopy(t->ioact, ap);
555 r->left = tcopy(t->left, ap);
556 r->right = tcopy(t->right, ap);
557 r->lineno = t->lineno;
563 wdcopy(const char *wp, Area *ap)
567 len = wdscan(wp, EOS) - wp;
568 return (memcpy(alloc(len, ap), wp, len));
571 /* return the position of prefix c in wp plus 1 */
573 wdscan(const char *wp, int c)
577 while (/* CONSTCOND */ 1)
600 while (*wp++ != '\0')
605 if (c == CSUBST && nest == 0)
615 if (c == wp[-1] && nest == 0)
622 "wdscan: unknown char 0x%x (carrying on)",
628 * return a copy of wp without any of the mark up characters and with
629 * quote characters (" ' \) stripped. (string is allocated from ATEMP)
632 wdstrip(const char *wp, int opmode)
636 shf_sopen(NULL, 32, SHF_WR | SHF_DYNAMIC, &shf);
637 wdvarput(&shf, wp, 0, opmode);
638 /* shf_sclose NUL terminates */
639 return (shf_sclose(&shf));
642 static struct ioword **
643 iocopy(struct ioword **iow, Area *ap)
651 ior = alloc2(ior - iow + 1, sizeof(struct ioword *), ap);
653 for (i = 0; iow[i] != NULL; i++) {
654 struct ioword *p, *q;
657 q = alloc(sizeof(struct ioword), ap);
661 q->name = wdcopy(p->name, ap);
662 if (p->delim != NULL)
663 q->delim = wdcopy(p->delim, ap);
664 if (p->heredoc != NULL)
665 strdupx(q->heredoc, p->heredoc, ap);
673 * free tree (for function definition)
676 tfree(struct op *t, Area *ap)
686 if (t->vars != NULL) {
687 for (w = t->vars; *w != NULL; w++)
692 if (t->args != NULL) {
693 /*XXX we assume the caller is right */
694 union mksh_ccphack cw;
697 for (w = cw.rw; *w != NULL; w++)
702 if (t->ioact != NULL)
703 iofree(t->ioact, ap);
712 iofree(struct ioword **iow, Area *ap)
718 while ((p = *iop++) != NULL) {
721 if (p->delim != NULL)
723 if (p->heredoc != NULL)
724 afree(p->heredoc, ap);
731 fpFUNCTf(struct shf *shf, int i, bool isksh, const char *k, struct op *v)
734 fptreef(shf, i, "%s %s %T", Tfunction, k, v);
736 fptreef(shf, i, "%s() %T", k, v);
742 vistree(char *dst, size_t sz, struct op *t)
748 buf = alloc(sz + 8, ATEMP);
749 snptreef(buf, sz + 8, "%T", t);
752 if (UTFMODE && (n = utf_mbtowc(&c, cp)) != (size_t)-1) {
753 if (c == 0 || n >= sz)
754 /* NUL or not enough free space */
756 /* copy multibyte char */
762 if (--sz == 0 || (c = (unsigned char)(*cp++)) == 0)
763 /* NUL or not enough free space */
765 if ((c & 0x60) == 0 || (c & 0x7F) == 0x7F) {
766 /* C0 or C1 control character or DEL */
768 /* not enough free space for two chars */
770 *dst++ = (c & 0x80) ? '$' : '^';
771 c = (c & 0x7F) ^ 0x40;
772 } else if (UTFMODE && c > 0x7F) {
773 /* better not try to display broken multibyte chars */
786 dumpchar(struct shf *shf, int c)
788 if (((c & 0x60) == 0) || ((c & 0x7F) == 0x7F)) {
789 /* C0 or C1 control character or DEL */
790 shf_putc((c & 0x80) ? '$' : '^', shf);
791 c = (c & 0x7F) ^ 0x40;
798 dumpwdvar_i(struct shf *shf, const char *wp, int quotelevel)
802 while (/* CONSTCOND */ 1) {
805 shf_puts("EOS", shf);
808 shf_puts("ADELIM=", shf);
811 shf_puts("CHAR=", shf);
812 dumpchar(shf, *wp++);
815 shf_puts("QCHAR<", shf);
817 if (quotelevel == 0 ||
818 (c == '"' || c == '`' || c == '$' || c == '\\'))
823 shf_puts("COMSUB<", shf);
825 while ((c = *wp++) != 0)
831 shf_puts("FUNSUB<", shf);
834 shf_puts("EXPRSUB<", shf);
837 shf_fprintf(shf, "OQUOTE{%d", ++quotelevel);
840 shf_fprintf(shf, "%d}CQUOTE", quotelevel);
844 shf_puts("(err)", shf);
847 shf_puts("OSUBST(", shf);
848 dumpchar(shf, *wp++);
850 while ((c = *wp++) != 0)
853 wp = dumpwdvar_i(shf, wp, 0);
856 shf_puts("]CSUBST(", shf);
857 dumpchar(shf, *wp++);
861 shf_puts("OPAT=", shf);
862 dumpchar(shf, *wp++);
865 shf_puts("SPAT", shf);
868 shf_puts("CPAT", shf);
871 shf_fprintf(shf, "INVAL<%u>", (uint8_t)wp[-1]);
878 dumpwdvar(struct shf *shf, const char *wp)
880 dumpwdvar_i(shf, wp, 0);
884 dumpioact(struct shf *shf, struct op *t)
886 struct ioword **ioact, *iop;
888 if ((ioact = t->ioact) == NULL)
891 shf_puts("{IOACT", shf);
892 while ((iop = *ioact++) != NULL) {
893 int type = iop->flag & IOTYPE;
894 #define DT(x) case x: shf_puts(#x, shf); break;
895 #define DB(x) if (iop->flag & x) shf_puts("|" #x, shf);
906 shf_fprintf(shf, "unk%d", type);
916 shf_fprintf(shf, ",unit=%d", iop->unit);
918 shf_puts(",delim<", shf);
919 dumpwdvar(shf, iop->delim);
923 if (iop->flag & IONAMEXP) {
924 shf_puts(",name=", shf);
925 print_value_quoted(shf, iop->name);
927 shf_puts(",name<", shf);
928 dumpwdvar(shf, iop->name);
933 shf_puts(",heredoc=", shf);
934 print_value_quoted(shf, iop->heredoc);
943 dumptree(struct shf *shf, struct op *t)
946 const char **w, *name;
950 for (i = 0; i < nesting; ++i)
953 shf_puts("{tree:" /*}*/, shf);
960 #define OPEN(x) case x: name = #x; shf_puts(" {" #x ":", shf); /*}*/
965 w = (const char **)t->vars;
968 for (j = 0; j < nesting; ++j)
970 shf_fprintf(shf, " var%d<", i++);
971 dumpwdvar(shf, *w++);
975 shf_puts(" #no-vars#", shf);
981 for (j = 0; j < nesting; ++j)
983 shf_fprintf(shf, " arg%d<", i++);
984 dumpwdvar(shf, *w++);
988 shf_puts(" #no-args#", shf);
1000 dumpleftmidrightandout:
1001 shf_putc('\n', shf);
1002 dumptree(shf, t->left);
1003 /* middumprightandout: (unused) */
1004 shf_fprintf(shf, "/%s:", name);
1009 goto dumpleftmidrightandout;
1011 goto dumpleftmidrightandout;
1013 goto dumpleftmidrightandout;
1015 goto dumprightandout;
1020 shf_putc('\n', shf);
1021 for (j = 0; j < nesting; ++j)
1022 shf_putc('\t', shf);
1023 shf_fprintf(shf, " arg%d<", i++);
1024 dumpwdvar(shf, *w++);
1030 shf_fprintf(shf, " str<%s>", t->str);
1031 if (t->vars != NULL) {
1033 w = (const char **)t->vars;
1035 shf_putc('\n', shf);
1036 for (j = 0; j < nesting; ++j)
1037 shf_putc('\t', shf);
1038 shf_fprintf(shf, " var%d<", i++);
1039 dumpwdvar(shf, *w++);
1043 goto dumpleftandout;
1047 shf_fprintf(shf, " str<%s>", t->str);
1049 for (t1 = t->left; t1 != NULL; t1 = t1->right) {
1050 shf_putc('\n', shf);
1051 for (j = 0; j < nesting; ++j)
1052 shf_putc('\t', shf);
1053 shf_fprintf(shf, " sub%d[(", i);
1054 w = (const char **)t1->vars;
1063 shf_putc('\n', shf);
1064 dumptree(shf, t1->left);
1065 shf_fprintf(shf, " ;%c/%d]", t1->u.charflag, i++);
1069 goto dumpleftmidrightandout;
1071 goto dumpleftmidrightandout;
1073 goto dumpleftandout;
1075 goto dumpleftandout;
1077 goto dumpleftandout;
1079 shf_fprintf(shf, " str<%s> ksh<%s>", t->str,
1080 t->u.ksh_func ? "yes" : "no");
1081 goto dumpleftandout;
1083 goto dumpleftandout;
1086 shf_putc('\n', shf);
1087 dumptree(shf, t->left);
1090 if (t->left != NULL) {
1091 shf_puts(" /TTHEN:\n", shf);
1092 dumptree(shf, t->left);
1094 if (t->right && t->right->type == TELIF) {
1095 shf_puts(" /TELIF:", shf);
1100 if (t->right != NULL) {
1101 shf_puts(" /TELSE:\n", shf);
1102 dumptree(shf, t->right);
1107 shf_puts("unexpected", shf);
1110 goto dumpunexpected;
1112 goto dumpunexpected;
1115 shf_fprintf(shf, "{T<%d>:" /*}*/, t->type);
1116 goto dumpunexpected;
1121 shf_fprintf(shf, /*{*/ " /%s}\n", name);