From: Felix Fietkau Date: Fri, 20 Aug 2010 12:59:27 +0000 (+0200) Subject: uci_history -> uci_delta X-Git-Url: http://git.archive.openwrt.org/?p=project%2Fuci.git;a=commitdiff_plain;h=c4402a9e59721172395d9403cfbe467209bcd6ad uci_history -> uci_delta --- diff --git a/Makefile b/Makefile index b00aac8..9f5b313 100644 --- a/Makefile +++ b/Makefile @@ -23,7 +23,7 @@ SOURCES = libuci.c file.c ucimap.c util.c all: uci libuci.$(SHLIB_EXT) uci-static ucimap-example -$(eval $(call add_dep,libuci,history.c list.c uci.h uci_config.h uci_internal.h)) +$(eval $(call add_dep,libuci,delta.c list.c uci.h uci_config.h uci_internal.h)) $(eval $(call add_dep,ucimap,uci.h uci_config.h ucimap.h)) cli.o: cli.c uci.h uci_config.h diff --git a/cli.c b/cli.c index 7bef7a9..0e98aa7 100644 --- a/cli.c +++ b/cli.c @@ -224,8 +224,8 @@ static void uci_show_changes(struct uci_package *p) { struct uci_element *e; - uci_foreach_element(&p->saved_history, e) { - struct uci_history *h = uci_to_history(e); + uci_foreach_element(&p->saved_delta, e) { + struct uci_delta *h = uci_to_delta(e); char *prefix = ""; char *op = "="; @@ -653,10 +653,10 @@ int main(int argc, char **argv) ctx->flags &= ~UCI_FLAG_EXPORT_NAME; break; case 'p': - uci_add_history_path(ctx, optarg); + uci_add_delta_path(ctx, optarg); break; case 'P': - uci_add_history_path(ctx, ctx->savedir); + uci_add_delta_path(ctx, ctx->savedir); uci_set_savedir(ctx, optarg); flags |= CLI_FLAG_NOCOMMIT; break; diff --git a/delta.c b/delta.c new file mode 100644 index 0000000..e0fa1b1 --- /dev/null +++ b/delta.c @@ -0,0 +1,483 @@ +/* + * libuci - Library for the Unified Configuration Interface + * Copyright (C) 2008 Felix Fietkau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/* + * This file contains the code for handling uci config delta files + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include + +/* record a change that was done to a package */ +void +uci_add_delta(struct uci_context *ctx, struct uci_list *list, int cmd, const char *section, const char *option, const char *value) +{ + struct uci_delta *h; + int size = strlen(section) + 1; + char *ptr; + + if (value) + size += strlen(value) + 1; + + h = uci_alloc_element(ctx, delta, option, size); + ptr = uci_dataptr(h); + h->cmd = cmd; + h->section = strcpy(ptr, section); + if (value) { + ptr += strlen(ptr) + 1; + h->value = strcpy(ptr, value); + } + uci_list_add(list, &h->e.list); +} + +void +uci_free_delta(struct uci_delta *h) +{ + if (!h) + return; + if ((h->section != NULL) && + (h->section != uci_dataptr(h))) { + free(h->section); + free(h->value); + } + uci_free_element(&h->e); +} + + +int uci_set_savedir(struct uci_context *ctx, const char *dir) +{ + char *sdir; + + UCI_HANDLE_ERR(ctx); + UCI_ASSERT(ctx, dir != NULL); + + sdir = uci_strdup(ctx, dir); + if (ctx->savedir != uci_savedir) + free(ctx->savedir); + ctx->savedir = sdir; + return 0; +} + +int uci_add_delta_path(struct uci_context *ctx, const char *dir) +{ + struct uci_element *e; + + UCI_HANDLE_ERR(ctx); + UCI_ASSERT(ctx, dir != NULL); + e = uci_alloc_generic(ctx, UCI_TYPE_PATH, dir, sizeof(struct uci_element)); + uci_list_add(&ctx->delta_path, &e->list); + + return 0; +} + +static inline int uci_parse_delta_tuple(struct uci_context *ctx, char **buf, struct uci_ptr *ptr) +{ + int c = UCI_CMD_CHANGE; + + switch(**buf) { + case '^': + c = UCI_CMD_REORDER; + break; + case '-': + c = UCI_CMD_REMOVE; + break; + case '@': + c = UCI_CMD_RENAME; + break; + case '+': + /* UCI_CMD_ADD is used for anonymous sections or list values */ + c = UCI_CMD_ADD; + break; + case '|': + c = UCI_CMD_LIST_ADD; + break; + } + + if (c != UCI_CMD_CHANGE) + *buf += 1; + + UCI_INTERNAL(uci_parse_ptr, ctx, ptr, *buf); + + if (!ptr->section) + goto error; + if (ptr->flags & UCI_LOOKUP_EXTENDED) + goto error; + + switch(c) { + case UCI_CMD_REORDER: + if (!ptr->value || ptr->option) + goto error; + break; + case UCI_CMD_RENAME: + if (!ptr->value || !uci_validate_name(ptr->value)) + goto error; + break; + case UCI_CMD_LIST_ADD: + if (!ptr->option) + goto error; + } + + return c; + +error: + UCI_THROW(ctx, UCI_ERR_INVAL); + return 0; +} + +static void uci_parse_delta_line(struct uci_context *ctx, struct uci_package *p, char *buf) +{ + struct uci_element *e = NULL; + struct uci_ptr ptr; + int cmd; + + cmd = uci_parse_delta_tuple(ctx, &buf, &ptr); + if (strcmp(ptr.package, p->e.name) != 0) + goto error; + + if (ctx->flags & UCI_FLAG_SAVED_DELTA) + uci_add_delta(ctx, &p->saved_delta, cmd, ptr.section, ptr.option, ptr.value); + + switch(cmd) { + case UCI_CMD_REORDER: + expand_ptr(ctx, &ptr, true); + if (!ptr.s) + UCI_THROW(ctx, UCI_ERR_NOTFOUND); + UCI_INTERNAL(uci_reorder_section, ctx, ptr.s, strtoul(ptr.value, NULL, 10)); + break; + case UCI_CMD_RENAME: + UCI_INTERNAL(uci_rename, ctx, &ptr); + break; + case UCI_CMD_REMOVE: + UCI_INTERNAL(uci_delete, ctx, &ptr); + break; + case UCI_CMD_LIST_ADD: + UCI_INTERNAL(uci_add_list, ctx, &ptr); + break; + case UCI_CMD_ADD: + case UCI_CMD_CHANGE: + UCI_INTERNAL(uci_set, ctx, &ptr); + e = ptr.last; + if (!ptr.option && e && (cmd == UCI_CMD_ADD)) + uci_to_section(e)->anonymous = true; + break; + } + return; +error: + UCI_THROW(ctx, UCI_ERR_PARSE); +} + +/* returns the number of changes that were successfully parsed */ +static int uci_parse_delta(struct uci_context *ctx, FILE *stream, struct uci_package *p) +{ + struct uci_parse_context *pctx; + int changes = 0; + + /* make sure no memory from previous parse attempts is leaked */ + uci_cleanup(ctx); + + pctx = (struct uci_parse_context *) uci_malloc(ctx, sizeof(struct uci_parse_context)); + ctx->pctx = pctx; + pctx->file = stream; + + while (!feof(pctx->file)) { + uci_getln(ctx, 0); + if (!pctx->buf[0]) + continue; + + /* + * ignore parse errors in single lines, we want to preserve as much + * delta as possible + */ + UCI_TRAP_SAVE(ctx, error); + uci_parse_delta_line(ctx, p, pctx->buf); + UCI_TRAP_RESTORE(ctx); + changes++; +error: + continue; + } + + /* no error happened, we can get rid of the parser context now */ + uci_cleanup(ctx); + return changes; +} + +/* returns the number of changes that were successfully parsed */ +static int uci_load_delta_file(struct uci_context *ctx, struct uci_package *p, char *filename, FILE **f, bool flush) +{ + FILE *stream = NULL; + int changes = 0; + + UCI_TRAP_SAVE(ctx, done); + stream = uci_open_stream(ctx, filename, SEEK_SET, flush, false); + if (p) + changes = uci_parse_delta(ctx, stream, p); + UCI_TRAP_RESTORE(ctx); +done: + if (f) + *f = stream; + else if (stream) + uci_close_stream(stream); + return changes; +} + +/* returns the number of changes that were successfully parsed */ +__private int uci_load_delta(struct uci_context *ctx, struct uci_package *p, bool flush) +{ + struct uci_element *e; + char *filename = NULL; + FILE *f = NULL; + int changes = 0; + + if (!p->has_delta) + return 0; + + uci_foreach_element(&ctx->delta_path, e) { + if ((asprintf(&filename, "%s/%s", e->name, p->e.name) < 0) || !filename) + UCI_THROW(ctx, UCI_ERR_MEM); + + uci_load_delta_file(ctx, p, filename, NULL, false); + free(filename); + } + + if ((asprintf(&filename, "%s/%s", ctx->savedir, p->e.name) < 0) || !filename) + UCI_THROW(ctx, UCI_ERR_MEM); + + changes = uci_load_delta_file(ctx, p, filename, &f, flush); + if (flush && f && (changes > 0)) { + rewind(f); + if (ftruncate(fileno(f), 0) < 0) { + uci_close_stream(f); + UCI_THROW(ctx, UCI_ERR_IO); + } + } + if (filename) + free(filename); + uci_close_stream(f); + ctx->err = 0; + return changes; +} + +static void uci_filter_delta(struct uci_context *ctx, const char *name, const char *section, const char *option) +{ + struct uci_parse_context *pctx; + struct uci_element *e, *tmp; + struct uci_list list; + char *filename = NULL; + struct uci_ptr ptr; + FILE *f = NULL; + + uci_list_init(&list); + uci_alloc_parse_context(ctx); + pctx = ctx->pctx; + + if ((asprintf(&filename, "%s/%s", ctx->savedir, name) < 0) || !filename) + UCI_THROW(ctx, UCI_ERR_MEM); + + UCI_TRAP_SAVE(ctx, done); + f = uci_open_stream(ctx, filename, SEEK_SET, true, false); + pctx->file = f; + while (!feof(f)) { + struct uci_element *e; + char *buf; + + uci_getln(ctx, 0); + buf = pctx->buf; + if (!buf[0]) + continue; + + /* NB: need to allocate the element before the call to + * uci_parse_delta_tuple, otherwise the original string + * gets modified before it is saved */ + e = uci_alloc_generic(ctx, UCI_TYPE_DELTA, pctx->buf, sizeof(struct uci_element)); + uci_list_add(&list, &e->list); + + uci_parse_delta_tuple(ctx, &buf, &ptr); + if (section) { + if (!ptr.section || (strcmp(section, ptr.section) != 0)) + continue; + } + if (option) { + if (!ptr.option || (strcmp(option, ptr.option) != 0)) + continue; + } + /* match, drop this element again */ + uci_free_element(e); + } + + /* rebuild the delta file */ + rewind(f); + if (ftruncate(fileno(f), 0) < 0) + UCI_THROW(ctx, UCI_ERR_IO); + uci_foreach_element_safe(&list, tmp, e) { + fprintf(f, "%s\n", e->name); + uci_free_element(e); + } + UCI_TRAP_RESTORE(ctx); + +done: + if (filename) + free(filename); + uci_close_stream(pctx->file); + uci_foreach_element_safe(&list, tmp, e) { + uci_free_element(e); + } + uci_cleanup(ctx); +} + +int uci_revert(struct uci_context *ctx, struct uci_ptr *ptr) +{ + char *package = NULL; + char *section = NULL; + char *option = NULL; + + UCI_HANDLE_ERR(ctx); + expand_ptr(ctx, ptr, false); + UCI_ASSERT(ctx, ptr->p->has_delta); + + /* + * - flush unwritten changes + * - save the package name + * - unload the package + * - filter the delta + * - reload the package + */ + UCI_TRAP_SAVE(ctx, error); + UCI_INTERNAL(uci_save, ctx, ptr->p); + + /* NB: need to clone package, section and option names, + * as they may get freed on uci_free_package() */ + package = uci_strdup(ctx, ptr->p->e.name); + if (ptr->section) + section = uci_strdup(ctx, ptr->section); + if (ptr->option) + option = uci_strdup(ctx, ptr->option); + + uci_free_package(&ptr->p); + uci_filter_delta(ctx, package, section, option); + + UCI_INTERNAL(uci_load, ctx, package, &ptr->p); + UCI_TRAP_RESTORE(ctx); + ctx->err = 0; + +error: + if (package) + free(package); + if (section) + free(section); + if (option) + free(option); + if (ctx->err) + UCI_THROW(ctx, ctx->err); + return 0; +} + +int uci_save(struct uci_context *ctx, struct uci_package *p) +{ + FILE *f = NULL; + char *filename = NULL; + struct uci_element *e, *tmp; + struct stat statbuf; + + UCI_HANDLE_ERR(ctx); + UCI_ASSERT(ctx, p != NULL); + + /* + * if the config file was outside of the /etc/config path, + * don't save the delta to a file, update the real file + * directly. + * does not modify the uci_package pointer + */ + if (!p->has_delta) + return uci_commit(ctx, &p, false); + + if (uci_list_empty(&p->delta)) + return 0; + + if (stat(ctx->savedir, &statbuf) < 0) + mkdir(ctx->savedir, UCI_DIRMODE); + else if ((statbuf.st_mode & S_IFMT) != S_IFDIR) + UCI_THROW(ctx, UCI_ERR_IO); + + if ((asprintf(&filename, "%s/%s", ctx->savedir, p->e.name) < 0) || !filename) + UCI_THROW(ctx, UCI_ERR_MEM); + + uci_foreach_element(&ctx->hooks, tmp) { + struct uci_hook *hook = uci_to_hook(tmp); + + if (!hook->ops->set) + continue; + + uci_foreach_element(&p->delta, e) { + hook->ops->set(hook->ops, p, uci_to_delta(e)); + } + } + + ctx->err = 0; + UCI_TRAP_SAVE(ctx, done); + f = uci_open_stream(ctx, filename, SEEK_END, true, true); + UCI_TRAP_RESTORE(ctx); + + uci_foreach_element_safe(&p->delta, tmp, e) { + struct uci_delta *h = uci_to_delta(e); + char *prefix = ""; + + switch(h->cmd) { + case UCI_CMD_REMOVE: + prefix = "-"; + break; + case UCI_CMD_RENAME: + prefix = "@"; + break; + case UCI_CMD_ADD: + prefix = "+"; + break; + case UCI_CMD_REORDER: + prefix = "^"; + break; + case UCI_CMD_LIST_ADD: + prefix = "|"; + break; + default: + break; + } + + fprintf(f, "%s%s.%s", prefix, p->e.name, h->section); + if (e->name) + fprintf(f, ".%s", e->name); + + if (h->cmd == UCI_CMD_REMOVE) + fprintf(f, "\n"); + else + fprintf(f, "=%s\n", h->value); + uci_free_delta(h); + } + +done: + uci_close_stream(f); + if (filename) + free(filename); + if (ctx->err) + UCI_THROW(ctx, ctx->err); + + return 0; +} + + diff --git a/file.c b/file.c index 33ba93c..0120505 100644 --- a/file.c +++ b/file.c @@ -694,14 +694,14 @@ void uci_file_commit(struct uci_context *ctx, struct uci_package **package, bool /* open the config file for writing now, so that it is locked */ f = uci_open_stream(ctx, p->path, SEEK_SET, true, true); - /* flush unsaved changes and reload from history file */ + /* flush unsaved changes and reload from delta file */ UCI_TRAP_SAVE(ctx, done); - if (p->has_history) { + if (p->has_delta) { if (!overwrite) { name = uci_strdup(ctx, p->e.name); path = uci_strdup(ctx, p->path); - /* dump our own changes to the history file */ - if (!uci_list_empty(&p->history)) + /* dump our own changes to the delta file */ + if (!uci_list_empty(&p->delta)) UCI_INTERNAL(uci_save, ctx, p); /* @@ -713,15 +713,15 @@ void uci_file_commit(struct uci_context *ctx, struct uci_package **package, bool UCI_INTERNAL(uci_import, ctx, f, name, &p, true); p->path = path; - p->has_history = true; + p->has_delta = true; *package = p; /* freed together with the uci_package */ path = NULL; } - /* flush history */ - if (!uci_load_history(ctx, p, true)) + /* flush delta */ + if (!uci_load_delta(ctx, p, true)) goto done; } @@ -841,8 +841,8 @@ static struct uci_package *uci_file_load(struct uci_context *ctx, const char *na if (package) { package->path = filename; - package->has_history = confdir; - uci_load_history(ctx, package, false); + package->has_delta = confdir; + uci_load_delta(ctx, package, false); } done: diff --git a/history.c b/history.c deleted file mode 100644 index 649ead1..0000000 --- a/history.c +++ /dev/null @@ -1,483 +0,0 @@ -/* - * libuci - Library for the Unified Configuration Interface - * Copyright (C) 2008 Felix Fietkau - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -/* - * This file contains the code for handling uci config history files - */ - -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include - -/* record a change that was done to a package */ -void -uci_add_history(struct uci_context *ctx, struct uci_list *list, int cmd, const char *section, const char *option, const char *value) -{ - struct uci_history *h; - int size = strlen(section) + 1; - char *ptr; - - if (value) - size += strlen(value) + 1; - - h = uci_alloc_element(ctx, history, option, size); - ptr = uci_dataptr(h); - h->cmd = cmd; - h->section = strcpy(ptr, section); - if (value) { - ptr += strlen(ptr) + 1; - h->value = strcpy(ptr, value); - } - uci_list_add(list, &h->e.list); -} - -void -uci_free_history(struct uci_history *h) -{ - if (!h) - return; - if ((h->section != NULL) && - (h->section != uci_dataptr(h))) { - free(h->section); - free(h->value); - } - uci_free_element(&h->e); -} - - -int uci_set_savedir(struct uci_context *ctx, const char *dir) -{ - char *sdir; - - UCI_HANDLE_ERR(ctx); - UCI_ASSERT(ctx, dir != NULL); - - sdir = uci_strdup(ctx, dir); - if (ctx->savedir != uci_savedir) - free(ctx->savedir); - ctx->savedir = sdir; - return 0; -} - -int uci_add_history_path(struct uci_context *ctx, const char *dir) -{ - struct uci_element *e; - - UCI_HANDLE_ERR(ctx); - UCI_ASSERT(ctx, dir != NULL); - e = uci_alloc_generic(ctx, UCI_TYPE_PATH, dir, sizeof(struct uci_element)); - uci_list_add(&ctx->history_path, &e->list); - - return 0; -} - -static inline int uci_parse_history_tuple(struct uci_context *ctx, char **buf, struct uci_ptr *ptr) -{ - int c = UCI_CMD_CHANGE; - - switch(**buf) { - case '^': - c = UCI_CMD_REORDER; - break; - case '-': - c = UCI_CMD_REMOVE; - break; - case '@': - c = UCI_CMD_RENAME; - break; - case '+': - /* UCI_CMD_ADD is used for anonymous sections or list values */ - c = UCI_CMD_ADD; - break; - case '|': - c = UCI_CMD_LIST_ADD; - break; - } - - if (c != UCI_CMD_CHANGE) - *buf += 1; - - UCI_INTERNAL(uci_parse_ptr, ctx, ptr, *buf); - - if (!ptr->section) - goto error; - if (ptr->flags & UCI_LOOKUP_EXTENDED) - goto error; - - switch(c) { - case UCI_CMD_REORDER: - if (!ptr->value || ptr->option) - goto error; - break; - case UCI_CMD_RENAME: - if (!ptr->value || !uci_validate_name(ptr->value)) - goto error; - break; - case UCI_CMD_LIST_ADD: - if (!ptr->option) - goto error; - } - - return c; - -error: - UCI_THROW(ctx, UCI_ERR_INVAL); - return 0; -} - -static void uci_parse_history_line(struct uci_context *ctx, struct uci_package *p, char *buf) -{ - struct uci_element *e = NULL; - struct uci_ptr ptr; - int cmd; - - cmd = uci_parse_history_tuple(ctx, &buf, &ptr); - if (strcmp(ptr.package, p->e.name) != 0) - goto error; - - if (ctx->flags & UCI_FLAG_SAVED_HISTORY) - uci_add_history(ctx, &p->saved_history, cmd, ptr.section, ptr.option, ptr.value); - - switch(cmd) { - case UCI_CMD_REORDER: - expand_ptr(ctx, &ptr, true); - if (!ptr.s) - UCI_THROW(ctx, UCI_ERR_NOTFOUND); - UCI_INTERNAL(uci_reorder_section, ctx, ptr.s, strtoul(ptr.value, NULL, 10)); - break; - case UCI_CMD_RENAME: - UCI_INTERNAL(uci_rename, ctx, &ptr); - break; - case UCI_CMD_REMOVE: - UCI_INTERNAL(uci_delete, ctx, &ptr); - break; - case UCI_CMD_LIST_ADD: - UCI_INTERNAL(uci_add_list, ctx, &ptr); - break; - case UCI_CMD_ADD: - case UCI_CMD_CHANGE: - UCI_INTERNAL(uci_set, ctx, &ptr); - e = ptr.last; - if (!ptr.option && e && (cmd == UCI_CMD_ADD)) - uci_to_section(e)->anonymous = true; - break; - } - return; -error: - UCI_THROW(ctx, UCI_ERR_PARSE); -} - -/* returns the number of changes that were successfully parsed */ -static int uci_parse_history(struct uci_context *ctx, FILE *stream, struct uci_package *p) -{ - struct uci_parse_context *pctx; - int changes = 0; - - /* make sure no memory from previous parse attempts is leaked */ - uci_cleanup(ctx); - - pctx = (struct uci_parse_context *) uci_malloc(ctx, sizeof(struct uci_parse_context)); - ctx->pctx = pctx; - pctx->file = stream; - - while (!feof(pctx->file)) { - uci_getln(ctx, 0); - if (!pctx->buf[0]) - continue; - - /* - * ignore parse errors in single lines, we want to preserve as much - * history as possible - */ - UCI_TRAP_SAVE(ctx, error); - uci_parse_history_line(ctx, p, pctx->buf); - UCI_TRAP_RESTORE(ctx); - changes++; -error: - continue; - } - - /* no error happened, we can get rid of the parser context now */ - uci_cleanup(ctx); - return changes; -} - -/* returns the number of changes that were successfully parsed */ -static int uci_load_history_file(struct uci_context *ctx, struct uci_package *p, char *filename, FILE **f, bool flush) -{ - FILE *stream = NULL; - int changes = 0; - - UCI_TRAP_SAVE(ctx, done); - stream = uci_open_stream(ctx, filename, SEEK_SET, flush, false); - if (p) - changes = uci_parse_history(ctx, stream, p); - UCI_TRAP_RESTORE(ctx); -done: - if (f) - *f = stream; - else if (stream) - uci_close_stream(stream); - return changes; -} - -/* returns the number of changes that were successfully parsed */ -__private int uci_load_history(struct uci_context *ctx, struct uci_package *p, bool flush) -{ - struct uci_element *e; - char *filename = NULL; - FILE *f = NULL; - int changes = 0; - - if (!p->has_history) - return 0; - - uci_foreach_element(&ctx->history_path, e) { - if ((asprintf(&filename, "%s/%s", e->name, p->e.name) < 0) || !filename) - UCI_THROW(ctx, UCI_ERR_MEM); - - uci_load_history_file(ctx, p, filename, NULL, false); - free(filename); - } - - if ((asprintf(&filename, "%s/%s", ctx->savedir, p->e.name) < 0) || !filename) - UCI_THROW(ctx, UCI_ERR_MEM); - - changes = uci_load_history_file(ctx, p, filename, &f, flush); - if (flush && f && (changes > 0)) { - rewind(f); - if (ftruncate(fileno(f), 0) < 0) { - uci_close_stream(f); - UCI_THROW(ctx, UCI_ERR_IO); - } - } - if (filename) - free(filename); - uci_close_stream(f); - ctx->err = 0; - return changes; -} - -static void uci_filter_history(struct uci_context *ctx, const char *name, const char *section, const char *option) -{ - struct uci_parse_context *pctx; - struct uci_element *e, *tmp; - struct uci_list list; - char *filename = NULL; - struct uci_ptr ptr; - FILE *f = NULL; - - uci_list_init(&list); - uci_alloc_parse_context(ctx); - pctx = ctx->pctx; - - if ((asprintf(&filename, "%s/%s", ctx->savedir, name) < 0) || !filename) - UCI_THROW(ctx, UCI_ERR_MEM); - - UCI_TRAP_SAVE(ctx, done); - f = uci_open_stream(ctx, filename, SEEK_SET, true, false); - pctx->file = f; - while (!feof(f)) { - struct uci_element *e; - char *buf; - - uci_getln(ctx, 0); - buf = pctx->buf; - if (!buf[0]) - continue; - - /* NB: need to allocate the element before the call to - * uci_parse_history_tuple, otherwise the original string - * gets modified before it is saved */ - e = uci_alloc_generic(ctx, UCI_TYPE_HISTORY, pctx->buf, sizeof(struct uci_element)); - uci_list_add(&list, &e->list); - - uci_parse_history_tuple(ctx, &buf, &ptr); - if (section) { - if (!ptr.section || (strcmp(section, ptr.section) != 0)) - continue; - } - if (option) { - if (!ptr.option || (strcmp(option, ptr.option) != 0)) - continue; - } - /* match, drop this element again */ - uci_free_element(e); - } - - /* rebuild the history file */ - rewind(f); - if (ftruncate(fileno(f), 0) < 0) - UCI_THROW(ctx, UCI_ERR_IO); - uci_foreach_element_safe(&list, tmp, e) { - fprintf(f, "%s\n", e->name); - uci_free_element(e); - } - UCI_TRAP_RESTORE(ctx); - -done: - if (filename) - free(filename); - uci_close_stream(pctx->file); - uci_foreach_element_safe(&list, tmp, e) { - uci_free_element(e); - } - uci_cleanup(ctx); -} - -int uci_revert(struct uci_context *ctx, struct uci_ptr *ptr) -{ - char *package = NULL; - char *section = NULL; - char *option = NULL; - - UCI_HANDLE_ERR(ctx); - expand_ptr(ctx, ptr, false); - UCI_ASSERT(ctx, ptr->p->has_history); - - /* - * - flush unwritten changes - * - save the package name - * - unload the package - * - filter the history - * - reload the package - */ - UCI_TRAP_SAVE(ctx, error); - UCI_INTERNAL(uci_save, ctx, ptr->p); - - /* NB: need to clone package, section and option names, - * as they may get freed on uci_free_package() */ - package = uci_strdup(ctx, ptr->p->e.name); - if (ptr->section) - section = uci_strdup(ctx, ptr->section); - if (ptr->option) - option = uci_strdup(ctx, ptr->option); - - uci_free_package(&ptr->p); - uci_filter_history(ctx, package, section, option); - - UCI_INTERNAL(uci_load, ctx, package, &ptr->p); - UCI_TRAP_RESTORE(ctx); - ctx->err = 0; - -error: - if (package) - free(package); - if (section) - free(section); - if (option) - free(option); - if (ctx->err) - UCI_THROW(ctx, ctx->err); - return 0; -} - -int uci_save(struct uci_context *ctx, struct uci_package *p) -{ - FILE *f = NULL; - char *filename = NULL; - struct uci_element *e, *tmp; - struct stat statbuf; - - UCI_HANDLE_ERR(ctx); - UCI_ASSERT(ctx, p != NULL); - - /* - * if the config file was outside of the /etc/config path, - * don't save the history to a file, update the real file - * directly. - * does not modify the uci_package pointer - */ - if (!p->has_history) - return uci_commit(ctx, &p, false); - - if (uci_list_empty(&p->history)) - return 0; - - if (stat(ctx->savedir, &statbuf) < 0) - mkdir(ctx->savedir, UCI_DIRMODE); - else if ((statbuf.st_mode & S_IFMT) != S_IFDIR) - UCI_THROW(ctx, UCI_ERR_IO); - - if ((asprintf(&filename, "%s/%s", ctx->savedir, p->e.name) < 0) || !filename) - UCI_THROW(ctx, UCI_ERR_MEM); - - uci_foreach_element(&ctx->hooks, tmp) { - struct uci_hook *hook = uci_to_hook(tmp); - - if (!hook->ops->set) - continue; - - uci_foreach_element(&p->history, e) { - hook->ops->set(hook->ops, p, uci_to_history(e)); - } - } - - ctx->err = 0; - UCI_TRAP_SAVE(ctx, done); - f = uci_open_stream(ctx, filename, SEEK_END, true, true); - UCI_TRAP_RESTORE(ctx); - - uci_foreach_element_safe(&p->history, tmp, e) { - struct uci_history *h = uci_to_history(e); - char *prefix = ""; - - switch(h->cmd) { - case UCI_CMD_REMOVE: - prefix = "-"; - break; - case UCI_CMD_RENAME: - prefix = "@"; - break; - case UCI_CMD_ADD: - prefix = "+"; - break; - case UCI_CMD_REORDER: - prefix = "^"; - break; - case UCI_CMD_LIST_ADD: - prefix = "|"; - break; - default: - break; - } - - fprintf(f, "%s%s.%s", prefix, p->e.name, h->section); - if (e->name) - fprintf(f, ".%s", e->name); - - if (h->cmd == UCI_CMD_REMOVE) - fprintf(f, "\n"); - else - fprintf(f, "=%s\n", h->value); - uci_free_history(h); - } - -done: - uci_close_stream(f); - if (filename) - free(filename); - if (ctx->err) - UCI_THROW(ctx, ctx->err); - - return 0; -} - - diff --git a/libuci.c b/libuci.c index 5b22ef1..1a09fc7 100644 --- a/libuci.c +++ b/libuci.c @@ -44,7 +44,7 @@ static void uci_unload_plugin(struct uci_context *ctx, struct uci_plugin *p); #include "uci_internal.h" #include "list.c" -#include "history.c" +#include "delta.c" /* exported functions */ struct uci_context *uci_alloc_context(void) @@ -57,11 +57,11 @@ struct uci_context *uci_alloc_context(void) memset(ctx, 0, sizeof(struct uci_context)); uci_list_init(&ctx->root); - uci_list_init(&ctx->history_path); + uci_list_init(&ctx->delta_path); uci_list_init(&ctx->backends); uci_list_init(&ctx->hooks); uci_list_init(&ctx->plugins); - ctx->flags = UCI_FLAG_STRICT | UCI_FLAG_SAVED_HISTORY; + ctx->flags = UCI_FLAG_STRICT | UCI_FLAG_SAVED_DELTA; ctx->confdir = (char *) uci_confdir; ctx->savedir = (char *) uci_savedir; @@ -87,7 +87,7 @@ void uci_free_context(struct uci_context *ctx) struct uci_package *p = uci_to_package(e); uci_free_package(&p); } - uci_foreach_element_safe(&ctx->history_path, tmp, e) { + uci_foreach_element_safe(&ctx->delta_path, tmp, e) { uci_free_element(e); } UCI_TRAP_RESTORE(ctx); diff --git a/list.c b/list.c index 1a080fa..cee1063 100644 --- a/list.c +++ b/list.c @@ -224,8 +224,8 @@ uci_alloc_package(struct uci_context *ctx, const char *name) p = uci_alloc_element(ctx, package, name, 0); p->ctx = ctx; uci_list_init(&p->sections); - uci_list_init(&p->history); - uci_list_init(&p->saved_history); + uci_list_init(&p->delta); + uci_list_init(&p->saved_delta); return p; } @@ -243,11 +243,11 @@ uci_free_package(struct uci_package **package) uci_foreach_element_safe(&p->sections, tmp, e) { uci_free_section(uci_to_section(e)); } - uci_foreach_element_safe(&p->history, tmp, e) { - uci_free_history(uci_to_history(e)); + uci_foreach_element_safe(&p->delta, tmp, e) { + uci_free_delta(uci_to_delta(e)); } - uci_foreach_element_safe(&p->saved_history, tmp, e) { - uci_free_history(uci_to_history(e)); + uci_foreach_element_safe(&p->saved_delta, tmp, e) { + uci_free_delta(uci_to_delta(e)); } uci_free_element(&p->e); *package = NULL; @@ -452,8 +452,8 @@ static void uci_add_element_list(struct uci_context *ctx, struct uci_ptr *ptr, b struct uci_package *p; p = ptr->p; - if (!internal && p->has_history) - uci_add_history(ctx, &p->history, UCI_CMD_LIST_ADD, ptr->section, ptr->option, ptr->value); + if (!internal && p->has_delta) + uci_add_delta(ctx, &p->delta, UCI_CMD_LIST_ADD, ptr->section, ptr->option, ptr->value); e = uci_alloc_generic(ctx, UCI_TYPE_ITEM, ptr->value, sizeof(struct uci_option)); uci_list_add(&ptr->o->v.list, &e->list); @@ -461,7 +461,7 @@ static void uci_add_element_list(struct uci_context *ctx, struct uci_ptr *ptr, b int uci_rename(struct uci_context *ctx, struct uci_ptr *ptr) { - /* NB: UCI_INTERNAL use means without history tracking */ + /* NB: UCI_INTERNAL use means without delta tracking */ bool internal = ctx->internal; struct uci_element *e; struct uci_package *p; @@ -475,8 +475,8 @@ int uci_rename(struct uci_context *ctx, struct uci_ptr *ptr) UCI_ASSERT(ctx, ptr->s); UCI_ASSERT(ctx, ptr->value); - if (!internal && p->has_history) - uci_add_history(ctx, &p->history, UCI_CMD_RENAME, ptr->section, ptr->option, ptr->value); + if (!internal && p->has_delta) + uci_add_delta(ctx, &p->delta, UCI_CMD_RENAME, ptr->section, ptr->option, ptr->value); n = uci_strdup(ctx, ptr->value); if (e->name) @@ -497,9 +497,9 @@ int uci_reorder_section(struct uci_context *ctx, struct uci_section *s, int pos) UCI_HANDLE_ERR(ctx); uci_list_set_pos(&s->package->sections, &s->e.list, pos); - if (!ctx->internal && p->has_history) { + if (!ctx->internal && p->has_delta) { sprintf(order, "%d", pos); - uci_add_history(ctx, &p->history, UCI_CMD_REORDER, s->e.name, NULL, order); + uci_add_delta(ctx, &p->delta, UCI_CMD_REORDER, s->e.name, NULL, order); } return 0; @@ -515,8 +515,8 @@ int uci_add_section(struct uci_context *ctx, struct uci_package *p, const char * s = uci_alloc_section(p, type, NULL); uci_fixup_section(ctx, s); *res = s; - if (!internal && p->has_history) - uci_add_history(ctx, &p->history, UCI_CMD_ADD, s->e.name, NULL, type); + if (!internal && p->has_delta) + uci_add_delta(ctx, &p->delta, UCI_CMD_ADD, s->e.name, NULL, type); return 0; } @@ -535,8 +535,8 @@ int uci_delete(struct uci_context *ctx, struct uci_ptr *ptr) UCI_ASSERT(ctx, ptr->s); - if (!internal && p->has_history) - uci_add_history(ctx, &p->history, UCI_CMD_REMOVE, ptr->section, ptr->option, NULL); + if (!internal && p->has_delta) + uci_add_delta(ctx, &p->delta, UCI_CMD_REMOVE, ptr->section, ptr->option, NULL); uci_free_any(&e); @@ -550,7 +550,7 @@ int uci_delete(struct uci_context *ctx, struct uci_ptr *ptr) int uci_add_list(struct uci_context *ctx, struct uci_ptr *ptr) { - /* NB: UCI_INTERNAL use means without history tracking */ + /* NB: UCI_INTERNAL use means without delta tracking */ bool internal = ctx->internal; struct uci_option *prev = NULL; const char *value2 = NULL; @@ -591,7 +591,7 @@ int uci_add_list(struct uci_context *ctx, struct uci_ptr *ptr) int uci_set(struct uci_context *ctx, struct uci_ptr *ptr) { - /* NB: UCI_INTERNAL use means without history tracking */ + /* NB: UCI_INTERNAL use means without delta tracking */ bool internal = ctx->internal; UCI_HANDLE_ERR(ctx); @@ -644,8 +644,8 @@ int uci_set(struct uci_context *ctx, struct uci_ptr *ptr) UCI_THROW(ctx, UCI_ERR_INVAL); } - if (!internal && ptr->p->has_history) - uci_add_history(ctx, &ptr->p->history, UCI_CMD_CHANGE, ptr->section, ptr->option, ptr->value); + if (!internal && ptr->p->has_delta) + uci_add_delta(ctx, &ptr->p->delta, UCI_CMD_CHANGE, ptr->section, ptr->option, ptr->value); return 0; } diff --git a/lua/uci.c b/lua/uci.c index b355420..efa8c1a 100644 --- a/lua/uci.c +++ b/lua/uci.c @@ -689,10 +689,10 @@ uci_lua_revert(lua_State *L) static void uci_lua_add_change(lua_State *L, struct uci_element *e) { - struct uci_history *h; + struct uci_delta *h; const char *name; - h = uci_to_history(e); + h = uci_to_delta(e); if (!h->section) return; @@ -728,14 +728,14 @@ uci_lua_changes_pkg(lua_State *L, struct uci_context *ctx, const char *package) return; } - if (uci_list_empty(&p->history) && uci_list_empty(&p->saved_history)) + if (uci_list_empty(&p->delta) && uci_list_empty(&p->saved_delta)) goto done; lua_newtable(L); - uci_foreach_element(&p->saved_history, e) { + uci_foreach_element(&p->saved_delta, e) { uci_lua_add_change(L, e); } - uci_foreach_element(&p->history, e) { + uci_foreach_element(&p->delta, e) { uci_lua_add_change(L, e); } lua_setfield(L, -2, p->e.name); @@ -810,14 +810,14 @@ uci_lua_get_savedir(lua_State *L) } static int -uci_lua_add_history(lua_State *L) +uci_lua_add_delta(lua_State *L) { struct uci_context *ctx; int ret, offset = 0; ctx = find_context(L, &offset); luaL_checkstring(L, 1 + offset); - ret = uci_add_history_path(ctx, lua_tostring(L, -1)); + ret = uci_add_delta_path(ctx, lua_tostring(L, -1)); return uci_push_status(L, ctx, false); } @@ -902,7 +902,7 @@ static const luaL_Reg uci[] = { { "reorder", uci_lua_reorder }, { "changes", uci_lua_changes }, { "foreach", uci_lua_foreach }, - { "add_history", uci_lua_add_history }, + { "add_delta", uci_lua_add_delta }, { "load_plugins", uci_lua_load_plugins }, { "get_confdir", uci_lua_get_confdir }, { "set_confdir", uci_lua_set_confdir }, diff --git a/uci.h b/uci.h index 4fa0b6a..5644ca8 100644 --- a/uci.h +++ b/uci.h @@ -62,7 +62,7 @@ struct uci_element; struct uci_package; struct uci_section; struct uci_option; -struct uci_history; +struct uci_delta; struct uci_context; struct uci_backend; struct uci_parse_context; @@ -104,7 +104,7 @@ extern void uci_get_errorstr(struct uci_context *ctx, char **dest, const char *s * @single: ignore the 'package' keyword and parse everything into a single package * * the name parameter is for config files that don't explicitly use the 'package <...>' keyword - * if 'package' points to a non-null struct pointer, enable history tracking and merge + * if 'package' points to a non-null struct pointer, enable delta tracking and merge */ extern int uci_import(struct uci_context *ctx, FILE *stream, const char *name, struct uci_package **package, bool single); @@ -204,7 +204,7 @@ extern int uci_rename(struct uci_context *ctx, struct uci_ptr *ptr); extern int uci_delete(struct uci_context *ctx, struct uci_ptr *ptr); /** - * uci_save: save change history for a package + * uci_save: save change delta for a package * @ctx: uci context * @p: uci_package struct */ @@ -214,7 +214,7 @@ extern int uci_save(struct uci_context *ctx, struct uci_package *p); * uci_commit: commit changes to a package * @ctx: uci context * @p: uci_package struct pointer - * @overwrite: overwrite existing config data and flush history + * @overwrite: overwrite existing config data and flush delta * * committing may reload the whole uci_package data, * the supplied pointer is updated accordingly @@ -230,7 +230,7 @@ extern int uci_commit(struct uci_context *ctx, struct uci_package **p, bool over extern int uci_list_configs(struct uci_context *ctx, char ***list); /** - * uci_set_savedir: override the default history save directory + * uci_set_savedir: override the default delta save directory * @ctx: uci context * @dir: directory name */ @@ -244,14 +244,14 @@ extern int uci_set_savedir(struct uci_context *ctx, const char *dir); extern int uci_set_confdir(struct uci_context *ctx, const char *dir); /** - * uci_add_history_path: add a directory to the search path for change history files + * uci_add_delta_path: add a directory to the search path for change delta files * @ctx: uci context * @dir: directory name * * This function allows you to add directories, which contain 'overlays' * for the active config, that will never be committed. */ -extern int uci_add_history_path(struct uci_context *ctx, const char *dir); +extern int uci_add_delta_path(struct uci_context *ctx, const char *dir); /** * uci_revert: revert all changes to a config item @@ -336,7 +336,7 @@ int uci_parse_ptr(struct uci_context *ctx, struct uci_ptr *ptr, char *str); /* UCI data structures */ enum uci_type { UCI_TYPE_UNSPEC = 0, - UCI_TYPE_HISTORY = 1, + UCI_TYPE_DELTA = 1, UCI_TYPE_PACKAGE = 2, UCI_TYPE_SECTION = 3, UCI_TYPE_OPTION = 4, @@ -356,7 +356,7 @@ enum uci_flags { UCI_FLAG_STRICT = (1 << 0), /* strict mode for the parser */ UCI_FLAG_PERROR = (1 << 1), /* print parser error messages */ UCI_FLAG_EXPORT_NAME = (1 << 2), /* when exporting, name unnamed sections */ - UCI_FLAG_SAVED_HISTORY = (1 << 3), /* store the saved history in memory as well */ + UCI_FLAG_SAVED_DELTA = (1 << 3), /* store the saved delta in memory as well */ }; struct uci_element @@ -396,8 +396,8 @@ struct uci_context char *confdir; char *savedir; - /* search path for history files */ - struct uci_list history_path; + /* search path for delta files */ + struct uci_list delta_path; /* private: */ int err; @@ -416,15 +416,15 @@ struct uci_package struct uci_element e; struct uci_list sections; struct uci_context *ctx; - bool has_history; + bool has_delta; char *path; /* private: */ struct uci_backend *backend; void *priv; int n_section; - struct uci_list history; - struct uci_list saved_history; + struct uci_list delta; + struct uci_list saved_delta; }; struct uci_section @@ -456,7 +456,7 @@ enum uci_command { UCI_CMD_LIST_ADD, }; -struct uci_history +struct uci_delta { struct uci_element e; enum uci_command cmd; @@ -487,7 +487,7 @@ struct uci_ptr struct uci_hook_ops { void (*load)(const struct uci_hook_ops *ops, struct uci_package *p); - void (*set)(const struct uci_hook_ops *ops, struct uci_package *p, struct uci_history *e); + void (*set)(const struct uci_hook_ops *ops, struct uci_package *p, struct uci_delta *e); }; struct uci_hook @@ -575,7 +575,7 @@ struct uci_plugin /* wrappers for dynamic type handling */ #define uci_type_backend UCI_TYPE_BACKEND -#define uci_type_history UCI_TYPE_HISTORY +#define uci_type_delta UCI_TYPE_DELTA #define uci_type_package UCI_TYPE_PACKAGE #define uci_type_section UCI_TYPE_SECTION #define uci_type_option UCI_TYPE_OPTION @@ -586,7 +586,7 @@ struct uci_plugin #ifdef UCI_DEBUG_TYPECAST static const char *uci_typestr[] = { [uci_type_backend] = "backend", - [uci_type_history] = "history", + [uci_type_delta] = "delta", [uci_type_package] = "package", [uci_type_section] = "section", [uci_type_option] = "option", @@ -609,7 +609,7 @@ static void uci_typecast_error(int from, int to) } BUILD_CAST(backend) -BUILD_CAST(history) +BUILD_CAST(delta) BUILD_CAST(package) BUILD_CAST(section) BUILD_CAST(option) @@ -618,7 +618,7 @@ BUILD_CAST(plugin) #else #define uci_to_backend(ptr) container_of(ptr, struct uci_backend, e) -#define uci_to_history(ptr) container_of(ptr, struct uci_history, e) +#define uci_to_delta(ptr) container_of(ptr, struct uci_delta, e) #define uci_to_package(ptr) container_of(ptr, struct uci_package, e) #define uci_to_section(ptr) container_of(ptr, struct uci_section, e) #define uci_to_option(ptr) container_of(ptr, struct uci_option, e) diff --git a/uci_internal.h b/uci_internal.h index 7ce00d0..728920d 100644 --- a/uci_internal.h +++ b/uci_internal.h @@ -44,8 +44,8 @@ __plugin void *uci_malloc(struct uci_context *ctx, size_t size); __plugin void *uci_realloc(struct uci_context *ctx, void *ptr, size_t size); __plugin char *uci_strdup(struct uci_context *ctx, const char *str); __plugin bool uci_validate_str(const char *str, bool name); -__plugin void uci_add_history(struct uci_context *ctx, struct uci_list *list, int cmd, const char *section, const char *option, const char *value); -__plugin void uci_free_history(struct uci_history *h); +__plugin void uci_add_delta(struct uci_context *ctx, struct uci_list *list, int cmd, const char *section, const char *option, const char *value); +__plugin void uci_free_delta(struct uci_delta *h); __plugin struct uci_package *uci_alloc_package(struct uci_context *ctx, const char *name); __private FILE *uci_open_stream(struct uci_context *ctx, const char *filename, int pos, bool write, bool create); @@ -60,7 +60,7 @@ __private struct uci_element *uci_lookup_list(struct uci_list *list, const char __private void uci_fixup_section(struct uci_context *ctx, struct uci_section *s); __private void uci_free_package(struct uci_package **package); -__private int uci_load_history(struct uci_context *ctx, struct uci_package *p, bool flush); +__private int uci_load_delta(struct uci_context *ctx, struct uci_package *p, bool flush); static inline bool uci_validate_package(const char *str) {