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