Blame src/config_file.c

Packit Service 20376f
/*
Packit Service 20376f
 * Copyright (C) the libgit2 contributors. All rights reserved.
Packit Service 20376f
 *
Packit Service 20376f
 * This file is part of libgit2, distributed under the GNU GPL v2 with
Packit Service 20376f
 * a Linking Exception. For full terms see the included COPYING file.
Packit Service 20376f
 */
Packit Service 20376f
Packit Service 20376f
#include "common.h"
Packit Service 20376f
#include "config.h"
Packit Service 20376f
#include "filebuf.h"
Packit Service 20376f
#include "sysdir.h"
Packit Service 20376f
#include "buffer.h"
Packit Service 20376f
#include "buf_text.h"
Packit Service 20376f
#include "git2/config.h"
Packit Service 20376f
#include "git2/sys/config.h"
Packit Service 20376f
#include "git2/types.h"
Packit Service 20376f
#include "strmap.h"
Packit Service 20376f
#include "array.h"
Packit Service 20376f
Packit Service 20376f
#include <ctype.h>
Packit Service 20376f
#include <sys/types.h>
Packit Service 20376f
#include <regex.h>
Packit Service 20376f
Packit Service 20376f
typedef struct cvar_t {
Packit Service 20376f
	struct cvar_t *next;
Packit Service 20376f
	git_config_entry *entry;
Packit Service 20376f
	bool included; /* whether this is part of [include] */
Packit Service 20376f
} cvar_t;
Packit Service 20376f
Packit Service 20376f
typedef struct git_config_file_iter {
Packit Service 20376f
	git_config_iterator parent;
Packit Service 20376f
	git_strmap_iter iter;
Packit Service 20376f
	cvar_t* next_var;
Packit Service 20376f
} git_config_file_iter;
Packit Service 20376f
Packit Service 20376f
/* Max depth for [include] directives */
Packit Service 20376f
#define MAX_INCLUDE_DEPTH 10
Packit Service 20376f
Packit Service 20376f
#define CVAR_LIST_HEAD(list) ((list)->head)
Packit Service 20376f
Packit Service 20376f
#define CVAR_LIST_TAIL(list) ((list)->tail)
Packit Service 20376f
Packit Service 20376f
#define CVAR_LIST_NEXT(var) ((var)->next)
Packit Service 20376f
Packit Service 20376f
#define CVAR_LIST_EMPTY(list) ((list)->head == NULL)
Packit Service 20376f
Packit Service 20376f
#define CVAR_LIST_APPEND(list, var) do {\
Packit Service 20376f
	if (CVAR_LIST_EMPTY(list)) {\
Packit Service 20376f
		CVAR_LIST_HEAD(list) = CVAR_LIST_TAIL(list) = var;\
Packit Service 20376f
	} else {\
Packit Service 20376f
		CVAR_LIST_NEXT(CVAR_LIST_TAIL(list)) = var;\
Packit Service 20376f
		CVAR_LIST_TAIL(list) = var;\
Packit Service 20376f
	}\
Packit Service 20376f
} while(0)
Packit Service 20376f
Packit Service 20376f
#define CVAR_LIST_REMOVE_HEAD(list) do {\
Packit Service 20376f
	CVAR_LIST_HEAD(list) = CVAR_LIST_NEXT(CVAR_LIST_HEAD(list));\
Packit Service 20376f
} while(0)
Packit Service 20376f
Packit Service 20376f
#define CVAR_LIST_REMOVE_AFTER(var) do {\
Packit Service 20376f
	CVAR_LIST_NEXT(var) = CVAR_LIST_NEXT(CVAR_LIST_NEXT(var));\
Packit Service 20376f
} while(0)
Packit Service 20376f
Packit Service 20376f
#define CVAR_LIST_FOREACH(list, iter)\
Packit Service 20376f
	for ((iter) = CVAR_LIST_HEAD(list);\
Packit Service 20376f
		 (iter) != NULL;\
Packit Service 20376f
		 (iter) = CVAR_LIST_NEXT(iter))
Packit Service 20376f
Packit Service 20376f
/*
Packit Service 20376f
 * Inspired by the FreeBSD functions
Packit Service 20376f
 */
Packit Service 20376f
#define CVAR_LIST_FOREACH_SAFE(start, iter, tmp)\
Packit Service 20376f
	for ((iter) = CVAR_LIST_HEAD(vars);\
Packit Service 20376f
		 (iter) && (((tmp) = CVAR_LIST_NEXT(iter) || 1));\
Packit Service 20376f
		 (iter) = (tmp))
