/************************************************************************
 * This program is Copyright (C) 1986 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.                                           *
 ************************************************************************/
/*+++*
 *  title:	readdir.h
 *  abstract:	basic directory handling.
 *  author:	T.R.Hageman, Groningen, The Netherlands.
 *  created:	January 1991, merged from existing sources.
 *  modified:	this obsoletes {atari,msdos}dir.inc
 *		23-Jul-91, add MAC types (implementation in mac.c)
 *  description:
 *	This declares the basic directory handling functions opendir,
 *	readdir, closedir and the higher-level scandir and friends.
 *
 *	If the compile-time flag READDIR_IMPLEMENTATION is defined,
 *	a limited implementation (or emulation) of these opendir, readdir,
 *	closedir is generated if your system does not support this -- if
 *	your UNIX has BSD-style directory (long names) be sure to define
 *	BSD_DIR so that the library readdir routines are used.
 *	This is done this peculiar way since we need these both in JOVE
 *	(from scandir.c) and in recover, in a slightly different version
 *	for either, and I wanted to keep the system-dependencies localized.
 *
 *  BUGS:
 *	some systems do have readdir support but use different or
 *	additional header files.
 *	- MAC and VMS implementations are in "mac.c" and "vms.c", respectively.
 *---*/

#ifndef _READDIR_H_
#define _READDIR_H_

#ifndef TUNED
#   include "tune.h"
#endif

#ifdef READDIR_IMPLEMENTATION
RCS_H(readdir, "$Id: readdir.h,v 14.30.0.6 1993/10/28 00:54:49 tom Exp tom $")
#endif

/* Define types `DIR' and `struct dirent' for systems that don't have'em. */

#if _I_DIRENT
#   include <dirent.h>

#   define DIRSIZE(entry)	(strlen((entry)->d_name))
#   define BSD_DIR
#else
#   define dirent	direct
# if unix
#   ifdef SYSNDIR
#	include <sys/ndir.h>
#	define BSD_DIR
#   else
#     ifndef SYSDIR
#	ifdef SYSV
#	    include <ndir.h>
#	    define BSD_DIR
#	else
#	    define SYSDIR	/* Provide default. */
#	endif
#     endif
#     ifdef SYSDIR
#	include <sys/dir.h>
#	ifndef V7
#	    ifndef MINIX
#		define BSD_DIR
#	    endif
#	endif
#     endif /* SYSDIR */
#   endif /* SYSNDIR */

#   ifdef BSD_DIR
#	define DIRSIZE(entry)	((entry)->d_namlen)
#   else
#	define DIRSIZE(entry)	((entry)->d_name[DIRSIZ-1] ? DIRSIZ : \
				 strlen((entry)->d_name))
#	ifdef NOT_JOVE
	    /* it is assumed that <stdio.h> is already included */
#	    define DIR	FILE
#	else
#	    ifndef _IO_H_
#		include "io.h"
#	    endif
#	    define DIR	File
#	endif
#   endif
# else /* !unix */

#   ifdef DOS
#	define DIRSIZ	14	/* 8 + 1 + 3 + 1 rounded up to word-boundary */
#   endif
#   ifdef MAC
#	define DIRSIZ	FILESIZE
#   endif
#   if vms
#	define DIRSIZ	FILESIZE
#   endif

struct direct {
	char	d_name[DIRSIZ];
};

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

# endif /*  unix */

/* Hide differences between Atari and MSDOS */

# ifdef DOS
#   ifdef ATARIST
#	include <osbind.h>	/* should define DTA */
#	ifdef __GNUC__
#	    define DTA		struct _dta
#	endif
#   endif
#   ifdef MSDOS
#	include <dos.h>
#	ifdef __TURBOC__
#	    include <dir.h>
#	    define DTA		struct ffblk
#	else /* MSC */
#	    define DTA		struct find_t
#	endif
#   endif

typedef struct {
	DTA	d_dta;
#   ifdef ATARIST
	DTA	*d_orgdta;
#   endif
} DIR;
# endif /* DOS */

# ifdef MAC
typedef struct {
	int	d_vol_id;
	long	d_dir_id;
	int	d_index;
} DIR;
# endif /* MAC */

# if vms
typedef struct {
	int	d_context;
	char	d_vms_dirname[FILESIZE];
	int	d_baselen;
	struct {
		unsigned short length;
		char body[FILESIZE];
	} d_next_entry;
} DIR;
# endif /* vms */
#endif /* _I_DIRENT */

