]> git.proxmox.com Git - libgit2.git/blob - src/config.c
Merge pull request #436 from libgit2/config-int-types
[libgit2.git] / src / config.c
1 /*
2 * Copyright (C) 2009-2011 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 void git_config_free(git_config *cfg)
26 {
27 unsigned int i;
28 git_config_file *file;
29 file_internal *internal;
30
31 if (cfg == NULL)
32 return;
33
34 for(i = 0; i < cfg->files.length; ++i){
35 internal = git_vector_get(&cfg->files, i);
36 file = internal->file;
37 file->free(file);
38 free(internal);
39 }
40
41 git_vector_free(&cfg->files);
42 free(cfg);
43 }
44
45 static int config_backend_cmp(const void *a, const void *b)
46 {
47 const file_internal *bk_a = (const file_internal *)(a);
48 const file_internal *bk_b = (const file_internal *)(b);
49
50 return bk_b->priority - bk_a->priority;
51 }
52
53 int git_config_new(git_config **out)
54 {
55 git_config *cfg;
56
57 cfg = git__malloc(sizeof(git_config));
58 if (cfg == NULL)
59 return GIT_ENOMEM;
60
61 memset(cfg, 0x0, sizeof(git_config));
62
63 if (git_vector_init(&cfg->files, 3, config_backend_cmp) < 0) {
64 free(cfg);
65 return GIT_ENOMEM;
66 }
67
68 *out = cfg;
69
70 return GIT_SUCCESS;
71 }
72
73 int git_config_add_file_ondisk(git_config *cfg, const char *path, int priority)
74 {
75 git_config_file *file = NULL;
76 int error;
77
78 error = git_config_file__ondisk(&file, path);
79 if (error < GIT_SUCCESS)
80 return error;
81
82 error = git_config_add_file(cfg, file, priority);
83 if (error < GIT_SUCCESS) {
84 /*
85 * free manually; the file is not owned by the config
86 * instance yet and will not be freed on cleanup
87 */
88 file->free(file);
89 return error;
90 }
91
92 return GIT_SUCCESS;
93 }
94
95 int git_config_open_ondisk(git_config **cfg, const char *path)
96 {
97 int error;
98
99 error = git_config_new(cfg);
100 if (error < GIT_SUCCESS)
101 return error;
102
103 error = git_config_add_file_ondisk(*cfg, path, 1);
104 if (error < GIT_SUCCESS)
105 git_config_free(*cfg);
106
107 return error;
108 }
109
110 int git_config_add_file(git_config *cfg, git_config_file *file, int priority)
111 {
112 file_internal *internal;
113 int error;
114
115 assert(cfg && file);
116
117 if ((error = file->open(file)) < GIT_SUCCESS)
118 return git__rethrow(error, "Failed to open config file");
119
120 internal = git__malloc(sizeof(file_internal));
121 if (internal == NULL)
122 return GIT_ENOMEM;
123
124 internal->file = file;
125 internal->priority = priority;
126
127 if (git_vector_insert(&cfg->files, internal) < 0) {
128 free(internal);
129 return GIT_ENOMEM;
130 }
131
132 git_vector_sort(&cfg->files);
133 internal->file->cfg = cfg;
134
135 return GIT_SUCCESS;
136 }
137
138 /*
139 * Loop over all the variables
140 */
141
142 int git_config_foreach(git_config *cfg, int (*fn)(const char *, const char *, void *), void *data)
143 {
144 int ret = GIT_SUCCESS;
145 unsigned int i;
146 file_internal *internal;
147 git_config_file *file;
148
149 for(i = 0; i < cfg->files.length && ret == 0; ++i) {
150 internal = git_vector_get(&cfg->files, i);
151 file = internal->file;
152 ret = file->foreach(file, fn, data);
153 }
154
155 return ret;
156 }
157
158 int git_config_delete(git_config *cfg, const char *name)
159 {
160 return git_config_set_string(cfg, name, NULL);
161 }
162
163 /**************
164 * Setters
165 **************/
166
167 int git_config_set_int64(git_config *cfg, const char *name, int64_t value)
168 {
169 char str_value[32]; /* All numbers should fit in here */
170 p_snprintf(str_value, sizeof(str_value), "%" PRId64, value);
171 return git_config_set_string(cfg, name, str_value);
172 }
173
174 int git_config_set_int32(git_config *cfg, const char *name, int32_t value)
175 {
176 return git_config_set_int64(cfg, name, (int64_t)value);
177 }
178
179 int git_config_set_bool(git_config *cfg, const char *name, int value)
180 {
181 return git_config_set_string(cfg, name, value ? "true" : "false");
182 }
183
184 int git_config_set_string(git_config *cfg, const char *name, const char *value)
185 {
186 file_internal *internal;
187 git_config_file *file;
188
189 if (cfg->files.length == 0)
190 return git__throw(GIT_EINVALIDARGS, "Cannot set variable value; no files open in the `git_config` instance");
191
192 internal = git_vector_get(&cfg->files, 0);
193 file = internal->file;
194
195 return file->set(file, name, value);
196 }
197
198 /***********
199 * Getters
200 ***********/
201
202 int git_config_get_int64(git_config *cfg, const char *name, int64_t *out)
203 {
204 const char *value, *num_end;
205 int ret;
206 int64_t num;
207
208 ret = git_config_get_string(cfg, name, &value);
209 if (ret < GIT_SUCCESS)
210 return git__rethrow(ret, "Failed to retrieve value for '%s'", name);
211
212 ret = git__strtol64(&num, value, &num_end, 0);
213 if (ret < GIT_SUCCESS)
214 return git__rethrow(ret, "Failed to convert value for '%s'", name);
215
216 switch (*num_end) {
217 case 'g':
218 case 'G':
219 num *= 1024;
220 /* fallthrough */
221
222 case 'm':
223 case 'M':
224 num *= 1024;
225 /* fallthrough */
226
227 case 'k':
228 case 'K':
229 num *= 1024;
230
231 /* check that that there are no more characters after the
232 * given modifier suffix */
233 if (num_end[1] != '\0')
234 return git__throw(GIT_EINVALIDTYPE,
235 "Failed to get value for '%s'. Invalid type suffix", name);
236
237 /* fallthrough */
238
239 case '\0':
240 *out = num;
241 return GIT_SUCCESS;
242
243 default:
244 return git__throw(GIT_EINVALIDTYPE,
245 "Failed to get value for '%s'. Value is of invalid type", name);
246 }
247 }
248
249 int git_config_get_int32(git_config *cfg, const char *name, int32_t *out)
250 {
251 int64_t tmp_long;
252 int32_t tmp_int;
253 int ret;
254
255 ret = git_config_get_int64(cfg, name, &tmp_long);
256 if (ret < GIT_SUCCESS)
257 return git__rethrow(ret, "Failed to convert value for '%s'", name);
258
259 tmp_int = tmp_long & 0xFFFFFFFF;
260 if (tmp_int != tmp_long)
261 return git__throw(GIT_EOVERFLOW, "Value for '%s' is too large", name);
262
263 *out = tmp_int;
264
265 return ret;
266 }
267
268 int git_config_get_bool(git_config *cfg, const char *name, int *out)
269 {
270 const char *value;
271 int error = GIT_SUCCESS;
272
273 error = git_config_get_string(cfg, name, &value);
274 if (error < GIT_SUCCESS)
275 return git__rethrow(error, "Failed to get value for %s", name);
276
277 /* A missing value means true */
278 if (value == NULL) {
279 *out = 1;
280 return GIT_SUCCESS;
281 }
282
283 if (!strcasecmp(value, "true") ||
284 !strcasecmp(value, "yes") ||
285 !strcasecmp(value, "on")) {
286 *out = 1;
287 return GIT_SUCCESS;
288 }
289 if (!strcasecmp(value, "false") ||
290 !strcasecmp(value, "no") ||
291 !strcasecmp(value, "off")) {
292 *out = 0;
293 return GIT_SUCCESS;
294 }
295
296 /* Try to parse it as an integer */
297 error = git_config_get_int32(cfg, name, out);
298 if (error == GIT_SUCCESS)
299 *out = !!(*out);
300
301 if (error < GIT_SUCCESS)
302 return git__rethrow(error, "Failed to get value for %s", name);
303 return error;
304 }
305
306 int git_config_get_string(git_config *cfg, const char *name, const char **out)
307 {
308 file_internal *internal;
309 git_config_file *file;
310 int error = GIT_ENOTFOUND;
311 unsigned int i;
312
313 if (cfg->files.length == 0)
314 return git__throw(GIT_EINVALIDARGS, "Cannot get variable value; no files open in the `git_config` instance");
315
316 for (i = 0; i < cfg->files.length; ++i) {
317 internal = git_vector_get(&cfg->files, i);
318 file = internal->file;
319 if ((error = file->get(file, name, out)) == GIT_SUCCESS)
320 return GIT_SUCCESS;
321 }
322
323 return git__throw(error, "Config value '%s' not found", name);
324 }
325
326 int git_config_find_global(char *global_config_path)
327 {
328 const char *home;
329
330 home = getenv("HOME");
331
332 #ifdef GIT_WIN32
333 if (home == NULL)
334 home = getenv("USERPROFILE");
335 #endif
336
337 if (home == NULL)
338 return git__throw(GIT_EOSERR, "Failed to open global config file. Cannot locate the user's home directory");
339
340 git_path_join(global_config_path, home, GIT_CONFIG_FILENAME);
341
342 if (git_futils_exists(global_config_path) < GIT_SUCCESS)
343 return git__throw(GIT_EOSERR, "Failed to open global config file. The file does not exist");
344
345 return GIT_SUCCESS;
346 }
347
348
349
350 #if GIT_WIN32
351 static int win32_find_system(char *system_config_path)
352 {
353 const wchar_t *query = L"%PROGRAMFILES%\\Git\\etc\\gitconfig";
354 wchar_t *apphome_utf16;
355 char *apphome_utf8;
356 DWORD size, ret;
357
358 size = ExpandEnvironmentStringsW(query, NULL, 0);
359 /* The function gave us the full size of the buffer in chars, including NUL */
360 apphome_utf16 = git__malloc(size * sizeof(wchar_t));
361 if (apphome_utf16 == NULL)
362 return GIT_ENOMEM;
363
364 ret = ExpandEnvironmentStringsW(query, apphome_utf16, size);
365 if (ret != size)
366 return git__throw(GIT_ERROR, "Failed to expand environment strings");
367
368 if (_waccess(apphome_utf16, F_OK) < 0) {
369 free(apphome_utf16);
370 return GIT_ENOTFOUND;
371 }
372
373 apphome_utf8 = conv_utf16_to_utf8(apphome_utf16);
374 free(apphome_utf16);
375
376 if (strlen(apphome_utf8) >= GIT_PATH_MAX) {
377 free(apphome_utf8);
378 return git__throw(GIT_ESHORTBUFFER, "Path is too long");
379 }
380
381 strcpy(system_config_path, apphome_utf8);
382 free(apphome_utf8);
383 return GIT_SUCCESS;
384 }
385 #endif
386
387 int git_config_find_system(char *system_config_path)
388 {
389 const char *etc = "/etc/gitconfig";
390
391 if (git_futils_exists(etc) == GIT_SUCCESS) {
392 memcpy(system_config_path, etc, strlen(etc) + 1);
393 return GIT_SUCCESS;
394 }
395
396 #if GIT_WIN32
397 return win32_find_system(system_config_path);
398 #else
399 return GIT_ENOTFOUND;
400 #endif
401 }
402
403 int git_config_open_global(git_config **out)
404 {
405 int error;
406 char global_path[GIT_PATH_MAX];
407
408 if ((error = git_config_find_global(global_path)) < GIT_SUCCESS)
409 return error;
410
411 return git_config_open_ondisk(out, global_path);
412 }
413