]> git.proxmox.com Git - libgit2.git/blob - src/config.c
Fix git__strntolower
[libgit2.git] / src / config.c
1 /*
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.
5 *
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.)
14 *
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.
19 *
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.
24 */
25
26 #include "common.h"
27 #include "fileops.h"
28 #include "hashtable.h"
29 #include "config.h"
30
31 #include <ctype.h>
32
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);
39
40 static void cvar_free(git_cvar *var)
41 {
42 if (var == NULL)
43 return;
44
45 free(var->name);
46 free(var->value);
47 free(var);
48 }
49
50 static void cvar_list_free(git_cvar_list *list)
51 {
52 git_cvar *cur;
53
54 while (!CVAR_LIST_EMPTY(list)) {
55 cur = CVAR_LIST_HEAD(list);
56 CVAR_LIST_REMOVE_HEAD(list);
57 cvar_free(cur);
58 }
59 }
60
61 /*
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
64 */
65 static int cvar_section_match(const char *local, const char *input)
66 {
67 char *input_dot = strrchr(input, '.');
68 char *local_last_dot = strrchr(local, '.');
69 char *local_sp = strchr(local, ' ');
70 int comparison_len;
71
72 /*
73 * If the local section name doesn't contain a space, then we can
74 * just do a case-insensitive compare.
75 */
76 if (local_sp == NULL)
77 return !strncasecmp(local, input, local_last_dot - local);
78
79 /* Anything before the space in local is case-insensitive */
80 if (strncasecmp(local, input, local_sp - local))
81 return 0;
82
83 /*
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.
88 *
89 * this "that".var
90 * ^ ^
91 * a b
92 *
93 * where a is (local_sp + 2) and b is local_last_dot. The comparison
94 * length is given by b - 1 - a.
95 */
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);
99 }
100
101 static int cvar_name_match(const char *local, const char *input)
102 {
103 char *input_dot = strrchr(input, '.');
104 char *local_dot = strrchr(local, '.');
105
106 /*
107 * First try to match the section name
108 */
109 if (!cvar_section_match(local, input))
110 return 0;
111
112 /*
113 * Anything after the last (possibly only) dot is case-insensitive
114 */
115 return !strcasecmp(input_dot, local_dot);
116 }
117
118 static git_cvar *cvar_list_find(git_cvar_list *list, const char *name)
119 {
120 git_cvar *iter;
121
122 CVAR_LIST_FOREACH (list, iter) {
123 if (cvar_name_match(iter->name, name))
124 return iter;
125 }
126
127 return NULL;
128 }
129
130 static int cvar_name_normalize(const char *input, char **output)
131 {
132 char *input_sp = strchr(input, ' ');
133 char *quote, *str;
134 int i;
135
136 /* We need to make a copy anyway */
137 str = git__strdup(input);
138 if (str == NULL)
139 return GIT_ENOMEM;
140
141 *output = str;
142
143 /* If there aren't any spaces, we don't need to do anything */
144 if (input_sp == NULL)
145 return GIT_SUCCESS;
146
147 /*
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
151 * disappears.
152 */
153 str[input_sp - input] = '.';
154
155 for (i = 0; i < 2; ++i) {
156 quote = strrchr(str, '"');
157 memmove(quote, quote + 1, strlen(quote));
158 }
159
160 return GIT_SUCCESS;
161 }
162
163 void git__strntolower(char *str, int len)
164 {
165 int i;
166
167 for (i = 0; i < len; ++i) {
168 str[i] = tolower(str[i]);
169 }
170 }
171
172 void git__strtolower(char *str)
173 {
174 git__strntolower(str, strlen(str));
175 }
176
177 int git_config_open(git_config **cfg_out, const char *path)
178 {
179 git_config *cfg;
180 int error = GIT_SUCCESS;
181
182 assert(cfg_out && path);
183
184 cfg = git__malloc(sizeof(git_config));
185 if (cfg == NULL)
186 return GIT_ENOMEM;
187
188 memset(cfg, 0x0, sizeof(git_config));
189
190 cfg->file_path = git__strdup(path);
191 if (cfg->file_path == NULL) {
192 error = GIT_ENOMEM;
193 goto cleanup;
194 }
195
196 error = gitfo_read_file(&cfg->reader.buffer, cfg->file_path);
197 if(error < GIT_SUCCESS)
198 goto cleanup;
199
200 error = config_parse(cfg);
201 if (error < GIT_SUCCESS)
202 goto cleanup;
203 else
204 *cfg_out = cfg;
205
206 gitfo_free_buf(&cfg->reader.buffer);
207
208 return error;
209
210 cleanup:
211 cvar_list_free(&cfg->var_list);
212 if (cfg->file_path)
213 free(cfg->file_path);
214 gitfo_free_buf(&cfg->reader.buffer);
215 free(cfg);
216
217 return error;
218 }
219
220 void git_config_free(git_config *cfg)
221 {
222 if (cfg == NULL)
223 return;
224
225 free(cfg->file_path);
226 cvar_list_free(&cfg->var_list);
227
228 free(cfg);
229 }
230
231 /*
232 * Loop over all the variables
233 */
234
235 int git_config_foreach(git_config *cfg, int (*fn)(const char *, void *), void *data)
236 {
237 int ret = GIT_SUCCESS;
238 git_cvar *var;
239 char *normalized;
240
241 CVAR_LIST_FOREACH(&cfg->var_list, var) {
242 ret = cvar_name_normalize(var->name, &normalized);
243 if (ret < GIT_SUCCESS)
244 return ret;
245
246 ret = fn(normalized, data);
247 free(normalized);
248 if (ret)
249 break;
250 }
251
252 return ret;
253 }
254
255 /**************
256 * Setters
257 **************/
258
259 /*
260 * Internal function to actually set the string value of a variable
261 */
262 static int config_set(git_config *cfg, const char *name, const char *value)
263 {
264 git_cvar *var = NULL;
265 git_cvar *existing = NULL;
266 int error = GIT_SUCCESS;
267
268 /*
269 * If it already exists, we just need to update its value.
270 */
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)
275 return GIT_ENOMEM;
276
277 free(existing->value);
278 existing->value = tmp;
279
280 return GIT_SUCCESS;
281 }
282
283 /*
284 * Otherwise, create it and stick it at the end of the queue.
285 */
286
287 var = git__malloc(sizeof(git_cvar));
288 if (var == NULL)
289 return GIT_ENOMEM;
290
291 memset(var, 0x0, sizeof(git_cvar));
292
293 var->name = git__strdup(name);
294 if (var->name == NULL) {
295 error = GIT_ENOMEM;
296 goto out;
297 }
298
299 var->value = value ? git__strdup(value) : NULL;
300 if (var->value == NULL && value != NULL) {
301 error = GIT_ENOMEM;
302 goto out;
303 }
304
305 CVAR_LIST_APPEND(&cfg->var_list, var);
306
307 out:
308 if (error < GIT_SUCCESS)
309 cvar_free(var);
310
311 return error;
312 }
313
314 int git_config_set_long(git_config *cfg, const char *name, long int value)
315 {
316 char str_value[5]; /* Most numbers should fit in here */
317 int buf_len = sizeof(str_value), ret;
318 char *help_buf = NULL;
319
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 */
322 buf_len = ret + 1;
323 help_buf = git__malloc(buf_len);
324 snprintf(help_buf, buf_len, "%ld", value);
325 ret = config_set(cfg, name, help_buf);
326 free(help_buf);
327 } else {
328 ret = config_set(cfg, name, str_value);
329 }
330
331 return ret;
332 }
333
334 int git_config_set_int(git_config *cfg, const char *name, int value)
335 {
336 return git_config_set_long(cfg, name, value);
337 }
338
339 int git_config_set_bool(git_config *cfg, const char *name, int value)
340 {
341 const char *str_value;
342
343 if (value == 0)
344 str_value = "false";
345 else
346 str_value = "true";
347
348 return config_set(cfg, name, str_value);
349 }
350
351 int git_config_set_string(git_config *cfg, const char *name, const char *value)
352 {
353 return config_set(cfg, name, value);
354 }
355
356 /***********
357 * Getters
358 ***********/
359
360 /*
361 * Internal function that actually gets the value in string form
362 */
363 static int config_get(git_config *cfg, const char *name, const char **out)
364 {
365 git_cvar *var;
366 int error = GIT_SUCCESS;
367
368 var = cvar_list_find(&cfg->var_list, name);
369
370 if (var == NULL)
371 return GIT_ENOTFOUND;
372
373 *out = var->value;
374
375 return error;
376 }
377
378 int git_config_get_long(git_config *cfg, const char *name, long int *out)
379 {
380 const char *value, *num_end;
381 int ret;
382 long int num;
383
384 ret = config_get(cfg, name, &value);
385 if (ret < GIT_SUCCESS)
386 return ret;
387
388 ret = git__strtol32(&num, value, &num_end, 0);
389 if (ret < GIT_SUCCESS)
390 return ret;
391
392 switch (*num_end) {
393 case '\0':
394 break;
395 case 'k':
396 case 'K':
397 num *= 1024;
398 break;
399 case 'm':
400 case 'M':
401 num *= 1024 * 1024;
402 break;
403 case 'g':
404 case 'G':
405 num *= 1024 * 1024 * 1024;
406 break;
407 default:
408 return GIT_EINVALIDTYPE;
409 }
410
411 *out = num;
412
413 return GIT_SUCCESS;
414 }
415
416 int git_config_get_int(git_config *cfg, const char *name, int *out)
417 {
418 long int tmp;
419 int ret;
420
421 ret = git_config_get_long(cfg, name, &tmp);
422
423 *out = (int) tmp;
424
425 return ret;
426 }
427
428 int git_config_get_bool(git_config *cfg, const char *name, int *out)
429 {
430 const char *value;
431 int error = GIT_SUCCESS;
432
433 error = config_get(cfg, name, &value);
434 if (error < GIT_SUCCESS)
435 return error;
436
437 /* A missing value means true */
438 if (value == NULL) {
439 *out = 1;
440 return GIT_SUCCESS;
441 }
442
443 if (!strcasecmp(value, "true") ||
444 !strcasecmp(value, "yes") ||
445 !strcasecmp(value, "on")) {
446 *out = 1;
447 return GIT_SUCCESS;
448 }
449 if (!strcasecmp(value, "false") ||
450 !strcasecmp(value, "no") ||
451 !strcasecmp(value, "off")) {
452 *out = 0;
453 return GIT_SUCCESS;
454 }
455
456 /* Try to parse it as an integer */
457 error = git_config_get_int(cfg, name, out);
458 if (error == GIT_SUCCESS)
459 *out = !!(*out);
460
461 return error;
462 }
463
464 int git_config_get_string(git_config *cfg, const char *name, const char **out)
465 {
466 return config_get(cfg, name, out);
467 }
468
469 static int cfg_getchar_raw(git_config *cfg)
470 {
471 int c;
472
473 c = *cfg->reader.read_ptr++;
474
475 /*
476 Win 32 line breaks: if we find a \r\n sequence,
477 return only the \n as a newline
478 */
479 if (c == '\r' && *cfg->reader.read_ptr == '\n') {
480 cfg->reader.read_ptr++;
481 c = '\n';
482 }
483
484 if (c == '\n')
485 cfg->reader.line_number++;
486
487 if (c == 0) {
488 cfg->reader.eof = 1;
489 c = '\n';
490 }
491
492 return c;
493 }
494
495 #define SKIP_WHITESPACE (1 << 1)
496 #define SKIP_COMMENTS (1 << 2)
497
498 static int cfg_getchar(git_config *cfg_file, int flags)
499 {
500 const int skip_whitespace = (flags & SKIP_WHITESPACE);
501 const int skip_comments = (flags & SKIP_COMMENTS);
502 int c;
503
504 assert(cfg_file->reader.read_ptr);
505
506 do c = cfg_getchar_raw(cfg_file);
507 while (skip_whitespace && isspace(c));
508
509 if (skip_comments && (c == '#' || c == ';')) {
510 do c = cfg_getchar_raw(cfg_file);
511 while (c != '\n');
512 }
513
514 return c;
515 }
516
517 /*
518 * Read the next char, but don't move the reading pointer.
519 */
520 static int cfg_peek(git_config *cfg, int flags)
521 {
522 void *old_read_ptr;
523 int old_lineno, old_eof;
524 int ret;
525
526 assert(cfg->reader.read_ptr);
527
528 old_read_ptr = cfg->reader.read_ptr;
529 old_lineno = cfg->reader.line_number;
530 old_eof = cfg->reader.eof;
531
532 ret = cfg_getchar(cfg, flags);
533
534 cfg->reader.read_ptr = old_read_ptr;
535 cfg->reader.line_number = old_lineno;
536 cfg->reader.eof = old_eof;
537
538 return ret;
539 }
540
541 static const char *LINEBREAK_UNIX = "\\\n";
542 static const char *LINEBREAK_WIN32 = "\\\r\n";
543
544 static int is_linebreak(const char *pos)
545 {
546 return memcmp(pos - 1, LINEBREAK_UNIX, sizeof(LINEBREAK_UNIX)) == 0 ||
547 memcmp(pos - 2, LINEBREAK_WIN32, sizeof(LINEBREAK_WIN32)) == 0;
548 }
549
550 /*
551 * Read and consume a line, returning it in newly-allocated memory.
552 */
553 static char *cfg_readline(git_config *cfg)
554 {
555 char *line = NULL;
556 char *line_src, *line_end;
557 int line_len;
558
559 line_src = cfg->reader.read_ptr;
560 line_end = strchr(line_src, '\n');
561
562 /* no newline at EOF */
563 if (line_end == NULL)
564 line_end = strchr(line_src, 0);
565 else
566 while (is_linebreak(line_end))
567 line_end = strchr(line_end + 1, '\n');
568
569
570 while (line_src < line_end && isspace(*line_src))
571 line_src++;
572
573 line = (char *)git__malloc((size_t)(line_end - line_src) + 1);
574 if (line == NULL)
575 return NULL;
576
577 line_len = 0;
578 while (line_src < line_end) {
579
580 if (memcmp(line_src, LINEBREAK_UNIX, sizeof(LINEBREAK_UNIX)) == 0) {
581 line_src += sizeof(LINEBREAK_UNIX);
582 continue;
583 }
584
585 if (memcmp(line_src, LINEBREAK_WIN32, sizeof(LINEBREAK_WIN32)) == 0) {
586 line_src += sizeof(LINEBREAK_WIN32);
587 continue;
588 }
589
590 line[line_len++] = *line_src++;
591 }
592
593 line[line_len] = '\0';
594
595 while (--line_len >= 0 && isspace(line[line_len]))
596 line[line_len] = '\0';
597
598 if (*line_end == '\n')
599 line_end++;
600
601 if (*line_end == '\0')
602 cfg->reader.eof = 1;
603
604 cfg->reader.line_number++;
605 cfg->reader.read_ptr = line_end;
606
607 return line;
608 }
609
610 /*
611 * Consume a line, without storing it anywhere
612 */
613 void cfg_consume_line(git_config *cfg)
614 {
615 char *line_start, *line_end;
616 int len;
617
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');
623 }
624
625 len = line_end - line_start;
626
627 if (*line_end == '\n')
628 line_end++;
629
630 if (*line_end == '\0')
631 cfg->reader.eof = 1;
632
633 cfg->reader.line_number++;
634 cfg->reader.read_ptr = line_end;
635 }
636
637 static inline int config_keychar(int c)
638 {
639 return isalnum(c) || c == '-';
640 }
641
642 /*
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.
647 */
648 static char *build_varname(const char *section, const char *name)
649 {
650 char *varname;
651 int section_len, ret;
652 int name_len;
653 size_t total_len;
654
655 name_len = strlen(name);
656 section_len = strlen(section);
657 total_len = section_len + name_len + 2;
658 varname = malloc(total_len);
659 if(varname == NULL)
660 return NULL;
661
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, '.');
665 if (dot != NULL)
666 git__strtolower(dot);
667 }
668
669 return varname;
670 }
671
672 static int parse_section_header_ext(const char *line, const char *base_name, char **section_name)
673 {
674 int buf_len, total_len, pos, rpos;
675 int c, ret;
676 char *subsection, *first_quote, *last_quote;
677 int error = GIT_SUCCESS;
678 int quote_marks;
679 /*
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.
683 */
684
685 first_quote = strchr(line, '"');
686 last_quote = strrchr(line, '"');
687
688 if (last_quote - first_quote == 0)
689 return GIT_EOBJCORRUPTED;
690
691 buf_len = last_quote - first_quote + 2;
692
693 subsection = git__malloc(buf_len + 2);
694 if (subsection == NULL)
695 return GIT_ENOMEM;
696
697 pos = 0;
698 rpos = 0;
699 quote_marks = 0;
700
701 line = first_quote;
702 c = line[rpos++];
703
704 /*
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
707 */
708 do {
709 switch (c) {
710 case '"':
711 if (quote_marks++ >= 2)
712 return GIT_EOBJCORRUPTED;
713 break;
714 case '\\':
715 c = line[rpos++];
716 switch (c) {
717 case '"':
718 case '\\':
719 break;
720 default:
721 error = GIT_EOBJCORRUPTED;
722 goto out;
723 }
724 default:
725 break;
726 }
727
728 subsection[pos++] = c;
729 } while ((c = line[rpos++]) != ']');
730
731 subsection[pos] = '\0';
732
733 total_len = strlen(base_name) + strlen(subsection) + 2;
734 *section_name = git__malloc(total_len);
735 if (*section_name == NULL) {
736 error = GIT_ENOMEM;
737 goto out;
738 }
739
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 */
743 error = GIT_ERROR;
744 goto out;
745 } else if (ret < 0) {
746 error = GIT_EOSERR;
747 goto out;
748 }
749
750 git__strntolower(*section_name, strchr(*section_name, ' ') - *section_name);
751
752 out:
753 free(subsection);
754
755 return error;
756 }
757
758 static int parse_section_header(git_config *cfg, char **section_out)
759 {
760 char *name, *name_end;
761 int name_length, c, pos;
762 int error = GIT_SUCCESS;
763 char *line;
764
765 line = cfg_readline(cfg);
766 if (line == NULL)
767 return GIT_ENOMEM;
768
769 /* find the end of the variable's name */
770 name_end = strchr(line, ']');
771 if (name_end == NULL)
772 return GIT_EOBJCORRUPTED;
773
774 name = (char *)git__malloc((size_t)(name_end - line) + 1);
775 if (name == NULL)
776 return GIT_EOBJCORRUPTED;
777
778 name_length = 0;
779 pos = 0;
780
781 /* Make sure we were given a section header */
782 c = line[pos++];
783 if (c != '[') {
784 error = GIT_EOBJCORRUPTED;
785 goto error;
786 }
787
788 c = line[pos++];
789
790 do {
791 if (cfg->reader.eof){
792 error = GIT_EOBJCORRUPTED;
793 goto error;
794 }
795
796 if (isspace(c)){
797 name[name_length] = '\0';
798 error = parse_section_header_ext(line, name, section_out);
799 free(line);
800 free(name);
801 return error;
802 }
803
804 if (!config_keychar(c) && c != '.') {
805 error = GIT_EOBJCORRUPTED;
806 goto error;
807 }
808
809 name[name_length++] = tolower(c);
810
811 } while ((c = line[pos++]) != ']');
812
813 name[name_length] = 0;
814 free(line);
815 git__strtolower(name);
816 *section_out = name;
817 return GIT_SUCCESS;
818
819 error:
820 free(line);
821 free(name);
822 return error;
823 }
824
825 static int skip_bom(git_config *cfg)
826 {
827 static const unsigned char *utf8_bom = "\xef\xbb\xbf";
828
829 if (memcmp(cfg->reader.read_ptr, utf8_bom, sizeof(utf8_bom)) == 0)
830 cfg->reader.read_ptr += sizeof(utf8_bom);
831
832 /* TODO: the reference implementation does pretty stupid
833 shit with the BoM
834 */
835
836 return GIT_SUCCESS;
837 }
838
839 /*
840 (* basic types *)
841 digit = "0".."9"
842 integer = digit { digit }
843 alphabet = "a".."z" + "A" .. "Z"
844
845 section_char = alphabet | "." | "-"
846 extension_char = (* any character except newline *)
847 any_char = (* any character *)
848 variable_char = "alphabet" | "-"
849
850
851 (* actual grammar *)
852 config = { section }
853
854 section = header { definition }
855
856 header = "[" section [subsection | subsection_ext] "]"
857
858 subsection = "." section
859 subsection_ext = "\"" extension "\""
860
861 section = section_char { section_char }
862 extension = extension_char { extension_char }
863
864 definition = variable_name ["=" variable_value] "\n"
865
866 variable_name = variable_char { variable_char }
867 variable_value = string | boolean | integer
868
869 string = quoted_string | plain_string
870 quoted_string = "\"" plain_string "\""
871 plain_string = { any_char }
872
873 boolean = boolean_true | boolean_false
874 boolean_true = "yes" | "1" | "true" | "on"
875 boolean_false = "no" | "0" | "false" | "off"
876 */
877
878 static void strip_comments(char *line)
879 {
880 int quote_count = 0;
881 char *ptr;
882
883 for (ptr = line; *ptr; ++ptr) {
884 if (ptr[0] == '"' && ptr > line && ptr[-1] != '\\')
885 quote_count++;
886
887 if ((ptr[0] == ';' || ptr[0] == '#') && (quote_count % 2) == 0) {
888 ptr[0] = '\0';
889 break;
890 }
891 }
892
893 if (isspace(ptr[-1])) {
894 /* TODO skip whitespace */
895 }
896 }
897
898 static int config_parse(git_config *cfg_file)
899 {
900 int error = GIT_SUCCESS, c;
901 char *current_section = NULL;
902 char *var_name;
903 char *var_value;
904 char *full_name;
905
906 /* Initialise the reading position */
907 cfg_file->reader.read_ptr = cfg_file->reader.buffer.data;
908 cfg_file->reader.eof = 0;
909
910 skip_bom(cfg_file);
911
912 while (error == GIT_SUCCESS && !cfg_file->reader.eof) {
913
914 c = cfg_peek(cfg_file, SKIP_WHITESPACE);
915
916 switch (c) {
917 case '\0': /* We've arrived at the end of the file */
918 break;
919
920 case '[': /* section header, new section begins */
921 free(current_section);
922 error = parse_section_header(cfg_file, &current_section);
923 break;
924
925 case ';':
926 case '#':
927 cfg_consume_line(cfg_file);
928 break;
929
930 default: /* assume variable declaration */
931 error = parse_variable(cfg_file, &var_name, &var_value);
932
933 if (error < GIT_SUCCESS)
934 break;
935
936 full_name = build_varname(current_section, var_name);
937 if (full_name == NULL) {
938 error = GIT_ENOMEM;
939 free(var_name);
940 free(var_value);
941 break;
942 }
943
944 config_set(cfg_file, full_name, var_value);
945 free(var_name);
946 free(var_value);
947 free(full_name);
948
949 break;
950 }
951 }
952
953 if (current_section)
954 free(current_section);
955
956 return error;
957 }
958
959 static int is_multiline_var(const char *str)
960 {
961 char *end = strrchr(str, '\0') - 1;
962
963 while (isspace(*end))
964 --end;
965
966 return *end == '\\';
967 }
968
969 static int parse_multiline_variable(git_config *cfg, const char *first, char **out)
970 {
971 char *line = NULL, *end;
972 int error = GIT_SUCCESS, len, ret;
973 char *buf;
974
975 /* Check that the next line exists */
976 line = cfg_readline(cfg);
977 if (line == NULL)
978 return GIT_ENOMEM;
979
980 /* We've reached the end of the file, there is input missing */
981 if (line[0] == '\0') {
982 error = GIT_EOBJCORRUPTED;
983 goto out;
984 }
985
986 strip_comments(line);
987
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);
991 goto out;
992 }
993
994 /* Find the continuation character '\' and strip the whitespace */
995 end = strrchr(first, '\\');
996 while (isspace(end[-1]))
997 --end;
998
999 *end = '\0'; /* Terminate the string here */
1000
1001 len = strlen(first) + strlen(line) + 2;
1002 buf = git__malloc(len);
1003 if (buf == NULL) {
1004 error = GIT_ENOMEM;
1005 goto out;
1006 }
1007
1008 ret = snprintf(buf, len, "%s %s", first, line);
1009 if (ret < 0) {
1010 error = GIT_EOSERR;
1011 free(buf);
1012 goto out;
1013 }
1014
1015 /*
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
1018 * ourselves.
1019 */
1020 if (is_multiline_var(buf)) {
1021 char *final_val;
1022 error = parse_multiline_variable(cfg, buf, &final_val);
1023 free(buf);
1024 buf = final_val;
1025 }
1026
1027 *out = buf;
1028
1029 out:
1030 free(line);
1031 return error;
1032 }
1033
1034 static int parse_variable(git_config *cfg, char **var_name, char **var_value)
1035 {
1036 char *tmp;
1037 int error = GIT_SUCCESS;
1038 const char *var_end = NULL;
1039 const char *value_start = NULL;
1040 char *line;
1041
1042 line = cfg_readline(cfg);
1043 if (line == NULL)
1044 return GIT_ENOMEM;
1045
1046 strip_comments(line);
1047
1048 var_end = strchr(line, '=');
1049
1050 if (var_end == NULL)
1051 var_end = strchr(line, '\0');
1052 else
1053 value_start = var_end + 1;
1054
1055 if (isspace(var_end[-1])) {
1056 do var_end--;
1057 while (isspace(var_end[0]));
1058 }
1059
1060 tmp = strndup(line, var_end - line + 1);
1061 if (tmp == NULL) {
1062 error = GIT_ENOMEM;
1063 goto out;
1064 }
1065
1066 *var_name = tmp;
1067
1068 /*
1069 * Now, let's try to parse the value
1070 */
1071 if (value_start != NULL) {
1072
1073 while (isspace(value_start[0]))
1074 value_start++;
1075
1076 if (value_start[0] == '\0')
1077 goto out;
1078
1079 if (is_multiline_var(value_start)) {
1080 error = parse_multiline_variable(cfg, value_start, var_value);
1081 if (error < GIT_SUCCESS)
1082 free(*var_name);
1083 goto out;
1084 }
1085
1086 tmp = strdup(value_start);
1087 if (tmp == NULL) {
1088 free(*var_name);
1089 error = GIT_ENOMEM;
1090 goto out;
1091 }
1092
1093 *var_value = tmp;
1094 } else {
1095 /* If thre is no value, boolean true is assumed */
1096 *var_value = NULL;
1097 }
1098
1099 out:
1100 free(line);
1101 return error;
1102 }