1 /* scandir: Scan a directory, collecting all (selected) items into a an array.
2  *
3  * This code borrowed from 'libit', which can be found here:
4  *
5  * http://www.iro.umontreal.ca/~pinard/libit/dist/scandir/
6  *
7  * The original author put this code in the public domain.
8  * It has been modified slightly to get rid of warnings, etc.
9  *
10  * Below is the email I received from pinard@iro.umontreal.ca (Fran�ois Pinard)
11  * when I sent him an email asking him about the license, etc. of this
12  * code which I obtained from his site.
13  *
14  * I think the correct spelling of his name is Rich Salz.  I think he's now
15  * rsalz@datapower.com...
16  * --
17  * Rich Salz, Chief Security Architect
18  * DataPower Technology                           http://www.datapower.com
19  * XS40 XML Security Gateway   http://www.datapower.com/products/xs40.html
20  *
21  *	Copyright(C):	none (public domain)
22  *	License:	none (public domain)
23  *	Author:		Rich Salz <rsalz@datapower.com>
24  *
25  *
26  *
27  *	-- Alan Robertson
28  *	   alanr@unix.sh
29  *
30  **************************************************************************
31  *
32  * Subject:	Re: Scandir replacement function
33  * Date:	18 May 2001 12:00:48 -0400
34  * From:	pinard@iro.umontreal.ca (Fran�ois Pinard)
35  * To:		Alan Robertson <alanr@unix.sh>
36  * References:	1
37  *
38  *
39  * [Alan Robertson]
40  *
41  * > Hi, I'd like to use your scandir replacement function found here:
42  * > http://www.iro.umontreal.ca/~pinard/libit/dist/scandir/ But, it does
43  * > not indicate authorship or licensing terms in it.  Could you tell me
44  * > who wrote this code, under what license you distribute it, and whether
45  * > and under what terms I may further distribute it?
46  *
47  * Hello, Alan.  These are (somewhat) explained in UNSHAR.HDR found in the
48  * same directory.  The routines have been written by Rick Saltz (I'm not
49  * completely sure of the spelling) a long while ago.  I think that nowadays,
50  * Rick is better known as the main author of the nice INN package.
51  *
52  **************************************************************************
53  *
54  * I spent a little time verifying this with Rick Salz.
55  * The results are below:
56  *
57  **************************************************************************
58  *
59  * Date: Tue, 20 Sep 2005 21:52:09 -0400 (EDT)
60  * From: Rich Salz <rsalz@datapower.com>
61  * To: Alan Robertson <alanr@unix.sh>
62  * Subject: Re: Verifying permissions/licenses/etc on some old code of yours -
63  *  scandir.c
64  * In-Reply-To: <433071CA.8000107@unix.sh>
65  * Message-ID: <Pine.LNX.4.44L0.0509202151270.9198-100000@smtp.datapower.com>
66  * Content-Type: TEXT/PLAIN; charset=US-ASCII
67  *
68  * yes, it's most definitely in the public domain.
69  *
70  * I'm glad you find it useful.  I'm surprised it hasn't been replaced by,
71  * e.g,. something in GLibC.  Ii'm impressed you tracked me down.
72  *
73  *	/r$
74  *
75  * --
76  * Rich Salz                  Chief Security Architect
77  * DataPower Technology       http://www.datapower.com
78  * XS40 XML Security Gateway  http://www.datapower.com/products/xs40.html
79  * ---------------------------------------------------------------------->
80  * Subject:	scandir, ftw REDUX
81  * Date: 	1 Jan 88 00:47:01 GMT
82  * From: 	rsalz@pebbles.bbn.com
83  * Newsgroups:  comp.sources.misc
84  *
85  *
86  * Forget my previous message -- I just decided for completeness's sake to
87  * implement the SysV ftw(3) routine, too.
88  *
89  * To repeat, these are public-domain implementations of the SystemV ftw()
90  * routine, the BSD scandir() and alphasort() routines, and documentation for
91  * same.  The FTW manpage could be more readable, but so it goes.
92  *
93  * Anyhow, feel free to post these, and incorporate them into your existing
94  * packages.  I have readdir() routiens for MSDOS and the Amiga if anyone
95  *  wants them, and should have them for VMS by the end of January; let me
96  *  know if you want copies.
97  *
98  *                        Yours in filesystems,
99  *                                /r$
100  *
101  *                                Anyhow, feel free to post
102  * ----------------------------------------------------------------------<
103  *
104  */
105 
106 #include <lha_internal.h>
107 #include <sys/types.h>
108 #include <dirent.h>
109 #include <stdlib.h>
110 #include <stddef.h>
111 #include <string.h>
112 
113 #ifndef NULL
114 # define NULL ((void *) 0)
115 #endif
116 
117 /* Initial guess at directory allocated.  */
118 #define INITIAL_ALLOCATION 20
119 
120 int
121 scandir (const char *directory_name,
122 	struct dirent ***array_pointer,
123 	int (*select_function) (const struct dirent *),
124 
125 #ifdef USE_SCANDIR_COMPARE_STRUCT_DIRENT
126 	/* This is what the Linux man page says */
127 	int (*compare_function) (const struct dirent**, const struct dirent**)
128 #else
129 	/* This is what the Linux header file says ... */
130 	int (*compare_function) (const void *, const void *)
131 #endif
132 	);
133 
134 int
scandir(const char * directory_name,struct dirent *** array_pointer,int (* select_function)(const struct dirent *),int (* compare_function)(const struct dirent **,const struct dirent **))135 scandir (const char *directory_name,
136 	struct dirent ***array_pointer,
137 	int (*select_function) (const struct dirent *),
138 #ifdef USE_SCANDIR_COMPARE_STRUCT_DIRENT
139 	/* This is what the linux man page says */
140 	int (*compare_function) (const struct dirent**, const struct dirent**)
141 #else
142 	/* This is what the linux header file says ... */
143 	int (*compare_function) (const void *, const void *)
144 #endif
145 	)
146 {
147   DIR *directory;
148   struct dirent **array;
149   struct dirent *entry;
150   struct dirent *copy;
151   int allocated = INITIAL_ALLOCATION;
152   int counter = 0;
153 
154   /* Get initial list space and open directory.  */
155 
156   if (directory = opendir (directory_name), directory == NULL)
157     return -1;
158 
159   if (array = (struct dirent **) malloc (allocated * sizeof (struct dirent *)),
160       array == NULL)
161     return -1;
162 
163   /* Read entries in the directory.  */
164 
165   while (entry = readdir (directory), entry)
166     if (select_function == NULL || (*select_function) (entry))
167       {
168 	/* User wants them all, or he wants this one.  Copy the entry.  */
169 
170 	/*
171 	 * On some OSes the declaration of "entry->d_name" is a minimal-length
172 	 * placeholder.  Example: Solaris:
173 	 * 	/usr/include/sys/dirent.h:
174 	 *		"char d_name[1];"
175 	 *	man page "dirent(3)":
176 	 *		The field d_name is the beginning of the character array
177 	 *		giving the name of the directory entry. This name is
178 	 *		null terminated and may have at most MAXNAMLEN chars.
179 	 * So our malloc length may need to be increased accordingly.
180 	 *	sizeof(entry->d_name): space (possibly minimal) in struct.
181 	 * 	strlen(entry->d_name): actual length of the entry.
182 	 *
183 	 *			John Kavadias <john_kavadias@hotmail.com>
184 	 *			David Lee <t.d.lee@durham.ac.uk>
185 	 */
186 	int namelength = strlen(entry->d_name) + 1;	/* length with NULL */
187 	int extra = 0;
188 
189 	if (sizeof(entry->d_name) <= namelength) {
190 		/* allocated space <= required space */
191 		extra += namelength - sizeof(entry->d_name);
192 	}
193 
194 	if (copy = (struct dirent *) malloc (sizeof (struct dirent) + extra),
195 	    copy == NULL)
196 	  {
197 	    closedir (directory);
198 	    free (array);
199 	    return -1;
200 	  }
201 	copy->d_ino = entry->d_ino;
202 	copy->d_reclen = entry->d_reclen;
203 	strcpy (copy->d_name, entry->d_name);
204 
205 	/* Save the copy.  */
206 
207 	if (counter + 1 == allocated)
208 	  {
209 	    allocated <<= 1;
210 	    array = (struct dirent **)
211 	      realloc ((char *) array, allocated * sizeof (struct dirent *));
212 	    if (array == NULL)
213 	      {
214 		closedir (directory);
215 		free (array);
216 		free (copy);
217 		return -1;
218 	      }
219 	  }
220 	array[counter++] = copy;
221       }
222 
223   /* Close things off.  */
224 
225   array[counter] = NULL;
226   *array_pointer = array;
227   closedir (directory);
228 
229   /* Sort?  */
230 
231   if (counter > 1 && compare_function)
232     qsort ((char *) array, counter, sizeof (struct dirent *)
233   	,	(int (*)(const void *, const void *))(compare_function));
234 
235   return counter;
236 }
237