4c7e8db5541a08e421be7f93ef727ea092987356
[platform/upstream/kbd.git] / src / libkeymap / findfile.c
1 #include "config.h"
2
3 #include <stdio.h>
4 #include <string.h>
5 #include <stdlib.h>
6 #include <unistd.h>
7 #include <sys/param.h>
8 #include <sys/types.h>
9 #include <sys/stat.h>
10 #include <dirent.h>
11 #include "nls.h"
12 #include "keymap/findfile.h"
13
14 void lk_fpclose(lkfile_t *fp)
15 {
16         if (!fp || !fp->fd)
17                 return;
18         if (fp->pipe)
19                 pclose(fp->fd);
20         else
21                 fclose(fp->fd);
22         fp->fd = NULL;
23 }
24
25 #define SIZE(a) (sizeof(a) / sizeof(a[0]))
26
27 static struct decompressor {
28         const char *ext; /* starts with `.', has no other dots */
29         const char *cmd;
30 } decompressors[] = {
31         { ".gz", "gzip -d -c" },
32         { ".bz2", "bzip2 -d -c" },
33         { 0, 0 }
34 };
35
36 static int
37 pipe_open(const struct decompressor *dc, lkfile_t *fp)
38 {
39         char *pipe_cmd;
40
41         pipe_cmd = malloc(strlen(dc->cmd) + strlen(fp->pathname) + 2);
42         if (pipe_cmd == NULL)
43                 return -1;
44
45         sprintf(pipe_cmd, "%s %s", dc->cmd, fp->pathname);
46
47         fp->fd   = popen(pipe_cmd, "r");
48         fp->pipe = 1;
49
50         free(pipe_cmd);
51
52         if (fp->fd == NULL)
53                 return -1;
54         return 0;
55 }
56
57 /* If a file PATHNAME exists, then open it.
58    If is has a `compressed' extension, then open a pipe reading it */
59 static int
60 maybe_pipe_open(lkfile_t *fp)
61 {
62         char *t;
63         struct stat st;
64         struct decompressor *dc;
65
66         if (stat(fp->pathname, &st) == -1 || !S_ISREG(st.st_mode) ||
67             access(fp->pathname, R_OK) == -1)
68                 return -1;
69
70         t = strrchr(fp->pathname, '.');
71         if (t) {
72                 for (dc = &decompressors[0]; dc->cmd; dc++) {
73                         if (strcmp(t, dc->ext) == 0)
74                                 return pipe_open(dc, fp);
75                 }
76         }
77         fp->fd   = fopen(fp->pathname, "r");
78         fp->pipe = 0;
79
80         if (fp->fd == NULL)
81                 return -1;
82
83         return 0;
84 }
85
86 static int
87 findfile_by_fullname(const char *fnam, const char *const *suffixes, lkfile_t *fp)
88 {
89         int i;
90         struct stat st;
91         struct decompressor *dc;
92         size_t fnam_len, sp_len;
93
94         fp->pipe = 0;
95         fnam_len = strlen(fnam);
96
97         for (i = 0; suffixes[i]; i++) {
98                 if (suffixes[i] == 0)
99                         continue; /* we tried it already */
100
101                 sp_len = strlen(suffixes[i]);
102
103                 if (fnam_len + sp_len + 1 > sizeof(fp->pathname))
104                         continue;
105
106                 sprintf(fp->pathname, "%s%s", fnam, suffixes[i]);
107
108                 if (stat(fp->pathname, &st) == 0 && S_ISREG(st.st_mode) && (fp->fd = fopen(fp->pathname, "r")) != NULL)
109                         return 0;
110
111                 for (dc = &decompressors[0]; dc->cmd; dc++) {
112                         if (fnam_len + sp_len + strlen(dc->ext) + 1 > sizeof(fp->pathname))
113                                 continue;
114
115                         sprintf(fp->pathname, "%s%s%s", fnam, suffixes[i], dc->ext);
116
117                         if (stat(fp->pathname, &st) == 0 && S_ISREG(st.st_mode) && access(fp->pathname, R_OK) == 0)
118                                 return pipe_open(dc, fp);
119                 }
120         }
121
122         return -1;
123 }
124
125 static int
126 findfile_in_dir(const char *fnam, const char *dir, const int recdepth, const char *const *suf, lkfile_t *fp)
127 {
128         DIR *d;
129         struct dirent *de;
130         char *ff, *fdir, *p;
131         const char *q;
132         struct decompressor *dc;
133         int i, rc = -1, secondpass = 0;
134         size_t dir_len;
135
136         fp->fd   = NULL;
137         fp->pipe = 0;
138
139         if ((d = opendir(dir)) == NULL)
140                 return -1;
141
142         dir_len = strlen(dir);
143
144         fdir = NULL;
145         if ((ff = strchr(fnam, '/')) != NULL) {
146                 if ((fdir = strndup(fnam, ff - fnam)) == NULL) {
147                         closedir(d);
148                         return -1;
149                 }
150         }
151
152 /* Scan the directory twice: first for files, then
153            for subdirectories, so that we do never search
154            a subdirectory when the directory itself already
155            contains the file we are looking for. */
156 StartScan:
157         while ((de = readdir(d)) != NULL) {
158                 struct stat st;
159                 int okdir;
160                 size_t d_len;
161
162                 d_len = strlen(de->d_name);
163                 if (d_len < 3) {
164                         if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
165                                 continue;
166                 }
167
168                 if (dir_len + d_len + 2 > sizeof(fp->pathname))
169                         continue;
170
171                 okdir = (ff && strcmp(de->d_name, fdir) == 0);
172
173                 if ((secondpass && recdepth) || okdir) {
174                         char *a;
175
176                         if ((a = malloc(dir_len + d_len + 2)) == NULL)
177                                 goto EndScan;
178
179                         sprintf(a, "%s/%s", dir, de->d_name);
180
181                         if (stat(a, &st) == 0 && S_ISDIR(st.st_mode)) {
182                                 if (okdir)
183                                         rc = findfile_in_dir(ff + 1, a, 0, suf, fp);
184
185                                 if (rc && recdepth)
186                                         rc = findfile_in_dir(fnam, a, recdepth - 1, suf, fp);
187
188                                 if (!rc) {
189                                         free(a);
190                                         goto EndScan;
191                                 }
192                         }
193                         free(a);
194                 }
195
196                 if (secondpass)
197                         continue;
198
199                 /* Should we be in a subdirectory? */
200                 if (ff)
201                         continue;
202
203                 /* Does d_name start right? */
204                 p = &de->d_name[0];
205                 q = fnam;
206                 while (*p && *p == *q)
207                         p++, q++;
208                 if (*q)
209                         continue;
210
211                 sprintf(fp->pathname, "%s/%s", dir, de->d_name);
212                 if (stat(fp->pathname, &st) != 0 || !S_ISREG(st.st_mode))
213                         continue;
214
215                 /* Does tail consist of a known suffix and possibly
216                a compression suffix? */
217                 for (i = 0; suf[i]; i++) {
218                         size_t l;
219
220                         if (!strcmp(p, suf[i])) {
221                                 rc = maybe_pipe_open(fp);
222                                 goto EndScan;
223                         }
224
225                         l = strlen(suf[i]);
226                         if (!strncmp(p, suf[i], l)) {
227                                 for (dc = &decompressors[0]; dc->cmd; dc++)
228                                         if (strcmp(p + l, dc->ext) == 0) {
229                                                 rc = pipe_open(dc, fp);
230                                                 goto EndScan;
231                                         }
232                         }
233                 }
234         }
235
236         if (recdepth > 0 && !secondpass) {
237                 secondpass = 1;
238                 seekdir(d, 0);
239                 goto StartScan;
240         }
241
242 EndScan:
243         if (fdir != NULL)
244                 free(fdir);
245         closedir(d);
246         return rc;
247 }
248
249 int lk_findfile(const char *fnam, const char *const *dirpath, const char *const *suffixes, lkfile_t *fp)
250 {
251         char *dir;
252         int dl, recdepth, rc, i;
253
254         fp->fd   = NULL;
255         fp->pipe = 0;
256
257         /* Try explicitly given name first */
258         strcpy(fp->pathname, fnam);
259
260         if (!maybe_pipe_open(fp))
261                 return 0;
262
263         /* Test for full pathname - opening it failed, so need suffix */
264         /* (This is just nonsense, for backwards compatibility.) */
265         if (*fnam == '/' &&
266             !findfile_by_fullname(fnam, suffixes, fp))
267                 return 0;
268
269         /* Search a list of directories and directory hierarchies */
270         for (i = 0; dirpath[i]; i++) {
271                 recdepth = 0;
272                 dl       = strlen(dirpath[i]);
273
274                 /* trailing stars denote recursion */
275                 while (dl && dirpath[i][dl - 1] == '*')
276                         dl--, recdepth++;
277
278                 /* delete trailing slashes */
279                 while (dl && dirpath[i][dl - 1] == '/')
280                         dl--;
281
282                 if (dl)
283                         dir = strndup(dirpath[i], dl);
284                 else
285                         dir = strdup(".");
286
287                 if (dir == NULL)
288                         return 1;
289
290                 rc = findfile_in_dir(fnam, dir, recdepth, suffixes, fp);
291                 free(dir);
292
293                 if (!rc)
294                         return 0;
295         }
296         return 1;
297 }
298
299 lkfile_t *
300 lk_fpopen(const char *filename)
301 {
302         lkfile_t *fp;
303
304         fp = malloc(sizeof(lkfile_t));
305         if (!fp)
306                 return NULL;
307
308         strcpy(fp->pathname, filename);
309
310         if (maybe_pipe_open(fp) < 0) {
311                 free(fp);
312                 return NULL;
313         }
314
315         return fp;
316 }