1 /* $NetBSD: scan_dir.c,v 1.1.1.1 2009/06/23 10:09:00 tron 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 65 /* System library. */ 66 67 #include <sys_defs.h> 68 #ifdef HAVE_DIRENT_H 69 #include <dirent.h> 70 #else 71 #define dirent direct 72 #ifdef HAVE_SYS_NDIR_H 73 #include <sys/ndir.h> 74 #endif 75 #ifdef HAVE_SYS_DIR_H 76 #include <sys/dir.h> 77 #endif 78 #ifdef HAVE_NDIR_H 79 #include <ndir.h> 80 #endif 81 #endif 82 #include <string.h> 83 84 /* Utility library. */ 85 86 #include "msg.h" 87 #include "mymalloc.h" 88 #include "stringops.h" 89 #include "vstring.h" 90 #include "scan_dir.h" 91 92 /* 93 * The interface is based on an opaque structure, so we don't have to expose 94 * the user to the guts. Subdirectory info sits in front of parent directory 95 * info: a simple last-in, first-out list. 96 */ 97 typedef struct SCAN_INFO SCAN_INFO; 98 99 struct SCAN_INFO { 100 char *path; /* directory name */ 101 DIR *dir; /* directory structure */ 102 SCAN_INFO *parent; /* linkage */ 103 }; 104 struct SCAN_DIR { 105 SCAN_INFO *current; /* current scan */ 106 }; 107 108 #define SCAN_DIR_PATH(scan) (scan->current->path) 109 #define STR(x) vstring_str(x) 110 111 /* scan_dir_path - return the path of the directory being read. */ 112 113 char *scan_dir_path(SCAN_DIR *scan) 114 { 115 return (SCAN_DIR_PATH(scan)); 116 } 117 118 /* scan_dir_push - enter directory */ 119 120 void scan_dir_push(SCAN_DIR *scan, const char *path) 121 { 122 const char *myname = "scan_dir_push"; 123 SCAN_INFO *info; 124 125 info = (SCAN_INFO *) mymalloc(sizeof(*info)); 126 if (scan->current) 127 info->path = concatenate(SCAN_DIR_PATH(scan), "/", path, (char *) 0); 128 else 129 info->path = mystrdup(path); 130 if ((info->dir = opendir(info->path)) == 0) 131 msg_fatal("%s: open directory %s: %m", myname, info->path); 132 if (msg_verbose > 1) 133 msg_info("%s: open %s", myname, info->path); 134 info->parent = scan->current; 135 scan->current = info; 136 } 137 138 /* scan_dir_pop - leave directory */ 139 140 SCAN_DIR *scan_dir_pop(SCAN_DIR *scan) 141 { 142 const char *myname = "scan_dir_pop"; 143 SCAN_INFO *info = scan->current; 144 SCAN_INFO *parent; 145 146 if (info == 0) 147 return (0); 148 parent = info->parent; 149 if (closedir(info->dir)) 150 msg_fatal("%s: close directory %s: %m", myname, info->path); 151 if (msg_verbose > 1) 152 msg_info("%s: close %s", myname, info->path); 153 myfree(info->path); 154 myfree((char *) info); 155 scan->current = parent; 156 return (parent ? scan : 0); 157 } 158 159 /* scan_dir_open - start directory scan */ 160 161 SCAN_DIR *scan_dir_open(const char *path) 162 { 163 SCAN_DIR *scan; 164 165 scan = (SCAN_DIR *) mymalloc(sizeof(*scan)); 166 scan->current = 0; 167 scan_dir_push(scan, path); 168 return (scan); 169 } 170 171 /* scan_dir_next - find next entry */ 172 173 char *scan_dir_next(SCAN_DIR *scan) 174 { 175 const char *myname = "scan_dir_next"; 176 SCAN_INFO *info = scan->current; 177 struct dirent *dp; 178 179 #define STREQ(x,y) (strcmp((x),(y)) == 0) 180 181 if (info) { 182 while ((dp = readdir(info->dir)) != 0) { 183 if (STREQ(dp->d_name, ".") || STREQ(dp->d_name, "..")) { 184 if (msg_verbose > 1) 185 msg_info("%s: skip %s", myname, dp->d_name); 186 continue; 187 } else { 188 if (msg_verbose > 1) 189 msg_info("%s: found %s", myname, dp->d_name); 190 return (dp->d_name); 191 } 192 } 193 } 194 return (0); 195 } 196 197 /* scan_dir_close - terminate directory scan */ 198 199 SCAN_DIR *scan_dir_close(SCAN_DIR *scan) 200 { 201 while (scan->current) 202 scan_dir_pop(scan); 203 myfree((char *) scan); 204 return (0); 205 } 206