1 /* $NetBSD: expand.c,v 1.17 2009/04/13 04:35:36 lukem Exp $ */
4 * Copyright (c) 1983, 1993
5 * The Regents of the University of California. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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.
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
32 #include <sys/cdefs.h>
35 static char sccsid
[] = "@(#)expand.c 8.1 (Berkeley) 6/9/93";
37 __RCSID("$NetBSD: expand.c,v 1.17 2009/04/13 04:35:36 lukem Exp $");
41 #include <sys/types.h>
48 #define GAVSIZ NCARGS / 6
52 static char shchars
[] = "${[*?";
54 int which
; /* bit mask of types to expand */
55 int eargc
; /* expanded arg count */
56 char **eargv
; /* expanded arg vectors */
60 const char *tilde
; /* "~user" if not expanding tilde, else "" */
64 int expany
; /* any expansions done? */
68 #define sort() qsort((char *)sortbase, &eargv[eargc] - sortbase, \
69 sizeof(*sortbase), argcmp), sortbase = &eargv[eargc]
71 static void Cat(const char *, const char *);
72 static void addpath(int);
73 static int amatch(char *, char *);
74 static int argcmp(const void *, const void *);
75 static int execbrc(char *, char *);
76 static void expsh(char *);
77 static void expstr(char *);
78 static int match(char *, char *);
79 static void matchdir(char *);
82 * Take a list of names and expand any macros, etc.
83 * wh = E_VARS if expanding variables.
84 * wh = E_SHELL if expanding shell characters.
85 * wh = E_TILDE if expanding `~'.
86 * or any of these or'ed together.
88 * Major portions of this were snarfed from csh/sh.glob.c.
91 expand(struct namelist
*list
, int wh
)
93 struct namelist
*nl
, *prev
;
96 char *argvbuf
[GAVSIZ
];
99 printf("expand(%lx, %d)\nlist = ", (long)list
, wh
);
106 for (nl
= list
; nl
!= NULL
; nl
= nl
->n_next
)
107 for (cp
= nl
->n_name
; *cp
; cp
++)
113 path
= tpathp
= pathp
= pathbuf
;
115 lastpathp
= &path
[sizeof pathbuf
- 2];
118 eargv
= sortbase
= argvbuf
;
122 * Walk the name list and expand names into eargv[];
124 for (nl
= list
; nl
!= NULL
; nl
= nl
->n_next
)
127 * Take expanded list of names from eargv[] and build a new list.
130 for (n
= 0; n
< eargc
; n
++) {
132 nl
->n_name
= eargv
[n
];
141 printf("expanded list = ");
155 extern char homedir
[];
157 if (s
== NULL
|| *s
== '\0')
160 if ((which
& E_VARS
) && (cp
= strchr(s
, '$')) != NULL
) {
163 yyerror("no variable name after '$'");
168 if ((tail
= strchr(cp
, RC
)) == NULL
) {
169 yyerror("unmatched '{'");
172 *tail
++ = savec
= '\0';
174 yyerror("no variable name after '$'");
182 tp
= lookup(cp
, 0, 0);
186 for (; tp
!= NULL
; tp
= tp
->n_next
) {
187 snprintf(expbuf
, sizeof(expbuf
), "%s%s%s", s
,
193 snprintf(expbuf
, sizeof(expbuf
), "%s%s", s
, tail
);
197 if ((which
& ~E_VARS
) == 0 || !strcmp(s
, "{") || !strcmp(s
, "{}")) {
204 if (*cp
== '\0' || *cp
== '/') {
208 tilde
= cp1
= expbuf
;
212 while (*cp
&& *cp
!= '/');
214 if (pw
== NULL
|| strcmp(pw
->pw_name
, expbuf
+1) != 0) {
215 if ((pw
= getpwnam(expbuf
+1)) == NULL
) {
216 strlcat(expbuf
, ": unknown user name",
225 for (cp
= path
; (*cp
++ = *cp1
++) != 0; )
227 tpathp
= pathp
= cp
- 1;
229 tpathp
= pathp
= path
;
233 if (!(which
& E_SHELL
)) {
245 Cat(s
, ""); /* "nonomatch" is set */
250 argcmp(const void *a1
, const void *a2
)
253 return (strcmp(*(const char * const *)a1
, *(const char * const *)a2
));
257 * If there are any Shell meta characters in the name,
258 * expand into a list, after searching directory
264 char *spathp
, *oldcp
;
269 while (!any(*cp
, shchars
)) {
271 if (!expany
|| stat(path
, &stb
) >= 0) {
282 while (cp
> s
&& *cp
!= '/')
298 matchdir(char *pattern
)
304 dirp
= opendir(path
);
310 if (fstat(dirp
->dd_fd
, &stb
) < 0)
312 if (!S_ISDIR(stb
.st_mode
)) {
316 while ((dp
= readdir(dirp
)) != NULL
)
317 if (match(dp
->d_name
, pattern
)) {
319 Cat(path
, dp
->d_name
);
321 strcpy(pathp
, dp
->d_name
);
333 strcat(path
, strerror(errno
));
338 execbrc(char *p
, char *s
)
340 char restbuf
[BUFSIZ
+ 2];
343 char *lm
, savec
, *spathp
;
345 for (lm
= restbuf
; *p
!= '{'; *lm
++ = *p
++)
347 for (pe
= ++p
; *pe
; pe
++)
361 for (pe
++; *pe
&& *pe
!= ']'; pe
++)
364 yyerror("Missing ']'");
368 if (brclev
|| !*pe
) {
369 yyerror("Missing '}'");
372 for (pl
= pm
= p
; pm
<= pe
; pm
++)
373 switch (*pm
& (QUOTE
|TRIM
)) {
392 strlcpy(lm
, pl
, sizeof(restbuf
) - (lm
- restbuf
));
393 strlcat(restbuf
, pe
+ 1, sizeof(restbuf
));
400 } else if (amatch(s
, restbuf
))
407 for (pm
++; *pm
&& *pm
!= ']'; pm
++)
410 yyerror("Missing ']'");
417 match(char *s
, char *p
)
421 char sexpany
= expany
;
423 if (*s
== '.' && *p
!= '.')
434 amatch(char *s
, char *p
)
448 return (execbrc(p
- 1, s
- 1));
453 while ((cc
= *p
++) != 0) {
460 if (lc
<= scc
&& scc
<= *p
++)
463 if (scc
== (lc
= cc
))
467 yyerror("Missing ']'");
485 return (scc
== '\0');
488 if ((c
& TRIM
) != scc
)
506 if (stat(path
, &stb
) == 0 && S_ISDIR(stb
.st_mode
)) {
523 Cat(const char *s1
, const char *s2
)
525 int len
= strlen(s1
) + strlen(s2
) + 1;
529 if (nleft
<= 0 || ++eargc
>= GAVSIZ
)
530 yyerror("Arguments too long");
532 eargv
[eargc
- 1] = s
= malloc(len
);
534 fatal("ran out of memory\n");
535 while ((*s
++ = *s1
++ & TRIM
) != 0)
538 while ((*s
++ = *s2
++ & TRIM
) != 0)
546 if (pathp
>= lastpathp
)
547 yyerror("Pathname too long");
555 * Expand file names beginning with `~' into the
556 * user's home directory path name. Return a pointer in buf to the
557 * part corresponding to `file'.
560 exptilde(char *expbuf
, char *file
)
563 extern char homedir
[];
566 strcpy(expbuf
, file
);
569 if (*++file
== '\0') {
572 } else if (*file
== '/') {
577 while (*s3
&& *s3
!= '/')
583 if (pw
== NULL
|| strcmp(pw
->pw_name
, file
) != 0) {
584 if ((pw
= getpwnam(file
)) == NULL
) {
585 error("%s: unknown user name\n", file
);
595 for (s1
= expbuf
; (*s1
++ = *s2
++) != 0; )
600 while ((*s1
++ = *s3
++) != 0)