/* This program will compare all files on a given drive from the current subdirectory forward to duplicate files in duplicate directories from a point specified on the command line. */ #include #include #include #include #define ALL FA_NORMAL | FA_RDONLY | FA_HIDDEN | FA_SYSTEM | FA_ARCH | FA_DIREC #define OK 0 #define TRUE 0 #define FALSE -1 char curdir[256], trgdir[256]; int integrity; main(argc, argv) int argc; char *argv[]; { struct ffblk fdat; if (argc != 2) { usage(); return 1; } integrity = OK; /* init the global 'all files OK' flag */ /* init the starting point of our search */ getcwd(curdir, 238); if (strlen(curdir) > 3) strcat(curdir, "\\"); /* init the starting point of our compare to tree */ strcpy(trgdir, argv[1]); if ((strlen(trgdir) >= 3) && (trgdir[strlen(trgdir) - 1] != '\\')) strcat(trgdir, "\\"); /* Seed an ffblk with a call to findfirst and start chknext searching */ /* This is the main strategy of this program, see chknext for details. */ if (findfirst("*.*", &fdat, ALL) == 0) { chknext(&fdat, curdir); /* the following four print statments erase the line and start over */ printf("\r"); printf(" "); printf(" "); printf("\r"); if (integrity == OK) { printf("All files compared identical.\n"); return 0; } else { printf("Errors or mismatches were encountered!\n"); return 1; } }else { usage(); return 1; } } usage() { clrscrn(); printf("ÂÄÄ¿ ÚÄÄ¿ ÚÄÂÄ¿ ÂÄÄ¿ Given free of charge with\n"); printf("ÃÄÂÙ ³ ³ ³ ³ ÃÄÄÙ source by Jim Phillips\n"); printf("Á \\ ÀÄÄÙ Á Á Á www.brouhaha.com\n"); printf("R Comp Recursive Compare\n"); printf("\n"); printf("Have you ever wanted to compare an XCOPY c: d: /s/v/e"); printf(" Now you can!\n"); printf(" \n"); printf("Usage: rcmp d:path \n"); printf(" \n"); printf("would search from the current drive and directory recursing \n"); printf("through all directories below that point comparing all files \n"); printf("to files expected to be found on d:path in a matching tree and\n"); printf("of matching names. \n"); printf(" \n"); printf("If all files match, and no errors are encountered a Zero\n"); printf("is returned to the batch file ERRORLEVEL otherwise a One\n"); printf("is the return value. \n"); printf("Note: this is ultra beta release... version - 0.02 \n"); } /* This recursive routine, descends from a selected subdirectory through all*/ /* branches of the tree below that point identifying each file encountered. */ /* To call this routine you pass it two arguments, first a pointer to an */ /* ffblk structure initalized with findfirst or findnext to some valid item */ /* within that directory, and as a second argument, a pointer to a string */ /* specifying the complete subdirectory name; example: C:\DOS\DRIVERS\ */ /* For this routine to earn it's keep, it calls a routine inside the "boxed"*/ /* section to perform an operation on each of the files it identifys, after */ /* first checking to make sure the file it now holds is not infact a */ /* subdirectory. The code that follows the "boxed" section, handles the */ /* case where it is a subdirectory, by descending to that directory, and */ /* recursively reinitallizing it'self with a new call to findfirst. Then */ /* unwinding any prior recursion it re-asscends by a call to chdir("..") */ /* Failing that it loops using findnext until all remaining files are found */ chknext(fdat, dirnam) struct ffblk *fdat; char *dirnam; { struct ffblk l_fdat; char l_dirnam[256], date_str[20]; do { if (chkdots(fdat) == TRUE) continue; /* ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ Boxed */ if (((*fdat).ff_attrib & FA_DIREC) != FA_DIREC) { compare(dirnam, (*fdat).ff_name); } /* ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ Section */ if (((*fdat).ff_attrib & FA_DIREC) == FA_DIREC) { strncpy(l_dirnam, dirnam, 238); l_dirnam[238] = 0; strncat(l_dirnam, (*fdat).ff_name, 12); strcat(l_dirnam, "\\"); chdir((*fdat).ff_name); if (findfirst("*.*", &l_fdat, ALL) == 0) { chknext(&l_fdat, l_dirnam); } chdir(".."); continue; } } while (findnext(fdat) == TRUE); } /* This routine looks at the filename feild inside an ffblk structure, and */ /* makes a determination whither or not that name is one of the first two */ /* files of a subdirectory, 'dot' or 'dot dot' These are not real files */ /* nor are they real subdirectorys rather they are links to the current */ /* directory and the parent directory. As such they require special */ /* treatment. To that end this routine checks for this condition and */ /* returns a simple TRUE or FALSE. */ chkdots(fdat) struct ffblk *fdat; { if ((((*fdat).ff_attrib & FA_DIREC) == FA_DIREC) && ((strcmp(&(*fdat).ff_name, ".") == 0) || (strcmp(&(*fdat).ff_name, "..") == 0))) return TRUE; else return FALSE; } /* This routine compares the data of two files, that are presumed to be */ /* identical, and reports the result if differences are found. The file */ /* names it opens for read only access, and those that it displays in */ /* messages to the user, are strings it manufactures out of string pointers */ /* raw, and fln passed to it by the calling routine, and from global */ /* variables is uses for the Current Directory, and Target Directory. If */ /* any file is found not to be a match it sets an integrity flag to FALSE */ /* This routine uses global variables; trgdir, curdir, and integrity. */ compare(raw, fln) char *raw, *fln; { FILE *hofp, *bofp; char hoky[256], bogi[256], greek[85]; char hoky_d[512], bogi_d[512]; int hosz, bosz, stat; strcpy(bogi, trgdir); strcat(bogi, raw + strlen(curdir)); strcat(bogi, fln); strcpy(hoky, raw); strcat(hoky, fln); if (strlen(hoky) > 54) /* if hoky is too long prepare to speak in Greek */ { strncpy(greek, hoky, 5); greek[5] = 0; strcat(greek, " ... "); strcat(greek, hoky + strlen(hoky) - 42); } /* the following four print statments erase the line and start over */ printf("\r"); printf(" "); printf(" "); printf("\r"); /* the following compound 'if' prints the best rendition of the file names while insuring they stay on a single line, leaving room for the longest possible, follow up error message */ if (strlen(hoky) > 54) /* if hoky is too long, speak in Greek */ { printf("comp %s to ...", greek); } else { if ((strlen(hoky) + strlen(bogi)) > 54) { /* Both too long? Say only one */ printf("comp %s to ...", hoky); } else printf("comp %s to %s", hoky, bogi); } /* Try to open the first file */ hofp = fopen(hoky, "rb"); if (hofp == NULL) { printf(" UNREACHABLE!\n"); integrity = FALSE; return; } /* Try to open the second file */ bofp = fopen(bogi, "rb"); if (bofp == NULL) { printf(" UNREACHABLE!\n"); integrity = FALSE; return; } /* do ... while loop compares the data of two files */ do { hosz = fread(hoky_d, 1, 512, hofp); bosz = fread(bogi_d, 1, 512, bofp); if (hosz == bosz) stat = memcmp(hoky_d, bogi_d, hosz); else stat = FALSE; } while ((stat == OK) && (hosz == 512)); /* check result, if bad, print DIFFERNT and advance to next line leaving a line that remains unerased on the screen and set a global flag to indicate that atleast one file was unlike it's counterpart */ if (stat != OK) { printf(" DIFFERENT!\n"); /* maybe we should do something like beep here! */ integrity = FALSE; } /* close the files before exiting this routine */ fclose(hofp); fclose(bofp); }