1 /* Commandline diff utility to test diff implementations. */ 2 /* 3 * Copyright (c) 2018 Martin Pieuchot 4 * Copyright (c) 2020 Neels Hofmeyr <neels@hofmeyr.de> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/mman.h> 20 #include <sys/stat.h> 21 #include <sys/types.h> 22 23 #include <err.h> 24 #include <fcntl.h> 25 #include <stdint.h> 26 #include <stdio.h> 27 #include <stdlib.h> 28 #include <stdbool.h> 29 #include <string.h> 30 #include <unistd.h> 31 32 #include <arraylist.h> 33 #include <diff_main.h> 34 #include <diff_output.h> 35 36 enum diffreg_algo { 37 DIFFREG_ALGO_MYERS_THEN_MYERS_DIVIDE = 0, 38 DIFFREG_ALGO_MYERS_THEN_PATIENCE = 1, 39 DIFFREG_ALGO_PATIENCE = 2, 40 DIFFREG_ALGO_NONE = 3, 41 }; 42 43 __dead void usage(void); 44 int diffreg(char *, char *, enum diffreg_algo, bool, bool, bool, 45 int, bool); 46 FILE * openfile(const char *, char **, struct stat *); 47 48 __dead void 49 usage(void) 50 { 51 fprintf(stderr, 52 "usage: %s [-apPQTwe] [-U n] file1 file2\n" 53 "\n" 54 " -a Treat input as ASCII even if binary data is detected\n" 55 " -p Show function prototypes in hunk headers\n" 56 " -P Use Patience Diff (slower but often nicer)\n" 57 " -Q Use forward-Myers for small files, otherwise Patience\n" 58 " -T Trivial algo: detect similar start and end only\n" 59 " -w Ignore Whitespace\n" 60 " -U n Number of Context Lines\n" 61 " -e Produce ed script output\n" 62 , getprogname()); 63 exit(1); 64 } 65 66 int 67 main(int argc, char *argv[]) 68 { 69 int ch, rc; 70 bool force_text = false; 71 bool ignore_whitespace = false; 72 bool show_function_prototypes = false; 73 bool edscript = false; 74 int context_lines = 3; 75 enum diffreg_algo algo = DIFFREG_ALGO_MYERS_THEN_MYERS_DIVIDE; 76 77 while ((ch = getopt(argc, argv, "apPQTwU:e")) != -1) { 78 switch (ch) { 79 case 'a': 80 force_text = true; 81 break; 82 case 'p': 83 show_function_prototypes = true; 84 break; 85 case 'P': 86 algo = DIFFREG_ALGO_PATIENCE; 87 break; 88 case 'Q': 89 algo = DIFFREG_ALGO_MYERS_THEN_PATIENCE; 90 break; 91 case 'T': 92 algo = DIFFREG_ALGO_NONE; 93 break; 94 case 'w': 95 ignore_whitespace = true; 96 break; 97 case 'U': 98 context_lines = atoi(optarg); 99 break; 100 case 'e': 101 edscript = true; 102 break; 103 default: 104 usage(); 105 } 106 } 107 108 argc -= optind; 109 argv += optind; 110 111 if (argc != 2) 112 usage(); 113 114 rc = diffreg(argv[0], argv[1], algo, force_text, ignore_whitespace, 115 show_function_prototypes, context_lines, edscript); 116 if (rc != DIFF_RC_OK) { 117 fprintf(stderr, "diff: %s\n", strerror(rc)); 118 return 1; 119 } 120 return 0; 121 } 122 123 const struct diff_algo_config myers_then_patience; 124 const struct diff_algo_config myers_then_myers_divide; 125 const struct diff_algo_config patience; 126 const struct diff_algo_config myers_divide; 127 128 const struct diff_algo_config myers_then_patience = (struct diff_algo_config){ 129 .impl = diff_algo_myers, 130 .permitted_state_size = 1024 * 1024 * sizeof(int), 131 .fallback_algo = &patience, 132 }; 133 134 const struct diff_algo_config myers_then_myers_divide = 135 (struct diff_algo_config){ 136 .impl = diff_algo_myers, 137 .permitted_state_size = 1024 * 1024 * sizeof(int), 138 .fallback_algo = &myers_divide, 139 }; 140 141 const struct diff_algo_config patience = (struct diff_algo_config){ 142 .impl = diff_algo_patience, 143 /* After subdivision, do Patience again: */ 144 .inner_algo = &patience, 145 /* If subdivision failed, do Myers Divide et Impera: */ 146 .fallback_algo = &myers_then_myers_divide, 147 }; 148 149 const struct diff_algo_config myers_divide = (struct diff_algo_config){ 150 .impl = diff_algo_myers_divide, 151 /* When division succeeded, start from the top: */ 152 .inner_algo = &myers_then_myers_divide, 153 /* (fallback_algo = NULL implies diff_algo_none). */ 154 }; 155 156 const struct diff_algo_config no_algo = (struct diff_algo_config){ 157 .impl = diff_algo_none, 158 }; 159 160 /* If the state for a forward-Myers is small enough, use Myers, otherwise first 161 * do a Myers-divide. */ 162 const struct diff_config diff_config_myers_then_myers_divide = { 163 .atomize_func = diff_atomize_text_by_line, 164 .algo = &myers_then_myers_divide, 165 }; 166 167 /* If the state for a forward-Myers is small enough, use Myers, otherwise first 168 * do a Patience. */ 169 const struct diff_config diff_config_myers_then_patience = { 170 .atomize_func = diff_atomize_text_by_line, 171 .algo = &myers_then_patience, 172 }; 173 174 /* Directly force Patience as a first divider of the source file. */ 175 const struct diff_config diff_config_patience = { 176 .atomize_func = diff_atomize_text_by_line, 177 .algo = &patience, 178 }; 179 180 /* Directly force Patience as a first divider of the source file. */ 181 const struct diff_config diff_config_no_algo = { 182 .atomize_func = diff_atomize_text_by_line, 183 }; 184 185 int 186 diffreg(char *file1, char *file2, enum diffreg_algo algo, bool force_text, 187 bool ignore_whitespace, bool show_function_prototypes, int context_lines, 188 bool edscript) 189 { 190 char *str1, *str2; 191 FILE *f1, *f2; 192 struct stat st1, st2; 193 struct diff_input_info info = { 194 .left_path = file1, 195 .right_path = file2, 196 }; 197 struct diff_data left = {}, right = {}; 198 struct diff_result *result = NULL; 199 int rc; 200 const struct diff_config *cfg; 201 int diff_flags = 0; 202 203 switch (algo) { 204 default: 205 case DIFFREG_ALGO_MYERS_THEN_MYERS_DIVIDE: 206 cfg = &diff_config_myers_then_myers_divide; 207 break; 208 case DIFFREG_ALGO_MYERS_THEN_PATIENCE: 209 cfg = &diff_config_myers_then_patience; 210 break; 211 case DIFFREG_ALGO_PATIENCE: 212 cfg = &diff_config_patience; 213 break; 214 case DIFFREG_ALGO_NONE: 215 cfg = &diff_config_no_algo; 216 break; 217 } 218 219 f1 = openfile(file1, &str1, &st1); 220 f2 = openfile(file2, &str2, &st2); 221 222 if (force_text) 223 diff_flags |= DIFF_FLAG_FORCE_TEXT_DATA; 224 if (ignore_whitespace) 225 diff_flags |= DIFF_FLAG_IGNORE_WHITESPACE; 226 if (show_function_prototypes) 227 diff_flags |= DIFF_FLAG_SHOW_PROTOTYPES; 228 229 rc = diff_atomize_file(&left, cfg, f1, str1, st1.st_size, diff_flags); 230 if (rc) 231 goto done; 232 rc = diff_atomize_file(&right, cfg, f2, str2, st2.st_size, diff_flags); 233 if (rc) 234 goto done; 235 236 result = diff_main(cfg, &left, &right); 237 #if 0 238 rc = diff_output_plain(stdout, &info, result); 239 #else 240 if (edscript) 241 rc = diff_output_edscript(NULL, stdout, &info, result); 242 else { 243 rc = diff_output_unidiff(NULL, stdout, &info, result, 244 context_lines); 245 } 246 #endif 247 done: 248 diff_result_free(result); 249 diff_data_free(&left); 250 diff_data_free(&right); 251 if (str1) 252 munmap(str1, st1.st_size); 253 if (str2) 254 munmap(str2, st2.st_size); 255 fclose(f1); 256 fclose(f2); 257 258 return rc; 259 } 260 261 FILE * 262 openfile(const char *path, char **p, struct stat *st) 263 { 264 FILE *f = NULL; 265 266 f = fopen(path, "r"); 267 if (f == NULL) 268 err(2, "%s", path); 269 270 if (fstat(fileno(f), st) == -1) 271 err(2, "%s", path); 272 273 #ifndef DIFF_NO_MMAP 274 *p = mmap(NULL, st->st_size, PROT_READ, MAP_PRIVATE, fileno(f), 0); 275 if (*p == MAP_FAILED) 276 #endif 277 *p = NULL; /* fall back on file I/O */ 278 279 return f; 280 } 281