1 /* $OpenBSD: expand.c,v 1.18 2019/06/28 13:35:03 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 <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 char pathbuf[BUFSIZ];
56
57 int expany; /* any expansions done? */
58 char *entp;
59 char **sortbase;
60 char *argvbuf[MAXEARGS];
61
62 #define sort() qsort((char *)sortbase, &eargv[eargc] - sortbase, \
63 sizeof(*sortbase), argcmp), sortbase = &eargv[eargc]
64
65 static void Cat(u_char *, u_char *);
66 static void addpath(int);
67 static int argcmp(const void *, const void *);
68
69 static void
Cat(u_char * s1,u_char * s2)70 Cat(u_char *s1, u_char *s2) /* quote in s1 and s2 */
71 {
72 char *cp;
73 int len = strlen((char *)s1) + strlen((char *)s2) + 2;
74
75 if ((eargc + 1) >= MAXEARGS) {
76 yyerror("Too many names");
77 return;
78 }
79
80 eargv[++eargc] = NULL;
81 eargv[eargc - 1] = cp = xmalloc(len);
82
83 do {
84 if (*s1 == QUOTECHAR)
85 s1++;
86 } while ((*cp++ = *s1++) != '\0');
87 cp--;
88 do {
89 if (*s2 == QUOTECHAR)
90 s2++;
91 } while ((*cp++ = *s2++) != '\0');
92 }
93
94 static void
addpath(int c)95 addpath(int c)
96 {
97 if (pathp >= lastpathp) {
98 yyerror("Pathname too long");
99 return;
100 } else {
101 *pathp++ = c;
102 *pathp = CNULL;
103 }
104 }
105
106 /*
107 * Take a list of names and expand any macros, etc.
108 * wh = E_VARS if expanding variables.
109 * wh = E_SHELL if expanding shell characters.
110 * wh = E_TILDE if expanding `~'.
111 * or any of these or'ed together.
112 *
113 * Major portions of this were snarfed from csh/sh.glob.c.
114 */
115 struct namelist *
expand(struct namelist * list,int wh)116 expand(struct namelist *list, int wh) /* quote in list->n_name */
117 {
118 struct namelist *nl, *prev;
119 int n;
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 *
xstrchr(u_char * str,int ch)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
expstr(u_char * s)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 if ((cp = strchr(s, '/')) == NULL) {
264 tilde = "~";
265 s++;
266 } else {
267 tilde = memcpy(ebuf, s, (cp - s));
268 ebuf[cp - s] = '\0';
269 s = cp;
270 }
271 cp = exptilde(path, tilde, sizeof(pathbuf));
272 tpathp = pathp = (char *)cp;
273 } else {
274 tpathp = pathp = path;
275 tilde = "";
276 }
277 *pathp = CNULL;
278 if (!(which & E_SHELL)) {
279 if (which & E_TILDE)
280 Cat((u_char *)path, s);
281 else
282 Cat((u_char *)tilde, s);
283 sort();
284 return;
285 }
286 oeargc = eargc;
287 expany = 0;
288 expsh(s);
289 if (eargc == oeargc)
290 Cat(s, (u_char *)""); /* "nonomatch" is set */
291 sort();
292 }
293
294 static int
argcmp(const void * v1,const void * v2)295 argcmp(const void *v1, const void *v2)
296 {
297 const char *const *a1 = v1, *const *a2 = v2;
298
299 return (strcmp(*a1, *a2));
300 }
301
302 /*
303 * If there are any Shell meta characters in the name,
304 * expand into a list, after searching directory
305 */
306 void
expsh(u_char * s)307 expsh(u_char *s) /* quote in s */
308 {
309 u_char *cp, *oldcp;
310 char *spathp;
311 struct stat stb;
312
313 spathp = pathp;
314 cp = s;
315 while (!any(*cp, shchars)) {
316 if (*cp == CNULL) {
317 if (!expany || stat(path, &stb) >= 0) {
318 if (which & E_TILDE)
319 Cat((u_char *)path, (u_char *)"");
320 else
321 Cat((u_char *)tilde, (u_char *)tpathp);
322 }
323 goto endit;
324 }
325 if (*cp == QUOTECHAR) cp++;
326 addpath(*cp++);
327 }
328 oldcp = cp;
329 while (cp > s && *cp != '/')
330 cp--, pathp--;
331 if (*cp == '/')
332 cp++, pathp++;
333 *pathp = CNULL;
334 if (*oldcp == '{') {
335 (void) execbrc(cp, NULL);
336 return;
337 }
338 matchdir((char *)cp);
339 endit:
340 pathp = spathp;
341 *pathp = CNULL;
342 }
343
344 void
matchdir(char * pattern)345 matchdir(char *pattern) /* quote in pattern */
346 {
347 struct stat stb;
348 struct dirent *dp;
349 DIR *dirp;
350
351 dirp = opendir(path);
352 if (dirp == NULL) {
353 if (expany)
354 return;
355 goto patherr2;
356 }
357 if (fstat(dirfd(dirp), &stb) == -1)
358 goto patherr1;
359 if (!S_ISDIR(stb.st_mode)) {
360 errno = ENOTDIR;
361 goto patherr1;
362 }
363 while ((dp = readdir(dirp)) != NULL)
364 if (match(dp->d_name, pattern)) {
365 if (which & E_TILDE)
366 Cat((u_char *)path, (u_char *)dp->d_name);
367 else {
368 (void) strlcpy(pathp, dp->d_name,
369 lastpathp - pathp + 2);
370 Cat((u_char *)tilde, (u_char *)tpathp);
371 *pathp = CNULL;
372 }
373 }
374 closedir(dirp);
375 return;
376
377 patherr1:
378 closedir(dirp);
379 patherr2:
380 (void) strlcat(path, ": ", lastpathp - path + 2);
381 (void) strlcat(path, SYSERR, lastpathp - path + 2);
382 yyerror(path);
383 }
384
385 int
execbrc(u_char * p,u_char * s)386 execbrc(u_char *p, u_char *s) /* quote in p */
387 {
388 u_char restbuf[BUFSIZ + 2];
389 u_char *pe, *pm, *pl;
390 int brclev = 0;
391 u_char *lm, savec;
392 char *spathp;
393
394 for (lm = restbuf; *p != '{'; *lm++ = *p++)
395 if (*p == QUOTECHAR) *lm++ = *p++;
396
397 for (pe = ++p; *pe; pe++)
398 switch (*pe) {
399
400 case '{':
401 brclev++;
402 continue;
403
404 case '}':
405 if (brclev == 0)
406 goto pend;
407 brclev--;
408 continue;
409
410 case '[':
411 for (pe++; *pe && *pe != ']'; pe++)
412 if (*p == QUOTECHAR) pe++;
413 if (!*pe)
414 yyerror("Missing ']'");
415 continue;
416
417 case QUOTECHAR: /* skip this character */
418 pe++;
419 continue;
420 }
421 pend:
422 if (brclev || !*pe) {
423 yyerror("Missing '}'");
424 return (0);
425 }
426 for (pl = pm = p; pm <= pe; pm++)
427 /* the strip code was a noop */
428 switch (*pm) {
429
430 case '{':
431 brclev++;
432 continue;
433
434 case '}':
435 if (brclev) {
436 brclev--;
437 continue;
438 }
439 goto doit;
440
441 case ',':
442 if (brclev)
443 continue;
444 doit:
445 savec = *pm;
446 *pm = 0;
447 *lm = 0;
448 (void) strlcat((char *)restbuf, (char *)pl,
449 sizeof(restbuf));
450 (void) strlcat((char *)restbuf, (char *)pe + 1,
451 sizeof(restbuf));
452 *pm = savec;
453 if (s == 0) {
454 spathp = pathp;
455 expsh(restbuf);
456 pathp = spathp;
457 *pathp = 0;
458 } else if (amatch((char *)s, restbuf))
459 return (1);
460 sort();
461 pl = pm + 1;
462 continue;
463
464 case '[':
465 for (pm++; *pm && *pm != ']'; pm++)
466 if (*pm == QUOTECHAR) pm++;
467 if (!*pm)
468 yyerror("Missing ']'");
469 continue;
470
471 case QUOTECHAR: /* skip one character */
472 pm++;
473 continue;
474 }
475 return (0);
476 }
477
478 int
match(char * s,char * p)479 match(char *s, char *p) /* quote in p */
480 {
481 int c;
482 char *sentp;
483 char sexpany = expany;
484
485 if (*s == '.' && *p != '.')
486 return (0);
487 sentp = entp;
488 entp = s;
489 c = amatch(s, p);
490 entp = sentp;
491 expany = sexpany;
492 return (c);
493 }
494
495 int
amatch(char * s,u_char * p)496 amatch(char *s, u_char *p) /* quote in p */
497 {
498 int scc;
499 int ok, lc;
500 char *spathp;
501 struct stat stb;
502 int c, cc;
503
504 expany = 1;
505 for (;;) {
506 scc = *s++;
507 switch (c = *p++) {
508
509 case '{':
510 return (execbrc((u_char *)p - 1, (u_char *)s - 1));
511
512 case '[':
513 ok = 0;
514 lc = 077777;
515 while ((cc = *p++) != '\0') {
516 if (cc == ']') {
517 if (ok)
518 break;
519 return (0);
520 }
521 if (cc == QUOTECHAR) cc = *p++;
522 if (cc == '-') {
523 if (lc <= scc && scc <= (int)*p++)
524 ok++;
525 } else
526 if (scc == (lc = cc))
527 ok++;
528 }
529 if (cc == 0) {
530 yyerror("Missing ']'");
531 return (0);
532 }
533 continue;
534
535 case '*':
536 if (!*p)
537 return (1);
538 if (*p == '/') {
539 p++;
540 goto slash;
541 }
542 for (s--; *s; s++)
543 if (amatch(s, p))
544 return (1);
545 return (0);
546
547 case CNULL:
548 return (scc == CNULL);
549
550 default:
551 if (c != scc)
552 return (0);
553 continue;
554
555 case '?':
556 if (scc == CNULL)
557 return (0);
558 continue;
559
560 case '/':
561 if (scc)
562 return (0);
563 slash:
564 s = entp;
565 spathp = pathp;
566 while (*s)
567 addpath(*s++);
568 addpath('/');
569 if (stat(path, &stb) == 0 && S_ISDIR(stb.st_mode)) {
570 if (*p == CNULL) {
571 if (which & E_TILDE) {
572 Cat((u_char *)path,
573 (u_char *)"");
574 } else {
575 Cat((u_char *)tilde,
576 (u_char *)tpathp);
577 }
578 } else
579 expsh(p);
580 }
581 pathp = spathp;
582 *pathp = CNULL;
583 return (0);
584 }
585 }
586 }
587