1/***********************************************************************
2*                                                                      *
3*               This software is part of the ast package               *
4*          Copyright (c) 1982-2011 AT&T Intellectual Property          *
5*                      and is licensed under the                       *
6*                  Common Public License, Version 1.0                  *
7*                    by AT&T Intellectual Property                     *
8*                                                                      *
9*                A copy of the License is available at                 *
10*            http://www.opensource.org/licenses/cpl1.0.txt             *
11*         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
12*                                                                      *
13*              Information and Software Systems Research               *
14*                            AT&T Research                             *
15*                           Florham Park NJ                            *
16*                                                                      *
17*                  David Korn <dgk@research.att.com>                   *
18*                                                                      *
19***********************************************************************/
20#pragma prototyped
21/*
22 * David Korn
23 * AT&T Labs
24 *
25 * shell deparser
26 *
27 */
28
29#include	"defs.h"
30#include	"shnodes.h"
31#include	"test.h"
32
33
34#define HUGE_INT	(((unsigned)-1)>>1)
35#define	BEGIN	0
36#define MIDDLE	1
37#define	END	2
38#define PRE	1
39#define POST	2
40
41
42/* flags that can be specified with p_tree() */
43#define NO_NEWLINE	1
44#define NEED_BRACE	2
45#define NO_BRACKET	4
46
47static void p_comlist(const struct dolnod*,int);
48static void p_arg(const struct argnod*, int endchar, int opts);
49static void p_comarg(const struct comnod*);
50static void p_keyword(const char*,int);
51static void p_redirect(const struct ionod*);
52static void p_switch(const struct regnod*);
53static void here_body(const struct ionod*);
54static void p_tree(const Shnode_t*,int);
55
56static int level;
57static int begin_line;
58static int end_line;
59static char io_op[7];
60static char un_op[3] = "-?";
61static const struct ionod *here_doc;
62static Sfio_t *outfile;
63static const char *forinit = "";
64
65extern void sh_deparse(Sfio_t*, const Shnode_t*,int);
66
67void sh_deparse(Sfio_t *out, const Shnode_t *t,int tflags)
68{
69	outfile = out;
70	p_tree(t,tflags);
71}
72/*
73 * print script corresponding to shell tree <t>
74 */
75static void p_tree(register const Shnode_t *t,register int tflags)
76{
77	register char *cp;
78	int save = end_line;
79	int needbrace = (tflags&NEED_BRACE);
80	tflags &= ~NEED_BRACE;
81	if(tflags&NO_NEWLINE)
82		end_line = ' ';
83	else
84		end_line = '\n';
85	switch(t->tre.tretyp&COMMSK)
86	{
87		case TTIME:
88			if(t->tre.tretyp&COMSCAN)
89				p_keyword("!",BEGIN);
90			else
91				p_keyword("time",BEGIN);
92			if(t->par.partre)
93				p_tree(t->par.partre,tflags);
94			level--;
95			break;
96
97		case TCOM:
98			if(begin_line && level>0)
99				sfnputc(outfile,'\t',level);
100			begin_line = 0;
101			p_comarg((struct comnod*)t);
102			break;
103
104		case TSETIO:
105			if(t->tre.tretyp&FPCL)
106				tflags |= NEED_BRACE;
107			else
108				tflags = NO_NEWLINE|NEED_BRACE;
109			p_tree(t->fork.forktre,tflags);
110			p_redirect(t->fork.forkio);
111			break;
112
113		case TFORK:
114			if(needbrace)
115				tflags |= NEED_BRACE;
116			if(t->tre.tretyp&(FAMP|FCOOP))
117			{
118				tflags = NEED_BRACE|NO_NEWLINE;
119				end_line = ' ';
120			}
121			else if(t->fork.forkio)
122				tflags = NO_NEWLINE;
123			p_tree(t->fork.forktre,tflags);
124			if(t->fork.forkio)
125				p_redirect(t->fork.forkio);
126			if(t->tre.tretyp&FCOOP)
127			{
128				sfputr(outfile,"|&",'\n');
129				begin_line = 1;
130			}
131			else if(t->tre.tretyp&FAMP)
132			{
133				sfputr(outfile,"&",'\n');
134				begin_line = 1;
135			}
136			break;
137
138		case TIF:
139			p_keyword("if",BEGIN);
140			p_tree(t->if_.iftre,0);
141			p_keyword("then",MIDDLE);
142			p_tree(t->if_.thtre,0);
143			if(t->if_.eltre)
144			{
145				p_keyword("else",MIDDLE);
146				p_tree(t->if_.eltre,0);
147			}
148			p_keyword("fi",END);
149			break;
150
151		case TWH:
152			if(t->wh.whinc)
153				cp = "for";
154			else if(t->tre.tretyp&COMSCAN)
155				cp = "until";
156			else
157				cp = "while";
158			p_keyword(cp,BEGIN);
159			if(t->wh.whinc)
160			{
161				struct argnod *arg = (t->wh.whtre)->ar.arexpr;
162				sfprintf(outfile,"(( %s; ",forinit);
163				forinit = "";
164				sfputr(outfile,arg->argval,';');
165				arg = (t->wh.whinc)->arexpr;
166				sfprintf(outfile," %s))\n",arg->argval);
167			}
168			else
169				p_tree(t->wh.whtre,0);
170			t = t->wh.dotre;
171			goto dolist;
172
173		case TLST:
174		{
175			Shnode_t *tr = t->lst.lstrit;
176			if(tr->tre.tretyp==TWH && tr->wh.whinc && t->lst.lstlef->tre.tretyp==TARITH)
177			{
178				/* arithmetic for statement */
179				struct argnod *init = (t->lst.lstlef)->ar.arexpr;
180				forinit= init->argval;
181				p_tree(t->lst.lstrit,tflags);
182				break;
183			}
184			if(needbrace)
185				p_keyword("{",BEGIN);
186			p_tree(t->lst.lstlef,0);
187			if(needbrace)
188				tflags = 0;
189			p_tree(t->lst.lstrit,tflags);
190			if(needbrace)
191				p_keyword("}",END);
192			break;
193		}
194
195		case TAND:
196			cp = "&&";
197			goto andor;
198		case TORF:
199			cp = "||";
200			goto andor;
201		case TFIL:
202			cp = "|";
203		andor:
204		{
205			int bracket = 0;
206			if(t->tre.tretyp&TTEST)
207			{
208				tflags |= NO_NEWLINE;
209				if(!(tflags&NO_BRACKET))
210				{
211					p_keyword("[[",BEGIN);
212					tflags |= NO_BRACKET;
213					bracket=1;
214				}
215			}
216			p_tree(t->lst.lstlef,NEED_BRACE|NO_NEWLINE|(tflags&NO_BRACKET));
217			if(tflags&FALTPIPE)
218			{
219				Shnode_t *tt = t->lst.lstrit;
220				if(tt->tre.tretyp!=TFIL || !(tt->lst.lstlef->tre.tretyp&FALTPIPE))
221				{
222					sfputc(outfile,'\n');
223					return;
224				}
225			}
226			sfputr(outfile,cp,here_doc?'\n':' ');
227			if(here_doc)
228			{
229				here_body(here_doc);
230				here_doc = 0;
231			}
232			level++;
233			p_tree(t->lst.lstrit,tflags|NEED_BRACE);
234			if(bracket)
235				p_keyword("]]",END);
236			level--;
237			break;
238		}
239
240		case TPAR:
241			p_keyword("(",BEGIN);
242			p_tree(t->par.partre,0);
243			p_keyword(")",END);
244			break;
245
246		case TARITH:
247		{
248			register struct argnod *ap = t->ar.arexpr;
249			if(begin_line && level)
250				sfnputc(outfile,'\t',level);
251			sfprintf(outfile,"(( %s ))%c",ap->argval,end_line);
252			if(!(tflags&NO_NEWLINE))
253				begin_line=1;
254			break;
255		}
256
257		case TFOR:
258			cp = ((t->tre.tretyp&COMSCAN)?"select":"for");
259			p_keyword(cp,BEGIN);
260			sfputr(outfile,t->for_.fornam,' ');
261			if(t->for_.forlst)
262			{
263				sfputr(outfile,"in",' ');
264				tflags = end_line;
265				end_line = '\n';
266				p_comarg(t->for_.forlst);
267				end_line = tflags;
268			}
269			else
270				sfputc(outfile,'\n');
271			begin_line = 1;
272			t = t->for_.fortre;
273		dolist:
274			p_keyword("do",MIDDLE);
275			p_tree(t,0);
276			p_keyword("done",END);
277			break;
278
279		case TSW:
280			p_keyword("case",BEGIN);
281			p_arg(t->sw.swarg,' ',0);
282			if(t->sw.swlst)
283			{
284				begin_line = 1;
285				sfputr(outfile,"in",'\n');
286				tflags = end_line;
287				end_line = '\n';
288				p_switch(t->sw.swlst);
289				end_line = tflags;
290			}
291			p_keyword("esac",END);
292			break;
293
294		case TFUN:
295			if(t->tre.tretyp&FPOSIX)
296			{
297				sfprintf(outfile,"%s",t->funct.functnam);
298				p_keyword("()\n",BEGIN);
299			}
300			else
301			{
302				p_keyword("function",BEGIN);
303				tflags = (t->funct.functargs?' ':'\n');
304				sfputr(outfile,t->funct.functnam,tflags);
305				if(t->funct.functargs)
306				{
307					tflags = end_line;
308					end_line = '\n';
309					p_comarg(t->funct.functargs);
310					end_line = tflags;
311				}
312			}
313			begin_line = 1;
314			p_keyword("{\n",MIDDLE);
315			begin_line = 1;
316			p_tree(t->funct.functtre,0);
317			p_keyword("}",END);
318			break;
319		/* new test compound command */
320		case TTST:
321			if(!(tflags&NO_BRACKET))
322				p_keyword("[[",BEGIN);
323			if((t->tre.tretyp&TPAREN)==TPAREN)
324			{
325				p_keyword("(",BEGIN);
326				p_tree(t->lst.lstlef,NO_BRACKET|NO_NEWLINE);
327				p_keyword(")",END);
328			}
329			else
330			{
331				int flags = (t->tre.tretyp)>>TSHIFT;
332				if(t->tre.tretyp&TNEGATE)
333					sfputr(outfile,"!",' ');
334				if(t->tre.tretyp&TUNARY)
335				{
336					un_op[1] = flags;
337					sfputr(outfile,un_op,' ');
338				}
339				else
340					cp = ((char*)(shtab_testops+(flags&037)-1)->sh_name);
341				p_arg(&(t->lst.lstlef->arg),' ',0);
342				if(t->tre.tretyp&TBINARY)
343				{
344					sfputr(outfile,cp,' ');
345					p_arg(&(t->lst.lstrit->arg),' ',0);
346				}
347			}
348			if(!(tflags&NO_BRACKET))
349				p_keyword("]]",END);
350	}
351	while(begin_line && here_doc)
352	{
353		here_body(here_doc);
354		here_doc = 0;
355	}
356	end_line = save;
357	return;
358}
359
360/*
361 * print a keyword
362 * increment indent level for flag==BEGIN
363 * decrement indent level for flag==END
364 */
365static void p_keyword(const char *word,int flag)
366{
367	register int sep;
368	if(flag==END)
369		sep = end_line;
370	else if(*word=='[' || *word=='(')
371		sep = ' ';
372	else
373		sep = '\t';
374	if(flag!=BEGIN)
375		level--;
376	if(begin_line && level)
377		sfnputc(outfile,'\t',level);
378	sfputr(outfile,word,sep);
379	if(sep=='\n')
380		begin_line=1;
381	else
382		begin_line=0;
383	if(flag!=END)
384		level++;
385}
386
387static void p_arg(register const struct argnod *arg,register int endchar,int opts)
388{
389	register const char *cp;
390	register int flag;
391	do
392	{
393		if(!arg->argnxt.ap)
394			flag = endchar;
395		else if(opts&PRE)
396		{
397			/* case alternation lists in reverse order */
398			p_arg(arg->argnxt.ap,'|',opts);
399			flag = endchar;
400		}
401		else if(opts)
402			flag = ' ';
403		cp = arg->argval;
404		if(*cp==0 && opts==POST && arg->argchn.ap)
405		{
406			/* compound assignment */
407			struct fornod *fp=(struct fornod*)arg->argchn.ap;
408			sfprintf(outfile,"%s=(\n",fp->fornam);
409			sfnputc(outfile,'\t',++level);
410			p_tree(fp->fortre,0);
411			if(--level)
412				sfnputc(outfile,'\t',level);
413			sfputc(outfile,')');
414		}
415		else if((arg->argflag&ARG_RAW) && (cp[1] || (*cp!='[' && *cp!=']')))
416			cp = sh_fmtq(cp);
417		sfputr(outfile,cp,flag);
418		if(flag=='\n')
419			begin_line = 1;
420		arg = arg->argnxt.ap;
421	}
422	while((opts&POST) && arg);
423	return;
424}
425
426static void p_redirect(register const struct ionod *iop)
427{
428	register char *cp;
429	register int iof,iof2;
430	for(;iop;iop=iop->ionxt)
431	{
432		iof=iop->iofile;
433		cp = io_op;
434		if(iop->iovname)
435		{
436			sfwrite(outfile,"(;",2);
437			sfputr(outfile,iop->iovname,')');
438			cp++;
439		}
440		else
441			*cp = '0'+(iof&IOUFD);
442		if(iof&IOPUT)
443		{
444			if(*cp == '1' && !iop->iovname)
445				cp++;
446			io_op[1] = '>';
447		}
448		else
449		{
450			if(*cp == '0' && !iop->iovname)
451				cp++;
452			io_op[1] = '<';
453		}
454		io_op[2] = 0;
455		io_op[3] = 0;
456		if(iof&IOLSEEK)
457		{
458			io_op[1] = '#';
459			if(iof&IOARITH)
460				strcpy(&io_op[3]," ((");
461		}
462		else if(iof&IOMOV)
463			io_op[2] = '&';
464		else if(iof&(IORDW|IOAPP))
465			io_op[2] = '>';
466		else if(iof&IOCLOB)
467			io_op[2] = '|';
468		if(iop->iodelim)
469		{
470			/* here document */
471#ifdef xxx
472			iop->iolink = (char*)here_doc;
473#endif
474			here_doc  = iop;
475			io_op[2] = '<';
476#ifdef future
477			if(iof&IOSTRIP)
478				io_op[3] = '-';
479#endif
480		}
481		sfputr(outfile,cp,' ');
482		if(iop->ionxt)
483			iof = ' ';
484		else
485		{
486			if((iof=end_line)=='\n')
487				begin_line = 1;
488		}
489		if((iof&IOLSEEK) && (iof&IOARITH))
490			iof2 = iof, iof = ' ';
491		if(iop->iodelim)
492		{
493			if(!(iop->iofile&IODOC))
494				sfwrite(outfile,"''",2);
495			sfputr(outfile,sh_fmtq(iop->iodelim),iof);
496		}
497		else if(iop->iofile&IORAW)
498			sfputr(outfile,sh_fmtq(iop->ioname),iof);
499		else
500			sfputr(outfile,iop->ioname,iof);
501		if((iof&IOLSEEK) && (iof&IOARITH))
502			sfputr(outfile, "))", iof2);
503	}
504	return;
505}
506
507static void p_comarg(register const struct comnod *com)
508{
509	register int flag = end_line;
510	if(com->comtyp&FAMP)
511		sfwrite(outfile,"& ",2);
512	if(com->comarg || com->comio)
513		flag = ' ';
514	if(com->comset)
515		p_arg(com->comset,flag,POST);
516	if(com->comarg)
517	{
518		if(!com->comio)
519			flag = end_line;
520		if(com->comtyp&COMSCAN)
521			p_arg(com->comarg,flag,POST);
522		else
523			p_comlist((struct dolnod*)com->comarg,flag);
524	}
525	if(com->comio)
526		p_redirect(com->comio);
527	return;
528}
529
530static void p_comlist(const struct dolnod *dol,int endchar)
531{
532	register char *cp, *const*argv;
533	register int flag = ' ', special;
534	argv = dol->dolval+ARG_SPARE;
535	cp = *argv;
536	special = (*cp=='[' && cp[1]==0);
537	do
538	{
539		if(cp)
540			argv++;
541		else
542			cp = "";
543		if(*argv==0)
544		{
545			if((flag=endchar)=='\n')
546				begin_line = 1;
547			special = (*cp==']' && cp[1]==0);
548		}
549		sfputr(outfile,special?cp:sh_fmtq(cp),flag);
550		special = 0;
551	}
552	while(cp  = *argv);
553	return;
554}
555
556static void p_switch(register const struct regnod *reg)
557{
558	if(level>1)
559		sfnputc(outfile,'\t',level-1);
560	p_arg(reg->regptr,')',PRE);
561	begin_line = 0;
562	sfputc(outfile,'\t');
563	if(reg->regcom)
564		p_tree(reg->regcom,0);
565	level++;
566	if(reg->regflag)
567		p_keyword(";&",END);
568	else
569		p_keyword(";;",END);
570	if(reg->regnxt)
571		p_switch(reg->regnxt);
572	return;
573}
574
575/*
576 * output here documents
577 */
578static void here_body(register const struct ionod *iop)
579{
580	Sfio_t *infile;
581#ifdef xxx
582	if(iop->iolink)
583		here_body((struct inode*)iop->iolink);
584	iop->iolink = 0;
585#endif
586	if(iop->iofile&IOSTRG)
587		infile = sfnew((Sfio_t*)0,iop->ioname,iop->iosize,-1,SF_STRING|SF_READ);
588	else
589		sfseek(infile=sh.heredocs,iop->iooffset,SEEK_SET);
590	sfmove(infile,outfile,iop->iosize,-1);
591	if(iop->iofile&IOSTRG)
592		sfclose(infile);
593	sfputr(outfile,iop->iodelim,'\n');
594}
595
596