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