CMD_EXPORT,
CMD_COMMIT,
/* other cmds */
+ CMD_ADD,
CMD_IMPORT,
CMD_HELP,
};
"\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"
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)
{
}
if (uci_parse_tuple(ctx, argv[1], &package, §ion, &option, ptr) != UCI_OK)
return 1;
+ if (section && !section[0])
+ return 1;
if (uci_load(ctx, package, &p) != UCI_OK) {
cli_perror();
cmd = CMD_IMPORT;
else if (!strcasecmp(argv[0], "help"))
cmd = CMD_HELP;
+ else if (!strcasecmp(argv[0], "add"))
+ cmd = CMD_ADD;
else
cmd = -1;
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;
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)
*/
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, §ion, &option, &value, &delete, &rename);
+ 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))
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);
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;
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)
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)
{
strcpy(s->type, type);
if (name == NULL)
s->anonymous = true;
+ p->n_section++;
uci_list_add(&p->sections, &s->e.list);
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)
{
/* 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)
*/
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
*/
/*
- * 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>
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)
*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;