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