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