]> git.proxmox.com Git - libgit2.git/blob - src/config.c
Merge pull request #575 from libgit2/filters
[libgit2.git] / src / config.c
1 /*
2 * Copyright (C) 2009-2012 the libgit2 contributors
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 "common.h"
9 #include "fileops.h"
10 #include "hashtable.h"
11 #include "config.h"
12 #include "git2/config.h"
13 #include "vector.h"
14 #if GIT_WIN32
15 # include <windows.h>
16 #endif
17
18 #include <ctype.h>
19
20 typedef struct {
21 git_config_file *file;
22 int priority;
23 } file_internal;
24
25 static void config_free(git_config *cfg)
26 {
27 unsigned int i;
28 git_config_file *file;
29 file_internal *internal;
30
31 for(i = 0; i < cfg->files.length; ++i){
32 internal = git_vector_get(&cfg->files, i);
33 file = internal->file;
34 file->free(file);
35 git__free(internal);
36 }
37
38 git_vector_free(&cfg->files);
39 git__free(cfg);
40 }
41
42 void git_config_free(git_config *cfg)
43 {
44 if (cfg == NULL)
45 return;
46
47 GIT_REFCOUNT_DEC(cfg, config_free);
48 }
49
50 static int config_backend_cmp(const void *a, const void *b)
51 {
52 const file_internal *bk_a = (const file_internal *)(a);
53 const file_internal *bk_b = (const file_internal *)(b);
54
55 return bk_b->priority - bk_a->priority;
56 }
57
58 int git_config_new(git_config **out)
59 {
60 git_config *cfg;
61
62 cfg = git__malloc(sizeof(git_config));
63 if (cfg == NULL)
64 return GIT_ENOMEM;
65
66 memset(cfg, 0x0, sizeof(git_config));
67
68 if (git_vector_init(&cfg->files, 3, config_backend_cmp) < 0) {
69 git__free(cfg);
70 return GIT_ENOMEM;
71 }
72
73 *out = cfg;
74 GIT_REFCOUNT_INC(cfg);
75 return GIT_SUCCESS;
76 }
77
78 int git_config_add_file_ondisk(git_config *cfg, const char *path, int priority)
79 {
80 git_config_file *file = NULL;
81 int error;
82
83 error = git_config_file__ondisk(&file, path);
84 if (error < GIT_SUCCESS)
85 return error;
86
87 error = git_config_add_file(cfg, file, priority);
88 if (error < GIT_SUCCESS) {
89 /*
90 * free manually; the file is not owned by the config
91 * instance yet and will not be freed on cleanup
92 */
93 file->free(file);
94 return error;
95 }
96
97 return GIT_SUCCESS;
98 }
99
100 int git_config_open_ondisk(git_config **cfg, const char *path)
101 {
102 int error;
103
104 error = git_config_new(cfg);
105 if (error < GIT_SUCCESS)
106 return error;
107
108 error = git_config_add_file_ondisk(*cfg, path, 1);
109 if (error < GIT_SUCCESS)
110 git_config_free(*cfg);
111
112 return error;
113 }
114
115 int git_config_add_file(git_config *cfg, git_config_file *file, int priority)
116 {
117 file_internal *internal;
118 int error;
119
120 assert(cfg && file);
121
122 if ((error = file->open(file)) < GIT_SUCCESS)
123 return git__throw(error, "Failed to open config file");
124
125 internal = git__malloc(sizeof(file_internal));
126 if (internal == NULL)
127 return GIT_ENOMEM;
128
129 internal->file = file;
130 internal->priority = priority;
131
132 if (git_vector_insert(&cfg->files, internal) < 0) {
133 git__free(internal);
134 return GIT_ENOMEM;
135 }
136
137 git_vector_sort(&cfg->files);
138 internal->file->cfg = cfg;
139
140 return GIT_SUCCESS;
141 }
142
143 /*
144 * Loop over all the variables
145 */
146
147 int git_config_foreach(git_config *cfg, int (*fn)(const char *, const char *, void *), void *data)
148 {
149 int ret = GIT_SUCCESS;
150 unsigned int i;
151 file_internal *internal;
152 git_config_file *file;
153
154 for(i = 0; i < cfg->files.length && ret == 0; ++i) {
155 internal = git_vector_get(&cfg->files, i);
156 file = internal->file;
157 ret = file->foreach(file, fn, data);
158 }
159
160 return ret;
161 }
162
163 int git_config_delete(git_config *cfg, const char *name)
164 {
165 file_internal *internal;
166 git_config_file *file;
167
168 if (cfg->files.length == 0)
169 return git__throw(GIT_EINVALIDARGS, "Cannot delete variable; no files open in the `git_config` instance");
170
171 internal = git_vector_get(&cfg->files, 0);
172 file = internal->file;
173
174 return file->del(file, name);
175 }
176
177 /**************
178 * Setters
179 **************/
180
181 int git_config_set_int64(git_config *cfg, const char *name, int64_t value)
182 {
183 char str_value[32]; /* All numbers should fit in here */
184 p_snprintf(str_value, sizeof(str_value), "%" PRId64, value);
185 return git_config_set_string(cfg, name, str_value);
186 }
187
188 int git_config_set_int32(git_config *cfg, const char *name, int32_t value)
189 {
190 return git_config_set_int64(cfg, name, (int64_t)value);
191 }
192
193 int git_config_set_bool(git_config *cfg, const char *name, int value)
194 {
195 return git_config_set_string(cfg, name, value ? "true" : "false");
196 }
197
198 int git_config_set_string(git_config *cfg, const char *name, const char *value)
199 {
200 file_internal *internal;
201 git_config_file *file;
202
203 if (cfg->files.length == 0)
204 return git__throw(GIT_EINVALIDARGS, "Cannot set variable value; no files open in the `git_config` instance");
205
206 internal = git_vector_get(&cfg->files, 0);
207 file = internal->file;
208
209 return file->set(file, name, value);
210 }
211
212 static int parse_bool(int *out, const char *value)
213 {
214 /* A missing value means true */
215 if (value == NULL) {
216 *out = 1;
217 return GIT_SUCCESS;
218 }
219
220 if (!strcasecmp(value, "true") ||
221 !strcasecmp(value, "yes") ||
222 !strcasecmp(value, "on")) {
223 *out = 1;
224 return GIT_SUCCESS;
225 }
226 if (!strcasecmp(value, "false") ||
227 !strcasecmp(value, "no") ||
228 !strcasecmp(value, "off")) {
229 *out = 0;
230 return GIT_SUCCESS;
231 }
232
233 return GIT_EINVALIDTYPE;
234 }
235
236 static int parse_int64(int64_t *out, const char *value)
237 {
238 const char *num_end;
239 int64_t num;
240
241 if (git__strtol64(&num, value, &num_end, 0) < 0)
242 return GIT_EINVALIDTYPE;
243
244 switch (*num_end) {
245 case 'g':
246 case 'G':
247 num *= 1024;
248 /* fallthrough */
249
250 case 'm':
251 case 'M':
252 num *= 1024;
253 /* fallthrough */
254
255 case 'k':
256 case 'K':
257 num *= 1024;
258
259 /* check that that there are no more characters after the
260 * given modifier suffix */
261 if (num_end[1] != '\0')
262 return GIT_EINVALIDTYPE;
263
264 /* fallthrough */
265
266 case '\0':
267 *out = num;
268 return 0;
269
270 default:
271 return GIT_EINVALIDTYPE;
272 }
273 }
274
275 static int parse_int32(int32_t *out, const char *value)
276 {
277 int64_t tmp;
278 int32_t truncate;
279
280 if (parse_int64(&tmp, value) < 0)
281 return GIT_EINVALIDTYPE;
282
283 truncate = tmp & 0xFFFFFFFF;
284 if (truncate != tmp)
285 return GIT_EOVERFLOW;
286
287 *out = truncate;
288 return 0;
289 }
290
291 /***********
292 * Getters
293 ***********/
294 int git_config_get_mapped(git_config *cfg, const char *name, git_cvar_map *maps, size_t map_n, int *out)
295 {
296 size_t i;
297 const char *value;
298 int error;
299
300 error = git_config_get_string(cfg, name, &value);
301 if (error < GIT_SUCCESS)
302 return error;
303
304 for (i = 0; i < map_n; ++i) {
305 git_cvar_map *m = maps + i;
306
307 switch (m->cvar_type) {
308 case GIT_CVAR_FALSE:
309 case GIT_CVAR_TRUE: {
310 int bool_val;
311
312 if (parse_bool(&bool_val, value) == 0 &&
313 bool_val == (int)m->cvar_type) {
314 *out = m->map_value;
315 return 0;
316 }
317
318 break;
319 }
320
321 case GIT_CVAR_INT32:
322 if (parse_int32(out, value) == 0)
323 return 0;
324
325 break;
326
327 case GIT_CVAR_STRING:
328 if (strcasecmp(value, m->str_match) == 0) {
329 *out = m->map_value;
330 return 0;
331 }
332 }
333 }
334
335 return git__throw(GIT_ENOTFOUND,
336 "Failed to map the '%s' config variable with a valid value", name);
337 }
338
339 int git_config_get_int64(git_config *cfg, const char *name, int64_t *out)
340 {
341 const char *value;
342 int ret;
343
344 ret = git_config_get_string(cfg, name, &value);
345 if (ret < GIT_SUCCESS)
346 return git__rethrow(ret, "Failed to retrieve value for '%s'", name);
347
348 if (parse_int64(out, value) < 0)
349 return git__throw(GIT_EINVALIDTYPE, "Failed to parse '%s' as an integer", value);
350
351 return GIT_SUCCESS;
352 }
353
354 int git_config_get_int32(git_config *cfg, const char *name, int32_t *out)
355 {
356 const char *value;
357 int error;
358
359 error = git_config_get_string(cfg, name, &value);
360 if (error < GIT_SUCCESS)
361 return git__rethrow(error, "Failed to get value for %s", name);
362
363 error = parse_int32(out, value);
364 if (error < GIT_SUCCESS)
365 return git__throw(GIT_EINVALIDTYPE, "Failed to parse '%s' as a 32-bit integer", value);
366
367 return GIT_SUCCESS;
368 }
369
370 int git_config_get_bool(git_config *cfg, const char *name, int *out)
371 {
372 const char *value;
373 int error = GIT_SUCCESS;
374
375 error = git_config_get_string(cfg, name, &value);
376 if (error < GIT_SUCCESS)
377 return git__rethrow(error, "Failed to get value for %s", name);
378
379 if (parse_bool(out, value) == 0)
380 return GIT_SUCCESS;
381
382 if (parse_int32(out, value) == 0) {
383 *out = !!(*out);
384 return GIT_SUCCESS;
385 }
386
387 return git__throw(GIT_EINVALIDTYPE, "Failed to parse '%s' as a boolean value", value);
388 }
389
390 int git_config_get_string(git_config *cfg, const char *name, const char **out)
391 {
392 file_internal *internal;
393 git_config_file *file;
394 int error = GIT_ENOTFOUND;
395 unsigned int i;
396
397 if (cfg->files.length == 0)
398 return git__throw(GIT_EINVALIDARGS, "Cannot get variable value; no files open in the `git_config` instance");
399
400 for (i = 0; i < cfg->files.length; ++i) {
401 internal = git_vector_get(&cfg->files, i);
402 file = internal->file;
403 if ((error = file->get(file, name, out)) == GIT_SUCCESS)
404 return GIT_SUCCESS;
405 }
406
407 return git__throw(error, "Config value '%s' not found", name);
408 }
409
410 int git_config_get_multivar(git_config *cfg, const char *name, const char *regexp,
411 int (*fn)(const char *value, void *data), void *data)
412 {
413 file_internal *internal;
414 git_config_file *file;
415 int error = GIT_ENOTFOUND;
416 unsigned int i;
417
418
419 if (cfg->files.length == 0)
420 return git__throw(GIT_EINVALIDARGS, "Cannot get variable value; no files open in the `git_config` instance");
421
422 /*
423 * This loop runs the "wrong" way 'round because we need to
424 * look at every value from the most general to most specific
425 */
426 for (i = cfg->files.length; i > 0; --i) {
427 internal = git_vector_get(&cfg->files, i - 1);
428 file = internal->file;
429 error = file->get_multivar(file, name, regexp, fn, data);
430 if (error < GIT_SUCCESS && error != GIT_ENOTFOUND)
431 git__rethrow(error, "Failed to get multivar");
432 }
433
434 return GIT_SUCCESS;
435 }
436
437 int git_config_set_multivar(git_config *cfg, const char *name, const char *regexp, const char *value)
438 {
439 file_internal *internal;
440 git_config_file *file;
441 int error = GIT_ENOTFOUND;
442 unsigned int i;
443
444 for (i = cfg->files.length; i > 0; --i) {
445 internal = git_vector_get(&cfg->files, i - 1);
446 file = internal->file;
447 error = file->set_multivar(file, name, regexp, value);
448 if (error < GIT_SUCCESS && error != GIT_ENOTFOUND)
449 git__rethrow(error, "Failed to replace multivar");
450 }
451
452 return GIT_SUCCESS;
453 }
454
455 int git_config_find_global_r(git_buf *path)
456 {
457 return git_futils_find_global_file(path, GIT_CONFIG_FILENAME);
458 }
459
460 int git_config_find_global(char *global_config_path)
461 {
462 git_buf path = GIT_BUF_INIT;
463 int error = git_config_find_global_r(&path);
464
465 if (error == GIT_SUCCESS) {
466 if (path.size > GIT_PATH_MAX)
467 error = git__throw(GIT_ESHORTBUFFER, "Path is too long");
468 else
469 git_buf_copy_cstr(global_config_path, GIT_PATH_MAX, &path);
470 }
471
472 git_buf_free(&path);
473
474 return error;
475 }
476
477 int git_config_find_system_r(git_buf *path)
478 {
479 return git_futils_find_system_file(path, GIT_CONFIG_FILENAME_SYSTEM);
480 }
481
482 int git_config_find_system(char *system_config_path)
483 {
484 git_buf path = GIT_BUF_INIT;
485 int error = git_config_find_system_r(&path);
486
487 if (error == GIT_SUCCESS) {
488 if (path.size > GIT_PATH_MAX)
489 error = git__throw(GIT_ESHORTBUFFER, "Path is too long");
490 else
491 git_buf_copy_cstr(system_config_path, GIT_PATH_MAX, &path);
492 }
493
494 git_buf_free(&path);
495
496 return error;
497 }
498
499 int git_config_open_global(git_config **out)
500 {
501 int error;
502 char global_path[GIT_PATH_MAX];
503
504 if ((error = git_config_find_global(global_path)) < GIT_SUCCESS)
505 return error;
506
507 return git_config_open_ondisk(out, global_path);
508 }
509