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