1 /*
2  * text.c - textual representations of syntax trees
3  *
4  * This file is part of zsh, the Z shell.
5  *
6  * Copyright (c) 1992-1997 Paul Falstad
7  * All rights reserved.
8  *
9  * Permission is hereby granted, without written agreement and without
10  * license or royalty fees, to use, copy, modify, and distribute this
11  * software and to distribute modified versions of this software for any
12  * purpose, provided that the above copyright notice and the following
13  * two paragraphs appear in all copies of this software.
14  *
15  * In no event shall Paul Falstad or the Zsh Development Group be liable
16  * to any party for direct, indirect, special, incidental, or consequential
17  * damages arising out of the use of this software and its documentation,
18  * even if Paul Falstad and the Zsh Development Group have been advised of
19  * the possibility of such damage.
20  *
21  * Paul Falstad and the Zsh Development Group specifically disclaim any
22  * warranties, including, but not limited to, the implied warranties of
23  * merchantability and fitness for a particular purpose.  The software
24  * provided hereunder is on an "as is" basis, and Paul Falstad and the
25  * Zsh Development Group have no obligation to provide maintenance,
26  * support, updates, enhancements, or modifications.
27  *
28  */
29 
30 #include "zsh.mdh"
31 #include "text.pro"
32 
33 /*
34  * If non-zero, expand syntactically significant leading tabs in text
35  * to this number of spaces.
36  *
37  * If negative, don't output leading whitespace at all.
38  */
39 
40 /**/
41 int text_expand_tabs;
42 
43 /*
44  * Binary operators in conditions.
45  * There order is tied to the order of the definitions COND_STREQ
46  * et seq. in zsh.h.
47  */
48 static const char *cond_binary_ops[] = {
49     "=", "==", "!=", "<", ">", "-nt", "-ot", "-ef", "-eq",
50     "-ne", "-lt", "-gt", "-le", "-ge", "=~", NULL
51 };
52 
53 static char *tptr, *tbuf, *tlim, *tpending;
54 static int tsiz, tindent, tnewlins, tjob;
55 
56 /**/
57 int
is_cond_binary_op(const char * str)58 is_cond_binary_op(const char *str)
59 {
60     const char **op;
61     for (op = cond_binary_ops; *op; op++)
62     {
63 	if (!strcmp(str, *op))
64 	    return 1;
65     }
66     return 0;
67 }
68 
69 static void
dec_tindent(void)70 dec_tindent(void)
71 {
72     DPUTS(tindent == 0, "attempting to decrement tindent below zero");
73     if (tindent > 0)
74 	tindent--;
75 }
76 
77 /*
78  * Add a pair of pending strings and a newline.
79  * This is used for here documents.  It will be output when
80  * we have a lexically significant newline.
81  *
82  * This isn't that common and a multiple use on the same line is *very*
83  * uncommon; we don't try to optimise it.
84  *
85  * This is not used for job text; there we bear the inaccuracy
86  * of turning this into a here-string.
87  */
88 static void
taddpending(char * str1,char * str2)89 taddpending(char *str1, char *str2)
90 {
91     int len = strlen(str1) + strlen(str2) + 1;
92 
93     /*
94      * We don't strip newlines from here-documents converted
95      * to here-strings, so no munging is required except to
96      * add a newline after the here-document terminator.
97      * However, because the job text doesn't automatically
98      * have a newline right at the end, we handle that
99      * specially.
100      */
101     if (tpending) {
102 	int oldlen = strlen(tpending);
103 	tpending = zrealloc(tpending, len + oldlen + 1);
104 	sprintf(tpending + oldlen, "\n%s%s", str1, str2);
105     } else {
106 	tpending = (char *)zalloc(len);
107 	sprintf(tpending, "%s%s", str1, str2);
108     }
109 }
110 
111 /* Output the pending string where appropriate */
112 
113 static void
tdopending(void)114 tdopending(void)
115 {
116     if (tpending) {
117 	taddchr('\n');
118 	taddstr(tpending);
119 	zsfree(tpending);
120 	tpending = NULL;
121     }
122 }
123 
124 /* add a character to the text buffer */
125 
126 /**/
127 static void
taddchr(int c)128 taddchr(int c)
129 {
130     *tptr++ = c;
131     if (tptr == tlim) {
132 	if (!tbuf) {
133 	    tptr--;
134 	    return;
135 	}
136 	tbuf = zrealloc(tbuf, tsiz *= 2);
137 	tlim = tbuf + tsiz;
138 	tptr = tbuf + tsiz / 2;
139     }
140 }
141 
142 /* add a string to the text buffer */
143 
144 /**/
145 static void
taddstr(const char * s)146 taddstr(const char *s)
147 {
148     int sl = strlen(s);
149     char c;
150 
151     while (tptr + sl >= tlim) {
152 	int x = tptr - tbuf;
153 
154 	if (!tbuf)
155 	    return;
156 	tbuf = zrealloc(tbuf, tsiz *= 2);
157 	tlim = tbuf + tsiz;
158 	tptr = tbuf + x;
159     }
160     if (tnewlins) {
161 	memcpy(tptr, s, sl);
162 	tptr += sl;
163     } else
164 	while ((c = *s++))
165 	    *tptr++ = (c == '\n' ? ' ' : c);
166 }
167 
168 /**/
169 static void
taddlist(Estate state,int num)170 taddlist(Estate state, int num)
171 {
172     if (num) {
173 	while (num--) {
174 	    taddstr(ecgetstr(state, EC_NODUP, NULL));
175 	    taddchr(' ');
176 	}
177 	tptr--;
178     }
179 }
180 
181 /* add an assignment */
182 
183 static void
taddassign(wordcode code,Estate state,int typeset)184 taddassign(wordcode code, Estate state, int typeset)
185 {
186     /* name */
187     taddstr(ecgetstr(state, EC_NODUP, NULL));
188     /* value... maybe */
189     if (WC_ASSIGN_TYPE2(code) == WC_ASSIGN_INC) {
190 	if (typeset) {
191 	    /* dummy assignment --- just var name */
192 	    (void)ecgetstr(state, EC_NODUP, NULL);
193 	    taddchr(' ');
194 	    return;
195 	}
196 	taddchr('+');
197     }
198     taddchr('=');
199     if (WC_ASSIGN_TYPE(code) == WC_ASSIGN_ARRAY) {
200 	taddchr('(');
201 	taddlist(state, WC_ASSIGN_NUM(code));
202 	taddstr(") ");
203     } else {
204 	taddstr(ecgetstr(state, EC_NODUP, NULL));
205 	taddchr(' ');
206     }
207 }
208 
209 /* add a number of assignments from typeset */
210 
211 /**/
212 static void
taddassignlist(Estate state,wordcode count)213 taddassignlist(Estate state, wordcode count)
214 {
215     if (count)
216 	taddchr(' ');
217     while (count--) {
218 	wordcode code = *state->pc++;
219 	taddassign(code, state, 1);
220     }
221 }
222 
223 /* add a newline, or something equivalent, to the text buffer */
224 
225 /**/
226 static void
taddnl(int no_semicolon)227 taddnl(int no_semicolon)
228 {
229     int t0;
230 
231     if (tnewlins) {
232 	tdopending();
233 	taddchr('\n');
234 	for (t0 = 0; t0 != tindent; t0++) {
235 	    if (text_expand_tabs >= 0) {
236 		if (text_expand_tabs) {
237 		    int t1;
238 		    for (t1 = 0; t1 < text_expand_tabs; t1++)
239 			taddchr(' ');
240 		} else
241 		    taddchr('\t');
242 	    }
243 	}
244     } else if (no_semicolon) {
245 	taddstr(" ");
246     } else {
247 	taddstr("; ");
248     }
249 }
250 
251 /*
252  * Output a tab that may be expanded as part of a leading set.
253  * Note this is not part of the text framework; it's for
254  * code that needs to output its own tabs that are to be
255  * consistent with those from getpermtext().
256  *
257  * Note these tabs are only expected to be useful at the
258  * start of the line, so we make no attempt to count columns.
259  */
260 
261 /**/
262 void
zoutputtab(FILE * outf)263 zoutputtab(FILE *outf)
264 {
265     if (text_expand_tabs < 0)
266 	return;
267     if (text_expand_tabs) {
268 	int i;
269 	for (i = 0; i < text_expand_tabs; i++)
270 	    fputc(' ', outf);
271     } else
272 	fputc('\t', outf);
273 }
274 
275 /* get a permanent textual representation of n */
276 
277 /**/
278 mod_export char *
getpermtext(Eprog prog,Wordcode c,int start_indent)279 getpermtext(Eprog prog, Wordcode c, int start_indent)
280 {
281     struct estate s;
282 
283     queue_signals();
284 
285     if (!c)
286 	c = prog->prog;
287 
288     useeprog(prog);		/* mark as used */
289 
290     s.prog = prog;
291     s.pc = c;
292     s.strs = prog->strs;
293 
294     tindent = start_indent;
295     tnewlins = 1;
296     tbuf = (char *)zalloc(tsiz = 32);
297     tptr = tbuf;
298     tlim = tbuf + tsiz;
299     tjob = 0;
300     if (prog->len)
301 	gettext2(&s);
302     *tptr = '\0';
303     freeeprog(prog);		/* mark as unused */
304     untokenize(tbuf);
305 
306     unqueue_signals();
307 
308     return tbuf;
309 }
310 
311 /* get a representation of n in a job text buffer */
312 
313 /**/
314 char *
getjobtext(Eprog prog,Wordcode c)315 getjobtext(Eprog prog, Wordcode c)
316 {
317     static char jbuf[JOBTEXTSIZE];
318 
319     struct estate s;
320 
321     queue_signals();
322 
323     if (!c)
324 	c = prog->prog;
325 
326     useeprog(prog);		/* mark as used */
327     s.prog = prog;
328     s.pc = c;
329     s.strs = prog->strs;
330 
331     tindent = 0;
332     tnewlins = 0;
333     tbuf = NULL;
334     tptr = jbuf;
335     tlim = tptr + JOBTEXTSIZE - 1;
336     tjob = 1;
337     gettext2(&s);
338     *tptr = '\0';
339     freeeprog(prog);		/* mark as unused */
340     untokenize(jbuf);
341 
342     unqueue_signals();
343 
344     return jbuf;
345 }
346 
347 /*
348  * gettext2() shows one way to walk through the word code without
349  * recursion. We start by reading a word code and executing the
350  * action for it. Some codes have sub-structures (like, e.g. WC_FOR)
351  * and require something to be done after the sub-structure has been
352  * handled. For these codes a tstack structure which describes what
353  * has to be done is pushed onto a stack. Codes without sub-structures
354  * arrange for the next structure being taken from the stack so that
355  * the action for it is executed instead of the one for the next
356  * word code. If the stack is empty at this point, we have handled
357  * the whole structure we were called for.
358  */
359 
360 typedef struct tstack *Tstack;
361 
362 struct tstack {
363     Tstack prev;
364     wordcode code;
365     int pop;
366     union {
367 	struct {
368 	    LinkList list;
369 	} _redir;
370 	struct {
371 	    char *strs;
372 	    Wordcode end;
373 	    int nargs;
374 	} _funcdef;
375 	struct {
376 	    Wordcode end;
377 	} _case;
378 	struct {
379 	    int cond;
380 	    Wordcode end;
381 	} _if;
382 	struct {
383 	    int par;
384 	} _cond;
385 	struct {
386 	    Wordcode end;
387 	} _subsh;
388     } u;
389 };
390 
391 static Tstack tstack, tfree;
392 
393 static Tstack
tpush(wordcode code,int pop)394 tpush(wordcode code, int pop)
395 {
396     Tstack s;
397 
398     if ((s = tfree))
399 	tfree = s->prev;
400     else
401 	s = (Tstack) zalloc(sizeof(*s));
402 
403     s->prev = tstack;
404     tstack = s;
405     s->code = code;
406     s->pop = pop;
407 
408     return s;
409 }
410 
411 /**/
412 static void
gettext2(Estate state)413 gettext2(Estate state)
414 {
415     Tstack s, n;
416     int stack = 0;
417     wordcode code;
418 
419     while (1) {
420 	if (stack) {
421 	    if (!(s = tstack))
422 		break;
423 	    if (s->pop) {
424 		tstack = s->prev;
425 		s->prev = tfree;
426 		tfree = s;
427 	    }
428 	    code = s->code;
429 	    stack = 0;
430 	} else {
431 	    s = NULL;
432 	    code = *state->pc++;
433 	}
434 	switch (wc_code(code)) {
435 	case WC_LIST:
436 	    if (!s) {
437 		s = tpush(code, (WC_LIST_TYPE(code) & Z_END));
438 		stack = 0;
439 	    } else {
440 		if (WC_LIST_TYPE(code) & Z_ASYNC) {
441 		    taddstr(" &");
442 		    if (WC_LIST_TYPE(code) & Z_DISOWN)
443 			taddstr("|");
444 		}
445 		if (!(stack = (WC_LIST_TYPE(code) & Z_END))) {
446 		    if (tnewlins)
447 			taddnl(0);
448 		    else
449 			taddstr((WC_LIST_TYPE(code) & Z_ASYNC) ? " " : "; ");
450 		    s->code = *state->pc++;
451 		    s->pop = (WC_LIST_TYPE(s->code) & Z_END);
452 		}
453 	    }
454 	    if (!stack && (WC_LIST_TYPE(s->code) & Z_SIMPLE))
455 		state->pc++;
456 	    break;
457 	case WC_SUBLIST:
458 	    if (!s) {
459                 if (!(WC_SUBLIST_FLAGS(code) & WC_SUBLIST_SIMPLE) &&
460                     wc_code(*state->pc) != WC_PIPE)
461                     stack = -1;
462 		if (WC_SUBLIST_FLAGS(code) & WC_SUBLIST_NOT)
463 		    taddstr(stack ? "!" : "! ");
464 		if (WC_SUBLIST_FLAGS(code) & WC_SUBLIST_COPROC)
465 		    taddstr(stack ? "coproc" : "coproc ");
466 		s = tpush(code, (WC_SUBLIST_TYPE(code) == WC_SUBLIST_END));
467 	    } else {
468 		if (!(stack = (WC_SUBLIST_TYPE(code) == WC_SUBLIST_END))) {
469 		    taddstr((WC_SUBLIST_TYPE(code) == WC_SUBLIST_OR) ?
470 			    " || " : " && ");
471 		    s->code = *state->pc++;
472 		    s->pop = (WC_SUBLIST_TYPE(s->code) == WC_SUBLIST_END);
473 		    if (WC_SUBLIST_FLAGS(s->code) & WC_SUBLIST_NOT) {
474 			if (WC_SUBLIST_SKIP(s->code) == 0)
475 			    stack = 1;
476 			taddstr((stack || (!(WC_SUBLIST_FLAGS(s->code) &
477 			        WC_SUBLIST_SIMPLE) && wc_code(*state->pc) !=
478 			        WC_PIPE)) ? "!" : "! ");
479 		    }
480 		    if (WC_SUBLIST_FLAGS(s->code) & WC_SUBLIST_COPROC)
481 			taddstr("coproc ");
482 		}
483 	    }
484 	    if (stack < 1 && (WC_SUBLIST_FLAGS(s->code) & WC_SUBLIST_SIMPLE))
485 		state->pc++;
486 	    break;
487 	case WC_PIPE:
488 	    if (!s) {
489 		tpush(code, (WC_PIPE_TYPE(code) == WC_PIPE_END));
490 		if (WC_PIPE_TYPE(code) == WC_PIPE_MID)
491 		    state->pc++;
492 	    } else {
493 		if (!(stack = (WC_PIPE_TYPE(code) == WC_PIPE_END))) {
494 		    taddstr(" | ");
495 		    s->code = *state->pc++;
496 		    if (!(s->pop = (WC_PIPE_TYPE(s->code) == WC_PIPE_END)))
497 			state->pc++;
498 		}
499 	    }
500 	    break;
501 	case WC_REDIR:
502 	    if (!s) {
503 		state->pc--;
504 		n = tpush(code, 1);
505 		n->u._redir.list = ecgetredirs(state);
506 	    } else {
507 		getredirs(s->u._redir.list);
508 		stack = 1;
509 	    }
510 	    break;
511 	case WC_ASSIGN:
512 	    taddassign(code, state, 0);
513 	    break;
514 	case WC_SIMPLE:
515 	    taddlist(state, WC_SIMPLE_ARGC(code));
516 	    stack = 1;
517 	    break;
518 	case WC_TYPESET:
519 	    taddlist(state, WC_TYPESET_ARGC(code));
520 	    taddassignlist(state, *state->pc++);
521 	    stack = 1;
522 	    break;
523 	case WC_SUBSH:
524 	    if (!s) {
525 		taddstr("(");
526 		tindent++;
527 		taddnl(1);
528 		n = tpush(code, 1);
529 		n->u._subsh.end = state->pc + WC_SUBSH_SKIP(code);
530 		/* skip word only use for try/always */
531 		state->pc++;
532 	    } else {
533 		state->pc = s->u._subsh.end;
534 		dec_tindent();
535 		/* semicolon is optional here but more standard */
536 		taddnl(0);
537 		taddstr(")");
538 		stack = 1;
539 	    }
540 	    break;
541 	case WC_CURSH:
542 	    if (!s) {
543 		taddstr("{");
544 		tindent++;
545 		taddnl(1);
546 		n = tpush(code, 1);
547 		n->u._subsh.end = state->pc + WC_CURSH_SKIP(code);
548 		/* skip word only use for try/always */
549 		state->pc++;
550 	    } else {
551 		state->pc = s->u._subsh.end;
552 		dec_tindent();
553 		/* semicolon is optional here but more standard */
554 		taddnl(0);
555 		taddstr("}");
556 		stack = 1;
557 	    }
558 	    break;
559 	case WC_TIMED:
560 	    if (!s) {
561 		taddstr("time");
562 		if (WC_TIMED_TYPE(code) == WC_TIMED_PIPE) {
563 		    taddchr(' ');
564 		    tindent++;
565 		    tpush(code, 1);
566 		} else
567 		    stack = 1;
568 	    } else {
569 		dec_tindent();
570 		stack = 1;
571 	    }
572 	    break;
573 	case WC_FUNCDEF:
574 	    if (!s) {
575 		Wordcode p = state->pc;
576 		Wordcode end = p + WC_FUNCDEF_SKIP(code);
577 		int nargs = *state->pc++;
578 
579 		taddlist(state, nargs);
580 		if (nargs)
581 		    taddstr(" ");
582 		if (tjob) {
583 		    taddstr("() { ... }");
584 		    state->pc = end;
585 		    if (!nargs) {
586 			/*
587 			 * Unnamed function.
588 			 * We're not going to pull any arguments off
589 			 * later, so skip them now...
590 			 */
591 			state->pc += *end;
592 		    }
593 		    stack = 1;
594 		} else {
595 		    taddstr("() {");
596 		    tindent++;
597 		    taddnl(1);
598 		    n = tpush(code, 1);
599 		    n->u._funcdef.strs = state->strs;
600 		    n->u._funcdef.end = end;
601 		    n->u._funcdef.nargs = nargs;
602 		    state->strs += *state->pc;
603 		    state->pc += 3;
604 		}
605 	    } else {
606 		state->strs = s->u._funcdef.strs;
607 		state->pc = s->u._funcdef.end;
608 		dec_tindent();
609 		taddnl(0);
610 		taddstr("}");
611 		if (s->u._funcdef.nargs == 0) {
612 		    /* Unnamed function with post-arguments */
613 		    int nargs;
614 		    s->u._funcdef.end += *state->pc++;
615 		    nargs = *state->pc++;
616 		    if (nargs) {
617 			taddstr(" ");
618 			taddlist(state, nargs);
619 		    }
620 		    state->pc = s->u._funcdef.end;
621 		}
622 		stack = 1;
623 	    }
624 	    break;
625 	case WC_FOR:
626 	    if (!s) {
627 		taddstr("for ");
628 		if (WC_FOR_TYPE(code) == WC_FOR_COND) {
629 		    taddstr("((");
630 		    taddstr(ecgetstr(state, EC_NODUP, NULL));
631 		    taddstr("; ");
632 		    taddstr(ecgetstr(state, EC_NODUP, NULL));
633 		    taddstr("; ");
634 		    taddstr(ecgetstr(state, EC_NODUP, NULL));
635 		    taddstr(")) do");
636 		} else {
637 		    taddlist(state, *state->pc++);
638 		    if (WC_FOR_TYPE(code) == WC_FOR_LIST) {
639 			taddstr(" in ");
640 			taddlist(state, *state->pc++);
641 		    }
642 		    taddnl(0);
643 		    taddstr("do");
644 		}
645 		tindent++;
646 		taddnl(0);
647 		tpush(code, 1);
648 	    } else {
649 		dec_tindent();
650 		taddnl(0);
651 		taddstr("done");
652 		stack = 1;
653 	    }
654 	    break;
655 	case WC_SELECT:
656 	    if (!s) {
657 		taddstr("select ");
658 		taddstr(ecgetstr(state, EC_NODUP, NULL));
659 		if (WC_SELECT_TYPE(code) == WC_SELECT_LIST) {
660 		    taddstr(" in ");
661 		    taddlist(state, *state->pc++);
662 		}
663 		taddnl(0);
664 		taddstr("do");
665 		taddnl(0);
666 		tindent++;
667 		tpush(code, 1);
668 	    } else {
669 		dec_tindent();
670 		taddnl(0);
671 		taddstr("done");
672 		stack = 1;
673 	    }
674 	    break;
675 	case WC_WHILE:
676 	    if (!s) {
677 		taddstr(WC_WHILE_TYPE(code) == WC_WHILE_UNTIL ?
678 			"until " : "while ");
679 		tindent++;
680 		tpush(code, 0);
681 	    } else if (!s->pop) {
682 		dec_tindent();
683 		taddnl(0);
684 		taddstr("do");
685 		tindent++;
686 		taddnl(0);
687 		s->pop = 1;
688 	    } else {
689 		dec_tindent();
690 		taddnl(0);
691 		taddstr("done");
692 		stack = 1;
693 	    }
694 	    break;
695 	case WC_REPEAT:
696 	    if (!s) {
697 		taddstr("repeat ");
698 		taddstr(ecgetstr(state, EC_NODUP, NULL));
699 		taddnl(0);
700 		taddstr("do");
701 		tindent++;
702 		taddnl(0);
703 		tpush(code, 1);
704 	    } else {
705 		dec_tindent();
706 		taddnl(0);
707 		taddstr("done");
708 		stack = 1;
709 	    }
710 	    break;
711 	case WC_CASE:
712 	    if (!s) {
713 		Wordcode end = state->pc + WC_CASE_SKIP(code);
714 		wordcode ialts;
715 
716 		taddstr("case ");
717 		taddstr(ecgetstr(state, EC_NODUP, NULL));
718 		taddstr(" in");
719 
720 		if (state->pc >= end) {
721 		    if (tnewlins)
722 			taddnl(0);
723 		    else
724 			taddchr(' ');
725 		    taddstr("esac");
726 		    stack = 1;
727 		} else {
728 		    Wordcode prev_pc;
729 		    tindent++;
730 		    if (tnewlins)
731 			taddnl(0);
732 		    else
733 			taddchr(' ');
734 		    taddstr("(");
735 		    code = *state->pc++;
736 		    prev_pc = state->pc++;
737 		    ialts = *prev_pc;
738 		    while (ialts--) {
739 			taddstr(ecgetstr(state, EC_NODUP, NULL));
740 			state->pc++;
741 			if (ialts)
742 			    taddstr(" | ");
743 		    }
744 		    taddstr(") ");
745 		    tindent++;
746 		    n = tpush(code, 0);
747 		    n->u._case.end = end;
748 		    n->pop = (prev_pc + WC_CASE_SKIP(code) >= end);
749 		}
750 	    } else if (state->pc < s->u._case.end) {
751 		Wordcode prev_pc;
752 		wordcode ialts;
753 		dec_tindent();
754 		switch (WC_CASE_TYPE(code)) {
755 		case WC_CASE_OR:
756 		    taddstr(" ;;");
757 		    break;
758 
759 		case WC_CASE_AND:
760 		    taddstr(" ;&");
761 		    break;
762 
763 		default:
764 		    taddstr(" ;|");
765 		    break;
766 		}
767 		if (tnewlins)
768 		    taddnl(0);
769 		else
770 		    taddchr(' ');
771 		taddstr("(");
772 		code = *state->pc++;
773 		prev_pc = state->pc++;
774 		ialts = *prev_pc;
775 		while (ialts--) {
776 		    taddstr(ecgetstr(state, EC_NODUP, NULL));
777 		    state->pc++;
778 		    if (ialts)
779 			taddstr(" | ");
780 		}
781 		taddstr(") ");
782 		tindent++;
783 		s->code = code;
784 		s->pop = (prev_pc + WC_CASE_SKIP(code) >=
785 			  s->u._case.end);
786 	    } else {
787 		dec_tindent();
788 		switch (WC_CASE_TYPE(code)) {
789 		case WC_CASE_OR:
790 		    taddstr(" ;;");
791 		    break;
792 
793 		case WC_CASE_AND:
794 		    taddstr(" ;&");
795 		    break;
796 
797 		default:
798 		    taddstr(" ;|");
799 		    break;
800 		}
801 		dec_tindent();
802 		if (tnewlins)
803 		    taddnl(0);
804 		else
805 		    taddchr(' ');
806 		taddstr("esac");
807 		stack = 1;
808 	    }
809 	    break;
810 	case WC_IF:
811 	    if (!s) {
812 		Wordcode end = state->pc + WC_IF_SKIP(code);
813 
814 		taddstr("if ");
815 		tindent++;
816 		state->pc++;
817 
818 		n = tpush(code, 0);
819 		n->u._if.end = end;
820 		n->u._if.cond = 1;
821 	    } else if (s->pop) {
822 		stack = 1;
823 	    } else if (s->u._if.cond) {
824 		dec_tindent();
825 		taddnl(0);
826 		taddstr("then");
827 		tindent++;
828 		taddnl(0);
829 		s->u._if.cond = 0;
830 	    } else if (state->pc < s->u._if.end) {
831 		dec_tindent();
832 		taddnl(0);
833 		code = *state->pc++;
834 		if (WC_IF_TYPE(code) == WC_IF_ELIF) {
835 		    taddstr("elif ");
836 		    tindent++;
837 		    s->u._if.cond = 1;
838 		} else {
839 		    taddstr("else");
840 		    tindent++;
841 		    taddnl(0);
842 		}
843 	    } else {
844 		s->pop = 1;
845 		dec_tindent();
846 		taddnl(0);
847 		taddstr("fi");
848 		stack = 1;
849 	    }
850 	    break;
851 	case WC_COND:
852 	    {
853 		int ctype;
854 
855 		if (!s) {
856 		    taddstr("[[ ");
857 		    n = tpush(code, 1);
858 		    n->u._cond.par = 2;
859 		} else if (s->u._cond.par == 2) {
860 		    taddstr(" ]]");
861 		    stack = 1;
862 		    break;
863 		} else if (s->u._cond.par == 1) {
864 		    taddstr(" )");
865 		    stack = 1;
866 		    break;
867 		} else if (WC_COND_TYPE(s->code) == COND_AND) {
868 		    taddstr(" && ");
869 		    code = *state->pc++;
870 		    if (WC_COND_TYPE(code) == COND_OR) {
871 			taddstr("( ");
872 			n = tpush(code, 1);
873 			n->u._cond.par = 1;
874 		    }
875 		} else if (WC_COND_TYPE(s->code) == COND_OR) {
876 		    taddstr(" || ");
877 		    code = *state->pc++;
878 		    if (WC_COND_TYPE(code) == COND_AND) {
879 			taddstr("( ");
880 			n = tpush(code, 1);
881 			n->u._cond.par = 1;
882 		    }
883 		}
884 		while (!stack) {
885 		    switch ((ctype = WC_COND_TYPE(code))) {
886 		    case COND_NOT:
887 			taddstr("! ");
888 			code = *state->pc++;
889 			if (WC_COND_TYPE(code) <= COND_OR) {
890 			    taddstr("( ");
891 			    n = tpush(code, 1);
892 			    n->u._cond.par = 1;
893 			}
894 			break;
895 		    case COND_AND:
896 			n = tpush(code, 1);
897 			n->u._cond.par = 0;
898 			code = *state->pc++;
899 			if (WC_COND_TYPE(code) == COND_OR) {
900 			    taddstr("( ");
901 			    n = tpush(code, 1);
902 			    n->u._cond.par = 1;
903 			}
904 			break;
905 		    case COND_OR:
906 			n = tpush(code, 1);
907 			n->u._cond.par = 0;
908 			code = *state->pc++;
909 			if (WC_COND_TYPE(code) == COND_AND) {
910 			    taddstr("( ");
911 			    n = tpush(code, 1);
912 			    n->u._cond.par = 1;
913 			}
914 			break;
915 		    case COND_MOD:
916 			taddstr(ecgetstr(state, EC_NODUP, NULL));
917 			taddchr(' ');
918 			taddlist(state, WC_COND_SKIP(code));
919 			stack = 1;
920 			break;
921 		    case COND_MODI:
922 			{
923 			    char *name = ecgetstr(state, EC_NODUP, NULL);
924 
925 			    taddstr(ecgetstr(state, EC_NODUP, NULL));
926 			    taddchr(' ');
927 			    taddstr(name);
928 			    taddchr(' ');
929 			    taddstr(ecgetstr(state, EC_NODUP, NULL));
930 			    stack = 1;
931 			}
932 			break;
933 		    default:
934 			if (ctype < COND_MOD) {
935 			    /* Binary test: `a = b' etc. */
936 			    taddstr(ecgetstr(state, EC_NODUP, NULL));
937 			    taddstr(" ");
938 			    taddstr(cond_binary_ops[ctype - COND_STREQ]);
939 			    taddstr(" ");
940 			    taddstr(ecgetstr(state, EC_NODUP, NULL));
941 			    if (ctype == COND_STREQ ||
942 				ctype == COND_STRDEQ ||
943 				ctype == COND_STRNEQ)
944 				state->pc++;
945 			} else {
946 			    /* Unary test: `-f foo' etc. */
947 			    char c2[4];
948 
949 			    c2[0] = '-';
950 			    c2[1] = ctype;
951 			    c2[2] = ' ';
952 			    c2[3] = '\0';
953 			    taddstr(c2);
954 			    taddstr(ecgetstr(state, EC_NODUP, NULL));
955 			}
956 			stack = 1;
957 			break;
958 		    }
959 		}
960 	    }
961 	    break;
962 	case WC_ARITH:
963 	    taddstr("((");
964 	    taddstr(ecgetstr(state, EC_NODUP, NULL));
965 	    taddstr("))");
966 	    stack = 1;
967 	    break;
968 	case WC_AUTOFN:
969 	    taddstr("builtin autoload -X");
970 	    stack = 1;
971 	    break;
972 	case WC_TRY:
973 	    if (!s) {
974 		taddstr("{");
975 		tindent++;
976 		taddnl(0);
977 		n = tpush(code, 0);
978 		state->pc++;
979 		/* this is the end of the try block alone */
980 		n->u._subsh.end = state->pc + WC_CURSH_SKIP(state->pc[-1]);
981 	    } else if (!s->pop) {
982 		state->pc = s->u._subsh.end;
983 		dec_tindent();
984 		taddnl(0);
985 		taddstr("} always {");
986 		tindent++;
987 		taddnl(0);
988 		s->pop = 1;
989 	    } else {
990 		dec_tindent();
991 		taddnl(0);
992 		taddstr("}");
993 		stack = 1;
994 	    }
995 	    break;
996 	case WC_END:
997 	    stack = 1;
998 	    break;
999 	default:
1000 	    DPUTS(1, "unknown word code in gettext2()");
1001 	    return;
1002 	}
1003     }
1004     tdopending();
1005 }
1006 
1007 /**/
1008 void
getredirs(LinkList redirs)1009 getredirs(LinkList redirs)
1010 {
1011     LinkNode n;
1012     static char *fstr[] =
1013     {
1014 	">", ">|", ">>", ">>|", "&>", "&>|", "&>>", "&>>|", "<>", "<",
1015 	"<<", "<<-", "<<<", "<&", ">&", NULL /* >&- */, "<", ">"
1016     };
1017 
1018     queue_signals();
1019 
1020     taddchr(' ');
1021     for (n = firstnode(redirs); n; incnode(n)) {
1022 	Redir f = (Redir) getdata(n);
1023 
1024 	switch (f->type) {
1025 	case REDIR_WRITE:
1026 	case REDIR_WRITENOW:
1027 	case REDIR_APP:
1028 	case REDIR_APPNOW:
1029 	case REDIR_ERRWRITE:
1030 	case REDIR_ERRWRITENOW:
1031 	case REDIR_ERRAPP:
1032 	case REDIR_ERRAPPNOW:
1033 	case REDIR_READ:
1034 	case REDIR_READWRITE:
1035 	case REDIR_HERESTR:
1036 	case REDIR_MERGEIN:
1037 	case REDIR_MERGEOUT:
1038 	case REDIR_INPIPE:
1039 	case REDIR_OUTPIPE:
1040 	    if (f->varid) {
1041 		taddchr('{');
1042 		taddstr(f->varid);
1043 		taddchr('}');
1044 	    } else if (f->fd1 != (IS_READFD(f->type) ? 0 : 1))
1045 		taddchr('0' + f->fd1);
1046 	    if (f->type == REDIR_HERESTR &&
1047 		(f->flags & REDIRF_FROM_HEREDOC)) {
1048 		if (tnewlins) {
1049 		    /*
1050 		     * Strings that came from here-documents are converted
1051 		     * to here strings without quotation, so convert them
1052 		     * back.
1053 		     */
1054 		    taddstr(fstr[REDIR_HEREDOC]);
1055 		    taddstr(f->here_terminator);
1056 		    taddpending(f->name, f->munged_here_terminator);
1057 		} else {
1058 		    int fnamelen, sav;
1059 		    taddstr(fstr[REDIR_HERESTR]);
1060 		    /*
1061 		     * Just a quick and dirty representation.
1062 		     * Remove a terminating newline, if any.
1063 		     */
1064 		    fnamelen = strlen(f->name);
1065 		    if (fnamelen > 0 && f->name[fnamelen-1] == '\n') {
1066 			sav = 1;
1067 			f->name[fnamelen-1] = '\0';
1068 		    } else
1069 			sav = 0;
1070 		    /*
1071 		     * Strings that came from here-documents are converted
1072 		     * to here strings without quotation, so add that
1073 		     * now.  If tokens are present we need to do double quoting.
1074 		     */
1075 		    if (!has_token(f->name)) {
1076 			taddchr('\'');
1077 			taddstr(quotestring(f->name, QT_SINGLE));
1078 			taddchr('\'');
1079 		    } else {
1080 			taddchr('"');
1081 			taddstr(quotestring(f->name, QT_DOUBLE));
1082 			taddchr('"');
1083 		    }
1084 		    if (sav)
1085 			f->name[fnamelen-1] = '\n';
1086 		}
1087 	    } else {
1088 		taddstr(fstr[f->type]);
1089 		if (f->type != REDIR_MERGEIN && f->type != REDIR_MERGEOUT)
1090 		    taddchr(' ');
1091 		taddstr(f->name);
1092 	    }
1093 	    taddchr(' ');
1094 	    break;
1095 #ifdef DEBUG
1096 	case REDIR_CLOSE:
1097 	    DPUTS(1, "BUG: CLOSE in getredirs()");
1098 	    taddchr(f->fd1 + '0');
1099 	    taddstr(">&- ");
1100 	    break;
1101 	default:
1102 	    DPUTS(1, "BUG: unknown redirection in getredirs()");
1103 #endif
1104 	}
1105     }
1106     tptr--;
1107 
1108     unqueue_signals();
1109 }
1110