diff options
Diffstat (limited to 'libtecla-1.6.1/direader.c')
-rw-r--r-- | libtecla-1.6.1/direader.c | 309 |
1 files changed, 309 insertions, 0 deletions
diff --git a/libtecla-1.6.1/direader.c b/libtecla-1.6.1/direader.c new file mode 100644 index 0000000..68db93f --- /dev/null +++ b/libtecla-1.6.1/direader.c @@ -0,0 +1,309 @@ +/* + * Copyright (c) 2000, 2001, 2002, 2003, 2004 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 */ |