1 /* Commandline diff utility to test diff implementations. */
3 * Copyright (c) 2018 Martin Pieuchot
4 * Copyright (c) 2020 Neels Hofmeyr <neels@hofmeyr.de>
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.
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.
21 #include <sys/types.h>
32 #include <arraylist.h>
33 #include <diff_main.h>
34 #include <diff_output.h>
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,
43 __dead void usage(void);
44 int diffreg(char *, char *, enum diffreg_algo, bool, bool, bool,
46 FILE * openfile(const char *, char **, struct stat *);
52 "usage: %s [-apPQTwe] [-U n] file1 file2\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"
67 main(int argc, char *argv[])
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;
77 while ((ch = getopt(argc, argv, "apPQTwU:e")) != -1) {
83 show_function_prototypes = true;
86 algo = DIFFREG_ALGO_PATIENCE;
89 algo = DIFFREG_ALGO_MYERS_THEN_PATIENCE;
92 algo = DIFFREG_ALGO_NONE;
95 ignore_whitespace = true;
98 context_lines = atoi(optarg);
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));
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;
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,
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,
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,
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). */
156 const struct diff_algo_config no_algo = (struct diff_algo_config){
157 .impl = diff_algo_none,
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,
167 /* If the state for a forward-Myers is small enough, use Myers, otherwise first
169 const struct diff_config diff_config_myers_then_patience = {
170 .atomize_func = diff_atomize_text_by_line,
171 .algo = &myers_then_patience,
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,
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,
186 diffreg(char *file1, char *file2, enum diffreg_algo algo, bool force_text,
187 bool ignore_whitespace, bool show_function_prototypes, int context_lines,
192 struct stat st1, st2;
193 struct diff_input_info info = {
197 struct diff_data left = {}, right = {};
198 struct diff_result *result = NULL;
200 const struct diff_config *cfg;
205 case DIFFREG_ALGO_MYERS_THEN_MYERS_DIVIDE:
206 cfg = &diff_config_myers_then_myers_divide;
208 case DIFFREG_ALGO_MYERS_THEN_PATIENCE:
209 cfg = &diff_config_myers_then_patience;
211 case DIFFREG_ALGO_PATIENCE:
212 cfg = &diff_config_patience;
214 case DIFFREG_ALGO_NONE:
215 cfg = &diff_config_no_algo;
219 f1 = openfile(file1, &str1, &st1);
220 f2 = openfile(file2, &str2, &st2);
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;
229 rc = diff_atomize_file(&left, cfg, f1, str1, st1.st_size, diff_flags);
232 rc = diff_atomize_file(&right, cfg, f2, str2, st2.st_size, diff_flags);
236 result = diff_main(cfg, &left, &right);
238 rc = diff_output_plain(stdout, &info, result);
241 rc = diff_output_edscript(NULL, stdout, &info, result);
243 rc = diff_output_unidiff(NULL, stdout, &info, result,
248 diff_result_free(result);
249 diff_data_free(&left);
250 diff_data_free(&right);
252 munmap(str1, st1.st_size);
254 munmap(str2, st2.st_size);
262 openfile(const char *path, char **p, struct stat *st)
266 f = fopen(path, "r");
270 if (fstat(fileno(f), st) == -1)
274 *p = mmap(NULL, st->st_size, PROT_READ, MAP_PRIVATE, fileno(f), 0);
275 if (*p == MAP_FAILED)
277 *p = NULL; /* fall back on file I/O */