Category : File Managers
Archive   : DIRCMP2.ZIP
Filename : DIRCMP.C
#include "stdio.h"
/*
* dircmp.
*
*
* See below for more information.
*
*/
char *documentation[] = {
"dircmp compares two directories. Execute by",
" dircmp [flags] path_a path_b",
"",
"Flags are single characters preceeded by '-':",
" -a Print filenames of files in path_a and not in path_b",
" -b Print filenames of files in path_b and not in path_a",
" -m Print filenames of files in both path_a and path_b",
" (path_a is output. See -p flag below)",
" -p Prefix file name with its entire path",
" -f Prefix a %1 and suffix a %2 to each line",
" -e Extended compare includes date/time in comparison",
"",
"The path_a and path_b are similar to the parameters to DOS COPY command).",
"",
"The output is sent to STDOUT and, therefore, may be redirected.",
"",
0 };
#define DeSmet 1
#define MSDOS 1
#ifndef stdin
#define stdin STDIN
#define stdout STDOUT
#define stderr STDERR
#endif
#define LMAX 512 /* max number of entries per directory */
int mflag = 0;
int fflag = 0;
int pflag = 0;
int eflag = 0;
int debug = 0; /* Set for debug code */
struct dirent {
char dname[13]; /* name + ext + EOS */
unsigned date;
unsigned time;
char fill_eos; /* give strcmp an end of string */
};
int numd1; /* number of entries in d1 */
struct dirent d1[LMAX]; /* area for storing directory a */
int numd2; /* number of entries in d2 */
struct dirent d2[LMAX]; /* area for storing directory b */
char endmark[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, '.',
0xff, 0xff, 0xff, 0x00};
struct dirent *dp[2];
int *dn[2];
char path_a[80], path_b[80], *paths[2];
char mydta[80] = {0};
/*******************************************************/
main(argc, argv)
char *argv[];
int argc;
{
register int c;
register char *p;
register int i, j;
int nfile, nmatch, loaddir(), outname(), usage(), error();
int help(), qsort(), strcmp();
long match;
dp[0] = d1; /* allow indirect addressing of d1 and d2 */
dp[1] = d2;
dn[0] = &numd1; /* allow indirect addressing of numd1 and numd2 */
dn[1] = &numd2;
paths[0] = path_a; /* indirect for path maintenance */
paths[1] = path_b;
if (argc <= 1)
usage("No arguments");
if (argc == 2 && argv[1][0] == '?' && argv[1][1] == 0) {
help(documentation);
return;
}
nfile = argc-1;
for (i=1; i < argc; ++i) {
p = argv[i];
if (*p == '-') {
++p;
while (c = *p++) {
switch(tolower(c)) {
case '?':
help(documentation);
break;
case 'a':
mflag = -1;
break;
case 'd':
++debug;
break;
case 'b':
mflag = 1;
break;
case 'p':
++pflag;
break;
case 'f':
++fflag;
break;
case 'm':
mflag = 0;
break;
case 'e':
++eflag;
break;
default:
usage("Unknown flag");
}
}
argv[i] = 0;
--nfile;
}
}
if (nfile < 2)
usage("Two paths are required. dircmp -? for instructions.");
else if (nfile > 2)
usage("Only two paths are allowed. dircmp -? for instructions.");
else {
j = 0;
for (i=1; i < argc; i++)
if (p = argv[i])
if (loaddir(p, j++)) /* j is index to paths[] and dp[] */
error("Cannot load directory for ", p);
}
qsort(d1, numd1, sizeof(d1[0]), strcmp); /* sort path_a array */
qsort(d2, numd2, sizeof(d1[0]), strcmp); /* sort path_b array */
i = j = 0;
while ((strcmp(&d1[i].dname, endmark))
|| (strcmp(&d2[j].dname, endmark))) {
/* check for match of names and (optionally) date/time */
nmatch = match = strcmp(&d1[i].dname, &d2[j].dname);
if ((!match) && eflag)
if (!(match = ((long)d2[j].date - (long)d1[i].date)))
match = ((long)d2[j].time - (long)d1[i].time);
/* normalize match variable to -1, 0, or +1 */
if (match)
if (match < 0)
match = -1;
else
match = 1;
if (match == mflag)
outname(i, j, match); /* output whichever is lower */
if (nmatch <= 0) /* pop path_a entry */
i++; /* on name match basis only */
if (nmatch >= 0) /* pop path_b entry */
j++;
}
if (mflag < 0) /* finish path a entries */
while (strcmp(&d1[i].dname, endmark)) {
outname(i, j, match); /* output one record */
i++;
}
else if (mflag > 0) /* finish path b entries */
while (strcmp(&d2[j].dname, endmark)) {
outname(i, j, match); /* output one record */
j++;
}
}
/*******************************************************/
int loaddir(path, indndx)
char *path, indndx;
int indndx;
{
char *fexpnd(), *getret;
int i, strcpy(), *cnvttime, *cnvtdate;
i = 0;
cnvttime = &mydta[22];
cnvtdate = &mydta[24];
while (getret = fexpnd(path, paths[indndx])) {
strcpy(dp[indndx] + i, getret); /* copy file.ext to next bucket */
if (i > (LMAX-1))
error("Table overflow for path ",path);
if (eflag) {
(*(dp[indndx]+i)).time = *cnvttime;
(*(dp[indndx]+i)).date = *cnvtdate;
}
(*(dp[indndx]+i)).fill_eos = '\0';
i++;
}
strcpy(dp[indndx] + i, endmark);
*dn[indndx] = i + 1;
return FALSE;
}
/*******************************************************/
outname(d1ndx, d2ndx, match)
int d1ndx, d2ndx, match;
{
int bldname();
if (match <= 0)
bldname(&d1[d1ndx].dname, 0);
else
bldname(&d2[d2ndx].dname, 1);
}
/*******************************************************/
bldname(fname, pathndx)
char *fname;
int pathndx;
{
int puts();
if (fflag)
puts("%1 "); /* output leading %1, if requested */
if (pflag)
puts(paths[pathndx]); /* output path prefix, if requested */
puts(fname); /* output file name */
if (fflag)
puts(" %2"); /* output trailing %2, if requested */
puts("\n"); /* and add end of line */
}
/*******************************************************/
usage(s)
char *s;
{
puts("?DIRCMP-E-");
puts(s);
puts("\n");
puts("Usage: dircmp [-abmfp]. dircmp ? for help\n");
exit(1);
}
/*******************************************************/
/*
fexpnd returns a pointer to the next filename.ext which
matches the first parameter (str) or a zero is no file
matches that parameter. The given parameter is processed
to generate a normalized search string and a normalized
path prefix. The prefix is returned in the area pointed to
by the second parameter. This string may be concatenated
with the returned value to give a fully-qualified dataset
name for open, rename, erase, etc.
You should continue calling fexpnd with the same first
parameter until it returns a zero, then pass it a new first
parameter. This can be used to expand a list of ambiguous
filenames on a parameter list. */
char fxs[80] = {0};
char fxsrch[80] = {0};
char fxpref[80] = {0};
char *fexpnd(str, path)
char *str, *path;
{
int getret, fixpath(), strcpy(), dirnxt(), dirfst();
int savdta(), restdta();
char *rindex();
savdta(); /* save old dta in an unknown location */
setdta(&mydta); /* make MS-DOS use my dta */
if (strcmp(str,fxs) == 0)
getret = dirnxt(); /* MS-DOS 2.0+ get next function */
else {
strcpy(fxs,str); /* for detection of a new name */
if (!fixpath(str, fxsrch, fxpref))
return (0);
strcpy(path, fxpref); /* give path to caller */
getret = dirfst(fxsrch); /* MS-DOS 2.0+ get first function */
}
restdta(); /* restore old dta */
if (getret == 0) {
return(&mydta[30]); /* return filename.ext */
}
else
return 0;
}
int dtads, dtadx;
savdta()
{
#asm
PUSH AX
PUSH ES
PUSH BX
MOV AH,2fh
INT 21h
MOV WORD dtads_,ES
MOV WORD dtadx_,BX
POP BX
POP ES
POP AX
#
}
restdta()
{
#asm
PUSH DS
PUSH DX
PUSH AX
MOV DX,WORD dtads_
MOV DS,DX
MOV DX,WORD dtadx_
MOV AH,1AH
INT 21h
POP AX
POP DX
POP DS
#
}
char *dtap;
setdta(newdta)
char *newdta;
{
dtap = newdta;
#asm
PUSH DX
PUSH AX
MOV DX,WORD dtap_
MOV AH,1AH
INT 21h
POP AX
POP DX
#
}
char *dirfnp;
dirfst(s)
char *s;
{
dirfnp = s;
#asm
PUSH DX
PUSH CX
MOV DX,WORD dirfnp_
MOV AH,4eh
INT 21h
JC D56X1
MOV AX,0
D56X1: NOP
POP CX
POP DX
#
}
dirnxt()
{
#asm
MOV AH,4fh
INT 21h
JC D56X2
MOV AX,0
D56X2: NOP
#
}
/* ================================================================ */
/*
From Dr. Dobbs #108 October 1985. */
/* fixpath() processes a DOS pathname for two different uses.
The input path, *ip, is presumably a command operand like the
first operand of DIR. One output, the search path *sp, is
the input possibly augmented with "*.*" or "\*.*" so that it
will work reliably with DOS functions 4E/$f. The other output
is a lead-in path, *lip, which can be prefixed to the simple
filename.ext returned by fuctions 4E/4F to make a complete path
for opening or erasing a file.
The function returns 1 if it is successful, but 0 if the
input path can't be processed and should not be used.
Some input paths can be processed here yet be invalid or
useless. The search path produced from such an input wwill
cause an error return from function 4E (search first match). */
/* ================================================================ */
int fixpath(ip, sp, lip)
register char *ip, /* input path */
*sp, /* the search path */
*lip; /* the lead-in or prefix path */
{
char *cp; /* work pointer for scanning paths */
char attr; /* attribute for chgattr */
int ret, strlen(), strcpy(), strcmp(), strcat(), chgattr();
/* ================================================================ */
/* Pick off 4 special cases:
(1) the NULL string, which we take to mean "*.*"
(2) the simple "d:" reference to a drive, which we also take
to mean "d:*.*"
(3) the root-dir reference "d:\" which is mishandled by
function 0x4300 of both dDOS 2.1 and 3.0.
(4) any reference that ends in "\"
In all cases, the input path is ok as a lead-in, but it needs
the global qualifier *.* added for searching.
/* ================================================================ */
if ((strlen(ip) == 0) /* null string */
|| (strcmp(ip+1, ":") == 0) /* d: only */
|| (ip[strlen(ip)-1] == '\\')) /* ends in backslash */
{
strcpy(lip, ip); /* input is ok as prefix */
strcpy(sp, ip);
strcat(sp, "*.*"); /* add *.* for search */
return (1);
}
/* ================================================================ */
/* Ok, we have a non-null string not ending in \ and not a lone
drive letter. Four possibilities remain:
(1) an explicit file reference, b:\mydir\mydat
(2) an explicit directory reference, \mydir
(3) an ambiguous file reference, *.* or b:\mydir\*.dat
(4) an unknown name, a:noway or b:\mydir\nonesuch.dat
We can separate this with fair reliability using DOS function
0x4300, get attributes from path.
/* ================================================================ */
attr = 0x00; /* output area for get command */
ret = chgattr('G', ip, &attr); /* get attributes for this path */
if (ret == 0x0002)
/* the path is valid, in that all directories and drives
named in it are valid, but the final name is unknown. No
files will be found in a search, so quit now. */
return (0);
if ((ret == 0x0003)
|| ((ret == 0) && ((attr & 0x0010) == 0))) {
/* Error 3, path not found, could mean total junk or a
misspelt directory name, but most likely it just means
the path ends in an ambiguous name. If there's an error
the initial search (function 4Eh) will fail.
With no error and reg cx not showing the directory
attribute flags 0010, we have a normal, unambiguous file
pathname like "b:\mydir\mydat" or just "myprog.com".
In either case the search path is the whole input
path. The lead-in is that shorn of whatever follows the
rightmost \ or :, whichever comes last -- or just a null
string if there is no \ or :. */
strcpy(sp, ip); /* working copy of ip in sp */
cp = sp + strlen(sp) -1;
for (; cp > sp; --cp)
if (('\\' == *cp) || (':' == *cp))
break;
if (cp > sp)
++cp; /* retain colon or slash */
*cp = '\0'; /* make a null string */
strcpy(lip, sp);
strcpy(sp, ip); /* whole input for search */
return (1);
}
if ((ret == 0) && (attr & 0x0010)) {
/* No error and the directory attribute returned in cx
shows an unambiguous path that happpens to end in the
name of a directory, for instance "..\..\bin" or
"b:\mydir". Applying the rules of DIR or COPY, we
have to treat these as global refs to all files named in
the directory. The search path is the input with
"\*.*" tacked onto it. The lead-in path is just the
input plus a backslash. */
strcpy(sp, ip);
strcpy(lip, ip);
strcat(sp, "\\*.*");
strcat(lip, "\\");
return (1);
}
else {
/* unexpected events make me nervous, so give up */
return (0);
}
}
int rax, rcx;
char *rdx;
int chgattr(getset, path, attr)
char getset, *path, *attr;
{
if (getset == 'S') {
rcx = *attr;
rax = 0x4301;
}
else
rax = 0x4300;
rdx = path;
#asm
PUSH DX
PUSH CX
PUSH AX
MOV DX,WORD rdx_
MOV AX,WORD rax_
MOV CX,WORD rcx_
INT 21H
MOV WORD rax_,AX
JC D56X9
MOV WORD rax_,0
D56X9: NOP
MOV WORD rcx_,CX
POP AX
POP CX
POP DX
#
if (rax == 0)
*attr = rcx;
return(rax);
}
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/