diff options
Diffstat (limited to 'src/util/scan_dir.c')
-rw-r--r-- | src/util/scan_dir.c | 216 |
1 files changed, 216 insertions, 0 deletions
diff --git a/src/util/scan_dir.c b/src/util/scan_dir.c new file mode 100644 index 0000000..d94c674 --- /dev/null +++ b/src/util/scan_dir.c @@ -0,0 +1,216 @@ +/*++ +/* NAME +/* scan_dir 3 +/* SUMMARY +/* directory scanning +/* SYNOPSIS +/* #include <scan_dir.h> +/* +/* SCAN_DIR *scan_dir_open(path) +/* const char *path; +/* +/* char *scan_dir_next(scan) +/* SCAN_DIR *scan; +/* +/* char *scan_dir_path(scan) +/* SCAN_DIR *scan; +/* +/* void scan_push(scan, entry) +/* SCAN_DIR *scan; +/* const char *entry; +/* +/* SCAN_DIR *scan_pop(scan) +/* SCAN_DIR *scan; +/* +/* SCAN_DIR *scan_dir_close(scan) +/* SCAN_DIR *scan; +/* DESCRIPTION +/* These functions scan directories for names. The "." and +/* ".." names are skipped. Essentially, this is <dirent> +/* extended with error handling and with knowledge of the +/* name of the directory being scanned. +/* +/* scan_dir_open() opens the named directory and +/* returns a handle for subsequent use. +/* +/* scan_dir_close() terminates the directory scan, cleans up +/* and returns a null pointer. +/* +/* scan_dir_next() returns the next requested object in the specified +/* directory. It skips the "." and ".." entries. +/* +/* scan_dir_path() returns the name of the directory being scanned. +/* +/* scan_dir_push() causes the specified directory scan to enter the +/* named subdirectory. +/* +/* scan_dir_pop() leaves the directory being scanned and returns +/* to the previous one. The result is the argument, null if no +/* previous directory information is available. +/* DIAGNOSTICS +/* All errors are fatal. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/* +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA +/*--*/ + +/* System library. */ + +#include <sys_defs.h> +#ifdef HAVE_DIRENT_H +#include <dirent.h> +#else +#define dirent direct +#ifdef HAVE_SYS_NDIR_H +#include <sys/ndir.h> +#endif +#ifdef HAVE_SYS_DIR_H +#include <sys/dir.h> +#endif +#ifdef HAVE_NDIR_H +#include <ndir.h> +#endif +#endif +#include <string.h> +#include <errno.h> + +/* Utility library. */ + +#include "msg.h" +#include "mymalloc.h" +#include "stringops.h" +#include "vstring.h" +#include "scan_dir.h" + + /* + * The interface is based on an opaque structure, so we don't have to expose + * the user to the guts. Subdirectory info sits in front of parent directory + * info: a simple last-in, first-out list. + */ +typedef struct SCAN_INFO SCAN_INFO; + +struct SCAN_INFO { + char *path; /* directory name */ + DIR *dir; /* directory structure */ + SCAN_INFO *parent; /* linkage */ +}; +struct SCAN_DIR { + SCAN_INFO *current; /* current scan */ +}; + +#define SCAN_DIR_PATH(scan) (scan->current->path) +#define STR(x) vstring_str(x) + +/* scan_dir_path - return the path of the directory being read. */ + +char *scan_dir_path(SCAN_DIR *scan) +{ + return (SCAN_DIR_PATH(scan)); +} + +/* scan_dir_push - enter directory */ + +void scan_dir_push(SCAN_DIR *scan, const char *path) +{ + const char *myname = "scan_dir_push"; + SCAN_INFO *info; + + info = (SCAN_INFO *) mymalloc(sizeof(*info)); + if (scan->current) + info->path = concatenate(SCAN_DIR_PATH(scan), "/", path, (char *) 0); + else + info->path = mystrdup(path); + if ((info->dir = opendir(info->path)) == 0) + msg_fatal("%s: open directory %s: %m", myname, info->path); + if (msg_verbose > 1) + msg_info("%s: open %s", myname, info->path); + info->parent = scan->current; + scan->current = info; +} + +/* scan_dir_pop - leave directory */ + +SCAN_DIR *scan_dir_pop(SCAN_DIR *scan) +{ + const char *myname = "scan_dir_pop"; + SCAN_INFO *info = scan->current; + SCAN_INFO *parent; + + if (info == 0) + return (0); + parent = info->parent; + if (closedir(info->dir)) + msg_fatal("%s: close directory %s: %m", myname, info->path); + if (msg_verbose > 1) + msg_info("%s: close %s", myname, info->path); + myfree(info->path); + myfree((void *) info); + scan->current = parent; + return (parent ? scan : 0); +} + +/* scan_dir_open - start directory scan */ + +SCAN_DIR *scan_dir_open(const char *path) +{ + SCAN_DIR *scan; + + scan = (SCAN_DIR *) mymalloc(sizeof(*scan)); + scan->current = 0; + scan_dir_push(scan, path); + return (scan); +} + +/* scan_dir_next - find next entry */ + +char *scan_dir_next(SCAN_DIR *scan) +{ + const char *myname = "scan_dir_next"; + SCAN_INFO *info = scan->current; + struct dirent *dp; + +#define STREQ(x,y) (strcmp((x),(y)) == 0) + + if (info) { + + /* + * Fix 20150421: readdir() does not reset errno after reaching the + * end-of-directory. This dates back all the way to the initial + * implementation of 19970309. + */ + errno = 0; + while ((dp = readdir(info->dir)) != 0) { + if (STREQ(dp->d_name, ".") || STREQ(dp->d_name, "..")) { + if (msg_verbose > 1) + msg_info("%s: skip %s", myname, dp->d_name); + continue; + } else { + if (msg_verbose > 1) + msg_info("%s: found %s", myname, dp->d_name); + return (dp->d_name); + } + } + } + return (0); +} + +/* scan_dir_close - terminate directory scan */ + +SCAN_DIR *scan_dir_close(SCAN_DIR *scan) +{ + while (scan->current) + scan_dir_pop(scan); + myfree((void *) scan); + return (0); +} |