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