xref: /freebsd-src/contrib/libdiff/lib/diff_main.c (revision 59c8e88e72633afbc47a4ace0d2170d00d51f7dc)
1*59c8e88eSDag-Erling Smørgrav /* Generic infrastructure to implement various diff algorithms (implementation). */
2*59c8e88eSDag-Erling Smørgrav /*
3*59c8e88eSDag-Erling Smørgrav  * Copyright (c) 2020 Neels Hofmeyr <neels@hofmeyr.de>
4*59c8e88eSDag-Erling Smørgrav  *
5*59c8e88eSDag-Erling Smørgrav  * Permission to use, copy, modify, and distribute this software for any
6*59c8e88eSDag-Erling Smørgrav  * purpose with or without fee is hereby granted, provided that the above
7*59c8e88eSDag-Erling Smørgrav  * copyright notice and this permission notice appear in all copies.
8*59c8e88eSDag-Erling Smørgrav  *
9*59c8e88eSDag-Erling Smørgrav  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10*59c8e88eSDag-Erling Smørgrav  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11*59c8e88eSDag-Erling Smørgrav  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12*59c8e88eSDag-Erling Smørgrav  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13*59c8e88eSDag-Erling Smørgrav  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14*59c8e88eSDag-Erling Smørgrav  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15*59c8e88eSDag-Erling Smørgrav  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16*59c8e88eSDag-Erling Smørgrav  */
17*59c8e88eSDag-Erling Smørgrav 
18*59c8e88eSDag-Erling Smørgrav 
19*59c8e88eSDag-Erling Smørgrav #include <sys/queue.h>
20*59c8e88eSDag-Erling Smørgrav #include <ctype.h>
21*59c8e88eSDag-Erling Smørgrav #include <errno.h>
22*59c8e88eSDag-Erling Smørgrav #include <stdint.h>
23*59c8e88eSDag-Erling Smørgrav #include <stdlib.h>
24*59c8e88eSDag-Erling Smørgrav #include <stdbool.h>
25*59c8e88eSDag-Erling Smørgrav #include <stdio.h>
26*59c8e88eSDag-Erling Smørgrav #include <string.h>
27*59c8e88eSDag-Erling Smørgrav #include <limits.h>
28*59c8e88eSDag-Erling Smørgrav #include <unistd.h>
29*59c8e88eSDag-Erling Smørgrav 
30*59c8e88eSDag-Erling Smørgrav #include <assert.h>
31*59c8e88eSDag-Erling Smørgrav 
32*59c8e88eSDag-Erling Smørgrav #include <arraylist.h>
33*59c8e88eSDag-Erling Smørgrav #include <diff_main.h>
34*59c8e88eSDag-Erling Smørgrav 
35*59c8e88eSDag-Erling Smørgrav #include "diff_internal.h"
36*59c8e88eSDag-Erling Smørgrav #include "diff_debug.h"
37*59c8e88eSDag-Erling Smørgrav 
38*59c8e88eSDag-Erling Smørgrav inline enum diff_chunk_type
39*59c8e88eSDag-Erling Smørgrav diff_chunk_type(const struct diff_chunk *chunk)
40*59c8e88eSDag-Erling Smørgrav {
41*59c8e88eSDag-Erling Smørgrav 	if (!chunk->left_count && !chunk->right_count)
42*59c8e88eSDag-Erling Smørgrav 		return CHUNK_EMPTY;
43*59c8e88eSDag-Erling Smørgrav 	if (!chunk->solved)
44*59c8e88eSDag-Erling Smørgrav 		return CHUNK_ERROR;
45*59c8e88eSDag-Erling Smørgrav 	if (!chunk->right_count)
46*59c8e88eSDag-Erling Smørgrav 		return CHUNK_MINUS;
47*59c8e88eSDag-Erling Smørgrav 	if (!chunk->left_count)
48*59c8e88eSDag-Erling Smørgrav 		return CHUNK_PLUS;
49*59c8e88eSDag-Erling Smørgrav 	if (chunk->left_count != chunk->right_count)
50*59c8e88eSDag-Erling Smørgrav 		return CHUNK_ERROR;
51*59c8e88eSDag-Erling Smørgrav 	return CHUNK_SAME;
52*59c8e88eSDag-Erling Smørgrav }
53*59c8e88eSDag-Erling Smørgrav 
54*59c8e88eSDag-Erling Smørgrav static int
55*59c8e88eSDag-Erling Smørgrav read_at(FILE *f, off_t at_pos, unsigned char *buf, size_t len)
56*59c8e88eSDag-Erling Smørgrav {
57*59c8e88eSDag-Erling Smørgrav 	int r;
58*59c8e88eSDag-Erling Smørgrav 	if (fseeko(f, at_pos, SEEK_SET) == -1)
59*59c8e88eSDag-Erling Smørgrav 		return errno;
60*59c8e88eSDag-Erling Smørgrav 	r = fread(buf, sizeof(char), len, f);
61*59c8e88eSDag-Erling Smørgrav 	if ((r == 0 || r < len) && ferror(f))
62*59c8e88eSDag-Erling Smørgrav 		return EIO;
63*59c8e88eSDag-Erling Smørgrav 	if (r != len)
64*59c8e88eSDag-Erling Smørgrav 		return EIO;
65*59c8e88eSDag-Erling Smørgrav 	return 0;
66*59c8e88eSDag-Erling Smørgrav }
67*59c8e88eSDag-Erling Smørgrav 
68*59c8e88eSDag-Erling Smørgrav static int
69*59c8e88eSDag-Erling Smørgrav buf_cmp(const unsigned char *left, size_t left_len,
70*59c8e88eSDag-Erling Smørgrav 	const unsigned char *right, size_t right_len,
71*59c8e88eSDag-Erling Smørgrav 	bool ignore_whitespace)
72*59c8e88eSDag-Erling Smørgrav {
73*59c8e88eSDag-Erling Smørgrav 	int cmp;
74*59c8e88eSDag-Erling Smørgrav 
75*59c8e88eSDag-Erling Smørgrav 	if (ignore_whitespace) {
76*59c8e88eSDag-Erling Smørgrav 		int il = 0, ir = 0;
77*59c8e88eSDag-Erling Smørgrav 		while (il < left_len && ir < right_len) {
78*59c8e88eSDag-Erling Smørgrav 			unsigned char cl = left[il];
79*59c8e88eSDag-Erling Smørgrav 			unsigned char cr = right[ir];
80*59c8e88eSDag-Erling Smørgrav 
81*59c8e88eSDag-Erling Smørgrav 			if (isspace((unsigned char)cl) && il < left_len) {
82*59c8e88eSDag-Erling Smørgrav 				il++;
83*59c8e88eSDag-Erling Smørgrav 				continue;
84*59c8e88eSDag-Erling Smørgrav 			}
85*59c8e88eSDag-Erling Smørgrav 			if (isspace((unsigned char)cr) && ir < right_len) {
86*59c8e88eSDag-Erling Smørgrav 				ir++;
87*59c8e88eSDag-Erling Smørgrav 				continue;
88*59c8e88eSDag-Erling Smørgrav 			}
89*59c8e88eSDag-Erling Smørgrav 
90*59c8e88eSDag-Erling Smørgrav 			if (cl > cr)
91*59c8e88eSDag-Erling Smørgrav 				return 1;
92*59c8e88eSDag-Erling Smørgrav 			if (cr > cl)
93*59c8e88eSDag-Erling Smørgrav 				return -1;
94*59c8e88eSDag-Erling Smørgrav 			il++;
95*59c8e88eSDag-Erling Smørgrav 			ir++;
96*59c8e88eSDag-Erling Smørgrav 		}
97*59c8e88eSDag-Erling Smørgrav 		while (il < left_len) {
98*59c8e88eSDag-Erling Smørgrav 			unsigned char cl = left[il++];
99*59c8e88eSDag-Erling Smørgrav 			if (!isspace((unsigned char)cl))
100*59c8e88eSDag-Erling Smørgrav 				return 1;
101*59c8e88eSDag-Erling Smørgrav 		}
102*59c8e88eSDag-Erling Smørgrav 		while (ir < right_len) {
103*59c8e88eSDag-Erling Smørgrav 			unsigned char cr = right[ir++];
104*59c8e88eSDag-Erling Smørgrav 			if (!isspace((unsigned char)cr))
105*59c8e88eSDag-Erling Smørgrav 				return -1;
106*59c8e88eSDag-Erling Smørgrav 		}
107*59c8e88eSDag-Erling Smørgrav 
108*59c8e88eSDag-Erling Smørgrav 		return 0;
109*59c8e88eSDag-Erling Smørgrav 	}
110*59c8e88eSDag-Erling Smørgrav 
111*59c8e88eSDag-Erling Smørgrav 	cmp = memcmp(left, right, MIN(left_len, right_len));
112*59c8e88eSDag-Erling Smørgrav 	if (cmp)
113*59c8e88eSDag-Erling Smørgrav 		return cmp;
114*59c8e88eSDag-Erling Smørgrav 	if (left_len == right_len)
115*59c8e88eSDag-Erling Smørgrav 		return 0;
116*59c8e88eSDag-Erling Smørgrav 	return (left_len > right_len) ? 1 : -1;
117*59c8e88eSDag-Erling Smørgrav }
118*59c8e88eSDag-Erling Smørgrav 
119*59c8e88eSDag-Erling Smørgrav int
120*59c8e88eSDag-Erling Smørgrav diff_atom_cmp(int *cmp,
121*59c8e88eSDag-Erling Smørgrav 	      const struct diff_atom *left,
122*59c8e88eSDag-Erling Smørgrav 	      const struct diff_atom *right)
123*59c8e88eSDag-Erling Smørgrav {
124*59c8e88eSDag-Erling Smørgrav 	off_t remain_left, remain_right;
125*59c8e88eSDag-Erling Smørgrav 	int flags = (left->root->diff_flags | right->root->diff_flags);
126*59c8e88eSDag-Erling Smørgrav 	bool ignore_whitespace = (flags & DIFF_FLAG_IGNORE_WHITESPACE);
127*59c8e88eSDag-Erling Smørgrav 
128*59c8e88eSDag-Erling Smørgrav 	if (!left->len && !right->len) {
129*59c8e88eSDag-Erling Smørgrav 		*cmp = 0;
130*59c8e88eSDag-Erling Smørgrav 		return 0;
131*59c8e88eSDag-Erling Smørgrav 	}
132*59c8e88eSDag-Erling Smørgrav 	if (!ignore_whitespace) {
133*59c8e88eSDag-Erling Smørgrav 		if (!right->len) {
134*59c8e88eSDag-Erling Smørgrav 			*cmp = 1;
135*59c8e88eSDag-Erling Smørgrav 			return 0;
136*59c8e88eSDag-Erling Smørgrav 		}
137*59c8e88eSDag-Erling Smørgrav 		if (!left->len) {
138*59c8e88eSDag-Erling Smørgrav 			*cmp = -1;
139*59c8e88eSDag-Erling Smørgrav 			return 0;
140*59c8e88eSDag-Erling Smørgrav 		}
141*59c8e88eSDag-Erling Smørgrav 	}
142*59c8e88eSDag-Erling Smørgrav 
143*59c8e88eSDag-Erling Smørgrav 	if (left->at != NULL && right->at != NULL) {
144*59c8e88eSDag-Erling Smørgrav 		*cmp = buf_cmp(left->at, left->len, right->at, right->len,
145*59c8e88eSDag-Erling Smørgrav 		    ignore_whitespace);
146*59c8e88eSDag-Erling Smørgrav 		return 0;
147*59c8e88eSDag-Erling Smørgrav 	}
148*59c8e88eSDag-Erling Smørgrav 
149*59c8e88eSDag-Erling Smørgrav 	remain_left = left->len;
150*59c8e88eSDag-Erling Smørgrav 	remain_right = right->len;
151*59c8e88eSDag-Erling Smørgrav 	while (remain_left > 0 || remain_right > 0) {
152*59c8e88eSDag-Erling Smørgrav 		const size_t chunksz = 8192;
153*59c8e88eSDag-Erling Smørgrav 		unsigned char buf_left[chunksz], buf_right[chunksz];
154*59c8e88eSDag-Erling Smørgrav 		const uint8_t *p_left, *p_right;
155*59c8e88eSDag-Erling Smørgrav 		off_t n_left, n_right;
156*59c8e88eSDag-Erling Smørgrav 		ssize_t r;
157*59c8e88eSDag-Erling Smørgrav 
158*59c8e88eSDag-Erling Smørgrav 		if (!remain_right) {
159*59c8e88eSDag-Erling Smørgrav 			*cmp = 1;
160*59c8e88eSDag-Erling Smørgrav 			return 0;
161*59c8e88eSDag-Erling Smørgrav 		}
162*59c8e88eSDag-Erling Smørgrav 		if (!remain_left) {
163*59c8e88eSDag-Erling Smørgrav 			*cmp = -1;
164*59c8e88eSDag-Erling Smørgrav 			return 0;
165*59c8e88eSDag-Erling Smørgrav 		}
166*59c8e88eSDag-Erling Smørgrav 
167*59c8e88eSDag-Erling Smørgrav 		n_left = MIN(chunksz, remain_left);
168*59c8e88eSDag-Erling Smørgrav 		n_right = MIN(chunksz, remain_right);
169*59c8e88eSDag-Erling Smørgrav 
170*59c8e88eSDag-Erling Smørgrav 		if (left->at == NULL) {
171*59c8e88eSDag-Erling Smørgrav 			r = read_at(left->root->f,
172*59c8e88eSDag-Erling Smørgrav 				    left->pos + (left->len - remain_left),
173*59c8e88eSDag-Erling Smørgrav 				    buf_left, n_left);
174*59c8e88eSDag-Erling Smørgrav 			if (r) {
175*59c8e88eSDag-Erling Smørgrav 				*cmp = 0;
176*59c8e88eSDag-Erling Smørgrav 				return r;
177*59c8e88eSDag-Erling Smørgrav 			}
178*59c8e88eSDag-Erling Smørgrav 			p_left = buf_left;
179*59c8e88eSDag-Erling Smørgrav 		} else {
180*59c8e88eSDag-Erling Smørgrav 			p_left = left->at + (left->len - remain_left);
181*59c8e88eSDag-Erling Smørgrav 		}
182*59c8e88eSDag-Erling Smørgrav 
183*59c8e88eSDag-Erling Smørgrav 		if (right->at == NULL) {
184*59c8e88eSDag-Erling Smørgrav 			r = read_at(right->root->f,
185*59c8e88eSDag-Erling Smørgrav 				    right->pos + (right->len - remain_right),
186*59c8e88eSDag-Erling Smørgrav 				    buf_right, n_right);
187*59c8e88eSDag-Erling Smørgrav 			if (r) {
188*59c8e88eSDag-Erling Smørgrav 				*cmp = 0;
189*59c8e88eSDag-Erling Smørgrav 				return r;
190*59c8e88eSDag-Erling Smørgrav 			}
191*59c8e88eSDag-Erling Smørgrav 			p_right = buf_right;
192*59c8e88eSDag-Erling Smørgrav 		} else {
193*59c8e88eSDag-Erling Smørgrav 			p_right = right->at + (right->len - remain_right);
194*59c8e88eSDag-Erling Smørgrav 		}
195*59c8e88eSDag-Erling Smørgrav 
196*59c8e88eSDag-Erling Smørgrav 		r = buf_cmp(p_left, n_left, p_right, n_right,
197*59c8e88eSDag-Erling Smørgrav 		    ignore_whitespace);
198*59c8e88eSDag-Erling Smørgrav 		if (r) {
199*59c8e88eSDag-Erling Smørgrav 			*cmp = r;
200*59c8e88eSDag-Erling Smørgrav 			return 0;
201*59c8e88eSDag-Erling Smørgrav 		}
202*59c8e88eSDag-Erling Smørgrav 
203*59c8e88eSDag-Erling Smørgrav 		remain_left -= n_left;
204*59c8e88eSDag-Erling Smørgrav 		remain_right -= n_right;
205*59c8e88eSDag-Erling Smørgrav 	}
206*59c8e88eSDag-Erling Smørgrav 
207*59c8e88eSDag-Erling Smørgrav 	*cmp = 0;
208*59c8e88eSDag-Erling Smørgrav 	return 0;
209*59c8e88eSDag-Erling Smørgrav }
210*59c8e88eSDag-Erling Smørgrav 
211*59c8e88eSDag-Erling Smørgrav int
212*59c8e88eSDag-Erling Smørgrav diff_atom_same(bool *same,
213*59c8e88eSDag-Erling Smørgrav 	       const struct diff_atom *left,
214*59c8e88eSDag-Erling Smørgrav 	       const struct diff_atom *right)
215*59c8e88eSDag-Erling Smørgrav {
216*59c8e88eSDag-Erling Smørgrav 	int cmp;
217*59c8e88eSDag-Erling Smørgrav 	int r;
218*59c8e88eSDag-Erling Smørgrav 	if (left->hash != right->hash) {
219*59c8e88eSDag-Erling Smørgrav 		*same = false;
220*59c8e88eSDag-Erling Smørgrav 		return 0;
221*59c8e88eSDag-Erling Smørgrav 	}
222*59c8e88eSDag-Erling Smørgrav 	r = diff_atom_cmp(&cmp, left, right);
223*59c8e88eSDag-Erling Smørgrav 	if (r) {
224*59c8e88eSDag-Erling Smørgrav 		*same = true;
225*59c8e88eSDag-Erling Smørgrav 		return r;
226*59c8e88eSDag-Erling Smørgrav 	}
227*59c8e88eSDag-Erling Smørgrav 	*same = (cmp == 0);
228*59c8e88eSDag-Erling Smørgrav 	return 0;
229*59c8e88eSDag-Erling Smørgrav }
230*59c8e88eSDag-Erling Smørgrav 
231*59c8e88eSDag-Erling Smørgrav static struct diff_chunk *
232*59c8e88eSDag-Erling Smørgrav diff_state_add_solved_chunk(struct diff_state *state,
233*59c8e88eSDag-Erling Smørgrav 			    const struct diff_chunk *chunk)
234*59c8e88eSDag-Erling Smørgrav {
235*59c8e88eSDag-Erling Smørgrav 	diff_chunk_arraylist_t *result;
236*59c8e88eSDag-Erling Smørgrav 	struct diff_chunk *new_chunk;
237*59c8e88eSDag-Erling Smørgrav 	enum diff_chunk_type last_t;
238*59c8e88eSDag-Erling Smørgrav 	enum diff_chunk_type new_t;
239*59c8e88eSDag-Erling Smørgrav 	struct diff_chunk *last;
240*59c8e88eSDag-Erling Smørgrav 
241*59c8e88eSDag-Erling Smørgrav 	/* Append to solved chunks; make sure that adjacent chunks of same type are combined, and that a minus chunk
242*59c8e88eSDag-Erling Smørgrav 	 * never directly follows a plus chunk. */
243*59c8e88eSDag-Erling Smørgrav 	result = &state->result->chunks;
244*59c8e88eSDag-Erling Smørgrav 
245*59c8e88eSDag-Erling Smørgrav 	last_t = result->len ? diff_chunk_type(&result->head[result->len - 1])
246*59c8e88eSDag-Erling Smørgrav 		: CHUNK_EMPTY;
247*59c8e88eSDag-Erling Smørgrav 	new_t = diff_chunk_type(chunk);
248*59c8e88eSDag-Erling Smørgrav 
249*59c8e88eSDag-Erling Smørgrav 	debug("ADD %s chunk #%u:\n", chunk->solved ? "solved" : "UNSOLVED",
250*59c8e88eSDag-Erling Smørgrav 	      result->len);
251*59c8e88eSDag-Erling Smørgrav 	debug("L\n");
252*59c8e88eSDag-Erling Smørgrav 	debug_dump_atoms(&state->left, chunk->left_start, chunk->left_count);
253*59c8e88eSDag-Erling Smørgrav 	debug("R\n");
254*59c8e88eSDag-Erling Smørgrav 	debug_dump_atoms(&state->right, chunk->right_start, chunk->right_count);
255*59c8e88eSDag-Erling Smørgrav 
256*59c8e88eSDag-Erling Smørgrav 	if (result->len) {
257*59c8e88eSDag-Erling Smørgrav 		last = &result->head[result->len - 1];
258*59c8e88eSDag-Erling Smørgrav 		assert(chunk->left_start
259*59c8e88eSDag-Erling Smørgrav 		       == last->left_start + last->left_count);
260*59c8e88eSDag-Erling Smørgrav 		assert(chunk->right_start
261*59c8e88eSDag-Erling Smørgrav 		       == last->right_start + last->right_count);
262*59c8e88eSDag-Erling Smørgrav 	}
263*59c8e88eSDag-Erling Smørgrav 
264*59c8e88eSDag-Erling Smørgrav 	if (new_t == last_t) {
265*59c8e88eSDag-Erling Smørgrav 		new_chunk = &result->head[result->len - 1];
266*59c8e88eSDag-Erling Smørgrav 		new_chunk->left_count += chunk->left_count;
267*59c8e88eSDag-Erling Smørgrav 		new_chunk->right_count += chunk->right_count;
268*59c8e88eSDag-Erling Smørgrav 		debug("  - added chunk touches previous one of same type, joined:\n");
269*59c8e88eSDag-Erling Smørgrav 		debug("L\n");
270*59c8e88eSDag-Erling Smørgrav 		debug_dump_atoms(&state->left, new_chunk->left_start, new_chunk->left_count);
271*59c8e88eSDag-Erling Smørgrav 		debug("R\n");
272*59c8e88eSDag-Erling Smørgrav 		debug_dump_atoms(&state->right, new_chunk->right_start, new_chunk->right_count);
273*59c8e88eSDag-Erling Smørgrav 	} else if (last_t == CHUNK_PLUS && new_t == CHUNK_MINUS) {
274*59c8e88eSDag-Erling Smørgrav 		enum diff_chunk_type prev_last_t =
275*59c8e88eSDag-Erling Smørgrav 			result->len > 1 ?
276*59c8e88eSDag-Erling Smørgrav 				diff_chunk_type(&result->head[result->len - 2])
277*59c8e88eSDag-Erling Smørgrav 				: CHUNK_EMPTY;
278*59c8e88eSDag-Erling Smørgrav 		/* If a minus-chunk follows a plus-chunk, place it above the plus-chunk->
279*59c8e88eSDag-Erling Smørgrav 		 * Is the one before that also a minus? combine. */
280*59c8e88eSDag-Erling Smørgrav 		if (prev_last_t == CHUNK_MINUS) {
281*59c8e88eSDag-Erling Smørgrav 			new_chunk = &result->head[result->len - 2];
282*59c8e88eSDag-Erling Smørgrav 			new_chunk->left_count += chunk->left_count;
283*59c8e88eSDag-Erling Smørgrav 			new_chunk->right_count += chunk->right_count;
284*59c8e88eSDag-Erling Smørgrav 
285*59c8e88eSDag-Erling Smørgrav 			debug("  - added minus-chunk follows plus-chunk,"
286*59c8e88eSDag-Erling Smørgrav 			      " put before that plus-chunk and joined"
287*59c8e88eSDag-Erling Smørgrav 			      " with preceding minus-chunk:\n");
288*59c8e88eSDag-Erling Smørgrav 			debug("L\n");
289*59c8e88eSDag-Erling Smørgrav 			debug_dump_atoms(&state->left, new_chunk->left_start, new_chunk->left_count);
290*59c8e88eSDag-Erling Smørgrav 			debug("R\n");
291*59c8e88eSDag-Erling Smørgrav 			debug_dump_atoms(&state->right, new_chunk->right_start, new_chunk->right_count);
292*59c8e88eSDag-Erling Smørgrav 		} else {
293*59c8e88eSDag-Erling Smørgrav 			ARRAYLIST_INSERT(new_chunk, *result, result->len - 1);
294*59c8e88eSDag-Erling Smørgrav 			if (!new_chunk)
295*59c8e88eSDag-Erling Smørgrav 				return NULL;
296*59c8e88eSDag-Erling Smørgrav 			*new_chunk = *chunk;
297*59c8e88eSDag-Erling Smørgrav 
298*59c8e88eSDag-Erling Smørgrav 			/* The new minus chunk indicates to which position on
299*59c8e88eSDag-Erling Smørgrav 			 * the right it corresponds, even though it doesn't add
300*59c8e88eSDag-Erling Smørgrav 			 * any lines on the right. By moving above a plus chunk,
301*59c8e88eSDag-Erling Smørgrav 			 * that position on the right has shifted. */
302*59c8e88eSDag-Erling Smørgrav 			last = &result->head[result->len - 1];
303*59c8e88eSDag-Erling Smørgrav 			new_chunk->right_start = last->right_start;
304*59c8e88eSDag-Erling Smørgrav 
305*59c8e88eSDag-Erling Smørgrav 			debug("  - added minus-chunk follows plus-chunk,"
306*59c8e88eSDag-Erling Smørgrav 			      " put before that plus-chunk\n");
307*59c8e88eSDag-Erling Smørgrav 		}
308*59c8e88eSDag-Erling Smørgrav 
309*59c8e88eSDag-Erling Smørgrav 		/* That last_t == CHUNK_PLUS indicates to which position on the
310*59c8e88eSDag-Erling Smørgrav 		 * left it corresponds, even though it doesn't add any lines on
311*59c8e88eSDag-Erling Smørgrav 		 * the left. By inserting/extending the prev_last_t ==
312*59c8e88eSDag-Erling Smørgrav 		 * CHUNK_MINUS, that position on the left has shifted. */
313*59c8e88eSDag-Erling Smørgrav 		last = &result->head[result->len - 1];
314*59c8e88eSDag-Erling Smørgrav 		last->left_start = new_chunk->left_start
315*59c8e88eSDag-Erling Smørgrav 			+ new_chunk->left_count;
316*59c8e88eSDag-Erling Smørgrav 
317*59c8e88eSDag-Erling Smørgrav 	} else {
318*59c8e88eSDag-Erling Smørgrav 		ARRAYLIST_ADD(new_chunk, *result);
319*59c8e88eSDag-Erling Smørgrav 		if (!new_chunk)
320*59c8e88eSDag-Erling Smørgrav 			return NULL;
321*59c8e88eSDag-Erling Smørgrav 		*new_chunk = *chunk;
322*59c8e88eSDag-Erling Smørgrav 	}
323*59c8e88eSDag-Erling Smørgrav 	return new_chunk;
324*59c8e88eSDag-Erling Smørgrav }
325*59c8e88eSDag-Erling Smørgrav 
326*59c8e88eSDag-Erling Smørgrav /* Even if a left or right side is empty, diff output may need to know the
327*59c8e88eSDag-Erling Smørgrav  * position in that file.
328*59c8e88eSDag-Erling Smørgrav  * So left_start or right_start must never be NULL -- pass left_count or
329*59c8e88eSDag-Erling Smørgrav  * right_count as zero to indicate staying at that position without consuming
330*59c8e88eSDag-Erling Smørgrav  * any lines. */
331*59c8e88eSDag-Erling Smørgrav struct diff_chunk *
332*59c8e88eSDag-Erling Smørgrav diff_state_add_chunk(struct diff_state *state, bool solved,
333*59c8e88eSDag-Erling Smørgrav 		     struct diff_atom *left_start, unsigned int left_count,
334*59c8e88eSDag-Erling Smørgrav 		     struct diff_atom *right_start, unsigned int right_count)
335*59c8e88eSDag-Erling Smørgrav {
336*59c8e88eSDag-Erling Smørgrav 	struct diff_chunk *new_chunk;
337*59c8e88eSDag-Erling Smørgrav 	struct diff_chunk chunk = {
338*59c8e88eSDag-Erling Smørgrav 		.solved = solved,
339*59c8e88eSDag-Erling Smørgrav 		.left_start = left_start,
340*59c8e88eSDag-Erling Smørgrav 		.left_count = left_count,
341*59c8e88eSDag-Erling Smørgrav 		.right_start = right_start,
342*59c8e88eSDag-Erling Smørgrav 		.right_count = right_count,
343*59c8e88eSDag-Erling Smørgrav 	};
344*59c8e88eSDag-Erling Smørgrav 
345*59c8e88eSDag-Erling Smørgrav 	/* An unsolved chunk means store as intermediate result for later
346*59c8e88eSDag-Erling Smørgrav 	 * re-iteration.
347*59c8e88eSDag-Erling Smørgrav 	 * If there already are intermediate results, that means even a
348*59c8e88eSDag-Erling Smørgrav 	 * following solved chunk needs to go to intermediate results, so that
349*59c8e88eSDag-Erling Smørgrav 	 * it is later put in the final correct position in solved chunks.
350*59c8e88eSDag-Erling Smørgrav 	 */
351*59c8e88eSDag-Erling Smørgrav 	if (!solved || state->temp_result.len) {
352*59c8e88eSDag-Erling Smørgrav 		/* Append to temp_result */
353*59c8e88eSDag-Erling Smørgrav 		debug("ADD %s chunk to temp result:\n",
354*59c8e88eSDag-Erling Smørgrav 		      chunk.solved ? "solved" : "UNSOLVED");
355*59c8e88eSDag-Erling Smørgrav 		debug("L\n");
356*59c8e88eSDag-Erling Smørgrav 		debug_dump_atoms(&state->left, left_start, left_count);
357*59c8e88eSDag-Erling Smørgrav 		debug("R\n");
358*59c8e88eSDag-Erling Smørgrav 		debug_dump_atoms(&state->right, right_start, right_count);
359*59c8e88eSDag-Erling Smørgrav 		ARRAYLIST_ADD(new_chunk, state->temp_result);
360*59c8e88eSDag-Erling Smørgrav 		if (!new_chunk)
361*59c8e88eSDag-Erling Smørgrav 			return NULL;
362*59c8e88eSDag-Erling Smørgrav 		*new_chunk = chunk;
363*59c8e88eSDag-Erling Smørgrav 		return new_chunk;
364*59c8e88eSDag-Erling Smørgrav 	}
365*59c8e88eSDag-Erling Smørgrav 
366*59c8e88eSDag-Erling Smørgrav 	return diff_state_add_solved_chunk(state, &chunk);
367*59c8e88eSDag-Erling Smørgrav }
368*59c8e88eSDag-Erling Smørgrav 
369*59c8e88eSDag-Erling Smørgrav static void
370*59c8e88eSDag-Erling Smørgrav diff_data_init_root(struct diff_data *d, FILE *f, const uint8_t *data,
371*59c8e88eSDag-Erling Smørgrav     unsigned long long len, int diff_flags)
372*59c8e88eSDag-Erling Smørgrav {
373*59c8e88eSDag-Erling Smørgrav 	*d = (struct diff_data){
374*59c8e88eSDag-Erling Smørgrav 		.f = f,
375*59c8e88eSDag-Erling Smørgrav 		.pos = 0,
376*59c8e88eSDag-Erling Smørgrav 		.data = data,
377*59c8e88eSDag-Erling Smørgrav 		.len = len,
378*59c8e88eSDag-Erling Smørgrav 		.root = d,
379*59c8e88eSDag-Erling Smørgrav 		.diff_flags = diff_flags,
380*59c8e88eSDag-Erling Smørgrav 	};
381*59c8e88eSDag-Erling Smørgrav }
382*59c8e88eSDag-Erling Smørgrav 
383*59c8e88eSDag-Erling Smørgrav void
384*59c8e88eSDag-Erling Smørgrav diff_data_init_subsection(struct diff_data *d, struct diff_data *parent,
385*59c8e88eSDag-Erling Smørgrav 			  struct diff_atom *from_atom, unsigned int atoms_count)
386*59c8e88eSDag-Erling Smørgrav {
387*59c8e88eSDag-Erling Smørgrav 	struct diff_atom *last_atom;
388*59c8e88eSDag-Erling Smørgrav 
389*59c8e88eSDag-Erling Smørgrav 	debug("diff_data %p  parent %p  from_atom %p  atoms_count %u\n",
390*59c8e88eSDag-Erling Smørgrav 	      d, parent, from_atom, atoms_count);
391*59c8e88eSDag-Erling Smørgrav 	debug("  from_atom ");
392*59c8e88eSDag-Erling Smørgrav 	debug_dump_atom(parent, NULL, from_atom);
393*59c8e88eSDag-Erling Smørgrav 
394*59c8e88eSDag-Erling Smørgrav 	if (atoms_count == 0) {
395*59c8e88eSDag-Erling Smørgrav 		*d = (struct diff_data){
396*59c8e88eSDag-Erling Smørgrav 			.f = NULL,
397*59c8e88eSDag-Erling Smørgrav 			.pos = 0,
398*59c8e88eSDag-Erling Smørgrav 			.data = NULL,
399*59c8e88eSDag-Erling Smørgrav 			.len = 0,
400*59c8e88eSDag-Erling Smørgrav 			.root = parent->root,
401*59c8e88eSDag-Erling Smørgrav 			.atoms.head = NULL,
402*59c8e88eSDag-Erling Smørgrav 			.atoms.len = atoms_count,
403*59c8e88eSDag-Erling Smørgrav 		};
404*59c8e88eSDag-Erling Smørgrav 
405*59c8e88eSDag-Erling Smørgrav 		return;
406*59c8e88eSDag-Erling Smørgrav 	}
407*59c8e88eSDag-Erling Smørgrav 
408*59c8e88eSDag-Erling Smørgrav 	last_atom = from_atom + atoms_count - 1;
409*59c8e88eSDag-Erling Smørgrav 	*d = (struct diff_data){
410*59c8e88eSDag-Erling Smørgrav 		.f = NULL,
411*59c8e88eSDag-Erling Smørgrav 		.pos = from_atom->pos,
412*59c8e88eSDag-Erling Smørgrav 		.data = from_atom->at,
413*59c8e88eSDag-Erling Smørgrav 		.len = (last_atom->pos + last_atom->len) - from_atom->pos,
414*59c8e88eSDag-Erling Smørgrav 		.root = parent->root,
415*59c8e88eSDag-Erling Smørgrav 		.atoms.head = from_atom,
416*59c8e88eSDag-Erling Smørgrav 		.atoms.len = atoms_count,
417*59c8e88eSDag-Erling Smørgrav 	};
418*59c8e88eSDag-Erling Smørgrav 
419*59c8e88eSDag-Erling Smørgrav 	debug("subsection:\n");
420*59c8e88eSDag-Erling Smørgrav 	debug_dump(d);
421*59c8e88eSDag-Erling Smørgrav }
422*59c8e88eSDag-Erling Smørgrav 
423*59c8e88eSDag-Erling Smørgrav void
424*59c8e88eSDag-Erling Smørgrav diff_data_free(struct diff_data *diff_data)
425*59c8e88eSDag-Erling Smørgrav {
426*59c8e88eSDag-Erling Smørgrav 	if (!diff_data)
427*59c8e88eSDag-Erling Smørgrav 		return;
428*59c8e88eSDag-Erling Smørgrav 	if (diff_data->atoms.allocated)
429*59c8e88eSDag-Erling Smørgrav 		ARRAYLIST_FREE(diff_data->atoms);
430*59c8e88eSDag-Erling Smørgrav }
431*59c8e88eSDag-Erling Smørgrav 
432*59c8e88eSDag-Erling Smørgrav int
433*59c8e88eSDag-Erling Smørgrav diff_algo_none(const struct diff_algo_config *algo_config,
434*59c8e88eSDag-Erling Smørgrav 	       struct diff_state *state)
435*59c8e88eSDag-Erling Smørgrav {
436*59c8e88eSDag-Erling Smørgrav 	debug("\n** %s\n", __func__);
437*59c8e88eSDag-Erling Smørgrav 	debug("left:\n");
438*59c8e88eSDag-Erling Smørgrav 	debug_dump(&state->left);
439*59c8e88eSDag-Erling Smørgrav 	debug("right:\n");
440*59c8e88eSDag-Erling Smørgrav 	debug_dump(&state->right);
441*59c8e88eSDag-Erling Smørgrav 	debug_dump_myers_graph(&state->left, &state->right, NULL, NULL, 0, NULL,
442*59c8e88eSDag-Erling Smørgrav 			       0);
443*59c8e88eSDag-Erling Smørgrav 
444*59c8e88eSDag-Erling Smørgrav 	/* Add a chunk of equal lines, if any */
445*59c8e88eSDag-Erling Smørgrav 	struct diff_atom *l = state->left.atoms.head;
446*59c8e88eSDag-Erling Smørgrav 	unsigned int l_len = state->left.atoms.len;
447*59c8e88eSDag-Erling Smørgrav 	struct diff_atom *r = state->right.atoms.head;
448*59c8e88eSDag-Erling Smørgrav 	unsigned int r_len = state->right.atoms.len;
449*59c8e88eSDag-Erling Smørgrav 	unsigned int equal_atoms_start = 0;
450*59c8e88eSDag-Erling Smørgrav 	unsigned int equal_atoms_end = 0;
451*59c8e88eSDag-Erling Smørgrav 	unsigned int l_idx = 0;
452*59c8e88eSDag-Erling Smørgrav 	unsigned int r_idx = 0;
453*59c8e88eSDag-Erling Smørgrav 
454*59c8e88eSDag-Erling Smørgrav 	while (equal_atoms_start < l_len
455*59c8e88eSDag-Erling Smørgrav 	       && equal_atoms_start < r_len) {
456*59c8e88eSDag-Erling Smørgrav 		int err;
457*59c8e88eSDag-Erling Smørgrav 		bool same;
458*59c8e88eSDag-Erling Smørgrav 		err = diff_atom_same(&same, &l[equal_atoms_start],
459*59c8e88eSDag-Erling Smørgrav 				     &r[equal_atoms_start]);
460*59c8e88eSDag-Erling Smørgrav 		if (err)
461*59c8e88eSDag-Erling Smørgrav 			return err;
462*59c8e88eSDag-Erling Smørgrav 		if (!same)
463*59c8e88eSDag-Erling Smørgrav 			break;
464*59c8e88eSDag-Erling Smørgrav 		equal_atoms_start++;
465*59c8e88eSDag-Erling Smørgrav 	}
466*59c8e88eSDag-Erling Smørgrav 	while (equal_atoms_end < (l_len - equal_atoms_start)
467*59c8e88eSDag-Erling Smørgrav 	       && equal_atoms_end < (r_len - equal_atoms_start)) {
468*59c8e88eSDag-Erling Smørgrav 		int err;
469*59c8e88eSDag-Erling Smørgrav 		bool same;
470*59c8e88eSDag-Erling Smørgrav 		err = diff_atom_same(&same, &l[l_len - 1 - equal_atoms_end],
471*59c8e88eSDag-Erling Smørgrav 				   &r[r_len - 1 - equal_atoms_end]);
472*59c8e88eSDag-Erling Smørgrav 		if (err)
473*59c8e88eSDag-Erling Smørgrav 			return err;
474*59c8e88eSDag-Erling Smørgrav 		if (!same)
475*59c8e88eSDag-Erling Smørgrav 			break;
476*59c8e88eSDag-Erling Smørgrav 		equal_atoms_end++;
477*59c8e88eSDag-Erling Smørgrav 	}
478*59c8e88eSDag-Erling Smørgrav 
479*59c8e88eSDag-Erling Smørgrav 	/* Add a chunk of equal lines at the start */
480*59c8e88eSDag-Erling Smørgrav 	if (equal_atoms_start) {
481*59c8e88eSDag-Erling Smørgrav 		if (!diff_state_add_chunk(state, true,
482*59c8e88eSDag-Erling Smørgrav 					  l, equal_atoms_start,
483*59c8e88eSDag-Erling Smørgrav 					  r, equal_atoms_start))
484*59c8e88eSDag-Erling Smørgrav 			return ENOMEM;
485*59c8e88eSDag-Erling Smørgrav 		l_idx += equal_atoms_start;
486*59c8e88eSDag-Erling Smørgrav 		r_idx += equal_atoms_start;
487*59c8e88eSDag-Erling Smørgrav 	}
488*59c8e88eSDag-Erling Smørgrav 
489*59c8e88eSDag-Erling Smørgrav 	/* Add a "minus" chunk with all lines from the left. */
490*59c8e88eSDag-Erling Smørgrav 	if (equal_atoms_start + equal_atoms_end < l_len) {
491*59c8e88eSDag-Erling Smørgrav 		unsigned int add_len = l_len - equal_atoms_start - equal_atoms_end;
492*59c8e88eSDag-Erling Smørgrav 		if (!diff_state_add_chunk(state, true,
493*59c8e88eSDag-Erling Smørgrav 					  &l[l_idx], add_len,
494*59c8e88eSDag-Erling Smørgrav 					  &r[r_idx], 0))
495*59c8e88eSDag-Erling Smørgrav 			return ENOMEM;
496*59c8e88eSDag-Erling Smørgrav 		l_idx += add_len;
497*59c8e88eSDag-Erling Smørgrav 	}
498*59c8e88eSDag-Erling Smørgrav 
499*59c8e88eSDag-Erling Smørgrav 	/* Add a "plus" chunk with all lines from the right. */
500*59c8e88eSDag-Erling Smørgrav 	if (equal_atoms_start + equal_atoms_end < r_len) {
501*59c8e88eSDag-Erling Smørgrav 		unsigned int add_len = r_len - equal_atoms_start - equal_atoms_end;
502*59c8e88eSDag-Erling Smørgrav 		if (!diff_state_add_chunk(state, true,
503*59c8e88eSDag-Erling Smørgrav 					  &l[l_idx], 0,
504*59c8e88eSDag-Erling Smørgrav 					  &r[r_idx], add_len))
505*59c8e88eSDag-Erling Smørgrav 			return ENOMEM;
506*59c8e88eSDag-Erling Smørgrav 		r_idx += add_len;
507*59c8e88eSDag-Erling Smørgrav 	}
508*59c8e88eSDag-Erling Smørgrav 
509*59c8e88eSDag-Erling Smørgrav 	/* Add a chunk of equal lines at the end */
510*59c8e88eSDag-Erling Smørgrav 	if (equal_atoms_end) {
511*59c8e88eSDag-Erling Smørgrav 		if (!diff_state_add_chunk(state, true,
512*59c8e88eSDag-Erling Smørgrav 					  &l[l_idx], equal_atoms_end,
513*59c8e88eSDag-Erling Smørgrav 					  &r[r_idx], equal_atoms_end))
514*59c8e88eSDag-Erling Smørgrav 			return ENOMEM;
515*59c8e88eSDag-Erling Smørgrav 	}
516*59c8e88eSDag-Erling Smørgrav 
517*59c8e88eSDag-Erling Smørgrav 	return DIFF_RC_OK;
518*59c8e88eSDag-Erling Smørgrav }
519*59c8e88eSDag-Erling Smørgrav 
520*59c8e88eSDag-Erling Smørgrav static int
521*59c8e88eSDag-Erling Smørgrav diff_run_algo(const struct diff_algo_config *algo_config,
522*59c8e88eSDag-Erling Smørgrav 	      struct diff_state *state)
523*59c8e88eSDag-Erling Smørgrav {
524*59c8e88eSDag-Erling Smørgrav 	int rc;
525*59c8e88eSDag-Erling Smørgrav 
526*59c8e88eSDag-Erling Smørgrav 	if (!algo_config || !algo_config->impl
527*59c8e88eSDag-Erling Smørgrav 	    || !state->recursion_depth_left
528*59c8e88eSDag-Erling Smørgrav 	    || !state->left.atoms.len || !state->right.atoms.len) {
529*59c8e88eSDag-Erling Smørgrav 		debug("Fall back to diff_algo_none():%s%s%s\n",
530*59c8e88eSDag-Erling Smørgrav 		      (!algo_config || !algo_config->impl) ? " no-cfg" : "",
531*59c8e88eSDag-Erling Smørgrav 		      (!state->recursion_depth_left) ? " max-depth" : "",
532*59c8e88eSDag-Erling Smørgrav 		      (!state->left.atoms.len || !state->right.atoms.len)?
533*59c8e88eSDag-Erling Smørgrav 			      " trivial" : "");
534*59c8e88eSDag-Erling Smørgrav 		return diff_algo_none(algo_config, state);
535*59c8e88eSDag-Erling Smørgrav 	}
536*59c8e88eSDag-Erling Smørgrav 
537*59c8e88eSDag-Erling Smørgrav 	ARRAYLIST_FREE(state->temp_result);
538*59c8e88eSDag-Erling Smørgrav 	ARRAYLIST_INIT(state->temp_result, DIFF_RESULT_ALLOC_BLOCKSIZE);
539*59c8e88eSDag-Erling Smørgrav 	rc = algo_config->impl(algo_config, state);
540*59c8e88eSDag-Erling Smørgrav 	switch (rc) {
541*59c8e88eSDag-Erling Smørgrav 	case DIFF_RC_USE_DIFF_ALGO_FALLBACK:
542*59c8e88eSDag-Erling Smørgrav 		debug("Got DIFF_RC_USE_DIFF_ALGO_FALLBACK (%p)\n",
543*59c8e88eSDag-Erling Smørgrav 		      algo_config->fallback_algo);
544*59c8e88eSDag-Erling Smørgrav 		rc = diff_run_algo(algo_config->fallback_algo, state);
545*59c8e88eSDag-Erling Smørgrav 		goto return_rc;
546*59c8e88eSDag-Erling Smørgrav 
547*59c8e88eSDag-Erling Smørgrav 	case DIFF_RC_OK:
548*59c8e88eSDag-Erling Smørgrav 		/* continue below */
549*59c8e88eSDag-Erling Smørgrav 		break;
550*59c8e88eSDag-Erling Smørgrav 
551*59c8e88eSDag-Erling Smørgrav 	default:
552*59c8e88eSDag-Erling Smørgrav 		/* some error happened */
553*59c8e88eSDag-Erling Smørgrav 		goto return_rc;
554*59c8e88eSDag-Erling Smørgrav 	}
555*59c8e88eSDag-Erling Smørgrav 
556*59c8e88eSDag-Erling Smørgrav 	/* Pick up any diff chunks that are still unsolved and feed to
557*59c8e88eSDag-Erling Smørgrav 	 * inner_algo.  inner_algo will solve unsolved chunks and append to
558*59c8e88eSDag-Erling Smørgrav 	 * result, and subsequent solved chunks on this level are then appended
559*59c8e88eSDag-Erling Smørgrav 	 * to result afterwards. */
560*59c8e88eSDag-Erling Smørgrav 	int i;
561*59c8e88eSDag-Erling Smørgrav 	for (i = 0; i < state->temp_result.len; i++) {
562*59c8e88eSDag-Erling Smørgrav 		struct diff_chunk *c = &state->temp_result.head[i];
563*59c8e88eSDag-Erling Smørgrav 		if (c->solved) {
564*59c8e88eSDag-Erling Smørgrav 			diff_state_add_solved_chunk(state, c);
565*59c8e88eSDag-Erling Smørgrav 			continue;
566*59c8e88eSDag-Erling Smørgrav 		}
567*59c8e88eSDag-Erling Smørgrav 
568*59c8e88eSDag-Erling Smørgrav 		/* c is an unsolved chunk, feed to inner_algo */
569*59c8e88eSDag-Erling Smørgrav 		struct diff_state inner_state = {
570*59c8e88eSDag-Erling Smørgrav 			.result = state->result,
571*59c8e88eSDag-Erling Smørgrav 			.recursion_depth_left = state->recursion_depth_left - 1,
572*59c8e88eSDag-Erling Smørgrav 			.kd_buf = state->kd_buf,
573*59c8e88eSDag-Erling Smørgrav 			.kd_buf_size = state->kd_buf_size,
574*59c8e88eSDag-Erling Smørgrav 		};
575*59c8e88eSDag-Erling Smørgrav 		diff_data_init_subsection(&inner_state.left, &state->left,
576*59c8e88eSDag-Erling Smørgrav 					  c->left_start, c->left_count);
577*59c8e88eSDag-Erling Smørgrav 		diff_data_init_subsection(&inner_state.right, &state->right,
578*59c8e88eSDag-Erling Smørgrav 					  c->right_start, c->right_count);
579*59c8e88eSDag-Erling Smørgrav 
580*59c8e88eSDag-Erling Smørgrav 		rc = diff_run_algo(algo_config->inner_algo, &inner_state);
581*59c8e88eSDag-Erling Smørgrav 		state->kd_buf = inner_state.kd_buf;
582*59c8e88eSDag-Erling Smørgrav 		state->kd_buf_size = inner_state.kd_buf_size;
583*59c8e88eSDag-Erling Smørgrav 		if (rc != DIFF_RC_OK)
584*59c8e88eSDag-Erling Smørgrav 			goto return_rc;
585*59c8e88eSDag-Erling Smørgrav 	}
586*59c8e88eSDag-Erling Smørgrav 
587*59c8e88eSDag-Erling Smørgrav 	rc = DIFF_RC_OK;
588*59c8e88eSDag-Erling Smørgrav return_rc:
589*59c8e88eSDag-Erling Smørgrav 	ARRAYLIST_FREE(state->temp_result);
590*59c8e88eSDag-Erling Smørgrav 	return rc;
591*59c8e88eSDag-Erling Smørgrav }
592*59c8e88eSDag-Erling Smørgrav 
593*59c8e88eSDag-Erling Smørgrav int
594*59c8e88eSDag-Erling Smørgrav diff_atomize_file(struct diff_data *d,
595*59c8e88eSDag-Erling Smørgrav 		  const struct diff_config *config,
596*59c8e88eSDag-Erling Smørgrav 		  FILE *f, const uint8_t *data, off_t len, int diff_flags)
597*59c8e88eSDag-Erling Smørgrav {
598*59c8e88eSDag-Erling Smørgrav 	if (!config->atomize_func)
599*59c8e88eSDag-Erling Smørgrav 		return EINVAL;
600*59c8e88eSDag-Erling Smørgrav 
601*59c8e88eSDag-Erling Smørgrav 	diff_data_init_root(d, f, data, len, diff_flags);
602*59c8e88eSDag-Erling Smørgrav 
603*59c8e88eSDag-Erling Smørgrav 	return config->atomize_func(config->atomize_func_data, d);
604*59c8e88eSDag-Erling Smørgrav 
605*59c8e88eSDag-Erling Smørgrav }
606*59c8e88eSDag-Erling Smørgrav 
607*59c8e88eSDag-Erling Smørgrav struct diff_result *
608*59c8e88eSDag-Erling Smørgrav diff_main(const struct diff_config *config, struct diff_data *left,
609*59c8e88eSDag-Erling Smørgrav 	  struct diff_data *right)
610*59c8e88eSDag-Erling Smørgrav {
611*59c8e88eSDag-Erling Smørgrav 	struct diff_result *result = malloc(sizeof(struct diff_result));
612*59c8e88eSDag-Erling Smørgrav 	if (!result)
613*59c8e88eSDag-Erling Smørgrav 		return NULL;
614*59c8e88eSDag-Erling Smørgrav 
615*59c8e88eSDag-Erling Smørgrav 	*result = (struct diff_result){};
616*59c8e88eSDag-Erling Smørgrav 	result->left = left;
617*59c8e88eSDag-Erling Smørgrav 	result->right = right;
618*59c8e88eSDag-Erling Smørgrav 
619*59c8e88eSDag-Erling Smørgrav 	struct diff_state state = {
620*59c8e88eSDag-Erling Smørgrav 		.result = result,
621*59c8e88eSDag-Erling Smørgrav 		.recursion_depth_left = config->max_recursion_depth ?
622*59c8e88eSDag-Erling Smørgrav 		    config->max_recursion_depth : UINT_MAX,
623*59c8e88eSDag-Erling Smørgrav 		.kd_buf = NULL,
624*59c8e88eSDag-Erling Smørgrav 		.kd_buf_size = 0,
625*59c8e88eSDag-Erling Smørgrav 	};
626*59c8e88eSDag-Erling Smørgrav 	diff_data_init_subsection(&state.left, left,
627*59c8e88eSDag-Erling Smørgrav 				  left->atoms.head,
628*59c8e88eSDag-Erling Smørgrav 				  left->atoms.len);
629*59c8e88eSDag-Erling Smørgrav 	diff_data_init_subsection(&state.right, right,
630*59c8e88eSDag-Erling Smørgrav 				  right->atoms.head,
631*59c8e88eSDag-Erling Smørgrav 				  right->atoms.len);
632*59c8e88eSDag-Erling Smørgrav 
633*59c8e88eSDag-Erling Smørgrav 	result->rc = diff_run_algo(config->algo, &state);
634*59c8e88eSDag-Erling Smørgrav 	free(state.kd_buf);
635*59c8e88eSDag-Erling Smørgrav 
636*59c8e88eSDag-Erling Smørgrav 	return result;
637*59c8e88eSDag-Erling Smørgrav }
638*59c8e88eSDag-Erling Smørgrav 
639*59c8e88eSDag-Erling Smørgrav void
640*59c8e88eSDag-Erling Smørgrav diff_result_free(struct diff_result *result)
641*59c8e88eSDag-Erling Smørgrav {
642*59c8e88eSDag-Erling Smørgrav 	if (!result)
643*59c8e88eSDag-Erling Smørgrav 		return;
644*59c8e88eSDag-Erling Smørgrav 	ARRAYLIST_FREE(result->chunks);
645*59c8e88eSDag-Erling Smørgrav 	free(result);
646*59c8e88eSDag-Erling Smørgrav }
647*59c8e88eSDag-Erling Smørgrav 
648*59c8e88eSDag-Erling Smørgrav int
649*59c8e88eSDag-Erling Smørgrav diff_result_contains_printable_chunks(struct diff_result *result)
650*59c8e88eSDag-Erling Smørgrav {
651*59c8e88eSDag-Erling Smørgrav 	struct diff_chunk *c;
652*59c8e88eSDag-Erling Smørgrav 	enum diff_chunk_type t;
653*59c8e88eSDag-Erling Smørgrav 	int i;
654*59c8e88eSDag-Erling Smørgrav 
655*59c8e88eSDag-Erling Smørgrav 	for (i = 0; i < result->chunks.len; i++) {
656*59c8e88eSDag-Erling Smørgrav 		c = &result->chunks.head[i];
657*59c8e88eSDag-Erling Smørgrav 		t = diff_chunk_type(c);
658*59c8e88eSDag-Erling Smørgrav 		if (t == CHUNK_MINUS || t == CHUNK_PLUS)
659*59c8e88eSDag-Erling Smørgrav 			return 1;
660*59c8e88eSDag-Erling Smørgrav 	}
661*59c8e88eSDag-Erling Smørgrav 
662*59c8e88eSDag-Erling Smørgrav 	return 0;
663*59c8e88eSDag-Erling Smørgrav }
664