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 * 14 * Copyright (c) 1998, by Sun Microsystems, Inc. 15 * All rights reserved. 16 */ 17 #pragma ident "%Z%%M% %I% %E% SMI" 18 19 #include "defs.h" 20 #include <string.h> 21 22 #define GAVSIZ NCARGS / 6 23 #define LC '{' 24 #define RC '}' 25 26 static char shchars[] = "${[*?"; 27 28 int which; /* bit mask of types to expand */ 29 int eargc; /* expanded arg count */ 30 char **eargv; /* expanded arg vectors */ 31 char *path; 32 char *pathp; 33 char *lastpathp; 34 char *tilde; /* "~user" if not expanding tilde, else "" */ 35 char *tpathp; 36 int nleft; 37 38 int expany; /* any expansions done? */ 39 char *entp; 40 char **sortbase; 41 42 char *index(); 43 int argcmp(); 44 45 #define sort() qsort((char *)sortbase, &eargv[eargc] - sortbase, \ 46 sizeof (*sortbase), argcmp), sortbase = &eargv[eargc] 47 48 #define MIN(a, b) ((a) < (b) ? (a) : (b)) 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[LINESIZE]; 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[LINESIZE]; 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 (void) snprintf(buf, sizeof (buf), "%s%s%s", s, 159 tp->n_name, tail); 160 expstr(buf); 161 } 162 return; 163 } 164 (void) snprintf(buf, sizeof (buf), "%s%s", s, tail); 165 expstr(buf); 166 return; 167 } 168 if ((which & ~E_VARS) == 0 || !strcmp(s, "{") || !strcmp(s, "{}")) { 169 Cat(s, ""); 170 sort(); 171 return; 172 } 173 if (*s == '~') { 174 cp = ++s; 175 if (*cp == '\0' || *cp == '/') { 176 tilde = "~"; 177 cp1 = homedir; 178 } else { 179 tilde = cp1 = buf; 180 *cp1++ = '~'; 181 do { 182 if (cp1 >= &buf[sizeof (buf)]) { 183 yyerror("User name too long"); 184 return; 185 } 186 *cp1++ = *cp++; 187 } while (*cp && *cp != '/'); 188 *cp1 = '\0'; 189 if (pw == NULL || strcmp(pw->pw_name, buf+1) != 0) { 190 if ((pw = getpwnam(buf+1)) == NULL) { 191 static char unknown_user[] = 192 ": unknown user name"; 193 194 cp1 = MIN(cp1, 195 &buf[sizeof (buf)] - 196 sizeof (unknown_user)); 197 strcpy(cp1, unknown_user); 198 yyerror(buf+1); 199 return; 200 } 201 } 202 cp1 = pw->pw_dir; 203 s = cp; 204 } 205 for (cp = path; cp <= lastpathp + 1 && (*cp++ = *cp1++); ) 206 ; 207 tpathp = pathp = cp - 1; 208 } else { 209 tpathp = pathp = path; 210 tilde = ""; 211 } 212 *pathp = '\0'; 213 if (!(which & E_SHELL)) { 214 if (which & E_TILDE) 215 Cat(path, s); 216 else 217 Cat(tilde, s); 218 sort(); 219 return; 220 } 221 oeargc = eargc; 222 expany = 0; 223 expsh(s); 224 if (eargc == oeargc) 225 Cat(s, ""); /* "nonomatch" is set */ 226 sort(); 227 } 228 229 static 230 argcmp(a1, a2) 231 char **a1, **a2; 232 { 233 234 return (strcmp(*a1, *a2)); 235 } 236 237 /* 238 * If there are any Shell meta characters in the name, 239 * expand into a list, after searching directory 240 */ 241 expsh(s) 242 char *s; 243 { 244 register char *cp; 245 register char *spathp, *oldcp; 246 struct stat stb; 247 248 spathp = pathp; 249 cp = s; 250 while (!any(*cp, shchars)) { 251 if (*cp == '\0') { 252 if (!expany || stat(path, &stb) >= 0) { 253 if (which & E_TILDE) 254 Cat(path, ""); 255 else 256 Cat(tilde, tpathp); 257 } 258 goto endit; 259 } 260 addpath(*cp++); 261 } 262 oldcp = cp; 263 while (cp > s && *cp != '/') 264 cp--, pathp--; 265 if (*cp == '/') 266 cp++, pathp++; 267 *pathp = '\0'; 268 if (*oldcp == '{') { 269 execbrc(cp, NULL); 270 return; 271 } 272 matchdir(cp); 273 endit: 274 pathp = spathp; 275 *pathp = '\0'; 276 } 277 278 matchdir(pattern) 279 char *pattern; 280 { 281 struct stat stb; 282 register struct dirent *dp; 283 DIR *dirp; 284 285 dirp = opendir(path); 286 if (dirp == NULL) { 287 if (expany) 288 return; 289 goto patherr2; 290 } 291 if (fstat(dirp->dd_fd, &stb) < 0) 292 goto patherr1; 293 if (!ISDIR(stb.st_mode)) { 294 errno = ENOTDIR; 295 goto patherr1; 296 } 297 while ((dp = readdir(dirp)) != NULL) 298 if (match(dp->d_name, pattern)) { 299 if (which & E_TILDE) 300 Cat(path, dp->d_name); 301 else { 302 if (pathp + strlen(dp->d_name) - 1 > 303 lastpathp) { 304 errno = ENAMETOOLONG; 305 goto patherr1; 306 } 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 { 319 char *strerr = strerror(errno); 320 321 if (path + strlen(path) + strlen(strerr) + 1 > lastpathp) 322 strcpy(lastpathp - strlen(strerr) - 1, ": "); 323 else 324 strcat(path, ": "); 325 strcat(path, strerr); 326 } 327 yyerror(path); 328 } 329 330 execbrc(p, s) 331 char *p, *s; 332 { 333 char restbuf[LINESIZE + 2]; 334 register char *pe, *pm, *pl; 335 int brclev = 0; 336 char *lm, savec, *spathp; 337 338 for (lm = restbuf; *p != '{'; *lm++ = *p++) { 339 if (lm >= &restbuf[sizeof (restbuf)]) { 340 yyerror("Pathname too long"); 341 return (0); 342 } 343 } 344 for (pe = ++p; *pe; pe++) 345 switch (*pe) { 346 347 case '{': 348 brclev++; 349 continue; 350 351 case '}': 352 if (brclev == 0) 353 goto pend; 354 brclev--; 355 continue; 356 357 case '[': 358 for (pe++; *pe && *pe != ']'; pe++) 359 continue; 360 if (!*pe) 361 yyerror("Missing ']'"); 362 continue; 363 } 364 pend: 365 if (brclev || !*pe) { 366 yyerror("Missing '}'"); 367 return (0); 368 } 369 for (pl = pm = p; pm <= pe; pm++) 370 switch (*pm & (QUOTE|TRIM)) { 371 372 case '{': 373 brclev++; 374 continue; 375 376 case '}': 377 if (brclev) { 378 brclev--; 379 continue; 380 } 381 goto doit; 382 383 case ',': 384 if (brclev) 385 continue; 386 doit: 387 savec = *pm; 388 *pm = 0; 389 if (lm + strlen(pl) + strlen(pe + 1) >= 390 &restbuf[sizeof (restbuf)]) { 391 yyerror("Pathname too long"); 392 return (0); 393 } 394 strcpy(lm, pl); 395 strcat(restbuf, pe + 1); 396 *pm = savec; 397 if (s == 0) { 398 spathp = pathp; 399 expsh(restbuf); 400 pathp = spathp; 401 *pathp = 0; 402 } else if (amatch(s, restbuf)) 403 return (1); 404 sort(); 405 pl = pm + 1; 406 continue; 407 408 case '[': 409 for (pm++; *pm && *pm != ']'; pm++) 410 continue; 411 if (!*pm) 412 yyerror("Missing ']'"); 413 continue; 414 } 415 return (0); 416 } 417 418 match(s, p) 419 char *s, *p; 420 { 421 register int c; 422 register char *sentp; 423 char sexpany = expany; 424 425 if (*s == '.' && *p != '.') 426 return (0); 427 sentp = entp; 428 entp = s; 429 c = amatch(s, p); 430 entp = sentp; 431 expany = sexpany; 432 return (c); 433 } 434 435 amatch(s, p) 436 register char *s, *p; 437 { 438 register int scc; 439 int ok, lc; 440 char *spathp; 441 struct stat stb; 442 int c, cc; 443 444 expany = 1; 445 for (;;) { 446 scc = *s++ & TRIM; 447 switch (c = *p++) { 448 449 case '{': 450 return (execbrc(p - 1, s - 1)); 451 452 case '[': 453 ok = 0; 454 lc = 077777; 455 while (cc = *p++) { 456 if (cc == ']') { 457 if (ok) 458 break; 459 return (0); 460 } 461 if (cc == '-') { 462 if (lc <= scc && scc <= *p++) 463 ok++; 464 } else 465 if (scc == (lc = cc)) 466 ok++; 467 } 468 if (cc == 0) { 469 yyerror("Missing ']'"); 470 return (0); 471 } 472 continue; 473 474 case '*': 475 if (!*p) 476 return (1); 477 if (*p == '/') { 478 p++; 479 goto slash; 480 } 481 for (s--; *s; s++) 482 if (amatch(s, p)) 483 return (1); 484 return (0); 485 486 case '\0': 487 return (scc == '\0'); 488 489 default: 490 if ((c & TRIM) != scc) 491 return (0); 492 continue; 493 494 case '?': 495 if (scc == '\0') 496 return (0); 497 continue; 498 499 case '/': 500 if (scc) 501 return (0); 502 slash: 503 s = entp; 504 spathp = pathp; 505 while (*s) 506 addpath(*s++); 507 addpath('/'); 508 if (stat(path, &stb) == 0 && ISDIR(stb.st_mode)) 509 if (*p == '\0') { 510 if (which & E_TILDE) 511 Cat(path, ""); 512 else 513 Cat(tilde, tpathp); 514 } else 515 expsh(p); 516 pathp = spathp; 517 *pathp = '\0'; 518 return (0); 519 } 520 } 521 } 522 523 smatch(s, p) 524 register char *s, *p; 525 { 526 register 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++) { 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 Cat(s1, s2) 582 register char *s1, *s2; 583 { 584 int len = strlen(s1) + strlen(s2) + 1; 585 register char *s; 586 587 nleft -= len; 588 if (nleft <= 0 || ++eargc >= GAVSIZ) 589 fatal("Arguments too long\n"); 590 eargv[eargc] = 0; 591 eargv[eargc - 1] = s = (char *)malloc(len); 592 if (s == NULL) 593 fatal("ran out of memory\n"); 594 while (*s++ = *s1++ & TRIM) 595 ; 596 s--; 597 while (*s++ = *s2++ & TRIM) 598 ; 599 } 600 601 addpath(c) 602 char 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(buf, len, file) 620 char buf[]; 621 unsigned int len; 622 register char *file; 623 { 624 register char *s1, *s2, *s3; 625 extern char homedir[]; 626 627 if (*file != '~') { 628 if (strlen(file) + 1 > len) { 629 error("pathname too long: %s\n", file); 630 return (NULL); 631 } 632 strcpy(buf, file); 633 return (buf); 634 } 635 if (*++file == '\0') { 636 s2 = homedir; 637 s3 = NULL; 638 } else if (*file == '/') { 639 s2 = homedir; 640 s3 = file; 641 } else { 642 s3 = file; 643 while (*s3 && *s3 != '/') 644 s3++; 645 if (*s3 == '/') 646 *s3 = '\0'; 647 else 648 s3 = NULL; 649 if (pw == NULL || strcmp(pw->pw_name, file) != 0) { 650 if ((pw = getpwnam(file)) == NULL) { 651 error("%s: unknown user name\n", file); 652 if (s3 != NULL) 653 *s3 = '/'; 654 return (NULL); 655 } 656 } 657 if (s3 != NULL) 658 *s3 = '/'; 659 s2 = pw->pw_dir; 660 } 661 for (s1 = buf; s1 < &buf[len] && (*s1++ = *s2++); ) 662 ; 663 s2 = --s1; 664 if (s3 != NULL) { 665 s2++; 666 while (s1 < &buf[len] && (*s1++ = *s3++)) 667 ; 668 } 669 if (s1 == &buf[len]) { 670 error("pathname too long: %s\n", file - 1); 671 return (NULL); 672 } 673 return (s2); 674 } 675