]> git.ipfire.org Git - thirdparty/bash.git/blame - pathexp.c
Imported from ../bash-2.04.tar.gz.
[thirdparty/bash.git] / pathexp.c
CommitLineData
ccc6cda3
JA
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
bb70624e 19 Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
ccc6cda3
JA
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>
b72432fd
JA
37
38#if defined (USE_POSIX_GLOB_LIBRARY)
39# include <glob.h>
40#else
41# include <glob/glob.h>
42#endif
ccc6cda3
JA
43
44/* Control whether * matches .files in globbing. */
45int glob_dot_filenames;
46
cce855bc
JA
47/* Control whether the extended globbing features are enabled. */
48int extended_glob = 0;
49
ccc6cda3
JA
50/* Return nonzero if STRING has any unquoted special globbing chars in it. */
51int
52unquoted_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
cce855bc
JA
76 case '+':
77 case '@':
78 case '!':
79 if (*string == '(') /*)*/
80 return (1);
81 continue;
82
ccc6cda3
JA
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
cce855bc 94 that the glob library recognizes. If flags includes QGLOB_CVTNULL,
ccc6cda3
JA
95 we change quoted null strings (pathname[0] == CTLNUL) into empty
96 strings (pathname[0] == 0). If this is called after quote removal
cce855bc 97 is performed, (flags & QGLOB_CVTNULL) should be 0; if called when quote
ccc6cda3 98 removal has not been done (for example, before attempting to match a
cce855bc
JA
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. */
ccc6cda3 102char *
cce855bc 103quote_string_for_globbing (pathname, qflags)
ccc6cda3 104 char *pathname;
cce855bc 105 int qflags;
ccc6cda3
JA
106{
107 char *temp;
cce855bc 108 register int i, j;
ccc6cda3 109
cce855bc 110 temp = xmalloc (strlen (pathname) + 1);
ccc6cda3 111
cce855bc 112 if ((qflags & QGLOB_CVTNULL) && QUOTED_NULL (pathname))
ccc6cda3
JA
113 {
114 temp[0] = '\0';
115 return temp;
116 }
117
cce855bc 118 for (i = j = 0; pathname[i]; i++)
ccc6cda3 119 {
cce855bc
JA
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];
ccc6cda3 128 }
cce855bc 129 temp[j] = '\0';
ccc6cda3
JA
130
131 return (temp);
132}
133
134char *
135quote_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;
cce855bc
JA
152 case '+':
153 case '@':
154 case '!':
155 if (s[1] == '(') /*(*/
156 *t++ = '\\';
157 break;
ccc6cda3
JA
158 }
159 *t++ = *s++;
160 }
161 *t = '\0';
162 return temp;
163}
164
165/* Call the glob library to do globbing on PATHNAME. */
166char **
167shell_glob_filename (pathname)
168 char *pathname;
169{
170#if defined (USE_POSIX_GLOB_LIBRARY)
171 register int i;
172 char *temp, **return_value;
173 glob_t filenames;
174 int glob_flags;
175
cce855bc 176 temp = quote_string_for_globbing (pathname, QGLOB_FILENAME);
ccc6cda3
JA
177
178 filenames.gl_offs = 0;
179
b72432fd 180# if defined (GLOB_PERIOD)
ccc6cda3 181 glob_flags = glob_dot_filenames ? GLOB_PERIOD : 0;
b72432fd
JA
182# else
183 glob_flags = 0;
184# endif /* !GLOB_PERIOD */
185
ccc6cda3
JA
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_ABEND)
193 return ((char **)NULL);
b72432fd
JA
194 else if (i == GLOB_NOMATCH)
195 filenames.gl_pathv = (char **)NULL;
196 else if (i != 0) /* other error codes not in POSIX.2 */
cce855bc 197 filenames.gl_pathv = (char **)NULL;
ccc6cda3 198
bb70624e
JA
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);
ccc6cda3
JA
215
216#else /* !USE_POSIX_GLOB_LIBRARY */
217
218 char *temp, **results;
219
220 noglob_dot_filenames = glob_dot_filenames == 0;
221
cce855bc 222 temp = quote_string_for_globbing (pathname, QGLOB_FILENAME);
ccc6cda3
JA
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
246static 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 `.'. */
258void
259setup_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
273int
274should_ignore_glob_matches ()
275{
276 return globignore.num_ignores;
277}
278
279/* Return 0 if NAME matches a pattern in the globignore.ignores list. */
280static int
281glob_name_is_acceptable (name)
282 char *name;
283{
284 struct ign *p;
cce855bc 285 int flags;
ccc6cda3
JA
286
287 /* . and .. are never matched */
288 if (name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')))
289 return (0);
290
cce855bc 291 flags = FNM_PATHNAME | FNMATCH_EXTFLAG;
ccc6cda3
JA
292 for (p = globignore.ignores; p->val; p++)
293 {
cce855bc 294 if (fnmatch (p->val, name, flags) != FNM_NOMATCH)
ccc6cda3
JA
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
306static void
307ignore_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 ;
bb70624e 316 newnames = alloc_array (i + 1);
ccc6cda3
JA
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;
d166f048 340 free (newnames);
ccc6cda3
JA
341}
342
343void
344ignore_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
353void
354setup_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}