1: /* 2: * Copyright (c) 1983 Regents of the University of California. 3: * All rights reserved. The Berkeley software License Agreement 4: * specifies the terms and conditions for redistribution. 5: */ 6: 7: #ifndef lint 8: static char sccsid[] = "@(#)expand.c 5.2 (Berkeley) 3/28/86"; 9: #endif not lint 10: 11: #include "defs.h" 12: 13: #define GAVSIZ NCARGS / 6 14: #define LC '{' 15: #define RC '}' 16: 17: static char shchars[] = "${[*?"; 18: 19: int which; /* bit mask of types to expand */ 20: int eargc; /* expanded arg count */ 21: char **eargv; /* expanded arg vectors */ 22: char *path; 23: char *pathp; 24: char *lastpathp; 25: char *tilde; /* "~user" if not expanding tilde, else "" */ 26: char *tpathp; 27: int nleft; 28: 29: int expany; /* any expansions done? */ 30: char *entp; 31: char **sortbase; 32: 33: char *index(); 34: int argcmp(); 35: 36: #define sort() qsort((char *)sortbase, &eargv[eargc] - sortbase, \ 37: sizeof(*sortbase), argcmp), sortbase = &eargv[eargc] 38: 39: /* 40: * Take a list of names and expand any macros, etc. 41: * wh = E_VARS if expanding variables. 42: * wh = E_SHELL if expanding shell characters. 43: * wh = E_TILDE if expanding `~'. 44: * or any of these or'ed together. 45: * 46: * Major portions of this were snarfed from csh/sh.glob.c. 47: */ 48: struct namelist * 49: expand(list, wh) 50: struct namelist *list; 51: int wh; 52: { 53: register struct namelist *nl, *prev; 54: register int n; 55: char pathbuf[BUFSIZ]; 56: char *argvbuf[GAVSIZ]; 57: 58: if (debug) { 59: printf("expand(%x, %d)\nlist = ", list, wh); 60: prnames(list); 61: } 62: 63: if (wh == 0) { 64: register char *cp; 65: 66: for (nl = list; nl != NULL; nl = nl->n_next) 67: for (cp = nl->n_name; *cp; cp++) 68: *cp = *cp & TRIM; 69: return(list); 70: } 71: 72: which = wh; 73: path = tpathp = pathp = pathbuf; 74: *pathp = '\0'; 75: lastpathp = &path[sizeof pathbuf - 2]; 76: tilde = ""; 77: eargc = 0; 78: eargv = sortbase = argvbuf; 79: *eargv = 0; 80: nleft = NCARGS - 4; 81: /* 82: * Walk the name list and expand names into eargv[]; 83: */ 84: for (nl = list; nl != NULL; nl = nl->n_next) 85: expstr(nl->n_name); 86: /* 87: * Take expanded list of names from eargv[] and build a new list. 88: */ 89: list = prev = NULL; 90: for (n = 0; n < eargc; n++) { 91: nl = makenl(NULL); 92: nl->n_name = eargv[n]; 93: if (prev == NULL) 94: list = prev = nl; 95: else { 96: prev->n_next = nl; 97: prev = nl; 98: } 99: } 100: if (debug) { 101: printf("expanded list = "); 102: prnames(list); 103: } 104: return(list); 105: } 106: 107: expstr(s) 108: char *s; 109: { 110: register char *cp, *cp1; 111: register struct namelist *tp; 112: char *tail; 113: char buf[BUFSIZ]; 114: int savec, oeargc; 115: extern char homedir[]; 116: 117: if (s == NULL || *s == '\0') 118: return; 119: 120: if ((which & E_VARS) && (cp = index(s, '$')) != NULL) { 121: *cp++ = '\0'; 122: if (*cp == '\0') { 123: yyerror("no variable name after '$'"); 124: return; 125: } 126: if (*cp == LC) { 127: cp++; 128: if ((tail = index(cp, RC)) == NULL) { 129: yyerror("unmatched '{'"); 130: return; 131: } 132: *tail++ = savec = '\0'; 133: if (*cp == '\0') { 134: yyerror("no variable name after '$'"); 135: return; 136: } 137: } else { 138: tail = cp + 1; 139: savec = *tail; 140: *tail = '\0'; 141: } 142: tp = lookup(cp, NULL, 0); 143: if (savec != '\0') 144: *tail = savec; 145: if (tp != NULL) { 146: for (; tp != NULL; tp = tp->n_next) { 147: sprintf(buf, "%s%s%s", s, tp->n_name, tail); 148: expstr(buf); 149: } 150: return; 151: } 152: sprintf(buf, "%s%s", s, tail); 153: expstr(buf); 154: return; 155: } 156: if ((which & ~E_VARS) == 0 || !strcmp(s, "{") || !strcmp(s, "{}")) { 157: Cat(s, ""); 158: sort(); 159: return; 160: } 161: if (*s == '~') { 162: cp = ++s; 163: if (*cp == '\0' || *cp == '/') { 164: tilde = "~"; 165: cp1 = homedir; 166: } else { 167: tilde = cp1 = buf; 168: *cp1++ = '~'; 169: do 170: *cp1++ = *cp++; 171: while (*cp && *cp != '/'); 172: *cp1 = '\0'; 173: if (pw == NULL || strcmp(pw->pw_name, buf+1) != 0) { 174: if ((pw = getpwnam(buf+1)) == NULL) { 175: strcat(buf, ": unknown user name"); 176: yyerror(buf+1); 177: return; 178: } 179: } 180: cp1 = pw->pw_dir; 181: s = cp; 182: } 183: for (cp = path; *cp++ = *cp1++; ) 184: ; 185: tpathp = pathp = cp - 1; 186: } else { 187: tpathp = pathp = path; 188: tilde = ""; 189: } 190: *pathp = '\0'; 191: if (!(which & E_SHELL)) { 192: if (which & E_TILDE) 193: Cat(path, s); 194: else 195: Cat(tilde, s); 196: sort(); 197: return; 198: } 199: oeargc = eargc; 200: expany = 0; 201: expsh(s); 202: if (eargc == oeargc) 203: Cat(s, ""); /* "nonomatch" is set */ 204: sort(); 205: } 206: 207: static 208: argcmp(a1, a2) 209: char **a1, **a2; 210: { 211: 212: return (strcmp(*a1, *a2)); 213: } 214: 215: /* 216: * If there are any Shell meta characters in the name, 217: * expand into a list, after searching directory 218: */ 219: expsh(s) 220: char *s; 221: { 222: register char *cp; 223: register char *spathp, *oldcp; 224: struct stat stb; 225: 226: spathp = pathp; 227: cp = s; 228: while (!any(*cp, shchars)) { 229: if (*cp == '\0') { 230: if (!expany || stat(path, &stb) >= 0) { 231: if (which & E_TILDE) 232: Cat(path, ""); 233: else 234: Cat(tilde, tpathp); 235: } 236: goto endit; 237: } 238: addpath(*cp++); 239: } 240: oldcp = cp; 241: while (cp > s && *cp != '/') 242: cp--, pathp--; 243: if (*cp == '/') 244: cp++, pathp++; 245: *pathp = '\0'; 246: if (*oldcp == '{') { 247: execbrc(cp, NULL); 248: return; 249: } 250: matchdir(cp); 251: endit: 252: pathp = spathp; 253: *pathp = '\0'; 254: } 255: 256: matchdir(pattern) 257: char *pattern; 258: { 259: struct stat stb; 260: register struct direct *dp; 261: DIR *dirp; 262: 263: dirp = opendir(path); 264: if (dirp == NULL) { 265: if (expany) 266: return; 267: goto patherr2; 268: } 269: if (fstat(dirp->dd_fd, &stb) < 0) 270: goto patherr1; 271: if (!ISDIR(stb.st_mode)) { 272: errno = ENOTDIR; 273: goto patherr1; 274: } 275: while ((dp = readdir(dirp)) != NULL) 276: if (match(dp->d_name, pattern)) { 277: if (which & E_TILDE) 278: Cat(path, dp->d_name); 279: else { 280: strcpy(pathp, dp->d_name); 281: Cat(tilde, tpathp); 282: *pathp = '\0'; 283: } 284: } 285: closedir(dirp); 286: return; 287: 288: patherr1: 289: closedir(dirp); 290: patherr2: 291: strcat(path, ": "); 292: strcat(path, sys_errlist[errno]); 293: yyerror(path); 294: } 295: 296: execbrc(p, s) 297: char *p, *s; 298: { 299: char restbuf[BUFSIZ + 2]; 300: register char *pe, *pm, *pl; 301: int brclev = 0; 302: char *lm, savec, *spathp; 303: 304: for (lm = restbuf; *p != '{'; *lm++ = *p++) 305: continue; 306: for (pe = ++p; *pe; pe++) 307: switch (*pe) { 308: 309: case '{': 310: brclev++; 311: continue; 312: 313: case '}': 314: if (brclev == 0) 315: goto pend; 316: brclev--; 317: continue; 318: 319: case '[': 320: for (pe++; *pe && *pe != ']'; pe++) 321: continue; 322: if (!*pe) 323: yyerror("Missing ']'"); 324: continue; 325: } 326: pend: 327: if (brclev || !*pe) { 328: yyerror("Missing '}'"); 329: return (0); 330: } 331: for (pl = pm = p; pm <= pe; pm++) 332: switch (*pm & (QUOTE|TRIM)) { 333: 334: case '{': 335: brclev++; 336: continue; 337: 338: case '}': 339: if (brclev) { 340: brclev--; 341: continue; 342: } 343: goto doit; 344: 345: case ',': 346: if (brclev) 347: continue; 348: doit: 349: savec = *pm; 350: *pm = 0; 351: strcpy(lm, pl); 352: strcat(restbuf, pe + 1); 353: *pm = savec; 354: if (s == 0) { 355: spathp = pathp; 356: expsh(restbuf); 357: pathp = spathp; 358: *pathp = 0; 359: } else if (amatch(s, restbuf)) 360: return (1); 361: sort(); 362: pl = pm + 1; 363: continue; 364: 365: case '[': 366: for (pm++; *pm && *pm != ']'; pm++) 367: continue; 368: if (!*pm) 369: yyerror("Missing ']'"); 370: continue; 371: } 372: return (0); 373: } 374: 375: match(s, p) 376: char *s, *p; 377: { 378: register int c; 379: register char *sentp; 380: char sexpany = expany; 381: 382: if (*s == '.' && *p != '.') 383: return (0); 384: sentp = entp; 385: entp = s; 386: c = amatch(s, p); 387: entp = sentp; 388: expany = sexpany; 389: return (c); 390: } 391: 392: amatch(s, p) 393: register char *s, *p; 394: { 395: register int scc; 396: int ok, lc; 397: char *spathp; 398: struct stat stb; 399: int c, cc; 400: 401: expany = 1; 402: for (;;) { 403: scc = *s++ & TRIM; 404: switch (c = *p++) { 405: 406: case '{': 407: return (execbrc(p - 1, s - 1)); 408: 409: case '[': 410: ok = 0; 411: lc = 077777; 412: while (cc = *p++) { 413: if (cc == ']') { 414: if (ok) 415: break; 416: return (0); 417: } 418: if (cc == '-') { 419: if (lc <= scc && scc <= *p++) 420: ok++; 421: } else 422: if (scc == (lc = cc)) 423: ok++; 424: } 425: if (cc == 0) { 426: yyerror("Missing ']'"); 427: return (0); 428: } 429: continue; 430: 431: case '*': 432: if (!*p) 433: return (1); 434: if (*p == '/') { 435: p++; 436: goto slash; 437: } 438: for (s--; *s; s++) 439: if (amatch(s, p)) 440: return (1); 441: return (0); 442: 443: case '\0': 444: return (scc == '\0'); 445: 446: default: 447: if ((c & TRIM) != scc) 448: return (0); 449: continue; 450: 451: case '?': 452: if (scc == '\0') 453: return (0); 454: continue; 455: 456: case '/': 457: if (scc) 458: return (0); 459: slash: 460: s = entp; 461: spathp = pathp; 462: while (*s) 463: addpath(*s++); 464: addpath('/'); 465: if (stat(path, &stb) == 0 && ISDIR(stb.st_mode)) 466: if (*p == '\0') { 467: if (which & E_TILDE) 468: Cat(path, ""); 469: else 470: Cat(tilde, tpathp); 471: } else 472: expsh(p); 473: pathp = spathp; 474: *pathp = '\0'; 475: return (0); 476: } 477: } 478: } 479: 480: smatch(s, p) 481: register char *s, *p; 482: { 483: register int scc; 484: int ok, lc; 485: int c, cc; 486: 487: for (;;) { 488: scc = *s++ & TRIM; 489: switch (c = *p++) { 490: 491: case '[': 492: ok = 0; 493: lc = 077777; 494: while (cc = *p++) { 495: if (cc == ']') { 496: if (ok) 497: break; 498: return (0); 499: } 500: if (cc == '-') { 501: if (lc <= scc && scc <= *p++) 502: ok++; 503: } else 504: if (scc == (lc = cc)) 505: ok++; 506: } 507: if (cc == 0) { 508: yyerror("Missing ']'"); 509: return (0); 510: } 511: continue; 512: 513: case '*': 514: if (!*p) 515: return (1); 516: for (s--; *s; s++) 517: if (smatch(s, p)) 518: return (1); 519: return (0); 520: 521: case '\0': 522: return (scc == '\0'); 523: 524: default: 525: if ((c & TRIM) != scc) 526: return (0); 527: continue; 528: 529: case '?': 530: if (scc == 0) 531: return (0); 532: continue; 533: 534: } 535: } 536: } 537: 538: Cat(s1, s2) 539: register char *s1, *s2; 540: { 541: int len = strlen(s1) + strlen(s2) + 1; 542: register char *s; 543: 544: nleft -= len; 545: if (nleft <= 0 || ++eargc >= GAVSIZ) 546: yyerror("Arguments too long"); 547: eargv[eargc] = 0; 548: eargv[eargc - 1] = s = malloc(len); 549: if (s == NULL) 550: fatal("ran out of memory\n"); 551: while (*s++ = *s1++ & TRIM) 552: ; 553: s--; 554: while (*s++ = *s2++ & TRIM) 555: ; 556: } 557: 558: addpath(c) 559: char c; 560: { 561: 562: if (pathp >= lastpathp) 563: yyerror("Pathname too long"); 564: else { 565: *pathp++ = c & TRIM; 566: *pathp = '\0'; 567: } 568: } 569: 570: /* 571: * Expand file names beginning with `~' into the 572: * user's home directory path name. Return a pointer in buf to the 573: * part corresponding to `file'. 574: */ 575: char * 576: exptilde(buf, file) 577: char buf[]; 578: register char *file; 579: { 580: register char *s1, *s2, *s3; 581: extern char homedir[]; 582: 583: if (*file != '~') { 584: strcpy(buf, file); 585: return(buf); 586: } 587: if (*++file == '\0') { 588: s2 = homedir; 589: s3 = NULL; 590: } else if (*file == '/') { 591: s2 = homedir; 592: s3 = file; 593: } else { 594: s3 = file; 595: while (*s3 && *s3 != '/') 596: s3++; 597: if (*s3 == '/') 598: *s3 = '\0'; 599: else 600: s3 = NULL; 601: if (pw == NULL || strcmp(pw->pw_name, file) != 0) { 602: if ((pw = getpwnam(file)) == NULL) { 603: error("%s: unknown user name\n", file); 604: if (s3 != NULL) 605: *s3 = '/'; 606: return(NULL); 607: } 608: } 609: if (s3 != NULL) 610: *s3 = '/'; 611: s2 = pw->pw_dir; 612: } 613: for (s1 = buf; *s1++ = *s2++; ) 614: ; 615: s2 = --s1; 616: if (s3 != NULL) { 617: s2++; 618: while (*s1++ = *s3++) 619: ; 620: } 621: return(s2); 622: }