/* 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 #include #include #include #include #include #include #include #include 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 ); }