aae09a7051fead8bac8d014f4e3a30814935a583
[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, 59 Temple Place, Suite 330, Boston, MA 02111 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/strmatch.h>
37
38 #if defined (USE_POSIX_GLOB_LIBRARY)
39 #  include <glob.h>
40 typedef int posix_glob_errfunc_t __P((const char *, int));
41 #else
42 #  include <glob/glob.h>
43 #endif
44
45 /* Control whether * matches .files in globbing. */
46 int glob_dot_filenames;
47
48 /* Control whether the extended globbing features are enabled. */
49 int extended_glob = 0;
50
51 /* Return nonzero if STRING has any unquoted special globbing chars in it.  */
52 int
53 unquoted_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
77         case '+':
78         case '@':
79         case '!':
80           if (*string == '(')   /*)*/
81             return (1);
82           continue;
83
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
95    that the glob library recognizes.  If flags includes QGLOB_CVTNULL,
96    we change quoted null strings (pathname[0] == CTLNUL) into empty
97    strings (pathname[0] == 0).  If this is called after quote removal
98    is performed, (flags & QGLOB_CVTNULL) should be 0; if called when quote
99    removal has not been done (for example, before attempting to match a
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. */
103 char *
104 quote_string_for_globbing (pathname, qflags)
105      const char *pathname;
106      int qflags;
107 {
108   char *temp;
109   register int i, j;
110
111   temp = (char *)xmalloc (strlen (pathname) + 1);
112
113   if ((qflags & QGLOB_CVTNULL) && QUOTED_NULL (pathname))
114     {
115       temp[0] = '\0';
116       return temp;
117     }
118
119   for (i = j = 0; pathname[i]; i++)
120     {
121       if (pathname[i] == CTLESC)
122         {
123           if ((qflags & QGLOB_FILENAME) && pathname[i+1] == '/')
124             continue;
125           temp[j++] = '\\';
126         }
127       else
128         temp[j++] = pathname[i];
129     }
130   temp[j] = '\0';
131
132   return (temp);
133 }
134
135 char *
136 quote_globbing_chars (string)
137      char *string;
138 {
139   char *temp, *s, *t;
140
141   temp = (char *)xmalloc (strlen (string) * 2 + 1);
142   for (t = temp, s = string; *s; )
143     {
144       switch (*s)
145         {
146         case '*':
147         case '[':
148         case ']':
149         case '?':
150         case '\\':
151           *t++ = '\\';
152           break;
153         case '+':
154         case '@':
155         case '!':
156           if (s[1] == '(')      /*(*/
157             *t++ = '\\';
158           break;
159         }
160       *t++ = *s++;
161     }
162   *t = '\0';
163   return temp;
164 }
165
166 /* Call the glob library to do globbing on PATHNAME. */
167 char **
168 shell_glob_filename (pathname)
169      const char *pathname;
170 {
171 #if defined (USE_POSIX_GLOB_LIBRARY)
172   register int i;
173   char *temp, **results;
174   glob_t filenames;
175   int glob_flags;
176
177   temp = quote_string_for_globbing (pathname, QGLOB_FILENAME);
178
179   filenames.gl_offs = 0;
180
181 #  if defined (GLOB_PERIOD)
182   glob_flags = glob_dot_filenames ? GLOB_PERIOD : 0;
183 #  else
184   glob_flags = 0;
185 #  endif /* !GLOB_PERIOD */
186
187   glob_flags |= (GLOB_ERR | GLOB_DOOFFS);
188
189   i = glob (temp, glob_flags, (posix_glob_errfunc_t *)NULL, &filenames);
190
191   free (temp);
192
193   if (i == GLOB_NOSPACE || i == GLOB_ABORTED)
194     return ((char **)NULL);
195   else if (i == GLOB_NOMATCH)
196     filenames.gl_pathv = (char **)NULL;
197   else if (i != 0)              /* other error codes not in POSIX.2 */
198     filenames.gl_pathv = (char **)NULL;
199
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);
216
217 #else /* !USE_POSIX_GLOB_LIBRARY */
218
219   char *temp, **results;
220
221   noglob_dot_filenames = glob_dot_filenames == 0;
222
223   temp = quote_string_for_globbing (pathname, QGLOB_FILENAME);
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
247 static struct ignorevar globignore =
248 {
249   "GLOBIGNORE",
250   (struct ign *)0,
251   0,
252   (char *)0,
253   (sh_iv_item_func_t *)0,
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 `.'. */
259 void
260 setup_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
274 int
275 should_ignore_glob_matches ()
276 {
277   return globignore.num_ignores;
278 }
279
280 /* Return 0 if NAME matches a pattern in the globignore.ignores list. */
281 static int
282 glob_name_is_acceptable (name)
283      const char *name;
284 {
285   struct ign *p;
286   int flags;
287
288   /* . and .. are never matched */
289   if (name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')))
290     return (0);
291
292   flags = FNM_PATHNAME | FNMATCH_EXTFLAG;
293   for (p = globignore.ignores; p->val; p++)
294     {
295       if (strmatch (p->val, (char *)name, flags) != FNM_NOMATCH)
296         return (0);
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
307 static void
308 ignore_globbed_names (names, name_func)
309      char **names;
310      sh_ignore_func_t *name_func;
311 {
312   char **newnames;
313   int n, i;
314
315   for (i = 0; names[i]; i++)
316     ;
317   newnames = alloc_array (i + 1);
318
319   for (n = i = 0; names[i]; i++)
320     {
321       if ((*name_func) (names[i]))
322         newnames[n++] = names[i];
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;
341   free (newnames);
342 }
343
344 void
345 ignore_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
354 void
355 setup_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)
404         (*ivp->item_func) (&ivp->ignores[numitems]);
405       numitems++;
406     }
407   ivp->ignores[numitems].val = (char *)NULL;
408   ivp->num_ignores = numitems;
409 }