file: preserve original file mode after commit
[oweals/uci.git] / delta.c
diff --git a/delta.c b/delta.c
index cdd46bcc5896f1c3742656453d1ff3dc11cd525c..d8bd3a6ccae8d0a6330c9660b3b5ee294cda1a3c 100644 (file)
--- a/delta.c
+++ b/delta.c
@@ -99,10 +99,24 @@ static void uci_delta_save(struct uci_context *ctx, FILE *f,
 int uci_set_savedir(struct uci_context *ctx, const char *dir)
 {
        char *sdir;
+       struct uci_element *e, *tmp;
+       volatile bool exists = false;
 
        UCI_HANDLE_ERR(ctx);
        UCI_ASSERT(ctx, dir != NULL);
 
+       /* Move dir to the end of ctx->delta_path */
+       uci_foreach_element_safe(&ctx->delta_path, tmp, e) {
+               if (!strcmp(e->name, dir)) {
+                       exists = true;
+                       uci_list_del(&e->list);
+                       break;
+               }
+       }
+       if (!exists)
+               e = uci_alloc_generic(ctx, UCI_TYPE_PATH, dir, sizeof(struct uci_element));
+       uci_list_add(&ctx->delta_path, &e->list);
+
        sdir = uci_strdup(ctx, dir);
        if (ctx->savedir != uci_savedir)
                free(ctx->savedir);
@@ -113,13 +127,21 @@ int uci_set_savedir(struct uci_context *ctx, const char *dir)
 int uci_add_delta_path(struct uci_context *ctx, const char *dir)
 {
        struct uci_element *e;
+       struct uci_list *savedir;
 
        UCI_HANDLE_ERR(ctx);
        UCI_ASSERT(ctx, dir != NULL);
-       if (!strcmp(dir, ctx->savedir))
-               return -1;
+
+       /* Duplicate delta path is not allowed */
+       uci_foreach_element(&ctx->delta_path, e) {
+               if (!strcmp(e->name, dir))
+                       UCI_THROW(ctx, UCI_ERR_DUPLICATE);
+       }
+
        e = uci_alloc_generic(ctx, UCI_TYPE_PATH, dir, sizeof(struct uci_element));
-       uci_list_add(&ctx->delta_path, &e->list);
+       /* Keep savedir at the end of ctx->delta_path list */
+       savedir = ctx->delta_path.prev;
+       uci_list_insert(savedir->prev, &e->list);
 
        return 0;
 }
@@ -141,6 +163,9 @@ static inline int uci_parse_delta_tuple(struct uci_context *ctx, struct uci_ptr
        int c;
 
        UCI_INTERNAL(uci_parse_argument, ctx, ctx->pctx->file, &str, &arg);
+       if (str && *str) {
+               goto error;
+       }
        for (c = 0; c <= __UCI_CMD_LAST; c++) {
                if (uci_command_char[c] == *arg)
                        break;
@@ -157,6 +182,9 @@ static inline int uci_parse_delta_tuple(struct uci_context *ctx, struct uci_ptr
                goto error;
        if (ptr->flags & UCI_LOOKUP_EXTENDED)
                goto error;
+       if (c != UCI_CMD_REMOVE && !ptr->value) {
+               goto error;
+       }
 
        switch(c) {
        case UCI_CMD_REORDER:
@@ -170,6 +198,7 @@ static inline int uci_parse_delta_tuple(struct uci_context *ctx, struct uci_ptr
        case UCI_CMD_LIST_ADD:
                if (!ptr->option)
                        goto error;
+               /* fall through */
        case UCI_CMD_LIST_DEL:
                if (!ptr->option)
                        goto error;
@@ -231,7 +260,7 @@ error:
 static int uci_parse_delta(struct uci_context *ctx, FILE *stream, struct uci_package *p)
 {
        struct uci_parse_context *pctx;
-       int changes = 0;
+       volatile int changes = 0;
 
        /* make sure no memory from previous parse attempts is leaked */
        uci_cleanup(ctx);
@@ -266,18 +295,20 @@ error:
 /* 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;
+       FILE *volatile stream = NULL;
+       volatile int changes = 0;
 
        UCI_TRAP_SAVE(ctx, done);
        stream = uci_open_stream(ctx, filename, NULL, SEEK_SET, flush, false);
+       UCI_TRAP_RESTORE(ctx);
+
        if (p)
                changes = uci_parse_delta(ctx, stream, p);
-       UCI_TRAP_RESTORE(ctx);
+
 done:
        if (f)
                *f = stream;
-       else if (stream)
+       else
                uci_close_stream(stream);
        return changes;
 }
@@ -287,8 +318,8 @@ __private int uci_load_delta(struct uci_context *ctx, struct uci_package *p, boo
 {
        struct uci_element *e;
        char *filename = NULL;
-       FILE *f = NULL;
-       int changes = 0;
+       FILE *volatile f = NULL;
+       volatile int changes = 0;
 
        if (!p->has_delta)
                return 0;
@@ -297,21 +328,25 @@ __private int uci_load_delta(struct uci_context *ctx, struct uci_package *p, boo
                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);
+               changes += 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);
+       UCI_TRAP_SAVE(ctx, done);
+       f = uci_open_stream(ctx, filename, NULL, SEEK_SET, flush, false);
+       UCI_TRAP_RESTORE(ctx);
 
-       changes = uci_load_delta_file(ctx, p, filename, &f, flush);
        if (flush && f && (changes > 0)) {
-               rewind(f);
                if (ftruncate(fileno(f), 0) < 0) {
+                       free(filename);
                        uci_close_stream(f);
                        UCI_THROW(ctx, UCI_ERR_IO);
                }
        }
+
+done:
        free(filename);
        uci_close_stream(f);
        ctx->err = 0;
@@ -357,7 +392,7 @@ static void uci_filter_delta(struct uci_context *ctx, const char *name, const ch
                                match = false;
                }
 
-               if (!match) {
+               if (!match && ptr.section) {
                        uci_add_delta(ctx, &list, c,
                                ptr.section, ptr.option, ptr.value);
                }
@@ -385,9 +420,9 @@ done:
 
 int uci_revert(struct uci_context *ctx, struct uci_ptr *ptr)
 {
-       char *package = NULL;
-       char *section = NULL;
-       char *option = NULL;
+       char *volatile package = NULL;
+       char *volatile section = NULL;
+       char *volatile option = NULL;
 
        UCI_HANDLE_ERR(ctx);
        uci_expand_ptr(ctx, ptr, false);
@@ -429,7 +464,7 @@ error:
 
 int uci_save(struct uci_context *ctx, struct uci_package *p)
 {
-       FILE *f = NULL;
+       FILE *volatile f = NULL;
        char *filename = NULL;
        struct uci_element *e, *tmp;
        struct stat statbuf;