]>
Commit | Line | Data |
---|---|---|
114f5a6c RB |
1 | /* |
2 | * Copyright (C) the libgit2 contributors. All rights reserved. | |
3 | * | |
4 | * This file is part of libgit2, distributed under the GNU GPL v2 with | |
5 | * a Linking Exception. For full terms see the included COPYING file. | |
6 | */ | |
7 | #include "common.h" | |
8 | #include "diff.h" | |
9 | #include "diff_driver.h" | |
10 | #include "diff_patch.h" | |
11 | #include "diff_xdiff.h" | |
12 | ||
13 | static int git_xdiff_scan_int(const char **str, int *value) | |
14 | { | |
15 | const char *scan = *str; | |
16 | int v = 0, digits = 0; | |
17 | /* find next digit */ | |
18 | for (scan = *str; *scan && !git__isdigit(*scan); scan++); | |
19 | /* parse next number */ | |
20 | for (; git__isdigit(*scan); scan++, digits++) | |
21 | v = (v * 10) + (*scan - '0'); | |
22 | *str = scan; | |
23 | *value = v; | |
24 | return (digits > 0) ? 0 : -1; | |
25 | } | |
26 | ||
27 | static int git_xdiff_parse_hunk(git_diff_range *range, const char *header) | |
28 | { | |
29 | /* expect something of the form "@@ -%d[,%d] +%d[,%d] @@" */ | |
30 | if (*header != '@') | |
31 | return -1; | |
32 | if (git_xdiff_scan_int(&header, &range->old_start) < 0) | |
33 | return -1; | |
34 | if (*header == ',') { | |
35 | if (git_xdiff_scan_int(&header, &range->old_lines) < 0) | |
36 | return -1; | |
37 | } else | |
38 | range->old_lines = 1; | |
39 | if (git_xdiff_scan_int(&header, &range->new_start) < 0) | |
40 | return -1; | |
41 | if (*header == ',') { | |
42 | if (git_xdiff_scan_int(&header, &range->new_lines) < 0) | |
43 | return -1; | |
44 | } else | |
45 | range->new_lines = 1; | |
46 | if (range->old_start < 0 || range->new_start < 0) | |
47 | return -1; | |
48 | ||
49 | return 0; | |
50 | } | |
51 | ||
52 | typedef struct { | |
53 | git_xdiff_output *xo; | |
54 | git_diff_patch *patch; | |
55 | git_diff_range range; | |
56 | } git_xdiff_info; | |
57 | ||
58 | static int git_xdiff_cb(void *priv, mmbuffer_t *bufs, int len) | |
59 | { | |
60 | git_xdiff_info *info = priv; | |
61 | git_diff_patch *patch = info->patch; | |
62 | git_diff_output *output = &info->xo->output; | |
63 | ||
64 | if (len == 1) { | |
65 | output->error = git_xdiff_parse_hunk(&info->range, bufs[0].ptr); | |
66 | if (output->error < 0) | |
67 | return output->error; | |
68 | ||
69 | if (output->hunk_cb != NULL && | |
70 | output->hunk_cb(patch->delta, &info->range, | |
71 | bufs[0].ptr, bufs[0].size, output->payload)) | |
72 | output->error = GIT_EUSER; | |
73 | } | |
74 | ||
75 | if (len == 2 || len == 3) { | |
76 | /* expect " "/"-"/"+", then data */ | |
77 | char origin = | |
78 | (*bufs[0].ptr == '+') ? GIT_DIFF_LINE_ADDITION : | |
79 | (*bufs[0].ptr == '-') ? GIT_DIFF_LINE_DELETION : | |
80 | GIT_DIFF_LINE_CONTEXT; | |
81 | ||
82 | if (output->data_cb != NULL && | |
83 | output->data_cb(patch->delta, &info->range, | |
84 | origin, bufs[1].ptr, bufs[1].size, output->payload)) | |
85 | output->error = GIT_EUSER; | |
86 | } | |
87 | ||
88 | if (len == 3 && !output->error) { | |
89 | /* If we have a '+' and a third buf, then we have added a line | |
90 | * without a newline and the old code had one, so DEL_EOFNL. | |
91 | * If we have a '-' and a third buf, then we have removed a line | |
92 | * with out a newline but added a blank line, so ADD_EOFNL. | |
93 | */ | |
94 | char origin = | |
95 | (*bufs[0].ptr == '+') ? GIT_DIFF_LINE_DEL_EOFNL : | |
96 | (*bufs[0].ptr == '-') ? GIT_DIFF_LINE_ADD_EOFNL : | |
97 | GIT_DIFF_LINE_CONTEXT_EOFNL; | |
98 | ||
99 | if (output->data_cb != NULL && | |
100 | output->data_cb(patch->delta, &info->range, | |
101 | origin, bufs[2].ptr, bufs[2].size, output->payload)) | |
102 | output->error = GIT_EUSER; | |
103 | } | |
104 | ||
105 | return output->error; | |
106 | } | |
107 | ||
108 | static int git_xdiff(git_diff_output *output, git_diff_patch *patch) | |
109 | { | |
110 | git_xdiff_output *xo = (git_xdiff_output *)output; | |
111 | git_xdiff_info info; | |
112 | mmfile_t old_xdiff_data, new_xdiff_data; | |
113 | ||
114 | memset(&info, 0, sizeof(info)); | |
115 | info.patch = patch; | |
116 | info.xo = xo; | |
117 | ||
118 | xo->callback.priv = &info; | |
119 | ||
120 | xo->config.find_func_priv = patch->ofile.driver; | |
121 | xo->config.find_func = patch->ofile.driver ? | |
122 | git_diff_driver_find_content_fn(patch->ofile.driver) : NULL; | |
123 | ||
124 | if (xo->config.find_func != NULL) | |
125 | xo->config.flags |= XDL_EMIT_FUNCNAMES; | |
126 | else | |
127 | xo->config.flags &= ~XDL_EMIT_FUNCNAMES; | |
128 | ||
129 | ||
130 | old_xdiff_data.ptr = patch->ofile.map.data; | |
131 | old_xdiff_data.size = patch->ofile.map.len; | |
132 | new_xdiff_data.ptr = patch->nfile.map.data; | |
133 | new_xdiff_data.size = patch->nfile.map.len; | |
134 | ||
135 | xdl_diff(&old_xdiff_data, &new_xdiff_data, | |
136 | &xo->params, &xo->config, &xo->callback); | |
137 | ||
138 | return xo->output.error; | |
139 | } | |
140 | ||
141 | void git_xdiff_init(git_xdiff_output *xo, const git_diff_options *opts) | |
142 | { | |
143 | uint32_t flags = opts ? opts->flags : GIT_DIFF_NORMAL; | |
144 | ||
145 | xo->output.diff_cb = git_xdiff; | |
146 | ||
147 | memset(&xo->config, 0, sizeof(xo->config)); | |
148 | xo->config.ctxlen = opts ? opts->context_lines : 3; | |
149 | xo->config.interhunkctxlen = opts ? opts->interhunk_lines : 0; | |
150 | ||
151 | memset(&xo->params, 0, sizeof(xo->params)); | |
152 | if (flags & GIT_DIFF_IGNORE_WHITESPACE) | |
153 | xo->params.flags |= XDF_WHITESPACE_FLAGS; | |
154 | if (flags & GIT_DIFF_IGNORE_WHITESPACE_CHANGE) | |
155 | xo->params.flags |= XDF_IGNORE_WHITESPACE_CHANGE; | |
156 | if (flags & GIT_DIFF_IGNORE_WHITESPACE_EOL) | |
157 | xo->params.flags |= XDF_IGNORE_WHITESPACE_AT_EOL; | |
158 | ||
159 | memset(&xo->callback, 0, sizeof(xo->callback)); | |
160 | xo->callback.outf = git_xdiff_cb; | |
161 | } |