Coverage Report

Created: 2024-09-08 06:23

/src/git/xdiff-interface.c
Line
Count
Source (jump to first uncovered line)
1
#define USE_THE_REPOSITORY_VARIABLE
2
3
#include "git-compat-util.h"
4
#include "gettext.h"
5
#include "config.h"
6
#include "hex.h"
7
#include "object-store-ll.h"
8
#include "strbuf.h"
9
#include "xdiff-interface.h"
10
#include "xdiff/xtypes.h"
11
#include "xdiff/xdiffi.h"
12
#include "xdiff/xutils.h"
13
14
struct xdiff_emit_state {
15
  xdiff_emit_hunk_fn hunk_fn;
16
  xdiff_emit_line_fn line_fn;
17
  void *consume_callback_data;
18
  struct strbuf remainder;
19
};
20
21
static int xdiff_out_hunk(void *priv_,
22
        long old_begin, long old_nr,
23
        long new_begin, long new_nr,
24
        const char *func, long funclen)
25
0
{
26
0
  struct xdiff_emit_state *priv = priv_;
27
28
0
  if (priv->remainder.len)
29
0
    BUG("xdiff emitted hunk in the middle of a line");
30
31
0
  priv->hunk_fn(priv->consume_callback_data,
32
0
          old_begin, old_nr, new_begin, new_nr,
33
0
          func, funclen);
34
0
  return 0;
35
0
}
36
37
static int consume_one(void *priv_, char *s, unsigned long size)
38
0
{
39
0
  struct xdiff_emit_state *priv = priv_;
40
0
  char *ep;
41
0
  while (size) {
42
0
    unsigned long this_size;
43
0
    int ret;
44
0
    ep = memchr(s, '\n', size);
45
0
    this_size = (ep == NULL) ? size : (ep - s + 1);
46
0
    ret = priv->line_fn(priv->consume_callback_data, s, this_size);
47
0
    if (ret)
48
0
      return ret;
49
0
    size -= this_size;
50
0
    s += this_size;
51
0
  }
52
0
  return 0;
53
0
}
54
55
static int xdiff_outf(void *priv_, mmbuffer_t *mb, int nbuf)
56
0
{
57
0
  struct xdiff_emit_state *priv = priv_;
58
0
  int i;
59
0
  int stop = 0;
60
61
0
  if (!priv->line_fn)
62
0
    return 0;
63
64
0
  for (i = 0; i < nbuf; i++) {
65
0
    if (stop)
66
0
      return 1;
67
0
    if (mb[i].ptr[mb[i].size-1] != '\n') {
68
      /* Incomplete line */
69
0
      strbuf_add(&priv->remainder, mb[i].ptr, mb[i].size);
70
0
      continue;
71
0
    }
72
73
    /* we have a complete line */
74
0
    if (!priv->remainder.len) {
75
0
      stop = consume_one(priv, mb[i].ptr, mb[i].size);
76
0
      continue;
77
0
    }
78
0
    strbuf_add(&priv->remainder, mb[i].ptr, mb[i].size);
79
0
    stop = consume_one(priv, priv->remainder.buf, priv->remainder.len);
80
0
    strbuf_reset(&priv->remainder);
81
0
  }
82
0
  if (stop)
83
0
    return -1;
84
0
  if (priv->remainder.len) {
85
0
    stop = consume_one(priv, priv->remainder.buf, priv->remainder.len);
86
0
    strbuf_reset(&priv->remainder);
87
0
  }
88
0
  if (stop)
89
0
    return -1;
90
0
  return 0;
91
0
}
92
93
/*
94
 * Trim down common substring at the end of the buffers,
95
 * but end on a complete line.
96
 */
