Coverage Report

Created: 2023-11-19 06:28

/src/libgit2/src/libgit2/config_mem.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 "config_backend.h"
11
#include "config_parse.h"
12
#include "config_list.h"
13
#include "strlist.h"
14
15
typedef struct {
16
  git_config_backend parent;
17
18
  char *backend_type;
19
  char *origin_path;
20
21
  git_config_list *config_list;
22
23
  /* Configuration data in the config file format */
24
  git_str cfg;
25
26
  /* Array of key=value pairs */
27
  char **values;
28
  size_t values_len;
29
} config_memory_backend;
30
31
typedef struct {
32
  const char *backend_type;
33
  const char *origin_path;
34
  git_config_list *config_list;
35
  git_config_level_t level;
36
} config_memory_parse_data;
37
38
static int config_error_readonly(void)
39
0
{
40
0
  git_error_set(GIT_ERROR_CONFIG, "this backend is read-only");
41
0
  return -1;
42
0
}
43
44
static int read_variable_cb(
45
  git_config_parser *reader,
46
  const char *current_section,
47
  const char *var_name,
48
  const char *var_value,
49
  const char *line,
50
  size_t line_len,
51
  void *payload)
52
43.4k
{
53
43.4k
  config_memory_parse_data *parse_data = (config_memory_parse_data *) payload;
54
43.4k
  git_str buf = GIT_STR_INIT;
55
43.4k
  git_config_list_entry *entry;
56
43.4k
  const char *c;
57
43.4k
  int result;
58
59
43.4k
  GIT_UNUSED(reader);
60
43.4k
  GIT_UNUSED(line);
61
43.4k
  GIT_UNUSED(line_len);
62
63
43.4k
  if (current_section) {
64
    /* TODO: Once warnings land, we should likely warn
65
     * here. Git appears to warn in most cases if it sees
66
     * un-namespaced config options.
67
     */
68
39.3k
    git_str_puts(&buf, current_section);
69
39.3k
    git_str_putc(&buf, '.');
70
39.3k
  }
71
72
1.15M
  for (c = var_name; *c; c++)
73
1.10M
    git_str_putc(&buf, git__tolower(*c));
74
75
43.4k
  if (git_str_oom(&buf))
76
0
    return -1;
77
78
43.4k
  entry = git__calloc(1, sizeof(git_config_list_entry));
79
43.4k
  GIT_ERROR_CHECK_ALLOC(entry);
80
43.4k
  entry->base.name = git_str_detach(&buf);
81
43.4k
  entry->base.value = var_value ? git__strdup(var_value) : NULL;
82
43.4k
  entry->base.level = parse_data->level;
83
43.4k
  entry->base.include_depth = 0;
84
43.4k
  entry->base.backend_type = parse_data->backend_type;
85
43.4k
  entry->base.origin_path = parse_data->origin_path;
86
43.4k
  entry->base.free = git_config_list_entry_free;
87
43.4k
  entry->config_list = parse_data->config_list;
88
89
43.4k
  if ((result = git_config_list_append(parse_data->config_list, entry)) < 0)
90
0
    return result;
91
92
43.4k
  return result;
93
43.4k
}
94
95
static int parse_config(
96
  config_memory_backend *memory_backend,
97
  git_config_level_t level)
98
1.61k
{
99
1.61k
  git_config_parser parser = GIT_PARSE_CTX_INIT;
100
1.61k
  config_memory_parse_data parse_data;
101
1.61k
  int error;
102
103
1.61k
  if ((error = git_config_parser_init(&parser, "in-memory",
104
1.61k
    memory_backend->cfg.ptr, memory_backend->cfg.size)) < 0)
105
0
    goto out;
106
107
1.61k
  parse_data.backend_type = git_config_list_add_string(
108
1.61k
    memory_backend->config_list, memory_backend->backend_type);
109
1.61k
  parse_data.origin_path = memory_backend->origin_path ?
110
0
    git_config_list_add_string(memory_backend->config_list,
111
0
      memory_backend->origin_path) :
112
1.61k
    NULL;
113
1.61k
  parse_data.config_list = memory_backend->config_list;
114
1.61k
  parse_data.level = level;
115
116
1.61k
  if ((error = git_config_parse(&parser, NULL, read_variable_cb,
117
1.61k
    NULL, NULL, &parse_data)) < 0)
118
817
    goto out;
119
120
1.61k
out:
121
1.61k
  git_config_parser_dispose(&parser);
122
1.61k
  return error;
123
1.61k
}
124
125
static int parse_values(
126
  config_memory_backend *memory_backend,
127
  git_config_level_t level)
