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 41union 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 56struct 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 68struct printmap 69{ 70 size_t size; 71 char *name; 72 char map[3]; 73 const char *description; 74}; 75 76const 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 87static int extend(Sfio_t*,void*, Sffmt_t*); 88static const char preformat[] = ""; 89static char *genformat(char*); 90static int fmtvecho(const char*, struct printf*); 91static ssize_t fmtbase64(Sfio_t*, char*, int); 92 93struct print 94{ 95 Shell_t *sh; 96 const char *options; 97 char raw; 98 char echon; 99}; 100 101static 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 145int 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 155static 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 171int 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); 280skip: 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++; 286skip2: 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 373int 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 */ 399static 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 455static 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 466static 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 515static ssize_t fmtbase64(Sfio_t *iop, char *string, int alt) 516#else 517static 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 640static 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 666static 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 678static 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 case 'b': 802 case 's': 803 case 'B': 804 case 'H': 805 case 'P': 806 case 'R': 807 fe->fmt = 's'; 808 fe->size = -1; 809 if(format=='s' && fe->base>=0) 810 { 811 value->p = pp->nextarg; 812 pp->nextarg = nullarg; 813 } 814 else 815 { 816 fe->base = -1; 817 value->s = argp; 818 } 819 fe->flags &= ~SFFMT_LONG; 820 break; 821 case 'c': 822 if(mbwide() && (n = mbsize(argp)) > 1) 823 { 824 fe->fmt = 's'; 825 fe->size = n; 826 value->s = argp; 827 } 828 else if(fe->base >=0) 829 value->s = argp; 830 else 831 value->c = *argp; 832 fe->flags &= ~SFFMT_LONG; 833 break; 834 case 'o': 835 case 'x': 836 case 'X': 837 case 'u': 838 case 'U': 839 longmax = LDBL_ULLONG_MAX; 840 case '.': 841 if(fe->size==2 && strchr("bcsqHPRQTZ",*fe->form)) 842 { 843 value->ll = ((unsigned char*)argp)[0]; 844 break; 845 } 846 case 'd': 847 case 'D': 848 case 'i': 849 switch(*argp) 850 { 851 case '\'': 852 case '"': 853 w = argp + 1; 854 if(mbwide() && mbsize(w) > 1) 855 value->ll = mbchar(w); 856 else 857 value->ll = *(unsigned char*)w++; 858 if(w[0] && (w[0] != argp[0] || w[1])) 859 { 860 errormsg(SH_DICT,ERROR_warn(0),e_charconst,argp); 861 pp->err = 1; 862 } 863 break; 864 default: 865 d = sh_strnum(argp,&lastchar,0); 866 if(d<longmin) 867 { 868 errormsg(SH_DICT,ERROR_warn(0),e_overflow,argp); 869 pp->err = 1; 870 d = longmin; 871 } 872 else if(d>longmax) 873 { 874 errormsg(SH_DICT,ERROR_warn(0),e_overflow,argp); 875 pp->err = 1; 876 d = longmax; 877 } 878 value->ll = (Sflong_t)d; 879 if(lastchar == *pp->nextarg) 880 { 881 value->ll = *argp; 882 lastchar = ""; 883 } 884 break; 885 } 886 if(neg) 887 value->ll = -value->ll; 888 fe->size = sizeof(value->ll); 889 break; 890 case 'a': 891 case 'e': 892 case 'f': 893 case 'g': 894 case 'A': 895 case 'E': 896 case 'F': 897 case 'G': 898 d = sh_strnum(*pp->nextarg,&lastchar,0); 899 switch(*argp) 900 { 901 case '\'': 902 case '"': 903 d = ((unsigned char*)argp)[1]; 904 if(argp[2] && (argp[2] != argp[0] || argp[3])) 905 { 906 errormsg(SH_DICT,ERROR_warn(0),e_charconst,argp); 907 pp->err = 1; 908 } 909 break; 910 default: 911 d = sh_strnum(*pp->nextarg,&lastchar,0); 912 break; 913 } 914 if(SFFMT_LDOUBLE) 915 { 916 value->ld = d; 917 fe->size = sizeof(value->ld); 918 } 919 else 920 { 921 value->d = d; 922 fe->size = sizeof(value->d); 923 } 924 break; 925 case 'Q': 926 value->ll = (Sflong_t)strelapsed(*pp->nextarg,&lastchar,1); 927 break; 928 case 'T': 929 value->ll = (Sflong_t)tmxdate(*pp->nextarg,&lastchar,TMX_NOW); 930 break; 931 default: 932 value->ll = 0; 933 fe->fmt = 'd'; 934 fe->size = sizeof(value->ll); 935 errormsg(SH_DICT,ERROR_exit(1),e_formspec,format); 936 break; 937 } 938 if (format == '.') 939 value->i = value->ll; 940 if(*lastchar) 941 { 942 errormsg(SH_DICT,ERROR_warn(0),e_argtype,format); 943 pp->err = 1; 944 } 945 pp->nextarg++; 946 } 947 switch(format) 948 { 949 case 'Z': 950 fe->fmt = 'c'; 951 fe->base = -1; 952 value->c = 0; 953 break; 954 case 'b': 955 if((n=fmtvecho(value->s,pp))>=0) 956 { 957 if(pp->nextarg == nullarg) 958 { 959 pp->argsize = n; 960 return -1; 961 } 962 value->s = stakptr(staktell()); 963 fe->size = n; 964 } 965 break; 966 case 'B': 967 if(!shp->strbuf2) 968 shp->strbuf2 = sfstropen(); 969 fe->size = fmtbase64(shp->strbuf2,value->s, fe->flags&SFFMT_ALTER); 970 value->s = sfstruse(shp->strbuf2); 971 fe->flags |= SFFMT_SHORT; 972 break; 973 case 'H': 974 value->s = fmthtml(value->s, fe->flags); 975 break; 976 case 'q': 977 value->s = sh_fmtqf(value->s, !!(fe->flags & SFFMT_ALTER), fold); 978 break; 979 case 'P': 980 s = fmtmatch(value->s); 981 if(!s || *s==0) 982 errormsg(SH_DICT,ERROR_exit(1),e_badregexp,value->s); 983 value->s = s; 984 break; 985 case 'R': 986 s = fmtre(value->s); 987 if(!s || *s==0) 988 errormsg(SH_DICT,ERROR_exit(1),e_badregexp,value->s); 989 value->s = s; 990 break; 991 case 'Q': 992 if (fe->n_str>0) 993 { 994 fe->fmt = 'd'; 995 fe->size = sizeof(value->ll); 996 } 997 else 998 { 999 value->s = fmtelapsed(value->ll, 1); 1000 fe->fmt = 's'; 1001 fe->size = -1; 1002 } 1003 break; 1004 case 'T': 1005 if(fe->n_str>0) 1006 { 1007 n = fe->t_str[fe->n_str]; 1008 fe->t_str[fe->n_str] = 0; 1009 value->s = fmttmx(fe->t_str, value->ll); 1010 fe->t_str[fe->n_str] = n; 1011 } 1012 else value->s = fmttmx(NIL(char*), value->ll); 1013 fe->fmt = 's'; 1014 fe->size = -1; 1015 break; 1016 } 1017 return 0; 1018} 1019 1020/* 1021 * construct System V echo string out of <cp> 1022 * If there are not escape sequences, returns -1 1023 * Otherwise, puts null terminated result on stack, but doesn't freeze it 1024 * returns length of output. 1025 */ 1026 1027static int fmtvecho(const char *string, struct printf *pp) 1028{ 1029 register const char *cp = string, *cpmax; 1030 register int c; 1031 register int offset = staktell(); 1032#if SHOPT_MULTIBYTE 1033 int chlen; 1034 if(mbwide()) 1035 { 1036 while(1) 1037 { 1038 if ((chlen = mbsize(cp)) > 1) 1039 /* Skip over multibyte characters */ 1040 cp += chlen; 1041 else if((c= *cp++)==0 || c == '\\') 1042 break; 1043 } 1044 } 1045 else 1046#endif /* SHOPT_MULTIBYTE */ 1047 while((c= *cp++) && (c!='\\')); 1048 if(c==0) 1049 return(-1); 1050 c = --cp - string; 1051 if(c>0) 1052 stakwrite((void*)string,c); 1053 for(; c= *cp; cp++) 1054 { 1055#if SHOPT_MULTIBYTE 1056 if (mbwide() && ((chlen = mbsize(cp)) > 1)) 1057 { 1058 stakwrite(cp,chlen); 1059 cp += (chlen-1); 1060 continue; 1061 } 1062#endif /* SHOPT_MULTIBYTE */ 1063 if( c=='\\') switch(*++cp) 1064 { 1065 case 'E': 1066 c = ('a'==97?'\033':39); /* ASCII/EBCDIC */ 1067 break; 1068 case 'a': 1069 c = '\a'; 1070 break; 1071 case 'b': 1072 c = '\b'; 1073 break; 1074 case 'c': 1075 pp->cescape++; 1076 pp->nextarg = nullarg; 1077 goto done; 1078 case 'f': 1079 c = '\f'; 1080 break; 1081 case 'n': 1082 c = '\n'; 1083 break; 1084 case 'r': 1085 c = '\r'; 1086 break; 1087 case 'v': 1088 c = '\v'; 1089 break; 1090 case 't': 1091 c = '\t'; 1092 break; 1093 case '\\': 1094 c = '\\'; 1095 break; 1096 case '0': 1097 c = 0; 1098 cpmax = cp + 4; 1099 while(++cp<cpmax && *cp>='0' && *cp<='7') 1100 { 1101 c <<= 3; 1102 c |= (*cp-'0'); 1103 } 1104 default: 1105 cp--; 1106 } 1107 stakputc(c); 1108 } 1109done: 1110 c = staktell()-offset; 1111 stakputc(0); 1112 stakseek(offset); 1113 return(c); 1114} 1115