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