|
| 1 | +#include "../include/filelist.h" |
| 2 | +#include "../include/filelib.h" |
| 3 | +#include "../include/logger.h" |
| 4 | +#include <string.h> |
| 5 | +#include <stdio.h> |
| 6 | + |
| 7 | +static int stringptr_endswithchar(stringptr* s, int ch) { |
| 8 | + if(stringptr_getsize(s) == 0) return 0; |
| 9 | + return stringptr_get(s)[stringptr_getsize(s)-1] == ch; |
| 10 | +} |
| 11 | + |
| 12 | +static int compare_files(stringptr* fn1, stringptr* fn2) { |
| 13 | + FILE *f1 = fopen(stringptr_get(fn1), "r"); |
| 14 | + FILE *f2 = fopen(stringptr_get(fn2), "r"); |
| 15 | + int ret = 1; |
| 16 | + size_t n; |
| 17 | + while(1) { |
| 18 | + unsigned char buf1[16*1024]; |
| 19 | + unsigned char buf2[16*1024]; |
| 20 | + n = fread(buf1, 1, sizeof buf1, f1); |
| 21 | + if(n == 0) { ret = 0; break; } |
| 22 | + if(n != fread(buf2, 1, sizeof buf2, f2)) break; |
| 23 | + if(memcmp(buf1, buf2, n)) break; |
| 24 | + } |
| 25 | + fclose(f1); |
| 26 | + fclose(f2); |
| 27 | + return ret; |
| 28 | +} |
| 29 | + |
| 30 | +static int mode = 0; /* 0: identical, 1: not identical */ |
| 31 | + |
| 32 | +static int process_path(stringptr* dir1, stringptr* dir2) { |
| 33 | + filelist fl1, fl2; |
| 34 | + int ret; |
| 35 | + ret = filelist_search(&fl1, dir1, SPL("*"), FLF_EXCLUDE_PATH|FLF_INCLUDE_HIDDEN); |
| 36 | + if(ret) return ret; |
| 37 | + ret = filelist_search(&fl2, dir2, SPL("*"), FLF_EXCLUDE_PATH|FLF_INCLUDE_HIDDEN); |
| 38 | + if(ret) return ret; |
| 39 | + size_t i, j; |
| 40 | + for(i=0; i < stringptrlist_getsize(fl1.files); ++i) { |
| 41 | + stringptr *f1 = stringptrlist_get(fl1.files, i); |
| 42 | + if(!stringptrlist_contains(fl2.files, f1)) continue; |
| 43 | + stringptr* full1 = stringptr_concat(dir1, stringptr_endswithchar(dir1, '/') ? SPL("") : SPL("/"), f1, NULL); |
| 44 | + stringptr* full2 = stringptr_concat(dir2, stringptr_endswithchar(dir1, '/') ? SPL("") : SPL("/"), f1, NULL); |
| 45 | + if(stringptr_endswithchar(f1, '/')) { |
| 46 | + ret = process_path(full1, full2); |
| 47 | + if(ret) return ret; |
| 48 | + goto next; |
| 49 | + } |
| 50 | + if(getfilesize(stringptr_get(full1)) != getfilesize(stringptr_get(full2))) |
| 51 | + if(mode) goto print; |
| 52 | + else goto next; |
| 53 | + if(compare_files(full1, full2) == mode) { |
| 54 | + print: |
| 55 | + log_puts(1, full1); |
| 56 | + log_putln(1); |
| 57 | + } |
| 58 | + next: |
| 59 | + stringptr_free(full1); |
| 60 | + stringptr_free(full2); |
| 61 | + } |
| 62 | + filelist_free(&fl1); |
| 63 | + filelist_free(&fl2); |
| 64 | +} |
| 65 | + |
| 66 | +static int usage() { |
| 67 | + log_puts(2, SPL( |
| 68 | + "usage: PROG MODE DIR1 DIR2\n" |
| 69 | + "MODE: i - identical or n - not identical\n" |
| 70 | + "finds all (not) identical files in DIR1/2 and its subdirs.\n" |
| 71 | + "prints filenames of DIR1 that match MODE.\n" |
| 72 | + "files that exist only in DIR1 or DIR2 are excluded.\n" |
| 73 | + )); |
| 74 | + return 1; |
| 75 | +} |
| 76 | + |
| 77 | +int main(int argc, char** argv) { |
| 78 | + if(argc != 4) return usage(); |
| 79 | + mode = argv[1][0] == 'i' ? 0 : 1; |
| 80 | + SPDECLAREC(dir1, argv[2]); |
| 81 | + SPDECLAREC(dir2, argv[3]); |
| 82 | + int ret = process_path(dir1, dir2); |
| 83 | + return ret; |
| 84 | +} |
0 commit comments