OSDN Git Service

3a79c15da6eb78eeb73d38c219466a721d7644a6
[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, 2015
6  *      Thorsten Glaser <tg@mirbsd.org>
7  *
8  * Provided that these terms and disclaimer and all copyright notices
9  * are retained or reproduced in an accompanying document, permission
10  * is granted to deal in this work without restriction, including un-
11  * limited rights to use, publicly perform, distribute, sell, modify,
12  * merge, give away, or sublicence.
13  *
14  * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
15  * the utmost extent permitted by applicable law, neither express nor
16  * implied; without malicious intent or gross negligence. In no event
17  * may a licensor, author or contributor be held liable for indirect,
18  * direct, other damage, loss, or other issues arising in any way out
19  * of dealing in the work, even if advised of the possibility of such
20  * damage or existence of a defect, except proven that it results out
21  * of said person's immediate fault when using the work as intended.
22  */
23
24 #include "sh.h"
25
26 __RCSID("$MirOS: src/bin/mksh/tree.c,v 1.72.2.1 2015/04/12 22:32:35 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]->ioflag & 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->ioflag & (IOTYPE | IOHERESTR)) == IOHERE &&
225                             iop->heredoc) {
226                                 shf_putc('\n', shf);
227                                 shf_puts(iop->heredoc, shf);
228                                 fptreef(shf, indent, "%s",
229                                     iop->ioflag & 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         unsigned short flag = iop->ioflag;
250         unsigned short type = flag & IOTYPE;
251         short 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", (int)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 (flag & IOHERESTR)
289                         shf_putc(' ', shf);
290         } else if (iop->name) {
291                 if (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 (ISCTRL(c & 0x7F)) {
782                 /* C0 or C1 control character or DEL */
783                 if (--sz == 0)
784                         /* not enough free space for two chars */
785                         goto vist_out;
786                 *dst++ = (c & 0x80) ? '$' : '^';
787                 c = UNCTRL(c & 0x7F);
788         } else if (UTFMODE && c > 0x7F) {
789                 /* better not try to display broken multibyte chars */
790                 /* also go easy on the Unicode: no U+FFFD here */
791                 c = '?';
792         }
793         *dst++ = c;
794         goto vist_loop;
795
796  vist_out:
797         *dst = '\0';
798         afree(buf, ATEMP);
799 }
800
801 #ifdef DEBUG
802 void
803 dumpchar(struct shf *shf, int c)
804 {
805         if (ISCTRL(c & 0x7F)) {
806                 /* C0 or C1 control character or DEL */
807                 shf_putc((c & 0x80) ? '$' : '^', shf);
808                 c = UNCTRL(c & 0x7F);
809         }
810         shf_putc(c, shf);
811 }
812
813 /* see: wdvarput */
814 static const char *
815 dumpwdvar_i(struct shf *shf, const char *wp, int quotelevel)
816 {
817         int c;
818
819         while (/* CONSTCOND */ 1) {
820                 switch(*wp++) {
821                 case EOS:
822                         shf_puts("EOS", shf);
823                         return (--wp);
824                 case ADELIM:
825                         shf_puts("ADELIM=", shf);
826                         if (0)
827                 case CHAR:
828                                 shf_puts("CHAR=", shf);
829                         dumpchar(shf, *wp++);
830                         break;
831                 case QCHAR:
832                         shf_puts("QCHAR<", shf);
833                         c = *wp++;
834                         if (quotelevel == 0 ||
835                             (c == '"' || c == '`' || c == '$' || c == '\\'))
836                                 shf_putc('\\', shf);
837                         dumpchar(shf, c);
838                         goto closeandout;
839                 case COMSUB:
840                         shf_puts("COMSUB<", shf);
841  dumpsub:
842                         while ((c = *wp++) != 0)
843                                 dumpchar(shf, c);
844  closeandout:
845                         shf_putc('>', shf);
846                         break;
847                 case FUNSUB:
848                         shf_puts("FUNSUB<", shf);
849                         goto dumpsub;
850                 case VALSUB:
851                         shf_puts("VALSUB<", shf);
852                         goto dumpsub;
853                 case EXPRSUB:
854                         shf_puts("EXPRSUB<", shf);
855                         goto dumpsub;
856                 case OQUOTE:
857                         shf_fprintf(shf, "OQUOTE{%d", ++quotelevel);
858                         break;
859                 case CQUOTE:
860                         shf_fprintf(shf, "%d}CQUOTE", quotelevel);
861                         if (quotelevel)
862                                 quotelevel--;
863                         else
864                                 shf_puts("(err)", shf);
865                         break;
866                 case OSUBST:
867                         shf_puts("OSUBST(", shf);
868                         dumpchar(shf, *wp++);
869                         shf_puts(")[", shf);
870                         while ((c = *wp++) != 0)
871                                 dumpchar(shf, c);
872                         shf_putc('|', shf);
873                         wp = dumpwdvar_i(shf, wp, 0);
874                         break;
875                 case CSUBST:
876                         shf_puts("]CSUBST(", shf);
877                         dumpchar(shf, *wp++);
878                         shf_putc(')', shf);
879                         return (wp);
880                 case OPAT:
881                         shf_puts("OPAT=", shf);
882                         dumpchar(shf, *wp++);
883                         break;
884                 case SPAT:
885                         shf_puts("SPAT", shf);
886                         break;
887                 case CPAT:
888                         shf_puts("CPAT", shf);
889                         break;
890                 default:
891                         shf_fprintf(shf, "INVAL<%u>", (uint8_t)wp[-1]);
892                         break;
893                 }
894                 shf_putc(' ', shf);
895         }
896 }
897 void
898 dumpwdvar(struct shf *shf, const char *wp)
899 {
900         dumpwdvar_i(shf, wp, 0);
901 }
902
903 void
904 dumpioact(struct shf *shf, struct op *t)
905 {
906         struct ioword **ioact, *iop;
907
908         if ((ioact = t->ioact) == NULL)
909                 return;
910
911         shf_puts("{IOACT", shf);
912         while ((iop = *ioact++) != NULL) {
913                 unsigned short type = iop->ioflag & IOTYPE;
914 #define DT(x) case x: shf_puts(#x, shf); break;
915 #define DB(x) if (iop->ioflag & x) shf_puts("|" #x, shf);
916
917                 shf_putc(';', shf);
918                 switch (type) {
919                 DT(IOREAD)
920                 DT(IOWRITE)
921                 DT(IORDWR)
922                 DT(IOHERE)
923                 DT(IOCAT)
924                 DT(IODUP)
925                 default:
926                         shf_fprintf(shf, "unk%d", type);
927                 }
928                 DB(IOEVAL)
929                 DB(IOSKIP)
930                 DB(IOCLOB)
931                 DB(IORDUP)
932                 DB(IONAMEXP)
933                 DB(IOBASH)
934                 DB(IOHERESTR)
935                 DB(IONDELIM)
936                 shf_fprintf(shf, ",unit=%d", (int)iop->unit);
937                 if (iop->delim) {
938                         shf_puts(",delim<", shf);
939                         dumpwdvar(shf, iop->delim);
940                         shf_putc('>', shf);
941                 }
942                 if (iop->name) {
943                         if (iop->ioflag & IONAMEXP) {
944                                 shf_puts(",name=", shf);
945                                 print_value_quoted(shf, iop->name);
946                         } else {
947                                 shf_puts(",name<", shf);
948                                 dumpwdvar(shf, iop->name);
949                                 shf_putc('>', shf);
950                         }
951                 }
952                 if (iop->heredoc) {
953                         shf_puts(",heredoc=", shf);
954                         print_value_quoted(shf, iop->heredoc);
955                 }
956 #undef DT
957 #undef DB
958         }
959         shf_putc('}', shf);
960 }
961
962 void
963 dumptree(struct shf *shf, struct op *t)
964 {
965         int i, j;
966         const char **w, *name;
967         struct op *t1;
968         static int nesting;
969
970         for (i = 0; i < nesting; ++i)
971                 shf_putc('\t', shf);
972         ++nesting;
973         shf_puts("{tree:" /*}*/, shf);
974         if (t == NULL) {
975                 name = "(null)";
976                 goto out;
977         }
978         dumpioact(shf, t);
979         switch (t->type) {
980 #define OPEN(x) case x: name = #x; shf_puts(" {" #x ":", shf); /*}*/
981
982         OPEN(TCOM)
983                 if (t->vars) {
984                         i = 0;
985                         w = (const char **)t->vars;
986                         while (*w) {
987                                 shf_putc('\n', shf);
988                                 for (j = 0; j < nesting; ++j)
989                                         shf_putc('\t', shf);
990                                 shf_fprintf(shf, " var%d<", i++);
991                                 dumpwdvar(shf, *w++);
992                                 shf_putc('>', shf);
993                         }
994                 } else
995                         shf_puts(" #no-vars#", shf);
996                 if (t->args) {
997                         i = 0;
998                         w = t->args;
999                         while (*w) {
1000                                 shf_putc('\n', shf);
1001                                 for (j = 0; j < nesting; ++j)
1002                                         shf_putc('\t', shf);
1003                                 shf_fprintf(shf, " arg%d<", i++);
1004                                 dumpwdvar(shf, *w++);
1005                                 shf_putc('>', shf);
1006                         }
1007                 } else
1008                         shf_puts(" #no-args#", shf);
1009                 break;
1010         OPEN(TEXEC)
1011  dumpleftandout:
1012                 t = t->left;
1013  dumpandout:
1014                 shf_putc('\n', shf);
1015                 dumptree(shf, t);
1016                 break;
1017         OPEN(TPAREN)
1018                 goto dumpleftandout;
1019         OPEN(TPIPE)
1020  dumpleftmidrightandout:
1021                 shf_putc('\n', shf);
1022                 dumptree(shf, t->left);
1023 /* middumprightandout: (unused) */
1024                 shf_fprintf(shf, "/%s:", name);
1025  dumprightandout:
1026                 t = t->right;
1027                 goto dumpandout;
1028         OPEN(TLIST)
1029                 goto dumpleftmidrightandout;
1030         OPEN(TOR)
1031                 goto dumpleftmidrightandout;
1032         OPEN(TAND)
1033                 goto dumpleftmidrightandout;
1034         OPEN(TBANG)
1035                 goto dumprightandout;
1036         OPEN(TDBRACKET)
1037                 i = 0;
1038                 w = t->args;
1039                 while (*w) {
1040                         shf_putc('\n', shf);
1041                         for (j = 0; j < nesting; ++j)
1042                                 shf_putc('\t', shf);
1043                         shf_fprintf(shf, " arg%d<", i++);
1044                         dumpwdvar(shf, *w++);
1045                         shf_putc('>', shf);
1046                 }
1047                 break;
1048         OPEN(TFOR)
1049  dumpfor:
1050                 shf_fprintf(shf, " str<%s>", t->str);
1051                 if (t->vars != NULL) {
1052                         i = 0;
1053                         w = (const char **)t->vars;
1054                         while (*w) {
1055                                 shf_putc('\n', shf);
1056                                 for (j = 0; j < nesting; ++j)
1057                                         shf_putc('\t', shf);
1058                                 shf_fprintf(shf, " var%d<", i++);
1059                                 dumpwdvar(shf, *w++);
1060                                 shf_putc('>', shf);
1061                         }
1062                 }
1063                 goto dumpleftandout;
1064         OPEN(TSELECT)
1065                 goto dumpfor;
1066         OPEN(TCASE)
1067                 shf_fprintf(shf, " str<%s>", t->str);
1068                 i = 0;
1069                 for (t1 = t->left; t1 != NULL; t1 = t1->right) {
1070                         shf_putc('\n', shf);
1071                         for (j = 0; j < nesting; ++j)
1072                                 shf_putc('\t', shf);
1073                         shf_fprintf(shf, " sub%d[(", i);
1074                         w = (const char **)t1->vars;
1075                         while (*w) {
1076                                 dumpwdvar(shf, *w);
1077                                 if (w[1] != NULL)
1078                                         shf_putc('|', shf);
1079                                 ++w;
1080                         }
1081                         shf_putc(')', shf);
1082                         dumpioact(shf, t);
1083                         shf_putc('\n', shf);
1084                         dumptree(shf, t1->left);
1085                         shf_fprintf(shf, " ;%c/%d]", t1->u.charflag, i++);
1086                 }
1087                 break;
1088         OPEN(TWHILE)
1089                 goto dumpleftmidrightandout;
1090         OPEN(TUNTIL)
1091                 goto dumpleftmidrightandout;
1092         OPEN(TBRACE)
1093                 goto dumpleftandout;
1094         OPEN(TCOPROC)
1095                 goto dumpleftandout;
1096         OPEN(TASYNC)
1097                 goto dumpleftandout;
1098         OPEN(TFUNCT)
1099                 shf_fprintf(shf, " str<%s> ksh<%s>", t->str,
1100                     t->u.ksh_func ? "yes" : "no");
1101                 goto dumpleftandout;
1102         OPEN(TTIME)
1103                 goto dumpleftandout;
1104         OPEN(TIF)
1105  dumpif:
1106                 shf_putc('\n', shf);
1107                 dumptree(shf, t->left);
1108                 t = t->right;
1109                 dumpioact(shf, t);
1110                 if (t->left != NULL) {
1111                         shf_puts(" /TTHEN:\n", shf);
1112                         dumptree(shf, t->left);
1113                 }
1114                 if (t->right && t->right->type == TELIF) {
1115                         shf_puts(" /TELIF:", shf);
1116                         t = t->right;
1117                         dumpioact(shf, t);
1118                         goto dumpif;
1119                 }
1120                 if (t->right != NULL) {
1121                         shf_puts(" /TELSE:\n", shf);
1122                         dumptree(shf, t->right);
1123                 }
1124                 break;
1125         OPEN(TEOF)
1126  dumpunexpected:
1127                 shf_puts("unexpected", shf);
1128                 break;
1129         OPEN(TELIF)
1130                 goto dumpunexpected;
1131         OPEN(TPAT)
1132                 goto dumpunexpected;
1133         default:
1134                 name = "TINVALID";
1135                 shf_fprintf(shf, "{T<%d>:" /*}*/, t->type);
1136                 goto dumpunexpected;
1137
1138 #undef OPEN
1139         }
1140  out:
1141         shf_fprintf(shf, /*{*/ " /%s}\n", name);
1142         --nesting;
1143 }
1144 #endif