322 lines
9.0 KiB
C
322 lines
9.0 KiB
C
/*
|
|
* MWC86 CPS Version 3.1.1.
|
|
* Copyright (c) 1982-1986 by Mark Williams Company, Chicago.
|
|
* All rights reserved. May not be copied or disclosed without permission.
|
|
*/
|
|
|
|
/*
|
|
* fdir.c
|
|
* 6/5/86
|
|
*
|
|
* This program prints a formatted directory of its command line arguments.
|
|
* Example command lines:
|
|
* fdir
|
|
* fdir *.c
|
|
* fdir \test\*.c \source\*.*
|
|
* It demonstrates some advanced features of MSDOS MWC86:
|
|
* (1) The intcall() function, used to call MSDOS.
|
|
* (2) The ptoreg() macro, used to pass a pointer to MSDOS
|
|
* with code which is independent of LARGE or SMALL model.
|
|
* (3) Bit fields, used to reference the dates of the files.
|
|
* (4) The qsort() function, used to sort files and directories.
|
|
* (5) The ctype macros.
|
|
* (6) Dynamic memory allocation.
|
|
* Files within each directory are sorted alphabetically.
|
|
* Directories are listed before other files.
|
|
* The output is in the following format for each file:
|
|
* <attribute bits> <file length> <last modification date> <filename>
|
|
*/
|
|
|
|
/*
|
|
* Header files.
|
|
*/
|
|
#include <stdio.h> /* Standard I/O definitions */
|
|
#include <ctype.h> /* Character type macros */
|
|
#include <dos.h> /* MSDOS definitions */
|
|
|
|
/*
|
|
* Predefined limits.
|
|
*/
|
|
#define FNLEN 80 /* Maximum filename length */
|
|
#define MAXFILES 512 /* Max number of files/directory */
|
|
|
|
/*
|
|
* This macro returns the number of elements of its array argument.
|
|
*/
|
|
#define SIZE(x) (sizeof(x)/sizeof(x[0]))
|
|
|
|
/*
|
|
* File attributes.
|
|
* Defined on page C-4 of the IBM-PC DOS 2.0 manual.
|
|
*/
|
|
#define A_RONLY 0x01 /* Read only file */
|
|
#define A_HIDDEN 0x02 /* Hidden file */
|
|
#define A_SYSTEM 0x04 /* System file */
|
|
#define A_DIRECTORY 0x10 /* Directory file */
|
|
#define A_ARCHIVE 0x20 /* Archive bit */
|
|
|
|
/*
|
|
* Attribute structure, associating a name with each mask.
|
|
*/
|
|
static struct atr {
|
|
int a_mask;
|
|
int a_name;
|
|
} atr[] = {
|
|
A_DIRECTORY, 'd', /* Directory file */
|
|
A_RONLY, 'r', /* Read only file */
|
|
A_HIDDEN, 'h', /* Hidden file */
|
|
A_SYSTEM, 's', /* System file */
|
|
A_ARCHIVE, 'a' /* Archive bit */
|
|
};
|
|
|
|
/*
|
|
* Structure of MSDOS date/time bit fields.
|
|
* Defined on page C-5 of the IBM-PC DOS 2.0 manual.
|
|
*/
|
|
struct date {
|
|
unsigned d_sec : 5; /* Time, 2 second intervals */
|
|
unsigned d_min : 6; /* Time, minutes */
|
|
unsigned d_hour : 5; /* Time, hours */
|
|
unsigned d_day : 5; /* Date, day of month */
|
|
unsigned d_month : 4; /* Date, month of year */
|
|
unsigned d_year : 7; /* Date, year since 1980 */
|
|
};
|
|
|
|
/*
|
|
* Structure filled in by MSDOS for FINDFIRST and FINDNEXT calls.
|
|
* Defined of page D-49 of the IBM-PC DOS 2.0 manual.
|
|
*/
|
|
#define FNDINFO 21
|
|
#define FNDNAME 13
|
|
struct find {
|
|
char fnd_dosinfo[FNDINFO]; /* Reserved for dos */
|
|
char fnd_attr; /* File attribute */
|
|
struct date fnd_date; /* Date structure */
|
|
long fnd_size; /* File size */
|
|
char fnd_name[FNDNAME]; /* File name less path */
|
|
};
|
|
|
|
/*
|
|
* Routines which return values other than int.
|
|
* All such routines should be declared before they are used.
|
|
* Failure to do so could sacrifice portability between machines
|
|
* or between LARGE and SMALL model.
|
|
*/
|
|
extern char *basename();
|
|
extern char *date();
|
|
extern void fatal();
|
|
extern char *malloc();
|
|
extern int qscmp();
|
|
extern char *rindex();
|
|
extern char *strcpy();
|
|
extern char *strlower();
|
|
|
|
/*
|
|
* Global data.
|
|
*/
|
|
int filecount = 0; /* Total number of files */
|
|
int dircount = 0; /* Total number of directories */
|
|
long bytecount = 0L; /* Total length of files in bytes */
|
|
char path[FNLEN]; /* Filename path */
|
|
struct find *fnd[MAXFILES]; /* Pointers to file structures */
|
|
|
|
main(argc, argv)
|
|
int argc; /* Number of command line arguments */
|
|
char *argv[]; /* Array of pointers to arguments */
|
|
{
|
|
register int i;
|
|
register int count; /* Number of files found */
|
|
register char *file; /* Command line argument */
|
|
register int status; /* Exit status */
|
|
|
|
status = 0;
|
|
|
|
/* If no command line arguments, i.e. "fdir", fake argument "*.*". */
|
|
if (argc == 1) {
|
|
argc = 2;
|
|
argv[1] = "*.*";
|
|
}
|
|
|
|
while (--argc) { /* Loop for each argument */
|
|
file = *++argv; /* Grab next argument */
|
|
i = basename(file) - file; /* Find pathname length */
|
|
if (i + FNDNAME > FNLEN)
|
|
fatal("filename \"%s\" too long", file);
|
|
strcpy(path, file); /* Copy file to path[] */
|
|
path[i] = '\0'; /* NUL-terminate pathname */
|
|
strlower(path); /* Convert pathname to lower case */
|
|
count = dosfind(file); /* Find matching files */
|
|
if (!count) { /* Issue error if no match */
|
|
++status;
|
|
fprintf(stderr, "fdir: cannot find \"%s\"\n", file);
|
|
}
|
|
qsort(fnd, count, sizeof(struct find *), qscmp); /* Sort */
|
|
for (i=0; i < count; i++) /* Loop for each matching file */
|
|
printinfo(fnd[i]); /* Print information */
|
|
}
|
|
|
|
/* Print totals. */
|
|
printf("\n");
|
|
if (dircount)
|
|
printf("Directories: %d\n", dircount);
|
|
if (filecount)
|
|
printf("Other files: %d\n", filecount);
|
|
if (bytecount)
|
|
printf("Total bytes: %D\n", bytecount);
|
|
exit(status);
|
|
}
|
|
|
|
/*
|
|
* Return a pointer to the beginning of the filename (past pathname, if any).
|
|
*/
|
|
char *basename(name) char *name;
|
|
{
|
|
register char *p;
|
|
|
|
if ((p = rindex(name, '\\')) != NULL || (p = rindex(name, ':')) != NULL)
|
|
return(++p);
|
|
return(name);
|
|
}
|
|
|
|
/*
|
|
* Given a pointer to a date structure, return a pointer to an ASCII date.
|
|
* The buffer for the return value is statically allocated.
|
|
*/
|
|
char *date(dp) register struct date *dp;
|
|
{
|
|
static char ad[21];
|
|
static char *month[] = {
|
|
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
|
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
|
|
};
|
|
|
|
sprintf(ad, "%s %2d %02d:%02d:%02d %4d",
|
|
month[dp->d_month - 1], dp->d_day,
|
|
dp->d_hour, dp->d_min, dp->d_sec * 2,
|
|
dp->d_year + 1980);
|
|
return(ad);
|
|
}
|
|
|
|
/*
|
|
* Expand filename 'file' possibly containing wildcard characters.
|
|
* Initialize global array fnd[] with pointers to matching filenames and
|
|
* return the number of matches found.
|
|
*/
|
|
int dosfind(file) char *file;
|
|
{
|
|
register int count; /* Number of files found */
|
|
struct find f; /* Filename return data from MSDOS */
|
|
struct reg r; /* Register values for intcall() */
|
|
|
|
/*
|
|
* Call MSDOS to set the disk transfer address (DTA) to &f.
|
|
* Macro ptoreg() stores the segment:offset of f
|
|
* in r.r_ds and r.r_dx in a model independent manner.
|
|
*/
|
|
r.r_ax = SETDTA; /* Function code to AX */
|
|
ptoreg(dsreg, r.r_dx, r.r_ds, &f); /* Seg:off of f to DS:DX */
|
|
intcall(&r, &r, DOSINT); /* Call MSDOS */
|
|
|
|
/* Call MSDOS to find the first matching filename. */
|
|
r.r_ax = NFFIRST; /* Function code to AX*/
|
|
r.r_cx = A_HIDDEN | A_SYSTEM | A_DIRECTORY | A_RONLY | A_ARCHIVE;
|
|
/* Attributes to CX */
|
|
ptoreg(dsreg, r.r_dx, r.r_ds, file); /* Seg:off of file to DS:DX */
|
|
intcall(&r, &r, DOSINT); /* Call MSDOS */
|
|
|
|
/*
|
|
* Loop for each matching filename.
|
|
* Carry flag set indicates no more matching files.
|
|
*/
|
|
for (count = 0; (r.r_flags & F_CF) == 0; count++) {
|
|
|
|
/* Do not exceed maximum number of matching files. */
|
|
if (count >= MAXFILES)
|
|
fatal("too many matching files");
|
|
|
|
/* Dynamically allocate memory for a find structure. */
|
|
if ((fnd[count] = (struct find *)malloc(sizeof(struct find))) == NULL)
|
|
fatal("out of memory");
|
|
|
|
/* Copy the structure to the allocated space. */
|
|
*fnd[count] = f; /* Structure assignment. */
|
|
|
|
/* Find the next matching filename. */
|
|
r.r_ax = NFNEXT; /* Find next function */
|
|
intcall(&r, &r, DOSINT); /* Call MSDOS */
|
|
}
|
|
return(count);
|
|
}
|
|
|
|
/*
|
|
* Fatal error.
|
|
* The "%r" (recursive) format specifier is non-portable.
|
|
*/
|
|
void fatal(p) char *p;
|
|
{
|
|
fprintf(stderr, "fdir: %r\n", &p);
|
|
exit(1);
|
|
}
|
|
|
|
/*
|
|
* Print a line of information about a file.
|
|
* Update running totals.
|
|
*/
|
|
printinfo(fndp) register struct fnd *fndp;
|
|
{
|
|
register int i, attr;
|
|
|
|
attr = fndp->fnd_attr;
|
|
if (attr&A_DIRECTORY)
|
|
++dircount;
|
|
else
|
|
++filecount;
|
|
bytecount += fndp->fnd_size;
|
|
|
|
/* Print attributes (name if bit is set, '-' if not). */
|
|
for (i = 0; i < SIZE(atr); i++)
|
|
putchar((atr[i].a_mask & attr) ? atr[i].a_name : '-');
|
|
|
|
/* Print size, date, name. */
|
|
printf(" %10D %s %s%s\n",
|
|
fndp->fnd_size, date(&(fndp->fnd_date)), path, strlower(fndp->fnd_name));
|
|
|
|
/* Free allocated structure. */
|
|
free(fndp);
|
|
}
|
|
|
|
/*
|
|
* Compare routine for qsort().
|
|
* Order: directories alphabetically first, then other files alphabetically.
|
|
* Called by qsort(), passed two pointers to find structure pointers.
|
|
* Only the pointers to the find structures are actually sorted.
|
|
*/
|
|
int qscmp(fpp1, fpp2) struct find **fpp1, **fpp2;
|
|
{
|
|
register struct find *f1;
|
|
register struct find *f2;
|
|
register int i;
|
|
|
|
f1 = *fpp1;
|
|
f2 = *fpp2;
|
|
if (i = (f2->fnd_attr&A_DIRECTORY) - (f1->fnd_attr&A_DIRECTORY))
|
|
return(i); /* Exactly one is directory */
|
|
return (strcmp(f1->fnd_name, f2->fnd_name));
|
|
}
|
|
|
|
/*
|
|
* Convert string 'str' to lower case (in-place) and return a pointer to it.
|
|
*/
|
|
char *strlower(str) char *str;
|
|
{
|
|
register int c;
|
|
register char *s;
|
|
|
|
s = str;
|
|
while (c = *s)
|
|
*s++ = tolower(c);
|
|
return(str);
|
|
}
|
|
|
|
/* end of fdir.c */
|