file: defer checking the tmpfile until it is actually needed.
[project/uci.git] / file.c
diff --git a/file.c b/file.c
index ffa3afa..8e6052c 100644 (file)
--- a/file.c
+++ b/file.c
@@ -33,7 +33,6 @@
 #include "uci_internal.h"
 
 #define LINEBUF        32
-#define LINEBUF_MAX    4096
 
 /*
  * Fetch a new line from the input stream and resize buffer if necessary
@@ -48,8 +47,8 @@ __private void uci_getln(struct uci_context *ctx, int offset)
                pctx->buf = uci_malloc(ctx, LINEBUF);
                pctx->bufsz = LINEBUF;
        }
-       /* `offset' may off by one */
-       if (offset >= pctx->bufsz) {
+       /* It takes 2 slots for fgets to read 1 char. */
+       if (offset >= pctx->bufsz - 1) {
                pctx->bufsz *= 2;
                pctx->buf = uci_realloc(ctx, pctx->buf, pctx->bufsz);
        }
@@ -69,11 +68,10 @@ __private void uci_getln(struct uci_context *ctx, int offset)
                        return;
                }
 
-               if (pctx->bufsz > LINEBUF_MAX/2)
-                       uci_parse_error(ctx, "line too long");
-
                pctx->bufsz *= 2;
                pctx->buf = uci_realloc(ctx, pctx->buf, pctx->bufsz);
+               if (!pctx->buf)
+                       UCI_THROW(ctx, UCI_ERR_MEM);
        } while (1);
 }
 
@@ -157,7 +155,6 @@ static void parse_double_quote(struct uci_context *ctx, int *target)
                        break;
                }
        }
-       uci_parse_error(ctx, "unterminated \"");
 }
 
 /*
@@ -180,14 +177,13 @@ static void parse_single_quote(struct uci_context *ctx, int *target)
                        /* Multi-line str value */
                        uci_getln(ctx, pctx->pos);
                        if (!pctx_cur_char(pctx))
-                               uci_parse_error(ctx, "EOF with unterminated \"");
+                               uci_parse_error(ctx, "EOF with unterminated '");
 
                        break;
                default:
                        addc(ctx, target, &pctx->pos);
                }
        }
-       uci_parse_error(ctx, "unterminated '");
 }
 
 /*
@@ -239,14 +235,14 @@ done:
 /*
  * extract the next argument from the command line
  */
