]> git.proxmox.com Git - libgit2.git/blame - src/config_file.c
Include stacktrace summary in memory leak output.
[libgit2.git] / src / config_file.c
CommitLineData
c0335005 1/*
359fc2d2 2 * Copyright (C) the libgit2 contributors. All rights reserved.
c0335005 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.
c0335005
CMN
6 */
7
8#include "common.h"
9#include "config.h"
8bb198e6 10#include "filebuf.h"
83634d38 11#include "sysdir.h"
9ac581bf 12#include "buffer.h"
7bf87ab6 13#include "buf_text.h"
b0b527e0 14#include "git2/config.h"
83041c71 15#include "git2/sys/config.h"
c0335005 16#include "git2/types.h"
c2b67043 17#include "strmap.h"
19be0692 18#include "array.h"
8bb198e6 19
c0335005 20#include <ctype.h>
5e0dc4af
CMN
21#include <sys/types.h>
22#include <regex.h>
c0335005 23
c8e02b87 24GIT__USE_STRMAP
01fed0a8 25
128d3731
VM
26typedef struct cvar_t {
27 struct cvar_t *next;
a1abe66a 28 git_config_entry *entry;
8c1f4ab4 29 bool included; /* whether this is part of [include] */
128d3731 30} cvar_t;
c0335005 31
a603c191 32typedef struct git_config_file_iter {
eba73992 33 git_config_iterator parent;
a603c191 34 git_strmap_iter iter;
eba73992 35 cvar_t* next_var;
a603c191
NG
36} git_config_file_iter;
37
d8d25acb
CMN
38/* Max depth for [include] directives */
39#define MAX_INCLUDE_DEPTH 10
a603c191 40
c0335005
CMN
41#define CVAR_LIST_HEAD(list) ((list)->head)
42
43#define CVAR_LIST_TAIL(list) ((list)->tail)
44
45#define CVAR_LIST_NEXT(var) ((var)->next)
46
47#define CVAR_LIST_EMPTY(list) ((list)->head == NULL)
48
49#define CVAR_LIST_APPEND(list, var) do {\
50 if (CVAR_LIST_EMPTY(list)) {\
51 CVAR_LIST_HEAD(list) = CVAR_LIST_TAIL(list) = var;\
52 } else {\
53 CVAR_LIST_NEXT(CVAR_LIST_TAIL(list)) = var;\
54 CVAR_LIST_TAIL(list) = var;\
55 }\
56} while(0)
57
58#define CVAR_LIST_REMOVE_HEAD(list) do {\
59 CVAR_LIST_HEAD(list) = CVAR_LIST_NEXT(CVAR_LIST_HEAD(list));\
60} while(0)
61
62#define CVAR_LIST_REMOVE_AFTER(var) do {\
63 CVAR_LIST_NEXT(var) = CVAR_LIST_NEXT(CVAR_LIST_NEXT(var));\
64} while(0)
65
66#define CVAR_LIST_FOREACH(list, iter)\
67 for ((iter) = CVAR_LIST_HEAD(list);\
68 (iter) != NULL;\
69 (iter) = CVAR_LIST_NEXT(iter))
70
71/*
72 * Inspired by the FreeBSD functions
73 */
74#define CVAR_LIST_FOREACH_SAFE(start, iter, tmp)\
75 for ((iter) = CVAR_LIST_HEAD(vars);\
76 (iter) && (((tmp) = CVAR_LIST_NEXT(iter) || 1));\
77 (iter) = (tmp))
78
d209cc47 79struct reader {
d8d25acb
CMN
80 time_t file_mtime;
81 size_t file_size;
d209cc47
CMN
82 char *file_path;
83 git_buf buffer;
84 char *read_ptr;
85 int line_number;
86 int eof;
87};
88
c0335005 89typedef struct {
4b99b8f5 90 git_atomic refcount;
c2b67043 91 git_strmap *values;
4b99b8f5
CMN
92} refcounted_strmap;
93
94typedef struct {
95 git_config_backend parent;
96 /* mutex to coordinate accessing the values */
97 git_mutex values_mutex;
98 refcounted_strmap *values;
55ebd7d3
CMN
99} diskfile_header;
100
101typedef struct {
102 diskfile_header header;
103
104 git_config_level_t level;
c0335005 105
19be0692 106 git_array_t(struct reader) readers;
c0335005 107
744cc03e 108 char *file_path;
b0b527e0 109} diskfile_backend;
c0335005 110
55ebd7d3
CMN
111typedef struct {
112 diskfile_header header;
113
114 diskfile_backend *snapshot_from;
115} diskfile_readonly_backend;
116
bf99390e 117static int config_read(git_strmap *values, diskfile_backend *cfg_file, struct reader *reader, git_config_level_t level, int depth);
3005855f 118static int config_write(diskfile_backend *cfg, const char *key, const regex_t *preg, const char *value);
49938cad 119static char *escape_value(const char *ptr);
c0335005 120
55ebd7d3 121int git_config_file__snapshot(git_config_backend **out, diskfile_backend *in);
0500a1ef 122static int config_snapshot(git_config_backend **out, git_config_backend *in);
55ebd7d3 123
d209cc47 124static void set_parse_error(struct reader *reader, int col, const char *error_str)
dda708e7
VM
125{
126 giterr_set(GITERR_CONFIG, "Failed to parse config file: %s (in %s:%d, column %d)",
d209cc47 127 error_str, reader->file_path, reader->line_number, col);
dda708e7
VM
128}
129
55ebd7d3
CMN
130static int config_error_readonly(void)
131{
132 giterr_set(GITERR_CONFIG, "this backend is read-only");
133 return -1;
134}
135
128d3731 136static void cvar_free(cvar_t *var)
c0335005
CMN
137{
138 if (var == NULL)
139 return;
140
a1abe66a 141 git__free((char*)var->entry->name);
142 git__free((char *)var->entry->value);
143 git__free(var->entry);
3286c408 144 git__free(var);
c0335005
CMN
145}
146
1e7799e8
RB
147int git_config_file_normalize_section(char *start, char *end)
148{
149 char *scan;
150
151 if (start == end)
152 return GIT_EINVALIDSPEC;
153
154 /* Validate and downcase range */
155 for (scan = start; *scan; ++scan) {
156 if (end && scan >= end)
157 break;
158 if (isalnum(*scan))
75a4636f 159 *scan = (char)git__tolower(*scan);
1e7799e8
RB
160 else if (*scan != '-' || scan == start)
161 return GIT_EINVALIDSPEC;
162 }
163
164 if (scan == start)
165 return GIT_EINVALIDSPEC;
166
167 return 0;
168}
169
55ebd7d3
CMN
170/* Add or append the new config option */
171static int append_entry(git_strmap *values, cvar_t *var)
172{
173 git_strmap_iter pos;
174 cvar_t *existing;
175 int error = 0;
176
177 pos = git_strmap_lookup_index(values, var->entry->name);
178 if (!git_strmap_valid_index(values, pos)) {
179 git_strmap_insert(values, var->entry->name, var, error);
180 } else {
181 existing = git_strmap_value_at(values, pos);
182 while (existing->next != NULL) {
183 existing = existing->next;
184 }
185 existing->next = var;
186 }
187
188 if (error > 0)
189 error = 0;
190
191 return error;
192}
193
c2b67043 194static void free_vars(git_strmap *values)
3b3577c7 195{
fefd4551 196 cvar_t *var = NULL;
3b3577c7 197
fefd4551
CMN
198 if (values == NULL)
199 return;
3b3577c7 200
c2b67043 201 git_strmap_foreach_value(values, var,
01fed0a8
RB
202 while (var != NULL) {
203 cvar_t *next = CVAR_LIST_NEXT(var);
204 cvar_free(var);
205 var = next;
206 });
3b3577c7 207
c2b67043 208 git_strmap_free(values);
3b3577c7
CMN
209}
210
4b99b8f5
CMN
211static void refcounted_strmap_free(refcounted_strmap *map)
212{
213 if (!map)
214 return;
215
216 if (git_atomic_dec(&map->refcount) != 0)
217 return;
218
219 free_vars(map->values);
220 git__free(map);
221}
222
223/**
224 * Take the current values map from the backend and increase its
225 * refcount. This is its own function to make sure we use the mutex to
226 * avoid the map pointer from changing under us.
227 */
228static refcounted_strmap *refcounted_strmap_take(diskfile_header *h)
229{
230 refcounted_strmap *map;
231
232 git_mutex_lock(&h->values_mutex);
233
234 map = h->values;
235 git_atomic_inc(&map->refcount);
236
237 git_mutex_unlock(&h->values_mutex);
238
239 return map;
240}
241
242static int refcounted_strmap_alloc(refcounted_strmap **out)
243{
244 refcounted_strmap *map;
245 int error;
246
247 map = git__calloc(1, sizeof(refcounted_strmap));
b1914c36 248 GITERR_CHECK_ALLOC(map);
4b99b8f5
CMN
249
250 git_atomic_set(&map->refcount, 1);
b1914c36
RB
251
252 if ((error = git_strmap_alloc(&map->values)) < 0)
4b99b8f5 253 git__free(map);
b1914c36
RB
254 else
255 *out = map;
4b99b8f5 256
4b99b8f5
CMN
257 return error;
258}
259
16adc9fa 260static int config_open(git_config_backend *cfg, git_config_level_t level)
c0335005 261{
dda708e7 262 int res;
19be0692 263 struct reader *reader;
b0b527e0 264 diskfile_backend *b = (diskfile_backend *)cfg;
c0335005 265
744cc03e
RB
266 b->level = level;
267
4b99b8f5 268 if ((res = refcounted_strmap_alloc(&b->header.values)) < 0)
40ed4990 269 return res;
fefd4551 270
19be0692
CMN
271 git_array_init(b->readers);
272 reader = git_array_alloc(b->readers);
40ed4990 273 if (!reader) {
4b99b8f5 274 refcounted_strmap_free(b->header.values);
40ed4990
RB
275 return -1;
276 }
19be0692
CMN
277 memset(reader, 0, sizeof(struct reader));
278
279 reader->file_path = git__strdup(b->file_path);
280 GITERR_CHECK_ALLOC(reader->file_path);
281
282 git_buf_init(&reader->buffer, 0);
744cc03e 283 res = git_futils_readbuffer_updated(
19be0692 284 &reader->buffer, b->file_path, &reader->file_mtime, &reader->file_size, NULL);
9462c471 285
4e90a0a4 286 /* It's fine if the file doesn't exist */
dda708e7
VM
287 if (res == GIT_ENOTFOUND)
288 return 0;
289
bf99390e 290 if (res < 0 || (res = config_read(b->header.values->values, b, reader, level, 0)) < 0) {
4b99b8f5 291 refcounted_strmap_free(b->header.values);
55ebd7d3 292 b->header.values = NULL;
dda708e7 293 }
c0335005 294
53ea0513 295 reader = git_array_get(b->readers, 0);
19be0692 296 git_buf_free(&reader->buffer);
40ed4990 297
744cc03e
RB
298 return res;
299}
300
bd95f836
CMN
301/* The meat of the refresh, as we want to use it in different places */
302static int config__refresh(git_config_backend *cfg)
303{
4b99b8f5 304 refcounted_strmap *values = NULL, *tmp;
bd95f836
CMN
305 diskfile_backend *b = (diskfile_backend *)cfg;
306 struct reader *reader = NULL;
307 int error = 0;
308
4b99b8f5 309 if ((error = refcounted_strmap_alloc(&values)) < 0)
bd95f836
CMN
310 goto out;
311
312 reader = git_array_get(b->readers, git_array_size(b->readers) - 1);
a37aa82e 313 GITERR_CHECK_ALLOC(reader);
bd95f836 314
bf99390e 315 if ((error = config_read(values->values, b, reader, b->level, 0)) < 0)
bd95f836
CMN
316 goto out;
317
4b99b8f5
CMN
318 git_mutex_lock(&b->header.values_mutex);
319
320 tmp = b->header.values;
321 b->header.values = values;
322 values = tmp;
323
324 git_mutex_unlock(&b->header.values_mutex);
bd95f836
CMN
325
326out:
4b99b8f5 327 refcounted_strmap_free(values);
a37aa82e
RB
328 if (reader)
329 git_buf_free(&reader->buffer);
bd95f836
CMN
330 return error;
331}
332
54b2a37a 333static int config_refresh(git_config_backend *cfg)
744cc03e 334{
c047317e 335 int error = 0, updated = 0, any_updated = 0;
744cc03e 336 diskfile_backend *b = (diskfile_backend *)cfg;
6014b7b5 337 struct reader *reader = NULL;
a9fb7989 338 uint32_t i;
744cc03e 339
a9fb7989
CMN
340 for (i = 0; i < git_array_size(b->readers); i++) {
341 reader = git_array_get(b->readers, i);
c047317e 342 error = git_futils_readbuffer_updated(
40ed4990
RB
343 &reader->buffer, reader->file_path,
344 &reader->file_mtime, &reader->file_size, &updated);
a9fb7989 345
a37aa82e
RB
346 if (error < 0 && error != GIT_ENOTFOUND)
347 return error;
a9fb7989
CMN
348
349 if (updated)
350 any_updated = 1;
351 }
352
353 if (!any_updated)
c047317e 354 return (error == GIT_ENOTFOUND) ? 0 : error;
744cc03e 355
bd95f836 356 return config__refresh(cfg);
c0335005
CMN
357}
358
54b2a37a 359static void backend_free(git_config_backend *_backend)
c0335005 360{
b0b527e0 361 diskfile_backend *backend = (diskfile_backend *)_backend;
19be0692 362 uint32_t i;
c0335005
CMN
363
364 if (backend == NULL)
365 return;
366
19be0692
CMN
367 for (i = 0; i < git_array_size(backend->readers); i++) {
368 struct reader *r = git_array_get(backend->readers, i);
369 git__free(r->file_path);
370 }
371 git_array_clear(backend->readers);
372
3286c408 373 git__free(backend->file_path);
4b99b8f5 374 refcounted_strmap_free(backend->header.values);
4af0ef96 375 git_mutex_free(&backend->header.values_mutex);
3286c408 376 git__free(backend);
c0335005
CMN
377}
378
a603c191 379static void config_iterator_free(
eba73992 380 git_config_iterator* iter)
c0335005 381{
0500a1ef 382 iter->backend->free(iter->backend);
a603c191
NG
383 git__free(iter);
384}
c0335005 385
4d588d97 386static int config_iterator_next(
99dfb538 387 git_config_entry **entry,
eba73992 388 git_config_iterator *iter)
a603c191 389{
82ae6fcd 390 git_config_file_iter *it = (git_config_file_iter *) iter;
55ebd7d3 391 diskfile_header *h = (diskfile_header *) it->parent.backend;
4b99b8f5 392 git_strmap *values = h->values->values;
82ae6fcd 393 int err = 0;
a603c191 394 cvar_t * var;
c0335005 395
eba73992 396 if (it->next_var == NULL) {
55ebd7d3 397 err = git_strmap_next((void**) &var, &(it->iter), values);
a603c191 398 } else {
eba73992 399 var = it->next_var;
b3ff1dab 400 }
c0335005 401
a603c191 402 if (err < 0) {
eba73992 403 it->next_var = NULL;
5880962d 404 return err;
a603c191 405 }
0c8858de 406
99dfb538 407 *entry = var->entry;
eba73992 408 it->next_var = CVAR_LIST_NEXT(var);
b3ff1dab 409
eba73992
CMN
410 return 0;
411}
c0335005 412
eba73992
CMN
413static int config_iterator_new(
414 git_config_iterator **iter,
415 struct git_config_backend* backend)
416{
0500a1ef
CMN
417 diskfile_header *h;
418 git_config_file_iter *it;
419 git_config_backend *snapshot;
420 diskfile_backend *b = (diskfile_backend *) backend;
421 int error;
b3ff1dab 422
0500a1ef
CMN
423 if ((error = config_snapshot(&snapshot, backend)) < 0)
424 return error;
425
426 if ((error = snapshot->open(snapshot, b->level)) < 0)
427 return error;
428
429 it = git__calloc(1, sizeof(git_config_file_iter));
eba73992
CMN
430 GITERR_CHECK_ALLOC(it);
431
0500a1ef
CMN
432 h = (diskfile_header *)snapshot;
433
55ebd7d3
CMN
434 /* strmap_begin() is currently a macro returning 0 */
435 GIT_UNUSED(h);
436
0500a1ef 437 it->parent.backend = snapshot;
55ebd7d3 438 it->iter = git_strmap_begin(h->values);
eba73992
CMN
439 it->next_var = NULL;
440
441 it->parent.next = config_iterator_next;
442 it->parent.free = config_iterator_free;
443 *iter = (git_config_iterator *) it;
a603c191
NG
444
445 return 0;
c0335005
CMN
446}
447
54b2a37a 448static int config_set(git_config_backend *cfg, const char *name, const char *value)
c0335005 449{
b0b527e0 450 diskfile_backend *b = (diskfile_backend *)cfg;
4b99b8f5
CMN
451 refcounted_strmap *map;
452 git_strmap *values;
49938cad 453 char *key, *esc_value = NULL;
01fed0a8 454 khiter_t pos;
49938cad 455 int rval, ret;
fefd4551 456
f4be8209 457 if ((rval = git_config__normalize_name(name, &key)) < 0)
1e7799e8 458 return rval;
c0335005 459
4b99b8f5
CMN
460 map = refcounted_strmap_take(&b->header);
461 values = map->values;
462
c0335005 463 /*
fefd4551
CMN
464 * Try to find it in the existing values and update it if it
465 * only has one value.
c0335005 466 */
55ebd7d3
CMN
467 pos = git_strmap_lookup_index(values, key);
468 if (git_strmap_valid_index(values, pos)) {
469 cvar_t *existing = git_strmap_value_at(values, pos);
aa13bf05 470
dda708e7
VM
471 if (existing->next != NULL) {
472 giterr_set(GITERR_CONFIG, "Multivar incompatible with simple set");
4b99b8f5
CMN
473 ret = -1;
474 goto out;
dda708e7 475 }
fefd4551 476
aa13bf05 477 /* don't update if old and new values already match */
a1abe66a 478 if ((!existing->entry->value && !value) ||
eaf37034
CMN
479 (existing->entry->value && value &&
480 !strcmp(existing->entry->value, value))) {
4b99b8f5
CMN
481 ret = 0;
482 goto out;
dda708e7 483 }
29dca088
CMN
484 }
485
eaf37034 486 /* No early returns due to sanity checks, let's write it out and refresh */
c0335005 487
dda708e7 488 if (value) {
49938cad
CMN
489 esc_value = escape_value(value);
490 GITERR_CHECK_ALLOC(esc_value);
c0335005
CMN
491 }
492
eaf37034
CMN
493 if ((ret = config_write(b, key, NULL, esc_value)) < 0)
494 goto out;
3df9cc59 495
eaf37034 496 ret = config_refresh(cfg);
c0335005 497
eaf37034 498out:
4b99b8f5 499 refcounted_strmap_free(map);
eaf37034
CMN
500 git__free(esc_value);
501 git__free(key);
502 return ret;
c0335005
CMN
503}
504
9a97f49e
CMN
505/* release the map containing the entry as an equivalent to freeing it */
506static void release_map(git_config_entry *entry)
507{
508 refcounted_strmap *map = (refcounted_strmap *) entry->payload;
509 refcounted_strmap_free(map);
510}
511
c0335005
CMN
512/*
513 * Internal function that actually gets the value in string form
514 */
9a97f49e 515static int config_get(git_config_backend *cfg, const char *key, git_config_entry **out)
c0335005 516{
55ebd7d3 517 diskfile_header *h = (diskfile_header *)cfg;
4b99b8f5 518 refcounted_strmap *map;
523032cd
CMN
519 git_strmap *values;
520 khiter_t pos;
73fc5e01 521 cvar_t *var;
9a97f49e 522 int error = 0;
523032cd 523
9a97f49e 524 if (!h->parent.readonly && ((error = config_refresh(cfg)) < 0))
523032cd
CMN
525 return error;
526
4b99b8f5
CMN
527 map = refcounted_strmap_take(h);
528 values = map->values;
529
523032cd 530 pos = git_strmap_lookup_index(values, key);
c0335005 531
dda708e7 532 /* no error message; the config system will write one */
4b99b8f5
CMN
533 if (!git_strmap_valid_index(values, pos)) {
534 refcounted_strmap_free(map);
dda708e7 535 return GIT_ENOTFOUND;
4b99b8f5 536 }
c0335005 537
55ebd7d3 538 var = git_strmap_value_at(values, pos);
73fc5e01
CMN
539 while (var->next)
540 var = var->next;
541
542 *out = var->entry;
9a97f49e
CMN
543 (*out)->free = release_map;
544 (*out)->payload = map;
545
546 return error;
c0335005
CMN
547}
548
01fed0a8 549static int config_set_multivar(
54b2a37a 550 git_config_backend *cfg, const char *name, const char *regexp, const char *value)
3005855f 551{
3005855f 552 diskfile_backend *b = (diskfile_backend *)cfg;
4b99b8f5
CMN
553 refcounted_strmap *map;
554 git_strmap *values;
3005855f
CMN
555 char *key;
556 regex_t preg;
dda708e7 557 int result;
01fed0a8 558 khiter_t pos;
3005855f 559
dda708e7 560 assert(regexp);
3005855f 561
f4be8209 562 if ((result = git_config__normalize_name(name, &key)) < 0)
1e7799e8 563 return result;
3005855f 564
4b99b8f5
CMN
565 map = refcounted_strmap_take(&b->header);
566 values = b->header.values->values;
567
55ebd7d3
CMN
568 pos = git_strmap_lookup_index(values, key);
569 if (!git_strmap_valid_index(values, pos)) {
e5a27f03
CMN
570 /* If we don't have it, behave like a normal set */
571 result = config_set(cfg, name, value);
4b99b8f5 572 refcounted_strmap_free(map);
01fed0a8 573 git__free(key);
e5a27f03 574 return result;
0a43d7cb 575 }
3005855f 576
dda708e7 577 result = regcomp(&preg, regexp, REG_EXTENDED);
129022ee 578 if (result != 0) {
dda708e7 579 giterr_set_regex(&preg, result);
4b99b8f5
CMN
580 result = -1;
581 goto out;
0a43d7cb 582 }
3005855f 583
eaf37034
CMN
584 /* If we do have it, set call config_write() and reload */
585 if ((result = config_write(b, key, &preg, value)) < 0)
586 goto out;
3005855f 587
eaf37034 588 result = config_refresh(cfg);
3005855f 589
eaf37034 590out:
4b99b8f5 591 refcounted_strmap_free(map);
2bc8fa02 592 git__free(key);
3005855f 593 regfree(&preg);
dda708e7
VM
594
595 return result;
3005855f
CMN
596}
597
54b2a37a 598static int config_delete(git_config_backend *cfg, const char *name)
80a665aa 599{
dda708e7 600 cvar_t *var;
80a665aa 601 diskfile_backend *b = (diskfile_backend *)cfg;
4b99b8f5 602 refcounted_strmap *map; git_strmap *values;
fefd4551 603 char *key;
dda708e7 604 int result;
01fed0a8 605 khiter_t pos;
fefd4551 606
f4be8209 607 if ((result = git_config__normalize_name(name, &key)) < 0)
1e7799e8 608 return result;
fefd4551 609
4b99b8f5
CMN
610 map = refcounted_strmap_take(&b->header);
611 values = b->header.values->values;
612
55ebd7d3 613 pos = git_strmap_lookup_index(values, key);
2bc8fa02 614 git__free(key);
fefd4551 615
55ebd7d3 616 if (!git_strmap_valid_index(values, pos)) {
4b99b8f5 617 refcounted_strmap_free(map);
1d4dcc4b 618 giterr_set(GITERR_CONFIG, "Could not find key '%s' to delete", name);
dda708e7 619 return GIT_ENOTFOUND;
1d4dcc4b 620 }
fefd4551 621
55ebd7d3 622 var = git_strmap_value_at(values, pos);
4b99b8f5 623 refcounted_strmap_free(map);
80a665aa 624
dda708e7
VM
625 if (var->next != NULL) {
626 giterr_set(GITERR_CONFIG, "Cannot delete multivar with a single delete");
627 return -1;
628 }
80a665aa 629
8c1f4ab4
CMN
630 if ((result = config_write(b, var->entry->name, NULL, NULL)) < 0)
631 return result;
fefd4551 632
8c1f4ab4 633 return config_refresh(cfg);
80a665aa
CMN
634}
635
3793fa9b
DRT
636static int config_delete_multivar(git_config_backend *cfg, const char *name, const char *regexp)
637{
3793fa9b 638 diskfile_backend *b = (diskfile_backend *)cfg;
4b99b8f5
CMN
639 refcounted_strmap *map;
640 git_strmap *values;
3793fa9b
DRT
641 char *key;
642 regex_t preg;
643 int result;
644 khiter_t pos;
645
646 if ((result = git_config__normalize_name(name, &key)) < 0)
647 return result;
648
4b99b8f5
CMN
649 map = refcounted_strmap_take(&b->header);
650 values = b->header.values->values;
651
55ebd7d3 652 pos = git_strmap_lookup_index(values, key);
3793fa9b 653
55ebd7d3 654 if (!git_strmap_valid_index(values, pos)) {
4b99b8f5 655 refcounted_strmap_free(map);
3793fa9b 656 git__free(key);
8c1f4ab4 657 giterr_set(GITERR_CONFIG, "Could not find key '%s' to delete", name);
3793fa9b
DRT
658 return GIT_ENOTFOUND;
659 }
660
4b99b8f5
CMN
661 refcounted_strmap_free(map);
662
3793fa9b 663 result = regcomp(&preg, regexp, REG_EXTENDED);
129022ee 664 if (result != 0) {
3793fa9b 665 giterr_set_regex(&preg, result);
8c1f4ab4
CMN
666 result = -1;
667 goto out;
3793fa9b
DRT
668 }
669
8c1f4ab4
CMN
670 if ((result = config_write(b, key, &preg, NULL)) < 0)
671 goto out;
3793fa9b 672
8c1f4ab4 673 result = config_refresh(cfg);
3793fa9b 674
8c1f4ab4 675out:
3793fa9b 676 git__free(key);
a71331eb 677 regfree(&preg);
3793fa9b
DRT
678 return result;
679}
680
55ebd7d3
CMN
681static int config_snapshot(git_config_backend **out, git_config_backend *in)
682{
683 diskfile_backend *b = (diskfile_backend *) in;
684
685 return git_config_file__snapshot(out, b);
686}
687
54b2a37a 688int git_config_file__ondisk(git_config_backend **out, const char *path)
c0335005 689{
b0b527e0 690 diskfile_backend *backend;
c0335005 691
69177621 692 backend = git__calloc(1, sizeof(diskfile_backend));
dda708e7 693 GITERR_CHECK_ALLOC(backend);
c0335005 694
55ebd7d3 695 backend->header.parent.version = GIT_CONFIG_BACKEND_VERSION;
4af0ef96 696 git_mutex_init(&backend->header.values_mutex);
c0335005
CMN
697
698 backend->file_path = git__strdup(path);
dda708e7 699 GITERR_CHECK_ALLOC(backend->file_path);
c0335005 700
55ebd7d3
CMN
701 backend->header.parent.open = config_open;
702 backend->header.parent.get = config_get;
703 backend->header.parent.set = config_set;
704 backend->header.parent.set_multivar = config_set_multivar;
705 backend->header.parent.del = config_delete;
706 backend->header.parent.del_multivar = config_delete_multivar;
707 backend->header.parent.iterator = config_iterator_new;
55ebd7d3
CMN
708 backend->header.parent.snapshot = config_snapshot;
709 backend->header.parent.free = backend_free;
710
711 *out = (git_config_backend *)backend;
712
713 return 0;
714}
715
716static int config_set_readonly(git_config_backend *cfg, const char *name, const char *value)
717{
718 GIT_UNUSED(cfg);
719 GIT_UNUSED(name);
720 GIT_UNUSED(value);
721
722 return config_error_readonly();
723}
724
725static int config_set_multivar_readonly(
726 git_config_backend *cfg, const char *name, const char *regexp, const char *value)
727{
728 GIT_UNUSED(cfg);
729 GIT_UNUSED(name);
730 GIT_UNUSED(regexp);
731 GIT_UNUSED(value);
732
733 return config_error_readonly();
734}
735
736static int config_delete_multivar_readonly(git_config_backend *cfg, const char *name, const char *regexp)
737{
738 GIT_UNUSED(cfg);
739 GIT_UNUSED(name);
740 GIT_UNUSED(regexp);
741
742 return config_error_readonly();
743}
744
745static int config_delete_readonly(git_config_backend *cfg, const char *name)
746{
747 GIT_UNUSED(cfg);
748 GIT_UNUSED(name);
749
750 return config_error_readonly();
751}
752
55ebd7d3
CMN
753static void backend_readonly_free(git_config_backend *_backend)
754{
755 diskfile_backend *backend = (diskfile_backend *)_backend;
756
757 if (backend == NULL)
758 return;
759
4b99b8f5 760 refcounted_strmap_free(backend->header.values);
4af0ef96 761 git_mutex_free(&backend->header.values_mutex);
55ebd7d3
CMN
762 git__free(backend);
763}
764
55ebd7d3
CMN
765static int config_readonly_open(git_config_backend *cfg, git_config_level_t level)
766{
767 diskfile_readonly_backend *b = (diskfile_readonly_backend *) cfg;
768 diskfile_backend *src = b->snapshot_from;
ad5adacb 769 diskfile_header *src_header = &src->header;
4b99b8f5 770 refcounted_strmap *src_map;
ad5adacb
AR
771 int error;
772
9a97f49e 773 if (!src_header->parent.readonly && (error = config_refresh(&src_header->parent)) < 0)
ad5adacb 774 return error;
55ebd7d3
CMN
775
776 /* We're just copying data, don't care about the level */
777 GIT_UNUSED(level);
778
ad5adacb 779 src_map = refcounted_strmap_take(src_header);
2280b388 780 b->header.values = src_map;
55ebd7d3 781
2280b388 782 return 0;
55ebd7d3
CMN
783}
784
785int git_config_file__snapshot(git_config_backend **out, diskfile_backend *in)
786{
787 diskfile_readonly_backend *backend;
788
789 backend = git__calloc(1, sizeof(diskfile_readonly_backend));
790 GITERR_CHECK_ALLOC(backend);
791
792 backend->header.parent.version = GIT_CONFIG_BACKEND_VERSION;
4af0ef96 793 git_mutex_init(&backend->header.values_mutex);
55ebd7d3
CMN
794
795 backend->snapshot_from = in;
796
9a97f49e 797 backend->header.parent.readonly = 1;
55ebd7d3
CMN
798 backend->header.parent.version = GIT_CONFIG_BACKEND_VERSION;
799 backend->header.parent.open = config_readonly_open;
800 backend->header.parent.get = config_get;
801 backend->header.parent.set = config_set_readonly;
802 backend->header.parent.set_multivar = config_set_multivar_readonly;
803 backend->header.parent.del = config_delete_readonly;
804 backend->header.parent.del_multivar = config_delete_multivar_readonly;
805 backend->header.parent.iterator = config_iterator_new;
55ebd7d3 806 backend->header.parent.free = backend_readonly_free;
c0335005 807
54b2a37a 808 *out = (git_config_backend *)backend;
c0335005 809
dda708e7 810 return 0;
c0335005
CMN
811}
812
d209cc47 813static int reader_getchar_raw(struct reader *reader)
c0335005
CMN
814{
815 int c;
816
d209cc47 817 c = *reader->read_ptr++;
c0335005
CMN
818
819 /*
820 Win 32 line breaks: if we find a \r\n sequence,
821 return only the \n as a newline
822 */
d209cc47
CMN
823 if (c == '\r' && *reader->read_ptr == '\n') {
824 reader->read_ptr++;
c0335005
CMN
825 c = '\n';
826 }
827
828 if (c == '\n')
d209cc47 829 reader->line_number++;
c0335005
CMN
830
831 if (c == 0) {
d209cc47 832 reader->eof = 1;
2a950c94 833 c = '\0';
c0335005
CMN
834 }
835
836 return c;
837}
838
839#define SKIP_WHITESPACE (1 << 1)
840#define SKIP_COMMENTS (1 << 2)
841
d209cc47 842static int reader_getchar(struct reader *reader, int flags)
c0335005
CMN
843{
844 const int skip_whitespace = (flags & SKIP_WHITESPACE);
845 const int skip_comments = (flags & SKIP_COMMENTS);
846 int c;
847
d209cc47 848 assert(reader->read_ptr);
c0335005 849
d209cc47
CMN
850 do {
851 c = reader_getchar_raw(reader);
2a950c94 852 } while (c != '\n' && c != '\0' && skip_whitespace && git__isspace(c));
c0335005
CMN
853
854 if (skip_comments && (c == '#' || c == ';')) {
d209cc47
CMN
855 do {
856 c = reader_getchar_raw(reader);
2a950c94 857 } while (c != '\n' && c != '\0');
c0335005
CMN
858 }
859
860 return c;
861}
862
863/*
864 * Read the next char, but don't move the reading pointer.
865 */
d209cc47 866static int reader_peek(struct reader *reader, int flags)
c0335005
CMN
867{
868 void *old_read_ptr;
869 int old_lineno, old_eof;
870 int ret;
871
d209cc47 872 assert(reader->read_ptr);
c0335005 873
d209cc47
CMN
874 old_read_ptr = reader->read_ptr;
875 old_lineno = reader->line_number;
876 old_eof = reader->eof;
c0335005 877
d209cc47 878 ret = reader_getchar(reader, flags);
c0335005 879
d209cc47
CMN
880 reader->read_ptr = old_read_ptr;
881 reader->line_number = old_lineno;
882 reader->eof = old_eof;
c0335005
CMN
883
884 return ret;
885}
886
c0335005
CMN
887/*
888 * Read and consume a line, returning it in newly-allocated memory.
889 */
d209cc47 890static char *reader_readline(struct reader *reader, bool skip_whitespace)
c0335005
CMN
891{
892 char *line = NULL;
893 char *line_src, *line_end;
f1453c59 894 size_t line_len, alloc_len;
c0335005 895
d209cc47 896 line_src = reader->read_ptr;
f2abee47 897
2c1075d6
CMN
898 if (skip_whitespace) {
899 /* Skip empty empty lines */
0f49200c 900 while (git__isspace(*line_src))
2c1075d6
CMN
901 ++line_src;
902 }
f2abee47 903
45e93ef3 904 line_end = strchr(line_src, '\n');
c0335005 905
45e93ef3 906 /* no newline at EOF */
c0335005
CMN
907 if (line_end == NULL)
908 line_end = strchr(line_src, 0);
c0335005 909
f2abee47 910 line_len = line_end - line_src;
c0335005 911
f1453c59
ET
912 if (GIT_ADD_SIZET_OVERFLOW(&alloc_len, line_len, 1) ||
913 (line = git__malloc(alloc_len)) == NULL) {
c0335005 914 return NULL;
392702ee 915 }
c0335005 916
f2abee47 917 memcpy(line, line_src, line_len);
c0335005 918
79a34396 919 do line[line_len] = '\0';
0f49200c 920 while (line_len-- > 0 && git__isspace(line[line_len]));
c0335005
CMN
921
922 if (*line_end == '\n')
923 line_end++;
924
925 if (*line_end == '\0')
d209cc47 926 reader->eof = 1;
c0335005 927
d209cc47
CMN
928 reader->line_number++;
929 reader->read_ptr = line_end;
c0335005
CMN
930
931 return line;
932}
933
934/*
935 * Consume a line, without storing it anywhere
936 */
d209cc47 937static void reader_consume_line(struct reader *reader)
c0335005
CMN
938{
939 char *line_start, *line_end;
940
d209cc47 941 line_start = reader->read_ptr;
c0335005
CMN
942 line_end = strchr(line_start, '\n');
943 /* No newline at EOF */
944 if(line_end == NULL){
945 line_end = strchr(line_start, '\0');
946 }
947
948 if (*line_end == '\n')
949 line_end++;
950
951 if (*line_end == '\0')
d209cc47 952 reader->eof = 1;
c0335005 953
d209cc47
CMN
954 reader->line_number++;
955 reader->read_ptr = line_end;
c0335005
CMN
956}
957
765fdf4a 958GIT_INLINE(int) config_keychar(int c)
c0335005
CMN
959{
960 return isalnum(c) || c == '-';
961}
962
d209cc47 963static int parse_section_header_ext(struct reader *reader, const char *line, const char *base_name, char **section_name)
c0335005 964{
9ac581bf
CMN
965 int c, rpos;
966 char *first_quote, *last_quote;
967 git_buf buf = GIT_BUF_INIT;
f1453c59 968 size_t quoted_len, alloc_len, base_name_len = strlen(base_name);
392702ee 969
c0335005
CMN
970 /*
971 * base_name is what came before the space. We should be at the
972 * first quotation mark, except for now, line isn't being kept in
973 * sync so we only really use it to calculate the length.
974 */
975
976 first_quote = strchr(line, '"');
977 last_quote = strrchr(line, '"');
392702ee 978 quoted_len = last_quote - first_quote;
c0335005 979
392702ee 980 if (quoted_len == 0) {
d209cc47 981 set_parse_error(reader, 0, "Missing closing quotation mark in section header");
dda708e7
VM
982 return -1;
983 }
c0335005 984
f1453c59
ET
985 GITERR_CHECK_ALLOC_ADD(&alloc_len, base_name_len, quoted_len);
986 GITERR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 2);
392702ee 987
f1453c59 988 git_buf_grow(&buf, alloc_len);
9ac581bf 989 git_buf_printf(&buf, "%s.", base_name);
c0335005 990
c0335005 991 rpos = 0;
c0335005
CMN
992
993 line = first_quote;
566dd8ce 994 c = line[++rpos];
c0335005
CMN
995
996 /*
997 * At the end of each iteration, whatever is stored in c will be
998 * added to the string. In case of error, jump to out
999 */
1000 do {
5892277c 1001
c0335005 1002 switch (c) {
2d9f5b9f 1003 case 0:
d209cc47 1004 set_parse_error(reader, 0, "Unexpected end-of-line in section header");
2d9f5b9f
ET
1005 git_buf_free(&buf);
1006 return -1;
1007
c0335005 1008 case '"':
566dd8ce 1009 goto end_parse;
dda708e7 1010
c0335005 1011 case '\\':
566dd8ce 1012 c = line[++rpos];
2d9f5b9f 1013
566dd8ce
L
1014 if (c == 0) {
1015 set_parse_error(reader, rpos, "Unexpected end-of-line in section header");
dda708e7
VM
1016 git_buf_free(&buf);
1017 return -1;
c0335005 1018 }
dda708e7 1019
c0335005
CMN
1020 default:
1021 break;
1022 }
1023
66566516 1024 git_buf_putc(&buf, (char)c);
566dd8ce
L
1025 c = line[++rpos];
1026 } while (line + rpos < last_quote);
1027
1028end_parse:
1029 if (line[rpos] != '"' || line[rpos + 1] != ']') {
1030 set_parse_error(reader, rpos, "Unexpected text after closing quotes");
1031 git_buf_free(&buf);
1032 return -1;
1033 }
c0335005 1034
dda708e7
VM
1035 *section_name = git_buf_detach(&buf);
1036 return 0;
c0335005
CMN
1037}
1038
d209cc47 1039static int parse_section_header(struct reader *reader, char **section_out)
c0335005
CMN
1040{
1041 char *name, *name_end;
1042 int name_length, c, pos;
dda708e7 1043 int result;
c0335005 1044 char *line;
392702ee 1045 size_t line_len;
c0335005 1046
d209cc47 1047 line = reader_readline(reader, true);
c0335005 1048 if (line == NULL)
dda708e7 1049 return -1;
c0335005
CMN
1050
1051 /* find the end of the variable's name */
566dd8ce 1052 name_end = strrchr(line, ']');
5a0659fe 1053 if (name_end == NULL) {
3286c408 1054 git__free(line);
d209cc47 1055 set_parse_error(reader, 0, "Missing ']' in section header");
dda708e7 1056 return -1;
5a0659fe 1057 }
c0335005 1058
f1453c59 1059 GITERR_CHECK_ALLOC_ADD(&line_len, (size_t)(name_end - line), 1);
392702ee 1060 name = git__malloc(line_len);
dda708e7 1061 GITERR_CHECK_ALLOC(name);
c0335005
CMN
1062
1063 name_length = 0;
1064 pos = 0;
1065
1066 /* Make sure we were given a section header */
1067 c = line[pos++];
dda708e7 1068 assert(c == '[');
c0335005
CMN
1069
1070 c = line[pos++];
1071
1072 do {
0f49200c 1073 if (git__isspace(c)){
c0335005 1074 name[name_length] = '\0';
d209cc47 1075 result = parse_section_header_ext(reader, line, name, section_out);
3286c408
VM
1076 git__free(line);
1077 git__free(name);
dda708e7 1078 return result;
c0335005
CMN
1079 }
1080
1081 if (!config_keychar(c) && c != '.') {
d209cc47 1082 set_parse_error(reader, pos, "Unexpected character in header");
dda708e7 1083 goto fail_parse;
c0335005
CMN
1084 }
1085
75a4636f 1086 name[name_length++] = (char)git__tolower(c);
c0335005
CMN
1087
1088 } while ((c = line[pos++]) != ']');
1089
5a0659fe 1090 if (line[pos - 1] != ']') {
d209cc47 1091 set_parse_error(reader, pos, "Unexpected end of file");
dda708e7 1092 goto fail_parse;
5a0659fe 1093 }
f58c53ce 1094
3286c408 1095 git__free(line);
dda708e7
VM
1096
1097 name[name_length] = 0;
c0335005 1098 *section_out = name;
c0335005 1099
dda708e7
VM
1100 return 0;
1101
1102fail_parse:
3286c408
VM
1103 git__free(line);
1104 git__free(name);
dda708e7 1105 return -1;
c0335005
CMN
1106}
1107
d209cc47 1108static int skip_bom(struct reader *reader)
c0335005 1109{
7bf87ab6
RB
1110 git_bom_t bom;
1111 int bom_offset = git_buf_text_detect_bom(&bom,
d209cc47 1112 &reader->buffer, reader->read_ptr - reader->buffer.ptr);
c0335005 1113
7bf87ab6 1114 if (bom == GIT_BOM_UTF8)
d209cc47 1115 reader->read_ptr += bom_offset;
c0335005 1116
7bf87ab6 1117 /* TODO: reference implementation is pretty stupid with BoM */
c0335005 1118
dda708e7 1119 return 0;
c0335005
CMN
1120}
1121
1122/*
1123 (* basic types *)
1124 digit = "0".."9"
1125 integer = digit { digit }
1126 alphabet = "a".."z" + "A" .. "Z"
1127
1128 section_char = alphabet | "." | "-"
1129 extension_char = (* any character except newline *)
1130 any_char = (* any character *)
1131 variable_char = "alphabet" | "-"
1132
1133
1134 (* actual grammar *)
1135 config = { section }
1136
1137 section = header { definition }
1138
1139 header = "[" section [subsection | subsection_ext] "]"
1140
1141 subsection = "." section
1142 subsection_ext = "\"" extension "\""
1143
1144 section = section_char { section_char }
1145 extension = extension_char { extension_char }
1146
1147 definition = variable_name ["=" variable_value] "\n"
1148
1149 variable_name = variable_char { variable_char }
1150 variable_value = string | boolean | integer
1151
1152 string = quoted_string | plain_string
1153 quoted_string = "\"" plain_string "\""
1154 plain_string = { any_char }
1155
1156 boolean = boolean_true | boolean_false
1157 boolean_true = "yes" | "1" | "true" | "on"
1158 boolean_false = "no" | "0" | "false" | "off"
1159*/
1160
2c1075d6 1161static int strip_comments(char *line, int in_quotes)
c0335005 1162{
e009a705 1163 int quote_count = in_quotes, backslash_count = 0;
c0335005
CMN
1164 char *ptr;
1165
1166 for (ptr = line; *ptr; ++ptr) {
1167 if (ptr[0] == '"' && ptr > line && ptr[-1] != '\\')
1168 quote_count++;
1169
e009a705
ET
1170 if ((ptr[0] == ';' || ptr[0] == '#') &&
1171 (quote_count % 2) == 0 &&
1172 (backslash_count % 2) == 0) {
c0335005
CMN
1173 ptr[0] = '\0';
1174 break;
1175 }
e009a705
ET
1176
1177 if (ptr[0] == '\\')
1178 backslash_count++;
1179 else
1180 backslash_count = 0;
c0335005
CMN
1181 }
1182
2c1075d6 1183 /* skip any space at the end */
0a641647 1184 while (ptr > line && git__isspace(ptr[-1])) {
2c1075d6 1185 ptr--;
c0335005 1186 }
2c1075d6
CMN
1187 ptr[0] = '\0';
1188
1189 return quote_count;
c0335005
CMN
1190}
1191
d8d25acb
CMN
1192static int included_path(git_buf *out, const char *dir, const char *path)
1193{
1194 /* From the user's home */
1195 if (path[0] == '~' && path[1] == '/')
83634d38 1196 return git_sysdir_find_global_file(out, &path[1]);
d8d25acb
CMN
1197
1198 return git_path_join_unrooted(out, path, dir, NULL);
1199}
1200
49938cad
CMN
1201static const char *escapes = "ntb\"\\";
1202static const char *escaped = "\n\t\b\"\\";
1203
1204/* Escape the values to write them to the file */
1205static char *escape_value(const char *ptr)
1206{
1207 git_buf buf = GIT_BUF_INIT;
1208 size_t len;
1209 const char *esc;
1210
1211 assert(ptr);
1212
1213 len = strlen(ptr);
c57f6682
NV
1214 if (!len)
1215 return git__calloc(1, sizeof(char));
1216
49938cad
CMN
1217 git_buf_grow(&buf, len);
1218
1219 while (*ptr != '\0') {
1220 if ((esc = strchr(escaped, *ptr)) != NULL) {
1221 git_buf_putc(&buf, '\\');
1222 git_buf_putc(&buf, escapes[esc - escaped]);
1223 } else {
1224 git_buf_putc(&buf, *ptr);
1225 }
1226 ptr++;
1227 }
1228
1229 if (git_buf_oom(&buf)) {
1230 git_buf_free(&buf);
1231 return NULL;
1232 }
1233
1234 return git_buf_detach(&buf);
1235}
1236
2c1075d6 1237/* '\"' -> '"' etc */
7f2e61f3
ET
1238static int unescape_line(
1239 char **out, bool *is_multi, const char *ptr, int quote_count)
c0335005 1240{
7f2e61f3 1241 char *str, *fixed, *esc;
f1453c59 1242 size_t ptr_len = strlen(ptr), alloc_len;
2c1075d6 1243
7f2e61f3
ET
1244 *is_multi = false;
1245
f1453c59
ET
1246 if (GIT_ADD_SIZET_OVERFLOW(&alloc_len, ptr_len, 1) ||
1247 (str = git__malloc(alloc_len)) == NULL) {
7f2e61f3 1248 return -1;
392702ee
ET
1249 }
1250
7f2e61f3 1251 fixed = str;
c0335005 1252
2c1075d6
CMN
1253 while (*ptr != '\0') {
1254 if (*ptr == '"') {
1255 quote_count++;
1256 } else if (*ptr != '\\') {
7f2e61f3 1257 *fixed++ = *ptr;
2c1075d6
CMN
1258 } else {
1259 /* backslash, check the next char */
1260 ptr++;
1261 /* if we're at the end, it's a multiline, so keep the backslash */
1262 if (*ptr == '\0') {
7f2e61f3
ET
1263 *is_multi = true;
1264 goto done;
2c1075d6 1265 }
2c1075d6 1266 if ((esc = strchr(escapes, *ptr)) != NULL) {
7f2e61f3 1267 *fixed++ = escaped[esc - escapes];
2c1075d6
CMN
1268 } else {
1269 git__free(str);
1270 giterr_set(GITERR_CONFIG, "Invalid escape at %s", ptr);
7f2e61f3 1271 return -1;
2c1075d6
CMN
1272 }
1273 }
1274 ptr++;
1275 }
1276
7f2e61f3
ET
1277done:
1278 *fixed = '\0';
1279 *out = str;
9f35754a 1280
7f2e61f3 1281 return 0;
c0335005
CMN
1282}
1283
d209cc47 1284static int parse_multiline_variable(struct reader *reader, git_buf *value, int in_quotes)
c0335005 1285{
2c1075d6
CMN
1286 char *line = NULL, *proc_line = NULL;
1287 int quote_count;
7f2e61f3 1288 bool multiline;
c0335005
CMN
1289
1290 /* Check that the next line exists */
d209cc47 1291 line = reader_readline(reader, false);
c0335005 1292 if (line == NULL)
dda708e7 1293 return -1;
c0335005 1294
7f2e61f3
ET
1295 /* We've reached the end of the file, there is no continuation.
1296 * (this is not an error).
1297 */
c0335005 1298 if (line[0] == '\0') {
dda708e7 1299 git__free(line);
7f2e61f3 1300 return 0;
c0335005
CMN
1301 }
1302
2c1075d6 1303 quote_count = strip_comments(line, !!in_quotes);
c0335005
CMN
1304
1305 /* If it was just a comment, pretend it didn't exist */
1306 if (line[0] == '\0') {
dda708e7 1307 git__free(line);
d209cc47 1308 return parse_multiline_variable(reader, value, quote_count);
dda708e7 1309 /* TODO: unbounded recursion. This **could** be exploitable */
c0335005
CMN
1310 }
1311
7f2e61f3 1312 if (unescape_line(&proc_line, &multiline, line, in_quotes) < 0) {
2c1075d6
CMN
1313 git__free(line);
1314 return -1;
c0335005 1315 }
dda708e7 1316 /* add this line to the multiline var */
e009a705 1317
2c1075d6 1318 git_buf_puts(value, proc_line);
dda708e7 1319 git__free(line);
2c1075d6 1320 git__free(proc_line);
c0335005
CMN
1321
1322 /*
dda708e7
VM
1323 * If we need to continue reading the next line, let's just
1324 * keep putting stuff in the buffer
c0335005 1325 */
7f2e61f3 1326 if (multiline)
d209cc47 1327 return parse_multiline_variable(reader, value, quote_count);
c0335005 1328
dda708e7 1329 return 0;
c0335005
CMN
1330}
1331
2c8c00c6
ET
1332GIT_INLINE(bool) is_namechar(char c)
1333{
1334 return isalnum(c) || c == '-';
1335}
1336
1337static int parse_name(
1338 char **name, const char **value, struct reader *reader, const char *line)
1339{
1340 const char *name_end = line, *value_start;
1341
1342 *name = NULL;
1343 *value = NULL;
1344
1345 while (*name_end && is_namechar(*name_end))
1346 name_end++;
1347
1348 if (line == name_end) {
1349 set_parse_error(reader, 0, "Invalid configuration key");
1350 return -1;
1351 }
1352
1353 value_start = name_end;
1354
1355 while (*value_start && git__isspace(*value_start))
1356 value_start++;
1357
1358 if (*value_start == '=') {
1359 *value = value_start + 1;
1360 } else if (*value_start) {
1361 set_parse_error(reader, 0, "Invalid configuration key");
1362 return -1;
1363 }
1364
1365 if ((*name = git__strndup(line, name_end - line)) == NULL)
1366 return -1;
1367
1368 return 0;
1369}
1370
d209cc47 1371static int parse_variable(struct reader *reader, char **var_name, char **var_value)
c0335005 1372{
c0335005
CMN
1373 const char *value_start = NULL;
1374 char *line;
2c1075d6 1375 int quote_count;
7f2e61f3 1376 bool multiline;
c0335005 1377
d209cc47 1378 line = reader_readline(reader, true);
c0335005 1379 if (line == NULL)
dda708e7 1380 return -1;
c0335005 1381
2c1075d6 1382 quote_count = strip_comments(line, 0);
c0335005 1383
dda708e7
VM
1384 /* If there is no value, boolean true is assumed */
1385 *var_value = NULL;
c0335005 1386
b162d97a
CMN
1387 if (parse_name(var_name, &value_start, reader, line) < 0)
1388 goto on_error;
1389
c0335005
CMN
1390 /*
1391 * Now, let's try to parse the value
1392 */
1393 if (value_start != NULL) {
0f49200c 1394 while (git__isspace(value_start[0]))
c0335005
CMN
1395 value_start++;
1396
7f2e61f3
ET
1397 if (unescape_line(var_value, &multiline, value_start, 0) < 0)
1398 goto on_error;
1399
1400 if (multiline) {
dda708e7 1401 git_buf multi_value = GIT_BUF_INIT;
7f2e61f3
ET
1402 git_buf_attach(&multi_value, *var_value, 0);
1403
1404 if (parse_multiline_variable(reader, &multi_value, quote_count) < 0 ||
1405 git_buf_oom(&multi_value)) {
dda708e7 1406 git_buf_free(&multi_value);
7f2e61f3 1407 goto on_error;
9f861826 1408 }
c0335005 1409
dda708e7 1410 *var_value = git_buf_detach(&multi_value);
c0335005 1411 }
c0335005
CMN
1412 }
1413
3286c408 1414 git__free(line);
dda708e7 1415 return 0;
7f2e61f3
ET
1416
1417on_error:
1418 git__free(*var_name);
1419 git__free(line);
1420 return -1;
c0335005 1421}
bf99390e
ET
1422
1423static int config_parse(
1424 struct reader *reader,
2a950c94
ET
1425 int (*on_section)(struct reader **reader, const char *current_section, const char *line, size_t line_len, void *data),
1426 int (*on_variable)(struct reader **reader, const char *current_section, char *var_name, char *var_value, const char *line, size_t line_len, void *data),
1427 int (*on_comment)(struct reader **reader, const char *line, size_t line_len, void *data),
bf99390e
ET
1428 int (*on_eof)(struct reader **reader, void *data),
1429 void *data)
1430{
2a950c94 1431 char *current_section = NULL, *var_name, *var_value, *line_start;
bf99390e 1432 char c;
2a950c94 1433 size_t line_len;
bf99390e
ET
1434 int result = 0;
1435
1436 skip_bom(reader);
1437
1438 while (result == 0 && !reader->eof) {
2a950c94
ET
1439 line_start = reader->read_ptr;
1440
bf99390e
ET
1441 c = reader_peek(reader, SKIP_WHITESPACE);
1442
1443 switch (c) {
2a950c94 1444 case '\0': /* EOF when peeking, set EOF in the reader to exit the loop */
bf99390e
ET
1445 reader->eof = 1;
1446 break;
1447
1448 case '[': /* section header, new section begins */
1449 git__free(current_section);
1450 current_section = NULL;
1451
2a950c94
ET
1452 if ((result = parse_section_header(reader, &current_section)) == 0 && on_section) {
1453 line_len = reader->read_ptr - line_start;
1454 result = on_section(&reader, current_section, line_start, line_len, data);
1455 }
bf99390e
ET
1456 break;
1457
2a950c94 1458 case '\n': /* comment or whitespace-only */
bf99390e
ET
1459 case ';':
1460 case '#':
bf99390e 1461 reader_consume_line(reader);
2a950c94
ET
1462
1463 if (on_comment) {
1464 line_len = reader->read_ptr - line_start;
1465 result = on_comment(&reader, line_start, line_len, data);
1466 }
bf99390e
ET
1467 break;
1468
1469 default: /* assume variable declaration */
2a950c94
ET
1470 if ((result = parse_variable(reader, &var_name, &var_value)) == 0 && on_variable) {
1471 line_len = reader->read_ptr - line_start;
1472 result = on_variable(&reader, current_section, var_name, var_value, line_start, line_len, data);
1473 }
bf99390e
ET
1474 break;
1475 }
1476 }
1477
1478 if (on_eof)
1479 result = on_eof(&reader, data);
1480
1481 git__free(current_section);
1482 return result;
1483}
1484
1485struct parse_data {
1486 git_strmap *values;
1487 diskfile_backend *cfg_file;
1488 uint32_t reader_idx;
1489 git_config_level_t level;
1490 int depth;
1491};
1492
2a950c94
ET
1493static int read_on_variable(
1494 struct reader **reader,
1495 const char *current_section,
1496 char *var_name,
1497 char *var_value,
1498 const char *line,
1499 size_t line_len,
1500 void *data)
bf99390e
ET
1501{
1502 struct parse_data *parse_data = (struct parse_data *)data;
1503 git_buf buf = GIT_BUF_INIT;
1504 cvar_t *var;
1505 int result = 0;
1506
63c0cc65
ET
1507 GIT_UNUSED(line);
1508 GIT_UNUSED(line_len);
1509
bf99390e
ET
1510 git__strtolower(var_name);
1511 git_buf_printf(&buf, "%s.%s", current_section, var_name);
1512 git__free(var_name);
1513
1514 if (git_buf_oom(&buf)) {
1515 git__free(var_value);
1516 return -1;
1517 }
1518
1519 var = git__calloc(1, sizeof(cvar_t));
1520 GITERR_CHECK_ALLOC(var);
1521 var->entry = git__calloc(1, sizeof(git_config_entry));
1522 GITERR_CHECK_ALLOC(var->entry);
1523
1524 var->entry->name = git_buf_detach(&buf);
1525 var->entry->value = var_value;
1526 var->entry->level = parse_data->level;
1527 var->included = !!parse_data->depth;
1528
1529 if ((result = append_entry(parse_data->values, var)) < 0)
1530 return result;
1531
1532 result = 0;
1533
1534 /* Add or append the new config option */
1535 if (!git__strcmp(var->entry->name, "include.path")) {
1536 struct reader *r;
1537 git_buf path = GIT_BUF_INIT;
1538 char *dir;
1539 uint32_t index;
1540
1541 r = git_array_alloc(parse_data->cfg_file->readers);
1542 /* The reader may have been reallocated */
1543 *reader = git_array_get(parse_data->cfg_file->readers, parse_data->reader_idx);
1544 memset(r, 0, sizeof(struct reader));
1545
1546 if ((result = git_path_dirname_r(&path, (*reader)->file_path)) < 0)
1547 return result;
1548
1549 /* We need to know our index in the array, as the next config_parse call may realloc */
1550 index = git_array_size(parse_data->cfg_file->readers) - 1;
1551 dir = git_buf_detach(&path);
1552 result = included_path(&path, dir, var->entry->value);
1553 git__free(dir);
1554
1555 if (result < 0)
1556 return result;
1557
1558 r->file_path = git_buf_detach(&path);
1559 git_buf_init(&r->buffer, 0);
1560
1561 result = git_futils_readbuffer_updated(
1562 &r->buffer, r->file_path, &r->file_mtime, &r->file_size, NULL);
1563
1564 if (result == 0) {
1565 result = config_read(parse_data->values, parse_data->cfg_file, r, parse_data->level, parse_data->depth+1);
1566 r = git_array_get(parse_data->cfg_file->readers, index);
1567 *reader = git_array_get(parse_data->cfg_file->readers, parse_data->reader_idx);
1568 } else if (result == GIT_ENOTFOUND) {
1569 giterr_clear();
1570 result = 0;
1571 }
1572
1573 git_buf_free(&r->buffer);
1574 }
1575
1576 return result;
1577}
1578
1579static int config_read(git_strmap *values, diskfile_backend *cfg_file, struct reader *reader, git_config_level_t level, int depth)
1580{
1581 struct parse_data parse_data;
1582
1583 if (depth >= MAX_INCLUDE_DEPTH) {
1584 giterr_set(GITERR_CONFIG, "Maximum config include depth reached");
1585 return -1;
1586 }
1587
1588 /* Initialize the reading position */
1589 reader->read_ptr = reader->buffer.ptr;
1590 reader->eof = 0;
1591
1592 /* If the file is empty, there's nothing for us to do */
1593 if (*reader->read_ptr == '\0')
1594 return 0;
1595
1596 parse_data.values = values;
1597 parse_data.cfg_file = cfg_file;
1598 parse_data.reader_idx = git_array_size(cfg_file->readers) - 1;
1599 parse_data.level = level;
1600 parse_data.depth = depth;
1601
2a950c94 1602 return config_parse(reader, NULL, read_on_variable, NULL, NULL, &parse_data);
bf99390e
ET
1603}
1604
1605static int write_section(git_filebuf *file, const char *key)
1606{
1607 int result;
1608 const char *dot;
1609 git_buf buf = GIT_BUF_INIT;
1610
1611 /* All of this just for [section "subsection"] */
1612 dot = strchr(key, '.');
1613 git_buf_putc(&buf, '[');
1614 if (dot == NULL) {
1615 git_buf_puts(&buf, key);
1616 } else {
1617 char *escaped;
1618 git_buf_put(&buf, key, dot - key);
1619 escaped = escape_value(dot + 1);
1620 GITERR_CHECK_ALLOC(escaped);
1621 git_buf_printf(&buf, " \"%s\"", escaped);
1622 git__free(escaped);
1623 }
1624 git_buf_puts(&buf, "]\n");
1625
1626 if (git_buf_oom(&buf))
1627 return -1;
1628
1629 result = git_filebuf_write(file, git_buf_cstr(&buf), buf.size);
1630 git_buf_free(&buf);
1631
1632 return result;
1633}
1634
1635static const char *quotes_for_value(const char *value)
1636{
1637 const char *ptr;
1638
1639 if (value[0] == ' ' || value[0] == '\0')
1640 return "\"";
1641
1642 for (ptr = value; *ptr; ++ptr) {
1643 if (*ptr == ';' || *ptr == '#')
1644 return "\"";
1645 }
1646
1647 if (ptr[-1] == ' ')
1648 return "\"";
1649
1650 return "";
1651}
1652
1653struct write_data {
1654 git_filebuf *file;
1655 unsigned int in_section : 1,
1656 preg_replaced : 1;
1657 const char *section;
1658 const char *name;
1659 const regex_t *preg;
1660 const char *value;
1661};
1662
2a950c94
ET
1663static int write_line(struct write_data *write_data, const char *line, size_t line_len)
1664{
1665 int result = git_filebuf_write(write_data->file, line, line_len);
1666
1667 if (!result && line_len && line[line_len-1] != '\n')
1668 result = git_filebuf_printf(write_data->file, "\n");
1669
1670 return result;
1671}
1672
bf99390e
ET
1673static int write_value(struct write_data *write_data)
1674{
1675 const char *q;
1676 int result;
1677
1678 q = quotes_for_value(write_data->value);
1679 result = git_filebuf_printf(write_data->file,
1680 "\t%s = %s%s%s\n", write_data->name, q, write_data->value, q);
1681
1682 /* If we are updating a single name/value, we're done. Setting `value`
1683 * to `NULL` will prevent us from trying to write it again later (in
1684 * `write_on_section`) if we see the same section repeated.
1685 */
1686 if (!write_data->preg)
1687 write_data->value = NULL;
1688
1689 return result;
1690}
1691
2a950c94
ET
1692static int write_on_section(
1693 struct reader **reader,
1694 const char *current_section,
1695 const char *line,
1696 size_t line_len,
1697 void *data)
bf99390e
ET
1698{
1699 struct write_data *write_data = (struct write_data *)data;
1700 int result = 0;
1701
63c0cc65
ET
1702 GIT_UNUSED(reader);
1703
bf99390e
ET
1704 /* If we were previously in the correct section (but aren't anymore)
1705 * and haven't written our value (for a simple name/value set, not
1706 * a multivar), then append it to the end of the section before writing
1707 * the new one.
1708 */
1709 if (write_data->in_section && !write_data->preg && write_data->value)
1710 result = write_value(write_data);
1711
1712 write_data->in_section = strcmp(current_section, write_data->section) == 0;
1713
bf99390e 1714 if (!result)
2a950c94 1715 result = write_line(write_data, line, line_len);
bf99390e
ET
1716
1717 return result;
1718}
1719
2a950c94
ET
1720static int write_on_variable(
1721 struct reader **reader,
1722 const char *current_section,
1723 char *var_name,
1724 char *var_value,
1725 const char *line,
1726 size_t line_len,
1727 void *data)
bf99390e
ET
1728{
1729 struct write_data *write_data = (struct write_data *)data;
1730 bool has_matched = false;
63c0cc65
ET
1731
1732 GIT_UNUSED(reader);
1733 GIT_UNUSED(current_section);
bf99390e
ET
1734
1735 /* See if we are to update this name/value pair; first examine name */
1736 if (write_data->in_section &&
1737 strcasecmp(write_data->name, var_name) == 0)
1738 has_matched = true;
1739
1740 /* If we have a regex to match the value, see if it matches */
1741 if (has_matched && write_data->preg != NULL)
1742 has_matched = (regexec(write_data->preg, var_value, 0, NULL, 0) == 0);
1743
2a950c94
ET
1744 git__free(var_name);
1745 git__free(var_value);
bf99390e
ET
1746
1747 /* If this isn't the name/value we're looking for, simply dump the
1748 * existing data back out and continue on.
1749 */
2a950c94
ET
1750 if (!has_matched)
1751 return write_line(write_data, line, line_len);
bf99390e
ET
1752
1753 write_data->preg_replaced = 1;
1754
1755 /* If value is NULL, we are deleting this value; write nothing. */
1756 if (!write_data->value)
1757 return 0;
1758
1759 return write_value(write_data);
1760}
1761
2a950c94
ET
1762static int write_on_comment(struct reader **reader, const char *line, size_t line_len, void *data)
1763{
63c0cc65
ET
1764 struct write_data *write_data;
1765
1766 GIT_UNUSED(reader);
1767
1768 write_data = (struct write_data *)data;
2a950c94
ET
1769 return write_line(write_data, line, line_len);
1770}
1771
bf99390e
ET
1772static int write_on_eof(struct reader **reader, void *data)
1773{
1774 struct write_data *write_data = (struct write_data *)data;
1775 int result = 0;
1776
63c0cc65
ET
1777 GIT_UNUSED(reader);
1778
bf99390e
ET
1779 /* If we are at the EOF and have not written our value (again, for a
1780 * simple name/value set, not a multivar) then we have never seen the
1781 * section in question and should create a new section and write the
1782 * value.
1783 */
1784 if ((!write_data->preg || !write_data->preg_replaced) && write_data->value) {
1785 if ((result = write_section(write_data->file, write_data->section)) == 0)
1786 result = write_value(write_data);
1787 }
1788
1789 return result;
1790}
1791
1792/*
1793 * This is pretty much the parsing, except we write out anything we don't have
1794 */
1795static int config_write(diskfile_backend *cfg, const char *key, const regex_t *preg, const char* value)
1796{
1797 int result;
63c0cc65 1798 char *section, *name, *ldot;
bf99390e
ET
1799 git_filebuf file = GIT_FILEBUF_INIT;
1800 struct reader *reader = git_array_get(cfg->readers, 0);
1801 struct write_data write_data;
1802
9c26de0f
ET
1803 /* Lock the file */
1804 if ((result = git_filebuf_open(
1805 &file, cfg->file_path, 0, GIT_CONFIG_FILE_MODE)) < 0) {
1806 git_buf_free(&reader->buffer);
1807 return result;
1808 }
bf99390e
ET
1809
1810 /* We need to read in our own config file */
1811 result = git_futils_readbuffer(&reader->buffer, cfg->file_path);
1812
1813 /* Initialise the reading position */
1814 if (result == GIT_ENOTFOUND) {
1815 reader->read_ptr = NULL;
1816 reader->eof = 1;
bf99390e
ET
1817 git_buf_clear(&reader->buffer);
1818 } else if (result == 0) {
1819 reader->read_ptr = reader->buffer.ptr;
1820 reader->eof = 0;
bf99390e 1821 } else {
9c26de0f 1822 git_filebuf_cleanup(&file);
bf99390e
ET
1823 return -1; /* OS error when reading the file */
1824 }
1825
bf99390e
ET
1826 ldot = strrchr(key, '.');
1827 name = ldot + 1;
1828 section = git__strndup(key, ldot - key);
1829
1830 write_data.file = &file;
1831 write_data.section = section;
1832 write_data.in_section = 0;
1833 write_data.preg_replaced = 0;
1834 write_data.name = name;
1835 write_data.preg = preg;
1836 write_data.value = value;
1837
b162d97a
CMN
1838 result = config_parse(reader, write_on_section, write_on_variable, write_on_comment, write_on_eof, &write_data);
1839 git__free(section);
1840
1841 if (result < 0) {
bf99390e
ET
1842 git_filebuf_cleanup(&file);
1843 goto done;
1844 }
1845
1846 /* refresh stats - if this errors, then commit will error too */
1847 (void)git_filebuf_stats(&reader->file_mtime, &reader->file_size, &file);
1848
1849 result = git_filebuf_commit(&file);
1850 git_buf_free(&reader->buffer);
1851
1852done:
1853 git_buf_free(&reader->buffer);
1854 return result;
1855}
1856