OSDN Git Service

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