1 /* 2 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ 7 /* All Rights Reserved */ 8 9 /* 10 * Copyright (c) 1980 Regents of the University of California. 11 * All rights reserved. The Berkeley Software License Agreement 12 * specifies the terms and conditions for redistribution. 13 */ 14 15 #include "sh.h" 16 #include "sh.tconst.h" 17 #include <dirent.h> 18 #include <strings.h> 19 #ifdef MBCHAR 20 #include <widec.h> /* wcsetno() */ 21 #include <fnmatch.h> /* fnmatch() */ 22 #endif /* MBCHAR */ 23 24 /* 25 * C Shell 26 */ 27 28 int globcnt; 29 30 tchar *gpath, *gpathp, *lastgpathp; 31 int globbed; 32 bool noglob; 33 bool nonomatch; 34 tchar *entp; 35 tchar **sortbas; 36 int sortscmp(tchar **, tchar **); 37 void ginit(tchar **); 38 void collect(tchar *); 39 void acollect(tchar *); 40 void expand(tchar *); 41 void matchdir_(tchar *); 42 void Gcat(tchar *, tchar *); 43 void addpath(tchar); 44 void tglob(tchar **); 45 tchar **dobackp(tchar *, bool); 46 void backeval(tchar *, bool); 47 void psave(tchar); 48 void pword(void); 49 50 extern DIR *opendir_(tchar *); 51 52 #define sort() qsort((char *)sortbas, &gargv[gargc] - sortbas, \ 53 sizeof (*sortbas), (int (*)(const void *, \ 54 const void *)) sortscmp), sortbas = &gargv[gargc] 55 56 57 tchar ** 58 glob(tchar **v) 59 { 60 tchar agpath[BUFSIZ]; 61 tchar *agargv[GAVSIZ]; 62 63 gpath = agpath; gpathp = gpath; *gpathp = 0; 64 lastgpathp = &gpath[BUFSIZ - 2]; 65 ginit(agargv); globcnt = 0; 66 #ifdef TRACE 67 tprintf("TRACE- glob()\n"); 68 #endif 69 #ifdef GDEBUG 70 printf("glob entered: "); blkpr(v); printf("\n"); 71 #endif 72 noglob = adrof(S_noglob /* "noglob" */) != 0; 73 nonomatch = adrof(S_nonomatch /* "nonomatch" */) != 0; 74 globcnt = noglob | nonomatch; 75 while (*v) 76 collect(*v++); 77 #ifdef GDEBUG 78 printf("glob done, globcnt=%d, gflag=%d: ", globcnt, gflag); 79 blkpr(gargv); printf("\n"); 80 #endif 81 if (globcnt == 0 && (gflag&1)) { 82 blkfree(gargv), gargv = 0; 83 return (0); 84 } else 85 return (gargv = copyblk(gargv)); 86 } 87 88 void 89 ginit(tchar **agargv) 90 { 91 92 agargv[0] = 0; gargv = agargv; sortbas = agargv; gargc = 0; 93 gnleft = NCARGS - 4; 94 } 95 96 void 97 collect(tchar *as) 98 { 99 int i; 100 101 #ifdef TRACE 102 tprintf("TRACE- collect()\n"); 103 #endif 104 if (any('`', as)) { 105 #ifdef GDEBUG 106 printf("doing backp of %t\n", as); 107 #endif 108 (void) dobackp(as, 0); 109 #ifdef GDEBUG 110 printf("backp done, acollect'ing\n"); 111 #endif 112 /* 113 * dobackp has the side effect of messing with 114 * gflag, since it does more globbing, so check 115 * if the results is still globbable 116 */ 117 tglob(pargv); 118 119 for (i = 0; i < pargc; i++) 120 if (noglob) { 121 Gcat(pargv[i], S_ /* "" */); 122 sortbas = &gargv[gargc]; 123 } else 124 acollect(pargv[i]); 125 if (pargv) 126 blkfree(pargv), pargv = 0; 127 #ifdef GDEBUG 128 printf("acollect done\n"); 129 #endif 130 } else if (noglob || eq(as, S_LBRA /* "{" */) || 131 eq(as, S_BRABRA /* "{}" */)) { 132 Gcat(as, S_ /* "" */); 133 sort(); 134 } else 135 acollect(as); 136 } 137 138 void 139 acollect(tchar *as) 140 { 141 long ogargc = gargc; 142 143 #ifdef TRACE 144 tprintf("TRACE- acollect()\n"); 145 #endif 146 gpathp = gpath; *gpathp = 0; globbed = 0; 147 expand(as); 148 if (gargc == ogargc) { 149 if (nonomatch) { 150 Gcat(as, S_ /* "" */); 151 sort(); 152 } 153 } else 154 sort(); 155 } 156 157 /* 158 * String compare for qsort. Also used by filec code in sh.file.c. 159 */ 160 int 161 sortscmp(tchar **a1, tchar **a2) 162 { 163 164 return (strcoll_(*a1, *a2)); 165 } 166 167 void 168 expand(tchar *as) 169 { 170 tchar *cs; 171 tchar *sgpathp, *oldcs; 172 struct stat stb; 173 174 #ifdef TRACE 175 tprintf("TRACE- expand()\n"); 176 #endif 177 sgpathp = gpathp; 178 cs = as; 179 if (*cs == '~' && gpathp == gpath) { 180 addpath('~'); 181 for (cs++; alnum(*cs) || *cs == '-'; ) 182 addpath(*cs++); 183 if (!*cs || *cs == '/') { 184 if (gpathp != gpath + 1) { 185 *gpathp = 0; 186 if (gethdir(gpath + 1)) 187 /* 188 * modified from %s to %t 189 */ 190 error("Unknown user: %t", gpath + 1); 191 (void) strcpy_(gpath, gpath + 1); 192 } else 193 (void) strcpy_(gpath, 194 value(S_home /* "home" */)); 195 gpathp = strend(gpath); 196 } 197 } 198 while (!isglob(*cs)) { 199 if (*cs == 0) { 200 if (!globbed) 201 Gcat(gpath, S_ /* "" */); 202 else if (lstat_(gpath, &stb) >= 0) { 203 Gcat(gpath, S_ /* "" */); 204 globcnt++; 205 } 206 goto endit; 207 } 208 addpath(*cs++); 209 } 210 oldcs = cs; 211 while (cs > as && *cs != '/') 212 cs--, gpathp--; 213 if (*cs == '/') 214 cs++, gpathp++; 215 *gpathp = 0; 216 if (*oldcs == '{') { 217 (void) execbrc(cs, NOSTR); 218 return; 219 } 220 matchdir_(cs); 221 endit: 222 gpathp = sgpathp; 223 *gpathp = 0; 224 } 225 226 void 227 matchdir_(tchar *pattern) 228 { 229 struct stat stb; 230 struct dirent *dp; 231 DIR *dirp; 232 tchar curdir_[MAXNAMLEN+1]; 233 int slproc = 0; 234 235 #ifdef TRACE 236 tprintf("TRACE- matchdir()\n"); 237 #endif 238 /* 239 * BSD's opendir would open "." if argument is NULL, but not S5 240 */ 241 242 if (*gpath == '\0') 243 dirp = opendir_(S_DOT /* "." */); 244 else 245 dirp = opendir_(gpath); 246 if (dirp == NULL) { 247 if (globbed) 248 return; 249 goto patherr2; 250 } 251 if (fstat(dirp->dd_fd, &stb) < 0) 252 goto patherr1; 253 if (!isdir(stb)) { 254 errno = ENOTDIR; 255 goto patherr1; 256 } 257 while ((dp = readdir(dirp)) != NULL) { 258 259 if (dp->d_ino == 0) 260 continue; 261 strtots(curdir_, dp->d_name); 262 slproc = 0; 263 if (match(curdir_, pattern, &slproc)) { 264 Gcat(gpath, curdir_); 265 globcnt++; 266 } 267 } 268 unsetfd(dirp->dd_fd); 269 closedir_(dirp); 270 return; 271 272 patherr1: 273 unsetfd(dirp->dd_fd); 274 closedir_(dirp); 275 patherr2: 276 Perror(gpath); 277 } 278 279 int 280 execbrc(tchar *p, tchar *s) 281 { 282 tchar restbuf[BUFSIZ + 2]; 283 tchar *pe, *pm, *pl; 284 int brclev = 0; 285 tchar *lm, savec, *sgpathp; 286 int slproc = 0; 287 288 #ifdef TRACE 289 tprintf("TRACE- execbrc()\n"); 290 #endif 291 for (lm = restbuf; *p != '{'; *lm++ = *p++) 292 continue; 293 for (pe = ++p; *pe; pe++) 294 switch (*pe) { 295 296 case '{': 297 brclev++; 298 continue; 299 300 case '}': 301 if (brclev == 0) 302 goto pend; 303 brclev--; 304 continue; 305 306 case '[': 307 for (pe++; *pe && *pe != ']'; pe++) 308 continue; 309 if (!*pe) 310 error("Missing ]"); 311 continue; 312 } 313 pend: 314 if (brclev || !*pe) 315 error("Missing }"); 316 for (pl = pm = p; pm <= pe; pm++) 317 switch (*pm & (QUOTE|TRIM)) { 318 319 case '{': 320 brclev++; 321 continue; 322 323 case '}': 324 if (brclev) { 325 brclev--; 326 continue; 327 } 328 goto doit; 329 330 case ',': 331 if (brclev) 332 continue; 333 doit: 334 savec = *pm; 335 *pm = 0; 336 (void) strcpy_(lm, pl); 337 (void) strcat_(restbuf, pe + 1); 338 *pm = savec; 339 if (s == 0) { 340 sgpathp = gpathp; 341 expand(restbuf); 342 gpathp = sgpathp; 343 *gpathp = 0; 344 } else if (amatch(s, restbuf, &slproc)) 345 return (1); 346 sort(); 347 pl = pm + 1; 348 continue; 349 350 case '[': 351 for (pm++; *pm && *pm != ']'; pm++) 352 continue; 353 if (!*pm) 354 error("Missing ]"); 355 continue; 356 } 357 return (0); 358 } 359 360 int 361 match(tchar *s, tchar *p, int *slproc) 362 { 363 int c; 364 tchar *sentp; 365 tchar sglobbed = globbed; 366 367 #ifdef TRACE 368 tprintf("TRACE- match()\n"); 369 #endif 370 if (*s == '.' && *p != '.') 371 return (0); 372 sentp = entp; 373 entp = s; 374 c = amatch(s, p, slproc); 375 entp = sentp; 376 globbed = sglobbed; 377 return (c); 378 } 379 380 int 381 amatch(tchar *s, tchar *p, int *slproc) 382 { 383 int scc; 384 int ok, lc; 385 tchar *sgpathp; 386 struct stat stb; 387 int c, cc; 388 389 #ifdef TRACE 390 tprintf("TRACE- amatch()\n"); 391 #endif 392 globbed = 1; 393 for (;;) { 394 scc = *s++ & TRIM; 395 switch (c = *p++) { 396 397 case '{': 398 return (execbrc(p - 1, s - 1)); 399 400 case '[': 401 ok = 0; 402 lc = TRIM; 403 while (cc = *p++) { 404 if (cc == ']') { 405 if (ok) 406 break; 407 return (0); 408 } 409 if (cc == '-') { 410 #ifdef MBCHAR 411 wchar_t rc = *p++; 412 if (rc == ']') { 413 p--; 414 continue; 415 } 416 /* 417 * Both ends of the char range 418 * must belong to the same codeset. 419 */ 420 if (sh_bracket_exp(scc, lc, rc)) 421 ok++; 422 #else /* !MBCHAR */ 423 if (lc <= scc && scc <= (int)*p++) 424 ok++; 425 #endif /* !MBCHAR */ 426 } else 427 if (scc == (lc = cc)) 428 ok++; 429 } 430 if (cc == 0) 431 error("Missing ]"); 432 continue; 433 434 case '*': 435 if (!*p) 436 return (1); 437 if (*p == '/') { 438 p++; 439 goto slash; 440 } else if (*p == '*') { 441 s--; 442 continue; 443 } 444 445 for (s--; *s; s++) 446 if (amatch(s, p, slproc)) 447 return (1); 448 449 return (0); 450 451 case 0: 452 return (scc == 0); 453 454 default: 455 if ((c & TRIM) != scc) 456 return (0); 457 continue; 458 459 case '?': 460 if (scc == 0) 461 return (0); 462 continue; 463 464 case '/': 465 if (scc) 466 return (0); 467 slash: 468 if (*slproc) /* Need to expand "/" only once */ 469 return (0); 470 else 471 *slproc = 1; 472 473 s = entp; 474 sgpathp = gpathp; 475 while (*s) 476 addpath(*s++); 477 addpath('/'); 478 if (stat_(gpath, &stb) == 0 && isdir(stb)) 479 if (*p == 0) { 480 Gcat(gpath, S_ /* "" */); 481 globcnt++; 482 } else 483 expand(p); 484 gpathp = sgpathp; 485 *gpathp = 0; 486 return (0); 487 } 488 } 489 } 490 491 int 492 Gmatch(tchar *s, tchar *p) 493 { 494 int scc; 495 int ok, lc; 496 int c, cc; 497 498 #ifdef TRACE 499 tprintf("TRACE- Gmatch()\n"); 500 #endif 501 for (;;) { 502 scc = *s++ & TRIM; 503 switch (c = *p++) { 504 505 case '[': 506 ok = 0; 507 lc = TRIM; 508 while (cc = *p++) { 509 if (cc == ']') { 510 if (ok) 511 break; 512 return (0); 513 } 514 if (cc == '-') { 515 #ifdef MBCHAR 516 wchar_t rc = *p++; 517 /* 518 * Both ends of the char range 519 * must belong to the same codeset... 520 */ 521 if (sh_bracket_exp(scc, lc, rc)) 522 ok++; 523 #else /* !MBCHAR */ 524 if (lc <= scc && scc <= (int)*p++) 525 ok++; 526 #endif /* !MBCHAR */ 527 } else 528 if (scc == (lc = cc)) 529 ok++; 530 } 531 if (cc == 0) 532 bferr("Missing ]"); 533 continue; 534 535 case '*': 536 if (!*p) 537 return (1); 538 for (s--; *s; s++) 539 if (Gmatch(s, p)) 540 return (1); 541 return (0); 542 543 case 0: 544 return (scc == 0); 545 546 default: 547 if ((c & TRIM) != scc) 548 return (0); 549 continue; 550 551 case '?': 552 if (scc == 0) 553 return (0); 554 continue; 555 556 } 557 } 558 } 559 560 void 561 Gcat(tchar *s1, tchar *s2) 562 { 563 tchar *p, *q; 564 int n; 565 566 #ifdef TRACE 567 tprintf("TRACE- Gcat()\n"); 568 #endif 569 for (p = s1; *p++; ) 570 ; 571 for (q = s2; *q++; ) 572 ; 573 gnleft -= (n = (p - s1) + (q - s2) - 1); 574 if (gnleft <= 0 || ++gargc >= GAVSIZ) 575 error("Arguments too long"); 576 gargv[gargc] = 0; 577 p = gargv[gargc - 1] = (tchar *) xalloc((unsigned)n*sizeof (tchar)); 578 579 for (q = s1; *p++ = *q++; ) 580 ; 581 for (p--, q = s2; *p++ = *q++; ) 582 ; 583 } 584 585 void 586 addpath(tchar c) 587 { 588 589 #ifdef TRACE 590 tprintf("TRACE- addpath()\n"); 591 #endif 592 if (gpathp >= lastgpathp) 593 error("Pathname too long"); 594 *gpathp++ = c & TRIM; 595 *gpathp = 0; 596 } 597 598 void 599 rscan(tchar **t, int (*f)(int)) 600 { 601 tchar *p; 602 603 #ifdef TRACE 604 tprintf("TRACE- rscan()\n"); 605 #endif 606 while (p = *t++) 607 while (*p) 608 (*f)(*p++); 609 } 610 611 void 612 trim(tchar **t) 613 { 614 tchar *p; 615 616 #ifdef TRACE 617 tprintf("TRACE- trim()\n"); 618 #endif 619 while (p = *t++) 620 while (*p) 621 *p++ &= TRIM; 622 } 623 624 void 625 tglob(tchar **t) 626 { 627 tchar *p, c; 628 629 #ifdef TRACE 630 tprintf("TRACE- tglob()\n"); 631 #endif 632 while (p = *t++) { 633 if (*p == '~') 634 gflag |= 2; 635 else if (*p == '{' && (p[1] == '\0' || 636 p[1] == '}' && p[2] == '\0')) 637 continue; 638 while (c = *p++) 639 if (isglob(c)) 640 gflag |= c == '{' ? 2 : 1; 641 } 642 } 643 644 tchar * 645 globone(tchar *str) 646 { 647 tchar *gv[2]; 648 tchar **gvp; 649 tchar *cp; 650 651 #ifdef TRACE 652 tprintf("TRACE- globone()\n"); 653 #endif 654 gv[0] = str; 655 gv[1] = 0; 656 gflag = 0; 657 tglob(gv); 658 if (gflag) { 659 gvp = glob(gv); 660 if (gvp == 0) { 661 setname(str); 662 bferr("No match"); 663 } 664 cp = *gvp++; 665 if (cp == 0) 666 cp = S_ /* "" */; 667 else if (*gvp) { 668 setname(str); 669 bferr("Ambiguous"); 670 } else 671 cp = strip(cp); 672 #if 0 673 if (cp == 0 || *gvp) { 674 setname(str); 675 bferr(cp ? "Ambiguous" : "No output"); 676 } 677 #endif 678 xfree((char *)gargv); gargv = 0; 679 } else { 680 trim(gv); 681 cp = savestr(gv[0]); 682 } 683 return (cp); 684 } 685 686 /* 687 * Command substitute cp. If literal, then this is 688 * a substitution from a << redirection, and so we should 689 * not crunch blanks and tabs, separating words only at newlines. 690 */ 691 tchar ** 692 dobackp(tchar *cp, bool literal) 693 { 694 tchar *lp, *rp; 695 tchar *ep; 696 tchar word[BUFSIZ]; 697 tchar *apargv[GAVSIZ + 2]; 698 699 #ifdef TRACE 700 tprintf("TRACE- dobackp()\n"); 701 #endif 702 if (pargv) { 703 blkfree(pargv); 704 } 705 pargv = apargv; 706 pargv[0] = NOSTR; 707 pargcp = pargs = word; 708 pargc = 0; 709 pnleft = BUFSIZ - 4; 710 for (;;) { 711 for (lp = cp; *lp != '`'; lp++) { 712 if (*lp == 0) { 713 if (pargcp != pargs) 714 pword(); 715 #ifdef GDEBUG 716 printf("leaving dobackp\n"); 717 #endif 718 return (pargv = copyblk(pargv)); 719 } 720 psave(*lp); 721 } 722 lp++; 723 for (rp = lp; *rp && *rp != '`'; rp++) 724 if (*rp == '\\') { 725 rp++; 726 if (!*rp) 727 goto oops; 728 } 729 if (!*rp) 730 oops: 731 error("Unmatched `"); 732 ep = savestr(lp); 733 ep[rp - lp] = 0; 734 backeval(ep, literal); 735 #ifdef GDEBUG 736 printf("back from backeval\n"); 737 #endif 738 cp = rp + 1; 739 } 740 } 741 742 void 743 backeval(tchar *cp, bool literal) 744 { 745 int pvec[2]; 746 int quoted = (literal || (cp[0] & QUOTE)) ? QUOTE : 0; 747 tchar ibuf[BUFSIZ + MB_LEN_MAX]; /* read_ can return extra bytes */ 748 int icnt = 0, c; 749 tchar *ip; 750 bool hadnl = 0; 751 tchar *fakecom[2]; 752 struct command faket; 753 754 #ifdef TRACE 755 tprintf("TRACE- backeval()\n"); 756 #endif 757 faket.t_dtyp = TCOM; 758 faket.t_dflg = 0; 759 faket.t_dlef = 0; 760 faket.t_drit = 0; 761 faket.t_dspr = 0; 762 faket.t_dcom = fakecom; 763 fakecom[0] = S_QPPPQ; /* "` ... `" */; 764 fakecom[1] = 0; 765 /* 766 * We do the psave job to temporarily change the current job 767 * so that the following fork is considered a separate job. 768 * This is so that when backquotes are used in a 769 * builtin function that calls glob the "current job" is not corrupted. 770 * We only need one level of pushed jobs as long as we are sure to 771 * fork here. 772 */ 773 psavejob(); 774 /* 775 * It would be nicer if we could integrate this redirection more 776 * with the routines in sh.sem.c by doing a fake execute on a builtin 777 * function that was piped out. 778 */ 779 mypipe(pvec); 780 if (pfork(&faket, -1) == 0) { 781 struct wordent paraml; 782 struct command *t; 783 tchar oHIST; 784 785 new_process(); 786 (void) close(pvec[0]); 787 unsetfd(pvec[0]); 788 (void) dmove(pvec[1], 1); 789 (void) dmove(SHDIAG, 2); 790 reinitdesc(0, NULL); 791 arginp = cp; 792 while (*cp) 793 *cp++ &= TRIM; 794 /* 795 * disable history subsitution in sub-shell 796 * of `` evaluation prevents possible 797 * infinite recursion of `` evaluation 798 */ 799 oHIST = HIST; 800 HIST = 0; 801 (void) lex(¶ml); 802 HIST = oHIST; 803 if (err) 804 error("%s", gettext(err)); 805 alias(¶ml); 806 t = syntax(paraml.next, ¶ml, 0); 807 if (err) 808 error("%s", gettext(err)); 809 if (t) 810 t->t_dflg |= FPAR; 811 (void) signal(SIGTSTP, SIG_IGN); 812 (void) signal(SIGTTIN, SIG_IGN); 813 (void) signal(SIGTTOU, SIG_IGN); 814 execute(t, -1); 815 exitstat(); 816 } 817 xfree(cp); 818 (void) close(pvec[1]); 819 unsetfd(pvec[1]); 820 do { 821 int cnt = 0; 822 for (;;) { 823 if (icnt == 0) { 824 ip = ibuf; 825 icnt = read_(pvec[0], ip, BUFSIZ); 826 if (icnt <= 0) { 827 c = -1; 828 break; 829 } 830 } 831 if (hadnl) 832 break; 833 --icnt; 834 c = (*ip++ & TRIM); 835 if (c == 0) 836 break; 837 if (c == '\n') { 838 /* 839 * Continue around the loop one 840 * more time, so that we can eat 841 * the last newline without terminating 842 * this word. 843 */ 844 hadnl = 1; 845 continue; 846 } 847 if (!quoted && issp(c)) 848 break; 849 cnt++; 850 psave(c | quoted); 851 } 852 /* 853 * Unless at end-of-file, we will form a new word 854 * here if there were characters in the word, or in 855 * any case when we take text literally. If 856 * we didn't make empty words here when literal was 857 * set then we would lose blank lines. 858 */ 859 if (c != -1 && (cnt || literal)) { 860 if (pargc == GAVSIZ) 861 break; 862 pword(); 863 } 864 hadnl = 0; 865 } while (c >= 0); 866 #ifdef GDEBUG 867 printf("done in backeval, pvec: %d %d\n", pvec[0], pvec[1]); 868 printf("also c = %c <%o>\n", (tchar) c, (tchar) c); 869 #endif 870 (void) close(pvec[0]); 871 unsetfd(pvec[0]); 872 pwait(); 873 prestjob(); 874 } 875 876 void 877 psave(tchar c) 878 { 879 #ifdef TRACE 880 tprintf("TRACE- psave()\n"); 881 #endif 882 883 if (--pnleft <= 0) 884 error("Word too long"); 885 *pargcp++ = c; 886 } 887 888 void 889 pword(void) 890 { 891 #ifdef TRACE 892 tprintf("TRACE- pword()\n"); 893 #endif 894 895 psave(0); 896 if (pargc == GAVSIZ) 897 error("Too many words from ``"); 898 pargv[pargc++] = savestr(pargs); 899 pargv[pargc] = NOSTR; 900 #ifdef GDEBUG 901 printf("got word %t\n", pargv[pargc-1]); 902 #endif 903 pargcp = pargs; 904 pnleft = BUFSIZ - 4; 905 } 906 907 908 909 /* 910 * returns pathname of the form dir/file; 911 * dir is a null-terminated string; 912 */ 913 char * 914 makename(char *dir, char *file) 915 { 916 /* 917 * Maximum length of a 918 * file/dir name in ls-command; 919 * dfile is static as this is returned 920 * by makename(); 921 */ 922 static char dfile[MAXNAMLEN]; 923 924 char *dp, *fp; 925 926 dp = dfile; 927 fp = dir; 928 while (*fp) 929 *dp++ = *fp++; 930 if (dp > dfile && *(dp - 1) != '/') 931 *dp++ = '/'; 932 fp = file; 933 while (*fp) 934 *dp++ = *fp++; 935 *dp = '\0'; 936 /* 937 * dfile points to the absolute pathname. We are 938 * only interested in the last component. 939 */ 940 return (rindex(dfile, '/') + 1); 941 } 942 943 int 944 sh_bracket_exp(tchar t_ch, tchar t_fch, tchar t_lch) 945 { 946 char t_char[MB_LEN_MAX + 1]; 947 char t_patan[MB_LEN_MAX * 2 + 8]; 948 char *p; 949 int i; 950 951 if ((t_ch == t_fch) || (t_ch == t_lch)) 952 return (1); 953 954 p = t_patan; 955 if ((i = wctomb(t_char, (wchar_t)t_ch)) <= 0) 956 return (0); 957 t_char[i] = 0; 958 959 *p++ = '['; 960 if ((i = wctomb(p, (wchar_t)t_fch)) <= 0) 961 return (0); 962 p += i; 963 *p++ = '-'; 964 if ((i = wctomb(p, (wchar_t)t_lch)) <= 0) 965 return (0); 966 p += i; 967 *p++ = ']'; 968 *p = 0; 969 970 if (fnmatch(t_patan, t_char, FNM_NOESCAPE)) 971 return (0); 972 return (1); 973 } 974