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