Commit 9e66590b by Patrick Steinhardt

config_parse: use common parser interface

As the config parser is now cleanly separated from the config file code,
we can easily refactor the code and make use of the common parser
module. This removes quite a lot of duplicated functionality previously
used for handling the actual parser state and replaces it with the
generic interface provided by the parser context.
parent 1953c68b
......@@ -1115,6 +1115,7 @@ static int config_read(
{
struct parse_data parse_data;
git_config_parser reader;
git_buf contents = GIT_BUF_INIT;
int error;
if (depth >= MAX_INCLUDE_DEPTH) {
......@@ -1122,22 +1123,20 @@ static int config_read(
return -1;
}
git_buf_init(&reader.buffer, 0);
if ((error = git_futils_readbuffer(&reader.buffer, file->path)) < 0)
if ((error = git_futils_readbuffer(&contents, file->path)) < 0)
goto out;
if ((error = git_hash_buf(&file->checksum, reader.buffer.ptr, reader.buffer.size)) < 0)
git_parse_ctx_init(&reader.ctx, contents.ptr, contents.size);
if ((error = git_hash_buf(&file->checksum, contents.ptr, contents.size)) < 0)
goto out;
/* Initialize the reading position */
reader.file = file;
reader.line_number = 0;
reader.read_ptr = reader.buffer.ptr;
reader.eof = 0;
git_parse_ctx_init(&reader.ctx, contents.ptr, contents.size);
/* If the file is empty, there's nothing for us to do */
if (*reader.read_ptr == '\0')
if (!reader.ctx.content || *reader.ctx.content == '\0')
goto out;
parse_data.repo = repo;
......@@ -1149,7 +1148,7 @@ static int config_read(
error = git_config_parse(&reader, NULL, read_on_variable, NULL, NULL, &parse_data);
out:
git_buf_free(&reader.buffer);
git_buf_free(&contents);
return error;
}
......@@ -1384,36 +1383,30 @@ static int config_write(diskfile_backend *cfg, const char *orig_key, const char
int result;
char *orig_section, *section, *orig_name, *name, *ldot;
git_filebuf file = GIT_FILEBUF_INIT;
git_buf buf = GIT_BUF_INIT;
git_buf buf = GIT_BUF_INIT, contents = GIT_BUF_INIT;
git_config_parser reader;
struct write_data write_data;
memset(&reader, 0, sizeof(reader));
git_buf_init(&reader.buffer, 0);
reader.file = &cfg->file;
if (cfg->locked) {
result = git_buf_puts(&reader.buffer, git_buf_cstr(&cfg->locked_content));
result = git_buf_puts(&contents, git_buf_cstr(&cfg->locked_content));
} else {
/* Lock the file */
if ((result = git_filebuf_open(
&file, cfg->file.path, GIT_FILEBUF_HASH_CONTENTS, GIT_CONFIG_FILE_MODE)) < 0) {
git_buf_free(&reader.buffer);
git_buf_free(&contents);
return result;
}
/* We need to read in our own config file */
result = git_futils_readbuffer(&reader.buffer, cfg->file.path);
result = git_futils_readbuffer(&contents, cfg->file.path);
}
/* Initialise the reading position */
if (result == GIT_ENOTFOUND) {
reader.read_ptr = NULL;
reader.eof = 1;
git_buf_clear(&reader.buffer);
} else if (result == 0) {
reader.read_ptr = reader.buffer.ptr;
reader.eof = 0;
if (result == 0 || result == GIT_ENOTFOUND) {
git_parse_ctx_init(&reader.ctx, contents.ptr, contents.size);
} else {
git_filebuf_cleanup(&file);
return -1; /* OS error when reading the file */
......@@ -1467,6 +1460,7 @@ static int config_write(diskfile_backend *cfg, const char *orig_key, const char
done:
git_buf_free(&buf);
git_buf_free(&reader.buffer);
git_buf_free(&contents);
git_parse_ctx_clear(&reader.ctx);
return result;
}
......@@ -13,154 +13,10 @@
static void set_parse_error(git_config_parser *reader, int col, const char *error_str)
{
giterr_set(GITERR_CONFIG, "failed to parse config file: %s (in %s:%d, column %d)",
error_str, reader->file->path, reader->line_number, col);
giterr_set(GITERR_CONFIG, "failed to parse config file: %s (in %s:%"PRIuZ", column %d)",
error_str, reader->file->path, reader->ctx.line_num, col);
}
static int reader_getchar_raw(git_config_parser *reader)
{
int c;
c = *reader->read_ptr++;
/*
Win 32 line breaks: if we find a \r\n sequence,
return only the \n as a newline
*/
if (c == '\r' && *reader->read_ptr == '\n') {
reader->read_ptr++;
c = '\n';
}
if (c == '\n')
reader->line_number++;
if (c == 0) {
reader->eof = 1;
c = '\0';
}
return c;
}
#define SKIP_WHITESPACE (1 << 1)
#define SKIP_COMMENTS (1 << 2)
static int reader_getchar(git_config_parser *reader, int flags)
{
const int skip_whitespace = (flags & SKIP_WHITESPACE);
const int skip_comments = (flags & SKIP_COMMENTS);
int c;
assert(reader->read_ptr);
do {
c = reader_getchar_raw(reader);
} while (c != '\n' && c != '\0' && skip_whitespace && git__isspace(c));
if (skip_comments && (c == '#' || c == ';')) {
do {
c = reader_getchar_raw(reader);
} while (c != '\n' && c != '\0');
}
return c;
}
/*
* Read the next char, but don't move the reading pointer.
*/
static int reader_peek(git_config_parser *reader, int flags)
{
void *old_read_ptr;
int old_lineno, old_eof;
int ret;
assert(reader->read_ptr);
old_read_ptr = reader->read_ptr;
old_lineno = reader->line_number;
old_eof = reader->eof;
ret = reader_getchar(reader, flags);
reader->read_ptr = old_read_ptr;
reader->line_number = old_lineno;
reader->eof = old_eof;
return ret;
}
/*
* Read and consume a line, returning it in newly-allocated memory.
*/
static char *reader_readline(git_config_parser *reader, bool skip_whitespace)
{
char *line = NULL;
char *line_src, *line_end;
size_t line_len, alloc_len;
line_src = reader->read_ptr;
if (skip_whitespace) {
/* Skip empty empty lines */
while (git__isspace(*line_src))
++line_src;
}
line_end = strchr(line_src, '\n');
/* no newline at EOF */
if (line_end == NULL)
line_end = strchr(line_src, 0);
line_len = line_end - line_src;
if (GIT_ADD_SIZET_OVERFLOW(&alloc_len, line_len, 1) ||
(line = git__malloc(alloc_len)) == NULL) {
return NULL;
}
memcpy(line, line_src, line_len);
do line[line_len] = '\0';
while (line_len-- > 0 && git__isspace(line[line_len]));
if (*line_end == '\n')
line_end++;
if (*line_end == '\0')
reader->eof = 1;
reader->line_number++;
reader->read_ptr = line_end;
return line;
}
/*
* Consume a line, without storing it anywhere
*/
static void reader_consume_line(git_config_parser *reader)
{
char *line_start, *line_end;
line_start = reader->read_ptr;
line_end = strchr(line_start, '\n');
/* No newline at EOF */
if(line_end == NULL){
line_end = strchr(line_start, '\0');
}
if (*line_end == '\n')
line_end++;
if (*line_end == '\0')
reader->eof = 1;
reader->line_number++;
reader->read_ptr = line_end;
}
GIT_INLINE(int) config_keychar(int c)
{
......@@ -295,7 +151,8 @@ static int parse_section_header(git_config_parser *reader, char **section_out)
char *line;
size_t line_len;
line = reader_readline(reader, true);
git_parse_advance_ws(&reader->ctx);
line = git__strndup(reader->ctx.line, reader->ctx.line_len);
if (line == NULL)
return -1;
......@@ -356,14 +213,14 @@ fail_parse:
return -1;
}
static int skip_bom(git_config_parser *reader)
static int skip_bom(git_parse_ctx *parser)
{
git_buf buf = GIT_BUF_INIT_CONST(parser->content, parser->content_len);
git_bom_t bom;
int bom_offset = git_buf_text_detect_bom(&bom,
&reader->buffer, reader->read_ptr - reader->buffer.ptr);
int bom_offset = git_buf_text_detect_bom(&bom, &buf, parser->content_len);
if (bom == GIT_BOM_UTF8)
reader->read_ptr += bom_offset;
git_parse_advance_chars(parser, bom_offset);
/* TODO: reference implementation is pretty stupid with BoM */
......@@ -463,7 +320,8 @@ static int parse_multiline_variable(git_config_parser *reader, git_buf *value, i
bool multiline;
/* Check that the next line exists */
line = reader_readline(reader, false);
git_parse_advance_line(&reader->ctx);
line = git__strndup(reader->ctx.line, reader->ctx.line_len);
if (line == NULL)
return -1;
......@@ -550,7 +408,8 @@ static int parse_variable(git_config_parser *reader, char **var_name, char **var
int quote_count;
bool multiline;
line = reader_readline(reader, true);
git_parse_advance_ws(&reader->ctx);
line = git__strndup(reader->ctx.line, reader->ctx.line_len);
if (line == NULL)
return -1;
......@@ -603,56 +462,58 @@ int git_config_parse(
git_config_parser_eof_cb on_eof,
void *data)
{
char *current_section = NULL, *var_name, *var_value, *line_start;
char c;
size_t line_len;
git_parse_ctx *ctx;
char *current_section = NULL, *var_name, *var_value;
int result = 0;
skip_bom(parser);
ctx = &parser->ctx;
while (result == 0 && !parser->eof) {
line_start = parser->read_ptr;
skip_bom(ctx);
c = reader_peek(parser, SKIP_WHITESPACE);
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;
switch (c) {
case '\0': /* EOF when peeking, set EOF in the parser to exit the loop */
parser->eof = 1;
break;
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) {
line_len = parser->read_ptr - line_start;
result = on_section(parser, current_section, line_start, line_len, data);
}
break;
case '\n': /* comment or whitespace-only */
case ' ':
case '\t':
case ';':
case '#':
reader_consume_line(parser);
if (on_comment) {
line_len = parser->read_ptr - line_start;
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) {
line_len = parser->read_ptr - line_start;
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;
}
......@@ -8,6 +8,7 @@
#include "common.h"
#include "array.h"
#include "oid.h"
#include "parse.h"
static const char *git_config_escapes = "ntb\"\\";
static const char *git_config_escaped = "\n\t\b\"\\";
......@@ -20,10 +21,7 @@ typedef struct config_file {
typedef struct {
struct config_file *file;
git_buf buffer;
char *read_ptr;
int line_number;
int eof;
git_parse_ctx ctx;
} git_config_parser;
typedef int (*git_config_parser_section_cb)(
......
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