Exemplo n.º 1
0
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;
}
Exemplo n.º 2
0
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;
}
Exemplo n.º 3
0
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;
}
Exemplo n.º 4
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;
}
Exemplo n.º 5
0
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;
}
Exemplo n.º 6
0
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, &current_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;
}