1 /* $NetBSD: scan_dir.c,v 1.2 2017/02/14 01:16:49 christos Exp $ */ 2 3 /*++ 4 /* NAME 5 /* scan_dir 3 6 /* SUMMARY 7 /* directory scanning 8 /* SYNOPSIS 9 /* #include <scan_dir.h> 10 /* 11 /* SCAN_DIR *scan_dir_open(path) 12 /* const char *path; 13 /* 14 /* char *scan_dir_next(scan) 15 /* SCAN_DIR *scan; 16 /* 17 /* char *scan_dir_path(scan) 18 /* SCAN_DIR *scan; 19 /* 20 /* void scan_push(scan, entry) 21 /* SCAN_DIR *scan; 22 /* const char *entry; 23 /* 24 /* SCAN_DIR *scan_pop(scan) 25 /* SCAN_DIR *scan; 26 /* 27 /* SCAN_DIR *scan_dir_close(scan) 28 /* SCAN_DIR *scan; 29 /* DESCRIPTION 30 /* These functions scan directories for names. The "." and 31 /* ".." names are skipped. Essentially, this is <dirent> 32 /* extended with error handling and with knowledge of the 33 /* name of the directory being scanned. 34 /* 35 /* scan_dir_open() opens the named directory and 36 /* returns a handle for subsequent use. 37 /* 38 /* scan_dir_close() terminates the directory scan, cleans up 39 /* and returns a null pointer. 40 /* 41 /* scan_dir_next() returns the next requested object in the specified 42 /* directory. It skips the "." and ".." entries. 43 /* 44 /* scan_dir_path() returns the name of the directory being scanned. 45 /* 46 /* scan_dir_push() causes the specified directory scan to enter the 47 /* named subdirectory. 48 /* 49 /* scan_dir_pop() leaves the directory being scanned and returns 50 /* to the previous one. The result is the argument, null if no 51 /* previous directory information is available. 52 /* DIAGNOSTICS 53 /* All errors are fatal. 54 /* LICENSE 55 /* .ad 56 /* .fi 57 /* The Secure Mailer license must be distributed with this software. 58 /* AUTHOR(S) 59 /* Wietse Venema 60 /* IBM T.J. Watson Research 61 /* P.O. Box 704 62 /* Yorktown Heights, NY 10598, USA 63 /* 64 /* Wietse Venema 65 /* Google, Inc. 66 /* 111 8th Avenue 67 /* New York, NY 10011, USA 68 /*--*/ 69 70 /* System library. */ 71 72 #include <sys_defs.h> 73 #ifdef HAVE_DIRENT_H 74 #include <dirent.h> 75 #else 76 #define dirent direct 77 #ifdef HAVE_SYS_NDIR_H 78 #include <sys/ndir.h> 79 #endif 80 #ifdef HAVE_SYS_DIR_H 81 #include <sys/dir.h> 82 #endif 83 #ifdef HAVE_NDIR_H 84 #include <ndir.h> 85 #endif 86 #endif 87 #include <string.h> 88 #include <errno.h> 89 90 /* Utility library. */ 91 92 #include "msg.h" 93 #include "mymalloc.h" 94 #include "stringops.h" 95 #include "vstring.h" 96 #include "scan_dir.h" 97 98 /* 99 * The interface is based on an opaque structure, so we don't have to expose 100 * the user to the guts. Subdirectory info sits in front of parent directory 101 * info: a simple last-in, first-out list. 102 */ 103 typedef struct SCAN_INFO SCAN_INFO; 104 105 struct SCAN_INFO { 106 char *path; /* directory name */ 107 DIR *dir; /* directory structure */ 108 SCAN_INFO *parent; /* linkage */ 109 }; 110 struct SCAN_DIR { 111 SCAN_INFO *current; /* current scan */ 112 }; 113 114 #define SCAN_DIR_PATH(scan) (scan->current->path) 115 #define STR(x) vstring_str(x) 116 117 /* scan_dir_path - return the path of the directory being read. */ 118 119 char *scan_dir_path(SCAN_DIR *scan) 120 { 121 return (SCAN_DIR_PATH(scan)); 122 } 123 124 /* scan_dir_push - enter directory */ 125 126 void scan_dir_push(SCAN_DIR *scan, const char *path) 127 { 128 const char *myname = "scan_dir_push"; 129 SCAN_INFO *info; 130 131 info = (SCAN_INFO *) mymalloc(sizeof(*info)); 132 if (scan->current) 133 info->path = concatenate(SCAN_DIR_PATH(scan), "/", path, (char *) 0); 134 else 135 info->path = mystrdup(path); 136 if ((info->dir = opendir(info->path)) == 0) 137 msg_fatal("%s: open directory %s: %m", myname, info->path); 138 if (msg_verbose > 1) 139 msg_info("%s: open %s", myname, info->path); 140 info->parent = scan->current; 141 scan->current = info; 142 } 143 144 /* scan_dir_pop - leave directory */ 145 146 SCAN_DIR *scan_dir_pop(SCAN_DIR *scan) 147 { 148 const char *myname = "scan_dir_pop"; 149 SCAN_INFO *info = scan->current; 150 SCAN_INFO *parent; 151 152 if (info == 0) 153 return (0); 154 parent = info->parent; 155 if (closedir(info->dir)) 156 msg_fatal("%s: close directory %s: %m", myname, info->path); 157 if (msg_verbose > 1) 158 msg_info("%s: close %s", myname, info->path); 159 myfree(info->path); 160 myfree((void *) info); 161 scan->current = parent; 162 return (parent ? scan : 0); 163 } 164 165 /* scan_dir_open - start directory scan */ 166 167 SCAN_DIR *scan_dir_open(const char *path) 168 { 169 SCAN_DIR *scan; 170 171 scan = (SCAN_DIR *) mymalloc(sizeof(*scan)); 172 scan->current = 0; 173 scan_dir_push(scan, path); 174 return (scan); 175 } 176 177 /* scan_dir_next - find next entry */ 178 179 char *scan_dir_next(SCAN_DIR *scan) 180 { 181 const char *myname = "scan_dir_next"; 182 SCAN_INFO *info = scan->current; 183 struct dirent *dp; 184 185 #define STREQ(x,y) (strcmp((x),(y)) == 0) 186 187 if (info) { 188 189 /* 190 * Fix 20150421: readdir() does not reset errno after reaching the 191 * end-of-directory. This dates back all the way to the initial 192 * implementation of 19970309. 193 */ 194 errno = 0; 195 while ((dp = readdir(info->dir)) != 0) { 196 if (STREQ(dp->d_name, ".") || STREQ(dp->d_name, "..")) { 197 if (msg_verbose > 1) 198 msg_info("%s: skip %s", myname, dp->d_name); 199 continue; 200 } else { 201 if (msg_verbose > 1) 202 msg_info("%s: found %s", myname, dp->d_name); 203 return (dp->d_name); 204 } 205 } 206 } 207 return (0); 208 } 209 210 /* scan_dir_close - terminate directory scan */ 211 212 SCAN_DIR *scan_dir_close(SCAN_DIR *scan) 213 { 214 while (scan->current) 215 scan_dir_pop(scan); 216 myfree((void *) scan); 217 return (0); 218 } 219