xref: /freebsd/lib/libc/gen/scandir.c (revision dc36d6f9)
18a16b7a1SPedro F. Giffuni /*-
28a16b7a1SPedro F. Giffuni  * SPDX-License-Identifier: BSD-3-Clause
38a16b7a1SPedro F. Giffuni  *
458f0484fSRodney W. Grimes  * Copyright (c) 1983, 1993
558f0484fSRodney W. Grimes  *	The Regents of the University of California.  All rights reserved.
658f0484fSRodney W. Grimes  *
758f0484fSRodney W. Grimes  * Redistribution and use in source and binary forms, with or without
858f0484fSRodney W. Grimes  * modification, are permitted provided that the following conditions
958f0484fSRodney W. Grimes  * are met:
1058f0484fSRodney W. Grimes  * 1. Redistributions of source code must retain the above copyright
1158f0484fSRodney W. Grimes  *    notice, this list of conditions and the following disclaimer.
1258f0484fSRodney W. Grimes  * 2. Redistributions in binary form must reproduce the above copyright
1358f0484fSRodney W. Grimes  *    notice, this list of conditions and the following disclaimer in the
1458f0484fSRodney W. Grimes  *    documentation and/or other materials provided with the distribution.
15fbbd9655SWarner Losh  * 3. Neither the name of the University nor the names of its contributors
1658f0484fSRodney W. Grimes  *    may be used to endorse or promote products derived from this software
1758f0484fSRodney W. Grimes  *    without specific prior written permission.
1858f0484fSRodney W. Grimes  *
1958f0484fSRodney W. Grimes  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2058f0484fSRodney W. Grimes  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2158f0484fSRodney W. Grimes  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2258f0484fSRodney W. Grimes  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2358f0484fSRodney W. Grimes  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2458f0484fSRodney W. Grimes  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2558f0484fSRodney W. Grimes  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2658f0484fSRodney W. Grimes  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2758f0484fSRodney W. Grimes  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2858f0484fSRodney W. Grimes  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2958f0484fSRodney W. Grimes  * SUCH DAMAGE.
3058f0484fSRodney W. Grimes  */
3158f0484fSRodney W. Grimes 
3258f0484fSRodney W. Grimes /*
3358f0484fSRodney W. Grimes  * Scan the directory dirname calling select to make a list of selected
3458f0484fSRodney W. Grimes  * directory entries then sort using qsort and compare routine dcomp.
3558f0484fSRodney W. Grimes  * Returns the number of entries and a pointer to a list of pointers to
3658f0484fSRodney W. Grimes  * struct dirent (through namelist). Returns -1 if there were any errors.
3758f0484fSRodney W. Grimes  */
3858f0484fSRodney W. Grimes 
39d201fe46SDaniel Eischen #include "namespace.h"
4058f0484fSRodney W. Grimes #include <dirent.h>
419fb8e8eeSKonstantin Belousov #include <fcntl.h>
4258f0484fSRodney W. Grimes #include <stdlib.h>
4358f0484fSRodney W. Grimes #include <string.h>
449fb8e8eeSKonstantin Belousov #include <unistd.h>
45d201fe46SDaniel Eischen #include "un-namespace.h"
4658f0484fSRodney W. Grimes 
4746cdc140SDavid Chisnall #ifdef	I_AM_SCANDIR_B
4846cdc140SDavid Chisnall #include "block_abi.h"
4946cdc140SDavid Chisnall #define	SELECT(x)	CALL_BLOCK(select, x)
5046cdc140SDavid Chisnall #ifndef __BLOCKS__
51cc321ccdSKonstantin Belousov void qsort_b(void *, size_t, size_t, void *);
5246cdc140SDavid Chisnall #endif
5346cdc140SDavid Chisnall #else
5446cdc140SDavid Chisnall #define	SELECT(x)	select(x)
5546cdc140SDavid Chisnall #endif
5646cdc140SDavid Chisnall 
57d10af81dSJohn Baldwin #ifdef I_AM_SCANDIR_B
58d10af81dSJohn Baldwin typedef DECLARE_BLOCK(int, select_block, const struct dirent *);
59d10af81dSJohn Baldwin typedef DECLARE_BLOCK(int, dcomp_block, const struct dirent **,
60d10af81dSJohn Baldwin     const struct dirent **);
61d10af81dSJohn Baldwin #else
62af3c7888SEd Schouten static int scandir_thunk_cmp(const void *p1, const void *p2, void *thunk);
63992bcb37SMateusz Guzik #endif
64f5636f88SKonstantin Belousov 
65cb6e97f4SKonstantin Belousov static int
6646cdc140SDavid Chisnall #ifdef I_AM_SCANDIR_B
scandir_b_dirp(DIR * dirp,struct dirent *** namelist,select_block select,dcomp_block dcomp)67cb6e97f4SKonstantin Belousov scandir_b_dirp(DIR *dirp, struct dirent ***namelist, select_block select,
68d10af81dSJohn Baldwin     dcomp_block dcomp)
6946cdc140SDavid Chisnall #else
70cb6e97f4SKonstantin Belousov scandir_dirp(DIR *dirp, struct dirent ***namelist,
714176dd52SKonstantin Belousov     int (*select)(const struct dirent *), int (*dcomp)(const struct dirent **,
724176dd52SKonstantin Belousov     const struct dirent **))
7346cdc140SDavid Chisnall #endif
7458f0484fSRodney W. Grimes {
75b231cb39SDavid E. O'Brien 	struct dirent *d, *p, **names = NULL;
765f2bd3bdSPedro F. Giffuni 	size_t arraysz, numitems;
7758f0484fSRodney W. Grimes 
780a8ff54eSConrad Meyer 	numitems = 0;
7918798c64SDavid Schultz 	arraysz = 32;	/* initial estimate of the array size */
8058f0484fSRodney W. Grimes 	names = (struct dirent **)malloc(arraysz * sizeof(struct dirent *));
8158f0484fSRodney W. Grimes 	if (names == NULL)
8229595ffdSJulian Elischer 		goto fail;
8358f0484fSRodney W. Grimes 
8458f0484fSRodney W. Grimes 	while ((d = readdir(dirp)) != NULL) {
8546cdc140SDavid Chisnall 		if (select != NULL && !SELECT(d))
8658f0484fSRodney W. Grimes 			continue;	/* just selected names */
8758f0484fSRodney W. Grimes 		/*
8858f0484fSRodney W. Grimes 		 * Make a minimum size copy of the data
8958f0484fSRodney W. Grimes 		 */
9069921123SKonstantin Belousov 		p = (struct dirent *)malloc(_GENERIC_DIRSIZ(d));
9158f0484fSRodney W. Grimes 		if (p == NULL)
9229595ffdSJulian Elischer 			goto fail;
93e169c667SPoul-Henning Kamp 		p->d_fileno = d->d_fileno;
94e169c667SPoul-Henning Kamp 		p->d_type = d->d_type;
9558f0484fSRodney W. Grimes 		p->d_reclen = d->d_reclen;
9658f0484fSRodney W. Grimes 		p->d_namlen = d->d_namlen;
9758f0484fSRodney W. Grimes 		bcopy(d->d_name, p->d_name, p->d_namlen + 1);
9858f0484fSRodney W. Grimes 		/*
9958f0484fSRodney W. Grimes 		 * Check to make sure the array has space left and
10058f0484fSRodney W. Grimes 		 * realloc the maximum size.
10158f0484fSRodney W. Grimes 		 */
10256362c6fSPedro F. Giffuni 		if (numitems >= arraysz) {
10329595ffdSJulian Elischer 			struct dirent **names2;
10429595ffdSJulian Elischer 
1059f36610fSPedro F. Giffuni 			names2 = reallocarray(names, arraysz,
1069f36610fSPedro F. Giffuni 			    2 * sizeof(struct dirent *));
10729595ffdSJulian Elischer 			if (names2 == NULL) {
10829595ffdSJulian Elischer 				free(p);
10929595ffdSJulian Elischer 				goto fail;
11058f0484fSRodney W. Grimes 			}
11129595ffdSJulian Elischer 			names = names2;
11218798c64SDavid Schultz 			arraysz *= 2;
11329595ffdSJulian Elischer 		}
11456362c6fSPedro F. Giffuni 		names[numitems++] = p;
11558f0484fSRodney W. Grimes 	}
11658f0484fSRodney W. Grimes 	closedir(dirp);
11756362c6fSPedro F. Giffuni 	if (numitems && dcomp != NULL)
11846cdc140SDavid Chisnall #ifdef I_AM_SCANDIR_B
11956362c6fSPedro F. Giffuni 		qsort_b(names, numitems, sizeof(struct dirent *), (void*)dcomp);
12046cdc140SDavid Chisnall #else
12156362c6fSPedro F. Giffuni 		qsort_r(names, numitems, sizeof(struct dirent *),
122af3c7888SEd Schouten 		    scandir_thunk_cmp, &dcomp);
12346cdc140SDavid Chisnall #endif
12458f0484fSRodney W. Grimes 	*namelist = names;
12556362c6fSPedro F. Giffuni 	return (numitems);
12629595ffdSJulian Elischer 
12729595ffdSJulian Elischer fail:
12856362c6fSPedro F. Giffuni 	while (numitems > 0)
12956362c6fSPedro F. Giffuni 		free(names[--numitems]);
13029595ffdSJulian Elischer 	free(names);
13129595ffdSJulian Elischer 	closedir(dirp);
1324176dd52SKonstantin Belousov 	return (-1);
13358f0484fSRodney W. Grimes }
13458f0484fSRodney W. Grimes 
135cb6e97f4SKonstantin Belousov int
136cb6e97f4SKonstantin Belousov #ifdef I_AM_SCANDIR_B
scandir_b(const char * dirname,struct dirent *** namelist,select_block select,dcomp_block dcomp)137cb6e97f4SKonstantin Belousov scandir_b(const char *dirname, struct dirent ***namelist, select_block select,
138cb6e97f4SKonstantin Belousov     dcomp_block dcomp)
139cb6e97f4SKonstantin Belousov #else
140cb6e97f4SKonstantin Belousov scandir(const char *dirname, struct dirent ***namelist,
141cb6e97f4SKonstantin Belousov     int (*select)(const struct dirent *), int (*dcomp)(const struct dirent **,
142cb6e97f4SKonstantin Belousov     const struct dirent **))
143cb6e97f4SKonstantin Belousov #endif
144cb6e97f4SKonstantin Belousov {
145cb6e97f4SKonstantin Belousov 	DIR *dirp;
146cb6e97f4SKonstantin Belousov 
147cb6e97f4SKonstantin Belousov 	dirp = opendir(dirname);
148cb6e97f4SKonstantin Belousov 	if (dirp == NULL)
149cb6e97f4SKonstantin Belousov 		return (-1);
150cb6e97f4SKonstantin Belousov 	return (
151cb6e97f4SKonstantin Belousov #ifdef I_AM_SCANDIR_B
152cb6e97f4SKonstantin Belousov 	    scandir_b_dirp
153cb6e97f4SKonstantin Belousov #else
154cb6e97f4SKonstantin Belousov 	    scandir_dirp
155cb6e97f4SKonstantin Belousov #endif
156cb6e97f4SKonstantin Belousov 	    (dirp, namelist, select, dcomp));
157cb6e97f4SKonstantin Belousov }
158cb6e97f4SKonstantin Belousov 
159cc321ccdSKonstantin Belousov #ifndef I_AM_SCANDIR_B
1609fb8e8eeSKonstantin Belousov int
scandirat(int dirfd,const char * dirname,struct dirent *** namelist,int (* select)(const struct dirent *),int (* dcomp)(const struct dirent **,const struct dirent **))1619fb8e8eeSKonstantin Belousov scandirat(int dirfd, const char *dirname, struct dirent ***namelist,
1629fb8e8eeSKonstantin Belousov     int (*select)(const struct dirent *), int (*dcomp)(const struct dirent **,
1639fb8e8eeSKonstantin Belousov     const struct dirent **))
1649fb8e8eeSKonstantin Belousov {
1659fb8e8eeSKonstantin Belousov 	DIR *dirp;
1669fb8e8eeSKonstantin Belousov 	int fd;
1679fb8e8eeSKonstantin Belousov 
1689fb8e8eeSKonstantin Belousov 	fd = _openat(dirfd, dirname, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
1699fb8e8eeSKonstantin Belousov 	if (fd == -1)
1709fb8e8eeSKonstantin Belousov 		return (-1);
1719fb8e8eeSKonstantin Belousov 	dirp = fdopendir(fd);
1729fb8e8eeSKonstantin Belousov 	if (dirp == NULL) {
1739fb8e8eeSKonstantin Belousov 		_close(fd);
1749fb8e8eeSKonstantin Belousov 		return (-1);
1759fb8e8eeSKonstantin Belousov 	}
1769fb8e8eeSKonstantin Belousov 	return (scandir_dirp(dirp, namelist, select, dcomp));
1779fb8e8eeSKonstantin Belousov }
1789fb8e8eeSKonstantin Belousov 
17958f0484fSRodney W. Grimes /*
18058f0484fSRodney W. Grimes  * Alphabetic order comparison routine for those who want it.
1815fc5e42aSAndrey A. Chernov  * POSIX 2008 requires that alphasort() uses strcoll().
18258f0484fSRodney W. Grimes  */
18358f0484fSRodney W. Grimes int
alphasort(const struct dirent ** d1,const struct dirent ** d2)1844176dd52SKonstantin Belousov alphasort(const struct dirent **d1, const struct dirent **d2)
18558f0484fSRodney W. Grimes {
1864176dd52SKonstantin Belousov 
187dcdafd0eSAndrey A. Chernov 	return (strcoll((*d1)->d_name, (*d2)->d_name));
18858f0484fSRodney W. Grimes }
189f5636f88SKonstantin Belousov 
19005c9a015SAymeric Wibo int
versionsort(const struct dirent ** d1,const struct dirent ** d2)19105c9a015SAymeric Wibo versionsort(const struct dirent **d1, const struct dirent **d2)
19205c9a015SAymeric Wibo {
19305c9a015SAymeric Wibo 
19405c9a015SAymeric Wibo 	return (strverscmp((*d1)->d_name, (*d2)->d_name));
19505c9a015SAymeric Wibo }
19605c9a015SAymeric Wibo 
197f5636f88SKonstantin Belousov static int
scandir_thunk_cmp(const void * p1,const void * p2,void * thunk)198af3c7888SEd Schouten scandir_thunk_cmp(const void *p1, const void *p2, void *thunk)
199f5636f88SKonstantin Belousov {
200f5636f88SKonstantin Belousov 	int (*dc)(const struct dirent **, const struct dirent **);
201f5636f88SKonstantin Belousov 
202f5636f88SKonstantin Belousov 	dc = *(int (**)(const struct dirent **, const struct dirent **))thunk;
203f5636f88SKonstantin Belousov 	return (dc((const struct dirent **)p1, (const struct dirent **)p2));
204f5636f88SKonstantin Belousov }
205cc321ccdSKonstantin Belousov #endif
206