2 * Copyright (C) 2009-2012 the libgit2 contributors
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.
10 #include "hashtable.h"
12 #include "git2/config.h"
21 git_config_file
*file
;
25 static void config_free(git_config
*cfg
)
28 git_config_file
*file
;
29 file_internal
*internal
;
31 for(i
= 0; i
< cfg
->files
.length
; ++i
){
32 internal
= git_vector_get(&cfg
->files
, i
);
33 file
= internal
->file
;
38 git_vector_free(&cfg
->files
);
42 void git_config_free(git_config
*cfg
)
47 GIT_REFCOUNT_DEC(cfg
, config_free
);
50 static int config_backend_cmp(const void *a
, const void *b
)
52 const file_internal
*bk_a
= (const file_internal
*)(a
);
53 const file_internal
*bk_b
= (const file_internal
*)(b
);
55 return bk_b
->priority
- bk_a
->priority
;
58 int git_config_new(git_config
**out
)
62 cfg
= git__malloc(sizeof(git_config
));
66 memset(cfg
, 0x0, sizeof(git_config
));
68 if (git_vector_init(&cfg
->files
, 3, config_backend_cmp
) < 0) {
74 GIT_REFCOUNT_INC(cfg
);
78 int git_config_add_file_ondisk(git_config
*cfg
, const char *path
, int priority
)
80 git_config_file
*file
= NULL
;
83 error
= git_config_file__ondisk(&file
, path
);
84 if (error
< GIT_SUCCESS
)
87 error
= git_config_add_file(cfg
, file
, priority
);
88 if (error
< GIT_SUCCESS
) {
90 * free manually; the file is not owned by the config
91 * instance yet and will not be freed on cleanup
100 int git_config_open_ondisk(git_config
**cfg
, const char *path
)
104 error
= git_config_new(cfg
);
105 if (error
< GIT_SUCCESS
)
108 error
= git_config_add_file_ondisk(*cfg
, path
, 1);
109 if (error
< GIT_SUCCESS
)
110 git_config_free(*cfg
);
115 int git_config_add_file(git_config
*cfg
, git_config_file
*file
, int priority
)
117 file_internal
*internal
;
122 if ((error
= file
->open(file
)) < GIT_SUCCESS
)
123 return git__throw(error
, "Failed to open config file");
125 internal
= git__malloc(sizeof(file_internal
));
126 if (internal
== NULL
)
129 internal
->file
= file
;
130 internal
->priority
= priority
;
132 if (git_vector_insert(&cfg
->files
, internal
) < 0) {
137 git_vector_sort(&cfg
->files
);
138 internal
->file
->cfg
= cfg
;
144 * Loop over all the variables
147 int git_config_foreach(git_config
*cfg
, int (*fn
)(const char *, const char *, void *), void *data
)
149 int ret
= GIT_SUCCESS
;
151 file_internal
*internal
;
152 git_config_file
*file
;
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
);
163 int git_config_delete(git_config
*cfg
, const char *name
)
165 file_internal
*internal
;
166 git_config_file
*file
;
168 if (cfg
->files
.length
== 0)
169 return git__throw(GIT_EINVALIDARGS
, "Cannot delete variable; no files open in the `git_config` instance");
171 internal
= git_vector_get(&cfg
->files
, 0);
172 file
= internal
->file
;
174 return file
->del(file
, name
);
181 int git_config_set_int64(git_config
*cfg
, const char *name
, int64_t value
)
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
);
188 int git_config_set_int32(git_config
*cfg
, const char *name
, int32_t value
)
190 return git_config_set_int64(cfg
, name
, (int64_t)value
);
193 int git_config_set_bool(git_config
*cfg
, const char *name
, int value
)
195 return git_config_set_string(cfg
, name
, value
? "true" : "false");
198 int git_config_set_string(git_config
*cfg
, const char *name
, const char *value
)
200 file_internal
*internal
;
201 git_config_file
*file
;
203 if (cfg
->files
.length
== 0)
204 return git__throw(GIT_EINVALIDARGS
, "Cannot set variable value; no files open in the `git_config` instance");
206 internal
= git_vector_get(&cfg
->files
, 0);
207 file
= internal
->file
;
209 return file
->set(file
, name
, value
);
212 static int parse_bool(int *out
, const char *value
)
214 /* A missing value means true */
220 if (!strcasecmp(value
, "true") ||
221 !strcasecmp(value
, "yes") ||
222 !strcasecmp(value
, "on")) {
226 if (!strcasecmp(value
, "false") ||
227 !strcasecmp(value
, "no") ||
228 !strcasecmp(value
, "off")) {
233 return GIT_EINVALIDTYPE
;
236 static int parse_int64(int64_t *out
, const char *value
)
241 if (git__strtol64(&num
, value
, &num_end
, 0) < 0)
242 return GIT_EINVALIDTYPE
;
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
;
271 return GIT_EINVALIDTYPE
;
275 static int parse_int32(int32_t *out
, const char *value
)
280 if (parse_int64(&tmp
, value
) < 0)
281 return GIT_EINVALIDTYPE
;
283 truncate
= tmp
& 0xFFFFFFFF;
285 return GIT_EOVERFLOW
;
294 int git_config_get_mapped(git_config
*cfg
, const char *name
, git_cvar_map
*maps
, size_t map_n
, int *out
)
300 error
= git_config_get_string(cfg
, name
, &value
);
301 if (error
< GIT_SUCCESS
)
304 for (i
= 0; i
< map_n
; ++i
) {
305 git_cvar_map
*m
= maps
+ i
;
307 switch (m
->cvar_type
) {
309 case GIT_CVAR_TRUE
: {
312 if (parse_bool(&bool_val
, value
) == 0 &&
313 bool_val
== (int)m
->cvar_type
) {
322 if (parse_int32(out
, value
) == 0)
327 case GIT_CVAR_STRING
:
328 if (strcasecmp(value
, m
->str_match
) == 0) {
335 return git__throw(GIT_ENOTFOUND
,
336 "Failed to map the '%s' config variable with a valid value", name
);
339 int git_config_get_int64(git_config
*cfg
, const char *name
, int64_t *out
)
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
);
348 if (parse_int64(out
, value
) < 0)
349 return git__throw(GIT_EINVALIDTYPE
, "Failed to parse '%s' as an integer", value
);
354 int git_config_get_int32(git_config
*cfg
, const char *name
, int32_t *out
)
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
);
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
);
370 int git_config_get_bool(git_config
*cfg
, const char *name
, int *out
)
373 int error
= GIT_SUCCESS
;
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
);
379 if (parse_bool(out
, value
) == 0)
382 if (parse_int32(out
, value
) == 0) {
387 return git__throw(GIT_EINVALIDTYPE
, "Failed to parse '%s' as a boolean value", value
);
390 int git_config_get_string(git_config
*cfg
, const char *name
, const char **out
)
392 file_internal
*internal
;
393 git_config_file
*file
;
394 int error
= GIT_ENOTFOUND
;
397 if (cfg
->files
.length
== 0)
398 return git__throw(GIT_EINVALIDARGS
, "Cannot get variable value; no files open in the `git_config` instance");
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
)
407 return git__throw(error
, "Config value '%s' not found", name
);
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
)
413 file_internal
*internal
;
414 git_config_file
*file
;
415 int error
= GIT_ENOTFOUND
;
419 if (cfg
->files
.length
== 0)
420 return git__throw(GIT_EINVALIDARGS
, "Cannot get variable value; no files open in the `git_config` instance");
423 * This loop runs the "wrong" way 'round because we need to
424 * look at every value from the most general to most specific
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");
437 int git_config_set_multivar(git_config
*cfg
, const char *name
, const char *regexp
, const char *value
)
439 file_internal
*internal
;
440 git_config_file
*file
;
441 int error
= GIT_ENOTFOUND
;
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");
455 int git_config_find_global_r(git_buf
*path
)
457 return git_futils_find_global_file(path
, GIT_CONFIG_FILENAME
);
460 int git_config_find_global(char *global_config_path
)
462 git_buf path
= GIT_BUF_INIT
;
463 int error
= git_config_find_global_r(&path
);
465 if (error
== GIT_SUCCESS
) {
466 if (path
.size
> GIT_PATH_MAX
)
467 error
= git__throw(GIT_ESHORTBUFFER
, "Path is too long");
469 git_buf_copy_cstr(global_config_path
, GIT_PATH_MAX
, &path
);
477 int git_config_find_system_r(git_buf
*path
)
479 return git_futils_find_system_file(path
, GIT_CONFIG_FILENAME_SYSTEM
);
482 int git_config_find_system(char *system_config_path
)
484 git_buf path
= GIT_BUF_INIT
;
485 int error
= git_config_find_system_r(&path
);
487 if (error
== GIT_SUCCESS
) {
488 if (path
.size
> GIT_PATH_MAX
)
489 error
= git__throw(GIT_ESHORTBUFFER
, "Path is too long");
491 git_buf_copy_cstr(system_config_path
, GIT_PATH_MAX
, &path
);
499 int git_config_open_global(git_config
**out
)
502 char global_path
[GIT_PATH_MAX
];
504 if ((error
= git_config_find_global(global_path
)) < GIT_SUCCESS
)
507 return git_config_open_ondisk(out
, global_path
);