1 /***********************************************************************
2 * *
3 * This software is part of the ast package *
4 * Copyright (c) 1982-2010 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
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 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 */
p_keyword(const char * word,int flag)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
p_arg(register const struct argnod * arg,register int endchar,int opts)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
p_redirect(register const struct ionod * iop)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
p_comarg(register const struct comnod * com)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
p_comlist(const struct dolnod * dol,int endchar)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
p_switch(register const struct regnod * reg)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 */
here_body(register const struct ionod * iop)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