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
,
104 git_config_backend
*file
= NULL
;
110 res
= p_stat(path
, &st
);
111 if (res
< 0 && errno
!= ENOENT
) {
112 giterr_set(GITERR_CONFIG
, "Error stat'ing config file '%s'", path
);
116 if (git_config_file__ondisk(&file
, path
) < 0)
119 if ((res
= git_config_add_backend(cfg
, file
, level
, force
)) < 0) {
121 * free manually; the file is not owned by the config
122 * instance yet and will not be freed on cleanup
131 int git_config_open_ondisk(git_config
**out
, const char *path
)
138 if (git_config_new(&config
) < 0)
141 if ((error
= git_config_add_file_ondisk(config
, path
, GIT_CONFIG_LEVEL_LOCAL
, 0)) < 0)
142 git_config_free(config
);
149 int git_config_snapshot(git_config
**out
, git_config
*in
)
153 file_internal
*internal
;
158 if (git_config_new(&config
) < 0)
161 git_vector_foreach(&in
->files
, i
, internal
) {
162 git_config_backend
*b
;
164 if ((error
= internal
->file
->snapshot(&b
, internal
->file
)) < 0)
167 if ((error
= git_config_add_backend(config
, b
, internal
->level
, 0)) < 0) {
174 git_config_free(config
);
181 static int find_internal_file_by_level(
182 file_internal
**internal_out
,
183 const git_config
*cfg
,
184 git_config_level_t level
)
187 file_internal
*internal
;
190 /* when passing GIT_CONFIG_HIGHEST_LEVEL, the idea is to get the config file
191 * which has the highest level. As config files are stored in a vector
192 * sorted by decreasing order of level, getting the file at position 0
195 if (level
== GIT_CONFIG_HIGHEST_LEVEL
) {
198 git_vector_foreach(&cfg
->files
, i
, internal
) {
199 if (internal
->level
== level
)
205 giterr_set(GITERR_CONFIG
,
206 "No config file exists for the given level '%i'", (int)level
);
207 return GIT_ENOTFOUND
;
210 *internal_out
= git_vector_get(&cfg
->files
, pos
);
215 static int duplicate_level(void **old_raw
, void *new_raw
)
217 file_internal
**old
= (file_internal
**)old_raw
;
221 giterr_set(GITERR_CONFIG
, "A file with the same level (%i) has already been added to the config", (int)(*old
)->level
);
225 static void try_remove_existing_file_internal(
227 git_config_level_t level
)
230 file_internal
*internal
;
233 git_vector_foreach(&cfg
->files
, i
, internal
) {
234 if (internal
->level
== level
)
241 internal
= git_vector_get(&cfg
->files
, pos
);
243 if (git_vector_remove(&cfg
->files
, pos
) < 0)
246 GIT_REFCOUNT_DEC(internal
, file_internal_free
);
249 static int git_config__add_internal(
251 file_internal
*internal
,
252 git_config_level_t level
,
257 /* delete existing config file for level if it exists */
259 try_remove_existing_file_internal(cfg
, level
);
261 if ((result
= git_vector_insert_sorted(&cfg
->files
,
262 internal
, &duplicate_level
)) < 0)
265 git_vector_sort(&cfg
->files
);
266 internal
->file
->cfg
= cfg
;
268 GIT_REFCOUNT_INC(internal
);
273 int git_config_open_global(git_config
**cfg_out
, git_config
*cfg
)
275 if (!git_config_open_level(cfg_out
, cfg
, GIT_CONFIG_LEVEL_XDG
))
278 return git_config_open_level(cfg_out
, cfg
, GIT_CONFIG_LEVEL_GLOBAL
);
281 int git_config_open_level(
282 git_config
**cfg_out
,
283 const git_config
*cfg_parent
,
284 git_config_level_t level
)
287 file_internal
*internal
;
290 if ((res
= find_internal_file_by_level(&internal
, cfg_parent
, level
)) < 0)
293 if ((res
= git_config_new(&cfg
)) < 0)
296 if ((res
= git_config__add_internal(cfg
, internal
, level
, true)) < 0) {
297 git_config_free(cfg
);
306 int git_config_add_backend(
308 git_config_backend
*file
,
309 git_config_level_t level
,
312 file_internal
*internal
;
317 GITERR_CHECK_VERSION(file
, GIT_CONFIG_BACKEND_VERSION
, "git_config_backend");
319 if ((result
= file
->open(file
, level
)) < 0)
322 internal
= git__malloc(sizeof(file_internal
));
323 GITERR_CHECK_ALLOC(internal
);
325 memset(internal
, 0x0, sizeof(file_internal
));
327 internal
->file
= file
;
328 internal
->level
= level
;
330 if ((result
= git_config__add_internal(cfg
, internal
, level
, force
)) < 0) {
339 * Loop over all the variables
343 git_config_iterator parent
;
344 git_config_iterator
*current
;
345 const git_config
*cfg
;
350 static int find_next_backend(size_t *out
, const git_config
*cfg
, size_t i
)
352 file_internal
*internal
;
355 internal
= git_vector_get(&cfg
->files
, i
- 1);
356 if (!internal
|| !internal
->file
)
366 static int all_iter_next(git_config_entry
**entry
, git_config_iterator
*_iter
)
368 all_iter
*iter
= (all_iter
*) _iter
;
369 file_internal
*internal
;
370 git_config_backend
*backend
;
374 if (iter
->current
!= NULL
&&
375 (error
= iter
->current
->next(entry
, iter
->current
)) == 0) {
379 if (error
< 0 && error
!= GIT_ITEROVER
)
383 if (find_next_backend(&i
, iter
->cfg
, iter
->i
) < 0)
386 internal
= git_vector_get(&iter
->cfg
->files
, i
- 1);
387 backend
= internal
->file
;
391 iter
->current
->free(iter
->current
);
393 iter
->current
= NULL
;
394 error
= backend
->iterator(&iter
->current
, backend
);
395 if (error
== GIT_ENOTFOUND
)
401 error
= iter
->current
->next(entry
, iter
->current
);
402 /* If this backend is empty, then keep going */
403 if (error
== GIT_ITEROVER
)
413 static int all_iter_glob_next(git_config_entry
**entry
, git_config_iterator
*_iter
)
416 all_iter
*iter
= (all_iter
*) _iter
;
419 * We use the "normal" function to grab the next one across
420 * backends and then apply the regex
422 while ((error
= all_iter_next(entry
, _iter
)) == 0) {
423 /* skip non-matching keys if regexp was provided */
424 if (regexec(&iter
->regex
, (*entry
)->name
, 0, NULL
, 0) != 0)
427 /* and simply return if we like the entry's name */
434 static void all_iter_free(git_config_iterator
*_iter
)
436 all_iter
*iter
= (all_iter
*) _iter
;
439 iter
->current
->free(iter
->current
);
444 static void all_iter_glob_free(git_config_iterator
*_iter
)
446 all_iter
*iter
= (all_iter
*) _iter
;
448 regfree(&iter
->regex
);
449 all_iter_free(_iter
);
452 int git_config_iterator_new(git_config_iterator
**out
, const git_config
*cfg
)
456 iter
= git__calloc(1, sizeof(all_iter
));
457 GITERR_CHECK_ALLOC(iter
);
459 iter
->parent
.free
= all_iter_free
;
460 iter
->parent
.next
= all_iter_next
;
462 iter
->i
= cfg
->files
.length
;
465 *out
= (git_config_iterator
*) iter
;
470 int git_config_iterator_glob_new(git_config_iterator
**out
, const git_config
*cfg
, const char *regexp
)
476 return git_config_iterator_new(out
, cfg
);
478 iter
= git__calloc(1, sizeof(all_iter
));
479 GITERR_CHECK_ALLOC(iter
);
481 if ((result
= regcomp(&iter
->regex
, regexp
, REG_EXTENDED
)) != 0) {
482 giterr_set_regex(&iter
->regex
, result
);
487 iter
->parent
.next
= all_iter_glob_next
;
488 iter
->parent
.free
= all_iter_glob_free
;
489 iter
->i
= cfg
->files
.length
;
492 *out
= (git_config_iterator
*) iter
;
497 int git_config_foreach(
498 const git_config
*cfg
, git_config_foreach_cb cb
, void *payload
)
500 return git_config_foreach_match(cfg
, NULL
, cb
, payload
);
503 int git_config_backend_foreach_match(
504 git_config_backend
*backend
,
506 git_config_foreach_cb cb
,
509 git_config_entry
*entry
;
510 git_config_iterator
* iter
;
514 if (regexp
!= NULL
) {
515 if ((error
= regcomp(®ex
, regexp
, REG_EXTENDED
)) != 0) {
516 giterr_set_regex(®ex
, error
);
522 if ((error
= backend
->iterator(&iter
, backend
)) < 0) {
527 while (!(iter
->next(&entry
, iter
) < 0)) {
528 /* skip non-matching keys if regexp was provided */
529 if (regexp
&& regexec(®ex
, entry
->name
, 0, NULL
, 0) != 0)
532 /* abort iterator on non-zero return value */
533 if ((error
= cb(entry
, payload
)) != 0) {
534 giterr_set_after_callback(error
);
547 int git_config_foreach_match(
548 const git_config
*cfg
,
550 git_config_foreach_cb cb
,
554 git_config_iterator
*iter
;
555 git_config_entry
*entry
;
557 if ((error
= git_config_iterator_glob_new(&iter
, cfg
, regexp
)) < 0)
560 while (!(error
= git_config_next(&entry
, iter
))) {
561 if ((error
= cb(entry
, payload
)) != 0) {
562 giterr_set_after_callback(error
);
567 git_config_iterator_free(iter
);
569 if (error
== GIT_ITEROVER
)
579 static int config_error_nofiles(const char *name
)
581 giterr_set(GITERR_CONFIG
,
582 "Cannot set value for '%s' when no config files exist", name
);
583 return GIT_ENOTFOUND
;
586 int git_config_delete_entry(git_config
*cfg
, const char *name
)
588 git_config_backend
*file
;
589 file_internal
*internal
;
591 internal
= git_vector_get(&cfg
->files
, 0);
592 if (!internal
|| !internal
->file
)
593 return config_error_nofiles(name
);
594 file
= internal
->file
;
596 return file
->del(file
, name
);
599 int git_config_set_int64(git_config
*cfg
, const char *name
, int64_t value
)
601 char str_value
[32]; /* All numbers should fit in here */
602 p_snprintf(str_value
, sizeof(str_value
), "%" PRId64
, value
);
603 return git_config_set_string(cfg
, name
, str_value
);
606 int git_config_set_int32(git_config
*cfg
, const char *name
, int32_t value
)
608 return git_config_set_int64(cfg
, name
, (int64_t)value
);
611 int git_config_set_bool(git_config
*cfg
, const char *name
, int value
)
613 return git_config_set_string(cfg
, name
, value
? "true" : "false");
616 int git_config_set_string(git_config
*cfg
, const char *name
, const char *value
)
619 git_config_backend
*file
;
620 file_internal
*internal
;
623 giterr_set(GITERR_CONFIG
, "The value to set cannot be NULL");
627 internal
= git_vector_get(&cfg
->files
, 0);
628 if (!internal
|| !internal
->file
)
629 return config_error_nofiles(name
);
630 file
= internal
->file
;
632 error
= file
->set(file
, name
, value
);
634 if (!error
&& GIT_REFCOUNT_OWNER(cfg
) != NULL
)
635 git_repository__cvar_cache_clear(GIT_REFCOUNT_OWNER(cfg
));
640 int git_config__update_entry(
644 bool overwrite_existing
,
645 bool only_if_existing
)
648 git_config_entry
*ce
= NULL
;
650 if ((error
= git_config__lookup_entry(&ce
, config
, key
, false)) < 0)
653 if (!ce
&& only_if_existing
) /* entry doesn't exist */
655 if (ce
&& !overwrite_existing
) /* entry would be overwritten */
657 if (value
&& ce
&& ce
->value
&& !strcmp(ce
->value
, value
)) /* no change */
659 if (!value
&& (!ce
|| !ce
->value
)) /* asked to delete absent entry */
663 error
= git_config_delete_entry(config
, key
);
665 error
= git_config_set_string(config
, key
, value
);
667 git_config_entry_free(ce
);
675 static int config_error_notfound(const char *name
)
677 giterr_set(GITERR_CONFIG
, "Config value '%s' was not found", name
);
678 return GIT_ENOTFOUND
;
687 static int get_entry(
688 git_config_entry
**out
,
689 const git_config
*cfg
,
694 int res
= GIT_ENOTFOUND
;
695 const char *key
= name
;
696 char *normalized
= NULL
;
698 file_internal
*internal
;
702 if (normalize_name
) {
703 if ((res
= git_config__normalize_name(name
, &normalized
)) < 0)
709 git_vector_foreach(&cfg
->files
, i
, internal
) {
710 if (!internal
|| !internal
->file
)
713 res
= internal
->file
->get(internal
->file
, key
, out
);
714 if (res
!= GIT_ENOTFOUND
)
718 git__free(normalized
);
721 if (res
== GIT_ENOTFOUND
)
722 res
= (want_errors
> GET_ALL_ERRORS
) ? 0 : config_error_notfound(name
);
723 else if (res
&& (want_errors
== GET_NO_ERRORS
)) {
731 int git_config_get_entry(
732 git_config_entry
**out
, const git_config
*cfg
, const char *name
)
734 return get_entry(out
, cfg
, name
, true, GET_ALL_ERRORS
);
737 int git_config__lookup_entry(
738 git_config_entry
**out
,
739 const git_config
*cfg
,
744 out
, cfg
, key
, false, no_errors
? GET_NO_ERRORS
: GET_NO_MISSING
);
747 int git_config_get_mapped(
749 const git_config
*cfg
,
751 const git_cvar_map
*maps
,
754 git_config_entry
*entry
;
757 if ((ret
= get_entry(&entry
, cfg
, name
, true, GET_ALL_ERRORS
)) < 0)
760 ret
= git_config_lookup_map_value(out
, maps
, map_n
, entry
->value
);
761 git_config_entry_free(entry
);
766 int git_config_get_int64(int64_t *out
, const git_config
*cfg
, const char *name
)
768 git_config_entry
*entry
;
771 if ((ret
= get_entry(&entry
, cfg
, name
, true, GET_ALL_ERRORS
)) < 0)
774 ret
= git_config_parse_int64(out
, entry
->value
);
775 git_config_entry_free(entry
);
780 int git_config_get_int32(int32_t *out
, const git_config
*cfg
, const char *name
)
782 git_config_entry
*entry
;
785 if ((ret
= get_entry(&entry
, cfg
, name
, true, GET_ALL_ERRORS
)) < 0)
788 ret
= git_config_parse_int32(out
, entry
->value
);
789 git_config_entry_free(entry
);
794 int git_config_get_bool(int *out
, const git_config
*cfg
, const char *name
)
796 git_config_entry
*entry
;
799 if ((ret
= get_entry(&entry
, cfg
, name
, true, GET_ALL_ERRORS
)) < 0)
802 ret
= git_config_parse_bool(out
, entry
->value
);
803 git_config_entry_free(entry
);
808 static int is_readonly(const git_config
*cfg
)
811 file_internal
*internal
;
813 git_vector_foreach(&cfg
->files
, i
, internal
) {
814 if (!internal
|| !internal
->file
)
817 if (!internal
->file
->readonly
)
824 int git_config_get_path(git_buf
*out
, const git_config
*cfg
, const char *name
)
826 git_config_entry
*entry
;
829 if ((error
= get_entry(&entry
, cfg
, name
, true, GET_ALL_ERRORS
)) < 0)
832 error
= git_config_parse_path(out
, entry
->value
);
833 git_config_entry_free(entry
);
838 int git_config_get_string(
839 const char **out
, const git_config
*cfg
, const char *name
)
841 git_config_entry
*entry
;
844 if (!is_readonly(cfg
)) {
845 giterr_set(GITERR_CONFIG
, "get_string called on a live config object");
849 ret
= get_entry(&entry
, cfg
, name
, true, GET_ALL_ERRORS
);
850 *out
= !ret
? (entry
->value
? entry
->value
: "") : NULL
;
852 git_config_entry_free(entry
);
857 int git_config_get_string_buf(
858 git_buf
*out
, const git_config
*cfg
, const char *name
)
860 git_config_entry
*entry
;
864 git_buf_sanitize(out
);
866 ret
= get_entry(&entry
, cfg
, name
, true, GET_ALL_ERRORS
);
867 str
= !ret
? (entry
->value
? entry
->value
: "") : NULL
;
870 ret
= git_buf_puts(out
, str
);
872 git_config_entry_free(entry
);
877 char *git_config__get_string_force(
878 const git_config
*cfg
, const char *key
, const char *fallback_value
)
880 git_config_entry
*entry
;
883 get_entry(&entry
, cfg
, key
, false, GET_NO_ERRORS
);
884 ret
= (entry
&& entry
->value
) ? git__strdup(entry
->value
) : fallback_value
? git__strdup(fallback_value
) : NULL
;
885 git_config_entry_free(entry
);
890 int git_config__get_bool_force(
891 const git_config
*cfg
, const char *key
, int fallback_value
)
893 int val
= fallback_value
;
894 git_config_entry
*entry
;
896 get_entry(&entry
, cfg
, key
, false, GET_NO_ERRORS
);
898 if (entry
&& git_config_parse_bool(&val
, entry
->value
) < 0)
901 git_config_entry_free(entry
);
905 int git_config__get_int_force(
906 const git_config
*cfg
, const char *key
, int fallback_value
)
908 int32_t val
= (int32_t)fallback_value
;
909 git_config_entry
*entry
;
911 get_entry(&entry
, cfg
, key
, false, GET_NO_ERRORS
);
913 if (entry
&& git_config_parse_int32(&val
, entry
->value
) < 0)
916 git_config_entry_free(entry
);
920 int git_config_get_multivar_foreach(
921 const git_config
*cfg
, const char *name
, const char *regexp
,
922 git_config_foreach_cb cb
, void *payload
)
925 git_config_iterator
*iter
;
926 git_config_entry
*entry
;
928 if ((err
= git_config_multivar_iterator_new(&iter
, cfg
, name
, regexp
)) < 0)
932 while ((err
= iter
->next(&entry
, iter
)) == 0) {
935 if ((err
= cb(entry
, payload
)) != 0) {
936 giterr_set_after_callback(err
);
942 if (err
== GIT_ITEROVER
)
945 if (found
== 0 && err
== 0)
946 err
= config_error_notfound(name
);
952 git_config_iterator parent
;
953 git_config_iterator
*iter
;
959 static int multivar_iter_next(git_config_entry
**entry
, git_config_iterator
*_iter
)
961 multivar_iter
*iter
= (multivar_iter
*) _iter
;
964 while ((error
= iter
->iter
->next(entry
, iter
->iter
)) == 0) {
965 if (git__strcmp(iter
->name
, (*entry
)->name
))
968 if (!iter
->have_regex
)
971 if (regexec(&iter
->regex
, (*entry
)->value
, 0, NULL
, 0) == 0)
978 void multivar_iter_free(git_config_iterator
*_iter
)
980 multivar_iter
*iter
= (multivar_iter
*) _iter
;
982 iter
->iter
->free(iter
->iter
);
984 git__free(iter
->name
);
985 if (iter
->have_regex
)
986 regfree(&iter
->regex
);
990 int git_config_multivar_iterator_new(git_config_iterator
**out
, const git_config
*cfg
, const char *name
, const char *regexp
)
992 multivar_iter
*iter
= NULL
;
993 git_config_iterator
*inner
= NULL
;
996 if ((error
= git_config_iterator_new(&inner
, cfg
)) < 0)
999 iter
= git__calloc(1, sizeof(multivar_iter
));
1000 GITERR_CHECK_ALLOC(iter
);
1002 if ((error
= git_config__normalize_name(name
, &iter
->name
)) < 0)
1005 if (regexp
!= NULL
) {
1006 error
= regcomp(&iter
->regex
, regexp
, REG_EXTENDED
);
1008 giterr_set_regex(&iter
->regex
, error
);
1010 regfree(&iter
->regex
);
1014 iter
->have_regex
= 1;
1018 iter
->parent
.free
= multivar_iter_free
;
1019 iter
->parent
.next
= multivar_iter_next
;
1021 *out
= (git_config_iterator
*) iter
;
1032 int git_config_set_multivar(git_config
*cfg
, const char *name
, const char *regexp
, const char *value
)
1034 git_config_backend
*file
;
1035 file_internal
*internal
;
1037 internal
= git_vector_get(&cfg
->files
, 0);
1038 if (!internal
|| !internal
->file
)
1039 return config_error_nofiles(name
);
1040 file
= internal
->file
;
1042 return file
->set_multivar(file
, name
, regexp
, value
);
1045 int git_config_delete_multivar(git_config
*cfg
, const char *name
, const char *regexp
)
1047 git_config_backend
*file
;
1048 file_internal
*internal
;
1050 internal
= git_vector_get(&cfg
->files
, 0);
1051 if (!internal
|| !internal
->file
)
1052 return config_error_nofiles(name
);
1053 file
= internal
->file
;
1055 return file
->del_multivar(file
, name
, regexp
);
1058 int git_config_next(git_config_entry
**entry
, git_config_iterator
*iter
)
1060 return iter
->next(entry
, iter
);
1063 void git_config_iterator_free(git_config_iterator
*iter
)
1071 int git_config_find_global(git_buf
*path
)
1073 git_buf_sanitize(path
);
1074 return git_sysdir_find_global_file(path
, GIT_CONFIG_FILENAME_GLOBAL
);
1077 int git_config_find_xdg(git_buf
*path
)
1079 git_buf_sanitize(path
);
1080 return git_sysdir_find_xdg_file(path
, GIT_CONFIG_FILENAME_XDG
);
1083 int git_config_find_system(git_buf
*path
)
1085 git_buf_sanitize(path
);
1086 return git_sysdir_find_system_file(path
, GIT_CONFIG_FILENAME_SYSTEM
);
1089 int git_config_find_programdata(git_buf
*path
)
1091 git_buf_sanitize(path
);
1092 return git_sysdir_find_programdata_file(path
, GIT_CONFIG_FILENAME_PROGRAMDATA
);
1095 int git_config__global_location(git_buf
*buf
)
1097 const git_buf
*paths
;
1098 const char *sep
, *start
;
1100 if (git_sysdir_get(&paths
, GIT_SYSDIR_GLOBAL
) < 0)
1103 /* no paths, so give up */
1104 if (!paths
|| !git_buf_len(paths
))
1107 /* find unescaped separator or end of string */
1108 for (sep
= start
= git_buf_cstr(paths
); *sep
; ++sep
) {
1109 if (*sep
== GIT_PATH_LIST_SEPARATOR
&&
1110 (sep
<= start
|| sep
[-1] != '\\'))
1114 if (git_buf_set(buf
, start
, (size_t)(sep
- start
)) < 0)
1117 return git_buf_joinpath(buf
, buf
->ptr
, GIT_CONFIG_FILENAME_GLOBAL
);
1120 int git_config_open_default(git_config
**out
)
1123 git_config
*cfg
= NULL
;
1124 git_buf buf
= GIT_BUF_INIT
;
1126 if ((error
= git_config_new(&cfg
)) < 0)
1129 if (!git_config_find_global(&buf
) || !git_config__global_location(&buf
)) {
1130 error
= git_config_add_file_ondisk(cfg
, buf
.ptr
,
1131 GIT_CONFIG_LEVEL_GLOBAL
, 0);
1134 if (!error
&& !git_config_find_xdg(&buf
))
1135 error
= git_config_add_file_ondisk(cfg
, buf
.ptr
,
1136 GIT_CONFIG_LEVEL_XDG
, 0);
1138 if (!error
&& !git_config_find_system(&buf
))
1139 error
= git_config_add_file_ondisk(cfg
, buf
.ptr
,
1140 GIT_CONFIG_LEVEL_SYSTEM
, 0);
1142 if (!error
&& !git_config_find_programdata(&buf
))
1143 error
= git_config_add_file_ondisk(cfg
, buf
.ptr
,
1144 GIT_CONFIG_LEVEL_PROGRAMDATA
, 0);
1149 git_config_free(cfg
);
1158 int git_config_lock(git_transaction
**out
, git_config
*cfg
)
1161 git_config_backend
*file
;
1162 file_internal
*internal
;
1164 internal
= git_vector_get(&cfg
->files
, 0);
1165 if (!internal
|| !internal
->file
) {
1166 giterr_set(GITERR_CONFIG
, "cannot lock; the config has no backends/files");
1169 file
= internal
->file
;
1171 if ((error
= file
->lock(file
)) < 0)
1174 return git_transaction_config_new(out
, cfg
);
1177 int git_config_unlock(git_config
*cfg
, int commit
)
1179 git_config_backend
*file
;
1180 file_internal
*internal
;
1182 internal
= git_vector_get(&cfg
->files
, 0);
1183 if (!internal
|| !internal
->file
) {
1184 giterr_set(GITERR_CONFIG
, "cannot lock; the config has no backends/files");
1188 file
= internal
->file
;
1190 return file
->unlock(file
, commit
);
1197 int git_config_lookup_map_value(
1199 const git_cvar_map
*maps
,
1208 for (i
= 0; i
< map_n
; ++i
) {
1209 const git_cvar_map
*m
= maps
+ i
;
1211 switch (m
->cvar_type
) {
1212 case GIT_CVAR_FALSE
:
1213 case GIT_CVAR_TRUE
: {
1216 if (git__parse_bool(&bool_val
, value
) == 0 &&
1217 bool_val
== (int)m
->cvar_type
) {
1218 *out
= m
->map_value
;
1224 case GIT_CVAR_INT32
:
1225 if (git_config_parse_int32(out
, value
) == 0)
1229 case GIT_CVAR_STRING
:
1230 if (strcasecmp(value
, m
->str_match
) == 0) {
1231 *out
= m
->map_value
;
1239 giterr_set(GITERR_CONFIG
, "Failed to map '%s'", value
);
1243 int git_config_lookup_map_enum(git_cvar_t
*type_out
, const char **str_out
,
1244 const git_cvar_map
*maps
, size_t map_n
, int enum_val
)
1248 for (i
= 0; i
< map_n
; i
++) {
1249 const git_cvar_map
*m
= &maps
[i
];
1251 if (m
->map_value
!= enum_val
)
1254 *type_out
= m
->cvar_type
;
1255 *str_out
= m
->str_match
;
1259 giterr_set(GITERR_CONFIG
, "invalid enum value");
1260 return GIT_ENOTFOUND
;
1263 int git_config_parse_bool(int *out
, const char *value
)
1265 if (git__parse_bool(out
, value
) == 0)
1268 if (git_config_parse_int32(out
, value
) == 0) {
1273 giterr_set(GITERR_CONFIG
, "Failed to parse '%s' as a boolean value", value
);
1277 int git_config_parse_int64(int64_t *out
, const char *value
)
1279 const char *num_end
;
1282 if (!value
|| git__strtol64(&num
, value
, &num_end
, 0) < 0)
1300 /* check that that there are no more characters after the
1301 * given modifier suffix */
1302 if (num_end
[1] != '\0')
1316 giterr_set(GITERR_CONFIG
, "Failed to parse '%s' as an integer", value
? value
: "(null)");
1320 int git_config_parse_int32(int32_t *out
, const char *value
)
1325 if (git_config_parse_int64(&tmp
, value
) < 0)
1328 truncate
= tmp
& 0xFFFFFFFF;
1329 if (truncate
!= tmp
)
1336 giterr_set(GITERR_CONFIG
, "Failed to parse '%s' as a 32-bit integer", value
? value
: "(null)");
1340 int git_config_parse_path(git_buf
*out
, const char *value
)
1343 const git_buf
*home
;
1345 assert(out
&& value
);
1347 git_buf_sanitize(out
);
1349 if (value
[0] == '~') {
1350 if (value
[1] != '\0' && value
[1] != '/') {
1351 giterr_set(GITERR_CONFIG
, "retrieving a homedir by name is not supported");
1355 if ((error
= git_sysdir_get(&home
, GIT_SYSDIR_GLOBAL
)) < 0)
1358 git_buf_sets(out
, home
->ptr
);
1359 git_buf_puts(out
, value
+ 1);
1361 if (git_buf_oom(out
))
1367 return git_buf_sets(out
, value
);
1370 /* Take something the user gave us and make it nice for our hash function */
1371 int git_config__normalize_name(const char *in
, char **out
)
1373 char *name
, *fdot
, *ldot
;
1377 name
= git__strdup(in
);
1378 GITERR_CHECK_ALLOC(name
);
1380 fdot
= strchr(name
, '.');
1381 ldot
= strrchr(name
, '.');
1383 if (fdot
== NULL
|| fdot
== name
|| ldot
== NULL
|| !ldot
[1])
1386 /* Validate and downcase up to first dot and after last dot */
1387 if (git_config_file_normalize_section(name
, fdot
) < 0 ||
1388 git_config_file_normalize_section(ldot
+ 1, NULL
) < 0)
1391 /* If there is a middle range, make sure it doesn't have newlines */
1393 if (*fdot
++ == '\n')
1401 giterr_set(GITERR_CONFIG
, "Invalid config item name '%s'", in
);
1402 return GIT_EINVALIDSPEC
;
1405 struct rename_data
{
1411 static int rename_config_entries_cb(
1412 const git_config_entry
*entry
,
1416 struct rename_data
*data
= (struct rename_data
*)payload
;
1417 size_t base_len
= git_buf_len(data
->name
);
1420 !(error
= git_buf_puts(data
->name
, entry
->name
+ data
->old_len
)))
1422 error
= git_config_set_string(
1423 data
->config
, git_buf_cstr(data
->name
), entry
->value
);
1425 git_buf_truncate(data
->name
, base_len
);
1429 error
= git_config_delete_entry(data
->config
, entry
->name
);
1434 int git_config_rename_section(
1435 git_repository
*repo
,
1436 const char *old_section_name
,
1437 const char *new_section_name
)
1440 git_buf pattern
= GIT_BUF_INIT
, replace
= GIT_BUF_INIT
;
1442 struct rename_data data
;
1444 git_buf_text_puts_escape_regex(&pattern
, old_section_name
);
1446 if ((error
= git_buf_puts(&pattern
, "\\..+")) < 0)
1449 if ((error
= git_repository_config__weakptr(&config
, repo
)) < 0)
1452 data
.config
= config
;
1453 data
.name
= &replace
;
1454 data
.old_len
= strlen(old_section_name
) + 1;
1456 if ((error
= git_buf_join(&replace
, '.', new_section_name
, "")) < 0)
1459 if (new_section_name
!= NULL
&&
1460 (error
= git_config_file_normalize_section(
1461 replace
.ptr
, strchr(replace
.ptr
, '.'))) < 0)
1464 GITERR_CONFIG
, "Invalid config section '%s'", new_section_name
);
1468 error
= git_config_foreach_match(
1469 config
, git_buf_cstr(&pattern
), rename_config_entries_cb
, &data
);
1472 git_buf_free(&pattern
);
1473 git_buf_free(&replace
);
1478 int git_config_init_backend(git_config_backend
*backend
, unsigned int version
)
1480 GIT_INIT_STRUCTURE_FROM_TEMPLATE(
1481 backend
, version
, git_config_backend
, GIT_CONFIG_BACKEND_INIT
);