1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2002 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ 28 /* All Rights Reserved */ 29 30 /* 31 * University Copyright- Copyright (c) 1982, 1986, 1988 32 * The Regents of the University of California 33 * All Rights Reserved 34 * 35 * University Acknowledgment- Portions of this document are derived from 36 * software developed by the University of California, Berkeley, and its 37 * contributors. 38 */ 39 40 #pragma ident "%Z%%M% %I% %E% SMI" 41 42 /* 43 * C-shell glob for random programs. 44 */ 45 46 #include "ftp_var.h" 47 48 #ifndef NCARGS 49 #define NCARGS 5120 50 #endif 51 52 #define QUOTE 0200 53 #define TRIM 0177 54 #define eq(a, b) (strcmp(a, b) == 0) 55 56 /* 57 * According to the person who wrote the C shell "glob" code, a reasonable 58 * limit on number of arguments would seem to be the maximum number of 59 * characters in an arg list / 6. 60 * 61 * XXX: With the new VM system, NCARGS has become enormous, making 62 * it impractical to allocate arrays with NCARGS / 6 entries on 63 * the stack. The proper fix is to revamp code elsewhere (in 64 * sh.dol.c and sh.glob.c) to use a different technique for handling 65 * command line arguments. In the meantime, we simply fall back 66 * on using the old value of NCARGS. 67 */ 68 #ifdef notyet 69 #define GAVSIZ (NCARGS / 6) 70 #else /* notyet */ 71 #define GAVSIZ (10240 / 6) 72 #endif /* notyet */ 73 74 static char **gargv; /* Pointer to the (stack) arglist */ 75 static char **agargv; 76 static int agargv_size; 77 static long gargc; /* Number args in gargv */ 78 static short gflag; 79 static char *strspl(); 80 static char *strend(char *cp); 81 static char *strspl(char *cp, char *dp); 82 static int tglob(char c); 83 static char **copyblk(char **v); 84 static void ginit(char **agargv); 85 static void addpath(char c); 86 static int any(int c, char *s); 87 static void Gcat(char *s1, char *s2); 88 static void collect(char *as); 89 static void acollect(char *as); 90 static void sort(void); 91 static void expand(char *as); 92 static void matchdir(char *pattern); 93 static int execbrc(char *p, char *s); 94 static int ftp_fnmatch(wchar_t t_ch, wchar_t t_fch, wchar_t t_lch); 95 static int gethdir(char *home); 96 static void xfree(char *cp); 97 static void rscan(char **t, int (*f)(char)); 98 static int letter(char c); 99 static int digit(char c); 100 static int match(char *s, char *p); 101 static int amatch(char *s, char *p); 102 static int blklen(char **av); 103 static char **blkcpy(char **oav, char **bv); 104 105 static int globcnt; 106 107 static char *globchars = "`{[*?"; 108 109 static char *gpath, *gpathp, *lastgpathp; 110 static int globbed; 111 static char *entp; 112 static char **sortbas; 113 114 char ** 115 glob(char *v) 116 { 117 char agpath[FTPBUFSIZ]; 118 char *vv[2]; 119 120 if (agargv == NULL) { 121 agargv = (char **)malloc(GAVSIZ * sizeof (char *)); 122 agargv_size = GAVSIZ; 123 if (agargv == NULL) { 124 globerr = "Arguments too long."; 125 return (0); 126 } 127 } 128 vv[0] = v; 129 vv[1] = 0; 130 globerr = 0; 131 gflag = 0; 132 rscan(vv, tglob); 133 if (gflag == 0) 134 return (copyblk(vv)); 135 136 gpath = agpath; 137 gpathp = gpath; 138 *gpathp = 0; 139 lastgpathp = &gpath[sizeof (agpath) - 2]; 140 ginit(agargv); 141 globcnt = 0; 142 collect(v); 143 if (globcnt == 0 && (gflag&1)) { 144 blkfree(gargv); 145 if (gargv == agargv) 146 agargv = 0; 147 gargv = 0; 148 return (0); 149 } else 150 return (gargv = copyblk(gargv)); 151 } 152 153 static void 154 ginit(char **agargv) 155 { 156 157 agargv[0] = 0; 158 gargv = agargv; 159 sortbas = agargv; 160 gargc = 0; 161 } 162 163 static void 164 collect(char *as) 165 { 166 if (eq(as, "{") || eq(as, "{}")) { 167 Gcat(as, ""); 168 sort(); 169 } else 170 acollect(as); 171 } 172 173 static void 174 acollect(char *as) 175 { 176 register long ogargc = gargc; 177 178 gpathp = gpath; *gpathp = 0; globbed = 0; 179 expand(as); 180 if (gargc != ogargc) 181 sort(); 182 } 183 184 static void 185 sort(void) 186 { 187 register char **p1, **p2, *c; 188 char **Gvp = &gargv[gargc]; 189 190 p1 = sortbas; 191 while (p1 < Gvp-1) { 192 p2 = p1; 193 while (++p2 < Gvp) 194 if (strcmp(*p1, *p2) > 0) 195 c = *p1, *p1 = *p2, *p2 = c; 196 p1++; 197 } 198 sortbas = Gvp; 199 } 200 201 static void 202 expand(char *as) 203 { 204 register char *cs; 205 register char *sgpathp, *oldcs; 206 struct stat stb; 207 208 sgpathp = gpathp; 209 cs = as; 210 if (*cs == '~' && gpathp == gpath) { 211 addpath('~'); 212 cs++; 213 while (letter(*cs) || digit(*cs) || *cs == '-') 214 addpath(*cs++); 215 if (!*cs || *cs == '/') { 216 if (gpathp != gpath + 1) { 217 *gpathp = 0; 218 if (gethdir(gpath + 1)) 219 globerr = "Unknown user name after ~"; 220 (void) strcpy(gpath, gpath + 1); 221 } else 222 (void) strcpy(gpath, home); 223 gpathp = strend(gpath); 224 } 225 } 226 while (!any(*cs, globchars)) { 227 if (*cs == 0) { 228 if (!globbed) 229 Gcat(gpath, ""); 230 else if (stat(gpath, &stb) >= 0) { 231 Gcat(gpath, ""); 232 globcnt++; 233 } 234 goto endit; 235 } 236 addpath(*cs++); 237 } 238 oldcs = cs; 239 while (cs > as && *cs != '/') 240 cs--, gpathp--; 241 if (*cs == '/') 242 cs++, gpathp++; 243 *gpathp = 0; 244 if (*oldcs == '{') { 245 (void) execbrc(cs, ((char *)0)); 246 return; 247 } 248 matchdir(cs); 249 endit: 250 gpathp = sgpathp; 251 *gpathp = 0; 252 } 253 254 static void 255 matchdir(char *pattern) 256 { 257 struct stat stb; 258 register struct dirent *dp; 259 DIR *dirp; 260 261 /* 262 * BSD/SunOS open() system call maps a null pathname into 263 * "." while System V does not. 264 */ 265 if (*gpath == (char)0) { 266 dirp = opendir("."); 267 } else 268 dirp = opendir(gpath); 269 if (dirp == NULL) { 270 if (globbed) 271 return; 272 goto patherr2; 273 } 274 if (fstat(dirp->dd_fd, &stb) < 0) 275 goto patherr1; 276 if (!S_ISDIR(stb.st_mode)) { 277 errno = ENOTDIR; 278 goto patherr1; 279 } 280 while ((dp = readdir(dirp)) != NULL) { 281 if (dp->d_ino == 0) 282 continue; 283 if (match(dp->d_name, pattern)) { 284 Gcat(gpath, dp->d_name); 285 globcnt++; 286 } 287 } 288 closedir(dirp); 289 return; 290 291 patherr1: 292 closedir(dirp); 293 patherr2: 294 globerr = "Bad directory components"; 295 } 296 297 static int 298 execbrc(char *p, char *s) 299 { 300 char restbuf[FTPBUFSIZ + 2]; 301 register char *pe, *pm, *pl; 302 int brclev = 0; 303 char *lm, savec, *sgpathp; 304 int len; 305 306 for (lm = restbuf; *p != '{'; *lm += len, p += len) { 307 if ((len = mblen(p, MB_CUR_MAX)) <= 0) 308 len = 1; 309 memcpy(lm, p, len); 310 } 311 312 for (pe = ++p; *pe; pe += len) { 313 if ((len = mblen(pe, MB_CUR_MAX)) <= 0) 314 len = 1; 315 316 switch (*pe) { 317 318 case '{': 319 brclev++; 320 continue; 321 322 case '}': 323 if (brclev == 0) 324 goto pend; 325 brclev--; 326 continue; 327 328 case '[': 329 for (pe++; *pe && *pe != ']'; pe += len) { 330 if ((len = mblen(pe, MB_CUR_MAX)) <= 0) 331 len = 1; 332 } 333 len = 1; 334 continue; 335 } 336 } 337 pend: 338 brclev = 0; 339 for (pl = pm = p; pm <= pe; pm += len) { 340 if ((len = mblen(pm, MB_CUR_MAX)) <= 0) 341 len = 1; 342 343 switch (*pm & (QUOTE|TRIM)) { 344 345 case '{': 346 brclev++; 347 continue; 348 349 case '}': 350 if (brclev) { 351 brclev--; 352 continue; 353 } 354 goto doit; 355 356 case ','|QUOTE: 357 case ',': 358 if (brclev) 359 continue; 360 doit: 361 savec = *pm; 362 *pm = 0; 363 (void) strcpy(lm, pl); 364 (void) strcat(restbuf, pe + 1); 365 *pm = savec; 366 if (s == 0) { 367 sgpathp = gpathp; 368 expand(restbuf); 369 gpathp = sgpathp; 370 *gpathp = 0; 371 } else if (amatch(s, restbuf)) 372 return (1); 373 sort(); 374 pl = pm + 1; 375 if (brclev) 376 return (0); 377 continue; 378 379 case '[': 380 for (pm++; *pm && *pm != ']'; pm += len) { 381 if ((len = mblen(pm, MB_CUR_MAX)) <= 0) 382 len = 1; 383 } 384 len = 1; 385 if (!*pm) 386 pm--; 387 continue; 388 } 389 } 390 if (brclev) 391 goto doit; 392 return (0); 393 } 394 395 static int 396 match(char *s, char *p) 397 { 398 register int c; 399 register char *sentp; 400 char sglobbed = globbed; 401 402 if (*s == '.' && *p != '.') 403 return (0); 404 sentp = entp; 405 entp = s; 406 c = amatch(s, p); 407 entp = sentp; 408 globbed = sglobbed; 409 return (c); 410 } 411 412 static int 413 amatch(char *s, char *p) 414 { 415 wchar_t scc; 416 int ok; 417 wchar_t lc1, lc2; 418 char *sgpathp; 419 struct stat stb; 420 wchar_t c, cc; 421 int len_s, len_p; 422 423 globbed = 1; 424 for (;;) { 425 if ((len_s = mbtowc(&scc, s, MB_CUR_MAX)) <= 0) { 426 scc = (unsigned char)*s; 427 len_s = 1; 428 } 429 /* scc = *s++ & TRIM; */ 430 s += len_s; 431 432 if ((len_p = mbtowc(&c, p, MB_CUR_MAX)) <= 0) { 433 c = (unsigned char)*p; 434 len_p = 1; 435 } 436 p += len_p; 437 switch (c) { 438 439 case '{': 440 return (execbrc(p - len_p, s - len_s)); 441 442 case '[': 443 ok = 0; 444 lc1 = 0; 445 while ((cc = *p) != '\0') { 446 if ((len_p = mbtowc(&cc, p, MB_CUR_MAX)) <= 0) { 447 cc = (unsigned char)*p; 448 len_p = 1; 449 } 450 p += len_p; 451 if (cc == ']') { 452 if (ok) 453 break; 454 return (0); 455 } 456 if (cc == '-') { 457 if ((len_p = mbtowc(&lc2, p, 458 MB_CUR_MAX)) <= 0) { 459 lc2 = (unsigned char)*p; 460 len_p = 1; 461 } 462 p += len_p; 463 if (ftp_fnmatch(scc, lc1, lc2)) 464 ok++; 465 } else 466 if (scc == (lc1 = cc)) 467 ok++; 468 } 469 if (cc == 0) 470 if (!ok) 471 return (0); 472 continue; 473 474 case '*': 475 if (!*p) 476 return (1); 477 if (*p == '/') { 478 p++; 479 goto slash; 480 } 481 s -= len_s; 482 do { 483 if (amatch(s, p)) 484 return (1); 485 } while (*s++); 486 return (0); 487 488 case 0: 489 return (scc == 0); 490 491 default: 492 if (c != scc) 493 return (0); 494 continue; 495 496 case '?': 497 if (scc == 0) 498 return (0); 499 continue; 500 501 case '/': 502 if (scc) 503 return (0); 504 slash: 505 s = entp; 506 sgpathp = gpathp; 507 while (*s) 508 addpath(*s++); 509 addpath('/'); 510 if (stat(gpath, &stb) == 0 && S_ISDIR(stb.st_mode)) 511 if (*p == 0) { 512 Gcat(gpath, ""); 513 globcnt++; 514 } else 515 expand(p); 516 gpathp = sgpathp; 517 *gpathp = 0; 518 return (0); 519 } 520 } 521 } 522 523 #ifdef notdef 524 static 525 Gmatch(s, p) 526 register char *s, *p; 527 { 528 register int scc; 529 int ok, lc; 530 int c, cc; 531 532 for (;;) { 533 scc = *s++ & TRIM; 534 switch (c = *p++) { 535 536 case '[': 537 ok = 0; 538 lc = 077777; 539 while (cc = *p++) { 540 if (cc == ']') { 541 if (ok) 542 break; 543 return (0); 544 } 545 if (cc == '-') { 546 if (lc <= scc && scc <= *p++) 547 ok++; 548 } else 549 if (scc == (lc = cc)) 550 ok++; 551 } 552 if (cc == 0) 553 if (ok) 554 p--; 555 else 556 return (0); 557 continue; 558 559 case '*': 560 if (!*p) 561 return (1); 562 for (s--; *s; s++) 563 if (Gmatch(s, p)) 564 return (1); 565 return (0); 566 567 case 0: 568 return (scc == 0); 569 570 default: 571 if ((c & TRIM) != scc) 572 return (0); 573 continue; 574 575 case '?': 576 if (scc == 0) 577 return (0); 578 continue; 579 580 } 581 } 582 } 583 #endif 584 585 static void 586 Gcat(char *s1, char *s2) 587 { 588 if (gargc >= agargv_size - 1) { 589 char **tmp; 590 591 if (globerr) { 592 return; 593 } 594 tmp = (char **)realloc(agargv, 595 (agargv_size + GAVSIZ) * sizeof (char *)); 596 if (tmp == NULL) { 597 globerr = "Arguments too long"; 598 return; 599 } else { 600 agargv = tmp; 601 agargv_size += GAVSIZ; 602 } 603 gargv = agargv; 604 sortbas = agargv; 605 } 606 gargc++; 607 gargv[gargc] = 0; 608 gargv[gargc - 1] = strspl(s1, s2); 609 } 610 611 static void 612 addpath(char c) 613 { 614 615 if (gpathp >= lastgpathp) 616 globerr = "Pathname too long"; 617 else { 618 *gpathp++ = c; 619 *gpathp = 0; 620 } 621 } 622 623 static void 624 rscan(char **t, int (*f)(char)) 625 { 626 register char *p, c; 627 int len; 628 629 while (p = *t++) { 630 if (f == tglob) 631 if (*p == '~') 632 gflag |= 2; 633 else if (eq(p, "{") || eq(p, "{}")) 634 continue; 635 while ((c = *p) != '\0') { 636 (void) (*f)(c); 637 if ((len = mblen(p, MB_CUR_MAX)) <= 0) 638 len = 1; 639 p += len; 640 } 641 } 642 } 643 644 static int 645 tglob(char c) 646 { 647 if (any(c, globchars)) 648 gflag |= c == '{' ? 2 : 1; 649 return (c); 650 } 651 652 static int 653 letter(char c) 654 { 655 return (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c == '_'); 656 } 657 658 static int 659 digit(char c) 660 { 661 return (c >= '0' && c <= '9'); 662 } 663 664 static int 665 any(int c, char *s) 666 { 667 int len; 668 669 while (*s) { 670 if (*s == c) 671 return (1); 672 if ((len = mblen(s, MB_CUR_MAX)) <= 0) 673 len = 1; 674 s += len; 675 } 676 return (0); 677 } 678 679 static int 680 blklen(char **av) 681 { 682 register int i = 0; 683 684 while (*av++) 685 i++; 686 return (i); 687 } 688 689 static char ** 690 blkcpy(char **oav, char **bv) 691 { 692 register char **av = oav; 693 694 while (*av++ = *bv++) 695 continue; 696 return (oav); 697 } 698 699 void 700 blkfree(char **av0) 701 { 702 register char **av = av0; 703 704 while (*av) 705 xfree(*av++); 706 free(av0); 707 } 708 709 static void 710 xfree(char *cp) 711 { 712 extern char end[]; 713 714 if (cp >= end && cp < (char *)&cp) 715 free(cp); 716 } 717 718 static char * 719 strspl(char *cp, char *dp) 720 { 721 register char *ep = malloc((unsigned)(strlen(cp) + strlen(dp) + 1)); 722 723 if (ep == (char *)0) 724 fatal("Out of memory"); 725 (void) strcpy(ep, cp); 726 (void) strcat(ep, dp); 727 return (ep); 728 } 729 730 static char ** 731 copyblk(char **v) 732 { 733 register char **nv = (char **)malloc((unsigned)((blklen(v) + 1) * 734 sizeof (char **))); 735 736 if (nv == (char **)0) 737 fatal("Out of memory"); 738 739 return (blkcpy(nv, v)); 740 } 741 742 static char * 743 strend(char *cp) 744 { 745 746 while (*cp) 747 cp++; 748 return (cp); 749 } 750 /* 751 * Extract a home directory from the password file 752 * The argument points to a buffer where the name of the 753 * user whose home directory is sought is currently. 754 * We write the home directory of the user back there. 755 */ 756 gethdir(char *home) 757 { 758 register struct passwd *pp = getpwnam(home); 759 760 if (!pp || home + strlen(pp->pw_dir) >= lastgpathp) 761 return (1); 762 (void) strcpy(home, pp->pw_dir); 763 return (0); 764 } 765 766 static int 767 ftp_fnmatch(wchar_t t_ch, wchar_t t_fch, wchar_t t_lch) 768 { 769 char t_char[MB_LEN_MAX + 1]; 770 char t_patan[MB_LEN_MAX * 2 + 8]; 771 char *p; 772 int i; 773 774 if ((t_ch == t_fch) || (t_ch == t_lch)) 775 return (1); 776 777 p = t_patan; 778 if ((i = wctomb(t_char, (wchar_t)t_ch)) <= 0) 779 return (0); 780 t_char[i] = 0; 781 782 *p++ = '['; 783 if ((i = wctomb(p, (wchar_t)t_fch)) <= 0) 784 return (0); 785 p += i; 786 *p++ = '-'; 787 if ((i = wctomb(p, (wchar_t)t_lch)) <= 0) 788 return (0); 789 p += i; 790 *p++ = ']'; 791 *p = 0; 792 793 if (fnmatch(t_patan, t_char, FNM_NOESCAPE)) 794 return (0); 795 return (1); 796 } 797