1 /*
2 * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd.
3 *
4 * All rights reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, and/or sell copies of the Software, and to permit persons
11 * to whom the Software is furnished to do so, provided that the above
12 * copyright notice(s) and this permission notice appear in all copies of
13 * the Software and that both the above copyright notice(s) and this
14 * permission notice appear in supporting documentation.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
19 * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
20 * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL
21 * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING
22 * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
23 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
24 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
25 *
26 * Except as contained in this notice, the name of a copyright holder
27 * shall not be used in advertising or otherwise to promote the sale, use
28 * or other dealings in this Software without prior written authorization
29 * of the copyright holder.
30 */
31
32 /*
33 * If file-system access is to be excluded, this module has no function,
34 * so all of its code should be excluded.
35 */
36 #ifndef WITHOUT_FILE_SYSTEM
37
38 /*
39 * Standard includes.
40 */
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <errno.h>
45
46 /*
47 * Operating system includes.
48 */
49 #include <unistd.h>
50 #include <sys/types.h>
51 #include <sys/stat.h>
52 #include <dirent.h>
53
54 #include "direader.h"
55 #include "errmsg.h"
56
57 /*
58 * Use the reentrant POSIX threads version of readdir()?
59 */
60 #if defined(PREFER_REENTRANT) && defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199506L
61 #define USE_READDIR_R 1
62 #endif
63
64 /*
65 * Objects of the following type are used to maintain the resources
66 * needed to read directories.
67 */
68 struct DirReader {
69 ErrMsg *err; /* The error reporting buffer */
70 DIR *dir; /* The directory stream (if open, NULL otherwise) */
71 struct dirent *file; /* The latest directory entry */
72 #ifdef USE_READDIR_R
73 struct dirent *buffer; /* A buffer used by the threaded version of */
74 /* readdir() */
75 int buffer_dim; /* The allocated size of buffer[] */
76 #endif
77 };
78
79 static int _dr_path_is_dir(const char *pathname);
80
81 /*.......................................................................
82 * Create a new DirReader object.
83 *
84 * Output:
85 * return DirReader * The new object, or NULL on error.
86 */
_new_DirReader(void)87 DirReader *_new_DirReader(void)
88 {
89 DirReader *dr; /* The object to be returned */
90 /*
91 * Allocate the container.
92 */
93 dr = (DirReader *) malloc(sizeof(DirReader));
94 if(!dr) {
95 errno = ENOMEM;
96 return NULL;
97 };
98 /*
99 * Before attempting any operation that might fail, initialize the
100 * container at least up to the point at which it can safely be passed
101 * to _del_DirReader().
102 */
103 dr->err = NULL;
104 dr->dir = NULL;
105 dr->file = NULL;
106 #ifdef USE_READDIR_R
107 dr->buffer = NULL;
108 dr->buffer_dim = 0;
109 #endif
110 /*
111 * Allocate a place to record error messages.
112 */
113 dr->err = _new_ErrMsg();
114 if(!dr->err)
115 return _del_DirReader(dr);
116 return dr;
117 }
118
119 /*.......................................................................
120 * Delete a DirReader object.
121 *
122 * Input:
123 * dr DirReader * The object to be deleted.
124 * Output:
125 * return DirReader * The deleted object (always NULL).
126 */
_del_DirReader(DirReader * dr)127 DirReader *_del_DirReader(DirReader *dr)
128 {
129 if(dr) {
130 _dr_close_dir(dr);
131 #ifdef USE_READDIR_R
132 free(dr->buffer);
133 #endif
134 dr->err = _del_ErrMsg(dr->err);
135 free(dr);
136 };
137 return NULL;
138 }
139
140 /*.......................................................................
141 * Open a new directory.
142 *
143 * Input:
144 * dr DirReader * The directory reader resource object.
145 * path const char * The directory to be opened.
146 * Input/Output:
147 * errmsg char ** If an error occurs and errmsg isn't NULL, a
148 * pointer to an error description will be assigned
149 * to *errmsg.
150 * Output:
151 * return int 0 - OK.
152 * 1 - Error (see *errmsg for a description).
153 */
_dr_open_dir(DirReader * dr,const char * path,char ** errmsg)154 int _dr_open_dir(DirReader *dr, const char *path, char **errmsg)
155 {
156 DIR *dir = NULL; /* The directory stream */
157 /*
158 * If a directory is already open, close it first.
159 */
160 (void) _dr_close_dir(dr);
161 /*
162 * Is the path a directory?
163 */
164 if(!_dr_path_is_dir(path)) {
165 if(errmsg) {
166 _err_record_msg(dr->err, "Can't open directory: ", path, END_ERR_MSG);
167 *errmsg = _err_get_msg(dr->err);
168 };
169 return 1;
170 };
171 /*
172 * Attempt to open the directory.
173 */
174 dir = opendir(path);
175 if(!dir) {
176 if(errmsg) {
177 _err_record_msg(dr->err, "Can't open directory: ", path, END_ERR_MSG);
178 *errmsg = _err_get_msg(dr->err);
179 };
180 return 1;
181 };
182 /*
183 * If using POSIX threads, allocate a buffer for readdir_r().
184 */
185 #ifdef USE_READDIR_R
186 {
187 size_t size;
188 int name_max = pathconf(path, _PC_NAME_MAX);
189 #ifdef NAME_MAX
190 if(name_max < 0)
191 name_max = NAME_MAX;
192 #endif
193 if(name_max < 0) {
194 if(errmsg) {
195 _err_record_msg(dr->err, "Unable to deduce readdir() buffer size.",
196 END_ERR_MSG);
197 *errmsg = _err_get_msg(dr->err);
198 };
199 closedir(dir);
200 return 1;
201 };
202 /*
203 * How big a buffer do we need to allocate?
204 */
205 size = sizeof(struct dirent) + name_max;
206 /*
207 * Extend the buffer?
208 */
209 if(size > dr->buffer_dim || !dr->buffer) {
210 struct dirent *buffer = (struct dirent *) (dr->buffer ?
211 realloc(dr->buffer, size) :
212 malloc(size));
213 if(!buffer) {
214 if(errmsg) {
215 _err_record_msg(dr->err, "Insufficient memory for readdir() buffer.",
216 END_ERR_MSG);
217 *errmsg = _err_get_msg(dr->err);
218 };
219 closedir(dir);
220 errno = ENOMEM;
221 return 1;
222 };
223 dr->buffer = buffer;
224 dr->buffer_dim = size;
225 };
226 };
227 #endif
228 /*
229 * Record the successfully opened directory.
230 */
231 dr->dir = dir;
232 return 0;
233 }
234
235 /*.......................................................................
236 * If the DirReader object is currently contains an open directory,
237 * close it.
238 *
239 * Input:
240 * dr DirReader * The directory reader resource object.
241 */
_dr_close_dir(DirReader * dr)242 void _dr_close_dir(DirReader *dr)
243 {
244 if(dr && dr->dir) {
245 closedir(dr->dir);
246 dr->dir = NULL;
247 dr->file = NULL;
248 _err_clear_msg(dr->err);
249 };
250 }
251
252 /*.......................................................................
253 * Read the next file from the directory opened with _dr_open_dir().
254 *
255 * Input:
256 * dr DirReader * The directory reader resource object.
257 * Output:
258 * return char * The name of the new file, or NULL if we reached
259 * the end of the directory.
260 */
_dr_next_file(DirReader * dr)261 char *_dr_next_file(DirReader *dr)
262 {
263 /*
264 * Are we currently reading a directory?
265 */
266 if(dr->dir) {
267 /*
268 * Read the next directory entry.
269 */
270 #ifdef USE_READDIR_R
271 if(readdir_r(dr->dir, dr->buffer, &dr->file) == 0 && dr->file)
272 return dr->file->d_name;
273 #else
274 dr->file = readdir(dr->dir);
275 if(dr->file)
276 return dr->file->d_name;
277 #endif
278 };
279 /*
280 * When the end of a directory is reached, close it.
281 */
282 _dr_close_dir(dr);
283 return NULL;
284 }
285
286 /*.......................................................................
287 * Return 1 if the specified pathname refers to a directory.
288 *
289 * Input:
290 * pathname const char * The path to test.
291 * Output:
292 * return int 0 - Not a directory.
293 * 1 - pathname[] refers to a directory.
294 */
_dr_path_is_dir(const char * pathname)295 static int _dr_path_is_dir(const char *pathname)
296 {
297 struct stat statbuf; /* The file-statistics return buffer */
298 /*
299 * Look up the file attributes.
300 */
301 if(stat(pathname, &statbuf) < 0)
302 return 0;
303 /*
304 * Is the file a directory?
305 */
306 return S_ISDIR(statbuf.st_mode) != 0;
307 }
308
309 #endif /* ifndef WITHOUT_FILE_SYSTEM */
310