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>
22 #include <err.h>
23 #include <fcntl.h>
24 #include <inttypes.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <stdbool.h>
28 #include <string.h>
29 #include <unistd.h>
31 #include <diff/arraylist.h>
32 #include <diff/diff_main.h>
33 #include <diff/diff_output.h>
35 #ifdef __linux__
36 /* stupid shims to compile and test on linux */
37 #define __dead
39 static inline const char *getprogname(void)
40 {
41 return "diff";
42 }
43 #endif
45 __dead void usage(void);
46 int diffreg(char *, char *, bool, bool, int);
47 int openfile(const char *, char **, struct stat *);
49 __dead void
50 usage(void)
51 {
52 fprintf(stderr,
53 "usage: %s [-pw] [-C n] file1 file2\n"
54 "\n"
55 " -p Use Patience Diff (slower but often nicer)\n"
56 " -w Ignore Whitespace\n"
57 " -C n Number of Context Lines\n"
58 , getprogname());
59 exit(1);
60 }
62 int
63 main(int argc, char *argv[])
64 {
65 int ch, rc;
66 bool do_patience = false, ignore_whitespace = false;
67 int context_lines = 3;
69 while ((ch = getopt(argc, argv, "pwC:")) != -1) {
70 switch (ch) {
71 case 'p':
72 do_patience = true;
73 break;
74 case 'w':
75 ignore_whitespace = true;
76 break;
77 case 'C':
78 context_lines = atoi(optarg);
79 break;
80 default:
81 usage();
82 }
83 }
85 argc -= optind;
86 argv += optind;
88 if (argc != 2)
89 usage();
91 rc = diffreg(argv[0], argv[1], do_patience, ignore_whitespace,
92 context_lines);
93 if (rc != DIFF_RC_OK) {
94 fprintf(stderr, "diff: %s\n", strerror(rc));
95 return 1;
96 }
97 return 0;
98 }
100 const struct diff_algo_config myers_then_patience;
101 const struct diff_algo_config myers_then_myers_divide;
102 const struct diff_algo_config patience;
103 const struct diff_algo_config myers_divide;
105 const struct diff_algo_config myers_then_patience = (struct diff_algo_config){
106 .impl = diff_algo_myers,
107 .permitted_state_size = 1024 * 1024 * sizeof(int),
108 .fallback_algo = &patience,
109 };
111 const struct diff_algo_config myers_then_myers_divide =
112 (struct diff_algo_config){
113 .impl = diff_algo_myers,
114 .permitted_state_size = 1024 * 1024 * sizeof(int),
115 .fallback_algo = &myers_divide,
116 };
118 const struct diff_algo_config patience = (struct diff_algo_config){
119 .impl = diff_algo_patience,
120 /* After subdivision, do Patience again: */
121 .inner_algo = &patience,
122 /* If subdivision failed, do Myers Divide et Impera: */
123 .fallback_algo = &myers_then_myers_divide,
124 };
126 const struct diff_algo_config myers_divide = (struct diff_algo_config){
127 .impl = diff_algo_myers_divide,
128 /* When division succeeded, start from the top: */
129 .inner_algo = &myers_then_myers_divide,
130 /* (fallback_algo = NULL implies diff_algo_none). */
131 };
133 const struct diff_config diff_config = {
134 .atomize_func = diff_atomize_text_by_line,
135 .algo = &myers_then_myers_divide,
136 };
138 const struct diff_config diff_config_patience = {
139 .atomize_func = diff_atomize_text_by_line,
140 .algo = &myers_then_patience,
141 };
143 int
144 diffreg(char *file1, char *file2, bool do_patience, bool ignore_whitespace,
145 int context_lines)
147 char *str1, *str2;
148 int fd1, fd2;
149 struct stat st1, st2;
150 struct diff_input_info info = {
151 .left_path = file1,
152 .right_path = file2,
153 };
154 struct diff_result *result;
155 int rc;
156 const struct diff_config *cfg;
158 cfg = do_patience ? &diff_config_patience : &diff_config;
160 fd1 = openfile(file1, &str1, &st1);
161 fd2 = openfile(file2, &str2, &st2);
163 result = diff_main(cfg, fd1, str1, st1.st_size, fd2, str2, st2.st_size,
164 ignore_whitespace);
165 #if 0
166 rc = diff_output_plain(stdout, &info, result);
167 #else
168 rc = diff_output_unidiff(stdout, &info, result, context_lines);
169 #endif
170 diff_result_free(result);
172 if (str1)
173 munmap(str1, st1.st_size);
174 if (str2)
175 munmap(str2, st2.st_size);
176 close(fd1);
177 close(fd2);
179 return rc;
182 int
183 openfile(const char *path, char **p, struct stat *st)
185 int fd;
187 fd = open(path, O_RDONLY);
188 if (fd == -1)
189 err(2, "%s", path);
191 if (fstat(fd, st) == -1)
192 err(2, "%s", path);
194 #ifndef DIFF_NO_MMAP
195 *p = mmap(NULL, st->st_size, PROT_READ, MAP_PRIVATE, fd, 0);
196 if (*p == MAP_FAILED)
197 #endif
198 *p = NULL; /* fall back on file I/O */
200 return fd;