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