msm related macros are defined in post scrpit of rpm-security-plugin
[platform/upstream/rpm.git] / rpmio / rpmglob.c
1 #include "system.h"
2
3 /* Copyright (C) 1991,92,93,94,95,96,97,98,99 Free Software Foundation, Inc.
4
5    This library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Library General Public License as
7    published by the Free Software Foundation; either version 2 of the
8    License, or (at your option) any later version.
9
10    This library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Library General Public License for more details.
14
15    You should have received a copy of the GNU Library General Public
16    License along with this library; see the file COPYING.LIB.  If not,
17    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18    Boston, MA 02111-1307, USA.  */
19
20 /* AIX requires this to be the first thing in the file.  */
21 #if defined _AIX && !defined __GNUC__
22 #pragma alloca
23 #endif
24
25 #include "system.h"
26
27 #include <stdlib.h>
28 #include <string.h>
29 #include <pwd.h>
30 #include <assert.h>
31 #include <sys/stat.h>           /* S_ISDIR */
32
33 /* Bits set in the FLAGS argument to `glob'.  */
34 #define GLOB_ERR        (1 << 0)        /* Return on read errors.  */
35 #define GLOB_MARK       (1 << 1)        /* Append a slash to each name.  */
36 #define GLOB_NOSORT     (1 << 2)        /* Don't sort the names.  */
37 #define GLOB_DOOFFS     (1 << 3)        /* Insert PGLOB->gl_offs NULLs.  */
38 #define GLOB_NOCHECK    (1 << 4)        /* If nothing matches, return the pattern.  */
39 #define GLOB_APPEND     (1 << 5)        /* Append to results of a previous call.  */
40 #define GLOB_NOESCAPE   (1 << 6)        /* Backslashes don't quote metacharacters.  */
41 #define GLOB_PERIOD     (1 << 7)        /* Leading `.' can be matched by metachars.  */
42
43 #define GLOB_MAGCHAR     (1 << 8)       /* Set in gl_flags if any metachars seen.  */
44 #define GLOB_ALTDIRFUNC (1 << 9)        /* Use gl_opendir et al functions.  */
45 #define GLOB_BRACE       (1 << 10)      /* Expand "{a,b}" to "a" "b".  */
46 #define GLOB_NOMAGIC     (1 << 11)      /* If no magic chars, return the pattern.  */
47 #define GLOB_TILDE       (1 << 12)      /* Expand ~user and ~ to home directories. */
48 #define GLOB_ONLYDIR     (1 << 13)      /* Match only directories.  */
49 #define GLOB_TILDE_CHECK (1 << 14)      /* Like GLOB_TILDE but return an error
50                                            if the user name is not available.  */
51 #define __GLOB_FLAGS    (GLOB_ERR|GLOB_MARK|GLOB_NOSORT|GLOB_DOOFFS| \
52                          GLOB_NOESCAPE|GLOB_NOCHECK|GLOB_APPEND|     \
53                          GLOB_PERIOD|GLOB_ALTDIRFUNC|GLOB_BRACE|     \
54                          GLOB_NOMAGIC|GLOB_TILDE|GLOB_ONLYDIR|GLOB_TILDE_CHECK)
55
56 /* Error returns from `glob'.  */
57 #define GLOB_NOSPACE    1       /* Ran out of memory.  */
58 #define GLOB_ABORTED    2       /* Read error.  */
59 #define GLOB_NOMATCH    3       /* No matches found.  */
60 #define GLOB_NOSYS      4       /* Not implemented.  */
61
62 /* Structure describing a globbing run.  */
63 typedef struct {
64     size_t gl_pathc;            /* Count of paths matched by the pattern.  */
65     char **gl_pathv;            /* List of matched pathnames.  */
66     size_t gl_offs;             /* Slots to reserve in `gl_pathv'.  */
67     int gl_flags;               /* Set to FLAGS, maybe | GLOB_MAGCHAR.  */
68
69     /* If the GLOB_ALTDIRFUNC flag is set, the following functions
70        are used instead of the normal file access functions.  */
71     void (*gl_closedir)(void *);
72     struct dirent *(*gl_readdir)(void *);
73     void *(*gl_opendir)(const char *);
74     int (*gl_lstat)(const char *, struct stat *);
75     int (*gl_stat)(const char *, struct stat *);
76 } glob_t;
77
78 #define NAMLEN(_d)      NLENGTH(_d)
79
80 #if (defined POSIX || defined WINDOWS32) && !defined __GNU_LIBRARY__
81 /* Posix does not require that the d_ino field be present, and some
82    systems do not provide it. */
83 #define REAL_DIR_ENTRY(dp) 1
84 #else
85 #define REAL_DIR_ENTRY(dp) (dp->d_ino != 0)
86 #endif                          /* POSIX */
87
88 #include <errno.h>
89 #ifndef __set_errno
90 #define __set_errno(val) errno = (val)
91 #endif
92
93 #include <popt.h>
94 #include <rpm/rpmfileutil.h>
95 #include <rpm/rpmurl.h>
96
97 #include "debug.h"
98
99 /* Outcomment the following line for production quality code.  */
100 /* #define NDEBUG 1 */
101
102 #define GLOB_INTERFACE_VERSION 1
103
104 static void globfree(glob_t * pglob);
105 static inline const char *next_brace_sub(const char *begin);
106 static int glob_in_dir(const char *pattern, const char *directory,
107                             int flags,
108                             int (*errfunc) (const char *, int),
109                             glob_t * pglob);
110 static int prefix_array(const char *prefix, char **array, size_t n);
111 static int collated_compare(const void *, const void *);
112
113 #ifndef HAVE_MEMPCPY
114 static void * mempcpy(void *dest, const void *src, size_t n)
115 {
116     return (char *) memcpy(dest, src, n) + n;
117 }
118 #endif
119
120 /* Find the end of the sub-pattern in a brace expression.  We define
121    this as an inline function if the compiler permits.  */
122 static inline const char *next_brace_sub(const char *begin)
123 {
124     unsigned int depth = 0;
125     const char *cp = begin;
126
127     while (1) {
128         if (depth == 0) {
129             if (*cp != ',' && *cp != '}' && *cp != '\0') {
130                 if (*cp == '{')
131                     ++depth;
132                 ++cp;
133                 continue;
134             }
135         } else {
136             while (*cp != '\0' && (*cp != '}' || depth > 0)) {
137                 if (*cp == '}')
138                     --depth;
139                 ++cp;
140             }
141             if (*cp == '\0')
142                 /* An incorrectly terminated brace expression.  */
143                 return NULL;
144
145             continue;
146         }
147         break;
148     }
149
150     return cp;
151 }
152
153 static int __glob_pattern_p(const char *pattern, int quote);
154
155 /* Do glob searching for PATTERN, placing results in PGLOB.
156    The bits defined above may be set in FLAGS.
157    If a directory cannot be opened or read and ERRFUNC is not nil,
158    it is called with the pathname that caused the error, and the
159    `errno' value from the failing call; if it returns non-zero
160    `glob' returns GLOB_ABORTED; if it returns zero, the error is ignored.
161    If memory cannot be allocated for PGLOB, GLOB_NOSPACE is returned.
162    Otherwise, `glob' returns zero.  */
163 static int
164 glob(const char *pattern, int flags,
165      int (*errfunc)(const char *, int), glob_t * pglob)
166 {
167     const char *filename;
168     const char *dirname;
169     size_t dirlen;
170     int status;
171     int oldcount;
172
173     if (pattern == NULL || pglob == NULL || (flags & ~__GLOB_FLAGS) != 0) {
174         __set_errno(EINVAL);
175         return -1;
176     }
177
178     if (flags & GLOB_BRACE) {
179         const char *begin = strchr(pattern, '{');
180         if (begin != NULL) {
181             /* Allocate working buffer large enough for our work.  Note that
182                we have at least an opening and closing brace.  */
183             int firstc;
184             char *alt_start;
185             const char *p;
186             const char *next;
187             const char *rest;
188             size_t rest_len;
189             char onealt[strlen(pattern) - 1];
190
191             /* We know the prefix for all sub-patterns.  */
192             alt_start = mempcpy(onealt, pattern, begin - pattern);
193
194             /* Find the first sub-pattern and at the same time find the
195                rest after the closing brace.  */
196             next = next_brace_sub(begin + 1);
197             if (next == NULL) {
198                 /* It is an illegal expression.  */
199                 return glob(pattern, flags & ~GLOB_BRACE, errfunc, pglob);
200             }
201
202             /* Now find the end of the whole brace expression.  */
203             rest = next;
204             while (*rest != '}') {
205                 rest = next_brace_sub(rest + 1);
206                 if (rest == NULL) {
207                     /* It is an illegal expression.  */
208                     return glob(pattern, flags & ~GLOB_BRACE, errfunc,
209                                 pglob);
210                 }
211             }
212             /* Please note that we now can be sure the brace expression
213                is well-formed.  */
214             rest_len = strlen(++rest) + 1;
215
216             /* We have a brace expression.  BEGIN points to the opening {,
217                NEXT points past the terminator of the first element, and END
218                points past the final }.  We will accumulate result names from
219                recursive runs for each brace alternative in the buffer using
220                GLOB_APPEND.  */
221
222             if (!(flags & GLOB_APPEND)) {
223                 /* This call is to set a new vector, so clear out the
224                    vector so we can append to it.  */
225                 pglob->gl_pathc = 0;
226                 pglob->gl_pathv = NULL;
227             }
228             firstc = pglob->gl_pathc;
229
230             p = begin + 1;
231             while (1) {
232                 int result;
233
234                 /* Construct the new glob expression.  */
235                 mempcpy(mempcpy(alt_start, p, next - p), rest, rest_len);
236
237                 result = glob(onealt,
238                               ((flags & ~(GLOB_NOCHECK | GLOB_NOMAGIC))
239                                | GLOB_APPEND), errfunc, pglob);
240
241                 /* If we got an error, return it.  */
242                 if (result && result != GLOB_NOMATCH) {
243                     if (!(flags & GLOB_APPEND))
244                         globfree(pglob);
245                     return result;
246                 }
247
248                 if (*next == '}')
249                     /* We saw the last entry.  */
250                     break;
251
252                 p = next + 1;
253                 next = next_brace_sub(p);
254                 assert(next != NULL);
255             }
256
257             if (pglob->gl_pathc != firstc)
258                 /* We found some entries.  */
259                 return 0;
260             else if (!(flags & (GLOB_NOCHECK | GLOB_NOMAGIC)))
261                 return GLOB_NOMATCH;
262         }
263     }
264
265     /* Find the filename.  */
266     filename = strrchr(pattern, '/');
267     if (filename == NULL) {
268         /* This can mean two things: a simple name or "~name".  The latter
269            case is nothing but a notation for a directory.  */
270         if ((flags & (GLOB_TILDE | GLOB_TILDE_CHECK)) && pattern[0] == '~') {
271             dirname = pattern;
272             dirlen = strlen(pattern);
273
274             /* Set FILENAME to NULL as a special flag.  This is ugly but
275                other solutions would require much more code.  We test for
276                this special case below.  */
277             filename = NULL;
278         } else {
279             filename = pattern;
280             dirname = ".";
281             dirlen = 0;
282         }
283     } else if (filename == pattern) {
284         /* "/pattern".  */
285         dirname = "/";
286         dirlen = 1;
287         ++filename;
288     } else {
289         char *newp;
290         dirlen = filename - pattern;
291         newp = (char *) alloca(dirlen + 1);
292         *((char *) mempcpy(newp, pattern, dirlen)) = '\0';
293         dirname = newp;
294         ++filename;
295
296         if (filename[0] == '\0' && dirlen > 1) {
297             /* "pattern/".  Expand "pattern", appending slashes.  */
298             int val = glob(dirname, flags | GLOB_MARK, errfunc, pglob);
299             if (val == 0)
300                 pglob->gl_flags = ((pglob->gl_flags & ~GLOB_MARK)
301                                    | (flags & GLOB_MARK));
302             return val;
303         }
304     }
305
306     if (!(flags & GLOB_APPEND)) {
307         pglob->gl_pathc = 0;
308         pglob->gl_pathv = NULL;
309     }
310
311     oldcount = pglob->gl_pathc;
312
313     if ((flags & (GLOB_TILDE | GLOB_TILDE_CHECK)) && dirname[0] == '~') {
314         if (dirname[1] == '\0' || dirname[1] == '/') {
315             /* Look up home directory.  */
316             const char *home_dir = getenv("HOME");
317             if (home_dir == NULL || home_dir[0] == '\0') {
318                 int success;
319                 char *name;
320                 success = (name = getlogin()) != NULL;
321                 if (success) {
322                     struct passwd *p;
323                     p = getpwnam(name);
324                     if (p != NULL)
325                         home_dir = p->pw_dir;
326                 }
327             }
328             if (home_dir == NULL || home_dir[0] == '\0') {
329                 if (flags & GLOB_TILDE_CHECK)
330                     return GLOB_NOMATCH;
331                 else
332                     home_dir = "~";     /* No luck.  */
333             }
334             /* Now construct the full directory.  */
335             if (dirname[1] == '\0')
336                 dirname = home_dir;
337             else {
338                 char *newp;
339                 size_t home_len = strlen(home_dir);
340                 newp = (char *) alloca(home_len + dirlen);
341                 mempcpy(mempcpy(newp, home_dir, home_len),
342                         &dirname[1], dirlen);
343                 dirname = newp;
344             }
345         }
346         else {
347             char *end_name = strchr(dirname, '/');
348             const char *user_name;
349             const char *home_dir;
350
351             if (end_name == NULL)
352                 user_name = dirname + 1;
353             else {
354                 char *newp;
355                 newp = (char *) alloca(end_name - dirname);
356                 *((char *) mempcpy(newp, dirname + 1, end_name - dirname))
357                     = '\0';
358                 user_name = newp;
359             }
360
361             /* Look up specific user's home directory.  */
362             {
363                 struct passwd *p;
364                 p = getpwnam(user_name);
365                 if (p != NULL)
366                     home_dir = p->pw_dir;
367                 else
368                     home_dir = NULL;
369             }
370             /* If we found a home directory use this.  */
371             if (home_dir != NULL) {
372                 char *newp;
373                 size_t home_len = strlen(home_dir);
374                 size_t rest_len = end_name == NULL ? 0 : strlen(end_name);
375                 newp = (char *) alloca(home_len + rest_len + 1);
376                 *((char *) mempcpy(mempcpy(newp, home_dir, home_len),
377                                    end_name, rest_len)) = '\0';
378                 dirname = newp;
379             } else if (flags & GLOB_TILDE_CHECK)
380                 /* We have to regard it as an error if we cannot find the
381                    home directory.  */
382                 return GLOB_NOMATCH;
383         }
384     }
385
386     /* Now test whether we looked for "~" or "~NAME".  In this case we
387        can give the answer now.  */
388     if (filename == NULL) {
389         struct stat st;
390
391         /* Return the directory if we don't check for error or if it exists.  */
392         if ((flags & GLOB_NOCHECK)
393             || (((flags & GLOB_ALTDIRFUNC)
394                  ? (*pglob->gl_stat) (dirname, &st)
395                  : stat(dirname, &st)) == 0 && S_ISDIR(st.st_mode))) {
396             pglob->gl_pathv
397                 = (char **) xrealloc(pglob->gl_pathv,
398                                      (pglob->gl_pathc +
399                                       ((flags & GLOB_DOOFFS) ?
400                                        pglob->gl_offs : 0) +
401                                       1 + 1) * sizeof(char *));
402
403             if (flags & GLOB_DOOFFS)
404                 while (pglob->gl_pathc < pglob->gl_offs)
405                     pglob->gl_pathv[pglob->gl_pathc++] = NULL;
406
407             pglob->gl_pathv[pglob->gl_pathc] = xstrdup(dirname);
408             if (pglob->gl_pathv[pglob->gl_pathc] == NULL) {
409                 free(pglob->gl_pathv);
410                 return GLOB_NOSPACE;
411             }
412             pglob->gl_pathv[++pglob->gl_pathc] = NULL;
413             pglob->gl_flags = flags;
414
415             return 0;
416         }
417
418         /* Not found.  */
419         return GLOB_NOMATCH;
420     }
421
422     if (__glob_pattern_p(dirname, !(flags & GLOB_NOESCAPE))) {
423         /* The directory name contains metacharacters, so we
424            have to glob for the directory, and then glob for
425            the pattern in each directory found.  */
426         glob_t dirs;
427         register int i;
428
429         if ((flags & GLOB_ALTDIRFUNC) != 0) {
430             /* Use the alternative access functions also in the recursive
431                call.  */
432             dirs.gl_opendir = pglob->gl_opendir;
433             dirs.gl_readdir = pglob->gl_readdir;
434             dirs.gl_closedir = pglob->gl_closedir;
435             dirs.gl_stat = pglob->gl_stat;
436             dirs.gl_lstat = pglob->gl_lstat;
437         }
438
439         status = glob(dirname,
440                       ((flags & (GLOB_ERR | GLOB_NOCHECK | GLOB_NOESCAPE
441                                  | GLOB_ALTDIRFUNC))
442                        | GLOB_NOSORT | GLOB_ONLYDIR), errfunc, &dirs);
443         if (status != 0)
444             return status;
445
446         /* We have successfully globbed the preceding directory name.
447            For each name we found, call glob_in_dir on it and FILENAME,
448            appending the results to PGLOB.  */
449         for (i = 0; i < dirs.gl_pathc; ++i) {
450             int old_pathc = pglob->gl_pathc;
451             status = glob_in_dir(filename, dirs.gl_pathv[i],
452                                  ((flags | GLOB_APPEND)
453                                   & ~(GLOB_NOCHECK | GLOB_ERR)),
454                                  errfunc, pglob);
455             if (status == GLOB_NOMATCH)
456                 /* No matches in this directory.  Try the next.  */
457                 continue;
458
459             if (status != 0) {
460                 globfree(&dirs);
461                 globfree(pglob);
462                 return status;
463             }
464
465             /* Stick the directory on the front of each name.  */
466             if (prefix_array(dirs.gl_pathv[i],
467                              &pglob->gl_pathv[old_pathc],
468                              pglob->gl_pathc - old_pathc)) {
469                 globfree(&dirs);
470                 globfree(pglob);
471                 return GLOB_NOSPACE;
472             }
473         }
474
475         flags |= GLOB_MAGCHAR;
476
477         /* We have ignored the GLOB_NOCHECK flag in the `glob_in_dir' calls.
478            But if we have not found any matching entry and thie GLOB_NOCHECK
479            flag was set we must return the list consisting of the disrectory
480            names followed by the filename.  */
481         if (pglob->gl_pathc == oldcount) {
482             /* No matches.  */
483             if (flags & GLOB_NOCHECK) {
484                 size_t filename_len = strlen(filename) + 1;
485                 char **new_pathv;
486                 struct stat st;
487
488                 /* This is an pessimistic guess about the size.  */
489                 pglob->gl_pathv
490                     = (char **) xrealloc(pglob->gl_pathv,
491                                          (pglob->gl_pathc +
492                                           ((flags & GLOB_DOOFFS) ?
493                                            pglob->gl_offs : 0) +
494                                           dirs.gl_pathc + 1) *
495                                          sizeof(char *));
496
497                 if (flags & GLOB_DOOFFS)
498                     while (pglob->gl_pathc < pglob->gl_offs)
499                         pglob->gl_pathv[pglob->gl_pathc++] = NULL;
500
501                 for (i = 0; i < dirs.gl_pathc; ++i) {
502                     const char *dir = dirs.gl_pathv[i];
503                     size_t dir_len = strlen(dir);
504
505                     /* First check whether this really is a directory.  */
506                     if (((flags & GLOB_ALTDIRFUNC)
507                          ? (*pglob->gl_stat) (dir, &st) : stat(dir,
508                                                                  &st)) != 0
509                         || !S_ISDIR(st.st_mode))
510                         /* No directory, ignore this entry.  */
511                         continue;
512
513                     pglob->gl_pathv[pglob->gl_pathc] = xmalloc(dir_len + 1
514                                                                +
515                                                                filename_len);
516                     mempcpy(mempcpy
517                             (mempcpy
518                              (pglob->gl_pathv[pglob->gl_pathc], dir,
519                               dir_len), "/", 1), filename, filename_len);
520                     ++pglob->gl_pathc;
521                 }
522
523                 pglob->gl_pathv[pglob->gl_pathc] = NULL;
524                 pglob->gl_flags = flags;
525
526                 /* Now we know how large the gl_pathv vector must be.  */
527                 new_pathv = (char **) xrealloc(pglob->gl_pathv,
528                                                ((pglob->gl_pathc + 1)
529                                                 * sizeof(char *)));
530                 pglob->gl_pathv = new_pathv;
531             } else
532                 return GLOB_NOMATCH;
533         }
534
535         globfree(&dirs);
536     } else {
537         status = glob_in_dir(filename, dirname, flags, errfunc, pglob);
538         if (status != 0)
539             return status;
540
541         if (dirlen > 0) {
542             /* Stick the directory on the front of each name.  */
543             int ignore = oldcount;
544
545             if ((flags & GLOB_DOOFFS) && ignore < pglob->gl_offs)
546                 ignore = pglob->gl_offs;
547
548             if (prefix_array(dirname,
549                              &pglob->gl_pathv[ignore],
550                              pglob->gl_pathc - ignore)) {
551                 globfree(pglob);
552                 return GLOB_NOSPACE;
553             }
554         }
555     }
556
557     if (flags & GLOB_MARK) {
558         /* Append slashes to directory names.  */
559         int i;
560         struct stat st;
561         for (i = oldcount; i < pglob->gl_pathc; ++i)
562             if (((flags & GLOB_ALTDIRFUNC)
563                  ? (*pglob->gl_stat) (pglob->gl_pathv[i], &st)
564                  : stat(pglob->gl_pathv[i], &st)) == 0
565                 && S_ISDIR(st.st_mode)) {
566                 size_t len = strlen(pglob->gl_pathv[i]) + 2;
567                 char *new = xrealloc(pglob->gl_pathv[i], len);
568                 strcpy(&new[len - 2], "/");
569                 pglob->gl_pathv[i] = new;
570             }
571     }
572
573     if (!(flags & GLOB_NOSORT)) {
574         /* Sort the vector.  */
575         int non_sort = oldcount;
576
577         if ((flags & GLOB_DOOFFS) && pglob->gl_offs > oldcount)
578             non_sort = pglob->gl_offs;
579
580         qsort(& pglob->gl_pathv[non_sort],
581               pglob->gl_pathc - non_sort,
582               sizeof(char *), collated_compare);
583     }
584
585     return 0;
586 }
587
588
589 /* Free storage allocated in PGLOB by a previous `glob' call.  */
590 static void globfree(glob_t * pglob)
591 {
592     if (pglob->gl_pathv != NULL) {
593         register int i;
594         for (i = 0; i < pglob->gl_pathc; ++i)
595             if (pglob->gl_pathv[i] != NULL)
596                 free(pglob->gl_pathv[i]);
597         free(pglob->gl_pathv);
598     }
599 }
600
601
602 /* Do a collated comparison of A and B.  */
603 static int collated_compare(const void * a, const void * b)
604 {
605     const char *const s1 = *(const char *const *const) a;
606     const char *const s2 = *(const char *const *const) b;
607
608     if (s1 == s2)
609         return 0;
610     if (s1 == NULL)
611         return 1;
612     if (s2 == NULL)
613         return -1;
614     return strcoll(s1, s2);
615 }
616
617
618 /* Prepend DIRNAME to each of N members of ARRAY, replacing ARRAY's
619    elements in place.  Return nonzero if out of memory, zero if successful.
620    A slash is inserted between DIRNAME and each elt of ARRAY,
621    unless DIRNAME is just "/".  Each old element of ARRAY is freed.  */
622 static int prefix_array(const char *dirname, char **array, size_t n)
623 {
624     register size_t i;
625     size_t dirlen = strlen(dirname);
626
627     if (dirlen == 1 && dirname[0] == '/')
628         /* DIRNAME is just "/", so normal prepending would get us "//foo".
629            We want "/foo" instead, so don't prepend any chars from DIRNAME.  */
630         dirlen = 0;
631
632     for (i = 0; i < n; ++i) {
633         size_t eltlen = strlen(array[i]) + 1;
634         char *new = (char *) xmalloc(dirlen + 1 + eltlen);
635         {
636             char *endp = (char *) mempcpy(new, dirname, dirlen);
637             *endp++ = '/';
638             mempcpy(endp, array[i], eltlen);
639         }
640         free(array[i]);
641         array[i] = new;
642     }
643
644     return 0;
645 }
646
647 /* Return nonzero if PATTERN contains any metacharacters.
648    Metacharacters can be quoted with backslashes if QUOTE is nonzero.  */
649 static int __glob_pattern_p(const char *pattern, int quote)
650 {
651     register const char *p;
652     int open = 0;
653
654     for (p = pattern; *p != '\0'; ++p)
655         switch (*p) {
656         case '?':
657         case '*':
658             return 1;
659
660         case '\\':
661             if (quote && p[1] != '\0')
662                 ++p;
663             break;
664
665         case '[':
666             open = 1;
667             break;
668
669         case ']':
670             if (open)
671                 return 1;
672             break;
673         }
674
675     return 0;
676 }
677
678 /* Like `glob', but PATTERN is a final pathname component,
679    and matches are searched for in DIRECTORY.
680    The GLOB_NOSORT bit in FLAGS is ignored.  No sorting is ever done.
681    The GLOB_APPEND flag is assumed to be set (always appends).  */
682 static int
683 glob_in_dir(const char *pattern, const char *directory, int flags,
684             int (*errfunc)(const char *, int), glob_t * pglob)
685 {
686     void * stream = NULL;
687
688     struct globlink {
689         struct globlink *next;
690         char *name;
691     };
692     struct globlink *names = NULL;
693     size_t nfound;
694     int meta;
695     int save;
696
697     meta = __glob_pattern_p(pattern, !(flags & GLOB_NOESCAPE));
698     if (meta == 0) {
699         if (flags & (GLOB_NOCHECK | GLOB_NOMAGIC))
700             /* We need not do any tests.  The PATTERN contains no meta
701                characters and we must not return an error therefore the
702                result will always contain exactly one name.  */
703             flags |= GLOB_NOCHECK;
704         else {
705             /* Since we use the normal file functions we can also use stat()
706                to verify the file is there.  */
707             struct stat st;
708             size_t patlen = strlen(pattern);
709             size_t dirlen = strlen(directory);
710             char *fullname = (char *) alloca(dirlen + 1 + patlen + 1);
711
712             mempcpy(mempcpy(mempcpy(fullname, directory, dirlen),
713                             "/", 1), pattern, patlen + 1);
714             if (((flags & GLOB_ALTDIRFUNC)
715                  ? (*pglob->gl_stat) (fullname, &st)
716                  : stat(fullname, &st)) == 0)
717                 /* We found this file to be existing.  Now tell the rest
718                    of the function to copy this name into the result.  */
719                 flags |= GLOB_NOCHECK;
720         }
721
722         nfound = 0;
723     } else {
724         if (pattern[0] == '\0') {
725             /* This is a special case for matching directories like in
726                "*a/".  */
727             names = (struct globlink *) alloca(sizeof(struct globlink));
728             names->name = (char *) xmalloc(1);
729             names->name[0] = '\0';
730             names->next = NULL;
731             nfound = 1;
732             meta = 0;
733         } else {
734             stream = ((flags & GLOB_ALTDIRFUNC)
735                       ? (*pglob->gl_opendir) (directory)
736                       : opendir(directory));
737             if (stream == NULL) {
738                 if (errno != ENOTDIR
739                     && ((errfunc != NULL && (*errfunc) (directory, errno))
740                         || (flags & GLOB_ERR)))
741                     return GLOB_ABORTED;
742                 nfound = 0;
743                 meta = 0;
744             } else {
745                 int fnm_flags = ((!(flags & GLOB_PERIOD) ? FNM_PERIOD : 0) |
746                                  ((flags & GLOB_NOESCAPE) ? FNM_NOESCAPE : 0));
747                 nfound = 0;
748                 flags |= GLOB_MAGCHAR;
749
750                 while (1) {
751                     const char *name;
752                     size_t len;
753                     struct dirent *d = ((flags & GLOB_ALTDIRFUNC)
754                                         ? (*pglob->gl_readdir) (stream)
755                                         : readdir((DIR *) stream));
756                     if (d == NULL)
757                         break;
758                     if (!REAL_DIR_ENTRY(d))
759                         continue;
760
761 #ifdef HAVE_STRUCT_DIRENT_D_TYPE
762                     /* If we shall match only directories use the information
763                        provided by the dirent call if possible.  */
764                     if ((flags & GLOB_ONLYDIR)
765                         && d->d_type != DT_UNKNOWN && d->d_type != DT_DIR)
766                         continue;
767 #endif
768
769                     name = d->d_name;
770
771                     if (fnmatch(pattern, name, fnm_flags) == 0) {
772                         struct globlink *new = (struct globlink *)
773                             alloca(sizeof(struct globlink));
774                         len = NAMLEN(d);
775                         new->name = (char *) xmalloc(len + 1);
776                         *((char *) mempcpy(new->name, name, len))
777                             = '\0';
778                         new->next = names;
779                         names = new;
780                         ++nfound;
781                     }
782                 }
783             }
784         }
785     }
786
787     if (nfound == 0 && (flags & GLOB_NOCHECK)) {
788         size_t len = strlen(pattern);
789         nfound = 1;
790         names = (struct globlink *) alloca(sizeof(struct globlink));
791         names->next = NULL;
792         names->name = (char *) xmalloc(len + 1);
793         *((char *) mempcpy(names->name, pattern, len)) = '\0';
794     }
795
796     if (nfound != 0) {
797         pglob->gl_pathv
798             = (char **) xrealloc(pglob->gl_pathv,
799                                  (pglob->gl_pathc +
800                                   ((flags & GLOB_DOOFFS) ? pglob->
801                                    gl_offs : 0) + nfound +
802                                   1) * sizeof(char *));
803
804         if (flags & GLOB_DOOFFS)
805             while (pglob->gl_pathc < pglob->gl_offs)
806                 pglob->gl_pathv[pglob->gl_pathc++] = NULL;
807
808         for (; names != NULL; names = names->next)
809             pglob->gl_pathv[pglob->gl_pathc++] = names->name;
810         pglob->gl_pathv[pglob->gl_pathc] = NULL;
811
812         pglob->gl_flags = flags;
813     }
814
815     save = errno;
816     if (stream != NULL) {
817         if (flags & GLOB_ALTDIRFUNC)
818             (*pglob->gl_closedir) (stream);
819         else
820             closedir((DIR *) stream);
821     }
822     __set_errno(save);
823
824     return nfound == 0 ? GLOB_NOMATCH : 0;
825 }
826
827 /* librpmio exported interfaces */
828
829 int rpmGlob(const char * patterns, int * argcPtr, ARGV_t * argvPtr)
830 {
831     int ac = 0;
832     const char ** av = NULL;
833     int argc = 0;
834     ARGV_t argv = NULL;
835     char * globRoot = NULL;
836     const char *home = getenv("HOME");
837     int gflags = 0;
838 #ifdef ENABLE_NLS
839     char * old_collate = NULL;
840     char * old_ctype = NULL;
841     const char * t;
842 #endif
843     size_t maxb, nb;
844     int i, j;
845     int rc;
846
847     if (home != NULL && strlen(home) > 0) 
848         gflags |= GLOB_TILDE;
849
850     /* Can't use argvSplit() here, it doesn't handle whitespace etc escapes */
851     rc = poptParseArgvString(patterns, &ac, &av);
852     if (rc)
853         return rc;
854
855 #ifdef ENABLE_NLS
856     t = setlocale(LC_COLLATE, NULL);
857     if (t)
858         old_collate = xstrdup(t);
859     t = setlocale(LC_CTYPE, NULL);
860     if (t)
861         old_ctype = xstrdup(t);
862     (void) setlocale(LC_COLLATE, "C");
863     (void) setlocale(LC_CTYPE, "C");
864 #endif
865         
866     if (av != NULL)
867     for (j = 0; j < ac; j++) {
868         char * globURL;
869         const char * path;
870         int ut = urlPath(av[j], &path);
871         int local = (ut == URL_IS_PATH) || (ut == URL_IS_UNKNOWN);
872         size_t plen = strlen(path);
873         int flags = gflags;
874         int dir_only = (plen > 0 && path[plen-1] == '/');
875         glob_t gl;
876
877         if (!local || (!rpmIsGlob(av[j], 0) && strchr(path, '~') == NULL)) {
878             argvAdd(&argv, av[j]);
879             continue;
880         }
881
882         if (dir_only)
883             flags |= GLOB_ONLYDIR;
884         
885         gl.gl_pathc = 0;
886         gl.gl_pathv = NULL;
887         
888         rc = glob(av[j], flags, NULL, &gl);
889         if (rc)
890             goto exit;
891
892         /* XXX Prepend the URL leader for globs that have stripped it off */
893         maxb = 0;
894         for (i = 0; i < gl.gl_pathc; i++) {
895             if ((nb = strlen(&(gl.gl_pathv[i][0]))) > maxb)
896                 maxb = nb;
897         }
898         
899         nb = ((ut == URL_IS_PATH) ? (path - av[j]) : 0);
900         maxb += nb;
901         maxb += 1;
902         globURL = globRoot = xmalloc(maxb);
903
904         switch (ut) {
905         case URL_IS_PATH:
906         case URL_IS_DASH:
907             strncpy(globRoot, av[j], nb);
908             break;
909         case URL_IS_HTTPS:
910         case URL_IS_HTTP:
911         case URL_IS_FTP:
912         case URL_IS_HKP:
913         case URL_IS_UNKNOWN:
914         default:
915             break;
916         }
917         globRoot += nb;
918         *globRoot = '\0';
919
920         for (i = 0; i < gl.gl_pathc; i++) {
921             const char * globFile = &(gl.gl_pathv[i][0]);
922
923             if (dir_only) {
924                 struct stat sb;
925                 if (lstat(gl.gl_pathv[i], &sb) || !S_ISDIR(sb.st_mode))
926                     continue;
927             }
928                 
929             if (globRoot > globURL && globRoot[-1] == '/')
930                 while (*globFile == '/') globFile++;
931             strcpy(globRoot, globFile);
932             argvAdd(&argv, globURL);
933         }
934         globfree(&gl);
935         free(globURL);
936     }
937
938     argc = argvCount(argv);
939     if (argc > 0) {
940         if (argvPtr)
941             *argvPtr = argv;
942         if (argcPtr)
943             *argcPtr = argc;
944         rc = 0;
945     } else
946         rc = 1;
947
948
949 exit:
950 #ifdef ENABLE_NLS       
951     if (old_collate) {
952         (void) setlocale(LC_COLLATE, old_collate);
953         free(old_collate);
954     }
955     if (old_ctype) {
956         (void) setlocale(LC_CTYPE, old_ctype);
957         free(old_ctype);
958     }
959 #endif
960     av = _free(av);
961     if (rc || argvPtr == NULL) {
962         argvFree(argv);
963     }
964     return rc;
965 }
966
967 int rpmIsGlob(const char * pattern, int quote)
968 {
969     return __glob_pattern_p(pattern, quote);
970 }