Home | History | Annotate | Line # | Download | only in util
      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 
    119 char   *scan_dir_path(SCAN_DIR *scan)
    120 {
    121     return (SCAN_DIR_PATH(scan));
    122 }
    123 
    124 /* scan_dir_push - enter directory */
    125 
    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 
    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 
    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 
    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 
    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