/*
 * Taken from net.sources.   Thanks to some unknown author somewhere
 * out there.
 */

/*
 *	dir.c - contains routines which behave (hopefully) like the
 *		similar versions on 4.2 BSD.
 *
 */

#ifdef MSDOS				/* entire file */
#include <errno.h>
#include <dir.h>

#include "args.h"

extern DIR *opendir ARGS((char *dirname));
extern int closedir ARGS((DIR *dirptr));
extern int seekdir ARGS((DIR *dirptr, long loc));
extern long telldir ARGS((DIR *dirptr));
extern struct direct *readdir ARGS((DIR *dirp));

#ifndef rewinddir /* rewinddir is a macro on some systems (ULTRIX, ...) */
extern void (rewinddir) ARGS((DIR *dirp));
#endif

extern int errno;
extern char *calloc ARGS((size_t nelem, size_t elsize));

static int freedir ARGS((struct direct **dirs));
static int scandir ARGS((char *dirname, struct direct *(*namelist[]),
	 int (*select )(), int (*compar )()));

/*
 *	opendir - opens given directory and reads its contents into memory.
 *	Other functions use this data or the DIR structure to do their work.
 */
DIR *
opendir(dirname)
char *dirname;
{
	struct direct **namelist;
	DIR *dirptr;

	dirptr = (DIR *)calloc(1, sizeof(DIR));
	if(dirptr == (DIR *)0)
	{
		errno = ENOMEM;
		return((DIR *)0);
	}
	dirptr->d_magic = DMAGIC;
	dirptr->d_pos = 0;
	dirptr->d_length = scandir(dirname, &(dirptr->namelist),
		(int (*)())0, (int (*)())0);
	if(dirptr->d_length < 0)
	{
		free((char *)dirptr);
		return((DIR *)0);
	}
	return(dirptr);
}

/*
 *	readdir - returns the next directory structure from the list and
 *	updates values in the DIR structure.
 */
struct direct *
readdir(dirptr)
DIR *dirptr;
{
	if(dirptr->d_magic != DMAGIC)
	{
		errno = ENOTDIR;
		return((struct direct *)0);
	}
	if(dirptr->d_pos >= dirptr->d_length)
	{
		errno = ENFILE;
		return((struct direct *)0);
	}
	return(dirptr->namelist[dirptr->d_pos++]);
}

/*
 *	telldir - return the current position of the directory.
 */
long
telldir(dirptr)
DIR *dirptr;
{
	if(dirptr->d_magic != DMAGIC)
	{
		errno = ENOTDIR;
		return(-1L);
	}
	return((long)dirptr->d_pos);
}

/*
 *	seekdir - position the given DIR stream to position given.
 */
int
seekdir(dirptr, loc)
DIR *dirptr;
long loc;
{
	if(dirptr->d_magic != DMAGIC)
	{
		errno = ENOTDIR;
		return(-1);
	}
	if(loc > (long)dirptr->d_length)
	{
		errno = EINVAL;
		return(-1);
	}
	dirptr->d_pos = (int)loc;
	return(0);
}

/*
 *	rewinddir - rewind given DIR to beginning
 */
void
rewinddir(dirptr)
DIR *dirptr;
{
	if(dirptr->d_magic != DMAGIC)
	{
		errno = ENOTDIR;
		return(-1);
	}
	dirptr->d_pos = 0;
	return(0);
}

/*
 *	closedir - close given directory. destroy given DIR struct so we
 *	know it is closed.
 */
int
closedir(dirptr)
DIR *dirptr;
{
	if(dirptr->d_magic != DMAGIC)
	{
		errno = ENOTDIR;
		return(-1);
	}
	dirptr->d_magic = ~DMAGIC;	/* mess it up a little */
	freedir(dirptr->namelist);
	free(dirptr);
	return(0);
}




/****************************/

#include <dos.h>

#define NULL (char *)0

static struct direct buffer;

#if HAVE_STDC
#include <stddef.h>
#include <stdlib.h>
#else
extern char *malloc ARGS((size_t));
extern char *realloc ARGS((void *, size_t))
#endif

static int
scandir(dirname, namelist, select, compar)
char *dirname;
struct direct *(*namelist[]);
int (*select)();
int (*compar)();
{
	register struct direct **names;
	register int dirno;
	union REGS sregs, oregs;
	struct SREGS segregs;
	char *ptr, *paths;

	names = (struct direct **)calloc(1, sizeof(struct direct *));
	paths = calloc(128, 1);
	if(names == (struct direct **)0 || paths == NULL)
	{
		errno = ENOMEM;
		return(-1);
	}
	strcpy(paths, dirname);
	ptr = &paths[strlen(paths) - 1];
	if(*ptr == '/' || *ptr == '\\')
		*ptr = '\0';
	strcat(paths, "/*.*");

	segread(&segregs);		/* set up segment registers */
	ptr = (char *)&buffer;
	sregs.h.ah = 0x1a;		/* set DTA to buffer */
	sregs.x.dx = FP_OFF(ptr);	/* offset */
#ifdef	M_I86LM
	segregs.ds = FP_SEG(ptr);	/* pointer in large model */
#endif /* M_I86LM */
	intdosx(&sregs, &oregs, &segregs);
	sregs.x.ax = 0x4e00;		/* search for first */
	sregs.x.cx = 0x1f;		/* include all attributes */
	sregs.x.dx = FP_OFF(paths);	/* offset to path */
#ifdef	M_I86LM
	segregs.ds = FP_SEG(paths);	/* segment for large model */
#endif /* M_I86LM */
	intdosx(&sregs, &oregs, &segregs);
	if(oregs.x.cflag)
	{
		errno = ENOTDIR;
		return(-1);
	}
	sregs.x.ax = 0x4f00;		/* search for next */
	for(dirno = 0; oregs.x.cflag == 0; intdosx(&sregs, &oregs, &segregs))
	{
	    if (!(buffer.d_attr & 0x08)) { /* skip Volume label */
		for(ptr = buffer.d_name; *ptr; ptr++)
			*ptr = tolower(*ptr);
		if(select == (int (*)())0 || (*select)(&buffer))
		{
			names = (struct direct **)realloc((char *)names,
				(dirno + 2)*sizeof(struct direct *));
			if(names == (struct direct **)0)
			{
				errno = ENOMEM;
				return(-1);
			}
			names[dirno] = (struct direct *)calloc(1,
				sizeof(struct direct));
			if(names[dirno] == (struct direct *)0)
			{
				errno = ENOMEM;
				return(-1);
			}
			*names[dirno] = buffer;
			names[++dirno] = (struct direct *)0;
		}
	    }
	}

	if(compar != (int (*)())0)
		qsort((char *)names, dirno, sizeof(char *),
		      (QSORT_FUN_TYPE)compar);

	*namelist = names;
	free(paths);			/* free temp space */
	return(dirno);
}

static int
freedir(dirs)
register struct direct **dirs;
{
	register int ii;

	if(dirs == (struct direct **)0)
		return(-1);
	for(ii = 0; dirs[ii] != (struct direct *)0; ii++)
		free(dirs[ii]);
	free(dirs);
	return(0);
}
#else
static char dummy; /* trick compilers that refuse to compile an empty file */
		   /* e.g. DEC Alpha OSF/1 cc */
#endif /* MSDOS */
