1 /* 2 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 #pragma ident "%Z%%M% %I% %E% SMI" 7 8 /**************************************************************************** 9 Copyright (c) 1999,2000,2001 WU-FTPD Development Group. 10 All rights reserved. 11 12 Portions Copyright (c) 1980, 1985, 1988, 1989, 1990, 1991, 1993, 1994 13 The Regents of the University of California. 14 Portions Copyright (c) 1993, 1994 Washington University in Saint Louis. 15 Portions Copyright (c) 1996, 1998 Berkeley Software Design, Inc. 16 Portions Copyright (c) 1989 Massachusetts Institute of Technology. 17 Portions Copyright (c) 1998 Sendmail, Inc. 18 Portions Copyright (c) 1983, 1995, 1996, 1997 Eric P. Allman. 19 Portions Copyright (c) 1997 by Stan Barber. 20 Portions Copyright (c) 1997 by Kent Landfield. 21 Portions Copyright (c) 1991, 1992, 1993, 1994, 1995, 1996, 1997 22 Free Software Foundation, Inc. 23 24 Use and distribution of this software and its source code are governed 25 by the terms and conditions of the WU-FTPD Software License ("LICENSE"). 26 27 If you did not receive a copy of the license, it may be obtained online 28 at http://www.wu-ftpd.org/license.html. 29 30 $Id: glob.c,v 1.14.2.2 2001/11/29 17:01:38 wuftpd Exp $ 31 32 ****************************************************************************/ 33 /* 34 * C-shell glob for random programs. 35 */ 36 37 #include "config.h" 38 39 #include <sys/param.h> 40 #include <sys/stat.h> 41 42 #ifdef HAVE_DIRENT_H 43 #include <dirent.h> 44 #else 45 #include <sys/dir.h> 46 #endif 47 48 #include <pwd.h> 49 #include <errno.h> 50 #include <stdio.h> 51 #include <stdlib.h> 52 #include <string.h> 53 54 #include "proto.h" 55 56 #define QUOTE 0200 57 #define TRIM 0177 58 #define eq(a,b) (strcmp(a, b)==0) 59 #define GAVSIZ (1024 * 8) 60 #define isdir(d) ((d.st_mode & S_IFMT) == S_IFDIR) 61 62 static char **gargv; /* Pointer to the (stack) arglist */ 63 static char **agargv; 64 static size_t agargv_size; 65 static int gargc; /* Number args in gargv */ 66 static size_t gnleft; 67 static short gflag; 68 static int tglob(register char); 69 70 /* Prototypes */ 71 72 static char *strend(register char *); 73 static void addpath(char); 74 static void ginit(char **); 75 static void collect(register char *); 76 static void acollect(register char *); 77 static void sort(void); 78 static void expand(char *); 79 static void matchdir(char *); 80 static int execbrc(char *, char *); 81 static int match(char *, char *); 82 static int amatch(char *, char *); 83 static void Gcat(register char *, register char *); 84 static void rscan(register char **, int (*f) (register char)); 85 static int tglob(register char c); 86 static int gethdir(char *); 87 88 int letter(register char); 89 int digit(register char); 90 int any(register int, register char *); 91 int blklen(register char **); 92 char **blkcpy(char **, register char **); 93 94 char *globerr; 95 char *home; 96 extern int errno; 97 98 static int globcnt; 99 100 char *globchars = "`{[*?"; 101 102 static char *gpath, *gpathp, *lastgpathp; 103 static int globbed; 104 static char *entp; 105 static char **sortbas; 106 107 #ifdef OTHER_PASSWD 108 #include "getpwnam.h" 109 extern char _path_passwd[]; 110 #endif 111 112 char **ftpglob(register char *v) 113 { 114 char agpath[BUFSIZ]; 115 char *vv[2]; 116 117 if (agargv == NULL) { 118 agargv = (char **) malloc(GAVSIZ * sizeof (char *)); 119 if (agargv == NULL) { 120 fatal("Out of memory"); 121 } 122 agargv_size = GAVSIZ; 123 } 124 fixpath(v); 125 if (v[0] == '\0') 126 v = "."; 127 else if ((strlen(v) > 1) && (v[strlen(v) - 1] == '/')) 128 v[strlen(v) - 1] = '\0'; 129 130 vv[0] = v; 131 vv[1] = NULL; 132 globerr = NULL; 133 gflag = 0; 134 rscan(vv, tglob); 135 if (gflag == 0) { 136 vv[0] = strspl(v, ""); 137 return (copyblk(vv)); 138 } 139 140 globerr = NULL; 141 gpath = agpath; 142 gpathp = gpath; 143 *gpathp = 0; 144 lastgpathp = &gpath[sizeof agpath - 2]; 145 ginit(agargv); 146 globcnt = 0; 147 collect(v); 148 if (globcnt == 0 && (gflag & 1)) { 149 blkfree(gargv), gargv = 0; 150 return (0); 151 } 152 else 153 return (gargv = copyblk(gargv)); 154 } 155 156 static void ginit(char **agargv) 157 { 158 159 agargv[0] = 0; 160 gargv = agargv; 161 sortbas = agargv; 162 gargc = 0; 163 gnleft = NCARGS - 4; 164 } 165 166 static void collect(register char *as) 167 { 168 if (eq(as, "{") || eq(as, "{}")) { 169 Gcat(as, ""); 170 sort(); 171 } 172 else 173 acollect(as); 174 } 175 176 static void acollect(register char *as) 177 { 178 register int ogargc = gargc; 179 180 gpathp = gpath; 181 *gpathp = 0; 182 globbed = 0; 183 expand(as); 184 if (gargc != ogargc) 185 sort(); 186 } 187 188 static int 189 argcmp(const void *p1, const void *p2) 190 { 191 char *s1 = *(char **) p1; 192 char *s2 = *(char **) p2; 193 194 return (strcmp(s1, s2)); 195 } 196 197 static void sort(void) 198 { 199 char **Gvp = &gargv[gargc]; 200 201 if (!globerr) 202 qsort(sortbas, Gvp - sortbas, sizeof (*sortbas), argcmp); 203 sortbas = Gvp; 204 } 205 206 static void expand(char *as) 207 { 208 register char *cs; 209 register char *sgpathp, *oldcs; 210 struct stat stb; 211 212 if (globerr) 213 return; 214 sgpathp = gpathp; 215 cs = as; 216 if (*cs == '~' && gpathp == gpath) { 217 addpath('~'); 218 for (cs++; letter(*cs) || digit(*cs) || *cs == '-';) 219 addpath(*cs++); 220 if (!*cs || *cs == '/') { 221 if (gpathp != gpath + 1) { 222 *gpathp = 0; 223 if (gethdir(gpath + 1)) 224 globerr = "Unknown user name after ~"; 225 /* memmove used as strings overlap */ 226 (void) memmove(gpath, gpath + 1, strlen(gpath + 1) + 1); 227 } 228 else 229 (void) strlcpy(gpath, home, BUFSIZ); 230 gpathp = strend(gpath); 231 } 232 } 233 while (!any(*cs, globchars)) { 234 if (*cs == 0) { 235 if (!globbed) 236 Gcat(gpath, ""); 237 else if (stat(gpath, &stb) >= 0) { 238 Gcat(gpath, ""); 239 globcnt++; 240 } 241 goto endit; 242 } 243 addpath(*cs++); 244 } 245 oldcs = cs; 246 while (cs > as && *cs != '/') 247 cs--, gpathp--; 248 if (*cs == '/') 249 cs++, gpathp++; 250 *gpathp = 0; 251 if (*oldcs == '{') { 252 (void) execbrc(cs, ((char *) 0)); 253 return; 254 } 255 matchdir(cs); 256 endit: 257 gpathp = sgpathp; 258 *gpathp = 0; 259 } 260 261 static void matchdir(char *pattern) 262 { 263 struct stat stb; 264 265 #ifdef HAVE_DIRENT_H 266 register struct dirent *dp; 267 #else 268 register struct direct *dp; 269 #endif 270 271 DIR *dirp; 272 273 dirp = opendir(*gpath == '\0' ? "." : gpath); 274 if (dirp == NULL) { 275 if (globbed) 276 return; 277 goto patherr2; 278 } 279 #ifdef HAVE_DIRFD 280 if (fstat(dirfd(dirp), &stb) < 0) 281 #else /* HAVE_DIRFD */ 282 if (fstat(dirp->dd_fd, &stb) < 0) 283 #endif /* HAVE_DIRFD */ 284 goto patherr1; 285 if (!isdir(stb)) { 286 errno = ENOTDIR; 287 goto patherr1; 288 } 289 while (!globerr && ((dp = readdir(dirp)) != NULL)) { 290 if (dp->d_ino == 0) 291 continue; 292 if (match(dp->d_name, pattern)) { 293 Gcat(gpath, dp->d_name); 294 globcnt++; 295 } 296 } 297 closedir(dirp); 298 return; 299 300 patherr1: 301 closedir(dirp); 302 patherr2: 303 globerr = "Bad directory components"; 304 } 305 306 static int execbrc(char *p, char *s) 307 { 308 char restbuf[BUFSIZ + 2]; 309 char *restbufend = &restbuf[sizeof(restbuf)]; 310 register char *pe, *pm, *pl; 311 int brclev = 0; 312 char *lm, savec, *sgpathp; 313 314 for (lm = restbuf; *p != '{'; *lm++ = *p++) { 315 if (lm >= restbufend) 316 return (0); 317 } 318 for (pe = ++p; *pe; pe++) { 319 switch (*pe) { 320 321 case '{': 322 brclev++; 323 continue; 324 325 case '}': 326 if (brclev == 0) 327 goto pend; 328 brclev--; 329 continue; 330 331 case '[': 332 for (pe++; *pe && *pe != ']'; pe++) 333 continue; 334 if (!*pe) { 335 globerr = "Missing ]"; 336 return (0); 337 } 338 continue; 339 } 340 } 341 pend: 342 if (brclev || !*pe) { 343 globerr = "Missing }"; 344 return (0); 345 } 346 for (pl = pm = p; pm <= pe; pm++) { 347 switch (*pm & (QUOTE | TRIM)) { 348 349 case '{': 350 brclev++; 351 continue; 352 353 case '}': 354 if (brclev) { 355 brclev--; 356 continue; 357 } 358 goto doit; 359 360 case ',' | QUOTE: 361 case ',': 362 if (brclev) 363 continue; 364 doit: 365 savec = *pm; 366 *pm = 0; 367 if (lm + strlen(pl) + strlen(pe + 1) >= restbufend) 368 return (0); 369 (void) strlcpy(lm, pl, restbufend - lm); 370 (void) strlcat(restbuf, pe + 1, sizeof(restbuf)); 371 *pm = savec; 372 if (s == 0) { 373 sgpathp = gpathp; 374 expand(restbuf); 375 gpathp = sgpathp; 376 *gpathp = 0; 377 } 378 else if (amatch(s, restbuf)) 379 return (1); 380 sort(); 381 pl = pm + 1; 382 continue; 383 384 case '[': 385 for (pm++; *pm && *pm != ']'; pm++) 386 continue; 387 if (!*pm) { 388 globerr = "Missing ]"; 389 return (0); 390 } 391 continue; 392 } 393 } 394 return (0); 395 } 396 397 static int match(char *s, char *p) 398 { 399 register int c; 400 register char *sentp; 401 char sglobbed = globbed; 402 403 if (*s == '.' && *p != '.') 404 return (0); 405 sentp = entp; 406 entp = s; 407 c = amatch(s, p); 408 entp = sentp; 409 globbed = sglobbed; 410 return (c); 411 } 412 413 static int amatch(char *s, char *p) 414 { 415 register int scc; 416 int ok, lc; 417 char *sgpathp; 418 struct stat stb; 419 int c, cc; 420 421 globbed = 1; 422 for (;;) { 423 scc = *s++ & TRIM; 424 switch (c = *p++) { 425 426 case '{': 427 return (execbrc(p - 1, s - 1)); 428 429 case '[': 430 ok = 0; 431 lc = 077777; 432 while ((cc = *p++)) { 433 if (cc == ']') { 434 if (ok) 435 break; 436 return (0); 437 } 438 if (cc == '-') { 439 if (lc <= scc && scc <= *p++) 440 ok++; 441 } 442 else if (scc == (lc = cc)) 443 ok++; 444 } 445 if (cc == 0) { 446 globerr = "Missing ]"; 447 return (0); 448 } 449 continue; 450 451 case '*': 452 if (!*p) 453 return (1); 454 if (*p == '/') { 455 p++; 456 goto slash; 457 } else if (*p == '*') { 458 s--; 459 continue; 460 } 461 s--; 462 do { 463 if (amatch(s, p)) 464 return (1); 465 } while (*s++); 466 return (0); 467 468 case 0: 469 return (scc == 0); 470 471 default: 472 if (c != scc) 473 return (0); 474 continue; 475 476 case '?': 477 if (scc == 0) 478 return (0); 479 continue; 480 481 case '/': 482 if (scc) 483 return (0); 484 slash: 485 s = entp; 486 sgpathp = gpathp; 487 while (*s) 488 addpath(*s++); 489 addpath('/'); 490 if (stat(gpath, &stb) == 0 && isdir(stb)) 491 if (*p == 0) { 492 Gcat(gpath, ""); 493 globcnt++; 494 } 495 else 496 expand(p); 497 gpathp = sgpathp; 498 *gpathp = 0; 499 return (0); 500 } 501 } 502 } 503 504 static void Gcat(register char *s1, register char *s2) 505 { 506 register size_t len = strlen(s1) + strlen(s2) + 1; 507 508 if (globerr) 509 return; 510 511 if ((len + sizeof (char *)) >= gnleft) { 512 globerr = "Arguments too long"; 513 return; 514 } 515 if (len > MAXPATHLEN) { 516 globerr = "Pathname too long"; 517 return; 518 } 519 if (gargc >= agargv_size - 1) { 520 char **tmp; 521 522 tmp = (char **)realloc(agargv, 523 (agargv_size + GAVSIZ) * sizeof (char *)); 524 if (tmp == NULL) { 525 fatal("Out of memory"); 526 } else { 527 agargv = tmp; 528 agargv_size += GAVSIZ; 529 } 530 gargv = agargv; 531 sortbas = agargv; 532 } 533 gargc++; 534 gnleft -= len + sizeof (char *); 535 gargv[gargc] = 0; 536 gargv[gargc - 1] = strspl(s1, s2); 537 } 538 539 static void addpath(char c) 540 { 541 542 if (gpathp >= lastgpathp) 543 globerr = "Pathname too long"; 544 else { 545 *gpathp++ = c; 546 *gpathp = 0; 547 } 548 } 549 550 static void rscan(register char **t, int (*f) (register char)) 551 { 552 register char *p, c; 553 554 while ((p = *t++)) { 555 if (*p == '~') 556 gflag |= 2; 557 else if (eq(p, "{") || eq(p, "{}")) 558 continue; 559 while ((c = *p++)) 560 (*f) (c); 561 } 562 } 563 static int tglob(register char c) 564 { 565 if (any(c, globchars)) 566 gflag |= c == '{' ? 2 : 1; 567 return (c); 568 } 569 570 int letter(register char c) 571 { 572 return (((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')) 573 || (c == '_')); 574 } 575 576 int digit(register char c) 577 { 578 return (c >= '0' && c <= '9'); 579 } 580 581 int any(register int c, register char *s) 582 { 583 while (*s) 584 if (*s++ == c) 585 return (1); 586 return (0); 587 } 588 589 int blklen(register char **av) 590 { 591 register int i = 0; 592 593 while (*av++) 594 i++; 595 return (i); 596 } 597 598 char **blkcpy(char **oav, register char **bv) 599 { 600 register char **av = oav; 601 602 while ((*av++ = *bv++)) 603 continue; 604 return (oav); 605 } 606 607 void blkfree(char **av0) 608 { 609 register char **av = av0; 610 611 if (av) { 612 while (*av) 613 free(*av++); 614 } 615 } 616 617 char *strspl(register char *cp, register char *dp) 618 { 619 int bufsize = strlen(cp) + strlen(dp) + 1; 620 char *ep = malloc(bufsize); 621 622 if (ep == NULL) 623 fatal("Out of memory"); 624 (void) strlcpy(ep, cp, bufsize); 625 (void) strlcat(ep, dp, bufsize); 626 return (ep); 627 } 628 629 char **copyblk(register char **v) 630 { 631 register char **nv = (char **) malloc((unsigned) ((blklen(v) + 1) * 632 sizeof(char **))); 633 if (nv == (char **) 0) 634 fatal("Out of memory"); 635 636 return (blkcpy(nv, v)); 637 } 638 639 static char *strend(register char *cp) 640 { 641 while (*cp) 642 cp++; 643 return (cp); 644 } 645 /* 646 * Extract a home directory from the password file 647 * The argument points to a buffer where the name of the 648 * user whose home directory is sought is currently. 649 * We write the home directory of the user back there. 650 */ 651 static int gethdir(char *home) 652 { 653 #ifdef OTHER_PASSWD 654 register struct passwd *pp = bero_getpwnam(home, _path_passwd); 655 #else 656 register struct passwd *pp = getpwnam(home); 657 #endif 658 register char *root = NULL; 659 if (!pp || home + strlen(pp->pw_dir) >= lastgpathp) 660 return (1); 661 root = strstr(pp->pw_dir, "/./"); 662 (void) strlcpy(home, root ? (root + 2) : pp->pw_dir, lastgpathp - home); 663 664 return (0); 665 } 666