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
output_edscript_chunk(struct diff_output_info * outinfo,FILE * dest,const struct diff_input_info * info,const struct diff_result * result,struct diff_chunk_context * cc)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
diff_output_edscript(struct diff_output_info ** output_info,FILE * dest,const struct diff_input_info * info,const struct diff_result * result)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