1 /* $NetBSD: expand.c,v 1.16 2003/08/07 11:15:35 agc 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. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 #ifndef lint 34 #if 0 35 static char sccsid[] = "@(#)expand.c 8.1 (Berkeley) 6/9/93"; 36 #else 37 __RCSID("$NetBSD: expand.c,v 1.16 2003/08/07 11:15:35 agc Exp $"); 38 #endif 39 #endif /* not lint */ 40 41 #include <sys/types.h> 42 43 #include <errno.h> 44 #include <pwd.h> 45 46 #include "defs.h" 47 48 #define GAVSIZ NCARGS / 6 49 #define LC '{' 50 #define RC '}' 51 52 static char shchars[] = "${[*?"; 53 54 int which; /* bit mask of types to expand */ 55 int eargc; /* expanded arg count */ 56 char **eargv; /* expanded arg vectors */ 57 char *path; 58 char *pathp; 59 char *lastpathp; 60 char *tilde; /* "~user" if not expanding tilde, else "" */ 61 char *tpathp; 62 int nleft; 63 64 int expany; /* any expansions done? */ 65 char *entp; 66 char **sortbase; 67 68 #define sort() qsort((char *)sortbase, &eargv[eargc] - sortbase, \ 69 sizeof(*sortbase), argcmp), sortbase = &eargv[eargc] 70 71 static void Cat(char *, char *); 72 static void addpath(int); 73 static int amatch(char *, char *); 74 static int argcmp(const void *, const void *); 75 static int execbrc(char *, char *); 76 static void expsh(char *); 77 static void expstr(char *); 78 static int match(char *, char *); 79 static void matchdir(char *); 80 static int smatch(char *, char *); 81 82 /* 83 * Take a list of names and expand any macros, etc. 84 * wh = E_VARS if expanding variables. 85 * wh = E_SHELL if expanding shell characters. 86 * wh = E_TILDE if expanding `~'. 87 * or any of these or'ed together. 88 * 89 * Major portions of this were snarfed from csh/sh.glob.c. 90 */ 91 struct namelist * 92 expand(struct namelist *list, int wh) 93 { 94 struct namelist *nl, *prev; 95 int n; 96 char pathbuf[BUFSIZ]; 97 char *argvbuf[GAVSIZ]; 98 99 if (debug) { 100 printf("expand(%lx, %d)\nlist = ", (long)list, wh); 101 prnames(list); 102 } 103 104 if (wh == 0) { 105 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(char *s) 150 { 151 char *cp, *cp1; 152 struct namelist *tp; 153 char *tail; 154 char buf[BUFSIZ]; 155 int savec, oeargc; 156 extern char homedir[]; 157 158 if (s == NULL || *s == '\0') 159 return; 160 161 if ((which & E_VARS) && (cp = strchr(s, '$')) != NULL) { 162 *cp++ = '\0'; 163 if (*cp == '\0') { 164 yyerror("no variable name after '$'"); 165 return; 166 } 167 if (*cp == LC) { 168 cp++; 169 if ((tail = strchr(cp, RC)) == NULL) { 170 yyerror("unmatched '{'"); 171 return; 172 } 173 *tail++ = savec = '\0'; 174 if (*cp == '\0') { 175 yyerror("no variable name after '$'"); 176 return; 177 } 178 } else { 179 tail = cp + 1; 180 savec = *tail; 181 *tail = '\0'; 182 } 183 tp = lookup(cp, 0, 0); 184 if (savec != '\0') 185 *tail = savec; 186 if (tp != NULL) { 187 for (; tp != NULL; tp = tp->n_next) { 188 snprintf(buf, sizeof(buf), "%s%s%s", s, 189 tp->n_name, tail); 190 expstr(buf); 191 } 192 return; 193 } 194 snprintf(buf, sizeof(buf), "%s%s", s, tail); 195 expstr(buf); 196 return; 197 } 198 if ((which & ~E_VARS) == 0 || !strcmp(s, "{") || !strcmp(s, "{}")) { 199 Cat(s, ""); 200 sort(); 201 return; 202 } 203 if (*s == '~') { 204 cp = ++s; 205 if (*cp == '\0' || *cp == '/') { 206 tilde = "~"; 207 cp1 = homedir; 208 } else { 209 tilde = cp1 = buf; 210 *cp1++ = '~'; 211 do 212 *cp1++ = *cp++; 213 while (*cp && *cp != '/'); 214 *cp1 = '\0'; 215 if (pw == NULL || strcmp(pw->pw_name, buf+1) != 0) { 216 if ((pw = getpwnam(buf+1)) == NULL) { 217 strlcat(buf, ": unknown user name", 218 sizeof(buf)); 219 yyerror(buf+1); 220 return; 221 } 222 } 223 cp1 = pw->pw_dir; 224 s = cp; 225 } 226 for (cp = path; (*cp++ = *cp1++) != 0; ) 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(const void *a1, const void *a2) 252 { 253 254 return (strcmp(*(char **)a1, *(char **)a2)); 255 } 256 257 /* 258 * If there are any Shell meta characters in the name, 259 * expand into a list, after searching directory 260 */ 261 static void 262 expsh(char *s) 263 { 264 char *cp; 265 char *spathp, *oldcp; 266 struct stat stb; 267 268 spathp = pathp; 269 cp = s; 270 while (!any(*cp, shchars)) { 271 if (*cp == '\0') { 272 if (!expany || stat(path, &stb) >= 0) { 273 if (which & E_TILDE) 274 Cat(path, ""); 275 else 276 Cat(tilde, tpathp); 277 } 278 goto endit; 279 } 280 addpath(*cp++); 281 } 282 oldcp = cp; 283 while (cp > s && *cp != '/') 284 cp--, pathp--; 285 if (*cp == '/') 286 cp++, pathp++; 287 *pathp = '\0'; 288 if (*oldcp == '{') { 289 execbrc(cp, NULL); 290 return; 291 } 292 matchdir(cp); 293 endit: 294 pathp = spathp; 295 *pathp = '\0'; 296 } 297 298 static void 299 matchdir(char *pattern) 300 { 301 struct stat stb; 302 struct dirent *dp; 303 DIR *dirp; 304 305 dirp = opendir(path); 306 if (dirp == NULL) { 307 if (expany) 308 return; 309 goto patherr2; 310 } 311 if (fstat(dirp->dd_fd, &stb) < 0) 312 goto patherr1; 313 if (!S_ISDIR(stb.st_mode)) { 314 errno = ENOTDIR; 315 goto patherr1; 316 } 317 while ((dp = readdir(dirp)) != NULL) 318 if (match(dp->d_name, pattern)) { 319 if (which & E_TILDE) 320 Cat(path, dp->d_name); 321 else { 322 strcpy(pathp, dp->d_name); 323 Cat(tilde, tpathp); 324 *pathp = '\0'; 325 } 326 } 327 closedir(dirp); 328 return; 329 330 patherr1: 331 closedir(dirp); 332 patherr2: 333 strcat(path, ": "); 334 strcat(path, strerror(errno)); 335 yyerror(path); 336 } 337 338 static int 339 execbrc(char *p, char *s) 340 { 341 char restbuf[BUFSIZ + 2]; 342 char *pe, *pm, *pl; 343 int brclev = 0; 344 char *lm, savec, *spathp; 345 346 for (lm = restbuf; *p != '{'; *lm++ = *p++) 347 continue; 348 for (pe = ++p; *pe; pe++) 349 switch (*pe) { 350 351 case '{': 352 brclev++; 353 continue; 354 355 case '}': 356 if (brclev == 0) 357 goto pend; 358 brclev--; 359 continue; 360 361 case '[': 362 for (pe++; *pe && *pe != ']'; pe++) 363 continue; 364 if (!*pe) 365 yyerror("Missing ']'"); 366 continue; 367 } 368 pend: 369 if (brclev || !*pe) { 370 yyerror("Missing '}'"); 371 return (0); 372 } 373 for (pl = pm = p; pm <= pe; pm++) 374 switch (*pm & (QUOTE|TRIM)) { 375 376 case '{': 377 brclev++; 378 continue; 379 380 case '}': 381 if (brclev) { 382 brclev--; 383 continue; 384 } 385 goto doit; 386 387 case ',': 388 if (brclev) 389 continue; 390 doit: 391 savec = *pm; 392 *pm = 0; 393 strlcpy(lm, pl, sizeof(restbuf) - (lm - restbuf)); 394 strlcat(restbuf, pe + 1, sizeof(restbuf)); 395 *pm = savec; 396 if (s == 0) { 397 spathp = pathp; 398 expsh(restbuf); 399 pathp = spathp; 400 *pathp = 0; 401 } else if (amatch(s, restbuf)) 402 return (1); 403 sort(); 404 pl = pm + 1; 405 continue; 406 407 case '[': 408 for (pm++; *pm && *pm != ']'; pm++) 409 continue; 410 if (!*pm) 411 yyerror("Missing ']'"); 412 continue; 413 } 414 return (0); 415 } 416 417 static int 418 match(char *s, char *p) 419 { 420 int c; 421 char *sentp; 422 char sexpany = expany; 423 424 if (*s == '.' && *p != '.') 425 return (0); 426 sentp = entp; 427 entp = s; 428 c = amatch(s, p); 429 entp = sentp; 430 expany = sexpany; 431 return (c); 432 } 433 434 static int 435 amatch(char *s, char *p) 436 { 437 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++) != 0) { 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 && S_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 } 516 pathp = spathp; 517 *pathp = '\0'; 518 return (0); 519 } 520 } 521 } 522 523 static int 524 smatch(char *s, char *p) 525 { 526 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++) != 0) { 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(char *s1, char *s2) 583 { 584 int len = strlen(s1) + strlen(s2) + 1; 585 char *s; 586 587 nleft -= len; 588 if (nleft <= 0 || ++eargc >= GAVSIZ) 589 yyerror("Arguments too long"); 590 eargv[eargc] = 0; 591 eargv[eargc - 1] = s = malloc(len); 592 if (s == NULL) 593 fatal("ran out of memory\n"); 594 while ((*s++ = *s1++ & TRIM) != 0) 595 ; 596 s--; 597 while ((*s++ = *s2++ & TRIM) != 0) 598 ; 599 } 600 601 static void 602 addpath(int c) 603 { 604 605 if (pathp >= lastpathp) 606 yyerror("Pathname too long"); 607 else { 608 *pathp++ = c & TRIM; 609 *pathp = '\0'; 610 } 611 } 612 613 /* 614 * Expand file names beginning with `~' into the 615 * user's home directory path name. Return a pointer in buf to the 616 * part corresponding to `file'. 617 */ 618 char * 619 exptilde(char *buf, char *file) 620 { 621 char *s1, *s2, *s3; 622 extern char homedir[]; 623 624 if (*file != '~') { 625 strcpy(buf, file); 626 return(buf); 627 } 628 if (*++file == '\0') { 629 s2 = homedir; 630 s3 = NULL; 631 } else if (*file == '/') { 632 s2 = homedir; 633 s3 = file; 634 } else { 635 s3 = file; 636 while (*s3 && *s3 != '/') 637 s3++; 638 if (*s3 == '/') 639 *s3 = '\0'; 640 else 641 s3 = NULL; 642 if (pw == NULL || strcmp(pw->pw_name, file) != 0) { 643 if ((pw = getpwnam(file)) == NULL) { 644 error("%s: unknown user name\n", file); 645 if (s3 != NULL) 646 *s3 = '/'; 647 return(NULL); 648 } 649 } 650 if (s3 != NULL) 651 *s3 = '/'; 652 s2 = pw->pw_dir; 653 } 654 for (s1 = buf; (*s1++ = *s2++) != 0; ) 655 ; 656 s2 = --s1; 657 if (s3 != NULL) { 658 s2++; 659 while ((*s1++ = *s3++) != 0) 660 ; 661 } 662 return(s2); 663 } 664