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