]> git.proxmox.com Git - libgit2.git/blob - src/config.c
Merge upstream/development
[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[len] = tolower(str[len]);
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;
381 char *num_end;
382 int ret;
383 long int num;
384
385 ret = config_get(cfg, name, &value);
386 if (ret < GIT_SUCCESS)
387 return ret;
388
389 errno = 0;
390 num = strtol(value, &num_end, 0);
391
392 /* There was some error */
393 if (num_end == value || errno != 0)
394 return GIT_EINVALIDTYPE;
395
396 switch (*num_end) {
397 case 'k':
398 num *= 1024;
399 break;
400 case 'm':
401 num *= 1024 * 1024;
402 break;
403 case 'g':
404 num *= 1024 * 1024 * 1024;
405 break;
406 default:
407 return GIT_EINVALIDTYPE;
408 }
409
410 *out = num;
411
412 return GIT_SUCCESS;
413 }
414
415 int git_config_get_int(git_config *cfg, const char *name, int *out)
416 {
417 long int tmp;
418 int ret;
419
420 ret = git_config_get_long(cfg, name, &tmp);
421
422 *out = (int) tmp;
423
424 return ret;
425 }
426
427 int git_config_get_bool(git_config *cfg, const char *name, int *out)
428 {
429 const char *value;
430 int error = GIT_SUCCESS;
431
432 error = config_get(cfg, name, &value);
433 if (error < GIT_SUCCESS)
434 return error;
435
436 /* A missing value means true */
437 if (value == NULL) {
438 *out = 1;
439 return GIT_SUCCESS;
440 }
441
442 if (!strcasecmp(value, "true") ||
443 !strcasecmp(value, "yes") ||
444 !strcasecmp(value, "on")) {
445 *out = 1;
446 return GIT_SUCCESS;
447 }
448 if (!strcasecmp(value, "false") ||
449 !strcasecmp(value, "no") ||
450 !strcasecmp(value, "off")) {
451 *out = 0;
452 return GIT_SUCCESS;
453 }
454
455 /* Try to parse it as an integer */
456 error = git_config_get_int(cfg, name, out);
457 if (error == GIT_SUCCESS)
458 *out = !!(*out);
459
460 return error;
461 }
462
463 int git_config_get_string(git_config *cfg, const char *name, const char **out)
464 {
465 return config_get(cfg, name, out);
466 }
467
468 static int cfg_getchar_raw(git_config *cfg)
469 {
470 int c;
471
472 c = *cfg->reader.read_ptr++;
473
474 /*
475 Win 32 line breaks: if we find a \r\n sequence,
476 return only the \n as a newline
477 */
478 if (c == '\r' && *cfg->reader.read_ptr == '\n') {
479 cfg->reader.read_ptr++;
480 c = '\n';
481 }
482
483 if (c == '\n')
484 cfg->reader.line_number++;
485
486 if (c == 0) {
487 cfg->reader.eof = 1;
488 c = '\n';
489 }
490
491 return c;
492 }
493
494 #define SKIP_WHITESPACE (1 << 1)
495 #define SKIP_COMMENTS (1 << 2)
496
497 static int cfg_getchar(git_config *cfg_file, int flags)
498 {
499 const int skip_whitespace = (flags & SKIP_WHITESPACE);
500 const int skip_comments = (flags & SKIP_COMMENTS);
501 int c;
502
503 assert(cfg_file->reader.read_ptr);
504
505 do c = cfg_getchar_raw(cfg_file);
506 while (skip_whitespace && isspace(c));
507
508 if (skip_comments && (c == '#' || c == ';')) {
509 do c = cfg_getchar_raw(cfg_file);
510 while (c != '\n');
511 }
512
513 return c;
514 }
515
516 /*
517 * Read the next char, but don't move the reading pointer.
518 */
519 static int cfg_peek(git_config *cfg, int flags)
520 {
521 void *old_read_ptr;
522 int old_lineno, old_eof;
523 int ret;
524
525 assert(cfg->reader.read_ptr);
526
527 old_read_ptr = cfg->reader.read_ptr;
528 old_lineno = cfg->reader.line_number;
529 old_eof = cfg->reader.eof;
530
531 ret = cfg_getchar(cfg, flags);
532
533 cfg->reader.read_ptr = old_read_ptr;
534 cfg->reader.line_number = old_lineno;
535 cfg->reader.eof = old_eof;
536
537 return ret;
538 }
539
540 static const char *LINEBREAK_UNIX = "\\\n";
541 static const char *LINEBREAK_WIN32 = "\\\r\n";
542
543 static int is_linebreak(const char *pos)
544 {
545 return memcmp(pos - 1, LINEBREAK_UNIX, sizeof(LINEBREAK_UNIX)) == 0 ||
546 memcmp(pos - 2, LINEBREAK_WIN32, sizeof(LINEBREAK_WIN32)) == 0;
547 }
548
549 /*
550 * Read and consume a line, returning it in newly-allocated memory.
551 */
552 static char *cfg_readline(git_config *cfg)
553 {
554 char *line = NULL;
555 char *line_src, *line_end;
556 int line_len;
557
558 line_src = cfg->reader.read_ptr;
559 line_end = strchr(line_src, '\n');
560
561 /* no newline at EOF */
562 if (line_end == NULL)
563 line_end = strchr(line_src, 0);
564 else
565 while (is_linebreak(line_end))
566 line_end = strchr(line_end + 1, '\n');
567
568
569 while (line_src < line_end && isspace(*line_src))
570 line_src++;
571
572 line = (char *)git__malloc((size_t)(line_end - line_src) + 1);
573 if (line == NULL)
574 return NULL;
575
576 line_len = 0;
577 while (line_src < line_end) {
578
579 if (memcmp(line_src, LINEBREAK_UNIX, sizeof(LINEBREAK_UNIX)) == 0) {
580 line_src += sizeof(LINEBREAK_UNIX);
581 continue;
582 }
583
584 if (memcmp(line_src, LINEBREAK_WIN32, sizeof(LINEBREAK_WIN32)) == 0) {
585 line_src += sizeof(LINEBREAK_WIN32);
586 continue;
587 }
588
589 line[line_len++] = *line_src++;
590 }
591
592 line[line_len] = '\0';
593
594 while (--line_len >= 0 && isspace(line[line_len]))
595 line[line_len] = '\0';
596
597 if (*line_end == '\n')
598 line_end++;
599
600 if (*line_end == '\0')
601 cfg->reader.eof = 1;
602
603 cfg->reader.line_number++;
604 cfg->reader.read_ptr = line_end;
605
606 return line;
607 }
608
609 /*
610 * Consume a line, without storing it anywhere
611 */
612 void cfg_consume_line(git_config *cfg)
613 {
614 char *line_start, *line_end;
615 int len;
616
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');
622 }
623
624 len = line_end - line_start;
625
626 if (*line_end == '\n')
627 line_end++;
628
629 if (*line_end == '\0')
630 cfg->reader.eof = 1;
631
632 cfg->reader.line_number++;
633 cfg->reader.read_ptr = line_end;
634 }
635
636 static inline int config_keychar(int c)
637 {
638 return isalnum(c) || c == '-';
639 }
640
641 /*
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.
646 */
647 static char *build_varname(const char *section, const char *name)
648 {
649 char *varname;
650 int section_len, ret;
651 int name_len;
652 size_t total_len;
653
654 name_len = strlen(name);
655 section_len = strlen(section);
656 total_len = section_len + name_len + 2;
657 varname = malloc(total_len);
658 if(varname == NULL)
659 return NULL;
660
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, '.');
664 if (dot != NULL)
665 git__strtolower(dot);
666 }
667
668 return varname;
669 }
670
671 static int parse_section_header_ext(const char *line, const char *base_name, char **section_name)
672 {
673 int buf_len, total_len, pos, rpos;
674 int c, ret;
675 char *subsection, *first_quote, *last_quote;
676 int error = GIT_SUCCESS;
677 int quote_marks;
678 /*
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.
682 */
683
684 first_quote = strchr(line, '"');
685 last_quote = strrchr(line, '"');
686
687 if (last_quote - first_quote == 0)
688 return GIT_EOBJCORRUPTED;
689
690 buf_len = last_quote - first_quote + 2;
691
692 subsection = git__malloc(buf_len + 2);
693 if (subsection == NULL)
694 return GIT_ENOMEM;
695
696 pos = 0;
697 rpos = 0;
698 quote_marks = 0;
699
700 line = first_quote;
701 c = line[rpos++];
702
703 /*
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
706 */
707 do {
708 switch (c) {
709 case '"':
710 if (quote_marks++ >= 2)
711 return GIT_EOBJCORRUPTED;
712 break;
713 case '\\':
714 c = line[rpos++];
715 switch (c) {
716 case '"':
717 case '\\':
718 break;
719 default:
720 error = GIT_EOBJCORRUPTED;
721 goto out;
722 }
723 default:
724 break;
725 }
726
727 subsection[pos++] = c;
728 } while ((c = line[rpos++]) != ']');
729
730 subsection[pos] = '\0';
731
732 total_len = strlen(base_name) + strlen(subsection) + 2;
733 *section_name = git__malloc(total_len);
734 if (*section_name == NULL) {
735 error = GIT_ENOMEM;
736 goto out;
737 }
738
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 */
742 error = GIT_ERROR;
743 goto out;
744 } else if (ret < 0) {
745 error = GIT_EOSERR;
746 goto out;
747 }
748
749 git__strntolower(*section_name, strchr(*section_name, ' ') - *section_name);
750
751 out:
752 free(subsection);
753
754 return error;
755 }
756
757 static int parse_section_header(git_config *cfg, char **section_out)
758 {
759 char *name, *name_end;
760 int name_length, c, pos;
761 int error = GIT_SUCCESS;
762 char *line;
763
764 line = cfg_readline(cfg);
765 if (line == NULL)
766 return GIT_ENOMEM;
767
768 /* find the end of the variable's name */
769 name_end = strchr(line, ']');
770 if (name_end == NULL)
771 return GIT_EOBJCORRUPTED;
772
773 name = (char *)git__malloc((size_t)(name_end - line) + 1);
774 if (name == NULL)
775 return GIT_EOBJCORRUPTED;
776
777 name_length = 0;
778 pos = 0;
779
780 /* Make sure we were given a section header */
781 c = line[pos++];
782 if (c != '[') {
783 error = GIT_EOBJCORRUPTED;
784 goto error;
785 }
786
787 c = line[pos++];
788
789 do {
790 if (cfg->reader.eof){
791 error = GIT_EOBJCORRUPTED;
792 goto error;
793 }
794
795 if (isspace(c)){
796 name[name_length] = '\0';
797 error = parse_section_header_ext(line, name, section_out);
798 free(line);
799 free(name);
800 return error;
801 }
802
803 if (!config_keychar(c) && c != '.') {
804 error = GIT_EOBJCORRUPTED;
805 goto error;
806 }
807
808 name[name_length++] = tolower(c);
809
810 } while ((c = line[pos++]) != ']');
811
812 name[name_length] = 0;
813 free(line);
814 git__strtolower(name);
815 *section_out = name;
816 return GIT_SUCCESS;
817
818 error:
819 free(line);
820 free(name);
821 return error;
822 }
823
824 static int skip_bom(git_config *cfg)
825 {
826 static const unsigned char *utf8_bom = "\xef\xbb\xbf";
827
828 if (memcmp(cfg->reader.read_ptr, utf8_bom, sizeof(utf8_bom)) == 0)
829 cfg->reader.read_ptr += sizeof(utf8_bom);
830
831 /* TODO: the reference implementation does pretty stupid
832 shit with the BoM
833 */
834
835 return GIT_SUCCESS;
836 }
837
838 /*
839 (* basic types *)
840 digit = "0".."9"
841 integer = digit { digit }
842 alphabet = "a".."z" + "A" .. "Z"
843
844 section_char = alphabet | "." | "-"
845 extension_char = (* any character except newline *)
846 any_char = (* any character *)
847 variable_char = "alphabet" | "-"
848
849
850 (* actual grammar *)
851 config = { section }
852
853 section = header { definition }
854
855 header = "[" section [subsection | subsection_ext] "]"
856
857 subsection = "." section
858 subsection_ext = "\"" extension "\""
859
860 section = section_char { section_char }
861 extension = extension_char { extension_char }
862
863 definition = variable_name ["=" variable_value] "\n"
864
865 variable_name = variable_char { variable_char }
866 variable_value = string | boolean | integer
867
868 string = quoted_string | plain_string
869 quoted_string = "\"" plain_string "\""
870 plain_string = { any_char }
871
872 boolean = boolean_true | boolean_false
873 boolean_true = "yes" | "1" | "true" | "on"
874 boolean_false = "no" | "0" | "false" | "off"
875 */
876
877 static void strip_comments(char *line)
878 {
879 int quote_count = 0;
880 char *ptr;
881
882 for (ptr = line; *ptr; ++ptr) {
883 if (ptr[0] == '"' && ptr > line && ptr[-1] != '\\')
884 quote_count++;
885
886 if ((ptr[0] == ';' || ptr[0] == '#') && (quote_count % 2) == 0) {
887 ptr[0] = '\0';
888 break;
889 }
890 }
891
892 if (isspace(ptr[-1])) {
893 /* TODO skip whitespace */
894 }
895 }
896
897 static int config_parse(git_config *cfg_file)
898 {
899 int error = GIT_SUCCESS, c;
900 char *current_section = NULL;
901 char *var_name;
902 char *var_value;
903 char *full_name;
904
905 /* Initialise the reading position */
906 cfg_file->reader.read_ptr = cfg_file->reader.buffer.data;
907 cfg_file->reader.eof = 0;
908
909 skip_bom(cfg_file);
910
911 while (error == GIT_SUCCESS && !cfg_file->reader.eof) {
912
913 c = cfg_peek(cfg_file, SKIP_WHITESPACE);
914
915 switch (c) {
916 case '\0': /* We've arrived at the end of the file */
917 break;
918
919 case '[': /* section header, new section begins */
920 free(current_section);
921 error = parse_section_header(cfg_file, &current_section);
922 break;
923
924 case ';':
925 case '#':
926 cfg_consume_line(cfg_file);
927 break;
928
929 default: /* assume variable declaration */
930 error = parse_variable(cfg_file, &var_name, &var_value);
931
932 if (error < GIT_SUCCESS)
933 break;
934
935 full_name = build_varname(current_section, var_name);
936 if (full_name == NULL) {
937 error = GIT_ENOMEM;
938 free(var_name);
939 free(var_value);
940 break;
941 }
942
943 config_set(cfg_file, full_name, var_value);
944 free(var_name);
945 free(var_value);
946 free(full_name);
947
948 break;
949 }
950 }
951
952 if (current_section)
953 free(current_section);
954
955 return error;
956 }
957
958 static int is_multiline_var(const char *str)
959 {
960 char *end = strrchr(str, '\0') - 1;
961
962 while (isspace(*end))
963 --end;
964
965 return *end == '\\';
966 }
967
968 static int parse_multiline_variable(git_config *cfg, const char *first, char **out)
969 {
970 char *line = NULL, *end;
971 int error = GIT_SUCCESS, len, ret;
972 char *buf;
973
974 /* Check that the next line exists */
975 line = cfg_readline(cfg);
976 if (line == NULL)
977 return GIT_ENOMEM;
978
979 /* We've reached the end of the file, there is input missing */
980 if (line[0] == '\0') {
981 error = GIT_EOBJCORRUPTED;
982 goto out;
983 }
984
985 strip_comments(line);
986
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);
990 goto out;
991 }
992
993 /* Find the continuation character '\' and strip the whitespace */
994 end = strrchr(first, '\\');
995 while (isspace(end[-1]))
996 --end;
997
998 *end = '\0'; /* Terminate the string here */
999
1000 len = strlen(first) + strlen(line) + 2;
1001 buf = git__malloc(len);
1002 if (buf == NULL) {
1003 error = GIT_ENOMEM;
1004 goto out;
1005 }
1006
1007 ret = snprintf(buf, len, "%s %s", first, line);
1008 if (ret < 0) {
1009 error = GIT_EOSERR;
1010 free(buf);
1011 goto out;
1012 }
1013
1014 /*
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
1017 * ourselves.
1018 */
1019 if (is_multiline_var(buf)) {
1020 char *final_val;
1021 error = parse_multiline_variable(cfg, buf, &final_val);
1022 free(buf);
1023 buf = final_val;
1024 }
1025
1026 *out = buf;
1027
1028 out:
1029 free(line);
1030 return error;
1031 }
1032
1033 static int parse_variable(git_config *cfg, char **var_name, char **var_value)
1034 {
1035 char *tmp;
1036 int error = GIT_SUCCESS;
1037 const char *var_end = NULL;
1038 const char *value_start = NULL;
1039 char *line;
1040
1041 line = cfg_readline(cfg);
1042 if (line == NULL)
1043 return GIT_ENOMEM;
1044
1045 strip_comments(line);
1046
1047 var_end = strchr(line, '=');
1048
1049 if (var_end == NULL)
1050 var_end = strchr(line, '\0');
1051 else
1052 value_start = var_end + 1;
1053
1054 if (isspace(var_end[-1])) {
1055 do var_end--;
1056 while (isspace(var_end[0]));
1057 }
1058
1059 tmp = strndup(line, var_end - line + 1);
1060 if (tmp == NULL) {
1061 error = GIT_ENOMEM;
1062 goto out;
1063 }
1064
1065 *var_name = tmp;
1066
1067 /*
1068 * Now, let's try to parse the value
1069 */
1070 if (value_start != NULL) {
1071
1072 while (isspace(value_start[0]))
1073 value_start++;
1074
1075 if (value_start[0] == '\0')
1076 goto out;
1077
1078 if (is_multiline_var(value_start)) {
1079 error = parse_multiline_variable(cfg, value_start, var_value);
1080 if (error < GIT_SUCCESS)
1081 free(*var_name);
1082 goto out;
1083 }
1084
1085 tmp = strdup(value_start);
1086 if (tmp == NULL) {
1087 free(*var_name);
1088 error = GIT_ENOMEM;
1089 goto out;
1090 }
1091
1092 *var_value = tmp;
1093 } else {
1094 /* If thre is no value, boolean true is assumed */
1095 *var_value = NULL;
1096 }
1097
1098 out:
1099 free(line);
1100 return error;
1101 }