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