more cleanup
[project/uci.git] / file.c
diff --git a/file.c b/file.c
index 883e36006e39b84390906943af13ac69365e49f0..04ef91e5e2193e5cda74536053ec12ba0575de84 100644 (file)
--- a/file.c
+++ b/file.c
@@ -24,6 +24,7 @@
 #include <fcntl.h>
 #include <stdio.h>
 #include <ctype.h>
+#include <glob.h>
 
 static struct uci_backend uci_file_backend;
 
@@ -35,6 +36,7 @@ static void assert_eol(struct uci_context *ctx, char **str)
 {
        char *tmp;
 
+       skip_whitespace(ctx, str);
        tmp = next_arg(ctx, str, false, false);
        if (*tmp && (ctx->flags & UCI_FLAG_STRICT))
                uci_parse_error(ctx, *str, "too many arguments");
@@ -69,7 +71,7 @@ static void uci_switch_config(struct uci_context *ctx)
         * if an older config under the same name exists, unload it
         * ignore errors here, e.g. if the config was not found
         */
-       e = uci_lookup_list(ctx, &ctx->root, name);
+       e = uci_lookup_list(&ctx->root, name);
        if (e)
                UCI_THROW(ctx, UCI_ERR_DUPLICATE);
        pctx->package = uci_alloc_package(ctx, name);
@@ -121,12 +123,7 @@ static void uci_parse_config(struct uci_context *ctx, char **str)
        assert_eol(ctx, str);
 
        if (pctx->merge) {
-               UCI_TRAP_SAVE(ctx, error);
-               uci_set(ctx, pctx->package, name, NULL, type, NULL);
-               UCI_TRAP_RESTORE(ctx);
-               return;
-error:
-               UCI_THROW(ctx, ctx->errno);
+               UCI_NESTED(uci_set, ctx, pctx->package, name, NULL, type, NULL);
        } else
                pctx->section = uci_alloc_section(pctx->package, type, name);
 }
@@ -151,16 +148,38 @@ static void uci_parse_option(struct uci_context *ctx, char **str)
        assert_eol(ctx, str);
 
        if (pctx->merge) {
-               UCI_TRAP_SAVE(ctx, error);
-               uci_set(ctx, pctx->package, pctx->section->e.name, name, value, NULL);
-               UCI_TRAP_RESTORE(ctx);
-               return;
-error:
-               UCI_THROW(ctx, ctx->errno);
+               UCI_NESTED(uci_set, ctx, pctx->package, pctx->section->e.name, name, value, NULL);
        } else
                uci_alloc_option(pctx->section, name, value);
 }
 
+static void uci_parse_list(struct uci_context *ctx, char **str)
+{
+       struct uci_parse_context *pctx = ctx->pctx;
+       struct uci_ptr ptr;
+       char *name = NULL;
+       char *value = NULL;
+
+       if (!pctx->section)
+               uci_parse_error(ctx, *str, "list command found before the first section");
+
+       /* command string null-terminated by strtok */
+       *str += strlen(*str) + 1;
+
+       name = next_arg(ctx, str, true, true);
+       value = next_arg(ctx, str, false, false);
+       assert_eol(ctx, str);
+
+       UCI_NESTED(uci_fill_ptr, ctx, &ptr, &pctx->section->e, false);
+       ptr.option = name;
+       ptr.value = value;
+
+       UCI_INTERNAL(uci_lookup_ptr, ctx, &ptr, NULL, false);
+
+       ctx->internal = !pctx->merge;
+       UCI_NESTED(uci_add_list, ctx, &ptr);
+}
+
 
 /*
  * parse a complete input line, split up combined commands by ';'
@@ -168,38 +187,50 @@ error:
 static void uci_parse_line(struct uci_context *ctx, bool single)
 {
        struct uci_parse_context *pctx = ctx->pctx;
-       char *word, *brk = NULL;
-
-       for (word = strtok_r(pctx->buf, ";", &brk);
-                word;
-                word = strtok_r(NULL, ";", &brk)) {
-
-               char *pbrk = NULL;
-               word = strtok_r(word, " \t", &pbrk);
+       char *word, *brk;
 
+       word = pctx->buf;
+       do {
+               brk = NULL;
+               word = strtok_r(word, " \t", &brk);
                if (!word)
-                       continue;
+                       return;
 
                switch(word[0]) {
+                       case 0:
                        case '#':
                                return;
                        case 'p':
                                if ((word[1] == 0) || !strcmp(word + 1, "ackage"))
                                        uci_parse_package(ctx, &word, single);
+                               else
+                                       goto invalid;
                                break;
                        case 'c':
                                if ((word[1] == 0) || !strcmp(word + 1, "onfig"))
                                        uci_parse_config(ctx, &word);
+                               else
+                                       goto invalid;
                                break;
                        case 'o':
                                if ((word[1] == 0) || !strcmp(word + 1, "ption"))
                                        uci_parse_option(ctx, &word);
+                               else
+                                       goto invalid;
                                break;
-                       default:
-                               uci_parse_error(ctx, word, "unterminated command");
+                       case 'l':
+                               if ((word[1] == 0) || !strcmp(word + 1, "ist"))
+                                       uci_parse_list(ctx, &word);
+                               else
+                                       goto invalid;
                                break;
+                       default:
+                               goto invalid;
                }
-       }
+               continue;
+invalid:
+               uci_parse_error(ctx, word, "invalid command");
+       } while (1);
 }
 
 /* max number of characters that escaping adds to the string */
