4a1eff27afddbce301ec4a69a06f7fe0ba2b56d0
[platform/upstream/kbd.git] / src / findfile.c
1 #include <stdio.h>
2 #include <string.h>
3 #include <stdlib.h>
4 #include <unistd.h>
5 #include <sys/types.h>
6 #include <sys/stat.h>
7 #include <dirent.h>
8 #include "xmalloc.h"
9 #include "findfile.h"
10 #include "nls.h"
11
12 char pathname[1024];
13 static int ispipe;
14
15 void fpclose(FILE *fp) {
16         if (ispipe)
17              pclose(fp);
18         else
19              fclose(fp);
20 }
21
22 #define SIZE(a) (sizeof(a)/sizeof(a[0]))
23
24 static struct decompressor {
25         char *ext;              /* starts with `.', has no other dots */
26         char *cmd;
27 } decompressors[] = {
28         { ".gz", "gzip -d -c" },
29         { ".bz2", "bzip2 -d -c" },
30         { 0, 0 }
31 };
32
33 static FILE *
34 pipe_open(struct decompressor *dc) {
35         char *pipe_cmd;
36         FILE *fp;
37
38         ispipe = 1;
39         pipe_cmd = xmalloc(strlen(dc->cmd) + strlen(pathname) + 2);
40         sprintf(pipe_cmd, "%s %s", dc->cmd, pathname);
41         fp = popen(pipe_cmd, "r");
42         if (fp == NULL)
43                 fprintf(stderr, _("error executing  %s\n"), pipe_cmd);
44         return fp;
45 }
46
47 /* If a file PATHNAME exists, then open it.
48    If is has a `compressed' extension, then open a pipe reading it */
49 static FILE *
50 maybe_pipe_open(void) {
51         FILE *fp;
52         char *t;
53         struct decompressor *dc;
54
55         if ((fp = fopen(pathname, "r")) != NULL) {
56             t = rindex(pathname, '.');
57             if (t) {
58                 for (dc = &decompressors[0]; dc->cmd; dc++)
59                     if (strcmp(t, dc->ext) == 0) {
60                         fclose(fp);
61                         return pipe_open(dc);
62                     }
63             }
64         }
65         return fp;
66 }
67
68 static FILE *
69 findfile_in_dir(char *fnam, char *dir, int recdepth, char **suf) {
70         FILE *fp = NULL;
71         DIR *d;
72         struct dirent *de;
73         char *ff, *fdir, *p, *q, **sp;
74         struct decompressor *dc;
75         int secondpass = 0;
76
77         ispipe = 0;
78
79         ff = index(fnam, '/');
80         if (ff) {
81                 fdir = xstrdup(fnam);
82                 fdir[ff-fnam] = 0;      /* caller guarantees fdir != "" */
83         } else
84                 fdir = 0;               /* just to please gcc */
85
86         /* Scan the directory twice: first for files, then
87            for subdirectories, so that we do never search
88            a subdirectory when the directory itself already
89            contains the file we are looking for. */
90  StartScan:
91         d = opendir(dir);
92         if (d == NULL)
93             return NULL;
94         while ((de = readdir(d)) != NULL) {
95             struct stat statbuf;
96             int okdir;
97
98             if (strcmp(de->d_name, ".") == 0 ||
99                 strcmp(de->d_name, "..") == 0)
100                 continue;
101
102             if (strlen(dir) + strlen(de->d_name) + 2 > sizeof(pathname))
103                 continue;
104
105             okdir = (ff && strcmp(de->d_name, fdir) == 0);
106
107             if ((secondpass && recdepth) || okdir) {
108                 char *a;
109
110                 a = xmalloc(strlen(dir) + strlen(de->d_name) + 2);
111                 sprintf(a, "%s/%s", dir, de->d_name);
112                 if (stat(a, &statbuf) == 0 &&
113                     S_ISDIR(statbuf.st_mode)) {
114                         if (okdir)
115                                 fp = findfile_in_dir(ff+1, a, 0, suf);
116                         if (!fp && recdepth)
117                                 fp = findfile_in_dir(fnam, a, recdepth-1, suf);
118                         if (fp)
119                                 return fp;
120                 }
121                 free(a);
122             }
123
124             if (secondpass)
125                     continue;
126
127             /* Should we be in a subdirectory? */
128             if (ff)
129                     continue;
130
131             /* Does d_name start right? */
132             p = &de->d_name[0];
133             q = fnam;
134             while (*p && *p == *q) p++,q++;
135             if (*q)
136                     continue;
137
138             sprintf(pathname, "%s/%s", dir, de->d_name);
139             if (stat(pathname, &statbuf) != 0 || !S_ISREG(statbuf.st_mode))
140                     continue;
141
142             /* Does tail consist of a known suffix and possibly
143                a compression suffix? */
144             for(sp = suf; *sp; sp++) {
145                     int l;
146
147                     if (!strcmp(p, *sp))
148                             return maybe_pipe_open();
149
150                     l = strlen(*sp);
151                     if (strncmp(p,*sp,l) == 0) {
152                         for (dc = &decompressors[0]; dc->cmd; dc++)
153                             if (strcmp(p+l, dc->ext) == 0)
154                                 return pipe_open(dc);
155                     }
156             }
157         }
158         closedir(d);
159         if (recdepth > 0 && !secondpass) {
160                 secondpass = 1;
161                 goto StartScan;
162         }
163         return NULL;
164 }
165
166 /* find input file; leave name in pathname[] */
167 FILE *findfile(char *fnam, char **dirpath, char **suffixes) {
168         char **dp, *dir, **sp;
169         FILE *fp;
170         int dl, recdepth;
171         struct decompressor *dc;
172
173         if (strlen(fnam) >= sizeof(pathname))
174                 return NULL;
175
176         /* Try explicitly given name first */
177         strcpy(pathname, fnam);
178         fp = maybe_pipe_open();
179         if (fp)
180                 return fp;
181
182         /* Test for full pathname - opening it failed, so need suffix */
183         /* (This is just nonsense, for backwards compatibility.) */
184         if (*fnam == '/') {
185             struct stat statbuf;
186
187             for (sp = suffixes; *sp; sp++) {
188                 if (strlen(fnam) + strlen(*sp) + 1 > sizeof(pathname))
189                     continue;
190                 if (*sp == 0)
191                     continue;   /* we tried it already */
192                 sprintf(pathname, "%s%s", fnam, *sp);
193                 if(stat(pathname, &statbuf) == 0 && S_ISREG(statbuf.st_mode)
194                    && (fp = fopen(pathname, "r")) != NULL)
195                     return fp;
196             }
197
198             for (sp = suffixes; *sp; sp++) {
199                 for (dc = &decompressors[0]; dc->cmd; dc++) {
200                     if (strlen(fnam) + strlen(*sp)
201                         + strlen(dc->ext) + 1 > sizeof(pathname))
202                             continue;
203                     sprintf(pathname, "%s%s%s", fnam, *sp, dc->ext);
204                     if (stat(pathname, &statbuf) == 0
205                         && S_ISREG(statbuf.st_mode)
206                         && (fp = fopen(pathname, "r")) != NULL) {
207                             fclose(fp);
208                             return pipe_open(dc);
209                     }
210                 }
211             }
212
213             return NULL;
214         }
215
216         /* Search a list of directories and directory hierarchies */
217         for (dp = dirpath; *dp; dp++) {
218
219             /* delete trailing slashes; trailing stars denote recursion */
220             dir = xstrdup(*dp);
221             dl = strlen(dir);
222             recdepth = 0;
223             while (dl && dir[dl-1] == '*') {
224                     dir[--dl] = 0;
225                     recdepth++;
226             }
227             if (dl == 0) {
228                     dir = ".";
229             } else if (dl > 1 && dir[dl-1] == '/') {
230                     dir[dl-1] = 0;
231             }
232
233             fp = findfile_in_dir(fnam, dir, recdepth, suffixes);
234             if (fp)
235                     return fp;
236         }
237
238         return NULL;
239 }