]> git.proxmox.com Git - libgit2.git/blame - src/diff_xdiff.c
start implementing diff driver registry
[libgit2.git] / src / diff_xdiff.c
CommitLineData
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
13static 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
27static 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
52typedef struct {
53 git_xdiff_output *xo;
54 git_diff_patch *patch;
55 git_diff_range range;
56} git_xdiff_info;
57
58static 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
108static 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
141void 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}