home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 October
/
usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso
/
misc
/
volume1
/
8712
/
du-dos
< prev
next >
Wrap
Text File
|
1990-07-13
|
21KB
|
804 lines
Path: uunet!munnari!basser!john
From: lim@mullian.oz.AU (Lim Ngee Ching)
Newsgroups: comp.sources.misc
Subject: DU for MS DOS (MSC 4.0)
Message-ID: <1149@basser.oz>
Date: 9 Dec 87 15:15:08 GMT
Sender: john@basser.oz
Lines: 793
Approved: john@basser.cs.su.oz.AU
[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(®, &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(®, &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);
}
}
@//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(®, &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);
}
}
@//E*O*F showbug.c//
chmod u=rw,g=r,o=r showbug.c
exit 0