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