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.
8 #include "config_file.h"
15 #include "git2/config.h"
16 #include "git2/sys/config.h"
17 #include "git2/types.h"
20 #include "config_parse.h"
23 #include <sys/types.h>
26 typedef struct cvar_t
{
28 git_config_entry
*entry
;
29 bool included
; /* whether this is part of [include] */
32 typedef struct git_config_file_iter
{
33 git_config_iterator parent
;
36 } git_config_file_iter
;
38 /* Max depth for [include] directives */
39 #define MAX_INCLUDE_DEPTH 10
41 #define CVAR_LIST_HEAD(list) ((list)->head)
43 #define CVAR_LIST_TAIL(list) ((list)->tail)
45 #define CVAR_LIST_NEXT(var) ((var)->next)
47 #define CVAR_LIST_EMPTY(list) ((list)->head == NULL)
49 #define CVAR_LIST_APPEND(list, var) do {\
50 if (CVAR_LIST_EMPTY(list)) {\
51 CVAR_LIST_HEAD(list) = CVAR_LIST_TAIL(list) = var;\
53 CVAR_LIST_NEXT(CVAR_LIST_TAIL(list)) = var;\
54 CVAR_LIST_TAIL(list) = var;\
58 #define CVAR_LIST_REMOVE_HEAD(list) do {\
59 CVAR_LIST_HEAD(list) = CVAR_LIST_NEXT(CVAR_LIST_HEAD(list));\
62 #define CVAR_LIST_REMOVE_AFTER(var) do {\
63 CVAR_LIST_NEXT(var) = CVAR_LIST_NEXT(CVAR_LIST_NEXT(var));\
66 #define CVAR_LIST_FOREACH(list, iter)\
67 for ((iter) = CVAR_LIST_HEAD(list);\
69 (iter) = CVAR_LIST_NEXT(iter))
72 * Inspired by the FreeBSD functions
74 #define CVAR_LIST_FOREACH_SAFE(start, iter, tmp)\
75 for ((iter) = CVAR_LIST_HEAD(vars);\
76 (iter) && (((tmp) = CVAR_LIST_NEXT(iter) || 1));\
85 git_config_backend parent
;
86 /* mutex to coordinate accessing the values */
87 git_mutex values_mutex
;
88 refcounted_strmap
*values
;
89 const git_repository
*repo
;
90 git_config_level_t level
;
94 diskfile_header header
;
96 git_array_t(git_config_parser
) readers
;
99 git_filebuf locked_buf
;
100 git_buf locked_content
;
102 struct config_file file
;
106 diskfile_header header
;
108 diskfile_backend
*snapshot_from
;
109 } diskfile_readonly_backend
;
111 static int config_read(git_strmap
*values
, const git_repository
*repo
, git_config_file
*file
, git_config_level_t level
, int depth
);
112 static int config_write(diskfile_backend
*cfg
, const char *orig_key
, const char *key
, const regex_t
*preg
, const char *value
);
113 static char *escape_value(const char *ptr
);
115 int git_config_file__snapshot(git_config_backend
**out
, diskfile_backend
*in
);
116 static int config_snapshot(git_config_backend
**out
, git_config_backend
*in
);
118 static int config_error_readonly(void)
120 giterr_set(GITERR_CONFIG
, "this backend is read-only");
124 static void cvar_free(cvar_t
*var
)
129 git__free((char*)var
->entry
->name
);
130 git__free((char *)var
->entry
->value
);
131 git__free(var
->entry
);
135 int git_config_file_normalize_section(char *start
, char *end
)
140 return GIT_EINVALIDSPEC
;
142 /* Validate and downcase range */
143 for (scan
= start
; *scan
; ++scan
) {
144 if (end
&& scan
>= end
)
147 *scan
= (char)git__tolower(*scan
);
148 else if (*scan
!= '-' || scan
== start
)
149 return GIT_EINVALIDSPEC
;
153 return GIT_EINVALIDSPEC
;
158 /* Add or append the new config option */
159 static int append_entry(git_strmap
*values
, cvar_t
*var
)
165 pos
= git_strmap_lookup_index(values
, var
->entry
->name
);
166 if (!git_strmap_valid_index(values
, pos
)) {
167 git_strmap_insert(values
, var
->entry
->name
, var
, &error
);
169 existing
= git_strmap_value_at(values
, pos
);
170 while (existing
->next
!= NULL
) {
171 existing
= existing
->next
;
173 existing
->next
= var
;
182 static void free_vars(git_strmap
*values
)
189 git_strmap_foreach_value(values
, var
,
190 while (var
!= NULL
) {
191 cvar_t
*next
= CVAR_LIST_NEXT(var
);
196 git_strmap_free(values
);
199 static void refcounted_strmap_free(refcounted_strmap
*map
)
204 if (git_atomic_dec(&map
->refcount
) != 0)
207 free_vars(map
->values
);
212 * Take the current values map from the backend and increase its
213 * refcount. This is its own function to make sure we use the mutex to
214 * avoid the map pointer from changing under us.
216 static refcounted_strmap
*refcounted_strmap_take(diskfile_header
*h
)
218 refcounted_strmap
*map
;
220 if (git_mutex_lock(&h
->values_mutex
) < 0) {
221 giterr_set(GITERR_OS
, "failed to lock config backend");
226 git_atomic_inc(&map
->refcount
);
228 git_mutex_unlock(&h
->values_mutex
);
233 static int refcounted_strmap_alloc(refcounted_strmap
**out
)
235 refcounted_strmap
*map
;
238 map
= git__calloc(1, sizeof(refcounted_strmap
));
239 GITERR_CHECK_ALLOC(map
);
241 git_atomic_set(&map
->refcount
, 1);
243 if ((error
= git_strmap_alloc(&map
->values
)) < 0)
251 static void config_file_clear(struct config_file
*file
)
253 struct config_file
*include
;
259 git_array_foreach(file
->includes
, i
, include
) {
260 config_file_clear(include
);
262 git_array_clear(file
->includes
);
264 git__free(file
->path
);
267 static int config_open(git_config_backend
*cfg
, git_config_level_t level
, const git_repository
*repo
)
270 diskfile_backend
*b
= (diskfile_backend
*)cfg
;
272 b
->header
.level
= level
;
273 b
->header
.repo
= repo
;
275 if ((res
= refcounted_strmap_alloc(&b
->header
.values
)) < 0)
278 if (!git_path_exists(b
->file
.path
))
281 if (res
< 0 || (res
= config_read(b
->header
.values
->values
, repo
, &b
->file
, level
, 0)) < 0) {
282 refcounted_strmap_free(b
->header
.values
);
283 b
->header
.values
= NULL
;
289 static int config_is_modified(int *modified
, struct config_file
*file
)
291 git_config_file
*include
;
292 git_buf buf
= GIT_BUF_INIT
;
299 if ((error
= git_futils_readbuffer(&buf
, file
->path
)) < 0)
302 if ((error
= git_hash_buf(&hash
, buf
.ptr
, buf
.size
)) < 0)
305 if (!git_oid_equal(&hash
, &file
->checksum
)) {
310 git_array_foreach(file
->includes
, i
, include
) {
311 if ((error
= config_is_modified(modified
, include
)) < 0 || *modified
)
321 static int config_refresh(git_config_backend
*cfg
)
323 diskfile_backend
*b
= (diskfile_backend
*)cfg
;
324 refcounted_strmap
*values
= NULL
, *tmp
;
325 git_config_file
*include
;
329 if (b
->header
.parent
.readonly
)
330 return config_error_readonly();
332 error
= config_is_modified(&modified
, &b
->file
);
333 if (error
< 0 && error
!= GIT_ENOTFOUND
)
339 if ((error
= refcounted_strmap_alloc(&values
)) < 0)
342 /* Reparse the current configuration */
343 git_array_foreach(b
->file
.includes
, i
, include
) {
344 config_file_clear(include
);
346 git_array_clear(b
->file
.includes
);
348 if ((error
= config_read(values
->values
, b
->header
.repo
, &b
->file
, b
->header
.level
, 0)) < 0)
351 if ((error
= git_mutex_lock(&b
->header
.values_mutex
)) < 0) {
352 giterr_set(GITERR_OS
, "failed to lock config backend");
356 tmp
= b
->header
.values
;
357 b
->header
.values
= values
;
360 git_mutex_unlock(&b
->header
.values_mutex
);
363 refcounted_strmap_free(values
);
365 return (error
== GIT_ENOTFOUND
) ? 0 : error
;
368 static void backend_free(git_config_backend
*_backend
)
370 diskfile_backend
*backend
= (diskfile_backend
*)_backend
;
375 config_file_clear(&backend
->file
);
376 refcounted_strmap_free(backend
->header
.values
);
377 git_mutex_free(&backend
->header
.values_mutex
);
381 static void config_iterator_free(
382 git_config_iterator
* iter
)
384 iter
->backend
->free(iter
->backend
);
388 static int config_iterator_next(
389 git_config_entry
**entry
,
390 git_config_iterator
*iter
)
392 git_config_file_iter
*it
= (git_config_file_iter
*) iter
;
393 diskfile_header
*h
= (diskfile_header
*) it
->parent
.backend
;
394 git_strmap
*values
= h
->values
->values
;
398 if (it
->next_var
== NULL
) {
399 err
= git_strmap_next((void**) &var
, &(it
->iter
), values
);
410 it
->next_var
= CVAR_LIST_NEXT(var
);
415 static int config_iterator_new(
416 git_config_iterator
**iter
,
417 struct git_config_backend
* backend
)
420 git_config_file_iter
*it
;
421 git_config_backend
*snapshot
;
422 diskfile_header
*bh
= (diskfile_header
*) backend
;
425 if ((error
= config_snapshot(&snapshot
, backend
)) < 0)
428 if ((error
= snapshot
->open(snapshot
, bh
->level
, bh
->repo
)) < 0)
431 it
= git__calloc(1, sizeof(git_config_file_iter
));
432 GITERR_CHECK_ALLOC(it
);
434 h
= (diskfile_header
*)snapshot
;
436 /* strmap_begin() is currently a macro returning 0 */
439 it
->parent
.backend
= snapshot
;
440 it
->iter
= git_strmap_begin(h
->values
);
443 it
->parent
.next
= config_iterator_next
;
444 it
->parent
.free
= config_iterator_free
;
445 *iter
= (git_config_iterator
*) it
;
450 static int config_set(git_config_backend
*cfg
, const char *name
, const char *value
)
452 diskfile_backend
*b
= (diskfile_backend
*)cfg
;
453 refcounted_strmap
*map
;
455 char *key
, *esc_value
= NULL
;
459 if ((rval
= git_config__normalize_name(name
, &key
)) < 0)
462 if ((map
= refcounted_strmap_take(&b
->header
)) == NULL
)
464 values
= map
->values
;
467 * Try to find it in the existing values and update it if it
468 * only has one value.
470 pos
= git_strmap_lookup_index(values
, key
);
471 if (git_strmap_valid_index(values
, pos
)) {
472 cvar_t
*existing
= git_strmap_value_at(values
, pos
);
474 if (existing
->next
!= NULL
) {
475 giterr_set(GITERR_CONFIG
, "multivar incompatible with simple set");
480 if (existing
->included
) {
481 giterr_set(GITERR_CONFIG
, "modifying included variable is not supported");
486 /* don't update if old and new values already match */
487 if ((!existing
->entry
->value
&& !value
) ||
488 (existing
->entry
->value
&& value
&&
489 !strcmp(existing
->entry
->value
, value
))) {
495 /* No early returns due to sanity checks, let's write it out and refresh */
498 esc_value
= escape_value(value
);
499 GITERR_CHECK_ALLOC(esc_value
);
502 if ((ret
= config_write(b
, name
, key
, NULL
, esc_value
)) < 0)
505 ret
= config_refresh(cfg
);
508 refcounted_strmap_free(map
);
509 git__free(esc_value
);
514 /* release the map containing the entry as an equivalent to freeing it */
515 static void release_map(git_config_entry
*entry
)
517 refcounted_strmap
*map
= (refcounted_strmap
*) entry
->payload
;
518 refcounted_strmap_free(map
);
522 * Internal function that actually gets the value in string form
524 static int config_get(git_config_backend
*cfg
, const char *key
, git_config_entry
**out
)
526 diskfile_header
*h
= (diskfile_header
*)cfg
;
527 refcounted_strmap
*map
;
533 if (!h
->parent
.readonly
&& ((error
= config_refresh(cfg
)) < 0))
536 if ((map
= refcounted_strmap_take(h
)) == NULL
)
538 values
= map
->values
;
540 pos
= git_strmap_lookup_index(values
, key
);
542 /* no error message; the config system will write one */
543 if (!git_strmap_valid_index(values
, pos
)) {
544 refcounted_strmap_free(map
);
545 return GIT_ENOTFOUND
;
548 var
= git_strmap_value_at(values
, pos
);
553 (*out
)->free
= release_map
;
554 (*out
)->payload
= map
;
559 static int config_set_multivar(
560 git_config_backend
*cfg
, const char *name
, const char *regexp
, const char *value
)
562 diskfile_backend
*b
= (diskfile_backend
*)cfg
;
569 if ((result
= git_config__normalize_name(name
, &key
)) < 0)
572 result
= p_regcomp(&preg
, regexp
, REG_EXTENDED
);
574 giterr_set_regex(&preg
, result
);
579 /* If we do have it, set call config_write() and reload */
580 if ((result
= config_write(b
, name
, key
, &preg
, value
)) < 0)
583 result
= config_refresh(cfg
);
592 static int config_delete(git_config_backend
*cfg
, const char *name
)
595 diskfile_backend
*b
= (diskfile_backend
*)cfg
;
596 refcounted_strmap
*map
; git_strmap
*values
;
601 if ((result
= git_config__normalize_name(name
, &key
)) < 0)
604 if ((map
= refcounted_strmap_take(&b
->header
)) == NULL
)
606 values
= b
->header
.values
->values
;
608 pos
= git_strmap_lookup_index(values
, key
);
611 if (!git_strmap_valid_index(values
, pos
)) {
612 refcounted_strmap_free(map
);
613 giterr_set(GITERR_CONFIG
, "could not find key '%s' to delete", name
);
614 return GIT_ENOTFOUND
;
617 var
= git_strmap_value_at(values
, pos
);
618 refcounted_strmap_free(map
);
621 giterr_set(GITERR_CONFIG
, "cannot delete included variable");
625 if (var
->next
!= NULL
) {
626 giterr_set(GITERR_CONFIG
, "cannot delete multivar with a single delete");
630 if ((result
= config_write(b
, name
, var
->entry
->name
, NULL
, NULL
)) < 0)
633 return config_refresh(cfg
);
636 static int config_delete_multivar(git_config_backend
*cfg
, const char *name
, const char *regexp
)
638 diskfile_backend
*b
= (diskfile_backend
*)cfg
;
639 refcounted_strmap
*map
;
646 if ((result
= git_config__normalize_name(name
, &key
)) < 0)
649 if ((map
= refcounted_strmap_take(&b
->header
)) == NULL
)
651 values
= b
->header
.values
->values
;
653 pos
= git_strmap_lookup_index(values
, key
);
655 if (!git_strmap_valid_index(values
, pos
)) {
656 refcounted_strmap_free(map
);
658 giterr_set(GITERR_CONFIG
, "could not find key '%s' to delete", name
);
659 return GIT_ENOTFOUND
;
662 refcounted_strmap_free(map
);
664 result
= p_regcomp(&preg
, regexp
, REG_EXTENDED
);
666 giterr_set_regex(&preg
, result
);
671 if ((result
= config_write(b
, name
, key
, &preg
, NULL
)) < 0)
674 result
= config_refresh(cfg
);
682 static int config_snapshot(git_config_backend
**out
, git_config_backend
*in
)
684 diskfile_backend
*b
= (diskfile_backend
*) in
;
686 return git_config_file__snapshot(out
, b
);
689 static int config_lock(git_config_backend
*_cfg
)
691 diskfile_backend
*cfg
= (diskfile_backend
*) _cfg
;
694 if ((error
= git_filebuf_open(&cfg
->locked_buf
, cfg
->file
.path
, 0, GIT_CONFIG_FILE_MODE
)) < 0)
697 error
= git_futils_readbuffer(&cfg
->locked_content
, cfg
->file
.path
);
698 if (error
< 0 && error
!= GIT_ENOTFOUND
) {
699 git_filebuf_cleanup(&cfg
->locked_buf
);
708 static int config_unlock(git_config_backend
*_cfg
, int success
)
710 diskfile_backend
*cfg
= (diskfile_backend
*) _cfg
;
714 git_filebuf_write(&cfg
->locked_buf
, cfg
->locked_content
.ptr
, cfg
->locked_content
.size
);
715 error
= git_filebuf_commit(&cfg
->locked_buf
);
718 git_filebuf_cleanup(&cfg
->locked_buf
);
719 git_buf_free(&cfg
->locked_content
);
725 int git_config_file__ondisk(git_config_backend
**out
, const char *path
)
727 diskfile_backend
*backend
;
729 backend
= git__calloc(1, sizeof(diskfile_backend
));
730 GITERR_CHECK_ALLOC(backend
);
732 backend
->header
.parent
.version
= GIT_CONFIG_BACKEND_VERSION
;
733 git_mutex_init(&backend
->header
.values_mutex
);
735 backend
->file
.path
= git__strdup(path
);
736 GITERR_CHECK_ALLOC(backend
->file
.path
);
737 git_array_init(backend
->file
.includes
);
739 backend
->header
.parent
.open
= config_open
;
740 backend
->header
.parent
.get
= config_get
;
741 backend
->header
.parent
.set
= config_set
;
742 backend
->header
.parent
.set_multivar
= config_set_multivar
;
743 backend
->header
.parent
.del
= config_delete
;
744 backend
->header
.parent
.del_multivar
= config_delete_multivar
;
745 backend
->header
.parent
.iterator
= config_iterator_new
;
746 backend
->header
.parent
.snapshot
= config_snapshot
;
747 backend
->header
.parent
.lock
= config_lock
;
748 backend
->header
.parent
.unlock
= config_unlock
;
749 backend
->header
.parent
.free
= backend_free
;
751 *out
= (git_config_backend
*)backend
;
756 static int config_set_readonly(git_config_backend
*cfg
, const char *name
, const char *value
)
762 return config_error_readonly();
765 static int config_set_multivar_readonly(
766 git_config_backend
*cfg
, const char *name
, const char *regexp
, const char *value
)
773 return config_error_readonly();
776 static int config_delete_multivar_readonly(git_config_backend
*cfg
, const char *name
, const char *regexp
)
782 return config_error_readonly();
785 static int config_delete_readonly(git_config_backend
*cfg
, const char *name
)
790 return config_error_readonly();
793 static int config_lock_readonly(git_config_backend
*_cfg
)
797 return config_error_readonly();
800 static int config_unlock_readonly(git_config_backend
*_cfg
, int success
)
805 return config_error_readonly();
808 static void backend_readonly_free(git_config_backend
*_backend
)
810 diskfile_backend
*backend
= (diskfile_backend
*)_backend
;
815 refcounted_strmap_free(backend
->header
.values
);
816 git_mutex_free(&backend
->header
.values_mutex
);
820 static int config_readonly_open(git_config_backend
*cfg
, git_config_level_t level
, const git_repository
*repo
)
822 diskfile_readonly_backend
*b
= (diskfile_readonly_backend
*) cfg
;
823 diskfile_backend
*src
= b
->snapshot_from
;
824 diskfile_header
*src_header
= &src
->header
;
825 refcounted_strmap
*src_map
;
828 if (!src_header
->parent
.readonly
&& (error
= config_refresh(&src_header
->parent
)) < 0)
831 /* We're just copying data, don't care about the level or repo*/
835 if ((src_map
= refcounted_strmap_take(src_header
)) == NULL
)
837 b
->header
.values
= src_map
;
842 int git_config_file__snapshot(git_config_backend
**out
, diskfile_backend
*in
)
844 diskfile_readonly_backend
*backend
;
846 backend
= git__calloc(1, sizeof(diskfile_readonly_backend
));
847 GITERR_CHECK_ALLOC(backend
);
849 backend
->header
.parent
.version
= GIT_CONFIG_BACKEND_VERSION
;
850 git_mutex_init(&backend
->header
.values_mutex
);
852 backend
->snapshot_from
= in
;
854 backend
->header
.parent
.readonly
= 1;
855 backend
->header
.parent
.version
= GIT_CONFIG_BACKEND_VERSION
;
856 backend
->header
.parent
.open
= config_readonly_open
;
857 backend
->header
.parent
.get
= config_get
;
858 backend
->header
.parent
.set
= config_set_readonly
;
859 backend
->header
.parent
.set_multivar
= config_set_multivar_readonly
;
860 backend
->header
.parent
.del
= config_delete_readonly
;
861 backend
->header
.parent
.del_multivar
= config_delete_multivar_readonly
;
862 backend
->header
.parent
.iterator
= config_iterator_new
;
863 backend
->header
.parent
.lock
= config_lock_readonly
;
864 backend
->header
.parent
.unlock
= config_unlock_readonly
;
865 backend
->header
.parent
.free
= backend_readonly_free
;
867 *out
= (git_config_backend
*)backend
;
872 static int included_path(git_buf
*out
, const char *dir
, const char *path
)
874 /* From the user's home */
875 if (path
[0] == '~' && path
[1] == '/')
876 return git_sysdir_expand_global_file(out
, &path
[1]);
878 return git_path_join_unrooted(out
, path
, dir
, NULL
);
881 /* Escape the values to write them to the file */
882 static char *escape_value(const char *ptr
)
892 return git__calloc(1, sizeof(char));
894 if (git_buf_init(&buf
, len
) < 0)
897 while (*ptr
!= '\0') {
898 if ((esc
= strchr(git_config_escaped
, *ptr
)) != NULL
) {
899 git_buf_putc(&buf
, '\\');
900 git_buf_putc(&buf
, git_config_escapes
[esc
- git_config_escaped
]);
902 git_buf_putc(&buf
, *ptr
);
907 if (git_buf_oom(&buf
)) {
912 return git_buf_detach(&buf
);
916 const git_repository
*repo
;
917 const char *file_path
;
919 git_config_level_t level
;
923 static int parse_include(git_config_parser
*reader
,
924 struct parse_data
*parse_data
, const char *file
)
926 struct config_file
*include
;
927 git_buf path
= GIT_BUF_INIT
;
934 if ((result
= git_path_dirname_r(&path
, reader
->file
->path
)) < 0)
937 dir
= git_buf_detach(&path
);
938 result
= included_path(&path
, dir
, file
);
944 include
= git_array_alloc(reader
->file
->includes
);
945 memset(include
, 0, sizeof(*include
));
946 git_array_init(include
->includes
);
947 include
->path
= git_buf_detach(&path
);
949 result
= config_read(parse_data
->values
, parse_data
->repo
,
950 include
, parse_data
->level
, parse_data
->depth
+1);
952 if (result
== GIT_ENOTFOUND
) {
960 static int do_match_gitdir(
962 const git_repository
*repo
,
963 const char *cfg_file
,
965 bool case_insensitive
)
967 git_buf path
= GIT_BUF_INIT
;
968 int error
, fnmatch_flags
;
970 if (value
[0] == '.' && git_path_is_dirsep(value
[1])) {
971 git_path_dirname_r(&path
, cfg_file
);
972 git_buf_joinpath(&path
, path
.ptr
, value
+ 2);
973 } else if (value
[0] == '~' && git_path_is_dirsep(value
[1]))
974 git_sysdir_expand_global_file(&path
, value
+ 1);
975 else if (!git_path_is_absolute(value
))
976 git_buf_joinpath(&path
, "**", value
);
978 git_buf_sets(&path
, value
);
980 if (git_buf_oom(&path
)) {
985 if (git_path_is_dirsep(value
[strlen(value
) - 1]))
986 git_buf_puts(&path
, "**");
988 fnmatch_flags
= FNM_PATHNAME
|FNM_LEADING_DIR
;
989 if (case_insensitive
)
990 fnmatch_flags
|= FNM_IGNORECASE
;
992 if ((error
= p_fnmatch(path
.ptr
, git_repository_path(repo
), fnmatch_flags
)) < 0)
995 *matches
= (error
== 0);
1002 static int conditional_match_gitdir(
1004 const git_repository
*repo
,
1005 const char *cfg_file
,
1008 return do_match_gitdir(matches
, repo
, cfg_file
, value
, false);
1011 static int conditional_match_gitdir_i(
1013 const git_repository
*repo
,
1014 const char *cfg_file
,
1017 return do_match_gitdir(matches
, repo
, cfg_file
, value
, true);
1020 static const struct {
1022 int (*matches
)(int *matches
, const git_repository
*repo
, const char *cfg
, const char *value
);
1024 { "gitdir:", conditional_match_gitdir
},
1025 { "gitdir/i:", conditional_match_gitdir_i
}
1028 static int parse_conditional_include(git_config_parser
*reader
,
1029 struct parse_data
*parse_data
, const char *section
, const char *file
)
1033 int error
= 0, matches
;
1035 if (!parse_data
->repo
|| !file
)
1038 condition
= git__substrdup(section
+ strlen("includeIf."),
1039 strlen(section
) - strlen("includeIf.") - strlen(".path"));
1041 for (i
= 0; i
< ARRAY_SIZE(conditions
); i
++) {
1042 if (git__prefixcmp(condition
, conditions
[i
].prefix
))
1045 if ((error
= conditions
[i
].matches(&matches
,
1047 parse_data
->file_path
,
1048 condition
+ strlen(conditions
[i
].prefix
))) < 0)
1052 error
= parse_include(reader
, parse_data
, file
);
1057 git__free(condition
);
1061 static int read_on_variable(
1062 git_config_parser
*reader
,
1063 const char *current_section
,
1070 struct parse_data
*parse_data
= (struct parse_data
*)data
;
1071 git_buf buf
= GIT_BUF_INIT
;
1076 GIT_UNUSED(line_len
);
1078 if (current_section
) {
1079 /* TODO: Once warnings lang, we should likely warn
1080 * here. Git appears to warn in most cases if it sees
1081 * un-namespaced config options.
1083 git_buf_puts(&buf
, current_section
);
1084 git_buf_putc(&buf
, '.');
1086 git__strtolower(var_name
);
1087 git_buf_puts(&buf
, var_name
);
1088 git__free(var_name
);
1090 if (git_buf_oom(&buf
)) {
1091 git__free(var_value
);
1095 var
= git__calloc(1, sizeof(cvar_t
));
1096 GITERR_CHECK_ALLOC(var
);
1097 var
->entry
= git__calloc(1, sizeof(git_config_entry
));
1098 GITERR_CHECK_ALLOC(var
->entry
);
1100 var
->entry
->name
= git_buf_detach(&buf
);
1101 var
->entry
->value
= var_value
;
1102 var
->entry
->level
= parse_data
->level
;
1103 var
->included
= !!parse_data
->depth
;
1105 if ((result
= append_entry(parse_data
->values
, var
)) < 0)
1110 /* Add or append the new config option */
1111 if (!git__strcmp(var
->entry
->name
, "include.path"))
1112 result
= parse_include(reader
, parse_data
, var
->entry
->value
);
1113 else if (!git__prefixcmp(var
->entry
->name
, "includeif.") &&
1114 !git__suffixcmp(var
->entry
->name
, ".path"))
1115 result
= parse_conditional_include(reader
, parse_data
,
1116 var
->entry
->name
, var
->entry
->value
);
1122 static int config_read(
1124 const git_repository
*repo
,
1125 git_config_file
*file
,
1126 git_config_level_t level
,
1129 struct parse_data parse_data
;
1130 git_config_parser reader
;
1131 git_buf contents
= GIT_BUF_INIT
;
1134 if (depth
>= MAX_INCLUDE_DEPTH
) {
1135 giterr_set(GITERR_CONFIG
, "maximum config include depth reached");
1139 if ((error
= git_futils_readbuffer(&contents
, file
->path
)) < 0)
1142 git_parse_ctx_init(&reader
.ctx
, contents
.ptr
, contents
.size
);
1144 if ((error
= git_hash_buf(&file
->checksum
, contents
.ptr
, contents
.size
)) < 0)
1147 /* Initialize the reading position */
1149 git_parse_ctx_init(&reader
.ctx
, contents
.ptr
, contents
.size
);
1151 /* If the file is empty, there's nothing for us to do */
1152 if (!reader
.ctx
.content
|| *reader
.ctx
.content
== '\0')
1155 parse_data
.repo
= repo
;
1156 parse_data
.file_path
= file
->path
;
1157 parse_data
.values
= values
;
1158 parse_data
.level
= level
;
1159 parse_data
.depth
= depth
;
1161 error
= git_config_parse(&reader
, NULL
, read_on_variable
, NULL
, NULL
, &parse_data
);
1164 git_buf_free(&contents
);
1168 static int write_section(git_buf
*fbuf
, const char *key
)
1172 git_buf buf
= GIT_BUF_INIT
;
1174 /* All of this just for [section "subsection"] */
1175 dot
= strchr(key
, '.');
1176 git_buf_putc(&buf
, '[');
1178 git_buf_puts(&buf
, key
);
1181 git_buf_put(&buf
, key
, dot
- key
);
1182 escaped
= escape_value(dot
+ 1);
1183 GITERR_CHECK_ALLOC(escaped
);
1184 git_buf_printf(&buf
, " \"%s\"", escaped
);
1187 git_buf_puts(&buf
, "]\n");
1189 if (git_buf_oom(&buf
))
1192 result
= git_buf_put(fbuf
, git_buf_cstr(&buf
), buf
.size
);
1198 static const char *quotes_for_value(const char *value
)
1202 if (value
[0] == ' ' || value
[0] == '\0')
1205 for (ptr
= value
; *ptr
; ++ptr
) {
1206 if (*ptr
== ';' || *ptr
== '#')
1218 git_buf buffered_comment
;
1219 unsigned int in_section
: 1,
1221 const char *orig_section
;
1222 const char *section
;
1223 const char *orig_name
;
1225 const regex_t
*preg
;
1229 static int write_line_to(git_buf
*buf
, const char *line
, size_t line_len
)
1231 int result
= git_buf_put(buf
, line
, line_len
);
1233 if (!result
&& line_len
&& line
[line_len
-1] != '\n')
1234 result
= git_buf_printf(buf
, "\n");
1239 static int write_line(struct write_data
*write_data
, const char *line
, size_t line_len
)
1241 return write_line_to(write_data
->buf
, line
, line_len
);
1244 static int write_value(struct write_data
*write_data
)
1249 q
= quotes_for_value(write_data
->value
);
1250 result
= git_buf_printf(write_data
->buf
,
1251 "\t%s = %s%s%s\n", write_data
->orig_name
, q
, write_data
->value
, q
);
1253 /* If we are updating a single name/value, we're done. Setting `value`
1254 * to `NULL` will prevent us from trying to write it again later (in
1255 * `write_on_section`) if we see the same section repeated.
1257 if (!write_data
->preg
)
1258 write_data
->value
= NULL
;
1263 static int write_on_section(
1264 git_config_parser
*reader
,
1265 const char *current_section
,
1270 struct write_data
*write_data
= (struct write_data
*)data
;
1275 /* If we were previously in the correct section (but aren't anymore)
1276 * and haven't written our value (for a simple name/value set, not
1277 * a multivar), then append it to the end of the section before writing
1280 if (write_data
->in_section
&& !write_data
->preg
&& write_data
->value
)
1281 result
= write_value(write_data
);
1283 write_data
->in_section
= strcmp(current_section
, write_data
->section
) == 0;
1286 * If there were comments just before this section, dump them as well.
1289 result
= git_buf_put(write_data
->buf
, write_data
->buffered_comment
.ptr
, write_data
->buffered_comment
.size
);
1290 git_buf_clear(&write_data
->buffered_comment
);
1294 result
= write_line(write_data
, line
, line_len
);
1299 static int write_on_variable(
1300 git_config_parser
*reader
,
1301 const char *current_section
,
1308 struct write_data
*write_data
= (struct write_data
*)data
;
1309 bool has_matched
= false;
1313 GIT_UNUSED(current_section
);
1316 * If there were comments just before this variable, let's dump them as well.
1318 if ((error
= git_buf_put(write_data
->buf
, write_data
->buffered_comment
.ptr
, write_data
->buffered_comment
.size
)) < 0)
1321 git_buf_clear(&write_data
->buffered_comment
);
1323 /* See if we are to update this name/value pair; first examine name */
1324 if (write_data
->in_section
&&
1325 strcasecmp(write_data
->name
, var_name
) == 0)
1328 /* If we have a regex to match the value, see if it matches */
1329 if (has_matched
&& write_data
->preg
!= NULL
)
1330 has_matched
= (regexec(write_data
->preg
, var_value
, 0, NULL
, 0) == 0);
1332 git__free(var_name
);
1333 git__free(var_value
);
1335 /* If this isn't the name/value we're looking for, simply dump the
1336 * existing data back out and continue on.
1339 return write_line(write_data
, line
, line_len
);
1341 write_data
->preg_replaced
= 1;
1343 /* If value is NULL, we are deleting this value; write nothing. */
1344 if (!write_data
->value
)
1347 return write_value(write_data
);
1350 static int write_on_comment(git_config_parser
*reader
, const char *line
, size_t line_len
, void *data
)
1352 struct write_data
*write_data
;
1356 write_data
= (struct write_data
*)data
;
1357 return write_line_to(&write_data
->buffered_comment
, line
, line_len
);
1360 static int write_on_eof(
1361 git_config_parser
*reader
, const char *current_section
, void *data
)
1363 struct write_data
*write_data
= (struct write_data
*)data
;
1369 * If we've buffered comments when reaching EOF, make sure to dump them.
1371 if ((result
= git_buf_put(write_data
->buf
, write_data
->buffered_comment
.ptr
, write_data
->buffered_comment
.size
)) < 0)
1374 /* If we are at the EOF and have not written our value (again, for a
1375 * simple name/value set, not a multivar) then we have never seen the
1376 * section in question and should create a new section and write the
1379 if ((!write_data
->preg
|| !write_data
->preg_replaced
) && write_data
->value
) {
1380 /* write the section header unless we're already in it */
1381 if (!current_section
|| strcmp(current_section
, write_data
->section
))
1382 result
= write_section(write_data
->buf
, write_data
->orig_section
);
1385 result
= write_value(write_data
);
1392 * This is pretty much the parsing, except we write out anything we don't have
1394 static int config_write(diskfile_backend
*cfg
, const char *orig_key
, const char *key
, const regex_t
*preg
, const char* value
)
1397 char *orig_section
, *section
, *orig_name
, *name
, *ldot
;
1398 git_filebuf file
= GIT_FILEBUF_INIT
;
1399 git_buf buf
= GIT_BUF_INIT
, contents
= GIT_BUF_INIT
;
1400 git_config_parser reader
;
1401 struct write_data write_data
;
1403 memset(&reader
, 0, sizeof(reader
));
1404 reader
.file
= &cfg
->file
;
1407 result
= git_buf_puts(&contents
, git_buf_cstr(&cfg
->locked_content
));
1410 if ((result
= git_filebuf_open(
1411 &file
, cfg
->file
.path
, GIT_FILEBUF_HASH_CONTENTS
, GIT_CONFIG_FILE_MODE
)) < 0) {
1412 git_buf_free(&contents
);
1416 /* We need to read in our own config file */
1417 result
= git_futils_readbuffer(&contents
, cfg
->file
.path
);
1420 /* Initialise the reading position */
1421 if (result
== 0 || result
== GIT_ENOTFOUND
) {
1422 git_parse_ctx_init(&reader
.ctx
, contents
.ptr
, contents
.size
);
1424 git_filebuf_cleanup(&file
);
1425 return -1; /* OS error when reading the file */
1428 ldot
= strrchr(key
, '.');
1430 section
= git__strndup(key
, ldot
- key
);
1431 GITERR_CHECK_ALLOC(section
);
1433 ldot
= strrchr(orig_key
, '.');
1434 orig_name
= ldot
+ 1;
1435 orig_section
= git__strndup(orig_key
, ldot
- orig_key
);
1436 GITERR_CHECK_ALLOC(orig_section
);
1438 write_data
.buf
= &buf
;
1439 git_buf_init(&write_data
.buffered_comment
, 0);
1440 write_data
.orig_section
= orig_section
;
1441 write_data
.section
= section
;
1442 write_data
.in_section
= 0;
1443 write_data
.preg_replaced
= 0;
1444 write_data
.orig_name
= orig_name
;
1445 write_data
.name
= name
;
1446 write_data
.preg
= preg
;
1447 write_data
.value
= value
;
1449 result
= git_config_parse(&reader
,
1456 git__free(orig_section
);
1457 git_buf_free(&write_data
.buffered_comment
);
1460 git_filebuf_cleanup(&file
);
1465 size_t len
= buf
.asize
;
1466 /* Update our copy with the modified contents */
1467 git_buf_free(&cfg
->locked_content
);
1468 git_buf_attach(&cfg
->locked_content
, git_buf_detach(&buf
), len
);
1470 git_filebuf_write(&file
, git_buf_cstr(&buf
), git_buf_len(&buf
));
1471 result
= git_filebuf_commit(&file
);
1476 git_buf_free(&contents
);
1477 git_parse_ctx_clear(&reader
.ctx
);