]> git.proxmox.com Git - libgit2.git/blame - src/config_file.c
Merge pull request #963 from carlosmn/remote-save-autotag
[libgit2.git] / src / config_file.c
CommitLineData
c0335005 1/*
5e0de328 2 * Copyright (C) 2009-2012 the libgit2 contributors
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"
b0b527e0 13#include "git2/config.h"
c0335005 14#include "git2/types.h"
c2b67043 15#include "strmap.h"
8bb198e6 16
c0335005 17#include <ctype.h>
5e0dc4af
CMN
18#include <sys/types.h>
19#include <regex.h>
c0335005 20
c2b67043 21GIT__USE_STRMAP;
01fed0a8 22
128d3731
VM
23typedef struct cvar_t {
24 struct cvar_t *next;
fefd4551 25 char *key; /* TODO: we might be able to get rid of this */
c0335005 26 char *value;
128d3731 27} cvar_t;
c0335005
CMN
28
29typedef struct {
128d3731
VM
30 struct cvar_t *head;
31 struct cvar_t *tail;
32} cvar_t_list;
c0335005
CMN
33
34#define CVAR_LIST_HEAD(list) ((list)->head)
35
36#define CVAR_LIST_TAIL(list) ((list)->tail)
37
38#define CVAR_LIST_NEXT(var) ((var)->next)
39
40#define CVAR_LIST_EMPTY(list) ((list)->head == NULL)
41
42#define CVAR_LIST_APPEND(list, var) do {\
43 if (CVAR_LIST_EMPTY(list)) {\
44 CVAR_LIST_HEAD(list) = CVAR_LIST_TAIL(list) = var;\
45 } else {\
46 CVAR_LIST_NEXT(CVAR_LIST_TAIL(list)) = var;\
47 CVAR_LIST_TAIL(list) = var;\
48 }\
49} while(0)
50
51#define CVAR_LIST_REMOVE_HEAD(list) do {\
52 CVAR_LIST_HEAD(list) = CVAR_LIST_NEXT(CVAR_LIST_HEAD(list));\
53} while(0)
54
55#define CVAR_LIST_REMOVE_AFTER(var) do {\
56 CVAR_LIST_NEXT(var) = CVAR_LIST_NEXT(CVAR_LIST_NEXT(var));\
57} while(0)
58
59#define CVAR_LIST_FOREACH(list, iter)\
60 for ((iter) = CVAR_LIST_HEAD(list);\
61 (iter) != NULL;\
62 (iter) = CVAR_LIST_NEXT(iter))
63
64/*
65 * Inspired by the FreeBSD functions
66 */
67#define CVAR_LIST_FOREACH_SAFE(start, iter, tmp)\
68 for ((iter) = CVAR_LIST_HEAD(vars);\
69 (iter) && (((tmp) = CVAR_LIST_NEXT(iter) || 1));\
70 (iter) = (tmp))
71
c0335005 72typedef struct {
b0b527e0 73 git_config_file parent;
c0335005 74
c2b67043 75 git_strmap *values;
c0335005
CMN
76
77 struct {
13224ea4 78 git_buf buffer;
c0335005
CMN
79 char *read_ptr;
80 int line_number;
81 int eof;
82 } reader;
83
84 char *file_path;
b0b527e0 85} diskfile_backend;
c0335005 86
b0b527e0
VM
87static int config_parse(diskfile_backend *cfg_file);
88static int parse_variable(diskfile_backend *cfg, char **var_name, char **var_value);
3005855f 89static int config_write(diskfile_backend *cfg, const char *key, const regex_t *preg, const char *value);
49938cad 90static char *escape_value(const char *ptr);
c0335005 91
dda708e7
VM
92static void set_parse_error(diskfile_backend *backend, int col, const char *error_str)
93{
94 giterr_set(GITERR_CONFIG, "Failed to parse config file: %s (in %s:%d, column %d)",
95 error_str, backend->file_path, backend->reader.line_number, col);
96}
97
128d3731 98static void cvar_free(cvar_t *var)
c0335005
CMN
99{
100 if (var == NULL)
101 return;
102
fefd4551 103 git__free(var->key);
3286c408
VM
104 git__free(var->value);
105 git__free(var);
c0335005
CMN
106}
107
fefd4551
CMN
108/* Take something the user gave us and make it nice for our hash function */
109static int normalize_name(const char *in, char **out)
c0335005 110{
fefd4551 111 char *name, *fdot, *ldot;
c0335005 112
fefd4551 113 assert(in && out);
c0335005 114
fefd4551 115 name = git__strdup(in);
dda708e7 116 GITERR_CHECK_ALLOC(name);
c0335005 117
fefd4551
CMN
118 fdot = strchr(name, '.');
119 ldot = strrchr(name, '.');
c0335005 120
fefd4551
CMN
121 if (fdot == NULL || ldot == NULL) {
122 git__free(name);
dda708e7
VM
123 giterr_set(GITERR_CONFIG,
124 "Invalid variable name: '%s'", in);
125 return -1;
6421c49a 126 }
c0335005 127
fefd4551
CMN
128 /* Downcase up to the first dot and after the last one */
129 git__strntolower(name, fdot - name);
130 git__strtolower(ldot);
131
132 *out = name;
dda708e7 133 return 0;
c0335005
CMN
134}
135
c2b67043 136static void free_vars(git_strmap *values)
3b3577c7 137{
fefd4551 138 cvar_t *var = NULL;
3b3577c7 139
fefd4551
CMN
140 if (values == NULL)
141 return;
3b3577c7 142
c2b67043 143 git_strmap_foreach_value(values, var,
01fed0a8
RB
144 while (var != NULL) {
145 cvar_t *next = CVAR_LIST_NEXT(var);
146 cvar_free(var);
147 var = next;
148 });
3b3577c7 149
c2b67043 150 git_strmap_free(values);
3b3577c7
CMN
151}
152
b0b527e0 153static int config_open(git_config_file *cfg)
c0335005 154{
dda708e7 155 int res;
b0b527e0 156 diskfile_backend *b = (diskfile_backend *)cfg;
c0335005 157
c2b67043 158 b->values = git_strmap_alloc();
dda708e7 159 GITERR_CHECK_ALLOC(b->values);
fefd4551 160
13224ea4 161 git_buf_init(&b->reader.buffer, 0);
dda708e7 162 res = git_futils_readbuffer(&b->reader.buffer, b->file_path);
9462c471 163
4e90a0a4 164 /* It's fine if the file doesn't exist */
dda708e7
VM
165 if (res == GIT_ENOTFOUND)
166 return 0;
167
168 if (res < 0 || config_parse(b) < 0) {
169 free_vars(b->values);
170 b->values = NULL;
171 git_buf_free(&b->reader.buffer);
172 return -1;
173 }
c0335005 174
13224ea4 175 git_buf_free(&b->reader.buffer);
dda708e7 176 return 0;
c0335005
CMN
177}
178
b0b527e0 179static void backend_free(git_config_file *_backend)
c0335005 180{
b0b527e0 181 diskfile_backend *backend = (diskfile_backend *)_backend;
c0335005
CMN
182
183 if (backend == NULL)
184 return;
185
3286c408 186 git__free(backend->file_path);
fefd4551 187 free_vars(backend->values);
3286c408 188 git__free(backend);
c0335005
CMN
189}
190
b3ff1dab
RB
191static int file_foreach(
192 git_config_file *backend,
193 const char *regexp,
194 int (*fn)(const char *, const char *, void *),
195 void *data)
c0335005 196{
b0b527e0 197 diskfile_backend *b = (diskfile_backend *)backend;
0c8858de 198 cvar_t *var, *next_var;
fefd4551 199 const char *key;
b3ff1dab
RB
200 regex_t regex;
201 int result = 0;
c0335005 202
bfc9ca59
RB
203 if (!b->values)
204 return 0;
c0335005 205
b3ff1dab
RB
206 if (regexp != NULL) {
207 if ((result = regcomp(&regex, regexp, REG_EXTENDED)) < 0) {
208 giterr_set_regex(&regex, result);
209 regfree(&regex);
210 return -1;
211 }
212 }
c0335005 213
b3ff1dab 214 git_strmap_foreach(b->values, key, var,
0c8858de
RB
215 for (; var != NULL; var = next_var) {
216 next_var = CVAR_LIST_NEXT(var);
217
b3ff1dab
RB
218 /* skip non-matching keys if regexp was provided */
219 if (regexp && regexec(&regex, key, 0, NULL, 0) != 0)
220 continue;
221
222 /* abort iterator on non-zero return value */
5dca2010 223 if (fn(key, var->value, data)) {
f335ecd6 224 giterr_clear();
5dca2010 225 result = GIT_EUSER;
b3ff1dab 226 goto cleanup;
5dca2010 227 }
b3ff1dab 228 }
01fed0a8 229 );
c0335005 230
b3ff1dab
RB
231cleanup:
232 if (regexp != NULL)
233 regfree(&regex);
234
235 return result;
c0335005
CMN
236}
237
b0b527e0 238static int config_set(git_config_file *cfg, const char *name, const char *value)
c0335005 239{
01fed0a8 240 cvar_t *var = NULL, *old_var;
b0b527e0 241 diskfile_backend *b = (diskfile_backend *)cfg;
49938cad 242 char *key, *esc_value = NULL;
01fed0a8 243 khiter_t pos;
49938cad 244 int rval, ret;
fefd4551 245
dda708e7
VM
246 if (normalize_name(name, &key) < 0)
247 return -1;
c0335005
CMN
248
249 /*
fefd4551
CMN
250 * Try to find it in the existing values and update it if it
251 * only has one value.
c0335005 252 */
c2b67043
RB
253 pos = git_strmap_lookup_index(b->values, key);
254 if (git_strmap_valid_index(b->values, pos)) {
255 cvar_t *existing = git_strmap_value_at(b->values, pos);
dda708e7 256 char *tmp = NULL;
fefd4551
CMN
257
258 git__free(key);
aa13bf05 259
dda708e7
VM
260 if (existing->next != NULL) {
261 giterr_set(GITERR_CONFIG, "Multivar incompatible with simple set");
262 return -1;
263 }
fefd4551 264
aa13bf05
RB
265 /* don't update if old and new values already match */
266 if ((!existing->value && !value) ||
267 (existing->value && value && !strcmp(existing->value, value)))
268 return 0;
269
dda708e7
VM
270 if (value) {
271 tmp = git__strdup(value);
272 GITERR_CHECK_ALLOC(tmp);
49938cad
CMN
273 esc_value = escape_value(value);
274 GITERR_CHECK_ALLOC(esc_value);
dda708e7 275 }
c0335005 276
3286c408 277 git__free(existing->value);
c0335005
CMN
278 existing->value = tmp;
279
49938cad
CMN
280 ret = config_write(b, existing->key, NULL, esc_value);
281
282 git__free(esc_value);
283 return ret;
29dca088
CMN
284 }
285
128d3731 286 var = git__malloc(sizeof(cvar_t));
dda708e7 287 GITERR_CHECK_ALLOC(var);
c0335005 288
128d3731 289 memset(var, 0x0, sizeof(cvar_t));
c0335005 290
fefd4551 291 var->key = key;
dda708e7 292 var->value = NULL;
c0335005 293
dda708e7
VM
294 if (value) {
295 var->value = git__strdup(value);
296 GITERR_CHECK_ALLOC(var->value);
49938cad
CMN
297 esc_value = escape_value(value);
298 GITERR_CHECK_ALLOC(esc_value);
c0335005
CMN
299 }
300
49938cad
CMN
301 if (config_write(b, key, NULL, esc_value) < 0) {
302 git__free(esc_value);
3df9cc59
CMN
303 cvar_free(var);
304 return -1;
305 }
306
49938cad 307 git__free(esc_value);
c2b67043 308 git_strmap_insert2(b->values, key, var, old_var, rval);
01fed0a8 309 if (rval < 0)
dda708e7 310 return -1;
01fed0a8
RB
311 if (old_var != NULL)
312 cvar_free(old_var);
c0335005 313
dda708e7 314 return 0;
c0335005
CMN
315}
316
317/*
318 * Internal function that actually gets the value in string form
319 */
b0b527e0 320static int config_get(git_config_file *cfg, const char *name, const char **out)
c0335005 321{
b0b527e0 322 diskfile_backend *b = (diskfile_backend *)cfg;
fefd4551 323 char *key;
01fed0a8 324 khiter_t pos;
c0335005 325
dda708e7
VM
326 if (normalize_name(name, &key) < 0)
327 return -1;
fefd4551 328
c2b67043 329 pos = git_strmap_lookup_index(b->values, key);
fefd4551 330 git__free(key);
c0335005 331
dda708e7 332 /* no error message; the config system will write one */
c2b67043 333 if (!git_strmap_valid_index(b->values, pos))
dda708e7 334 return GIT_ENOTFOUND;
c0335005 335
c2b67043 336 *out = ((cvar_t *)git_strmap_value_at(b->values, pos))->value;
7b2b4adf 337
dda708e7 338 return 0;
c0335005
CMN
339}
340
dda708e7
VM
341static int config_get_multivar(
342 git_config_file *cfg,
343 const char *name,
344 const char *regex_str,
345 int (*fn)(const char *, void *),
346 void *data)
5e0dc4af
CMN
347{
348 cvar_t *var;
5e0dc4af
CMN
349 diskfile_backend *b = (diskfile_backend *)cfg;
350 char *key;
01fed0a8 351 khiter_t pos;
5e0dc4af 352
dda708e7
VM
353 if (normalize_name(name, &key) < 0)
354 return -1;
5e0dc4af 355
c2b67043 356 pos = git_strmap_lookup_index(b->values, key);
5e0dc4af
CMN
357 git__free(key);
358
c2b67043 359 if (!git_strmap_valid_index(b->values, pos))
dda708e7 360 return GIT_ENOTFOUND;
5e0dc4af 361
c2b67043 362 var = git_strmap_value_at(b->values, pos);
5e0dc4af 363
dda708e7
VM
364 if (regex_str != NULL) {
365 regex_t regex;
366 int result;
5e0dc4af 367
dda708e7
VM
368 /* regex matching; build the regex */
369 result = regcomp(&regex, regex_str, REG_EXTENDED);
370 if (result < 0) {
371 giterr_set_regex(&regex, result);
b3ff1dab 372 regfree(&regex);
dda708e7 373 return -1;
5e0dc4af
CMN
374 }
375
dda708e7
VM
376 /* and throw the callback only on the variables that
377 * match the regex */
378 do {
379 if (regexec(&regex, var->value, 0, NULL, 0) == 0) {
380 /* early termination by the user is not an error;
381 * just break and return successfully */
382 if (fn(var->value, data) < 0)
383 break;
384 }
5e0dc4af 385
dda708e7
VM
386 var = var->next;
387 } while (var != NULL);
8e8b6b01 388 regfree(&regex);
dda708e7
VM
389 } else {
390 /* no regex; go through all the variables */
391 do {
392 /* early termination by the user is not an error;
393 * just break and return successfully */
394 if (fn(var->value, data) < 0)
395 break;
396
397 var = var->next;
398 } while (var != NULL);
399 }
5e0dc4af 400
dda708e7 401 return 0;
5e0dc4af
CMN
402}
403
01fed0a8
RB
404static int config_set_multivar(
405 git_config_file *cfg, const char *name, const char *regexp, const char *value)
3005855f 406{
dda708e7 407 int replaced = 0;
0a43d7cb 408 cvar_t *var, *newvar;
3005855f
CMN
409 diskfile_backend *b = (diskfile_backend *)cfg;
410 char *key;
411 regex_t preg;
dda708e7 412 int result;
01fed0a8 413 khiter_t pos;
3005855f 414
dda708e7 415 assert(regexp);
3005855f 416
dda708e7
VM
417 if (normalize_name(name, &key) < 0)
418 return -1;
3005855f 419
c2b67043
RB
420 pos = git_strmap_lookup_index(b->values, key);
421 if (!git_strmap_valid_index(b->values, pos)) {
01fed0a8 422 git__free(key);
dda708e7 423 return GIT_ENOTFOUND;
0a43d7cb 424 }
3005855f 425
c2b67043 426 var = git_strmap_value_at(b->values, pos);
3005855f 427
dda708e7
VM
428 result = regcomp(&preg, regexp, REG_EXTENDED);
429 if (result < 0) {
2bc8fa02 430 git__free(key);
dda708e7 431 giterr_set_regex(&preg, result);
b3ff1dab 432 regfree(&preg);
dda708e7 433 return -1;
0a43d7cb 434 }
3005855f 435
dda708e7
VM
436 for (;;) {
437 if (regexec(&preg, var->value, 0, NULL, 0) == 0) {
0a43d7cb 438 char *tmp = git__strdup(value);
dda708e7 439 GITERR_CHECK_ALLOC(tmp);
3005855f 440
2bc8fa02 441 git__free(var->value);
0a43d7cb
CMN
442 var->value = tmp;
443 replaced = 1;
444 }
445
dda708e7 446 if (var->next == NULL)
0a43d7cb 447 break;
dda708e7
VM
448
449 var = var->next;
450 }
0a43d7cb
CMN
451
452 /* If we've reached the end of the variables and we haven't found it yet, we need to append it */
453 if (!replaced) {
454 newvar = git__malloc(sizeof(cvar_t));
dda708e7 455 GITERR_CHECK_ALLOC(newvar);
3005855f
CMN
456
457 memset(newvar, 0x0, sizeof(cvar_t));
dda708e7 458
3005855f 459 newvar->key = git__strdup(var->key);
dda708e7
VM
460 GITERR_CHECK_ALLOC(newvar->key);
461
3005855f 462 newvar->value = git__strdup(value);
dda708e7 463 GITERR_CHECK_ALLOC(newvar->value);
3005855f 464
3005855f 465 var->next = newvar;
3005855f
CMN
466 }
467
dda708e7 468 result = config_write(b, key, &preg, value);
3005855f 469
2bc8fa02 470 git__free(key);
3005855f 471 regfree(&preg);
dda708e7
VM
472
473 return result;
3005855f
CMN
474}
475
80a665aa
CMN
476static int config_delete(git_config_file *cfg, const char *name)
477{
dda708e7 478 cvar_t *var;
80a665aa 479 diskfile_backend *b = (diskfile_backend *)cfg;
fefd4551 480 char *key;
dda708e7 481 int result;
01fed0a8 482 khiter_t pos;
fefd4551 483
dda708e7
VM
484 if (normalize_name(name, &key) < 0)
485 return -1;
fefd4551 486
c2b67043 487 pos = git_strmap_lookup_index(b->values, key);
2bc8fa02 488 git__free(key);
fefd4551 489
1d4dcc4b
CMN
490 if (!git_strmap_valid_index(b->values, pos)) {
491 giterr_set(GITERR_CONFIG, "Could not find key '%s' to delete", name);
dda708e7 492 return GIT_ENOTFOUND;
1d4dcc4b 493 }
fefd4551 494
c2b67043 495 var = git_strmap_value_at(b->values, pos);
80a665aa 496
dda708e7
VM
497 if (var->next != NULL) {
498 giterr_set(GITERR_CONFIG, "Cannot delete multivar with a single delete");
499 return -1;
500 }
80a665aa 501
c2b67043 502 git_strmap_delete_at(b->values, pos);
fefd4551 503
dda708e7 504 result = config_write(b, var->key, NULL, NULL);
fefd4551 505
dda708e7
VM
506 cvar_free(var);
507 return result;
80a665aa
CMN
508}
509
b0b527e0 510int git_config_file__ondisk(git_config_file **out, const char *path)
c0335005 511{
b0b527e0 512 diskfile_backend *backend;
c0335005 513
b0b527e0 514 backend = git__malloc(sizeof(diskfile_backend));
dda708e7 515 GITERR_CHECK_ALLOC(backend);
c0335005 516
b0b527e0 517 memset(backend, 0x0, sizeof(diskfile_backend));
c0335005
CMN
518
519 backend->file_path = git__strdup(path);
dda708e7 520 GITERR_CHECK_ALLOC(backend->file_path);
c0335005
CMN
521
522 backend->parent.open = config_open;
523 backend->parent.get = config_get;
5e0dc4af 524 backend->parent.get_multivar = config_get_multivar;
c0335005 525 backend->parent.set = config_set;
3005855f 526 backend->parent.set_multivar = config_set_multivar;
9dd4c3e8 527 backend->parent.del = config_delete;
c0335005
CMN
528 backend->parent.foreach = file_foreach;
529 backend->parent.free = backend_free;
530
b0b527e0 531 *out = (git_config_file *)backend;
c0335005 532
dda708e7 533 return 0;
c0335005
CMN
534}
535
b0b527e0 536static int cfg_getchar_raw(diskfile_backend *cfg)
c0335005
CMN
537{
538 int c;
539
540 c = *cfg->reader.read_ptr++;
541
542 /*
543 Win 32 line breaks: if we find a \r\n sequence,
544 return only the \n as a newline
545 */
546 if (c == '\r' && *cfg->reader.read_ptr == '\n') {
547 cfg->reader.read_ptr++;
548 c = '\n';
549 }
550
551 if (c == '\n')
552 cfg->reader.line_number++;
553
554 if (c == 0) {
555 cfg->reader.eof = 1;
556 c = '\n';
557 }
558
559 return c;
560}
561
562#define SKIP_WHITESPACE (1 << 1)
563#define SKIP_COMMENTS (1 << 2)
564
b0b527e0 565static int cfg_getchar(diskfile_backend *cfg_file, int flags)
c0335005
CMN
566{
567 const int skip_whitespace = (flags & SKIP_WHITESPACE);
568 const int skip_comments = (flags & SKIP_COMMENTS);
569 int c;
570
571 assert(cfg_file->reader.read_ptr);
572
573 do c = cfg_getchar_raw(cfg_file);
0f49200c 574 while (skip_whitespace && git__isspace(c) &&
c1c399cf 575 !cfg_file->reader.eof);
c0335005
CMN
576
577 if (skip_comments && (c == '#' || c == ';')) {
578 do c = cfg_getchar_raw(cfg_file);
579 while (c != '\n');
580 }
581
582 return c;
583}
584
585/*
586 * Read the next char, but don't move the reading pointer.
587 */
b0b527e0 588static int cfg_peek(diskfile_backend *cfg, int flags)
c0335005
CMN
589{
590 void *old_read_ptr;
591 int old_lineno, old_eof;
592 int ret;
593
594 assert(cfg->reader.read_ptr);
595
596 old_read_ptr = cfg->reader.read_ptr;
597 old_lineno = cfg->reader.line_number;
598 old_eof = cfg->reader.eof;
599
600 ret = cfg_getchar(cfg, flags);
601
602 cfg->reader.read_ptr = old_read_ptr;
603 cfg->reader.line_number = old_lineno;
604 cfg->reader.eof = old_eof;
605
606 return ret;
607}
608
c0335005
CMN
609/*
610 * Read and consume a line, returning it in newly-allocated memory.
611 */
2c1075d6 612static char *cfg_readline(diskfile_backend *cfg, bool skip_whitespace)
c0335005
CMN
613{
614 char *line = NULL;
615 char *line_src, *line_end;
26e74c6a 616 size_t line_len;
c0335005
CMN
617
618 line_src = cfg->reader.read_ptr;
f2abee47 619
2c1075d6
CMN
620 if (skip_whitespace) {
621 /* Skip empty empty lines */
0f49200c 622 while (git__isspace(*line_src))
2c1075d6
CMN
623 ++line_src;
624 }
f2abee47 625
45e93ef3 626 line_end = strchr(line_src, '\n');
c0335005 627
45e93ef3 628 /* no newline at EOF */
c0335005
CMN
629 if (line_end == NULL)
630 line_end = strchr(line_src, 0);
c0335005 631
f2abee47 632 line_len = line_end - line_src;
c0335005 633
f2abee47 634 line = git__malloc(line_len + 1);
c0335005
CMN
635 if (line == NULL)
636 return NULL;
637
f2abee47 638 memcpy(line, line_src, line_len);
c0335005 639
79a34396 640 do line[line_len] = '\0';
0f49200c 641 while (line_len-- > 0 && git__isspace(line[line_len]));
c0335005
CMN
642
643 if (*line_end == '\n')
644 line_end++;
645
646 if (*line_end == '\0')
647 cfg->reader.eof = 1;
648
649 cfg->reader.line_number++;
650 cfg->reader.read_ptr = line_end;
651
652 return line;
653}
654
655/*
656 * Consume a line, without storing it anywhere
657 */
d568d585 658static void cfg_consume_line(diskfile_backend *cfg)
c0335005
CMN
659{
660 char *line_start, *line_end;
661
662 line_start = cfg->reader.read_ptr;
663 line_end = strchr(line_start, '\n');
664 /* No newline at EOF */
665 if(line_end == NULL){
666 line_end = strchr(line_start, '\0');
667 }
668
669 if (*line_end == '\n')
670 line_end++;
671
672 if (*line_end == '\0')
673 cfg->reader.eof = 1;
674
675 cfg->reader.line_number++;
676 cfg->reader.read_ptr = line_end;
677}
678
765fdf4a 679GIT_INLINE(int) config_keychar(int c)
c0335005
CMN
680{
681 return isalnum(c) || c == '-';
682}
683
dda708e7 684static int parse_section_header_ext(diskfile_backend *cfg, const char *line, const char *base_name, char **section_name)
c0335005 685{
9ac581bf
CMN
686 int c, rpos;
687 char *first_quote, *last_quote;
688 git_buf buf = GIT_BUF_INIT;
c0335005
CMN
689 int quote_marks;
690 /*
691 * base_name is what came before the space. We should be at the
692 * first quotation mark, except for now, line isn't being kept in
693 * sync so we only really use it to calculate the length.
694 */
695
696 first_quote = strchr(line, '"');
697 last_quote = strrchr(line, '"');
698
dda708e7
VM
699 if (last_quote - first_quote == 0) {
700 set_parse_error(cfg, 0, "Missing closing quotation mark in section header");
701 return -1;
702 }
c0335005 703
9ac581bf
CMN
704 git_buf_grow(&buf, strlen(base_name) + last_quote - first_quote + 2);
705 git_buf_printf(&buf, "%s.", base_name);
c0335005 706
c0335005
CMN
707 rpos = 0;
708 quote_marks = 0;
709
710 line = first_quote;
711 c = line[rpos++];
712
713 /*
714 * At the end of each iteration, whatever is stored in c will be
715 * added to the string. In case of error, jump to out
716 */
717 do {
5892277c 718 if (quote_marks == 2) {
dda708e7
VM
719 set_parse_error(cfg, rpos, "Unexpected text after closing quotes");
720 git_buf_free(&buf);
721 return -1;
5892277c
CMN
722 }
723
c0335005
CMN
724 switch (c) {
725 case '"':
5892277c 726 ++quote_marks;
9ac581bf 727 continue;
dda708e7 728
c0335005
CMN
729 case '\\':
730 c = line[rpos++];
dda708e7 731
c0335005
CMN
732 switch (c) {
733 case '"':
734 case '\\':
735 break;
dda708e7 736
c0335005 737 default:
dda708e7
VM
738 set_parse_error(cfg, rpos, "Unsupported escape sequence");
739 git_buf_free(&buf);
740 return -1;
c0335005 741 }
dda708e7 742
c0335005
CMN
743 default:
744 break;
745 }
746
9ac581bf 747 git_buf_putc(&buf, c);
c0335005
CMN
748 } while ((c = line[rpos++]) != ']');
749
dda708e7
VM
750 *section_name = git_buf_detach(&buf);
751 return 0;
c0335005
CMN
752}
753
b0b527e0 754static int parse_section_header(diskfile_backend *cfg, char **section_out)
c0335005
CMN
755{
756 char *name, *name_end;
757 int name_length, c, pos;
dda708e7 758 int result;
c0335005
CMN
759 char *line;
760
2c1075d6 761 line = cfg_readline(cfg, true);
c0335005 762 if (line == NULL)
dda708e7 763 return -1;
c0335005
CMN
764
765 /* find the end of the variable's name */
766 name_end = strchr(line, ']');
5a0659fe 767 if (name_end == NULL) {
3286c408 768 git__free(line);
dda708e7
VM
769 set_parse_error(cfg, 0, "Missing ']' in section header");
770 return -1;
5a0659fe 771 }
c0335005
CMN
772
773 name = (char *)git__malloc((size_t)(name_end - line) + 1);
dda708e7 774 GITERR_CHECK_ALLOC(name);
c0335005
CMN
775
776 name_length = 0;
777 pos = 0;
778
779 /* Make sure we were given a section header */
780 c = line[pos++];
dda708e7 781 assert(c == '[');
c0335005
CMN
782
783 c = line[pos++];
784
785 do {
0f49200c 786 if (git__isspace(c)){
c0335005 787 name[name_length] = '\0';
dda708e7 788 result = parse_section_header_ext(cfg, line, name, section_out);
3286c408
VM
789 git__free(line);
790 git__free(name);
dda708e7 791 return result;
c0335005
CMN
792 }
793
794 if (!config_keychar(c) && c != '.') {
dda708e7
VM
795 set_parse_error(cfg, pos, "Unexpected character in header");
796 goto fail_parse;
c0335005
CMN
797 }
798
3a1c4310 799 name[name_length++] = (char) tolower(c);
c0335005
CMN
800
801 } while ((c = line[pos++]) != ']');
802
5a0659fe 803 if (line[pos - 1] != ']') {
dda708e7
VM
804 set_parse_error(cfg, pos, "Unexpected end of file");
805 goto fail_parse;
5a0659fe 806 }
f58c53ce 807
3286c408 808 git__free(line);
dda708e7
VM
809
810 name[name_length] = 0;
c0335005 811 *section_out = name;
c0335005 812
dda708e7
VM
813 return 0;
814
815fail_parse:
3286c408
VM
816 git__free(line);
817 git__free(name);
dda708e7 818 return -1;
c0335005
CMN
819}
820
b0b527e0 821static int skip_bom(diskfile_backend *cfg)
c0335005 822{
b200a813 823 static const char utf8_bom[] = { '\xef', '\xbb', '\xbf' };
c0335005 824
13224ea4 825 if (cfg->reader.buffer.size < sizeof(utf8_bom))
dda708e7 826 return 0;
4e90a0a4 827
c0335005
CMN
828 if (memcmp(cfg->reader.read_ptr, utf8_bom, sizeof(utf8_bom)) == 0)
829 cfg->reader.read_ptr += sizeof(utf8_bom);
830
87d9869f 831 /* TODO: the reference implementation does pretty stupid
c0335005
CMN
832 shit with the BoM
833 */
834
dda708e7 835 return 0;
c0335005
CMN
836}
837
838/*
839 (* basic types *)
840 digit = "0".."9"
841 integer = digit { digit }
842 alphabet = "a".."z" + "A" .. "Z"
843
844 section_char = alphabet | "." | "-"
845 extension_char = (* any character except newline *)
846 any_char = (* any character *)
847 variable_char = "alphabet" | "-"
848
849
850 (* actual grammar *)
851 config = { section }
852
853 section = header { definition }
854
855 header = "[" section [subsection | subsection_ext] "]"
856
857 subsection = "." section
858 subsection_ext = "\"" extension "\""
859
860 section = section_char { section_char }
861 extension = extension_char { extension_char }
862
863 definition = variable_name ["=" variable_value] "\n"
864
865 variable_name = variable_char { variable_char }
866 variable_value = string | boolean | integer
867
868 string = quoted_string | plain_string
869 quoted_string = "\"" plain_string "\""
870 plain_string = { any_char }
871
872 boolean = boolean_true | boolean_false
873 boolean_true = "yes" | "1" | "true" | "on"
874 boolean_false = "no" | "0" | "false" | "off"
875*/
876
2c1075d6 877static int strip_comments(char *line, int in_quotes)
c0335005 878{
2c1075d6 879 int quote_count = in_quotes;
c0335005
CMN
880 char *ptr;
881
882 for (ptr = line; *ptr; ++ptr) {
883 if (ptr[0] == '"' && ptr > line && ptr[-1] != '\\')
884 quote_count++;
885
886 if ((ptr[0] == ';' || ptr[0] == '#') && (quote_count % 2) == 0) {
887 ptr[0] = '\0';
888 break;
889 }
890 }
891
2c1075d6 892 /* skip any space at the end */
0f49200c 893 if (git__isspace(ptr[-1])) {
2c1075d6 894 ptr--;
c0335005 895 }
2c1075d6
CMN
896 ptr[0] = '\0';
897
898 return quote_count;
c0335005
CMN
899}
900
b0b527e0 901static int config_parse(diskfile_backend *cfg_file)
c0335005 902{
dda708e7 903 int c;
c0335005
CMN
904 char *current_section = NULL;
905 char *var_name;
906 char *var_value;
0774d94d 907 cvar_t *var, *existing;
fefd4551 908 git_buf buf = GIT_BUF_INIT;
dda708e7 909 int result = 0;
01fed0a8 910 khiter_t pos;
c0335005 911
8133afef 912 /* Initialize the reading position */
13224ea4 913 cfg_file->reader.read_ptr = cfg_file->reader.buffer.ptr;
c0335005
CMN
914 cfg_file->reader.eof = 0;
915
c7e6e958
CMN
916 /* If the file is empty, there's nothing for us to do */
917 if (*cfg_file->reader.read_ptr == '\0')
dda708e7 918 return 0;
c7e6e958 919
c0335005
CMN
920 skip_bom(cfg_file);
921
dda708e7 922 while (result == 0 && !cfg_file->reader.eof) {
c0335005
CMN
923
924 c = cfg_peek(cfg_file, SKIP_WHITESPACE);
925
926 switch (c) {
c1c399cf
CMN
927 case '\n': /* EOF when peeking, set EOF in the reader to exit the loop */
928 cfg_file->reader.eof = 1;
c0335005
CMN
929 break;
930
931 case '[': /* section header, new section begins */
3286c408 932 git__free(current_section);
7bc9e2aa 933 current_section = NULL;
dda708e7 934 result = parse_section_header(cfg_file, &current_section);
c0335005
CMN
935 break;
936
937 case ';':
938 case '#':
939 cfg_consume_line(cfg_file);
940 break;
941
942 default: /* assume variable declaration */
dda708e7
VM
943 result = parse_variable(cfg_file, &var_name, &var_value);
944 if (result < 0)
c0335005
CMN
945 break;
946
3286c408 947 var = git__malloc(sizeof(cvar_t));
dda708e7 948 GITERR_CHECK_ALLOC(var);
c0335005 949
128d3731 950 memset(var, 0x0, sizeof(cvar_t));
c0335005 951
fefd4551
CMN
952 git__strtolower(var_name);
953 git_buf_printf(&buf, "%s.%s", current_section, var_name);
954 git__free(var_name);
955
dda708e7
VM
956 if (git_buf_oom(&buf))
957 return -1;
c0335005 958
fefd4551 959 var->key = git_buf_detach(&buf);
c0335005 960 var->value = var_value;
c0335005 961
0774d94d 962 /* Add or append the new config option */
c2b67043
RB
963 pos = git_strmap_lookup_index(cfg_file->values, var->key);
964 if (!git_strmap_valid_index(cfg_file->values, pos)) {
965 git_strmap_insert(cfg_file->values, var->key, var, result);
01fed0a8
RB
966 if (result < 0)
967 break;
968 result = 0;
0774d94d 969 } else {
c2b67043 970 existing = git_strmap_value_at(cfg_file->values, pos);
0774d94d
CMN
971 while (existing->next != NULL) {
972 existing = existing->next;
973 }
974 existing->next = var;
975 }
c0335005
CMN
976
977 break;
978 }
979 }
980
3286c408 981 git__free(current_section);
dda708e7 982 return result;
c0335005
CMN
983}
984
fefd4551 985static int write_section(git_filebuf *file, const char *key)
8bb198e6 986{
dda708e7 987 int result;
54fef6eb 988 const char *dot;
fefd4551 989 git_buf buf = GIT_BUF_INIT;
8bb198e6 990
fefd4551 991 /* All of this just for [section "subsection"] */
54fef6eb 992 dot = strchr(key, '.');
fefd4551 993 git_buf_putc(&buf, '[');
54fef6eb 994 if (dot == NULL) {
fefd4551 995 git_buf_puts(&buf, key);
54fef6eb 996 } else {
5d9cfa07 997 char *escaped;
54fef6eb 998 git_buf_put(&buf, key, dot - key);
5d9cfa07
CMN
999 escaped = escape_value(dot + 1);
1000 GITERR_CHECK_ALLOC(escaped);
1001 git_buf_printf(&buf, " \"%s\"", escaped);
1002 git__free(escaped);
fefd4551
CMN
1003 }
1004 git_buf_puts(&buf, "]\n");
dda708e7 1005
fefd4551 1006 if (git_buf_oom(&buf))
dda708e7 1007 return -1;
fefd4551 1008
dda708e7 1009 result = git_filebuf_write(file, git_buf_cstr(&buf), buf.size);
fefd4551 1010 git_buf_free(&buf);
8bb198e6 1011
dda708e7 1012 return result;
8bb198e6
CMN
1013}
1014
1015/*
1016 * This is pretty much the parsing, except we write out anything we don't have
1017 */
3005855f 1018static int config_write(diskfile_backend *cfg, const char *key, const regex_t *preg, const char* value)
8bb198e6 1019{
dda708e7
VM
1020 int result, c;
1021 int section_matches = 0, last_section_matched = 0, preg_replaced = 0, write_trailer = 0;
1022 const char *pre_end = NULL, *post_start = NULL, *data_start;
fefd4551 1023 char *current_section = NULL, *section, *name, *ldot;
b762e576 1024 git_filebuf file = GIT_FILEBUF_INIT;
8bb198e6
CMN
1025
1026 /* We need to read in our own config file */
dda708e7 1027 result = git_futils_readbuffer(&cfg->reader.buffer, cfg->file_path);
8bb198e6
CMN
1028
1029 /* Initialise the reading position */
dda708e7 1030 if (result == GIT_ENOTFOUND) {
4e90a0a4
CMN
1031 cfg->reader.read_ptr = NULL;
1032 cfg->reader.eof = 1;
1033 data_start = NULL;
13224ea4 1034 git_buf_clear(&cfg->reader.buffer);
dda708e7 1035 } else if (result == 0) {
13224ea4 1036 cfg->reader.read_ptr = cfg->reader.buffer.ptr;
4e90a0a4
CMN
1037 cfg->reader.eof = 0;
1038 data_start = cfg->reader.read_ptr;
dda708e7
VM
1039 } else {
1040 return -1; /* OS error when reading the file */
4e90a0a4 1041 }
8bb198e6
CMN
1042
1043 /* Lock the file */
dda708e7
VM
1044 if (git_filebuf_open(&file, cfg->file_path, 0) < 0)
1045 return -1;
8bb198e6
CMN
1046
1047 skip_bom(cfg);
fefd4551
CMN
1048 ldot = strrchr(key, '.');
1049 name = ldot + 1;
1050 section = git__strndup(key, ldot - key);
8bb198e6 1051
dda708e7 1052 while (!cfg->reader.eof) {
8bb198e6
CMN
1053 c = cfg_peek(cfg, SKIP_WHITESPACE);
1054
dda708e7 1055 if (c == '\0') { /* We've arrived at the end of the file */
8bb198e6
CMN
1056 break;
1057
dda708e7 1058 } else if (c == '[') { /* section header, new section begins */
8bb198e6
CMN
1059 /*
1060 * We set both positions to the current one in case we
1061 * need to add a variable to the end of a section. In that
1062 * case, we want both variables to point just before the
1063 * new section. If we actually want to replace it, the
1064 * default case will take care of updating them.
1065 */
1066 pre_end = post_start = cfg->reader.read_ptr;
dda708e7
VM
1067
1068 git__free(current_section);
3df9cc59 1069 current_section = NULL;
dda708e7
VM
1070 if (parse_section_header(cfg, &current_section) < 0)
1071 goto rewrite_fail;
8bb198e6
CMN
1072
1073 /* Keep track of when it stops matching */
1074 last_section_matched = section_matches;
fefd4551 1075 section_matches = !strcmp(current_section, section);
dda708e7 1076 }
8bb198e6 1077
dda708e7 1078 else if (c == ';' || c == '#') {
8bb198e6 1079 cfg_consume_line(cfg);
dda708e7 1080 }
8bb198e6 1081
dda708e7 1082 else {
8bb198e6
CMN
1083 /*
1084 * If the section doesn't match, but the last section did,
1085 * it means we need to add a variable (so skip the line
1086 * otherwise). If both the section and name match, we need
1087 * to overwrite the variable (so skip the line
1088 * otherwise). pre_end needs to be updated each time so we
1089 * don't loose that information, but we only need to
1090 * update post_start if we're going to use it in this
1091 * iteration.
1092 */
1093 if (!section_matches) {
1094 if (!last_section_matched) {
1095 cfg_consume_line(cfg);
dda708e7 1096 continue;
8bb198e6
CMN
1097 }
1098 } else {
dda708e7
VM
1099 int has_matched = 0;
1100 char *var_name, *var_value;
b2e361cc 1101
8bb198e6 1102 pre_end = cfg->reader.read_ptr;
dda708e7
VM
1103 if (parse_variable(cfg, &var_name, &var_value) < 0)
1104 goto rewrite_fail;
b2e361cc 1105
dda708e7
VM
1106 /* First try to match the name of the variable */
1107 if (strcasecmp(name, var_name) == 0)
1108 has_matched = 1;
b2e361cc 1109
dda708e7
VM
1110 /* If the name matches, and we have a regex to match the
1111 * value, try to match it */
1112 if (has_matched && preg != NULL)
1113 has_matched = (regexec(preg, var_value, 0, NULL, 0) == 0);
3005855f 1114
3286c408
VM
1115 git__free(var_name);
1116 git__free(var_value);
b2e361cc 1117
dda708e7
VM
1118 /* if there is no match, keep going */
1119 if (!has_matched)
1120 continue;
b2e361cc 1121
8bb198e6
CMN
1122 post_start = cfg->reader.read_ptr;
1123 }
1124
dda708e7
VM
1125 /* We've found the variable we wanted to change, so
1126 * write anything up to it */
1127 git_filebuf_write(&file, data_start, pre_end - data_start);
0a43d7cb 1128 preg_replaced = 1;
8bb198e6 1129
dda708e7
VM
1130 /* Then replace the variable. If the value is NULL, it
1131 * means we want to delete it, so don't write anything. */
1132 if (value != NULL) {
1133 git_filebuf_printf(&file, "\t%s = %s\n", name, value);
8bb198e6
CMN
1134 }
1135
dda708e7 1136 /* multiline variable? we need to keep reading lines to match */
0a43d7cb
CMN
1137 if (preg != NULL) {
1138 data_start = post_start;
1139 continue;
1140 }
1141
dda708e7
VM
1142 write_trailer = 1;
1143 break; /* break from the loop */
8bb198e6
CMN
1144 }
1145 }
1146
1147 /*
1148 * Being here can mean that
1149 *
1150 * 1) our section is the last one in the file and we're
1151 * adding a variable
1152 *
1153 * 2) we didn't find a section for us so we need to create it
1154 * ourselves.
1155 *
0a43d7cb
CMN
1156 * 3) we're setting a multivar with a regex, which means we
1157 * continue to search for matching values
1158 *
1159 * In the last case, if we've already replaced a value, we
1160 * want to write the rest of the file. Otherwise we need to write
1161 * out the whole file and then the new variable.
8bb198e6 1162 */
dda708e7
VM
1163 if (write_trailer) {
1164 /* Write out rest of the file */
1165 git_filebuf_write(&file, post_start, cfg->reader.buffer.size - (post_start - data_start));
1166 } else {
1167 if (preg_replaced) {
1168 git_filebuf_printf(&file, "\n%s", data_start);
1169 } else {
1170 git_filebuf_write(&file, cfg->reader.buffer.ptr, cfg->reader.buffer.size);
1171
1172 /* And now if we just need to add a variable */
1173 if (!section_matches && write_section(&file, section) < 0)
1174 goto rewrite_fail;
1175
1176 /* Sanity check: if we are here, and value is NULL, that means that somebody
1177 * touched the config file after our intial read. We should probably assert()
1178 * this, but instead we'll handle it gracefully with an error. */
1179 if (value == NULL) {
1180 giterr_set(GITERR_CONFIG,
1181 "Race condition when writing a config file (a cvar has been removed)");
1182 goto rewrite_fail;
1183 }
8bb198e6 1184
dda708e7
VM
1185 git_filebuf_printf(&file, "\t%s = %s\n", name, value);
1186 }
8bb198e6
CMN
1187 }
1188
dda708e7
VM
1189 git__free(section);
1190 git__free(current_section);
8bb198e6 1191
dda708e7
VM
1192 result = git_filebuf_commit(&file, GIT_CONFIG_FILE_MODE);
1193 git_buf_free(&cfg->reader.buffer);
1194 return result;
8bb198e6 1195
dda708e7 1196rewrite_fail:
fefd4551 1197 git__free(section);
3286c408 1198 git__free(current_section);
8bb198e6 1199
dda708e7 1200 git_filebuf_cleanup(&file);
13224ea4 1201 git_buf_free(&cfg->reader.buffer);
dda708e7 1202 return -1;
8bb198e6
CMN
1203}
1204
49938cad
CMN
1205static const char *escapes = "ntb\"\\";
1206static const char *escaped = "\n\t\b\"\\";
1207
1208/* Escape the values to write them to the file */
1209static char *escape_value(const char *ptr)
1210{
1211 git_buf buf = GIT_BUF_INIT;
1212 size_t len;
1213 const char *esc;
1214
1215 assert(ptr);
1216
1217 len = strlen(ptr);
1218 git_buf_grow(&buf, len);
1219
1220 while (*ptr != '\0') {
1221 if ((esc = strchr(escaped, *ptr)) != NULL) {
1222 git_buf_putc(&buf, '\\');
1223 git_buf_putc(&buf, escapes[esc - escaped]);
1224 } else {
1225 git_buf_putc(&buf, *ptr);
1226 }
1227 ptr++;
1228 }
1229
1230 if (git_buf_oom(&buf)) {
1231 git_buf_free(&buf);
1232 return NULL;
1233 }
1234
1235 return git_buf_detach(&buf);
1236}
1237
2c1075d6
CMN
1238/* '\"' -> '"' etc */
1239static char *fixup_line(const char *ptr, int quote_count)
c0335005 1240{
2c1075d6
CMN
1241 char *str = git__malloc(strlen(ptr) + 1);
1242 char *out = str, *esc;
2c1075d6
CMN
1243
1244 if (str == NULL)
1245 return NULL;
c0335005 1246
2c1075d6
CMN
1247 while (*ptr != '\0') {
1248 if (*ptr == '"') {
1249 quote_count++;
1250 } else if (*ptr != '\\') {
1251 *out++ = *ptr;
1252 } else {
1253 /* backslash, check the next char */
1254 ptr++;
1255 /* if we're at the end, it's a multiline, so keep the backslash */
1256 if (*ptr == '\0') {
1257 *out++ = '\\';
1258 goto out;
1259 }
2c1075d6
CMN
1260 if ((esc = strchr(escapes, *ptr)) != NULL) {
1261 *out++ = escaped[esc - escapes];
1262 } else {
1263 git__free(str);
1264 giterr_set(GITERR_CONFIG, "Invalid escape at %s", ptr);
1265 return NULL;
1266 }
1267 }
1268 ptr++;
1269 }
1270
1271out:
1272 *out = '\0';
1273
1274 return str;
1275}
c0335005 1276
c0335005
CMN
1277static int is_multiline_var(const char *str)
1278{
dda708e7
VM
1279 const char *end = str + strlen(str);
1280 return (end > str) && (end[-1] == '\\');
c0335005
CMN
1281}
1282
2c1075d6 1283static int parse_multiline_variable(diskfile_backend *cfg, git_buf *value, int in_quotes)
c0335005 1284{
2c1075d6
CMN
1285 char *line = NULL, *proc_line = NULL;
1286 int quote_count;
c0335005
CMN
1287
1288 /* Check that the next line exists */
2c1075d6 1289 line = cfg_readline(cfg, false);
c0335005 1290 if (line == NULL)
dda708e7 1291 return -1;
c0335005
CMN
1292
1293 /* We've reached the end of the file, there is input missing */
1294 if (line[0] == '\0') {
dda708e7
VM
1295 set_parse_error(cfg, 0, "Unexpected end of file while parsing multine var");
1296 git__free(line);
1297 return -1;
c0335005
CMN
1298 }
1299
2c1075d6 1300 quote_count = strip_comments(line, !!in_quotes);
c0335005
CMN
1301
1302 /* If it was just a comment, pretend it didn't exist */
1303 if (line[0] == '\0') {
dda708e7 1304 git__free(line);
2c1075d6 1305 return parse_multiline_variable(cfg, value, quote_count);
dda708e7 1306 /* TODO: unbounded recursion. This **could** be exploitable */
c0335005
CMN
1307 }
1308
dda708e7
VM
1309 /* Drop the continuation character '\': to closely follow the UNIX
1310 * standard, this character **has** to be last one in the buf, with
1311 * no whitespace after it */
1312 assert(is_multiline_var(value->ptr));
fa6420f7 1313 git_buf_truncate(value, git_buf_len(value) - 1);
c0335005 1314
2c1075d6
CMN
1315 proc_line = fixup_line(line, in_quotes);
1316 if (proc_line == NULL) {
1317 git__free(line);
1318 return -1;
c0335005 1319 }
dda708e7 1320 /* add this line to the multiline var */
2c1075d6 1321 git_buf_puts(value, proc_line);
dda708e7 1322 git__free(line);
2c1075d6 1323 git__free(proc_line);
c0335005
CMN
1324
1325 /*
dda708e7
VM
1326 * If we need to continue reading the next line, let's just
1327 * keep putting stuff in the buffer
c0335005 1328 */
dda708e7 1329 if (is_multiline_var(value->ptr))
2c1075d6 1330 return parse_multiline_variable(cfg, value, quote_count);
c0335005 1331
dda708e7 1332 return 0;
c0335005
CMN
1333}
1334
b0b527e0 1335static int parse_variable(diskfile_backend *cfg, char **var_name, char **var_value)
c0335005 1336{
c0335005
CMN
1337 const char *var_end = NULL;
1338 const char *value_start = NULL;
1339 char *line;
2c1075d6 1340 int quote_count;
c0335005 1341
2c1075d6 1342 line = cfg_readline(cfg, true);
c0335005 1343 if (line == NULL)
dda708e7 1344 return -1;
c0335005 1345
2c1075d6 1346 quote_count = strip_comments(line, 0);
c0335005
CMN
1347
1348 var_end = strchr(line, '=');
1349
1350 if (var_end == NULL)
1351 var_end = strchr(line, '\0');
1352 else
1353 value_start = var_end + 1;
1354
616c1433
RB
1355 do var_end--;
1356 while (git__isspace(*var_end));
c0335005 1357
dda708e7
VM
1358 *var_name = git__strndup(line, var_end - line + 1);
1359 GITERR_CHECK_ALLOC(*var_name);
c0335005 1360
dda708e7
VM
1361 /* If there is no value, boolean true is assumed */
1362 *var_value = NULL;
c0335005
CMN
1363
1364 /*
1365 * Now, let's try to parse the value
1366 */
1367 if (value_start != NULL) {
0f49200c 1368 while (git__isspace(value_start[0]))
c0335005
CMN
1369 value_start++;
1370
c0335005 1371 if (is_multiline_var(value_start)) {
dda708e7 1372 git_buf multi_value = GIT_BUF_INIT;
2c1075d6
CMN
1373 char *proc_line = fixup_line(value_start, 0);
1374 GITERR_CHECK_ALLOC(proc_line);
1375 git_buf_puts(&multi_value, proc_line);
2bc8fa02 1376 git__free(proc_line);
2c1075d6 1377 if (parse_multiline_variable(cfg, &multi_value, quote_count) < 0 || git_buf_oom(&multi_value)) {
3286c408 1378 git__free(*var_name);
dda708e7
VM
1379 git__free(line);
1380 git_buf_free(&multi_value);
1381 return -1;
9f861826 1382 }
c0335005 1383
dda708e7 1384 *var_value = git_buf_detach(&multi_value);
2c1075d6 1385
dda708e7
VM
1386 }
1387 else if (value_start[0] != '\0') {
2c1075d6 1388 *var_value = fixup_line(value_start, 0);
dda708e7 1389 GITERR_CHECK_ALLOC(*var_value);
c0335005
CMN
1390 }
1391
c0335005
CMN
1392 }
1393
3286c408 1394 git__free(line);
dda708e7 1395 return 0;
c0335005 1396}