windows-nt/Source/XPSP1/NT/sdktools/fcom/fcom.c
2020-09-26 16:20:57 +08:00

632 lines
19 KiB
C

/* fcom.c - file compare
*
* 4/30/86 daniel lipkie LineCompare, at end, if NOT Same then error
* 5/01/86 daniel lipkie SYM files treaded as binary
* if (quiet mode) then exit(1) on FIRST difference
* 27-May-1986 mz Make line arrays dynamically allocated at read
* time.
* Make default size be 300 lines.
* 05-Aug-1986 DANL MAX default line size 255
* 10-Feb-1987 mz Make /m imply /t
* 10-Jun-1987 danl fill -> fillbuf
* 09-Nov-1987 mz Fix 0D0D0A bug
* Fix different at line 2 bug
* 25-Nov-1987 mz All errors to stderr
* 30-Nov-1987 mz Resync fails dumps entire file
* 21-Jul-1988 bw Don't GP fault on empty test files
* 06-Aug-1990 davegi Changed Move to memmove (OS/2 2.0)
*
* Fcom compares two files in either a line-by-line mode or in a strict
* byte-by-byte mode.
*
* The byte-by-byte mode is simple; merely read both files and print the
* offsets where they differ and the contents.
*
* The line compare mode attempts to isolate differences in ranges of lines.
* Two buffers of lines are read and compared. No hashing of lines needs
* to be done; hashing only speedily tells you when things are different,
* not the same. Most files run through this are expected to be largely
* the same. Thus, hashing buys nothing.
*
* [0] Fill buffers
* [1] If both buffers are empty then
* [1.1] Done
* [2] Adjust buffers so 1st differing lines are at top.
* [3] If buffers are empty then
* [3.1] Goto [0]
*
* This is the difficult part. We assume that there is a sequence of inserts,
* deletes and replacements that will bring the buffers back into alignment.
*
* [4] xd = yd = FALSE
* [5] xc = yc = 1
* [6] xp = yp = 1
* [7] If buffer1[xc] and buffer2[yp] begin a "sync" range then
* [7.1] Output lines 1 through xc-1 in buffer 1
* [7.2] Output lines 1 through yp-1 in buffer 2
* [7.3] Adjust buffer 1 so line xc is at beginning
* [7.4] Adjust buffer 2 so line yp is at beginning
* [7.5] Goto [0]
* [8] If buffer1[xp] and buffer2[yc] begin a "sync" range then
* [8.1] Output lines 1 through xp-1 in buffer 1
* [8.2] Output lines 1 through yc-1 in buffer 2
* [8.3] Adjust buffer 1 so line xp is at beginning
* [8.4] Adjust buffer 2 so line yc is at beginning
* [8.5] Goto [0]
* [9] xp = xp + 1
* [10] if xp > xc then
* [10.1] xp = 1
* [10.2] xc = xc + 1
* [10.3] if xc > number of lines in buffer 1 then
* [10.4] xc = number of lines
* [10.5] xd = TRUE
* [11] if yp > yc then
* [11.1] yp = 1
* [11.2] yc = yc + 1
* [11.3] if yc > number of lines in buffer 2 then
* [11.4] yc = number of lines
* [11.5] yd = TRUE
* [12] if not xd or not yd then
* [12.1] goto [6]
*
* At this point there is no possible match between the buffers. For
* simplicity, we punt.
*
* [13] Display error message.
*
* Certain flags may be set to modify the behavior of the comparison:
*
* -a abbreviated output. Rather than displaying all of the modified
* ranges, just display the beginning, ... and the ending difference
* -b compare the files in binary (or byte-by-byte) mode. This mode is
* default on .EXE, .OBJ, .SYM, .LIB, .COM, .BIN, and .SYS files
* -c ignore case on compare (cmp = strcmpi instead of strcmp)
* -l compare files in line-by-line mode
* -lb n set the size of the internal line buffer to n lines from default
* of 300
* -m merge the input files into one for output. Use extention to
* indicate what kind of separators to use.
* -n output the line number also
* -NNNN set the number of lines to resynchronize to NNNN which defaults
* to 2
* -w ignore blank lines and white space (ignore len 0, use strcmps)
* -t do not untabify (use fgets instead of fgetl)
*/
#include <malloc.h>
#include <stdio.h>
#include <string.h>
#include <process.h>
#include <ctype.h>
#include <math.h>
#include <stdlib.h>
#include <windows.h>
#include <tools.h>
int ctSync = -1; /* number of lines required to sync */
int cLine = -1; /* number of lines in internal buffs */
flagType fAbbrev = FALSE; /* abbreviated output */
flagType fBinary = FALSE; /* binary comparison */
flagType fLine = FALSE; /* line comparison */
flagType fNumb = FALSE; /* display line numbers */
flagType fCase = TRUE; /* case is significant */
flagType fIgnore = FALSE; /* ignore spaces and blank lines */
flagType fQuiet = FALSE; /* TRUE => no messages output */
flagType fMerge = FALSE; /* TRUE => merge files onto stdout */
int debug;
#define D_COMP 0x0001
#define D_CCALL 0x0002
#define D_RESYNC 0x0004
#define D_CALL 0x0008
#define D_SYNC 0x0010
struct lineType {
int line; /* line number */
char *text; /* body of line */
};
struct lineType *buffer1, *buffer2;
/*
* Forward Function Declarations
*/
void usage( char *, int );
int fillbuf( struct lineType *, FILE *, int, int * );
flagType compare( int, int, int, register int, register int);
int BinaryCompare( char *, char * );
void pline(struct lineType *);
void dump(struct lineType *, int, int);
int adjust (struct lineType *, int, int);
void LineCompare (char *, char *);
char * (__cdecl *funcRead) (char *, int, FILE *);
/* function to use to read lines */
int (__cdecl *fCmp)(const char *, const char *);
/* function to use to compare lines */
char line[MAXLINELEN]; /* single line buffer */
char *extBin[] = { ".EXE", ".OBJ", ".SYM", ".LIB", ".COM", ".BIN",
".SYS", NULL };
void
usage (p, erc)
char *p;
int erc;
{
if (!fQuiet) {
if (p)
fprintf (stderr, "fcom: %s\n", p);
else
fprintf (stderr, "usage: fcom [/a] [/b] [/c] [/l] [/lb n] [/m] [/n] [/NNNN] [/w] [/t] file1 file2\n");
}
exit(erc);
}
/* return number of lines read in
*
* pl line buffer to fill
* fh handle to read
* ct max number to read
* plnum line number counter to use
*
* returns number of lines read
*/
int fillbuf( struct lineType *pl, FILE *fh, int ct, int *plnum )
{
char *line;
int i, l;
if ((line = malloc (MAXLINELEN)) == NULL)
usage ("out of memory", 2);
if (TESTFLAG (debug, D_CALL))
printf ("fillbuf (%p, %p, %d)\n", pl, fh, ct);
i = 0;
while (ct-- && (*funcRead) (line, MAXLINELEN, fh) != NULL) {
if (pl->text != NULL)
free (pl->text);
l = strlen (line);
if ((pl->text = malloc (l+1)) == NULL)
usage ("out of memory", 2);
// djg Move ((char far *)line, (char far *) (pl->text), l+1);
memmove ((char *) (pl->text), (char *)line, l+1);
if (funcRead == fgets)
pl->text[l-2] = 0;
if (fIgnore && !strcmps (pl->text, ""))
pl->text[0] = 0;
if (l != 0 || !fIgnore) {
pl->line = ++*plnum;
pl++;
i++;
}
}
if (TESTFLAG (debug, D_CALL))
printf ("fillbuf returns %d\n", i);
free (line);
return i;
}
/* compare a range of lines
*
* l1, l2 number of lines in each line buffer
* s1, s2 beginning location in each buffer to begin compare
* ct number of contiguous lines to compare
*/
flagType compare (l1, s1, l2, s2, ct)
int l1, l2, ct;
register int s1, s2;
{
if (TESTFLAG (debug, D_CCALL))
printf ("compare (%d, %d, %d, %d, %d)\n", l1, s1, l2, s2, ct);
if (ct <= 0 || s1+ct > l1 || s2+ct > l2)
return FALSE;
while (ct--) {
if (TESTFLAG (debug, D_COMP))
printf ("'%s' == '%s'? ", buffer1[s1].text, buffer2[s2].text);
if ((*fCmp)(buffer1[s1++].text, buffer2[s2++].text)) {
if (TESTFLAG (debug, D_CCALL))
printf ("No\n");
return FALSE;
}
}
if (TESTFLAG (debug, D_CCALL))
printf ("Yes\n");
return TRUE;
}
BinaryCompare (f1, f2)
char *f1, *f2;
{
register int c1, c2;
long pos;
FILE *fh1, *fh2;
flagType fSame;
fSame = TRUE;
if ((fh1 = fopen (f1, "rb")) == NULL) {
sprintf (line, "cannot open %s - %s", f1, error ());
usage (line, 2);
}
if ((fh2 = fopen (f2, "rb")) == NULL) {
sprintf (line, "cannot open %s - %s", f2, error ());
usage (line, 2);
}
pos = 0L;
while (TRUE) {
if ((c1 = getc (fh1)) != EOF)
if ((c2 = getc (fh2)) != EOF)
if (c1 == c2)
;
else {
fSame = FALSE;
if (fQuiet)
exit(1);
else
printf ("%08lx: %02x %02x\n", pos, c1, c2);
}
else {
sprintf (line, "%s longer than %s", f1, f2);
usage (line, 1);
}
else
if ((c2 = getc (fh2)) == EOF)
if (fSame)
usage ("no differences encountered", 0);
else
exit (1);
else {
sprintf (line, "%s longer than %s", f2, f1);
usage (line, 1);
}
pos++;
}
return( 0 );
}
/* print out a single line */
void pline (pl)
struct lineType *pl;
{
if (fNumb)
printf ("%5d: ", pl->line);
printf ("%s\n", pl->text);
}
/* output a range of lines */
void dump( struct lineType *pl, int start, int end )
{
if (fAbbrev && end-start > 2) {
pline (pl+start);
printf ("...\n");
pline (pl+end);
}
else
while (start <= end)
pline (pl+start++);
}
/* adjust returns number of lines in buffer
*
* pl line buffer to be adjusted
* ml number of line in line buffer
* lt number of lines to scroll
*/
adjust( struct lineType *pl, int ml, int lt)
{
int i;
if (TESTFLAG (debug, D_CALL))
printf ("adjust (%p, %d, %d) = ", pl, ml, lt);
if (TESTFLAG (debug, D_CALL))
printf ("%d\n", ml-lt);
if (ml <= lt)
return 0;
if (TESTFLAG (debug, D_CALL))
printf ("move (%p, %p, %04x)\n", &pl[lt], &pl[0], sizeof (*pl)*(ml-lt));
/* buffer has 0..lt-1 lt..ml
* we free 0..lt-1
*/
for (i = 0; i < lt; i++)
if (pl[i].text != NULL)
free (pl[i].text);
/* buffer is 0..0 lt..ml
* compact to lt..ml ???
*/
// djg Move ((char far *)&pl[lt], (char far *)&pl[0], sizeof (*pl)*(ml-lt));
memmove ((char *)&pl[0], (char *)&pl[lt], sizeof (*pl)*(ml-lt));
/* buffer is lt..ml ??
* fill to be lt..ml 0..0
*/
for (i = ml-lt; i < ml; i++)
pl[i].text = NULL;
return ml-lt;
}
void LineCompare (f1, f2)
char *f1, *f2;
{
FILE *fh1, *fh2;
int l1, l2, i, xp, yp, xc, yc;
flagType xd, yd, fSame, fFirstLineDifferent;
int line1, line2;
fFirstLineDifferent = TRUE;
fSame = TRUE;
if ((fh1 = fopen (f1, "rb")) == NULL) {
sprintf (line, "cannot open %s - %s", f1, error ());
usage (line, 2);
}
if ((fh2 = fopen (f2, "rb")) == NULL) {
sprintf (line, "cannot open %s - %s", f2, error ());
usage (line, 2);
}
if ((buffer1 = (struct lineType *)calloc (cLine, sizeof *buffer1)) == NULL ||
(buffer2 = (struct lineType *)calloc (cLine, sizeof *buffer1)) == NULL)
usage ("out of memory\n", 2);
l1 = l2 = 0;
line1 = line2 = 0;
l0: if (TESTFLAG (debug, D_SYNC))
printf ("At scan beginning\n");
if (fQuiet && !fSame)
exit(1);
l1 += fillbuf (buffer1+l1, fh1, cLine-l1, &line1);
l2 += fillbuf (buffer2+l2, fh2, cLine-l2, &line2);
if (l1 == 0 && l2 == 0) {
if (fSame)
usage ("no differences encountered", 0);
else
usage ("differences encountered", 1);
}
/* find first line that differs in buffer
*/
xc = min (l1, l2);
for (i=0; i < xc; i++)
if (!compare (l1, i, l2, i, 1))
break;
if (fMerge)
dump (buffer2, 0, i-1);
/* If we are different at a place other than at the top then we know
* that there will be a matching line at the head of the buffer
*/
if (i != 0)
fFirstLineDifferent = FALSE;
/* if we found one at all, then adjust all buffers so last matching
* line is at top. Note that if we are doing this for the first buffers
* worth in the file then the top lines WON'T MATCH
*/
if (i != xc)
i = max (i-1, 0);
l1 = adjust (buffer1, l1, i);
l2 = adjust (buffer2, l2, i);
/* if we've matched the entire buffers-worth then go back and fill some
* more
*/
if (l1 == 0 && l2 == 0) {
fFirstLineDifferent = FALSE;
goto l0;
}
/* Fill up the buffers as much as possible; the next match may occur
* AFTER the current set of buffers
*/
l1 += fillbuf (buffer1+l1, fh1, cLine-l1, &line1);
l2 += fillbuf (buffer2+l2, fh2, cLine-l2, &line2);
if (TESTFLAG (debug, D_SYNC))
printf ("buffers are adjusted, %d, %d remain\n", l1, l2);
xd = yd = FALSE;
xc = yc = 1;
xp = yp = 1;
/* begin trying to match the buffers
*/
l6: if (TESTFLAG (debug, D_RESYNC))
printf ("Trying resync %d,%d %d,%d\n", xc, xp, yc, yp);
i = min (l1-xc,l2-yp);
i = min (i, ctSync);
if (compare (l1, xc, l2, yp, i)) {
fSame = FALSE;
if (fMerge) {
printf ("#if OLDVERSION\n");
dump (buffer1, fFirstLineDifferent ? 0 : 1, xc-1);
printf ("#else\n");
dump (buffer2, fFirstLineDifferent ? 0 : 1, yp-1);
printf ("#endif\n");
}
else
if (!fQuiet) {
printf ("***** %s\n", f1);
dump (buffer1, 0, xc);
printf ("***** %s\n", f2);
dump (buffer2, 0, yp);
printf ("*****\n\n");
}
if (TESTFLAG (debug, D_SYNC))
printf ("Sync at %d,%d\n", xc, yp);
l1 = adjust (buffer1, l1, xc);
l2 = adjust (buffer2, l2, yp);
fFirstLineDifferent = FALSE;
goto l0;
}
i = min (l1-xp, l2-yc);
i = min (i, ctSync);
if (compare (l1, xp, l2, yc, i)) {
fSame = FALSE;
if (fMerge) {
printf ("#if OLDVERSION\n");
dump (buffer1, fFirstLineDifferent ? 0 : 1, xp-1);
printf ("#else\n");
dump (buffer2, fFirstLineDifferent ? 0 : 1, yc-1);
printf ("#endif\n");
}
else
if (!fQuiet) {
printf ("***** %s\n", f1);
dump (buffer1, 0, xp);
printf ("***** %s\n", f2);
dump (buffer2, 0, yc);
printf ("*****\n\n");
}
if (TESTFLAG (debug, D_SYNC))
printf ("Sync at %d,%d\n", xp, yc);
l1 = adjust (buffer1, l1, xp);
l2 = adjust (buffer2, l2, yc);
fFirstLineDifferent = FALSE;
goto l0;
}
if (++xp > xc) {
xp = 1;
if (++xc >= l1) {
xc = l1;
xd = TRUE;
}
}
if (++yp > yc) {
yp = 1;
if (++yc >= l2) {
yc = l1;
yd = TRUE;
}
}
if (!xd || !yd)
goto l6;
fSame = FALSE;
if (fMerge) {
if (l1 >= cLine || l2 >= cLine)
fprintf (stderr, "resync failed. Files are too different\n");
printf ("#if OLDVERSION\n");
do {
dump (buffer1, 0, l1-1);
l1 = adjust (buffer1, l1, l1);
} while (l1 += fillbuf (buffer1+l1, fh1, cLine-l1, &line1));
printf ("#else\n");
do {
dump (buffer2, 0, l2-1);
l2 = adjust (buffer2, l2, l2);
} while (l2 += fillbuf (buffer2+l2, fh2, cLine-l2, &line2));
printf ("#endif\n");
}
else
if (!fQuiet) {
if (l1 >= cLine || l2 >= cLine)
fprintf (stderr, "resync failed. Files are too different\n");
printf ("***** %s\n", f1);
do {
dump (buffer1, 0, l1-1);
l1 = adjust (buffer1, l1, l1);
} while (l1 += fillbuf (buffer1+l1, fh1, cLine-l1, &line1));
printf ("***** %s\n", f2);
do {
dump (buffer2, 0, l2-1);
l2 = adjust (buffer2, l2, l2);
} while (l2 += fillbuf (buffer2+l2, fh2, cLine-l2, &line2));
printf ("*****\n\n");
}
exit (1);
}
__cdecl main (c, v)
int c;
char *v[];
{
int i;
funcRead = fgetl;
ConvertAppToOem( c, v );
if (c == 1)
usage (NULL, 2);
SHIFT(c,v);
while (fSwitChr (**v)) {
if (!strcmp (*v+1, "a"))
fAbbrev = TRUE;
else
if (!strcmp (*v+1, "b"))
fBinary = TRUE;
else
if (!strcmp (*v+1, "c"))
fCase = FALSE;
else
if (!strncmp (*v+1, "d", 1))
debug = atoi (*v+2);
else
if (!strcmp (*v+1, "l"))
fLine = TRUE;
else
if (!strcmp (*v+1, "lb")) {
SHIFT(c,v);
cLine = ntoi (*v, 10);
}
else
if (!strcmp (*v+1, "m")) {
fMerge = TRUE;
funcRead = fgets;
}
else
if (!strcmp (*v+1, "n"))
fNumb = TRUE;
else
if (!strcmp (*v+1, "q"))
fQuiet = TRUE;
else
if (*strbskip (*v+1, "0123456789") == '\0')
ctSync = ntoi (*v+1, 10);
else
if (!strcmp (*v+1, "t"))
funcRead = fgets;
else
if (!strcmp (*v+1, "w"))
fIgnore = TRUE;
else
usage (NULL, 2);
SHIFT(c,v);
}
if (c != 2)
usage (NULL, 2);
if (ctSync != -1)
fLine = TRUE;
else
ctSync = 2;
if (cLine == -1)
cLine = 300;
if (!fBinary && !fLine) {
extention (v[0], line);
for (i = 0; extBin[i]; i++)
if (!_strcmpi (extBin[i], line))
fBinary = TRUE;
if (!fBinary)
fLine = TRUE;
}
if (fBinary && (fLine || fNumb))
usage ("incompatable switches", 2);
if (fIgnore)
if (fCase)
fCmp = strcmps;
else
fCmp = strcmpis;
else
if (fCase)
fCmp = strcmp;
else
fCmp = _strcmpi;
if (fBinary)
BinaryCompare (v[0], v[1]);
else
LineCompare (v[0], v[1]);
return( 0 );
}