1 #include "clar_libgit2.h"
2 #include "buffer.h"
3 #include "path.h"
4 
5 static git_buf buf = GIT_BUF_INIT;
6 
test_config_read__cleanup(void)7 void test_config_read__cleanup(void)
8 {
9 	git_buf_dispose(&buf);
10 }
11 
test_config_read__simple_read(void)12 void test_config_read__simple_read(void)
13 {
14 	git_config *cfg;
15 	int32_t i;
16 
17 	cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config0")));
18 
19 	cl_git_pass(git_config_get_int32(&i, cfg, "core.repositoryformatversion"));
20 	cl_assert(i == 0);
21 	cl_git_pass(git_config_get_bool(&i, cfg, "core.filemode"));
22 	cl_assert(i == 1);
23 	cl_git_pass(git_config_get_bool(&i, cfg, "core.bare"));
24 	cl_assert(i == 0);
25 	cl_git_pass(git_config_get_bool(&i, cfg, "core.logallrefupdates"));
26 	cl_assert(i == 1);
27 
28 	git_config_free(cfg);
29 }
30 
test_config_read__case_sensitive(void)31 void test_config_read__case_sensitive(void)
32 {
33 	git_config *cfg;
34 	int i;
35 
36 	cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config1")));
37 
38 	cl_git_pass(git_config_get_string_buf(&buf, cfg, "this.that.other"));
39 	cl_assert_equal_s("true", git_buf_cstr(&buf));
40 	git_buf_clear(&buf);
41 
42 	cl_git_pass(git_config_get_string_buf(&buf, cfg, "this.That.other"));
43 	cl_assert_equal_s("yes", git_buf_cstr(&buf));
44 
45 	cl_git_pass(git_config_get_bool(&i, cfg, "this.that.other"));
46 	cl_assert(i == 1);
47 	cl_git_pass(git_config_get_bool(&i, cfg, "this.That.other"));
48 	cl_assert(i == 1);
49 
50 	/* This one doesn't exist */
51 	cl_must_fail(git_config_get_bool(&i, cfg, "this.thaT.other"));
52 
53 	git_config_free(cfg);
54 }
55 
56 /*
57  * If \ is the last non-space character on the line, we read the next
58  * one, separating each line with SP.
59  */
test_config_read__multiline_value(void)60 void test_config_read__multiline_value(void)
61 {
62 	git_config *cfg;
63 
64 	cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config2")));
65 
66 	cl_git_pass(git_config_get_string_buf(&buf, cfg, "this.That.and"));
67 	cl_assert_equal_s("one one one two two three three", git_buf_cstr(&buf));
68 
69 	git_config_free(cfg);
70 }
71 
clean_test_config(void * unused)72 static void clean_test_config(void *unused)
73 {
74 	GIT_UNUSED(unused);
75 	cl_fixture_cleanup("./testconfig");
76 }
77 
test_config_read__multiline_value_and_eof(void)78 void test_config_read__multiline_value_and_eof(void)
79 {
80 	git_config *cfg;
81 
82 	cl_set_cleanup(&clean_test_config, NULL);
83 	cl_git_mkfile("./testconfig", "[header]\n  key1 = foo\\\n");
84 	cl_git_pass(git_config_open_ondisk(&cfg, "./testconfig"));
85 
86 	cl_git_pass(git_config_get_string_buf(&buf, cfg, "header.key1"));
87 	cl_assert_equal_s("foo", git_buf_cstr(&buf));
88 
89 	git_config_free(cfg);
90 }
91 
test_config_read__multiline_eof(void)92 void test_config_read__multiline_eof(void)
93 {
94 	git_config *cfg;
95 
96 	cl_set_cleanup(&clean_test_config, NULL);
97 	cl_git_mkfile("./testconfig", "[header]\n  key1 = \\\n");
98 	cl_git_pass(git_config_open_ondisk(&cfg, "./testconfig"));
99 
100 	cl_git_pass(git_config_get_string_buf(&buf, cfg, "header.key1"));
101 	cl_assert_equal_s("", git_buf_cstr(&buf));
102 
103 	git_config_free(cfg);
104 }
105 
106 /*
107  * This kind of subsection declaration is case-insensitive
108  */
test_config_read__subsection_header(void)109 void test_config_read__subsection_header(void)
110 {
111 	git_config *cfg;
112 
113 	cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config3")));
114 
115 	cl_git_pass(git_config_get_string_buf(&buf, cfg, "section.subsection.var"));
116 	cl_assert_equal_s("hello", git_buf_cstr(&buf));
117 
118 	/* The subsection is transformed to lower-case */
119 	cl_must_fail(git_config_get_string_buf(&buf, cfg, "section.subSectIon.var"));
120 
121 	git_config_free(cfg);
122 }
123 
test_config_read__lone_variable(void)124 void test_config_read__lone_variable(void)
125 {
126 	git_config *cfg;
127 	int i;
128 
129 	cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config4")));
130 
131 	cl_git_fail(git_config_get_int32(&i, cfg, "some.section.variable"));
132 
133 	cl_git_pass(git_config_get_string_buf(&buf, cfg, "some.section.variable"));
134 	cl_assert_equal_s("", git_buf_cstr(&buf));
135 	git_buf_clear(&buf);
136 
137 	cl_git_pass(git_config_get_bool(&i, cfg, "some.section.variable"));
138 	cl_assert(i == 1);
139 
140 	cl_git_pass(git_config_get_string_buf(&buf, cfg, "some.section.variableeq"));
141 	cl_assert_equal_s("", git_buf_cstr(&buf));
142 
143 	cl_git_pass(git_config_get_bool(&i, cfg, "some.section.variableeq"));
144 	cl_assert(i == 0);
145 
146 	git_config_free(cfg);
147 }
148 
test_config_read__number_suffixes(void)149 void test_config_read__number_suffixes(void)
150 {
151 	git_config *cfg;
152 	int64_t i;
153 
154 	cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config5")));
155 
156 	cl_git_pass(git_config_get_int64(&i, cfg, "number.simple"));
157 	cl_assert(i == 1);
158 
159 	cl_git_pass(git_config_get_int64(&i, cfg, "number.k"));
160 	cl_assert(i == 1 * 1024);
161 
162 	cl_git_pass(git_config_get_int64(&i, cfg, "number.kk"));
163 	cl_assert(i == 1 * 1024);
164 
165 	cl_git_pass(git_config_get_int64(&i, cfg, "number.m"));
166 	cl_assert(i == 1 * 1024 * 1024);
167 
168 	cl_git_pass(git_config_get_int64(&i, cfg, "number.mm"));
169 	cl_assert(i == 1 * 1024 * 1024);
170 
171 	cl_git_pass(git_config_get_int64(&i, cfg, "number.g"));
172 	cl_assert(i == 1 * 1024 * 1024 * 1024);
173 
174 	cl_git_pass(git_config_get_int64(&i, cfg, "number.gg"));
175 	cl_assert(i == 1 * 1024 * 1024 * 1024);
176 
177 	git_config_free(cfg);
178 }
179 
test_config_read__blank_lines(void)180 void test_config_read__blank_lines(void)
181 {
182 	git_config *cfg;
183 	int i;
184 
185 	cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config6")));
186 
187 	cl_git_pass(git_config_get_bool(&i, cfg, "valid.subsection.something"));
188 	cl_assert(i == 1);
189 
190 	cl_git_pass(git_config_get_bool(&i, cfg, "something.else.something"));
191 	cl_assert(i == 0);
192 
193 	git_config_free(cfg);
194 }
195 
test_config_read__invalid_ext_headers(void)196 void test_config_read__invalid_ext_headers(void)
197 {
198 	git_config *cfg;
199 	cl_must_fail(git_config_open_ondisk(&cfg, cl_fixture("config/config7")));
200 }
201 
test_config_read__empty_files(void)202 void test_config_read__empty_files(void)
203 {
204 	git_config *cfg;
205 	cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config8")));
206 	git_config_free(cfg);
207 }
208 
test_config_read__symbol_headers(void)209 void test_config_read__symbol_headers(void)
210 {
211 	git_config *cfg;
212 	cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config20")));
213 	git_config_free(cfg);
214 }
215 
test_config_read__multiline_multiple_quoted_comment_chars(void)216 void test_config_read__multiline_multiple_quoted_comment_chars(void)
217 {
218 	git_config *cfg;
219 	cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config21")));
220 	git_config_free(cfg);
221 }
222 
test_config_read__header_in_last_line(void)223 void test_config_read__header_in_last_line(void)
224 {
225 	git_config *cfg;
226 
227 	cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config10")));
228 	git_config_free(cfg);
229 }
230 
test_config_read__prefixes(void)231 void test_config_read__prefixes(void)
232 {
233 	git_config *cfg;
234 
235 	cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config9")));
236 	cl_git_pass(git_config_get_string_buf(&buf, cfg, "remote.ab.url"));
237 	cl_assert_equal_s("http://example.com/git/ab", git_buf_cstr(&buf));
238 	git_buf_clear(&buf);
239 
240 	cl_git_pass(git_config_get_string_buf(&buf, cfg, "remote.abba.url"));
241 	cl_assert_equal_s("http://example.com/git/abba", git_buf_cstr(&buf));
242 
243 	git_config_free(cfg);
244 }
245 
test_config_read__escaping_quotes(void)246 void test_config_read__escaping_quotes(void)
247 {
248 	git_config *cfg;
249 
250 	cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config13")));
251 	cl_git_pass(git_config_get_string_buf(&buf, cfg, "core.editor"));
252 	cl_assert_equal_s("\"C:/Program Files/Nonsense/bah.exe\" \"--some option\"", git_buf_cstr(&buf));
253 
254 	git_config_free(cfg);
255 }
256 
test_config_read__invalid_escape_sequence(void)257 void test_config_read__invalid_escape_sequence(void)
258 {
259 	git_config *cfg;
260 
261 	cl_set_cleanup(&clean_test_config, NULL);
262 	cl_git_mkfile("./testconfig", "[header]\n  key1 = \\\\\\;\n  key2 = value2\n");
263 	cl_git_fail(git_config_open_ondisk(&cfg, "./testconfig"));
264 
265 	git_config_free(cfg);
266 }
267 
count_cfg_entries_and_compare_levels(const git_config_entry * entry,void * payload)268 static int count_cfg_entries_and_compare_levels(
269 	const git_config_entry *entry, void *payload)
270 {
271 	int *count = payload;
272 
273 	if (!strcmp(entry->value, "7") || !strcmp(entry->value, "17"))
274 		cl_assert(entry->level == GIT_CONFIG_LEVEL_GLOBAL);
275 	else
276 		cl_assert(entry->level == GIT_CONFIG_LEVEL_SYSTEM);
277 
278 	(*count)++;
279 	return 0;
280 }
281 
cfg_callback_countdown(const git_config_entry * entry,void * payload)282 static int cfg_callback_countdown(const git_config_entry *entry, void *payload)
283 {
284 	int *count = payload;
285 	GIT_UNUSED(entry);
286 	(*count)--;
287 	if (*count == 0)
288 		return -100;
289 	return 0;
290 }
291 
test_config_read__foreach(void)292 void test_config_read__foreach(void)
293 {
294 	git_config *cfg;
295 	int count, ret;
296 
297 	cl_git_pass(git_config_new(&cfg));
298 	cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config9"),
299 		GIT_CONFIG_LEVEL_SYSTEM, NULL, 0));
300 	cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config15"),
301 		GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
302 
303 	count = 0;
304 	cl_git_pass(git_config_foreach(cfg, count_cfg_entries_and_compare_levels, &count));
305 	cl_assert_equal_i(7, count);
306 
307 	count = 3;
308 	cl_git_fail(ret = git_config_foreach(cfg, cfg_callback_countdown, &count));
309 	cl_assert_equal_i(-100, ret);
310 
311 	git_config_free(cfg);
312 }
313 
test_config_read__iterator(void)314 void test_config_read__iterator(void)
315 {
316 	const char *keys[] = {
317 		"core.dummy2",
318 		"core.verylong",
319 		"core.dummy",
320 		"remote.ab.url",
321 		"remote.abba.url",
322 		"core.dummy2",
323 		"core.global"
324 	};
325 	git_config *cfg;
326 	git_config_iterator *iter;
327 	git_config_entry *entry;
328 	int count, ret;
329 
330 	cl_git_pass(git_config_new(&cfg));
331 	cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config9"),
332 		GIT_CONFIG_LEVEL_SYSTEM, NULL, 0));
333 	cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config15"),
334 		GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
335 
336 	count = 0;
337 	cl_git_pass(git_config_iterator_new(&iter, cfg));
338 
339 	while ((ret = git_config_next(&entry, iter)) == 0) {
340 		cl_assert_equal_s(entry->name, keys[count]);
341 		count++;
342 	}
343 
344 	git_config_iterator_free(iter);
345 	cl_assert_equal_i(GIT_ITEROVER, ret);
346 	cl_assert_equal_i(7, count);
347 
348 	count = 3;
349 	cl_git_pass(git_config_iterator_new(&iter, cfg));
350 
351 	git_config_iterator_free(iter);
352 	git_config_free(cfg);
353 }
354 
count_cfg_entries(const git_config_entry * entry,void * payload)355 static int count_cfg_entries(const git_config_entry *entry, void *payload)
356 {
357 	int *count = payload;
358 	GIT_UNUSED(entry);
359 	(*count)++;
360 	return 0;
361 }
362 
test_config_read__foreach_match(void)363 void test_config_read__foreach_match(void)
364 {
365 	git_config *cfg;
366 	int count;
367 
368 	cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config9")));
369 
370 	count = 0;
371 	cl_git_pass(
372 		git_config_foreach_match(cfg, "core.*", count_cfg_entries, &count));
373 	cl_assert_equal_i(3, count);
374 
375 	count = 0;
376 	cl_git_pass(
377 		git_config_foreach_match(cfg, "remote\\.ab.*", count_cfg_entries, &count));
378 	cl_assert_equal_i(2, count);
379 
380 	count = 0;
381 	cl_git_pass(
382 		git_config_foreach_match(cfg, ".*url$", count_cfg_entries, &count));
383 	cl_assert_equal_i(2, count);
384 
385 	count = 0;
386 	cl_git_pass(
387 		git_config_foreach_match(cfg, ".*dummy.*", count_cfg_entries, &count));
388 	cl_assert_equal_i(2, count);
389 
390 	count = 0;
391 	cl_git_pass(
392 		git_config_foreach_match(cfg, ".*nomatch.*", count_cfg_entries, &count));
393 	cl_assert_equal_i(0, count);
394 
395 	git_config_free(cfg);
396 }
397 
check_glob_iter(git_config * cfg,const char * regexp,int expected)398 static void check_glob_iter(git_config *cfg, const char *regexp, int expected)
399 {
400 	git_config_iterator *iter;
401 	git_config_entry *entry;
402 	int count, error;
403 
404 	cl_git_pass(git_config_iterator_glob_new(&iter, cfg, regexp));
405 
406 	count = 0;
407 	while ((error = git_config_next(&entry, iter)) == 0)
408 		count++;
409 
410 	cl_assert_equal_i(GIT_ITEROVER, error);
411 	cl_assert_equal_i(expected, count);
412 	git_config_iterator_free(iter);
413 }
414 
test_config_read__iterator_invalid_glob(void)415 void test_config_read__iterator_invalid_glob(void)
416 {
417 	git_config *cfg;
418 	git_config_iterator *iter;
419 
420 	cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config9")));
421 
422 	cl_git_fail(git_config_iterator_glob_new(&iter, cfg, "*"));
423 
424 	git_config_free(cfg);
425 }
426 
test_config_read__iterator_glob(void)427 void test_config_read__iterator_glob(void)
428 {
429 	git_config *cfg;
430 
431 	cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config9")));
432 
433 	check_glob_iter(cfg, "core.*", 3);
434 	check_glob_iter(cfg, "remote\\.ab.*", 2);
435 	check_glob_iter(cfg, ".*url$", 2);
436 	check_glob_iter(cfg, ".*dummy.*", 2);
437 	check_glob_iter(cfg, ".*nomatch.*", 0);
438 
439 	git_config_free(cfg);
440 }
441 
test_config_read__whitespace_not_required_around_assignment(void)442 void test_config_read__whitespace_not_required_around_assignment(void)
443 {
444 	git_config *cfg;
445 
446 	cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config14")));
447 
448 	cl_git_pass(git_config_get_string_buf(&buf, cfg, "a.b"));
449 	cl_assert_equal_s("c", git_buf_cstr(&buf));
450 	git_buf_clear(&buf);
451 
452 	cl_git_pass(git_config_get_string_buf(&buf, cfg, "d.e"));
453 	cl_assert_equal_s("f", git_buf_cstr(&buf));
454 
455 	git_config_free(cfg);
456 }
457 
test_config_read__read_git_config_entry(void)458 void test_config_read__read_git_config_entry(void)
459 {
460 	git_config *cfg;
461 	git_config_entry *entry;
462 
463 	cl_git_pass(git_config_new(&cfg));
464 	cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config9"),
465 		GIT_CONFIG_LEVEL_SYSTEM, NULL, 0));
466 
467 	cl_git_pass(git_config_get_entry(&entry, cfg, "core.dummy2"));
468 	cl_assert_equal_s("core.dummy2", entry->name);
469 	cl_assert_equal_s("42", entry->value);
470 	cl_assert_equal_i(GIT_CONFIG_LEVEL_SYSTEM, entry->level);
471 
472 	git_config_entry_free(entry);
473 	git_config_free(cfg);
474 }
475 
476 /*
477  * At the beginning of the test:
478  *  - config9 has: core.dummy2=42
479  *  - config15 has: core.dummy2=7
480  *  - config16 has: core.dummy2=28
481  */
test_config_read__local_config_overrides_global_config_overrides_system_config(void)482 void test_config_read__local_config_overrides_global_config_overrides_system_config(void)
483 {
484 	git_config *cfg;
485 	int32_t i;
486 
487 	cl_git_pass(git_config_new(&cfg));
488 	cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config9"),
489 		GIT_CONFIG_LEVEL_SYSTEM, NULL, 0));
490 	cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config15"),
491 		GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
492 	cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config16"),
493 		GIT_CONFIG_LEVEL_LOCAL, NULL, 0));
494 
495 	cl_git_pass(git_config_get_int32(&i, cfg, "core.dummy2"));
496 	cl_assert_equal_i(28, i);
497 
498 	git_config_free(cfg);
499 
500 	cl_git_pass(git_config_new(&cfg));
501 	cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config9"),
502 		GIT_CONFIG_LEVEL_SYSTEM, NULL, 0));
503 	cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config15"),
504 		GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
505 
506 	cl_git_pass(git_config_get_int32(&i, cfg, "core.dummy2"));
507 	cl_assert_equal_i(7, i);
508 
509 	git_config_free(cfg);
510 }
511 
512 /*
513  * At the beginning of the test:
514  *  - config9 has: core.global does not exist
515  *  - config15 has: core.global=17
516  *  - config16 has: core.global=29
517  *
518  * And also:
519  *  - config9 has: core.system does not exist
520  *  - config15 has: core.system does not exist
521  *  - config16 has: core.system=11
522  */
test_config_read__fallback_from_local_to_global_and_from_global_to_system(void)523 void test_config_read__fallback_from_local_to_global_and_from_global_to_system(void)
524 {
525 	git_config *cfg;
526 	int32_t i;
527 
528 	cl_git_pass(git_config_new(&cfg));
529 	cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config9"),
530 		GIT_CONFIG_LEVEL_SYSTEM, NULL, 0));
531 	cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config15"),
532 		GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
533 	cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config16"),
534 		GIT_CONFIG_LEVEL_LOCAL, NULL, 0));
535 
536 	cl_git_pass(git_config_get_int32(&i, cfg, "core.global"));
537 	cl_assert_equal_i(17, i);
538 	cl_git_pass(git_config_get_int32(&i, cfg, "core.system"));
539 	cl_assert_equal_i(11, i);
540 
541 	git_config_free(cfg);
542 }
543 
test_config_read__parent_dir_is_file(void)544 void test_config_read__parent_dir_is_file(void)
545 {
546 	git_config *cfg;
547 	int count;
548 
549 	cl_git_pass(git_config_new(&cfg));
550 	/*
551 	 * Verify we can add non-existing files when the parent directory is not
552 	 * a directory.
553 	 */
554 	cl_git_pass(git_config_add_file_ondisk(cfg, "/dev/null/.gitconfig",
555 		GIT_CONFIG_LEVEL_SYSTEM, NULL, 0));
556 
557 	count = 0;
558 	cl_git_pass(git_config_foreach(cfg, count_cfg_entries_and_compare_levels, &count));
559 	cl_assert_equal_i(0, count);
560 
561 	git_config_free(cfg);
562 }
563 
564 /*
565  * At the beginning of the test, config18 has:
566  *	int32global = 28
567  *	int64global = 9223372036854775803
568  *	boolglobal = true
569  *	stringglobal = I'm a global config value!
570  *
571  * And config19 has:
572  *	int32global = -1
573  *	int64global = -2
574  *	boolglobal = false
575  *	stringglobal = don't find me!
576  *
577  */
test_config_read__simple_read_from_specific_level(void)578 void test_config_read__simple_read_from_specific_level(void)
579 {
580 	git_config *cfg, *cfg_specific;
581 	int i;
582 	int64_t l, expected = +9223372036854775803;
583 
584 	cl_git_pass(git_config_new(&cfg));
585 	cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config18"),
586 		GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
587 	cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config19"),
588 		GIT_CONFIG_LEVEL_SYSTEM, NULL, 0));
589 
590 	cl_git_pass(git_config_open_level(&cfg_specific, cfg, GIT_CONFIG_LEVEL_GLOBAL));
591 
592 	cl_git_pass(git_config_get_int32(&i, cfg_specific, "core.int32global"));
593 	cl_assert_equal_i(28, i);
594 	cl_git_pass(git_config_get_int64(&l, cfg_specific, "core.int64global"));
595 	cl_assert(l == expected);
596 	cl_git_pass(git_config_get_bool(&i, cfg_specific, "core.boolglobal"));
597 	cl_assert_equal_b(true, i);
598 	cl_git_pass(git_config_get_string_buf(&buf, cfg_specific, "core.stringglobal"));
599 	cl_assert_equal_s("I'm a global config value!", git_buf_cstr(&buf));
600 
601 	git_config_free(cfg_specific);
602 	git_config_free(cfg);
603 }
604 
test_config_read__can_load_and_parse_an_empty_config_file(void)605 void test_config_read__can_load_and_parse_an_empty_config_file(void)
606 {
607 	git_config *cfg;
608 	int i;
609 
610 	cl_set_cleanup(&clean_test_config, NULL);
611 	cl_git_mkfile("./testconfig", "");
612 	cl_git_pass(git_config_open_ondisk(&cfg, "./testconfig"));
613 	cl_assert_equal_i(GIT_ENOTFOUND, git_config_get_int32(&i, cfg, "nope.neither"));
614 
615 	git_config_free(cfg);
616 }
617 
test_config_read__corrupt_header(void)618 void test_config_read__corrupt_header(void)
619 {
620 	git_config *cfg;
621 
622 	cl_set_cleanup(&clean_test_config, NULL);
623 	cl_git_mkfile("./testconfig", "[sneaky ] \"quoted closing quote mark\\\"");
624 	cl_git_fail(git_config_open_ondisk(&cfg, "./testconfig"));
625 
626 	git_config_free(cfg);
627 }
628 
test_config_read__corrupt_header2(void)629 void test_config_read__corrupt_header2(void)
630 {
631 	git_config *cfg;
632 
633 	cl_set_cleanup(&clean_test_config, NULL);
634 	cl_git_mkfile("./testconfig", "[unclosed \"bracket\"\n    lib = git2\n");
635 	cl_git_fail(git_config_open_ondisk(&cfg, "./testconfig"));
636 
637 	git_config_free(cfg);
638 }
639 
test_config_read__corrupt_header3(void)640 void test_config_read__corrupt_header3(void)
641 {
642 	git_config *cfg;
643 
644 	cl_set_cleanup(&clean_test_config, NULL);
645 	cl_git_mkfile("./testconfig", "[unclosed \"slash\\\"]\n    lib = git2\n");
646 	cl_git_fail(git_config_open_ondisk(&cfg, "./testconfig"));
647 
648 	git_config_free(cfg);
649 }
650 
test_config_read__invalid_key_chars(void)651 void test_config_read__invalid_key_chars(void)
652 {
653 	git_config *cfg;
654 
655 	cl_set_cleanup(&clean_test_config, NULL);
656 	cl_git_mkfile("./testconfig", "[foo]\n    has_underscore = git2\n");
657 	cl_git_fail(git_config_open_ondisk(&cfg, "./testconfig"));
658 
659 	cl_git_rewritefile("./testconfig", "[foo]\n  has/slash = git2\n");
660 	cl_git_fail(git_config_open_ondisk(&cfg, "./testconfig"));
661 
662 	cl_git_rewritefile("./testconfig", "[foo]\n  has+plus = git2\n");
663 	cl_git_fail(git_config_open_ondisk(&cfg, "./testconfig"));
664 
665 	cl_git_rewritefile("./testconfig", "[no_key]\n  = git2\n");
666 	cl_git_fail(git_config_open_ondisk(&cfg, "./testconfig"));
667 
668 	git_config_free(cfg);
669 }
670 
test_config_read__lone_variable_with_trailing_whitespace(void)671 void test_config_read__lone_variable_with_trailing_whitespace(void)
672 {
673 	git_config *cfg;
674 	int b;
675 
676 	cl_set_cleanup(&clean_test_config, NULL);
677 	cl_git_mkfile("./testconfig", "[foo]\n    lonevariable   \n");
678 	cl_git_pass(git_config_open_ondisk(&cfg, "./testconfig"));
679 
680 	cl_git_pass(git_config_get_bool(&b, cfg, "foo.lonevariable"));
681 	cl_assert_equal_b(true, b);
682 
683 	git_config_free(cfg);
684 }
685 
test_config_read__override_variable(void)686 void test_config_read__override_variable(void)
687 {
688 	git_config *cfg;
689 
690 	cl_set_cleanup(&clean_test_config, NULL);
691 	cl_git_mkfile("./testconfig", "[some] var = one\nvar = two");
692 	cl_git_pass(git_config_open_ondisk(&cfg, "./testconfig"));
693 
694 	cl_git_pass(git_config_get_string_buf(&buf, cfg, "some.var"));
695 	cl_assert_equal_s("two", git_buf_cstr(&buf));
696 
697 	git_config_free(cfg);
698 }
699 
test_config_read__path(void)700 void test_config_read__path(void)
701 {
702 	git_config *cfg;
703 	git_buf path = GIT_BUF_INIT;
704 	git_buf old_path = GIT_BUF_INIT;
705 	git_buf home_path = GIT_BUF_INIT;
706 	git_buf expected_path = GIT_BUF_INIT;
707 
708 	cl_git_pass(p_mkdir("fakehome", 0777));
709 	cl_git_pass(git_path_prettify(&home_path, "fakehome", NULL));
710 	cl_git_pass(git_libgit2_opts(GIT_OPT_GET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, &old_path));
711 	cl_git_pass(git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, home_path.ptr));
712 	cl_git_mkfile("./testconfig", "[some]\n path = ~/somefile");
713 	cl_git_pass(git_path_join_unrooted(&expected_path, "somefile", home_path.ptr, NULL));
714 
715 	cl_git_pass(git_config_open_ondisk(&cfg, "./testconfig"));
716 	cl_git_pass(git_config_get_path(&path, cfg, "some.path"));
717 	cl_assert_equal_s(expected_path.ptr, path.ptr);
718 	git_buf_dispose(&path);
719 
720 	cl_git_mkfile("./testconfig", "[some]\n path = ~/");
721 	cl_git_pass(git_path_join_unrooted(&expected_path, "", home_path.ptr, NULL));
722 
723 	cl_git_pass(git_config_get_path(&path, cfg, "some.path"));
724 	cl_assert_equal_s(expected_path.ptr, path.ptr);
725 	git_buf_dispose(&path);
726 
727 	cl_git_mkfile("./testconfig", "[some]\n path = ~");
728 	cl_git_pass(git_buf_sets(&expected_path, home_path.ptr));
729 
730 	cl_git_pass(git_config_get_path(&path, cfg, "some.path"));
731 	cl_assert_equal_s(expected_path.ptr, path.ptr);
732 	git_buf_dispose(&path);
733 
734 	cl_git_mkfile("./testconfig", "[some]\n path = ~user/foo");
735 	cl_git_fail(git_config_get_path(&path, cfg, "some.path"));
736 
737 	cl_git_pass(git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, old_path.ptr));
738 	git_buf_dispose(&old_path);
739 	git_buf_dispose(&home_path);
740 	git_buf_dispose(&expected_path);
741 	git_config_free(cfg);
742 }
743 
test_config_read__crlf_style_line_endings(void)744 void test_config_read__crlf_style_line_endings(void)
745 {
746 	git_buf buf = GIT_BUF_INIT;
747 	git_config *cfg;
748 
749 	cl_set_cleanup(&clean_test_config, NULL);
750 	cl_git_mkfile("./testconfig", "[some]\r\n var = value\r\n");
751 	cl_git_pass(git_config_open_ondisk(&cfg, "./testconfig"));
752 	cl_git_pass(git_config_get_string_buf(&buf, cfg, "some.var"));
753 	cl_assert_equal_s(buf.ptr, "value");
754 
755 	git_config_free(cfg);
756 	git_buf_dispose(&buf);
757 }
758 
test_config_read__trailing_crlf(void)759 void test_config_read__trailing_crlf(void)
760 {
761 	git_buf buf = GIT_BUF_INIT;
762 	git_config *cfg;
763 
764 	cl_set_cleanup(&clean_test_config, NULL);
765 	cl_git_mkfile("./testconfig", "[some]\r\n var = value\r\n\r\n");
766 	cl_git_pass(git_config_open_ondisk(&cfg, "./testconfig"));
767 	cl_git_pass(git_config_get_string_buf(&buf, cfg, "some.var"));
768 	cl_assert_equal_s(buf.ptr, "value");
769 
770 	git_config_free(cfg);
771 	git_buf_dispose(&buf);
772 }
773 
test_config_read__bom(void)774 void test_config_read__bom(void)
775 {
776 	git_buf buf = GIT_BUF_INIT;
777 	git_config *cfg;
778 
779 	cl_set_cleanup(&clean_test_config, NULL);
780 	cl_git_mkfile("./testconfig", "\xEF\xBB\xBF[some]\n var = value\n");
781 	cl_git_pass(git_config_open_ondisk(&cfg, "./testconfig"));
782 	cl_git_pass(git_config_get_string_buf(&buf, cfg, "some.var"));
783 	cl_assert_equal_s(buf.ptr, "value");
784 
785 	git_config_free(cfg);
786 	git_buf_dispose(&buf);
787 }
788 
test_config_read__arbitrary_whitespace_before_subsection(void)789 void test_config_read__arbitrary_whitespace_before_subsection(void)
790 {
791 	git_buf buf = GIT_BUF_INIT;
792 	git_config *cfg;
793 
794 	cl_set_cleanup(&clean_test_config, NULL);
795 	cl_git_mkfile("./testconfig", "[some \t \"subsection\"]\n var = value\n");
796 	cl_git_pass(git_config_open_ondisk(&cfg, "./testconfig"));
797 	cl_git_pass(git_config_get_string_buf(&buf, cfg, "some.subsection.var"));
798 	cl_assert_equal_s(buf.ptr, "value");
799 
800 	git_config_free(cfg);
801 	git_buf_dispose(&buf);
802 }
803 
test_config_read__no_whitespace_after_subsection(void)804 void test_config_read__no_whitespace_after_subsection(void)
805 {
806 	git_config *cfg;
807 
808 	cl_set_cleanup(&clean_test_config, NULL);
809 	cl_git_mkfile("./testconfig", "[some \"subsection\" ]\n var = value\n");
810 	cl_git_fail(git_config_open_ondisk(&cfg, "./testconfig"));
811 
812 	git_config_free(cfg);
813 }
814 
test_config_read__invalid_space_section(void)815 void test_config_read__invalid_space_section(void)
816 {
817 	git_config *cfg;
818 
819 	cl_set_cleanup(&clean_test_config, NULL);
820 	cl_git_mkfile("./testconfig", "\xEF\xBB\xBF[some section]\n var = value\n");
821 	cl_git_fail(git_config_open_ondisk(&cfg, "./testconfig"));
822 
823 	git_config_free(cfg);
824 }
825 
test_config_read__invalid_quoted_first_section(void)826 void test_config_read__invalid_quoted_first_section(void)
827 {
828 	git_config *cfg;
829 
830 	cl_set_cleanup(&clean_test_config, NULL);
831 	cl_git_mkfile("./testconfig", "\xEF\xBB\xBF[\"some\"]\n var = value\n");
832 	cl_git_fail(git_config_open_ondisk(&cfg, "./testconfig"));
833 
834 	git_config_free(cfg);
835 }
836 
test_config_read__invalid_unquoted_subsection(void)837 void test_config_read__invalid_unquoted_subsection(void)
838 {
839 	git_config *cfg;
840 
841 	cl_set_cleanup(&clean_test_config, NULL);
842 	cl_git_mkfile("./testconfig", "\xEF\xBB\xBF[some sub section]\n var = value\n");
843 	cl_git_fail(git_config_open_ondisk(&cfg, "./testconfig"));
844 
845 	git_config_free(cfg);
846 }
847 
test_config_read__invalid_quoted_third_section(void)848 void test_config_read__invalid_quoted_third_section(void)
849 {
850 	git_config *cfg;
851 
852 	cl_set_cleanup(&clean_test_config, NULL);
853 	cl_git_mkfile("./testconfig", "\xEF\xBB\xBF[some sub \"section\"]\n var = value\n");
854 	cl_git_fail(git_config_open_ondisk(&cfg, "./testconfig"));
855 
856 	git_config_free(cfg);
857 }
858 
test_config_read__unreadable_file_ignored(void)859 void test_config_read__unreadable_file_ignored(void)
860 {
861 	git_buf buf = GIT_BUF_INIT;
862 	git_config *cfg;
863 	int ret;
864 
865 	cl_set_cleanup(&clean_test_config, NULL);
866 	cl_git_mkfile("./testconfig", "[some] var = value\n[some \"OtheR\"] var = value");
867 	cl_git_pass(p_chmod("./testconfig", 0));
868 
869 	ret = git_config_open_ondisk(&cfg, "./test/config");
870 	cl_assert(ret == 0 || ret == GIT_ENOTFOUND);
871 
872 	git_config_free(cfg);
873 	git_buf_dispose(&buf);
874 }
875 
test_config_read__single_line(void)876 void test_config_read__single_line(void)
877 {
878 	git_buf buf = GIT_BUF_INIT;
879 	git_config *cfg;
880 
881 	cl_set_cleanup(&clean_test_config, NULL);
882 	cl_git_mkfile("./testconfig", "[some] var = value\n[some \"OtheR\"] var = value");
883 	cl_git_pass(git_config_open_ondisk(&cfg, "./testconfig"));
884 	cl_git_pass(git_config_get_string_buf(&buf, cfg, "some.var"));
885 	cl_assert_equal_s(buf.ptr, "value");
886 
887 	git_buf_clear(&buf);
888 	cl_git_pass(git_config_get_string_buf(&buf, cfg, "some.OtheR.var"));
889 	cl_assert_equal_s(buf.ptr, "value");
890 
891 	git_config_free(cfg);
892 	cl_git_mkfile("./testconfig", "[some] var = value\n[some \"OtheR\"]var = value");
893 	cl_git_pass(git_config_open_ondisk(&cfg, "./testconfig"));
894 	git_buf_clear(&buf);
895 	cl_git_pass(git_config_get_string_buf(&buf, cfg, "some.var"));
896 	cl_assert_equal_s(buf.ptr, "value");
897 
898 	git_buf_clear(&buf);
899 	cl_git_pass(git_config_get_string_buf(&buf, cfg, "some.OtheR.var"));
900 	cl_assert_equal_s(buf.ptr, "value");
901 
902 	git_config_free(cfg);
903 	git_buf_dispose(&buf);
904 }
905 
read_nosection_cb(const git_config_entry * entry,void * payload)906 static int read_nosection_cb(const git_config_entry *entry, void *payload) {
907 	int *seen = (int*)payload;
908 	if (strcmp(entry->name, "key") == 0) {
909 		(*seen)++;
910 	}
911 	return 0;
912 }
913 
914 /* This would ideally issue a warning, if we had a way to do so. */
test_config_read__nosection(void)915 void test_config_read__nosection(void)
916 {
917 	git_config *cfg;
918 	git_buf buf = GIT_BUF_INIT;
919 	int seen = 0;
920 
921 	cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config-nosection")));
922 
923 	/*
924 	 * Given a key with no section, we do not allow reading it,
925 	 * but we do include it in an iteration over the config
926 	 * store. This appears to match how git's own APIs (and
927 	 * git-config(1)) behave.
928 	 */
929 
930 	cl_git_fail_with(git_config_get_string_buf(&buf, cfg, "key"), GIT_EINVALIDSPEC);
931 
932 	cl_git_pass(git_config_foreach(cfg, read_nosection_cb, &seen));
933 	cl_assert_equal_i(seen, 1);
934 
935 	git_buf_dispose(&buf);
936 	git_config_free(cfg);
937 }
938 
939 enum {
940 	MAP_TRUE = 0,
941 	MAP_FALSE = 1,
942 	MAP_ALWAYS = 2
943 };
944 
945 static git_configmap _test_map1[] = {
946 	{GIT_CONFIGMAP_STRING, "always", MAP_ALWAYS},
947 	{GIT_CONFIGMAP_FALSE, NULL, MAP_FALSE},
948 	{GIT_CONFIGMAP_TRUE, NULL, MAP_TRUE},
949 };
950 
951 static git_configmap _test_map2[] = {
952 	{GIT_CONFIGMAP_INT32, NULL, 0},
953 };
954 
test_config_read__get_mapped(void)955 void test_config_read__get_mapped(void)
956 {
957 	git_config *cfg;
958 	int val;
959 	int known_good;
960 
961 	cl_set_cleanup(&clean_test_config, NULL);
962 	cl_git_mkfile("./testconfig", "[header]\n"
963 								  "  key1 = 1\n"
964 								  "  key2 = true\n"
965 								  "  key3\n"
966 								  "  key4 = always\n"
967 								  "  key5 = false\n"
968 								  "  key6 = 0\n"
969 								  "  key7 = never\n"
970 								  "  key8 = On\n"
971 								  "  key9 = off\n");
972 	cl_git_pass(git_config_open_ondisk(&cfg, "./testconfig"));
973 
974 	/* check parsing bool and string */
975 	cl_git_pass(git_config_get_mapped(&val, cfg, "header.key1", _test_map1, ARRAY_SIZE(_test_map1)));
976 	cl_assert_equal_i(val, MAP_TRUE);
977 	cl_git_pass(git_config_get_mapped(&val, cfg, "header.key2", _test_map1, ARRAY_SIZE(_test_map1)));
978 	cl_assert_equal_i(val, MAP_TRUE);
979 	cl_git_pass(git_config_get_mapped(&val, cfg, "header.key3", _test_map1, ARRAY_SIZE(_test_map1)));
980 	cl_assert_equal_i(val, MAP_TRUE);
981 	cl_git_pass(git_config_get_mapped(&val, cfg, "header.key8", _test_map1, ARRAY_SIZE(_test_map1)));
982 	cl_assert_equal_i(val, MAP_TRUE);
983 
984 	cl_git_pass(git_config_get_mapped(&val, cfg, "header.key4", _test_map1, ARRAY_SIZE(_test_map1)));
985 	cl_assert_equal_i(val, MAP_ALWAYS);
986 
987 	cl_git_pass(git_config_get_mapped(&val, cfg, "header.key5", _test_map1, ARRAY_SIZE(_test_map1)));
988 	cl_assert_equal_i(val, MAP_FALSE);
989 	cl_git_pass(git_config_get_mapped(&val, cfg, "header.key6", _test_map1, ARRAY_SIZE(_test_map1)));
990 	cl_assert_equal_i(val, MAP_FALSE);
991 	cl_git_pass(git_config_get_mapped(&val, cfg, "header.key9", _test_map1, ARRAY_SIZE(_test_map1)));
992 	cl_assert_equal_i(val, MAP_FALSE);
993 
994 	cl_git_fail(git_config_get_mapped(&val, cfg, "header.key7", _test_map1, ARRAY_SIZE(_test_map1)));
995 
996 	/* check parsing int values */
997 	cl_git_pass(git_config_get_mapped(&val, cfg, "header.key1", _test_map2, ARRAY_SIZE(_test_map2)));
998 	cl_git_pass(git_config_get_int32(&known_good, cfg, "header.key1"));
999 	cl_assert_equal_i(val, known_good);
1000 	cl_git_pass(git_config_get_mapped(&val, cfg, "header.key6", _test_map2, ARRAY_SIZE(_test_map2)));
1001 	cl_git_pass(git_config_get_int32(&known_good, cfg, "header.key6"));
1002 	cl_assert_equal_i(val, known_good);
1003 
1004 	cl_git_fail(git_config_get_mapped(&val, cfg, "header.key2", _test_map2, ARRAY_SIZE(_test_map2)));
1005 	cl_git_fail(git_config_get_mapped(&val, cfg, "header.key3", _test_map2, ARRAY_SIZE(_test_map2)));
1006 	cl_git_fail(git_config_get_mapped(&val, cfg, "header.key4", _test_map2, ARRAY_SIZE(_test_map2)));
1007 	cl_git_fail(git_config_get_mapped(&val, cfg, "header.key5", _test_map2, ARRAY_SIZE(_test_map2)));
1008 	cl_git_fail(git_config_get_mapped(&val, cfg, "header.key7", _test_map2, ARRAY_SIZE(_test_map2)));
1009 	cl_git_fail(git_config_get_mapped(&val, cfg, "header.key8", _test_map2, ARRAY_SIZE(_test_map2)));
1010 	cl_git_fail(git_config_get_mapped(&val, cfg, "header.key9", _test_map2, ARRAY_SIZE(_test_map2)));
1011 
1012 	git_config_free(cfg);
1013 }
1014