reorganize some code, add an extra command for adding unnamed sections
authorFelix Fietkau <nbd@openwrt.org>
Sat, 9 Feb 2008 16:31:21 +0000 (17:31 +0100)
committerFelix Fietkau <nbd@openwrt.org>
Sat, 9 Feb 2008 16:31:21 +0000 (17:31 +0100)
cli.c
file.c
history.c
list.c
uci.h
util.c

diff --git a/cli.c b/cli.c
index 95bc34e..be479c4 100644 (file)
--- a/cli.c
+++ b/cli.c
@@ -43,6 +43,7 @@ enum {
        CMD_EXPORT,
        CMD_COMMIT,
        /* other cmds */
+       CMD_ADD,
        CMD_IMPORT,
        CMD_HELP,
 };
@@ -58,6 +59,7 @@ static void uci_usage(void)
                "\texport     [<config>]\n"
                "\timport     [<config>]\n"
                "\tchanges    [<config>]\n"
+               "\tadd        <config> <section-type>\n"
                "\tshow       [<config>[.<section>[.<option>]]]\n"
                "\tget        <config>.<section>[.<option>]\n"
                "\tset        <config>.<section>[.<option>]=<value>\n"
@@ -233,6 +235,33 @@ static int uci_do_package_cmd(int cmd, int argc, char **argv)
        return 0;
 }
 
+static int uci_do_add(int argc, char **argv)
+{
+       struct uci_package *p = NULL;
+       struct uci_section *s = NULL;
+       int ret;
+
+       if (argc != 3)
+               return 255;
+
+       ret = uci_load(ctx, argv[1], &p);
+       if (ret != UCI_OK)
+               goto done;
+
+       ret = uci_add_section(ctx, p, argv[2], &s);
+       if (ret != UCI_OK)
+               goto done;
+
+       ret = uci_save(ctx, p);
+
+done:
+       if (ret != UCI_OK)
+               cli_perror();
+       else if (s)
+               fprintf(stdout, "%s\n", s->e.name);
+
+       return ret;
+}
 
 static int uci_do_section_cmd(int cmd, int argc, char **argv)
 {
@@ -258,6 +287,8 @@ static int uci_do_section_cmd(int cmd, int argc, char **argv)
        }
        if (uci_parse_tuple(ctx, argv[1], &package, &section, &option, ptr) != UCI_OK)
                return 1;
+       if (section && !section[0])
+               return 1;
 
        if (uci_load(ctx, package, &p) != UCI_OK) {
                cli_perror();
@@ -409,6 +440,8 @@ static int uci_cmd(int argc, char **argv)
                cmd = CMD_IMPORT;
        else if (!strcasecmp(argv[0], "help"))
                cmd = CMD_HELP;
+       else if (!strcasecmp(argv[0], "add"))
+               cmd = CMD_ADD;
        else
                cmd = -1;
 
@@ -426,6 +459,8 @@ static int uci_cmd(int argc, char **argv)
                        return uci_do_package_cmd(cmd, argc, argv);
                case CMD_IMPORT:
                        return uci_do_import(argc, argv);
+               case CMD_ADD:
+                       return uci_do_add(argc, argv);
                case CMD_HELP:
                        uci_usage();
                        return 0;
diff --git a/file.c b/file.c
index 401d4a8..553e519 100644 (file)
--- a/file.c
+++ b/file.c
@@ -119,50 +119,6 @@ static void uci_parse_package(struct uci_context *ctx, char **str, bool single)
        uci_switch_config(ctx);
 }
 
-/* Based on an efficient hash function published by D. J. Bernstein */
-static unsigned int djbhash(unsigned int hash, char *str)
-{
-       int len = strlen(str);
-       int i;
-
-       /* initial value */
-       if (hash == ~0)
-               hash = 5381;
-
-       for(i = 0; i < len; i++) {
-               hash = ((hash << 5) + hash) + str[i];
-       }
-       return (hash & 0x7FFFFFFF);
-}
-
-/* fix up an unnamed section */
-static void uci_fixup_section(struct uci_context *ctx, struct uci_section *s)
-{
-       unsigned int hash = ~0;
-       struct uci_element *e;
-       char buf[16];
-
-       if (!s || s->e.name)
-               return;
-
-       /*
-        * Generate a name for unnamed sections. This is used as reference
-        * when locating or updating the section from apps/scripts.
-        * To make multiple concurrent versions somewhat safe for updating,
-        * the name is generated from a hash of its type and name/value
-        * pairs of its option, and it is prefixed by a counter value.
-        * If the order of the unnamed sections changes for some reason,
-        * updates to them will be rejected.
-        */
-       hash = djbhash(hash, s->type);
-       uci_foreach_element(&s->options, e) {
-               hash = djbhash(hash, e->name);
-               hash = djbhash(hash, uci_to_option(e)->value);
-       }
-       sprintf(buf, "cfg%02x%04x", ++s->package->n_section, hash % (1 << 16));
-       s->e.name = uci_strdup(ctx, buf);
-}
-
 /*
  * parse the 'config' uci command (open a section)
  */
