]> git.proxmox.com Git - libgit2.git/blob - src/config.c
Rewrite `git_config_open_global`
[libgit2.git] / src / config.c
1 /*
2 * This file is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License, version 2,
4 * as published by the Free Software Foundation.
5 *
6 * In addition to the permissions in the GNU General Public License,
7 * the authors give you unlimited permission to link the compiled
8 * version of this file into combinations with other programs,
9 * and to distribute those combinations without any restriction
10 * coming from the use of this file. (The General Public License
11 * restrictions do apply in other respects; for example, they cover
12 * modification of the file, and distribution when not linked into
13 * a combined executable.)
14 *
15 * This file is distributed in the hope that it will be useful, but
16 * WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; see the file COPYING. If not, write to
22 * the Free Software Foundation, 51 Franklin Street, Fifth Floor,
23 * Boston, MA 02110-1301, USA.
24 */
25
26 #include "common.h"
27 #include "fileops.h"
28 #include "hashtable.h"
29 #include "config.h"
30 #include "git2/config_backend.h"
31 #include "vector.h"
32
33 #include <ctype.h>
34
35 typedef struct {
36 git_config_backend *backend;
37 int priority;
38 } backend_internal;
39
40 int git_config_open_bare(git_config **out, const char *path)
41 {
42 git_config_backend *backend = NULL;
43 git_config *cfg = NULL;
44 int error = GIT_SUCCESS;
45
46 error = git_config_new(&cfg);
47 if (error < GIT_SUCCESS)
48 goto error;
49
50 error = git_config_backend_file(&backend, path);
51 if (error < GIT_SUCCESS)
52 goto error;
53
54 error = git_config_add_backend(cfg, backend, 1);
55 if (error < GIT_SUCCESS)
56 goto error;
57
58 error = backend->open(backend);
59 if (error < GIT_SUCCESS)
60 goto error;
61
62 *out = cfg;
63
64 return error;
65
66 error:
67 if(backend)
68 backend->free(backend);
69
70 return error;
71 }
72
73 int git_config_open_global(git_config **out)
74 {
75 char full_path[GIT_PATH_MAX];
76 const char *home;
77
78 home = getenv("HOME");
79 if (home == NULL)
80 return git__throw(GIT_EOSERR, "Failed to find $HOME variable");
81
82 git__joinpath(full_path, home, GIT_CONFIG_FILENAME);
83
84 return git_config_open_bare(out, filename);
85 }
86
87 void git_config_free(git_config *cfg)
88 {
89 unsigned int i;
90 git_config_backend *backend;
91 backend_internal *internal;
92
93 for(i = 0; i < cfg->backends.length; ++i){
94 internal = git_vector_get(&cfg->backends, i);
95 backend = internal->backend;
96 backend->free(backend);
97 free(internal);
98 }
99
100 git_vector_free(&cfg->backends);
101 free(cfg);
102 }
103
104 static int config_backend_cmp(const void *a, const void *b)
105 {
106 const backend_internal *bk_a = *(const backend_internal **)(a);
107 const backend_internal *bk_b = *(const backend_internal **)(b);
108
109 return bk_b->priority - bk_a->priority;
110 }
111
112 int git_config_new(git_config **out)
113 {
114 git_config *cfg;
115
116 cfg = git__malloc(sizeof(git_config));
117 if (cfg == NULL)
118 return GIT_ENOMEM;
119
120 memset(cfg, 0x0, sizeof(git_config));
121
122 if (git_vector_init(&cfg->backends, 3, config_backend_cmp) < 0) {
123 free(cfg);
124 return GIT_ENOMEM;
125 }
126
127 *out = cfg;
128
129 return GIT_SUCCESS;
130 }
131
132 int git_config_add_backend(git_config *cfg, git_config_backend *backend, int priority)
133 {
134 backend_internal *internal;
135
136 assert(cfg && backend);
137
138 internal = git__malloc(sizeof(backend_internal));
139 if (internal == NULL)
140 return GIT_ENOMEM;
141
142 internal->backend = backend;
143 internal->priority = priority;
144
145 if (git_vector_insert(&cfg->backends, internal) < 0) {
146 free(internal);
147 return GIT_ENOMEM;
148 }
149
150 git_vector_sort(&cfg->backends);
151 internal->backend->cfg = cfg;
152
153 return GIT_SUCCESS;
154 }
155
156 /*
157 * Loop over all the variables
158 */
159
160 int git_config_foreach(git_config *cfg, int (*fn)(const char *, void *), void *data)
161 {
162 int ret = GIT_SUCCESS;
163 unsigned int i;
164 backend_internal *internal;
165 git_config_backend *backend;
166
167 for(i = 0; i < cfg->backends.length && ret == 0; ++i) {
168 internal = git_vector_get(&cfg->backends, i);
169 backend = internal->backend;
170 ret = backend->foreach(backend, fn, data);
171 }
172
173 return ret;
174 }
175
176
177 /**************
178 * Setters
179 **************/
180
181 /*
182 * Internal function to actually set the string value of a variable
183 */
184
185 int git_config_set_long(git_config *cfg, const char *name, long int value)
186 {
187 char str_value[5]; /* Most numbers should fit in here */
188 int buf_len = sizeof(str_value), ret;
189 char *help_buf = NULL;
190
191 if ((ret = snprintf(str_value, buf_len, "%ld", value)) >= buf_len - 1){
192 /* The number is too large, we need to allocate more memory */
193 buf_len = ret + 1;
194 help_buf = git__malloc(buf_len);
195 snprintf(help_buf, buf_len, "%ld", value);
196 ret = git_config_set_string(cfg, name, help_buf);
197 free(help_buf);
198 } else {
199 ret = git_config_set_string(cfg, name, str_value);
200 }
201
202 return ret;
203 }
204
205 int git_config_set_int(git_config *cfg, const char *name, int value)
206 {
207 return git_config_set_long(cfg, name, value);
208 }
209
210 int git_config_set_bool(git_config *cfg, const char *name, int value)
211 {
212 const char *str_value;
213
214 if (value == 0)
215 str_value = "false";
216 else
217 str_value = "true";
218
219 return git_config_set_string(cfg, name, str_value);
220 }
221
222 int git_config_set_string(git_config *cfg, const char *name, const char *value)
223 {
224 backend_internal *internal;
225 git_config_backend *backend;
226
227 assert(cfg->backends.length > 0);
228
229 internal = git_vector_get(&cfg->backends, 0);
230 backend = internal->backend;
231
232 return backend->set(backend, name, value);
233 }
234
235 /***********
236 * Getters
237 ***********/
238
239 int git_config_get_long(git_config *cfg, const char *name, long int *out)
240 {
241 const char *value, *num_end;
242 int ret;
243 long int num;
244
245 ret = git_config_get_string(cfg, name, &value);
246 if (ret < GIT_SUCCESS)
247 return ret;
248
249 ret = git__strtol32(&num, value, &num_end, 0);
250 if (ret < GIT_SUCCESS)
251 return ret;
252
253 switch (*num_end) {
254 case '\0':
255 break;
256 case 'k':
257 case 'K':
258 num *= 1024;
259 break;
260 case 'm':
261 case 'M':
262 num *= 1024 * 1024;
263 break;
264 case 'g':
265 case 'G':
266 num *= 1024 * 1024 * 1024;
267 break;
268 default:
269 return GIT_EINVALIDTYPE;
270 }
271
272 *out = num;
273
274 return GIT_SUCCESS;
275 }
276
277 int git_config_get_int(git_config *cfg, const char *name, int *out)
278 {
279 long int tmp;
280 int ret;
281
282 ret = git_config_get_long(cfg, name, &tmp);
283
284 *out = (int) tmp;
285
286 return ret;
287 }
288
289 int git_config_get_bool(git_config *cfg, const char *name, int *out)
290 {
291 const char *value;
292 int error = GIT_SUCCESS;
293
294 error = git_config_get_string(cfg, name, &value);
295 if (error < GIT_SUCCESS)
296 return error;
297
298 /* A missing value means true */
299 if (value == NULL) {
300 *out = 1;
301 return GIT_SUCCESS;
302 }
303
304 if (!strcasecmp(value, "true") ||
305 !strcasecmp(value, "yes") ||
306 !strcasecmp(value, "on")) {
307 *out = 1;
308 return GIT_SUCCESS;
309 }
310 if (!strcasecmp(value, "false") ||
311 !strcasecmp(value, "no") ||
312 !strcasecmp(value, "off")) {
313 *out = 0;
314 return GIT_SUCCESS;
315 }
316
317 /* Try to parse it as an integer */
318 error = git_config_get_int(cfg, name, out);
319 if (error == GIT_SUCCESS)
320 *out = !!(*out);
321
322 return error;
323 }
324
325 int git_config_get_string(git_config *cfg, const char *name, const char **out)
326 {
327 backend_internal *internal;
328 git_config_backend *backend;
329
330 assert(cfg->backends.length > 0);
331
332 internal = git_vector_get(&cfg->backends, 0);
333 backend = internal->backend;
334
335 return backend->get(backend, name, out);
336 }
337