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