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