blob: 90ef72d8405d106acd151ed321a4745d42038141 [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
Jari Aaltobb706242000-03-17 21:46:59 +000019 Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
Jari Aaltoccc6cda1996-12-23 17:02:34 +000020
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 Aalto28ef6c32001-04-06 19:14:31 +0000104 const 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)
Jari Aalto28ef6c32001-04-06 19:14:31 +0000121 {
122 if ((qflags & QGLOB_FILENAME) && pathname[i+1] == '/')
123 continue;
Jari Aaltocce855b1998-04-17 19:52:44 +0000124 temp[j++] = '\\';
Jari Aalto28ef6c32001-04-06 19:14:31 +0000125 }
Jari Aaltocce855b1998-04-17 19:52:44 +0000126 else
Jari Aalto28ef6c32001-04-06 19:14:31 +0000127 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)
Jari Aalto28ef6c32001-04-06 19:14:31 +0000144 {
145 case '*':
146 case '[':
147 case ']':
148 case '?':
149 case '\\':
150 *t++ = '\\';
151 break;
152 case '+':
153 case '@':
154 case '!':
Jari Aaltocce855b1998-04-17 19:52:44 +0000155 if (s[1] == '(') /*(*/
156 *t++ = '\\';
157 break;
Jari Aalto28ef6c32001-04-06 19:14:31 +0000158 }
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000159 *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)
Jari Aalto28ef6c32001-04-06 19:14:31 +0000168 const char *pathname;
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000169{
170#if defined (USE_POSIX_GLOB_LIBRARY)
171 register int i;
Jari Aalto28ef6c32001-04-06 19:14:31 +0000172 char *temp, **results;
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000173 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
Jari Aalto28ef6c32001-04-06 19:14:31 +0000192 if (i == GLOB_NOSPACE || i == GLOB_ABORTED)
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000193 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
Jari Aaltobb706242000-03-17 21:46:59 +0000199 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);
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000215
216#else /* !USE_POSIX_GLOB_LIBRARY */
217
218 char *temp, **results;
219
220 noglob_dot_filenames = glob_dot_filenames == 0;
221
Jari Aaltocce855b1998-04-17 19:52:44 +0000222 temp = quote_string_for_globbing (pathname, QGLOB_FILENAME);
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000223
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;
Jari Aaltocce855b1998-04-17 19:52:44 +0000285 int flags;
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000286
287 /* . and .. are never matched */
288 if (name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')))
289 return (0);
290
Jari Aaltocce855b1998-04-17 19:52:44 +0000291 flags = FNM_PATHNAME | FNMATCH_EXTFLAG;
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000292 for (p = globignore.ignores; p->val; p++)
293 {
Jari Aaltocce855b1998-04-17 19:52:44 +0000294 if (fnmatch (p->val, name, flags) != FNM_NOMATCH)
Jari Aalto28ef6c32001-04-06 19:14:31 +0000295 return (0);
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000296 }
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 ;
Jari Aaltobb706242000-03-17 21:46:59 +0000316 newnames = alloc_array (i + 1);
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000317
318 for (n = i = 0; names[i]; i++)
319 {
320 if ((*name_func) (names[i]))
Jari Aalto28ef6c32001-04-06 19:14:31 +0000321 newnames[n++] = names[i];
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000322 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;
Jari Aaltod166f041997-06-05 14:59:13 +0000340 free (newnames);
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000341}
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)
Jari Aalto28ef6c32001-04-06 19:14:31 +0000403 (*ivp->item_func) (&ivp->ignores[numitems]);
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000404 numitems++;
405 }
406 ivp->ignores[numitems].val = (char *)NULL;
407 ivp->num_ignores = numitems;
408}