640451494c8ae942bfb13928708d79604db87d35
[platform/upstream/bash.git] / pathexp.c
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. */
40 int glob_dot_filenames;
41
42 /* Return nonzero if STRING has any unquoted special globbing chars in it.  */
43 int
44 unquoted_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. */
86 char *
87 quote_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
111 char *
112 quote_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. */
137 char **
138 shell_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
196 static 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 `.'. */
208 void
209 setup_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
223 int
224 should_ignore_glob_matches ()
225 {
226   return globignore.num_ignores;
227 }
228
229 /* Return 0 if NAME matches a pattern in the globignore.ignores list. */
230 static int
231 glob_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
254 static void
255 ignore_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;
288 }
289
290 void
291 ignore_glob_matches (names)
292      char **names;
293 {
294   if (globignore.num_ignores == 0)
295     return;
296
297   ignore_globbed_names (names, glob_name_is_acceptable);
298 }
299
300 void
301 setup_ignore_patterns (ivp)
302      struct ignorevar *ivp;
303 {
304   int numitems, maxitems, ptr;
305   char *colon_bit, *this_ignoreval;
306   struct ign *p;
307
308   this_ignoreval = get_string_value (ivp->varname);
309
310   /* If nothing has changed then just exit now. */
311   if ((this_ignoreval && ivp->last_ignoreval && STREQ (this_ignoreval, ivp->last_ignoreval)) ||
312       (!this_ignoreval && !ivp->last_ignoreval))
313     return;
314
315   /* Oops.  The ignore variable has changed.  Re-parse it. */
316   ivp->num_ignores = 0;
317
318   if (ivp->ignores)
319     {
320       for (p = ivp->ignores; p->val; p++)
321         free(p->val);
322       free (ivp->ignores);
323       ivp->ignores = (struct ign *)NULL;
324     }
325
326   if (ivp->last_ignoreval)
327     {
328       free (ivp->last_ignoreval);
329       ivp->last_ignoreval = (char *)NULL;
330     }
331
332   if (this_ignoreval == 0 || *this_ignoreval == '\0')
333     return;
334
335   ivp->last_ignoreval = savestring (this_ignoreval);
336
337   numitems = maxitems = ptr = 0;
338
339   while (colon_bit = extract_colon_unit (this_ignoreval, &ptr))
340     {
341       if (numitems + 1 >= maxitems)
342         {
343           maxitems += 10;
344           ivp->ignores = (struct ign *)xrealloc (ivp->ignores, maxitems * sizeof (struct ign));
345         }
346       ivp->ignores[numitems].val = colon_bit;
347       ivp->ignores[numitems].len = strlen (colon_bit);
348       ivp->ignores[numitems].flags = 0;
349       if (ivp->item_func)
350         (*ivp->item_func) (&ivp->ignores[numitems]);
351       numitems++;
352     }
353   ivp->ignores[numitems].val = (char *)NULL;
354   ivp->num_ignores = numitems;
355 }