]> git.proxmox.com Git - libgit2.git/blob - src/config.c
push: remove own copy of callbacks
[libgit2.git] / src / config.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 "sysdir.h"
10 #include "config.h"
11 #include "git2/config.h"
12 #include "git2/sys/config.h"
13 #include "vector.h"
14 #include "buf_text.h"
15 #include "config_file.h"
16 #if GIT_WIN32
17 # include <windows.h>
18 #endif
19
20 #include <ctype.h>
21
22 void git_config_entry_free(git_config_entry *entry)
23 {
24 if (!entry)
25 return;
26
27 entry->free(entry);
28 }
29
30 typedef struct {
31 git_refcount rc;
32
33 git_config_backend *file;
34 git_config_level_t level;
35 } file_internal;
36
37 static void file_internal_free(file_internal *internal)
38 {
39 git_config_backend *file;
40
41 file = internal->file;
42 file->free(file);
43 git__free(internal);
44 }
45
46 static void config_free(git_config *cfg)
47 {
48 size_t i;
49 file_internal *internal;
50
51 for (i = 0; i < cfg->files.length; ++i) {
52 internal = git_vector_get(&cfg->files, i);
53 GIT_REFCOUNT_DEC(internal, file_internal_free);
54 }
55
56 git_vector_free(&cfg->files);
57
58 git__memzero(cfg, sizeof(*cfg));
59 git__free(cfg);
60 }
61
62 void git_config_free(git_config *cfg)
63 {
64 if (cfg == NULL)
65 return;
66
67 GIT_REFCOUNT_DEC(cfg, config_free);
68 }
69
70 static int config_backend_cmp(const void *a, const void *b)
71 {
72 const file_internal *bk_a = (const file_internal *)(a);
73 const file_internal *bk_b = (const file_internal *)(b);
74
75 return bk_b->level - bk_a->level;
76 }
77
78 int git_config_new(git_config **out)
79 {
80 git_config *cfg;
81
82 cfg = git__malloc(sizeof(git_config));
83 GITERR_CHECK_ALLOC(cfg);
84
85 memset(cfg, 0x0, sizeof(git_config));
86
87 if (git_vector_init(&cfg->files, 3, config_backend_cmp) < 0) {
88 git__free(cfg);
89 return -1;
90 }
91
92 *out = cfg;
93 GIT_REFCOUNT_INC(cfg);
94 return 0;
95 }
96
97 int git_config_add_file_ondisk(
98 git_config *cfg,
99 const char *path,
100 git_config_level_t level,
101 int force)
102 {
103 git_config_backend *file = NULL;
104 struct stat st;
105 int res;
106
107 assert(cfg && path);
108
109 res = p_stat(path, &st);
110 if (res < 0 && errno != ENOENT) {
111 giterr_set(GITERR_CONFIG, "Error stat'ing config file '%s'", path);
112 return -1;
113 }
114
115 if (git_config_file__ondisk(&file, path) < 0)
116 return -1;
117
118 if ((res = git_config_add_backend(cfg, file, level, force)) < 0) {
119 /*
120 * free manually; the file is not owned by the config
121 * instance yet and will not be freed on cleanup
122 */
123 file->free(file);
124 return res;
125 }
126
127 return 0;
128 }
129
130 int git_config_open_ondisk(git_config **out, const char *path)
131 {
132 int error;
133 git_config *config;
134
135 *out = NULL;
136
137 if (git_config_new(&config) < 0)
138 return -1;
139
140 if ((error = git_config_add_file_ondisk(config, path, GIT_CONFIG_LEVEL_LOCAL, 0)) < 0)
141 git_config_free(config);
142 else
143 *out = config;
144
145 return error;
146 }
147
148 int git_config_snapshot(git_config **out, git_config *in)
149 {
150 int error = 0;
151 size_t i;
152 file_internal *internal;
153 git_config *config;
154
155 *out = NULL;
156
157 if (git_config_new(&config) < 0)
158 return -1;
159
160 git_vector_foreach(&in->files, i, internal) {
161 git_config_backend *b;
162
163 if ((error = internal->file->snapshot(&b, internal->file)) < 0)
164 break;
165
166 if ((error = git_config_add_backend(config, b, internal->level, 0)) < 0) {
167 b->free(b);
168 break;
169 }
170 }
171
172 if (error < 0)
173 git_config_free(config);
174 else
175 *out = config;
176
177 return error;
178 }
179
180 static int find_internal_file_by_level(
181 file_internal **internal_out,
182 const git_config *cfg,
183 git_config_level_t level)
184 {
185 int pos = -1;
186 file_internal *internal;
187 size_t i;
188
189 /* when passing GIT_CONFIG_HIGHEST_LEVEL, the idea is to get the config file
190 * which has the highest level. As config files are stored in a vector
191 * sorted by decreasing order of level, getting the file at position 0
192 * will do the job.
193 */
194 if (level == GIT_CONFIG_HIGHEST_LEVEL) {
195 pos = 0;
196 } else {
197 git_vector_foreach(&cfg->files, i, internal) {
198 if (internal->level == level)
199 pos = (int)i;
200 }
201 }
202
203 if (pos == -1) {
204 giterr_set(GITERR_CONFIG,
205 "No config file exists for the given level '%i'", (int)level);
206 return GIT_ENOTFOUND;
207 }
208
209 *internal_out = git_vector_get(&cfg->files, pos);
210
211 return 0;
212 }
213
214 static int duplicate_level(void **old_raw, void *new_raw)
215 {
216 file_internal **old = (file_internal **)old_raw;
217
218 GIT_UNUSED(new_raw);
219
220 giterr_set(GITERR_CONFIG, "A file with the same level (%i) has already been added to the config", (int)(*old)->level);
221 return GIT_EEXISTS;
222 }
223
224 static void try_remove_existing_file_internal(
225 git_config *cfg,
226 git_config_level_t level)
227 {
228 int pos = -1;
229 file_internal *internal;
230 size_t i;
231
232 git_vector_foreach(&cfg->files, i, internal) {
233 if (internal->level == level)
234 pos = (int)i;
235 }
236
237 if (pos == -1)
238 return;
239
240 internal = git_vector_get(&cfg->files, pos);
241
242 if (git_vector_remove(&cfg->files, pos) < 0)
243 return;
244
245 GIT_REFCOUNT_DEC(internal, file_internal_free);
246 }
247
248 static int git_config__add_internal(
249 git_config *cfg,
250 file_internal *internal,
251 git_config_level_t level,
252 int force)
253 {
254 int result;
255
256 /* delete existing config file for level if it exists */
257 if (force)
258 try_remove_existing_file_internal(cfg, level);
259
260 if ((result = git_vector_insert_sorted(&cfg->files,
261 internal, &duplicate_level)) < 0)
262 return result;
263
264 git_vector_sort(&cfg->files);
265 internal->file->cfg = cfg;
266
267 GIT_REFCOUNT_INC(internal);
268
269 return 0;
270 }
271
272 int git_config_open_global(git_config **cfg_out, git_config *cfg)
273 {
274 if (!git_config_open_level(cfg_out, cfg, GIT_CONFIG_LEVEL_XDG))
275 return 0;
276
277 return git_config_open_level(cfg_out, cfg, GIT_CONFIG_LEVEL_GLOBAL);
278 }
279
280 int git_config_open_level(
281 git_config **cfg_out,
282 const git_config *cfg_parent,
283 git_config_level_t level)
284 {
285 git_config *cfg;
286 file_internal *internal;
287 int res;
288
289 if ((res = find_internal_file_by_level(&internal, cfg_parent, level)) < 0)
290 return res;
291
292 if ((res = git_config_new(&cfg)) < 0)
293 return res;
294
295 if ((res = git_config__add_internal(cfg, internal, level, true)) < 0) {
296 git_config_free(cfg);
297 return res;
298 }
299
300 *cfg_out = cfg;
301
302 return 0;
303 }
304
305 int git_config_add_backend(
306 git_config *cfg,
307 git_config_backend *file,
308 git_config_level_t level,
309 int force)
310 {
311 file_internal *internal;
312 int result;
313
314 assert(cfg && file);
315
316 GITERR_CHECK_VERSION(file, GIT_CONFIG_BACKEND_VERSION, "git_config_backend");
317
318 if ((result = file->open(file, level)) < 0)
319 return result;
320
321 internal = git__malloc(sizeof(file_internal));
322 GITERR_CHECK_ALLOC(internal);
323
324 memset(internal, 0x0, sizeof(file_internal));
325
326 internal->file = file;
327 internal->level = level;
328
329 if ((result = git_config__add_internal(cfg, internal, level, force)) < 0) {
330 git__free(internal);
331 return result;
332 }
333
334 return 0;
335 }
336
337 /*
338 * Loop over all the variables
339 */
340
341 typedef struct {
342 git_config_iterator parent;
343 git_config_iterator *current;
344 const git_config *cfg;
345 regex_t regex;
346 size_t i;
347 } all_iter;
348
349 static int find_next_backend(size_t *out, const git_config *cfg, size_t i)
350 {
351 file_internal *internal;
352
353 for (; i > 0; --i) {
354 internal = git_vector_get(&cfg->files, i - 1);
355 if (!internal || !internal->file)
356 continue;
357
358 *out = i;
359 return 0;
360 }
361
362 return -1;
363 }
364
365 static int all_iter_next(git_config_entry **entry, git_config_iterator *_iter)
366 {
367 all_iter *iter = (all_iter *) _iter;
368 file_internal *internal;
369 git_config_backend *backend;
370 size_t i;
371 int error = 0;
372
373 if (iter->current != NULL &&
374 (error = iter->current->next(entry, iter->current)) == 0) {
375 return 0;
376 }
377
378 if (error < 0 && error != GIT_ITEROVER)
379 return error;
380
381 do {
382 if (find_next_backend(&i, iter->cfg, iter->i) < 0)
383 return GIT_ITEROVER;
384
385 internal = git_vector_get(&iter->cfg->files, i - 1);
386 backend = internal->file;
387 iter->i = i - 1;
388
389 if (iter->current)
390 iter->current->free(iter->current);
391
392 iter->current = NULL;
393 error = backend->iterator(&iter->current, backend);
394 if (error == GIT_ENOTFOUND)
395 continue;
396
397 if (error < 0)
398 return error;
399
400 error = iter->current->next(entry, iter->current);
401 /* If this backend is empty, then keep going */
402 if (error == GIT_ITEROVER)
403 continue;
404
405 return error;
406
407 } while(1);
408
409 return GIT_ITEROVER;
410 }
411
412 static int all_iter_glob_next(git_config_entry **entry, git_config_iterator *_iter)
413 {
414 int error;
415 all_iter *iter = (all_iter *) _iter;
416
417 /*
418 * We use the "normal" function to grab the next one across
419 * backends and then apply the regex
420 */
421 while ((error = all_iter_next(entry, _iter)) == 0) {
422 /* skip non-matching keys if regexp was provided */
423 if (regexec(&iter->regex, (*entry)->name, 0, NULL, 0) != 0)
424 continue;
425
426 /* and simply return if we like the entry's name */
427 return 0;
428 }
429
430 return error;
431 }
432
433 static void all_iter_free(git_config_iterator *_iter)
434 {
435 all_iter *iter = (all_iter *) _iter;
436
437 if (iter->current)
438 iter->current->free(iter->current);
439
440 git__free(iter);
441 }
442
443 static void all_iter_glob_free(git_config_iterator *_iter)
444 {
445 all_iter *iter = (all_iter *) _iter;
446
447 regfree(&iter->regex);
448 all_iter_free(_iter);
449 }
450
451 int git_config_iterator_new(git_config_iterator **out, const git_config *cfg)
452 {
453 all_iter *iter;
454
455 iter = git__calloc(1, sizeof(all_iter));
456 GITERR_CHECK_ALLOC(iter);
457
458 iter->parent.free = all_iter_free;
459 iter->parent.next = all_iter_next;
460
461 iter->i = cfg->files.length;
462 iter->cfg = cfg;
463
464 *out = (git_config_iterator *) iter;
465
466 return 0;
467 }
468
469 int git_config_iterator_glob_new(git_config_iterator **out, const git_config *cfg, const char *regexp)
470 {
471 all_iter *iter;
472 int result;
473
474 if (regexp == NULL)
475 return git_config_iterator_new(out, cfg);
476
477 iter = git__calloc(1, sizeof(all_iter));
478 GITERR_CHECK_ALLOC(iter);
479
480 if ((result = regcomp(&iter->regex, regexp, REG_EXTENDED)) != 0) {
481 giterr_set_regex(&iter->regex, result);
482 git__free(iter);
483 return -1;
484 }
485
486 iter->parent.next = all_iter_glob_next;
487 iter->parent.free = all_iter_glob_free;
488 iter->i = cfg->files.length;
489 iter->cfg = cfg;
490
491 *out = (git_config_iterator *) iter;
492
493 return 0;
494 }
495
496 int git_config_foreach(
497 const git_config *cfg, git_config_foreach_cb cb, void *payload)
498 {
499 return git_config_foreach_match(cfg, NULL, cb, payload);
500 }
501
502 int git_config_backend_foreach_match(
503 git_config_backend *backend,
504 const char *regexp,
505 git_config_foreach_cb cb,
506 void *payload)
507 {
508 git_config_entry *entry;
509 git_config_iterator* iter;
510 regex_t regex;
511 int error = 0;
512
513 if (regexp != NULL) {
514 if ((error = regcomp(&regex, regexp, REG_EXTENDED)) != 0) {
515 giterr_set_regex(&regex, error);
516 regfree(&regex);
517 return -1;
518 }
519 }
520
521 if ((error = backend->iterator(&iter, backend)) < 0) {
522 iter = NULL;
523 return -1;
524 }
525
526 while (!(iter->next(&entry, iter) < 0)) {
527 /* skip non-matching keys if regexp was provided */
528 if (regexp && regexec(&regex, entry->name, 0, NULL, 0) != 0)
529 continue;
530
531 /* abort iterator on non-zero return value */
532 if ((error = cb(entry, payload)) != 0) {
533 giterr_set_after_callback(error);
534 break;
535 }
536 }
537
538 if (regexp != NULL)
539 regfree(&regex);
540
541 iter->free(iter);
542
543 return error;
544 }
545
546 int git_config_foreach_match(
547 const git_config *cfg,
548 const char *regexp,
549 git_config_foreach_cb cb,
550 void *payload)
551 {
552 int error;
553 git_config_iterator *iter;
554 git_config_entry *entry;
555
556 if ((error = git_config_iterator_glob_new(&iter, cfg, regexp)) < 0)
557 return error;
558
559 while (!(error = git_config_next(&entry, iter))) {
560 if ((error = cb(entry, payload)) != 0) {
561 giterr_set_after_callback(error);
562 break;
563 }
564 }
565
566 git_config_iterator_free(iter);
567
568 if (error == GIT_ITEROVER)
569 error = 0;
570
571 return error;
572 }
573
574 /**************
575 * Setters
576 **************/
577
578 static int config_error_nofiles(const char *name)
579 {
580 giterr_set(GITERR_CONFIG,
581 "Cannot set value for '%s' when no config files exist", name);
582 return GIT_ENOTFOUND;
583 }
584
585 int git_config_delete_entry(git_config *cfg, const char *name)
586 {
587 git_config_backend *file;
588 file_internal *internal;
589
590 internal = git_vector_get(&cfg->files, 0);
591 if (!internal || !internal->file)
592 return config_error_nofiles(name);
593 file = internal->file;
594
595 return file->del(file, name);
596 }
597
598 int git_config_set_int64(git_config *cfg, const char *name, int64_t value)
599 {
600 char str_value[32]; /* All numbers should fit in here */
601 p_snprintf(str_value, sizeof(str_value), "%" PRId64, value);
602 return git_config_set_string(cfg, name, str_value);
603 }
604
605 int git_config_set_int32(git_config *cfg, const char *name, int32_t value)
606 {
607 return git_config_set_int64(cfg, name, (int64_t)value);
608 }
609
610 int git_config_set_bool(git_config *cfg, const char *name, int value)
611 {
612 return git_config_set_string(cfg, name, value ? "true" : "false");
613 }
614
615 int git_config_set_string(git_config *cfg, const char *name, const char *value)
616 {
617 int error;
618 git_config_backend *file;
619 file_internal *internal;
620
621 if (!value) {
622 giterr_set(GITERR_CONFIG, "The value to set cannot be NULL");
623 return -1;
624 }
625
626 internal = git_vector_get(&cfg->files, 0);
627 if (!internal || !internal->file)
628 return config_error_nofiles(name);
629 file = internal->file;
630
631 error = file->set(file, name, value);
632
633 if (!error && GIT_REFCOUNT_OWNER(cfg) != NULL)
634 git_repository__cvar_cache_clear(GIT_REFCOUNT_OWNER(cfg));
635
636 return error;
637 }
638
639 int git_config__update_entry(
640 git_config *config,
641 const char *key,
642 const char *value,
643 bool overwrite_existing,
644 bool only_if_existing)
645 {
646 int error = 0;
647 git_config_entry *ce = NULL;
648
649 if ((error = git_config__lookup_entry(&ce, config, key, false)) < 0)
650 return error;
651
652 if (!ce && only_if_existing) /* entry doesn't exist */
653 return 0;
654 if (ce && !overwrite_existing) /* entry would be overwritten */
655 return 0;
656 if (value && ce && ce->value && !strcmp(ce->value, value)) /* no change */
657 return 0;
658 if (!value && (!ce || !ce->value)) /* asked to delete absent entry */
659 return 0;
660
661 if (!value)
662 error = git_config_delete_entry(config, key);
663 else
664 error = git_config_set_string(config, key, value);
665
666 git_config_entry_free(ce);
667 return error;
668 }
669
670 /***********
671 * Getters
672 ***********/
673
674 static int config_error_notfound(const char *name)
675 {
676 giterr_set(GITERR_CONFIG, "Config value '%s' was not found", name);
677 return GIT_ENOTFOUND;
678 }
679
680 enum {
681 GET_ALL_ERRORS = 0,
682 GET_NO_MISSING = 1,
683 GET_NO_ERRORS = 2
684 };
685
686 static int get_entry(
687 git_config_entry **out,
688 const git_config *cfg,
689 const char *name,
690 bool normalize_name,
691 int want_errors)
692 {
693 int res = GIT_ENOTFOUND;
694 const char *key = name;
695 char *normalized = NULL;
696 size_t i;
697 file_internal *internal;
698
699 *out = NULL;
700
701 if (normalize_name) {
702 if ((res = git_config__normalize_name(name, &normalized)) < 0)
703 goto cleanup;
704 key = normalized;
705 }
706
707 res = GIT_ENOTFOUND;
708 git_vector_foreach(&cfg->files, i, internal) {
709 if (!internal || !internal->file)
710 continue;
711
712 res = internal->file->get(internal->file, key, out);
713 if (res != GIT_ENOTFOUND)
714 break;
715 }
716
717 git__free(normalized);
718
719 cleanup:
720 if (res == GIT_ENOTFOUND)
721 res = (want_errors > GET_ALL_ERRORS) ? 0 : config_error_notfound(name);
722 else if (res && (want_errors == GET_NO_ERRORS)) {
723 giterr_clear();
724 res = 0;
725 }
726
727 return res;
728 }
729
730 int git_config_get_entry(
731 git_config_entry **out, const git_config *cfg, const char *name)
732 {
733 return get_entry(out, cfg, name, true, GET_ALL_ERRORS);
734 }
735
736 int git_config__lookup_entry(
737 git_config_entry **out,
738 const git_config *cfg,
739 const char *key,
740 bool no_errors)
741 {
742 return get_entry(
743 out, cfg, key, false, no_errors ? GET_NO_ERRORS : GET_NO_MISSING);
744 }
745
746 int git_config_get_mapped(
747 int *out,
748 const git_config *cfg,
749 const char *name,
750 const git_cvar_map *maps,
751 size_t map_n)
752 {
753 git_config_entry *entry;
754 int ret;
755
756 if ((ret = get_entry(&entry, cfg, name, true, GET_ALL_ERRORS)) < 0)
757 return ret;
758
759 ret = git_config_lookup_map_value(out, maps, map_n, entry->value);
760 git_config_entry_free(entry);
761
762 return ret;
763 }
764
765 int git_config_get_int64(int64_t *out, const git_config *cfg, const char *name)
766 {
767 git_config_entry *entry;
768 int ret;
769
770 if ((ret = get_entry(&entry, cfg, name, true, GET_ALL_ERRORS)) < 0)
771 return ret;
772
773 ret = git_config_parse_int64(out, entry->value);
774 git_config_entry_free(entry);
775
776 return ret;
777 }
778
779 int git_config_get_int32(int32_t *out, const git_config *cfg, const char *name)
780 {
781 git_config_entry *entry;
782 int ret;
783
784 if ((ret = get_entry(&entry, cfg, name, true, GET_ALL_ERRORS)) < 0)
785 return ret;
786
787 ret = git_config_parse_int32(out, entry->value);
788 git_config_entry_free(entry);
789
790 return ret;
791 }
792
793 int git_config_get_bool(int *out, const git_config *cfg, const char *name)
794 {
795 git_config_entry *entry;
796 int ret;
797
798 if ((ret = get_entry(&entry, cfg, name, true, GET_ALL_ERRORS)) < 0)
799 return ret;
800
801 ret = git_config_parse_bool(out, entry->value);
802 git_config_entry_free(entry);
803
804 return ret;
805 }
806
807 static int is_readonly(const git_config *cfg)
808 {
809 size_t i;
810 file_internal *internal;
811
812 git_vector_foreach(&cfg->files, i, internal) {
813 if (!internal || !internal->file)
814 continue;
815
816 if (!internal->file->readonly)
817 return 0;
818 }
819
820 return 1;
821 }
822
823 int git_config_get_path(git_buf *out, const git_config *cfg, const char *name)
824 {
825 git_config_entry *entry;
826 int error;
827
828 if ((error = get_entry(&entry, cfg, name, true, GET_ALL_ERRORS)) < 0)
829 return error;
830
831 error = git_config_parse_path(out, entry->value);
832 git_config_entry_free(entry);
833
834 return error;
835 }
836
837 int git_config_get_string(
838 const char **out, const git_config *cfg, const char *name)
839 {
840 git_config_entry *entry;
841 int ret;
842
843 if (!is_readonly(cfg)) {
844 giterr_set(GITERR_CONFIG, "get_string called on a live config object");
845 return -1;
846 }
847
848 ret = get_entry(&entry, cfg, name, true, GET_ALL_ERRORS);
849 *out = !ret ? (entry->value ? entry->value : "") : NULL;
850
851 git_config_entry_free(entry);
852
853 return ret;
854 }
855
856 int git_config_get_string_buf(
857 git_buf *out, const git_config *cfg, const char *name)
858 {
859 git_config_entry *entry;
860 int ret;
861 const char *str;
862
863 git_buf_sanitize(out);
864
865 ret = get_entry(&entry, cfg, name, true, GET_ALL_ERRORS);
866 str = !ret ? (entry->value ? entry->value : "") : NULL;
867
868 if (str)
869 ret = git_buf_puts(out, str);
870
871 git_config_entry_free(entry);
872
873 return ret;
874 }
875
876 char *git_config__get_string_force(
877 const git_config *cfg, const char *key, const char *fallback_value)
878 {
879 git_config_entry *entry;
880 char *ret;
881
882 get_entry(&entry, cfg, key, false, GET_NO_ERRORS);
883 ret = (entry && entry->value) ? git__strdup(entry->value) : fallback_value ? git__strdup(fallback_value) : NULL;
884 git_config_entry_free(entry);
885
886 return ret;
887 }
888
889 int git_config__get_bool_force(
890 const git_config *cfg, const char *key, int fallback_value)
891 {
892 int val = fallback_value;
893 git_config_entry *entry;
894
895 get_entry(&entry, cfg, key, false, GET_NO_ERRORS);
896
897 if (entry && git_config_parse_bool(&val, entry->value) < 0)
898 giterr_clear();
899
900 git_config_entry_free(entry);
901 return val;
902 }
903
904 int git_config__get_int_force(
905 const git_config *cfg, const char *key, int fallback_value)
906 {
907 int32_t val = (int32_t)fallback_value;
908 git_config_entry *entry;
909
910 get_entry(&entry, cfg, key, false, GET_NO_ERRORS);
911
912 if (entry && git_config_parse_int32(&val, entry->value) < 0)
913 giterr_clear();
914
915 git_config_entry_free(entry);
916 return (int)val;
917 }
918
919 int git_config_get_multivar_foreach(
920 const git_config *cfg, const char *name, const char *regexp,
921 git_config_foreach_cb cb, void *payload)
922 {
923 int err, found;
924 git_config_iterator *iter;
925 git_config_entry *entry;
926
927 if ((err = git_config_multivar_iterator_new(&iter, cfg, name, regexp)) < 0)
928 return err;
929
930 found = 0;
931 while ((err = iter->next(&entry, iter)) == 0) {
932 found = 1;
933
934 if ((err = cb(entry, payload)) != 0) {
935 giterr_set_after_callback(err);
936 break;
937 }
938 }
939
940 iter->free(iter);
941 if (err == GIT_ITEROVER)
942 err = 0;
943
944 if (found == 0 && err == 0)
945 err = config_error_notfound(name);
946
947 return err;
948 }
949
950 typedef struct {
951 git_config_iterator parent;
952 git_config_iterator *iter;
953 char *name;
954 regex_t regex;
955 int have_regex;
956 } multivar_iter;
957
958 static int multivar_iter_next(git_config_entry **entry, git_config_iterator *_iter)
959 {
960 multivar_iter *iter = (multivar_iter *) _iter;
961 int error = 0;
962
963 while ((error = iter->iter->next(entry, iter->iter)) == 0) {
964 if (git__strcmp(iter->name, (*entry)->name))
965 continue;
966
967 if (!iter->have_regex)
968 return 0;
969
970 if (regexec(&iter->regex, (*entry)->value, 0, NULL, 0) == 0)
971 return 0;
972 }
973
974 return error;
975 }
976
977 void multivar_iter_free(git_config_iterator *_iter)
978 {
979 multivar_iter *iter = (multivar_iter *) _iter;
980
981 iter->iter->free(iter->iter);
982
983 git__free(iter->name);
984 if (iter->have_regex)
985 regfree(&iter->regex);
986 git__free(iter);
987 }
988
989 int git_config_multivar_iterator_new(git_config_iterator **out, const git_config *cfg, const char *name, const char *regexp)
990 {
991 multivar_iter *iter = NULL;
992 git_config_iterator *inner = NULL;
993 int error;
994
995 if ((error = git_config_iterator_new(&inner, cfg)) < 0)
996 return error;
997
998 iter = git__calloc(1, sizeof(multivar_iter));
999 GITERR_CHECK_ALLOC(iter);
1000
1001 if ((error = git_config__normalize_name(name, &iter->name)) < 0)
1002 goto on_error;
1003
1004 if (regexp != NULL) {
1005 error = regcomp(&iter->regex, regexp, REG_EXTENDED);
1006 if (error != 0) {
1007 giterr_set_regex(&iter->regex, error);
1008 error = -1;
1009 regfree(&iter->regex);
1010 goto on_error;
1011 }
1012
1013 iter->have_regex = 1;
1014 }
1015
1016 iter->iter = inner;
1017 iter->parent.free = multivar_iter_free;
1018 iter->parent.next = multivar_iter_next;
1019
1020 *out = (git_config_iterator *) iter;
1021
1022 return 0;
1023
1024 on_error:
1025
1026 inner->free(inner);
1027 git__free(iter);
1028 return error;
1029 }
1030
1031 int git_config_set_multivar(git_config *cfg, const char *name, const char *regexp, const char *value)
1032 {
1033 git_config_backend *file;
1034 file_internal *internal;
1035
1036 internal = git_vector_get(&cfg->files, 0);
1037 if (!internal || !internal->file)
1038 return config_error_nofiles(name);
1039 file = internal->file;
1040
1041 return file->set_multivar(file, name, regexp, value);
1042 }
1043
1044 int git_config_delete_multivar(git_config *cfg, const char *name, const char *regexp)
1045 {
1046 git_config_backend *file;
1047 file_internal *internal;
1048
1049 internal = git_vector_get(&cfg->files, 0);
1050 if (!internal || !internal->file)
1051 return config_error_nofiles(name);
1052 file = internal->file;
1053
1054 return file->del_multivar(file, name, regexp);
1055 }
1056
1057 int git_config_next(git_config_entry **entry, git_config_iterator *iter)
1058 {
1059 return iter->next(entry, iter);
1060 }
1061
1062 void git_config_iterator_free(git_config_iterator *iter)
1063 {
1064 if (iter == NULL)
1065 return;
1066
1067 iter->free(iter);
1068 }
1069
1070 int git_config_find_global(git_buf *path)
1071 {
1072 git_buf_sanitize(path);
1073 return git_sysdir_find_global_file(path, GIT_CONFIG_FILENAME_GLOBAL);
1074 }
1075
1076 int git_config_find_xdg(git_buf *path)
1077 {
1078 git_buf_sanitize(path);
1079 return git_sysdir_find_xdg_file(path, GIT_CONFIG_FILENAME_XDG);
1080 }
1081
1082 int git_config_find_system(git_buf *path)
1083 {
1084 git_buf_sanitize(path);
1085 return git_sysdir_find_system_file(path, GIT_CONFIG_FILENAME_SYSTEM);
1086 }
1087
1088 int git_config__global_location(git_buf *buf)
1089 {
1090 const git_buf *paths;
1091 const char *sep, *start;
1092
1093 if (git_sysdir_get(&paths, GIT_SYSDIR_GLOBAL) < 0)
1094 return -1;
1095
1096 /* no paths, so give up */
1097 if (!paths || !git_buf_len(paths))
1098 return -1;
1099
1100 /* find unescaped separator or end of string */
1101 for (sep = start = git_buf_cstr(paths); *sep; ++sep) {
1102 if (*sep == GIT_PATH_LIST_SEPARATOR &&
1103 (sep <= start || sep[-1] != '\\'))
1104 break;
1105 }
1106
1107 if (git_buf_set(buf, start, (size_t)(sep - start)) < 0)
1108 return -1;
1109
1110 return git_buf_joinpath(buf, buf->ptr, GIT_CONFIG_FILENAME_GLOBAL);
1111 }
1112
1113 int git_config_open_default(git_config **out)
1114 {
1115 int error;
1116 git_config *cfg = NULL;
1117 git_buf buf = GIT_BUF_INIT;
1118
1119 if ((error = git_config_new(&cfg)) < 0)
1120 return error;
1121
1122 if (!git_config_find_global(&buf) || !git_config__global_location(&buf)) {
1123 error = git_config_add_file_ondisk(cfg, buf.ptr,
1124 GIT_CONFIG_LEVEL_GLOBAL, 0);
1125 }
1126
1127 if (!error && !git_config_find_xdg(&buf))
1128 error = git_config_add_file_ondisk(cfg, buf.ptr,
1129 GIT_CONFIG_LEVEL_XDG, 0);
1130
1131 if (!error && !git_config_find_system(&buf))
1132 error = git_config_add_file_ondisk(cfg, buf.ptr,
1133 GIT_CONFIG_LEVEL_SYSTEM, 0);
1134
1135 git_buf_free(&buf);
1136
1137 if (error) {
1138 git_config_free(cfg);
1139 cfg = NULL;
1140 }
1141
1142 *out = cfg;
1143
1144 return error;
1145 }
1146
1147 /***********
1148 * Parsers
1149 ***********/
1150
1151 int git_config_lookup_map_value(
1152 int *out,
1153 const git_cvar_map *maps,
1154 size_t map_n,
1155 const char *value)
1156 {
1157 size_t i;
1158
1159 if (!value)
1160 goto fail_parse;
1161
1162 for (i = 0; i < map_n; ++i) {
1163 const git_cvar_map *m = maps + i;
1164
1165 switch (m->cvar_type) {
1166 case GIT_CVAR_FALSE:
1167 case GIT_CVAR_TRUE: {
1168 int bool_val;
1169
1170 if (git__parse_bool(&bool_val, value) == 0 &&
1171 bool_val == (int)m->cvar_type) {
1172 *out = m->map_value;
1173 return 0;
1174 }
1175 break;
1176 }
1177
1178 case GIT_CVAR_INT32:
1179 if (git_config_parse_int32(out, value) == 0)
1180 return 0;
1181 break;
1182
1183 case GIT_CVAR_STRING:
1184 if (strcasecmp(value, m->str_match) == 0) {
1185 *out = m->map_value;
1186 return 0;
1187 }
1188 break;
1189 }
1190 }
1191
1192 fail_parse:
1193 giterr_set(GITERR_CONFIG, "Failed to map '%s'", value);
1194 return -1;
1195 }
1196
1197 int git_config_parse_bool(int *out, const char *value)
1198 {
1199 if (git__parse_bool(out, value) == 0)
1200 return 0;
1201
1202 if (git_config_parse_int32(out, value) == 0) {
1203 *out = !!(*out);
1204 return 0;
1205 }
1206
1207 giterr_set(GITERR_CONFIG, "Failed to parse '%s' as a boolean value", value);
1208 return -1;
1209 }
1210
1211 int git_config_parse_int64(int64_t *out, const char *value)
1212 {
1213 const char *num_end;
1214 int64_t num;
1215
1216 if (!value || git__strtol64(&num, value, &num_end, 0) < 0)
1217 goto fail_parse;
1218
1219 switch (*num_end) {
1220 case 'g':
1221 case 'G':
1222 num *= 1024;
1223 /* fallthrough */
1224
1225 case 'm':
1226 case 'M':
1227 num *= 1024;
1228 /* fallthrough */
1229
1230 case 'k':
1231 case 'K':
1232 num *= 1024;
1233
1234 /* check that that there are no more characters after the
1235 * given modifier suffix */
1236 if (num_end[1] != '\0')
1237 return -1;
1238
1239 /* fallthrough */
1240
1241 case '\0':
1242 *out = num;
1243 return 0;
1244
1245 default:
1246 goto fail_parse;
1247 }
1248
1249 fail_parse:
1250 giterr_set(GITERR_CONFIG, "Failed to parse '%s' as an integer", value ? value : "(null)");
1251 return -1;
1252 }
1253
1254 int git_config_parse_int32(int32_t *out, const char *value)
1255 {
1256 int64_t tmp;
1257 int32_t truncate;
1258
1259 if (git_config_parse_int64(&tmp, value) < 0)
1260 goto fail_parse;
1261
1262 truncate = tmp & 0xFFFFFFFF;
1263 if (truncate != tmp)
1264 goto fail_parse;
1265
1266 *out = truncate;
1267 return 0;
1268
1269 fail_parse:
1270 giterr_set(GITERR_CONFIG, "Failed to parse '%s' as a 32-bit integer", value ? value : "(null)");
1271 return -1;
1272 }
1273
1274 int git_config_parse_path(git_buf *out, const char *value)
1275 {
1276 int error = 0;
1277 const git_buf *home;
1278
1279 assert(out && value);
1280
1281 git_buf_sanitize(out);
1282
1283 if (value[0] == '~') {
1284 if (value[1] != '\0' && value[1] != '/') {
1285 giterr_set(GITERR_CONFIG, "retrieving a homedir by name is not supported");
1286 return -1;
1287 }
1288
1289 if ((error = git_sysdir_get(&home, GIT_SYSDIR_GLOBAL)) < 0)
1290 return error;
1291
1292 git_buf_sets(out, home->ptr);
1293 git_buf_puts(out, value + 1);
1294
1295 if (git_buf_oom(out))
1296 return -1;
1297
1298 return 0;
1299 }
1300
1301 return git_buf_sets(out, value);
1302 }
1303
1304 /* Take something the user gave us and make it nice for our hash function */
1305 int git_config__normalize_name(const char *in, char **out)
1306 {
1307 char *name, *fdot, *ldot;
1308
1309 assert(in && out);
1310
1311 name = git__strdup(in);
1312 GITERR_CHECK_ALLOC(name);
1313
1314 fdot = strchr(name, '.');
1315 ldot = strrchr(name, '.');
1316
1317 if (fdot == NULL || fdot == name || ldot == NULL || !ldot[1])
1318 goto invalid;
1319
1320 /* Validate and downcase up to first dot and after last dot */
1321 if (git_config_file_normalize_section(name, fdot) < 0 ||
1322 git_config_file_normalize_section(ldot + 1, NULL) < 0)
1323 goto invalid;
1324
1325 /* If there is a middle range, make sure it doesn't have newlines */
1326 while (fdot < ldot)
1327 if (*fdot++ == '\n')
1328 goto invalid;
1329
1330 *out = name;
1331 return 0;
1332
1333 invalid:
1334 git__free(name);
1335 giterr_set(GITERR_CONFIG, "Invalid config item name '%s'", in);
1336 return GIT_EINVALIDSPEC;
1337 }
1338
1339 struct rename_data {
1340 git_config *config;
1341 git_buf *name;
1342 size_t old_len;
1343 };
1344
1345 static int rename_config_entries_cb(
1346 const git_config_entry *entry,
1347 void *payload)
1348 {
1349 int error = 0;
1350 struct rename_data *data = (struct rename_data *)payload;
1351 size_t base_len = git_buf_len(data->name);
1352
1353 if (base_len > 0 &&
1354 !(error = git_buf_puts(data->name, entry->name + data->old_len)))
1355 {
1356 error = git_config_set_string(
1357 data->config, git_buf_cstr(data->name), entry->value);
1358
1359 git_buf_truncate(data->name, base_len);
1360 }
1361
1362 if (!error)
1363 error = git_config_delete_entry(data->config, entry->name);
1364
1365 return error;
1366 }
1367
1368 int git_config_rename_section(
1369 git_repository *repo,
1370 const char *old_section_name,
1371 const char *new_section_name)
1372 {
1373 git_config *config;
1374 git_buf pattern = GIT_BUF_INIT, replace = GIT_BUF_INIT;
1375 int error = 0;
1376 struct rename_data data;
1377
1378 git_buf_text_puts_escape_regex(&pattern, old_section_name);
1379
1380 if ((error = git_buf_puts(&pattern, "\\..+")) < 0)
1381 goto cleanup;
1382
1383 if ((error = git_repository_config__weakptr(&config, repo)) < 0)
1384 goto cleanup;
1385
1386 data.config = config;
1387 data.name = &replace;
1388 data.old_len = strlen(old_section_name) + 1;
1389
1390 if ((error = git_buf_join(&replace, '.', new_section_name, "")) < 0)
1391 goto cleanup;
1392
1393 if (new_section_name != NULL &&
1394 (error = git_config_file_normalize_section(
1395 replace.ptr, strchr(replace.ptr, '.'))) < 0)
1396 {
1397 giterr_set(
1398 GITERR_CONFIG, "Invalid config section '%s'", new_section_name);
1399 goto cleanup;
1400 }
1401
1402 error = git_config_foreach_match(
1403 config, git_buf_cstr(&pattern), rename_config_entries_cb, &data);
1404
1405 cleanup:
1406 git_buf_free(&pattern);
1407 git_buf_free(&replace);
1408
1409 return error;
1410 }
1411
1412 int git_config_init_backend(git_config_backend *backend, unsigned int version)
1413 {
1414 GIT_INIT_STRUCTURE_FROM_TEMPLATE(
1415 backend, version, git_config_backend, GIT_CONFIG_BACKEND_INIT);
1416 return 0;
1417 }