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