17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd.
3*1da57d55SToomas Soome  *
47c478bd9Sstevel@tonic-gate  * All rights reserved.
5*1da57d55SToomas Soome  *
67c478bd9Sstevel@tonic-gate  * Permission is hereby granted, free of charge, to any person obtaining a
77c478bd9Sstevel@tonic-gate  * copy of this software and associated documentation files (the
87c478bd9Sstevel@tonic-gate  * "Software"), to deal in the Software without restriction, including
97c478bd9Sstevel@tonic-gate  * without limitation the rights to use, copy, modify, merge, publish,
107c478bd9Sstevel@tonic-gate  * distribute, and/or sell copies of the Software, and to permit persons
117c478bd9Sstevel@tonic-gate  * to whom the Software is furnished to do so, provided that the above
127c478bd9Sstevel@tonic-gate  * copyright notice(s) and this permission notice appear in all copies of
137c478bd9Sstevel@tonic-gate  * the Software and that both the above copyright notice(s) and this
147c478bd9Sstevel@tonic-gate  * permission notice appear in supporting documentation.
15*1da57d55SToomas Soome  *
167c478bd9Sstevel@tonic-gate  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
177c478bd9Sstevel@tonic-gate  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
187c478bd9Sstevel@tonic-gate  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
197c478bd9Sstevel@tonic-gate  * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
207c478bd9Sstevel@tonic-gate  * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL
217c478bd9Sstevel@tonic-gate  * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING
227c478bd9Sstevel@tonic-gate  * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
237c478bd9Sstevel@tonic-gate  * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
247c478bd9Sstevel@tonic-gate  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
25*1da57d55SToomas Soome  *
267c478bd9Sstevel@tonic-gate  * Except as contained in this notice, the name of a copyright holder
277c478bd9Sstevel@tonic-gate  * shall not be used in advertising or otherwise to promote the sale, use
287c478bd9Sstevel@tonic-gate  * or other dealings in this Software without prior written authorization
297c478bd9Sstevel@tonic-gate  * of the copyright holder.
307c478bd9Sstevel@tonic-gate  */
317c478bd9Sstevel@tonic-gate 
327c478bd9Sstevel@tonic-gate /*
337c478bd9Sstevel@tonic-gate  * If file-system access is to be excluded, this module has no function,
347c478bd9Sstevel@tonic-gate  * so all of its code should be excluded.
357c478bd9Sstevel@tonic-gate  */
367c478bd9Sstevel@tonic-gate #ifndef WITHOUT_FILE_SYSTEM
377c478bd9Sstevel@tonic-gate 
387c478bd9Sstevel@tonic-gate /*
397c478bd9Sstevel@tonic-gate  * Standard includes.
407c478bd9Sstevel@tonic-gate  */
417c478bd9Sstevel@tonic-gate #include <stdio.h>
427c478bd9Sstevel@tonic-gate #include <stdlib.h>
437c478bd9Sstevel@tonic-gate #include <string.h>
447c478bd9Sstevel@tonic-gate #include <errno.h>
457c478bd9Sstevel@tonic-gate 
467c478bd9Sstevel@tonic-gate /*
477c478bd9Sstevel@tonic-gate  * Operating system includes.
487c478bd9Sstevel@tonic-gate  */
497c478bd9Sstevel@tonic-gate #include <unistd.h>
507c478bd9Sstevel@tonic-gate #include <sys/types.h>
517c478bd9Sstevel@tonic-gate #include <sys/stat.h>
527c478bd9Sstevel@tonic-gate #include <dirent.h>
537c478bd9Sstevel@tonic-gate 
547c478bd9Sstevel@tonic-gate #include "direader.h"
557c478bd9Sstevel@tonic-gate #include "errmsg.h"
567c478bd9Sstevel@tonic-gate 
577c478bd9Sstevel@tonic-gate /*
587c478bd9Sstevel@tonic-gate  * Use the reentrant POSIX threads version of readdir()?
597c478bd9Sstevel@tonic-gate  */
607c478bd9Sstevel@tonic-gate #if defined(PREFER_REENTRANT) && defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199506L
617c478bd9Sstevel@tonic-gate #define USE_READDIR_R 1
627c478bd9Sstevel@tonic-gate #endif
637c478bd9Sstevel@tonic-gate 
647c478bd9Sstevel@tonic-gate /*
657c478bd9Sstevel@tonic-gate  * Objects of the following type are used to maintain the resources
667c478bd9Sstevel@tonic-gate  * needed to read directories.
677c478bd9Sstevel@tonic-gate  */
687c478bd9Sstevel@tonic-gate struct DirReader {
697c478bd9Sstevel@tonic-gate   ErrMsg *err;             /* The error reporting buffer */
707c478bd9Sstevel@tonic-gate   DIR *dir;                /* The directory stream (if open, NULL otherwise) */
717c478bd9Sstevel@tonic-gate   struct dirent *file;     /* The latest directory entry */
727c478bd9Sstevel@tonic-gate #ifdef USE_READDIR_R
737c478bd9Sstevel@tonic-gate   struct dirent *buffer;   /* A buffer used by the threaded version of */
747c478bd9Sstevel@tonic-gate                            /*  readdir() */
757c478bd9Sstevel@tonic-gate   int buffer_dim;          /* The allocated size of buffer[] */
767c478bd9Sstevel@tonic-gate #endif
777c478bd9Sstevel@tonic-gate };
787c478bd9Sstevel@tonic-gate 
797c478bd9Sstevel@tonic-gate static int _dr_path_is_dir(const char *pathname);
807c478bd9Sstevel@tonic-gate 
817c478bd9Sstevel@tonic-gate /*.......................................................................
827c478bd9Sstevel@tonic-gate  * Create a new DirReader object.
837c478bd9Sstevel@tonic-gate  *
847c478bd9Sstevel@tonic-gate  * Output:
857c478bd9Sstevel@tonic-gate  *  return  DirReader *  The new object, or NULL on error.
867c478bd9Sstevel@tonic-gate  */
_new_DirReader(void)877c478bd9Sstevel@tonic-gate DirReader *_new_DirReader(void)
887c478bd9Sstevel@tonic-gate {
897c478bd9Sstevel@tonic-gate   DirReader *dr;  /* The object to be returned */
907c478bd9Sstevel@tonic-gate /*
917c478bd9Sstevel@tonic-gate  * Allocate the container.
927c478bd9Sstevel@tonic-gate  */
937c478bd9Sstevel@tonic-gate   dr = (DirReader *) malloc(sizeof(DirReader));
947c478bd9Sstevel@tonic-gate   if(!dr) {
957c478bd9Sstevel@tonic-gate     errno = ENOMEM;
967c478bd9Sstevel@tonic-gate     return NULL;
977c478bd9Sstevel@tonic-gate   };
987c478bd9Sstevel@tonic-gate /*
997c478bd9Sstevel@tonic-gate  * Before attempting any operation that might fail, initialize the
1007c478bd9Sstevel@tonic-gate  * container at least up to the point at which it can safely be passed
1017c478bd9Sstevel@tonic-gate  * to _del_DirReader().
1027c478bd9Sstevel@tonic-gate  */
1037c478bd9Sstevel@tonic-gate   dr->err = NULL;
1047c478bd9Sstevel@tonic-gate   dr->dir = NULL;
1057c478bd9Sstevel@tonic-gate   dr->file = NULL;
1067c478bd9Sstevel@tonic-gate #ifdef USE_READDIR_R
1077c478bd9Sstevel@tonic-gate   dr->buffer = NULL;
1087c478bd9Sstevel@tonic-gate   dr->buffer_dim = 0;
1097c478bd9Sstevel@tonic-gate #endif
1107c478bd9Sstevel@tonic-gate /*
1117c478bd9Sstevel@tonic-gate  * Allocate a place to record error messages.
1127c478bd9Sstevel@tonic-gate  */
1137c478bd9Sstevel@tonic-gate   dr->err = _new_ErrMsg();
1147c478bd9Sstevel@tonic-gate   if(!dr->err)
1157c478bd9Sstevel@tonic-gate     return _del_DirReader(dr);
1167c478bd9Sstevel@tonic-gate   return dr;
1177c478bd9Sstevel@tonic-gate }
1187c478bd9Sstevel@tonic-gate 
1197c478bd9Sstevel@tonic-gate /*.......................................................................
1207c478bd9Sstevel@tonic-gate  * Delete a DirReader object.
1217c478bd9Sstevel@tonic-gate  *
1227c478bd9Sstevel@tonic-gate  * Input:
1237c478bd9Sstevel@tonic-gate  *  dr     DirReader *  The object to be deleted.
1247c478bd9Sstevel@tonic-gate  * Output:
1257c478bd9Sstevel@tonic-gate  *  return DirReader *  The deleted object (always NULL).
1267c478bd9Sstevel@tonic-gate  */
_del_DirReader(DirReader * dr)1277c478bd9Sstevel@tonic-gate DirReader *_del_DirReader(DirReader *dr)
1287c478bd9Sstevel@tonic-gate {
1297c478bd9Sstevel@tonic-gate   if(dr) {
1307c478bd9Sstevel@tonic-gate     _dr_close_dir(dr);
1317c478bd9Sstevel@tonic-gate #ifdef USE_READDIR_R
1327c478bd9Sstevel@tonic-gate     free(dr->buffer);
1337c478bd9Sstevel@tonic-gate #endif
1347c478bd9Sstevel@tonic-gate     dr->err = _del_ErrMsg(dr->err);
1357c478bd9Sstevel@tonic-gate     free(dr);
1367c478bd9Sstevel@tonic-gate   };
1377c478bd9Sstevel@tonic-gate   return NULL;
1387c478bd9Sstevel@tonic-gate }
1397c478bd9Sstevel@tonic-gate 
1407c478bd9Sstevel@tonic-gate /*.......................................................................
1417c478bd9Sstevel@tonic-gate  * Open a new directory.
1427c478bd9Sstevel@tonic-gate  *
1437c478bd9Sstevel@tonic-gate  * Input:
1447c478bd9Sstevel@tonic-gate  *  dr      DirReader *   The directory reader resource object.
1457c478bd9Sstevel@tonic-gate  *  path   const char *   The directory to be opened.
1467c478bd9Sstevel@tonic-gate  * Input/Output:
1477c478bd9Sstevel@tonic-gate  *  errmsg       char **  If an error occurs and errmsg isn't NULL, a
1487c478bd9Sstevel@tonic-gate  *                        pointer to an error description will be assigned
1497c478bd9Sstevel@tonic-gate  *                        to *errmsg.
1507c478bd9Sstevel@tonic-gate  * Output:
1517c478bd9Sstevel@tonic-gate  *  return        int     0 - OK.
1527c478bd9Sstevel@tonic-gate  *                        1 - Error (see *errmsg for a description).
1537c478bd9Sstevel@tonic-gate  */
_dr_open_dir(DirReader * dr,const char * path,char ** errmsg)1547c478bd9Sstevel@tonic-gate int _dr_open_dir(DirReader *dr, const char *path, char **errmsg)
1557c478bd9Sstevel@tonic-gate {
1567c478bd9Sstevel@tonic-gate   DIR *dir = NULL;   /* The directory stream */
1577c478bd9Sstevel@tonic-gate /*
1587c478bd9Sstevel@tonic-gate  * If a directory is already open, close it first.
1597c478bd9Sstevel@tonic-gate  */
1607c478bd9Sstevel@tonic-gate   (void) _dr_close_dir(dr);
1617c478bd9Sstevel@tonic-gate /*
1627c478bd9Sstevel@tonic-gate  * Is the path a directory?
1637c478bd9Sstevel@tonic-gate  */
1647c478bd9Sstevel@tonic-gate   if(!_dr_path_is_dir(path)) {
1657c478bd9Sstevel@tonic-gate     if(errmsg) {
1667c478bd9Sstevel@tonic-gate       _err_record_msg(dr->err, "Can't open directory: ", path, END_ERR_MSG);
1677c478bd9Sstevel@tonic-gate       *errmsg = _err_get_msg(dr->err);
1687c478bd9Sstevel@tonic-gate     };
1697c478bd9Sstevel@tonic-gate     return 1;
1707c478bd9Sstevel@tonic-gate   };
1717c478bd9Sstevel@tonic-gate /*
1727c478bd9Sstevel@tonic-gate  * Attempt to open the directory.
1737c478bd9Sstevel@tonic-gate  */
1747c478bd9Sstevel@tonic-gate   dir = opendir(path);
1757c478bd9Sstevel@tonic-gate   if(!dir) {
1767c478bd9Sstevel@tonic-gate     if(errmsg) {
1777c478bd9Sstevel@tonic-gate       _err_record_msg(dr->err, "Can't open directory: ", path, END_ERR_MSG);
1787c478bd9Sstevel@tonic-gate       *errmsg = _err_get_msg(dr->err);
1797c478bd9Sstevel@tonic-gate     };
1807c478bd9Sstevel@tonic-gate     return 1;
1817c478bd9Sstevel@tonic-gate   };
1827c478bd9Sstevel@tonic-gate /*
1837c478bd9Sstevel@tonic-gate  * If using POSIX threads, allocate a buffer for readdir_r().
1847c478bd9Sstevel@tonic-gate  */
1857c478bd9Sstevel@tonic-gate #ifdef USE_READDIR_R
1867c478bd9Sstevel@tonic-gate   {
1877c478bd9Sstevel@tonic-gate     size_t size;
1887c478bd9Sstevel@tonic-gate     int name_max = pathconf(path, _PC_NAME_MAX);
1897c478bd9Sstevel@tonic-gate #ifdef NAME_MAX
1907c478bd9Sstevel@tonic-gate     if(name_max < 0)
1917c478bd9Sstevel@tonic-gate       name_max = NAME_MAX;
1927c478bd9Sstevel@tonic-gate #endif
1937c478bd9Sstevel@tonic-gate     if(name_max < 0) {
1947c478bd9Sstevel@tonic-gate       if(errmsg) {
1957c478bd9Sstevel@tonic-gate 	_err_record_msg(dr->err, "Unable to deduce readdir() buffer size.",
1967c478bd9Sstevel@tonic-gate 			END_ERR_MSG);
1977c478bd9Sstevel@tonic-gate 	*errmsg = _err_get_msg(dr->err);
1987c478bd9Sstevel@tonic-gate       };
1997c478bd9Sstevel@tonic-gate       closedir(dir);
2007c478bd9Sstevel@tonic-gate       return 1;
2017c478bd9Sstevel@tonic-gate     };
2027c478bd9Sstevel@tonic-gate /*
2037c478bd9Sstevel@tonic-gate  * How big a buffer do we need to allocate?
2047c478bd9Sstevel@tonic-gate  */
2057c478bd9Sstevel@tonic-gate     size = sizeof(struct dirent) + name_max;
2067c478bd9Sstevel@tonic-gate /*
2077c478bd9Sstevel@tonic-gate  * Extend the buffer?
2087c478bd9Sstevel@tonic-gate  */
2097c478bd9Sstevel@tonic-gate     if(size > dr->buffer_dim || !dr->buffer) {
2107c478bd9Sstevel@tonic-gate       struct dirent *buffer = (struct dirent *) (dr->buffer ?
2117c478bd9Sstevel@tonic-gate 						 realloc(dr->buffer, size) :
2127c478bd9Sstevel@tonic-gate 						 malloc(size));
2137c478bd9Sstevel@tonic-gate       if(!buffer) {
2147c478bd9Sstevel@tonic-gate 	if(errmsg) {
2157c478bd9Sstevel@tonic-gate 	  _err_record_msg(dr->err, "Insufficient memory for readdir() buffer.",
2167c478bd9Sstevel@tonic-gate 			  END_ERR_MSG);
2177c478bd9Sstevel@tonic-gate 	  *errmsg = _err_get_msg(dr->err);
2187c478bd9Sstevel@tonic-gate 	};
2197c478bd9Sstevel@tonic-gate 	closedir(dir);
2207c478bd9Sstevel@tonic-gate 	errno = ENOMEM;
2217c478bd9Sstevel@tonic-gate 	return 1;
2227c478bd9Sstevel@tonic-gate       };
2237c478bd9Sstevel@tonic-gate       dr->buffer = buffer;
2247c478bd9Sstevel@tonic-gate       dr->buffer_dim = size;
2257c478bd9Sstevel@tonic-gate     };
2267c478bd9Sstevel@tonic-gate   };
2277c478bd9Sstevel@tonic-gate #endif
2287c478bd9Sstevel@tonic-gate /*
2297c478bd9Sstevel@tonic-gate  * Record the successfully opened directory.
2307c478bd9Sstevel@tonic-gate  */
2317c478bd9Sstevel@tonic-gate   dr->dir = dir;
2327c478bd9Sstevel@tonic-gate   return 0;
2337c478bd9Sstevel@tonic-gate }
2347c478bd9Sstevel@tonic-gate 
2357c478bd9Sstevel@tonic-gate /*.......................................................................
2367c478bd9Sstevel@tonic-gate  * If the DirReader object is currently contains an open directory,
2377c478bd9Sstevel@tonic-gate  * close it.
2387c478bd9Sstevel@tonic-gate  *
2397c478bd9Sstevel@tonic-gate  * Input:
2407c478bd9Sstevel@tonic-gate  *  dr    DirReader *   The directory reader resource object.
2417c478bd9Sstevel@tonic-gate  */
_dr_close_dir(DirReader * dr)2427c478bd9Sstevel@tonic-gate void _dr_close_dir(DirReader *dr)
2437c478bd9Sstevel@tonic-gate {
2447c478bd9Sstevel@tonic-gate   if(dr && dr->dir) {
2457c478bd9Sstevel@tonic-gate     closedir(dr->dir);
2467c478bd9Sstevel@tonic-gate     dr->dir = NULL;
2477c478bd9Sstevel@tonic-gate     dr->file = NULL;
2487c478bd9Sstevel@tonic-gate     _err_clear_msg(dr->err);
2497c478bd9Sstevel@tonic-gate   };
2507c478bd9Sstevel@tonic-gate }
2517c478bd9Sstevel@tonic-gate 
2527c478bd9Sstevel@tonic-gate /*.......................................................................
2537c478bd9Sstevel@tonic-gate  * Read the next file from the directory opened with _dr_open_dir().
2547c478bd9Sstevel@tonic-gate  *
2557c478bd9Sstevel@tonic-gate  * Input:
2567c478bd9Sstevel@tonic-gate  *  dr    DirReader *  The directory reader resource object.
2577c478bd9Sstevel@tonic-gate  * Output:
2587c478bd9Sstevel@tonic-gate  *  return     char *  The name of the new file, or NULL if we reached
2597c478bd9Sstevel@tonic-gate  *                     the end of the directory.
2607c478bd9Sstevel@tonic-gate  */
_dr_next_file(DirReader * dr)2617c478bd9Sstevel@tonic-gate char *_dr_next_file(DirReader *dr)
2627c478bd9Sstevel@tonic-gate {
2637c478bd9Sstevel@tonic-gate /*
2647c478bd9Sstevel@tonic-gate  * Are we currently reading a directory?
2657c478bd9Sstevel@tonic-gate  */
2667c478bd9Sstevel@tonic-gate   if(dr->dir) {
2677c478bd9Sstevel@tonic-gate /*
2687c478bd9Sstevel@tonic-gate  * Read the next directory entry.
2697c478bd9Sstevel@tonic-gate  */
270*1da57d55SToomas Soome #ifdef USE_READDIR_R
2717c478bd9Sstevel@tonic-gate     if(readdir_r(dr->dir, dr->buffer, &dr->file) == 0 && dr->file)
2727c478bd9Sstevel@tonic-gate       return dr->file->d_name;
2737c478bd9Sstevel@tonic-gate #else
2747c478bd9Sstevel@tonic-gate     dr->file = readdir(dr->dir);
2757c478bd9Sstevel@tonic-gate     if(dr->file)
2767c478bd9Sstevel@tonic-gate       return dr->file->d_name;
2777c478bd9Sstevel@tonic-gate #endif
2787c478bd9Sstevel@tonic-gate   };
2797c478bd9Sstevel@tonic-gate /*
2807c478bd9Sstevel@tonic-gate  * When the end of a directory is reached, close it.
2817c478bd9Sstevel@tonic-gate  */
2827c478bd9Sstevel@tonic-gate   _dr_close_dir(dr);
2837c478bd9Sstevel@tonic-gate   return NULL;
2847c478bd9Sstevel@tonic-gate }
2857c478bd9Sstevel@tonic-gate 
2867c478bd9Sstevel@tonic-gate /*.......................................................................
2877c478bd9Sstevel@tonic-gate  * Return 1 if the specified pathname refers to a directory.
2887c478bd9Sstevel@tonic-gate  *
2897c478bd9Sstevel@tonic-gate  * Input:
2907c478bd9Sstevel@tonic-gate  *  pathname  const char *  The path to test.
2917c478bd9Sstevel@tonic-gate  * Output:
2927c478bd9Sstevel@tonic-gate  *  return           int    0 - Not a directory.
2937c478bd9Sstevel@tonic-gate  *                          1 - pathname[] refers to a directory.
2947c478bd9Sstevel@tonic-gate  */
_dr_path_is_dir(const char * pathname)2957c478bd9Sstevel@tonic-gate static int _dr_path_is_dir(const char *pathname)
2967c478bd9Sstevel@tonic-gate {
2977c478bd9Sstevel@tonic-gate   struct stat statbuf;    /* The file-statistics return buffer */
2987c478bd9Sstevel@tonic-gate /*
2997c478bd9Sstevel@tonic-gate  * Look up the file attributes.
3007c478bd9Sstevel@tonic-gate  */
3017c478bd9Sstevel@tonic-gate   if(stat(pathname, &statbuf) < 0)
3027c478bd9Sstevel@tonic-gate     return 0;
3037c478bd9Sstevel@tonic-gate /*
3047c478bd9Sstevel@tonic-gate  * Is the file a directory?
3057c478bd9Sstevel@tonic-gate  */
3067c478bd9Sstevel@tonic-gate   return S_ISDIR(statbuf.st_mode) != 0;
3077c478bd9Sstevel@tonic-gate }
3087c478bd9Sstevel@tonic-gate 
3097c478bd9Sstevel@tonic-gate #endif  /* ifndef WITHOUT_FILE_SYSTEM */
310