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 | } |