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 * string processing routines for Korn shell 23 * 24 */ 25 26#include <ast.h> 27#include <ast_wchar.h> 28#include "defs.h" 29#include <stak.h> 30#include <ccode.h> 31#include "shtable.h" 32#include "lexstates.h" 33#include "national.h" 34 35#if _hdr_wctype 36# include <wctype.h> 37#endif 38 39#if !_lib_iswprint && !defined(iswprint) 40# define iswprint(c) (((c)&~0377) || isprint(c)) 41#endif 42 43 44/* 45 * Table lookup routine 46 * <table> is searched for string <sp> and corresponding value is returned 47 * This is only used for small tables and is used to save non-sharable memory 48 */ 49 50const Shtable_t *sh_locate(register const char *sp,const Shtable_t *table,int size) 51{ 52 register int first; 53 register const Shtable_t *tp; 54 register int c; 55 static const Shtable_t empty = {0,0}; 56 if(sp==0 || (first= *sp)==0) 57 return(&empty); 58 tp=table; 59 while((c= *tp->sh_name) && (CC_NATIVE!=CC_ASCII || c <= first)) 60 { 61 if(first == c && strcmp(sp,tp->sh_name)==0) 62 return(tp); 63 tp = (Shtable_t*)((char*)tp+size); 64 } 65 return(&empty); 66} 67 68/* 69 * shtab_options lookup routine 70 */ 71 72#define sep(c) ((c)=='-'||(c)=='_') 73 74int sh_lookopt(register const char *sp, int *invert) 75{ 76 register int first; 77 register const Shtable_t *tp; 78 register int c; 79 register const char *s, *t, *sw, *tw; 80 int amb; 81 int hit; 82 int inv; 83 int no; 84 if(sp==0) 85 return(0); 86 if(*sp=='n' && *(sp+1)=='o' && (*(sp+2)!='t' || *(sp+3)!='i')) 87 { 88 sp+=2; 89 if(sep(*sp)) 90 sp++; 91 *invert = !*invert; 92 } 93 if((first= *sp)==0) 94 return(0); 95 tp=shtab_options; 96 amb=hit=0; 97 for(;;) 98 { 99 t=tp->sh_name; 100 if(no = *t=='n' && *(t+1)=='o' && *(t+2)!='t') 101 t+=2; 102 if(!(c= *t)) 103 break; 104 if(first == c) 105 { 106 if(strcmp(sp,t)==0) 107 { 108 *invert ^= no; 109 return(tp->sh_number); 110 } 111 s=sw=sp; 112 tw=t; 113 for(;;) 114 { 115 if(!*s || *s=='=') 116 { 117 if (*s == '=' && !strtol(s+1, NiL, 0)) 118 no = !no; 119 if (!*t) 120 { 121 *invert ^= no; 122 return(tp->sh_number); 123 } 124 if (hit || amb) 125 { 126 hit = 0; 127 amb = 1; 128 } 129 else 130 { 131 hit = tp->sh_number; 132 inv = no; 133 } 134 break; 135 } 136 else if(!*t) 137 break; 138 else if(sep(*s)) 139 sw = ++s; 140 else if(sep(*t)) 141 tw = ++t; 142 else if(*s==*t) 143 { 144 s++; 145 t++; 146 } 147 else if(s==sw && t==tw) 148 break; 149 else 150 { 151 if(t!=tw) 152 { 153 while(*t && !sep(*t)) 154 t++; 155 if(!*t) 156 break; 157 tw = ++t; 158 } 159 while (s>sw && *s!=*t) 160 s--; 161 } 162 } 163 } 164 tp = (Shtable_t*)((char*)tp+sizeof(*shtab_options)); 165 } 166 if(hit) 167 *invert ^= inv; 168 return(hit); 169} 170 171/* 172 * look for the substring <oldsp> in <string> and replace with <newsp> 173 * The new string is put on top of the stack 174 */ 175char *sh_substitute(const char *string,const char *oldsp,char *newsp) 176/*@ 177 assume string!=NULL && oldsp!=NULL && newsp!=NULL; 178 return x satisfying x==NULL || 179 strlen(x)==(strlen(in string)+strlen(in newsp)-strlen(in oldsp)); 180@*/ 181{ 182 register const char *sp = string; 183 register const char *cp; 184 const char *savesp = 0; 185 stakseek(0); 186 if(*sp==0) 187 return((char*)0); 188 if(*(cp=oldsp) == 0) 189 goto found; 190#if SHOPT_MULTIBYTE 191 mbinit(); 192#endif /* SHOPT_MULTIBYTE */ 193 do 194 { 195 /* skip to first character which matches start of oldsp */ 196 while(*sp && (savesp==sp || *sp != *cp)) 197 { 198#if SHOPT_MULTIBYTE 199 /* skip a whole character at a time */ 200 int c = mbsize(sp); 201 if(c < 0) 202 sp++; 203 while(c-- > 0) 204#endif /* SHOPT_MULTIBYTE */ 205 stakputc(*sp++); 206 } 207 if(*sp == 0) 208 return((char*)0); 209 savesp = sp; 210 for(;*cp;cp++) 211 { 212 if(*cp != *sp++) 213 break; 214 } 215 if(*cp==0) 216 /* match found */ 217 goto found; 218 sp = savesp; 219 cp = oldsp; 220 } 221 while(*sp); 222 return((char*)0); 223 224found: 225 /* copy new */ 226 stakputs(newsp); 227 /* copy rest of string */ 228 stakputs(sp); 229 return(stakfreeze(1)); 230} 231 232/* 233 * TRIM(sp) 234 * Remove escape characters from characters in <sp> and eliminate quoted nulls. 235 */ 236 237void sh_trim(register char *sp) 238/*@ 239 assume sp!=NULL; 240 promise strlen(in sp) <= in strlen(sp); 241@*/ 242{ 243 register char *dp; 244 register int c; 245 if(sp) 246 { 247 dp = sp; 248 while(c= *sp) 249 { 250#if SHOPT_MULTIBYTE 251 int len; 252 if(mbwide() && (len=mbsize(sp))>1) 253 { 254 memmove(dp, sp, len); 255 dp += len; 256 sp += len; 257 continue; 258 } 259#endif /* SHOPT_MULTIBYTE */ 260 sp++; 261 if(c == '\\') 262 c = *sp++; 263 if(c) 264 *dp++ = c; 265 } 266 *dp = 0; 267 } 268} 269 270/* 271 * copy <str1> to <str2> changing upper case to lower case 272 * <str2> must be big enough to hold <str1> 273 * <str1> and <str2> may point to the same place. 274 */ 275 276void sh_utol(register char const *str1,register char *str2) 277/*@ 278 assume str1!=0 && str2!=0 279 return x satisfying strlen(in str1)==strlen(in str2); 280@*/ 281{ 282 register int c; 283 for(; c= *((unsigned char*)str1); str1++,str2++) 284 { 285 if(isupper(c)) 286 *str2 = tolower(c); 287 else 288 *str2 = c; 289 } 290 *str2 = 0; 291} 292 293/* 294 * format string as a csv field 295 */ 296static char *sh_fmtcsv(const char *string) 297{ 298 register const char *cp = string; 299 register int c; 300 int offset; 301 if(!cp) 302 return((char*)0); 303 offset = staktell(); 304 while((c=mbchar(cp)),isaname(c)); 305 if(c==0) 306 return((char*)string); 307 stakputc('"'); 308 stakwrite(string,cp-string); 309 if(c=='"') 310 stakputc('"'); 311 string = cp; 312 while(c=mbchar(cp)) 313 { 314 if(c=='"') 315 { 316 stakwrite(string,cp-string); 317 string = cp; 318 stakputc('"'); 319 } 320 } 321 if(--cp>string) 322 stakwrite(string,cp-string); 323 stakputc('"'); 324 stakputc(0); 325 return(stakptr(offset)); 326} 327 328/* 329 * print <str> quoting chars so that it can be read by the shell 330 * puts null terminated result on stack, but doesn't freeze it 331 */ 332char *sh_fmtq(const char *string) 333{ 334 register const char *cp = string, *op; 335 register int c, state; 336 int offset; 337 if(!cp) 338 return((char*)0); 339 offset = staktell(); 340 state = ((c= mbchar(cp))==0); 341 if(isaletter(c)) 342 { 343 while((c=mbchar(cp)),isaname(c)); 344 if(c==0) 345 return((char*)string); 346 if(c=='=') 347 { 348 if(*cp==0) 349 return((char*)string); 350 if(*cp=='=') 351 cp++; 352 c = cp - string; 353 stakwrite(string,c); 354 string = cp; 355 c = mbchar(cp); 356 } 357 } 358 if(c==0 || c=='#' || c=='~') 359 state = 1; 360 for(;c;c= mbchar(cp)) 361 { 362#if SHOPT_MULTIBYTE 363 if(c=='\'' || c>=128 || c<0 || !iswprint(c)) 364#else 365 if(c=='\'' || !isprint(c)) 366#endif /* SHOPT_MULTIBYTE */ 367 state = 2; 368 else if(c==']' || c=='=' || (c!=':' && c<=0x7f && (c=sh_lexstates[ST_NORM][c]) && c!=S_EPAT)) 369 state |=1; 370 } 371 if(state<2) 372 { 373 if(state==1) 374 stakputc('\''); 375 if(c = --cp - string) 376 stakwrite(string,c); 377 if(state==1) 378 stakputc('\''); 379 } 380 else 381 { 382 int isbyte=0; 383 stakwrite("$'",2); 384 cp = string; 385#if SHOPT_MULTIBYTE 386 while(op = cp, c= mbchar(cp)) 387#else 388 while(op = cp, c= *(unsigned char*)cp++) 389#endif 390 { 391 state=1; 392 switch(c) 393 { 394 case ('a'==97?'\033':39): 395 c = 'E'; 396 break; 397 case '\n': 398 c = 'n'; 399 break; 400 case '\r': 401 c = 'r'; 402 break; 403 case '\t': 404 c = 't'; 405 break; 406 case '\f': 407 c = 'f'; 408 break; 409 case '\b': 410 c = 'b'; 411 break; 412 case '\a': 413 c = 'a'; 414 break; 415 case '\\': case '\'': 416 break; 417 default: 418#if SHOPT_MULTIBYTE 419 isbyte = 0; 420 if(c<0) 421 { 422 c = *((unsigned char *)op); 423 cp = op+1; 424 isbyte = 1; 425 } 426 if(mbwide() && ((cp-op)>1)) 427 { 428 sfprintf(staksp,"\\u[%x]",c); 429 continue; 430 } 431 else if(!iswprint(c) || isbyte) 432#else 433 if(!isprint(c)) 434#endif 435 { 436 sfprintf(staksp,"\\x%.2x",c); 437 continue; 438 } 439 state=0; 440 break; 441 } 442 if(state) 443 { 444 stakputc('\\'); 445 stakputc(c); 446 } 447 else 448 stakwrite(op, cp-op); 449 } 450 stakputc('\''); 451 } 452 stakputc(0); 453 return(stakptr(offset)); 454} 455 456/* 457 * print <str> quoting chars so that it can be read by the shell 458 * puts null terminated result on stack, but doesn't freeze it 459 * single!=0 limits quoting to '...' 460 * fold>0 prints raw newlines and inserts appropriately 461 * escaped newlines every (fold-x) chars 462 */ 463char *sh_fmtqf(const char *string, int single, int fold) 464{ 465 register const char *cp = string; 466 register const char *bp; 467 register const char *vp; 468 register int c; 469 register int n; 470 register int q; 471 register int a; 472 int offset; 473 474 if (--fold < 8) 475 fold = 0; 476 if(single) 477 return sh_fmtcsv(cp); 478 if (!cp || !*cp || !fold || fold && strlen(string) < fold) 479 return sh_fmtq(cp); 480 offset = staktell(); 481 single = single ? 1 : 3; 482 c = mbchar(string); 483 a = isaletter(c) ? '=' : 0; 484 vp = cp + 1; 485 do 486 { 487 q = 0; 488 n = fold; 489 bp = cp; 490 while ((!n || n-- > 0) && (c = mbchar(cp))) 491 { 492 if (a && !isaname(c)) 493 a = 0; 494#if SHOPT_MULTIBYTE 495 if (c >= 0x200) 496 continue; 497 if (c == '\'' || !iswprint(c)) 498#else 499 if (c == '\'' || !isprint(c)) 500#endif /* SHOPT_MULTIBYTE */ 501 { 502 q = single; 503 break; 504 } 505 if (c == '\n') 506 q = 1; 507 else if (c == a) 508 { 509 stakwrite(bp, cp - bp); 510 bp = cp; 511 vp = cp + 1; 512 a = 0; 513 } 514 else if ((c == '#' || c == '~') && cp == vp || c == ']' || c != ':' && (c = sh_lexstates[ST_NORM][c]) && c != S_EPAT) 515 q = 1; 516 } 517 if (q & 2) 518 { 519 stakputc('$'); 520 stakputc('\''); 521 cp = bp; 522 n = fold - 3; 523 q = 1; 524 while (c = mbchar(cp)) 525 { 526 switch (c) 527 { 528 case ('a'==97?'\033':39): 529 c = 'E'; 530 break; 531 case '\n': 532 q = 0; 533 n = fold - 1; 534 break; 535 case '\r': 536 c = 'r'; 537 break; 538 case '\t': 539 c = 't'; 540 break; 541 case '\f': 542 c = 'f'; 543 break; 544 case '\b': 545 c = 'b'; 546 break; 547 case '\a': 548 c = 'a'; 549 break; 550 case '\\': 551 if (*cp == 'n') 552 { 553 c = '\n'; 554 q = 0; 555 n = fold - 1; 556 break; 557 } 558 case '\'': 559 break; 560 default: 561#if SHOPT_MULTIBYTE 562 if(!iswprint(c)) 563#else 564 if(!isprint(c)) 565#endif 566 { 567 if ((n -= 4) <= 0) 568 { 569 stakwrite("'\\\n$'", 5); 570 n = fold - 7; 571 } 572 sfprintf(staksp, "\\%03o", c); 573 continue; 574 } 575 q = 0; 576 break; 577 } 578 if ((n -= q + 1) <= 0) 579 { 580 if (!q) 581 { 582 stakputc('\''); 583 cp = bp; 584 break; 585 } 586 stakwrite("'\\\n$'", 5); 587 n = fold - 5; 588 } 589 if (q) 590 stakputc('\\'); 591 else 592 q = 1; 593 stakputc(c); 594 bp = cp; 595 } 596 if (!c) 597 stakputc('\''); 598 } 599 else if (q & 1) 600 { 601 stakputc('\''); 602 cp = bp; 603 n = fold ? (fold - 2) : 0; 604 while (c = mbchar(cp)) 605 { 606 if (c == '\n') 607 n = fold - 1; 608 else if (n && --n <= 0) 609 { 610 n = fold - 2; 611 stakwrite(bp, --cp - bp); 612 bp = cp; 613 stakwrite("'\\\n'", 4); 614 } 615 else if (n == 1 && *cp == '\'') 616 { 617 n = fold - 5; 618 stakwrite(bp, --cp - bp); 619 bp = cp; 620 stakwrite("'\\\n\\''", 6); 621 } 622 else if (c == '\'') 623 { 624 stakwrite(bp, cp - bp - 1); 625 bp = cp; 626 if (n && (n -= 4) <= 0) 627 { 628 n = fold - 5; 629 stakwrite("'\\\n\\''", 6); 630 } 631 else 632 stakwrite("'\\''", 4); 633 } 634 } 635 stakwrite(bp, cp - bp - 1); 636 stakputc('\''); 637 } 638 else if (n = fold) 639 { 640 cp = bp; 641 while (c = mbchar(cp)) 642 { 643 if (--n <= 0) 644 { 645 n = fold; 646 stakwrite(bp, --cp - bp); 647 bp = cp; 648 stakwrite("\\\n", 2); 649 } 650 } 651 stakwrite(bp, cp - bp - 1); 652 } 653 else 654 stakwrite(bp, cp - bp); 655 if (c) 656 { 657 stakputc('\\'); 658 stakputc('\n'); 659 } 660 } while (c); 661 stakputc(0); 662 return(stakptr(offset)); 663} 664 665#if SHOPT_MULTIBYTE 666 int sh_strchr(const char *string, register const char *dp) 667 { 668 wchar_t c, d; 669 register const char *cp=string; 670 mbinit(); 671 d = mbchar(dp); 672 mbinit(); 673 while(c = mbchar(cp)) 674 { 675 if(c==d) 676 return(cp-string); 677 } 678 if(d==0) 679 return(cp-string); 680 return(-1); 681 } 682#endif /* SHOPT_MULTIBYTE */ 683 684const char *_sh_translate(const char *message) 685{ 686#if ERROR_VERSION >= 20000317L 687 return(ERROR_translate(0,0,e_dict,message)); 688#else 689#if ERROR_VERSION >= 20000101L 690 return(ERROR_translate(e_dict,message)); 691#else 692 return(ERROR_translate(message,1)); 693#endif 694#endif 695} 696 697/* 698 * change '['identifier']' to identifier 699 * character before <str> must be a '[' 700 * returns pointer to last character 701 */ 702char *sh_checkid(char *str, char *last) 703{ 704 register unsigned char *cp = (unsigned char*)str; 705 register unsigned char *v = cp; 706 register int c; 707 if(c=mbchar(cp),isaletter(c)) 708 while(c=mbchar(cp),isaname(c)); 709 if(c==']' && (!last || ((char*)cp==last))) 710 { 711 /* eliminate [ and ] */ 712 while(v < cp) 713 { 714 v[-1] = *v; 715 v++; 716 } 717 if(last) 718 last -=2; 719 else 720 { 721 while(*v) 722 { 723 v[-2] = *v; 724 v++; 725 } 726 v[-2] = 0; 727 last = (char*)v; 728 } 729 } 730 return(last); 731} 732 733#if _AST_VERSION <= 20000317L 734char *fmtident(const char *string) 735{ 736 return((char*)string); 737} 738#endif 739