Coverage Report

Created: 2024-09-08 06:23

/src/git/xdiff/xutils.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 *  LibXDiff by Davide Libenzi ( File Differential Library )
3
 *  Copyright (C) 2003  Davide Libenzi
4
 *
5
 *  This library is free software; you can redistribute it and/or
6
 *  modify it under the terms of the GNU Lesser General Public
7
 *  License as published by the Free Software Foundation; either
8
 *  version 2.1 of the License, or (at your option) any later version.
9
 *
10
 *  This library is distributed in the hope that it will be useful,
11
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
 *  Lesser General Public License for more details.
14
 *
15
 *  You should have received a copy of the GNU Lesser General Public
16
 *  License along with this library; if not, see
17
 *  <http://www.gnu.org/licenses/>.
18
 *
19
 *  Davide Libenzi <davidel@xmailserver.org>
20
 *
21
 */
22
23
#include "xinclude.h"
24
25
26
0
long xdl_bogosqrt(long n) {
27
0
  long i;
28
29
  /*
30
   * Classical integer square root approximation using shifts.
31
   */
32
0
  for (i = 1; n > 0; n >>= 2)
33
0
    i <<= 1;
34
35
0
  return i;
36
0
}
37
38
39
int xdl_emit_diffrec(char const *rec, long size, char const *pre, long psize,
40
0
         xdemitcb_t *ecb) {
41
0
  int i = 2;
42
0
  mmbuffer_t mb[3];
43
44
0
  mb[0].ptr = (char *) pre;
45
0
  mb[0].size = psize;
46
0
  mb[1].ptr = (char *) rec;
47
0
  mb[1].size = size;
48
0
  if (size > 0 && rec[size - 1] != '\n') {
49
0
    mb[2].ptr = (char *) "\n\\ No newline at end of file\n";
50
0
    mb[2].size = strlen(mb[2].ptr);
51
0
    i++;
52
0
  }
53
0
  if (ecb->out_line(ecb->priv, mb, i) < 0) {
54
55
0
    return -1;
56
0
  }
57
58
0
  return 0;
59
0
}
60
61
void *xdl_mmfile_first(mmfile_t *mmf, long *size)
62
0
{
63
0
  *size = mmf->size;
64
0
  return mmf->ptr;
65
0
}
66
67
68
long xdl_mmfile_size(mmfile_t *mmf)
69
0
{
70
0
  return mmf->size;
71
0
}
72
73
74
0
int xdl_cha_init(chastore_t *cha, long isize, long icount) {
75
76
0
  cha->head = cha->tail = NULL;
77
0
  cha->isize = isize;
78
0
  cha->nsize = icount * isize;
79
0
  cha->ancur = cha->sncur = NULL;
80
0
  cha->scurr = 0;
81
82
0
  return 0;
83
0
}
84
85
86
0
void xdl_cha_free(chastore_t *cha) {
87
0
  chanode_t *cur, *tmp;
88
89
0
  for (cur = cha->head; (tmp = cur) != NULL;) {
90
0
    cur = cur->next;
91
0
    xdl_free(tmp);
92
0
  }
93
0
}
94
95
96
0
void *xdl_cha_alloc(chastore_t *cha) {
97
0
  chanode_t *ancur;
98
0
  void *data;
99
100
0
  if (!(ancur = cha->ancur) || ancur->icurr == cha->nsize) {
101
0
    if (!(ancur = (chanode_t *) xdl_malloc(sizeof(chanode_t) + cha->nsize))) {
102
103
0
      return NULL;
104
0
    }
105
0
    ancur->icurr = 0;
106
0
    ancur->next = NULL;
107
0
    if (cha->tail)
108
0
      cha->tail->next = ancur;
109
0
    if (!cha->head)
110
0
      cha->head = ancur;
111
0
    cha->tail = ancur;
112
0
    cha->ancur = ancur;
113
0
  }
114
115
0
  data = (char *) ancur + sizeof(chanode_t) + ancur->icurr;
116
0
  ancur->icurr += cha->isize;
117
118
0
  return data;
119
0
}
120
121
0
long xdl_guess_lines(mmfile_t *mf, long sample) {
122
0
  long nl = 0, size, tsize = 0;
123
0
  char const *data, *cur, *top;
124
125
0
  if ((cur = data = xdl_mmfile_first(mf, &size))) {
126
0
    for (top = data + size; nl < sample && cur < top; ) {
127
0
      nl++;
128
0
      if (!(cur = memchr(cur, '\n', top - cur)))
129
0
        cur = top;
130
0
      else
131
0
        cur++;
132
0
    }
133
0
    tsize += (long) (cur - data);
134
0
  }
135
136
0
  if (nl && tsize)
137
0
    nl = xdl_mmfile_size(mf) / (tsize / nl);
138
139
0
  return nl + 1;
140
0
}
141
142
int xdl_blankline(const char *line, long size, long flags)
143
0
{
144
0
  long i;
145
146
0
  if (!(flags & XDF_WHITESPACE_FLAGS))
147
0
    return (size <= 1);
148
149
0
  for (i = 0; i < size && XDL_ISSPACE(line[i]); i++)
150
0
    ;
151
152
0
  return (i == size);
153
0
}
154
155
/*
156
 * Have we eaten everything on the line, except for an optional
157
 * CR at the very end?
158
 */
