|
|
1.1 ! root 1: /* finddupes.c */ ! 2: ! 3: /* Search for (and optionally delete) duplicate files in multiple */ ! 4: /* directories based on size and either MD5 or CRC-32 "chksums". */ ! 5: ! 6: #include <stdio.h> ! 7: #include <time.h> ! 8: ! 9: #include "dirwrap.h" ! 10: #ifdef USE_MD5 ! 11: #include "md5.h" ! 12: #else /* CRC-32 */ ! 13: #include "crc32.h" ! 14: #endif ! 15: ! 16: typedef struct { ! 17: char path[MAX_PATH+1]; ! 18: long length; ! 19: time_t date; ! 20: #ifdef USE_MD5 ! 21: BYTE chksum[MD5_DIGEST_SIZE]; ! 22: #else ! 23: ulong chksum; ! 24: #endif ! 25: } file_t; ! 26: ! 27: file_t* file; ! 28: ulong file_count=0; ! 29: ! 30: #ifdef FREE ! 31: #undef FREE ! 32: #endif ! 33: #define FREE(x) if(x!=NULL) free(x); ! 34: ! 35: int fchksum(const char* fname, long length, ! 36: #ifdef USE_MD5 ! 37: BYTE* ! 38: #else ! 39: ulong* ! 40: #endif ! 41: chksum) ! 42: { ! 43: BYTE* buf=NULL; ! 44: FILE* fp; ! 45: ! 46: if((fp=fopen(fname,"rb"))==NULL) { ! 47: perror(fname); ! 48: return(-1); ! 49: } ! 50: ! 51: if(length && (buf=malloc(length))==NULL) { ! 52: printf("!Error allocating %ld bytes of memory for %s\n" ! 53: ,length,fname); ! 54: fclose(fp); ! 55: return(-1); ! 56: } ! 57: ! 58: if(fread(buf,sizeof(BYTE),length,fp) != length) { ! 59: perror(fname); ! 60: fclose(fp); ! 61: FREE(buf); ! 62: return(-1); ! 63: } ! 64: ! 65: fclose(fp); ! 66: #ifdef USE_MD5 ! 67: MD5_calc(chksum, buf, length); ! 68: #else ! 69: *chksum = crc32(buf, length); ! 70: #endif ! 71: FREE(buf); ! 72: return(0); ! 73: } ! 74: ! 75: char* timestr(void) ! 76: { ! 77: char* p; ! 78: time_t t=time(NULL); ! 79: p=ctime(&t); ! 80: p[19]=0; /* chop off year and \n */ ! 81: return(p+4); /* skip day-of-week */ ! 82: } ! 83: ! 84: int searchdir(const char* path, BOOL recursive, ulong compare_bytes) ! 85: { ! 86: DIR* dir; ! 87: struct dirent* ent; ! 88: file_t* fp; ! 89: char fpath[MAX_PATH+1]; ! 90: ! 91: printf("%s begin searching %s\n",timestr(), path); ! 92: if((dir = opendir(path))==NULL) { ! 93: perror(path); ! 94: return(1); ! 95: } ! 96: ! 97: while((ent = readdir(dir))!=NULL) { ! 98: if(kbhit()) ! 99: break; ! 100: if(strcmp(ent->d_name,".")==0 || strcmp(ent->d_name,"..")==0) ! 101: continue; ! 102: strcpy(fpath,path); ! 103: backslash(fpath); ! 104: strcat(fpath,ent->d_name); ! 105: if(isdir(fpath)) { ! 106: if(recursive) ! 107: searchdir(fpath, recursive, compare_bytes); ! 108: continue; ! 109: } ! 110: ! 111: file=realloc(file,sizeof(file_t)*(file_count+1)); ! 112: if(file==NULL) { ! 113: printf("!Error allocating %lu bytes\n",sizeof(file_t)*(file_count+1)); ! 114: exit(1); ! 115: } ! 116: fp=&file[file_count]; ! 117: memset(fp,0,sizeof(file_t)); ! 118: strcpy(fp->path,fpath); ! 119: fp->date=fdate(fp->path); ! 120: if((fp->length=flength(fp->path))==-1) { ! 121: printf("!Failed to get length of %s\n",fp->path); ! 122: continue; ! 123: } ! 124: if(compare_bytes && fp->length > compare_bytes) ! 125: fp->length = compare_bytes; ! 126: if(fchksum(fp->path, fp->length, ! 127: #ifdef USE_MD5 ! 128: fp->chksum ! 129: #else ! 130: &fp->chksum ! 131: #endif ! 132: )) ! 133: continue; ! 134: file_count++; ! 135: printf("%lu\r", file_count); ! 136: } ! 137: ! 138: closedir(dir); ! 139: printf("%s done searching %s\n",timestr(), path); ! 140: return(0); ! 141: } ! 142: ! 143: int compare_files(const file_t *f1, const file_t *f2 ) ! 144: { ! 145: int result; ! 146: ! 147: /* Sort first by size (descending) */ ! 148: if((result = f2->length - f1->length) != 0) ! 149: return(result); ! 150: ! 151: /* Then by chksum (ascending) */ ! 152: if((result = memcmp(&f1->chksum, &f2->chksum, sizeof(f1->chksum))) != 0) ! 153: return(result); ! 154: ! 155: /* Then by date (descending) */ ! 156: return(f2->date - f1->date); ! 157: } ! 158: ! 159: int main(int argc, char** argv) ! 160: { ! 161: char hex[32]; ! 162: int i; ! 163: ulong fsize; ! 164: ulong dupe_count=0; ! 165: ulong del_files=0; ! 166: ulong del_bytes=0; ! 167: ulong compare_bytes=0; ! 168: BOOL recursive=FALSE; ! 169: BOOL del_dupes=FALSE; ! 170: BOOL dir_specified=FALSE; ! 171: ! 172: for(i=1;i<argc;i++) { ! 173: if(!stricmp(argv[i],"-d")) ! 174: del_dupes=TRUE; ! 175: else if(!stricmp(argv[i],"-r")) ! 176: recursive=TRUE; ! 177: else if(!stricmp(argv[i],"-b") && i<argc+1) ! 178: compare_bytes=atoi(argv[++i]); ! 179: else if(!stricmp(argv[i],"-k") && i<argc+1) ! 180: compare_bytes=atoi(argv[++i])*1024; ! 181: else if(argv[i][0]=='-') { ! 182: printf("%s [[-opt] [-opt] [...]] [[path] [path] [...]]\n", argv[0]); ! 183: printf("-r\t search directories recursively\n"); ! 184: printf("-d\t delete duplicate files found\n"); ! 185: printf("-b n\t compare up to n bytes of each file\n"); ! 186: printf("-k n\t compare up to n kilobytes each of file\n"); ! 187: exit(0); ! 188: } ! 189: else { ! 190: dir_specified=TRUE; ! 191: searchdir(argv[i], recursive, compare_bytes); ! 192: } ! 193: } ! 194: if(!dir_specified) ! 195: searchdir(".", recursive, compare_bytes); ! 196: ! 197: if(!file_count) { ! 198: printf("no files.\n"); ! 199: return(0); ! 200: } ! 201: ! 202: printf("%s begin sorting (%lu files)\n", timestr(), file_count); ! 203: qsort(file,file_count,sizeof(file_t),compare_files); ! 204: printf("%s end sorting\n", timestr()); ! 205: ! 206: printf("%s comparing (%lu files)\n", timestr(), file_count); ! 207: ! 208: for(i=0;i<file_count-1;i++) { ! 209: if(file[i].length != file[i+1].length) ! 210: continue; /* sizes must match */ ! 211: if(memcmp(&file[i].chksum, &file[i+1].chksum, sizeof(file[i].chksum))) ! 212: continue; /* chksums must match */ ! 213: #ifdef USE_MD5 ! 214: MD5_hex(hex, file[i].chksum); ! 215: #else ! 216: sprintf(hex, "%08lx", file[i].chksum); ! 217: #endif ! 218: printf("Dupe: %s %7lu %s\n", hex, file[i].length, getfname(file[i].path)); ! 219: if(del_dupes) { ! 220: fsize=flength(file[i].path); ! 221: printf("Removing %s (%lu bytes)\n", file[i].path, fsize); ! 222: if(remove(file[i].path)!=0) ! 223: perror(file[i].path); ! 224: else { ! 225: del_files++; ! 226: del_bytes+=fsize; ! 227: } ! 228: } ! 229: dupe_count++; ! 230: } ! 231: ! 232: printf("%s done (%lu duplicates found)\n", timestr(), dupe_count); ! 233: if(del_files) ! 234: printf("%lu bytes deleted in %lu files\n", del_bytes, del_files); ! 235: return(0); ! 236: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.