]> git.ipfire.org Git - thirdparty/bash.git/blob - pathexp.c
Imported from ../bash-2.05a.tar.gz.
[thirdparty/bash.git] / pathexp.c
1 /* 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, 59 Temple Place, Suite 330, Boston, MA 02111 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/strmatch.h>
37
38 #if defined (USE_POSIX_GLOB_LIBRARY)
39 # include <glob.h>
40 typedef int posix_glob_errfunc_t __P((const char *, int));
41 #else
42 # include <glob/glob.h>
43 #endif
44
45 /* Control whether * matches .files in globbing. */
46 int glob_dot_filenames;
47
48 /* Control whether the extended globbing features are enabled. */
49 int extended_glob = 0;
50
51 /* Return nonzero if STRING has any unquoted special globbing chars in it. */
52 int
53 unquoted_glob_pattern_p (string)
54 register char *string;
55 {
56 register int c;
57 int open;
58
59 open = 0;
60 while (c = *string++)
61 {
62 switch (c)
63 {
64 case '?':
65 case '*':
66 return (1);
67
68 case '[':
69 open++;
70 continue;
71
72 case ']':
73 if (open)
74 return (1);
75 continue;
76
77 case '+':
78 case '@':
79 case '!':
80 if (*string == '(') /*)*/
81 return (1);
82 continue;
83
84 case CTLESC:
85 case '\\':
86 if (*string++ == '\0')
87 return (0);
88 }
89 }
90 return (0);
91 }
92
93 /* PATHNAME can contain characters prefixed by CTLESC; this indicates
94 that the character is to be quoted. We quote it here in the style
95 that the glob library recognizes. If flags includes QGLOB_CVTNULL,
96 we change quoted null strings (pathname[0] == CTLNUL) into empty
97 strings (pathname[0] == 0). If this is called after quote removal
98 is performed, (flags & QGLOB_CVTNULL) should be 0; if called when quote
99 removal has not been done (for example, before attempting to match a
100 pattern while executing a case statement), flags should include
101 QGLOB_CVTNULL. If flags includes QGLOB_FILENAME, appropriate quoting
102 to match a filename should be performed. */
103 char *
104 quote_string_for_globbing (pathname, qflags)
105 const char *pathname;
106 int qflags;
107 {
108 char *temp;
109 register int i, j;
110
111 temp = (char *)xmalloc (strlen (pathname) + 1);
112
113 if ((qflags & QGLOB_CVTNULL) && QUOTED_NULL (pathname))
114 {
115 temp[0] = '\0';
116 return temp;
117 }
118
119 for (i = j = 0; pathname[i]; i++)
120 {
121 if (pathname[i] == CTLESC)
122 {
123 if ((qflags & QGLOB_FILENAME) && pathname[i+1] == '/')
124 continue;
125 temp[j++] = '\\';
126 }
127 else
128 temp[j++] = pathname[i];
129 }
130 temp[j] = '\0';
131
132 return (temp);
133 }
134
135 char *
136 quote_globbing_chars (string)
137 char *string;
138 {
139 char *temp, *s, *t;
140
141 temp = (char *)xmalloc (strlen (string) * 2 + 1);
142 for (t = temp, s = string; *s; )
143 {
144 switch (*s)
145 {
146 case '*':
147 case '[':
148 case ']':
149 case '?':
150 case '\\':
151 *t++ = '\\';
152 break;
153 case '+':
154 case '@':
155 case '!':
156 if (s[1] == '(') /*(*/
157 *t++ = '\\';
158 break;
159 }
160 *t++ = *s++;
161 }
162 *t = '\0';
163 return temp;
164 }
165
166 /* Call the glob library to do globbing on PATHNAME. */
167 char **
168 shell_glob_filename (pathname)
169 const char *pathname;
170 {
171 #if defined (USE_POSIX_GLOB_LIBRARY)
172 register int i;
173 char *temp, **results;
174 glob_t filenames;
175 int glob_flags;
176
177 temp = quote_string_for_globbing (pathname, QGLOB_FILENAME);
178
179 filenames.gl_offs = 0;
180
181 # if defined (GLOB_PERIOD)
182 glob_flags = glob_dot_filenames ? GLOB_PERIOD : 0;
183 # else
184 glob_flags = 0;
185 # endif /* !GLOB_PERIOD */
186
187 glob_flags |= (GLOB_ERR | GLOB_DOOFFS);
188
189 i = glob (temp, glob_flags, (posix_glob_errfunc_t *)NULL, &filenames);
190
191 free (temp);
192
193 if (i == GLOB_NOSPACE || i == GLOB_ABORTED)
194 return ((char **)NULL);
195 else if (i == GLOB_NOMATCH)
196 filenames.gl_pathv = (char **)NULL;
197 else if (i != 0) /* other error codes not in POSIX.2 */
198 filenames.gl_pathv = (char **)NULL;
199
200 results = filenames.gl_pathv;
201
202 if (results && ((GLOB_FAILED (results)) == 0))
203 {
204 if (should_ignore_glob_matches ())
205 ignore_glob_matches (results);
206 if (results && results[0])
207 sort_char_array (results);
208 else
209 {
210 FREE (results);
211 results = (char **)NULL;
212 }
213 }
214
215 return (results);
216
217 #else /* !USE_POSIX_GLOB_LIBRARY */
218
219 char *temp, **results;
220
221 noglob_dot_filenames = glob_dot_filenames == 0;
222
223 temp = quote_string_for_globbing (pathname, QGLOB_FILENAME);
224
225 results = glob_filename (temp);
226 free (temp);
227
228 if (results && ((GLOB_FAILED (results)) == 0))
229 {
230 if (should_ignore_glob_matches ())
231 ignore_glob_matches (results);
232 if (results && results[0])
233 sort_char_array (results);
234 else
235 {
236 FREE (results);
237 results = (char **)&glob_error_return;
238 }
239 }
240
241 return (results);
242 #endif /* !USE_POSIX_GLOB_LIBRARY */
243 }
244
245 /* Stuff for GLOBIGNORE. */
246
247 static struct ignorevar globignore =
248 {
249 "GLOBIGNORE",
250 (struct ign *)0,
251 0,
252 (char *)0,
253 (sh_iv_item_func_t *)0,
254 };
255
256 /* Set up to ignore some glob matches because the value of GLOBIGNORE
257 has changed. If GLOBIGNORE is being unset, we also need to disable
258 the globbing of filenames beginning with a `.'. */
259 void
260 setup_glob_ignore (name)
261 char *name;
262 {
263 char *v;
264
265 v = get_string_value (name);
266 setup_ignore_patterns (&globignore);
267
268 if (globignore.num_ignores)
269 glob_dot_filenames = 1;
270 else if (v == 0)
271 glob_dot_filenames = 0;
272 }
273
274 int
275 should_ignore_glob_matches ()
276 {
277 return globignore.num_ignores;
278 }
279
280 /* Return 0 if NAME matches a pattern in the globignore.ignores list. */
281 static int
282 glob_name_is_acceptable (name)
283 const char *name;
284 {
285 struct ign *p;
286 int flags;
287
288 /* . and .. are never matched */
289 if (name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')))
290 return (0);
291
292 flags = FNM_PATHNAME | FNMATCH_EXTFLAG;
293 for (p = globignore.ignores; p->val; p++)
294 {
295 if (strmatch (p->val, (char *)name, flags) != FNM_NOMATCH)
296 return (0);
297 }
298 return (1);
299 }
300
301 /* Internal function to test whether filenames in NAMES should be
302 ignored. NAME_FUNC is a pointer to a function to call with each
303 name. It returns non-zero if the name is acceptable to the particular
304 ignore function which called _ignore_names; zero if the name should
305 be removed from NAMES. */
306
307 static void
308 ignore_globbed_names (names, name_func)
309 char **names;
310 sh_ignore_func_t *name_func;
311 {
312 char **newnames;
313 int n, i;
314
315 for (i = 0; names[i]; i++)
316 ;
317 newnames = alloc_array (i + 1);
318
319 for (n = i = 0; names[i]; i++)
320 {
321 if ((*name_func) (names[i]))
322 newnames[n++] = names[i];
323 else
324 free (names[i]);
325 }
326
327 newnames[n] = (char *)NULL;
328
329 if (n == 0)
330 {
331 names[0] = (char *)NULL;
332 free (newnames);
333 return;
334 }
335
336 /* Copy the acceptable names from NEWNAMES back to NAMES and set the
337 new array end. */
338 for (n = 0; newnames[n]; n++)
339 names[n] = newnames[n];
340 names[n] = (char *)NULL;
341 free (newnames);
342 }
343
344 void
345 ignore_glob_matches (names)
346 char **names;
347 {
348 if (globignore.num_ignores == 0)
349 return;
350
351 ignore_globbed_names (names, glob_name_is_acceptable);
352 }
353
354 void
355 setup_ignore_patterns (ivp)
356 struct ignorevar *ivp;
357 {
358 int numitems, maxitems, ptr;
359 char *colon_bit, *this_ignoreval;
360 struct ign *p;
361
362 this_ignoreval = get_string_value (ivp->varname);
363
364 /* If nothing has changed then just exit now. */
365 if ((this_ignoreval && ivp->last_ignoreval && STREQ (this_ignoreval, ivp->last_ignoreval)) ||
366 (!this_ignoreval && !ivp->last_ignoreval))
367 return;
368
369 /* Oops. The ignore variable has changed. Re-parse it. */
370 ivp->num_ignores = 0;
371
372 if (ivp->ignores)
373 {
374 for (p = ivp->ignores; p->val; p++)
375 free(p->val);
376 free (ivp->ignores);
377 ivp->ignores = (struct ign *)NULL;
378 }
379
380 if (ivp->last_ignoreval)
381 {
382 free (ivp->last_ignoreval);
383 ivp->last_ignoreval = (char *)NULL;
384 }
385
386 if (this_ignoreval == 0 || *this_ignoreval == '\0')
387 return;
388
389 ivp->last_ignoreval = savestring (this_ignoreval);
390
391 numitems = maxitems = ptr = 0;
392
393 while (colon_bit = extract_colon_unit (this_ignoreval, &ptr))
394 {
395 if (numitems + 1 >= maxitems)
396 {
397 maxitems += 10;
398 ivp->ignores = (struct ign *)xrealloc (ivp->ignores, maxitems * sizeof (struct ign));
399 }
400 ivp->ignores[numitems].val = colon_bit;
401 ivp->ignores[numitems].len = strlen (colon_bit);
402 ivp->ignores[numitems].flags = 0;
403 if (ivp->item_func)
404 (*ivp->item_func) (&ivp->ignores[numitems]);
405 numitems++;
406 }
407 ivp->ignores[numitems].val = (char *)NULL;
408 ivp->num_ignores = numitems;
409 }