1 /* $OpenBSD: tree.c,v 1.21 2015/09/01 13:12:31 tedu Exp $ */
4 * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
5 * 2011, 2012, 2013, 2015, 2016, 2017
6 * mirabilos <m@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.89 2017/04/12 16:46:23 tg Exp $");
30 static void ptree(struct op *, int, struct shf *);
31 static void pioact(struct shf *, 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;
59 prevent_semicolon = false;
60 /* special-case 'var=<<EOF' (cf. exec.c:execute) */
62 /* we have zero arguments, i.e. no program to run */
64 /* we have exactly one variable assignment */
65 t->vars[0] != NULL && t->vars[1] == NULL &&
66 /* we have exactly one I/O redirection */
67 t->ioact != NULL && t->ioact[0] != NULL &&
68 t->ioact[1] == NULL &&
69 /* of type "here document" (or "here string") */
70 (t->ioact[0]->ioflag & IOTYPE) == IOHERE &&
71 /* the variable assignment begins with a valid varname */
72 (ccp = skip_wdvarname(t->vars[0], true)) != t->vars[0] &&
73 /* and has no right-hand side (i.e. "varname=") */
74 ccp[0] == CHAR && ((ccp[1] == '=' && ccp[2] == EOS) ||
75 /* or "varname+=" */ (ccp[1] == '+' && ccp[2] == CHAR &&
76 ccp[3] == '=' && ccp[4] == EOS))) {
77 fptreef(shf, indent, Tf_S, t->vars[0]);
82 w = (const char **)t->vars;
84 fptreef(shf, indent, Tf_S_, *w++);
86 shf_puts("#no-vars# ", shf);
89 if (*w && **w == CHAR) {
90 char *cp = wdstrip(*w++, WDS_TPUTS);
92 if (valid_alias_name(cp))
99 fptreef(shf, indent, Tf_S_, *w++);
101 shf_puts("#no-args# ", shf);
107 fptreef(shf, indent + 2, "( %T) ", t->left);
110 fptreef(shf, indent, "%T| ", t->left);
114 fptreef(shf, indent, "%T%;", t->left);
119 fptreef(shf, indent, "%T%s %T",
120 t->left, (t->type == TOR) ? "||" : "&&", t->right);
124 prevent_semicolon = false;
131 fptreef(shf, indent, Tf__S, *w++);
132 shf_puts(" ]] ", shf);
136 fptreef(shf, indent, "%s %s ",
137 (t->type == TFOR) ? "for" : Tselect, t->str);
138 if (t->vars != NULL) {
139 shf_puts("in ", shf);
140 w = (const char **)t->vars;
142 fptreef(shf, indent, Tf_S_, *w++);
143 fptreef(shf, indent, Tft_end);
145 fptreef(shf, indent + INDENT, "do%N%T", t->left);
146 fptreef(shf, indent, "%;done ");
149 fptreef(shf, indent, "case %S in", t->str);
150 for (t1 = t->left; t1 != NULL; t1 = t1->right) {
151 fptreef(shf, indent, "%N(");
152 w = (const char **)t1->vars;
154 fptreef(shf, indent, "%S%c", *w,
155 (w[1] != NULL) ? '|' : ')');
158 fptreef(shf, indent + INDENT, "%N%T%N;%c", t1->left,
161 fptreef(shf, indent, "%Nesac ");
164 internal_errorf(TELIF_unexpected);
173 fptreef(shf, indent, Tft_end);
175 /* 5 == strlen("elif ") */
176 fptreef(shf, indent + 5 - i, Telif_pT + i, t1->left);
178 if (t1->left != NULL) {
179 fptreef(shf, indent, Tft_end);
180 fptreef(shf, indent + INDENT, "%s%N%T",
183 } while (t1->right && t1->right->type == TELIF);
184 if (t1->right != NULL) {
185 fptreef(shf, indent, Tft_end);
186 fptreef(shf, indent + INDENT, "%s%N%T",
189 fptreef(shf, indent, "%;fi ");
193 /* 6 == strlen("while "/"until ") */
194 fptreef(shf, indent + 6, Tf_s_T,
195 (t->type == TWHILE) ? "while" : "until",
197 fptreef(shf, indent, Tft_end);
198 fptreef(shf, indent + INDENT, "do%N%T", t->right);
199 fptreef(shf, indent, "%;done ");
202 fptreef(shf, indent + INDENT, "{%N%T", t->left);
203 fptreef(shf, indent, "%;} ");
206 fptreef(shf, indent, "%T|& ", t->left);
207 prevent_semicolon = true;
210 fptreef(shf, indent, "%T& ", t->left);
211 prevent_semicolon = true;
214 fpFUNCTf(shf, indent, tobool(t->u.ksh_func), t->str, t->left);
217 fptreef(shf, indent, Tf_s_T, Ttime, t->left);
220 shf_puts("<botch>", shf);
221 prevent_semicolon = false;
224 if ((ioact = t->ioact) != NULL) {
225 bool need_nl = false;
227 while (*ioact != NULL)
228 pioact(shf, *ioact++);
229 /* Print here documents after everything else... */
231 while (*ioact != NULL) {
232 struct ioword *iop = *ioact++;
234 /* heredoc is NULL when tracing (set -x) */
235 if ((iop->ioflag & (IOTYPE | IOHERESTR)) == IOHERE &&
238 shf_puts(iop->heredoc, shf);
239 fptreef(shf, indent, Tf_s,
240 evalstr(iop->delim, 0));
245 * Last delimiter must be followed by a newline (this
246 * often leads to an extra blank line, but it's not
247 * worth worrying about)
251 prevent_semicolon = true;
257 pioact(struct shf *shf, struct ioword *iop)
259 unsigned short flag = iop->ioflag;
260 unsigned short type = flag & IOTYPE;
263 expected = (type == IOREAD || type == IORDWR || type == IOHERE) ? 0 :
264 (type == IOCAT || type == IOWRITE) ? 1 :
265 (type == IODUP && (iop->unit == !(flag & IORDUP))) ? iop->unit :
267 if (iop->unit != expected)
268 shf_fprintf(shf, Tf_d, (int)iop->unit);
278 else if (flag & IOHERESTR)
293 shf_puts(flag & IORDUP ? "<&" : ">&", shf);
296 /* name/delim are NULL when printing syntax errors */
297 if (type == IOHERE) {
298 if (iop->delim && !(iop->ioflag & IONDELIM))
299 wdvarput(shf, iop->delim, 0, WDS_TPUTS);
300 } else if (iop->ioname) {
302 print_value_quoted(shf, iop->ioname);
304 wdvarput(shf, iop->ioname, 0, WDS_TPUTS);
307 prevent_semicolon = false;
310 /* variant of fputs for ptreef and wdstrip */
312 wdvarput(struct shf *shf, const char *wp, int quotelevel, int opmode)
321 * x${foo:-"hi"} -> x${foo:-hi} unless WDS_TPUTS
322 * x${foo:-'hi'} -> x${foo:-hi}
323 * could change encoding to:
324 * OQUOTE ["'] ... CQUOTE ["']
325 * COMSUB [(`] ...\0 (handle $ ` \ and maybe " in `...` case)
327 while (/* CONSTCOND */ 1)
332 if (*wp == /*{*/'}') {
334 goto wdvarput_csubst;
343 if (opmode & WDS_TPUTS)
346 if (quotelevel == 0) {
368 if (*wp == '(' /*)*/)
371 while ((c = *wp++) != 0)
388 shf_puts("$((", shf);
392 if (opmode & WDS_TPUTS) {
398 if (opmode & WDS_TPUTS) {
408 while ((c = *wp++) != 0)
410 wp = wdvarput(shf, wp, 0, opmode);
419 shf_putchar(*wp++, shf);
434 * this is the _only_ way to reliably handle
435 * variable args with an ANSI compiler
439 fptreef(struct shf *shf, int indent, const char *fmt, ...)
444 vfptreef(shf, indent, fmt, va);
450 snptreef(char *s, ssize_t n, const char *fmt, ...)
455 shf_sopen(s, n, SHF_WR | (s ? 0 : SHF_DYNAMIC), &shf);
458 vfptreef(&shf, 0, fmt, va);
461 /* shf_sclose NUL terminates */
462 return (shf_sclose(&shf));
466 vfptreef(struct shf *shf, int indent, const char *fmt, va_list va)
470 while ((c = *fmt++)) {
472 switch ((c = *fmt++)) {
474 /* character (octet, probably) */
475 shf_putchar(va_arg(va, int), shf);
479 shf_puts(va_arg(va, char *), shf);
483 wdvarput(shf, va_arg(va, char *), 0, WDS_TPUTS);
487 shf_fprintf(shf, Tf_d, va_arg(va, int));
490 /* unsigned decimal */
491 shf_fprintf(shf, "%u", va_arg(va, unsigned int));
495 ptree(va_arg(va, struct op *), indent, shf);
496 goto dont_trash_prevent_semicolon;
500 /* newline or space */
501 if (shf->flags & SHF_STRING) {
502 if (c == ';' && !prevent_semicolon)
519 /* I/O redirection */
520 pioact(shf, va_arg(va, struct ioword *));
528 prevent_semicolon = false;
529 dont_trash_prevent_semicolon:
535 * copy tree (for function definition)
538 tcopy(struct op *t, Area *ap)
547 r = alloc(sizeof(struct op), ap);
550 r->u.evalflags = t->u.evalflags;
552 if (t->type == TCASE)
553 r->str = wdcopy(t->str, ap);
555 strdupx(r->str, t->str, ap);
560 tw = (const char **)t->vars;
563 rw = r->vars = alloc2(tw - (const char **)t->vars + 1,
565 tw = (const char **)t->vars;
567 *rw++ = wdcopy(*tw++, ap);
577 r->args = (const char **)(rw = alloc2(tw - t->args + 1,
581 *rw++ = wdcopy(*tw++, ap);
585 r->ioact = (t->ioact == NULL) ? NULL : iocopy(t->ioact, ap);
587 r->left = tcopy(t->left, ap);
588 r->right = tcopy(t->right, ap);
589 r->lineno = t->lineno;
595 wdcopy(const char *wp, Area *ap)
599 len = wdscan(wp, EOS) - wp;
600 return (memcpy(alloc(len, ap), wp, len));
603 /* return the position of prefix c in wp plus 1 */
605 wdscan(const char *wp, int c)
609 while (/* CONSTCOND */ 1)
614 if (c == ADELIM && nest == 0)
637 while (*wp++ != '\0')
643 if (c == CSUBST && nest == 0)
653 if (c == wp[-1] && nest == 0)
660 "wdscan: unknown char 0x%X (carrying on)",
661 (unsigned char)wp[-1]);
666 * return a copy of wp without any of the mark up characters and with
667 * quote characters (" ' \) stripped. (string is allocated from ATEMP)
670 wdstrip(const char *wp, int opmode)
674 shf_sopen(NULL, 32, SHF_WR | SHF_DYNAMIC, &shf);
675 wdvarput(&shf, wp, 0, opmode);
676 /* shf_sclose NUL terminates */
677 return (shf_sclose(&shf));
680 static struct ioword **
681 iocopy(struct ioword **iow, Area *ap)
689 ior = alloc2(ior - iow + 1, sizeof(struct ioword *), ap);
691 for (i = 0; iow[i] != NULL; i++) {
692 struct ioword *p, *q;
695 q = alloc(sizeof(struct ioword), ap);
698 if (p->ioname != NULL)
699 q->ioname = wdcopy(p->ioname, ap);
700 if (p->delim != NULL)
701 q->delim = wdcopy(p->delim, ap);
702 if (p->heredoc != NULL)
703 strdupx(q->heredoc, p->heredoc, ap);
711 * free tree (for function definition)
714 tfree(struct op *t, Area *ap)
723 if (t->vars != NULL) {
724 for (w = t->vars; *w != NULL; w++)
729 if (t->args != NULL) {
730 /*XXX we assume the caller is right */
731 union mksh_ccphack cw;
734 for (w = cw.rw; *w != NULL; w++)
739 if (t->ioact != NULL)
740 iofree(t->ioact, ap);
749 iofree(struct ioword **iow, Area *ap)
755 while ((p = *iop++) != NULL) {
756 afree(p->ioname, ap);
758 afree(p->heredoc, ap);
765 fpFUNCTf(struct shf *shf, int i, bool isksh, const char *k, struct op *v)
768 fptreef(shf, i, "%s %s %T", Tfunction, k, v);
769 else if (ktsearch(&keywords, k, hash(k)))
770 fptreef(shf, i, "%s %s() %T", Tfunction, k, v);
772 fptreef(shf, i, "%s() %T", k, v);
778 vistree(char *dst, size_t sz, struct op *t)
784 buf = alloc(sz + 16, ATEMP);
785 snptreef(buf, sz + 16, Tf_T, t);
788 if (UTFMODE && (n = utf_mbtowc(&c, cp)) != (size_t)-1) {
789 if (c == 0 || n >= sz)
790 /* NUL or not enough free space */
792 /* copy multibyte char */
798 if (--sz == 0 || (c = (unsigned char)(*cp++)) == 0)
799 /* NUL or not enough free space */
801 if (ISCTRL(c & 0x7F)) {
802 /* C0 or C1 control character or DEL */
804 /* not enough free space for two chars */
806 *dst++ = (c & 0x80) ? '$' : '^';
807 c = UNCTRL(c & 0x7F);
808 } else if (UTFMODE && c > 0x7F) {
809 /* better not try to display broken multibyte chars */
810 /* also go easy on the Unicode: no U+FFFD here */
823 dumpchar(struct shf *shf, int c)
825 if (ISCTRL(c & 0x7F)) {
826 /* C0 or C1 control character or DEL */
827 shf_putc((c & 0x80) ? '$' : '^', shf);
828 c = UNCTRL(c & 0x7F);
835 dumpwdvar_i(struct shf *shf, const char *wp, int quotelevel)
839 while (/* CONSTCOND */ 1) {
842 shf_puts("EOS", shf);
845 if (*wp == /*{*/'}') {
846 shf_puts(/*{*/ "]ADELIM(})", shf);
849 shf_puts("ADELIM=", shf);
853 shf_puts("CHAR=", shf);
854 dumpchar(shf, *wp++);
857 shf_puts("QCHAR<", shf);
859 if (quotelevel == 0 ||
860 (c == '"' || c == '`' || c == '$' || c == '\\'))
865 shf_puts("COMASUB<", shf);
868 shf_puts("COMSUB<", shf);
870 while ((c = *wp++) != 0)
876 shf_puts("FUNASUB<", shf);
879 shf_puts("FUNSUB<", shf);
882 shf_puts("VALSUB<", shf);
885 shf_puts("EXPRSUB<", shf);
888 shf_fprintf(shf, "OQUOTE{%d" /*}*/, ++quotelevel);
891 shf_fprintf(shf, /*{*/ "%d}CQUOTE", quotelevel);
895 shf_puts("(err)", shf);
898 shf_puts("OSUBST(", shf);
899 dumpchar(shf, *wp++);
901 while ((c = *wp++) != 0)
904 wp = dumpwdvar_i(shf, wp, 0);
907 shf_puts("]CSUBST(", shf);
908 dumpchar(shf, *wp++);
912 shf_puts("OPAT=", shf);
913 dumpchar(shf, *wp++);
916 shf_puts("SPAT", shf);
919 shf_puts("CPAT", shf);
922 shf_fprintf(shf, "INVAL<%u>", (uint8_t)wp[-1]);
929 dumpwdvar(struct shf *shf, const char *wp)
931 dumpwdvar_i(shf, wp, 0);
935 dumpioact(struct shf *shf, struct op *t)
937 struct ioword **ioact, *iop;
939 if ((ioact = t->ioact) == NULL)
942 shf_puts("{IOACT", shf);
943 while ((iop = *ioact++) != NULL) {
944 unsigned short type = iop->ioflag & IOTYPE;
945 #define DT(x) case x: shf_puts(#x, shf); break;
946 #define DB(x) if (iop->ioflag & x) shf_puts("|" #x, shf);
957 shf_fprintf(shf, "unk%d", type);
967 shf_fprintf(shf, ",unit=%d", (int)iop->unit);
968 if (iop->delim && !(iop->ioflag & IONDELIM)) {
969 shf_puts(",delim<", shf);
970 dumpwdvar(shf, iop->delim);
974 if (iop->ioflag & IONAMEXP) {
975 shf_puts(",name=", shf);
976 print_value_quoted(shf, iop->ioname);
978 shf_puts(",name<", shf);
979 dumpwdvar(shf, iop->ioname);
984 shf_puts(",heredoc=", shf);
985 print_value_quoted(shf, iop->heredoc);
994 dumptree(struct shf *shf, struct op *t)
997 const char **w, *name;
1001 for (i = 0; i < nesting; ++i)
1002 shf_putc('\t', shf);
1004 shf_puts("{tree:" /*}*/, shf);
1011 #define OPEN(x) case x: name = #x; shf_puts(" {" #x ":", shf); /*}*/
1016 w = (const char **)t->vars;
1018 shf_putc('\n', shf);
1019 for (j = 0; j < nesting; ++j)
1020 shf_putc('\t', shf);
1021 shf_fprintf(shf, " var%d<", i++);
1022 dumpwdvar(shf, *w++);
1026 shf_puts(" #no-vars#", shf);
1031 shf_putc('\n', shf);
1032 for (j = 0; j < nesting; ++j)
1033 shf_putc('\t', shf);
1034 shf_fprintf(shf, " arg%d<", i++);
1035 dumpwdvar(shf, *w++);
1039 shf_puts(" #no-args#", shf);
1045 shf_putc('\n', shf);
1049 goto dumpleftandout;
1051 dumpleftmidrightandout:
1052 shf_putc('\n', shf);
1053 dumptree(shf, t->left);
1054 /* middumprightandout: (unused) */
1055 shf_fprintf(shf, "/%s:", name);
1060 goto dumpleftmidrightandout;
1062 goto dumpleftmidrightandout;
1064 goto dumpleftmidrightandout;
1066 goto dumprightandout;
1071 shf_putc('\n', shf);
1072 for (j = 0; j < nesting; ++j)
1073 shf_putc('\t', shf);
1074 shf_fprintf(shf, " arg%d<", i++);
1075 dumpwdvar(shf, *w++);
1081 shf_fprintf(shf, " str<%s>", t->str);
1082 if (t->vars != NULL) {
1084 w = (const char **)t->vars;
1086 shf_putc('\n', shf);
1087 for (j = 0; j < nesting; ++j)
1088 shf_putc('\t', shf);
1089 shf_fprintf(shf, " var%d<", i++);
1090 dumpwdvar(shf, *w++);
1094 goto dumpleftandout;
1098 shf_fprintf(shf, " str<%s>", t->str);
1100 for (t1 = t->left; t1 != NULL; t1 = t1->right) {
1101 shf_putc('\n', shf);
1102 for (j = 0; j < nesting; ++j)
1103 shf_putc('\t', shf);
1104 shf_fprintf(shf, " sub%d[(", i);
1105 w = (const char **)t1->vars;
1114 shf_putc('\n', shf);
1115 dumptree(shf, t1->left);
1116 shf_fprintf(shf, " ;%c/%d]", t1->u.charflag, i++);
1120 goto dumpleftmidrightandout;
1122 goto dumpleftmidrightandout;
1124 goto dumpleftandout;
1126 goto dumpleftandout;
1128 goto dumpleftandout;
1130 shf_fprintf(shf, " str<%s> ksh<%s>", t->str,
1131 t->u.ksh_func ? Ttrue : Tfalse);
1132 goto dumpleftandout;
1134 goto dumpleftandout;
1137 shf_putc('\n', shf);
1138 dumptree(shf, t->left);
1141 if (t->left != NULL) {
1142 shf_puts(" /TTHEN:\n", shf);
1143 dumptree(shf, t->left);
1145 if (t->right && t->right->type == TELIF) {
1146 shf_puts(" /TELIF:", shf);
1151 if (t->right != NULL) {
1152 shf_puts(" /TELSE:\n", shf);
1153 dumptree(shf, t->right);
1158 shf_puts(Tunexpected, shf);
1161 goto dumpunexpected;
1163 goto dumpunexpected;
1166 shf_fprintf(shf, "{T<%d>:" /*}*/, t->type);
1167 goto dumpunexpected;
1172 shf_fprintf(shf, /*{*/ " /%s}\n", name);