/* Commandline diff utility to test diff implementations. */ /* * Copyright (c) 2018 Martin Pieuchot * Copyright (c) 2020 Neels Hofmeyr * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include __dead void usage(void); int diffreg(char *, char *, bool, bool, int); FILE * openfile(const char *, char **, struct stat *); __dead void usage(void) { fprintf(stderr, "usage: %s [-pw] [-C n] file1 file2\n" "\n" " -p Use Patience Diff (slower but often nicer)\n" " -w Ignore Whitespace\n" " -C n Number of Context Lines\n" , getprogname()); exit(1); } int main(int argc, char *argv[]) { int ch, rc; bool do_patience = false, ignore_whitespace = false; int context_lines = 3; while ((ch = getopt(argc, argv, "pwC:")) != -1) { switch (ch) { case 'p': do_patience = true; break; case 'w': ignore_whitespace = true; break; case 'C': context_lines = atoi(optarg); break; default: usage(); } } argc -= optind; argv += optind; if (argc != 2) usage(); rc = diffreg(argv[0], argv[1], do_patience, ignore_whitespace, context_lines); if (rc != DIFF_RC_OK) { fprintf(stderr, "diff: %s\n", strerror(rc)); return 1; } return 0; } const struct diff_algo_config myers_then_patience; const struct diff_algo_config myers_then_myers_divide; const struct diff_algo_config patience; const struct diff_algo_config myers_divide; const struct diff_algo_config myers_then_patience = (struct diff_algo_config){ .impl = diff_algo_myers, .permitted_state_size = 1024 * 1024 * sizeof(int), .fallback_algo = &patience, }; const struct diff_algo_config myers_then_myers_divide = (struct diff_algo_config){ .impl = diff_algo_myers, .permitted_state_size = 1024 * 1024 * sizeof(int), .fallback_algo = &myers_divide, }; const struct diff_algo_config patience = (struct diff_algo_config){ .impl = diff_algo_patience, /* After subdivision, do Patience again: */ .inner_algo = &patience, /* If subdivision failed, do Myers Divide et Impera: */ .fallback_algo = &myers_then_myers_divide, }; const struct diff_algo_config myers_divide = (struct diff_algo_config){ .impl = diff_algo_myers_divide, /* When division succeeded, start from the top: */ .inner_algo = &myers_then_myers_divide, /* (fallback_algo = NULL implies diff_algo_none). */ }; const struct diff_config diff_config = { .atomize_func = diff_atomize_text_by_line, .algo = &myers_then_myers_divide, }; const struct diff_config diff_config_patience = { .atomize_func = diff_atomize_text_by_line, .algo = &myers_then_patience, }; int diffreg(char *file1, char *file2, bool do_patience, bool ignore_whitespace, int context_lines) { char *str1, *str2; FILE *f1, *f2; struct stat st1, st2; struct diff_input_info info = { .left_path = file1, .right_path = file2, }; struct diff_result *result; int rc; const struct diff_config *cfg; int diff_flags = 0; cfg = do_patience ? &diff_config_patience : &diff_config; f1 = openfile(file1, &str1, &st1); f2 = openfile(file2, &str2, &st2); if (ignore_whitespace) diff_flags |= DIFF_FLAG_IGNORE_WHITESPACE; result = diff_main(cfg, f1, str1, st1.st_size, f2, str2, st2.st_size, diff_flags); #if 0 rc = diff_output_plain(stdout, &info, result); #else rc = diff_output_unidiff(NULL, stdout, &info, result, context_lines); #endif diff_result_free(result); if (str1) munmap(str1, st1.st_size); if (str2) munmap(str2, st2.st_size); fclose(f1); fclose(f2); return rc; } FILE * openfile(const char *path, char **p, struct stat *st) { FILE *f = NULL; f = fopen(path, "r"); if (f == NULL) err(2, "%s", path); if (fstat(fileno(f), st) == -1) err(2, "%s", path); #ifndef DIFF_NO_MMAP *p = mmap(NULL, st->st_size, PROT_READ, MAP_PRIVATE, fileno(f), 0); if (*p == MAP_FAILED) #endif *p = NULL; /* fall back on file I/O */ return f; }