File Coverage

deps/libgit2/src/diff_print.c
Criterion Covered Total %
statement 296 395 74.9
branch 129 238 54.2
condition n/a
subroutine n/a
pod n/a
total 425 633 67.1


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