FLTK 1.3.9: scandir_posix.c Source File

FLTK 1.3.9

Loading...
Searching...
No Matches
scandir_posix.c
1/*
2 * "$Id$"
3 *
4 * This implementation of 'scandir()' is intended to be POSIX.1-2008 compliant.
5 * A POSIX.1-1990 compliant system is required as minimum base.
6 * Note:
7 * The 'const' declarations were removed to match FLTK 1.3 wrapper (STR #2931)
8 *
9 * Copyright (c) 2013 by Michael Baeuerle
10 *
11 * This library is free software. Distribution and use rights are outlined in
12 * the file "COPYING" which should have been included with this file. If this
13 * file is missing or damaged, see the license at:
14 *
15 * http://www.fltk.org/COPYING.php
16 *
17 * Please report all bugs and problems on the following page:
18 *
19 * http://www.fltk.org/str.php
20 *
21 * It is required that 'SIZE_MAX' is at least 'INT_MAX'.
22 * Don't use a C++ compiler to build this module.
23 *
24 * The build system must define 'HAVE_PTHREAD' and link against a potentially
25 * required library to switch this implementation into thread-safe mode.
26 * The POSIX.1c-1995 extension is required if 'HAVE_PTHREAD' is defined.
27 *
28 * Note:
29 * In theory, a system that provide threads should also provide 'readdir_r()',
30 * a thread-safe version of 'readdir()'. In reality this is not always the case.
31 * In addition there may be a race condition that can lead to a buffer overflow:
32 * http://womble.decadent.org.uk/readdir_r-advisory.html
33 */
34
35#ifndef HAVE_PTHREAD
36 /* Switch system headers into POSIX.1-1990 mode */
37# define _POSIX_SOURCE
38#else /* HAVE_PTHREAD */
39 /* Switch system headers into POSIX.1c-1995 mode */
40# define _POSIX_C_SOURCE 199506L
41#endif /* HAVE_PTHREAD */
42
43#include <sys/types.h> /* XPG2 require this for '*dir()' functions */
44#include <dirent.h>
45#include <errno.h>
46#include <stdlib.h> /* For 'malloc()', 'realloc()' and 'qsort()' */
47#include <stddef.h> /* For 'offsetof()', 'NULL' and 'size_t' */
48#include <limits.h> /* For 'INT_MAX' */
49#include <string.h> /* For 'memcpy()' */
50#if defined(HAVE_PTHREAD) && defined(HAVE_PTHREAD_H)
51# include <pthread.h>
52#endif /* HAVE_PTHREAD */
53
54
55/* ========================================================================== */
56/* At startup allocate memory for this number of result array elements */
57#define ENTRIES_MIN (size_t) 32
58
59
60/* ========================================================================== */
61#ifdef HAVE_PTHREAD
62static pthread_mutex_t scandir_mutex = PTHREAD_MUTEX_INITIALIZER;
63#endif /* HAVE_PTHREAD */
64
65
66/* ========================================================================== */
67/*
68 * This function reads the next entry from the directory referenced by 'dirp',
69 * allocate a buffer for the entry and copy it into this buffer.
70 * A pointer to this buffer is written to 'entryp' and the size of the buffer is
71 * written to 'len'.
72 * Success and a NULL pointer is returned for 'entryp' if there are no more
73 * entries in the directory.
74 * On sucess zero is returned and the caller is responsible for 'free()'ing the
75 * buffer after use.
76 * On error the return value is nonzero, 'entryp' and 'len' are invalid.
77 *
78 * Should be declared as 'static inline' if the compiler support that.
79 */
80static int
81readentry(DIR *dirp, struct dirent **entryp, size_t *len)
82{
83 int result = -1;
84 struct dirent *e;
85
86#ifdef HAVE_PTHREAD
87 if (!pthread_mutex_lock(&scandir_mutex))
88 {
89 /* Ensure that there is no code path that bypass the '_unlock()' call! */
90#endif /* HAVE_PTHREAD */
91 errno = 0;
92 e = readdir(dirp);
93 if (NULL == e)
94 {
95 if (!errno)
96 {
97 /* No more entries in directory */
98 *entryp = NULL;
99 *len = 0;
100 result = 0;
101 }
102 }
103 else
104 {
105 /* Entry found, allocate local buffer */
106 *len = offsetof(struct dirent, d_name) + strlen(e->d_name) + (size_t) 1;
107 *entryp = (struct dirent *) malloc(*len);
108 if (NULL != *entryp)
109 {
110 memcpy((void *) *entryp, (void *) e, *len);
111 /* Force NUL termination at end of buffer */
112 ((char *) *entryp)[*len - (size_t) 1] = 0;
113 result = 0;
114 }
115 }
116#ifdef HAVE_PTHREAD
117 /*
118 * In a multithreading environment the systems dirent buffer may be shared
119 * between all threads. Therefore the mutex must stay locked until we have
120 * copied the data to our thread local buffer.
121 */
122 pthread_mutex_unlock(&scandir_mutex);
123 }
124#endif /* HAVE_PTHREAD */
125
126 return result;
127}
128
129
130/* ========================================================================== */
131int
132fl_scandir(const char *dir, struct dirent ***namelist,
133 int (*sel)(struct dirent *),
134 int (*compar)(struct dirent **, struct dirent **))
135{
136 int result = -1;
137 DIR *dirp;
138 size_t len, num = 0, max = ENTRIES_MIN;
139 struct dirent *entryp, **entries, **p;
140
141 entries = (struct dirent **) malloc(sizeof(*entries) * max);
142 if (NULL != entries)
143 {
144 /* Open directory 'dir' (and verify that it really is a directory) */
145 dirp = opendir(dir);
146 if (NULL != dirp)
147 {
148 /* Read next directory entry */
149 while (!readentry(dirp, &entryp, &len))
150 {
151 if (NULL == entryp)
152 {
153 /* EOD => Return number of directory entries */
154 result = (int) num;
155 break;
156 }
157 /* Apply select function if there is one provided */
158 if (NULL != sel) { if (!sel(entryp)) continue; }
159 entries[num++] = entryp;
160 if (num >= max)
161 {
162 /* Allocate exponentially increasing sized memory chunks */
163 if (INT_MAX / 2 >= (int) max) { max *= (size_t) 2; }
164 else
165 {
166 errno = ENOMEM;
167 break;
168 }
169 p = (struct dirent **) realloc((void *) entries,
170 sizeof(*entries) * max);
171 if (NULL != p) { entries = p; }
172 else break;
173 }
174 }
175 closedir(dirp);
176 /*
177 * A standard compliant 'closedir()' is allowed to fail with 'EINTR', but
178 * the state of the directory structure is undefined in this case.
179 * Therefore we ignore the return value because we can't call 'closedir()'
180 * again and must hope that the system has released all ressources.
181 */
182 }
183 /* Sort entries in array if there is a compare function provided */
184 if (NULL != compar)
185 {
186 qsort((void *) entries, num, sizeof(*entries),
187 (int (*)(const void *, const void *)) compar);
188 }
189 *namelist = entries;
190 }
191
192 /* Check for error */
193 if (-1 == result)
194 {
195 /* Free all memory we have allocated */
196 while (num--) { free(entries[num]); }
197 free(entries);
198 }
199
200 return result;
201}
202
203/*
204 * End of "$Id$".
205 */