1 /*
2 Copyright (C) 2000 Masanao Izumo <mo@goice.co.jp>
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 */
18
19 #include "config.h"
20 #include <stdio.h>
21 #include <stdlib.h>
22 #ifndef NO_STRING_H
23 #include <string.h>
24 #else
25 #include <strings.h>
26 #endif
27 #include "libarc/url.h"
28
29 #ifdef HAVE_SAFE_MALLOC
30 extern char *safe_strdup(char *);
31 #else
32 #define safe_strdup strdup
33 #endif /* HAVE_SAFE_MALLOC */
34
35 #ifdef __W32READDIR__
36 #include "readdir.h"
37 # define NAMLEN(dirent) strlen((dirent)->d_name)
38 #elif __MACOS__
39 # include "mac_readdir.h"
40 # define NAMLEN(dirent) strlen((dirent)->d_name)
41 #else
42
43 #include <sys/types.h>
44 #if defined(sun)
45 #include <dirent.h>
46 #define NAMLEN(dirent) strlen((dirent)->d_name)
47 #elif defined(SVR4) || defined(SYSTYPE_SVR4) || defined(__svr4__)
48 #include <sys/dir.h>
49 #define dirent direct
50 #define NAMLEN(dirent) strlen((dirent)->d_namlen)
51 #else
52 #include <dirent.h>
53 #define NAMLEN(dirent) strlen((dirent)->d_name)
54 #endif
55
56
57 #endif
58
59 #ifdef URL_DIR_CACHE_ENABLE
60 #include <sys/stat.h>
61 #ifdef HAVE_FCNTL_H
62 #include <fcntl.h>
63 #endif /* HAVE_FCNTL_H */
64 #include "strtab.h"
65
66 #ifndef S_ISDIR
67 #define S_ISDIR(mode) (((mode)&0xF000) == 0x4000)
68 #endif /* S_ISDIR */
69
70 #ifdef unix
71 #define INODE_AVAILABLE
72 #endif /* unix */
73
74 struct dir_cache_t
75 {
76 char **fnames;
77 #ifdef INODE_AVAILABLE
78 dev_t dev;
79 ino_t ino;
80 #else
81 char *dirname;
82 #endif
83 time_t dir_mtime;
84 struct dir_cache_t *next;
85 };
86 static struct dir_cache_t *dir_cache = NULL;
87
88 #ifdef INODE_AVAILABLE
89 #define REMOVE_CACHE_ENT(p, isalloced) if(isalloced) free(p); else (p)->ino = 0
90 #else
91 #define REMOVE_CACHE_ENT(p, isalloced) free((p)->dirname); if(isalloced) free(p); else p->dirname = NULL
92 #endif /* INODE_AVAILABLE */
93
scan_cached_files(struct dir_cache_t * p,struct stat * s,char * dirname)94 static struct dir_cache_t *scan_cached_files(struct dir_cache_t *p,
95 struct stat *s,
96 char *dirname)
97 {
98 StringTable stab;
99 DIR *dirp;
100 struct dirent *d;
101 int allocated;
102
103 if(p == NULL)
104 {
105 if((p = (struct dir_cache_t *)malloc(sizeof(struct dir_cache_t))) ==
106 NULL)
107 return NULL;
108 allocated = 1;
109 } else
110 allocated = 0;
111
112 /* save directory information */
113 #ifdef INODE_AVAILABLE
114 p->ino = s->st_ino;
115 p->dev = s->st_dev;
116 #else
117 p->dirname = safe_strdup(dirname);
118 #endif /* INODE_AVAILABLE */
119 p->dir_mtime = s->st_mtime;
120
121 if((dirp = opendir(dirname)) == NULL)
122 {
123 url_errno = errno;
124 REMOVE_CACHE_ENT(p, allocated);
125 errno = url_errno;
126 return NULL;
127 }
128
129 init_string_table(&stab);
130 while((d = readdir(dirp)) != NULL)
131 {
132 int dlen;
133
134 #ifdef INODE_AVAILABLE
135 if(d->d_ino == 0)
136 continue;
137 #endif
138 if((dlen = NAMLEN(d)) == 0)
139 continue;
140
141 /* put into string table */
142 if(put_string_table(&stab, d->d_name, dlen) == NULL)
143 {
144 url_errno = errno;
145 delete_string_table(&stab);
146 REMOVE_CACHE_ENT(p, allocated);
147 closedir(dirp);
148 errno = url_errno;
149 return NULL;
150 }
151 }
152 closedir(dirp);
153
154 /* make string array */
155 p->fnames = make_string_array(&stab);
156 if(p->fnames == NULL)
157 {
158 url_errno = errno;
159 delete_string_table(&stab);
160 REMOVE_CACHE_ENT(p, allocated);
161 errno = url_errno;
162 return NULL;
163 }
164 return p;
165 }
166
read_cached_files(char * dirname)167 static struct dir_cache_t *read_cached_files(char *dirname)
168 {
169 struct dir_cache_t *p, *q;
170 struct stat s;
171
172 if(stat(dirname, &s) < 0)
173 return NULL;
174 if(!S_ISDIR(s.st_mode))
175 {
176 errno = url_errno = ENOTDIR;
177 return NULL;
178 }
179
180 q = NULL;
181 for(p = dir_cache; p; p = p->next)
182 {
183 #ifdef INODE_AVAILABLE
184 if(p->ino == 0)
185 #else
186 if(p->dirname == NULL)
187 #endif /* INODE_AVAILABLE */
188 {
189 /* Entry is removed.
190 * Save the entry to `q' which is reused for puting in new entry.
191 */
192 if(q != NULL)
193 q = p;
194 continue;
195 }
196
197 #ifdef INODE_AVAILABLE
198 if(s.st_dev == p->dev && s.st_ino == p->ino)
199 #else
200 if(strcmp(p->dirname, dirname) == 0)
201 #endif /* INODE_AVAILABLE */
202
203 {
204 /* found */
205 if(p->dir_mtime == s.st_mtime)
206 return p;
207
208 /* Directory entry is updated */
209 free(p->fnames[0]);
210 free(p->fnames);
211 #ifndef INODE_AVAILABLE
212 free(p->dirname);
213 #endif /* !INODE_AVAILABLE */
214 return scan_cached_files(p, &s, dirname);
215 }
216 }
217 /* New directory */
218 if((p = scan_cached_files(q, &s, dirname)) == NULL)
219 return NULL;
220 p->next = dir_cache;
221 dir_cache = p;
222 return p;
223 }
224 #endif /* URL_DIR_CACHE_ENABLE */
225
226 typedef struct _URL_dir
227 {
228 char common[sizeof(struct _URL)];
229 #ifdef URL_DIR_CACHE_ENABLE
230 char **fptr;
231 #else
232 DIR *dirp;
233 struct dirent *d;
234 #endif /* URL_DIR_CACHE_ENABLE */
235
236 char *ptr;
237 int len;
238 long total;
239 char *dirname;
240 int endp;
241 } URL_dir;
242
243 static int name_dir_check(char *url_string);
244 static long url_dir_read(URL url, void *buff, long n);
245 static char *url_dir_gets(URL url, char *buff, int n);
246 static long url_dir_tell(URL url);
247 static void url_dir_close(URL url);
248
249 struct URL_module URL_module_dir =
250 {
251 URL_dir_t, /* type */
252 name_dir_check, /* URL checker */
253 NULL, /* initializer */
254 url_dir_open, /* open */
255 NULL /* must be NULL */
256 };
257
pathsep_strrchr(char * path)258 static char *pathsep_strrchr(char *path)
259 {
260 #ifdef PATH_SEP2
261 char *last_sep = NULL;
262 while(*path)
263 {
264 if(*path == PATH_SEP || *path == PATH_SEP2)
265 last_sep = path;
266 path++;
267 }
268 return last_sep;
269 #else
270 return strrchr(path, PATH_SEP);
271 #endif
272 }
273
name_dir_check(char * url_string)274 static int name_dir_check(char *url_string)
275 {
276 if(strncasecmp(url_string, "dir:", 4) == 0)
277 return 1;
278 url_string = pathsep_strrchr(url_string);
279 return url_string != NULL && *(url_string + 1) == '\0';
280 }
281
282 #ifdef URL_DIR_CACHE_ENABLE
url_dir_open(char * dname)283 URL url_dir_open(char *dname)
284 {
285 struct dir_cache_t *d;
286 URL_dir *url;
287 int dlen;
288
289 if(dname == NULL)
290 dname = ".";
291 else
292 {
293 if(strncasecmp(dname, "dir:", 4) == 0)
294 dname += 4;
295 if(*dname == '\0')
296 dname = ".";
297 else
298 dname = url_expand_home_dir(dname);
299 }
300 dname = safe_strdup(dname);
301
302 /* Remove tail of path sep. */
303 dlen = strlen(dname);
304 while(dlen > 0 && IS_PATH_SEP(dname[dlen - 1]))
305 dlen--;
306 dname[dlen] = '\0';
307 if(dlen == 0)
308 strcpy(dname, PATH_STRING); /* root */
309
310 d = read_cached_files(dname);
311 if(d == NULL)
312 {
313 free(dname);
314 return NULL;
315 }
316
317 url = (URL_dir *)alloc_url(sizeof(URL_dir));
318 if(url == NULL)
319 {
320 url_errno = errno;
321 free(dname);
322 errno = url_errno; /* restore errno */
323 return NULL;
324 }
325
326 /* common members */
327 URLm(url, type) = URL_dir_t;
328 URLm(url, url_read) = url_dir_read;
329 URLm(url, url_gets) = url_dir_gets;
330 URLm(url, url_fgetc) = NULL;
331 URLm(url, url_seek) = NULL;
332 URLm(url, url_tell) = url_dir_tell;
333 URLm(url, url_close) = url_dir_close;
334
335 /* private members */
336 url->fptr = d->fnames;
337 url->ptr = NULL;
338 url->len = 0;
339 url->total = 0;
340 url->dirname = dname;
341 url->endp = 0;
342
343 return (URL)url;
344 }
345 #else
url_dir_open(char * dname)346 URL url_dir_open(char *dname)
347 {
348 URL_dir *url;
349 DIR *dirp;
350 int dlen;
351
352 if(dname == NULL)
353 dname = ".";
354 else
355 {
356 if(strncasecmp(dname, "dir:", 4) == 0)
357 dname += 4;
358 if(*dname == '\0')
359 dname = ".";
360 else
361 dname = url_expand_home_dir(dname);
362 }
363 dname = safe_strdup(dname);
364
365 /* Remove tail of path sep. */
366 dlen = strlen(dname);
367 while(dlen > 0 && IS_PATH_SEP(dname[dlen - 1]))
368 dlen--;
369 dname[dlen] = '\0';
370 if(dlen == 0)
371 strcpy(dname, PATH_STRING); /* root */
372
373 if((dirp = opendir(dname)) == NULL)
374 {
375 url_errno = errno;
376 free(dname);
377 errno = url_errno;
378 return NULL;
379 }
380
381 url = (URL_dir *)alloc_url(sizeof(URL_dir));
382 if(url == NULL)
383 {
384 url_errno = errno;
385 closedir(dirp);
386 free(dname);
387 errno = url_errno;
388 return NULL;
389 }
390
391 /* common members */
392 URLm(url, type) = URL_dir_t;
393 URLm(url, url_read) = url_dir_read;
394 URLm(url, url_gets) = url_dir_gets;
395 URLm(url, url_fgetc) = NULL;
396 URLm(url, url_seek) = NULL;
397 URLm(url, url_tell) = url_dir_tell;
398 URLm(url, url_close) = url_dir_close;
399
400 /* private members */
401 url->dirp = dirp;
402 url->d = NULL;
403 url->ptr = NULL;
404 url->len = 0;
405 url->total = 0;
406 url->dirname = dname;
407 url->endp = 0;
408
409 return (URL)url;
410 }
411 #endif /* URL_DIR_CACHE_ENABLE */
412
url_dir_tell(URL url)413 static long url_dir_tell(URL url)
414 {
415 return ((URL_dir *)url)->total;
416 }
417
url_dir_name(URL url)418 char *url_dir_name(URL url)
419 {
420 if(url->type != URL_dir_t)
421 return NULL;
422 return ((URL_dir *)url)->dirname;
423 }
424
url_dir_close(URL url)425 static void url_dir_close(URL url)
426 {
427 URL_dir *urlp = (URL_dir *)url;
428 #ifndef URL_DIR_CACHE_ENABLE
429 closedir(urlp->dirp);
430 #endif
431 free(urlp->dirname);
432 free(urlp);
433 }
434
url_dir_read(URL url,void * buff,long n)435 static long url_dir_read(URL url, void *buff, long n)
436 {
437 char *p;
438
439 p = url_dir_gets(url, (char *)buff, (int)n);
440 if(p == NULL)
441 return 0;
442 return (long)strlen(p);
443 }
444
url_dir_gets(URL url,char * buff,int n)445 static char *url_dir_gets(URL url, char *buff, int n)
446 {
447 URL_dir *urlp = (URL_dir *)url;
448 int i;
449
450 if(urlp->endp)
451 return NULL;
452 if(n <= 0)
453 return buff;
454 if(n == 1)
455 {
456 *buff = '\0';
457 return buff;
458 }
459 n--; /* for '\0' */;
460 for(;;)
461 {
462 if(urlp->len > 0)
463 {
464 i = urlp->len;
465 if(i > n)
466 i = n;
467 memcpy(buff, urlp->ptr, i);
468 buff[i] = '\0';
469 urlp->len -= i;
470 urlp->ptr += i;
471 urlp->total += i;
472 return buff;
473 }
474
475 #ifdef URL_DIR_CACHE_ENABLE
476 if(*urlp->fptr == NULL)
477 {
478 urlp->endp = 1;
479 return NULL;
480 }
481 urlp->ptr = *urlp->fptr;
482 urlp->fptr++;
483 urlp->len = strlen(urlp->ptr);
484 #else
485 do
486 if((urlp->d = readdir(urlp->dirp)) == NULL)
487 {
488 urlp->endp = 1;
489 return NULL;
490 }
491 while (
492 #ifdef INODE_AVAILABLE
493 urlp->d->d_ino == 0 ||
494 #endif /* INODE_AVAILABLE */
495 NAMLEN(urlp->d) == 0);
496 urlp->ptr = urlp->d->d_name;
497 urlp->len = NAMLEN(urlp->d);
498 #endif
499 }
500 }
501