]> git.proxmox.com Git - libgit2.git/blob - src/config_file.c
c9e36493ea63dc831c475ad1dd0d39945c37f656
[libgit2.git] / src / config_file.c
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 "array.h"
14 #include "buffer.h"
15 #include "config_backend.h"
16 #include "config_entries.h"
17 #include "config_parse.h"
18 #include "filebuf.h"
19 #include "regexp.h"
20 #include "sysdir.h"
21 #include "wildmatch.h"
22
23 /* Max depth for [include] directives */
24 #define MAX_INCLUDE_DEPTH 10
25
26 typedef struct config_file {
27 git_futils_filestamp stamp;
28 git_oid checksum;
29 char *path;
30 git_array_t(struct config_file) includes;
31 } config_file;
32
33 typedef struct {
34 git_config_backend parent;
35 git_mutex values_mutex;
36 git_config_entries *entries;
37 const git_repository *repo;
38 git_config_level_t level;
39
40 git_array_t(git_config_parser) readers;
41
42 bool locked;
43 git_filebuf locked_buf;
44 git_buf locked_content;
45
46 config_file file;
47 } config_file_backend;
48
49 typedef struct {
50 const git_repository *repo;
51 config_file *file;
52 git_config_entries *entries;
53 git_config_level_t level;
54 unsigned int depth;
55 } config_file_parse_data;
56
57 static int config_file_read(git_config_entries *entries, const git_repository *repo, config_file *file, git_config_level_t level, int depth);
58 static int config_file_read_buffer(git_config_entries *entries, const git_repository *repo, config_file *file, git_config_level_t level, int depth, const char *buf, size_t buflen);
59 static int config_file_write(config_file_backend *cfg, const char *orig_key, const char *key, const git_regexp *preg, const char *value);
60 static char *escape_value(const char *ptr);
61
62 /**
63 * Take the current values map from the backend and increase its
64 * refcount. This is its own function to make sure we use the mutex to
65 * avoid the map pointer from changing under us.
66 */
67 static int config_file_entries_take(git_config_entries **out, config_file_backend *b)
68 {
69 int error;
70
71 if ((error = git_mutex_lock(&b->values_mutex)) < 0) {
72 git_error_set(GIT_ERROR_OS, "failed to lock config backend");
73 return error;
74 }
75
76 git_config_entries_incref(b->entries);
77 *out = b->entries;
78
79 git_mutex_unlock(&b->values_mutex);
80
81 return 0;
82 }
83
84 static void config_file_clear(config_file *file)
85 {
86 config_file *include;
87 uint32_t i;
88
89 if (file == NULL)
90 return;
91
92 git_array_foreach(file->includes, i, include) {
93 config_file_clear(include);
94 }
95 git_array_clear(file->includes);
96
97 git__free(file->path);
98 }
99
100 static int config_file_open(git_config_backend *cfg, git_config_level_t level, const git_repository *repo)
101 {
102 config_file_backend *b = GIT_CONTAINER_OF(cfg, config_file_backend, parent);
103 int res;
104
105 b->level = level;
106 b->repo = repo;
107
108 if ((res = git_config_entries_new(&b->entries)) < 0)
109 return res;
110
111 if (!git_path_exists(b->file.path))
112 return 0;
113
114 if (res < 0 || (res = config_file_read(b->entries, repo, &b->file, level, 0)) < 0) {
115 git_config_entries_free(b->entries);
116 b->entries = NULL;
117 }
118
119 return res;
120 }
121
122 static int config_file_is_modified(int *modified, config_file *file)
123 {
124 config_file *include;
125 git_buf buf = GIT_BUF_INIT;
126 git_oid hash;
127 uint32_t i;
128 int error = 0;
129
130 *modified = 0;
131
132 if (!git_futils_filestamp_check(&file->stamp, file->path))
133 goto check_includes;
134
135 if ((error = git_futils_readbuffer(&buf, file->path)) < 0)
136 goto out;
137
138 if ((error = git_hash_buf(&hash, buf.ptr, buf.size)) < 0)
139 goto out;
140
141 if (!git_oid_equal(&hash, &file->checksum)) {
142 *modified = 1;
143 goto out;
144 }
145
146 check_includes:
147 git_array_foreach(file->includes, i, include) {
148 if ((error = config_file_is_modified(modified, include)) < 0 || *modified)
149 goto out;
150 }
151
152 out:
153 git_buf_dispose(&buf);
154
155 return error;
156 }
157
158 static int config_file_set_entries(git_config_backend *cfg, git_config_entries *entries)
159 {
160 config_file_backend *b = GIT_CONTAINER_OF(cfg, config_file_backend, parent);
161 git_config_entries *old = NULL;
162 config_file *include;
163 int error;
164 uint32_t i;
165
166 if (b->parent.readonly) {
167 git_error_set(GIT_ERROR_CONFIG, "this backend is read-only");
168 return -1;
169 }
170
171 git_array_foreach(b->file.includes, i, include)
172 config_file_clear(include);
173 git_array_clear(b->file.includes);
174
175 if ((error = git_mutex_lock(&b->values_mutex)) < 0) {
176 git_error_set(GIT_ERROR_OS, "failed to lock config backend");
177 goto out;
178 }
179
180 old = b->entries;
181 b->entries = entries;
182
183 git_mutex_unlock(&b->values_mutex);
184
185 out:
186 git_config_entries_free(old);
187 return error;
188 }
189
190 static int config_file_refresh_from_buffer(git_config_backend *cfg, const char *buf, size_t buflen)
191 {
192 config_file_backend *b = GIT_CONTAINER_OF(cfg, config_file_backend, parent);
193 git_config_entries *entries = NULL;
194 int error;
195
196 if ((error = git_config_entries_new(&entries)) < 0 ||
197 (error = config_file_read_buffer(entries, b->repo, &b->file,
198 b->level, 0, buf, buflen)) < 0 ||
199 (error = config_file_set_entries(cfg, entries)) < 0)
200 goto out;
201
202 entries = NULL;
203 out:
204 git_config_entries_free(entries);
205 return error;
206 }
207
208 static int config_file_refresh(git_config_backend *cfg)
209 {
210 config_file_backend *b = GIT_CONTAINER_OF(cfg, config_file_backend, parent);
211 git_config_entries *entries = NULL;
212 int error, modified;
213
214 if (cfg->readonly)
215 return 0;
216
217 if ((error = config_file_is_modified(&modified, &b->file)) < 0 && error != GIT_ENOTFOUND)
218 goto out;
219
220 if (!modified)
221 return 0;
222
223 if ((error = git_config_entries_new(&entries)) < 0 ||
224 (error = config_file_read(entries, b->repo, &b->file, b->level, 0)) < 0 ||
225 (error = config_file_set_entries(cfg, entries)) < 0)
226 goto out;
227
228 entries = NULL;
229 out:
230 git_config_entries_free(entries);
231
232 return (error == GIT_ENOTFOUND) ? 0 : error;
233 }
234
235 static void config_file_free(git_config_backend *_backend)
236 {
237 config_file_backend *backend = GIT_CONTAINER_OF(_backend, config_file_backend, parent);
238
239 if (backend == NULL)
240 return;
241
242 config_file_clear(&backend->file);
243 git_config_entries_free(backend->entries);
244 git_mutex_free(&backend->values_mutex);
245 git__free(backend);
246 }
247
248 static int config_file_iterator(
249 git_config_iterator **iter,
250 struct git_config_backend *backend)
251 {
252 config_file_backend *b = GIT_CONTAINER_OF(backend, config_file_backend, parent);
253 git_config_entries *dupped = NULL, *entries = NULL;
254 int error;
255
256 if ((error = config_file_refresh(backend)) < 0 ||
257 (error = config_file_entries_take(&entries, b)) < 0 ||
258 (error = git_config_entries_dup(&dupped, entries)) < 0 ||
259 (error = git_config_entries_iterator_new(iter, dupped)) < 0)
260 goto out;
261
262 out:
263 /* Let iterator delete duplicated entries when it's done */
264 git_config_entries_free(entries);
265 git_config_entries_free(dupped);
266 return error;
267 }
268
269 static int config_file_snapshot(git_config_backend **out, git_config_backend *backend)
270 {
271 return git_config_backend_snapshot(out, backend);
272 }
273
274 static int config_file_set(git_config_backend *cfg, const char *name, const char *value)
275 {
276 config_file_backend *b = GIT_CONTAINER_OF(cfg, config_file_backend, parent);
277 git_config_entries *entries;
278 git_config_entry *existing;
279 char *key, *esc_value = NULL;
280 int error;
281
282 if ((error = git_config__normalize_name(name, &key)) < 0)
283 return error;
284
285 if ((error = config_file_entries_take(&entries, b)) < 0)
286 return error;
287
288 /* Check whether we'd be modifying an included or multivar key */
289 if ((error = git_config_entries_get_unique(&existing, entries, key)) < 0) {
290 if (error != GIT_ENOTFOUND)
291 goto out;
292 error = 0;
293 } else if ((!existing->value && !value) ||
294 (existing->value && value && !strcmp(existing->value, value))) {
295 /* don't update if old and new values already match */
296 error = 0;
297 goto out;
298 }
299
300 /* No early returns due to sanity checks, let's write it out and refresh */
301 if (value) {
302 esc_value = escape_value(value);
303 GIT_ERROR_CHECK_ALLOC(esc_value);
304 }
305
306 if ((error = config_file_write(b, name, key, NULL, esc_value)) < 0)
307 goto out;
308
309 out:
310 git_config_entries_free(entries);
311 git__free(esc_value);
312 git__free(key);
313 return error;
314 }
315
316 /* release the map containing the entry as an equivalent to freeing it */
317 static void config_file_entry_free(git_config_entry *entry)
318 {
319 git_config_entries *entries = (git_config_entries *) entry->payload;
320 git_config_entries_free(entries);
321 }
322
323 /*
324 * Internal function that actually gets the value in string form
325 */
326 static int config_file_get(git_config_backend *cfg, const char *key, git_config_entry **out)
327 {
328 config_file_backend *h = GIT_CONTAINER_OF(cfg, config_file_backend, parent);
329 git_config_entries *entries = NULL;
330 git_config_entry *entry;
331 int error = 0;
332
333 if (!h->parent.readonly && ((error = config_file_refresh(cfg)) < 0))
334 return error;
335
336 if ((error = config_file_entries_take(&entries, h)) < 0)
337 return error;
338
339 if ((error = (git_config_entries_get(&entry, entries, key))) < 0) {
340 git_config_entries_free(entries);
341 return error;
342 }
343
344 entry->free = config_file_entry_free;
345 entry->payload = entries;
346 *out = entry;
347
348 return 0;
349 }
350
351 static int config_file_set_multivar(
352 git_config_backend *cfg, const char *name, const char *regexp, const char *value)
353 {
354 config_file_backend *b = GIT_CONTAINER_OF(cfg, config_file_backend, parent);
355 git_regexp preg;
356 int result;
357 char *key;
358
359 assert(regexp);
360
361 if ((result = git_config__normalize_name(name, &key)) < 0)
362 return result;
363
364 if ((result = git_regexp_compile(&preg, regexp, 0)) < 0)
365 goto out;
366
367 /* If we do have it, set call config_file_write() and reload */
368 if ((result = config_file_write(b, name, key, &preg, value)) < 0)
369 goto out;
370
371 out:
372 git__free(key);
373 git_regexp_dispose(&preg);
374
375 return result;
376 }
377
378 static int config_file_delete(git_config_backend *cfg, const char *name)
379 {
380 config_file_backend *b = GIT_CONTAINER_OF(cfg, config_file_backend, parent);
381 git_config_entries *entries = NULL;
382 git_config_entry *entry;
383 char *key = NULL;
384 int error;
385
386 if ((error = git_config__normalize_name(name, &key)) < 0)
387 goto out;
388
389 if ((error = config_file_entries_take(&entries, b)) < 0)
390 goto out;
391
392 /* Check whether we'd be modifying an included or multivar key */
393 if ((error = git_config_entries_get_unique(&entry, entries, key)) < 0) {
394 if (error == GIT_ENOTFOUND)
395 git_error_set(GIT_ERROR_CONFIG, "could not find key '%s' to delete", name);
396 goto out;
397 }
398
399 if ((error = config_file_write(b, name, entry->name, NULL, NULL)) < 0)
400 goto out;
401
402 out:
403 git_config_entries_free(entries);
404 git__free(key);
405 return error;
406 }
407
408 static int config_file_delete_multivar(git_config_backend *cfg, const char *name, const char *regexp)
409 {
410 config_file_backend *b = GIT_CONTAINER_OF(cfg, config_file_backend, parent);
411 git_config_entries *entries = NULL;
412 git_config_entry *entry = NULL;
413 git_regexp preg = GIT_REGEX_INIT;
414 char *key = NULL;
415 int result;
416
417 if ((result = git_config__normalize_name(name, &key)) < 0)
418 goto out;
419
420 if ((result = config_file_entries_take(&entries, b)) < 0)
421 goto out;
422
423 if ((result = git_config_entries_get(&entry, entries, key)) < 0) {
424 if (result == GIT_ENOTFOUND)
425 git_error_set(GIT_ERROR_CONFIG, "could not find key '%s' to delete", name);
426 goto out;
427 }
428
429 if ((result = git_regexp_compile(&preg, regexp, 0)) < 0)
430 goto out;
431
432 if ((result = config_file_write(b, name, key, &preg, NULL)) < 0)
433 goto out;
434
435 out:
436 git_config_entries_free(entries);
437 git__free(key);
438 git_regexp_dispose(&preg);
439 return result;
440 }
441
442 static int config_file_lock(git_config_backend *_cfg)
443 {
444 config_file_backend *cfg = GIT_CONTAINER_OF(_cfg, config_file_backend, parent);
445 int error;
446
447 if ((error = git_filebuf_open(&cfg->locked_buf, cfg->file.path, 0, GIT_CONFIG_FILE_MODE)) < 0)
448 return error;
449
450 error = git_futils_readbuffer(&cfg->locked_content, cfg->file.path);
451 if (error < 0 && error != GIT_ENOTFOUND) {
452 git_filebuf_cleanup(&cfg->locked_buf);
453 return error;
454 }
455
456 cfg->locked = true;
457 return 0;
458
459 }
460
461 static int config_file_unlock(git_config_backend *_cfg, int success)
462 {
463 config_file_backend *cfg = GIT_CONTAINER_OF(_cfg, config_file_backend, parent);
464 int error = 0;
465
466 if (success) {
467 git_filebuf_write(&cfg->locked_buf, cfg->locked_content.ptr, cfg->locked_content.size);
468 error = git_filebuf_commit(&cfg->locked_buf);
469 }
470
471 git_filebuf_cleanup(&cfg->locked_buf);
472 git_buf_dispose(&cfg->locked_content);
473 cfg->locked = false;
474
475 return error;
476 }
477
478 int git_config_backend_from_file(git_config_backend **out, const char *path)
479 {
480 config_file_backend *backend;
481
482 backend = git__calloc(1, sizeof(config_file_backend));
483 GIT_ERROR_CHECK_ALLOC(backend);
484
485 backend->parent.version = GIT_CONFIG_BACKEND_VERSION;
486 git_mutex_init(&backend->values_mutex);
487
488 backend->file.path = git__strdup(path);
489 GIT_ERROR_CHECK_ALLOC(backend->file.path);
490 git_array_init(backend->file.includes);
491
492 backend->parent.open = config_file_open;
493 backend->parent.get = config_file_get;
494 backend->parent.set = config_file_set;
495 backend->parent.set_multivar = config_file_set_multivar;
496 backend->parent.del = config_file_delete;
497 backend->parent.del_multivar = config_file_delete_multivar;
498 backend->parent.iterator = config_file_iterator;
499 backend->parent.snapshot = config_file_snapshot;
500 backend->parent.lock = config_file_lock;
501 backend->parent.unlock = config_file_unlock;
502 backend->parent.free = config_file_free;
503
504 *out = (git_config_backend *)backend;
505
506 return 0;
507 }
508
509 static int included_path(git_buf *out, const char *dir, const char *path)
510 {
511 /* From the user's home */
512 if (path[0] == '~' && path[1] == '/')
513 return git_sysdir_expand_global_file(out, &path[1]);
514
515 return git_path_join_unrooted(out, path, dir, NULL);
516 }
517
518 /* Escape the values to write them to the file */
519 static char *escape_value(const char *ptr)
520 {
521 git_buf buf;
522 size_t len;
523 const char *esc;
524
525 assert(ptr);
526
527 len = strlen(ptr);
528 if (!len)
529 return git__calloc(1, sizeof(char));
530
531 if (git_buf_init(&buf, len) < 0)
532 return NULL;
533
534 while (*ptr != '\0') {
535 if ((esc = strchr(git_config_escaped, *ptr)) != NULL) {
536 git_buf_putc(&buf, '\\');
537 git_buf_putc(&buf, git_config_escapes[esc - git_config_escaped]);
538 } else {
539 git_buf_putc(&buf, *ptr);
540 }
541 ptr++;
542 }
543
544 if (git_buf_oom(&buf))
545 return NULL;
546
547 return git_buf_detach(&buf);
548 }
549
550 static int parse_include(config_file_parse_data *parse_data, const char *file)
551 {
552 config_file *include;
553 git_buf path = GIT_BUF_INIT;
554 char *dir;
555 int result;
556
557 if (!file)
558 return 0;
559
560 if ((result = git_path_dirname_r(&path, parse_data->file->path)) < 0)
561 return result;
562
563 dir = git_buf_detach(&path);
564 result = included_path(&path, dir, file);
565 git__free(dir);
566
567 if (result < 0)
568 return result;
569
570 include = git_array_alloc(parse_data->file->includes);
571 GIT_ERROR_CHECK_ALLOC(include);
572 memset(include, 0, sizeof(*include));
573 git_array_init(include->includes);
574 include->path = git_buf_detach(&path);
575
576 result = config_file_read(parse_data->entries, parse_data->repo, include,
577 parse_data->level, parse_data->depth+1);
578
579 if (result == GIT_ENOTFOUND) {
580 git_error_clear();
581 result = 0;
582 }
583
584 return result;
585 }
586
587 static int do_match_gitdir(
588 int *matches,
589 const git_repository *repo,
590 const char *cfg_file,
591 const char *condition,
592 bool case_insensitive)
593 {
594 git_buf pattern = GIT_BUF_INIT, gitdir = GIT_BUF_INIT;
595 int error;
596
597 if (condition[0] == '.' && git_path_is_dirsep(condition[1])) {
598 git_path_dirname_r(&pattern, cfg_file);
599 git_buf_joinpath(&pattern, pattern.ptr, condition + 2);
600 } else if (condition[0] == '~' && git_path_is_dirsep(condition[1]))
601 git_sysdir_expand_global_file(&pattern, condition + 1);
602 else if (!git_path_is_absolute(condition))
603 git_buf_joinpath(&pattern, "**", condition);
604 else
605 git_buf_sets(&pattern, condition);
606
607 if (git_path_is_dirsep(condition[strlen(condition) - 1]))
608 git_buf_puts(&pattern, "**");
609
610 if (git_buf_oom(&pattern)) {
611 error = -1;
612 goto out;
613 }
614
615 if ((error = git_repository_item_path(&gitdir, repo, GIT_REPOSITORY_ITEM_GITDIR)) < 0)
616 goto out;
617
618 if (git_path_is_dirsep(gitdir.ptr[gitdir.size - 1]))
619 git_buf_truncate(&gitdir, gitdir.size - 1);
620
621 *matches = wildmatch(pattern.ptr, gitdir.ptr,
622 WM_PATHNAME | (case_insensitive ? WM_CASEFOLD : 0)) == WM_MATCH;
623 out:
624 git_buf_dispose(&pattern);
625 git_buf_dispose(&gitdir);
626 return error;
627 }
628
629 static int conditional_match_gitdir(
630 int *matches,
631 const git_repository *repo,
632 const char *cfg_file,
633 const char *value)
634 {
635 return do_match_gitdir(matches, repo, cfg_file, value, false);
636 }
637
638 static int conditional_match_gitdir_i(
639 int *matches,
640 const git_repository *repo,
641 const char *cfg_file,
642 const char *value)
643 {
644 return do_match_gitdir(matches, repo, cfg_file, value, true);
645 }
646
647 static int conditional_match_onbranch(
648 int *matches,
649 const git_repository *repo,
650 const char *cfg_file,
651 const char *condition)
652 {
653 git_buf reference = GIT_BUF_INIT, buf = GIT_BUF_INIT;
654 int error;
655
656 GIT_UNUSED(cfg_file);
657
658 /*
659 * NOTE: you cannot use `git_repository_head` here. Looking up the
660 * HEAD reference will create the ODB, which causes us to read the
661 * repo's config for keys like core.precomposeUnicode. As we're
662 * just parsing the config right now, though, this would result in
663 * an endless recursion.
664 */
665
666 if ((error = git_buf_joinpath(&buf, git_repository_path(repo), GIT_HEAD_FILE)) < 0 ||
667 (error = git_futils_readbuffer(&reference, buf.ptr)) < 0)
668 goto out;
669 git_buf_rtrim(&reference);
670
671 if (git__strncmp(reference.ptr, GIT_SYMREF, strlen(GIT_SYMREF)))
672 goto out;
673 git_buf_consume(&reference, reference.ptr + strlen(GIT_SYMREF));
674
675 if (git__strncmp(reference.ptr, GIT_REFS_HEADS_DIR, strlen(GIT_REFS_HEADS_DIR)))
676 goto out;
677 git_buf_consume(&reference, reference.ptr + strlen(GIT_REFS_HEADS_DIR));
678
679 /*
680 * If the condition ends with a '/', then we should treat it as if
681 * it had '**' appended.
682 */
683 if ((error = git_buf_sets(&buf, condition)) < 0)
684 goto out;
685 if (git_path_is_dirsep(condition[strlen(condition) - 1]) &&
686 (error = git_buf_puts(&buf, "**")) < 0)
687 goto out;
688
689 *matches = wildmatch(buf.ptr, reference.ptr, WM_PATHNAME) == WM_MATCH;
690 out:
691 git_buf_dispose(&reference);
692 git_buf_dispose(&buf);
693
694 return error;
695
696 }
697
698 static const struct {
699 const char *prefix;
700 int (*matches)(int *matches, const git_repository *repo, const char *cfg, const char *value);
701 } conditions[] = {
702 { "gitdir:", conditional_match_gitdir },
703 { "gitdir/i:", conditional_match_gitdir_i },
704 { "onbranch:", conditional_match_onbranch }
705 };
706
707 static int parse_conditional_include(config_file_parse_data *parse_data, const char *section, const char *file)
708 {
709 char *condition;
710 size_t i;
711 int error = 0, matches;
712
713 if (!parse_data->repo || !file)
714 return 0;
715
716 condition = git__substrdup(section + strlen("includeIf."),
717 strlen(section) - strlen("includeIf.") - strlen(".path"));
718
719 for (i = 0; i < ARRAY_SIZE(conditions); i++) {
720 if (git__prefixcmp(condition, conditions[i].prefix))
721 continue;
722
723 if ((error = conditions[i].matches(&matches,
724 parse_data->repo,
725 parse_data->file->path,
726 condition + strlen(conditions[i].prefix))) < 0)
727 break;
728
729 if (matches)
730 error = parse_include(parse_data, file);
731
732 break;
733 }
734
735 git__free(condition);
736 return error;
737 }
738
739 static int read_on_variable(
740 git_config_parser *reader,
741 const char *current_section,
742 const char *var_name,
743 const char *var_value,
744 const char *line,
745 size_t line_len,
746 void *data)
747 {
748 config_file_parse_data *parse_data = (config_file_parse_data *)data;
749 git_buf buf = GIT_BUF_INIT;
750 git_config_entry *entry;
751 const char *c;
752 int result = 0;
753
754 GIT_UNUSED(reader);
755 GIT_UNUSED(line);
756 GIT_UNUSED(line_len);
757
758 if (current_section) {
759 /* TODO: Once warnings lang, we should likely warn
760 * here. Git appears to warn in most cases if it sees
761 * un-namespaced config options.
762 */
763 git_buf_puts(&buf, current_section);
764 git_buf_putc(&buf, '.');
765 }
766
767 for (c = var_name; *c; c++)
768 git_buf_putc(&buf, git__tolower(*c));
769
770 if (git_buf_oom(&buf))
771 return -1;
772
773 entry = git__calloc(1, sizeof(git_config_entry));
774 GIT_ERROR_CHECK_ALLOC(entry);
775 entry->name = git_buf_detach(&buf);
776 entry->value = var_value ? git__strdup(var_value) : NULL;
777 entry->level = parse_data->level;
778 entry->include_depth = parse_data->depth;
779
780 if ((result = git_config_entries_append(parse_data->entries, entry)) < 0)
781 return result;
782
783 result = 0;
784
785 /* Add or append the new config option */
786 if (!git__strcmp(entry->name, "include.path"))
787 result = parse_include(parse_data, entry->value);
788 else if (!git__prefixcmp(entry->name, "includeif.") &&
789 !git__suffixcmp(entry->name, ".path"))
790 result = parse_conditional_include(parse_data, entry->name, entry->value);
791
792 return result;
793 }
794
795 static int config_file_read_buffer(
796 git_config_entries *entries,
797 const git_repository *repo,
798 config_file *file,
799 git_config_level_t level,
800 int depth,
801 const char *buf,
802 size_t buflen)
803 {
804 config_file_parse_data parse_data;
805 git_config_parser reader;
806 int error;
807
808 if (depth >= MAX_INCLUDE_DEPTH) {
809 git_error_set(GIT_ERROR_CONFIG, "maximum config include depth reached");
810 return -1;
811 }
812
813 /* Initialize the reading position */
814 reader.path = file->path;
815 git_parse_ctx_init(&reader.ctx, buf, buflen);
816
817 /* If the file is empty, there's nothing for us to do */
818 if (!reader.ctx.content || *reader.ctx.content == '\0') {
819 error = 0;
820 goto out;
821 }
822
823 parse_data.repo = repo;
824 parse_data.file = file;
825 parse_data.entries = entries;
826 parse_data.level = level;
827 parse_data.depth = depth;
828
829 error = git_config_parse(&reader, NULL, read_on_variable, NULL, NULL, &parse_data);
830
831 out:
832 return error;
833 }
834
835 static int config_file_read(
836 git_config_entries *entries,
837 const git_repository *repo,
838 config_file *file,
839 git_config_level_t level,
840 int depth)
841 {
842 git_buf contents = GIT_BUF_INIT;
843 struct stat st;
844 int error;
845
846 if (p_stat(file->path, &st) < 0) {
847 error = git_path_set_error(errno, file->path, "stat");
848 goto out;
849 }
850
851 if ((error = git_futils_readbuffer(&contents, file->path)) < 0)
852 goto out;
853
854 git_futils_filestamp_set_from_stat(&file->stamp, &st);
855 if ((error = git_hash_buf(&file->checksum, contents.ptr, contents.size)) < 0)
856 goto out;
857
858 if ((error = config_file_read_buffer(entries, repo, file, level, depth,
859 contents.ptr, contents.size)) < 0)
860 goto out;
861
862 out:
863 git_buf_dispose(&contents);
864 return error;
865 }
866
867 static int write_section(git_buf *fbuf, const char *key)
868 {
869 int result;
870 const char *dot;
871 git_buf buf = GIT_BUF_INIT;
872
873 /* All of this just for [section "subsection"] */
874 dot = strchr(key, '.');
875 git_buf_putc(&buf, '[');
876 if (dot == NULL) {
877 git_buf_puts(&buf, key);
878 } else {
879 char *escaped;
880 git_buf_put(&buf, key, dot - key);
881 escaped = escape_value(dot + 1);
882 GIT_ERROR_CHECK_ALLOC(escaped);
883 git_buf_printf(&buf, " \"%s\"", escaped);
884 git__free(escaped);
885 }
886 git_buf_puts(&buf, "]\n");
887
888 if (git_buf_oom(&buf))
889 return -1;
890
891 result = git_buf_put(fbuf, git_buf_cstr(&buf), buf.size);
892 git_buf_dispose(&buf);
893
894 return result;
895 }
896
897 static const char *quotes_for_value(const char *value)
898 {
899 const char *ptr;
900
901 if (value[0] == ' ' || value[0] == '\0')
902 return "\"";
903
904 for (ptr = value; *ptr; ++ptr) {
905 if (*ptr == ';' || *ptr == '#')
906 return "\"";
907 }
908
909 if (ptr[-1] == ' ')
910 return "\"";
911
912 return "";
913 }
914
915 struct write_data {
916 git_buf *buf;
917 git_buf buffered_comment;
918 unsigned int in_section : 1,
919 preg_replaced : 1;
920 const char *orig_section;
921 const char *section;
922 const char *orig_name;
923 const char *name;
924 const git_regexp *preg;
925 const char *value;
926 };
927
928 static int write_line_to(git_buf *buf, const char *line, size_t line_len)
929 {
930 int result = git_buf_put(buf, line, line_len);
931
932 if (!result && line_len && line[line_len-1] != '\n')
933 result = git_buf_printf(buf, "\n");
934
935 return result;
936 }
937
938 static int write_line(struct write_data *write_data, const char *line, size_t line_len)
939 {
940 return write_line_to(write_data->buf, line, line_len);
941 }
942
943 static int write_value(struct write_data *write_data)
944 {
945 const char *q;
946 int result;
947
948 q = quotes_for_value(write_data->value);
949 result = git_buf_printf(write_data->buf,
950 "\t%s = %s%s%s\n", write_data->orig_name, q, write_data->value, q);
951
952 /* If we are updating a single name/value, we're done. Setting `value`
953 * to `NULL` will prevent us from trying to write it again later (in
954 * `write_on_section`) if we see the same section repeated.
955 */
956 if (!write_data->preg)
957 write_data->value = NULL;
958
959 return result;
960 }
961
962 static int write_on_section(
963 git_config_parser *reader,
964 const char *current_section,
965 const char *line,
966 size_t line_len,
967 void *data)
968 {
969 struct write_data *write_data = (struct write_data *)data;
970 int result = 0;
971
972 GIT_UNUSED(reader);
973
974 /* If we were previously in the correct section (but aren't anymore)
975 * and haven't written our value (for a simple name/value set, not
976 * a multivar), then append it to the end of the section before writing
977 * the new one.
978 */
979 if (write_data->in_section && !write_data->preg && write_data->value)
980 result = write_value(write_data);
981
982 write_data->in_section = strcmp(current_section, write_data->section) == 0;
983
984 /*
985 * If there were comments just before this section, dump them as well.
986 */
987 if (!result) {
988 result = git_buf_put(write_data->buf, write_data->buffered_comment.ptr, write_data->buffered_comment.size);
989 git_buf_clear(&write_data->buffered_comment);
990 }
991
992 if (!result)
993 result = write_line(write_data, line, line_len);
994
995 return result;
996 }
997
998 static int write_on_variable(
999 git_config_parser *reader,
1000 const char *current_section,
1001 const char *var_name,
1002 const char *var_value,
1003 const char *line,
1004 size_t line_len,
1005 void *data)
1006 {
1007 struct write_data *write_data = (struct write_data *)data;
1008 bool has_matched = false;
1009 int error;
1010
1011 GIT_UNUSED(reader);
1012 GIT_UNUSED(current_section);
1013
1014 /*
1015 * If there were comments just before this variable, let's dump them as well.
1016 */
1017 if ((error = git_buf_put(write_data->buf, write_data->buffered_comment.ptr, write_data->buffered_comment.size)) < 0)
1018 return error;
1019
1020 git_buf_clear(&write_data->buffered_comment);
1021
1022 /* See if we are to update this name/value pair; first examine name */
1023 if (write_data->in_section &&
1024 strcasecmp(write_data->name, var_name) == 0)
1025 has_matched = true;
1026
1027 /* If we have a regex to match the value, see if it matches */
1028 if (has_matched && write_data->preg != NULL)
1029 has_matched = (git_regexp_match(write_data->preg, var_value) == 0);
1030
1031 /* If this isn't the name/value we're looking for, simply dump the
1032 * existing data back out and continue on.
1033 */
1034 if (!has_matched)
1035 return write_line(write_data, line, line_len);
1036
1037 write_data->preg_replaced = 1;
1038
1039 /* If value is NULL, we are deleting this value; write nothing. */
1040 if (!write_data->value)
1041 return 0;
1042
1043 return write_value(write_data);
1044 }
1045
1046 static int write_on_comment(git_config_parser *reader, const char *line, size_t line_len, void *data)
1047 {
1048 struct write_data *write_data;
1049
1050 GIT_UNUSED(reader);
1051
1052 write_data = (struct write_data *)data;
1053 return write_line_to(&write_data->buffered_comment, line, line_len);
1054 }
1055
1056 static int write_on_eof(
1057 git_config_parser *reader, const char *current_section, void *data)
1058 {
1059 struct write_data *write_data = (struct write_data *)data;
1060 int result = 0;
1061
1062 GIT_UNUSED(reader);
1063
1064 /*
1065 * If we've buffered comments when reaching EOF, make sure to dump them.
1066 */
1067 if ((result = git_buf_put(write_data->buf, write_data->buffered_comment.ptr, write_data->buffered_comment.size)) < 0)
1068 return result;
1069
1070 /* If we are at the EOF and have not written our value (again, for a
1071 * simple name/value set, not a multivar) then we have never seen the
1072 * section in question and should create a new section and write the
1073 * value.
1074 */
1075 if ((!write_data->preg || !write_data->preg_replaced) && write_data->value) {
1076 /* write the section header unless we're already in it */
1077 if (!current_section || strcmp(current_section, write_data->section))
1078 result = write_section(write_data->buf, write_data->orig_section);
1079
1080 if (!result)
1081 result = write_value(write_data);
1082 }
1083
1084 return result;
1085 }
1086
1087 /*
1088 * This is pretty much the parsing, except we write out anything we don't have
1089 */
1090 static int config_file_write(config_file_backend *cfg, const char *orig_key, const char *key, const git_regexp *preg, const char* value)
1091
1092 {
1093 char *orig_section = NULL, *section = NULL, *orig_name, *name, *ldot;
1094 git_buf buf = GIT_BUF_INIT, contents = GIT_BUF_INIT;
1095 git_config_parser parser = GIT_CONFIG_PARSER_INIT;
1096 git_filebuf file = GIT_FILEBUF_INIT;
1097 struct write_data write_data;
1098 int error;
1099
1100 memset(&write_data, 0, sizeof(write_data));
1101
1102 if (cfg->locked) {
1103 error = git_buf_puts(&contents, git_buf_cstr(&cfg->locked_content) == NULL ? "" : git_buf_cstr(&cfg->locked_content));
1104 } else {
1105 if ((error = git_filebuf_open(&file, cfg->file.path, GIT_FILEBUF_HASH_CONTENTS,
1106 GIT_CONFIG_FILE_MODE)) < 0)
1107 goto done;
1108
1109 /* We need to read in our own config file */
1110 error = git_futils_readbuffer(&contents, cfg->file.path);
1111 }
1112 if (error < 0 && error != GIT_ENOTFOUND)
1113 goto done;
1114
1115 if ((git_config_parser_init(&parser, cfg->file.path, contents.ptr, contents.size)) < 0)
1116 goto done;
1117
1118 ldot = strrchr(key, '.');
1119 name = ldot + 1;
1120 section = git__strndup(key, ldot - key);
1121 GIT_ERROR_CHECK_ALLOC(section);
1122
1123 ldot = strrchr(orig_key, '.');
1124 orig_name = ldot + 1;
1125 orig_section = git__strndup(orig_key, ldot - orig_key);
1126 GIT_ERROR_CHECK_ALLOC(orig_section);
1127
1128 write_data.buf = &buf;
1129 write_data.orig_section = orig_section;
1130 write_data.section = section;
1131 write_data.orig_name = orig_name;
1132 write_data.name = name;
1133 write_data.preg = preg;
1134 write_data.value = value;
1135
1136 if ((error = git_config_parse(&parser, write_on_section, write_on_variable,
1137 write_on_comment, write_on_eof, &write_data)) < 0)
1138 goto done;
1139
1140 if (cfg->locked) {
1141 size_t len = buf.asize;
1142 /* Update our copy with the modified contents */
1143 git_buf_dispose(&cfg->locked_content);
1144 git_buf_attach(&cfg->locked_content, git_buf_detach(&buf), len);
1145 } else {
1146 git_filebuf_write(&file, git_buf_cstr(&buf), git_buf_len(&buf));
1147
1148 if ((error = git_filebuf_commit(&file)) < 0)
1149 goto done;
1150
1151 if ((error = config_file_refresh_from_buffer(&cfg->parent, buf.ptr, buf.size)) < 0)
1152 goto done;
1153 }
1154
1155 done:
1156 git__free(section);
1157 git__free(orig_section);
1158 git_buf_dispose(&write_data.buffered_comment);
1159 git_buf_dispose(&buf);
1160 git_buf_dispose(&contents);
1161 git_filebuf_cleanup(&file);
1162 git_config_parser_dispose(&parser);
1163
1164 return error;
1165 }