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