static int parse_hunk_header( git_patch_hunk *hunk, git_patch_parse_ctx *ctx) { const char *header_start = ctx->parse_ctx.line; char c; hunk->hunk.old_lines = 1; hunk->hunk.new_lines = 1; if (git_parse_advance_expected_str(&ctx->parse_ctx, "@@ -") < 0 || parse_int(&hunk->hunk.old_start, ctx) < 0) goto fail; if (git_parse_peek(&c, &ctx->parse_ctx, 0) == 0 && c == ',') { if (git_parse_advance_expected_str(&ctx->parse_ctx, ",") < 0 || parse_int(&hunk->hunk.old_lines, ctx) < 0) goto fail; } if (git_parse_advance_expected_str(&ctx->parse_ctx, " +") < 0 || parse_int(&hunk->hunk.new_start, ctx) < 0) goto fail; if (git_parse_peek(&c, &ctx->parse_ctx, 0) == 0 && c == ',') { if (git_parse_advance_expected_str(&ctx->parse_ctx, ",") < 0 || parse_int(&hunk->hunk.new_lines, ctx) < 0) goto fail; } if (git_parse_advance_expected_str(&ctx->parse_ctx, " @@") < 0) goto fail; git_parse_advance_line(&ctx->parse_ctx); if (!hunk->hunk.old_lines && !hunk->hunk.new_lines) goto fail; hunk->hunk.header_len = ctx->parse_ctx.line - header_start; if (hunk->hunk.header_len > (GIT_DIFF_HUNK_HEADER_SIZE - 1)) return git_parse_err("oversized patch hunk header at line %"PRIuZ, ctx->parse_ctx.line_num); memcpy(hunk->hunk.header, header_start, hunk->hunk.header_len); hunk->hunk.header[hunk->hunk.header_len] = '\0'; return 0; fail: giterr_set(GITERR_PATCH, "invalid patch hunk header at line %"PRIuZ, ctx->parse_ctx.line_num); return -1; }
static int parse_header_git_index( git_patch_parsed *patch, git_patch_parse_ctx *ctx) { char c; if (parse_header_oid(&patch->base.delta->old_file.id, &patch->base.delta->old_file.id_abbrev, ctx) < 0 || git_parse_advance_expected_str(&ctx->parse_ctx, "..") < 0 || parse_header_oid(&patch->base.delta->new_file.id, &patch->base.delta->new_file.id_abbrev, ctx) < 0) return -1; if (git_parse_peek(&c, &ctx->parse_ctx, 0) == 0 && c == ' ') { uint16_t mode; git_parse_advance_chars(&ctx->parse_ctx, 1); if (parse_header_mode(&mode, ctx) < 0) return -1; if (!patch->base.delta->new_file.mode) patch->base.delta->new_file.mode = mode; if (!patch->base.delta->old_file.mode) patch->base.delta->old_file.mode = mode; } return 0; }
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 (git_parse_ctx_contains_s(&ctx->parse_ctx, "literal ")) { type = GIT_DIFF_BINARY_LITERAL; git_parse_advance_chars(&ctx->parse_ctx, 8); } else if (git_parse_ctx_contains_s(&ctx->parse_ctx, "delta ")) { type = GIT_DIFF_BINARY_DELTA; git_parse_advance_chars(&ctx->parse_ctx, 6); } else { error = git_parse_err( "unknown binary delta type at line %"PRIuZ, ctx->parse_ctx.line_num); goto done; } if (git_parse_advance_digit(&len, &ctx->parse_ctx, 10) < 0 || git_parse_advance_nl(&ctx->parse_ctx) < 0 || len < 0) { error = git_parse_err("invalid binary size at line %"PRIuZ, ctx->parse_ctx.line_num); goto done; } while (ctx->parse_ctx.line_len) { char c; size_t encoded_len, decoded_len = 0, decoded_orig = decoded.size; git_parse_peek(&c, &ctx->parse_ctx, 0); 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 = git_parse_err("invalid binary length at line %"PRIuZ, ctx->parse_ctx.line_num); goto done; } git_parse_advance_chars(&ctx->parse_ctx, 1); encoded_len = ((decoded_len / 4) + !!(decoded_len % 4)) * 5; if (encoded_len > ctx->parse_ctx.line_len - 1) { error = git_parse_err("truncated binary data at line %"PRIuZ, ctx->parse_ctx.line_num); goto done; } if ((error = git_buf_decode_base85( &decoded, ctx->parse_ctx.line, encoded_len, decoded_len)) < 0) goto done; if (decoded.size - decoded_orig != decoded_len) { error = git_parse_err("truncated binary data at line %"PRIuZ, ctx->parse_ctx.line_num); goto done; } git_parse_advance_chars(&ctx->parse_ctx, encoded_len); if (git_parse_advance_nl(&ctx->parse_ctx) < 0) { error = git_parse_err("trailing data at line %"PRIuZ, ctx->parse_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_dispose(&base85); git_buf_dispose(&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->parse_ctx.remain_len > 1 && (oldlines || newlines) && !git_parse_ctx_contains_s(&ctx->parse_ctx, "@@ -"); git_parse_advance_line(&ctx->parse_ctx)) { char c; int origin; int prefix = 1; if (ctx->parse_ctx.line_len == 0 || ctx->parse_ctx.line[ctx->parse_ctx.line_len - 1] != '\n') { error = git_parse_err("invalid patch instruction at line %"PRIuZ, ctx->parse_ctx.line_num); goto done; } git_parse_peek(&c, &ctx->parse_ctx, 0); switch (c) { case '\n': prefix = 0; /* fall through */ 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 = git_parse_err("invalid patch hunk at line %"PRIuZ, ctx->parse_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->parse_ctx.line + prefix; line->content_len = ctx->parse_ctx.line_len - prefix; line->content_offset = ctx->parse_ctx.content_len - ctx->parse_ctx.remain_len; line->origin = origin; hunk->line_count++; } if (oldlines || newlines) { error = git_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 (git_parse_ctx_contains_s(&ctx->parse_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 = git_parse_err("cannot trim trailing newline of empty line"); goto done; } line->content_len--; git_parse_advance_line(&ctx->parse_ctx); } done: return error; }
int git_config_parse( git_config_parser *parser, git_config_parser_section_cb on_section, git_config_parser_variable_cb on_variable, git_config_parser_comment_cb on_comment, git_config_parser_eof_cb on_eof, void *data) { git_parse_ctx *ctx; char *current_section = NULL, *var_name, *var_value; int result = 0; ctx = &parser->ctx; skip_bom(ctx); for (; ctx->remain_len > 0; git_parse_advance_line(ctx)) { const char *line_start = parser->ctx.line; size_t line_len = parser->ctx.line_len; char c; if (git_parse_peek(&c, ctx, GIT_PARSE_PEEK_SKIP_WHITESPACE) < 0 && git_parse_peek(&c, ctx, 0) < 0) continue; switch (c) { case '[': /* section header, new section begins */ git__free(current_section); current_section = NULL; if ((result = parse_section_header(parser, ¤t_section)) == 0 && on_section) { result = on_section(parser, current_section, line_start, line_len, data); } break; case '\n': /* comment or whitespace-only */ case ' ': case '\t': case ';': case '#': if (on_comment) { result = on_comment(parser, line_start, line_len, data); } break; default: /* assume variable declaration */ if ((result = parse_variable(parser, &var_name, &var_value)) == 0 && on_variable) { result = on_variable(parser, current_section, var_name, var_value, line_start, line_len, data); } break; } if (result < 0) goto out; } if (on_eof) result = on_eof(parser, current_section, data); out: git__free(current_section); return result; }