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