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