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