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