|
Packit Service |
20376f |
/*
|
|
Packit Service |
20376f |
* Copyright (C) the libgit2 contributors. All rights reserved.
|
|
Packit Service |
20376f |
*
|
|
Packit Service |
20376f |
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
|
Packit Service |
20376f |
* a Linking Exception. For full terms see the included COPYING file.
|
|
Packit Service |
20376f |
*/
|
|
Packit Service |
20376f |
#include "git2/errors.h"
|
|
Packit Service |
20376f |
#include "common.h"
|
|
Packit Service |
20376f |
#include "diff.h"
|
|
Packit Service |
20376f |
#include "diff_driver.h"
|
|
Packit Service |
20376f |
#include "diff_xdiff.h"
|
|
Packit Service |
20376f |
#include "patch_generate.h"
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
static int git_xdiff_scan_int(const char **str, int *value)
|
|
Packit Service |
20376f |
{
|
|
Packit Service |
20376f |
const char *scan = *str;
|
|
Packit Service |
20376f |
int v = 0, digits = 0;
|
|
Packit Service |
20376f |
/* find next digit */
|
|
Packit Service |
20376f |
for (scan = *str; *scan && !git__isdigit(*scan); scan++);
|
|
Packit Service |
20376f |
/* parse next number */
|
|
Packit Service |
20376f |
for (; git__isdigit(*scan); scan++, digits++)
|
|
Packit Service |
20376f |
v = (v * 10) + (*scan - '0');
|
|
Packit Service |
20376f |
*str = scan;
|
|
Packit Service |
20376f |
*value = v;
|
|
Packit Service |
20376f |
return (digits > 0) ? 0 : -1;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
static int git_xdiff_parse_hunk(git_diff_hunk *hunk, const char *header)
|
|
Packit Service |
20376f |
{
|
|
Packit Service |
20376f |
/* expect something of the form "@@ -%d[,%d] +%d[,%d] @@" */
|
|
Packit Service |
20376f |
if (*header != '@')
|
|
Packit Service |
20376f |
goto fail;
|
|
Packit Service |
20376f |
if (git_xdiff_scan_int(&header, &hunk->old_start) < 0)
|
|
Packit Service |
20376f |
goto fail;
|
|
Packit Service |
20376f |
if (*header == ',') {
|
|
Packit Service |
20376f |
if (git_xdiff_scan_int(&header, &hunk->old_lines) < 0)
|
|
Packit Service |
20376f |
goto fail;
|
|
Packit Service |
20376f |
} else
|
|
Packit Service |
20376f |
hunk->old_lines = 1;
|
|
Packit Service |
20376f |
if (git_xdiff_scan_int(&header, &hunk->new_start) < 0)
|
|
Packit Service |
20376f |
goto fail;
|
|
Packit Service |
20376f |
if (*header == ',') {
|
|
Packit Service |
20376f |
if (git_xdiff_scan_int(&header, &hunk->new_lines) < 0)
|
|
Packit Service |
20376f |
goto fail;
|
|
Packit Service |
20376f |
} else
|
|
Packit Service |
20376f |
hunk->new_lines = 1;
|
|
Packit Service |
20376f |
if (hunk->old_start < 0 || hunk->new_start < 0)
|
|
Packit Service |
20376f |
goto fail;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
return 0;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
fail:
|
|
Packit Service |
20376f |
giterr_set(GITERR_INVALID, "malformed hunk header from xdiff");
|
|
Packit Service |
20376f |
return -1;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
typedef struct {
|
|
Packit Service |
20376f |
git_xdiff_output *xo;
|
|
Packit Service |
20376f |
git_patch_generated *patch;
|
|
Packit Service |
20376f |
git_diff_hunk hunk;
|
|
Packit Service |
20376f |
int old_lineno, new_lineno;
|
|
Packit Service |
20376f |
mmfile_t xd_old_data, xd_new_data;
|
|
Packit Service |
20376f |
} git_xdiff_info;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
static int diff_update_lines(
|
|
Packit Service |
20376f |
git_xdiff_info *info,
|
|
Packit Service |
20376f |
git_diff_line *line,
|
|
Packit Service |
20376f |
const char *content,
|
|
Packit Service |
20376f |
size_t content_len)
|
|
Packit Service |
20376f |
{
|
|
Packit Service |
20376f |
const char *scan = content, *scan_end = content + content_len;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
for (line->num_lines = 0; scan < scan_end; ++scan)
|
|
Packit Service |
20376f |
if (*scan == '\n')
|
|
Packit Service |
20376f |
++line->num_lines;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
line->content = content;
|
|
Packit Service |
20376f |
line->content_len = content_len;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
/* expect " "/"-"/"+", then data */
|
|
Packit Service |
20376f |
switch (line->origin) {
|
|
Packit Service |
20376f |
case GIT_DIFF_LINE_ADDITION:
|
|
Packit Service |
20376f |
case GIT_DIFF_LINE_DEL_EOFNL:
|
|
Packit Service |
20376f |
line->old_lineno = -1;
|
|
Packit Service |
20376f |
line->new_lineno = info->new_lineno;
|
|
Packit Service |
20376f |
info->new_lineno += (int)line->num_lines;
|
|
Packit Service |
20376f |
break;
|
|
Packit Service |
20376f |
case GIT_DIFF_LINE_DELETION:
|
|
Packit Service |
20376f |
case GIT_DIFF_LINE_ADD_EOFNL:
|
|
Packit Service |
20376f |
line->old_lineno = info->old_lineno;
|
|
Packit Service |
20376f |
line->new_lineno = -1;
|
|
Packit Service |
20376f |
info->old_lineno += (int)line->num_lines;
|
|
Packit Service |
20376f |
break;
|
|
Packit Service |
20376f |
case GIT_DIFF_LINE_CONTEXT:
|
|
Packit Service |
20376f |
case GIT_DIFF_LINE_CONTEXT_EOFNL:
|
|
Packit Service |
20376f |
line->old_lineno = info->old_lineno;
|
|
Packit Service |
20376f |
line->new_lineno = info->new_lineno;
|
|
Packit Service |
20376f |
info->old_lineno += (int)line->num_lines;
|
|
Packit Service |
20376f |
info->new_lineno += (int)line->num_lines;
|
|
Packit Service |
20376f |
break;
|
|
Packit Service |
20376f |
default:
|
|
Packit Service |
20376f |
giterr_set(GITERR_INVALID, "unknown diff line origin %02x",
|
|
Packit Service |
20376f |
(unsigned int)line->origin);
|
|
Packit Service |
20376f |
return -1;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
return 0;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
static int git_xdiff_cb(void *priv, mmbuffer_t *bufs, int len)
|
|
Packit Service |
20376f |
{
|
|
Packit Service |
20376f |
git_xdiff_info *info = priv;
|
|
Packit Service |
20376f |
git_patch_generated *patch = info->patch;
|
|
Packit Service |
20376f |
const git_diff_delta *delta = patch->base.delta;
|
|
Packit Service |
20376f |
git_patch_generated_output *output = &info->xo->output;
|
|
Packit Service |
20376f |
git_diff_line line;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (len == 1) {
|
|
Packit Service |
20376f |
output->error = git_xdiff_parse_hunk(&info->hunk, bufs[0].ptr);
|
|
Packit Service |
20376f |
if (output->error < 0)
|
|
Packit Service |
20376f |
return output->error;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
info->hunk.header_len = bufs[0].size;
|
|
Packit Service |
20376f |
if (info->hunk.header_len >= sizeof(info->hunk.header))
|
|
Packit Service |
20376f |
info->hunk.header_len = sizeof(info->hunk.header) - 1;
|
|
Packit Service |
20376f |
memcpy(info->hunk.header, bufs[0].ptr, info->hunk.header_len);
|
|
Packit Service |
20376f |
info->hunk.header[info->hunk.header_len] = '\0';
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (output->hunk_cb != NULL &&
|
|
Packit Service |
20376f |
(output->error = output->hunk_cb(
|
|
Packit Service |
20376f |
delta, &info->hunk, output->payload)))
|
|
Packit Service |
20376f |
return output->error;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
info->old_lineno = info->hunk.old_start;
|
|
Packit Service |
20376f |
info->new_lineno = info->hunk.new_start;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (len == 2 || len == 3) {
|
|
Packit Service |
20376f |
/* expect " "/"-"/"+", then data */
|
|
Packit Service |
20376f |
line.origin =
|
|
Packit Service |
20376f |
(*bufs[0].ptr == '+') ? GIT_DIFF_LINE_ADDITION :
|
|
Packit Service |
20376f |
(*bufs[0].ptr == '-') ? GIT_DIFF_LINE_DELETION :
|
|
Packit Service |
20376f |
GIT_DIFF_LINE_CONTEXT;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (line.origin == GIT_DIFF_LINE_ADDITION)
|
|
Packit Service |
20376f |
line.content_offset = bufs[1].ptr - info->xd_new_data.ptr;
|
|
Packit Service |
20376f |
else if (line.origin == GIT_DIFF_LINE_DELETION)
|
|
Packit Service |
20376f |
line.content_offset = bufs[1].ptr - info->xd_old_data.ptr;
|
|
Packit Service |
20376f |
else
|
|
Packit Service |
20376f |
line.content_offset = -1;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
output->error = diff_update_lines(
|
|
Packit Service |
20376f |
info, &line, bufs[1].ptr, bufs[1].size);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (!output->error && output->data_cb != NULL)
|
|
Packit Service |
20376f |
output->error = output->data_cb(
|
|
Packit Service |
20376f |
delta, &info->hunk, &line, output->payload);
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (len == 3 && !output->error) {
|
|
Packit Service |
20376f |
/* If we have a '+' and a third buf, then we have added a line
|
|
Packit Service |
20376f |
* without a newline and the old code had one, so DEL_EOFNL.
|
|
Packit Service |
20376f |
* If we have a '-' and a third buf, then we have removed a line
|
|
Packit Service |
20376f |
* with out a newline but added a blank line, so ADD_EOFNL.
|
|
Packit Service |
20376f |
*/
|
|
Packit Service |
20376f |
line.origin =
|
|
Packit Service |
20376f |
(*bufs[0].ptr == '+') ? GIT_DIFF_LINE_DEL_EOFNL :
|
|
Packit Service |
20376f |
(*bufs[0].ptr == '-') ? GIT_DIFF_LINE_ADD_EOFNL :
|
|
Packit Service |
20376f |
GIT_DIFF_LINE_CONTEXT_EOFNL;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
line.content_offset = -1;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
output->error = diff_update_lines(
|
|
Packit Service |
20376f |
info, &line, bufs[2].ptr, bufs[2].size);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (!output->error && output->data_cb != NULL)
|
|
Packit Service |
20376f |
output->error = output->data_cb(
|
|
Packit Service |
20376f |
delta, &info->hunk, &line, output->payload);
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
return output->error;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
static int git_xdiff(git_patch_generated_output *output, git_patch_generated *patch)
|
|
Packit Service |
20376f |
{
|
|
Packit Service |
20376f |
git_xdiff_output *xo = (git_xdiff_output *)output;
|
|
Packit Service |
20376f |
git_xdiff_info info;
|
|
Packit Service |
20376f |
git_diff_find_context_payload findctxt;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
memset(&info, 0, sizeof(info));
|
|
Packit Service |
20376f |
info.patch = patch;
|
|
Packit Service |
20376f |
info.xo = xo;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
xo->callback.priv = &info;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
git_diff_find_context_init(
|
|
Packit Service |
20376f |
&xo->config.find_func, &findctxt, git_patch_generated_driver(patch));
|
|
Packit Service |
20376f |
xo->config.find_func_priv = &findctxt;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (xo->config.find_func != NULL)
|
|
Packit Service |
20376f |
xo->config.flags |= XDL_EMIT_FUNCNAMES;
|
|
Packit Service |
20376f |
else
|
|
Packit Service |
20376f |
xo->config.flags &= ~XDL_EMIT_FUNCNAMES;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
/* TODO: check ofile.opts_flags to see if driver-specific per-file
|
|
Packit Service |
20376f |
* updates are needed to xo->params.flags
|
|
Packit Service |
20376f |
*/
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
git_patch_generated_old_data(&info.xd_old_data.ptr, &info.xd_old_data.size, patch);
|
|
Packit Service |
20376f |
git_patch_generated_new_data(&info.xd_new_data.ptr, &info.xd_new_data.size, patch);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (info.xd_old_data.size > GIT_XDIFF_MAX_SIZE ||
|
|
Packit Service |
20376f |
info.xd_new_data.size > GIT_XDIFF_MAX_SIZE) {
|
|
Packit Service |
20376f |
giterr_set(GITERR_INVALID, "files too large for diff");
|
|
Packit Service |
20376f |
return -1;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
xdl_diff(&info.xd_old_data, &info.xd_new_data,
|
|
Packit Service |
20376f |
&xo->params, &xo->config, &xo->callback);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
git_diff_find_context_clear(&findctxt);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
return xo->output.error;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
void git_xdiff_init(git_xdiff_output *xo, const git_diff_options *opts)
|
|
Packit Service |
20376f |
{
|
|
Packit Service |
20376f |
uint32_t flags = opts ? opts->flags : 0;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
xo->output.diff_cb = git_xdiff;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
xo->config.ctxlen = opts ? opts->context_lines : 3;
|
|
Packit Service |
20376f |
xo->config.interhunkctxlen = opts ? opts->interhunk_lines : 0;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (flags & GIT_DIFF_IGNORE_WHITESPACE)
|
|
Packit Service |
20376f |
xo->params.flags |= XDF_WHITESPACE_FLAGS;
|
|
Packit Service |
20376f |
if (flags & GIT_DIFF_IGNORE_WHITESPACE_CHANGE)
|
|
Packit Service |
20376f |
xo->params.flags |= XDF_IGNORE_WHITESPACE_CHANGE;
|
|
Packit Service |
20376f |
if (flags & GIT_DIFF_IGNORE_WHITESPACE_EOL)
|
|
Packit Service |
20376f |
xo->params.flags |= XDF_IGNORE_WHITESPACE_AT_EOL;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (flags & GIT_DIFF_PATIENCE)
|
|
Packit Service |
20376f |
xo->params.flags |= XDF_PATIENCE_DIFF;
|
|
Packit Service |
20376f |
if (flags & GIT_DIFF_MINIMAL)
|
|
Packit Service |
20376f |
xo->params.flags |= XDF_NEED_MINIMAL;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
xo->callback.outf = git_xdiff_cb;
|
|
Packit Service |
20376f |
}
|