xml_include fails if file is not found YAZ-668
[yaz-moved-to-github.git] / src / file_glob.c
1 /* This file is part of the YAZ toolkit.
2  * Copyright (C) 1995-2013 Index Data
3  * See the file LICENSE for details.
4  */
5
6 /** \file
7     \brief File globbing (ala POSIX glob, but simpler)
8 */
9 #if HAVE_CONFIG_H
10 #include <config.h>
11 #endif
12
13 #include <sys/types.h>
14 #include <sys/stat.h>
15 #include <stdio.h>
16 #include <string.h>
17 #include <stdlib.h>
18 #include <assert.h>
19 #include <yaz/wrbuf.h>
20 #include <yaz/tpath.h>
21 #include <yaz/log.h>
22 #include <yaz/dirent.h>
23 #include <yaz/nmem.h>
24 #include <yaz/file_glob.h>
25 #include <yaz/match_glob.h>
26
27 struct res_entry {
28     struct res_entry *next;
29     char *file;
30 };
31
32 struct glob_res {
33     NMEM nmem;
34     size_t number_of_entries;
35     struct res_entry **last_entry;
36     struct res_entry *entries;
37 };
38
39 static void add_entry(yaz_glob_res_t res, const char *str)
40 {
41     struct res_entry *ent =
42         nmem_malloc(res->nmem, sizeof(*ent));
43     ent->file = nmem_strdup(res->nmem, str);
44     ent->next = 0;
45     *res->last_entry = ent;
46     res->last_entry = &ent->next;
47     res->number_of_entries++;
48 }
49
50 static void glob_r(yaz_glob_res_t res, const char *pattern, size_t off,
51                    char *prefix)
52 {
53     size_t prefix_len = strlen(prefix);
54     int is_pattern = 0;
55     size_t i = off;
56     while (pattern[i] && !strchr("/\\", pattern[i]))
57     {
58         if (strchr("?*", pattern[i]))
59             is_pattern = 1;
60         i++;
61     }
62
63     if (!is_pattern && pattern[i]) /* no pattern and directory part */
64     {
65         i++; /* skip dir sep */
66         memcpy(prefix + prefix_len, pattern + off, i - off);
67         prefix[prefix_len + i - off] = '\0';
68         glob_r(res, pattern, i, prefix);
69         prefix[prefix_len] = '\0';
70     }
71     else if (!is_pattern && !pattern[i])
72     {
73         strcpy(prefix + prefix_len, pattern + off);
74         add_entry(res, prefix);
75     }
76     else
77     {
78         DIR * dir = opendir(*prefix ? prefix : "." );
79
80         if (dir)
81         {
82             struct dirent *ent;
83
84             while ((ent = readdir(dir)))
85             {
86                 int r;
87                 memcpy(prefix + prefix_len, pattern + off, i - off);
88                 prefix[prefix_len + i - off] = '\0';
89                 r = yaz_match_glob(prefix + prefix_len, ent->d_name);
90                 prefix[prefix_len] = '\0';
91
92                 if (r)
93                 {
94                     strcpy(prefix + prefix_len, ent->d_name);
95                     if (pattern[i])
96                     {
97                         glob_r(res, pattern, i, prefix);
98                     }
99                     else
100                     {
101                         add_entry(res, prefix);
102                     }
103                     prefix[prefix_len] = '\0';
104                 }
105             }
106             closedir(dir);
107         }
108     }
109 }
110
111 static int cmp_entry(const void *a, const void *b)
112 {
113     struct res_entry *ent_a = *(struct res_entry **) a;
114     struct res_entry *ent_b = *(struct res_entry **) b;
115     return strcmp(ent_a->file, ent_b->file);
116 }
117
118 static void sort_them(yaz_glob_res_t res)
119 {
120     size_t i;
121     struct res_entry **ent_p;
122     struct res_entry **ent = nmem_malloc(res->nmem, sizeof(*ent) * res->number_of_entries);
123     struct res_entry *ent_i = res->entries;
124     for (i = 0; i < res->number_of_entries; i++)
125     {
126         ent[i] = ent_i;
127         ent_i = ent_i->next;
128     }
129     qsort(ent, res->number_of_entries, sizeof(*ent), cmp_entry);
130     ent_p = &res->entries;
131     for (i = 0; i < res->number_of_entries; i++)
132     {
133         *ent_p = ent[i];
134         ent_p = &ent[i]->next;
135     }
136     *ent_p = 0;
137 }
138
139 int yaz_file_glob(const char *pattern, yaz_glob_res_t *res)
140 {
141     char prefix[FILENAME_MAX+1];
142     NMEM nmem = nmem_create();
143
144     *prefix = '\0';
145     *res = nmem_malloc(nmem, sizeof(**res));
146     (*res)->number_of_entries = 0;
147     (*res)->nmem = nmem;
148     (*res)->entries = 0;
149     (*res)->last_entry = &(*res)->entries;
150     glob_r(*res, pattern, 0, prefix);
151     sort_them(*res);
152     return 0;
153 }
154
155 void yaz_file_globfree(yaz_glob_res_t *res)
156 {
157     if (*res)
158     {
159         /* must free entries as well */
160         nmem_destroy((*res)->nmem);
161         *res = 0;
162     }
163 }
164
165 const char *yaz_file_glob_get_file(yaz_glob_res_t res, size_t idx)
166 {
167     struct res_entry *ent = res->entries;
168     while (idx && ent)
169     {
170         ent = ent->next;
171         idx--;
172     }
173     if (!ent)
174         return 0;
175     return ent->file;
176 }
177
178 size_t yaz_file_glob_get_num(yaz_glob_res_t res)
179 {
180     return res->number_of_entries;
181 }
182
183 /*
184  * Local variables:
185  * c-basic-offset: 4
186  * c-file-style: "Stroustrup"
187  * indent-tabs-mode: nil
188  * End:
189  * vim: shiftwidth=4 tabstop=8 expandtab
190  */
191