128
0
{
129
0
  git_config_list_entry *entry;
130
0
  const char *eql, *backend_type, *origin_path;
131
0
  size_t name_len, i;
132
133
0
  backend_type = git_config_list_add_string(
134
0
    memory_backend->config_list, memory_backend->backend_type);
135
0
  GIT_ERROR_CHECK_ALLOC(backend_type);
136
137
0
  origin_path = memory_backend->origin_path ?
138
0
    git_config_list_add_string(memory_backend->config_list,
139
0
      memory_backend->origin_path) :
140
0
    NULL;
141
142
0
  for (i = 0; i < memory_backend->values_len; i++) {
143
0
    eql = strchr(memory_backend->values[i], '=');
144
0
    name_len = eql - memory_backend->values[i];
145
146
0
    if (name_len == 0) {
147
0
      git_error_set(GIT_ERROR_CONFIG, "empty config key");
148
0
      return -1;
149
0
    }
150
151
0
    entry = git__calloc(1, sizeof(git_config_list_entry));
152
0
    GIT_ERROR_CHECK_ALLOC(entry);
153
154
0
    entry->base.name = git__strndup(memory_backend->values[i], name_len);
155
0
    GIT_ERROR_CHECK_ALLOC(entry->base.name);
156
157
0
    if (eql) {
158
0
      entry->base.value = git__strdup(eql + 1);
159
0
      GIT_ERROR_CHECK_ALLOC(entry->base.value);
160
0
    }
161
162
0
    entry->base.level = level;
163
0
    entry->base.include_depth = 0;
164
0
    entry->base.backend_type = backend_type;
165
0
    entry->base.origin_path = origin_path;
166
0
    entry->base.free = git_config_list_entry_free;
167
0
    entry->config_list = memory_backend->config_list;
168
169
0
    if (git_config_list_append(memory_backend->config_list, entry) < 0)
170
0
      return -1;
171
0
  }
172
173
0
  return 0;
174
0
}
175
176
static int config_memory_open(git_config_backend *backend, git_config_level_t level, const git_repository *repo)
177
1.61k
{
178
1.61k
  config_memory_backend *memory_backend = (config_memory_backend *) backend;
179
180
1.61k
  GIT_UNUSED(repo);
181
182
1.61k
  if (memory_backend->cfg.size > 0 &&
183
1.61k
      parse_config(memory_backend, level) < 0)
184
817
    return -1;
185
186
799
  if (memory_backend->values_len > 0 &&
187
799
      parse_values(memory_backend, level) < 0)
188
0
    return -1;
189
190
799
  return 0;
191
799
}
192
193
static int config_memory_get(git_config_backend *backend, const char *key, git_config_entry **out)
194
0
{
195
0
  config_memory_backend *memory_backend = (config_memory_backend *) backend;
196
0
  git_config_list_entry *entry;
197
0
  int error;
198
199
0
  if ((error = git_config_list_get(&entry, memory_backend->config_list, key)) != 0)
200
0
    return error;
201
202
0
  *out = &entry->base;
203
0
  return 0;
204
0
}
205
206
static int config_memory_iterator(
207
  git_config_iterator **iter,
208
  git_config_backend *backend)
209
799
{
210
799
  config_memory_backend *memory_backend = (config_memory_backend *) backend;
211
799
  git_config_list *config_list;
212
799
  int error;
213
214
799
  if ((error = git_config_list_dup(&config_list, memory_backend->config_list)) < 0)
215
0
    goto out;
216
217
799
  if ((error = git_config_list_iterator_new(iter, config_list)) < 0)
218
0
    goto out;
219
220
799
out:
221
  /* Let iterator delete duplicated config_list when it's done */
222
799
  git_config_list_free(config_list);
223
799
  return error;
224
799
}
225
226
static int config_memory_set(git_config_backend *backend, const char *name, const char *value)
227
0
{
228
0
  GIT_UNUSED(backend);
229
0
  GIT_UNUSED(name);
230
0
  GIT_UNUSED(value);
231
0
  return config_error_readonly();
232
0
}
233
234
static int config_memory_set_multivar(
235
  git_config_backend *backend, const char *name, const char *regexp, const char *value)
