File Coverage

deps/libgit2/src/libgit2/config.c
Criterion Covered Total %
statement 429 684 62.7
branch 210 482 43.5
condition n/a
subroutine n/a
pod n/a
total 639 1166 54.8


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