1/* 2 * Copyright (c) 2017 Stefan Sperling <stsp@openbsd.org> 3 * 4 * Permission to use, copy, modify, and distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the above 6 * copyright notice and this permission notice appear in all copies. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 */ 16 17#include <sys/queue.h> 18#include <sys/stat.h> 19 20#include <stdbool.h> 21#include <stdio.h> 22#include <stdlib.h> 23#include <string.h> 24#include <limits.h> 25#include <sha1.h> 26#include <zlib.h> 27 28#include "got_object.h" 29#include "got_repository.h" 30#include "got_error.h" 31#include "got_diff.h" 32#include "got_opentemp.h" 33#include "got_path.h" 34#include "got_cancel.h" 35#include "got_worktree.h" 36 37#include "got_lib_diff.h" 38#include "got_lib_delta.h" 39#include "got_lib_inflate.h" 40#include "got_lib_object.h" 41 42static const struct got_error * 43add_line_offset(off_t **line_offsets, size_t *nlines, off_t off) 44{ 45 off_t *p; 46 47 p = reallocarray(*line_offsets, *nlines + 1, sizeof(off_t)); 48 if (p == NULL) 49 return got_error_from_errno("reallocarray"); 50 *line_offsets = p; 51 (*line_offsets)[*nlines] = off; 52 (*nlines)++; 53 return NULL; 54} 55 56static const struct got_error * 57diff_blobs(off_t **line_offsets, size_t *nlines, 58 struct got_diffreg_result **resultp, struct got_blob_object *blob1, 59 struct got_blob_object *blob2, 60 const char *label1, const char *label2, mode_t mode1, mode_t mode2, 61 int diff_context, int ignore_whitespace, FILE *outfile) 62{ 63 const struct got_error *err = NULL, *free_err; 64 FILE *f1 = NULL, *f2 = NULL; 65 char hex1[SHA1_DIGEST_STRING_LENGTH]; 66 char hex2[SHA1_DIGEST_STRING_LENGTH]; 67 char *idstr1 = NULL, *idstr2 = NULL; 68 size_t size1, size2; 69 struct got_diffreg_result *result; 70 off_t outoff = 0; 71 int n; 72 73 if (line_offsets && *line_offsets && *nlines > 0) 74 outoff = (*line_offsets)[*nlines - 1]; 75 76 if (resultp) 77 *resultp = NULL; 78 79 if (blob1) { 80 f1 = got_opentemp(); 81 if (f1 == NULL) 82 return got_error_from_errno("got_opentemp"); 83 } 84 85 if (blob2) { 86 f2 = got_opentemp(); 87 if (f2 == NULL) { 88 err = got_error_from_errno("got_opentemp"); 89 fclose(f1); 90 return err; 91 } 92 } 93 94 size1 = 0; 95 if (blob1) { 96 idstr1 = got_object_blob_id_str(blob1, hex1, sizeof(hex1)); 97 err = got_object_blob_dump_to_file(&size1, NULL, NULL, f1, 98 blob1); 99 if (err) 100 goto done; 101 } else 102 idstr1 = "/dev/null"; 103 104 size2 = 0; 105 if (blob2) { 106 idstr2 = got_object_blob_id_str(blob2, hex2, sizeof(hex2)); 107 err = got_object_blob_dump_to_file(&size2, NULL, NULL, f2, 108 blob2); 109 if (err) 110 goto done; 111 } else 112 idstr2 = "/dev/null"; 113 114 if (outfile) { 115 char *modestr1 = NULL, *modestr2 = NULL; 116 int modebits; 117 if (mode1 && mode1 != mode2) { 118 if (S_ISLNK(mode1)) 119 modebits = S_IFLNK; 120 else 121 modebits = (S_IRWXU | S_IRWXG | S_IRWXO); 122 if (asprintf(&modestr1, " (mode %o)", 123 mode1 & modebits) == -1) { 124 err = got_error_from_errno("asprintf"); 125 goto done; 126 } 127 } 128 if (mode2 && mode1 != mode2) { 129 if (S_ISLNK(mode2)) 130 modebits = S_IFLNK; 131 else 132 modebits = (S_IRWXU | S_IRWXG | S_IRWXO); 133 if (asprintf(&modestr2, " (mode %o)", 134 mode2 & modebits) == -1) { 135 err = got_error_from_errno("asprintf"); 136 goto done; 137 } 138 } 139 n = fprintf(outfile, "blob - %s%s\n", idstr1, 140 modestr1 ? modestr1 : ""); 141 if (n < 0) { 142 err = got_error_from_errno("fprintf"); 143 goto done; 144 } 145 outoff += n; 146 if (line_offsets) { 147 err = add_line_offset(line_offsets, nlines, outoff); 148 if (err) 149 goto done; 150 } 151 152 n = fprintf(outfile, "blob + %s%s\n", idstr2, 153 modestr2 ? modestr2 : ""); 154 if (n < 0) { 155 err = got_error_from_errno("fprintf"); 156 goto done; 157 } 158 outoff += n; 159 if (line_offsets) { 160 err = add_line_offset(line_offsets, nlines, outoff); 161 if (err) 162 goto done; 163 } 164 165 free(modestr1); 166 free(modestr2); 167 } 168 err = got_diffreg(&result, f1, f2, GOT_DIFF_ALGORITHM_MYERS, 169 ignore_whitespace); 170 if (err) 171 goto done; 172 173 if (outfile) { 174 err = got_diffreg_output(line_offsets, nlines, result, f1, f2, 175 label1 ? label1 : idstr1, 176 label2 ? label2 : idstr2, 177 GOT_DIFF_OUTPUT_UNIDIFF, diff_context, outfile); 178 if (err) 179 goto done; 180 } 181 182 if (resultp && err == NULL) 183 *resultp = result; 184 else { 185 free_err = got_diffreg_result_free(result); 186 if (free_err && err == NULL) 187 err = free_err; 188 } 189done: 190 if (f1 && fclose(f1) != 0 && err == NULL) 191 err = got_error_from_errno("fclose"); 192 if (f2 && fclose(f2) != 0 && err == NULL) 193 err = got_error_from_errno("fclose"); 194 return err; 195} 196 197const struct got_error * 198got_diff_blob_output_unidiff(void *arg, struct got_blob_object *blob1, 199 struct got_blob_object *blob2, struct got_object_id *id1, 200 struct got_object_id *id2, const char *label1, const char *label2, 201 mode_t mode1, mode_t mode2, struct got_repository *repo) 202{ 203 struct got_diff_blob_output_unidiff_arg *a = arg; 204 205 return diff_blobs(&a->line_offsets, &a->nlines, NULL, 206 blob1, blob2, label1, label2, mode1, mode2, a->diff_context, 207 a->ignore_whitespace, a->outfile); 208} 209 210const struct got_error * 211got_diff_blob(off_t **line_offsets, size_t *nlines, 212 struct got_blob_object *blob1, struct got_blob_object *blob2, 213 const char *label1, const char *label2, int diff_context, 214 int ignore_whitespace, FILE *outfile) 215{ 216 return diff_blobs(line_offsets, nlines, NULL, blob1, blob2, 217 label1, label2, 0, 0, diff_context, ignore_whitespace, outfile); 218} 219 220static const struct got_error * 221diff_blob_file(struct got_diffreg_result **resultp, 222 struct got_blob_object *blob1, const char *label1, FILE *f2, size_t size2, 223 const char *label2, int diff_context, int ignore_whitespace, FILE *outfile) 224{ 225 const struct got_error *err = NULL, *free_err; 226 FILE *f1 = NULL; 227 char hex1[SHA1_DIGEST_STRING_LENGTH]; 228 char *idstr1 = NULL; 229 size_t size1; 230 struct got_diffreg_result *result = NULL; 231 232 if (resultp) 233 *resultp = NULL; 234 235 size1 = 0; 236 if (blob1) { 237 f1 = got_opentemp(); 238 if (f1 == NULL) 239 return got_error_from_errno("got_opentemp"); 240 idstr1 = got_object_blob_id_str(blob1, hex1, sizeof(hex1)); 241 err = got_object_blob_dump_to_file(&size1, NULL, NULL, f1, 242 blob1); 243 if (err) 244 goto done; 245 } else { 246 idstr1 = "/dev/null"; 247 } 248 249 if (outfile) { 250 fprintf(outfile, "blob - %s\n", label1 ? label1 : idstr1); 251 fprintf(outfile, "file + %s\n", 252 f2 == NULL ? "/dev/null" : label2); 253 } 254 255 err = got_diffreg(&result, f1, f2, GOT_DIFF_ALGORITHM_MYERS, 256 ignore_whitespace); 257 if (err) 258 goto done; 259 260 if (outfile) { 261 err = got_diffreg_output(NULL, NULL, result, f1, f2, 262 label2, label2, GOT_DIFF_OUTPUT_UNIDIFF, diff_context, 263 outfile); 264 if (err) 265 goto done; 266 } 267 268 if (resultp && err == NULL) 269 *resultp = result; 270 else if (result) { 271 free_err = got_diffreg_result_free(result); 272 if (free_err && err == NULL) 273 err = free_err; 274 } 275done: 276 if (f1 && fclose(f1) != 0 && err == NULL) 277 err = got_error_from_errno("fclose"); 278 return err; 279} 280 281const struct got_error * 282got_diff_blob_file(struct got_blob_object *blob1, const char *label1, 283 FILE *f2, size_t size2, const char *label2, int diff_context, 284 int ignore_whitespace, FILE *outfile) 285{ 286 return diff_blob_file(NULL, blob1, label1, f2, size2, label2, 287 diff_context, ignore_whitespace, outfile); 288} 289 290const struct got_error * 291got_diff_blob_prepared_file(struct got_diffreg_result **resultp, 292 struct diff_data *data1, struct got_blob_object *blob1, 293 struct diff_data *data2, FILE *f2, char *p2, size_t size2, 294 const struct diff_config *cfg, int ignore_whitespace) 295{ 296 const struct got_error *err = NULL, *free_err; 297 FILE *f1 = NULL; 298 char hex1[SHA1_DIGEST_STRING_LENGTH]; 299 char *idstr1 = NULL, *p1 = NULL; 300 size_t size1, size; 301 struct got_diffreg_result *result = NULL; 302 int f1_created = 0; 303 304 *resultp = NULL; 305 306 size1 = 0; 307 if (blob1) { 308 f1 = got_opentemp(); 309 if (f1 == NULL) 310 return got_error_from_errno("got_opentemp"); 311 idstr1 = got_object_blob_id_str(blob1, hex1, sizeof(hex1)); 312 err = got_object_blob_dump_to_file(&size1, NULL, NULL, f1, 313 blob1); 314 if (err) 315 goto done; 316 } else { 317 idstr1 = "/dev/null"; 318 } 319 320 err = got_diff_prepare_file(&f1, &p1, &f1_created, &size, 321 data1, cfg, ignore_whitespace); 322 if (err) 323 goto done; 324 325 err = got_diffreg_prepared_files(&result, cfg, data1, f1, 326 p1, size1, data2, f2, p2, size2); 327 if (err) 328 goto done; 329 330 *resultp = result; 331done: 332 if (err) { 333 if (result) 334 free_err = got_diffreg_result_free_left(result); 335 else 336 free_err = got_diffreg_close(f1, p1, size1, NULL, 337 NULL, 0); 338 if (free_err && err == NULL) 339 err = free_err; 340 } 341 return err; 342} 343 344static const struct got_error * 345diff_added_blob(struct got_object_id *id, const char *label, mode_t mode, 346 struct got_repository *repo, got_diff_blob_cb cb, void *cb_arg) 347{ 348 const struct got_error *err; 349 struct got_blob_object *blob = NULL; 350 struct got_object *obj = NULL; 351 352 err = got_object_open(&obj, repo, id); 353 if (err) 354 return err; 355 356 err = got_object_blob_open(&blob, repo, obj, 8192); 357 if (err) 358 goto done; 359 err = cb(cb_arg, NULL, blob, NULL, id, NULL, label, 0, mode, repo); 360done: 361 got_object_close(obj); 362 if (blob) 363 got_object_blob_close(blob); 364 return err; 365} 366 367static const struct got_error * 368diff_modified_blob(struct got_object_id *id1, struct got_object_id *id2, 369 const char *label1, const char *label2, mode_t mode1, mode_t mode2, 370 struct got_repository *repo, got_diff_blob_cb cb, void *cb_arg) 371{ 372 const struct got_error *err; 373 struct got_object *obj1 = NULL; 374 struct got_object *obj2 = NULL; 375 struct got_blob_object *blob1 = NULL; 376 struct got_blob_object *blob2 = NULL; 377 378 err = got_object_open(&obj1, repo, id1); 379 if (err) 380 return err; 381 if (obj1->type != GOT_OBJ_TYPE_BLOB) { 382 err = got_error(GOT_ERR_OBJ_TYPE); 383 goto done; 384 } 385 386 err = got_object_open(&obj2, repo, id2); 387 if (err) 388 goto done; 389 if (obj2->type != GOT_OBJ_TYPE_BLOB) { 390 err = got_error(GOT_ERR_BAD_OBJ_DATA); 391 goto done; 392 } 393 394 err = got_object_blob_open(&blob1, repo, obj1, 8192); 395 if (err) 396 goto done; 397 398 err = got_object_blob_open(&blob2, repo, obj2, 8192); 399 if (err) 400 goto done; 401 402 err = cb(cb_arg, blob1, blob2, id1, id2, label1, label2, mode1, mode2, 403 repo); 404done: 405 if (obj1) 406 got_object_close(obj1); 407 if (obj2) 408 got_object_close(obj2); 409 if (blob1) 410 got_object_blob_close(blob1); 411 if (blob2) 412 got_object_blob_close(blob2); 413 return err; 414} 415 416static const struct got_error * 417diff_deleted_blob(struct got_object_id *id, const char *label, mode_t mode, 418 struct got_repository *repo, got_diff_blob_cb cb, void *cb_arg) 419{ 420 const struct got_error *err; 421 struct got_blob_object *blob = NULL; 422 struct got_object *obj = NULL; 423 424 err = got_object_open(&obj, repo, id); 425 if (err) 426 return err; 427 428 err = got_object_blob_open(&blob, repo, obj, 8192); 429 if (err) 430 goto done; 431 err = cb(cb_arg, blob, NULL, id, NULL, label, NULL, mode, 0, repo); 432done: 433 got_object_close(obj); 434 if (blob) 435 got_object_blob_close(blob); 436 return err; 437} 438 439static const struct got_error * 440diff_added_tree(struct got_object_id *id, const char *label, 441 struct got_repository *repo, got_diff_blob_cb cb, void *cb_arg, 442 int diff_content) 443{ 444 const struct got_error *err = NULL; 445 struct got_object *treeobj = NULL; 446 struct got_tree_object *tree = NULL; 447 448 err = got_object_open(&treeobj, repo, id); 449 if (err) 450 goto done; 451 452 if (treeobj->type != GOT_OBJ_TYPE_TREE) { 453 err = got_error(GOT_ERR_OBJ_TYPE); 454 goto done; 455 } 456 457 err = got_object_tree_open(&tree, repo, treeobj); 458 if (err) 459 goto done; 460 461 err = got_diff_tree(NULL, tree, NULL, label, repo, cb, cb_arg, 462 diff_content); 463done: 464 if (tree) 465 got_object_tree_close(tree); 466 if (treeobj) 467 got_object_close(treeobj); 468 return err; 469} 470 471static const struct got_error * 472diff_modified_tree(struct got_object_id *id1, struct got_object_id *id2, 473 const char *label1, const char *label2, struct got_repository *repo, 474 got_diff_blob_cb cb, void *cb_arg, int diff_content) 475{ 476 const struct got_error *err; 477 struct got_object *treeobj1 = NULL; 478 struct got_object *treeobj2 = NULL; 479 struct got_tree_object *tree1 = NULL; 480 struct got_tree_object *tree2 = NULL; 481 482 err = got_object_open(&treeobj1, repo, id1); 483 if (err) 484 goto done; 485 486 if (treeobj1->type != GOT_OBJ_TYPE_TREE) { 487 err = got_error(GOT_ERR_OBJ_TYPE); 488 goto done; 489 } 490 491 err = got_object_open(&treeobj2, repo, id2); 492 if (err) 493 goto done; 494 495 if (treeobj2->type != GOT_OBJ_TYPE_TREE) { 496 err = got_error(GOT_ERR_OBJ_TYPE); 497 goto done; 498 } 499 500 err = got_object_tree_open(&tree1, repo, treeobj1); 501 if (err) 502 goto done; 503 504 err = got_object_tree_open(&tree2, repo, treeobj2); 505 if (err) 506 goto done; 507 508 err = got_diff_tree(tree1, tree2, label1, label2, repo, cb, cb_arg, 509 diff_content); 510 511done: 512 if (tree1) 513 got_object_tree_close(tree1); 514 if (tree2) 515 got_object_tree_close(tree2); 516 if (treeobj1) 517 got_object_close(treeobj1); 518 if (treeobj2) 519 got_object_close(treeobj2); 520 return err; 521} 522 523static const struct got_error * 524diff_deleted_tree(struct got_object_id *id, const char *label, 525 struct got_repository *repo, got_diff_blob_cb cb, void *cb_arg, 526 int diff_content) 527{ 528 const struct got_error *err; 529 struct got_object *treeobj = NULL; 530 struct got_tree_object *tree = NULL; 531 532 err = got_object_open(&treeobj, repo, id); 533 if (err) 534 goto done; 535 536 if (treeobj->type != GOT_OBJ_TYPE_TREE) { 537 err = got_error(GOT_ERR_OBJ_TYPE); 538 goto done; 539 } 540 541 err = got_object_tree_open(&tree, repo, treeobj); 542 if (err) 543 goto done; 544 545 err = got_diff_tree(tree, NULL, label, NULL, repo, cb, cb_arg, 546 diff_content); 547done: 548 if (tree) 549 got_object_tree_close(tree); 550 if (treeobj) 551 got_object_close(treeobj); 552 return err; 553} 554 555static const struct got_error * 556diff_kind_mismatch(struct got_object_id *id1, struct got_object_id *id2, 557 const char *label1, const char *label2, struct got_repository *repo, 558 got_diff_blob_cb cb, void *cb_arg) 559{ 560 /* XXX TODO */ 561 return NULL; 562} 563 564static const struct got_error * 565diff_entry_old_new(struct got_tree_entry *te1, 566 struct got_tree_entry *te2, const char *label1, const char *label2, 567 struct got_repository *repo, got_diff_blob_cb cb, void *cb_arg, 568 int diff_content) 569{ 570 const struct got_error *err = NULL; 571 int id_match; 572 573 if (got_object_tree_entry_is_submodule(te1)) 574 return NULL; 575 576 if (te2 == NULL) { 577 if (S_ISDIR(te1->mode)) 578 err = diff_deleted_tree(&te1->id, label1, repo, 579 cb, cb_arg, diff_content); 580 else { 581 if (diff_content) 582 err = diff_deleted_blob(&te1->id, label1, 583 te1->mode, repo, cb, cb_arg); 584 else 585 err = cb(cb_arg, NULL, NULL, &te1->id, NULL, 586 label1, NULL, te1->mode, 0, repo); 587 } 588 return err; 589 } else if (got_object_tree_entry_is_submodule(te2)) 590 return NULL; 591 592 id_match = (got_object_id_cmp(&te1->id, &te2->id) == 0); 593 if (S_ISDIR(te1->mode) && S_ISDIR(te2->mode)) { 594 if (!id_match) 595 return diff_modified_tree(&te1->id, &te2->id, 596 label1, label2, repo, cb, cb_arg, diff_content); 597 } else if ((S_ISREG(te1->mode) || S_ISLNK(te1->mode)) && 598 (S_ISREG(te2->mode) || S_ISLNK(te2->mode))) { 599 if (!id_match || 600 ((te1->mode & (S_IFLNK | S_IXUSR))) != 601 (te2->mode & (S_IFLNK | S_IXUSR))) { 602 if (diff_content) 603 return diff_modified_blob(&te1->id, &te2->id, 604 label1, label2, te1->mode, te2->mode, 605 repo, cb, cb_arg); 606 else 607 return cb(cb_arg, NULL, NULL, &te1->id, 608 &te2->id, label1, label2, te1->mode, 609 te2->mode, repo); 610 } 611 } 612 613 if (id_match) 614 return NULL; 615 616 return diff_kind_mismatch(&te1->id, &te2->id, label1, label2, repo, 617 cb, cb_arg); 618} 619 620static const struct got_error * 621diff_entry_new_old(struct got_tree_entry *te2, 622 struct got_tree_entry *te1, const char *label2, 623 struct got_repository *repo, got_diff_blob_cb cb, void *cb_arg, 624 int diff_content) 625{ 626 if (te1 != NULL) /* handled by diff_entry_old_new() */ 627 return NULL; 628 629 if (got_object_tree_entry_is_submodule(te2)) 630 return NULL; 631 632 if (S_ISDIR(te2->mode)) 633 return diff_added_tree(&te2->id, label2, repo, cb, cb_arg, 634 diff_content); 635 636 if (diff_content) 637 return diff_added_blob(&te2->id, label2, te2->mode, repo, cb, 638 cb_arg); 639 640 return cb(cb_arg, NULL, NULL, NULL, &te2->id, NULL, label2, 0, 641 te2->mode, repo); 642} 643 644const struct got_error * 645got_diff_tree_collect_changed_paths(void *arg, struct got_blob_object *blob1, 646 struct got_blob_object *blob2, struct got_object_id *id1, 647 struct got_object_id *id2, const char *label1, const char *label2, 648 mode_t mode1, mode_t mode2, struct got_repository *repo) 649{ 650 const struct got_error *err = NULL; 651 struct got_pathlist_head *paths = arg; 652 struct got_diff_changed_path *change = NULL; 653 char *path = NULL; 654 655 path = strdup(label2 ? label2 : label1); 656 if (path == NULL) 657 return got_error_from_errno("malloc"); 658 659 change = malloc(sizeof(*change)); 660 if (change == NULL) { 661 err = got_error_from_errno("malloc"); 662 goto done; 663 } 664 665 change->status = GOT_STATUS_NO_CHANGE; 666 if (id1 == NULL) 667 change->status = GOT_STATUS_ADD; 668 else if (id2 == NULL) 669 change->status = GOT_STATUS_DELETE; 670 else { 671 if (got_object_id_cmp(id1, id2) != 0) 672 change->status = GOT_STATUS_MODIFY; 673 else if (mode1 != mode2) 674 change->status = GOT_STATUS_MODE_CHANGE; 675 } 676 677 err = got_pathlist_insert(NULL, paths, path, change); 678done: 679 if (err) { 680 free(path); 681 free(change); 682 } 683 return err; 684} 685 686const struct got_error * 687got_diff_tree(struct got_tree_object *tree1, struct got_tree_object *tree2, 688 const char *label1, const char *label2, struct got_repository *repo, 689 got_diff_blob_cb cb, void *cb_arg, int diff_content) 690{ 691 const struct got_error *err = NULL; 692 struct got_tree_entry *te1 = NULL; 693 struct got_tree_entry *te2 = NULL; 694 char *l1 = NULL, *l2 = NULL; 695 int tidx1 = 0, tidx2 = 0; 696 697 if (tree1) { 698 te1 = got_object_tree_get_entry(tree1, 0); 699 if (te1 && asprintf(&l1, "%s%s%s", label1, label1[0] ? "/" : "", 700 te1->name) == -1) 701 return got_error_from_errno("asprintf"); 702 } 703 if (tree2) { 704 te2 = got_object_tree_get_entry(tree2, 0); 705 if (te2 && asprintf(&l2, "%s%s%s", label2, label2[0] ? "/" : "", 706 te2->name) == -1) 707 return got_error_from_errno("asprintf"); 708 } 709 710 do { 711 if (te1) { 712 struct got_tree_entry *te = NULL; 713 if (tree2) 714 te = got_object_tree_find_entry(tree2, 715 te1->name); 716 if (te) { 717 free(l2); 718 l2 = NULL; 719 if (te && asprintf(&l2, "%s%s%s", label2, 720 label2[0] ? "/" : "", te->name) == -1) 721 return 722 got_error_from_errno("asprintf"); 723 } 724 err = diff_entry_old_new(te1, te, l1, l2, repo, cb, 725 cb_arg, diff_content); 726 if (err) 727 break; 728 } 729 730 if (te2) { 731 struct got_tree_entry *te = NULL; 732 if (tree1) 733 te = got_object_tree_find_entry(tree1, 734 te2->name); 735 free(l2); 736 if (te) { 737 if (asprintf(&l2, "%s%s%s", label2, 738 label2[0] ? "/" : "", te->name) == -1) 739 return 740 got_error_from_errno("asprintf"); 741 } else { 742 if (asprintf(&l2, "%s%s%s", label2, 743 label2[0] ? "/" : "", te2->name) == -1) 744 return 745 got_error_from_errno("asprintf"); 746 } 747 err = diff_entry_new_old(te2, te, l2, repo, 748 cb, cb_arg, diff_content); 749 if (err) 750 break; 751 } 752 753 free(l1); 754 l1 = NULL; 755 if (te1) { 756 tidx1++; 757 te1 = got_object_tree_get_entry(tree1, tidx1); 758 if (te1 && 759 asprintf(&l1, "%s%s%s", label1, 760 label1[0] ? "/" : "", te1->name) == -1) 761 return got_error_from_errno("asprintf"); 762 } 763 free(l2); 764 l2 = NULL; 765 if (te2) { 766 tidx2++; 767 te2 = got_object_tree_get_entry(tree2, tidx2); 768 if (te2 && 769 asprintf(&l2, "%s%s%s", label2, 770 label2[0] ? "/" : "", te2->name) == -1) 771 return got_error_from_errno("asprintf"); 772 } 773 } while (te1 || te2); 774 775 return err; 776} 777 778const struct got_error * 779got_diff_objects_as_blobs(off_t **line_offsets, size_t *nlines, 780 struct got_object_id *id1, struct got_object_id *id2, 781 const char *label1, const char *label2, int diff_context, 782 int ignore_whitespace, struct got_repository *repo, FILE *outfile) 783{ 784 const struct got_error *err; 785 struct got_blob_object *blob1 = NULL, *blob2 = NULL; 786 787 if (id1 == NULL && id2 == NULL) 788 return got_error(GOT_ERR_NO_OBJ); 789 790 if (id1) { 791 err = got_object_open_as_blob(&blob1, repo, id1, 8192); 792 if (err) 793 goto done; 794 } 795 if (id2) { 796 err = got_object_open_as_blob(&blob2, repo, id2, 8192); 797 if (err) 798 goto done; 799 } 800 err = got_diff_blob(line_offsets, nlines, blob1, blob2, 801 label1, label2, diff_context, ignore_whitespace, outfile); 802done: 803 if (blob1) 804 got_object_blob_close(blob1); 805 if (blob2) 806 got_object_blob_close(blob2); 807 return err; 808} 809 810const struct got_error * 811got_diff_objects_as_trees(off_t **line_offsets, size_t *nlines, 812 struct got_object_id *id1, struct got_object_id *id2, 813 char *label1, char *label2, int diff_context, int ignore_whitespace, 814 struct got_repository *repo, FILE *outfile) 815{ 816 const struct got_error *err; 817 struct got_tree_object *tree1 = NULL, *tree2 = NULL; 818 struct got_diff_blob_output_unidiff_arg arg; 819 int want_lineoffsets = (line_offsets != NULL && *line_offsets != NULL); 820 821 if (id1 == NULL && id2 == NULL) 822 return got_error(GOT_ERR_NO_OBJ); 823 824 if (id1) { 825 err = got_object_open_as_tree(&tree1, repo, id1); 826 if (err) 827 goto done; 828 } 829 if (id2) { 830 err = got_object_open_as_tree(&tree2, repo, id2); 831 if (err) 832 goto done; 833 } 834 arg.diff_context = diff_context; 835 arg.ignore_whitespace = ignore_whitespace; 836 arg.outfile = outfile; 837 if (want_lineoffsets) { 838 arg.line_offsets = *line_offsets; 839 arg.nlines = *nlines; 840 } else { 841 arg.line_offsets = NULL; 842 arg.nlines = 0; 843 } 844 err = got_diff_tree(tree1, tree2, label1, label2, repo, 845 got_diff_blob_output_unidiff, &arg, 1); 846 847 if (want_lineoffsets) { 848 *line_offsets = arg.line_offsets; /* was likely re-allocated */ 849 *nlines = arg.nlines; 850 } 851done: 852 if (tree1) 853 got_object_tree_close(tree1); 854 if (tree2) 855 got_object_tree_close(tree2); 856 return err; 857} 858 859const struct got_error * 860got_diff_objects_as_commits(off_t **line_offsets, size_t *nlines, 861 struct got_object_id *id1, struct got_object_id *id2, 862 int diff_context, int ignore_whitespace, 863 struct got_repository *repo, FILE *outfile) 864{ 865 const struct got_error *err; 866 struct got_commit_object *commit1 = NULL, *commit2 = NULL; 867 868 if (id2 == NULL) 869 return got_error(GOT_ERR_NO_OBJ); 870 871 if (id1) { 872 err = got_object_open_as_commit(&commit1, repo, id1); 873 if (err) 874 goto done; 875 } 876 877 err = got_object_open_as_commit(&commit2, repo, id2); 878 if (err) 879 goto done; 880 881 err = got_diff_objects_as_trees(line_offsets, nlines, 882 commit1 ? got_object_commit_get_tree_id(commit1) : NULL, 883 got_object_commit_get_tree_id(commit2), "", "", diff_context, 884 ignore_whitespace, repo, outfile); 885done: 886 if (commit1) 887 got_object_commit_close(commit1); 888 if (commit2) 889 got_object_commit_close(commit2); 890 return err; 891} 892 893const struct got_error * 894got_diff_files(struct got_diffreg_result **resultp, 895 FILE *f1, const char *label1, FILE *f2, const char *label2, 896 int diff_context, int ignore_whitespace, FILE *outfile) 897{ 898 const struct got_error *err = NULL; 899 struct got_diffreg_result *diffreg_result = NULL; 900 901 if (resultp) 902 *resultp = NULL; 903 904 if (outfile) { 905 fprintf(outfile, "file - %s\n", 906 f1 == NULL ? "/dev/null" : label1); 907 fprintf(outfile, "file + %s\n", 908 f2 == NULL ? "/dev/null" : label2); 909 } 910 911 err = got_diffreg(&diffreg_result, f1, f2, GOT_DIFF_ALGORITHM_MYERS, 912 ignore_whitespace); 913 if (err) 914 goto done; 915 916 if (outfile) { 917 err = got_diffreg_output(NULL, NULL, diffreg_result, 918 f1, f2, label1, label2, GOT_DIFF_OUTPUT_UNIDIFF, 919 diff_context, outfile); 920 if (err) 921 goto done; 922 } 923 924done: 925 if (resultp && err == NULL) 926 *resultp = diffreg_result; 927 else if (diffreg_result) { 928 const struct got_error *free_err; 929 free_err = got_diffreg_result_free(diffreg_result); 930 if (free_err && err == NULL) 931 err = free_err; 932 } 933 934 return err; 935} 936