#include #include #include #include #include #include #define TAB_SIZE 8 #define DEFAULT_MAX_DIFF 10 #define MAX_LINE_LEN 1023 #define END_OF_STRING 0 #define NO_DIFFERENCES "No differences encountered\n" #define TOO_MANY "Too many differences encountered (Use /LBn to change the maximum)\n" #define LINE_TOO_LONG "FC: Line too long (exceeding %d chars)\n" typedef enum { FALSE, TRUE } bool; // Global Variables bool NoTabExpand = FALSE, Trim = FALSE, FirstAndLastOnly = FALSE; bool IgnoreCase = FALSE, ShowLineNumbers = FALSE; FILE *FilePointers[2]; char *FileNames[2]; int MaxDifferences = DEFAULT_MAX_DIFF; enum { DEFAULT, ASCII, BINARY } CompareMode = DEFAULT; // Returns size of a file long FileSize(FILE* Stream) { long CurrPos, Length; CurrPos = ftell(Stream); fseek(Stream, 0L, SEEK_END); Length = ftell(Stream); fseek(Stream, CurrPos, SEEK_SET); return Length; } // displays help message void HelpMessage() { printf("Compares two files or sets of files and displays the differences between them.\n"); printf("\nFC [/A] [/C] [/L] [/LBn] [/N] [/T] [/W] [/nnnn] [drive1:][path1]filename1"); printf("\n [drive2][path2]filename2"); printf("\nFC /B [drive1][path1]filename1 [drive2:][path2]filename2\n"); printf("\n /A Displays only first and last lines for each set of differences."); printf("\n /B Performs a binary comparison."); printf("\n /C Disregards the case of letters."); printf("\n /L Compares files as ASCII text."); printf("\n /LBn Sets the maximum consecutive mismatches to n lines."); printf("\n /N Displays the line numbers on an ASCII comparison."); printf("\n /T Does not expand tabs to spaces."); printf("\n /W Compress white space (tabs and spaces) for comparison.\n"); } // Scan arguments for switches and files bool ScanArguments(int argc, char *argv[]) { int FileCounter = 0; // scan through arguments for (int i = 1; i < argc; i++) if ((argv[i][0] == '/') || (argv[i][0] == '-')) // Switch char if (argv[i][2] == END_OF_STRING) switch (toupper(argv[i][1])) // Single char switch in the form "/x" { case 'B': CompareMode = BINARY; break; case 'L': CompareMode = ASCII; break; case 'A': FirstAndLastOnly = TRUE; break; case 'C': IgnoreCase = TRUE; break; case 'N': ShowLineNumbers = TRUE; break; case 'T': NoTabExpand = TRUE; break; case 'W': Trim = TRUE; break; case '?': case 'H': HelpMessage(); return FALSE; default: printf("FC: Invalid switch: %s\n", argv[i]); return FALSE; } else // Switch not single char if ((toupper(argv[i][1]) == 'L') && (toupper(argv[i][2]) == 'B')) MaxDifferences = atoi(&argv[i][3]); else { printf("FC: Invalid switch: %s\n", argv[i]); return FALSE; } else // Not a switch: it's a filename { if (FileCounter >= 2) { printf("FC: Too many filespecs\n"); return FALSE; } FileNames[FileCounter] = strdup(argv[i]); FileCounter++; } switch (FileCounter) { case 0: printf("FC: No file specified\n"); return FALSE; case 1: FileNames[1] = strdup("."); } return TRUE; } // Binary compare two files void BinaryCompare(void) { unsigned char a, b; long int i, FileSize1, FileSize2; int Differences = 0; FileSize1 = FileSize(FilePointers[0]); FileSize2 = FileSize(FilePointers[1]); if (FileSize1 != FileSize2) { printf("FC: Warning: the files are of different size!\n"); if (FileSize1 < FileSize2) FileSize1 = FileSize2; } for (i = 0; i < FileSize1; i++) { fread(&a, 1, 1, FilePointers[0]); fread(&b, 1, 1, FilePointers[1]); if (a != b) { Differences++; if (Differences > MaxDifferences) { printf(TOO_MANY); return; } printf("%08X: %02X %02X\n", i, a, b); } } if (Differences == 0) printf(NO_DIFFERENCES); } // Reads a line from an ascii file void ReadLine(FILE* fp, char* Line) { char* CurrPos = Line; bool Space = FALSE; char c = ' '; while ((c != '\n') && (!feof(fp))) { c = fgetc(fp); if (Trim) { if ((c == ' ') || (c == '\t')) { if (Space) continue; // No more blanks or tabs allowed: ignore Space = TRUE; c = ' '; // Insert a blank } else Space = FALSE; } else if ((c == '\t') && (!NoTabExpand)) { int LineLength = (int)(CurrPos - Line); int Blanks = TAB_SIZE - (LineLength % TAB_SIZE); if ((LineLength + Blanks) >= MAX_LINE_LEN) { printf(LINE_TOO_LONG, MAX_LINE_LEN); return; } do { *CurrPos = ' '; CurrPos++; Blanks--; } while (Blanks > 0); continue; // Chars inserted: go on } if ((CurrPos - Line) < MAX_LINE_LEN) // If there is room { if (c == END_OF_STRING) { printf("FC: Invalid char (#0) in file\n"); return; } *CurrPos = c; CurrPos++; // Insert the char } else { printf(LINE_TOO_LONG, MAX_LINE_LEN); return; } } *CurrPos = END_OF_STRING; // Close the string } // Ascii compare two files void AsciiCompare(void) { unsigned int LineNumber; char* String[2]; bool Equal, Show, FirstDiff; int Differences; int i; for (i = 0; i < 2; i++) String[i] = new char[MAX_LINE_LEN + 1]; for (i = 0; i < 2; i++) { rewind(FilePointers[0]); rewind(FilePointers[1]); Differences = 0; LineNumber = 0; Show = TRUE; FirstDiff = TRUE; while (!(feof(FilePointers[0]) || feof(FilePointers[1]))) { LineNumber++; ReadLine(FilePointers[0], String[0]); ReadLine(FilePointers[1], String[1]); if (!IgnoreCase) Equal = (bool)(strcmp(String[0], String[1]) == 0); else Equal = (bool)(strcmp(strupr(String[0]), strupr(String[1])) == 0); if (!Equal) { if (FirstDiff) printf("****** %s\n", FileNames[i]); FirstDiff = FALSE; Differences++; if (Differences > MaxDifferences) { printf("******\n"); printf(TOO_MANY); return; } if (Show) { if (ShowLineNumbers) printf("%d: ", LineNumber); printf("%s", String[i]); } if (FirstAndLastOnly) Show = FALSE; } else { Differences = 0; if (!Show) // If it's the first matching line after a difference { if (ShowLineNumbers) printf("%d: ", LineNumber); printf("%s", String[i]); Show = TRUE; } } } if (!Show) // No matching line after a difference { // Print the last one if (ShowLineNumbers) printf("%d: ", LineNumber); printf("%s", String[i]); } if (!FirstDiff) printf("******\n"); } for (i = 0; i < 2; i++) delete (String[i]); if (Differences == 0) printf(NO_DIFFERENCES); } bool HasWildcards(const char* Filename) { return (bool)(fnsplit(Filename, NULL, NULL, NULL, NULL) & WILDCARDS); } bool IsADirectory(char* Filename) { struct stat statbuf; if (stat(Filename, &statbuf) != -1) if (statbuf.st_mode & S_IFDIR) return TRUE; return FALSE; } /* Add a '\' at the end of Filename if not already present */ void EndingBackSlash(char** Filename) { if ((*Filename)[strlen(*Filename) - 1] != '\\') { char* Temp = (char*)malloc(strlen(*Filename) + 2); sprintf(Temp, "%s\\", *Filename); free(*Filename); (*Filename) = Temp; } } /* Extract the path part of the filename */ char* FilePath(char* Filename) { char drive[MAXDRIVE]; char dir[MAXDIR]; char *Path; fnsplit(Filename, drive, dir, NULL, NULL); Path = (char*)malloc(strlen(drive) + strlen(dir) + 1); sprintf(Path, "%s%s", drive, dir); return Path; } bool BinaryFile(char* Filename) { const char* BinExt[] = { ".EXE", ".COM", ".SYS", ".OBJ", ".LIB", ".BIN" }; char Ext[MAXEXT]; if (fnsplit(Filename, NULL, NULL, NULL, Ext) & EXTENSION) for (int i = 0; i < sizeof (BinExt)/sizeof (BinExt[0]); i++) if (strcmpi(BinExt[i], Ext) == 0) return TRUE; return FALSE; } int main(int argc, char *argv[]) { if (!ScanArguments(argc, argv)) return 1; char* Path0; char* Path1; if (IsADirectory(FileNames[0])) { Path0 = FileNames[0]; EndingBackSlash(&Path0); /* Assume all files */ FileNames[0] = (char*)malloc(strlen(Path0) + 3 + 1); sprintf(FileNames[0], "%s*.*", Path0); } else Path0 = FilePath(FileNames[0]); bool File1IsDirectory = TRUE; if (FileNames[1][strlen(FileNames[1]) - 1] == ':') /* Disk drive only */ Path1 = strdup(FileNames[1]); else if (IsADirectory(FileNames[1])) { Path1 = strdup(FileNames[1]); EndingBackSlash(&Path1); } else { Path1 = FilePath(FileNames[1]); /* Any wildcard here means "all files" */ File1IsDirectory = HasWildcards(FileNames[1]); } int Count = 0; struct ffblk FindData; bool Done0 = (bool)findfirst(FileNames[0], &FindData, 0); while (!Done0) { free(FileNames[0]); FileNames[0] = (char*)malloc(strlen(Path0) + strlen(FindData.ff_name) + 1); sprintf(FileNames[0], "%s%s", Path0, FindData.ff_name); if (File1IsDirectory) { free(FileNames[1]); FileNames[1] = (char*)malloc(strlen(Path1) + strlen(FindData.ff_name) + 1); sprintf(FileNames[1], "%s%s", Path1, FindData.ff_name); } if ((FilePointers[1] = fopen(FileNames[1], "rb")) != NULL) { if ((FilePointers[0] = fopen(FileNames[0], "rb")) == NULL) { fcloseall(); printf("FC: Error opening file %s\n", FileNames[0]); return 1; } printf("Comparing files %s and %s\n", FileNames[0], FileNames[1]); switch (CompareMode) { case DEFAULT: if (BinaryFile(FileNames[0]) || BinaryFile(FileNames[1])) BinaryCompare(); else AsciiCompare(); break; case ASCII: AsciiCompare(); break; case BINARY: BinaryCompare(); break; } fcloseall(); } Done0 = (bool)findnext(&FindData); } free (Path0); free (Path1); if (Count == 0) { fprintf(stderr, "FC: No such file or directory\n"); return 1; } return 0; }