159
static int ends_with_optional_cr(const char *l, long s, long i)
160
0
{
161
0
  int complete = s && l[s-1] == '\n';
162
163
0
  if (complete)
164
0
    s--;
165
0
  if (s == i)
166
0
    return 1;
167
  /* do not ignore CR at the end of an incomplete line */
168
0
  if (complete && s == i + 1 && l[i] == '\r')
169
0
    return 1;
170
0
  return 0;
171
0
}
172
173
int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags)
174
0
{
175
0
  int i1, i2;
176
177
0
  if (s1 == s2 && !memcmp(l1, l2, s1))
178
0
    return 1;
179
0
  if (!(flags & XDF_WHITESPACE_FLAGS))
180
0
    return 0;
181
182
0
  i1 = 0;
183
0
  i2 = 0;
184
185
  /*
186
   * -w matches everything that matches with -b, and -b in turn
187
   * matches everything that matches with --ignore-space-at-eol,
188
   * which in turn matches everything that matches with --ignore-cr-at-eol.
189
   *
190
   * Each flavor of ignoring needs different logic to skip whitespaces
191
   * while we have both sides to compare.
192
   */
193
0
  if (flags & XDF_IGNORE_WHITESPACE) {
194
0
    goto skip_ws;
195
0
    while (i1 < s1 && i2 < s2) {
196
0
      if (l1[i1++] != l2[i2++])
197
0
        return 0;
198
0
    skip_ws:
199
0
      while (i1 < s1 && XDL_ISSPACE(l1[i1]))
200
0
        i1++;
201
0
      while (i2 < s2 && XDL_ISSPACE(l2[i2]))
202
0
        i2++;
203
0
    }
204
0
  } else if (flags & XDF_IGNORE_WHITESPACE_CHANGE) {
205
0
    while (i1 < s1 && i2 < s2) {
206
0
      if (XDL_ISSPACE(l1[i1]) && XDL_ISSPACE(l2[i2])) {
207
        /* Skip matching spaces and try again */
208
0
        while (i1 < s1 && XDL_ISSPACE(l1[i1]))
209
0
          i1++;
210
0
        while (i2 < s2 && XDL_ISSPACE(l2[i2]))
211
0
          i2++;
212
0
        continue;
213
0
      }
214
0
      if (l1[i1++] != l2[i2++])
215
0
        return 0;
216
0
    }
217
0
  } else if (flags & XDF_IGNORE_WHITESPACE_AT_EOL) {
218
0
    while (i1 < s1 && i2 < s2 && l1[i1] == l2[i2]) {
219
0
      i1++;
220
0
      i2++;
221
0
    }
222
0
  } else if (flags & XDF_IGNORE_CR_AT_EOL) {
223
    /* Find the first difference and see how the line ends */
224
0
    while (i1 < s1 && i2 < s2 && l1[i1] == l2[i2]) {
225
0
      i1++;
226
0
      i2++;
227
0
    }
228
0
    return (ends_with_optional_cr(l1, s1, i1) &&
229
0
      ends_with_optional_cr(l2, s2, i2));
230
0
  }
231
232
  /*
233
   * After running out of one side, the remaining side must have
234
   * nothing but whitespace for the lines to match.  Note that
235
   * ignore-whitespace-at-eol case may break out of the loop
236
   * while there still are characters remaining on both lines.
237
   */
238
0
  if (i1 < s1) {
239
0
    while (i1 < s1 && XDL_ISSPACE(l1[i1]))
240
0
      i1++;
241
0
    if (s1 != i1)
242
0
      return 0;
243
0
  }
244
0
  if (i2 < s2) {
245
0
    while (i2 < s2 && XDL_ISSPACE(l2[i2]))
246
0
      i2++;
247
0
    return (s2 == i2);
248
0
  }
249
0
  return 1;
250
0
}
251
252
static unsigned long xdl_hash_record_with_whitespace(char const **data,
253
0
    char const *top, long flags) {
254
0
  unsigned long ha = 5381;
255
0
  char const *ptr = *data;
256
0
  int cr_at_eol_only = (flags & XDF_WHITESPACE_FLAGS) == XDF_IGNORE_CR_AT_EOL;
257
258
0
  for (; ptr < top && *ptr != '\n'; ptr++) {
259
0
    if (cr_at_eol_only) {
260
      /* do not ignore CR at the end of an incomplete line */
261
0
      if (*ptr == '\r' &&
262
0
          (ptr + 1 < top && ptr[1] == '\n'))
263
0
        continue;
264
0
    }
265
0
    else if (XDL_ISSPACE(*ptr)) {
266
0
      const char *ptr2 = ptr;
267
0
      int at_eol;
268
0
      while (ptr + 1 < top && XDL_ISSPACE(ptr[1])
269
0
          && ptr[1] != '\n')
270
0
        ptr++;
271
0
      at_eol = (top <= ptr + 1 || ptr[1] == '\n');
272
0
      if (flags & XDF_IGNORE_WHITESPACE)
273
0
        ; /* already handled */
274
0
      else if (flags & XDF_IGNORE_WHITESPACE_CHANGE
275
0
         && !at_eol) {
276
0
        ha += (ha << 5);
277
0
        ha ^= (unsigned long) ' ';
278
0
      }
279
0
      else if (flags & XDF_IGNORE_WHITESPACE_AT_EOL
280
0
         && !at_eol) {
281
0
        while (ptr2 != ptr + 1) {
282
0
          ha += (ha << 5);
283
0
          ha ^= (unsigned long) *ptr2;
284
0
          ptr2++;
285
0
        }
286
0
      }
287
0
      continue;
288
0
    }
289
0
    ha += (ha << 5);
290
0
    ha ^= (unsigned long) *ptr;
291
0
  }
292
0
  *data = ptr < top ? ptr + 1: ptr;
293
294
0
  return ha;
295
0
}
296
297
0
unsigned long xdl_hash_record(char const **data, char const *top, long flags) {
298
0
  unsigned long ha = 5381;
299
0
  char const *ptr = *data;
300
301
0
  if (flags & XDF_WHITESPACE_FLAGS)
302
0
    return xdl_hash_record_with_whitespace(data, top, flags);
303
304
0
  for (; ptr < top && *ptr != '\n'; ptr++) {
305
0
    ha += (ha << 5);
306
0
    ha ^= (unsigned long) *ptr;
307
0
  }
308
0
  *data = ptr < top ? ptr + 1: ptr;
309
310
0
  return ha;
311
0
}
312
313
0
unsigned int xdl_hashbits(unsigned int size) {
314
0
  unsigned int val = 1, bits = 0;
315
316
0
  for (; val < size && bits < CHAR_BIT * sizeof(unsigned int); val <<= 1, bits++);
317
0
  return bits ? bits: 1;
318
0
}
319
320
321
0
int xdl_num_out(char *out, long val) {
322
0
  char *ptr, *str = out;
323
0
  char buf[32];
324
325
0
  ptr = buf + sizeof(buf) - 1;
326
0
  *ptr = '\0';
327
0
  if (val < 0) {
328
0
    *--ptr = '-';
329
0
    val = -val;
330
0
  }
331
0
  for (; val && ptr > buf; val /= 10)
332
0
    *--ptr = "0123456789"[val % 10];
333
0
  if (*ptr)
334
0
    for (; *ptr; ptr++, str++)
335
0
      *str = *ptr;
336
0
  else
337
0
    *str++ = '0';
338
0
  *str = '\0';
339
340
0
  return str - out;
341
0
}
342
343
static int xdl_format_hunk_hdr(long s1, long c1, long s2, long c2,
344
             const char *func, long funclen,
345
0
             xdemitcb_t *ecb) {
346
0
  int nb = 0;
347
0
  mmbuffer_t mb;
348
0
  char buf[128];
349
350
0
  memcpy(buf, "@@ -", 4);
351
0
  nb += 4;
352
353
0
  nb += xdl_num_out(buf + nb, c1 ? s1: s1 - 1);
354
355
0
  if (c1 != 1) {
356
0
    memcpy(buf + nb, ",", 1);
357
0
    nb += 1;
358
359
0
    nb += xdl_num_out(buf + nb, c1);
360
0
  }
361
362
0
  memcpy(buf + nb, " +", 2);
363
0
  nb += 2;
364
365
0
  nb += xdl_num_out(buf + nb, c2 ? s2: s2 - 1);
366
367
0
  if (c2 != 1) {
368
0
    memcpy(buf + nb, ",", 1);
369
0
    nb += 1;
370
371
0
    nb += xdl_num_out(buf + nb, c2);
372
0
  }
373
374
0
  memcpy(buf + nb, " @@", 3);
375
0
  nb += 3;
376
0
  if (func && funclen) {
377
0
    buf[nb++] = ' ';
378
0
    if (funclen > sizeof(buf) - nb - 1)
379
0
      funclen = sizeof(buf) - nb - 1;
380
0
    memcpy(buf + nb, func, funclen);
381
0
    nb += funclen;
382
0
  }
383
0
  buf[nb++] = '\n';
384
385
0
  mb.ptr = buf;
386
0
  mb.size = nb;
387
0
  if (ecb->out_line(ecb->priv, &mb, 1) < 0)
388
0
    return -1;
389
0
  return 0;
390
0
}
391
392
int xdl_emit_hunk_hdr(long s1, long c1, long s2, long c2,
393
          const char *func, long funclen,
394
0
          xdemitcb_t *ecb) {
395
0
  if (!ecb->out_hunk)
396
0
    return xdl_format_hunk_hdr(s1, c1, s2, c2, func, funclen, ecb);
397
0
  if (ecb->out_hunk(ecb->priv,
398
0
        c1 ? s1 : s1 - 1, c1,
399
0
        c2 ? s2 : s2 - 1, c2,
400
0
        func, funclen) < 0)
401
0
    return -1;
402
0
  return 0;
403
0
}
404
405
int xdl_fall_back_diff(xdfenv_t *diff_env, xpparam_t const *xpp,
406
    int line1, int count1, int line2, int count2)
