diff options
| author | Junio C Hamano <gitster@pobox.com> | 2014-09-02 13:24:17 -0700 | 
|---|---|---|
| committer | Junio C Hamano <gitster@pobox.com> | 2014-09-02 13:24:18 -0700 | 
| commit | 56f214e0716dec043b50a7e1e8cc181be2ef7df2 (patch) | |
| tree | 9233df7f61bba4048b92768a07b97db9ecb7b2d3 | |
| parent | e8e4ce72cd9ebb5e5dfe580f98b7764ca6dc08ea (diff) | |
| parent | 4c715ebb96acc77008e9cbebc381738611c6006f (diff) | |
| download | git-56f214e0716dec043b50a7e1e8cc181be2ef7df2.tar.gz | |
Merge branch 'ta/config-set'
Add in-core caching layer to let us avoid reading the same
configuration files number of times.
* ta/config-set:
  test-config: add tests for the config_set API
  add `config_set` API for caching config-like files
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | Documentation/technical/api-config.txt | 142 | ||||
| -rw-r--r-- | Makefile | 1 | ||||
| -rw-r--r-- | cache.h | 32 | ||||
| -rw-r--r-- | config.c | 274 | ||||
| -rw-r--r-- | setup.c | 9 | ||||
| -rwxr-xr-x | t/t1308-config-set.sh | 200 | ||||
| -rw-r--r-- | test-config.c | 142 | 
8 files changed, 801 insertions, 0 deletions
| diff --git a/.gitignore b/.gitignore index 81e12c0621..5bfb234591 100644 --- a/.gitignore +++ b/.gitignore @@ -178,6 +178,7 @@  /gitweb/static/gitweb.min.*  /test-chmtime  /test-ctype +/test-config  /test-date  /test-delta  /test-dump-cache-tree diff --git a/Documentation/technical/api-config.txt b/Documentation/technical/api-config.txt index edd5018e15..21f280ca6d 100644 --- a/Documentation/technical/api-config.txt +++ b/Documentation/technical/api-config.txt @@ -77,6 +77,86 @@ To read a specific file in git-config format, use  `git_config_from_file`. This takes the same callback and data parameters  as `git_config`. +Querying For Specific Variables +------------------------------- + +For programs wanting to query for specific variables in a non-callback +manner, the config API provides two functions `git_config_get_value` +and `git_config_get_value_multi`. They both read values from an internal +cache generated previously from reading the config files. + +`int git_config_get_value(const char *key, const char **value)`:: + +	Finds the highest-priority value for the configuration variable `key`, +	stores the pointer to it in `value` and returns 0. When the +	configuration variable `key` is not found, returns 1 without touching +	`value`. The caller should not free or modify `value`, as it is owned +	by the cache. + +`const struct string_list *git_config_get_value_multi(const char *key)`:: + +	Finds and returns the value list, sorted in order of increasing priority +	for the configuration variable `key`. When the configuration variable +	`key` is not found, returns NULL. The caller should not free or modify +	the returned pointer, as it is owned by the cache. + +`void git_config_clear(void)`:: + +	Resets and invalidates the config cache. + +The config API also provides type specific API functions which do conversion +as well as retrieval for the queried variable, including: + +`int git_config_get_int(const char *key, int *dest)`:: + +	Finds and parses the value to an integer for the configuration variable +	`key`. Dies on error; otherwise, stores the value of the parsed integer in +	`dest` and returns 0. When the configuration variable `key` is not found, +	returns 1 without touching `dest`. + +`int git_config_get_ulong(const char *key, unsigned long *dest)`:: + +	Similar to `git_config_get_int` but for unsigned longs. + +`int git_config_get_bool(const char *key, int *dest)`:: + +	Finds and parses the value into a boolean value, for the configuration +	variable `key` respecting keywords like "true" and "false". Integer +	values are converted into true/false values (when they are non-zero or +	zero, respectively). Other values cause a die(). If parsing is successful, +	stores the value of the parsed result in `dest` and returns 0. When the +	configuration variable `key` is not found, returns 1 without touching +	`dest`. + +`int git_config_get_bool_or_int(const char *key, int *is_bool, int *dest)`:: + +	Similar to `git_config_get_bool`, except that integers are copied as-is, +	and `is_bool` flag is unset. + +`int git_config_get_maybe_bool(const char *key, int *dest)`:: + +	Similar to `git_config_get_bool`, except that it returns -1 on error +	rather than dying. + +`int git_config_get_string_const(const char *key, const char **dest)`:: + +	Allocates and copies the retrieved string into the `dest` parameter for +	the configuration variable `key`; if NULL string is given, prints an +	error message and returns -1. When the configuration variable `key` is +	not found, returns 1 without touching `dest`. + +`int git_config_get_string(const char *key, char **dest)`:: + +	Similar to `git_config_get_string_const`, except that retrieved value +	copied into the `dest` parameter is a mutable string. + +`int git_config_get_pathname(const char *key, const char **dest)`:: + +	Similar to `git_config_get_string`, but expands `~` or `~user` into +	the user's home directory when found at the beginning of the path. + +See test-config.c for usage examples. +  Value Parsing Helpers  --------------------- @@ -134,6 +214,68 @@ int read_file_with_include(const char *file, config_fn_t fn, void *data)  `git_config` respects includes automatically. The lower-level  `git_config_from_file` does not. +Custom Configsets +----------------- + +A `config_set` can be used to construct an in-memory cache for +config-like files that the caller specifies (i.e., files like `.gitmodules`, +`~/.gitconfig` etc.). For example, + +--------------------------------------- +struct config_set gm_config; +git_configset_init(&gm_config); +int b; +/* we add config files to the config_set */ +git_configset_add_file(&gm_config, ".gitmodules"); +git_configset_add_file(&gm_config, ".gitmodules_alt"); + +if (!git_configset_get_bool(gm_config, "submodule.frotz.ignore", &b)) { +	/* hack hack hack */ +} + +/* when we are done with the configset */ +git_configset_clear(&gm_config); +---------------------------------------- + +Configset API provides functions for the above mentioned work flow, including: + +`void git_configset_init(struct config_set *cs)`:: + +	Initializes the config_set `cs`. + +`int git_configset_add_file(struct config_set *cs, const char *filename)`:: + +	Parses the file and adds the variable-value pairs to the `config_set`, +	dies if there is an error in parsing the file. Returns 0 on success, or +	-1 if the file does not exist or is inaccessible. The user has to decide +	if he wants to free the incomplete configset or continue using it when +	the function returns -1. + +`int git_configset_get_value(struct config_set *cs, const char *key, const char **value)`:: + +	Finds the highest-priority value for the configuration variable `key` +	and config set `cs`, stores the pointer to it in `value` and returns 0. +	When the configuration variable `key` is not found, returns 1 without +	touching `value`. The caller should not free or modify `value`, as it +	is owned by the cache. + +`const struct string_list *git_configset_get_value_multi(struct config_set *cs, const char *key)`:: + +	Finds and returns the value list, sorted in order of increasing priority +	for the configuration variable `key` and config set `cs`. When the +	configuration variable `key` is not found, returns NULL. The caller +	should not free or modify the returned pointer, as it is owned by the cache. + +`void git_configset_clear(struct config_set *cs)`:: + +	Clears `config_set` structure, removes all saved variable-value pairs. + +In addition to above functions, the `config_set` API provides type specific +functions in the vein of `git_config_get_int` and family but with an extra +parameter, pointer to struct `config_set`. +They all behave similarly to the `git_config_get*()` family described in +"Querying For Specific Variables" above. +  Writing Config Files  -------------------- @@ -551,6 +551,7 @@ PROGRAMS += $(patsubst %.o,git-%$X,$(PROGRAM_OBJS))  TEST_PROGRAMS_NEED_X += test-chmtime  TEST_PROGRAMS_NEED_X += test-ctype +TEST_PROGRAMS_NEED_X += test-config  TEST_PROGRAMS_NEED_X += test-date  TEST_PROGRAMS_NEED_X += test-delta  TEST_PROGRAMS_NEED_X += test-dump-cache-tree @@ -1353,6 +1353,38 @@ extern int parse_config_key(const char *var,  			    const char **subsection, int *subsection_len,  			    const char **key); +struct config_set { +	struct hashmap config_hash; +	int hash_initialized; +}; + +extern void git_configset_init(struct config_set *cs); +extern int git_configset_add_file(struct config_set *cs, const char *filename); +extern int git_configset_get_value(struct config_set *cs, const char *key, const char **value); +extern const struct string_list *git_configset_get_value_multi(struct config_set *cs, const char *key); +extern void git_configset_clear(struct config_set *cs); +extern int git_configset_get_string_const(struct config_set *cs, const char *key, const char **dest); +extern int git_configset_get_string(struct config_set *cs, const char *key, char **dest); +extern int git_configset_get_int(struct config_set *cs, const char *key, int *dest); +extern int git_configset_get_ulong(struct config_set *cs, const char *key, unsigned long *dest); +extern int git_configset_get_bool(struct config_set *cs, const char *key, int *dest); +extern int git_configset_get_bool_or_int(struct config_set *cs, const char *key, int *is_bool, int *dest); +extern int git_configset_get_maybe_bool(struct config_set *cs, const char *key, int *dest); +extern int git_configset_get_pathname(struct config_set *cs, const char *key, const char **dest); + +extern int git_config_get_value(const char *key, const char **value); +extern const struct string_list *git_config_get_value_multi(const char *key); +extern void git_config_clear(void); +extern void git_config_iter(config_fn_t fn, void *data); +extern int git_config_get_string_const(const char *key, const char **dest); +extern int git_config_get_string(const char *key, char **dest); +extern int git_config_get_int(const char *key, int *dest); +extern int git_config_get_ulong(const char *key, unsigned long *dest); +extern int git_config_get_bool(const char *key, int *dest); +extern int git_config_get_bool_or_int(const char *key, int *is_bool, int *dest); +extern int git_config_get_maybe_bool(const char *key, int *dest); +extern int git_config_get_pathname(const char *key, const char **dest); +  extern int committer_ident_sufficiently_given(void);  extern int author_ident_sufficiently_given(void); @@ -9,6 +9,8 @@  #include "exec_cmd.h"  #include "strbuf.h"  #include "quote.h" +#include "hashmap.h" +#include "string-list.h"  struct config_source {  	struct config_source *prev; @@ -33,10 +35,23 @@ struct config_source {  	long (*do_ftell)(struct config_source *c);  }; +struct config_set_element { +	struct hashmap_entry ent; +	char *key; +	struct string_list value_list; +}; +  static struct config_source *cf;  static int zlib_compression_seen; +/* + * Default config_set that contains key-value pairs from the usual set of config + * config files (i.e repo specific .git/config, user wide ~/.gitconfig, XDG + * config file and the global /etc/gitconfig) + */ +static struct config_set the_config_set; +  static int config_file_fgetc(struct config_source *conf)  {  	return fgetc(conf->u.file); @@ -1210,6 +1225,262 @@ int git_config(config_fn_t fn, void *data)  	return git_config_with_options(fn, data, NULL, 1);  } +static struct config_set_element *configset_find_element(struct config_set *cs, const char *key) +{ +	struct config_set_element k; +	struct config_set_element *found_entry; +	char *normalized_key; +	int ret; +	/* +	 * `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); + +	if (ret) +		return NULL; + +	hashmap_entry_init(&k, strhash(normalized_key)); +	k.key = normalized_key; +	found_entry = hashmap_get(&cs->config_hash, &k, NULL); +	free(normalized_key); +	return found_entry; +} + +static int configset_add_value(struct config_set *cs, const char *key, const char *value) +{ +	struct config_set_element *e; +	e = configset_find_element(cs, key); +	/* +	 * Since the keys are being fed by git_config*() callback mechanism, they +	 * are already normalized. So simply add them without any further munging. +	 */ +	if (!e) { +		e = xmalloc(sizeof(*e)); +		hashmap_entry_init(e, strhash(key)); +		e->key = xstrdup(key); +		string_list_init(&e->value_list, 1); +		hashmap_add(&cs->config_hash, e); +	} +	string_list_append_nodup(&e->value_list, value ? xstrdup(value) : NULL); + +	return 0; +} + +static int config_set_element_cmp(const struct config_set_element *e1, +				 const struct config_set_element *e2, const void *unused) +{ +	return strcmp(e1->key, e2->key); +} + +void git_configset_init(struct config_set *cs) +{ +	hashmap_init(&cs->config_hash, (hashmap_cmp_fn)config_set_element_cmp, 0); +	cs->hash_initialized = 1; +} + +void git_configset_clear(struct config_set *cs) +{ +	struct config_set_element *entry; +	struct hashmap_iter iter; +	if (!cs->hash_initialized) +		return; + +	hashmap_iter_init(&cs->config_hash, &iter); +	while ((entry = hashmap_iter_next(&iter))) { +		free(entry->key); +		string_list_clear(&entry->value_list, 0); +	} +	hashmap_free(&cs->config_hash, 1); +	cs->hash_initialized = 0; +} + +static int config_set_callback(const char *key, const char *value, void *cb) +{ +	struct config_set *cs = cb; +	configset_add_value(cs, key, value); +	return 0; +} + +int git_configset_add_file(struct config_set *cs, const char *filename) +{ +	return git_config_from_file(config_set_callback, filename, cs); +} + +int git_configset_get_value(struct config_set *cs, const char *key, const char **value) +{ +	const struct string_list *values = NULL; +	/* +	 * Follows "last one wins" semantic, i.e., if there are multiple matches for the +	 * queried key in the files of the configset, the value returned will be the last +	 * value in the value list for that key. +	 */ +	values = git_configset_get_value_multi(cs, key); + +	if (!values) +		return 1; +	assert(values->nr > 0); +	*value = values->items[values->nr - 1].string; +	return 0; +} + +const struct string_list *git_configset_get_value_multi(struct config_set *cs, const char *key) +{ +	struct config_set_element *e = configset_find_element(cs, key); +	return e ? &e->value_list : NULL; +} + +int git_configset_get_string_const(struct config_set *cs, const char *key, const char **dest) +{ +	const char *value; +	if (!git_configset_get_value(cs, key, &value)) +		return git_config_string(dest, key, value); +	else +		return 1; +} + +int git_configset_get_string(struct config_set *cs, const char *key, char **dest) +{ +	return git_configset_get_string_const(cs, key, (const char **)dest); +} + +int git_configset_get_int(struct config_set *cs, const char *key, int *dest) +{ +	const char *value; +	if (!git_configset_get_value(cs, key, &value)) { +		*dest = git_config_int(key, value); +		return 0; +	} else +		return 1; +} + +int git_configset_get_ulong(struct config_set *cs, const char *key, unsigned long *dest) +{ +	const char *value; +	if (!git_configset_get_value(cs, key, &value)) { +		*dest = git_config_ulong(key, value); +		return 0; +	} else +		return 1; +} + +int git_configset_get_bool(struct config_set *cs, const char *key, int *dest) +{ +	const char *value; +	if (!git_configset_get_value(cs, key, &value)) { +		*dest = git_config_bool(key, value); +		return 0; +	} else +		return 1; +} + +int git_configset_get_bool_or_int(struct config_set *cs, const char *key, +				int *is_bool, int *dest) +{ +	const char *value; +	if (!git_configset_get_value(cs, key, &value)) { +		*dest = git_config_bool_or_int(key, value, is_bool); +		return 0; +	} else +		return 1; +} + +int git_configset_get_maybe_bool(struct config_set *cs, const char *key, int *dest) +{ +	const char *value; +	if (!git_configset_get_value(cs, key, &value)) { +		*dest = git_config_maybe_bool(key, value); +		if (*dest == -1) +			return -1; +		return 0; +	} else +		return 1; +} + +int git_configset_get_pathname(struct config_set *cs, const char *key, const char **dest) +{ +	const char *value; +	if (!git_configset_get_value(cs, key, &value)) +		return git_config_pathname(dest, key, value); +	else +		return 1; +} + +static void git_config_check_init(void) +{ +	if (the_config_set.hash_initialized) +		return; +	git_configset_init(&the_config_set); +	git_config(config_set_callback, &the_config_set); +} + +void git_config_clear(void) +{ +	if (!the_config_set.hash_initialized) +		return; +	git_configset_clear(&the_config_set); +} + +int git_config_get_value(const char *key, const char **value) +{ +	git_config_check_init(); +	return git_configset_get_value(&the_config_set, key, value); +} + +const struct string_list *git_config_get_value_multi(const char *key) +{ +	git_config_check_init(); +	return git_configset_get_value_multi(&the_config_set, key); +} + +int git_config_get_string_const(const char *key, const char **dest) +{ +	git_config_check_init(); +	return git_configset_get_string_const(&the_config_set, key, dest); +} + +int git_config_get_string(const char *key, char **dest) +{ +	git_config_check_init(); +	return git_config_get_string_const(key, (const char **)dest); +} + +int git_config_get_int(const char *key, int *dest) +{ +	git_config_check_init(); +	return git_configset_get_int(&the_config_set, key, dest); +} + +int git_config_get_ulong(const char *key, unsigned long *dest) +{ +	git_config_check_init(); +	return git_configset_get_ulong(&the_config_set, key, dest); +} + +int git_config_get_bool(const char *key, int *dest) +{ +	git_config_check_init(); +	return git_configset_get_bool(&the_config_set, key, dest); +} + +int git_config_get_bool_or_int(const char *key, int *is_bool, int *dest) +{ +	git_config_check_init(); +	return git_configset_get_bool_or_int(&the_config_set, key, is_bool, dest); +} + +int git_config_get_maybe_bool(const char *key, int *dest) +{ +	git_config_check_init(); +	return git_configset_get_maybe_bool(&the_config_set, key, dest); +} + +int git_config_get_pathname(const char *key, const char **dest) +{ +	git_config_check_init(); +	return git_configset_get_pathname(&the_config_set, key, dest); +} +  /*   * Find all the stuff for git_config_set() below.   */ @@ -1705,6 +1976,9 @@ int git_config_set_multivar_in_file(const char *config_filename,  	lock = NULL;  	ret = 0; +	/* Invalidate the config cache */ +	git_config_clear(); +  out_free:  	if (lock)  		rollback_lock_file(lock); @@ -625,6 +625,15 @@ static const char *setup_git_directory_gently_1(int *nongit_ok)  	int one_filesystem = 1;  	/* +	 * We may have read an incomplete configuration before +	 * setting-up the git directory. If so, clear the cache so +	 * that the next queries to the configuration reload complete +	 * configuration (including the per-repo config file that we +	 * ignored previously). +	 */ +	git_config_clear(); + +	/*  	 * Let's assume that we are in a git repository.  	 * If it turns out later that we are somewhere else, the value will be  	 * updated accordingly. diff --git a/t/t1308-config-set.sh b/t/t1308-config-set.sh new file mode 100755 index 0000000000..7fdf840b00 --- /dev/null +++ b/t/t1308-config-set.sh @@ -0,0 +1,200 @@ +#!/bin/sh + +test_description='Test git config-set API in different settings' + +. ./test-lib.sh + +# 'check_config get_* section.key value' verifies that the entry for +# section.key is 'value' +check_config () { +	if test "$1" = expect_code +	then +		expect_code="$2" && shift && shift +	else +		expect_code=0 +	fi && +	op=$1 key=$2 && shift && shift && +	if test $# != 0 +	then +		printf "%s\n" "$@" +	fi >expect && +	test_expect_code $expect_code test-config "$op" "$key" >actual && +	test_cmp expect actual +} + +test_expect_success 'setup default config' ' +	cat >.git/config <<\EOF +	[case] +		penguin = very blue +		Movie = BadPhysics +		UPPERCASE = true +		MixedCase = true +		my = +		foo +		baz = sam +	[Cores] +		WhatEver = Second +		baz = bar +	[cores] +		baz = bat +	[CORES] +		baz = ball +	[my "Foo bAr"] +		hi = mixed-case +	[my "FOO BAR"] +		hi = upper-case +	[my "foo bar"] +		hi = lower-case +	[case] +		baz = bat +		baz = hask +	[lamb] +		chop = 65 +		head = none +	[goat] +		legs = 4 +		head = true +		skin = false +		nose = 1 +		horns +	EOF +' + +test_expect_success 'get value for a simple key' ' +	check_config get_value case.penguin "very blue" +' + +test_expect_success 'get value for a key with value as an empty string' ' +	check_config get_value case.my "" +' + +test_expect_success 'get value for a key with value as NULL' ' +	check_config get_value case.foo "(NULL)" +' + +test_expect_success 'upper case key' ' +	check_config get_value case.UPPERCASE "true" && +	check_config get_value case.uppercase "true" +' + +test_expect_success 'mixed case key' ' +	check_config get_value case.MixedCase "true" && +	check_config get_value case.MIXEDCASE "true" && +	check_config get_value case.mixedcase "true" +' + +test_expect_success 'key and value with mixed case' ' +	check_config get_value case.Movie "BadPhysics" +' + +test_expect_success 'key with case sensitive subsection' ' +	check_config get_value "my.Foo bAr.hi" "mixed-case" && +	check_config get_value "my.FOO BAR.hi" "upper-case" && +	check_config get_value "my.foo bar.hi" "lower-case" +' + +test_expect_success 'key with case insensitive section header' ' +	check_config get_value cores.baz "ball" && +	check_config get_value Cores.baz "ball" && +	check_config get_value CORES.baz "ball" && +	check_config get_value coreS.baz "ball" +' + +test_expect_success 'key with case insensitive section header & variable' ' +	check_config get_value CORES.BAZ "ball" && +	check_config get_value cores.baz "ball" && +	check_config get_value cores.BaZ "ball" && +	check_config get_value cOreS.bAz "ball" +' + +test_expect_success 'find value with misspelled key' ' +	check_config expect_code 1 get_value "my.fOo Bar.hi" "Value not found for \"my.fOo Bar.hi\"" +' + +test_expect_success 'find value with the highest priority' ' +	check_config get_value case.baz "hask" +' + +test_expect_success 'find integer value for a key' ' +	check_config get_int lamb.chop 65 +' + +test_expect_success 'find integer if value is non parse-able' ' +	check_config expect_code 128 get_int lamb.head +' + +test_expect_success 'find bool value for the entered key' ' +	check_config get_bool goat.head 1 && +	check_config get_bool goat.skin 0 && +	check_config get_bool goat.nose 1 && +	check_config get_bool goat.horns 1 && +	check_config get_bool goat.legs 1 +' + +test_expect_success 'find multiple values' ' +	check_config get_value_multi case.baz sam bat hask +' + +test_expect_success 'find value from a configset' ' +	cat >config2 <<-\EOF && +	[case] +		baz = lama +	[my] +		new = silk +	[case] +		baz = ball +	EOF +	echo silk >expect && +	test-config configset_get_value my.new config2 .git/config >actual && +	test_cmp expect actual +' + +test_expect_success 'find value with highest priority from a configset' ' +	echo hask >expect && +	test-config configset_get_value case.baz config2 .git/config >actual && +	test_cmp expect actual +' + +test_expect_success 'find value_list for a key from a configset' ' +	cat >except <<-\EOF && +	sam +	bat +	hask +	lama +	ball +	EOF +	test-config configset_get_value case.baz config2 .git/config >actual && +	test_cmp expect actual +' + +test_expect_success 'proper error on non-existent files' ' +	echo "Error (-1) reading configuration file non-existent-file." >expect && +	test_expect_code 2 test-config configset_get_value foo.bar non-existent-file 2>actual && +	test_cmp expect actual +' + +test_expect_success POSIXPERM,SANITY 'proper error on non-accessible files' ' +	chmod -r .git/config && +	test_when_finished "chmod +r .git/config" && +	echo "Error (-1) reading configuration file .git/config." >expect && +	test_expect_code 2 test-config configset_get_value foo.bar .git/config 2>actual && +	test_cmp expect actual +' + +test_expect_success 'proper error on error in default config files' ' +	cp .git/config .git/config.old && +	test_when_finished "mv .git/config.old .git/config" && +	echo "[" >>.git/config && +	echo "fatal: bad config file line 35 in .git/config" >expect && +	test_expect_code 128 test-config get_value foo.bar 2>actual && +	test_cmp expect actual +' + +test_expect_success 'proper error on error in custom config files' ' +	echo "[" >>syntax-error && +	echo "fatal: bad config file line 1 in syntax-error" >expect && +	test_expect_code 128 test-config configset_get_value foo.bar syntax-error 2>actual && +	test_cmp expect actual +' + +test_done diff --git a/test-config.c b/test-config.c new file mode 100644 index 0000000000..9dd1b22630 --- /dev/null +++ b/test-config.c @@ -0,0 +1,142 @@ +#include "cache.h" +#include "string-list.h" + +/* + * This program exposes the C API of the configuration mechanism + * as a set of simple commands in order to facilitate testing. + * + * Reads stdin and prints result of command to stdout: + * + * get_value -> prints the value with highest priority for the entered key + * + * get_value_multi -> prints all values for the entered key in increasing order + *		     of priority + * + * get_int -> print integer value for the entered key or die + * + * get_bool -> print bool value for the entered key or die + * + * configset_get_value -> returns value with the highest priority for the entered key + * 			from a config_set constructed from files entered as arguments. + * + * configset_get_value_multi -> returns value_list for the entered key sorted in + * 				ascending order of priority from a config_set + * 				constructed from files entered as arguments. + * + * Examples: + * + * To print the value with highest priority for key "foo.bAr Baz.rock": + * 	test-config get_value "foo.bAr Baz.rock" + * + */ + + +int main(int argc, char **argv) +{ +	int i, val; +	const char *v; +	const struct string_list *strptr; +	struct config_set cs; +	git_configset_init(&cs); + +	if (argc < 2) { +		fprintf(stderr, "Please, provide a command name on the command-line\n"); +		goto exit1; +	} else if (argc == 3 && !strcmp(argv[1], "get_value")) { +		if (!git_config_get_value(argv[2], &v)) { +			if (!v) +				printf("(NULL)\n"); +			else +				printf("%s\n", v); +			goto exit0; +		} else { +			printf("Value not found for \"%s\"\n", argv[2]); +			goto exit1; +		} +	} else if (argc == 3 && !strcmp(argv[1], "get_value_multi")) { +		strptr = git_config_get_value_multi(argv[2]); +		if (strptr) { +			for (i = 0; i < strptr->nr; i++) { +				v = strptr->items[i].string; +				if (!v) +					printf("(NULL)\n"); +				else +					printf("%s\n", v); +			} +			goto exit0; +		} else { +			printf("Value not found for \"%s\"\n", argv[2]); +			goto exit1; +		} +	} else if (argc == 3 && !strcmp(argv[1], "get_int")) { +		if (!git_config_get_int(argv[2], &val)) { +			printf("%d\n", val); +			goto exit0; +		} else { +			printf("Value not found for \"%s\"\n", argv[2]); +			goto exit1; +		} +	} else if (argc == 3 && !strcmp(argv[1], "get_bool")) { +		if (!git_config_get_bool(argv[2], &val)) { +			printf("%d\n", val); +			goto exit0; +		} else { +			printf("Value not found for \"%s\"\n", argv[2]); +			goto exit1; +		} +	} else if (!strcmp(argv[1], "configset_get_value")) { +		for (i = 3; i < argc; i++) { +			int err; +			if ((err = git_configset_add_file(&cs, argv[i]))) { +				fprintf(stderr, "Error (%d) reading configuration file %s.\n", err, argv[i]); +				goto exit2; +			} +		} +		if (!git_configset_get_value(&cs, argv[2], &v)) { +			if (!v) +				printf("(NULL)\n"); +			else +				printf("%s\n", v); +			goto exit0; +		} else { +			printf("Value not found for \"%s\"\n", argv[2]); +			goto exit1; +		} +	} else if (!strcmp(argv[1], "configset_get_value_multi")) { +		for (i = 3; i < argc; i++) { +			int err; +			if ((err = git_configset_add_file(&cs, argv[i]))) { +				fprintf(stderr, "Error (%d) reading configuration file %s.\n", err, argv[i]); +				goto exit2; +			} +		} +		strptr = git_configset_get_value_multi(&cs, argv[2]); +		if (strptr) { +			for (i = 0; i < strptr->nr; i++) { +				v = strptr->items[i].string; +				if (!v) +					printf("(NULL)\n"); +				else +					printf("%s\n", v); +			} +			goto exit0; +		} else { +			printf("Value not found for \"%s\"\n", argv[2]); +			goto exit1; +		} +	} + +	die("%s: Please check the syntax and the function name", argv[0]); + +exit0: +	git_configset_clear(&cs); +	return 0; + +exit1: +	git_configset_clear(&cs); +	return 1; + +exit2: +	git_configset_clear(&cs); +	return 2; +} | 
