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.
10 #include "git2/config.h"
11 #include "git2/sys/config.h"
15 #include "config_backend.h"
16 #include "config_entries.h"
17 #include "config_parse.h"
21 #include "wildmatch.h"
24 /* Max depth for [include] directives */
25 #define MAX_INCLUDE_DEPTH 10
27 typedef struct config_file
{
28 git_futils_filestamp stamp
;
29 unsigned char checksum
[GIT_HASH_SHA1_SIZE
];
31 git_array_t(struct config_file
) includes
;
35 git_config_backend parent
;
36 git_mutex values_mutex
;
37 git_config_entries
*entries
;
38 const git_repository
*repo
;
39 git_config_level_t level
;
41 git_array_t(git_config_parser
) readers
;
44 git_filebuf locked_buf
;
45 git_str locked_content
;
48 } config_file_backend
;
51 const git_repository
*repo
;
53 git_config_entries
*entries
;
54 git_config_level_t level
;
56 } config_file_parse_data
;
58 static int config_file_read(git_config_entries
*entries
, const git_repository
*repo
, config_file
*file
, git_config_level_t level
, int depth
);
59 static int config_file_read_buffer(git_config_entries
*entries
, const git_repository
*repo
, config_file
*file
, git_config_level_t level
, int depth
, const char *buf
, size_t buflen
);
60 static int config_file_write(config_file_backend
*cfg
, const char *orig_key
, const char *key
, const git_regexp
*preg
, const char *value
);
61 static char *escape_value(const char *ptr
);
64 * Take the current values map from the backend and increase its
65 * refcount. This is its own function to make sure we use the mutex to
66 * avoid the map pointer from changing under us.
68 static int config_file_entries_take(git_config_entries
**out
, config_file_backend
*b
)
72 if ((error
= git_mutex_lock(&b
->values_mutex
)) < 0) {
73 git_error_set(GIT_ERROR_OS
, "failed to lock config backend");
77 git_config_entries_incref(b
->entries
);
80 git_mutex_unlock(&b
->values_mutex
);
85 static void config_file_clear(config_file
*file
)
93 git_array_foreach(file
->includes
, i
, include
) {
94 config_file_clear(include
);
96 git_array_clear(file
->includes
);
98 git__free(file
->path
);
101 static int config_file_open(git_config_backend
*cfg
, git_config_level_t level
, const git_repository
*repo
)
103 config_file_backend
*b
= GIT_CONTAINER_OF(cfg
, config_file_backend
, parent
);
109 if ((res
= git_config_entries_new(&b
->entries
)) < 0)
112 if (!git_fs_path_exists(b
->file
.path
))
116 * git silently ignores configuration files that are not
117 * readable. We emulate that behavior. This is particularly
118 * important for sandboxed applications on macOS where the
119 * git configuration files may not be readable.
121 if (p_access(b
->file
.path
, R_OK
) < 0)
122 return GIT_ENOTFOUND
;
124 if (res
< 0 || (res
= config_file_read(b
->entries
, repo
, &b
->file
, level
, 0)) < 0) {
125 git_config_entries_free(b
->entries
);
132 static int config_file_is_modified(int *modified
, config_file
*file
)
134 config_file
*include
;
135 git_str buf
= GIT_STR_INIT
;
136 unsigned char checksum
[GIT_HASH_SHA1_SIZE
];
142 if (!git_futils_filestamp_check(&file
->stamp
, file
->path
))
145 if ((error
= git_futils_readbuffer(&buf
, file
->path
)) < 0)
148 if ((error
= git_hash_buf(checksum
, buf
.ptr
, buf
.size
, GIT_HASH_ALGORITHM_SHA1
)) < 0)
151 if (memcmp(checksum
, file
->checksum
, GIT_HASH_SHA1_SIZE
) != 0) {
157 git_array_foreach(file
->includes
, i
, include
) {
158 if ((error
= config_file_is_modified(modified
, include
)) < 0 || *modified
)
163 git_str_dispose(&buf
);
168 static void config_file_clear_includes(config_file_backend
*cfg
)
170 config_file
*include
;
173 git_array_foreach(cfg
->file
.includes
, i
, include
)
174 config_file_clear(include
);
175 git_array_clear(cfg
->file
.includes
);
178 static int config_file_set_entries(git_config_backend
*cfg
, git_config_entries
*entries
)
180 config_file_backend
*b
= GIT_CONTAINER_OF(cfg
, config_file_backend
, parent
);
181 git_config_entries
*old
= NULL
;
184 if (b
->parent
.readonly
) {
185 git_error_set(GIT_ERROR_CONFIG
, "this backend is read-only");
189 if ((error
= git_mutex_lock(&b
->values_mutex
)) < 0) {
190 git_error_set(GIT_ERROR_OS
, "failed to lock config backend");
195 b
->entries
= entries
;
197 git_mutex_unlock(&b
->values_mutex
);
200 git_config_entries_free(old
);
204 static int config_file_refresh_from_buffer(git_config_backend
*cfg
, const char *buf
, size_t buflen
)
206 config_file_backend
*b
= GIT_CONTAINER_OF(cfg
, config_file_backend
, parent
);
207 git_config_entries
*entries
= NULL
;
210 config_file_clear_includes(b
);
212 if ((error
= git_config_entries_new(&entries
)) < 0 ||
213 (error
= config_file_read_buffer(entries
, b
->repo
, &b
->file
,
214 b
->level
, 0, buf
, buflen
)) < 0 ||
215 (error
= config_file_set_entries(cfg
, entries
)) < 0)
220 git_config_entries_free(entries
);
224 static int config_file_refresh(git_config_backend
*cfg
)
226 config_file_backend
*b
= GIT_CONTAINER_OF(cfg
, config_file_backend
, parent
);
227 git_config_entries
*entries
= NULL
;
233 if ((error
= config_file_is_modified(&modified
, &b
->file
)) < 0 && error
!= GIT_ENOTFOUND
)
239 config_file_clear_includes(b
);
241 if ((error
= git_config_entries_new(&entries
)) < 0 ||
242 (error
= config_file_read(entries
, b
->repo
, &b
->file
, b
->level
, 0)) < 0 ||
243 (error
= config_file_set_entries(cfg
, entries
)) < 0)
248 git_config_entries_free(entries
);
250 return (error
== GIT_ENOTFOUND
) ? 0 : error
;
253 static void config_file_free(git_config_backend
*_backend
)
255 config_file_backend
*backend
= GIT_CONTAINER_OF(_backend
, config_file_backend
, parent
);
260 config_file_clear(&backend
->file
);
261 git_config_entries_free(backend
->entries
);
262 git_mutex_free(&backend
->values_mutex
);
266 static int config_file_iterator(
267 git_config_iterator
**iter
,
268 struct git_config_backend
*backend
)
270 config_file_backend
*b
= GIT_CONTAINER_OF(backend
, config_file_backend
, parent
);
271 git_config_entries
*dupped
= NULL
, *entries
= NULL
;
274 if ((error
= config_file_refresh(backend
)) < 0 ||
275 (error
= config_file_entries_take(&entries
, b
)) < 0 ||
276 (error
= git_config_entries_dup(&dupped
, entries
)) < 0 ||
277 (error
= git_config_entries_iterator_new(iter
, dupped
)) < 0)
281 /* Let iterator delete duplicated entries when it's done */
282 git_config_entries_free(entries
);
283 git_config_entries_free(dupped
);
287 static int config_file_snapshot(git_config_backend
**out
, git_config_backend
*backend
)
289 return git_config_backend_snapshot(out
, backend
);
292 static int config_file_set(git_config_backend
*cfg
, const char *name
, const char *value
)
294 config_file_backend
*b
= GIT_CONTAINER_OF(cfg
, config_file_backend
, parent
);
295 git_config_entries
*entries
;
296 git_config_entry
*existing
;
297 char *key
, *esc_value
= NULL
;
300 if ((error
= git_config__normalize_name(name
, &key
)) < 0)
303 if ((error
= config_file_entries_take(&entries
, b
)) < 0)
306 /* Check whether we'd be modifying an included or multivar key */
307 if ((error
= git_config_entries_get_unique(&existing
, entries
, key
)) < 0) {
308 if (error
!= GIT_ENOTFOUND
)
311 } else if ((!existing
->value
&& !value
) ||
312 (existing
->value
&& value
&& !strcmp(existing
->value
, value
))) {
313 /* don't update if old and new values already match */
318 /* No early returns due to sanity checks, let's write it out and refresh */
320 esc_value
= escape_value(value
);
321 GIT_ERROR_CHECK_ALLOC(esc_value
);
324 if ((error
= config_file_write(b
, name
, key
, NULL
, esc_value
)) < 0)
328 git_config_entries_free(entries
);
329 git__free(esc_value
);
334 /* release the map containing the entry as an equivalent to freeing it */
335 static void config_file_entry_free(git_config_entry
*entry
)
337 git_config_entries
*entries
= (git_config_entries
*) entry
->payload
;
338 git_config_entries_free(entries
);
342 * Internal function that actually gets the value in string form
344 static int config_file_get(git_config_backend
*cfg
, const char *key
, git_config_entry
**out
)
346 config_file_backend
*h
= GIT_CONTAINER_OF(cfg
, config_file_backend
, parent
);
347 git_config_entries
*entries
= NULL
;
348 git_config_entry
*entry
;
351 if (!h
->parent
.readonly
&& ((error
= config_file_refresh(cfg
)) < 0))
354 if ((error
= config_file_entries_take(&entries
, h
)) < 0)
357 if ((error
= (git_config_entries_get(&entry
, entries
, key
))) < 0) {
358 git_config_entries_free(entries
);
362 entry
->free
= config_file_entry_free
;
363 entry
->payload
= entries
;
369 static int config_file_set_multivar(
370 git_config_backend
*cfg
, const char *name
, const char *regexp
, const char *value
)
372 config_file_backend
*b
= GIT_CONTAINER_OF(cfg
, config_file_backend
, parent
);
377 GIT_ASSERT_ARG(regexp
);
379 if ((result
= git_config__normalize_name(name
, &key
)) < 0)
382 if ((result
= git_regexp_compile(&preg
, regexp
, 0)) < 0)
385 /* If we do have it, set call config_file_write() and reload */
386 if ((result
= config_file_write(b
, name
, key
, &preg
, value
)) < 0)
391 git_regexp_dispose(&preg
);
396 static int config_file_delete(git_config_backend
*cfg
, const char *name
)
398 config_file_backend
*b
= GIT_CONTAINER_OF(cfg
, config_file_backend
, parent
);
399 git_config_entries
*entries
= NULL
;
400 git_config_entry
*entry
;
404 if ((error
= git_config__normalize_name(name
, &key
)) < 0)
407 if ((error
= config_file_entries_take(&entries
, b
)) < 0)
410 /* Check whether we'd be modifying an included or multivar key */
411 if ((error
= git_config_entries_get_unique(&entry
, entries
, key
)) < 0) {
412 if (error
== GIT_ENOTFOUND
)
413 git_error_set(GIT_ERROR_CONFIG
, "could not find key '%s' to delete", name
);
417 if ((error
= config_file_write(b
, name
, entry
->name
, NULL
, NULL
)) < 0)
421 git_config_entries_free(entries
);
426 static int config_file_delete_multivar(git_config_backend
*cfg
, const char *name
, const char *regexp
)
428 config_file_backend
*b
= GIT_CONTAINER_OF(cfg
, config_file_backend
, parent
);
429 git_config_entries
*entries
= NULL
;
430 git_config_entry
*entry
= NULL
;
431 git_regexp preg
= GIT_REGEX_INIT
;
435 if ((result
= git_config__normalize_name(name
, &key
)) < 0)
438 if ((result
= config_file_entries_take(&entries
, b
)) < 0)
441 if ((result
= git_config_entries_get(&entry
, entries
, key
)) < 0) {
442 if (result
== GIT_ENOTFOUND
)
443 git_error_set(GIT_ERROR_CONFIG
, "could not find key '%s' to delete", name
);
447 if ((result
= git_regexp_compile(&preg
, regexp
, 0)) < 0)
450 if ((result
= config_file_write(b
, name
, key
, &preg
, NULL
)) < 0)
454 git_config_entries_free(entries
);
456 git_regexp_dispose(&preg
);
460 static int config_file_lock(git_config_backend
*_cfg
)
462 config_file_backend
*cfg
= GIT_CONTAINER_OF(_cfg
, config_file_backend
, parent
);
465 if ((error
= git_filebuf_open(&cfg
->locked_buf
, cfg
->file
.path
, 0, GIT_CONFIG_FILE_MODE
)) < 0)
468 error
= git_futils_readbuffer(&cfg
->locked_content
, cfg
->file
.path
);
469 if (error
< 0 && error
!= GIT_ENOTFOUND
) {
470 git_filebuf_cleanup(&cfg
->locked_buf
);
479 static int config_file_unlock(git_config_backend
*_cfg
, int success
)
481 config_file_backend
*cfg
= GIT_CONTAINER_OF(_cfg
, config_file_backend
, parent
);
485 git_filebuf_write(&cfg
->locked_buf
, cfg
->locked_content
.ptr
, cfg
->locked_content
.size
);
486 error
= git_filebuf_commit(&cfg
->locked_buf
);
489 git_filebuf_cleanup(&cfg
->locked_buf
);
490 git_str_dispose(&cfg
->locked_content
);
496 int git_config_backend_from_file(git_config_backend
**out
, const char *path
)
498 config_file_backend
*backend
;
500 backend
= git__calloc(1, sizeof(config_file_backend
));
501 GIT_ERROR_CHECK_ALLOC(backend
);
503 backend
->parent
.version
= GIT_CONFIG_BACKEND_VERSION
;
504 git_mutex_init(&backend
->values_mutex
);
506 backend
->file
.path
= git__strdup(path
);
507 GIT_ERROR_CHECK_ALLOC(backend
->file
.path
);
508 git_array_init(backend
->file
.includes
);
510 backend
->parent
.open
= config_file_open
;
511 backend
->parent
.get
= config_file_get
;
512 backend
->parent
.set
= config_file_set
;
513 backend
->parent
.set_multivar
= config_file_set_multivar
;
514 backend
->parent
.del
= config_file_delete
;
515 backend
->parent
.del_multivar
= config_file_delete_multivar
;
516 backend
->parent
.iterator
= config_file_iterator
;
517 backend
->parent
.snapshot
= config_file_snapshot
;
518 backend
->parent
.lock
= config_file_lock
;
519 backend
->parent
.unlock
= config_file_unlock
;
520 backend
->parent
.free
= config_file_free
;
522 *out
= (git_config_backend
*)backend
;
527 static int included_path(git_str
*out
, const char *dir
, const char *path
)
529 /* From the user's home */
530 if (path
[0] == '~' && path
[1] == '/')
531 return git_sysdir_expand_global_file(out
, &path
[1]);
533 return git_fs_path_join_unrooted(out
, path
, dir
, NULL
);
536 /* Escape the values to write them to the file */
537 static char *escape_value(const char *ptr
)
543 GIT_ASSERT_ARG_WITH_RETVAL(ptr
, NULL
);
547 return git__calloc(1, sizeof(char));
549 if (git_str_init(&buf
, len
) < 0)
552 while (*ptr
!= '\0') {
553 if ((esc
= strchr(git_config_escaped
, *ptr
)) != NULL
) {
554 git_str_putc(&buf
, '\\');
555 git_str_putc(&buf
, git_config_escapes
[esc
- git_config_escaped
]);
557 git_str_putc(&buf
, *ptr
);
562 if (git_str_oom(&buf
))
565 return git_str_detach(&buf
);
568 static int parse_include(config_file_parse_data
*parse_data
, const char *file
)
570 config_file
*include
;
571 git_str path
= GIT_STR_INIT
;
578 if ((result
= git_fs_path_dirname_r(&path
, parse_data
->file
->path
)) < 0)
581 dir
= git_str_detach(&path
);
582 result
= included_path(&path
, dir
, file
);
588 include
= git_array_alloc(parse_data
->file
->includes
);
589 GIT_ERROR_CHECK_ALLOC(include
);
590 memset(include
, 0, sizeof(*include
));
591 git_array_init(include
->includes
);
592 include
->path
= git_str_detach(&path
);
594 result
= config_file_read(parse_data
->entries
, parse_data
->repo
, include
,
595 parse_data
->level
, parse_data
->depth
+1);
597 if (result
== GIT_ENOTFOUND
) {
605 static int do_match_gitdir(
607 const git_repository
*repo
,
608 const char *cfg_file
,
609 const char *condition
,
610 bool case_insensitive
)
612 git_str pattern
= GIT_STR_INIT
, gitdir
= GIT_STR_INIT
;
615 if (condition
[0] == '.' && git_fs_path_is_dirsep(condition
[1])) {
616 git_fs_path_dirname_r(&pattern
, cfg_file
);
617 git_str_joinpath(&pattern
, pattern
.ptr
, condition
+ 2);
618 } else if (condition
[0] == '~' && git_fs_path_is_dirsep(condition
[1]))
619 git_sysdir_expand_global_file(&pattern
, condition
+ 1);
620 else if (!git_fs_path_is_absolute(condition
))
621 git_str_joinpath(&pattern
, "**", condition
);
623 git_str_sets(&pattern
, condition
);
625 if (git_fs_path_is_dirsep(condition
[strlen(condition
) - 1]))
626 git_str_puts(&pattern
, "**");
628 if (git_str_oom(&pattern
)) {
633 if ((error
= git_repository__item_path(&gitdir
, repo
, GIT_REPOSITORY_ITEM_GITDIR
)) < 0)
636 if (git_fs_path_is_dirsep(gitdir
.ptr
[gitdir
.size
- 1]))
637 git_str_truncate(&gitdir
, gitdir
.size
- 1);
639 *matches
= wildmatch(pattern
.ptr
, gitdir
.ptr
,
640 WM_PATHNAME
| (case_insensitive
? WM_CASEFOLD
: 0)) == WM_MATCH
;
642 git_str_dispose(&pattern
);
643 git_str_dispose(&gitdir
);
647 static int conditional_match_gitdir(
649 const git_repository
*repo
,
650 const char *cfg_file
,
653 return do_match_gitdir(matches
, repo
, cfg_file
, value
, false);
656 static int conditional_match_gitdir_i(
658 const git_repository
*repo
,
659 const char *cfg_file
,
662 return do_match_gitdir(matches
, repo
, cfg_file
, value
, true);
665 static int conditional_match_onbranch(
667 const git_repository
*repo
,
668 const char *cfg_file
,
669 const char *condition
)
671 git_str reference
= GIT_STR_INIT
, buf
= GIT_STR_INIT
;
674 GIT_UNUSED(cfg_file
);
677 * NOTE: you cannot use `git_repository_head` here. Looking up the
678 * HEAD reference will create the ODB, which causes us to read the
679 * repo's config for keys like core.precomposeUnicode. As we're
680 * just parsing the config right now, though, this would result in
681 * an endless recursion.
684 if ((error
= git_str_joinpath(&buf
, git_repository_path(repo
), GIT_HEAD_FILE
)) < 0 ||
685 (error
= git_futils_readbuffer(&reference
, buf
.ptr
)) < 0)
687 git_str_rtrim(&reference
);
689 if (git__strncmp(reference
.ptr
, GIT_SYMREF
, strlen(GIT_SYMREF
)))
691 git_str_consume(&reference
, reference
.ptr
+ strlen(GIT_SYMREF
));
693 if (git__strncmp(reference
.ptr
, GIT_REFS_HEADS_DIR
, strlen(GIT_REFS_HEADS_DIR
)))
695 git_str_consume(&reference
, reference
.ptr
+ strlen(GIT_REFS_HEADS_DIR
));
698 * If the condition ends with a '/', then we should treat it as if
699 * it had '**' appended.
701 if ((error
= git_str_sets(&buf
, condition
)) < 0)
703 if (git_fs_path_is_dirsep(condition
[strlen(condition
) - 1]) &&
704 (error
= git_str_puts(&buf
, "**")) < 0)
707 *matches
= wildmatch(buf
.ptr
, reference
.ptr
, WM_PATHNAME
) == WM_MATCH
;
709 git_str_dispose(&reference
);
710 git_str_dispose(&buf
);
716 static const struct {
718 int (*matches
)(int *matches
, const git_repository
*repo
, const char *cfg
, const char *value
);
720 { "gitdir:", conditional_match_gitdir
},
721 { "gitdir/i:", conditional_match_gitdir_i
},
722 { "onbranch:", conditional_match_onbranch
}
725 static int parse_conditional_include(config_file_parse_data
*parse_data
, const char *section
, const char *file
)
728 size_t section_len
, i
;
729 int error
= 0, matches
;
731 if (!parse_data
->repo
|| !file
)
734 section_len
= strlen(section
);
737 * We checked that the string starts with `includeIf.` and ends
738 * in `.path` to get here. Make sure it consists of more.
740 if (section_len
< CONST_STRLEN("includeIf.") + CONST_STRLEN(".path"))
743 condition
= git__substrdup(section
+ CONST_STRLEN("includeIf."),
744 section_len
- CONST_STRLEN("includeIf.") - CONST_STRLEN(".path"));
746 GIT_ERROR_CHECK_ALLOC(condition
);
748 for (i
= 0; i
< ARRAY_SIZE(conditions
); i
++) {
749 if (git__prefixcmp(condition
, conditions
[i
].prefix
))
752 if ((error
= conditions
[i
].matches(&matches
,
754 parse_data
->file
->path
,
755 condition
+ strlen(conditions
[i
].prefix
))) < 0)
759 error
= parse_include(parse_data
, file
);
764 git__free(condition
);
768 static int read_on_variable(
769 git_config_parser
*reader
,
770 const char *current_section
,
771 const char *var_name
,
772 const char *var_value
,
777 config_file_parse_data
*parse_data
= (config_file_parse_data
*)data
;
778 git_str buf
= GIT_STR_INIT
;
779 git_config_entry
*entry
;
785 GIT_UNUSED(line_len
);
787 if (current_section
) {
788 /* TODO: Once warnings lang, we should likely warn
789 * here. Git appears to warn in most cases if it sees
790 * un-namespaced config options.
792 git_str_puts(&buf
, current_section
);
793 git_str_putc(&buf
, '.');
796 for (c
= var_name
; *c
; c
++)
797 git_str_putc(&buf
, git__tolower(*c
));
799 if (git_str_oom(&buf
))
802 entry
= git__calloc(1, sizeof(git_config_entry
));
803 GIT_ERROR_CHECK_ALLOC(entry
);
804 entry
->name
= git_str_detach(&buf
);
805 entry
->value
= var_value
? git__strdup(var_value
) : NULL
;
806 entry
->level
= parse_data
->level
;
807 entry
->include_depth
= parse_data
->depth
;
809 if ((result
= git_config_entries_append(parse_data
->entries
, entry
)) < 0)
814 /* Add or append the new config option */
815 if (!git__strcmp(entry
->name
, "include.path"))
816 result
= parse_include(parse_data
, entry
->value
);
817 else if (!git__prefixcmp(entry
->name
, "includeif.") &&
818 !git__suffixcmp(entry
->name
, ".path"))
819 result
= parse_conditional_include(parse_data
, entry
->name
, entry
->value
);
824 static int config_file_read_buffer(
825 git_config_entries
*entries
,
826 const git_repository
*repo
,
828 git_config_level_t level
,
833 config_file_parse_data parse_data
;
834 git_config_parser reader
;
837 if (depth
>= MAX_INCLUDE_DEPTH
) {
838 git_error_set(GIT_ERROR_CONFIG
, "maximum config include depth reached");
842 /* Initialize the reading position */
843 reader
.path
= file
->path
;
844 git_parse_ctx_init(&reader
.ctx
, buf
, buflen
);
846 /* If the file is empty, there's nothing for us to do */
847 if (!reader
.ctx
.content
|| *reader
.ctx
.content
== '\0') {
852 parse_data
.repo
= repo
;
853 parse_data
.file
= file
;
854 parse_data
.entries
= entries
;
855 parse_data
.level
= level
;
856 parse_data
.depth
= depth
;
858 error
= git_config_parse(&reader
, NULL
, read_on_variable
, NULL
, NULL
, &parse_data
);
864 static int config_file_read(
865 git_config_entries
*entries
,
866 const git_repository
*repo
,
868 git_config_level_t level
,
871 git_str contents
= GIT_STR_INIT
;
875 if (p_stat(file
->path
, &st
) < 0) {
876 error
= git_fs_path_set_error(errno
, file
->path
, "stat");
880 if ((error
= git_futils_readbuffer(&contents
, file
->path
)) < 0)
883 git_futils_filestamp_set_from_stat(&file
->stamp
, &st
);
884 if ((error
= git_hash_buf(file
->checksum
, contents
.ptr
, contents
.size
, GIT_HASH_ALGORITHM_SHA1
)) < 0)
887 if ((error
= config_file_read_buffer(entries
, repo
, file
, level
, depth
,
888 contents
.ptr
, contents
.size
)) < 0)
892 git_str_dispose(&contents
);
896 static int write_section(git_str
*fbuf
, const char *key
)
900 git_str buf
= GIT_STR_INIT
;
902 /* All of this just for [section "subsection"] */
903 dot
= strchr(key
, '.');
904 git_str_putc(&buf
, '[');
906 git_str_puts(&buf
, key
);
909 git_str_put(&buf
, key
, dot
- key
);
910 escaped
= escape_value(dot
+ 1);
911 GIT_ERROR_CHECK_ALLOC(escaped
);
912 git_str_printf(&buf
, " \"%s\"", escaped
);
915 git_str_puts(&buf
, "]\n");
917 if (git_str_oom(&buf
))
920 result
= git_str_put(fbuf
, git_str_cstr(&buf
), buf
.size
);
921 git_str_dispose(&buf
);
926 static const char *quotes_for_value(const char *value
)
930 if (value
[0] == ' ' || value
[0] == '\0')
933 for (ptr
= value
; *ptr
; ++ptr
) {
934 if (*ptr
== ';' || *ptr
== '#')
946 git_str buffered_comment
;
947 unsigned int in_section
: 1,
949 const char *orig_section
;
951 const char *orig_name
;
953 const git_regexp
*preg
;
957 static int write_line_to(git_str
*buf
, const char *line
, size_t line_len
)
959 int result
= git_str_put(buf
, line
, line_len
);
961 if (!result
&& line_len
&& line
[line_len
-1] != '\n')
962 result
= git_str_printf(buf
, "\n");
967 static int write_line(struct write_data
*write_data
, const char *line
, size_t line_len
)
969 return write_line_to(write_data
->buf
, line
, line_len
);
972 static int write_value(struct write_data
*write_data
)
977 q
= quotes_for_value(write_data
->value
);
978 result
= git_str_printf(write_data
->buf
,
979 "\t%s = %s%s%s\n", write_data
->orig_name
, q
, write_data
->value
, q
);
981 /* If we are updating a single name/value, we're done. Setting `value`
982 * to `NULL` will prevent us from trying to write it again later (in
983 * `write_on_section`) if we see the same section repeated.
985 if (!write_data
->preg
)
986 write_data
->value
= NULL
;
991 static int write_on_section(
992 git_config_parser
*reader
,
993 const char *current_section
,
998 struct write_data
*write_data
= (struct write_data
*)data
;
1003 /* If we were previously in the correct section (but aren't anymore)
1004 * and haven't written our value (for a simple name/value set, not
1005 * a multivar), then append it to the end of the section before writing
1008 if (write_data
->in_section
&& !write_data
->preg
&& write_data
->value
)
1009 result
= write_value(write_data
);
1011 write_data
->in_section
= strcmp(current_section
, write_data
->section
) == 0;
1014 * If there were comments just before this section, dump them as well.
1017 result
= git_str_put(write_data
->buf
, write_data
->buffered_comment
.ptr
, write_data
->buffered_comment
.size
);
1018 git_str_clear(&write_data
->buffered_comment
);
1022 result
= write_line(write_data
, line
, line_len
);
1027 static int write_on_variable(
1028 git_config_parser
*reader
,
1029 const char *current_section
,
1030 const char *var_name
,
1031 const char *var_value
,
1036 struct write_data
*write_data
= (struct write_data
*)data
;
1037 bool has_matched
= false;
1041 GIT_UNUSED(current_section
);
1044 * If there were comments just before this variable, let's dump them as well.
1046 if ((error
= git_str_put(write_data
->buf
, write_data
->buffered_comment
.ptr
, write_data
->buffered_comment
.size
)) < 0)
1049 git_str_clear(&write_data
->buffered_comment
);
1051 /* See if we are to update this name/value pair; first examine name */
1052 if (write_data
->in_section
&&
1053 strcasecmp(write_data
->name
, var_name
) == 0)
1056 /* If we have a regex to match the value, see if it matches */
1057 if (has_matched
&& write_data
->preg
!= NULL
)
1058 has_matched
= (git_regexp_match(write_data
->preg
, var_value
) == 0);
1060 /* If this isn't the name/value we're looking for, simply dump the
1061 * existing data back out and continue on.
1064 return write_line(write_data
, line
, line_len
);
1066 write_data
->preg_replaced
= 1;
1068 /* If value is NULL, we are deleting this value; write nothing. */
1069 if (!write_data
->value
)
1072 return write_value(write_data
);
1075 static int write_on_comment(git_config_parser
*reader
, const char *line
, size_t line_len
, void *data
)
1077 struct write_data
*write_data
;
1081 write_data
= (struct write_data
*)data
;
1082 return write_line_to(&write_data
->buffered_comment
, line
, line_len
);
1085 static int write_on_eof(
1086 git_config_parser
*reader
, const char *current_section
, void *data
)
1088 struct write_data
*write_data
= (struct write_data
*)data
;
1094 * If we've buffered comments when reaching EOF, make sure to dump them.
1096 if ((result
= git_str_put(write_data
->buf
, write_data
->buffered_comment
.ptr
, write_data
->buffered_comment
.size
)) < 0)
1099 /* If we are at the EOF and have not written our value (again, for a
1100 * simple name/value set, not a multivar) then we have never seen the
1101 * section in question and should create a new section and write the
1104 if ((!write_data
->preg
|| !write_data
->preg_replaced
) && write_data
->value
) {
1105 /* write the section header unless we're already in it */
1106 if (!current_section
|| strcmp(current_section
, write_data
->section
))
1107 result
= write_section(write_data
->buf
, write_data
->orig_section
);
1110 result
= write_value(write_data
);
1117 * This is pretty much the parsing, except we write out anything we don't have
1119 static int config_file_write(config_file_backend
*cfg
, const char *orig_key
, const char *key
, const git_regexp
*preg
, const char *value
)
1122 char *orig_section
= NULL
, *section
= NULL
, *orig_name
, *name
, *ldot
;
1123 git_str buf
= GIT_STR_INIT
, contents
= GIT_STR_INIT
;
1124 git_config_parser parser
= GIT_CONFIG_PARSER_INIT
;
1125 git_filebuf file
= GIT_FILEBUF_INIT
;
1126 struct write_data write_data
;
1129 memset(&write_data
, 0, sizeof(write_data
));
1132 error
= git_str_puts(&contents
, git_str_cstr(&cfg
->locked_content
) == NULL
? "" : git_str_cstr(&cfg
->locked_content
));
1134 if ((error
= git_filebuf_open(&file
, cfg
->file
.path
, GIT_FILEBUF_HASH_CONTENTS
,
1135 GIT_CONFIG_FILE_MODE
)) < 0)
1138 /* We need to read in our own config file */
1139 error
= git_futils_readbuffer(&contents
, cfg
->file
.path
);
1141 if (error
< 0 && error
!= GIT_ENOTFOUND
)
1144 if ((git_config_parser_init(&parser
, cfg
->file
.path
, contents
.ptr
, contents
.size
)) < 0)
1147 ldot
= strrchr(key
, '.');
1149 section
= git__strndup(key
, ldot
- key
);
1150 GIT_ERROR_CHECK_ALLOC(section
);
1152 ldot
= strrchr(orig_key
, '.');
1153 orig_name
= ldot
+ 1;
1154 orig_section
= git__strndup(orig_key
, ldot
- orig_key
);
1155 GIT_ERROR_CHECK_ALLOC(orig_section
);
1157 write_data
.buf
= &buf
;
1158 write_data
.orig_section
= orig_section
;
1159 write_data
.section
= section
;
1160 write_data
.orig_name
= orig_name
;
1161 write_data
.name
= name
;
1162 write_data
.preg
= preg
;
1163 write_data
.value
= value
;
1165 if ((error
= git_config_parse(&parser
, write_on_section
, write_on_variable
,
1166 write_on_comment
, write_on_eof
, &write_data
)) < 0)
1170 size_t len
= buf
.asize
;
1171 /* Update our copy with the modified contents */
1172 git_str_dispose(&cfg
->locked_content
);
1173 git_str_attach(&cfg
->locked_content
, git_str_detach(&buf
), len
);
1175 git_filebuf_write(&file
, git_str_cstr(&buf
), git_str_len(&buf
));
1177 if ((error
= git_filebuf_commit(&file
)) < 0)
1180 if ((error
= config_file_refresh_from_buffer(&cfg
->parent
, buf
.ptr
, buf
.size
)) < 0)
1186 git__free(orig_section
);
1187 git_str_dispose(&write_data
.buffered_comment
);
1188 git_str_dispose(&buf
);
1189 git_str_dispose(&contents
);
1190 git_filebuf_cleanup(&file
);
1191 git_config_parser_dispose(&parser
);