File Coverage

deps/libgit2/src/libgit2/diff_print.c
Criterion Covered Total %
statement 309 419 73.7
branch 136 260 52.3
condition n/a
subroutine n/a
pod n/a
total 445 679 65.5


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