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