Line Flow Count Block(s) Source
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 -
8 - #include "patch_parse.h"
9 -
10 - #include "git2/patch.h"
11 - #include "patch.h"
12 - #include "diff_parse.h"
13 - #include "path.h"
14 -
15 - typedef struct {
16 - git_patch base;
17 -
18 - git_patch_parse_ctx *ctx;
19 -
20 - /* the paths from the `diff --git` header, these will be used if this is not
21 - * a rename (and rename paths are specified) or if no `+++`/`---` line specify
22 - * the paths.
23 - */
24 - char *header_old_path, *header_new_path;
25 -
26 - /* renamed paths are precise and are not prefixed */
27 - char *rename_old_path, *rename_new_path;
28 -
29 - /* the paths given in `---` and `+++` lines */
30 - char *old_path, *new_path;
31 -
32 - /* the prefixes from the old/new paths */
33 - char *old_prefix, *new_prefix;
34 - } git_patch_parsed;
35 -
36 - static int git_parse_err(const char *fmt, ...) GIT_FORMAT_PRINTF(1, 2);
37 24 2 static int git_parse_err(const char *fmt, ...)
38 - {
39 - va_list ap;
40 -
41 24 2 va_start(ap, fmt);
42 24 2 git_error_vset(GIT_ERROR_PATCH, fmt, ap);
43 24 3 va_end(ap);
44 -
45 24 3 return -1;
46 - }
47 -
48 542 2 static size_t header_path_len(git_patch_parse_ctx *ctx)
49 - {
50 542 2 bool inquote = 0;
51 542 2 bool quoted = git_parse_ctx_contains_s(&ctx->parse_ctx, "\"");
52 - size_t len;
53 -
54 6399 3,15,16 for (len = quoted; len < ctx->parse_ctx.line_len; len++) {
55 6399 4-6 if (!quoted && git__isspace(ctx->parse_ctx.line[len]))
56 - break;
57 5863 7-9 else if (quoted && !inquote && ctx->parse_ctx.line[len] == '"') {
58 6 10 len++;
59 6 10 break;
60 - }
61 -
62 5857 11-14 inquote = (!inquote && ctx->parse_ctx.line[len] == '\\');
63 - }
64 -
65 542 17 return len;
66 - }
67 -
68 916 2 static int parse_header_path_buf(git_buf *path, git_patch_parse_ctx *ctx, size_t path_len)
69 - {
70 - int error;
71 -
72 916 2,3 if ((error = git_buf_put(path, ctx->parse_ctx.line, path_len)) < 0)
73 ##### 4 return error;
74 -
75 916 5 git_parse_advance_chars(&ctx->parse_ctx, path_len);
76 -
77 916 6 git_buf_rtrim(path);
78 -
79 916 7-10 if (path->size > 0 && path->ptr[0] == '"' &&
80 - (error = git_buf_unquote(path)) < 0)
81 ##### 11 return error;
82 -
83 916 12 git_path_squash_slashes(path);
84 -
85 916 13 if (!path->size)
86 5 14 return git_parse_err("patch contains empty path at line %"PRIuZ,
87 - ctx->parse_ctx.line_num);
88 -
89 911 15 return 0;
90 - }
91 -
92 486 2 static int parse_header_path(char **out, git_patch_parse_ctx *ctx)
93 - {
94 486 2 git_buf path = GIT_BUF_INIT;
95 - int error;
96 -
97 486 2-4 if ((error = parse_header_path_buf(&path, ctx, header_path_len(ctx))) < 0)
98 2 5 goto out;
99 484 6,7 *out = git_buf_detach(&path);
100 -
101 - out:
102 486 8 git_buf_dispose(&path);
103 486 9 return error;
104 - }
105 -
106 190 2 static int parse_header_git_oldpath(
107 - git_patch_parsed *patch, git_patch_parse_ctx *ctx)
108 - {
109 190 2 git_buf old_path = GIT_BUF_INIT;
110 - int error;
111 -
112 190 2 if (patch->old_path) {
113 ##### 3 error = git_parse_err("patch contains duplicate old path at line %"PRIuZ,
114 - ctx->parse_ctx.line_num);
115 ##### 4 goto out;
116 - }
117 -
118 190 5,6 if ((error = parse_header_path_buf(&old_path, ctx, ctx->parse_ctx.line_len - 1)) < 0)
119 3 7 goto out;
120 -
121 187 8,9 patch->old_path = git_buf_detach(&old_path);
122 -
123 - out:
124 190 10 git_buf_dispose(&old_path);
125 190 11 return error;
126 - }
127 -
128 184 2 static int parse_header_git_newpath(
129 - git_patch_parsed *patch, git_patch_parse_ctx *ctx)
130 - {
131 184 2 git_buf new_path = GIT_BUF_INIT;
132 - int error;
133 -
134 184 2 if (patch->new_path) {
135 ##### 3 error = git_parse_err("patch contains duplicate new path at line %"PRIuZ,
136 - ctx->parse_ctx.line_num);
137 ##### 4 goto out;
138 - }
139 -
140 184 5,6 if ((error = parse_header_path_buf(&new_path, ctx, ctx->parse_ctx.line_len - 1)) < 0)
141 ##### 7 goto out;
142 184 8,9 patch->new_path = git_buf_detach(&new_path);
143 -
144 - out:
145 184 10 git_buf_dispose(&new_path);
146 184 11 return error;
147 - }
148 -
149 228 2 static int parse_header_mode(uint16_t *mode, git_patch_parse_ctx *ctx)
150 - {
151 - int64_t m;
152 -
153 228 2,3 if ((git_parse_advance_digit(&m, &ctx->parse_ctx, 8)) < 0)
154 ##### 4 return git_parse_err("invalid file mode at line %"PRIuZ, ctx->parse_ctx.line_num);
155 -
156 228 5 if (m > UINT16_MAX)
157 ##### 6 return -1;
158 -
159 228 7 *mode = (uint16_t)m;
160 -
161 228 7 return 0;
162 - }
163 -
164 422 2 static int parse_header_oid(
165 - git_oid *oid,
166 - uint16_t *oid_len,
167 - git_patch_parse_ctx *ctx)
168 - {
169 - size_t len;
170 -
171 6018 2,6-8 for (len = 0; len < ctx->parse_ctx.line_len && len < GIT_OID_HEXSZ; len++) {
172 5938 3,4 if (!git__isxdigit(ctx->parse_ctx.line[len]))
173 342 5 break;
174 - }
175 -
176 422 9,10,12 if (len < GIT_OID_MINPREFIXLEN || len > GIT_OID_HEXSZ ||
177 422 11 git_oid_fromstrn(oid, ctx->parse_ctx.line, len) < 0)
178 ##### 13 return git_parse_err("invalid hex formatted object id at line %"PRIuZ,
179 - ctx->parse_ctx.line_num);
180 -
181 422 14 git_parse_advance_chars(&ctx->parse_ctx, len);
182 -
183 422 15 *oid_len = (uint16_t)len;
184 -
185 422 15 return 0;
186 - }
187 -
188 211 2 static int parse_header_git_index(
189 - git_patch_parsed *patch, git_patch_parse_ctx *ctx)
190 - {
191 - char c;
192 -
193 211 2,3 if (parse_header_oid(&patch->base.delta->old_file.id,
194 211 2,5 &patch->base.delta->old_file.id_abbrev, ctx) < 0 ||
195 211 4,7 git_parse_advance_expected_str(&ctx->parse_ctx, "..") < 0 ||
196 211 6 parse_header_oid(&patch->base.delta->new_file.id,
197 211 6 &patch->base.delta->new_file.id_abbrev, ctx) < 0)
198 ##### 8 return -1;
199 -
200 211 9-11 if (git_parse_peek(&c, &ctx->parse_ctx, 0) == 0 && c == ' ') {
201 - uint16_t mode;
202 -
203 162 12 git_parse_advance_chars(&ctx->parse_ctx, 1);
204 -
205 162 13,14 if (parse_header_mode(&mode, ctx) < 0)
206 ##### 15 return -1;
207 -
208 162 16 if (!patch->base.delta->new_file.mode)
209 161 17 patch->base.delta->new_file.mode = mode;
210 -
211 162 18 if (!patch->base.delta->old_file.mode)
212 162 19,20 patch->base.delta->old_file.mode = mode;
213 - }
214 -
215 211 21 return 0;
216 - }
217 -
218 10 2 static int parse_header_git_oldmode(
219 - git_patch_parsed *patch, git_patch_parse_ctx *ctx)
220 - {
221 10 2 return parse_header_mode(&patch->base.delta->old_file.mode, ctx);
222 - }
223 -
224 10 2 static int parse_header_git_newmode(
225 - git_patch_parsed *patch, git_patch_parse_ctx *ctx)
226 - {
227 10 2 return parse_header_mode(&patch->base.delta->new_file.mode, ctx);
228 - }
229 -
230 21 2 static int parse_header_git_deletedfilemode(
231 - git_patch_parsed *patch,
232 - git_patch_parse_ctx *ctx)
233 - {
234 21 2 git__free((char *)patch->base.delta->new_file.path);
235 -
236 21 3 patch->base.delta->new_file.path = NULL;
237 21 3 patch->base.delta->status = GIT_DELTA_DELETED;
238 21 3 patch->base.delta->nfiles = 1;
239 -
240 21 3 return parse_header_mode(&patch->base.delta->old_file.mode, ctx);
241 - }
242 -
243 25 2 static int parse_header_git_newfilemode(
244 - git_patch_parsed *patch,
245 - git_patch_parse_ctx *ctx)
246 - {
247 25 2 git__free((char *)patch->base.delta->old_file.path);
248 -
249 25 3 patch->base.delta->old_file.path = NULL;
250 25 3 patch->base.delta->status = GIT_DELTA_ADDED;
251 25 3 patch->base.delta->nfiles = 1;
252 -
253 25 3 return parse_header_mode(&patch->base.delta->new_file.mode, ctx);
254 - }
255 -
256 56 2 static int parse_header_rename(
257 - char **out,
258 - git_patch_parse_ctx *ctx)
259 - {
260 56 2 git_buf path = GIT_BUF_INIT;
261 -
262 56 2-4 if (parse_header_path_buf(&path, ctx, header_path_len(ctx)) < 0)
263 ##### 5 return -1;
264 -
265 - /* Note: the `rename from` and `rename to` lines include the literal
266 - * filename. They do *not* include the prefix. (Who needs consistency?)
267 - */
268 56 6 *out = git_buf_detach(&path);
269 56 7 return 0;
270 - }
271 -
272 27 2 static int parse_header_renamefrom(
273 - git_patch_parsed *patch, git_patch_parse_ctx *ctx)
274 - {
275 27 2 patch->base.delta->status = GIT_DELTA_RENAMED;
276 27 2 return parse_header_rename(&patch->rename_old_path, ctx);
277 - }
278 -
279 27 2 static int parse_header_renameto(
280 - git_patch_parsed *patch, git_patch_parse_ctx *ctx)
281 - {
282 27 2 patch->base.delta->status = GIT_DELTA_RENAMED;
283 27 2 return parse_header_rename(&patch->rename_new_path, ctx);
284 - }
285 -
286 1 2 static int parse_header_copyfrom(
287 - git_patch_parsed *patch, git_patch_parse_ctx *ctx)
288 - {
289 1 2 patch->base.delta->status = GIT_DELTA_COPIED;
290 1 2 return parse_header_rename(&patch->rename_old_path, ctx);
291 - }
292 -
293 1 2 static int parse_header_copyto(
294 - git_patch_parsed *patch, git_patch_parse_ctx *ctx)
295 - {
296 1 2 patch->base.delta->status = GIT_DELTA_COPIED;
297 1 2 return parse_header_rename(&patch->rename_new_path, ctx);
298 - }
299 -
300 28 2 static int parse_header_percent(uint16_t *out, git_patch_parse_ctx *ctx)
301 - {
302 - int64_t val;
303 -
304 28 2,3 if (git_parse_advance_digit(&val, &ctx->parse_ctx, 10) < 0)
305 ##### 4 return -1;
306 -
307 28 5,6 if (git_parse_advance_expected_str(&ctx->parse_ctx, "%") < 0)
308 ##### 7 return -1;
309 -
310 28 8,9 if (val < 0 || val > 100)
311 ##### 10 return -1;
312 -
313 28 11 *out = (uint16_t)val;
314 28 11 return 0;
315 - }
316 -
317 28 2 static int parse_header_similarity(
318 - git_patch_parsed *patch, git_patch_parse_ctx *ctx)
319 - {
320 28 2,3 if (parse_header_percent(&patch->base.delta->similarity, ctx) < 0)
321 ##### 4 return git_parse_err("invalid similarity percentage at line %"PRIuZ,
322 - ctx->parse_ctx.line_num);
323 -
324 28 5 return 0;
325 - }
326 -
327 ##### 2 static int parse_header_dissimilarity(
328 - git_patch_parsed *patch, git_patch_parse_ctx *ctx)
329 - {
330 - uint16_t dissimilarity;
331 -
332 ##### 2,3 if (parse_header_percent(&dissimilarity, ctx) < 0)
333 ##### 4 return git_parse_err("invalid similarity percentage at line %"PRIuZ,
334 - ctx->parse_ctx.line_num);
335 -
336 ##### 5 patch->base.delta->similarity = 100 - dissimilarity;
337 -
338 ##### 5 return 0;
339 - }
340 -
341 245 2 static int parse_header_start(git_patch_parsed *patch, git_patch_parse_ctx *ctx)
342 - {
343 245 2,3 if (parse_header_path(&patch->header_old_path, ctx) < 0)
344 2 4 return git_parse_err("corrupt old path in git diff header at line %"PRIuZ,
345 - ctx->parse_ctx.line_num);
346 -
347 243 5,6,8 if (git_parse_advance_ws(&ctx->parse_ctx) < 0 ||
348 241 7 parse_header_path(&patch->header_new_path, ctx) < 0)
349 2 9 return git_parse_err("corrupt new path in git diff header at line %"PRIuZ,
350 - ctx->parse_ctx.line_num);
351 -
352 - /*
353 - * We cannot expect to be able to always parse paths correctly at this
354 - * point. Due to the possibility of unquoted names, whitespaces in
355 - * filenames and custom prefixes we have to allow that, though, and just
356 - * proceeed here. We then hope for the "---" and "+++" lines to fix that
357 - * for us.
358 - */
359 241 10,11,13 if (!git_parse_ctx_contains(&ctx->parse_ctx, "\n", 1) &&
360 5 12 !git_parse_ctx_contains(&ctx->parse_ctx, "\r\n", 2)) {
361 4 14 git_parse_advance_chars(&ctx->parse_ctx, ctx->parse_ctx.line_len - 1);
362 -
363 4 15 git__free(patch->header_old_path);
364 4 16 patch->header_old_path = NULL;
365 4 16 git__free(patch->header_new_path);
366 4 17 patch->header_new_path = NULL;
367 - }
368 -
369 241 18 return 0;
370 - }
371 -
372 - typedef enum {
373 - STATE_START,
374 -
375 - STATE_DIFF,
376 - STATE_FILEMODE,
377 - STATE_MODE,
378 - STATE_INDEX,
379 - STATE_PATH,
380 -
381 - STATE_SIMILARITY,
382 - STATE_RENAME,
383 - STATE_COPY,
384 -
385 - STATE_END,
386 - } parse_header_state;
387 -
388 - typedef struct {
389 - const char *str;
390 - parse_header_state expected_state;
391 - parse_header_state next_state;
392 - int(*fn)(git_patch_parsed *, git_patch_parse_ctx *);
393 - } parse_header_transition;
394 -
395 - static const parse_header_transition transitions[] = {
396 - /* Start */
397 - { "diff --git " , STATE_START, STATE_DIFF, parse_header_start },
398 -
399 - { "deleted file mode " , STATE_DIFF, STATE_FILEMODE, parse_header_git_deletedfilemode },
400 - { "new file mode " , STATE_DIFF, STATE_FILEMODE, parse_header_git_newfilemode },
401 - { "old mode " , STATE_DIFF, STATE_MODE, parse_header_git_oldmode },
402 - { "new mode " , STATE_MODE, STATE_END, parse_header_git_newmode },
403 -
404 - { "index " , STATE_FILEMODE, STATE_INDEX, parse_header_git_index },
405 - { "index " , STATE_DIFF, STATE_INDEX, parse_header_git_index },
406 - { "index " , STATE_END, STATE_INDEX, parse_header_git_index },
407 -
408 - { "--- " , STATE_DIFF, STATE_PATH, parse_header_git_oldpath },
409 - { "--- " , STATE_INDEX, STATE_PATH, parse_header_git_oldpath },
410 - { "+++ " , STATE_PATH, STATE_END, parse_header_git_newpath },
411 - { "GIT binary patch" , STATE_INDEX, STATE_END, NULL },
412 - { "Binary files " , STATE_INDEX, STATE_END, NULL },
413 -
414 - { "similarity index " , STATE_END, STATE_SIMILARITY, parse_header_similarity },
415 - { "similarity index " , STATE_DIFF, STATE_SIMILARITY, parse_header_similarity },
416 - { "dissimilarity index ", STATE_DIFF, STATE_SIMILARITY, parse_header_dissimilarity },
417 - { "rename from " , STATE_SIMILARITY, STATE_RENAME, parse_header_renamefrom },
418 - { "rename old " , STATE_SIMILARITY, STATE_RENAME, parse_header_renamefrom },
419 - { "copy from " , STATE_SIMILARITY, STATE_COPY, parse_header_copyfrom },
420 - { "rename to " , STATE_RENAME, STATE_END, parse_header_renameto },
421 - { "rename new " , STATE_RENAME, STATE_END, parse_header_renameto },
422 - { "copy to " , STATE_COPY, STATE_END, parse_header_copyto },
423 -
424 - /* Next patch */
425 - { "diff --git " , STATE_END, 0, NULL },
426 - { "@@ -" , STATE_END, 0, NULL },
427 - { "-- " , STATE_INDEX, 0, NULL },
428 - { "-- " , STATE_END, 0, NULL },
429 - };
430 -
431 245 2 static int parse_header_git(
432 - git_patch_parsed *patch,
433 - git_patch_parse_ctx *ctx)
434 - {
435 - size_t i;
436 245 2 int error = 0;
437 245 2 parse_header_state state = STATE_START;
438 -
439 - /* Parse remaining header lines */
440 1218 2,27,28 for (; ctx->parse_ctx.remain_len > 0; git_parse_advance_line(&ctx->parse_ctx)) {
441 1201 3 bool found = false;
442 -
443 1201 3,4 if (ctx->parse_ctx.line_len == 0 || ctx->parse_ctx.line[ctx->parse_ctx.line_len - 1] != '\n')
444 - break;
445 -
446 12304 5,9,23 for (i = 0; i < ARRAY_SIZE(transitions); i++) {
447 12295 6 const parse_header_transition *transition = &transitions[i];
448 12295 6 size_t op_len = strlen(transition->str);
449 -
450 12295 6,8 if (transition->expected_state != state ||
451 2469 7 git__prefixcmp(ctx->parse_ctx.line, transition->str) != 0)
452 11104 9 continue;
453 -
454 1191 10 state = transition->next_state;
455 -
456 - /* Do not advance if this is the patch separator */
457 1191 10 if (transition->fn == NULL)
458 211 11 goto done;
459 -
460 980 12 git_parse_advance_chars(&ctx->parse_ctx, op_len);
461 -
462 980 13,14 if ((error = transition->fn(patch, ctx)) < 0)
463 7 15 goto done;
464 -
465 973 16 git_parse_advance_ws(&ctx->parse_ctx);
466 -
467 973 17-19 if (git_parse_advance_expected_str(&ctx->parse_ctx, "\n") < 0 ||
468 973 19 ctx->parse_ctx.line_len > 0) {
469 ##### 20 error = git_parse_err("trailing data at line %"PRIuZ, ctx->parse_ctx.line_num);
470 ##### 21 goto done;
471 - }
472 -
473 973 22 found = true;
474 973 22 break;
475 - }
476 -
477 982 24 if (!found) {
478 9 25 error = git_parse_err("invalid patch header at line %"PRIuZ,
479 - ctx->parse_ctx.line_num);
480 9 26 goto done;
481 - }
482 - }
483 -
484 18 29 if (state != STATE_END) {
485 ##### 30 error = git_parse_err("unexpected header line %"PRIuZ, ctx->parse_ctx.line_num);
486 ##### 31 goto done;
487 - }
488 -
489 - done:
490 245 32 return error;
491 - }
492 -
493 739 2 static int parse_int(int *out, git_patch_parse_ctx *ctx)
494 - {
495 - int64_t num;
496 -
497 739 2-5 if (git_parse_advance_digit(&num, &ctx->parse_ctx, 10) < 0 || !git__is_int(num))
498 2 6 return -1;
499 -
500 737 7 *out = (int)num;
501 737 7 return 0;
502 - }
503 -
504 231 2 static int parse_hunk_header(
505 - git_patch_hunk *hunk,
506 - git_patch_parse_ctx *ctx)
507 - {
508 231 2 const char *header_start = ctx->parse_ctx.line;
509 - char c;
510 -
511 231 2 hunk->hunk.old_lines = 1;
512 231 2 hunk->hunk.new_lines = 1;
513 -
514 231 2,3,5 if (git_parse_advance_expected_str(&ctx->parse_ctx, "@@ -") < 0 ||
515 231 4 parse_int(&hunk->hunk.old_start, ctx) < 0)
516 - goto fail;
517 -
518 229 6-8 if (git_parse_peek(&c, &ctx->parse_ctx, 0) == 0 && c == ',') {
519 141 9,10,12 if (git_parse_advance_expected_str(&ctx->parse_ctx, ",") < 0 ||
520 141 11 parse_int(&hunk->hunk.old_lines, ctx) < 0)
521 - goto fail;
522 - }
523 -
524 229 13,14,16 if (git_parse_advance_expected_str(&ctx->parse_ctx, " +") < 0 ||
525 229 15 parse_int(&hunk->hunk.new_start, ctx) < 0)
526 - goto fail;
527 -
528 229 17-19 if (git_parse_peek(&c, &ctx->parse_ctx, 0) == 0 && c == ',') {
529 138 20,21,23 if (git_parse_advance_expected_str(&ctx->parse_ctx, ",") < 0 ||
530 138 22 parse_int(&hunk->hunk.new_lines, ctx) < 0)
531 - goto fail;
532 - }
533 -
534 229 24,25 if (git_parse_advance_expected_str(&ctx->parse_ctx, " @@") < 0)
535 ##### 26 goto fail;
536 -
537 229 27 git_parse_advance_line(&ctx->parse_ctx);
538 -
539 229 28,29 if (!hunk->hunk.old_lines && !hunk->hunk.new_lines)
540 3 30 goto fail;
541 -
542 226 31 hunk->hunk.header_len = ctx->parse_ctx.line - header_start;
543 226 31 if (hunk->hunk.header_len > (GIT_DIFF_HUNK_HEADER_SIZE - 1))
544 ##### 32 return git_parse_err("oversized patch hunk header at line %"PRIuZ,
545 - ctx->parse_ctx.line_num);
546 -
547 226 33 memcpy(hunk->hunk.header, header_start, hunk->hunk.header_len);
548 226 33 hunk->hunk.header[hunk->hunk.header_len] = '\0';
549 -
550 226 33 return 0;
551 -
552 - fail:
553 5 34 git_error_set(GIT_ERROR_PATCH, "invalid patch hunk header at line %"PRIuZ,
554 - ctx->parse_ctx.line_num);
555 5 35 return -1;
556 - }
557 -
558 10 2 static int eof_for_origin(int origin) {
559 10 2 if (origin == GIT_DIFF_LINE_ADDITION)
560 7 3 return GIT_DIFF_LINE_ADD_EOFNL;
561 3 4 if (origin == GIT_DIFF_LINE_DELETION)
562 3 5 return GIT_DIFF_LINE_DEL_EOFNL;
563 ##### 6 return GIT_DIFF_LINE_CONTEXT_EOFNL;
564 - }
565 -
566 226 2 static int parse_hunk_body(
567 - git_patch_parsed *patch,
568 - git_patch_hunk *hunk,
569 - git_patch_parse_ctx *ctx)
570 - {
571 - git_diff_line *line;
572 226 2 int error = 0;
573 -
574 226 2 int oldlines = hunk->hunk.old_lines;
575 226 2 int newlines = hunk->hunk.new_lines;
576 226 2 int last_origin = 0;
577 -
578 1429 2,42 for (;
579 1429 42,43 ctx->parse_ctx.remain_len > 1 &&
580 1204 44,46 (oldlines || newlines) &&
581 1204 45 !git_parse_ctx_contains_s(&ctx->parse_ctx, "@@ -");
582 1203 38 git_parse_advance_line(&ctx->parse_ctx)) {
583 -
584 1204 3 int old_lineno, new_lineno, origin, prefix = 1;
585 - char c;
586 -
587 1204 3,4,6 if (git__add_int_overflow(&old_lineno, hunk->hunk.old_start, hunk->hunk.old_lines) ||
588 1204 5,8 git__sub_int_overflow(&old_lineno, old_lineno, oldlines) ||
589 1204 7,10 git__add_int_overflow(&new_lineno, hunk->hunk.new_start, hunk->hunk.new_lines) ||
590 1203 9 git__sub_int_overflow(&new_lineno, new_lineno, newlines)) {
591 1 11 error = git_parse_err("unrepresentable line count at line %"PRIuZ,
592 - ctx->parse_ctx.line_num);
593 1 39,40 goto done;
594 - }
595 -
596 1203 12,13 if (ctx->parse_ctx.line_len == 0 || ctx->parse_ctx.line[ctx->parse_ctx.line_len - 1] != '\n') {
597 ##### 14 error = git_parse_err("invalid patch instruction at line %"PRIuZ,
598 - ctx->parse_ctx.line_num);
599 ##### 15 goto done;
600 - }
601 -
602 1203 16 git_parse_peek(&c, &ctx->parse_ctx, 0);
603 -
604 1203 17 switch (c) {
605 - case '\n':
606 10 18 prefix = 0;
607 - /* fall through */
608 -
609 - case ' ':
610 378 19 origin = GIT_DIFF_LINE_CONTEXT;
611 378 19 oldlines--;
612 378 19 newlines--;
613 378 19 break;
614 -
615 - case '-':
616 386 20 origin = GIT_DIFF_LINE_DELETION;
617 386 20 oldlines--;
618 386 20 new_lineno = -1;
619 386 20 break;
620 -
621 - case '+':
622 438 21 origin = GIT_DIFF_LINE_ADDITION;
623 438 21 newlines--;
624 438 21 old_lineno = -1;
625 438 21 break;
626 -
627 - case '\\':
628 - /*
629 - * If there are no oldlines left, then this is probably
630 - * the "\ No newline at end of file" marker. Do not
631 - * verify its format, as it may be localized.
632 - */
633 1 22 if (!oldlines) {
634 1 23 prefix = 0;
635 1 23 origin = eof_for_origin(last_origin);
636 1 24 old_lineno = -1;
637 1 24 new_lineno = -1;
638 1 24 break;
639 - }
640 - /* fall through */
641 -
642 - default:
643 ##### 25 error = git_parse_err("invalid patch hunk at line %"PRIuZ, ctx->parse_ctx.line_num);
644 ##### 26 goto done;
645 - }
646 -
647 1203 27-32 line = git_array_alloc(patch->base.lines);
648 1203 33,34,41 GIT_ERROR_CHECK_ALLOC(line);
649 -
650 1203 35 memset(line, 0x0, sizeof(git_diff_line));
651 -
652 1203 35 line->content_len = ctx->parse_ctx.line_len - prefix;
653 1203 35 line->content = git__strndup(ctx->parse_ctx.line + prefix, line->content_len);
654 1203 36,37 GIT_ERROR_CHECK_ALLOC(line->content);
655 1203 38 line->content_offset = ctx->parse_ctx.content_len - ctx->parse_ctx.remain_len;
656 1203 38 line->origin = origin;
657 1203 38 line->num_lines = 1;
658 1203 38 line->old_lineno = old_lineno;
659 1203 38 line->new_lineno = new_lineno;
660 -
661 1203 38 hunk->line_count++;
662 -
663 1203 38 last_origin = origin;
664 - }
665 -
666 225 47,48 if (oldlines || newlines) {
667 3 49 error = git_parse_err(
668 - "invalid patch hunk, expected %d old lines and %d new lines",
669 - hunk->hunk.old_lines, hunk->hunk.new_lines);
670 3 50 goto done;
671 - }
672 -
673 - /*
674 - * Handle "\ No newline at end of file". Only expect the leading
675 - * backslash, though, because the rest of the string could be
676 - * localized. Because `diff` optimizes for the case where you
677 - * want to apply the patch by hand.
678 - */
679 222 51-53 if (git_parse_ctx_contains_s(&ctx->parse_ctx, "\\ ") &&
680 9 53 git_array_size(patch->base.lines) > 0) {
681 -
682 9 54-56 line = git_array_get(patch->base.lines, git_array_size(patch->base.lines) - 1);
683 -
684 9 57 if (line->content_len < 1) {
685 ##### 58 error = git_parse_err("last line has no trailing newline");
686 ##### 59 goto done;
687 - }
688 -
689 9 60-65 line = git_array_alloc(patch->base.lines);
690 9 66,67 GIT_ERROR_CHECK_ALLOC(line);
691 -
692 9 68 memset(line, 0x0, sizeof(git_diff_line));
693 -
694 9 68 line->content_len = ctx->parse_ctx.line_len;
695 9 68 line->content = git__strndup(ctx->parse_ctx.line, line->content_len);
696 9 69,70 GIT_ERROR_CHECK_ALLOC(line->content);
697 9 71 line->content_offset = ctx->parse_ctx.content_len - ctx->parse_ctx.remain_len;
698 9 71 line->origin = eof_for_origin(last_origin);
699 9 72 line->num_lines = 1;
700 9 72 line->old_lineno = -1;
701 9 72 line->new_lineno = -1;
702 -
703 9 72 hunk->line_count++;
704 -
705 9 72 git_parse_advance_line(&ctx->parse_ctx);
706 - }
707 -
708 - done:
709 226 73 return error;
710 - }
711 -
712 255 2 static int parse_patch_header(
713 - git_patch_parsed *patch,
714 - git_patch_parse_ctx *ctx)
715 - {
716 255 2 int error = 0;
717 -
718 319 2,20,21 for (; ctx->parse_ctx.remain_len > 0; git_parse_advance_line(&ctx->parse_ctx)) {
719 - /* This line is too short to be a patch header. */
720 317 3 if (ctx->parse_ctx.line_len < 6)
721 9 4 continue;
722 -
723 - /* This might be a hunk header without a patch header, provide a
724 - * sensible error message. */
725 308 5,6 if (git_parse_ctx_contains_s(&ctx->parse_ctx, "@@ -")) {
726 2 7 size_t line_num = ctx->parse_ctx.line_num;
727 - git_patch_hunk hunk;
728 -
729 - /* If this cannot be parsed as a hunk header, it's just leading
730 - * noise, continue.
731 - */
732 2 7,8 if (parse_hunk_header(&hunk, ctx) < 0) {
733 2 9 git_error_clear();
734 2 10 continue;
735 - }
736 -
737 ##### 11 error = git_parse_err("invalid hunk header outside patch at line %"PRIuZ,
738 - line_num);
739 ##### 12 goto done;
740 - }
741 -
742 - /* This buffer is too short to contain a patch. */
743 306 13 if (ctx->parse_ctx.remain_len < ctx->parse_ctx.line_len + 6)
744 8 14 break;
745 -
746 - /* A proper git patch */
747 298 15,16 if (git_parse_ctx_contains_s(&ctx->parse_ctx, "diff --git ")) {
748 245 17 error = parse_header_git(patch, ctx);
749 245 18 goto done;
750 - }
751 -
752 53 19 error = 0;
753 53 19 continue;
754 - }
755 -
756 10 22 git_error_set(GIT_ERROR_PATCH, "no patch found");
757 10 23 error = GIT_ENOTFOUND;
758 -
759 - done:
760 255 24 return error;
761 - }
762 -
763 39 2 static int parse_patch_binary_side(
764 - git_diff_binary_file *binary,
765 - git_patch_parse_ctx *ctx)
766 - {
767 39 2 git_diff_binary_t type = GIT_DIFF_BINARY_NONE;
768 39 2 git_buf base85 = GIT_BUF_INIT, decoded = GIT_BUF_INIT;
769 - int64_t len;
770 39 2 int error = 0;
771 -
772 39 2,3 if (git_parse_ctx_contains_s(&ctx->parse_ctx, "literal ")) {
773 18 4 type = GIT_DIFF_BINARY_LITERAL;
774 18 4 git_parse_advance_chars(&ctx->parse_ctx, 8);
775 21 5,6 } else if (git_parse_ctx_contains_s(&ctx->parse_ctx, "delta ")) {
776 21 7 type = GIT_DIFF_BINARY_DELTA;
777 21 7 git_parse_advance_chars(&ctx->parse_ctx, 6);
778 - } else {
779 ##### 8 error = git_parse_err(
780 - "unknown binary delta type at line %"PRIuZ, ctx->parse_ctx.line_num);
781 ##### 9 goto done;
782 - }
783 -
784 39 10,11,13 if (git_parse_advance_digit(&len, &ctx->parse_ctx, 10) < 0 ||
785 39 12,14 git_parse_advance_nl(&ctx->parse_ctx) < 0 || len < 0) {
786 ##### 15 error = git_parse_err("invalid binary size at line %"PRIuZ, ctx->parse_ctx.line_num);
787 ##### 16 goto done;
788 - }
789 -
790 85 17,49 while (ctx->parse_ctx.line_len) {
791 - char c;
792 85 18 size_t encoded_len, decoded_len = 0, decoded_orig = decoded.size;
793 -
794 85 18 git_parse_peek(&c, &ctx->parse_ctx, 0);
795 -
796 85 19 if (c == '\n')
797 38 20 break;
798 47 21,22 else if (c >= 'A' && c <= 'Z')
799 14 23 decoded_len = c - 'A' + 1;
800 33 24,25 else if (c >= 'a' && c <= 'z')
801 33 26 decoded_len = c - 'a' + (('z' - 'a') + 1) + 1;
802 -
803 47 27 if (!decoded_len) {
804 ##### 28 error = git_parse_err("invalid binary length at line %"PRIuZ, ctx->parse_ctx.line_num);
805 1 47,48 goto done;
806 - }
807 -
808 47 29 git_parse_advance_chars(&ctx->parse_ctx, 1);
809 -
810 47 30 encoded_len = ((decoded_len / 4) + !!(decoded_len % 4)) * 5;
811 -
812 47 30-32 if (!encoded_len || !ctx->parse_ctx.line_len || encoded_len > ctx->parse_ctx.line_len - 1) {
813 1 33 error = git_parse_err("truncated binary data at line %"PRIuZ, ctx->parse_ctx.line_num);
814 1 34 goto done;
815 - }
816 -
817 46 35,36 if ((error = git_buf_decode_base85(
818 - &decoded, ctx->parse_ctx.line, encoded_len, decoded_len)) < 0)
819 ##### 37 goto done;
820 -
821 46 38 if (decoded.size - decoded_orig != decoded_len) {
822 ##### 39 error = git_parse_err("truncated binary data at line %"PRIuZ, ctx->parse_ctx.line_num);
823 ##### 40 goto done;
824 - }
825 -
826 46 41 git_parse_advance_chars(&ctx->parse_ctx, encoded_len);
827 -
828 46 42,43 if (git_parse_advance_nl(&ctx->parse_ctx) < 0) {
829 ##### 44 error = git_parse_err("trailing data at line %"PRIuZ, ctx->parse_ctx.line_num);
830 46 45,46 goto done;
831 - }
832 - }
833 -
834 38 50 binary->type = type;
835 38 50 binary->inflatedlen = (size_t)len;
836 38 50 binary->datalen = decoded.size;
837 38 50,51 binary->data = git_buf_detach(&decoded);
838 -
839 - done:
840 39 52 git_buf_dispose(&base85);
841 39 53 git_buf_dispose(&decoded);
842 39 54 return error;
843 - }
844 -
845 20 2 static int parse_patch_binary(
846 - git_patch_parsed *patch,
847 - git_patch_parse_ctx *ctx)
848 - {
849 - int error;
850 -
851 20 2,3,5 if (git_parse_advance_expected_str(&ctx->parse_ctx, "GIT binary patch") < 0 ||
852 20 4 git_parse_advance_nl(&ctx->parse_ctx) < 0)
853 ##### 6 return git_parse_err("corrupt git binary header at line %"PRIuZ, ctx->parse_ctx.line_num);
854 -
855 - /* parse old->new binary diff */
856 20 7,8 if ((error = parse_patch_binary_side(
857 - &patch->base.binary.new_file, ctx)) < 0)
858 1 9 return error;
859 -
860 19 10,11 if (git_parse_advance_nl(&ctx->parse_ctx) < 0)
861 ##### 12 return git_parse_err("corrupt git binary separator at line %"PRIuZ,
862 - ctx->parse_ctx.line_num);
863 -
864 - /* parse new->old binary diff */
865 19 13,14 if ((error = parse_patch_binary_side(
866 - &patch->base.binary.old_file, ctx)) < 0)
867 ##### 15 return error;
868 -
869 19 16,17 if (git_parse_advance_nl(&ctx->parse_ctx) < 0)
870 ##### 18 return git_parse_err("corrupt git binary patch separator at line %"PRIuZ,
871 - ctx->parse_ctx.line_num);
872 -
873 19 19 patch->base.binary.contains_data = 1;
874 19 19 patch->base.delta->flags |= GIT_DIFF_FLAG_BINARY;
875 19 19 return 0;
876 - }
877 -
878 4 2 static int parse_patch_binary_nodata(
879 - git_patch_parsed *patch,
880 - git_patch_parse_ctx *ctx)
881 - {
882 4 2-4 const char *old = patch->old_path ? patch->old_path : patch->header_old_path;
883 4 5-7 const char *new = patch->new_path ? patch->new_path : patch->header_new_path;
884 -
885 4 8,9 if (!old || !new)
886 ##### 10 return git_parse_err("corrupt binary data without paths at line %"PRIuZ, ctx->parse_ctx.line_num);
887 -
888 4 11 if (patch->base.delta->status == GIT_DELTA_ADDED)
889 2 12 old = "/dev/null";
890 2 13 else if (patch->base.delta->status == GIT_DELTA_DELETED)
891 ##### 14 new = "/dev/null";
892 -
893 4 15,16,18 if (git_parse_advance_expected_str(&ctx->parse_ctx, "Binary files ") < 0 ||
894 4 17,20 git_parse_advance_expected_str(&ctx->parse_ctx, old) < 0 ||
895 4 19,22 git_parse_advance_expected_str(&ctx->parse_ctx, " and ") < 0 ||
896 4 21,24 git_parse_advance_expected_str(&ctx->parse_ctx, new) < 0 ||
897 4 23,26 git_parse_advance_expected_str(&ctx->parse_ctx, " differ") < 0 ||
898 4 25 git_parse_advance_nl(&ctx->parse_ctx) < 0)
899 1 27 return git_parse_err("corrupt git binary header at line %"PRIuZ, ctx->parse_ctx.line_num);
900 -
901 3 28 patch->base.binary.contains_data = 0;
902 3 28 patch->base.delta->flags |= GIT_DIFF_FLAG_BINARY;
903 3 28 return 0;
904 - }
905 -
906 205 2 static int parse_patch_hunks(
907 - git_patch_parsed *patch,
908 - git_patch_parse_ctx *ctx)
909 - {
910 - git_patch_hunk *hunk;
911 205 2 int error = 0;
912 -
913 427 2,15,16 while (git_parse_ctx_contains_s(&ctx->parse_ctx, "@@ -")) {
914 229 3-8 hunk = git_array_alloc(patch->base.hunks);
915 229 9,10 GIT_ERROR_CHECK_ALLOC(hunk);
916 -
917 229 11 memset(hunk, 0, sizeof(git_patch_hunk));
918 -
919 229 11 hunk->line_start = git_array_size(patch->base.lines);
920 229 11 hunk->line_count = 0;
921 -
922 229 11-14 if ((error = parse_hunk_header(hunk, ctx)) < 0 ||
923 - (error = parse_hunk_body(patch, hunk, ctx)) < 0)
924 - goto done;
925 - }
926 -
927 198 17 patch->base.delta->flags |= GIT_DIFF_FLAG_NOT_BINARY;
928 -
929 - done:
930 205 18 return error;
931 - }
932 -
933 229 2 static int parse_patch_body(
934 - git_patch_parsed *patch, git_patch_parse_ctx *ctx)
935 - {
936 229 2,3 if (git_parse_ctx_contains_s(&ctx->parse_ctx, "GIT binary patch"))
937 20 4 return parse_patch_binary(patch, ctx);
938 209 5,6 else if (git_parse_ctx_contains_s(&ctx->parse_ctx, "Binary files "))
939 4 7 return parse_patch_binary_nodata(patch, ctx);
940 - else
941 205 8 return parse_patch_hunks(patch, ctx);
942 - }
943 -
944 440 2 static int check_header_names(
945 - const char *one,
946 - const char *two,
947 - const char *old_or_new,
948 - bool two_null)
949 - {
950 440 2,3 if (!one || !two)
951 98 4 return 0;
952 -
953 342 5,6 if (two_null && strcmp(two, "/dev/null") != 0)
954 ##### 7 return git_parse_err("expected %s path of '/dev/null'", old_or_new);
955 -
956 342 8,9 else if (!two_null && strcmp(one, two) != 0)
957 ##### 10 return git_parse_err("mismatched %s path names", old_or_new);
958 -
959 342 11 return 0;
960 - }
961 -
962 439 2 static int check_prefix(
963 - char **out,
964 - size_t *out_len,
965 - git_patch_parsed *patch,
966 - const char *path_start)
967 - {
968 439 2 const char *path = path_start;
969 439 2 size_t prefix_len = patch->ctx->opts.prefix_len;
970 439 2 size_t remain_len = prefix_len;
971 -
972 439 2 *out = NULL;
973 439 2 *out_len = 0;
974 -
975 439 2 if (prefix_len == 0)
976 ##### 3 goto done;
977 -
978 - /* leading slashes do not count as part of the prefix in git apply */
979 439 4,6 while (*path == '/')
980 ##### 5 path++;
981 -
982 1317 7,11,12 while (*path && remain_len) {
983 878 8 if (*path == '/')
984 439 9 remain_len--;
985 -
986 878 10 path++;
987 - }
988 -
989 439 13,14 if (remain_len || !*path)
990 ##### 15 return git_parse_err(
991 - "header filename does not contain %"PRIuZ" path components",
992 - prefix_len);
993 -
994 - done:
995 439 16 *out_len = (path - path_start);
996 439 16 *out = git__strndup(path_start, *out_len);
997 -
998 439 17 return (*out == NULL) ? -1 : 0;
999 - }
1000 -
1001 220 2 static int check_filenames(git_patch_parsed *patch)
1002 - {
1003 - const char *prefixed_new, *prefixed_old;
1004 220 2 size_t old_prefixlen = 0, new_prefixlen = 0;
1005 220 2 bool added = (patch->base.delta->status == GIT_DELTA_ADDED);
1006 220 2 bool deleted = (patch->base.delta->status == GIT_DELTA_DELETED);
1007 -
1008 220 2,3 if (patch->old_path && !patch->new_path)
1009 ##### 4 return git_parse_err("missing new path");
1010 -
1011 220 5,6 if (!patch->old_path && patch->new_path)
1012 ##### 7 return git_parse_err("missing old path");
1013 -
1014 - /* Ensure (non-renamed) paths match */
1015 220 8,9,11 if (check_header_names(patch->header_old_path, patch->old_path, "old", added) < 0 ||
1016 220 10 check_header_names(patch->header_new_path, patch->new_path, "new", deleted) < 0)
1017 ##### 12 return -1;
1018 -
1019 220 13-16 prefixed_old = (!added && patch->old_path) ? patch->old_path : patch->header_old_path;
1020 220 17-20 prefixed_new = (!deleted && patch->new_path) ? patch->new_path : patch->header_new_path;
1021 -
1022 220 21-24 if ((prefixed_old && check_prefix(&patch->old_prefix, &old_prefixlen, patch, prefixed_old) < 0) ||
1023 220 25,26 (prefixed_new && check_prefix(&patch->new_prefix, &new_prefixlen, patch, prefixed_new) < 0))
1024 ##### 27 return -1;
1025 -
1026 - /* Prefer the rename filenames as they are unambiguous and unprefixed */
1027 220 28 if (patch->rename_old_path)
1028 28 29 patch->base.delta->old_file.path = patch->rename_old_path;
1029 192 30 else if (prefixed_old)
1030 191 31 patch->base.delta->old_file.path = prefixed_old + old_prefixlen;
1031 - else
1032 1 32 patch->base.delta->old_file.path = NULL;
1033 -
1034 220 33 if (patch->rename_new_path)
1035 28 34 patch->base.delta->new_file.path = patch->rename_new_path;
1036 192 35 else if (prefixed_new)
1037 192 36 patch->base.delta->new_file.path = prefixed_new + new_prefixlen;
1038 - else
1039 ##### 37 patch->base.delta->new_file.path = NULL;
1040 -
1041 220 38,39 if (!patch->base.delta->old_file.path &&
1042 1 39 !patch->base.delta->new_file.path)
1043 ##### 40 return git_parse_err("git diff header lacks old / new paths");
1044 -
1045 220 41 return 0;
1046 - }
1047 -
1048 220 2 static int check_patch(git_patch_parsed *patch)
1049 - {
1050 220 2 git_diff_delta *delta = patch->base.delta;
1051 -
1052 220 2,3 if (check_filenames(patch) < 0)
1053 ##### 4 return -1;
1054 -
1055 220 5,6 if (delta->old_file.path &&
1056 219 6,7 delta->status != GIT_DELTA_DELETED &&
1057 198 7 !delta->new_file.mode)
1058 18 8 delta->new_file.mode = delta->old_file.mode;
1059 -
1060 220 9,10 if (delta->status == GIT_DELTA_MODIFIED &&
1061 146 10,11 !(delta->flags & GIT_DIFF_FLAG_BINARY) &&
1062 132 11,12 delta->new_file.mode == delta->old_file.mode &&
1063 123 12 git_array_size(patch->base.hunks) == 0)
1064 ##### 13 return git_parse_err("patch with no hunks");
1065 -
1066 220 14 if (delta->status == GIT_DELTA_ADDED) {
1067 25 15 memset(&delta->old_file.id, 0x0, sizeof(git_oid));
1068 25 15 delta->old_file.id_abbrev = 0;
1069 - }
1070 -
1071 220 16 if (delta->status == GIT_DELTA_DELETED) {
1072 21 17 memset(&delta->new_file.id, 0x0, sizeof(git_oid));
1073 21 17 delta->new_file.id_abbrev = 0;
1074 - }
1075 -
1076 220 18 return 0;
1077 - }
1078 -
1079 190 2 git_patch_parse_ctx *git_patch_parse_ctx_init(
1080 - const char *content,
1081 - size_t content_len,
1082 - const git_patch_options *opts)
1083 - {
1084 - git_patch_parse_ctx *ctx;
1085 190 2 git_patch_options default_opts = GIT_PATCH_OPTIONS_INIT;
1086 -
1087 190 2,3 if ((ctx = git__calloc(1, sizeof(git_patch_parse_ctx))) == NULL)
1088 ##### 4 return NULL;
1089 -
1090 190 5,6 if ((git_parse_ctx_init(&ctx->parse_ctx, content, content_len)) < 0) {
1091 ##### 7 git__free(ctx);
1092 ##### 8 return NULL;
1093 - }
1094 -
1095 190 9 if (opts)
1096 ##### 10 memcpy(&ctx->opts, opts, sizeof(git_patch_options));
1097 - else
1098 190 11 memcpy(&ctx->opts, &default_opts, sizeof(git_patch_options));
1099 -
1100 190 12 GIT_REFCOUNT_INC(ctx);
1101 190 13 return ctx;
1102 - }
1103 -
1104 190 2 static void patch_parse_ctx_free(git_patch_parse_ctx *ctx)
1105 - {
1106 190 2 if (!ctx)
1107 190 3,6 return;
1108 -
1109 190 4 git_parse_ctx_clear(&ctx->parse_ctx);
1110 190 5 git__free(ctx);
1111 - }
1112 -
1113 445 2 void git_patch_parse_ctx_free(git_patch_parse_ctx *ctx)
1114 - {
1115 445 2-5 GIT_REFCOUNT_DEC(ctx, patch_parse_ctx_free);
1116 445 6 }
1117 -
1118 110 2 int git_patch_parsed_from_diff(git_patch **out, git_diff *d, size_t idx)
1119 - {
1120 110 2 git_diff_parsed *diff = (git_diff_parsed *)d;
1121 - git_patch *p;
1122 -
1123 110 2,3 if ((p = git_vector_get(&diff->patches, idx)) == NULL)
1124 ##### 4 return -1;
1125 -
1126 110 5 GIT_REFCOUNT_INC(p);
1127 110 6 *out = p;
1128 -
1129 110 6 return 0;
1130 - }
1131 -
1132 255 2 static void patch_parsed__free(git_patch *p)
1133 - {
1134 255 2 git_patch_parsed *patch = (git_patch_parsed *)p;
1135 - git_diff_line *line;
1136 - size_t i;
1137 -
1138 255 2 if (!patch)
1139 255 3,24 return;
1140 -
1141 255 4 git_patch_parse_ctx_free(patch->ctx);
1142 -
1143 255 5 git__free((char *)patch->base.binary.old_file.data);
1144 255 6 git__free((char *)patch->base.binary.new_file.data);
1145 255 7 git_array_clear(patch->base.hunks);
1146 1467 8,10-12 git_array_foreach(patch->base.lines, i, line)
1147 1212 9 git__free((char *) line->content);
1148 255 13 git_array_clear(patch->base.lines);
1149 255 14 git__free(patch->base.delta);
1150 -
1151 255 15 git__free(patch->old_prefix);
1152 255 16 git__free(patch->new_prefix);
1153 255 17 git__free(patch->header_old_path);
1154 255 18 git__free(patch->header_new_path);
1155 255 19 git__free(patch->rename_old_path);
1156 255 20 git__free(patch->rename_new_path);
1157 255 21 git__free(patch->old_path);
1158 255 22 git__free(patch->new_path);
1159 255 23 git__free(patch);
1160 - }
1161 -
1162 255 2 int git_patch_parse(
1163 - git_patch **out,
1164 - git_patch_parse_ctx *ctx)
1165 - {
1166 - git_patch_parsed *patch;
1167 - size_t start, used;
1168 255 2 int error = 0;
1169 -
1170 255 2-4 assert(out && ctx);
1171 -
1172 255 5 *out = NULL;
1173 -
1174 255 5 patch = git__calloc(1, sizeof(git_patch_parsed));
1175 255 6,7 GIT_ERROR_CHECK_ALLOC(patch);
1176 -
1177 255 8 patch->ctx = ctx;
1178 255 8 GIT_REFCOUNT_INC(patch->ctx);
1179 -
1180 255 9 patch->base.free_fn = patch_parsed__free;
1181 -
1182 255 9 patch->base.delta = git__calloc(1, sizeof(git_diff_delta));
1183 255 10,11 GIT_ERROR_CHECK_ALLOC(patch->base.delta);
1184 -
1185 255 12 patch->base.delta->status = GIT_DELTA_MODIFIED;
1186 255 12 patch->base.delta->nfiles = 2;
1187 -
1188 255 12 start = ctx->parse_ctx.remain_len;
1189 -
1190 255 12-15 if ((error = parse_patch_header(patch, ctx)) < 0 ||
1191 220 16,17 (error = parse_patch_body(patch, ctx)) < 0 ||
1192 - (error = check_patch(patch)) < 0)
1193 - goto done;
1194 -
1195 220 18 used = start - ctx->parse_ctx.remain_len;
1196 220 18 ctx->parse_ctx.remain += used;
1197 -
1198 220 18 patch->base.diff_opts.old_prefix = patch->old_prefix;
1199 220 18 patch->base.diff_opts.new_prefix = patch->new_prefix;
1200 220 18 patch->base.diff_opts.flags |= GIT_DIFF_SHOW_BINARY;
1201 -
1202 220 18 GIT_REFCOUNT_INC(&patch->base);
1203 220 19 *out = &patch->base;
1204 -
1205 - done:
1206 255 20 if (error < 0)
1207 35 21 patch_parsed__free(&patch->base);
1208 -
1209 255 22 return error;
1210 - }
1211 -
1212 93 2 int git_patch_from_buffer(
1213 - git_patch **out,
1214 - const char *content,
1215 - size_t content_len,
1216 - const git_patch_options *opts)
1217 - {
1218 - git_patch_parse_ctx *ctx;
1219 - int error;
1220 -
1221 93 2 ctx = git_patch_parse_ctx_init(content, content_len, opts);
1222 93 3,4 GIT_ERROR_CHECK_ALLOC(ctx);
1223 -
1224 93 5 error = git_patch_parse(out, ctx);
1225 -
1226 93 6 git_patch_parse_ctx_free(ctx);
1227 93 7 return error;
1228 - }
1229 -