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