use -fPIC
[project/uci.git] / parse.c
diff --git a/parse.c b/parse.c
index 4c51715..1297b80 100644 (file)
--- a/parse.c
+++ b/parse.c
@@ -56,6 +56,7 @@ static void uci_getln(struct uci_context *ctx)
                }
 
                if (pctx->bufsz > LINEBUF_MAX/2) {
+                       pctx->reason = "line too long";
                        pctx->byte = LINEBUF_MAX;
                        UCI_THROW(ctx, UCI_ERR_PARSE);
                }
@@ -76,9 +77,10 @@ static void uci_parse_cleanup(struct uci_context *ctx)
        if (!pctx)
                return;
 
+       ctx->pctx = NULL;
        if (pctx->cfg) {
                uci_list_del(&pctx->cfg->list);
-               uci_drop_file(pctx->cfg);
+               uci_drop_config(pctx->cfg);
        }
        if (pctx->buf)
                free(pctx->buf);
@@ -137,6 +139,7 @@ static void parse_double_quote(struct uci_context *ctx, char **str, char **targe
                        break;
                }
        }
+       ctx->pctx->reason = "unterminated \"";
        ctx->pctx->byte = *str - ctx->pctx->buf;
        UCI_THROW(ctx, UCI_ERR_PARSE);
 }
@@ -160,6 +163,7 @@ static void parse_single_quote(struct uci_context *ctx, char **str, char **targe
                        addc(target, str);
                }
        }
+       ctx->pctx->reason = "unterminated '";
        ctx->pctx->byte = *str - ctx->pctx->buf;
        UCI_THROW(ctx, UCI_ERR_PARSE);
 }
@@ -213,6 +217,7 @@ static char *next_arg(struct uci_context *ctx, char **str, bool required)
        skip_whitespace(str);
        parse_str(ctx, str, &ptr);
        if (required && !*val) {
+               ctx->pctx->reason = "insufficient arguments";
                ctx->pctx->byte = *str - ctx->pctx->buf;
                UCI_THROW(ctx, UCI_ERR_PARSE);
        }
@@ -230,6 +235,7 @@ static void assert_eol(struct uci_context *ctx, char **str)
 
        tmp = next_arg(ctx, str, false);
        if (tmp && *tmp) {
+               ctx->pctx->reason = "too many arguments";
                ctx->pctx->byte = tmp - ctx->pctx->buf;
                UCI_THROW(ctx, UCI_ERR_PARSE);
        }
@@ -240,18 +246,26 @@ static void assert_eol(struct uci_context *ctx, char **str)
  */
 static void uci_parse_config(struct uci_context *ctx, char **str)
 {
-       char *type, *name;
+       char *name = NULL;
+       char *type = NULL;
 
+       /* command string null-terminated by strtok */
        *str += strlen(*str) + 1;
 
-       if (!*str) {
-               ctx->pctx->byte = *str - ctx->pctx->buf;
-               UCI_THROW(ctx, UCI_ERR_PARSE);
-       }
-
+       UCI_TRAP_SAVE(ctx, error);
        type = next_arg(ctx, str, true);
        name = next_arg(ctx, str, false);
        assert_eol(ctx, str);
+       ctx->pctx->section = uci_add_section(ctx->pctx->cfg, type, name);
+       UCI_TRAP_RESTORE(ctx);
+       return;
+
+error:
+       if (name)
+               free(name);
+       if (type)
+               free(type);
+       UCI_THROW(ctx, ctx->errno);
 }
 
 /*
@@ -259,15 +273,31 @@ static void uci_parse_config(struct uci_context *ctx, char **str)
  */
 static void uci_parse_option(struct uci_context *ctx, char **str)
 {
-       char *name, *value;
+       char *name = NULL;
+       char *value = NULL;
 
+       if (!ctx->pctx->section) {
+               ctx->pctx->byte = *str - ctx->pctx->buf;
+               ctx->pctx->reason = "option command found before the first section";
+               UCI_THROW(ctx, UCI_ERR_PARSE);
+       }
+       /* command string null-terminated by strtok */
        *str += strlen(*str) + 1;
 
+       UCI_TRAP_SAVE(ctx, error);
        name = next_arg(ctx, str, true);
        value = next_arg(ctx, str, true);
        assert_eol(ctx, str);
-
-       DPRINTF("\tOption: %s=\"%s\"\n", name, value);
+       uci_add_option(ctx->pctx->section, name, value);
+       UCI_TRAP_RESTORE(ctx);
+       return;
+
+error:
+       if (name)
+               free(name);
+       if (value)
+               free(value);
+       UCI_THROW(ctx, ctx->errno);
 }
 
 /*
@@ -295,6 +325,7 @@ static void uci_parse_line(struct uci_context *ctx)
                                        uci_parse_option(ctx, &word);
                                break;
                        default:
+                               pctx->reason = "unterminated command";
                                pctx->byte = word - pctx->buf;
                                UCI_THROW(ctx, UCI_ERR_PARSE);
                                break;
@@ -302,7 +333,7 @@ static void uci_parse_line(struct uci_context *ctx)
        }
 }
 
-int uci_load(struct uci_context *ctx, const char *name)
+int uci_load(struct uci_context *ctx, const char *name, struct uci_config **cfg)
 {
        struct uci_parse_context *pctx;
        struct stat statbuf;
@@ -312,6 +343,13 @@ int uci_load(struct uci_context *ctx, const char *name)
        UCI_HANDLE_ERR(ctx);
        UCI_ASSERT(ctx, name != NULL);
 
+       UCI_TRAP_SAVE(ctx, ignore);
+       uci_unload(ctx, name);
+       UCI_TRAP_RESTORE(ctx);
+
+ignore:
+       ctx->errno = 0;
+
        /* make sure no memory from previous parse attempts is leaked */
        uci_parse_cleanup(ctx);
 
@@ -333,8 +371,9 @@ int uci_load(struct uci_context *ctx, const char *name)
        }
 
        if ((stat(filename, &statbuf) < 0) ||
-               ((statbuf.st_mode &  S_IFMT) != S_IFREG))
+               ((statbuf.st_mode &  S_IFMT) != S_IFREG)) {
                UCI_THROW(ctx, UCI_ERR_NOTFOUND);
+       }
 
        pctx->file = fopen(filename, "r");
        if (filename != name)
@@ -343,7 +382,7 @@ int uci_load(struct uci_context *ctx, const char *name)
        if (!pctx->file)
                UCI_THROW(ctx, UCI_ERR_IO);
 
-       pctx->cfg = uci_alloc_file(ctx, name);
+       pctx->cfg = uci_alloc_config(ctx, name);
 
        while (!feof(pctx->file)) {
                uci_getln(ctx);
@@ -353,6 +392,9 @@ int uci_load(struct uci_context *ctx, const char *name)
 
        /* add to main config file list */
        uci_list_add(&ctx->root, &pctx->cfg->list);
+       if (cfg)
+               *cfg = pctx->cfg;
+
        pctx->cfg = NULL;
 
        /* no error happened, we can get rid of the parser context now */