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