[comp.sources.misc] DU for MS DOS

lim@mullian.oz.AU (Lim Ngee Ching) (12/09/87)

    [Moderator's note:

    This software arrived as two postings, the second arriving before
    I had posted the first.  The second shar contained new versions
    of the files du.c and msd_dir.c, and a new file showbug.c.  I have
    repackaged these along with the other files from the first shar
    into a single shar.  Here is the author's note from the first
    posting:]

I have waited around long enough for a proper MS DOS DU, so I wrote my
own.

This "DU" compiles in MSC 4.0 under MS DOS. As such it has very strong
MS DOS flavour. It is basically a superset of the Unix DU - a lot more
options available - to cater for the quirks of MS DOS. Essentially it
produces the same reading as CHKDSK in MS DOS except in cases where
you have directories which used to hold a lot of files which has been
erased - the erased directory entries will not be accounted for, and
can't be without resorting to very low level FAT fiddling which is not
worth the while. At the moment, it doesn't allow specification of files
- only the whole directory - may be someone would like to change this.
It also displays sizes in bytes and not kilobytes - a feature to suit
MS DOS's various cluster sizes.

Msd_dir.* are borrowed from the TAR package put together by Michael
Rendell ({uunet,utai}michael@garfield) with some fairly kludgy modifi-
cations and extensions. See read.me for some extra info (not much :-)).
For now unshar this message and type 'make make.msc' to compile DU.

-----------------------------------------------------------------------
This program is placed in the public domain without any warranty. As
far as I know it is not a virius ;-).

From:	Peter Lim,	lim@mullian.oz

    [And the author's note from the second posting:

						-- John.]

Oops ! I didn't test the program thoroughly enough. Just found one
bug. Fixed here. It allows du .. if .. specifies the root directory.
I also stumble on a rather serious bug in MSC 4.0 with stat().
Read msd_dir.c for detail, a program SHOWBUG.C is included to
demonstrate this bug. Hope I don't have to fix any more bug. :-)

Peter Lim

# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# Wrapped by john on Thu Dec 10 02:05:11 EST 1987
# Contents:  read.me du.c du.hlp make.msc msd_dir.c msd_dir.h showbug.c
 
