159c8e88eSDag-Erling Smørgrav /* Produce a unidiff output from a diff_result. */
259c8e88eSDag-Erling Smørgrav /*
359c8e88eSDag-Erling Smørgrav  * Copyright (c) 2020 Neels Hofmeyr <neels@hofmeyr.de>
459c8e88eSDag-Erling Smørgrav  *
559c8e88eSDag-Erling Smørgrav  * Permission to use, copy, modify, and distribute this software for any
659c8e88eSDag-Erling Smørgrav  * purpose with or without fee is hereby granted, provided that the above
759c8e88eSDag-Erling Smørgrav  * copyright notice and this permission notice appear in all copies.
859c8e88eSDag-Erling Smørgrav  *
959c8e88eSDag-Erling Smørgrav  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1059c8e88eSDag-Erling Smørgrav  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1159c8e88eSDag-Erling Smørgrav  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1259c8e88eSDag-Erling Smørgrav  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1359c8e88eSDag-Erling Smørgrav  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1459c8e88eSDag-Erling Smørgrav  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1559c8e88eSDag-Erling Smørgrav  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1659c8e88eSDag-Erling Smørgrav  */
1759c8e88eSDag-Erling Smørgrav 
1859c8e88eSDag-Erling Smørgrav #include <errno.h>
1959c8e88eSDag-Erling Smørgrav #include <stdbool.h>
2059c8e88eSDag-Erling Smørgrav #include <stdint.h>
2159c8e88eSDag-Erling Smørgrav #include <stdio.h>
2259c8e88eSDag-Erling Smørgrav #include <stdlib.h>
2359c8e88eSDag-Erling Smørgrav #include <string.h>
2459c8e88eSDag-Erling Smørgrav #include <assert.h>
2559c8e88eSDag-Erling Smørgrav 
2659c8e88eSDag-Erling Smørgrav #include <arraylist.h>
2759c8e88eSDag-Erling Smørgrav #include <diff_main.h>
2859c8e88eSDag-Erling Smørgrav #include <diff_output.h>
2959c8e88eSDag-Erling Smørgrav 
3059c8e88eSDag-Erling Smørgrav #include "diff_internal.h"
3159c8e88eSDag-Erling Smørgrav #include "diff_debug.h"
3259c8e88eSDag-Erling Smørgrav 
3359c8e88eSDag-Erling Smørgrav off_t
diff_chunk_get_left_start_pos(const struct diff_chunk * c)3459c8e88eSDag-Erling Smørgrav diff_chunk_get_left_start_pos(const struct diff_chunk *c)
3559c8e88eSDag-Erling Smørgrav {
3659c8e88eSDag-Erling Smørgrav 	return c->left_start->pos;
3759c8e88eSDag-Erling Smørgrav }
3859c8e88eSDag-Erling Smørgrav 
3959c8e88eSDag-Erling Smørgrav off_t
diff_chunk_get_right_start_pos(const struct diff_chunk * c)4059c8e88eSDag-Erling Smørgrav diff_chunk_get_right_start_pos(const struct diff_chunk *c)
4159c8e88eSDag-Erling Smørgrav {
4259c8e88eSDag-Erling Smørgrav 	return c->right_start->pos;
4359c8e88eSDag-Erling Smørgrav }
4459c8e88eSDag-Erling Smørgrav 
4559c8e88eSDag-Erling Smørgrav bool
diff_chunk_context_empty(const struct diff_chunk_context * cc)4659c8e88eSDag-Erling Smørgrav diff_chunk_context_empty(const struct diff_chunk_context *cc)
4759c8e88eSDag-Erling Smørgrav {
4859c8e88eSDag-Erling Smørgrav 	return diff_range_empty(&cc->chunk);
4959c8e88eSDag-Erling Smørgrav }
5059c8e88eSDag-Erling Smørgrav 
5159c8e88eSDag-Erling Smørgrav int
diff_chunk_get_left_start(const struct diff_chunk * c,const struct diff_result * r,int context_lines)5259c8e88eSDag-Erling Smørgrav diff_chunk_get_left_start(const struct diff_chunk *c,
5359c8e88eSDag-Erling Smørgrav     const struct diff_result *r, int context_lines)
5459c8e88eSDag-Erling Smørgrav {
5559c8e88eSDag-Erling Smørgrav 	int left_start = diff_atom_root_idx(r->left, c->left_start);
5659c8e88eSDag-Erling Smørgrav 	return MAX(0, left_start - context_lines);
5759c8e88eSDag-Erling Smørgrav }
5859c8e88eSDag-Erling Smørgrav 
5959c8e88eSDag-Erling Smørgrav int
diff_chunk_get_left_end(const struct diff_chunk * c,const struct diff_result * r,int context_lines)6059c8e88eSDag-Erling Smørgrav diff_chunk_get_left_end(const struct diff_chunk *c,
6159c8e88eSDag-Erling Smørgrav     const struct diff_result *r, int context_lines)
6259c8e88eSDag-Erling Smørgrav {
6359c8e88eSDag-Erling Smørgrav 	int left_start = diff_chunk_get_left_start(c, r, 0);
6459c8e88eSDag-Erling Smørgrav 	return MIN(r->left->atoms.len,
6559c8e88eSDag-Erling Smørgrav 	    left_start + c->left_count + context_lines);
6659c8e88eSDag-Erling Smørgrav }
6759c8e88eSDag-Erling Smørgrav 
6859c8e88eSDag-Erling Smørgrav int
diff_chunk_get_right_start(const struct diff_chunk * c,const struct diff_result * r,int context_lines)6959c8e88eSDag-Erling Smørgrav diff_chunk_get_right_start(const struct diff_chunk *c,
7059c8e88eSDag-Erling Smørgrav     const struct diff_result *r, int context_lines)
7159c8e88eSDag-Erling Smørgrav {
7259c8e88eSDag-Erling Smørgrav 	int right_start = diff_atom_root_idx(r->right, c->right_start);
7359c8e88eSDag-Erling Smørgrav 	return MAX(0, right_start - context_lines);
7459c8e88eSDag-Erling Smørgrav }
7559c8e88eSDag-Erling Smørgrav 
7659c8e88eSDag-Erling Smørgrav int
diff_chunk_get_right_end(const struct diff_chunk * c,const struct diff_result * r,int context_lines)7759c8e88eSDag-Erling Smørgrav diff_chunk_get_right_end(const struct diff_chunk *c,
7859c8e88eSDag-Erling Smørgrav     const struct diff_result *r, int context_lines)
7959c8e88eSDag-Erling Smørgrav {
8059c8e88eSDag-Erling Smørgrav 	int right_start = diff_chunk_get_right_start(c, r, 0);
8159c8e88eSDag-Erling Smørgrav 	return MIN(r->right->atoms.len,
8259c8e88eSDag-Erling Smørgrav 	    right_start + c->right_count + context_lines);
8359c8e88eSDag-Erling Smørgrav }
8459c8e88eSDag-Erling Smørgrav 
8559c8e88eSDag-Erling Smørgrav struct diff_chunk *
diff_chunk_get(const struct diff_result * r,int chunk_idx)8659c8e88eSDag-Erling Smørgrav diff_chunk_get(const struct diff_result *r, int chunk_idx)
8759c8e88eSDag-Erling Smørgrav {
8859c8e88eSDag-Erling Smørgrav 	return &r->chunks.head[chunk_idx];
8959c8e88eSDag-Erling Smørgrav }
9059c8e88eSDag-Erling Smørgrav 
9159c8e88eSDag-Erling Smørgrav int
diff_chunk_get_left_count(struct diff_chunk * c)9259c8e88eSDag-Erling Smørgrav diff_chunk_get_left_count(struct diff_chunk *c)
9359c8e88eSDag-Erling Smørgrav {
9459c8e88eSDag-Erling Smørgrav 	return c->left_count;
9559c8e88eSDag-Erling Smørgrav }
9659c8e88eSDag-Erling Smørgrav 
9759c8e88eSDag-Erling Smørgrav int
diff_chunk_get_right_count(struct diff_chunk * c)9859c8e88eSDag-Erling Smørgrav diff_chunk_get_right_count(struct diff_chunk *c)
9959c8e88eSDag-Erling Smørgrav {
10059c8e88eSDag-Erling Smørgrav 	return c->right_count;
10159c8e88eSDag-Erling Smørgrav }
10259c8e88eSDag-Erling Smørgrav 
10359c8e88eSDag-Erling Smørgrav void
diff_chunk_context_get(struct diff_chunk_context * cc,const struct diff_result * r,int chunk_idx,int context_lines)10459c8e88eSDag-Erling Smørgrav diff_chunk_context_get(struct diff_chunk_context *cc, const struct diff_result *r,
10559c8e88eSDag-Erling Smørgrav 		  int chunk_idx, int context_lines)
10659c8e88eSDag-Erling Smørgrav {
10759c8e88eSDag-Erling Smørgrav 	const struct diff_chunk *c = &r->chunks.head[chunk_idx];
10859c8e88eSDag-Erling Smørgrav 	int left_start = diff_chunk_get_left_start(c, r, context_lines);
10959c8e88eSDag-Erling Smørgrav 	int left_end = diff_chunk_get_left_end(c, r, context_lines);
11059c8e88eSDag-Erling Smørgrav 	int right_start = diff_chunk_get_right_start(c, r, context_lines);
11159c8e88eSDag-Erling Smørgrav 	int right_end = diff_chunk_get_right_end(c, r,  context_lines);
11259c8e88eSDag-Erling Smørgrav 
11359c8e88eSDag-Erling Smørgrav 	*cc = (struct diff_chunk_context){
11459c8e88eSDag-Erling Smørgrav 		.chunk = {
11559c8e88eSDag-Erling Smørgrav 			.start = chunk_idx,
11659c8e88eSDag-Erling Smørgrav 			.end = chunk_idx + 1,
11759c8e88eSDag-Erling Smørgrav 		},
11859c8e88eSDag-Erling Smørgrav 		.left = {
11959c8e88eSDag-Erling Smørgrav 			.start = left_start,
12059c8e88eSDag-Erling Smørgrav 			.end = left_end,
12159c8e88eSDag-Erling Smørgrav 		},
12259c8e88eSDag-Erling Smørgrav 		.right = {
12359c8e88eSDag-Erling Smørgrav 			.start = right_start,
12459c8e88eSDag-Erling Smørgrav 			.end = right_end,
12559c8e88eSDag-Erling Smørgrav 		},
12659c8e88eSDag-Erling Smørgrav 	};
12759c8e88eSDag-Erling Smørgrav }
12859c8e88eSDag-Erling Smørgrav 
12959c8e88eSDag-Erling Smørgrav bool
diff_chunk_contexts_touch(const struct diff_chunk_context * cc,const struct diff_chunk_context * other)13059c8e88eSDag-Erling Smørgrav diff_chunk_contexts_touch(const struct diff_chunk_context *cc,
13159c8e88eSDag-Erling Smørgrav 			  const struct diff_chunk_context *other)
13259c8e88eSDag-Erling Smørgrav {
13359c8e88eSDag-Erling Smørgrav 	return diff_ranges_touch(&cc->chunk, &other->chunk)
13459c8e88eSDag-Erling Smørgrav 		|| diff_ranges_touch(&cc->left, &other->left)
13559c8e88eSDag-Erling Smørgrav 		|| diff_ranges_touch(&cc->right, &other->right);
13659c8e88eSDag-Erling Smørgrav }
13759c8e88eSDag-Erling Smørgrav 
13859c8e88eSDag-Erling Smørgrav void
diff_chunk_contexts_merge(struct diff_chunk_context * cc,const struct diff_chunk_context * other)13959c8e88eSDag-Erling Smørgrav diff_chunk_contexts_merge(struct diff_chunk_context *cc,
14059c8e88eSDag-Erling Smørgrav 			  const struct diff_chunk_context *other)
14159c8e88eSDag-Erling Smørgrav {
14259c8e88eSDag-Erling Smørgrav 	diff_ranges_merge(&cc->chunk, &other->chunk);
14359c8e88eSDag-Erling Smørgrav 	diff_ranges_merge(&cc->left, &other->left);
14459c8e88eSDag-Erling Smørgrav 	diff_ranges_merge(&cc->right, &other->right);
14559c8e88eSDag-Erling Smørgrav }
14659c8e88eSDag-Erling Smørgrav 
14759c8e88eSDag-Erling Smørgrav void
diff_chunk_context_load_change(struct diff_chunk_context * cc,int * nchunks_used,struct diff_result * result,int start_chunk_idx,int context_lines)14859c8e88eSDag-Erling Smørgrav diff_chunk_context_load_change(struct diff_chunk_context *cc,
14959c8e88eSDag-Erling Smørgrav 			       int *nchunks_used,
15059c8e88eSDag-Erling Smørgrav 			       struct diff_result *result,
15159c8e88eSDag-Erling Smørgrav 			       int start_chunk_idx,
15259c8e88eSDag-Erling Smørgrav 			       int context_lines)
15359c8e88eSDag-Erling Smørgrav {
15459c8e88eSDag-Erling Smørgrav 	int i;
15559c8e88eSDag-Erling Smørgrav 	int seen_minus = 0, seen_plus = 0;
15659c8e88eSDag-Erling Smørgrav 
15759c8e88eSDag-Erling Smørgrav 	if (nchunks_used)
15859c8e88eSDag-Erling Smørgrav 		*nchunks_used = 0;
15959c8e88eSDag-Erling Smørgrav 
16059c8e88eSDag-Erling Smørgrav 	for (i = start_chunk_idx; i < result->chunks.len; i++) {
16159c8e88eSDag-Erling Smørgrav 		struct diff_chunk *chunk = &result->chunks.head[i];
16259c8e88eSDag-Erling Smørgrav 		enum diff_chunk_type t = diff_chunk_type(chunk);
16359c8e88eSDag-Erling Smørgrav 		struct diff_chunk_context next;
16459c8e88eSDag-Erling Smørgrav 
16559c8e88eSDag-Erling Smørgrav 		if (t != CHUNK_MINUS && t != CHUNK_PLUS) {
16659c8e88eSDag-Erling Smørgrav 			if (nchunks_used)
16759c8e88eSDag-Erling Smørgrav 				(*nchunks_used)++;
16859c8e88eSDag-Erling Smørgrav 			if (seen_minus || seen_plus)
16959c8e88eSDag-Erling Smørgrav 				break;
17059c8e88eSDag-Erling Smørgrav 			else
17159c8e88eSDag-Erling Smørgrav 				continue;
17259c8e88eSDag-Erling Smørgrav 		} else if (t == CHUNK_MINUS)
17359c8e88eSDag-Erling Smørgrav 			seen_minus = 1;
17459c8e88eSDag-Erling Smørgrav 		else if (t == CHUNK_PLUS)
17559c8e88eSDag-Erling Smørgrav 			seen_plus = 1;
17659c8e88eSDag-Erling Smørgrav 
17759c8e88eSDag-Erling Smørgrav 		if (diff_chunk_context_empty(cc)) {
17859c8e88eSDag-Erling Smørgrav 			/* Note down the start point, any number of subsequent
17959c8e88eSDag-Erling Smørgrav 			 * chunks may be joined up to this chunk by being
18059c8e88eSDag-Erling Smørgrav 			 * directly adjacent. */
18159c8e88eSDag-Erling Smørgrav 			diff_chunk_context_get(cc, result, i, context_lines);
18259c8e88eSDag-Erling Smørgrav 			if (nchunks_used)
18359c8e88eSDag-Erling Smørgrav 				(*nchunks_used)++;
18459c8e88eSDag-Erling Smørgrav 			continue;
18559c8e88eSDag-Erling Smørgrav 		}
18659c8e88eSDag-Erling Smørgrav 
18759c8e88eSDag-Erling Smørgrav 		/* There already is a previous chunk noted down for being
18859c8e88eSDag-Erling Smørgrav 		 * printed. Does it join up with this one? */
18959c8e88eSDag-Erling Smørgrav 		diff_chunk_context_get(&next, result, i, context_lines);
19059c8e88eSDag-Erling Smørgrav 
19159c8e88eSDag-Erling Smørgrav 		if (diff_chunk_contexts_touch(cc, &next)) {
19259c8e88eSDag-Erling Smørgrav 			/* This next context touches or overlaps the previous
19359c8e88eSDag-Erling Smørgrav 			 * one, join. */
19459c8e88eSDag-Erling Smørgrav 			diff_chunk_contexts_merge(cc, &next);
19559c8e88eSDag-Erling Smørgrav 			if (nchunks_used)
19659c8e88eSDag-Erling Smørgrav 				(*nchunks_used)++;
19759c8e88eSDag-Erling Smørgrav 			continue;
19859c8e88eSDag-Erling Smørgrav 		} else
19959c8e88eSDag-Erling Smørgrav 			break;
20059c8e88eSDag-Erling Smørgrav 	}
20159c8e88eSDag-Erling Smørgrav }
20259c8e88eSDag-Erling Smørgrav 
20359c8e88eSDag-Erling Smørgrav struct diff_output_unidiff_state {
20459c8e88eSDag-Erling Smørgrav 	bool header_printed;
20559c8e88eSDag-Erling Smørgrav 	char prototype[DIFF_FUNCTION_CONTEXT_SIZE];
20659c8e88eSDag-Erling Smørgrav 	int last_prototype_idx;
20759c8e88eSDag-Erling Smørgrav };
20859c8e88eSDag-Erling Smørgrav 
20959c8e88eSDag-Erling Smørgrav struct diff_output_unidiff_state *
diff_output_unidiff_state_alloc(void)21059c8e88eSDag-Erling Smørgrav diff_output_unidiff_state_alloc(void)
21159c8e88eSDag-Erling Smørgrav {
21259c8e88eSDag-Erling Smørgrav 	struct diff_output_unidiff_state *state;
21359c8e88eSDag-Erling Smørgrav 
21459c8e88eSDag-Erling Smørgrav 	state = calloc(1, sizeof(struct diff_output_unidiff_state));
21559c8e88eSDag-Erling Smørgrav 	if (state != NULL)
21659c8e88eSDag-Erling Smørgrav 		diff_output_unidiff_state_reset(state);
21759c8e88eSDag-Erling Smørgrav 	return state;
21859c8e88eSDag-Erling Smørgrav }
21959c8e88eSDag-Erling Smørgrav 
22059c8e88eSDag-Erling Smørgrav void
diff_output_unidiff_state_reset(struct diff_output_unidiff_state * state)22159c8e88eSDag-Erling Smørgrav diff_output_unidiff_state_reset(struct diff_output_unidiff_state *state)
22259c8e88eSDag-Erling Smørgrav {
22359c8e88eSDag-Erling Smørgrav 	state->header_printed = false;
22459c8e88eSDag-Erling Smørgrav 	memset(state->prototype, 0, sizeof(state->prototype));
22559c8e88eSDag-Erling Smørgrav 	state->last_prototype_idx = 0;
22659c8e88eSDag-Erling Smørgrav }
22759c8e88eSDag-Erling Smørgrav 
22859c8e88eSDag-Erling Smørgrav void
diff_output_unidiff_state_free(struct diff_output_unidiff_state * state)22959c8e88eSDag-Erling Smørgrav diff_output_unidiff_state_free(struct diff_output_unidiff_state *state)
23059c8e88eSDag-Erling Smørgrav {
23159c8e88eSDag-Erling Smørgrav 	free(state);
23259c8e88eSDag-Erling Smørgrav }
23359c8e88eSDag-Erling Smørgrav 
23459c8e88eSDag-Erling Smørgrav static int
output_unidiff_chunk(struct diff_output_info * outinfo,FILE * dest,struct diff_output_unidiff_state * state,const struct diff_input_info * info,const struct diff_result * result,bool print_header,bool show_function_prototypes,const struct diff_chunk_context * cc)23559c8e88eSDag-Erling Smørgrav output_unidiff_chunk(struct diff_output_info *outinfo, FILE *dest,
23659c8e88eSDag-Erling Smørgrav 		     struct diff_output_unidiff_state *state,
23759c8e88eSDag-Erling Smørgrav 		     const struct diff_input_info *info,
23859c8e88eSDag-Erling Smørgrav 		     const struct diff_result *result,
23959c8e88eSDag-Erling Smørgrav 		     bool print_header, bool show_function_prototypes,
24059c8e88eSDag-Erling Smørgrav 		     const struct diff_chunk_context *cc)
24159c8e88eSDag-Erling Smørgrav {
24259c8e88eSDag-Erling Smørgrav 	int rc, left_start, left_len, right_start, right_len;
24359c8e88eSDag-Erling Smørgrav 	off_t outoff = 0, *offp;
24459c8e88eSDag-Erling Smørgrav 	uint8_t *typep;
24559c8e88eSDag-Erling Smørgrav 
24659c8e88eSDag-Erling Smørgrav 	if (diff_range_empty(&cc->left) && diff_range_empty(&cc->right))
24759c8e88eSDag-Erling Smørgrav 		return DIFF_RC_OK;
24859c8e88eSDag-Erling Smørgrav 
24959c8e88eSDag-Erling Smørgrav 	if (outinfo && outinfo->line_offsets.len > 0) {
25059c8e88eSDag-Erling Smørgrav 		unsigned int idx = outinfo->line_offsets.len - 1;
25159c8e88eSDag-Erling Smørgrav 		outoff = outinfo->line_offsets.head[idx];
25259c8e88eSDag-Erling Smørgrav 	}
25359c8e88eSDag-Erling Smørgrav 
25459c8e88eSDag-Erling Smørgrav 	if (print_header && !(state->header_printed)) {
25559c8e88eSDag-Erling Smørgrav 		rc = fprintf(dest, "--- %s\n",
25659c8e88eSDag-Erling Smørgrav 		    diff_output_get_label_left(info));
25759c8e88eSDag-Erling Smørgrav 		if (rc < 0)
25859c8e88eSDag-Erling Smørgrav 			return errno;
25959c8e88eSDag-Erling Smørgrav 		if (outinfo) {
26059c8e88eSDag-Erling Smørgrav 			ARRAYLIST_ADD(offp, outinfo->line_offsets);
26159c8e88eSDag-Erling Smørgrav 			if (offp == NULL)
26259c8e88eSDag-Erling Smørgrav 				return ENOMEM;
26359c8e88eSDag-Erling Smørgrav 			outoff += rc;
26459c8e88eSDag-Erling Smørgrav 			*offp = outoff;
26559c8e88eSDag-Erling Smørgrav 			ARRAYLIST_ADD(typep, outinfo->line_types);
26659c8e88eSDag-Erling Smørgrav 			if (typep == NULL)
26759c8e88eSDag-Erling Smørgrav 				return ENOMEM;
26859c8e88eSDag-Erling Smørgrav 			*typep = DIFF_LINE_MINUS;
26959c8e88eSDag-Erling Smørgrav 		}
27059c8e88eSDag-Erling Smørgrav 		rc = fprintf(dest, "+++ %s\n",
27159c8e88eSDag-Erling Smørgrav 		    diff_output_get_label_right(info));
27259c8e88eSDag-Erling Smørgrav 		if (rc < 0)
27359c8e88eSDag-Erling Smørgrav 			return errno;
27459c8e88eSDag-Erling Smørgrav 		if (outinfo) {
27559c8e88eSDag-Erling Smørgrav 			ARRAYLIST_ADD(offp, outinfo->line_offsets);
27659c8e88eSDag-Erling Smørgrav 			if (offp == NULL)
27759c8e88eSDag-Erling Smørgrav 				return ENOMEM;
27859c8e88eSDag-Erling Smørgrav 			outoff += rc;
27959c8e88eSDag-Erling Smørgrav 			*offp = outoff;
28059c8e88eSDag-Erling Smørgrav 			ARRAYLIST_ADD(typep, outinfo->line_types);
28159c8e88eSDag-Erling Smørgrav 			if (typep == NULL)
28259c8e88eSDag-Erling Smørgrav 				return ENOMEM;
28359c8e88eSDag-Erling Smørgrav 			*typep = DIFF_LINE_PLUS;
28459c8e88eSDag-Erling Smørgrav 		}
28559c8e88eSDag-Erling Smørgrav 		state->header_printed = true;
28659c8e88eSDag-Erling Smørgrav 	}
28759c8e88eSDag-Erling Smørgrav 
28859c8e88eSDag-Erling Smørgrav 	left_len = cc->left.end - cc->left.start;
28959c8e88eSDag-Erling Smørgrav 	if (result->left->atoms.len == 0)
29059c8e88eSDag-Erling Smørgrav 		left_start = 0;
29159c8e88eSDag-Erling Smørgrav 	else if (left_len == 0 && cc->left.start > 0)
29259c8e88eSDag-Erling Smørgrav 		left_start = cc->left.start;
29359c8e88eSDag-Erling Smørgrav 	else
29459c8e88eSDag-Erling Smørgrav 		left_start = cc->left.start + 1;
29559c8e88eSDag-Erling Smørgrav 
29659c8e88eSDag-Erling Smørgrav 	right_len = cc->right.end - cc->right.start;
29759c8e88eSDag-Erling Smørgrav 	if (result->right->atoms.len == 0)
29859c8e88eSDag-Erling Smørgrav 		right_start = 0;
29959c8e88eSDag-Erling Smørgrav 	else if (right_len == 0 && cc->right.start > 0)
30059c8e88eSDag-Erling Smørgrav 		right_start = cc->right.start;
30159c8e88eSDag-Erling Smørgrav 	else
30259c8e88eSDag-Erling Smørgrav 		right_start = cc->right.start + 1;
30359c8e88eSDag-Erling Smørgrav 
3045fbe8912SDag-Erling Smørgrav 	/* Got the absolute line numbers where to start printing, and the index
3055fbe8912SDag-Erling Smørgrav 	 * of the interesting (non-context) chunk.
3065fbe8912SDag-Erling Smørgrav 	 * To print context lines above the interesting chunk, nipping on the
3075fbe8912SDag-Erling Smørgrav 	 * previous chunk index may be necessary.
3085fbe8912SDag-Erling Smørgrav 	 * It is guaranteed to be only context lines where left == right, so it
3095fbe8912SDag-Erling Smørgrav 	 * suffices to look on the left. */
3105fbe8912SDag-Erling Smørgrav 	const struct diff_chunk *first_chunk;
3115fbe8912SDag-Erling Smørgrav 	int chunk_start_line;
3125fbe8912SDag-Erling Smørgrav 	first_chunk = &result->chunks.head[cc->chunk.start];
3135fbe8912SDag-Erling Smørgrav 	chunk_start_line = diff_atom_root_idx(result->left,
3145fbe8912SDag-Erling Smørgrav 					      first_chunk->left_start);
31559c8e88eSDag-Erling Smørgrav 	if (show_function_prototypes) {
31659c8e88eSDag-Erling Smørgrav 		rc = diff_output_match_function_prototype(state->prototype,
31759c8e88eSDag-Erling Smørgrav 		    sizeof(state->prototype), &state->last_prototype_idx,
3185fbe8912SDag-Erling Smørgrav 		    result, chunk_start_line);
31959c8e88eSDag-Erling Smørgrav 		if (rc)
32059c8e88eSDag-Erling Smørgrav 			return rc;
32159c8e88eSDag-Erling Smørgrav 	}
32259c8e88eSDag-Erling Smørgrav 
32359c8e88eSDag-Erling Smørgrav 	if (left_len == 1 && right_len == 1) {
32459c8e88eSDag-Erling Smørgrav 		rc = fprintf(dest, "@@ -%d +%d @@%s%s\n",
32559c8e88eSDag-Erling Smørgrav 			left_start, right_start,
32659c8e88eSDag-Erling Smørgrav 			state->prototype[0] ? " " : "",
32759c8e88eSDag-Erling Smørgrav 			state->prototype[0] ? state->prototype : "");
32859c8e88eSDag-Erling Smørgrav 	} else if (left_len == 1 && right_len != 1) {
32959c8e88eSDag-Erling Smørgrav 		rc = fprintf(dest, "@@ -%d +%d,%d @@%s%s\n",
33059c8e88eSDag-Erling Smørgrav 			left_start, right_start, right_len,
33159c8e88eSDag-Erling Smørgrav 			state->prototype[0] ? " " : "",
33259c8e88eSDag-Erling Smørgrav 			state->prototype[0] ? state->prototype : "");
33359c8e88eSDag-Erling Smørgrav 	} else if (left_len != 1 && right_len == 1) {
33459c8e88eSDag-Erling Smørgrav 		rc = fprintf(dest, "@@ -%d,%d +%d @@%s%s\n",
33559c8e88eSDag-Erling Smørgrav 			left_start, left_len, right_start,
33659c8e88eSDag-Erling Smørgrav 			state->prototype[0] ? " " : "",
33759c8e88eSDag-Erling Smørgrav 			state->prototype[0] ? state->prototype : "");
33859c8e88eSDag-Erling Smørgrav 	} else {
33959c8e88eSDag-Erling Smørgrav 		rc = fprintf(dest, "@@ -%d,%d +%d,%d @@%s%s\n",
34059c8e88eSDag-Erling Smørgrav 			left_start, left_len, right_start, right_len,
34159c8e88eSDag-Erling Smørgrav 			state->prototype[0] ? " " : "",
34259c8e88eSDag-Erling Smørgrav 			state->prototype[0] ? state->prototype : "");
34359c8e88eSDag-Erling Smørgrav 	}
34459c8e88eSDag-Erling Smørgrav 	if (rc < 0)
34559c8e88eSDag-Erling Smørgrav 		return errno;
34659c8e88eSDag-Erling Smørgrav 	if (outinfo) {
34759c8e88eSDag-Erling Smørgrav 		ARRAYLIST_ADD(offp, outinfo->line_offsets);
34859c8e88eSDag-Erling Smørgrav 		if (offp == NULL)
34959c8e88eSDag-Erling Smørgrav 			return ENOMEM;
35059c8e88eSDag-Erling Smørgrav 		outoff += rc;
35159c8e88eSDag-Erling Smørgrav 		*offp = outoff;
35259c8e88eSDag-Erling Smørgrav 		ARRAYLIST_ADD(typep, outinfo->line_types);
35359c8e88eSDag-Erling Smørgrav 		if (typep == NULL)
35459c8e88eSDag-Erling Smørgrav 			return ENOMEM;
35559c8e88eSDag-Erling Smørgrav 		*typep = DIFF_LINE_HUNK;
35659c8e88eSDag-Erling Smørgrav 	}
35759c8e88eSDag-Erling Smørgrav 
35859c8e88eSDag-Erling Smørgrav 	if (cc->left.start < chunk_start_line) {
35959c8e88eSDag-Erling Smørgrav 		rc = diff_output_lines(outinfo, dest, " ",
36059c8e88eSDag-Erling Smørgrav 				  &result->left->atoms.head[cc->left.start],
36159c8e88eSDag-Erling Smørgrav 				  chunk_start_line - cc->left.start);
36259c8e88eSDag-Erling Smørgrav 		if (rc)
36359c8e88eSDag-Erling Smørgrav 			return rc;
36459c8e88eSDag-Erling Smørgrav 	}
36559c8e88eSDag-Erling Smørgrav 
36659c8e88eSDag-Erling Smørgrav 	/* Now write out all the joined chunks and contexts between them */
36759c8e88eSDag-Erling Smørgrav 	int c_idx;
36859c8e88eSDag-Erling Smørgrav 	for (c_idx = cc->chunk.start; c_idx < cc->chunk.end; c_idx++) {
36959c8e88eSDag-Erling Smørgrav 		const struct diff_chunk *c = &result->chunks.head[c_idx];
37059c8e88eSDag-Erling Smørgrav 
37159c8e88eSDag-Erling Smørgrav 		if (c->left_count && c->right_count)
37259c8e88eSDag-Erling Smørgrav 			rc = diff_output_lines(outinfo, dest,
37359c8e88eSDag-Erling Smørgrav 					  c->solved ? " " : "?",
37459c8e88eSDag-Erling Smørgrav 					  c->left_start, c->left_count);
37559c8e88eSDag-Erling Smørgrav 		else if (c->left_count && !c->right_count)
37659c8e88eSDag-Erling Smørgrav 			rc = diff_output_lines(outinfo, dest,
37759c8e88eSDag-Erling Smørgrav 					  c->solved ? "-" : "?",
37859c8e88eSDag-Erling Smørgrav 					  c->left_start, c->left_count);
37959c8e88eSDag-Erling Smørgrav 		else if (c->right_count && !c->left_count)
38059c8e88eSDag-Erling Smørgrav 			rc = diff_output_lines(outinfo, dest,
38159c8e88eSDag-Erling Smørgrav 					  c->solved ? "+" : "?",
38259c8e88eSDag-Erling Smørgrav 					  c->right_start, c->right_count);
38359c8e88eSDag-Erling Smørgrav 		if (rc)
38459c8e88eSDag-Erling Smørgrav 			return rc;
38559c8e88eSDag-Erling Smørgrav 
38659c8e88eSDag-Erling Smørgrav 		if (cc->chunk.end == result->chunks.len) {
38759c8e88eSDag-Erling Smørgrav 			rc = diff_output_trailing_newline_msg(outinfo, dest, c);
38859c8e88eSDag-Erling Smørgrav 			if (rc != DIFF_RC_OK)
38959c8e88eSDag-Erling Smørgrav 				return rc;
39059c8e88eSDag-Erling Smørgrav 		}
39159c8e88eSDag-Erling Smørgrav 	}
39259c8e88eSDag-Erling Smørgrav 
39359c8e88eSDag-Erling Smørgrav 	/* Trailing context? */
39459c8e88eSDag-Erling Smørgrav 	const struct diff_chunk *last_chunk;
39559c8e88eSDag-Erling Smørgrav 	int chunk_end_line;
39659c8e88eSDag-Erling Smørgrav 	last_chunk = &result->chunks.head[cc->chunk.end - 1];
39759c8e88eSDag-Erling Smørgrav 	chunk_end_line = diff_atom_root_idx(result->left,
39859c8e88eSDag-Erling Smørgrav 					    last_chunk->left_start
39959c8e88eSDag-Erling Smørgrav 					    + last_chunk->left_count);
40059c8e88eSDag-Erling Smørgrav 	if (cc->left.end > chunk_end_line) {
40159c8e88eSDag-Erling Smørgrav 		rc = diff_output_lines(outinfo, dest, " ",
40259c8e88eSDag-Erling Smørgrav 				  &result->left->atoms.head[chunk_end_line],
40359c8e88eSDag-Erling Smørgrav 				  cc->left.end - chunk_end_line);
40459c8e88eSDag-Erling Smørgrav 		if (rc)
40559c8e88eSDag-Erling Smørgrav 			return rc;
40659c8e88eSDag-Erling Smørgrav 
40759c8e88eSDag-Erling Smørgrav 		if (cc->left.end == result->left->atoms.len) {
40859c8e88eSDag-Erling Smørgrav 			rc = diff_output_trailing_newline_msg(outinfo, dest,
40959c8e88eSDag-Erling Smørgrav 			    &result->chunks.head[result->chunks.len - 1]);
41059c8e88eSDag-Erling Smørgrav 			if (rc != DIFF_RC_OK)
41159c8e88eSDag-Erling Smørgrav 				return rc;
41259c8e88eSDag-Erling Smørgrav 		}
41359c8e88eSDag-Erling Smørgrav 	}
41459c8e88eSDag-Erling Smørgrav 
41559c8e88eSDag-Erling Smørgrav 	return DIFF_RC_OK;
41659c8e88eSDag-Erling Smørgrav }
41759c8e88eSDag-Erling Smørgrav 
41859c8e88eSDag-Erling Smørgrav int
diff_output_unidiff_chunk(struct diff_output_info ** output_info,FILE * dest,struct diff_output_unidiff_state * state,const struct diff_input_info * info,const struct diff_result * result,const struct diff_chunk_context * cc)41959c8e88eSDag-Erling Smørgrav diff_output_unidiff_chunk(struct diff_output_info **output_info, FILE *dest,
42059c8e88eSDag-Erling Smørgrav 			  struct diff_output_unidiff_state *state,
42159c8e88eSDag-Erling Smørgrav 			  const struct diff_input_info *info,
42259c8e88eSDag-Erling Smørgrav 			  const struct diff_result *result,
42359c8e88eSDag-Erling Smørgrav 			  const struct diff_chunk_context *cc)
42459c8e88eSDag-Erling Smørgrav {
42559c8e88eSDag-Erling Smørgrav 	struct diff_output_info *outinfo = NULL;
42659c8e88eSDag-Erling Smørgrav 	int flags = (result->left->root->diff_flags |
42759c8e88eSDag-Erling Smørgrav 	    result->right->root->diff_flags);
42859c8e88eSDag-Erling Smørgrav 	bool show_function_prototypes = (flags & DIFF_FLAG_SHOW_PROTOTYPES);
42959c8e88eSDag-Erling Smørgrav 
43059c8e88eSDag-Erling Smørgrav 	if (output_info) {
43159c8e88eSDag-Erling Smørgrav 		*output_info = diff_output_info_alloc();
43259c8e88eSDag-Erling Smørgrav 		if (*output_info == NULL)
43359c8e88eSDag-Erling Smørgrav 			return ENOMEM;
43459c8e88eSDag-Erling Smørgrav 		outinfo = *output_info;
43559c8e88eSDag-Erling Smørgrav 	}
43659c8e88eSDag-Erling Smørgrav 
43759c8e88eSDag-Erling Smørgrav 	return output_unidiff_chunk(outinfo, dest, state, info,
43859c8e88eSDag-Erling Smørgrav 	    result, false, show_function_prototypes, cc);
43959c8e88eSDag-Erling Smørgrav }
44059c8e88eSDag-Erling Smørgrav 
44159c8e88eSDag-Erling Smørgrav int
diff_output_unidiff(struct diff_output_info ** output_info,FILE * dest,const struct diff_input_info * info,const struct diff_result * result,unsigned int context_lines)44259c8e88eSDag-Erling Smørgrav diff_output_unidiff(struct diff_output_info **output_info,
44359c8e88eSDag-Erling Smørgrav 		    FILE *dest, const struct diff_input_info *info,
44459c8e88eSDag-Erling Smørgrav 		    const struct diff_result *result,
44559c8e88eSDag-Erling Smørgrav 		    unsigned int context_lines)
44659c8e88eSDag-Erling Smørgrav {
44759c8e88eSDag-Erling Smørgrav 	struct diff_output_unidiff_state *state;
44859c8e88eSDag-Erling Smørgrav 	struct diff_chunk_context cc = {};
44959c8e88eSDag-Erling Smørgrav 	struct diff_output_info *outinfo = NULL;
45059c8e88eSDag-Erling Smørgrav 	int atomizer_flags = (result->left->atomizer_flags|
45159c8e88eSDag-Erling Smørgrav 	    result->right->atomizer_flags);
45259c8e88eSDag-Erling Smørgrav 	int flags = (result->left->root->diff_flags |
45359c8e88eSDag-Erling Smørgrav 	    result->right->root->diff_flags);
45459c8e88eSDag-Erling Smørgrav 	bool show_function_prototypes = (flags & DIFF_FLAG_SHOW_PROTOTYPES);
45559c8e88eSDag-Erling Smørgrav 	bool force_text = (flags & DIFF_FLAG_FORCE_TEXT_DATA);
45659c8e88eSDag-Erling Smørgrav 	bool have_binary = (atomizer_flags & DIFF_ATOMIZER_FOUND_BINARY_DATA);
45759c8e88eSDag-Erling Smørgrav 	off_t outoff = 0, *offp;
45859c8e88eSDag-Erling Smørgrav 	uint8_t *typep;
45959c8e88eSDag-Erling Smørgrav 	int rc, i;
46059c8e88eSDag-Erling Smørgrav 
46159c8e88eSDag-Erling Smørgrav 	if (!result)
46259c8e88eSDag-Erling Smørgrav 		return EINVAL;
46359c8e88eSDag-Erling Smørgrav 	if (result->rc != DIFF_RC_OK)
46459c8e88eSDag-Erling Smørgrav 		return result->rc;
46559c8e88eSDag-Erling Smørgrav 
46659c8e88eSDag-Erling Smørgrav 	if (output_info) {
46759c8e88eSDag-Erling Smørgrav 		*output_info = diff_output_info_alloc();
46859c8e88eSDag-Erling Smørgrav 		if (*output_info == NULL)
46959c8e88eSDag-Erling Smørgrav 			return ENOMEM;
47059c8e88eSDag-Erling Smørgrav 		outinfo = *output_info;
47159c8e88eSDag-Erling Smørgrav 	}
47259c8e88eSDag-Erling Smørgrav 
47359c8e88eSDag-Erling Smørgrav 	if (have_binary && !force_text) {
47459c8e88eSDag-Erling Smørgrav 		for (i = 0; i < result->chunks.len; i++) {
47559c8e88eSDag-Erling Smørgrav 			struct diff_chunk *c = &result->chunks.head[i];
47659c8e88eSDag-Erling Smørgrav 			enum diff_chunk_type t = diff_chunk_type(c);
47759c8e88eSDag-Erling Smørgrav 
47859c8e88eSDag-Erling Smørgrav 			if (t != CHUNK_MINUS && t != CHUNK_PLUS)
47959c8e88eSDag-Erling Smørgrav 				continue;
48059c8e88eSDag-Erling Smørgrav 
48159c8e88eSDag-Erling Smørgrav 			if (outinfo && outinfo->line_offsets.len > 0) {
48259c8e88eSDag-Erling Smørgrav 				unsigned int idx =
48359c8e88eSDag-Erling Smørgrav 				    outinfo->line_offsets.len - 1;
48459c8e88eSDag-Erling Smørgrav 				outoff = outinfo->line_offsets.head[idx];
48559c8e88eSDag-Erling Smørgrav 			}
48659c8e88eSDag-Erling Smørgrav 
48759c8e88eSDag-Erling Smørgrav 			rc = fprintf(dest, "Binary files %s and %s differ\n",
48859c8e88eSDag-Erling Smørgrav 			    diff_output_get_label_left(info),
48959c8e88eSDag-Erling Smørgrav 			    diff_output_get_label_right(info));
49059c8e88eSDag-Erling Smørgrav 			if (outinfo) {
49159c8e88eSDag-Erling Smørgrav 				ARRAYLIST_ADD(offp, outinfo->line_offsets);
49259c8e88eSDag-Erling Smørgrav 				if (offp == NULL)
49359c8e88eSDag-Erling Smørgrav 					return ENOMEM;
49459c8e88eSDag-Erling Smørgrav 				outoff += rc;
49559c8e88eSDag-Erling Smørgrav 				*offp = outoff;
49659c8e88eSDag-Erling Smørgrav 				ARRAYLIST_ADD(typep, outinfo->line_types);
49759c8e88eSDag-Erling Smørgrav 				if (typep == NULL)
49859c8e88eSDag-Erling Smørgrav 					return ENOMEM;
49959c8e88eSDag-Erling Smørgrav 				*typep = DIFF_LINE_NONE;
50059c8e88eSDag-Erling Smørgrav 			}
50159c8e88eSDag-Erling Smørgrav 			break;
50259c8e88eSDag-Erling Smørgrav 		}
50359c8e88eSDag-Erling Smørgrav 
50459c8e88eSDag-Erling Smørgrav 		return DIFF_RC_OK;
50559c8e88eSDag-Erling Smørgrav 	}
50659c8e88eSDag-Erling Smørgrav 
50759c8e88eSDag-Erling Smørgrav 	state = diff_output_unidiff_state_alloc();
50859c8e88eSDag-Erling Smørgrav 	if (state == NULL) {
50959c8e88eSDag-Erling Smørgrav 		if (output_info) {
51059c8e88eSDag-Erling Smørgrav 			diff_output_info_free(*output_info);
51159c8e88eSDag-Erling Smørgrav 			*output_info = NULL;
51259c8e88eSDag-Erling Smørgrav 		}
51359c8e88eSDag-Erling Smørgrav 		return ENOMEM;
51459c8e88eSDag-Erling Smørgrav 	}
51559c8e88eSDag-Erling Smørgrav 
51659c8e88eSDag-Erling Smørgrav #if DEBUG
51759c8e88eSDag-Erling Smørgrav 	unsigned int check_left_pos, check_right_pos;
51859c8e88eSDag-Erling Smørgrav 	check_left_pos = 0;
51959c8e88eSDag-Erling Smørgrav 	check_right_pos = 0;
52059c8e88eSDag-Erling Smørgrav 	for (i = 0; i < result->chunks.len; i++) {
52159c8e88eSDag-Erling Smørgrav 		struct diff_chunk *c = &result->chunks.head[i];
52259c8e88eSDag-Erling Smørgrav 		enum diff_chunk_type t = diff_chunk_type(c);
52359c8e88eSDag-Erling Smørgrav 
52459c8e88eSDag-Erling Smørgrav 		debug("[%d] %s lines L%d R%d @L %d @R %d\n",
52559c8e88eSDag-Erling Smørgrav 		      i, (t == CHUNK_MINUS ? "minus" :
52659c8e88eSDag-Erling Smørgrav 			  (t == CHUNK_PLUS ? "plus" :
52759c8e88eSDag-Erling Smørgrav 			   (t == CHUNK_SAME ? "same" : "?"))),
52859c8e88eSDag-Erling Smørgrav 		      c->left_count,
52959c8e88eSDag-Erling Smørgrav 		      c->right_count,
53059c8e88eSDag-Erling Smørgrav 		      c->left_start ? diff_atom_root_idx(result->left, c->left_start) : -1,
53159c8e88eSDag-Erling Smørgrav 		      c->right_start ? diff_atom_root_idx(result->right, c->right_start) : -1);
53259c8e88eSDag-Erling Smørgrav 		assert(check_left_pos == diff_atom_root_idx(result->left, c->left_start));
53359c8e88eSDag-Erling Smørgrav 		assert(check_right_pos == diff_atom_root_idx(result->right, c->right_start));
53459c8e88eSDag-Erling Smørgrav 		check_left_pos += c->left_count;
53559c8e88eSDag-Erling Smørgrav 		check_right_pos += c->right_count;
53659c8e88eSDag-Erling Smørgrav 
53759c8e88eSDag-Erling Smørgrav 	}
53859c8e88eSDag-Erling Smørgrav 	assert(check_left_pos == result->left->atoms.len);
53959c8e88eSDag-Erling Smørgrav 	assert(check_right_pos == result->right->atoms.len);
54059c8e88eSDag-Erling Smørgrav #endif
54159c8e88eSDag-Erling Smørgrav 
54259c8e88eSDag-Erling Smørgrav 	for (i = 0; i < result->chunks.len; i++) {
54359c8e88eSDag-Erling Smørgrav 		struct diff_chunk *c = &result->chunks.head[i];
54459c8e88eSDag-Erling Smørgrav 		enum diff_chunk_type t = diff_chunk_type(c);
54559c8e88eSDag-Erling Smørgrav 		struct diff_chunk_context next;
54659c8e88eSDag-Erling Smørgrav 
54759c8e88eSDag-Erling Smørgrav 		if (t != CHUNK_MINUS && t != CHUNK_PLUS)
54859c8e88eSDag-Erling Smørgrav 			continue;
54959c8e88eSDag-Erling Smørgrav 
55059c8e88eSDag-Erling Smørgrav 		if (diff_chunk_context_empty(&cc)) {
55159c8e88eSDag-Erling Smørgrav 			/* These are the first lines being printed.
55259c8e88eSDag-Erling Smørgrav 			 * Note down the start point, any number of subsequent
55359c8e88eSDag-Erling Smørgrav 			 * chunks may be joined up to this unidiff chunk by
55459c8e88eSDag-Erling Smørgrav 			 * context lines or by being directly adjacent. */
55559c8e88eSDag-Erling Smørgrav 			diff_chunk_context_get(&cc, result, i, context_lines);
55659c8e88eSDag-Erling Smørgrav 			debug("new chunk to be printed:"
55759c8e88eSDag-Erling Smørgrav 			      " chunk %d-%d left %d-%d right %d-%d\n",
55859c8e88eSDag-Erling Smørgrav 			      cc.chunk.start, cc.chunk.end,
55959c8e88eSDag-Erling Smørgrav 			      cc.left.start, cc.left.end,
56059c8e88eSDag-Erling Smørgrav 			      cc.right.start, cc.right.end);
56159c8e88eSDag-Erling Smørgrav 			continue;
56259c8e88eSDag-Erling Smørgrav 		}
56359c8e88eSDag-Erling Smørgrav 
56459c8e88eSDag-Erling Smørgrav 		/* There already is a previous chunk noted down for being
56559c8e88eSDag-Erling Smørgrav 		 * printed. Does it join up with this one? */
56659c8e88eSDag-Erling Smørgrav 		diff_chunk_context_get(&next, result, i, context_lines);
56759c8e88eSDag-Erling Smørgrav 		debug("new chunk to be printed:"
56859c8e88eSDag-Erling Smørgrav 		      " chunk %d-%d left %d-%d right %d-%d\n",
56959c8e88eSDag-Erling Smørgrav 		      next.chunk.start, next.chunk.end,
57059c8e88eSDag-Erling Smørgrav 		      next.left.start, next.left.end,
57159c8e88eSDag-Erling Smørgrav 		      next.right.start, next.right.end);
57259c8e88eSDag-Erling Smørgrav 
57359c8e88eSDag-Erling Smørgrav 		if (diff_chunk_contexts_touch(&cc, &next)) {
57459c8e88eSDag-Erling Smørgrav 			/* This next context touches or overlaps the previous
57559c8e88eSDag-Erling Smørgrav 			 * one, join. */
57659c8e88eSDag-Erling Smørgrav 			diff_chunk_contexts_merge(&cc, &next);
57759c8e88eSDag-Erling Smørgrav 			debug("new chunk to be printed touches previous chunk,"
57859c8e88eSDag-Erling Smørgrav 			      " now: left %d-%d right %d-%d\n",
57959c8e88eSDag-Erling Smørgrav 			      cc.left.start, cc.left.end,
58059c8e88eSDag-Erling Smørgrav 			      cc.right.start, cc.right.end);
58159c8e88eSDag-Erling Smørgrav 			continue;
58259c8e88eSDag-Erling Smørgrav 		}
58359c8e88eSDag-Erling Smørgrav 
58459c8e88eSDag-Erling Smørgrav 		/* No touching, so the previous context is complete with a gap
58559c8e88eSDag-Erling Smørgrav 		 * between it and this next one. Print the previous one and
58659c8e88eSDag-Erling Smørgrav 		 * start fresh here. */
58759c8e88eSDag-Erling Smørgrav 		debug("new chunk to be printed does not touch previous chunk;"
58859c8e88eSDag-Erling Smørgrav 		      " print left %d-%d right %d-%d\n",
58959c8e88eSDag-Erling Smørgrav 		      cc.left.start, cc.left.end, cc.right.start, cc.right.end);
59059c8e88eSDag-Erling Smørgrav 		output_unidiff_chunk(outinfo, dest, state, info, result,
59159c8e88eSDag-Erling Smørgrav 		    true, show_function_prototypes, &cc);
59259c8e88eSDag-Erling Smørgrav 		cc = next;
59359c8e88eSDag-Erling Smørgrav 		debug("new unprinted chunk is left %d-%d right %d-%d\n",
59459c8e88eSDag-Erling Smørgrav 		      cc.left.start, cc.left.end, cc.right.start, cc.right.end);
59559c8e88eSDag-Erling Smørgrav 	}
59659c8e88eSDag-Erling Smørgrav 
59759c8e88eSDag-Erling Smørgrav 	if (!diff_chunk_context_empty(&cc))
59859c8e88eSDag-Erling Smørgrav 		output_unidiff_chunk(outinfo, dest, state, info, result,
59959c8e88eSDag-Erling Smørgrav 		    true, show_function_prototypes, &cc);
60059c8e88eSDag-Erling Smørgrav 	diff_output_unidiff_state_free(state);
60159c8e88eSDag-Erling Smørgrav 	return DIFF_RC_OK;
60259c8e88eSDag-Erling Smørgrav }
603