xref: /netbsd-src/external/ibm-public/postfix/dist/src/util/scan_dir.c (revision e89934bbf778a6d6d6894877c4da59d0c7835b0f)
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