echo x - read.me
sed 's/^@//' > "read.me" <<'@//E*O*F read.me//'
This program (DU) should be compiled at least using COMPACT memory model.
The recursive nature would readily overflow a SMALL memory model program's
data space. Beside that, the stack should be much bigger than the default
value of 2048 (say 10k or 20k would be fine). However, one might consider
using the xVARSTCK.OBJ supplied by Microsoft (see page 197, chapter 9 --
Advanced Topics, of Microsoft C compiler User's Guide). Then one should use
the /Gs or /Ox option during compilation.


One probable compilation command would be :

  CL /Gs /Ox /AC du.c msd_dir.c cvarstck.obj -o du -link /STACK:20480


Peter lim
07-Dec-87
@//E*O*F read.me//
chmod u=rw,g=r,o=r read.me
 
echo x - du.c
sed 's/^@//' > "du.c" <<'@//E*O*F du.c//'
/*
 * DU - Disk Usage. A Unix style utility. Written by Peter Lim 07-Dec-87.
 *
 *	Usage:  du [-s] [-a] [-z] [-c] [-r] [-h] [-nnnn] [pathname(s) ... ]
 *		where,
 *			-s : Summary listing only  (Toggle, default=off).
 *			-a : Generate a list of all files
 *						   (Toggle, default=off).
 *			-z : Show total statistics (Toggle, default=on).
 *			-c : Show cluster size	   (Toggle, default=on).
 *			-r : Recursive traversal of sub-directories
 *						   (Toggle, default=on).
 *			-h : Include HIDDEN & SYSTEM files
 *						   (Toggle, default=off).
 *			-nnnn : Force cluster size to be nnnn bytes.
 *				nnnn = 0 releases previous forcing.
 *		Default pathname is the current directory on current drive.
 */

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "msd_dir.h"
#include <dos.h>
#include <strings.h>
#include <ctype.h>


unsigned long	traverse_dir();
unsigned long	get_cluster_size();
unsigned long	get_path_size ();
#define  print_size(size, path)  printf ("%-11lu%s\n", size, path)

unsigned long	bpc;		/* number of bytes per cluster */
int	filecnt=0, dircnt=0;
int	summary=0, show_all=0, show_stat=1,
	show_cluster=1, force_cluster=0, recurse=1, incl_hidn=0;
unsigned int	att_mask;

main (argc, argv)
int argc;
char **argv;
{
	unsigned long	total=0;
	int		path_specified=0;

	for (; --argc > 0; ) {
		*++argv;
		if (**argv == '-')
			switch ((*argv)[1]) {
				case 's' :
				case 'S' :
					summary = !summary;
					continue;
				case 'z' :
				case 'Z' :
					show_stat = !show_stat;
					continue;
				case 'a' :
				case 'A' :
					show_all = !show_all;
					continue;
				case 'c' :
				case 'C' :
					show_cluster = !show_cluster;
					continue;
				case 'r' :
				case 'R' :
					recurse = !recurse;
					continue;
				case 'h' :
				case 'H' :
					incl_hidn = !incl_hidn;
					continue;
				default  :
					if (!sscanf (*argv, "-%lu", &bpc))
						printf ("Unknown option %s\n", *argv);
					else
						force_cluster = bpc ? 1 : 0;
					continue;
			}
		path_specified = 1;
			/* At this point we know at least one path is specified. */
		total += get_path_size(*argv);
	}

	if (!path_specified)
		total = get_path_size(".");
		/* If no pathname were specified. */

	if (show_stat) {
	   printf ("Total %d files in %d directories.\n", filecnt, dircnt);
	   printf ("Total disk space used = %lu bytes (%.2lfk).\n",
			 total, total / 1024.0);
	}
}


unsigned long	get_path_size (pathname)
char *pathname;
{
	unsigned char	drive_id;
	unsigned long	total;

	if (incl_hidn)
		att_mask = (A_HIDDEN | A_SYSTEM);
	else
		att_mask = 0;	/* Set attribute mask for files to find.
				   A_DIR will always be set. */
	if (!force_cluster) {
		if (isalpha (*pathname) && (pathname[1] == ':'))
		    drive_id = *pathname -  ((islower(*pathname)) ? 'a' : 'A') + 1;
		else
		    drive_id = 0;
		if (!(bpc =  get_cluster_size(drive_id))) {
			printf ("Invalid drive %c\:\n", *pathname);
			exit (1);
		}
	}
	if (show_cluster)
		printf ("Cluster size = %lu bytes.\n", bpc);
	total = traverse_dir(pathname);
	if (summary)
		print_size (total, pathname);
		/* At least say something even if only summary is required. */
	return (total);
}


unsigned long	traverse_dir(cur_path)
char	*cur_path;
{
	DIR *dp;
	struct direct *direntry;
	char	s[MAXPATHLEN+1];
	char	c;
	unsigned long	total, file_size;
	unsigned int	dir_ent_cnt;	/* Count the number of directory entry. */
	#define  bpdent (unsigned int) 32
		/* Number of bytes per directory entry,
		   = 32 from DOS 2.10 tech ref pp. 4-5.  lim@mullian.oz */
	int	not_root_dir;

	total = 0;
	if (!(dp=opendir(cur_path, att_mask))) {
		printf ("Can't open directory \"%s\" or memory allocation failure.\n",
			cur_path);
		exit(2);
	}

	if (recurse) {
		while (direntry=readdir(dp))
			if (((*direntry).d_attribute == A_DIR)
			    && (strcmp ((*direntry).d_name, "."))
			    && (strcmp ((*direntry).d_name, ".."))) {
				   strcpy (s, cur_path);
				   if ((c = s[strlen(s)-1]) != '\\' &&
					c != '/' && c != ':')
					strcat (s, "\\");
				   strcat (s, (*direntry).d_name);
					total += traverse_dir(s);
			}
		(void)	rewinddir(dp);
	}

	dir_ent_cnt = not_root_dir = 0;
	while (direntry=readdir(dp)) {
		dir_ent_cnt++;
		if ((*direntry).d_attribute != A_DIR) {
			total += file_size = ( ((*direntry).d_size / bpc) +
				 (((*direntry).d_size % bpc) ? 1 : 0) ) * bpc;
			if (show_all) {
				strcpy (s, cur_path);
				if ((c = s[strlen(s)-1]) != '\\' && c != '/')
					strcat (s, "\\");
				print_size (file_size, strcat (s, (*direntry).d_name));
			}
			filecnt++;	/* Counting all files (exclude dir). */
		}
		else if (!strcmp ((*direntry).d_name, ".")) {
			dircnt++;	/* Counting every occurance of ".". */
			not_root_dir = 1;
			/* Not root directory if "." exist. */
		}
	}
	if (not_root_dir)
		total += ( ((dir_ent_cnt * bpdent) / bpc) +
			  (((dir_ent_cnt * bpdent) % bpc) ? 1 : 0) ) * bpc;
		/* Add the number of directory entry counted * bytes per entry rounded
		   up to the nearest cluster. The only things missed by this method of
		   counting are the directories with a lot of erased files. Can't be
		   helped without resorting to very low level FAT probing.
		   NOTE: The root directory uses zero byte here - complying
			 with CHKDSK from MS DOS.  Another MS DOS quirk. */
	if (!summary)
		print_size (total, cur_path);

	closedir(dp);
	return (total);
}


#define DOSI_GDFREE	0x36;
static	union REGS	reg, nreg;

unsigned long get_cluster_size(drive_id)
unsigned char	drive_id;
{
	reg.h.ah = DOSI_GDFREE;
	reg.h.dl = drive_id;
	intdos(&reg, &nreg);
	if (nreg.x.ax == 0xffff)
		return ((unsigned long) 0);
	else
		return ((unsigned long) nreg.x.cx * nreg.x.ax);
}
@//E*O*F du.c//
chmod u=rw,g=r,o=r du.c
 
echo x - du.hlp
sed 's/^@//' > "du.hlp" <<'@//E*O*F du.hlp//'

  DU - Disk Usage. A Unix style utility. Written by Peter Lim 07-Dec-87.
 
 	Usage:  du [-s] [-a] [-z] [-c] [-r] [-h] [-nnnn] [pathname(s) ... ]
 		where,
 			-s : Summary listing only  (Toggle, default=off).
 			-a : Generate a list of all files
 						   (Toggle, default=off).
 			-z : Show total statistics (Toggle, default=on).
 			-c : Show cluster size	   (Toggle, default=on).
 			-r : Recursive traversal of sub-directories
 						   (Toggle, default=on).
 			-h : Include HIDDEN & SYSTEM files
 						   (Toggle, default=off).
 			-nnnn : Force cluster size to be nnnn bytes.
 				nnnn = 0 releases previous forcing.
 		Default pathname is the current directory on current drive.

@//E*O*F du.hlp//
chmod u=rw,g=r,o=r du.hlp
 
echo x - make.msc
sed 's/^@//' > "make.msc" <<'@//E*O*F make.msc//'
CFLAGS = /AC
LFLAGS = /STACK:20480

@.c.obj:
		msc $(CFLAGS) $*;

du.obj:		du.c msd_dir.h

msd_dir.obj:	msd_dir.c msd_dir.h

du.exe:		du.obj msd_dir.obj
		link $(LFLAGS) du+msd_dir;
@//E*O*F make.msc//
chmod u=rw,g=r,o=r make.msc
 
echo x - msd_dir.c
sed 's/^@//' > "msd_dir.c" <<'@//E*O*F msd_dir.c//'
/*
 * @(#)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(&reg, &nreg, &sreg);
#else
	intdos(&reg, &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(&reg, &nreg, &sreg);
#else
	reg.x.dx = (int) dtapnt;
	intdos(&reg, &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(&reg, &nreg, &sreg);
#else
	reg.x.si = (int) tmpbuffer;
	intdos(&reg, &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);
	}
}
@//E*O*F msd_dir.c//
chmod u=rw,g=r,o=r msd_dir.c
 
echo x - msd_dir.h
sed 's/^@//' > "msd_dir.h" <<'@//E*O*F msd_dir.h//'
/*
 * @(#)msd_dir.h 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 some #define's positions    December 1987
 */

#define	rewinddir(dirp)	seekdir(dirp, 0L)

#define	MAXNAMLEN	12

#ifndef	MAXPATHLEN
# define	MAXPATHLEN	255
#endif	/* MAXPATHLEN */

/* attribute stuff */
#define	A_RONLY		0x01
#define	A_HIDDEN	0x02
#define	A_SYSTEM	0x04
#define	A_LABEL		0x08
#define	A_DIR		0x10
#define	A_ARCHIVE	0x20

struct direct {
	ino_t	d_ino;			/* a bit of a farce */
	int	d_reclen;		/* more farce */
	int	d_namlen;		/* length of d_name */
	char	d_name[MAXNAMLEN + 1];		/* garentee null termination */
	char	d_attribute;
	unsigned long	d_size;
};

struct _dircontents {
	char	*_d_entry;
	char	d_attribute;
	unsigned long	d_size;
	struct _dircontents	*_d_next;
};

typedef struct _dirdesc {
	int		dd_id;	/* uniquely identify each open directory */
	long		dd_loc;	/* where we are in directory entry is this */
	struct _dircontents	*dd_contents;	/* pointer to contents of dir */
	struct _dircontents	*dd_cp;	/* pointer to current position */
} DIR;

extern	DIR		*opendir();
extern	struct direct	*readdir();
extern	void		seekdir();
extern	long		telldir();
extern	void		closedir();
@//E*O*F msd_dir.h//
chmod u=rw,g=r,o=r msd_dir.h
 
echo x - showbug.c
sed 's/^@//' > "showbug.c" <<'@//E*O*F showbug.c//'
/*
 *	To show the bug associated with stat(), it works okay if the drive
 *	letter entered is for the current drive.
 */

#include	<stdio.h>
#include	<dos.h>
#include	<sys/types.h>
#include	<sys/stat.h>
#include	<strings.h>
#include	<ctype.h>
struct	stat		statb;

static	char	*extgetcwd();
#define DOSI_GCDIR	0x47
static	union REGS	reg, nreg;
#if	defined(M_I86LM)
static	struct SREGS	sreg;
#endif
#define	MAXPATHLEN	128

main()
{
	char	*s = ":\\tmp";
	char	ss[MAXPATHLEN+1], dd[MAXPATHLEN+1];
	char	c;

	printf ("Enter drive letter with directory \\tmp : ");
	c = getchar();
	if (isupper(c))
		c += 'a' - 'A';
	ss[0] = c;
	ss[1] = NULL;
	strcat (ss, s);
	chdir(ss);
	extgetcwd (c+1, ss, MAXPATHLEN);
	printf ("Current directory before stat() = %s\n", ss);
	dd[0] = c;
	dd[1] = NULL;
	strcat (dd, ":\\");
	stat(dd, &statb);
	extgetcwd (c+1, dd, MAXPATHLEN);
	printf ("Current directory after stat() = %s\n", dd);
}


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(&reg, &nreg, &sreg);
#else
	reg.x.si = (int) tmpbuffer;
	intdos(&reg, &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);
	}
}
@//E*O*F showbug.c//
chmod u=rw,g=r,o=r showbug.c
 
exit 0