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
output_plain_chunk(struct diff_output_info * outinfo,FILE * dest,const struct diff_input_info * info,const struct diff_result * result,struct diff_chunk_context * cc,off_t * outoff,bool headers_only)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
diff_output_plain(struct diff_output_info ** output_info,FILE * dest,const struct diff_input_info * info,const struct diff_result * result,int hunk_headers_only)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