xref: /netbsd-src/external/ibm-public/postfix/dist/src/util/scan_dir.c (revision e89934bbf778a6d6d6894877c4da59d0c7835b0f)
1*e89934bbSchristos /*	$NetBSD: scan_dir.c,v 1.2 2017/02/14 01:16:49 christos Exp $	*/
241fbaed0Stron 
341fbaed0Stron /*++
441fbaed0Stron /* NAME
541fbaed0Stron /*	scan_dir 3
641fbaed0Stron /* SUMMARY
741fbaed0Stron /*	directory scanning
841fbaed0Stron /* SYNOPSIS
941fbaed0Stron /*	#include <scan_dir.h>
1041fbaed0Stron /*
1141fbaed0Stron /*	SCAN_DIR *scan_dir_open(path)
1241fbaed0Stron /*	const char *path;
1341fbaed0Stron /*
1441fbaed0Stron /*	char	*scan_dir_next(scan)
1541fbaed0Stron /*	SCAN_DIR *scan;
1641fbaed0Stron /*
1741fbaed0Stron /*	char	*scan_dir_path(scan)
1841fbaed0Stron /*	SCAN_DIR *scan;
1941fbaed0Stron /*
2041fbaed0Stron /*	void	scan_push(scan, entry)
2141fbaed0Stron /*	SCAN_DIR *scan;
2241fbaed0Stron /*	const char *entry;
2341fbaed0Stron /*
2441fbaed0Stron /*	SCAN_DIR *scan_pop(scan)
2541fbaed0Stron /*	SCAN_DIR *scan;
2641fbaed0Stron /*
2741fbaed0Stron /*	SCAN_DIR *scan_dir_close(scan)
2841fbaed0Stron /*	SCAN_DIR *scan;
2941fbaed0Stron /* DESCRIPTION
3041fbaed0Stron /*	These functions scan directories for names. The "." and
3141fbaed0Stron /*	".." names are skipped. Essentially, this is <dirent>
3241fbaed0Stron /*	extended with error handling and with knowledge of the
3341fbaed0Stron /*	name of the directory being scanned.
3441fbaed0Stron /*
3541fbaed0Stron /*	scan_dir_open() opens the named directory and
3641fbaed0Stron /*	returns a handle for subsequent use.
3741fbaed0Stron /*
3841fbaed0Stron /*	scan_dir_close() terminates the directory scan, cleans up
3941fbaed0Stron /*	and returns a null pointer.
4041fbaed0Stron /*
4141fbaed0Stron /*	scan_dir_next() returns the next requested object in the specified
4241fbaed0Stron /*	directory. It skips the "." and ".." entries.
4341fbaed0Stron /*
4441fbaed0Stron /*	scan_dir_path() returns the name of the directory being scanned.
4541fbaed0Stron /*
4641fbaed0Stron /*	scan_dir_push() causes the specified directory scan to enter the
4741fbaed0Stron /*	named subdirectory.
4841fbaed0Stron /*
4941fbaed0Stron /*	scan_dir_pop() leaves the directory being scanned and returns
5041fbaed0Stron /*	to the previous one. The result is the argument, null if no
5141fbaed0Stron /*	previous directory information is available.
5241fbaed0Stron /* DIAGNOSTICS
5341fbaed0Stron /*	All errors are fatal.
5441fbaed0Stron /* LICENSE
5541fbaed0Stron /* .ad
5641fbaed0Stron /* .fi
5741fbaed0Stron /*	The Secure Mailer license must be distributed with this software.
5841fbaed0Stron /* AUTHOR(S)
5941fbaed0Stron /*	Wietse Venema
6041fbaed0Stron /*	IBM T.J. Watson Research
6141fbaed0Stron /*	P.O. Box 704
6241fbaed0Stron /*	Yorktown Heights, NY 10598, USA
63e262b48eSchristos /*
64e262b48eSchristos /*	Wietse Venema
65e262b48eSchristos /*	Google, Inc.
66e262b48eSchristos /*	111 8th Avenue
67e262b48eSchristos /*	New York, NY 10011, USA
6841fbaed0Stron /*--*/
6941fbaed0Stron 
7041fbaed0Stron /* System library. */
7141fbaed0Stron 
7241fbaed0Stron #include <sys_defs.h>
7341fbaed0Stron #ifdef HAVE_DIRENT_H
7441fbaed0Stron #include <dirent.h>
7541fbaed0Stron #else
7641fbaed0Stron #define dirent direct
7741fbaed0Stron #ifdef HAVE_SYS_NDIR_H
7841fbaed0Stron #include <sys/ndir.h>
7941fbaed0Stron #endif
8041fbaed0Stron #ifdef HAVE_SYS_DIR_H
8141fbaed0Stron #include <sys/dir.h>
8241fbaed0Stron #endif
8341fbaed0Stron #ifdef HAVE_NDIR_H
8441fbaed0Stron #include <ndir.h>
8541fbaed0Stron #endif
8641fbaed0Stron #endif
8741fbaed0Stron #include <string.h>
88e262b48eSchristos #include <errno.h>
8941fbaed0Stron 
9041fbaed0Stron /* Utility library. */
9141fbaed0Stron 
9241fbaed0Stron #include "msg.h"
9341fbaed0Stron #include "mymalloc.h"
9441fbaed0Stron #include "stringops.h"
9541fbaed0Stron #include "vstring.h"
9641fbaed0Stron #include "scan_dir.h"
9741fbaed0Stron 
9841fbaed0Stron  /*
9941fbaed0Stron   * The interface is based on an opaque structure, so we don't have to expose
10041fbaed0Stron   * the user to the guts. Subdirectory info sits in front of parent directory
10141fbaed0Stron   * info: a simple last-in, first-out list.
10241fbaed0Stron   */
10341fbaed0Stron typedef struct SCAN_INFO SCAN_INFO;
10441fbaed0Stron 
10541fbaed0Stron struct SCAN_INFO {
10641fbaed0Stron     char   *path;			/* directory name */
10741fbaed0Stron     DIR    *dir;			/* directory structure */
10841fbaed0Stron     SCAN_INFO *parent;			/* linkage */
10941fbaed0Stron };
11041fbaed0Stron struct SCAN_DIR {
11141fbaed0Stron     SCAN_INFO *current;			/* current scan */
11241fbaed0Stron };
11341fbaed0Stron 
11441fbaed0Stron #define SCAN_DIR_PATH(scan)	(scan->current->path)
11541fbaed0Stron #define STR(x)			vstring_str(x)
11641fbaed0Stron 
11741fbaed0Stron /* scan_dir_path - return the path of the directory being read.  */
11841fbaed0Stron 
scan_dir_path(SCAN_DIR * scan)11941fbaed0Stron char   *scan_dir_path(SCAN_DIR *scan)
12041fbaed0Stron {
12141fbaed0Stron     return (SCAN_DIR_PATH(scan));
12241fbaed0Stron }
12341fbaed0Stron 
12441fbaed0Stron /* scan_dir_push - enter directory */
12541fbaed0Stron 
scan_dir_push(SCAN_DIR * scan,const char * path)12641fbaed0Stron void    scan_dir_push(SCAN_DIR *scan, const char *path)
12741fbaed0Stron {
12841fbaed0Stron     const char *myname = "scan_dir_push";
12941fbaed0Stron     SCAN_INFO *info;
13041fbaed0Stron 
13141fbaed0Stron     info = (SCAN_INFO *) mymalloc(sizeof(*info));
13241fbaed0Stron     if (scan->current)
13341fbaed0Stron 	info->path = concatenate(SCAN_DIR_PATH(scan), "/", path, (char *) 0);
13441fbaed0Stron     else
13541fbaed0Stron 	info->path = mystrdup(path);
13641fbaed0Stron     if ((info->dir = opendir(info->path)) == 0)
13741fbaed0Stron 	msg_fatal("%s: open directory %s: %m", myname, info->path);
13841fbaed0Stron     if (msg_verbose > 1)
13941fbaed0Stron 	msg_info("%s: open %s", myname, info->path);
14041fbaed0Stron     info->parent = scan->current;
14141fbaed0Stron     scan->current = info;
14241fbaed0Stron }
14341fbaed0Stron 
14441fbaed0Stron /* scan_dir_pop - leave directory */
14541fbaed0Stron 
scan_dir_pop(SCAN_DIR * scan)14641fbaed0Stron SCAN_DIR *scan_dir_pop(SCAN_DIR *scan)
14741fbaed0Stron {
14841fbaed0Stron     const char *myname = "scan_dir_pop";
14941fbaed0Stron     SCAN_INFO *info = scan->current;
15041fbaed0Stron     SCAN_INFO *parent;
15141fbaed0Stron 
15241fbaed0Stron     if (info == 0)
15341fbaed0Stron 	return (0);
15441fbaed0Stron     parent = info->parent;
15541fbaed0Stron     if (closedir(info->dir))
15641fbaed0Stron 	msg_fatal("%s: close directory %s: %m", myname, info->path);
15741fbaed0Stron     if (msg_verbose > 1)
15841fbaed0Stron 	msg_info("%s: close %s", myname, info->path);
15941fbaed0Stron     myfree(info->path);
160e262b48eSchristos     myfree((void *) info);
16141fbaed0Stron     scan->current = parent;
16241fbaed0Stron     return (parent ? scan : 0);
16341fbaed0Stron }
16441fbaed0Stron 
16541fbaed0Stron /* scan_dir_open - start directory scan */
16641fbaed0Stron 
scan_dir_open(const char * path)16741fbaed0Stron SCAN_DIR *scan_dir_open(const char *path)
16841fbaed0Stron {
16941fbaed0Stron     SCAN_DIR *scan;
17041fbaed0Stron 
17141fbaed0Stron     scan = (SCAN_DIR *) mymalloc(sizeof(*scan));
17241fbaed0Stron     scan->current = 0;
17341fbaed0Stron     scan_dir_push(scan, path);
17441fbaed0Stron     return (scan);
17541fbaed0Stron }
17641fbaed0Stron 
17741fbaed0Stron /* scan_dir_next - find next entry */
17841fbaed0Stron 
scan_dir_next(SCAN_DIR * scan)17941fbaed0Stron char   *scan_dir_next(SCAN_DIR *scan)
18041fbaed0Stron {
18141fbaed0Stron     const char *myname = "scan_dir_next";
18241fbaed0Stron     SCAN_INFO *info = scan->current;
18341fbaed0Stron     struct dirent *dp;
18441fbaed0Stron 
18541fbaed0Stron #define STREQ(x,y)	(strcmp((x),(y)) == 0)
18641fbaed0Stron 
18741fbaed0Stron     if (info) {
188e262b48eSchristos 
189e262b48eSchristos 	/*
190e262b48eSchristos 	 * Fix 20150421: readdir() does not reset errno after reaching the
191e262b48eSchristos 	 * end-of-directory. This dates back all the way to the initial
192e262b48eSchristos 	 * implementation of 19970309.
193e262b48eSchristos 	 */
194e262b48eSchristos 	errno = 0;
19541fbaed0Stron 	while ((dp = readdir(info->dir)) != 0) {
19641fbaed0Stron 	    if (STREQ(dp->d_name, ".") || STREQ(dp->d_name, "..")) {
19741fbaed0Stron 		if (msg_verbose > 1)
19841fbaed0Stron 		    msg_info("%s: skip %s", myname, dp->d_name);
19941fbaed0Stron 		continue;
20041fbaed0Stron 	    } else {
20141fbaed0Stron 		if (msg_verbose > 1)
20241fbaed0Stron 		    msg_info("%s: found %s", myname, dp->d_name);
20341fbaed0Stron 		return (dp->d_name);
20441fbaed0Stron 	    }
20541fbaed0Stron 	}
20641fbaed0Stron     }
20741fbaed0Stron     return (0);
20841fbaed0Stron }
20941fbaed0Stron 
21041fbaed0Stron /* scan_dir_close - terminate directory scan */
21141fbaed0Stron 
scan_dir_close(SCAN_DIR * scan)21241fbaed0Stron SCAN_DIR *scan_dir_close(SCAN_DIR *scan)
21341fbaed0Stron {
21441fbaed0Stron     while (scan->current)
21541fbaed0Stron 	scan_dir_pop(scan);
216e262b48eSchristos     myfree((void *) scan);
21741fbaed0Stron     return (0);
21841fbaed0Stron }
219