static int parse_patch_body( git_patch_parsed *patch, git_patch_parse_ctx *ctx) { if (parse_ctx_contains_s(ctx, "GIT binary patch")) return parse_patch_binary(patch, ctx); else if (parse_ctx_contains_s(ctx, "Binary files ")) return parse_patch_binary_nodata(patch, ctx); else return parse_patch_hunks(patch, ctx); }
static int parse_patch_header( git_patch_parsed *patch, git_patch_parse_ctx *ctx) { int error = 0; for (ctx->line = ctx->remain; ctx->remain_len > 0; parse_advance_line(ctx)) { /* This line is too short to be a patch header. */ if (ctx->line_len < 6) continue; /* This might be a hunk header without a patch header, provide a * sensible error message. */ if (parse_ctx_contains_s(ctx, "@@ -")) { size_t line_num = ctx->line_num; git_patch_hunk hunk; /* If this cannot be parsed as a hunk header, it's just leading * noise, continue. */ if (parse_hunk_header(&hunk, ctx) < 0) { giterr_clear(); continue; } error = parse_err("invalid hunk header outside patch at line %d", line_num); goto done; } /* This buffer is too short to contain a patch. */ if (ctx->remain_len < ctx->line_len + 6) break; /* A proper git patch */ if (parse_ctx_contains_s(ctx, "diff --git ")) { error = parse_header_git(patch, ctx); goto done; } error = 0; continue; } giterr_set(GITERR_PATCH, "no patch found"); error = GIT_ENOTFOUND; done: return error; }
static int parse_patch_hunks( git_patch_parsed *patch, git_patch_parse_ctx *ctx) { git_patch_hunk *hunk; int error = 0; while (parse_ctx_contains_s(ctx, "@@ -")) { hunk = git_array_alloc(patch->base.hunks); GITERR_CHECK_ALLOC(hunk); memset(hunk, 0, sizeof(git_patch_hunk)); hunk->line_start = git_array_size(patch->base.lines); hunk->line_count = 0; if ((error = parse_hunk_header(hunk, ctx)) < 0 || (error = parse_hunk_body(patch, hunk, ctx)) < 0) goto done; } patch->base.delta->flags |= GIT_DIFF_FLAG_NOT_BINARY; done: return error; }
static int parse_patch_binary_side( git_diff_binary_file *binary, git_patch_parse_ctx *ctx) { git_diff_binary_t type = GIT_DIFF_BINARY_NONE; git_buf base85 = GIT_BUF_INIT, decoded = GIT_BUF_INIT; git_off_t len; int error = 0; if (parse_ctx_contains_s(ctx, "literal ")) { type = GIT_DIFF_BINARY_LITERAL; parse_advance_chars(ctx, 8); } else if (parse_ctx_contains_s(ctx, "delta ")) { type = GIT_DIFF_BINARY_DELTA; parse_advance_chars(ctx, 6); } else { error = parse_err( "unknown binary delta type at line %d", ctx->line_num); goto done; } if (parse_number(&len, ctx) < 0 || parse_advance_nl(ctx) < 0 || len < 0) { error = parse_err("invalid binary size at line %d", ctx->line_num); goto done; } while (ctx->line_len) { char c = ctx->line[0]; size_t encoded_len, decoded_len = 0, decoded_orig = decoded.size; if (c == '\n') break; else if (c >= 'A' && c <= 'Z') decoded_len = c - 'A' + 1; else if (c >= 'a' && c <= 'z') decoded_len = c - 'a' + (('z' - 'a') + 1) + 1; if (!decoded_len) { error = parse_err("invalid binary length at line %d", ctx->line_num); goto done; } parse_advance_chars(ctx, 1); encoded_len = ((decoded_len / 4) + !!(decoded_len % 4)) * 5; if (encoded_len > ctx->line_len - 1) { error = parse_err("truncated binary data at line %d", ctx->line_num); goto done; } if ((error = git_buf_decode_base85( &decoded, ctx->line, encoded_len, decoded_len)) < 0) goto done; if (decoded.size - decoded_orig != decoded_len) { error = parse_err("truncated binary data at line %d", ctx->line_num); goto done; } parse_advance_chars(ctx, encoded_len); if (parse_advance_nl(ctx) < 0) { error = parse_err("trailing data at line %d", ctx->line_num); goto done; } } binary->type = type; binary->inflatedlen = (size_t)len; binary->datalen = decoded.size; binary->data = git_buf_detach(&decoded); done: git_buf_free(&base85); git_buf_free(&decoded); return error; }
static int parse_hunk_body( git_patch_parsed *patch, git_patch_hunk *hunk, git_patch_parse_ctx *ctx) { git_diff_line *line; int error = 0; int oldlines = hunk->hunk.old_lines; int newlines = hunk->hunk.new_lines; for (; ctx->remain_len > 4 && (oldlines || newlines) && memcmp(ctx->line, "@@ -", 4) != 0; parse_advance_line(ctx)) { int origin; int prefix = 1; if (ctx->line_len == 0 || ctx->line[ctx->line_len - 1] != '\n') { error = parse_err("invalid patch instruction at line %d", ctx->line_num); goto done; } switch (ctx->line[0]) { case '\n': prefix = 0; case ' ': origin = GIT_DIFF_LINE_CONTEXT; oldlines--; newlines--; break; case '-': origin = GIT_DIFF_LINE_DELETION; oldlines--; break; case '+': origin = GIT_DIFF_LINE_ADDITION; newlines--; break; default: error = parse_err("invalid patch hunk at line %d", ctx->line_num); goto done; } line = git_array_alloc(patch->base.lines); GITERR_CHECK_ALLOC(line); memset(line, 0x0, sizeof(git_diff_line)); line->content = ctx->line + prefix; line->content_len = ctx->line_len - prefix; line->content_offset = ctx->content_len - ctx->remain_len; line->origin = origin; hunk->line_count++; } if (oldlines || newlines) { error = parse_err( "invalid patch hunk, expected %d old lines and %d new lines", hunk->hunk.old_lines, hunk->hunk.new_lines); goto done; } /* Handle "\ No newline at end of file". Only expect the leading * backslash, though, because the rest of the string could be * localized. Because `diff` optimizes for the case where you * want to apply the patch by hand. */ if (parse_ctx_contains_s(ctx, "\\ ") && git_array_size(patch->base.lines) > 0) { line = git_array_get(patch->base.lines, git_array_size(patch->base.lines) - 1); if (line->content_len < 1) { error = parse_err("cannot trim trailing newline of empty line"); goto done; } line->content_len--; parse_advance_line(ctx); } done: return error; }