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"
16 #include "transaction.h"
23 void git_config_entry_free(git_config_entry
*entry
)
34 git_config_backend
*file
;
35 git_config_level_t level
;
38 static void file_internal_free(file_internal
*internal
)
40 git_config_backend
*file
;
42 file
= internal
->file
;
47 static void config_free(git_config
*cfg
)
50 file_internal
*internal
;
52 for (i
= 0; i
< cfg
->files
.length
; ++i
) {
53 internal
= git_vector_get(&cfg
->files
, i
);
54 GIT_REFCOUNT_DEC(internal
, file_internal_free
);
57 git_vector_free(&cfg
->files
);
59 git__memzero(cfg
, sizeof(*cfg
));
63 void git_config_free(git_config
*cfg
)
68 GIT_REFCOUNT_DEC(cfg
, config_free
);
71 static int config_backend_cmp(const void *a
, const void *b
)
73 const file_internal
*bk_a
= (const file_internal
*)(a
);
74 const file_internal
*bk_b
= (const file_internal
*)(b
);
76 return bk_b
->level
- bk_a
->level
;
79 int git_config_new(git_config
**out
)
83 cfg
= git__malloc(sizeof(git_config
));
84 GITERR_CHECK_ALLOC(cfg
);
86 memset(cfg
, 0x0, sizeof(git_config
));
88 if (git_vector_init(&cfg
->files
, 3, config_backend_cmp
) < 0) {
94 GIT_REFCOUNT_INC(cfg
);
98 int git_config_add_file_ondisk(
101 git_config_level_t level
,
102 const git_repository
*repo
,
105 git_config_backend
*file
= NULL
;
111 res
= p_stat(path
, &st
);
112 if (res
< 0 && errno
!= ENOENT
) {
113 giterr_set(GITERR_CONFIG
, "failed to stat '%s'", path
);
117 if (git_config_file__ondisk(&file
, path
) < 0)
120 if ((res
= git_config_add_backend(cfg
, file
, level
, repo
, force
)) < 0) {
122 * free manually; the file is not owned by the config
123 * instance yet and will not be freed on cleanup
132 int git_config_open_ondisk(git_config
**out
, const char *path
)
139 if (git_config_new(&config
) < 0)
142 if ((error
= git_config_add_file_ondisk(config
, path
, GIT_CONFIG_LEVEL_LOCAL
, NULL
, 0)) < 0)
143 git_config_free(config
);
150 int git_config_snapshot(git_config
**out
, git_config
*in
)
154 file_internal
*internal
;
159 if (git_config_new(&config
) < 0)
162 git_vector_foreach(&in
->files
, i
, internal
) {
163 git_config_backend
*b
;
165 if ((error
= internal
->file
->snapshot(&b
, internal
->file
)) < 0)
168 if ((error
= git_config_add_backend(config
, b
, internal
->level
, NULL
, 0)) < 0) {
175 git_config_free(config
);
182 static int find_internal_file_by_level(
183 file_internal
**internal_out
,
184 const git_config
*cfg
,
185 git_config_level_t level
)
188 file_internal
*internal
;
191 /* when passing GIT_CONFIG_HIGHEST_LEVEL, the idea is to get the config file
192 * which has the highest level. As config files are stored in a vector
193 * sorted by decreasing order of level, getting the file at position 0
196 if (level
== GIT_CONFIG_HIGHEST_LEVEL
) {
199 git_vector_foreach(&cfg
->files
, i
, internal
) {
200 if (internal
->level
== level
)
206 giterr_set(GITERR_CONFIG
,
207 "no config file exists for the given level '%i'", (int)level
);
208 return GIT_ENOTFOUND
;
211 *internal_out
= git_vector_get(&cfg
->files
, pos
);
216 static int duplicate_level(void **old_raw
, void *new_raw
)
218 file_internal
**old
= (file_internal
**)old_raw
;
222 giterr_set(GITERR_CONFIG
, "a file with the same level (%i) has already been added to the config", (int)(*old
)->level
);
226 static void try_remove_existing_file_internal(
228 git_config_level_t level
)
231 file_internal
*internal
;
234 git_vector_foreach(&cfg
->files
, i
, internal
) {
235 if (internal
->level
== level
)
242 internal
= git_vector_get(&cfg
->files
, pos
);
244 if (git_vector_remove(&cfg
->files
, pos
) < 0)
247 GIT_REFCOUNT_DEC(internal
, file_internal_free
);
250 static int git_config__add_internal(
252 file_internal
*internal
,
253 git_config_level_t level
,
258 /* delete existing config file for level if it exists */
260 try_remove_existing_file_internal(cfg
, level
);
262 if ((result
= git_vector_insert_sorted(&cfg
->files
,
263 internal
, &duplicate_level
)) < 0)
266 git_vector_sort(&cfg
->files
);
267 internal
->file
->cfg
= cfg
;
269 GIT_REFCOUNT_INC(internal
);
274 int git_config_open_global(git_config
**cfg_out
, git_config
*cfg
)
276 if (!git_config_open_level(cfg_out
, cfg
, GIT_CONFIG_LEVEL_XDG
))
279 return git_config_open_level(cfg_out
, cfg
, GIT_CONFIG_LEVEL_GLOBAL
);
282 int git_config_open_level(
283 git_config
**cfg_out
,
284 const git_config
*cfg_parent
,
285 git_config_level_t level
)
288 file_internal
*internal
;
291 if ((res
= find_internal_file_by_level(&internal
, cfg_parent
, level
)) < 0)
294 if ((res
= git_config_new(&cfg
)) < 0)
297 if ((res
= git_config__add_internal(cfg
, internal
, level
, true)) < 0) {
298 git_config_free(cfg
);
307 int git_config_add_backend(
309 git_config_backend
*file
,
310 git_config_level_t level
,
311 const git_repository
*repo
,
314 file_internal
*internal
;
319 GITERR_CHECK_VERSION(file
, GIT_CONFIG_BACKEND_VERSION
, "git_config_backend");
321 if ((result
= file
->open(file
, level
, repo
)) < 0)
324 internal
= git__malloc(sizeof(file_internal
));
325 GITERR_CHECK_ALLOC(internal
);
327 memset(internal
, 0x0, sizeof(file_internal
));
329 internal
->file
= file
;
330 internal
->level
= level
;
332 if ((result
= git_config__add_internal(cfg
, internal
, level
, force
)) < 0) {
341 * Loop over all the variables
345 git_config_iterator parent
;
346 git_config_iterator
*current
;
347 const git_config
*cfg
;
352 static int find_next_backend(size_t *out
, const git_config
*cfg
, size_t i
)
354 file_internal
*internal
;
357 internal
= git_vector_get(&cfg
->files
, i
- 1);
358 if (!internal
|| !internal
->file
)
368 static int all_iter_next(git_config_entry
**entry
, git_config_iterator
*_iter
)
370 all_iter
*iter
= (all_iter
*) _iter
;
371 file_internal
*internal
;
372 git_config_backend
*backend
;
376 if (iter
->current
!= NULL
&&
377 (error
= iter
->current
->next(entry
, iter
->current
)) == 0) {
381 if (error
< 0 && error
!= GIT_ITEROVER
)
385 if (find_next_backend(&i
, iter
->cfg
, iter
->i
) < 0)
388 internal
= git_vector_get(&iter
->cfg
->files
, i
- 1);
389 backend
= internal
->file
;
393 iter
->current
->free(iter
->current
);
395 iter
->current
= NULL
;
396 error
= backend
->iterator(&iter
->current
, backend
);
397 if (error
== GIT_ENOTFOUND
)
403 error
= iter
->current
->next(entry
, iter
->current
);
404 /* If this backend is empty, then keep going */
405 if (error
== GIT_ITEROVER
)
415 static int all_iter_glob_next(git_config_entry
**entry
, git_config_iterator
*_iter
)
418 all_iter
*iter
= (all_iter
*) _iter
;
421 * We use the "normal" function to grab the next one across
422 * backends and then apply the regex
424 while ((error
= all_iter_next(entry
, _iter
)) == 0) {
425 /* skip non-matching keys if regexp was provided */
426 if (regexec(&iter
->regex
, (*entry
)->name
, 0, NULL
, 0) != 0)
429 /* and simply return if we like the entry's name */
436 static void all_iter_free(git_config_iterator
*_iter
)
438 all_iter
*iter
= (all_iter
*) _iter
;
441 iter
->current
->free(iter
->current
);
446 static void all_iter_glob_free(git_config_iterator
*_iter
)
448 all_iter
*iter
= (all_iter
*) _iter
;
450 regfree(&iter
->regex
);
451 all_iter_free(_iter
);
454 int git_config_iterator_new(git_config_iterator
**out
, const git_config
*cfg
)
458 iter
= git__calloc(1, sizeof(all_iter
));
459 GITERR_CHECK_ALLOC(iter
);
461 iter
->parent
.free
= all_iter_free
;
462 iter
->parent
.next
= all_iter_next
;
464 iter
->i
= cfg
->files
.length
;
467 *out
= (git_config_iterator
*) iter
;
472 int git_config_iterator_glob_new(git_config_iterator
**out
, const git_config
*cfg
, const char *regexp
)
478 return git_config_iterator_new(out
, cfg
);
480 iter
= git__calloc(1, sizeof(all_iter
));
481 GITERR_CHECK_ALLOC(iter
);
483 if ((result
= p_regcomp(&iter
->regex
, regexp
, REG_EXTENDED
)) != 0) {
484 giterr_set_regex(&iter
->regex
, result
);
489 iter
->parent
.next
= all_iter_glob_next
;
490 iter
->parent
.free
= all_iter_glob_free
;
491 iter
->i
= cfg
->files
.length
;
494 *out
= (git_config_iterator
*) iter
;
499 int git_config_foreach(
500 const git_config
*cfg
, git_config_foreach_cb cb
, void *payload
)
502 return git_config_foreach_match(cfg
, NULL
, cb
, payload
);
505 int git_config_backend_foreach_match(
506 git_config_backend
*backend
,
508 git_config_foreach_cb cb
,
511 git_config_entry
*entry
;
512 git_config_iterator
* iter
;
516 if (regexp
!= NULL
) {
517 if ((error
= p_regcomp(®ex
, regexp
, REG_EXTENDED
)) != 0) {
518 giterr_set_regex(®ex
, error
);
524 if ((error
= backend
->iterator(&iter
, backend
)) < 0) {
529 while (!(iter
->next(&entry
, iter
) < 0)) {
530 /* skip non-matching keys if regexp was provided */
531 if (regexp
&& regexec(®ex
, entry
->name
, 0, NULL
, 0) != 0)
534 /* abort iterator on non-zero return value */
535 if ((error
= cb(entry
, payload
)) != 0) {
536 giterr_set_after_callback(error
);
549 int git_config_foreach_match(
550 const git_config
*cfg
,
552 git_config_foreach_cb cb
,
556 git_config_iterator
*iter
;
557 git_config_entry
*entry
;
559 if ((error
= git_config_iterator_glob_new(&iter
, cfg
, regexp
)) < 0)
562 while (!(error
= git_config_next(&entry
, iter
))) {
563 if ((error
= cb(entry
, payload
)) != 0) {
564 giterr_set_after_callback(error
);
569 git_config_iterator_free(iter
);
571 if (error
== GIT_ITEROVER
)
586 static const char *uses
[] = {
591 static int get_backend_for_use(git_config_backend
**out
,
592 git_config
*cfg
, const char *name
, backend_use use
)
599 if (git_vector_length(&cfg
->files
) == 0) {
600 giterr_set(GITERR_CONFIG
,
601 "cannot %s value for '%s' when no config files exist",
603 return GIT_ENOTFOUND
;
606 git_vector_foreach(&cfg
->files
, i
, f
) {
607 if (!f
->file
->readonly
) {
613 giterr_set(GITERR_CONFIG
,
614 "cannot %s value for '%s' when all config files are readonly",
616 return GIT_ENOTFOUND
;
619 int git_config_delete_entry(git_config
*cfg
, const char *name
)
621 git_config_backend
*file
;
623 if (get_backend_for_use(&file
, cfg
, name
, BACKEND_USE_DELETE
) < 0)
624 return GIT_ENOTFOUND
;
626 return file
->del(file
, name
);
629 int git_config_set_int64(git_config
*cfg
, const char *name
, int64_t value
)
631 char str_value
[32]; /* All numbers should fit in here */
632 p_snprintf(str_value
, sizeof(str_value
), "%" PRId64
, value
);
633 return git_config_set_string(cfg
, name
, str_value
);
636 int git_config_set_int32(git_config
*cfg
, const char *name
, int32_t value
)
638 return git_config_set_int64(cfg
, name
, (int64_t)value
);
641 int git_config_set_bool(git_config
*cfg
, const char *name
, int value
)
643 return git_config_set_string(cfg
, name
, value
? "true" : "false");
646 int git_config_set_string(git_config
*cfg
, const char *name
, const char *value
)
649 git_config_backend
*file
;
652 giterr_set(GITERR_CONFIG
, "the value to set cannot be NULL");
656 if (get_backend_for_use(&file
, cfg
, name
, BACKEND_USE_SET
) < 0)
657 return GIT_ENOTFOUND
;
659 error
= file
->set(file
, name
, value
);
661 if (!error
&& GIT_REFCOUNT_OWNER(cfg
) != NULL
)
662 git_repository__cvar_cache_clear(GIT_REFCOUNT_OWNER(cfg
));
667 int git_config__update_entry(
671 bool overwrite_existing
,
672 bool only_if_existing
)
675 git_config_entry
*ce
= NULL
;
677 if ((error
= git_config__lookup_entry(&ce
, config
, key
, false)) < 0)
680 if (!ce
&& only_if_existing
) /* entry doesn't exist */
682 if (ce
&& !overwrite_existing
) /* entry would be overwritten */
684 if (value
&& ce
&& ce
->value
&& !strcmp(ce
->value
, value
)) /* no change */
686 if (!value
&& (!ce
|| !ce
->value
)) /* asked to delete absent entry */
690 error
= git_config_delete_entry(config
, key
);
692 error
= git_config_set_string(config
, key
, value
);
694 git_config_entry_free(ce
);
702 static int config_error_notfound(const char *name
)
704 giterr_set(GITERR_CONFIG
, "config value '%s' was not found", name
);
705 return GIT_ENOTFOUND
;
714 static int get_entry(
715 git_config_entry
**out
,
716 const git_config
*cfg
,
721 int res
= GIT_ENOTFOUND
;
722 const char *key
= name
;
723 char *normalized
= NULL
;
725 file_internal
*internal
;
729 if (normalize_name
) {
730 if ((res
= git_config__normalize_name(name
, &normalized
)) < 0)
736 git_vector_foreach(&cfg
->files
, i
, internal
) {
737 if (!internal
|| !internal
->file
)
740 res
= internal
->file
->get(internal
->file
, key
, out
);
741 if (res
!= GIT_ENOTFOUND
)
745 git__free(normalized
);
748 if (res
== GIT_ENOTFOUND
)
749 res
= (want_errors
> GET_ALL_ERRORS
) ? 0 : config_error_notfound(name
);
750 else if (res
&& (want_errors
== GET_NO_ERRORS
)) {
758 int git_config_get_entry(
759 git_config_entry
**out
, const git_config
*cfg
, const char *name
)
761 return get_entry(out
, cfg
, name
, true, GET_ALL_ERRORS
);
764 int git_config__lookup_entry(
765 git_config_entry
**out
,
766 const git_config
*cfg
,
771 out
, cfg
, key
, false, no_errors
? GET_NO_ERRORS
: GET_NO_MISSING
);
774 int git_config_get_mapped(
776 const git_config
*cfg
,
778 const git_cvar_map
*maps
,
781 git_config_entry
*entry
;
784 if ((ret
= get_entry(&entry
, cfg
, name
, true, GET_ALL_ERRORS
)) < 0)
787 ret
= git_config_lookup_map_value(out
, maps
, map_n
, entry
->value
);
788 git_config_entry_free(entry
);
793 int git_config_get_int64(int64_t *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_int64(out
, entry
->value
);
802 git_config_entry_free(entry
);
807 int git_config_get_int32(int32_t *out
, const git_config
*cfg
, const char *name
)
809 git_config_entry
*entry
;
812 if ((ret
= get_entry(&entry
, cfg
, name
, true, GET_ALL_ERRORS
)) < 0)
815 ret
= git_config_parse_int32(out
, entry
->value
);
816 git_config_entry_free(entry
);
821 int git_config_get_bool(int *out
, const git_config
*cfg
, const char *name
)
823 git_config_entry
*entry
;
826 if ((ret
= get_entry(&entry
, cfg
, name
, true, GET_ALL_ERRORS
)) < 0)
829 ret
= git_config_parse_bool(out
, entry
->value
);
830 git_config_entry_free(entry
);
835 static int is_readonly(const git_config
*cfg
)
838 file_internal
*internal
;
840 git_vector_foreach(&cfg
->files
, i
, internal
) {
841 if (!internal
|| !internal
->file
)
844 if (!internal
->file
->readonly
)
851 int git_config_get_path(git_buf
*out
, const git_config
*cfg
, const char *name
)
853 git_config_entry
*entry
;
856 if ((error
= get_entry(&entry
, cfg
, name
, true, GET_ALL_ERRORS
)) < 0)
859 error
= git_config_parse_path(out
, entry
->value
);
860 git_config_entry_free(entry
);
865 int git_config_get_string(
866 const char **out
, const git_config
*cfg
, const char *name
)
868 git_config_entry
*entry
;
871 if (!is_readonly(cfg
)) {
872 giterr_set(GITERR_CONFIG
, "get_string called on a live config object");
876 ret
= get_entry(&entry
, cfg
, name
, true, GET_ALL_ERRORS
);
877 *out
= !ret
? (entry
->value
? entry
->value
: "") : NULL
;
879 git_config_entry_free(entry
);
884 int git_config_get_string_buf(
885 git_buf
*out
, const git_config
*cfg
, const char *name
)
887 git_config_entry
*entry
;
891 git_buf_sanitize(out
);
893 ret
= get_entry(&entry
, cfg
, name
, true, GET_ALL_ERRORS
);
894 str
= !ret
? (entry
->value
? entry
->value
: "") : NULL
;
897 ret
= git_buf_puts(out
, str
);
899 git_config_entry_free(entry
);
904 char *git_config__get_string_force(
905 const git_config
*cfg
, const char *key
, const char *fallback_value
)
907 git_config_entry
*entry
;
910 get_entry(&entry
, cfg
, key
, false, GET_NO_ERRORS
);
911 ret
= (entry
&& entry
->value
) ? git__strdup(entry
->value
) : fallback_value
? git__strdup(fallback_value
) : NULL
;
912 git_config_entry_free(entry
);
917 int git_config__get_bool_force(
918 const git_config
*cfg
, const char *key
, int fallback_value
)
920 int val
= fallback_value
;
921 git_config_entry
*entry
;
923 get_entry(&entry
, cfg
, key
, false, GET_NO_ERRORS
);
925 if (entry
&& git_config_parse_bool(&val
, entry
->value
) < 0)
928 git_config_entry_free(entry
);
932 int git_config__get_int_force(
933 const git_config
*cfg
, const char *key
, int fallback_value
)
935 int32_t val
= (int32_t)fallback_value
;
936 git_config_entry
*entry
;
938 get_entry(&entry
, cfg
, key
, false, GET_NO_ERRORS
);
940 if (entry
&& git_config_parse_int32(&val
, entry
->value
) < 0)
943 git_config_entry_free(entry
);
947 int git_config_get_multivar_foreach(
948 const git_config
*cfg
, const char *name
, const char *regexp
,
949 git_config_foreach_cb cb
, void *payload
)
952 git_config_iterator
*iter
;
953 git_config_entry
*entry
;
955 if ((err
= git_config_multivar_iterator_new(&iter
, cfg
, name
, regexp
)) < 0)
959 while ((err
= iter
->next(&entry
, iter
)) == 0) {
962 if ((err
= cb(entry
, payload
)) != 0) {
963 giterr_set_after_callback(err
);
969 if (err
== GIT_ITEROVER
)
972 if (found
== 0 && err
== 0)
973 err
= config_error_notfound(name
);
979 git_config_iterator parent
;
980 git_config_iterator
*iter
;
986 static int multivar_iter_next(git_config_entry
**entry
, git_config_iterator
*_iter
)
988 multivar_iter
*iter
= (multivar_iter
*) _iter
;
991 while ((error
= iter
->iter
->next(entry
, iter
->iter
)) == 0) {
992 if (git__strcmp(iter
->name
, (*entry
)->name
))
995 if (!iter
->have_regex
)
998 if (regexec(&iter
->regex
, (*entry
)->value
, 0, NULL
, 0) == 0)
1005 void multivar_iter_free(git_config_iterator
*_iter
)
1007 multivar_iter
*iter
= (multivar_iter
*) _iter
;
1009 iter
->iter
->free(iter
->iter
);
1011 git__free(iter
->name
);
1012 if (iter
->have_regex
)
1013 regfree(&iter
->regex
);
1017 int git_config_multivar_iterator_new(git_config_iterator
**out
, const git_config
*cfg
, const char *name
, const char *regexp
)
1019 multivar_iter
*iter
= NULL
;
1020 git_config_iterator
*inner
= NULL
;
1023 if ((error
= git_config_iterator_new(&inner
, cfg
)) < 0)
1026 iter
= git__calloc(1, sizeof(multivar_iter
));
1027 GITERR_CHECK_ALLOC(iter
);
1029 if ((error
= git_config__normalize_name(name
, &iter
->name
)) < 0)
1032 if (regexp
!= NULL
) {
1033 error
= p_regcomp(&iter
->regex
, regexp
, REG_EXTENDED
);
1035 giterr_set_regex(&iter
->regex
, error
);
1037 regfree(&iter
->regex
);
1041 iter
->have_regex
= 1;
1045 iter
->parent
.free
= multivar_iter_free
;
1046 iter
->parent
.next
= multivar_iter_next
;
1048 *out
= (git_config_iterator
*) iter
;
1059 int git_config_set_multivar(git_config
*cfg
, const char *name
, const char *regexp
, const char *value
)
1061 git_config_backend
*file
;
1063 if (get_backend_for_use(&file
, cfg
, name
, BACKEND_USE_DELETE
) < 0)
1064 return GIT_ENOTFOUND
;
1066 return file
->set_multivar(file
, name
, regexp
, value
);
1069 int git_config_delete_multivar(git_config
*cfg
, const char *name
, const char *regexp
)
1071 git_config_backend
*file
;
1073 if (get_backend_for_use(&file
, cfg
, name
, BACKEND_USE_DELETE
) < 0)
1074 return GIT_ENOTFOUND
;
1076 return file
->del_multivar(file
, name
, regexp
);
1079 int git_config_next(git_config_entry
**entry
, git_config_iterator
*iter
)
1081 return iter
->next(entry
, iter
);
1084 void git_config_iterator_free(git_config_iterator
*iter
)
1092 int git_config_find_global(git_buf
*path
)
1094 git_buf_sanitize(path
);
1095 return git_sysdir_find_global_file(path
, GIT_CONFIG_FILENAME_GLOBAL
);
1098 int git_config_find_xdg(git_buf
*path
)
1100 git_buf_sanitize(path
);
1101 return git_sysdir_find_xdg_file(path
, GIT_CONFIG_FILENAME_XDG
);
1104 int git_config_find_system(git_buf
*path
)
1106 git_buf_sanitize(path
);
1107 return git_sysdir_find_system_file(path
, GIT_CONFIG_FILENAME_SYSTEM
);
1110 int git_config_find_programdata(git_buf
*path
)
1112 git_buf_sanitize(path
);
1113 return git_sysdir_find_programdata_file(path
, GIT_CONFIG_FILENAME_PROGRAMDATA
);
1116 int git_config__global_location(git_buf
*buf
)
1118 const git_buf
*paths
;
1119 const char *sep
, *start
;
1121 if (git_sysdir_get(&paths
, GIT_SYSDIR_GLOBAL
) < 0)
1124 /* no paths, so give up */
1125 if (!paths
|| !git_buf_len(paths
))
1128 /* find unescaped separator or end of string */
1129 for (sep
= start
= git_buf_cstr(paths
); *sep
; ++sep
) {
1130 if (*sep
== GIT_PATH_LIST_SEPARATOR
&&
1131 (sep
<= start
|| sep
[-1] != '\\'))
1135 if (git_buf_set(buf
, start
, (size_t)(sep
- start
)) < 0)
1138 return git_buf_joinpath(buf
, buf
->ptr
, GIT_CONFIG_FILENAME_GLOBAL
);
1141 int git_config_open_default(git_config
**out
)
1144 git_config
*cfg
= NULL
;
1145 git_buf buf
= GIT_BUF_INIT
;
1147 if ((error
= git_config_new(&cfg
)) < 0)
1150 if (!git_config_find_global(&buf
) || !git_config__global_location(&buf
)) {
1151 error
= git_config_add_file_ondisk(cfg
, buf
.ptr
,
1152 GIT_CONFIG_LEVEL_GLOBAL
, NULL
, 0);
1155 if (!error
&& !git_config_find_xdg(&buf
))
1156 error
= git_config_add_file_ondisk(cfg
, buf
.ptr
,
1157 GIT_CONFIG_LEVEL_XDG
, NULL
, 0);
1159 if (!error
&& !git_config_find_system(&buf
))
1160 error
= git_config_add_file_ondisk(cfg
, buf
.ptr
,
1161 GIT_CONFIG_LEVEL_SYSTEM
, NULL
, 0);
1163 if (!error
&& !git_config_find_programdata(&buf
))
1164 error
= git_config_add_file_ondisk(cfg
, buf
.ptr
,
1165 GIT_CONFIG_LEVEL_PROGRAMDATA
, NULL
, 0);
1170 git_config_free(cfg
);
1179 int git_config_lock(git_transaction
**out
, git_config
*cfg
)
1182 git_config_backend
*file
;
1183 file_internal
*internal
;
1185 internal
= git_vector_get(&cfg
->files
, 0);
1186 if (!internal
|| !internal
->file
) {
1187 giterr_set(GITERR_CONFIG
, "cannot lock; the config has no backends/files");
1190 file
= internal
->file
;
1192 if ((error
= file
->lock(file
)) < 0)
1195 return git_transaction_config_new(out
, cfg
);
1198 int git_config_unlock(git_config
*cfg
, int commit
)
1200 git_config_backend
*file
;
1201 file_internal
*internal
;
1203 internal
= git_vector_get(&cfg
->files
, 0);
1204 if (!internal
|| !internal
->file
) {
1205 giterr_set(GITERR_CONFIG
, "cannot lock; the config has no backends/files");
1209 file
= internal
->file
;
1211 return file
->unlock(file
, commit
);
1218 int git_config_lookup_map_value(
1220 const git_cvar_map
*maps
,
1229 for (i
= 0; i
< map_n
; ++i
) {
1230 const git_cvar_map
*m
= maps
+ i
;
1232 switch (m
->cvar_type
) {
1233 case GIT_CVAR_FALSE
:
1234 case GIT_CVAR_TRUE
: {
1237 if (git__parse_bool(&bool_val
, value
) == 0 &&
1238 bool_val
== (int)m
->cvar_type
) {
1239 *out
= m
->map_value
;
1245 case GIT_CVAR_INT32
:
1246 if (git_config_parse_int32(out
, value
) == 0)
1250 case GIT_CVAR_STRING
:
1251 if (strcasecmp(value
, m
->str_match
) == 0) {
1252 *out
= m
->map_value
;
1260 giterr_set(GITERR_CONFIG
, "failed to map '%s'", value
);
1264 int git_config_lookup_map_enum(git_cvar_t
*type_out
, const char **str_out
,
1265 const git_cvar_map
*maps
, size_t map_n
, int enum_val
)
1269 for (i
= 0; i
< map_n
; i
++) {
1270 const git_cvar_map
*m
= &maps
[i
];
1272 if (m
->map_value
!= enum_val
)
1275 *type_out
= m
->cvar_type
;
1276 *str_out
= m
->str_match
;
1280 giterr_set(GITERR_CONFIG
, "invalid enum value");
1281 return GIT_ENOTFOUND
;
1284 int git_config_parse_bool(int *out
, const char *value
)
1286 if (git__parse_bool(out
, value
) == 0)
1289 if (git_config_parse_int32(out
, value
) == 0) {
1294 giterr_set(GITERR_CONFIG
, "failed to parse '%s' as a boolean value", value
);
1298 int git_config_parse_int64(int64_t *out
, const char *value
)
1300 const char *num_end
;
1303 if (!value
|| git__strntol64(&num
, value
, strlen(value
), &num_end
, 0) < 0)
1321 /* check that that there are no more characters after the
1322 * given modifier suffix */
1323 if (num_end
[1] != '\0')
1337 giterr_set(GITERR_CONFIG
, "failed to parse '%s' as an integer", value
? value
: "(null)");
1341 int git_config_parse_int32(int32_t *out
, const char *value
)
1346 if (git_config_parse_int64(&tmp
, value
) < 0)
1349 truncate
= tmp
& 0xFFFFFFFF;
1350 if (truncate
!= tmp
)
1357 giterr_set(GITERR_CONFIG
, "failed to parse '%s' as a 32-bit integer", value
? value
: "(null)");
1361 int git_config_parse_path(git_buf
*out
, const char *value
)
1363 assert(out
&& value
);
1365 git_buf_sanitize(out
);
1367 if (value
[0] == '~') {
1368 if (value
[1] != '\0' && value
[1] != '/') {
1369 giterr_set(GITERR_CONFIG
, "retrieving a homedir by name is not supported");
1373 return git_sysdir_expand_global_file(out
, value
[1] ? &value
[2] : NULL
);
1376 return git_buf_sets(out
, value
);
1379 /* Take something the user gave us and make it nice for our hash function */
1380 int git_config__normalize_name(const char *in
, char **out
)
1382 char *name
, *fdot
, *ldot
;
1386 name
= git__strdup(in
);
1387 GITERR_CHECK_ALLOC(name
);
1389 fdot
= strchr(name
, '.');
1390 ldot
= strrchr(name
, '.');
1392 if (fdot
== NULL
|| fdot
== name
|| ldot
== NULL
|| !ldot
[1])
1395 /* Validate and downcase up to first dot and after last dot */
1396 if (git_config_file_normalize_section(name
, fdot
) < 0 ||
1397 git_config_file_normalize_section(ldot
+ 1, NULL
) < 0)
1400 /* If there is a middle range, make sure it doesn't have newlines */
1402 if (*fdot
++ == '\n')
1410 giterr_set(GITERR_CONFIG
, "invalid config item name '%s'", in
);
1411 return GIT_EINVALIDSPEC
;
1414 struct rename_data
{
1420 static int rename_config_entries_cb(
1421 const git_config_entry
*entry
,
1425 struct rename_data
*data
= (struct rename_data
*)payload
;
1426 size_t base_len
= git_buf_len(data
->name
);
1429 !(error
= git_buf_puts(data
->name
, entry
->name
+ data
->old_len
)))
1431 error
= git_config_set_string(
1432 data
->config
, git_buf_cstr(data
->name
), entry
->value
);
1434 git_buf_truncate(data
->name
, base_len
);
1438 error
= git_config_delete_entry(data
->config
, entry
->name
);
1443 int git_config_rename_section(
1444 git_repository
*repo
,
1445 const char *old_section_name
,
1446 const char *new_section_name
)
1449 git_buf pattern
= GIT_BUF_INIT
, replace
= GIT_BUF_INIT
;
1451 struct rename_data data
;
1453 git_buf_text_puts_escape_regex(&pattern
, old_section_name
);
1455 if ((error
= git_buf_puts(&pattern
, "\\..+")) < 0)
1458 if ((error
= git_repository_config__weakptr(&config
, repo
)) < 0)
1461 data
.config
= config
;
1462 data
.name
= &replace
;
1463 data
.old_len
= strlen(old_section_name
) + 1;
1465 if ((error
= git_buf_join(&replace
, '.', new_section_name
, "")) < 0)
1468 if (new_section_name
!= NULL
&&
1469 (error
= git_config_file_normalize_section(
1470 replace
.ptr
, strchr(replace
.ptr
, '.'))) < 0)
1473 GITERR_CONFIG
, "invalid config section '%s'", new_section_name
);
1477 error
= git_config_foreach_match(
1478 config
, git_buf_cstr(&pattern
), rename_config_entries_cb
, &data
);
1481 git_buf_free(&pattern
);
1482 git_buf_free(&replace
);
1487 int git_config_init_backend(git_config_backend
*backend
, unsigned int version
)
1489 GIT_INIT_STRUCTURE_FROM_TEMPLATE(
1490 backend
, version
, git_config_backend
, GIT_CONFIG_BACKEND_INIT
);