}
-/*
- * parse a character escaped by '\'
- * returns true if the escaped character is to be parsed
- * returns false if the escaped character is to be ignored
- */
-static inline bool parse_backslash(struct uci_context *ctx, char **str)
-{
- /* skip backslash */
- *str += 1;
-
- /* undecoded backslash at the end of line, fetch the next line */
- if (!**str) {
- *str += 1;
- uci_getln(ctx, *str - ctx->pctx->buf);
- return false;
- }
-
- /* FIXME: decode escaped char, necessary? */
- return true;
-}
-
-/*
- * move the string pointer forward until a non-whitespace character or
- * EOL is reached
- */
-static void skip_whitespace(struct uci_context *ctx, char **str)
-{
-restart:
- while (**str && isspace(**str))
- *str += 1;
-
- if (**str == '\\') {
- if (!parse_backslash(ctx, str))
- goto restart;
- }
-}
-
-static inline void addc(char **dest, char **src)
-{
- **dest = **src;
- *dest += 1;
- *src += 1;
-}
-
-/*
- * parse a double quoted string argument from the command line
- */
-static void parse_double_quote(struct uci_context *ctx, char **str, char **target)
-{
- char c;
-
- /* skip quote character */
- *str += 1;
-
- while ((c = **str)) {
- switch(c) {
- case '"':
- **target = 0;
- *str += 1;
- return;
- case '\\':
- if (!parse_backslash(ctx, str))
- continue;
- /* fall through */
- default:
- addc(target, str);
- break;
- }
- }
- uci_parse_error(ctx, *str, "unterminated \"");
-}
-
-/*
- * parse a single quoted string argument from the command line
- */
-static void parse_single_quote(struct uci_context *ctx, char **str, char **target)
-{
- char c;
- /* skip quote character */
- *str += 1;
-
- while ((c = **str)) {
- switch(c) {
- case '\'':
- **target = 0;
- *str += 1;
- return;
- default:
- addc(target, str);
- }
- }
- uci_parse_error(ctx, *str, "unterminated '");
-}
-
-/*
- * parse a string from the command line and detect the quoting style
- */
-static void parse_str(struct uci_context *ctx, char **str, char **target)
-{
- do {
- switch(**str) {
- case '\'':
- parse_single_quote(ctx, str, target);
- break;
- case '"':
- parse_double_quote(ctx, str, target);
- break;
- case '#':
- **str = 0;
- /* fall through */
- case 0:
- goto done;
- case '\\':
- if (!parse_backslash(ctx, str))
- continue;
- /* fall through */
- default:
- addc(target, str);
- break;
- }
- } while (**str && !isspace(**str));
-done:
-
- /*
- * if the string was unquoted and we've stopped at a whitespace
- * character, skip to the next one, because the whitespace will
- * be overwritten by a null byte here
- */
- if (**str)
- *str += 1;
-
- /* terminate the parsed string */
- **target = 0;
-}
-
-/*
- * extract the next argument from the command line
- */
-static char *next_arg(struct uci_context *ctx, char **str, bool required, bool name)
-{
- char *val;
- char *ptr;
-
- val = ptr = *str;
- skip_whitespace(ctx, str);
- parse_str(ctx, str, &ptr);
- if (!*val) {
- if (required)
- uci_parse_error(ctx, *str, "insufficient arguments");
- goto done;
- }
-
- if (name && !uci_validate_name(val))
- uci_parse_error(ctx, val, "invalid character in field");
-
-done:
- return val;
-}
-
/*
* verify that the end of the line or command is reached.
* throw an error if extra arguments are given on the command line
char *tmp;
tmp = next_arg(ctx, str, false, false);
- if (tmp && *tmp && (ctx->flags & UCI_FLAG_STRICT))
+ if (*tmp && (ctx->flags & UCI_FLAG_STRICT))
uci_parse_error(ctx, *str, "too many arguments");
}
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)
*/
/* command string null-terminated by strtok */
*str += strlen(*str) + 1;
- type = next_arg(ctx, str, true, true);
+ type = next_arg(ctx, str, true, false);
+ if (!uci_validate_str(type, false))
+ uci_parse_error(ctx, type, "invalid character in field");
name = next_arg(ctx, str, false, true);
assert_eol(ctx, str);
if (pctx->merge) {
UCI_TRAP_SAVE(ctx, error);
- uci_set(ctx, pctx->package, name, NULL, type);
+ uci_set(ctx, pctx->package, name, NULL, type, NULL);
UCI_TRAP_RESTORE(ctx);
return;
error:
*str += strlen(*str) + 1;
name = next_arg(ctx, str, true, true);
- value = next_arg(ctx, str, true, false);
+ value = next_arg(ctx, str, false, false);
assert_eol(ctx, str);
if (pctx->merge) {
UCI_TRAP_SAVE(ctx, error);
- uci_set(ctx, pctx->package, pctx->section->e.name, name, value);
+ uci_set(ctx, pctx->package, pctx->section->e.name, name, value, NULL);
UCI_TRAP_RESTORE(ctx);
return;
error:
/* make sure no memory from previous parse attempts is leaked */
uci_file_cleanup(ctx);
- pctx = (struct uci_parse_context *) uci_malloc(ctx, sizeof(struct uci_parse_context));
- ctx->pctx = pctx;
+ uci_alloc_parse_context(ctx);
+ pctx = ctx->pctx;
pctx->file = stream;
if (*package && single) {
pctx->package = *package;
/* freed together with the uci_package */
path = NULL;
- /* check for updated history, just in case */
- uci_load_history(ctx, p, true);
+ /* check for updated history, flush */
+ if (!uci_load_history(ctx, p, true))
+ goto done;
} else {
/* flush history */
- uci_load_history(ctx, NULL, true);
+ if (!uci_load_history(ctx, NULL, true))
+ goto done;
}
}