Bug Summary

File:tog/../lib/diff_main.c
Warning:line 250, column 25
Access to field 'left_count' results in a dereference of a null pointer (loaded from variable 'new_chunk')

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd6.9 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name diff_main.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/local/lib/clang/11.1.0 -I /home/ben/Projects/got/tog/../include -I /home/ben/Projects/got/tog/../lib -D GOT_LIBEXECDIR=/home/ben/bin -D GOT_VERSION=0.53-current -internal-isystem /usr/local/lib/clang/11.1.0/include -internal-externc-isystem /usr/include -O0 -fdebug-compilation-dir /home/ben/Projects/got/tog/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fgnuc-version=4.2.1 -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -o /home/ben/Projects/got/scan/2021-05-28-230913-68537-1 -x c /home/ben/Projects/got/tog/../lib/diff_main.c
1/* Generic infrastructure to implement various diff algorithms (implementation). */
2/*
3 * Copyright (c) 2020 Neels Hofmeyr <neels@hofmeyr.de>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18
19#include <sys/queue.h>
20#include <ctype.h>
21#include <errno(*__errno()).h>
22#include <stdint.h>
23#include <stdlib.h>
24#include <stdbool.h>
25#include <stdio.h>
26#include <string.h>
27#include <limits.h>
28#include <unistd.h>
29
30#include <assert.h>
31
32#include <arraylist.h>
33#include <diff_main.h>
34
35#include "diff_internal.h"
36#include "diff_debug.h"
37
38static int
39read_at(FILE *f, off_t at_pos, unsigned char *buf, size_t len)
40{
41 int r;
42 if (fseeko(f, at_pos, SEEK_SET0) == -1)
43 return errno(*__errno());
44 r = fread(buf, sizeof(char), len, f);
45 if ((r == 0 || r < len) && ferror(f)(!__isthreaded ? (((f)->_flags & 0x0040) != 0) : (ferror
)(f))
)
46 return errno(*__errno());
47 if (r != len)
48 return EIO5;
49 return 0;
50}
51
52static int
53buf_cmp(const unsigned char *left, size_t left_len,
54 const unsigned char *right, size_t right_len,
55 bool_Bool ignore_whitespace)
56{
57 int cmp;
58
59 if (ignore_whitespace) {
60 int il = 0, ir = 0;
61 while (il < left_len && ir < right_len) {
62 unsigned char cl = left[il];
63 unsigned char cr = right[ir];
64
65 if (isspace(cl) && il < left_len) {
66 il++;
67 continue;
68 }
69 if (isspace(cr) && ir < right_len) {
70 ir++;
71 continue;
72 }
73
74 if (cl > cr)
75 return 1;
76 if (cr > cl)
77 return -1;
78 il++;
79 ir++;
80 }
81 while (il < left_len) {
82 unsigned char cl = left[il++];
83 if (!isspace(cl))
84 return 1;
85 }
86 while (ir < right_len) {
87 unsigned char cr = right[ir++];
88 if (!isspace(cr))
89 return -1;
90 }
91
92 return 0;
93 }
94
95 cmp = memcmp(left, right, MIN(left_len, right_len)((left_len)<(right_len)?(left_len):(right_len)));
96 if (cmp)
97 return cmp;
98 if (left_len == right_len)
99 return 0;
100 return (left_len > right_len) ? 1 : -1;
101}
102
103int
104diff_atom_cmp(int *cmp,
105 const struct diff_atom *left,
106 const struct diff_atom *right)
107{
108 off_t remain_left, remain_right;
109 int flags = (left->root->diff_flags | right->root->diff_flags);
110 bool_Bool ignore_whitespace = (flags & DIFF_FLAG_IGNORE_WHITESPACE0x00000001);
111
112 if (!left->len && !right->len) {
113 *cmp = 0;
114 return 0;
115 }
116 if (!ignore_whitespace) {
117 if (!right->len) {
118 *cmp = 1;
119 return 0;
120 }
121 if (!left->len) {
122 *cmp = -1;
123 return 0;
124 }
125 }
126
127 if (left->at != NULL((void *)0) && right->at != NULL((void *)0)) {
128 *cmp = buf_cmp(left->at, left->len, right->at, right->len,
129 ignore_whitespace);
130 return 0;
131 }
132
133 remain_left = left->len;
134 remain_right = right->len;
135 while (remain_left > 0 || remain_right > 0) {
136 const size_t chunksz = 8192;
137 unsigned char buf_left[chunksz], buf_right[chunksz];
138 const uint8_t *p_left, *p_right;
139 off_t n_left, n_right;
140 ssize_t r;
141
142 if (!remain_right) {
143 *cmp = 1;
144 return 0;
145 }
146 if (!remain_left) {
147 *cmp = -1;
148 return 0;
149 }
150
151 n_left = MIN(chunksz, remain_left)((chunksz)<(remain_left)?(chunksz):(remain_left));
152 n_right = MIN(chunksz, remain_right)((chunksz)<(remain_right)?(chunksz):(remain_right));
153
154 if (left->at == NULL((void *)0)) {
155 r = read_at(left->root->f,
156 left->pos + (left->len - remain_left),
157 buf_left, n_left);
158 if (r) {
159 *cmp = 0;
160 return r;
161 }
162 p_left = buf_left;
163 } else {
164 p_left = left->at + (left->len - remain_left);
165 }
166
167 if (right->at == NULL((void *)0)) {
168 r = read_at(right->root->f,
169 right->pos + (right->len - remain_right),
170 buf_right, n_right);
171 if (r) {
172 *cmp = 0;
173 return r;
174 }
175 p_right = buf_right;
176 } else {
177 p_right = right->at + (right->len - remain_right);
178 }
179
180 r = buf_cmp(p_left, n_left, p_right, n_right,
181 ignore_whitespace);
182 if (r) {
183 *cmp = r;
184 return 0;
185 }
186
187 remain_left -= n_left;
188 remain_right -= n_right;
189 }
190
191 *cmp = 0;
192 return 0;
193}
194
195int
196diff_atom_same(bool_Bool *same,
197 const struct diff_atom *left,
198 const struct diff_atom *right)
199{
200 int cmp;
201 int r;
202 if (left->hash != right->hash) {
203 *same = false0;
204 return 0;
205 }
206 r = diff_atom_cmp(&cmp, left, right);
207 if (r) {
208 *same = true1;
209 return r;
210 }
211 *same = (cmp == 0);
212 return 0;
213}
214
215static struct diff_chunk *
216diff_state_add_solved_chunk(struct diff_state *state,
217 const struct diff_chunk *chunk)
218{
219 diff_chunk_arraylist_t *result;
220 struct diff_chunk *new_chunk;
221 enum diff_chunk_type last_t;
222 enum diff_chunk_type new_t;
223 struct diff_chunk *last;
224
225 /* Append to solved chunks; make sure that adjacent chunks of same type are combined, and that a minus chunk
226 * never directly follows a plus chunk. */
227 result = &state->result->chunks;
228
229 last_t = result->len
21.1
Field 'len' is 0
? diff_chunk_type(&result->head[result->len - 1])
22
'?' condition is false
230 : CHUNK_EMPTY;
231 new_t = diff_chunk_type(chunk);
232
233 debug("ADD %s chunk #%u:\n", chunk->solved ? "solved" : "UNSOLVED",
234 result->len);
235 debug("L\n");
236 debug_dump_atoms(&state->left, chunk->left_start, chunk->left_count);
237 debug("R\n");
238 debug_dump_atoms(&state->right, chunk->right_start, chunk->right_count);
239
240 if (result->len
22.1
Field 'len' is 0
) {
23
Taking false branch
241 last = &result->head[result->len - 1];
242 assert(chunk->left_start((chunk->left_start == last->left_start + last->left_count
) ? (void)0 : __assert2("/home/ben/Projects/got/tog/../lib/diff_main.c"
, 243, __func__, "chunk->left_start == last->left_start + last->left_count"
))
243 == last->left_start + last->left_count)((chunk->left_start == last->left_start + last->left_count
) ? (void)0 : __assert2("/home/ben/Projects/got/tog/../lib/diff_main.c"
, 243, __func__, "chunk->left_start == last->left_start + last->left_count"
))
;
244 assert(chunk->right_start((chunk->right_start == last->right_start + last->right_count
) ? (void)0 : __assert2("/home/ben/Projects/got/tog/../lib/diff_main.c"
, 245, __func__, "chunk->right_start == last->right_start + last->right_count"
))
245 == last->right_start + last->right_count)((chunk->right_start == last->right_start + last->right_count
) ? (void)0 : __assert2("/home/ben/Projects/got/tog/../lib/diff_main.c"
, 245, __func__, "chunk->right_start == last->right_start + last->right_count"
))
;
246 }
247
248 if (new_t == last_t) {
24
Assuming 'new_t' is equal to 'last_t'
25
Taking true branch
249 new_chunk = &result->head[result->len - 1];
26
Null pointer value stored to 'new_chunk'
250 new_chunk->left_count += chunk->left_count;
27
Access to field 'left_count' results in a dereference of a null pointer (loaded from variable 'new_chunk')
251 new_chunk->right_count += chunk->right_count;
252 debug(" - added chunk touches previous one of same type, joined:\n");
253 debug("L\n");
254 debug_dump_atoms(&state->left, new_chunk->left_start, new_chunk->left_count);
255 debug("R\n");
256 debug_dump_atoms(&state->right, new_chunk->right_start, new_chunk->right_count);
257 } else if (last_t == CHUNK_PLUS && new_t == CHUNK_MINUS) {
258 enum diff_chunk_type prev_last_t =
259 result->len > 1 ?
260 diff_chunk_type(&result->head[result->len - 2])
261 : CHUNK_EMPTY;
262 /* If a minus-chunk follows a plus-chunk, place it above the plus-chunk->
263 * Is the one before that also a minus? combine. */
264 if (prev_last_t == CHUNK_MINUS) {
265 new_chunk = &result->head[result->len - 2];
266 new_chunk->left_count += chunk->left_count;
267 new_chunk->right_count += chunk->right_count;
268
269 debug(" - added minus-chunk follows plus-chunk,"
270 " put before that plus-chunk and joined"
271 " with preceding minus-chunk:\n");
272 debug("L\n");
273 debug_dump_atoms(&state->left, new_chunk->left_start, new_chunk->left_count);
274 debug("R\n");
275 debug_dump_atoms(&state->right, new_chunk->right_start, new_chunk->right_count);
276 } else {
277 ARRAYLIST_INSERT(new_chunk, *result, result->len - 1)do { int _at_idx = (result->len - 1); do { if ((*result).len
&& !(*result).allocated) { new_chunk = ((void *)0); break
; } if ((*result).head == ((void *)0) || (*result).allocated <
(*result).len + 1) { (*result).p = recallocarray((*result).head
, (*result).len, (*result).allocated + ((*result).alloc_blocksize
? : 8), sizeof(*(*result).head)); if ((*result).p == ((void *
)0)) { new_chunk = ((void *)0); break; } (*result).allocated +=
(*result).alloc_blocksize ? : 8; (*result).head = (*result).
p; (*result).p = ((void *)0); }; if ((*result).head == ((void
*)0) || (*result).allocated < (*result).len + 1) { new_chunk
= ((void *)0); break; } (new_chunk) = &(*result).head[(*
result).len]; (*result).len++; } while (0); if ((new_chunk) &&
_at_idx >= 0 && _at_idx < (*result).len) { memmove
(&(*result).head[_at_idx + 1], &(*result).head[_at_idx
], ((*result).len - 1 - _at_idx) * sizeof(*(*result).head)); (
new_chunk) = &(*result).head[_at_idx]; }; } while (0)
;
278 if (!new_chunk)
279 return NULL((void *)0);
280 *new_chunk = *chunk;
281
282 /* The new minus chunk indicates to which position on
283 * the right it corresponds, even though it doesn't add
284 * any lines on the right. By moving above a plus chunk,
285 * that position on the right has shifted. */
286 last = &result->head[result->len - 1];
287 new_chunk->right_start = last->right_start;
288
289 debug(" - added minus-chunk follows plus-chunk,"
290 " put before that plus-chunk\n");
291 }
292
293 /* That last_t == CHUNK_PLUS indicates to which position on the
294 * left it corresponds, even though it doesn't add any lines on
295 * the left. By inserting/extending the prev_last_t ==
296 * CHUNK_MINUS, that position on the left has shifted. */
297 last = &result->head[result->len - 1];
298 last->left_start = new_chunk->left_start
299 + new_chunk->left_count;
300
301 } else {
302 ARRAYLIST_ADD(new_chunk, *result)do { if ((*result).len && !(*result).allocated) { new_chunk
= ((void *)0); break; } if ((*result).head == ((void *)0) ||
(*result).allocated < (*result).len + 1) { (*result).p = recallocarray
((*result).head, (*result).len, (*result).allocated + ((*result
).alloc_blocksize ? : 8), sizeof(*(*result).head)); if ((*result
).p == ((void *)0)) { new_chunk = ((void *)0); break; } (*result
).allocated += (*result).alloc_blocksize ? : 8; (*result).head
= (*result).p; (*result).p = ((void *)0); }; if ((*result).head
== ((void *)0) || (*result).allocated < (*result).len + 1
) { new_chunk = ((void *)0); break; } (new_chunk) = &(*result
).head[(*result).len]; (*result).len++; } while (0)
;
303 if (!new_chunk)
304 return NULL((void *)0);
305 *new_chunk = *chunk;
306 }
307 return new_chunk;
308}
309
310/* Even if a left or right side is empty, diff output may need to know the
311 * position in that file.
312 * So left_start or right_start must never be NULL -- pass left_count or
313 * right_count as zero to indicate staying at that position without consuming
314 * any lines. */
315struct diff_chunk *
316diff_state_add_chunk(struct diff_state *state, bool_Bool solved,
317 struct diff_atom *left_start, unsigned int left_count,
318 struct diff_atom *right_start, unsigned int right_count)
319{
320 struct diff_chunk *new_chunk;
321 struct diff_chunk chunk = {
322 .solved = solved,
323 .left_start = left_start,
324 .left_count = left_count,
325 .right_start = right_start,
326 .right_count = right_count,
327 };
328
329 /* An unsolved chunk means store as intermediate result for later
330 * re-iteration.
331 * If there already are intermediate results, that means even a
332 * following solved chunk needs to go to intermediate results, so that
333 * it is later put in the final correct position in solved chunks.
334 */
335 if (!solved
19.1
'solved' is true
|| state->temp_result.len
19.2
Field 'len' is 0
) {
20
Taking false branch
336 /* Append to temp_result */
337 debug("ADD %s chunk to temp result:\n",
338 chunk.solved ? "solved" : "UNSOLVED");
339 debug("L\n");
340 debug_dump_atoms(&state->left, left_start, left_count);
341 debug("R\n");
342 debug_dump_atoms(&state->right, right_start, right_count);
343 ARRAYLIST_ADD(new_chunk, state->temp_result)do { if ((state->temp_result).len && !(state->temp_result
).allocated) { new_chunk = ((void *)0); break; } if ((state->
temp_result).head == ((void *)0) || (state->temp_result).allocated
< (state->temp_result).len + 1) { (state->temp_result
).p = recallocarray((state->temp_result).head, (state->
temp_result).len, (state->temp_result).allocated + ((state
->temp_result).alloc_blocksize ? : 8), sizeof(*(state->
temp_result).head)); if ((state->temp_result).p == ((void *
)0)) { new_chunk = ((void *)0); break; } (state->temp_result
).allocated += (state->temp_result).alloc_blocksize ? : 8;
(state->temp_result).head = (state->temp_result).p; (state
->temp_result).p = ((void *)0); }; if ((state->temp_result
).head == ((void *)0) || (state->temp_result).allocated <
(state->temp_result).len + 1) { new_chunk = ((void *)0); break
; } (new_chunk) = &(state->temp_result).head[(state->
temp_result).len]; (state->temp_result).len++; } while (0)
;
344 if (!new_chunk)
345 return NULL((void *)0);
346 *new_chunk = chunk;
347 return new_chunk;
348 }
349
350 return diff_state_add_solved_chunk(state, &chunk);
21
Calling 'diff_state_add_solved_chunk'
351}
352
353void
354diff_data_init_root(struct diff_data *d, FILE *f, const uint8_t *data,
355 unsigned long long len, int diff_flags)
356{
357 *d = (struct diff_data){
358 .f = f,
359 .pos = 0,
360 .data = data,
361 .len = len,
362 .root = d,
363 .diff_flags = diff_flags,
364 };
365}
366
367void
368diff_data_init_subsection(struct diff_data *d, struct diff_data *parent,
369 struct diff_atom *from_atom, unsigned int atoms_count)
370{
371 struct diff_atom *last_atom;
372
373 debug("diff_data %p parent %p from_atom %p atoms_count %u\n",
374 d, parent, from_atom, atoms_count);
375 debug(" from_atom ");
376 debug_dump_atom(parent, NULL, from_atom);
377
378 if (atoms_count == 0) {
379 *d = (struct diff_data){
380 .f = NULL((void *)0),
381 .pos = 0,
382 .data = NULL((void *)0),
383 .len = 0,
384 .root = parent->root,
385 .atoms.head = NULL((void *)0),
386 .atoms.len = atoms_count,
387 };
388
389 return;
390 }
391
392 last_atom = from_atom + atoms_count - 1;
393 *d = (struct diff_data){
394 .f = NULL((void *)0),
395 .pos = from_atom->pos,
396 .data = from_atom->at,
397 .len = (last_atom->pos + last_atom->len) - from_atom->pos,
398 .root = parent->root,
399 .atoms.head = from_atom,
400 .atoms.len = atoms_count,
401 };
402
403 debug("subsection:\n");
404 debug_dump(d);
405}
406
407void
408diff_data_free(struct diff_data *diff_data)
409{
410 if (!diff_data)
411 return;
412 if (diff_data->atoms.allocated)
413 ARRAYLIST_FREE(diff_data->atoms)do { if ((diff_data->atoms).head && (diff_data->
atoms).allocated) free((diff_data->atoms).head); do { (diff_data
->atoms).head = ((void *)0); (diff_data->atoms).len = 0
; (diff_data->atoms).allocated = 0; (diff_data->atoms).
alloc_blocksize = (diff_data->atoms).alloc_blocksize; } while
(0); } while(0)
;
414}
415
416int
417diff_algo_none(const struct diff_algo_config *algo_config,
418 struct diff_state *state)
419{
420 debug("\n** %s\n", __func__);
421 debug("left:\n");
422 debug_dump(&state->left);
423 debug("right:\n");
424 debug_dump(&state->right);
425 debug_dump_myers_graph(&state->left, &state->right, NULL, NULL, 0, NULL,
426 0);
427
428 /* Add a chunk of equal lines, if any */
429 struct diff_atom *l = state->left.atoms.head;
430 unsigned int l_len = state->left.atoms.len;
431 struct diff_atom *r = state->right.atoms.head;
432 unsigned int r_len = state->right.atoms.len;
433 unsigned int equal_atoms_start = 0;
434 unsigned int equal_atoms_end = 0;
435 unsigned int l_idx = 0;
436 unsigned int r_idx = 0;
437
438 while (equal_atoms_start
8.1
'equal_atoms_start' is < 'l_len'
< l_len
9
Loop condition is true. Entering loop body
439 && equal_atoms_start
8.2
'equal_atoms_start' is < 'r_len'
< r_len) {
440 int err;
441 bool_Bool same;
442 err = diff_atom_same(&same, &l[equal_atoms_start],
443 &r[equal_atoms_start]);
444 if (err
9.1
'err' is 0
)
10
Taking false branch
445 return err;
446 if (!same
10.1
'same' is false
)
11
Taking true branch
447 break;
12
Execution continues on line 450
448 equal_atoms_start++;
449 }
450 while (equal_atoms_end < (l_len - equal_atoms_start)
13
Loop condition is true. Entering loop body
451 && equal_atoms_end < (r_len - equal_atoms_start)) {
452 int err;
453 bool_Bool same;
454 err = diff_atom_same(&same, &l[l_len - 1 - equal_atoms_end],
455 &r[r_len - 1 - equal_atoms_end]);
456 if (err
13.1
'err' is 0
)
14
Taking false branch
457 return err;
458 if (!same
14.1
'same' is false
)
15
Taking true branch
459 break;
16
Execution continues on line 464
460 equal_atoms_end++;
461 }
462
463 /* Add a chunk of equal lines at the start */
464 if (equal_atoms_start
16.1
'equal_atoms_start' is 0
) {
17
Taking false branch
465 if (!diff_state_add_chunk(state, true1,
466 l, equal_atoms_start,
467 r, equal_atoms_start))
468 return ENOMEM12;
469 l_idx += equal_atoms_start;
470 r_idx += equal_atoms_start;
471 }
472
473 /* Add a "minus" chunk with all lines from the left. */
474 if (equal_atoms_start + equal_atoms_end < l_len) {
18
Taking true branch
475 unsigned int add_len = l_len - equal_atoms_start - equal_atoms_end;
476 if (!diff_state_add_chunk(state, true1,
19
Calling 'diff_state_add_chunk'
477 &l[l_idx], add_len,
478 &r[r_idx], 0))
479 return ENOMEM12;
480 l_idx += add_len;
481 }
482
483 /* Add a "plus" chunk with all lines from the right. */
484 if (equal_atoms_start + equal_atoms_end < r_len) {
485 unsigned int add_len = r_len - equal_atoms_start - equal_atoms_end;
486 if (!diff_state_add_chunk(state, true1,
487 &l[l_idx], 0,
488 &r[r_idx], add_len))
489 return ENOMEM12;
490 r_idx += add_len;
491 }
492
493 /* Add a chunk of equal lines at the end */
494 if (equal_atoms_end) {
495 if (!diff_state_add_chunk(state, true1,
496 &l[l_idx], equal_atoms_end,
497 &r[r_idx], equal_atoms_end))
498 return ENOMEM12;
499 }
500
501 return DIFF_RC_OK0;
502}
503
504int
505diff_run_algo(const struct diff_algo_config *algo_config,
506 struct diff_state *state)
507{
508 int rc;
509
510 if (!algo_config || !algo_config->impl
6
Assuming 'algo_config' is non-null
7
Assuming field 'impl' is null
511 || !state->recursion_depth_left
512 || !state->left.atoms.len || !state->right.atoms.len) {
513 debug("Fall back to diff_algo_none():%s%s%s\n",
514 (!algo_config || !algo_config->impl) ? " no-cfg" : "",
515 (!state->recursion_depth_left) ? " max-depth" : "",
516 (!state->left.atoms.len || !state->right.atoms.len)?
517 " trivial" : "");
518 return diff_algo_none(algo_config, state);
8
Calling 'diff_algo_none'
519 }
520
521 ARRAYLIST_FREE(state->temp_result)do { if ((state->temp_result).head && (state->temp_result
).allocated) free((state->temp_result).head); do { (state->
temp_result).head = ((void *)0); (state->temp_result).len =
0; (state->temp_result).allocated = 0; (state->temp_result
).alloc_blocksize = (state->temp_result).alloc_blocksize; }
while(0); } while(0)
;
522 ARRAYLIST_INIT(state->temp_result, DIFF_RESULT_ALLOC_BLOCKSIZE)do { (state->temp_result).head = ((void *)0); (state->temp_result
).len = 0; (state->temp_result).allocated = 0; (state->
temp_result).alloc_blocksize = 128; } while(0)
;
523 rc = algo_config->impl(algo_config, state);
524 switch (rc) {
525 case DIFF_RC_USE_DIFF_ALGO_FALLBACK-1:
526 debug("Got DIFF_RC_USE_DIFF_ALGO_FALLBACK (%p)\n",
527 algo_config->fallback_algo);
528 rc = diff_run_algo(algo_config->fallback_algo, state);
529 goto return_rc;
530
531 case DIFF_RC_OK0:
532 /* continue below */
533 break;
534
535 default:
536 /* some error happened */
537 goto return_rc;
538 }
539
540 /* Pick up any diff chunks that are still unsolved and feed to
541 * inner_algo. inner_algo will solve unsolved chunks and append to
542 * result, and subsequent solved chunks on this level are then appended
543 * to result afterwards. */
544 int i;
545 for (i = 0; i < state->temp_result.len; i++) {
546 struct diff_chunk *c = &state->temp_result.head[i];
547 if (c->solved) {
548 diff_state_add_solved_chunk(state, c);
549 continue;
550 }
551
552 /* c is an unsolved chunk, feed to inner_algo */
553 struct diff_state inner_state = {
554 .result = state->result,
555 .recursion_depth_left = state->recursion_depth_left - 1,
556 .kd_buf = state->kd_buf,
557 .kd_buf_size = state->kd_buf_size,
558 };
559 diff_data_init_subsection(&inner_state.left, &state->left,
560 c->left_start, c->left_count);
561 diff_data_init_subsection(&inner_state.right, &state->right,
562 c->right_start, c->right_count);
563
564 rc = diff_run_algo(algo_config->inner_algo, &inner_state);
565 state->kd_buf = inner_state.kd_buf;
566 state->kd_buf_size = inner_state.kd_buf_size;
567 if (rc != DIFF_RC_OK0)
568 goto return_rc;
569 }
570
571 rc = DIFF_RC_OK0;
572return_rc:
573 ARRAYLIST_FREE(state->temp_result)do { if ((state->temp_result).head && (state->temp_result
).allocated) free((state->temp_result).head); do { (state->
temp_result).head = ((void *)0); (state->temp_result).len =
0; (state->temp_result).allocated = 0; (state->temp_result
).alloc_blocksize = (state->temp_result).alloc_blocksize; }
while(0); } while(0)
;
574 return rc;
575}
576
577int
578diff_atomize_file(struct diff_data *d,
579 const struct diff_config *config,
580 FILE *f, const uint8_t *data, off_t len, int diff_flags)
581{
582 if (!config->atomize_func)
583 return EINVAL22;
584
585 diff_data_init_root(d, f, data, len, diff_flags);
586
587 return config->atomize_func(config->atomize_func_data, d);
588
589}
590
591struct diff_result *
592diff_main(const struct diff_config *config, struct diff_data *left,
593 struct diff_data *right)
594{
595 struct diff_result *result = malloc(sizeof(struct diff_result));
596 if (!result)
1
Assuming 'result' is non-null
2
Taking false branch
597 return NULL((void *)0);
598
599 *result = (struct diff_result){};
3
Null pointer value stored to field 'head'
600 result->left = left;
601 result->right = right;
602
603 struct diff_state state = {
604 .result = result,
605 .recursion_depth_left = config->max_recursion_depth ? : UINT_MAX(2147483647 *2U +1U),
4
'?' condition is false
606 .kd_buf = NULL((void *)0),
607 .kd_buf_size = 0,
608 };
609 diff_data_init_subsection(&state.left, left,
610 left->atoms.head,
611 left->atoms.len);
612 diff_data_init_subsection(&state.right, right,
613 right->atoms.head,
614 right->atoms.len);
615
616 result->rc = diff_run_algo(config->algo, &state);
5
Calling 'diff_run_algo'
617 free(state.kd_buf);
618
619 return result;
620}
621
622void
623diff_result_free(struct diff_result *result)
624{
625 if (!result)
626 return;
627 ARRAYLIST_FREE(result->chunks)do { if ((result->chunks).head && (result->chunks
).allocated) free((result->chunks).head); do { (result->
chunks).head = ((void *)0); (result->chunks).len = 0; (result
->chunks).allocated = 0; (result->chunks).alloc_blocksize
= (result->chunks).alloc_blocksize; } while(0); } while(0
)
;
628 free(result);
629}