File Coverage

deps/libgit2/src/config.c
Criterion Covered Total %
statement 421 661 63.6
branch 203 454 44.7
condition n/a
subroutine n/a
pod n/a
total 624 1115 55.9


line stmt bran cond sub pod time code
1             /*
2             * Copyright (C) the libgit2 contributors. All rights reserved.
3             *
4             * This file is part of libgit2, distributed under the GNU GPL v2 with
5             * a Linking Exception. For full terms see the included COPYING file.
6             */
7              
8             #include "config.h"
9              
10             #include "git2/config.h"
11             #include "git2/sys/config.h"
12              
13             #include "buf_text.h"
14             #include "config_backend.h"
15             #include "regexp.h"
16             #include "sysdir.h"
17             #include "transaction.h"
18             #include "vector.h"
19             #if GIT_WIN32
20             # include
21             #endif
22              
23             #include
24              
25 2990           void git_config_entry_free(git_config_entry *entry)
26             {
27 2990 100         if (!entry)
28 2097           return;
29              
30 893           entry->free(entry);
31             }
32              
33             typedef struct {
34             git_refcount rc;
35              
36             git_config_backend *backend;
37             git_config_level_t level;
38             } backend_internal;
39              
40 1134           static void backend_internal_free(backend_internal *internal)
41             {
42             git_config_backend *backend;
43              
44 1134           backend = internal->backend;
45 1134           backend->free(backend);
46 1134           git__free(internal);
47 1134           }
48              
49 571           static void config_free(git_config *cfg)
50             {
51             size_t i;
52             backend_internal *internal;
53              
54 1705 100         for (i = 0; i < cfg->backends.length; ++i) {
55 1134           internal = git_vector_get(&cfg->backends, i);
56 1134 50         GIT_REFCOUNT_DEC(internal, backend_internal_free);
    50          
57             }
58              
59 571           git_vector_free(&cfg->backends);
60              
61 571           git__memzero(cfg, sizeof(*cfg));
62 571           git__free(cfg);
63 571           }
64              
65 591           void git_config_free(git_config *cfg)
66             {
67 591 100         if (cfg == NULL)
68 5           return;
69              
70 586 100         GIT_REFCOUNT_DEC(cfg, config_free);
    50          
71             }
72              
73 565           static int config_backend_cmp(const void *a, const void *b)
74             {
75 565           const backend_internal *bk_a = (const backend_internal *)(a);
76 565           const backend_internal *bk_b = (const backend_internal *)(b);
77              
78 565           return bk_b->level - bk_a->level;
79             }
80              
81 573           int git_config_new(git_config **out)
82             {
83             git_config *cfg;
84              
85 573           cfg = git__malloc(sizeof(git_config));
86 573 50         GIT_ERROR_CHECK_ALLOC(cfg);
87              
88 573           memset(cfg, 0x0, sizeof(git_config));
89              
90 573 50         if (git_vector_init(&cfg->backends, 3, config_backend_cmp) < 0) {
91 0           git__free(cfg);
92 0           return -1;
93             }
94              
95 573           *out = cfg;
96 573           GIT_REFCOUNT_INC(cfg);
97 573           return 0;
98             }
99              
100 134           int git_config_add_file_ondisk(
101             git_config *cfg,
102             const char *path,
103             git_config_level_t level,
104             const git_repository *repo,
105             int force)
106             {
107 134           git_config_backend *file = NULL;
108             struct stat st;
109             int res;
110              
111 134 50         assert(cfg && path);
    50          
112              
113 134           res = p_stat(path, &st);
114 134 100         if (res < 0 && errno != ENOENT && errno != ENOTDIR) {
    50          
    0          
115 0           git_error_set(GIT_ERROR_CONFIG, "failed to stat '%s'", path);
116 0           return -1;
117             }
118              
119 134 50         if (git_config_backend_from_file(&file, path) < 0)
120 0           return -1;
121              
122 134 50         if ((res = git_config_add_backend(cfg, file, level, repo, force)) < 0) {
123             /*
124             * free manually; the file is not owned by the config
125             * instance yet and will not be freed on cleanup
126             */
127 0           file->free(file);
128 0           return res;
129             }
130              
131 134           return 0;
132             }
133              
134 5           int git_config_open_ondisk(git_config **out, const char *path)
135             {
136             int error;
137             git_config *config;
138              
139 5           *out = NULL;
140              
141 5 50         if (git_config_new(&config) < 0)
142 0           return -1;
143              
144 5 50         if ((error = git_config_add_file_ondisk(config, path, GIT_CONFIG_LEVEL_LOCAL, NULL, 0)) < 0)
145 0           git_config_free(config);
146             else
147 5           *out = config;
148              
149 5           return error;
150             }
151              
152 502           int git_config_snapshot(git_config **out, git_config *in)
153             {
154 502           int error = 0;
155             size_t i;
156             backend_internal *internal;
157             git_config *config;
158              
159 502           *out = NULL;
160              
161 502 50         if (git_config_new(&config) < 0)
162 0           return -1;
163              
164 1506 100         git_vector_foreach(&in->backends, i, internal) {
165             git_config_backend *b;
166              
167 1004 50         if ((error = internal->backend->snapshot(&b, internal->backend)) < 0)
168 0           break;
169              
170 1004 50         if ((error = git_config_add_backend(config, b, internal->level, NULL, 0)) < 0) {
171 0           b->free(b);
172 0           break;
173             }
174             }
175              
176 502 50         if (error < 0)
177 0           git_config_free(config);
178             else
179 502           *out = config;
180              
181 502           return error;
182             }
183              
184 0           static int find_backend_by_level(
185             backend_internal **out,
186             const git_config *cfg,
187             git_config_level_t level)
188             {
189 0           int pos = -1;
190             backend_internal *internal;
191             size_t i;
192              
193             /* when passing GIT_CONFIG_HIGHEST_LEVEL, the idea is to get the config backend
194             * which has the highest level. As config backends are stored in a vector
195             * sorted by decreasing order of level, getting the backend at position 0
196             * will do the job.
197             */
198 0 0         if (level == GIT_CONFIG_HIGHEST_LEVEL) {
199 0           pos = 0;
200             } else {
201 0 0         git_vector_foreach(&cfg->backends, i, internal) {
202 0 0         if (internal->level == level)
203 0           pos = (int)i;
204             }
205             }
206              
207 0 0         if (pos == -1) {
208 0           git_error_set(GIT_ERROR_CONFIG,
209             "no configuration exists for the given level '%i'", (int)level);
210 0           return GIT_ENOTFOUND;
211             }
212              
213 0           *out = git_vector_get(&cfg->backends, pos);
214              
215 0           return 0;
216             }
217              
218 0           static int duplicate_level(void **old_raw, void *new_raw)
219             {
220 0           backend_internal **old = (backend_internal **)old_raw;
221              
222             GIT_UNUSED(new_raw);
223              
224 0           git_error_set(GIT_ERROR_CONFIG, "there already exists a configuration for the given level (%i)", (int)(*old)->level);
225 0           return GIT_EEXISTS;
226             }
227              
228 0           static void try_remove_existing_backend(
229             git_config *cfg,
230             git_config_level_t level)
231             {
232 0           int pos = -1;
233             backend_internal *internal;
234             size_t i;
235              
236 0 0         git_vector_foreach(&cfg->backends, i, internal) {
237 0 0         if (internal->level == level)
238 0           pos = (int)i;
239             }
240              
241 0 0         if (pos == -1)
242 0           return;
243              
244 0           internal = git_vector_get(&cfg->backends, pos);
245              
246 0 0         if (git_vector_remove(&cfg->backends, pos) < 0)
247 0           return;
248              
249 0 0         GIT_REFCOUNT_DEC(internal, backend_internal_free);
    0          
250             }
251              
252 1138           static int git_config__add_internal(
253             git_config *cfg,
254             backend_internal *internal,
255             git_config_level_t level,
256             int force)
257             {
258             int result;
259              
260             /* delete existing config backend for level if it exists */
261 1138 50         if (force)
262 0           try_remove_existing_backend(cfg, level);
263              
264 1138 50         if ((result = git_vector_insert_sorted(&cfg->backends,
265             internal, &duplicate_level)) < 0)
266 0           return result;
267              
268 1138           git_vector_sort(&cfg->backends);
269 1138           internal->backend->cfg = cfg;
270              
271 1138           GIT_REFCOUNT_INC(internal);
272              
273 1138           return 0;
274             }
275              
276 0           int git_config_open_global(git_config **cfg_out, git_config *cfg)
277             {
278 0 0         if (!git_config_open_level(cfg_out, cfg, GIT_CONFIG_LEVEL_XDG))
279 0           return 0;
280              
281 0           return git_config_open_level(cfg_out, cfg, GIT_CONFIG_LEVEL_GLOBAL);
282             }
283              
284 0           int git_config_open_level(
285             git_config **cfg_out,
286             const git_config *cfg_parent,
287             git_config_level_t level)
288             {
289             git_config *cfg;
290             backend_internal *internal;
291             int res;
292              
293 0 0         if ((res = find_backend_by_level(&internal, cfg_parent, level)) < 0)
294 0           return res;
295              
296 0 0         if ((res = git_config_new(&cfg)) < 0)
297 0           return res;
298              
299 0 0         if ((res = git_config__add_internal(cfg, internal, level, true)) < 0) {
300 0           git_config_free(cfg);
301 0           return res;
302             }
303              
304 0           *cfg_out = cfg;
305              
306 0           return 0;
307             }
308              
309 1138           int git_config_add_backend(
310             git_config *cfg,
311             git_config_backend *backend,
312             git_config_level_t level,
313             const git_repository *repo,
314             int force)
315             {
316             backend_internal *internal;
317             int result;
318              
319 1138 50         assert(cfg && backend);
    50          
320              
321 1138 50         GIT_ERROR_CHECK_VERSION(backend, GIT_CONFIG_BACKEND_VERSION, "git_config_backend");
322              
323 1138 50         if ((result = backend->open(backend, level, repo)) < 0)
324 0           return result;
325              
326 1138           internal = git__malloc(sizeof(backend_internal));
327 1138 50         GIT_ERROR_CHECK_ALLOC(internal);
328              
329 1138           memset(internal, 0x0, sizeof(backend_internal));
330              
331 1138           internal->backend = backend;
332 1138           internal->level = level;
333              
334 1138 50         if ((result = git_config__add_internal(cfg, internal, level, force)) < 0) {
335 0           git__free(internal);
336 0           return result;
337             }
338              
339 1138           return 0;
340             }
341              
342             /*
343             * Loop over all the variables
344             */
345              
346             typedef struct {
347             git_config_iterator parent;
348             git_config_iterator *current;
349             const git_config *cfg;
350             git_regexp regex;
351             size_t i;
352             } all_iter;
353              
354 155           static int find_next_backend(size_t *out, const git_config *cfg, size_t i)
355             {
356             backend_internal *internal;
357              
358 155 100         for (; i > 0; --i) {
359 104           internal = git_vector_get(&cfg->backends, i - 1);
360 104 50         if (!internal || !internal->backend)
    50          
361 0           continue;
362              
363 104           *out = i;
364 104           return 0;
365             }
366              
367 51           return -1;
368             }
369              
370 905           static int all_iter_next(git_config_entry **entry, git_config_iterator *_iter)
371             {
372 905           all_iter *iter = (all_iter *) _iter;
373             backend_internal *internal;
374             git_config_backend *backend;
375             size_t i;
376 905           int error = 0;
377              
378 905 100         if (iter->current != NULL &&
    100          
379 853           (error = iter->current->next(entry, iter->current)) == 0) {
380 802           return 0;
381             }
382              
383 103 100         if (error < 0 && error != GIT_ITEROVER)
    50          
384 0           return error;
385              
386             do {
387 155 100         if (find_next_backend(&i, iter->cfg, iter->i) < 0)
388 51           return GIT_ITEROVER;
389              
390 104           internal = git_vector_get(&iter->cfg->backends, i - 1);
391 104           backend = internal->backend;
392 104           iter->i = i - 1;
393              
394 104 100         if (iter->current)
395 52           iter->current->free(iter->current);
396              
397 104           iter->current = NULL;
398 104           error = backend->iterator(&iter->current, backend);
399 104 50         if (error == GIT_ENOTFOUND)
400 0           continue;
401              
402 104 50         if (error < 0)
403 0           return error;
404              
405 104           error = iter->current->next(entry, iter->current);
406             /* If this backend is empty, then keep going */
407 104 100         if (error == GIT_ITEROVER)
408 52           continue;
409              
410 52           return error;
411              
412 957           } while(1);
413              
414             return GIT_ITEROVER;
415             }
416              
417 40           static int all_iter_glob_next(git_config_entry **entry, git_config_iterator *_iter)
418             {
419             int error;
420 40           all_iter *iter = (all_iter *) _iter;
421              
422             /*
423             * We use the "normal" function to grab the next one across
424             * backends and then apply the regex
425             */
426 479 100         while ((error = all_iter_next(entry, _iter)) == 0) {
427             /* skip non-matching keys if regexp was provided */
428 453 100         if (git_regexp_match(&iter->regex, (*entry)->name) != 0)
429 439           continue;
430              
431             /* and simply return if we like the entry's name */
432 14           return 0;
433             }
434              
435 26           return error;
436             }
437              
438 52           static void all_iter_free(git_config_iterator *_iter)
439             {
440 52           all_iter *iter = (all_iter *) _iter;
441              
442 52 50         if (iter->current)
443 52           iter->current->free(iter->current);
444              
445 52           git__free(iter);
446 52           }
447              
448 26           static void all_iter_glob_free(git_config_iterator *_iter)
449             {
450 26           all_iter *iter = (all_iter *) _iter;
451              
452 26           git_regexp_dispose(&iter->regex);
453 26           all_iter_free(_iter);
454 26           }
455              
456 26           int git_config_iterator_new(git_config_iterator **out, const git_config *cfg)
457             {
458             all_iter *iter;
459              
460 26           iter = git__calloc(1, sizeof(all_iter));
461 26 50         GIT_ERROR_CHECK_ALLOC(iter);
462              
463 26           iter->parent.free = all_iter_free;
464 26           iter->parent.next = all_iter_next;
465              
466 26           iter->i = cfg->backends.length;
467 26           iter->cfg = cfg;
468              
469 26           *out = (git_config_iterator *) iter;
470              
471 26           return 0;
472             }
473              
474 28           int git_config_iterator_glob_new(git_config_iterator **out, const git_config *cfg, const char *regexp)
475             {
476             all_iter *iter;
477             int result;
478              
479 28 100         if (regexp == NULL)
480 2           return git_config_iterator_new(out, cfg);
481              
482 26           iter = git__calloc(1, sizeof(all_iter));
483 26 50         GIT_ERROR_CHECK_ALLOC(iter);
484              
485 26 50         if ((result = git_regexp_compile(&iter->regex, regexp, 0)) < 0) {
486 0           git__free(iter);
487 0           return -1;
488             }
489              
490 26           iter->parent.next = all_iter_glob_next;
491 26           iter->parent.free = all_iter_glob_free;
492 26           iter->i = cfg->backends.length;
493 26           iter->cfg = cfg;
494              
495 26           *out = (git_config_iterator *) iter;
496              
497 26           return 0;
498             }
499              
500 2           int git_config_foreach(
501             const git_config *cfg, git_config_foreach_cb cb, void *payload)
502             {
503 2           return git_config_foreach_match(cfg, NULL, cb, payload);
504             }
505              
506 0           int git_config_backend_foreach_match(
507             git_config_backend *backend,
508             const char *regexp,
509             git_config_foreach_cb cb,
510             void *payload)
511             {
512             git_config_entry *entry;
513             git_config_iterator* iter;
514             git_regexp regex;
515 0           int error = 0;
516              
517 0 0         assert(backend && cb);
    0          
518              
519 0 0         if (regexp && git_regexp_compile(®ex, regexp, 0) < 0)
    0          
520 0           return -1;
521              
522 0 0         if ((error = backend->iterator(&iter, backend)) < 0) {
523 0           iter = NULL;
524 0           return -1;
525             }
526              
527 0 0         while (!(iter->next(&entry, iter) < 0)) {
528             /* skip non-matching keys if regexp was provided */
529 0 0         if (regexp && git_regexp_match(®ex, entry->name) != 0)
    0          
530 0           continue;
531              
532             /* abort iterator on non-zero return value */
533 0 0         if ((error = cb(entry, payload)) != 0) {
534 0           git_error_set_after_callback(error);
535 0           break;
536             }
537             }
538              
539 0 0         if (regexp != NULL)
540 0           git_regexp_dispose(®ex);
541              
542 0           iter->free(iter);
543              
544 0           return error;
545             }
546              
547 9           int git_config_foreach_match(
548             const git_config *cfg,
549             const char *regexp,
550             git_config_foreach_cb cb,
551             void *payload)
552             {
553             int error;
554             git_config_iterator *iter;
555             git_config_entry *entry;
556              
557 9 50         if ((error = git_config_iterator_glob_new(&iter, cfg, regexp)) < 0)
558 0           return error;
559              
560 34 100         while (!(error = git_config_next(&entry, iter))) {
561 26 100         if ((error = cb(entry, payload)) != 0) {
562 1           git_error_set_after_callback(error);
563 1           break;
564             }
565             }
566              
567 9           git_config_iterator_free(iter);
568              
569 9 100         if (error == GIT_ITEROVER)
570 8           error = 0;
571              
572 9           return error;
573             }
574              
575             /**************
576             * Setters
577             **************/
578              
579             typedef enum {
580             BACKEND_USE_SET,
581             BACKEND_USE_DELETE
582             } backend_use;
583              
584             static const char *uses[] = {
585             "set",
586             "delete"
587             };
588              
589 60           static int get_backend_for_use(git_config_backend **out,
590             git_config *cfg, const char *name, backend_use use)
591             {
592             size_t i;
593             backend_internal *backend;
594              
595 60           *out = NULL;
596              
597 60 50         if (git_vector_length(&cfg->backends) == 0) {
598 0           git_error_set(GIT_ERROR_CONFIG,
599             "cannot %s value for '%s' when no config backends exist",
600             uses[use], name);
601 0           return GIT_ENOTFOUND;
602             }
603              
604 60 50         git_vector_foreach(&cfg->backends, i, backend) {
605 60 50         if (!backend->backend->readonly) {
606 60           *out = backend->backend;
607 60           return 0;
608             }
609             }
610              
611 0           git_error_set(GIT_ERROR_CONFIG,
612             "cannot %s value for '%s' when all config backends are readonly",
613             uses[use], name);
614 0           return GIT_ENOTFOUND;
615             }
616              
617 15           int git_config_delete_entry(git_config *cfg, const char *name)
618             {
619             git_config_backend *backend;
620              
621 15 50         if (get_backend_for_use(&backend, cfg, name, BACKEND_USE_DELETE) < 0)
622 0           return GIT_ENOTFOUND;
623              
624 15           return backend->del(backend, name);
625             }
626              
627 6           int git_config_set_int64(git_config *cfg, const char *name, int64_t value)
628             {
629             char str_value[32]; /* All numbers should fit in here */
630 6           p_snprintf(str_value, sizeof(str_value), "%" PRId64, value);
631 6           return git_config_set_string(cfg, name, str_value);
632             }
633              
634 6           int git_config_set_int32(git_config *cfg, const char *name, int32_t value)
635             {
636 6           return git_config_set_int64(cfg, name, (int64_t)value);
637             }
638              
639 17           int git_config_set_bool(git_config *cfg, const char *name, int value)
640             {
641 17 100         return git_config_set_string(cfg, name, value ? "true" : "false");
642             }
643              
644 40           int git_config_set_string(git_config *cfg, const char *name, const char *value)
645             {
646             int error;
647             git_config_backend *backend;
648              
649 40 50         if (!value) {
650 0           git_error_set(GIT_ERROR_CONFIG, "the value to set cannot be NULL");
651 0           return -1;
652             }
653              
654 40 50         if (get_backend_for_use(&backend, cfg, name, BACKEND_USE_SET) < 0)
655 0           return GIT_ENOTFOUND;
656              
657 40           error = backend->set(backend, name, value);
658              
659 40 50         if (!error && GIT_REFCOUNT_OWNER(cfg) != NULL)
    100          
660 21           git_repository__configmap_lookup_cache_clear(GIT_REFCOUNT_OWNER(cfg));
661              
662 40           return error;
663             }
664              
665 0           int git_config__update_entry(
666             git_config *config,
667             const char *key,
668             const char *value,
669             bool overwrite_existing,
670             bool only_if_existing)
671             {
672 0           int error = 0;
673 0           git_config_entry *ce = NULL;
674              
675 0 0         if ((error = git_config__lookup_entry(&ce, config, key, false)) < 0)
676 0           return error;
677              
678 0 0         if (!ce && only_if_existing) /* entry doesn't exist */
    0          
679 0           return 0;
680 0 0         if (ce && !overwrite_existing) /* entry would be overwritten */
    0          
681 0           return 0;
682 0 0         if (value && ce && ce->value && !strcmp(ce->value, value)) /* no change */
    0          
    0          
    0          
683 0           return 0;
684 0 0         if (!value && (!ce || !ce->value)) /* asked to delete absent entry */
    0          
    0          
685 0           return 0;
686              
687 0 0         if (!value)
688 0           error = git_config_delete_entry(config, key);
689             else
690 0           error = git_config_set_string(config, key, value);
691              
692 0           git_config_entry_free(ce);
693 0           return error;
694             }
695              
696             /***********
697             * Getters
698             ***********/
699              
700 167           static int config_error_notfound(const char *name)
701             {
702 167           git_error_set(GIT_ERROR_CONFIG, "config value '%s' was not found", name);
703 167           return GIT_ENOTFOUND;
704             }
705              
706             enum {
707             GET_ALL_ERRORS = 0,
708             GET_NO_MISSING = 1,
709             GET_NO_ERRORS = 2
710             };
711              
712 3049           static int get_entry(
713             git_config_entry **out,
714             const git_config *cfg,
715             const char *name,
716             bool normalize_name,
717             int want_errors)
718             {
719 3049           int res = GIT_ENOTFOUND;
720 3049           const char *key = name;
721 3049           char *normalized = NULL;
722             size_t i;
723             backend_internal *internal;
724              
725 3049           *out = NULL;
726              
727 3049 100         if (normalize_name) {
728 483 50         if ((res = git_config__normalize_name(name, &normalized)) < 0)
729 0           goto cleanup;
730 483           key = normalized;
731             }
732              
733 3049           res = GIT_ENOTFOUND;
734 7361 100         git_vector_foreach(&cfg->backends, i, internal) {
735 5205 50         if (!internal || !internal->backend)
    50          
736 0           continue;
737              
738 5205           res = internal->backend->get(internal->backend, key, out);
739 5205 100         if (res != GIT_ENOTFOUND)
740 893           break;
741             }
742              
743 3049           git__free(normalized);
744              
745             cleanup:
746 3049 100         if (res == GIT_ENOTFOUND)
747 2156 100         res = (want_errors > GET_ALL_ERRORS) ? 0 : config_error_notfound(name);
748 893 50         else if (res && (want_errors == GET_NO_ERRORS)) {
    0          
749 0           git_error_clear();
750 0           res = 0;
751             }
752              
753 3049           return res;
754             }
755              
756 75           int git_config_get_entry(
757             git_config_entry **out, const git_config *cfg, const char *name)
758             {
759 75           return get_entry(out, cfg, name, true, GET_ALL_ERRORS);
760             }
761              
762 1898           int git_config__lookup_entry(
763             git_config_entry **out,
764             const git_config *cfg,
765             const char *key,
766             bool no_errors)
767             {
768 1898 100         return get_entry(
769             out, cfg, key, false, no_errors ? GET_NO_ERRORS : GET_NO_MISSING);
770             }
771              
772 0           int git_config_get_mapped(
773             int *out,
774             const git_config *cfg,
775             const char *name,
776             const git_configmap *maps,
777             size_t map_n)
778             {
779             git_config_entry *entry;
780             int ret;
781              
782 0 0         if ((ret = get_entry(&entry, cfg, name, true, GET_ALL_ERRORS)) < 0)
783 0           return ret;
784              
785 0           ret = git_config_lookup_map_value(out, maps, map_n, entry->value);
786 0           git_config_entry_free(entry);
787              
788 0           return ret;
789             }
790              
791 28           int git_config_get_int64(int64_t *out, const git_config *cfg, const char *name)
792             {
793             git_config_entry *entry;
794             int ret;
795              
796 28 50         if ((ret = get_entry(&entry, cfg, name, true, GET_ALL_ERRORS)) < 0)
797 28           return ret;
798              
799 0           ret = git_config_parse_int64(out, entry->value);
800 0           git_config_entry_free(entry);
801              
802 28           return ret;
803             }
804              
805 65           int git_config_get_int32(int32_t *out, const git_config *cfg, const char *name)
806             {
807             git_config_entry *entry;
808             int ret;
809              
810 65 100         if ((ret = get_entry(&entry, cfg, name, true, GET_ALL_ERRORS)) < 0)
811 1           return ret;
812              
813 64           ret = git_config_parse_int32(out, entry->value);
814 64           git_config_entry_free(entry);
815              
816 65           return ret;
817             }
818              
819 95           int git_config_get_bool(int *out, const git_config *cfg, const char *name)
820             {
821             git_config_entry *entry;
822             int ret;
823              
824 95 100         if ((ret = get_entry(&entry, cfg, name, true, GET_ALL_ERRORS)) < 0)
825 30           return ret;
826              
827 65           ret = git_config_parse_bool(out, entry->value);
828 65           git_config_entry_free(entry);
829              
830 95           return ret;
831             }
832              
833 198           static int is_readonly(const git_config *cfg)
834             {
835             size_t i;
836             backend_internal *internal;
837              
838 594 100         git_vector_foreach(&cfg->backends, i, internal) {
839 396 50         if (!internal || !internal->backend)
    50          
840 0           continue;
841              
842 396 50         if (!internal->backend->readonly)
843 0           return 0;
844             }
845              
846 198           return 1;
847             }
848              
849 0           int git_config_get_path(git_buf *out, const git_config *cfg, const char *name)
850             {
851             git_config_entry *entry;
852             int error;
853              
854 0 0         if ((error = get_entry(&entry, cfg, name, true, GET_ALL_ERRORS)) < 0)
855 0           return error;
856              
857 0           error = git_config_parse_path(out, entry->value);
858 0           git_config_entry_free(entry);
859              
860 0           return error;
861             }
862              
863 198           int git_config_get_string(
864             const char **out, const git_config *cfg, const char *name)
865             {
866             git_config_entry *entry;
867             int ret;
868              
869 198 50         if (!is_readonly(cfg)) {
870 0           git_error_set(GIT_ERROR_CONFIG, "get_string called on a live config object");
871 0           return -1;
872             }
873              
874 198           ret = get_entry(&entry, cfg, name, true, GET_ALL_ERRORS);
875 198 100         *out = !ret ? (entry->value ? entry->value : "") : NULL;
    50          
876              
877 198           git_config_entry_free(entry);
878              
879 198           return ret;
880             }
881              
882 22           int git_config_get_string_buf(
883             git_buf *out, const git_config *cfg, const char *name)
884             {
885             git_config_entry *entry;
886             int ret;
887             const char *str;
888              
889 22           git_buf_sanitize(out);
890              
891 22           ret = get_entry(&entry, cfg, name, true, GET_ALL_ERRORS);
892 22 100         str = !ret ? (entry->value ? entry->value : "") : NULL;
    50          
893              
894 22 100         if (str)
895 15           ret = git_buf_puts(out, str);
896              
897 22           git_config_entry_free(entry);
898              
899 22           return ret;
900             }
901              
902 14           char *git_config__get_string_force(
903             const git_config *cfg, const char *key, const char *fallback_value)
904             {
905             git_config_entry *entry;
906             char *ret;
907              
908 14           get_entry(&entry, cfg, key, false, GET_NO_ERRORS);
909 14 50         ret = (entry && entry->value) ? git__strdup(entry->value) : fallback_value ? git__strdup(fallback_value) : NULL;
    0          
    50          
910 14           git_config_entry_free(entry);
911              
912 14           return ret;
913             }
914              
915 598           int git_config__get_bool_force(
916             const git_config *cfg, const char *key, int fallback_value)
917             {
918 598           int val = fallback_value;
919             git_config_entry *entry;
920              
921 598           get_entry(&entry, cfg, key, false, GET_NO_ERRORS);
922              
923 598 100         if (entry && git_config_parse_bool(&val, entry->value) < 0)
    50          
924 0           git_error_clear();
925              
926 598           git_config_entry_free(entry);
927 598           return val;
928             }
929              
930 56           int git_config__get_int_force(
931             const git_config *cfg, const char *key, int fallback_value)
932             {
933 56           int32_t val = (int32_t)fallback_value;
934             git_config_entry *entry;
935              
936 56           get_entry(&entry, cfg, key, false, GET_NO_ERRORS);
937              
938 56 50         if (entry && git_config_parse_int32(&val, entry->value) < 0)
    0          
939 0           git_error_clear();
940              
941 56           git_config_entry_free(entry);
942 56           return (int)val;
943             }
944              
945 20           int git_config_get_multivar_foreach(
946             const git_config *cfg, const char *name, const char *regexp,
947             git_config_foreach_cb cb, void *payload)
948             {
949             int err, found;
950             git_config_iterator *iter;
951             git_config_entry *entry;
952              
953 20 50         if ((err = git_config_multivar_iterator_new(&iter, cfg, name, regexp)) < 0)
954 0           return err;
955              
956 20           found = 0;
957 30 100         while ((err = iter->next(&entry, iter)) == 0) {
958 10           found = 1;
959              
960 10 50         if ((err = cb(entry, payload)) != 0) {
961 0           git_error_set_after_callback(err);
962 0           break;
963             }
964             }
965              
966 20           iter->free(iter);
967 20 50         if (err == GIT_ITEROVER)
968 20           err = 0;
969              
970 20 100         if (found == 0 && err == 0)
    50          
971 10           err = config_error_notfound(name);
972              
973 20           return err;
974             }
975              
976             typedef struct {
977             git_config_iterator parent;
978             git_config_iterator *iter;
979             char *name;
980             git_regexp regex;
981             int have_regex;
982             } multivar_iter;
983              
984 39           static int multivar_iter_next(git_config_entry **entry, git_config_iterator *_iter)
985             {
986 39           multivar_iter *iter = (multivar_iter *) _iter;
987 39           int error = 0;
988              
989 413 100         while ((error = iter->iter->next(entry, iter->iter)) == 0) {
990 389 100         if (git__strcmp(iter->name, (*entry)->name))
991 374           continue;
992              
993 15 50         if (!iter->have_regex)
994 15           return 0;
995              
996 0 0         if (git_regexp_match(&iter->regex, (*entry)->value) == 0)
997 0           return 0;
998             }
999              
1000 24           return error;
1001             }
1002              
1003 24           static void multivar_iter_free(git_config_iterator *_iter)
1004             {
1005 24           multivar_iter *iter = (multivar_iter *) _iter;
1006              
1007 24           iter->iter->free(iter->iter);
1008              
1009 24           git__free(iter->name);
1010 24 50         if (iter->have_regex)
1011 0           git_regexp_dispose(&iter->regex);
1012 24           git__free(iter);
1013 24           }
1014              
1015 24           int git_config_multivar_iterator_new(git_config_iterator **out, const git_config *cfg, const char *name, const char *regexp)
1016             {
1017 24           multivar_iter *iter = NULL;
1018 24           git_config_iterator *inner = NULL;
1019             int error;
1020              
1021 24 50         if ((error = git_config_iterator_new(&inner, cfg)) < 0)
1022 0           return error;
1023              
1024 24           iter = git__calloc(1, sizeof(multivar_iter));
1025 24 50         GIT_ERROR_CHECK_ALLOC(iter);
1026              
1027 24 50         if ((error = git_config__normalize_name(name, &iter->name)) < 0)
1028 0           goto on_error;
1029              
1030 24 50         if (regexp != NULL) {
1031 0 0         if ((error = git_regexp_compile(&iter->regex, regexp, 0)) < 0)
1032 0           goto on_error;
1033              
1034 0           iter->have_regex = 1;
1035             }
1036              
1037 24           iter->iter = inner;
1038 24           iter->parent.free = multivar_iter_free;
1039 24           iter->parent.next = multivar_iter_next;
1040              
1041 24           *out = (git_config_iterator *) iter;
1042              
1043 24           return 0;
1044              
1045             on_error:
1046              
1047 0           inner->free(inner);
1048 0           git__free(iter);
1049 24           return error;
1050             }
1051              
1052 5           int git_config_set_multivar(git_config *cfg, const char *name, const char *regexp, const char *value)
1053             {
1054             git_config_backend *backend;
1055              
1056 5 50         if (get_backend_for_use(&backend, cfg, name, BACKEND_USE_DELETE) < 0)
1057 0           return GIT_ENOTFOUND;
1058              
1059 5           return backend->set_multivar(backend, name, regexp, value);
1060             }
1061              
1062 0           int git_config_delete_multivar(git_config *cfg, const char *name, const char *regexp)
1063             {
1064             git_config_backend *backend;
1065              
1066 0 0         if (get_backend_for_use(&backend, cfg, name, BACKEND_USE_DELETE) < 0)
1067 0           return GIT_ENOTFOUND;
1068              
1069 0           return backend->del_multivar(backend, name, regexp);
1070             }
1071              
1072 7971           int git_config_next(git_config_entry **entry, git_config_iterator *iter)
1073             {
1074 7971           return iter->next(entry, iter);
1075             }
1076              
1077 1036           void git_config_iterator_free(git_config_iterator *iter)
1078             {
1079 1036 50         if (iter == NULL)
1080 0           return;
1081              
1082 1036           iter->free(iter);
1083             }
1084              
1085 64           int git_config_find_global(git_buf *path)
1086             {
1087 64           git_buf_sanitize(path);
1088 64           return git_sysdir_find_global_file(path, GIT_CONFIG_FILENAME_GLOBAL);
1089             }
1090              
1091 64           int git_config_find_xdg(git_buf *path)
1092             {
1093 64           git_buf_sanitize(path);
1094 64           return git_sysdir_find_xdg_file(path, GIT_CONFIG_FILENAME_XDG);
1095             }
1096              
1097 64           int git_config_find_system(git_buf *path)
1098             {
1099 64           git_buf_sanitize(path);
1100 64           return git_sysdir_find_system_file(path, GIT_CONFIG_FILENAME_SYSTEM);
1101             }
1102              
1103 64           int git_config_find_programdata(git_buf *path)
1104             {
1105             int ret;
1106              
1107 64           git_buf_sanitize(path);
1108 64           ret = git_sysdir_find_programdata_file(path,
1109             GIT_CONFIG_FILENAME_PROGRAMDATA);
1110 64 50         if (ret != GIT_OK)
1111 64           return ret;
1112              
1113 0           return git_path_validate_system_file_ownership(path->ptr);
1114             }
1115              
1116 64           int git_config__global_location(git_buf *buf)
1117             {
1118             const git_buf *paths;
1119             const char *sep, *start;
1120              
1121 64 50         if (git_sysdir_get(&paths, GIT_SYSDIR_GLOBAL) < 0)
1122 0           return -1;
1123              
1124             /* no paths, so give up */
1125 64 50         if (!paths || !git_buf_len(paths))
    50          
1126 0           return -1;
1127              
1128             /* find unescaped separator or end of string */
1129 384 100         for (sep = start = git_buf_cstr(paths); *sep; ++sep) {
1130 320 50         if (*sep == GIT_PATH_LIST_SEPARATOR &&
    0          
1131 0 0         (sep <= start || sep[-1] != '\\'))
1132             break;
1133             }
1134              
1135 64 50         if (git_buf_set(buf, start, (size_t)(sep - start)) < 0)
1136 0           return -1;
1137              
1138 64           return git_buf_joinpath(buf, buf->ptr, GIT_CONFIG_FILENAME_GLOBAL);
1139             }
1140              
1141 1           int git_config_open_default(git_config **out)
1142             {
1143             int error;
1144 1           git_config *cfg = NULL;
1145 1           git_buf buf = GIT_BUF_INIT;
1146              
1147 1 50         if ((error = git_config_new(&cfg)) < 0)
1148 0           return error;
1149              
1150 1 50         if (!git_config_find_global(&buf) || !git_config__global_location(&buf)) {
    50          
1151 1           error = git_config_add_file_ondisk(cfg, buf.ptr,
1152             GIT_CONFIG_LEVEL_GLOBAL, NULL, 0);
1153             }
1154              
1155 1 50         if (!error && !git_config_find_xdg(&buf))
    50          
1156 0           error = git_config_add_file_ondisk(cfg, buf.ptr,
1157             GIT_CONFIG_LEVEL_XDG, NULL, 0);
1158              
1159 1 50         if (!error && !git_config_find_system(&buf))
    50          
1160 0           error = git_config_add_file_ondisk(cfg, buf.ptr,
1161             GIT_CONFIG_LEVEL_SYSTEM, NULL, 0);
1162              
1163 1 50         if (!error && !git_config_find_programdata(&buf))
    50          
1164 0           error = git_config_add_file_ondisk(cfg, buf.ptr,
1165             GIT_CONFIG_LEVEL_PROGRAMDATA, NULL, 0);
1166              
1167 1           git_buf_dispose(&buf);
1168              
1169 1 50         if (error) {
1170 0           git_config_free(cfg);
1171 0           cfg = NULL;
1172             }
1173              
1174 1           *out = cfg;
1175              
1176 1           return error;
1177             }
1178              
1179 0           int git_config_lock(git_transaction **out, git_config *cfg)
1180             {
1181             int error;
1182             git_config_backend *backend;
1183             backend_internal *internal;
1184              
1185 0 0         assert(cfg);
1186              
1187 0           internal = git_vector_get(&cfg->backends, 0);
1188 0 0         if (!internal || !internal->backend) {
    0          
1189 0           git_error_set(GIT_ERROR_CONFIG, "cannot lock; the config has no backends");
1190 0           return -1;
1191             }
1192 0           backend = internal->backend;
1193              
1194 0 0         if ((error = backend->lock(backend)) < 0)
1195 0           return error;
1196              
1197 0           return git_transaction_config_new(out, cfg);
1198             }
1199              
1200 0           int git_config_unlock(git_config *cfg, int commit)
1201             {
1202             git_config_backend *backend;
1203             backend_internal *internal;
1204              
1205 0 0         assert(cfg);
1206              
1207 0           internal = git_vector_get(&cfg->backends, 0);
1208 0 0         if (!internal || !internal->backend) {
    0          
1209 0           git_error_set(GIT_ERROR_CONFIG, "cannot lock; the config has no backends");
1210 0           return -1;
1211             }
1212              
1213 0           backend = internal->backend;
1214              
1215 0           return backend->unlock(backend, commit);
1216             }
1217              
1218             /***********
1219             * Parsers
1220             ***********/
1221              
1222 23           int git_config_lookup_map_value(
1223             int *out,
1224             const git_configmap *maps,
1225             size_t map_n,
1226             const char *value)
1227             {
1228             size_t i;
1229              
1230 23 50         if (!value)
1231 0           goto fail_parse;
1232              
1233 53 50         for (i = 0; i < map_n; ++i) {
1234 53           const git_configmap *m = maps + i;
1235              
1236 53           switch (m->type) {
1237             case GIT_CONFIGMAP_FALSE:
1238             case GIT_CONFIGMAP_TRUE: {
1239             int bool_val;
1240              
1241 46 100         if (git__parse_bool(&bool_val, value) == 0 &&
    100          
1242 32           bool_val == (int)m->type) {
1243 16           *out = m->map_value;
1244 16           return 0;
1245             }
1246 30           break;
1247             }
1248              
1249             case GIT_CONFIGMAP_INT32:
1250 0 0         if (git_config_parse_int32(out, value) == 0)
1251 0           return 0;
1252 0           break;
1253              
1254             case GIT_CONFIGMAP_STRING:
1255 7 50         if (strcasecmp(value, m->str_match) == 0) {
1256 7           *out = m->map_value;
1257 7           return 0;
1258             }
1259 0           break;
1260             }
1261             }
1262              
1263             fail_parse:
1264 0           git_error_set(GIT_ERROR_CONFIG, "failed to map '%s'", value);
1265 0           return -1;
1266             }
1267              
1268 0           int git_config_lookup_map_enum(git_configmap_t *type_out, const char **str_out,
1269             const git_configmap *maps, size_t map_n, int enum_val)
1270             {
1271             size_t i;
1272              
1273 0 0         for (i = 0; i < map_n; i++) {
1274 0           const git_configmap *m = &maps[i];
1275              
1276 0 0         if (m->map_value != enum_val)
1277 0           continue;
1278              
1279 0           *type_out = m->type;
1280 0           *str_out = m->str_match;
1281 0           return 0;
1282             }
1283              
1284 0           git_error_set(GIT_ERROR_CONFIG, "invalid enum value");
1285 0           return GIT_ENOTFOUND;
1286             }
1287              
1288 609           int git_config_parse_bool(int *out, const char *value)
1289             {
1290 609 50         if (git__parse_bool(out, value) == 0)
1291 609           return 0;
1292              
1293 0 0         if (git_config_parse_int32(out, value) == 0) {
1294 0           *out = !!(*out);
1295 0           return 0;
1296             }
1297              
1298 0           git_error_set(GIT_ERROR_CONFIG, "failed to parse '%s' as a boolean value", value);
1299 0           return -1;
1300             }
1301              
1302 64           int git_config_parse_int64(int64_t *out, const char *value)
1303             {
1304             const char *num_end;
1305             int64_t num;
1306              
1307 64 50         if (!value || git__strntol64(&num, value, strlen(value), &num_end, 0) < 0)
    50          
1308             goto fail_parse;
1309              
1310 64           switch (*num_end) {
1311             case 'g':
1312             case 'G':
1313 0           num *= 1024;
1314             /* fallthrough */
1315              
1316             case 'm':
1317             case 'M':
1318 0           num *= 1024;
1319             /* fallthrough */
1320              
1321             case 'k':
1322             case 'K':
1323 0           num *= 1024;
1324              
1325             /* check that that there are no more characters after the
1326             * given modifier suffix */
1327 0 0         if (num_end[1] != '\0')
1328 0           return -1;
1329              
1330             /* fallthrough */
1331              
1332             case '\0':
1333 64           *out = num;
1334 64           return 0;
1335              
1336             default:
1337 0           goto fail_parse;
1338             }
1339              
1340             fail_parse:
1341 0 0         git_error_set(GIT_ERROR_CONFIG, "failed to parse '%s' as an integer", value ? value : "(null)");
1342 64           return -1;
1343             }
1344              
1345 64           int git_config_parse_int32(int32_t *out, const char *value)
1346             {
1347             int64_t tmp;
1348             int32_t truncate;
1349              
1350 64 50         if (git_config_parse_int64(&tmp, value) < 0)
1351 0           goto fail_parse;
1352              
1353 64           truncate = tmp & 0xFFFFFFFF;
1354 64 50         if (truncate != tmp)
1355 0           goto fail_parse;
1356              
1357 64           *out = truncate;
1358 64           return 0;
1359              
1360             fail_parse:
1361 0 0         git_error_set(GIT_ERROR_CONFIG, "failed to parse '%s' as a 32-bit integer", value ? value : "(null)");
1362 64           return -1;
1363             }
1364              
1365 0           int git_config_parse_path(git_buf *out, const char *value)
1366             {
1367 0 0         assert(out && value);
    0          
1368              
1369 0           git_buf_sanitize(out);
1370              
1371 0 0         if (value[0] == '~') {
1372 0 0         if (value[1] != '\0' && value[1] != '/') {
    0          
1373 0           git_error_set(GIT_ERROR_CONFIG, "retrieving a homedir by name is not supported");
1374 0           return -1;
1375             }
1376              
1377 0 0         return git_sysdir_expand_global_file(out, value[1] ? &value[2] : NULL);
1378             }
1379              
1380 0           return git_buf_sets(out, value);
1381             }
1382              
1383 1136           static int normalize_section(char *start, char *end)
1384             {
1385             char *scan;
1386              
1387 1136 50         if (start == end)
1388 0           return GIT_EINVALIDSPEC;
1389              
1390             /* Validate and downcase range */
1391 8346 100         for (scan = start; *scan; ++scan) {
1392 7779 100         if (end && scan >= end)
    100          
1393 569           break;
1394 7210 50         if (isalnum(*scan))
1395 7210           *scan = (char)git__tolower(*scan);
1396 0 0         else if (*scan != '-' || scan == start)
    0          
1397 0           return GIT_EINVALIDSPEC;
1398             }
1399              
1400 1136 50         if (scan == start)
1401 0           return GIT_EINVALIDSPEC;
1402              
1403 1136           return 0;
1404             }
1405              
1406              
1407             /* Take something the user gave us and make it nice for our hash function */
1408 567           int git_config__normalize_name(const char *in, char **out)
1409             {
1410             char *name, *fdot, *ldot;
1411              
1412 567 50         assert(in && out);
    50          
1413              
1414 567           name = git__strdup(in);
1415 567 50         GIT_ERROR_CHECK_ALLOC(name);
1416              
1417 567           fdot = strchr(name, '.');
1418 567           ldot = strrchr(name, '.');
1419              
1420 567 50         if (fdot == NULL || fdot == name || ldot == NULL || !ldot[1])
    50          
    50          
    50          
1421             goto invalid;
1422              
1423             /* Validate and downcase up to first dot and after last dot */
1424 1134           if (normalize_section(name, fdot) < 0 ||
1425 567           normalize_section(ldot + 1, NULL) < 0)
1426             goto invalid;
1427              
1428             /* If there is a middle range, make sure it doesn't have newlines */
1429 1359 100         while (fdot < ldot)
1430 792 50         if (*fdot++ == '\n')
1431 0           goto invalid;
1432              
1433 567           *out = name;
1434 567           return 0;
1435              
1436             invalid:
1437 0           git__free(name);
1438 0           git_error_set(GIT_ERROR_CONFIG, "invalid config item name '%s'", in);
1439 0           return GIT_EINVALIDSPEC;
1440             }
1441              
1442             struct rename_data {
1443             git_config *config;
1444             git_buf *name;
1445             size_t old_len;
1446             };
1447              
1448 4           static int rename_config_entries_cb(
1449             const git_config_entry *entry,
1450             void *payload)
1451             {
1452 4           int error = 0;
1453 4           struct rename_data *data = (struct rename_data *)payload;
1454 4           size_t base_len = git_buf_len(data->name);
1455              
1456 4 100         if (base_len > 0 &&
    50          
1457 2           !(error = git_buf_puts(data->name, entry->name + data->old_len)))
1458             {
1459 2           error = git_config_set_string(
1460 2           data->config, git_buf_cstr(data->name), entry->value);
1461              
1462 2           git_buf_truncate(data->name, base_len);
1463             }
1464              
1465 4 50         if (!error)
1466 4           error = git_config_delete_entry(data->config, entry->name);
1467              
1468 4           return error;
1469             }
1470              
1471 3           int git_config_rename_section(
1472             git_repository *repo,
1473             const char *old_section_name,
1474             const char *new_section_name)
1475             {
1476             git_config *config;
1477 3           git_buf pattern = GIT_BUF_INIT, replace = GIT_BUF_INIT;
1478 3           int error = 0;
1479             struct rename_data data;
1480              
1481 3           git_buf_text_puts_escape_regex(&pattern, old_section_name);
1482              
1483 3 50         if ((error = git_buf_puts(&pattern, "\\..+")) < 0)
1484 0           goto cleanup;
1485              
1486 3 50         if ((error = git_repository_config__weakptr(&config, repo)) < 0)
1487 0           goto cleanup;
1488              
1489 3           data.config = config;
1490 3           data.name = &replace;
1491 3           data.old_len = strlen(old_section_name) + 1;
1492              
1493 3 50         if ((error = git_buf_join(&replace, '.', new_section_name, "")) < 0)
1494 0           goto cleanup;
1495              
1496 3 100         if (new_section_name != NULL &&
    50          
1497 2           (error = normalize_section(replace.ptr, strchr(replace.ptr, '.'))) < 0)
1498             {
1499 0           git_error_set(
1500             GIT_ERROR_CONFIG, "invalid config section '%s'", new_section_name);
1501 0           goto cleanup;
1502             }
1503              
1504 3           error = git_config_foreach_match(
1505             config, git_buf_cstr(&pattern), rename_config_entries_cb, &data);
1506              
1507             cleanup:
1508 3           git_buf_dispose(&pattern);
1509 3           git_buf_dispose(&replace);
1510              
1511 3           return error;
1512             }
1513              
1514 0           int git_config_init_backend(git_config_backend *backend, unsigned int version)
1515             {
1516 0 0         GIT_INIT_STRUCTURE_FROM_TEMPLATE(
1517             backend, version, git_config_backend, GIT_CONFIG_BACKEND_INIT);
1518 0           return 0;
1519             }