From patchwork Mon Jul 31 23:46:38 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Glen Choo X-Patchwork-Id: 13335597 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id BBEF8C001DE for ; Mon, 31 Jul 2023 23:50:08 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232014AbjGaXuH (ORCPT ); Mon, 31 Jul 2023 19:50:07 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:54716 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231898AbjGaXuA (ORCPT ); Mon, 31 Jul 2023 19:50:00 -0400 Received: from mail-pl1-x649.google.com (mail-pl1-x649.google.com [IPv6:2607:f8b0:4864:20::649]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id C7FFB1BE6 for ; Mon, 31 Jul 2023 16:49:52 -0700 (PDT) Received: by mail-pl1-x649.google.com with SMTP id d9443c01a7336-1bb83eb84e5so52917025ad.1 for ; Mon, 31 Jul 2023 16:49:52 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20221208; t=1690847392; x=1691452192; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=IltbKIl/Wq5qmvu86I0yaKvu6MaZ/RepGR/92Z6jm6U=; b=t2ZvH4ZxFDBtglHQN15RST8U1thV7Jp8HesKpEqU5qsBWI9ej5n1QfpRz1zBMwOD/x GtlFQGxALJV0lndYFZcWN1/gh8dilnl9OCz3l8owG73dwyC2FoqWpdNezxx1VjkS9rzz EK3uf070NIDhKz5gMrrp1eojuFDX45zte1kIwfJ/Cp1tadPtrotneM7RlCu2xvmhlw73 /MxmMjPR4MEPcS0a5Xr7gfv+g+P/KZOOu2p3KwZCKtVmXvz0c5fCEUXkVuZrwbGDaie4 cJSbnpqLYNNbRz4Pvm5tqm7sSf62fHpjqlMhtaocGKBV65aH9jlkifoDMQrCNJ1r2HtP VeNA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1690847392; x=1691452192; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=IltbKIl/Wq5qmvu86I0yaKvu6MaZ/RepGR/92Z6jm6U=; b=awbP6Kof6EYoYC76qUSWwjRYhj/ArYRxb5z0xZONgVrkWA/cDj817oOHrzEbn93QFD n7PJtUpBivAKW7444tU+nBkbvtZXBHu+JRilEjzkpNtFssZYG3nd2Y5w7MbLwYi8NQyg qXqawYbD/t40L4VIuS59wsk11SF+qsNs7Ui1hSqDX9VpSREN2RrGooeD2reYakdREOxd mLKWAFrgITZO+BHjy+QdJO/6eYgvqo+C9K1GHmA2uehE3lDWe0Hjgi2z06uDMKncUyod Zw2Jma6/KDH7KrADox3JQq4j0CdWZ0qLvDWdC3IKse9u3jlwH+3SWdwlSMU3y4X4bFh8 eylg== X-Gm-Message-State: ABy/qLZcgBPFajvz037PI82ZwEhqH1A0NDEfsjttNnvhSZ5xrzVTCCyc 0Tn0ZilOKt5pC5jMtcLj01eP0Q+5nXIINqwd484cPBIwiEoKiXod4Bck+FZTNu/xhfvA4a0DI72 NJdbu5rDyZ9+/uHpfqmdJo/h+VR/y43s/ZGHl9nCJL9a9T6GARh30pEc0faKt11w= X-Google-Smtp-Source: APBJJlFBcGMO+Oxg3yaw8LClF5hcJdjOEK3pAObxMiPl6QkJ4+eNFFYyz9lQ3QX89eo2k/Lf19tqBsbLKzdIlg== X-Received: from chooglen.c.googlers.com ([fda3:e722:ac3:cc00:24:72f4:c0a8:3a07]) (user=chooglen job=sendgmr) by 2002:a17:902:fa0d:b0:1bb:a13a:c21e with SMTP id la13-20020a170902fa0d00b001bba13ac21emr43087plb.10.1690847391363; Mon, 31 Jul 2023 16:49:51 -0700 (PDT) Date: Mon, 31 Jul 2023 16:46:38 -0700 In-Reply-To: <20230731234910.94149-1-chooglen@google.com> Mime-Version: 1.0 References: <20230731234910.94149-1-chooglen@google.com> X-Mailer: git-send-email 2.41.0.585.gd2178a4bd4-goog Message-ID: <20230731234910.94149-2-chooglen@google.com> Subject: [RFC PATCH v1.5 1/5] config: return positive from git_config_parse_key() From: Glen Choo To: git@vger.kernel.org Cc: Glen Choo , Jonathan Tan , Calvin Wan Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org git_config_parse_key() returns #define-d error codes, but negated. This negation is merely a convenience to other parts of config.c that don't bother inspecting the return value before passing it along. But: a) There's no good reason why those callers couldn't negate the value themselves. b) In other callers, this value eventually gets fed to exit(3), and those callers need to sanitize the negative value (and they sometimes do so lossily, by overriding the return value with CONFIG_INVALID_KEY). c) We want to move that into a separate library, and returning only negative values no longer makes as much sense. Change git_config_parse_key() to return positive values instead, and adjust callers accordingly. Callers that sanitize the negative sign for exit(3) now pass the return value opaquely, fixing a bug where "git config " results in a different exit code depending on whether we are setting or getting config. Callers that wanted to pass along a negative value now negate the return value themselves. Signed-off-by: Glen Choo --- builtin/config.c | 3 +-- config.c | 16 ++++++++-------- config.h | 2 +- submodule-config.c | 4 ++-- t/t1300-config.sh | 16 ++++++++++++++++ 5 files changed, 28 insertions(+), 13 deletions(-) diff --git a/builtin/config.c b/builtin/config.c index 1c75cbc43d..8a2840f0a8 100644 --- a/builtin/config.c +++ b/builtin/config.c @@ -362,8 +362,7 @@ static int get_value(const char *key_, const char *regex_, unsigned flags) goto free_strings; } } else { - if (git_config_parse_key(key_, &key, NULL)) { - ret = CONFIG_INVALID_KEY; + if ((ret = git_config_parse_key(key_, &key, NULL))) { goto free_strings; } } diff --git a/config.c b/config.c index 85c5f35132..ca77ca17a4 100644 --- a/config.c +++ b/config.c @@ -534,8 +534,9 @@ static inline int iskeychar(int c) * Auxiliary function to sanity-check and split the key into the section * identifier and variable name. * - * Returns 0 on success, -1 when there is an invalid character in the key and - * -2 if there is no section name in the key. + * Returns 0 on success, CONFIG_INVALID_KEY when there is an invalid character + * in the key and CONFIG_NO_SECTION_OR_NAME if there is no section name in the + * key. * * store_key - pointer to char* which will hold a copy of the key with * lowercase section and variable name @@ -555,12 +556,12 @@ int git_config_parse_key(const char *key, char **store_key, size_t *baselen_) if (last_dot == NULL || last_dot == key) { error(_("key does not contain a section: %s"), key); - return -CONFIG_NO_SECTION_OR_NAME; + return CONFIG_NO_SECTION_OR_NAME; } if (!last_dot[1]) { error(_("key does not contain variable name: %s"), key); - return -CONFIG_NO_SECTION_OR_NAME; + return CONFIG_NO_SECTION_OR_NAME; } baselen = last_dot - key; @@ -596,7 +597,7 @@ int git_config_parse_key(const char *key, char **store_key, size_t *baselen_) out_free_ret_1: FREE_AND_NULL(*store_key); - return -CONFIG_INVALID_KEY; + return CONFIG_INVALID_KEY; } static int config_parse_pair(const char *key, const char *value, @@ -2346,7 +2347,7 @@ static int configset_find_element(struct config_set *set, const char *key, * `key` may come from the user, so normalize it before using it * for querying entries from the hashmap. */ - ret = git_config_parse_key(key, &normalized_key, NULL); + ret = -git_config_parse_key(key, &normalized_key, NULL); if (ret) return ret; @@ -3334,8 +3335,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename, size_t contents_sz; struct config_store_data store = CONFIG_STORE_INIT; - /* parse-key returns negative; flip the sign to feed exit(3) */ - ret = 0 - git_config_parse_key(key, &store.key, &store.baselen); + ret = git_config_parse_key(key, &store.key, &store.baselen); if (ret) goto out_free; diff --git a/config.h b/config.h index 6332d74904..40966cb682 100644 --- a/config.h +++ b/config.h @@ -23,7 +23,7 @@ struct object_id; -/* git_config_parse_key() returns these negated: */ +/* git_config_parse_key() returns these: */ #define CONFIG_INVALID_KEY 1 #define CONFIG_NO_SECTION_OR_NAME 2 /* git_config_set_gently(), git_config_set_multivar_gently() return the above or these: */ diff --git a/submodule-config.c b/submodule-config.c index b6908e295f..2aafc7f9cb 100644 --- a/submodule-config.c +++ b/submodule-config.c @@ -824,8 +824,8 @@ int print_config_from_gitmodules(struct repository *repo, const char *key) char *store_key; ret = git_config_parse_key(key, &store_key, NULL); - if (ret < 0) - return CONFIG_INVALID_KEY; + if (ret) + return ret; config_from_gitmodules(config_print_callback, repo, store_key); diff --git a/t/t1300-config.sh b/t/t1300-config.sh index 387d336c91..3202b0f884 100755 --- a/t/t1300-config.sh +++ b/t/t1300-config.sh @@ -2590,4 +2590,20 @@ test_expect_success 'includeIf.hasconfig:remote.*.url forbids remote url in such grep "fatal: remote URLs cannot be configured in file directly or indirectly included by includeIf.hasconfig:remote.*.url" err ' +# Exit codes +test_expect_success '--get with bad key' ' + # Also exits with 1 if the value is not found + test_expect_code 1 git config --get "bad.name\n" 2>err && + grep "error: invalid key" err && + test_expect_code 2 git config --get "bad." 2>err && + grep "error: key does not contain variable name" err +' + +test_expect_success 'set with bad key' ' + test_expect_code 1 git config "bad.name\n" var 2>err && + grep "error: invalid key" err && + test_expect_code 2 git config "bad." var 2>err && + grep "error: key does not contain variable name" err +' + test_done From patchwork Mon Jul 31 23:46:39 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Glen Choo X-Patchwork-Id: 13335599 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 0D090C04FE0 for ; Mon, 31 Jul 2023 23:50:14 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231956AbjGaXuN (ORCPT ); Mon, 31 Jul 2023 19:50:13 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:54614 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232038AbjGaXuB (ORCPT ); Mon, 31 Jul 2023 19:50:01 -0400 Received: from mail-yb1-xb49.google.com (mail-yb1-xb49.google.com [IPv6:2607:f8b0:4864:20::b49]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 95C131FDA for ; Mon, 31 Jul 2023 16:49:54 -0700 (PDT) Received: by mail-yb1-xb49.google.com with SMTP id 3f1490d57ef6-d1851c52f3dso5105283276.1 for ; Mon, 31 Jul 2023 16:49:54 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20221208; t=1690847393; x=1691452193; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=pKhUecvx+n0CWYwJAHDbzvTBtPHSbIjmafI92mPc288=; b=D9/8POwDem3famyBRiyzRYYWMqjWt0UHAiwwb1N7qjmRazbZoi/Jv1IC2QxVVvRgci T3+G4NoF0VRWNXd1hFj4z6amXnTwWJQB+toY8rUWerhQTeWLeJWo3PatxjQhFlZws6mB ZgG8IVDY1F5ff20VWLZv7Zy8Wk5MTKp0HPP6mZr4Uzo38CcrWpA2bzV9wJGaOP206KvC BKqjUbsWjOn7IEC+Uc2gLG9rK3Omtcx3qrHPBOVRE41kp8Bf45MbzxARiuiqF1y5IGra iKeLGz8E2WEEecPpSSh2g92GJtgiMnoT71aZIk4P0KIvmWv+AFHgocaGLYf6I00ou0PB 4woQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1690847393; x=1691452193; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=pKhUecvx+n0CWYwJAHDbzvTBtPHSbIjmafI92mPc288=; b=MpXKWxNK3wgFbbtiA4MkMtYmlmTjFsfvZWy/KrXe3Z6BVXq55ZpDNHNCXq8/3T732o ebJcCwbII0lDoRZvaX/MuxCfJvxiAsH9D6U40ERFLHnzPT75i68OWQ3wmJN28P46NdGZ U3Y+dO0B2a5aMKee2SmeGc68OEGYePRguZHtybcawGre6keu0AAiXA6unQmhkuQrPoKA oEoPQsuIwqvWRw3VpQ8uIRwj6JjSweNnoNjouG+VdJpFsSCWWjbM3+IjmlxLzSY1iuBV HPhWVChKGBYF75NLZJGhb0Mh7qSIZf6P9v4ZISC+EmguEeEQ57O241/gN9+kMQR6P6Fp C1GA== X-Gm-Message-State: ABy/qLawl4hWeeg3LG2Fiw/Xg/B6LlIKVbYLRMtk5v6MvuKaC9Ls7xHJ j4PGdV77zIFTYee9J637OAI1HDeRjIx68yAMCFJ40I0Px+wup93XSxQ2j67MBOD2Di21qczZ8w2 Uw8B0k/r9Sw6KuIGeBbIDhuYOEnDajDpqQIYDdVdVaISqUcsoDGqHNb0zGM3iCjs= X-Google-Smtp-Source: APBJJlEEXosT+wCFAFm2LAbz3Gsi0vU2I04X1u0ceYHThsqov4QMI9AMU2ih8UESwWLxQ+OEOxmj7v1v+2Tqdg== X-Received: from chooglen.c.googlers.com ([fda3:e722:ac3:cc00:24:72f4:c0a8:3a07]) (user=chooglen job=sendgmr) by 2002:a05:6902:100f:b0:cf9:3564:33cc with SMTP id w15-20020a056902100f00b00cf9356433ccmr78908ybt.13.1690847393223; Mon, 31 Jul 2023 16:49:53 -0700 (PDT) Date: Mon, 31 Jul 2023 16:46:39 -0700 In-Reply-To: <20230731234910.94149-1-chooglen@google.com> Mime-Version: 1.0 References: <20230731234910.94149-1-chooglen@google.com> X-Mailer: git-send-email 2.41.0.585.gd2178a4bd4-goog Message-ID: <20230731234910.94149-3-chooglen@google.com> Subject: [RFC PATCH v1.5 2/5] config: split out config_parse_options From: Glen Choo To: git@vger.kernel.org Cc: Glen Choo , Jonathan Tan , Calvin Wan Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org "struct config_options" is a disjoint set of options options used by the config parser (e.g. event listners) and options used by config_with_options() (e.g. to handle includes, choose which config files to parse). Split parser-only options into config_parse_options. Signed-off-by: Glen Choo --- bundle-uri.c | 2 +- config.c | 14 +++++++------- config.h | 37 ++++++++++++++++++++----------------- fsck.c | 2 +- 4 files changed, 29 insertions(+), 26 deletions(-) diff --git a/bundle-uri.c b/bundle-uri.c index 4b5c49b93d..f93ca6a486 100644 --- a/bundle-uri.c +++ b/bundle-uri.c @@ -237,7 +237,7 @@ int bundle_uri_parse_config_format(const char *uri, struct bundle_list *list) { int result; - struct config_options opts = { + struct config_parse_options opts = { .error_action = CONFIG_ERROR_ERROR, }; diff --git a/config.c b/config.c index ca77ca17a4..dc6cda03aa 100644 --- a/config.c +++ b/config.c @@ -983,7 +983,7 @@ static int get_base_var(struct config_source *cs, struct strbuf *name) struct parse_event_data { enum config_event_t previous_type; size_t previous_offset; - const struct config_options *opts; + const struct config_parse_options *opts; }; static int do_event(struct config_source *cs, enum config_event_t type, @@ -1031,7 +1031,7 @@ static void kvi_from_source(struct config_source *cs, static int git_parse_source(struct config_source *cs, config_fn_t fn, struct key_value_info *kvi, void *data, - const struct config_options *opts) + const struct config_parse_options *opts) { int comment = 0; size_t baselen = 0; @@ -1968,7 +1968,7 @@ int git_default_config(const char *var, const char *value, */ static int do_config_from(struct config_source *top, config_fn_t fn, void *data, enum config_scope scope, - const struct config_options *opts) + const struct config_parse_options *opts) { struct key_value_info kvi = KVI_INIT; int ret; @@ -1993,7 +1993,7 @@ static int do_config_from_file(config_fn_t fn, const enum config_origin_type origin_type, const char *name, const char *path, FILE *f, void *data, enum config_scope scope, - const struct config_options *opts) + const struct config_parse_options *opts) { struct config_source top = CONFIG_SOURCE_INIT; int ret; @@ -2022,7 +2022,7 @@ static int git_config_from_stdin(config_fn_t fn, void *data, int git_config_from_file_with_options(config_fn_t fn, const char *filename, void *data, enum config_scope scope, - const struct config_options *opts) + const struct config_parse_options *opts) { int ret = -1; FILE *f; @@ -2048,7 +2048,7 @@ int git_config_from_mem(config_fn_t fn, const enum config_origin_type origin_type, const char *name, const char *buf, size_t len, void *data, enum config_scope scope, - const struct config_options *opts) + const struct config_parse_options *opts) { struct config_source top = CONFIG_SOURCE_INIT; @@ -3380,7 +3380,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename, struct stat st; size_t copy_begin, copy_end; int i, new_line = 0; - struct config_options opts; + struct config_parse_options opts; if (!value_pattern) store.value_pattern = NULL; diff --git a/config.h b/config.h index 40966cb682..b13586307d 100644 --- a/config.h +++ b/config.h @@ -85,6 +85,21 @@ typedef int (*config_parser_event_fn_t)(enum config_event_t type, struct config_source *cs, void *event_fn_data); +struct config_parse_options { + enum config_error_action { + CONFIG_ERROR_UNSET = 0, /* use source-specific default */ + CONFIG_ERROR_DIE, /* die() on error */ + CONFIG_ERROR_ERROR, /* error() on error, return -1 */ + CONFIG_ERROR_SILENT, /* return -1 */ + } error_action; + /* + * event_fn and event_fn_data are for internal use only. Handles events + * emitted by the config parser. + */ + config_parser_event_fn_t event_fn; + void *event_fn_data; +}; + struct config_options { unsigned int respect_includes : 1; unsigned int ignore_repo : 1; @@ -92,6 +107,9 @@ struct config_options { unsigned int ignore_cmdline : 1; unsigned int system_gently : 1; + const char *commondir; + const char *git_dir; + struct config_parse_options parse_options; /* * For internal use. Include all includeif.hasremoteurl paths without * checking if the repo has that remote URL, and when doing so, verify @@ -99,21 +117,6 @@ struct config_options { * themselves. */ unsigned int unconditional_remote_url : 1; - - const char *commondir; - const char *git_dir; - /* - * event_fn and event_fn_data are for internal use only. Handles events - * emitted by the config parser. - */ - config_parser_event_fn_t event_fn; - void *event_fn_data; - enum config_error_action { - CONFIG_ERROR_UNSET = 0, /* use source-specific default */ - CONFIG_ERROR_DIE, /* die() on error */ - CONFIG_ERROR_ERROR, /* error() on error, return -1 */ - CONFIG_ERROR_SILENT, /* return -1 */ - } error_action; }; /* Config source metadata for a given config key-value pair */ @@ -178,13 +181,13 @@ int git_config_from_file(config_fn_t fn, const char *, void *); int git_config_from_file_with_options(config_fn_t fn, const char *, void *, enum config_scope, - const struct config_options *); + const struct config_parse_options *); int git_config_from_mem(config_fn_t fn, const enum config_origin_type, const char *name, const char *buf, size_t len, void *data, enum config_scope scope, - const struct config_options *opts); + const struct config_parse_options *opts); int git_config_from_blob_oid(config_fn_t fn, const char *name, struct repository *repo, const struct object_id *oid, void *data, diff --git a/fsck.c b/fsck.c index 3be86616c5..522ee1c18a 100644 --- a/fsck.c +++ b/fsck.c @@ -1219,7 +1219,7 @@ static int fsck_blob(const struct object_id *oid, const char *buf, return 0; if (oidset_contains(&options->gitmodules_found, oid)) { - struct config_options config_opts = { 0 }; + struct config_parse_options config_opts = { 0 }; struct fsck_gitmodules_data data; oidset_insert(&options->gitmodules_done, oid); From patchwork Mon Jul 31 23:46:40 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Glen Choo X-Patchwork-Id: 13335598 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 41644C001DE for ; Mon, 31 Jul 2023 23:50:13 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231978AbjGaXuL (ORCPT ); Mon, 31 Jul 2023 19:50:11 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:54706 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232066AbjGaXuD (ORCPT ); Mon, 31 Jul 2023 19:50:03 -0400 Received: from mail-yw1-x1149.google.com (mail-yw1-x1149.google.com [IPv6:2607:f8b0:4864:20::1149]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 8F3E02103 for ; Mon, 31 Jul 2023 16:49:56 -0700 (PDT) Received: by mail-yw1-x1149.google.com with SMTP id 00721157ae682-56942667393so64655577b3.2 for ; Mon, 31 Jul 2023 16:49:56 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20221208; t=1690847395; x=1691452195; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=oKBykUg19E8qWdIb2KledQLUxNTMY8Qfj0DyUlA8+TY=; b=ZUaTbkYlET6Iz0yk5DYu0HXIqd/lZ/cAIz23BdP3vzYT7CKl5OpL4RSIH5zeeDkpkq +l2P7fEZws9dXaACRE3Mm+pCq4GWxxzrB4QPi47OZ0fymzLCNRPpE1edQdT/8bRKXsoG qxgZn6RWInMV8uGPp9JX8aelJmOMmDW5WH4Cw5rFE6nKKGpNMWgELWt4cUlzPg+Lp4IT SDK9fso9hiUl9QLHlr0NmIEJttP3OVzxXDZAX/cVW4qGI1qn5BTdqKTjD/HhC2mvFABT YGaJRXYVGATqg8jIKeylNjT8TdgLE+MGQCp4eJD3RVlQsMMwMslaNC7raC34rlAQw/7w lc5w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1690847395; x=1691452195; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=oKBykUg19E8qWdIb2KledQLUxNTMY8Qfj0DyUlA8+TY=; b=g52nze64+OplhG4gDxWcCQc7K7RuUbvfeoVciwOuHa6Byv3xVeIeu6xgIpx0Tes24p ieHn+IC9+T9KBp//AKzqvU7cDYexIkUSvQKgJqanl+XWjgWSAR1TYZpyMo0FK53yIJ/F YBl4ouy2q0oY2201lRLeIEVEGMozi4prEaOId+kVY9JweIyF/+A8qYcWCJs7MJWDCmt9 pU94QoypRCN8/orH8asHt6qOhRRjpxqT8oBG+2myyj46wdGsDPb+nng8OXa+l4gN06NV 0vKnFg6jqhXtoiTxpVr11F8ZowhFtkCk1FICK5HBXpLfrsqTGRhNIx9CJfcfmpd7d3h2 Y+0g== X-Gm-Message-State: ABy/qLb7hm/am0WMIDFMCNIvtkAwKdhypia9eW4etv/JcJQxCt2f7W5p 37gGQEe6C+JP3ss0C9BgByGRuPSAae4iQRXUQTChQkmi3HlepBZMdrlNJQoG+n8Pzr898jBBDfA U5+efi0cHx6fCa0osLudBgcJ/8Uvm86bk8+jfv2NQFMPuB/FzLR5cfCRIGZBwGqI= X-Google-Smtp-Source: APBJJlFsMErJtP4B1jXH0KC2oOvu5PXe2wJPIf/aj8libW+hd1lNsuDoyd0VEO1MI6qXJ0YDumJjFSKGk8VRMw== X-Received: from chooglen.c.googlers.com ([fda3:e722:ac3:cc00:24:72f4:c0a8:3a07]) (user=chooglen job=sendgmr) by 2002:a81:ad27:0:b0:581:3899:91bc with SMTP id l39-20020a81ad27000000b00581389991bcmr87842ywh.6.1690847395133; Mon, 31 Jul 2023 16:49:55 -0700 (PDT) Date: Mon, 31 Jul 2023 16:46:40 -0700 In-Reply-To: <20230731234910.94149-1-chooglen@google.com> Mime-Version: 1.0 References: <20230731234910.94149-1-chooglen@google.com> X-Mailer: git-send-email 2.41.0.585.gd2178a4bd4-goog Message-ID: <20230731234910.94149-4-chooglen@google.com> Subject: [RFC PATCH v1.5 3/5] config: report config parse errors using cb From: Glen Choo To: git@vger.kernel.org Cc: Glen Choo , Jonathan Tan , Calvin Wan Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org In a subsequent commit, config parsing will become its own library, and it's likely that the caller will want flexibility in handling errors (instead of being limited to the error handling we have in-tree). Move the Git-specific error handling into a config_parser_event_fn_t that responds to config errors, and make git_parse_source() always return -1 (careful inspection shows that it was always returning -1 already). This makes CONFIG_ERROR_SILENT obsolete since that is equivalent to not specifying an error event listener. Also, remove CONFIG_ERROR_UNSET and the config_source 'default', since all callers are now expected to specify the error handling they want. Signed-off-by: Glen Choo --- builtin/config.c | 4 +- bundle-uri.c | 4 +- config.c | 175 ++++++++++++++++++++++++++------------------- config.h | 20 ++++-- fsck.c | 4 +- submodule-config.c | 9 ++- 6 files changed, 129 insertions(+), 87 deletions(-) diff --git a/builtin/config.c b/builtin/config.c index 8a2840f0a8..de1878b947 100644 --- a/builtin/config.c +++ b/builtin/config.c @@ -42,7 +42,9 @@ static int actions, type; static char *default_value; static int end_nul; static int respect_includes_opt = -1; -static struct config_options config_options; +static struct config_options config_options = { + .parse_options = CP_OPTS_INIT(CONFIG_ERROR_DIE) +}; static int show_origin; static int show_scope; static int fixed_value; diff --git a/bundle-uri.c b/bundle-uri.c index f93ca6a486..856bffdcad 100644 --- a/bundle-uri.c +++ b/bundle-uri.c @@ -237,9 +237,7 @@ int bundle_uri_parse_config_format(const char *uri, struct bundle_list *list) { int result; - struct config_parse_options opts = { - .error_action = CONFIG_ERROR_ERROR, - }; + struct config_parse_options opts = CP_OPTS_INIT(CONFIG_ERROR_ERROR); if (!list->baseURI) { struct strbuf baseURI = STRBUF_INIT; diff --git a/config.c b/config.c index dc6cda03aa..b6d0e16240 100644 --- a/config.c +++ b/config.c @@ -55,7 +55,6 @@ struct config_source { enum config_origin_type origin_type; const char *name; const char *path; - enum config_error_action default_error_action; int linenr; int eof; size_t total_len; @@ -185,13 +184,15 @@ static int handle_path_include(const struct key_value_info *kvi, } if (!access_or_die(path, R_OK, 0)) { + struct config_parse_options config_opts = CP_OPTS_INIT(CONFIG_ERROR_DIE); + if (++inc->depth > MAX_INCLUDE_DEPTH) die(_(include_depth_advice), MAX_INCLUDE_DEPTH, path, !kvi ? "" : kvi->filename ? kvi->filename : "the command line"); ret = git_config_from_file_with_options(git_config_include, path, inc, - kvi->scope, NULL); + kvi->scope, &config_opts); inc->depth--; } cleanup: @@ -339,7 +340,9 @@ static int add_remote_url(const char *var, const char *value, static void populate_remote_urls(struct config_include_data *inc) { - struct config_options opts; + struct config_options opts = { + .parse_options = CP_OPTS_INIT(CONFIG_ERROR_DIE), + }; opts = *inc->opts; opts.unconditional_remote_url = 1; @@ -1029,6 +1032,56 @@ static void kvi_from_source(struct config_source *cs, out->path = cs->path; } +int git_config_err_fn(enum config_event_t type, size_t begin_offset UNUSED, + size_t end_offset UNUSED, struct config_source *cs, + void *data) +{ + char *error_msg = NULL; + int error_return = 0; + enum config_error_action *action = data; + + if (type != CONFIG_EVENT_ERROR) + return 0; + + switch (cs->origin_type) { + case CONFIG_ORIGIN_BLOB: + error_msg = xstrfmt(_("bad config line %d in blob %s"), + cs->linenr, cs->name); + break; + case CONFIG_ORIGIN_FILE: + error_msg = xstrfmt(_("bad config line %d in file %s"), + cs->linenr, cs->name); + break; + case CONFIG_ORIGIN_STDIN: + error_msg = xstrfmt(_("bad config line %d in standard input"), + cs->linenr); + break; + case CONFIG_ORIGIN_SUBMODULE_BLOB: + error_msg = xstrfmt(_("bad config line %d in submodule-blob %s"), + cs->linenr, cs->name); + break; + case CONFIG_ORIGIN_CMDLINE: + error_msg = xstrfmt(_("bad config line %d in command line %s"), + cs->linenr, cs->name); + break; + default: + error_msg = xstrfmt(_("bad config line %d in %s"), + cs->linenr, cs->name); + } + + switch (*action) { + case CONFIG_ERROR_DIE: + die("%s", error_msg); + break; + case CONFIG_ERROR_ERROR: + error_return = error("%s", error_msg); + break; + } + + free(error_msg); + return error_return; +} + static int git_parse_source(struct config_source *cs, config_fn_t fn, struct key_value_info *kvi, void *data, const struct config_parse_options *opts) @@ -1036,8 +1089,6 @@ static int git_parse_source(struct config_source *cs, config_fn_t fn, int comment = 0; size_t baselen = 0; struct strbuf *var = &cs->var; - int error_return = 0; - char *error_msg = NULL; /* U+FEFF Byte Order Mark in UTF8 */ const char *bomptr = utf8_bom; @@ -1119,53 +1170,14 @@ static int git_parse_source(struct config_source *cs, config_fn_t fn, break; } - if (do_event(cs, CONFIG_EVENT_ERROR, &event_data) < 0) - return -1; - - switch (cs->origin_type) { - case CONFIG_ORIGIN_BLOB: - error_msg = xstrfmt(_("bad config line %d in blob %s"), - cs->linenr, cs->name); - break; - case CONFIG_ORIGIN_FILE: - error_msg = xstrfmt(_("bad config line %d in file %s"), - cs->linenr, cs->name); - break; - case CONFIG_ORIGIN_STDIN: - error_msg = xstrfmt(_("bad config line %d in standard input"), - cs->linenr); - break; - case CONFIG_ORIGIN_SUBMODULE_BLOB: - error_msg = xstrfmt(_("bad config line %d in submodule-blob %s"), - cs->linenr, cs->name); - break; - case CONFIG_ORIGIN_CMDLINE: - error_msg = xstrfmt(_("bad config line %d in command line %s"), - cs->linenr, cs->name); - break; - default: - error_msg = xstrfmt(_("bad config line %d in %s"), - cs->linenr, cs->name); - } - - switch (opts && opts->error_action ? - opts->error_action : - cs->default_error_action) { - case CONFIG_ERROR_DIE: - die("%s", error_msg); - break; - case CONFIG_ERROR_ERROR: - error_return = error("%s", error_msg); - break; - case CONFIG_ERROR_SILENT: - error_return = -1; - break; - case CONFIG_ERROR_UNSET: - BUG("config error action unset"); - } - - free(error_msg); - return error_return; + /* + * FIXME for whatever reason, do_event passes the _previous_ event, so + * in order for our callback to receive the error event, we have to call + * do_event twice + */ + do_event(cs, CONFIG_EVENT_ERROR, &event_data); + do_event(cs, CONFIG_EVENT_ERROR, &event_data); + return -1; } static uintmax_t get_unit_factor(const char *end) @@ -2002,7 +2014,6 @@ static int do_config_from_file(config_fn_t fn, top.origin_type = origin_type; top.name = name; top.path = path; - top.default_error_action = CONFIG_ERROR_DIE; top.do_fgetc = config_file_fgetc; top.do_ungetc = config_file_ungetc; top.do_ftell = config_file_ftell; @@ -2016,8 +2027,10 @@ static int do_config_from_file(config_fn_t fn, static int git_config_from_stdin(config_fn_t fn, void *data, enum config_scope scope) { + struct config_parse_options config_opts = CP_OPTS_INIT(CONFIG_ERROR_DIE); + return do_config_from_file(fn, CONFIG_ORIGIN_STDIN, "", NULL, stdin, - data, scope, NULL); + data, scope, &config_opts); } int git_config_from_file_with_options(config_fn_t fn, const char *filename, @@ -2040,8 +2053,10 @@ int git_config_from_file_with_options(config_fn_t fn, const char *filename, int git_config_from_file(config_fn_t fn, const char *filename, void *data) { + struct config_parse_options config_opts = CP_OPTS_INIT(CONFIG_ERROR_DIE); + return git_config_from_file_with_options(fn, filename, data, - CONFIG_SCOPE_UNKNOWN, NULL); + CONFIG_SCOPE_UNKNOWN, &config_opts); } int git_config_from_mem(config_fn_t fn, @@ -2058,7 +2073,6 @@ int git_config_from_mem(config_fn_t fn, top.origin_type = origin_type; top.name = name; top.path = NULL; - top.default_error_action = CONFIG_ERROR_ERROR; top.do_fgetc = config_buf_fgetc; top.do_ungetc = config_buf_ungetc; top.do_ftell = config_buf_ftell; @@ -2077,6 +2091,7 @@ int git_config_from_blob_oid(config_fn_t fn, char *buf; unsigned long size; int ret; + struct config_parse_options config_opts = CP_OPTS_INIT(CONFIG_ERROR_ERROR); buf = repo_read_object_file(repo, oid, &type, &size); if (!buf) @@ -2087,7 +2102,7 @@ int git_config_from_blob_oid(config_fn_t fn, } ret = git_config_from_mem(fn, CONFIG_ORIGIN_BLOB, name, buf, size, - data, scope, NULL); + data, scope, &config_opts); free(buf); return ret; @@ -2188,29 +2203,32 @@ static int do_git_config_sequence(const struct config_options *opts, opts->system_gently ? ACCESS_EACCES_OK : 0)) ret += git_config_from_file_with_options(fn, system_config, data, CONFIG_SCOPE_SYSTEM, - NULL); + &opts->parse_options); git_global_config(&user_config, &xdg_config); if (xdg_config && !access_or_die(xdg_config, R_OK, ACCESS_EACCES_OK)) ret += git_config_from_file_with_options(fn, xdg_config, data, - CONFIG_SCOPE_GLOBAL, NULL); + CONFIG_SCOPE_GLOBAL, + &opts->parse_options); if (user_config && !access_or_die(user_config, R_OK, ACCESS_EACCES_OK)) ret += git_config_from_file_with_options(fn, user_config, data, - CONFIG_SCOPE_GLOBAL, NULL); + CONFIG_SCOPE_GLOBAL, + &opts->parse_options); if (!opts->ignore_repo && repo_config && !access_or_die(repo_config, R_OK, 0)) ret += git_config_from_file_with_options(fn, repo_config, data, - CONFIG_SCOPE_LOCAL, NULL); + CONFIG_SCOPE_LOCAL, + &opts->parse_options); if (!opts->ignore_worktree && worktree_config && repo && repo->repository_format_worktree_config && !access_or_die(worktree_config, R_OK, 0)) { ret += git_config_from_file_with_options(fn, worktree_config, data, CONFIG_SCOPE_WORKTREE, - NULL); + &opts->parse_options); } if (!opts->ignore_cmdline && git_config_from_parameters(fn, data) < 0) @@ -2251,7 +2269,7 @@ int config_with_options(config_fn_t fn, void *data, } else if (config_source && config_source->file) { ret = git_config_from_file_with_options(fn, config_source->file, data, config_source->scope, - NULL); + &opts->parse_options); } else if (config_source && config_source->blob) { ret = git_config_from_blob_ref(fn, repo, config_source->blob, data, config_source->scope); @@ -2289,9 +2307,11 @@ static void configset_iter(struct config_set *set, config_fn_t fn, void *data) void read_early_config(config_fn_t cb, void *data) { - struct config_options opts = {0}; struct strbuf commondir = STRBUF_INIT; struct strbuf gitdir = STRBUF_INIT; + struct config_options opts = { + .parse_options = CP_OPTS_INIT(CONFIG_ERROR_DIE), + }; opts.respect_includes = 1; @@ -2323,7 +2343,9 @@ void read_early_config(config_fn_t cb, void *data) */ void read_very_early_config(config_fn_t cb, void *data) { - struct config_options opts = { 0 }; + struct config_options opts = { + .parse_options = CP_OPTS_INIT(CONFIG_ERROR_DIE), + }; opts.respect_includes = 1; opts.ignore_repo = 1; @@ -2614,7 +2636,9 @@ int git_configset_get_pathname(struct config_set *set, const char *key, const ch /* Functions use to read configuration from a repository */ static void repo_read_config(struct repository *repo) { - struct config_options opts = { 0 }; + struct config_options opts = { + .parse_options = CP_OPTS_INIT(CONFIG_ERROR_DIE), + }; opts.respect_includes = 1; opts.commondir = repo->commondir; @@ -2761,12 +2785,14 @@ int repo_config_get_pathname(struct repository *repo, static void read_protected_config(void) { struct config_options opts = { - .respect_includes = 1, - .ignore_repo = 1, - .ignore_worktree = 1, - .system_gently = 1, + .parse_options = CP_OPTS_INIT(CONFIG_ERROR_DIE), }; + opts.respect_includes = 1; + opts.ignore_repo = 1; + opts.ignore_worktree = 1; + opts.system_gently = 1; + git_configset_init(&protected_config); config_with_options(config_set_callback, &protected_config, NULL, NULL, &opts); @@ -2977,6 +3003,7 @@ struct config_store_data { enum config_event_t type; int is_keys_section; } *parsed; + enum config_error_action error_action; unsigned int parsed_nr, parsed_alloc, *seen, seen_nr, seen_alloc; unsigned int key_seen:1, section_seen:1, is_keys_section:1; }; @@ -3044,6 +3071,10 @@ static int store_aux_event(enum config_event_t type, size_t begin, size_t end, store->seen[store->seen_nr] = store->parsed_nr; } } + if (type == CONFIG_EVENT_ERROR) { + return git_config_err_fn(type, begin, end, cs, + &store->error_action); + } store->parsed_nr++; @@ -3380,7 +3411,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename, struct stat st; size_t copy_begin, copy_end; int i, new_line = 0; - struct config_parse_options opts; + struct config_parse_options opts = CP_OPTS_INIT(CONFIG_ERROR_DIE); if (!value_pattern) store.value_pattern = NULL; @@ -3407,8 +3438,8 @@ int git_config_set_multivar_in_file_gently(const char *config_filename, ALLOC_GROW(store.parsed, 1, store.parsed_alloc); store.parsed[0].end = 0; + store.error_action = CONFIG_ERROR_DIE; - memset(&opts, 0, sizeof(opts)); opts.event_fn = store_aux_event; opts.event_fn_data = &store; diff --git a/config.h b/config.h index b13586307d..1aed02cd5d 100644 --- a/config.h +++ b/config.h @@ -86,12 +86,6 @@ typedef int (*config_parser_event_fn_t)(enum config_event_t type, void *event_fn_data); struct config_parse_options { - enum config_error_action { - CONFIG_ERROR_UNSET = 0, /* use source-specific default */ - CONFIG_ERROR_DIE, /* die() on error */ - CONFIG_ERROR_ERROR, /* error() on error, return -1 */ - CONFIG_ERROR_SILENT, /* return -1 */ - } error_action; /* * event_fn and event_fn_data are for internal use only. Handles events * emitted by the config parser. @@ -100,6 +94,11 @@ struct config_parse_options { void *event_fn_data; }; +#define CP_OPTS_INIT(error_action) { \ + .event_fn = git_config_err_fn, \ + .event_fn_data = (enum config_error_action []){(error_action)}, \ +} + struct config_options { unsigned int respect_includes : 1; unsigned int ignore_repo : 1; @@ -119,6 +118,15 @@ struct config_options { unsigned int unconditional_remote_url : 1; }; +enum config_error_action { + CONFIG_ERROR_DIE, /* die() on error */ + CONFIG_ERROR_ERROR, /* error() on error, return -1 */ +}; + +int git_config_err_fn(enum config_event_t type, size_t begin_offset, + size_t end_offset, struct config_source *cs, + void *event_fn_data); + /* Config source metadata for a given config key-value pair */ struct key_value_info { const char *filename; diff --git a/fsck.c b/fsck.c index 522ee1c18a..bc0ca11421 100644 --- a/fsck.c +++ b/fsck.c @@ -1219,7 +1219,6 @@ static int fsck_blob(const struct object_id *oid, const char *buf, return 0; if (oidset_contains(&options->gitmodules_found, oid)) { - struct config_parse_options config_opts = { 0 }; struct fsck_gitmodules_data data; oidset_insert(&options->gitmodules_done, oid); @@ -1238,10 +1237,9 @@ static int fsck_blob(const struct object_id *oid, const char *buf, data.oid = oid; data.options = options; data.ret = 0; - config_opts.error_action = CONFIG_ERROR_SILENT; if (git_config_from_mem(fsck_gitmodules_fn, CONFIG_ORIGIN_BLOB, ".gitmodules", buf, size, &data, - CONFIG_SCOPE_UNKNOWN, &config_opts)) + CONFIG_SCOPE_UNKNOWN, NULL)) data.ret |= report(options, oid, OBJ_BLOB, FSCK_MSG_GITMODULES_PARSE, "could not parse gitmodules blob"); diff --git a/submodule-config.c b/submodule-config.c index 2aafc7f9cb..bcdd6feefa 100644 --- a/submodule-config.c +++ b/submodule-config.c @@ -565,6 +565,8 @@ static const struct submodule *config_from(struct submodule_cache *cache, enum object_type type; const struct submodule *submodule = NULL; struct parse_config_parameter parameter; + struct config_parse_options config_opts = CP_OPTS_INIT(CONFIG_ERROR_ERROR); + /* * If any parameter except the cache is a NULL pointer just @@ -608,7 +610,8 @@ static const struct submodule *config_from(struct submodule_cache *cache, parameter.gitmodules_oid = &oid; parameter.overwrite = 0; git_config_from_mem(parse_config, CONFIG_ORIGIN_SUBMODULE_BLOB, rev.buf, - config, config_size, ¶meter, CONFIG_SCOPE_UNKNOWN, NULL); + config, config_size, ¶meter, + CONFIG_SCOPE_UNKNOWN, &config_opts); strbuf_release(&rev); free(config); @@ -652,7 +655,9 @@ static void config_from_gitmodules(config_fn_t fn, struct repository *repo, void struct git_config_source config_source = { 0, .scope = CONFIG_SCOPE_SUBMODULE }; - const struct config_options opts = { 0 }; + struct config_options opts = { + .parse_options = CP_OPTS_INIT(CONFIG_ERROR_DIE), + }; struct object_id oid; char *file; char *oidstr = NULL; From patchwork Mon Jul 31 23:46:41 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Glen Choo X-Patchwork-Id: 13335600 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 64D35C04FE2 for ; Mon, 31 Jul 2023 23:50:17 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231949AbjGaXuQ (ORCPT ); Mon, 31 Jul 2023 19:50:16 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:54904 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231638AbjGaXuG (ORCPT ); Mon, 31 Jul 2023 19:50:06 -0400 Received: from mail-yw1-x1149.google.com (mail-yw1-x1149.google.com [IPv6:2607:f8b0:4864:20::1149]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 8515D10C7 for ; Mon, 31 Jul 2023 16:49:58 -0700 (PDT) Received: by mail-yw1-x1149.google.com with SMTP id 00721157ae682-583a4015791so45285217b3.1 for ; Mon, 31 Jul 2023 16:49:58 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20221208; t=1690847397; x=1691452197; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=H2oPWbVuymBjneQYbPQLzD5OBULkdkE8A8kMIDx+JPY=; b=CUywR1ZdWISEozDr8/1kfoi+sfhQfwU1qhsAzQmgjJtA9fG8Jm9EZbVqGU2nW3UiVw 3fTxqUwZNv6lb9APVFZGXL3RoG0WzDWECYFyE+Iq4Gr9PCrlhS6etkVlMu9Wm1GumD8V sgz38iIMTeCERo7RgneLBBGmCCq8DnnUQibtsbHWA5L1rEg9n8+3fKqrZiYIPmAFilVF FjDVSjMSNhux2VfNZ+Aqf/zqCCBYXYoS7ToxFuuQfraW0GE1Tdeklk12XqKgJkt6kAct eWTafo6W+ulGEH1i/C7t5/gHjTwo+E3ff0PBqI5hUJMAZHX9OCrtBbHKQ0CwYpqmBnq5 ixYQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1690847397; x=1691452197; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=H2oPWbVuymBjneQYbPQLzD5OBULkdkE8A8kMIDx+JPY=; b=HACecg4iMsSeMBqZhep5llVJ+z2M4TbzMNziZeZsm6aodVuhWxIVUSuz7XCuqnSe9+ esIZw9Tkto4jsUIZEqQHKfi+o+ono0XWFMrCIzuG1fSR17uj+9ikymjZjF/hHS1Fep/1 AQf2/EDY8dhoRX1m4k1B5AhGKd64w8b4g5v7woXGJRI0QN+pZUDOx05PPIz9X+fmHjFm lg5qeqQs6dbDPYjUInugOHlE8G6L97ohjyd4qbDta7miPNRqQ5QpB6wgEPMXhsMo8bpl vwv/f9/2epH2L1mFrc2ukaj+QZcGg1Ae7vTmi46tOkbM7JXO45iNnxueNGPBbW8BUkvR DoPw== X-Gm-Message-State: ABy/qLZ7oYqut5T2ryZS7ZtWrpf+/uxZC8VvfrpKjUQ1nTBWibTQpi3j TwKhMJeGYbtYxpOvUFZdJ9w4PklM5QqhhE4orVy8bekfZFzjo4TVsDqA+8NPwcN0fbmHSes6ygE 8PTcv3EVfTtxyYSV8h7MdZeX4U71Ceh78izPhwm88Y80dCW73Y6FJJttZmQtO+CU= X-Google-Smtp-Source: APBJJlGoSk+g/cvJXMZSFV5OkQ4nvWs14pS0vDGTF49FQCW1m61S8B3f+NCBaXq5eZqejZ+T/rj1h1YQ4s095A== X-Received: from chooglen.c.googlers.com ([fda3:e722:ac3:cc00:24:72f4:c0a8:3a07]) (user=chooglen job=sendgmr) by 2002:a25:abe3:0:b0:d22:4059:b2bd with SMTP id v90-20020a25abe3000000b00d224059b2bdmr68539ybi.1.1690847397025; Mon, 31 Jul 2023 16:49:57 -0700 (PDT) Date: Mon, 31 Jul 2023 16:46:41 -0700 In-Reply-To: <20230731234910.94149-1-chooglen@google.com> Mime-Version: 1.0 References: <20230731234910.94149-1-chooglen@google.com> X-Mailer: git-send-email 2.41.0.585.gd2178a4bd4-goog Message-ID: <20230731234910.94149-5-chooglen@google.com> Subject: [RFC PATCH v1.5 4/5] config.c: accept config_parse_options in git_config_from_stdin From: Glen Choo To: git@vger.kernel.org Cc: Glen Choo , Jonathan Tan , Calvin Wan Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org A later commit will move git_config_from_stdin() to a library, so it will need to accept event listeners. Signed-off-by: Glen Choo --- config.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/config.c b/config.c index b6d0e16240..cf0785a258 100644 --- a/config.c +++ b/config.c @@ -2025,12 +2025,11 @@ static int do_config_from_file(config_fn_t fn, } static int git_config_from_stdin(config_fn_t fn, void *data, - enum config_scope scope) + enum config_scope scope, + const struct config_parse_options *config_opts) { - struct config_parse_options config_opts = CP_OPTS_INIT(CONFIG_ERROR_DIE); - return do_config_from_file(fn, CONFIG_ORIGIN_STDIN, "", NULL, stdin, - data, scope, &config_opts); + data, scope, config_opts); } int git_config_from_file_with_options(config_fn_t fn, const char *filename, @@ -2265,7 +2264,8 @@ int config_with_options(config_fn_t fn, void *data, * regular lookup sequence. */ if (config_source && config_source->use_stdin) { - ret = git_config_from_stdin(fn, data, config_source->scope); + ret = git_config_from_stdin(fn, data, config_source->scope, + &opts->parse_options); } else if (config_source && config_source->file) { ret = git_config_from_file_with_options(fn, config_source->file, data, config_source->scope, From patchwork Mon Jul 31 23:46:42 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Glen Choo X-Patchwork-Id: 13335601 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 3531FC001DE for ; Mon, 31 Jul 2023 23:50:46 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232151AbjGaXuo (ORCPT ); Mon, 31 Jul 2023 19:50:44 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:55204 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232055AbjGaXuV (ORCPT ); Mon, 31 Jul 2023 19:50:21 -0400 Received: from mail-yw1-x114a.google.com (mail-yw1-x114a.google.com [IPv6:2607:f8b0:4864:20::114a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 770621724 for ; Mon, 31 Jul 2023 16:50:00 -0700 (PDT) Received: by mail-yw1-x114a.google.com with SMTP id 00721157ae682-58440eb872aso66889957b3.3 for ; Mon, 31 Jul 2023 16:49:59 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20221208; t=1690847399; x=1691452199; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=kPmmSzcl39is5xs4zbPEzunSbiDzkPoxKtmxe9Wow7o=; b=gtcgSAI0sP/NE+mMoaOnZKgxOFjfnjMTeXzdso4MR+GKb+wtGc3iJN2lay+cDrelwT QY5FhmpgKIeminVZJ4MwEJms4ngHVrfaUOqgWrgAds1uLIJfxLZdh6PaF3FmjAfSXyRf ONJSlnPahwbp/8YPpOQYjH91iyy78fk2J7nD9tpqIIqrcTanqIVKbs6kpWGm65xsnsm1 p/2citGL2OrcIkl0xUIMGDhDPWsvoqKe2T0bOAPVse2oM3T6jVm5VAu1LlY4ntB+aR0o LqRp8A+8GZ7oYXa+zIhM715qSV82QL+/xRiaJG+9F8yQ53SEkVAo/miueC4lqXHbo++m HA7Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1690847399; x=1691452199; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=kPmmSzcl39is5xs4zbPEzunSbiDzkPoxKtmxe9Wow7o=; b=e7u2o7aDS0r26tlH+xZOYBSVxhMsEI+AJEkGvcb/Sylx/iXekXdgvpoc6QtMkpCDCy tpMwbOCvnbupZIM+xyR7xmm9shDqf3a88Ihn1vE1hqhHMKazHAL6meoNHaN19R2ub2Q6 vshci08h9mHjZ0eiJkTuiBCTIeKwYz0VMwVOcfOHAJSFrhF4JepEpvRA0z3cGtTMFJCJ 5FvC0E7ItyuW9Pt+aR9TT/fVHkTgVVGbmcf3r5MvebqOR6JQGPbWN03Cep6JPPtip/BO wthfJXX3rRlThgVRa0h6QzXwPO/w8SYiEQHJvhzcCvnMFCfOnBE+j5c8htbiauhyIud7 0usA== X-Gm-Message-State: ABy/qLYl7NOJDcDmyaiHfB3HdxZB8yXs1OrQJzDPU3yqcfEEXp8HRhMs Hze3LE9ywIXbd867APBzqqkbQgs5Y4FdnjmZh1ALecjJQfESvRbZuOvS1qZgr1DiNEajdB1V0P9 1tY/6daU61DEo2yaydHZheibLOb4A4/ns0cvmf+56MglP6Ily8DEIdhUYyes4rk0= X-Google-Smtp-Source: APBJJlGacg9CxPOW5xdjON52CjdEfnQpHs92cnW55UFluo+lYp0IKjOcq9S82lGW0odAuxvcHkJavxRik1LGqA== X-Received: from chooglen.c.googlers.com ([fda3:e722:ac3:cc00:24:72f4:c0a8:3a07]) (user=chooglen job=sendgmr) by 2002:a81:ad5b:0:b0:584:3d8f:a425 with SMTP id l27-20020a81ad5b000000b005843d8fa425mr97260ywk.10.1690847398723; Mon, 31 Jul 2023 16:49:58 -0700 (PDT) Date: Mon, 31 Jul 2023 16:46:42 -0700 In-Reply-To: <20230731234910.94149-1-chooglen@google.com> Mime-Version: 1.0 References: <20230731234910.94149-1-chooglen@google.com> X-Mailer: git-send-email 2.41.0.585.gd2178a4bd4-goog Message-ID: <20230731234910.94149-6-chooglen@google.com> Subject: [RFC PATCH v1.5 5/5] config-parse: split library out of config.[c|h] From: Glen Choo To: git@vger.kernel.org Cc: Glen Choo , Jonathan Tan , Calvin Wan Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org The config parsing machinery (besides "include" directives) is usable by programs other than Git - it works with works with any file written in Git config syntax (IOW it doesn't rely on 'core' Git features like a repository), and as of the series ending at 6e8e7981eb (config: pass source to config_parser_event_fn_t, 2023-06-28), it no longer relies on global state. Thus, we can and should start turning it into a library other programs can use. Begin this process by splitting the config parsing code out of config.[c|h] and into config-parse.[c|h]. Do not change interfaces or function bodies, but tweak visibility and includes where appropriate, namely: - git_config_from_stdin() is now non-static so that it can be seen by config.c. - "struct config_source" is now defined in the .h file so that it can be seen by config.c. And as a result, config-lib.h needs to "#include strbuf.h". In theory, this makes it possible for in-tree files to decide whether they only need all of the config functionality or only config parsing, and bring in the smallest bit of functionality needed. But for now, there are no in-tree files that can swap "#include config.h" for "#include config-parse.h". E.g. Bundle URIs would only need config parsing to parse bundle lists, but bundle-uri.c uses other config.h functionality like key parsing and reading repo settings. The resulting library is usable, though it is unergonomic to do so, e.g. the caller needs to "#include git-compat-util.h" and other dependencies, and we don't have an easy way of linking in the required objects. This isn't the end state we want for our libraries, but at least we have _some_ library whose usability we can improve in future series. Signed-off-by: Glen Choo --- Makefile | 1 + config-parse.c | 561 +++++++++++++++++++++++++++++++++++++++++++++++ config-parse.h | 155 +++++++++++++ config.c | 583 ------------------------------------------------- config.h | 119 +--------- 5 files changed, 718 insertions(+), 701 deletions(-) create mode 100644 config-parse.c create mode 100644 config-parse.h diff --git a/Makefile b/Makefile index fb541dedc9..67e05bcee5 100644 --- a/Makefile +++ b/Makefile @@ -992,6 +992,7 @@ LIB_OBJS += compat/obstack.o LIB_OBJS += compat/terminal.o LIB_OBJS += compat/zlib-uncompress2.o LIB_OBJS += config.o +LIB_OBJS += config-parse.o LIB_OBJS += connect.o LIB_OBJS += connected.o LIB_OBJS += convert.o diff --git a/config-parse.c b/config-parse.c new file mode 100644 index 0000000000..ff6abc7bd3 --- /dev/null +++ b/config-parse.c @@ -0,0 +1,561 @@ +#include "git-compat-util.h" +#include "strbuf.h" +#include "gettext.h" +#include "hashmap.h" +#include "utf8.h" +#include "config-parse.h" + +static int config_file_fgetc(struct config_source *conf) +{ + return getc_unlocked(conf->u.file); +} + +static int config_file_ungetc(int c, struct config_source *conf) +{ + return ungetc(c, conf->u.file); +} + +static long config_file_ftell(struct config_source *conf) +{ + return ftell(conf->u.file); +} + + +static int config_buf_fgetc(struct config_source *conf) +{ + if (conf->u.buf.pos < conf->u.buf.len) + return conf->u.buf.buf[conf->u.buf.pos++]; + + return EOF; +} + +static int config_buf_ungetc(int c, struct config_source *conf) +{ + if (conf->u.buf.pos > 0) { + conf->u.buf.pos--; + if (conf->u.buf.buf[conf->u.buf.pos] != c) + BUG("config_buf can only ungetc the same character"); + return c; + } + + return EOF; +} + +static long config_buf_ftell(struct config_source *conf) +{ + return conf->u.buf.pos; +} + +static inline int iskeychar(int c) +{ + return isalnum(c) || c == '-'; +} + +/* + * Auxiliary function to sanity-check and split the key into the section + * identifier and variable name. + * + * Returns 0 on success, CONFIG_INVALID_KEY when there is an invalid character + * in the key and CONFIG_NO_SECTION_OR_NAME if there is no section name in the + * key. + * + * store_key - pointer to char* which will hold a copy of the key with + * lowercase section and variable name + * baselen - pointer to size_t which will hold the length of the + * section + subsection part, can be NULL + */ +int git_config_parse_key(const char *key, char **store_key, size_t *baselen_) +{ + size_t i, baselen; + int dot; + const char *last_dot = strrchr(key, '.'); + + /* + * Since "key" actually contains the section name and the real + * key name separated by a dot, we have to know where the dot is. + */ + + if (last_dot == NULL || last_dot == key) { + error(_("key does not contain a section: %s"), key); + return CONFIG_NO_SECTION_OR_NAME; + } + + if (!last_dot[1]) { + error(_("key does not contain variable name: %s"), key); + return CONFIG_NO_SECTION_OR_NAME; + } + + baselen = last_dot - key; + if (baselen_) + *baselen_ = baselen; + + /* + * Validate the key and while at it, lower case it for matching. + */ + *store_key = xmallocz(strlen(key)); + + dot = 0; + for (i = 0; key[i]; i++) { + unsigned char c = key[i]; + if (c == '.') + dot = 1; + /* Leave the extended basename untouched.. */ + if (!dot || i > baselen) { + if (!iskeychar(c) || + (i == baselen + 1 && !isalpha(c))) { + error(_("invalid key: %s"), key); + goto out_free_ret_1; + } + c = tolower(c); + } else if (c == '\n') { + error(_("invalid key (newline): %s"), key); + goto out_free_ret_1; + } + (*store_key)[i] = c; + } + + return 0; + +out_free_ret_1: + FREE_AND_NULL(*store_key); + return CONFIG_INVALID_KEY; +} + +static int get_next_char(struct config_source *cs) +{ + int c = cs->do_fgetc(cs); + + if (c == '\r') { + /* DOS like systems */ + c = cs->do_fgetc(cs); + if (c != '\n') { + if (c != EOF) + cs->do_ungetc(c, cs); + c = '\r'; + } + } + + if (c != EOF && ++cs->total_len > INT_MAX) { + /* + * This is an absurdly long config file; refuse to parse + * further in order to protect downstream code from integer + * overflows. Note that we can't return an error specifically, + * but we can mark EOF and put trash in the return value, + * which will trigger a parse error. + */ + cs->eof = 1; + return 0; + } + + if (c == '\n') + cs->linenr++; + if (c == EOF) { + cs->eof = 1; + cs->linenr++; + c = '\n'; + } + return c; +} + +static char *parse_value(struct config_source *cs) +{ + int quote = 0, comment = 0, space = 0; + + strbuf_reset(&cs->value); + for (;;) { + int c = get_next_char(cs); + if (c == '\n') { + if (quote) { + cs->linenr--; + return NULL; + } + return cs->value.buf; + } + if (comment) + continue; + if (isspace(c) && !quote) { + if (cs->value.len) + space++; + continue; + } + if (!quote) { + if (c == ';' || c == '#') { + comment = 1; + continue; + } + } + for (; space; space--) + strbuf_addch(&cs->value, ' '); + if (c == '\\') { + c = get_next_char(cs); + switch (c) { + case '\n': + continue; + case 't': + c = '\t'; + break; + case 'b': + c = '\b'; + break; + case 'n': + c = '\n'; + break; + /* Some characters escape as themselves */ + case '\\': case '"': + break; + /* Reject unknown escape sequences */ + default: + return NULL; + } + strbuf_addch(&cs->value, c); + continue; + } + if (c == '"') { + quote = 1-quote; + continue; + } + strbuf_addch(&cs->value, c); + } +} + +static int get_value(struct config_source *cs, struct key_value_info *kvi, + config_fn_t fn, void *data, struct strbuf *name) +{ + int c; + char *value; + int ret; + struct config_context ctx = { + .kvi = kvi, + }; + + /* Get the full name */ + for (;;) { + c = get_next_char(cs); + if (cs->eof) + break; + if (!iskeychar(c)) + break; + strbuf_addch(name, tolower(c)); + } + + while (c == ' ' || c == '\t') + c = get_next_char(cs); + + value = NULL; + if (c != '\n') { + if (c != '=') + return -1; + value = parse_value(cs); + if (!value) + return -1; + } + /* + * We already consumed the \n, but we need linenr to point to + * the line we just parsed during the call to fn to get + * accurate line number in error messages. + */ + cs->linenr--; + kvi->linenr = cs->linenr; + ret = fn(name->buf, value, &ctx, data); + if (ret >= 0) + cs->linenr++; + return ret; +} + +static int get_extended_base_var(struct config_source *cs, struct strbuf *name, + int c) +{ + cs->subsection_case_sensitive = 0; + do { + if (c == '\n') + goto error_incomplete_line; + c = get_next_char(cs); + } while (isspace(c)); + + /* We require the format to be '[base "extension"]' */ + if (c != '"') + return -1; + strbuf_addch(name, '.'); + + for (;;) { + int c = get_next_char(cs); + if (c == '\n') + goto error_incomplete_line; + if (c == '"') + break; + if (c == '\\') { + c = get_next_char(cs); + if (c == '\n') + goto error_incomplete_line; + } + strbuf_addch(name, c); + } + + /* Final ']' */ + if (get_next_char(cs) != ']') + return -1; + return 0; +error_incomplete_line: + cs->linenr--; + return -1; +} + +static int get_base_var(struct config_source *cs, struct strbuf *name) +{ + cs->subsection_case_sensitive = 1; + for (;;) { + int c = get_next_char(cs); + if (cs->eof) + return -1; + if (c == ']') + return 0; + if (isspace(c)) + return get_extended_base_var(cs, name, c); + if (!iskeychar(c) && c != '.') + return -1; + strbuf_addch(name, tolower(c)); + } +} + +struct parse_event_data { + enum config_event_t previous_type; + size_t previous_offset; + const struct config_parse_options *opts; +}; + +static int do_event(struct config_source *cs, enum config_event_t type, + struct parse_event_data *data) +{ + size_t offset; + + if (!data->opts || !data->opts->event_fn) + return 0; + + if (type == CONFIG_EVENT_WHITESPACE && + data->previous_type == type) + return 0; + + offset = cs->do_ftell(cs); + /* + * At EOF, the parser always "inserts" an extra '\n', therefore + * the end offset of the event is the current file position, otherwise + * we will already have advanced to the next event. + */ + if (type != CONFIG_EVENT_EOF) + offset--; + + if (data->previous_type != CONFIG_EVENT_EOF && + data->opts->event_fn(data->previous_type, data->previous_offset, + offset, cs, data->opts->event_fn_data) < 0) + return -1; + + data->previous_type = type; + data->previous_offset = offset; + + return 0; +} + +static void kvi_from_source(struct config_source *cs, + enum config_scope scope, + struct key_value_info *out) +{ + out->filename = strintern(cs->name); + out->origin_type = cs->origin_type; + out->linenr = cs->linenr; + out->scope = scope; + out->path = cs->path; +} + +static int git_parse_source(struct config_source *cs, config_fn_t fn, + struct key_value_info *kvi, void *data, + const struct config_parse_options *opts) +{ + int comment = 0; + size_t baselen = 0; + struct strbuf *var = &cs->var; + + /* U+FEFF Byte Order Mark in UTF8 */ + const char *bomptr = utf8_bom; + + /* For the parser event callback */ + struct parse_event_data event_data = { + CONFIG_EVENT_EOF, 0, opts + }; + + for (;;) { + int c; + + c = get_next_char(cs); + if (bomptr && *bomptr) { + /* We are at the file beginning; skip UTF8-encoded BOM + * if present. Sane editors won't put this in on their + * own, but e.g. Windows Notepad will do it happily. */ + if (c == (*bomptr & 0377)) { + bomptr++; + continue; + } else { + /* Do not tolerate partial BOM. */ + if (bomptr != utf8_bom) + break; + /* No BOM at file beginning. Cool. */ + bomptr = NULL; + } + } + if (c == '\n') { + if (cs->eof) { + if (do_event(cs, CONFIG_EVENT_EOF, &event_data) < 0) + return -1; + return 0; + } + if (do_event(cs, CONFIG_EVENT_WHITESPACE, &event_data) < 0) + return -1; + comment = 0; + continue; + } + if (comment) + continue; + if (isspace(c)) { + if (do_event(cs, CONFIG_EVENT_WHITESPACE, &event_data) < 0) + return -1; + continue; + } + if (c == '#' || c == ';') { + if (do_event(cs, CONFIG_EVENT_COMMENT, &event_data) < 0) + return -1; + comment = 1; + continue; + } + if (c == '[') { + if (do_event(cs, CONFIG_EVENT_SECTION, &event_data) < 0) + return -1; + + /* Reset prior to determining a new stem */ + strbuf_reset(var); + if (get_base_var(cs, var) < 0 || var->len < 1) + break; + strbuf_addch(var, '.'); + baselen = var->len; + continue; + } + if (!isalpha(c)) + break; + + if (do_event(cs, CONFIG_EVENT_ENTRY, &event_data) < 0) + return -1; + + /* + * Truncate the var name back to the section header + * stem prior to grabbing the suffix part of the name + * and the value. + */ + strbuf_setlen(var, baselen); + strbuf_addch(var, tolower(c)); + if (get_value(cs, kvi, fn, data, var) < 0) + break; + } + /* + * FIXME for whatever reason, do_event passes the _previous_ event, so + * in order for our callback to receive the error event, we have to call + * do_event twice + */ + do_event(cs, CONFIG_EVENT_ERROR, &event_data); + do_event(cs, CONFIG_EVENT_ERROR, &event_data); + return -1; +} + +/* + * All source specific fields in the union, die_on_error, name and the callbacks + * fgetc, ungetc, ftell of top need to be initialized before calling + * this function. + */ +static int do_config_from(struct config_source *top, config_fn_t fn, + void *data, enum config_scope scope, + const struct config_parse_options *opts) +{ + struct key_value_info kvi = KVI_INIT; + int ret; + + /* push config-file parsing state stack */ + top->linenr = 1; + top->eof = 0; + top->total_len = 0; + strbuf_init(&top->value, 1024); + strbuf_init(&top->var, 1024); + kvi_from_source(top, scope, &kvi); + + ret = git_parse_source(top, fn, &kvi, data, opts); + + strbuf_release(&top->value); + strbuf_release(&top->var); + + return ret; +} + +static int do_config_from_file(config_fn_t fn, + const enum config_origin_type origin_type, + const char *name, const char *path, FILE *f, + void *data, enum config_scope scope, + const struct config_parse_options *opts) +{ + struct config_source top = CONFIG_SOURCE_INIT; + int ret; + + top.u.file = f; + top.origin_type = origin_type; + top.name = name; + top.path = path; + top.do_fgetc = config_file_fgetc; + top.do_ungetc = config_file_ungetc; + top.do_ftell = config_file_ftell; + + flockfile(f); + ret = do_config_from(&top, fn, data, scope, opts); + funlockfile(f); + return ret; +} + +int git_config_from_stdin(config_fn_t fn, void *data, enum config_scope scope, + const struct config_parse_options *config_opts) +{ + return do_config_from_file(fn, CONFIG_ORIGIN_STDIN, "", NULL, stdin, + data, scope, config_opts); +} + +int git_config_from_file_with_options(config_fn_t fn, const char *filename, + void *data, enum config_scope scope, + const struct config_parse_options *opts) +{ + int ret = -1; + FILE *f; + + if (!filename) + BUG("filename cannot be NULL"); + f = fopen_or_warn(filename, "r"); + if (f) { + ret = do_config_from_file(fn, CONFIG_ORIGIN_FILE, filename, + filename, f, data, scope, opts); + fclose(f); + } + return ret; +} + +int git_config_from_mem(config_fn_t fn, + const enum config_origin_type origin_type, + const char *name, const char *buf, size_t len, + void *data, enum config_scope scope, + const struct config_parse_options *opts) +{ + struct config_source top = CONFIG_SOURCE_INIT; + + top.u.buf.buf = buf; + top.u.buf.len = len; + top.u.buf.pos = 0; + top.origin_type = origin_type; + top.name = name; + top.path = NULL; + top.do_fgetc = config_buf_fgetc; + top.do_ungetc = config_buf_ungetc; + top.do_ftell = config_buf_ftell; + + return do_config_from(&top, fn, data, scope, opts); +} diff --git a/config-parse.h b/config-parse.h new file mode 100644 index 0000000000..d5623906ef --- /dev/null +++ b/config-parse.h @@ -0,0 +1,155 @@ +/* + * Low level config parsing. + */ +#ifndef CONFIG_PARSE_H +#define CONFIG_PARSE_H + +#include "strbuf.h" + +/* git_config_parse_key() returns these: */ +#define CONFIG_INVALID_KEY 1 +#define CONFIG_NO_SECTION_OR_NAME 2 + +int git_config_parse_key(const char *, char **, size_t *); + +enum config_scope { + CONFIG_SCOPE_UNKNOWN = 0, + CONFIG_SCOPE_SYSTEM, + CONFIG_SCOPE_GLOBAL, + CONFIG_SCOPE_LOCAL, + CONFIG_SCOPE_WORKTREE, + CONFIG_SCOPE_COMMAND, + CONFIG_SCOPE_SUBMODULE, +}; +const char *config_scope_name(enum config_scope scope); + +enum config_origin_type { + CONFIG_ORIGIN_UNKNOWN = 0, + CONFIG_ORIGIN_BLOB, + CONFIG_ORIGIN_FILE, + CONFIG_ORIGIN_STDIN, + CONFIG_ORIGIN_SUBMODULE_BLOB, + CONFIG_ORIGIN_CMDLINE +}; + +enum config_event_t { + CONFIG_EVENT_SECTION, + CONFIG_EVENT_ENTRY, + CONFIG_EVENT_WHITESPACE, + CONFIG_EVENT_COMMENT, + CONFIG_EVENT_EOF, + CONFIG_EVENT_ERROR +}; + +struct config_source; +/* + * The parser event function (if not NULL) is called with the event type and + * the begin/end offsets of the parsed elements. + * + * Note: for CONFIG_EVENT_ENTRY (i.e. config variables), the trailing newline + * character is considered part of the element. + */ +typedef int (*config_parser_event_fn_t)(enum config_event_t type, + size_t begin_offset, size_t end_offset, + struct config_source *cs, + void *event_fn_data); + +struct config_parse_options { + /* + * event_fn and event_fn_data are for internal use only. Handles events + * emitted by the config parser. + */ + config_parser_event_fn_t event_fn; + void *event_fn_data; +}; + +struct config_source { + struct config_source *prev; + union { + FILE *file; + struct config_buf { + const char *buf; + size_t len; + size_t pos; + } buf; + } u; + enum config_origin_type origin_type; + const char *name; + const char *path; + int linenr; + int eof; + size_t total_len; + struct strbuf value; + struct strbuf var; + unsigned subsection_case_sensitive : 1; + + int (*do_fgetc)(struct config_source *c); + int (*do_ungetc)(int c, struct config_source *conf); + long (*do_ftell)(struct config_source *c); +}; +#define CONFIG_SOURCE_INIT { 0 } + +/* Config source metadata for a given config key-value pair */ +struct key_value_info { + const char *filename; + int linenr; + enum config_origin_type origin_type; + enum config_scope scope; + const char *path; +}; +#define KVI_INIT { \ + .filename = NULL, \ + .linenr = -1, \ + .origin_type = CONFIG_ORIGIN_UNKNOWN, \ + .scope = CONFIG_SCOPE_UNKNOWN, \ + .path = NULL, \ +} + +/* Captures additional information that a config callback can use. */ +struct config_context { + /* Config source metadata for key and value. */ + const struct key_value_info *kvi; +}; +#define CONFIG_CONTEXT_INIT { 0 } + +/** + * A config callback function takes four parameters: + * + * - the name of the parsed variable. This is in canonical "flat" form: the + * section, subsection, and variable segments will be separated by dots, + * and the section and variable segments will be all lowercase. E.g., + * `core.ignorecase`, `diff.SomeType.textconv`. + * + * - the value of the found variable, as a string. If the variable had no + * value specified, the value will be NULL (typically this means it + * should be interpreted as boolean true). + * + * - the 'config context', that is, additional information about the config + * iteration operation provided by the config machinery. For example, this + * includes information about the config source being parsed (e.g. the + * filename). + * + * - a void pointer passed in by the caller of the config API; this can + * contain callback-specific data + * + * A config callback should return 0 for success, or -1 if the variable + * could not be parsed properly. + */ +typedef int (*config_fn_t)(const char *, const char *, + const struct config_context *, void *); + +int git_config_from_file_with_options(config_fn_t fn, const char *, + void *, enum config_scope, + const struct config_parse_options *); + +int git_config_from_mem(config_fn_t fn, + const enum config_origin_type, + const char *name, + const char *buf, size_t len, + void *data, enum config_scope scope, + const struct config_parse_options *opts); + +int git_config_from_stdin(config_fn_t fn, void *data, enum config_scope scope, + const struct config_parse_options *config_opts); + +#endif /* CONFIG_PARSE_H */ diff --git a/config.c b/config.c index cf0785a258..9688e5eb8f 100644 --- a/config.c +++ b/config.c @@ -42,32 +42,6 @@ #include "wrapper.h" #include "write-or-die.h" -struct config_source { - struct config_source *prev; - union { - FILE *file; - struct config_buf { - const char *buf; - size_t len; - size_t pos; - } buf; - } u; - enum config_origin_type origin_type; - const char *name; - const char *path; - int linenr; - int eof; - size_t total_len; - struct strbuf value; - struct strbuf var; - unsigned subsection_case_sensitive : 1; - - int (*do_fgetc)(struct config_source *c); - int (*do_ungetc)(int c, struct config_source *conf); - long (*do_ftell)(struct config_source *c); -}; -#define CONFIG_SOURCE_INIT { 0 } - static int pack_compression_seen; static int zlib_compression_seen; @@ -82,47 +56,6 @@ static int zlib_compression_seen; */ static struct config_set protected_config; -static int config_file_fgetc(struct config_source *conf) -{ - return getc_unlocked(conf->u.file); -} - -static int config_file_ungetc(int c, struct config_source *conf) -{ - return ungetc(c, conf->u.file); -} - -static long config_file_ftell(struct config_source *conf) -{ - return ftell(conf->u.file); -} - - -static int config_buf_fgetc(struct config_source *conf) -{ - if (conf->u.buf.pos < conf->u.buf.len) - return conf->u.buf.buf[conf->u.buf.pos++]; - - return EOF; -} - -static int config_buf_ungetc(int c, struct config_source *conf) -{ - if (conf->u.buf.pos > 0) { - conf->u.buf.pos--; - if (conf->u.buf.buf[conf->u.buf.pos] != c) - BUG("config_buf can only ungetc the same character"); - return c; - } - - return EOF; -} - -static long config_buf_ftell(struct config_source *conf) -{ - return conf->u.buf.pos; -} - struct config_include_data { int depth; config_fn_t fn; @@ -528,81 +461,6 @@ void git_config_push_env(const char *spec) free(key); } -static inline int iskeychar(int c) -{ - return isalnum(c) || c == '-'; -} - -/* - * Auxiliary function to sanity-check and split the key into the section - * identifier and variable name. - * - * Returns 0 on success, CONFIG_INVALID_KEY when there is an invalid character - * in the key and CONFIG_NO_SECTION_OR_NAME if there is no section name in the - * key. - * - * store_key - pointer to char* which will hold a copy of the key with - * lowercase section and variable name - * baselen - pointer to size_t which will hold the length of the - * section + subsection part, can be NULL - */ -int git_config_parse_key(const char *key, char **store_key, size_t *baselen_) -{ - size_t i, baselen; - int dot; - const char *last_dot = strrchr(key, '.'); - - /* - * Since "key" actually contains the section name and the real - * key name separated by a dot, we have to know where the dot is. - */ - - if (last_dot == NULL || last_dot == key) { - error(_("key does not contain a section: %s"), key); - return CONFIG_NO_SECTION_OR_NAME; - } - - if (!last_dot[1]) { - error(_("key does not contain variable name: %s"), key); - return CONFIG_NO_SECTION_OR_NAME; - } - - baselen = last_dot - key; - if (baselen_) - *baselen_ = baselen; - - /* - * Validate the key and while at it, lower case it for matching. - */ - *store_key = xmallocz(strlen(key)); - - dot = 0; - for (i = 0; key[i]; i++) { - unsigned char c = key[i]; - if (c == '.') - dot = 1; - /* Leave the extended basename untouched.. */ - if (!dot || i > baselen) { - if (!iskeychar(c) || - (i == baselen + 1 && !isalpha(c))) { - error(_("invalid key: %s"), key); - goto out_free_ret_1; - } - c = tolower(c); - } else if (c == '\n') { - error(_("invalid key (newline): %s"), key); - goto out_free_ret_1; - } - (*store_key)[i] = c; - } - - return 0; - -out_free_ret_1: - FREE_AND_NULL(*store_key); - return CONFIG_INVALID_KEY; -} - static int config_parse_pair(const char *key, const char *value, struct key_value_info *kvi, config_fn_t fn, void *data) @@ -787,251 +645,6 @@ int git_config_from_parameters(config_fn_t fn, void *data) return ret; } -static int get_next_char(struct config_source *cs) -{ - int c = cs->do_fgetc(cs); - - if (c == '\r') { - /* DOS like systems */ - c = cs->do_fgetc(cs); - if (c != '\n') { - if (c != EOF) - cs->do_ungetc(c, cs); - c = '\r'; - } - } - - if (c != EOF && ++cs->total_len > INT_MAX) { - /* - * This is an absurdly long config file; refuse to parse - * further in order to protect downstream code from integer - * overflows. Note that we can't return an error specifically, - * but we can mark EOF and put trash in the return value, - * which will trigger a parse error. - */ - cs->eof = 1; - return 0; - } - - if (c == '\n') - cs->linenr++; - if (c == EOF) { - cs->eof = 1; - cs->linenr++; - c = '\n'; - } - return c; -} - -static char *parse_value(struct config_source *cs) -{ - int quote = 0, comment = 0, space = 0; - - strbuf_reset(&cs->value); - for (;;) { - int c = get_next_char(cs); - if (c == '\n') { - if (quote) { - cs->linenr--; - return NULL; - } - return cs->value.buf; - } - if (comment) - continue; - if (isspace(c) && !quote) { - if (cs->value.len) - space++; - continue; - } - if (!quote) { - if (c == ';' || c == '#') { - comment = 1; - continue; - } - } - for (; space; space--) - strbuf_addch(&cs->value, ' '); - if (c == '\\') { - c = get_next_char(cs); - switch (c) { - case '\n': - continue; - case 't': - c = '\t'; - break; - case 'b': - c = '\b'; - break; - case 'n': - c = '\n'; - break; - /* Some characters escape as themselves */ - case '\\': case '"': - break; - /* Reject unknown escape sequences */ - default: - return NULL; - } - strbuf_addch(&cs->value, c); - continue; - } - if (c == '"') { - quote = 1-quote; - continue; - } - strbuf_addch(&cs->value, c); - } -} - -static int get_value(struct config_source *cs, struct key_value_info *kvi, - config_fn_t fn, void *data, struct strbuf *name) -{ - int c; - char *value; - int ret; - struct config_context ctx = { - .kvi = kvi, - }; - - /* Get the full name */ - for (;;) { - c = get_next_char(cs); - if (cs->eof) - break; - if (!iskeychar(c)) - break; - strbuf_addch(name, tolower(c)); - } - - while (c == ' ' || c == '\t') - c = get_next_char(cs); - - value = NULL; - if (c != '\n') { - if (c != '=') - return -1; - value = parse_value(cs); - if (!value) - return -1; - } - /* - * We already consumed the \n, but we need linenr to point to - * the line we just parsed during the call to fn to get - * accurate line number in error messages. - */ - cs->linenr--; - kvi->linenr = cs->linenr; - ret = fn(name->buf, value, &ctx, data); - if (ret >= 0) - cs->linenr++; - return ret; -} - -static int get_extended_base_var(struct config_source *cs, struct strbuf *name, - int c) -{ - cs->subsection_case_sensitive = 0; - do { - if (c == '\n') - goto error_incomplete_line; - c = get_next_char(cs); - } while (isspace(c)); - - /* We require the format to be '[base "extension"]' */ - if (c != '"') - return -1; - strbuf_addch(name, '.'); - - for (;;) { - int c = get_next_char(cs); - if (c == '\n') - goto error_incomplete_line; - if (c == '"') - break; - if (c == '\\') { - c = get_next_char(cs); - if (c == '\n') - goto error_incomplete_line; - } - strbuf_addch(name, c); - } - - /* Final ']' */ - if (get_next_char(cs) != ']') - return -1; - return 0; -error_incomplete_line: - cs->linenr--; - return -1; -} - -static int get_base_var(struct config_source *cs, struct strbuf *name) -{ - cs->subsection_case_sensitive = 1; - for (;;) { - int c = get_next_char(cs); - if (cs->eof) - return -1; - if (c == ']') - return 0; - if (isspace(c)) - return get_extended_base_var(cs, name, c); - if (!iskeychar(c) && c != '.') - return -1; - strbuf_addch(name, tolower(c)); - } -} - -struct parse_event_data { - enum config_event_t previous_type; - size_t previous_offset; - const struct config_parse_options *opts; -}; - -static int do_event(struct config_source *cs, enum config_event_t type, - struct parse_event_data *data) -{ - size_t offset; - - if (!data->opts || !data->opts->event_fn) - return 0; - - if (type == CONFIG_EVENT_WHITESPACE && - data->previous_type == type) - return 0; - - offset = cs->do_ftell(cs); - /* - * At EOF, the parser always "inserts" an extra '\n', therefore - * the end offset of the event is the current file position, otherwise - * we will already have advanced to the next event. - */ - if (type != CONFIG_EVENT_EOF) - offset--; - - if (data->previous_type != CONFIG_EVENT_EOF && - data->opts->event_fn(data->previous_type, data->previous_offset, - offset, cs, data->opts->event_fn_data) < 0) - return -1; - - data->previous_type = type; - data->previous_offset = offset; - - return 0; -} - -static void kvi_from_source(struct config_source *cs, - enum config_scope scope, - struct key_value_info *out) -{ - out->filename = strintern(cs->name); - out->origin_type = cs->origin_type; - out->linenr = cs->linenr; - out->scope = scope; - out->path = cs->path; -} - int git_config_err_fn(enum config_event_t type, size_t begin_offset UNUSED, size_t end_offset UNUSED, struct config_source *cs, void *data) @@ -1082,104 +695,6 @@ int git_config_err_fn(enum config_event_t type, size_t begin_offset UNUSED, return error_return; } -static int git_parse_source(struct config_source *cs, config_fn_t fn, - struct key_value_info *kvi, void *data, - const struct config_parse_options *opts) -{ - int comment = 0; - size_t baselen = 0; - struct strbuf *var = &cs->var; - - /* U+FEFF Byte Order Mark in UTF8 */ - const char *bomptr = utf8_bom; - - /* For the parser event callback */ - struct parse_event_data event_data = { - CONFIG_EVENT_EOF, 0, opts - }; - - for (;;) { - int c; - - c = get_next_char(cs); - if (bomptr && *bomptr) { - /* We are at the file beginning; skip UTF8-encoded BOM - * if present. Sane editors won't put this in on their - * own, but e.g. Windows Notepad will do it happily. */ - if (c == (*bomptr & 0377)) { - bomptr++; - continue; - } else { - /* Do not tolerate partial BOM. */ - if (bomptr != utf8_bom) - break; - /* No BOM at file beginning. Cool. */ - bomptr = NULL; - } - } - if (c == '\n') { - if (cs->eof) { - if (do_event(cs, CONFIG_EVENT_EOF, &event_data) < 0) - return -1; - return 0; - } - if (do_event(cs, CONFIG_EVENT_WHITESPACE, &event_data) < 0) - return -1; - comment = 0; - continue; - } - if (comment) - continue; - if (isspace(c)) { - if (do_event(cs, CONFIG_EVENT_WHITESPACE, &event_data) < 0) - return -1; - continue; - } - if (c == '#' || c == ';') { - if (do_event(cs, CONFIG_EVENT_COMMENT, &event_data) < 0) - return -1; - comment = 1; - continue; - } - if (c == '[') { - if (do_event(cs, CONFIG_EVENT_SECTION, &event_data) < 0) - return -1; - - /* Reset prior to determining a new stem */ - strbuf_reset(var); - if (get_base_var(cs, var) < 0 || var->len < 1) - break; - strbuf_addch(var, '.'); - baselen = var->len; - continue; - } - if (!isalpha(c)) - break; - - if (do_event(cs, CONFIG_EVENT_ENTRY, &event_data) < 0) - return -1; - - /* - * Truncate the var name back to the section header - * stem prior to grabbing the suffix part of the name - * and the value. - */ - strbuf_setlen(var, baselen); - strbuf_addch(var, tolower(c)); - if (get_value(cs, kvi, fn, data, var) < 0) - break; - } - - /* - * FIXME for whatever reason, do_event passes the _previous_ event, so - * in order for our callback to receive the error event, we have to call - * do_event twice - */ - do_event(cs, CONFIG_EVENT_ERROR, &event_data); - do_event(cs, CONFIG_EVENT_ERROR, &event_data); - return -1; -} - static uintmax_t get_unit_factor(const char *end) { if (!*end) @@ -1973,83 +1488,6 @@ int git_default_config(const char *var, const char *value, return 0; } -/* - * All source specific fields in the union, die_on_error, name and the callbacks - * fgetc, ungetc, ftell of top need to be initialized before calling - * this function. - */ -static int do_config_from(struct config_source *top, config_fn_t fn, - void *data, enum config_scope scope, - const struct config_parse_options *opts) -{ - struct key_value_info kvi = KVI_INIT; - int ret; - - /* push config-file parsing state stack */ - top->linenr = 1; - top->eof = 0; - top->total_len = 0; - strbuf_init(&top->value, 1024); - strbuf_init(&top->var, 1024); - kvi_from_source(top, scope, &kvi); - - ret = git_parse_source(top, fn, &kvi, data, opts); - - strbuf_release(&top->value); - strbuf_release(&top->var); - - return ret; -} - -static int do_config_from_file(config_fn_t fn, - const enum config_origin_type origin_type, - const char *name, const char *path, FILE *f, - void *data, enum config_scope scope, - const struct config_parse_options *opts) -{ - struct config_source top = CONFIG_SOURCE_INIT; - int ret; - - top.u.file = f; - top.origin_type = origin_type; - top.name = name; - top.path = path; - top.do_fgetc = config_file_fgetc; - top.do_ungetc = config_file_ungetc; - top.do_ftell = config_file_ftell; - - flockfile(f); - ret = do_config_from(&top, fn, data, scope, opts); - funlockfile(f); - return ret; -} - -static int git_config_from_stdin(config_fn_t fn, void *data, - enum config_scope scope, - const struct config_parse_options *config_opts) -{ - return do_config_from_file(fn, CONFIG_ORIGIN_STDIN, "", NULL, stdin, - data, scope, config_opts); -} - -int git_config_from_file_with_options(config_fn_t fn, const char *filename, - void *data, enum config_scope scope, - const struct config_parse_options *opts) -{ - int ret = -1; - FILE *f; - - if (!filename) - BUG("filename cannot be NULL"); - f = fopen_or_warn(filename, "r"); - if (f) { - ret = do_config_from_file(fn, CONFIG_ORIGIN_FILE, filename, - filename, f, data, scope, opts); - fclose(f); - } - return ret; -} - int git_config_from_file(config_fn_t fn, const char *filename, void *data) { struct config_parse_options config_opts = CP_OPTS_INIT(CONFIG_ERROR_DIE); @@ -2058,27 +1496,6 @@ int git_config_from_file(config_fn_t fn, const char *filename, void *data) CONFIG_SCOPE_UNKNOWN, &config_opts); } -int git_config_from_mem(config_fn_t fn, - const enum config_origin_type origin_type, - const char *name, const char *buf, size_t len, - void *data, enum config_scope scope, - const struct config_parse_options *opts) -{ - struct config_source top = CONFIG_SOURCE_INIT; - - top.u.buf.buf = buf; - top.u.buf.len = len; - top.u.buf.pos = 0; - top.origin_type = origin_type; - top.name = name; - top.path = NULL; - top.do_fgetc = config_buf_fgetc; - top.do_ungetc = config_buf_ungetc; - top.do_ftell = config_buf_ftell; - - return do_config_from(&top, fn, data, scope, opts); -} - int git_config_from_blob_oid(config_fn_t fn, const char *name, struct repository *repo, diff --git a/config.h b/config.h index 1aed02cd5d..3bad5e1c32 100644 --- a/config.h +++ b/config.h @@ -4,7 +4,7 @@ #include "hashmap.h" #include "string-list.h" #include "repository.h" - +#include "config-parse.h" /** * The config API gives callers a way to access Git configuration files @@ -23,9 +23,6 @@ struct object_id; -/* git_config_parse_key() returns these: */ -#define CONFIG_INVALID_KEY 1 -#define CONFIG_NO_SECTION_OR_NAME 2 /* git_config_set_gently(), git_config_set_multivar_gently() return the above or these: */ #define CONFIG_NO_LOCK -1 #define CONFIG_INVALID_FILE 3 @@ -36,17 +33,6 @@ struct object_id; #define CONFIG_REGEX_NONE ((void *)1) -enum config_scope { - CONFIG_SCOPE_UNKNOWN = 0, - CONFIG_SCOPE_SYSTEM, - CONFIG_SCOPE_GLOBAL, - CONFIG_SCOPE_LOCAL, - CONFIG_SCOPE_WORKTREE, - CONFIG_SCOPE_COMMAND, - CONFIG_SCOPE_SUBMODULE, -}; -const char *config_scope_name(enum config_scope scope); - struct git_config_source { unsigned int use_stdin:1; const char *file; @@ -54,46 +40,6 @@ struct git_config_source { enum config_scope scope; }; -enum config_origin_type { - CONFIG_ORIGIN_UNKNOWN = 0, - CONFIG_ORIGIN_BLOB, - CONFIG_ORIGIN_FILE, - CONFIG_ORIGIN_STDIN, - CONFIG_ORIGIN_SUBMODULE_BLOB, - CONFIG_ORIGIN_CMDLINE -}; - -enum config_event_t { - CONFIG_EVENT_SECTION, - CONFIG_EVENT_ENTRY, - CONFIG_EVENT_WHITESPACE, - CONFIG_EVENT_COMMENT, - CONFIG_EVENT_EOF, - CONFIG_EVENT_ERROR -}; - -struct config_source; -/* - * The parser event function (if not NULL) is called with the event type and - * the begin/end offsets of the parsed elements. - * - * Note: for CONFIG_EVENT_ENTRY (i.e. config variables), the trailing newline - * character is considered part of the element. - */ -typedef int (*config_parser_event_fn_t)(enum config_event_t type, - size_t begin_offset, size_t end_offset, - struct config_source *cs, - void *event_fn_data); - -struct config_parse_options { - /* - * event_fn and event_fn_data are for internal use only. Handles events - * emitted by the config parser. - */ - config_parser_event_fn_t event_fn; - void *event_fn_data; -}; - #define CP_OPTS_INIT(error_action) { \ .event_fn = git_config_err_fn, \ .event_fn_data = (enum config_error_action []){(error_action)}, \ @@ -126,59 +72,8 @@ enum config_error_action { int git_config_err_fn(enum config_event_t type, size_t begin_offset, size_t end_offset, struct config_source *cs, void *event_fn_data); - -/* Config source metadata for a given config key-value pair */ -struct key_value_info { - const char *filename; - int linenr; - enum config_origin_type origin_type; - enum config_scope scope; - const char *path; -}; -#define KVI_INIT { \ - .filename = NULL, \ - .linenr = -1, \ - .origin_type = CONFIG_ORIGIN_UNKNOWN, \ - .scope = CONFIG_SCOPE_UNKNOWN, \ - .path = NULL, \ -} - -/* Captures additional information that a config callback can use. */ -struct config_context { - /* Config source metadata for key and value. */ - const struct key_value_info *kvi; -}; -#define CONFIG_CONTEXT_INIT { 0 } - -/** - * A config callback function takes four parameters: - * - * - the name of the parsed variable. This is in canonical "flat" form: the - * section, subsection, and variable segments will be separated by dots, - * and the section and variable segments will be all lowercase. E.g., - * `core.ignorecase`, `diff.SomeType.textconv`. - * - * - the value of the found variable, as a string. If the variable had no - * value specified, the value will be NULL (typically this means it - * should be interpreted as boolean true). - * - * - the 'config context', that is, additional information about the config - * iteration operation provided by the config machinery. For example, this - * includes information about the config source being parsed (e.g. the - * filename). - * - * - a void pointer passed in by the caller of the config API; this can - * contain callback-specific data - * - * A config callback should return 0 for success, or -1 if the variable - * could not be parsed properly. - */ -typedef int (*config_fn_t)(const char *, const char *, - const struct config_context *, void *); - int git_default_config(const char *, const char *, const struct config_context *, void *); - /** * Read a specific file in git-config format. * This function takes the same callback and data parameters as `git_config`. @@ -186,16 +81,6 @@ int git_default_config(const char *, const char *, * Unlike git_config(), this function does not respect includes. */ int git_config_from_file(config_fn_t fn, const char *, void *); - -int git_config_from_file_with_options(config_fn_t fn, const char *, - void *, enum config_scope, - const struct config_parse_options *); -int git_config_from_mem(config_fn_t fn, - const enum config_origin_type, - const char *name, - const char *buf, size_t len, - void *data, enum config_scope scope, - const struct config_parse_options *opts); int git_config_from_blob_oid(config_fn_t fn, const char *name, struct repository *repo, const struct object_id *oid, void *data, @@ -333,8 +218,6 @@ int repo_config_set_worktree_gently(struct repository *, const char *, const cha */ void git_config_set(const char *, const char *); -int git_config_parse_key(const char *, char **, size_t *); - /* * The following macros specify flag bits that alter the behavior * of the git_config_set_multivar*() methods.