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.

631 lines
19 KiB

  1. /* fcom.c - file compare
  2. *
  3. * 4/30/86 daniel lipkie LineCompare, at end, if NOT Same then error
  4. * 5/01/86 daniel lipkie SYM files treaded as binary
  5. * if (quiet mode) then exit(1) on FIRST difference
  6. * 27-May-1986 mz Make line arrays dynamically allocated at read
  7. * time.
  8. * Make default size be 300 lines.
  9. * 05-Aug-1986 DANL MAX default line size 255
  10. * 10-Feb-1987 mz Make /m imply /t
  11. * 10-Jun-1987 danl fill -> fillbuf
  12. * 09-Nov-1987 mz Fix 0D0D0A bug
  13. * Fix different at line 2 bug
  14. * 25-Nov-1987 mz All errors to stderr
  15. * 30-Nov-1987 mz Resync fails dumps entire file
  16. * 21-Jul-1988 bw Don't GP fault on empty test files
  17. * 06-Aug-1990 davegi Changed Move to memmove (OS/2 2.0)
  18. *
  19. * Fcom compares two files in either a line-by-line mode or in a strict
  20. * byte-by-byte mode.
  21. *
  22. * The byte-by-byte mode is simple; merely read both files and print the
  23. * offsets where they differ and the contents.
  24. *
  25. * The line compare mode attempts to isolate differences in ranges of lines.
  26. * Two buffers of lines are read and compared. No hashing of lines needs
  27. * to be done; hashing only speedily tells you when things are different,
  28. * not the same. Most files run through this are expected to be largely
  29. * the same. Thus, hashing buys nothing.
  30. *
  31. * [0] Fill buffers
  32. * [1] If both buffers are empty then
  33. * [1.1] Done
  34. * [2] Adjust buffers so 1st differing lines are at top.
  35. * [3] If buffers are empty then
  36. * [3.1] Goto [0]
  37. *
  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. *
  41. * [4] xd = yd = FALSE
  42. * [5] xc = yc = 1
  43. * [6] xp = yp = 1
  44. * [7] If buffer1[xc] and buffer2[yp] begin a "sync" range then
  45. * [7.1] Output lines 1 through xc-1 in buffer 1
  46. * [7.2] Output lines 1 through yp-1 in buffer 2
  47. * [7.3] Adjust buffer 1 so line xc is at beginning
  48. * [7.4] Adjust buffer 2 so line yp is at beginning
  49. * [7.5] Goto [0]
  50. * [8] If buffer1[xp] and buffer2[yc] begin a "sync" range then
  51. * [8.1] Output lines 1 through xp-1 in buffer 1
  52. * [8.2] Output lines 1 through yc-1 in buffer 2
  53. * [8.3] Adjust buffer 1 so line xp is at beginning
  54. * [8.4] Adjust buffer 2 so line yc is at beginning
  55. * [8.5] Goto [0]
  56. * [9] xp = xp + 1
  57. * [10] if xp > xc then
  58. * [10.1] xp = 1
  59. * [10.2] xc = xc + 1
  60. * [10.3] if xc > number of lines in buffer 1 then
  61. * [10.4] xc = number of lines
  62. * [10.5] xd = TRUE
  63. * [11] if yp > yc then
  64. * [11.1] yp = 1
  65. * [11.2] yc = yc + 1
  66. * [11.3] if yc > number of lines in buffer 2 then
  67. * [11.4] yc = number of lines
  68. * [11.5] yd = TRUE
  69. * [12] if not xd or not yd then
  70. * [12.1] goto [6]
  71. *
  72. * At this point there is no possible match between the buffers. For
  73. * simplicity, we punt.
  74. *
  75. * [13] Display error message.
  76. *
  77. * Certain flags may be set to modify the behavior of the comparison:
  78. *
  79. * -a abbreviated output. Rather than displaying all of the modified
  80. * ranges, just display the beginning, ... and the ending difference
  81. * -b compare the files in binary (or byte-by-byte) mode. This mode is
  82. * default on .EXE, .OBJ, .SYM, .LIB, .COM, .BIN, and .SYS files
  83. * -c ignore case on compare (cmp = strcmpi instead of strcmp)
  84. * -l compare files in line-by-line mode
  85. * -lb n set the size of the internal line buffer to n lines from default
  86. * of 300
  87. * -m merge the input files into one for output. Use extention to
  88. * indicate what kind of separators to use.
  89. * -n output the line number also
  90. * -NNNN set the number of lines to resynchronize to NNNN which defaults
  91. * to 2
  92. * -w ignore blank lines and white space (ignore len 0, use strcmps)
  93. * -t do not untabify (use fgets instead of fgetl)
  94. */
  95. #include <malloc.h>
  96. #include <stdio.h>
  97. #include <string.h>
  98. #include <process.h>
  99. #include <ctype.h>
  100. #include <math.h>
  101. #include <stdlib.h>
  102. #include <windows.h>
  103. #include <tools.h>
  104. int ctSync = -1; /* number of lines required to sync */
  105. int cLine = -1; /* number of lines in internal buffs */
  106. flagType fAbbrev = FALSE; /* abbreviated output */
  107. flagType fBinary = FALSE; /* binary comparison */
  108. flagType fLine = FALSE; /* line comparison */
  109. flagType fNumb = FALSE; /* display line numbers */
  110. flagType fCase = TRUE; /* case is significant */
  111. flagType fIgnore = FALSE; /* ignore spaces and blank lines */
  112. flagType fQuiet = FALSE; /* TRUE => no messages output */
  113. flagType fMerge = FALSE; /* TRUE => merge files onto stdout */
  114. int debug;
  115. #define D_COMP 0x0001
  116. #define D_CCALL 0x0002
  117. #define D_RESYNC 0x0004
  118. #define D_CALL 0x0008
  119. #define D_SYNC 0x0010
  120. struct lineType {
  121. int line; /* line number */
  122. char *text; /* body of line */
  123. };
  124. struct lineType *buffer1, *buffer2;
  125. /*
  126. * Forward Function Declarations
  127. */
  128. void usage( char *, int );
  129. int fillbuf( struct lineType *, FILE *, int, int * );
  130. flagType compare( int, int, int, register int, register int);
  131. int BinaryCompare( char *, char * );
  132. void pline(struct lineType *);
  133. void dump(struct lineType *, int, int);
  134. int adjust (struct lineType *, int, int);
  135. void LineCompare (char *, char *);
  136. char * (__cdecl *funcRead) (char *, int, FILE *);
  137. /* function to use to read lines */
  138. int (__cdecl *fCmp)(const char *, const char *);
  139. /* function to use to compare lines */
  140. char line[MAXLINELEN]; /* single line buffer */
  141. char *extBin[] = { ".EXE", ".OBJ", ".SYM", ".LIB", ".COM", ".BIN",
  142. ".SYS", NULL };
  143. void
  144. usage (p, erc)
  145. char *p;
  146. int erc;
  147. {
  148. if (!fQuiet) {
  149. if (p)
  150. fprintf (stderr, "fcom: %s\n", p);
  151. else
  152. fprintf (stderr, "usage: fcom [/a] [/b] [/c] [/l] [/lb n] [/m] [/n] [/NNNN] [/w] [/t] file1 file2\n");
  153. }
  154. exit(erc);
  155. }
  156. /* return number of lines read in
  157. *
  158. * pl line buffer to fill
  159. * fh handle to read
  160. * ct max number to read
  161. * plnum line number counter to use
  162. *
  163. * returns number of lines read
  164. */
  165. int fillbuf( struct lineType *pl, FILE *fh, int ct, int *plnum )
  166. {
  167. char *line;
  168. int i, l;
  169. if ((line = malloc (MAXLINELEN)) == NULL)
  170. usage ("out of memory", 2);
  171. if (TESTFLAG (debug, D_CALL))
  172. printf ("fillbuf (%p, %p, %d)\n", pl, fh, ct);
  173. i = 0;
  174. while (ct-- && (*funcRead) (line, MAXLINELEN, fh) != NULL) {
  175. if (pl->text != NULL)
  176. free (pl->text);
  177. l = strlen (line);
  178. if ((pl->text = malloc (l+1)) == NULL)
  179. usage ("out of memory", 2);
  180. // djg Move ((char far *)line, (char far *) (pl->text), l+1);
  181. memmove ((char *) (pl->text), (char *)line, l+1);
  182. if (funcRead == fgets)
  183. pl->text[l-2] = 0;
  184. if (fIgnore && !strcmps (pl->text, ""))
  185. pl->text[0] = 0;
  186. if (l != 0 || !fIgnore) {
  187. pl->line = ++*plnum;
  188. pl++;
  189. i++;
  190. }
  191. }
  192. if (TESTFLAG (debug, D_CALL))
  193. printf ("fillbuf returns %d\n", i);
  194. free (line);
  195. return i;
  196. }
  197. /* compare a range of lines
  198. *
  199. * l1, l2 number of lines in each line buffer
  200. * s1, s2 beginning location in each buffer to begin compare
  201. * ct number of contiguous lines to compare
  202. */
  203. flagType compare (l1, s1, l2, s2, ct)
  204. int l1, l2, ct;
  205. register int s1, s2;
  206. {
  207. if (TESTFLAG (debug, D_CCALL))
  208. printf ("compare (%d, %d, %d, %d, %d)\n", l1, s1, l2, s2, ct);
  209. if (ct <= 0 || s1+ct > l1 || s2+ct > l2)
  210. return FALSE;
  211. while (ct--) {
  212. if (TESTFLAG (debug, D_COMP))
  213. printf ("'%s' == '%s'? ", buffer1[s1].text, buffer2[s2].text);
  214. if ((*fCmp)(buffer1[s1++].text, buffer2[s2++].text)) {
  215. if (TESTFLAG (debug, D_CCALL))
  216. printf ("No\n");
  217. return FALSE;
  218. }
  219. }
  220. if (TESTFLAG (debug, D_CCALL))
  221. printf ("Yes\n");
  222. return TRUE;
  223. }
  224. BinaryCompare (f1, f2)
  225. char *f1, *f2;
  226. {
  227. register int c1, c2;
  228. long pos;
  229. FILE *fh1, *fh2;
  230. flagType fSame;
  231. fSame = TRUE;
  232. if ((fh1 = fopen (f1, "rb")) == NULL) {
  233. sprintf (line, "cannot open %s - %s", f1, error ());
  234. usage (line, 2);
  235. }
  236. if ((fh2 = fopen (f2, "rb")) == NULL) {
  237. sprintf (line, "cannot open %s - %s", f2, error ());
  238. usage (line, 2);
  239. }
  240. pos = 0L;
  241. while (TRUE) {
  242. if ((c1 = getc (fh1)) != EOF)
  243. if ((c2 = getc (fh2)) != EOF)
  244. if (c1 == c2)
  245. ;
  246. else {
  247. fSame = FALSE;
  248. if (fQuiet)
  249. exit(1);
  250. else
  251. printf ("%08lx: %02x %02x\n", pos, c1, c2);
  252. }
  253. else {
  254. sprintf (line, "%s longer than %s", f1, f2);
  255. usage (line, 1);
  256. }
  257. else
  258. if ((c2 = getc (fh2)) == EOF)
  259. if (fSame)
  260. usage ("no differences encountered", 0);
  261. else
  262. exit (1);
  263. else {
  264. sprintf (line, "%s longer than %s", f2, f1);
  265. usage (line, 1);
  266. }
  267. pos++;
  268. }
  269. return( 0 );
  270. }
  271. /* print out a single line */
  272. void pline (pl)
  273. struct lineType *pl;
  274. {
  275. if (fNumb)
  276. printf ("%5d: ", pl->line);
  277. printf ("%s\n", pl->text);
  278. }
  279. /* output a range of lines */
  280. void dump( struct lineType *pl, int start, int end )
  281. {
  282. if (fAbbrev && end-start > 2) {
  283. pline (pl+start);
  284. printf ("...\n");
  285. pline (pl+end);
  286. }
  287. else
  288. while (start <= end)
  289. pline (pl+start++);
  290. }
  291. /* adjust returns number of lines in buffer
  292. *
  293. * pl line buffer to be adjusted
  294. * ml number of line in line buffer
  295. * lt number of lines to scroll
  296. */
  297. adjust( struct lineType *pl, int ml, int lt)
  298. {
  299. int i;
  300. if (TESTFLAG (debug, D_CALL))
  301. printf ("adjust (%p, %d, %d) = ", pl, ml, lt);
  302. if (TESTFLAG (debug, D_CALL))
  303. printf ("%d\n", ml-lt);
  304. if (ml <= lt)
  305. return 0;
  306. if (TESTFLAG (debug, D_CALL))
  307. printf ("move (%p, %p, %04x)\n", &pl[lt], &pl[0], sizeof (*pl)*(ml-lt));
  308. /* buffer has 0..lt-1 lt..ml
  309. * we free 0..lt-1
  310. */
  311. for (i = 0; i < lt; i++)
  312. if (pl[i].text != NULL)
  313. free (pl[i].text);
  314. /* buffer is 0..0 lt..ml
  315. * compact to lt..ml ???
  316. */
  317. // djg Move ((char far *)&pl[lt], (char far *)&pl[0], sizeof (*pl)*(ml-lt));
  318. memmove ((char *)&pl[0], (char *)&pl[lt], sizeof (*pl)*(ml-lt));
  319. /* buffer is lt..ml ??
  320. * fill to be lt..ml 0..0
  321. */
  322. for (i = ml-lt; i < ml; i++)
  323. pl[i].text = NULL;
  324. return ml-lt;
  325. }
  326. void LineCompare (f1, f2)
  327. char *f1, *f2;
  328. {
  329. FILE *fh1, *fh2;
  330. int l1, l2, i, xp, yp, xc, yc;
  331. flagType xd, yd, fSame, fFirstLineDifferent;
  332. int line1, line2;
  333. fFirstLineDifferent = TRUE;
  334. fSame = TRUE;
  335. if ((fh1 = fopen (f1, "rb")) == NULL) {
  336. sprintf (line, "cannot open %s - %s", f1, error ());
  337. usage (line, 2);
  338. }
  339. if ((fh2 = fopen (f2, "rb")) == NULL) {
  340. sprintf (line, "cannot open %s - %s", f2, error ());
  341. usage (line, 2);
  342. }
  343. if ((buffer1 = (struct lineType *)calloc (cLine, sizeof *buffer1)) == NULL ||
  344. (buffer2 = (struct lineType *)calloc (cLine, sizeof *buffer1)) == NULL)
  345. usage ("out of memory\n", 2);
  346. l1 = l2 = 0;
  347. line1 = line2 = 0;
  348. l0: if (TESTFLAG (debug, D_SYNC))
  349. printf ("At scan beginning\n");
  350. if (fQuiet && !fSame)
  351. exit(1);
  352. l1 += fillbuf (buffer1+l1, fh1, cLine-l1, &line1);
  353. l2 += fillbuf (buffer2+l2, fh2, cLine-l2, &line2);
  354. if (l1 == 0 && l2 == 0) {
  355. if (fSame)
  356. usage ("no differences encountered", 0);
  357. else
  358. usage ("differences encountered", 1);
  359. }
  360. /* find first line that differs in buffer
  361. */
  362. xc = min (l1, l2);
  363. for (i=0; i < xc; i++)
  364. if (!compare (l1, i, l2, i, 1))
  365. break;
  366. if (fMerge)
  367. dump (buffer2, 0, i-1);
  368. /* If we are different at a place other than at the top then we know
  369. * that there will be a matching line at the head of the buffer
  370. */
  371. if (i != 0)
  372. fFirstLineDifferent = FALSE;
  373. /* if we found one at all, then adjust all buffers so last matching
  374. * line is at top. Note that if we are doing this for the first buffers
  375. * worth in the file then the top lines WON'T MATCH
  376. */
  377. if (i != xc)
  378. i = max (i-1, 0);
  379. l1 = adjust (buffer1, l1, i);
  380. l2 = adjust (buffer2, l2, i);
  381. /* if we've matched the entire buffers-worth then go back and fill some
  382. * more
  383. */
  384. if (l1 == 0 && l2 == 0) {
  385. fFirstLineDifferent = FALSE;
  386. goto l0;
  387. }
  388. /* Fill up the buffers as much as possible; the next match may occur
  389. * AFTER the current set of buffers
  390. */
  391. l1 += fillbuf (buffer1+l1, fh1, cLine-l1, &line1);
  392. l2 += fillbuf (buffer2+l2, fh2, cLine-l2, &line2);
  393. if (TESTFLAG (debug, D_SYNC))
  394. printf ("buffers are adjusted, %d, %d remain\n", l1, l2);
  395. xd = yd = FALSE;
  396. xc = yc = 1;
  397. xp = yp = 1;
  398. /* begin trying to match the buffers
  399. */
  400. l6: if (TESTFLAG (debug, D_RESYNC))
  401. printf ("Trying resync %d,%d %d,%d\n", xc, xp, yc, yp);
  402. i = min (l1-xc,l2-yp);
  403. i = min (i, ctSync);
  404. if (compare (l1, xc, l2, yp, i)) {
  405. fSame = FALSE;
  406. if (fMerge) {
  407. printf ("#if OLDVERSION\n");
  408. dump (buffer1, fFirstLineDifferent ? 0 : 1, xc-1);
  409. printf ("#else\n");
  410. dump (buffer2, fFirstLineDifferent ? 0 : 1, yp-1);
  411. printf ("#endif\n");
  412. }
  413. else
  414. if (!fQuiet) {
  415. printf ("***** %s\n", f1);
  416. dump (buffer1, 0, xc);
  417. printf ("***** %s\n", f2);
  418. dump (buffer2, 0, yp);
  419. printf ("*****\n\n");
  420. }
  421. if (TESTFLAG (debug, D_SYNC))
  422. printf ("Sync at %d,%d\n", xc, yp);
  423. l1 = adjust (buffer1, l1, xc);
  424. l2 = adjust (buffer2, l2, yp);
  425. fFirstLineDifferent = FALSE;
  426. goto l0;
  427. }
  428. i = min (l1-xp, l2-yc);
  429. i = min (i, ctSync);
  430. if (compare (l1, xp, l2, yc, i)) {
  431. fSame = FALSE;
  432. if (fMerge) {
  433. printf ("#if OLDVERSION\n");
  434. dump (buffer1, fFirstLineDifferent ? 0 : 1, xp-1);
  435. printf ("#else\n");
  436. dump (buffer2, fFirstLineDifferent ? 0 : 1, yc-1);
  437. printf ("#endif\n");
  438. }
  439. else
  440. if (!fQuiet) {
  441. printf ("***** %s\n", f1);
  442. dump (buffer1, 0, xp);
  443. printf ("***** %s\n", f2);
  444. dump (buffer2, 0, yc);
  445. printf ("*****\n\n");
  446. }
  447. if (TESTFLAG (debug, D_SYNC))
  448. printf ("Sync at %d,%d\n", xp, yc);
  449. l1 = adjust (buffer1, l1, xp);
  450. l2 = adjust (buffer2, l2, yc);
  451. fFirstLineDifferent = FALSE;
  452. goto l0;
  453. }
  454. if (++xp > xc) {
  455. xp = 1;
  456. if (++xc >= l1) {
  457. xc = l1;
  458. xd = TRUE;
  459. }
  460. }
  461. if (++yp > yc) {
  462. yp = 1;
  463. if (++yc >= l2) {
  464. yc = l1;
  465. yd = TRUE;
  466. }
  467. }
  468. if (!xd || !yd)
  469. goto l6;
  470. fSame = FALSE;
  471. if (fMerge) {
  472. if (l1 >= cLine || l2 >= cLine)
  473. fprintf (stderr, "resync failed. Files are too different\n");
  474. printf ("#if OLDVERSION\n");
  475. do {
  476. dump (buffer1, 0, l1-1);
  477. l1 = adjust (buffer1, l1, l1);
  478. } while (l1 += fillbuf (buffer1+l1, fh1, cLine-l1, &line1));
  479. printf ("#else\n");
  480. do {
  481. dump (buffer2, 0, l2-1);
  482. l2 = adjust (buffer2, l2, l2);
  483. } while (l2 += fillbuf (buffer2+l2, fh2, cLine-l2, &line2));
  484. printf ("#endif\n");
  485. }
  486. else
  487. if (!fQuiet) {
  488. if (l1 >= cLine || l2 >= cLine)
  489. fprintf (stderr, "resync failed. Files are too different\n");
  490. printf ("***** %s\n", f1);
  491. do {
  492. dump (buffer1, 0, l1-1);
  493. l1 = adjust (buffer1, l1, l1);
  494. } while (l1 += fillbuf (buffer1+l1, fh1, cLine-l1, &line1));
  495. printf ("***** %s\n", f2);
  496. do {
  497. dump (buffer2, 0, l2-1);
  498. l2 = adjust (buffer2, l2, l2);
  499. } while (l2 += fillbuf (buffer2+l2, fh2, cLine-l2, &line2));
  500. printf ("*****\n\n");
  501. }
  502. exit (1);
  503. }
  504. __cdecl main (c, v)
  505. int c;
  506. char *v[];
  507. {
  508. int i;
  509. funcRead = fgetl;
  510. ConvertAppToOem( c, v );
  511. if (c == 1)
  512. usage (NULL, 2);
  513. SHIFT(c,v);
  514. while (fSwitChr (**v)) {
  515. if (!strcmp (*v+1, "a"))
  516. fAbbrev = TRUE;
  517. else
  518. if (!strcmp (*v+1, "b"))
  519. fBinary = TRUE;
  520. else
  521. if (!strcmp (*v+1, "c"))
  522. fCase = FALSE;
  523. else
  524. if (!strncmp (*v+1, "d", 1))
  525. debug = atoi (*v+2);
  526. else
  527. if (!strcmp (*v+1, "l"))
  528. fLine = TRUE;
  529. else
  530. if (!strcmp (*v+1, "lb")) {
  531. SHIFT(c,v);
  532. cLine = ntoi (*v, 10);
  533. }
  534. else
  535. if (!strcmp (*v+1, "m")) {
  536. fMerge = TRUE;
  537. funcRead = fgets;
  538. }
  539. else
  540. if (!strcmp (*v+1, "n"))
  541. fNumb = TRUE;
  542. else
  543. if (!strcmp (*v+1, "q"))
  544. fQuiet = TRUE;
  545. else
  546. if (*strbskip (*v+1, "0123456789") == '\0')
  547. ctSync = ntoi (*v+1, 10);
  548. else
  549. if (!strcmp (*v+1, "t"))
  550. funcRead = fgets;
  551. else
  552. if (!strcmp (*v+1, "w"))
  553. fIgnore = TRUE;
  554. else
  555. usage (NULL, 2);
  556. SHIFT(c,v);
  557. }
  558. if (c != 2)
  559. usage (NULL, 2);
  560. if (ctSync != -1)
  561. fLine = TRUE;
  562. else
  563. ctSync = 2;
  564. if (cLine == -1)
  565. cLine = 300;
  566. if (!fBinary && !fLine) {
  567. extention (v[0], line);
  568. for (i = 0; extBin[i]; i++)
  569. if (!_strcmpi (extBin[i], line))
  570. fBinary = TRUE;
  571. if (!fBinary)
  572. fLine = TRUE;
  573. }
  574. if (fBinary && (fLine || fNumb))
  575. usage ("incompatable switches", 2);
  576. if (fIgnore)
  577. if (fCase)
  578. fCmp = strcmps;
  579. else
  580. fCmp = strcmpis;
  581. else
  582. if (fCase)
  583. fCmp = strcmp;
  584. else
  585. fCmp = _strcmpi;
  586. if (fBinary)
  587. BinaryCompare (v[0], v[1]);
  588. else
  589. LineCompare (v[0], v[1]);
  590. return( 0 );
  591. }