xref: /freebsd-src/usr.bin/diff/diffreg_new.c (revision 893839b119880d3fe8ab18aba4563af6c80cb875)
1d9a9f23dSDag-Erling Smørgrav /*
2d9a9f23dSDag-Erling Smørgrav  * Copyright (c) 2018 Martin Pieuchot
3d9a9f23dSDag-Erling Smørgrav  * Copyright (c) 2020 Neels Hofmeyr <neels@hofmeyr.de>
4d9a9f23dSDag-Erling Smørgrav  *
5d9a9f23dSDag-Erling Smørgrav  * Permission to use, copy, modify, and distribute this software for any
6d9a9f23dSDag-Erling Smørgrav  * purpose with or without fee is hereby granted, provided that the above
7d9a9f23dSDag-Erling Smørgrav  * copyright notice and this permission notice appear in all copies.
8d9a9f23dSDag-Erling Smørgrav  *
9d9a9f23dSDag-Erling Smørgrav  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10d9a9f23dSDag-Erling Smørgrav  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11d9a9f23dSDag-Erling Smørgrav  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12d9a9f23dSDag-Erling Smørgrav  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13d9a9f23dSDag-Erling Smørgrav  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14d9a9f23dSDag-Erling Smørgrav  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15d9a9f23dSDag-Erling Smørgrav  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16d9a9f23dSDag-Erling Smørgrav  */
17d9a9f23dSDag-Erling Smørgrav 
18d9a9f23dSDag-Erling Smørgrav #include <sys/types.h>
19eea5f8d4SDag-Erling Smørgrav #include <sys/capsicum.h>
20eea5f8d4SDag-Erling Smørgrav #ifndef DIFF_NO_MMAP
21eea5f8d4SDag-Erling Smørgrav #include <sys/mman.h>
22eea5f8d4SDag-Erling Smørgrav #endif
23eea5f8d4SDag-Erling Smørgrav #include <sys/stat.h>
24d9a9f23dSDag-Erling Smørgrav 
25d9a9f23dSDag-Erling Smørgrav #include <capsicum_helpers.h>
26d9a9f23dSDag-Erling Smørgrav #include <err.h>
27d9a9f23dSDag-Erling Smørgrav #include <fcntl.h>
28eea5f8d4SDag-Erling Smørgrav #include <stdbool.h>
29d9a9f23dSDag-Erling Smørgrav #include <stdint.h>
30d9a9f23dSDag-Erling Smørgrav #include <stdio.h>
31d9a9f23dSDag-Erling Smørgrav #include <stdlib.h>
32d9a9f23dSDag-Erling Smørgrav #include <string.h>
33eea5f8d4SDag-Erling Smørgrav #include <time.h>
34d9a9f23dSDag-Erling Smørgrav #include <unistd.h>
35d9a9f23dSDag-Erling Smørgrav 
36d9a9f23dSDag-Erling Smørgrav #include "diff.h"
37d9a9f23dSDag-Erling Smørgrav #include <arraylist.h>
38d9a9f23dSDag-Erling Smørgrav #include <diff_main.h>
39d9a9f23dSDag-Erling Smørgrav #include <diff_output.h>
40d9a9f23dSDag-Erling Smørgrav 
41d9a9f23dSDag-Erling Smørgrav const char *format_label(const char *, struct stat *);
42d9a9f23dSDag-Erling Smørgrav 
43d9a9f23dSDag-Erling Smørgrav enum diffreg_algo {
44d9a9f23dSDag-Erling Smørgrav 	DIFFREG_ALGO_MYERS_THEN_MYERS_DIVIDE = 0,
45d9a9f23dSDag-Erling Smørgrav 	DIFFREG_ALGO_MYERS_THEN_PATIENCE = 1,
46d9a9f23dSDag-Erling Smørgrav 	DIFFREG_ALGO_PATIENCE = 2,
47d9a9f23dSDag-Erling Smørgrav 	DIFFREG_ALGO_NONE = 3,
48d9a9f23dSDag-Erling Smørgrav };
49d9a9f23dSDag-Erling Smørgrav 
50d9a9f23dSDag-Erling Smørgrav int		 diffreg_new(char *, char *, int, int);
51d9a9f23dSDag-Erling Smørgrav FILE *		 openfile(const char *, char **, struct stat *);
52d9a9f23dSDag-Erling Smørgrav 
53d9a9f23dSDag-Erling Smørgrav static const struct diff_algo_config myers_then_patience;
54d9a9f23dSDag-Erling Smørgrav static const struct diff_algo_config myers_then_myers_divide;
55d9a9f23dSDag-Erling Smørgrav static const struct diff_algo_config patience;
56d9a9f23dSDag-Erling Smørgrav static const struct diff_algo_config myers_divide;
57d9a9f23dSDag-Erling Smørgrav 
58d9a9f23dSDag-Erling Smørgrav static const struct diff_algo_config myers_then_patience = (struct diff_algo_config){
59d9a9f23dSDag-Erling Smørgrav 	.impl = diff_algo_myers,
60d9a9f23dSDag-Erling Smørgrav 	.permitted_state_size = 1024 * 1024 * sizeof(int),
61d9a9f23dSDag-Erling Smørgrav 	.fallback_algo = &patience,
62d9a9f23dSDag-Erling Smørgrav };
63d9a9f23dSDag-Erling Smørgrav 
64d9a9f23dSDag-Erling Smørgrav static const struct diff_algo_config myers_then_myers_divide =
65d9a9f23dSDag-Erling Smørgrav 	(struct diff_algo_config){
66d9a9f23dSDag-Erling Smørgrav 	.impl = diff_algo_myers,
67d9a9f23dSDag-Erling Smørgrav 	.permitted_state_size = 1024 * 1024 * sizeof(int),
68d9a9f23dSDag-Erling Smørgrav 	.fallback_algo = &myers_divide,
69d9a9f23dSDag-Erling Smørgrav };
70d9a9f23dSDag-Erling Smørgrav 
71d9a9f23dSDag-Erling Smørgrav static const struct diff_algo_config patience = (struct diff_algo_config){
72d9a9f23dSDag-Erling Smørgrav 	.impl = diff_algo_patience,
73d9a9f23dSDag-Erling Smørgrav 	/* After subdivision, do Patience again: */
74d9a9f23dSDag-Erling Smørgrav 	.inner_algo = &patience,
75d9a9f23dSDag-Erling Smørgrav 	/* If subdivision failed, do Myers Divide et Impera: */
76d9a9f23dSDag-Erling Smørgrav 	.fallback_algo = &myers_then_myers_divide,
77d9a9f23dSDag-Erling Smørgrav };
78d9a9f23dSDag-Erling Smørgrav 
79d9a9f23dSDag-Erling Smørgrav static const struct diff_algo_config myers_divide = (struct diff_algo_config){
80d9a9f23dSDag-Erling Smørgrav 	.impl = diff_algo_myers_divide,
81d9a9f23dSDag-Erling Smørgrav 	/* When division succeeded, start from the top: */
82d9a9f23dSDag-Erling Smørgrav 	.inner_algo = &myers_then_myers_divide,
83d9a9f23dSDag-Erling Smørgrav 	/* (fallback_algo = NULL implies diff_algo_none). */
84d9a9f23dSDag-Erling Smørgrav };
85d9a9f23dSDag-Erling Smørgrav 
86d9a9f23dSDag-Erling Smørgrav static const struct diff_algo_config no_algo = (struct diff_algo_config){
87d9a9f23dSDag-Erling Smørgrav 	.impl = diff_algo_none,
88d9a9f23dSDag-Erling Smørgrav };
89d9a9f23dSDag-Erling Smørgrav 
90d9a9f23dSDag-Erling Smørgrav /* If the state for a forward-Myers is small enough, use Myers, otherwise first
91d9a9f23dSDag-Erling Smørgrav  * do a Myers-divide. */
92d9a9f23dSDag-Erling Smørgrav static const struct diff_config diff_config_myers_then_myers_divide = {
93d9a9f23dSDag-Erling Smørgrav 	.atomize_func = diff_atomize_text_by_line,
94d9a9f23dSDag-Erling Smørgrav 	.algo = &myers_then_myers_divide,
95d9a9f23dSDag-Erling Smørgrav };
96d9a9f23dSDag-Erling Smørgrav 
97d9a9f23dSDag-Erling Smørgrav /* If the state for a forward-Myers is small enough, use Myers, otherwise first
98d9a9f23dSDag-Erling Smørgrav  * do a Patience. */
99d9a9f23dSDag-Erling Smørgrav static const struct diff_config diff_config_myers_then_patience = {
100d9a9f23dSDag-Erling Smørgrav 	.atomize_func = diff_atomize_text_by_line,
101d9a9f23dSDag-Erling Smørgrav 	.algo = &myers_then_patience,
102d9a9f23dSDag-Erling Smørgrav };
103d9a9f23dSDag-Erling Smørgrav 
104d9a9f23dSDag-Erling Smørgrav /* Directly force Patience as a first divider of the source file. */
105d9a9f23dSDag-Erling Smørgrav static const struct diff_config diff_config_patience = {
106d9a9f23dSDag-Erling Smørgrav 	.atomize_func = diff_atomize_text_by_line,
107d9a9f23dSDag-Erling Smørgrav 	.algo = &patience,
108d9a9f23dSDag-Erling Smørgrav };
109d9a9f23dSDag-Erling Smørgrav 
110d9a9f23dSDag-Erling Smørgrav /* Directly force Patience as a first divider of the source file. */
111d9a9f23dSDag-Erling Smørgrav static const struct diff_config diff_config_no_algo = {
112d9a9f23dSDag-Erling Smørgrav 	.atomize_func = diff_atomize_text_by_line,
113d9a9f23dSDag-Erling Smørgrav };
114d9a9f23dSDag-Erling Smørgrav 
115d9a9f23dSDag-Erling Smørgrav const char *
116d9a9f23dSDag-Erling Smørgrav format_label(const char *oldlabel, struct stat *stb)
117d9a9f23dSDag-Erling Smørgrav {
118d9a9f23dSDag-Erling Smørgrav 	const char *time_format = "%Y-%m-%d %H:%M:%S";
119d9a9f23dSDag-Erling Smørgrav 	char *newlabel;
120d9a9f23dSDag-Erling Smørgrav 	char buf[256];
121d9a9f23dSDag-Erling Smørgrav 	char end[10];
122d9a9f23dSDag-Erling Smørgrav 	struct tm tm, *tm_ptr;
123d9a9f23dSDag-Erling Smørgrav 	int nsec = stb->st_mtim.tv_nsec;
124d9a9f23dSDag-Erling Smørgrav 	size_t newlabellen, timelen, endlen;
125d9a9f23dSDag-Erling Smørgrav 	tm_ptr = localtime_r(&stb->st_mtime, &tm);
126d9a9f23dSDag-Erling Smørgrav 
127d9a9f23dSDag-Erling Smørgrav 	timelen = strftime(buf, 256, time_format, tm_ptr);
128d9a9f23dSDag-Erling Smørgrav 	endlen = strftime(end, 10, "%z", tm_ptr);
129d9a9f23dSDag-Erling Smørgrav 
130d9a9f23dSDag-Erling Smørgrav 	/*
131d9a9f23dSDag-Erling Smørgrav 	 * The new label is the length of the time, old label, timezone,
132d9a9f23dSDag-Erling Smørgrav 	 * 9 characters for nanoseconds, and 4 characters for a period
133d9a9f23dSDag-Erling Smørgrav 	 * and for formatting.
134d9a9f23dSDag-Erling Smørgrav 	 */
135d9a9f23dSDag-Erling Smørgrav 	newlabellen = timelen + strlen(oldlabel) + endlen + 9 + 4;
136d9a9f23dSDag-Erling Smørgrav 	newlabel = calloc(newlabellen, sizeof(char));
137d9a9f23dSDag-Erling Smørgrav 
138d9a9f23dSDag-Erling Smørgrav 	snprintf(newlabel, newlabellen ,"%s\t%s.%.9d %s\n",
139d9a9f23dSDag-Erling Smørgrav 		oldlabel, buf, nsec, end);
140d9a9f23dSDag-Erling Smørgrav 
141d9a9f23dSDag-Erling Smørgrav 	return newlabel;
142d9a9f23dSDag-Erling Smørgrav }
143d9a9f23dSDag-Erling Smørgrav 
144d9a9f23dSDag-Erling Smørgrav int
145d9a9f23dSDag-Erling Smørgrav diffreg_new(char *file1, char *file2, int flags, int capsicum)
146d9a9f23dSDag-Erling Smørgrav {
147d9a9f23dSDag-Erling Smørgrav 	char *str1, *str2;
148d9a9f23dSDag-Erling Smørgrav 	FILE *f1, *f2;
149d9a9f23dSDag-Erling Smørgrav 	struct stat st1, st2;
150d9a9f23dSDag-Erling Smørgrav 	struct diff_input_info info;
151d9a9f23dSDag-Erling Smørgrav 	struct diff_data left = {}, right = {};
152d9a9f23dSDag-Erling Smørgrav 	struct diff_result *result = NULL;
153d9a9f23dSDag-Erling Smørgrav 	bool force_text, have_binary;
154d9a9f23dSDag-Erling Smørgrav 	int rc, atomizer_flags, rflags, diff_flags = 0;
155d9a9f23dSDag-Erling Smørgrav 	int context_lines = diff_context;
156d9a9f23dSDag-Erling Smørgrav 	const struct diff_config *cfg;
157d9a9f23dSDag-Erling Smørgrav 	enum diffreg_algo algo;
158d9a9f23dSDag-Erling Smørgrav 	cap_rights_t rights_ro;
159d9a9f23dSDag-Erling Smørgrav 
160d9a9f23dSDag-Erling Smørgrav 	algo = DIFFREG_ALGO_MYERS_THEN_MYERS_DIVIDE;
161d9a9f23dSDag-Erling Smørgrav 
162d9a9f23dSDag-Erling Smørgrav 	switch (algo) {
163d9a9f23dSDag-Erling Smørgrav 	default:
164d9a9f23dSDag-Erling Smørgrav 	case DIFFREG_ALGO_MYERS_THEN_MYERS_DIVIDE:
165d9a9f23dSDag-Erling Smørgrav 		cfg = &diff_config_myers_then_myers_divide;
166d9a9f23dSDag-Erling Smørgrav 		break;
167d9a9f23dSDag-Erling Smørgrav 	case DIFFREG_ALGO_MYERS_THEN_PATIENCE:
168d9a9f23dSDag-Erling Smørgrav 		cfg = &diff_config_myers_then_patience;
169d9a9f23dSDag-Erling Smørgrav 		break;
170d9a9f23dSDag-Erling Smørgrav 	case DIFFREG_ALGO_PATIENCE:
171d9a9f23dSDag-Erling Smørgrav 		cfg = &diff_config_patience;
172d9a9f23dSDag-Erling Smørgrav 		break;
173d9a9f23dSDag-Erling Smørgrav 	case DIFFREG_ALGO_NONE:
174d9a9f23dSDag-Erling Smørgrav 		cfg = &diff_config_no_algo;
175d9a9f23dSDag-Erling Smørgrav 		break;
176d9a9f23dSDag-Erling Smørgrav 	}
177d9a9f23dSDag-Erling Smørgrav 
178d9a9f23dSDag-Erling Smørgrav 	f1 = openfile(file1, &str1, &st1);
179d9a9f23dSDag-Erling Smørgrav 	f2 = openfile(file2, &str2, &st2);
180d9a9f23dSDag-Erling Smørgrav 
181d9a9f23dSDag-Erling Smørgrav 	if (capsicum) {
182d9a9f23dSDag-Erling Smørgrav 		cap_rights_init(&rights_ro, CAP_READ, CAP_FSTAT, CAP_SEEK);
183d9a9f23dSDag-Erling Smørgrav 		if (caph_rights_limit(fileno(f1), &rights_ro) < 0)
184d9a9f23dSDag-Erling Smørgrav 			err(2, "unable to limit rights on: %s", file1);
185d9a9f23dSDag-Erling Smørgrav 		if (caph_rights_limit(fileno(f2), &rights_ro) < 0)
186d9a9f23dSDag-Erling Smørgrav 			err(2, "unable to limit rights on: %s", file2);
187d9a9f23dSDag-Erling Smørgrav 		if (fileno(f1) == STDIN_FILENO || fileno(f2) == STDIN_FILENO) {
188d9a9f23dSDag-Erling Smørgrav 			/* stdin has already been limited */
189d9a9f23dSDag-Erling Smørgrav 			if (caph_limit_stderr() == -1)
190d9a9f23dSDag-Erling Smørgrav 				err(2, "unable to limit stderr");
191d9a9f23dSDag-Erling Smørgrav 			if (caph_limit_stdout() == -1)
192d9a9f23dSDag-Erling Smørgrav 				err(2, "unable to limit stdout");
193d9a9f23dSDag-Erling Smørgrav 		} else if (caph_limit_stdio() == -1)
194d9a9f23dSDag-Erling Smørgrav 				err(2, "unable to limit stdio");
195d9a9f23dSDag-Erling Smørgrav 		caph_cache_catpages();
196d9a9f23dSDag-Erling Smørgrav 		caph_cache_tzdata();
197d9a9f23dSDag-Erling Smørgrav 		if (caph_enter() < 0)
198d9a9f23dSDag-Erling Smørgrav 			err(2, "unable to enter capability mode");
199d9a9f23dSDag-Erling Smørgrav 	}
200d9a9f23dSDag-Erling Smørgrav 	/*
201d9a9f23dSDag-Erling Smørgrav 	 * If we have been given a label use that for the paths, if not format
202d9a9f23dSDag-Erling Smørgrav 	 * the path with the files modification time.
203d9a9f23dSDag-Erling Smørgrav 	 */
204d9a9f23dSDag-Erling Smørgrav 	info.flags = 0;
205d9a9f23dSDag-Erling Smørgrav 	info.left_path = (label[0] != NULL) ?
206d9a9f23dSDag-Erling Smørgrav 		label[0] : format_label(file1, &stb1);
207d9a9f23dSDag-Erling Smørgrav 	info.right_path = (label[1] != NULL) ?
208d9a9f23dSDag-Erling Smørgrav 		label[1] : format_label(file2, &stb2);
209d9a9f23dSDag-Erling Smørgrav 
210d9a9f23dSDag-Erling Smørgrav 	if (flags & D_FORCEASCII)
211d9a9f23dSDag-Erling Smørgrav 		diff_flags |= DIFF_FLAG_FORCE_TEXT_DATA;
212d9a9f23dSDag-Erling Smørgrav 	if (flags & D_IGNOREBLANKS)
213d9a9f23dSDag-Erling Smørgrav 		diff_flags |= DIFF_FLAG_IGNORE_WHITESPACE;
214d9a9f23dSDag-Erling Smørgrav 	if (flags & D_PROTOTYPE)
215d9a9f23dSDag-Erling Smørgrav 		diff_flags |= DIFF_FLAG_SHOW_PROTOTYPES;
216d9a9f23dSDag-Erling Smørgrav 
217d9a9f23dSDag-Erling Smørgrav 	if (diff_atomize_file(&left, cfg, f1, (uint8_t *)str1, st1.st_size, diff_flags)) {
218d9a9f23dSDag-Erling Smørgrav 		rc = D_ERROR;
219d9a9f23dSDag-Erling Smørgrav 		goto done;
220d9a9f23dSDag-Erling Smørgrav 	}
221b780b650SDag-Erling Smørgrav 	if (left.atomizer_flags & DIFF_ATOMIZER_FILE_TRUNCATED)
222b780b650SDag-Erling Smørgrav 		warnx("%s truncated", file1);
223d9a9f23dSDag-Erling Smørgrav 	if (diff_atomize_file(&right, cfg, f2, (uint8_t *)str2, st2.st_size, diff_flags)) {
224d9a9f23dSDag-Erling Smørgrav 		rc = D_ERROR;
225d9a9f23dSDag-Erling Smørgrav 		goto done;
226d9a9f23dSDag-Erling Smørgrav 	}
227b780b650SDag-Erling Smørgrav 	if (right.atomizer_flags & DIFF_ATOMIZER_FILE_TRUNCATED)
228b780b650SDag-Erling Smørgrav 		warnx("%s truncated", file2);
229d9a9f23dSDag-Erling Smørgrav 
230d9a9f23dSDag-Erling Smørgrav 	result = diff_main(cfg, &left, &right);
231d9a9f23dSDag-Erling Smørgrav 	if (result->rc != DIFF_RC_OK) {
232d9a9f23dSDag-Erling Smørgrav 		rc = D_ERROR;
233d9a9f23dSDag-Erling Smørgrav 		status |= 2;
234d9a9f23dSDag-Erling Smørgrav 		goto done;
235d9a9f23dSDag-Erling Smørgrav 	}
236d9a9f23dSDag-Erling Smørgrav 	/*
237d9a9f23dSDag-Erling Smørgrav 	 * If there wasn't an error, but we don't have any printable chunks
238d9a9f23dSDag-Erling Smørgrav 	 * then the files must match.
239d9a9f23dSDag-Erling Smørgrav 	 */
240d9a9f23dSDag-Erling Smørgrav 	if (!diff_result_contains_printable_chunks(result)) {
241d9a9f23dSDag-Erling Smørgrav 		rc = D_SAME;
242d9a9f23dSDag-Erling Smørgrav 		goto done;
243d9a9f23dSDag-Erling Smørgrav 	}
244d9a9f23dSDag-Erling Smørgrav 
245d9a9f23dSDag-Erling Smørgrav 	atomizer_flags = (result->left->atomizer_flags | result->right->atomizer_flags);
246d9a9f23dSDag-Erling Smørgrav 	rflags = (result->left->root->diff_flags | result->right->root->diff_flags);
247d9a9f23dSDag-Erling Smørgrav 	force_text = (rflags & DIFF_FLAG_FORCE_TEXT_DATA);
248d9a9f23dSDag-Erling Smørgrav 	have_binary = (atomizer_flags & DIFF_ATOMIZER_FOUND_BINARY_DATA);
249d9a9f23dSDag-Erling Smørgrav 
250d9a9f23dSDag-Erling Smørgrav 	if (have_binary && !force_text) {
251d9a9f23dSDag-Erling Smørgrav 		rc = D_BINARY;
252d9a9f23dSDag-Erling Smørgrav 		status |= 1;
253d9a9f23dSDag-Erling Smørgrav 		goto done;
254d9a9f23dSDag-Erling Smørgrav 	}
255d9a9f23dSDag-Erling Smørgrav 
2564e859e67SDag-Erling Smørgrav 	if (color)
2574e859e67SDag-Erling Smørgrav 		diff_output_set_colors(color, del_code, add_code);
258d9a9f23dSDag-Erling Smørgrav 	if (diff_format == D_NORMAL) {
259d9a9f23dSDag-Erling Smørgrav 		rc = diff_output_plain(NULL, stdout, &info, result, false);
260d9a9f23dSDag-Erling Smørgrav 	} else if (diff_format == D_EDIT) {
261d9a9f23dSDag-Erling Smørgrav 		rc = diff_output_edscript(NULL, stdout, &info, result);
262d9a9f23dSDag-Erling Smørgrav 	} else {
263d9a9f23dSDag-Erling Smørgrav 		rc = diff_output_unidiff(NULL, stdout, &info, result,
264d9a9f23dSDag-Erling Smørgrav 		    context_lines);
265d9a9f23dSDag-Erling Smørgrav 	}
266d9a9f23dSDag-Erling Smørgrav 	if (rc != DIFF_RC_OK) {
267d9a9f23dSDag-Erling Smørgrav 		rc = D_ERROR;
268d9a9f23dSDag-Erling Smørgrav 		status |= 2;
269d9a9f23dSDag-Erling Smørgrav 	} else {
270d9a9f23dSDag-Erling Smørgrav 		rc = D_DIFFER;
271d9a9f23dSDag-Erling Smørgrav 		status |= 1;
272d9a9f23dSDag-Erling Smørgrav 	}
273d9a9f23dSDag-Erling Smørgrav done:
274d9a9f23dSDag-Erling Smørgrav 	diff_result_free(result);
275d9a9f23dSDag-Erling Smørgrav 	diff_data_free(&left);
276d9a9f23dSDag-Erling Smørgrav 	diff_data_free(&right);
277eea5f8d4SDag-Erling Smørgrav #ifndef DIFF_NO_MMAP
278d9a9f23dSDag-Erling Smørgrav 	if (str1)
279d9a9f23dSDag-Erling Smørgrav 		munmap(str1, st1.st_size);
280d9a9f23dSDag-Erling Smørgrav 	if (str2)
281d9a9f23dSDag-Erling Smørgrav 		munmap(str2, st2.st_size);
282eea5f8d4SDag-Erling Smørgrav #endif
283d9a9f23dSDag-Erling Smørgrav 	fclose(f1);
284d9a9f23dSDag-Erling Smørgrav 	fclose(f2);
285d9a9f23dSDag-Erling Smørgrav 
286d9a9f23dSDag-Erling Smørgrav 	return rc;
287d9a9f23dSDag-Erling Smørgrav }
288d9a9f23dSDag-Erling Smørgrav 
289d9a9f23dSDag-Erling Smørgrav FILE *
290d9a9f23dSDag-Erling Smørgrav openfile(const char *path, char **p, struct stat *st)
291d9a9f23dSDag-Erling Smørgrav {
292d9a9f23dSDag-Erling Smørgrav 	FILE *f = NULL;
293d9a9f23dSDag-Erling Smørgrav 
294d9a9f23dSDag-Erling Smørgrav 	if (strcmp(path, "-") == 0)
295d9a9f23dSDag-Erling Smørgrav 		f = stdin;
296d9a9f23dSDag-Erling Smørgrav 	else
297d9a9f23dSDag-Erling Smørgrav 		f = fopen(path, "r");
298d9a9f23dSDag-Erling Smørgrav 
299d9a9f23dSDag-Erling Smørgrav 	if (f == NULL)
300d9a9f23dSDag-Erling Smørgrav 		err(2, "%s", path);
301d9a9f23dSDag-Erling Smørgrav 
302d9a9f23dSDag-Erling Smørgrav 	if (fstat(fileno(f), st) == -1)
303d9a9f23dSDag-Erling Smørgrav 		err(2, "%s", path);
304d9a9f23dSDag-Erling Smørgrav 
305d9a9f23dSDag-Erling Smørgrav #ifndef DIFF_NO_MMAP
306d9a9f23dSDag-Erling Smørgrav 	*p = mmap(NULL, st->st_size, PROT_READ, MAP_PRIVATE, fileno(f), 0);
307d9a9f23dSDag-Erling Smørgrav 	if (*p == MAP_FAILED)
308d9a9f23dSDag-Erling Smørgrav #endif
309d9a9f23dSDag-Erling Smørgrav 		*p = NULL; /* fall back on file I/O */
310d9a9f23dSDag-Erling Smørgrav 
311d9a9f23dSDag-Erling Smørgrav 	return f;
312d9a9f23dSDag-Erling Smørgrav }
313d9a9f23dSDag-Erling Smørgrav 
314d9a9f23dSDag-Erling Smørgrav bool
315d9a9f23dSDag-Erling Smørgrav can_libdiff(int flags)
316d9a9f23dSDag-Erling Smørgrav {
317*893839b1SDag-Erling Smørgrav 	/* libdiff's atomizer can only deal with files */
318*893839b1SDag-Erling Smørgrav 	if (!S_ISREG(stb1.st_mode) || !S_ISREG(stb2.st_mode))
319d9a9f23dSDag-Erling Smørgrav 		return false;
320d9a9f23dSDag-Erling Smørgrav 
321d9a9f23dSDag-Erling Smørgrav 	/* Is this one of the supported input/output modes for diffreg_new? */
322d9a9f23dSDag-Erling Smørgrav 	if ((flags == 0 || !(flags & ~D_NEWALGO_FLAGS)) &&
323d9a9f23dSDag-Erling Smørgrav 		ignore_pats == NULL && (
324d9a9f23dSDag-Erling Smørgrav 		diff_format == D_NORMAL ||
325d9a9f23dSDag-Erling Smørgrav #if 0
326d9a9f23dSDag-Erling Smørgrav 		diff_format == D_EDIT ||
327d9a9f23dSDag-Erling Smørgrav #endif
328d9a9f23dSDag-Erling Smørgrav 		diff_format == D_UNIFIED) &&
329d9a9f23dSDag-Erling Smørgrav 		(diff_algorithm == D_DIFFMYERS || diff_algorithm == D_DIFFPATIENCE)) {
330d9a9f23dSDag-Erling Smørgrav 		return true;
331d9a9f23dSDag-Erling Smørgrav 	}
332d9a9f23dSDag-Erling Smørgrav 
333d9a9f23dSDag-Erling Smørgrav 	/* Fallback to using stone. */
334d9a9f23dSDag-Erling Smørgrav 	return false;
335d9a9f23dSDag-Erling Smørgrav }
336