]>
git.proxmox.com Git - libgit2.git/blob - src/config_file.c
2 * Copyright (C) 2009-2012 the libgit2 contributors
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.
13 #include "git2/config.h"
14 #include "git2/types.h"
18 #include <sys/types.h>
23 typedef struct cvar_t
{
25 char *key
; /* TODO: we might be able to get rid of this */
34 #define CVAR_LIST_HEAD(list) ((list)->head)
36 #define CVAR_LIST_TAIL(list) ((list)->tail)
38 #define CVAR_LIST_NEXT(var) ((var)->next)
40 #define CVAR_LIST_EMPTY(list) ((list)->head == NULL)
42 #define CVAR_LIST_APPEND(list, var) do {\
43 if (CVAR_LIST_EMPTY(list)) {\
44 CVAR_LIST_HEAD(list) = CVAR_LIST_TAIL(list) = var;\
46 CVAR_LIST_NEXT(CVAR_LIST_TAIL(list)) = var;\
47 CVAR_LIST_TAIL(list) = var;\
51 #define CVAR_LIST_REMOVE_HEAD(list) do {\
52 CVAR_LIST_HEAD(list) = CVAR_LIST_NEXT(CVAR_LIST_HEAD(list));\
55 #define CVAR_LIST_REMOVE_AFTER(var) do {\
56 CVAR_LIST_NEXT(var) = CVAR_LIST_NEXT(CVAR_LIST_NEXT(var));\
59 #define CVAR_LIST_FOREACH(list, iter)\
60 for ((iter) = CVAR_LIST_HEAD(list);\
62 (iter) = CVAR_LIST_NEXT(iter))
65 * Inspired by the FreeBSD functions
67 #define CVAR_LIST_FOREACH_SAFE(start, iter, tmp)\
68 for ((iter) = CVAR_LIST_HEAD(vars);\
69 (iter) && (((tmp) = CVAR_LIST_NEXT(iter) || 1));\
73 git_config_file parent
;
87 static int config_parse(diskfile_backend
*cfg_file
);
88 static int parse_variable(diskfile_backend
*cfg
, char **var_name
, char **var_value
);
89 static int config_write(diskfile_backend
*cfg
, const char *key
, const regex_t
*preg
, const char *value
);
90 static char *escape_value(const char *ptr
);
92 static void set_parse_error(diskfile_backend
*backend
, int col
, const char *error_str
)
94 giterr_set(GITERR_CONFIG
, "Failed to parse config file: %s (in %s:%d, column %d)",
95 error_str
, backend
->file_path
, backend
->reader
.line_number
, col
);
98 static void cvar_free(cvar_t
*var
)
104 git__free(var
->value
);
108 /* Take something the user gave us and make it nice for our hash function */
109 static int normalize_name(const char *in
, char **out
)
111 char *name
, *fdot
, *ldot
;
115 name
= git__strdup(in
);
116 GITERR_CHECK_ALLOC(name
);
118 fdot
= strchr(name
, '.');
119 ldot
= strrchr(name
, '.');
121 if (fdot
== NULL
|| ldot
== NULL
) {
123 giterr_set(GITERR_CONFIG
,
124 "Invalid variable name: '%s'", in
);
128 /* Downcase up to the first dot and after the last one */
129 git__strntolower(name
, fdot
- name
);
130 git__strtolower(ldot
);
136 static void free_vars(git_strmap
*values
)
143 git_strmap_foreach_value(values
, var
,
144 while (var
!= NULL
) {
145 cvar_t
*next
= CVAR_LIST_NEXT(var
);
150 git_strmap_free(values
);
153 static int config_open(git_config_file
*cfg
)
156 diskfile_backend
*b
= (diskfile_backend
*)cfg
;
158 b
->values
= git_strmap_alloc();
159 GITERR_CHECK_ALLOC(b
->values
);
161 git_buf_init(&b
->reader
.buffer
, 0);
162 res
= git_futils_readbuffer(&b
->reader
.buffer
, b
->file_path
);
164 /* It's fine if the file doesn't exist */
165 if (res
== GIT_ENOTFOUND
)
168 if (res
< 0 || config_parse(b
) < 0) {
169 free_vars(b
->values
);
171 git_buf_free(&b
->reader
.buffer
);
175 git_buf_free(&b
->reader
.buffer
);
179 static void backend_free(git_config_file
*_backend
)
181 diskfile_backend
*backend
= (diskfile_backend
*)_backend
;
186 git__free(backend
->file_path
);
187 free_vars(backend
->values
);
191 static int file_foreach(
192 git_config_file
*backend
,
194 int (*fn
)(const char *, const char *, void *),
197 diskfile_backend
*b
= (diskfile_backend
*)backend
;
198 cvar_t
*var
, *next_var
;
206 if (regexp
!= NULL
) {
207 if ((result
= regcomp(®ex
, regexp
, REG_EXTENDED
)) < 0) {
208 giterr_set_regex(®ex
, result
);
214 git_strmap_foreach(b
->values
, key
, var
,
215 for (; var
!= NULL
; var
= next_var
) {
216 next_var
= CVAR_LIST_NEXT(var
);
218 /* skip non-matching keys if regexp was provided */
219 if (regexp
&& regexec(®ex
, key
, 0, NULL
, 0) != 0)
222 /* abort iterator on non-zero return value */
223 if (fn(key
, var
->value
, data
)) {
238 static int config_set(git_config_file
*cfg
, const char *name
, const char *value
)
240 cvar_t
*var
= NULL
, *old_var
;
241 diskfile_backend
*b
= (diskfile_backend
*)cfg
;
242 char *key
, *esc_value
= NULL
;
246 if (normalize_name(name
, &key
) < 0)
250 * Try to find it in the existing values and update it if it
251 * only has one value.
253 pos
= git_strmap_lookup_index(b
->values
, key
);
254 if (git_strmap_valid_index(b
->values
, pos
)) {
255 cvar_t
*existing
= git_strmap_value_at(b
->values
, pos
);
260 if (existing
->next
!= NULL
) {
261 giterr_set(GITERR_CONFIG
, "Multivar incompatible with simple set");
265 /* don't update if old and new values already match */
266 if ((!existing
->value
&& !value
) ||
267 (existing
->value
&& value
&& !strcmp(existing
->value
, value
)))
271 tmp
= git__strdup(value
);
272 GITERR_CHECK_ALLOC(tmp
);
273 esc_value
= escape_value(value
);
274 GITERR_CHECK_ALLOC(esc_value
);
277 git__free(existing
->value
);
278 existing
->value
= tmp
;
280 ret
= config_write(b
, existing
->key
, NULL
, esc_value
);
282 git__free(esc_value
);
286 var
= git__malloc(sizeof(cvar_t
));
287 GITERR_CHECK_ALLOC(var
);
289 memset(var
, 0x0, sizeof(cvar_t
));
295 var
->value
= git__strdup(value
);
296 GITERR_CHECK_ALLOC(var
->value
);
297 esc_value
= escape_value(value
);
298 GITERR_CHECK_ALLOC(esc_value
);
301 if (config_write(b
, key
, NULL
, esc_value
) < 0) {
302 git__free(esc_value
);
307 git__free(esc_value
);
308 git_strmap_insert2(b
->values
, key
, var
, old_var
, rval
);
318 * Internal function that actually gets the value in string form
320 static int config_get(git_config_file
*cfg
, const char *name
, const char **out
)
322 diskfile_backend
*b
= (diskfile_backend
*)cfg
;
326 if (normalize_name(name
, &key
) < 0)
329 pos
= git_strmap_lookup_index(b
->values
, key
);
332 /* no error message; the config system will write one */
333 if (!git_strmap_valid_index(b
->values
, pos
))
334 return GIT_ENOTFOUND
;
336 *out
= ((cvar_t
*)git_strmap_value_at(b
->values
, pos
))->value
;
341 static int config_get_multivar(
342 git_config_file
*cfg
,
344 const char *regex_str
,
345 int (*fn
)(const char *, void *),
349 diskfile_backend
*b
= (diskfile_backend
*)cfg
;
353 if (normalize_name(name
, &key
) < 0)
356 pos
= git_strmap_lookup_index(b
->values
, key
);
359 if (!git_strmap_valid_index(b
->values
, pos
))
360 return GIT_ENOTFOUND
;
362 var
= git_strmap_value_at(b
->values
, pos
);
364 if (regex_str
!= NULL
) {
368 /* regex matching; build the regex */
369 result
= regcomp(®ex
, regex_str
, REG_EXTENDED
);
371 giterr_set_regex(®ex
, result
);
376 /* and throw the callback only on the variables that
379 if (regexec(®ex
, var
->value
, 0, NULL
, 0) == 0) {
380 /* early termination by the user is not an error;
381 * just break and return successfully */
382 if (fn(var
->value
, data
) < 0)
387 } while (var
!= NULL
);
390 /* no regex; go through all the variables */
392 /* early termination by the user is not an error;
393 * just break and return successfully */
394 if (fn(var
->value
, data
) < 0)
398 } while (var
!= NULL
);
404 static int config_set_multivar(
405 git_config_file
*cfg
, const char *name
, const char *regexp
, const char *value
)
408 cvar_t
*var
, *newvar
;
409 diskfile_backend
*b
= (diskfile_backend
*)cfg
;
417 if (normalize_name(name
, &key
) < 0)
420 pos
= git_strmap_lookup_index(b
->values
, key
);
421 if (!git_strmap_valid_index(b
->values
, pos
)) {
423 return GIT_ENOTFOUND
;
426 var
= git_strmap_value_at(b
->values
, pos
);
428 result
= regcomp(&preg
, regexp
, REG_EXTENDED
);
431 giterr_set_regex(&preg
, result
);
437 if (regexec(&preg
, var
->value
, 0, NULL
, 0) == 0) {
438 char *tmp
= git__strdup(value
);
439 GITERR_CHECK_ALLOC(tmp
);
441 git__free(var
->value
);
446 if (var
->next
== NULL
)
452 /* If we've reached the end of the variables and we haven't found it yet, we need to append it */
454 newvar
= git__malloc(sizeof(cvar_t
));
455 GITERR_CHECK_ALLOC(newvar
);
457 memset(newvar
, 0x0, sizeof(cvar_t
));
459 newvar
->key
= git__strdup(var
->key
);
460 GITERR_CHECK_ALLOC(newvar
->key
);
462 newvar
->value
= git__strdup(value
);
463 GITERR_CHECK_ALLOC(newvar
->value
);
468 result
= config_write(b
, key
, &preg
, value
);
476 static int config_delete(git_config_file
*cfg
, const char *name
)
479 diskfile_backend
*b
= (diskfile_backend
*)cfg
;
484 if (normalize_name(name
, &key
) < 0)
487 pos
= git_strmap_lookup_index(b
->values
, key
);
490 if (!git_strmap_valid_index(b
->values
, pos
)) {
491 giterr_set(GITERR_CONFIG
, "Could not find key '%s' to delete", name
);
492 return GIT_ENOTFOUND
;
495 var
= git_strmap_value_at(b
->values
, pos
);
497 if (var
->next
!= NULL
) {
498 giterr_set(GITERR_CONFIG
, "Cannot delete multivar with a single delete");
502 git_strmap_delete_at(b
->values
, pos
);
504 result
= config_write(b
, var
->key
, NULL
, NULL
);
510 int git_config_file__ondisk(git_config_file
**out
, const char *path
)
512 diskfile_backend
*backend
;
514 backend
= git__malloc(sizeof(diskfile_backend
));
515 GITERR_CHECK_ALLOC(backend
);
517 memset(backend
, 0x0, sizeof(diskfile_backend
));
519 backend
->file_path
= git__strdup(path
);
520 GITERR_CHECK_ALLOC(backend
->file_path
);
522 backend
->parent
.open
= config_open
;
523 backend
->parent
.get
= config_get
;
524 backend
->parent
.get_multivar
= config_get_multivar
;
525 backend
->parent
.set
= config_set
;
526 backend
->parent
.set_multivar
= config_set_multivar
;
527 backend
->parent
.del
= config_delete
;
528 backend
->parent
.foreach
= file_foreach
;
529 backend
->parent
.free
= backend_free
;
531 *out
= (git_config_file
*)backend
;
536 static int cfg_getchar_raw(diskfile_backend
*cfg
)
540 c
= *cfg
->reader
.read_ptr
++;
543 Win 32 line breaks: if we find a \r\n sequence,
544 return only the \n as a newline
546 if (c
== '\r' && *cfg
->reader
.read_ptr
== '\n') {
547 cfg
->reader
.read_ptr
++;
552 cfg
->reader
.line_number
++;
562 #define SKIP_WHITESPACE (1 << 1)
563 #define SKIP_COMMENTS (1 << 2)
565 static int cfg_getchar(diskfile_backend
*cfg_file
, int flags
)
567 const int skip_whitespace
= (flags
& SKIP_WHITESPACE
);
568 const int skip_comments
= (flags
& SKIP_COMMENTS
);
571 assert(cfg_file
->reader
.read_ptr
);
573 do c
= cfg_getchar_raw(cfg_file
);
574 while (skip_whitespace
&& git__isspace(c
) &&
575 !cfg_file
->reader
.eof
);
577 if (skip_comments
&& (c
== '#' || c
== ';')) {
578 do c
= cfg_getchar_raw(cfg_file
);
586 * Read the next char, but don't move the reading pointer.
588 static int cfg_peek(diskfile_backend
*cfg
, int flags
)
591 int old_lineno
, old_eof
;
594 assert(cfg
->reader
.read_ptr
);
596 old_read_ptr
= cfg
->reader
.read_ptr
;
597 old_lineno
= cfg
->reader
.line_number
;
598 old_eof
= cfg
->reader
.eof
;
600 ret
= cfg_getchar(cfg
, flags
);
602 cfg
->reader
.read_ptr
= old_read_ptr
;
603 cfg
->reader
.line_number
= old_lineno
;
604 cfg
->reader
.eof
= old_eof
;
610 * Read and consume a line, returning it in newly-allocated memory.
612 static char *cfg_readline(diskfile_backend
*cfg
, bool skip_whitespace
)
615 char *line_src
, *line_end
;
618 line_src
= cfg
->reader
.read_ptr
;
620 if (skip_whitespace
) {
621 /* Skip empty empty lines */
622 while (git__isspace(*line_src
))
626 line_end
= strchr(line_src
, '\n');
628 /* no newline at EOF */
629 if (line_end
== NULL
)
630 line_end
= strchr(line_src
, 0);
632 line_len
= line_end
- line_src
;
634 line
= git__malloc(line_len
+ 1);
638 memcpy(line
, line_src
, line_len
);
640 do line
[line_len
] = '\0';
641 while (line_len
-- > 0 && git__isspace(line
[line_len
]));
643 if (*line_end
== '\n')
646 if (*line_end
== '\0')
649 cfg
->reader
.line_number
++;
650 cfg
->reader
.read_ptr
= line_end
;
656 * Consume a line, without storing it anywhere
658 static void cfg_consume_line(diskfile_backend
*cfg
)
660 char *line_start
, *line_end
;
662 line_start
= cfg
->reader
.read_ptr
;
663 line_end
= strchr(line_start
, '\n');
664 /* No newline at EOF */
665 if(line_end
== NULL
){
666 line_end
= strchr(line_start
, '\0');
669 if (*line_end
== '\n')
672 if (*line_end
== '\0')
675 cfg
->reader
.line_number
++;
676 cfg
->reader
.read_ptr
= line_end
;
679 GIT_INLINE(int) config_keychar(int c
)
681 return isalnum(c
) || c
== '-';
684 static int parse_section_header_ext(diskfile_backend
*cfg
, const char *line
, const char *base_name
, char **section_name
)
687 char *first_quote
, *last_quote
;
688 git_buf buf
= GIT_BUF_INIT
;
691 * base_name is what came before the space. We should be at the
692 * first quotation mark, except for now, line isn't being kept in
693 * sync so we only really use it to calculate the length.
696 first_quote
= strchr(line
, '"');
697 last_quote
= strrchr(line
, '"');
699 if (last_quote
- first_quote
== 0) {
700 set_parse_error(cfg
, 0, "Missing closing quotation mark in section header");
704 git_buf_grow(&buf
, strlen(base_name
) + last_quote
- first_quote
+ 2);
705 git_buf_printf(&buf
, "%s.", base_name
);
714 * At the end of each iteration, whatever is stored in c will be
715 * added to the string. In case of error, jump to out
718 if (quote_marks
== 2) {
719 set_parse_error(cfg
, rpos
, "Unexpected text after closing quotes");
738 set_parse_error(cfg
, rpos
, "Unsupported escape sequence");
747 git_buf_putc(&buf
, c
);
748 } while ((c
= line
[rpos
++]) != ']');
750 *section_name
= git_buf_detach(&buf
);
754 static int parse_section_header(diskfile_backend
*cfg
, char **section_out
)
756 char *name
, *name_end
;
757 int name_length
, c
, pos
;
761 line
= cfg_readline(cfg
, true);
765 /* find the end of the variable's name */
766 name_end
= strchr(line
, ']');
767 if (name_end
== NULL
) {
769 set_parse_error(cfg
, 0, "Missing ']' in section header");
773 name
= (char *)git__malloc((size_t)(name_end
- line
) + 1);
774 GITERR_CHECK_ALLOC(name
);
779 /* Make sure we were given a section header */
786 if (git__isspace(c
)){
787 name
[name_length
] = '\0';
788 result
= parse_section_header_ext(cfg
, line
, name
, section_out
);
794 if (!config_keychar(c
) && c
!= '.') {
795 set_parse_error(cfg
, pos
, "Unexpected character in header");
799 name
[name_length
++] = (char) tolower(c
);
801 } while ((c
= line
[pos
++]) != ']');
803 if (line
[pos
- 1] != ']') {
804 set_parse_error(cfg
, pos
, "Unexpected end of file");
810 name
[name_length
] = 0;
821 static int skip_bom(diskfile_backend
*cfg
)
823 static const char utf8_bom
[] = { '\xef', '\xbb', '\xbf' };
825 if (cfg
->reader
.buffer
.size
< sizeof(utf8_bom
))
828 if (memcmp(cfg
->reader
.read_ptr
, utf8_bom
, sizeof(utf8_bom
)) == 0)
829 cfg
->reader
.read_ptr
+= sizeof(utf8_bom
);
831 /* TODO: the reference implementation does pretty stupid
841 integer = digit { digit }
842 alphabet = "a".."z" + "A" .. "Z"
844 section_char = alphabet | "." | "-"
845 extension_char = (* any character except newline *)
846 any_char = (* any character *)
847 variable_char = "alphabet" | "-"
853 section = header { definition }
855 header = "[" section [subsection | subsection_ext] "]"
857 subsection = "." section
858 subsection_ext = "\"" extension "\""
860 section = section_char { section_char }
861 extension = extension_char { extension_char }
863 definition = variable_name ["=" variable_value] "\n"
865 variable_name = variable_char { variable_char }
866 variable_value = string | boolean | integer
868 string = quoted_string | plain_string
869 quoted_string = "\"" plain_string "\""
870 plain_string = { any_char }
872 boolean = boolean_true | boolean_false
873 boolean_true = "yes" | "1" | "true" | "on"
874 boolean_false = "no" | "0" | "false" | "off"
877 static int strip_comments(char *line
, int in_quotes
)
879 int quote_count
= in_quotes
;
882 for (ptr
= line
; *ptr
; ++ptr
) {
883 if (ptr
[0] == '"' && ptr
> line
&& ptr
[-1] != '\\')
886 if ((ptr
[0] == ';' || ptr
[0] == '#') && (quote_count
% 2) == 0) {
892 /* skip any space at the end */
893 if (git__isspace(ptr
[-1])) {
901 static int config_parse(diskfile_backend
*cfg_file
)
904 char *current_section
= NULL
;
907 cvar_t
*var
, *existing
;
908 git_buf buf
= GIT_BUF_INIT
;
912 /* Initialize the reading position */
913 cfg_file
->reader
.read_ptr
= cfg_file
->reader
.buffer
.ptr
;
914 cfg_file
->reader
.eof
= 0;
916 /* If the file is empty, there's nothing for us to do */
917 if (*cfg_file
->reader
.read_ptr
== '\0')
922 while (result
== 0 && !cfg_file
->reader
.eof
) {
924 c
= cfg_peek(cfg_file
, SKIP_WHITESPACE
);
927 case '\n': /* EOF when peeking, set EOF in the reader to exit the loop */
928 cfg_file
->reader
.eof
= 1;
931 case '[': /* section header, new section begins */
932 git__free(current_section
);
933 current_section
= NULL
;
934 result
= parse_section_header(cfg_file
, ¤t_section
);
939 cfg_consume_line(cfg_file
);
942 default: /* assume variable declaration */
943 result
= parse_variable(cfg_file
, &var_name
, &var_value
);
947 var
= git__malloc(sizeof(cvar_t
));
948 GITERR_CHECK_ALLOC(var
);
950 memset(var
, 0x0, sizeof(cvar_t
));
952 git__strtolower(var_name
);
953 git_buf_printf(&buf
, "%s.%s", current_section
, var_name
);
956 if (git_buf_oom(&buf
))
959 var
->key
= git_buf_detach(&buf
);
960 var
->value
= var_value
;
962 /* Add or append the new config option */
963 pos
= git_strmap_lookup_index(cfg_file
->values
, var
->key
);
964 if (!git_strmap_valid_index(cfg_file
->values
, pos
)) {
965 git_strmap_insert(cfg_file
->values
, var
->key
, var
, result
);
970 existing
= git_strmap_value_at(cfg_file
->values
, pos
);
971 while (existing
->next
!= NULL
) {
972 existing
= existing
->next
;
974 existing
->next
= var
;
981 git__free(current_section
);
985 static int write_section(git_filebuf
*file
, const char *key
)
989 git_buf buf
= GIT_BUF_INIT
;
991 /* All of this just for [section "subsection"] */
992 dot
= strchr(key
, '.');
993 git_buf_putc(&buf
, '[');
995 git_buf_puts(&buf
, key
);
998 git_buf_put(&buf
, key
, dot
- key
);
999 escaped
= escape_value(dot
+ 1);
1000 GITERR_CHECK_ALLOC(escaped
);
1001 git_buf_printf(&buf
, " \"%s\"", escaped
);
1004 git_buf_puts(&buf
, "]\n");
1006 if (git_buf_oom(&buf
))
1009 result
= git_filebuf_write(file
, git_buf_cstr(&buf
), buf
.size
);
1016 * This is pretty much the parsing, except we write out anything we don't have
1018 static int config_write(diskfile_backend
*cfg
, const char *key
, const regex_t
*preg
, const char* value
)
1021 int section_matches
= 0, last_section_matched
= 0, preg_replaced
= 0, write_trailer
= 0;
1022 const char *pre_end
= NULL
, *post_start
= NULL
, *data_start
;
1023 char *current_section
= NULL
, *section
, *name
, *ldot
;
1024 git_filebuf file
= GIT_FILEBUF_INIT
;
1026 /* We need to read in our own config file */
1027 result
= git_futils_readbuffer(&cfg
->reader
.buffer
, cfg
->file_path
);
1029 /* Initialise the reading position */
1030 if (result
== GIT_ENOTFOUND
) {
1031 cfg
->reader
.read_ptr
= NULL
;
1032 cfg
->reader
.eof
= 1;
1034 git_buf_clear(&cfg
->reader
.buffer
);
1035 } else if (result
== 0) {
1036 cfg
->reader
.read_ptr
= cfg
->reader
.buffer
.ptr
;
1037 cfg
->reader
.eof
= 0;
1038 data_start
= cfg
->reader
.read_ptr
;
1040 return -1; /* OS error when reading the file */
1044 if (git_filebuf_open(&file
, cfg
->file_path
, 0) < 0)
1048 ldot
= strrchr(key
, '.');
1050 section
= git__strndup(key
, ldot
- key
);
1052 while (!cfg
->reader
.eof
) {
1053 c
= cfg_peek(cfg
, SKIP_WHITESPACE
);
1055 if (c
== '\0') { /* We've arrived at the end of the file */
1058 } else if (c
== '[') { /* section header, new section begins */
1060 * We set both positions to the current one in case we
1061 * need to add a variable to the end of a section. In that
1062 * case, we want both variables to point just before the
1063 * new section. If we actually want to replace it, the
1064 * default case will take care of updating them.
1066 pre_end
= post_start
= cfg
->reader
.read_ptr
;
1068 git__free(current_section
);
1069 current_section
= NULL
;
1070 if (parse_section_header(cfg
, ¤t_section
) < 0)
1073 /* Keep track of when it stops matching */
1074 last_section_matched
= section_matches
;
1075 section_matches
= !strcmp(current_section
, section
);
1078 else if (c
== ';' || c
== '#') {
1079 cfg_consume_line(cfg
);
1084 * If the section doesn't match, but the last section did,
1085 * it means we need to add a variable (so skip the line
1086 * otherwise). If both the section and name match, we need
1087 * to overwrite the variable (so skip the line
1088 * otherwise). pre_end needs to be updated each time so we
1089 * don't loose that information, but we only need to
1090 * update post_start if we're going to use it in this
1093 if (!section_matches
) {
1094 if (!last_section_matched
) {
1095 cfg_consume_line(cfg
);
1099 int has_matched
= 0;
1100 char *var_name
, *var_value
;
1102 pre_end
= cfg
->reader
.read_ptr
;
1103 if (parse_variable(cfg
, &var_name
, &var_value
) < 0)
1106 /* First try to match the name of the variable */
1107 if (strcasecmp(name
, var_name
) == 0)
1110 /* If the name matches, and we have a regex to match the
1111 * value, try to match it */
1112 if (has_matched
&& preg
!= NULL
)
1113 has_matched
= (regexec(preg
, var_value
, 0, NULL
, 0) == 0);
1115 git__free(var_name
);
1116 git__free(var_value
);
1118 /* if there is no match, keep going */
1122 post_start
= cfg
->reader
.read_ptr
;
1125 /* We've found the variable we wanted to change, so
1126 * write anything up to it */
1127 git_filebuf_write(&file
, data_start
, pre_end
- data_start
);
1130 /* Then replace the variable. If the value is NULL, it
1131 * means we want to delete it, so don't write anything. */
1132 if (value
!= NULL
) {
1133 git_filebuf_printf(&file
, "\t%s = %s\n", name
, value
);
1136 /* multiline variable? we need to keep reading lines to match */
1138 data_start
= post_start
;
1143 break; /* break from the loop */
1148 * Being here can mean that
1150 * 1) our section is the last one in the file and we're
1153 * 2) we didn't find a section for us so we need to create it
1156 * 3) we're setting a multivar with a regex, which means we
1157 * continue to search for matching values
1159 * In the last case, if we've already replaced a value, we
1160 * want to write the rest of the file. Otherwise we need to write
1161 * out the whole file and then the new variable.
1163 if (write_trailer
) {
1164 /* Write out rest of the file */
1165 git_filebuf_write(&file
, post_start
, cfg
->reader
.buffer
.size
- (post_start
- data_start
));
1167 if (preg_replaced
) {
1168 git_filebuf_printf(&file
, "\n%s", data_start
);
1170 git_filebuf_write(&file
, cfg
->reader
.buffer
.ptr
, cfg
->reader
.buffer
.size
);
1172 /* And now if we just need to add a variable */
1173 if (!section_matches
&& write_section(&file
, section
) < 0)
1176 /* Sanity check: if we are here, and value is NULL, that means that somebody
1177 * touched the config file after our intial read. We should probably assert()
1178 * this, but instead we'll handle it gracefully with an error. */
1179 if (value
== NULL
) {
1180 giterr_set(GITERR_CONFIG
,
1181 "Race condition when writing a config file (a cvar has been removed)");
1185 git_filebuf_printf(&file
, "\t%s = %s\n", name
, value
);
1190 git__free(current_section
);
1192 result
= git_filebuf_commit(&file
, GIT_CONFIG_FILE_MODE
);
1193 git_buf_free(&cfg
->reader
.buffer
);
1198 git__free(current_section
);
1200 git_filebuf_cleanup(&file
);
1201 git_buf_free(&cfg
->reader
.buffer
);
1205 static const char *escapes
= "ntb\"\\";
1206 static const char *escaped
= "\n\t\b\"\\";
1208 /* Escape the values to write them to the file */
1209 static char *escape_value(const char *ptr
)
1211 git_buf buf
= GIT_BUF_INIT
;
1218 git_buf_grow(&buf
, len
);
1220 while (*ptr
!= '\0') {
1221 if ((esc
= strchr(escaped
, *ptr
)) != NULL
) {
1222 git_buf_putc(&buf
, '\\');
1223 git_buf_putc(&buf
, escapes
[esc
- escaped
]);
1225 git_buf_putc(&buf
, *ptr
);
1230 if (git_buf_oom(&buf
)) {
1235 return git_buf_detach(&buf
);
1238 /* '\"' -> '"' etc */
1239 static char *fixup_line(const char *ptr
, int quote_count
)
1241 char *str
= git__malloc(strlen(ptr
) + 1);
1242 char *out
= str
, *esc
;
1247 while (*ptr
!= '\0') {
1250 } else if (*ptr
!= '\\') {
1253 /* backslash, check the next char */
1255 /* if we're at the end, it's a multiline, so keep the backslash */
1260 if ((esc
= strchr(escapes
, *ptr
)) != NULL
) {
1261 *out
++ = escaped
[esc
- escapes
];
1264 giterr_set(GITERR_CONFIG
, "Invalid escape at %s", ptr
);
1277 static int is_multiline_var(const char *str
)
1279 const char *end
= str
+ strlen(str
);
1280 return (end
> str
) && (end
[-1] == '\\');
1283 static int parse_multiline_variable(diskfile_backend
*cfg
, git_buf
*value
, int in_quotes
)
1285 char *line
= NULL
, *proc_line
= NULL
;
1288 /* Check that the next line exists */
1289 line
= cfg_readline(cfg
, false);
1293 /* We've reached the end of the file, there is input missing */
1294 if (line
[0] == '\0') {
1295 set_parse_error(cfg
, 0, "Unexpected end of file while parsing multine var");
1300 quote_count
= strip_comments(line
, !!in_quotes
);
1302 /* If it was just a comment, pretend it didn't exist */
1303 if (line
[0] == '\0') {
1305 return parse_multiline_variable(cfg
, value
, quote_count
);
1306 /* TODO: unbounded recursion. This **could** be exploitable */
1309 /* Drop the continuation character '\': to closely follow the UNIX
1310 * standard, this character **has** to be last one in the buf, with
1311 * no whitespace after it */
1312 assert(is_multiline_var(value
->ptr
));
1313 git_buf_truncate(value
, git_buf_len(value
) - 1);
1315 proc_line
= fixup_line(line
, in_quotes
);
1316 if (proc_line
== NULL
) {
1320 /* add this line to the multiline var */
1321 git_buf_puts(value
, proc_line
);
1323 git__free(proc_line
);
1326 * If we need to continue reading the next line, let's just
1327 * keep putting stuff in the buffer
1329 if (is_multiline_var(value
->ptr
))
1330 return parse_multiline_variable(cfg
, value
, quote_count
);
1335 static int parse_variable(diskfile_backend
*cfg
, char **var_name
, char **var_value
)
1337 const char *var_end
= NULL
;
1338 const char *value_start
= NULL
;
1342 line
= cfg_readline(cfg
, true);
1346 quote_count
= strip_comments(line
, 0);
1348 var_end
= strchr(line
, '=');
1350 if (var_end
== NULL
)
1351 var_end
= strchr(line
, '\0');
1353 value_start
= var_end
+ 1;
1356 while (git__isspace(*var_end
));
1358 *var_name
= git__strndup(line
, var_end
- line
+ 1);
1359 GITERR_CHECK_ALLOC(*var_name
);
1361 /* If there is no value, boolean true is assumed */
1365 * Now, let's try to parse the value
1367 if (value_start
!= NULL
) {
1368 while (git__isspace(value_start
[0]))
1371 if (is_multiline_var(value_start
)) {
1372 git_buf multi_value
= GIT_BUF_INIT
;
1373 char *proc_line
= fixup_line(value_start
, 0);
1374 GITERR_CHECK_ALLOC(proc_line
);
1375 git_buf_puts(&multi_value
, proc_line
);
1376 git__free(proc_line
);
1377 if (parse_multiline_variable(cfg
, &multi_value
, quote_count
) < 0 || git_buf_oom(&multi_value
)) {
1378 git__free(*var_name
);
1380 git_buf_free(&multi_value
);
1384 *var_value
= git_buf_detach(&multi_value
);
1387 else if (value_start
[0] != '\0') {
1388 *var_value
= fixup_line(value_start
, 0);
1389 GITERR_CHECK_ALLOC(*var_value
);