Skip to content

Commit eb4e87c

Browse files
committed
Merge branch 'jc/config-case-cmdline-take-2' into maint
The code to parse "git -c VAR=VAL cmd" and set configuration variable for the duration of cmd had two small bugs, which have been fixed. This supersedes jc/config-case-cmdline topic that has been discarded. * jc/config-case-cmdline-take-2: config: use git_config_parse_key() in git_config_parse_parameter() config: move a few helper functions up
2 parents ea7aa5a + 1274a15 commit eb4e87c

File tree

2 files changed

+163
-97
lines changed

2 files changed

+163
-97
lines changed

config.c

Lines changed: 101 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -201,11 +201,105 @@ void git_config_push_parameter(const char *text)
201201
strbuf_release(&env);
202202
}
203203

204+
static inline int iskeychar(int c)
205+
{
206+
return isalnum(c) || c == '-';
207+
}
208+
209+
/*
210+
* Auxiliary function to sanity-check and split the key into the section
211+
* identifier and variable name.
212+
*
213+
* Returns 0 on success, -1 when there is an invalid character in the key and
214+
* -2 if there is no section name in the key.
215+
*
216+
* store_key - pointer to char* which will hold a copy of the key with
217+
* lowercase section and variable name
218+
* baselen - pointer to int which will hold the length of the
219+
* section + subsection part, can be NULL
220+
*/
221+
static int git_config_parse_key_1(const char *key, char **store_key, int *baselen_, int quiet)
222+
{
223+
int i, dot, baselen;
224+
const char *last_dot = strrchr(key, '.');
225+
226+
/*
227+
* Since "key" actually contains the section name and the real
228+
* key name separated by a dot, we have to know where the dot is.
229+
*/
230+
231+
if (last_dot == NULL || last_dot == key) {
232+
if (!quiet)
233+
error("key does not contain a section: %s", key);
234+
return -CONFIG_NO_SECTION_OR_NAME;
235+
}
236+
237+
if (!last_dot[1]) {
238+
if (!quiet)
239+
error("key does not contain variable name: %s", key);
240+
return -CONFIG_NO_SECTION_OR_NAME;
241+
}
242+
243+
baselen = last_dot - key;
244+
if (baselen_)
245+
*baselen_ = baselen;
246+
247+
/*
248+
* Validate the key and while at it, lower case it for matching.
249+
*/
250+
if (store_key)
251+
*store_key = xmallocz(strlen(key));
252+
253+
dot = 0;
254+
for (i = 0; key[i]; i++) {
255+
unsigned char c = key[i];
256+
if (c == '.')
257+
dot = 1;
258+
/* Leave the extended basename untouched.. */
259+
if (!dot || i > baselen) {
260+
if (!iskeychar(c) ||
261+
(i == baselen + 1 && !isalpha(c))) {
262+
if (!quiet)
263+
error("invalid key: %s", key);
264+
goto out_free_ret_1;
265+
}
266+
c = tolower(c);
267+
} else if (c == '\n') {
268+
if (!quiet)
269+
error("invalid key (newline): %s", key);
270+
goto out_free_ret_1;
271+
}
272+
if (store_key)
273+
(*store_key)[i] = c;
274+
}
275+
276+
return 0;
277+
278+
out_free_ret_1:
279+
if (store_key) {
280+
free(*store_key);
281+
*store_key = NULL;
282+
}
283+
return -CONFIG_INVALID_KEY;
284+
}
285+
286+
int git_config_parse_key(const char *key, char **store_key, int *baselen)
287+
{
288+
return git_config_parse_key_1(key, store_key, baselen, 0);
289+
}
290+
291+
int git_config_key_is_valid(const char *key)
292+
{
293+
return !git_config_parse_key_1(key, NULL, NULL, 1);
294+
}
295+
204296
int git_config_parse_parameter(const char *text,
205297
config_fn_t fn, void *data)
206298
{
207299
const char *value;
300+
char *canonical_name;
208301
struct strbuf **pair;
302+
int ret;
209303

210304
pair = strbuf_split_str(text, '=', 2);
211305
if (!pair[0])
@@ -223,13 +317,15 @@ int git_config_parse_parameter(const char *text,
223317
strbuf_list_free(pair);
224318
return error("bogus config parameter: %s", text);
225319
}
226-
strbuf_tolower(pair[0]);
227-
if (fn(pair[0]->buf, value, data) < 0) {
228-
strbuf_list_free(pair);
229-
return -1;
320+
321+
if (git_config_parse_key(pair[0]->buf, &canonical_name, NULL)) {
322+
ret = -1;
323+
} else {
324+
ret = (fn(canonical_name, value, data) < 0) ? -1 : 0;
325+
free(canonical_name);
230326
}
231327
strbuf_list_free(pair);
232-
return 0;
328+
return ret;
233329
}
234330

235331
int git_config_from_parameters(config_fn_t fn, void *data)
@@ -356,11 +452,6 @@ static char *parse_value(void)
356452
}
357453
}
358454

