windows-nt/Source/XPSP1/NT/base/fs/utils/fc/fc.cxx

1393 lines
38 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1990-2000 Microsoft Corporation
Module Name:
FC
Abstract:
FC is a DOS-5 compatible file comparison utility
Author:
Ramon Juan San Andres (ramonsa) 01-May-1991
Notes:
This FC is a port of the DOS5 FC code. It has been slightly modified
to use some of the ULIB functionality (e.g. argument parsing), however
it does not make full use of the ULIB functionality (e.g. it uses
stdio.h functions for file handling).
Revision History:
--*/
/****************************************************************************
File Compare
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.
***********************************************************************
The algorithm that immediately follows does not work. There is an error
somewhere in the range of lines 11 on. An alternative explanation follows.
KGS
************************************************************************
[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.
EXPLANATION 2
This is a variation of the Largest Common Subsequence problem. A
detailed explanation of this can be found on p 189 of Data Structures
and Algorithms by Aho Hopcroft and Ulman.
FC maintains two buffers within which it tries to find the Largest Common
Subsequence (The largest common subsequence is simply the pattern in
buffer1 that yields the most matches with the pattern in buffer2, or the
pattern in buffer2 that yields the most matches with the pattern in buffer1)
FC makes a simplifying assumption that the contents of one buffer can be
converted to the contents of the other buffer by deleting the lines that are
different between the two buffers.
Two indices into each buffer are maintained:
xc, yc == point to the last line that has been scanned up to now
xp, yp == point to the first line that has not been exhaustively
compared to lines 0 - #c in the other buffer.
FC now makes a second simplifying assumption:
It is unnecessary to do any calculations on lines that are equal.
Hence FC scans File1 and File two line by line until a difference is
encountered.
When a difference is encountered the two buffers are filled such that
the line containing the first difference heads the buffer. The following
exhaustive search algorithm is applied to find the first "sync" occurance.
(The below is simplified to use == for comparison. In practice more than
one line needs to match for a "sync" to be established).
FOR xc,yc = 1; xc,yx <= sizeof( BUFFERS ); xc++, yc++
FOR xp,yp = 1; xp,yp <= xc,yc; xp++, yp++
IF ( BUFFER1[xp] == BUFFER2[yc] )
Then the range of lines BUFFER1[ 1 ... xp ] and
BUFFER2[ 1 ... yc ] need to be deleted for the
two files to be equal. Therefore DISPLAY these
ranges, and begin scanning both files starting at
the matching lines.
FI
IF ( BUFFER1[yp] == BUFFER2[xc] )
Then the range of lines BUFFER2[ 1 ... yp ] and
BUFFER1[ 1 ... xc ] need to be deleted for the
two files to be equal. Therefore DISPLAY these
ranges, and begin scanning both files starting at
the matching lines.
FI
FOREND
FOREND
If a match is not found within the buffers, the message "RESYNC FAILED"
is issued and further comparison is aborted since there is no valid way
to find further matching lines.
END EXPLANATION 2
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, .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 100
-u Files to be compared are UNICODE text files
-w ignore blank lines and white space (ignore len 0, use strcmps)
-t do not untabify (use fgets instead of fgetl)
-n output the line number also
-NNNN set the number of lines to resynchronize to n which defaults
to 2. Failure to have this value set correctly can result in
odd output:
file1: file2:
abcdefg abcdefg
aaaaaaa aaaaaab
aaaaaaa aaaaaaa
aaaaaaa aaaaaaa
abcdefg abcdefg
with default sync of 2 yields: with sync => 3 yields:
*****f1 *****f1
abcdefg abcdefg
aaaaaaa aaaaaaa
*****f2 aaaaaaa
abcdefg *****f2
aaaaaab abcdefg
aaaaaaa aaaaaab
aaaaaaa
*****f1
aaaaaaa
aaaaaaa
abcdefg
*****f2
aaaaaaa
abcdefg
WARNING:
This program makes use of GOTO's and hence is not as straightforward
as it could be! CAVEAT PROGRAMMER.
****************************************************************************/
#include "ulib.hxx"
#include "fc.hxx"
#include "arg.hxx"
#include "array.hxx"
#include "arrayit.hxx"
#include "bytestrm.hxx"
#include "dir.hxx"
#include "file.hxx"
#include "filestrm.hxx"
#include "filter.hxx"
#include "mbstr.hxx"
#include "system.hxx"
#include "wstring.hxx"
#include <malloc.h>
#include <process.h>
#include <stdlib.h>
#include <math.h>
#include <locale.h>
/**************************************************************************/
/* main */
/**************************************************************************/
INT __cdecl
main (
)
{
DEFINE_CLASS_DESCRIPTOR( FC );
{
FC Fc;
if ( Fc.Initialize() ) {
return Fc.Fcmain();
}
}
return FAILURE;
}
CHAR *ExtBin[] = { "EXE", "OBJ", "LIB",
"COM", "BIN", "SYS", NULL };
DEFINE_CONSTRUCTOR( FC, PROGRAM );
FC::~FC () {
}
BOOLEAN FC::Initialize() {
if ( PROGRAM::Initialize() ) {
ValidateVersion();
ctSync = -1; // number of lines required to sync
cLine = -1; // number of lines in internal buffs
fAbbrev = FALSE; // abbreviated output
fBinary = FALSE; // binary comparison
fLine = FALSE; // line comparison
fNumb = FALSE; // display line numbers
fCase = TRUE; // case is significant
fIgnore = FALSE; // ignore spaces and blank lines
fSkipOffline = TRUE; // skip offline files
fOfflineSkipped = FALSE; // no files are skipped
#ifdef DEBUG
fDebug = FALSE;
#endif
fExpandTabs = TRUE;
// funcRead = (int (*)(char *,int,FILE *))fgetl;
extBin = (CHAR **)ExtBin;
return ParseArguments();
}
return FALSE;
}
BOOLEAN
FC::ParseArguments(
)
{
ARGUMENT_LEXEMIZER ArgLex;
ARRAY LexArray;
ARRAY ArrayOfArg;
PATH_ARGUMENT ProgramName;
FLAG_ARGUMENT FlagAbbreviate;
FLAG_ARGUMENT FlagAsciiCompare;
FLAG_ARGUMENT FlagBinaryCompare;
FLAG_ARGUMENT FlagCaseInsensitive;
FLAG_ARGUMENT FlagCompression;
FLAG_ARGUMENT FlagExpansion;
FLAG_ARGUMENT FlagLineNumber;
FLAG_ARGUMENT FlagRequestHelp;
FLAG_ARGUMENT FlagUnicode;
FLAG_ARGUMENT FlagIncludeOffline;
FLAG_ARGUMENT FlagIncludeOffline2;
LONG_ARGUMENT LongBufferSize;
#ifdef DEBUG
FLAG_ARGUMENT FlagDebug;
#endif
STRING_ARGUMENT LongMatch;
PATH_ARGUMENT InFile1;
PATH_ARGUMENT InFile2;
LONG Long;
INT i;
if( !LexArray.Initialize() ) {
DebugAbort( "LexArray.Initialize() Failed!\n" );
return( FALSE );
}
if( !ArgLex.Initialize(&LexArray) ) {
DebugAbort( "ArgLex.Initialize() Failed!\n" );
return( FALSE );
}
// Allow only the '/' as a valid switch
ArgLex.PutSwitches("/");
ArgLex.SetCaseSensitive( FALSE );
ArgLex.PutStartQuotes("\"");
ArgLex.PutEndQuotes("\"");
ArgLex.PutSeparators(" \t");
if( !ArgLex.PrepareToParse() ) {
DebugAbort( "ArgLex.PrepareToParse() Failed!\n" );
return( FALSE );
}
if( !ProgramName.Initialize("*") ||
!FlagAbbreviate.Initialize("/A") ||
!FlagAsciiCompare.Initialize("/L") ||
!FlagBinaryCompare.Initialize("/B") ||
!FlagCaseInsensitive.Initialize("/C") ||
!FlagCompression.Initialize("/W") ||
!FlagExpansion.Initialize("/T") ||
!FlagLineNumber.Initialize("/N") ||
!FlagRequestHelp.Initialize("/?") ||
!FlagUnicode.Initialize("/U") ||
!FlagIncludeOffline.Initialize("/OFFLINE") ||
!FlagIncludeOffline2.Initialize("/OFF") ||
#ifdef DEBUG
!FlagDebug.Initialize("/D") ||
#endif
!LongBufferSize.Initialize("/LB#") ||
!LongMatch.Initialize("/*") ||
!InFile1.Initialize("*") ||
!InFile2.Initialize("*") ) {
DebugAbort( "Unable to Initialize some or all of the Arguments!\n" );
return( FALSE );
}
if( !ArrayOfArg.Initialize() ) {
DebugAbort( "ArrayOfArg.Initialize() Failed\n" );
return( FALSE );
}
if( !ArrayOfArg.Put(&ProgramName) ||
!ArrayOfArg.Put(&FlagAbbreviate) ||
!ArrayOfArg.Put(&FlagAsciiCompare) ||
!ArrayOfArg.Put(&FlagBinaryCompare) ||
!ArrayOfArg.Put(&FlagCaseInsensitive) ||
!ArrayOfArg.Put(&FlagCompression) ||
!ArrayOfArg.Put(&FlagExpansion) ||
!ArrayOfArg.Put(&FlagLineNumber) ||
!ArrayOfArg.Put(&FlagRequestHelp) ||
!ArrayOfArg.Put(&FlagUnicode) ||
!ArrayOfArg.Put(&FlagIncludeOffline) ||
!ArrayOfArg.Put(&FlagIncludeOffline2) ||
#ifdef DEBUG
!ArrayOfArg.Put(&FlagDebug) ||
#endif
!ArrayOfArg.Put(&LongBufferSize) ||
!ArrayOfArg.Put(&LongMatch) ||
!ArrayOfArg.Put(&InFile1) ||
!ArrayOfArg.Put(&InFile2) ) {
DebugAbort( "ArrayOfArg.Put() Failed!\n" );
return( FALSE );
}
if( !( ArgLex.DoParsing( &ArrayOfArg ) ) ) {
// For each incorrect command line parameter, FC displays the
// following message:
//
// FC: Invalid Switch
//
// It does *not* die if a parameter is unrecognized...(Dos does...)
//
DisplayMessage( MSG_FC_INVALID_SWITCH, ERROR_MESSAGE, "" );
// return( FALSE );
}
//
// It should now be safe to test the arguments for their values...
//
if( FlagRequestHelp.QueryFlag() ) {
DisplayMessage( MSG_FC_HELP_MESSAGE, NORMAL_MESSAGE, "" );
return( FALSE );
}
if( FlagBinaryCompare.QueryFlag() &&
( FlagAsciiCompare.QueryFlag() || FlagLineNumber.QueryFlag() ) ) {
DisplayMessage( MSG_FC_INCOMPATIBLE_SWITCHES, ERROR_MESSAGE, "" );
return( FALSE );
}
if( !InFile1.IsValueSet() ||
!InFile2.IsValueSet() ) {
DisplayMessage( MSG_FC_INSUFFICIENT_FILES, ERROR_MESSAGE, "" );
return( FALSE );
}
//
// Convert filenames to upper case
//
_File1.Initialize( InFile1.GetPath() );
_File2.Initialize( InFile2.GetPath() );
((PWSTRING)_File1.GetPathString())->Strupr();
((PWSTRING)_File2.GetPathString())->Strupr();
fUnicode = FlagUnicode.QueryFlag();
fAbbrev = FlagAbbreviate.QueryFlag();
fCase = !FlagCaseInsensitive.QueryFlag();
fIgnore = FlagCompression.QueryFlag();
fNumb = FlagLineNumber.QueryFlag();
fBinary = FlagBinaryCompare.QueryFlag();
fSkipOffline = ( !FlagIncludeOffline.QueryFlag() ) &&
( !FlagIncludeOffline2.QueryFlag() );
if ( FlagExpansion.QueryFlag() ) {
fExpandTabs = FALSE;
//funcRead = (int (*)(char *,int,FILE *))fgets;
}
#ifdef DEBUG
fDebug = FlagDebug.QueryFlag();
#endif
if ( LongBufferSize.IsValueSet() ) {
cLine = (INT)LongBufferSize.QueryLong();
fLine = TRUE;
} else {
cLine = 100;
}
if ( LongMatch.IsValueSet() ) {
if ( LongMatch.GetString()->QueryNumber( &Long ) ) {
ctSync = (INT)Long;
fLine = TRUE;
} else {
DisplayMessage( MSG_FC_INVALID_SWITCH, ERROR_MESSAGE, "" );
ctSync = 2;
}
} else {
ctSync = 2;
}
if (!fBinary && !fLine) {
DSTRING ExtBin;
PWSTRING Ext = _File1.QueryExt();
if ( Ext ) {
for (i=0; extBin[i]; i++) {
ExtBin.Initialize( extBin[i] );
if ( !ExtBin.Stricmp( Ext ) ) {
fBinary = TRUE;
break;
}
}
DELETE( Ext );
}
if (!fBinary) {
fLine = TRUE;
}
}
if (!fUnicode) {
if (fIgnore) {
if (fCase) {
fCmp = MBSTR::Strcmps;
} else {
fCmp = MBSTR::Strcmpis;
}
} else {
if (fCase) {
fCmp = MBSTR::Strcmp;
} else {
fCmp = MBSTR::Stricmp;
}
}
} else {
if (fIgnore) {
if (fCase) {
fCmp_U = WSTRING::Strcmps;
} else {
fCmp_U = WSTRING::Strcmpis;
}
} else {
if (fCase) {
fCmp_U = WSTRING::Strcmp;
} else {
fCmp_U = WSTRING::Stricmp;
}
}
}
return( TRUE );
}
INT
FC::Fcmain
(
)
{
return ParseFileNames();
}
/**************************************************************************/
/* BinaryCompare */
/**************************************************************************/
int
FC::BinaryCompare (
PCWSTRING f1,
PCWSTRING f2
)
{
PATH FileName;
PFSN_FILE File1 = NULL;
PFSN_FILE File2 = NULL;
PFILE_STREAM Stream1 = NULL;
PFILE_STREAM Stream2 = NULL;;
BYTE_STREAM Bs1;
BYTE_STREAM Bs2;
BYTE c1, c2;
ULONG64 pos;
BOOLEAN fSame;
char buffer[128];
BOOLEAN fFileSkipped;
if ( !FileName.Initialize( f1 ) ||
!(File1 = SYSTEM::QueryFile( &FileName, fSkipOffline, &fFileSkipped )) ||
!(Stream1 = File1->QueryStream( READ_ACCESS, FILE_FLAG_OPEN_NO_RECALL )) ||
!Bs1.Initialize( Stream1 )
) {
DELETE( Stream2 );
DELETE( File2 );
DELETE( Stream1 );
DELETE( File1 );
if (fFileSkipped) {
// Skipping offline files is not an error, just track this happened
fOfflineSkipped = TRUE;
return FILES_OFFLINE;
} else {
DisplayMessage( MSG_FC_UNABLE_TO_OPEN, ERROR_MESSAGE, "%W", f1 );
return FILES_NOT_FOUND;
}
}
if ( !FileName.Initialize( f2 ) ||
!(File2 = SYSTEM::QueryFile( &FileName, fSkipOffline, &fFileSkipped )) ||
!(Stream2 = File2->QueryStream( READ_ACCESS, FILE_FLAG_OPEN_NO_RECALL )) ||
!Bs2.Initialize( Stream2 )
) {
DELETE( Stream2 );
DELETE( File2 );
DELETE( Stream1 );
DELETE( File1 );
if (fFileSkipped) {
// Skipping offline files is not an error, just track this happened
fOfflineSkipped = TRUE;
return FILES_OFFLINE;
} else {
DisplayMessage( MSG_FC_UNABLE_TO_OPEN, ERROR_MESSAGE, "%W", f2 );
return FILES_NOT_FOUND;
}
}
fSame = TRUE;
pos = 0;
while ( TRUE ) {
if ( Bs1.ReadByte( &c1 ) ) {
if ( Bs2.ReadByte( &c2 ) ) {
if (c1 != c2) {
if (pos > MAXULONG) {
sprintf( buffer, "%016I64X: %02X %02X", pos, c1, c2 );
} else {
sprintf( buffer, "%08I64X: %02X %02X", pos, c1, c2 );
}
DisplayMessage( MSG_FC_DATA, NORMAL_MESSAGE, "%s", buffer );
fSame = FALSE;
}
} else {
DisplayMessage( MSG_FC_FILES_DIFFERENT_LENGTH, NORMAL_MESSAGE, "%W%W", f1, f2 );
fSame = FALSE;
break;
}
} else {
if ( Bs2.ReadByte( &c2 ) ) {
DisplayMessage( MSG_FC_FILES_DIFFERENT_LENGTH, NORMAL_MESSAGE, "%W%W", f2, f1 );
fSame = FALSE;
break;
} else {
if (fSame) {
DisplayMessage( MSG_FC_NO_DIFFERENCES, NORMAL_MESSAGE );
}
break;
}
}
pos++;
}
DELETE( Stream2 );
DELETE( File2 );
DELETE( Stream1 );
DELETE( File1 );
return fSame ? SUCCESS : FILES_ARE_DIFFERENT;
}
/**************************************************************************/
/* Compare a range of lines. */
/**************************************************************************/
BOOLEAN FC::compare (int l1, register int s1, int l2, register int s2, int ct)
{
#ifdef DEBUG
if (fDebug)
DebugPrintTrace(("compare (%d, %d, %d, %d, %d)\n", l1, s1, l2, s2, ct));
#endif
if (ct <= 0 || s1+ct > l1 || s2+ct > l2)
return (FALSE);
while (ct--)
{
#ifdef DEBUG
if (fDebug)
DebugPrintTrace(("'%s' == '%s'? ", buffer1[s1].text, buffer2[s2].text));
#endif
if(!fUnicode) {
if ((*fCmp)(buffer1[s1++].text, buffer2[s2++].text)) {
#ifdef DEBUG
if (fDebug)
DebugPrintTrace(("No\n"));
#endif
return (FALSE);
}
} else {
if ((*fCmp_U)(buffer1[s1++].wtext, buffer2[s2++].wtext)) {
#ifdef DEBUG
if (fDebug)
DebugPrintTrace(("No\n"));
#endif
return (FALSE);
}
}
}
#ifdef DEBUG
if (fDebug)
DebugPrintTrace(("Yes\n"));
#endif
return (TRUE);
}
/**************************************************************************/
/* LineCompare */
/**************************************************************************/
INT
FC::LineCompare(
PCWSTRING f1,
PCWSTRING f2
)
{
PATH FileName;
PFSN_FILE File1 = NULL;
PFSN_FILE File2 = NULL;
PFILE_STREAM Stream1 = NULL;
PFILE_STREAM Stream2 = NULL;
int result;
BOOLEAN fFileSkipped;
buffer1 = buffer2 = NULL;
if ( !FileName.Initialize( f1 ) ||
!(File1 = SYSTEM::QueryFile( &FileName, fSkipOffline, &fFileSkipped )) ||
!(Stream1 = File1->QueryStream( READ_ACCESS, FILE_FLAG_OPEN_NO_RECALL ))
) {
FREE(buffer1);
FREE(buffer2);
DELETE(Stream2);
DELETE(File2);
DELETE(Stream1);
DELETE(File1);
if (fFileSkipped) {
// Skipping offline files is not an error, just track this happened
fOfflineSkipped = TRUE;
return FILES_OFFLINE;
} else {
DisplayMessage( MSG_FC_UNABLE_TO_OPEN, ERROR_MESSAGE, "%W", f1 );
return FILES_NOT_FOUND;
}
}
if ( !FileName.Initialize( f2 ) ||
!(File2 = SYSTEM::QueryFile( &FileName, fSkipOffline, &fFileSkipped )) ||
!(Stream2 = File2->QueryStream( READ_ACCESS, FILE_FLAG_OPEN_NO_RECALL ))
) {
FREE(buffer1);
FREE(buffer2);
DELETE(Stream2);
DELETE(File2);
DELETE(Stream1);
DELETE(File1);
if (fFileSkipped) {
// Skipping offline files is not an error, just track this happened
fOfflineSkipped = TRUE;
return FILES_OFFLINE;
} else {
DisplayMessage( MSG_FC_UNABLE_TO_OPEN, ERROR_MESSAGE, "%W", f2 );
return FILES_NOT_FOUND;
}
}
if ( (buffer1 = (struct lineType *)MALLOC(cLine * (sizeof *buffer1))) == NULL ||
(buffer2 = (struct lineType *)MALLOC(cLine * (sizeof *buffer1))) == NULL) {
DisplayMessage( MSG_FC_OUT_OF_MEMORY, ERROR_MESSAGE );
FREE(buffer1);
FREE(buffer2);
DELETE(Stream2);
DELETE(File2);
DELETE(Stream1);
DELETE(File1);
return FAILURE;
}
result = RealLineCompare( f1, f2, Stream1, Stream2 );
FREE(buffer1);
FREE(buffer2);
DELETE(Stream2);
DELETE(File2);
DELETE(Stream1);
DELETE(File1);
return result;
}
int
FC::RealLineCompare (
PCWSTRING f1,
PCWSTRING f2,
PSTREAM Stream1,
PSTREAM Stream2
)
{
int l1, l2, i, xp, yp, xc, yc;
BOOLEAN xd, yd, fSame;
int line1, line2;
fSame = TRUE;
l1 = l2 = 0;
line1 = line2 = 0;
l0:
#ifdef DEBUG
if (fDebug) {
DebugPrintTrace(("At scan beginning\n"));
}
#endif
l1 += xfill (buffer1+l1, Stream1, cLine-l1, &line1);
l2 += xfill (buffer2+l2, Stream2, cLine-l2, &line2);
if (l1 == 0 && l2 == 0) {
if (fSame) {
DisplayMessage( MSG_FC_NO_DIFFERENCES, NORMAL_MESSAGE );
}
return fSame ? SUCCESS : FILES_ARE_DIFFERENT;
}
xc = min (l1, l2);
for (i=0; i < xc; i++) {
if (!compare (l1, i, l2, i, 1)) {
break;
}
}
if (i != xc) {
i = ( i-1 > 0 )? ( i-1 ) : 0;
}
l1 = adjust (buffer1, l1, i);
l2 = adjust (buffer2, l2, i);
if (l1 == 0 && l2 == 0) {
goto l0;
}
l1 += xfill (buffer1+l1, Stream1, cLine-l1, &line1);
l2 += xfill (buffer2+l2, Stream2, cLine-l2, &line2);
#ifdef DEBUG
if (fDebug) {
DebugPrintTrace(("buffers are adjusted, %d, %d remain\n", l1, l2));
}
#endif
xd = yd = FALSE;
xc = yc = 1;
xp = yp = 1;
l6:
#ifdef DEBUG
if (fDebug) {
DebugPrintTrace(("Trying resync %d,%d %d,%d\n", xc, xp, yc, yp));
}
#endif
i = min (l1-xc,l2-yp);
i = min (i, ctSync);
if (compare (l1, xc, l2, yp, i)) {
fSame = FALSE;
DisplayMessage( MSG_FC_OUTPUT_FILENAME, NORMAL_MESSAGE, "%W", f1 );
dump (buffer1, 0, xc);
DisplayMessage( MSG_FC_OUTPUT_FILENAME, NORMAL_MESSAGE, "%W", f2 );
dump (buffer2, 0, yp);
DisplayMessage( MSG_FC_DUMP_END, NORMAL_MESSAGE );
l1 = adjust (buffer1, l1, xc);
l2 = adjust (buffer2, l2, yp);
goto l0;
}
i = min (l1-xp, l2-yc);
i = min (i, ctSync);
if (compare (l1, xp, l2, yc, i)) {
fSame = FALSE;
DisplayMessage( MSG_FC_OUTPUT_FILENAME, NORMAL_MESSAGE, "%W", f1 );
dump (buffer1, 0, xp);
DisplayMessage( MSG_FC_OUTPUT_FILENAME, NORMAL_MESSAGE, "%W", f2 );
dump (buffer2, 0, yc);
DisplayMessage( MSG_FC_DUMP_END, NORMAL_MESSAGE );
l1 = adjust (buffer1, l1, xp);
l2 = adjust (buffer2, l2, yc);
goto l0;
}
if (++xp > xc) {
xp = 1;
if (++xc >= l1) {
xc = l1;
xd = TRUE;
}
}
if (++yp > yc) {
yp = 1;
if (++yc >= l2) {
yc = l2;
yd = TRUE;
}
}
if (!xd || !yd) {
goto l6;
}
fSame = FALSE;
if (l1 >= cLine || l2 >= cLine) {
DisplayMessage( MSG_FC_RESYNC_FAILED, NORMAL_MESSAGE );
}
DisplayMessage( MSG_FC_OUTPUT_FILENAME, NORMAL_MESSAGE, "%W", f1 );
dump (buffer1, 0, l1-1);
DisplayMessage( MSG_FC_OUTPUT_FILENAME, NORMAL_MESSAGE, "%W", f2 );
dump (buffer2, 0, l2-1);
DisplayMessage( MSG_FC_DUMP_END, NORMAL_MESSAGE );
return fSame ? SUCCESS : FILES_ARE_DIFFERENT;
}
/**************************************************************************/
/* Return number of lines read in. */
/**************************************************************************/
FC::xfill (struct lineType *pl, PSTREAM Stream, int ct, int *plnum)
{
int i;
DWORD StrSize;
#ifdef DEBUG
if (fDebug)
DebugPrintTrace(("xfill (%04x, %04x)\n", pl, fh));
#endif
i = 0;
if (!fUnicode) {
while ( ct-- && !Stream->IsAtEnd() && Stream->ReadMbLine( pl->text, MAXLINESIZE, &StrSize, fExpandTabs, 8 ) ) {
if (fIgnore && !MBSTR::Strcmps(pl->text, "")) {
pl->text[0] = 0;
++*plnum;
}
if (strlen (pl->text) != 0 || !fIgnore)
{
pl->line = ++*plnum;
pl++;
i++;
}
}
} else {
while( ct-- && !Stream->IsAtEnd() && Stream->ReadWLine( pl->wtext,MAXLINESIZE, &StrSize, fExpandTabs, 8 ) ) {
//while( ct-- && !Stream->IsAtEnd() && Stream->ReadLine( &_String , TRUE )) {
//_String.QueryWSTR(0,TO_END,pl->wtext,MAXLINESIZE,TRUE);
if (fIgnore && !WSTRING::Strcmps((PWSTR)pl->wtext, (PWSTR)L"")) {
pl->wtext[0] = 0;
++*plnum;
}
if (wcslen (pl->wtext) != 0 || !fIgnore)
{
pl->line = ++*plnum;
pl++;
i++;
}
}
}
#ifdef DEBUG
if (fDebug)
DebugPrintTrace(("xfill returns %d\n", i));
#endif
return (i);
#if 0
while (ct-- && (*funcRead) (pl->text, MAXLINESIZE, fh) != NULL)
{
if (funcRead == ( int (*) (char *,int, FILE *))fgets)
pl->text[strlen(pl->text)-1] = 0;
if (fIgnore && !MBSTR::Strcmps(pl->text, ""))
{
pl->text[0] = 0;
++*plnum;
}
if (strlen (pl->text) != 0 || !fIgnore)
{
pl->line = ++*plnum;
pl++;
i++;
}
}
#ifdef DEBUG
if (fDebug)
DebugPrintTrace(("xfill returns %d\n", i));
#endif
return (i);
# endif
}
/**************************************************************************/
/* Adjust returns number of lines in buffer. */
/**************************************************************************/
FC::adjust (struct lineType *pl, int ml, int lt)
{
#ifdef DEBUG
if (fDebug)
DebugPrintTrace(("adjust (%04x, %d, %d) = ", pl, ml, lt));
if (fDebug)
DebugPrintTrace(("%d\n", ml-lt));
#endif
if (ml <= lt)
return (0);
#ifdef DEBUG
if (fDebug)
DebugPrintTrace(("move (%04x, %04x, %04x)\n", &pl[lt], &pl[0], sizeof (*pl)*(ml-lt)));
#endif
// Move((char *)&pl[lt], (char *)&pl[0], sizeof (*pl)*(ml-lt));
memmove( (char *)&pl[0], (char *)&pl[lt], sizeof (*pl)*(ml-lt) );
return ml-lt;
}
/**************************************************************************/
/* dump */
/* dump outputs a range of lines. */
/* */
/* INPUTS */
/* pl pointer to current lineType structure */
/* start starting line number */
/* end ending line number */
/* */
/* CALLS */
/* pline, printf */
/**************************************************************************/
void FC::dump (struct lineType *pl, int start, int end)
{
if (fAbbrev && end-start > 2)
{
pline (pl+start);
DisplayMessage( MSG_FC_ABBREVIATE_SYMBOL, NORMAL_MESSAGE );
pline (pl+end);
}
else
{
while (start <= end)
pline (pl+start++);
}
}
/**************************************************************************/
/* PrintLINE */
/* pline prints a single line of output. If the /n flag */
/* has been specified, the line number of the printed text is added. */
/* */
/* Inputs */
/* pl pointer to current lineType structure */
/* fNumb TRUE if /n specified */
/**************************************************************************/
void FC::pline (struct lineType *pl)
{
if (!fUnicode) {
if (fNumb)
DisplayMessage( MSG_FC_NUMBERED_DATA, NORMAL_MESSAGE, "%5d%s",
pl->line, pl->text );
else
DisplayMessage( MSG_FC_DATA, NORMAL_MESSAGE, "%s", pl->text );
} else {
FSTRING f;
if (fNumb)
DisplayMessage( MSG_FC_NUMBERED_DATA, NORMAL_MESSAGE, "%5d%W",
pl->line, f.Initialize(pl->wtext) );
else
DisplayMessage( MSG_FC_DATA, NORMAL_MESSAGE, "%W",
f.Initialize(pl->wtext) );
}
}
/*********************************************************************/
/* Routine: ParseFileNames */
/* */
/* Function: Parses the two given filenames and then compares the */
/* appropriate filenames. This routine handles wildcard */
/* characters in both filenames. */
/*********************************************************************/
INT
FC::ParseFileNames()
{
PATH File1;
PATH File2;
FSN_FILTER Filter;
PWSTRING Name;
PARRAY NodeArray;
PITERATOR Iterator;
PFSN_DIRECTORY Dir;
PFSNODE File;
PPATH ExpandedPath;
PATH TmpPath;
int result = SUCCESS;
char *locale;
if (!File1.Initialize( &_File1 ) ||
!File2.Initialize( &_File2 ) ||
!Filter.Initialize()) {
DisplayMessage( MSG_FC_OUT_OF_MEMORY, ERROR_MESSAGE );
return FAILURE;
}
if (!(Name = File1.QueryName())) {
DisplayMessage( MSG_FC_UNABLE_TO_OPEN, ERROR_MESSAGE, "%W", File1.GetPathString() );
return FILES_NOT_FOUND;
}
if (!Filter.SetFileName( Name ) ||
!Filter.SetAttributes( (FSN_ATTRIBUTE)0, // ALL
FSN_ATTRIBUTE_FILES, // ANY
FSN_ATTRIBUTE_DIRECTORY )) { // NONE
DELETE( Name );
DisplayMessage( MSG_FC_OUT_OF_MEMORY, ERROR_MESSAGE );
return FAILURE;
}
DELETE( Name );
if (!TmpPath.Initialize( &File1, TRUE ) ||
!TmpPath.TruncateBase()) {
DisplayMessage( MSG_FC_OUT_OF_MEMORY, ERROR_MESSAGE );
return FAILURE;
}
if ( !(Dir = SYSTEM::QueryDirectory( &TmpPath )) ||
!(NodeArray = Dir->QueryFsnodeArray( &Filter )) ||
!(Iterator = NodeArray->QueryIterator()) ||
!(File = (PFSNODE)Iterator->GetNext())
) {
DisplayMessage( MSG_FC_UNABLE_TO_OPEN, ERROR_MESSAGE, "%W", File1.GetPathString() );
return FILES_NOT_FOUND;
}
Iterator->Reset();
while ( File = (PFSNODE)Iterator->GetNext() ) {
PWSTRING Name1;
PWSTRING Name2;
if (!(Name1 = File->QueryName())) {
DisplayMessage( MSG_FC_UNABLE_TO_OPEN, ERROR_MESSAGE, "%W", File->GetPath()->GetPathString() );
return FILES_NOT_FOUND;
}
if ( _File2.HasWildCard() ) {
if ( !(ExpandedPath = _File2.QueryWCExpansion( (PPATH)File->GetPath() ))) {
if (!(Name2 = _File2.QueryName())) {
DisplayMessage( MSG_FC_UNABLE_TO_OPEN, ERROR_MESSAGE, "%W", _File2.GetPathString() );
return FILES_NOT_FOUND;
}
DisplayMessage( MSG_FC_CANT_EXPAND_TO_MATCH, ERROR_MESSAGE, "%W%W", Name1, Name2 );
DELETE( Name2 );
DELETE( Name1 );
DELETE( Iterator );
DELETE( NodeArray );
DELETE( Dir );
return FAILURE;
}
} else {
if ( !(ExpandedPath = NEW PATH) ||
!ExpandedPath->Initialize( &_File2 ) ) {
DisplayMessage( MSG_FC_OUT_OF_MEMORY, ERROR_MESSAGE );
DELETE( Name1 );
DELETE( Iterator );
DELETE( NodeArray );
DELETE( Dir );
return FAILURE;
}
}
if (!(Name2 = ExpandedPath->QueryName())) {
DisplayMessage( MSG_FC_UNABLE_TO_OPEN, ERROR_MESSAGE, "%W", ExpandedPath->GetPathString() );
return FILES_NOT_FOUND;
}
if (!File1.SetName( Name1 ) ||
!File2.SetName( Name2 )) {
DisplayMessage( MSG_FC_OUT_OF_MEMORY, ERROR_MESSAGE );
return FAILURE;
}
switch (comp( File1.GetPathString(), File2.GetPathString() )) {
case FAILURE:
return FAILURE;
case SUCCESS:
break;
case FILES_ARE_DIFFERENT:
result |= FILES_ARE_DIFFERENT;
break;
case FILES_NOT_FOUND:
result |= FILES_NOT_FOUND;
break;
case FILES_OFFLINE:
result |= FILES_OFFLINE;
break;
default:
DebugAssert(FALSE);
}
DELETE( Name2 );
DELETE( Name1 );
DELETE( ExpandedPath );
}
// Print a warning in case that offline files were skipped
if (fOfflineSkipped) {
DisplayMessage(MSG_FC_OFFLINE_FILES_SKIPPED, ERROR_MESSAGE);
}
DELETE( Iterator );
NodeArray->DeleteAllMembers();
DELETE( NodeArray );
DELETE( Dir );
return result;
}
/*********************************************************************/
/* Routine: comp */
/* */
/* Function: Compares the two files. */
/*********************************************************************/
INT
FC::comp(PCWSTRING file1, PCWSTRING file2)
{
DisplayMessage( MSG_FC_COMPARING_FILES, NORMAL_MESSAGE, "%W%W", file1, file2 );
if (fBinary) {
return BinaryCompare (file1, file2);
} else {
return LineCompare (file1, file2);
}
}
#if 0
/* returns line from file (no CRLFs); returns NULL if EOF */
FC::fgetl ( char *buf, int len, FILE *fh)
{
register int c;
register char *p;
/* remember NUL at end */
len--;
p = buf;
while (len) {
c = getc (fh);
if (c == EOF || c == '\n')
break;
#if MSDOS
if (c != '\r')
#endif
if (c != '\t') {
*p++ = (char)c;
len--;
}
else {
c = min (8 - ((int)(p-buf) & 0x0007), len);
memset(p, ' ', c);
p += c;
len -= c;
}
}
*p = 0;
return ! ( (c == EOF) && (p == buf) );
}
#endif