FLTK 1.3.11
Toggle main menu visibility
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
62
static
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
*/
80
static
int
81
readentry(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
/* ========================================================================== */
131
int
132
fl_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
*/
src
scandir_posix.c
Generated by
1.17.0