]>
git.proxmox.com Git - libgit2.git/blob - src/config.c
2 * This file is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License, version 2,
4 * as published by the Free Software Foundation.
6 * In addition to the permissions in the GNU General Public License,
7 * the authors give you unlimited permission to link the compiled
8 * version of this file into combinations with other programs,
9 * and to distribute those combinations without any restriction
10 * coming from the use of this file. (The General Public License
11 * restrictions do apply in other respects; for example, they cover
12 * modification of the file, and distribution when not linked into
13 * a combined executable.)
15 * This file is distributed in the hope that it will be useful, but
16 * WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; see the file COPYING. If not, write to
22 * the Free Software Foundation, 51 Franklin Street, Fifth Floor,
23 * Boston, MA 02110-1301, USA.
28 #include "hashtable.h"
33 /**********************
34 * Forward declarations
35 ***********************/
36 static int config_parse(git_config
*cfg_file
);
37 static int parse_variable(git_config
*cfg
, char **var_name
, char **var_value
);
38 void git_config_free(git_config
*cfg
);
40 static void cvar_free(git_cvar
*var
)
51 static void cvar_list_free(git_cvar_list
*list
)
55 while (!CVAR_LIST_EMPTY(list
)) {
56 cur
= CVAR_LIST_HEAD(list
);
57 CVAR_LIST_REMOVE_HEAD(list
);
63 * Compare two strings according to the git section-subsection
64 * rules. The order of the strings is important because local is
65 * assumed to have the internal format (only the section name and with
66 * case information) and input the normalized one (only dots, no case
69 static int cvar_match_section(const char *local
, const char *input
)
71 char *first_dot
, *last_dot
;
72 char *local_sp
= strchr(local
, ' ');
76 * If the local section name doesn't contain a space, then we can
77 * just do a case-insensitive compare.
80 return !strncasecmp(local
, input
, strlen(local
));
83 * From here onwards, there is a space diving the section and the
84 * subsection. Anything before the space in local is
87 if (strncasecmp(local
, input
, local_sp
- local
))
91 * We compare starting from the first character after the
92 * quotation marks, which is two characters beyond the space. For
93 * the input, we start one character beyond the dot. If the names
94 * have different lengths, then we can fail early, as we know they
96 * The length is given by the length between the quotation marks.
99 first_dot
= strchr(input
, '.');
100 last_dot
= strrchr(input
, '.');
101 comparison_len
= strlen(local_sp
+ 2) - 1;
103 if (last_dot
== first_dot
|| last_dot
- first_dot
- 1 != comparison_len
)
106 return !strncmp(local_sp
+ 2, first_dot
+ 1, comparison_len
);
109 static int cvar_match_name(const git_cvar
*var
, const char *str
)
111 const char *name_start
;
113 if (!cvar_match_section(var
->section
, str
)) {
116 /* Early exit if the lengths are different */
117 name_start
= strrchr(str
, '.') + 1;
118 if (strlen(var
->name
) != strlen(name_start
))
121 return !strcasecmp(var
->name
, name_start
);
124 static git_cvar
*cvar_list_find(git_cvar_list
*list
, const char *name
)
128 CVAR_LIST_FOREACH (list
, iter
) {
129 if (cvar_match_name(iter
, name
))
136 static int cvar_name_normalize(const char *input
, char **output
)
138 char *input_sp
= strchr(input
, ' ');
142 /* We need to make a copy anyway */
143 str
= git__strdup(input
);
149 /* If there aren't any spaces, we don't need to do anything */
150 if (input_sp
== NULL
)
154 * If there are spaces, we replace the space by a dot, move the
155 * variable name so that the dot before it replaces the last
156 * quotation mark and repeat so that the first quotation mark
159 str
[input_sp
- input
] = '.';
161 for (i
= 0; i
< 2; ++i
) {
162 quote
= strrchr(str
, '"');
163 memmove(quote
, quote
+ 1, strlen(quote
));
169 void git__strntolower(char *str
, int len
)
173 for (i
= 0; i
< len
; ++i
) {
174 str
[i
] = tolower(str
[i
]);
178 void git__strtolower(char *str
)
180 git__strntolower(str
, strlen(str
));
183 int git_config_open(git_config
**cfg_out
, const char *path
)
186 int error
= GIT_SUCCESS
;
188 assert(cfg_out
&& path
);
190 cfg
= git__malloc(sizeof(git_config
));
194 memset(cfg
, 0x0, sizeof(git_config
));
196 cfg
->file_path
= git__strdup(path
);
197 if (cfg
->file_path
== NULL
) {
202 error
= gitfo_read_file(&cfg
->reader
.buffer
, cfg
->file_path
);
203 if(error
< GIT_SUCCESS
)
206 error
= config_parse(cfg
);
207 if (error
< GIT_SUCCESS
)
212 gitfo_free_buf(&cfg
->reader
.buffer
);
217 cvar_list_free(&cfg
->var_list
);
219 free(cfg
->file_path
);
220 gitfo_free_buf(&cfg
->reader
.buffer
);
226 void git_config_free(git_config
*cfg
)
231 free(cfg
->file_path
);
232 cvar_list_free(&cfg
->var_list
);
238 * Loop over all the variables
241 int git_config_foreach(git_config
*cfg
, int (*fn
)(const char *, void *), void *data
)
243 int ret
= GIT_SUCCESS
;
247 CVAR_LIST_FOREACH(&cfg
->var_list
, var
) {
248 ret
= cvar_name_normalize(var
->name
, &normalized
);
249 if (ret
< GIT_SUCCESS
)
252 ret
= fn(normalized
, data
);
266 * Internal function to actually set the string value of a variable
268 static int config_set(git_config
*cfg
, const char *name
, const char *value
)
270 git_cvar
*var
= NULL
;
271 git_cvar
*existing
= NULL
;
272 int error
= GIT_SUCCESS
;
273 const char *last_dot
;
276 * If it already exists, we just need to update its value.
278 existing
= cvar_list_find(&cfg
->var_list
, name
);
279 if (existing
!= NULL
) {
280 char *tmp
= value
? git__strdup(value
) : NULL
;
281 if (tmp
== NULL
&& value
!= NULL
)
284 free(existing
->value
);
285 existing
->value
= tmp
;
291 * Otherwise, create it and stick it at the end of the queue.
294 var
= git__malloc(sizeof(git_cvar
));
298 memset(var
, 0x0, sizeof(git_cvar
));
300 last_dot
= strrchr(name
, '.');
301 if (last_dot
== NULL
) {
306 var
->section
= git__strndup(name
, last_dot
- name
);
307 if (var
->section
== NULL
) {
312 var
->name
= git__strdup(last_dot
+ 1);
313 if (var
->name
== NULL
) {
318 var
->value
= value
? git__strdup(value
) : NULL
;
319 if (var
->value
== NULL
&& value
!= NULL
) {
324 CVAR_LIST_APPEND(&cfg
->var_list
, var
);
327 if (error
< GIT_SUCCESS
)
333 int git_config_set_long(git_config
*cfg
, const char *name
, long int value
)
335 char str_value
[5]; /* Most numbers should fit in here */
336 int buf_len
= sizeof(str_value
), ret
;
337 char *help_buf
= NULL
;
339 if ((ret
= snprintf(str_value
, buf_len
, "%ld", value
)) >= buf_len
- 1){
340 /* The number is too large, we need to allocate more memory */
342 help_buf
= git__malloc(buf_len
);
343 snprintf(help_buf
, buf_len
, "%ld", value
);
344 ret
= config_set(cfg
, name
, help_buf
);
347 ret
= config_set(cfg
, name
, str_value
);
353 int git_config_set_int(git_config
*cfg
, const char *name
, int value
)
355 return git_config_set_long(cfg
, name
, value
);
358 int git_config_set_bool(git_config
*cfg
, const char *name
, int value
)
360 const char *str_value
;
367 return config_set(cfg
, name
, str_value
);
370 int git_config_set_string(git_config
*cfg
, const char *name
, const char *value
)
372 return config_set(cfg
, name
, value
);
380 * Internal function that actually gets the value in string form
382 static int config_get(git_config
*cfg
, const char *name
, const char **out
)
385 int error
= GIT_SUCCESS
;
387 var
= cvar_list_find(&cfg
->var_list
, name
);
390 return GIT_ENOTFOUND
;
397 int git_config_get_long(git_config
*cfg
, const char *name
, long int *out
)
399 const char *value
, *num_end
;
403 ret
= config_get(cfg
, name
, &value
);
404 if (ret
< GIT_SUCCESS
)
407 ret
= git__strtol32(&num
, value
, &num_end
, 0);
408 if (ret
< GIT_SUCCESS
)
424 num
*= 1024 * 1024 * 1024;
427 return GIT_EINVALIDTYPE
;
435 int git_config_get_int(git_config
*cfg
, const char *name
, int *out
)
440 ret
= git_config_get_long(cfg
, name
, &tmp
);
447 int git_config_get_bool(git_config
*cfg
, const char *name
, int *out
)
450 int error
= GIT_SUCCESS
;
452 error
= config_get(cfg
, name
, &value
);
453 if (error
< GIT_SUCCESS
)
456 /* A missing value means true */
462 if (!strcasecmp(value
, "true") ||
463 !strcasecmp(value
, "yes") ||
464 !strcasecmp(value
, "on")) {
468 if (!strcasecmp(value
, "false") ||
469 !strcasecmp(value
, "no") ||
470 !strcasecmp(value
, "off")) {
475 /* Try to parse it as an integer */
476 error
= git_config_get_int(cfg
, name
, out
);
477 if (error
== GIT_SUCCESS
)
483 int git_config_get_string(git_config
*cfg
, const char *name
, const char **out
)
485 return config_get(cfg
, name
, out
);
488 static int cfg_getchar_raw(git_config
*cfg
)
492 c
= *cfg
->reader
.read_ptr
++;
495 Win 32 line breaks: if we find a \r\n sequence,
496 return only the \n as a newline
498 if (c
== '\r' && *cfg
->reader
.read_ptr
== '\n') {
499 cfg
->reader
.read_ptr
++;
504 cfg
->reader
.line_number
++;
514 #define SKIP_WHITESPACE (1 << 1)
515 #define SKIP_COMMENTS (1 << 2)
517 static int cfg_getchar(git_config
*cfg_file
, int flags
)
519 const int skip_whitespace
= (flags
& SKIP_WHITESPACE
);
520 const int skip_comments
= (flags
& SKIP_COMMENTS
);
523 assert(cfg_file
->reader
.read_ptr
);
525 do c
= cfg_getchar_raw(cfg_file
);
526 while (skip_whitespace
&& isspace(c
));
528 if (skip_comments
&& (c
== '#' || c
== ';')) {
529 do c
= cfg_getchar_raw(cfg_file
);
537 * Read the next char, but don't move the reading pointer.
539 static int cfg_peek(git_config
*cfg
, int flags
)
542 int old_lineno
, old_eof
;
545 assert(cfg
->reader
.read_ptr
);
547 old_read_ptr
= cfg
->reader
.read_ptr
;
548 old_lineno
= cfg
->reader
.line_number
;
549 old_eof
= cfg
->reader
.eof
;
551 ret
= cfg_getchar(cfg
, flags
);
553 cfg
->reader
.read_ptr
= old_read_ptr
;
554 cfg
->reader
.line_number
= old_lineno
;
555 cfg
->reader
.eof
= old_eof
;
560 static const char *LINEBREAK_UNIX
= "\\\n";
561 static const char *LINEBREAK_WIN32
= "\\\r\n";
563 static int is_linebreak(const char *pos
)
565 return memcmp(pos
- 1, LINEBREAK_UNIX
, sizeof(LINEBREAK_UNIX
)) == 0 ||
566 memcmp(pos
- 2, LINEBREAK_WIN32
, sizeof(LINEBREAK_WIN32
)) == 0;
570 * Read and consume a line, returning it in newly-allocated memory.
572 static char *cfg_readline(git_config
*cfg
)
575 char *line_src
, *line_end
;
578 line_src
= cfg
->reader
.read_ptr
;
579 line_end
= strchr(line_src
, '\n');
581 /* no newline at EOF */
582 if (line_end
== NULL
)
583 line_end
= strchr(line_src
, 0);
585 while (is_linebreak(line_end
))
586 line_end
= strchr(line_end
+ 1, '\n');
589 while (line_src
< line_end
&& isspace(*line_src
))
592 line
= (char *)git__malloc((size_t)(line_end
- line_src
) + 1);
597 while (line_src
< line_end
) {
599 if (memcmp(line_src
, LINEBREAK_UNIX
, sizeof(LINEBREAK_UNIX
)) == 0) {
600 line_src
+= sizeof(LINEBREAK_UNIX
);
604 if (memcmp(line_src
, LINEBREAK_WIN32
, sizeof(LINEBREAK_WIN32
)) == 0) {
605 line_src
+= sizeof(LINEBREAK_WIN32
);
609 line
[line_len
++] = *line_src
++;
612 line
[line_len
] = '\0';
614 while (--line_len
>= 0 && isspace(line
[line_len
]))
615 line
[line_len
] = '\0';
617 if (*line_end
== '\n')
620 if (*line_end
== '\0')
623 cfg
->reader
.line_number
++;
624 cfg
->reader
.read_ptr
= line_end
;
630 * Consume a line, without storing it anywhere
632 void cfg_consume_line(git_config
*cfg
)
634 char *line_start
, *line_end
;
637 line_start
= cfg
->reader
.read_ptr
;
638 line_end
= strchr(line_start
, '\n');
639 /* No newline at EOF */
640 if(line_end
== NULL
){
641 line_end
= strchr(line_start
, '\0');
644 len
= line_end
- line_start
;
646 if (*line_end
== '\n')
649 if (*line_end
== '\0')
652 cfg
->reader
.line_number
++;
653 cfg
->reader
.read_ptr
= line_end
;
656 static inline int config_keychar(int c
)
658 return isalnum(c
) || c
== '-';
661 static int parse_section_header_ext(const char *line
, const char *base_name
, char **section_name
)
663 int buf_len
, total_len
, pos
, rpos
;
665 char *subsection
, *first_quote
, *last_quote
;
666 int error
= GIT_SUCCESS
;
669 * base_name is what came before the space. We should be at the
670 * first quotation mark, except for now, line isn't being kept in
671 * sync so we only really use it to calculate the length.
674 first_quote
= strchr(line
, '"');
675 last_quote
= strrchr(line
, '"');
677 if (last_quote
- first_quote
== 0)
678 return GIT_EOBJCORRUPTED
;
680 buf_len
= last_quote
- first_quote
+ 2;
682 subsection
= git__malloc(buf_len
+ 2);
683 if (subsection
== NULL
)
694 * At the end of each iteration, whatever is stored in c will be
695 * added to the string. In case of error, jump to out
700 if (quote_marks
++ >= 2)
701 return GIT_EOBJCORRUPTED
;
710 error
= GIT_EOBJCORRUPTED
;
717 subsection
[pos
++] = c
;
718 } while ((c
= line
[rpos
++]) != ']');
720 subsection
[pos
] = '\0';
722 total_len
= strlen(base_name
) + strlen(subsection
) + 2;
723 *section_name
= git__malloc(total_len
);
724 if (*section_name
== NULL
) {
729 ret
= snprintf(*section_name
, total_len
, "%s %s", base_name
, subsection
);
730 if (ret
>= total_len
) {
731 /* If this fails, we've checked the length wrong */
734 } else if (ret
< 0) {
739 git__strntolower(*section_name
, strchr(*section_name
, ' ') - *section_name
);
747 static int parse_section_header(git_config
*cfg
, char **section_out
)
749 char *name
, *name_end
;
750 int name_length
, c
, pos
;
751 int error
= GIT_SUCCESS
;
754 line
= cfg_readline(cfg
);
758 /* find the end of the variable's name */
759 name_end
= strchr(line
, ']');
760 if (name_end
== NULL
)
761 return GIT_EOBJCORRUPTED
;
763 name
= (char *)git__malloc((size_t)(name_end
- line
) + 1);
765 return GIT_EOBJCORRUPTED
;
770 /* Make sure we were given a section header */
773 error
= GIT_EOBJCORRUPTED
;
780 if (cfg
->reader
.eof
){
781 error
= GIT_EOBJCORRUPTED
;
786 name
[name_length
] = '\0';
787 error
= parse_section_header_ext(line
, name
, section_out
);
793 if (!config_keychar(c
) && c
!= '.') {
794 error
= GIT_EOBJCORRUPTED
;
798 name
[name_length
++] = tolower(c
);
800 } while ((c
= line
[pos
++]) != ']');
802 name
[name_length
] = 0;
804 git__strtolower(name
);
814 static int skip_bom(git_config
*cfg
)
816 static const unsigned char *utf8_bom
= "\xef\xbb\xbf";
818 if (memcmp(cfg
->reader
.read_ptr
, utf8_bom
, sizeof(utf8_bom
)) == 0)
819 cfg
->reader
.read_ptr
+= sizeof(utf8_bom
);
821 /* TODO: the reference implementation does pretty stupid
831 integer = digit { digit }
832 alphabet = "a".."z" + "A" .. "Z"
834 section_char = alphabet | "." | "-"
835 extension_char = (* any character except newline *)
836 any_char = (* any character *)
837 variable_char = "alphabet" | "-"
843 section = header { definition }
845 header = "[" section [subsection | subsection_ext] "]"
847 subsection = "." section
848 subsection_ext = "\"" extension "\""
850 section = section_char { section_char }
851 extension = extension_char { extension_char }
853 definition = variable_name ["=" variable_value] "\n"
855 variable_name = variable_char { variable_char }
856 variable_value = string | boolean | integer
858 string = quoted_string | plain_string
859 quoted_string = "\"" plain_string "\""
860 plain_string = { any_char }
862 boolean = boolean_true | boolean_false
863 boolean_true = "yes" | "1" | "true" | "on"
864 boolean_false = "no" | "0" | "false" | "off"
867 static void strip_comments(char *line
)
872 for (ptr
= line
; *ptr
; ++ptr
) {
873 if (ptr
[0] == '"' && ptr
> line
&& ptr
[-1] != '\\')
876 if ((ptr
[0] == ';' || ptr
[0] == '#') && (quote_count
% 2) == 0) {
882 if (isspace(ptr
[-1])) {
883 /* TODO skip whitespace */
887 static int config_parse(git_config
*cfg_file
)
889 int error
= GIT_SUCCESS
, c
;
890 char *current_section
= NULL
;
895 /* Initialise the reading position */
896 cfg_file
->reader
.read_ptr
= cfg_file
->reader
.buffer
.data
;
897 cfg_file
->reader
.eof
= 0;
901 while (error
== GIT_SUCCESS
&& !cfg_file
->reader
.eof
) {
903 c
= cfg_peek(cfg_file
, SKIP_WHITESPACE
);
906 case '\0': /* We've arrived at the end of the file */
909 case '[': /* section header, new section begins */
910 free(current_section
);
911 error
= parse_section_header(cfg_file
, ¤t_section
);
916 cfg_consume_line(cfg_file
);
919 default: /* assume variable declaration */
920 error
= parse_variable(cfg_file
, &var_name
, &var_value
);
922 if (error
< GIT_SUCCESS
)
925 var
= malloc(sizeof(git_cvar
));
931 memset(var
, 0x0, sizeof(git_cvar
));
933 var
->section
= git__strdup(current_section
);
934 if (var
->section
== NULL
) {
940 var
->name
= var_name
;
941 var
->value
= var_value
;
942 git__strtolower(var
->name
);
944 CVAR_LIST_APPEND(&cfg_file
->var_list
, var
);
951 free(current_section
);
956 static int is_multiline_var(const char *str
)
958 char *end
= strrchr(str
, '\0') - 1;
960 while (isspace(*end
))
966 static int parse_multiline_variable(git_config
*cfg
, const char *first
, char **out
)
968 char *line
= NULL
, *end
;
969 int error
= GIT_SUCCESS
, len
, ret
;
972 /* Check that the next line exists */
973 line
= cfg_readline(cfg
);
977 /* We've reached the end of the file, there is input missing */
978 if (line
[0] == '\0') {
979 error
= GIT_EOBJCORRUPTED
;
983 strip_comments(line
);
985 /* If it was just a comment, pretend it didn't exist */
986 if (line
[0] == '\0') {
987 error
= parse_multiline_variable(cfg
, first
, out
);
991 /* Find the continuation character '\' and strip the whitespace */
992 end
= strrchr(first
, '\\');
993 while (isspace(end
[-1]))
996 *end
= '\0'; /* Terminate the string here */
998 len
= strlen(first
) + strlen(line
) + 2;
999 buf
= git__malloc(len
);
1005 ret
= snprintf(buf
, len
, "%s %s", first
, line
);
1013 * If we need to continue reading the next line, pretend
1014 * everything we've read up to now was in one line and call
1017 if (is_multiline_var(buf
)) {
1019 error
= parse_multiline_variable(cfg
, buf
, &final_val
);
1031 static int parse_variable(git_config
*cfg
, char **var_name
, char **var_value
)
1034 int error
= GIT_SUCCESS
;
1035 const char *var_end
= NULL
;
1036 const char *value_start
= NULL
;
1039 line
= cfg_readline(cfg
);
1043 strip_comments(line
);
1045 var_end
= strchr(line
, '=');
1047 if (var_end
== NULL
)
1048 var_end
= strchr(line
, '\0');
1050 value_start
= var_end
+ 1;
1052 if (isspace(var_end
[-1])) {
1054 while (isspace(var_end
[0]));
1057 tmp
= strndup(line
, var_end
- line
+ 1);
1066 * Now, let's try to parse the value
1068 if (value_start
!= NULL
) {
1070 while (isspace(value_start
[0]))
1073 if (value_start
[0] == '\0')
1076 if (is_multiline_var(value_start
)) {
1077 error
= parse_multiline_variable(cfg
, value_start
, var_value
);
1078 if (error
< GIT_SUCCESS
)
1083 tmp
= strdup(value_start
);
1092 /* If thre is no value, boolean true is assumed */