]>
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
[i
] = tolower(str
[i
]);
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
)
380 const char *value
, *num_end
;
384 ret
= config_get(cfg
, name
, &value
);
385 if (ret
< GIT_SUCCESS
)
388 ret
= git__strtol32(&num
, value
, &num_end
, 0);
389 if (ret
< GIT_SUCCESS
)
405 num
*= 1024 * 1024 * 1024;
408 return GIT_EINVALIDTYPE
;
416 int git_config_get_int(git_config
*cfg
, const char *name
, int *out
)
421 ret
= git_config_get_long(cfg
, name
, &tmp
);
428 int git_config_get_bool(git_config
*cfg
, const char *name
, int *out
)
431 int error
= GIT_SUCCESS
;
433 error
= config_get(cfg
, name
, &value
);
434 if (error
< GIT_SUCCESS
)
437 /* A missing value means true */
443 if (!strcasecmp(value
, "true") ||
444 !strcasecmp(value
, "yes") ||
445 !strcasecmp(value
, "on")) {
449 if (!strcasecmp(value
, "false") ||
450 !strcasecmp(value
, "no") ||
451 !strcasecmp(value
, "off")) {
456 /* Try to parse it as an integer */
457 error
= git_config_get_int(cfg
, name
, out
);
458 if (error
== GIT_SUCCESS
)
464 int git_config_get_string(git_config
*cfg
, const char *name
, const char **out
)
466 return config_get(cfg
, name
, out
);
469 static int cfg_getchar_raw(git_config
*cfg
)
473 c
= *cfg
->reader
.read_ptr
++;
476 Win 32 line breaks: if we find a \r\n sequence,
477 return only the \n as a newline
479 if (c
== '\r' && *cfg
->reader
.read_ptr
== '\n') {
480 cfg
->reader
.read_ptr
++;
485 cfg
->reader
.line_number
++;
495 #define SKIP_WHITESPACE (1 << 1)
496 #define SKIP_COMMENTS (1 << 2)
498 static int cfg_getchar(git_config
*cfg_file
, int flags
)
500 const int skip_whitespace
= (flags
& SKIP_WHITESPACE
);
501 const int skip_comments
= (flags
& SKIP_COMMENTS
);
504 assert(cfg_file
->reader
.read_ptr
);
506 do c
= cfg_getchar_raw(cfg_file
);
507 while (skip_whitespace
&& isspace(c
));
509 if (skip_comments
&& (c
== '#' || c
== ';')) {
510 do c
= cfg_getchar_raw(cfg_file
);
518 * Read the next char, but don't move the reading pointer.
520 static int cfg_peek(git_config
*cfg
, int flags
)
523 int old_lineno
, old_eof
;
526 assert(cfg
->reader
.read_ptr
);
528 old_read_ptr
= cfg
->reader
.read_ptr
;
529 old_lineno
= cfg
->reader
.line_number
;
530 old_eof
= cfg
->reader
.eof
;
532 ret
= cfg_getchar(cfg
, flags
);
534 cfg
->reader
.read_ptr
= old_read_ptr
;
535 cfg
->reader
.line_number
= old_lineno
;
536 cfg
->reader
.eof
= old_eof
;
541 static const char *LINEBREAK_UNIX
= "\\\n";
542 static const char *LINEBREAK_WIN32
= "\\\r\n";
544 static int is_linebreak(const char *pos
)
546 return memcmp(pos
- 1, LINEBREAK_UNIX
, sizeof(LINEBREAK_UNIX
)) == 0 ||
547 memcmp(pos
- 2, LINEBREAK_WIN32
, sizeof(LINEBREAK_WIN32
)) == 0;
551 * Read and consume a line, returning it in newly-allocated memory.
553 static char *cfg_readline(git_config
*cfg
)
556 char *line_src
, *line_end
;
559 line_src
= cfg
->reader
.read_ptr
;
560 line_end
= strchr(line_src
, '\n');
562 /* no newline at EOF */
563 if (line_end
== NULL
)
564 line_end
= strchr(line_src
, 0);
566 while (is_linebreak(line_end
))
567 line_end
= strchr(line_end
+ 1, '\n');
570 while (line_src
< line_end
&& isspace(*line_src
))
573 line
= (char *)git__malloc((size_t)(line_end
- line_src
) + 1);
578 while (line_src
< line_end
) {
580 if (memcmp(line_src
, LINEBREAK_UNIX
, sizeof(LINEBREAK_UNIX
)) == 0) {
581 line_src
+= sizeof(LINEBREAK_UNIX
);
585 if (memcmp(line_src
, LINEBREAK_WIN32
, sizeof(LINEBREAK_WIN32
)) == 0) {
586 line_src
+= sizeof(LINEBREAK_WIN32
);
590 line
[line_len
++] = *line_src
++;
593 line
[line_len
] = '\0';
595 while (--line_len
>= 0 && isspace(line
[line_len
]))
596 line
[line_len
] = '\0';
598 if (*line_end
== '\n')
601 if (*line_end
== '\0')
604 cfg
->reader
.line_number
++;
605 cfg
->reader
.read_ptr
= line_end
;
611 * Consume a line, without storing it anywhere
613 void cfg_consume_line(git_config
*cfg
)
615 char *line_start
, *line_end
;
618 line_start
= cfg
->reader
.read_ptr
;
619 line_end
= strchr(line_start
, '\n');
620 /* No newline at EOF */
621 if(line_end
== NULL
){
622 line_end
= strchr(line_start
, '\0');
625 len
= line_end
- line_start
;
627 if (*line_end
== '\n')
630 if (*line_end
== '\0')
633 cfg
->reader
.line_number
++;
634 cfg
->reader
.read_ptr
= line_end
;
637 static inline int config_keychar(int c
)
639 return isalnum(c
) || c
== '-';
643 * Returns $section.$name, using only name_len chars from the name,
644 * which is useful so we don't have to copy the variable name
645 * twice. The name of the variable is set to lowercase.
646 * Don't forget to free the buffer.
648 static char *build_varname(const char *section
, const char *name
)
651 int section_len
, ret
;
655 name_len
= strlen(name
);
656 section_len
= strlen(section
);
657 total_len
= section_len
+ name_len
+ 2;
658 varname
= malloc(total_len
);
662 ret
= snprintf(varname
, total_len
, "%s.%s", section
, name
);
663 if (ret
>= 0) { /* lowercase from the last dot onwards */
664 char *dot
= strrchr(varname
, '.');
666 git__strtolower(dot
);
672 static int parse_section_header_ext(const char *line
, const char *base_name
, char **section_name
)
674 int buf_len
, total_len
, pos
, rpos
;
676 char *subsection
, *first_quote
, *last_quote
;
677 int error
= GIT_SUCCESS
;
680 * base_name is what came before the space. We should be at the
681 * first quotation mark, except for now, line isn't being kept in
682 * sync so we only really use it to calculate the length.
685 first_quote
= strchr(line
, '"');
686 last_quote
= strrchr(line
, '"');
688 if (last_quote
- first_quote
== 0)
689 return GIT_EOBJCORRUPTED
;
691 buf_len
= last_quote
- first_quote
+ 2;
693 subsection
= git__malloc(buf_len
+ 2);
694 if (subsection
== NULL
)
705 * At the end of each iteration, whatever is stored in c will be
706 * added to the string. In case of error, jump to out
711 if (quote_marks
++ >= 2)
712 return GIT_EOBJCORRUPTED
;
721 error
= GIT_EOBJCORRUPTED
;
728 subsection
[pos
++] = c
;
729 } while ((c
= line
[rpos
++]) != ']');
731 subsection
[pos
] = '\0';
733 total_len
= strlen(base_name
) + strlen(subsection
) + 2;
734 *section_name
= git__malloc(total_len
);
735 if (*section_name
== NULL
) {
740 ret
= snprintf(*section_name
, total_len
, "%s %s", base_name
, subsection
);
741 if (ret
>= total_len
) {
742 /* If this fails, we've checked the length wrong */
745 } else if (ret
< 0) {
750 git__strntolower(*section_name
, strchr(*section_name
, ' ') - *section_name
);
758 static int parse_section_header(git_config
*cfg
, char **section_out
)
760 char *name
, *name_end
;
761 int name_length
, c
, pos
;
762 int error
= GIT_SUCCESS
;
765 line
= cfg_readline(cfg
);
769 /* find the end of the variable's name */
770 name_end
= strchr(line
, ']');
771 if (name_end
== NULL
)
772 return GIT_EOBJCORRUPTED
;
774 name
= (char *)git__malloc((size_t)(name_end
- line
) + 1);
776 return GIT_EOBJCORRUPTED
;
781 /* Make sure we were given a section header */
784 error
= GIT_EOBJCORRUPTED
;
791 if (cfg
->reader
.eof
){
792 error
= GIT_EOBJCORRUPTED
;
797 name
[name_length
] = '\0';
798 error
= parse_section_header_ext(line
, name
, section_out
);
804 if (!config_keychar(c
) && c
!= '.') {
805 error
= GIT_EOBJCORRUPTED
;
809 name
[name_length
++] = tolower(c
);
811 } while ((c
= line
[pos
++]) != ']');
813 name
[name_length
] = 0;
815 git__strtolower(name
);
825 static int skip_bom(git_config
*cfg
)
827 static const unsigned char *utf8_bom
= "\xef\xbb\xbf";
829 if (memcmp(cfg
->reader
.read_ptr
, utf8_bom
, sizeof(utf8_bom
)) == 0)
830 cfg
->reader
.read_ptr
+= sizeof(utf8_bom
);
832 /* TODO: the reference implementation does pretty stupid
842 integer = digit { digit }
843 alphabet = "a".."z" + "A" .. "Z"
845 section_char = alphabet | "." | "-"
846 extension_char = (* any character except newline *)
847 any_char = (* any character *)
848 variable_char = "alphabet" | "-"
854 section = header { definition }
856 header = "[" section [subsection | subsection_ext] "]"
858 subsection = "." section
859 subsection_ext = "\"" extension "\""
861 section = section_char { section_char }
862 extension = extension_char { extension_char }
864 definition = variable_name ["=" variable_value] "\n"
866 variable_name = variable_char { variable_char }
867 variable_value = string | boolean | integer
869 string = quoted_string | plain_string
870 quoted_string = "\"" plain_string "\""
871 plain_string = { any_char }
873 boolean = boolean_true | boolean_false
874 boolean_true = "yes" | "1" | "true" | "on"
875 boolean_false = "no" | "0" | "false" | "off"
878 static void strip_comments(char *line
)
883 for (ptr
= line
; *ptr
; ++ptr
) {
884 if (ptr
[0] == '"' && ptr
> line
&& ptr
[-1] != '\\')
887 if ((ptr
[0] == ';' || ptr
[0] == '#') && (quote_count
% 2) == 0) {
893 if (isspace(ptr
[-1])) {
894 /* TODO skip whitespace */
898 static int config_parse(git_config
*cfg_file
)
900 int error
= GIT_SUCCESS
, c
;
901 char *current_section
= NULL
;
906 /* Initialise the reading position */
907 cfg_file
->reader
.read_ptr
= cfg_file
->reader
.buffer
.data
;
908 cfg_file
->reader
.eof
= 0;
912 while (error
== GIT_SUCCESS
&& !cfg_file
->reader
.eof
) {
914 c
= cfg_peek(cfg_file
, SKIP_WHITESPACE
);
917 case '\0': /* We've arrived at the end of the file */
920 case '[': /* section header, new section begins */
921 free(current_section
);
922 error
= parse_section_header(cfg_file
, ¤t_section
);
927 cfg_consume_line(cfg_file
);
930 default: /* assume variable declaration */
931 error
= parse_variable(cfg_file
, &var_name
, &var_value
);
933 if (error
< GIT_SUCCESS
)
936 full_name
= build_varname(current_section
, var_name
);
937 if (full_name
== NULL
) {
944 config_set(cfg_file
, full_name
, var_value
);
954 free(current_section
);
959 static int is_multiline_var(const char *str
)
961 char *end
= strrchr(str
, '\0') - 1;
963 while (isspace(*end
))
969 static int parse_multiline_variable(git_config
*cfg
, const char *first
, char **out
)
971 char *line
= NULL
, *end
;
972 int error
= GIT_SUCCESS
, len
, ret
;
975 /* Check that the next line exists */
976 line
= cfg_readline(cfg
);
980 /* We've reached the end of the file, there is input missing */
981 if (line
[0] == '\0') {
982 error
= GIT_EOBJCORRUPTED
;
986 strip_comments(line
);
988 /* If it was just a comment, pretend it didn't exist */
989 if (line
[0] == '\0') {
990 error
= parse_multiline_variable(cfg
, first
, out
);
994 /* Find the continuation character '\' and strip the whitespace */
995 end
= strrchr(first
, '\\');
996 while (isspace(end
[-1]))
999 *end
= '\0'; /* Terminate the string here */
1001 len
= strlen(first
) + strlen(line
) + 2;
1002 buf
= git__malloc(len
);
1008 ret
= snprintf(buf
, len
, "%s %s", first
, line
);
1016 * If we need to continue reading the next line, pretend
1017 * everything we've read up to now was in one line and call
1020 if (is_multiline_var(buf
)) {
1022 error
= parse_multiline_variable(cfg
, buf
, &final_val
);
1034 static int parse_variable(git_config
*cfg
, char **var_name
, char **var_value
)
1037 int error
= GIT_SUCCESS
;
1038 const char *var_end
= NULL
;
1039 const char *value_start
= NULL
;
1042 line
= cfg_readline(cfg
);
1046 strip_comments(line
);
1048 var_end
= strchr(line
, '=');
1050 if (var_end
== NULL
)
1051 var_end
= strchr(line
, '\0');
1053 value_start
= var_end
+ 1;
1055 if (isspace(var_end
[-1])) {
1057 while (isspace(var_end
[0]));
1060 tmp
= strndup(line
, var_end
- line
+ 1);
1069 * Now, let's try to parse the value
1071 if (value_start
!= NULL
) {
1073 while (isspace(value_start
[0]))
1076 if (value_start
[0] == '\0')
1079 if (is_multiline_var(value_start
)) {
1080 error
= parse_multiline_variable(cfg
, value_start
, var_value
);
1081 if (error
< GIT_SUCCESS
)
1086 tmp
= strdup(value_start
);
1095 /* If thre is no value, boolean true is assumed */