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