OSDN Git Service

dcbd7a1cc29589bc2e7c5c0ee51d2326cdfa826c
[android-x86/external-mksh.git] / src / tree.c
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.71 2013/07/26 20:33:24 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
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
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 *
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
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 *
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
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 *
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 *
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 *
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 *
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 **
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
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
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
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
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 ((c & 0x60) == 0 || (c & 0x7F) == 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 = (c & 0x7F) ^ 0x40;
788         } else if (UTFMODE && c > 0x7F) {
789                 /* better not try to display broken multibyte chars */
790                 c = '?';
791         }
792         *dst++ = c;
793         goto vist_loop;
794
795  vist_out:
796         *dst = '\0';
797         afree(buf, ATEMP);
798 }
799
800 #ifdef DEBUG
801 void
802 dumpchar(struct shf *shf, int c)
803 {
804         if (((c & 0x60) == 0) || ((c & 0x7F) == 0x7F)) {
805                 /* C0 or C1 control character or DEL */
806                 shf_putc((c & 0x80) ? '$' : '^', shf);
807                 c = (c & 0x7F) ^ 0x40;
808         }
809         shf_putc(c, shf);
810 }
811
812 /* see: wdvarput */
813 static const char *
814 dumpwdvar_i(struct shf *shf, const char *wp, int quotelevel)
815 {
816         int c;
817
818         while (/* CONSTCOND */ 1) {
819                 switch(*wp++) {
820                 case EOS:
821                         shf_puts("EOS", shf);
822                         return (--wp);
823                 case ADELIM:
824                         shf_puts("ADELIM=", shf);
825                         if (0)
826                 case CHAR:
827                                 shf_puts("CHAR=", shf);
828                         dumpchar(shf, *wp++);
829                         break;
830                 case QCHAR:
831                         shf_puts("QCHAR<", shf);
832                         c = *wp++;
833                         if (quotelevel == 0 ||
834                             (c == '"' || c == '`' || c == '$' || c == '\\'))
835                                 shf_putc('\\', shf);
836                         dumpchar(shf, c);
837                         goto closeandout;
838                 case COMSUB:
839                         shf_puts("COMSUB<", shf);
840  dumpsub:
841                         while ((c = *wp++) != 0)
842                                 dumpchar(shf, c);
843  closeandout:
844                         shf_putc('>', shf);
845                         break;
846                 case FUNSUB:
847                         shf_puts("FUNSUB<", shf);
848                         goto dumpsub;
849                 case VALSUB:
850                         shf_puts("VALSUB<", shf);
851                         goto dumpsub;
852                 case EXPRSUB:
853                         shf_puts("EXPRSUB<", shf);
854                         goto dumpsub;
855                 case OQUOTE:
856                         shf_fprintf(shf, "OQUOTE{%d", ++quotelevel);
857                         break;
858                 case CQUOTE:
859                         shf_fprintf(shf, "%d}CQUOTE", quotelevel);
860                         if (quotelevel)
861                                 quotelevel--;
862                         else
863                                 shf_puts("(err)", shf);
864                         break;
865                 case OSUBST:
866                         shf_puts("OSUBST(", shf);
867                         dumpchar(shf, *wp++);
868                         shf_puts(")[", shf);
869                         while ((c = *wp++) != 0)
870                                 dumpchar(shf, c);
871                         shf_putc('|', shf);
872                         wp = dumpwdvar_i(shf, wp, 0);
873                         break;
874                 case CSUBST:
875                         shf_puts("]CSUBST(", shf);
876                         dumpchar(shf, *wp++);
877                         shf_putc(')', shf);
878                         return (wp);
879                 case OPAT:
880                         shf_puts("OPAT=", shf);
881                         dumpchar(shf, *wp++);
882                         break;
883                 case SPAT:
884                         shf_puts("SPAT", shf);
885                         break;
886                 case CPAT:
887                         shf_puts("CPAT", shf);
888                         break;
889                 default:
890                         shf_fprintf(shf, "INVAL<%u>", (uint8_t)wp[-1]);
891                         break;
892                 }
893                 shf_putc(' ', shf);
894         }
895 }
896 void
897 dumpwdvar(struct shf *shf, const char *wp)
898 {
899         dumpwdvar_i(shf, wp, 0);
900 }
901
902 void
903 dumpioact(struct shf *shf, struct op *t)
904 {
905         struct ioword **ioact, *iop;
906
907         if ((ioact = t->ioact) == NULL)
908                 return;
909
910         shf_puts("{IOACT", shf);
911         while ((iop = *ioact++) != NULL) {
912                 int type = iop->flag & IOTYPE;
913 #define DT(x) case x: shf_puts(#x, shf); break;
914 #define DB(x) if (iop->flag & x) shf_puts("|" #x, shf);
915
916                 shf_putc(';', shf);
917                 switch (type) {
918                 DT(IOREAD)
919                 DT(IOWRITE)
920                 DT(IORDWR)
921                 DT(IOHERE)
922                 DT(IOCAT)
923                 DT(IODUP)
924                 default:
925                         shf_fprintf(shf, "unk%d", type);
926                 }
927                 DB(IOEVAL)
928                 DB(IOSKIP)
929                 DB(IOCLOB)
930                 DB(IORDUP)
931                 DB(IONAMEXP)
932                 DB(IOBASH)
933                 DB(IOHERESTR)
934                 DB(IONDELIM)
935                 shf_fprintf(shf, ",unit=%d", iop->unit);
936                 if (iop->delim) {
937                         shf_puts(",delim<", shf);
938                         dumpwdvar(shf, iop->delim);
939                         shf_putc('>', shf);
940                 }
941                 if (iop->name) {
942                         if (iop->flag & IONAMEXP) {
943                                 shf_puts(",name=", shf);
944                                 print_value_quoted(shf, iop->name);
945                         } else {
946                                 shf_puts(",name<", shf);
947                                 dumpwdvar(shf, iop->name);
948                                 shf_putc('>', shf);
949                         }
950                 }
951                 if (iop->heredoc) {
952                         shf_puts(",heredoc=", shf);
953                         print_value_quoted(shf, iop->heredoc);
954                 }
955 #undef DT
956 #undef DB
957         }
958         shf_putc('}', shf);
959 }
960
961 void
962 dumptree(struct shf *shf, struct op *t)
963 {
964         int i, j;
965         const char **w, *name;
966         struct op *t1;
967         static int nesting;
968
969         for (i = 0; i < nesting; ++i)
970                 shf_putc('\t', shf);
971         ++nesting;
972         shf_puts("{tree:" /*}*/, shf);
973         if (t == NULL) {
974                 name = "(null)";
975                 goto out;
976         }
977         dumpioact(shf, t);
978         switch (t->type) {
979 #define OPEN(x) case x: name = #x; shf_puts(" {" #x ":", shf); /*}*/
980
981         OPEN(TCOM)
982                 if (t->vars) {
983                         i = 0;
984                         w = (const char **)t->vars;
985                         while (*w) {
986                                 shf_putc('\n', shf);
987                                 for (j = 0; j < nesting; ++j)
988                                         shf_putc('\t', shf);
989                                 shf_fprintf(shf, " var%d<", i++);
990                                 dumpwdvar(shf, *w++);
991                                 shf_putc('>', shf);
992                         }
993                 } else
994                         shf_puts(" #no-vars#", shf);
995                 if (t->args) {
996                         i = 0;
997                         w = t->args;
998                         while (*w) {
999                                 shf_putc('\n', shf);
1000                                 for (j = 0; j < nesting; ++j)
1001                                         shf_putc('\t', shf);
1002                                 shf_fprintf(shf, " arg%d<", i++);
1003                                 dumpwdvar(shf, *w++);
1004                                 shf_putc('>', shf);
1005                         }
1006                 } else
1007                         shf_puts(" #no-args#", shf);
1008                 break;
1009         OPEN(TEXEC)
1010  dumpleftandout:
1011                 t = t->left;
1012  dumpandout:
1013                 shf_putc('\n', shf);
1014                 dumptree(shf, t);
1015                 break;
1016         OPEN(TPAREN)
1017                 goto dumpleftandout;
1018         OPEN(TPIPE)
1019  dumpleftmidrightandout:
1020                 shf_putc('\n', shf);
1021                 dumptree(shf, t->left);
1022 /* middumprightandout: (unused) */
1023                 shf_fprintf(shf, "/%s:", name);
1024  dumprightandout:
1025                 t = t->right;
1026                 goto dumpandout;
1027         OPEN(TLIST)
1028                 goto dumpleftmidrightandout;
1029         OPEN(TOR)
1030                 goto dumpleftmidrightandout;
1031         OPEN(TAND)
1032                 goto dumpleftmidrightandout;
1033         OPEN(TBANG)
1034                 goto dumprightandout;
1035         OPEN(TDBRACKET)
1036                 i = 0;
1037                 w = t->args;
1038                 while (*w) {
1039                         shf_putc('\n', shf);
1040                         for (j = 0; j < nesting; ++j)
1041                                 shf_putc('\t', shf);
1042                         shf_fprintf(shf, " arg%d<", i++);
1043                         dumpwdvar(shf, *w++);
1044                         shf_putc('>', shf);
1045                 }
1046                 break;
1047         OPEN(TFOR)
1048  dumpfor:
1049                 shf_fprintf(shf, " str<%s>", t->str);
1050                 if (t->vars != NULL) {
1051                         i = 0;
1052                         w = (const char **)t->vars;
1053                         while (*w) {
1054                                 shf_putc('\n', shf);
1055                                 for (j = 0; j < nesting; ++j)
1056                                         shf_putc('\t', shf);
1057                                 shf_fprintf(shf, " var%d<", i++);
1058                                 dumpwdvar(shf, *w++);
1059                                 shf_putc('>', shf);
1060                         }
1061                 }
1062                 goto dumpleftandout;
1063         OPEN(TSELECT)
1064                 goto dumpfor;
1065         OPEN(TCASE)
1066                 shf_fprintf(shf, " str<%s>", t->str);
1067                 i = 0;
1068                 for (t1 = t->left; t1 != NULL; t1 = t1->right) {
1069                         shf_putc('\n', shf);
1070                         for (j = 0; j < nesting; ++j)
1071                                 shf_putc('\t', shf);
1072                         shf_fprintf(shf, " sub%d[(", i);
1073                         w = (const char **)t1->vars;
1074                         while (*w) {
1075                                 dumpwdvar(shf, *w);
1076                                 if (w[1] != NULL)
1077                                         shf_putc('|', shf);
1078                                 ++w;
1079                         }
1080                         shf_putc(')', shf);
1081                         dumpioact(shf, t);
1082                         shf_putc('\n', shf);
1083                         dumptree(shf, t1->left);
1084                         shf_fprintf(shf, " ;%c/%d]", t1->u.charflag, i++);
1085                 }
1086                 break;
1087         OPEN(TWHILE)
1088                 goto dumpleftmidrightandout;
1089         OPEN(TUNTIL)
1090                 goto dumpleftmidrightandout;
1091         OPEN(TBRACE)
1092                 goto dumpleftandout;
1093         OPEN(TCOPROC)
1094                 goto dumpleftandout;
1095         OPEN(TASYNC)
1096                 goto dumpleftandout;
1097         OPEN(TFUNCT)
1098                 shf_fprintf(shf, " str<%s> ksh<%s>", t->str,
1099                     t->u.ksh_func ? "yes" : "no");
1100                 goto dumpleftandout;
1101         OPEN(TTIME)
1102                 goto dumpleftandout;
1103         OPEN(TIF)
1104  dumpif:
1105                 shf_putc('\n', shf);
1106                 dumptree(shf, t->left);
1107                 t = t->right;
1108                 dumpioact(shf, t);
1109                 if (t->left != NULL) {
1110                         shf_puts(" /TTHEN:\n", shf);
1111                         dumptree(shf, t->left);
1112                 }
1113                 if (t->right && t->right->type == TELIF) {
1114                         shf_puts(" /TELIF:", shf);
1115                         t = t->right;
1116                         dumpioact(shf, t);
1117                         goto dumpif;
1118                 }
1119                 if (t->right != NULL) {
1120                         shf_puts(" /TELSE:\n", shf);
1121                         dumptree(shf, t->right);
1122                 }
1123                 break;
1124         OPEN(TEOF)
1125  dumpunexpected:
1126                 shf_puts("unexpected", shf);
1127                 break;
1128         OPEN(TELIF)
1129                 goto dumpunexpected;
1130         OPEN(TPAT)
1131                 goto dumpunexpected;
1132         default:
1133                 name = "TINVALID";
1134                 shf_fprintf(shf, "{T<%d>:" /*}*/, t->type);
1135                 goto dumpunexpected;
1136
1137 #undef OPEN
1138         }
1139  out:
1140         shf_fprintf(shf, /*{*/ " /%s}\n", name);
1141         --nesting;
1142 }
1143 #endif