/*++ /* NAME /* scan_dir 3 /* SUMMARY /* directory scanning /* SYNOPSIS /* #include /* /* 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 /* 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 #ifdef HAVE_DIRENT_H #include #else #define dirent direct #ifdef HAVE_SYS_NDIR_H #include #endif #ifdef HAVE_SYS_DIR_H #include #endif #ifdef HAVE_NDIR_H #include #endif #endif #include #include /* 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); }