-static char *next_arg(struct uci_context *ctx, bool required, bool name)
+static int next_arg(struct uci_context *ctx, bool required, bool name, bool package)
 {
        struct uci_parse_context *pctx = ctx->pctx;
        int val, ptr;
 
        skip_whitespace(ctx);
        val = ptr = pctx_pos(pctx);
-       if(pctx_cur_char(pctx) == ';') {
+       if (pctx_cur_char(pctx) == ';') {
                pctx_cur_char(pctx) = 0;
                pctx->pos += 1;
        } else {
@@ -258,15 +254,17 @@ static char *next_arg(struct uci_context *ctx, bool required, bool name)
                goto done;
        }
 
-       if (name && !uci_validate_name(pctx_str(pctx, val)))
+       if (name && !uci_validate_str(pctx_str(pctx, val), name, package))
                uci_parse_error(ctx, "invalid character in name field");
 
 done:
-       return pctx_str(pctx, val);
+       return val;
 }
 
 int uci_parse_argument(struct uci_context *ctx, FILE *stream, char **str, char **result)
 {
+       int ofs_result;
+
        UCI_HANDLE_ERR(ctx);
        UCI_ASSERT(ctx, str != NULL);
        UCI_ASSERT(ctx, result != NULL);
@@ -278,15 +276,14 @@ int uci_parse_argument(struct uci_context *ctx, FILE *stream, char **str, char *
                uci_alloc_parse_context(ctx);
 
        ctx->pctx->file = stream;
-
        if (!*str) {
+               ctx->pctx->pos = 0;
                uci_getln(ctx, 0);
-               *str = ctx->pctx->buf;
-       } else {
-               UCI_ASSERT(ctx, ctx->pctx->pos == *str - ctx->pctx->buf);
        }
 
-       *result = next_arg(ctx, false, false);
+       ofs_result = next_arg(ctx, false, false, false);
+       *result = pctx_str(ctx->pctx, ofs_result);
+       *str = pctx_cur_str(ctx->pctx);
 
        return 0;
 }
@@ -335,9 +332,11 @@ fill_package:
 static void assert_eol(struct uci_context *ctx)
 {
        char *tmp;
+       int ofs_tmp;
 
        skip_whitespace(ctx);
-       tmp = next_arg(ctx, false, false);
+       ofs_tmp = next_arg(ctx, false, false, false);
+       tmp = pctx_str(ctx->pctx, ofs_tmp);
        if (*tmp && (ctx->flags & UCI_FLAG_STRICT))
                uci_parse_error(ctx, "too many arguments");
 }
@@ -383,12 +382,14 @@ static void uci_switch_config(struct uci_context *ctx)
 static void uci_parse_package(struct uci_context *ctx, bool single)
 {
        struct uci_parse_context *pctx = ctx->pctx;
-       char *name = NULL;
+       int ofs_name;
+       char *name;
 
        /* command string null-terminated by strtok */
        pctx->pos += strlen(pctx_cur_str(pctx)) + 1;
 
-       name = next_arg(ctx, true, true);
+       ofs_name = next_arg(ctx, true, true, true);
+       name = pctx_str(pctx, ofs_name);
        assert_eol(ctx);
        if (single)
                return;
@@ -405,8 +406,9 @@ static void uci_parse_config(struct uci_context *ctx)
        struct uci_parse_context *pctx = ctx->pctx;
        struct uci_element *e;
        struct uci_ptr ptr;
-       char *name = NULL;
-       char *type = NULL;
+       int ofs_name, ofs_type;
+       char *name;
+       char *type;
 
        uci_fixup_section(ctx, ctx->pctx->section);
        if (!ctx->pctx->package) {
@@ -419,10 +421,14 @@ static void uci_parse_config(struct uci_context *ctx)
        /* command string null-terminated by strtok */
        pctx->pos += strlen(pctx_cur_str(pctx)) + 1;
 
-       type = next_arg(ctx, true, false);
+       ofs_type = next_arg(ctx, true, false, false);
+       type = pctx_str(pctx, ofs_type);
        if (!uci_validate_type(type))
                uci_parse_error(ctx, "invalid character in type field");
-       name = next_arg(ctx, false, true);
+
+       ofs_name = next_arg(ctx, false, true, false);
+       type = pctx_str(pctx, ofs_type);
+       name = pctx_str(pctx, ofs_name);
        assert_eol(ctx);
 
        if (!name || !name[0]) {
@@ -450,6 +456,7 @@ static void uci_parse_option(struct uci_context *ctx, bool list)
        struct uci_parse_context *pctx = ctx->pctx;
        struct uci_element *e;
        struct uci_ptr ptr;
+       int ofs_name, ofs_value;
        char *name = NULL;
        char *value = NULL;
 
@@ -459,8 +466,10 @@ static void uci_parse_option(struct uci_context *ctx, bool list)
        /* command string null-terminated by strtok */
        pctx->pos += strlen(pctx_cur_str(pctx)) + 1;
 
-       name = next_arg(ctx, true, true);
-       value = next_arg(ctx, false, false);
+       ofs_name = next_arg(ctx, true, true, false);
+       ofs_value = next_arg(ctx, false, false, false);
+       name = pctx_str(pctx, ofs_name);
+       value = pctx_str(pctx, ofs_value);
        assert_eol(ctx);
 
        uci_fill_ptr(ctx, &ptr, &pctx->section->e);
@@ -726,17 +735,6 @@ static void uci_file_commit(struct uci_context *ctx, struct uci_package **packag
        if ((asprintf(&filename, "%s/.%s.uci-XXXXXX", ctx->confdir, p->e.name) < 0) || !filename)
                UCI_THROW(ctx, UCI_ERR_MEM);
 
-       if (!mktemp(filename))
-               *filename = 0;
-
-       if (!*filename) {
-               free(filename);
-               UCI_THROW(ctx, UCI_ERR_IO);
-       }
-
-       if ((stat(filename, &statbuf) == 0) && ((statbuf.st_mode & S_IFMT) != S_IFREG))
-               UCI_THROW(ctx, UCI_ERR_IO);
-
        /* open the config file for writing now, so that it is locked */
        f1 = uci_open_stream(ctx, p->path, NULL, SEEK_SET, true, true);
 
@@ -771,6 +769,17 @@ static void uci_file_commit(struct uci_context *ctx, struct uci_package **packag
                        goto done;
        }
 
+       if (!mktemp(filename))
+               *filename = 0;
+
+       if (!*filename) {
+               free(filename);
+               UCI_THROW(ctx, UCI_ERR_IO);
+       }
+
+       if ((stat(filename, &statbuf) == 0) && ((statbuf.st_mode & S_IFMT) != S_IFREG))
+               UCI_THROW(ctx, UCI_ERR_IO);
+
        f2 = uci_open_stream(ctx, filename, p->path, SEEK_SET, true, true);
        uci_export(ctx, f2, p, false);
 
@@ -791,7 +800,6 @@ done:
                UCI_THROW(ctx, UCI_ERR_IO);
        }
        free(filename);
-       sync();
        if (ctx->err)
                UCI_THROW(ctx, ctx->err);
 }