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