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 #pragma ident "%Z%%M% %I% %E% SMI"
33
34 /*
35 * If file-system access is to be excluded, this module has no function,
36 * so all of its code should be excluded.
37 */
38 #ifndef WITHOUT_FILE_SYSTEM
39
40 /*
41 * Standard includes.
42 */
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <errno.h>
47
48 /*
49 * Operating system includes.
50 */
51 #include <unistd.h>
52 #include <sys/types.h>
53 #include <sys/stat.h>
54 #include <dirent.h>
55
56 #include "direader.h"
57 #include "errmsg.h"
58
59 /*
60 * Use the reentrant POSIX threads version of readdir()?
61 */
62 #if defined(PREFER_REENTRANT) && defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199506L
63 #define USE_READDIR_R 1
64 #endif
65
66 /*
67 * Objects of the following type are used to maintain the resources
68 * needed to read directories.
69 */
70 struct DirReader {
71 ErrMsg *err; /* The error reporting buffer */
72 DIR *dir; /* The directory stream (if open, NULL otherwise) */
73 struct dirent *file; /* The latest directory entry */
74 #ifdef USE_READDIR_R
75 struct dirent *buffer; /* A buffer used by the threaded version of */
76 /* readdir() */
77 int buffer_dim; /* The allocated size of buffer[] */
78 #endif
79 };
80
81 static int _dr_path_is_dir(const char *pathname);
82
83 /*.......................................................................
84 * Create a new DirReader object.
85 *
86 * Output:
87 * return DirReader * The new object, or NULL on error.
88 */
_new_DirReader(void)89 DirReader *_new_DirReader(void)
90 {
91 DirReader *dr; /* The object to be returned */
92 /*
93 * Allocate the container.
94 */
95 dr = (DirReader *) malloc(sizeof(DirReader));
96 if(!dr) {
97 errno = ENOMEM;
98 return NULL;
99 };
100 /*
101 * Before attempting any operation that might fail, initialize the
102 * container at least up to the point at which it can safely be passed
103 * to _del_DirReader().
104 */
105 dr->err = NULL;
106 dr->dir = NULL;
107 dr->file = NULL;
108 #ifdef USE_READDIR_R
109 dr->buffer = NULL;
110 dr->buffer_dim = 0;
111 #endif
112 /*
113 * Allocate a place to record error messages.
114 */
115 dr->err = _new_ErrMsg();
116 if(!dr->err)
117 return _del_DirReader(dr);
118 return dr;
119 }
120
121 /*.......................................................................
122 * Delete a DirReader object.
123 *
124 * Input:
125 * dr DirReader * The object to be deleted.
126 * Output:
127 * return DirReader * The deleted object (always NULL).
128 */
_del_DirReader(DirReader * dr)129 DirReader *_del_DirReader(DirReader *dr)
130 {
131 if(dr) {
132 _dr_close_dir(dr);
133 #ifdef USE_READDIR_R
134 free(dr->buffer);
135 #endif
136 dr->err = _del_ErrMsg(dr->err);
137 free(dr);
138 };
139 return NULL;
140 }
141
142 /*.......................................................................
143 * Open a new directory.
144 *
145 * Input:
146 * dr DirReader * The directory reader resource object.
147 * path const char * The directory to be opened.
148 * Input/Output:
149 * errmsg char ** If an error occurs and errmsg isn't NULL, a
150 * pointer to an error description will be assigned
151 * to *errmsg.
152 * Output:
153 * return int 0 - OK.
154 * 1 - Error (see *errmsg for a description).
155 */
_dr_open_dir(DirReader * dr,const char * path,char ** errmsg)156 int _dr_open_dir(DirReader *dr, const char *path, char **errmsg)
157 {
158 DIR *dir = NULL; /* The directory stream */
159 /*
160 * If a directory is already open, close it first.
161 */
162 (void) _dr_close_dir(dr);
163 /*
164 * Is the path a directory?
165 */
166 if(!_dr_path_is_dir(path)) {
167 if(errmsg) {
168 _err_record_msg(dr->err, "Can't open directory: ", path, END_ERR_MSG);
169 *errmsg = _err_get_msg(dr->err);
170 };
171 return 1;
172 };
173 /*
174 * Attempt to open the directory.
175 */
176 dir = opendir(path);
177 if(!dir) {
178 if(errmsg) {
179 _err_record_msg(dr->err, "Can't open directory: ", path, END_ERR_MSG);
180 *errmsg = _err_get_msg(dr->err);
181 };
182 return 1;
183 };
184 /*
185 * If using POSIX threads, allocate a buffer for readdir_r().
186 */
187 #ifdef USE_READDIR_R
188 {
189 size_t size;
190 int name_max = pathconf(path, _PC_NAME_MAX);
191 #ifdef NAME_MAX
192 if(name_max < 0)
193 name_max = NAME_MAX;
194 #endif
195 if(name_max < 0) {
196 if(errmsg) {
197 _err_record_msg(dr->err, "Unable to deduce readdir() buffer size.",
198 END_ERR_MSG);
199 *errmsg = _err_get_msg(dr->err);
200 };
201 closedir(dir);
202 return 1;
203 };
204 /*
205 * How big a buffer do we need to allocate?
206 */
207 size = sizeof(struct dirent) + name_max;
208 /*
209 * Extend the buffer?
210 */
211 if(size > dr->buffer_dim || !dr->buffer) {
212 struct dirent *buffer = (struct dirent *) (dr->buffer ?
213 realloc(dr->buffer, size) :
214 malloc(size));
215 if(!buffer) {
216 if(errmsg) {
217 _err_record_msg(dr->err, "Insufficient memory for readdir() buffer.",
218 END_ERR_MSG);
219 *errmsg = _err_get_msg(dr->err);
220 };
221 closedir(dir);
222 errno = ENOMEM;
223 return 1;
224 };
225 dr->buffer = buffer;
226 dr->buffer_dim = size;
227 };
228 };
229 #endif
230 /*
231 * Record the successfully opened directory.
232 */
233 dr->dir = dir;
234 return 0;
235 }
236
237 /*.......................................................................
238 * If the DirReader object is currently contains an open directory,
239 * close it.
240 *
241 * Input:
242 * dr DirReader * The directory reader resource object.
243 */
_dr_close_dir(DirReader * dr)244 void _dr_close_dir(DirReader *dr)
245 {
246 if(dr && dr->dir) {
247 closedir(dr->dir);
248 dr->dir = NULL;
249 dr->file = NULL;
250 _err_clear_msg(dr->err);
251 };
252 }
253
254 /*.......................................................................
255 * Read the next file from the directory opened with _dr_open_dir().
256 *
257 * Input:
258 * dr DirReader * The directory reader resource object.
259 * Output:
260 * return char * The name of the new file, or NULL if we reached
261 * the end of the directory.
262 */
_dr_next_file(DirReader * dr)263 char *_dr_next_file(DirReader *dr)
264 {
265 /*
266 * Are we currently reading a directory?
267 */
268 if(dr->dir) {
269 /*
270 * Read the next directory entry.
271 */
272 #ifdef USE_READDIR_R
273 if(readdir_r(dr->dir, dr->buffer, &dr->file) == 0 && dr->file)
274 return dr->file->d_name;
275 #else
276 dr->file = readdir(dr->dir);
277 if(dr->file)
278 return dr->file->d_name;
279 #endif
280 };
281 /*
282 * When the end of a directory is reached, close it.
283 */
284 _dr_close_dir(dr);
285 return NULL;
286 }
287
288 /*.......................................................................
289 * Return 1 if the specified pathname refers to a directory.
290 *
291 * Input:
292 * pathname const char * The path to test.
293 * Output:
294 * return int 0 - Not a directory.
295 * 1 - pathname[] refers to a directory.
296 */
_dr_path_is_dir(const char * pathname)297 static int _dr_path_is_dir(const char *pathname)
298 {
299 struct stat statbuf; /* The file-statistics return buffer */
300 /*
301 * Look up the file attributes.
302 */
303 if(stat(pathname, &statbuf) < 0)
304 return 0;
305 /*
306 * Is the file a directory?
307 */
308 return S_ISDIR(statbuf.st_mode) != 0;
309 }
310
311 #endif /* ifndef WITHOUT_FILE_SYSTEM */
312