2 * Copyright (C) the libgit2 contributors. All rights reserved.
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.
11 #include "git2/config.h"
12 #include "git2/sys/config.h"
15 #include "config_file.h"
22 void git_config_entry_free(git_config_entry
*entry
)
33 git_config_backend
*file
;
34 git_config_level_t level
;
37 static void file_internal_free(file_internal
*internal
)
39 git_config_backend
*file
;
41 file
= internal
->file
;
46 static void config_free(git_config
*cfg
)
49 file_internal
*internal
;
51 for (i
= 0; i
< cfg
->files
.length
; ++i
) {
52 internal
= git_vector_get(&cfg
->files
, i
);
53 GIT_REFCOUNT_DEC(internal
, file_internal_free
);
56 git_vector_free(&cfg
->files
);
58 git__memzero(cfg
, sizeof(*cfg
));
62 void git_config_free(git_config
*cfg
)
67 GIT_REFCOUNT_DEC(cfg
, config_free
);
70 static int config_backend_cmp(const void *a
, const void *b
)
72 const file_internal
*bk_a
= (const file_internal
*)(a
);
73 const file_internal
*bk_b
= (const file_internal
*)(b
);
75 return bk_b
->level
- bk_a
->level
;
78 int git_config_new(git_config
**out
)
82 cfg
= git__malloc(sizeof(git_config
));
83 GITERR_CHECK_ALLOC(cfg
);
85 memset(cfg
, 0x0, sizeof(git_config
));
87 if (git_vector_init(&cfg
->files
, 3, config_backend_cmp
) < 0) {
93 GIT_REFCOUNT_INC(cfg
);
97 int git_config_add_file_ondisk(
100 git_config_level_t level
,
103 git_config_backend
*file
= NULL
;
109 res
= p_stat(path
, &st
);
110 if (res
< 0 && errno
!= ENOENT
) {
111 giterr_set(GITERR_CONFIG
, "Error stat'ing config file '%s'", path
);
115 if (git_config_file__ondisk(&file
, path
) < 0)
118 if ((res
= git_config_add_backend(cfg
, file
, level
, force
)) < 0) {
120 * free manually; the file is not owned by the config
121 * instance yet and will not be freed on cleanup
130 int git_config_open_ondisk(git_config
**out
, const char *path
)
137 if (git_config_new(&config
) < 0)
140 if ((error
= git_config_add_file_ondisk(config
, path
, GIT_CONFIG_LEVEL_LOCAL
, 0)) < 0)
141 git_config_free(config
);
148 int git_config_snapshot(git_config
**out
, git_config
*in
)
152 file_internal
*internal
;
157 if (git_config_new(&config
) < 0)
160 git_vector_foreach(&in
->files
, i
, internal
) {
161 git_config_backend
*b
;
163 if ((error
= internal
->file
->snapshot(&b
, internal
->file
)) < 0)
166 if ((error
= git_config_add_backend(config
, b
, internal
->level
, 0)) < 0) {
173 git_config_free(config
);
180 static int find_internal_file_by_level(
181 file_internal
**internal_out
,
182 const git_config
*cfg
,
183 git_config_level_t level
)
186 file_internal
*internal
;
189 /* when passing GIT_CONFIG_HIGHEST_LEVEL, the idea is to get the config file
190 * which has the highest level. As config files are stored in a vector
191 * sorted by decreasing order of level, getting the file at position 0
194 if (level
== GIT_CONFIG_HIGHEST_LEVEL
) {
197 git_vector_foreach(&cfg
->files
, i
, internal
) {
198 if (internal
->level
== level
)
204 giterr_set(GITERR_CONFIG
,
205 "No config file exists for the given level '%i'", (int)level
);
206 return GIT_ENOTFOUND
;
209 *internal_out
= git_vector_get(&cfg
->files
, pos
);
214 static int duplicate_level(void **old_raw
, void *new_raw
)
216 file_internal
**old
= (file_internal
**)old_raw
;
220 giterr_set(GITERR_CONFIG
, "A file with the same level (%i) has already been added to the config", (int)(*old
)->level
);
224 static void try_remove_existing_file_internal(
226 git_config_level_t level
)
229 file_internal
*internal
;
232 git_vector_foreach(&cfg
->files
, i
, internal
) {
233 if (internal
->level
== level
)
240 internal
= git_vector_get(&cfg
->files
, pos
);
242 if (git_vector_remove(&cfg
->files
, pos
) < 0)
245 GIT_REFCOUNT_DEC(internal
, file_internal_free
);
248 static int git_config__add_internal(
250 file_internal
*internal
,
251 git_config_level_t level
,
256 /* delete existing config file for level if it exists */
258 try_remove_existing_file_internal(cfg
, level
);
260 if ((result
= git_vector_insert_sorted(&cfg
->files
,
261 internal
, &duplicate_level
)) < 0)
264 git_vector_sort(&cfg
->files
);
265 internal
->file
->cfg
= cfg
;
267 GIT_REFCOUNT_INC(internal
);
272 int git_config_open_global(git_config
**cfg_out
, git_config
*cfg
)
274 if (!git_config_open_level(cfg_out
, cfg
, GIT_CONFIG_LEVEL_XDG
))
277 return git_config_open_level(cfg_out
, cfg
, GIT_CONFIG_LEVEL_GLOBAL
);
280 int git_config_open_level(
281 git_config
**cfg_out
,
282 const git_config
*cfg_parent
,
283 git_config_level_t level
)
286 file_internal
*internal
;
289 if ((res
= find_internal_file_by_level(&internal
, cfg_parent
, level
)) < 0)
292 if ((res
= git_config_new(&cfg
)) < 0)
295 if ((res
= git_config__add_internal(cfg
, internal
, level
, true)) < 0) {
296 git_config_free(cfg
);
305 int git_config_add_backend(
307 git_config_backend
*file
,
308 git_config_level_t level
,
311 file_internal
*internal
;
316 GITERR_CHECK_VERSION(file
, GIT_CONFIG_BACKEND_VERSION
, "git_config_backend");
318 if ((result
= file
->open(file
, level
)) < 0)
321 internal
= git__malloc(sizeof(file_internal
));
322 GITERR_CHECK_ALLOC(internal
);
324 memset(internal
, 0x0, sizeof(file_internal
));
326 internal
->file
= file
;
327 internal
->level
= level
;
329 if ((result
= git_config__add_internal(cfg
, internal
, level
, force
)) < 0) {
338 * Loop over all the variables
342 git_config_iterator parent
;
343 git_config_iterator
*current
;
344 const git_config
*cfg
;
349 static int find_next_backend(size_t *out
, const git_config
*cfg
, size_t i
)
351 file_internal
*internal
;
354 internal
= git_vector_get(&cfg
->files
, i
- 1);
355 if (!internal
|| !internal
->file
)
365 static int all_iter_next(git_config_entry
**entry
, git_config_iterator
*_iter
)
367 all_iter
*iter
= (all_iter
*) _iter
;
368 file_internal
*internal
;
369 git_config_backend
*backend
;
373 if (iter
->current
!= NULL
&&
374 (error
= iter
->current
->next(entry
, iter
->current
)) == 0) {
378 if (error
< 0 && error
!= GIT_ITEROVER
)
382 if (find_next_backend(&i
, iter
->cfg
, iter
->i
) < 0)
385 internal
= git_vector_get(&iter
->cfg
->files
, i
- 1);
386 backend
= internal
->file
;
390 iter
->current
->free(iter
->current
);
392 iter
->current
= NULL
;
393 error
= backend
->iterator(&iter
->current
, backend
);
394 if (error
== GIT_ENOTFOUND
)
400 error
= iter
->current
->next(entry
, iter
->current
);
401 /* If this backend is empty, then keep going */
402 if (error
== GIT_ITEROVER
)
412 static int all_iter_glob_next(git_config_entry
**entry
, git_config_iterator
*_iter
)
415 all_iter
*iter
= (all_iter
*) _iter
;
418 * We use the "normal" function to grab the next one across
419 * backends and then apply the regex
421 while ((error
= all_iter_next(entry
, _iter
)) == 0) {
422 /* skip non-matching keys if regexp was provided */
423 if (regexec(&iter
->regex
, (*entry
)->name
, 0, NULL
, 0) != 0)
426 /* and simply return if we like the entry's name */
433 static void all_iter_free(git_config_iterator
*_iter
)
435 all_iter
*iter
= (all_iter
*) _iter
;
438 iter
->current
->free(iter
->current
);
443 static void all_iter_glob_free(git_config_iterator
*_iter
)
445 all_iter
*iter
= (all_iter
*) _iter
;
447 regfree(&iter
->regex
);
448 all_iter_free(_iter
);
451 int git_config_iterator_new(git_config_iterator
**out
, const git_config
*cfg
)
455 iter
= git__calloc(1, sizeof(all_iter
));
456 GITERR_CHECK_ALLOC(iter
);
458 iter
->parent
.free
= all_iter_free
;
459 iter
->parent
.next
= all_iter_next
;
461 iter
->i
= cfg
->files
.length
;
464 *out
= (git_config_iterator
*) iter
;
469 int git_config_iterator_glob_new(git_config_iterator
**out
, const git_config
*cfg
, const char *regexp
)
475 return git_config_iterator_new(out
, cfg
);
477 iter
= git__calloc(1, sizeof(all_iter
));
478 GITERR_CHECK_ALLOC(iter
);
480 if ((result
= regcomp(&iter
->regex
, regexp
, REG_EXTENDED
)) != 0) {
481 giterr_set_regex(&iter
->regex
, result
);
486 iter
->parent
.next
= all_iter_glob_next
;
487 iter
->parent
.free
= all_iter_glob_free
;
488 iter
->i
= cfg
->files
.length
;
491 *out
= (git_config_iterator
*) iter
;
496 int git_config_foreach(
497 const git_config
*cfg
, git_config_foreach_cb cb
, void *payload
)
499 return git_config_foreach_match(cfg
, NULL
, cb
, payload
);
502 int git_config_backend_foreach_match(
503 git_config_backend
*backend
,
505 git_config_foreach_cb cb
,
508 git_config_entry
*entry
;
509 git_config_iterator
* iter
;
513 if (regexp
!= NULL
) {
514 if ((error
= regcomp(®ex
, regexp
, REG_EXTENDED
)) != 0) {
515 giterr_set_regex(®ex
, error
);
521 if ((error
= backend
->iterator(&iter
, backend
)) < 0) {
526 while (!(iter
->next(&entry
, iter
) < 0)) {
527 /* skip non-matching keys if regexp was provided */
528 if (regexp
&& regexec(®ex
, entry
->name
, 0, NULL
, 0) != 0)
531 /* abort iterator on non-zero return value */
532 if ((error
= cb(entry
, payload
)) != 0) {
533 giterr_set_after_callback(error
);
546 int git_config_foreach_match(
547 const git_config
*cfg
,
549 git_config_foreach_cb cb
,
553 git_config_iterator
*iter
;
554 git_config_entry
*entry
;
556 if ((error
= git_config_iterator_glob_new(&iter
, cfg
, regexp
)) < 0)
559 while (!(error
= git_config_next(&entry
, iter
))) {
560 if ((error
= cb(entry
, payload
)) != 0) {
561 giterr_set_after_callback(error
);
566 git_config_iterator_free(iter
);
568 if (error
== GIT_ITEROVER
)
578 static int config_error_nofiles(const char *name
)
580 giterr_set(GITERR_CONFIG
,
581 "Cannot set value for '%s' when no config files exist", name
);
582 return GIT_ENOTFOUND
;
585 int git_config_delete_entry(git_config
*cfg
, const char *name
)
587 git_config_backend
*file
;
588 file_internal
*internal
;
590 internal
= git_vector_get(&cfg
->files
, 0);
591 if (!internal
|| !internal
->file
)
592 return config_error_nofiles(name
);
593 file
= internal
->file
;
595 return file
->del(file
, name
);
598 int git_config_set_int64(git_config
*cfg
, const char *name
, int64_t value
)
600 char str_value
[32]; /* All numbers should fit in here */
601 p_snprintf(str_value
, sizeof(str_value
), "%" PRId64
, value
);
602 return git_config_set_string(cfg
, name
, str_value
);
605 int git_config_set_int32(git_config
*cfg
, const char *name
, int32_t value
)
607 return git_config_set_int64(cfg
, name
, (int64_t)value
);
610 int git_config_set_bool(git_config
*cfg
, const char *name
, int value
)
612 return git_config_set_string(cfg
, name
, value
? "true" : "false");
615 int git_config_set_string(git_config
*cfg
, const char *name
, const char *value
)
618 git_config_backend
*file
;
619 file_internal
*internal
;
622 giterr_set(GITERR_CONFIG
, "The value to set cannot be NULL");
626 internal
= git_vector_get(&cfg
->files
, 0);
627 if (!internal
|| !internal
->file
)
628 return config_error_nofiles(name
);
629 file
= internal
->file
;
631 error
= file
->set(file
, name
, value
);
633 if (!error
&& GIT_REFCOUNT_OWNER(cfg
) != NULL
)
634 git_repository__cvar_cache_clear(GIT_REFCOUNT_OWNER(cfg
));
639 int git_config__update_entry(
643 bool overwrite_existing
,
644 bool only_if_existing
)
647 git_config_entry
*ce
= NULL
;
649 if ((error
= git_config__lookup_entry(&ce
, config
, key
, false)) < 0)
652 if (!ce
&& only_if_existing
) /* entry doesn't exist */
654 if (ce
&& !overwrite_existing
) /* entry would be overwritten */
656 if (value
&& ce
&& ce
->value
&& !strcmp(ce
->value
, value
)) /* no change */
658 if (!value
&& (!ce
|| !ce
->value
)) /* asked to delete absent entry */
662 error
= git_config_delete_entry(config
, key
);
664 error
= git_config_set_string(config
, key
, value
);
666 git_config_entry_free(ce
);
674 static int config_error_notfound(const char *name
)
676 giterr_set(GITERR_CONFIG
, "Config value '%s' was not found", name
);
677 return GIT_ENOTFOUND
;
686 static int get_entry(
687 git_config_entry
**out
,
688 const git_config
*cfg
,
693 int res
= GIT_ENOTFOUND
;
694 const char *key
= name
;
695 char *normalized
= NULL
;
697 file_internal
*internal
;
701 if (normalize_name
) {
702 if ((res
= git_config__normalize_name(name
, &normalized
)) < 0)
708 git_vector_foreach(&cfg
->files
, i
, internal
) {
709 if (!internal
|| !internal
->file
)
712 res
= internal
->file
->get(internal
->file
, key
, out
);
713 if (res
!= GIT_ENOTFOUND
)
717 git__free(normalized
);
720 if (res
== GIT_ENOTFOUND
)
721 res
= (want_errors
> GET_ALL_ERRORS
) ? 0 : config_error_notfound(name
);
722 else if (res
&& (want_errors
== GET_NO_ERRORS
)) {
730 int git_config_get_entry(
731 git_config_entry
**out
, const git_config
*cfg
, const char *name
)
733 return get_entry(out
, cfg
, name
, true, GET_ALL_ERRORS
);
736 int git_config__lookup_entry(
737 git_config_entry
**out
,
738 const git_config
*cfg
,
743 out
, cfg
, key
, false, no_errors
? GET_NO_ERRORS
: GET_NO_MISSING
);
746 int git_config_get_mapped(
748 const git_config
*cfg
,
750 const git_cvar_map
*maps
,
753 git_config_entry
*entry
;
756 if ((ret
= get_entry(&entry
, cfg
, name
, true, GET_ALL_ERRORS
)) < 0)
759 ret
= git_config_lookup_map_value(out
, maps
, map_n
, entry
->value
);
760 git_config_entry_free(entry
);
765 int git_config_get_int64(int64_t *out
, const git_config
*cfg
, const char *name
)
767 git_config_entry
*entry
;
770 if ((ret
= get_entry(&entry
, cfg
, name
, true, GET_ALL_ERRORS
)) < 0)
773 ret
= git_config_parse_int64(out
, entry
->value
);
774 git_config_entry_free(entry
);
779 int git_config_get_int32(int32_t *out
, const git_config
*cfg
, const char *name
)
781 git_config_entry
*entry
;
784 if ((ret
= get_entry(&entry
, cfg
, name
, true, GET_ALL_ERRORS
)) < 0)
787 ret
= git_config_parse_int32(out
, entry
->value
);
788 git_config_entry_free(entry
);
793 int git_config_get_bool(int *out
, const git_config
*cfg
, const char *name
)
795 git_config_entry
*entry
;
798 if ((ret
= get_entry(&entry
, cfg
, name
, true, GET_ALL_ERRORS
)) < 0)
801 ret
= git_config_parse_bool(out
, entry
->value
);
802 git_config_entry_free(entry
);
807 static int is_readonly(const git_config
*cfg
)
810 file_internal
*internal
;
812 git_vector_foreach(&cfg
->files
, i
, internal
) {
813 if (!internal
|| !internal
->file
)
816 if (!internal
->file
->readonly
)
823 int git_config_get_path(git_buf
*out
, const git_config
*cfg
, const char *name
)
825 git_config_entry
*entry
;
828 if ((error
= get_entry(&entry
, cfg
, name
, true, GET_ALL_ERRORS
)) < 0)
831 error
= git_config_parse_path(out
, entry
->value
);
832 git_config_entry_free(entry
);
837 int git_config_get_string(
838 const char **out
, const git_config
*cfg
, const char *name
)
840 git_config_entry
*entry
;
843 if (!is_readonly(cfg
)) {
844 giterr_set(GITERR_CONFIG
, "get_string called on a live config object");
848 ret
= get_entry(&entry
, cfg
, name
, true, GET_ALL_ERRORS
);
849 *out
= !ret
? (entry
->value
? entry
->value
: "") : NULL
;
851 git_config_entry_free(entry
);
856 int git_config_get_string_buf(
857 git_buf
*out
, const git_config
*cfg
, const char *name
)
859 git_config_entry
*entry
;
863 git_buf_sanitize(out
);
865 ret
= get_entry(&entry
, cfg
, name
, true, GET_ALL_ERRORS
);
866 str
= !ret
? (entry
->value
? entry
->value
: "") : NULL
;
869 ret
= git_buf_puts(out
, str
);
871 git_config_entry_free(entry
);
876 char *git_config__get_string_force(
877 const git_config
*cfg
, const char *key
, const char *fallback_value
)
879 git_config_entry
*entry
;
882 get_entry(&entry
, cfg
, key
, false, GET_NO_ERRORS
);
883 ret
= (entry
&& entry
->value
) ? git__strdup(entry
->value
) : fallback_value
? git__strdup(fallback_value
) : NULL
;
884 git_config_entry_free(entry
);
889 int git_config__get_bool_force(
890 const git_config
*cfg
, const char *key
, int fallback_value
)
892 int val
= fallback_value
;
893 git_config_entry
*entry
;
895 get_entry(&entry
, cfg
, key
, false, GET_NO_ERRORS
);
897 if (entry
&& git_config_parse_bool(&val
, entry
->value
) < 0)
900 git_config_entry_free(entry
);
904 int git_config__get_int_force(
905 const git_config
*cfg
, const char *key
, int fallback_value
)
907 int32_t val
= (int32_t)fallback_value
;
908 git_config_entry
*entry
;
910 get_entry(&entry
, cfg
, key
, false, GET_NO_ERRORS
);
912 if (entry
&& git_config_parse_int32(&val
, entry
->value
) < 0)
915 git_config_entry_free(entry
);
919 int git_config_get_multivar_foreach(
920 const git_config
*cfg
, const char *name
, const char *regexp
,
921 git_config_foreach_cb cb
, void *payload
)
924 git_config_iterator
*iter
;
925 git_config_entry
*entry
;
927 if ((err
= git_config_multivar_iterator_new(&iter
, cfg
, name
, regexp
)) < 0)
931 while ((err
= iter
->next(&entry
, iter
)) == 0) {
934 if ((err
= cb(entry
, payload
)) != 0) {
935 giterr_set_after_callback(err
);
941 if (err
== GIT_ITEROVER
)
944 if (found
== 0 && err
== 0)
945 err
= config_error_notfound(name
);
951 git_config_iterator parent
;
952 git_config_iterator
*iter
;
958 static int multivar_iter_next(git_config_entry
**entry
, git_config_iterator
*_iter
)
960 multivar_iter
*iter
= (multivar_iter
*) _iter
;
963 while ((error
= iter
->iter
->next(entry
, iter
->iter
)) == 0) {
964 if (git__strcmp(iter
->name
, (*entry
)->name
))
967 if (!iter
->have_regex
)
970 if (regexec(&iter
->regex
, (*entry
)->value
, 0, NULL
, 0) == 0)
977 void multivar_iter_free(git_config_iterator
*_iter
)
979 multivar_iter
*iter
= (multivar_iter
*) _iter
;
981 iter
->iter
->free(iter
->iter
);
983 git__free(iter
->name
);
984 if (iter
->have_regex
)
985 regfree(&iter
->regex
);
989 int git_config_multivar_iterator_new(git_config_iterator
**out
, const git_config
*cfg
, const char *name
, const char *regexp
)
991 multivar_iter
*iter
= NULL
;
992 git_config_iterator
*inner
= NULL
;
995 if ((error
= git_config_iterator_new(&inner
, cfg
)) < 0)
998 iter
= git__calloc(1, sizeof(multivar_iter
));
999 GITERR_CHECK_ALLOC(iter
);
1001 if ((error
= git_config__normalize_name(name
, &iter
->name
)) < 0)
1004 if (regexp
!= NULL
) {
1005 error
= regcomp(&iter
->regex
, regexp
, REG_EXTENDED
);
1007 giterr_set_regex(&iter
->regex
, error
);
1009 regfree(&iter
->regex
);
1013 iter
->have_regex
= 1;
1017 iter
->parent
.free
= multivar_iter_free
;
1018 iter
->parent
.next
= multivar_iter_next
;
1020 *out
= (git_config_iterator
*) iter
;
1031 int git_config_set_multivar(git_config
*cfg
, const char *name
, const char *regexp
, const char *value
)
1033 git_config_backend
*file
;
1034 file_internal
*internal
;
1036 internal
= git_vector_get(&cfg
->files
, 0);
1037 if (!internal
|| !internal
->file
)
1038 return config_error_nofiles(name
);
1039 file
= internal
->file
;
1041 return file
->set_multivar(file
, name
, regexp
, value
);
1044 int git_config_delete_multivar(git_config
*cfg
, const char *name
, const char *regexp
)
1046 git_config_backend
*file
;
1047 file_internal
*internal
;
1049 internal
= git_vector_get(&cfg
->files
, 0);
1050 if (!internal
|| !internal
->file
)
1051 return config_error_nofiles(name
);
1052 file
= internal
->file
;
1054 return file
->del_multivar(file
, name
, regexp
);
1057 int git_config_next(git_config_entry
**entry
, git_config_iterator
*iter
)
1059 return iter
->next(entry
, iter
);
1062 void git_config_iterator_free(git_config_iterator
*iter
)
1070 int git_config_find_global(git_buf
*path
)
1072 git_buf_sanitize(path
);
1073 return git_sysdir_find_global_file(path
, GIT_CONFIG_FILENAME_GLOBAL
);
1076 int git_config_find_xdg(git_buf
*path
)
1078 git_buf_sanitize(path
);
1079 return git_sysdir_find_xdg_file(path
, GIT_CONFIG_FILENAME_XDG
);
1082 int git_config_find_system(git_buf
*path
)
1084 git_buf_sanitize(path
);
1085 return git_sysdir_find_system_file(path
, GIT_CONFIG_FILENAME_SYSTEM
);
1088 int git_config__global_location(git_buf
*buf
)
1090 const git_buf
*paths
;
1091 const char *sep
, *start
;
1093 if (git_sysdir_get(&paths
, GIT_SYSDIR_GLOBAL
) < 0)
1096 /* no paths, so give up */
1097 if (!paths
|| !git_buf_len(paths
))
1100 /* find unescaped separator or end of string */
1101 for (sep
= start
= git_buf_cstr(paths
); *sep
; ++sep
) {
1102 if (*sep
== GIT_PATH_LIST_SEPARATOR
&&
1103 (sep
<= start
|| sep
[-1] != '\\'))
1107 if (git_buf_set(buf
, start
, (size_t)(sep
- start
)) < 0)
1110 return git_buf_joinpath(buf
, buf
->ptr
, GIT_CONFIG_FILENAME_GLOBAL
);
1113 int git_config_open_default(git_config
**out
)
1116 git_config
*cfg
= NULL
;
1117 git_buf buf
= GIT_BUF_INIT
;
1119 if ((error
= git_config_new(&cfg
)) < 0)
1122 if (!git_config_find_global(&buf
) || !git_config__global_location(&buf
)) {
1123 error
= git_config_add_file_ondisk(cfg
, buf
.ptr
,
1124 GIT_CONFIG_LEVEL_GLOBAL
, 0);
1127 if (!error
&& !git_config_find_xdg(&buf
))
1128 error
= git_config_add_file_ondisk(cfg
, buf
.ptr
,
1129 GIT_CONFIG_LEVEL_XDG
, 0);
1131 if (!error
&& !git_config_find_system(&buf
))
1132 error
= git_config_add_file_ondisk(cfg
, buf
.ptr
,
1133 GIT_CONFIG_LEVEL_SYSTEM
, 0);
1138 git_config_free(cfg
);
1151 int git_config_lookup_map_value(
1153 const git_cvar_map
*maps
,
1162 for (i
= 0; i
< map_n
; ++i
) {
1163 const git_cvar_map
*m
= maps
+ i
;
1165 switch (m
->cvar_type
) {
1166 case GIT_CVAR_FALSE
:
1167 case GIT_CVAR_TRUE
: {
1170 if (git__parse_bool(&bool_val
, value
) == 0 &&
1171 bool_val
== (int)m
->cvar_type
) {
1172 *out
= m
->map_value
;
1178 case GIT_CVAR_INT32
:
1179 if (git_config_parse_int32(out
, value
) == 0)
1183 case GIT_CVAR_STRING
:
1184 if (strcasecmp(value
, m
->str_match
) == 0) {
1185 *out
= m
->map_value
;
1193 giterr_set(GITERR_CONFIG
, "Failed to map '%s'", value
);
1197 int git_config_parse_bool(int *out
, const char *value
)
1199 if (git__parse_bool(out
, value
) == 0)
1202 if (git_config_parse_int32(out
, value
) == 0) {
1207 giterr_set(GITERR_CONFIG
, "Failed to parse '%s' as a boolean value", value
);
1211 int git_config_parse_int64(int64_t *out
, const char *value
)
1213 const char *num_end
;
1216 if (!value
|| git__strtol64(&num
, value
, &num_end
, 0) < 0)
1234 /* check that that there are no more characters after the
1235 * given modifier suffix */
1236 if (num_end
[1] != '\0')
1250 giterr_set(GITERR_CONFIG
, "Failed to parse '%s' as an integer", value
? value
: "(null)");
1254 int git_config_parse_int32(int32_t *out
, const char *value
)
1259 if (git_config_parse_int64(&tmp
, value
) < 0)
1262 truncate
= tmp
& 0xFFFFFFFF;
1263 if (truncate
!= tmp
)
1270 giterr_set(GITERR_CONFIG
, "Failed to parse '%s' as a 32-bit integer", value
? value
: "(null)");
1274 int git_config_parse_path(git_buf
*out
, const char *value
)
1277 const git_buf
*home
;
1279 assert(out
&& value
);
1281 git_buf_sanitize(out
);
1283 if (value
[0] == '~') {
1284 if (value
[1] != '\0' && value
[1] != '/') {
1285 giterr_set(GITERR_CONFIG
, "retrieving a homedir by name is not supported");
1289 if ((error
= git_sysdir_get(&home
, GIT_SYSDIR_GLOBAL
)) < 0)
1292 git_buf_sets(out
, home
->ptr
);
1293 git_buf_puts(out
, value
+ 1);
1295 if (git_buf_oom(out
))
1301 return git_buf_sets(out
, value
);
1304 /* Take something the user gave us and make it nice for our hash function */
1305 int git_config__normalize_name(const char *in
, char **out
)
1307 char *name
, *fdot
, *ldot
;
1311 name
= git__strdup(in
);
1312 GITERR_CHECK_ALLOC(name
);
1314 fdot
= strchr(name
, '.');
1315 ldot
= strrchr(name
, '.');
1317 if (fdot
== NULL
|| fdot
== name
|| ldot
== NULL
|| !ldot
[1])
1320 /* Validate and downcase up to first dot and after last dot */
1321 if (git_config_file_normalize_section(name
, fdot
) < 0 ||
1322 git_config_file_normalize_section(ldot
+ 1, NULL
) < 0)
1325 /* If there is a middle range, make sure it doesn't have newlines */
1327 if (*fdot
++ == '\n')
1335 giterr_set(GITERR_CONFIG
, "Invalid config item name '%s'", in
);
1336 return GIT_EINVALIDSPEC
;
1339 struct rename_data
{
1345 static int rename_config_entries_cb(
1346 const git_config_entry
*entry
,
1350 struct rename_data
*data
= (struct rename_data
*)payload
;
1351 size_t base_len
= git_buf_len(data
->name
);
1354 !(error
= git_buf_puts(data
->name
, entry
->name
+ data
->old_len
)))
1356 error
= git_config_set_string(
1357 data
->config
, git_buf_cstr(data
->name
), entry
->value
);
1359 git_buf_truncate(data
->name
, base_len
);
1363 error
= git_config_delete_entry(data
->config
, entry
->name
);
1368 int git_config_rename_section(
1369 git_repository
*repo
,
1370 const char *old_section_name
,
1371 const char *new_section_name
)
1374 git_buf pattern
= GIT_BUF_INIT
, replace
= GIT_BUF_INIT
;
1376 struct rename_data data
;
1378 git_buf_text_puts_escape_regex(&pattern
, old_section_name
);
1380 if ((error
= git_buf_puts(&pattern
, "\\..+")) < 0)
1383 if ((error
= git_repository_config__weakptr(&config
, repo
)) < 0)
1386 data
.config
= config
;
1387 data
.name
= &replace
;
1388 data
.old_len
= strlen(old_section_name
) + 1;
1390 if ((error
= git_buf_join(&replace
, '.', new_section_name
, "")) < 0)
1393 if (new_section_name
!= NULL
&&
1394 (error
= git_config_file_normalize_section(
1395 replace
.ptr
, strchr(replace
.ptr
, '.'))) < 0)
1398 GITERR_CONFIG
, "Invalid config section '%s'", new_section_name
);
1402 error
= git_config_foreach_match(
1403 config
, git_buf_cstr(&pattern
), rename_config_entries_cb
, &data
);
1406 git_buf_free(&pattern
);
1407 git_buf_free(&replace
);
1412 int git_config_init_backend(git_config_backend
*backend
, unsigned int version
)
1414 GIT_INIT_STRUCTURE_FROM_TEMPLATE(
1415 backend
, version
, git_config_backend
, GIT_CONFIG_BACKEND_INIT
);