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
scan_dir_path(SCAN_DIR * scan)119 char *scan_dir_path(SCAN_DIR *scan)
120 {
121 return (SCAN_DIR_PATH(scan));
122 }
123
124 /* scan_dir_push - enter directory */
125
scan_dir_push(SCAN_DIR * scan,const char * path)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
scan_dir_pop(SCAN_DIR * scan)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
scan_dir_open(const char * path)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
scan_dir_next(SCAN_DIR * scan)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
scan_dir_close(SCAN_DIR * scan)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