Metropoli BBS
VIEWER: director.c MODE: TEXT (ASCII)
/*
 * @(#)msd_dir.c 1.4 87/11/06	Public Domain.
 *
 *  A public domain implementation of BSD directory routines for
 *  MS-DOS.  Written by Michael Rendell ({uunet,utai}michael@garfield),
 *  August 1897
 *
 *  Modified by Ian Stewartson, Data Logic.
 */

#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <string.h>
#include <limits.h>
#include <ctype.h>
#include <errno.h>
#include <dirent.h>
#ifdef OS2
#define INCL_DOSFILEMGR
#include <os2.h>
#else
#include <dos.h>
#endif

#ifdef OS2
#  define ATTRIBUTES		(FILE_DIRECTORY | FILE_HIDDEN | FILE_SYSTEM | \
				 FILE_NORMAL | FILE_READONLY | FILE_ARCHIVED)
#else
#  define ATTRIBUTES		(_A_SUBDIR | _A_HIDDEN | _A_SYSTEM | \
				 _A_NORMAL | _A_RDONLY | _A_ARCH)
#endif

typedef struct _dircontents	DIRCONT;
static void			free_dircontents (DIRCONT *);

DIR		*opendir (name)
const char	*name;
{
    struct stat		statb;
    DIR			*dirp;
    char		*last;
    DIRCONT		*dp;
    char		*nbuf;
#ifdef OS2
    FILEFINDBUF 	dtabuf;
    HDIR		d_handle = HDIR_SYSTEM;
    USHORT		d_count = 1;
    bool		HPFS = FALSE;
#else
    struct find_t	dtabuf;
#endif
    int			len = strlen (name);
    
    if (!len)
    {
	errno = ENOTDIR;
	return (DIR *)NULL;
    }

    if ((nbuf = malloc (len + 5)) == (char *)NULL)
	return (DIR *) NULL;

    strcpy (nbuf, name);
    last = &nbuf[len - 1];

/* Ok, DOS is very picky about its directory names.  The following are
 * valid.
 *
 *  c:/
 *  c:.
 *  c:name/name1
 *
 *  c:name/ is not valid
 */

    if (((*last == '\\') || (*last == '/')) && (len > 1) &&
	(!((len == 3) && (name[1] == ':'))))
	*(last--) = 0;

/* Check its a directory */    

    if (stat (nbuf, &statb) < 0)
    {
	free (nbuf);
	return (DIR *) NULL;
    }

    if (!S_ISDIR (statb.st_mode))
    {
	free (nbuf);
	errno = ENOTDIR;
	return (DIR *)NULL;
    }

    if ((dirp = (DIR *) malloc (sizeof (DIR))) == (DIR *) NULL)
    {
	free (nbuf);
	return (DIR *) NULL;
    }

/* Set up to find everything */

    if ((*last != '\\') && (*last != '/'))
	strcat (last, "/");

    strcat (last, "*.*");

/* For OS/2, find the file system type */

#ifdef OS2
    HPFS = IsHPFSFileSystem (nbuf);
#endif

    dirp->dd_loc      = 0;
    dirp->dd_cp       = (DIRCONT *) NULL;
    dirp->dd_contents = (DIRCONT *) NULL;

#ifdef OS2
    if (DosFindFirst (nbuf, &d_handle, ATTRIBUTES, &dtabuf,
		      sizeof (FILEFINDBUF), &d_count, (ULONG)0) != 0)
#else
    if (_dos_findfirst (nbuf, ATTRIBUTES, &dtabuf) != 0)
#endif
    {
	free (nbuf);
	return dirp;
    }

    do 
    {
#ifdef OS2
	if (((dp = (DIRCONT *) malloc (sizeof (DIRCONT))) == (DIRCONT *)NULL) ||
	    ((dp->_d_entry = strdup (dtabuf.achName)) == (char *) NULL))
#else
	if (((dp = (DIRCONT *) malloc (sizeof (DIRCONT))) == (DIRCONT *)NULL) ||
	    ((dp->_d_entry = strdup (dtabuf.name)) == (char *) NULL))
#endif
	{
	    if (dp != (char *)NULL)
		free ((char *)dp);

	    free (nbuf);
	    free_dircontents (dirp->dd_contents);
#ifdef OS2
	    DosFindClose (d_handle);
#endif
	    return (DIR *) NULL;
	}

#ifdef OS2
	if (!HPFS)
	    strlwr (dp->_d_entry);
#endif

	if (dirp->dd_contents != (DIRCONT *) NULL)
	    dirp->dd_cp = dirp->dd_cp->_d_next = dp;

	else
	    dirp->dd_contents = dirp->dd_cp = dp;

	dp->_d_next = (DIRCONT *) NULL;

#ifdef OS2
	d_count = 1;
    } while (DosFindNext (d_handle, &dtabuf, sizeof (FILEFINDBUF),
			  &d_count) == 0);
#else
    } while (_dos_findnext (&dtabuf) == 0);
