]>
Commit | Line | Data |
---|---|---|
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 |
25 | void git_config_entry_free(git_config_entry *entry) |
26 | { | |
27 | if (!entry) | |
28 | return; | |
29 | ||
30 | entry->free(entry); | |
31 | } | |
32 | ||
c0335005 | 33 | typedef 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 | 40 | static 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 | 49 | static 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 |
65 | void git_config_free(git_config *cfg) |
66 | { | |
67 | if (cfg == NULL) | |
68 | return; | |
69 | ||
70 | GIT_REFCOUNT_DEC(cfg, config_free); | |
71 | } | |
72 | ||
c0335005 | 73 | static 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 | 81 | int 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 | 100 | int 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 | 134 | int 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 |
152 | int 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 |
184 | static 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 | ||
218 | static 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 | 228 | static 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 | ||
252 | static 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 |
276 | int git_config_open_global(git_config **cfg_out, git_config *cfg) |
277 | { | |
278 | if (!git_config_open_level(cfg_out, cfg, GIT_CONFIG_LEVEL_XDG)) | |
279 | return 0; | |
280 | ||
281 | return git_config_open_level(cfg_out, cfg, GIT_CONFIG_LEVEL_GLOBAL); | |
282 | } | |
283 | ||
a1abe66a | 284 | int 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 | 309 | int 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 |
346 | typedef 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 | ||
354 | static 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 | ||
370 | static 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 |
417 | static int all_iter_glob_next(git_config_entry **entry, git_config_iterator *_iter) |
418 | { | |
419 | int error; | |
420 | all_iter *iter = (all_iter *) _iter; | |
421 | ||
422 | /* | |
423 | * We use the "normal" function to grab the next one across | |
424 | * backends and then apply the regex | |
425 | */ | |
426 | while ((error = all_iter_next(entry, _iter)) == 0) { | |
427 | /* skip non-matching keys if regexp was provided */ | |
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 |
438 | static void all_iter_free(git_config_iterator *_iter) |
439 | { | |
440 | all_iter *iter = (all_iter *) _iter; | |
441 | ||
442 | if (iter->current) | |
443 | iter->current->free(iter->current); | |
444 | ||
445 | git__free(iter); | |
446 | } | |
447 | ||
54f3a572 CMN |
448 | static 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 |
456 | int git_config_iterator_new(git_config_iterator **out, const git_config *cfg) |
457 | { | |
458 | all_iter *iter; | |
459 | ||
460 | iter = git__calloc(1, sizeof(all_iter)); | |
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 |
474 | int git_config_iterator_glob_new(git_config_iterator **out, const git_config *cfg, const char *regexp) |
475 | { | |
476 | all_iter *iter; | |
477 | int result; | |
478 | ||
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 | 500 | int 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 |
506 | int 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(®ex, 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(®ex, 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(®ex); |
a603c191 | 541 | |
eba73992 | 542 | iter->free(iter); |
a603c191 | 543 | |
25e0b157 | 544 | return error; |
a603c191 NG |
545 | } |
546 | ||
b3ff1dab | 547 | int 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 |
579 | typedef enum { |
580 | BACKEND_USE_SET, | |
581 | BACKEND_USE_DELETE | |
582 | } backend_use; | |
583 | ||
584 | static const char *uses[] = { | |
585 | "set", | |
586 | "delete" | |
587 | }; | |
588 | ||
589 | static int get_backend_for_use(git_config_backend **out, | |
590 | git_config *cfg, const char *name, backend_use use) | |
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 |
617 | int 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 | 627 | int 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 | 634 | int 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 |
639 | int 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 |
644 | int 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 |
665 | int git_config__update_entry( |
666 | git_config *config, | |
667 | const char *key, | |
668 | const char *value, | |
669 | bool overwrite_existing, | |
670 | bool only_if_existing) | |
671 | { | |
672 | int error = 0; | |
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 | |
700 | static 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 | ||
706 | enum { | |
707 | GET_ALL_ERRORS = 0, | |
708 | GET_NO_MISSING = 1, | |
709 | GET_NO_ERRORS = 2 | |
710 | }; | |
711 | ||
712 | static 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 | ||
745 | cleanup: | |
746 | if (res == GIT_ENOTFOUND) | |
747 | res = (want_errors > GET_ALL_ERRORS) ? 0 : config_error_notfound(name); | |
748 | else if (res && (want_errors == GET_NO_ERRORS)) { | |
ac3d33df | 749 | git_error_clear(); |
9f77b3f6 RB |
750 | res = 0; |
751 | } | |
752 | ||
753 | return res; | |
754 | } | |
755 | ||
756 | int 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 | ||
762 | int 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 |
772 | int 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 | 791 | int 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 | 805 | int 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 | 819 | int 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 | ||
833 | static 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 |
849 | int 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 |
863 | int 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 | ||
882 | int git_config_get_string_buf( | |
883 | git_buf *out, const git_config *cfg, const char *name) | |
884 | { | |
885 | git_config_entry *entry; | |
886 | int ret; | |
887 | const char *str; | |
888 | ||
889 | git_buf_sanitize(out); | |
890 | ||
891 | ret = get_entry(&entry, cfg, name, true, GET_ALL_ERRORS); | |
892 | str = !ret ? (entry->value ? entry->value : "") : NULL; | |
893 | ||
894 | if (str) | |
895 | ret = git_buf_puts(out, str); | |
896 | ||
897 | git_config_entry_free(entry); | |
898 | ||
9f77b3f6 | 899 | return ret; |
3f663178 RB |
900 | } |
901 | ||
9a97f49e | 902 | char *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 |
915 | int 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 |
930 | int 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 | 945 | int 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 | 976 | typedef 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 | 984 | static 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 | 1003 | static 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 | 1015 | int 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 | |
1045 | on_error: | |
1046 | ||
1047 | inner->free(inner); | |
1048 | git__free(iter); | |
1049 | return error; | |
3a7ffc29 CMN |
1050 | } |
1051 | ||
3005855f CMN |
1052 | int 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 |
1062 | int 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 |
1072 | int git_config_next(git_config_entry **entry, git_config_iterator *iter) |
1073 | { | |
1074 | return iter->next(entry, iter); | |
1075 | } | |
1076 | ||
1077 | void git_config_iterator_free(git_config_iterator *iter) | |
1078 | { | |
e3c6a1bf BR |
1079 | if (iter == NULL) |
1080 | return; | |
1081 | ||
1e96c9d5 CMN |
1082 | iter->free(iter); |
1083 | } | |
1084 | ||
ee550477 | 1085 | int 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 | 1091 | int 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 | 1097 | int 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 |
1103 | int 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 |
1116 | int 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 | 1141 | int 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 | 1179 | int 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 | ||
1200 | int 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 | 1222 | int 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 |
1264 | int git_config_lookup_map_enum(git_configmap_t *type_out, const char **str_out, |
1265 | const git_configmap *maps, size_t map_n, int enum_val) | |
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 | 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 | ||
ac3d33df | 1294 | git_error_set(GIT_ERROR_CONFIG, "failed to parse '%s' as a boolean value", value); |
a1abe66a | 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 | ||
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 | ||
1336 | fail_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 | ||
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: | |
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 |
1361 | int 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 |
1379 | static int normalize_section(char *start, char *end) |
1380 | { | |
1381 | char *scan; | |
1382 | ||
1383 | if (start == end) | |
1384 | return GIT_EINVALIDSPEC; | |
1385 | ||
1386 | /* Validate and downcase range */ | |
1387 | for (scan = start; *scan; ++scan) { | |
1388 | if (end && scan >= end) | |
1389 | break; | |
1390 | if (isalnum(*scan)) | |
1391 | *scan = (char)git__tolower(*scan); | |
1392 | else if (*scan != '-' || scan == start) | |
1393 | return GIT_EINVALIDSPEC; | |
1394 | } | |
1395 | ||
1396 | if (scan == start) | |
1397 | return GIT_EINVALIDSPEC; | |
1398 | ||
1399 | return 0; | |
1400 | } | |
1401 | ||
1402 | ||
f4be8209 CMN |
1403 | /* Take something the user gave us and make it nice for our hash function */ |
1404 | int git_config__normalize_name(const char *in, char **out) | |
1405 | { | |
1406 | char *name, *fdot, *ldot; | |
1407 | ||
1408 | assert(in && out); | |
1409 | ||
1410 | name = git__strdup(in); | |
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 | ||
1432 | invalid: | |
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 | 1438 | struct rename_data { |
aba70781 | 1439 | git_config *config; |
1e7799e8 RB |
1440 | git_buf *name; |
1441 | size_t old_len; | |
aba70781 | 1442 | }; |
1443 | ||
1444 | static 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 | ||
1467 | int git_config_rename_section( | |
1468 | git_repository *repo, | |
1469 | const char *old_section_name, | |
1470 | const char *new_section_name) | |
1471 | { | |
1472 | git_config *config; | |
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 | 1503 | cleanup: |
ac3d33df JK |
1504 | git_buf_dispose(&pattern); |
1505 | git_buf_dispose(&replace); | |
1e7799e8 | 1506 | |
aba70781 | 1507 | return error; |
1508 | } | |
b9f81997 | 1509 | |
bc91347b | 1510 | int 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 | } |