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