index 14c650a..75e3f40 100644 (file)
--- a/history.c
+++ b/history.c
@@ -88,30 +88,42 @@ 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, bool *delete, bool *rename)
+static inline void uci_parse_history_tuple(struct uci_context *ctx, char **buf, char **package, char **section, char **option, char **value, int *cmd)
 {
+       int c = UCI_CMD_CHANGE;
+
        if (**buf == '-') {
-               if (delete)
-                       *delete = true;
+               c = UCI_CMD_REMOVE;
                *buf += 1;
        } else if (**buf == '@') {
-               if (rename)
-                       *rename = true;
+               c = UCI_CMD_RENAME;
+               *buf += 1;
+       } else if (**buf == '+') {
+               /* UCI_CMD_ADD is used for anonymous sections */
+               c = UCI_CMD_ADD;
                *buf += 1;
        }
+       if (cmd)
+               *cmd = c;
 
        UCI_INTERNAL(uci_parse_tuple, ctx, *buf, package, section, option, value);
+       if (!*section[0])
+               UCI_THROW(ctx, UCI_ERR_PARSE);
+
 }
+
 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;
+       int cmd;
 
-       uci_parse_history_tuple(ctx, &buf, &package, &section, &option, &value, &delete, &rename);
+       uci_parse_history_tuple(ctx, &buf, &package, &section, &option, &value, &cmd);
        if (!package || (strcmp(package, p->e.name) != 0))
                goto error;
        if (!uci_validate_name(section))
