blob: 3c02effc66943f2d53faeaa12a20828825ef40b9 [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>
Jari Aaltob72432f1999-02-19 17:11:39 +000037
38#if defined (USE_POSIX_GLOB_LIBRARY)
39# include <glob.h>
40#else
41# include <glob/glob.h>
42#endif
Jari Aaltoccc6cda1996-12-23 17:02:34 +000043
44/* Control whether * matches .files in globbing. */
45int glob_dot_filenames;
46
Jari Aaltocce855b1998-04-17 19:52:44 +000047/* Control whether the extended globbing features are enabled. */
48int extended_glob = 0;
49
Jari Aaltoccc6cda1996-12-23 17:02:34 +000050/* 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
Jari Aaltocce855b1998-04-17 19:52:44 +000076 case '+':
77 case '@':
78 case '!':
79 if (*string == '(') /*)*/
80 return (1);
81 continue;
82
Jari Aaltoccc6cda1996-12-23 17:02:34 +000083 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
Jari Aaltocce855b1998-04-17 19:52:44 +000094 that the glob library recognizes. If flags includes QGLOB_CVTNULL,
Jari Aaltoccc6cda1996-12-23 17:02:34 +000095 we change quoted null strings (pathname[0] == CTLNUL) into empty
96 strings (pathname[0] == 0). If this is called after quote removal
Jari Aaltocce855b1998-04-17 19:52:44 +000097 is performed, (flags & QGLOB_CVTNULL) should be 0; if called when quote
Jari Aaltoccc6cda1996-12-23 17:02:34 +000098 removal has not been done (for example, before attempting to match a
Jari Aaltocce855b1998-04-17 19:52:44 +000099 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. */
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000102char *
Jari Aaltocce855b1998-04-17 19:52:44 +0000103quote_string_for_globbing (pathname, qflags)
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000104 char *pathname;
Jari Aaltocce855b1998-04-17 19:52:44 +0000105 int qflags;
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000106{
107 char *temp;
Jari Aaltocce855b1998-04-17 19:52:44 +0000108 register int i, j;
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000109
Jari Aaltocce855b1998-04-17 19:52:44 +0000110 temp = xmalloc (strlen (pathname) + 1);
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000111
Jari Aaltocce855b1998-04-17 19:52:44 +0000112 if ((qflags & QGLOB_CVTNULL) && QUOTED_NULL (pathname))
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000113 {
114 temp[0] = '\0';
115 return temp;
116 }
117
Jari Aaltocce855b1998-04-17 19:52:44 +0000118 for (i = j = 0; pathname[i]; i++)
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000119 {
Jari Aaltocce855b1998-04-17 19:52:44 +0000120 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];
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000128 }
Jari Aaltocce855b1998-04-17 19:52:44 +0000129 temp[j] = '\0';
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000130
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;
Jari Aaltocce855b1998-04-17 19:52:44 +0000152 case '+':
153 case '@':
154 case '!':
155 if (s[1] == '(') /*(*/
156 *t++ = '\\';
157 break;
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000158 }
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
Jari Aaltocce855b1998-04-17 19:52:44 +0000176 temp = quote_string_for_globbing (pathname, QGLOB_FILENAME);
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000177
178 filenames.gl_offs = 0;
179
Jari Aaltob72432f1999-02-19 17:11:39 +0000180# if defined (GLOB_PERIOD)
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000181 glob_flags = glob_dot_filenames ? GLOB_PERIOD : 0;
Jari Aaltob72432f1999-02-19 17:11:39 +0000182# else
183 glob_flags = 0;
184# endif /* !GLOB_PERIOD */
185
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000186 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);
Jari Aaltob72432f1999-02-19 17:11:39 +0000194 else if (i == GLOB_NOMATCH)
195 filenames.gl_pathv = (char **)NULL;
196 else if (i != 0) /* other error codes not in POSIX.2 */
Jari Aaltocce855b1998-04-17 19:52:44 +0000197 filenames.gl_pathv = (char **)NULL;
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000198
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
Jari Aaltocce855b1998-04-17 19:52:44 +0000207 temp = quote_string_for_globbing (pathname, QGLOB_FILENAME);
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000208
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;
Jari Aaltocce855b1998-04-17 19:52:44 +0000270 int flags;
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000271
272 /* . and .. are never matched */
273 if (name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')))
274 return (0);
275
Jari Aaltocce855b1998-04-17 19:52:44 +0000276 flags = FNM_PATHNAME | FNMATCH_EXTFLAG;
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000277 for (p = globignore.ignores; p->val; p++)
278 {
Jari Aaltocce855b1998-04-17 19:52:44 +0000279 if (fnmatch (p->val, name, flags) != FNM_NOMATCH)
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000280 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;
Jari Aaltod166f041997-06-05 14:59:13 +0000325 free (newnames);
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000326}
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}