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