X-Git-Url: http://git.archive.openwrt.org/?p=project%2Fuci.git;a=blobdiff_plain;f=history.c;h=c698192eda56f760665933f589fb32764927c070;hp=c46c408c73ff4f2a0aabcc27fedec3ff3d0eb564;hb=ec7b1de53e16991b071c892fac149b4bfe73c539;hpb=7ca5c675d7dc21fa7767b64a42a1705bb800fc86 diff --git a/history.c b/history.c index c46c408..c698192 100644 --- a/history.c +++ b/history.c @@ -27,8 +27,8 @@ #include /* record a change that was done to a package */ -static void -uci_add_history(struct uci_context *ctx, struct uci_list *list, int cmd, char *section, char *option, char *value) +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; @@ -48,7 +48,7 @@ uci_add_history(struct uci_context *ctx, struct uci_list *list, int cmd, char *s uci_list_add(list, &h->e.list); } -static void +void uci_free_history(struct uci_history *h) { if (!h) @@ -88,65 +88,94 @@ int uci_add_history_path(struct uci_context *ctx, const char *dir) return 0; } -static inline void uci_parse_history_tuple(struct uci_context *ctx, char **buf, char **package, char **section, char **option, char **value, int *cmd) +static inline int uci_parse_history_tuple(struct uci_context *ctx, char **buf, struct uci_ptr *ptr) { int c = UCI_CMD_CHANGE; - if (**buf == '-') { + switch(**buf) { + case '^': + c = UCI_CMD_REORDER; + break; + case '-': c = UCI_CMD_REMOVE; - *buf += 1; - } else if (**buf == '@') { + break; + case '@': c = UCI_CMD_RENAME; - *buf += 1; - } else if (**buf == '+') { - /* UCI_CMD_ADD is used for anonymous sections */ + 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; } - if (cmd) - *cmd = c; - UCI_INTERNAL(uci_parse_tuple, ctx, *buf, package, section, option, value); - if (!*section[0]) - UCI_THROW(ctx, UCI_ERR_PARSE); + 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; - bool delete = false; - bool rename = false; - char *package = NULL; - char *section = NULL; - char *option = NULL; - char *value = NULL; + struct uci_ptr ptr; int cmd; - uci_parse_history_tuple(ctx, &buf, &package, §ion, &option, &value, &cmd); - if (!package || (strcmp(package, p->e.name) != 0)) - goto error; - if (!uci_validate_name(section)) - goto error; - if (option && !uci_validate_name(option)) - goto error; - if (rename && !uci_validate_str(value, (option || delete))) + 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, section, option, value); + 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, p, section, option, value); + UCI_INTERNAL(uci_rename, ctx, &ptr); break; case UCI_CMD_REMOVE: - UCI_INTERNAL(uci_delete, ctx, p, section, option); + 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, p, section, option, value, &e); - if (!option && e && (cmd == UCI_CMD_ADD)) + UCI_INTERNAL(uci_set, ctx, &ptr); + e = ptr.last; + if (!ptr.option && e && (cmd == UCI_CMD_ADD)) uci_to_section(e)->anonymous = true; break; } @@ -234,25 +263,25 @@ static int uci_load_history(struct uci_context *ctx, struct uci_package *p, bool changes = uci_load_history_file(ctx, p, filename, &f, flush); if (flush && f && (changes > 0)) { rewind(f); - ftruncate(fileno(f), 0); + 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->errno = 0; + ctx->err = 0; return changes; } -static void uci_filter_history(struct uci_context *ctx, const char *name, char *section, char *option) +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; - char *p = NULL; - char *s = NULL; - char *o = NULL; - char *v = NULL; + struct uci_ptr ptr; FILE *f = NULL; uci_list_init(&list); @@ -280,13 +309,13 @@ static void uci_filter_history(struct uci_context *ctx, const char *name, char * 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, &p, &s, &o, &v, NULL); + uci_parse_history_tuple(ctx, &buf, &ptr); if (section) { - if (!s || (strcmp(section, s) != 0)) + if (!ptr.section || (strcmp(section, ptr.section) != 0)) continue; } if (option) { - if (!o || (strcmp(option, o) != 0)) + if (!ptr.option || (strcmp(option, ptr.option) != 0)) continue; } /* match, drop this element again */ @@ -295,7 +324,8 @@ static void uci_filter_history(struct uci_context *ctx, const char *name, char * /* rebuild the history file */ rewind(f); - ftruncate(fileno(f), 0); + 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); @@ -305,23 +335,22 @@ static void uci_filter_history(struct uci_context *ctx, const char *name, char * done: if (filename) free(filename); - uci_close_stream(f); + 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_package **pkg, char *section, char *option) +int uci_revert(struct uci_context *ctx, struct uci_ptr *ptr) { - struct uci_package *p; - char *name = NULL; + char *package = NULL; + char *section = NULL; + char *option = NULL; UCI_HANDLE_ERR(ctx); - UCI_ASSERT(ctx, pkg != NULL); - p = *pkg; - UCI_ASSERT(ctx, p != NULL); - UCI_ASSERT(ctx, p->has_history); + expand_ptr(ctx, ptr, false); + UCI_ASSERT(ctx, ptr->p->has_history); /* * - flush unwritten changes @@ -331,22 +360,32 @@ int uci_revert(struct uci_context *ctx, struct uci_package **pkg, char *section, * - reload the package */ UCI_TRAP_SAVE(ctx, error); - UCI_INTERNAL(uci_save, ctx, p); - name = uci_strdup(ctx, p->e.name); + UCI_INTERNAL(uci_save, ctx, ptr->p); - *pkg = NULL; - uci_free_package(&p); - uci_filter_history(ctx, name, section, option); + /* 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_INTERNAL(uci_load, ctx, name, &p); + 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->errno = 0; + ctx->err = 0; error: - if (name) - free(name); - if (ctx->errno) - UCI_THROW(ctx, ctx->errno); + if (package) + free(package); + if (section) + free(section); + if (option) + free(option); + if (ctx->err) + UCI_THROW(ctx, ctx->err); return 0; } @@ -355,6 +394,7 @@ 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); @@ -371,32 +411,55 @@ int uci_save(struct uci_context *ctx, struct uci_package *p) 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); - ctx->errno = 0; + 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: - fprintf(f, "-"); + prefix = "-"; break; case UCI_CMD_RENAME: - fprintf(f, "@"); + prefix = "@"; break; case UCI_CMD_ADD: - fprintf(f, "+"); + prefix = "+"; + break; + case UCI_CMD_REORDER: + prefix = "^"; + break; + case UCI_CMD_LIST_ADD: + prefix = "|"; break; default: break; } - fprintf(f, "%s.%s", p->e.name, h->section); + fprintf(f, "%s%s.%s", prefix, p->e.name, h->section); if (e->name) fprintf(f, ".%s", e->name); @@ -411,8 +474,8 @@ done: uci_close_stream(f); if (filename) free(filename); - if (ctx->errno) - UCI_THROW(ctx, ctx->errno); + if (ctx->err) + UCI_THROW(ctx, ctx->err); return 0; }