Commit c5e94482 by Vicent Martí

config: Refactor & add `git_config_get_mapped`

Sane API for real-world usage.
parent 27950fa3
......@@ -37,6 +37,19 @@ struct git_config_file {
void (*free)(struct git_config_file *);
};
typedef enum {
GIT_CVAR_FALSE = 0,
GIT_CVAR_TRUE = 1,
GIT_CVAR_INT32,
GIT_CVAR_STRING
} git_cvar_t;
typedef struct {
git_cvar_t cvar_type;
const char *str_match;
int map_value;
} git_cvar_map;
/**
* Locate the path to the global configuration file
*
......@@ -301,6 +314,43 @@ GIT_EXTERN(int) git_config_foreach(
int (*callback)(const char *var_name, const char *value, void *payload),
void *payload);
/**
* Query the value of a config variable and return it mapped to
* an integer constant.
*
* This is a helper method to easily map different possible values
* to a variable to integer constants that easily identify them.
*
* A mapping array looks as follows:
*
* git_cvar_map autocrlf_mapping[3] = {
* {GIT_CVAR_FALSE, NULL, GIT_AUTO_CRLF_FALSE},
* {GIT_CVAR_TRUE, NULL, GIT_AUTO_CRLF_TRUE},
* {GIT_CVAR_STRING, "input", GIT_AUTO_CRLF_INPUT},
* {GIT_CVAR_STRING, "default", GIT_AUTO_CRLF_DEFAULT}};
*
* On any "false" value for the variable (e.g. "false", "FALSE", "no"), the
* mapping will store `GIT_AUTO_CRLF_FALSE` in the `out` parameter.
*
* The same thing applies for any "true" value such as "true", "yes" or "1", storing
* the `GIT_AUTO_CRLF_TRUE` variable.
*
* Otherwise, if the value matches the string "input" (with case insensitive comparison),
* the given constant will be stored in `out`, and likewise for "default".
*
* If not a single match can be made to store in `out`, an error code will be
* returned.
*
* @param cfg config file to get the variables from
* @param name name of the config variable to lookup
* @param maps array of `git_cvar_map` objects specifying the possible mappings
* @param map_n number of mapping objects in `maps`
* @param out place to store the result of the mapping
* @return GIT_SUCCESS on success, error code otherwise
*/
GIT_EXTERN(int) git_config_get_mapped(git_config *cfg, const char *name, git_cvar_map *maps, size_t map_n, int *out);
/** @} */
GIT_END_DECL
#endif
......@@ -209,23 +209,37 @@ int git_config_set_string(git_config *cfg, const char *name, const char *value)
return file->set(file, name, value);
}
/***********
* Getters
***********/
static int parse_bool(int *out, const char *value)
{
/* A missing value means true */
if (value == NULL) {
*out = 1;
return GIT_SUCCESS;
}
int git_config_get_int64(git_config *cfg, const char *name, int64_t *out)
if (!strcasecmp(value, "true") ||
!strcasecmp(value, "yes") ||
!strcasecmp(value, "on")) {
*out = 1;
return GIT_SUCCESS;
}
if (!strcasecmp(value, "false") ||
!strcasecmp(value, "no") ||
!strcasecmp(value, "off")) {
*out = 0;
return GIT_SUCCESS;
}
return GIT_EINVALIDTYPE;
}
static int parse_int64(int64_t *out, const char *value)
{
const char *value, *num_end;
int ret;
const char *num_end;
int64_t num;
ret = git_config_get_string(cfg, name, &value);
if (ret < GIT_SUCCESS)
return git__rethrow(ret, "Failed to retrieve value for '%s'", name);
ret = git__strtol64(&num, value, &num_end, 0);
if (ret < GIT_SUCCESS)
return git__rethrow(ret, "Failed to convert value for '%s'", name);
if (git__strtol64(&num, value, &num_end, 0) < 0)
return GIT_EINVALIDTYPE;
switch (*num_end) {
case 'g':
......@@ -245,38 +259,112 @@ int git_config_get_int64(git_config *cfg, const char *name, int64_t *out)
/* check that that there are no more characters after the
* given modifier suffix */
if (num_end[1] != '\0')
return git__throw(GIT_EINVALIDTYPE,
"Failed to get value for '%s'. Invalid type suffix", name);
return GIT_EINVALIDTYPE;
/* fallthrough */
case '\0':
*out = num;
return GIT_SUCCESS;
return 0;
default:
return git__throw(GIT_EINVALIDTYPE,
"Failed to get value for '%s'. Value is of invalid type", name);
return GIT_EINVALIDTYPE;
}
}
int git_config_get_int32(git_config *cfg, const char *name, int32_t *out)
static int parse_int32(int32_t *out, const char *value)
{
int64_t tmp_long;
int32_t tmp_int;
int64_t tmp;
int32_t truncate;
if (parse_int64(&tmp, value) < 0)
return GIT_EINVALIDTYPE;
truncate = tmp & 0xFFFFFFFF;
if (truncate != tmp)
return GIT_EOVERFLOW;
*out = truncate;
return 0;
}
/***********
* Getters
***********/
int git_config_get_mapped(git_config *cfg, const char *name, git_cvar_map *maps, size_t map_n, int *out)
{
size_t i;
const char *value;
int error;
error = git_config_get_string(cfg, name, &value);
if (error < GIT_SUCCESS)
return error;
for (i = 0; i < map_n; ++i) {
git_cvar_map *m = maps + i;
switch (m->cvar_type) {
case GIT_CVAR_FALSE:
case GIT_CVAR_TRUE: {
int bool_val;
if (parse_bool(&bool_val, value) == 0 &&
bool_val == (int)m->cvar_type) {
*out = m->map_value;
return 0;
}
break;
}
case GIT_CVAR_INT32:
if (parse_int32(out, value) == 0)
return 0;
break;
case GIT_CVAR_STRING:
if (strcasecmp(value, m->str_match) == 0) {
*out = m->map_value;
return 0;
}
}
}
return git__throw(GIT_ENOTFOUND,
"Failed to map the '%s' config variable with a valid value", name);
}
int git_config_get_int64(git_config *cfg, const char *name, int64_t *out)
{
const char *value;
int ret;
ret = git_config_get_int64(cfg, name, &tmp_long);
ret = git_config_get_string(cfg, name, &value);
if (ret < GIT_SUCCESS)
return git__rethrow(ret, "Failed to convert value for '%s'", name);
tmp_int = tmp_long & 0xFFFFFFFF;
if (tmp_int != tmp_long)
return git__throw(GIT_EOVERFLOW, "Value for '%s' is too large", name);
return git__rethrow(ret, "Failed to retrieve value for '%s'", name);
*out = tmp_int;
if (parse_int64(out, value) < 0)
return git__throw(GIT_EINVALIDTYPE, "Failed to parse '%s' as an integer", value);
return ret;
return GIT_SUCCESS;
}
int git_config_get_int32(git_config *cfg, const char *name, int32_t *out)
{
const char *value;
int error;
error = git_config_get_string(cfg, name, &value);
if (error < GIT_SUCCESS)
return git__rethrow(error, "Failed to get value for %s", name);
error = parse_int32(out, value);
if (error < GIT_SUCCESS)
return git__throw(GIT_EINVALIDTYPE, "Failed to parse '%s' as a 32-bit integer", value);
return GIT_SUCCESS;
}
int git_config_get_bool(git_config *cfg, const char *name, int *out)
......@@ -288,33 +376,15 @@ int git_config_get_bool(git_config *cfg, const char *name, int *out)
if (error < GIT_SUCCESS)
return git__rethrow(error, "Failed to get value for %s", name);
/* A missing value means true */
if (value == NULL) {
*out = 1;
if (parse_bool(out, value) == 0)
return GIT_SUCCESS;
}
if (!strcasecmp(value, "true") ||
!strcasecmp(value, "yes") ||
!strcasecmp(value, "on")) {
*out = 1;
return GIT_SUCCESS;
}
if (!strcasecmp(value, "false") ||
!strcasecmp(value, "no") ||
!strcasecmp(value, "off")) {
*out = 0;
if (parse_int32(out, value) == 0) {
*out = !!(*out);
return GIT_SUCCESS;
}
/* Try to parse it as an integer */
error = git_config_get_int32(cfg, name, out);
if (error == GIT_SUCCESS)
*out = !!(*out);
if (error < GIT_SUCCESS)
return git__rethrow(error, "Failed to get value for %s", name);
return error;
return git__throw(GIT_EINVALIDTYPE, "Failed to parse '%s' as a boolean value", value);
}
int git_config_get_string(git_config *cfg, const char *name, const char **out)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment