config.c 8.02 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
/*
 * This file is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License, version 2,
 * as published by the Free Software Foundation.
 *
 * In addition to the permissions in the GNU General Public License,
 * the authors give you unlimited permission to link the compiled
 * version of this file into combinations with other programs,
 * and to distribute those combinations without any restriction
 * coming from the use of this file.  (The General Public License
 * restrictions do apply in other respects; for example, they cover
 * modification of the file, and distribution when not linked into
 * a combined executable.)
 *
 * This file is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; see the file COPYING.  If not, write to
 * the Free Software Foundation, 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */

#include "common.h"
#include "fileops.h"
#include "hashtable.h"
#include "config.h"
30
#include "git2/config.h"
31
#include "vector.h"
32 33 34

#include <ctype.h>

35
typedef struct {
36
	git_config_file *file;
37
	int priority;
38
} file_internal;
39

40
void git_config_free(git_config *cfg)
41
{
42
	unsigned int i;
43 44
	git_config_file *file;
	file_internal *internal;
45

46 47 48 49
	for(i = 0; i < cfg->files.length; ++i){
		internal = git_vector_get(&cfg->files, i);
		file = internal->file;
		file->free(file);
50
		free(internal);
51
	}
52

53
	git_vector_free(&cfg->files);
54
	free(cfg);
55 56
}

57
static int config_backend_cmp(const void *a, const void *b)
58
{
59 60
	const file_internal *bk_a = *(const file_internal **)(a);
	const file_internal *bk_b = *(const file_internal **)(b);
61 62

	return bk_b->priority - bk_a->priority;
63 64
}

65
int git_config_new(git_config **out)
66 67 68 69 70 71 72 73 74
{
	git_config *cfg;

	cfg = git__malloc(sizeof(git_config));
	if (cfg == NULL)
		return GIT_ENOMEM;

	memset(cfg, 0x0, sizeof(git_config));

75
	if (git_vector_init(&cfg->files, 3, config_backend_cmp) < 0) {
76 77
		free(cfg);
		return GIT_ENOMEM;
78
	}
79

80
	*out = cfg;
81

82 83
	return GIT_SUCCESS;
}
84

85 86 87 88 89 90 91 92 93 94 95
int git_config_add_file_ondisk(git_config *cfg, const char *path, int priority)
{
	git_config_file *file = NULL;
	int error;

	error = git_config_file__ondisk(&file, path);
	if (error < GIT_SUCCESS)
		return error;

	error = git_config_add_file(cfg, file, priority);
	if (error < GIT_SUCCESS) {
Vicent Marti committed
96 97 98 99 100
		/*
		 * free manually; the file is not owned by the config
		 * instance yet and will not be freed on cleanup
		 */
		file->free(file);
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
		return error;
	}

	return GIT_SUCCESS;
}

int git_config_open_ondisk(git_config **cfg, const char *path)
{
	int error;

	error = git_config_new(cfg);
	if (error < GIT_SUCCESS)
		return error;

	error = git_config_add_file_ondisk(*cfg, path, 1);
	if (error < GIT_SUCCESS)
		git_config_free(*cfg);

	return error;
}

122
int git_config_add_file(git_config *cfg, git_config_file *file, int priority)
123
{
124
	file_internal *internal;
125
	int error;
126

127
	assert(cfg && file);
128

129 130 131
	if ((error = file->open(file)) < GIT_SUCCESS)
		return git__throw(error, "Failed to open config file");

132
	internal = git__malloc(sizeof(file_internal));
133 134
	if (internal == NULL)
		return GIT_ENOMEM;
135

136
	internal->file = file;
137
	internal->priority = priority;
138

139
	if (git_vector_insert(&cfg->files, internal) < 0) {
140 141 142
		free(internal);
		return GIT_ENOMEM;
	}
143

144 145
	git_vector_sort(&cfg->files);
	internal->file->cfg = cfg;
146

147
	return GIT_SUCCESS;
148 149
}

150 151 152 153
/*
 * Loop over all the variables
 */

154
int git_config_foreach(git_config *cfg, int (*fn)(const char *, const char *, void *), void *data)
155 156
{
	int ret = GIT_SUCCESS;
157
	unsigned int i;
158 159
	file_internal *internal;
	git_config_file *file;
160

161 162 163 164
	for(i = 0; i < cfg->files.length && ret == 0; ++i) {
		internal = git_vector_get(&cfg->files, i);
		file = internal->file;
		ret = file->foreach(file, fn, data);
165
	}
166 167 168 169

	return ret;
}

170

171
/**************
172
 * Setters
173
 **************/
174

175 176 177
/*
 * Internal function to actually set the string value of a variable
 */
178

179
int git_config_set_long(git_config *cfg, const char *name, long int value)
180
{
Vicent Marti committed
181 182 183
	char str_value[32]; /* All numbers should fit in here */
	snprintf(str_value, sizeof(str_value), "%ld", value);
	return git_config_set_string(cfg, name, str_value);
184
}
185

186 187 188 189 190
int git_config_set_int(git_config *cfg, const char *name, int value)
{
	return git_config_set_long(cfg, name, value);
}

