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