source src/diff_xdiff.c
| 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 | } |