1 /*********************************************************************** 2 * * 3 * This software is part of the ast package * 4 * Copyright (c) 1982-2009 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 * echo [arg...] 23 * print [-nrps] [-f format] [-u filenum] [arg...] 24 * printf format [arg...] 25 * 26 * David Korn 27 * AT&T Labs 28 */ 29 30 #include "defs.h" 31 #include <error.h> 32 #include <stak.h> 33 #include "io.h" 34 #include "name.h" 35 #include "history.h" 36 #include "builtins.h" 37 #include "streval.h" 38 #include <tmx.h> 39 #include <ccode.h> 40 41 union types_t 42 { 43 unsigned char c; 44 short h; 45 int i; 46 long l; 47 Sflong_t ll; 48 Sfdouble_t ld; 49 double d; 50 float f; 51 char *s; 52 int *ip; 53 char **p; 54 }; 55 56 struct printf 57 { 58 Sffmt_t hdr; 59 int argsize; 60 int intvar; 61 char **nextarg; 62 char *lastarg; 63 char cescape; 64 char err; 65 Shell_t *sh; 66 }; 67 68 static int extend(Sfio_t*,void*, Sffmt_t*); 69 static const char preformat[] = ""; 70 static char *genformat(char*); 71 static int fmtvecho(const char*, struct printf*); 72 static ssize_t fmtbase64(Sfio_t*, char*, int); 73 74 struct print 75 { 76 Shell_t *sh; 77 const char *options; 78 char raw; 79 char echon; 80 }; 81 82 static char* nullarg[] = { 0, 0 }; 83 84 #if !SHOPT_ECHOPRINT 85 int B_echo(int argc, char *argv[],void *extra) 86 { 87 static char bsd_univ; 88 struct print prdata; 89 prdata.options = sh_optecho+5; 90 prdata.raw = prdata.echon = 0; 91 prdata.sh = ((Shbltin_t*)extra)->shp; 92 NOT_USED(argc); 93 /* This mess is because /bin/echo on BSD is different */ 94 if(!prdata.sh->universe) 95 { 96 register char *universe; 97 if(universe=astconf("UNIVERSE",0,0)) 98 bsd_univ = (strcmp(universe,"ucb")==0); 99 prdata.sh->universe = 1; 100 } 101 if(!bsd_univ) 102 return(b_print(0,argv,&prdata)); 103 prdata.options = sh_optecho; 104 prdata.raw = 1; 105 while(argv[1] && *argv[1]=='-') 106 { 107 if(strcmp(argv[1],"-n")==0) 108 prdata.echon = 1; 109 #if !SHOPT_ECHOE 110 else if(strcmp(argv[1],"-e")==0) 111 prdata.raw = 0; 112 else if(strcmp(argv[1],"-ne")==0 || strcmp(argv[1],"-en")==0) 113 { 114 prdata.raw = 0; 115 prdata.echon = 1; 116 } 117 #endif /* SHOPT_ECHOE */ 118 else 119 break; 120 argv++; 121 } 122 return(b_print(0,argv,&prdata)); 123 } 124 #endif /* SHOPT_ECHOPRINT */ 125 126 int b_printf(int argc, char *argv[],void *extra) 127 { 128 struct print prdata; 129 NOT_USED(argc); 130 memset(&prdata,0,sizeof(prdata)); 131 prdata.sh = ((Shbltin_t*)extra)->shp; 132 prdata.options = sh_optprintf; 133 return(b_print(-1,argv,&prdata)); 134 } 135 136 /* 137 * argc==0 when called from echo 138 * argc==-1 when called from printf 139 */ 140 141 int b_print(int argc, char *argv[], void *extra) 142 { 143 register Sfio_t *outfile; 144 register int exitval=0,n, fd = 1; 145 register Shell_t *shp = ((Shbltin_t*)extra)->shp; 146 const char *options, *msg = e_file+4; 147 char *format = 0; 148 int sflag = 0, nflag=0, rflag=0, vflag=0; 149 if(argc>0) 150 { 151 options = sh_optprint; 152 nflag = rflag = 0; 153 format = 0; 154 } 155 else 156 { 157 struct print *pp = (struct print*)extra; 158 shp = pp->sh; 159 options = pp->options; 160 if(argc==0) 161 { 162 nflag = pp->echon; 163 rflag = pp->raw; 164 argv++; 165 goto skip; 166 } 167 } 168 while((n = optget(argv,options))) switch(n) 169 { 170 case 'n': 171 nflag++; 172 break; 173 case 'p': 174 fd = shp->coutpipe; 175 msg = e_query; 176 break; 177 case 'f': 178 format = opt_info.arg; 179 break; 180 case 's': 181 /* print to history file */ 182 if(!sh_histinit((void*)shp)) 183 errormsg(SH_DICT,ERROR_system(1),e_history); 184 fd = sffileno(shp->hist_ptr->histfp); 185 sh_onstate(SH_HISTORY); 186 sflag++; 187 break; 188 case 'e': 189 rflag = 0; 190 break; 191 case 'r': 192 rflag = 1; 193 break; 194 case 'u': 195 fd = (int)strtol(opt_info.arg,&opt_info.arg,10); 196 if(*opt_info.arg) 197 fd = -1; 198 else if(fd<0 || fd >= shp->lim.open_max) 199 fd = -1; 200 else if(!(sh.inuse_bits&(1<<fd)) && (sh_inuse(fd) || (shp->hist_ptr && fd==sffileno(shp->hist_ptr->histfp)))) 201 202 fd = -1; 203 break; 204 case 'v': 205 vflag='v'; 206 break; 207 case 'C': 208 vflag='C'; 209 break; 210 case ':': 211 /* The following is for backward compatibility */ 212 #if OPT_VERSION >= 19990123 213 if(strcmp(opt_info.name,"-R")==0) 214 #else 215 if(strcmp(opt_info.option,"-R")==0) 216 #endif 217 { 218 rflag = 1; 219 if(error_info.errors==0) 220 { 221 argv += opt_info.index+1; 222 /* special case test for -Rn */ 223 if(strchr(argv[-1],'n')) 224 nflag++; 225 if(*argv && strcmp(*argv,"-n")==0) 226 { 227 228 nflag++; 229 argv++; 230 } 231 goto skip2; 232 } 233 } 234 else 235 errormsg(SH_DICT,2, "%s", opt_info.arg); 236 break; 237 case '?': 238 errormsg(SH_DICT,ERROR_usage(2), "%s", opt_info.arg); 239 break; 240 } 241 argv += opt_info.index; 242 if(error_info.errors || (argc<0 && !(format = *argv++))) 243 errormsg(SH_DICT,ERROR_usage(2),"%s",optusage((char*)0)); 244 if(vflag && format) 245 errormsg(SH_DICT,ERROR_usage(2),"-%c and -f are mutually exclusive",vflag); 246 skip: 247 if(format) 248 format = genformat(format); 249 /* handle special case of '-' operand for print */ 250 if(argc>0 && *argv && strcmp(*argv,"-")==0 && strcmp(argv[-1],"--")) 251 argv++; 252 skip2: 253 if(fd < 0) 254 { 255 errno = EBADF; 256 n = 0; 257 } 258 else if(!(n=shp->fdstatus[fd])) 259 n = sh_iocheckfd(shp,fd); 260 if(!(n&IOWRITE)) 261 { 262 /* don't print error message for stdout for compatibility */ 263 if(fd==1) 264 return(1); 265 errormsg(SH_DICT,ERROR_system(1),msg); 266 } 267 if(!(outfile=shp->sftable[fd])) 268 { 269 sh_onstate(SH_NOTRACK); 270 n = SF_WRITE|((n&IOREAD)?SF_READ:0); 271 shp->sftable[fd] = outfile = sfnew(NIL(Sfio_t*),shp->outbuff,IOBSIZE,fd,n); 272 sh_offstate(SH_NOTRACK); 273 sfpool(outfile,shp->outpool,SF_WRITE); 274 } 275 /* turn off share to guarantee atomic writes for printf */ 276 n = sfset(outfile,SF_SHARE|SF_PUBLIC,0); 277 if(format) 278 { 279 /* printf style print */ 280 Sfio_t *pool; 281 struct printf pdata; 282 memset(&pdata, 0, sizeof(pdata)); 283 pdata.sh = shp; 284 pdata.hdr.version = SFIO_VERSION; 285 pdata.hdr.extf = extend; 286 pdata.nextarg = argv; 287 sh_offstate(SH_STOPOK); 288 pool=sfpool(sfstderr,NIL(Sfio_t*),SF_WRITE); 289 do 290 { 291 if(shp->trapnote&SH_SIGSET) 292 break; 293 pdata.hdr.form = format; 294 sfprintf(outfile,"%!",&pdata); 295 } while(*pdata.nextarg && pdata.nextarg!=argv); 296 if(pdata.nextarg == nullarg && pdata.argsize>0) 297 sfwrite(outfile,stakptr(staktell()),pdata.argsize); 298 if(sffileno(outfile)!=sffileno(sfstderr)) 299 sfsync(outfile); 300 sfpool(sfstderr,pool,SF_WRITE); 301 exitval = pdata.err; 302 } 303 else if(vflag) 304 { 305 while(*argv) 306 { 307 fmtbase64(outfile,*argv++,vflag=='C'); 308 if(!nflag) 309 sfputc(outfile,'\n'); 310 } 311 } 312 else 313 { 314 /* echo style print */ 315 if(nflag && !argv[0]) 316 sfsync((Sfio_t*)0); 317 else if(sh_echolist(outfile,rflag,argv) && !nflag) 318 sfputc(outfile,'\n'); 319 } 320 if(sflag) 321 { 322 hist_flush(shp->hist_ptr); 323 sh_offstate(SH_HISTORY); 324 } 325 else if(n&SF_SHARE) 326 { 327 sfset(outfile,SF_SHARE|SF_PUBLIC,1); 328 sfsync(outfile); 329 } 330 return(exitval); 331 } 332 333 /* 334 * echo the argument list onto <outfile> 335 * if <raw> is non-zero then \ is not a special character. 336 * returns 0 for \c otherwise 1. 337 */ 338 339 int sh_echolist(Sfio_t *outfile, int raw, char *argv[]) 340 { 341 register char *cp; 342 register int n; 343 struct printf pdata; 344 pdata.cescape = 0; 345 pdata.err = 0; 346 while(!pdata.cescape && (cp= *argv++)) 347 { 348 if(!raw && (n=fmtvecho(cp,&pdata))>=0) 349 { 350 if(n) 351 sfwrite(outfile,stakptr(staktell()),n); 352 } 353 else 354 sfputr(outfile,cp,-1); 355 if(*argv) 356 sfputc(outfile,' '); 357 sh_sigcheck(); 358 } 359 return(!pdata.cescape); 360 } 361 362 /* 363 * modified version of stresc for generating formats 364 */ 365 static char strformat(char *s) 366 { 367 register char* t; 368 register int c; 369 char* b; 370 char* p; 371 372 b = t = s; 373 for (;;) 374 { 375 switch (c = *s++) 376 { 377 case '\\': 378 if(*s==0) 379 break; 380 c = chresc(s - 1, &p); 381 s = p; 382 #if SHOPT_MULTIBYTE 383 if(c>UCHAR_MAX && mbwide()) 384 { 385 t += wctomb(t, c); 386 continue; 387 } 388 #endif /* SHOPT_MULTIBYTE */ 389 if(c=='%') 390 *t++ = '%'; 391 else if(c==0) 392 { 393 *t++ = '%'; 394 c = 'Z'; 395 } 396 break; 397 case 0: 398 *t = 0; 399 return(t - b); 400 } 401 *t++ = c; 402 } 403 } 404 405 406 static char *genformat(char *format) 407 { 408 register char *fp; 409 stakseek(0); 410 stakputs(preformat); 411 stakputs(format); 412 fp = (char*)stakfreeze(1); 413 strformat(fp+sizeof(preformat)-1); 414 return(fp); 415 } 416 417 static char *fmthtml(const char *string) 418 { 419 register const char *cp = string; 420 register int c, offset = staktell(); 421 while(c= *(unsigned char*)cp++) 422 { 423 #if SHOPT_MULTIBYTE 424 register int s; 425 if((s=mbsize(cp-1)) > 1) 426 { 427 cp += (s-1); 428 continue; 429 } 430 #endif /* SHOPT_MULTIBYTE */ 431 if(c=='<') 432 stakputs("<"); 433 else if(c=='>') 434 stakputs(">"); 435 else if(c=='&') 436 stakputs("&"); 437 else if(c=='"') 438 stakputs("""); 439 else if(c=='\'') 440 stakputs("'"); 441 else if(c==' ') 442 stakputs(" "); 443 else if(!isprint(c) && c!='\n' && c!='\r') 444 sfprintf(stkstd,"&#%X;",CCMAPC(c,CC_NATIVE,CC_ASCII)); 445 else 446 stakputc(c); 447 } 448 stakputc(0); 449 return(stakptr(offset)); 450 } 451 452 #if 1 453 static ssize_t fmtbase64(Sfio_t *iop, char *string, int alt) 454 #else 455 static void *fmtbase64(char *string, ssize_t *sz, int alt) 456 #endif 457 { 458 char *cp; 459 Sfdouble_t d; 460 ssize_t size; 461 Namval_t *np = nv_open(string, NiL, NV_VARNAME|NV_NOASSIGN|NV_NOADD); 462 static union types_t number; 463 if(!np || nv_isnull(np)) 464 { 465 if(sh_isoption(SH_NOUNSET)) 466 errormsg(SH_DICT,ERROR_exit(1),e_notset,string); 467 return(0); 468 } 469 if(nv_isattr(np,NV_INTEGER)) 470 { 471 d = nv_getnum(np); 472 if(nv_isattr(np,NV_DOUBLE)) 473 { 474 if(nv_isattr(np,NV_LONG)) 475 { 476 size = sizeof(Sfdouble_t); 477 number.ld = d; 478 } 479 else if(nv_isattr(np,NV_SHORT)) 480 { 481 size = sizeof(float); 482 number.f = (float)d; 483 } 484 else 485 { 486 size = sizeof(double); 487 number.d = (double)d; 488 } 489 } 490 else 491 { 492 if(nv_isattr(np,NV_LONG)) 493 { 494 size = sizeof(Sflong_t); 495 number.ll = (Sflong_t)d; 496 } 497 else if(nv_isattr(np,NV_SHORT)) 498 { 499 size = sizeof(short); 500 number.h = (short)d; 501 } 502 else 503 { 504 size = sizeof(short); 505 number.i = (int)d; 506 } 507 } 508 #if 1 509 return(sfwrite(iop, (void*)&number, size)); 510 #else 511 if(sz) 512 *sz = size; 513 return((void*)&number); 514 #endif 515 } 516 if(nv_isattr(np,NV_BINARY)) 517 #if 1 518 { 519 Namfun_t *fp; 520 for(fp=np->nvfun; fp;fp=fp->next) 521 { 522 if(fp->disc && fp->disc->writef) 523 break; 524 } 525 if(fp) 526 return (*fp->disc->writef)(np, iop, 0, fp); 527 else 528 { 529 int n = nv_size(np); 530 if(nv_isarray(np)) 531 { 532 nv_onattr(np,NV_RAW); 533 cp = nv_getval(np); 534 nv_offattr(np,NV_RAW); 535 } 536 else 537 cp = (char*)np->nvalue.cp; 538 if((size = n)==0) 539 size = strlen(cp); 540 size = sfwrite(iop, cp, size); 541 return(n?n:size); 542 } 543 } 544 else if(nv_isarray(np) && nv_arrayptr(np)) 545 { 546 nv_outnode(np,iop,(alt?-1:0),0); 547 sfputc(iop,')'); 548 return(sftell(iop)); 549 } 550 else 551 { 552 if(alt && nv_isvtree(np)) 553 nv_onattr(np,NV_EXPORT); 554 if(!(cp = nv_getval(np))) 555 return(0); 556 size = strlen(cp); 557 return(sfwrite(iop,cp,size)); 558 } 559 #else 560 nv_onattr(np,NV_RAW); 561 cp = nv_getval(np); 562 if(nv_isattr(np,NV_BINARY)) 563 nv_offattr(np,NV_RAW); 564 if((size = nv_size(np))==0) 565 size = strlen(cp); 566 if(sz) 567 *sz = size; 568 return((void*)cp); 569 #endif 570 } 571 572 static int varname(const char *str, int n) 573 { 574 register int c,dot=1,len=1; 575 if(n < 0) 576 { 577 if(*str=='.') 578 str++; 579 n = strlen(str); 580 } 581 for(;n > 0; n-=len) 582 { 583 #ifdef SHOPT_MULTIBYTE 584 len = mbsize(str); 585 c = mbchar(str); 586 #else 587 c = *(unsigned char*)str++; 588 #endif 589 if(dot && !(isalpha(c)||c=='_')) 590 break; 591 else if(dot==0 && !(isalnum(c) || c=='_' || c == '.')) 592 break; 593 dot = (c=='.'); 594 } 595 return(n==0); 596 } 597 598 static int extend(Sfio_t* sp, void* v, Sffmt_t* fe) 599 { 600 char* lastchar = ""; 601 register int neg = 0; 602 Sfdouble_t d; 603 Sfdouble_t longmin = LDBL_LLONG_MIN; 604 Sfdouble_t longmax = LDBL_LLONG_MAX; 605 int format = fe->fmt; 606 int n; 607 int fold = fe->base; 608 union types_t* value = (union types_t*)v; 609 struct printf* pp = (struct printf*)fe; 610 register char* argp = *pp->nextarg; 611 char* w; 612 613 if(fe->n_str>0 && varname(fe->t_str,fe->n_str) && (!argp || varname(argp,-1))) 614 { 615 if(argp) 616 pp->lastarg = argp; 617 else 618 argp = pp->lastarg; 619 if(argp) 620 { 621 sfprintf(pp->sh->strbuf,"%s.%.*s%c",argp,fe->n_str,fe->t_str,0); 622 argp = sfstruse(pp->sh->strbuf); 623 } 624 } 625 else 626 pp->lastarg = 0; 627 fe->flags |= SFFMT_VALUE; 628 if(!argp || format=='Z') 629 { 630 switch(format) 631 { 632 case 'c': 633 value->c = 0; 634 fe->flags &= ~SFFMT_LONG; 635 break; 636 case 'q': 637 format = 's'; 638 /* FALL THROUGH */ 639 case 's': 640 case 'H': 641 case 'B': 642 case 'P': 643 case 'R': 644 case 'Z': 645 case 'b': 646 fe->fmt = 's'; 647 fe->size = -1; 648 fe->base = -1; 649 value->s = ""; 650 fe->flags &= ~SFFMT_LONG; 651 break; 652 case 'a': 653 case 'e': 654 case 'f': 655 case 'g': 656 case 'A': 657 case 'E': 658 case 'F': 659 case 'G': 660 if(SFFMT_LDOUBLE) 661 value->ld = 0.; 662 else 663 value->d = 0.; 664 break; 665 case 'n': 666 value->ip = &pp->intvar; 667 break; 668 case 'Q': 669 value->ll = 0; 670 break; 671 case 'T': 672 fe->fmt = 'd'; 673 value->ll = tmxgettime(); 674 break; 675 default: 676 if(!strchr("DdXxoUu",format)) 677 errormsg(SH_DICT,ERROR_exit(1),e_formspec,format); 678 fe->fmt = 'd'; 679 value->ll = 0; 680 break; 681 } 682 } 683 else 684 { 685 switch(format) 686 { 687 case 'p': 688 value->p = (char**)strtol(argp,&lastchar,10); 689 break; 690 case 'n': 691 { 692 Namval_t *np; 693 np = nv_open(argp,sh.var_tree,NV_VARNAME|NV_NOASSIGN|NV_NOARRAY); 694 nv_unset(np); 695 nv_onattr(np,NV_INTEGER); 696 if (np->nvalue.lp = new_of(int32_t,0)) 697 *np->nvalue.lp = 0; 698 nv_setsize(np,10); 699 if(sizeof(int)==sizeof(int32_t)) 700 value->ip = (int*)np->nvalue.lp; 701 else 702 { 703 int32_t sl = 1; 704 value->ip = (int*)(((char*)np->nvalue.lp) + (*((char*)&sl) ? 0 : sizeof(int))); 705 } 706 nv_close(np); 707 break; 708 } 709 case 'q': 710 case 'b': 711 case 's': 712 case 'B': 713 case 'H': 714 case 'P': 715 case 'R': 716 fe->fmt = 's'; 717 fe->size = -1; 718 if(format=='s' && fe->base>=0) 719 { 720 value->p = pp->nextarg; 721 pp->nextarg = nullarg; 722 } 723 else 724 { 725 fe->base = -1; 726 value->s = argp; 727 } 728 fe->flags &= ~SFFMT_LONG; 729 break; 730 case 'c': 731 if(mbwide() && (n = mbsize(argp)) > 1) 732 { 733 fe->fmt = 's'; 734 fe->size = n; 735 value->s = argp; 736 } 737 else if(fe->base >=0) 738 value->s = argp; 739 else 740 value->c = *argp; 741 fe->flags &= ~SFFMT_LONG; 742 break; 743 case 'o': 744 case 'x': 745 case 'X': 746 case 'u': 747 case 'U': 748 longmax = LDBL_ULLONG_MAX; 749 case '.': 750 if(fe->size==2 && strchr("bcsqHPRQTZ",*fe->form)) 751 { 752 value->ll = ((unsigned char*)argp)[0]; 753 break; 754 } 755 case 'd': 756 case 'D': 757 case 'i': 758 switch(*argp) 759 { 760 case '\'': 761 case '"': 762 w = argp + 1; 763 if(mbwide() && mbsize(w) > 1) 764 value->ll = mbchar(w); 765 else 766 value->ll = *(unsigned char*)w++; 767 if(w[0] && (w[0] != argp[0] || w[1])) 768 { 769 errormsg(SH_DICT,ERROR_warn(0),e_charconst,argp); 770 pp->err = 1; 771 } 772 break; 773 default: 774 d = sh_strnum(argp,&lastchar,0); 775 if(d<longmin) 776 { 777 errormsg(SH_DICT,ERROR_warn(0),e_overflow,argp); 778 pp->err = 1; 779 d = longmin; 780 } 781 else if(d>longmax) 782 { 783 errormsg(SH_DICT,ERROR_warn(0),e_overflow,argp); 784 pp->err = 1; 785 d = longmax; 786 } 787 value->ll = (Sflong_t)d; 788 if(lastchar == *pp->nextarg) 789 { 790 value->ll = *argp; 791 lastchar = ""; 792 } 793 break; 794 } 795 if(neg) 796 value->ll = -value->ll; 797 fe->size = sizeof(value->ll); 798 break; 799 case 'a': 800 case 'e': 801 case 'f': 802 case 'g': 803 case 'A': 804 case 'E': 805 case 'F': 806 case 'G': 807 d = sh_strnum(*pp->nextarg,&lastchar,0); 808 switch(*argp) 809 { 810 case '\'': 811 case '"': 812 d = ((unsigned char*)argp)[1]; 813 if(argp[2] && (argp[2] != argp[0] || argp[3])) 814 { 815 errormsg(SH_DICT,ERROR_warn(0),e_charconst,argp); 816 pp->err = 1; 817 } 818 break; 819 default: 820 d = sh_strnum(*pp->nextarg,&lastchar,0); 821 break; 822 } 823 if(SFFMT_LDOUBLE) 824 { 825 value->ld = d; 826 fe->size = sizeof(value->ld); 827 } 828 else 829 { 830 value->d = d; 831 fe->size = sizeof(value->d); 832 } 833 break; 834 case 'Q': 835 value->ll = (Sflong_t)strelapsed(*pp->nextarg,&lastchar,1); 836 break; 837 case 'T': 838 value->ll = (Sflong_t)tmxdate(*pp->nextarg,&lastchar,TMX_NOW); 839 break; 840 default: 841 value->ll = 0; 842 fe->fmt = 'd'; 843 fe->size = sizeof(value->ll); 844 errormsg(SH_DICT,ERROR_exit(1),e_formspec,format); 845 break; 846 } 847 if (format == '.') 848 value->i = value->ll; 849 if(*lastchar) 850 { 851 errormsg(SH_DICT,ERROR_warn(0),e_argtype,format); 852 pp->err = 1; 853 } 854 pp->nextarg++; 855 } 856 switch(format) 857 { 858 case 'Z': 859 fe->fmt = 'c'; 860 fe->base = -1; 861 value->c = 0; 862 break; 863 case 'b': 864 if((n=fmtvecho(value->s,pp))>=0) 865 { 866 if(pp->nextarg == nullarg) 867 { 868 pp->argsize = n; 869 return -1; 870 } 871 value->s = stakptr(staktell()); 872 } 873 break; 874 case 'B': 875 if(!sh.strbuf2) 876 sh.strbuf2 = sfstropen(); 877 fe->size = fmtbase64(sh.strbuf2,value->s, fe->flags&SFFMT_ALTER); 878 value->s = sfstruse(sh.strbuf2); 879 fe->flags |= SFFMT_SHORT; 880 break; 881 case 'H': 882 value->s = fmthtml(value->s); 883 break; 884 case 'q': 885 value->s = sh_fmtqf(value->s, !!(fe->flags & SFFMT_ALTER), fold); 886 break; 887 case 'P': 888 { 889 char *s = fmtmatch(value->s); 890 if(!s || *s==0) 891 errormsg(SH_DICT,ERROR_exit(1),e_badregexp,value->s); 892 value->s = s; 893 break; 894 } 895 case 'R': 896 value->s = fmtre(value->s); 897 if(*value->s==0) 898 errormsg(SH_DICT,ERROR_exit(1),e_badregexp,value->s); 899 break; 900 case 'Q': 901 if (fe->n_str>0) 902 { 903 fe->fmt = 'd'; 904 fe->size = sizeof(value->ll); 905 } 906 else 907 { 908 value->s = fmtelapsed(value->ll, 1); 909 fe->fmt = 's'; 910 fe->size = -1; 911 } 912 break; 913 case 'T': 914 if(fe->n_str>0) 915 { 916 n = fe->t_str[fe->n_str]; 917 fe->t_str[fe->n_str] = 0; 918 value->s = fmttmx(fe->t_str, value->ll); 919 fe->t_str[fe->n_str] = n; 920 } 921 else value->s = fmttmx(NIL(char*), value->ll); 922 fe->fmt = 's'; 923 fe->size = -1; 924 break; 925 } 926 return 0; 927 } 928 929 /* 930 * construct System V echo string out of <cp> 931 * If there are not escape sequences, returns -1 932 * Otherwise, puts null terminated result on stack, but doesn't freeze it 933 * returns length of output. 934 */ 935 936 static int fmtvecho(const char *string, struct printf *pp) 937 { 938 register const char *cp = string, *cpmax; 939 register int c; 940 register int offset = staktell(); 941 #if SHOPT_MULTIBYTE 942 int chlen; 943 if(mbwide()) 944 { 945 while(1) 946 { 947 if ((chlen = mbsize(cp)) > 1) 948 /* Skip over multibyte characters */ 949 cp += chlen; 950 else if((c= *cp++)==0 || c == '\\') 951 break; 952 } 953 } 954 else 955 #endif /* SHOPT_MULTIBYTE */ 956 while((c= *cp++) && (c!='\\')); 957 if(c==0) 958 return(-1); 959 c = --cp - string; 960 if(c>0) 961 stakwrite((void*)string,c); 962 for(; c= *cp; cp++) 963 { 964 #if SHOPT_MULTIBYTE 965 if (mbwide() && ((chlen = mbsize(cp)) > 1)) 966 { 967 stakwrite(cp,chlen); 968 cp += (chlen-1); 969 continue; 970 } 971 #endif /* SHOPT_MULTIBYTE */ 972 if( c=='\\') switch(*++cp) 973 { 974 case 'E': 975 c = ('a'==97?'\033':39); /* ASCII/EBCDIC */ 976 break; 977 case 'a': 978 c = '\a'; 979 break; 980 case 'b': 981 c = '\b'; 982 break; 983 case 'c': 984 pp->cescape++; 985 pp->nextarg = nullarg; 986 goto done; 987 case 'f': 988 c = '\f'; 989 break; 990 case 'n': 991 c = '\n'; 992 break; 993 case 'r': 994 c = '\r'; 995 break; 996 case 'v': 997 c = '\v'; 998 break; 999 case 't': 1000 c = '\t'; 1001 break; 1002 case '\\': 1003 c = '\\'; 1004 break; 1005 case '0': 1006 c = 0; 1007 cpmax = cp + 4; 1008 while(++cp<cpmax && *cp>='0' && *cp<='7') 1009 { 1010 c <<= 3; 1011 c |= (*cp-'0'); 1012 } 1013 default: 1014 cp--; 1015 } 1016 stakputc(c); 1017 } 1018 done: 1019 c = staktell()-offset; 1020 stakputc(0); 1021 stakseek(offset); 1022 return(c); 1023 } 1024