359-
static inline int iskeychar(int c)
360-
{
361-
return isalnum(c) || c == '-';
362-
}
363-
364455
static int get_value(config_fn_t fn, void *data, struct strbuf *name)
365456
{
366457
int c;
@@ -1989,93 +2080,6 @@ void git_config_set(const char *key, const char *value)
19892080
git_config_set_multivar(key, value, NULL, 0);
19902081
}
19912082

1992-
/*
1993-
* Auxiliary function to sanity-check and split the key into the section
1994-
* identifier and variable name.
1995-
*
1996-
* Returns 0 on success, -1 when there is an invalid character in the key and
1997-
* -2 if there is no section name in the key.
1998-
*
1999-
* store_key - pointer to char* which will hold a copy of the key with
2000-
* lowercase section and variable name
2001-
* baselen - pointer to int which will hold the length of the
2002-
* section + subsection part, can be NULL
2003-
*/
2004-
static int git_config_parse_key_1(const char *key, char **store_key, int *baselen_, int quiet)
2005-
{
2006-
int i, dot, baselen;
2007-
const char *last_dot = strrchr(key, '.');
2008-
2009-
/*
2010-
* Since "key" actually contains the section name and the real
2011-
* key name separated by a dot, we have to know where the dot is.
2012-
*/
2013-
2014-
if (last_dot == NULL || last_dot == key) {
2015-
if (!quiet)
2016-
error("key does not contain a section: %s", key);
2017-
return -CONFIG_NO_SECTION_OR_NAME;
2018-
}
2019-
2020-
if (!last_dot[1]) {
2021-
if (!quiet)
2022-
error("key does not contain variable name: %s", key);
2023-
return -CONFIG_NO_SECTION_OR_NAME;
2024-
}
2025-
2026-
baselen = last_dot - key;
2027-
if (baselen_)
2028-
*baselen_ = baselen;
2029-
2030-
/*
2031-
* Validate the key and while at it, lower case it for matching.
2032-
*/
2033-
if (store_key)
2034-
*store_key = xmallocz(strlen(key));
2035-
2036-
dot = 0;
2037-
for (i = 0; key[i]; i++) {
2038-
unsigned char c = key[i];
2039-
if (c == '.')
2040-
dot = 1;
2041-
/* Leave the extended basename untouched.. */
2042-
if (!dot || i > baselen) {
2043-
if (!iskeychar(c) ||
2044-
(i == baselen + 1 && !isalpha(c))) {
2045-
if (!quiet)
2046-
error("invalid key: %s", key);
2047-
goto out_free_ret_1;
2048-
}
2049-
c = tolower(c);
2050-
} else if (c == '\n') {
2051-
if (!quiet)
2052-
error("invalid key (newline): %s", key);
2053-
goto out_free_ret_1;
2054-
}
2055-
if (store_key)
2056-
(*store_key)[i] = c;
2057-
}
2058-
2059-
return 0;
2060-
2061-
out_free_ret_1:
2062-
if (store_key) {
2063-
free(*store_key);
2064-
*store_key = NULL;
2065-
}
2066-
return -CONFIG_INVALID_KEY;
2067-
}
2068-
2069-
int git_config_parse_key(const char *key, char **store_key, int *baselen)
2070-
{
2071-
return git_config_parse_key_1(key, store_key, baselen, 0);
2072-
}
2073-
2074-
int git_config_key_is_valid(const char *key)
2075-
{
2076-
return !git_config_parse_key_1(key, NULL, NULL, 1);
2077-
}
2078-
20792083
/*
20802084
* If value==NULL, unset in (remove from) config,
20812085
* if value_regex!=NULL, disregard key/value pairs where value does not match.

t/t1300-repo-config.sh

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1097,6 +1097,68 @@ test_expect_success 'multiple git -c appends config' '
10971097
test_cmp expect actual
10981098
'
10991099

1100+
test_expect_success 'last one wins: two level vars' '
1101+
1102+
# sec.var and sec.VAR are the same variable, as the first
1103+
# and the last level of a configuration variable name is
1104+
# case insensitive.
1105+
1106+
echo VAL >expect &&
1107+
1108+
git -c sec.var=val -c sec.VAR=VAL config --get sec.var >actual &&
1109+
test_cmp expect actual &&
1110+
git -c SEC.var=val -c sec.var=VAL config --get sec.var >actual &&
1111+
test_cmp expect actual &&
1112+
1113+
git -c sec.var=val -c sec.VAR=VAL config --get SEC.var >actual &&
1114+
test_cmp expect actual &&
1115+
git -c SEC.var=val -c sec.var=VAL config --get sec.VAR >actual &&
1116+
test_cmp expect actual
1117+
'
1118+
1119+
test_expect_success 'last one wins: three level vars' '
1120+
1121+
# v.a.r and v.A.r are not the same variable, as the middle
1122+
# level of a three-level configuration variable name is
1123+
# case sensitive.
1124+
1125+
echo val >expect &&
1126+
git -c v.a.r=val -c v.A.r=VAL config --get v.a.r >actual &&
1127+
test_cmp expect actual &&
1128+
git -c v.a.r=val -c v.A.r=VAL config --get V.a.R >actual &&
1129+
test_cmp expect actual &&
1130+
1131+
# v.a.r and V.a.R are the same variable, as the first
1132+
# and the last level of a configuration variable name is
1133+
# case insensitive.
1134+
1135+
echo VAL >expect &&
1136+
git -c v.a.r=val -c v.a.R=VAL config --get v.a.r >actual &&
1137+
test_cmp expect actual &&
1138+
git -c v.a.r=val -c V.a.r=VAL config --get v.a.r >actual &&
1139+
test_cmp expect actual &&
1140+
git -c v.a.r=val -c v.a.R=VAL config --get V.a.R >actual &&
1141+
test_cmp expect actual &&
1142+
git -c v.a.r=val -c V.a.r=VAL config --get V.a.R >actual &&
1143+
test_cmp expect actual
1144+
'
1145+
1146+
for VAR in a .a a. a.0b a."b c". a."b c".0d
1147+
do
1148+
test_expect_success "git -c $VAR=VAL rejects invalid '$VAR'" '
1149+
test_must_fail git -c "$VAR=VAL" config -l
1150+
'
1151+
done
1152+
1153+
for VAR in a.b a."b c".d
1154+
do
1155+
test_expect_success "git -c $VAR=VAL works with valid '$VAR'" '
1156+
echo VAL >expect &&
1157+
git -c "$VAR=VAL" config --get "$VAR" >actual &&
1158+
test_cmp expect actual
1159+
'
1160+
done
1161+
11001162
test_expect_success 'git -c is not confused by empty environment' '
11011163
GIT_CONFIG_PARAMETERS="" git -c x.one=1 config --list
11021164
'

0 commit comments

Comments
 (0)