diff options
Diffstat (limited to 'builtin/config.c')
-rw-r--r-- | builtin/config.c | 220 |
1 files changed, 169 insertions, 51 deletions
diff --git a/builtin/config.c b/builtin/config.c index d13daeeb55..97b58c4aea 100644 --- a/builtin/config.c +++ b/builtin/config.c @@ -25,7 +25,8 @@ static char term = '\n'; static int use_global_config, use_system_config, use_local_config; static struct git_config_source given_config_source; -static int actions, types; +static int actions, type; +static char *default_value; static int end_null; static int respect_includes_opt = -1; static struct config_options config_options; @@ -48,10 +49,74 @@ static int show_origin; #define ACTION_GET_COLORBOOL (1<<14) #define ACTION_GET_URLMATCH (1<<15) -#define TYPE_BOOL (1<<0) -#define TYPE_INT (1<<1) -#define TYPE_BOOL_OR_INT (1<<2) -#define TYPE_PATH (1<<3) +/* + * The actions "ACTION_LIST | ACTION_GET_*" which may produce more than + * one line of output and which should therefore be paged. + */ +#define PAGING_ACTIONS (ACTION_LIST | ACTION_GET_ALL | \ + ACTION_GET_REGEXP | ACTION_GET_URLMATCH) + +#define TYPE_BOOL 1 +#define TYPE_INT 2 +#define TYPE_BOOL_OR_INT 3 +#define TYPE_PATH 4 +#define TYPE_EXPIRY_DATE 5 +#define TYPE_COLOR 6 + +#define OPT_CALLBACK_VALUE(s, l, v, h, i) \ + { OPTION_CALLBACK, (s), (l), (v), NULL, (h), PARSE_OPT_NOARG | \ + PARSE_OPT_NONEG, option_parse_type, (i) } + +static NORETURN void usage_builtin_config(void); + +static int option_parse_type(const struct option *opt, const char *arg, + int unset) +{ + int new_type, *to_type; + + if (unset) { + *((int *) opt->value) = 0; + return 0; + } + + /* + * To support '--<type>' style flags, begin with new_type equal to + * opt->defval. + */ + new_type = opt->defval; + if (!new_type) { + if (!strcmp(arg, "bool")) + new_type = TYPE_BOOL; + else if (!strcmp(arg, "int")) + new_type = TYPE_INT; + else if (!strcmp(arg, "bool-or-int")) + new_type = TYPE_BOOL_OR_INT; + else if (!strcmp(arg, "path")) + new_type = TYPE_PATH; + else if (!strcmp(arg, "expiry-date")) + new_type = TYPE_EXPIRY_DATE; + else if (!strcmp(arg, "color")) + new_type = TYPE_COLOR; + else + die(_("unrecognized --type argument, %s"), arg); + } + + to_type = opt->value; + if (*to_type && *to_type != new_type) { + /* + * Complain when there is a new type not equal to the old type. + * This allows for combinations like '--int --type=int' and + * '--type=int --type=int', but disallows ones like '--type=bool + * --int' and '--type=bool + * --type=int'. + */ + error(_("only one type at a time")); + usage_builtin_config(); + } + *to_type = new_type; + + return 0; +} static struct option builtin_config_options[] = { OPT_GROUP(N_("Config file location")), @@ -76,23 +141,35 @@ static struct option builtin_config_options[] = { OPT_BIT(0, "get-color", &actions, N_("find the color configured: slot [default]"), ACTION_GET_COLOR), OPT_BIT(0, "get-colorbool", &actions, N_("find the color setting: slot [stdout-is-tty]"), ACTION_GET_COLORBOOL), OPT_GROUP(N_("Type")), - OPT_BIT(0, "bool", &types, N_("value is \"true\" or \"false\""), TYPE_BOOL), - OPT_BIT(0, "int", &types, N_("value is decimal number"), TYPE_INT), - OPT_BIT(0, "bool-or-int", &types, N_("value is --bool or --int"), TYPE_BOOL_OR_INT), - OPT_BIT(0, "path", &types, N_("value is a path (file or directory name)"), TYPE_PATH), + OPT_CALLBACK('t', "type", &type, "", N_("value is given this type"), option_parse_type), + OPT_CALLBACK_VALUE(0, "bool", &type, N_("value is \"true\" or \"false\""), TYPE_BOOL), + OPT_CALLBACK_VALUE(0, "int", &type, N_("value is decimal number"), TYPE_INT), + OPT_CALLBACK_VALUE(0, "bool-or-int", &type, N_("value is --bool or --int"), TYPE_BOOL_OR_INT), + OPT_CALLBACK_VALUE(0, "path", &type, N_("value is a path (file or directory name)"), TYPE_PATH), + OPT_CALLBACK_VALUE(0, "expiry-date", &type, N_("value is an expiry date"), TYPE_EXPIRY_DATE), OPT_GROUP(N_("Other")), OPT_BOOL('z', "null", &end_null, N_("terminate values with NUL byte")), OPT_BOOL(0, "name-only", &omit_values, N_("show variable names only")), OPT_BOOL(0, "includes", &respect_includes_opt, N_("respect include directives on lookup")), OPT_BOOL(0, "show-origin", &show_origin, N_("show origin of config (file, standard input, blob, command line)")), + OPT_STRING(0, "default", &default_value, N_("value"), N_("with --get, use default value when missing entry")), OPT_END(), }; +static NORETURN void usage_builtin_config(void) +{ + usage_with_options(builtin_config_usage, builtin_config_options); +} + static void check_argc(int argc, int min, int max) { if (argc >= min && argc <= max) return; - error("wrong number of arguments"); - usage_with_options(builtin_config_usage, builtin_config_options); + if (min == max) + error(_("wrong number of arguments, should be %d"), min); + else + error(_("wrong number of arguments, should be from %d to %d"), + min, max); + usage_builtin_config(); } static void show_config_origin(struct strbuf *buf) @@ -140,25 +217,35 @@ static int format_config(struct strbuf *buf, const char *key_, const char *value if (show_keys) strbuf_addch(buf, key_delim); - if (types == TYPE_INT) + if (type == TYPE_INT) strbuf_addf(buf, "%"PRId64, git_config_int64(key_, value_ ? value_ : "")); - else if (types == TYPE_BOOL) + else if (type == TYPE_BOOL) strbuf_addstr(buf, git_config_bool(key_, value_) ? "true" : "false"); - else if (types == TYPE_BOOL_OR_INT) { + else if (type == TYPE_BOOL_OR_INT) { int is_bool, v; v = git_config_bool_or_int(key_, value_, &is_bool); if (is_bool) strbuf_addstr(buf, v ? "true" : "false"); else strbuf_addf(buf, "%d", v); - } else if (types == TYPE_PATH) { + } else if (type == TYPE_PATH) { const char *v; if (git_config_pathname(&v, key_, value_) < 0) return -1; strbuf_addstr(buf, v); free((char *)v); + } else if (type == TYPE_EXPIRY_DATE) { + timestamp_t t; + if (git_config_expiry_date(&t, key_, value_) < 0) + return -1; + strbuf_addf(buf, "%"PRItime, t); + } else if (type == TYPE_COLOR) { + char v[COLOR_MAXLEN]; + if (git_config_color(v, key_, value_) < 0) + return -1; + strbuf_addstr(buf, v); } else if (value_) { strbuf_addstr(buf, value_); } else { @@ -214,7 +301,7 @@ static int get_value(const char *key_, const char *regex_) key_regexp = (regex_t*)xmalloc(sizeof(regex_t)); if (regcomp(key_regexp, key, REG_EXTENDED)) { - error("invalid key pattern: %s", key_); + error(_("invalid key pattern: %s"), key_); FREE_AND_NULL(key_regexp); ret = CONFIG_INVALID_PATTERN; goto free_strings; @@ -234,7 +321,7 @@ static int get_value(const char *key_, const char *regex_) regexp = (regex_t*)xmalloc(sizeof(regex_t)); if (regcomp(regexp, regex_, REG_EXTENDED)) { - error("invalid pattern: %s", regex_); + error(_("invalid pattern: %s"), regex_); FREE_AND_NULL(regexp); ret = CONFIG_INVALID_PATTERN; goto free_strings; @@ -244,6 +331,16 @@ static int get_value(const char *key_, const char *regex_) config_with_options(collect_config, &values, &given_config_source, &config_options); + if (!values.nr && default_value) { + struct strbuf *item; + ALLOC_GROW(values.items, values.nr + 1, values.alloc); + item = &values.items[values.nr++]; + strbuf_init(item, 0); + if (format_config(item, key_, default_value) < 0) + die(_("failed to format default config value: %s"), + default_value); + } + ret = !values.nr; for (i = 0; i < values.nr; i++) { @@ -273,19 +370,20 @@ static char *normalize_value(const char *key, const char *value) if (!value) return NULL; - if (types == 0 || types == TYPE_PATH) + if (type == 0 || type == TYPE_PATH || type == TYPE_EXPIRY_DATE) /* * We don't do normalization for TYPE_PATH here: If * the path is like ~/foobar/, we prefer to store * "~/foobar/" in the config file, and to expand the ~ * when retrieving the value. + * Also don't do normalization for expiry dates. */ return xstrdup(value); - if (types == TYPE_INT) + if (type == TYPE_INT) return xstrfmt("%"PRId64, git_config_int64(key, value)); - if (types == TYPE_BOOL) + if (type == TYPE_BOOL) return xstrdup(git_config_bool(key, value) ? "true" : "false"); - if (types == TYPE_BOOL_OR_INT) { + if (type == TYPE_BOOL_OR_INT) { int is_bool, v; v = git_config_bool_or_int(key, value, &is_bool); if (!is_bool) @@ -293,8 +391,22 @@ static char *normalize_value(const char *key, const char *value) else return xstrdup(v ? "true" : "false"); } + if (type == TYPE_COLOR) { + char v[COLOR_MAXLEN]; + if (git_config_color(v, key, value)) + die(_("cannot parse color '%s'"), value); + + /* + * The contents of `v` now contain an ANSI escape + * sequence, not suitable for including within a + * configuration file. Treat the above as a + * "sanity-check", and return the given value, which we + * know is representable as valid color code. + */ + return xstrdup(value); + } - die("BUG: cannot normalize type %d", types); + BUG("cannot normalize type %d", type); } static int get_color_found; @@ -377,13 +489,13 @@ static int get_colorbool(const char *var, int print) static void check_write(void) { if (!given_config_source.file && !startup_info->have_repository) - die("not in a git directory"); + die(_("not in a git directory")); if (given_config_source.use_stdin) - die("writing to stdin is not supported"); + die(_("writing to stdin is not supported")); if (given_config_source.blob) - die("writing config blobs is not supported"); + die(_("writing config blobs is not supported")); } struct urlmatch_current_candidate_value { @@ -491,13 +603,16 @@ int cmd_config(int argc, const char **argv, const char *prefix) if (use_global_config + use_system_config + use_local_config + !!given_config_source.file + !!given_config_source.blob > 1) { - error("only one config file at a time."); - usage_with_options(builtin_config_usage, builtin_config_options); + error(_("only one config file at a time")); + usage_builtin_config(); } if (use_local_config && nongit) die(_("--local can only be used inside a git repository")); + if (given_config_source.blob && nongit) + die(_("--blob can only be used inside a git repository")); + if (given_config_source.file && !strcmp(given_config_source.file, "-")) { given_config_source.file = NULL; @@ -515,7 +630,7 @@ int cmd_config(int argc, const char **argv, const char *prefix) * location; error out even if XDG_CONFIG_HOME * is set and points at a sane location. */ - die("$HOME not set"); + die(_("$HOME not set")); if (access_or_warn(user_config, R_OK, 0) && xdg_config && !access_or_warn(xdg_config, R_OK, 0)) { @@ -551,19 +666,14 @@ int cmd_config(int argc, const char **argv, const char *prefix) key_delim = '\n'; } - if (HAS_MULTI_BITS(types)) { - error("only one type at a time."); - usage_with_options(builtin_config_usage, builtin_config_options); - } - - if ((actions & (ACTION_GET_COLOR|ACTION_GET_COLORBOOL)) && types) { - error("--get-color and variable type are incoherent"); - usage_with_options(builtin_config_usage, builtin_config_options); + if ((actions & (ACTION_GET_COLOR|ACTION_GET_COLORBOOL)) && type) { + error(_("--get-color and variable type are incoherent")); + usage_builtin_config(); } if (HAS_MULTI_BITS(actions)) { - error("only one action at a time."); - usage_with_options(builtin_config_usage, builtin_config_options); + error(_("only one action at a time")); + usage_builtin_config(); } if (actions == 0) switch (argc) { @@ -571,31 +681,39 @@ int cmd_config(int argc, const char **argv, const char *prefix) case 2: actions = ACTION_SET; break; case 3: actions = ACTION_SET_ALL; break; default: - usage_with_options(builtin_config_usage, builtin_config_options); + usage_builtin_config(); } if (omit_values && !(actions == ACTION_LIST || actions == ACTION_GET_REGEXP)) { - error("--name-only is only applicable to --list or --get-regexp"); - usage_with_options(builtin_config_usage, builtin_config_options); + error(_("--name-only is only applicable to --list or --get-regexp")); + usage_builtin_config(); } if (show_origin && !(actions & (ACTION_GET|ACTION_GET_ALL|ACTION_GET_REGEXP|ACTION_LIST))) { - error("--show-origin is only applicable to --get, --get-all, " - "--get-regexp, and --list."); - usage_with_options(builtin_config_usage, builtin_config_options); + error(_("--show-origin is only applicable to --get, --get-all, " + "--get-regexp, and --list")); + usage_builtin_config(); } + if (default_value && !(actions & ACTION_GET)) { + error(_("--default is only applicable to --get")); + usage_builtin_config(); + } + + if (actions & PAGING_ACTIONS) + setup_auto_pager("config", 1); + if (actions == ACTION_LIST) { check_argc(argc, 0, 0); if (config_with_options(show_all_config, NULL, &given_config_source, &config_options) < 0) { if (given_config_source.file) - die_errno("unable to read config file '%s'", + die_errno(_("unable to read config file '%s'"), given_config_source.file); else - die("error processing config file(s)"); + die(_("error processing config file(s)")); } } else if (actions == ACTION_EDIT) { @@ -603,11 +721,11 @@ int cmd_config(int argc, const char **argv, const char *prefix) check_argc(argc, 0, 0); if (!given_config_source.file && nongit) - die("not in a git directory"); + die(_("not in a git directory")); if (given_config_source.use_stdin) - die("editing stdin is not supported"); + die(_("editing stdin is not supported")); if (given_config_source.blob) - die("editing blobs is not supported"); + die(_("editing blobs is not supported")); git_config(git_default_config, NULL); config_file = given_config_source.file ? xstrdup(given_config_source.file) : @@ -708,7 +826,7 @@ int cmd_config(int argc, const char **argv, const char *prefix) if (ret < 0) return ret; if (ret == 0) - die("No such section!"); + die(_("no such section: %s"), argv[0]); } else if (actions == ACTION_REMOVE_SECTION) { int ret; @@ -719,7 +837,7 @@ int cmd_config(int argc, const char **argv, const char *prefix) if (ret < 0) return ret; if (ret == 0) - die("No such section!"); + die(_("no such section: %s"), argv[0]); } else if (actions == ACTION_GET_COLOR) { check_argc(argc, 1, 2); |