97
static void trim_common_tail(mmfile_t *a, mmfile_t *b)
98
0
{
99
0
  const int blk = 1024;
100
0
  long trimmed = 0, recovered = 0;
101
0
  char *ap = a->size ? a->ptr + a->size : a->ptr;
102
0
  char *bp = b->size ? b->ptr + b->size : b->ptr;
103
0
  long smaller = (a->size < b->size) ? a->size : b->size;
104
105
0
  while (blk + trimmed <= smaller && !memcmp(ap - blk, bp - blk, blk)) {
106
0
    trimmed += blk;
107
0
    ap -= blk;
108
0
    bp -= blk;
109
0
  }
110
111
0
  while (recovered < trimmed)
112
0
    if (ap[recovered++] == '\n')
113
0
      break;
114
0
  a->size -= trimmed - recovered;
115
0
  b->size -= trimmed - recovered;
116
0
}
117
118
int xdi_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp, xdemitconf_t const *xecfg, xdemitcb_t *xecb)
119
0
{
120
0
  mmfile_t a = *mf1;
121
0
  mmfile_t b = *mf2;
122
123
0
  if (mf1->size > MAX_XDIFF_SIZE || mf2->size > MAX_XDIFF_SIZE)
124
0
    return -1;
125
126
0
  if (!xecfg->ctxlen && !(xecfg->flags & XDL_EMIT_FUNCCONTEXT))
127
0
    trim_common_tail(&a, &b);
128
129
0
  return xdl_diff(&a, &b, xpp, xecfg, xecb);
130
0
}
131
132
int xdi_diff_outf(mmfile_t *mf1, mmfile_t *mf2,
133
      xdiff_emit_hunk_fn hunk_fn,
134
      xdiff_emit_line_fn line_fn,
135
      void *consume_callback_data,
136
      xpparam_t const *xpp, xdemitconf_t const *xecfg)
137
0
{
138
0
  int ret;
139
0
  struct xdiff_emit_state state;
140
0
  xdemitcb_t ecb;
141
142
0
  memset(&state, 0, sizeof(state));
143
0
  state.hunk_fn = hunk_fn;
144
0
  state.line_fn = line_fn;
145
0
  state.consume_callback_data = consume_callback_data;
146
0
  memset(&ecb, 0, sizeof(ecb));
147
0
  if (hunk_fn)
148
0
    ecb.out_hunk = xdiff_out_hunk;
149
0
  ecb.out_line = xdiff_outf;
150
0
  ecb.priv = &state;
151
0
  strbuf_init(&state.remainder, 0);
152
0
  ret = xdi_diff(mf1, mf2, xpp, xecfg, &ecb);
153
0
  strbuf_release(&state.remainder);
154
0
  return ret;
155
0
}
156
157
int read_mmfile(mmfile_t *ptr, const char *filename)
158
0
{
159
0
  struct stat st;
160
0
  FILE *f;
161
0
  size_t sz;
162
163
0
  if (stat(filename, &st))
164
0
    return error_errno("Could not stat %s", filename);
165
0
  if (!(f = fopen(filename, "rb")))
166
0
    return error_errno("Could not open %s", filename);
167
0
  sz = xsize_t(st.st_size);
168
0
  ptr->ptr = xmalloc(sz ? sz : 1);
169
0
  if (sz && fread(ptr->ptr, sz, 1, f) != 1) {
170
0
    fclose(f);
171
0
    return error("Could not read %s", filename);
172
0
  }
173
0
  fclose(f);
174
0
  ptr->size = sz;
175
0
  return 0;
176
0
}
177
178
void read_mmblob(mmfile_t *ptr, const struct object_id *oid)
179
0
{
180
0
  unsigned long size;
181
0
  enum object_type type;
182
183
0
  if (oideq(oid, null_oid())) {
184
0
    ptr->ptr = xstrdup("");
185
0
    ptr->size = 0;
186
0
    return;
187
0
  }
188
189
0
  ptr->ptr = repo_read_object_file(the_repository, oid, &type, &size);
190
0
  if (!ptr->ptr || type != OBJ_BLOB)
191
0
    die("unable to read blob object %s", oid_to_hex(oid));
192
0
  ptr->size = size;
193
0
}
194
195
0
#define FIRST_FEW_BYTES 8000
196
int buffer_is_binary(const char *ptr, unsigned long size)
197
0
{
198
0
  if (FIRST_FEW_BYTES < size)
199
0
    size = FIRST_FEW_BYTES;
200
0
  return !!memchr(ptr, 0, size);
201
0
}
202
203
struct ff_regs {
204
  int nr;
205
  struct ff_reg {
206
    regex_t re;
207
    int negate;
208
  } *array;
209
};
210
211
static long ff_regexp(const char *line, long len,
212
    char *buffer, long buffer_size, void *priv)