191 192
int git_config_set_bool(git_config *cfg, const char *name, int value)
{
Vicent Marti committed
193
	return git_config_set_string(cfg, name, value ? "true" : "false");
194
}
195

196 197
int git_config_set_string(git_config *cfg, const char *name, const char *value)
{
198 199
	file_internal *internal;
	git_config_file *file;
200

201 202
	if (cfg->files.length == 0)
		return git__throw(GIT_EINVALIDARGS, "Cannot set variable value; no files open in the `git_config` instance");
203

204 205
	internal = git_vector_get(&cfg->files, 0);
	file = internal->file;
206

207
	return file->set(file, name, value);
208 209
}

210 211 212
/***********
 * Getters
 ***********/
213

214
int git_config_get_long(git_config *cfg, const char *name, long int *out)
215
{
216
	const char *value, *num_end;
217
	int ret;
218
	long int num;
219

220
	ret = git_config_get_string(cfg, name, &value);
221
	if (ret < GIT_SUCCESS)
Vicent Marti committed
222
		return git__rethrow(ret, "Failed to get value for %s", name);
223

224 225
	ret = git__strtol32(&num, value, &num_end, 0);
	if (ret < GIT_SUCCESS)
Vicent Marti committed
226
		return git__rethrow(ret, "Failed to get value for %s", name);
227 228

	switch (*num_end) {
229 230
	case '\0':
		break;
231
	case 'k':
232
	case 'K':
233 234 235
		num *= 1024;
		break;
	case 'm':
236
	case 'M':
237 238 239
		num *= 1024 * 1024;
		break;
	case 'g':
240
	case 'G':
241 242 243
		num *= 1024 * 1024 * 1024;
		break;
	default:
244
		return git__throw(GIT_EINVALIDTYPE, "Failed to get value for %s. Value is of invalid type", name);
245
	}
246

247 248
	*out = num;

249
	return GIT_SUCCESS;
250 251
}

252 253 254 255 256 257 258 259 260 261 262 263
int git_config_get_int(git_config *cfg, const char *name, int *out)
{
	long int tmp;
	int ret;

	ret = git_config_get_long(cfg, name, &tmp);

	*out = (int) tmp;

	return ret;
}

264
int git_config_get_bool(git_config *cfg, const char *name, int *out)
265
{
266
	const char *value;
267 268
	int error = GIT_SUCCESS;

269
	error = git_config_get_string(cfg, name, &value);
270
	if (error < GIT_SUCCESS)
Vicent Marti committed
271
		return git__rethrow(error, "Failed to get value for %s", name);
272 273 274 275 276

	/* A missing value means true */
	if (value == NULL) {
		*out = 1;
		return GIT_SUCCESS;
277 278
	}

279 280
	if (!strcasecmp(value, "true") ||
		!strcasecmp(value, "yes") ||
281
		!strcasecmp(value, "on")) {
282 283 284 285 286
		*out = 1;
		return GIT_SUCCESS;
	}
	if (!strcasecmp(value, "false") ||
		!strcasecmp(value, "no") ||
287
		!strcasecmp(value, "off")) {
288 289 290 291 292 293 294 295
		*out = 0;
		return GIT_SUCCESS;
	}

	/* Try to parse it as an integer */
	error = git_config_get_int(cfg, name, out);
	if (error == GIT_SUCCESS)
		*out = !!(*out);
296

Vicent Marti committed
297 298
	if (error < GIT_SUCCESS)
		return git__rethrow(error, "Failed to get value for %s", name);
299 300 301
	return error;
}

302 303
int git_config_get_string(git_config *cfg, const char *name, const char **out)
{
304 305
	file_internal *internal;
	git_config_file *file;
306 307
	int error = GIT_ENOTFOUND;
	unsigned int i;
308

309 310
	if (cfg->files.length == 0)
		return git__throw(GIT_EINVALIDARGS, "Cannot get variable value; no files open in the `git_config` instance");
311

312 313 314
	for (i = 0; i < cfg->files.length; ++i) {
		internal = git_vector_get(&cfg->files, i);
		file = internal->file;
315 316
		if ((error = file->get(file, name, out)) == GIT_SUCCESS)
			return GIT_SUCCESS;
317
	}
318

319
	return git__throw(error, "Config value '%s' not found", name);
320 321
}

322 323 324 325 326 327 328 329 330 331 332 333 334 335
int git_config_find_global(char *global_config_path)
{
	const char *home;

	home = getenv("HOME");

#ifdef GIT_WIN32
	if (home == NULL)
		home = getenv("USERPROFILE");
#endif

	if (home == NULL)
		return git__throw(GIT_EOSERR, "Failed to open global config file. Cannot locate the user's home directory");

Vicent Marti committed
336
	git_path_join(global_config_path, home, GIT_CONFIG_FILENAME);
337

Vicent Marti committed
338
	if (git_futils_exists(global_config_path) < GIT_SUCCESS)
339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354
		return git__throw(GIT_EOSERR, "Failed to open global config file. The file does not exist");

	return GIT_SUCCESS;
}

int git_config_open_global(git_config **out)
{
	int error;
	char global_path[GIT_PATH_MAX];

	if ((error = git_config_find_global(global_path)) < GIT_SUCCESS)
		return error;

	return git_config_open_ondisk(out, global_path);
}