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