213
0
{
214
0
  struct ff_regs *regs = priv;
215
0
  regmatch_t pmatch[2];
216
0
  int i;
217
0
  int result;
218
219
  /* Exclude terminating newline (and cr) from matching */
220
0
  if (len > 0 && line[len-1] == '\n') {
221
0
    if (len > 1 && line[len-2] == '\r')
222
0
      len -= 2;
223
0
    else
224
0
      len--;
225
0
  }
226
227
0
  for (i = 0; i < regs->nr; i++) {
228
0
    struct ff_reg *reg = regs->array + i;
229
0
    if (!regexec_buf(&reg->re, line, len, 2, pmatch, 0)) {
230
0
      if (reg->negate)
231
0
        return -1;
232
0
      break;
233
0
    }
234
0
  }
235
0
  if (regs->nr <= i)
236
0
    return -1;
237
0
  i = pmatch[1].rm_so >= 0 ? 1 : 0;
238
0
  line += pmatch[i].rm_so;
239
0
  result = pmatch[i].rm_eo - pmatch[i].rm_so;
240
0
  if (result > buffer_size)
241
0
    result = buffer_size;
242
0
  while (result > 0 && (isspace(line[result - 1])))
243
0
    result--;
244
0
  memcpy(buffer, line, result);
245
0
  return result;
246
0
}
247
248
void xdiff_set_find_func(xdemitconf_t *xecfg, const char *value, int cflags)
249
0
{
250
0
  int i;
251
0
  struct ff_regs *regs;
252
253
0
  xecfg->find_func = ff_regexp;
254
0
  regs = xecfg->find_func_priv = xmalloc(sizeof(struct ff_regs));
255
0
  for (i = 0, regs->nr = 1; value[i]; i++)
256
0
    if (value[i] == '\n')
257
0
      regs->nr++;
258
0
  ALLOC_ARRAY(regs->array, regs->nr);
259
0
  for (i = 0; i < regs->nr; i++) {
260
0
    struct ff_reg *reg = regs->array + i;
261
0
    const char *ep, *expression;
262
0
    char *buffer = NULL;
263
264
0
    if (!value)
265
0
      BUG("mismatch between line count and parsing");
266
0
    ep = strchr(value, '\n');
267
268
0
    reg->negate = (*value == '!');
269
0
    if (reg->negate && i == regs->nr - 1)
270
0
      die("Last expression must not be negated: %s", value);
271
0
    if (*value == '!')
272
0
      value++;
273
0
    if (ep)
274
0
      expression = buffer = xstrndup(value, ep - value);
275
0
    else
276
0
      expression = value;
277
0
    if (regcomp(&reg->re, expression, cflags))
278
0
      die("Invalid regexp to look for hunk header: %s", expression);
279
0
    free(buffer);
280
0
    value = ep ? ep + 1 : NULL;
281
0
  }
282
0
}
283
284
void xdiff_clear_find_func(xdemitconf_t *xecfg)
285
0
{
286
0
  if (xecfg->find_func) {
287
0
    int i;
288
0
    struct ff_regs *regs = xecfg->find_func_priv;
289
290
0
    for (i = 0; i < regs->nr; i++)
291
0
      regfree(&regs->array[i].re);
292
0
    free(regs->array);
293
0
    free(regs);
294
0
    xecfg->find_func = NULL;
295
0
    xecfg->find_func_priv = NULL;
296
0
  }
297
0
}
298
299
unsigned long xdiff_hash_string(const char *s, size_t len, long flags)
300
0
{
301
0
  return xdl_hash_record(&s, s + len, flags);
302
0
}
303
304
int xdiff_compare_lines(const char *l1, long s1,
305
      const char *l2, long s2, long flags)
306
0
{
307
0
  return xdl_recmatch(l1, s1, l2, s2, flags);
308
0
}
309
310
int parse_conflict_style_name(const char *value)
311
0
{
312
0
  if (!strcmp(value, "diff3"))
313
0
    return XDL_MERGE_DIFF3;
314
0
  else if (!strcmp(value, "zdiff3"))
315
0
    return XDL_MERGE_ZEALOUS_DIFF3;
316
0
  else if (!strcmp(value, "merge"))
317
0
    return 0;
318
  /*
319
   * Please update _git_checkout() in git-completion.bash when
320
   * you add new merge config
321
   */
322
0
  else
323
0
    return -1;
324
0
}
325
326
int git_xmerge_style = -1;
327
328
int git_xmerge_config(const char *var, const char *value,
329
          const struct config_context *ctx, void *cb)
330
0
{
331
0
  if (!strcmp(var, "merge.conflictstyle")) {
332
0
    if (!value)
333
0
      return config_error_nonbool(var);
334
0
    git_xmerge_style = parse_conflict_style_name(value);
335
0
    if (git_xmerge_style == -1)
336
0
      return error(_("unknown style '%s' given for '%s'"),
337
0
             value, var);
338
0
    return 0;
339
0
  }
340
0
  return git_default_config(var, value, ctx, cb);
341
0
}