236
0
{
237
0
  GIT_UNUSED(backend);
238
0
  GIT_UNUSED(name);
239
0
  GIT_UNUSED(regexp);
240
0
  GIT_UNUSED(value);
241
0
  return config_error_readonly();
242
0
}
243
244
static int config_memory_delete(git_config_backend *backend, const char *name)
245
0
{
246
0
  GIT_UNUSED(backend);
247
0
  GIT_UNUSED(name);
248
0
  return config_error_readonly();
249
0
}
250
251
static int config_memory_delete_multivar(git_config_backend *backend, const char *name, const char *regexp)
252
0
{
253
0
  GIT_UNUSED(backend);
254
0
  GIT_UNUSED(name);
255
0
  GIT_UNUSED(regexp);
256
0
  return config_error_readonly();
257
0
}
258
259
static int config_memory_lock(git_config_backend *backend)
260
0
{
261
0
  GIT_UNUSED(backend);
262
0
  return config_error_readonly();
263
0
}
264
265
static int config_memory_unlock(git_config_backend *backend, int success)
266
0
{
267
0
  GIT_UNUSED(backend);
268
0
  GIT_UNUSED(success);
269
0
  return config_error_readonly();
270
0
}
271
272
static void config_memory_free(git_config_backend *_backend)
273
1.61k
{
274
1.61k
  config_memory_backend *backend = (config_memory_backend *)_backend;
275
276
1.61k
  if (backend == NULL)
277
0
    return;
278
279
1.61k
  git__free(backend->origin_path);
280
1.61k
  git__free(backend->backend_type);
281
1.61k
  git_config_list_free(backend->config_list);
282
1.61k
  git_strlist_free(backend->values, backend->values_len);
283
1.61k
  git_str_dispose(&backend->cfg);
284
1.61k
  git__free(backend);
285
1.61k
}
286
287
static config_memory_backend *config_backend_new(
288
  git_config_backend_memory_options *opts)
289
1.61k
{
290
1.61k
  config_memory_backend *backend;
291
292
1.61k
  if ((backend = git__calloc(1, sizeof(config_memory_backend))) == NULL)
293
0
    return NULL;
294
295
1.61k
  if (git_config_list_new(&backend->config_list) < 0)
296
0
    goto on_error;
297
298
1.61k
  backend->parent.version = GIT_CONFIG_BACKEND_VERSION;
299
1.61k
  backend->parent.readonly = 1;
300
1.61k
  backend->parent.open = config_memory_open;
301
1.61k
  backend->parent.get = config_memory_get;
302
1.61k
  backend->parent.set = config_memory_set;
303
1.61k
  backend->parent.set_multivar = config_memory_set_multivar;
304
1.61k
  backend->parent.del = config_memory_delete;
305
1.61k
  backend->parent.del_multivar = config_memory_delete_multivar;
306
1.61k
  backend->parent.iterator = config_memory_iterator;
307
1.61k
  backend->parent.lock = config_memory_lock;
308
1.61k
  backend->parent.unlock = config_memory_unlock;
309
1.61k
  backend->parent.snapshot = git_config_backend_snapshot;
310
1.61k
  backend->parent.free = config_memory_free;
311
312
1.61k
  backend->backend_type = git__strdup(opts && opts->backend_type ?
313
1.61k
    opts->backend_type : "in-memory");
314
315
1.61k
  if (backend->backend_type == NULL)
316
0
    goto on_error;
317
318
1.61k
  if (opts && opts->origin_path &&
319
1.61k
      (backend->origin_path = git__strdup(opts->origin_path)) == NULL)
320
0
    goto on_error;
321
322
1.61k
  return backend;
323
324
0
on_error:
325
0
  git_config_list_free(backend->config_list);
326
0
  git__free(backend->origin_path);
327
0
  git__free(backend->backend_type);
328
0
  git__free(backend);
329
0
  return NULL;
330
1.61k
}
331
332
int git_config_backend_from_string(
333
  git_config_backend **out,
334
  const char *cfg,
335
  size_t len,
336
  git_config_backend_memory_options *opts)
337
1.61k
{
338
1.61k
  config_memory_backend *backend;
339
340
1.61k
  if ((backend = config_backend_new(opts)) == NULL)
341
0
    return -1;
342
343
1.61k
  if (git_str_set(&backend->cfg, cfg, len) < 0) {
344
0
    git_config_list_free(backend->config_list);
345
0
    git__free(backend);
346
0
    return -1;
347
0
  }
348
349
1.61k
  *out = (git_config_backend *)backend;
350
1.61k
  return 0;
351
1.61k
}
352
353
int git_config_backend_from_values(
354
  git_config_backend **out,
355
  const char **values,
356
  size_t len,
357
  git_config_backend_memory_options *opts)
358
0
{
359
0
  config_memory_backend *backend;
360
361
0
  if ((backend = config_backend_new(opts)) == NULL)
362
0
    return -1;
363
364
0
  if (git_strlist_copy(&backend->values, values, len) < 0) {
365
0
    git_config_list_free(backend->config_list);
366
0
    git__free(backend);
367
0
    return -1;
368
0
  }
369
370
0
  backend->values_len = len;
371
372
0
  *out = (git_config_backend *)backend;
373
0
  return 0;
374
0
}