config: Refactor & add git_config_get_mapped

Sane API for real-world usage.
This commit is contained in:
Vicent Martí 2012-03-01 00:52:21 +01:00
parent 27950fa3f4
commit c5e944820a
2 changed files with 203 additions and 83 deletions

View File

@ -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

View File

@ -209,85 +209,8 @@ int git_config_set_string(git_config *cfg, const char *name, const char *value)
return file->set(file, name, value);
}
/***********
* Getters
***********/
int git_config_get_int64(git_config *cfg, const char *name, int64_t *out)
static int parse_bool(int *out, const char *value)
{
const char *value, *num_end;
int ret;
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);
switch (*num_end) {
case 'g':
case 'G':
num *= 1024;
/* fallthrough */
case 'm':
case 'M':
num *= 1024;
/* fallthrough */
case 'k':
case 'K':
num *= 1024;
/* 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);
/* fallthrough */
case '\0':
*out = num;
return GIT_SUCCESS;
default:
return git__throw(GIT_EINVALIDTYPE,
"Failed to get value for '%s'. Value is of invalid type", name);
}
}
int git_config_get_int32(git_config *cfg, const char *name, int32_t *out)
{
int64_t tmp_long;
int32_t tmp_int;
int ret;
ret = git_config_get_int64(cfg, name, &tmp_long);
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);
*out = tmp_int;
return ret;
}
int git_config_get_bool(git_config *cfg, const char *name, int *out)
{
const char *value;
int error = GIT_SUCCESS;
error = git_config_get_string(cfg, name, &value);
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;
@ -307,14 +230,161 @@ int git_config_get_bool(git_config *cfg, const char *name, int *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);
return GIT_EINVALIDTYPE;
}
static int parse_int64(int64_t *out, const char *value)
{
const char *num_end;
int64_t num;
if (git__strtol64(&num, value, &num_end, 0) < 0)
return GIT_EINVALIDTYPE;
switch (*num_end) {
case 'g':
case 'G':
num *= 1024;
/* fallthrough */
case 'm':
case 'M':
num *= 1024;
/* fallthrough */
case 'k':
case 'K':
num *= 1024;
/* check that that there are no more characters after the
* given modifier suffix */
if (num_end[1] != '\0')
return GIT_EINVALIDTYPE;
/* fallthrough */
case '\0':
*out = num;
return 0;
default:
return GIT_EINVALIDTYPE;
}
}
static int parse_int32(int32_t *out, const char *value)
{
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_string(cfg, name, &value);
if (ret < GIT_SUCCESS)
return git__rethrow(ret, "Failed to retrieve value for '%s'", name);
if (parse_int64(out, value) < 0)
return git__throw(GIT_EINVALIDTYPE, "Failed to parse '%s' as an integer", value);
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);
return error;
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)
{
const char *value;
int error = GIT_SUCCESS;
error = git_config_get_string(cfg, name, &value);
if (error < GIT_SUCCESS)
return git__rethrow(error, "Failed to get value for %s", name);
if (parse_bool(out, value) == 0)
return GIT_SUCCESS;
if (parse_int32(out, value) == 0) {
*out = !!(*out);
return GIT_SUCCESS;
}
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)