1 /* Lips of Suna
2 * Copyright© 2007-2009 Lips of Suna development team.
3 *
4 * Lips of Suna is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as
6 * published by the Free Software Foundation, either version 3 of the
7 * License, or (at your option) any later version.
8 *
9 * Lips of Suna 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 Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with Lips of Suna. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 /**
19 * \addtogroup lisys System
20 * @{
21 * \addtogroup lisysDir Directory
22 * @{
23 */
24
25 #include <assert.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <dirent.h>
32 #include <unistd.h>
33 #include "system-directory.h"
34
35 #ifdef LI_ENABLE_ERROR
36 #include "system-error.h"
37 #define error_memory() lisys_error_set (ENOMEM, NULL)
38 #define error_open(fmt, file) lisys_error_set (EIO, fmt, file)
39 #else
40 #define error_memory()
41 #define error_open(fmt, file)
42 #endif
43
44 struct _lisysDir
45 {
46 void* dir;
47 char* path;
48 struct
49 {
50 lisysDirFilter filter;
51 lisysDirSorter sorter;
52 } calls;
53 struct
54 {
55 int count;
56 char** array;
57 } names;
58 };
59
60 static char*
61 private_concat_paths (const char* a,
62 const char* b);
63
64 /*****************************************************************************/
65
66 /**
67 * \brief Opens a directory.
68 *
69 * \param path Path to directory.
70 * \return Directory or NULL.
71 */
72 lisysDir*
lisys_dir_open(const char * path)73 lisys_dir_open (const char* path)
74 {
75 lisysDir* self;
76
77 /* Allocate self. */
78 self = calloc (1, sizeof (lisysDir));
79 if (self == NULL)
80 {
81 error_memory ();
82 return NULL;
83 }
84
85 /* Store path. */
86 self->path = strdup (path);
87 if (path == NULL)
88 {
89 error_memory ();
90 free (self);
91 return NULL;
92 }
93
94 /* Open the directory. */
95 self->dir = opendir (path);
96 if (self->dir == NULL)
97 {
98 error_open ("cannot open directory `%s'", path);
99 free (self->path);
100 free (self);
101 return NULL;
102 }
103
104 return self;
105 }
106
107 /**
108 * \brief Frees a directory.
109 *
110 * \param self Directory.
111 */
112 void
lisys_dir_free(lisysDir * self)113 lisys_dir_free (lisysDir* self)
114 {
115 int i;
116
117 closedir (self->dir);
118 for (i = 0 ; i < self->names.count ; i++)
119 free (self->names.array[i]);
120 free (self->names.array);
121 free (self->path);
122 free (self);
123 }
124
125 /**
126 * \brief Scans a directory.
127 *
128 * Scans the directory for files and filters and sorts them according to the
129 * current filter and sorter rules. The results are stored to the names array.
130 *
131 * \param self Directory.
132 * \return Nonzero on success.
133 */
134 int
lisys_dir_scan(lisysDir * self)135 lisys_dir_scan (lisysDir* self)
136 {
137 int i;
138 int num = 0;
139 int cap = 16;
140 char** tmp;
141 char** list;
142 struct dirent* ent;
143
144 /* Allocate the name list. */
145 list = malloc (cap * sizeof (char*));
146 if (list == NULL)
147 {
148 error_memory ();
149 return 0;
150 }
151
152 /* Read names. */
153 rewinddir (self->dir);
154 while ((ent = readdir (self->dir)))
155 {
156 /* Filter. */
157 if (self->calls.filter != NULL)
158 {
159 if (!self->calls.filter (self->path, ent->d_name))
160 continue;
161 }
162
163 /* Resize the list. */
164 if (num == cap)
165 {
166 tmp = realloc (list, (cap << 1) * sizeof (char*));
167 if (tmp == NULL)
168 {
169 error_memory ();
170 goto error;
171 }
172 cap <<= 1;
173 list = tmp;
174 }
175
176 /* Append to the list. */
177 list[num] = strdup (ent->d_name);
178 if (list[num] == NULL)
179 {
180 error_memory ();
181 goto error;
182 }
183 num++;
184 }
185
186 /* Shrink. */
187 if (num != 0)
188 {
189 tmp = realloc (list, num * sizeof (char*));
190 if (tmp != NULL)
191 list = tmp;
192 }
193 else
194 {
195 free (list);
196 list = NULL;
197 }
198
199 /* Sort names. */
200 if (self->calls.sorter != NULL)
201 qsort (list, num, sizeof (char*), (int(*)(const void*, const void*)) self->calls.sorter);
202
203 /* Replace old names. */
204 for (i = 0 ; i < self->names.count ; i++)
205 free (self->names.array[i]);
206 free (self->names.array);
207 self->names.count = num;
208 self->names.array = list;
209
210 return 1;
211
212 error:
213 for (i = 0 ; i < num ; i++)
214 free (list[i]);
215 free (list);
216 return 0;
217 }
218
219 /**
220 * \brief Gets the number of files in the directory.
221 *
222 * \param self Directory.
223 * \return Number of files.
224 */
225 int
lisys_dir_get_count(const lisysDir * self)226 lisys_dir_get_count (const lisysDir* self)
227 {
228 return self->names.count;
229 }
230
231 /**
232 * \brief Gets the name of one of the files in the name array.
233 *
234 * \param self Directory.
235 * \param i Index in the name array.
236 * \return String owned by the directory.
237 */
238 const char*
lisys_dir_get_name(const lisysDir * self,int i)239 lisys_dir_get_name (const lisysDir* self,
240 int i)
241 {
242 assert (i >= 0);
243 assert (i < self->names.count);
244
245 return self->names.array[i];
246 }
247
248 /**
249 * \brief Gets the path to one of the files in the name array.
250 *
251 * \param self Directory.
252 * \param i Index in the name array.
253 * \return New string or NULL.
254 */
255 char*
lisys_dir_get_path(const lisysDir * self,int i)256 lisys_dir_get_path (const lisysDir* self,
257 int i)
258 {
259 assert (i >= 0);
260 assert (i < self->names.count);
261
262 return private_concat_paths (self->path, self->names.array[i]);
263 }
264
265 /**
266 * \brief Set the current filter rule.
267 *
268 * \param self Directory.
269 * \param filter Fiulter function or NULL.
270 */
271 void
lisys_dir_set_filter(lisysDir * self,lisysDirFilter filter)272 lisys_dir_set_filter (lisysDir* self,
273 lisysDirFilter filter)
274 {
275 self->calls.filter = filter;
276 }
277
278 /**
279 * \brief Set the current sorter rule.
280 *
281 * \param self Directory.
282 * \param sorter Sorter function or NULL.
283 */
284 void
lisys_dir_set_sorter(lisysDir * self,lisysDirSorter sorter)285 lisys_dir_set_sorter (lisysDir* self,
286 lisysDirSorter sorter)
287 {
288 self->calls.sorter = sorter;
289 }
290
291 /*****************************************************************************/
292
293 /**
294 * \brief Lets everything except directories through.
295 */
296 int
LISYS_DIR_FILTER_FILES(const char * dir,const char * name)297 LISYS_DIR_FILTER_FILES (const char* dir,
298 const char* name)
299 {
300 char* path;
301 struct stat st;
302
303 path = private_concat_paths (dir, name);
304 if (path == NULL)
305 return 1;
306 if (stat (path, &st) < 0)
307 {
308 free (path);
309 return 0;
310 }
311 if (!S_ISDIR (st.st_mode))
312 {
313 free (path);
314 return 1;
315 }
316
317 free (path);
318 return 0;
319 }
320
321 /**
322 * \brief Only lets directories through.
323 */
324 int
LISYS_DIR_FILTER_DIRS(const char * dir,const char * name)325 LISYS_DIR_FILTER_DIRS (const char* dir,
326 const char* name)
327 {
328 char* path;
329 struct stat st;
330
331 path = private_concat_paths (dir, name);
332 if (path == NULL)
333 return 1;
334 if (stat (path, &st) < 0)
335 {
336 free (path);
337 return 0;
338 }
339 if (S_ISDIR (st.st_mode))
340 {
341 free (path);
342 return 1;
343 }
344
345 free (path);
346 return 0;
347 }
348
349 /**
350 * \brief Only lets hidden files through.
351 */
352 int
LISYS_DIR_FILTER_HIDDEN(const char * dir,const char * name)353 LISYS_DIR_FILTER_HIDDEN (const char* dir,
354 const char* name)
355 {
356 if (name[0] == '.')
357 return 1;
358 return 0;
359 }
360
361 /**
362 * \brief Only lets non-hidden files through.
363 */
364 int
LISYS_DIR_FILTER_VISIBLE(const char * dir,const char * name)365 LISYS_DIR_FILTER_VISIBLE (const char* dir,
366 const char* name)
367 {
368 if (name[0] == '.')
369 return 0;
370 return 1;
371 }
372
373 /**
374 * \brief Sorts entries alphabetically.
375 */
376 int
LISYS_DIR_SORTER_ALPHA(const char ** name0,const char ** name1)377 LISYS_DIR_SORTER_ALPHA (const char** name0,
378 const char** name1)
379 {
380 return strcmp (*name0, *name1);
381 }
382
383 /*****************************************************************************/
384
385 static char*
private_concat_paths(const char * a,const char * b)386 private_concat_paths (const char* a,
387 const char* b)
388 {
389 int len0;
390 int len1;
391 char* ret;
392
393 len0 = strlen (a);
394 len1 = strlen (b);
395 ret = malloc (len0 + len1 + 2);
396 if (ret == NULL)
397 {
398 error_memory ();
399 return NULL;
400 }
401 strcpy (ret, a);
402 if (len0 && a[len0] != '/')
403 ret[len0++] = '/';
404 strcpy (ret + len0, b);
405 ret[len0 + len1] = '\0';
406
407 return ret;
408 }
409
410 /** @} */
411 /** @} */
412