Commit 5d17d726 by Edward Thomson Committed by Edward Thomson

patch parsing: parse binary patch files

parent b8dc2fdb
...@@ -65,6 +65,15 @@ static int parse_advance_ws(patch_parse_ctx *ctx) ...@@ -65,6 +65,15 @@ static int parse_advance_ws(patch_parse_ctx *ctx)
return ret; return ret;
} }
static int parse_advance_nl(patch_parse_ctx *ctx)
{
if (ctx->line_len != 1 || ctx->line[0] != '\n')
return -1;
parse_advance_line(ctx);
return 0;
}
static int header_path_len(patch_parse_ctx *ctx) static int header_path_len(patch_parse_ctx *ctx)
{ {
bool inquote = 0; bool inquote = 0;
...@@ -354,6 +363,7 @@ typedef struct { ...@@ -354,6 +363,7 @@ typedef struct {
static const header_git_op header_git_ops[] = { static const header_git_op header_git_ops[] = {
{ "@@ -", NULL }, { "@@ -", NULL },
{ "GIT binary patch", NULL },
{ "--- ", parse_header_git_oldpath }, { "--- ", parse_header_git_oldpath },
{ "+++ ", parse_header_git_newpath }, { "+++ ", parse_header_git_newpath },
{ "index ", parse_header_git_index }, { "index ", parse_header_git_index },
...@@ -426,7 +436,7 @@ done: ...@@ -426,7 +436,7 @@ done:
return error; return error;
} }
static int parse_number(int *out, patch_parse_ctx *ctx) static int parse_number(git_off_t *out, patch_parse_ctx *ctx)
{ {
const char *end; const char *end;
int64_t num; int64_t num;
...@@ -440,12 +450,23 @@ static int parse_number(int *out, patch_parse_ctx *ctx) ...@@ -440,12 +450,23 @@ static int parse_number(int *out, patch_parse_ctx *ctx)
if (num < 0) if (num < 0)
return -1; return -1;
*out = (int)num; *out = num;
parse_advance_chars(ctx, (end - ctx->line)); parse_advance_chars(ctx, (end - ctx->line));
return 0; return 0;
} }
static int parse_int(int *out, patch_parse_ctx *ctx)
{
git_off_t num;
if (parse_number(&num, ctx) < 0 || !git__is_int(num))
return -1;
*out = (int)num;
return 0;
}
static int parse_hunk_header( static int parse_hunk_header(
diff_patch_hunk *hunk, diff_patch_hunk *hunk,
patch_parse_ctx *ctx) patch_parse_ctx *ctx)
...@@ -456,22 +477,22 @@ static int parse_hunk_header( ...@@ -456,22 +477,22 @@ static int parse_hunk_header(
hunk->hunk.new_lines = 1; hunk->hunk.new_lines = 1;
if (parse_advance_expected(ctx, "@@ -", 4) < 0 || if (parse_advance_expected(ctx, "@@ -", 4) < 0 ||
parse_number(&hunk->hunk.old_start, ctx) < 0) parse_int(&hunk->hunk.old_start, ctx) < 0)
goto fail; goto fail;
if (ctx->line_len > 0 && ctx->line[0] == ',') { if (ctx->line_len > 0 && ctx->line[0] == ',') {
if (parse_advance_expected(ctx, ",", 1) < 0 || if (parse_advance_expected(ctx, ",", 1) < 0 ||
parse_number(&hunk->hunk.old_lines, ctx) < 0) parse_int(&hunk->hunk.old_lines, ctx) < 0)
goto fail; goto fail;
} }
if (parse_advance_expected(ctx, " +", 2) < 0 || if (parse_advance_expected(ctx, " +", 2) < 0 ||
parse_number(&hunk->hunk.new_start, ctx) < 0) parse_int(&hunk->hunk.new_start, ctx) < 0)
goto fail; goto fail;
if (ctx->line_len > 0 && ctx->line[0] == ',') { if (ctx->line_len > 0 && ctx->line[0] == ',') {
if (parse_advance_expected(ctx, ",", 1) < 0 || if (parse_advance_expected(ctx, ",", 1) < 0 ||
parse_number(&hunk->hunk.new_lines, ctx) < 0) parse_int(&hunk->hunk.new_lines, ctx) < 0)
goto fail; goto fail;
} }
...@@ -672,7 +693,110 @@ done: ...@@ -672,7 +693,110 @@ done:
return error; return error;
} }
static int parse_patch_body( static int parse_patch_binary_side(
git_diff_binary_file *binary,
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 (ctx->line_len >= 8 && memcmp(ctx->line, "literal ", 8) == 0) {
type = GIT_DIFF_BINARY_LITERAL;
parse_advance_chars(ctx, 8);
} else if (ctx->line_len >= 6 && memcmp(ctx->line, "delta ", 6) == 0) {
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' + 26 + 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_patch_binary(
git_patch *patch,
patch_parse_ctx *ctx)
{
int error;
if (parse_advance_expected(ctx, "GIT binary patch", 16) < 0 ||
parse_advance_nl(ctx) < 0)
return parse_err("corrupt git binary header at line %d", ctx->line_num);
/* parse old->new binary diff */
if ((error = parse_patch_binary_side(&patch->binary.new_file, ctx)) < 0)
return error;
if (parse_advance_nl(ctx) < 0)
return parse_err("corrupt git binary separator at line %d", ctx->line_num);
/* parse new->old binary diff */
if ((error = parse_patch_binary_side(&patch->binary.old_file, ctx)) < 0)
return error;
patch->delta->flags |= GIT_DIFF_FLAG_BINARY;
return 0;
}
static int parse_patch_hunks(
git_patch *patch, git_patch *patch,
patch_parse_ctx *ctx) patch_parse_ctx *ctx)
{ {
...@@ -698,6 +822,17 @@ done: ...@@ -698,6 +822,17 @@ done:
return error; return error;
} }
static int parse_patch_body(git_patch *patch, patch_parse_ctx *ctx)
{
if (ctx->line_len >= 16 && memcmp(ctx->line, "GIT binary patch", 16) == 0)
return parse_patch_binary(patch, ctx);
else if (ctx->line_len >= 4 && memcmp(ctx->line, "@@ -", 4) == 0)
return parse_patch_hunks(patch, ctx);
return 0;
}
static int check_patch(git_patch *patch) static int check_patch(git_patch *patch)
{ {
if (!patch->ofile.file->path && patch->delta->status != GIT_DELTA_ADDED) if (!patch->ofile.file->path && patch->delta->status != GIT_DELTA_ADDED)
...@@ -712,8 +847,9 @@ static int check_patch(git_patch *patch) ...@@ -712,8 +847,9 @@ static int check_patch(git_patch *patch)
} }
if (patch->delta->status == GIT_DELTA_MODIFIED && if (patch->delta->status == GIT_DELTA_MODIFIED &&
patch->nfile.file->mode == patch->ofile.file->mode && !(patch->delta->flags & GIT_DIFF_FLAG_BINARY) &&
git_array_size(patch->hunks) == 0) patch->nfile.file->mode == patch->ofile.file->mode &&
git_array_size(patch->hunks) == 0)
return parse_err("patch with no hunks"); return parse_err("patch with no hunks");
return 0; return 0;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment