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