Packit Service 20376f
Packit Service 20376f
struct reader {
Packit Service 20376f
	git_oid checksum;
Packit Service 20376f
	char *file_path;
Packit Service 20376f
	git_buf buffer;
Packit Service 20376f
	char *read_ptr;
Packit Service 20376f
	int line_number;
Packit Service 20376f
	int eof;
Packit Service 20376f
};
Packit Service 20376f
Packit Service 20376f
typedef struct {
Packit Service 20376f
	git_atomic refcount;
Packit Service 20376f
	git_strmap *values;
Packit Service 20376f
} refcounted_strmap;
Packit Service 20376f
Packit Service 20376f
typedef struct {
Packit Service 20376f
	git_config_backend parent;
Packit Service 20376f
	/* mutex to coordinate accessing the values */
Packit Service 20376f
	git_mutex values_mutex;
Packit Service 20376f
	refcounted_strmap *values;
Packit Service 20376f
} diskfile_header;
Packit Service 20376f
Packit Service 20376f
typedef struct {
Packit Service 20376f
	diskfile_header header;
Packit Service 20376f
Packit Service 20376f
	git_config_level_t level;
Packit Service 20376f
Packit Service 20376f
	git_array_t(struct reader) readers;
Packit Service 20376f
Packit Service 20376f
	bool locked;
Packit Service 20376f
	git_filebuf locked_buf;
Packit Service 20376f
	git_buf locked_content;
Packit Service 20376f
Packit Service 20376f
	char  *file_path;
Packit Service 20376f
} diskfile_backend;
Packit Service 20376f
Packit Service 20376f
typedef struct {
Packit Service 20376f
	diskfile_header header;
Packit Service 20376f
Packit Service 20376f
	diskfile_backend *snapshot_from;
Packit Service 20376f
} diskfile_readonly_backend;
Packit Service 20376f
Packit Service 20376f
static int config_read(git_strmap *values, diskfile_backend *cfg_file, struct reader *reader, git_config_level_t level, int depth);
Packit Service 20376f
static int config_write(diskfile_backend *cfg, const char *key, const regex_t *preg, const char *value);
Packit Service 20376f
static char *escape_value(const char *ptr);
Packit Service 20376f
Packit Service 20376f
int git_config_file__snapshot(git_config_backend **out, diskfile_backend *in);
Packit Service 20376f
static int config_snapshot(git_config_backend **out, git_config_backend *in);
Packit Service 20376f
Packit Service 20376f
static void set_parse_error(struct reader *reader, int col, const char *error_str)
Packit Service 20376f
{
Packit Service 20376f
	giterr_set(GITERR_CONFIG, "failed to parse config file: %s (in %s:%d, column %d)",
Packit Service 20376f
		error_str, reader->file_path, reader->line_number, col);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int config_error_readonly(void)
Packit Service 20376f
{
Packit Service 20376f
	giterr_set(GITERR_CONFIG, "this backend is read-only");
Packit Service 20376f
	return -1;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static void cvar_free(cvar_t *var)
Packit Service 20376f
{
Packit Service 20376f
	if (var == NULL)
Packit Service 20376f
		return;
Packit Service 20376f
Packit Service 20376f
	git__free((char*)var->entry->name);
Packit Service 20376f
	git__free((char *)var->entry->value);
Packit Service 20376f
	git__free(var->entry);
Packit Service 20376f
	git__free(var);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_config_file_normalize_section(char *start, char *end)
Packit Service 20376f
{
Packit Service 20376f
	char *scan;
Packit Service 20376f
Packit Service 20376f
	if (start == end)
Packit Service 20376f
		return GIT_EINVALIDSPEC;
Packit Service 20376f
Packit Service 20376f
	/* Validate and downcase range */
Packit Service 20376f
	for (scan = start; *scan; ++scan) {
Packit Service 20376f
		if (end && scan >= end)
Packit Service 20376f
			break;
Packit Service 20376f
		if (isalnum(*scan))
Packit Service 20376f
			*scan = (char)git__tolower(*scan);
Packit Service 20376f
		else if (*scan != '-' || scan == start)
Packit Service 20376f
			return GIT_EINVALIDSPEC;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if (scan == start)
Packit Service 20376f
		return GIT_EINVALIDSPEC;
Packit Service 20376f
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
/* Add or append the new config option */
Packit Service 20376f
static int append_entry(git_strmap *values, cvar_t *var)
Packit Service 20376f
{
Packit Service 20376f
	git_strmap_iter pos;
Packit Service 20376f
	cvar_t *existing;
Packit Service 20376f
	int error = 0;
Packit Service 20376f
Packit Service 20376f
	pos = git_strmap_lookup_index(values, var->entry->name);
Packit Service 20376f
	if (!git_strmap_valid_index(values, pos)) {
Packit Service 20376f
		git_strmap_insert(values, var->entry->name, var, &error);
Packit Service 20376f
	} else {
Packit Service 20376f
		existing = git_strmap_value_at(values, pos);
Packit Service 20376f
		while (existing->next != NULL) {
Packit Service 20376f
			existing = existing->next;
Packit Service 20376f
		}
Packit Service 20376f
		existing->next = var;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if (error > 0)
Packit Service 20376f
		error = 0;
Packit Service 20376f
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static void free_vars(git_strmap *values)
Packit Service 20376f
{
Packit Service 20376f
	cvar_t *var = NULL;
Packit Service 20376f
Packit Service 20376f
	if (values == NULL)
Packit Service 20376f
		return;
Packit Service 20376f
Packit Service 20376f
	git_strmap_foreach_value(values, var,
Packit Service 20376f
		while (var != NULL) {
Packit Service 20376f
			cvar_t *next = CVAR_LIST_NEXT(var);
Packit Service 20376f
			cvar_free(var);
Packit Service 20376f
			var = next;
Packit Service 20376f
		});
Packit Service 20376f
Packit Service 20376f
	git_strmap_free(values);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static void refcounted_strmap_free(refcounted_strmap *map)
Packit Service 20376f
{
Packit Service 20376f
	if (!map)
Packit Service 20376f
		return;
Packit Service 20376f
Packit Service 20376f
	if (git_atomic_dec(&map->refcount) != 0)
Packit Service 20376f
		return;
Packit Service 20376f
Packit Service 20376f
	free_vars(map->values);
Packit Service 20376f
	git__free(map);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
/**
Packit Service 20376f
 * Take the current values map from the backend and increase its
Packit Service 20376f
 * refcount. This is its own function to make sure we use the mutex to
Packit Service 20376f
 * avoid the map pointer from changing under us.
Packit Service 20376f
 */
Packit Service 20376f
static refcounted_strmap *refcounted_strmap_take(diskfile_header *h)
Packit Service 20376f
{
Packit Service 20376f
	refcounted_strmap *map;
Packit Service 20376f
Packit Service 20376f
	if (git_mutex_lock(&h->values_mutex) < 0) {
Packit Service 20376f
	    giterr_set(GITERR_OS, "failed to lock config backend");
Packit Service 20376f
	    return NULL;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	map = h->values;
Packit Service 20376f
	git_atomic_inc(&map->refcount);
Packit Service 20376f
Packit Service 20376f
	git_mutex_unlock(&h->values_mutex);
Packit Service 20376f
Packit Service 20376f
	return map;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int refcounted_strmap_alloc(refcounted_strmap **out)
Packit Service 20376f
{
Packit Service 20376f
	refcounted_strmap *map;
Packit Service 20376f
	int error;
Packit Service 20376f
Packit Service 20376f
	map = git__calloc(1, sizeof(refcounted_strmap));
Packit Service 20376f
	GITERR_CHECK_ALLOC(map);
Packit Service 20376f
Packit Service 20376f
	git_atomic_set(&map->refcount, 1);
Packit Service 20376f
Packit Service 20376f
	if ((error = git_strmap_alloc(&map->values)) < 0)
Packit Service 20376f
		git__free(map);
Packit Service 20376f
	else
Packit Service 20376f
		*out = map;
Packit Service 20376f
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int config_open(git_config_backend *cfg, git_config_level_t level)
Packit Service 20376f
{
Packit Service 20376f
	int res;
Packit Service 20376f
	struct reader *reader;
Packit Service 20376f
	diskfile_backend *b = (diskfile_backend *)cfg;
Packit Service 20376f
Packit Service 20376f
	b->level = level;
Packit Service 20376f
Packit Service 20376f
	if ((res = refcounted_strmap_alloc(&b->header.values)) < 0)
Packit Service 20376f
		return res;
Packit Service 20376f
Packit Service 20376f
	git_array_init(b->readers);
Packit Service 20376f
	reader = git_array_alloc(b->readers);
Packit Service 20376f
	if (!reader) {
Packit Service 20376f
		refcounted_strmap_free(b->header.values);
Packit Service 20376f
		return -1;
Packit Service 20376f
	}
Packit Service 20376f
	memset(reader, 0, sizeof(struct reader));
Packit Service 20376f
Packit Service 20376f
	reader->file_path = git__strdup(b->file_path);
Packit Service 20376f
	GITERR_CHECK_ALLOC(reader->file_path);
Packit Service 20376f
Packit Service 20376f
	git_buf_init(&reader->buffer, 0);
Packit Service 20376f
	res = git_futils_readbuffer_updated(
Packit Service 20376f
		&reader->buffer, b->file_path, &reader->checksum, NULL);
Packit Service 20376f
Packit Service 20376f
	/* It's fine if the file doesn't exist */
Packit Service 20376f
	if (res == GIT_ENOTFOUND)
Packit Service 20376f
		return 0;
Packit Service 20376f
Packit Service 20376f
	if (res < 0 || (res = config_read(b->header.values->values, b, reader, level, 0)) < 0) {
Packit Service 20376f
		refcounted_strmap_free(b->header.values);
Packit Service 20376f
		b->header.values = NULL;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	reader = git_array_get(b->readers, 0);
Packit Service 20376f
	git_buf_free(&reader->buffer);
Packit Service 20376f
Packit Service 20376f
	return res;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
/* The meat of the refresh, as we want to use it in different places */
Packit Service 20376f
static int config__refresh(git_config_backend *cfg)
Packit Service 20376f
{
Packit Service 20376f
	refcounted_strmap *values = NULL, *tmp;
Packit Service 20376f
	diskfile_backend *b = (diskfile_backend *)cfg;
Packit Service 20376f
	struct reader *reader = NULL;
Packit Service 20376f
	int error = 0;
Packit Service 20376f
Packit Service 20376f
	if ((error = refcounted_strmap_alloc(&values)) < 0)
Packit Service 20376f
		goto out;
Packit Service 20376f
Packit Service 20376f
	reader = git_array_get(b->readers, git_array_size(b->readers) - 1);
Packit Service 20376f
	GITERR_CHECK_ALLOC(reader);
Packit Service 20376f
Packit Service 20376f
	if ((error = config_read(values->values, b, reader, b->level, 0)) < 0)
Packit Service 20376f
		goto out;
Packit Service 20376f
Packit Service 20376f
	if ((error = git_mutex_lock(&b->header.values_mutex)) < 0) {
Packit Service 20376f
		giterr_set(GITERR_OS, "failed to lock config backend");
Packit Service 20376f
		goto out;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	tmp = b->header.values;
Packit Service 20376f
	b->header.values = values;
Packit Service 20376f
	values = tmp;
Packit Service 20376f
Packit Service 20376f
	git_mutex_unlock(&b->header.values_mutex);
Packit Service 20376f
Packit Service 20376f
out:
Packit Service 20376f
	refcounted_strmap_free(values);
Packit Service 20376f
	if (reader)
Packit Service 20376f
		git_buf_free(&reader->buffer);
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int config_refresh(git_config_backend *cfg)
Packit Service 20376f
{
Packit Service 20376f
	int error = 0, updated = 0, any_updated = 0;
Packit Service 20376f
	diskfile_backend *b = (diskfile_backend *)cfg;
Packit Service 20376f
	struct reader *reader = NULL;
Packit Service 20376f
	uint32_t i;
Packit Service 20376f
Packit Service 20376f
	for (i = 0; i < git_array_size(b->readers); i++) {
Packit Service 20376f
		reader = git_array_get(b->readers, i);
Packit Service 20376f
		error = git_futils_readbuffer_updated(
Packit Service 20376f
			&reader->buffer, reader->file_path,
Packit Service 20376f
			&reader->checksum, &updated);
Packit Service 20376f
Packit Service 20376f
		if (error < 0 && error != GIT_ENOTFOUND)
Packit Service 20376f
			return error;
Packit Service 20376f
Packit Service 20376f
		if (updated)
Packit Service 20376f
			any_updated = 1;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if (!any_updated)
Packit Service 20376f
		return (error == GIT_ENOTFOUND) ? 0 : error;
Packit Service 20376f
Packit Service 20376f
	return config__refresh(cfg);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static void backend_free(git_config_backend *_backend)
Packit Service 20376f
{
Packit Service 20376f
	diskfile_backend *backend = (diskfile_backend *)_backend;
Packit Service 20376f
	uint32_t i;
Packit Service 20376f
Packit Service 20376f
	if (backend == NULL)
Packit Service 20376f
		return;
Packit Service 20376f
Packit Service 20376f
	for (i = 0; i < git_array_size(backend->readers); i++) {
Packit Service 20376f
		struct reader *r = git_array_get(backend->readers, i);
Packit Service 20376f
		git__free(r->file_path);
Packit Service 20376f
	}
Packit Service 20376f
	git_array_clear(backend->readers);
Packit Service 20376f
Packit Service 20376f
	git__free(backend->file_path);
Packit Service 20376f
	refcounted_strmap_free(backend->header.values);
Packit Service 20376f
	git_mutex_free(&backend->header.values_mutex);
Packit Service 20376f
	git__free(backend);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static void config_iterator_free(
Packit Service 20376f
	git_config_iterator* iter)
Packit Service 20376f
{
Packit Service 20376f
	iter->backend->free(iter->backend);
Packit Service 20376f
	git__free(iter);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int config_iterator_next(
Packit Service 20376f
	git_config_entry **entry,
Packit Service 20376f
	git_config_iterator *iter)
Packit Service 20376f
{
Packit Service 20376f
	git_config_file_iter *it = (git_config_file_iter *) iter;
Packit Service 20376f
	diskfile_header *h = (diskfile_header *) it->parent.backend;
Packit Service 20376f
	git_strmap *values = h->values->values;
Packit Service 20376f
	int err = 0;
Packit Service 20376f
	cvar_t * var;
Packit Service 20376f
Packit Service 20376f
	if (it->next_var == NULL) {
Packit Service 20376f
		err = git_strmap_next((void**) &var, &(it->iter), values);
Packit Service 20376f
	} else {
Packit Service 20376f
		var = it->next_var;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if (err < 0) {
Packit Service 20376f
		it->next_var = NULL;
Packit Service 20376f
		return err;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	*entry = var->entry;
Packit Service 20376f
	it->next_var = CVAR_LIST_NEXT(var);
Packit Service 20376f
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int config_iterator_new(
Packit Service 20376f
	git_config_iterator **iter,
Packit Service 20376f
	struct git_config_backend* backend)
Packit Service 20376f
{
Packit Service 20376f
	diskfile_header *h;
Packit Service 20376f
	git_config_file_iter *it;
Packit Service 20376f
	git_config_backend *snapshot;
Packit Service 20376f
	diskfile_backend *b = (diskfile_backend *) backend;
Packit Service 20376f
	int error;
Packit Service 20376f
Packit Service 20376f
	if ((error = config_snapshot(&snapshot, backend)) < 0)
Packit Service 20376f
		return error;
Packit Service 20376f
Packit Service 20376f
	if ((error = snapshot->open(snapshot, b->level)) < 0)
Packit Service 20376f
		return error;
Packit Service 20376f
Packit Service 20376f
	it = git__calloc(1, sizeof(git_config_file_iter));
Packit Service 20376f
	GITERR_CHECK_ALLOC(it);
Packit Service 20376f
Packit Service 20376f
	h = (diskfile_header *)snapshot;
Packit Service 20376f
Packit Service 20376f
	/* strmap_begin() is currently a macro returning 0 */
Packit Service 20376f
	GIT_UNUSED(h);
Packit Service 20376f
Packit Service 20376f
	it->parent.backend = snapshot;
Packit Service 20376f
	it->iter = git_strmap_begin(h->values);
Packit Service 20376f
	it->next_var = NULL;
Packit Service 20376f
Packit Service 20376f
	it->parent.next = config_iterator_next;
Packit Service 20376f
	it->parent.free = config_iterator_free;
Packit Service 20376f
	*iter = (git_config_iterator *) it;
Packit Service 20376f
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int config_set(git_config_backend *cfg, const char *name, const char *value)
Packit Service 20376f
{
Packit Service 20376f
	diskfile_backend *b = (diskfile_backend *)cfg;
Packit Service 20376f
	refcounted_strmap *map;
Packit Service 20376f
	git_strmap *values;
Packit Service 20376f
	char *key, *esc_value = NULL;
Packit Service 20376f
	khiter_t pos;
Packit Service 20376f
	int rval, ret;
Packit Service 20376f
Packit Service 20376f
	if ((rval = git_config__normalize_name(name, &key)) < 0)
Packit Service 20376f
		return rval;
Packit Service 20376f
Packit Service 20376f
	if ((map = refcounted_strmap_take(&b->header)) == NULL)
Packit Service 20376f
		return -1;
Packit Service 20376f
	values = map->values;
Packit Service 20376f
Packit Service 20376f
	/*
Packit Service 20376f
	 * Try to find it in the existing values and update it if it
Packit Service 20376f
	 * only has one value.
Packit Service 20376f
	 */
Packit Service 20376f
	pos = git_strmap_lookup_index(values, key);
Packit Service 20376f
	if (git_strmap_valid_index(values, pos)) {
Packit Service 20376f
		cvar_t *existing = git_strmap_value_at(values, pos);
Packit Service 20376f
Packit Service 20376f
		if (existing->next != NULL) {
Packit Service 20376f
			giterr_set(GITERR_CONFIG, "multivar incompatible with simple set");
Packit Service 20376f
			ret = -1;
Packit Service 20376f
			goto out;
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		/* don't update if old and new values already match */
Packit Service 20376f
		if ((!existing->entry->value && !value) ||
Packit Service 20376f
			(existing->entry->value && value &&
Packit Service 20376f
			 !strcmp(existing->entry->value, value))) {
Packit Service 20376f
			ret = 0;
Packit Service 20376f
			goto out;
Packit Service 20376f
		}
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	/* No early returns due to sanity checks, let's write it out and refresh */
Packit Service 20376f
Packit Service 20376f
	if (value) {
Packit Service 20376f
		esc_value = escape_value(value);
Packit Service 20376f
		GITERR_CHECK_ALLOC(esc_value);
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if ((ret = config_write(b, key, NULL, esc_value)) < 0)
Packit Service 20376f
		goto out;
Packit Service 20376f
Packit Service 20376f
	ret = config_refresh(cfg);
Packit Service 20376f
Packit Service 20376f
out:
Packit Service 20376f
	refcounted_strmap_free(map);
Packit Service 20376f
	git__free(esc_value);
Packit Service 20376f
	git__free(key);
Packit Service 20376f
	return ret;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
/* release the map containing the entry as an equivalent to freeing it */
Packit Service 20376f
static void release_map(git_config_entry *entry)
Packit Service 20376f
{
Packit Service 20376f
	refcounted_strmap *map = (refcounted_strmap *) entry->payload;
Packit Service 20376f
	refcounted_strmap_free(map);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
/*
Packit Service 20376f
 * Internal function that actually gets the value in string form
Packit Service 20376f
 */
Packit Service 20376f
static int config_get(git_config_backend *cfg, const char *key, git_config_entry **out)
Packit Service 20376f
{
Packit Service 20376f
	diskfile_header *h = (diskfile_header *)cfg;
Packit Service 20376f
	refcounted_strmap *map;
Packit Service 20376f
	git_strmap *values;
Packit Service 20376f
	khiter_t pos;
Packit Service 20376f
	cvar_t *var;
Packit Service 20376f
	int error = 0;
Packit Service 20376f
Packit Service 20376f
	if (!h->parent.readonly && ((error = config_refresh(cfg)) < 0))
Packit Service 20376f
		return error;
Packit Service 20376f
Packit Service 20376f
	if ((map = refcounted_strmap_take(h)) == NULL)
Packit Service 20376f
		return -1;
Packit Service 20376f
	values = map->values;
Packit Service 20376f
Packit Service 20376f
	pos = git_strmap_lookup_index(values, key);
Packit Service 20376f
Packit Service 20376f
	/* no error message; the config system will write one */
Packit Service 20376f
	if (!git_strmap_valid_index(values, pos)) {
Packit Service 20376f
		refcounted_strmap_free(map);
Packit Service 20376f
		return GIT_ENOTFOUND;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	var = git_strmap_value_at(values, pos);
Packit Service 20376f
	while (var->next)
Packit Service 20376f
		var = var->next;
Packit Service 20376f
Packit Service 20376f
	*out = var->entry;
Packit Service 20376f
	(*out)->free = release_map;
Packit Service 20376f
	(*out)->payload = map;
Packit Service 20376f
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int config_set_multivar(
Packit Service 20376f
	git_config_backend *cfg, const char *name, const char *regexp, const char *value)
Packit Service 20376f
{
Packit Service 20376f
	diskfile_backend *b = (diskfile_backend *)cfg;
Packit Service 20376f
	char *key;
Packit Service 20376f
	regex_t preg;
Packit Service 20376f
	int result;
Packit Service 20376f
Packit Service 20376f
	assert(regexp);
Packit Service 20376f
Packit Service 20376f
	if ((result = git_config__normalize_name(name, &key)) < 0)
Packit Service 20376f
		return result;
Packit Service 20376f
Packit Service 20376f
	result = p_regcomp(&preg, regexp, REG_EXTENDED);
Packit Service 20376f
	if (result != 0) {
Packit Service 20376f
		giterr_set_regex(&preg, result);
Packit Service 20376f
		result = -1;
Packit Service 20376f
		goto out;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	/* If we do have it, set call config_write() and reload */
Packit Service 20376f
	if ((result = config_write(b, key, &preg, value)) < 0)
Packit Service 20376f
		goto out;
Packit Service 20376f
Packit Service 20376f
	result = config_refresh(cfg);
Packit Service 20376f
Packit Service 20376f
out:
Packit Service 20376f
	git__free(key);
Packit Service 20376f
	regfree(&preg;;
Packit Service 20376f
Packit Service 20376f
	return result;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int config_delete(git_config_backend *cfg, const char *name)
Packit Service 20376f
{
Packit Service 20376f
	cvar_t *var;
Packit Service 20376f
	diskfile_backend *b = (diskfile_backend *)cfg;
Packit Service 20376f
	refcounted_strmap *map;	git_strmap *values;
Packit Service 20376f
	char *key;
Packit Service 20376f
	int result;
Packit Service 20376f
	khiter_t pos;
Packit Service 20376f
Packit Service 20376f
	if ((result = git_config__normalize_name(name, &key)) < 0)
Packit Service 20376f
		return result;
Packit Service 20376f
Packit Service 20376f
	if ((map = refcounted_strmap_take(&b->header)) == NULL)
Packit Service 20376f
		return -1;
Packit Service 20376f
	values = b->header.values->values;
Packit Service 20376f
Packit Service 20376f
	pos = git_strmap_lookup_index(values, key);
Packit Service 20376f
	git__free(key);
Packit Service 20376f
Packit Service 20376f
	if (!git_strmap_valid_index(values, pos)) {
Packit Service 20376f
		refcounted_strmap_free(map);
Packit Service 20376f
		giterr_set(GITERR_CONFIG, "could not find key '%s' to delete", name);
Packit Service 20376f
		return GIT_ENOTFOUND;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	var = git_strmap_value_at(values, pos);
Packit Service 20376f
	refcounted_strmap_free(map);
Packit Service 20376f
Packit Service 20376f
	if (var->next != NULL) {
Packit Service 20376f
		giterr_set(GITERR_CONFIG, "cannot delete multivar with a single delete");
Packit Service 20376f
		return -1;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if ((result = config_write(b, var->entry->name, NULL, NULL)) < 0)
Packit Service 20376f
		return result;
Packit Service 20376f
Packit Service 20376f
	return config_refresh(cfg);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int config_delete_multivar(git_config_backend *cfg, const char *name, const char *regexp)
Packit Service 20376f
{
Packit Service 20376f
	diskfile_backend *b = (diskfile_backend *)cfg;
Packit Service 20376f
	refcounted_strmap *map;
Packit Service 20376f
	git_strmap *values;
Packit Service 20376f
	char *key;
Packit Service 20376f
	regex_t preg;
Packit Service 20376f
	int result;
Packit Service 20376f
	khiter_t pos;
Packit Service 20376f
Packit Service 20376f
	if ((result = git_config__normalize_name(name, &key)) < 0)
Packit Service 20376f
		return result;
Packit Service 20376f
Packit Service 20376f
	if ((map = refcounted_strmap_take(&b->header)) == NULL)
Packit Service 20376f
		return -1;
Packit Service 20376f
	values = b->header.values->values;
Packit Service 20376f
Packit Service 20376f
	pos = git_strmap_lookup_index(values, key);
Packit Service 20376f
Packit Service 20376f
	if (!git_strmap_valid_index(values, pos)) {
Packit Service 20376f
		refcounted_strmap_free(map);
Packit Service 20376f
		git__free(key);
Packit Service 20376f
		giterr_set(GITERR_CONFIG, "could not find key '%s' to delete", name);
Packit Service 20376f
		return GIT_ENOTFOUND;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	refcounted_strmap_free(map);
Packit Service 20376f
Packit Service 20376f
	result = p_regcomp(&preg, regexp, REG_EXTENDED);
Packit Service 20376f
	if (result != 0) {
Packit Service 20376f
		giterr_set_regex(&preg, result);
Packit Service 20376f
		result = -1;
Packit Service 20376f
		goto out;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if ((result = config_write(b, key, &preg, NULL)) < 0)
Packit Service 20376f
		goto out;
Packit Service 20376f
Packit Service 20376f
	result = config_refresh(cfg);
Packit Service 20376f
Packit Service 20376f
out:
Packit Service 20376f
	git__free(key);
Packit Service 20376f
	regfree(&preg;;
Packit Service 20376f
	return result;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int config_snapshot(git_config_backend **out, git_config_backend *in)
Packit Service 20376f
{
Packit Service 20376f
	diskfile_backend *b = (diskfile_backend *) in;
Packit Service 20376f
Packit Service 20376f
	return git_config_file__snapshot(out, b);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int config_lock(git_config_backend *_cfg)
Packit Service 20376f
{
Packit Service 20376f
	diskfile_backend *cfg = (diskfile_backend *) _cfg;
Packit Service 20376f
	int error;
Packit Service 20376f
Packit Service 20376f
	if ((error = git_filebuf_open(&cfg->locked_buf, cfg->file_path, 0, GIT_CONFIG_FILE_MODE)) < 0)
Packit Service 20376f
		return error;
Packit Service 20376f
Packit Service 20376f
	error = git_futils_readbuffer(&cfg->locked_content, cfg->file_path);
Packit Service 20376f
	if (error < 0 && error != GIT_ENOTFOUND) {
Packit Service 20376f
		git_filebuf_cleanup(&cfg->locked_buf);
Packit Service 20376f
		return error;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	cfg->locked = true;
Packit Service 20376f
	return 0;
Packit Service 20376f
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int config_unlock(git_config_backend *_cfg, int success)
Packit Service 20376f
{
Packit Service 20376f
	diskfile_backend *cfg = (diskfile_backend *) _cfg;
Packit Service 20376f
	int error = 0;
Packit Service 20376f
Packit Service 20376f
	if (success) {
Packit Service 20376f
		git_filebuf_write(&cfg->locked_buf, cfg->locked_content.ptr, cfg->locked_content.size);
Packit Service 20376f
		error = git_filebuf_commit(&cfg->locked_buf);
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	git_filebuf_cleanup(&cfg->locked_buf);
Packit Service 20376f
	git_buf_free(&cfg->locked_content);
Packit Service 20376f
	cfg->locked = false;
Packit Service 20376f
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_config_file__ondisk(git_config_backend **out, const char *path)
Packit Service 20376f
{
Packit Service 20376f
	diskfile_backend *backend;
Packit Service 20376f
Packit Service 20376f
	backend = git__calloc(1, sizeof(diskfile_backend));
Packit Service 20376f
	GITERR_CHECK_ALLOC(backend);
Packit Service 20376f
Packit Service 20376f
	backend->header.parent.version = GIT_CONFIG_BACKEND_VERSION;
Packit Service 20376f
	git_mutex_init(&backend->header.values_mutex);
Packit Service 20376f
Packit Service 20376f
	backend->file_path = git__strdup(path);
Packit Service 20376f
	GITERR_CHECK_ALLOC(backend->file_path);
Packit Service 20376f
Packit Service 20376f
	backend->header.parent.open = config_open;
Packit Service 20376f
	backend->header.parent.get = config_get;
Packit Service 20376f
	backend->header.parent.set = config_set;
Packit Service 20376f
	backend->header.parent.set_multivar = config_set_multivar;
Packit Service 20376f
	backend->header.parent.del = config_delete;
Packit Service 20376f
	backend->header.parent.del_multivar = config_delete_multivar;
Packit Service 20376f
	backend->header.parent.iterator = config_iterator_new;
Packit Service 20376f
	backend->header.parent.snapshot = config_snapshot;
Packit Service 20376f
	backend->header.parent.lock = config_lock;
Packit Service 20376f
	backend->header.parent.unlock = config_unlock;
Packit Service 20376f
	backend->header.parent.free = backend_free;
Packit Service 20376f
Packit Service 20376f
	*out = (git_config_backend *)backend;
Packit Service 20376f
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int config_set_readonly(git_config_backend *cfg, const char *name, const char *value)
Packit Service 20376f
{
Packit Service 20376f
	GIT_UNUSED(cfg);
Packit Service 20376f
	GIT_UNUSED(name);
Packit Service 20376f
	GIT_UNUSED(value);
Packit Service 20376f
Packit Service 20376f
	return config_error_readonly();
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int config_set_multivar_readonly(
Packit Service 20376f
	git_config_backend *cfg, const char *name, const char *regexp, const char *value)
Packit Service 20376f
{
Packit Service 20376f
	GIT_UNUSED(cfg);
Packit Service 20376f
	GIT_UNUSED(name);
Packit Service 20376f
	GIT_UNUSED(regexp);
Packit Service 20376f
	GIT_UNUSED(value);
Packit Service 20376f
Packit Service 20376f
	return config_error_readonly();
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int config_delete_multivar_readonly(git_config_backend *cfg, const char *name, const char *regexp)
Packit Service 20376f
{
Packit Service 20376f
	GIT_UNUSED(cfg);
Packit Service 20376f
	GIT_UNUSED(name);
Packit Service 20376f
	GIT_UNUSED(regexp);
Packit Service 20376f
Packit Service 20376f
	return config_error_readonly();
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int config_delete_readonly(git_config_backend *cfg, const char *name)
Packit Service 20376f
{
Packit Service 20376f
	GIT_UNUSED(cfg);
Packit Service 20376f
	GIT_UNUSED(name);
Packit Service 20376f
Packit Service 20376f
	return config_error_readonly();
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int config_lock_readonly(git_config_backend *_cfg)
Packit Service 20376f
{
Packit Service 20376f
	GIT_UNUSED(_cfg);
Packit Service 20376f
Packit Service 20376f
	return config_error_readonly();
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int config_unlock_readonly(git_config_backend *_cfg, int success)
Packit Service 20376f
{
Packit Service 20376f
	GIT_UNUSED(_cfg);
Packit Service 20376f
	GIT_UNUSED(success);
Packit Service 20376f
Packit Service 20376f
	return config_error_readonly();
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static void backend_readonly_free(git_config_backend *_backend)
Packit Service 20376f
{
Packit Service 20376f
	diskfile_backend *backend = (diskfile_backend *)_backend;
Packit Service 20376f
Packit Service 20376f
	if (backend == NULL)
Packit Service 20376f
		return;
Packit Service 20376f
Packit Service 20376f
	refcounted_strmap_free(backend->header.values);
Packit Service 20376f
	git_mutex_free(&backend->header.values_mutex);
Packit Service 20376f
	git__free(backend);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int config_readonly_open(git_config_backend *cfg, git_config_level_t level)
Packit Service 20376f
{
Packit Service 20376f
	diskfile_readonly_backend *b = (diskfile_readonly_backend *) cfg;
Packit Service 20376f
	diskfile_backend *src = b->snapshot_from;
Packit Service 20376f
	diskfile_header *src_header = &src->header;
Packit Service 20376f
	refcounted_strmap *src_map;
Packit Service 20376f
	int error;
Packit Service 20376f
Packit Service 20376f
	if (!src_header->parent.readonly && (error = config_refresh(&src_header->parent)) < 0)
Packit Service 20376f
		return error;
Packit Service 20376f
Packit Service 20376f
	/* We're just copying data, don't care about the level */
Packit Service 20376f
	GIT_UNUSED(level);
Packit Service 20376f
Packit Service 20376f
	if ((src_map = refcounted_strmap_take(src_header)) == NULL)
Packit Service 20376f
		return -1;
Packit Service 20376f
	b->header.values = src_map;
Packit Service 20376f
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_config_file__snapshot(git_config_backend **out, diskfile_backend *in)
Packit Service 20376f
{
Packit Service 20376f
	diskfile_readonly_backend *backend;
Packit Service 20376f
Packit Service 20376f
	backend = git__calloc(1, sizeof(diskfile_readonly_backend));
Packit Service 20376f
	GITERR_CHECK_ALLOC(backend);
Packit Service 20376f
Packit Service 20376f
	backend->header.parent.version = GIT_CONFIG_BACKEND_VERSION;
Packit Service 20376f
	git_mutex_init(&backend->header.values_mutex);
Packit Service 20376f
Packit Service 20376f
	backend->snapshot_from = in;
Packit Service 20376f
Packit Service 20376f
	backend->header.parent.readonly = 1;
Packit Service 20376f
	backend->header.parent.version = GIT_CONFIG_BACKEND_VERSION;
Packit Service 20376f
	backend->header.parent.open = config_readonly_open;
Packit Service 20376f
	backend->header.parent.get = config_get;
Packit Service 20376f
	backend->header.parent.set = config_set_readonly;
Packit Service 20376f
	backend->header.parent.set_multivar = config_set_multivar_readonly;
Packit Service 20376f
	backend->header.parent.del = config_delete_readonly;
Packit Service 20376f
	backend->header.parent.del_multivar = config_delete_multivar_readonly;
Packit Service 20376f
	backend->header.parent.iterator = config_iterator_new;
Packit Service 20376f
	backend->header.parent.lock = config_lock_readonly;
Packit Service 20376f
	backend->header.parent.unlock = config_unlock_readonly;
Packit Service 20376f
	backend->header.parent.free = backend_readonly_free;
Packit Service 20376f
Packit Service 20376f
	*out = (git_config_backend *)backend;
Packit Service 20376f
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int reader_getchar_raw(struct reader *reader)
Packit Service 20376f
{
Packit Service 20376f
	int c;
Packit Service 20376f
Packit Service 20376f
	c = *reader->read_ptr++;
Packit Service 20376f
Packit Service 20376f
	/*
Packit Service 20376f
	Win 32 line breaks: if we find a \r\n sequence,
Packit Service 20376f
	return only the \n as a newline
Packit Service 20376f
	*/
Packit Service 20376f
	if (c == '\r' && *reader->read_ptr == '\n') {
Packit Service 20376f
		reader->read_ptr++;
Packit Service 20376f
		c = '\n';
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if (c == '\n')
Packit Service 20376f
		reader->line_number++;
Packit Service 20376f
Packit Service 20376f
	if (c == 0) {
Packit Service 20376f
		reader->eof = 1;
Packit Service 20376f
		c = '\0';
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	return c;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
#define SKIP_WHITESPACE (1 << 1)
Packit Service 20376f
#define SKIP_COMMENTS (1 << 2)
Packit Service 20376f
Packit Service 20376f
static int reader_getchar(struct reader *reader, int flags)
Packit Service 20376f
{
Packit Service 20376f
	const int skip_whitespace = (flags & SKIP_WHITESPACE);
Packit Service 20376f
	const int skip_comments = (flags & SKIP_COMMENTS);
Packit Service 20376f
	int c;
Packit Service 20376f
Packit Service 20376f
	assert(reader->read_ptr);
Packit Service 20376f
Packit Service 20376f
	do {
Packit Service 20376f
		c = reader_getchar_raw(reader);
Packit Service 20376f
	} while (c != '\n' && c != '\0' && skip_whitespace && git__isspace(c));
Packit Service 20376f
Packit Service 20376f
	if (skip_comments && (c == '#' || c == ';')) {
Packit Service 20376f
		do {
Packit Service 20376f
			c = reader_getchar_raw(reader);
Packit Service 20376f
		} while (c != '\n' && c != '\0');
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	return c;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
/*
Packit Service 20376f
 * Read the next char, but don't move the reading pointer.
Packit Service 20376f
 */
Packit Service 20376f
static int reader_peek(struct reader *reader, int flags)
Packit Service 20376f
{
Packit Service 20376f
	void *old_read_ptr;
Packit Service 20376f
	int old_lineno, old_eof;
Packit Service 20376f
	int ret;
Packit Service 20376f
Packit Service 20376f
	assert(reader->read_ptr);
Packit Service 20376f
Packit Service 20376f
	old_read_ptr = reader->read_ptr;
Packit Service 20376f
	old_lineno = reader->line_number;
Packit Service 20376f
	old_eof = reader->eof;
Packit Service 20376f
Packit Service 20376f
	ret = reader_getchar(reader, flags);
Packit Service 20376f
Packit Service 20376f
	reader->read_ptr = old_read_ptr;
Packit Service 20376f
	reader->line_number = old_lineno;
Packit Service 20376f
	reader->eof = old_eof;
Packit Service 20376f
Packit Service 20376f
	return ret;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
/*
Packit Service 20376f
 * Read and consume a line, returning it in newly-allocated memory.
Packit Service 20376f
 */
Packit Service 20376f
static char *reader_readline(struct reader *reader, bool skip_whitespace)
Packit Service 20376f
{
Packit Service 20376f
	char *line = NULL;
Packit Service 20376f
	char *line_src, *line_end;
Packit Service 20376f
	size_t line_len, alloc_len;
Packit Service 20376f
Packit Service 20376f
	line_src = reader->read_ptr;
Packit Service 20376f
Packit Service 20376f
	if (skip_whitespace) {
Packit Service 20376f
		/* Skip empty empty lines */
Packit Service 20376f
		while (git__isspace(*line_src))
Packit Service 20376f
			++line_src;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	line_end = strchr(line_src, '\n');
Packit Service 20376f
Packit Service 20376f
	/* no newline at EOF */
Packit Service 20376f
	if (line_end == NULL)
Packit Service 20376f
		line_end = strchr(line_src, 0);
Packit Service 20376f
Packit Service 20376f
	line_len = line_end - line_src;
Packit Service 20376f
Packit Service 20376f
	if (GIT_ADD_SIZET_OVERFLOW(&alloc_len, line_len, 1) ||
Packit Service 20376f
		(line = git__malloc(alloc_len)) == NULL) {
Packit Service 20376f
		return NULL;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	memcpy(line, line_src, line_len);
Packit Service 20376f
Packit Service 20376f
	do line[line_len] = '\0';
Packit Service 20376f
	while (line_len-- > 0 && git__isspace(line[line_len]));
Packit Service 20376f
Packit Service 20376f
	if (*line_end == '\n')
Packit Service 20376f
		line_end++;
Packit Service 20376f
Packit Service 20376f
	if (*line_end == '\0')
Packit Service 20376f
		reader->eof = 1;
Packit Service 20376f
Packit Service 20376f
	reader->line_number++;
Packit Service 20376f
	reader->read_ptr = line_end;
Packit Service 20376f
Packit Service 20376f
	return line;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
/*
Packit Service 20376f
 * Consume a line, without storing it anywhere
Packit Service 20376f
 */
Packit Service 20376f
static void reader_consume_line(struct reader *reader)
Packit Service 20376f
{
Packit Service 20376f
	char *line_start, *line_end;
Packit Service 20376f
Packit Service 20376f
	line_start = reader->read_ptr;
Packit Service 20376f
	line_end = strchr(line_start, '\n');
Packit Service 20376f
	/* No newline at EOF */
Packit Service 20376f
	if(line_end == NULL){
Packit Service 20376f
		line_end = strchr(line_start, '\0');
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if (*line_end == '\n')
Packit Service 20376f
		line_end++;
Packit Service 20376f
Packit Service 20376f
	if (*line_end == '\0')
Packit Service 20376f
		reader->eof = 1;
Packit Service 20376f
Packit Service 20376f
	reader->line_number++;
Packit Service 20376f
	reader->read_ptr = line_end;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
GIT_INLINE(int) config_keychar(int c)
Packit Service 20376f
{
Packit Service 20376f
	return isalnum(c) || c == '-';
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int parse_section_header_ext(struct reader *reader, const char *line, const char *base_name, char **section_name)
Packit Service 20376f
{
Packit Service 20376f
	int c, rpos;
Packit Service 20376f
	char *first_quote, *last_quote;
Packit Service 20376f
	git_buf buf = GIT_BUF_INIT;
Packit Service 20376f
	size_t quoted_len, alloc_len, base_name_len = strlen(base_name);
Packit Service 20376f
Packit Service 20376f
	/*
Packit Service 20376f
	 * base_name is what came before the space. We should be at the
Packit Service 20376f
	 * first quotation mark, except for now, line isn't being kept in
Packit Service 20376f
	 * sync so we only really use it to calculate the length.
Packit Service 20376f
	 */
Packit Service 20376f
Packit Service 20376f
	first_quote = strchr(line, '"');
Packit Service 20376f
	if (first_quote == NULL) {
Packit Service 20376f
		set_parse_error(reader, 0, "Missing quotation marks in section header");
Packit Service 20376f
		goto end_error;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	last_quote = strrchr(line, '"');
Packit Service 20376f
	quoted_len = last_quote - first_quote;
Packit Service 20376f
Packit Service 20376f
	if (quoted_len == 0) {
Packit Service 20376f
		set_parse_error(reader, 0, "Missing closing quotation mark in section header");
Packit Service 20376f
		goto end_error;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	GITERR_CHECK_ALLOC_ADD(&alloc_len, base_name_len, quoted_len);
Packit Service 20376f
	GITERR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 2);
Packit Service 20376f
Packit Service 20376f
	if (git_buf_grow(&buf, alloc_len) < 0 ||
Packit Service 20376f
	    git_buf_printf(&buf, "%s.", base_name) < 0)
Packit Service 20376f
		goto end_error;
Packit Service 20376f
Packit Service 20376f
	rpos = 0;
Packit Service 20376f
Packit Service 20376f
	line = first_quote;
Packit Service 20376f
	c = line[++rpos];
Packit Service 20376f
Packit Service 20376f
	/*
Packit Service 20376f
	 * At the end of each iteration, whatever is stored in c will be
Packit Service 20376f
	 * added to the string. In case of error, jump to out
Packit Service 20376f
	 */
Packit Service 20376f
	do {
Packit Service 20376f
Packit Service 20376f
		switch (c) {
Packit Service 20376f
		case 0:
Packit Service 20376f
			set_parse_error(reader, 0, "Unexpected end-of-line in section header");
Packit Service 20376f
			goto end_error;
Packit Service 20376f
Packit Service 20376f
		case '"':
Packit Service 20376f
			goto end_parse;
Packit Service 20376f
Packit Service 20376f
		case '\\':
Packit Service 20376f
			c = line[++rpos];
Packit Service 20376f
Packit Service 20376f
			if (c == 0) {
Packit Service 20376f
				set_parse_error(reader, rpos, "Unexpected end-of-line in section header");
Packit Service 20376f
				goto end_error;
Packit Service 20376f
			}
Packit Service 20376f
Packit Service 20376f
		default:
Packit Service 20376f
			break;
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		git_buf_putc(&buf, (char)c);
Packit Service 20376f
		c = line[++rpos];
Packit Service 20376f
	} while (line + rpos < last_quote);
Packit Service 20376f
Packit Service 20376f
end_parse:
Packit Service 20376f
	if (git_buf_oom(&buf))
Packit Service 20376f
		goto end_error;
Packit Service 20376f
Packit Service 20376f
	if (line[rpos] != '"' || line[rpos + 1] != ']') {
Packit Service 20376f
		set_parse_error(reader, rpos, "Unexpected text after closing quotes");
Packit Service 20376f
		git_buf_free(&buf;;
Packit Service 20376f
		return -1;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	*section_name = git_buf_detach(&buf;;
Packit Service 20376f
	return 0;
Packit Service 20376f
Packit Service 20376f
end_error:
Packit Service 20376f
	git_buf_free(&buf;;
Packit Service 20376f
Packit Service 20376f
	return -1;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int parse_section_header(struct reader *reader, char **section_out)
Packit Service 20376f
{
Packit Service 20376f
	char *name, *name_end;
Packit Service 20376f
	int name_length, c, pos;
Packit Service 20376f
	int result;
Packit Service 20376f
	char *line;
Packit Service 20376f
	size_t line_len;
Packit Service 20376f
Packit Service 20376f
	line = reader_readline(reader, true);
Packit Service 20376f
	if (line == NULL)
Packit Service 20376f
		return -1;
Packit Service 20376f
Packit Service 20376f
	/* find the end of the variable's name */
Packit Service 20376f
	name_end = strrchr(line, ']');
Packit Service 20376f
	if (name_end == NULL) {
Packit Service 20376f
		git__free(line);
Packit Service 20376f
		set_parse_error(reader, 0, "Missing ']' in section header");
Packit Service 20376f
		return -1;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	GITERR_CHECK_ALLOC_ADD(&line_len, (size_t)(name_end - line), 1);
Packit Service 20376f
	name = git__malloc(line_len);
Packit Service 20376f
	GITERR_CHECK_ALLOC(name);
Packit Service 20376f
Packit Service 20376f
	name_length = 0;
Packit Service 20376f
	pos = 0;
Packit Service 20376f
Packit Service 20376f
	/* Make sure we were given a section header */
Packit Service 20376f
	c = line[pos++];
Packit Service 20376f
	assert(c == '[');
Packit Service 20376f
Packit Service 20376f
	c = line[pos++];
Packit Service 20376f
Packit Service 20376f
	do {
Packit Service 20376f
		if (git__isspace(c)){
Packit Service 20376f
			name[name_length] = '\0';
Packit Service 20376f
			result = parse_section_header_ext(reader, line, name, section_out);
Packit Service 20376f
			git__free(line);
Packit Service 20376f
			git__free(name);
Packit Service 20376f
			return result;
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		if (!config_keychar(c) && c != '.') {
Packit Service 20376f
			set_parse_error(reader, pos, "Unexpected character in header");
Packit Service 20376f
			goto fail_parse;
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		name[name_length++] = (char)git__tolower(c);
Packit Service 20376f
Packit Service 20376f
	} while ((c = line[pos++]) != ']');
Packit Service 20376f
Packit Service 20376f
	if (line[pos - 1] != ']') {
Packit Service 20376f
		set_parse_error(reader, pos, "Unexpected end of file");
Packit Service 20376f
		goto fail_parse;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	git__free(line);
Packit Service 20376f
Packit Service 20376f
	name[name_length] = 0;
Packit Service 20376f
	*section_out = name;
Packit Service 20376f
Packit Service 20376f
	return 0;
Packit Service 20376f
Packit Service 20376f
fail_parse:
Packit Service 20376f
	git__free(line);
Packit Service 20376f
	git__free(name);
Packit Service 20376f
	return -1;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int skip_bom(struct reader *reader)
Packit Service 20376f
{
Packit Service 20376f
	git_bom_t bom;
Packit Service 20376f
	int bom_offset = git_buf_text_detect_bom(&bom,
Packit Service 20376f
		&reader->buffer, reader->read_ptr - reader->buffer.ptr);
Packit Service 20376f
Packit Service 20376f
	if (bom == GIT_BOM_UTF8)
Packit Service 20376f
		reader->read_ptr += bom_offset;
Packit Service 20376f
Packit Service 20376f
	/* TODO: reference implementation is pretty stupid with BoM */
Packit Service 20376f
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
/*
Packit Service 20376f
	(* basic types *)
Packit Service 20376f
	digit = "0".."9"
Packit Service 20376f
	integer = digit { digit }
Packit Service 20376f
	alphabet = "a".."z" + "A" .. "Z"
Packit Service 20376f
Packit Service 20376f
	section_char = alphabet | "." | "-"
Packit Service 20376f
	extension_char = (* any character except newline *)
Packit Service 20376f
	any_char = (* any character *)
Packit Service 20376f
	variable_char = "alphabet" | "-"
Packit Service 20376f
Packit Service 20376f
Packit Service 20376f
	(* actual grammar *)
Packit Service 20376f
	config = { section }
Packit Service 20376f
Packit Service 20376f
	section = header { definition }
Packit Service 20376f
Packit Service 20376f
	header = "[" section [subsection | subsection_ext] "]"
Packit Service 20376f
Packit Service 20376f
	subsection = "." section
Packit Service 20376f
	subsection_ext = "\"" extension "\""
Packit Service 20376f
Packit Service 20376f
	section = section_char { section_char }
Packit Service 20376f
	extension = extension_char { extension_char }
Packit Service 20376f
Packit Service 20376f
	definition = variable_name ["=" variable_value] "\n"
Packit Service 20376f
Packit Service 20376f
	variable_name = variable_char { variable_char }
Packit Service 20376f
	variable_value = string | boolean | integer
Packit Service 20376f
Packit Service 20376f
	string = quoted_string | plain_string
Packit Service 20376f
	quoted_string = "\"" plain_string "\""
Packit Service 20376f
	plain_string = { any_char }
Packit Service 20376f
Packit Service 20376f
	boolean = boolean_true | boolean_false
Packit Service 20376f
	boolean_true = "yes" | "1" | "true" | "on"
Packit Service 20376f
	boolean_false = "no" | "0" | "false" | "off"
Packit Service 20376f
*/
Packit Service 20376f
Packit Service 20376f
static int strip_comments(char *line, int in_quotes)
Packit Service 20376f
{
Packit Service 20376f
	int quote_count = in_quotes, backslash_count = 0;
Packit Service 20376f
	char *ptr;
Packit Service 20376f
Packit Service 20376f
	for (ptr = line; *ptr; ++ptr) {
Packit Service 20376f
		if (ptr[0] == '"' && ptr > line && ptr[-1] != '\\')
Packit Service 20376f
			quote_count++;
Packit Service 20376f
Packit Service 20376f
		if ((ptr[0] == ';' || ptr[0] == '#') &&
Packit Service 20376f
			(quote_count % 2) == 0 &&
Packit Service 20376f
			(backslash_count % 2) == 0) {
Packit Service 20376f
			ptr[0] = '\0';
Packit Service 20376f
			break;
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		if (ptr[0] == '\\')
Packit Service 20376f
			backslash_count++;
Packit Service 20376f
		else
Packit Service 20376f
			backslash_count = 0;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	/* skip any space at the end */
Packit Service 20376f
	while (ptr > line && git__isspace(ptr[-1])) {
Packit Service 20376f
		ptr--;
Packit Service 20376f
	}
Packit Service 20376f
	ptr[0] = '\0';
Packit Service 20376f
Packit Service 20376f
	return quote_count;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int included_path(git_buf *out, const char *dir, const char *path)
Packit Service 20376f
{
Packit Service 20376f
	/* From the user's home */
Packit Service 20376f
	if (path[0] == '~' && path[1] == '/')
Packit Service 20376f
		return git_sysdir_expand_global_file(out, &path[1]);
Packit Service 20376f
Packit Service 20376f
	return git_path_join_unrooted(out, path, dir, NULL);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static const char *escapes = "ntb\"\\";
Packit Service 20376f
static const char *escaped = "\n\t\b\"\\";
Packit Service 20376f
Packit Service 20376f
/* Escape the values to write them to the file */
Packit Service 20376f
static char *escape_value(const char *ptr)
Packit Service 20376f
{
Packit Service 20376f
	git_buf buf;
Packit Service 20376f
	size_t len;
Packit Service 20376f
	const char *esc;
Packit Service 20376f
Packit Service 20376f
	assert(ptr);
Packit Service 20376f
Packit Service 20376f
	len = strlen(ptr);
Packit Service 20376f
	if (!len)
Packit Service 20376f
		return git__calloc(1, sizeof(char));
Packit Service 20376f
Packit Service 20376f
	if (git_buf_init(&buf, len) < 0)
Packit Service 20376f
		return NULL;
Packit Service 20376f
Packit Service 20376f
	while (*ptr != '\0') {
Packit Service 20376f
		if ((esc = strchr(escaped, *ptr)) != NULL) {
Packit Service 20376f
			git_buf_putc(&buf, '\\');
Packit Service 20376f
			git_buf_putc(&buf, escapes[esc - escaped]);
Packit Service 20376f
		} else {
Packit Service 20376f
			git_buf_putc(&buf, *ptr);
Packit Service 20376f
		}
Packit Service 20376f
		ptr++;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if (git_buf_oom(&buf)) {
Packit Service 20376f
		git_buf_free(&buf;;
Packit Service 20376f
		return NULL;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	return git_buf_detach(&buf;;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
/* '\"' -> '"' etc */
Packit Service 20376f
static int unescape_line(
Packit Service 20376f
	char **out, bool *is_multi, const char *ptr, int quote_count)
Packit Service 20376f
{
Packit Service 20376f
	char *str, *fixed, *esc;
Packit Service 20376f
	size_t ptr_len = strlen(ptr), alloc_len;
Packit Service 20376f
Packit Service 20376f
	*is_multi = false;
Packit Service 20376f
Packit Service 20376f
	if (GIT_ADD_SIZET_OVERFLOW(&alloc_len, ptr_len, 1) ||
Packit Service 20376f
		(str = git__malloc(alloc_len)) == NULL) {
Packit Service 20376f
		return -1;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	fixed = str;
Packit Service 20376f
Packit Service 20376f
	while (*ptr != '\0') {
Packit Service 20376f
		if (*ptr == '"') {
Packit Service 20376f
			quote_count++;
Packit Service 20376f
		} else if (*ptr != '\\') {
Packit Service 20376f
			*fixed++ = *ptr;
Packit Service 20376f
		} else {
Packit Service 20376f
			/* backslash, check the next char */
Packit Service 20376f
			ptr++;
Packit Service 20376f
			/* if we're at the end, it's a multiline, so keep the backslash */
Packit Service 20376f
			if (*ptr == '\0') {
Packit Service 20376f
				*is_multi = true;
Packit Service 20376f
				goto done;
Packit Service 20376f
			}
Packit Service 20376f
			if ((esc = strchr(escapes, *ptr)) != NULL) {
Packit Service 20376f
				*fixed++ = escaped[esc - escapes];
Packit Service 20376f
			} else {
Packit Service 20376f
				git__free(str);
Packit Service 20376f
				giterr_set(GITERR_CONFIG, "invalid escape at %s", ptr);
Packit Service 20376f
				return -1;
Packit Service 20376f
			}
Packit Service 20376f
		}
Packit Service 20376f
		ptr++;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
done:
Packit Service 20376f
	*fixed = '\0';
Packit Service 20376f
	*out = str;
Packit Service 20376f
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int parse_multiline_variable(struct reader *reader, git_buf *value, int in_quotes)
Packit Service 20376f
{
Packit Service 20376f
	int quote_count;
Packit Service 20376f
	bool multiline = true;
Packit Service 20376f
Packit Service 20376f
	while (multiline) {
Packit Service 20376f
		char *line = NULL, *proc_line = NULL;
Packit Service 20376f
		int error;
Packit Service 20376f
Packit Service 20376f
		/* Check that the next line exists */
Packit Service 20376f
		line = reader_readline(reader, false);
Packit Service 20376f
		GITERR_CHECK_ALLOC(line);
Packit Service 20376f
Packit Service 20376f
		/*
Packit Service 20376f
		 * We've reached the end of the file, there is no continuation.
Packit Service 20376f
		 * (this is not an error).
Packit Service 20376f
		 */
Packit Service 20376f
		if (line[0] == '\0') {
Packit Service 20376f
			error = 0;
Packit Service 20376f
			goto out;
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		/* If it was just a comment, pretend it didn't exist */
Packit Service 20376f
		quote_count = strip_comments(line, !!in_quotes);
Packit Service 20376f
		if (line[0] == '\0')
Packit Service 20376f
			goto next;
Packit Service 20376f
Packit Service 20376f
		if ((error = unescape_line(&proc_line, &multiline,
Packit Service 20376f
					   line, in_quotes)) < 0)
Packit Service 20376f
			goto out;
Packit Service 20376f
Packit Service 20376f
		/* Add this line to the multiline var */
Packit Service 20376f
		if ((error = git_buf_puts(value, proc_line)) < 0)
Packit Service 20376f
			goto out;
Packit Service 20376f
Packit Service 20376f
next:
Packit Service 20376f
		git__free(line);
Packit Service 20376f
		git__free(proc_line);
Packit Service 20376f
		in_quotes = quote_count;
Packit Service 20376f
		continue;
Packit Service 20376f
Packit Service 20376f
out:
Packit Service 20376f
		git__free(line);
Packit Service 20376f
		git__free(proc_line);
Packit Service 20376f
		return error;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
GIT_INLINE(bool) is_namechar(char c)
Packit Service 20376f
{
Packit Service 20376f
	return isalnum(c) || c == '-';
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int parse_name(
Packit Service 20376f
	char **name, const char **value, struct reader *reader, const char *line)
Packit Service 20376f
{
Packit Service 20376f
	const char *name_end = line, *value_start;
Packit Service 20376f
Packit Service 20376f
	*name = NULL;
Packit Service 20376f
	*value = NULL;
Packit Service 20376f
Packit Service 20376f
	while (*name_end && is_namechar(*name_end))
Packit Service 20376f
		name_end++;
Packit Service 20376f
Packit Service 20376f
	if (line == name_end) {
Packit Service 20376f
		set_parse_error(reader, 0, "Invalid configuration key");
Packit Service 20376f
		return -1;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	value_start = name_end;
Packit Service 20376f
Packit Service 20376f
	while (*value_start && git__isspace(*value_start))
Packit Service 20376f
		value_start++;
Packit Service 20376f
Packit Service 20376f
	if (*value_start == '=') {
Packit Service 20376f
		*value = value_start + 1;
Packit Service 20376f
	} else if (*value_start) {
Packit Service 20376f
		set_parse_error(reader, 0, "Invalid configuration key");
Packit Service 20376f
		return -1;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if ((*name = git__strndup(line, name_end - line)) == NULL)
Packit Service 20376f
		return -1;
Packit Service 20376f
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int parse_variable(struct reader *reader, char **var_name, char **var_value)
Packit Service 20376f
{
Packit Service 20376f
	const char *value_start = NULL;
Packit Service 20376f
	char *line;
Packit Service 20376f
	int quote_count;
Packit Service 20376f
	bool multiline;
Packit Service 20376f
Packit Service 20376f
	line = reader_readline(reader, true);
Packit Service 20376f
	if (line == NULL)
Packit Service 20376f
		return -1;
Packit Service 20376f
Packit Service 20376f
	quote_count = strip_comments(line, 0);
Packit Service 20376f
Packit Service 20376f
	/* If there is no value, boolean true is assumed */
Packit Service 20376f
	*var_value = NULL;
Packit Service 20376f
Packit Service 20376f
	if (parse_name(var_name, &value_start, reader, line) < 0)
Packit Service 20376f
		goto on_error;
Packit Service 20376f
Packit Service 20376f
	/*
Packit Service 20376f
	 * Now, let's try to parse the value
Packit Service 20376f
	 */
Packit Service 20376f
	if (value_start != NULL) {
Packit Service 20376f
		while (git__isspace(value_start[0]))
Packit Service 20376f
			value_start++;
Packit Service 20376f
Packit Service 20376f
		if (unescape_line(var_value, &multiline, value_start, 0) < 0)
Packit Service 20376f
			goto on_error;
Packit Service 20376f
Packit Service 20376f
		if (multiline) {
Packit Service 20376f
			git_buf multi_value = GIT_BUF_INIT;
Packit Service 20376f
			git_buf_attach(&multi_value, *var_value, 0);
Packit Service 20376f
Packit Service 20376f
			if (parse_multiline_variable(reader, &multi_value, quote_count) < 0 ||
Packit Service 20376f
				git_buf_oom(&multi_value)) {
Packit Service 20376f
				git_buf_free(&multi_value);
Packit Service 20376f
				goto on_error;
Packit Service 20376f
			}
Packit Service 20376f
Packit Service 20376f
			*var_value = git_buf_detach(&multi_value);
Packit Service 20376f
		}
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	git__free(line);
Packit Service 20376f
	return 0;
Packit Service 20376f
Packit Service 20376f
on_error:
Packit Service 20376f
	git__free(*var_name);
Packit Service 20376f
	git__free(line);
Packit Service 20376f
	return -1;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int config_parse(
Packit Service 20376f
	struct reader *reader,
Packit Service 20376f
	int (*on_section)(struct reader **reader, const char *current_section, const char *line, size_t line_len, void *data),
Packit Service 20376f
	int (*on_variable)(struct reader **reader, const char *current_section, char *var_name, char *var_value, const char *line, size_t line_len, void *data),
Packit Service 20376f
	int (*on_comment)(struct reader **reader, const char *line, size_t line_len, void *data),
Packit Service 20376f
	int (*on_eof)(struct reader **reader, const char *current_section, void *data),
Packit Service 20376f
	void *data)
Packit Service 20376f
{
Packit Service 20376f
	char *current_section = NULL, *var_name, *var_value, *line_start;
Packit Service 20376f
	char c;
Packit Service 20376f
	size_t line_len;
Packit Service 20376f
	int result = 0;
Packit Service 20376f
Packit Service 20376f
	skip_bom(reader);
Packit Service 20376f
Packit Service 20376f
	while (result == 0 && !reader->eof) {
Packit Service 20376f
		line_start = reader->read_ptr;
Packit Service 20376f
Packit Service 20376f
		c = reader_peek(reader, SKIP_WHITESPACE);
Packit Service 20376f
Packit Service 20376f
		switch (c) {
Packit Service 20376f
		case '\0': /* EOF when peeking, set EOF in the reader to exit the loop */
Packit Service 20376f
			reader->eof = 1;
Packit Service 20376f
			break;
Packit Service 20376f
Packit Service 20376f
		case '[': /* section header, new section begins */
Packit Service 20376f
			git__free(current_section);
Packit Service 20376f
			current_section = NULL;
Packit Service 20376f
Packit Service 20376f
			if ((result = parse_section_header(reader, &current_section)) == 0 && on_section) {
Packit Service 20376f
				line_len = reader->read_ptr - line_start;
Packit Service 20376f
				result = on_section(&reader, current_section, line_start, line_len, data);
Packit Service 20376f
			}
Packit Service 20376f
			break;
Packit Service 20376f
Packit Service 20376f
		case '\n': /* comment or whitespace-only */
Packit Service 20376f
		case ';':
Packit Service 20376f
		case '#':
Packit Service 20376f
			reader_consume_line(reader);
Packit Service 20376f
Packit Service 20376f
			if (on_comment) {
Packit Service 20376f
				line_len = reader->read_ptr - line_start;
Packit Service 20376f
				result = on_comment(&reader, line_start, line_len, data);
Packit Service 20376f
			}
Packit Service 20376f
			break;
Packit Service 20376f
Packit Service 20376f
		default: /* assume variable declaration */
Packit Service 20376f
			if ((result = parse_variable(reader, &var_name, &var_value)) == 0 && on_variable) {
Packit Service 20376f
				line_len = reader->read_ptr - line_start;
Packit Service 20376f
				result = on_variable(&reader, current_section, var_name, var_value, line_start, line_len, data);
Packit Service 20376f
			}
Packit Service 20376f
			break;
Packit Service 20376f
		}
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if (on_eof)
Packit Service 20376f
		result = on_eof(&reader, current_section, data);
Packit Service 20376f
Packit Service 20376f
	git__free(current_section);
Packit Service 20376f
	return result;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
struct parse_data {
Packit Service 20376f
	git_strmap *values;
Packit Service 20376f
	diskfile_backend *cfg_file;
Packit Service 20376f
	uint32_t reader_idx;
Packit Service 20376f
	git_config_level_t level;
Packit Service 20376f
	int depth;
Packit Service 20376f
};
Packit Service 20376f
Packit Service 20376f
static int read_on_variable(
Packit Service 20376f
	struct reader **reader,
Packit Service 20376f
	const char *current_section,
Packit Service 20376f
	char *var_name,
Packit Service 20376f
	char *var_value,
Packit Service 20376f
	const char *line,
Packit Service 20376f
	size_t line_len,
Packit Service 20376f
	void *data)
Packit Service 20376f
{
Packit Service 20376f
	struct parse_data *parse_data = (struct parse_data *)data;
Packit Service 20376f
	git_buf buf = GIT_BUF_INIT;
Packit Service 20376f
	cvar_t *var;
Packit Service 20376f
	int result = 0;
Packit Service 20376f
Packit Service 20376f
	GIT_UNUSED(line);
Packit Service 20376f
	GIT_UNUSED(line_len);
Packit Service 20376f
Packit Service 20376f
	git__strtolower(var_name);
Packit Service 20376f
	git_buf_printf(&buf, "%s.%s", current_section, var_name);
Packit Service 20376f
	git__free(var_name);
Packit Service 20376f
Packit Service 20376f
	if (git_buf_oom(&buf)) {
Packit Service 20376f
		git__free(var_value);
Packit Service 20376f
		return -1;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	var = git__calloc(1, sizeof(cvar_t));
Packit Service 20376f
	GITERR_CHECK_ALLOC(var);
Packit Service 20376f
	var->entry = git__calloc(1, sizeof(git_config_entry));
Packit Service 20376f
	GITERR_CHECK_ALLOC(var->entry);
Packit Service 20376f
Packit Service 20376f
	var->entry->name = git_buf_detach(&buf;;
Packit Service 20376f
	var->entry->value = var_value;
Packit Service 20376f
	var->entry->level = parse_data->level;
Packit Service 20376f
	var->included = !!parse_data->depth;
Packit Service 20376f
Packit Service 20376f
	if ((result = append_entry(parse_data->values, var)) < 0)
Packit Service 20376f
		return result;
Packit Service 20376f
Packit Service 20376f
	result = 0;
Packit Service 20376f
Packit Service 20376f
	/* Add or append the new config option */
Packit Service 20376f
	if (!git__strcmp(var->entry->name, "include.path") && var->entry->value) {
Packit Service 20376f
		struct reader *r;
Packit Service 20376f
		git_buf path = GIT_BUF_INIT;
Packit Service 20376f
		char *dir;
Packit Service 20376f
		uint32_t index;
Packit Service 20376f
Packit Service 20376f
		r = git_array_alloc(parse_data->cfg_file->readers);
Packit Service 20376f
		/* The reader may have been reallocated */
Packit Service 20376f
		*reader = git_array_get(parse_data->cfg_file->readers, parse_data->reader_idx);
Packit Service 20376f
		memset(r, 0, sizeof(struct reader));
Packit Service 20376f
Packit Service 20376f
		if ((result = git_path_dirname_r(&path, (*reader)->file_path)) < 0)
Packit Service 20376f
			return result;
Packit Service 20376f
Packit Service 20376f
		/* We need to know our index in the array, as the next config_parse call may realloc */
Packit Service 20376f
		index = git_array_size(parse_data->cfg_file->readers) - 1;
Packit Service 20376f
		dir = git_buf_detach(&path);
Packit Service 20376f
		result = included_path(&path, dir, var->entry->value);
Packit Service 20376f
		git__free(dir);
Packit Service 20376f
Packit Service 20376f
		if (result < 0)
Packit Service 20376f
			return result;
Packit Service 20376f
Packit Service 20376f
		r->file_path = git_buf_detach(&path);
Packit Service 20376f
		git_buf_init(&r->buffer, 0);
Packit Service 20376f
Packit Service 20376f
		result = git_futils_readbuffer_updated(
Packit Service 20376f
			&r->buffer, r->file_path, &r->checksum, NULL);
Packit Service 20376f
Packit Service 20376f
		if (result == 0) {
Packit Service 20376f
			result = config_read(parse_data->values, parse_data->cfg_file, r, parse_data->level, parse_data->depth+1);
Packit Service 20376f
			r = git_array_get(parse_data->cfg_file->readers, index);
Packit Service 20376f
			*reader = git_array_get(parse_data->cfg_file->readers, parse_data->reader_idx);
Packit Service 20376f
		} else if (result == GIT_ENOTFOUND) {
Packit Service 20376f
			giterr_clear();
Packit Service 20376f
			result = 0;
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		git_buf_free(&r->buffer);
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	return result;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int config_read(git_strmap *values, diskfile_backend *cfg_file, struct reader *reader, git_config_level_t level, int depth)
Packit Service 20376f
{
Packit Service 20376f
	struct parse_data parse_data;
Packit Service 20376f
Packit Service 20376f
	if (depth >= MAX_INCLUDE_DEPTH) {
Packit Service 20376f
		giterr_set(GITERR_CONFIG, "maximum config include depth reached");
Packit Service 20376f
		return -1;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	/* Initialize the reading position */
Packit Service 20376f
	reader->read_ptr = reader->buffer.ptr;
Packit Service 20376f
	reader->eof = 0;
Packit Service 20376f
Packit Service 20376f
	/* If the file is empty, there's nothing for us to do */
Packit Service 20376f
	if (*reader->read_ptr == '\0')
Packit Service 20376f
		return 0;
Packit Service 20376f
Packit Service 20376f
	parse_data.values = values;
Packit Service 20376f
	parse_data.cfg_file = cfg_file;
Packit Service 20376f
	parse_data.reader_idx = git_array_size(cfg_file->readers) - 1;
Packit Service 20376f
	parse_data.level = level;
Packit Service 20376f
	parse_data.depth = depth;
Packit Service 20376f
Packit Service 20376f
	return config_parse(reader, NULL, read_on_variable, NULL, NULL, &parse_data);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int write_section(git_buf *fbuf, const char *key)
Packit Service 20376f
{
Packit Service 20376f
	int result;
Packit Service 20376f
	const char *dot;
Packit Service 20376f
	git_buf buf = GIT_BUF_INIT;
Packit Service 20376f
Packit Service 20376f
	/* All of this just for [section "subsection"] */
Packit Service 20376f
	dot = strchr(key, '.');
Packit Service 20376f
	git_buf_putc(&buf, '[');
Packit Service 20376f
	if (dot == NULL) {
Packit Service 20376f
		git_buf_puts(&buf, key);
Packit Service 20376f
	} else {
Packit Service 20376f
		char *escaped;
Packit Service 20376f
		git_buf_put(&buf, key, dot - key);
Packit Service 20376f
		escaped = escape_value(dot + 1);
Packit Service 20376f
		GITERR_CHECK_ALLOC(escaped);
Packit Service 20376f
		git_buf_printf(&buf, " \"%s\"", escaped);
Packit Service 20376f
		git__free(escaped);
Packit Service 20376f
	}
Packit Service 20376f
	git_buf_puts(&buf, "]\n");
Packit Service 20376f
Packit Service 20376f
	if (git_buf_oom(&buf))
Packit Service 20376f
		return -1;
Packit Service 20376f
Packit Service 20376f
	result = git_buf_put(fbuf, git_buf_cstr(&buf), buf.size);
Packit Service 20376f
	git_buf_free(&buf;;
Packit Service 20376f
Packit Service 20376f
	return result;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static const char *quotes_for_value(const char *value)
Packit Service 20376f
{
Packit Service 20376f
	const char *ptr;
Packit Service 20376f
Packit Service 20376f
	if (value[0] == ' ' || value[0] == '\0')
Packit Service 20376f
		return "\"";
Packit Service 20376f
Packit Service 20376f
	for (ptr = value; *ptr; ++ptr) {
Packit Service 20376f
		if (*ptr == ';' || *ptr == '#')
Packit Service 20376f
			return "\"";
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if (ptr[-1] == ' ')
Packit Service 20376f
		return "\"";
Packit Service 20376f
Packit Service 20376f
	return "";
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
struct write_data {
Packit Service 20376f
	git_buf *buf;
Packit Service 20376f
	git_buf buffered_comment;
Packit Service 20376f
	unsigned int in_section : 1,
Packit Service 20376f
		preg_replaced : 1;
Packit Service 20376f
	const char *section;
Packit Service 20376f
	const char *name;
Packit Service 20376f
	const regex_t *preg;
Packit Service 20376f
	const char *value;
Packit Service 20376f
};
Packit Service 20376f
Packit Service 20376f
static int write_line_to(git_buf *buf, const char *line, size_t line_len)
Packit Service 20376f
{
Packit Service 20376f
	int result = git_buf_put(buf, line, line_len);
Packit Service 20376f
Packit Service 20376f
	if (!result && line_len && line[line_len-1] != '\n')
Packit Service 20376f
		result = git_buf_printf(buf, "\n");
Packit Service 20376f
Packit Service 20376f
	return result;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int write_line(struct write_data *write_data, const char *line, size_t line_len)
Packit Service 20376f
{
Packit Service 20376f
	return write_line_to(write_data->buf, line, line_len);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int write_value(struct write_data *write_data)
Packit Service 20376f
{
Packit Service 20376f
	const char *q;
Packit Service 20376f
	int result;
Packit Service 20376f
Packit Service 20376f
	q = quotes_for_value(write_data->value);
Packit Service 20376f
	result = git_buf_printf(write_data->buf,
Packit Service 20376f
		"\t%s = %s%s%s\n", write_data->name, q, write_data->value, q);
Packit Service 20376f
Packit Service 20376f
	/* If we are updating a single name/value, we're done.  Setting `value`
Packit Service 20376f
	 * to `NULL` will prevent us from trying to write it again later (in
Packit Service 20376f
	 * `write_on_section`) if we see the same section repeated.
Packit Service 20376f
	 */
Packit Service 20376f
	if (!write_data->preg)
Packit Service 20376f
		write_data->value = NULL;
Packit Service 20376f
Packit Service 20376f
	return result;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int write_on_section(
Packit Service 20376f
	struct reader **reader,
Packit Service 20376f
	const char *current_section,
Packit Service 20376f
	const char *line,
Packit Service 20376f
	size_t line_len,
Packit Service 20376f
	void *data)
Packit Service 20376f
{
Packit Service 20376f
	struct write_data *write_data = (struct write_data *)data;
Packit Service 20376f
	int result = 0;
Packit Service 20376f
Packit Service 20376f
	GIT_UNUSED(reader);
Packit Service 20376f
Packit Service 20376f
	/* If we were previously in the correct section (but aren't anymore)
Packit Service 20376f
	 * and haven't written our value (for a simple name/value set, not
Packit Service 20376f
	 * a multivar), then append it to the end of the section before writing
Packit Service 20376f
	 * the new one.
Packit Service 20376f
	 */
Packit Service 20376f
	if (write_data->in_section && !write_data->preg && write_data->value)
Packit Service 20376f
		result = write_value(write_data);
Packit Service 20376f
Packit Service 20376f
	write_data->in_section = strcmp(current_section, write_data->section) == 0;
Packit Service 20376f
Packit Service 20376f
	/*
Packit Service 20376f
	 * If there were comments just before this section, dump them as well.
Packit Service 20376f
	 */
Packit Service 20376f
	if (!result) {
Packit Service 20376f
		result = git_buf_put(write_data->buf, write_data->buffered_comment.ptr, write_data->buffered_comment.size);
Packit Service 20376f
		git_buf_clear(&write_data->buffered_comment);
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if (!result)
Packit Service 20376f
		result = write_line(write_data, line, line_len);
Packit Service 20376f
Packit Service 20376f
	return result;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int write_on_variable(
Packit Service 20376f
	struct reader **reader,
Packit Service 20376f
	const char *current_section,
Packit Service 20376f
	char *var_name,
Packit Service 20376f
	char *var_value,
Packit Service 20376f
	const char *line,
Packit Service 20376f
	size_t line_len,
Packit Service 20376f
	void *data)
Packit Service 20376f
{
Packit Service 20376f
	struct write_data *write_data = (struct write_data *)data;
Packit Service 20376f
	bool has_matched = false;
Packit Service 20376f
	int error;
Packit Service 20376f
Packit Service 20376f
	GIT_UNUSED(reader);
Packit Service 20376f
	GIT_UNUSED(current_section);
Packit Service 20376f
Packit Service 20376f
	/*
Packit Service 20376f
	 * If there were comments just before this variable, let's dump them as well.
Packit Service 20376f
	 */
Packit Service 20376f
	if ((error = git_buf_put(write_data->buf, write_data->buffered_comment.ptr, write_data->buffered_comment.size)) < 0)
Packit Service 20376f
		return error;
Packit Service 20376f
Packit Service 20376f
	git_buf_clear(&write_data->buffered_comment);
Packit Service 20376f
Packit Service 20376f
	/* See if we are to update this name/value pair; first examine name */
Packit Service 20376f
	if (write_data->in_section &&
Packit Service 20376f
		strcasecmp(write_data->name, var_name) == 0)
Packit Service 20376f
		has_matched = true;
Packit Service 20376f
Packit Service 20376f
	/* If we have a regex to match the value, see if it matches */
Packit Service 20376f
	if (has_matched && write_data->preg != NULL)
Packit Service 20376f
		has_matched = (regexec(write_data->preg, var_value, 0, NULL, 0) == 0);
Packit Service 20376f
Packit Service 20376f
	git__free(var_name);
Packit Service 20376f
	git__free(var_value);
Packit Service 20376f
Packit Service 20376f
	/* If this isn't the name/value we're looking for, simply dump the
Packit Service 20376f
	 * existing data back out and continue on.
Packit Service 20376f
	 */
Packit Service 20376f
	if (!has_matched)
Packit Service 20376f
		return write_line(write_data, line, line_len);
Packit Service 20376f
Packit Service 20376f
	write_data->preg_replaced = 1;
Packit Service 20376f
Packit Service 20376f
	/* If value is NULL, we are deleting this value; write nothing. */
Packit Service 20376f
	if (!write_data->value)
Packit Service 20376f
		return 0;
Packit Service 20376f
Packit Service 20376f
	return write_value(write_data);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int write_on_comment(struct reader **reader, const char *line, size_t line_len, void *data)
Packit Service 20376f
{
Packit Service 20376f
	struct write_data *write_data;
Packit Service 20376f
Packit Service 20376f
	GIT_UNUSED(reader);
Packit Service 20376f
Packit Service 20376f
	write_data = (struct write_data *)data;
Packit Service 20376f
	return write_line_to(&write_data->buffered_comment, line, line_len);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int write_on_eof(
Packit Service 20376f
	struct reader **reader, const char *current_section, void *data)
Packit Service 20376f
{
Packit Service 20376f
	struct write_data *write_data = (struct write_data *)data;
Packit Service 20376f
	int result = 0;
Packit Service 20376f
Packit Service 20376f
	GIT_UNUSED(reader);
Packit Service 20376f
Packit Service 20376f
	/*
Packit Service 20376f
	 * If we've buffered comments when reaching EOF, make sure to dump them.
Packit Service 20376f
	 */
Packit Service 20376f
	if ((result = git_buf_put(write_data->buf, write_data->buffered_comment.ptr, write_data->buffered_comment.size)) < 0)
Packit Service 20376f
		return result;
Packit Service 20376f
Packit Service 20376f
	/* If we are at the EOF and have not written our value (again, for a
Packit Service 20376f
	 * simple name/value set, not a multivar) then we have never seen the
Packit Service 20376f
	 * section in question and should create a new section and write the
Packit Service 20376f
	 * value.
Packit Service 20376f
	 */
Packit Service 20376f
	if ((!write_data->preg || !write_data->preg_replaced) && write_data->value) {
Packit Service 20376f
		/* write the section header unless we're already in it */
Packit Service 20376f
		if (!current_section || strcmp(current_section, write_data->section))
Packit Service 20376f
			result = write_section(write_data->buf, write_data->section);
Packit Service 20376f
Packit Service 20376f
		if (!result)
Packit Service 20376f
			result = write_value(write_data);
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	return result;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
/*
Packit Service 20376f
 * This is pretty much the parsing, except we write out anything we don't have
Packit Service 20376f
 */
Packit Service 20376f
static int config_write(diskfile_backend *cfg, const char *key, const regex_t *preg, const char* value)
Packit Service 20376f
{
Packit Service 20376f
	int result;
Packit Service 20376f
	char *section, *name, *ldot;
Packit Service 20376f
	git_filebuf file = GIT_FILEBUF_INIT;
Packit Service 20376f
	git_buf buf = GIT_BUF_INIT;
Packit Service 20376f
	struct reader *reader = git_array_get(cfg->readers, 0);
Packit Service 20376f
	struct write_data write_data;
Packit Service 20376f
Packit Service 20376f
	if (cfg->locked) {
Packit Service 20376f
		result = git_buf_puts(&reader->buffer, git_buf_cstr(&cfg->locked_content));
Packit Service 20376f
	} else {
Packit Service 20376f
		/* Lock the file */
Packit Service 20376f
		if ((result = git_filebuf_open(
Packit Service 20376f
			     &file, cfg->file_path, GIT_FILEBUF_HASH_CONTENTS, GIT_CONFIG_FILE_MODE)) < 0) {
Packit Service 20376f
			git_buf_free(&reader->buffer);
Packit Service 20376f
			return result;
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		/* We need to read in our own config file */
Packit Service 20376f
		result = git_futils_readbuffer(&reader->buffer, cfg->file_path);
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	/* Initialise the reading position */
Packit Service 20376f
	if (result == GIT_ENOTFOUND) {
Packit Service 20376f
		reader->read_ptr = NULL;
Packit Service 20376f
		reader->eof = 1;
Packit Service 20376f
		git_buf_clear(&reader->buffer);
Packit Service 20376f
	} else if (result == 0) {
Packit Service 20376f
		reader->read_ptr = reader->buffer.ptr;
Packit Service 20376f
		reader->eof = 0;
Packit Service 20376f
	} else {
Packit Service 20376f
		git_filebuf_cleanup(&file;;
Packit Service 20376f
		return -1; /* OS error when reading the file */
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	ldot = strrchr(key, '.');
Packit Service 20376f
	name = ldot + 1;
Packit Service 20376f
	section = git__strndup(key, ldot - key);
Packit Service 20376f
Packit Service 20376f
	write_data.buf = &buf;
Packit Service 20376f
	git_buf_init(&write_data.buffered_comment, 0);
Packit Service 20376f
	write_data.section = section;
Packit Service 20376f
	write_data.in_section = 0;
Packit Service 20376f
	write_data.preg_replaced = 0;
Packit Service 20376f
	write_data.name = name;
Packit Service 20376f
	write_data.preg = preg;
Packit Service 20376f
	write_data.value = value;
Packit Service 20376f
Packit Service 20376f
	result = config_parse(reader, write_on_section, write_on_variable, write_on_comment, write_on_eof, &write_data);
Packit Service 20376f
	git__free(section);
Packit Service 20376f
	git_buf_free(&write_data.buffered_comment);
Packit Service 20376f
Packit Service 20376f
	if (result < 0) {
Packit Service 20376f
		git_filebuf_cleanup(&file;;
Packit Service 20376f
		goto done;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if (cfg->locked) {
Packit Service 20376f
		size_t len = buf.asize;
Packit Service 20376f
		/* Update our copy with the modified contents */
Packit Service 20376f
		git_buf_free(&cfg->locked_content);
Packit Service 20376f
		git_buf_attach(&cfg->locked_content, git_buf_detach(&buf), len);
Packit Service 20376f
	} else {
Packit Service 20376f
		git_filebuf_write(&file, git_buf_cstr(&buf), git_buf_len(&buf));
Packit Service 20376f
		result = git_filebuf_commit(&file;;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
done:
Packit Service 20376f
	git_buf_free(&buf;;
Packit Service 20376f
	git_buf_free(&reader->buffer);
Packit Service 20376f
	return result;
Packit Service 20376f
}