]> git.proxmox.com Git - libgit2.git/blame - src/config_file.c
remote: create FETCH_HEAD with a refspecless remote
[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"
10#include "fileops.h"
8bb198e6 11#include "filebuf.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
c2b67043 24GIT__USE_STRMAP;
01fed0a8 25
128d3731
VM
26typedef struct cvar_t {
27 struct cvar_t *next;
a1abe66a 28 git_config_entry *entry;
d209cc47 29 int 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 {
54b2a37a 90 git_config_backend parent;
c0335005 91
c2b67043 92 git_strmap *values;
c0335005 93
19be0692 94 git_array_t(struct reader) readers;
c0335005 95
744cc03e 96 char *file_path;
744cc03e 97
16adc9fa 98 git_config_level_t level;
b0b527e0 99} diskfile_backend;
c0335005 100
d209cc47
CMN
101static int config_parse(diskfile_backend *cfg_file, struct reader *reader, git_config_level_t level, int depth);
102static int parse_variable(struct reader *reader, char **var_name, char **var_value);
3005855f 103static int config_write(diskfile_backend *cfg, const char *key, const regex_t *preg, const char *value);
49938cad 104static char *escape_value(const char *ptr);
c0335005 105
d209cc47 106static void set_parse_error(struct reader *reader, int col, const char *error_str)
dda708e7
VM
107{
108 giterr_set(GITERR_CONFIG, "Failed to parse config file: %s (in %s:%d, column %d)",
d209cc47 109 error_str, reader->file_path, reader->line_number, col);
dda708e7
VM
110}
111
128d3731 112static void cvar_free(cvar_t *var)
c0335005
CMN
113{
114 if (var == NULL)
115 return;
116
a1abe66a 117 git__free((char*)var->entry->name);
118 git__free((char *)var->entry->value);
119 git__free(var->entry);
3286c408 120 git__free(var);
c0335005
CMN
121}
122
3793fa9b
DRT
123static int cvar_length(cvar_t *var)
124{
125 int length = 0;
126
127 while (var) {
b22593fb 128 length++;
3793fa9b
DRT
129 var = var->next;
130 }
131
132 return length;
133}
134
1e7799e8
RB
135int git_config_file_normalize_section(char *start, char *end)
136{
137 char *scan;
138
139 if (start == end)
140 return GIT_EINVALIDSPEC;
141
142 /* Validate and downcase range */
143 for (scan = start; *scan; ++scan) {
144 if (end && scan >= end)
145 break;
146 if (isalnum(*scan))
66566516 147 *scan = (char)tolower(*scan);
1e7799e8
RB
148 else if (*scan != '-' || scan == start)
149 return GIT_EINVALIDSPEC;
150 }
151
152 if (scan == start)
153 return GIT_EINVALIDSPEC;
154
155 return 0;
156}
157
c2b67043 158static void free_vars(git_strmap *values)
3b3577c7 159{
fefd4551 160 cvar_t *var = NULL;
3b3577c7 161
fefd4551
CMN
162 if (values == NULL)
163 return;
3b3577c7 164
c2b67043 165 git_strmap_foreach_value(values, var,
01fed0a8
RB
166 while (var != NULL) {
167 cvar_t *next = CVAR_LIST_NEXT(var);
168 cvar_free(var);
169 var = next;
170 });
3b3577c7 171
c2b67043 172 git_strmap_free(values);
3b3577c7
CMN
173}
174
16adc9fa 175static int config_open(git_config_backend *cfg, git_config_level_t level)
c0335005 176{
dda708e7 177 int res;
19be0692 178 struct reader *reader;
b0b527e0 179 diskfile_backend *b = (diskfile_backend *)cfg;
c0335005 180
744cc03e
RB
181 b->level = level;
182
c2b67043 183 b->values = git_strmap_alloc();
dda708e7 184 GITERR_CHECK_ALLOC(b->values);
fefd4551 185
19be0692
CMN
186 git_array_init(b->readers);
187 reader = git_array_alloc(b->readers);
188 memset(reader, 0, sizeof(struct reader));
189
190 reader->file_path = git__strdup(b->file_path);
191 GITERR_CHECK_ALLOC(reader->file_path);
192
193 git_buf_init(&reader->buffer, 0);
744cc03e 194 res = git_futils_readbuffer_updated(
19be0692 195 &reader->buffer, b->file_path, &reader->file_mtime, &reader->file_size, NULL);
9462c471 196
4e90a0a4 197 /* It's fine if the file doesn't exist */
dda708e7
VM
198 if (res == GIT_ENOTFOUND)
199 return 0;
200
19be0692 201 if (res < 0 || (res = config_parse(b, reader, level, 0)) < 0) {
dda708e7
VM
202 free_vars(b->values);
203 b->values = NULL;
dda708e7 204 }
c0335005 205
53ea0513 206 reader = git_array_get(b->readers, 0);
19be0692 207 git_buf_free(&reader->buffer);
744cc03e
RB
208 return res;
209}
210
54b2a37a 211static int config_refresh(git_config_backend *cfg)
744cc03e 212{
a9fb7989 213 int res = 0, updated = 0, any_updated = 0;
744cc03e
RB
214 diskfile_backend *b = (diskfile_backend *)cfg;
215 git_strmap *old_values;
a9fb7989
CMN
216 struct reader *reader;
217 uint32_t i;
744cc03e 218
a9fb7989
CMN
219 for (i = 0; i < git_array_size(b->readers); i++) {
220 reader = git_array_get(b->readers, i);
221 res = git_futils_readbuffer_updated(
222 &reader->buffer, reader->file_path, &reader->file_mtime, &reader->file_size, &updated);
223
224 if (res < 0)
225 return (res == GIT_ENOTFOUND) ? 0 : res;
226
227 if (updated)
228 any_updated = 1;
229 }
230
231 if (!any_updated)
744cc03e
RB
232 return (res == GIT_ENOTFOUND) ? 0 : res;
233
234 /* need to reload - store old values and prep for reload */
235 old_values = b->values;
236 b->values = git_strmap_alloc();
237 GITERR_CHECK_ALLOC(b->values);
238
19be0692 239 if ((res = config_parse(b, reader, b->level, 0)) < 0) {
744cc03e
RB
240 free_vars(b->values);
241 b->values = old_values;
242 } else {
243 free_vars(old_values);
244 }
245
19be0692 246 git_buf_free(&reader->buffer);
744cc03e 247 return res;
c0335005
CMN
248}
249
54b2a37a 250static void backend_free(git_config_backend *_backend)
c0335005 251{
b0b527e0 252 diskfile_backend *backend = (diskfile_backend *)_backend;
19be0692 253 uint32_t i;
c0335005
CMN
254
255 if (backend == NULL)
256 return;
257
19be0692
CMN
258 for (i = 0; i < git_array_size(backend->readers); i++) {
259 struct reader *r = git_array_get(backend->readers, i);
260 git__free(r->file_path);
261 }
262 git_array_clear(backend->readers);
263
3286c408 264 git__free(backend->file_path);
fefd4551 265 free_vars(backend->values);
3286c408 266 git__free(backend);
c0335005
CMN
267}
268
a603c191 269static void config_iterator_free(
eba73992 270 git_config_iterator* iter)
c0335005 271{
a603c191
NG
272 git__free(iter);
273}
c0335005 274
4d588d97 275static int config_iterator_next(
99dfb538 276 git_config_entry **entry,
eba73992 277 git_config_iterator *iter)
a603c191 278{
82ae6fcd
CMN
279 git_config_file_iter *it = (git_config_file_iter *) iter;
280 diskfile_backend *b = (diskfile_backend *) it->parent.backend;
281 int err = 0;
a603c191 282 cvar_t * var;
c0335005 283
eba73992 284 if (it->next_var == NULL) {
43e5dda7 285 err = git_strmap_next((void**) &var, &(it->iter), b->values);
a603c191 286 } else {
eba73992 287 var = it->next_var;
b3ff1dab 288 }
c0335005 289
a603c191 290 if (err < 0) {
eba73992 291 it->next_var = NULL;
5880962d 292 return err;
a603c191 293 }
0c8858de 294
99dfb538 295 *entry = var->entry;
eba73992 296 it->next_var = CVAR_LIST_NEXT(var);
b3ff1dab 297
eba73992
CMN
298 return 0;
299}
c0335005 300
eba73992
CMN
301static int config_iterator_new(
302 git_config_iterator **iter,
303 struct git_config_backend* backend)
304{
305 diskfile_backend *b = (diskfile_backend *)backend;
306 git_config_file_iter *it = git__calloc(1, sizeof(git_config_file_iter));
b3ff1dab 307
a9f51e43
RB
308 GIT_UNUSED(b);
309
eba73992
CMN
310 GITERR_CHECK_ALLOC(it);
311
312 it->parent.backend = backend;
313 it->iter = git_strmap_begin(b->values);
314 it->next_var = NULL;
315
316 it->parent.next = config_iterator_next;
317 it->parent.free = config_iterator_free;
318 *iter = (git_config_iterator *) it;
a603c191
NG
319
320 return 0;
c0335005
CMN
321}
322
54b2a37a 323static int config_set(git_config_backend *cfg, const char *name, const char *value)
c0335005 324{
e583334c 325 cvar_t *var = NULL, *old_var = NULL;
b0b527e0 326 diskfile_backend *b = (diskfile_backend *)cfg;
49938cad 327 char *key, *esc_value = NULL;
01fed0a8 328 khiter_t pos;
49938cad 329 int rval, ret;
fefd4551 330
f4be8209 331 if ((rval = git_config__normalize_name(name, &key)) < 0)
1e7799e8 332 return rval;
c0335005
CMN
333
334 /*
fefd4551
CMN
335 * Try to find it in the existing values and update it if it
336 * only has one value.
c0335005 337 */
c2b67043
RB
338 pos = git_strmap_lookup_index(b->values, key);
339 if (git_strmap_valid_index(b->values, pos)) {
340 cvar_t *existing = git_strmap_value_at(b->values, pos);
dda708e7 341 char *tmp = NULL;
fefd4551
CMN
342
343 git__free(key);
aa13bf05 344
dda708e7
VM
345 if (existing->next != NULL) {
346 giterr_set(GITERR_CONFIG, "Multivar incompatible with simple set");
347 return -1;
348 }
fefd4551 349
aa13bf05 350 /* don't update if old and new values already match */
a1abe66a 351 if ((!existing->entry->value && !value) ||
352 (existing->entry->value && value && !strcmp(existing->entry->value, value)))
aa13bf05
RB
353 return 0;
354
dda708e7
VM
355 if (value) {
356 tmp = git__strdup(value);
357 GITERR_CHECK_ALLOC(tmp);
49938cad
CMN
358 esc_value = escape_value(value);
359 GITERR_CHECK_ALLOC(esc_value);
dda708e7 360 }
c0335005 361
a1abe66a 362 git__free((void *)existing->entry->value);
363 existing->entry->value = tmp;
c0335005 364
a1abe66a 365 ret = config_write(b, existing->entry->name, NULL, esc_value);
49938cad
CMN
366
367 git__free(esc_value);
368 return ret;
29dca088
CMN
369 }
370
128d3731 371 var = git__malloc(sizeof(cvar_t));
dda708e7 372 GITERR_CHECK_ALLOC(var);
128d3731 373 memset(var, 0x0, sizeof(cvar_t));
a1abe66a 374 var->entry = git__malloc(sizeof(git_config_entry));
375 GITERR_CHECK_ALLOC(var->entry);
376 memset(var->entry, 0x0, sizeof(git_config_entry));
c0335005 377
a1abe66a 378 var->entry->name = key;
379 var->entry->value = NULL;
c0335005 380
dda708e7 381 if (value) {
a1abe66a 382 var->entry->value = git__strdup(value);
383 GITERR_CHECK_ALLOC(var->entry->value);
49938cad
CMN
384 esc_value = escape_value(value);
385 GITERR_CHECK_ALLOC(esc_value);
c0335005
CMN
386 }
387
49938cad
CMN
388 if (config_write(b, key, NULL, esc_value) < 0) {
389 git__free(esc_value);
3df9cc59
CMN
390 cvar_free(var);
391 return -1;
392 }
393
49938cad 394 git__free(esc_value);
c2b67043 395 git_strmap_insert2(b->values, key, var, old_var, rval);
01fed0a8 396 if (rval < 0)
dda708e7 397 return -1;
01fed0a8
RB
398 if (old_var != NULL)
399 cvar_free(old_var);
c0335005 400
dda708e7 401 return 0;
c0335005
CMN
402}
403
404/*
405 * Internal function that actually gets the value in string form
406 */
54b2a37a 407static int config_get(const git_config_backend *cfg, const char *name, const git_config_entry **out)
c0335005 408{
b0b527e0 409 diskfile_backend *b = (diskfile_backend *)cfg;
fefd4551 410 char *key;
01fed0a8 411 khiter_t pos;
1e7799e8 412 int error;
73fc5e01 413 cvar_t *var;
c0335005 414
f4be8209 415 if ((error = git_config__normalize_name(name, &key)) < 0)
1e7799e8 416 return error;
fefd4551 417
c2b67043 418 pos = git_strmap_lookup_index(b->values, key);
fefd4551 419 git__free(key);
c0335005 420
dda708e7 421 /* no error message; the config system will write one */
c2b67043 422 if (!git_strmap_valid_index(b->values, pos))
dda708e7 423 return GIT_ENOTFOUND;
c0335005 424
73fc5e01
CMN
425 var = git_strmap_value_at(b->values, pos);
426 while (var->next)
427 var = var->next;
428
429 *out = var->entry;
7b2b4adf 430
dda708e7 431 return 0;
c0335005
CMN
432}
433
01fed0a8 434static int config_set_multivar(
54b2a37a 435 git_config_backend *cfg, const char *name, const char *regexp, const char *value)
3005855f 436{
dda708e7 437 int replaced = 0;
0a43d7cb 438 cvar_t *var, *newvar;
3005855f
CMN
439 diskfile_backend *b = (diskfile_backend *)cfg;
440 char *key;
441 regex_t preg;
dda708e7 442 int result;
01fed0a8 443 khiter_t pos;
3005855f 444
dda708e7 445 assert(regexp);
3005855f 446
f4be8209 447 if ((result = git_config__normalize_name(name, &key)) < 0)
1e7799e8 448 return result;
3005855f 449
c2b67043
RB
450 pos = git_strmap_lookup_index(b->values, key);
451 if (!git_strmap_valid_index(b->values, pos)) {
e5a27f03
CMN
452 /* If we don't have it, behave like a normal set */
453 result = config_set(cfg, name, value);
01fed0a8 454 git__free(key);
e5a27f03 455 return result;
0a43d7cb 456 }
3005855f 457
c2b67043 458 var = git_strmap_value_at(b->values, pos);
3005855f 459
dda708e7
VM
460 result = regcomp(&preg, regexp, REG_EXTENDED);
461 if (result < 0) {
2bc8fa02 462 git__free(key);
dda708e7 463 giterr_set_regex(&preg, result);
b3ff1dab 464 regfree(&preg);
dda708e7 465 return -1;
0a43d7cb 466 }
3005855f 467
dda708e7 468 for (;;) {
a1abe66a 469 if (regexec(&preg, var->entry->value, 0, NULL, 0) == 0) {
0a43d7cb 470 char *tmp = git__strdup(value);
dda708e7 471 GITERR_CHECK_ALLOC(tmp);
3005855f 472
a1abe66a 473 git__free((void *)var->entry->value);
474 var->entry->value = tmp;
0a43d7cb
CMN
475 replaced = 1;
476 }
477
dda708e7 478 if (var->next == NULL)
0a43d7cb 479 break;
dda708e7
VM
480
481 var = var->next;
482 }
0a43d7cb
CMN
483
484 /* If we've reached the end of the variables and we haven't found it yet, we need to append it */
485 if (!replaced) {
486 newvar = git__malloc(sizeof(cvar_t));
dda708e7 487 GITERR_CHECK_ALLOC(newvar);
3005855f 488 memset(newvar, 0x0, sizeof(cvar_t));
a1abe66a 489 newvar->entry = git__malloc(sizeof(git_config_entry));
490 GITERR_CHECK_ALLOC(newvar->entry);
491 memset(newvar->entry, 0x0, sizeof(git_config_entry));
492
493 newvar->entry->name = git__strdup(var->entry->name);
494 GITERR_CHECK_ALLOC(newvar->entry->name);
dda708e7 495
a1abe66a 496 newvar->entry->value = git__strdup(value);
497 GITERR_CHECK_ALLOC(newvar->entry->value);
dda708e7 498
a1abe66a 499 newvar->entry->level = var->entry->level;
3005855f 500
3005855f 501 var->next = newvar;
3005855f
CMN
502 }
503
dda708e7 504 result = config_write(b, key, &preg, value);
3005855f 505
2bc8fa02 506 git__free(key);
3005855f 507 regfree(&preg);
dda708e7
VM
508
509 return result;
3005855f
CMN
510}
511
54b2a37a 512static int config_delete(git_config_backend *cfg, const char *name)
80a665aa 513{
dda708e7 514 cvar_t *var;
80a665aa 515 diskfile_backend *b = (diskfile_backend *)cfg;
fefd4551 516 char *key;
dda708e7 517 int result;
01fed0a8 518 khiter_t pos;
fefd4551 519
f4be8209 520 if ((result = git_config__normalize_name(name, &key)) < 0)
1e7799e8 521 return result;
fefd4551 522
c2b67043 523 pos = git_strmap_lookup_index(b->values, key);
2bc8fa02 524 git__free(key);
fefd4551 525
1d4dcc4b
CMN
526 if (!git_strmap_valid_index(b->values, pos)) {
527 giterr_set(GITERR_CONFIG, "Could not find key '%s' to delete", name);
dda708e7 528 return GIT_ENOTFOUND;
1d4dcc4b 529 }
fefd4551 530
c2b67043 531 var = git_strmap_value_at(b->values, pos);
80a665aa 532
dda708e7
VM
533 if (var->next != NULL) {
534 giterr_set(GITERR_CONFIG, "Cannot delete multivar with a single delete");
535 return -1;
536 }
80a665aa 537
c2b67043 538 git_strmap_delete_at(b->values, pos);
fefd4551 539
a1abe66a 540 result = config_write(b, var->entry->name, NULL, NULL);
fefd4551 541
dda708e7
VM
542 cvar_free(var);
543 return result;
80a665aa
CMN
544}
545
3793fa9b
DRT
546static int config_delete_multivar(git_config_backend *cfg, const char *name, const char *regexp)
547{
548 cvar_t *var, *prev = NULL, *new_head = NULL;
549 cvar_t **to_delete;
550 int to_delete_idx;
551 diskfile_backend *b = (diskfile_backend *)cfg;
552 char *key;
553 regex_t preg;
554 int result;
555 khiter_t pos;
556
557 if ((result = git_config__normalize_name(name, &key)) < 0)
558 return result;
559
560 pos = git_strmap_lookup_index(b->values, key);
561
562 if (!git_strmap_valid_index(b->values, pos)) {
563 giterr_set(GITERR_CONFIG, "Could not find key '%s' to delete", name);
564 git__free(key);
565 return GIT_ENOTFOUND;
566 }
567
568 var = git_strmap_value_at(b->values, pos);
569
570 result = regcomp(&preg, regexp, REG_EXTENDED);
571 if (result < 0) {
572 git__free(key);
573 giterr_set_regex(&preg, result);
574 regfree(&preg);
575 return -1;
576 }
577
578 to_delete = git__calloc(cvar_length(var), sizeof(cvar_t *));
579 GITERR_CHECK_ALLOC(to_delete);
580 to_delete_idx = 0;
581
b22593fb
VM
582 while (var != NULL) {
583 cvar_t *next = var->next;
3793fa9b
DRT
584
585 if (regexec(&preg, var->entry->value, 0, NULL, 0) == 0) {
586 // If we are past the head, reattach previous node to next one,
587 // otherwise set the new head for the strmap.
588 if (prev != NULL) {
b22593fb 589 prev->next = next;
3793fa9b 590 } else {
b22593fb 591 new_head = next;
3793fa9b
DRT
592 }
593
594 to_delete[to_delete_idx++] = var;
595 } else {
596 prev = var;
597 }
598
b22593fb 599 var = next;
3793fa9b
DRT
600 }
601
602 if (new_head != NULL) {
603 git_strmap_set_value_at(b->values, pos, new_head);
604 } else {
605 git_strmap_delete_at(b->values, pos);
606 }
607
608 if (to_delete_idx > 0)
609 result = config_write(b, key, &preg, NULL);
610
611 while (to_delete_idx-- > 0)
612 cvar_free(to_delete[to_delete_idx]);
613
614 git__free(key);
a71331eb
DRT
615 git__free(to_delete);
616 regfree(&preg);
3793fa9b
DRT
617 return result;
618}
619
54b2a37a 620int git_config_file__ondisk(git_config_backend **out, const char *path)
c0335005 621{
b0b527e0 622 diskfile_backend *backend;
c0335005 623
69177621 624 backend = git__calloc(1, sizeof(diskfile_backend));
dda708e7 625 GITERR_CHECK_ALLOC(backend);
c0335005 626
69177621 627 backend->parent.version = GIT_CONFIG_BACKEND_VERSION;
c0335005
CMN
628
629 backend->file_path = git__strdup(path);
dda708e7 630 GITERR_CHECK_ALLOC(backend->file_path);
c0335005
CMN
631
632 backend->parent.open = config_open;
633 backend->parent.get = config_get;
634 backend->parent.set = config_set;
3005855f 635 backend->parent.set_multivar = config_set_multivar;
9dd4c3e8 636 backend->parent.del = config_delete;
3793fa9b 637 backend->parent.del_multivar = config_delete_multivar;
eba73992 638 backend->parent.iterator = config_iterator_new;
744cc03e 639 backend->parent.refresh = config_refresh;
c0335005
CMN
640 backend->parent.free = backend_free;
641
54b2a37a 642 *out = (git_config_backend *)backend;
c0335005 643
dda708e7 644 return 0;
c0335005
CMN
645}
646
d209cc47 647static int reader_getchar_raw(struct reader *reader)
c0335005
CMN
648{
649 int c;
650
d209cc47 651 c = *reader->read_ptr++;
c0335005
CMN
652
653 /*
654 Win 32 line breaks: if we find a \r\n sequence,
655 return only the \n as a newline
656 */
d209cc47
CMN
657 if (c == '\r' && *reader->read_ptr == '\n') {
658 reader->read_ptr++;
c0335005
CMN
659 c = '\n';
660 }
661
662 if (c == '\n')
d209cc47 663 reader->line_number++;
c0335005
CMN
664
665 if (c == 0) {
d209cc47 666 reader->eof = 1;
c0335005
CMN
667 c = '\n';
668 }
669
670 return c;
671}
672
673#define SKIP_WHITESPACE (1 << 1)
674#define SKIP_COMMENTS (1 << 2)
675
d209cc47 676static int reader_getchar(struct reader *reader, int flags)
c0335005
CMN
677{
678 const int skip_whitespace = (flags & SKIP_WHITESPACE);
679 const int skip_comments = (flags & SKIP_COMMENTS);
680 int c;
681
d209cc47 682 assert(reader->read_ptr);
c0335005 683
d209cc47
CMN
684 do {
685 c = reader_getchar_raw(reader);
686 } while (skip_whitespace && git__isspace(c) &&
687 !reader->eof);
c0335005
CMN
688
689 if (skip_comments && (c == '#' || c == ';')) {
d209cc47
CMN
690 do {
691 c = reader_getchar_raw(reader);
692 } while (c != '\n');
c0335005
CMN
693 }
694
695 return c;
696}
697
698/*
699 * Read the next char, but don't move the reading pointer.
700 */
d209cc47 701static int reader_peek(struct reader *reader, int flags)
c0335005
CMN
702{
703 void *old_read_ptr;
704 int old_lineno, old_eof;
705 int ret;
706
d209cc47 707 assert(reader->read_ptr);
c0335005 708
d209cc47
CMN
709 old_read_ptr = reader->read_ptr;
710 old_lineno = reader->line_number;
711 old_eof = reader->eof;
c0335005 712
d209cc47 713 ret = reader_getchar(reader, flags);
c0335005 714
d209cc47
CMN
715 reader->read_ptr = old_read_ptr;
716 reader->line_number = old_lineno;
717 reader->eof = old_eof;
c0335005
CMN
718
719 return ret;
720}
721
c0335005
CMN
722/*
723 * Read and consume a line, returning it in newly-allocated memory.
724 */
d209cc47 725static char *reader_readline(struct reader *reader, bool skip_whitespace)
c0335005
CMN
726{
727 char *line = NULL;
728 char *line_src, *line_end;
26e74c6a 729 size_t line_len;
c0335005 730
d209cc47 731 line_src = reader->read_ptr;
f2abee47 732
2c1075d6
CMN
733 if (skip_whitespace) {
734 /* Skip empty empty lines */
0f49200c 735 while (git__isspace(*line_src))
2c1075d6
CMN
736 ++line_src;
737 }
f2abee47 738
45e93ef3 739 line_end = strchr(line_src, '\n');
c0335005 740
45e93ef3 741 /* no newline at EOF */
c0335005
CMN
742 if (line_end == NULL)
743 line_end = strchr(line_src, 0);
c0335005 744
f2abee47 745 line_len = line_end - line_src;
c0335005 746
f2abee47 747 line = git__malloc(line_len + 1);
c0335005
CMN
748 if (line == NULL)
749 return NULL;
750
f2abee47 751 memcpy(line, line_src, line_len);
c0335005 752
79a34396 753 do line[line_len] = '\0';
0f49200c 754 while (line_len-- > 0 && git__isspace(line[line_len]));
c0335005
CMN
755
756 if (*line_end == '\n')
757 line_end++;
758
759 if (*line_end == '\0')
d209cc47 760 reader->eof = 1;
c0335005 761
d209cc47
CMN
762 reader->line_number++;
763 reader->read_ptr = line_end;
c0335005
CMN
764
765 return line;
766}
767
768/*
769 * Consume a line, without storing it anywhere
770 */
d209cc47 771static void reader_consume_line(struct reader *reader)
c0335005
CMN
772{
773 char *line_start, *line_end;
774
d209cc47 775 line_start = reader->read_ptr;
c0335005
CMN
776 line_end = strchr(line_start, '\n');
777 /* No newline at EOF */
778 if(line_end == NULL){
779 line_end = strchr(line_start, '\0');
780 }
781
782 if (*line_end == '\n')
783 line_end++;
784
785 if (*line_end == '\0')
d209cc47 786 reader->eof = 1;
c0335005 787
d209cc47
CMN
788 reader->line_number++;
789 reader->read_ptr = line_end;
c0335005
CMN
790}
791
765fdf4a 792GIT_INLINE(int) config_keychar(int c)
c0335005
CMN
793{
794 return isalnum(c) || c == '-';
795}
796
d209cc47 797static int parse_section_header_ext(struct reader *reader, const char *line, const char *base_name, char **section_name)
c0335005 798{
9ac581bf
CMN
799 int c, rpos;
800 char *first_quote, *last_quote;
801 git_buf buf = GIT_BUF_INIT;
c0335005
CMN
802 /*
803 * base_name is what came before the space. We should be at the
804 * first quotation mark, except for now, line isn't being kept in
805 * sync so we only really use it to calculate the length.
806 */
807
808 first_quote = strchr(line, '"');
809 last_quote = strrchr(line, '"');
810
dda708e7 811 if (last_quote - first_quote == 0) {
d209cc47 812 set_parse_error(reader, 0, "Missing closing quotation mark in section header");
dda708e7
VM
813 return -1;
814 }
c0335005 815
9ac581bf
CMN
816 git_buf_grow(&buf, strlen(base_name) + last_quote - first_quote + 2);
817 git_buf_printf(&buf, "%s.", base_name);
c0335005 818
c0335005 819 rpos = 0;
c0335005
CMN
820
821 line = first_quote;
566dd8ce 822 c = line[++rpos];
c0335005
CMN
823
824 /*
825 * At the end of each iteration, whatever is stored in c will be
826 * added to the string. In case of error, jump to out
827 */
828 do {
5892277c 829
c0335005 830 switch (c) {
2d9f5b9f 831 case 0:
d209cc47 832 set_parse_error(reader, 0, "Unexpected end-of-line in section header");
2d9f5b9f
ET
833 git_buf_free(&buf);
834 return -1;
835
c0335005 836 case '"':
566dd8ce 837 goto end_parse;
dda708e7 838
c0335005 839 case '\\':
566dd8ce 840 c = line[++rpos];
2d9f5b9f 841
566dd8ce
L
842 if (c == 0) {
843 set_parse_error(reader, rpos, "Unexpected end-of-line in section header");
dda708e7
VM
844 git_buf_free(&buf);
845 return -1;
c0335005 846 }
dda708e7 847
c0335005
CMN
848 default:
849 break;
850 }
851
66566516 852 git_buf_putc(&buf, (char)c);
566dd8ce
L
853 c = line[++rpos];
854 } while (line + rpos < last_quote);
855
856end_parse:
857 if (line[rpos] != '"' || line[rpos + 1] != ']') {
858 set_parse_error(reader, rpos, "Unexpected text after closing quotes");
859 git_buf_free(&buf);
860 return -1;
861 }
c0335005 862
dda708e7
VM
863 *section_name = git_buf_detach(&buf);
864 return 0;
c0335005
CMN
865}
866
d209cc47 867static int parse_section_header(struct reader *reader, char **section_out)
c0335005
CMN
868{
869 char *name, *name_end;
870 int name_length, c, pos;
dda708e7 871 int result;
c0335005
CMN
872 char *line;
873
d209cc47 874 line = reader_readline(reader, true);
c0335005 875 if (line == NULL)
dda708e7 876 return -1;
c0335005
CMN
877
878 /* find the end of the variable's name */
566dd8ce 879 name_end = strrchr(line, ']');
5a0659fe 880 if (name_end == NULL) {
3286c408 881 git__free(line);
d209cc47 882 set_parse_error(reader, 0, "Missing ']' in section header");
dda708e7 883 return -1;
5a0659fe 884 }
c0335005
CMN
885
886 name = (char *)git__malloc((size_t)(name_end - line) + 1);
dda708e7 887 GITERR_CHECK_ALLOC(name);
c0335005
CMN
888
889 name_length = 0;
890 pos = 0;
891
892 /* Make sure we were given a section header */
893 c = line[pos++];
dda708e7 894 assert(c == '[');
c0335005
CMN
895
896 c = line[pos++];
897
898 do {
0f49200c 899 if (git__isspace(c)){
c0335005 900 name[name_length] = '\0';
d209cc47 901 result = parse_section_header_ext(reader, line, name, section_out);
3286c408
VM
902 git__free(line);
903 git__free(name);
dda708e7 904 return result;
c0335005
CMN
905 }
906
907 if (!config_keychar(c) && c != '.') {
d209cc47 908 set_parse_error(reader, pos, "Unexpected character in header");
dda708e7 909 goto fail_parse;
c0335005
CMN
910 }
911
3a1c4310 912 name[name_length++] = (char) tolower(c);
c0335005
CMN
913
914 } while ((c = line[pos++]) != ']');
915
5a0659fe 916 if (line[pos - 1] != ']') {
d209cc47 917 set_parse_error(reader, pos, "Unexpected end of file");
dda708e7 918 goto fail_parse;
5a0659fe 919 }
f58c53ce 920
3286c408 921 git__free(line);
dda708e7
VM
922
923 name[name_length] = 0;
c0335005 924 *section_out = name;
c0335005 925
dda708e7
VM
926 return 0;
927
928fail_parse:
3286c408
VM
929 git__free(line);
930 git__free(name);
dda708e7 931 return -1;
c0335005
CMN
932}
933
d209cc47 934static int skip_bom(struct reader *reader)
c0335005 935{
7bf87ab6
RB
936 git_bom_t bom;
937 int bom_offset = git_buf_text_detect_bom(&bom,
d209cc47 938 &reader->buffer, reader->read_ptr - reader->buffer.ptr);
c0335005 939
7bf87ab6 940 if (bom == GIT_BOM_UTF8)
d209cc47 941 reader->read_ptr += bom_offset;
c0335005 942
7bf87ab6 943 /* TODO: reference implementation is pretty stupid with BoM */
c0335005 944
dda708e7 945 return 0;
c0335005
CMN
946}
947
948/*
949 (* basic types *)
950 digit = "0".."9"
951 integer = digit { digit }
952 alphabet = "a".."z" + "A" .. "Z"
953
954 section_char = alphabet | "." | "-"
955 extension_char = (* any character except newline *)
956 any_char = (* any character *)
957 variable_char = "alphabet" | "-"
958
959
960 (* actual grammar *)
961 config = { section }
962
963 section = header { definition }
964
965 header = "[" section [subsection | subsection_ext] "]"
966
967 subsection = "." section
968 subsection_ext = "\"" extension "\""
969
970 section = section_char { section_char }
971 extension = extension_char { extension_char }
972
973 definition = variable_name ["=" variable_value] "\n"
974
975 variable_name = variable_char { variable_char }
976 variable_value = string | boolean | integer
977
978 string = quoted_string | plain_string
979 quoted_string = "\"" plain_string "\""
980 plain_string = { any_char }
981
982 boolean = boolean_true | boolean_false
983 boolean_true = "yes" | "1" | "true" | "on"
984 boolean_false = "no" | "0" | "false" | "off"
985*/
986
2c1075d6 987static int strip_comments(char *line, int in_quotes)
c0335005 988{
2c1075d6 989 int quote_count = in_quotes;
c0335005
CMN
990 char *ptr;
991
992 for (ptr = line; *ptr; ++ptr) {
993 if (ptr[0] == '"' && ptr > line && ptr[-1] != '\\')
994 quote_count++;
995
996 if ((ptr[0] == ';' || ptr[0] == '#') && (quote_count % 2) == 0) {
997 ptr[0] = '\0';
998 break;
999 }
1000 }
1001
2c1075d6 1002 /* skip any space at the end */
bcad677b 1003 if (ptr > line && git__isspace(ptr[-1])) {
2c1075d6 1004 ptr--;
c0335005 1005 }
2c1075d6
CMN
1006 ptr[0] = '\0';
1007
1008 return quote_count;
c0335005
CMN
1009}
1010
d8d25acb
CMN
1011static int included_path(git_buf *out, const char *dir, const char *path)
1012{
1013 /* From the user's home */
1014 if (path[0] == '~' && path[1] == '/')
1015 return git_futils_find_global_file(out, &path[1]);
1016
1017 return git_path_join_unrooted(out, path, dir, NULL);
1018}
1019
d209cc47 1020static int config_parse(diskfile_backend *cfg_file, struct reader *reader, git_config_level_t level, int depth)
c0335005 1021{
dda708e7 1022 int c;
c0335005
CMN
1023 char *current_section = NULL;
1024 char *var_name;
1025 char *var_value;
0774d94d 1026 cvar_t *var, *existing;
fefd4551 1027 git_buf buf = GIT_BUF_INIT;
dda708e7 1028 int result = 0;
01fed0a8 1029 khiter_t pos;
53ea0513 1030 uint32_t reader_idx;
c0335005 1031
69789922
CMN
1032 if (depth >= MAX_INCLUDE_DEPTH) {
1033 giterr_set(GITERR_CONFIG, "Maximum config include depth reached");
1034 return -1;
1035 }
d8d25acb 1036
53ea0513 1037 reader_idx = git_array_size(cfg_file->readers) - 1;
8133afef 1038 /* Initialize the reading position */
d209cc47
CMN
1039 reader->read_ptr = reader->buffer.ptr;
1040 reader->eof = 0;
c0335005 1041
c7e6e958 1042 /* If the file is empty, there's nothing for us to do */
d209cc47 1043 if (*reader->read_ptr == '\0')
dda708e7 1044 return 0;
c7e6e958 1045
d209cc47 1046 skip_bom(reader);
c0335005 1047
d209cc47 1048 while (result == 0 && !reader->eof) {
c0335005 1049
d209cc47 1050 c = reader_peek(reader, SKIP_WHITESPACE);
c0335005
CMN
1051
1052 switch (c) {
c1c399cf 1053 case '\n': /* EOF when peeking, set EOF in the reader to exit the loop */
d209cc47 1054 reader->eof = 1;
c0335005
CMN
1055 break;
1056
1057 case '[': /* section header, new section begins */
3286c408 1058 git__free(current_section);
7bc9e2aa 1059 current_section = NULL;
d209cc47 1060 result = parse_section_header(reader, &current_section);
c0335005
CMN
1061 break;
1062
1063 case ';':
1064 case '#':
d209cc47 1065 reader_consume_line(reader);
c0335005
CMN
1066 break;
1067
1068 default: /* assume variable declaration */
d209cc47 1069 result = parse_variable(reader, &var_name, &var_value);
dda708e7 1070 if (result < 0)
c0335005
CMN
1071 break;
1072
3286c408 1073 var = git__malloc(sizeof(cvar_t));
dda708e7 1074 GITERR_CHECK_ALLOC(var);
128d3731 1075 memset(var, 0x0, sizeof(cvar_t));
a1abe66a 1076 var->entry = git__malloc(sizeof(git_config_entry));
1077 GITERR_CHECK_ALLOC(var->entry);
1078 memset(var->entry, 0x0, sizeof(git_config_entry));
c0335005 1079
fefd4551
CMN
1080 git__strtolower(var_name);
1081 git_buf_printf(&buf, "%s.%s", current_section, var_name);
1082 git__free(var_name);
1083
dda708e7
VM
1084 if (git_buf_oom(&buf))
1085 return -1;
c0335005 1086
a1abe66a 1087 var->entry->name = git_buf_detach(&buf);
1088 var->entry->value = var_value;
1089 var->entry->level = level;
d209cc47 1090 var->included = !!depth;
c0335005 1091
0774d94d 1092 /* Add or append the new config option */
a1abe66a 1093 pos = git_strmap_lookup_index(cfg_file->values, var->entry->name);
c2b67043 1094 if (!git_strmap_valid_index(cfg_file->values, pos)) {
a1abe66a 1095 git_strmap_insert(cfg_file->values, var->entry->name, var, result);
01fed0a8
RB
1096 if (result < 0)
1097 break;
1098 result = 0;
0774d94d 1099 } else {
c2b67043 1100 existing = git_strmap_value_at(cfg_file->values, pos);
0774d94d
CMN
1101 while (existing->next != NULL) {
1102 existing = existing->next;
1103 }
1104 existing->next = var;
1105 }
c0335005 1106
d8d25acb 1107 if (!git__strcmp(var->entry->name, "include.path")) {
19be0692 1108 struct reader *r;
d8d25acb
CMN
1109 git_buf path = GIT_BUF_INIT;
1110 char *dir;
53ea0513 1111 uint32_t index;
d8d25acb 1112
19be0692 1113 r = git_array_alloc(cfg_file->readers);
53ea0513
CMN
1114 /* The reader may have been reallocated */
1115 reader = git_array_get(cfg_file->readers, reader_idx);
19be0692 1116 memset(r, 0, sizeof(struct reader));
d8d25acb
CMN
1117 if ((result = git_path_dirname_r(&path, reader->file_path)) < 0)
1118 break;
1119
53ea0513
CMN
1120 /* We need to know out index in the array, as the next config_parse call may realloc */
1121 index = git_array_size(cfg_file->readers) - 1;
d8d25acb
CMN
1122 dir = git_buf_detach(&path);
1123 result = included_path(&path, dir, var->entry->value);
1124 git__free(dir);
1125
1126 if (result < 0)
1127 break;
1128
19be0692
CMN
1129 r->file_path = git_buf_detach(&path);
1130 git_buf_init(&r->buffer, 0);
1131 if ((result = git_futils_readbuffer_updated(&r->buffer, r->file_path, &r->file_mtime,
1132 &r->file_size, NULL)) < 0)
d8d25acb 1133 break;
d8d25acb 1134
19be0692 1135 result = config_parse(cfg_file, r, level, depth+1);
53ea0513 1136 r = git_array_get(cfg_file->readers, index);
19be0692 1137 git_buf_free(&r->buffer);
d8d25acb
CMN
1138
1139 if (result < 0)
1140 break;
1141 }
1142
c0335005
CMN
1143 break;
1144 }
1145 }
1146
3286c408 1147 git__free(current_section);
dda708e7 1148 return result;
c0335005
CMN
1149}
1150
fefd4551 1151static int write_section(git_filebuf *file, const char *key)
8bb198e6 1152{
dda708e7 1153 int result;
54fef6eb 1154 const char *dot;
fefd4551 1155 git_buf buf = GIT_BUF_INIT;
8bb198e6 1156
fefd4551 1157 /* All of this just for [section "subsection"] */
54fef6eb 1158 dot = strchr(key, '.');
fefd4551 1159 git_buf_putc(&buf, '[');
54fef6eb 1160 if (dot == NULL) {
fefd4551 1161 git_buf_puts(&buf, key);
54fef6eb 1162 } else {
5d9cfa07 1163 char *escaped;
54fef6eb 1164 git_buf_put(&buf, key, dot - key);
5d9cfa07
CMN
1165 escaped = escape_value(dot + 1);
1166 GITERR_CHECK_ALLOC(escaped);
1167 git_buf_printf(&buf, " \"%s\"", escaped);
1168 git__free(escaped);
fefd4551
CMN
1169 }
1170 git_buf_puts(&buf, "]\n");
dda708e7 1171
fefd4551 1172 if (git_buf_oom(&buf))
dda708e7 1173 return -1;
fefd4551 1174
dda708e7 1175 result = git_filebuf_write(file, git_buf_cstr(&buf), buf.size);
fefd4551 1176 git_buf_free(&buf);
8bb198e6 1177
dda708e7 1178 return result;
8bb198e6
CMN
1179}
1180
1181/*
1182 * This is pretty much the parsing, except we write out anything we don't have
1183 */
3005855f 1184static int config_write(diskfile_backend *cfg, const char *key, const regex_t *preg, const char* value)
8bb198e6 1185{
dda708e7
VM
1186 int result, c;
1187 int section_matches = 0, last_section_matched = 0, preg_replaced = 0, write_trailer = 0;
1188 const char *pre_end = NULL, *post_start = NULL, *data_start;
fefd4551 1189 char *current_section = NULL, *section, *name, *ldot;
b762e576 1190 git_filebuf file = GIT_FILEBUF_INIT;
19be0692 1191 struct reader *reader = git_array_get(cfg->readers, 0);
8bb198e6
CMN
1192
1193 /* We need to read in our own config file */
19be0692 1194 result = git_futils_readbuffer(&reader->buffer, cfg->file_path);
8bb198e6
CMN
1195
1196 /* Initialise the reading position */
dda708e7 1197 if (result == GIT_ENOTFOUND) {
19be0692
CMN
1198 reader->read_ptr = NULL;
1199 reader->eof = 1;
4e90a0a4 1200 data_start = NULL;
19be0692 1201 git_buf_clear(&reader->buffer);
dda708e7 1202 } else if (result == 0) {
19be0692
CMN
1203 reader->read_ptr = reader->buffer.ptr;
1204 reader->eof = 0;
1205 data_start = reader->read_ptr;
dda708e7
VM
1206 } else {
1207 return -1; /* OS error when reading the file */
4e90a0a4 1208 }
8bb198e6
CMN
1209
1210 /* Lock the file */
dda708e7
VM
1211 if (git_filebuf_open(&file, cfg->file_path, 0) < 0)
1212 return -1;
8bb198e6 1213
19be0692 1214 skip_bom(reader);
fefd4551
CMN
1215 ldot = strrchr(key, '.');
1216 name = ldot + 1;
1217 section = git__strndup(key, ldot - key);
8bb198e6 1218
19be0692
CMN
1219 while (!reader->eof) {
1220 c = reader_peek(reader, SKIP_WHITESPACE);
8bb198e6 1221
dda708e7 1222 if (c == '\0') { /* We've arrived at the end of the file */
8bb198e6
CMN
1223 break;
1224
dda708e7 1225 } else if (c == '[') { /* section header, new section begins */
8bb198e6
CMN
1226 /*
1227 * We set both positions to the current one in case we
1228 * need to add a variable to the end of a section. In that
1229 * case, we want both variables to point just before the
1230 * new section. If we actually want to replace it, the
1231 * default case will take care of updating them.
1232 */
19be0692 1233 pre_end = post_start = reader->read_ptr;
dda708e7
VM
1234
1235 git__free(current_section);
3df9cc59 1236 current_section = NULL;
19be0692 1237 if (parse_section_header(reader, &current_section) < 0)
dda708e7 1238 goto rewrite_fail;
8bb198e6
CMN
1239
1240 /* Keep track of when it stops matching */
1241 last_section_matched = section_matches;
fefd4551 1242 section_matches = !strcmp(current_section, section);
dda708e7 1243 }
8bb198e6 1244
dda708e7 1245 else if (c == ';' || c == '#') {
19be0692 1246 reader_consume_line(reader);
dda708e7 1247 }
8bb198e6 1248
dda708e7 1249 else {
8bb198e6
CMN
1250 /*
1251 * If the section doesn't match, but the last section did,
1252 * it means we need to add a variable (so skip the line
1253 * otherwise). If both the section and name match, we need
1254 * to overwrite the variable (so skip the line
1255 * otherwise). pre_end needs to be updated each time so we
1256 * don't loose that information, but we only need to
1257 * update post_start if we're going to use it in this
1258 * iteration.
1259 */
1260 if (!section_matches) {
1261 if (!last_section_matched) {
19be0692 1262 reader_consume_line(reader);
dda708e7 1263 continue;
8bb198e6
CMN
1264 }
1265 } else {
dda708e7
VM
1266 int has_matched = 0;
1267 char *var_name, *var_value;
b2e361cc 1268
19be0692
CMN
1269 pre_end = reader->read_ptr;
1270 if (parse_variable(reader, &var_name, &var_value) < 0)
dda708e7 1271 goto rewrite_fail;
b2e361cc 1272
dda708e7
VM
1273 /* First try to match the name of the variable */
1274 if (strcasecmp(name, var_name) == 0)
1275 has_matched = 1;
b2e361cc 1276
dda708e7
VM
1277 /* If the name matches, and we have a regex to match the
1278 * value, try to match it */
1279 if (has_matched && preg != NULL)
1280 has_matched = (regexec(preg, var_value, 0, NULL, 0) == 0);
3005855f 1281
3286c408
VM
1282 git__free(var_name);
1283 git__free(var_value);
b2e361cc 1284
dda708e7
VM
1285 /* if there is no match, keep going */
1286 if (!has_matched)
1287 continue;
b2e361cc 1288
19be0692 1289 post_start = reader->read_ptr;
8bb198e6
CMN
1290 }
1291
dda708e7
VM
1292 /* We've found the variable we wanted to change, so
1293 * write anything up to it */
1294 git_filebuf_write(&file, data_start, pre_end - data_start);
0a43d7cb 1295 preg_replaced = 1;
8bb198e6 1296
dda708e7
VM
1297 /* Then replace the variable. If the value is NULL, it
1298 * means we want to delete it, so don't write anything. */
1299 if (value != NULL) {
1300 git_filebuf_printf(&file, "\t%s = %s\n", name, value);
8bb198e6
CMN
1301 }
1302
dda708e7 1303 /* multiline variable? we need to keep reading lines to match */
3793fa9b 1304 if (preg != NULL && section_matches) {
0a43d7cb
CMN
1305 data_start = post_start;
1306 continue;
1307 }
1308
dda708e7
VM
1309 write_trailer = 1;
1310 break; /* break from the loop */
8bb198e6
CMN
1311 }
1312 }
1313
1314 /*
1315 * Being here can mean that
1316 *
1317 * 1) our section is the last one in the file and we're
1318 * adding a variable
1319 *
1320 * 2) we didn't find a section for us so we need to create it
1321 * ourselves.
1322 *
0a43d7cb
CMN
1323 * 3) we're setting a multivar with a regex, which means we
1324 * continue to search for matching values
1325 *
1326 * In the last case, if we've already replaced a value, we
1327 * want to write the rest of the file. Otherwise we need to write
1328 * out the whole file and then the new variable.
8bb198e6 1329 */
dda708e7
VM
1330 if (write_trailer) {
1331 /* Write out rest of the file */
19be0692 1332 git_filebuf_write(&file, post_start, reader->buffer.size - (post_start - data_start));
dda708e7
VM
1333 } else {
1334 if (preg_replaced) {
1335 git_filebuf_printf(&file, "\n%s", data_start);
1336 } else {
19be0692 1337 git_filebuf_write(&file, reader->buffer.ptr, reader->buffer.size);
dda708e7
VM
1338
1339 /* And now if we just need to add a variable */
1340 if (!section_matches && write_section(&file, section) < 0)
1341 goto rewrite_fail;
1342
1343 /* Sanity check: if we are here, and value is NULL, that means that somebody
1344 * touched the config file after our intial read. We should probably assert()
1345 * this, but instead we'll handle it gracefully with an error. */
1346 if (value == NULL) {
1347 giterr_set(GITERR_CONFIG,
1348 "Race condition when writing a config file (a cvar has been removed)");
1349 goto rewrite_fail;
1350 }
8bb198e6 1351
f8ede948 1352 /* If we are here, there is at least a section line */
19be0692 1353 if (reader->buffer.size > 0 && *(reader->buffer.ptr + reader->buffer.size - 1) != '\n')
f8ede948 1354 git_filebuf_write(&file, "\n", 1);
1355
dda708e7
VM
1356 git_filebuf_printf(&file, "\t%s = %s\n", name, value);
1357 }
8bb198e6
CMN
1358 }
1359
dda708e7
VM
1360 git__free(section);
1361 git__free(current_section);
8bb198e6 1362
744cc03e 1363 /* refresh stats - if this errors, then commit will error too */
19be0692 1364 (void)git_filebuf_stats(&reader->file_mtime, &reader->file_size, &file);
744cc03e 1365
dda708e7 1366 result = git_filebuf_commit(&file, GIT_CONFIG_FILE_MODE);
19be0692 1367 git_buf_free(&reader->buffer);
744cc03e 1368
dda708e7 1369 return result;
8bb198e6 1370
dda708e7 1371rewrite_fail:
fefd4551 1372 git__free(section);
3286c408 1373 git__free(current_section);
8bb198e6 1374
dda708e7 1375 git_filebuf_cleanup(&file);
19be0692 1376 git_buf_free(&reader->buffer);
dda708e7 1377 return -1;
8bb198e6
CMN
1378}
1379
49938cad
CMN
1380static const char *escapes = "ntb\"\\";
1381static const char *escaped = "\n\t\b\"\\";
1382
1383/* Escape the values to write them to the file */
1384static char *escape_value(const char *ptr)
1385{
1386 git_buf buf = GIT_BUF_INIT;
1387 size_t len;
1388 const char *esc;
1389
1390 assert(ptr);
1391
1392 len = strlen(ptr);
c57f6682
NV
1393 if (!len)
1394 return git__calloc(1, sizeof(char));
1395
49938cad
CMN
1396 git_buf_grow(&buf, len);
1397
1398 while (*ptr != '\0') {
1399 if ((esc = strchr(escaped, *ptr)) != NULL) {
1400 git_buf_putc(&buf, '\\');
1401 git_buf_putc(&buf, escapes[esc - escaped]);
1402 } else {
1403 git_buf_putc(&buf, *ptr);
1404 }
1405 ptr++;
1406 }
1407
1408 if (git_buf_oom(&buf)) {
1409 git_buf_free(&buf);
1410 return NULL;
1411 }
1412
1413 return git_buf_detach(&buf);
1414}
1415
2c1075d6
CMN
1416/* '\"' -> '"' etc */
1417static char *fixup_line(const char *ptr, int quote_count)
c0335005 1418{
2c1075d6
CMN
1419 char *str = git__malloc(strlen(ptr) + 1);
1420 char *out = str, *esc;
2c1075d6
CMN
1421
1422 if (str == NULL)
1423 return NULL;
c0335005 1424
2c1075d6
CMN
1425 while (*ptr != '\0') {
1426 if (*ptr == '"') {
1427 quote_count++;
1428 } else if (*ptr != '\\') {
1429 *out++ = *ptr;
1430 } else {
1431 /* backslash, check the next char */
1432 ptr++;
1433 /* if we're at the end, it's a multiline, so keep the backslash */
1434 if (*ptr == '\0') {
1435 *out++ = '\\';
1436 goto out;
1437 }
2c1075d6
CMN
1438 if ((esc = strchr(escapes, *ptr)) != NULL) {
1439 *out++ = escaped[esc - escapes];
1440 } else {
1441 git__free(str);
1442 giterr_set(GITERR_CONFIG, "Invalid escape at %s", ptr);
1443 return NULL;
1444 }
1445 }
1446 ptr++;
1447 }
1448
1449out:
1450 *out = '\0';
1451
1452 return str;
1453}
c0335005 1454
c0335005
CMN
1455static int is_multiline_var(const char *str)
1456{
9f35754a 1457 int count = 0;
dda708e7 1458 const char *end = str + strlen(str);
9f35754a
CMN
1459 while (end > str && end[-1] == '\\') {
1460 count++;
1461 end--;
1462 }
1463
1464 /* An odd number means last backslash wasn't escaped, so it's multiline */
1465 return (end > str) && (count & 1);
c0335005
CMN
1466}
1467
d209cc47 1468static int parse_multiline_variable(struct reader *reader, git_buf *value, int in_quotes)
c0335005 1469{
2c1075d6
CMN
1470 char *line = NULL, *proc_line = NULL;
1471 int quote_count;
c0335005
CMN
1472
1473 /* Check that the next line exists */
d209cc47 1474 line = reader_readline(reader, false);
c0335005 1475 if (line == NULL)
dda708e7 1476 return -1;
c0335005
CMN
1477
1478 /* We've reached the end of the file, there is input missing */
1479 if (line[0] == '\0') {
d209cc47 1480 set_parse_error(reader, 0, "Unexpected end of file while parsing multine var");
dda708e7
VM
1481 git__free(line);
1482 return -1;
c0335005
CMN
1483 }
1484
2c1075d6 1485 quote_count = strip_comments(line, !!in_quotes);
c0335005
CMN
1486
1487 /* If it was just a comment, pretend it didn't exist */
1488 if (line[0] == '\0') {
dda708e7 1489 git__free(line);
d209cc47 1490 return parse_multiline_variable(reader, value, quote_count);
dda708e7 1491 /* TODO: unbounded recursion. This **could** be exploitable */
c0335005
CMN
1492 }
1493
dda708e7
VM
1494 /* Drop the continuation character '\': to closely follow the UNIX
1495 * standard, this character **has** to be last one in the buf, with
1496 * no whitespace after it */
1497 assert(is_multiline_var(value->ptr));
278ce746 1498 git_buf_shorten(value, 1);
c0335005 1499
2c1075d6
CMN
1500 proc_line = fixup_line(line, in_quotes);
1501 if (proc_line == NULL) {
1502 git__free(line);
1503 return -1;
c0335005 1504 }
dda708e7 1505 /* add this line to the multiline var */
2c1075d6 1506 git_buf_puts(value, proc_line);
dda708e7 1507 git__free(line);
2c1075d6 1508 git__free(proc_line);
c0335005
CMN
1509
1510 /*
dda708e7
VM
1511 * If we need to continue reading the next line, let's just
1512 * keep putting stuff in the buffer
c0335005 1513 */
dda708e7 1514 if (is_multiline_var(value->ptr))
d209cc47 1515 return parse_multiline_variable(reader, value, quote_count);
c0335005 1516
dda708e7 1517 return 0;
c0335005
CMN
1518}
1519
d209cc47 1520static int parse_variable(struct reader *reader, char **var_name, char **var_value)
c0335005 1521{
c0335005
CMN
1522 const char *var_end = NULL;
1523 const char *value_start = NULL;
1524 char *line;
2c1075d6 1525 int quote_count;
c0335005 1526
d209cc47 1527 line = reader_readline(reader, true);
c0335005 1528 if (line == NULL)
dda708e7 1529 return -1;
c0335005 1530
2c1075d6 1531 quote_count = strip_comments(line, 0);
c0335005
CMN
1532
1533 var_end = strchr(line, '=');
1534
1535 if (var_end == NULL)
1536 var_end = strchr(line, '\0');
1537 else
1538 value_start = var_end + 1;
1539
616c1433 1540 do var_end--;
bcad677b 1541 while (var_end>line && git__isspace(*var_end));
c0335005 1542
dda708e7
VM
1543 *var_name = git__strndup(line, var_end - line + 1);
1544 GITERR_CHECK_ALLOC(*var_name);
c0335005 1545
dda708e7
VM
1546 /* If there is no value, boolean true is assumed */
1547 *var_value = NULL;
c0335005
CMN
1548
1549 /*
1550 * Now, let's try to parse the value
1551 */
1552 if (value_start != NULL) {
0f49200c 1553 while (git__isspace(value_start[0]))
c0335005
CMN
1554 value_start++;
1555
c0335005 1556 if (is_multiline_var(value_start)) {
dda708e7 1557 git_buf multi_value = GIT_BUF_INIT;
2c1075d6
CMN
1558 char *proc_line = fixup_line(value_start, 0);
1559 GITERR_CHECK_ALLOC(proc_line);
1560 git_buf_puts(&multi_value, proc_line);
2bc8fa02 1561 git__free(proc_line);
d209cc47 1562 if (parse_multiline_variable(reader, &multi_value, quote_count) < 0 || git_buf_oom(&multi_value)) {
3286c408 1563 git__free(*var_name);
dda708e7
VM
1564 git__free(line);
1565 git_buf_free(&multi_value);
1566 return -1;
9f861826 1567 }
c0335005 1568
dda708e7 1569 *var_value = git_buf_detach(&multi_value);
2c1075d6 1570
dda708e7
VM
1571 }
1572 else if (value_start[0] != '\0') {
2c1075d6 1573 *var_value = fixup_line(value_start, 0);
dda708e7 1574 GITERR_CHECK_ALLOC(*var_value);
47db054d
CMN
1575 } else { /* equals sign but missing rhs */
1576 *var_value = git__strdup("");
1577 GITERR_CHECK_ALLOC(*var_value);
c0335005 1578 }
c0335005
CMN
1579 }
1580
3286c408 1581 git__free(line);
dda708e7 1582 return 0;
c0335005 1583}