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