summaryrefslogtreecommitdiff
path: root/v4.0/src/CMD/FC/FC.C
diff options
context:
space:
mode:
authorGravatar Mark Zbikowski2024-04-25 21:24:10 +0100
committerGravatar Microsoft Open Source2024-04-25 22:32:27 +0000
commit2d04cacc5322951f187bb17e017c12920ac8ebe2 (patch)
tree80ee017efa878dfd5344b44249e6a241f2a7f6e2 /v4.0/src/CMD/FC/FC.C
parentMerge pull request #430 from jpbaltazar/typoptbr (diff)
downloadms-dos-main.tar.gz
ms-dos-main.tar.xz
ms-dos-main.zip
MZ is back!HEADmain
Diffstat (limited to 'v4.0/src/CMD/FC/FC.C')
-rw-r--r--v4.0/src/CMD/FC/FC.C864
1 files changed, 864 insertions, 0 deletions
diff --git a/v4.0/src/CMD/FC/FC.C b/v4.0/src/CMD/FC/FC.C
new file mode 100644
index 0000000..d1dc0f6
--- /dev/null
+++ b/v4.0/src/CMD/FC/FC.C
@@ -0,0 +1,864 @@
1/* file compare
2
3 Fcom compares two files in either a line-by-line mode or in a strict
4 byte-by-byte mode.
5
6 The byte-by-byte mode is simple; merely read both files and print the
7 offsets where they differ and the contents.
8
9 The line compare mode attempts to isolate differences in ranges of lines.
10 Two buffers of lines are read and compared. No hashing of lines needs
11 to be done; hashing only speedily tells you when things are different,
12 not the same. Most files run through this are expected to be largely
13 the same. Thus, hashing buys nothing.
14
15
16***********************************************************************
17The algorithm that immediately follows does not work. There is an error
18somewhere in the range of lines 11 on. An alternative explanation follows.
19 KGS
20************************************************************************
21
22 [0] Fill buffers
23 [1] If both buffers are empty then
24 [1.1] Done
25 [2] Adjust buffers so 1st differing lines are at top.
26 [3] If buffers are empty then
27 [3.1] Goto [0]
28
29 This is the difficult part. We assume that there is a sequence of inserts,
30 deletes and replacements that will bring the buffers back into alignment.
31
32 [4] xd = yd = FALSE
33 [5] xc = yc = 1
34 [6] xp = yp = 1
35 [7] If buffer1[xc] and buffer2[yp] begin a "sync" range then
36 [7.1] Output lines 1 through xc-1 in buffer 1
37 [7.2] Output lines 1 through yp-1 in buffer 2
38 [7.3] Adjust buffer 1 so line xc is at beginning
39 [7.4] Adjust buffer 2 so line yp is at beginning
40 [7.5] Goto [0]
41 [8] If buffer1[xp] and buffer2[yc] begin a "sync" range then
42 [8.1] Output lines 1 through xp-1 in buffer 1
43 [8.2] Output lines 1 through yc-1 in buffer 2
44 [8.3] Adjust buffer 1 so line xp is at beginning
45 [8.4] Adjust buffer 2 so line yc is at beginning
46 [8.5] Goto [0]
47 [9] xp = xp + 1
48 [10] if xp > xc then
49 [10.1] xp = 1
50 [10.2] xc = xc + 1
51 [10.3] if xc > number of lines in buffer 1 then
52 [10.4] xc = number of lines
53 [10.5] xd = TRUE
54 [11] if yp > yc then
55 [11.1] yp = 1
56 [11.2] yc = yc + 1
57 [11.3] if yc > number of lines in buffer 2 then
58 [11.4] yc = number of lines
59 [11.5] yd = TRUE
60 [12] if not xd or not yd then
61 [12.1] goto [6]
62
63 At this point there is no possible match between the buffers. For
64 simplicity, we punt.
65
66 [13] Display error message.
67
68EXPLANATION 2
69
70 This is a variation of the Largest Common Subsequence problem. A
71 detailed explanation of this can be found on p 189 of Data Structures
72 and Algorithms by Aho Hopcroft and Ulman.
73
74
75
76 FC maintains two buffers within which it tries to find the Largest Common
77 Subsequence (The largest common subsequence is simply the pattern in
78 buffer1 that yields the most matches with the pattern in buffer2, or the
79 pattern in buffer2 that yields the most matches with the pattern in buffer1)
80
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
85 Two indices into each buffer are maintained:
86
87 xc, yc == point to the last line that has been scanned up to now
88
89 xp, yp == point to the first line that has not been exhaustively
90 compared to lines 0 - #c in the other buffer.
91
92 FC now makes a second simplifying assumption:
93 It is unnecessary to do any calculations on lines that are equal.
94
95 Hence FC scans File1 and File two line by line until a difference is
96 encountered.
97
98
99 When a difference is encountered the two buffers are filled such that
100 the line containing the first difference heads the buffer. The following
101 exhaustive search algorithm is applied to find the first "sync" occurance.
102 (The below is simplified to use == for comparison. In practice more than
103 one line needs to match for a "sync" to be established).
104
105
106 FOR xc,yc = 1; xc,yx <= sizeof( BUFFERS ); xc++, yc++
107
108 FOR xp,yp = 1; xp,yp <= xc,yc; xp++, yp++
109
110 IF ( BUFFER1[xp] == BUFFER2[yc] )
111
112 Then the range of lines BUFFER1[ 1 ... xp ] and
113 BUFFER2[ 1 ... yc ] need to be deleted for the
114 two files to be equal. Therefore DISPLAY these
115 ranges, and begin scanning both files starting at
116 the matching lines.
117 FI
118
119 IF ( BUFFER1[yp] == BUFFER2[xc] )
120
121 Then the range of lines BUFFER2[ 1 ... yp ] and
122 BUFFER1[ 1 ... xc ] need to be deleted for the
123 two files to be equal. Therefore DISPLAY these
124 ranges, and begin scanning both files starting at
125 the matching lines.
126 FI
127 FOREND
128 FOREND
129
130 If a match is not found within the buffers, the message "RESYNC FAILED"
131 is issued and further comparison is aborted since there is no valid way
132 to find further matching lines.
133
134
135
136
137END EXPLANATION 2
138
139
140
141
142
143 Certain flags may be set to modify the behavior of the comparison:
144
145 -a abbreviated output. Rather than displaying all of the modified
146 ranges, just display the beginning, ... and the ending difference
147 -b compare the files in binary (or byte-by-byte) mode. This mode is
148 default on .EXE, .OBJ, .LIB, .COM, .BIN, and .SYS files
149 -c ignore case on compare (cmp = strcmpi instead of strcmp)
150 -l compare files in line-by-line mode
151 -lb n set the size of the internal line buffer to n lines from default
152 of 100
153 -w ignore blank lines and white space (ignore len 0, use strcmps)
154 -t do not untabify (use fgets instead of fgetl)
155 -n output the line number also
156 -NNNN set the number of lines to resynchronize to n which defaults
157 to 2. Failure to have this value set correctly can result in
158 odd output:
159 file1: file2:
160 abcdefg abcdefg
161 aaaaaaa aaaaaab
162 aaaaaaa aaaaaaa
163 aaaaaaa aaaaaaa
164 abcdefg abcdefg
165
166 with default sync of 2 yields: with sync => 3 yields:
167
168 *****f1 *****f1
169 abcdefg abcdefg
170 aaaaaaa aaaaaaa
171 *****f2 aaaaaaa
172 abcdefg *****f2
173 aaaaaab abcdefg
174 aaaaaaa aaaaaab
175 aaaaaaa
176 *****f1
177 aaaaaaa
178 aaaaaaa
179 abcdefg
180 *****f2
181 aaaaaaa
182 abcdefg
183
184
185
186
187
188
189WARNING:
190 This program makes use of GOTO's and hence is not as straightforward
191 as it could be! CAVEAT PROGRAMMER.
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207 */
208
209
210#include "tools.h"
211#include "fc.h"
212
213/* #define DEBUG FALSE */
214
215extern int fgetl(),
216 strcmp ();
217
218extern byte toupper ();
219
220int (*funcRead) (), /* function to use to read lines */
221 (*fCmp) (); /* function to use to compare lines */
222
223extern byte BadSw[],
224 Bad_ver[],
225 BadOpn[],
226 LngFil[],
227 NoDif[],
228 NoMem[],
229 UseMes[],
230 ReSyncMes[];
231
232int ctSync = -1, /* number of lines required to sync */
233 cLine = -1; /* number of lines in internal buffs */
234
235flagType fAbbrev = FALSE, /* abbreviated output */
236 fBinary = FALSE, /* binary comparison */
237 fLine = FALSE, /* line comparison */
238 fNumb = FALSE, /* display line numbers */
239 fCase = TRUE, /* case is significant */
240 fIgnore = FALSE; /* ignore spaces and blank lines */
241
242#ifdef DEBUG
243
244flagType fDebug = FALSE;
245#endif
246
247struct lineType *buffer1,
248 *buffer2;
249
250byte line[MAXARG]; /* single line buffer */
251
252byte *extBin[] = { ".EXE", ".OBJ", ".LIB",
253 ".COM", ".BIN", ".SYS", NULL };
254
255
256main (c, v)
257int c;
258byte *v[];
259{
260
261 int i;
262 int j;
263 int fileargs;
264 char *strpbrk(),
265 *slash;
266 char n[2][80];
267 char temp;
268
269
270
271 extern byte _osmajor, _osminor;
272 word version; /* _osmajor._osminor, used for */
273 /* version binding checks. */
274
275
276
277 /* Issue error message if DOS version is not within valid range. */
278 version = ((word)_osmajor << 8) + (word)_osminor;
279 if (( LOWVERSION > version) || (version > HIGHVERSION))
280 {
281 usage (Bad_ver, 1);
282 }
283
284 funcRead = (int (*) ())FNADDR(fgetl);
285
286 fileargs=0;
287
288 for (i=1; i < c ; i++)
289 {
290/**
291 * If argument doesn't begin with a /, parse a filename off of it
292 * then examine the argument for following switches.
293 *
294**/
295 if (*v[i] != '/')
296 {
297 slash= strpbrk( v[i],"/" );
298
299 if ( slash )
300 {
301 temp = *slash;
302 *slash='\0' ;
303 strcpy(n[fileargs++],v[i]);
304 *slash =temp ;
305 }
306 else
307 strcpy(n[fileargs++],v[i]);
308 }
309
310 for ( j=0 ; j < strlen( v[i] ) ; j++)
311 {
312 if(*(v[i]+j)=='/')
313 {
314 switch(toupper( *(v[i]+j+1)))
315 {
316 case 'A' :
317 fAbbrev = TRUE;
318 break;
319 case 'B' :
320 fBinary = TRUE;
321 break;
322 case 'C' :
323 fCase = FALSE;
324 break;
325#ifdef DEBUG
326 case 'D' :
327 fDebug = TRUE;
328 break;
329#endif
330 case 'W' :
331 fIgnore = TRUE;
332 break;
333 case 'L' :
334 if (toupper(*(v[i]+j+2))=='B')
335 {
336 cLine = ntoi ((v[i]+j+3),10);
337 break;
338 }
339 else
340 fLine = TRUE;
341 break;
342 case 'N' :
343 fNumb = TRUE;
344 break;
345 case 'T' :
346 funcRead =(int (*) ())FNADDR(fgets);
347 break;
348 default:
349 if (*strbskip((v[i]+j+1),"0123456789") == 0)
350 {
351 ctSync = ntoi ((v[i]+j+1), 10);
352 }
353 else
354 {
355 usage (NULL, 1);
356 }
357 } /* end switch */
358 } /* end if */
359 } /* end parse of argument for '/' */
360 } /* End ARGUMENT Search */
361
362
363
364 if (fileargs != 2)
365 usage (NULL, 1);
366
367 if (ctSync != -1)
368 fLine = TRUE;
369 else
370 ctSync = 2;
371
372 if (cLine == -1)
373 cLine = 100;
374
375 if (!fBinary && !fLine)
376 {
377 extention (n[0], line);
378
379 for (i = 0; extBin[i]; i++)
380 if (!strcmpi (extBin[i], line))
381 fBinary = TRUE;
382
383 if (!fBinary)
384 fLine = TRUE;
385 }
386
387 if (fBinary && (fLine || fNumb))
388 usage (BadSw, 1);
389
390 if (fIgnore)
391 {
392 if (fCase)
393 fCmp = FNADDR(strcmps);
394 else
395 fCmp = FNADDR(strcmpis);
396 }
397 else
398 {
399 if (fCase)
400 fCmp = FNADDR(strcmp);
401 else
402 fCmp = FNADDR(strcmpi);
403 }
404
405 if (fBinary)
406 BinaryCompare (n[0], n[1]);
407 else
408 LineCompare (n[0], n[1]);
409
410}
411
412usage (p, erc)
413unsigned char *p;
414{
415 if (p)
416 printf ("fc: %s\n", p);
417 else
418 printf (UseMes);
419
420 exit (erc);
421}
422
423BinaryCompare (f1, f2)
424unsigned char *f1, *f2;
425{
426 register int c1, c2;
427 long pos;
428 FILE *fh1, *fh2;
429 flagType fSame;
430
431 fSame = TRUE;
432
433 if ((fh1 = fopen (f1, "rb")) == NULL)
434 {
435 sprintf (line, BadOpn, f1, error ());
436 usage (line, 1);
437 }
438
439 if ((fh2 = fopen (f2, "rb")) == NULL)
440 {
441 sprintf (line, BadOpn, f2, error ());
442 usage (line, 1);
443 }
444 pos = 0L;
445
446 while (TRUE)
447 {
448 if ((c1 = getc (fh1)) != EOF)
449 {
450 if ((c2 = getc (fh2)) != EOF)
451 {
452 if (c1 == c2)
453 ;
454 else
455 {
456 fSame = FALSE;
457 printf ("%08lX: %02X %02X\n", pos, c1, c2);
458 }
459 }
460 else
461 {
462 sprintf (line, LngFil, f1, f2);
463 usage (line, 1);
464 }
465 }
466 else
467 {
468 if ((c2 = getc (fh2)) == EOF)
469 {
470 if (fSame)
471 usage (NoDif, 0);
472 else
473 exit (1);
474 }
475 else
476 {
477 sprintf (line, LngFil, f2, f1);
478 usage (line, 1);
479 }
480 }
481 pos++;
482 }
483}
484
485/* compare a range of lines */
486flagType compare (l1, s1, l2, s2, ct)
487int l1, l2, ct;
488register int s1, s2;
489{
490
491#ifdef DEBUG
492 if (fDebug)
493 printf ("compare (%d, %d, %d, %d, %d)\n", l1, s1, l2, s2, ct);
494#endif
495
496 if (ct == 0 || s1+ct > l1 || s2+ct > l2)
497 return FALSE;
498
499 while (ct--)
500 {
501
502#ifdef DEBUG
503 if (fDebug)
504 printf ("'%s' == '%s'? ", buffer1[s1].text, buffer2[s2].text);
505#endif
506
507 if ((*fCmp)(buffer1[s1++].text, buffer2[s2++].text))
508 {
509
510#ifdef DEBUG
511 if (fDebug)
512 printf ("No\n");
513#endif
514 return FALSE;
515 }
516 }
517
518#ifdef DEBUG
519 if (fDebug)
520 printf ("Yes\n");
521#endif
522
523 return TRUE;
524}
525
526LineCompare (f1, f2)
527unsigned char *f1, *f2;
528{
529 FILE *fh1, *fh2;
530 int l1, l2, i, xp, yp, xc, yc;
531 flagType xd, yd, fSame;
532 int line1, line2;
533
534 fSame = TRUE;
535
536 if ((fh1 = fopen (f1, "rb")) == NULL)
537 {
538 sprintf (line, BadOpn, f1, error ());
539 usage (line, 1);
540 }
541
542 if ((fh2 = fopen (f2, "rb")) == NULL)
543 {
544 sprintf (line, BadOpn, f2, error ());
545 usage (line, 1);
546 }
547
548 if ((buffer1 = (struct lineType *)malloc (cLine * (sizeof *buffer1))) == NULL ||
549 (buffer2 = (struct lineType *)malloc (cLine * (sizeof *buffer1))) == NULL)
550 usage (NoMem);
551
552 l1 = l2 = 0;
553 line1 = line2 = 0;
554l0:
555
556#ifdef DEBUG
557 if (fDebug)
558 printf ("At scan beginning\n");
559#endif
560
561 l1 += xfill (buffer1+l1, fh1, cLine-l1, &line1);
562 l2 += xfill (buffer2+l2, fh2, cLine-l2, &line2);
563
564 if (l1 == 0 && l2 == 0)
565 {
566 if (fSame)
567 usage (NoDif, 0);
568 return;
569 }
570 xc = min (l1, l2);
571
572 for (i=0; i < xc; i++)
573 {
574 if (!compare (l1, i, l2, i, 1))
575 break;
576 }
577
578 if (i != xc)
579 i = max (i-1, 0);
580
581 l1 = adjust (buffer1, l1, i);
582 l2 = adjust (buffer2, l2, i);
583
584 /* KLUDGE ALERT!! GOTO USED */
585 if (l1 == 0 && l2 == 0)
586 goto l0;
587
588 l1 += xfill (buffer1+l1, fh1, cLine-l1, &line1);
589 l2 += xfill (buffer2+l2, fh2, cLine-l2, &line2);
590
591#ifdef DEBUG
592 if (fDebug)
593 printf ("buffers are adjusted, %d, %d remain\n", l1, l2);
594#endif
595
596 xd = yd = FALSE;
597 xc = yc = 1;
598 xp = yp = 1;
599
600l6:
601
602#ifdef DEBUG
603 if (fDebug)
604 printf ("Trying resync %d,%d %d,%d\n", xc, xp, yc, yp);
605#endif
606
607 i = min (l1-xc,l2-yp);
608 i = min (i, ctSync);
609
610 if (compare (l1, xc, l2, yp, i))
611 {
612 fSame = FALSE;
613 printf ("***** %s\n", f1);
614 dump (buffer1, 0, xc);
615 printf ("***** %s\n", f2);
616 dump (buffer2, 0, yp);
617 printf ("*****\n\n");
618
619 l1 = adjust (buffer1, l1, xc);
620 l2 = adjust (buffer2, l2, yp);
621
622 /* KLUDGE ALERT!! GOTO USED */
623 goto l0;
624 }
625 i = min (l1-xp, l2-yc);
626 i = min (i, ctSync);
627
628 if (compare (l1, xp, l2, yc, i))
629 {
630 fSame = FALSE;
631 printf ("***** %s\n", f1);
632 dump (buffer1, 0, xp);
633 printf ("***** %s\n", f2);
634 dump (buffer2, 0, yc);
635 printf ("*****\n\n");
636
637 l1 = adjust (buffer1, l1, xp);
638 l2 = adjust (buffer2, l2, yc);
639
640 /* KLUDGE ALERT!! GOTO USED */
641 goto l0;
642 }
643
644 if (++xp > xc)
645 {
646 xp = 1;
647 if (++xc >= l1)
648 {
649 xc = l1;
650 xd = TRUE;
651 }
652 }
653
654 if (++yp > yc)
655 {
656 yp = 1;
657 if (++yc >= l2)
658 {
659 yc = l1;
660 yd = TRUE;
661 }
662 }
663
664 if (!xd || !yd)
665 goto l6;
666 fSame = FALSE;
667
668 if (l1 >= cLine || l2 >= cLine)
669 printf ("%s", ReSyncMes);
670
671 printf ("***** %s\n", f1);
672 dump (buffer1, 0, l1-1);
673 printf ("***** %s\n", f2);
674 dump (buffer2, 0, l2-1);
675 printf ("*****\n\n");
676 exit (1);
677}
678
679
680
681/* return number of lines read in */
682xfill (pl, fh, ct, plnum)
683struct lineType *pl;
684FILE *fh;
685int ct;
686int *plnum;
687{
688 int i;
689
690#ifdef DEBUG
691 if (fDebug)
692 printf ("xfill (%04x, %04x)\n", pl, fh);
693#endif
694
695 i = 0;
696 while (ct-- && (*funcRead) (pl->text, MAXARG, fh) != NULL)
697 {
698 if (funcRead == (int (*) ())FNADDR(fgets))
699 pl->text[strlen(pl->text)-1] = 0;
700 if (fIgnore && !strcmps (pl->text, ""))
701 pl->text[0] = 0;
702 if (strlen (pl->text) != 0 || !fIgnore)
703 {
704 pl->line = ++*plnum;
705 pl++;
706 i++;
707 }
708 }
709
710#ifdef DEBUG
711 if (fDebug)
712 printf ("xfill returns %d\n", i);
713#endif
714
715 return i;
716}
717
718
719/* adjust returns number of lines in buffer */
720adjust (pl, ml, lt)
721struct lineType *pl;
722int ml;
723int lt;
724{
725
726#ifdef DEBUG
727 if (fDebug)
728 printf ("adjust (%04x, %d, %d) = ", pl, ml, lt);
729 if (fDebug)
730 printf ("%d\n", ml-lt);
731#endif
732
733 if (ml <= lt)
734 return 0;
735
736#ifdef DEBUG
737 if (fDebug)
738 printf ("move (%04x, %04x, %04x)\n", &pl[lt], &pl[0], sizeof (*pl)*(ml-lt));
739#endif
740
741 Move ((unsigned char far *)&pl[lt], (char far *)&pl[0], sizeof (*pl)*(ml-lt));
742 return ml-lt;
743}
744
745
746/* dump
747 * dump outputs a range of lines.
748 *
749 * INPUTS
750 * pl pointer to current lineType structure
751 * start starting line number
752 * end ending line number
753 *
754 * CALLS
755 * pline, printf
756 *
757 */
758dump (pl, start, end)
759struct lineType *pl;
760int start, end;
761{
762 if (fAbbrev && end-start > 2)
763 {
764 pline (pl+start);
765 printf ("...\n");
766 pline (pl+end);
767 }
768 else
769 while (start <= end)
770 pline (pl+start++);
771}
772
773
774
775
776/* PrintLINE
777 * pline prints a single line of output. If the /n flag
778 * has been specified, the line number of the printed text is added.
779 *
780 * Inputs
781 * pl pointer to current lineType structure
782 * fNumb TRUE if /n specified
783 *
784 */
785pline (pl)
786struct lineType *pl;
787{
788 if (fNumb)
789 printf ("%5d: ", pl->line);
790
791 printf ("%s\n", pl->text);
792}
793
794/*
795 * strcmpi will compare two string lexically and return one of
796 * the following:
797 * - 0 if the strings are equal
798 * - 1 if first > the second
799 * - (-1) if first < the second
800 *
801 * This was written to replace the run time library version of
802 * strcmpi which does not correctly compare the european character set.
803 * This version relies on a version of toupper which uses IToupper.
804 */
805
806int strcmpi(str1, str2)
807unsigned char *str1, *str2;
808{
809 unsigned char c1, c2;
810
811 while ((c1 = toupper(*str1++)) == (c2 = toupper(*str2++))) {
812 if (c1 == '\0')
813 return(0);
814 }
815
816 if (c1 > c2)
817 return(1);
818 else
819 return(-1);
820}
821
822
823/* compare two strings, ignoring white space, case is significant, return
824 * 0 if identical, <>0 otherwise
825 */
826strcmps (p1, p2)
827unsigned char *p1, *p2;
828{
829 while (TRUE) {
830 while (ISSPACE(*p1))
831 p1++;
832 while (ISSPACE(*p2))
833 p2++;
834 if (*p1 == *p2)
835 if (*p1++ == 0)
836 return 0;
837 else
838 p2++;
839 else
840 return *p1-*p2;
841 }
842}
843
844
845/* compare two strings, ignoring white space, case is not significant, return
846 * 0 if identical, <>0 otherwise
847 */
848int strcmpis (p1, p2)
849unsigned char *p1, *p2;
850{
851 while (TRUE) {
852 while (ISSPACE(*p1))
853 p1++;
854 while (ISSPACE(*p2))
855 p2++;
856 if (toupper (*p1) == toupper (*p2))
857 if (*p1++ == 0)
858 return 0;
859 else
860 p2++;
861 else
862 return *p1-*p2;
863 }
864}