/* function prototypes. */

#ifndef BSD_DIR
#   ifndef NO_PROCDECL
extern DIR		*opendir __(( const char *_(dir) ));
extern struct dirent	*readdir __(( DIR *_(dp) ));
extern void		closedir __(( DIR *_(dp) ));
#   endif
#endif

#ifndef NOT_JOVE
#   ifdef CHDIR
#	ifndef TINY
extern int	ask_dir_only;
#	endif
#   endif
#else
#   define	ask_dir_only (-1)
#endif

/* This is requested from scandir.c and recover.c */

#ifdef READDIR_IMPLEMENTATION

# if !_I_DIRENT
#   ifndef S_ISDIR	/* not yet loaded? */
#	ifndef S_IFDIR
#	    if _I_SYS_STAT - -1
#		include <sys/stat.h>
#	    else
#		if _I_STAT
#		    include <stat.h>
#		endif
#	    endif
#	endif
#	ifndef S_ISDIR
#	    define S_ISDIR(m)	(((m)&S_IFMT)==S_IFDIR)
#	endif
#   endif

#   if unix
#	ifndef BSD_DIR
#
#	    ifdef NOT_JOVE
#		define DIR_OPEN(dname)	fopen(dname, "rb")
#		define DIR_STAT(d,st)	fstat(fileno(d), st)
#		define DIR_CLOSE(d)	fclose(d)
#		define DIR_READ(d,b,s)	(fread(b, 1, s, d) - s)
#	    else
#		define DIR_OPEN(dname)	f_open(dname, F_READ|F_BINARY, iobuff, BLKSIZ)
#		define DIR_STAT(d,st)	fstat((d)->f_fd, st)
#		define DIR_CLOSE(d)	f_close(d)
#		define DIR_READ(d,b,s)	fgetnchar((char *)(b), s, d)
#	    endif

/* At most one DIR can be active simultaneously, but that is all we need. */

DIR *
opendir(dir)
const char	*dir;
{
	register DIR	*dp;
	struct stat	stbuf;

	if (dp = DIR_OPEN(dir)) {
		if (DIR_STAT(dp, &stbuf) < 0 || !S_ISDIR(stbuf.st_mode)) {
			closedir(dp);
			dp = NULL;	/* this isn't a directory! */
		}
	}
	return dp;
}

void
closedir(dp)
DIR	*dp;
{
	DIR_CLOSE(dp);
}

struct direct *
readdir(dp)
register DIR	*dp;
{
	static struct direct	dir;

	do
		if (DIR_READ(dp, &dir, sizeof dir) != 0)
			return NULL;
	while (dir.d_ino == 0);

	return &dir;
}

#	    undef DIR_OPEN
#	    undef DIR_STAT
#	    undef DIR_CLOSE
#	    undef DIR_READ
#	endif /* !BSD_DIR */
#   endif /* unix */


#   ifdef DOS

#	ifndef _IO_H
#	    include "io.h"	/* for FIX_FILENAME */
#	endif

/* More disguise. */

#	ifdef ATARIST
#	    ifdef __GNUC__
#		define d_attrib		dta_attribute
#		define d_fname		dta_name
#		define FA_READONLY	FA_RDONLY
#		define FA_SUBDIR	FA_DIR
#		define FA_ARCHIVE	FA_CHANGED
#	    endif
#	    define DIR_FIRST(name,mode,dp)	((dp)->d_orgdta = Fgetdta(), \
						 Fsetdta(&(dp)->d_dta),	     \
						 (int) Fsfirst(name, mode))

#	    define DIR_NEXT(dp)			Fsnext()
#	    define DIR_CLOSE(dp)		Fsetdta((dp)->d_orgdta)
#	    define FA_ALL (FA_READONLY|FA_HIDDEN|FA_SYSTEM|FA_SUBDIR|FA_ARCHIVE)
#	endif
#	ifdef MSDOS
#	    ifdef __TURBOC__
#		define d_fname			ff_name
#		define d_attrib			ff_attrib
#		define DIR_FIRST(n,m,dp)	findfirst(n, &(dp)->d_dta, m)
#		define DIR_NEXT(dp)		findnext(&(dp)->d_dta)
#		define DIR_CLOSE(dp)
#		define FA_ALL (FA_RDONLY|FA_HIDDEN|FA_SYSTEM|FA_DIREC|FA_ARCH)
#		define FA_SUBDIR	FA_DIREC
#	    else /* MSC */
#		define d_fname			name
#		define d_attrib			attrib
#		define DIR_FIRST(n,m,dp)	_dos_findfirst(n,m,&(dp)->d_dta)
#		define DIR_NEXT(dp)		_dos_findnext(&(dp)->d_dta)
#		define DIR_CLOSE(dp)
#		define FA_ALL (_A_NORMAL|_A_RDONLY|_A_HIDDEN|_A_SYSTEM|_A_SUBDIR|_A_ARCH)
#		define FA_SUBDIR	_A_SUBDIR
#	    endif
#	endif

