Blob


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 */
19 #include <sys/mman.h>
20 #include <sys/stat.h>
21 #include <sys/types.h>
23 #include <err.h>
24 #include <fcntl.h>
25 #include <inttypes.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <stdbool.h>
29 #include <string.h>
30 #include <unistd.h>
32 #include <arraylist.h>
33 #include <diff_main.h>
34 #include <diff_output.h>
36 __dead void usage(void);
37 int diffreg(char *, char *, bool, bool, int, bool);
38 FILE * openfile(const char *, char **, struct stat *);
40 __dead void
41 usage(void)
42 {
43 fprintf(stderr,
44 "usage: %s [-pwe] [-U n] file1 file2\n"
45 "\n"
46 " -p Use Patience Diff (slower but often nicer)\n"
47 " -w Ignore Whitespace\n"
48 " -U n Number of Context Lines\n"
49 " -e Produce ed script output\n"
50 , getprogname());
51 exit(1);
52 }
54 int
55 main(int argc, char *argv[])
56 {
57 int ch, rc;
58 bool do_patience = false, ignore_whitespace = false;
59 bool edscript = false;
60 int context_lines = 3;
62 while ((ch = getopt(argc, argv, "pwU:e")) != -1) {
63 switch (ch) {
64 case 'p':
65 do_patience = true;
66 break;
67 case 'w':
68 ignore_whitespace = true;
69 break;
70 case 'U':
71 context_lines = atoi(optarg);
72 break;
73 case 'e':
74 edscript = true;
75 break;
76 default:
77 usage();
78 }
79 }
81 argc -= optind;
82 argv += optind;
84 if (argc != 2)
85 usage();
87 rc = diffreg(argv[0], argv[1], do_patience, ignore_whitespace,
88 context_lines, edscript);
89 if (rc != DIFF_RC_OK) {
90 fprintf(stderr, "diff: %s\n", strerror(rc));
91 return 1;
92 }
93 return 0;
94 }
96 const struct diff_algo_config myers_then_patience;
97 const struct diff_algo_config myers_then_myers_divide;
98 const struct diff_algo_config patience;
99 const struct diff_algo_config myers_divide;
101 const struct diff_algo_config myers_then_patience = (struct diff_algo_config){
102 .impl = diff_algo_myers,
103 .permitted_state_size = 1024 * 1024 * sizeof(int),
104 .fallback_algo = &patience,
105 };
107 const struct diff_algo_config myers_then_myers_divide =
108 (struct diff_algo_config){
109 .impl = diff_algo_myers,
110 .permitted_state_size = 1024 * 1024 * sizeof(int),
111 .fallback_algo = &myers_divide,
112 };
114 const struct diff_algo_config patience = (struct diff_algo_config){
115 .impl = diff_algo_patience,
116 /* After subdivision, do Patience again: */
117 .inner_algo = &patience,
118 /* If subdivision failed, do Myers Divide et Impera: */
119 .fallback_algo = &myers_then_myers_divide,
120 };
122 const struct diff_algo_config myers_divide = (struct diff_algo_config){
123 .impl = diff_algo_myers_divide,
124 /* When division succeeded, start from the top: */
125 .inner_algo = &myers_then_myers_divide,
126 /* (fallback_algo = NULL implies diff_algo_none). */
127 };
129 const struct diff_config diff_config = {
130 .atomize_func = diff_atomize_text_by_line,
131 .algo = &myers_then_myers_divide,
132 };
134 const struct diff_config diff_config_patience = {
135 .atomize_func = diff_atomize_text_by_line,
136 .algo = &myers_then_patience,
137 };
139 int
140 diffreg(char *file1, char *file2, bool do_patience, bool ignore_whitespace,
141 int context_lines, bool edscript)
143 char *str1, *str2;
144 FILE *f1, *f2;
145 struct stat st1, st2;
146 struct diff_input_info info = {
147 .left_path = file1,
148 .right_path = file2,
149 };
150 struct diff_result *result;
151 int rc;
152 const struct diff_config *cfg;
153 int diff_flags = 0;
155 cfg = do_patience ? &diff_config_patience : &diff_config;
157 f1 = openfile(file1, &str1, &st1);
158 f2 = openfile(file2, &str2, &st2);
160 if (ignore_whitespace)
161 diff_flags |= DIFF_FLAG_IGNORE_WHITESPACE;
163 result = diff_main(cfg, f1, str1, st1.st_size, f2, str2, st2.st_size,
164 diff_flags);
165 #if 0
166 rc = diff_output_plain(stdout, &info, result);
167 #else
168 if (edscript)
169 rc = diff_output_edscript(NULL, stdout, &info, result);
170 else {
171 rc = diff_output_unidiff(NULL, stdout, &info, result,
172 context_lines);
174 #endif
175 diff_result_free(result);
177 if (str1)
178 munmap(str1, st1.st_size);
179 if (str2)
180 munmap(str2, st2.st_size);
181 fclose(f1);
182 fclose(f2);
184 return rc;
187 FILE *
188 openfile(const char *path, char **p, struct stat *st)
190 FILE *f = NULL;
192 f = fopen(path, "r");
193 if (f == NULL)
194 err(2, "%s", path);
196 if (fstat(fileno(f), st) == -1)
197 err(2, "%s", path);
199 #ifndef DIFF_NO_MMAP
200 *p = mmap(NULL, st->st_size, PROT_READ, MAP_PRIVATE, fileno(f), 0);
201 if (*p == MAP_FAILED)
202 #endif
203 *p = NULL; /* fall back on file I/O */
205 return f;