OSDN Git Service

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