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