@@ -121,27 +133,23 @@ static void uci_parse_history_line(struct uci_context *ctx, struct uci_package *
        if (rename && !uci_validate_str(value, (option || delete)))
                goto error;
 
-       if (ctx->flags & UCI_FLAG_SAVED_HISTORY) {
-               int cmd;
-
-               /* NB: no distinction between CMD_CHANGE and CMD_ADD possible at this point */
-               if(delete)
-                       cmd = UCI_CMD_REMOVE;
-               else if (rename)
-                       cmd = UCI_CMD_RENAME;
-               else
-                       cmd = UCI_CMD_CHANGE;
-
+       if (ctx->flags & UCI_FLAG_SAVED_HISTORY)
                uci_add_history(ctx, &p->saved_history, cmd, section, option, value);
-       }
 
-       if (rename)
+       switch(cmd) {
+       case UCI_CMD_RENAME:
                UCI_INTERNAL(uci_rename, ctx, p, section, option, value);
-       else if (delete)
+               break;
+       case UCI_CMD_REMOVE:
                UCI_INTERNAL(uci_delete, ctx, p, section, option);
-       else
-               UCI_INTERNAL(uci_set, ctx, p, section, option, value, NULL);
-
+               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_to_section(e)->anonymous = true;
+               break;
+       }
        return;
 error:
        UCI_THROW(ctx, UCI_ERR_PARSE);
@@ -274,7 +282,7 @@ 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, NULL);
+               uci_parse_history_tuple(ctx, &buf, &p, &s, &o, &v, NULL);
                if (section) {
                        if (!s || (strcmp(section, s) != 0))
                                continue;
@@ -377,10 +385,19 @@ int uci_save(struct uci_context *ctx, struct uci_package *p)
        uci_foreach_element_safe(&p->history, tmp, e) {
                struct uci_history *h = uci_to_history(e);
 
-               if (h->cmd == UCI_CMD_REMOVE)
+               switch(h->cmd) {
+               case UCI_CMD_REMOVE:
                        fprintf(f, "-");
-               else if (h->cmd == UCI_CMD_RENAME)
+                       break;
+               case UCI_CMD_RENAME:
                        fprintf(f, "@");
+                       break;
+               case UCI_CMD_ADD:
+                       fprintf(f, "+");
+                       break;
+               default:
+                       break;
+               }
 
                fprintf(f, "%s.%s", p->e.name, h->section);
                if (e->name)
diff --git a/list.c b/list.c
index c6b7ebc..a28c5e1 100644 (file)
--- a/list.c
+++ b/list.c
@@ -115,6 +115,34 @@ uci_free_option(struct uci_option *o)
        uci_free_element(&o->e);
 }
 
+/* fix up an unnamed section, e.g. after adding options to it */
+static void uci_fixup_section(struct uci_context *ctx, struct uci_section *s)
+{
+       unsigned int hash = ~0;
+       struct uci_element *e;
+       char buf[16];
+
+       if (!s || s->e.name)
+               return;
+
+       /*
+        * Generate a name for unnamed sections. This is used as reference
+        * when locating or updating the section from apps/scripts.
+        * To make multiple concurrent versions somewhat safe for updating,
+        * the name is generated from a hash of its type and name/value
+        * pairs of its option, and it is prefixed by a counter value.
+        * If the order of the unnamed sections changes for some reason,
+        * updates to them will be rejected.
+        */
+       hash = djbhash(hash, s->type);
+       uci_foreach_element(&s->options, e) {
+               hash = djbhash(hash, e->name);
+               hash = djbhash(hash, uci_to_option(e)->value);
+       }
+       sprintf(buf, "cfg%02x%04x", ++s->package->n_section, hash % (1 << 16));
+       s->e.name = uci_strdup(ctx, buf);
+}
+
 static struct uci_section *
 uci_alloc_section(struct uci_package *p, const char *type, const char *name)
 {
@@ -131,6 +159,7 @@ uci_alloc_section(struct uci_package *p, const char *type, const char *name)
        strcpy(s->type, type);
        if (name == NULL)
                s->anonymous = true;
+       p->n_section++;
 
        uci_list_add(&p->sections, &s->e.list);
 
@@ -370,6 +399,19 @@ int uci_rename(struct uci_context *ctx, struct uci_package *p, char *section, ch
        return 0;
 }
 
+int uci_add_section(struct uci_context *ctx, struct uci_package *p, char *type, struct uci_section **res)
+{
+       struct uci_section *s;
+
+       UCI_HANDLE_ERR(ctx);
+       UCI_ASSERT(ctx, p != NULL);
+       s = uci_alloc_section(p, type, NULL);
+       uci_fixup_section(ctx, s);
+       *res = s;
+       uci_add_history(ctx, &p->history, UCI_CMD_ADD, s->e.name, NULL, type);
+
+       return 0;
+}
 
 int uci_delete(struct uci_context *ctx, struct uci_package *p, char *section, char *option)
 {
@@ -452,7 +494,7 @@ notfound:
 
        /* now add the missing entry */
        if (!internal && p->confdir)
-               uci_add_history(ctx, &p->history, UCI_CMD_ADD, section, option, value);
+               uci_add_history(ctx, &p->history, UCI_CMD_CHANGE, section, option, value);
        if (s) {
                o = uci_alloc_option(s, option, value);
                if (result)
diff --git a/uci.h b/uci.h
index 0a2a662..8f4fa71 100644 (file)
--- a/uci.h
+++ b/uci.h
@@ -156,6 +156,15 @@ extern int uci_cleanup(struct uci_context *ctx);
  */
 extern int uci_lookup(struct uci_context *ctx, struct uci_element **res, struct uci_package *package, char *section, char *option);
 
+/**
+ * uci_add_section: Add an unnamed section
+ * @ctx: uci context
+ * @p: package to add the section to
+ * @type: section type
+ * @res: pointer to store a reference to the new section in
+ */
+extern int uci_add_section(struct uci_context *ctx, struct uci_package *p, char *type, struct uci_section **res);
+
 /**
  * uci_set_element_value: Replace an element's value with a new one
  * @ctx: uci context
diff --git a/util.c b/util.c
index eb50b89..f098c4b 100644 (file)
--- a/util.c
+++ b/util.c
@@ -13,8 +13,8 @@
  */
 
 /*
- * This file contains wrappers to standard functions, which
- * throw exceptions upon failure.
+ * This file contains misc utility functions and wrappers to standard
+ * functions, which throw exceptions upon failure.
  */
 #include <sys/types.h>
 #include <sys/stat.h>
@@ -59,6 +59,22 @@ static char *uci_strdup(struct uci_context *ctx, const char *str)
        return ptr;
 }
 
+/* Based on an efficient hash function published by D. J. Bernstein */
+static unsigned int djbhash(unsigned int hash, char *str)
+{
+       int len = strlen(str);
+       int i;
+
+       /* initial value */
+       if (hash == ~0)
+               hash = 5381;
+
+       for(i = 0; i < len; i++) {
+               hash = ((hash << 5) + hash) + str[i];
+       }
+       return (hash & 0x7FFFFFFF);
+}
+
 /*
  * validate strings for names and types, reject special characters
  * for names, only alphanum and _ is allowed (shell compatibility)
@@ -125,7 +141,7 @@ lastval:
                *value = last;
        }
 
-       if (*section && !uci_validate_name(*section))
+       if (*section && *section[0] && !uci_validate_name(*section))
                goto error;
        if (*option && !uci_validate_name(*option))
                goto error;