{"action":"created","comment":{"url":"https://api.github.com/repos/libgit2/libgit2/pulls/comments/45206151","id":45206151,"diff_hunk":"@@ -0,0 +1,1025 @@\n+#include \"git2/patch.h\"\n+#include \"patch.h\"\n+#include \"path.h\"\n+\n+#define parse_err(...) \\\n+\t( giterr_set(GITERR_PATCH, __VA_ARGS__), -1 )\n+\n+typedef struct {\n+\tgit_patch base;\n+\n+\tgit_patch_options opts;\n+\n+\t/* the patch contents, which lines will point into. */\n+\t/* TODO: allow us to point somewhere refcounted. */\n+\tchar *content;\n+\n+\t/* the paths from the `diff --git` header, these will be used if this is not\n+\t * a rename (and rename paths are specified) or if no `+++`/`---` line specify\n+\t * the paths.\n+\t */\n+\tchar *header_old_path, *header_new_path;\n+\n+\t/* renamed paths are precise and are not prefixed */\n+\tchar *rename_old_path, *rename_new_path;\n+\n+\t/* the paths given in `---` and `+++` lines */\n+\tchar *old_path, *new_path;\n+\n+\t/* the prefixes from the old/new paths */\n+\tchar *old_prefix, *new_prefix;\n+} git_patch_parsed;\n+\n+typedef struct {\n+\tconst char *content;\n+\tsize_t content_len;\n+\n+\tconst char *line;\n+\tsize_t line_len;\n+\tsize_t line_num;\n+\n+\tsize_t remain;\n+} patch_parse_ctx;\n+\n+\n+static void parse_advance_line(patch_parse_ctx *ctx)\n+{\n+\tctx->line += ctx->line_len;\n+\tctx->remain -= ctx->line_len;\n+\tctx->line_len = git__linenlen(ctx->line, ctx->remain);\n+\tctx->line_num++;\n+}\n+\n+static void parse_advance_chars(patch_parse_ctx *ctx, size_t char_cnt)\n+{\n+\tctx->line += char_cnt;\n+\tctx->remain -= char_cnt;\n+\tctx->line_len -= char_cnt;\n+}\n+\n+static int parse_advance_expected(\n+\tpatch_parse_ctx *ctx,\n+\tconst char *expected,\n+\tsize_t expected_len)\n+{\n+\tif (ctx->line_len < expected_len)\n+\t\treturn -1;\n+\n+\tif (memcmp(ctx->line, expected, expected_len) != 0)\n+\t\treturn -1;\n+\n+\tparse_advance_chars(ctx, expected_len);\n+\treturn 0;\n+}\n+\n+static int parse_advance_ws(patch_parse_ctx *ctx)\n+{\n+\tint ret = -1;\n+\n+\twhile (ctx->line_len > 0 &&\n+\t\tctx->line[0] != '\\n' &&\n+\t\tgit__isspace(ctx->line[0])) {\n+\t\tctx->line++;\n+\t\tctx->line_len--;\n+\t\tctx->remain--;\n+\t\tret = 0;\n+\t}\n+\n+\treturn ret;\n+}\n+\n+static int parse_advance_nl(patch_parse_ctx *ctx)\n+{\n+\tif (ctx->line_len != 1 || ctx->line[0] != '\\n')\n+\t\treturn -1;\n+\n+\tparse_advance_line(ctx);\n+\treturn 0;\n+}\n+\n+static int header_path_len(patch_parse_ctx *ctx)\n+{\n+\tbool inquote = 0;\n+\tbool quoted = (ctx->line_len > 0 && ctx->line[0] == '\"');\n+\tsize_t len;\n+\n+\tfor (len = quoted; len < ctx->line_len; len++) {\n+\t\tif (!quoted && git__isspace(ctx->line[len]))\n+\t\t\tbreak;\n+\t\telse if (quoted && !inquote && ctx->line[len] == '\"') {\n+\t\t\tlen++;\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\tinquote = (!inquote && ctx->line[len] == '\\\\');\n+\t}\n+\n+\treturn len;\n+}\n+\n+static int parse_header_path_buf(git_buf *path, patch_parse_ctx *ctx)\n+{\n+\tint path_len, error = 0;\n+\n+\tpath_len = header_path_len(ctx);\n+\n+\tif ((error = git_buf_put(path, ctx->line, path_len)) < 0)\n+\t\tgoto done;\n+\n+\tparse_advance_chars(ctx, path_len);\n+\n+\tgit_buf_rtrim(path);\n+\n+\tif (path->size > 0 && path->ptr[0] == '\"')\n+\t\terror = git_buf_unquote(path);\n+\n+\tif (error < 0)\n+\t\tgoto done;\n+\n+\tgit_path_squash_slashes(path);\n+\n+done:\n+\treturn error;\n+}\n+\n+static int parse_header_path(char **out, patch_parse_ctx *ctx)\n+{\n+\tgit_buf path = GIT_BUF_INIT;\n+\tint error = parse_header_path_buf(&path, ctx);\n+\n+\t*out = git_buf_detach(&path);\n+\n+\treturn error;\n+}\n+\n+static int parse_header_git_oldpath(\n+\tgit_patch_parsed *patch, patch_parse_ctx *ctx)\n+{\n+\treturn parse_header_path(&patch->old_path, ctx);\n+}\n+\n+static int parse_header_git_newpath(\n+\tgit_patch_parsed *patch, patch_parse_ctx *ctx)\n+{\n+\treturn parse_header_path(&patch->new_path, ctx);\n+}\n+\n+static int parse_header_mode(uint16_t *mode, patch_parse_ctx *ctx)\n+{\n+\tconst char *end;\n+\tint32_t m;\n+\tint ret;\n+\n+\tif (ctx->line_len < 1 || !git__isdigit(ctx->line[0]))\n+\t\treturn parse_err(\"invalid file mode at line %d\", ctx->line_num);\n+\n+\tif ((ret = git__strtonl32(&m, ctx->line, ctx->line_len, &end, 8)) < 0)\n+\t\treturn ret;\n+\n+\tif (m > UINT16_MAX)\n+\t\treturn -1;\n+\n+\t*mode = (uint16_t)m;\n+\n+\tparse_advance_chars(ctx, (end - ctx->line));\n+\n+\treturn ret;\n+}\n+\n+static int parse_header_oid(\n+\tgit_oid *oid,\n+\tint *oid_len,\n+\tpatch_parse_ctx *ctx)\n+{\n+\tsize_t len;\n+\n+\tfor (len = 0; len < ctx->line_len && len < GIT_OID_HEXSZ; len++) {\n+\t\tif (!git__isxdigit(ctx->line[len]))\n+\t\t\tbreak;\n+\t}\n+\n+\tif (len < GIT_OID_MINPREFIXLEN ||\n+\t\tgit_oid_fromstrn(oid, ctx->line, len) < 0)\n+\t\treturn parse_err(\"invalid hex formatted object id at line %d\",\n+\t\t\tctx->line_num);\n+\n+\tparse_advance_chars(ctx, len);\n+\n+\t*oid_len = (int)len;\n+\n+\treturn 0;\n+}\n+\n+static int parse_header_git_index(\n+\tgit_patch_parsed *patch, patch_parse_ctx *ctx)\n+{\n+\tif (parse_header_oid(&patch->base.delta->old_file.id,\n+\t\t\t&patch->base.delta->old_file.id_abbrev, ctx) < 0 ||\n+\t\tparse_advance_expected(ctx, \"..\", 2) < 0 ||\n+\t\tparse_header_oid(&patch->base.delta->new_file.id,\n+\t\t\t&patch->base.delta->new_file.id_abbrev, ctx) < 0)\n+\t\treturn -1;\n+\n+\tif (ctx->line_len > 0 && ctx->line[0] == ' ') {\n+\t\tuint16_t mode;\n+\n+\t\tparse_advance_chars(ctx, 1);\n+\n+\t\tif (parse_header_mode(&mode, ctx) < 0)\n+\t\t\treturn -1;\n+\n+\t\tif (!patch->base.delta->new_file.mode)\n+\t\t\tpatch->base.delta->new_file.mode = mode;\n+\n+\t\tif (!patch->base.delta->old_file.mode)\n+\t\t\tpatch->base.delta->old_file.mode = mode;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int parse_header_git_oldmode(\n+\tgit_patch_parsed *patch, patch_parse_ctx *ctx)\n+{\n+\treturn parse_header_mode(&patch->base.delta->old_file.mode, ctx);\n+}\n+\n+static int parse_header_git_newmode(\n+\tgit_patch_parsed *patch, patch_parse_ctx *ctx)\n+{\n+\treturn parse_header_mode(&patch->base.delta->new_file.mode, ctx);\n+}\n+\n+static int parse_header_git_deletedfilemode(\n+\tgit_patch_parsed *patch,\n+\tpatch_parse_ctx *ctx)\n+{\n+\tgit__free((char *)patch->base.delta->old_file.path);\n+\n+\tpatch->base.delta->old_file.path = NULL;\n+\tpatch->base.delta->status = GIT_DELTA_DELETED;\n+\tpatch->base.delta->nfiles = 1;\n+\n+\treturn parse_header_mode(&patch->base.delta->old_file.mode, ctx);\n+}\n+\n+static int parse_header_git_newfilemode(\n+\tgit_patch_parsed *patch,\n+\tpatch_parse_ctx *ctx)\n+{\n+\tgit__free((char *)patch->base.delta->new_file.path);\n+\n+\tpatch->base.delta->new_file.path = NULL;\n+\tpatch->base.delta->status = GIT_DELTA_ADDED;\n+\tpatch->base.delta->nfiles = 1;\n+\n+\treturn parse_header_mode(&patch->base.delta->new_file.mode, ctx);\n+}\n+\n+static int parse_header_rename(\n+\tchar **out,\n+\tpatch_parse_ctx *ctx)\n+{\n+\tgit_buf path = GIT_BUF_INIT;\n+\n+\tif (parse_header_path_buf(&path, ctx) < 0)\n+\t\treturn -1;\n+\n+\t/* Note: the `rename from` and `rename to` lines include the literal\n+\t * filename.  They do *not* include the prefix.  (Who needs consistency?)\n+\t */\n+\t*out = git_buf_detach(&path);\n+\treturn 0;\n+}\n+\n+static int parse_header_renamefrom(\n+\tgit_patch_parsed *patch, patch_parse_ctx *ctx)\n+{\n+\tpatch->base.delta->status = GIT_DELTA_RENAMED;\n+\treturn parse_header_rename(&patch->rename_old_path, ctx);\n+}\n+\n+static int parse_header_renameto(\n+\tgit_patch_parsed *patch, patch_parse_ctx *ctx)\n+{\n+\tpatch->base.delta->status = GIT_DELTA_RENAMED;\n+\treturn parse_header_rename(&patch->rename_new_path, ctx);\n+}\n+\n+static int parse_header_percent(uint16_t *out, patch_parse_ctx *ctx)\n+{\n+\tint32_t val;\n+\tconst char *end;\n+\n+\tif (ctx->line_len < 1 || !git__isdigit(ctx->line[0]) ||\n+\t\tgit__strtonl32(&val, ctx->line, ctx->line_len, &end, 10) < 0)\n+\t\treturn -1;\n+\n+\tparse_advance_chars(ctx, (end - ctx->line));\n+\n+\tif (parse_advance_expected(ctx, \"%\", 1) < 0)\n+\t\treturn -1;\n+\n+\tif (val > 100)\n+\t\treturn -1;\n+\n+\t*out = val;\n+\treturn 0;\n+}\n+\n+static int parse_header_similarity(\n+\tgit_patch_parsed *patch, patch_parse_ctx *ctx)\n+{\n+\tif (parse_header_percent(&patch->base.delta->similarity, ctx) < 0)\n+\t\treturn parse_err(\"invalid similarity percentage at line %d\",\n+\t\t\tctx->line_num);\n+\n+\treturn 0;\n+}\n+\n+static int parse_header_dissimilarity(\n+\tgit_patch_parsed *patch, patch_parse_ctx *ctx)\n+{\n+\tuint16_t dissimilarity;\n+\n+\tif (parse_header_percent(&dissimilarity, ctx) < 0)\n+\t\treturn parse_err(\"invalid similarity percentage at line %d\",\n+\t\t\tctx->line_num);\n+\n+\tpatch->base.delta->similarity = 100 - dissimilarity;\n+\n+\treturn 0;\n+}\n+\n+typedef struct {\n+\tconst char *str;\n+\tint(*fn)(git_patch_parsed *, patch_parse_ctx *);\n+} header_git_op;\n+\n+static const header_git_op header_git_ops[] = {\n+\t{ \"@@ -\", NULL },\n+\t{ \"GIT binary patch\", NULL },\n+\t{ \"--- \", parse_header_git_oldpath },\n+\t{ \"+++ \", parse_header_git_newpath },\n+\t{ \"index \", parse_header_git_index },\n+\t{ \"old mode \", parse_header_git_oldmode },\n+\t{ \"new mode \", parse_header_git_newmode },\n+\t{ \"deleted file mode \", parse_header_git_deletedfilemode },\n+\t{ \"new file mode \", parse_header_git_newfilemode },\n+\t{ \"rename from \", parse_header_renamefrom },\n+\t{ \"rename to \", parse_header_renameto },\n+\t{ \"rename old \", parse_header_renamefrom },\n+\t{ \"rename new \", parse_header_renameto },\n+\t{ \"similarity index \", parse_header_similarity },\n+\t{ \"dissimilarity index \", parse_header_dissimilarity },\n+};\n+\n+static int parse_header_git(\n+\tgit_patch_parsed *patch,\n+\tpatch_parse_ctx *ctx)\n+{\n+\tsize_t i;\n+\tint error = 0;\n+\n+\t/* Parse the diff --git line */\n+\tif (parse_advance_expected(ctx, \"diff --git \", 11) < 0)\n+\t\treturn parse_err(\"corrupt git diff header at line %d\", ctx->line_num);\n+\n+\tif (parse_header_path(&patch->header_old_path, ctx) < 0)\n+\t\treturn parse_err(\"corrupt old path in git diff header at line %d\",\n+\t\t\tctx->line_num);\n+\n+\tif (parse_advance_ws(ctx) < 0 ||\n+\t\tparse_header_path(&patch->header_new_path, ctx) < 0)\n+\t\treturn parse_err(\"corrupt new path in git diff header at line %d\",\n+\t\t\tctx->line_num);\n+\n+\t/* Parse remaining header lines */\n+\tfor (parse_advance_line(ctx); ctx->remain > 0; parse_advance_line(ctx)) {\n+\t\tif (ctx->line_len == 0 || ctx->line[ctx->line_len - 1] != '\\n')\n+\t\t\tbreak;\n+\n+\t\tfor (i = 0; i < ARRAY_SIZE(header_git_ops); i++) {\n+\t\t\tconst header_git_op *op = &header_git_ops[i];\n+\t\t\tsize_t op_len = strlen(op->str);\n+\n+\t\t\tif (memcmp(ctx->line, op->str, min(op_len, ctx->line_len)) != 0)\n+\t\t\t\tcontinue;\n+\n+\t\t\t/* Do not advance if this is the patch separator */\n+\t\t\tif (op->fn == NULL)\n+\t\t\t\tgoto done;\n+\n+\t\t\tparse_advance_chars(ctx, op_len);\n+\n+\t\t\tif ((error = op->fn(patch, ctx)) < 0)\n+\t\t\t\tgoto done;\n+\n+\t\t\tparse_advance_ws(ctx);\n+\t\t\tparse_advance_expected(ctx, \"\\n\", 1);\n+\n+\t\t\tif (ctx->line_len > 0) {\n+\t\t\t\terror = parse_err(\"trailing data at line %d\", ctx->line_num);\n+\t\t\t\tgoto done;\n+\t\t\t}\n+\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\n+done:\n+\treturn error;\n+}\n+\n+static int parse_number(git_off_t *out, patch_parse_ctx *ctx)\n+{\n+\tconst char *end;\n+\tint64_t num;\n+\n+\tif (!git__isdigit(ctx->line[0]))\n+\t\treturn -1;\n+\n+\tif (git__strtonl64(&num, ctx->line, ctx->line_len, &end, 10) < 0)\n+\t\treturn -1;\n+\n+\tif (num < 0)\n+\t\treturn -1;\n+\n+\t*out = num;\n+\tparse_advance_chars(ctx, (end - ctx->line));\n+\n+\treturn 0;\n+}\n+\n+static int parse_int(int *out, patch_parse_ctx *ctx)\n+{\n+\tgit_off_t num;\n+\n+\tif (parse_number(&num, ctx) < 0 || !git__is_int(num))\n+\t\treturn -1;\n+\n+\t*out = (int)num;\n+\treturn 0;\n+}\n+\n+static int parse_hunk_header(\n+\tgit_patch_hunk *hunk,\n+\tpatch_parse_ctx *ctx)\n+{\n+\tconst char *header_start = ctx->line;\n+\n+\thunk->hunk.old_lines = 1;\n+\thunk->hunk.new_lines = 1;\n+\n+\tif (parse_advance_expected(ctx, \"@@ -\", 4) < 0 ||\n+\t\tparse_int(&hunk->hunk.old_start, ctx) < 0)\n+\t\tgoto fail;\n+\n+\tif (ctx->line_len > 0 && ctx->line[0] == ',') {\n+\t\tif (parse_advance_expected(ctx, \",\", 1) < 0 ||\n+\t\t\tparse_int(&hunk->hunk.old_lines, ctx) < 0)\n+\t\t\tgoto fail;\n+\t}\n+\n+\tif (parse_advance_expected(ctx, \" +\", 2) < 0 ||\n+\t\tparse_int(&hunk->hunk.new_start, ctx) < 0)\n+\t\tgoto fail;\n+\n+\tif (ctx->line_len > 0 && ctx->line[0] == ',') {\n+\t\tif (parse_advance_expected(ctx, \",\", 1) < 0 ||\n+\t\t\tparse_int(&hunk->hunk.new_lines, ctx) < 0)\n+\t\t\tgoto fail;\n+\t}\n+\n+\tif (parse_advance_expected(ctx, \" @@\", 3) < 0)\n+\t\tgoto fail;\n+\n+\tparse_advance_line(ctx);\n+\n+\tif (!hunk->hunk.old_lines && !hunk->hunk.new_lines)\n+\t\tgoto fail;\n+\n+\thunk->hunk.header_len = ctx->line - header_start;\n+\tif (hunk->hunk.header_len > (GIT_DIFF_HUNK_HEADER_SIZE - 1))\n+\t\treturn parse_err(\"oversized patch hunk header at line %d\",\n+\t\t\tctx->line_num);\n+\n+\tmemcpy(hunk->hunk.header, header_start, hunk->hunk.header_len);\n+\thunk->hunk.header[hunk->hunk.header_len] = '\\0';\n+\n+\treturn 0;\n+\n+fail:\n+\tgiterr_set(GITERR_PATCH, \"invalid patch hunk header at line %d\",\n+\t\tctx->line_num);\n+\treturn -1;\n+}\n+\n+static int parse_hunk_body(\n+\tgit_patch_parsed *patch,\n+\tgit_patch_hunk *hunk,\n+\tpatch_parse_ctx *ctx)\n+{\n+\tgit_diff_line *line;\n+\tint error = 0;\n+\n+\tint oldlines = hunk->hunk.old_lines;\n+\tint newlines = hunk->hunk.new_lines;\n+\n+\tfor (;\n+\t\tctx->remain > 4 && (oldlines || newlines) &&\n+\t\tmemcmp(ctx->line, \"@@ -\", 4) != 0;\n+\t\tparse_advance_line(ctx)) {\n+\n+\t\tint origin;\n+\t\tint prefix = 1;\n+\n+\t\tif (ctx->line_len == 0 || ctx->line[ctx->line_len - 1] != '\\n') {\n+\t\t\terror = parse_err(\"invalid patch instruction at line %d\",\n+\t\t\t\tctx->line_num);\n+\t\t\tgoto done;\n+\t\t}\n+\n+\t\tswitch (ctx->line[0]) {\n+\t\tcase '\\n':\n+\t\t\tprefix = 0;\n+\n+\t\tcase ' ':\n+\t\t\torigin = GIT_DIFF_LINE_CONTEXT;\n+\t\t\toldlines--;\n+\t\t\tnewlines--;\n+\t\t\tbreak;\n+\n+\t\tcase '-':\n+\t\t\torigin = GIT_DIFF_LINE_DELETION;\n+\t\t\toldlines--;\n+\t\t\tbreak;\n+\n+\t\tcase '+':\n+\t\t\torigin = GIT_DIFF_LINE_ADDITION;\n+\t\t\tnewlines--;\n+\t\t\tbreak;\n+\n+\t\tdefault:\n+\t\t\terror = parse_err(\"invalid patch hunk at line %d\", ctx->line_num);\n+\t\t\tgoto done;\n+\t\t}\n+\n+\t\tline = git_array_alloc(patch->base.lines);\n+\t\tGITERR_CHECK_ALLOC(line);\n+\n+\t\tmemset(line, 0x0, sizeof(git_diff_line));\n+\n+\t\tline->content = ctx->line + prefix;\n+\t\tline->content_len = ctx->line_len - prefix;\n+\t\tline->content_offset = ctx->content_len - ctx->remain;\n+\t\tline->origin = origin;\n+\n+\t\thunk->line_count++;\n+\t}\n+\n+\tif (oldlines || newlines) {\n+\t\terror = parse_err(\n+\t\t\t\"invalid patch hunk, expected %d old lines and %d new lines\",\n+\t\t\thunk->hunk.old_lines, hunk->hunk.new_lines);\n+\t\tgoto done;\n+\t}\n+\n+\t/* Handle \"\\ No newline at end of file\".  Only expect the leading\n+\t* backslash, though, because the rest of the string could be\n+\t* localized.  Because `diff` optimizes for the case where you\n+\t* want to apply the patch by hand.\n+\t*/\n+\tif (ctx->line_len >= 2 && memcmp(ctx->line, \"\\\\ \", 2) == 0 &&\n+\t\tgit_array_size(patch->base.lines) > 0) {\n+\n+\t\tline = git_array_get(patch->base.lines, git_array_size(patch->base.lines) - 1);\n+\n+\t\tif (line->content_len < 1) {\n+\t\t\terror = parse_err(\"cannot trim trailing newline of empty line\");\n+\t\t\tgoto done;\n+\t\t}\n+\n+\t\tline->content_len--;\n+\n+\t\tparse_advance_line(ctx);\n+\t}\n+\n+done:\n+\treturn error;\n+}\n+\n+static int parse_header_traditional(\n+\tgit_patch_parsed *patch, patch_parse_ctx *ctx)\n+{\n+\tGIT_UNUSED(patch);\n+\tGIT_UNUSED(ctx);\n+\n+\treturn 1;\n+}\n+\n+static int parsed_patch_header(\n+\tgit_patch_parsed *patch,\n+\tpatch_parse_ctx *ctx)\n+{\n+\tint error = 0;\n+\n+\tfor (ctx->line = ctx->content; ctx->remain > 0; parse_advance_line(ctx)) {\n+\t\t/* This line is too short to be a patch header. */\n+\t\tif (ctx->line_len < 6)\n+\t\t\tcontinue;\n+\n+\t\t/* This might be a hunk header without a patch header, provide a\n+\t\t* sensible error message. */\n+\t\tif (memcmp(ctx->line, \"@@ -\", 4) == 0) {\n+\t\t\tsize_t line_num = ctx->line_num;\n+\t\t\tgit_patch_hunk hunk;\n+\n+\t\t\t/* If this cannot be parsed as a hunk header, it's just leading\n+\t\t\t* noise, continue.\n+\t\t\t*/\n+\t\t\tif (parse_hunk_header(&hunk, ctx) < 0) {\n+\t\t\t\tgiterr_clear();\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\n+\t\t\terror = parse_err(\"invalid hunk header outside patch at line %d\",\n+\t\t\t\tline_num);\n+\t\t\tgoto done;\n+\t\t}\n+\n+\t\t/* This buffer is too short to contain a patch. */\n+\t\tif (ctx->remain < ctx->line_len + 6)\n+\t\t\tbreak;\n+\n+\t\t/* A proper git patch */\n+\t\tif (ctx->line_len >= 11 && memcmp(ctx->line, \"diff --git \", 11) == 0) {\n+\t\t\terror = parse_header_git(patch, ctx);\n+\t\t\tgoto done;\n+\t\t}\n+\n+\t\tif ((error = parse_header_traditional(patch, ctx)) <= 0)\n+\t\t\tgoto done;\n+\n+\t\terror = 0;\n+\t\tcontinue;\n+\t}\n+\n+\terror = parse_err(\"no header in patch file\");\n+\n+done:\n+\treturn error;\n+}\n+\n+static int parsed_patch_binary_side(\n+\tgit_diff_binary_file *binary,\n+\tpatch_parse_ctx *ctx)\n+{\n+\tgit_diff_binary_t type = GIT_DIFF_BINARY_NONE;\n+\tgit_buf base85 = GIT_BUF_INIT, decoded = GIT_BUF_INIT;\n+\tgit_off_t len;\n+\tint error = 0;\n+\n+\tif (ctx->line_len >= 8 && memcmp(ctx->line, \"literal \", 8) == 0) {\n+\t\ttype = GIT_DIFF_BINARY_LITERAL;\n+\t\tparse_advance_chars(ctx, 8);\n+\t}\n+\telse if (ctx->line_len >= 6 && memcmp(ctx->line, \"delta \", 6) == 0) {\n+\t\ttype = GIT_DIFF_BINARY_DELTA;\n+\t\tparse_advance_chars(ctx, 6);\n+\t}\n+\telse {\n+\t\terror = parse_err(\"unknown binary delta type at line %d\", ctx->line_num);\n+\t\tgoto done;\n+\t}\n+\n+\tif (parse_number(&len, ctx) < 0 || parse_advance_nl(ctx) < 0 || len < 0) {\n+\t\terror = parse_err(\"invalid binary size at line %d\", ctx->line_num);\n+\t\tgoto done;\n+\t}\n+\n+\twhile (ctx->line_len) {\n+\t\tchar c = ctx->line[0];\n+\t\tsize_t encoded_len, decoded_len = 0, decoded_orig = decoded.size;\n+\n+\t\tif (c == '\\n')\n+\t\t\tbreak;\n+\t\telse if (c >= 'A' && c <= 'Z')\n+\t\t\tdecoded_len = c - 'A' + 1;\n+\t\telse if (c >= 'a' && c <= 'z')\n+\t\t\tdecoded_len = c - 'a' + 26 + 1;","path":"src/patch_parse.c","position":710,"original_position":710,"commit_id":"829bb21c9bef2f488fe37fcf810888fd79d72890","original_commit_id":"829bb21c9bef2f488fe37fcf810888fd79d72890","user":{"login":"carlosmn","id":335443,"avatar_url":"https://avatars.githubusercontent.com/u/335443?v=3","gravatar_id":"","url":"https://api.github.com/users/carlosmn","html_url":"https://github.com/carlosmn","followers_url":"https://api.github.com/users/carlosmn/followers","following_url":"https://api.github.com/users/carlosmn/following{/other_user}","gists_url":"https://api.github.com/users/carlosmn/gists{/gist_id}","starred_url":"https://api.github.com/users/carlosmn/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/carlosmn/subscriptions","organizations_url":"https://api.github.com/users/carlosmn/orgs","repos_url":"https://api.github.com/users/carlosmn/repos","events_url":"https://api.github.com/users/carlosmn/events{/privacy}","received_events_url":"https://api.github.com/users/carlosmn/received_events","type":"User","site_admin":false},"body":"This `26` looks like it's the length of the basic English alphabet, but it still feels rather magic. Maybe we can put it as `('z' - 'a') + 1` or have a comment?","created_at":"2015-11-18T14:38:21Z","updated_at":"2015-11-18T14:38:21Z","html_url":"https://github.com/libgit2/libgit2/pull/3223#discussion_r45206151","pull_request_url":"https://api.github.com/repos/libgit2/libgit2/pulls/3223","_links":{"self":{"href":"https://api.github.com/repos/libgit2/libgit2/pulls/comments/45206151"},"html":{"href":"https://github.com/libgit2/libgit2/pull/3223#discussion_r45206151"},"pull_request":{"href":"https://api.github.com/repos/libgit2/libgit2/pulls/3223"}}},"pull_request":{"url":"https://api.github.com/repos/libgit2/libgit2/pulls/3223","id":37732987,"html_url":"https://github.com/libgit2/libgit2/pull/3223","diff_url":"https://github.com/libgit2/libgit2/pull/3223.diff","patch_url":"https://github.com/libgit2/libgit2/pull/3223.patch","issue_url":"https://api.github.com/repos/libgit2/libgit2/issues/3223","number":3223,"state":"open","locked":false,"title":"Patch application","user":{"login":"ethomson","id":1130014,"avatar_url":"https://avatars.githubusercontent.com/u/1130014?v=3","gravatar_id":"","url":"https://api.github.com/users/ethomson","html_url":"https://github.com/ethomson","followers_url":"https://api.github.com/users/ethomson/followers","following_url":"https://api.github.com/users/ethomson/following{/other_user}","gists_url":"https://api.github.com/users/ethomson/gists{/gist_id}","starred_url":"https://api.github.com/users/ethomson/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ethomson/subscriptions","organizations_url":"https://api.github.com/users/ethomson/orgs","repos_url":"https://api.github.com/users/ethomson/repos","events_url":"https://api.github.com/users/ethomson/events{/privacy}","received_events_url":"https://api.github.com/users/ethomson/received_events","type":"User","site_admin":false},"body":"Moved #2280 over here and based it on master.  With the refactored binary diff handling, I can proceed on binary application.\r\n\r\nThis PR is attempting to take an initial bite out of patch application, a la `git-apply`.  My goal is to parse a git-style patch file created with libgit2 or git.git into a `git_patch` and be able to apply a `git_patch` to a buffer.  This does not yet open up any public API for application, just internal methods; I was hoping to get some eyeballs on this and perhaps even merge it before I did something like applying patches into the working directory.\r\n\r\n- [x] Binary patches\r\n- [ ] ~~Traditional-format patches (should just need to parse their headers)~~\r\n- [ ] ~~Improved whitespace tolerance during application~~\r\n- [ ] ~~Context reduction during application~~\r\n- [x] Handle abbreviated OIDs better","created_at":"2015-06-15T20:40:29Z","updated_at":"2015-11-18T14:38:21Z","closed_at":null,"merged_at":null,"merge_commit_sha":"6b21456ddd811d0e62fae313d7cf87dba925e88d","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/libgit2/libgit2/pulls/3223/commits","review_comments_url":"https://api.github.com/repos/libgit2/libgit2/pulls/3223/comments","review_comment_url":"https://api.github.com/repos/libgit2/libgit2/pulls/comments{/number}","comments_url":"https://api.github.com/repos/libgit2/libgit2/issues/3223/comments","statuses_url":"https://api.github.com/repos/libgit2/libgit2/statuses/829bb21c9bef2f488fe37fcf810888fd79d72890","head":{"label":"ethomson:apply","ref":"apply","sha":"829bb21c9bef2f488fe37fcf810888fd79d72890","user":{"login":"ethomson","id":1130014,"avatar_url":"https://avatars.githubusercontent.com/u/1130014?v=3","gravatar_id":"","url":"https://api.github.com/users/ethomson","html_url":"https://github.com/ethomson","followers_url":"https://api.github.com/users/ethomson/followers","following_url":"https://api.github.com/users/ethomson/following{/other_user}","gists_url":"https://api.github.com/users/ethomson/gists{/gist_id}","starred_url":"https://api.github.com/users/ethomson/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ethomson/subscriptions","organizations_url":"https://api.github.com/users/ethomson/orgs","repos_url":"https://api.github.com/users/ethomson/repos","events_url":"https://api.github.com/users/ethomson/events{/privacy}","received_events_url":"https://api.github.com/users/ethomson/received_events","type":"User","site_admin":false},"repo":{"id":6315414,"name":"libgit2","full_name":"ethomson/libgit2","owner":{"login":"ethomson","id":1130014,"avatar_url":"https://avatars.githubusercontent.com/u/1130014?v=3","gravatar_id":"","url":"https://api.github.com/users/ethomson","html_url":"https://github.com/ethomson","followers_url":"https://api.github.com/users/ethomson/followers","following_url":"https://api.github.com/users/ethomson/following{/other_user}","gists_url":"https://api.github.com/users/ethomson/gists{/gist_id}","starred_url":"https://api.github.com/users/ethomson/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ethomson/subscriptions","organizations_url":"https://api.github.com/users/ethomson/orgs","repos_url":"https://api.github.com/users/ethomson/repos","events_url":"https://api.github.com/users/ethomson/events{/privacy}","received_events_url":"https://api.github.com/users/ethomson/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/ethomson/libgit2","description":"The Library","fork":true,"url":"https://api.github.com/repos/ethomson/libgit2","forks_url":"https://api.github.com/repos/ethomson/libgit2/forks","keys_url":"https://api.github.com/repos/ethomson/libgit2/keys{/key_id}","collaborators_url":"https://api.github.com/repos/ethomson/libgit2/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/ethomson/libgit2/teams","hooks_url":"https://api.github.com/repos/ethomson/libgit2/hooks","issue_events_url":"https://api.github.com/repos/ethomson/libgit2/issues/events{/number}","events_url":"https://api.github.com/repos/ethomson/libgit2/events","assignees_url":"https://api.github.com/repos/ethomson/libgit2/assignees{/user}","branches_url":"https://api.github.com/repos/ethomson/libgit2/branches{/branch}","tags_url":"https://api.github.com/repos/ethomson/libgit2/tags","blobs_url":"https://api.github.com/repos/ethomson/libgit2/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/ethomson/libgit2/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/ethomson/libgit2/git/refs{/sha}","trees_url":"https://api.github.com/repos/ethomson/libgit2/git/trees{/sha}","statuses_url":"https://api.github.com/repos/ethomson/libgit2/statuses/{sha}","languages_url":"https://api.github.com/repos/ethomson/libgit2/languages","stargazers_url":"https://api.github.com/repos/ethomson/libgit2/stargazers","contributors_url":"https://api.github.com/repos/ethomson/libgit2/contributors","subscribers_url":"https://api.github.com/repos/ethomson/libgit2/subscribers","subscription_url":"https://api.github.com/repos/ethomson/libgit2/subscription","commits_url":"https://api.github.com/repos/ethomson/libgit2/commits{/sha}","git_commits_url":"https://api.github.com/repos/ethomson/libgit2/git/commits{/sha}","comments_url":"https://api.github.com/repos/ethomson/libgit2/comments{/number}","issue_comment_url":"https://api.github.com/repos/ethomson/libgit2/issues/comments{/number}","contents_url":"https://api.github.com/repos/ethomson/libgit2/contents/{+path}","compare_url":"https://api.github.com/repos/ethomson/libgit2/compare/{base}...{head}","merges_url":"https://api.github.com/repos/ethomson/libgit2/merges","archive_url":"https://api.github.com/repos/ethomson/libgit2/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/ethomson/libgit2/downloads","issues_url":"https://api.github.com/repos/ethomson/libgit2/issues{/number}","pulls_url":"https://api.github.com/repos/ethomson/libgit2/pulls{/number}","milestones_url":"https://api.github.com/repos/ethomson/libgit2/milestones{/number}","notifications_url":"https://api.github.com/repos/ethomson/libgit2/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/ethomson/libgit2/labels{/name}","releases_url":"https://api.github.com/repos/ethomson/libgit2/releases{/id}","created_at":"2012-10-21T00:45:28Z","updated_at":"2015-03-30T13:32:43Z","pushed_at":"2015-11-17T16:22:47Z","git_url":"git://github.com/ethomson/libgit2.git","ssh_url":"git@github.com:ethomson/libgit2.git","clone_url":"https://github.com/ethomson/libgit2.git","svn_url":"https://github.com/ethomson/libgit2","homepage":"http://libgit2.github.com","size":22185,"stargazers_count":2,"watchers_count":2,"language":"C","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":2,"mirror_url":null,"open_issues_count":0,"forks":2,"open_issues":0,"watchers":2,"default_branch":"master"}},"base":{"label":"libgit2:master","ref":"master","sha":"1b4449b40d1a69026311a45d2e3534b1d2cb66a9","user":{"login":"libgit2","id":394745,"avatar_url":"https://avatars.githubusercontent.com/u/394745?v=3","gravatar_id":"","url":"https://api.github.com/users/libgit2","html_url":"https://github.com/libgit2","followers_url":"https://api.github.com/users/libgit2/followers","following_url":"https://api.github.com/users/libgit2/following{/other_user}","gists_url":"https://api.github.com/users/libgit2/gists{/gist_id}","starred_url":"https://api.github.com/users/libgit2/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/libgit2/subscriptions","organizations_url":"https://api.github.com/users/libgit2/orgs","repos_url":"https://api.github.com/users/libgit2/repos","events_url":"https://api.github.com/users/libgit2/events{/privacy}","received_events_url":"https://api.github.com/users/libgit2/received_events","type":"Organization","site_admin":false},"repo":{"id":901662,"name":"libgit2","full_name":"libgit2/libgit2","owner":{"login":"libgit2","id":394745,"avatar_url":"https://avatars.githubusercontent.com/u/394745?v=3","gravatar_id":"","url":"https://api.github.com/users/libgit2","html_url":"https://github.com/libgit2","followers_url":"https://api.github.com/users/libgit2/followers","following_url":"https://api.github.com/users/libgit2/following{/other_user}","gists_url":"https://api.github.com/users/libgit2/gists{/gist_id}","starred_url":"https://api.github.com/users/libgit2/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/libgit2/subscriptions","organizations_url":"https://api.github.com/users/libgit2/orgs","repos_url":"https://api.github.com/users/libgit2/repos","events_url":"https://api.github.com/users/libgit2/events{/privacy}","received_events_url":"https://api.github.com/users/libgit2/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/libgit2/libgit2","description":"The Library","fork":false,"url":"https://api.github.com/repos/libgit2/libgit2","forks_url":"https://api.github.com/repos/libgit2/libgit2/forks","keys_url":"https://api.github.com/repos/libgit2/libgit2/keys{/key_id}","collaborators_url":"https://api.github.com/repos/libgit2/libgit2/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/libgit2/libgit2/teams","hooks_url":"https://api.github.com/repos/libgit2/libgit2/hooks","issue_events_url":"https://api.github.com/repos/libgit2/libgit2/issues/events{/number}","events_url":"https://api.github.com/repos/libgit2/libgit2/events","assignees_url":"https://api.github.com/repos/libgit2/libgit2/assignees{/user}","branches_url":"https://api.github.com/repos/libgit2/libgit2/branches{/branch}","tags_url":"https://api.github.com/repos/libgit2/libgit2/tags","blobs_url":"https://api.github.com/repos/libgit2/libgit2/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/libgit2/libgit2/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/libgit2/libgit2/git/refs{/sha}","trees_url":"https://api.github.com/repos/libgit2/libgit2/git/trees{/sha}","statuses_url":"https://api.github.com/repos/libgit2/libgit2/statuses/{sha}","languages_url":"https://api.github.com/repos/libgit2/libgit2/languages","stargazers_url":"https://api.github.com/repos/libgit2/libgit2/stargazers","contributors_url":"https://api.github.com/repos/libgit2/libgit2/contributors","subscribers_url":"https://api.github.com/repos/libgit2/libgit2/subscribers","subscription_url":"https://api.github.com/repos/libgit2/libgit2/subscription","commits_url":"https://api.github.com/repos/libgit2/libgit2/commits{/sha}","git_commits_url":"https://api.github.com/repos/libgit2/libgit2/git/commits{/sha}","comments_url":"https://api.github.com/repos/libgit2/libgit2/comments{/number}","issue_comment_url":"https://api.github.com/repos/libgit2/libgit2/issues/comments{/number}","contents_url":"https://api.github.com/repos/libgit2/libgit2/contents/{+path}","compare_url":"https://api.github.com/repos/libgit2/libgit2/compare/{base}...{head}","merges_url":"https://api.github.com/repos/libgit2/libgit2/merges","archive_url":"https://api.github.com/repos/libgit2/libgit2/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/libgit2/libgit2/downloads","issues_url":"https://api.github.com/repos/libgit2/libgit2/issues{/number}","pulls_url":"https://api.github.com/repos/libgit2/libgit2/pulls{/number}","milestones_url":"https://api.github.com/repos/libgit2/libgit2/milestones{/number}","notifications_url":"https://api.github.com/repos/libgit2/libgit2/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/libgit2/libgit2/labels{/name}","releases_url":"https://api.github.com/repos/libgit2/libgit2/releases{/id}","created_at":"2010-09-10T16:17:48Z","updated_at":"2015-11-18T00:57:03Z","pushed_at":"2015-11-17T18:21:18Z","git_url":"git://github.com/libgit2/libgit2.git","ssh_url":"git@github.com:libgit2/libgit2.git","clone_url":"https://github.com/libgit2/libgit2.git","svn_url":"https://github.com/libgit2/libgit2","homepage":"http://libgit2.github.com","size":32793,"stargazers_count":5258,"watchers_count":5258,"language":"C","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":true,"forks_count":1255,"mirror_url":null,"open_issues_count":122,"forks":1255,"open_issues":122,"watchers":5258,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/libgit2/libgit2/pulls/3223"},"html":{"href":"https://github.com/libgit2/libgit2/pull/3223"},"issue":{"href":"https://api.github.com/repos/libgit2/libgit2/issues/3223"},"comments":{"href":"https://api.github.com/repos/libgit2/libgit2/issues/3223/comments"},"review_comments":{"href":"https://api.github.com/repos/libgit2/libgit2/pulls/3223/comments"},"review_comment":{"href":"https://api.github.com/repos/libgit2/libgit2/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/libgit2/libgit2/pulls/3223/commits"},"statuses":{"href":"https://api.github.com/repos/libgit2/libgit2/statuses/829bb21c9bef2f488fe37fcf810888fd79d72890"}}}}