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