OSDN Git Service

Merge "Upgrade to mksh R56c."
[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.95 2018/01/14 00:03:05 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 (ord(*wp) == ORD(/*{*/ '}')) {
333                                 ++wp;
334                                 goto wdvarput_csubst;
335                         }
336                         /* FALLTHROUGH */
337                 case CHAR:
338                         c = ord(*wp++);
339                         shf_putc(c, shf);
340                         break;
341                 case QCHAR:
342                         c = ord(*wp++);
343                         if (opmode & WDS_TPUTS)
344                                 switch (c) {
345                                 case ORD('\n'):
346                                         if (quotelevel == 0) {
347                                                 c = ORD('\'');
348                                                 shf_putc(c, shf);
349                                                 shf_putc(ORD('\n'), shf);
350                                         }
351                                         break;
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
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 = ord(*fmt++))) {
471                 if (c == '%') {
472                         switch ((c = ord(*fmt++))) {
473                         case ORD('c'):
474                                 /* character (octet, probably) */
475                                 shf_putchar(va_arg(va, int), shf);
476                                 break;
477                         case ORD('s'):
478                                 /* string */
479                                 shf_puts(va_arg(va, char *), shf);
480                                 break;
481                         case ORD('S'):
482                                 /* word */
483                                 wdvarput(shf, va_arg(va, char *), 0, WDS_TPUTS);
484                                 break;
485                         case ORD('d'):
486                                 /* signed decimal */
487                                 shf_fprintf(shf, Tf_d, va_arg(va, int));
488                                 break;
489                         case ORD('u'):
490                                 /* unsigned decimal */
491                                 shf_fprintf(shf, "%u", va_arg(va, unsigned int));
492                                 break;
493                         case ORD('T'):
494                                 /* format tree */
495                                 ptree(va_arg(va, struct op *), indent, shf);
496                                 goto dont_trash_prevent_semicolon;
497                         case ORD(';'):
498                                 /* newline or ; */
499                         case ORD('N'):
500                                 /* newline or space */
501                                 if (shf->flags & SHF_STRING) {
502                                         if ((unsigned int)c == ORD(';') &&
503                                             !prevent_semicolon)
504                                                 shf_putc(';', shf);
505                                         shf_putc(' ', shf);
506                                 } else {
507                                         int i;
508
509                                         shf_putc('\n', shf);
510                                         i = indent;
511                                         while (i >= 8) {
512                                                 shf_putc('\t', shf);
513                                                 i -= 8;
514                                         }
515                                         while (i--)
516                                                 shf_putc(' ', shf);
517                                 }
518                                 break;
519                         case ORD('R'):
520                                 /* I/O redirection */
521                                 pioact(shf, va_arg(va, struct ioword *));
522                                 break;
523                         default:
524                                 shf_putc(c, shf);
525                                 break;
526                         }
527                 } else
528                         shf_putc(c, shf);
529                 prevent_semicolon = false;
530  dont_trash_prevent_semicolon:
531                 ;
532         }
533 }
534
535 /*
536  * copy tree (for function definition)
537  */
538 struct op *
539 tcopy(struct op *t, Area *ap)
540 {
541         struct op *r;
542         const char **tw;
543         char **rw;
544
545         if (t == NULL)
546                 return (NULL);
547
548         r = alloc(sizeof(struct op), ap);
549
550         r->type = t->type;
551         r->u.evalflags = t->u.evalflags;
552
553         if (t->type == TCASE)
554                 r->str = wdcopy(t->str, ap);
555         else
556                 strdupx(r->str, t->str, ap);
557
558         if (t->vars == NULL)
559                 r->vars = NULL;
560         else {
561                 tw = (const char **)t->vars;
562                 while (*tw)
563                         ++tw;
564                 rw = r->vars = alloc2(tw - (const char **)t->vars + 1,
565                     sizeof(*tw), ap);
566                 tw = (const char **)t->vars;
567                 while (*tw)
568                         *rw++ = wdcopy(*tw++, ap);
569                 *rw = NULL;
570         }
571
572         if (t->args == NULL)
573                 r->args = NULL;
574         else {
575                 tw = t->args;
576                 while (*tw)
577                         ++tw;
578                 r->args = (const char **)(rw = alloc2(tw - t->args + 1,
579                     sizeof(*tw), ap));
580                 tw = t->args;
581                 while (*tw)
582                         *rw++ = wdcopy(*tw++, ap);
583                 *rw = NULL;
584         }
585
586         r->ioact = (t->ioact == NULL) ? NULL : iocopy(t->ioact, ap);
587
588         r->left = tcopy(t->left, ap);
589         r->right = tcopy(t->right, ap);
590         r->lineno = t->lineno;
591
592         return (r);
593 }
594
595 char *
596 wdcopy(const char *wp, Area *ap)
597 {
598         size_t len;
599
600         len = wdscan(wp, EOS) - wp;
601         return (memcpy(alloc(len, ap), wp, len));
602 }
603
604 /* return the position of prefix c in wp plus 1 */
605 const char *
606 wdscan(const char *wp, int c)
607 {
608         int nest = 0;
609
610         while (/* CONSTCOND */ 1)
611                 switch (*wp++) {
612                 case EOS:
613                         return (wp);
614                 case ADELIM:
615                         if (c == ADELIM && nest == 0)
616                                 return (wp + 1);
617                         if (ord(*wp) == ORD(/*{*/ '}'))
618                                 goto wdscan_csubst;
619                         /* FALLTHROUGH */
620                 case CHAR:
621                 case QCHAR:
622                         wp++;
623                         break;
624                 case COMASUB:
625                 case COMSUB:
626                 case FUNASUB:
627                 case FUNSUB:
628                 case VALSUB:
629                 case EXPRSUB:
630                         while (*wp++ != 0)
631                                 ;
632                         break;
633                 case OQUOTE:
634                 case CQUOTE:
635                         break;
636                 case OSUBST:
637                         nest++;
638                         while (*wp++ != '\0')
639                                 ;
640                         break;
641                 case CSUBST:
642  wdscan_csubst:
643                         wp++;
644                         if (c == CSUBST && nest == 0)
645                                 return (wp);
646                         nest--;
647                         break;
648                 case OPAT:
649                         nest++;
650                         wp++;
651                         break;
652                 case SPAT:
653                 case CPAT:
654                         if (c == wp[-1] && nest == 0)
655                                 return (wp);
656                         if (wp[-1] == CPAT)
657                                 nest--;
658                         break;
659                 default:
660                         internal_warningf(
661                             "wdscan: unknown char 0x%X (carrying on)",
662                             (unsigned char)wp[-1]);
663                 }
664 }
665
666 /*
667  * return a copy of wp without any of the mark up characters and with
668  * quote characters (" ' \) stripped. (string is allocated from ATEMP)
669  */
670 char *
671 wdstrip(const char *wp, int opmode)
672 {
673         struct shf shf;
674
675         shf_sopen(NULL, 32, SHF_WR | SHF_DYNAMIC, &shf);
676         wdvarput(&shf, wp, 0, opmode);
677         /* shf_sclose NUL terminates */
678         return (shf_sclose(&shf));
679 }
680
681 static struct ioword **
682 iocopy(struct ioword **iow, Area *ap)
683 {
684         struct ioword **ior;
685         int i;
686
687         ior = iow;
688         while (*ior)
689                 ++ior;
690         ior = alloc2(ior - iow + 1, sizeof(struct ioword *), ap);
691
692         for (i = 0; iow[i] != NULL; i++) {
693                 struct ioword *p, *q;
694
695                 p = iow[i];
696                 q = alloc(sizeof(struct ioword), ap);
697                 ior[i] = q;
698                 *q = *p;
699                 if (p->ioname != NULL)
700                         q->ioname = wdcopy(p->ioname, ap);
701                 if (p->delim != NULL)
702                         q->delim = wdcopy(p->delim, ap);
703                 if (p->heredoc != NULL)
704                         strdupx(q->heredoc, p->heredoc, ap);
705         }
706         ior[i] = NULL;
707
708         return (ior);
709 }
710
711 /*
712  * free tree (for function definition)
713  */
714 void
715 tfree(struct op *t, Area *ap)
716 {
717         char **w;
718
719         if (t == NULL)
720                 return;
721
722         afree(t->str, ap);
723
724         if (t->vars != NULL) {
725                 for (w = t->vars; *w != NULL; w++)
726                         afree(*w, ap);
727                 afree(t->vars, ap);
728         }
729
730         if (t->args != NULL) {
731                 /*XXX we assume the caller is right */
732                 union mksh_ccphack cw;
733
734                 cw.ro = t->args;
735                 for (w = cw.rw; *w != NULL; w++)
736                         afree(*w, ap);
737                 afree(t->args, ap);
738         }
739
740         if (t->ioact != NULL)
741                 iofree(t->ioact, ap);
742
743         tfree(t->left, ap);
744         tfree(t->right, ap);
745
746         afree(t, ap);
747 }
748
749 static void
750 iofree(struct ioword **iow, Area *ap)
751 {
752         struct ioword **iop;
753         struct ioword *p;
754
755         iop = iow;
756         while ((p = *iop++) != NULL) {
757                 afree(p->ioname, ap);
758                 afree(p->delim, ap);
759                 afree(p->heredoc, ap);
760                 afree(p, ap);
761         }
762         afree(iow, ap);
763 }
764
765 void
766 fpFUNCTf(struct shf *shf, int i, bool isksh, const char *k, struct op *v)
767 {
768         if (isksh)
769                 fptreef(shf, i, "%s %s %T", Tfunction, k, v);
770         else if (ktsearch(&keywords, k, hash(k)))
771                 fptreef(shf, i, "%s %s() %T", Tfunction, k, v);
772         else
773                 fptreef(shf, i, "%s() %T", k, v);
774 }
775
776
777 /* for jobs.c */
778 void
779 vistree(char *dst, size_t sz, struct op *t)
780 {
781         unsigned int c;
782         char *cp, *buf;
783         size_t n;
784
785         buf = alloc(sz + 16, ATEMP);
786         snptreef(buf, sz + 16, Tf_T, t);
787         cp = buf;
788  vist_loop:
789         if (UTFMODE && (n = utf_mbtowc(&c, cp)) != (size_t)-1) {
790                 if (c == 0 || n >= sz)
791                         /* NUL or not enough free space */
792                         goto vist_out;
793                 /* copy multibyte char */
794                 sz -= n;
795                 while (n--)
796                         *dst++ = *cp++;
797                 goto vist_loop;
798         }
799         if (--sz == 0 || (c = ord(*cp++)) == 0)
800                 /* NUL or not enough free space */
801                 goto vist_out;
802         if (ksh_isctrl(c)) {
803                 /* C0 or C1 control character or DEL */
804                 if (--sz == 0)
805                         /* not enough free space for two chars */
806                         goto vist_out;
807                 *dst++ = '^';
808                 c = ksh_unctrl(c);
809         } else if (UTFMODE && rtt2asc(c) > 0x7F) {
810                 /* better not try to display broken multibyte chars */
811                 /* also go easy on the Unicode: no U+FFFD here */
812                 c = ORD('?');
813         }
814         *dst++ = c;
815         goto vist_loop;
816
817  vist_out:
818         *dst = '\0';
819         afree(buf, ATEMP);
820 }
821
822 #ifdef DEBUG
823 void
824 dumpchar(struct shf *shf, int c)
825 {
826         if (ksh_isctrl(c)) {
827                 /* C0 or C1 control character or DEL */
828                 shf_putc('^', shf);
829                 c = ksh_unctrl(c);
830         }
831         shf_putc(c, shf);
832 }
833
834 /* see: wdvarput */
835 static const char *
836 dumpwdvar_i(struct shf *shf, const char *wp, int quotelevel)
837 {
838         int c;
839
840         while (/* CONSTCOND */ 1) {
841                 switch(*wp++) {
842                 case EOS:
843                         shf_puts("EOS", shf);
844                         return (--wp);
845                 case ADELIM:
846                         if (ord(*wp) == ORD(/*{*/ '}')) {
847                                 shf_puts(/*{*/ "]ADELIM(})", shf);
848                                 return (wp + 1);
849                         }
850                         shf_puts("ADELIM=", shf);
851                         if (0)
852                                 /* FALLTHROUGH */
853                 case CHAR:
854                           shf_puts("CHAR=", shf);
855                         dumpchar(shf, *wp++);
856                         break;
857                 case QCHAR:
858                         shf_puts("QCHAR<", shf);
859                         c = ord(*wp++);
860                         if (quotelevel == 0 || c == ORD('"') ||
861                             c == ORD('\\') || ctype(c, C_DOLAR | C_GRAVE))
862                                 shf_putc('\\', shf);
863                         dumpchar(shf, c);
864                         goto closeandout;
865                 case COMASUB:
866                         shf_puts("COMASUB<", shf);
867                         goto dumpsub;
868                 case COMSUB:
869                         shf_puts("COMSUB<", shf);
870  dumpsub:
871                         while ((c = *wp++) != 0)
872                                 dumpchar(shf, c);
873  closeandout:
874                         shf_putc('>', shf);
875                         break;
876                 case FUNASUB:
877                         shf_puts("FUNASUB<", shf);
878                         goto dumpsub;
879                 case FUNSUB:
880                         shf_puts("FUNSUB<", shf);
881                         goto dumpsub;
882                 case VALSUB:
883                         shf_puts("VALSUB<", shf);
884                         goto dumpsub;
885                 case EXPRSUB:
886                         shf_puts("EXPRSUB<", shf);
887                         goto dumpsub;
888                 case OQUOTE:
889                         shf_fprintf(shf, "OQUOTE{%d" /*}*/, ++quotelevel);
890                         break;
891                 case CQUOTE:
892                         shf_fprintf(shf, /*{*/ "%d}CQUOTE", quotelevel);
893                         if (quotelevel)
894                                 quotelevel--;
895                         else
896                                 shf_puts("(err)", shf);
897                         break;
898                 case OSUBST:
899                         shf_puts("OSUBST(", shf);
900                         dumpchar(shf, *wp++);
901                         shf_puts(")[", shf);
902                         while ((c = *wp++) != 0)
903                                 dumpchar(shf, c);
904                         shf_putc('|', shf);
905                         wp = dumpwdvar_i(shf, wp, 0);
906                         break;
907                 case CSUBST:
908                         shf_puts("]CSUBST(", shf);
909                         dumpchar(shf, *wp++);
910                         shf_putc(')', shf);
911                         return (wp);
912                 case OPAT:
913                         shf_puts("OPAT=", shf);
914                         dumpchar(shf, *wp++);
915                         break;
916                 case SPAT:
917                         shf_puts("SPAT", shf);
918                         break;
919                 case CPAT:
920                         shf_puts("CPAT", shf);
921                         break;
922                 default:
923                         shf_fprintf(shf, "INVAL<%u>", (uint8_t)wp[-1]);
924                         break;
925                 }
926                 shf_putc(' ', shf);
927         }
928 }
929 void
930 dumpwdvar(struct shf *shf, const char *wp)
931 {
932         dumpwdvar_i(shf, wp, 0);
933 }
934
935 void
936 dumpioact(struct shf *shf, struct op *t)
937 {
938         struct ioword **ioact, *iop;
939
940         if ((ioact = t->ioact) == NULL)
941                 return;
942
943         shf_puts("{IOACT", shf);
944         while ((iop = *ioact++) != NULL) {
945                 unsigned short type = iop->ioflag & IOTYPE;
946 #define DT(x) case x: shf_puts(#x, shf); break;
947 #define DB(x) if (iop->ioflag & x) shf_puts("|" #x, shf);
948
949                 shf_putc(';', shf);
950                 switch (type) {
951                 DT(IOREAD)
952                 DT(IOWRITE)
953                 DT(IORDWR)
954                 DT(IOHERE)
955                 DT(IOCAT)
956                 DT(IODUP)
957                 default:
958                         shf_fprintf(shf, "unk%d", type);
959                 }
960                 DB(IOEVAL)
961                 DB(IOSKIP)
962                 DB(IOCLOB)
963                 DB(IORDUP)
964                 DB(IONAMEXP)
965                 DB(IOBASH)
966                 DB(IOHERESTR)
967                 DB(IONDELIM)
968                 shf_fprintf(shf, ",unit=%d", (int)iop->unit);
969                 if (iop->delim && !(iop->ioflag & IONDELIM)) {
970                         shf_puts(",delim<", shf);
971                         dumpwdvar(shf, iop->delim);
972                         shf_putc('>', shf);
973                 }
974                 if (iop->ioname) {
975                         if (iop->ioflag & IONAMEXP) {
976                                 shf_puts(",name=", shf);
977                                 print_value_quoted(shf, iop->ioname);
978                         } else {
979                                 shf_puts(",name<", shf);
980                                 dumpwdvar(shf, iop->ioname);
981                                 shf_putc('>', shf);
982                         }
983                 }
984                 if (iop->heredoc) {
985                         shf_puts(",heredoc=", shf);
986                         print_value_quoted(shf, iop->heredoc);
987                 }
988 #undef DT
989 #undef DB
990         }
991         shf_putc('}', shf);
992 }
993
994 void
995 dumptree(struct shf *shf, struct op *t)
996 {
997         int i, j;
998         const char **w, *name;
999         struct op *t1;
1000         static int nesting;
1001
1002         for (i = 0; i < nesting; ++i)
1003                 shf_putc('\t', shf);
1004         ++nesting;
1005         shf_puts("{tree:" /*}*/, shf);
1006         if (t == NULL) {
1007                 name = "(null)";
1008                 goto out;
1009         }
1010         dumpioact(shf, t);
1011         switch (t->type) {
1012 #define OPEN(x) case x: name = #x; shf_puts(" {" #x ":", shf); /*}*/
1013
1014         OPEN(TCOM)
1015                 if (t->vars) {
1016                         i = 0;
1017                         w = (const char **)t->vars;
1018                         while (*w) {
1019                                 shf_putc('\n', shf);
1020                                 for (j = 0; j < nesting; ++j)
1021                                         shf_putc('\t', shf);
1022                                 shf_fprintf(shf, " var%d<", i++);
1023                                 dumpwdvar(shf, *w++);
1024                                 shf_putc('>', shf);
1025                         }
1026                 } else
1027                         shf_puts(" #no-vars#", shf);
1028                 if (t->args) {
1029                         i = 0;
1030                         w = t->args;
1031                         while (*w) {
1032                                 shf_putc('\n', shf);
1033                                 for (j = 0; j < nesting; ++j)
1034                                         shf_putc('\t', shf);
1035                                 shf_fprintf(shf, " arg%d<", i++);
1036                                 dumpwdvar(shf, *w++);
1037                                 shf_putc('>', shf);
1038                         }
1039                 } else
1040                         shf_puts(" #no-args#", shf);
1041                 break;
1042         OPEN(TEXEC)
1043  dumpleftandout:
1044                 t = t->left;
1045  dumpandout:
1046                 shf_putc('\n', shf);
1047                 dumptree(shf, t);
1048                 break;
1049         OPEN(TPAREN)
1050                 goto dumpleftandout;
1051         OPEN(TPIPE)
1052  dumpleftmidrightandout:
1053                 shf_putc('\n', shf);
1054                 dumptree(shf, t->left);
1055 /* middumprightandout: (unused) */
1056                 shf_fprintf(shf, "/%s:", name);
1057  dumprightandout:
1058                 t = t->right;
1059                 goto dumpandout;
1060         OPEN(TLIST)
1061                 goto dumpleftmidrightandout;
1062         OPEN(TOR)
1063                 goto dumpleftmidrightandout;
1064         OPEN(TAND)
1065                 goto dumpleftmidrightandout;
1066         OPEN(TBANG)
1067                 goto dumprightandout;
1068         OPEN(TDBRACKET)
1069                 i = 0;
1070                 w = t->args;
1071                 while (*w) {
1072                         shf_putc('\n', shf);
1073                         for (j = 0; j < nesting; ++j)
1074                                 shf_putc('\t', shf);
1075                         shf_fprintf(shf, " arg%d<", i++);
1076                         dumpwdvar(shf, *w++);
1077                         shf_putc('>', shf);
1078                 }
1079                 break;
1080         OPEN(TFOR)
1081  dumpfor:
1082                 shf_fprintf(shf, " str<%s>", t->str);
1083                 if (t->vars != NULL) {
1084                         i = 0;
1085                         w = (const char **)t->vars;
1086                         while (*w) {
1087                                 shf_putc('\n', shf);
1088                                 for (j = 0; j < nesting; ++j)
1089                                         shf_putc('\t', shf);
1090                                 shf_fprintf(shf, " var%d<", i++);
1091                                 dumpwdvar(shf, *w++);
1092                                 shf_putc('>', shf);
1093                         }
1094                 }
1095                 goto dumpleftandout;
1096         OPEN(TSELECT)
1097                 goto dumpfor;
1098         OPEN(TCASE)
1099                 shf_fprintf(shf, " str<%s>", t->str);
1100                 i = 0;
1101                 for (t1 = t->left; t1 != NULL; t1 = t1->right) {
1102                         shf_putc('\n', shf);
1103                         for (j = 0; j < nesting; ++j)
1104                                 shf_putc('\t', shf);
1105                         shf_fprintf(shf, " sub%d[(", i);
1106                         w = (const char **)t1->vars;
1107                         while (*w) {
1108                                 dumpwdvar(shf, *w);
1109                                 if (w[1] != NULL)
1110                                         shf_putc('|', shf);
1111                                 ++w;
1112                         }
1113                         shf_putc(')', shf);
1114                         dumpioact(shf, t);
1115                         shf_putc('\n', shf);
1116                         dumptree(shf, t1->left);
1117                         shf_fprintf(shf, " ;%c/%d]", t1->u.charflag, i++);
1118                 }
1119                 break;
1120         OPEN(TWHILE)
1121                 goto dumpleftmidrightandout;
1122         OPEN(TUNTIL)
1123                 goto dumpleftmidrightandout;
1124         OPEN(TBRACE)
1125                 goto dumpleftandout;
1126         OPEN(TCOPROC)
1127                 goto dumpleftandout;
1128         OPEN(TASYNC)
1129                 goto dumpleftandout;
1130         OPEN(TFUNCT)
1131                 shf_fprintf(shf, " str<%s> ksh<%s>", t->str,
1132                     t->u.ksh_func ? Ttrue : Tfalse);
1133                 goto dumpleftandout;
1134         OPEN(TTIME)
1135                 goto dumpleftandout;
1136         OPEN(TIF)
1137  dumpif:
1138                 shf_putc('\n', shf);
1139                 dumptree(shf, t->left);
1140                 t = t->right;
1141                 dumpioact(shf, t);
1142                 if (t->left != NULL) {
1143                         shf_puts(" /TTHEN:\n", shf);
1144                         dumptree(shf, t->left);
1145                 }
1146                 if (t->right && t->right->type == TELIF) {
1147                         shf_puts(" /TELIF:", shf);
1148                         t = t->right;
1149                         dumpioact(shf, t);
1150                         goto dumpif;
1151                 }
1152                 if (t->right != NULL) {
1153                         shf_puts(" /TELSE:\n", shf);
1154                         dumptree(shf, t->right);
1155                 }
1156                 break;
1157         OPEN(TEOF)
1158  dumpunexpected:
1159                 shf_puts(Tunexpected, shf);
1160                 break;
1161         OPEN(TELIF)
1162                 goto dumpunexpected;
1163         OPEN(TPAT)
1164                 goto dumpunexpected;
1165         default:
1166                 name = "TINVALID";
1167                 shf_fprintf(shf, "{T<%d>:" /*}*/, t->type);
1168                 goto dumpunexpected;
1169
1170 #undef OPEN
1171         }
1172  out:
1173         shf_fprintf(shf, /*{*/ " /%s}\n", name);
1174         --nesting;
1175 }
1176 #endif