xref: /illumos-gate/usr/src/contrib/ast/src/cmd/ksh93/sh/deparse.c (revision b30d193948be5a7794d7ae3ba0ed9c2f72c88e0f)
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 *                 Eclipse Public License, Version 1.0                  *
7 *                    by AT&T Intellectual Property                     *
8 *                                                                      *
9 *                A copy of the License is available at                 *
10 *          http://www.eclipse.org/org/documents/epl-v10.html           *
11 *         (with md5 checksum b35adb5213ca9657e911e9befb180842)         *
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 
47 static void p_comlist(const struct dolnod*,int);
48 static void p_arg(const struct argnod*, int endchar, int opts);
49 static void p_comarg(const struct comnod*);
50 static void p_keyword(const char*,int);
51 static void p_redirect(const struct ionod*);
52 static void p_switch(const struct regnod*);
53 static void here_body(const struct ionod*);
54 static void p_tree(const Shnode_t*,int);
55 
56 static int level;
57 static int begin_line;
58 static int end_line;
59 static char io_op[7];
60 static char un_op[3] = "-?";
61 static const struct ionod *here_doc;
62 static Sfio_t *outfile;
63 static const char *forinit = "";
64 
65 extern void sh_deparse(Sfio_t*, const Shnode_t*,int);
66 
sh_deparse(Sfio_t * out,const Shnode_t * t,int tflags)67 void 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  */
p_tree(register const Shnode_t * t,register int tflags)75 static 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  */
p_keyword(const char * word,int flag)365 static 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 
p_arg(register const struct argnod * arg,register int endchar,int opts)387 static 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 && (arg->argflag&ARG_EXP)  && arg->argchn.ap)
405 		{
406 			int c = (arg->argflag&ARG_RAW)?'>':'<';
407 			sfputc(outfile,c);
408 			sfputc(outfile,'(');
409 			p_tree((Shnode_t*)arg->argchn.ap,0);
410 			sfputc(outfile,')');
411 		}
412 		else if(*cp==0 && opts==POST && arg->argchn.ap)
413 		{
414 			/* compound assignment */
415 			struct fornod *fp=(struct fornod*)arg->argchn.ap;
416 			sfprintf(outfile,"%s=(\n",fp->fornam);
417 			sfnputc(outfile,'\t',++level);
418 			p_tree(fp->fortre,0);
419 			if(--level)
420 				sfnputc(outfile,'\t',level);
421 			sfputc(outfile,')');
422 		}
423 		else if((arg->argflag&ARG_RAW) && (cp[1] || (*cp!='[' && *cp!=']')))
424 			cp = sh_fmtq(cp);
425 		sfputr(outfile,cp,flag);
426 		if(flag=='\n')
427 			begin_line = 1;
428 		arg = arg->argnxt.ap;
429 	}
430 	while((opts&POST) && arg);
431 	return;
432 }
433 
p_redirect(register const struct ionod * iop)434 static void p_redirect(register const struct ionod *iop)
435 {
436 	register char *cp;
437 	register int iof,iof2;
438 	for(;iop;iop=iop->ionxt)
439 	{
440 		iof=iop->iofile;
441 		cp = io_op;
442 		if(iop->iovname)
443 		{
444 			sfwrite(outfile,"(;",2);
445 			sfputr(outfile,iop->iovname,')');
446 			cp++;
447 		}
448 		else
449 			*cp = '0'+(iof&IOUFD);
450 		if(iof&IOPUT)
451 		{
452 			if(*cp == '1' && !iop->iovname)
453 				cp++;
454 			io_op[1] = '>';
455 		}
456 		else
457 		{
458 			if(*cp == '0' && !iop->iovname)
459 				cp++;
460 			io_op[1] = '<';
461 		}
462 		io_op[2] = 0;
463 		io_op[3] = 0;
464 		if(iof&IOLSEEK)
465 		{
466 			io_op[1] = '#';
467 			if(iof&IOARITH)
468 				strcpy(&io_op[3]," ((");
469 		}
470 		else if(iof&IOMOV)
471 			io_op[2] = '&';
472 		else if(iof&(IORDW|IOAPP))
473 			io_op[2] = '>';
474 		else if(iof&IOCLOB)
475 			io_op[2] = '|';
476 		if(iop->iodelim)
477 		{
478 			/* here document */
479 #ifdef xxx
480 			iop->iolink = (char*)here_doc;
481 #endif
482 			here_doc  = iop;
483 			io_op[2] = '<';
484 #ifdef future
485 			if(iof&IOSTRIP)
486 				io_op[3] = '-';
487 #endif
488 		}
489 		sfputr(outfile,cp,' ');
490 		if(iop->ionxt)
491 			iof = ' ';
492 		else
493 		{
494 			if((iof=end_line)=='\n')
495 				begin_line = 1;
496 		}
497 		if((iof&IOLSEEK) && (iof&IOARITH))
498 			iof2 = iof, iof = ' ';
499 		if(iop->iodelim)
500 		{
501 			if(!(iop->iofile&IODOC))
502 				sfwrite(outfile,"''",2);
503 			sfputr(outfile,sh_fmtq(iop->iodelim),iof);
504 		}
505 		else if(iop->iofile&IORAW)
506 			sfputr(outfile,sh_fmtq(iop->ioname),iof);
507 		else
508 			sfputr(outfile,iop->ioname,iof);
509 		if((iof&IOLSEEK) && (iof&IOARITH))
510 			sfputr(outfile, "))", iof2);
511 	}
512 	return;
513 }
514 
p_comarg(register const struct comnod * com)515 static void p_comarg(register const struct comnod *com)
516 {
517 	register int flag = end_line;
518 	if(com->comtyp&FAMP)
519 		sfwrite(outfile,"& ",2);
520 	if(com->comarg || com->comio)
521 		flag = ' ';
522 	if(com->comset)
523 		p_arg(com->comset,flag,POST);
524 	if(com->comarg)
525 	{
526 		if(!com->comio)
527 			flag = end_line;
528 		if(com->comtyp&COMSCAN)
529 			p_arg(com->comarg,flag,POST);
530 		else
531 			p_comlist((struct dolnod*)com->comarg,flag);
532 	}
533 	if(com->comio)
534 		p_redirect(com->comio);
535 	return;
536 }
537 
p_comlist(const struct dolnod * dol,int endchar)538 static void p_comlist(const struct dolnod *dol,int endchar)
539 {
540 	register char *cp, *const*argv;
541 	register int flag = ' ', special;
542 	argv = dol->dolval+ARG_SPARE;
543 	cp = *argv;
544 	special = (*cp=='[' && cp[1]==0);
545 	do
546 	{
547 		if(cp)
548 			argv++;
549 		else
550 			cp = "";
551 		if(*argv==0)
552 		{
553 			if((flag=endchar)=='\n')
554 				begin_line = 1;
555 			special = (*cp==']' && cp[1]==0);
556 		}
557 		sfputr(outfile,special?cp:sh_fmtq(cp),flag);
558 		special = 0;
559 	}
560 	while(cp  = *argv);
561 	return;
562 }
563 
p_switch(register const struct regnod * reg)564 static void p_switch(register const struct regnod *reg)
565 {
566 	if(level>1)
567 		sfnputc(outfile,'\t',level-1);
568 	p_arg(reg->regptr,')',PRE);
569 	begin_line = 0;
570 	sfputc(outfile,'\t');
571 	if(reg->regcom)
572 		p_tree(reg->regcom,0);
573 	level++;
574 	if(reg->regflag)
575 		p_keyword(";&",END);
576 	else
577 		p_keyword(";;",END);
578 	if(reg->regnxt)
579 		p_switch(reg->regnxt);
580 	return;
581 }
582 
583 /*
584  * output here documents
585  */
here_body(register const struct ionod * iop)586 static void here_body(register const struct ionod *iop)
587 {
588 	Sfio_t *infile;
589 #ifdef xxx
590 	if(iop->iolink)
591 		here_body((struct inode*)iop->iolink);
592 	iop->iolink = 0;
593 #endif
594 	if(iop->iofile&IOSTRG)
595 		infile = sfnew((Sfio_t*)0,iop->ioname,iop->iosize,-1,SF_STRING|SF_READ);
596 	else
597 		sfseek(infile=sh.heredocs,iop->iooffset,SEEK_SET);
598 	sfmove(infile,outfile,iop->iosize,-1);
599 	if(iop->iofile&IOSTRG)
600 		sfclose(infile);
601 	sfputr(outfile,iop->iodelim,'\n');
602 }
603 
604