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"
23 /* Max depth for [include] directives */
24 #define MAX_INCLUDE_DEPTH 10
26 typedef struct config_file
{
27 git_futils_filestamp stamp
;
30 git_array_t(struct config_file
) includes
;
34 git_config_backend parent
;
35 git_mutex values_mutex
;
36 git_config_entries
*entries
;
37 const git_repository
*repo
;
38 git_config_level_t level
;
40 git_array_t(git_config_parser
) readers
;
43 git_filebuf locked_buf
;
44 git_buf locked_content
;
47 } config_file_backend
;
50 const git_repository
*repo
;
52 git_config_entries
*entries
;
53 git_config_level_t level
;
55 } config_file_parse_data
;
57 static int config_file_read(git_config_entries
*entries
, const git_repository
*repo
, config_file
*file
, git_config_level_t level
, int depth
);
58 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
);
59 static int config_file_write(config_file_backend
*cfg
, const char *orig_key
, const char *key
, const git_regexp
*preg
, const char *value
);
60 static char *escape_value(const char *ptr
);
63 * Take the current values map from the backend and increase its
64 * refcount. This is its own function to make sure we use the mutex to
65 * avoid the map pointer from changing under us.
67 static int config_file_entries_take(git_config_entries
**out
, config_file_backend
*b
)
71 if ((error
= git_mutex_lock(&b
->values_mutex
)) < 0) {
72 git_error_set(GIT_ERROR_OS
, "failed to lock config backend");
76 git_config_entries_incref(b
->entries
);
79 git_mutex_unlock(&b
->values_mutex
);
84 static void config_file_clear(config_file
*file
)
92 git_array_foreach(file
->includes
, i
, include
) {
93 config_file_clear(include
);
95 git_array_clear(file
->includes
);
97 git__free(file
->path
);
100 static int config_file_open(git_config_backend
*cfg
, git_config_level_t level
, const git_repository
*repo
)
102 config_file_backend
*b
= GIT_CONTAINER_OF(cfg
, config_file_backend
, parent
);
108 if ((res
= git_config_entries_new(&b
->entries
)) < 0)
111 if (!git_path_exists(b
->file
.path
))
114 if (res
< 0 || (res
= config_file_read(b
->entries
, repo
, &b
->file
, level
, 0)) < 0) {
115 git_config_entries_free(b
->entries
);
122 static int config_file_is_modified(int *modified
, config_file
*file
)
124 config_file
*include
;
125 git_buf buf
= GIT_BUF_INIT
;
132 if (!git_futils_filestamp_check(&file
->stamp
, file
->path
))
135 if ((error
= git_futils_readbuffer(&buf
, file
->path
)) < 0)
138 if ((error
= git_hash_buf(&hash
, buf
.ptr
, buf
.size
)) < 0)
141 if (!git_oid_equal(&hash
, &file
->checksum
)) {
147 git_array_foreach(file
->includes
, i
, include
) {
148 if ((error
= config_file_is_modified(modified
, include
)) < 0 || *modified
)
153 git_buf_dispose(&buf
);
158 static int config_file_set_entries(git_config_backend
*cfg
, git_config_entries
*entries
)
160 config_file_backend
*b
= GIT_CONTAINER_OF(cfg
, config_file_backend
, parent
);
161 git_config_entries
*old
= NULL
;
162 config_file
*include
;
166 if (b
->parent
.readonly
) {
167 git_error_set(GIT_ERROR_CONFIG
, "this backend is read-only");
171 git_array_foreach(b
->file
.includes
, i
, include
)
172 config_file_clear(include
);
173 git_array_clear(b
->file
.includes
);
175 if ((error
= git_mutex_lock(&b
->values_mutex
)) < 0) {
176 git_error_set(GIT_ERROR_OS
, "failed to lock config backend");
181 b
->entries
= entries
;
183 git_mutex_unlock(&b
->values_mutex
);
186 git_config_entries_free(old
);
190 static int config_file_refresh_from_buffer(git_config_backend
*cfg
, const char *buf
, size_t buflen
)
192 config_file_backend
*b
= GIT_CONTAINER_OF(cfg
, config_file_backend
, parent
);
193 git_config_entries
*entries
= NULL
;
196 if ((error
= git_config_entries_new(&entries
)) < 0 ||
197 (error
= config_file_read_buffer(entries
, b
->repo
, &b
->file
,
198 b
->level
, 0, buf
, buflen
)) < 0 ||
199 (error
= config_file_set_entries(cfg
, entries
)) < 0)
204 git_config_entries_free(entries
);
208 static int config_file_refresh(git_config_backend
*cfg
)
210 config_file_backend
*b
= GIT_CONTAINER_OF(cfg
, config_file_backend
, parent
);
211 git_config_entries
*entries
= NULL
;
217 if ((error
= config_file_is_modified(&modified
, &b
->file
)) < 0 && error
!= GIT_ENOTFOUND
)
223 if ((error
= git_config_entries_new(&entries
)) < 0 ||
224 (error
= config_file_read(entries
, b
->repo
, &b
->file
, b
->level
, 0)) < 0 ||
225 (error
= config_file_set_entries(cfg
, entries
)) < 0)
230 git_config_entries_free(entries
);
232 return (error
== GIT_ENOTFOUND
) ? 0 : error
;
235 static void config_file_free(git_config_backend
*_backend
)
237 config_file_backend
*backend
= GIT_CONTAINER_OF(_backend
, config_file_backend
, parent
);
242 config_file_clear(&backend
->file
);
243 git_config_entries_free(backend
->entries
);
244 git_mutex_free(&backend
->values_mutex
);
248 static int config_file_iterator(
249 git_config_iterator
**iter
,
250 struct git_config_backend
*backend
)
252 config_file_backend
*b
= GIT_CONTAINER_OF(backend
, config_file_backend
, parent
);
253 git_config_entries
*dupped
= NULL
, *entries
= NULL
;
256 if ((error
= config_file_refresh(backend
)) < 0 ||
257 (error
= config_file_entries_take(&entries
, b
)) < 0 ||
258 (error
= git_config_entries_dup(&dupped
, entries
)) < 0 ||
259 (error
= git_config_entries_iterator_new(iter
, dupped
)) < 0)
263 /* Let iterator delete duplicated entries when it's done */
264 git_config_entries_free(entries
);
265 git_config_entries_free(dupped
);
269 static int config_file_snapshot(git_config_backend
**out
, git_config_backend
*backend
)
271 return git_config_backend_snapshot(out
, backend
);
274 static int config_file_set(git_config_backend
*cfg
, const char *name
, const char *value
)
276 config_file_backend
*b
= GIT_CONTAINER_OF(cfg
, config_file_backend
, parent
);
277 git_config_entries
*entries
;
278 git_config_entry
*existing
;
279 char *key
, *esc_value
= NULL
;
282 if ((error
= git_config__normalize_name(name
, &key
)) < 0)
285 if ((error
= config_file_entries_take(&entries
, b
)) < 0)
288 /* Check whether we'd be modifying an included or multivar key */
289 if ((error
= git_config_entries_get_unique(&existing
, entries
, key
)) < 0) {
290 if (error
!= GIT_ENOTFOUND
)
293 } else if ((!existing
->value
&& !value
) ||
294 (existing
->value
&& value
&& !strcmp(existing
->value
, value
))) {
295 /* don't update if old and new values already match */
300 /* No early returns due to sanity checks, let's write it out and refresh */
302 esc_value
= escape_value(value
);
303 GIT_ERROR_CHECK_ALLOC(esc_value
);
306 if ((error
= config_file_write(b
, name
, key
, NULL
, esc_value
)) < 0)
310 git_config_entries_free(entries
);
311 git__free(esc_value
);
316 /* release the map containing the entry as an equivalent to freeing it */
317 static void config_file_entry_free(git_config_entry
*entry
)
319 git_config_entries
*entries
= (git_config_entries
*) entry
->payload
;
320 git_config_entries_free(entries
);
324 * Internal function that actually gets the value in string form
326 static int config_file_get(git_config_backend
*cfg
, const char *key
, git_config_entry
**out
)
328 config_file_backend
*h
= GIT_CONTAINER_OF(cfg
, config_file_backend
, parent
);
329 git_config_entries
*entries
= NULL
;
330 git_config_entry
*entry
;
333 if (!h
->parent
.readonly
&& ((error
= config_file_refresh(cfg
)) < 0))
336 if ((error
= config_file_entries_take(&entries
, h
)) < 0)
339 if ((error
= (git_config_entries_get(&entry
, entries
, key
))) < 0) {
340 git_config_entries_free(entries
);
344 entry
->free
= config_file_entry_free
;
345 entry
->payload
= entries
;
351 static int config_file_set_multivar(
352 git_config_backend
*cfg
, const char *name
, const char *regexp
, const char *value
)
354 config_file_backend
*b
= GIT_CONTAINER_OF(cfg
, config_file_backend
, parent
);
361 if ((result
= git_config__normalize_name(name
, &key
)) < 0)
364 if ((result
= git_regexp_compile(&preg
, regexp
, 0)) < 0)
367 /* If we do have it, set call config_file_write() and reload */
368 if ((result
= config_file_write(b
, name
, key
, &preg
, value
)) < 0)
373 git_regexp_dispose(&preg
);
378 static int config_file_delete(git_config_backend
*cfg
, const char *name
)
380 config_file_backend
*b
= GIT_CONTAINER_OF(cfg
, config_file_backend
, parent
);
381 git_config_entries
*entries
= NULL
;
382 git_config_entry
*entry
;
386 if ((error
= git_config__normalize_name(name
, &key
)) < 0)
389 if ((error
= config_file_entries_take(&entries
, b
)) < 0)
392 /* Check whether we'd be modifying an included or multivar key */
393 if ((error
= git_config_entries_get_unique(&entry
, entries
, key
)) < 0) {
394 if (error
== GIT_ENOTFOUND
)
395 git_error_set(GIT_ERROR_CONFIG
, "could not find key '%s' to delete", name
);
399 if ((error
= config_file_write(b
, name
, entry
->name
, NULL
, NULL
)) < 0)
403 git_config_entries_free(entries
);
408 static int config_file_delete_multivar(git_config_backend
*cfg
, const char *name
, const char *regexp
)
410 config_file_backend
*b
= GIT_CONTAINER_OF(cfg
, config_file_backend
, parent
);
411 git_config_entries
*entries
= NULL
;
412 git_config_entry
*entry
= NULL
;
413 git_regexp preg
= GIT_REGEX_INIT
;
417 if ((result
= git_config__normalize_name(name
, &key
)) < 0)
420 if ((result
= config_file_entries_take(&entries
, b
)) < 0)
423 if ((result
= git_config_entries_get(&entry
, entries
, key
)) < 0) {
424 if (result
== GIT_ENOTFOUND
)
425 git_error_set(GIT_ERROR_CONFIG
, "could not find key '%s' to delete", name
);
429 if ((result
= git_regexp_compile(&preg
, regexp
, 0)) < 0)
432 if ((result
= config_file_write(b
, name
, key
, &preg
, NULL
)) < 0)
436 git_config_entries_free(entries
);
438 git_regexp_dispose(&preg
);
442 static int config_file_lock(git_config_backend
*_cfg
)
444 config_file_backend
*cfg
= GIT_CONTAINER_OF(_cfg
, config_file_backend
, parent
);
447 if ((error
= git_filebuf_open(&cfg
->locked_buf
, cfg
->file
.path
, 0, GIT_CONFIG_FILE_MODE
)) < 0)
450 error
= git_futils_readbuffer(&cfg
->locked_content
, cfg
->file
.path
);
451 if (error
< 0 && error
!= GIT_ENOTFOUND
) {
452 git_filebuf_cleanup(&cfg
->locked_buf
);
461 static int config_file_unlock(git_config_backend
*_cfg
, int success
)
463 config_file_backend
*cfg
= GIT_CONTAINER_OF(_cfg
, config_file_backend
, parent
);
467 git_filebuf_write(&cfg
->locked_buf
, cfg
->locked_content
.ptr
, cfg
->locked_content
.size
);
468 error
= git_filebuf_commit(&cfg
->locked_buf
);
471 git_filebuf_cleanup(&cfg
->locked_buf
);
472 git_buf_dispose(&cfg
->locked_content
);
478 int git_config_backend_from_file(git_config_backend
**out
, const char *path
)
480 config_file_backend
*backend
;
482 backend
= git__calloc(1, sizeof(config_file_backend
));
483 GIT_ERROR_CHECK_ALLOC(backend
);
485 backend
->parent
.version
= GIT_CONFIG_BACKEND_VERSION
;
486 git_mutex_init(&backend
->values_mutex
);
488 backend
->file
.path
= git__strdup(path
);
489 GIT_ERROR_CHECK_ALLOC(backend
->file
.path
);
490 git_array_init(backend
->file
.includes
);
492 backend
->parent
.open
= config_file_open
;
493 backend
->parent
.get
= config_file_get
;
494 backend
->parent
.set
= config_file_set
;
495 backend
->parent
.set_multivar
= config_file_set_multivar
;
496 backend
->parent
.del
= config_file_delete
;
497 backend
->parent
.del_multivar
= config_file_delete_multivar
;
498 backend
->parent
.iterator
= config_file_iterator
;
499 backend
->parent
.snapshot
= config_file_snapshot
;
500 backend
->parent
.lock
= config_file_lock
;
501 backend
->parent
.unlock
= config_file_unlock
;
502 backend
->parent
.free
= config_file_free
;
504 *out
= (git_config_backend
*)backend
;
509 static int included_path(git_buf
*out
, const char *dir
, const char *path
)
511 /* From the user's home */
512 if (path
[0] == '~' && path
[1] == '/')
513 return git_sysdir_expand_global_file(out
, &path
[1]);
515 return git_path_join_unrooted(out
, path
, dir
, NULL
);
518 /* Escape the values to write them to the file */
519 static char *escape_value(const char *ptr
)
529 return git__calloc(1, sizeof(char));
531 if (git_buf_init(&buf
, len
) < 0)
534 while (*ptr
!= '\0') {
535 if ((esc
= strchr(git_config_escaped
, *ptr
)) != NULL
) {
536 git_buf_putc(&buf
, '\\');
537 git_buf_putc(&buf
, git_config_escapes
[esc
- git_config_escaped
]);
539 git_buf_putc(&buf
, *ptr
);
544 if (git_buf_oom(&buf
))
547 return git_buf_detach(&buf
);
550 static int parse_include(config_file_parse_data
*parse_data
, const char *file
)
552 config_file
*include
;
553 git_buf path
= GIT_BUF_INIT
;
560 if ((result
= git_path_dirname_r(&path
, parse_data
->file
->path
)) < 0)
563 dir
= git_buf_detach(&path
);
564 result
= included_path(&path
, dir
, file
);
570 include
= git_array_alloc(parse_data
->file
->includes
);
571 GIT_ERROR_CHECK_ALLOC(include
);
572 memset(include
, 0, sizeof(*include
));
573 git_array_init(include
->includes
);
574 include
->path
= git_buf_detach(&path
);
576 result
= config_file_read(parse_data
->entries
, parse_data
->repo
, include
,
577 parse_data
->level
, parse_data
->depth
+1);
579 if (result
== GIT_ENOTFOUND
) {
587 static int do_match_gitdir(
589 const git_repository
*repo
,
590 const char *cfg_file
,
591 const char *condition
,
592 bool case_insensitive
)
594 git_buf pattern
= GIT_BUF_INIT
, gitdir
= GIT_BUF_INIT
;
597 if (condition
[0] == '.' && git_path_is_dirsep(condition
[1])) {
598 git_path_dirname_r(&pattern
, cfg_file
);
599 git_buf_joinpath(&pattern
, pattern
.ptr
, condition
+ 2);
600 } else if (condition
[0] == '~' && git_path_is_dirsep(condition
[1]))
601 git_sysdir_expand_global_file(&pattern
, condition
+ 1);
602 else if (!git_path_is_absolute(condition
))
603 git_buf_joinpath(&pattern
, "**", condition
);
605 git_buf_sets(&pattern
, condition
);
607 if (git_path_is_dirsep(condition
[strlen(condition
) - 1]))
608 git_buf_puts(&pattern
, "**");
610 if (git_buf_oom(&pattern
)) {
615 if ((error
= git_repository_item_path(&gitdir
, repo
, GIT_REPOSITORY_ITEM_GITDIR
)) < 0)
618 if (git_path_is_dirsep(gitdir
.ptr
[gitdir
.size
- 1]))
619 git_buf_truncate(&gitdir
, gitdir
.size
- 1);
621 *matches
= wildmatch(pattern
.ptr
, gitdir
.ptr
,
622 WM_PATHNAME
| (case_insensitive
? WM_CASEFOLD
: 0)) == WM_MATCH
;
624 git_buf_dispose(&pattern
);
625 git_buf_dispose(&gitdir
);
629 static int conditional_match_gitdir(
631 const git_repository
*repo
,
632 const char *cfg_file
,
635 return do_match_gitdir(matches
, repo
, cfg_file
, value
, false);
638 static int conditional_match_gitdir_i(
640 const git_repository
*repo
,
641 const char *cfg_file
,
644 return do_match_gitdir(matches
, repo
, cfg_file
, value
, true);
647 static int conditional_match_onbranch(
649 const git_repository
*repo
,
650 const char *cfg_file
,
651 const char *condition
)
653 git_buf reference
= GIT_BUF_INIT
, buf
= GIT_BUF_INIT
;
656 GIT_UNUSED(cfg_file
);
659 * NOTE: you cannot use `git_repository_head` here. Looking up the
660 * HEAD reference will create the ODB, which causes us to read the
661 * repo's config for keys like core.precomposeUnicode. As we're
662 * just parsing the config right now, though, this would result in
663 * an endless recursion.
666 if ((error
= git_buf_joinpath(&buf
, git_repository_path(repo
), GIT_HEAD_FILE
)) < 0 ||
667 (error
= git_futils_readbuffer(&reference
, buf
.ptr
)) < 0)
669 git_buf_rtrim(&reference
);
671 if (git__strncmp(reference
.ptr
, GIT_SYMREF
, strlen(GIT_SYMREF
)))
673 git_buf_consume(&reference
, reference
.ptr
+ strlen(GIT_SYMREF
));
675 if (git__strncmp(reference
.ptr
, GIT_REFS_HEADS_DIR
, strlen(GIT_REFS_HEADS_DIR
)))
677 git_buf_consume(&reference
, reference
.ptr
+ strlen(GIT_REFS_HEADS_DIR
));
680 * If the condition ends with a '/', then we should treat it as if
681 * it had '**' appended.
683 if ((error
= git_buf_sets(&buf
, condition
)) < 0)
685 if (git_path_is_dirsep(condition
[strlen(condition
) - 1]) &&
686 (error
= git_buf_puts(&buf
, "**")) < 0)
689 *matches
= wildmatch(buf
.ptr
, reference
.ptr
, WM_PATHNAME
) == WM_MATCH
;
691 git_buf_dispose(&reference
);
692 git_buf_dispose(&buf
);
698 static const struct {
700 int (*matches
)(int *matches
, const git_repository
*repo
, const char *cfg
, const char *value
);
702 { "gitdir:", conditional_match_gitdir
},
703 { "gitdir/i:", conditional_match_gitdir_i
},
704 { "onbranch:", conditional_match_onbranch
}
707 static int parse_conditional_include(config_file_parse_data
*parse_data
, const char *section
, const char *file
)
711 int error
= 0, matches
;
713 if (!parse_data
->repo
|| !file
)
716 condition
= git__substrdup(section
+ strlen("includeIf."),
717 strlen(section
) - strlen("includeIf.") - strlen(".path"));
719 for (i
= 0; i
< ARRAY_SIZE(conditions
); i
++) {
720 if (git__prefixcmp(condition
, conditions
[i
].prefix
))
723 if ((error
= conditions
[i
].matches(&matches
,
725 parse_data
->file
->path
,
726 condition
+ strlen(conditions
[i
].prefix
))) < 0)
730 error
= parse_include(parse_data
, file
);
735 git__free(condition
);
739 static int read_on_variable(
740 git_config_parser
*reader
,
741 const char *current_section
,
742 const char *var_name
,
743 const char *var_value
,
748 config_file_parse_data
*parse_data
= (config_file_parse_data
*)data
;
749 git_buf buf
= GIT_BUF_INIT
;
750 git_config_entry
*entry
;
756 GIT_UNUSED(line_len
);
758 if (current_section
) {
759 /* TODO: Once warnings lang, we should likely warn
760 * here. Git appears to warn in most cases if it sees
761 * un-namespaced config options.
763 git_buf_puts(&buf
, current_section
);
764 git_buf_putc(&buf
, '.');
767 for (c
= var_name
; *c
; c
++)
768 git_buf_putc(&buf
, git__tolower(*c
));
770 if (git_buf_oom(&buf
))
773 entry
= git__calloc(1, sizeof(git_config_entry
));
774 GIT_ERROR_CHECK_ALLOC(entry
);
775 entry
->name
= git_buf_detach(&buf
);
776 entry
->value
= var_value
? git__strdup(var_value
) : NULL
;
777 entry
->level
= parse_data
->level
;
778 entry
->include_depth
= parse_data
->depth
;
780 if ((result
= git_config_entries_append(parse_data
->entries
, entry
)) < 0)
785 /* Add or append the new config option */
786 if (!git__strcmp(entry
->name
, "include.path"))
787 result
= parse_include(parse_data
, entry
->value
);
788 else if (!git__prefixcmp(entry
->name
, "includeif.") &&
789 !git__suffixcmp(entry
->name
, ".path"))
790 result
= parse_conditional_include(parse_data
, entry
->name
, entry
->value
);
795 static int config_file_read_buffer(
796 git_config_entries
*entries
,
797 const git_repository
*repo
,
799 git_config_level_t level
,
804 config_file_parse_data parse_data
;
805 git_config_parser reader
;
808 if (depth
>= MAX_INCLUDE_DEPTH
) {
809 git_error_set(GIT_ERROR_CONFIG
, "maximum config include depth reached");
813 /* Initialize the reading position */
814 reader
.path
= file
->path
;
815 git_parse_ctx_init(&reader
.ctx
, buf
, buflen
);
817 /* If the file is empty, there's nothing for us to do */
818 if (!reader
.ctx
.content
|| *reader
.ctx
.content
== '\0') {
823 parse_data
.repo
= repo
;
824 parse_data
.file
= file
;
825 parse_data
.entries
= entries
;
826 parse_data
.level
= level
;
827 parse_data
.depth
= depth
;
829 error
= git_config_parse(&reader
, NULL
, read_on_variable
, NULL
, NULL
, &parse_data
);
835 static int config_file_read(
836 git_config_entries
*entries
,
837 const git_repository
*repo
,
839 git_config_level_t level
,
842 git_buf contents
= GIT_BUF_INIT
;
846 if (p_stat(file
->path
, &st
) < 0) {
847 error
= git_path_set_error(errno
, file
->path
, "stat");
851 if ((error
= git_futils_readbuffer(&contents
, file
->path
)) < 0)
854 git_futils_filestamp_set_from_stat(&file
->stamp
, &st
);
855 if ((error
= git_hash_buf(&file
->checksum
, contents
.ptr
, contents
.size
)) < 0)
858 if ((error
= config_file_read_buffer(entries
, repo
, file
, level
, depth
,
859 contents
.ptr
, contents
.size
)) < 0)
863 git_buf_dispose(&contents
);
867 static int write_section(git_buf
*fbuf
, const char *key
)
871 git_buf buf
= GIT_BUF_INIT
;
873 /* All of this just for [section "subsection"] */
874 dot
= strchr(key
, '.');
875 git_buf_putc(&buf
, '[');
877 git_buf_puts(&buf
, key
);
880 git_buf_put(&buf
, key
, dot
- key
);
881 escaped
= escape_value(dot
+ 1);
882 GIT_ERROR_CHECK_ALLOC(escaped
);
883 git_buf_printf(&buf
, " \"%s\"", escaped
);
886 git_buf_puts(&buf
, "]\n");
888 if (git_buf_oom(&buf
))
891 result
= git_buf_put(fbuf
, git_buf_cstr(&buf
), buf
.size
);
892 git_buf_dispose(&buf
);
897 static const char *quotes_for_value(const char *value
)
901 if (value
[0] == ' ' || value
[0] == '\0')
904 for (ptr
= value
; *ptr
; ++ptr
) {
905 if (*ptr
== ';' || *ptr
== '#')
917 git_buf buffered_comment
;
918 unsigned int in_section
: 1,
920 const char *orig_section
;
922 const char *orig_name
;
924 const git_regexp
*preg
;
928 static int write_line_to(git_buf
*buf
, const char *line
, size_t line_len
)
930 int result
= git_buf_put(buf
, line
, line_len
);
932 if (!result
&& line_len
&& line
[line_len
-1] != '\n')
933 result
= git_buf_printf(buf
, "\n");
938 static int write_line(struct write_data
*write_data
, const char *line
, size_t line_len
)
940 return write_line_to(write_data
->buf
, line
, line_len
);
943 static int write_value(struct write_data
*write_data
)
948 q
= quotes_for_value(write_data
->value
);
949 result
= git_buf_printf(write_data
->buf
,
950 "\t%s = %s%s%s\n", write_data
->orig_name
, q
, write_data
->value
, q
);
952 /* If we are updating a single name/value, we're done. Setting `value`
953 * to `NULL` will prevent us from trying to write it again later (in
954 * `write_on_section`) if we see the same section repeated.
956 if (!write_data
->preg
)
957 write_data
->value
= NULL
;
962 static int write_on_section(
963 git_config_parser
*reader
,
964 const char *current_section
,
969 struct write_data
*write_data
= (struct write_data
*)data
;
974 /* If we were previously in the correct section (but aren't anymore)
975 * and haven't written our value (for a simple name/value set, not
976 * a multivar), then append it to the end of the section before writing
979 if (write_data
->in_section
&& !write_data
->preg
&& write_data
->value
)
980 result
= write_value(write_data
);
982 write_data
->in_section
= strcmp(current_section
, write_data
->section
) == 0;
985 * If there were comments just before this section, dump them as well.
988 result
= git_buf_put(write_data
->buf
, write_data
->buffered_comment
.ptr
, write_data
->buffered_comment
.size
);
989 git_buf_clear(&write_data
->buffered_comment
);
993 result
= write_line(write_data
, line
, line_len
);
998 static int write_on_variable(
999 git_config_parser
*reader
,
1000 const char *current_section
,
1001 const char *var_name
,
1002 const char *var_value
,
1007 struct write_data
*write_data
= (struct write_data
*)data
;
1008 bool has_matched
= false;
1012 GIT_UNUSED(current_section
);
1015 * If there were comments just before this variable, let's dump them as well.
1017 if ((error
= git_buf_put(write_data
->buf
, write_data
->buffered_comment
.ptr
, write_data
->buffered_comment
.size
)) < 0)
1020 git_buf_clear(&write_data
->buffered_comment
);
1022 /* See if we are to update this name/value pair; first examine name */
1023 if (write_data
->in_section
&&
1024 strcasecmp(write_data
->name
, var_name
) == 0)
1027 /* If we have a regex to match the value, see if it matches */
1028 if (has_matched
&& write_data
->preg
!= NULL
)
1029 has_matched
= (git_regexp_match(write_data
->preg
, var_value
) == 0);
1031 /* If this isn't the name/value we're looking for, simply dump the
1032 * existing data back out and continue on.
1035 return write_line(write_data
, line
, line_len
);
1037 write_data
->preg_replaced
= 1;
1039 /* If value is NULL, we are deleting this value; write nothing. */
1040 if (!write_data
->value
)
1043 return write_value(write_data
);
1046 static int write_on_comment(git_config_parser
*reader
, const char *line
, size_t line_len
, void *data
)
1048 struct write_data
*write_data
;
1052 write_data
= (struct write_data
*)data
;
1053 return write_line_to(&write_data
->buffered_comment
, line
, line_len
);
1056 static int write_on_eof(
1057 git_config_parser
*reader
, const char *current_section
, void *data
)
1059 struct write_data
*write_data
= (struct write_data
*)data
;
1065 * If we've buffered comments when reaching EOF, make sure to dump them.
1067 if ((result
= git_buf_put(write_data
->buf
, write_data
->buffered_comment
.ptr
, write_data
->buffered_comment
.size
)) < 0)
1070 /* If we are at the EOF and have not written our value (again, for a
1071 * simple name/value set, not a multivar) then we have never seen the
1072 * section in question and should create a new section and write the
1075 if ((!write_data
->preg
|| !write_data
->preg_replaced
) && write_data
->value
) {
1076 /* write the section header unless we're already in it */
1077 if (!current_section
|| strcmp(current_section
, write_data
->section
))
1078 result
= write_section(write_data
->buf
, write_data
->orig_section
);
1081 result
= write_value(write_data
);
1088 * This is pretty much the parsing, except we write out anything we don't have
1090 static int config_file_write(config_file_backend
*cfg
, const char *orig_key
, const char *key
, const git_regexp
*preg
, const char* value
)
1093 char *orig_section
= NULL
, *section
= NULL
, *orig_name
, *name
, *ldot
;
1094 git_buf buf
= GIT_BUF_INIT
, contents
= GIT_BUF_INIT
;
1095 git_config_parser parser
= GIT_CONFIG_PARSER_INIT
;
1096 git_filebuf file
= GIT_FILEBUF_INIT
;
1097 struct write_data write_data
;
1100 memset(&write_data
, 0, sizeof(write_data
));
1103 error
= git_buf_puts(&contents
, git_buf_cstr(&cfg
->locked_content
) == NULL
? "" : git_buf_cstr(&cfg
->locked_content
));
1105 if ((error
= git_filebuf_open(&file
, cfg
->file
.path
, GIT_FILEBUF_HASH_CONTENTS
,
1106 GIT_CONFIG_FILE_MODE
)) < 0)
1109 /* We need to read in our own config file */
1110 error
= git_futils_readbuffer(&contents
, cfg
->file
.path
);
1112 if (error
< 0 && error
!= GIT_ENOTFOUND
)
1115 if ((git_config_parser_init(&parser
, cfg
->file
.path
, contents
.ptr
, contents
.size
)) < 0)
1118 ldot
= strrchr(key
, '.');
1120 section
= git__strndup(key
, ldot
- key
);
1121 GIT_ERROR_CHECK_ALLOC(section
);
1123 ldot
= strrchr(orig_key
, '.');
1124 orig_name
= ldot
+ 1;
1125 orig_section
= git__strndup(orig_key
, ldot
- orig_key
);
1126 GIT_ERROR_CHECK_ALLOC(orig_section
);
1128 write_data
.buf
= &buf
;
1129 write_data
.orig_section
= orig_section
;
1130 write_data
.section
= section
;
1131 write_data
.orig_name
= orig_name
;
1132 write_data
.name
= name
;
1133 write_data
.preg
= preg
;
1134 write_data
.value
= value
;
1136 if ((error
= git_config_parse(&parser
, write_on_section
, write_on_variable
,
1137 write_on_comment
, write_on_eof
, &write_data
)) < 0)
1141 size_t len
= buf
.asize
;
1142 /* Update our copy with the modified contents */
1143 git_buf_dispose(&cfg
->locked_content
);
1144 git_buf_attach(&cfg
->locked_content
, git_buf_detach(&buf
), len
);
1146 git_filebuf_write(&file
, git_buf_cstr(&buf
), git_buf_len(&buf
));
1148 if ((error
= git_filebuf_commit(&file
)) < 0)
1151 if ((error
= config_file_refresh_from_buffer(&cfg
->parent
, buf
.ptr
, buf
.size
)) < 0)
1157 git__free(orig_section
);
1158 git_buf_dispose(&write_data
.buffered_comment
);
1159 git_buf_dispose(&buf
);
1160 git_buf_dispose(&contents
);
1161 git_filebuf_cleanup(&file
);
1162 git_config_parser_dispose(&parser
);