1 /*
2 ** dirlist.c - Get a directory listing
3 **
4 ** Copyright (c) 1999-2000 Peter Eriksson <pen@lysator.liu.se>
5 **
6 ** This program is free software; you can redistribute it and/or
7 ** modify it as you wish - as long as you don't claim that you wrote
8 ** it.
9 **
10 ** This program is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 */
14 
15 #include "plib/config.h"
16 
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <syslog.h>
21 
22 #include "plib/safeio.h"
23 #include "plib/safestr.h"
24 #include "plib/dirlist.h"
25 #include "plib/strl.h"
26 #include "plib/aalloc.h"
27 
28 
29 
30 /* XXX: Should perhaps be dynamically allocated? */
31 #define MAX_PATHNAMELEN 2048
32 
33 int dirlist_use_lstat = 1;
34 
35 
36 static int
dirent_compare(const void * e1,const void * e2)37 dirent_compare(const void *e1,
38 	       const void *e2)
39 {
40     const struct dirlist_ent *d1, *d2;
41 
42 
43     d1 = (struct dirlist_ent *) e1;
44     d2 = (struct dirlist_ent *) e2;
45 
46     if (S_ISDIR(d1->s.st_mode) &&
47 	!S_ISDIR(d2->s.st_mode))
48 	return -1;
49 
50     if (!S_ISDIR(d1->s.st_mode) &&
51 	S_ISDIR(d2->s.st_mode))
52 	return 1;
53 
54     return strcmp(d1->d->d_name, d2->d->d_name);
55 }
56 
57 
58 static struct dirent *
dirent_dup(const struct dirent * dep)59 dirent_dup(const struct dirent *dep)
60 {
61     struct dirent *n_dep;
62     size_t len;
63 
64 #if 0
65     len = sizeof(*dep)+dep->d_reclen;
66     n_dep = a_malloc(len, "struct dirent");
67     memcpy(n_dep, dep, len);
68 #else
69 # ifdef __DragonFly__
70     n_dep = a_malloc(len = _DIRENT_RECLEN(dep->d_namlen), "struct dirent");
71 # else
72     n_dep = a_malloc(len = dep->d_reclen, "struct dirent");
73 # endif
74 #endif
75     memcpy(n_dep, dep, len);
76 
77     return n_dep;
78 }
79 
80 
81 static struct dirent *
dirent_alloc(ino_t ino,const char * name)82 dirent_alloc(ino_t ino, const char *name)
83 {
84     struct dirent *dp;
85     int len;
86 
87 
88 
89     len = strlen(name);
90 
91 #if 0
92     fprintf(stderr, "dirent_alloc: len=%d, name=%s\n",
93 	    len, name);
94 #endif
95 
96     dp = a_malloc(sizeof(*dp)+len, "struct dirent");
97     dp->d_ino = ino;
98 #ifdef		linux
99     dp->d_off = 0;
100 #endif		/* linux */
101 #ifndef		__DragonFly__
102     dp->d_reclen = len;
103 #endif
104     strcpy(dp->d_name, name);
105 
106     return dp;
107 }
108 
109 
110 DIRLIST *
dirlist_get(const char * path)111 dirlist_get(const char *path)
112 {
113     DIR *dp;
114     DIRLIST *dlp;
115     struct dirent *dep;
116     char p_buf[MAX_PATHNAMELEN], *pp, *pend;
117     int err, p_len;
118     struct stat sb;
119     const char *cp;
120 
121 
122     if (path == NULL)
123 	return NULL;
124 
125     dp = opendir(path);
126     if (dp == NULL)
127     {
128 	if (dirlist_use_lstat)
129 	    err = lstat(path, &sb);
130 	else
131 	    err = stat(path, &sb);
132 
133 	if (err)
134 	    return NULL;
135 
136 	A_NEW(dlp);
137 	dlp->dec = 0;
138 	dlp->des = 2;
139 	dlp->dev = a_malloc(dlp->des * sizeof(dlp->dev[0]), "DIRLIST dev[]");
140 
141 	cp = strrchr(path, '/');
142 	if (cp)
143 	    ++cp;
144 	else
145 	    cp = path;
146 
147 	dlp->dev[dlp->dec].s = sb;
148 	dlp->dev[dlp->dec].d = dirent_alloc(sb.st_ino, cp);
149 
150 	dlp->dec++;
151 	dlp->dev[dlp->dec].d = NULL;
152 
153 	return dlp;
154     }
155 
156     p_len = strlcpy(p_buf, path, sizeof(p_buf));
157     if (p_len >= sizeof(p_buf))
158 	return NULL;
159 
160     pp = p_buf+p_len;
161     if (pp > p_buf && pp[-1] != '/')
162     {
163 	if (p_len + 1 >= sizeof(p_buf))
164 	    return NULL;
165 
166 	*pp++ = '/';
167 	*pp = '\0';
168     }
169 
170     pend = pp;
171 
172     A_NEW(dlp);
173 
174     dlp->dec = 0;
175     dlp->des = 64;
176     dlp->dev = a_malloc(dlp->des * sizeof(dlp->dev[0]), "DIRLIST dev[]");
177 
178     while ((dep = readdir(dp)) != NULL)
179     {
180 #if 0
181 	fprintf(stderr, "dirent_get: readdir: dep->d_reclen = %d, dep->d_name = %s\n",
182 		dep->d_reclen, dep->d_name);
183 #endif
184 
185 	*pend = '\0';
186 
187 	if (dlp->dec == dlp->des - 1)
188 	{
189 	    dlp->des += 64;
190 	    dlp->dev = a_realloc(dlp->dev,
191 				 dlp->des * sizeof(dlp->dev[0]),
192 				 "DIRENT dev[]");
193 	}
194 
195 	dlp->dev[dlp->dec].d = dirent_dup(dep);
196 
197 	if (strlcat(p_buf, dep->d_name, sizeof(p_buf)) >= sizeof(p_buf))
198 	{
199 	    syslog(LOG_ERR, "dirlist_get: path too long (skipped): %s", p_buf);
200 	    continue;
201 	}
202 
203 	if (dirlist_use_lstat)
204 	    err = lstat(p_buf, &dlp->dev[dlp->dec].s);
205 	else
206 	    err = stat(p_buf, &dlp->dev[dlp->dec].s);
207 
208 	if (err)
209 	{
210 	    syslog(LOG_WARNING, "dirlist_get: stat(%s) failed: %m\n", p_buf);
211 	    continue;
212 	}
213 
214 	dlp->dec++;
215     }
216 
217     closedir(dp);
218     dlp->dev[dlp->dec].d = NULL;
219 
220     qsort(dlp->dev, dlp->dec, sizeof(dlp->dev[0]), dirent_compare);
221 
222     return dlp;
223 }
224 
225 
226 void
dirlist_free(DIRLIST * dlp)227 dirlist_free(DIRLIST *dlp)
228 {
229     int i;
230 
231 
232     if (dlp == NULL)
233 	return;
234 
235     for (i = 0; i < dlp->dec; i++)
236 	a_free(dlp->dev[i].d);
237     a_free(dlp->dev);
238     a_free(dlp);
239 }
240