Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/libdiff/lib/diff_output.c
35065 views
1
/* Common parts for printing diff output */
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 <ctype.h>
19
#include <errno.h>
20
#include <stdbool.h>
21
#include <stdint.h>
22
#include <stdio.h>
23
#include <stdlib.h>
24
#include <string.h>
25
#include <unistd.h>
26
27
#include <arraylist.h>
28
#include <diff_main.h>
29
#include <diff_output.h>
30
31
#include "diff_internal.h"
32
33
static bool color;
34
static const char *del_code = "31";
35
static const char *add_code = "32";
36
37
void
38
diff_output_set_colors(bool _color,
39
const char *_del_code,
40
const char *_add_code)
41
{
42
color = _color;
43
if (_del_code)
44
del_code = _del_code;
45
if (_add_code)
46
add_code = _add_code;
47
}
48
49
static int
50
get_atom_byte(int *ch, struct diff_atom *atom, off_t off)
51
{
52
off_t cur;
53
54
if (atom->at != NULL) {
55
*ch = atom->at[off];
56
return 0;
57
}
58
59
cur = ftello(atom->root->f);
60
if (cur == -1)
61
return errno;
62
63
if (cur != atom->pos + off &&
64
fseeko(atom->root->f, atom->pos + off, SEEK_SET) == -1)
65
return errno;
66
67
*ch = fgetc(atom->root->f);
68
if (*ch == EOF && ferror(atom->root->f))
69
return errno;
70
71
return 0;
72
}
73
74
#define DIFF_OUTPUT_BUF_SIZE 512
75
76
int
77
diff_output_lines(struct diff_output_info *outinfo, FILE *dest,
78
const char *prefix, struct diff_atom *start_atom,
79
unsigned int count)
80
{
81
struct diff_atom *atom;
82
off_t outoff = 0, *offp;
83
uint8_t *typep;
84
int rc;
85
bool colored;
86
87
if (outinfo && outinfo->line_offsets.len > 0) {
88
unsigned int idx = outinfo->line_offsets.len - 1;
89
outoff = outinfo->line_offsets.head[idx];
90
}
91
92
if (color) {
93
colored = true;
94
if (*prefix == '-' || *prefix == '<')
95
printf("\033[%sm", del_code);
96
else if (*prefix == '+' || *prefix == '>')
97
printf("\033[%sm", add_code);
98
else
99
colored = false;
100
} else {
101
colored = false;
102
}
103
104
foreach_diff_atom(atom, start_atom, count) {
105
off_t outlen = 0;
106
int i, ch, nbuf = 0;
107
size_t len = atom->len, wlen;
108
char buf[DIFF_OUTPUT_BUF_SIZE + 1 /* '\n' */];
109
size_t n;
110
111
n = strlcpy(buf, prefix, sizeof(buf));
112
if (n >= DIFF_OUTPUT_BUF_SIZE) { /* leave room for '\n' */
113
rc = ENOBUFS;
114
goto out;
115
}
116
nbuf += n;
117
118
if (len) {
119
rc = get_atom_byte(&ch, atom, len - 1);
120
if (rc)
121
goto out;
122
if (ch == '\n')
123
len--;
124
}
125
126
for (i = 0; i < len; i++) {
127
rc = get_atom_byte(&ch, atom, i);
128
if (rc)
129
goto out;
130
if (nbuf >= DIFF_OUTPUT_BUF_SIZE) {
131
wlen = fwrite(buf, 1, nbuf, dest);
132
if (wlen != nbuf) {
133
rc = errno;
134
goto out;
135
}
136
outlen += wlen;
137
nbuf = 0;
138
}
139
buf[nbuf++] = ch;
140
}
141
buf[nbuf++] = '\n';
142
wlen = fwrite(buf, 1, nbuf, dest);
143
if (wlen != nbuf) {
144
rc = errno;
145
goto out;
146
}
147
outlen += wlen;
148
if (outinfo) {
149
ARRAYLIST_ADD(offp, outinfo->line_offsets);
150
if (offp == NULL) {
151
rc = ENOMEM;
152
goto out;
153
}
154
outoff += outlen;
155
*offp = outoff;
156
ARRAYLIST_ADD(typep, outinfo->line_types);
157
if (typep == NULL) {
158
rc = ENOMEM;
159
goto out;
160
}
161
*typep = *prefix == ' ' ? DIFF_LINE_CONTEXT :
162
*prefix == '-' ? DIFF_LINE_MINUS :
163
*prefix == '+' ? DIFF_LINE_PLUS : DIFF_LINE_NONE;
164
}
165
}
166
167
rc = DIFF_RC_OK;
168
out:
169
if (colored)
170
printf("\033[m");
171
return rc;
172
}
173
174
int
175
diff_output_chunk_left_version(struct diff_output_info **output_info,
176
FILE *dest,
177
const struct diff_input_info *info,
178
const struct diff_result *result,
179
const struct diff_chunk_context *cc)
180
{
181
int rc, c_idx;
182
struct diff_output_info *outinfo = NULL;
183
184
if (diff_range_empty(&cc->left))
185
return DIFF_RC_OK;
186
187
if (output_info) {
188
*output_info = diff_output_info_alloc();
189
if (*output_info == NULL)
190
return ENOMEM;
191
outinfo = *output_info;
192
}
193
194
/* Write out all chunks on the left side. */
195
for (c_idx = cc->chunk.start; c_idx < cc->chunk.end; c_idx++) {
196
const struct diff_chunk *c = &result->chunks.head[c_idx];
197
198
if (c->left_count) {
199
rc = diff_output_lines(outinfo, dest, "",
200
c->left_start, c->left_count);
201
if (rc)
202
return rc;
203
}
204
}
205
206
return DIFF_RC_OK;
207
}
208
209
int
210
diff_output_chunk_right_version(struct diff_output_info **output_info,
211
FILE *dest,
212
const struct diff_input_info *info,
213
const struct diff_result *result,
214
const struct diff_chunk_context *cc)
215
{
216
int rc, c_idx;
217
struct diff_output_info *outinfo = NULL;
218
219
if (diff_range_empty(&cc->right))
220
return DIFF_RC_OK;
221
222
if (output_info) {
223
*output_info = diff_output_info_alloc();
224
if (*output_info == NULL)
225
return ENOMEM;
226
outinfo = *output_info;
227
}
228
229
/* Write out all chunks on the right side. */
230
for (c_idx = cc->chunk.start; c_idx < cc->chunk.end; c_idx++) {
231
const struct diff_chunk *c = &result->chunks.head[c_idx];
232
233
if (c->right_count) {
234
rc = diff_output_lines(outinfo, dest, "", c->right_start,
235
c->right_count);
236
if (rc)
237
return rc;
238
}
239
}
240
241
return DIFF_RC_OK;
242
}
243
244
int
245
diff_output_trailing_newline_msg(struct diff_output_info *outinfo, FILE *dest,
246
const struct diff_chunk *c)
247
{
248
enum diff_chunk_type chunk_type = diff_chunk_type(c);
249
struct diff_atom *atom, *start_atom;
250
unsigned int atom_count;
251
int rc, ch;
252
off_t outoff = 0, *offp;
253
uint8_t *typep;
254
255
256
if (chunk_type == CHUNK_MINUS || chunk_type == CHUNK_SAME) {
257
start_atom = c->left_start;
258
atom_count = c->left_count;
259
} else if (chunk_type == CHUNK_PLUS) {
260
start_atom = c->right_start;
261
atom_count = c->right_count;
262
} else
263
return EINVAL;
264
265
/* Locate the last atom. */
266
if (atom_count == 0)
267
return EINVAL;
268
atom = &start_atom[atom_count - 1];
269
270
rc = get_atom_byte(&ch, atom, atom->len - 1);
271
if (rc != DIFF_RC_OK)
272
return rc;
273
274
if (ch != '\n') {
275
if (outinfo && outinfo->line_offsets.len > 0) {
276
unsigned int idx = outinfo->line_offsets.len - 1;
277
outoff = outinfo->line_offsets.head[idx];
278
}
279
rc = fprintf(dest, "\\ No newline at end of file\n");
280
if (rc < 0)
281
return errno;
282
if (outinfo) {
283
ARRAYLIST_ADD(offp, outinfo->line_offsets);
284
if (offp == NULL)
285
return ENOMEM;
286
outoff += rc;
287
*offp = outoff;
288
ARRAYLIST_ADD(typep, outinfo->line_types);
289
if (typep == NULL)
290
return ENOMEM;
291
*typep = DIFF_LINE_NONE;
292
}
293
}
294
295
return DIFF_RC_OK;
296
}
297
298
static bool
299
is_function_prototype(char ch)
300
{
301
return (isalpha((unsigned char)ch) || ch == '_' || ch == '$' ||
302
ch == '-' || ch == '+');
303
}
304
305
#define begins_with(s, pre) (strncmp(s, pre, sizeof(pre)-1) == 0)
306
307
int
308
diff_output_match_function_prototype(char *prototype, size_t prototype_size,
309
int *last_prototype_idx, const struct diff_result *result,
310
int chunk_start_line)
311
{
312
struct diff_atom *start_atom, *atom;
313
const struct diff_data *data;
314
char buf[DIFF_FUNCTION_CONTEXT_SIZE];
315
const char *state = NULL;
316
int rc, i, ch;
317
318
if (result->left->atoms.len > 0 && chunk_start_line > 0) {
319
data = result->left;
320
start_atom = &data->atoms.head[chunk_start_line - 1];
321
} else
322
return DIFF_RC_OK;
323
324
diff_data_foreach_atom_backwards_from(start_atom, atom, data) {
325
int atom_idx = diff_atom_root_idx(data, atom);
326
if (atom_idx < *last_prototype_idx)
327
break;
328
rc = get_atom_byte(&ch, atom, 0);
329
if (rc)
330
return rc;
331
buf[0] = ch;
332
if (!is_function_prototype(buf[0]))
333
continue;
334
for (i = 1; i < atom->len && i < sizeof(buf) - 1; i++) {
335
rc = get_atom_byte(&ch, atom, i);
336
if (rc)
337
return rc;
338
if (ch == '\n')
339
break;
340
buf[i] = ch;
341
}
342
buf[i] = '\0';
343
if (begins_with(buf, "private:")) {
344
if (!state)
345
state = " (private)";
346
} else if (begins_with(buf, "protected:")) {
347
if (!state)
348
state = " (protected)";
349
} else if (begins_with(buf, "public:")) {
350
if (!state)
351
state = " (public)";
352
} else {
353
if (state) /* don't care about truncation */
354
strlcat(buf, state, sizeof(buf));
355
strlcpy(prototype, buf, prototype_size);
356
break;
357
}
358
}
359
360
*last_prototype_idx = diff_atom_root_idx(data, start_atom);
361
return DIFF_RC_OK;
362
}
363
364
struct diff_output_info *
365
diff_output_info_alloc(void)
366
{
367
struct diff_output_info *output_info;
368
off_t *offp;
369
uint8_t *typep;
370
371
output_info = malloc(sizeof(*output_info));
372
if (output_info != NULL) {
373
ARRAYLIST_INIT(output_info->line_offsets, 128);
374
ARRAYLIST_ADD(offp, output_info->line_offsets);
375
if (offp == NULL) {
376
diff_output_info_free(output_info);
377
return NULL;
378
}
379
*offp = 0;
380
ARRAYLIST_INIT(output_info->line_types, 128);
381
ARRAYLIST_ADD(typep, output_info->line_types);
382
if (typep == NULL) {
383
diff_output_info_free(output_info);
384
return NULL;
385
}
386
*typep = DIFF_LINE_NONE;
387
}
388
return output_info;
389
}
390
391
void
392
diff_output_info_free(struct diff_output_info *output_info)
393
{
394
ARRAYLIST_FREE(output_info->line_offsets);
395
ARRAYLIST_FREE(output_info->line_types);
396
free(output_info);
397
}
398
399
const char *
400
diff_output_get_label_left(const struct diff_input_info *info)
401
{
402
if (info->flags & DIFF_INPUT_LEFT_NONEXISTENT)
403
return "/dev/null";
404
405
return info->left_path ? info->left_path : "a";
406
}
407
408
const char *
409
diff_output_get_label_right(const struct diff_input_info *info)
410
{
411
if (info->flags & DIFF_INPUT_RIGHT_NONEXISTENT)
412
return "/dev/null";
413
414
return info->right_path ? info->right_path : "b";
415
}
416
417