Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/libdiff/lib/diff_output_plain.c
35065 views
1
/* Output all lines of a diff_result. */
2
/*
3
* Copyright (c) 2020 Neels Hofmeyr <[email protected]>
4
*
5
* Permission to use, copy, modify, and distribute this software for any
6
* purpose with or without fee is hereby granted, provided that the above
7
* copyright notice and this permission notice appear in all copies.
8
*
9
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16
*/
17
18
#include <errno.h>
19
#include <stdint.h>
20
#include <stdio.h>
21
#include <stdbool.h>
22
#include <stdlib.h>
23
24
#include <arraylist.h>
25
#include <diff_main.h>
26
#include <diff_output.h>
27
28
#include "diff_internal.h"
29
30
static int
31
output_plain_chunk(struct diff_output_info *outinfo,
32
FILE *dest, const struct diff_input_info *info,
33
const struct diff_result *result,
34
struct diff_chunk_context *cc, off_t *outoff, bool headers_only)
35
{
36
off_t *offp;
37
int left_start, left_len, right_start, right_len;
38
int rc;
39
bool change = false;
40
41
left_len = cc->left.end - cc->left.start;
42
if (left_len < 0)
43
return EINVAL;
44
else if (result->left->atoms.len == 0)
45
left_start = 0;
46
else if (left_len == 0 && cc->left.start > 0)
47
left_start = cc->left.start;
48
else if (cc->left.end > 0)
49
left_start = cc->left.start + 1;
50
else
51
left_start = cc->left.start;
52
53
right_len = cc->right.end - cc->right.start;
54
if (right_len < 0)
55
return EINVAL;
56
else if (result->right->atoms.len == 0)
57
right_start = 0;
58
else if (right_len == 0 && cc->right.start > 0)
59
right_start = cc->right.start;
60
else if (cc->right.end > 0)
61
right_start = cc->right.start + 1;
62
else
63
right_start = cc->right.start;
64
65
if (left_len == 0) {
66
/* addition */
67
if (right_len == 1) {
68
rc = fprintf(dest, "%da%d\n", left_start, right_start);
69
} else {
70
rc = fprintf(dest, "%da%d,%d\n", left_start,
71
right_start, cc->right.end);
72
}
73
} else if (right_len == 0) {
74
/* deletion */
75
if (left_len == 1) {
76
rc = fprintf(dest, "%dd%d\n", left_start,
77
right_start);
78
} else {
79
rc = fprintf(dest, "%d,%dd%d\n", left_start,
80
cc->left.end, right_start);
81
}
82
} else {
83
/* change */
84
change = true;
85
if (left_len == 1 && right_len == 1) {
86
rc = fprintf(dest, "%dc%d\n", left_start, right_start);
87
} else if (left_len == 1) {
88
rc = fprintf(dest, "%dc%d,%d\n", left_start,
89
right_start, cc->right.end);
90
} else if (right_len == 1) {
91
rc = fprintf(dest, "%d,%dc%d\n", left_start,
92
cc->left.end, right_start);
93
} else {
94
rc = fprintf(dest, "%d,%dc%d,%d\n", left_start,
95
cc->left.end, right_start, cc->right.end);
96
}
97
}
98
if (rc < 0)
99
return errno;
100
if (outinfo) {
101
ARRAYLIST_ADD(offp, outinfo->line_offsets);
102
if (offp == NULL)
103
return ENOMEM;
104
*outoff += rc;
105
*offp = *outoff;
106
}
107
108
/*
109
* Now write out all the joined chunks.
110
*
111
* If the hunk denotes a change, it will come in the form of a deletion
112
* chunk followed by a addition chunk. Print a marker to break up the
113
* additions and deletions when this happens.
114
*/
115
int c_idx;
116
for (c_idx = cc->chunk.start; !headers_only && c_idx < cc->chunk.end;
117
c_idx++) {
118
const struct diff_chunk *c = &result->chunks.head[c_idx];
119
if (c->left_count && !c->right_count)
120
rc = diff_output_lines(outinfo, dest,
121
c->solved ? "< " : "?",
122
c->left_start, c->left_count);
123
else if (c->right_count && !c->left_count) {
124
if (change) {
125
rc = fprintf(dest, "---\n");
126
if (rc < 0)
127
return errno;
128
if (outinfo) {
129
ARRAYLIST_ADD(offp,
130
outinfo->line_offsets);
131
if (offp == NULL)
132
return ENOMEM;
133
*outoff += rc;
134
*offp = *outoff;
135
}
136
}
137
rc = diff_output_lines(outinfo, dest,
138
c->solved ? "> " : "?",
139
c->right_start, c->right_count);
140
}
141
if (rc)
142
return rc;
143
if (cc->chunk.end == result->chunks.len) {
144
rc = diff_output_trailing_newline_msg(outinfo, dest, c);
145
if (rc != DIFF_RC_OK)
146
return rc;
147
}
148
}
149
150
return DIFF_RC_OK;
151
}
152
153
int
154
diff_output_plain(struct diff_output_info **output_info,
155
FILE *dest, const struct diff_input_info *info,
156
const struct diff_result *result, int hunk_headers_only)
157
{
158
struct diff_output_info *outinfo = NULL;
159
struct diff_chunk_context cc = {};
160
int atomizer_flags = (result->left->atomizer_flags|
161
result->right->atomizer_flags);
162
int flags = (result->left->root->diff_flags |
163
result->right->root->diff_flags);
164
bool force_text = (flags & DIFF_FLAG_FORCE_TEXT_DATA);
165
bool have_binary = (atomizer_flags & DIFF_ATOMIZER_FOUND_BINARY_DATA);
166
int i, rc;
167
off_t outoff = 0, *offp;
168
169
if (!result)
170
return EINVAL;
171
if (result->rc != DIFF_RC_OK)
172
return result->rc;
173
174
if (output_info) {
175
*output_info = diff_output_info_alloc();
176
if (*output_info == NULL)
177
return ENOMEM;
178
outinfo = *output_info;
179
}
180
181
if (have_binary && !force_text) {
182
for (i = 0; i < result->chunks.len; i++) {
183
struct diff_chunk *c = &result->chunks.head[i];
184
enum diff_chunk_type t = diff_chunk_type(c);
185
186
if (t != CHUNK_MINUS && t != CHUNK_PLUS)
187
continue;
188
189
rc = fprintf(dest, "Binary files %s and %s differ\n",
190
diff_output_get_label_left(info),
191
diff_output_get_label_right(info));
192
if (rc < 0)
193
return errno;
194
if (outinfo) {
195
ARRAYLIST_ADD(offp, outinfo->line_offsets);
196
if (offp == NULL)
197
return ENOMEM;
198
outoff += rc;
199
*offp = outoff;
200
}
201
break;
202
}
203
204
return DIFF_RC_OK;
205
}
206
207
for (i = 0; i < result->chunks.len; i++) {
208
struct diff_chunk *chunk = &result->chunks.head[i];
209
enum diff_chunk_type t = diff_chunk_type(chunk);
210
struct diff_chunk_context next;
211
212
if (t != CHUNK_MINUS && t != CHUNK_PLUS)
213
continue;
214
215
if (diff_chunk_context_empty(&cc)) {
216
/* Note down the start point, any number of subsequent
217
* chunks may be joined up to this chunk by being
218
* directly adjacent. */
219
diff_chunk_context_get(&cc, result, i, 0);
220
continue;
221
}
222
223
/* There already is a previous chunk noted down for being
224
* printed. Does it join up with this one? */
225
diff_chunk_context_get(&next, result, i, 0);
226
227
if (diff_chunk_contexts_touch(&cc, &next)) {
228
/* This next context touches or overlaps the previous
229
* one, join. */
230
diff_chunk_contexts_merge(&cc, &next);
231
/* When we merge the last chunk we can end up with one
232
* hanging chunk and have to come back for it after the
233
* loop */
234
continue;
235
}
236
rc = output_plain_chunk(outinfo, dest, info, result, &cc,
237
&outoff, hunk_headers_only);
238
if (rc != DIFF_RC_OK)
239
return rc;
240
cc = next;
241
}
242
if (!diff_chunk_context_empty(&cc))
243
return output_plain_chunk(outinfo, dest, info, result, &cc,
244
&outoff, hunk_headers_only);
245
return DIFF_RC_OK;
246
}
247
248