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