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