Skip to content

Commit 9e66590

Browse files
committed
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.
1 parent 1953c68 commit 9e66590

File tree

3 files changed

+48
-195
lines changed

3 files changed

+48
-195
lines changed

src/config_file.c

Lines changed: 16 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1115,29 +1115,28 @@ static int config_read(
11151115
{
11161116
struct parse_data parse_data;
11171117
git_config_parser reader;
1118+
git_buf contents = GIT_BUF_INIT;
11181119
int error;
11191120

11201121
if (depth >= MAX_INCLUDE_DEPTH) {
11211122
giterr_set(GITERR_CONFIG, "maximum config include depth reached");
11221123
return -1;
11231124
}
11241125

1125-
git_buf_init(&reader.buffer, 0);
1126-
1127-
if ((error = git_futils_readbuffer(&reader.buffer, file->path)) < 0)
1126+
if ((error = git_futils_readbuffer(&contents, file->path)) < 0)
11281127
goto out;
11291128

1130-
if ((error = git_hash_buf(&file->checksum, reader.buffer.ptr, reader.buffer.size)) < 0)
1129+
git_parse_ctx_init(&reader.ctx, contents.ptr, contents.size);
1130+
1131+
if ((error = git_hash_buf(&file->checksum, contents.ptr, contents.size)) < 0)
11311132
goto out;
11321133

11331134
/* Initialize the reading position */
11341135
reader.file = file;
1135-
reader.line_number = 0;
1136-
reader.read_ptr = reader.buffer.ptr;
1137-
reader.eof = 0;
1136+
git_parse_ctx_init(&reader.ctx, contents.ptr, contents.size);
11381137

11391138
/* If the file is empty, there's nothing for us to do */
1140-
if (*reader.read_ptr == '\0')
1139+
if (!reader.ctx.content || *reader.ctx.content == '\0')
11411140
goto out;
11421141

11431142
parse_data.repo = repo;
@@ -1149,7 +1148,7 @@ static int config_read(
11491148
error = git_config_parse(&reader, NULL, read_on_variable, NULL, NULL, &parse_data);
11501149

11511150
out:
1152-
git_buf_free(&reader.buffer);
1151+
git_buf_free(&contents);
11531152
return error;
11541153
}
11551154

@@ -1384,36 +1383,30 @@ static int config_write(diskfile_backend *cfg, const char *orig_key, const char
13841383
int result;
13851384
char *orig_section, *section, *orig_name, *name, *ldot;
13861385
git_filebuf file = GIT_FILEBUF_INIT;
1387-
git_buf buf = GIT_BUF_INIT;
1386+
git_buf buf = GIT_BUF_INIT, contents = GIT_BUF_INIT;
13881387
git_config_parser reader;
13891388
struct write_data write_data;
13901389

13911390
memset(&reader, 0, sizeof(reader));
1392-
git_buf_init(&reader.buffer, 0);
13931391
reader.file = &cfg->file;
13941392

13951393
if (cfg->locked) {
1396-
result = git_buf_puts(&reader.buffer, git_buf_cstr(&cfg->locked_content));
1394+
result = git_buf_puts(&contents, git_buf_cstr(&cfg->locked_content));
13971395
} else {
13981396
/* Lock the file */
13991397
if ((result = git_filebuf_open(
14001398
&file, cfg->file.path, GIT_FILEBUF_HASH_CONTENTS, GIT_CONFIG_FILE_MODE)) < 0) {
1401-
git_buf_free(&reader.buffer);
1399+
git_buf_free(&contents);
14021400
return result;
14031401
}
14041402

14051403
/* We need to read in our own config file */
1406-
result = git_futils_readbuffer(&reader.buffer, cfg->file.path);
1404+
result = git_futils_readbuffer(&contents, cfg->file.path);
14071405
}
14081406

14091407
/* Initialise the reading position */
1410-
if (result == GIT_ENOTFOUND) {
1411-
reader.read_ptr = NULL;
1412-
reader.eof = 1;
1413-
git_buf_clear(&reader.buffer);
1414-
} else if (result == 0) {
1415-
reader.read_ptr = reader.buffer.ptr;
1416-
reader.eof = 0;
1408+
if (result == 0 || result == GIT_ENOTFOUND) {
1409+
git_parse_ctx_init(&reader.ctx, contents.ptr, contents.size);
14171410
} else {
14181411
git_filebuf_cleanup(&file);
14191412
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
14671460

14681461
done:
14691462
git_buf_free(&buf);
1470-
git_buf_free(&reader.buffer);
1463+
git_buf_free(&contents);
1464+
git_parse_ctx_clear(&reader.ctx);
14711465
return result;
14721466
}

src/config_parse.c

Lines changed: 30 additions & 169 deletions
Original file line numberDiff line numberDiff line change
@@ -13,154 +13,10 @@
1313

1414
static void set_parse_error(git_config_parser *reader, int col, const char *error_str)
1515
{
16-
giterr_set(GITERR_CONFIG, "failed to parse config file: %s (in %s:%d, column %d)",
17-
error_str, reader->file->path, reader->line_number, col);
16+
giterr_set(GITERR_CONFIG, "failed to parse config file: %s (in %s:%"PRIuZ", column %d)",
17+
error_str, reader->file->path, reader->ctx.line_num, col);
1818
}
1919

20-
static int reader_getchar_raw(git_config_parser *reader)
21-
{
22-
int c;
23-
24-
c = *reader->read_ptr++;
25-
26-
/*
27-
Win 32 line breaks: if we find a \r\n sequence,
28-
return only the \n as a newline
29-
*/
30-
if (c == '\r' && *reader->read_ptr == '\n') {
31-
reader->read_ptr++;
32-
c = '\n';
33-
}
34-
35-
if (c == '\n')
36-
reader->line_number++;
37-
38-
if (c == 0) {
39-
reader->eof = 1;
40-
c = '\0';
41-
}
42-
43-
return c;
44-
}
45-
46-
#define SKIP_WHITESPACE (1 << 1)
47-
#define SKIP_COMMENTS (1 << 2)
48-
49-
static int reader_getchar(git_config_parser *reader, int flags)
50-
{
51-
const int skip_whitespace = (flags & SKIP_WHITESPACE);
52-
const int skip_comments = (flags & SKIP_COMMENTS);
53-
int c;
54-
55-
assert(reader->read_ptr);
56-
57-
do {
58-
c = reader_getchar_raw(reader);
59-
} while (c != '\n' && c != '\0' && skip_whitespace && git__isspace(c));
60-
61-
if (skip_comments && (c == '#' || c == ';')) {
62-
do {
63-
c = reader_getchar_raw(reader);
64-
} while (c != '\n' && c != '\0');
65-
}
66-
67-
return c;
68-
}
69-
70-
/*
71-
* Read the next char, but don't move the reading pointer.
72-
*/
73-
static int reader_peek(git_config_parser *reader, int flags)
74-
{
75-
void *old_read_ptr;
76-
int old_lineno, old_eof;
77-
int ret;
78-
79-
assert(reader->read_ptr);
80-
81-
old_read_ptr = reader->read_ptr;
82-
old_lineno = reader->line_number;
83-
old_eof = reader->eof;
84-
85-
ret = reader_getchar(reader, flags);
86-
87-
reader->read_ptr = old_read_ptr;
88-
reader->line_number = old_lineno;
89-
reader->eof = old_eof;
90-
91-
return ret;
92-
}
93-
94-
/*
95-
* Read and consume a line, returning it in newly-allocated memory.
96-
*/
97-
static char *reader_readline(git_config_parser *reader, bool skip_whitespace)
98-
{
99-
char *line = NULL;
100-
char *line_src, *line_end;
101-
size_t line_len, alloc_len;
102-
103-
line_src = reader->read_ptr;
104-
105-
if (skip_whitespace) {
106-
/* Skip empty empty lines */
107-
while (git__isspace(*line_src))
108-
++line_src;
109-
}
110-
111-
line_end = strchr(line_src, '\n');
112-
113-
/* no newline at EOF */
114-
if (line_end == NULL)
115-
line_end = strchr(line_src, 0);
116-
117-
line_len = line_end - line_src;
118-
119-
if (GIT_ADD_SIZET_OVERFLOW(&alloc_len, line_len, 1) ||
120-
(line = git__malloc(alloc_len)) == NULL) {
121-
return NULL;
122-
}
123-
124-
memcpy(line, line_src, line_len);
125-
126-
do line[line_len] = '\0';
127-
while (line_len-- > 0 && git__isspace(line[line_len]));
128-
129-
if (*line_end == '\n')
130-
line_end++;
131-
132-
if (*line_end == '\0')
133-
reader->eof = 1;
134-
135-
reader->line_number++;
136-
reader->read_ptr = line_end;
137-
138-
return line;
139-
}
140-
141-
/*
142-
* Consume a line, without storing it anywhere
143-
*/
144-
static void reader_consume_line(git_config_parser *reader)
145-
{
146-
char *line_start, *line_end;
147-
148-
line_start = reader->read_ptr;
149-
line_end = strchr(line_start, '\n');
150-
/* No newline at EOF */
151-
if(line_end == NULL){
152-
line_end = strchr(line_start, '\0');
153-
}
154-
155-
if (*line_end == '\n')
156-
line_end++;
157-
158-
if (*line_end == '\0')
159-
reader->eof = 1;
160-
161-
reader->line_number++;
162-
reader->read_ptr = line_end;
163-
}
16420

16521
GIT_INLINE(int) config_keychar(int c)
16622
{
@@ -295,7 +151,8 @@ static int parse_section_header(git_config_parser *reader, char **section_out)
295151
char *line;
296152
size_t line_len;
297153

298-
line = reader_readline(reader, true);
154+
git_parse_advance_ws(&reader->ctx);
155+
line = git__strndup(reader->ctx.line, reader->ctx.line_len);
299156
if (line == NULL)
300157
return -1;
301158

@@ -356,14 +213,14 @@ static int parse_section_header(git_config_parser *reader, char **section_out)
356213
return -1;
357214
}
358215

359-
static int skip_bom(git_config_parser *reader)
216+
static int skip_bom(git_parse_ctx *parser)
360217
{
218+
git_buf buf = GIT_BUF_INIT_CONST(parser->content, parser->content_len);
361219
git_bom_t bom;
362-
int bom_offset = git_buf_text_detect_bom(&bom,
363-
&reader->buffer, reader->read_ptr - reader->buffer.ptr);
220+
int bom_offset = git_buf_text_detect_bom(&bom, &buf, parser->content_len);
364221

365222
if (bom == GIT_BOM_UTF8)
366-
reader->read_ptr += bom_offset;
223+
git_parse_advance_chars(parser, bom_offset);
367224

368225
/* TODO: reference implementation is pretty stupid with BoM */
369226

@@ -463,7 +320,8 @@ static int parse_multiline_variable(git_config_parser *reader, git_buf *value, i
463320
bool multiline;
464321

465322
/* Check that the next line exists */
466-
line = reader_readline(reader, false);
323+
git_parse_advance_line(&reader->ctx);
324+
line = git__strndup(reader->ctx.line, reader->ctx.line_len);
467325
if (line == NULL)
468326
return -1;
469327

@@ -550,7 +408,8 @@ static int parse_variable(git_config_parser *reader, char **var_name, char **var
550408
int quote_count;
551409
bool multiline;
552410

553-
line = reader_readline(reader, true);
411+
git_parse_advance_ws(&reader->ctx);
412+
line = git__strndup(reader->ctx.line, reader->ctx.line_len);
554413
if (line == NULL)
555414
return -1;
556415

@@ -603,56 +462,58 @@ int git_config_parse(
603462
git_config_parser_eof_cb on_eof,
604463
void *data)
605464
{
606-
char *current_section = NULL, *var_name, *var_value, *line_start;
607-
char c;
608-
size_t line_len;
465+
git_parse_ctx *ctx;
466+
char *current_section = NULL, *var_name, *var_value;
609467
int result = 0;
610468

611-
skip_bom(parser);
469+
ctx = &parser->ctx;
612470

613-
while (result == 0 && !parser->eof) {
614-
line_start = parser->read_ptr;
471+
skip_bom(ctx);
615472

616-
c = reader_peek(parser, SKIP_WHITESPACE);
473+
for (; ctx->remain_len > 0; git_parse_advance_line(ctx)) {
474+
const char *line_start = parser->ctx.line;
475+
size_t line_len = parser->ctx.line_len;
476+
char c;
617477

618-
switch (c) {
619-
case '\0': /* EOF when peeking, set EOF in the parser to exit the loop */
620-
parser->eof = 1;
621-
break;
478+
if (git_parse_peek(&c, ctx, GIT_PARSE_PEEK_SKIP_WHITESPACE) < 0 &&
479+
git_parse_peek(&c, ctx, 0) < 0)
480+
continue;
622481

482+
switch (c) {
623483
case '[': /* section header, new section begins */
624484
git__free(current_section);
625485
current_section = NULL;
626486

627487
if ((result = parse_section_header(parser, &current_section)) == 0 && on_section) {
628-
line_len = parser->read_ptr - line_start;
629488
result = on_section(parser, current_section, line_start, line_len, data);
630489
}
631490
break;
632491

633492
case '\n': /* comment or whitespace-only */
493+
case ' ':
494+
case '\t':
634495
case ';':
635496
case '#':
636-
reader_consume_line(parser);
637-
638497
if (on_comment) {
639-
line_len = parser->read_ptr - line_start;
640498
result = on_comment(parser, line_start, line_len, data);
641499
}
642500
break;
643501

644502
default: /* assume variable declaration */
645503
if ((result = parse_variable(parser, &var_name, &var_value)) == 0 && on_variable) {
646-
line_len = parser->read_ptr - line_start;
647504
result = on_variable(parser, current_section, var_name, var_value, line_start, line_len, data);
648505
}
649506
break;
650507
}
508+
509+
if (result < 0)
510+
goto out;
651511
}
652512

653513
if (on_eof)
654514
result = on_eof(parser, current_section, data);
655515

516+
out:
656517
git__free(current_section);
657518
return result;
658519
}

src/config_parse.h

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "common.h"
99
#include "array.h"
1010
#include "oid.h"
11+
#include "parse.h"
1112

1213
static const char *git_config_escapes = "ntb\"\\";
1314
static const char *git_config_escaped = "\n\t\b\"\\";
@@ -20,10 +21,7 @@ typedef struct config_file {
2021

2122
typedef struct {
2223
struct config_file *file;
23-
git_buf buffer;
24-
char *read_ptr;
25-
int line_number;
26-
int eof;
24+
git_parse_ctx ctx;
2725
} git_config_parser;
2826

2927
typedef int (*git_config_parser_section_cb)(

0 commit comments

Comments
 (0)