xref: /freebsd/contrib/libdiff/lib/diff_output_edscript.c (revision 59c8e88e72633afbc47a4ace0d2170d00d51f7dc)
1*59c8e88eSDag-Erling Smørgrav /* Produce ed(1) script output from 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  * Copyright (c) 2020 Stefan Sperling <stsp@openbsd.org>
5*59c8e88eSDag-Erling Smørgrav  *
6*59c8e88eSDag-Erling Smørgrav  * Permission to use, copy, modify, and distribute this software for any
7*59c8e88eSDag-Erling Smørgrav  * purpose with or without fee is hereby granted, provided that the above
8*59c8e88eSDag-Erling Smørgrav  * copyright notice and this permission notice appear in all copies.
9*59c8e88eSDag-Erling Smørgrav  *
10*59c8e88eSDag-Erling Smørgrav  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11*59c8e88eSDag-Erling Smørgrav  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12*59c8e88eSDag-Erling Smørgrav  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13*59c8e88eSDag-Erling Smørgrav  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14*59c8e88eSDag-Erling Smørgrav  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15*59c8e88eSDag-Erling Smørgrav  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16*59c8e88eSDag-Erling Smørgrav  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*59c8e88eSDag-Erling Smørgrav  */
18*59c8e88eSDag-Erling Smørgrav 
19*59c8e88eSDag-Erling Smørgrav #include <errno.h>
20*59c8e88eSDag-Erling Smørgrav #include <stdint.h>
21*59c8e88eSDag-Erling Smørgrav #include <stdio.h>
22*59c8e88eSDag-Erling Smørgrav #include <stdlib.h>
23*59c8e88eSDag-Erling Smørgrav #include <stdbool.h>
24*59c8e88eSDag-Erling Smørgrav 
25*59c8e88eSDag-Erling Smørgrav #include <arraylist.h>
26*59c8e88eSDag-Erling Smørgrav #include <diff_main.h>
27*59c8e88eSDag-Erling Smørgrav #include <diff_output.h>
28*59c8e88eSDag-Erling Smørgrav 
29*59c8e88eSDag-Erling Smørgrav #include "diff_internal.h"
30*59c8e88eSDag-Erling Smørgrav 
31*59c8e88eSDag-Erling Smørgrav static int
32*59c8e88eSDag-Erling Smørgrav output_edscript_chunk(struct diff_output_info *outinfo,
33*59c8e88eSDag-Erling Smørgrav     FILE *dest, const struct diff_input_info *info,
34*59c8e88eSDag-Erling Smørgrav     const struct diff_result *result,
35*59c8e88eSDag-Erling Smørgrav     struct diff_chunk_context *cc)
36*59c8e88eSDag-Erling Smørgrav {
37*59c8e88eSDag-Erling Smørgrav 	off_t outoff = 0, *offp;
38*59c8e88eSDag-Erling Smørgrav 	int left_start, left_len, right_start, right_len;
39*59c8e88eSDag-Erling Smørgrav 	int rc;
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 		if (left_len == 1 && right_len == 1) {
85*59c8e88eSDag-Erling Smørgrav 			rc = fprintf(dest, "%dc%d\n", left_start, right_start);
86*59c8e88eSDag-Erling Smørgrav 		} else if (left_len == 1) {
87*59c8e88eSDag-Erling Smørgrav 			rc = fprintf(dest, "%dc%d,%d\n", left_start,
88*59c8e88eSDag-Erling Smørgrav 			    right_start, cc->right.end);
89*59c8e88eSDag-Erling Smørgrav 		} else if (right_len == 1) {
90*59c8e88eSDag-Erling Smørgrav 			rc = fprintf(dest, "%d,%dc%d\n", left_start,
91*59c8e88eSDag-Erling Smørgrav 			    cc->left.end, right_start);
92*59c8e88eSDag-Erling Smørgrav 		} else {
93*59c8e88eSDag-Erling Smørgrav 			rc = fprintf(dest, "%d,%dc%d,%d\n", left_start,
94*59c8e88eSDag-Erling Smørgrav 			    cc->left.end, right_start, cc->right.end);
95*59c8e88eSDag-Erling Smørgrav 		}
96*59c8e88eSDag-Erling Smørgrav 	}
97*59c8e88eSDag-Erling Smørgrav 	if (rc < 0)
98*59c8e88eSDag-Erling Smørgrav 		return errno;
99*59c8e88eSDag-Erling Smørgrav 	if (outinfo) {
100*59c8e88eSDag-Erling Smørgrav 		ARRAYLIST_ADD(offp, outinfo->line_offsets);
101*59c8e88eSDag-Erling Smørgrav 		if (offp == NULL)
102*59c8e88eSDag-Erling Smørgrav 			return ENOMEM;
103*59c8e88eSDag-Erling Smørgrav 		outoff += rc;
104*59c8e88eSDag-Erling Smørgrav 		*offp = outoff;
105*59c8e88eSDag-Erling Smørgrav 	}
106*59c8e88eSDag-Erling Smørgrav 
107*59c8e88eSDag-Erling Smørgrav 	return DIFF_RC_OK;
108*59c8e88eSDag-Erling Smørgrav }
109*59c8e88eSDag-Erling Smørgrav 
110*59c8e88eSDag-Erling Smørgrav int
111*59c8e88eSDag-Erling Smørgrav diff_output_edscript(struct diff_output_info **output_info,
112*59c8e88eSDag-Erling Smørgrav     FILE *dest, const struct diff_input_info *info,
113*59c8e88eSDag-Erling Smørgrav     const struct diff_result *result)
114*59c8e88eSDag-Erling Smørgrav {
115*59c8e88eSDag-Erling Smørgrav 	struct diff_output_info *outinfo = NULL;
116*59c8e88eSDag-Erling Smørgrav 	struct diff_chunk_context cc = {};
117*59c8e88eSDag-Erling Smørgrav 	int atomizer_flags = (result->left->atomizer_flags|
118*59c8e88eSDag-Erling Smørgrav 	    result->right->atomizer_flags);
119*59c8e88eSDag-Erling Smørgrav 	int flags = (result->left->root->diff_flags |
120*59c8e88eSDag-Erling Smørgrav 	    result->right->root->diff_flags);
121*59c8e88eSDag-Erling Smørgrav 	bool force_text = (flags & DIFF_FLAG_FORCE_TEXT_DATA);
122*59c8e88eSDag-Erling Smørgrav 	bool have_binary = (atomizer_flags & DIFF_ATOMIZER_FOUND_BINARY_DATA);
123*59c8e88eSDag-Erling Smørgrav 	int i, rc;
124*59c8e88eSDag-Erling Smørgrav 
125*59c8e88eSDag-Erling Smørgrav 	if (!result)
126*59c8e88eSDag-Erling Smørgrav 		return EINVAL;
127*59c8e88eSDag-Erling Smørgrav 	if (result->rc != DIFF_RC_OK)
128*59c8e88eSDag-Erling Smørgrav 		return result->rc;
129*59c8e88eSDag-Erling Smørgrav 
130*59c8e88eSDag-Erling Smørgrav 	if (output_info) {
131*59c8e88eSDag-Erling Smørgrav 		*output_info = diff_output_info_alloc();
132*59c8e88eSDag-Erling Smørgrav 		if (*output_info == NULL)
133*59c8e88eSDag-Erling Smørgrav 			return ENOMEM;
134*59c8e88eSDag-Erling Smørgrav 		outinfo = *output_info;
135*59c8e88eSDag-Erling Smørgrav 	}
136*59c8e88eSDag-Erling Smørgrav 
137*59c8e88eSDag-Erling Smørgrav 	if (have_binary && !force_text) {
138*59c8e88eSDag-Erling Smørgrav 		for (i = 0; i < result->chunks.len; i++) {
139*59c8e88eSDag-Erling Smørgrav 			struct diff_chunk *c = &result->chunks.head[i];
140*59c8e88eSDag-Erling Smørgrav 			enum diff_chunk_type t = diff_chunk_type(c);
141*59c8e88eSDag-Erling Smørgrav 
142*59c8e88eSDag-Erling Smørgrav 			if (t != CHUNK_MINUS && t != CHUNK_PLUS)
143*59c8e88eSDag-Erling Smørgrav 				continue;
144*59c8e88eSDag-Erling Smørgrav 
145*59c8e88eSDag-Erling Smørgrav 			fprintf(dest, "Binary files %s and %s differ\n",
146*59c8e88eSDag-Erling Smørgrav 			    diff_output_get_label_left(info),
147*59c8e88eSDag-Erling Smørgrav 			    diff_output_get_label_right(info));
148*59c8e88eSDag-Erling Smørgrav 			break;
149*59c8e88eSDag-Erling Smørgrav 		}
150*59c8e88eSDag-Erling Smørgrav 
151*59c8e88eSDag-Erling Smørgrav 		return DIFF_RC_OK;
152*59c8e88eSDag-Erling Smørgrav 	}
153*59c8e88eSDag-Erling Smørgrav 
154*59c8e88eSDag-Erling Smørgrav 	for (i = 0; i < result->chunks.len; i++) {
155*59c8e88eSDag-Erling Smørgrav 		struct diff_chunk *chunk = &result->chunks.head[i];
156*59c8e88eSDag-Erling Smørgrav 		enum diff_chunk_type t = diff_chunk_type(chunk);
157*59c8e88eSDag-Erling Smørgrav 		struct diff_chunk_context next;
158*59c8e88eSDag-Erling Smørgrav 
159*59c8e88eSDag-Erling Smørgrav 		if (t != CHUNK_MINUS && t != CHUNK_PLUS)
160*59c8e88eSDag-Erling Smørgrav 			continue;
161*59c8e88eSDag-Erling Smørgrav 
162*59c8e88eSDag-Erling Smørgrav 		if (diff_chunk_context_empty(&cc)) {
163*59c8e88eSDag-Erling Smørgrav 			/* Note down the start point, any number of subsequent
164*59c8e88eSDag-Erling Smørgrav 			 * chunks may be joined up to this chunk by being
165*59c8e88eSDag-Erling Smørgrav 			 * directly adjacent. */
166*59c8e88eSDag-Erling Smørgrav 			diff_chunk_context_get(&cc, result, i, 0);
167*59c8e88eSDag-Erling Smørgrav 			continue;
168*59c8e88eSDag-Erling Smørgrav 		}
169*59c8e88eSDag-Erling Smørgrav 
170*59c8e88eSDag-Erling Smørgrav 		/* There already is a previous chunk noted down for being
171*59c8e88eSDag-Erling Smørgrav 		 * printed. Does it join up with this one? */
172*59c8e88eSDag-Erling Smørgrav 		diff_chunk_context_get(&next, result, i, 0);
173*59c8e88eSDag-Erling Smørgrav 
174*59c8e88eSDag-Erling Smørgrav 		if (diff_chunk_contexts_touch(&cc, &next)) {
175*59c8e88eSDag-Erling Smørgrav 			/* This next context touches or overlaps the previous
176*59c8e88eSDag-Erling Smørgrav 			 * one, join. */
177*59c8e88eSDag-Erling Smørgrav 			diff_chunk_contexts_merge(&cc, &next);
178*59c8e88eSDag-Erling Smørgrav 			continue;
179*59c8e88eSDag-Erling Smørgrav 		}
180*59c8e88eSDag-Erling Smørgrav 
181*59c8e88eSDag-Erling Smørgrav 		rc = output_edscript_chunk(outinfo, dest, info, result, &cc);
182*59c8e88eSDag-Erling Smørgrav 		if (rc != DIFF_RC_OK)
183*59c8e88eSDag-Erling Smørgrav 			return rc;
184*59c8e88eSDag-Erling Smørgrav 		cc = next;
185*59c8e88eSDag-Erling Smørgrav 	}
186*59c8e88eSDag-Erling Smørgrav 
187*59c8e88eSDag-Erling Smørgrav 	if (!diff_chunk_context_empty(&cc))
188*59c8e88eSDag-Erling Smørgrav 		return output_edscript_chunk(outinfo, dest, info, result, &cc);
189*59c8e88eSDag-Erling Smørgrav 	return DIFF_RC_OK;
190*59c8e88eSDag-Erling Smørgrav }
191