1 /*
2  *  DIRUTE.H
3  *  Contributes findfirst, findnext and findclose for POSIX systems.
4  *  Based on code taken from Msged TE. Mostly written by Tobias Ernst.
5  *  patmat() written by Sreenath Chary on Nov 29 1998.
6  *  Released to the Public Domain.
7  *
8  *  Please note that you must call findclose!!! So it is a good idea
9  *  to also include this file on non-Unix systems, because in this
10  *  case, it provides a dummy findclose.
11  */
12 
13 #include "dirute.h"
14 #include <errno.h>
15 
16 #if defined(UNIX)
17 
18 #include <ctype.h>
19 #include <stdlib.h>
20 #include <time.h>
21 #include <string.h>
22 #include <assert.h>
23 
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 
patmat(char * raw,char * pat)27 static int patmat(char *raw, char *pat)
28 {
29         int i;
30 
31         if (*pat == '\0' && *raw == '\0')
32         {
33                 /* if it is the end of both strings, then match */
34                 return 1;
35         }
36         if (*pat == '\0')
37         {
38                 /* if it is the end of only pat then mismatch */
39                 return 0;
40         }
41 
42         /* if pattern is `*' */
43 
44         if (*pat == '*')
45         {
46                 if (*(pat + 1) == '\0')
47                 {
48                         /* if pat is just `*' then match */
49                         return 1;
50                 }
51                 else
52                 {
53                         /* else hunt for match or wild card */
54                         for (i = 0; i <= strlen(raw); i++)
55                         {
56                                 if (*(raw + i) == *(pat + 1) ||
57                                     *(pat + 1) == '?')
58                                 {
59                                         /* if found, match rest of pattern */
60                                         if (patmat(raw + i + 1, pat + 2) == 1)
61                                         {
62                                                 return 1;
63                                         }
64                                 }
65                         }
66                 }
67         }
68         else
69         {
70                 if (*raw == '\0')
71                 {
72                         /* we've reached the end of raw; return a mismatch */
73                         return 0;
74                 }
75 
76                 if (*pat == '?' || *pat == *raw)
77                 {
78                         /* if chars match then try and match rest of it */
79                         if (patmat(raw + 1, pat + 1) == 1)
80                         {
81                                 return 1;
82                         }
83                 }
84         }
85 
86         /* fell through; no match found */
87         return 0;
88 }
89 
match(const char * name,const char * pattern,int attribute,int mode,int rdonly)90 static int match(const char *name, const char *pattern, int attribute,
91                  int mode, int rdonly)
92 {
93         char *matpattern = strdup(pattern);
94         char *matname = strdup(name);
95         char *cp;
96         int rc;
97 
98         if (matpattern == NULL || matname == NULL)
99         {
100                 return 0;
101         }
102 
103         if (!(attribute & FA_CASE))
104         {
105                 for (cp=matpattern; *cp; cp++)
106                 {
107                         *cp = toupper(*cp);
108                 }
109                 for (cp=matname; *cp; cp++)
110                 {
111                         *cp = toupper(*cp);
112                 }
113         }
114 
115         rc = patmat(matname, matpattern);
116 
117         free(matname);
118         free(matpattern);
119 
120         if (rc)
121         {
122                 /* the name matches, now check the other criteria */
123 
124                 if (!(attribute & FA_RDONLY))
125                         if (rdonly)
126                                 return 0;
127 
128                 if (!(attribute & FA_DIREC))
129                         if (S_ISDIR(mode))
130                                 return 0;
131 
132                 if (!(attribute & FA_HIDDEN))
133                         if (name[0] == '.')
134                                 return 0;
135         }
136 
137         return rc;
138 }
139 
copy_info(struct ffblk * pffblk,struct dirent * de,struct stat * psb)140 static void copy_info(struct ffblk *pffblk, struct dirent *de,
141                       struct stat *psb)
142 {
143         struct tm *ltm;
144 
145         strcpy(pffblk->ff_name, de->d_name);
146         pffblk->ff_fsize = psb->st_size;
147         pffblk->ff_attrib = 0;
148         if (!access(pffblk->fullname, W_OK))
149         {
150                 pffblk->ff_attrib |= FA_RDONLY;
151         }
152         if (S_ISDIR(psb->st_mode))
153         {
154                 pffblk->ff_attrib |= FA_DIREC;
155         }
156         if (de->d_name[0] == '.')
157         {
158                 pffblk->ff_attrib |= FA_HIDDEN;
159         }
160 
161         ltm = localtime(&(psb->st_mtime));
162         pffblk->ff_ftime = ((ltm->tm_sec >> 1) & 31) |
163                            ((ltm->tm_min & 63) << 5) |
164                            ((ltm->tm_hour & 31) << 11);
165         pffblk->ff_fdate = (ltm->tm_mday & 31) |
166                            (((ltm->tm_mon + 1) & 15) << 5) |
167                            (((ltm->tm_year - 80) & 127) << 9);
168 }
169 
findfirst(const char * filename,struct ffblk * pffblk,int attribute)170 int findfirst(const char *filename, struct ffblk* pffblk, int attribute)
171 {
172         char *p;
173         int fin = 0;
174         struct stat sb;
175 
176         p = strrchr(filename, '/');
177         if (p == NULL)
178         {
179                 strcpy(pffblk->firstbit, ".");
180                 strcpy(pffblk->lastbit, filename);
181         }
182         else
183         {
184                 memcpy(pffblk->firstbit, filename, p - filename);
185                 pffblk->firstbit[p - filename] = '\0';
186                 strcpy(pffblk->lastbit, p + 1);
187         }
188 
189         if (*pffblk->firstbit)
190                 pffblk->dir = opendir(pffblk->firstbit);
191         else
192                 pffblk->dir = opendir("/");
193 
194         if (pffblk->dir == NULL)
195         {
196                 return -1;
197         }
198 
199         while (!fin)
200         {
201                 pffblk->de = readdir(pffblk->dir);
202                 if (pffblk->de == NULL)
203                 {
204                         closedir(pffblk->dir);
205                         pffblk->dir = NULL;
206                         return -1;
207                 }
208 
209                 strcpy(pffblk->fullname, pffblk->firstbit);
210                 strcat(pffblk->fullname, "/");
211                 strcat(pffblk->fullname, pffblk->de->d_name);
212 
213                 if (stat(pffblk->fullname, &sb))
214                 {
215                         closedir(pffblk->dir);
216                         pffblk->dir = NULL;
217                         return -1;
218                 }
219 
220                 if (match(pffblk->de->d_name, pffblk->lastbit,
221                           attribute, sb.st_mode,
222                           access(pffblk->fullname, W_OK)))
223                         fin = 1;
224         }
225         copy_info(pffblk, pffblk->de, &sb);
226         pffblk->attribute_mask = attribute;
227         return 0;
228 }
229 
findnext(struct ffblk * pffblk)230 int findnext(struct ffblk *pffblk)
231 {
232         int fin = 0;
233         struct stat sb;
234 
235         while (!fin)
236         {
237                 pffblk->de = readdir(pffblk->dir);
238                 if (pffblk->de == NULL)
239                 {
240                         closedir(pffblk->dir);
241                         pffblk->dir = NULL;
242                         return -1;
243                 }
244 
245                 strcpy(pffblk->fullname, pffblk->firstbit);
246                 strcat(pffblk->fullname, "/");
247                 strcat(pffblk->fullname, pffblk->de->d_name);
248 
249                 if (stat(pffblk->fullname, &sb))
250                 {
251                         closedir(pffblk->dir);
252                         pffblk->dir = NULL;
253                         return -1;
254                 }
255 
256                 if (match(pffblk->de->d_name, pffblk->lastbit,
257                           pffblk->attribute_mask, sb.st_mode,
258                           access(pffblk->fullname, W_OK)))
259                         fin = 1;
260         }
261         copy_info(pffblk, pffblk->de, &sb);
262         return 0;
263 }
264 
findclose(struct ffblk * pffblk)265 int findclose(struct ffblk *pffblk)
266 {
267         if (pffblk->dir != NULL)
268                 closedir(pffblk->dir);
269         pffblk->dir = NULL;
270         return 0;
271 }
272 
273 /* The adaptcase routine behaves as follows: It assumes that pathname
274    is a path name which may contain multiple dashes, and it assumes
275    that you run on a case sensitive file system but want / must to
276    match the path name insensitively. adaptcase takes every path
277    element out of pathname and uses findfirst to check if it
278    exists. If it exists, the path element is replaced by the exact
279    spelling as used by the file system. If it does not exist, it is
280    converted to lowercase.  This allows you to make you program deal
281    with things like mounts of DOS file systems under unix
282 
283    Return value is 1 if the file exists and 0 if not.
284 */
285 
adaptcase(char * pathname)286 int adaptcase(char *pathname)
287 {
288         int i,j, found=1;
289         char buf[FILENAME_MAX + 1];
290         char buf2[FILENAME_MAX + 1];
291         char *cp;
292         struct ffblk ffblk;
293 
294         if (!*pathname)
295                 return 0;
296 
297         j = 0; i = 0;
298 
299         while (pathname[i])
300         {
301                 if (pathname[i] == '/')
302                 {
303                         buf[i] = tolower(((int)(pathname[i]))); i++;
304                 }
305                 j = i;
306                 for (; pathname[i] && pathname[i]!='/'; i++)
307                         buf[i] = tolower(((int)(pathname[i])));
308                 buf[i] = '\0';
309 
310                 if (!file_exists(buf))
311                 {
312                     if (!findfirst(buf, &ffblk, FA_DIREC | FA_RDONLY |
313                                    FA_ARCH | FA_HIDDEN))
314                     {
315                         /* file exists, take over it's name */
316 
317                         if (ffblk.fullname[0] == '.' &&
318                             ffblk.fullname[1] == '/' &&
319                             buf[0] != '.' && buf[1] != '/')
320                                 cp = ffblk.fullname + 2;
321                         else
322                                 cp = ffblk.fullname;
323                         assert(strlen(buf) == strlen(cp));
324                         strcpy(buf , cp);
325                     }
326                     else
327                     {
328                         /* file does not exist - so the rest is brand new and
329                            should be converted to lower case */
330 
331                         for (i = j; pathname[i]; i++)
332                             buf[i] = tolower(pathname[i]);
333                         buf[i] = '\0';
334                         findclose(&ffblk);
335                         found = 0;
336                         break;
337                     }
338                     findclose(&ffblk);
339                 }
340         }
341         assert(strlen(pathname) == strlen(buf));
342         strcpy(pathname, buf);
343         return found;
344 }
345 
346 #endif
347 
348 #ifndef UNIX
349 #include <stdio.h>
350 #include <stdlib.h>
351 #endif
352 
file_exists(char * fn)353 int file_exists(char *fn)
354 {
355         FILE *f;
356 
357         f = fopen(fn, "r");
358         if ((f == NULL) && (errno == ENOENT))
359                 return 0;
360         if (f != NULL)
361                 fclose(f);
362         return 1;
363 }
364 
365 #if defined (TEST)
main(int argc,char ** argv)366 int main(int argc, char **argv)
367 {
368         struct ffblk ffblk; int rc;
369 
370         if (argc < 2)
371                 return 0;
372 
373 /*        rc = findfirst(argv[1], &ffblk, FA_RDONLY | FA_DIREC |
374                        FA_HIDDEN | FA_ARCH);
375         while (!rc)
376         {
377                 printf("%s \t %ld\n", ffblk.ff_name, ffblk.ff_fsize);
378                 rc = findnext(&ffblk);
379         }
380         findclose(&ffblk); */
381         printf ("%s\n", adaptcase(argv[1]));
382 
383         return 0;
384 }
385 
386 #endif
387