1 /* $OpenBSD: tree.c,v 1.21 2015/09/01 13:12:31 tedu Exp $ */
2
3 /*-
4 * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
5 * 2011, 2012, 2013, 2015, 2016, 2017
6 * mirabilos <m@mirbsd.org>
7 *
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.
13 *
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.
22 */
23
24 #include "sh.h"
25
26 __RCSID("$MirOS: src/bin/mksh/tree.c,v 1.100 2020/10/31 04:28:54 tg Exp $");
27
28 #define INDENT 8
29
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 *);
36
37 /* "foo& ; bar" and "foo |& ; bar" are invalid */
38 static bool prevent_semicolon;
39
40 /* here document diversion */
41 static unsigned short ptree_nest;
42 static bool ptree_hashere;
43 static struct shf ptree_heredoc;
44 #define ptree_outhere(shf) do { \
45 if (ptree_hashere) { \
46 char *ptree_thehere; \
47 \
48 ptree_thehere = shf_sclose(&ptree_heredoc); \
49 shf_puts(ptree_thehere, (shf)); \
50 shf_putc('\n', (shf)); \
51 afree(ptree_thehere, ATEMP); \
52 ptree_hashere = false; \
53 /*prevent_semicolon = true;*/ \
54 } \
55 } while (/* CONSTCOND */ 0)
56
57 static const char Telif_pT[] = "elif %T";
58
59 /*
60 * print a command tree
61 */
62 static void
ptree(struct op * t,int indent,struct shf * shf)63 ptree(struct op *t, int indent, struct shf *shf)
64 {
65 const char **w;
66 struct ioword **ioact;
67 struct op *t1;
68 int i;
69 const char *ccp;
70
71 Chain:
72 if (t == NULL)
73 return;
74 switch (t->type) {
75 case TCOM:
76 prevent_semicolon = false;
77 /* special-case 'var=<<EOF' (cf. exec.c:execute) */
78 if (t->args &&
79 /* we have zero arguments, i.e. no program to run */
80 t->args[0] == NULL &&
81 /* we have exactly one variable assignment */
82 t->vars[0] != NULL && t->vars[1] == NULL &&
83 /* we have exactly one I/O redirection */
84 t->ioact != NULL && t->ioact[0] != NULL &&
85 t->ioact[1] == NULL &&
86 /* of type "here document" (or "here string") */
87 (t->ioact[0]->ioflag & IOTYPE) == IOHERE &&
88 /* the variable assignment begins with a valid varname */
89 (ccp = skip_wdvarname(t->vars[0], true)) != t->vars[0] &&
90 /* and has no right-hand side (i.e. "varname=") */
91 ccp[0] == CHAR && ((ccp[1] == '=' && ccp[2] == EOS) ||
92 /* or "varname+=" */ (ccp[1] == '+' && ccp[2] == CHAR &&
93 ccp[3] == '=' && ccp[4] == EOS))) {
94 fptreef(shf, indent, Tf_S, t->vars[0]);
95 break;
96 }
97
98 if (t->vars) {
99 w = (const char **)t->vars;
100 while (*w)
101 fptreef(shf, indent, Tf_S_, *w++);
102 }
103 #ifndef MKSH_SMALL
104 else
105 shf_puts("#no-vars# ", shf);
106 #endif
107 if (t->args) {
108 w = t->args;
109 if (*w && **w == CHAR) {
110 char *cp = wdstrip(*w++, WDS_TPUTS);
111
112 if (valid_alias_name(cp))
113 shf_putc('\\', shf);
114 shf_puts(cp, shf);
115 shf_putc(' ', shf);
116 afree(cp, ATEMP);
117 }
118 while (*w)
119 fptreef(shf, indent, Tf_S_, *w++);
120 }
121 #ifndef MKSH_SMALL
122 else
123 shf_puts("#no-args# ", shf);
124 #endif
125 break;
126 case TEXEC:
127 t = t->left;
128 goto Chain;
129 case TPAREN:
130 fptreef(shf, indent + 2, "( %T) ", t->left);
131 break;
132 case TPIPE:
133 fptreef(shf, indent, "%T| ", t->left);
134 t = t->right;
135 goto Chain;
136 case TLIST:
137 fptreef(shf, indent, "%T%;", t->left);
138 t = t->right;
139 goto Chain;
140 case TOR:
141 case TAND:
142 fptreef(shf, indent, "%T%s %T",
143 t->left, (t->type == TOR) ? "||" : "&&", t->right);
144 break;
145 case TBANG:
146 shf_puts("! ", shf);
147 prevent_semicolon = false;
148 t = t->right;
149 goto Chain;
150 case TDBRACKET:
151 w = t->args;
152 shf_puts("[[", shf);
153 while (*w)
154 fptreef(shf, indent, Tf__S, *w++);
155 shf_puts(" ]] ", shf);
156 break;
157 case TSELECT:
158 case TFOR:
159 fptreef(shf, indent, "%s %s ",
160 (t->type == TFOR) ? "for" : Tselect, t->str);
161 if (t->vars != NULL) {
162 shf_puts("in ", shf);
163 w = (const char **)t->vars;
164 while (*w)
165 fptreef(shf, indent, Tf_S_, *w++);
166 fptreef(shf, indent, Tft_end);
167 }
168 fptreef(shf, indent + INDENT, "do%N%T", t->left);
169 fptreef(shf, indent, "%;done ");
170 break;
171 case TCASE:
172 fptreef(shf, indent, "case %S in", t->str);
173 for (t1 = t->left; t1 != NULL; t1 = t1->right) {
174 fptreef(shf, indent, "%N(");
175 w = (const char **)t1->vars;
176 while (*w) {
177 fptreef(shf, indent, "%S%c", *w,
178 (w[1] != NULL) ? '|' : ')');
179 ++w;
180 }
181 fptreef(shf, indent + INDENT, "%N%T%N;%c", t1->left,
182 t1->u.charflag);
183 }
184 fptreef(shf, indent, "%Nesac ");
185 break;
186 case TELIF:
187 internal_errorf(TELIF_unexpected);
188 /* FALLTHROUGH */
189 case TIF:
190 i = 2;
191 t1 = t;
192 goto process_TIF;
193 do {
194 t1 = t1->right;
195 i = 0;
196 fptreef(shf, indent, Tft_end);
197 process_TIF:
198 /* 5 == strlen("elif ") */
199 fptreef(shf, indent + 5 - i, Telif_pT + i, t1->left);
200 t1 = t1->right;
201 if (t1->left != NULL) {
202 fptreef(shf, indent, Tft_end);
203 fptreef(shf, indent + INDENT, "%s%N%T",
204 "then", t1->left);
205 }
206 } while (t1->right && t1->right->type == TELIF);
207 if (t1->right != NULL) {
208 fptreef(shf, indent, Tft_end);
209 fptreef(shf, indent + INDENT, "%s%N%T",
210 "else", t1->right);
211 }
212 fptreef(shf, indent, "%;fi ");
213 break;
214 case TWHILE:
215 case TUNTIL:
216 /* 6 == strlen("while "/"until ") */
217 fptreef(shf, indent + 6, Tf_s_T,
218 (t->type == TWHILE) ? "while" : "until",
219 t->left);
220 fptreef(shf, indent, Tft_end);
221 fptreef(shf, indent + INDENT, "do%N%T", t->right);
222 fptreef(shf, indent, "%;done ");
223 break;
224 case TBRACE:
225 fptreef(shf, indent + INDENT, "{%N%T", t->left);
226 fptreef(shf, indent, "%;} ");
227 break;
228 case TCOPROC:
229 fptreef(shf, indent, "%T|& ", t->left);
230 prevent_semicolon = true;
231 break;
232 case TASYNC:
233 fptreef(shf, indent, "%T& ", t->left);
234 prevent_semicolon = true;
235 break;
236 case TFUNCT:
237 fpFUNCTf(shf, indent, tobool(t->u.ksh_func), t->str, t->left);
238 break;
239 case TTIME:
240 fptreef(shf, indent, Tf_s_T, Ttime, t->left);
241 break;
242 default:
243 shf_puts("<botch>", shf);
244 prevent_semicolon = false;
245 break;
246 }
247 if ((ioact = t->ioact) != NULL)
248 while (*ioact != NULL)
249 pioact(shf, *ioact++);
250 }
251
252 static void
pioact(struct shf * shf,struct ioword * iop)253 pioact(struct shf *shf, struct ioword *iop)
254 {
255 unsigned short flag = iop->ioflag;
256 unsigned short type = flag & IOTYPE;
257 short expected;
258
259 expected = (type == IOREAD || type == IORDWR || type == IOHERE) ? 0 :
260 (type == IOCAT || type == IOWRITE) ? 1 :
261 (type == IODUP && (iop->unit == !(flag & IORDUP))) ? iop->unit :
262 iop->unit + 1;
263 if (iop->unit != expected)
264 shf_fprintf(shf, Tf_d, (int)iop->unit);
265
266 switch (type) {
267 case IOREAD:
268 shf_putc('<', shf);
269 break;
270 case IOHERE:
271 if (flag & IOHERESTR) {
272 shf_puts("<<<", shf);
273 goto ioheredelim;
274 }
275 shf_puts("<<", shf);
276 if (flag & IOSKIP)
277 shf_putc('-', shf);
278 if (iop->heredoc /* nil when tracing */) {
279 /* here document diversion */
280 if (!ptree_hashere) {
281 shf_sopen(NULL, 0, SHF_WR | SHF_DYNAMIC,
282 &ptree_heredoc);
283 ptree_hashere = true;
284 }
285 shf_putc('\n', &ptree_heredoc);
286 shf_puts(iop->heredoc, &ptree_heredoc);
287 /* iop->delim is set before iop->heredoc */
288 shf_puts(evalstr(iop->delim, 0), &ptree_heredoc);
289 }
290 ioheredelim:
291 /* delim is NULL during syntax error printing */
292 if (iop->delim && !(iop->ioflag & IONDELIM))
293 wdvarput(shf, iop->delim, 0, WDS_TPUTS);
294 break;
295 case IOCAT:
296 shf_puts(">>", shf);
297 break;
298 case IOWRITE:
299 shf_putc('>', shf);
300 if (flag & IOCLOB)
301 shf_putc('|', shf);
302 break;
303 case IORDWR:
304 shf_puts("<>", shf);
305 break;
306 case IODUP:
307 shf_puts(flag & IORDUP ? "<&" : ">&", shf);
308 break;
309 }
310 /* name is NULL for IOHERE or when printing syntax errors */
311 if (iop->ioname) {
312 if (flag & IONAMEXP)
313 print_value_quoted(shf, iop->ioname);
314 else
315 wdvarput(shf, iop->ioname, 0, WDS_TPUTS);
316 }
317 shf_putc(' ', shf);
318 prevent_semicolon = false;
319 }
320
321 /* variant of fputs for ptreef and wdstrip */
322 static const char *
wdvarput(struct shf * shf,const char * wp,int quotelevel,int opmode)323 wdvarput(struct shf *shf, const char *wp, int quotelevel, int opmode)
324 {
325 int c;
326 const char *cs;
327
328 /*-
329 * problems:
330 * `...` -> $(...)
331 * 'foo' -> "foo"
332 * x${foo:-"hi"} -> x${foo:-hi} unless WDS_TPUTS
333 * x${foo:-'hi'} -> x${foo:-hi}
334 * could change encoding to:
335 * OQUOTE ["'] ... CQUOTE ["']
336 * COMSUB [(`] ...\0 (handle $ ` \ and maybe " in `...` case)
337 */
338 while (/* CONSTCOND */ 1)
339 switch (*wp++) {
340 case EOS:
341 return (--wp);
342 case ADELIM:
343 if (ord(*wp) == ORD(/*{*/ '}')) {
344 ++wp;
345 goto wdvarput_csubst;
346 }
347 /* FALLTHROUGH */
348 case CHAR:
349 c = ord(*wp++);
350 shf_putc(c, shf);
351 break;
352 case QCHAR:
353 c = ord(*wp++);
354 if (opmode & WDS_TPUTS)
355 switch (c) {
356 default:
357 if (quotelevel == 0)
358 /* FALLTHROUGH */
359 case ORD('"'):
360 case ORD('`'):
361 case ORD('$'):
362 case ORD('\\'):
363 shf_putc(ORD('\\'), shf);
364 break;
365 }
366 shf_putc(c, shf);
367 break;
368 case COMASUB:
369 case COMSUB:
370 shf_puts("$(", shf);
371 cs = ")";
372 if (ord(*wp) == ORD('(' /*)*/))
373 shf_putc(' ', shf);
374 pSUB:
375 while ((c = *wp++) != 0)
376 shf_putc(c, shf);
377 shf_puts(cs, shf);
378 break;
379 case FUNASUB:
380 case FUNSUB:
381 c = ORD(' ');
382 if (0)
383 /* FALLTHROUGH */
384 case VALSUB:
385 c = ORD('|');
386 shf_putc('$', shf);
387 shf_putc('{', shf);
388 shf_putc(c, shf);
389 cs = ";}";
390 goto pSUB;
391 case EXPRSUB:
392 shf_puts("$((", shf);
393 cs = "))";
394 goto pSUB;
395 case OQUOTE:
396 if (opmode & WDS_TPUTS) {
397 quotelevel++;
398 shf_putc('"', shf);
399 }
400 break;
401 case CQUOTE:
402 if (opmode & WDS_TPUTS) {
403 if (quotelevel)
404 quotelevel--;
405 shf_putc('"', shf);
406 }
407 break;
408 case OSUBST:
409 shf_putc('$', shf);
410 if (ord(*wp++) == ORD('{'))
411 shf_putc('{', shf);
412 while ((c = *wp++) != 0)
413 shf_putc(c, shf);
414 wp = wdvarput(shf, wp, 0, opmode);
415 break;
416 case CSUBST:
417 if (ord(*wp++) == ORD('}')) {
418 wdvarput_csubst:
419 shf_putc('}', shf);
420 }
421 return (wp);
422 case OPAT:
423 shf_putchar(*wp++, shf);
424 shf_putc('(', shf);
425 break;
426 case SPAT:
427 c = ORD('|');
428 if (0)
429 /* FALLTHROUGH */
430 case CPAT:
431 c = ORD(/*(*/ ')');
432 shf_putc(c, shf);
433 break;
434 }
435 }
436
437 /*
438 * this is the _only_ way to reliably handle
439 * variable args with an ANSI compiler
440 */
441 /* VARARGS */
442 void
fptreef(struct shf * shf,int indent,const char * fmt,...)443 fptreef(struct shf *shf, int indent, const char *fmt, ...)
444 {
445 va_list va;
446
447 va_start(va, fmt);
448 vfptreef(shf, indent, fmt, va);
449 va_end(va);
450 }
451
452 /* VARARGS */
453 char *
snptreef(char * s,ssize_t n,const char * fmt,...)454 snptreef(char *s, ssize_t n, const char *fmt, ...)
455 {
456 va_list va;
457 struct shf shf;
458
459 shf_sopen(s, n, SHF_WR | (s ? 0 : SHF_DYNAMIC), &shf);
460
461 va_start(va, fmt);
462 vfptreef(&shf, 0, fmt, va);
463 va_end(va);
464
465 /* shf_sclose NUL terminates */
466 return (shf_sclose(&shf));
467 }
468
469 static void
vfptreef(struct shf * shf,int indent,const char * fmt,va_list va)470 vfptreef(struct shf *shf, int indent, const char *fmt, va_list va)
471 {
472 int c;
473
474 if (!ptree_nest++)
475 ptree_hashere = false;
476
477 while ((c = ord(*fmt++))) {
478 if (c == '%') {
479 switch ((c = ord(*fmt++))) {
480 case ORD('c'):
481 /* character (octet, probably) */
482 shf_putchar(va_arg(va, int), shf);
483 break;
484 case ORD('s'):
485 /* string */
486 shf_puts(va_arg(va, char *), shf);
487 break;
488 case ORD('S'):
489 /* word */
490 wdvarput(shf, va_arg(va, char *), 0, WDS_TPUTS);
491 break;
492 case ORD('d'):
493 /* signed decimal */
494 shf_fprintf(shf, Tf_d, va_arg(va, int));
495 break;
496 case ORD('u'):
497 /* unsigned decimal */
498 shf_fprintf(shf, "%u", va_arg(va, unsigned int));
499 break;
500 case ORD('T'):
501 /* format tree */
502 ptree(va_arg(va, struct op *), indent, shf);
503 goto dont_trash_prevent_semicolon;
504 case ORD(';'):
505 /* newline or ; */
506 case ORD('N'):
507 /* newline or space */
508 if (shf->flags & SHF_STRING) {
509 if ((unsigned int)c == ORD(';') &&
510 !prevent_semicolon)
511 shf_putc(';', shf);
512 shf_putc(' ', shf);
513 } else {
514 int i = indent;
515
516 ptree_outhere(shf);
517 shf_putc('\n', shf);
518 while (i >= 8) {
519 shf_putc('\t', shf);
520 i -= 8;
521 }
522 while (i--)
523 shf_putc(' ', shf);
524 }
525 break;
526 case ORD('R'):
527 /* I/O redirection */
528 pioact(shf, va_arg(va, struct ioword *));
529 break;
530 default:
531 shf_putc(c, shf);
532 break;
533 }
534 } else
535 shf_putc(c, shf);
536 prevent_semicolon = false;
537 dont_trash_prevent_semicolon:
538 ;
539 }
540
541 if (!--ptree_nest)
542 ptree_outhere(shf);
543 }
544
545 /*
546 * copy tree (for function definition)
547 */
548 struct op *
tcopy(struct op * t,Area * ap)549 tcopy(struct op *t, Area *ap)
550 {
551 struct op *r;
552 const char **tw;
553 char **rw;
554
555 if (t == NULL)
556 return (NULL);
557
558 r = alloc(sizeof(struct op), ap);
559
560 r->type = t->type;
561 r->u.evalflags = t->u.evalflags;
562
563 if (t->type == TCASE)
564 r->str = wdcopy(t->str, ap);
565 else
566 strdupx(r->str, t->str, ap);
567
568 if (t->vars == NULL)
569 r->vars = NULL;
570 else {
571 tw = (const char **)t->vars;
572 while (*tw)
573 ++tw;
574 rw = r->vars = alloc2(tw - (const char **)t->vars + 1,
575 sizeof(*tw), ap);
576 tw = (const char **)t->vars;
577 while (*tw)
578 *rw++ = wdcopy(*tw++, ap);
579 *rw = NULL;
580 }
581
582 if (t->args == NULL)
583 r->args = NULL;
584 else {
585 tw = t->args;
586 while (*tw)
587 ++tw;
588 r->args = (const char **)(rw = alloc2(tw - t->args + 1,
589 sizeof(*tw), ap));
590 tw = t->args;
591 while (*tw)
592 *rw++ = wdcopy(*tw++, ap);
593 *rw = NULL;
594 }
595
596 r->ioact = (t->ioact == NULL) ? NULL : iocopy(t->ioact, ap);
597
598 r->left = tcopy(t->left, ap);
599 r->right = tcopy(t->right, ap);
600 r->lineno = t->lineno;
601
602 return (r);
603 }
604
605 char *
wdcopy(const char * wp,Area * ap)606 wdcopy(const char *wp, Area *ap)
607 {
608 size_t len;
609
610 len = wdscan(wp, EOS) - wp;
611 return (memcpy(alloc(len, ap), wp, len));
612 }
613
614 /* return the position of prefix c in wp plus 1 */
615 const char *
wdscan(const char * wp,int c)616 wdscan(const char *wp, int c)
617 {
618 int nest = 0;
619
620 while (/* CONSTCOND */ 1)
621 switch (*wp++) {
622 case EOS:
623 return (wp);
624 case ADELIM:
625 if (c == ADELIM && nest == 0)
626 return (wp + 1);
627 if (ord(*wp) == ORD(/*{*/ '}'))
628 goto wdscan_csubst;
629 /* FALLTHROUGH */
630 case CHAR:
631 case QCHAR:
632 wp++;
633 break;
634 case COMASUB:
635 case COMSUB:
636 case FUNASUB:
637 case FUNSUB:
638 case VALSUB:
639 case EXPRSUB:
640 while (*wp++ != 0)
641 ;
642 break;
643 case OQUOTE:
644 case CQUOTE:
645 break;
646 case OSUBST:
647 nest++;
648 while (*wp++ != '\0')
649 ;
650 break;
651 case CSUBST:
652 wdscan_csubst:
653 wp++;
654 if (c == CSUBST && nest == 0)
655 return (wp);
656 nest--;
657 break;
658 case OPAT:
659 nest++;
660 wp++;
661 break;
662 case SPAT:
663 case CPAT:
664 if (c == wp[-1] && nest == 0)
665 return (wp);
666 if (wp[-1] == CPAT)
667 nest--;
668 break;
669 default:
670 internal_warningf(
671 "wdscan: unknown char 0x%X (carrying on)",
672 (unsigned char)wp[-1]);
673 }
674 }
675
676 /*
677 * return a copy of wp without any of the mark up characters and with
678 * quote characters (" ' \) stripped. (string is allocated from ATEMP)
679 */
680 char *
wdstrip(const char * wp,int opmode)681 wdstrip(const char *wp, int opmode)
682 {
683 struct shf shf;
684
685 shf_sopen(NULL, 32, SHF_WR | SHF_DYNAMIC, &shf);
686 wdvarput(&shf, wp, 0, opmode);
687 /* shf_sclose NUL terminates */
688 return (shf_sclose(&shf));
689 }
690
691 static struct ioword **
iocopy(struct ioword ** iow,Area * ap)692 iocopy(struct ioword **iow, Area *ap)
693 {
694 struct ioword **ior;
695 int i;
696
697 ior = iow;
698 while (*ior)
699 ++ior;
700 ior = alloc2(ior - iow + 1, sizeof(struct ioword *), ap);
701
702 for (i = 0; iow[i] != NULL; i++) {
703 struct ioword *p, *q;
704
705 p = iow[i];
706 q = alloc(sizeof(struct ioword), ap);
707 ior[i] = q;
708 *q = *p;
709 if (p->ioname != NULL)
710 q->ioname = wdcopy(p->ioname, ap);
711 if (p->delim != NULL)
712 q->delim = wdcopy(p->delim, ap);
713 if (p->heredoc != NULL)
714 strdupx(q->heredoc, p->heredoc, ap);
715 }
716 ior[i] = NULL;
717
718 return (ior);
719 }
720
721 /*
722 * free tree (for function definition)
723 */
724 void
tfree(struct op * t,Area * ap)725 tfree(struct op *t, Area *ap)
726 {
727 char **w;
728
729 if (t == NULL)
730 return;
731
732 afree(t->str, ap);
733
734 if (t->vars != NULL) {
735 for (w = t->vars; *w != NULL; w++)
736 afree(*w, ap);
737 afree(t->vars, ap);
738 }
739
740 if (t->args != NULL) {
741 /*XXX we assume the caller is right */
742 union mksh_ccphack cw;
743
744 cw.ro = t->args;
745 for (w = cw.rw; *w != NULL; w++)
746 afree(*w, ap);
747 afree(t->args, ap);
748 }
749
750 if (t->ioact != NULL)
751 iofree(t->ioact, ap);
752
753 tfree(t->left, ap);
754 tfree(t->right, ap);
755
756 afree(t, ap);
757 }
758
759 static void
iofree(struct ioword ** iow,Area * ap)760 iofree(struct ioword **iow, Area *ap)
761 {
762 struct ioword **iop;
763 struct ioword *p;
764
765 iop = iow;
766 while ((p = *iop++) != NULL) {
767 afree(p->ioname, ap);
768 afree(p->delim, ap);
769 afree(p->heredoc, ap);
770 afree(p, ap);
771 }
772 afree(iow, ap);
773 }
774
775 void
fpFUNCTf(struct shf * shf,int i,bool isksh,const char * k,struct op * v)776 fpFUNCTf(struct shf *shf, int i, bool isksh, const char *k, struct op *v)
777 {
778 if (isksh)
779 fptreef(shf, i, "%s %s %T", Tfunction, k, v);
780 else if (ktsearch(&keywords, k, hash(k)))
781 fptreef(shf, i, "%s %s() %T", Tfunction, k, v);
782 else
783 fptreef(shf, i, "%s() %T", k, v);
784 }
785
786
787 /* for jobs.c */
788 void
vistree(char * dst,size_t sz,struct op * t)789 vistree(char *dst, size_t sz, struct op *t)
790 {
791 unsigned int c;
792 char *cp, *buf;
793 size_t n;
794
795 buf = alloc(sz + 16, ATEMP);
796 snptreef(buf, sz + 16, Tf_T, t);
797 cp = buf;
798 vist_loop:
799 if (UTFMODE && (n = utf_mbtowc(&c, cp)) != (size_t)-1) {
800 if (c == 0 || n >= sz)
801 /* NUL or not enough free space */
802 goto vist_out;
803 /* copy multibyte char */
804 sz -= n;
805 while (n--)
806 *dst++ = *cp++;
807 goto vist_loop;
808 }
809 if (--sz == 0 || (c = ord(*cp++)) == 0)
810 /* NUL or not enough free space */
811 goto vist_out;
812 if (ksh_isctrl(c)) {
813 /* C0 or C1 control character or DEL */
814 if (--sz == 0)
815 /* not enough free space for two chars */
816 goto vist_out;
817 *dst++ = '^';
818 c = ksh_unctrl(c);
819 } else if (UTFMODE && rtt2asc(c) > 0x7F) {
820 /* better not try to display broken multibyte chars */
821 /* also go easy on the UCS: no U+FFFD here */
822 c = ORD('?');
823 }
824 *dst++ = c;
825 goto vist_loop;
826
827 vist_out:
828 *dst = '\0';
829 afree(buf, ATEMP);
830 }
831
832 #ifdef DEBUG
833 void
dumpchar(struct shf * shf,unsigned char c)834 dumpchar(struct shf *shf, unsigned char c)
835 {
836 if (ksh_isctrl(c)) {
837 /* C0 or C1 control character or DEL */
838 shf_putc('^', shf);
839 c = ksh_unctrl(c);
840 }
841 shf_putc(c, shf);
842 }
843
844 /* see: wdvarput */
845 static const char *
dumpwdvar_i(struct shf * shf,const char * wp,int quotelevel)846 dumpwdvar_i(struct shf *shf, const char *wp, int quotelevel)
847 {
848 int c;
849
850 while (/* CONSTCOND */ 1) {
851 switch(*wp++) {
852 case EOS:
853 shf_puts("EOS", shf);
854 return (--wp);
855 case ADELIM:
856 if (ord(*wp) == ORD(/*{*/ '}')) {
857 shf_puts(/*{*/ "]ADELIM(})", shf);
858 return (wp + 1);
859 }
860 shf_puts("ADELIM=", shf);
861 if (0)
862 /* FALLTHROUGH */
863 case CHAR:
864 shf_puts("CHAR=", shf);
865 dumpchar(shf, *wp++);
866 break;
867 case QCHAR:
868 shf_puts("QCHAR<", shf);
869 c = ord(*wp++);
870 if (quotelevel == 0 || c == ORD('"') ||
871 c == ORD('\\') || ctype(c, C_DOLAR | C_GRAVE))
872 shf_putc('\\', shf);
873 dumpchar(shf, c);
874 goto closeandout;
875 case COMASUB:
876 shf_puts("COMASUB<", shf);
877 goto dumpsub;
878 case COMSUB:
879 shf_puts("COMSUB<", shf);
880 dumpsub:
881 while ((c = *wp++) != 0)
882 dumpchar(shf, c);
883 closeandout:
884 shf_putc('>', shf);
885 break;
886 case FUNASUB:
887 shf_puts("FUNASUB<", shf);
888 goto dumpsub;
889 case FUNSUB:
890 shf_puts("FUNSUB<", shf);
891 goto dumpsub;
892 case VALSUB:
893 shf_puts("VALSUB<", shf);
894 goto dumpsub;
895 case EXPRSUB:
896 shf_puts("EXPRSUB<", shf);
897 goto dumpsub;
898 case OQUOTE:
899 shf_fprintf(shf, "OQUOTE{%d" /*}*/, ++quotelevel);
900 break;
901 case CQUOTE:
902 shf_fprintf(shf, /*{*/ "%d}CQUOTE", quotelevel);
903 if (quotelevel)
904 quotelevel--;
905 else
906 shf_puts("(err)", shf);
907 break;
908 case OSUBST:
909 shf_puts("OSUBST(", shf);
910 dumpchar(shf, *wp++);
911 shf_puts(")[", shf);
912 while ((c = *wp++) != 0)
913 dumpchar(shf, c);
914 shf_putc('|', shf);
915 wp = dumpwdvar_i(shf, wp, 0);
916 break;
917 case CSUBST:
918 shf_puts("]CSUBST(", shf);
919 dumpchar(shf, *wp++);
920 shf_putc(')', shf);
921 return (wp);
922 case OPAT:
923 shf_puts("OPAT=", shf);
924 dumpchar(shf, *wp++);
925 break;
926 case SPAT:
927 shf_puts("SPAT", shf);
928 break;
929 case CPAT:
930 shf_puts("CPAT", shf);
931 break;
932 default:
933 shf_fprintf(shf, "INVAL<%u>", (uint8_t)wp[-1]);
934 break;
935 }
936 shf_putc(' ', shf);
937 }
938 }
939 void
dumpwdvar(struct shf * shf,const char * wp)940 dumpwdvar(struct shf *shf, const char *wp)
941 {
942 dumpwdvar_i(shf, wp, 0);
943 }
944
945 void
dumpioact(struct shf * shf,struct op * t)946 dumpioact(struct shf *shf, struct op *t)
947 {
948 struct ioword **ioact, *iop;
949
950 if ((ioact = t->ioact) == NULL)
951 return;
952
953 shf_puts("{IOACT", shf);
954 while ((iop = *ioact++) != NULL) {
955 unsigned short type = iop->ioflag & IOTYPE;
956 #define DT(x) case x: shf_puts(#x, shf); break;
957 #define DB(x) if (iop->ioflag & x) shf_puts("|" #x, shf);
958
959 shf_putc(';', shf);
960 switch (type) {
961 DT(IOREAD)
962 DT(IOWRITE)
963 DT(IORDWR)
964 DT(IOHERE)
965 DT(IOCAT)
966 DT(IODUP)
967 default:
968 shf_fprintf(shf, "unk%d", type);
969 }
970 DB(IOEVAL)
971 DB(IOSKIP)
972 DB(IOCLOB)
973 DB(IORDUP)
974 DB(IONAMEXP)
975 DB(IOBASH)
976 DB(IOHERESTR)
977 DB(IONDELIM)
978 shf_fprintf(shf, ",unit=%d", (int)iop->unit);
979 if (iop->delim && !(iop->ioflag & IONDELIM)) {
980 shf_puts(",delim<", shf);
981 dumpwdvar(shf, iop->delim);
982 shf_putc('>', shf);
983 }
984 if (iop->ioname) {
985 if (iop->ioflag & IONAMEXP) {
986 shf_puts(",name=", shf);
987 print_value_quoted(shf, iop->ioname);
988 } else {
989 shf_puts(",name<", shf);
990 dumpwdvar(shf, iop->ioname);
991 shf_putc('>', shf);
992 }
993 }
994 if (iop->heredoc) {
995 shf_puts(",heredoc=", shf);
996 print_value_quoted(shf, iop->heredoc);
997 }
998 #undef DT
999 #undef DB
1000 }
1001 shf_putc('}', shf);
1002 }
1003
1004 void
dumptree(struct shf * shf,struct op * t)1005 dumptree(struct shf *shf, struct op *t)
1006 {
1007 int i, j;
1008 const char **w, *name;
1009 struct op *t1;
1010 static int nesting;
1011
1012 for (i = 0; i < nesting; ++i)
1013 shf_putc('\t', shf);
1014 ++nesting;
1015 shf_puts("{tree:" /*}*/, shf);
1016 if (t == NULL) {
1017 name = "(null)";
1018 goto out;
1019 }
1020 dumpioact(shf, t);
1021 switch (t->type) {
1022 #define OPEN(x) case x: name = #x; shf_puts(" {" #x ":", shf); /*}*/
1023
1024 OPEN(TCOM)
1025 if (t->vars) {
1026 i = 0;
1027 w = (const char **)t->vars;
1028 while (*w) {
1029 shf_putc('\n', shf);
1030 for (j = 0; j < nesting; ++j)
1031 shf_putc('\t', shf);
1032 shf_fprintf(shf, " var%d<", i++);
1033 dumpwdvar(shf, *w++);
1034 shf_putc('>', shf);
1035 }
1036 } else
1037 shf_puts(" #no-vars#", shf);
1038 if (t->args) {
1039 i = 0;
1040 w = t->args;
1041 while (*w) {
1042 shf_putc('\n', shf);
1043 for (j = 0; j < nesting; ++j)
1044 shf_putc('\t', shf);
1045 shf_fprintf(shf, " arg%d<", i++);
1046 dumpwdvar(shf, *w++);
1047 shf_putc('>', shf);
1048 }
1049 } else
1050 shf_puts(" #no-args#", shf);
1051 break;
1052 OPEN(TEXEC)
1053 dumpleftandout:
1054 t = t->left;
1055 dumpandout:
1056 shf_putc('\n', shf);
1057 dumptree(shf, t);
1058 break;
1059 OPEN(TPAREN)
1060 goto dumpleftandout;
1061 OPEN(TPIPE)
1062 dumpleftmidrightandout:
1063 shf_putc('\n', shf);
1064 dumptree(shf, t->left);
1065 /* middumprightandout: (unused) */
1066 shf_fprintf(shf, "/%s:", name);
1067 dumprightandout:
1068 t = t->right;
1069 goto dumpandout;
1070 OPEN(TLIST)
1071 goto dumpleftmidrightandout;
1072 OPEN(TOR)
1073 goto dumpleftmidrightandout;
1074 OPEN(TAND)
1075 goto dumpleftmidrightandout;
1076 OPEN(TBANG)
1077 goto dumprightandout;
1078 OPEN(TDBRACKET)
1079 i = 0;
1080 w = t->args;
1081 while (*w) {
1082 shf_putc('\n', shf);
1083 for (j = 0; j < nesting; ++j)
1084 shf_putc('\t', shf);
1085 shf_fprintf(shf, " arg%d<", i++);
1086 dumpwdvar(shf, *w++);
1087 shf_putc('>', shf);
1088 }
1089 break;
1090 OPEN(TFOR)
1091 dumpfor:
1092 shf_fprintf(shf, " str<%s>", t->str);
1093 if (t->vars != NULL) {
1094 i = 0;
1095 w = (const char **)t->vars;
1096 while (*w) {
1097 shf_putc('\n', shf);
1098 for (j = 0; j < nesting; ++j)
1099 shf_putc('\t', shf);
1100 shf_fprintf(shf, " var%d<", i++);
1101 dumpwdvar(shf, *w++);
1102 shf_putc('>', shf);
1103 }
1104 }
1105 goto dumpleftandout;
1106 OPEN(TSELECT)
1107 goto dumpfor;
1108 OPEN(TCASE)
1109 shf_fprintf(shf, " str<%s>", t->str);
1110 i = 0;
1111 for (t1 = t->left; t1 != NULL; t1 = t1->right) {
1112 shf_putc('\n', shf);
1113 for (j = 0; j < nesting; ++j)
1114 shf_putc('\t', shf);
1115 shf_fprintf(shf, " sub%d[(", i);
1116 w = (const char **)t1->vars;
1117 while (*w) {
1118 dumpwdvar(shf, *w);
1119 if (w[1] != NULL)
1120 shf_putc('|', shf);
1121 ++w;
1122 }
1123 shf_putc(')', shf);
1124 dumpioact(shf, t);
1125 shf_putc('\n', shf);
1126 dumptree(shf, t1->left);
1127 shf_fprintf(shf, " ;%c/%d]", t1->u.charflag, i++);
1128 }
1129 break;
1130 OPEN(TWHILE)
1131 goto dumpleftmidrightandout;
1132 OPEN(TUNTIL)
1133 goto dumpleftmidrightandout;
1134 OPEN(TBRACE)
1135 goto dumpleftandout;
1136 OPEN(TCOPROC)
1137 goto dumpleftandout;
1138 OPEN(TASYNC)
1139 goto dumpleftandout;
1140 OPEN(TFUNCT)
1141 shf_fprintf(shf, " str<%s> ksh<%s>", t->str,
1142 t->u.ksh_func ? Ttrue : Tfalse);
1143 goto dumpleftandout;
1144 OPEN(TTIME)
1145 goto dumpleftandout;
1146 OPEN(TIF)
1147 dumpif:
1148 shf_putc('\n', shf);
1149 dumptree(shf, t->left);
1150 t = t->right;
1151 dumpioact(shf, t);
1152 if (t->left != NULL) {
1153 shf_puts(" /TTHEN:\n", shf);
1154 dumptree(shf, t->left);
1155 }
1156 if (t->right && t->right->type == TELIF) {
1157 shf_puts(" /TELIF:", shf);
1158 t = t->right;
1159 dumpioact(shf, t);
1160 goto dumpif;
1161 }
1162 if (t->right != NULL) {
1163 shf_puts(" /TELSE:\n", shf);
1164 dumptree(shf, t->right);
1165 }
1166 break;
1167 OPEN(TEOF)
1168 dumpunexpected:
1169 shf_puts(Tunexpected, shf);
1170 break;
1171 OPEN(TELIF)
1172 goto dumpunexpected;
1173 OPEN(TPAT)
1174 goto dumpunexpected;
1175 default:
1176 name = "TINVALID";
1177 shf_fprintf(shf, "{T<%d>:" /*}*/, t->type);
1178 goto dumpunexpected;
1179
1180 #undef OPEN
1181 }
1182 out:
1183 shf_fprintf(shf, /*{*/ " /%s}\n", name);
1184 --nesting;
1185 }
1186 #endif
1187