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