xref: /original-bsd/lib/libc/gen/scandir.c (revision f43fc9d7)
1 /*
2  * Copyright (c) 1983 Regents of the University of California.
3  * All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  */
7 
8 #if defined(LIBC_SCCS) && !defined(lint)
9 static char sccsid[] = "@(#)scandir.c	5.9 (Berkeley) 06/24/90";
10 #endif /* LIBC_SCCS and not lint */
11 
12 /*
13  * Scan the directory dirname calling select to make a list of selected
14  * directory entries then sort using qsort and compare routine dcomp.
15  * Returns the number of entries and a pointer to a list of pointers to
16  * struct dirent (through namelist). Returns -1 if there were any errors.
17  */
18 
19 #include <sys/types.h>
20 #include <sys/stat.h>
21 #include <dirent.h>
22 
23 /*
24  * The DIRSIZ macro gives the minimum record length which will hold
25  * the directory entry.  This requires the amount of space in struct dirent
26  * without the d_name field, plus enough space for the name with a terminating
27  * null byte (dp->d_namlen+1), rounded up to a 4 byte boundary.
28  */
29 #undef DIRSIZ
30 #define DIRSIZ(dp) \
31     ((sizeof (struct dirent) - (MAXNAMLEN+1)) + (((dp)->d_namlen+1 + 3) &~ 3))
32 
33 scandir(dirname, namelist, select, dcomp)
34 	char *dirname;
35 	struct dirent ***namelist;
36 	int (*select)(), (*dcomp)();
37 {
38 	register struct dirent *d, *p, **names;
39 	register int nitems;
40 	struct stat stb;
41 	long arraysz;
42 	DIR *dirp;
43 
44 	if ((dirp = opendir(dirname)) == NULL)
45 		return(-1);
46 	if (fstat(dirp->dd_fd, &stb) < 0)
47 		return(-1);
48 
49 	/*
50 	 * estimate the array size by taking the size of the directory file
51 	 * and dividing it by a multiple of the minimum size entry.
52 	 */
53 	arraysz = (stb.st_size / 24);
54 	names = (struct dirent **)malloc(arraysz * sizeof(struct dirent *));
55 	if (names == NULL)
56 		return(-1);
57 
58 	nitems = 0;
59 	while ((d = readdir(dirp)) != NULL) {
60 		if (select != NULL && !(*select)(d))
61 			continue;	/* just selected names */
62 		/*
63 		 * Make a minimum size copy of the data
64 		 */
65 		p = (struct dirent *)malloc(DIRSIZ(d));
66 		if (p == NULL)
67 			return(-1);
68 		p->d_ino = d->d_ino;
69 		p->d_reclen = d->d_reclen;
70 		p->d_namlen = d->d_namlen;
71 		bcopy(d->d_name, p->d_name, p->d_namlen + 1);
72 		/*
73 		 * Check to make sure the array has space left and
74 		 * realloc the maximum size.
75 		 */
76 		if (++nitems >= arraysz) {
77 			if (fstat(dirp->dd_fd, &stb) < 0)
78 				return(-1);	/* just might have grown */
79 			arraysz = stb.st_size / 12;
80 			names = (struct dirent **)realloc((char *)names,
81 				arraysz * sizeof(struct dirent *));
82 			if (names == NULL)
83 				return(-1);
84 		}
85 		names[nitems-1] = p;
86 	}
87 	closedir(dirp);
88 	if (nitems && dcomp != NULL)
89 		qsort(names, nitems, sizeof(struct dirent *), dcomp);
90 	*namelist = names;
91 	return(nitems);
92 }
93 
94 /*
95  * Alphabetic order comparison routine for those who want it.
96  */
97 alphasort(d1, d2)
98 	void *d1, *d2;
99 {
100 	return(strcmp((*(struct dirent **)d1)->d_name,
101 	    (*(struct dirent **)d2)->d_name));
102 }
103