]> git.proxmox.com Git - libgit2.git/blame - src/diff_print.c
git_buf_quote: quote ugly characters
[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 */
7#include "common.h"
8#include "diff.h"
804d5fe9
ET
9#include "diff_file.h"
10#include "patch_diff.h"
f240acce 11#include "fileops.h"
e349ed50
ET
12#include "zstream.h"
13#include "blob.h"
14#include "delta.h"
27e54bcf 15#include "git2/sys/diff.h"
7000f3fa
RB
16
17typedef struct {
10672e3e 18 git_diff_format_t format;
3ff1d123 19 git_diff_line_cb print_cb;
7000f3fa 20 void *payload;
804d5fe9 21
7000f3fa 22 git_buf *buf;
804d5fe9
ET
23 git_diff_line line;
24
25 const char *old_prefix;
26 const char *new_prefix;
10672e3e 27 uint32_t flags;
7000f3fa 28 int oid_strlen;
804d5fe9
ET
29
30 int (*strcomp)(const char *, const char *);
7000f3fa
RB
31} diff_print_info;
32
8147b1af 33static int diff_print_info_init__common(
7000f3fa 34 diff_print_info *pi,
10672e3e 35 git_buf *out,
8147b1af 36 git_repository *repo,
10672e3e
RB
37 git_diff_format_t format,
38 git_diff_line_cb cb,
39 void *payload)
7000f3fa 40{
8147b1af 41 pi->format = format;
7000f3fa 42 pi->print_cb = cb;
8147b1af
ET
43 pi->payload = payload;
44 pi->buf = out;
45
46 if (!pi->oid_strlen) {
47 if (!repo)
48 pi->oid_strlen = GIT_ABBREV_DEFAULT;
49 else if (git_repository__cvar(&pi->oid_strlen, repo, GIT_CVAR_ABBREV) < 0)
50 return -1;
51 }
7000f3fa
RB
52
53 pi->oid_strlen += 1; /* for NUL byte */
54
8147b1af 55 if (pi->oid_strlen > GIT_OID_HEXSZ + 1)
7000f3fa
RB
56 pi->oid_strlen = GIT_OID_HEXSZ + 1;
57
3b5f7954
RB
58 memset(&pi->line, 0, sizeof(pi->line));
59 pi->line.old_lineno = -1;
60 pi->line.new_lineno = -1;
8147b1af 61 pi->line.num_lines = 1;
3b5f7954 62
7000f3fa
RB
63 return 0;
64}
65
8147b1af
ET
66static int diff_print_info_init_fromdiff(
67 diff_print_info *pi,
68 git_buf *out,
69 git_diff *diff,
70 git_diff_format_t format,
71 git_diff_line_cb cb,
72 void *payload)
73{
74 git_repository *repo = diff ? diff->repo : NULL;
75
76 memset(pi, 0, sizeof(diff_print_info));
77
8147b1af
ET
78 if (diff) {
79 pi->flags = diff->opts.flags;
80 pi->oid_strlen = diff->opts.id_abbrev;
804d5fe9
ET
81 pi->old_prefix = diff->opts.old_prefix;
82 pi->new_prefix = diff->opts.new_prefix;
83
84 pi->strcomp = diff->strcomp;
8147b1af
ET
85 }
86
87 return diff_print_info_init__common(pi, out, repo, format, cb, payload);
88}
89
90static int diff_print_info_init_frompatch(
91 diff_print_info *pi,
92 git_buf *out,
93 git_patch *patch,
94 git_diff_format_t format,
95 git_diff_line_cb cb,
96 void *payload)
97{
be8479c9
PS
98 assert(patch);
99
8147b1af
ET
100 memset(pi, 0, sizeof(diff_print_info));
101
8147b1af
ET
102 pi->flags = patch->diff_opts.flags;
103 pi->oid_strlen = patch->diff_opts.id_abbrev;
804d5fe9
ET
104 pi->old_prefix = patch->diff_opts.old_prefix;
105 pi->new_prefix = patch->diff_opts.new_prefix;
8147b1af 106
804d5fe9 107 return diff_print_info_init__common(pi, out, patch->repo, format, cb, payload);
8147b1af
ET
108}
109
a1683f28 110static char diff_pick_suffix(int mode)
7000f3fa
RB
111{
112 if (S_ISDIR(mode))
113 return '/';
a7fcc44d 114 else if (GIT_PERMS_IS_EXEC(mode)) /* -V536 */
7000f3fa
RB
115 /* in git, modes are very regular, so we must have 0100755 mode */
116 return '*';
117 else
118 return ' ';
119}
120
121char git_diff_status_char(git_delta_t status)
122{
123 char code;
124
125 switch (status) {
85b7268e
AR
126 case GIT_DELTA_ADDED: code = 'A'; break;
127 case GIT_DELTA_DELETED: code = 'D'; break;
128 case GIT_DELTA_MODIFIED: code = 'M'; break;
129 case GIT_DELTA_RENAMED: code = 'R'; break;
130 case GIT_DELTA_COPIED: code = 'C'; break;
131 case GIT_DELTA_IGNORED: code = 'I'; break;
132 case GIT_DELTA_UNTRACKED: code = '?'; break;
133 case GIT_DELTA_UNREADABLE: code = 'X'; break;
134 default: code = ' '; break;
7000f3fa
RB
135 }
136
137 return code;
138}
139
10672e3e
RB
140static int diff_print_one_name_only(
141 const git_diff_delta *delta, float progress, void *data)
142{
143 diff_print_info *pi = data;
144 git_buf *out = pi->buf;
145
146 GIT_UNUSED(progress);
147
148 if ((pi->flags & GIT_DIFF_SHOW_UNMODIFIED) == 0 &&
149 delta->status == GIT_DELTA_UNMODIFIED)
150 return 0;
151
152 git_buf_clear(out);
25e0b157
RB
153 git_buf_puts(out, delta->new_file.path);
154 git_buf_putc(out, '\n');
155 if (git_buf_oom(out))
156 return -1;
10672e3e 157
3b5f7954
RB
158 pi->line.origin = GIT_DIFF_LINE_FILE_HDR;
159 pi->line.content = git_buf_cstr(out);
160 pi->line.content_len = git_buf_len(out);
161
25e0b157 162 return pi->print_cb(delta, NULL, &pi->line, pi->payload);
10672e3e
RB
163}
164
165static int diff_print_one_name_status(
7000f3fa
RB
166 const git_diff_delta *delta, float progress, void *data)
167{
168 diff_print_info *pi = data;
a1683f28 169 git_buf *out = pi->buf;
7000f3fa 170 char old_suffix, new_suffix, code = git_diff_status_char(delta->status);
804d5fe9
ET
171 int(*strcomp)(const char *, const char *) = pi->strcomp ?
172 pi->strcomp : git__strcmp;
7000f3fa
RB
173
174 GIT_UNUSED(progress);
175
10672e3e 176 if ((pi->flags & GIT_DIFF_SHOW_UNMODIFIED) == 0 && code == ' ')
7000f3fa
RB
177 return 0;
178
a1683f28
RB
179 old_suffix = diff_pick_suffix(delta->old_file.mode);
180 new_suffix = diff_pick_suffix(delta->new_file.mode);
7000f3fa 181
a1683f28 182 git_buf_clear(out);
7000f3fa
RB
183
184 if (delta->old_file.path != delta->new_file.path &&
74ded024 185 strcomp(delta->old_file.path,delta->new_file.path) != 0)
df40f398 186 git_buf_printf(out, "%c\t%s%c %s%c\n", code,
7000f3fa
RB
187 delta->old_file.path, old_suffix, delta->new_file.path, new_suffix);
188 else if (delta->old_file.mode != delta->new_file.mode &&
189 delta->old_file.mode != 0 && delta->new_file.mode != 0)
df40f398
RB
190 git_buf_printf(out, "%c\t%s%c %s%c\n", code,
191 delta->old_file.path, old_suffix, delta->new_file.path, new_suffix);
7000f3fa 192 else if (old_suffix != ' ')
a1683f28 193 git_buf_printf(out, "%c\t%s%c\n", code, delta->old_file.path, old_suffix);
7000f3fa 194 else
a1683f28 195 git_buf_printf(out, "%c\t%s\n", code, delta->old_file.path);
a1683f28 196 if (git_buf_oom(out))
25e0b157 197 return -1;
7000f3fa 198
3b5f7954
RB
199 pi->line.origin = GIT_DIFF_LINE_FILE_HDR;
200 pi->line.content = git_buf_cstr(out);
201 pi->line.content_len = git_buf_len(out);
202
25e0b157 203 return pi->print_cb(delta, NULL, &pi->line, pi->payload);
7000f3fa
RB
204}
205
a1683f28 206static int diff_print_one_raw(
7000f3fa
RB
207 const git_diff_delta *delta, float progress, void *data)
208{
209 diff_print_info *pi = data;
a1683f28 210 git_buf *out = pi->buf;
d68cb736 211 int id_abbrev;
7000f3fa
RB
212 char code = git_diff_status_char(delta->status);
213 char start_oid[GIT_OID_HEXSZ+1], end_oid[GIT_OID_HEXSZ+1];
214
215 GIT_UNUSED(progress);
216
10672e3e 217 if ((pi->flags & GIT_DIFF_SHOW_UNMODIFIED) == 0 && code == ' ')
7000f3fa
RB
218 return 0;
219
a1683f28 220 git_buf_clear(out);
7000f3fa 221
d68cb736
ET
222 id_abbrev = delta->old_file.mode ? delta->old_file.id_abbrev :
223 delta->new_file.id_abbrev;
224
225 if (pi->oid_strlen - 1 > id_abbrev) {
226 giterr_set(GITERR_PATCH,
227 "The patch input contains %d id characters (cannot print %d)",
228 id_abbrev, pi->oid_strlen);
229 return -1;
230 }
231
9950bb4e
CMN
232 git_oid_tostr(start_oid, pi->oid_strlen, &delta->old_file.id);
233 git_oid_tostr(end_oid, pi->oid_strlen, &delta->new_file.id);
7000f3fa
RB
234
235 git_buf_printf(
12e422a0
RB
236 out, (pi->oid_strlen <= GIT_OID_HEXSZ) ?
237 ":%06o %06o %s... %s... %c" : ":%06o %06o %s %s %c",
7000f3fa
RB
238 delta->old_file.mode, delta->new_file.mode, start_oid, end_oid, code);
239
240 if (delta->similarity > 0)
a1683f28 241 git_buf_printf(out, "%03u", delta->similarity);
7000f3fa 242
e4acc3ba 243 if (delta->old_file.path != delta->new_file.path)
7000f3fa 244 git_buf_printf(
a1683f28 245 out, "\t%s %s\n", delta->old_file.path, delta->new_file.path);
7000f3fa
RB
246 else
247 git_buf_printf(
a1683f28 248 out, "\t%s\n", delta->old_file.path ?
7000f3fa
RB
249 delta->old_file.path : delta->new_file.path);
250
a1683f28 251 if (git_buf_oom(out))
25e0b157 252 return -1;
7000f3fa 253
3b5f7954
RB
254 pi->line.origin = GIT_DIFF_LINE_FILE_HDR;
255 pi->line.content = git_buf_cstr(out);
256 pi->line.content_len = git_buf_len(out);
257
25e0b157 258 return pi->print_cb(delta, NULL, &pi->line, pi->payload);
7000f3fa
RB
259}
260
197b8966
RB
261static int diff_print_oid_range(
262 git_buf *out, const git_diff_delta *delta, int oid_strlen)
7000f3fa
RB
263{
264 char start_oid[GIT_OID_HEXSZ+1], end_oid[GIT_OID_HEXSZ+1];
265
d68cb736
ET
266 if (delta->old_file.mode &&
267 oid_strlen - 1 > delta->old_file.id_abbrev) {
268 giterr_set(GITERR_PATCH,
269 "The patch input contains %d id characters (cannot print %d)",
270 delta->old_file.id_abbrev, oid_strlen);
271 return -1;
272 }
273
274 if ((delta->new_file.mode &&
275 oid_strlen - 1 > delta->new_file.id_abbrev)) {
276 giterr_set(GITERR_PATCH,
277 "The patch input contains %d id characters (cannot print %d)",
278 delta->new_file.id_abbrev, oid_strlen);
279 return -1;
280 }
281
9950bb4e
CMN
282 git_oid_tostr(start_oid, oid_strlen, &delta->old_file.id);
283 git_oid_tostr(end_oid, oid_strlen, &delta->new_file.id);
7000f3fa 284
7000f3fa 285 if (delta->old_file.mode == delta->new_file.mode) {
a1683f28 286 git_buf_printf(out, "index %s..%s %o\n",
7000f3fa
RB
287 start_oid, end_oid, delta->old_file.mode);
288 } else {
289 if (delta->old_file.mode == 0) {
a1683f28 290 git_buf_printf(out, "new file mode %o\n", delta->new_file.mode);
7000f3fa 291 } else if (delta->new_file.mode == 0) {
a1683f28 292 git_buf_printf(out, "deleted file mode %o\n", delta->old_file.mode);
7000f3fa 293 } else {
a1683f28
RB
294 git_buf_printf(out, "old mode %o\n", delta->old_file.mode);
295 git_buf_printf(out, "new mode %o\n", delta->new_file.mode);
7000f3fa 296 }
a1683f28 297 git_buf_printf(out, "index %s..%s\n", start_oid, end_oid);
7000f3fa
RB
298 }
299
25e0b157 300 return git_buf_oom(out) ? -1 : 0;
7000f3fa
RB
301}
302
eb1c1707 303static int diff_delta_format_with_paths(
197b8966
RB
304 git_buf *out,
305 const git_diff_delta *delta,
306 const char *oldpfx,
307 const char *newpfx,
eb1c1707 308 const char *template)
7000f3fa 309{
7000f3fa 310 const char *oldpath = delta->old_file.path;
7000f3fa 311 const char *newpath = delta->new_file.path;
7000f3fa 312
9950bb4e 313 if (git_oid_iszero(&delta->old_file.id)) {
eb1c1707
RB
314 oldpfx = "";
315 oldpath = "/dev/null";
316 }
9950bb4e 317 if (git_oid_iszero(&delta->new_file.id)) {
eb1c1707
RB
318 newpfx = "";
319 newpath = "/dev/null";
320 }
321
322 return git_buf_printf(out, template, oldpfx, oldpath, newpfx, newpath);
323}
324
19e46645
ET
325int diff_delta_format_rename_header(
326 git_buf *out,
327 const git_diff_delta *delta)
328{
329 if (delta->similarity > 100) {
330 giterr_set(GITERR_PATCH, "invalid similarity %d", delta->similarity);
331 return -1;
332 }
333
334 git_buf_printf(out,
335 "similarity index %d%%\n"
336 "rename from %s\n"
337 "rename to %s\n",
338 delta->similarity,
339 delta->old_file.path,
340 delta->new_file.path);
341
342 return git_buf_oom(out) ? -1 : 0;
343}
344
eb1c1707
RB
345int git_diff_delta__format_file_header(
346 git_buf *out,
347 const git_diff_delta *delta,
348 const char *oldpfx,
349 const char *newpfx,
350 int oid_strlen)
351{
72806f4c
ET
352 bool skip_index;
353
7000f3fa
RB
354 if (!oldpfx)
355 oldpfx = DIFF_OLD_PREFIX_DEFAULT;
7000f3fa
RB
356 if (!newpfx)
357 newpfx = DIFF_NEW_PREFIX_DEFAULT;
197b8966
RB
358 if (!oid_strlen)
359 oid_strlen = GIT_ABBREV_DEFAULT + 1;
7000f3fa 360
197b8966
RB
361 git_buf_clear(out);
362
363 git_buf_printf(out, "diff --git %s%s %s%s\n",
eb1c1707 364 oldpfx, delta->old_file.path, newpfx, delta->new_file.path);
7000f3fa 365
19e46645
ET
366 if (delta->status == GIT_DELTA_RENAMED)
367 GITERR_CHECK_ERROR(diff_delta_format_rename_header(out, delta));
368
72806f4c
ET
369 skip_index = (delta->status == GIT_DELTA_RENAMED &&
370 delta->similarity == 100 &&
371 delta->old_file.mode == 0 &&
372 delta->new_file.mode == 0);
373
374 if (!skip_index) {
375 GITERR_CHECK_ERROR(diff_print_oid_range(out, delta, oid_strlen));
7000f3fa 376
72806f4c
ET
377 if ((delta->flags & GIT_DIFF_FLAG_BINARY) == 0)
378 diff_delta_format_with_paths(
379 out, delta, oldpfx, newpfx, "--- %s%s\n+++ %s%s\n");
380 }
7000f3fa 381
197b8966
RB
382 return git_buf_oom(out) ? -1 : 0;
383}
7000f3fa 384
8147b1af
ET
385static int format_binary(
386 diff_print_info *pi,
387 git_diff_binary_t type,
388 const char *data,
389 size_t datalen,
390 size_t inflatedlen)
e349ed50 391{
8147b1af
ET
392 const char *typename = type == GIT_DIFF_BINARY_DELTA ?
393 "delta" : "literal";
394 const char *scan, *end;
e349ed50 395
49840056 396 git_buf_printf(pi->buf, "%s %" PRIuZ "\n", typename, inflatedlen);
e349ed50
ET
397 pi->line.num_lines++;
398
8147b1af 399 for (scan = data, end = data + datalen; scan < end; ) {
947a58c1
RB
400 size_t chunk_len = end - scan;
401 if (chunk_len > 52)
402 chunk_len = 52;
e349ed50
ET
403
404 if (chunk_len <= 26)
c6320bec 405 git_buf_putc(pi->buf, (char)chunk_len + 'A' - 1);
e349ed50 406 else
c6320bec 407 git_buf_putc(pi->buf, (char)chunk_len - 26 + 'a' - 1);
e349ed50 408
e003f83a 409 git_buf_encode_base85(pi->buf, scan, chunk_len);
e349ed50
ET
410 git_buf_putc(pi->buf, '\n');
411
8147b1af
ET
412 if (git_buf_oom(pi->buf))
413 return -1;
e349ed50 414
947a58c1 415 scan += chunk_len;
e349ed50
ET
416 pi->line.num_lines++;
417 }
e4b2b919 418 git_buf_putc(pi->buf, '\n');
e349ed50 419
8147b1af
ET
420 return 0;
421}
e349ed50 422
e349ed50 423static int diff_print_patch_file_binary(
8147b1af
ET
424 diff_print_info *pi, git_diff_delta *delta,
425 const char *old_pfx, const char *new_pfx,
426 const git_diff_binary *binary)
e349ed50 427{
947a58c1 428 size_t pre_binary_size;
8147b1af 429 int error;
e349ed50 430
947a58c1
RB
431 if ((pi->flags & GIT_DIFF_SHOW_BINARY) == 0)
432 goto noshow;
e349ed50 433
3149ff6f
ET
434 if (binary->new_file.datalen == 0 && binary->old_file.datalen == 0)
435 return 0;
436
947a58c1 437 pre_binary_size = pi->buf->size;
e349ed50
ET
438 git_buf_printf(pi->buf, "GIT binary patch\n");
439 pi->line.num_lines++;
440
8147b1af
ET
441 if ((error = format_binary(pi, binary->new_file.type, binary->new_file.data,
442 binary->new_file.datalen, binary->new_file.inflatedlen)) < 0 ||
8147b1af
ET
443 (error = format_binary(pi, binary->old_file.type, binary->old_file.data,
444 binary->old_file.datalen, binary->old_file.inflatedlen)) < 0) {
445
947a58c1
RB
446 if (error == GIT_EBUFS) {
447 giterr_clear();
448 git_buf_truncate(pi->buf, pre_binary_size);
449 goto noshow;
450 }
451 }
e349ed50
ET
452
453 pi->line.num_lines++;
e349ed50 454 return error;
947a58c1
RB
455
456noshow:
457 pi->line.num_lines = 1;
458 return diff_delta_format_with_paths(
8147b1af 459 pi->buf, delta, old_pfx, new_pfx,
947a58c1 460 "Binary files %s%s and %s%s differ\n");
e349ed50
ET
461}
462
197b8966
RB
463static int diff_print_patch_file(
464 const git_diff_delta *delta, float progress, void *data)
465{
25e0b157 466 int error;
197b8966 467 diff_print_info *pi = data;
eb1c1707 468 const char *oldpfx =
804d5fe9 469 pi->old_prefix ? pi->old_prefix : DIFF_OLD_PREFIX_DEFAULT;
eb1c1707 470 const char *newpfx =
804d5fe9 471 pi->new_prefix ? pi->new_prefix : DIFF_NEW_PREFIX_DEFAULT;
197b8966 472
8147b1af
ET
473 bool binary = (delta->flags & GIT_DIFF_FLAG_BINARY) ||
474 (pi->flags & GIT_DIFF_FORCE_BINARY);
e349ed50
ET
475 bool show_binary = !!(pi->flags & GIT_DIFF_SHOW_BINARY);
476 int oid_strlen = binary && show_binary ?
477 GIT_OID_HEXSZ + 1 : pi->oid_strlen;
478
197b8966 479 GIT_UNUSED(progress);
7000f3fa 480
197b8966
RB
481 if (S_ISDIR(delta->new_file.mode) ||
482 delta->status == GIT_DELTA_UNMODIFIED ||
483 delta->status == GIT_DELTA_IGNORED ||
61bef72d 484 delta->status == GIT_DELTA_UNREADABLE ||
197b8966 485 (delta->status == GIT_DELTA_UNTRACKED &&
10672e3e 486 (pi->flags & GIT_DIFF_SHOW_UNTRACKED_CONTENT) == 0))
7000f3fa
RB
487 return 0;
488
25e0b157 489 if ((error = git_diff_delta__format_file_header(
e349ed50 490 pi->buf, delta, oldpfx, newpfx, oid_strlen)) < 0)
25e0b157 491 return error;
7000f3fa 492
3b5f7954
RB
493 pi->line.origin = GIT_DIFF_LINE_FILE_HDR;
494 pi->line.content = git_buf_cstr(pi->buf);
495 pi->line.content_len = git_buf_len(pi->buf);
496
8147b1af
ET
497 return pi->print_cb(delta, NULL, &pi->line, pi->payload);
498}
eb1c1707 499
8147b1af
ET
500static int diff_print_patch_binary(
501 const git_diff_delta *delta,
502 const git_diff_binary *binary,
503 void *data)
504{
505 diff_print_info *pi = data;
506 const char *old_pfx =
804d5fe9 507 pi->old_prefix ? pi->old_prefix : DIFF_OLD_PREFIX_DEFAULT;
8147b1af 508 const char *new_pfx =
804d5fe9 509 pi->new_prefix ? pi->new_prefix : DIFF_NEW_PREFIX_DEFAULT;
8147b1af 510 int error;
eb1c1707
RB
511
512 git_buf_clear(pi->buf);
513
8147b1af
ET
514 if ((error = diff_print_patch_file_binary(
515 pi, (git_diff_delta *)delta, old_pfx, new_pfx, binary)) < 0)
25e0b157 516 return error;
eb1c1707 517
8147b1af
ET
518 pi->line.origin = GIT_DIFF_LINE_BINARY;
519 pi->line.content = git_buf_cstr(pi->buf);
3b5f7954 520 pi->line.content_len = git_buf_len(pi->buf);
3b5f7954 521
25e0b157 522 return pi->print_cb(delta, NULL, &pi->line, pi->payload);
7000f3fa
RB
523}
524
a1683f28 525static int diff_print_patch_hunk(
7000f3fa 526 const git_diff_delta *d,
3b5f7954 527 const git_diff_hunk *h,
7000f3fa
RB
528 void *data)
529{
530 diff_print_info *pi = data;
531
532 if (S_ISDIR(d->new_file.mode))
533 return 0;
534
3b5f7954
RB
535 pi->line.origin = GIT_DIFF_LINE_HUNK_HDR;
536 pi->line.content = h->header;
537 pi->line.content_len = h->header_len;
7000f3fa 538
25e0b157 539 return pi->print_cb(d, h, &pi->line, pi->payload);
7000f3fa
RB
540}
541
a1683f28 542static int diff_print_patch_line(
7000f3fa 543 const git_diff_delta *delta,
3b5f7954
RB
544 const git_diff_hunk *hunk,
545 const git_diff_line *line,
7000f3fa
RB
546 void *data)
547{
548 diff_print_info *pi = data;
549
550 if (S_ISDIR(delta->new_file.mode))
551 return 0;
552
25e0b157 553 return pi->print_cb(delta, hunk, line, pi->payload);
7000f3fa
RB
554}
555
10672e3e
RB
556/* print a git_diff to an output callback */
557int git_diff_print(
3ff1d123 558 git_diff *diff,
10672e3e 559 git_diff_format_t format,
3ff1d123 560 git_diff_line_cb print_cb,
7000f3fa
RB
561 void *payload)
562{
563 int error;
564 git_buf buf = GIT_BUF_INIT;
565 diff_print_info pi;
10672e3e 566 git_diff_file_cb print_file = NULL;
8147b1af 567 git_diff_binary_cb print_binary = NULL;
10672e3e
RB
568 git_diff_hunk_cb print_hunk = NULL;
569 git_diff_line_cb print_line = NULL;
570
571 switch (format) {
572 case GIT_DIFF_FORMAT_PATCH:
573 print_file = diff_print_patch_file;
8147b1af 574 print_binary = diff_print_patch_binary;
10672e3e
RB
575 print_hunk = diff_print_patch_hunk;
576 print_line = diff_print_patch_line;
577 break;
578 case GIT_DIFF_FORMAT_PATCH_HEADER:
579 print_file = diff_print_patch_file;
580 break;
581 case GIT_DIFF_FORMAT_RAW:
582 print_file = diff_print_one_raw;
583 break;
584 case GIT_DIFF_FORMAT_NAME_ONLY:
585 print_file = diff_print_one_name_only;
586 break;
587 case GIT_DIFF_FORMAT_NAME_STATUS:
588 print_file = diff_print_one_name_status;
589 break;
590 default:
591 giterr_set(GITERR_INVALID, "Unknown diff output format (%d)", format);
592 return -1;
593 }
7000f3fa 594
8147b1af
ET
595 if (!(error = diff_print_info_init_fromdiff(
596 &pi, &buf, diff, format, print_cb, payload))) {
7000f3fa 597 error = git_diff_foreach(
8147b1af 598 diff, print_file, print_binary, print_hunk, print_line, &pi);
7000f3fa 599
25e0b157 600 if (error) /* make sure error message is set */
26c1cb91 601 giterr_set_after_callback_function(error, "git_diff_print");
96869a4e
RB
602 }
603
7000f3fa
RB
604 git_buf_free(&buf);
605
606 return error;
607}
608
27e54bcf 609int git_diff_print_callback__to_buf(
a1683f28 610 const git_diff_delta *delta,
3b5f7954
RB
611 const git_diff_hunk *hunk,
612 const git_diff_line *line,
a1683f28
RB
613 void *payload)
614{
615 git_buf *output = payload;
3b5f7954
RB
616 GIT_UNUSED(delta); GIT_UNUSED(hunk);
617
27e54bcf
RB
618 if (!output) {
619 giterr_set(GITERR_INVALID, "Buffer pointer must be provided");
620 return -1;
621 }
622
3b5f7954
RB
623 if (line->origin == GIT_DIFF_LINE_ADDITION ||
624 line->origin == GIT_DIFF_LINE_DELETION ||
625 line->origin == GIT_DIFF_LINE_CONTEXT)
626 git_buf_putc(output, line->origin);
627
628 return git_buf_put(output, line->content, line->content_len);
a1683f28
RB
629}
630
27e54bcf
RB
631int git_diff_print_callback__to_file_handle(
632 const git_diff_delta *delta,
633 const git_diff_hunk *hunk,
634 const git_diff_line *line,
635 void *payload)
636{
637 FILE *fp = payload ? payload : stdout;
638
639 GIT_UNUSED(delta); GIT_UNUSED(hunk);
640
641 if (line->origin == GIT_DIFF_LINE_CONTEXT ||
642 line->origin == GIT_DIFF_LINE_ADDITION ||
643 line->origin == GIT_DIFF_LINE_DELETION)
644 fputc(line->origin, fp);
645 fwrite(line->content, 1, line->content_len, fp);
646 return 0;
647}
648
804d5fe9
ET
649/* print a git_patch to an output callback */
650int git_patch_print(
651 git_patch *patch,
652 git_diff_line_cb print_cb,
653 void *payload)
654{
655 int error;
656 git_buf temp = GIT_BUF_INIT;
657 diff_print_info pi;
658
659 assert(patch && print_cb);
660
661 if (!(error = diff_print_info_init_frompatch(
662 &pi, &temp, patch,
663 GIT_DIFF_FORMAT_PATCH, print_cb, payload)))
664 {
665 error = git_patch__invoke_callbacks(
666 patch,
667 diff_print_patch_file, diff_print_patch_binary,
668 diff_print_patch_hunk, diff_print_patch_line,
669 &pi);
670
671 if (error) /* make sure error message is set */
672 giterr_set_after_callback_function(error, "git_patch_print");
673 }
674
675 git_buf_free(&temp);
676
677 return error;
678}
679
450e8e9e 680/* print a git_patch to a git_buf */
1e4976cb 681int git_patch_to_buf(git_buf *out, git_patch *patch)
450e8e9e 682{
1e4976cb
RB
683 assert(out && patch);
684 git_buf_sanitize(out);
27e54bcf 685 return git_patch_print(patch, git_diff_print_callback__to_buf, out);
450e8e9e 686}