]> git.proxmox.com Git - libgit2.git/blame - src/diff_print.c
New upstream version 1.4.3+dfsg.1
[libgit2.git] / src / diff_print.c
CommitLineData
7000f3fa
RB
1/*
2 * Copyright (C) the libgit2 contributors. All rights reserved.
3 *
4 * This file is part of libgit2, distributed under the GNU GPL v2 with
5 * a Linking Exception. For full terms see the included COPYING file.
6 */
eae0bfdc 7
7000f3fa 8#include "common.h"
eae0bfdc 9
e579e0f7 10#include "buf.h"
7000f3fa 11#include "diff.h"
804d5fe9 12#include "diff_file.h"
8d44f8b7 13#include "patch_generate.h"
22a2d3d5 14#include "futils.h"
e349ed50
ET
15#include "zstream.h"
16#include "blob.h"
17#include "delta.h"
27e54bcf 18#include "git2/sys/diff.h"
7000f3fa
RB
19
20typedef struct {
10672e3e 21 git_diff_format_t format;
3ff1d123 22 git_diff_line_cb print_cb;
7000f3fa 23 void *payload;
804d5fe9 24
e579e0f7 25 git_str *buf;
804d5fe9
ET
26 git_diff_line line;
27
28 const char *old_prefix;
29 const char *new_prefix;
10672e3e 30 uint32_t flags;
040ec883 31 int id_strlen;
804d5fe9
ET
32
33 int (*strcomp)(const char *, const char *);
7000f3fa
RB
34} diff_print_info;
35
8147b1af 36static int diff_print_info_init__common(
7000f3fa 37 diff_print_info *pi,
e579e0f7 38 git_str *out,
8147b1af 39 git_repository *repo,
10672e3e
RB
40 git_diff_format_t format,
41 git_diff_line_cb cb,
42 void *payload)
7000f3fa 43{
8147b1af 44 pi->format = format;
7000f3fa 45 pi->print_cb = cb;
8147b1af
ET
46 pi->payload = payload;
47 pi->buf = out;
48
040ec883 49 if (!pi->id_strlen) {
8147b1af 50 if (!repo)
040ec883 51 pi->id_strlen = GIT_ABBREV_DEFAULT;
22a2d3d5 52 else if (git_repository__configmap_lookup(&pi->id_strlen, repo, GIT_CONFIGMAP_ABBREV) < 0)
8147b1af
ET
53 return -1;
54 }
7000f3fa 55
040ec883
ET
56 if (pi->id_strlen > GIT_OID_HEXSZ)
57 pi->id_strlen = GIT_OID_HEXSZ;
7000f3fa 58
3b5f7954
RB
59 memset(&pi->line, 0, sizeof(pi->line));
60 pi->line.old_lineno = -1;
61 pi->line.new_lineno = -1;
8147b1af 62 pi->line.num_lines = 1;
3b5f7954 63
7000f3fa
RB
64 return 0;
65}
66
8147b1af
ET
67static int diff_print_info_init_fromdiff(
68 diff_print_info *pi,
e579e0f7 69 git_str *out,
8147b1af
ET
70 git_diff *diff,
71 git_diff_format_t format,
72 git_diff_line_cb cb,
73 void *payload)
74{
75 git_repository *repo = diff ? diff->repo : NULL;
76
77 memset(pi, 0, sizeof(diff_print_info));
78
8147b1af
ET
79 if (diff) {
80 pi->flags = diff->opts.flags;
040ec883 81 pi->id_strlen = diff->opts.id_abbrev;
804d5fe9
ET
82 pi->old_prefix = diff->opts.old_prefix;
83 pi->new_prefix = diff->opts.new_prefix;
84
85 pi->strcomp = diff->strcomp;
8147b1af
ET
86 }
87
88 return diff_print_info_init__common(pi, out, repo, format, cb, payload);
89}
90
91static int diff_print_info_init_frompatch(
92 diff_print_info *pi,
e579e0f7 93 git_str *out,
8147b1af
ET
94 git_patch *patch,
95 git_diff_format_t format,
96 git_diff_line_cb cb,
97 void *payload)
98{
c25aa7cd 99 GIT_ASSERT_ARG(patch);
be8479c9 100
8147b1af
ET
101 memset(pi, 0, sizeof(diff_print_info));
102
8147b1af 103 pi->flags = patch->diff_opts.flags;
040ec883 104 pi->id_strlen = patch->diff_opts.id_abbrev;
804d5fe9
ET
105 pi->old_prefix = patch->diff_opts.old_prefix;
106 pi->new_prefix = patch->diff_opts.new_prefix;
8147b1af 107
804d5fe9 108 return diff_print_info_init__common(pi, out, patch->repo, format, cb, payload);
8147b1af
ET
109}
110
a1683f28 111static char diff_pick_suffix(int mode)
7000f3fa
RB
112{
113 if (S_ISDIR(mode))
114 return '/';
a7fcc44d 115 else if (GIT_PERMS_IS_EXEC(mode)) /* -V536 */
7000f3fa
RB
116 /* in git, modes are very regular, so we must have 0100755 mode */
117 return '*';
118 else
119 return ' ';
120}
121
122char git_diff_status_char(git_delta_t status)
123{
124 char code;
125
126 switch (status) {
85b7268e
AR
127 case GIT_DELTA_ADDED: code = 'A'; break;
128 case GIT_DELTA_DELETED: code = 'D'; break;
129 case GIT_DELTA_MODIFIED: code = 'M'; break;
130 case GIT_DELTA_RENAMED: code = 'R'; break;
131 case GIT_DELTA_COPIED: code = 'C'; break;
132 case GIT_DELTA_IGNORED: code = 'I'; break;
133 case GIT_DELTA_UNTRACKED: code = '?'; break;
4b3ec53c 134 case GIT_DELTA_TYPECHANGE: code = 'T'; break;
85b7268e
AR
135 case GIT_DELTA_UNREADABLE: code = 'X'; break;
136 default: code = ' '; break;
7000f3fa
RB
137 }
138
139 return code;
140}
141
10672e3e
RB
142static int diff_print_one_name_only(
143 const git_diff_delta *delta, float progress, void *data)
144{
145 diff_print_info *pi = data;
e579e0f7 146 git_str *out = pi->buf;
10672e3e
RB
147
148 GIT_UNUSED(progress);
149
150 if ((pi->flags & GIT_DIFF_SHOW_UNMODIFIED) == 0 &&
151 delta->status == GIT_DELTA_UNMODIFIED)
152 return 0;
153
e579e0f7
MB
154 git_str_clear(out);
155 git_str_puts(out, delta->new_file.path);
156 git_str_putc(out, '\n');
157 if (git_str_oom(out))
25e0b157 158 return -1;
10672e3e 159
3b5f7954 160 pi->line.origin = GIT_DIFF_LINE_FILE_HDR;
e579e0f7
MB
161 pi->line.content = git_str_cstr(out);
162 pi->line.content_len = git_str_len(out);
3b5f7954 163
25e0b157 164 return pi->print_cb(delta, NULL, &pi->line, pi->payload);
10672e3e
RB
165}
166
167static int diff_print_one_name_status(
7000f3fa
RB
168 const git_diff_delta *delta, float progress, void *data)
169{
170 diff_print_info *pi = data;
e579e0f7 171 git_str *out = pi->buf;
7000f3fa 172 char old_suffix, new_suffix, code = git_diff_status_char(delta->status);
804d5fe9
ET
173 int(*strcomp)(const char *, const char *) = pi->strcomp ?
174 pi->strcomp : git__strcmp;
7000f3fa
RB
175
176 GIT_UNUSED(progress);
177
10672e3e 178 if ((pi->flags & GIT_DIFF_SHOW_UNMODIFIED) == 0 && code == ' ')
7000f3fa
RB
179 return 0;
180
a1683f28
RB
181 old_suffix = diff_pick_suffix(delta->old_file.mode);
182 new_suffix = diff_pick_suffix(delta->new_file.mode);
7000f3fa 183
e579e0f7 184 git_str_clear(out);
7000f3fa
RB
185
186 if (delta->old_file.path != delta->new_file.path &&
74ded024 187 strcomp(delta->old_file.path,delta->new_file.path) != 0)
e579e0f7 188 git_str_printf(out, "%c\t%s%c %s%c\n", code,
7000f3fa
RB
189 delta->old_file.path, old_suffix, delta->new_file.path, new_suffix);
190 else if (delta->old_file.mode != delta->new_file.mode &&
191 delta->old_file.mode != 0 && delta->new_file.mode != 0)
e579e0f7 192 git_str_printf(out, "%c\t%s%c %s%c\n", code,
df40f398 193 delta->old_file.path, old_suffix, delta->new_file.path, new_suffix);
7000f3fa 194 else if (old_suffix != ' ')
e579e0f7 195 git_str_printf(out, "%c\t%s%c\n", code, delta->old_file.path, old_suffix);
7000f3fa 196 else
e579e0f7
MB
197 git_str_printf(out, "%c\t%s\n", code, delta->old_file.path);
198 if (git_str_oom(out))
25e0b157 199 return -1;
7000f3fa 200
3b5f7954 201 pi->line.origin = GIT_DIFF_LINE_FILE_HDR;
e579e0f7
MB
202 pi->line.content = git_str_cstr(out);
203 pi->line.content_len = git_str_len(out);
3b5f7954 204
25e0b157 205 return pi->print_cb(delta, NULL, &pi->line, pi->payload);
7000f3fa
RB
206}
207
a1683f28 208static int diff_print_one_raw(
7000f3fa
RB
209 const git_diff_delta *delta, float progress, void *data)
210{
211 diff_print_info *pi = data;
e579e0f7 212 git_str *out = pi->buf;
d68cb736 213 int id_abbrev;
7000f3fa
RB
214 char code = git_diff_status_char(delta->status);
215 char start_oid[GIT_OID_HEXSZ+1], end_oid[GIT_OID_HEXSZ+1];
216
217 GIT_UNUSED(progress);
218
10672e3e 219 if ((pi->flags & GIT_DIFF_SHOW_UNMODIFIED) == 0 && code == ' ')
7000f3fa
RB
220 return 0;
221
e579e0f7 222 git_str_clear(out);
7000f3fa 223
d68cb736
ET
224 id_abbrev = delta->old_file.mode ? delta->old_file.id_abbrev :
225 delta->new_file.id_abbrev;
226
040ec883 227 if (pi->id_strlen > id_abbrev) {
ac3d33df 228 git_error_set(GIT_ERROR_PATCH,
909d5494 229 "the patch input contains %d id characters (cannot print %d)",
040ec883 230 id_abbrev, pi->id_strlen);
d68cb736
ET
231 return -1;
232 }
233
040ec883
ET
234 git_oid_tostr(start_oid, pi->id_strlen + 1, &delta->old_file.id);
235 git_oid_tostr(end_oid, pi->id_strlen + 1, &delta->new_file.id);
7000f3fa 236
e579e0f7 237 git_str_printf(
040ec883 238 out, (pi->id_strlen <= GIT_OID_HEXSZ) ?
12e422a0 239 ":%06o %06o %s... %s... %c" : ":%06o %06o %s %s %c",
7000f3fa
RB
240 delta->old_file.mode, delta->new_file.mode, start_oid, end_oid, code);
241
242 if (delta->similarity > 0)
e579e0f7 243 git_str_printf(out, "%03u", delta->similarity);
7000f3fa 244
e4acc3ba 245 if (delta->old_file.path != delta->new_file.path)
e579e0f7 246 git_str_printf(
a1683f28 247 out, "\t%s %s\n", delta->old_file.path, delta->new_file.path);
7000f3fa 248 else
e579e0f7 249 git_str_printf(
a1683f28 250 out, "\t%s\n", delta->old_file.path ?
7000f3fa
RB
251 delta->old_file.path : delta->new_file.path);
252
e579e0f7 253 if (git_str_oom(out))
25e0b157 254 return -1;
7000f3fa 255
3b5f7954 256 pi->line.origin = GIT_DIFF_LINE_FILE_HDR;
e579e0f7
MB
257 pi->line.content = git_str_cstr(out);
258 pi->line.content_len = git_str_len(out);
3b5f7954 259
25e0b157 260 return pi->print_cb(delta, NULL, &pi->line, pi->payload);
7000f3fa
RB
261}
262
e2cdc145 263static int diff_print_modes(
e579e0f7 264 git_str *out, const git_diff_delta *delta)
e2cdc145 265{
e579e0f7
MB
266 git_str_printf(out, "old mode %o\n", delta->old_file.mode);
267 git_str_printf(out, "new mode %o\n", delta->new_file.mode);
e2cdc145 268
e579e0f7 269 return git_str_oom(out) ? -1 : 0;
e2cdc145
ET
270}
271
197b8966 272static int diff_print_oid_range(
e579e0f7 273 git_str *out, const git_diff_delta *delta, int id_strlen,
22a2d3d5 274 bool print_index)
7000f3fa
RB
275{
276 char start_oid[GIT_OID_HEXSZ+1], end_oid[GIT_OID_HEXSZ+1];
277
d68cb736 278 if (delta->old_file.mode &&
040ec883 279 id_strlen > delta->old_file.id_abbrev) {
ac3d33df 280 git_error_set(GIT_ERROR_PATCH,
909d5494 281 "the patch input contains %d id characters (cannot print %d)",
040ec883 282 delta->old_file.id_abbrev, id_strlen);
d68cb736
ET
283 return -1;
284 }
285
286 if ((delta->new_file.mode &&
040ec883 287 id_strlen > delta->new_file.id_abbrev)) {
ac3d33df 288 git_error_set(GIT_ERROR_PATCH,
909d5494 289 "the patch input contains %d id characters (cannot print %d)",
040ec883 290 delta->new_file.id_abbrev, id_strlen);
d68cb736
ET
291 return -1;
292 }
293
040ec883
ET
294 git_oid_tostr(start_oid, id_strlen + 1, &delta->old_file.id);
295 git_oid_tostr(end_oid, id_strlen + 1, &delta->new_file.id);
7000f3fa 296
7000f3fa 297 if (delta->old_file.mode == delta->new_file.mode) {
22a2d3d5 298 if (print_index)
e579e0f7 299 git_str_printf(out, "index %s..%s %o\n",
22a2d3d5 300 start_oid, end_oid, delta->old_file.mode);
7000f3fa 301 } else {
e2cdc145 302 if (delta->old_file.mode == 0)
e579e0f7 303 git_str_printf(out, "new file mode %o\n", delta->new_file.mode);
e2cdc145 304 else if (delta->new_file.mode == 0)
e579e0f7 305 git_str_printf(out, "deleted file mode %o\n", delta->old_file.mode);
e2cdc145
ET
306 else
307 diff_print_modes(out, delta);
308
22a2d3d5 309 if (print_index)
e579e0f7 310 git_str_printf(out, "index %s..%s\n", start_oid, end_oid);
7000f3fa
RB
311 }
312
e579e0f7 313 return git_str_oom(out) ? -1 : 0;
7000f3fa
RB
314}
315
4ac2d8ac 316static int diff_delta_format_path(
e579e0f7 317 git_str *out, const char *prefix, const char *filename)
4ac2d8ac 318{
e579e0f7
MB
319 if (!filename) {
320 /* don't prefix "/dev/null" */
321 return git_str_puts(out, "/dev/null");
322 }
323
324 if (git_str_joinpath(out, prefix, filename) < 0)
4ac2d8ac
ET
325 return -1;
326
e579e0f7 327 return git_str_quote(out);
4ac2d8ac
ET
328}
329
eb1c1707 330static int diff_delta_format_with_paths(
e579e0f7 331 git_str *out,
197b8966 332 const git_diff_delta *delta,
4ac2d8ac
ET
333 const char *template,
334 const char *oldpath,
335 const char *newpath)
7000f3fa 336{
22a2d3d5 337 if (git_oid_is_zero(&delta->old_file.id))
eb1c1707 338 oldpath = "/dev/null";
4ac2d8ac 339
22a2d3d5 340 if (git_oid_is_zero(&delta->new_file.id))
eb1c1707 341 newpath = "/dev/null";
eb1c1707 342
e579e0f7 343 return git_str_printf(out, template, oldpath, newpath);
eb1c1707
RB
344}
345
22a2d3d5 346static int diff_delta_format_similarity_header(
e579e0f7 347 git_str *out,
19e46645
ET
348 const git_diff_delta *delta)
349{
e579e0f7 350 git_str old_path = GIT_STR_INIT, new_path = GIT_STR_INIT;
1a79cd95 351 const char *type;
4ac2d8ac
ET
352 int error = 0;
353
19e46645 354 if (delta->similarity > 100) {
ac3d33df 355 git_error_set(GIT_ERROR_PATCH, "invalid similarity %d", delta->similarity);
4ac2d8ac
ET
356 error = -1;
357 goto done;
19e46645
ET
358 }
359
22a2d3d5 360 GIT_ASSERT(delta->status == GIT_DELTA_RENAMED || delta->status == GIT_DELTA_COPIED);
1a79cd95
ET
361 if (delta->status == GIT_DELTA_RENAMED)
362 type = "rename";
1a79cd95 363 else
22a2d3d5 364 type = "copy";
1a79cd95 365
e579e0f7
MB
366 if ((error = git_str_puts(&old_path, delta->old_file.path)) < 0 ||
367 (error = git_str_puts(&new_path, delta->new_file.path)) < 0 ||
368 (error = git_str_quote(&old_path)) < 0 ||
369 (error = git_str_quote(&new_path)) < 0)
4ac2d8ac
ET
370 goto done;
371
e579e0f7 372 git_str_printf(out,
19e46645 373 "similarity index %d%%\n"
1a79cd95
ET
374 "%s from %s\n"
375 "%s to %s\n",
19e46645 376 delta->similarity,
1a79cd95
ET
377 type, old_path.ptr,
378 type, new_path.ptr);
19e46645 379
e579e0f7 380 if (git_str_oom(out))
4ac2d8ac
ET
381 error = -1;
382
383done:
e579e0f7
MB
384 git_str_dispose(&old_path);
385 git_str_dispose(&new_path);
4ac2d8ac
ET
386
387 return error;
19e46645
ET
388}
389
1a79cd95
ET
390static bool delta_is_unchanged(const git_diff_delta *delta)
391{
22a2d3d5
UG
392 if (git_oid_is_zero(&delta->old_file.id) &&
393 git_oid_is_zero(&delta->new_file.id))
1a79cd95
ET
394 return true;
395
396 if (delta->old_file.mode == GIT_FILEMODE_COMMIT ||
397 delta->new_file.mode == GIT_FILEMODE_COMMIT)
398 return false;
399
400 if (git_oid_equal(&delta->old_file.id, &delta->new_file.id))
401 return true;
402
403 return false;
404}
405
eb1c1707 406int git_diff_delta__format_file_header(
e579e0f7 407 git_str *out,
eb1c1707
RB
408 const git_diff_delta *delta,
409 const char *oldpfx,
410 const char *newpfx,
22a2d3d5
UG
411 int id_strlen,
412 bool print_index)
eb1c1707 413{
e579e0f7 414 git_str old_path = GIT_STR_INIT, new_path = GIT_STR_INIT;
1a79cd95 415 bool unchanged = delta_is_unchanged(delta);
4ac2d8ac 416 int error = 0;
72806f4c 417
7000f3fa
RB
418 if (!oldpfx)
419 oldpfx = DIFF_OLD_PREFIX_DEFAULT;
7000f3fa
RB
420 if (!newpfx)
421 newpfx = DIFF_NEW_PREFIX_DEFAULT;
040ec883
ET
422 if (!id_strlen)
423 id_strlen = GIT_ABBREV_DEFAULT;
7000f3fa 424
4ac2d8ac
ET
425 if ((error = diff_delta_format_path(
426 &old_path, oldpfx, delta->old_file.path)) < 0 ||
427 (error = diff_delta_format_path(
428 &new_path, newpfx, delta->new_file.path)) < 0)
429 goto done;
430
e579e0f7 431 git_str_clear(out);
197b8966 432
e579e0f7 433 git_str_printf(out, "diff --git %s %s\n",
4ac2d8ac 434 old_path.ptr, new_path.ptr);
7000f3fa 435
22a2d3d5
UG
436 if (unchanged && delta->old_file.mode != delta->new_file.mode)
437 diff_print_modes(out, delta);
438
1a79cd95 439 if (delta->status == GIT_DELTA_RENAMED ||
22a2d3d5 440 (delta->status == GIT_DELTA_COPIED && unchanged)) {
1a79cd95 441 if ((error = diff_delta_format_similarity_header(out, delta)) < 0)
4ac2d8ac
ET
442 goto done;
443 }
19e46645 444
e2cdc145 445 if (!unchanged) {
22a2d3d5
UG
446 if ((error = diff_print_oid_range(out, delta,
447 id_strlen, print_index)) < 0)
4ac2d8ac 448 goto done;
7000f3fa 449
72806f4c 450 if ((delta->flags & GIT_DIFF_FLAG_BINARY) == 0)
4ac2d8ac
ET
451 diff_delta_format_with_paths(out, delta,
452 "--- %s\n+++ %s\n", old_path.ptr, new_path.ptr);
72806f4c 453 }
7000f3fa 454
e579e0f7 455 if (git_str_oom(out))
4ac2d8ac
ET
456 error = -1;
457
458done:
e579e0f7
MB
459 git_str_dispose(&old_path);
460 git_str_dispose(&new_path);
4ac2d8ac
ET
461
462 return error;
197b8966 463}
7000f3fa 464
8147b1af
ET
465static int format_binary(
466 diff_print_info *pi,
467 git_diff_binary_t type,
468 const char *data,
469 size_t datalen,
470 size_t inflatedlen)
e349ed50 471{
8147b1af
ET
472 const char *typename = type == GIT_DIFF_BINARY_DELTA ?
473 "delta" : "literal";
474 const char *scan, *end;
e349ed50 475
e579e0f7 476 git_str_printf(pi->buf, "%s %" PRIuZ "\n", typename, inflatedlen);
e349ed50
ET
477 pi->line.num_lines++;
478
8147b1af 479 for (scan = data, end = data + datalen; scan < end; ) {
947a58c1
RB
480 size_t chunk_len = end - scan;
481 if (chunk_len > 52)
482 chunk_len = 52;
e349ed50
ET
483
484 if (chunk_len <= 26)
e579e0f7 485 git_str_putc(pi->buf, (char)chunk_len + 'A' - 1);
e349ed50 486 else
e579e0f7 487 git_str_putc(pi->buf, (char)chunk_len - 26 + 'a' - 1);
e349ed50 488
e579e0f7
MB
489 git_str_encode_base85(pi->buf, scan, chunk_len);
490 git_str_putc(pi->buf, '\n');
e349ed50 491
e579e0f7 492 if (git_str_oom(pi->buf))
8147b1af 493 return -1;
e349ed50 494
947a58c1 495 scan += chunk_len;
e349ed50
ET
496 pi->line.num_lines++;
497 }
e579e0f7 498 git_str_putc(pi->buf, '\n');
e349ed50 499
e579e0f7 500 if (git_str_oom(pi->buf))
22a2d3d5
UG
501 return -1;
502
8147b1af
ET
503 return 0;
504}
e349ed50 505
4ac2d8ac
ET
506static int diff_print_patch_file_binary_noshow(
507 diff_print_info *pi, git_diff_delta *delta,
f941f035 508 const char *old_pfx, const char *new_pfx)
4ac2d8ac 509{
e579e0f7 510 git_str old_path = GIT_STR_INIT, new_path = GIT_STR_INIT;
4ac2d8ac
ET
511 int error;
512
22a2d3d5
UG
513 if ((error = diff_delta_format_path(&old_path, old_pfx, delta->old_file.path)) < 0 ||
514 (error = diff_delta_format_path(&new_path, new_pfx, delta->new_file.path)) < 0 ||
515 (error = diff_delta_format_with_paths(pi->buf, delta, "Binary files %s and %s differ\n",
516 old_path.ptr, new_path.ptr)) < 0)
4ac2d8ac
ET
517 goto done;
518
4ac2d8ac 519 pi->line.num_lines = 1;
4ac2d8ac
ET
520
521done:
e579e0f7
MB
522 git_str_dispose(&old_path);
523 git_str_dispose(&new_path);
4ac2d8ac
ET
524 return error;
525}
526
e349ed50 527static int diff_print_patch_file_binary(
8147b1af
ET
528 diff_print_info *pi, git_diff_delta *delta,
529 const char *old_pfx, const char *new_pfx,
530 const git_diff_binary *binary)
e349ed50 531{
947a58c1 532 size_t pre_binary_size;
8147b1af 533 int error;
e349ed50 534
f4e3dae7
ET
535 if (delta->status == GIT_DELTA_UNMODIFIED)
536 return 0;
537
adedac5a 538 if ((pi->flags & GIT_DIFF_SHOW_BINARY) == 0 || !binary->contains_data)
4ac2d8ac 539 return diff_print_patch_file_binary_noshow(
f941f035 540 pi, delta, old_pfx, new_pfx);
e349ed50 541
947a58c1 542 pre_binary_size = pi->buf->size;
e579e0f7 543 git_str_printf(pi->buf, "GIT binary patch\n");
e349ed50
ET
544 pi->line.num_lines++;
545
8147b1af 546 if ((error = format_binary(pi, binary->new_file.type, binary->new_file.data,
22a2d3d5
UG
547 binary->new_file.datalen, binary->new_file.inflatedlen)) < 0 ||
548 (error = format_binary(pi, binary->old_file.type, binary->old_file.data,
549 binary->old_file.datalen, binary->old_file.inflatedlen)) < 0) {
947a58c1 550 if (error == GIT_EBUFS) {
ac3d33df 551 git_error_clear();
e579e0f7 552 git_str_truncate(pi->buf, pre_binary_size);
4ac2d8ac
ET
553
554 return diff_print_patch_file_binary_noshow(
f941f035 555 pi, delta, old_pfx, new_pfx);
947a58c1
RB
556 }
557 }
e349ed50
ET
558
559 pi->line.num_lines++;
e349ed50
ET
560 return error;
561}
562
197b8966
RB
563static int diff_print_patch_file(
564 const git_diff_delta *delta, float progress, void *data)
565{
25e0b157 566 int error;
197b8966 567 diff_print_info *pi = data;
eb1c1707 568 const char *oldpfx =
804d5fe9 569 pi->old_prefix ? pi->old_prefix : DIFF_OLD_PREFIX_DEFAULT;
eb1c1707 570 const char *newpfx =
804d5fe9 571 pi->new_prefix ? pi->new_prefix : DIFF_NEW_PREFIX_DEFAULT;
197b8966 572
8147b1af
ET
573 bool binary = (delta->flags & GIT_DIFF_FLAG_BINARY) ||
574 (pi->flags & GIT_DIFF_FORCE_BINARY);
e349ed50 575 bool show_binary = !!(pi->flags & GIT_DIFF_SHOW_BINARY);
adedac5a 576 int id_strlen = pi->id_strlen;
22a2d3d5 577 bool print_index = (pi->format != GIT_DIFF_FORMAT_PATCH_ID);
adedac5a
ET
578
579 if (binary && show_binary)
580 id_strlen = delta->old_file.id_abbrev ? delta->old_file.id_abbrev :
581 delta->new_file.id_abbrev;
e349ed50 582
197b8966 583 GIT_UNUSED(progress);
7000f3fa 584
197b8966 585 if (S_ISDIR(delta->new_file.mode) ||
22a2d3d5
UG
586 delta->status == GIT_DELTA_UNMODIFIED ||
587 delta->status == GIT_DELTA_IGNORED ||
588 delta->status == GIT_DELTA_UNREADABLE ||
589 (delta->status == GIT_DELTA_UNTRACKED &&
10672e3e 590 (pi->flags & GIT_DIFF_SHOW_UNTRACKED_CONTENT) == 0))
7000f3fa
RB
591 return 0;
592
22a2d3d5
UG
593 if ((error = git_diff_delta__format_file_header(pi->buf, delta, oldpfx, newpfx,
594 id_strlen, print_index)) < 0)
25e0b157 595 return error;
7000f3fa 596
3b5f7954 597 pi->line.origin = GIT_DIFF_LINE_FILE_HDR;
e579e0f7
MB
598 pi->line.content = git_str_cstr(pi->buf);
599 pi->line.content_len = git_str_len(pi->buf);
3b5f7954 600
8147b1af
ET
601 return pi->print_cb(delta, NULL, &pi->line, pi->payload);
602}
eb1c1707 603
8147b1af
ET
604static int diff_print_patch_binary(
605 const git_diff_delta *delta,
606 const git_diff_binary *binary,
607 void *data)
608{
609 diff_print_info *pi = data;
610 const char *old_pfx =
804d5fe9 611 pi->old_prefix ? pi->old_prefix : DIFF_OLD_PREFIX_DEFAULT;
8147b1af 612 const char *new_pfx =
804d5fe9 613 pi->new_prefix ? pi->new_prefix : DIFF_NEW_PREFIX_DEFAULT;
8147b1af 614 int error;
eb1c1707 615
e579e0f7 616 git_str_clear(pi->buf);
eb1c1707 617
8147b1af
ET
618 if ((error = diff_print_patch_file_binary(
619 pi, (git_diff_delta *)delta, old_pfx, new_pfx, binary)) < 0)
25e0b157 620 return error;
eb1c1707 621
8147b1af 622 pi->line.origin = GIT_DIFF_LINE_BINARY;
e579e0f7
MB
623 pi->line.content = git_str_cstr(pi->buf);
624 pi->line.content_len = git_str_len(pi->buf);
3b5f7954 625
25e0b157 626 return pi->print_cb(delta, NULL, &pi->line, pi->payload);
7000f3fa
RB
627}
628
a1683f28 629static int diff_print_patch_hunk(
7000f3fa 630 const git_diff_delta *d,
3b5f7954 631 const git_diff_hunk *h,
7000f3fa
RB
632 void *data)
633{
634 diff_print_info *pi = data;
635
636 if (S_ISDIR(d->new_file.mode))
637 return 0;
638
3b5f7954
RB
639 pi->line.origin = GIT_DIFF_LINE_HUNK_HDR;
640 pi->line.content = h->header;
641 pi->line.content_len = h->header_len;
7000f3fa 642
25e0b157 643 return pi->print_cb(d, h, &pi->line, pi->payload);
7000f3fa
RB
644}
645
a1683f28 646static int diff_print_patch_line(
7000f3fa 647 const git_diff_delta *delta,
3b5f7954
RB
648 const git_diff_hunk *hunk,
649 const git_diff_line *line,
7000f3fa
RB
650 void *data)
651{
652 diff_print_info *pi = data;
653
654 if (S_ISDIR(delta->new_file.mode))
655 return 0;
656
25e0b157 657 return pi->print_cb(delta, hunk, line, pi->payload);
7000f3fa
RB
658}
659
10672e3e
RB
660/* print a git_diff to an output callback */
661int git_diff_print(
3ff1d123 662 git_diff *diff,
10672e3e 663 git_diff_format_t format,
3ff1d123 664 git_diff_line_cb print_cb,
7000f3fa
RB
665 void *payload)
666{
667 int error;
e579e0f7 668 git_str buf = GIT_STR_INIT;
7000f3fa 669 diff_print_info pi;
10672e3e 670 git_diff_file_cb print_file = NULL;
8147b1af 671 git_diff_binary_cb print_binary = NULL;
10672e3e
RB
672 git_diff_hunk_cb print_hunk = NULL;
673 git_diff_line_cb print_line = NULL;
674
675 switch (format) {
676 case GIT_DIFF_FORMAT_PATCH:
677 print_file = diff_print_patch_file;
8147b1af 678 print_binary = diff_print_patch_binary;
10672e3e
RB
679 print_hunk = diff_print_patch_hunk;
680 print_line = diff_print_patch_line;
681 break;
22a2d3d5
UG
682 case GIT_DIFF_FORMAT_PATCH_ID:
683 print_file = diff_print_patch_file;
684 print_binary = diff_print_patch_binary;
685 print_line = diff_print_patch_line;
686 break;
10672e3e
RB
687 case GIT_DIFF_FORMAT_PATCH_HEADER:
688 print_file = diff_print_patch_file;
689 break;
690 case GIT_DIFF_FORMAT_RAW:
691 print_file = diff_print_one_raw;
692 break;
693 case GIT_DIFF_FORMAT_NAME_ONLY:
694 print_file = diff_print_one_name_only;
695 break;
696 case GIT_DIFF_FORMAT_NAME_STATUS:
697 print_file = diff_print_one_name_status;
698 break;
699 default:
ac3d33df 700 git_error_set(GIT_ERROR_INVALID, "unknown diff output format (%d)", format);
10672e3e
RB
701 return -1;
702 }
7000f3fa 703
22a2d3d5
UG
704 if ((error = diff_print_info_init_fromdiff(&pi, &buf, diff, format, print_cb, payload)) < 0)
705 goto out;
7000f3fa 706
22a2d3d5
UG
707 if ((error = git_diff_foreach(diff, print_file, print_binary, print_hunk, print_line, &pi)) != 0) {
708 git_error_set_after_callback_function(error, "git_diff_print");
709 goto out;
96869a4e
RB
710 }
711
22a2d3d5 712out:
e579e0f7 713 git_str_dispose(&buf);
7000f3fa
RB
714 return error;
715}
716
27e54bcf 717int git_diff_print_callback__to_buf(
a1683f28 718 const git_diff_delta *delta,
3b5f7954
RB
719 const git_diff_hunk *hunk,
720 const git_diff_line *line,
a1683f28
RB
721 void *payload)
722{
e579e0f7 723 git_str *output = payload;
3b5f7954
RB
724 GIT_UNUSED(delta); GIT_UNUSED(hunk);
725
27e54bcf 726 if (!output) {
ac3d33df 727 git_error_set(GIT_ERROR_INVALID, "buffer pointer must be provided");
27e54bcf
RB
728 return -1;
729 }
730
3b5f7954 731 if (line->origin == GIT_DIFF_LINE_ADDITION ||
22a2d3d5
UG
732 line->origin == GIT_DIFF_LINE_DELETION ||
733 line->origin == GIT_DIFF_LINE_CONTEXT)
e579e0f7 734 git_str_putc(output, line->origin);
3b5f7954 735
e579e0f7 736 return git_str_put(output, line->content, line->content_len);
a1683f28
RB
737}
738
27e54bcf
RB
739int git_diff_print_callback__to_file_handle(
740 const git_diff_delta *delta,
741 const git_diff_hunk *hunk,
742 const git_diff_line *line,
743 void *payload)
744{
745 FILE *fp = payload ? payload : stdout;
22a2d3d5 746 int error;
27e54bcf 747
22a2d3d5
UG
748 GIT_UNUSED(delta);
749 GIT_UNUSED(hunk);
27e54bcf
RB
750
751 if (line->origin == GIT_DIFF_LINE_CONTEXT ||
22a2d3d5
UG
752 line->origin == GIT_DIFF_LINE_ADDITION ||
753 line->origin == GIT_DIFF_LINE_DELETION) {
754 while ((error = fputc(line->origin, fp)) == EINTR)
755 continue;
756 if (error) {
757 git_error_set(GIT_ERROR_OS, "could not write status");
758 return -1;
759 }
760 }
761
762 if (fwrite(line->content, line->content_len, 1, fp) != 1) {
763 git_error_set(GIT_ERROR_OS, "could not write line");
764 return -1;
765 }
766
27e54bcf
RB
767 return 0;
768}
769
e579e0f7 770/* print a git_diff to a git_str */
72827490
ET
771int git_diff_to_buf(git_buf *out, git_diff *diff, git_diff_format_t format)
772{
e579e0f7 773 git_str str = GIT_STR_INIT;
c25aa7cd
PP
774 int error;
775
776 GIT_ASSERT_ARG(out);
777 GIT_ASSERT_ARG(diff);
778
e579e0f7
MB
779 if ((error = git_buf_tostr(&str, out)) < 0 ||
780 (error = git_diff_print(diff, format, git_diff_print_callback__to_buf, &str)) < 0)
781 goto done;
782
783 error = git_buf_fromstr(out, &str);
c25aa7cd 784
e579e0f7
MB
785done:
786 git_str_dispose(&str);
787 return error;
72827490
ET
788}
789
804d5fe9
ET
790/* print a git_patch to an output callback */
791int git_patch_print(
792 git_patch *patch,
793 git_diff_line_cb print_cb,
794 void *payload)
795{
e579e0f7 796 git_str temp = GIT_STR_INIT;
804d5fe9 797 diff_print_info pi;
22a2d3d5 798 int error;
804d5fe9 799
c25aa7cd
PP
800 GIT_ASSERT_ARG(patch);
801 GIT_ASSERT_ARG(print_cb);
804d5fe9 802
22a2d3d5
UG
803 if ((error = diff_print_info_init_frompatch(&pi, &temp, patch,
804 GIT_DIFF_FORMAT_PATCH, print_cb, payload)) < 0)
805 goto out;
806
807 if ((error = git_patch__invoke_callbacks(patch, diff_print_patch_file, diff_print_patch_binary,
808 diff_print_patch_hunk, diff_print_patch_line, &pi)) < 0) {
809 git_error_set_after_callback_function(error, "git_patch_print");
810 goto out;
804d5fe9
ET
811 }
812
22a2d3d5 813out:
e579e0f7 814 git_str_dispose(&temp);
804d5fe9
ET
815 return error;
816}
817
e579e0f7 818/* print a git_patch to a git_str */
1e4976cb 819int git_patch_to_buf(git_buf *out, git_patch *patch)
450e8e9e 820{
e579e0f7
MB
821 GIT_BUF_WRAP_PRIVATE(out, git_patch__to_buf, patch);
822}
c25aa7cd 823
e579e0f7
MB
824int git_patch__to_buf(git_str *out, git_patch *patch)
825{
c25aa7cd
PP
826 GIT_ASSERT_ARG(out);
827 GIT_ASSERT_ARG(patch);
828
27e54bcf 829 return git_patch_print(patch, git_diff_print_callback__to_buf, out);
450e8e9e 830}