Pre-2.0 release, MFC firewire disk changes to properly detach SIMs.
[dragonfly.git] / usr.bin / rdist / expand.c
blobf730383a48f8a7561b01ee1d0425beb12fe79c55
1 /*
2 * Copyright (c) 1983, 1993
3 * The Regents of the University of California. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
33 * @(#)expand.c 8.1 (Berkeley) 6/9/93
34 * $FreeBSD: src/usr.bin/rdist/expand.c,v 1.8 1999/08/28 01:05:06 peter Exp $
35 * $DragonFly: src/usr.bin/rdist/expand.c,v 1.5 2007/11/25 18:10:07 swildner Exp $
38 #include "defs.h"
40 #define GAVSIZ NCARGS / 6
41 #define LC '{'
42 #define RC '}'
44 #define sort() qsort((char *)sortbase, &eargv[eargc] - sortbase, \
45 sizeof(*sortbase), argcmp), sortbase = &eargv[eargc]
47 static char shchars[] = "${[*?";
49 int which; /* bit mask of types to expand */
50 int eargc; /* expanded arg count */
51 char **eargv; /* expanded arg vectors */
52 char *path;
53 char *pathp;
54 char *lastpathp;
55 char *tilde; /* "~user" if not expanding tilde, else "" */
56 char *tpathp;
57 int nleft;
59 int expany; /* any expansions done? */
60 char *entp;
61 char **sortbase;
63 static void Cat(char *, char *);
64 static void addpath(int);
65 static int amatch(char *, char *);
66 static int argcmp(const void *, const void *);
67 static int execbrc(char *, char *);
68 static void expsh(char *);
69 static void expstr(char *);
70 static int match(char *, char *);
71 static void matchdir(char *);
72 static int smatch(char *, char *);
75 * Take a list of names and expand any macros, etc.
76 * wh = E_VARS if expanding variables.
77 * wh = E_SHELL if expanding shell characters.
78 * wh = E_TILDE if expanding `~'.
79 * or any of these or'ed together.
81 * Major portions of this were snarfed from csh/sh.glob.c.
83 struct namelist *
84 expand(struct namelist *list, int wh)
86 register struct namelist *nl, *prev;
87 register int n;
88 char pathbuf[BUFSIZ];
89 char *argvbuf[GAVSIZ];
91 if (debug) {
92 printf("expand(%p, %d)\nlist = ", list, wh);
93 prnames(list);
96 if (wh == 0) {
97 register char *cp;
99 for (nl = list; nl != NULL; nl = nl->n_next)
100 for (cp = nl->n_name; *cp; cp++)
101 *cp = *cp & TRIM;
102 return(list);
105 which = wh;
106 path = tpathp = pathp = pathbuf;
107 *pathp = '\0';
108 lastpathp = &path[sizeof pathbuf - 2];
109 tilde = "";
110 eargc = 0;
111 eargv = sortbase = argvbuf;
112 *eargv = 0;
113 nleft = NCARGS - 4;
115 * Walk the name list and expand names into eargv[];
117 for (nl = list; nl != NULL; nl = nl->n_next)
118 expstr(nl->n_name);
120 * Take expanded list of names from eargv[] and build a new list.
122 list = prev = NULL;
123 for (n = 0; n < eargc; n++) {
124 nl = makenl(NULL);
125 nl->n_name = eargv[n];
126 if (prev == NULL)
127 list = prev = nl;
128 else {
129 prev->n_next = nl;
130 prev = nl;
133 if (debug) {
134 printf("expanded list = ");
135 prnames(list);
137 return(list);
140 static void
141 expstr(char *s)
143 register char *cp, *cp1;
144 register struct namelist *tp;
145 char *tail;
146 char buf[BUFSIZ];
147 int savec, oeargc;
148 extern char homedir[];
150 if (s == NULL || *s == '\0')
151 return;
153 if ((which & E_VARS) && (cp = index(s, '$')) != NULL) {
154 *cp++ = '\0';
155 if (*cp == '\0') {
156 yyerror("no variable name after '$'");
157 return;
159 if (*cp == LC) {
160 cp++;
161 if ((tail = index(cp, RC)) == NULL) {
162 yyerror("unmatched '{'");
163 return;
165 *tail++ = savec = '\0';
166 if (*cp == '\0') {
167 yyerror("no variable name after '$'");
168 return;
170 } else {
171 tail = cp + 1;
172 savec = *tail;
173 *tail = '\0';
175 tp = lookup(cp, 0, NULL);
176 if (savec != '\0')
177 *tail = savec;
178 if (tp != NULL) {
179 for (; tp != NULL; tp = tp->n_next) {
180 snprintf(buf, sizeof(buf),
181 "%s%s%s", s, tp->n_name, tail);
182 expstr(buf);
184 return;
186 snprintf(buf, sizeof(buf), "%s%s", s, tail);
187 expstr(buf);
188 return;
190 if ((which & ~E_VARS) == 0 || !strcmp(s, "{") || !strcmp(s, "{}")) {
191 Cat(s, "");
192 sort();
193 return;
195 if (*s == '~') {
196 cp = ++s;
197 if (*cp == '\0' || *cp == '/') {
198 tilde = "~";
199 cp1 = homedir;
200 } else {
201 tilde = cp1 = buf;
202 *cp1++ = '~';
204 *cp1++ = *cp++;
205 while (*cp && *cp != '/');
206 *cp1 = '\0';
207 if (pw == NULL || strcmp(pw->pw_name, buf+1) != 0) {
208 if ((pw = getpwnam(buf+1)) == NULL) {
209 strcat(buf, ": unknown user name");
210 yyerror(buf+1);
211 return;
214 cp1 = pw->pw_dir;
215 s = cp;
217 for (cp = path; (*cp++ = *cp1++); )
219 tpathp = pathp = cp - 1;
220 } else {
221 tpathp = pathp = path;
222 tilde = "";
224 *pathp = '\0';
225 if (!(which & E_SHELL)) {
226 if (which & E_TILDE)
227 Cat(path, s);
228 else
229 Cat(tilde, s);
230 sort();
231 return;
233 oeargc = eargc;
234 expany = 0;
235 expsh(s);
236 if (eargc == oeargc)
237 Cat(s, ""); /* "nonomatch" is set */
238 sort();
241 static int
242 argcmp(const void *a1, const void *a2)
245 return (strcmp(*(char **)a1, *(char **)a2));
249 * If there are any Shell meta characters in the name,
250 * expand into a list, after searching directory
252 static void
253 expsh(char *s)
255 register char *cp;
256 register char *spathp, *oldcp;
257 struct stat stb;
259 spathp = pathp;
260 cp = s;
261 while (!any(*cp, shchars)) {
262 if (*cp == '\0') {
263 if (!expany || stat(path, &stb) >= 0) {
264 if (which & E_TILDE)
265 Cat(path, "");
266 else
267 Cat(tilde, tpathp);
269 goto endit;
271 addpath(*cp++);
273 oldcp = cp;
274 while (cp > s && *cp != '/')
275 cp--, pathp--;
276 if (*cp == '/')
277 cp++, pathp++;
278 *pathp = '\0';
279 if (*oldcp == '{') {
280 execbrc(cp, NULL);
281 return;
283 matchdir(cp);
284 endit:
285 pathp = spathp;
286 *pathp = '\0';
289 static void
290 matchdir(char *pattern)
292 struct stat stb;
293 register struct dirent *dp;
294 DIR *dirp;
296 dirp = opendir(path);
297 if (dirp == NULL) {
298 if (expany)
299 return;
300 goto patherr2;
302 if (fstat(dirp->dd_fd, &stb) < 0)
303 goto patherr1;
304 if (!ISDIR(stb.st_mode)) {
305 errno = ENOTDIR;
306 goto patherr1;
308 while ((dp = readdir(dirp)) != NULL)
309 if (match(dp->d_name, pattern)) {
310 if (which & E_TILDE)
311 Cat(path, dp->d_name);
312 else {
313 strcpy(pathp, dp->d_name);
314 Cat(tilde, tpathp);
315 *pathp = '\0';
318 closedir(dirp);
319 return;
321 patherr1:
322 closedir(dirp);
323 patherr2:
324 strcat(path, ": ");
325 strcat(path, strerror(errno));
326 yyerror(path);
329 static int
330 execbrc(char *p, char *s)
332 char restbuf[BUFSIZ + 2];
333 register char *pe, *pm, *pl;
334 int brclev;
335 char *lm, savec, *spathp;
337 brclev = 0;
339 for (lm = restbuf; *p != '{'; *lm++ = *p++)
340 continue;
341 for (pe = ++p; *pe; pe++)
342 switch (*pe) {
344 case '{':
345 brclev++;
346 continue;
348 case '}':
349 if (brclev == 0)
350 goto pend;
351 brclev--;
352 continue;
354 case '[':
355 for (pe++; *pe && *pe != ']'; pe++)
356 continue;
357 if (!*pe)
358 yyerror("Missing ']'");
359 continue;
361 pend:
362 if (brclev || !*pe) {
363 yyerror("Missing '}'");
364 return (0);
366 for (pl = pm = p; pm <= pe; pm++)
367 switch (*pm & (QUOTE|TRIM)) {
369 case '{':
370 brclev++;
371 continue;
373 case '}':
374 if (brclev) {
375 brclev--;
376 continue;
378 goto doit;
380 case ',':
381 if (brclev)
382 continue;
383 doit:
384 savec = *pm;
385 *pm = 0;
386 strcpy(lm, pl);
387 strcat(restbuf, pe + 1);
388 *pm = savec;
389 if (s == 0) {
390 spathp = pathp;
391 expsh(restbuf);
392 pathp = spathp;
393 *pathp = 0;
394 } else if (amatch(s, restbuf))
395 return (1);
396 sort();
397 pl = pm + 1;
398 continue;
400 case '[':
401 for (pm++; *pm && *pm != ']'; pm++)
402 continue;
403 if (!*pm)
404 yyerror("Missing ']'");
405 continue;
407 return (0);
410 static int
411 match(char *s, char *p)
413 register int c;
414 register char *sentp;
415 char sexpany;
417 sexpany = expany;
418 if (*s == '.' && *p != '.')
419 return (0);
420 sentp = entp;
421 entp = s;
422 c = amatch(s, p);
423 entp = sentp;
424 expany = sexpany;
425 return (c);
428 static int
429 amatch(register char *s, register char *p)
431 register int scc;
432 int ok, lc, c, cc;
433 char *spathp;
434 struct stat stb;
436 expany = 1;
437 for (;;) {
438 scc = *s++ & TRIM;
439 switch (c = *p++) {
441 case '{':
442 return (execbrc(p - 1, s - 1));
444 case '[':
445 ok = 0;
446 lc = 077777;
447 while ((cc = *p++)) {
448 if (cc == ']') {
449 if (ok)
450 break;
451 return (0);
453 if (cc == '-') {
454 if (lc <= scc && scc <= *p++)
455 ok++;
456 } else
457 if (scc == (lc = cc))
458 ok++;
460 if (cc == 0) {
461 yyerror("Missing ']'");
462 return (0);
464 continue;
466 case '*':
467 if (!*p)
468 return (1);
469 if (*p == '/') {
470 p++;
471 goto slash;
473 for (s--; *s; s++)
474 if (amatch(s, p))
475 return (1);
476 return (0);
478 case '\0':
479 return (scc == '\0');
481 default:
482 if ((c & TRIM) != scc)
483 return (0);
484 continue;
486 case '?':
487 if (scc == '\0')
488 return (0);
489 continue;
491 case '/':
492 if (scc)
493 return (0);
494 slash:
495 s = entp;
496 spathp = pathp;
497 while (*s)
498 addpath(*s++);
499 addpath('/');
500 if (stat(path, &stb) == 0 && ISDIR(stb.st_mode)) {
501 if (*p == '\0') {
502 if (which & E_TILDE)
503 Cat(path, "");
504 else
505 Cat(tilde, tpathp);
506 } else
507 expsh(p);
509 pathp = spathp;
510 *pathp = '\0';
511 return (0);
516 static int
517 smatch(register char *s, register char *p)
519 register int scc;
520 int ok, lc, c, cc;
522 for (;;) {
523 scc = *s++ & TRIM;
524 switch (c = *p++) {
526 case '[':
527 ok = 0;
528 lc = 077777;
529 while ((cc = *p++)) {
530 if (cc == ']') {
531 if (ok)
532 break;
533 return (0);
535 if (cc == '-') {
536 if (lc <= scc && scc <= *p++)
537 ok++;
538 } else
539 if (scc == (lc = cc))
540 ok++;
542 if (cc == 0) {
543 yyerror("Missing ']'");
544 return (0);
546 continue;
548 case '*':
549 if (!*p)
550 return (1);
551 for (s--; *s; s++)
552 if (smatch(s, p))
553 return (1);
554 return (0);
556 case '\0':
557 return (scc == '\0');
559 default:
560 if ((c & TRIM) != scc)
561 return (0);
562 continue;
564 case '?':
565 if (scc == 0)
566 return (0);
567 continue;
573 static void
574 Cat(register char *s1, register char *s2)
576 int len;
577 register char *s;
579 len = strlen(s1) + strlen(s2) + 1;
580 nleft -= len;
581 if (nleft <= 0 || ++eargc >= GAVSIZ)
582 yyerror("Arguments too long");
583 eargv[eargc] = 0;
584 eargv[eargc - 1] = s = malloc(len);
585 if (s == NULL)
586 fatal("ran out of memory\n");
587 while ((*s++ = *s1++ & TRIM))
589 s--;
590 while ((*s++ = *s2++ & TRIM))
594 static void
595 addpath(int c)
598 if (pathp >= lastpathp)
599 yyerror("Pathname too long");
600 else {
601 *pathp++ = c & TRIM;
602 *pathp = '\0';
607 * Expand file names beginning with `~' into the
608 * user's home directory path name. Return a pointer in buf to the
609 * part corresponding to `file'.
611 char *
612 exptilde(char buf[], register char *file, int maxlen)
614 register char *s1, *s2, *s3;
615 extern char homedir[];
617 if (strlen(file) >= maxlen)
618 return(NULL);
619 if (*file != '~') {
620 strcpy(buf, file);
621 return(buf);
623 if (*++file == '\0') {
624 s2 = homedir;
625 s3 = NULL;
626 } else if (*file == '/') {
627 s2 = homedir;
628 s3 = file;
629 } else {
630 s3 = file;
631 while (*s3 && *s3 != '/')
632 s3++;
633 if (*s3 == '/')
634 *s3 = '\0';
635 else
636 s3 = NULL;
637 if (pw == NULL || strcmp(pw->pw_name, file) != 0) {
638 if ((pw = getpwnam(file)) == NULL) {
639 error("%s: unknown user name\n", file);
640 if (s3 != NULL)
641 *s3 = '/';
642 return(NULL);
645 if (s3 != NULL)
646 *s3 = '/';
647 s2 = pw->pw_dir;
649 for (s1 = buf; (*s1++ = *s2++) && s1 < buf+maxlen; )
651 s2 = --s1;
652 if (s3 != NULL && s1 < buf+maxlen) {
653 s2++;
654 while ((*s1++ = *s3++) && s1 < buf+maxlen)
657 if (s1 == buf+maxlen)
658 return(NULL);
659 return(s2);