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
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
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 $
40 #define GAVSIZ NCARGS / 6
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 */
55 char *tilde
; /* "~user" if not expanding tilde, else "" */
59 int expany
; /* any expansions done? */
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.
84 expand(struct namelist
*list
, int wh
)
86 register struct namelist
*nl
, *prev
;
89 char *argvbuf
[GAVSIZ
];
92 printf("expand(%p, %d)\nlist = ", list
, wh
);
99 for (nl
= list
; nl
!= NULL
; nl
= nl
->n_next
)
100 for (cp
= nl
->n_name
; *cp
; cp
++)
106 path
= tpathp
= pathp
= pathbuf
;
108 lastpathp
= &path
[sizeof pathbuf
- 2];
111 eargv
= sortbase
= argvbuf
;
115 * Walk the name list and expand names into eargv[];
117 for (nl
= list
; nl
!= NULL
; nl
= nl
->n_next
)
120 * Take expanded list of names from eargv[] and build a new list.
123 for (n
= 0; n
< eargc
; n
++) {
125 nl
->n_name
= eargv
[n
];
134 printf("expanded list = ");
143 register char *cp
, *cp1
;
144 register struct namelist
*tp
;
148 extern char homedir
[];
150 if (s
== NULL
|| *s
== '\0')
153 if ((which
& E_VARS
) && (cp
= index(s
, '$')) != NULL
) {
156 yyerror("no variable name after '$'");
161 if ((tail
= index(cp
, RC
)) == NULL
) {
162 yyerror("unmatched '{'");
165 *tail
++ = savec
= '\0';
167 yyerror("no variable name after '$'");
175 tp
= lookup(cp
, 0, NULL
);
179 for (; tp
!= NULL
; tp
= tp
->n_next
) {
180 snprintf(buf
, sizeof(buf
),
181 "%s%s%s", s
, tp
->n_name
, tail
);
186 snprintf(buf
, sizeof(buf
), "%s%s", s
, tail
);
190 if ((which
& ~E_VARS
) == 0 || !strcmp(s
, "{") || !strcmp(s
, "{}")) {
197 if (*cp
== '\0' || *cp
== '/') {
205 while (*cp
&& *cp
!= '/');
207 if (pw
== NULL
|| strcmp(pw
->pw_name
, buf
+1) != 0) {
208 if ((pw
= getpwnam(buf
+1)) == NULL
) {
209 strcat(buf
, ": unknown user name");
217 for (cp
= path
; (*cp
++ = *cp1
++); )
219 tpathp
= pathp
= cp
- 1;
221 tpathp
= pathp
= path
;
225 if (!(which
& E_SHELL
)) {
237 Cat(s
, ""); /* "nonomatch" is set */
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
256 register char *spathp
, *oldcp
;
261 while (!any(*cp
, shchars
)) {
263 if (!expany
|| stat(path
, &stb
) >= 0) {
274 while (cp
> s
&& *cp
!= '/')
290 matchdir(char *pattern
)
293 register struct dirent
*dp
;
296 dirp
= opendir(path
);
302 if (fstat(dirp
->dd_fd
, &stb
) < 0)
304 if (!ISDIR(stb
.st_mode
)) {
308 while ((dp
= readdir(dirp
)) != NULL
)
309 if (match(dp
->d_name
, pattern
)) {
311 Cat(path
, dp
->d_name
);
313 strcpy(pathp
, dp
->d_name
);
325 strcat(path
, strerror(errno
));
330 execbrc(char *p
, char *s
)
332 char restbuf
[BUFSIZ
+ 2];
333 register char *pe
, *pm
, *pl
;
335 char *lm
, savec
, *spathp
;
339 for (lm
= restbuf
; *p
!= '{'; *lm
++ = *p
++)
341 for (pe
= ++p
; *pe
; pe
++)
355 for (pe
++; *pe
&& *pe
!= ']'; pe
++)
358 yyerror("Missing ']'");
362 if (brclev
|| !*pe
) {
363 yyerror("Missing '}'");
366 for (pl
= pm
= p
; pm
<= pe
; pm
++)
367 switch (*pm
& (QUOTE
|TRIM
)) {
387 strcat(restbuf
, pe
+ 1);
394 } else if (amatch(s
, restbuf
))
401 for (pm
++; *pm
&& *pm
!= ']'; pm
++)
404 yyerror("Missing ']'");
411 match(char *s
, char *p
)
414 register char *sentp
;
418 if (*s
== '.' && *p
!= '.')
429 amatch(register char *s
, register char *p
)
442 return (execbrc(p
- 1, s
- 1));
447 while ((cc
= *p
++)) {
454 if (lc
<= scc
&& scc
<= *p
++)
457 if (scc
== (lc
= cc
))
461 yyerror("Missing ']'");
479 return (scc
== '\0');
482 if ((c
& TRIM
) != scc
)
500 if (stat(path
, &stb
) == 0 && ISDIR(stb
.st_mode
)) {
517 smatch(register char *s
, register char *p
)
529 while ((cc
= *p
++)) {
536 if (lc
<= scc
&& scc
<= *p
++)
539 if (scc
== (lc
= cc
))
543 yyerror("Missing ']'");
557 return (scc
== '\0');
560 if ((c
& TRIM
) != scc
)
574 Cat(register char *s1
, register char *s2
)
579 len
= strlen(s1
) + strlen(s2
) + 1;
581 if (nleft
<= 0 || ++eargc
>= GAVSIZ
)
582 yyerror("Arguments too long");
584 eargv
[eargc
- 1] = s
= malloc(len
);
586 fatal("ran out of memory\n");
587 while ((*s
++ = *s1
++ & TRIM
))
590 while ((*s
++ = *s2
++ & TRIM
))
598 if (pathp
>= lastpathp
)
599 yyerror("Pathname too long");
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'.
612 exptilde(char buf
[], register char *file
, int maxlen
)
614 register char *s1
, *s2
, *s3
;
615 extern char homedir
[];
617 if (strlen(file
) >= maxlen
)
623 if (*++file
== '\0') {
626 } else if (*file
== '/') {
631 while (*s3
&& *s3
!= '/')
637 if (pw
== NULL
|| strcmp(pw
->pw_name
, file
) != 0) {
638 if ((pw
= getpwnam(file
)) == NULL
) {
639 error("%s: unknown user name\n", file
);
649 for (s1
= buf
; (*s1
++ = *s2
++) && s1
< buf
+maxlen
; )
652 if (s3
!= NULL
&& s1
< buf
+maxlen
) {
654 while ((*s1
++ = *s3
++) && s1
< buf
+maxlen
)
657 if (s1
== buf
+maxlen
)