clang -cc1 -cc1 -triple amd64-unknown-openbsd6.9 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name diff.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/local/lib/clang/11.1.0 -I /home/ben/Projects/got/tog/../include -I /home/ben/Projects/got/tog/../lib -D GOT_LIBEXECDIR=/home/ben/bin -D GOT_VERSION=0.53-current -internal-isystem /usr/local/lib/clang/11.1.0/include -internal-externc-isystem /usr/include -O0 -fdebug-compilation-dir /home/ben/Projects/got/tog/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fgnuc-version=4.2.1 -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -o /home/ben/Projects/got/scan/2021-05-28-230913-68537-1 -x c /home/ben/Projects/got/tog/../lib/diff.c
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | #include <sys/queue.h> |
18 | #include <sys/stat.h> |
19 | |
20 | #include <stdio.h> |
21 | #include <stdlib.h> |
22 | #include <string.h> |
23 | #include <limits.h> |
24 | #include <sha1.h> |
25 | #include <zlib.h> |
26 | |
27 | #include "got_object.h" |
28 | #include "got_repository.h" |
29 | #include "got_error.h" |
30 | #include "got_diff.h" |
31 | #include "got_opentemp.h" |
32 | #include "got_path.h" |
33 | #include "got_cancel.h" |
34 | #include "got_worktree.h" |
35 | |
36 | #include "got_lib_diff.h" |
37 | #include "got_lib_delta.h" |
38 | #include "got_lib_inflate.h" |
39 | #include "got_lib_object.h" |
40 | |
41 | static const struct got_error * |
42 | add_line_offset(off_t **line_offsets, size_t *nlines, off_t off) |
43 | { |
44 | off_t *p; |
45 | |
46 | p = reallocarray(*line_offsets, *nlines + 1, sizeof(off_t)); |
47 | if (p == NULL) |
48 | return got_error_from_errno("reallocarray"); |
49 | *line_offsets = p; |
50 | (*line_offsets)[*nlines] = off; |
51 | (*nlines)++; |
52 | return NULL; |
53 | } |
54 | |
55 | static const struct got_error * |
56 | diff_blobs(off_t **line_offsets, size_t *nlines, |
57 | struct got_diffreg_result **resultp, struct got_blob_object *blob1, |
58 | struct got_blob_object *blob2, |
59 | const char *label1, const char *label2, mode_t mode1, mode_t mode2, |
60 | int diff_context, int ignore_whitespace, int force_text_diff, FILE *outfile) |
61 | { |
62 | const struct got_error *err = NULL, *free_err; |
63 | FILE *f1 = NULL, *f2 = NULL; |
64 | char hex1[SHA1_DIGEST_STRING_LENGTH]; |
65 | char hex2[SHA1_DIGEST_STRING_LENGTH]; |
66 | char *idstr1 = NULL, *idstr2 = NULL; |
67 | off_t size1, size2; |
68 | struct got_diffreg_result *result; |
69 | off_t outoff = 0; |
70 | int n; |
71 | |
72 | if (line_offsets && *line_offsets && *nlines > 0) |
73 | outoff = (*line_offsets)[*nlines - 1]; |
74 | else if (line_offsets) { |
75 | err = add_line_offset(line_offsets, nlines, 0); |
76 | if (err) |
77 | goto done; |
78 | } |
79 | |
80 | if (resultp) |
81 | *resultp = NULL; |
82 | |
83 | if (blob1) { |
84 | f1 = got_opentemp(); |
85 | if (f1 == NULL) |
86 | return got_error_from_errno("got_opentemp"); |
87 | } |
88 | |
89 | if (blob2) { |
90 | f2 = got_opentemp(); |
91 | if (f2 == NULL) { |
92 | err = got_error_from_errno("got_opentemp"); |
93 | if (f1) |
94 | fclose(f1); |
95 | return err; |
96 | } |
97 | } |
98 | |
99 | size1 = 0; |
100 | if (blob1) { |
101 | idstr1 = got_object_blob_id_str(blob1, hex1, sizeof(hex1)); |
102 | err = got_object_blob_dump_to_file(&size1, NULL, NULL, f1, |
103 | blob1); |
104 | if (err) |
105 | goto done; |
106 | } else |
107 | idstr1 = "/dev/null"; |
108 | |
109 | size2 = 0; |
110 | if (blob2) { |
111 | idstr2 = got_object_blob_id_str(blob2, hex2, sizeof(hex2)); |
112 | err = got_object_blob_dump_to_file(&size2, NULL, NULL, f2, |
113 | blob2); |
114 | if (err) |
115 | goto done; |
116 | } else |
117 | idstr2 = "/dev/null"; |
118 | |
119 | if (outfile) { |
120 | char *modestr1 = NULL, *modestr2 = NULL; |
121 | int modebits; |
122 | if (mode1 && mode1 != mode2) { |
123 | if (S_ISLNK(mode1)) |
124 | modebits = S_IFLNK; |
125 | else |
126 | modebits = (S_IRWXU | S_IRWXG | S_IRWXO); |
127 | if (asprintf(&modestr1, " (mode %o)", |
128 | mode1 & modebits) == -1) { |
129 | err = got_error_from_errno("asprintf"); |
130 | goto done; |
131 | } |
132 | } |
133 | if (mode2 && mode1 != mode2) { |
134 | if (S_ISLNK(mode2)) |
135 | modebits = S_IFLNK; |
136 | else |
137 | modebits = (S_IRWXU | S_IRWXG | S_IRWXO); |
138 | if (asprintf(&modestr2, " (mode %o)", |
139 | mode2 & modebits) == -1) { |
140 | err = got_error_from_errno("asprintf"); |
141 | goto done; |
142 | } |
143 | } |
144 | n = fprintf(outfile, "blob - %s%s\n", idstr1, |
145 | modestr1 ? modestr1 : ""); |
146 | if (n < 0) |
147 | goto done; |
148 | outoff += n; |
149 | if (line_offsets) { |
150 | err = add_line_offset(line_offsets, nlines, outoff); |
151 | if (err) |
152 | goto done; |
153 | } |
154 | |
155 | n = fprintf(outfile, "blob + %s%s\n", idstr2, |
156 | modestr2 ? modestr2 : ""); |
157 | if (n < 0) |
158 | goto done; |
159 | outoff += n; |
160 | if (line_offsets) { |
161 | err = add_line_offset(line_offsets, nlines, outoff); |
162 | if (err) |
163 | goto done; |
164 | } |
165 | |
166 | free(modestr1); |
167 | free(modestr2); |
168 | } |
169 | err = got_diffreg(&result, f1, f2, GOT_DIFF_ALGORITHM_PATIENCE, |
170 | ignore_whitespace, force_text_diff); |
171 | if (err) |
172 | goto done; |
173 | |
174 | if (outfile) { |
175 | err = got_diffreg_output(line_offsets, nlines, result, |
176 | blob1 != NULL, blob2 != NULL, |
177 | label1 ? label1 : idstr1, |
178 | label2 ? label2 : idstr2, |
179 | GOT_DIFF_OUTPUT_UNIDIFF, diff_context, outfile); |
180 | if (err) |
181 | goto done; |
182 | } |
183 | |
184 | if (resultp && err == NULL) |
185 | *resultp = result; |
186 | else { |
187 | free_err = got_diffreg_result_free(result); |
188 | if (free_err && err == NULL) |
189 | err = free_err; |
190 | } |
191 | done: |
192 | if (f1 && fclose(f1) == EOF && err == NULL) |
193 | err = got_error_from_errno("fclose"); |
194 | if (f2 && fclose(f2) == EOF && err == NULL) |
195 | err = got_error_from_errno("fclose"); |
196 | return err; |
197 | } |
198 | |
199 | const struct got_error * |
200 | got_diff_blob_output_unidiff(void *arg, struct got_blob_object *blob1, |
201 | struct got_blob_object *blob2, struct got_object_id *id1, |
202 | struct got_object_id *id2, const char *label1, const char *label2, |
203 | mode_t mode1, mode_t mode2, struct got_repository *repo) |
204 | { |
205 | struct got_diff_blob_output_unidiff_arg *a = arg; |
206 | |
207 | return diff_blobs(&a->line_offsets, &a->nlines, NULL, |
208 | blob1, blob2, label1, label2, mode1, mode2, a->diff_context, |
209 | a->ignore_whitespace, a->force_text_diff, a->outfile); |
210 | } |
211 | |
212 | const struct got_error * |
213 | got_diff_blob(off_t **line_offsets, size_t *nlines, |
214 | struct got_blob_object *blob1, struct got_blob_object *blob2, |
215 | const char *label1, const char *label2, int diff_context, |
216 | int ignore_whitespace, int force_text_diff, FILE *outfile) |
217 | { |
218 | return diff_blobs(line_offsets, nlines, NULL, blob1, blob2, |
219 | label1, label2, 0, 0, diff_context, ignore_whitespace, |
220 | force_text_diff, outfile); |
221 | } |
222 | |
223 | static const struct got_error * |
224 | diff_blob_file(struct got_diffreg_result **resultp, |
225 | struct got_blob_object *blob1, const char *label1, FILE *f2, size_t size2, |
226 | const char *label2, int diff_context, int ignore_whitespace, |
227 | int force_text_diff, FILE *outfile) |
228 | { |
229 | const struct got_error *err = NULL, *free_err; |
230 | FILE *f1 = NULL; |
231 | char hex1[SHA1_DIGEST_STRING_LENGTH]; |
232 | char *idstr1 = NULL; |
233 | off_t size1; |
234 | struct got_diffreg_result *result = NULL; |
235 | |
236 | if (resultp) |
237 | *resultp = NULL; |
238 | |
239 | size1 = 0; |
240 | if (blob1) { |
241 | f1 = got_opentemp(); |
242 | if (f1 == NULL) |
243 | return got_error_from_errno("got_opentemp"); |
244 | idstr1 = got_object_blob_id_str(blob1, hex1, sizeof(hex1)); |
245 | err = got_object_blob_dump_to_file(&size1, NULL, NULL, f1, |
246 | blob1); |
247 | if (err) |
248 | goto done; |
249 | } else { |
250 | idstr1 = "/dev/null"; |
251 | } |
252 | |
253 | if (outfile) { |
254 | fprintf(outfile, "blob - %s\n", label1 ? label1 : idstr1); |
255 | fprintf(outfile, "file + %s\n", |
256 | f2 == NULL ? "/dev/null" : label2); |
257 | } |
258 | |
259 | err = got_diffreg(&result, f1, f2, GOT_DIFF_ALGORITHM_PATIENCE, |
260 | ignore_whitespace, force_text_diff); |
261 | if (err) |
262 | goto done; |
263 | |
264 | if (outfile) { |
265 | err = got_diffreg_output(NULL, NULL, result, |
266 | blob1 != NULL, f2 != NULL, |
267 | label2, |
268 | label2, GOT_DIFF_OUTPUT_UNIDIFF, |
269 | diff_context, outfile); |
270 | if (err) |
271 | goto done; |
272 | } |
273 | |
274 | if (resultp && err == NULL) |
275 | *resultp = result; |
276 | else if (result) { |
277 | free_err = got_diffreg_result_free(result); |
278 | if (free_err && err == NULL) |
279 | err = free_err; |
280 | } |
281 | done: |
282 | if (f1 && fclose(f1) == EOF && err == NULL) |
283 | err = got_error_from_errno("fclose"); |
284 | return err; |
285 | } |
286 | |
287 | const struct got_error * |
288 | got_diff_blob_file(struct got_blob_object *blob1, const char *label1, |
289 | FILE *f2, size_t size2, const char *label2, int diff_context, |
290 | int ignore_whitespace, int force_text_diff, FILE *outfile) |
291 | { |
292 | return diff_blob_file(NULL, blob1, label1, f2, size2, label2, |
293 | diff_context, ignore_whitespace, force_text_diff, outfile); |
294 | } |
295 | |
296 | static const struct got_error * |
297 | diff_added_blob(struct got_object_id *id, const char *label, mode_t mode, |
298 | struct got_repository *repo, got_diff_blob_cb cb, void *cb_arg) |
299 | { |
300 | const struct got_error *err; |
301 | struct got_blob_object *blob = NULL; |
302 | struct got_object *obj = NULL; |
303 | |
304 | err = got_object_open(&obj, repo, id); |
305 | if (err) |
306 | return err; |
307 | |
308 | err = got_object_blob_open(&blob, repo, obj, 8192); |
309 | if (err) |
310 | goto done; |
311 | err = cb(cb_arg, NULL, blob, NULL, id, NULL, label, 0, mode, repo); |
312 | done: |
313 | got_object_close(obj); |
314 | if (blob) |
315 | got_object_blob_close(blob); |
316 | return err; |
317 | } |
318 | |
319 | static const struct got_error * |
320 | diff_modified_blob(struct got_object_id *id1, struct got_object_id *id2, |
321 | const char *label1, const char *label2, mode_t mode1, mode_t mode2, |
322 | struct got_repository *repo, got_diff_blob_cb cb, void *cb_arg) |
323 | { |
324 | const struct got_error *err; |
325 | struct got_object *obj1 = NULL; |
326 | struct got_object *obj2 = NULL; |
327 | struct got_blob_object *blob1 = NULL; |
328 | struct got_blob_object *blob2 = NULL; |
329 | |
330 | err = got_object_open(&obj1, repo, id1); |
331 | if (err) |
332 | return err; |
333 | if (obj1->type != GOT_OBJ_TYPE_BLOB) { |
334 | err = got_error(GOT_ERR_OBJ_TYPE); |
335 | goto done; |
336 | } |
337 | |
338 | err = got_object_open(&obj2, repo, id2); |
339 | if (err) |
340 | goto done; |
341 | if (obj2->type != GOT_OBJ_TYPE_BLOB) { |
342 | err = got_error(GOT_ERR_BAD_OBJ_DATA); |
343 | goto done; |
344 | } |
345 | |
346 | err = got_object_blob_open(&blob1, repo, obj1, 8192); |
347 | if (err) |
348 | goto done; |
349 | |
350 | err = got_object_blob_open(&blob2, repo, obj2, 8192); |
351 | if (err) |
352 | goto done; |
353 | |
354 | err = cb(cb_arg, blob1, blob2, id1, id2, label1, label2, mode1, mode2, |
355 | repo); |
356 | done: |
357 | if (obj1) |
358 | got_object_close(obj1); |
359 | if (obj2) |
360 | got_object_close(obj2); |
361 | if (blob1) |
362 | got_object_blob_close(blob1); |
363 | if (blob2) |
364 | got_object_blob_close(blob2); |
365 | return err; |
366 | } |
367 | |
368 | static const struct got_error * |
369 | diff_deleted_blob(struct got_object_id *id, const char *label, mode_t mode, |
370 | struct got_repository *repo, got_diff_blob_cb cb, void *cb_arg) |
371 | { |
372 | const struct got_error *err; |
373 | struct got_blob_object *blob = NULL; |
374 | struct got_object *obj = NULL; |
375 | |
376 | err = got_object_open(&obj, repo, id); |
377 | if (err) |
378 | return err; |
379 | |
380 | err = got_object_blob_open(&blob, repo, obj, 8192); |
381 | if (err) |
382 | goto done; |
383 | err = cb(cb_arg, blob, NULL, id, NULL, label, NULL, mode, 0, repo); |
384 | done: |
385 | got_object_close(obj); |
386 | if (blob) |
387 | got_object_blob_close(blob); |
388 | return err; |
389 | } |
390 | |
391 | static const struct got_error * |
392 | diff_added_tree(struct got_object_id *id, const char *label, |
393 | struct got_repository *repo, got_diff_blob_cb cb, void *cb_arg, |
394 | int diff_content) |
395 | { |
396 | const struct got_error *err = NULL; |
397 | struct got_object *treeobj = NULL; |
398 | struct got_tree_object *tree = NULL; |
399 | |
400 | err = got_object_open(&treeobj, repo, id); |
401 | if (err) |
402 | goto done; |
403 | |
404 | if (treeobj->type != GOT_OBJ_TYPE_TREE) { |
405 | err = got_error(GOT_ERR_OBJ_TYPE); |
406 | goto done; |
407 | } |
408 | |
409 | err = got_object_tree_open(&tree, repo, treeobj); |
410 | if (err) |
411 | goto done; |
412 | |
413 | err = got_diff_tree(NULL, tree, NULL, label, repo, cb, cb_arg, |
414 | diff_content); |
415 | done: |
416 | if (tree) |
417 | got_object_tree_close(tree); |
418 | if (treeobj) |
419 | got_object_close(treeobj); |
420 | return err; |
421 | } |
422 | |
423 | static const struct got_error * |
424 | diff_modified_tree(struct got_object_id *id1, struct got_object_id *id2, |
425 | const char *label1, const char *label2, struct got_repository *repo, |
426 | got_diff_blob_cb cb, void *cb_arg, int diff_content) |
427 | { |
428 | const struct got_error *err; |
429 | struct got_object *treeobj1 = NULL; |
430 | struct got_object *treeobj2 = NULL; |
431 | struct got_tree_object *tree1 = NULL; |
432 | struct got_tree_object *tree2 = NULL; |
433 | |
434 | err = got_object_open(&treeobj1, repo, id1); |
435 | if (err) |
436 | goto done; |
437 | |
438 | if (treeobj1->type != GOT_OBJ_TYPE_TREE) { |
439 | err = got_error(GOT_ERR_OBJ_TYPE); |
440 | goto done; |
441 | } |
442 | |
443 | err = got_object_open(&treeobj2, repo, id2); |
444 | if (err) |
445 | goto done; |
446 | |
447 | if (treeobj2->type != GOT_OBJ_TYPE_TREE) { |
448 | err = got_error(GOT_ERR_OBJ_TYPE); |
449 | goto done; |
450 | } |
451 | |
452 | err = got_object_tree_open(&tree1, repo, treeobj1); |
453 | if (err) |
454 | goto done; |
455 | |
456 | err = got_object_tree_open(&tree2, repo, treeobj2); |
457 | if (err) |
458 | goto done; |
459 | |
460 | err = got_diff_tree(tree1, tree2, label1, label2, repo, cb, cb_arg, |
461 | diff_content); |
462 | |
463 | done: |
464 | if (tree1) |
465 | got_object_tree_close(tree1); |
466 | if (tree2) |
467 | got_object_tree_close(tree2); |
468 | if (treeobj1) |
469 | got_object_close(treeobj1); |
470 | if (treeobj2) |
471 | got_object_close(treeobj2); |
472 | return err; |
473 | } |
474 | |
475 | static const struct got_error * |
476 | diff_deleted_tree(struct got_object_id *id, const char *label, |
477 | struct got_repository *repo, got_diff_blob_cb cb, void *cb_arg, |
478 | int diff_content) |
479 | { |
480 | const struct got_error *err; |
481 | struct got_object *treeobj = NULL; |
482 | struct got_tree_object *tree = NULL; |
483 | |
484 | err = got_object_open(&treeobj, repo, id); |
485 | if (err) |
486 | goto done; |
487 | |
488 | if (treeobj->type != GOT_OBJ_TYPE_TREE) { |
489 | err = got_error(GOT_ERR_OBJ_TYPE); |
490 | goto done; |
491 | } |
492 | |
493 | err = got_object_tree_open(&tree, repo, treeobj); |
494 | if (err) |
495 | goto done; |
496 | |
497 | err = got_diff_tree(tree, NULL, label, NULL, repo, cb, cb_arg, |
498 | diff_content); |
499 | done: |
500 | if (tree) |
501 | got_object_tree_close(tree); |
502 | if (treeobj) |
503 | got_object_close(treeobj); |
504 | return err; |
505 | } |
506 | |
507 | static const struct got_error * |
508 | diff_kind_mismatch(struct got_object_id *id1, struct got_object_id *id2, |
509 | const char *label1, const char *label2, struct got_repository *repo, |
510 | got_diff_blob_cb cb, void *cb_arg) |
511 | { |
512 | |
513 | return NULL; |
514 | } |
515 | |
516 | static const struct got_error * |
517 | diff_entry_old_new(struct got_tree_entry *te1, |
518 | struct got_tree_entry *te2, const char *label1, const char *label2, |
519 | struct got_repository *repo, got_diff_blob_cb cb, void *cb_arg, |
520 | int diff_content) |
521 | { |
522 | const struct got_error *err = NULL; |
523 | int id_match; |
524 | |
525 | if (got_object_tree_entry_is_submodule(te1)) |
526 | return NULL; |
527 | |
528 | if (te2 == NULL) { |
529 | if (S_ISDIR(te1->mode)) |
530 | err = diff_deleted_tree(&te1->id, label1, repo, |
531 | cb, cb_arg, diff_content); |
532 | else { |
533 | if (diff_content) |
534 | err = diff_deleted_blob(&te1->id, label1, |
535 | te1->mode, repo, cb, cb_arg); |
536 | else |
537 | err = cb(cb_arg, NULL, NULL, &te1->id, NULL, |
538 | label1, NULL, te1->mode, 0, repo); |
539 | } |
540 | return err; |
541 | } else if (got_object_tree_entry_is_submodule(te2)) |
542 | return NULL; |
543 | |
544 | id_match = (got_object_id_cmp(&te1->id, &te2->id) == 0); |
545 | if (S_ISDIR(te1->mode) && S_ISDIR(te2->mode)) { |
546 | if (!id_match) |
547 | return diff_modified_tree(&te1->id, &te2->id, |
548 | label1, label2, repo, cb, cb_arg, diff_content); |
549 | } else if ((S_ISREG(te1->mode) || S_ISLNK(te1->mode)) && |
550 | (S_ISREG(te2->mode) || S_ISLNK(te2->mode))) { |
551 | if (!id_match || |
552 | ((te1->mode & (S_IFLNK | S_IXUSR))) != |
553 | (te2->mode & (S_IFLNK | S_IXUSR))) { |
554 | if (diff_content) |
555 | return diff_modified_blob(&te1->id, &te2->id, |
556 | label1, label2, te1->mode, te2->mode, |
557 | repo, cb, cb_arg); |
558 | else |
559 | return cb(cb_arg, NULL, NULL, &te1->id, |
560 | &te2->id, label1, label2, te1->mode, |
561 | te2->mode, repo); |
562 | } |
563 | } |
564 | |
565 | if (id_match) |
566 | return NULL; |
567 | |
568 | return diff_kind_mismatch(&te1->id, &te2->id, label1, label2, repo, |
569 | cb, cb_arg); |
570 | } |
571 | |
572 | static const struct got_error * |
573 | diff_entry_new_old(struct got_tree_entry *te2, |
574 | struct got_tree_entry *te1, const char *label2, |
575 | struct got_repository *repo, got_diff_blob_cb cb, void *cb_arg, |
576 | int diff_content) |
577 | { |
578 | if (te1 != NULL) |
579 | return NULL; |
580 | |
581 | if (got_object_tree_entry_is_submodule(te2)) |
582 | return NULL; |
583 | |
584 | if (S_ISDIR(te2->mode)) |
585 | return diff_added_tree(&te2->id, label2, repo, cb, cb_arg, |
586 | diff_content); |
587 | |
588 | if (diff_content) |
589 | return diff_added_blob(&te2->id, label2, te2->mode, repo, cb, |
590 | cb_arg); |
591 | |
592 | return cb(cb_arg, NULL, NULL, NULL, &te2->id, NULL, label2, 0, |
593 | te2->mode, repo); |
594 | } |
595 | |
596 | const struct got_error * |
597 | got_diff_tree_collect_changed_paths(void *arg, struct got_blob_object *blob1, |
598 | struct got_blob_object *blob2, struct got_object_id *id1, |
599 | struct got_object_id *id2, const char *label1, const char *label2, |
600 | mode_t mode1, mode_t mode2, struct got_repository *repo) |
601 | { |
602 | const struct got_error *err = NULL; |
603 | struct got_pathlist_head *paths = arg; |
604 | struct got_diff_changed_path *change = NULL; |
605 | char *path = NULL; |
606 | |
607 | path = strdup(label2 ? label2 : label1); |
| 1 | Assuming 'label2' is null | |
|
| |
| |
608 | if (path == NULL) |
| 4 | | Assuming 'path' is not equal to NULL | |
|
| |
609 | return got_error_from_errno("malloc"); |
610 | |
611 | change = malloc(sizeof(*change)); |
612 | if (change == NULL) { |
| 6 | | Assuming 'change' is equal to NULL | |
|
| |
613 | err = got_error_from_errno("malloc"); |
614 | goto done; |
| 8 | | Control jumps to line 631 | |
|
615 | } |
616 | |
617 | change->status = GOT_STATUS_NO_CHANGE; |
618 | if (id1 == NULL) |
619 | change->status = GOT_STATUS_ADD; |
620 | else if (id2 == NULL) |
621 | change->status = GOT_STATUS_DELETE; |
622 | else { |
623 | if (got_object_id_cmp(id1, id2) != 0) |
624 | change->status = GOT_STATUS_MODIFY; |
625 | else if (mode1 != mode2) |
626 | change->status = GOT_STATUS_MODE_CHANGE; |
627 | } |
628 | |
629 | err = got_pathlist_insert(NULL, paths, path, change); |
630 | done: |
631 | if (err) { |
| |
| |
632 | free(path); |
633 | free(change); |
634 | } |
635 | return err; |
| 11 | | Potential leak of memory pointed to by 'path' |
|
636 | } |
637 | |
638 | const struct got_error * |
639 | got_diff_tree(struct got_tree_object *tree1, struct got_tree_object *tree2, |
640 | const char *label1, const char *label2, struct got_repository *repo, |
641 | got_diff_blob_cb cb, void *cb_arg, int diff_content) |
642 | { |
643 | const struct got_error *err = NULL; |
644 | struct got_tree_entry *te1 = NULL; |
645 | struct got_tree_entry *te2 = NULL; |
646 | char *l1 = NULL, *l2 = NULL; |
647 | int tidx1 = 0, tidx2 = 0; |
648 | |
649 | if (tree1) { |
650 | te1 = got_object_tree_get_entry(tree1, 0); |
651 | if (te1 && asprintf(&l1, "%s%s%s", label1, label1[0] ? "/" : "", |
652 | te1->name) == -1) |
653 | return got_error_from_errno("asprintf"); |
654 | } |
655 | if (tree2) { |
656 | te2 = got_object_tree_get_entry(tree2, 0); |
657 | if (te2 && asprintf(&l2, "%s%s%s", label2, label2[0] ? "/" : "", |
658 | te2->name) == -1) |
659 | return got_error_from_errno("asprintf"); |
660 | } |
661 | |
662 | do { |
663 | if (te1) { |
664 | struct got_tree_entry *te = NULL; |
665 | if (tree2) |
666 | te = got_object_tree_find_entry(tree2, |
667 | te1->name); |
668 | if (te) { |
669 | free(l2); |
670 | l2 = NULL; |
671 | if (te && asprintf(&l2, "%s%s%s", label2, |
672 | label2[0] ? "/" : "", te->name) == -1) |
673 | return |
674 | got_error_from_errno("asprintf"); |
675 | } |
676 | err = diff_entry_old_new(te1, te, l1, l2, repo, cb, |
677 | cb_arg, diff_content); |
678 | if (err) |
679 | break; |
680 | } |
681 | |
682 | if (te2) { |
683 | struct got_tree_entry *te = NULL; |
684 | if (tree1) |
685 | te = got_object_tree_find_entry(tree1, |
686 | te2->name); |
687 | free(l2); |
688 | if (te) { |
689 | if (asprintf(&l2, "%s%s%s", label2, |
690 | label2[0] ? "/" : "", te->name) == -1) |
691 | return |
692 | got_error_from_errno("asprintf"); |
693 | } else { |
694 | if (asprintf(&l2, "%s%s%s", label2, |
695 | label2[0] ? "/" : "", te2->name) == -1) |
696 | return |
697 | got_error_from_errno("asprintf"); |
698 | } |
699 | err = diff_entry_new_old(te2, te, l2, repo, |
700 | cb, cb_arg, diff_content); |
701 | if (err) |
702 | break; |
703 | } |
704 | |
705 | free(l1); |
706 | l1 = NULL; |
707 | if (te1) { |
708 | tidx1++; |
709 | te1 = got_object_tree_get_entry(tree1, tidx1); |
710 | if (te1 && |
711 | asprintf(&l1, "%s%s%s", label1, |
712 | label1[0] ? "/" : "", te1->name) == -1) |
713 | return got_error_from_errno("asprintf"); |
714 | } |
715 | free(l2); |
716 | l2 = NULL; |
717 | if (te2) { |
718 | tidx2++; |
719 | te2 = got_object_tree_get_entry(tree2, tidx2); |
720 | if (te2 && |
721 | asprintf(&l2, "%s%s%s", label2, |
722 | label2[0] ? "/" : "", te2->name) == -1) |
723 | return got_error_from_errno("asprintf"); |
724 | } |
725 | } while (te1 || te2); |
726 | |
727 | return err; |
728 | } |
729 | |
730 | const struct got_error * |
731 | got_diff_objects_as_blobs(off_t **line_offsets, size_t *nlines, |
732 | struct got_object_id *id1, struct got_object_id *id2, |
733 | const char *label1, const char *label2, int diff_context, |
734 | int ignore_whitespace, int force_text_diff, |
735 | struct got_repository *repo, FILE *outfile) |
736 | { |
737 | const struct got_error *err; |
738 | struct got_blob_object *blob1 = NULL, *blob2 = NULL; |
739 | |
740 | if (id1 == NULL && id2 == NULL) |
741 | return got_error(GOT_ERR_NO_OBJ); |
742 | |
743 | if (id1) { |
744 | err = got_object_open_as_blob(&blob1, repo, id1, 8192); |
745 | if (err) |
746 | goto done; |
747 | } |
748 | if (id2) { |
749 | err = got_object_open_as_blob(&blob2, repo, id2, 8192); |
750 | if (err) |
751 | goto done; |
752 | } |
753 | err = got_diff_blob(line_offsets, nlines, blob1, blob2, |
754 | label1, label2, diff_context, ignore_whitespace, force_text_diff, |
755 | outfile); |
756 | done: |
757 | if (blob1) |
758 | got_object_blob_close(blob1); |
759 | if (blob2) |
760 | got_object_blob_close(blob2); |
761 | return err; |
762 | } |
763 | |
764 | const struct got_error * |
765 | got_diff_objects_as_trees(off_t **line_offsets, size_t *nlines, |
766 | struct got_object_id *id1, struct got_object_id *id2, |
767 | char *label1, char *label2, int diff_context, int ignore_whitespace, |
768 | int force_text_diff, struct got_repository *repo, FILE *outfile) |
769 | { |
770 | const struct got_error *err; |
771 | struct got_tree_object *tree1 = NULL, *tree2 = NULL; |
772 | struct got_diff_blob_output_unidiff_arg arg; |
773 | int want_lineoffsets = (line_offsets != NULL && *line_offsets != NULL); |
774 | |
775 | if (id1 == NULL && id2 == NULL) |
776 | return got_error(GOT_ERR_NO_OBJ); |
777 | |
778 | if (id1) { |
779 | err = got_object_open_as_tree(&tree1, repo, id1); |
780 | if (err) |
781 | goto done; |
782 | } |
783 | if (id2) { |
784 | err = got_object_open_as_tree(&tree2, repo, id2); |
785 | if (err) |
786 | goto done; |
787 | } |
788 | arg.diff_context = diff_context; |
789 | arg.ignore_whitespace = ignore_whitespace; |
790 | arg.force_text_diff = force_text_diff; |
791 | arg.outfile = outfile; |
792 | if (want_lineoffsets) { |
793 | arg.line_offsets = *line_offsets; |
794 | arg.nlines = *nlines; |
795 | } else { |
796 | arg.line_offsets = NULL; |
797 | arg.nlines = 0; |
798 | } |
799 | err = got_diff_tree(tree1, tree2, label1, label2, repo, |
800 | got_diff_blob_output_unidiff, &arg, 1); |
801 | |
802 | if (want_lineoffsets) { |
803 | *line_offsets = arg.line_offsets; |
804 | *nlines = arg.nlines; |
805 | } |
806 | done: |
807 | if (tree1) |
808 | got_object_tree_close(tree1); |
809 | if (tree2) |
810 | got_object_tree_close(tree2); |
811 | return err; |
812 | } |
813 | |
814 | const struct got_error * |
815 | got_diff_objects_as_commits(off_t **line_offsets, size_t *nlines, |
816 | struct got_object_id *id1, struct got_object_id *id2, |
817 | int diff_context, int ignore_whitespace, int force_text_diff, |
818 | struct got_repository *repo, FILE *outfile) |
819 | { |
820 | const struct got_error *err; |
821 | struct got_commit_object *commit1 = NULL, *commit2 = NULL; |
822 | |
823 | if (id2 == NULL) |
824 | return got_error(GOT_ERR_NO_OBJ); |
825 | |
826 | if (id1) { |
827 | err = got_object_open_as_commit(&commit1, repo, id1); |
828 | if (err) |
829 | goto done; |
830 | } |
831 | |
832 | err = got_object_open_as_commit(&commit2, repo, id2); |
833 | if (err) |
834 | goto done; |
835 | |
836 | err = got_diff_objects_as_trees(line_offsets, nlines, |
837 | commit1 ? got_object_commit_get_tree_id(commit1) : NULL, |
838 | got_object_commit_get_tree_id(commit2), "", "", diff_context, |
839 | ignore_whitespace, force_text_diff, repo, outfile); |
840 | done: |
841 | if (commit1) |
842 | got_object_commit_close(commit1); |
843 | if (commit2) |
844 | got_object_commit_close(commit2); |
845 | return err; |
846 | } |
847 | |
848 | const struct got_error * |
849 | got_diff_files(struct got_diffreg_result **resultp, |
850 | FILE *f1, const char *label1, FILE *f2, const char *label2, |
851 | int diff_context, int ignore_whitespace, int force_text_diff, |
852 | FILE *outfile) |
853 | { |
854 | const struct got_error *err = NULL; |
855 | struct got_diffreg_result *diffreg_result = NULL; |
856 | |
857 | if (resultp) |
858 | *resultp = NULL; |
859 | |
860 | if (outfile) { |
861 | fprintf(outfile, "file - %s\n", |
862 | f1 == NULL ? "/dev/null" : label1); |
863 | fprintf(outfile, "file + %s\n", |
864 | f2 == NULL ? "/dev/null" : label2); |
865 | } |
866 | |
867 | err = got_diffreg(&diffreg_result, f1, f2, GOT_DIFF_ALGORITHM_PATIENCE, |
868 | ignore_whitespace, force_text_diff); |
869 | if (err) |
870 | goto done; |
871 | |
872 | if (outfile) { |
873 | err = got_diffreg_output(NULL, NULL, diffreg_result, |
874 | f1 != NULL, f2 != NULL, label1, label2, |
875 | GOT_DIFF_OUTPUT_UNIDIFF, diff_context, outfile); |
876 | if (err) |
877 | goto done; |
878 | } |
879 | |
880 | done: |
881 | if (resultp && err == NULL) |
882 | *resultp = diffreg_result; |
883 | else if (diffreg_result) { |
884 | const struct got_error *free_err; |
885 | free_err = got_diffreg_result_free(diffreg_result); |
886 | if (free_err && err == NULL) |
887 | err = free_err; |
888 | } |
889 | |
890 | return err; |
891 | } |