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