/* At most one DIR can be active simultaneously, but that is all we need. */

DIR *
opendir(dir)
const char	*dir;
{
	static DIR	theDIR;
	register DIR	*dp = &theDIR;
	char		dirpath[FILESIZE];
	struct stat	stbuf;

	make_filename(dirpath, dir, "*.*");

	if (DIR_FIRST(dirpath, FA_ALL, dp) != 0) {
		if (stat(dir, &stbuf) < 0 || !S_ISDIR(stbuf.st_mode)) {
			closedir(dp);
			return NULL;
		}
		dp->d_dta.d_fname[0] = '\0';	/* to flag the end */
	}
	return dp;
} /* opendir */

void
closedir(dp)
register DIR	*dp;
{
	DIR_CLOSE(dp);
}

struct direct *
readdir(dp)
register DIR	*dp;
{
	static struct direct	dir;

	if (dp->d_dta.d_fname[0] == '\0')
		return NULL;

#	ifndef NOT_JOVE
#	    ifdef CHDIR
#		ifndef TINY
	/* Cheat -- deliver only directories if so requested;
	   This is much more efficient than delivering all filenames
	   and stat()ing each of them. */
	if (ask_dir_only >= 0)
		while (!(dp->d_dta.d_attrib & FA_SUBDIR))
			if (DIR_NEXT(dp) != 0)
				return NULL;
#		endif
#	    endif
#	endif

	strcpy(dir.d_name, dp->d_dta.d_fname);
	FIX_FILENAME(dir.d_name);

	if (DIR_NEXT(dp) != 0)
		dp->d_dta.d_fname[0] = '\0';	/* to flag the end */

	return &dir;
}

#	undef DIR_FIRST
#	undef DIR_NEXT
#	undef DIR_CLOSE
#	undef FA_ALL
#	undef d_fname
#   endif /* DOS */

#   ifdef MAC
#	ifdef NOT_JOVE
#	    include "mac.c"		/* most of which is excluded. */
#	endif
#   endif

#   if vms
#	ifdef NOT_JOVE
#	    include "vms.c"		/* most of which is excluded. */
#	endif
#   endif

# endif /* _I_DIRENT */

#endif /* READDIR_IMPLEMENTATION */


/* scandir.c */

#ifndef NOT_JOVE
#   ifdef F_COMPLETION

/* this is to avoid conflict with prototype in <sys/dir.h> on some systems. */
#	define scandir	Jscandir
#	define freedir	Jfreedir

#ifndef NO_PROCDECL
extern int	scandir __(( const char *_(dir), char ***_(nmptr),
			     int (*_(qualify))(const char *_(name)),
			     int (*_(sorter))(const char **_(a),
					      const char **_(b)) ));

extern void	freedir __(( char ***_(nmptr), int _(nent) ));
#endif /* NO_PROCDECL */
extern int	alphacomp __(( const char **_(a), const char **_(b) ));

#   endif
#endif

#endif /* _READDIR_H_ */


/*======================================================================
 * $Log: readdir.h,v $
 * Revision 14.30.0.6  1993/10/28  00:54:49  tom
 * (DOS) readdir: replace strlwr() with FIX_FILENAME().
 *
 * Revision 14.30  1993/01/26  18:43:05  tom
 * cleanup whitespace.
 *
 * Revision 14.28  1992/10/23  17:15:58  tom
 * convert to "port{ansi,defs}.h" conventions; improve Posix compatibility.
 *
 * Revision 14.27  1992/09/21  23:54:20  tom
 * add missing #endif (how did I miss it???); remove empty `#' directives.
 *
 * Revision 14.26  1992/08/26  23:56:47  tom
 * some portability improvements (I hope:-); add RCS directives.
 *
 */
