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 <unistd.h>
29 #include <diff/diff_main.h>
30 #include <diff/diff_output.h>
32 #ifdef __linux__
33 /* stupid shims to compile and test on linux */
34 #define __dead
36 static const char *getprogname()
37 {
38 return "diff";
39 }
40 #endif
42 __dead void usage(void);
43 int diffreg(char *, char *, int);
44 char *mmapfile(const char *, struct stat *);
46 __dead void
47 usage(void)
48 {
49 fprintf(stderr,
50 "usage: %s [-p] file1 file2\n"
51 "\n"
52 " -p Use Patience Diff (slower but often nicer)\n"
53 , getprogname());
54 exit(1);
55 }
57 static bool do_patience = false;
59 int
60 main(int argc, char *argv[])
61 {
62 int ch;
64 while ((ch = getopt(argc, argv, "p")) != -1) {
65 switch (ch) {
66 case 'p':
67 do_patience = true;
68 break;
69 default:
70 usage();
71 }
72 }
74 argc -= optind;
75 argv += optind;
77 if (argc != 2)
78 usage();
80 return diffreg(argv[0], argv[1], 0);
81 }
83 const struct diff_algo_config myers_then_patience;
84 const struct diff_algo_config myers_then_myers_divide;
85 const struct diff_algo_config patience;
86 const struct diff_algo_config myers_divide;
88 const struct diff_algo_config myers_then_patience = (struct diff_algo_config){
89 .impl = diff_algo_myers,
90 .permitted_state_size = 1024 * 1024 * sizeof(int),
91 .fallback_algo = &patience,
92 };
94 const struct diff_algo_config myers_then_myers_divide =
95 (struct diff_algo_config){
96 .impl = diff_algo_myers,
97 .permitted_state_size = 1024 * 1024 * sizeof(int),
98 .fallback_algo = &myers_divide,
99 };
101 const struct diff_algo_config patience = (struct diff_algo_config){
102 .impl = diff_algo_patience,
103 /* After subdivision, do Patience again: */
104 .inner_algo = &patience,
105 /* If subdivision failed, do Myers Divide et Impera: */
106 .fallback_algo = &myers_then_myers_divide,
107 };
109 const struct diff_algo_config myers_divide = (struct diff_algo_config){
110 .impl = diff_algo_myers_divide,
111 /* When division succeeded, start from the top: */
112 .inner_algo = &myers_then_myers_divide,
113 /* (fallback_algo = NULL implies diff_algo_none). */
114 };
116 const struct diff_config diff_config = {
117 .atomize_func = diff_atomize_text_by_line,
118 .algo = &myers_then_myers_divide,
119 };
121 const struct diff_config diff_config_patience = {
122 .atomize_func = diff_atomize_text_by_line,
123 .algo = &myers_then_patience,
124 };
126 int
127 diffreg(char *file1, char *file2, int flags)
129 char *str1, *str2;
130 struct stat st1, st2;
131 struct diff_input_info info = {
132 .left_path = file1,
133 .right_path = file2,
134 };
135 struct diff_result *result;
136 enum diff_rc rc;
137 const struct diff_config *cfg;
139 cfg = do_patience ? &diff_config_patience : &diff_config;
141 str1 = mmapfile(file1, &st1);
142 str2 = mmapfile(file2, &st2);
144 result = diff_main(cfg, str1, st1.st_size, str2, st2.st_size);
145 #if 0
146 rc = diff_output_plain(stdout, &info, result);
147 #else
148 rc = diff_output_unidiff(stdout, &info, result, 3);
149 #endif
150 diff_result_free(result);
152 munmap(str1, st1.st_size);
153 munmap(str2, st2.st_size);
155 return rc;
158 char *
159 mmapfile(const char *path, struct stat *st)
161 int fd;
162 char *p;
164 fd = open(path, O_RDONLY);
165 if (fd == -1)
166 err(2, "%s", path);
168 if (fstat(fd, st) == -1)
169 err(2, "%s", path);
171 if ((uintmax_t)st->st_size > SIZE_MAX)
172 errx(2, "%s: file too big to fit memory", path);
174 p = mmap(NULL, st->st_size, PROT_READ, MAP_PRIVATE, fd, 0);
175 if (p == MAP_FAILED)
176 err(2, "mmap");
178 close(fd);
180 return p;