]>
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
)
50 static void cvar_list_free(git_cvar_list
*list
)
54 while (!CVAR_LIST_EMPTY(list
)) {
55 cur
= CVAR_LIST_HEAD(list
);
56 CVAR_LIST_REMOVE_HEAD(list
);
62 * The order is important. The first parameter is the name we want to
63 * match against, and the second one is what we're looking for
65 static int cvar_section_match(const char *local
, const char *input
)
67 char *input_dot
= strrchr(input
, '.');
68 char *local_last_dot
= strrchr(local
, '.');
69 char *local_sp
= strchr(local
, ' ');
73 * If the local section name doesn't contain a space, then we can
74 * just do a case-insensitive compare.
77 return !strncasecmp(local
, input
, local_last_dot
- local
);
79 /* Anything before the space in local is case-insensitive */
80 if (strncasecmp(local
, input
, local_sp
- local
))
84 * We compare starting from the first character after the
85 * quotation marks, which is two characters beyond the space. For
86 * the input, we start one character beyond the first dot.
87 * The length is given by the length between the quotation marks.
93 * where a is (local_sp + 2) and b is local_last_dot. The comparison
94 * length is given by b - 1 - a.
96 input_dot
= strchr(input
, '.');
97 comparison_len
= local_last_dot
- 1 - (local_sp
+ 2);
98 return !strncmp(local_sp
+ 2, input_dot
+ 1, comparison_len
);
101 static int cvar_name_match(const char *local
, const char *input
)
103 char *input_dot
= strrchr(input
, '.');
104 char *local_dot
= strrchr(local
, '.');
107 * First try to match the section name
109 if (!cvar_section_match(local
, input
))
113 * Anything after the last (possibly only) dot is case-insensitive
115 return !strcasecmp(input_dot
, local_dot
);
118 static git_cvar
*cvar_list_find(git_cvar_list
*list
, const char *name
)
122 CVAR_LIST_FOREACH (list
, iter
) {
123 if (cvar_name_match(iter
->name
, name
))
130 static int cvar_name_normalize(const char *input
, char **output
)
132 char *input_sp
= strchr(input
, ' ');
136 /* We need to make a copy anyway */
137 str
= git__strdup(input
);
143 /* If there aren't any spaces, we don't need to do anything */
144 if (input_sp
== NULL
)
148 * If there are spaces, we replace the space by a dot, move the
149 * variable name so that the dot before it replaces the last
150 * quotation mark and repeat so that the first quotation mark
153 str
[input_sp
- input
] = '.';
155 for (i
= 0; i
< 2; ++i
) {
156 quote
= strrchr(str
, '"');
157 memmove(quote
, quote
+ 1, strlen(quote
));
163 void git__strntolower(char *str
, int len
)
167 for (i
= 0; i
< len
; ++i
) {
168 str
[len
] = tolower(str
[len
]);
172 void git__strtolower(char *str
)
174 git__strntolower(str
, strlen(str
));
177 int git_config_open(git_config
**cfg_out
, const char *path
)
180 int error
= GIT_SUCCESS
;
182 assert(cfg_out
&& path
);
184 cfg
= git__malloc(sizeof(git_config
));
188 memset(cfg
, 0x0, sizeof(git_config
));
190 cfg
->file_path
= git__strdup(path
);
191 if (cfg
->file_path
== NULL
) {
196 error
= gitfo_read_file(&cfg
->reader
.buffer
, cfg
->file_path
);
197 if(error
< GIT_SUCCESS
)
200 error
= config_parse(cfg
);
201 if (error
< GIT_SUCCESS
)
206 gitfo_free_buf(&cfg
->reader
.buffer
);
211 cvar_list_free(&cfg
->var_list
);
213 free(cfg
->file_path
);
214 gitfo_free_buf(&cfg
->reader
.buffer
);
220 void git_config_free(git_config
*cfg
)
225 free(cfg
->file_path
);
226 cvar_list_free(&cfg
->var_list
);
232 * Loop over all the variables
235 int git_config_foreach(git_config
*cfg
, int (*fn
)(const char *, void *), void *data
)
237 int ret
= GIT_SUCCESS
;
241 CVAR_LIST_FOREACH(&cfg
->var_list
, var
) {
242 ret
= cvar_name_normalize(var
->name
, &normalized
);
243 if (ret
< GIT_SUCCESS
)
246 ret
= fn(normalized
, data
);
260 * Internal function to actually set the string value of a variable
262 static int config_set(git_config
*cfg
, const char *name
, const char *value
)
264 git_cvar
*var
= NULL
;
265 git_cvar
*existing
= NULL
;
266 int error
= GIT_SUCCESS
;
269 * If it already exists, we just need to update its value.
271 existing
= cvar_list_find(&cfg
->var_list
, name
);
272 if (existing
!= NULL
) {
273 char *tmp
= value
? git__strdup(value
) : NULL
;
274 if (tmp
== NULL
&& value
!= NULL
)
277 free(existing
->value
);
278 existing
->value
= tmp
;
284 * Otherwise, create it and stick it at the end of the queue.
287 var
= git__malloc(sizeof(git_cvar
));
291 memset(var
, 0x0, sizeof(git_cvar
));
293 var
->name
= git__strdup(name
);
294 if (var
->name
== NULL
) {
299 var
->value
= value
? git__strdup(value
) : NULL
;
300 if (var
->value
== NULL
&& value
!= NULL
) {
305 CVAR_LIST_APPEND(&cfg
->var_list
, var
);
308 if (error
< GIT_SUCCESS
)
314 int git_config_set_long(git_config
*cfg
, const char *name
, long int value
)
316 char str_value
[5]; /* Most numbers should fit in here */
317 int buf_len
= sizeof(str_value
), ret
;
318 char *help_buf
= NULL
;
320 if ((ret
= snprintf(str_value
, buf_len
, "%ld", value
)) >= buf_len
- 1){
321 /* The number is too large, we need to allocate more memory */
323 help_buf
= git__malloc(buf_len
);
324 snprintf(help_buf
, buf_len
, "%ld", value
);
325 ret
= config_set(cfg
, name
, help_buf
);
328 ret
= config_set(cfg
, name
, str_value
);
334 int git_config_set_int(git_config
*cfg
, const char *name
, int value
)
336 return git_config_set_long(cfg
, name
, value
);
339 int git_config_set_bool(git_config
*cfg
, const char *name
, int value
)
341 const char *str_value
;
348 return config_set(cfg
, name
, str_value
);
351 int git_config_set_string(git_config
*cfg
, const char *name
, const char *value
)
353 return config_set(cfg
, name
, value
);
361 * Internal function that actually gets the value in string form
363 static int config_get(git_config
*cfg
, const char *name
, const char **out
)
366 int error
= GIT_SUCCESS
;
368 var
= cvar_list_find(&cfg
->var_list
, name
);
371 return GIT_ENOTFOUND
;
378 int git_config_get_long(git_config
*cfg
, const char *name
, long int *out
)
385 ret
= config_get(cfg
, name
, &value
);
386 if (ret
< GIT_SUCCESS
)
390 num
= strtol(value
, &num_end
, 0);
392 /* There was some error */
393 if (num_end
== value
|| errno
!= 0)
394 return GIT_EINVALIDTYPE
;
404 num
*= 1024 * 1024 * 1024;
407 return GIT_EINVALIDTYPE
;
415 int git_config_get_int(git_config
*cfg
, const char *name
, int *out
)
420 ret
= git_config_get_long(cfg
, name
, &tmp
);
427 int git_config_get_bool(git_config
*cfg
, const char *name
, int *out
)
430 int error
= GIT_SUCCESS
;
432 error
= config_get(cfg
, name
, &value
);
433 if (error
< GIT_SUCCESS
)
436 /* A missing value means true */
442 if (!strcasecmp(value
, "true") ||
443 !strcasecmp(value
, "yes") ||
444 !strcasecmp(value
, "on")) {
448 if (!strcasecmp(value
, "false") ||
449 !strcasecmp(value
, "no") ||
450 !strcasecmp(value
, "off")) {
455 /* Try to parse it as an integer */
456 error
= git_config_get_int(cfg
, name
, out
);
457 if (error
== GIT_SUCCESS
)
463 int git_config_get_string(git_config
*cfg
, const char *name
, const char **out
)
465 return config_get(cfg
, name
, out
);
468 static int cfg_getchar_raw(git_config
*cfg
)
472 c
= *cfg
->reader
.read_ptr
++;
475 Win 32 line breaks: if we find a \r\n sequence,
476 return only the \n as a newline
478 if (c
== '\r' && *cfg
->reader
.read_ptr
== '\n') {
479 cfg
->reader
.read_ptr
++;
484 cfg
->reader
.line_number
++;
494 #define SKIP_WHITESPACE (1 << 1)
495 #define SKIP_COMMENTS (1 << 2)
497 static int cfg_getchar(git_config
*cfg_file
, int flags
)
499 const int skip_whitespace
= (flags
& SKIP_WHITESPACE
);
500 const int skip_comments
= (flags
& SKIP_COMMENTS
);
503 assert(cfg_file
->reader
.read_ptr
);
505 do c
= cfg_getchar_raw(cfg_file
);
506 while (skip_whitespace
&& isspace(c
));
508 if (skip_comments
&& (c
== '#' || c
== ';')) {
509 do c
= cfg_getchar_raw(cfg_file
);
517 * Read the next char, but don't move the reading pointer.
519 static int cfg_peek(git_config
*cfg
, int flags
)
522 int old_lineno
, old_eof
;
525 assert(cfg
->reader
.read_ptr
);
527 old_read_ptr
= cfg
->reader
.read_ptr
;
528 old_lineno
= cfg
->reader
.line_number
;
529 old_eof
= cfg
->reader
.eof
;
531 ret
= cfg_getchar(cfg
, flags
);
533 cfg
->reader
.read_ptr
= old_read_ptr
;
534 cfg
->reader
.line_number
= old_lineno
;
535 cfg
->reader
.eof
= old_eof
;
540 static const char *LINEBREAK_UNIX
= "\\\n";
541 static const char *LINEBREAK_WIN32
= "\\\r\n";
543 static int is_linebreak(const char *pos
)
545 return memcmp(pos
- 1, LINEBREAK_UNIX
, sizeof(LINEBREAK_UNIX
)) == 0 ||
546 memcmp(pos
- 2, LINEBREAK_WIN32
, sizeof(LINEBREAK_WIN32
)) == 0;
550 * Read and consume a line, returning it in newly-allocated memory.
552 static char *cfg_readline(git_config
*cfg
)
555 char *line_src
, *line_end
;
558 line_src
= cfg
->reader
.read_ptr
;
559 line_end
= strchr(line_src
, '\n');
561 /* no newline at EOF */
562 if (line_end
== NULL
)
563 line_end
= strchr(line_src
, 0);
565 while (is_linebreak(line_end
))
566 line_end
= strchr(line_end
+ 1, '\n');
569 while (line_src
< line_end
&& isspace(*line_src
))
572 line
= (char *)git__malloc((size_t)(line_end
- line_src
) + 1);
577 while (line_src
< line_end
) {
579 if (memcmp(line_src
, LINEBREAK_UNIX
, sizeof(LINEBREAK_UNIX
)) == 0) {
580 line_src
+= sizeof(LINEBREAK_UNIX
);
584 if (memcmp(line_src
, LINEBREAK_WIN32
, sizeof(LINEBREAK_WIN32
)) == 0) {
585 line_src
+= sizeof(LINEBREAK_WIN32
);
589 line
[line_len
++] = *line_src
++;
592 line
[line_len
] = '\0';
594 while (--line_len
>= 0 && isspace(line
[line_len
]))
595 line
[line_len
] = '\0';
597 if (*line_end
== '\n')
600 if (*line_end
== '\0')
603 cfg
->reader
.line_number
++;
604 cfg
->reader
.read_ptr
= line_end
;
610 * Consume a line, without storing it anywhere
612 void cfg_consume_line(git_config
*cfg
)
614 char *line_start
, *line_end
;
617 line_start
= cfg
->reader
.read_ptr
;
618 line_end
= strchr(line_start
, '\n');
619 /* No newline at EOF */
620 if(line_end
== NULL
){
621 line_end
= strchr(line_start
, '\0');
624 len
= line_end
- line_start
;
626 if (*line_end
== '\n')
629 if (*line_end
== '\0')
632 cfg
->reader
.line_number
++;
633 cfg
->reader
.read_ptr
= line_end
;
636 static inline int config_keychar(int c
)
638 return isalnum(c
) || c
== '-';
642 * Returns $section.$name, using only name_len chars from the name,
643 * which is useful so we don't have to copy the variable name
644 * twice. The name of the variable is set to lowercase.
645 * Don't forget to free the buffer.
647 static char *build_varname(const char *section
, const char *name
)
650 int section_len
, ret
;
654 name_len
= strlen(name
);
655 section_len
= strlen(section
);
656 total_len
= section_len
+ name_len
+ 2;
657 varname
= malloc(total_len
);
661 ret
= snprintf(varname
, total_len
, "%s.%s", section
, name
);
662 if (ret
>= 0) { /* lowercase from the last dot onwards */
663 char *dot
= strrchr(varname
, '.');
665 git__strtolower(dot
);
671 static int parse_section_header_ext(const char *line
, const char *base_name
, char **section_name
)
673 int buf_len
, total_len
, pos
, rpos
;
675 char *subsection
, *first_quote
, *last_quote
;
676 int error
= GIT_SUCCESS
;
679 * base_name is what came before the space. We should be at the
680 * first quotation mark, except for now, line isn't being kept in
681 * sync so we only really use it to calculate the length.
684 first_quote
= strchr(line
, '"');
685 last_quote
= strrchr(line
, '"');
687 if (last_quote
- first_quote
== 0)
688 return GIT_EOBJCORRUPTED
;
690 buf_len
= last_quote
- first_quote
+ 2;
692 subsection
= git__malloc(buf_len
+ 2);
693 if (subsection
== NULL
)
704 * At the end of each iteration, whatever is stored in c will be
705 * added to the string. In case of error, jump to out
710 if (quote_marks
++ >= 2)
711 return GIT_EOBJCORRUPTED
;
720 error
= GIT_EOBJCORRUPTED
;
727 subsection
[pos
++] = c
;
728 } while ((c
= line
[rpos
++]) != ']');
730 subsection
[pos
] = '\0';
732 total_len
= strlen(base_name
) + strlen(subsection
) + 2;
733 *section_name
= git__malloc(total_len
);
734 if (*section_name
== NULL
) {
739 ret
= snprintf(*section_name
, total_len
, "%s %s", base_name
, subsection
);
740 if (ret
>= total_len
) {
741 /* If this fails, we've checked the length wrong */
744 } else if (ret
< 0) {
749 git__strntolower(*section_name
, strchr(*section_name
, ' ') - *section_name
);
757 static int parse_section_header(git_config
*cfg
, char **section_out
)
759 char *name
, *name_end
;
760 int name_length
, c
, pos
;
761 int error
= GIT_SUCCESS
;
764 line
= cfg_readline(cfg
);
768 /* find the end of the variable's name */
769 name_end
= strchr(line
, ']');
770 if (name_end
== NULL
)
771 return GIT_EOBJCORRUPTED
;
773 name
= (char *)git__malloc((size_t)(name_end
- line
) + 1);
775 return GIT_EOBJCORRUPTED
;
780 /* Make sure we were given a section header */
783 error
= GIT_EOBJCORRUPTED
;
790 if (cfg
->reader
.eof
){
791 error
= GIT_EOBJCORRUPTED
;
796 name
[name_length
] = '\0';
797 error
= parse_section_header_ext(line
, name
, section_out
);
803 if (!config_keychar(c
) && c
!= '.') {
804 error
= GIT_EOBJCORRUPTED
;
808 name
[name_length
++] = tolower(c
);
810 } while ((c
= line
[pos
++]) != ']');
812 name
[name_length
] = 0;
814 git__strtolower(name
);
824 static int skip_bom(git_config
*cfg
)
826 static const unsigned char *utf8_bom
= "\xef\xbb\xbf";
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 void strip_comments(char *line
)
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 if (isspace(ptr
[-1])) {
893 /* TODO skip whitespace */
897 static int config_parse(git_config
*cfg_file
)
899 int error
= GIT_SUCCESS
, c
;
900 char *current_section
= NULL
;
905 /* Initialise the reading position */
906 cfg_file
->reader
.read_ptr
= cfg_file
->reader
.buffer
.data
;
907 cfg_file
->reader
.eof
= 0;
911 while (error
== GIT_SUCCESS
&& !cfg_file
->reader
.eof
) {
913 c
= cfg_peek(cfg_file
, SKIP_WHITESPACE
);
916 case '\0': /* We've arrived at the end of the file */
919 case '[': /* section header, new section begins */
920 free(current_section
);
921 error
= parse_section_header(cfg_file
, ¤t_section
);
926 cfg_consume_line(cfg_file
);
929 default: /* assume variable declaration */
930 error
= parse_variable(cfg_file
, &var_name
, &var_value
);
932 if (error
< GIT_SUCCESS
)
935 full_name
= build_varname(current_section
, var_name
);
936 if (full_name
== NULL
) {
943 config_set(cfg_file
, full_name
, var_value
);
953 free(current_section
);
958 static int is_multiline_var(const char *str
)
960 char *end
= strrchr(str
, '\0') - 1;
962 while (isspace(*end
))
968 static int parse_multiline_variable(git_config
*cfg
, const char *first
, char **out
)
970 char *line
= NULL
, *end
;
971 int error
= GIT_SUCCESS
, len
, ret
;
974 /* Check that the next line exists */
975 line
= cfg_readline(cfg
);
979 /* We've reached the end of the file, there is input missing */
980 if (line
[0] == '\0') {
981 error
= GIT_EOBJCORRUPTED
;
985 strip_comments(line
);
987 /* If it was just a comment, pretend it didn't exist */
988 if (line
[0] == '\0') {
989 error
= parse_multiline_variable(cfg
, first
, out
);
993 /* Find the continuation character '\' and strip the whitespace */
994 end
= strrchr(first
, '\\');
995 while (isspace(end
[-1]))
998 *end
= '\0'; /* Terminate the string here */
1000 len
= strlen(first
) + strlen(line
) + 2;
1001 buf
= git__malloc(len
);
1007 ret
= snprintf(buf
, len
, "%s %s", first
, line
);
1015 * If we need to continue reading the next line, pretend
1016 * everything we've read up to now was in one line and call
1019 if (is_multiline_var(buf
)) {
1021 error
= parse_multiline_variable(cfg
, buf
, &final_val
);
1033 static int parse_variable(git_config
*cfg
, char **var_name
, char **var_value
)
1036 int error
= GIT_SUCCESS
;
1037 const char *var_end
= NULL
;
1038 const char *value_start
= NULL
;
1041 line
= cfg_readline(cfg
);
1045 strip_comments(line
);
1047 var_end
= strchr(line
, '=');
1049 if (var_end
== NULL
)
1050 var_end
= strchr(line
, '\0');
1052 value_start
= var_end
+ 1;
1054 if (isspace(var_end
[-1])) {
1056 while (isspace(var_end
[0]));
1059 tmp
= strndup(line
, var_end
- line
+ 1);
1068 * Now, let's try to parse the value
1070 if (value_start
!= NULL
) {
1072 while (isspace(value_start
[0]))
1075 if (value_start
[0] == '\0')
1078 if (is_multiline_var(value_start
)) {
1079 error
= parse_multiline_variable(cfg
, value_start
, var_value
);
1080 if (error
< GIT_SUCCESS
)
1085 tmp
= strdup(value_start
);
1094 /* If thre is no value, boolean true is assumed */