@@ -208,49 +239,54 @@ static void uci_parse_line(struct uci_context *ctx, bool single)
 /*
  * escape an uci string for export
  */
-static char *uci_escape(struct uci_context *ctx, char *str)
+static char *uci_escape(struct uci_context *ctx, const char *str)
 {
-       char *s, *p;
-       int pos = 0;
+       const char *end;
+       int ofs = 0;
 
        if (!ctx->buf) {
                ctx->bufsz = LINEBUF;
                ctx->buf = malloc(LINEBUF);
        }
 
-       s = str;
-       p = strchr(str, '\'');
-       if (!p)
-               return str;
+       while (1) {
+               int len;
 
-       do {
-               int len = p - s;
-               if (len > 0) {
-                       if (p + sizeof(UCI_QUOTE_ESCAPE) - str >= ctx->bufsz) {
-                               ctx->bufsz *= 2;
-                               ctx->buf = realloc(ctx->buf, ctx->bufsz);
-                               if (!ctx->buf)
-                                       UCI_THROW(ctx, UCI_ERR_MEM);
-                       }
-                       memcpy(&ctx->buf[pos], s, len);
-                       pos += len;
+               end = strchr(str, '\'');
+               if (!end)
+                       end = str + strlen(str);
+               len = end - str;
+
+               /* make sure that we have enough room in the buffer */
+               while (ofs + len + sizeof(UCI_QUOTE_ESCAPE) + 1 > ctx->bufsz) {
+                       ctx->bufsz *= 2;
+                       ctx->buf = uci_realloc(ctx, ctx->buf, ctx->bufsz);
                }
-               strcpy(&ctx->buf[pos], UCI_QUOTE_ESCAPE);
-               pos += sizeof(UCI_QUOTE_ESCAPE);
-               s = p + 1;
-       } while ((p = strchr(s, '\'')));
 
+               /* copy the string until the character before the quote */
+               memcpy(&ctx->buf[ofs], str, len);
+               ofs += len;
+
+               /* end of string? return the buffer */
+               if (*end == 0)
+                       break;
+
+               memcpy(&ctx->buf[ofs], UCI_QUOTE_ESCAPE, sizeof(UCI_QUOTE_ESCAPE));
+               ofs += strlen(&ctx->buf[ofs]);
+               str = end + 1;
+       }
+
+       ctx->buf[ofs] = 0;
        return ctx->buf;
 }
 
-
 /*
  * export a single config package to a file stream
  */
 static void uci_export_package(struct uci_package *p, FILE *stream, bool header)
 {
        struct uci_context *ctx = p->ctx;
-       struct uci_element *s, *o;
+       struct uci_element *s, *o, *i;
 
        if (header)
                fprintf(stream, "package '%s'\n", uci_escape(ctx, p->e.name));
@@ -262,8 +298,21 @@ static void uci_export_package(struct uci_package *p, FILE *stream, bool header)
                fprintf(stream, "\n");
                uci_foreach_element(&sec->options, o) {
                        struct uci_option *opt = uci_to_option(o);
-                       fprintf(stream, "\toption '%s'", uci_escape(ctx, opt->e.name));
-                       fprintf(stream, " '%s'\n", uci_escape(ctx, opt->value));
+                       switch(opt->type) {
+                       case UCI_TYPE_STRING:
+                               fprintf(stream, "\toption '%s'", uci_escape(ctx, opt->e.name));
+                               fprintf(stream, " '%s'\n", uci_escape(ctx, opt->v.string));
+                               break;
+                       case UCI_TYPE_LIST:
+                               uci_foreach_element(&opt->v.list, i) {
+                                       fprintf(stream, "\tlist '%s'", uci_escape(ctx, opt->e.name));
+                                       fprintf(stream, " '%s'\n", uci_escape(ctx, i->name));
+                               }
+                               break;
+                       default:
+                               fprintf(stream, "\t# unknown type for option '%s'\n", uci_escape(ctx, opt->e.name));
+                               break;
+                       }
                }
        }
        fprintf(stream, "\n");
@@ -309,7 +358,7 @@ int uci_import(struct uci_context *ctx, FILE *stream, const char *name, struct u
         * NB: the config file can still override the package name
         */
        if (name) {
-               UCI_ASSERT(ctx, uci_validate_name(name));
+               UCI_ASSERT(ctx, uci_validate_str(name, false));
                pctx->name = name;
        }
 
@@ -323,12 +372,14 @@ int uci_import(struct uci_context *ctx, FILE *stream, const char *name, struct u
 error:
                if (ctx->flags & UCI_FLAG_PERROR)
                        uci_perror(ctx, NULL);
-               if ((ctx->errno != UCI_ERR_PARSE) ||
+               if ((ctx->err != UCI_ERR_PARSE) ||
                        (ctx->flags & UCI_FLAG_STRICT))
-                       UCI_THROW(ctx, ctx->errno);
+                       UCI_THROW(ctx, ctx->err);
        }
 
        uci_fixup_section(ctx, ctx->pctx->section);
+       if (!pctx->package && name)
+               uci_switch_config(ctx);
        if (package)
                *package = pctx->package;
        if (pctx->merge)
@@ -348,7 +399,7 @@ static char *uci_config_path(struct uci_context *ctx, const char *name)
 {
        char *filename;
 
-       UCI_ASSERT(ctx, uci_validate_name(name));
+       UCI_ASSERT(ctx, uci_validate_str(name, false));
        filename = uci_malloc(ctx, strlen(name) + strlen(ctx->confdir) + 2);
        sprintf(filename, "%s/%s", ctx->confdir, name);
 
@@ -419,8 +470,8 @@ done:
        if (path)
                free(path);
        uci_close_stream(f);
-       if (ctx->errno)
-               UCI_THROW(ctx, ctx->errno);
+       if (ctx->err)
+               UCI_THROW(ctx, ctx->err);
 }
 
 
@@ -474,6 +525,9 @@ static char **uci_list_config_files(struct uci_context *ctx)
                if (!p)
                        continue;
 
+               if (!uci_validate_name(p))
+                       continue;
+
                configs[i] = buf;
                strcpy(buf, p);
                buf += strlen(buf) + 1;
@@ -509,7 +563,7 @@ static struct uci_package *uci_file_load(struct uci_context *ctx, const char *na
        }
 
        file = uci_open_stream(ctx, filename, SEEK_SET, false, false);
-       ctx->errno = 0;
+       ctx->err = 0;
        UCI_TRAP_SAVE(ctx, done);
        UCI_INTERNAL(uci_import, ctx, file, name, &package, true);
        UCI_TRAP_RESTORE(ctx);
@@ -522,8 +576,8 @@ static struct uci_package *uci_file_load(struct uci_context *ctx, const char *na
 
 done:
        uci_close_stream(file);
-       if (ctx->errno)
-               UCI_THROW(ctx, ctx->errno);
+       if (ctx->err)
+               UCI_THROW(ctx, ctx->err);
        return package;
 }