Sanity-check config variable names
Sanity-check config variable names when adding and retrieving them. As a side effect code duplication between git_config_set_multivar and get_value (in builtin/config.c) was removed and the common functionality was placed in git_config_parse_key. This breaks a test in t1300 which used invalid section-less keys in the tests for "git -c". However, allowing such names there was useless, since there was no way to set them via config file, and no part of git actually tried to use section-less keys. This patch updates the test to use more realistic examples as well as adding its own test. Signed-off-by: Libor Pechacek <lpechacek@suse.cz> Acked-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Libor Pechacek committed
Jan 30, 2011 at 20:40 UTC
b09c53a3e331211fc0154de8ebb271e48f8c7ee5
4 files changed
+102
-50
builtin/config.c
+20
-7
@@ -153,7 +153,6 @@ static int show_config(const char *key_, const char *value_, void *cb)
153
static int get_value(const char *key_, const char *regex_)
154
{
155
int ret = -1;
156
- char *tl;
156
char *global = NULL, *repo_config = NULL;
157
const char *system_wide = NULL, *local;
158
@@ -167,18 +166,32 @@ static int get_value(const char *key_, const char *regex_)
166
system_wide = git_etc_gitconfig();
167
}
168
170
- key = xstrdup(key_);
171
- for (tl=key+strlen(key)-1; tl >= key && *tl != '.'; --tl)
172
- *tl = tolower(*tl);
173
- for (tl=key; *tl && *tl != '.'; ++tl)
174
- *tl = tolower(*tl);
175
-
169
if (use_key_regexp) {
170
+ char *tl;
171
+
172
+ /*
173
+ * NEEDSWORK: this naive pattern lowercasing obviously does not
174
+ * work for more complex patterns like "^[^.]*Foo.*bar".
175
+ * Perhaps we should deprecate this altogether someday.
176
+ */
177
+
178
+ key = xstrdup(key_);
179
+ for (tl = key + strlen(key) - 1;
180
+ tl >= key && *tl != '.';
181
+ tl--)
182
+ *tl = tolower(*tl);
183
+ for (tl = key; *tl && *tl != '.'; tl++)
184
+ *tl = tolower(*tl);
185
+
186
key_regexp = (regex_t*)xmalloc(sizeof(regex_t));
187
if (regcomp(key_regexp, key, REG_EXTENDED)) {
188
fprintf(stderr, "Invalid key pattern: %s\n", key_);
189
+ free(key);
190
goto free_strings;
191
}
192
+ } else {
193
+ if (git_config_parse_key(key_, &key, NULL))
194
+ goto free_strings;
195
}
196
197
if (regex_) {
cache.h
+1
@@ -997,6 +997,7 @@ extern int git_config_maybe_bool(const char *, const char *);
997
extern int git_config_string(const char **, const char *, const char *);
998
extern int git_config_pathname(const char **, const char *, const char *);
999
extern int git_config_set(const char *, const char *);
1000
+extern int git_config_parse_key(const char *, char **, int *);
1001
extern int git_config_set_multivar(const char *, const char *, const char *, int);
1002
extern int git_config_rename_section(const char *, const char *);
1003
extern const char *git_etc_gitconfig(void);
config.c
+67
-39
@@ -1098,6 +1098,70 @@ int git_config_set(const char *key, const char *value)
1098
return git_config_set_multivar(key, value, NULL, 0);
1099
}
1100
1101
+/*
1102
+ * Auxiliary function to sanity-check and split the key into the section
1103
+ * identifier and variable name.
1104
+ *
1105
+ * Returns 0 on success, -1 when there is an invalid character in the key and
1106
+ * -2 if there is no section name in the key.
1107
+ *
1108
+ * store_key - pointer to char* which will hold a copy of the key with
1109
+ * lowercase section and variable name
1110
+ * baselen - pointer to int which will hold the length of the
1111
+ * section + subsection part, can be NULL
1112
+ */
1113
+int git_config_parse_key(const char *key, char **store_key, int *baselen_)
1114
+{
1115
+ int i, dot, baselen;
1116
+ const char *last_dot = strrchr(key, '.');
1117
+
1118
+ /*
1119
+ * Since "key" actually contains the section name and the real
1120
+ * key name separated by a dot, we have to know where the dot is.
1121
+ */
1122
+
1123
+ if (last_dot == NULL) {
1124
+ error("key does not contain a section: %s", key);
1125
+ return -2;
1126
+ }
1127
+
1128
+ baselen = last_dot - key;
1129
+ if (baselen_)
1130
+ *baselen_ = baselen;
1131
+
1132
+ /*
1133
+ * Validate the key and while at it, lower case it for matching.
1134
+ */
1135
+ *store_key = xmalloc(strlen(key) + 1);
1136
+
1137
+ dot = 0;
1138
+ for (i = 0; key[i]; i++) {
1139
+ unsigned char c = key[i];
1140
+ if (c == '.')
1141
+ dot = 1;
1142
+ /* Leave the extended basename untouched.. */
1143
+ if (!dot || i > baselen) {
1144
+ if (!iskeychar(c) ||
1145
+ (i == baselen + 1 && !isalpha(c))) {
1146
+ error("invalid key: %s", key);
1147
+ goto out_free_ret_1;
1148
+ }
1149
+ c = tolower(c);
1150
+ } else if (c == '\n') {
1151
+ error("invalid key (newline): %s", key);
1152
+ goto out_free_ret_1;
1153
+ }
1154
+ (*store_key)[i] = c;
1155
+ }
1156
+ (*store_key)[i] = 0;
1157
+
1158
+ return 0;
1159
+
1160
+out_free_ret_1:
1161
+ free(*store_key);
1162
+ return -1;
1163
+}
1164
+
1165
/*
1166
* If value==NULL, unset in (remove from) config,
1167
* if value_regex!=NULL, disregard key/value pairs where value does not match.
@@ -1124,59 +1188,23 @@ int git_config_set(const char *key, const char *value)
1188
int git_config_set_multivar(const char *key, const char *value,
1189
const char *value_regex, int multi_replace)
1190
{
1127
- int i, dot;
1191
int fd = -1, in_fd;
1192
int ret;
1193
char *config_filename;
1194
struct lock_file *lock = NULL;
1132
- const char *last_dot = strrchr(key, '.');
1195
1196
if (config_exclusive_filename)
1197
config_filename = xstrdup(config_exclusive_filename);
1198
else
1199
config_filename = git_pathdup("config");
1200
1139
- /*
1140
- * Since "key" actually contains the section name and the real
1141
- * key name separated by a dot, we have to know where the dot is.
1142
- */
1143
-
1144
- if (last_dot == NULL) {
1145
- error("key does not contain a section: %s", key);
1146
- ret = 2;
1201
+ /* parse-key returns negative; flip the sign to feed exit(3) */
1202
+ ret = 0 - git_config_parse_key(key, &store.key, &store.baselen);
1203
+ if (ret)
1204
goto out_free;
1148
- }
1149
- store.baselen = last_dot - key;
1205
1206
store.multi_replace = multi_replace;
1207
1153
- /*
1154
- * Validate the key and while at it, lower case it for matching.
1155
- */
1156
- store.key = xmalloc(strlen(key) + 1);
1157
- dot = 0;
1158
- for (i = 0; key[i]; i++) {
1159
- unsigned char c = key[i];
1160
- if (c == '.')
1161
- dot = 1;
1162
- /* Leave the extended basename untouched.. */
1163
- if (!dot || i > store.baselen) {
1164
- if (!iskeychar(c) || (i == store.baselen+1 && !isalpha(c))) {
1165
- error("invalid key: %s", key);
1166
- free(store.key);
1167
- ret = 1;
1168
- goto out_free;
1169
- }
1170
- c = tolower(c);
1171
- } else if (c == '\n') {
1172
- error("invalid key (newline): %s", key);
1173
- free(store.key);
1174
- ret = 1;
1175
- goto out_free;
1176
- }
1177
- store.key[i] = c;
1178
- }
1179
- store.key[i] = 0;
1208
1209
/*
1210
* The lock serves a purpose in addition to locking: the new
t/t1300-repo-config.sh
+14
-4
@@ -876,11 +876,21 @@ test_expect_success 'check split_cmdline return' "
876
"
877
878
test_expect_success 'git -c "key=value" support' '
879
- test "z$(git -c name=value config name)" = zvalue &&
879
test "z$(git -c core.name=value config core.name)" = zvalue &&
881
- test "z$(git -c CamelCase=value config camelcase)" = zvalue &&
882
- test "z$(git -c flag config --bool flag)" = ztrue &&
883
- test_must_fail git -c core.name=value config name
880
+ test "z$(git -c foo.CamelCase=value config foo.camelcase)" = zvalue &&
881
+ test "z$(git -c foo.flag config --bool foo.flag)" = ztrue &&
882
+ test_must_fail git -c name=value config core.name
883
+'
884
+
885
+test_expect_success 'key sanity-checking' '
886
+ test_must_fail git config foo=bar &&
887
+ test_must_fail git config foo=.bar &&
888
+ test_must_fail git config foo.ba=r &&
889
+ test_must_fail git config foo.1bar &&
890
+ test_must_fail git config foo."ba
891
+ z".bar &&
892
+ git config foo.bar true &&
893
+ git config foo."ba =z".bar false
894
'
895
896
test_done