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