]>
git.proxmox.com Git - libgit2.git/blob - src/config_file.c
2 * Copyright (C) 2009-2011 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.
12 #include "git2/config.h"
13 #include "git2/types.h"
18 typedef struct cvar_t
{
30 #define CVAR_LIST_HEAD(list) ((list)->head)
32 #define CVAR_LIST_TAIL(list) ((list)->tail)
34 #define CVAR_LIST_NEXT(var) ((var)->next)
36 #define CVAR_LIST_EMPTY(list) ((list)->head == NULL)
38 #define CVAR_LIST_APPEND(list, var) do {\
39 if (CVAR_LIST_EMPTY(list)) {\
40 CVAR_LIST_HEAD(list) = CVAR_LIST_TAIL(list) = var;\
42 CVAR_LIST_NEXT(CVAR_LIST_TAIL(list)) = var;\
43 CVAR_LIST_TAIL(list) = var;\
47 #define CVAR_LIST_REMOVE_HEAD(list) do {\
48 CVAR_LIST_HEAD(list) = CVAR_LIST_NEXT(CVAR_LIST_HEAD(list));\
51 #define CVAR_LIST_REMOVE_AFTER(var) do {\
52 CVAR_LIST_NEXT(var) = CVAR_LIST_NEXT(CVAR_LIST_NEXT(var));\
55 #define CVAR_LIST_FOREACH(list, iter)\
56 for ((iter) = CVAR_LIST_HEAD(list);\
58 (iter) = CVAR_LIST_NEXT(iter))
61 * Inspired by the FreeBSD functions
63 #define CVAR_LIST_FOREACH_SAFE(start, iter, tmp)\
64 for ((iter) = CVAR_LIST_HEAD(vars);\
65 (iter) && (((tmp) = CVAR_LIST_NEXT(iter) || 1));\
69 git_config_file parent
;
83 static int config_parse(diskfile_backend
*cfg_file
);
84 static int parse_variable(diskfile_backend
*cfg
, char **var_name
, char **var_value
);
85 static int config_write(diskfile_backend
*cfg
, cvar_t
*var
);
87 static void cvar_free(cvar_t
*var
)
98 static void cvar_list_free(cvar_t_list
*list
)
102 while (!CVAR_LIST_EMPTY(list
)) {
103 cur
= CVAR_LIST_HEAD(list
);
104 CVAR_LIST_REMOVE_HEAD(list
);
110 * Compare two strings according to the git section-subsection
111 * rules. The order of the strings is important because local is
112 * assumed to have the internal format (only the section name and with
113 * case information) and input the normalized one (only dots, no case
116 static int cvar_match_section(const char *local
, const char *input
)
119 char *local_sp
= strchr(local
, ' ');
120 size_t comparison_len
;
123 * If the local section name doesn't contain a space, then we can
124 * just do a case-insensitive compare.
126 if (local_sp
== NULL
)
127 return !strncasecmp(local
, input
, strlen(local
));
130 * From here onwards, there is a space diving the section and the
131 * subsection. Anything before the space in local is
134 if (strncasecmp(local
, input
, local_sp
- local
))
138 * We compare starting from the first character after the
139 * quotation marks, which is two characters beyond the space. For
140 * the input, we start one character beyond the dot. If the names
141 * have different lengths, then we can fail early, as we know they
143 * The length is given by the length between the quotation marks.
146 first_dot
= strchr(input
, '.');
147 comparison_len
= strlen(local_sp
+ 2) - 1;
149 return !strncmp(local_sp
+ 2, first_dot
+ 1, comparison_len
);
152 static int cvar_match_name(const cvar_t
*var
, const char *str
)
154 const char *name_start
;
156 if (!cvar_match_section(var
->section
, str
)) {
159 /* Early exit if the lengths are different */
160 name_start
= strrchr(str
, '.') + 1;
161 if (strlen(var
->name
) != strlen(name_start
))
164 return !strcasecmp(var
->name
, name_start
);
167 static cvar_t
*cvar_list_find(cvar_t_list
*list
, const char *name
)
171 CVAR_LIST_FOREACH (list
, iter
) {
172 if (cvar_match_name(iter
, name
))
179 static int cvar_normalize_name(cvar_t
*var
, char **output
)
181 char *section_sp
= strchr(var
->section
, ' ');
187 * The final string is going to be at most one char longer than
190 len
= strlen(var
->section
) + strlen(var
->name
) + 1;
191 name
= git__malloc(len
+ 1);
195 /* If there aren't any spaces in the section, it's easy */
196 if (section_sp
== NULL
) {
197 ret
= p_snprintf(name
, len
+ 1, "%s.%s", var
->section
, var
->name
);
200 return git__throw(GIT_EOSERR
, "Failed to normalize name. OS err: %s", strerror(errno
));
208 * If there are spaces, we replace the space by a dot, move
209 * section name so it overwrites the first quotation mark and
210 * replace the last quotation mark by a dot. We then append the
213 strcpy(name
, var
->section
);
214 section_sp
= strchr(name
, ' ');
216 /* Remove first quote */
217 quote
= strchr(name
, '"');
218 memmove(quote
, quote
+1, strlen(quote
+1));
219 /* Remove second quote */
220 quote
= strchr(name
, '"');
222 strcpy(quote
+1, var
->name
);
228 static char *interiorize_section(const char *orig
)
230 char *dot
, *last_dot
, *section
, *ret
;
233 dot
= strchr(orig
, '.');
234 last_dot
= strrchr(orig
, '.');
235 len
= last_dot
- orig
;
237 /* No subsection, this is easy */
239 return git__strndup(orig
, dot
- orig
);
241 section
= git__malloc(len
+ 4);
245 memset(section
, 0x0, len
+ 4);
248 memcpy(section
, orig
, len
);
251 memcpy(section
, " \"", len
);
253 len
= last_dot
- dot
- 1;
254 memcpy(section
, dot
+ 1, len
);
261 static int config_open(git_config_file
*cfg
)
264 diskfile_backend
*b
= (diskfile_backend
*)cfg
;
266 error
= git_futils_readbuffer(&b
->reader
.buffer
, b
->file_path
);
267 if(error
< GIT_SUCCESS
)
270 error
= config_parse(b
);
271 if (error
< GIT_SUCCESS
)
274 git_futils_freebuffer(&b
->reader
.buffer
);
279 cvar_list_free(&b
->var_list
);
280 git_futils_freebuffer(&b
->reader
.buffer
);
282 return git__rethrow(error
, "Failed to open config");
285 static void backend_free(git_config_file
*_backend
)
287 diskfile_backend
*backend
= (diskfile_backend
*)_backend
;
292 free(backend
->file_path
);
293 cvar_list_free(&backend
->var_list
);
298 static int file_foreach(git_config_file
*backend
, int (*fn
)(const char *, const char *, void *), void *data
)
300 int ret
= GIT_SUCCESS
;
302 diskfile_backend
*b
= (diskfile_backend
*)backend
;
304 CVAR_LIST_FOREACH(&b
->var_list
, var
) {
305 char *normalized
= NULL
;
307 ret
= cvar_normalize_name(var
, &normalized
);
308 if (ret
< GIT_SUCCESS
)
311 ret
= fn(normalized
, var
->value
, data
);
320 static int config_set(git_config_file
*cfg
, const char *name
, const char *value
)
323 cvar_t
*existing
= NULL
;
324 int error
= GIT_SUCCESS
;
325 const char *last_dot
;
326 diskfile_backend
*b
= (diskfile_backend
*)cfg
;
329 * If it already exists, we just need to update its value.
331 existing
= cvar_list_find(&b
->var_list
, name
);
332 if (existing
!= NULL
) {
333 char *tmp
= value
? git__strdup(value
) : NULL
;
334 if (tmp
== NULL
&& value
!= NULL
)
337 free(existing
->value
);
338 existing
->value
= tmp
;
340 return config_write(b
, existing
);
344 * Otherwise, create it and stick it at the end of the queue. If
345 * value is NULL, we return an error, because you can't delete a
346 * variable that doesn't exist.
350 return git__throw(GIT_ENOTFOUND
, "Can't delete non-exitent variable");
352 last_dot
= strrchr(name
, '.');
353 if (last_dot
== NULL
) {
354 return git__throw(GIT_EINVALIDTYPE
, "Variables without section aren't allowed");
357 var
= git__malloc(sizeof(cvar_t
));
361 memset(var
, 0x0, sizeof(cvar_t
));
363 var
->section
= interiorize_section(name
);
364 if (var
->section
== NULL
) {
369 var
->name
= git__strdup(last_dot
+ 1);
370 if (var
->name
== NULL
) {
375 var
->value
= value
? git__strdup(value
) : NULL
;
376 if (var
->value
== NULL
&& value
!= NULL
) {
381 CVAR_LIST_APPEND(&b
->var_list
, var
);
382 error
= config_write(b
, var
);
385 if (error
< GIT_SUCCESS
)
388 return error
== GIT_SUCCESS
? GIT_SUCCESS
: git__rethrow(error
, "Failed to set config value");
392 * Internal function that actually gets the value in string form
394 static int config_get(git_config_file
*cfg
, const char *name
, const char **out
)
397 int error
= GIT_SUCCESS
;
398 diskfile_backend
*b
= (diskfile_backend
*)cfg
;
400 var
= cvar_list_find(&b
->var_list
, name
);
403 return git__throw(GIT_ENOTFOUND
, "Variable '%s' not found", name
);
407 return error
== GIT_SUCCESS
? GIT_SUCCESS
: git__rethrow(error
, "Failed to get config value for %s", name
);
410 int git_config_file__ondisk(git_config_file
**out
, const char *path
)
412 diskfile_backend
*backend
;
414 backend
= git__malloc(sizeof(diskfile_backend
));
418 memset(backend
, 0x0, sizeof(diskfile_backend
));
420 backend
->file_path
= git__strdup(path
);
421 if (backend
->file_path
== NULL
) {
426 backend
->parent
.open
= config_open
;
427 backend
->parent
.get
= config_get
;
428 backend
->parent
.set
= config_set
;
429 backend
->parent
.foreach
= file_foreach
;
430 backend
->parent
.free
= backend_free
;
432 *out
= (git_config_file
*)backend
;
437 static int cfg_getchar_raw(diskfile_backend
*cfg
)
441 c
= *cfg
->reader
.read_ptr
++;
444 Win 32 line breaks: if we find a \r\n sequence,
445 return only the \n as a newline
447 if (c
== '\r' && *cfg
->reader
.read_ptr
== '\n') {
448 cfg
->reader
.read_ptr
++;
453 cfg
->reader
.line_number
++;
463 #define SKIP_WHITESPACE (1 << 1)
464 #define SKIP_COMMENTS (1 << 2)
466 static int cfg_getchar(diskfile_backend
*cfg_file
, int flags
)
468 const int skip_whitespace
= (flags
& SKIP_WHITESPACE
);
469 const int skip_comments
= (flags
& SKIP_COMMENTS
);
472 assert(cfg_file
->reader
.read_ptr
);
474 do c
= cfg_getchar_raw(cfg_file
);
475 while (skip_whitespace
&& isspace(c
));
477 if (skip_comments
&& (c
== '#' || c
== ';')) {
478 do c
= cfg_getchar_raw(cfg_file
);
486 * Read the next char, but don't move the reading pointer.
488 static int cfg_peek(diskfile_backend
*cfg
, int flags
)
491 int old_lineno
, old_eof
;
494 assert(cfg
->reader
.read_ptr
);
496 old_read_ptr
= cfg
->reader
.read_ptr
;
497 old_lineno
= cfg
->reader
.line_number
;
498 old_eof
= cfg
->reader
.eof
;
500 ret
= cfg_getchar(cfg
, flags
);
502 cfg
->reader
.read_ptr
= old_read_ptr
;
503 cfg
->reader
.line_number
= old_lineno
;
504 cfg
->reader
.eof
= old_eof
;
510 * Read and consume a line, returning it in newly-allocated memory.
512 static char *cfg_readline(diskfile_backend
*cfg
)
515 char *line_src
, *line_end
;
518 line_src
= cfg
->reader
.read_ptr
;
520 /* Skip empty empty lines */
521 while (isspace(*line_src
))
524 line_end
= strchr(line_src
, '\n');
526 /* no newline at EOF */
527 if (line_end
== NULL
)
528 line_end
= strchr(line_src
, 0);
530 line_len
= line_end
- line_src
;
532 line
= git__malloc(line_len
+ 1);
536 memcpy(line
, line_src
, line_len
);
538 line
[line_len
] = '\0';
540 while (--line_len
>= 0 && isspace(line
[line_len
]))
541 line
[line_len
] = '\0';
543 if (*line_end
== '\n')
546 if (*line_end
== '\0')
549 cfg
->reader
.line_number
++;
550 cfg
->reader
.read_ptr
= line_end
;
556 * Consume a line, without storing it anywhere
558 void cfg_consume_line(diskfile_backend
*cfg
)
560 char *line_start
, *line_end
;
562 line_start
= cfg
->reader
.read_ptr
;
563 line_end
= strchr(line_start
, '\n');
564 /* No newline at EOF */
565 if(line_end
== NULL
){
566 line_end
= strchr(line_start
, '\0');
569 if (*line_end
== '\n')
572 if (*line_end
== '\0')
575 cfg
->reader
.line_number
++;
576 cfg
->reader
.read_ptr
= line_end
;
579 GIT_INLINE(int) config_keychar(int c
)
581 return isalnum(c
) || c
== '-';
584 static int parse_section_header_ext(const char *line
, const char *base_name
, char **section_name
)
586 size_t buf_len
, total_len
;
589 char *subsection
, *first_quote
, *last_quote
;
590 int error
= GIT_SUCCESS
;
593 * base_name is what came before the space. We should be at the
594 * first quotation mark, except for now, line isn't being kept in
595 * sync so we only really use it to calculate the length.
598 first_quote
= strchr(line
, '"');
599 last_quote
= strrchr(line
, '"');
601 if (last_quote
- first_quote
== 0)
602 return git__throw(GIT_EOBJCORRUPTED
, "Failed to parse ext header. There is no final quotation mark");
604 buf_len
= last_quote
- first_quote
+ 2;
606 subsection
= git__malloc(buf_len
+ 2);
607 if (subsection
== NULL
)
618 * At the end of each iteration, whatever is stored in c will be
619 * added to the string. In case of error, jump to out
622 if (quote_marks
== 2) {
623 error
= git__throw(GIT_EOBJCORRUPTED
, "Falied to parse ext header. Text after closing quote");
639 error
= git__throw(GIT_EOBJCORRUPTED
, "Failed to parse ext header. Unsupported escape char \\%c", c
);
647 subsection
[pos
++] = (char) c
;
648 } while ((c
= line
[rpos
++]) != ']');
650 subsection
[pos
] = '\0';
652 total_len
= strlen(base_name
) + strlen(subsection
) + 2;
653 *section_name
= git__malloc(total_len
);
654 if (*section_name
== NULL
) {
659 ret
= p_snprintf(*section_name
, total_len
, "%s %s", base_name
, subsection
);
661 error
= git__throw(GIT_EOSERR
, "Failed to parse ext header. OS error: %s", strerror(errno
));
665 git__strntolower(*section_name
, strchr(*section_name
, ' ') - *section_name
);
673 static int parse_section_header(diskfile_backend
*cfg
, char **section_out
)
675 char *name
, *name_end
;
676 int name_length
, c
, pos
;
677 int error
= GIT_SUCCESS
;
680 line
= cfg_readline(cfg
);
684 /* find the end of the variable's name */
685 name_end
= strchr(line
, ']');
686 if (name_end
== NULL
) {
688 return git__throw(GIT_EOBJCORRUPTED
, "Failed to parse header. Can't find header name end");
691 name
= (char *)git__malloc((size_t)(name_end
- line
) + 1);
700 /* Make sure we were given a section header */
703 error
= git__throw(GIT_ERROR
, "Failed to parse header. Didn't get section header. This is a bug");
711 name
[name_length
] = '\0';
712 error
= parse_section_header_ext(line
, name
, section_out
);
715 return error
== GIT_SUCCESS
? GIT_SUCCESS
: git__rethrow(error
, "Failed to parse header");
718 if (!config_keychar(c
) && c
!= '.') {
719 error
= git__throw(GIT_EOBJCORRUPTED
, "Failed to parse header. Wrong format on header");
723 name
[name_length
++] = (char) tolower(c
);
725 } while ((c
= line
[pos
++]) != ']');
727 if (line
[pos
- 1] != ']') {
728 error
= git__throw(GIT_EOBJCORRUPTED
, "Failed to parse header. Config file ended unexpectedly");
732 name
[name_length
] = 0;
734 git__strtolower(name
);
744 static int skip_bom(diskfile_backend
*cfg
)
746 static const char *utf8_bom
= "\xef\xbb\xbf";
748 if (memcmp(cfg
->reader
.read_ptr
, utf8_bom
, sizeof(utf8_bom
)) == 0)
749 cfg
->reader
.read_ptr
+= sizeof(utf8_bom
);
751 /* TODO: the reference implementation does pretty stupid
761 integer = digit { digit }
762 alphabet = "a".."z" + "A" .. "Z"
764 section_char = alphabet | "." | "-"
765 extension_char = (* any character except newline *)
766 any_char = (* any character *)
767 variable_char = "alphabet" | "-"
773 section = header { definition }
775 header = "[" section [subsection | subsection_ext] "]"
777 subsection = "." section
778 subsection_ext = "\"" extension "\""
780 section = section_char { section_char }
781 extension = extension_char { extension_char }
783 definition = variable_name ["=" variable_value] "\n"
785 variable_name = variable_char { variable_char }
786 variable_value = string | boolean | integer
788 string = quoted_string | plain_string
789 quoted_string = "\"" plain_string "\""
790 plain_string = { any_char }
792 boolean = boolean_true | boolean_false
793 boolean_true = "yes" | "1" | "true" | "on"
794 boolean_false = "no" | "0" | "false" | "off"
797 static void strip_comments(char *line
)
802 for (ptr
= line
; *ptr
; ++ptr
) {
803 if (ptr
[0] == '"' && ptr
> line
&& ptr
[-1] != '\\')
806 if ((ptr
[0] == ';' || ptr
[0] == '#') && (quote_count
% 2) == 0) {
812 if (isspace(ptr
[-1])) {
813 /* TODO skip whitespace */
817 static int config_parse(diskfile_backend
*cfg_file
)
819 int error
= GIT_SUCCESS
, c
;
820 char *current_section
= NULL
;
825 /* Initialize the reading position */
826 cfg_file
->reader
.read_ptr
= cfg_file
->reader
.buffer
.data
;
827 cfg_file
->reader
.eof
= 0;
829 /* If the file is empty, there's nothing for us to do */
830 if (*cfg_file
->reader
.read_ptr
== '\0')
835 while (error
== GIT_SUCCESS
&& !cfg_file
->reader
.eof
) {
837 c
= cfg_peek(cfg_file
, SKIP_WHITESPACE
);
840 case '\0': /* We've arrived at the end of the file */
843 case '[': /* section header, new section begins */
844 free(current_section
);
845 current_section
= NULL
;
846 error
= parse_section_header(cfg_file
, ¤t_section
);
851 cfg_consume_line(cfg_file
);
854 default: /* assume variable declaration */
855 error
= parse_variable(cfg_file
, &var_name
, &var_value
);
857 if (error
< GIT_SUCCESS
)
860 var
= malloc(sizeof(cvar_t
));
866 memset(var
, 0x0, sizeof(cvar_t
));
868 var
->section
= git__strdup(current_section
);
869 if (var
->section
== NULL
) {
875 var
->name
= var_name
;
876 var
->value
= var_value
;
877 git__strtolower(var
->name
);
879 CVAR_LIST_APPEND(&cfg_file
->var_list
, var
);
885 free(current_section
);
887 return error
== GIT_SUCCESS
? GIT_SUCCESS
: git__rethrow(error
, "Failed to parse config");
890 static int write_section(git_filebuf
*file
, cvar_t
*var
)
894 error
= git_filebuf_printf(file
, "[%s]\n", var
->section
);
895 if (error
< GIT_SUCCESS
)
898 error
= git_filebuf_printf(file
, " %s = %s\n", var
->name
, var
->value
);
903 * This is pretty much the parsing, except we write out anything we don't have
905 static int config_write(diskfile_backend
*cfg
, cvar_t
*var
)
907 int error
= GIT_SUCCESS
, c
;
908 int section_matches
= 0, last_section_matched
= 0;
909 char *current_section
= NULL
;
910 char *var_name
, *var_value
, *data_start
;
912 const char *pre_end
= NULL
, *post_start
= NULL
;
914 /* We need to read in our own config file */
915 error
= git_futils_readbuffer(&cfg
->reader
.buffer
, cfg
->file_path
);
916 if (error
< GIT_SUCCESS
) {
917 return git__rethrow(error
, "Failed to read existing config file %s", cfg
->file_path
);
920 /* Initialise the reading position */
921 cfg
->reader
.read_ptr
= cfg
->reader
.buffer
.data
;
923 data_start
= cfg
->reader
.read_ptr
;
926 error
= git_filebuf_open(&file
, cfg
->file_path
, 0);
927 if (error
< GIT_SUCCESS
)
928 return git__rethrow(error
, "Failed to lock config file");
932 while (error
== GIT_SUCCESS
&& !cfg
->reader
.eof
) {
933 c
= cfg_peek(cfg
, SKIP_WHITESPACE
);
936 case '\0': /* We've arrived at the end of the file */
939 case '[': /* section header, new section begins */
941 * We set both positions to the current one in case we
942 * need to add a variable to the end of a section. In that
943 * case, we want both variables to point just before the
944 * new section. If we actually want to replace it, the
945 * default case will take care of updating them.
947 pre_end
= post_start
= cfg
->reader
.read_ptr
;
949 free(current_section
);
950 error
= parse_section_header(cfg
, ¤t_section
);
951 if (error
< GIT_SUCCESS
)
954 /* Keep track of when it stops matching */
955 last_section_matched
= section_matches
;
956 section_matches
= !strcmp(current_section
, var
->section
);
961 cfg_consume_line(cfg
);
966 * If the section doesn't match, but the last section did,
967 * it means we need to add a variable (so skip the line
968 * otherwise). If both the section and name match, we need
969 * to overwrite the variable (so skip the line
970 * otherwise). pre_end needs to be updated each time so we
971 * don't loose that information, but we only need to
972 * update post_start if we're going to use it in this
975 if (!section_matches
) {
976 if (!last_section_matched
) {
977 cfg_consume_line(cfg
);
983 pre_end
= cfg
->reader
.read_ptr
;
984 if ((error
= parse_variable(cfg
, &var_name
, &var_value
)) == GIT_SUCCESS
)
985 cmp
= strcasecmp(var
->name
, var_name
);
993 post_start
= cfg
->reader
.read_ptr
;
997 * We've found the variable we wanted to change, so
998 * write anything up to it
1000 error
= git_filebuf_write(&file
, data_start
, pre_end
- data_start
);
1001 if (error
< GIT_SUCCESS
) {
1002 git__rethrow(error
, "Failed to write the first part of the file");
1007 * Then replace the variable. If the value is NULL, it
1008 * means we want to delete it, so pretend everything went
1011 if (var
->value
== NULL
)
1012 error
= GIT_SUCCESS
;
1014 error
= git_filebuf_printf(&file
, "\t%s = %s\n", var
->name
, var
->value
);
1015 if (error
< GIT_SUCCESS
) {
1016 git__rethrow(error
, "Failed to overwrite the variable");
1020 /* And then the write out rest of the file */
1021 error
= git_filebuf_write(&file
, post_start
,
1022 cfg
->reader
.buffer
.len
- (post_start
- data_start
));
1024 if (error
< GIT_SUCCESS
) {
1025 git__rethrow(error
, "Failed to write the rest of the file");
1034 * Being here can mean that
1036 * 1) our section is the last one in the file and we're
1039 * 2) we didn't find a section for us so we need to create it
1042 * Either way we need to write out the whole file.
1045 error
= git_filebuf_write(&file
, cfg
->reader
.buffer
.data
, cfg
->reader
.buffer
.len
);
1046 if (error
< GIT_SUCCESS
) {
1047 git__rethrow(error
, "Failed to write original config content");
1051 /* And now if we just need to add a variable */
1052 if (section_matches
) {
1053 error
= git_filebuf_printf(&file
, "\t%s = %s\n", var
->name
, var
->value
);
1057 /* Or maybe we need to write out a whole section */
1058 error
= write_section(&file
, var
);
1059 if (error
< GIT_SUCCESS
)
1060 git__rethrow(error
, "Failed to write new section");
1063 free(current_section
);
1065 if (error
< GIT_SUCCESS
)
1066 git_filebuf_cleanup(&file
);
1068 error
= git_filebuf_commit(&file
);
1070 git_futils_freebuffer(&cfg
->reader
.buffer
);
1074 static int is_multiline_var(const char *str
)
1076 char *end
= strrchr(str
, '\0') - 1;
1078 while (isspace(*end
))
1081 return *end
== '\\';
1084 static int parse_multiline_variable(diskfile_backend
*cfg
, const char *first
, char **out
)
1086 char *line
= NULL
, *end
;
1087 int error
= GIT_SUCCESS
, ret
;
1091 /* Check that the next line exists */
1092 line
= cfg_readline(cfg
);
1096 /* We've reached the end of the file, there is input missing */
1097 if (line
[0] == '\0') {
1098 error
= git__throw(GIT_EOBJCORRUPTED
, "Failed to parse multiline var. File ended unexpectedly");
1102 strip_comments(line
);
1104 /* If it was just a comment, pretend it didn't exist */
1105 if (line
[0] == '\0') {
1106 error
= parse_multiline_variable(cfg
, first
, out
);
1110 /* Find the continuation character '\' and strip the whitespace */
1111 end
= strrchr(first
, '\\');
1112 while (isspace(end
[-1]))
1115 *end
= '\0'; /* Terminate the string here */
1117 len
= strlen(first
) + strlen(line
) + 2;
1118 buf
= git__malloc(len
);
1124 ret
= p_snprintf(buf
, len
, "%s %s", first
, line
);
1126 error
= git__throw(GIT_EOSERR
, "Failed to parse multiline var. Failed to put together two lines. OS err: %s", strerror(errno
));
1132 * If we need to continue reading the next line, pretend
1133 * everything we've read up to now was in one line and call
1136 if (is_multiline_var(buf
)) {
1138 error
= parse_multiline_variable(cfg
, buf
, &final_val
);
1150 static int parse_variable(diskfile_backend
*cfg
, char **var_name
, char **var_value
)
1153 int error
= GIT_SUCCESS
;
1154 const char *var_end
= NULL
;
1155 const char *value_start
= NULL
;
1158 line
= cfg_readline(cfg
);
1162 strip_comments(line
);
1164 var_end
= strchr(line
, '=');
1166 if (var_end
== NULL
)
1167 var_end
= strchr(line
, '\0');
1169 value_start
= var_end
+ 1;
1171 if (isspace(var_end
[-1])) {
1173 while (isspace(var_end
[0]));
1176 tmp
= git__strndup(line
, var_end
- line
+ 1);
1185 * Now, let's try to parse the value
1187 if (value_start
!= NULL
) {
1189 while (isspace(value_start
[0]))
1192 if (value_start
[0] == '\0')
1195 if (is_multiline_var(value_start
)) {
1196 error
= parse_multiline_variable(cfg
, value_start
, var_value
);
1197 if (error
< GIT_SUCCESS
)
1202 tmp
= strdup(value_start
);
1211 /* If there is no value, boolean true is assumed */