]> git.ipfire.org Git - thirdparty/bash.git/blame - pathexp.c
Imported from ../bash-2.03.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
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>
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
JA
198
199 return (filenames.gl_pathv);
200
201#else /* !USE_POSIX_GLOB_LIBRARY */
202
203 char *temp, **results;
204
205 noglob_dot_filenames = glob_dot_filenames == 0;
206
cce855bc 207 temp = quote_string_for_globbing (pathname, QGLOB_FILENAME);
ccc6cda3
JA
208
209 results = glob_filename (temp);
210 free (temp);
211
212 if (results && ((GLOB_FAILED (results)) == 0))
213 {
214 if (should_ignore_glob_matches ())
215 ignore_glob_matches (results);
216 if (results && results[0])
217 sort_char_array (results);
218 else
219 {
220 FREE (results);
221 results = (char **)&glob_error_return;
222 }
223 }
224
225 return (results);
226#endif /* !USE_POSIX_GLOB_LIBRARY */
227}
228
229/* Stuff for GLOBIGNORE. */
230
231static struct ignorevar globignore =
232{
233 "GLOBIGNORE",
234 (struct ign *)0,
235 0,
236 (char *)0,
237 (Function *)0,
238};
239
240/* Set up to ignore some glob matches because the value of GLOBIGNORE
241 has changed. If GLOBIGNORE is being unset, we also need to disable
242 the globbing of filenames beginning with a `.'. */
243void
244setup_glob_ignore (name)
245 char *name;
246{
247 char *v;
248
249 v = get_string_value (name);
250 setup_ignore_patterns (&globignore);
251
252 if (globignore.num_ignores)
253 glob_dot_filenames = 1;
254 else if (v == 0)
255 glob_dot_filenames = 0;
256}
257
258int
259should_ignore_glob_matches ()
260{
261 return globignore.num_ignores;
262}
263
264/* Return 0 if NAME matches a pattern in the globignore.ignores list. */
265static int
266glob_name_is_acceptable (name)
267 char *name;
268{
269 struct ign *p;
cce855bc 270 int flags;
ccc6cda3
JA
271
272 /* . and .. are never matched */
273 if (name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')))
274 return (0);
275
cce855bc 276 flags = FNM_PATHNAME | FNMATCH_EXTFLAG;
ccc6cda3
JA
277 for (p = globignore.ignores; p->val; p++)
278 {
cce855bc 279 if (fnmatch (p->val, name, flags) != FNM_NOMATCH)
ccc6cda3
JA
280 return (0);
281 }
282 return (1);
283}
284
285/* Internal function to test whether filenames in NAMES should be
286 ignored. NAME_FUNC is a pointer to a function to call with each
287 name. It returns non-zero if the name is acceptable to the particular
288 ignore function which called _ignore_names; zero if the name should
289 be removed from NAMES. */
290
291static void
292ignore_globbed_names (names, name_func)
293 char **names;
294 Function *name_func;
295{
296 char **newnames;
297 int n, i;
298
299 for (i = 0; names[i]; i++)
300 ;
301 newnames = (char **)xmalloc ((i + 1) * sizeof (char *));
302
303 for (n = i = 0; names[i]; i++)
304 {
305 if ((*name_func) (names[i]))
306 newnames[n++] = names[i];
307 else
308 free (names[i]);
309 }
310
311 newnames[n] = (char *)NULL;
312
313 if (n == 0)
314 {
315 names[0] = (char *)NULL;
316 free (newnames);
317 return;
318 }
319
320 /* Copy the acceptable names from NEWNAMES back to NAMES and set the
321 new array end. */
322 for (n = 0; newnames[n]; n++)
323 names[n] = newnames[n];
324 names[n] = (char *)NULL;
d166f048 325 free (newnames);
ccc6cda3
JA
326}
327
328void
329ignore_glob_matches (names)
330 char **names;
331{
332 if (globignore.num_ignores == 0)
333 return;
334
335 ignore_globbed_names (names, glob_name_is_acceptable);
336}
337
338void
339setup_ignore_patterns (ivp)
340 struct ignorevar *ivp;
341{
342 int numitems, maxitems, ptr;
343 char *colon_bit, *this_ignoreval;
344 struct ign *p;
345
346 this_ignoreval = get_string_value (ivp->varname);
347
348 /* If nothing has changed then just exit now. */
349 if ((this_ignoreval && ivp->last_ignoreval && STREQ (this_ignoreval, ivp->last_ignoreval)) ||
350 (!this_ignoreval && !ivp->last_ignoreval))
351 return;
352
353 /* Oops. The ignore variable has changed. Re-parse it. */
354 ivp->num_ignores = 0;
355
356 if (ivp->ignores)
357 {
358 for (p = ivp->ignores; p->val; p++)
359 free(p->val);
360 free (ivp->ignores);
361 ivp->ignores = (struct ign *)NULL;
362 }
363
364 if (ivp->last_ignoreval)
365 {
366 free (ivp->last_ignoreval);
367 ivp->last_ignoreval = (char *)NULL;
368 }
369
370 if (this_ignoreval == 0 || *this_ignoreval == '\0')
371 return;
372
373 ivp->last_ignoreval = savestring (this_ignoreval);
374
375 numitems = maxitems = ptr = 0;
376
377 while (colon_bit = extract_colon_unit (this_ignoreval, &ptr))
378 {
379 if (numitems + 1 >= maxitems)
380 {
381 maxitems += 10;
382 ivp->ignores = (struct ign *)xrealloc (ivp->ignores, maxitems * sizeof (struct ign));
383 }
384 ivp->ignores[numitems].val = colon_bit;
385 ivp->ignores[numitems].len = strlen (colon_bit);
386 ivp->ignores[numitems].flags = 0;
387 if (ivp->item_func)
388 (*ivp->item_func) (&ivp->ignores[numitems]);
389 numitems++;
390 }
391 ivp->ignores[numitems].val = (char *)NULL;
392 ivp->num_ignores = numitems;
393}