#include <dirent.h>
#include <stdlib.h>
#include <string.h>

int scandir(const char *dirname,struct dirent ***namelist,
	int (*select)(const struct dirent *),
	int (*dcomp)(const struct dirent **, const struct dirent **))
{
	register struct dirent *d, *p, **names;
	register size_t nitems;
	size_t arraysz;
	DIR *dirp;
	int successful = 0;
	int rc = 0;
	
	dirp = NULL;
	names = NULL;
	nitems = 0;
	
	if ((dirp = opendir(dirname)) == NULL)
		return(-1);
	
	/*
	 * estimate the array size.
	 */
	arraysz = 16;
	names = (struct dirent **)malloc(arraysz * sizeof(struct dirent *));
	if (names == NULL)
		goto cleanup;
	
	while ((d = readdir(dirp)) != NULL) {
		if (select != NULL && !(*select)(d))
			continue;	/* just selected names */
		/*
		 * Make a minimum size copy of the data
		 */
		p = (struct dirent *)malloc(sizeof(struct dirent));
		if (p == NULL)
			goto cleanup;
		p->d_fileno = d->d_fileno;
		p->d_reclen = d->d_reclen;
		p->d_namlen = d->d_namlen;
		bcopy(d->d_name, p->d_name, p->d_namlen + 1);
		/*
		 * Check to make sure the array has space left and
		 * realloc the maximum size.
		 */
		if (nitems == arraysz) {
			void *n = realloc((char *)names,
				(arraysz+=16) * sizeof(struct dirent *));
			if (n == NULL)
				goto cleanup;
			names = (struct dirent **)n;
		}
		names[nitems++] = p;
	}
	successful = 1;
cleanup:
	closedir(dirp);
	if (successful) {
		if (nitems && dcomp != NULL)
			qsort(names, nitems, sizeof(struct dirent *), (int (*)(const void *,const void *))dcomp);
		*namelist = names;
		rc = nitems;
	} else {  /* We were unsuccessful, clean up storage and return -1.  */
		if ( names ) {
			size_t i;
			for (i=0; i < nitems; i++ )
				free( names[i] );
			free( names );
		}
		rc = -1;
	}
	
	return(rc);
}

/*
 * Alphabetic order comparison routine for those who want it.
 */
int alphasort(const struct dirent **d1,const struct dirent **d2)
{
  return(strcmp((*d1)->d_name, (*d2)->d_name));
}
