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 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 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 */ 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 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 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 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 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 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 */ 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