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