/******************************************************************************
 * NOTE: This file has been modified for use with MSDOS and the WATCOM C/386
 * compiler.  Darryl Okahata, March 1993.
 *****************************************************************************/

/* $RCSfile: directory.c,v $$Revision: 4.0.1.1 $$Date: 91/06/07 11:22:24 $
 *
 *    (C) Copyright 1987, 1988, 1990 Diomidis Spinellis.
 *
 *    You may distribute under the terms of either the GNU General Public
 *    License or the Artistic License, as specified in the README file.
 *
 * $Log:	directory.c,v $
 * Revision 4.0.1.1  91/06/07  11:22:24  lwall
 * patch4: new copyright notice
 * 
 * Revision 4.0  91/03/20  01:34:24  lwall
 * 4.0 baseline.
 * 
 * Revision 3.0.1.1  90/03/27  16:07:37  lwall
 * patch16: MSDOS support
 * 
 * Revision 1.3  90/03/16  22:39:40  dds
 * Fixed malloc problem.
 *
 * Revision 1.2  88/07/23  00:08:39  dds
 * Added inode non-zero filling.
 *
 * Revision 1.1  88/07/23  00:03:50  dds
 * Initial revision
 *
 */

/*
 * UNIX compatible directory access functions
 */

#include <sys/types.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <dos.h>
#include <ctype.h>
#ifdef __WATCOMC__
# include "dir.h"
#else
# include <sys/dir.h>
#endif

#include "dpmi.h"

/*
 * File names are converted to lowercase if the
 * CONVERT_TO_LOWER_CASE variable is defined.
 */
#define CONVERT_TO_LOWER_CASE

#define PATHLEN 65

#ifndef lint
static char rcsid[] = "$RCSfile: directory.c,v $$Revision: 4.0.1.1 $$Date: 91/06/07 11:22:24 $";
#endif

#ifdef DOS_EXTENDER
static DPMIREGS		dregs;
static DPMIBLOCK	dos_memory1, dos_memory2;
static int		dos_memory_allocated = 0;
typedef short int	WORD;		/* 2-byte variable */
#else
typedef int		WORD;		/* 2-byte variable */
#endif

#ifdef __WATCOMC__
/*
 * Turn on maximal structure packing (no alignment of any type).
 * We want this because the DOS dir_buff structure must be packed.
 * We put this here because #include "dmpi.h" resets the packing.
 */
# pragma pack(1);
#endif

