1 /* $OpenBSD: expand.c,v 1.11 2003/06/03 02:56:14 millert Exp $ */ 2 3 /* 4 * Copyright (c) 1983 Regents of the University of California. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include "defs.h" 33 #ifndef lint 34 #if 0 35 static char RCSid[] __attribute__((__unused__)) = 36 "$From: expand.c,v 1.4 1999/08/04 15:57:33 christos Exp $"; 37 #else 38 static char RCSid[] __attribute__((__unused__)) = 39 "$OpenBSD: expand.c,v 1.11 2003/06/03 02:56:14 millert Exp $"; 40 #endif 41 42 static char sccsid[] __attribute__((__unused__)) = 43 "@(#)expand.c 5.2 (Berkeley) 3/28/86"; 44 45 char copyright[] __attribute__((__unused__)) = 46 "@(#) Copyright (c) 1983 Regents of the University of California.\n\ 47 All rights reserved.\n"; 48 #endif /* not lint */ 49 50 51 #define MAXEARGS 2048 52 #define LC '{' 53 #define RC '}' 54 55 static char shchars[] = "${[*?"; 56 57 int which; /* bit mask of types to expand */ 58 int eargc; /* expanded arg count */ 59 char **eargv; /* expanded arg vectors */ 60 char *path; 61 char *pathp; 62 char *lastpathp; 63 char *tilde; /* "~user" if not expanding tilde, else "" */ 64 char *tpathp; 65 66 int expany; /* any expansions done? */ 67 char *entp; 68 char **sortbase; 69 char *argvbuf[MAXEARGS]; 70 71 #define sort() qsort((char *)sortbase, &eargv[eargc] - sortbase, \ 72 sizeof(*sortbase), argcmp), sortbase = &eargv[eargc] 73 74 static void Cat(u_char *, u_char *); 75 static void addpath(int); 76 static int argcmp(const void *, const void *); 77 78 static void 79 Cat(u_char *s1, u_char *s2) /* quote in s1 and s2 */ 80 { 81 char *cp; 82 int len = strlen((char *)s1) + strlen((char *)s2) + 2; 83 84 if ((eargc + 1) >= MAXEARGS) { 85 yyerror("Too many names"); 86 return; 87 } 88 89 eargv[++eargc] = NULL; 90 eargv[eargc - 1] = cp = xmalloc(len); 91 92 do { 93 if (*s1 == QUOTECHAR) 94 s1++; 95 } while ((*cp++ = *s1++) != '\0'); 96 cp--; 97 do { 98 if (*s2 == QUOTECHAR) 99 s2++; 100 } while ((*cp++ = *s2++) != '\0'); 101 } 102 103 static void 104 addpath(int c) 105 { 106 if (pathp >= lastpathp) { 107 yyerror("Pathname too long"); 108 return; 109 } else { 110 *pathp++ = c; 111 *pathp = CNULL; 112 } 113 } 114 115 /* 116 * Take a list of names and expand any macros, etc. 117 * wh = E_VARS if expanding variables. 118 * wh = E_SHELL if expanding shell characters. 119 * wh = E_TILDE if expanding `~'. 120 * or any of these or'ed together. 121 * 122 * Major portions of this were snarfed from csh/sh.glob.c. 123 */ 124 struct namelist * 125 expand(struct namelist *list, int wh) /* quote in list->n_name */ 126 { 127 struct namelist *nl, *prev; 128 int n; 129 char pathbuf[BUFSIZ]; 130 131 if (debug) 132 debugmsg(DM_CALL, "expand(%x, %d) start, list = %s", 133 list, wh, getnlstr(list)); 134 135 if (wh == 0) 136 fatalerr("expand() contains invalid 'wh' argument."); 137 138 which = wh; 139 path = tpathp = pathp = pathbuf; 140 *pathp = CNULL; 141 lastpathp = &pathbuf[sizeof pathbuf - 2]; 142 tilde = ""; 143 eargc = 0; 144 eargv = sortbase = argvbuf; 145 *eargv = NULL; 146 147 /* 148 * Walk the name list and expand names into eargv[]; 149 */ 150 for (nl = list; nl != NULL; nl = nl->n_next) 151 expstr((u_char *)nl->n_name); 152 /* 153 * Take expanded list of names from eargv[] and build a new list. 154 */ 155 list = prev = NULL; 156 for (n = 0; n < eargc; n++) { 157 nl = makenl(NULL); 158 nl->n_name = eargv[n]; 159 if (prev == NULL) 160 list = prev = nl; 161 else { 162 prev->n_next = nl; 163 prev = nl; 164 } 165 } 166 167 return(list); 168 } 169 170 /* 171 * xstrchr() is a version of strchr() that 172 * handles u_char buffers. 173 */ 174 u_char * 175 xstrchr(u_char *str, int ch) 176 { 177 u_char *cp; 178 179 for (cp = str; cp && *cp != CNULL; ++cp) 180 if (ch == *cp) 181 return(cp); 182 183 return(NULL); 184 } 185 186 void 187 expstr(u_char *s) 188 { 189 u_char *cp, *cp1; 190 struct namelist *tp; 191 u_char *tail; 192 u_char ebuf[BUFSIZ]; 193 u_char varbuff[BUFSIZ]; 194 int savec, oeargc; 195 extern char *homedir; 196 197 if (s == NULL || *s == CNULL) 198 return; 199 200 /* 201 * Remove quoted characters 202 */ 203 if (IS_ON(which, E_VARS)) { 204 if (strlen((char *)s) > sizeof(varbuff)) { 205 yyerror("Variable is too large."); 206 return; 207 } 208 for (cp = s, cp1 = varbuff; cp && *cp; ++cp) { 209 /* 210 * remove quoted character if the next 211 * character is not $ 212 */ 213 if (*cp == QUOTECHAR && *(cp+1) != '$') 214 ++cp; 215 else 216 *cp1++ = *cp; 217 } 218 *cp1 = CNULL; 219 s = varbuff; 220 } 221 222 /* 223 * Consider string 's' a variable that should be expanded if 224 * there is a '$' in 's' that is not quoted. 225 */ 226 if (IS_ON(which, E_VARS) && 227 ((cp = xstrchr(s, '$')) && !(cp > s && *(cp-1) == QUOTECHAR))) { 228 *cp++ = CNULL; 229 if (*cp == CNULL) { 230 yyerror("no variable name after '$'"); 231 return; 232 } 233 if (*cp == LC) { 234 cp++; 235 for (cp1 = cp; ; cp1 = tail + 1) { 236 if ((tail = xstrchr(cp1, RC)) == NULL) { 237 yyerror("unmatched '{'"); 238 return; 239 } 240 if (tail[-1] != QUOTECHAR) break; 241 } 242 *tail++ = savec = CNULL; 243 if (*cp == CNULL) { 244 yyerror("no variable name after '$'"); 245 return; 246 } 247 } else { 248 tail = cp + 1; 249 savec = *tail; 250 *tail = CNULL; 251 } 252 tp = lookup((char *)cp, LOOKUP, NULL); 253 if (savec != CNULL) 254 *tail = savec; 255 if (tp != NULL) { 256 for (; tp != NULL; tp = tp->n_next) { 257 (void) snprintf((char *)ebuf, sizeof(ebuf), 258 "%s%s%s", s, tp->n_name, tail); 259 expstr(ebuf); 260 } 261 return; 262 } 263 (void) snprintf((char *)ebuf, sizeof(ebuf), "%s%s", s, tail); 264 expstr(ebuf); 265 return; 266 } 267 if ((which & ~E_VARS) == 0 || !strcmp((char *)s, "{") || 268 !strcmp((char *)s, "{}")) { 269 Cat(s, (u_char *)""); 270 sort(); 271 return; 272 } 273 if (*s == '~') { 274 cp = ++s; 275 if (*cp == CNULL || *cp == '/') { 276 tilde = "~"; 277 cp1 = (u_char *)homedir; 278 } else { 279 tilde = (char *)(cp1 = ebuf); 280 *cp1++ = '~'; 281 do 282 *cp1++ = *cp++; 283 while (*cp && *cp != '/'); 284 *cp1 = CNULL; 285 if (pw == NULL || strcmp(pw->pw_name, 286 (char *)ebuf+1) != 0) { 287 if ((pw = getpwnam((char *)ebuf+1)) == NULL) { 288 strlcat((char *)ebuf, 289 ": unknown user name", 290 sizeof(ebuf)); 291 yyerror((char *)ebuf+1); 292 return; 293 } 294 } 295 cp1 = (u_char *)pw->pw_dir; 296 s = cp; 297 } 298 for (cp = (u_char *)path; (*cp++ = *cp1++) != '\0'; ) 299 continue; 300 tpathp = pathp = (char *)cp - 1; 301 } else { 302 tpathp = pathp = path; 303 tilde = ""; 304 } 305 *pathp = CNULL; 306 if (!(which & E_SHELL)) { 307 if (which & E_TILDE) 308 Cat((u_char *)path, s); 309 else 310 Cat((u_char *)tilde, s); 311 sort(); 312 return; 313 } 314 oeargc = eargc; 315 expany = 0; 316 expsh(s); 317 if (eargc == oeargc) 318 Cat(s, (u_char *)""); /* "nonomatch" is set */ 319 sort(); 320 } 321 322 static int 323 argcmp(const void *v1, const void *v2) 324 { 325 const char *const *a1 = v1, *const *a2 = v2; 326 327 return (strcmp(*a1, *a2)); 328 } 329 330 /* 331 * If there are any Shell meta characters in the name, 332 * expand into a list, after searching directory 333 */ 334 void 335 expsh(u_char *s) /* quote in s */ 336 { 337 u_char *cp, *oldcp; 338 char *spathp; 339 struct stat stb; 340 341 spathp = pathp; 342 cp = s; 343 while (!any(*cp, shchars)) { 344 if (*cp == CNULL) { 345 if (!expany || stat(path, &stb) >= 0) { 346 if (which & E_TILDE) 347 Cat((u_char *)path, (u_char *)""); 348 else 349 Cat((u_char *)tilde, (u_char *)tpathp); 350 } 351 goto endit; 352 } 353 if (*cp == QUOTECHAR) cp++; 354 addpath(*cp++); 355 } 356 oldcp = cp; 357 while (cp > s && *cp != '/') 358 cp--, pathp--; 359 if (*cp == '/') 360 cp++, pathp++; 361 *pathp = CNULL; 362 if (*oldcp == '{') { 363 (void) execbrc(cp, NULL); 364 return; 365 } 366 matchdir((char *)cp); 367 endit: 368 pathp = spathp; 369 *pathp = CNULL; 370 } 371 372 void 373 matchdir(char *pattern) /* quote in pattern */ 374 { 375 struct stat stb; 376 DIRENTRY *dp; 377 DIR *dirp; 378 379 dirp = opendir(path); 380 if (dirp == NULL) { 381 if (expany) 382 return; 383 goto patherr2; 384 } 385 if (fstat(dirfd(dirp), &stb) < 0) 386 goto patherr1; 387 if (!S_ISDIR(stb.st_mode)) { 388 errno = ENOTDIR; 389 goto patherr1; 390 } 391 while ((dp = readdir(dirp)) != NULL) 392 if (match(dp->d_name, pattern)) { 393 if (which & E_TILDE) 394 Cat((u_char *)path, (u_char *)dp->d_name); 395 else { 396 (void) strlcpy(pathp, dp->d_name, 397 lastpathp - pathp + 2); 398 Cat((u_char *)tilde, (u_char *)tpathp); 399 *pathp = CNULL; 400 } 401 } 402 closedir(dirp); 403 return; 404 405 patherr1: 406 closedir(dirp); 407 patherr2: 408 (void) strlcat(path, ": ", lastpathp - path + 2); 409 (void) strlcat(path, SYSERR, lastpathp - path + 2); 410 yyerror(path); 411 } 412 413 int 414 execbrc(u_char *p, u_char *s) /* quote in p */ 415 { 416 u_char restbuf[BUFSIZ + 2]; 417 u_char *pe, *pm, *pl; 418 int brclev = 0; 419 u_char *lm, savec; 420 char *spathp; 421 422 for (lm = restbuf; *p != '{'; *lm++ = *p++) 423 if (*p == QUOTECHAR) *lm++ = *p++; 424 425 for (pe = ++p; *pe; pe++) 426 switch (*pe) { 427 428 case '{': 429 brclev++; 430 continue; 431 432 case '}': 433 if (brclev == 0) 434 goto pend; 435 brclev--; 436 continue; 437 438 case '[': 439 for (pe++; *pe && *pe != ']'; pe++) 440 if (*p == QUOTECHAR) pe++; 441 if (!*pe) 442 yyerror("Missing ']'"); 443 continue; 444 445 case QUOTECHAR: /* skip this character */ 446 pe++; 447 continue; 448 } 449 pend: 450 if (brclev || !*pe) { 451 yyerror("Missing '}'"); 452 return (0); 453 } 454 for (pl = pm = p; pm <= pe; pm++) 455 /* the strip code was a noop */ 456 switch (*pm) { 457 458 case '{': 459 brclev++; 460 continue; 461 462 case '}': 463 if (brclev) { 464 brclev--; 465 continue; 466 } 467 goto doit; 468 469 case ',': 470 if (brclev) 471 continue; 472 doit: 473 savec = *pm; 474 *pm = 0; 475 *lm = 0; 476 (void) strlcat((char *)restbuf, (char *)pl, 477 sizeof(restbuf)); 478 (void) strlcat((char *)restbuf, (char *)pe + 1, 479 sizeof(restbuf)); 480 *pm = savec; 481 if (s == 0) { 482 spathp = pathp; 483 expsh(restbuf); 484 pathp = spathp; 485 *pathp = 0; 486 } else if (amatch((char *)s, restbuf)) 487 return (1); 488 sort(); 489 pl = pm + 1; 490 continue; 491 492 case '[': 493 for (pm++; *pm && *pm != ']'; pm++) 494 if (*pm == QUOTECHAR) pm++; 495 if (!*pm) 496 yyerror("Missing ']'"); 497 continue; 498 499 case QUOTECHAR: /* skip one character */ 500 pm++; 501 continue; 502 } 503 return (0); 504 } 505 506 int 507 match(char *s, char *p) /* quote in p */ 508 { 509 int c; 510 char *sentp; 511 char sexpany = expany; 512 513 if (*s == '.' && *p != '.') 514 return (0); 515 sentp = entp; 516 entp = s; 517 c = amatch(s, p); 518 entp = sentp; 519 expany = sexpany; 520 return (c); 521 } 522 523 int 524 amatch(char *s, u_char *p) /* quote in p */ 525 { 526 int scc; 527 int ok, lc; 528 char *spathp; 529 struct stat stb; 530 int c, cc; 531 532 expany = 1; 533 for (;;) { 534 scc = *s++; 535 switch (c = *p++) { 536 537 case '{': 538 return (execbrc((u_char *)p - 1, (u_char *)s - 1)); 539 540 case '[': 541 ok = 0; 542 lc = 077777; 543 while ((cc = *p++) != '\0') { 544 if (cc == ']') { 545 if (ok) 546 break; 547 return (0); 548 } 549 if (cc == QUOTECHAR) cc = *p++; 550 if (cc == '-') { 551 if (lc <= scc && scc <= (int)*p++) 552 ok++; 553 } else 554 if (scc == (lc = cc)) 555 ok++; 556 } 557 if (cc == 0) { 558 yyerror("Missing ']'"); 559 return (0); 560 } 561 continue; 562 563 case '*': 564 if (!*p) 565 return (1); 566 if (*p == '/') { 567 p++; 568 goto slash; 569 } 570 for (s--; *s; s++) 571 if (amatch(s, p)) 572 return (1); 573 return (0); 574 575 case CNULL: 576 return (scc == CNULL); 577 578 default: 579 if (c != scc) 580 return (0); 581 continue; 582 583 case '?': 584 if (scc == CNULL) 585 return (0); 586 continue; 587 588 case '/': 589 if (scc) 590 return (0); 591 slash: 592 s = entp; 593 spathp = pathp; 594 while (*s) 595 addpath(*s++); 596 addpath('/'); 597 if (stat(path, &stb) == 0 && S_ISDIR(stb.st_mode)) { 598 if (*p == CNULL) { 599 if (which & E_TILDE) { 600 Cat((u_char *)path, 601 (u_char *)""); 602 } else { 603 Cat((u_char *)tilde, 604 (u_char *)tpathp); 605 } 606 } else 607 expsh(p); 608 } 609 pathp = spathp; 610 *pathp = CNULL; 611 return (0); 612 } 613 } 614 } 615