Skip to content

Commit 8032cc4

Browse files
dschogitster
authored andcommitted
config: introduce an optional event stream while parsing
This extends our config parser so that it can optionally produce an event stream via callback function, where it reports e.g. when a comment was parsed, or a section header, etc. This parser will be used subsequently to handle the scenarios better where removing config entries would make sections empty, or where a new entry could be added to an already-existing, empty section. Signed-off-by: Johannes Schindelin <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent b73bdc3 commit 8032cc4

File tree

2 files changed

+114
-12
lines changed

2 files changed

+114
-12
lines changed

config.c

Lines changed: 89 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -653,7 +653,45 @@ static int get_base_var(struct strbuf *name)
653653
}
654654
}
655655

656-
static int git_parse_source(config_fn_t fn, void *data)
656+
struct parse_event_data {
657+
enum config_event_t previous_type;
658+
size_t previous_offset;
659+
const struct config_options *opts;
660+
};
661+
662+
static int do_event(enum config_event_t type, struct parse_event_data *data)
663+
{
664+
size_t offset;
665+
666+
if (!data->opts || !data->opts->event_fn)
667+
return 0;
668+
669+
if (type == CONFIG_EVENT_WHITESPACE &&
670+
data->previous_type == type)
671+
return 0;
672+
673+
offset = cf->do_ftell(cf);
674+
/*
675+
* At EOF, the parser always "inserts" an extra '\n', therefore
676+
* the end offset of the event is the current file position, otherwise
677+
* we will already have advanced to the next event.
678+
*/
679+
if (type != CONFIG_EVENT_EOF)
680+
offset--;
681+
682+
if (data->previous_type != CONFIG_EVENT_EOF &&
683+
data->opts->event_fn(data->previous_type, data->previous_offset,
684+
offset, data->opts->event_fn_data) < 0)
685+
return -1;
686+
687+
data->previous_type = type;
688+
data->previous_offset = offset;
689+
690+
return 0;
691+
}
692+
693+
static int git_parse_source(config_fn_t fn, void *data,
694+
const struct config_options *opts)
657695
{
658696
int comment = 0;
659697
int baselen = 0;
@@ -664,8 +702,15 @@ static int git_parse_source(config_fn_t fn, void *data)
664702
/* U+FEFF Byte Order Mark in UTF8 */
665703
const char *bomptr = utf8_bom;
666704

705+
/* For the parser event callback */
706+
struct parse_event_data event_data = {
707+
CONFIG_EVENT_EOF, 0, opts
708+
};
709+
667710
for (;;) {
668-
int c = get_next_char();
711+
int c;
712+
713+
c = get_next_char();
669714
if (bomptr && *bomptr) {
670715
/* We are at the file beginning; skip UTF8-encoded BOM
671716
* if present. Sane editors won't put this in on their
@@ -682,18 +727,33 @@ static int git_parse_source(config_fn_t fn, void *data)
682727
}
683728
}
684729
if (c == '\n') {
685-
if (cf->eof)
730+
if (cf->eof) {
731+
if (do_event(CONFIG_EVENT_EOF, &event_data) < 0)
732+
return -1;
686733
return 0;
734+
}
735+
if (do_event(CONFIG_EVENT_WHITESPACE, &event_data) < 0)
736+
return -1;
687737
comment = 0;
688738
continue;
689739
}
690-
if (comment || isspace(c))
740+
if (comment)
691741
continue;
742+
if (isspace(c)) {
743+
if (do_event(CONFIG_EVENT_WHITESPACE, &event_data) < 0)
744+
return -1;
745+
continue;
746+
}
692747
if (c == '#' || c == ';') {
748+
if (do_event(CONFIG_EVENT_COMMENT, &event_data) < 0)
749+
return -1;
693750
comment = 1;
694751
continue;
695752
}
696753
if (c == '[') {
754+
if (do_event(CONFIG_EVENT_SECTION, &event_data) < 0)
755+
return -1;
756+
697757
/* Reset prior to determining a new stem */
698758
strbuf_reset(var);
699759
if (get_base_var(var) < 0 || var->len < 1)
@@ -704,6 +764,10 @@ static int git_parse_source(config_fn_t fn, void *data)
704764
}
705765
if (!isalpha(c))
706766
break;
767+
768+
if (do_event(CONFIG_EVENT_ENTRY, &event_data) < 0)
769+
return -1;
770+
707771
/*
708772
* Truncate the var name back to the section header
709773
* stem prior to grabbing the suffix part of the name
@@ -715,6 +779,9 @@ static int git_parse_source(config_fn_t fn, void *data)
715779
break;
716780
}
717781

782+
if (do_event(CONFIG_EVENT_ERROR, &event_data) < 0)
783+
return -1;
784+
718785
switch (cf->origin_type) {
719786
case CONFIG_ORIGIN_BLOB:
720787
error_msg = xstrfmt(_("bad config line %d in blob %s"),
@@ -1390,7 +1457,8 @@ int git_default_config(const char *var, const char *value, void *dummy)
13901457
* fgetc, ungetc, ftell of top need to be initialized before calling
13911458
* this function.
13921459
*/
1393-
static int do_config_from(struct config_source *top, config_fn_t fn, void *data)
1460+
static int do_config_from(struct config_source *top, config_fn_t fn, void *data,
1461+
const struct config_options *opts)
13941462
{
13951463
int ret;
13961464

@@ -1402,7 +1470,7 @@ static int do_config_from(struct config_source *top, config_fn_t fn, void *data)
14021470
strbuf_init(&top->var, 1024);
14031471
cf = top;
14041472

1405-
ret = git_parse_source(fn, data);
1473+
ret = git_parse_source(fn, data, opts);
14061474

14071475
/* pop config-file parsing state stack */
14081476
strbuf_release(&top->value);
@@ -1415,7 +1483,7 @@ static int do_config_from(struct config_source *top, config_fn_t fn, void *data)
14151483
static int do_config_from_file(config_fn_t fn,
14161484
const enum config_origin_type origin_type,
14171485
const char *name, const char *path, FILE *f,
1418-
void *data)
1486+
void *data, const struct config_options *opts)
14191487
{
14201488
struct config_source top;
14211489

@@ -1428,29 +1496,38 @@ static int do_config_from_file(config_fn_t fn,
14281496
top.do_ungetc = config_file_ungetc;
14291497
top.do_ftell = config_file_ftell;
14301498

1431-
return do_config_from(&top, fn, data);
1499+
return do_config_from(&top, fn, data, opts);
14321500
}
14331501

14341502
static int git_config_from_stdin(config_fn_t fn, void *data)
14351503
{
1436-
return do_config_from_file(fn, CONFIG_ORIGIN_STDIN, "", NULL, stdin, data);
1504+
return do_config_from_file(fn, CONFIG_ORIGIN_STDIN, "", NULL, stdin,
1505+
data, NULL);
14371506
}
14381507

1439-
int git_config_from_file(config_fn_t fn, const char *filename, void *data)
1508+
int git_config_from_file_with_options(config_fn_t fn, const char *filename,
1509+
void *data,
1510+
const struct config_options *opts)
14401511
{
14411512
int ret = -1;
14421513
FILE *f;
14431514

14441515
f = fopen_or_warn(filename, "r");
14451516
if (f) {
14461517
flockfile(f);
1447-
ret = do_config_from_file(fn, CONFIG_ORIGIN_FILE, filename, filename, f, data);
1518+
ret = do_config_from_file(fn, CONFIG_ORIGIN_FILE, filename,
1519+
filename, f, data, opts);
14481520
funlockfile(f);
14491521
fclose(f);
14501522
}
14511523
return ret;
14521524
}
14531525

1526+
int git_config_from_file(config_fn_t fn, const char *filename, void *data)
1527+
{
1528+
return git_config_from_file_with_options(fn, filename, data, NULL);
1529+
}
1530+
14541531
int git_config_from_mem(config_fn_t fn, const enum config_origin_type origin_type,
14551532
const char *name, const char *buf, size_t len, void *data)
14561533
{
@@ -1467,7 +1544,7 @@ int git_config_from_mem(config_fn_t fn, const enum config_origin_type origin_typ
14671544
top.do_ungetc = config_buf_ungetc;
14681545
top.do_ftell = config_buf_ftell;
14691546

1470-
return do_config_from(&top, fn, data);
1547+
return do_config_from(&top, fn, data, NULL);
14711548
}
14721549

14731550
int git_config_from_blob_oid(config_fn_t fn,

config.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,40 @@ enum config_origin_type {
2828
CONFIG_ORIGIN_CMDLINE
2929
};
3030

31+
enum config_event_t {
32+
CONFIG_EVENT_SECTION,
33+
CONFIG_EVENT_ENTRY,
34+
CONFIG_EVENT_WHITESPACE,
35+
CONFIG_EVENT_COMMENT,
36+
CONFIG_EVENT_EOF,
37+
CONFIG_EVENT_ERROR
38+
};
39+
40+
/*
41+
* The parser event function (if not NULL) is called with the event type and
42+
* the begin/end offsets of the parsed elements.
43+
*
44+
* Note: for CONFIG_EVENT_ENTRY (i.e. config variables), the trailing newline
45+
* character is considered part of the element.
46+
*/
47+
typedef int (*config_parser_event_fn_t)(enum config_event_t type,
48+
size_t begin_offset, size_t end_offset,
49+
void *event_fn_data);
50+
3151
struct config_options {
3252
unsigned int respect_includes : 1;
3353
const char *commondir;
3454
const char *git_dir;
55+
config_parser_event_fn_t event_fn;
56+
void *event_fn_data;
3557
};
3658

3759
typedef int (*config_fn_t)(const char *, const char *, void *);
3860
extern int git_default_config(const char *, const char *, void *);
3961
extern int git_config_from_file(config_fn_t fn, const char *, void *);
62+
extern int git_config_from_file_with_options(config_fn_t fn, const char *,
63+
void *,
64+
const struct config_options *);
4065
extern int git_config_from_mem(config_fn_t fn, const enum config_origin_type,
4166
const char *name, const char *buf, size_t len, void *data);
4267
extern int git_config_from_blob_oid(config_fn_t fn, const char *name,

0 commit comments

Comments
 (0)