Category : File Managers
Archive   : FREE183.ZIP
Filename : FREE.C
Output of file : FREE.C contained in archive : FREE183.ZIP
FREE - Free Disk Space utility
Version 1.83
by
Howard Kapustein
Howard Kapustein
2 Waterview Road - Apt N-1
West Chester, PA 19380
(215) 692-8412
Program, Source, Documentation and attendant materials
(c) Copyright 1988, 1989, 1990 All rights reserved
See FREE.DOC for documentation.
FREE was compiled with Turbo C (tm) 2.0 on an IBM PC with the
Tiny memory model optimized on Speed and all optimizations
turned on (Register and Jump optimization, and Merge Duplicate
Strings). The object files distributed with FREE were created
with Turbo Assembler (tm) 1.0.
Release notes: 2-2-88 Version 1.0 - Initial release
2-22-88 Version 1.1
- checks for drive ready before checking
free space
- skips drive B: only 1 drive present
(B: is a logical drive, not a physical
drive)
- optional wait after execution can now
wait for a specific key (specify its
decimal scan code, only valid for keys
1-255 (no extended keys i.e. Alt-C, etc.)
- fixed a bug with the strtocomma() function.
If a 4-digit number (i.e. 9216) was passed
to it it would not insert any commas
- I no longer make FREE.COM, for some reason
under DESQview FREE.COM 1.0 still needed
approx 70K, so until I can figure out how
to get FREE to use appropriate only the 11K
or so it needs, I'm not going to bother
playing with COM files
2-29-88 Version 1.2
- recompiled under Turbo C 1.5 (it only took
them 6 weeks instead of the 2-3 it should
have...) I turned on ALL optimizations
(speed, use regs, optimize regs and jumps.)
Previous versions were only compiled with
speed and use registers.
- modified the parsing for more coherent
program flow
- made waitforkey() after having extensive
difficulties getting the -w option to work.
This seems to have fixed the runtime bugs
I was getting after recompiling with TC 1.5
- changed the HEAP and STACK size so it will
run with less memory
- removed some needless comments left over
from old coding
- include a DESQview .DVP file for use with
DESQview.
3-19-88 Version 1.3
- fixed the bug with multiple drives
specified on the command line. It seems
that TC 1.5 DOES NOT let you use the
generic argc and argv as in
main(int argc, char *argv[]) Now Borland
wants you to use their global variables
(extern int argc, etc.). Nice of them not
to mention the fact that the old method
no longer works.
3-23-88 Version 1.31 - Internal release only
- if you try to do a FREE - with no
parms free would check the default drive.
This has been fixed to display the help
(technically, doing FREE - is wrong)
- version 1.2 allowed -w[n] option to check
for extended key codes, but I forgot to
document it. Use this formula to get the
value for the wait option:
extended * 256 + scancode
where extended is 0 if False and 1 if True
and scancode is the scan code returned by
the key (refer to one of Norton's books
or some other reference guide for a list
of IBM extended scan codes.)
4-10-88 Version 1.32 - Internal release only
- I removed a printf() left over from
debugging
- included info about contacting me during
the school year
5-6-88 Version 1.33 - Internal release only
- included % free
5-11-88 Version 1.34 - Internal release only
- fixed -w option, was skipping past
1st digit of keycode ([n])
- also, discovered the problem with the
-w option. Compiled w/full optimization,
the waitkey value was stored in a
register which was destroyed during the
absread(). This is a problem of the
TC library itself. I don't remember
who it was who discovered this flaw, but
if you drop me a line I'll update these
docs. He has notified Borland, so
hopefully the next TC will have this bug
fixed (if not sooner, but I'm not
holding my breath.) Thank you for the
debugging, it gave me many a restless
hour. DO NOT compile FREE with register
variables on, or unknown results may
(and probably will) occur
11-9-88 Version 1.4
- recompiled under TC 2.0 in Small model,
Merge duplicate strings On, Standard
stack frame Off, and all Debug info
left out. Couldn't compile under Tiny
(Cannot generate COM file: segment-
relocatable items present) probably because
of the FP emulator - see below
- added optional message if wait for key
- added -f and -n (search only floppies,
non-floppies). The -x option is equivalent
to -fn
- added -e option (full error display, useful
if you want to see any errors during a -f,
-n, or -x run). This setting is turned on
automatically for single mode (if -f, -n
and -x are not specified)
- minor code tinkering for aesthetics
- DESQview 2.2 .DVP file
- for some reason, FREE running under
DV 2.2 now requires 65K. According to my
estimates, it needs anywhere from 44K to
66K. I think this is related to the Tiny
problem (inability to make .COM file). If
anyone can figure out why, please let me
know. Without the FP emulator, I can't
display a % free, but requiring 65K to get
a free disk space count is ridiculous
2-7-89 Version 1.5
- new option -N, Network compatibility,
skips the absolute disk read before
checking free space. If a free space
request is attempted on drive A: with
no floppy in the drive, DOS bombs out to
the Abort,Ignore,Retry message. Turning on
this option should allow execution on
drives not physically available (networks,
those accessed via device drivers, etc.)
- changed option letter -N to -H to allow
network compatibility. The -H option
will scan non-floppy drives (generally
fixed disks, C: and up.)
- no longer compiled with the FP emulation,
the % free real number is now achieved via
integers. Without the FP emulation FREE can
be compiled with the Tiny memory model.
- added check for DOS environment variable
FREEOPT for options
- changed the Heap size to 4K and the stack
to 1K
- changed the command line parsing to a
separate function, and no longer copy each
*_argv before processing it. The pointer
char *temp replace the older char temp[30]
for several reasons: instead of a strcpy()
we can just do a temp = *_argv, and if an
argument string was longer than 29
characters, we would have problems copying
to temp[30]. Pointers make life simpler
- you can now check multiple drives via
command line switch, and specific drives
via drive letter, in the same run (i.e.
FREE /F B: checks all floppy drives and
drive B:)
- IMPORTANT! FREE is now distributed as a
.COM file! If you are using an earlier
verison with a .EXE extension, make sure
you delete it otherwise DOS may not find
the proper file to run. See above regarding
FP emulation as to why we're back to a
.COM file.
Note: I've enclosed the substitute division
function within a conditional #if...#endif
set so you can recompile easily with the
emulation or 8087 library and not have
unnecessary code. If MATH_FP87 or MATH_EMU
is #defined, the rdiv() function will not
be compiled. Otherwise, no math library
will be linked in, and the final .COM
will only need ~15K to run.
2-8-89 Version 1.51
- if no command line arguments given, only
the current drive would be checked, even
if FREEOPT=X. Fixed
2-17-89 Version 1.6
- new option -t gives disk total size, used
disk space, etc. a list of all disk space
info. To do this, the functions rsub_t(),
rnormalize() and lpow() were created (real
subtraction w/o a math library), as well
as their attendant global variables.
WARNING: these substitute floating point math
functions have minimal error checking.
Given the scope of this program, they
are adequate, but for a more complete
implementation of these (and other)
functions, try my Turbo C library TCHK
(currently distributed as TCHK20.ARC).
NOTE: as per the rdiv() function, these
substitute fp functions/variables are
enclosed within #if...#endif to avoid
compilation if a fp library is used.
FURTHER: the compiler may generate 2 warnings
(structure passed by value) if your
warning settings are just right. Don't
worry about it, those are the structures
passed to rsub(). It's valid C (or at
least ANSI C) but potentially erroneous
to pass structures instead of pointers to
structures, so Turbo C warns you.
- changed the code to check only as many
drives as DOS recognizes. Under DOS 1.x,
2.x and 3.x, there can be as many as 16,
63 or 26 drives available, respectively.
Previously I just checked drives 1-26
(A:-Z:), which was an erroneous assumption
on my part. Thanks to John Craven for
mentioning Int 21h Function 0Eh (setdisk()
in Turbo C) as the proper method for
detecting the number of logical drives.
- changed the code to skip B: if it does
not physically exist. This should clear
up the problem Michael Thompson has been
having with the scan drives (/x or /f)
option.
3-18-89 Version 1.61
- fixed bug, FREE would not recognize a B:
drive. The biosequip() & 0xB0 ... line was
faulty, should be 0xC0 (wrong bit). This
produced random (and usually wrong) results.
Thanks to Scott Mason for informing me of
the error and his and Michael Thompson's
efforts in finding the solution
- removed #define isBlogical() (not used in
FREE 1.6 but never removed)
6-24-89 Version 1.70
- changed numbers in table output (-t) to be
comma separated for easier reading. I left
the old printf() command commented out in
case you prefer the raw numeric output. If
you uncomment the old code, don't forget
there are 2 places to do it (because of
the #ifdef...MATH...#else...#endif
simulated math)
- Renamed flag.disktotal variable to
flag.disktable
- new option -s to give summary of free
space (totals, synopsis, etc.). Added
variables total_... to options structure
to support this
- changed exit code numbering, now sets
errorlevel to 0, 1, 2 or 3 (previously used
3, 9, 11, and 67, not necessarily in that
order)
- during parsing of command line for drive
letters, the character '*' is now
interpreted as the current drive (i.e.
FREE A * will search drive A: and the
current drive)
- now distributed with files FREE.MSG,
FREE.GEN, FREE.PRO and FREE.ZDR which
are a brief overview of Free, a file
description for use by GEnie, and file
descriptions for use by ProDoor and ZDoor
1-14-90 Version 1.80
- New: Supports >32M partitions. DOS 3.31 w/partitions > 32M
and 4.0+ make a slightly different call to do an absolute
read than previous versions of DOS (TC's absread() is for
pre-4.0 disks with partitions < 32M). I've written my own
absolute disk read functions that can work on any drive
(irrelevant of partition size) in Assembler, as well as the
drive identification functions. Since not everyone has an
assembler, but want to be able to compile FREE (whether for
personal customization or simply educational benefit, I've
provided conditional compiling of the C functions.
Note: I've enclosed the assembler substitute functions
within a conditional #if... #endif set so you can
recompile easily if you don't have an assembler. If
ASSEMBLY_REQUIRED is #defined, FREE can be recompiled
with just Turbo C. The distributed .COM file has been
created with the assembly code linked in, providing
full functionality.
- New: Detects the drive type. See Drive Type Identification for
more details. Seems ot have a problem with the Multisoft's
RAM disk (part of their Super PC-Kwik Power Pack). See
Known Incompatibilities for more details.
- New: added -I[d] parameter to ignore drives. New structure
element flag.ignoredrives is a bit field, where bits 0-25
represent drives A: - Z:. If a bit is on, the drive is
ignored. Just -I, no drive letter, resets the ignore list.
- New: added exit code 4 when a bad drive letter is specified
for the -I[d] parameter
- NewCode: added structure element flag.nothingchecked, so if
all drives to be searched are ignored, the message "No
drives checked" is displayed
- Fixed: skips whitespace in FREEOPT (e.g. "SET FREEOPT=T X"
works now, equivalent to FREE /t /x)
- ChangedCode: changed the definition of boolean from a
#define to a typedef
- Changed: My new phone number is listed
- Changed: revamped documentation
- Changed: FREE is now compiled with a make file instead of
batch files. Distribution includes the make file as well
the TURBOC.CFG and FREE.RSP files used to create FREE
- New: FREE now requires registration by commercial
institutions. The registration is nominal, and the
advantage is source code to the Assembly modules. This
allows me to distribute full source code and still honor
legal and ethical obligations.
1-20-90 Version 1.81 - Internal release only
- Fixed: Network drives were identified as Error instead of
Network, due to a flaw in the GetDriveType() function.
GetDriveType() now returns different values, and the C return
type is now an int instead of a char. Due to necessary
changes, GetDriveType() requires DOS 3.1+. If used under
earlier versions of DOS, an error is returned.
BUG NOTE 1: identifying LANtastic nodes as IFS, not Network.
Auto-detect network drives wasn't working, but I think it's
related. Will be corrected before public release.
BUG NOTE 2: Fixed(?): IFS drives are only recognized under DOS
4.x+. If the List of Lists says the device is IFS but not DOS
4.x (3.x or earlier), it's assumed to be a network drive. For
some reason, under 3Com's 3+ network, bits 14 and 15 were on
for networked drives (14=local, 15=network, both=IFS,
neither=error) under DOS 3.3. I assume this was the same
problem under LANtastic. This is the real fix. See notes for
version 1.82 for more details.
- New: added -a option, Auto-Detect Network. If FREE determines
a drive is a network drive, it will NOT perform an absolute
disk read to check if the drive is ready. This options is on
by default. To disable auto-detection, include -A- on the
command line.
- Changed: -n renamed from "network compatibility" to "force
network compatiblity". Renamed network variable in struct
options to forcenetworkcompat.
- Changed: some of the code has been prettied up. Function names
are now mixed case, etc.
- Fixed: forgot to update Help screen information between
completion of code changes and completion of documentation.
- Fixed(?): Rick Nakroshis notified me of a problem with Disk
Manager and large partitions. I have changed the assembler
code that determines whether to use the new absolute disk read
(for large, >32M, partitions) or the old call. Previously if
DOS was 3.3 or earlier, the old call was always used, without
bothering to check the device attribute word. However, DOS 3.3
and earlier can have large partitions via third party software
(e.g. Disk Manager). Now the DAW is always checked, and the
answer determines which call to use.
NOTE: I do not have access to a large partition drive, DOS 3.31,
4.x or any partitioning software. If you have access to
any of the above equipment, please test this program
and let me know if there are any problems.
1-25-90 Version 1.82
- Fixed: Network drives were not being properly detected, due to
a flaw in the code. After some changes were made, network
drives were always identified as IFS (tested under LANtastic
and 3COM's 3+). Performing further testing (thank you Turbo
Debugger) showed that network drives under DOS 3.3 were
identified as IFS drives. This has been fixed, I hope, to
distinguish between network drives and IFS drives.
Why? Try this one on for size: In the List of Lists, a word
signifies what 'category' the drive falls under, SUBST, JOIN,
Network, Local or IFS. Bit 15 is the Network bit and bit 14 is
the local bit. If neither bit is on, it is an error. If one
bit is on, it is that type. According to old information, if
both bits are on, it is an error. Poring through new
information (the Unsenet INTerrupt list version 90.1) shows
that if both bits are on, the drive is IFS. IFS drives were
added to DOS 4.0, and are not available in earlier versions.
Yet, for some reason, networks under DOS 3.3 set both bits on.
Since IFS drives require DOS 4.0+, if a drive is identified
as IFS and the DOS version is earlier than 4.0, it is
considered a network drive. Testing under networks shows FREE
to work fine, and since I do not know of any IFS' under DOS
4.x, I have not had a chance to test FREE's detection of IFS
drives. If you experience problems with incorrectly identified
network or IFS drives, please let me know and I will look into
it, though I doubt there will be any problems.
- New: added note to Incompatiblities section in documentation
regarding VDISK (VDISK drives are not identified, Error),
running FREE in the OS/2 (In)Compatibility Box (all drive type
identifications should fail, Error), and running FREE when
ASSIGN is installed.
2-03-90 Version 1.83
- Fixed: my absolute disk read function for large partitions
(>32M) had a bug, resulting in unpredictable results (i.e.
YASC - Yet Another System Crash, as we all know and love under
DOS) when performing absolute disk reads on large partitions.
FYI, the new INT 25h (absolute disk read for large
partitions), has the same "bug" as the old INT 25h, leaving
the flags on the stack upon return.
Thanks to Charles Kleis, Tadas Osmolskis, John Redford, Rick
Nakroshis, Robert Blacher and several others on Computer
Connections for informing me about problems using FREE with
large partitions, and to Dirk Lesko for pointing out the INT
25h "bug" (and Ray Duncan for his excellent reference Advanced
MS-DOS, 2nd Edition).
Thanks to the graciousness of the sysops at The BOSS, Computer
Connections, The Invention Factory and The Tool Shop, I have
easier access to their BBSes. The latest versions of my software
can be found there. I can quite often be found on Computer
Connections, but only infrequently at the others.
If there are some features you would like to see implemented, or
you have any questions or comments, please contact me. I can be
reached at the following:
Howard S. Kapustein
2 Waterview Road - Apt N-1
West Chester, PA 19380
Phone: (215) 692-8412
*** If I have to call you I'll have to call collect ***
or at the following electronic services/bulletin boards:
The Boss dBBS (201) 568-7293 *** Support BBS ***
The Invention Factory (212) 431-1254 Distribution BBS
Computer Connection (202) 547-2008 *** Support BBS ***
The Tool Shop (602) 279-2673 Distribution BBS
GEnie H. Kapustein
Also: IBM RT, SET 4, TOPIC # 25
Borland RT, SET 2, TOPIC # 55
These are the places I can be reached by modem. FREE (and all
other software I release) is supported through The BOSS and
Computer Connections.
- End of Comment -
****************************************************************************/
#include
#include
#include
#include
#include
#include
#include
struct rdiv_t {
long rint; /* real - integer */
long rfrac; /* real - fraction */
int precision; /* precision of fraction (# of decimal places) */
};
/* Drive IDs */
#define DRIVE_ID_ERROR 0xFFFF
/* Major categories */
#define DRIVE_ID_SUBST 0x1000
#define DRIVE_ID_JOIN 0x2000
#define DRIVE_ID_LOCAL 0x4000
#define DRIVE_ID_NETWORK 0x8000
#define DRIVE_ID_IFS 0xC000
/* Specific device types (currently only identified for local drives) */
#define DRIVE_ID_360K (DRIVE_ID_LOCAL|0x0000)
#define DRIVE_ID_1_2M (DRIVE_ID_LOCAL|0x0001)
#define DRIVE_ID_720K (DRIVE_ID_LOCAL|0x0002)
#define DRIVE_ID_8SD_FLOPPY (DRIVE_ID_LOCAL|0x0003)
#define DRIVE_ID_8DD_FLOPPY (DRIVE_ID_LOCAL|0x0004)
#define DRIVE_ID_HARD_DISK (DRIVE_ID_LOCAL|0x0005)
#define DRIVE_ID_TAPE_DRIVE (DRIVE_ID_LOCAL|0x0006)
#define DRIVE_ID_1_44M (DRIVE_ID_LOCAL|0x0007)
#define DRIVE_ID_OTHER (DRIVE_ID_LOCAL|0x0008)
/* Drive ID flags */
#define DRIVE_ID_ASSIGN 0x0800
#define DRIVE_ID_UNKNOWN 0x0400
#define DRIVE_ID_FLOPPY 0x0200
#define DRIVE_ID_SPECIAL 0x0100
/* Special drive types */
#define DRIVE_ID_BERNOULLI (DRIVE_ID_HARD_DISK|DRIVE_ID_SPECIAL)
typedef unsigned char boolean;
#define TRUE 1
#define FALSE 0
#define BUFF_LEN 512 /* buffer length for disk read */
#define FLOPPY 0x10
#define FIXED 0x20
#define ALLDRIVES (FIXED|FLOPPY)
#define IGNORE_RESET 0lu /* Default/Reset value for Ignore drives flag */
#define MAXRPRECISION 8 /* maximum real precision (decimal places) */
#define _HEADING_ "FREE Free disk space Version 1.83 2-03-90 FREE/?\n"
#define _HEADINGCOPY_ "Copyright (c) 1988,1989,1990 Howard Kapustein. All Rights Reserved for help\n\n"
struct Options {
char ScanDrives;
unsigned long IgnoreDrives; /* Bit field: bits 0-25 = drives A-Z, bits 26-31 = reserved */
boolean AutoDetectNetwork;
boolean ForceNetworkCompat;
boolean DiskTable;
boolean ErrorMsg;
boolean Wait;
boolean WaitMsg;
int WaitKey;
boolean SummaryTotals;
char Total_Drives; /* Total # drives checked */
long Total_Size; /* Total size checked */
long Total_Used; /* Total used disk space */
long Total_Free; /* Total free disk space */
boolean NothingChecked; /* Flag if no drives have been checked */
};
/* Global variables */
struct Options Flag;
#if !defined(MATH_FP87) && !defined(MATH_EMU) /* using no math library */
int _r_minpre, _r_maxpre;
boolean _r_maximum = FALSE; /* should rnormalize() try to minimize or maximize the precision */
#endif
/* function prototypes */
void main(); /* prevents compiler warning */
void check(int drive); /* actual free space check and output */
char *StrToComma(char *source); /* convert a string to xx,xxx,xxx format */
void Help(void); /* help */
void WaitForKey(int keycode, boolean msg); /* wait for keypress when done */
void ParseCommandLine(char *cmdline); /* parse a command line */
void Bye(int errcode); /* generalized quit routine */
struct rdiv_t RDiv(long int numer, long int denom, int precision); /* real division without FP emulator library */
struct rdiv_t RSub(struct rdiv_t val, struct rdiv_t sval, int precision); /* real subtraction without FP emulator library */
void RNormalize(struct rdiv_t *r1, struct rdiv_t *r2); /* normalize emulated fp values (fix the decimal places) */
long LPow(long base, int exponent); /* raise a base to an exponent */
int GetDriveType(int drive); /* gets drive type (Hard disk, floppy, etc.) */
int AbsoluteRead(int drive, int nsects, long lsect, void *buffer); /* reads absolute disk sector, partition size irrelevant */
void main()
{
extern int _argc;
extern char **_argv;
extern unsigned _heaplen, _stklen;
char *temp;
boolean Blogical;
int i, floppies, totaldrives;
_heaplen = 4096u; /* heap = 4K */
_stklen = 1024u; /* stack = 1K */
_argc--;
_argv++;
Flag.ScanDrives = FALSE; /* initialize settings */
Flag.IgnoreDrives = IGNORE_RESET;
Flag.AutoDetectNetwork = TRUE;
Flag.ForceNetworkCompat = FALSE;
Flag.DiskTable = FALSE;
Flag.ErrorMsg = FALSE;
Flag.Wait = FALSE;
Flag.WaitMsg = TRUE;
Flag.WaitKey = 0;
Flag.SummaryTotals = FALSE;
Flag.Total_Drives = 0l;
Flag.Total_Size = 0l;
Flag.Total_Used = 0l;
Flag.Total_Free = 0l;
Flag.NothingChecked = TRUE;
if ((temp = getenv("FREEOPT")) != NULL)
ParseCommandLine(temp);
if (_argc > 0) /* check for options */
for (temp = *_argv; (temp[0] == '-') || (temp[0] == '/'); temp = *_argv) {
if (temp[1] == '\0')
Help();
ParseCommandLine(temp+1);
_argc--;
_argv++;
if (_argc == 0)
break;
}
if (Flag.DiskTable)
fputs("\n" _HEADING_ _HEADINGCOPY_
"Drive Size Used Free % Used % Free Type\n"
"----- ------------- ------------- ------------- ------ ------ ----\n",stdout);
if (Flag.ScanDrives) { /* check multiple drives? */
floppies = ((biosequip() & 0xC0) >> 6) + 1;
Blogical = (floppies > 1) ? FALSE : TRUE;
if (Flag.ScanDrives & FLOPPY) /* check floppy drives? */
for (i=1; i<=floppies; i++) /* check floppies */
if (!(Blogical && i==2))
check(i);
if (Flag.ScanDrives & FIXED) { /* check non-floppies? */
totaldrives = setdisk(getdisk());
i = ++floppies;
if (Blogical)
i++;
for (; i<=totaldrives; i++) /* check hard drives */
check(i);
}
}
if ((_argc == 0) && !Flag.ScanDrives) { /* ok, just parms (no drive specified) */
Flag.ErrorMsg = TRUE; /* force error message display for non-multiple mode */
check(0);
if (Flag.Wait)
WaitForKey(Flag.WaitKey,Flag.WaitMsg);
Bye(0);
}
Flag.ErrorMsg = TRUE; /* force error message display for non-multiple mode */
for (; _argc>0; _argc--, _argv++) {
strcpy(temp,*_argv);
if (temp[0] == '*') /* '*' interpreted as current drive */
check(0);
else if (!isalpha(temp[0]=toupper(temp[0])))
printf("Invalid drive: %c\n",temp[0]);
else
check(temp[0]-64);
}
if (Flag.Wait)
WaitForKey(Flag.WaitKey,Flag.WaitMsg);
Bye(0);
}
void check(int drive)
{
extern struct Options Flag;
static char *DriveTypeString[] = { "Error",
"Subst","Join","Network","Local","IFS",
"360K","1.2M","720K","8\" SD","8\" DD",
"Hard disk","Tape drive","1.44M",
"Other","Bernoulli","Floppy","?" };
struct dfree df;
char size[30], *DriveIDString;
unsigned char buffer[BUFF_LEN];
long avail, total;
unsigned DriveID;
#if defined(MATH_FP87) || defined(MATH_EMU) /* using 80x87 or Emulation math library */
double percent;
#else /* using No math library */
struct rdiv_t percent, percentused, percent100;
percent100.rint = 100l;
percent100.rfrac = 0l;
percent100.precision = MAXRPRECISION;
#endif
if (drive==0)
drive = getdisk() + 1;
if (Flag.IgnoreDrives & (1lu << (drive-1))) /* Ignore this drive ? */
return;
else
Flag.NothingChecked = FALSE; /* Prevents FREE from displaying nothing when ignoring all drives */
DriveID = GetDriveType(drive-1); /* Determine drive type */
if (((!Flag.AutoDetectNetwork) || /* Do not auto detect network drives */
(Flag.AutoDetectNetwork && (DriveID != DRIVE_ID_NETWORK))) && /* Network not auto-detected */
!Flag.ForceNetworkCompat && /* Network compatibility not forced */
(AbsoluteRead(drive-1,1,0,buffer) == -1)) { /* Disk not ready */
if (Flag.ErrorMsg)
printf("Drive %c: is not available (drive not ready)\n",drive+64);
} else {
getdfree(drive,&df);
if (df.df_sclus == (unsigned)(-1)) { /* error */
if (Flag.ErrorMsg)
printf("Drive %c: is not available (can't get free space)\n",drive+64);
} else {
avail = (long)df.df_avail * df.df_bsec * df.df_sclus;
total = (long)df.df_total * df.df_bsec * df.df_sclus;
#if defined(MATH_FP87) || defined(MATH_EMU) /* using 80x87 or Emulation math library */
percent = (avail * 100.0) / ((long)df.df_total * df.df_bsec * df.df_sclus);
if (Flag.DiskTable) {
/* printf(" %c: %10ld %10ld %10ld %6.2lf %6.2lf\n",drive+64,total,total-avail,avail,100.0-percent,percent);*/
ltoa(total,size,10);
printf(" %c: %13s",drive+64,StrToComma(size));
ltoa(total-avail,size,10);
printf(" %13s",StrToComma(size));
ltoa(avail,size,10);
if (DriveID == DRIVE_ID_ERROR) /* Error ? */
DriveIDString = DriveTypeString[0];
else if (DriveID & 0x00FF) /* Got the drive's specific type */
switch (DriveID) {
case DRIVE_ID_IFS: { DriveIDString = DriveTypeString[5]; break; }
case DRIVE_ID_360K: { DriveIDString = DriveTypeString[6]; break; }
case DRIVE_ID_1_2M: { DriveIDString = DriveTypeString[7]; break; }
case DRIVE_ID_720K: { DriveIDString = DriveTypeString[8]; break; }
case DRIVE_ID_8SD_FLOPPY: { DriveIDString = DriveTypeString[9]; break; }
case DRIVE_ID_8DD_FLOPPY: { DriveIDString = DriveTypeString[10]; break; }
case DRIVE_ID_HARD_DISK: { DriveIDString = DriveTypeString[11]; break; }
case DRIVE_ID_TAPE_DRIVE: { DriveIDString = DriveTypeString[12]; break; }
case DRIVE_ID_1_44M: { DriveIDString = DriveTypeString[13]; break; }
case DRIVE_ID_OTHER: { DriveIDString = DriveTypeString[14]; break; }
case DRIVE_ID_BERNOULLI:{ DriveIDString = DriveTypeString[15]; break; }
case DRIVE_ID_FLOPPY: { DriveIDString = DriveTypeString[16]; break; }
case DRIVE_ID_UNKNOWN:
default: { DriveIDString = DriveTypeString[17]; break; }
}
} else
switch (DriveID) { /* Only know the device's category */
case DRIVE_ID_SUBST: { DriveIDString = DriveTypeString[1]; break; }
case DRIVE_ID_JOIN: { DriveIDString = DriveTypeString[2]; break; }
case DRIVE_ID_NETWORK: { DriveIDString = DriveTypeString[3]; break; }
case DRIVE_ID_LOCAL: { DriveIDString = DriveTypeString[4]; break; }
case DRIVE_ID_UNKNOWN:
default: { DriveIDString = DriveTypeString[17]; break; }
}
printf(" %13s %6.2lf %6.2lf %s\n",StrToComma(size),100.0-percent,percent,DriveIDString);
} else {
ltoa(avail,size,10);
printf("Drive %c: has %s bytes free (%.2lf %%)\n",drive+64,StrToComma(size),percent);
}
#else /* using No math library */
percent = RDiv(avail,total,4);
percent.rint = percent.rint * 100l + percent.rfrac / 100l;
percent.rfrac %= 100l;
percent.precision -= 2;
percentused = RSub(percent100,percent,2);
if (Flag.DiskTable) {
/* printf(" %c: %10ld %10ld %10ld %3ld.%02ld %3ld.%02ld\n",drive+64,total,total-avail,avail,
percentused.rint,percentused.rfrac,percent.rint,percent.rfrac);*/
ltoa(total,size,10);
printf(" %c: %13s",drive+64,StrToComma(size));
ltoa(total-avail,size,10);
printf(" %13s",StrToComma(size));
ltoa(avail,size,10);
if (DriveID == DRIVE_ID_ERROR) /* Error ? */
DriveIDString = DriveTypeString[0];
else if (DriveID & 0x00FF) /* Got the drive's specific type */
switch (DriveID) {
case DRIVE_ID_IFS: { DriveIDString = DriveTypeString[5]; break; }
case DRIVE_ID_360K: { DriveIDString = DriveTypeString[6]; break; }
case DRIVE_ID_1_2M: { DriveIDString = DriveTypeString[7]; break; }
case DRIVE_ID_720K: { DriveIDString = DriveTypeString[8]; break; }
case DRIVE_ID_8SD_FLOPPY: { DriveIDString = DriveTypeString[9]; break; }
case DRIVE_ID_8DD_FLOPPY: { DriveIDString = DriveTypeString[10]; break; }
case DRIVE_ID_HARD_DISK: { DriveIDString = DriveTypeString[11]; break; }
case DRIVE_ID_TAPE_DRIVE: { DriveIDString = DriveTypeString[12]; break; }
case DRIVE_ID_1_44M: { DriveIDString = DriveTypeString[13]; break; }
case DRIVE_ID_OTHER: { DriveIDString = DriveTypeString[14]; break; }
case DRIVE_ID_BERNOULLI:{ DriveIDString = DriveTypeString[15]; break; }
case DRIVE_ID_FLOPPY: { DriveIDString = DriveTypeString[16]; break; }
case DRIVE_ID_UNKNOWN:
default: { DriveIDString = DriveTypeString[17]; break; }
}
else
switch (DriveID) { /* Only know the device's category */
case DRIVE_ID_SUBST: { DriveIDString = DriveTypeString[1]; break; }
case DRIVE_ID_JOIN: { DriveIDString = DriveTypeString[2]; break; }
case DRIVE_ID_NETWORK: { DriveIDString = DriveTypeString[3]; break; }
case DRIVE_ID_LOCAL: { DriveIDString = DriveTypeString[4]; break; }
case DRIVE_ID_UNKNOWN:
default: { DriveIDString = DriveTypeString[17]; break; }
}
printf(" %13s %3ld.%02ld %3ld.%02ld %s\n",StrToComma(size),
percentused.rint,percentused.rfrac,percent.rint,percent.rfrac,DriveIDString);
} else {
ltoa(avail,size,10);
printf("Drive %c: has %s bytes free (%ld.%02ld %%)\n",drive+64,StrToComma(size),percent.rint,percent.rfrac);
}
#endif
if (Flag.SummaryTotals) {
Flag.Total_Drives++;
Flag.Total_Size += total;
Flag.Total_Used += (total - avail);
Flag.Total_Free += avail;
}
}
}
}
char *StrToComma(char *source) /* convert a string to xx,xxx,xxx format */
{
char temp[80],*t,*s;
int i,len;
len = strlen(source) * 4/3 + 1;
strcpy(temp,"");
if ((len = strlen(source)) <= 3)
strcpy(temp,source);
else
for (i=len, t=temp, s=source; i>0; i--) {
if ((i%3 == 0) && (i != len))
*t++ = ',';
*t++ = *s++;
}
*t = '\0';
strcpy(source,temp);
return(source);
}
void Help(void)
{
extern struct Options Flag;
fputs(_HEADING_ _HEADINGCOPY_,stdout);
fputs("Usage: FREE [options] [drive] [drive] [drive] ...\n",stdout);
fputs("Options are: (* = default; -x- = turn swtich x off; -x* = toggle switch x)\n",stdout);
fputs(" -a* = auto-detect network drives -f = check all floppy drives\n",stdout);
fputs(" -e = display all error messages -h = check all non-floppy drives\n",stdout);
fputs(" -n = force network compatibility -x = check all drives\n",stdout);
fputs(" -s = summary totals -i[d] = ignore/skip drive d:\n",stdout);
fputs(" -t = table form output -w[0][n] = wait for keypress when done\n\n",stdout);
fputs("Specifying * for a drive letter will search the current drive (i.e. FREE *)\n\n",stdout);
fputs("I am but a poor starving working class programmer, so all donations will be\n",stdout);
fputs("appreciated. Registration ($7) is REQUIRED of commercial users. See the\n",stdout);
fputs("documentation for licensing information. I can be reached at:\n\n",stdout);
fputs("VOICE: Howard Kapustein MODEM: Computer Connections (202) 547-2008\n",stdout);
fputs(" 2 Waterview Road - Apt N-1 The BOSS dBBS (201) 568-7293\n",stdout);
fputs(" West Chester, PA 13980 GEnie: H.Kapustein\n",stdout);
fputs(" Phone: (215) 692-8412 \n\n",stdout);
fputs("I also write TCHK (TCHK21*.ZIP), Subtree Find (SF332.ZIP) and other shareware.\n",stdout);
Flag.SummaryTotals = FALSE;
Flag.NothingChecked = FALSE;
Bye(1);
}
void WaitForKey(int keycode, boolean msg) /* wait for keypress when done */
{
int key;
if (msg) {
if (keycode == 0)
fputs("Press any key to end...",stdout);
else
printf("Press key #%d to end...",keycode);
}
if (keycode == 0)
bioskey(0);
else
do {
key = bioskey(0);
if (key & 0xFF)
key = key & 0xFF;
else
key = ((key & 0xFF00) >> 8) + 256;
} while (key != keycode);
}
void ParseCommandLine(char *cmdline)
{
extern struct Options Flag;
int i;
for (i=0; cmdline[i]!='\0'; i++)
switch (toupper(cmdline[i])) {
case '/': { if (cmdline[++i] == '\0') {
Flag.SummaryTotals = FALSE;
Flag.NothingChecked = FALSE;
Bye(3);
}
break; }
case 'A': { switch (cmdline[i+1]) {
case '-': { Flag.AutoDetectNetwork = FALSE;
i++;
break; }
case '*': { Flag.AutoDetectNetwork ^= TRUE;
i++;
break; }
case '+': i++;
default: Flag.AutoDetectNetwork = TRUE; break;
}
break; }
case 'N': { switch (cmdline[i+1]) {
case '-': { Flag.ForceNetworkCompat = FALSE;
i++;
break; }
case '*': { Flag.ForceNetworkCompat ^= TRUE;
i++;
break; }
case '+': i++;
default: Flag.ForceNetworkCompat = TRUE; break;
}
break; }
case 'E': { switch (cmdline[i+1]) {
case '-': { Flag.ErrorMsg = FALSE;
i++;
break; }
case '*': { Flag.ErrorMsg ^= TRUE;
i++;
break; }
case '+': i++;
default: Flag.ErrorMsg = TRUE; break;
}
break; }
case 'F': { switch (cmdline[i+1]) {
case '-': { Flag.ScanDrives &= ~FLOPPY;
i++;
break; }
case '*': { Flag.ScanDrives ^= FLOPPY;
i++;
break; }
case '+': i++;
default: Flag.ScanDrives |= FLOPPY; break;
}
break; }
case 'H': { switch (cmdline[i+1]) {
case '-': { Flag.ScanDrives &= ~FIXED;
i++;
break; }
case '*': { Flag.ScanDrives ^= FIXED;
i++;
break; }
case '+': i++;
default: Flag.ScanDrives |= FIXED; break;
}
break; }
case 'I': { if (isalpha(cmdline[i+1])) { /* Ignore this drive */
Flag.IgnoreDrives |= 1lu << (toupper(cmdline[i+1])-'A');
i++;
} else if (cmdline[i+1] == '\0') /* reset Ignore setting */
Flag.IgnoreDrives = IGNORE_RESET;
else {
printf(_HEADING_ _HEADINGCOPY_
"Bad drive letter for -I parameter: %c\n",cmdline[i+1]);
Flag.SummaryTotals = FALSE;
Flag.NothingChecked = FALSE;
Bye(4);
}
break; }
case 'S': { switch (cmdline[i+1]) {
case '-': { Flag.SummaryTotals = FALSE;
i++;
break; }
case '*': { Flag.SummaryTotals ^= TRUE;
i++;
break; }
case '+': i++;
default: Flag.SummaryTotals = TRUE; break;
}
break; }
case 'T': { switch (cmdline[i+1]) {
case '-': { Flag.DiskTable = FALSE;
i++;
break; }
case '*': { Flag.DiskTable ^= TRUE;
i++;
break; }
case '+': i++;
default: Flag.DiskTable = TRUE; break;
}
break; }
case 'X': { switch (cmdline[i+1]) {
case '-': { Flag.ScanDrives &= ~ALLDRIVES;
i++;
break; }
case '*': { Flag.ScanDrives ^= ALLDRIVES;
i++;
break; }
case '+': i++;
default: Flag.ScanDrives |= ALLDRIVES; break;
}
break; }
case 'W': { if (isdigit(cmdline[i+1])) {
Flag.WaitMsg = (cmdline[++i] == '0') ? FALSE : TRUE;
Flag.WaitKey = atoi(cmdline+i);
for (; isdigit(cmdline[i+1]); i++);
} else
Flag.WaitKey = 0;
switch (cmdline[i+1]) {
case '-': { Flag.Wait = FALSE;
i++;
break; }
case '*': { Flag.Wait ^= TRUE;
i++;
break; }
case '+': i++;
default: Flag.Wait = TRUE; break;
}
break; }
case '?': Help();
case ' ': { i++; /* skips whitespace in environment variable (FREEOPT) */
break; }
default : { printf(_HEADING_ _HEADINGCOPY_
"Unknown parameter: %c\n",cmdline[i]);
Flag.SummaryTotals = FALSE;
Flag.NothingChecked = FALSE;
Bye(2); }
}
}
void Bye(int errcode) /* generalized quit routine */
{
extern struct Options Flag;
char size[30];
if (Flag.NothingChecked)
fputs("No drives checked\n",stdout);
if (Flag.SummaryTotals) {
if (Flag.DiskTable) {
ltoa(Flag.Total_Size,size,10);
printf("----- ------------- ------------- -------------\n"
"%3d %13s",Flag.Total_Drives,StrToComma(size));
ltoa(Flag.Total_Used,size,10);
printf(" %13s",StrToComma(size));
ltoa(Flag.Total_Free,size,10);
printf(" %13s\n",StrToComma(size));
} else {
ltoa(Flag.Total_Free,size,10);
printf("-----\n"
"%d %s %s bytes free\n",Flag.Total_Drives,(Flag.Total_Drives==1l)?"drive has":"drives have",StrToComma(size));
}
}
/* switch (errcode) {
0: normal execution, no errors
1: help
2: unknown parameter
3: missing parameter
4: bad drive letter for -I parameter
}
*/
exit(errcode);
}
#if !defined(MATH_FP87) && !defined(MATH_EMU) /* using no math library */
struct rdiv_t RDiv(long int numer, long int denom, int precision) /* real division without FP emulator library */
{
struct rdiv_t retval;
long i;
if (denom == 0l) {
retval.rint = -1;
retval.rfrac = -1;
return(retval);
}
retval.precision = precision; /* store precision of fraction */
retval.rint = numer / denom;
denom -= numer * retval.rint;
retval.rfrac = 0l;
if (denom == 0l)
return(retval);
for (; precision >= 0; precision--) {
numer *= 10l;
i = numer / denom;
if (precision != 0)
retval.rfrac = retval.rfrac * 10l + i;
else
if (numer / denom >= 5l) /* round off last decimal place */
retval.rfrac++;
numer = numer - i * denom;
}
return(retval);
}
struct rdiv_t RSub(struct rdiv_t val, struct rdiv_t sval, int precision) /* real subtraction without FP emulator library */
{
extern int _r_minpre;
struct rdiv_t retval;
int i;
RNormalize(&val,&sval);
retval.rint = val.rint - sval.rint;
if (val.rfrac < sval.rfrac) {
retval.rint--; /* carry the one... */
val.rfrac += LPow(10l,val.precision);
}
retval.rfrac = val.rfrac - sval.rfrac;
retval.precision = precision;
i = precision - _r_minpre;
if (i < 0)
for (; i<0; i++)
retval.rfrac /= 10l;
else if (i > 0)
for (; i>0; i--)
retval.rfrac *= 10l;
return(retval);
}
void RNormalize(struct rdiv_t *r1, struct rdiv_t *r2) /* normalize emulated fp values (fix the decimal places) */
{
extern int _r_minpre, _r_maxpre;
extern boolean _r_maximum;
struct rdiv_t *r;
int i;
_r_minpre = min(r1->precision,r2->precision);
_r_maxpre = max(r1->precision,r2->precision);
i = r1->precision - r2->precision;
if (i == 0)
return;
if (i < 0) {
i = -i;
r = (_r_maximum) ? r1 : r2;
} else
r = (_r_maximum) ? r2 : r1;
if (_r_maximum) /* adjust to largest amount of precision (extend) */
for (; i>0; i--) {
r->rfrac *= 10l;
r->precision++;
}
else /* adjust to smallest amount of precision (truncate) */
for (; i>0; i--) {
r->rfrac /= 10l;
r->precision--;
}
}
long LPow(long base, int exponent) /* raise a base to an exponent */
{
long retval;
for(retval = 1l; exponent > 0; exponent--)
retval *= base;
return(retval);
}
#endif
#if !defined(ASSEMBLY_REQUIRED)
int GetDriveType(int drive) /* gets drive type (Hard disk, floppy, etc.) */
{
return('?');
}
int AbsoluteRead(int drive, int nsects, long lsect, void *buffer) /* reads absolute disk sector, partition size irrelevant */
{
extern unsigned char _osmjor, _osminor;
if ((_osmajor > 3) ||
((_osmajor == 3) && (_osminor > 30))) { /* DOS 3.31+ needs some Assembly code. See docs */
printf("Cannot perform absolute disk reads under DOS 3.31 or later.\n"
"Restart with /N parameter to access drive %c (ignore next line).\n",drive+'A');
return(-1);
}
return( absread(drive,nsects,(int)lsect,buffer) );
}
#endif
Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!
This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.
But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/