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