Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1392 lines
38 KiB

  1. /*++
  2. Copyright (c) 1990-2000 Microsoft Corporation
  3. Module Name:
  4. FC
  5. Abstract:
  6. FC is a DOS-5 compatible file comparison utility
  7. Author:
  8. Ramon Juan San Andres (ramonsa) 01-May-1991
  9. Notes:
  10. This FC is a port of the DOS5 FC code. It has been slightly modified
  11. to use some of the ULIB functionality (e.g. argument parsing), however
  12. it does not make full use of the ULIB functionality (e.g. it uses
  13. stdio.h functions for file handling).
  14. Revision History:
  15. --*/
  16. /****************************************************************************
  17. File Compare
  18. Fcom compares two files in either a line-by-line mode or in a strict
  19. BYTE-by-BYTE mode.
  20. The BYTE-by-BYTE mode is simple; merely read both files and print the
  21. offsets where they differ and the contents.
  22. The line compare mode attempts to isolate differences in ranges of lines.
  23. Two buffers of lines are read and compared. No hashing of lines needs
  24. to be done; hashing only speedily tells you when things are different,
  25. not the same. Most files run through this are expected to be largely
  26. the same. Thus, hashing buys nothing.
  27. ***********************************************************************
  28. The algorithm that immediately follows does not work. There is an error
  29. somewhere in the range of lines 11 on. An alternative explanation follows.
  30. KGS
  31. ************************************************************************
  32. [0] Fill buffers
  33. [1] If both buffers are empty then
  34. [1.1] Done
  35. [2] Adjust buffers so 1st differing lines are at top.
  36. [3] If buffers are empty then
  37. [3.1] Goto [0]
  38. This is the difficult part. We assume that there is a sequence of inserts,
  39. deletes and replacements that will bring the buffers back into alignment.
  40. [4] xd = yd = FALSE
  41. [5] xc = yc = 1
  42. [6] xp = yp = 1
  43. [7] If buffer1[xc] and buffer2[yp] begin a "sync" range then
  44. [7.1] Output lines 1 through xc-1 in buffer 1
  45. [7.2] Output lines 1 through yp-1 in buffer 2
  46. [7.3] Adjust buffer 1 so line xc is at beginning
  47. [7.4] Adjust buffer 2 so line yp is at beginning
  48. [7.5] Goto [0]
  49. [8] If buffer1[xp] and buffer2[yc] begin a "sync" range then
  50. [8.1] Output lines 1 through xp-1 in buffer 1
  51. [8.2] Output lines 1 through yc-1 in buffer 2
  52. [8.3] Adjust buffer 1 so line xp is at beginning
  53. [8.4] Adjust buffer 2 so line yc is at beginning
  54. [8.5] Goto [0]
  55. [9] xp = xp + 1
  56. [10] if xp > xc then
  57. [10.1] xp = 1
  58. [10.2] xc = xc + 1
  59. [10.3] if xc > number of lines in buffer 1 then
  60. [10.4] xc = number of lines
  61. [10.5] xd = TRUE
  62. [11] if yp > yc then
  63. [11.1] yp = 1
  64. [11.2] yc = yc + 1
  65. [11.3] if yc > number of lines in buffer 2 then
  66. [11.4] yc = number of lines
  67. [11.5] yd = TRUE
  68. [12] if not xd or not yd then
  69. [12.1] goto [6]
  70. At this point there is no possible match between the buffers. For
  71. simplicity, we punt.
  72. [13] Display error message.
  73. EXPLANATION 2
  74. This is a variation of the Largest Common Subsequence problem. A
  75. detailed explanation of this can be found on p 189 of Data Structures
  76. and Algorithms by Aho Hopcroft and Ulman.
  77. FC maintains two buffers within which it tries to find the Largest Common
  78. Subsequence (The largest common subsequence is simply the pattern in
  79. buffer1 that yields the most matches with the pattern in buffer2, or the
  80. pattern in buffer2 that yields the most matches with the pattern in buffer1)
  81. FC makes a simplifying assumption that the contents of one buffer can be
  82. converted to the contents of the other buffer by deleting the lines that are
  83. different between the two buffers.
  84. Two indices into each buffer are maintained:
  85. xc, yc == point to the last line that has been scanned up to now
  86. xp, yp == point to the first line that has not been exhaustively
  87. compared to lines 0 - #c in the other buffer.
  88. FC now makes a second simplifying assumption:
  89. It is unnecessary to do any calculations on lines that are equal.
  90. Hence FC scans File1 and File two line by line until a difference is
  91. encountered.
  92. When a difference is encountered the two buffers are filled such that
  93. the line containing the first difference heads the buffer. The following
  94. exhaustive search algorithm is applied to find the first "sync" occurance.
  95. (The below is simplified to use == for comparison. In practice more than
  96. one line needs to match for a "sync" to be established).
  97. FOR xc,yc = 1; xc,yx <= sizeof( BUFFERS ); xc++, yc++
  98. FOR xp,yp = 1; xp,yp <= xc,yc; xp++, yp++
  99. IF ( BUFFER1[xp] == BUFFER2[yc] )
  100. Then the range of lines BUFFER1[ 1 ... xp ] and
  101. BUFFER2[ 1 ... yc ] need to be deleted for the
  102. two files to be equal. Therefore DISPLAY these
  103. ranges, and begin scanning both files starting at
  104. the matching lines.
  105. FI
  106. IF ( BUFFER1[yp] == BUFFER2[xc] )
  107. Then the range of lines BUFFER2[ 1 ... yp ] and
  108. BUFFER1[ 1 ... xc ] need to be deleted for the
  109. two files to be equal. Therefore DISPLAY these
  110. ranges, and begin scanning both files starting at
  111. the matching lines.
  112. FI
  113. FOREND
  114. FOREND
  115. If a match is not found within the buffers, the message "RESYNC FAILED"
  116. is issued and further comparison is aborted since there is no valid way
  117. to find further matching lines.
  118. END EXPLANATION 2
  119. Certain flags may be set to modify the behavior of the comparison:
  120. -a abbreviated output. Rather than displaying all of the modified
  121. ranges, just display the beginning, ... and the ending difference
  122. -b compare the files in binary (or BYTE-by-BYTE) mode. This mode is
  123. default on .EXE, .OBJ, .LIB, .COM, .BIN, and .SYS files
  124. -c ignore case on compare (cmp = strcmpi instead of strcmp)
  125. -l compare files in line-by-line mode
  126. -lb n set the size of the internal line buffer to n lines from default
  127. of 100
  128. -u Files to be compared are UNICODE text files
  129. -w ignore blank lines and white space (ignore len 0, use strcmps)
  130. -t do not untabify (use fgets instead of fgetl)
  131. -n output the line number also
  132. -NNNN set the number of lines to resynchronize to n which defaults
  133. to 2. Failure to have this value set correctly can result in
  134. odd output:
  135. file1: file2:
  136. abcdefg abcdefg
  137. aaaaaaa aaaaaab
  138. aaaaaaa aaaaaaa
  139. aaaaaaa aaaaaaa
  140. abcdefg abcdefg
  141. with default sync of 2 yields: with sync => 3 yields:
  142. *****f1 *****f1
  143. abcdefg abcdefg
  144. aaaaaaa aaaaaaa
  145. *****f2 aaaaaaa
  146. abcdefg *****f2
  147. aaaaaab abcdefg
  148. aaaaaaa aaaaaab
  149. aaaaaaa
  150. *****f1
  151. aaaaaaa
  152. aaaaaaa
  153. abcdefg
  154. *****f2
  155. aaaaaaa
  156. abcdefg
  157. WARNING:
  158. This program makes use of GOTO's and hence is not as straightforward
  159. as it could be! CAVEAT PROGRAMMER.
  160. ****************************************************************************/
  161. #include "ulib.hxx"
  162. #include "fc.hxx"
  163. #include "arg.hxx"
  164. #include "array.hxx"
  165. #include "arrayit.hxx"
  166. #include "bytestrm.hxx"
  167. #include "dir.hxx"
  168. #include "file.hxx"
  169. #include "filestrm.hxx"
  170. #include "filter.hxx"
  171. #include "mbstr.hxx"
  172. #include "system.hxx"
  173. #include "wstring.hxx"
  174. #include <malloc.h>
  175. #include <process.h>
  176. #include <stdlib.h>
  177. #include <math.h>
  178. #include <locale.h>
  179. /**************************************************************************/
  180. /* main */
  181. /**************************************************************************/
  182. INT __cdecl
  183. main (
  184. )
  185. {
  186. DEFINE_CLASS_DESCRIPTOR( FC );
  187. {
  188. FC Fc;
  189. if ( Fc.Initialize() ) {
  190. return Fc.Fcmain();
  191. }
  192. }
  193. return FAILURE;
  194. }
  195. CHAR *ExtBin[] = { "EXE", "OBJ", "LIB",
  196. "COM", "BIN", "SYS", NULL };
  197. DEFINE_CONSTRUCTOR( FC, PROGRAM );
  198. FC::~FC () {
  199. }
  200. BOOLEAN FC::Initialize() {
  201. if ( PROGRAM::Initialize() ) {
  202. ValidateVersion();
  203. ctSync = -1; // number of lines required to sync
  204. cLine = -1; // number of lines in internal buffs
  205. fAbbrev = FALSE; // abbreviated output
  206. fBinary = FALSE; // binary comparison
  207. fLine = FALSE; // line comparison
  208. fNumb = FALSE; // display line numbers
  209. fCase = TRUE; // case is significant
  210. fIgnore = FALSE; // ignore spaces and blank lines
  211. fSkipOffline = TRUE; // skip offline files
  212. fOfflineSkipped = FALSE; // no files are skipped
  213. #ifdef DEBUG
  214. fDebug = FALSE;
  215. #endif
  216. fExpandTabs = TRUE;
  217. // funcRead = (int (*)(char *,int,FILE *))fgetl;
  218. extBin = (CHAR **)ExtBin;
  219. return ParseArguments();
  220. }
  221. return FALSE;
  222. }
  223. BOOLEAN
  224. FC::ParseArguments(
  225. )
  226. {
  227. ARGUMENT_LEXEMIZER ArgLex;
  228. ARRAY LexArray;
  229. ARRAY ArrayOfArg;
  230. PATH_ARGUMENT ProgramName;
  231. FLAG_ARGUMENT FlagAbbreviate;
  232. FLAG_ARGUMENT FlagAsciiCompare;
  233. FLAG_ARGUMENT FlagBinaryCompare;
  234. FLAG_ARGUMENT FlagCaseInsensitive;
  235. FLAG_ARGUMENT FlagCompression;
  236. FLAG_ARGUMENT FlagExpansion;
  237. FLAG_ARGUMENT FlagLineNumber;
  238. FLAG_ARGUMENT FlagRequestHelp;
  239. FLAG_ARGUMENT FlagUnicode;
  240. FLAG_ARGUMENT FlagIncludeOffline;
  241. FLAG_ARGUMENT FlagIncludeOffline2;
  242. LONG_ARGUMENT LongBufferSize;
  243. #ifdef DEBUG
  244. FLAG_ARGUMENT FlagDebug;
  245. #endif
  246. STRING_ARGUMENT LongMatch;
  247. PATH_ARGUMENT InFile1;
  248. PATH_ARGUMENT InFile2;
  249. LONG Long;
  250. INT i;
  251. if( !LexArray.Initialize() ) {
  252. DebugAbort( "LexArray.Initialize() Failed!\n" );
  253. return( FALSE );
  254. }
  255. if( !ArgLex.Initialize(&LexArray) ) {
  256. DebugAbort( "ArgLex.Initialize() Failed!\n" );
  257. return( FALSE );
  258. }
  259. // Allow only the '/' as a valid switch
  260. ArgLex.PutSwitches("/");
  261. ArgLex.SetCaseSensitive( FALSE );
  262. ArgLex.PutStartQuotes("\"");
  263. ArgLex.PutEndQuotes("\"");
  264. ArgLex.PutSeparators(" \t");
  265. if( !ArgLex.PrepareToParse() ) {
  266. DebugAbort( "ArgLex.PrepareToParse() Failed!\n" );
  267. return( FALSE );
  268. }
  269. if( !ProgramName.Initialize("*") ||
  270. !FlagAbbreviate.Initialize("/A") ||
  271. !FlagAsciiCompare.Initialize("/L") ||
  272. !FlagBinaryCompare.Initialize("/B") ||
  273. !FlagCaseInsensitive.Initialize("/C") ||
  274. !FlagCompression.Initialize("/W") ||
  275. !FlagExpansion.Initialize("/T") ||
  276. !FlagLineNumber.Initialize("/N") ||
  277. !FlagRequestHelp.Initialize("/?") ||
  278. !FlagUnicode.Initialize("/U") ||
  279. !FlagIncludeOffline.Initialize("/OFFLINE") ||
  280. !FlagIncludeOffline2.Initialize("/OFF") ||
  281. #ifdef DEBUG
  282. !FlagDebug.Initialize("/D") ||
  283. #endif
  284. !LongBufferSize.Initialize("/LB#") ||
  285. !LongMatch.Initialize("/*") ||
  286. !InFile1.Initialize("*") ||
  287. !InFile2.Initialize("*") ) {
  288. DebugAbort( "Unable to Initialize some or all of the Arguments!\n" );
  289. return( FALSE );
  290. }
  291. if( !ArrayOfArg.Initialize() ) {
  292. DebugAbort( "ArrayOfArg.Initialize() Failed\n" );
  293. return( FALSE );
  294. }
  295. if( !ArrayOfArg.Put(&ProgramName) ||
  296. !ArrayOfArg.Put(&FlagAbbreviate) ||
  297. !ArrayOfArg.Put(&FlagAsciiCompare) ||
  298. !ArrayOfArg.Put(&FlagBinaryCompare) ||
  299. !ArrayOfArg.Put(&FlagCaseInsensitive) ||
  300. !ArrayOfArg.Put(&FlagCompression) ||
  301. !ArrayOfArg.Put(&FlagExpansion) ||
  302. !ArrayOfArg.Put(&FlagLineNumber) ||
  303. !ArrayOfArg.Put(&FlagRequestHelp) ||
  304. !ArrayOfArg.Put(&FlagUnicode) ||
  305. !ArrayOfArg.Put(&FlagIncludeOffline) ||
  306. !ArrayOfArg.Put(&FlagIncludeOffline2) ||
  307. #ifdef DEBUG
  308. !ArrayOfArg.Put(&FlagDebug) ||
  309. #endif
  310. !ArrayOfArg.Put(&LongBufferSize) ||
  311. !ArrayOfArg.Put(&LongMatch) ||
  312. !ArrayOfArg.Put(&InFile1) ||
  313. !ArrayOfArg.Put(&InFile2) ) {
  314. DebugAbort( "ArrayOfArg.Put() Failed!\n" );
  315. return( FALSE );
  316. }
  317. if( !( ArgLex.DoParsing( &ArrayOfArg ) ) ) {
  318. // For each incorrect command line parameter, FC displays the
  319. // following message:
  320. //
  321. // FC: Invalid Switch
  322. //
  323. // It does *not* die if a parameter is unrecognized...(Dos does...)
  324. //
  325. DisplayMessage( MSG_FC_INVALID_SWITCH, ERROR_MESSAGE, "" );
  326. // return( FALSE );
  327. }
  328. //
  329. // It should now be safe to test the arguments for their values...
  330. //
  331. if( FlagRequestHelp.QueryFlag() ) {
  332. DisplayMessage( MSG_FC_HELP_MESSAGE, NORMAL_MESSAGE, "" );
  333. return( FALSE );
  334. }
  335. if( FlagBinaryCompare.QueryFlag() &&
  336. ( FlagAsciiCompare.QueryFlag() || FlagLineNumber.QueryFlag() ) ) {
  337. DisplayMessage( MSG_FC_INCOMPATIBLE_SWITCHES, ERROR_MESSAGE, "" );
  338. return( FALSE );
  339. }
  340. if( !InFile1.IsValueSet() ||
  341. !InFile2.IsValueSet() ) {
  342. DisplayMessage( MSG_FC_INSUFFICIENT_FILES, ERROR_MESSAGE, "" );
  343. return( FALSE );
  344. }
  345. //
  346. // Convert filenames to upper case
  347. //
  348. _File1.Initialize( InFile1.GetPath() );
  349. _File2.Initialize( InFile2.GetPath() );
  350. ((PWSTRING)_File1.GetPathString())->Strupr();
  351. ((PWSTRING)_File2.GetPathString())->Strupr();
  352. fUnicode = FlagUnicode.QueryFlag();
  353. fAbbrev = FlagAbbreviate.QueryFlag();
  354. fCase = !FlagCaseInsensitive.QueryFlag();
  355. fIgnore = FlagCompression.QueryFlag();
  356. fNumb = FlagLineNumber.QueryFlag();
  357. fBinary = FlagBinaryCompare.QueryFlag();
  358. fSkipOffline = ( !FlagIncludeOffline.QueryFlag() ) &&
  359. ( !FlagIncludeOffline2.QueryFlag() );
  360. if ( FlagExpansion.QueryFlag() ) {
  361. fExpandTabs = FALSE;
  362. //funcRead = (int (*)(char *,int,FILE *))fgets;
  363. }
  364. #ifdef DEBUG
  365. fDebug = FlagDebug.QueryFlag();
  366. #endif
  367. if ( LongBufferSize.IsValueSet() ) {
  368. cLine = (INT)LongBufferSize.QueryLong();
  369. fLine = TRUE;
  370. } else {
  371. cLine = 100;
  372. }
  373. if ( LongMatch.IsValueSet() ) {
  374. if ( LongMatch.GetString()->QueryNumber( &Long ) ) {
  375. ctSync = (INT)Long;
  376. fLine = TRUE;
  377. } else {
  378. DisplayMessage( MSG_FC_INVALID_SWITCH, ERROR_MESSAGE, "" );
  379. ctSync = 2;
  380. }
  381. } else {
  382. ctSync = 2;
  383. }
  384. if (!fBinary && !fLine) {
  385. DSTRING ExtBin;
  386. PWSTRING Ext = _File1.QueryExt();
  387. if ( Ext ) {
  388. for (i=0; extBin[i]; i++) {
  389. ExtBin.Initialize( extBin[i] );
  390. if ( !ExtBin.Stricmp( Ext ) ) {
  391. fBinary = TRUE;
  392. break;
  393. }
  394. }
  395. DELETE( Ext );
  396. }
  397. if (!fBinary) {
  398. fLine = TRUE;
  399. }
  400. }
  401. if (!fUnicode) {
  402. if (fIgnore) {
  403. if (fCase) {
  404. fCmp = MBSTR::Strcmps;
  405. } else {
  406. fCmp = MBSTR::Strcmpis;
  407. }
  408. } else {
  409. if (fCase) {
  410. fCmp = MBSTR::Strcmp;
  411. } else {
  412. fCmp = MBSTR::Stricmp;
  413. }
  414. }
  415. } else {
  416. if (fIgnore) {
  417. if (fCase) {
  418. fCmp_U = WSTRING::Strcmps;
  419. } else {
  420. fCmp_U = WSTRING::Strcmpis;
  421. }
  422. } else {
  423. if (fCase) {
  424. fCmp_U = WSTRING::Strcmp;
  425. } else {
  426. fCmp_U = WSTRING::Stricmp;
  427. }
  428. }
  429. }
  430. return( TRUE );
  431. }
  432. INT
  433. FC::Fcmain
  434. (
  435. )
  436. {
  437. return ParseFileNames();
  438. }
  439. /**************************************************************************/
  440. /* BinaryCompare */
  441. /**************************************************************************/
  442. int
  443. FC::BinaryCompare (
  444. PCWSTRING f1,
  445. PCWSTRING f2
  446. )
  447. {
  448. PATH FileName;
  449. PFSN_FILE File1 = NULL;
  450. PFSN_FILE File2 = NULL;
  451. PFILE_STREAM Stream1 = NULL;
  452. PFILE_STREAM Stream2 = NULL;;
  453. BYTE_STREAM Bs1;
  454. BYTE_STREAM Bs2;
  455. BYTE c1, c2;
  456. ULONG64 pos;
  457. BOOLEAN fSame;
  458. char buffer[128];
  459. BOOLEAN fFileSkipped;
  460. if ( !FileName.Initialize( f1 ) ||
  461. !(File1 = SYSTEM::QueryFile( &FileName, fSkipOffline, &fFileSkipped )) ||
  462. !(Stream1 = File1->QueryStream( READ_ACCESS, FILE_FLAG_OPEN_NO_RECALL )) ||
  463. !Bs1.Initialize( Stream1 )
  464. ) {
  465. DELETE( Stream2 );
  466. DELETE( File2 );
  467. DELETE( Stream1 );
  468. DELETE( File1 );
  469. if (fFileSkipped) {
  470. // Skipping offline files is not an error, just track this happened
  471. fOfflineSkipped = TRUE;
  472. return FILES_OFFLINE;
  473. } else {
  474. DisplayMessage( MSG_FC_UNABLE_TO_OPEN, ERROR_MESSAGE, "%W", f1 );
  475. return FILES_NOT_FOUND;
  476. }
  477. }
  478. if ( !FileName.Initialize( f2 ) ||
  479. !(File2 = SYSTEM::QueryFile( &FileName, fSkipOffline, &fFileSkipped )) ||
  480. !(Stream2 = File2->QueryStream( READ_ACCESS, FILE_FLAG_OPEN_NO_RECALL )) ||
  481. !Bs2.Initialize( Stream2 )
  482. ) {
  483. DELETE( Stream2 );
  484. DELETE( File2 );
  485. DELETE( Stream1 );
  486. DELETE( File1 );
  487. if (fFileSkipped) {
  488. // Skipping offline files is not an error, just track this happened
  489. fOfflineSkipped = TRUE;
  490. return FILES_OFFLINE;
  491. } else {
  492. DisplayMessage( MSG_FC_UNABLE_TO_OPEN, ERROR_MESSAGE, "%W", f2 );
  493. return FILES_NOT_FOUND;
  494. }
  495. }
  496. fSame = TRUE;
  497. pos = 0;
  498. while ( TRUE ) {
  499. if ( Bs1.ReadByte( &c1 ) ) {
  500. if ( Bs2.ReadByte( &c2 ) ) {
  501. if (c1 != c2) {
  502. if (pos > MAXULONG) {
  503. sprintf( buffer, "%016I64X: %02X %02X", pos, c1, c2 );
  504. } else {
  505. sprintf( buffer, "%08I64X: %02X %02X", pos, c1, c2 );
  506. }
  507. DisplayMessage( MSG_FC_DATA, NORMAL_MESSAGE, "%s", buffer );
  508. fSame = FALSE;
  509. }
  510. } else {
  511. DisplayMessage( MSG_FC_FILES_DIFFERENT_LENGTH, NORMAL_MESSAGE, "%W%W", f1, f2 );
  512. fSame = FALSE;
  513. break;
  514. }
  515. } else {
  516. if ( Bs2.ReadByte( &c2 ) ) {
  517. DisplayMessage( MSG_FC_FILES_DIFFERENT_LENGTH, NORMAL_MESSAGE, "%W%W", f2, f1 );
  518. fSame = FALSE;
  519. break;
  520. } else {
  521. if (fSame) {
  522. DisplayMessage( MSG_FC_NO_DIFFERENCES, NORMAL_MESSAGE );
  523. }
  524. break;
  525. }
  526. }
  527. pos++;
  528. }
  529. DELETE( Stream2 );
  530. DELETE( File2 );
  531. DELETE( Stream1 );
  532. DELETE( File1 );
  533. return fSame ? SUCCESS : FILES_ARE_DIFFERENT;
  534. }
  535. /**************************************************************************/
  536. /* Compare a range of lines. */
  537. /**************************************************************************/
  538. BOOLEAN FC::compare (int l1, register int s1, int l2, register int s2, int ct)
  539. {
  540. #ifdef DEBUG
  541. if (fDebug)
  542. DebugPrintTrace(("compare (%d, %d, %d, %d, %d)\n", l1, s1, l2, s2, ct));
  543. #endif
  544. if (ct <= 0 || s1+ct > l1 || s2+ct > l2)
  545. return (FALSE);
  546. while (ct--)
  547. {
  548. #ifdef DEBUG
  549. if (fDebug)
  550. DebugPrintTrace(("'%s' == '%s'? ", buffer1[s1].text, buffer2[s2].text));
  551. #endif
  552. if(!fUnicode) {
  553. if ((*fCmp)(buffer1[s1++].text, buffer2[s2++].text)) {
  554. #ifdef DEBUG
  555. if (fDebug)
  556. DebugPrintTrace(("No\n"));
  557. #endif
  558. return (FALSE);
  559. }
  560. } else {
  561. if ((*fCmp_U)(buffer1[s1++].wtext, buffer2[s2++].wtext)) {
  562. #ifdef DEBUG
  563. if (fDebug)
  564. DebugPrintTrace(("No\n"));
  565. #endif
  566. return (FALSE);
  567. }
  568. }
  569. }
  570. #ifdef DEBUG
  571. if (fDebug)
  572. DebugPrintTrace(("Yes\n"));
  573. #endif
  574. return (TRUE);
  575. }
  576. /**************************************************************************/
  577. /* LineCompare */
  578. /**************************************************************************/
  579. INT
  580. FC::LineCompare(
  581. PCWSTRING f1,
  582. PCWSTRING f2
  583. )
  584. {
  585. PATH FileName;
  586. PFSN_FILE File1 = NULL;
  587. PFSN_FILE File2 = NULL;
  588. PFILE_STREAM Stream1 = NULL;
  589. PFILE_STREAM Stream2 = NULL;
  590. int result;
  591. BOOLEAN fFileSkipped;
  592. buffer1 = buffer2 = NULL;
  593. if ( !FileName.Initialize( f1 ) ||
  594. !(File1 = SYSTEM::QueryFile( &FileName, fSkipOffline, &fFileSkipped )) ||
  595. !(Stream1 = File1->QueryStream( READ_ACCESS, FILE_FLAG_OPEN_NO_RECALL ))
  596. ) {
  597. FREE(buffer1);
  598. FREE(buffer2);
  599. DELETE(Stream2);
  600. DELETE(File2);
  601. DELETE(Stream1);
  602. DELETE(File1);
  603. if (fFileSkipped) {
  604. // Skipping offline files is not an error, just track this happened
  605. fOfflineSkipped = TRUE;
  606. return FILES_OFFLINE;
  607. } else {
  608. DisplayMessage( MSG_FC_UNABLE_TO_OPEN, ERROR_MESSAGE, "%W", f1 );
  609. return FILES_NOT_FOUND;
  610. }
  611. }
  612. if ( !FileName.Initialize( f2 ) ||
  613. !(File2 = SYSTEM::QueryFile( &FileName, fSkipOffline, &fFileSkipped )) ||
  614. !(Stream2 = File2->QueryStream( READ_ACCESS, FILE_FLAG_OPEN_NO_RECALL ))
  615. ) {
  616. FREE(buffer1);
  617. FREE(buffer2);
  618. DELETE(Stream2);
  619. DELETE(File2);
  620. DELETE(Stream1);
  621. DELETE(File1);
  622. if (fFileSkipped) {
  623. // Skipping offline files is not an error, just track this happened
  624. fOfflineSkipped = TRUE;
  625. return FILES_OFFLINE;
  626. } else {
  627. DisplayMessage( MSG_FC_UNABLE_TO_OPEN, ERROR_MESSAGE, "%W", f2 );
  628. return FILES_NOT_FOUND;
  629. }
  630. }
  631. if ( (buffer1 = (struct lineType *)MALLOC(cLine * (sizeof *buffer1))) == NULL ||
  632. (buffer2 = (struct lineType *)MALLOC(cLine * (sizeof *buffer1))) == NULL) {
  633. DisplayMessage( MSG_FC_OUT_OF_MEMORY, ERROR_MESSAGE );
  634. FREE(buffer1);
  635. FREE(buffer2);
  636. DELETE(Stream2);
  637. DELETE(File2);
  638. DELETE(Stream1);
  639. DELETE(File1);
  640. return FAILURE;
  641. }
  642. result = RealLineCompare( f1, f2, Stream1, Stream2 );
  643. FREE(buffer1);
  644. FREE(buffer2);
  645. DELETE(Stream2);
  646. DELETE(File2);
  647. DELETE(Stream1);
  648. DELETE(File1);
  649. return result;
  650. }
  651. int
  652. FC::RealLineCompare (
  653. PCWSTRING f1,
  654. PCWSTRING f2,
  655. PSTREAM Stream1,
  656. PSTREAM Stream2
  657. )
  658. {
  659. int l1, l2, i, xp, yp, xc, yc;
  660. BOOLEAN xd, yd, fSame;
  661. int line1, line2;
  662. fSame = TRUE;
  663. l1 = l2 = 0;
  664. line1 = line2 = 0;
  665. l0:
  666. #ifdef DEBUG
  667. if (fDebug) {
  668. DebugPrintTrace(("At scan beginning\n"));
  669. }
  670. #endif
  671. l1 += xfill (buffer1+l1, Stream1, cLine-l1, &line1);
  672. l2 += xfill (buffer2+l2, Stream2, cLine-l2, &line2);
  673. if (l1 == 0 && l2 == 0) {
  674. if (fSame) {
  675. DisplayMessage( MSG_FC_NO_DIFFERENCES, NORMAL_MESSAGE );
  676. }
  677. return fSame ? SUCCESS : FILES_ARE_DIFFERENT;
  678. }
  679. xc = min (l1, l2);
  680. for (i=0; i < xc; i++) {
  681. if (!compare (l1, i, l2, i, 1)) {
  682. break;
  683. }
  684. }
  685. if (i != xc) {
  686. i = ( i-1 > 0 )? ( i-1 ) : 0;
  687. }
  688. l1 = adjust (buffer1, l1, i);
  689. l2 = adjust (buffer2, l2, i);
  690. if (l1 == 0 && l2 == 0) {
  691. goto l0;
  692. }
  693. l1 += xfill (buffer1+l1, Stream1, cLine-l1, &line1);
  694. l2 += xfill (buffer2+l2, Stream2, cLine-l2, &line2);
  695. #ifdef DEBUG
  696. if (fDebug) {
  697. DebugPrintTrace(("buffers are adjusted, %d, %d remain\n", l1, l2));
  698. }
  699. #endif
  700. xd = yd = FALSE;
  701. xc = yc = 1;
  702. xp = yp = 1;
  703. l6:
  704. #ifdef DEBUG
  705. if (fDebug) {
  706. DebugPrintTrace(("Trying resync %d,%d %d,%d\n", xc, xp, yc, yp));
  707. }
  708. #endif
  709. i = min (l1-xc,l2-yp);
  710. i = min (i, ctSync);
  711. if (compare (l1, xc, l2, yp, i)) {
  712. fSame = FALSE;
  713. DisplayMessage( MSG_FC_OUTPUT_FILENAME, NORMAL_MESSAGE, "%W", f1 );
  714. dump (buffer1, 0, xc);
  715. DisplayMessage( MSG_FC_OUTPUT_FILENAME, NORMAL_MESSAGE, "%W", f2 );
  716. dump (buffer2, 0, yp);
  717. DisplayMessage( MSG_FC_DUMP_END, NORMAL_MESSAGE );
  718. l1 = adjust (buffer1, l1, xc);
  719. l2 = adjust (buffer2, l2, yp);
  720. goto l0;
  721. }
  722. i = min (l1-xp, l2-yc);
  723. i = min (i, ctSync);
  724. if (compare (l1, xp, l2, yc, i)) {
  725. fSame = FALSE;
  726. DisplayMessage( MSG_FC_OUTPUT_FILENAME, NORMAL_MESSAGE, "%W", f1 );
  727. dump (buffer1, 0, xp);
  728. DisplayMessage( MSG_FC_OUTPUT_FILENAME, NORMAL_MESSAGE, "%W", f2 );
  729. dump (buffer2, 0, yc);
  730. DisplayMessage( MSG_FC_DUMP_END, NORMAL_MESSAGE );
  731. l1 = adjust (buffer1, l1, xp);
  732. l2 = adjust (buffer2, l2, yc);
  733. goto l0;
  734. }
  735. if (++xp > xc) {
  736. xp = 1;
  737. if (++xc >= l1) {
  738. xc = l1;
  739. xd = TRUE;
  740. }
  741. }
  742. if (++yp > yc) {
  743. yp = 1;
  744. if (++yc >= l2) {
  745. yc = l2;
  746. yd = TRUE;
  747. }
  748. }
  749. if (!xd || !yd) {
  750. goto l6;
  751. }
  752. fSame = FALSE;
  753. if (l1 >= cLine || l2 >= cLine) {
  754. DisplayMessage( MSG_FC_RESYNC_FAILED, NORMAL_MESSAGE );
  755. }
  756. DisplayMessage( MSG_FC_OUTPUT_FILENAME, NORMAL_MESSAGE, "%W", f1 );
  757. dump (buffer1, 0, l1-1);
  758. DisplayMessage( MSG_FC_OUTPUT_FILENAME, NORMAL_MESSAGE, "%W", f2 );
  759. dump (buffer2, 0, l2-1);
  760. DisplayMessage( MSG_FC_DUMP_END, NORMAL_MESSAGE );
  761. return fSame ? SUCCESS : FILES_ARE_DIFFERENT;
  762. }
  763. /**************************************************************************/
  764. /* Return number of lines read in. */
  765. /**************************************************************************/
  766. FC::xfill (struct lineType *pl, PSTREAM Stream, int ct, int *plnum)
  767. {
  768. int i;
  769. DWORD StrSize;
  770. #ifdef DEBUG
  771. if (fDebug)
  772. DebugPrintTrace(("xfill (%04x, %04x)\n", pl, fh));
  773. #endif
  774. i = 0;
  775. if (!fUnicode) {
  776. while ( ct-- && !Stream->IsAtEnd() && Stream->ReadMbLine( pl->text, MAXLINESIZE, &StrSize, fExpandTabs, 8 ) ) {
  777. if (fIgnore && !MBSTR::Strcmps(pl->text, "")) {
  778. pl->text[0] = 0;
  779. ++*plnum;
  780. }
  781. if (strlen (pl->text) != 0 || !fIgnore)
  782. {
  783. pl->line = ++*plnum;
  784. pl++;
  785. i++;
  786. }
  787. }
  788. } else {
  789. while( ct-- && !Stream->IsAtEnd() && Stream->ReadWLine( pl->wtext,MAXLINESIZE, &StrSize, fExpandTabs, 8 ) ) {
  790. //while( ct-- && !Stream->IsAtEnd() && Stream->ReadLine( &_String , TRUE )) {
  791. //_String.QueryWSTR(0,TO_END,pl->wtext,MAXLINESIZE,TRUE);
  792. if (fIgnore && !WSTRING::Strcmps((PWSTR)pl->wtext, (PWSTR)L"")) {
  793. pl->wtext[0] = 0;
  794. ++*plnum;
  795. }
  796. if (wcslen (pl->wtext) != 0 || !fIgnore)
  797. {
  798. pl->line = ++*plnum;
  799. pl++;
  800. i++;
  801. }
  802. }
  803. }
  804. #ifdef DEBUG
  805. if (fDebug)
  806. DebugPrintTrace(("xfill returns %d\n", i));
  807. #endif
  808. return (i);
  809. #if 0
  810. while (ct-- && (*funcRead) (pl->text, MAXLINESIZE, fh) != NULL)
  811. {
  812. if (funcRead == ( int (*) (char *,int, FILE *))fgets)
  813. pl->text[strlen(pl->text)-1] = 0;
  814. if (fIgnore && !MBSTR::Strcmps(pl->text, ""))
  815. {
  816. pl->text[0] = 0;
  817. ++*plnum;
  818. }
  819. if (strlen (pl->text) != 0 || !fIgnore)
  820. {
  821. pl->line = ++*plnum;
  822. pl++;
  823. i++;
  824. }
  825. }
  826. #ifdef DEBUG
  827. if (fDebug)
  828. DebugPrintTrace(("xfill returns %d\n", i));
  829. #endif
  830. return (i);
  831. # endif
  832. }
  833. /**************************************************************************/
  834. /* Adjust returns number of lines in buffer. */
  835. /**************************************************************************/
  836. FC::adjust (struct lineType *pl, int ml, int lt)
  837. {
  838. #ifdef DEBUG
  839. if (fDebug)
  840. DebugPrintTrace(("adjust (%04x, %d, %d) = ", pl, ml, lt));
  841. if (fDebug)
  842. DebugPrintTrace(("%d\n", ml-lt));
  843. #endif
  844. if (ml <= lt)
  845. return (0);
  846. #ifdef DEBUG
  847. if (fDebug)
  848. DebugPrintTrace(("move (%04x, %04x, %04x)\n", &pl[lt], &pl[0], sizeof (*pl)*(ml-lt)));
  849. #endif
  850. // Move((char *)&pl[lt], (char *)&pl[0], sizeof (*pl)*(ml-lt));
  851. memmove( (char *)&pl[0], (char *)&pl[lt], sizeof (*pl)*(ml-lt) );
  852. return ml-lt;
  853. }
  854. /**************************************************************************/
  855. /* dump */
  856. /* dump outputs a range of lines. */
  857. /* */
  858. /* INPUTS */
  859. /* pl pointer to current lineType structure */
  860. /* start starting line number */
  861. /* end ending line number */
  862. /* */
  863. /* CALLS */
  864. /* pline, printf */
  865. /**************************************************************************/
  866. void FC::dump (struct lineType *pl, int start, int end)
  867. {
  868. if (fAbbrev && end-start > 2)
  869. {
  870. pline (pl+start);
  871. DisplayMessage( MSG_FC_ABBREVIATE_SYMBOL, NORMAL_MESSAGE );
  872. pline (pl+end);
  873. }
  874. else
  875. {
  876. while (start <= end)
  877. pline (pl+start++);
  878. }
  879. }
  880. /**************************************************************************/
  881. /* PrintLINE */
  882. /* pline prints a single line of output. If the /n flag */
  883. /* has been specified, the line number of the printed text is added. */
  884. /* */
  885. /* Inputs */
  886. /* pl pointer to current lineType structure */
  887. /* fNumb TRUE if /n specified */
  888. /**************************************************************************/
  889. void FC::pline (struct lineType *pl)
  890. {
  891. if (!fUnicode) {
  892. if (fNumb)
  893. DisplayMessage( MSG_FC_NUMBERED_DATA, NORMAL_MESSAGE, "%5d%s",
  894. pl->line, pl->text );
  895. else
  896. DisplayMessage( MSG_FC_DATA, NORMAL_MESSAGE, "%s", pl->text );
  897. } else {
  898. FSTRING f;
  899. if (fNumb)
  900. DisplayMessage( MSG_FC_NUMBERED_DATA, NORMAL_MESSAGE, "%5d%W",
  901. pl->line, f.Initialize(pl->wtext) );
  902. else
  903. DisplayMessage( MSG_FC_DATA, NORMAL_MESSAGE, "%W",
  904. f.Initialize(pl->wtext) );
  905. }
  906. }
  907. /*********************************************************************/
  908. /* Routine: ParseFileNames */
  909. /* */
  910. /* Function: Parses the two given filenames and then compares the */
  911. /* appropriate filenames. This routine handles wildcard */
  912. /* characters in both filenames. */
  913. /*********************************************************************/
  914. INT
  915. FC::ParseFileNames()
  916. {
  917. PATH File1;
  918. PATH File2;
  919. FSN_FILTER Filter;
  920. PWSTRING Name;
  921. PARRAY NodeArray;
  922. PITERATOR Iterator;
  923. PFSN_DIRECTORY Dir;
  924. PFSNODE File;
  925. PPATH ExpandedPath;
  926. PATH TmpPath;
  927. int result = SUCCESS;
  928. char *locale;
  929. if (!File1.Initialize( &_File1 ) ||
  930. !File2.Initialize( &_File2 ) ||
  931. !Filter.Initialize()) {
  932. DisplayMessage( MSG_FC_OUT_OF_MEMORY, ERROR_MESSAGE );
  933. return FAILURE;
  934. }
  935. if (!(Name = File1.QueryName())) {
  936. DisplayMessage( MSG_FC_UNABLE_TO_OPEN, ERROR_MESSAGE, "%W", File1.GetPathString() );
  937. return FILES_NOT_FOUND;
  938. }
  939. if (!Filter.SetFileName( Name ) ||
  940. !Filter.SetAttributes( (FSN_ATTRIBUTE)0, // ALL
  941. FSN_ATTRIBUTE_FILES, // ANY
  942. FSN_ATTRIBUTE_DIRECTORY )) { // NONE
  943. DELETE( Name );
  944. DisplayMessage( MSG_FC_OUT_OF_MEMORY, ERROR_MESSAGE );
  945. return FAILURE;
  946. }
  947. DELETE( Name );
  948. if (!TmpPath.Initialize( &File1, TRUE ) ||
  949. !TmpPath.TruncateBase()) {
  950. DisplayMessage( MSG_FC_OUT_OF_MEMORY, ERROR_MESSAGE );
  951. return FAILURE;
  952. }
  953. if ( !(Dir = SYSTEM::QueryDirectory( &TmpPath )) ||
  954. !(NodeArray = Dir->QueryFsnodeArray( &Filter )) ||
  955. !(Iterator = NodeArray->QueryIterator()) ||
  956. !(File = (PFSNODE)Iterator->GetNext())
  957. ) {
  958. DisplayMessage( MSG_FC_UNABLE_TO_OPEN, ERROR_MESSAGE, "%W", File1.GetPathString() );
  959. return FILES_NOT_FOUND;
  960. }
  961. Iterator->Reset();
  962. while ( File = (PFSNODE)Iterator->GetNext() ) {
  963. PWSTRING Name1;
  964. PWSTRING Name2;
  965. if (!(Name1 = File->QueryName())) {
  966. DisplayMessage( MSG_FC_UNABLE_TO_OPEN, ERROR_MESSAGE, "%W", File->GetPath()->GetPathString() );
  967. return FILES_NOT_FOUND;
  968. }
  969. if ( _File2.HasWildCard() ) {
  970. if ( !(ExpandedPath = _File2.QueryWCExpansion( (PPATH)File->GetPath() ))) {
  971. if (!(Name2 = _File2.QueryName())) {
  972. DisplayMessage( MSG_FC_UNABLE_TO_OPEN, ERROR_MESSAGE, "%W", _File2.GetPathString() );
  973. return FILES_NOT_FOUND;
  974. }
  975. DisplayMessage( MSG_FC_CANT_EXPAND_TO_MATCH, ERROR_MESSAGE, "%W%W", Name1, Name2 );
  976. DELETE( Name2 );
  977. DELETE( Name1 );
  978. DELETE( Iterator );
  979. DELETE( NodeArray );
  980. DELETE( Dir );
  981. return FAILURE;
  982. }
  983. } else {
  984. if ( !(ExpandedPath = NEW PATH) ||
  985. !ExpandedPath->Initialize( &_File2 ) ) {
  986. DisplayMessage( MSG_FC_OUT_OF_MEMORY, ERROR_MESSAGE );
  987. DELETE( Name1 );
  988. DELETE( Iterator );
  989. DELETE( NodeArray );
  990. DELETE( Dir );
  991. return FAILURE;
  992. }
  993. }
  994. if (!(Name2 = ExpandedPath->QueryName())) {
  995. DisplayMessage( MSG_FC_UNABLE_TO_OPEN, ERROR_MESSAGE, "%W", ExpandedPath->GetPathString() );
  996. return FILES_NOT_FOUND;
  997. }
  998. if (!File1.SetName( Name1 ) ||
  999. !File2.SetName( Name2 )) {
  1000. DisplayMessage( MSG_FC_OUT_OF_MEMORY, ERROR_MESSAGE );
  1001. return FAILURE;
  1002. }
  1003. switch (comp( File1.GetPathString(), File2.GetPathString() )) {
  1004. case FAILURE:
  1005. return FAILURE;
  1006. case SUCCESS:
  1007. break;
  1008. case FILES_ARE_DIFFERENT:
  1009. result |= FILES_ARE_DIFFERENT;
  1010. break;
  1011. case FILES_NOT_FOUND:
  1012. result |= FILES_NOT_FOUND;
  1013. break;
  1014. case FILES_OFFLINE:
  1015. result |= FILES_OFFLINE;
  1016. break;
  1017. default:
  1018. DebugAssert(FALSE);
  1019. }
  1020. DELETE( Name2 );
  1021. DELETE( Name1 );
  1022. DELETE( ExpandedPath );
  1023. }
  1024. // Print a warning in case that offline files were skipped
  1025. if (fOfflineSkipped) {
  1026. DisplayMessage(MSG_FC_OFFLINE_FILES_SKIPPED, ERROR_MESSAGE);
  1027. }
  1028. DELETE( Iterator );
  1029. NodeArray->DeleteAllMembers();
  1030. DELETE( NodeArray );
  1031. DELETE( Dir );
  1032. return result;
  1033. }
  1034. /*********************************************************************/
  1035. /* Routine: comp */
  1036. /* */
  1037. /* Function: Compares the two files. */
  1038. /*********************************************************************/
  1039. INT
  1040. FC::comp(PCWSTRING file1, PCWSTRING file2)
  1041. {
  1042. DisplayMessage( MSG_FC_COMPARING_FILES, NORMAL_MESSAGE, "%W%W", file1, file2 );
  1043. if (fBinary) {
  1044. return BinaryCompare (file1, file2);
  1045. } else {
  1046. return LineCompare (file1, file2);
  1047. }
  1048. }
  1049. #if 0
  1050. /* returns line from file (no CRLFs); returns NULL if EOF */
  1051. FC::fgetl ( char *buf, int len, FILE *fh)
  1052. {
  1053. register int c;
  1054. register char *p;
  1055. /* remember NUL at end */
  1056. len--;
  1057. p = buf;
  1058. while (len) {
  1059. c = getc (fh);
  1060. if (c == EOF || c == '\n')
  1061. break;
  1062. #if MSDOS
  1063. if (c != '\r')
  1064. #endif
  1065. if (c != '\t') {
  1066. *p++ = (char)c;
  1067. len--;
  1068. }
  1069. else {
  1070. c = min (8 - ((int)(p-buf) & 0x0007), len);
  1071. memset(p, ' ', c);
  1072. p += c;
  1073. len -= c;
  1074. }
  1075. }
  1076. *p = 0;
  1077. return ! ( (c == EOF) && (p == buf) );
  1078. }
  1079. #endif