407
0
{
408
  /*
409
   * This probably does not work outside Git, since
410
   * we have a very simple mmfile structure.
411
   *
412
   * Note: ideally, we would reuse the prepared environment, but
413
   * the libxdiff interface does not (yet) allow for diffing only
414
   * ranges of lines instead of the whole files.
415
   */
416
0
  mmfile_t subfile1, subfile2;
417
0
  xdfenv_t env;
418
419
0
  subfile1.ptr = (char *)diff_env->xdf1.recs[line1 - 1]->ptr;
420
0
  subfile1.size = diff_env->xdf1.recs[line1 + count1 - 2]->ptr +
421
0
    diff_env->xdf1.recs[line1 + count1 - 2]->size - subfile1.ptr;
422
0
  subfile2.ptr = (char *)diff_env->xdf2.recs[line2 - 1]->ptr;
423
0
  subfile2.size = diff_env->xdf2.recs[line2 + count2 - 2]->ptr +
424
0
    diff_env->xdf2.recs[line2 + count2 - 2]->size - subfile2.ptr;
425
0
  if (xdl_do_diff(&subfile1, &subfile2, xpp, &env) < 0)
426
0
    return -1;
427
428
0
  memcpy(diff_env->xdf1.rchg + line1 - 1, env.xdf1.rchg, count1);
429
0
  memcpy(diff_env->xdf2.rchg + line2 - 1, env.xdf2.rchg, count2);
430
431
0
  xdl_free_env(&env);
432
433
0
  return 0;
434
0
}
435
436
void* xdl_alloc_grow_helper(void *p, long nr, long *alloc, size_t size)
437
0
{
438
0
  void *tmp = NULL;
439
0
  size_t n = ((LONG_MAX - 16) / 2 >= *alloc) ? 2 * *alloc + 16 : LONG_MAX;
440
0
  if (nr > n)
441
0
    n = nr;
442
0
  if (SIZE_MAX / size >= n)
443
0
    tmp = xdl_realloc(p, n * size);
444
0
  if (tmp) {
445
0
    *alloc = n;
446
0
  } else {
447
0
    xdl_free(p);
448
0
    *alloc = 0;
449
0
  }
450
0
  return tmp;
451
0
}