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