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