Blob


1 /*
2 * Copyright (c) 2020 Neels Hofmeyr <neels@hofmeyr.de>
3 * Copyright (c) 2020 Stefan Sperling <stsp@openbsd.org>
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 */
18 #include <sys/mman.h>
19 #include <sys/stat.h>
20 #include <sys/queue.h>
22 #include <errno.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
27 #include "got_object.h"
28 #include "got_opentemp.h"
29 #include "got_error.h"
31 #include "got_lib_diff.h"
33 const struct diff_algo_config myers_then_patience;
34 const struct diff_algo_config myers_then_myers_divide;
35 const struct diff_algo_config patience;
36 const struct diff_algo_config myers_divide;
38 const struct diff_algo_config myers_then_patience = (struct diff_algo_config){
39 .impl = diff_algo_myers,
40 .permitted_state_size = 1024 * 1024 * sizeof(int),
41 .fallback_algo = &patience,
42 };
44 const struct diff_algo_config myers_then_myers_divide =
45 (struct diff_algo_config){
46 .impl = diff_algo_myers,
47 .permitted_state_size = 1024 * 1024 * sizeof(int),
48 .fallback_algo = &myers_divide,
49 };
51 const struct diff_algo_config patience = (struct diff_algo_config){
52 .impl = diff_algo_patience,
53 /* After subdivision, do Patience again: */
54 .inner_algo = &patience,
55 /* If subdivision failed, do Myers Divide et Impera: */
56 .fallback_algo = &myers_then_myers_divide,
57 };
59 const struct diff_algo_config myers_divide = (struct diff_algo_config){
60 .impl = diff_algo_myers_divide,
61 /* When division succeeded, start from the top: */
62 .inner_algo = &myers_then_myers_divide,
63 /* (fallback_algo = NULL implies diff_algo_none). */
64 };
66 /* If the state for a forward-Myers is small enough, use Myers, otherwise first
67 * do a Myers-divide. */
68 const struct diff_config diff_config_myers_then_myers_divide = {
69 .atomize_func = diff_atomize_text_by_line,
70 .algo = &myers_then_myers_divide,
71 };
73 /* If the state for a forward-Myers is small enough, use Myers, otherwise first
74 * do a Patience. */
75 const struct diff_config diff_config_myers_then_patience = {
76 .atomize_func = diff_atomize_text_by_line,
77 .algo = &myers_then_patience,
78 };
80 /* Directly force Patience as a first divider of the source file. */
81 const struct diff_config diff_config_patience = {
82 .atomize_func = diff_atomize_text_by_line,
83 .algo = &patience,
84 };
86 /* Directly force Patience as a first divider of the source file. */
87 const struct diff_config diff_config_no_algo = {
88 .atomize_func = diff_atomize_text_by_line,
89 };
91 const struct got_error *
92 got_diffreg_close(FILE *f1, char *p1, size_t size1,
93 FILE *f2, char *p2, size_t size2)
94 {
95 const struct got_error *err = NULL;
97 if (p1 && munmap(p1, size1) == -1 && err == NULL)
98 err = got_error_from_errno("munmap");
99 if (p2 && munmap(p2, size2) == -1 && err == NULL)
100 err = got_error_from_errno("munmap");
101 if (f1 && fclose(f1) != 0 && err == NULL)
102 err = got_error_from_errno("fclose");
103 if (f2 && fclose(f2) != 0 && err == NULL)
104 err = got_error_from_errno("fclose");
105 return err;
108 const struct diff_config *
109 got_diff_get_config(enum got_diff_algorithm algorithm)
111 switch (algorithm) {
112 case GOT_DIFF_ALGORITHM_PATIENCE:
113 return &diff_config_patience;
114 case GOT_DIFF_ALGORITHM_MYERS:
115 return &diff_config_myers_then_myers_divide;
117 return NULL; /* should not happen */
120 const struct got_error *
121 got_diff_prepare_file(FILE **f, char **p, int *f_created, size_t *size,
122 struct diff_data *diff_data, const struct diff_config *cfg,
123 int ignore_whitespace)
125 const struct got_error *err = NULL;
126 struct stat st;
127 int diff_flags = 0, rc;
129 *size = 0;
131 diff_flags |= DIFF_FLAG_SHOW_PROTOTYPES;
132 if (ignore_whitespace)
133 diff_flags |= DIFF_FLAG_IGNORE_WHITESPACE;
135 if (f && *f) {
136 if (fstat(fileno(*f), &st) == -1) {
137 err = got_error_from_errno("fstat");
138 goto done;
140 #ifndef GOT_DIFF_NO_MMAP
141 *p = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE,
142 fileno(*f), 0);
143 if (*p == MAP_FAILED)
144 #endif
145 *p = NULL; /* fall back on file I/O */
146 } else {
147 *f_created = 1;
148 st.st_size = 0;
149 *f = got_opentemp();
150 if (*f == NULL) {
151 err = got_error_from_errno("got_opentemp");
152 goto done;
156 rc = diff_atomize_file(diff_data, cfg, *f, *p, st.st_size, diff_flags);
157 if (rc) {
158 err = got_error_set_errno(rc, "diff_atomize_file");
159 goto done;
161 done:
162 if (err)
163 diff_data_free(diff_data);
164 else
165 *size = st.st_size;
166 return err;
169 const struct got_error *
170 got_diffreg_prepared_files(struct got_diffreg_result **diffreg_result,
171 const struct diff_config *cfg,
172 struct diff_data *left, FILE *f1, char *p1, size_t size1,
173 struct diff_data *right, FILE *f2, char *p2, size_t size2)
175 const struct got_error *err = NULL;
176 struct diff_result *diff_result;
178 *diffreg_result = calloc(1, sizeof(**diffreg_result));
179 if (*diffreg_result == NULL)
180 return got_error_from_errno("calloc");
182 diff_result = diff_main(cfg, left, right);
183 if (diff_result == NULL) {
184 err = got_error_set_errno(ENOMEM, "malloc");
185 goto done;
187 if (diff_result->rc != DIFF_RC_OK) {
188 err = got_error_set_errno(diff_result->rc, "diff");
189 goto done;
192 (*diffreg_result)->result = diff_result;
193 (*diffreg_result)->f1 = f1;
194 (*diffreg_result)->map1 = p1;
195 (*diffreg_result)->size1 = size1;
196 (*diffreg_result)->f2 = f2;
197 (*diffreg_result)->map2 = p2;
198 (*diffreg_result)->size2 = size2;
199 done:
200 if (err) {
201 if (diffreg_result) {
202 free(*diffreg_result);
203 *diffreg_result = NULL;
207 return err;
210 const struct got_error *
211 got_diffreg(struct got_diffreg_result **diffreg_result, FILE *f1, FILE *f2,
212 enum got_diff_algorithm algorithm, int ignore_whitespace)
214 const struct got_error *err = NULL;
215 const struct diff_config *cfg;
216 char *p1 = NULL, *p2 = NULL;
217 int f1_created = 0, f2_created = 0;
218 size_t size1, size2;
219 struct diff_data d_left, d_right;
220 struct diff_data *left, *right;
221 struct diff_result *diff_result;
223 if (diffreg_result) {
224 *diffreg_result = calloc(1, sizeof(**diffreg_result));
225 if (*diffreg_result == NULL)
226 return got_error_from_errno("calloc");
227 left = &(*diffreg_result)->left;
228 right = &(*diffreg_result)->right;
229 } else {
230 memset(&d_left, 0, sizeof(d_left));
231 memset(&d_right, 0, sizeof(d_right));
232 left = &d_left;
233 right = &d_right;
236 cfg = got_diff_get_config(algorithm);
237 if (cfg == NULL) {
238 err = got_error(GOT_ERR_NOT_IMPL);
239 goto done;
242 err = got_diff_prepare_file(&f1, &p1, &f1_created, &size1,
243 left, cfg, ignore_whitespace);
244 if (err)
245 goto done;
247 err = got_diff_prepare_file(&f2, &p2, &f2_created, &size2,
248 right, cfg, ignore_whitespace);
249 if (err)
250 goto done;
252 diff_result = diff_main(cfg, left, right);
253 if (diff_result == NULL) {
254 err = got_error_set_errno(ENOMEM, "malloc");
255 goto done;
257 if (diff_result->rc != DIFF_RC_OK) {
258 err = got_error_set_errno(diff_result->rc, "diff");
259 goto done;
262 if (diffreg_result) {
263 (*diffreg_result)->result = diff_result;
264 if (f1_created)
265 (*diffreg_result)->f1 = f1;
266 (*diffreg_result)->map1 = p1;
267 (*diffreg_result)->size1 = size1;
268 if (f2_created)
269 (*diffreg_result)->f2 = f2;
270 (*diffreg_result)->map2 = p2;
271 (*diffreg_result)->size2 = size2;
273 done:
274 if (diffreg_result == NULL) {
275 diff_data_free(left);
276 diff_data_free(right);
278 if (err) {
279 got_diffreg_close(f1_created ? f1 : NULL, p1, size1,
280 f2_created ? f2 : NULL, p2, size2);
281 if (diffreg_result) {
282 diff_data_free(left);
283 diff_data_free(right);
284 free(*diffreg_result);
285 *diffreg_result = NULL;
289 return err;
292 const struct got_error *
293 got_diffreg_output(off_t **line_offsets, size_t *nlines,
294 struct got_diffreg_result *diff_result, FILE *f1, FILE *f2,
295 const char *path1, const char *path2,
296 enum got_diff_output_format output_format, int context_lines, FILE *outfile)
298 struct diff_input_info info = {
299 .left_path = path1,
300 .right_path = path2,
301 };
302 int rc;
303 struct diff_output_info *output_info;
305 switch (output_format) {
306 case GOT_DIFF_OUTPUT_UNIDIFF:
307 rc = diff_output_unidiff(
308 line_offsets ? &output_info : NULL, outfile, &info,
309 diff_result->result, context_lines);
310 if (rc != DIFF_RC_OK)
311 return got_error_set_errno(rc, "diff_output_unidiff");
312 break;
313 case GOT_DIFF_OUTPUT_EDSCRIPT:
314 rc = diff_output_edscript(line_offsets ? &output_info : NULL,
315 outfile, &info, diff_result->result);
316 if (rc != DIFF_RC_OK)
317 return got_error_set_errno(rc, "diff_output_edscript");
318 break;
322 if (line_offsets && *line_offsets) {
323 if (output_info->line_offsets.len > 0) {
324 off_t prev_offset = 0, *p, *o;
325 int i, len;
326 if (*nlines > 0) {
327 prev_offset = (*line_offsets)[*nlines - 1];
328 /*
329 * First line offset is always zero. Skip it
330 * when appending to a pre-populated array.
331 */
332 o = &output_info->line_offsets.head[1];
333 len = output_info->line_offsets.len - 1;
334 } else {
335 o = &output_info->line_offsets.head[0];
336 len = output_info->line_offsets.len;
338 p = reallocarray(*line_offsets, *nlines + len,
339 sizeof(off_t));
340 if (p == NULL)
341 return got_error_from_errno("calloc");
342 for (i = 0; i < len; i++)
343 p[*nlines + i] = o[i] + prev_offset;
344 *line_offsets = p;
345 *nlines += len;
347 diff_output_info_free(output_info);
350 return NULL;
353 const struct got_error *
354 got_diffreg_result_free(struct got_diffreg_result *diffreg_result)
356 const struct got_error *err;
358 diff_result_free(diffreg_result->result);
359 diff_data_free(&diffreg_result->left);
360 diff_data_free(&diffreg_result->right);
361 err = got_diffreg_close(diffreg_result->f1, diffreg_result->map1,
362 diffreg_result->size1, diffreg_result->f2,
363 diffreg_result->map2, diffreg_result->size2);
364 free(diffreg_result);
365 return err;
368 const struct got_error *
369 got_diffreg_result_free_left(struct got_diffreg_result *diffreg_result)
371 diff_data_free(&diffreg_result->left);
372 memset(&diffreg_result->left, 0, sizeof(diffreg_result->left));
373 return got_diffreg_close(diffreg_result->f1, diffreg_result->map1,
374 diffreg_result->size1, NULL, NULL, 0);
377 const struct got_error *
378 got_diffreg_result_free_right(struct got_diffreg_result *diffreg_result)
380 diff_data_free(&diffreg_result->right);
381 memset(&diffreg_result->right, 0, sizeof(diffreg_result->right));
382 return got_diffreg_close(NULL, NULL, 0, diffreg_result->f2,
383 diffreg_result->map2, diffreg_result->size2);
386 void
387 got_diff_dump_change(FILE *outfile, struct diff_chunk *change,
388 FILE *f1, FILE *f2)