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