/* * 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 #include #include #include /* * Operating system includes. */ #include #include #include #include #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 */