File Coverage

deps/libgit2/src/diff_print.c
Criterion Covered Total %
statement 296 403 73.4
branch 131 250 52.4
condition n/a
subroutine n/a
pod n/a
total 427 653 65.3


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           static 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         GIT_ASSERT(delta->status == GIT_DELTA_RENAMED || delta->status == GIT_DELTA_COPIED);
    0          
355 2 50         if (delta->status == GIT_DELTA_RENAMED)
356 2           type = "rename";
357             else
358 0           type = "copy";
359              
360 2 50         if ((error = git_buf_puts(&old_path, delta->old_file.path)) < 0 ||
    50          
361 2 50         (error = git_buf_puts(&new_path, delta->new_file.path)) < 0 ||
362 2 50         (error = git_buf_quote(&old_path)) < 0 ||
363             (error = git_buf_quote(&new_path)) < 0)
364             goto done;
365              
366 2           git_buf_printf(out,
367             "similarity index %d%%\n"
368             "%s from %s\n"
369             "%s to %s\n",
370 2           delta->similarity,
371             type, old_path.ptr,
372             type, new_path.ptr);
373              
374 2 50         if (git_buf_oom(out))
375 0           error = -1;
376              
377             done:
378 2           git_buf_dispose(&old_path);
379 2           git_buf_dispose(&new_path);
380              
381 2           return error;
382             }
383              
384 23           static bool delta_is_unchanged(const git_diff_delta *delta)
385             {
386 38           if (git_oid_is_zero(&delta->old_file.id) &&
387 15           git_oid_is_zero(&delta->new_file.id))
388 0           return true;
389              
390 23 50         if (delta->old_file.mode == GIT_FILEMODE_COMMIT ||
    50          
391 23           delta->new_file.mode == GIT_FILEMODE_COMMIT)
392 0           return false;
393              
394 23 100         if (git_oid_equal(&delta->old_file.id, &delta->new_file.id))
395 1           return true;
396              
397 22           return false;
398             }
399              
400 23           int git_diff_delta__format_file_header(
401             git_buf *out,
402             const git_diff_delta *delta,
403             const char *oldpfx,
404             const char *newpfx,
405             int id_strlen,
406             bool print_index)
407             {
408 23           git_buf old_path = GIT_BUF_INIT, new_path = GIT_BUF_INIT;
409 23           bool unchanged = delta_is_unchanged(delta);
410 23           int error = 0;
411              
412 23 50         if (!oldpfx)
413 0           oldpfx = DIFF_OLD_PREFIX_DEFAULT;
414 23 50         if (!newpfx)
415 0           newpfx = DIFF_NEW_PREFIX_DEFAULT;
416 23 50         if (!id_strlen)
417 0           id_strlen = GIT_ABBREV_DEFAULT;
418              
419 23 50         if ((error = diff_delta_format_path(
420 23 50         &old_path, oldpfx, delta->old_file.path)) < 0 ||
421 23           (error = diff_delta_format_path(
422             &new_path, newpfx, delta->new_file.path)) < 0)
423             goto done;
424              
425 23           git_buf_clear(out);
426              
427 23           git_buf_printf(out, "diff --git %s %s\n",
428             old_path.ptr, new_path.ptr);
429              
430 23 100         if (unchanged && delta->old_file.mode != delta->new_file.mode)
    50          
431 0           diff_print_modes(out, delta);
432              
433 23 100         if (delta->status == GIT_DELTA_RENAMED ||
    50          
434 0 0         (delta->status == GIT_DELTA_COPIED && unchanged)) {
435 2 50         if ((error = diff_delta_format_similarity_header(out, delta)) < 0)
436 0           goto done;
437             }
438              
439 23 100         if (!unchanged) {
440 22 50         if ((error = diff_print_oid_range(out, delta,
441             id_strlen, print_index)) < 0)
442 0           goto done;
443              
444 22 100         if ((delta->flags & GIT_DIFF_FLAG_BINARY) == 0)
445 21           diff_delta_format_with_paths(out, delta,
446 21           "--- %s\n+++ %s\n", old_path.ptr, new_path.ptr);
447             }
448              
449 23 50         if (git_buf_oom(out))
450 0           error = -1;
451              
452             done:
453 23           git_buf_dispose(&old_path);
454 23           git_buf_dispose(&new_path);
455              
456 23           return error;
457             }
458              
459 2           static int format_binary(
460             diff_print_info *pi,
461             git_diff_binary_t type,
462             const char *data,
463             size_t datalen,
464             size_t inflatedlen)
465             {
466 2           const char *typename = type == GIT_DIFF_BINARY_DELTA ?
467 2 50         "delta" : "literal";
468             const char *scan, *end;
469              
470 2           git_buf_printf(pi->buf, "%s %" PRIuZ "\n", typename, inflatedlen);
471 2           pi->line.num_lines++;
472              
473 4 100         for (scan = data, end = data + datalen; scan < end; ) {
474 2           size_t chunk_len = end - scan;
475 2 50         if (chunk_len > 52)
476 0           chunk_len = 52;
477              
478 2 50         if (chunk_len <= 26)
479 2           git_buf_putc(pi->buf, (char)chunk_len + 'A' - 1);
480             else
481 0           git_buf_putc(pi->buf, (char)chunk_len - 26 + 'a' - 1);
482              
483 2           git_buf_encode_base85(pi->buf, scan, chunk_len);
484 2           git_buf_putc(pi->buf, '\n');
485              
486 2 50         if (git_buf_oom(pi->buf))
487 0           return -1;
488              
489 2           scan += chunk_len;
490 2           pi->line.num_lines++;
491             }
492 2           git_buf_putc(pi->buf, '\n');
493              
494 2 50         if (git_buf_oom(pi->buf))
495 0           return -1;
496              
497 2           return 0;
498             }
499              
500 0           static int diff_print_patch_file_binary_noshow(
501             diff_print_info *pi, git_diff_delta *delta,
502             const char *old_pfx, const char *new_pfx)
503             {
504 0           git_buf old_path = GIT_BUF_INIT, new_path = GIT_BUF_INIT;
505             int error;
506              
507 0 0         if ((error = diff_delta_format_path(&old_path, old_pfx, delta->old_file.path)) < 0 ||
    0          
508 0 0         (error = diff_delta_format_path(&new_path, new_pfx, delta->new_file.path)) < 0 ||
509 0           (error = diff_delta_format_with_paths(pi->buf, delta, "Binary files %s and %s differ\n",
510 0           old_path.ptr, new_path.ptr)) < 0)
511             goto done;
512              
513 0           pi->line.num_lines = 1;
514              
515             done:
516 0           git_buf_dispose(&old_path);
517 0           git_buf_dispose(&new_path);
518 0           return error;
519             }
520              
521 1           static int diff_print_patch_file_binary(
522             diff_print_info *pi, git_diff_delta *delta,
523             const char *old_pfx, const char *new_pfx,
524             const git_diff_binary *binary)
525             {
526             size_t pre_binary_size;
527             int error;
528              
529 1 50         if (delta->status == GIT_DELTA_UNMODIFIED)
530 0           return 0;
531              
532 1 50         if ((pi->flags & GIT_DIFF_SHOW_BINARY) == 0 || !binary->contains_data)
    50          
533 0           return diff_print_patch_file_binary_noshow(
534             pi, delta, old_pfx, new_pfx);
535              
536 1           pre_binary_size = pi->buf->size;
537 1           git_buf_printf(pi->buf, "GIT binary patch\n");
538 1           pi->line.num_lines++;
539              
540 1 50         if ((error = format_binary(pi, binary->new_file.type, binary->new_file.data,
541 1 50         binary->new_file.datalen, binary->new_file.inflatedlen)) < 0 ||
542 1           (error = format_binary(pi, binary->old_file.type, binary->old_file.data,
543             binary->old_file.datalen, binary->old_file.inflatedlen)) < 0) {
544 0 0         if (error == GIT_EBUFS) {
545 0           git_error_clear();
546 0           git_buf_truncate(pi->buf, pre_binary_size);
547              
548 0           return diff_print_patch_file_binary_noshow(
549             pi, delta, old_pfx, new_pfx);
550             }
551             }
552              
553 1           pi->line.num_lines++;
554 1           return error;
555             }
556              
557 23           static int diff_print_patch_file(
558             const git_diff_delta *delta, float progress, void *data)
559             {
560             int error;
561 23           diff_print_info *pi = data;
562 23           const char *oldpfx =
563 23 100         pi->old_prefix ? pi->old_prefix : DIFF_OLD_PREFIX_DEFAULT;
564 23           const char *newpfx =
565 23 100         pi->new_prefix ? pi->new_prefix : DIFF_NEW_PREFIX_DEFAULT;
566              
567 23 100         bool binary = (delta->flags & GIT_DIFF_FLAG_BINARY) ||
    50          
568 22           (pi->flags & GIT_DIFF_FORCE_BINARY);
569 23           bool show_binary = !!(pi->flags & GIT_DIFF_SHOW_BINARY);
570 23           int id_strlen = pi->id_strlen;
571 23           bool print_index = (pi->format != GIT_DIFF_FORMAT_PATCH_ID);
572              
573 23 100         if (binary && show_binary)
    50          
574 1 50         id_strlen = delta->old_file.id_abbrev ? delta->old_file.id_abbrev :
575 0           delta->new_file.id_abbrev;
576              
577             GIT_UNUSED(progress);
578              
579 23 50         if (S_ISDIR(delta->new_file.mode) ||
    50          
580 23 50         delta->status == GIT_DELTA_UNMODIFIED ||
581 23 50         delta->status == GIT_DELTA_IGNORED ||
582 23 50         delta->status == GIT_DELTA_UNREADABLE ||
583 0 0         (delta->status == GIT_DELTA_UNTRACKED &&
584 0           (pi->flags & GIT_DIFF_SHOW_UNTRACKED_CONTENT) == 0))
585 0           return 0;
586              
587 23 50         if ((error = git_diff_delta__format_file_header(pi->buf, delta, oldpfx, newpfx,
588             id_strlen, print_index)) < 0)
589 0           return error;
590              
591 23           pi->line.origin = GIT_DIFF_LINE_FILE_HDR;
592 23           pi->line.content = git_buf_cstr(pi->buf);
593 23           pi->line.content_len = git_buf_len(pi->buf);
594              
595 23           return pi->print_cb(delta, NULL, &pi->line, pi->payload);
596             }
597              
598 1           static int diff_print_patch_binary(
599             const git_diff_delta *delta,
600             const git_diff_binary *binary,
601             void *data)
602             {
603 1           diff_print_info *pi = data;
604 1           const char *old_pfx =
605 1 50         pi->old_prefix ? pi->old_prefix : DIFF_OLD_PREFIX_DEFAULT;
606 1           const char *new_pfx =
607 1 50         pi->new_prefix ? pi->new_prefix : DIFF_NEW_PREFIX_DEFAULT;
608             int error;
609              
610 1           git_buf_clear(pi->buf);
611              
612 1 50         if ((error = diff_print_patch_file_binary(
613             pi, (git_diff_delta *)delta, old_pfx, new_pfx, binary)) < 0)
614 0           return error;
615              
616 1           pi->line.origin = GIT_DIFF_LINE_BINARY;
617 1           pi->line.content = git_buf_cstr(pi->buf);
618 1           pi->line.content_len = git_buf_len(pi->buf);
619              
620 1           return pi->print_cb(delta, NULL, &pi->line, pi->payload);
621             }
622              
623 17           static int diff_print_patch_hunk(
624             const git_diff_delta *d,
625             const git_diff_hunk *h,
626             void *data)
627             {
628 17           diff_print_info *pi = data;
629              
630 17 50         if (S_ISDIR(d->new_file.mode))
631 0           return 0;
632              
633 17           pi->line.origin = GIT_DIFF_LINE_HUNK_HDR;
634 17           pi->line.content = h->header;
635 17           pi->line.content_len = h->header_len;
636              
637 17           return pi->print_cb(d, h, &pi->line, pi->payload);
638             }
639              
640 38           static int diff_print_patch_line(
641             const git_diff_delta *delta,
642             const git_diff_hunk *hunk,
643             const git_diff_line *line,
644             void *data)
645             {
646 38           diff_print_info *pi = data;
647              
648 38 50         if (S_ISDIR(delta->new_file.mode))
649 0           return 0;
650              
651 38           return pi->print_cb(delta, hunk, line, pi->payload);
652             }
653              
654             /* print a git_diff to an output callback */
655 14           int git_diff_print(
656             git_diff *diff,
657             git_diff_format_t format,
658             git_diff_line_cb print_cb,
659             void *payload)
660             {
661             int error;
662 14           git_buf buf = GIT_BUF_INIT;
663             diff_print_info pi;
664 14           git_diff_file_cb print_file = NULL;
665 14           git_diff_binary_cb print_binary = NULL;
666 14           git_diff_hunk_cb print_hunk = NULL;
667 14           git_diff_line_cb print_line = NULL;
668              
669 14           switch (format) {
670             case GIT_DIFF_FORMAT_PATCH:
671 8           print_file = diff_print_patch_file;
672 8           print_binary = diff_print_patch_binary;
673 8           print_hunk = diff_print_patch_hunk;
674 8           print_line = diff_print_patch_line;
675 8           break;
676             case GIT_DIFF_FORMAT_PATCH_ID:
677 1           print_file = diff_print_patch_file;
678 1           print_binary = diff_print_patch_binary;
679 1           print_line = diff_print_patch_line;
680 1           break;
681             case GIT_DIFF_FORMAT_PATCH_HEADER:
682 1           print_file = diff_print_patch_file;
683 1           break;
684             case GIT_DIFF_FORMAT_RAW:
685 1           print_file = diff_print_one_raw;
686 1           break;
687             case GIT_DIFF_FORMAT_NAME_ONLY:
688 1           print_file = diff_print_one_name_only;
689 1           break;
690             case GIT_DIFF_FORMAT_NAME_STATUS:
691 2           print_file = diff_print_one_name_status;
692 2           break;
693             default:
694 0           git_error_set(GIT_ERROR_INVALID, "unknown diff output format (%d)", format);
695 0           return -1;
696             }
697              
698 14 50         if ((error = diff_print_info_init_fromdiff(&pi, &buf, diff, format, print_cb, payload)) < 0)
699 0           goto out;
700              
701 14 50         if ((error = git_diff_foreach(diff, print_file, print_binary, print_hunk, print_line, &pi)) != 0) {
702 0           git_error_set_after_callback_function(error, "git_diff_print");
703 0           goto out;
704             }
705              
706             out:
707 14           git_buf_dispose(&buf);
708 14           return error;
709             }
710              
711 44           int git_diff_print_callback__to_buf(
712             const git_diff_delta *delta,
713             const git_diff_hunk *hunk,
714             const git_diff_line *line,
715             void *payload)
716             {
717 44           git_buf *output = payload;
718             GIT_UNUSED(delta); GIT_UNUSED(hunk);
719              
720 44 50         if (!output) {
721 0           git_error_set(GIT_ERROR_INVALID, "buffer pointer must be provided");
722 0           return -1;
723             }
724              
725 44 100         if (line->origin == GIT_DIFF_LINE_ADDITION ||
    100          
726 32 100         line->origin == GIT_DIFF_LINE_DELETION ||
727 32           line->origin == GIT_DIFF_LINE_CONTEXT)
728 17           git_buf_putc(output, line->origin);
729              
730 44           return git_buf_put(output, line->content, line->content_len);
731             }
732              
733 0           int git_diff_print_callback__to_file_handle(
734             const git_diff_delta *delta,
735             const git_diff_hunk *hunk,
736             const git_diff_line *line,
737             void *payload)
738             {
739 0 0         FILE *fp = payload ? payload : stdout;
740             int error;
741              
742             GIT_UNUSED(delta);
743             GIT_UNUSED(hunk);
744              
745 0 0         if (line->origin == GIT_DIFF_LINE_CONTEXT ||
    0          
746 0 0         line->origin == GIT_DIFF_LINE_ADDITION ||
747 0           line->origin == GIT_DIFF_LINE_DELETION) {
748 0 0         while ((error = fputc(line->origin, fp)) == EINTR)
749 0           continue;
750 0 0         if (error) {
751 0           git_error_set(GIT_ERROR_OS, "could not write status");
752 0           return -1;
753             }
754             }
755              
756 0 0         if (fwrite(line->content, line->content_len, 1, fp) != 1) {
757 0           git_error_set(GIT_ERROR_OS, "could not write line");
758 0           return -1;
759             }
760              
761 0           return 0;
762             }
763              
764             /* print a git_diff to a git_buf */
765 1           int git_diff_to_buf(git_buf *out, git_diff *diff, git_diff_format_t format)
766             {
767 1 50         assert(out && diff);
    50          
768 1           git_buf_sanitize(out);
769 1           return git_diff_print(diff, format, git_diff_print_callback__to_buf, out);
770             }
771              
772             /* print a git_patch to an output callback */
773 10           int git_patch_print(
774             git_patch *patch,
775             git_diff_line_cb print_cb,
776             void *payload)
777             {
778 10           git_buf temp = GIT_BUF_INIT;
779             diff_print_info pi;
780             int error;
781              
782 10 50         assert(patch && print_cb);
    50          
783              
784 10 50         if ((error = diff_print_info_init_frompatch(&pi, &temp, patch,
785             GIT_DIFF_FORMAT_PATCH, print_cb, payload)) < 0)
786 0           goto out;
787              
788 10 50         if ((error = git_patch__invoke_callbacks(patch, diff_print_patch_file, diff_print_patch_binary,
789             diff_print_patch_hunk, diff_print_patch_line, &pi)) < 0) {
790 0           git_error_set_after_callback_function(error, "git_patch_print");
791 0           goto out;
792             }
793              
794             out:
795 10           git_buf_dispose(&temp);
796 10           return error;
797             }
798              
799             /* print a git_patch to a git_buf */
800 10           int git_patch_to_buf(git_buf *out, git_patch *patch)
801             {
802 10 50         assert(out && patch);
    50          
803 10           git_buf_sanitize(out);
804 10           return git_patch_print(patch, git_diff_print_callback__to_buf, out);
805             }