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