From 76749dfbbd07122d9a920fbcf59df6a122ade085 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Fri, 21 Jun 2019 12:33:31 +0200 Subject: config_parse: rename `data` parameter to `payload` for clarity By convention, parameters that get passed to callbacks are usually named `payload` in our codebase. Rename the `data` parameters in the configuration parser callbacks to `payload` to avoid confusion. --- src/config_parse.c | 10 +++++----- src/config_parse.h | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/config_parse.c b/src/config_parse.c index 46dce4038..6354f526b 100644 --- a/src/config_parse.c +++ b/src/config_parse.c @@ -482,7 +482,7 @@ int git_config_parse( git_config_parser_variable_cb on_variable, git_config_parser_comment_cb on_comment, git_config_parser_eof_cb on_eof, - void *data) + void *payload) { git_parse_ctx *ctx; char *current_section = NULL, *var_name = NULL, *var_value = NULL; @@ -522,7 +522,7 @@ int git_config_parse( git_parse_advance_chars(ctx, result); if (on_section) - result = on_section(parser, current_section, line_start, line_len, data); + result = on_section(parser, current_section, line_start, line_len, payload); /* * After we've parsed the section header we may not be * done with the line. If there's still data in there, @@ -542,13 +542,13 @@ int git_config_parse( case ';': case '#': if (on_comment) { - result = on_comment(parser, line_start, line_len, data); + result = on_comment(parser, line_start, line_len, payload); } 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); + result = on_variable(parser, current_section, var_name, var_value, line_start, line_len, payload); git__free(var_name); git__free(var_value); } @@ -561,7 +561,7 @@ int git_config_parse( } if (on_eof) - result = on_eof(parser, current_section, data); + result = on_eof(parser, current_section, payload); out: git__free(current_section); diff --git a/src/config_parse.h b/src/config_parse.h index 4db31bd1f..3eef7d3cd 100644 --- a/src/config_parse.h +++ b/src/config_parse.h @@ -34,7 +34,7 @@ typedef int (*git_config_parser_section_cb)( const char *current_section, const char *line, size_t line_len, - void *data); + void *payload); typedef int (*git_config_parser_variable_cb)( git_config_parser *parser, @@ -43,18 +43,18 @@ typedef int (*git_config_parser_variable_cb)( const char *var_value, const char *line, size_t line_len, - void *data); + void *payload); typedef int (*git_config_parser_comment_cb)( git_config_parser *parser, const char *line, size_t line_len, - void *data); + void *payload); typedef int (*git_config_parser_eof_cb)( git_config_parser *parser, const char *current_section, - void *data); + void *payload); int git_config_parse( git_config_parser *parser, @@ -62,6 +62,6 @@ int git_config_parse( git_config_parser_variable_cb on_variable, git_config_parser_comment_cb on_comment, git_config_parser_eof_cb on_eof, - void *data); + void *payload); #endif -- cgit v1.2.1 From 54d350e05367a88874c291c6aa2c1801c5062031 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Fri, 21 Jun 2019 12:53:43 +0200 Subject: config_file: embed file in diskfile parse data The config file code needs to keep track of the actual `git_config_file` structure, as it not only contains the path of the current configuration file, but it also keeps tracks of all includes of that file. Right now, we keep track of that structure via the `git_config_parser`, but as that's supposed to be a backend generic implementation of configuration parsing it's a layering violation to have it in there. Switch over the config file backend to use its own config file structure that's embedded in the backend parse data. This allows us to switch over the generic config parser to avoid using the `git_config_file` structure. --- src/config_file.c | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/config_file.c b/src/config_file.c index 38653274f..8cadeba46 100644 --- a/src/config_file.c +++ b/src/config_file.c @@ -55,7 +55,7 @@ typedef struct { typedef struct { const git_repository *repo; - const char *file_path; + git_config_file *file; git_config_entries *entries; git_config_level_t level; unsigned int depth; @@ -677,8 +677,7 @@ static char *escape_value(const char *ptr) return git_buf_detach(&buf); } -static int parse_include(git_config_parser *reader, - diskfile_parse_state *parse_data, const char *file) +static int parse_include(diskfile_parse_state *parse_data, const char *file) { git_config_file *include; git_buf path = GIT_BUF_INIT; @@ -688,7 +687,7 @@ static int parse_include(git_config_parser *reader, if (!file) return 0; - if ((result = git_path_dirname_r(&path, reader->file->path)) < 0) + if ((result = git_path_dirname_r(&path, parse_data->file->path)) < 0) return result; dir = git_buf_detach(&path); @@ -698,7 +697,7 @@ static int parse_include(git_config_parser *reader, if (result < 0) return result; - include = git_array_alloc(reader->file->includes); + include = git_array_alloc(parse_data->file->includes); GIT_ERROR_CHECK_ALLOC(include); memset(include, 0, sizeof(*include)); git_array_init(include->includes); @@ -783,8 +782,7 @@ static const struct { { "gitdir/i:", conditional_match_gitdir_i } }; -static int parse_conditional_include(git_config_parser *reader, - diskfile_parse_state *parse_data, const char *section, const char *file) +static int parse_conditional_include(diskfile_parse_state *parse_data, const char *section, const char *file) { char *condition; size_t i; @@ -802,12 +800,12 @@ static int parse_conditional_include(git_config_parser *reader, if ((error = conditions[i].matches(&matches, parse_data->repo, - parse_data->file_path, + parse_data->file->path, condition + strlen(conditions[i].prefix))) < 0) break; if (matches) - error = parse_include(reader, parse_data, file); + error = parse_include(parse_data, file); break; } @@ -831,6 +829,7 @@ static int read_on_variable( const char *c; int result = 0; + GIT_UNUSED(reader); GIT_UNUSED(line); GIT_UNUSED(line_len); @@ -863,11 +862,10 @@ static int read_on_variable( /* Add or append the new config option */ if (!git__strcmp(entry->name, "include.path")) - result = parse_include(reader, parse_data, entry->value); + result = parse_include(parse_data, entry->value); else if (!git__prefixcmp(entry->name, "includeif.") && !git__suffixcmp(entry->name, ".path")) - result = parse_conditional_include(reader, parse_data, - entry->name, entry->value); + result = parse_conditional_include(parse_data, entry->name, entry->value); return result; } @@ -901,7 +899,7 @@ static int config_read_buffer( } parse_data.repo = repo; - parse_data.file_path = file->path; + parse_data.file = file; parse_data.entries = entries; parse_data.level = level; parse_data.depth = depth; -- cgit v1.2.1 From 6e6da75fdb3c5d53c8df94d551aa9e6b9f4d8957 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Thu, 11 Jul 2019 11:00:05 +0200 Subject: config_parse: remove use of `git_config_file` The config parser code needs to keep track of the current parsed file's name so that we are able to provide proper error messages to the user. Right now, we do that by storing a `git_config_file` in the parser structure, but as that is a specific backend and the parser aims to be generic, it is a layering violation. Switch over to use a simple string to fix that. --- src/config_file.c | 4 ++-- src/config_mem.c | 2 +- src/config_parse.c | 6 ++---- src/config_parse.h | 2 +- 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/config_file.c b/src/config_file.c index 8cadeba46..89da82967 100644 --- a/src/config_file.c +++ b/src/config_file.c @@ -889,7 +889,7 @@ static int config_read_buffer( } /* Initialize the reading position */ - reader.file = file; + reader.path = file->path; git_parse_ctx_init(&reader.ctx, buf, buflen); /* If the file is empty, there's nothing for us to do */ @@ -1175,7 +1175,7 @@ static int config_write(diskfile_backend *cfg, const char *orig_key, const char struct write_data write_data; memset(&reader, 0, sizeof(reader)); - reader.file = &cfg->file; + reader.path = cfg->file.path; if (cfg->locked) { result = git_buf_puts(&contents, git_buf_cstr(&cfg->locked_content) == NULL ? "" : git_buf_cstr(&cfg->locked_content)); diff --git a/src/config_mem.c b/src/config_mem.c index c2ecfda12..b563a972e 100644 --- a/src/config_mem.c +++ b/src/config_mem.c @@ -87,7 +87,7 @@ static int config_memory_open(git_config_backend *backend, git_config_level_t le return 0; git_parse_ctx_init(&reader.ctx, memory_backend->cfg.ptr, memory_backend->cfg.size); - reader.file = NULL; + reader.path = "in-memory"; parse_data.entries = memory_backend->entries; parse_data.level = level; diff --git a/src/config_parse.c b/src/config_parse.c index 6354f526b..bda1840fe 100644 --- a/src/config_parse.c +++ b/src/config_parse.c @@ -16,16 +16,14 @@ const char *git_config_escaped = "\n\t\b\"\\"; static void set_parse_error(git_config_parser *reader, int col, const char *error_str) { - const char *file = reader->file ? reader->file->path : "in-memory"; - if (col) git_error_set(GIT_ERROR_CONFIG, "failed to parse config file: %s (in %s:%"PRIuZ", column %d)", - error_str, file, reader->ctx.line_num, col); + error_str, reader->path, reader->ctx.line_num, col); else git_error_set(GIT_ERROR_CONFIG, "failed to parse config file: %s (in %s:%"PRIuZ")", - error_str, file, reader->ctx.line_num); + error_str, reader->path, reader->ctx.line_num); } diff --git a/src/config_parse.h b/src/config_parse.h index 3eef7d3cd..a25da9dd2 100644 --- a/src/config_parse.h +++ b/src/config_parse.h @@ -25,7 +25,7 @@ typedef struct config_file { } git_config_file; typedef struct { - git_config_file *file; + const char *path; git_parse_ctx ctx; } git_config_parser; -- cgit v1.2.1 From 820fa1a3a7ebfcc3c0823ca859b56186dab7477a Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Thu, 11 Jul 2019 11:04:33 +0200 Subject: config_file: internalize `git_config_file` struct With the previous commits, we have finally separated the config parsing logic from the specific configuration file backend. Due to that, we can now move the `git_config_file` structure into the config file backend's implementation so that no other code may accidentally start using it again. Furthermore, we rename the structure to `diskfile` to make it obvious that it is internal, only, and to unify it with naming scheme of the other diskfile structures. --- src/config_file.c | 33 ++++++++++++++++++++------------- src/config_parse.h | 7 ------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/config_file.c b/src/config_file.c index 89da82967..14fc87ffc 100644 --- a/src/config_file.c +++ b/src/config_file.c @@ -26,6 +26,13 @@ /* Max depth for [include] directives */ #define MAX_INCLUDE_DEPTH 10 +typedef struct diskfile { + git_futils_filestamp stamp; + git_oid checksum; + char *path; + git_array_t(struct diskfile) includes; +} diskfile; + typedef struct { git_config_backend parent; /* mutex to coordinate accessing the values */ @@ -44,7 +51,7 @@ typedef struct { git_filebuf locked_buf; git_buf locked_content; - git_config_file file; + diskfile file; } diskfile_backend; typedef struct { @@ -55,14 +62,14 @@ typedef struct { typedef struct { const git_repository *repo; - git_config_file *file; + diskfile *file; git_config_entries *entries; git_config_level_t level; unsigned int depth; } diskfile_parse_state; -static int config_read(git_config_entries *entries, const git_repository *repo, git_config_file *file, git_config_level_t level, int depth); -static int config_read_buffer(git_config_entries *entries, const git_repository *repo, git_config_file *file, git_config_level_t level, int depth, const char *buf, size_t buflen); +static int config_read(git_config_entries *entries, const git_repository *repo, diskfile *file, git_config_level_t level, int depth); +static int config_read_buffer(git_config_entries *entries, const git_repository *repo, diskfile *file, git_config_level_t level, int depth, const char *buf, size_t buflen); static int config_write(diskfile_backend *cfg, const char *orig_key, const char *key, const p_regex_t *preg, const char *value); static char *escape_value(const char *ptr); @@ -96,9 +103,9 @@ static git_config_entries *diskfile_entries_take(diskfile_header *h) return entries; } -static void config_file_clear(git_config_file *file) +static void config_file_clear(diskfile *file) { - git_config_file *include; + diskfile *include; uint32_t i; if (file == NULL) @@ -134,9 +141,9 @@ static int config_open(git_config_backend *cfg, git_config_level_t level, const return res; } -static int config_is_modified(int *modified, git_config_file *file) +static int config_is_modified(int *modified, diskfile *file) { - git_config_file *include; + diskfile *include; git_buf buf = GIT_BUF_INIT; git_oid hash; uint32_t i; @@ -174,9 +181,9 @@ static int config_set_entries(git_config_backend *cfg, git_config_entries *entri { diskfile_backend *b = (diskfile_backend *)cfg; git_config_entries *old = NULL; - git_config_file *include; + diskfile *include; int error; - size_t i; + uint32_t i; if (b->header.parent.readonly) return config_error_readonly(); @@ -679,7 +686,7 @@ static char *escape_value(const char *ptr) static int parse_include(diskfile_parse_state *parse_data, const char *file) { - git_config_file *include; + diskfile *include; git_buf path = GIT_BUF_INIT; char *dir; int result; @@ -873,7 +880,7 @@ static int read_on_variable( static int config_read_buffer( git_config_entries *entries, const git_repository *repo, - git_config_file *file, + diskfile *file, git_config_level_t level, int depth, const char *buf, @@ -913,7 +920,7 @@ out: static int config_read( git_config_entries *entries, const git_repository *repo, - git_config_file *file, + diskfile *file, git_config_level_t level, int depth) { diff --git a/src/config_parse.h b/src/config_parse.h index a25da9dd2..a8657fd4a 100644 --- a/src/config_parse.h +++ b/src/config_parse.h @@ -17,13 +17,6 @@ extern const char *git_config_escapes; extern const char *git_config_escaped; -typedef struct config_file { - git_futils_filestamp stamp; - git_oid checksum; - char *path; - git_array_t(struct config_file) includes; -} git_config_file; - typedef struct { const char *path; git_parse_ctx ctx; -- cgit v1.2.1 From 3215752653885cbd33abb22ae9c356434d9f9dce Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Thu, 11 Jul 2019 11:10:02 +0200 Subject: config_file: refactor error handling in `config_write` Error handling in `config_write` is rather convoluted and does not match our current code style. Refactor it to make it easier to understand. --- src/config_file.c | 64 +++++++++++++++++++++---------------------------------- 1 file changed, 24 insertions(+), 40 deletions(-) diff --git a/src/config_file.c b/src/config_file.c index 14fc87ffc..d2238e811 100644 --- a/src/config_file.c +++ b/src/config_file.c @@ -1174,37 +1174,30 @@ static int write_on_eof( */ static int config_write(diskfile_backend *cfg, const char *orig_key, const char *key, const p_regex_t *preg, const char* value) { - int result; - char *orig_section, *section, *orig_name, *name, *ldot; - git_filebuf file = GIT_FILEBUF_INIT; + char *orig_section = NULL, *section = NULL, *orig_name, *name, *ldot; git_buf buf = GIT_BUF_INIT, contents = GIT_BUF_INIT; - git_config_parser reader; + git_filebuf file = GIT_FILEBUF_INIT; struct write_data write_data; + git_config_parser reader; + int error; - memset(&reader, 0, sizeof(reader)); - reader.path = cfg->file.path; + memset(&write_data, 0, sizeof(write_data)); if (cfg->locked) { - result = git_buf_puts(&contents, git_buf_cstr(&cfg->locked_content) == NULL ? "" : git_buf_cstr(&cfg->locked_content)); + error = git_buf_puts(&contents, git_buf_cstr(&cfg->locked_content) == NULL ? "" : 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_dispose(&contents); - return result; - } + if ((error = git_filebuf_open(&file, cfg->file.path, GIT_FILEBUF_HASH_CONTENTS, + GIT_CONFIG_FILE_MODE)) < 0) + goto done; /* We need to read in our own config file */ - result = git_futils_readbuffer(&contents, cfg->file.path); + error = git_futils_readbuffer(&contents, cfg->file.path); } + if (error < 0 && error != GIT_ENOTFOUND) + goto done; - /* Initialise the reading position */ - 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 */ - } + reader.path = cfg->file.path; + git_parse_ctx_init(&reader.ctx, contents.ptr, contents.size); ldot = strrchr(key, '.'); name = ldot + 1; @@ -1217,30 +1210,16 @@ static int config_write(diskfile_backend *cfg, const char *orig_key, const char GIT_ERROR_CHECK_ALLOC(orig_section); write_data.buf = &buf; - git_buf_init(&write_data.buffered_comment, 0); write_data.orig_section = orig_section; write_data.section = section; - write_data.in_section = 0; - write_data.preg_replaced = 0; write_data.orig_name = orig_name; write_data.name = name; write_data.preg = preg; write_data.value = value; - result = git_config_parse(&reader, - write_on_section, - write_on_variable, - write_on_comment, - write_on_eof, - &write_data); - git__free(section); - git__free(orig_section); - git_buf_dispose(&write_data.buffered_comment); - - if (result < 0) { - git_filebuf_cleanup(&file); + if ((error = git_config_parse(&reader, write_on_section, write_on_variable, + write_on_comment, write_on_eof, &write_data)) < 0) goto done; - } if (cfg->locked) { size_t len = buf.asize; @@ -1250,16 +1229,21 @@ static int config_write(diskfile_backend *cfg, const char *orig_key, const char } else { git_filebuf_write(&file, git_buf_cstr(&buf), git_buf_len(&buf)); - if ((result = git_filebuf_commit(&file)) < 0) + if ((error = git_filebuf_commit(&file)) < 0) goto done; - if ((result = config_refresh_from_buffer(&cfg->header.parent, buf.ptr, buf.size)) < 0) + if ((error = config_refresh_from_buffer(&cfg->header.parent, buf.ptr, buf.size)) < 0) goto done; } done: + git__free(section); + git__free(orig_section); + git_buf_dispose(&write_data.buffered_comment); git_buf_dispose(&buf); git_buf_dispose(&contents); + git_filebuf_cleanup(&file); git_parse_ctx_clear(&reader.ctx); - return result; + + return error; } -- cgit v1.2.1 From dbeadf8a9e9cb66f65b894e4dfd1fb23f9f31d5b Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Thu, 11 Jul 2019 10:56:05 +0200 Subject: config_parse: provide parser init and dispose functions Right now, all configuration file backends are expected to directly mess with the configuration parser's internals in order to set it up. Let's avoid doing that by implementing both a `git_config_parser_init` and `git_config_parser_dispose` function to clearly define the interface between configuration backends and the parser. Ideally, we would make the `git_config_parser` structure definition private to its implementation. But as that would require an additional memory allocation that was not required before we just live with it being visible to others. --- src/config_file.c | 10 +++++----- src/config_mem.c | 18 +++++++++++------- src/config_parse.c | 11 +++++++++++ src/config_parse.h | 5 +++++ src/parse.h | 2 ++ 5 files changed, 34 insertions(+), 12 deletions(-) diff --git a/src/config_file.c b/src/config_file.c index d2238e811..51a3e93e2 100644 --- a/src/config_file.c +++ b/src/config_file.c @@ -1176,9 +1176,9 @@ static int config_write(diskfile_backend *cfg, const char *orig_key, const char { char *orig_section = NULL, *section = NULL, *orig_name, *name, *ldot; git_buf buf = GIT_BUF_INIT, contents = GIT_BUF_INIT; + git_config_parser parser = GIT_CONFIG_PARSER_INIT; git_filebuf file = GIT_FILEBUF_INIT; struct write_data write_data; - git_config_parser reader; int error; memset(&write_data, 0, sizeof(write_data)); @@ -1196,8 +1196,8 @@ static int config_write(diskfile_backend *cfg, const char *orig_key, const char if (error < 0 && error != GIT_ENOTFOUND) goto done; - reader.path = cfg->file.path; - git_parse_ctx_init(&reader.ctx, contents.ptr, contents.size); + if ((git_config_parser_init(&parser, cfg->file.path, contents.ptr, contents.size)) < 0) + goto done; ldot = strrchr(key, '.'); name = ldot + 1; @@ -1217,7 +1217,7 @@ static int config_write(diskfile_backend *cfg, const char *orig_key, const char write_data.preg = preg; write_data.value = value; - if ((error = git_config_parse(&reader, write_on_section, write_on_variable, + if ((error = git_config_parse(&parser, write_on_section, write_on_variable, write_on_comment, write_on_eof, &write_data)) < 0) goto done; @@ -1243,7 +1243,7 @@ done: git_buf_dispose(&buf); git_buf_dispose(&contents); git_filebuf_cleanup(&file); - git_parse_ctx_clear(&reader.ctx); + git_config_parser_dispose(&parser); return error; } diff --git a/src/config_mem.c b/src/config_mem.c index b563a972e..e4006db32 100644 --- a/src/config_mem.c +++ b/src/config_mem.c @@ -78,20 +78,24 @@ static int read_variable_cb( static int config_memory_open(git_config_backend *backend, git_config_level_t level, const git_repository *repo) { config_memory_backend *memory_backend = (config_memory_backend *) backend; + git_config_parser parser = GIT_PARSE_CTX_INIT; config_memory_parse_data parse_data; - git_config_parser reader; + int error; GIT_UNUSED(repo); - if (memory_backend->cfg.size == 0) - return 0; - - git_parse_ctx_init(&reader.ctx, memory_backend->cfg.ptr, memory_backend->cfg.size); - reader.path = "in-memory"; + if ((error = git_config_parser_init(&parser, "in-memory", memory_backend->cfg.ptr, + memory_backend->cfg.size)) < 0) + goto out; parse_data.entries = memory_backend->entries; parse_data.level = level; - return git_config_parse(&reader, NULL, read_variable_cb, NULL, NULL, &parse_data); + if ((error = git_config_parse(&parser, NULL, read_variable_cb, NULL, NULL, &parse_data)) < 0) + goto out; + +out: + git_config_parser_dispose(&parser); + return error; } static int config_memory_get(git_config_backend *backend, const char *key, git_config_entry **out) diff --git a/src/config_parse.c b/src/config_parse.c index bda1840fe..48ad1164f 100644 --- a/src/config_parse.c +++ b/src/config_parse.c @@ -474,6 +474,17 @@ out: return error; } +int git_config_parser_init(git_config_parser *out, const char *path, const char *data, size_t datalen) +{ + out->path = path; + return git_parse_ctx_init(&out->ctx, data, datalen); +} + +void git_config_parser_dispose(git_config_parser *parser) +{ + git_parse_ctx_clear(&parser->ctx); +} + int git_config_parse( git_config_parser *parser, git_config_parser_section_cb on_section, diff --git a/src/config_parse.h b/src/config_parse.h index a8657fd4a..0129ee309 100644 --- a/src/config_parse.h +++ b/src/config_parse.h @@ -22,6 +22,8 @@ typedef struct { git_parse_ctx ctx; } git_config_parser; +#define GIT_CONFIG_PARSER_INIT { NULL, GIT_PARSE_CTX_INIT } + typedef int (*git_config_parser_section_cb)( git_config_parser *parser, const char *current_section, @@ -49,6 +51,9 @@ typedef int (*git_config_parser_eof_cb)( const char *current_section, void *payload); +int git_config_parser_init(git_config_parser *out, const char *path, const char *data, size_t datalen); +void git_config_parser_dispose(git_config_parser *parser); + int git_config_parse( git_config_parser *parser, git_config_parser_section_cb on_section, diff --git a/src/parse.h b/src/parse.h index 21dcf9bd1..42a2aff1a 100644 --- a/src/parse.h +++ b/src/parse.h @@ -23,6 +23,8 @@ typedef struct { size_t line_num; } git_parse_ctx; +#define GIT_PARSE_CTX_INIT { 0 } + int git_parse_ctx_init(git_parse_ctx *ctx, const char *content, size_t content_len); void git_parse_ctx_clear(git_parse_ctx *ctx); -- cgit v1.2.1