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