]> git.proxmox.com Git - libgit2.git/blob - src/config_file.c
remote: create FETCH_HEAD with a refspecless remote
[libgit2.git] / src / config_file.c
1 /*
2 * Copyright (C) the libgit2 contributors. All rights reserved.
3 *
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.
6 */
7
8 #include "common.h"
9 #include "config.h"
10 #include "fileops.h"
11 #include "filebuf.h"
12 #include "buffer.h"
13 #include "buf_text.h"
14 #include "git2/config.h"
15 #include "git2/sys/config.h"
16 #include "git2/types.h"
17 #include "strmap.h"
18 #include "array.h"
19
20 #include <ctype.h>
21 #include <sys/types.h>
22 #include <regex.h>
23
24 GIT__USE_STRMAP;
25
26 typedef struct cvar_t {
27 struct cvar_t *next;
28 git_config_entry *entry;
29 int included; /* whether this is part of [include] */
30 } cvar_t;
31
32 typedef struct git_config_file_iter {
33 git_config_iterator parent;
34 git_strmap_iter iter;
35 cvar_t* next_var;
36 } git_config_file_iter;
37
38 /* Max depth for [include] directives */
39 #define MAX_INCLUDE_DEPTH 10
40
41 #define CVAR_LIST_HEAD(list) ((list)->head)
42
43 #define CVAR_LIST_TAIL(list) ((list)->tail)
44
45 #define CVAR_LIST_NEXT(var) ((var)->next)
46
47 #define CVAR_LIST_EMPTY(list) ((list)->head == NULL)
48
49 #define CVAR_LIST_APPEND(list, var) do {\
50 if (CVAR_LIST_EMPTY(list)) {\
51 CVAR_LIST_HEAD(list) = CVAR_LIST_TAIL(list) = var;\
52 } else {\
53 CVAR_LIST_NEXT(CVAR_LIST_TAIL(list)) = var;\
54 CVAR_LIST_TAIL(list) = var;\
55 }\
56 } while(0)
57
58 #define CVAR_LIST_REMOVE_HEAD(list) do {\
59 CVAR_LIST_HEAD(list) = CVAR_LIST_NEXT(CVAR_LIST_HEAD(list));\
60 } while(0)
61
62 #define CVAR_LIST_REMOVE_AFTER(var) do {\
63 CVAR_LIST_NEXT(var) = CVAR_LIST_NEXT(CVAR_LIST_NEXT(var));\
64 } while(0)
65
66 #define CVAR_LIST_FOREACH(list, iter)\
67 for ((iter) = CVAR_LIST_HEAD(list);\
68 (iter) != NULL;\
69 (iter) = CVAR_LIST_NEXT(iter))
70
71 /*
72 * Inspired by the FreeBSD functions
73 */
74 #define CVAR_LIST_FOREACH_SAFE(start, iter, tmp)\
75 for ((iter) = CVAR_LIST_HEAD(vars);\
76 (iter) && (((tmp) = CVAR_LIST_NEXT(iter) || 1));\
77 (iter) = (tmp))
78
79 struct reader {
80 time_t file_mtime;
81 size_t file_size;
82 char *file_path;
83 git_buf buffer;
84 char *read_ptr;
85 int line_number;
86 int eof;
87 };
88
89 typedef struct {
90 git_config_backend parent;
91
92 git_strmap *values;
93
94 git_array_t(struct reader) readers;
95
96 char *file_path;
97
98 git_config_level_t level;
99 } diskfile_backend;
100
101 static int config_parse(diskfile_backend *cfg_file, struct reader *reader, git_config_level_t level, int depth);
102 static int parse_variable(struct reader *reader, char **var_name, char **var_value);
103 static int config_write(diskfile_backend *cfg, const char *key, const regex_t *preg, const char *value);
104 static char *escape_value(const char *ptr);
105
106 static void set_parse_error(struct reader *reader, int col, const char *error_str)
107 {
108 giterr_set(GITERR_CONFIG, "Failed to parse config file: %s (in %s:%d, column %d)",
109 error_str, reader->file_path, reader->line_number, col);
110 }
111
112 static void cvar_free(cvar_t *var)
113 {
114 if (var == NULL)
115 return;
116
117 git__free((char*)var->entry->name);
118 git__free((char *)var->entry->value);
119 git__free(var->entry);
120 git__free(var);
121 }
122
123 static int cvar_length(cvar_t *var)
124 {
125 int length = 0;
126
127 while (var) {
128 length++;
129 var = var->next;
130 }
131
132 return length;
133 }
134
135 int git_config_file_normalize_section(char *start, char *end)
136 {
137 char *scan;
138
139 if (start == end)
140 return GIT_EINVALIDSPEC;
141
142 /* Validate and downcase range */
143 for (scan = start; *scan; ++scan) {
144 if (end && scan >= end)
145 break;
146 if (isalnum(*scan))
147 *scan = (char)tolower(*scan);
148 else if (*scan != '-' || scan == start)
149 return GIT_EINVALIDSPEC;
150 }
151
152 if (scan == start)
153 return GIT_EINVALIDSPEC;
154
155 return 0;
156 }
157
158 static void free_vars(git_strmap *values)
159 {
160 cvar_t *var = NULL;
161
162 if (values == NULL)
163 return;
164
165 git_strmap_foreach_value(values, var,
166 while (var != NULL) {
167 cvar_t *next = CVAR_LIST_NEXT(var);
168 cvar_free(var);
169 var = next;
170 });
171
172 git_strmap_free(values);
173 }
174
175 static int config_open(git_config_backend *cfg, git_config_level_t level)
176 {
177 int res;
178 struct reader *reader;
179 diskfile_backend *b = (diskfile_backend *)cfg;
180
181 b->level = level;
182
183 b->values = git_strmap_alloc();
184 GITERR_CHECK_ALLOC(b->values);
185
186 git_array_init(b->readers);
187 reader = git_array_alloc(b->readers);
188 memset(reader, 0, sizeof(struct reader));
189
190 reader->file_path = git__strdup(b->file_path);
191 GITERR_CHECK_ALLOC(reader->file_path);
192
193 git_buf_init(&reader->buffer, 0);
194 res = git_futils_readbuffer_updated(
195 &reader->buffer, b->file_path, &reader->file_mtime, &reader->file_size, NULL);
196
197 /* It's fine if the file doesn't exist */
198 if (res == GIT_ENOTFOUND)
199 return 0;
200
201 if (res < 0 || (res = config_parse(b, reader, level, 0)) < 0) {
202 free_vars(b->values);
203 b->values = NULL;
204 }
205
206 reader = git_array_get(b->readers, 0);
207 git_buf_free(&reader->buffer);
208 return res;
209 }
210
211 static int config_refresh(git_config_backend *cfg)
212 {
213 int res = 0, updated = 0, any_updated = 0;
214 diskfile_backend *b = (diskfile_backend *)cfg;
215 git_strmap *old_values;
216 struct reader *reader;
217 uint32_t i;
218
219 for (i = 0; i < git_array_size(b->readers); i++) {
220 reader = git_array_get(b->readers, i);
221 res = git_futils_readbuffer_updated(
222 &reader->buffer, reader->file_path, &reader->file_mtime, &reader->file_size, &updated);
223
224 if (res < 0)
225 return (res == GIT_ENOTFOUND) ? 0 : res;
226
227 if (updated)
228 any_updated = 1;
229 }
230
231 if (!any_updated)
232 return (res == GIT_ENOTFOUND) ? 0 : res;
233
234 /* need to reload - store old values and prep for reload */
235 old_values = b->values;
236 b->values = git_strmap_alloc();
237 GITERR_CHECK_ALLOC(b->values);
238
239 if ((res = config_parse(b, reader, b->level, 0)) < 0) {
240 free_vars(b->values);
241 b->values = old_values;
242 } else {
243 free_vars(old_values);
244 }
245
246 git_buf_free(&reader->buffer);
247 return res;
248 }
249
250 static void backend_free(git_config_backend *_backend)
251 {
252 diskfile_backend *backend = (diskfile_backend *)_backend;
253 uint32_t i;
254
255 if (backend == NULL)
256 return;
257
258 for (i = 0; i < git_array_size(backend->readers); i++) {
259 struct reader *r = git_array_get(backend->readers, i);
260 git__free(r->file_path);
261 }
262 git_array_clear(backend->readers);
263
264 git__free(backend->file_path);
265 free_vars(backend->values);
266 git__free(backend);
267 }
268
269 static void config_iterator_free(
270 git_config_iterator* iter)
271 {
272 git__free(iter);
273 }
274
275 static int config_iterator_next(
276 git_config_entry **entry,
277 git_config_iterator *iter)
278 {
279 git_config_file_iter *it = (git_config_file_iter *) iter;
280 diskfile_backend *b = (diskfile_backend *) it->parent.backend;
281 int err = 0;
282 cvar_t * var;
283
284 if (it->next_var == NULL) {
285 err = git_strmap_next((void**) &var, &(it->iter), b->values);
286 } else {
287 var = it->next_var;
288 }
289
290 if (err < 0) {
291 it->next_var = NULL;
292 return err;
293 }
294
295 *entry = var->entry;
296 it->next_var = CVAR_LIST_NEXT(var);
297
298 return 0;
299 }
300
301 static int config_iterator_new(
302 git_config_iterator **iter,
303 struct git_config_backend* backend)
304 {
305 diskfile_backend *b = (diskfile_backend *)backend;
306 git_config_file_iter *it = git__calloc(1, sizeof(git_config_file_iter));
307
308 GIT_UNUSED(b);
309
310 GITERR_CHECK_ALLOC(it);
311
312 it->parent.backend = backend;
313 it->iter = git_strmap_begin(b->values);
314 it->next_var = NULL;
315
316 it->parent.next = config_iterator_next;
317 it->parent.free = config_iterator_free;
318 *iter = (git_config_iterator *) it;
319
320 return 0;
321 }
322
323 static int config_set(git_config_backend *cfg, const char *name, const char *value)
324 {
325 cvar_t *var = NULL, *old_var = NULL;
326 diskfile_backend *b = (diskfile_backend *)cfg;
327 char *key, *esc_value = NULL;
328 khiter_t pos;
329 int rval, ret;
330
331 if ((rval = git_config__normalize_name(name, &key)) < 0)
332 return rval;
333
334 /*
335 * Try to find it in the existing values and update it if it
336 * only has one value.
337 */
338 pos = git_strmap_lookup_index(b->values, key);
339 if (git_strmap_valid_index(b->values, pos)) {
340 cvar_t *existing = git_strmap_value_at(b->values, pos);
341 char *tmp = NULL;
342
343 git__free(key);
344
345 if (existing->next != NULL) {
346 giterr_set(GITERR_CONFIG, "Multivar incompatible with simple set");
347 return -1;
348 }
349
350 /* don't update if old and new values already match */
351 if ((!existing->entry->value && !value) ||
352 (existing->entry->value && value && !strcmp(existing->entry->value, value)))
353 return 0;
354
355 if (value) {
356 tmp = git__strdup(value);
357 GITERR_CHECK_ALLOC(tmp);
358 esc_value = escape_value(value);
359 GITERR_CHECK_ALLOC(esc_value);
360 }
361
362 git__free((void *)existing->entry->value);
363 existing->entry->value = tmp;
364
365 ret = config_write(b, existing->entry->name, NULL, esc_value);
366
367 git__free(esc_value);
368 return ret;
369 }
370
371 var = git__malloc(sizeof(cvar_t));
372 GITERR_CHECK_ALLOC(var);
373 memset(var, 0x0, sizeof(cvar_t));
374 var->entry = git__malloc(sizeof(git_config_entry));
375 GITERR_CHECK_ALLOC(var->entry);
376 memset(var->entry, 0x0, sizeof(git_config_entry));
377
378 var->entry->name = key;
379 var->entry->value = NULL;
380
381 if (value) {
382 var->entry->value = git__strdup(value);
383 GITERR_CHECK_ALLOC(var->entry->value);
384 esc_value = escape_value(value);
385 GITERR_CHECK_ALLOC(esc_value);
386 }
387
388 if (config_write(b, key, NULL, esc_value) < 0) {
389 git__free(esc_value);
390 cvar_free(var);
391 return -1;
392 }
393
394 git__free(esc_value);
395 git_strmap_insert2(b->values, key, var, old_var, rval);
396 if (rval < 0)
397 return -1;
398 if (old_var != NULL)
399 cvar_free(old_var);
400
401 return 0;
402 }
403
404 /*
405 * Internal function that actually gets the value in string form
406 */
407 static int config_get(const git_config_backend *cfg, const char *name, const git_config_entry **out)
408 {
409 diskfile_backend *b = (diskfile_backend *)cfg;
410 char *key;
411 khiter_t pos;
412 int error;
413 cvar_t *var;
414
415 if ((error = git_config__normalize_name(name, &key)) < 0)
416 return error;
417
418 pos = git_strmap_lookup_index(b->values, key);
419 git__free(key);
420
421 /* no error message; the config system will write one */
422 if (!git_strmap_valid_index(b->values, pos))
423 return GIT_ENOTFOUND;
424
425 var = git_strmap_value_at(b->values, pos);
426 while (var->next)
427 var = var->next;
428
429 *out = var->entry;
430
431 return 0;
432 }
433
434 static int config_set_multivar(
435 git_config_backend *cfg, const char *name, const char *regexp, const char *value)
436 {
437 int replaced = 0;
438 cvar_t *var, *newvar;
439 diskfile_backend *b = (diskfile_backend *)cfg;
440 char *key;
441 regex_t preg;
442 int result;
443 khiter_t pos;
444
445 assert(regexp);
446
447 if ((result = git_config__normalize_name(name, &key)) < 0)
448 return result;
449
450 pos = git_strmap_lookup_index(b->values, key);
451 if (!git_strmap_valid_index(b->values, pos)) {
452 /* If we don't have it, behave like a normal set */
453 result = config_set(cfg, name, value);
454 git__free(key);
455 return result;
456 }
457
458 var = git_strmap_value_at(b->values, pos);
459
460 result = regcomp(&preg, regexp, REG_EXTENDED);
461 if (result < 0) {
462 git__free(key);
463 giterr_set_regex(&preg, result);
464 regfree(&preg);
465 return -1;
466 }
467
468 for (;;) {
469 if (regexec(&preg, var->entry->value, 0, NULL, 0) == 0) {
470 char *tmp = git__strdup(value);
471 GITERR_CHECK_ALLOC(tmp);
472
473 git__free((void *)var->entry->value);
474 var->entry->value = tmp;
475 replaced = 1;
476 }
477
478 if (var->next == NULL)
479 break;
480
481 var = var->next;
482 }
483
484 /* If we've reached the end of the variables and we haven't found it yet, we need to append it */
485 if (!replaced) {
486 newvar = git__malloc(sizeof(cvar_t));
487 GITERR_CHECK_ALLOC(newvar);
488 memset(newvar, 0x0, sizeof(cvar_t));
489 newvar->entry = git__malloc(sizeof(git_config_entry));
490 GITERR_CHECK_ALLOC(newvar->entry);
491 memset(newvar->entry, 0x0, sizeof(git_config_entry));
492
493 newvar->entry->name = git__strdup(var->entry->name);
494 GITERR_CHECK_ALLOC(newvar->entry->name);
495
496 newvar->entry->value = git__strdup(value);
497 GITERR_CHECK_ALLOC(newvar->entry->value);
498
499 newvar->entry->level = var->entry->level;
500
501 var->next = newvar;
502 }
503
504 result = config_write(b, key, &preg, value);
505
506 git__free(key);
507 regfree(&preg);
508
509 return result;
510 }
511
512 static int config_delete(git_config_backend *cfg, const char *name)
513 {
514 cvar_t *var;
515 diskfile_backend *b = (diskfile_backend *)cfg;
516 char *key;
517 int result;
518 khiter_t pos;
519
520 if ((result = git_config__normalize_name(name, &key)) < 0)
521 return result;
522
523 pos = git_strmap_lookup_index(b->values, key);
524 git__free(key);
525
526 if (!git_strmap_valid_index(b->values, pos)) {
527 giterr_set(GITERR_CONFIG, "Could not find key '%s' to delete", name);
528 return GIT_ENOTFOUND;
529 }
530
531 var = git_strmap_value_at(b->values, pos);
532
533 if (var->next != NULL) {
534 giterr_set(GITERR_CONFIG, "Cannot delete multivar with a single delete");
535 return -1;
536 }
537
538 git_strmap_delete_at(b->values, pos);
539
540 result = config_write(b, var->entry->name, NULL, NULL);
541
542 cvar_free(var);
543 return result;
544 }
545
546 static int config_delete_multivar(git_config_backend *cfg, const char *name, const char *regexp)
547 {
548 cvar_t *var, *prev = NULL, *new_head = NULL;
549 cvar_t **to_delete;
550 int to_delete_idx;
551 diskfile_backend *b = (diskfile_backend *)cfg;
552 char *key;
553 regex_t preg;
554 int result;
555 khiter_t pos;
556
557 if ((result = git_config__normalize_name(name, &key)) < 0)
558 return result;
559
560 pos = git_strmap_lookup_index(b->values, key);
561
562 if (!git_strmap_valid_index(b->values, pos)) {
563 giterr_set(GITERR_CONFIG, "Could not find key '%s' to delete", name);
564 git__free(key);
565 return GIT_ENOTFOUND;
566 }
567
568 var = git_strmap_value_at(b->values, pos);
569
570 result = regcomp(&preg, regexp, REG_EXTENDED);
571 if (result < 0) {
572 git__free(key);
573 giterr_set_regex(&preg, result);
574 regfree(&preg);
575 return -1;
576 }
577
578 to_delete = git__calloc(cvar_length(var), sizeof(cvar_t *));
579 GITERR_CHECK_ALLOC(to_delete);
580 to_delete_idx = 0;
581
582 while (var != NULL) {
583 cvar_t *next = var->next;
584
585 if (regexec(&preg, var->entry->value, 0, NULL, 0) == 0) {
586 // If we are past the head, reattach previous node to next one,
587 // otherwise set the new head for the strmap.
588 if (prev != NULL) {
589 prev->next = next;
590 } else {
591 new_head = next;
592 }
593
594 to_delete[to_delete_idx++] = var;
595 } else {
596 prev = var;
597 }
598
599 var = next;
600 }
601
602 if (new_head != NULL) {
603 git_strmap_set_value_at(b->values, pos, new_head);
604 } else {
605 git_strmap_delete_at(b->values, pos);
606 }
607
608 if (to_delete_idx > 0)
609 result = config_write(b, key, &preg, NULL);
610
611 while (to_delete_idx-- > 0)
612 cvar_free(to_delete[to_delete_idx]);
613
614 git__free(key);
615 git__free(to_delete);
616 regfree(&preg);
617 return result;
618 }
619
620 int git_config_file__ondisk(git_config_backend **out, const char *path)
621 {
622 diskfile_backend *backend;
623
624 backend = git__calloc(1, sizeof(diskfile_backend));
625 GITERR_CHECK_ALLOC(backend);
626
627 backend->parent.version = GIT_CONFIG_BACKEND_VERSION;
628
629 backend->file_path = git__strdup(path);
630 GITERR_CHECK_ALLOC(backend->file_path);
631
632 backend->parent.open = config_open;
633 backend->parent.get = config_get;
634 backend->parent.set = config_set;
635 backend->parent.set_multivar = config_set_multivar;
636 backend->parent.del = config_delete;
637 backend->parent.del_multivar = config_delete_multivar;
638 backend->parent.iterator = config_iterator_new;
639 backend->parent.refresh = config_refresh;
640 backend->parent.free = backend_free;
641
642 *out = (git_config_backend *)backend;
643
644 return 0;
645 }
646
647 static int reader_getchar_raw(struct reader *reader)
648 {
649 int c;
650
651 c = *reader->read_ptr++;
652
653 /*
654 Win 32 line breaks: if we find a \r\n sequence,
655 return only the \n as a newline
656 */
657 if (c == '\r' && *reader->read_ptr == '\n') {
658 reader->read_ptr++;
659 c = '\n';
660 }
661
662 if (c == '\n')
663 reader->line_number++;
664
665 if (c == 0) {
666 reader->eof = 1;
667 c = '\n';
668 }
669
670 return c;
671 }
672
673 #define SKIP_WHITESPACE (1 << 1)
674 #define SKIP_COMMENTS (1 << 2)
675
676 static int reader_getchar(struct reader *reader, int flags)
677 {
678 const int skip_whitespace = (flags & SKIP_WHITESPACE);
679 const int skip_comments = (flags & SKIP_COMMENTS);
680 int c;
681
682 assert(reader->read_ptr);
683
684 do {
685 c = reader_getchar_raw(reader);
686 } while (skip_whitespace && git__isspace(c) &&
687 !reader->eof);
688
689 if (skip_comments && (c == '#' || c == ';')) {
690 do {
691 c = reader_getchar_raw(reader);
692 } while (c != '\n');
693 }
694
695 return c;
696 }
697
698 /*
699 * Read the next char, but don't move the reading pointer.
700 */
701 static int reader_peek(struct reader *reader, int flags)
702 {
703 void *old_read_ptr;
704 int old_lineno, old_eof;
705 int ret;
706
707 assert(reader->read_ptr);
708
709 old_read_ptr = reader->read_ptr;
710 old_lineno = reader->line_number;
711 old_eof = reader->eof;
712
713 ret = reader_getchar(reader, flags);
714
715 reader->read_ptr = old_read_ptr;
716 reader->line_number = old_lineno;
717 reader->eof = old_eof;
718
719 return ret;
720 }
721
722 /*
723 * Read and consume a line, returning it in newly-allocated memory.
724 */
725 static char *reader_readline(struct reader *reader, bool skip_whitespace)
726 {
727 char *line = NULL;
728 char *line_src, *line_end;
729 size_t line_len;
730
731 line_src = reader->read_ptr;
732
733 if (skip_whitespace) {
734 /* Skip empty empty lines */
735 while (git__isspace(*line_src))
736 ++line_src;
737 }
738
739 line_end = strchr(line_src, '\n');
740
741 /* no newline at EOF */
742 if (line_end == NULL)
743 line_end = strchr(line_src, 0);
744
745 line_len = line_end - line_src;
746
747 line = git__malloc(line_len + 1);
748 if (line == NULL)
749 return NULL;
750
751 memcpy(line, line_src, line_len);
752
753 do line[line_len] = '\0';
754 while (line_len-- > 0 && git__isspace(line[line_len]));
755
756 if (*line_end == '\n')
757 line_end++;
758
759 if (*line_end == '\0')
760 reader->eof = 1;
761
762 reader->line_number++;
763 reader->read_ptr = line_end;
764
765 return line;
766 }
767
768 /*
769 * Consume a line, without storing it anywhere
770 */
771 static void reader_consume_line(struct reader *reader)
772 {
773 char *line_start, *line_end;
774
775 line_start = reader->read_ptr;
776 line_end = strchr(line_start, '\n');
777 /* No newline at EOF */
778 if(line_end == NULL){
779 line_end = strchr(line_start, '\0');
780 }
781
782 if (*line_end == '\n')
783 line_end++;
784
785 if (*line_end == '\0')
786 reader->eof = 1;
787
788 reader->line_number++;
789 reader->read_ptr = line_end;
790 }
791
792 GIT_INLINE(int) config_keychar(int c)
793 {
794 return isalnum(c) || c == '-';
795 }
796
797 static int parse_section_header_ext(struct reader *reader, const char *line, const char *base_name, char **section_name)
798 {
799 int c, rpos;
800 char *first_quote, *last_quote;
801 git_buf buf = GIT_BUF_INIT;
802 /*
803 * base_name is what came before the space. We should be at the
804 * first quotation mark, except for now, line isn't being kept in
805 * sync so we only really use it to calculate the length.
806 */
807
808 first_quote = strchr(line, '"');
809 last_quote = strrchr(line, '"');
810
811 if (last_quote - first_quote == 0) {
812 set_parse_error(reader, 0, "Missing closing quotation mark in section header");
813 return -1;
814 }
815
816 git_buf_grow(&buf, strlen(base_name) + last_quote - first_quote + 2);
817 git_buf_printf(&buf, "%s.", base_name);
818
819 rpos = 0;
820
821 line = first_quote;
822 c = line[++rpos];
823
824 /*
825 * At the end of each iteration, whatever is stored in c will be
826 * added to the string. In case of error, jump to out
827 */
828 do {
829
830 switch (c) {
831 case 0:
832 set_parse_error(reader, 0, "Unexpected end-of-line in section header");
833 git_buf_free(&buf);
834 return -1;
835
836 case '"':
837 goto end_parse;
838
839 case '\\':
840 c = line[++rpos];
841
842 if (c == 0) {
843 set_parse_error(reader, rpos, "Unexpected end-of-line in section header");
844 git_buf_free(&buf);
845 return -1;
846 }
847
848 default:
849 break;
850 }
851
852 git_buf_putc(&buf, (char)c);
853 c = line[++rpos];
854 } while (line + rpos < last_quote);
855
856 end_parse:
857 if (line[rpos] != '"' || line[rpos + 1] != ']') {
858 set_parse_error(reader, rpos, "Unexpected text after closing quotes");
859 git_buf_free(&buf);
860 return -1;
861 }
862
863 *section_name = git_buf_detach(&buf);
864 return 0;
865 }
866
867 static int parse_section_header(struct reader *reader, char **section_out)
868 {
869 char *name, *name_end;
870 int name_length, c, pos;
871 int result;
872 char *line;
873
874 line = reader_readline(reader, true);
875 if (line == NULL)
876 return -1;
877
878 /* find the end of the variable's name */
879 name_end = strrchr(line, ']');
880 if (name_end == NULL) {
881 git__free(line);
882 set_parse_error(reader, 0, "Missing ']' in section header");
883 return -1;
884 }
885
886 name = (char *)git__malloc((size_t)(name_end - line) + 1);
887 GITERR_CHECK_ALLOC(name);
888
889 name_length = 0;
890 pos = 0;
891
892 /* Make sure we were given a section header */
893 c = line[pos++];
894 assert(c == '[');
895
896 c = line[pos++];
897
898 do {
899 if (git__isspace(c)){
900 name[name_length] = '\0';
901 result = parse_section_header_ext(reader, line, name, section_out);
902 git__free(line);
903 git__free(name);
904 return result;
905 }
906
907 if (!config_keychar(c) && c != '.') {
908 set_parse_error(reader, pos, "Unexpected character in header");
909 goto fail_parse;
910 }
911
912 name[name_length++] = (char) tolower(c);
913
914 } while ((c = line[pos++]) != ']');
915
916 if (line[pos - 1] != ']') {
917 set_parse_error(reader, pos, "Unexpected end of file");
918 goto fail_parse;
919 }
920
921 git__free(line);
922
923 name[name_length] = 0;
924 *section_out = name;
925
926 return 0;
927
928 fail_parse:
929 git__free(line);
930 git__free(name);
931 return -1;
932 }
933
934 static int skip_bom(struct reader *reader)
935 {
936 git_bom_t bom;
937 int bom_offset = git_buf_text_detect_bom(&bom,
938 &reader->buffer, reader->read_ptr - reader->buffer.ptr);
939
940 if (bom == GIT_BOM_UTF8)
941 reader->read_ptr += bom_offset;
942
943 /* TODO: reference implementation is pretty stupid with BoM */
944
945 return 0;
946 }
947
948 /*
949 (* basic types *)
950 digit = "0".."9"
951 integer = digit { digit }
952 alphabet = "a".."z" + "A" .. "Z"
953
954 section_char = alphabet | "." | "-"
955 extension_char = (* any character except newline *)
956 any_char = (* any character *)
957 variable_char = "alphabet" | "-"
958
959
960 (* actual grammar *)
961 config = { section }
962
963 section = header { definition }
964
965 header = "[" section [subsection | subsection_ext] "]"
966
967 subsection = "." section
968 subsection_ext = "\"" extension "\""
969
970 section = section_char { section_char }
971 extension = extension_char { extension_char }
972
973 definition = variable_name ["=" variable_value] "\n"
974
975 variable_name = variable_char { variable_char }
976 variable_value = string | boolean | integer
977
978 string = quoted_string | plain_string
979 quoted_string = "\"" plain_string "\""
980 plain_string = { any_char }
981
982 boolean = boolean_true | boolean_false
983 boolean_true = "yes" | "1" | "true" | "on"
984 boolean_false = "no" | "0" | "false" | "off"
985 */
986
987 static int strip_comments(char *line, int in_quotes)
988 {
989 int quote_count = in_quotes;
990 char *ptr;
991
992 for (ptr = line; *ptr; ++ptr) {
993 if (ptr[0] == '"' && ptr > line && ptr[-1] != '\\')
994 quote_count++;
995
996 if ((ptr[0] == ';' || ptr[0] == '#') && (quote_count % 2) == 0) {
997 ptr[0] = '\0';
998 break;
999 }
1000 }
1001
1002 /* skip any space at the end */
1003 if (ptr > line && git__isspace(ptr[-1])) {
1004 ptr--;
1005 }
1006 ptr[0] = '\0';
1007
1008 return quote_count;
1009 }
1010
1011 static int included_path(git_buf *out, const char *dir, const char *path)
1012 {
1013 /* From the user's home */
1014 if (path[0] == '~' && path[1] == '/')
1015 return git_futils_find_global_file(out, &path[1]);
1016
1017 return git_path_join_unrooted(out, path, dir, NULL);
1018 }
1019
1020 static int config_parse(diskfile_backend *cfg_file, struct reader *reader, git_config_level_t level, int depth)
1021 {
1022 int c;
1023 char *current_section = NULL;
1024 char *var_name;
1025 char *var_value;
1026 cvar_t *var, *existing;
1027 git_buf buf = GIT_BUF_INIT;
1028 int result = 0;
1029 khiter_t pos;
1030 uint32_t reader_idx;
1031
1032 if (depth >= MAX_INCLUDE_DEPTH) {
1033 giterr_set(GITERR_CONFIG, "Maximum config include depth reached");
1034 return -1;
1035 }
1036
1037 reader_idx = git_array_size(cfg_file->readers) - 1;
1038 /* Initialize the reading position */
1039 reader->read_ptr = reader->buffer.ptr;
1040 reader->eof = 0;
1041
1042 /* If the file is empty, there's nothing for us to do */
1043 if (*reader->read_ptr == '\0')
1044 return 0;
1045
1046 skip_bom(reader);
1047
1048 while (result == 0 && !reader->eof) {
1049
1050 c = reader_peek(reader, SKIP_WHITESPACE);
1051
1052 switch (c) {
1053 case '\n': /* EOF when peeking, set EOF in the reader to exit the loop */
1054 reader->eof = 1;
1055 break;
1056
1057 case '[': /* section header, new section begins */
1058 git__free(current_section);
1059 current_section = NULL;
1060 result = parse_section_header(reader, &current_section);
1061 break;
1062
1063 case ';':
1064 case '#':
1065 reader_consume_line(reader);
1066 break;
1067
1068 default: /* assume variable declaration */
1069 result = parse_variable(reader, &var_name, &var_value);
1070 if (result < 0)
1071 break;
1072
1073 var = git__malloc(sizeof(cvar_t));
1074 GITERR_CHECK_ALLOC(var);
1075 memset(var, 0x0, sizeof(cvar_t));
1076 var->entry = git__malloc(sizeof(git_config_entry));
1077 GITERR_CHECK_ALLOC(var->entry);
1078 memset(var->entry, 0x0, sizeof(git_config_entry));
1079
1080 git__strtolower(var_name);
1081 git_buf_printf(&buf, "%s.%s", current_section, var_name);
1082 git__free(var_name);
1083
1084 if (git_buf_oom(&buf))
1085 return -1;
1086
1087 var->entry->name = git_buf_detach(&buf);
1088 var->entry->value = var_value;
1089 var->entry->level = level;
1090 var->included = !!depth;
1091
1092 /* Add or append the new config option */
1093 pos = git_strmap_lookup_index(cfg_file->values, var->entry->name);
1094 if (!git_strmap_valid_index(cfg_file->values, pos)) {
1095 git_strmap_insert(cfg_file->values, var->entry->name, var, result);
1096 if (result < 0)
1097 break;
1098 result = 0;
1099 } else {
1100 existing = git_strmap_value_at(cfg_file->values, pos);
1101 while (existing->next != NULL) {
1102 existing = existing->next;
1103 }
1104 existing->next = var;
1105 }
1106
1107 if (!git__strcmp(var->entry->name, "include.path")) {
1108 struct reader *r;
1109 git_buf path = GIT_BUF_INIT;
1110 char *dir;
1111 uint32_t index;
1112
1113 r = git_array_alloc(cfg_file->readers);
1114 /* The reader may have been reallocated */
1115 reader = git_array_get(cfg_file->readers, reader_idx);
1116 memset(r, 0, sizeof(struct reader));
1117 if ((result = git_path_dirname_r(&path, reader->file_path)) < 0)
1118 break;
1119
1120 /* We need to know out index in the array, as the next config_parse call may realloc */
1121 index = git_array_size(cfg_file->readers) - 1;
1122 dir = git_buf_detach(&path);
1123 result = included_path(&path, dir, var->entry->value);
1124 git__free(dir);
1125
1126 if (result < 0)
1127 break;
1128
1129 r->file_path = git_buf_detach(&path);
1130 git_buf_init(&r->buffer, 0);
1131 if ((result = git_futils_readbuffer_updated(&r->buffer, r->file_path, &r->file_mtime,
1132 &r->file_size, NULL)) < 0)
1133 break;
1134
1135 result = config_parse(cfg_file, r, level, depth+1);
1136 r = git_array_get(cfg_file->readers, index);
1137 git_buf_free(&r->buffer);
1138
1139 if (result < 0)
1140 break;
1141 }
1142
1143 break;
1144 }
1145 }
1146
1147 git__free(current_section);
1148 return result;
1149 }
1150
1151 static int write_section(git_filebuf *file, const char *key)
1152 {
1153 int result;
1154 const char *dot;
1155 git_buf buf = GIT_BUF_INIT;
1156
1157 /* All of this just for [section "subsection"] */
1158 dot = strchr(key, '.');
1159 git_buf_putc(&buf, '[');
1160 if (dot == NULL) {
1161 git_buf_puts(&buf, key);
1162 } else {
1163 char *escaped;
1164 git_buf_put(&buf, key, dot - key);
1165 escaped = escape_value(dot + 1);
1166 GITERR_CHECK_ALLOC(escaped);
1167 git_buf_printf(&buf, " \"%s\"", escaped);
1168 git__free(escaped);
1169 }
1170 git_buf_puts(&buf, "]\n");
1171
1172 if (git_buf_oom(&buf))
1173 return -1;
1174
1175 result = git_filebuf_write(file, git_buf_cstr(&buf), buf.size);
1176 git_buf_free(&buf);
1177
1178 return result;
1179 }
1180
1181 /*
1182 * This is pretty much the parsing, except we write out anything we don't have
1183 */
1184 static int config_write(diskfile_backend *cfg, const char *key, const regex_t *preg, const char* value)
1185 {
1186 int result, c;
1187 int section_matches = 0, last_section_matched = 0, preg_replaced = 0, write_trailer = 0;
1188 const char *pre_end = NULL, *post_start = NULL, *data_start;
1189 char *current_section = NULL, *section, *name, *ldot;
1190 git_filebuf file = GIT_FILEBUF_INIT;
1191 struct reader *reader = git_array_get(cfg->readers, 0);
1192
1193 /* We need to read in our own config file */
1194 result = git_futils_readbuffer(&reader->buffer, cfg->file_path);
1195
1196 /* Initialise the reading position */
1197 if (result == GIT_ENOTFOUND) {
1198 reader->read_ptr = NULL;
1199 reader->eof = 1;
1200 data_start = NULL;
1201 git_buf_clear(&reader->buffer);
1202 } else if (result == 0) {
1203 reader->read_ptr = reader->buffer.ptr;
1204 reader->eof = 0;
1205 data_start = reader->read_ptr;
1206 } else {
1207 return -1; /* OS error when reading the file */
1208 }
1209
1210 /* Lock the file */
1211 if (git_filebuf_open(&file, cfg->file_path, 0) < 0)
1212 return -1;
1213
1214 skip_bom(reader);
1215 ldot = strrchr(key, '.');
1216 name = ldot + 1;
1217 section = git__strndup(key, ldot - key);
1218
1219 while (!reader->eof) {
1220 c = reader_peek(reader, SKIP_WHITESPACE);
1221
1222 if (c == '\0') { /* We've arrived at the end of the file */
1223 break;
1224
1225 } else if (c == '[') { /* section header, new section begins */
1226 /*
1227 * We set both positions to the current one in case we
1228 * need to add a variable to the end of a section. In that
1229 * case, we want both variables to point just before the
1230 * new section. If we actually want to replace it, the
1231 * default case will take care of updating them.
1232 */
1233 pre_end = post_start = reader->read_ptr;
1234
1235 git__free(current_section);
1236 current_section = NULL;
1237 if (parse_section_header(reader, &current_section) < 0)
1238 goto rewrite_fail;
1239
1240 /* Keep track of when it stops matching */
1241 last_section_matched = section_matches;
1242 section_matches = !strcmp(current_section, section);
1243 }
1244
1245 else if (c == ';' || c == '#') {
1246 reader_consume_line(reader);
1247 }
1248
1249 else {
1250 /*
1251 * If the section doesn't match, but the last section did,
1252 * it means we need to add a variable (so skip the line
1253 * otherwise). If both the section and name match, we need
1254 * to overwrite the variable (so skip the line
1255 * otherwise). pre_end needs to be updated each time so we
1256 * don't loose that information, but we only need to
1257 * update post_start if we're going to use it in this
1258 * iteration.
1259 */
1260 if (!section_matches) {
1261 if (!last_section_matched) {
1262 reader_consume_line(reader);
1263 continue;
1264 }
1265 } else {
1266 int has_matched = 0;
1267 char *var_name, *var_value;
1268
1269 pre_end = reader->read_ptr;
1270 if (parse_variable(reader, &var_name, &var_value) < 0)
1271 goto rewrite_fail;
1272
1273 /* First try to match the name of the variable */
1274 if (strcasecmp(name, var_name) == 0)
1275 has_matched = 1;
1276
1277 /* If the name matches, and we have a regex to match the
1278 * value, try to match it */
1279 if (has_matched && preg != NULL)
1280 has_matched = (regexec(preg, var_value, 0, NULL, 0) == 0);
1281
1282 git__free(var_name);
1283 git__free(var_value);
1284
1285 /* if there is no match, keep going */
1286 if (!has_matched)
1287 continue;
1288
1289 post_start = reader->read_ptr;
1290 }
1291
1292 /* We've found the variable we wanted to change, so
1293 * write anything up to it */
1294 git_filebuf_write(&file, data_start, pre_end - data_start);
1295 preg_replaced = 1;
1296
1297 /* Then replace the variable. If the value is NULL, it
1298 * means we want to delete it, so don't write anything. */
1299 if (value != NULL) {
1300 git_filebuf_printf(&file, "\t%s = %s\n", name, value);
1301 }
1302
1303 /* multiline variable? we need to keep reading lines to match */
1304 if (preg != NULL && section_matches) {
1305 data_start = post_start;
1306 continue;
1307 }
1308
1309 write_trailer = 1;
1310 break; /* break from the loop */
1311 }
1312 }
1313
1314 /*
1315 * Being here can mean that
1316 *
1317 * 1) our section is the last one in the file and we're
1318 * adding a variable
1319 *
1320 * 2) we didn't find a section for us so we need to create it
1321 * ourselves.
1322 *
1323 * 3) we're setting a multivar with a regex, which means we
1324 * continue to search for matching values
1325 *
1326 * In the last case, if we've already replaced a value, we
1327 * want to write the rest of the file. Otherwise we need to write
1328 * out the whole file and then the new variable.
1329 */
1330 if (write_trailer) {
1331 /* Write out rest of the file */
1332 git_filebuf_write(&file, post_start, reader->buffer.size - (post_start - data_start));
1333 } else {
1334 if (preg_replaced) {
1335 git_filebuf_printf(&file, "\n%s", data_start);
1336 } else {
1337 git_filebuf_write(&file, reader->buffer.ptr, reader->buffer.size);
1338
1339 /* And now if we just need to add a variable */
1340 if (!section_matches && write_section(&file, section) < 0)
1341 goto rewrite_fail;
1342
1343 /* Sanity check: if we are here, and value is NULL, that means that somebody
1344 * touched the config file after our intial read. We should probably assert()
1345 * this, but instead we'll handle it gracefully with an error. */
1346 if (value == NULL) {
1347 giterr_set(GITERR_CONFIG,
1348 "Race condition when writing a config file (a cvar has been removed)");
1349 goto rewrite_fail;
1350 }
1351
1352 /* If we are here, there is at least a section line */
1353 if (reader->buffer.size > 0 && *(reader->buffer.ptr + reader->buffer.size - 1) != '\n')
1354 git_filebuf_write(&file, "\n", 1);
1355
1356 git_filebuf_printf(&file, "\t%s = %s\n", name, value);
1357 }
1358 }
1359
1360 git__free(section);
1361 git__free(current_section);
1362
1363 /* refresh stats - if this errors, then commit will error too */
1364 (void)git_filebuf_stats(&reader->file_mtime, &reader->file_size, &file);
1365
1366 result = git_filebuf_commit(&file, GIT_CONFIG_FILE_MODE);
1367 git_buf_free(&reader->buffer);
1368
1369 return result;
1370
1371 rewrite_fail:
1372 git__free(section);
1373 git__free(current_section);
1374
1375 git_filebuf_cleanup(&file);
1376 git_buf_free(&reader->buffer);
1377 return -1;
1378 }
1379
1380 static const char *escapes = "ntb\"\\";
1381 static const char *escaped = "\n\t\b\"\\";
1382
1383 /* Escape the values to write them to the file */
1384 static char *escape_value(const char *ptr)
1385 {
1386 git_buf buf = GIT_BUF_INIT;
1387 size_t len;
1388 const char *esc;
1389
1390 assert(ptr);
1391
1392 len = strlen(ptr);
1393 if (!len)
1394 return git__calloc(1, sizeof(char));
1395
1396 git_buf_grow(&buf, len);
1397
1398 while (*ptr != '\0') {
1399 if ((esc = strchr(escaped, *ptr)) != NULL) {
1400 git_buf_putc(&buf, '\\');
1401 git_buf_putc(&buf, escapes[esc - escaped]);
1402 } else {
1403 git_buf_putc(&buf, *ptr);
1404 }
1405 ptr++;
1406 }
1407
1408 if (git_buf_oom(&buf)) {
1409 git_buf_free(&buf);
1410 return NULL;
1411 }
1412
1413 return git_buf_detach(&buf);
1414 }
1415
1416 /* '\"' -> '"' etc */
1417 static char *fixup_line(const char *ptr, int quote_count)
1418 {
1419 char *str = git__malloc(strlen(ptr) + 1);
1420 char *out = str, *esc;
1421
1422 if (str == NULL)
1423 return NULL;
1424
1425 while (*ptr != '\0') {
1426 if (*ptr == '"') {
1427 quote_count++;
1428 } else if (*ptr != '\\') {
1429 *out++ = *ptr;
1430 } else {
1431 /* backslash, check the next char */
1432 ptr++;
1433 /* if we're at the end, it's a multiline, so keep the backslash */
1434 if (*ptr == '\0') {
1435 *out++ = '\\';
1436 goto out;
1437 }
1438 if ((esc = strchr(escapes, *ptr)) != NULL) {
1439 *out++ = escaped[esc - escapes];
1440 } else {
1441 git__free(str);
1442 giterr_set(GITERR_CONFIG, "Invalid escape at %s", ptr);
1443 return NULL;
1444 }
1445 }
1446 ptr++;
1447 }
1448
1449 out:
1450 *out = '\0';
1451
1452 return str;
1453 }
1454
1455 static int is_multiline_var(const char *str)
1456 {
1457 int count = 0;
1458 const char *end = str + strlen(str);
1459 while (end > str && end[-1] == '\\') {
1460 count++;
1461 end--;
1462 }
1463
1464 /* An odd number means last backslash wasn't escaped, so it's multiline */
1465 return (end > str) && (count & 1);
1466 }
1467
1468 static int parse_multiline_variable(struct reader *reader, git_buf *value, int in_quotes)
1469 {
1470 char *line = NULL, *proc_line = NULL;
1471 int quote_count;
1472
1473 /* Check that the next line exists */
1474 line = reader_readline(reader, false);
1475 if (line == NULL)
1476 return -1;
1477
1478 /* We've reached the end of the file, there is input missing */
1479 if (line[0] == '\0') {
1480 set_parse_error(reader, 0, "Unexpected end of file while parsing multine var");
1481 git__free(line);
1482 return -1;
1483 }
1484
1485 quote_count = strip_comments(line, !!in_quotes);
1486
1487 /* If it was just a comment, pretend it didn't exist */
1488 if (line[0] == '\0') {
1489 git__free(line);
1490 return parse_multiline_variable(reader, value, quote_count);
1491 /* TODO: unbounded recursion. This **could** be exploitable */
1492 }
1493
1494 /* Drop the continuation character '\': to closely follow the UNIX
1495 * standard, this character **has** to be last one in the buf, with
1496 * no whitespace after it */
1497 assert(is_multiline_var(value->ptr));
1498 git_buf_shorten(value, 1);
1499
1500 proc_line = fixup_line(line, in_quotes);
1501 if (proc_line == NULL) {
1502 git__free(line);
1503 return -1;
1504 }
1505 /* add this line to the multiline var */
1506 git_buf_puts(value, proc_line);
1507 git__free(line);
1508 git__free(proc_line);
1509
1510 /*
1511 * If we need to continue reading the next line, let's just
1512 * keep putting stuff in the buffer
1513 */
1514 if (is_multiline_var(value->ptr))
1515 return parse_multiline_variable(reader, value, quote_count);
1516
1517 return 0;
1518 }
1519
1520 static int parse_variable(struct reader *reader, char **var_name, char **var_value)
1521 {
1522 const char *var_end = NULL;
1523 const char *value_start = NULL;
1524 char *line;
1525 int quote_count;
1526
1527 line = reader_readline(reader, true);
1528 if (line == NULL)
1529 return -1;
1530
1531 quote_count = strip_comments(line, 0);
1532
1533 var_end = strchr(line, '=');
1534
1535 if (var_end == NULL)
1536 var_end = strchr(line, '\0');
1537 else
1538 value_start = var_end + 1;
1539
1540 do var_end--;
1541 while (var_end>line && git__isspace(*var_end));
1542
1543 *var_name = git__strndup(line, var_end - line + 1);
1544 GITERR_CHECK_ALLOC(*var_name);
1545
1546 /* If there is no value, boolean true is assumed */
1547 *var_value = NULL;
1548
1549 /*
1550 * Now, let's try to parse the value
1551 */
1552 if (value_start != NULL) {
1553 while (git__isspace(value_start[0]))
1554 value_start++;
1555
1556 if (is_multiline_var(value_start)) {
1557 git_buf multi_value = GIT_BUF_INIT;
1558 char *proc_line = fixup_line(value_start, 0);
1559 GITERR_CHECK_ALLOC(proc_line);
1560 git_buf_puts(&multi_value, proc_line);
1561 git__free(proc_line);
1562 if (parse_multiline_variable(reader, &multi_value, quote_count) < 0 || git_buf_oom(&multi_value)) {
1563 git__free(*var_name);
1564 git__free(line);
1565 git_buf_free(&multi_value);
1566 return -1;
1567 }
1568
1569 *var_value = git_buf_detach(&multi_value);
1570
1571 }
1572 else if (value_start[0] != '\0') {
1573 *var_value = fixup_line(value_start, 0);
1574 GITERR_CHECK_ALLOC(*var_value);
1575 } else { /* equals sign but missing rhs */
1576 *var_value = git__strdup("");
1577 GITERR_CHECK_ALLOC(*var_value);
1578 }
1579 }
1580
1581 git__free(line);
1582 return 0;
1583 }