/***************************************************************************
 * This program is Copyright (C) 1986, 1987, 1988 by Jonathan Payne.  JOVE *
 * is provided to you without charge, and with no warranty.  You may give  *
 * away copies of JOVE, including sources, provided that this notice is    *
 * included in all the files.                                              *
 ***************************************************************************/

/*
 * This file is used as a compiled module by Jove and also included as
 * source in recover.c
 */
#ifndef TUNED
# include "jove.h"
#endif
#include "scandir.h"

#ifdef	F_COMPLETION

#ifdef	MSDOS
# include <dos.h>
# include <search.h>
#endif

#ifdef	UNIX
# include <sys/stat.h>
# ifdef	M_XENIX
#  include <sys/ndir.h>
#  ifndef dirent
#   define dirent direct
#  endif
# else
#  ifdef	DIRENT
#   include <dirent.h>
#  else
#   include <sys/dir.h>
#   ifndef dirent
#    define dirent direct
#   endif
#  endif	/* DIRENT */
# endif	/* M_XENIX */
#endif

#ifdef	UNIX

#define DIRSIZE(entry)	(strlen((entry)->d_name))

#if !defined(BSD_DIR) && !defined(DIRENT)

typedef struct {
	int	d_fd;		/* File descriptor for this directory */
} DIR;

private int
closedir(dp)
DIR	*dp;
{
	(void) close(dp->d_fd);
	free((UnivPtr) dp);
	return 0;   /* don't know how to fail */
}

private DIR *
opendir(dir)
char	*dir;
{
	DIR	*dp = (DIR *) emalloc(sizeof *dp);
	struct stat	stbuf;

	if ((dp->d_fd = open(dir, 0)) == -1) {
		/* this doesn't exist */
	} else if ((fstat(dp->d_fd, &stbuf) == -1) || !(stbuf.st_mode & S_IFDIR)) {
		/* this isn't a directory! */
		(void) close(dp->d_fd);
	} else {
		return dp;
	}
	free((UnivPtr) dp);
	return NULL;
}

private dirent *
readdir(dp)
DIR	*dp;
{
	static dirent	dir;

	do {
		if (read(dp->d_fd, (UnivPtr) &dir, sizeof dir) != sizeof dir)
			return NULL;
#if	defined(elxsi) && defined(SYSV)
	/*
	 * Elxsi has a BSD4.2 implementation which may or may not use
	 * `twisted inodes' ...  Anyone able to check?
	 */
	} while (*(unsigned short *)&dir.d_ino == 0);
#else
	} while (dir.d_ino == 0);
#endif

	return &dir;
}

#endif	/* ! BSD_DIR */

/* jscandir returns the number of entries or -1 if the directory cannot
   be opened or malloc fails. */

int
jscandir(dir, nmptr, qualify, sorter)
char	*dir;
char	***nmptr;
int	(*qualify) proto((char *));
int	(*sorter) proto((UnivConstPtr, UnivConstPtr));
{
	DIR	*dirp;
	struct  dirent	*entry;
	char	**ourarray;
	unsigned int	nalloc = 10,
			nentries = 0;

	if ((dirp = opendir(dir)) == NULL)
		return -1;
	ourarray = (char **) emalloc(nalloc * sizeof (char *));
	while ((entry = readdir(dirp)) != NULL) {
		if (qualify != NULL && (*qualify)(entry->d_name) == 0)
			continue;
		if (nentries == nalloc) {
			ourarray = (char **) realloc((UnivPtr) ourarray, (nalloc += 10) * sizeof (char *));
			if (ourarray == NULL)
				complain("[Malloc failed: cannot scandir]");
		}
		ourarray[nentries] = (char *) emalloc(DIRSIZE(entry) + 1);
		null_ncpy(ourarray[nentries], entry->d_name, (size_t) DIRSIZE(entry));
		nentries += 1;
	}
	closedir(dirp);
	if ((nentries + 1) != nalloc)
		ourarray = (char **) erealloc((UnivPtr) ourarray,
					((nentries + 1) * sizeof (char *)));
	if (sorter != NULL)
		qsort((UnivPtr) ourarray, nentries, sizeof (char **), sorter);
	*nmptr = ourarray;
	ourarray[nentries] = NULL;		/* guaranteed NULL pointer */

	return nentries;
}

#endif	/* UNIX */

#ifdef	MSDOS
# define DIRSIZE(entry)	strlen((entry).name)

/* Scandir returns the number of entries or -1 if the directory cannot
   be opened or malloc fails. */

unsigned int fmask = _A_NORMAL|_A_RDONLY|_A_HIDDEN|_A_SUBDIR;

int
jscandir(dir, nmptr, qualify, sorter)
char	*dir;
char	***nmptr;
int	(*qualify) proto((char *));
int	(*sorter) proto((UnivConstPtr, UnivConstPtr));
{
	char dirname[FILESIZE];
	struct find_t entry;
	char *ptr;
	char	**ourarray;
	unsigned int	nalloc = 10,
			nentries = 0;

	strcpy(dirname, dir);
	ptr = &dirname[strlen(dirname)-1];
	if ((dirname[1] == ':' && !dirname[2]) || (*ptr == '/') || (*ptr == '\\'))
	   strcat(dirname, "*.*");
	else
	   strcat(dirname, "/*.*");

	if (_dos_findfirst(dirname, fmask, &entry))
	   return -1;
	if ((ourarray = (char **) emalloc(nalloc * sizeof (char *))) == NULL)
	do  {
		if ((fmask == 0x10) && !(entry.attrib&fmask))
			continue;
		strlwr(entry.name);
		if (qualify != NULL && (*qualify)(entry.name) == 0)
			continue;
		if (nentries == nalloc) {
			ourarray = (char **) realloc((char *) ourarray, (nalloc += 10) * sizeof (char *));
			if (ourarray == NULL)
				complain("[Malloc failed: cannot scandir]");
		}
		ourarray[nentries] = (char *) emalloc(DIRSIZE(entry) + 1);
		null_ncpy(ourarray[nentries], entry.name, (int) DIRSIZE(entry));
		nentries++;
	} while (_dos_findnext(&entry) == 0);

	if ((nentries + 1) != nalloc)
		ourarray = (char **) erealloc((char *) ourarray,
					((nentries + 1) * sizeof (char *)));
	if (sorter != (int (*)())NULL)
		qsort((char *) ourarray, nentries, sizeof (char **), sorter);
	*nmptr = ourarray;
	ourarray[nentries] = NULL;		/* guaranteed NULL pointer */

	return nentries;
}

#endif	/* MSDOS */

void
freedir(nmptr, nentries)
char	***nmptr;
int	nentries;
{
	char	**ourarray = *nmptr;

	while (--nentries >= 0)
		free((UnivPtr) *ourarray++);
	free((UnivPtr) *nmptr);
	*nmptr = NULL;
}

int
alphacomp(a, b)
UnivConstPtr	a,
	b;
{
	return strcmp(*(const char **)a, *(const char **)b);
}
#endif /* F_COMPLETION */
