blob: 640451494c8ae942bfb13928708d79604db87d35 [file] [log] [blame]
Jari Aaltoccc6cda1996-12-23 17:02:34 +00001/* pathexp.c -- The shell interface to the globbing library. */
2
3/* Copyright (C) 1995 Free Software Foundation, Inc.
4
5 This file is part of GNU Bash, the Bourne Again SHell.
6
7 Bash is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 2, or (at your option) any later
10 version.
11
12 Bash is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 for more details.
16
17 You should have received a copy of the GNU General Public License along
18 with Bash; see the file COPYING. If not, write to the Free Software
19 Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
20
21#include "config.h"
22
23#include "bashtypes.h"
24#include <stdio.h>
25
26#if defined (HAVE_UNISTD_H)
27# include <unistd.h>
28#endif
29
30#include "bashansi.h"
31
32#include "shell.h"
33#include "pathexp.h"
34#include "flags.h"
35
36#include <glob/fnmatch.h>
37#include <glob/glob.h>
38
39/* Control whether * matches .files in globbing. */
40int glob_dot_filenames;
41
42/* Return nonzero if STRING has any unquoted special globbing chars in it. */
43int
44unquoted_glob_pattern_p (string)
45 register char *string;
46{
47 register int c;
48 int open;
49
50 open = 0;
51 while (c = *string++)
52 {
53 switch (c)
54 {
55 case '?':
56 case '*':
57 return (1);
58
59 case '[':
60 open++;
61 continue;
62
63 case ']':
64 if (open)
65 return (1);
66 continue;
67
68 case CTLESC:
69 case '\\':
70 if (*string++ == '\0')
71 return (0);
72 }
73 }
74 return (0);
75}
76
77/* PATHNAME can contain characters prefixed by CTLESC; this indicates
78 that the character is to be quoted. We quote it here in the style
79 that the glob library recognizes. If CONVERT_QUOTED_NULLS is non-zero,
80 we change quoted null strings (pathname[0] == CTLNUL) into empty
81 strings (pathname[0] == 0). If this is called after quote removal
82 is performed, CONVERT_QUOTED_NULLS should be 0; if called when quote
83 removal has not been done (for example, before attempting to match a
84 pattern while executing a case statement), CONVERT_QUOTED_NULLS should
85 be 1. */
86char *
87quote_string_for_globbing (pathname, convert_quoted_nulls)
88 char *pathname;
89 int convert_quoted_nulls;
90{
91 char *temp;
92 register int i;
93
94 temp = savestring (pathname);
95
96 if (convert_quoted_nulls && QUOTED_NULL (pathname))
97 {
98 temp[0] = '\0';
99 return temp;
100 }
101
102 for (i = 0; temp[i]; i++)
103 {
104 if (temp[i] == CTLESC)
105 temp[i++] = '\\';
106 }
107
108 return (temp);
109}
110
111char *
112quote_globbing_chars (string)
113 char *string;
114{
115 char *temp, *s, *t;
116
117 temp = xmalloc (strlen (string) * 2 + 1);
118 for (t = temp, s = string; *s; )
119 {
120 switch (*s)
121 {
122 case '*':
123 case '[':
124 case ']':
125 case '?':
126 case '\\':
127 *t++ = '\\';
128 break;
129 }
130 *t++ = *s++;
131 }
132 *t = '\0';
133 return temp;
134}
135
136/* Call the glob library to do globbing on PATHNAME. */
137char **
138shell_glob_filename (pathname)
139 char *pathname;
140{
141#if defined (USE_POSIX_GLOB_LIBRARY)
142 register int i;
143 char *temp, **return_value;
144 glob_t filenames;
145 int glob_flags;
146
147 temp = quote_string_for_globbing (pathname, 0);
148
149 filenames.gl_offs = 0;
150
151 glob_flags = glob_dot_filenames ? GLOB_PERIOD : 0;
152 glob_flags |= (GLOB_ERR | GLOB_DOOFFS);
153
154 i = glob (temp, glob_flags, (Function *)NULL, &filenames);
155
156 free (temp);
157
158 if (i == GLOB_NOSPACE || i == GLOB_ABEND)
159 return ((char **)NULL);
160
161 if (i == GLOB_NOMATCH)
162 filenames.gl_pathv[0] = (char *)NULL;
163
164 return (filenames.gl_pathv);
165
166#else /* !USE_POSIX_GLOB_LIBRARY */
167
168 char *temp, **results;
169
170 noglob_dot_filenames = glob_dot_filenames == 0;
171
172 temp = quote_string_for_globbing (pathname, 0);
173
174 results = glob_filename (temp);
175 free (temp);
176
177 if (results && ((GLOB_FAILED (results)) == 0))
178 {
179 if (should_ignore_glob_matches ())
180 ignore_glob_matches (results);
181 if (results && results[0])
182 sort_char_array (results);
183 else
184 {
185 FREE (results);
186 results = (char **)&glob_error_return;
187 }
188 }
189
190 return (results);
191#endif /* !USE_POSIX_GLOB_LIBRARY */
192}
193
194/* Stuff for GLOBIGNORE. */
195
196static struct ignorevar globignore =
197{
198 "GLOBIGNORE",
199 (struct ign *)0,
200 0,
201 (char *)0,
202 (Function *)0,
203};
204
205/* Set up to ignore some glob matches because the value of GLOBIGNORE
206 has changed. If GLOBIGNORE is being unset, we also need to disable
207 the globbing of filenames beginning with a `.'. */
208void
209setup_glob_ignore (name)
210 char *name;
211{
212 char *v;
213
214 v = get_string_value (name);
215 setup_ignore_patterns (&globignore);
216
217 if (globignore.num_ignores)
218 glob_dot_filenames = 1;
219 else if (v == 0)
220 glob_dot_filenames = 0;
221}
222
223int
224should_ignore_glob_matches ()
225{
226 return globignore.num_ignores;
227}
228
229/* Return 0 if NAME matches a pattern in the globignore.ignores list. */
230static int
231glob_name_is_acceptable (name)
232 char *name;
233{
234 struct ign *p;
235
236 /* . and .. are never matched */
237 if (name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')))
238 return (0);
239
240 for (p = globignore.ignores; p->val; p++)
241 {
242 if (fnmatch (p->val, name, FNM_PATHNAME) != FNM_NOMATCH)
243 return (0);
244 }
245 return (1);
246}
247
248/* Internal function to test whether filenames in NAMES should be
249 ignored. NAME_FUNC is a pointer to a function to call with each
250 name. It returns non-zero if the name is acceptable to the particular
251 ignore function which called _ignore_names; zero if the name should
252 be removed from NAMES. */
253
254static void
255ignore_globbed_names (names, name_func)
256 char **names;
257 Function *name_func;
258{
259 char **newnames;
260 int n, i;
261
262 for (i = 0; names[i]; i++)
263 ;
264 newnames = (char **)xmalloc ((i + 1) * sizeof (char *));
265
266 for (n = i = 0; names[i]; i++)
267 {
268 if ((*name_func) (names[i]))
269 newnames[n++] = names[i];
270 else
271 free (names[i]);
272 }
273
274 newnames[n] = (char *)NULL;
275
276 if (n == 0)
277 {
278 names[0] = (char *)NULL;
279 free (newnames);
280 return;
281 }
282
283 /* Copy the acceptable names from NEWNAMES back to NAMES and set the
284 new array end. */
285 for (n = 0; newnames[n]; n++)
286 names[n] = newnames[n];
287 names[n] = (char *)NULL;
288}
289
290void
291ignore_glob_matches (names)
292 char **names;
293{
294 if (globignore.num_ignores == 0)
295 return;
296
297 ignore_globbed_names (names, glob_name_is_acceptable);
298}
299
300void
301setup_ignore_patterns (ivp)
302 struct ignorevar *ivp;
303{
304 int numitems, maxitems, ptr;
305 char *colon_bit, *this_ignoreval;
306 struct ign *p;
307
308 this_ignoreval = get_string_value (ivp->varname);
309
310 /* If nothing has changed then just exit now. */
311 if ((this_ignoreval && ivp->last_ignoreval && STREQ (this_ignoreval, ivp->last_ignoreval)) ||
312 (!this_ignoreval && !ivp->last_ignoreval))
313 return;
314
315 /* Oops. The ignore variable has changed. Re-parse it. */
316 ivp->num_ignores = 0;
317
318 if (ivp->ignores)
319 {
320 for (p = ivp->ignores; p->val; p++)
321 free(p->val);
322 free (ivp->ignores);
323 ivp->ignores = (struct ign *)NULL;
324 }
325
326 if (ivp->last_ignoreval)
327 {
328 free (ivp->last_ignoreval);
329 ivp->last_ignoreval = (char *)NULL;
330 }
331
332 if (this_ignoreval == 0 || *this_ignoreval == '\0')
333 return;
334
335 ivp->last_ignoreval = savestring (this_ignoreval);
336
337 numitems = maxitems = ptr = 0;
338
339 while (colon_bit = extract_colon_unit (this_ignoreval, &ptr))
340 {
341 if (numitems + 1 >= maxitems)
342 {
343 maxitems += 10;
344 ivp->ignores = (struct ign *)xrealloc (ivp->ignores, maxitems * sizeof (struct ign));
345 }
346 ivp->ignores[numitems].val = colon_bit;
347 ivp->ignores[numitems].len = strlen (colon_bit);
348 ivp->ignores[numitems].flags = 0;
349 if (ivp->item_func)
350 (*ivp->item_func) (&ivp->ignores[numitems]);
351 numitems++;
352 }
353 ivp->ignores[numitems].val = (char *)NULL;
354 ivp->num_ignores = numitems;
355}