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_patch_header( git_patch_parsed *patch, git_patch_parse_ctx *ctx) { int error = 0; for (; ctx->parse_ctx.remain_len > 0; git_parse_advance_line(&ctx->parse_ctx)) { /* This line is too short to be a patch header. */ if (ctx->parse_ctx.line_len < 6) continue; /* This might be a hunk header without a patch header, provide a * sensible error message. */ if (git_parse_ctx_contains_s(&ctx->parse_ctx, "@@ -")) { size_t line_num = ctx->parse_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 = git_parse_err("invalid hunk header outside patch at line %"PRIuZ, line_num); goto done; } /* This buffer is too short to contain a patch. */ if (ctx->parse_ctx.remain_len < ctx->parse_ctx.line_len + 6) break; /* A proper git patch */ if (git_parse_ctx_contains_s(&ctx->parse_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_multiline_variable(git_config_parser *reader, git_buf *value, int in_quotes) { char *line = NULL, *proc_line = NULL; int quote_count; bool multiline; /* Check that the next line exists */ git_parse_advance_line(&reader->ctx); line = git__strndup(reader->ctx.line, reader->ctx.line_len); if (line == NULL) return -1; /* We've reached the end of the file, there is no continuation. * (this is not an error). */ if (line[0] == '\0') { git__free(line); return 0; } quote_count = strip_comments(line, !!in_quotes); /* If it was just a comment, pretend it didn't exist */ if (line[0] == '\0') { git__free(line); return parse_multiline_variable(reader, value, quote_count); /* TODO: unbounded recursion. This **could** be exploitable */ } if (unescape_line(&proc_line, &multiline, line, in_quotes) < 0) { git__free(line); return -1; } /* add this line to the multiline var */ git_buf_puts(value, proc_line); git__free(line); git__free(proc_line); /* * If we need to continue reading the next line, let's just * keep putting stuff in the buffer */ if (multiline) return parse_multiline_variable(reader, value, quote_count); return 0; }
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; }
static int parse_header_git( git_patch_parsed *patch, git_patch_parse_ctx *ctx) { size_t i; int error = 0; parse_header_state state = STATE_START; /* Parse remaining header lines */ for (; ctx->parse_ctx.remain_len > 0; git_parse_advance_line(&ctx->parse_ctx)) { bool found = false; if (ctx->parse_ctx.line_len == 0 || ctx->parse_ctx.line[ctx->parse_ctx.line_len - 1] != '\n') break; for (i = 0; i < ARRAY_SIZE(transitions); i++) { const parse_header_transition *transition = &transitions[i]; size_t op_len = strlen(transition->str); if (transition->expected_state != state || git__prefixcmp(ctx->parse_ctx.line, transition->str) != 0) continue; state = transition->next_state; /* Do not advance if this is the patch separator */ if (transition->fn == NULL) goto done; git_parse_advance_chars(&ctx->parse_ctx, op_len); if ((error = transition->fn(patch, ctx)) < 0) goto done; git_parse_advance_ws(&ctx->parse_ctx); if (git_parse_advance_expected_str(&ctx->parse_ctx, "\n") < 0 || ctx->parse_ctx.line_len > 0) { error = git_parse_err("trailing data at line %"PRIuZ, ctx->parse_ctx.line_num); goto done; } found = true; break; } if (!found) { error = git_parse_err("invalid patch header at line %"PRIuZ, ctx->parse_ctx.line_num); goto done; } } if (state != STATE_END) { error = git_parse_err("unexpected header line %"PRIuZ, ctx->parse_ctx.line_num); goto done; } 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; }