1/* glob.c: The csh et al glob pattern matching routines. 2 3%%% copyright-cmetz-96 4This software is Copyright 1996-2001 by Craig Metz, All Rights Reserved. 5The Inner Net License Version 3 applies to this software. 6You should have received a copy of the license with this software. If 7you didn't get a copy, you may request one from <license@inner.net>. 8 9Portions of this software are Copyright 1995 by Randall Atkinson and Dan 10McDonald, All Rights Reserved. All Rights under this copyright are assigned 11to the U.S. Naval Research Laboratory (NRL). The NRL Copyright Notice and 12License Agreement applies to this software. 13 14 History: 15 16 Modified by cmetz for OPIE 2.32. Remove include of dirent.h here; it's 17 done already (and conditionally) in opie_cfg.h. 18 Modified by cmetz for OPIE 2.2. Use FUNCTION declaration et al. 19 Remove useless strings. Prototype right. 20 Modified at NRL for OPIE 2.0. 21 Originally from BSD. 22*/ 23/* 24 * Copyright (c) 1980 Regents of the University of California. 25 * All rights reserved. 26 * 27 * Redistribution and use in source and binary forms, with or without 28 * modification, are permitted provided that the following conditions 29 * are met: 30 * 1. Redistributions of source code must retain the above copyright 31 * notice, this list of conditions and the following disclaimer. 32 * 2. Redistributions in binary form must reproduce the above copyright 33 * notice, this list of conditions and the following disclaimer in the 34 * documentation and/or other materials provided with the distribution. 35 * 3. All advertising materials mentioning features or use of this software 36 * must display the following acknowledgement: 37 * This product includes software developed by the University of 38 * California, Berkeley and its contributors. 39 * 4. Neither the name of the University nor the names of its contributors 40 * may be used to endorse or promote products derived from this software 41 * without specific prior written permission. 42 * 43 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 44 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 45 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 46 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 47 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 48 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 49 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 50 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 51 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 52 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 53 * SUCH DAMAGE. 54 */ 55 56/* 57 * C-shell glob for random programs. 58 */ 59 60#include "opie_cfg.h" 61 62#if HAVE_SYS_PARAM_H 63#include <sys/param.h> 64#endif /* HAVE_SYS_PARAM_H */ 65#include <sys/stat.h> 66 67#if HAVE_PWD_H 68#include <pwd.h> 69#endif /* HAVE_PWD_H */ 70#include <errno.h> 71#include <stdio.h> 72#include <string.h> 73#if HAVE_LIMITS_H 74#include <limits.h> 75#endif /* HAVE_LIMITS_H */ 76 77#include "opie.h" 78 79#ifndef NCARGS 80#define NCARGS 600 81#endif /* NCARGS */ 82#define QUOTE 0200 83#define TRIM 0177 84#define eq(a,b) (strcmp((a),(b)) == (0)) 85#define GAVSIZ (NCARGS/6) 86#define isdir(d) (((d.st_mode) & S_IFMT) == S_IFDIR) 87 88static char **gargv; /* Pointer to the (stack) arglist */ 89static int gargc; /* Number args in gargv */ 90static int gnleft; 91static short gflag; 92 93static int letter __P((register char)); 94static int digit __P((register char)); 95static int any __P((int, char *)); 96static int blklen __P((register char **)); 97VOIDRET blkfree __P((char **)); 98static char *strspl __P((register char *, register char *)); 99 100static int tglob __P((register char c)); 101 102extern int errno; 103static char *strend __P((char *)); 104 105static int globcnt; 106 107static char *globchars = "`{[*?"; 108char *globerr = NULL; 109char *home = NULL; 110 111static char *gpath, *gpathp, *lastgpathp; 112static int globbed; 113static char *entp; 114static char **sortbas; 115 116static int amatch __P((char *p, char *s)); 117static int execbrc __P((register char *p, register char *s)); 118VOIDRET opiefatal __P((char *)); 119char **copyblk __P((char **)); 120 121static int match FUNCTION((s, p), char *s AND char *p) 122{ 123 register int c; 124 register char *sentp; 125 char sglobbed = globbed; 126 127 if (*s == '.' && *p != '.') 128 return (0); 129 sentp = entp; 130 entp = s; 131 c = amatch(s, p); 132 entp = sentp; 133 globbed = sglobbed; 134 return (c); 135} 136 137 138static int Gmatch FUNCTION((s, p), register char *s AND register char *p) 139{ 140 register int scc; 141 int ok, lc; 142 int c, cc; 143 144 for (;;) { 145 scc = *s++ & TRIM; 146 switch (c = *p++) { 147 148 case '[': 149 ok = 0; 150 lc = 077777; 151 while (cc = *p++) { 152 if (cc == ']') { 153 if (ok) 154 break; 155 return (0); 156 } 157 if (cc == '-') { 158 if (lc <= scc && scc <= *p++) 159 ok++; 160 } else 161 if (scc == (lc = cc)) 162 ok++; 163 } 164 if (cc == 0) 165 if (ok) 166 p--; 167 else 168 return 0; 169 continue; 170 171 case '*': 172 if (!*p) 173 return (1); 174 for (s--; *s; s++) 175 if (Gmatch(s, p)) 176 return (1); 177 return (0); 178 179 case 0: 180 return (scc == 0); 181 182 default: 183 if ((c & TRIM) != scc) 184 return (0); 185 continue; 186 187 case '?': 188 if (scc == 0) 189 return (0); 190 continue; 191 192 } 193 } 194} 195 196static VOIDRET Gcat FUNCTION((s1, s2), register char *s1 AND register char *s2) 197{ 198 register int len = strlen(s1) + strlen(s2) + 1; 199 200 if (len >= gnleft || gargc >= GAVSIZ - 1) 201 globerr = "Arguments too long"; 202 else { 203 gargc++; 204 gnleft -= len; 205 gargv[gargc] = 0; 206 gargv[gargc - 1] = strspl(s1, s2); 207 } 208} 209 210static VOIDRET addpath FUNCTION((c), char c) 211{ 212 213 if (gpathp >= lastgpathp) 214 globerr = "Pathname too long"; 215 else { 216 *gpathp++ = c; 217 *gpathp = 0; 218 } 219} 220 221static VOIDRET rscan FUNCTION((t, f), register char **t AND int (*f)__P((char))) 222{ 223 register char *p, c; 224 225 while (p = *t++) { 226 if (f == tglob) 227 if (*p == '~') 228 gflag |= 2; 229 else 230 if (eq(p, "{") || eq(p, "{}")) 231 continue; 232 while (c = *p++) 233 (*f) (c); 234 } 235} 236 237static int tglob FUNCTION((c), register char c) 238{ 239 if (any(c, globchars)) 240 gflag |= c == '{' ? 2 : 1; 241 return (c); 242} 243 244static int letter FUNCTION((c), register char c) 245{ 246 return (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c == '_'); 247} 248 249static int digit FUNCTION((c), register char c) 250{ 251 return (c >= '0' && c <= '9'); 252} 253 254static int any FUNCTION((c, s), int c AND char *s) 255{ 256 while (*s) 257 if (*s++ == c) 258 return (1); 259 return (0); 260} 261 262static int blklen FUNCTION((av), register char **av) 263{ 264 register int i = 0; 265 266 while (*av++) 267 i++; 268 return (i); 269} 270 271static char **blkcpy FUNCTION((oav, bv), char **oav AND register char **bv) 272{ 273 register char **av = oav; 274 275 while (*av++ = *bv++) 276 continue; 277 return (oav); 278} 279 280VOIDRET blkfree FUNCTION((av0), char **av0) 281{ 282 register char **av = av0; 283 284 while (*av) 285 free(*av++); 286} 287 288static char *strspl FUNCTION((cp, dp), register char *cp AND register char *dp) 289{ 290 register char *ep = (char *) malloc((unsigned) (strlen(cp) + 291 strlen(dp) + 1)); 292 293 if (ep == (char *) 0) 294 opiefatal("Out of memory"); 295 strcpy(ep, cp); 296 strcat(ep, dp); 297 return (ep); 298} 299 300char **copyblk FUNCTION((v), char **v) 301{ 302 register char **nv = (char **) malloc((unsigned) ((blklen(v) + 1) * 303 sizeof(char **))); 304 305 if (nv == (char **) 0) 306 opiefatal("Out of memory"); 307 308 return (blkcpy(nv, v)); 309} 310 311static char *strend FUNCTION((cp), register char *cp) 312{ 313 314 while (*cp) 315 cp++; 316 return (cp); 317} 318 319/* 320 * Extract a home directory from the password file 321 * The argument points to a buffer where the name of the 322 * user whose home directory is sought is currently. 323 * We write the home directory of the user back there. 324 */ 325static int gethdir FUNCTION((home), char *home) 326{ 327 register struct passwd *pp = getpwnam(home); 328 329 if (!pp || home + strlen(pp->pw_dir) >= lastgpathp) 330 return (1); 331 strcpy(home, pp->pw_dir); 332 return (0); 333} 334 335static VOIDRET ginit FUNCTION((agargv), char **agargv) 336{ 337 agargv[0] = 0; 338 gargv = agargv; 339 sortbas = agargv; 340 gargc = 0; 341 gnleft = NCARGS - 4; 342} 343 344static VOIDRET sort FUNCTION_NOARGS 345{ 346 register char **p1, **p2, *c; 347 char **Gvp = &gargv[gargc]; 348 349 p1 = sortbas; 350 while (p1 < Gvp - 1) { 351 p2 = p1; 352 while (++p2 < Gvp) 353 if (strcmp(*p1, *p2) > 0) 354 c = *p1, *p1 = *p2, *p2 = c; 355 p1++; 356 } 357 sortbas = Gvp; 358} 359 360static VOIDRET matchdir FUNCTION((pattern), char *pattern) 361{ 362 struct stat stb; 363 364 register struct dirent *dp; 365 366 DIR *dirp; 367 368 dirp = opendir(*gpath == '\0' ? "." : gpath); 369 if (dirp == NULL) { 370 if (globbed) 371 return; 372 goto patherr2; 373 } 374#if !defined(linux) 375 if (fstat(dirp->dd_fd, &stb) < 0) 376 goto patherr1; 377 if (!isdir(stb)) { 378 errno = ENOTDIR; 379 goto patherr1; 380 } 381#endif /* !defined(linux) */ 382 while ((dp = readdir(dirp)) != NULL) { 383 if (dp->d_ino == 0) 384 continue; 385 if (match(dp->d_name, pattern)) { 386 Gcat(gpath, dp->d_name); 387 globcnt++; 388 } 389 } 390 closedir(dirp); 391 return; 392 393patherr1: 394 closedir(dirp); 395patherr2: 396 globerr = "Bad directory components"; 397} 398 399static VOIDRET expand FUNCTION((as), char *as) 400{ 401 register char *cs; 402 register char *sgpathp, *oldcs; 403 struct stat stb; 404 405 sgpathp = gpathp; 406 cs = as; 407 if (*cs == '~' && gpathp == gpath) { 408 addpath('~'); 409 for (cs++; letter(*cs) || digit(*cs) || *cs == '-';) 410 addpath(*cs++); 411 if (!*cs || *cs == '/') { 412 if (gpathp != gpath + 1) { 413 *gpathp = 0; 414 if (gethdir(gpath + 1)) 415 globerr = "Unknown user name after ~"; 416 strcpy(gpath, gpath + 1); 417 } else 418 strcpy(gpath, home); 419 gpathp = strend(gpath); 420 } 421 } 422 while (!any(*cs, globchars)) { 423 if (*cs == 0) { 424 if (!globbed) 425 Gcat(gpath, ""); 426 else 427 if (stat(gpath, &stb) >= 0) { 428 Gcat(gpath, ""); 429 globcnt++; 430 } 431 goto endit; 432 } 433 addpath(*cs++); 434 } 435 oldcs = cs; 436 while (cs > as && *cs != '/') 437 cs--, gpathp--; 438 if (*cs == '/') 439 cs++, gpathp++; 440 *gpathp = 0; 441 if (*oldcs == '{') { 442 execbrc(cs, ((char *) 0)); 443 return; 444 } 445 matchdir(cs); 446endit: 447 gpathp = sgpathp; 448 *gpathp = 0; 449} 450 451static int execbrc FUNCTION((p, s), char *p AND char *s) 452{ 453 char restbuf[BUFSIZ + 2]; 454 register char *pe, *pm, *pl; 455 int brclev = 0; 456 char *lm, savec, *sgpathp; 457 458 for (lm = restbuf; *p != '{'; *lm++ = *p++) 459 continue; 460 for (pe = ++p; *pe; pe++) 461 switch (*pe) { 462 463 case '{': 464 brclev++; 465 continue; 466 467 case '}': 468 if (brclev == 0) 469 goto pend; 470 brclev--; 471 continue; 472 473 case '[': 474 for (pe++; *pe && *pe != ']'; pe++) 475 continue; 476 continue; 477 } 478pend: 479 brclev = 0; 480 for (pl = pm = p; pm <= pe; pm++) 481 switch (*pm & (QUOTE | TRIM)) { 482 483 case '{': 484 brclev++; 485 continue; 486 487 case '}': 488 if (brclev) { 489 brclev--; 490 continue; 491 } 492 goto doit; 493 494 case ',' | QUOTE: 495 case ',': 496 if (brclev) 497 continue; 498 doit: 499 savec = *pm; 500 *pm = 0; 501 strcpy(lm, pl); 502 strcat(restbuf, pe + 1); 503 *pm = savec; 504 if (s == 0) { 505 sgpathp = gpathp; 506 expand(restbuf); 507 gpathp = sgpathp; 508 *gpathp = 0; 509 } else 510 if (amatch(s, restbuf)) 511 return (1); 512 sort(); 513 pl = pm + 1; 514 if (brclev) 515 return (0); 516 continue; 517 518 case '[': 519 for (pm++; *pm && *pm != ']'; pm++) 520 continue; 521 if (!*pm) 522 pm--; 523 continue; 524 } 525 if (brclev) 526 goto doit; 527 return (0); 528} 529 530static VOIDRET acollect FUNCTION((as), register char *as) 531{ 532 register int ogargc = gargc; 533 534 gpathp = gpath; 535 *gpathp = 0; 536 globbed = 0; 537 expand(as); 538 if (gargc != ogargc) 539 sort(); 540} 541 542static VOIDRET collect FUNCTION((as), register char *as) 543{ 544 if (eq(as, "{") || eq(as, "{}")) { 545 Gcat(as, ""); 546 sort(); 547 } else 548 acollect(as); 549} 550 551static int amatch FUNCTION((s, p), register char *s AND register char *p) 552{ 553 register int scc; 554 int ok, lc; 555 char *sgpathp; 556 struct stat stb; 557 int c, cc; 558 559 globbed = 1; 560 for (;;) { 561 scc = *s++ & TRIM; 562 switch (c = *p++) { 563 564 case '{': 565 return (execbrc(p - 1, s - 1)); 566 567 case '[': 568 ok = 0; 569 lc = 077777; 570 while (cc = *p++) { 571 if (cc == ']') { 572 if (ok) 573 break; 574 return (0); 575 } 576 if (cc == '-') { 577 if (lc <= scc && scc <= *p++) 578 ok++; 579 } else 580 if (scc == (lc = cc)) 581 ok++; 582 } 583 if (cc == 0) 584 if (ok) 585 p--; 586 else 587 return 0; 588 continue; 589 590 case '*': 591 if (!*p) 592 return (1); 593 if (*p == '/') { 594 p++; 595 goto slash; 596 } 597 s--; 598 do { 599 if (amatch(s, p)) 600 return (1); 601 } 602 while (*s++); 603 return (0); 604 605 case 0: 606 return (scc == 0); 607 608 default: 609 if (c != scc) 610 return (0); 611 continue; 612 613 case '?': 614 if (scc == 0) 615 return (0); 616 continue; 617 618 case '/': 619 if (scc) 620 return (0); 621 slash: 622 s = entp; 623 sgpathp = gpathp; 624 while (*s) 625 addpath(*s++); 626 addpath('/'); 627 if (stat(gpath, &stb) == 0 && isdir(stb)) 628 if (*p == 0) { 629 Gcat(gpath, ""); 630 globcnt++; 631 } else 632 expand(p); 633 gpathp = sgpathp; 634 *gpathp = 0; 635 return (0); 636 } 637 } 638} 639 640 641char **ftpglob FUNCTION((v), register char *v) 642{ 643 char agpath[BUFSIZ]; 644 char *agargv[GAVSIZ]; 645 char *vv[2]; 646 647 vv[0] = v; 648 vv[1] = 0; 649 gflag = 0; 650 rscan(vv, tglob); 651 if (gflag == 0) { 652 vv[0] = strspl(v, ""); 653 return (copyblk(vv)); 654 } 655 globerr = 0; 656 gpath = agpath; 657 gpathp = gpath; 658 *gpathp = 0; 659 lastgpathp = &gpath[sizeof agpath - 2]; 660 ginit(agargv); 661 globcnt = 0; 662 collect(v); 663 if (globcnt == 0 && (gflag & 1)) { 664 blkfree(gargv), gargv = 0; 665 return (0); 666 } else 667 return (gargv = copyblk(gargv)); 668} 669