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