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