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