home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Power Programming
/
powerprogramming1994.iso
/
progtool
/
dirutl
/
di11.arc
/
DIPRINT.C
< prev
next >
Wrap
Text File
|
1987-11-29
|
21KB
|
516 lines
#include <stdio.h>
#include <dir.h>
#include <dos.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <io.h>
#include "di.h"
long FreeSpace(int Drive);
long SpaceUsed(Entry *Entries,int Num,int Drive);
char *Label(int Drive);
char *AddCommasL(long N);
char *FileDate(int PDate);
char *FileTime(int PTime);
void PrintFull(Entry *Entries,int Num,char *Dir);
void PrintLong(Entry *Entries,int Num);
void PrintShort(Entry *Entries,int Num,int LineEnts);
void FiltAttribs(int *OldAtts,int *NewAtts,int Att);
AUSize(int Drive);
MatchingAtts(int Attribs[],char Attrib);
void PrintHeader(NameNode *Files,int PrintFree)
/* Prints the volume label and, if PrintFree is true, the free space for all
drives specified in the filespecs in Files, including the default drive if
indicated by any filespec. */
{
int Drives[26], /* One entry for each drive, indicating whether it should be
printed */
Drive, /* Specific or default drive specified in current filespec */
CurDrive, /* Default drive */
I;
char *Volume; /* Volume label of a drive */
CurDrive = getdisk(); /* Get default drive in case it is specified */
for (I = 0; I <= 25; I++) /* Initialize array to no drives speced */
Drives[I] = FALSE;
while (Files != NULL) { /* For each filespec */
if (strchr(Files -> Name,':') == NULL) /* If no drive speced, use
default drive */
Drives[CurDrive] = TRUE;
else {
Drive = Files -> Name[0] - 'A'; /* Get drive number */
if (0 <= Drive && Drive <= 25) /* Make sure it is a letter */
Drives[Drive] = TRUE;
}
Files = Files -> Next;
}
putchar('\n'); /* Put a blank line between program invokation and
first line of listing */
for (I = 0; I <= 25; I++) /* For each possible drive */
if (Drives[I]) { /* If drive was specified in any filespec */
Volume = Label(I); /* Get volume label */
if (Volume == NULL)
printf("Volume in drive %c has no label",I + 'A');
else
printf("Volume in drive %c is %s",I + 'A',Volume);
if (PrintFree) /* If PrintFree is true, print free space */
printf(" : %s bytes free",AddCommasL(FreeSpace(I)));
puts(".");
More(1);
}
}
int AUSize(int Drive)
/* Returns the size of the allocation units on drive Drive, in bytes. */
{
struct fatinfo FatInfo; /* Struct used by getfat() */
getfat(Drive + 1,&FatInfo);
/* Size of allocation units = # of sectors per cluster * bytes per sector */
return(FatInfo.fi_sclus * FatInfo.fi_bysec);
}
long SpaceUsed(Entry *Entries,int Num,int Drive)
/* Returns the actual amount of space used by all of the files in Entries,
assuming that they are on drive Drive */
{
int AU; /* Size of allocation units on drive Drive */
int I;
long TotalSpace = 0L; /* Total space used by the files */
AU = AUSize(Drive); /* Find size of alloc units */
for (I = 0; I < Num; I++) /* For each file, add the space it uses */
TotalSpace += (Entries[I] -> ff_fsize + AU - 1) / AU * AU;
return(TotalSpace);
}
char *AddCommasL(long N)
/* Converts long integer N to a string, stores it in a static buffer,
inserts commas where appropriate, and returns a pointer to the bufffer */
{
static char Buf[15]; /* Buffer */
int Pos, /* Current position that comma is being inserted at */
Num, /* Number of commas to insert */
I;
Pos = sprintf(Buf,"%ld",N); /* Convert N to a string and put the length of
the string in Pos */
Num = ((Buf[0] == '-' ? Pos - 1 : Pos) - 1) / 3; /* Find num of commas
to insert */
for (I = 0; I < Num; I++) {
Pos -= 3; /* Find next location at which to insert comma */
Insert(Buf,",",Pos);
}
return(Buf);
}
long FreeSpace(int Drive)
/* Returns the free space on drive Drive */
{
struct dfree DFree; /* Structure used by getdfree */
getdfree(Drive + 1,&DFree); /* Get available clusters, sectors/cluster,
and bytes/sector */
ExTest(DFree.df_sclus == -1,"Disk error."); /* Exit if error */
return((long) DFree.df_avail * DFree.df_bsec * (long) DFree.df_sclus);
}
char *Label(int Drive)
/* if drive Drive (0 = A, 1 = B, etc) has a label, it is put in a static
buffer and a pointer to the buffer is returned. Otherwise a null
pointer is returned */
{
static char *FileSpec = " :\\*.*", /* Filespec that will by used to
find label, with room for drive letter */
Volume[13]; /* Buffer */
struct ffblk FFBlk; /* Structure used by findfirst() */
char *Dot; /* Start of file ext in volume label */
FileSpec[0] = Drive + 'A'; /* Add drive letter to filespec */
if (findfirst(FileSpec,&FFBlk,FA_LABEL)) /* Get label */
return(NULL); /* if no label, return null */
else {
strcpy(Volume,FFBlk.ff_name); /* Copy label to volume */
Dot = strchr(Volume,'.');
if (Dot != NULL) /* If volume had a dot in it, remove it */
strcpy(Dot,Dot - Volume + FFBlk.ff_name + 1);
return(Volume);
}
}
void GetEntries(EntryListType *EntryList,int Attribs[],char Name[],int PrintCP)
/* calloc() is used to make an array of pointers to the structure returned by
findfirst/next. The array is expanded with realloc() as neccessary.
Elements of the array are made to point to matching directory entries.
A dir entry will match if its name matches Name and its attribs match at
least one of the attrib and-terms in Attribs. "." and ".." dir entries will
match only attrib specs that specifically include dirs, and only if PrintCP
is true. The entry list and number of matching entries found are returned
in EntryList. */
{
int Done, /* Whether all matching files have been found */
NumEnts, /* Number of matching entries found */
DirAttribs[MAX_ATTS]; /* Attribute list containing those attrib terms in
Attribs that specifically include directories */
unsigned Length; /* Current length that the entry array has been
c/realloc()ed to */
Entry *Entries; /* Entry array */
if (PrintCP)
FiltAttribs(Attribs,DirAttribs,FA_DIREC); /* Copy all entries in Attribs
that specifically include dirs into DirAttribs */
else
*DirAttribs = NO_AT;
Length = ENTRY_INC; /* Do initial allocation for Entries */
Entries = (Entry *) CallocT(Length,sizeof(Entry));
NumEnts = 0; /* No entries found yet */
Entries[0] = (Entry) MallocT(sizeof(struct ffblk)); /* Start with one
empty entry */
Done = findfirst(Name,Entries[0],0x3f);
while (!Done) { /* For each entry in specified dir that matches Name */
if (MatchingAtts(CPDir(Entries[NumEnts] -> ff_name) ?
DirAttribs : Attribs,Entries[NumEnts] -> ff_attrib)) { /* Check whether
the attribs of the found entry match any of those in Attribs if the
found entry is not the current or parent dir ("." or ".."), or in
DirAttribs if it is one of them. The current or parent dir will
therefore only be printed if directories have specifically been
requested */
if (NumEnts == Length - 1) { /* Reallocate Entries with more space */
Length += ENTRY_INC;
Entries = (Entry *) ReallocT(Entries,Length * sizeof(Entry));
}
Entries[++NumEnts] = (Entry) MallocT(sizeof(struct ffblk)); /* Add
new empty node */
*(Entries[NumEnts]) = *(Entries[NumEnts - 1]); /* copy last matching
entry to new node for next call to findnext() */
}
Done = findnext(Entries[NumEnts]); /* Get next matching entry */
}
free(Entries[NumEnts]); /* Get rid of last node added, since it is the
one that findnext() failed on */
EntryList -> NumEnts = NumEnts; /* Copy array & num of entries to
EntryList */
EntryList -> Entries = Entries;
}
void FiltAttribs(int OldAtts[],int NewAtts[],int Att)
/* Copies all attributes in OldAtts that have at least one of the attribs
indicated in Att, or that have no attribs (= 0) to NewAtts */
{
int I,
NumAtts; /* Number of matching attrib terms found */
NumAtts = I = 0;
while (OldAtts[I] != NO_AT) {
if (OldAtts[I] & Att || OldAtts[I] == 0)
NewAtts[NumAtts++] = OldAtts[I];
I++;
}
NewAtts[NumAtts] = NO_AT; /* Mark end of array */
}
void PrintList(EntryListType *EntryList, /* Entry list and length of list */
ListType *ListSpec, /* Specifies what type of listing to do */
char *Dir /* Name of path that list was produced from */)
/* Print list of dir entries according to ListSpec */
{
int J,
NumEnts; /* Number of entries in list */
Entry *Entries; /* Entry list */
Entries = EntryList -> Entries;
NumEnts = EntryList -> NumEnts;
if (ListSpec -> ListType != 'F' && ListSpec -> PrintHeaders) {
/* If list type is not "full path" and headers are to be printed...*/
printf("Directory of %s : %d matching file(s)",Dir,NumEnts);
if (ListSpec -> PrintFree) /* Print total space used by files */
printf(" using %ldk",
(SpaceUsed(Entries,NumEnts,(int) *Dir - 'A') + 1023) >> 10);
puts(".\n");
More(2);
}
if (NumEnts) { /* If at least one matching entry for dir */
switch (ListSpec -> ListType) {
case 'W' : PrintShort(Entries,NumEnts,ListSpec -> LineEnts);
break;
case 'L' : PrintLong(Entries,NumEnts);
break;
case 'F' : PrintFull(Entries,NumEnts,Dir);
}
for (J = 0; J < NumEnts; J++) /* Free entry nodes pointed to by array */
free(Entries[J]);
}
free(Entries); /* Free array */
}
MatchingAtts(int Attribs[],char Attrib)
/* Returns true if Attrib matches any of the attrib and-terms in Attribs;
otherwise returns false. Attrib will match a term if it has all of the
attributes specified in the low byte of the term and none of the attribs
specified in the high byte of the term */
{
int I,
FullAtt; /* Full attribute */
FullAtt = Attrib | (~((int) Attrib) << 8); /* Produce full attrib by
copying Attrib in true form into the low byte and complemented form
into the high byte. */
I = 0;
while (Attribs[I] != NO_AT && (Attribs[I] & FullAtt) != Attribs[I])
/* Check whether Attrib matches any of the attribute terms given in
Attribs. To check for match, FullAtt is first ANDed with an attrib
term. Since Attrib is true in the low byte of FullAtt, all attrib
bits that Attrib has and are specified in lo byte of the term (attribs
that Attrib must have to match) will be on in the low byte of the
result. Since Attrib is complemented in the high
byte of FullAtt, all attrib bits that Attrib does not have and are
specified in the high byte of the term (attribs that Attrib cannot
have if it is to match) will be on in the high byte of the result.
In order to match, the result must therefor be equal to the term. */
I++;
return(Attribs[I] != NO_AT);
}
void PrintShort(Entry *Entries,int Num,int LineEnts)
/* Prints a 'short' listing of the entries in Entries. Prints the file names
only, with LineEnts names on each line. A backslash is appended to
directory entries. Num is the number of entries in Entries */
{
int I,
Width, /* Width to print each name with (padding with blanks) */
NotEven; /* Whether the entries will exactly fit on a line */
char *Name, /* Points to the name being printed (either the original
list entry or a copy of it with a backslash appended) */
Dir[14]; /* Copy of the file name, with a backslash appended to
indicate that it is a directory */
Width = 80 / LineEnts; /* Find width to print each name with */
NotEven = 80 % LineEnts; /* Determine whether LineEnts names will
exactly fit on a line */
for (I = 0; I < Num; I++) { /* For each entry */
if (Entries[I] -> ff_attrib & FA_DIREC) { /* If the entry is a dir, make
a copy of it, append a backslash to it, & make Name point to the copy */
strcpy(Dir,Entries[I] -> ff_name);
strcat(Dir,"\\");
Name = Dir;
}
else /* Otherwise make Name point to the original name */
Name = Entries[I] -> ff_name;
printf("%-*s",Width,Name); /* Print the name */
if ((I + 1) % LineEnts == 0) { /* If it was the last name on a line */
if (NotEven) /* If LineEnts entries do not exactly fit on a line,
the cursor will not have automatically wrapped around to the next
line; print a newline */
putchar('\n');
More(1);
}
}
if (I % LineEnts) { /* If the last name printed was not the last one on
a line and therefore did not cause a CR, print one */
putchar('\n');
More(1);
}
}
void PrintLong(Entry *Entries,int Num)
/* Print a 'long' dir listing. Prints each file's name, ext, size, time, date,
and attributes. Dirs and the volume label report a size of 0, so they are
printed with a special label in the size column. The list is modified. */
{
int I;
Entry File; /* Entry being printed */
char Size[12], /* File size converted to a string, or a special label for
dirs and the volume label */
*Ext; /* Start of the extension of the file name */
for (I = 0; I < Num; I++) {
File = Entries[I];
if (File -> ff_attrib & FA_DIREC) /* If entry is a dir or the vol label,
give it a label instead of a file size */
strcpy(Size,"<DIR> ");
else if (File -> ff_attrib & FA_LABEL)
strcpy(Size,"<LAB> ");
else
sprintf(Size,"%ld",File -> ff_fsize);
Ext = strchr(File -> ff_name,'.'); /* Find the start of the ext
of the file name */
if (Ext == NULL || CPDir(File -> ff_name)) /* If there is no ext,
or the first dot in the entry for the current or parent dir was found,
make Ext point to a null string */
Ext = "";
else {
*Ext = '\0'; /* Otherwise make Ext point to the start of the file
ext and replace the dot with a null char so that when the file name
is printed it will not include the file ext */
Ext++;
}
printf("%-9s%-3s%9s%10s%8s %-6s\n",File -> ff_name,Ext,Size,
FileDate(File -> ff_fdate),FileTime(File -> ff_ftime),
AttribWtoS(File -> ff_attrib));
More(1);
}
}
char *FileDate(int PDate)
/* Converts the file date word PDate into a string of the form mm-dd-yy,
puts it in a static buffer and returns a pointer to the buffer */
{
static char Date[9];
sprintf(Date,"%2u-%02u-%02u",(PDate >> 5) & 0xf,PDate & 0x1f,
((PDate >> 9) & 0x7f) + 80);
return(Date);
}
char *FileTime(int PTime)
/* Converts the file time word PTime to a string of the form HH:MMm, where m
is 'a' for AM and 'p' for PM, puts it in a static buffer, and returns a
pointer to the buffer */
{
static char Time[7];
int NHour; /* File hour in 12-hour format */
NHour = ((PTime >> 11) & 0x1f) % 12;
if (NHour == 0)
NHour = 12;
sprintf(Time,"%2u:%02u%c",NHour,(PTime >> 5) & 0x3f,
((PTime >> 11) & 0x1f) > 11 ? 'p' : 'a');
return(Time);
}
void PrintFull(Entry *Entries,int Num,char *Dir)
/* Print 'Full Path' listing of the file names in Entries. The entries in
Entries are assumed to have been produced from the path given in Dir.
Dir is copied, and the filename is removed from it. For each file name in
Entries, prints the file's size, time, date, and attributes; then Dir is
printed followed by the filename. If the entry is a dir, a backslash is
printed at the end.
Dirs and the volume label report a size of 0, so they are printed with a
special label in the size column.
The list is modified. Num is the # of entries in Entries. */
{
char Name[MAXPATH]; /* Full path name of the file name */
int I;
Entry File; /* Entry being printed */
char Size[12]; /* File size converted to a string, or a special label for
dirs and the volume label */
strcpy(Name,Dir);
*(strrchr(Name,'\\') + 1) = '\0'; /* Remove the file name from Dir */
for (I = 0; I < Num; I++) {
File = Entries[I];
if (File -> ff_attrib & FA_DIREC) /* If entry is a dir or the vol label,
give it a label instead of a file size */
strcpy(Size,"<DIR> ");
else if (File -> ff_attrib & FA_LABEL)
strcpy(Size,"<LAB> ");
else
sprintf(Size,"%ld",File -> ff_fsize);
/* If the entry is a directory, follow it with a backslash */
printf("%9s%10s%8s %-6s%s%s%s\n",Size,FileDate(File -> ff_fdate),
FileTime(File -> ff_ftime),AttribWtoS(File -> ff_attrib),Name,
File -> ff_name,File -> ff_attrib & FA_DIREC ? "\\" : "");
More(1);
}
}
void SortList(EntryListType *EntryList,char Sort)
/* Sorts EntryList according the sort specification Sort. Any groups of
entries that are not differentiated by the first sort are then sorted
by name. The sort specs are:
X = alpha by file eXtension; T = by Time & date, oldest to newest;
Z = by file siZe, smallest to largest; I = by directory attribute,
dirs first, then other files; N = sort by Name only; */
{
int Num, /* Number of entries */
Start, /* Start of a group of undifferentiated entries */
I;
Entry *List; /* List to sort */
int (*CompFunc)(Entry *E1,Entry *E2), /* Comparison func to be used in sort */
ExtComp(Entry *E1,Entry *E2),TDComp(Entry *E1,Entry *E2),
SizeComp(Entry *E1,Entry *E2),DAComp(Entry *E1,Entry *E2),
NameComp(Entry *E1,Entry *E2); /* Comparison functions */
Num = EntryList -> NumEnts;
List = EntryList -> Entries;
if (Sort != 'N') { /* Do a first sort */
switch (Sort) { /* Select sort function */
case 'X' : CompFunc = ExtComp;
break;
case 'T' : CompFunc = TDComp;
break;
case 'Z' : CompFunc = SizeComp;
break;
case 'I' : CompFunc = DAComp;
}
qsort(List,Num,sizeof(Entry),CompFunc); /* Do first sort */
}
Start = 0; /* Intialize undiffentiated entries start to 0 */
for (I = 1; I < Num; I++)
if ((*CompFunc)(&List[I - 1],&List[I])) { /* If comp func differentiates
current entry from the previous one */
if (I - Start > 1) /* If more than one entry was passed before
differentiation was found, sort undifferentiated entries by name */
qsort(List + Start,I - Start,sizeof(Entry),NameComp);
Start = I; /* Make start of undiff ents be current ent */
}
if (I - Start > 1) /* If the last entry was the last of a group of
undiffed entries, sort them */
qsort(List + Start,I - Start,sizeof(Entry),NameComp);
}
DAComp(Entry *E1,Entry *E2)
/* Compares file dir attribs. Returns a negative int if E1 is a dir and
E2 is not; 0 if they both are or are not, and a positive int if E2 is
and E1 is not */
{
return(((*E2) -> ff_attrib & FA_DIREC) - ((*E1) -> ff_attrib & FA_DIREC));
}
NameComp(Entry *E1,Entry *E2)
/* Compares the names of E1 and E2. Returns a neg int, zero, or a pos int if
the name of E1 is lexicographically less than, equal to, or greater than
the name of E2. Since strings are terminated with a null (lex value 0),
a file name will be less than the same initial name with extra characters,
including an ext. Because '.' has a value less than any letter, a file name
with an extension will be less than a file name with the same initial name
but other extra chars instead of an ext. */
{
return(strcmp((*E1) -> ff_name,(*E2) -> ff_name));
}
ExtComp(Entry *E1,Entry *E2)
/* Compares file names by their extension. Returns a neg int, zero, or a
pos int if the ext of E1 is lexicographically less than, equal to, or
greater than the ext of E2. A file name without an ext will be less than
a file name with one. */
{
char *Ext1,*Ext2; /* Exts of the files, or null strings if they did
not have exts */
Ext1 = strchr((*E1) -> ff_name,'.');
if (Ext1 == NULL)
Ext1 = "";
Ext2 = strchr((*E2) -> ff_name,'.');
if (Ext2 == NULL)
Ext2 = "";
return(strcmp(Ext1,Ext2));
}
TDComp(Entry *E1,Entry *E2)
/* Compares the time and date of E1 and E2. Returns -1, 0, or +1 if the
file date if E1 is earlier than, the same as, or later than the file
date of E2 */
{
return(Sign((((long) ((*E1) -> ff_fdate) << 16) + (*E1) -> ff_ftime) -
(((long) ((*E2) -> ff_fdate) << 16) + (*E2) -> ff_ftime)));
}
SizeComp(Entry *E1,Entry *E2)
/* Compares the size fo E1 and E2. Returns -1, 0, or +1 if E1 is smaller
than, the same size as, or larger than E2 */
{
return(Sign((*E1) -> ff_fsize - (*E2) -> ff_fsize));
}