home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Simtel MSDOS 1992 December
/
simtel1292_SIMTEL_1292_Walnut_Creek.iso
/
msdos
/
c
/
du.arc
/
MSD_DIR.C
< prev
next >
Wrap
C/C++ Source or Header
|
1987-12-26
|
8KB
|
307 lines
/*
* @(#)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
*
* Extended by Peter Lim (lim@mullian.oz) to overcome some MS DOS quirks
* and returns 2 more pieces of information - file size & attribute.
* Plus a little reshuffling of #define's positions December 1987
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "msd_dir.h"
#include <malloc.h>
#include <string.h>
#include <ctype.h>
#include <dos.h>
#include <direct.h>
#ifndef NULL
# define NULL 0
#endif /* NULL */
/* dos call values */
#define DOSI_FINDF 0x4e
#define DOSI_FINDN 0x4f
#define DOSI_SDTA 0x1a
#define DOSI_GCDIR 0x47
#define Newisnull(a, t) ((a = (t *) malloc(sizeof(t))) == (t *) NULL)
/* what find first/next calls look use */
typedef struct {
char d_buf[21];
char d_attribute;
unsigned short d_time;
unsigned short d_date;
unsigned long d_size;
char d_name[13];
} Dta_buf;
static char *getdirent();
static void setdta();
static void free_dircontents();
static char *extgetcwd();
static Dta_buf dtabuf;
static Dta_buf *dtapnt = &dtabuf;
static union REGS reg, nreg;
#if defined(M_I86LM)
static struct SREGS sreg;
#endif
DIR *
opendir(pathname, att_mask)
char *pathname;
unsigned int att_mask;
{
struct stat statb;
DIR *dirp;
char c;
char *s;
struct _dircontents *dp;
char nbuf[MAXPATHLEN + 1];
char name[MAXPATHLEN + 1];
unsigned char drive_id;
char *phead;
strcpy (name, pathname);
/* Work on temporary buffer only. Never write it back,
the calling argument may not have room for it ! */
if (stat(name, &statb) < 0 || (statb.st_mode & S_IFMT) != S_IFDIR) {
/* Give it a second try, after modifying input pathname,
before giving up to counter some MS DOS quirks.
This kludge is fairly simple and will not handle weird
though valid path name correctly such as multiple ../.. and
other mix which eventually end up on the root directory. */
if (isalpha (*name) && (name[1] == ':')) {
drive_id = *name - ((islower(*name)) ? 'a' : 'A') + 1;
phead = pathname+2;
}
else {
drive_id = 0;
phead = pathname;
}
if ((c = name[strlen(name) - 1]) == '\\' || c == '/')
name[strlen(name) - 1] = NULL;
/* Try removing one trailing / or \ */
if (*phead == '.' || *phead == '\0') {
/* If . or nothing specified, assume current directory
and go get the directory. */
if (extgetcwd (drive_id, name, MAXPATHLEN) == NULL)
return (DIR *) NULL;
strcpy (nbuf, name);
/* There is an undocumented BUG in MSC 4.0 such that
stat (root, ..) will cause the current directory on the
specified drive to be changed to root if the current
directory in question is exactly one level deep !
So, keep current directory for chdir() back after doing
stat (root, ..). lim@mullian.oz */
if (*(phead+1) == '.') {
/* i.e. ".." specified. Then backup one level. Firstly
check that we are not already at the root. */
if (name[strlen(name) - 1] == '\\')
return (DIR *) NULL;
while (name[strlen(name) - 1] != '\\')
name[strlen(name) - 1] = NULL;
if (*(phead+2) == '\\' || *(phead+2) == '/')
/* Make sure we don't have a '\' double up. */
strcat (name, phead+3);
}
else if (*(phead) == '.')
/* Just plain "." specified. */
strcat (name, phead+1);
}
else
*nbuf = NULL; /* Don't chdir() wrongly. */
if (stat(name, &statb) < 0 || (statb.st_mode & S_IFMT) != S_IFDIR)
return (DIR *) NULL;
if (*nbuf)
(void) chdir (nbuf); /* Fixing the stat() BUG ! */
}
if (Newisnull(dirp, DIR))
return (DIR *) NULL;
if (*name && (c = name[strlen(name) - 1]) != '\\' && c != '/')
(void) strcat(strcpy(nbuf, name), "\\*.*");
else
(void) strcat(strcpy(nbuf, name), "*.*");
dirp->dd_loc = 0;
setdta();
dirp->dd_contents = dirp->dd_cp = (struct _dircontents *) NULL;
if ((s = getdirent(nbuf, att_mask | A_DIR)) == (char *) NULL)
return dirp;
do {
if (Newisnull(dp, struct _dircontents) || (dp->_d_entry =
(char *) malloc((unsigned) (strlen(s) + 1))) == (char *) NULL)
{
if (dp)
free((char *) dp);
free_dircontents(dirp->dd_contents);
return (DIR *) NULL;
}
if (dirp->dd_contents)
dirp->dd_cp = dirp->dd_cp->_d_next = dp;
else
dirp->dd_contents = dirp->dd_cp = dp;
(void) strcpy(dp->_d_entry, s);
dp->d_attribute = dtabuf.d_attribute;
dp->d_size = dtabuf.d_size;
/* A SUPER Kludge ! Using 'dtabuf' as global variable. lim@mullian.oz */
dp->_d_next = (struct _dircontents *) NULL;
} while ((s = getdirent((char *) NULL, att_mask | A_DIR)) != (char *) NULL);
dirp->dd_cp = dirp->dd_contents;
return dirp;
}
void
closedir(dirp)
DIR *dirp;
{
free_dircontents(dirp->dd_contents);
free((char *) dirp);
}
struct direct *
readdir(dirp)
DIR *dirp;
{
static struct direct dp;
if (dirp->dd_cp == (struct _dircontents *) NULL)
return (struct direct *) NULL;
dp.d_namlen = dp.d_reclen =
strlen(strcpy(dp.d_name, dirp->dd_cp->_d_entry));
dp.d_ino = 0;
dp.d_attribute = dirp->dd_cp->d_attribute;
dp.d_size = dirp->dd_cp->d_size;
dirp->dd_cp = dirp->dd_cp->_d_next;
dirp->dd_loc++;
return &dp;
}
void
seekdir(dirp, off)
DIR *dirp;
long off;
{
long i = off;
struct _dircontents *dp;
if (off < 0)
return;
for (dp = dirp->dd_contents ; --i >= 0 && dp ; dp = dp->_d_next)
;
dirp->dd_loc = off - (i + 1);
dirp->dd_cp = dp;
}
long
telldir(dirp)
DIR *dirp;
{
return dirp->dd_loc;
}
static void
free_dircontents(dp)
struct _dircontents *dp;
{
struct _dircontents *odp;
while (dp) {
if (dp->_d_entry)
free(dp->_d_entry);
dp = (odp = dp)->_d_next;
free((char *) odp);
}
}
static char *
getdirent(dir, att_mask)
char *dir;
unsigned int att_mask;
{
if (dir != (char *) NULL) { /* get first entry */
reg.h.ah = DOSI_FINDF;
reg.h.cl = att_mask;
#if defined(M_I86LM)
reg.x.dx = FP_OFF(dir);
sreg.ds = FP_SEG(dir);
#else
reg.x.dx = (unsigned) dir;
#endif
} else { /* get next entry */
reg.h.ah = DOSI_FINDN;
#if defined(M_I86LM)
reg.x.dx = FP_OFF(dtapnt);
sreg.ds = FP_SEG(dtapnt);
#else
reg.x.dx = (unsigned) dtapnt;
#endif
}
#if defined(M_I86LM)
intdosx(®, &nreg, &sreg);
#else
intdos(®, &nreg);
#endif
if (nreg.x.cflag)
return (char *) NULL;
return dtabuf.d_name;
}
static void
setdta()
{
reg.h.ah = DOSI_SDTA;
#if defined(M_I86LM)
reg.x.dx = FP_OFF(dtapnt);
sreg.ds = FP_SEG(dtapnt);
intdosx(®, &nreg, &sreg);
#else
reg.x.dx = (int) dtapnt;
intdos(®, &nreg);
#endif
}
static char *extgetcwd(drive_id, buffer, buffer_size)
/* Extended get current directory on specified drive. Peter Lim 07-Dec-87. */
unsigned char drive_id;
char *buffer;
int buffer_size;
{
char tmpbuffer[MAXPATHLEN+1];
if (!drive_id)
return (getcwd (buffer, buffer_size));
/* If it is current drive, use the standard getcwd() */
reg.h.ah = DOSI_GCDIR;
reg.h.dl = drive_id;
#if defined(M_I86LM)
reg.x.si = FP_OFF(tmpbuffer);
sreg.ds = FP_SEG(tmpbuffer);
intdosx(®, &nreg, &sreg);
#else
reg.x.si = (int) tmpbuffer;
intdos(®, &nreg);
#endif
if (nreg.x.ax == 0xf)
return ((char *) NULL);
/* Invalid drive specification. */
else {
if (drive_id)
sprintf (buffer, "%c:\\%s", drive_id+'A'-1, tmpbuffer);
else
sprintf (buffer, "\\%s", tmpbuffer);
return (buffer);
}
}