mirror of
https://git.proxmox.com/git/libgit2
synced 2025-05-08 19:51:31 +00:00
838 lines
16 KiB
C
838 lines
16 KiB
C
/*
|
|
* Copyright (C) the libgit2 contributors. All rights reserved.
|
|
*
|
|
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
|
* a Linking Exception. For full terms see the included COPYING file.
|
|
*/
|
|
|
|
#include "common.h"
|
|
#include "fileops.h"
|
|
#include "config.h"
|
|
#include "git2/config.h"
|
|
#include "vector.h"
|
|
#include "buf_text.h"
|
|
#if GIT_WIN32
|
|
# include <windows.h>
|
|
#endif
|
|
|
|
#include <ctype.h>
|
|
|
|
typedef struct {
|
|
git_refcount rc;
|
|
|
|
git_config_backend *file;
|
|
unsigned int level;
|
|
} file_internal;
|
|
|
|
static void file_internal_free(file_internal *internal)
|
|
{
|
|
git_config_backend *file;
|
|
|
|
file = internal->file;
|
|
file->free(file);
|
|
git__free(internal);
|
|
}
|
|
|
|
static void config_free(git_config *cfg)
|
|
{
|
|
unsigned int i;
|
|
file_internal *internal;
|
|
|
|
for(i = 0; i < cfg->files.length; ++i){
|
|
internal = git_vector_get(&cfg->files, i);
|
|
GIT_REFCOUNT_DEC(internal, file_internal_free);
|
|
}
|
|
|
|
git_vector_free(&cfg->files);
|
|
git__free(cfg);
|
|
}
|
|
|
|
void git_config_free(git_config *cfg)
|
|
{
|
|
if (cfg == NULL)
|
|
return;
|
|
|
|
GIT_REFCOUNT_DEC(cfg, config_free);
|
|
}
|
|
|
|
static int config_backend_cmp(const void *a, const void *b)
|
|
{
|
|
const file_internal *bk_a = (const file_internal *)(a);
|
|
const file_internal *bk_b = (const file_internal *)(b);
|
|
|
|
return bk_b->level - bk_a->level;
|
|
}
|
|
|
|
int git_config_new(git_config **out)
|
|
{
|
|
git_config *cfg;
|
|
|
|
cfg = git__malloc(sizeof(git_config));
|
|
GITERR_CHECK_ALLOC(cfg);
|
|
|
|
memset(cfg, 0x0, sizeof(git_config));
|
|
|
|
if (git_vector_init(&cfg->files, 3, config_backend_cmp) < 0) {
|
|
git__free(cfg);
|
|
return -1;
|
|
}
|
|
|
|
*out = cfg;
|
|
GIT_REFCOUNT_INC(cfg);
|
|
return 0;
|
|
}
|
|
|
|
int git_config_add_file_ondisk(
|
|
git_config *cfg,
|
|
const char *path,
|
|
unsigned int level,
|
|
int force)
|
|
{
|
|
git_config_backend *file = NULL;
|
|
int res;
|
|
|
|
assert(cfg && path);
|
|
|
|
if (!git_path_isfile(path)) {
|
|
giterr_set(GITERR_CONFIG, "Cannot find config file '%s'", path);
|
|
return GIT_ENOTFOUND;
|
|
}
|
|
|
|
if (git_config_file__ondisk(&file, path) < 0)
|
|
return -1;
|
|
|
|
if ((res = git_config_add_backend(cfg, file, level, force)) < 0) {
|
|
/*
|
|
* free manually; the file is not owned by the config
|
|
* instance yet and will not be freed on cleanup
|
|
*/
|
|
file->free(file);
|
|
return res;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int git_config_open_ondisk(git_config **out, const char *path)
|
|
{
|
|
int error;
|
|
git_config *config;
|
|
|
|
*out = NULL;
|
|
|
|
if (git_config_new(&config) < 0)
|
|
return -1;
|
|
|
|
if ((error = git_config_add_file_ondisk(config, path, GIT_CONFIG_LEVEL_LOCAL, 0)) < 0)
|
|
git_config_free(config);
|
|
else
|
|
*out = config;
|
|
|
|
return error;
|
|
}
|
|
|
|
static int find_internal_file_by_level(
|
|
file_internal **internal_out,
|
|
const git_config *cfg,
|
|
int level)
|
|
{
|
|
int pos = -1;
|
|
file_internal *internal;
|
|
unsigned int i;
|
|
|
|
/* when passing GIT_CONFIG_HIGHEST_LEVEL, the idea is to get the config file
|
|
* which has the highest level. As config files are stored in a vector
|
|
* sorted by decreasing order of level, getting the file at position 0
|
|
* will do the job.
|
|
*/
|
|
if (level == GIT_CONFIG_HIGHEST_LEVEL) {
|
|
pos = 0;
|
|
} else {
|
|
git_vector_foreach(&cfg->files, i, internal) {
|
|
if (internal->level == (unsigned int)level)
|
|
pos = i;
|
|
}
|
|
}
|
|
|
|
if (pos == -1) {
|
|
giterr_set(GITERR_CONFIG,
|
|
"No config file exists for the given level '%i'", level);
|
|
return GIT_ENOTFOUND;
|
|
}
|
|
|
|
*internal_out = git_vector_get(&cfg->files, pos);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int duplicate_level(void **old_raw, void *new_raw)
|
|
{
|
|
file_internal **old = (file_internal **)old_raw;
|
|
|
|
GIT_UNUSED(new_raw);
|
|
|
|
giterr_set(GITERR_CONFIG, "A file with the same level (%i) has already been added to the config", (*old)->level);
|
|
return GIT_EEXISTS;
|
|
}
|
|
|
|
static void try_remove_existing_file_internal(
|
|
git_config *cfg,
|
|
unsigned int level)
|
|
{
|
|
int pos = -1;
|
|
file_internal *internal;
|
|
unsigned int i;
|
|
|
|
git_vector_foreach(&cfg->files, i, internal) {
|
|
if (internal->level == level)
|
|
pos = i;
|
|
}
|
|
|
|
if (pos == -1)
|
|
return;
|
|
|
|
internal = git_vector_get(&cfg->files, pos);
|
|
|
|
if (git_vector_remove(&cfg->files, pos) < 0)
|
|
return;
|
|
|
|
GIT_REFCOUNT_DEC(internal, file_internal_free);
|
|
}
|
|
|
|
static int git_config__add_internal(
|
|
git_config *cfg,
|
|
file_internal *internal,
|
|
unsigned int level,
|
|
int force)
|
|
{
|
|
int result;
|
|
|
|
/* delete existing config file for level if it exists */
|
|
if (force)
|
|
try_remove_existing_file_internal(cfg, level);
|
|
|
|
if ((result = git_vector_insert_sorted(&cfg->files,
|
|
internal, &duplicate_level)) < 0)
|
|
return result;
|
|
|
|
git_vector_sort(&cfg->files);
|
|
internal->file->cfg = cfg;
|
|
|
|
GIT_REFCOUNT_INC(internal);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int git_config_open_level(
|
|
git_config **cfg_out,
|
|
const git_config *cfg_parent,
|
|
unsigned int level)
|
|
{
|
|
git_config *cfg;
|
|
file_internal *internal;
|
|
int res;
|
|
|
|
if ((res = find_internal_file_by_level(&internal, cfg_parent, level)) < 0)
|
|
return res;
|
|
|
|
if ((res = git_config_new(&cfg)) < 0)
|
|
return res;
|
|
|
|
if ((res = git_config__add_internal(cfg, internal, level, true)) < 0) {
|
|
git_config_free(cfg);
|
|
return res;
|
|
}
|
|
|
|
*cfg_out = cfg;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int git_config_add_backend(
|
|
git_config *cfg,
|
|
git_config_backend *file,
|
|
unsigned int level,
|
|
int force)
|
|
{
|
|
file_internal *internal;
|
|
int result;
|
|
|
|
assert(cfg && file);
|
|
|
|
GITERR_CHECK_VERSION(file, GIT_CONFIG_BACKEND_VERSION, "git_config_backend");
|
|
|
|
if ((result = file->open(file, level)) < 0)
|
|
return result;
|
|
|
|
internal = git__malloc(sizeof(file_internal));
|
|
GITERR_CHECK_ALLOC(internal);
|
|
|
|
memset(internal, 0x0, sizeof(file_internal));
|
|
|
|
internal->file = file;
|
|
internal->level = level;
|
|
|
|
if ((result = git_config__add_internal(cfg, internal, level, force)) < 0) {
|
|
git__free(internal);
|
|
return result;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int git_config_refresh(git_config *cfg)
|
|
{
|
|
int error = 0;
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < cfg->files.length && !error; ++i) {
|
|
file_internal *internal = git_vector_get(&cfg->files, i);
|
|
git_config_backend *file = internal->file;
|
|
error = file->refresh(file);
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
/*
|
|
* Loop over all the variables
|
|
*/
|
|
|
|
int git_config_foreach(
|
|
const git_config *cfg, git_config_foreach_cb cb, void *payload)
|
|
{
|
|
return git_config_foreach_match(cfg, NULL, cb, payload);
|
|
}
|
|
|
|
int git_config_foreach_match(
|
|
const git_config *cfg,
|
|
const char *regexp,
|
|
git_config_foreach_cb cb,
|
|
void *payload)
|
|
{
|
|
int ret = 0;
|
|
unsigned int i;
|
|
file_internal *internal;
|
|
git_config_backend *file;
|
|
|
|
for (i = 0; i < cfg->files.length && ret == 0; ++i) {
|
|
internal = git_vector_get(&cfg->files, i);
|
|
file = internal->file;
|
|
ret = file->foreach(file, regexp, cb, payload);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int git_config_delete_entry(git_config *cfg, const char *name)
|
|
{
|
|
git_config_backend *file;
|
|
file_internal *internal;
|
|
|
|
internal = git_vector_get(&cfg->files, 0);
|
|
file = internal->file;
|
|
|
|
return file->del(file, name);
|
|
}
|
|
|
|
/**************
|
|
* Setters
|
|
**************/
|
|
|
|
int git_config_set_int64(git_config *cfg, const char *name, int64_t value)
|
|
{
|
|
char str_value[32]; /* All numbers should fit in here */
|
|
p_snprintf(str_value, sizeof(str_value), "%" PRId64, value);
|
|
return git_config_set_string(cfg, name, str_value);
|
|
}
|
|
|
|
int git_config_set_int32(git_config *cfg, const char *name, int32_t value)
|
|
{
|
|
return git_config_set_int64(cfg, name, (int64_t)value);
|
|
}
|
|
|
|
int git_config_set_bool(git_config *cfg, const char *name, int value)
|
|
{
|
|
return git_config_set_string(cfg, name, value ? "true" : "false");
|
|
}
|
|
|
|
int git_config_set_string(git_config *cfg, const char *name, const char *value)
|
|
{
|
|
git_config_backend *file;
|
|
file_internal *internal;
|
|
|
|
internal = git_vector_get(&cfg->files, 0);
|
|
file = internal->file;
|
|
|
|
return file->set(file, name, value);
|
|
}
|
|
|
|
/***********
|
|
* Getters
|
|
***********/
|
|
int git_config_get_mapped(
|
|
int *out,
|
|
const git_config *cfg,
|
|
const char *name,
|
|
const git_cvar_map *maps,
|
|
size_t map_n)
|
|
{
|
|
const char *value;
|
|
int ret;
|
|
|
|
if ((ret = git_config_get_string(&value, cfg, name)) < 0)
|
|
return ret;
|
|
|
|
return git_config_lookup_map_value(out, maps, map_n, value);
|
|
}
|
|
|
|
int git_config_get_int64(int64_t *out, const git_config *cfg, const char *name)
|
|
{
|
|
const char *value;
|
|
int ret;
|
|
|
|
if ((ret = git_config_get_string(&value, cfg, name)) < 0)
|
|
return ret;
|
|
|
|
return git_config_parse_int64(out, value);
|
|
}
|
|
|
|
int git_config_get_int32(int32_t *out, const git_config *cfg, const char *name)
|
|
{
|
|
const char *value;
|
|
int ret;
|
|
|
|
if ((ret = git_config_get_string(&value, cfg, name)) < 0)
|
|
return ret;
|
|
|
|
return git_config_parse_int32(out, value);
|
|
}
|
|
|
|
static int get_string_at_file(const char **out, const git_config_backend *file, const char *name)
|
|
{
|
|
const git_config_entry *entry;
|
|
int res;
|
|
|
|
res = file->get(file, name, &entry);
|
|
if (!res)
|
|
*out = entry->value;
|
|
|
|
return res;
|
|
}
|
|
|
|
static int get_string(const char **out, const git_config *cfg, const char *name)
|
|
{
|
|
file_internal *internal;
|
|
unsigned int i;
|
|
|
|
assert(cfg->files.length);
|
|
|
|
git_vector_foreach(&cfg->files, i, internal) {
|
|
int res = get_string_at_file(out, internal->file, name);
|
|
|
|
if (res != GIT_ENOTFOUND)
|
|
return res;
|
|
}
|
|
|
|
return GIT_ENOTFOUND;
|
|
}
|
|
|
|
int git_config_get_bool(int *out, const git_config *cfg, const char *name)
|
|
{
|
|
const char *value = NULL;
|
|
int ret;
|
|
|
|
if ((ret = get_string(&value, cfg, name)) < 0)
|
|
return ret;
|
|
|
|
return git_config_parse_bool(out, value);
|
|
}
|
|
|
|
int git_config_get_string(const char **out, const git_config *cfg, const char *name)
|
|
{
|
|
int ret;
|
|
const char *str = NULL;
|
|
|
|
if ((ret = get_string(&str, cfg, name)) < 0)
|
|
return ret;
|
|
|
|
*out = str == NULL ? "" : str;
|
|
return 0;
|
|
}
|
|
|
|
int git_config_get_entry(const git_config_entry **out, const git_config *cfg, const char *name)
|
|
{
|
|
file_internal *internal;
|
|
unsigned int i;
|
|
|
|
assert(cfg->files.length);
|
|
|
|
*out = NULL;
|
|
|
|
git_vector_foreach(&cfg->files, i, internal) {
|
|
git_config_backend *file = internal->file;
|
|
int ret = file->get(file, name, out);
|
|
if (ret != GIT_ENOTFOUND)
|
|
return ret;
|
|
}
|
|
|
|
return GIT_ENOTFOUND;
|
|
}
|
|
|
|
int git_config_get_multivar(const git_config *cfg, const char *name, const char *regexp,
|
|
git_config_foreach_cb cb, void *payload)
|
|
{
|
|
file_internal *internal;
|
|
git_config_backend *file;
|
|
int ret = GIT_ENOTFOUND;
|
|
size_t i;
|
|
|
|
assert(cfg->files.length);
|
|
|
|
/*
|
|
* This loop runs the "wrong" way 'round because we need to
|
|
* look at every value from the most general to most specific
|
|
*/
|
|
for (i = cfg->files.length; i > 0; --i) {
|
|
internal = git_vector_get(&cfg->files, i - 1);
|
|
file = internal->file;
|
|
ret = file->get_multivar(file, name, regexp, cb, payload);
|
|
if (ret < 0 && ret != GIT_ENOTFOUND)
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int git_config_set_multivar(git_config *cfg, const char *name, const char *regexp, const char *value)
|
|
{
|
|
git_config_backend *file;
|
|
file_internal *internal;
|
|
|
|
internal = git_vector_get(&cfg->files, 0);
|
|
file = internal->file;
|
|
|
|
return file->set_multivar(file, name, regexp, value);
|
|
}
|
|
|
|
int git_config_find_global_r(git_buf *path)
|
|
{
|
|
int error = git_futils_find_global_file(path, GIT_CONFIG_FILENAME);
|
|
|
|
return error;
|
|
}
|
|
|
|
int git_config_find_xdg_r(git_buf *path)
|
|
{
|
|
int error = git_futils_find_global_file(path, GIT_CONFIG_FILENAME_ALT);
|
|
|
|
return error;
|
|
}
|
|
|
|
int git_config_find_global(char *global_config_path, size_t length)
|
|
{
|
|
git_buf path = GIT_BUF_INIT;
|
|
int ret = git_config_find_global_r(&path);
|
|
|
|
if (ret < 0) {
|
|
git_buf_free(&path);
|
|
return ret;
|
|
}
|
|
|
|
if (path.size >= length) {
|
|
git_buf_free(&path);
|
|
giterr_set(GITERR_NOMEMORY,
|
|
"Path is to long to fit on the given buffer");
|
|
return -1;
|
|
}
|
|
|
|
git_buf_copy_cstr(global_config_path, length, &path);
|
|
git_buf_free(&path);
|
|
return 0;
|
|
}
|
|
|
|
int git_config_find_xdg(char *xdg_config_path, size_t length)
|
|
{
|
|
git_buf path = GIT_BUF_INIT;
|
|
int ret = git_config_find_xdg_r(&path);
|
|
|
|
if (ret < 0) {
|
|
git_buf_free(&path);
|
|
return ret;
|
|
}
|
|
|
|
if (path.size >= length) {
|
|
git_buf_free(&path);
|
|
giterr_set(GITERR_NOMEMORY,
|
|
"Path is to long to fit on the given buffer");
|
|
return -1;
|
|
}
|
|
|
|
git_buf_copy_cstr(xdg_config_path, length, &path);
|
|
git_buf_free(&path);
|
|
return 0;
|
|
}
|
|
|
|
int git_config_find_system_r(git_buf *path)
|
|
{
|
|
return git_futils_find_system_file(path, GIT_CONFIG_FILENAME_SYSTEM);
|
|
}
|
|
|
|
int git_config_find_system(char *system_config_path, size_t length)
|
|
{
|
|
git_buf path = GIT_BUF_INIT;
|
|
int ret = git_config_find_system_r(&path);
|
|
|
|
if (ret < 0) {
|
|
git_buf_free(&path);
|
|
return ret;
|
|
}
|
|
|
|
if (path.size >= length) {
|
|
git_buf_free(&path);
|
|
giterr_set(GITERR_NOMEMORY,
|
|
"Path is to long to fit on the given buffer");
|
|
return -1;
|
|
}
|
|
|
|
git_buf_copy_cstr(system_config_path, length, &path);
|
|
git_buf_free(&path);
|
|
return 0;
|
|
}
|
|
|
|
int git_config_open_default(git_config **out)
|
|
{
|
|
int error;
|
|
git_config *cfg = NULL;
|
|
git_buf buf = GIT_BUF_INIT;
|
|
|
|
error = git_config_new(&cfg);
|
|
|
|
if (!error && !git_config_find_global_r(&buf))
|
|
error = git_config_add_file_ondisk(cfg, buf.ptr,
|
|
GIT_CONFIG_LEVEL_GLOBAL, 0);
|
|
|
|
if (!error && !git_config_find_xdg_r(&buf))
|
|
error = git_config_add_file_ondisk(cfg, buf.ptr,
|
|
GIT_CONFIG_LEVEL_XDG, 0);
|
|
|
|
if (!error && !git_config_find_system_r(&buf))
|
|
error = git_config_add_file_ondisk(cfg, buf.ptr,
|
|
GIT_CONFIG_LEVEL_SYSTEM, 0);
|
|
|
|
git_buf_free(&buf);
|
|
|
|
if (error && cfg) {
|
|
git_config_free(cfg);
|
|
cfg = NULL;
|
|
}
|
|
|
|
*out = cfg;
|
|
|
|
return error;
|
|
}
|
|
|
|
/***********
|
|
* Parsers
|
|
***********/
|
|
int git_config_lookup_map_value(
|
|
int *out,
|
|
const git_cvar_map *maps,
|
|
size_t map_n,
|
|
const char *value)
|
|
{
|
|
size_t i;
|
|
|
|
if (!value)
|
|
goto fail_parse;
|
|
|
|
for (i = 0; i < map_n; ++i) {
|
|
const git_cvar_map *m = maps + i;
|
|
|
|
switch (m->cvar_type) {
|
|
case GIT_CVAR_FALSE:
|
|
case GIT_CVAR_TRUE: {
|
|
int bool_val;
|
|
|
|
if (git__parse_bool(&bool_val, value) == 0 &&
|
|
bool_val == (int)m->cvar_type) {
|
|
*out = m->map_value;
|
|
return 0;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case GIT_CVAR_INT32:
|
|
if (git_config_parse_int32(out, value) == 0)
|
|
return 0;
|
|
break;
|
|
|
|
case GIT_CVAR_STRING:
|
|
if (strcasecmp(value, m->str_match) == 0) {
|
|
*out = m->map_value;
|
|
return 0;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
fail_parse:
|
|
giterr_set(GITERR_CONFIG, "Failed to map '%s'", value);
|
|
return -1;
|
|
}
|
|
|
|
int git_config_parse_bool(int *out, const char *value)
|
|
{
|
|
if (git__parse_bool(out, value) == 0)
|
|
return 0;
|
|
|
|
if (git_config_parse_int32(out, value) == 0) {
|
|
*out = !!(*out);
|
|
return 0;
|
|
}
|
|
|
|
giterr_set(GITERR_CONFIG, "Failed to parse '%s' as a boolean value", value);
|
|
return -1;
|
|
}
|
|
|
|
int git_config_parse_int64(int64_t *out, const char *value)
|
|
{
|
|
const char *num_end;
|
|
int64_t num;
|
|
|
|
if (git__strtol64(&num, value, &num_end, 0) < 0)
|
|
goto fail_parse;
|
|
|
|
switch (*num_end) {
|
|
case 'g':
|
|
case 'G':
|
|
num *= 1024;
|
|
/* fallthrough */
|
|
|
|
case 'm':
|
|
case 'M':
|
|
num *= 1024;
|
|
/* fallthrough */
|
|
|
|
case 'k':
|
|
case 'K':
|
|
num *= 1024;
|
|
|
|
/* check that that there are no more characters after the
|
|
* given modifier suffix */
|
|
if (num_end[1] != '\0')
|
|
return -1;
|
|
|
|
/* fallthrough */
|
|
|
|
case '\0':
|
|
*out = num;
|
|
return 0;
|
|
|
|
default:
|
|
goto fail_parse;
|
|
}
|
|
|
|
fail_parse:
|
|
giterr_set(GITERR_CONFIG, "Failed to parse '%s' as an integer", value);
|
|
return -1;
|
|
}
|
|
|
|
int git_config_parse_int32(int32_t *out, const char *value)
|
|
{
|
|
int64_t tmp;
|
|
int32_t truncate;
|
|
|
|
if (git_config_parse_int64(&tmp, value) < 0)
|
|
goto fail_parse;
|
|
|
|
truncate = tmp & 0xFFFFFFFF;
|
|
if (truncate != tmp)
|
|
goto fail_parse;
|
|
|
|
*out = truncate;
|
|
return 0;
|
|
|
|
fail_parse:
|
|
giterr_set(GITERR_CONFIG, "Failed to parse '%s' as a 32-bit integer", value);
|
|
return -1;
|
|
}
|
|
|
|
struct rename_data
|
|
{
|
|
git_config *config;
|
|
const char *old_name;
|
|
const char *new_name;
|
|
};
|
|
|
|
static int rename_config_entries_cb(
|
|
const git_config_entry *entry,
|
|
void *payload)
|
|
{
|
|
struct rename_data *data = (struct rename_data *)payload;
|
|
|
|
if (data->new_name != NULL) {
|
|
git_buf name = GIT_BUF_INIT;
|
|
int error;
|
|
|
|
if (git_buf_printf(
|
|
&name,
|
|
"%s.%s",
|
|
data->new_name,
|
|
entry->name + strlen(data->old_name) + 1) < 0)
|
|
return -1;
|
|
|
|
error = git_config_set_string(
|
|
data->config,
|
|
git_buf_cstr(&name),
|
|
entry->value);
|
|
|
|
git_buf_free(&name);
|
|
|
|
if (error)
|
|
return error;
|
|
}
|
|
|
|
return git_config_delete_entry(data->config, entry->name);
|
|
}
|
|
|
|
int git_config_rename_section(
|
|
git_repository *repo,
|
|
const char *old_section_name,
|
|
const char *new_section_name)
|
|
{
|
|
git_config *config;
|
|
git_buf pattern = GIT_BUF_INIT;
|
|
int error = -1;
|
|
struct rename_data data;
|
|
|
|
git_buf_text_puts_escape_regex(&pattern, old_section_name);
|
|
git_buf_puts(&pattern, "\\..+");
|
|
if (git_buf_oom(&pattern))
|
|
goto cleanup;
|
|
|
|
if (git_repository_config__weakptr(&config, repo) < 0)
|
|
goto cleanup;
|
|
|
|
data.config = config;
|
|
data.old_name = old_section_name;
|
|
data.new_name = new_section_name;
|
|
|
|
if ((error = git_config_foreach_match(
|
|
config,
|
|
git_buf_cstr(&pattern),
|
|
rename_config_entries_cb, &data)) < 0) {
|
|
giterr_set(GITERR_CONFIG,
|
|
"Cannot rename config section '%s' to '%s'",
|
|
old_section_name,
|
|
new_section_name);
|
|
goto cleanup;
|
|
}
|
|
|
|
error = 0;
|
|
|
|
cleanup:
|
|
git_buf_free(&pattern);
|
|
return error;
|
|
}
|