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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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