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