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
))
115 * git silently ignores configuration files that are not
116 * readable. We emulate that behavior. This is particularly
117 * important for sandboxed applications on macOS where the
118 * git configuration files may not be readable.
120 if (p_access(b
->file
.path
, R_OK
) < 0)
121 return GIT_ENOTFOUND
;
123 if (res
< 0 || (res
= config_file_read(b
->entries
, repo
, &b
->file
, level
, 0)) < 0) {
124 git_config_entries_free(b
->entries
);
131 static int config_file_is_modified(int *modified
, config_file
*file
)
133 config_file
*include
;
134 git_buf buf
= GIT_BUF_INIT
;
141 if (!git_futils_filestamp_check(&file
->stamp
, file
->path
))
144 if ((error
= git_futils_readbuffer(&buf
, file
->path
)) < 0)
147 if ((error
= git_hash_buf(&hash
, buf
.ptr
, buf
.size
)) < 0)
150 if (!git_oid_equal(&hash
, &file
->checksum
)) {
156 git_array_foreach(file
->includes
, i
, include
) {
157 if ((error
= config_file_is_modified(modified
, include
)) < 0 || *modified
)
162 git_buf_dispose(&buf
);
167 static int config_file_set_entries(git_config_backend
*cfg
, git_config_entries
*entries
)
169 config_file_backend
*b
= GIT_CONTAINER_OF(cfg
, config_file_backend
, parent
);
170 git_config_entries
*old
= NULL
;
171 config_file
*include
;
175 if (b
->parent
.readonly
) {
176 git_error_set(GIT_ERROR_CONFIG
, "this backend is read-only");
180 git_array_foreach(b
->file
.includes
, i
, include
)
181 config_file_clear(include
);
182 git_array_clear(b
->file
.includes
);
184 if ((error
= git_mutex_lock(&b
->values_mutex
)) < 0) {
185 git_error_set(GIT_ERROR_OS
, "failed to lock config backend");
190 b
->entries
= entries
;
192 git_mutex_unlock(&b
->values_mutex
);
195 git_config_entries_free(old
);
199 static int config_file_refresh_from_buffer(git_config_backend
*cfg
, const char *buf
, size_t buflen
)
201 config_file_backend
*b
= GIT_CONTAINER_OF(cfg
, config_file_backend
, parent
);
202 git_config_entries
*entries
= NULL
;
205 if ((error
= git_config_entries_new(&entries
)) < 0 ||
206 (error
= config_file_read_buffer(entries
, b
->repo
, &b
->file
,
207 b
->level
, 0, buf
, buflen
)) < 0 ||
208 (error
= config_file_set_entries(cfg
, entries
)) < 0)
213 git_config_entries_free(entries
);
217 static int config_file_refresh(git_config_backend
*cfg
)
219 config_file_backend
*b
= GIT_CONTAINER_OF(cfg
, config_file_backend
, parent
);
220 git_config_entries
*entries
= NULL
;
226 if ((error
= config_file_is_modified(&modified
, &b
->file
)) < 0 && error
!= GIT_ENOTFOUND
)
232 if ((error
= git_config_entries_new(&entries
)) < 0 ||
233 (error
= config_file_read(entries
, b
->repo
, &b
->file
, b
->level
, 0)) < 0 ||
234 (error
= config_file_set_entries(cfg
, entries
)) < 0)
239 git_config_entries_free(entries
);
241 return (error
== GIT_ENOTFOUND
) ? 0 : error
;
244 static void config_file_free(git_config_backend
*_backend
)
246 config_file_backend
*backend
= GIT_CONTAINER_OF(_backend
, config_file_backend
, parent
);
251 config_file_clear(&backend
->file
);
252 git_config_entries_free(backend
->entries
);
253 git_mutex_free(&backend
->values_mutex
);
257 static int config_file_iterator(
258 git_config_iterator
**iter
,
259 struct git_config_backend
*backend
)
261 config_file_backend
*b
= GIT_CONTAINER_OF(backend
, config_file_backend
, parent
);
262 git_config_entries
*dupped
= NULL
, *entries
= NULL
;
265 if ((error
= config_file_refresh(backend
)) < 0 ||
266 (error
= config_file_entries_take(&entries
, b
)) < 0 ||
267 (error
= git_config_entries_dup(&dupped
, entries
)) < 0 ||
268 (error
= git_config_entries_iterator_new(iter
, dupped
)) < 0)
272 /* Let iterator delete duplicated entries when it's done */
273 git_config_entries_free(entries
);
274 git_config_entries_free(dupped
);
278 static int config_file_snapshot(git_config_backend
**out
, git_config_backend
*backend
)
280 return git_config_backend_snapshot(out
, backend
);
283 static int config_file_set(git_config_backend
*cfg
, const char *name
, const char *value
)
285 config_file_backend
*b
= GIT_CONTAINER_OF(cfg
, config_file_backend
, parent
);
286 git_config_entries
*entries
;
287 git_config_entry
*existing
;
288 char *key
, *esc_value
= NULL
;
291 if ((error
= git_config__normalize_name(name
, &key
)) < 0)
294 if ((error
= config_file_entries_take(&entries
, b
)) < 0)
297 /* Check whether we'd be modifying an included or multivar key */
298 if ((error
= git_config_entries_get_unique(&existing
, entries
, key
)) < 0) {
299 if (error
!= GIT_ENOTFOUND
)
302 } else if ((!existing
->value
&& !value
) ||
303 (existing
->value
&& value
&& !strcmp(existing
->value
, value
))) {
304 /* don't update if old and new values already match */
309 /* No early returns due to sanity checks, let's write it out and refresh */
311 esc_value
= escape_value(value
);
312 GIT_ERROR_CHECK_ALLOC(esc_value
);
315 if ((error
= config_file_write(b
, name
, key
, NULL
, esc_value
)) < 0)
319 git_config_entries_free(entries
);
320 git__free(esc_value
);
325 /* release the map containing the entry as an equivalent to freeing it */
326 static void config_file_entry_free(git_config_entry
*entry
)
328 git_config_entries
*entries
= (git_config_entries
*) entry
->payload
;
329 git_config_entries_free(entries
);
333 * Internal function that actually gets the value in string form
335 static int config_file_get(git_config_backend
*cfg
, const char *key
, git_config_entry
**out
)
337 config_file_backend
*h
= GIT_CONTAINER_OF(cfg
, config_file_backend
, parent
);
338 git_config_entries
*entries
= NULL
;
339 git_config_entry
*entry
;
342 if (!h
->parent
.readonly
&& ((error
= config_file_refresh(cfg
)) < 0))
345 if ((error
= config_file_entries_take(&entries
, h
)) < 0)
348 if ((error
= (git_config_entries_get(&entry
, entries
, key
))) < 0) {
349 git_config_entries_free(entries
);
353 entry
->free
= config_file_entry_free
;
354 entry
->payload
= entries
;
360 static int config_file_set_multivar(
361 git_config_backend
*cfg
, const char *name
, const char *regexp
, const char *value
)
363 config_file_backend
*b
= GIT_CONTAINER_OF(cfg
, config_file_backend
, parent
);
370 if ((result
= git_config__normalize_name(name
, &key
)) < 0)
373 if ((result
= git_regexp_compile(&preg
, regexp
, 0)) < 0)
376 /* If we do have it, set call config_file_write() and reload */
377 if ((result
= config_file_write(b
, name
, key
, &preg
, value
)) < 0)
382 git_regexp_dispose(&preg
);
387 static int config_file_delete(git_config_backend
*cfg
, const char *name
)
389 config_file_backend
*b
= GIT_CONTAINER_OF(cfg
, config_file_backend
, parent
);
390 git_config_entries
*entries
= NULL
;
391 git_config_entry
*entry
;
395 if ((error
= git_config__normalize_name(name
, &key
)) < 0)
398 if ((error
= config_file_entries_take(&entries
, b
)) < 0)
401 /* Check whether we'd be modifying an included or multivar key */
402 if ((error
= git_config_entries_get_unique(&entry
, entries
, key
)) < 0) {
403 if (error
== GIT_ENOTFOUND
)
404 git_error_set(GIT_ERROR_CONFIG
, "could not find key '%s' to delete", name
);
408 if ((error
= config_file_write(b
, name
, entry
->name
, NULL
, NULL
)) < 0)
412 git_config_entries_free(entries
);
417 static int config_file_delete_multivar(git_config_backend
*cfg
, const char *name
, const char *regexp
)
419 config_file_backend
*b
= GIT_CONTAINER_OF(cfg
, config_file_backend
, parent
);
420 git_config_entries
*entries
= NULL
;
421 git_config_entry
*entry
= NULL
;
422 git_regexp preg
= GIT_REGEX_INIT
;
426 if ((result
= git_config__normalize_name(name
, &key
)) < 0)
429 if ((result
= config_file_entries_take(&entries
, b
)) < 0)
432 if ((result
= git_config_entries_get(&entry
, entries
, key
)) < 0) {
433 if (result
== GIT_ENOTFOUND
)
434 git_error_set(GIT_ERROR_CONFIG
, "could not find key '%s' to delete", name
);
438 if ((result
= git_regexp_compile(&preg
, regexp
, 0)) < 0)
441 if ((result
= config_file_write(b
, name
, key
, &preg
, NULL
)) < 0)
445 git_config_entries_free(entries
);
447 git_regexp_dispose(&preg
);
451 static int config_file_lock(git_config_backend
*_cfg
)
453 config_file_backend
*cfg
= GIT_CONTAINER_OF(_cfg
, config_file_backend
, parent
);
456 if ((error
= git_filebuf_open(&cfg
->locked_buf
, cfg
->file
.path
, 0, GIT_CONFIG_FILE_MODE
)) < 0)
459 error
= git_futils_readbuffer(&cfg
->locked_content
, cfg
->file
.path
);
460 if (error
< 0 && error
!= GIT_ENOTFOUND
) {
461 git_filebuf_cleanup(&cfg
->locked_buf
);
470 static int config_file_unlock(git_config_backend
*_cfg
, int success
)
472 config_file_backend
*cfg
= GIT_CONTAINER_OF(_cfg
, config_file_backend
, parent
);
476 git_filebuf_write(&cfg
->locked_buf
, cfg
->locked_content
.ptr
, cfg
->locked_content
.size
);
477 error
= git_filebuf_commit(&cfg
->locked_buf
);
480 git_filebuf_cleanup(&cfg
->locked_buf
);
481 git_buf_dispose(&cfg
->locked_content
);
487 int git_config_backend_from_file(git_config_backend
**out
, const char *path
)
489 config_file_backend
*backend
;
491 backend
= git__calloc(1, sizeof(config_file_backend
));
492 GIT_ERROR_CHECK_ALLOC(backend
);
494 backend
->parent
.version
= GIT_CONFIG_BACKEND_VERSION
;
495 git_mutex_init(&backend
->values_mutex
);
497 backend
->file
.path
= git__strdup(path
);
498 GIT_ERROR_CHECK_ALLOC(backend
->file
.path
);
499 git_array_init(backend
->file
.includes
);
501 backend
->parent
.open
= config_file_open
;
502 backend
->parent
.get
= config_file_get
;
503 backend
->parent
.set
= config_file_set
;
504 backend
->parent
.set_multivar
= config_file_set_multivar
;
505 backend
->parent
.del
= config_file_delete
;
506 backend
->parent
.del_multivar
= config_file_delete_multivar
;
507 backend
->parent
.iterator
= config_file_iterator
;
508 backend
->parent
.snapshot
= config_file_snapshot
;
509 backend
->parent
.lock
= config_file_lock
;
510 backend
->parent
.unlock
= config_file_unlock
;
511 backend
->parent
.free
= config_file_free
;
513 *out
= (git_config_backend
*)backend
;
518 static int included_path(git_buf
*out
, const char *dir
, const char *path
)
520 /* From the user's home */
521 if (path
[0] == '~' && path
[1] == '/')
522 return git_sysdir_expand_global_file(out
, &path
[1]);
524 return git_path_join_unrooted(out
, path
, dir
, NULL
);
527 /* Escape the values to write them to the file */
528 static char *escape_value(const char *ptr
)
538 return git__calloc(1, sizeof(char));
540 if (git_buf_init(&buf
, len
) < 0)
543 while (*ptr
!= '\0') {
544 if ((esc
= strchr(git_config_escaped
, *ptr
)) != NULL
) {
545 git_buf_putc(&buf
, '\\');
546 git_buf_putc(&buf
, git_config_escapes
[esc
- git_config_escaped
]);
548 git_buf_putc(&buf
, *ptr
);
553 if (git_buf_oom(&buf
))
556 return git_buf_detach(&buf
);
559 static int parse_include(config_file_parse_data
*parse_data
, const char *file
)
561 config_file
*include
;
562 git_buf path
= GIT_BUF_INIT
;
569 if ((result
= git_path_dirname_r(&path
, parse_data
->file
->path
)) < 0)
572 dir
= git_buf_detach(&path
);
573 result
= included_path(&path
, dir
, file
);
579 include
= git_array_alloc(parse_data
->file
->includes
);
580 GIT_ERROR_CHECK_ALLOC(include
);
581 memset(include
, 0, sizeof(*include
));
582 git_array_init(include
->includes
);
583 include
->path
= git_buf_detach(&path
);
585 result
= config_file_read(parse_data
->entries
, parse_data
->repo
, include
,
586 parse_data
->level
, parse_data
->depth
+1);
588 if (result
== GIT_ENOTFOUND
) {
596 static int do_match_gitdir(
598 const git_repository
*repo
,
599 const char *cfg_file
,
600 const char *condition
,
601 bool case_insensitive
)
603 git_buf pattern
= GIT_BUF_INIT
, gitdir
= GIT_BUF_INIT
;
606 if (condition
[0] == '.' && git_path_is_dirsep(condition
[1])) {
607 git_path_dirname_r(&pattern
, cfg_file
);
608 git_buf_joinpath(&pattern
, pattern
.ptr
, condition
+ 2);
609 } else if (condition
[0] == '~' && git_path_is_dirsep(condition
[1]))
610 git_sysdir_expand_global_file(&pattern
, condition
+ 1);
611 else if (!git_path_is_absolute(condition
))
612 git_buf_joinpath(&pattern
, "**", condition
);
614 git_buf_sets(&pattern
, condition
);
616 if (git_path_is_dirsep(condition
[strlen(condition
) - 1]))
617 git_buf_puts(&pattern
, "**");
619 if (git_buf_oom(&pattern
)) {
624 if ((error
= git_repository_item_path(&gitdir
, repo
, GIT_REPOSITORY_ITEM_GITDIR
)) < 0)
627 if (git_path_is_dirsep(gitdir
.ptr
[gitdir
.size
- 1]))
628 git_buf_truncate(&gitdir
, gitdir
.size
- 1);
630 *matches
= wildmatch(pattern
.ptr
, gitdir
.ptr
,
631 WM_PATHNAME
| (case_insensitive
? WM_CASEFOLD
: 0)) == WM_MATCH
;
633 git_buf_dispose(&pattern
);
634 git_buf_dispose(&gitdir
);
638 static int conditional_match_gitdir(
640 const git_repository
*repo
,
641 const char *cfg_file
,
644 return do_match_gitdir(matches
, repo
, cfg_file
, value
, false);
647 static int conditional_match_gitdir_i(
649 const git_repository
*repo
,
650 const char *cfg_file
,
653 return do_match_gitdir(matches
, repo
, cfg_file
, value
, true);
656 static int conditional_match_onbranch(
658 const git_repository
*repo
,
659 const char *cfg_file
,
660 const char *condition
)
662 git_buf reference
= GIT_BUF_INIT
, buf
= GIT_BUF_INIT
;
665 GIT_UNUSED(cfg_file
);
668 * NOTE: you cannot use `git_repository_head` here. Looking up the
669 * HEAD reference will create the ODB, which causes us to read the
670 * repo's config for keys like core.precomposeUnicode. As we're
671 * just parsing the config right now, though, this would result in
672 * an endless recursion.
675 if ((error
= git_buf_joinpath(&buf
, git_repository_path(repo
), GIT_HEAD_FILE
)) < 0 ||
676 (error
= git_futils_readbuffer(&reference
, buf
.ptr
)) < 0)
678 git_buf_rtrim(&reference
);
680 if (git__strncmp(reference
.ptr
, GIT_SYMREF
, strlen(GIT_SYMREF
)))
682 git_buf_consume(&reference
, reference
.ptr
+ strlen(GIT_SYMREF
));
684 if (git__strncmp(reference
.ptr
, GIT_REFS_HEADS_DIR
, strlen(GIT_REFS_HEADS_DIR
)))
686 git_buf_consume(&reference
, reference
.ptr
+ strlen(GIT_REFS_HEADS_DIR
));
689 * If the condition ends with a '/', then we should treat it as if
690 * it had '**' appended.
692 if ((error
= git_buf_sets(&buf
, condition
)) < 0)
694 if (git_path_is_dirsep(condition
[strlen(condition
) - 1]) &&
695 (error
= git_buf_puts(&buf
, "**")) < 0)
698 *matches
= wildmatch(buf
.ptr
, reference
.ptr
, WM_PATHNAME
) == WM_MATCH
;
700 git_buf_dispose(&reference
);
701 git_buf_dispose(&buf
);
707 static const struct {
709 int (*matches
)(int *matches
, const git_repository
*repo
, const char *cfg
, const char *value
);
711 { "gitdir:", conditional_match_gitdir
},
712 { "gitdir/i:", conditional_match_gitdir_i
},
713 { "onbranch:", conditional_match_onbranch
}
716 static int parse_conditional_include(config_file_parse_data
*parse_data
, const char *section
, const char *file
)
720 int error
= 0, matches
;
722 if (!parse_data
->repo
|| !file
)
725 condition
= git__substrdup(section
+ strlen("includeIf."),
726 strlen(section
) - strlen("includeIf.") - strlen(".path"));
728 for (i
= 0; i
< ARRAY_SIZE(conditions
); i
++) {
729 if (git__prefixcmp(condition
, conditions
[i
].prefix
))
732 if ((error
= conditions
[i
].matches(&matches
,
734 parse_data
->file
->path
,
735 condition
+ strlen(conditions
[i
].prefix
))) < 0)
739 error
= parse_include(parse_data
, file
);
744 git__free(condition
);
748 static int read_on_variable(
749 git_config_parser
*reader
,
750 const char *current_section
,
751 const char *var_name
,
752 const char *var_value
,
757 config_file_parse_data
*parse_data
= (config_file_parse_data
*)data
;
758 git_buf buf
= GIT_BUF_INIT
;
759 git_config_entry
*entry
;
765 GIT_UNUSED(line_len
);
767 if (current_section
) {
768 /* TODO: Once warnings lang, we should likely warn
769 * here. Git appears to warn in most cases if it sees
770 * un-namespaced config options.
772 git_buf_puts(&buf
, current_section
);
773 git_buf_putc(&buf
, '.');
776 for (c
= var_name
; *c
; c
++)
777 git_buf_putc(&buf
, git__tolower(*c
));
779 if (git_buf_oom(&buf
))
782 entry
= git__calloc(1, sizeof(git_config_entry
));
783 GIT_ERROR_CHECK_ALLOC(entry
);
784 entry
->name
= git_buf_detach(&buf
);
785 entry
->value
= var_value
? git__strdup(var_value
) : NULL
;
786 entry
->level
= parse_data
->level
;
787 entry
->include_depth
= parse_data
->depth
;
789 if ((result
= git_config_entries_append(parse_data
->entries
, entry
)) < 0)
794 /* Add or append the new config option */
795 if (!git__strcmp(entry
->name
, "include.path"))
796 result
= parse_include(parse_data
, entry
->value
);
797 else if (!git__prefixcmp(entry
->name
, "includeif.") &&
798 !git__suffixcmp(entry
->name
, ".path"))
799 result
= parse_conditional_include(parse_data
, entry
->name
, entry
->value
);
804 static int config_file_read_buffer(
805 git_config_entries
*entries
,
806 const git_repository
*repo
,
808 git_config_level_t level
,
813 config_file_parse_data parse_data
;
814 git_config_parser reader
;
817 if (depth
>= MAX_INCLUDE_DEPTH
) {
818 git_error_set(GIT_ERROR_CONFIG
, "maximum config include depth reached");
822 /* Initialize the reading position */
823 reader
.path
= file
->path
;
824 git_parse_ctx_init(&reader
.ctx
, buf
, buflen
);
826 /* If the file is empty, there's nothing for us to do */
827 if (!reader
.ctx
.content
|| *reader
.ctx
.content
== '\0') {
832 parse_data
.repo
= repo
;
833 parse_data
.file
= file
;
834 parse_data
.entries
= entries
;
835 parse_data
.level
= level
;
836 parse_data
.depth
= depth
;
838 error
= git_config_parse(&reader
, NULL
, read_on_variable
, NULL
, NULL
, &parse_data
);
844 static int config_file_read(
845 git_config_entries
*entries
,
846 const git_repository
*repo
,
848 git_config_level_t level
,
851 git_buf contents
= GIT_BUF_INIT
;
855 if (p_stat(file
->path
, &st
) < 0) {
856 error
= git_path_set_error(errno
, file
->path
, "stat");
860 if ((error
= git_futils_readbuffer(&contents
, file
->path
)) < 0)
863 git_futils_filestamp_set_from_stat(&file
->stamp
, &st
);
864 if ((error
= git_hash_buf(&file
->checksum
, contents
.ptr
, contents
.size
)) < 0)
867 if ((error
= config_file_read_buffer(entries
, repo
, file
, level
, depth
,
868 contents
.ptr
, contents
.size
)) < 0)
872 git_buf_dispose(&contents
);
876 static int write_section(git_buf
*fbuf
, const char *key
)
880 git_buf buf
= GIT_BUF_INIT
;
882 /* All of this just for [section "subsection"] */
883 dot
= strchr(key
, '.');
884 git_buf_putc(&buf
, '[');
886 git_buf_puts(&buf
, key
);
889 git_buf_put(&buf
, key
, dot
- key
);
890 escaped
= escape_value(dot
+ 1);
891 GIT_ERROR_CHECK_ALLOC(escaped
);
892 git_buf_printf(&buf
, " \"%s\"", escaped
);
895 git_buf_puts(&buf
, "]\n");
897 if (git_buf_oom(&buf
))
900 result
= git_buf_put(fbuf
, git_buf_cstr(&buf
), buf
.size
);
901 git_buf_dispose(&buf
);
906 static const char *quotes_for_value(const char *value
)
910 if (value
[0] == ' ' || value
[0] == '\0')
913 for (ptr
= value
; *ptr
; ++ptr
) {
914 if (*ptr
== ';' || *ptr
== '#')
926 git_buf buffered_comment
;
927 unsigned int in_section
: 1,
929 const char *orig_section
;
931 const char *orig_name
;
933 const git_regexp
*preg
;
937 static int write_line_to(git_buf
*buf
, const char *line
, size_t line_len
)
939 int result
= git_buf_put(buf
, line
, line_len
);
941 if (!result
&& line_len
&& line
[line_len
-1] != '\n')
942 result
= git_buf_printf(buf
, "\n");
947 static int write_line(struct write_data
*write_data
, const char *line
, size_t line_len
)
949 return write_line_to(write_data
->buf
, line
, line_len
);
952 static int write_value(struct write_data
*write_data
)
957 q
= quotes_for_value(write_data
->value
);
958 result
= git_buf_printf(write_data
->buf
,
959 "\t%s = %s%s%s\n", write_data
->orig_name
, q
, write_data
->value
, q
);
961 /* If we are updating a single name/value, we're done. Setting `value`
962 * to `NULL` will prevent us from trying to write it again later (in
963 * `write_on_section`) if we see the same section repeated.
965 if (!write_data
->preg
)
966 write_data
->value
= NULL
;
971 static int write_on_section(
972 git_config_parser
*reader
,
973 const char *current_section
,
978 struct write_data
*write_data
= (struct write_data
*)data
;
983 /* If we were previously in the correct section (but aren't anymore)
984 * and haven't written our value (for a simple name/value set, not
985 * a multivar), then append it to the end of the section before writing
988 if (write_data
->in_section
&& !write_data
->preg
&& write_data
->value
)
989 result
= write_value(write_data
);
991 write_data
->in_section
= strcmp(current_section
, write_data
->section
) == 0;
994 * If there were comments just before this section, dump them as well.
997 result
= git_buf_put(write_data
->buf
, write_data
->buffered_comment
.ptr
, write_data
->buffered_comment
.size
);
998 git_buf_clear(&write_data
->buffered_comment
);
1002 result
= write_line(write_data
, line
, line_len
);
1007 static int write_on_variable(
1008 git_config_parser
*reader
,
1009 const char *current_section
,
1010 const char *var_name
,
1011 const char *var_value
,
1016 struct write_data
*write_data
= (struct write_data
*)data
;
1017 bool has_matched
= false;
1021 GIT_UNUSED(current_section
);
1024 * If there were comments just before this variable, let's dump them as well.
1026 if ((error
= git_buf_put(write_data
->buf
, write_data
->buffered_comment
.ptr
, write_data
->buffered_comment
.size
)) < 0)
1029 git_buf_clear(&write_data
->buffered_comment
);
1031 /* See if we are to update this name/value pair; first examine name */
1032 if (write_data
->in_section
&&
1033 strcasecmp(write_data
->name
, var_name
) == 0)
1036 /* If we have a regex to match the value, see if it matches */
1037 if (has_matched
&& write_data
->preg
!= NULL
)
1038 has_matched
= (git_regexp_match(write_data
->preg
, var_value
) == 0);
1040 /* If this isn't the name/value we're looking for, simply dump the
1041 * existing data back out and continue on.
1044 return write_line(write_data
, line
, line_len
);
1046 write_data
->preg_replaced
= 1;
1048 /* If value is NULL, we are deleting this value; write nothing. */
1049 if (!write_data
->value
)
1052 return write_value(write_data
);
1055 static int write_on_comment(git_config_parser
*reader
, const char *line
, size_t line_len
, void *data
)
1057 struct write_data
*write_data
;
1061 write_data
= (struct write_data
*)data
;
1062 return write_line_to(&write_data
->buffered_comment
, line
, line_len
);
1065 static int write_on_eof(
1066 git_config_parser
*reader
, const char *current_section
, void *data
)
1068 struct write_data
*write_data
= (struct write_data
*)data
;
1074 * If we've buffered comments when reaching EOF, make sure to dump them.
1076 if ((result
= git_buf_put(write_data
->buf
, write_data
->buffered_comment
.ptr
, write_data
->buffered_comment
.size
)) < 0)
1079 /* If we are at the EOF and have not written our value (again, for a
1080 * simple name/value set, not a multivar) then we have never seen the
1081 * section in question and should create a new section and write the
1084 if ((!write_data
->preg
|| !write_data
->preg_replaced
) && write_data
->value
) {
1085 /* write the section header unless we're already in it */
1086 if (!current_section
|| strcmp(current_section
, write_data
->section
))
1087 result
= write_section(write_data
->buf
, write_data
->orig_section
);
1090 result
= write_value(write_data
);
1097 * This is pretty much the parsing, except we write out anything we don't have
1099 static int config_file_write(config_file_backend
*cfg
, const char *orig_key
, const char *key
, const git_regexp
*preg
, const char* value
)
1102 char *orig_section
= NULL
, *section
= NULL
, *orig_name
, *name
, *ldot
;
1103 git_buf buf
= GIT_BUF_INIT
, contents
= GIT_BUF_INIT
;
1104 git_config_parser parser
= GIT_CONFIG_PARSER_INIT
;
1105 git_filebuf file
= GIT_FILEBUF_INIT
;
1106 struct write_data write_data
;
1109 memset(&write_data
, 0, sizeof(write_data
));
1112 error
= git_buf_puts(&contents
, git_buf_cstr(&cfg
->locked_content
) == NULL
? "" : git_buf_cstr(&cfg
->locked_content
));
1114 if ((error
= git_filebuf_open(&file
, cfg
->file
.path
, GIT_FILEBUF_HASH_CONTENTS
,
1115 GIT_CONFIG_FILE_MODE
)) < 0)
1118 /* We need to read in our own config file */
1119 error
= git_futils_readbuffer(&contents
, cfg
->file
.path
);
1121 if (error
< 0 && error
!= GIT_ENOTFOUND
)
1124 if ((git_config_parser_init(&parser
, cfg
->file
.path
, contents
.ptr
, contents
.size
)) < 0)
1127 ldot
= strrchr(key
, '.');
1129 section
= git__strndup(key
, ldot
- key
);
1130 GIT_ERROR_CHECK_ALLOC(section
);
1132 ldot
= strrchr(orig_key
, '.');
1133 orig_name
= ldot
+ 1;
1134 orig_section
= git__strndup(orig_key
, ldot
- orig_key
);
1135 GIT_ERROR_CHECK_ALLOC(orig_section
);
1137 write_data
.buf
= &buf
;
1138 write_data
.orig_section
= orig_section
;
1139 write_data
.section
= section
;
1140 write_data
.orig_name
= orig_name
;
1141 write_data
.name
= name
;
1142 write_data
.preg
= preg
;
1143 write_data
.value
= value
;
1145 if ((error
= git_config_parse(&parser
, write_on_section
, write_on_variable
,
1146 write_on_comment
, write_on_eof
, &write_data
)) < 0)
1150 size_t len
= buf
.asize
;
1151 /* Update our copy with the modified contents */
1152 git_buf_dispose(&cfg
->locked_content
);
1153 git_buf_attach(&cfg
->locked_content
, git_buf_detach(&buf
), len
);
1155 git_filebuf_write(&file
, git_buf_cstr(&buf
), git_buf_len(&buf
));
1157 if ((error
= git_filebuf_commit(&file
)) < 0)
1160 if ((error
= config_file_refresh_from_buffer(&cfg
->parent
, buf
.ptr
, buf
.size
)) < 0)
1166 git__free(orig_section
);
1167 git_buf_dispose(&write_data
.buffered_comment
);
1168 git_buf_dispose(&buf
);
1169 git_buf_dispose(&contents
);
1170 git_filebuf_cleanup(&file
);
1171 git_config_parser_dispose(&parser
);