#endif

    dirp->dd_cp = dirp->dd_contents;
    free (nbuf);

#ifdef OS2
    DosFindClose (d_handle);
#endif

    return dirp;
}

int	closedir (dirp)
DIR	*dirp;
{
    free_dircontents (dirp->dd_contents);
    free ((char *)dirp);
    return 0;
}

struct dirent	*readdir (dirp)
DIR		*dirp;
{
    static struct dirent	dp;
    
    if (dirp->dd_cp == (DIRCONT *) NULL)
	return (struct dirent *) NULL;

    dp.d_reclen = strlen (strcpy (dp.d_name, dirp->dd_cp->_d_entry));
    dp.d_off    = dirp->dd_loc * 32;
    dp.d_ino    = (ino_t)++dirp->dd_loc;
    dirp->dd_cp = dirp->dd_cp->_d_next;
#ifndef OS2
    strlwr (dp.d_name);
#endif

    return &dp;
}

void	rewinddir (dirp)
DIR	*dirp;
{
    seekdir (dirp, (off_t)0);
}

void	seekdir (dirp, off)
DIR	*dirp;
off_t	off;
{
    long	i = off;
    DIRCONT	*dp;

    if (off < 0L)
	return;

    for (dp = dirp->dd_contents; (--i >= 0) && (dp != (DIRCONT *)NULL);
	 dp = dp->_d_next)
	;

    dirp->dd_loc = off - (i + 1);
    dirp->dd_cp = dp;
}

off_t	telldir(dirp)
DIR	*dirp;
{
    return dirp->dd_loc;
}

static void	free_dircontents (dp)
DIRCONT		*dp;
{
    DIRCONT	*odp;

    while ((odp = dp) != (DIRCONT *)NULL) 
    {
	if (dp->_d_entry != (char *)NULL)
	    free (dp->_d_entry);

	dp = dp->_d_next;
	free ((char *)odp);
    }
}

/* 
 * For OS/2, we need to know if we have to convert to lower case.  This
 * only applies to non-HPFS (FAT, NETWARE etc) file systems.
 */

#ifdef OS2
bool		IsHPFSFileSystem (char *directory)
{
    USHORT	nDrive;
    ULONG	lMap;
    BYTE	bData[64];
    BYTE	bName[3];
    USHORT	cbData;

    if ( _osmode == DOS_MODE )
	return FALSE;

/*
 * Mike tells me there are IFS calls to determine this, but he carn't
 * remember which.  So we read the partition info and check for HPFS.
 */

    if (isalpha (directory[0]) && (directory[1] == ':'))
	nDrive = toupper (directory[0]) - '@';

    else
	DosQCurDisk (&nDrive, &lMap);

/* Set up the drive name */

    bName[0] = (char) (nDrive + '@');
    bName[1] = ':';
    bName[2] = 0;

    cbData = sizeof (bData);

/* Read the info, if we fail - assume non-HPFS */

    if (DosQFSAttach (bName, 0, FSAIL_QUERYNAME, bData, &cbData, 0L) )
	return FALSE;

    else if (!strcmp (bData + (*((USHORT *) (bData + 2)) + 7), "HPFS"))
	return TRUE;

    else
	return FALSE;
}
#endif
[ RETURN TO DIRECTORY ]