Coverage Report

Created: 2023-11-27 07:10

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