xref: /titanic_50/usr/src/lib/libshell/common/sh/deparse.c (revision b5d3ab78446c645a1150b57b7a58b535229ee742)
1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *           Copyright (c) 1982-2007 AT&T Knowledge Ventures            *
5 *                      and is licensed under the                       *
6 *                  Common Public License, Version 1.0                  *
7 *                      by AT&T Knowledge Ventures                      *
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 
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 
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  */
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 			sfputr(outfile,cp,here_doc?'\n':' ');
218 			if(here_doc)
219 			{
220 				here_body(here_doc);
221 				here_doc = 0;
222 			}
223 			level++;
224 			p_tree(t->lst.lstrit,tflags|NEED_BRACE);
225 			if(bracket)
226 				p_keyword("]]",END);
227 			level--;
228 			break;
229 		}
230 
231 		case TPAR:
232 			p_keyword("(",BEGIN);
233 			p_tree(t->par.partre,0);
234 			p_keyword(")",END);
235 			break;
236 
237 		case TARITH:
238 		{
239 			register struct argnod *ap = t->ar.arexpr;
240 			if(begin_line && level)
241 				sfnputc(outfile,'\t',level);
242 			sfprintf(outfile,"(( %s ))%c",ap->argval,end_line);
243 			if(!(tflags&NO_NEWLINE))
244 				begin_line=1;
245 			break;
246 		}
247 
248 		case TFOR:
249 			cp = ((t->tre.tretyp&COMSCAN)?"select":"for");
250 			p_keyword(cp,BEGIN);
251 			sfputr(outfile,t->for_.fornam,' ');
252 			if(t->for_.forlst)
253 			{
254 				sfputr(outfile,"in",' ');
255 				tflags = end_line;
256 				end_line = '\n';
257 				p_comarg(t->for_.forlst);
258 				end_line = tflags;
259 			}
260 			else
261 				sfputc(outfile,'\n');
262 			begin_line = 1;
263 			t = t->for_.fortre;
264 		dolist:
265 			p_keyword("do",MIDDLE);
266 			p_tree(t,0);
267 			p_keyword("done",END);
268 			break;
269 
270 		case TSW:
271 			p_keyword("case",BEGIN);
272 			p_arg(t->sw.swarg,' ',0);
273 			if(t->sw.swlst)
274 			{
275 				begin_line = 1;
276 				sfputr(outfile,"in",'\n');
277 				tflags = end_line;
278 				end_line = '\n';
279 				p_switch(t->sw.swlst);
280 				end_line = tflags;
281 			}
282 			p_keyword("esac",END);
283 			break;
284 
285 		case TFUN:
286 			if(t->tre.tretyp&FPOSIX)
287 			{
288 				sfprintf(outfile,"%s",t->funct.functnam);
289 				p_keyword("()\n",BEGIN);
290 			}
291 			else
292 			{
293 				p_keyword("function",BEGIN);
294 				tflags = (t->funct.functargs?' ':'\n');
295 				sfputr(outfile,t->funct.functnam,tflags);
296 				if(t->funct.functargs)
297 				{
298 					tflags = end_line;
299 					end_line = '\n';
300 					p_comarg(t->funct.functargs);
301 					end_line = tflags;
302 				}
303 			}
304 			begin_line = 1;
305 			p_keyword("{\n",MIDDLE);
306 			begin_line = 1;
307 			p_tree(t->funct.functtre,0);
308 			p_keyword("}",END);
309 			break;
310 		/* new test compound command */
311 		case TTST:
312 			if(!(tflags&NO_BRACKET))
313 				p_keyword("[[",BEGIN);
314 			if((t->tre.tretyp&TPAREN)==TPAREN)
315 			{
316 				p_keyword("(",BEGIN);
317 				p_tree(t->lst.lstlef,NO_BRACKET|NO_NEWLINE);
318 				p_keyword(")",END);
319 			}
320 			else
321 			{
322 				int flags = (t->tre.tretyp)>>TSHIFT;
323 				if(t->tre.tretyp&TNEGATE)
324 					sfputr(outfile,"!",' ');
325 				if(t->tre.tretyp&TUNARY)
326 				{
327 					un_op[1] = flags;
328 					sfputr(outfile,un_op,' ');
329 				}
330 				else
331 					cp = ((char*)(shtab_testops+(flags&037)-1)->sh_name);
332 				p_arg(&(t->lst.lstlef->arg),' ',0);
333 				if(t->tre.tretyp&TBINARY)
334 				{
335 					sfputr(outfile,cp,' ');
336 					p_arg(&(t->lst.lstrit->arg),' ',0);
337 				}
338 			}
339 			if(!(tflags&NO_BRACKET))
340 				p_keyword("]]",END);
341 	}
342 	while(begin_line && here_doc)
343 	{
344 		here_body(here_doc);
345 		here_doc = 0;
346 	}
347 	end_line = save;
348 	return;
349 }
350 
351 /*
352  * print a keyword
353  * increment indent level for flag==BEGIN
354  * decrement indent level for flag==END
355  */
356 static void p_keyword(const char *word,int flag)
357 {
358 	register int sep;
359 	if(flag==END)
360 		sep = end_line;
361 	else if(*word=='[' || *word=='(')
362 		sep = ' ';
363 	else
364 		sep = '\t';
365 	if(flag!=BEGIN)
366 		level--;
367 	if(begin_line && level)
368 		sfnputc(outfile,'\t',level);
369 	sfputr(outfile,word,sep);
370 	if(sep=='\n')
371 		begin_line=1;
372 	else
373 		begin_line=0;
374 	if(flag!=END)
375 		level++;
376 }
377 
378 static void p_arg(register const struct argnod *arg,register int endchar,int opts)
379 {
380 	register const char *cp;
381 	register int flag;
382 	do
383 	{
384 		if(!arg->argnxt.ap)
385 			flag = endchar;
386 		else if(opts&PRE)
387 		{
388 			/* case alternation lists in reverse order */
389 			p_arg(arg->argnxt.ap,'|',opts);
390 			flag = endchar;
391 		}
392 		else if(opts)
393 			flag = ' ';
394 		cp = arg->argval;
395 		if(*cp==0 && opts==POST && arg->argchn.ap)
396 		{
397 			/* compound assignment */
398 			struct fornod *fp=(struct fornod*)arg->argchn.ap;
399 			sfprintf(outfile,"%s=(\n",fp->fornam);
400 			sfnputc(outfile,'\t',++level);
401 			p_tree(fp->fortre,0);
402 			if(--level)
403 				sfnputc(outfile,'\t',level);
404 			sfputc(outfile,')');
405 		}
406 		else if((arg->argflag&ARG_RAW) && (cp[1] || (*cp!='[' && *cp!=']')))
407 			cp = sh_fmtq(cp);
408 		sfputr(outfile,cp,flag);
409 		if(flag=='\n')
410 			begin_line = 1;
411 		arg = arg->argnxt.ap;
412 	}
413 	while((opts&POST) && arg);
414 	return;
415 }
416 
417 static void p_redirect(register const struct ionod *iop)
418 {
419 	register char *cp;
420 	register int iof,iof2;
421 	for(;iop;iop=iop->ionxt)
422 	{
423 		iof=iop->iofile;
424 		cp = io_op;
425 		if(iop->iovname)
426 		{
427 			sfwrite(outfile,"(;",2);
428 			sfputr(outfile,iop->iovname,')');
429 			cp++;
430 		}
431 		else
432 			*cp = '0'+(iof&IOUFD);
433 		if(iof&IOPUT)
434 		{
435 			if(*cp == '1' && !iop->iovname)
436 				cp++;
437 			io_op[1] = '>';
438 		}
439 		else
440 		{
441 			if(*cp == '0' && !iop->iovname)
442 				cp++;
443 			io_op[1] = '<';
444 		}
445 		io_op[2] = 0;
446 		io_op[3] = 0;
447 		if(iof&IOLSEEK)
448 		{
449 			io_op[1] = '#';
450 			if(iof&IOARITH)
451 				strcpy(&io_op[3]," ((");
452 		}
453 		else if(iof&IOMOV)
454 			io_op[2] = '&';
455 		else if(iof&(IORDW|IOAPP))
456 			io_op[2] = '>';
457 		else if(iof&IOCLOB)
458 			io_op[2] = '|';
459 		if(iop->iodelim)
460 		{
461 			/* here document */
462 #ifdef xxx
463 			iop->iolink = (char*)here_doc;
464 #endif
465 			here_doc  = iop;
466 			io_op[2] = '<';
467 #ifdef future
468 			if(iof&IOSTRIP)
469 				io_op[3] = '-';
470 #endif
471 		}
472 		sfputr(outfile,cp,' ');
473 		if(iop->ionxt)
474 			iof = ' ';
475 		else
476 		{
477 			if((iof=end_line)=='\n')
478 				begin_line = 1;
479 		}
480 		if((iof&IOLSEEK) && (iof&IOARITH))
481 			iof2 = iof, iof = ' ';
482 		if(iop->iodelim)
483 		{
484 			if(!(iop->iofile&IODOC))
485 				sfwrite(outfile,"''",2);
486 			sfputr(outfile,sh_fmtq(iop->iodelim),iof);
487 		}
488 		else if(iop->iofile&IORAW)
489 			sfputr(outfile,sh_fmtq(iop->ioname),iof);
490 		else
491 			sfputr(outfile,iop->ioname,iof);
492 		if((iof&IOLSEEK) && (iof&IOARITH))
493 			sfputr(outfile, "))", iof2);
494 	}
495 	return;
496 }
497 
498 static void p_comarg(register const struct comnod *com)
499 {
500 	register int flag = end_line;
501 	if(com->comarg || com->comio)
502 		flag = ' ';
503 	if(com->comset)
504 		p_arg(com->comset,flag,POST);
505 	if(com->comarg)
506 	{
507 		if(!com->comio)
508 			flag = end_line;
509 		if(com->comtyp&COMSCAN)
510 			p_arg(com->comarg,flag,POST);
511 		else
512 			p_comlist((struct dolnod*)com->comarg,flag);
513 	}
514 	if(com->comio)
515 		p_redirect(com->comio);
516 	return;
517 }
518 
519 static void p_comlist(const struct dolnod *dol,int endchar)
520 {
521 	register char *cp, *const*argv;
522 	register int flag = ' ', special;
523 	argv = dol->dolval+ARG_SPARE;
524 	cp = *argv;
525 	special = (*cp=='[' && cp[1]==0);
526 	do
527 	{
528 		if(cp)
529 			argv++;
530 		else
531 			cp = "";
532 		if(*argv==0)
533 		{
534 			if((flag=endchar)=='\n')
535 				begin_line = 1;
536 			special = (*cp==']' && cp[1]==0);
537 		}
538 		sfputr(outfile,special?cp:sh_fmtq(cp),flag);
539 		special = 0;
540 	}
541 	while(cp  = *argv);
542 	return;
543 }
544 
545 static void p_switch(register const struct regnod *reg)
546 {
547 	if(level>1)
548 		sfnputc(outfile,'\t',level-1);
549 	p_arg(reg->regptr,')',PRE);
550 	begin_line = 0;
551 	sfputc(outfile,'\t');
552 	if(reg->regcom)
553 		p_tree(reg->regcom,0);
554 	level++;
555 	if(reg->regflag)
556 		p_keyword(";&",END);
557 	else
558 		p_keyword(";;",END);
559 	if(reg->regnxt)
560 		p_switch(reg->regnxt);
561 	return;
562 }
563 
564 /*
565  * output here documents
566  */
567 static void here_body(register const struct ionod *iop)
568 {
569 	Sfio_t *infile;
570 #ifdef xxx
571 	if(iop->iolink)
572 		here_body((struct inode*)iop->iolink);
573 	iop->iolink = 0;
574 #endif
575 	if(iop->iofile&IOSTRG)
576 		infile = sfnew((Sfio_t*)0,iop->ioname,iop->iosize,-1,SF_STRING|SF_READ);
577 	else
578 		sfseek(infile=sh.heredocs,iop->iooffset,SEEK_SET);
579 	sfmove(infile,outfile,iop->iosize,-1);
580 	if(iop->iofile&IOSTRG)
581 		sfclose(infile);
582 	sfputr(outfile,iop->iodelim,'\n');
583 }
584 
585