Line Flow Count Block(s) Source
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 "diff_xdiff.h"
9 - #include "util.h"
10 -
11 - #include "git2/errors.h"
12 - #include "diff.h"
13 - #include "diff_driver.h"
14 - #include "patch_generate.h"
15 -
16 3234 2 static int git_xdiff_scan_int(const char **str, int *value)
17 - {
18 3234 2 const char *scan = *str;
19 3234 2 int v = 0, digits = 0;
20 - /* find next digit */
21 10248 2-6 for (scan = *str; *scan && !git__isdigit(*scan); scan++);
22 - /* parse next number */
23 6970 7-10 for (; git__isdigit(*scan); scan++, digits++)
24 3736 8 v = (v * 10) + (*scan - '0');
25 3234 11 *str = scan;
26 3234 11 *value = v;
27 3234 11 return (digits > 0) ? 0 : -1;
28 - }
29 -
30 945 2 static int git_xdiff_parse_hunk(git_diff_hunk *hunk, const char *header)
31 - {
32 - /* expect something of the form "@@ -%d[,%d] +%d[,%d] @@" */
33 945 2 if (*header != '@')
34 ##### 3 goto fail;
35 945 4,5 if (git_xdiff_scan_int(&header, &hunk->old_start) < 0)
36 ##### 6 goto fail;
37 945 7 if (*header == ',') {
38 558 8,9 if (git_xdiff_scan_int(&header, &hunk->old_lines) < 0)
39 ##### 10 goto fail;
40 - } else
41 387 11 hunk->old_lines = 1;
42 945 12,13 if (git_xdiff_scan_int(&header, &hunk->new_start) < 0)
43 ##### 14 goto fail;
44 945 15 if (*header == ',') {
45 786 16,17 if (git_xdiff_scan_int(&header, &hunk->new_lines) < 0)
46 ##### 18 goto fail;
47 - } else
48 159 19 hunk->new_lines = 1;
49 945 20,21 if (hunk->old_start < 0 || hunk->new_start < 0)
50 - goto fail;
51 -
52 945 22 return 0;
53 -
54 - fail:
55 ##### 23 git_error_set(GIT_ERROR_INVALID, "malformed hunk header from xdiff");
56 ##### 24 return -1;
57 - }
58 -
59 - typedef struct {
60 - git_xdiff_output *xo;
61 - git_patch_generated *patch;
62 - git_diff_hunk hunk;
63 - int old_lineno, new_lineno;
64 - mmfile_t xd_old_data, xd_new_data;
65 - } git_xdiff_info;
66 -
67 8761 2 static int diff_update_lines(
68 - git_xdiff_info *info,
69 - git_diff_line *line,
70 - const char *content,
71 - size_t content_len)
72 - {
73 8761 2 const char *scan = content, *scan_end = content + content_len;
74 -
75 297984 2,5,6 for (line->num_lines = 0; scan < scan_end; ++scan)
76 289223 3 if (*scan == '\n')
77 8761 4 ++line->num_lines;
78 -
79 8761 7 line->content = content;
80 8761 7 line->content_len = content_len;
81 -
82 - /* expect " "/"-"/"+", then data */
83 8761 7 switch (line->origin) {
84 - case GIT_DIFF_LINE_ADDITION:
85 - case GIT_DIFF_LINE_DEL_EOFNL:
86 3674 8 line->old_lineno = -1;
87 3674 8 line->new_lineno = info->new_lineno;
88 3674 8 info->new_lineno += (int)line->num_lines;
89 3674 8 break;
90 - case GIT_DIFF_LINE_DELETION:
91 - case GIT_DIFF_LINE_ADD_EOFNL:
92 3538 9 line->old_lineno = info->old_lineno;
93 3538 9 line->new_lineno = -1;
94 3538 9 info->old_lineno += (int)line->num_lines;
95 3538 9 break;
96 - case GIT_DIFF_LINE_CONTEXT:
97 - case GIT_DIFF_LINE_CONTEXT_EOFNL:
98 1549 10 line->old_lineno = info->old_lineno;
99 1549 10 line->new_lineno = info->new_lineno;
100 1549 10 info->old_lineno += (int)line->num_lines;
101 1549 10 info->new_lineno += (int)line->num_lines;
102 1549 10 break;
103 - default:
104 ##### 11 git_error_set(GIT_ERROR_INVALID, "unknown diff line origin %02x",
105 ##### 11 (unsigned int)line->origin);
106 ##### 12 return -1;
107 - }
108 -
109 8761 13 return 0;
110 - }
111 -
112 9641 2 static int git_xdiff_cb(void *priv, mmbuffer_t *bufs, int len)
113 - {
114 9641 2 git_xdiff_info *info = priv;
115 9641 2 git_patch_generated *patch = info->patch;
116 9641 2 const git_diff_delta *delta = patch->base.delta;
117 9641 2 git_patch_generated_output *output = &info->xo->output;
118 - git_diff_line line;
119 - size_t buffer_len;
120 -
121 9641 2 if (len == 1) {
122 945 3 output->error = git_xdiff_parse_hunk(&info->hunk, bufs[0].ptr);
123 945 4 if (output->error < 0)
124 ##### 5 return output->error;
125 -
126 945 6 info->hunk.header_len = bufs[0].size;
127 945 6 if (info->hunk.header_len >= sizeof(info->hunk.header))
128 ##### 7 info->hunk.header_len = sizeof(info->hunk.header) - 1;
129 -
130 - /* Sanitize the hunk header in case there is invalid Unicode */
131 945 8 buffer_len = git__utf8_valid_buf_length((const uint8_t *) bufs[0].ptr, info->hunk.header_len);
132 - /* Sanitizing the hunk header may delete the newline, so add it back again if there is room */
133 945 9 if (buffer_len < info->hunk.header_len) {
134 1 10 bufs[0].ptr[buffer_len] = '\n';
135 1 10 buffer_len += 1;
136 1 10 info->hunk.header_len = buffer_len;
137 - }
138 -
139 945 11 memcpy(info->hunk.header, bufs[0].ptr, info->hunk.header_len);
140 945 11 info->hunk.header[info->hunk.header_len] = '\0';
141 -
142 945 11,13 if (output->hunk_cb != NULL &&
143 945 12,12 (output->error = output->hunk_cb(
144 945 12 delta, &info->hunk, output->payload)))
145 ##### 14 return output->error;
146 -
147 945 15 info->old_lineno = info->hunk.old_start;
148 945 15 info->new_lineno = info->hunk.new_start;
149 - }
150 -
151 9641 16,17 if (len == 2 || len == 3) {
152 - /* expect " "/"-"/"+", then data */
153 8696 18-23 line.origin =
154 8696 18 (*bufs[0].ptr == '+') ? GIT_DIFF_LINE_ADDITION :
155 5053 19 (*bufs[0].ptr == '-') ? GIT_DIFF_LINE_DELETION :
156 - GIT_DIFF_LINE_CONTEXT;
157 -
158 8696 24 if (line.origin == GIT_DIFF_LINE_ADDITION)
159 3643 25 line.content_offset = bufs[1].ptr - info->xd_new_data.ptr;
160 5053 26 else if (line.origin == GIT_DIFF_LINE_DELETION)
161 3510 27 line.content_offset = bufs[1].ptr - info->xd_old_data.ptr;
162 - else
163 1543 28 line.content_offset = -1;
164 -
165 8696 29,29 output->error = diff_update_lines(
166 8696 29,29 info, &line, bufs[1].ptr, bufs[1].size);
167 -
168 8696 30,31 if (!output->error && output->data_cb != NULL)
169 8696 32,32,33 output->error = output->data_cb(
170 8696 32 delta, &info->hunk, &line, output->payload);
171 - }
172 -
173 9641 34,35 if (len == 3 && !output->error) {
174 - /* If we have a '+' and a third buf, then we have added a line
175 - * without a newline and the old code had one, so DEL_EOFNL.
176 - * If we have a '-' and a third buf, then we have removed a line
177 - * with out a newline but added a blank line, so ADD_EOFNL.
178 - */
179 65 36-41 line.origin =
180 65 36 (*bufs[0].ptr == '+') ? GIT_DIFF_LINE_DEL_EOFNL :
181 34 37 (*bufs[0].ptr == '-') ? GIT_DIFF_LINE_ADD_EOFNL :
182 - GIT_DIFF_LINE_CONTEXT_EOFNL;
183 -
184 65 42 line.content_offset = -1;
185 -
186 65 42,42 output->error = diff_update_lines(
187 65 42,42 info, &line, bufs[2].ptr, bufs[2].size);
188 -
189 65 43,44 if (!output->error && output->data_cb != NULL)
190 65 45,45,46 output->error = output->data_cb(
191 65 45 delta, &info->hunk, &line, output->payload);
192 - }
193 -
194 9641 47 return output->error;
195 - }
196 -
197 829 2 static int git_xdiff(git_patch_generated_output *output, git_patch_generated *patch)
198 - {
199 829 2 git_xdiff_output *xo = (git_xdiff_output *)output;
200 - git_xdiff_info info;
201 - git_diff_find_context_payload findctxt;
202 -
203 829 2 memset(&info, 0, sizeof(info));
204 829 2 info.patch = patch;
205 829 2 info.xo = xo;
206 -
207 829 2 xo->callback.priv = &info;
208 -
209 829 2,3 git_diff_find_context_init(
210 829 3 &xo->config.find_func, &findctxt, git_patch_generated_driver(patch));
211 829 4 xo->config.find_func_priv = &findctxt;
212 -
213 829 4 if (xo->config.find_func != NULL)
214 829 5 xo->config.flags |= XDL_EMIT_FUNCNAMES;
215 - else
216 ##### 6 xo->config.flags &= ~XDL_EMIT_FUNCNAMES;
217 -
218 - /* TODO: check ofile.opts_flags to see if driver-specific per-file
219 - * updates are needed to xo->params.flags
220 - */
221 -
222 829 7 git_patch_generated_old_data(&info.xd_old_data.ptr, &info.xd_old_data.size, patch);
223 829 8 git_patch_generated_new_data(&info.xd_new_data.ptr, &info.xd_new_data.size, patch);
224 -
225 829 9,10 if (info.xd_old_data.size > GIT_XDIFF_MAX_SIZE ||
226 829 10 info.xd_new_data.size > GIT_XDIFF_MAX_SIZE) {
227 ##### 11 git_error_set(GIT_ERROR_INVALID, "files too large for diff");
228 ##### 12 return -1;
229 - }
230 -
231 829 13,13 xdl_diff(&info.xd_old_data, &info.xd_new_data,
232 829 13 &xo->params, &xo->config, &xo->callback);
233 -
234 829 14 git_diff_find_context_clear(&findctxt);
235 -
236 829 15 return xo->output.error;
237 - }
238 -
239 1266 2 void git_xdiff_init(git_xdiff_output *xo, const git_diff_options *opts)
240 - {
241 1266 2-4 uint32_t flags = opts ? opts->flags : 0;
242 -
243 1266 5 xo->output.diff_cb = git_xdiff;
244 -
245 1266 5-7 xo->config.ctxlen = opts ? opts->context_lines : 3;
246 1266 8-10 xo->config.interhunkctxlen = opts ? opts->interhunk_lines : 0;
247 -
248 1266 11 if (flags & GIT_DIFF_IGNORE_WHITESPACE)
249 2 12 xo->params.flags |= XDF_WHITESPACE_FLAGS;
250 1266 13 if (flags & GIT_DIFF_IGNORE_WHITESPACE_CHANGE)
251 1 14 xo->params.flags |= XDF_IGNORE_WHITESPACE_CHANGE;
252 1266 15 if (flags & GIT_DIFF_IGNORE_WHITESPACE_EOL)
253 1 16 xo->params.flags |= XDF_IGNORE_WHITESPACE_AT_EOL;
254 1266 17 if (flags & GIT_DIFF_INDENT_HEURISTIC)
255 ##### 18 xo->params.flags |= XDF_INDENT_HEURISTIC;
256 -
257 1266 19 if (flags & GIT_DIFF_PATIENCE)
258 1 20 xo->params.flags |= XDF_PATIENCE_DIFF;
259 1266 21 if (flags & GIT_DIFF_MINIMAL)
260 ##### 22 xo->params.flags |= XDF_NEED_MINIMAL;
261 -
262 1266 23 xo->callback.outf = git_xdiff_cb;
263 1266 23 }