xref: /freebsd/contrib/libdiff/lib/diff_output_plain.c (revision 59c8e88e72633afbc47a4ace0d2170d00d51f7dc)
1*59c8e88eSDag-Erling Smørgrav /* Output all lines of a diff_result. */
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 #include <errno.h>
19*59c8e88eSDag-Erling Smørgrav #include <stdint.h>
20*59c8e88eSDag-Erling Smørgrav #include <stdio.h>
21*59c8e88eSDag-Erling Smørgrav #include <stdbool.h>
22*59c8e88eSDag-Erling Smørgrav #include <stdlib.h>
23*59c8e88eSDag-Erling Smørgrav 
24*59c8e88eSDag-Erling Smørgrav #include <arraylist.h>
25*59c8e88eSDag-Erling Smørgrav #include <diff_main.h>
26*59c8e88eSDag-Erling Smørgrav #include <diff_output.h>
27*59c8e88eSDag-Erling Smørgrav 
28*59c8e88eSDag-Erling Smørgrav #include "diff_internal.h"
29*59c8e88eSDag-Erling Smørgrav 
30*59c8e88eSDag-Erling Smørgrav static int
31*59c8e88eSDag-Erling Smørgrav output_plain_chunk(struct diff_output_info *outinfo,
32*59c8e88eSDag-Erling Smørgrav     FILE *dest, const struct diff_input_info *info,
33*59c8e88eSDag-Erling Smørgrav     const struct diff_result *result,
34*59c8e88eSDag-Erling Smørgrav     struct diff_chunk_context *cc, off_t *outoff, bool headers_only)
35*59c8e88eSDag-Erling Smørgrav {
36*59c8e88eSDag-Erling Smørgrav 	off_t *offp;
37*59c8e88eSDag-Erling Smørgrav 	int left_start, left_len, right_start, right_len;
38*59c8e88eSDag-Erling Smørgrav 	int rc;
39*59c8e88eSDag-Erling Smørgrav 	bool change = false;
40*59c8e88eSDag-Erling Smørgrav 
41*59c8e88eSDag-Erling Smørgrav 	left_len = cc->left.end - cc->left.start;
42*59c8e88eSDag-Erling Smørgrav 	if (left_len < 0)
43*59c8e88eSDag-Erling Smørgrav 		return EINVAL;
44*59c8e88eSDag-Erling Smørgrav 	else if (result->left->atoms.len == 0)
45*59c8e88eSDag-Erling Smørgrav 		left_start = 0;
46*59c8e88eSDag-Erling Smørgrav 	else if (left_len == 0 && cc->left.start > 0)
47*59c8e88eSDag-Erling Smørgrav 		left_start = cc->left.start;
48*59c8e88eSDag-Erling Smørgrav 	else if (cc->left.end > 0)
49*59c8e88eSDag-Erling Smørgrav 		left_start = cc->left.start + 1;
50*59c8e88eSDag-Erling Smørgrav 	else
51*59c8e88eSDag-Erling Smørgrav 		left_start = cc->left.start;
52*59c8e88eSDag-Erling Smørgrav 
53*59c8e88eSDag-Erling Smørgrav 	right_len = cc->right.end - cc->right.start;
54*59c8e88eSDag-Erling Smørgrav 	if (right_len < 0)
55*59c8e88eSDag-Erling Smørgrav 		return EINVAL;
56*59c8e88eSDag-Erling Smørgrav 	else if (result->right->atoms.len == 0)
57*59c8e88eSDag-Erling Smørgrav 		right_start = 0;
58*59c8e88eSDag-Erling Smørgrav 	else if (right_len == 0 && cc->right.start > 0)
59*59c8e88eSDag-Erling Smørgrav 		right_start = cc->right.start;
60*59c8e88eSDag-Erling Smørgrav 	else if (cc->right.end > 0)
61*59c8e88eSDag-Erling Smørgrav 		right_start = cc->right.start + 1;
62*59c8e88eSDag-Erling Smørgrav 	else
63*59c8e88eSDag-Erling Smørgrav 		right_start = cc->right.start;
64*59c8e88eSDag-Erling Smørgrav 
65*59c8e88eSDag-Erling Smørgrav 	if (left_len == 0) {
66*59c8e88eSDag-Erling Smørgrav 		/* addition */
67*59c8e88eSDag-Erling Smørgrav 		if (right_len == 1) {
68*59c8e88eSDag-Erling Smørgrav 			rc = fprintf(dest, "%da%d\n", left_start, right_start);
69*59c8e88eSDag-Erling Smørgrav 		} else {
70*59c8e88eSDag-Erling Smørgrav 			rc = fprintf(dest, "%da%d,%d\n", left_start,
71*59c8e88eSDag-Erling Smørgrav 			    right_start, cc->right.end);
72*59c8e88eSDag-Erling Smørgrav 		}
73*59c8e88eSDag-Erling Smørgrav 	} else if (right_len == 0) {
74*59c8e88eSDag-Erling Smørgrav 		/* deletion */
75*59c8e88eSDag-Erling Smørgrav 		if (left_len == 1) {
76*59c8e88eSDag-Erling Smørgrav 			rc = fprintf(dest, "%dd%d\n", left_start,
77*59c8e88eSDag-Erling Smørgrav 			    right_start);
78*59c8e88eSDag-Erling Smørgrav 		} else {
79*59c8e88eSDag-Erling Smørgrav 			rc = fprintf(dest, "%d,%dd%d\n", left_start,
80*59c8e88eSDag-Erling Smørgrav 			    cc->left.end, right_start);
81*59c8e88eSDag-Erling Smørgrav 		}
82*59c8e88eSDag-Erling Smørgrav 	} else {
83*59c8e88eSDag-Erling Smørgrav 		/* change */
84*59c8e88eSDag-Erling Smørgrav 		change = true;
85*59c8e88eSDag-Erling Smørgrav 		if (left_len == 1 && right_len == 1) {
86*59c8e88eSDag-Erling Smørgrav 			rc = fprintf(dest, "%dc%d\n", left_start, right_start);
87*59c8e88eSDag-Erling Smørgrav 		} else if (left_len == 1) {
88*59c8e88eSDag-Erling Smørgrav 			rc = fprintf(dest, "%dc%d,%d\n", left_start,
89*59c8e88eSDag-Erling Smørgrav 			    right_start, cc->right.end);
90*59c8e88eSDag-Erling Smørgrav 		} else if (right_len == 1) {
91*59c8e88eSDag-Erling Smørgrav 			rc = fprintf(dest, "%d,%dc%d\n", left_start,
92*59c8e88eSDag-Erling Smørgrav 			    cc->left.end, right_start);
93*59c8e88eSDag-Erling Smørgrav 		} else {
94*59c8e88eSDag-Erling Smørgrav 			rc = fprintf(dest, "%d,%dc%d,%d\n", left_start,
95*59c8e88eSDag-Erling Smørgrav 			    cc->left.end, right_start, cc->right.end);
96*59c8e88eSDag-Erling Smørgrav 		}
97*59c8e88eSDag-Erling Smørgrav 	}
98*59c8e88eSDag-Erling Smørgrav 	if (rc < 0)
99*59c8e88eSDag-Erling Smørgrav 		return errno;
100*59c8e88eSDag-Erling Smørgrav 	if (outinfo) {
101*59c8e88eSDag-Erling Smørgrav 		ARRAYLIST_ADD(offp, outinfo->line_offsets);
102*59c8e88eSDag-Erling Smørgrav 		if (offp == NULL)
103*59c8e88eSDag-Erling Smørgrav 			return ENOMEM;
104*59c8e88eSDag-Erling Smørgrav 		*outoff += rc;
105*59c8e88eSDag-Erling Smørgrav 		*offp = *outoff;
106*59c8e88eSDag-Erling Smørgrav 	}
107*59c8e88eSDag-Erling Smørgrav 
108*59c8e88eSDag-Erling Smørgrav 	/*
109*59c8e88eSDag-Erling Smørgrav 	 * Now write out all the joined chunks.
110*59c8e88eSDag-Erling Smørgrav 	 *
111*59c8e88eSDag-Erling Smørgrav 	 * If the hunk denotes a change, it will come in the form of a deletion
112*59c8e88eSDag-Erling Smørgrav 	 * chunk followed by a addition chunk. Print a marker to break up the
113*59c8e88eSDag-Erling Smørgrav 	 * additions and deletions when this happens.
114*59c8e88eSDag-Erling Smørgrav 	 */
115*59c8e88eSDag-Erling Smørgrav 	int c_idx;
116*59c8e88eSDag-Erling Smørgrav 	for (c_idx = cc->chunk.start; !headers_only && c_idx < cc->chunk.end;
117*59c8e88eSDag-Erling Smørgrav 	    c_idx++) {
118*59c8e88eSDag-Erling Smørgrav 		const struct diff_chunk *c = &result->chunks.head[c_idx];
119*59c8e88eSDag-Erling Smørgrav 		if (c->left_count && !c->right_count)
120*59c8e88eSDag-Erling Smørgrav 			rc = diff_output_lines(outinfo, dest,
121*59c8e88eSDag-Erling Smørgrav 					  c->solved ? "< " : "?",
122*59c8e88eSDag-Erling Smørgrav 					  c->left_start, c->left_count);
123*59c8e88eSDag-Erling Smørgrav 		else if (c->right_count && !c->left_count) {
124*59c8e88eSDag-Erling Smørgrav 			if (change) {
125*59c8e88eSDag-Erling Smørgrav 				rc = fprintf(dest, "---\n");
126*59c8e88eSDag-Erling Smørgrav 				if (rc < 0)
127*59c8e88eSDag-Erling Smørgrav 					return errno;
128*59c8e88eSDag-Erling Smørgrav 				if (outinfo) {
129*59c8e88eSDag-Erling Smørgrav 					ARRAYLIST_ADD(offp,
130*59c8e88eSDag-Erling Smørgrav 					    outinfo->line_offsets);
131*59c8e88eSDag-Erling Smørgrav 					if (offp == NULL)
132*59c8e88eSDag-Erling Smørgrav 						return ENOMEM;
133*59c8e88eSDag-Erling Smørgrav 					*outoff += rc;
134*59c8e88eSDag-Erling Smørgrav 					*offp = *outoff;
135*59c8e88eSDag-Erling Smørgrav 				}
136*59c8e88eSDag-Erling Smørgrav 			}
137*59c8e88eSDag-Erling Smørgrav 			rc = diff_output_lines(outinfo, dest,
138*59c8e88eSDag-Erling Smørgrav 					  c->solved ? "> " : "?",
139*59c8e88eSDag-Erling Smørgrav 					  c->right_start, c->right_count);
140*59c8e88eSDag-Erling Smørgrav 		}
141*59c8e88eSDag-Erling Smørgrav 		if (rc)
142*59c8e88eSDag-Erling Smørgrav 			return rc;
143*59c8e88eSDag-Erling Smørgrav 		if (cc->chunk.end == result->chunks.len) {
144*59c8e88eSDag-Erling Smørgrav 			rc = diff_output_trailing_newline_msg(outinfo, dest, c);
145*59c8e88eSDag-Erling Smørgrav 			if (rc != DIFF_RC_OK)
146*59c8e88eSDag-Erling Smørgrav 				return rc;
147*59c8e88eSDag-Erling Smørgrav 		}
148*59c8e88eSDag-Erling Smørgrav 	}
149*59c8e88eSDag-Erling Smørgrav 
150*59c8e88eSDag-Erling Smørgrav 	return DIFF_RC_OK;
151*59c8e88eSDag-Erling Smørgrav }
152*59c8e88eSDag-Erling Smørgrav 
153*59c8e88eSDag-Erling Smørgrav int
154*59c8e88eSDag-Erling Smørgrav diff_output_plain(struct diff_output_info **output_info,
155*59c8e88eSDag-Erling Smørgrav     FILE *dest, const struct diff_input_info *info,
156*59c8e88eSDag-Erling Smørgrav     const struct diff_result *result, int hunk_headers_only)
157*59c8e88eSDag-Erling Smørgrav {
158*59c8e88eSDag-Erling Smørgrav 	struct diff_output_info *outinfo = NULL;
159*59c8e88eSDag-Erling Smørgrav 	struct diff_chunk_context cc = {};
160*59c8e88eSDag-Erling Smørgrav 	int atomizer_flags = (result->left->atomizer_flags|
161*59c8e88eSDag-Erling Smørgrav 	    result->right->atomizer_flags);
162*59c8e88eSDag-Erling Smørgrav 	int flags = (result->left->root->diff_flags |
163*59c8e88eSDag-Erling Smørgrav 	    result->right->root->diff_flags);
164*59c8e88eSDag-Erling Smørgrav 	bool force_text = (flags & DIFF_FLAG_FORCE_TEXT_DATA);
165*59c8e88eSDag-Erling Smørgrav 	bool have_binary = (atomizer_flags & DIFF_ATOMIZER_FOUND_BINARY_DATA);
166*59c8e88eSDag-Erling Smørgrav 	int i, rc;
167*59c8e88eSDag-Erling Smørgrav 	off_t outoff = 0, *offp;
168*59c8e88eSDag-Erling Smørgrav 
169*59c8e88eSDag-Erling Smørgrav 	if (!result)
170*59c8e88eSDag-Erling Smørgrav 		return EINVAL;
171*59c8e88eSDag-Erling Smørgrav 	if (result->rc != DIFF_RC_OK)
172*59c8e88eSDag-Erling Smørgrav 		return result->rc;
173*59c8e88eSDag-Erling Smørgrav 
174*59c8e88eSDag-Erling Smørgrav 	if (output_info) {
175*59c8e88eSDag-Erling Smørgrav 		*output_info = diff_output_info_alloc();
176*59c8e88eSDag-Erling Smørgrav 		if (*output_info == NULL)
177*59c8e88eSDag-Erling Smørgrav 			return ENOMEM;
178*59c8e88eSDag-Erling Smørgrav 		outinfo = *output_info;
179*59c8e88eSDag-Erling Smørgrav 	}
180*59c8e88eSDag-Erling Smørgrav 
181*59c8e88eSDag-Erling Smørgrav 	if (have_binary && !force_text) {
182*59c8e88eSDag-Erling Smørgrav 		for (i = 0; i < result->chunks.len; i++) {
183*59c8e88eSDag-Erling Smørgrav 			struct diff_chunk *c = &result->chunks.head[i];
184*59c8e88eSDag-Erling Smørgrav 			enum diff_chunk_type t = diff_chunk_type(c);
185*59c8e88eSDag-Erling Smørgrav 
186*59c8e88eSDag-Erling Smørgrav 			if (t != CHUNK_MINUS && t != CHUNK_PLUS)
187*59c8e88eSDag-Erling Smørgrav 				continue;
188*59c8e88eSDag-Erling Smørgrav 
189*59c8e88eSDag-Erling Smørgrav 			rc = fprintf(dest, "Binary files %s and %s differ\n",
190*59c8e88eSDag-Erling Smørgrav 			    diff_output_get_label_left(info),
191*59c8e88eSDag-Erling Smørgrav 			    diff_output_get_label_right(info));
192*59c8e88eSDag-Erling Smørgrav 			if (rc < 0)
193*59c8e88eSDag-Erling Smørgrav 				return errno;
194*59c8e88eSDag-Erling Smørgrav 			if (outinfo) {
195*59c8e88eSDag-Erling Smørgrav 				ARRAYLIST_ADD(offp, outinfo->line_offsets);
196*59c8e88eSDag-Erling Smørgrav 				if (offp == NULL)
197*59c8e88eSDag-Erling Smørgrav 					return ENOMEM;
198*59c8e88eSDag-Erling Smørgrav 				outoff += rc;
199*59c8e88eSDag-Erling Smørgrav 				*offp = outoff;
200*59c8e88eSDag-Erling Smørgrav 			}
201*59c8e88eSDag-Erling Smørgrav 			break;
202*59c8e88eSDag-Erling Smørgrav 		}
203*59c8e88eSDag-Erling Smørgrav 
204*59c8e88eSDag-Erling Smørgrav 		return DIFF_RC_OK;
205*59c8e88eSDag-Erling Smørgrav 	}
206*59c8e88eSDag-Erling Smørgrav 
207*59c8e88eSDag-Erling Smørgrav 	for (i = 0; i < result->chunks.len; i++) {
208*59c8e88eSDag-Erling Smørgrav 		struct diff_chunk *chunk = &result->chunks.head[i];
209*59c8e88eSDag-Erling Smørgrav 		enum diff_chunk_type t = diff_chunk_type(chunk);
210*59c8e88eSDag-Erling Smørgrav 		struct diff_chunk_context next;
211*59c8e88eSDag-Erling Smørgrav 
212*59c8e88eSDag-Erling Smørgrav 		if (t != CHUNK_MINUS && t != CHUNK_PLUS)
213*59c8e88eSDag-Erling Smørgrav 			continue;
214*59c8e88eSDag-Erling Smørgrav 
215*59c8e88eSDag-Erling Smørgrav 		if (diff_chunk_context_empty(&cc)) {
216*59c8e88eSDag-Erling Smørgrav 			/* Note down the start point, any number of subsequent
217*59c8e88eSDag-Erling Smørgrav 			 * chunks may be joined up to this chunk by being
218*59c8e88eSDag-Erling Smørgrav 			 * directly adjacent. */
219*59c8e88eSDag-Erling Smørgrav 			diff_chunk_context_get(&cc, result, i, 0);
220*59c8e88eSDag-Erling Smørgrav 			continue;
221*59c8e88eSDag-Erling Smørgrav 		}
222*59c8e88eSDag-Erling Smørgrav 
223*59c8e88eSDag-Erling Smørgrav 		/* There already is a previous chunk noted down for being
224*59c8e88eSDag-Erling Smørgrav 		 * printed. Does it join up with this one? */
225*59c8e88eSDag-Erling Smørgrav 		diff_chunk_context_get(&next, result, i, 0);
226*59c8e88eSDag-Erling Smørgrav 
227*59c8e88eSDag-Erling Smørgrav 		if (diff_chunk_contexts_touch(&cc, &next)) {
228*59c8e88eSDag-Erling Smørgrav 			/* This next context touches or overlaps the previous
229*59c8e88eSDag-Erling Smørgrav 			 * one, join. */
230*59c8e88eSDag-Erling Smørgrav 			diff_chunk_contexts_merge(&cc, &next);
231*59c8e88eSDag-Erling Smørgrav 			/* When we merge the last chunk we can end up with one
232*59c8e88eSDag-Erling Smørgrav 			 * hanging chunk and have to come back for it after the
233*59c8e88eSDag-Erling Smørgrav 			 * loop */
234*59c8e88eSDag-Erling Smørgrav 			continue;
235*59c8e88eSDag-Erling Smørgrav 		}
236*59c8e88eSDag-Erling Smørgrav 		rc = output_plain_chunk(outinfo, dest, info, result, &cc,
237*59c8e88eSDag-Erling Smørgrav 		    &outoff, hunk_headers_only);
238*59c8e88eSDag-Erling Smørgrav 		if (rc != DIFF_RC_OK)
239*59c8e88eSDag-Erling Smørgrav 			return rc;
240*59c8e88eSDag-Erling Smørgrav 		cc = next;
241*59c8e88eSDag-Erling Smørgrav 	}
242*59c8e88eSDag-Erling Smørgrav 	if (!diff_chunk_context_empty(&cc))
243*59c8e88eSDag-Erling Smørgrav 		return output_plain_chunk(outinfo, dest, info, result, &cc,
244*59c8e88eSDag-Erling Smørgrav 		    &outoff, hunk_headers_only);
245*59c8e88eSDag-Erling Smørgrav 	return DIFF_RC_OK;
246*59c8e88eSDag-Erling Smørgrav }
247