DIR *
opendir(char *filename)
{
	DIR            *p;
	char           *oldresult, *result;
	union REGS      srv;
	struct SREGS    segregs;
	register        reslen = 0;
	char            scannamespc[PATHLEN];
	char		*scanname = scannamespc;	/* To take address we need a pointer */
#ifdef DOS_EXTENDER
	char far	*str1, *str2;
#endif

	/*
	 * Structure used by the MS-DOS directory system calls.
	 */
	struct dir_buff {
		char            reserved[21];	/* Reserved for MS-DOS */
		unsigned char   attribute;	/* Attribute */
		WORD		time;		/* Time */
		WORD		date;		/* Date */
		long            size;		/* Size of file */
		char            fn[13];		/* Filename */
	}
#ifdef DOS_EXTENDER
	far *buffptr;
	struct dir_buff buffspc, *buff = &buffspc;
#else
	/* Plain old DOS */
	buffspc, *buff = &buffspc;
#endif

#ifdef DOS_EXTENDER
	if (!dos_memory_allocated) {
		if (!dpmi_allocate_block(1024, &dos_memory1)) {
			return NULL;
		}
		if (!dpmi_allocate_block(1024, &dos_memory2)) {
			return NULL;
		}
		dos_memory_allocated = 1;
	}
#endif
	if (!(p = (DIR *) malloc(sizeof(DIR))))
		return NULL;

	/* Initialize result to use realloc on it */
	if (!(result = malloc(1))) {
		free(p);
		return NULL;
	}

	/* Create the search pattern */
	strcpy(scanname, filename);
	if (strchr("/\\", *(scanname + strlen(scanname) - 1)) == NULL)
		strcat(scanname, "/*.*");
	else
		strcat(scanname, "*.*");

#ifdef DOS_EXTENDER
	str1 = MK_FP(dos_memory1.selector, 0);
	_fstrcpy(str1, scanname);
	/*
	 * Set DTA to dos_memory2.segment:0
	 */
	memset(&dregs, 0, sizeof(dregs));
	dregs.EAX = 0x00001a00;
	dregs.DS = dos_memory2.segment;
	dregs.EDX = 0;
	dpmi_call_dos(&dregs);
	/*
	 * Setup pointer to scanname
	 */
	dregs.DS = dos_memory1.segment;
	dregs.EDX = 0;

	/*
	 * Reset buffptr to point to DTA
	 */
	buffptr = (struct dir_buff far *) MK_FP(dos_memory2.selector, 0);

	dregs.ECX = 0xff;	/* Search mode */

	for (dregs.EAX = 0x4e00; dpmi_call_dos(&dregs); dregs.EAX = 0x4f00) {
		buffspc = *buffptr;	/* Copy struct to where it can be
					   easily manipulated.  If we don't do
					   this, we have to worry about
					   truncated far pointers being passed
					   to functions.  There are, probably,
					   much more efficient ways to do this,
					   but this is the easiest.  */
		if ((result = (char *)
		     realloc(result, reslen + strlen(buff->fn) + 1)) ==
		    NULL) {
			free(p);
			free(oldresult);
			return NULL;
		}
		oldresult = result;
#ifdef CONVERT_TO_LOWER_CASE
		strcpy(result + reslen, strlwr(buff->fn));
#else
		strcpy(result + reslen, buff->fn);
#endif
		reslen += strlen(buff->fn) + 1;
	}
#else	/* not DOS_EXTENDER */
	segread(&segregs);
#if ( defined(M_I86LM) || defined(M_I86CM) || defined(M_I86HM) )
	segregs.ds = FP_SEG(buff);
	srv.x.dx = FP_OFF(buff);
#else
	srv.x.dx = (unsigned int) buff;
#endif
	srv.h.ah = 0x1a;	/* Set DTA to DS:DX */
	intdosx(&srv, &srv, &segregs);

#if ( defined(M_I86LM) || defined(M_I86CM) || defined(M_I86HM) )
	segregs.ds = FP_SEG(scanname);
	srv.x.dx = FP_OFF(scanname);
#else
	srv.x.dx = (unsigned int) scanname;
#endif

	srv.x.cx = 0xff;	/* Search mode */

	for (srv.h.ah = 0x4e; !intdosx(&srv, &srv, &segregs); srv.h.ah = 0x4f) {
		if ((result = (char *) realloc(result, reslen + strlen(buff->fn) + 1)) ==
 NULL) {
			free(p);
			free(oldresult);
			return NULL;
		}
		oldresult = result;
#ifdef CONVERT_TO_LOWER_CASE
		strcpy(result + reslen, strlwr(buff->fn));
#else
		strcpy(result + reslen, buff->fn);
#endif
		reslen += strlen(buff->fn) + 1;
	}
#endif	/* not DOS_EXTENDER */

	if (!(result = realloc(result, reslen + 1))) {
		free(p);
		free(oldresult);
		return NULL;
	} else {
		p->start = result;
		p->curr = result;
		*(result + reslen) = '\0';
		return p;
	}
}


struct direct  *
readdir(DIR *dirp)
{
	char           *p;
	register        len;
	static          dummy;

	p = dirp->curr;
	len = strlen(p);
	if (*p) {
		dirp->curr += len + 1;
		strcpy(dirp->dirstr.d_name, p);
		dirp->dirstr.d_namlen = len;
		/* To fool programs */
		dirp->dirstr.d_ino = ++dummy;
		return &(dirp->dirstr);
	} else {
		return NULL;
	}
}

long
telldir(DIR *dirp)
{
	return (long) dirp->curr;	/* ouch! pointer to long cast */
}

void
seekdir(DIR *dirp, long loc)
{
	dirp->curr = (char *) loc;	/* ouch! long to pointer cast */
}

void
rewinddir(DIR *dirp)
{
	dirp->curr = dirp->start;
}

void
closedir(DIR *dirp)
{
	free(dirp->start);
	free(dirp);
}
