#define _GNU_SOURCE
#include <sys/types.h>
-#include <sys/stat.h>
#include <sys/file.h>
#include <stdbool.h>
#include <unistd.h>
#include <stdio.h>
#include <ctype.h>
-#define LINEBUF 32
-#define LINEBUF_MAX 4096
-
-static void uci_parse_error(struct uci_context *ctx, char *pos, char *reason)
-{
- struct uci_parse_context *pctx = ctx->pctx;
-
- pctx->reason = reason;
- pctx->byte = pos - pctx->buf;
- UCI_THROW(ctx, UCI_ERR_PARSE);
-}
-
-/*
- * Fetch a new line from the input stream and resize buffer if necessary
- */
-static void uci_getln(struct uci_context *ctx, int offset)
-{
- struct uci_parse_context *pctx = ctx->pctx;
- char *p;
- int ofs;
-
- if (pctx->buf == NULL) {
- pctx->buf = uci_malloc(ctx, LINEBUF);
- pctx->bufsz = LINEBUF;
- }
-
- ofs = offset;
- do {
- p = &pctx->buf[ofs];
- p[ofs] = 0;
-
- p = fgets(p, pctx->bufsz - ofs, pctx->file);
- if (!p || !*p)
- return;
-
- ofs += strlen(p);
- if (pctx->buf[ofs - 1] == '\n') {
- pctx->line++;
- pctx->buf[ofs - 1] = 0;
- return;
- }
-
- if (pctx->bufsz > LINEBUF_MAX/2)
- uci_parse_error(ctx, p, "line too long");
-
- pctx->bufsz *= 2;
- pctx->buf = uci_realloc(ctx, pctx->buf, pctx->bufsz);
- } while (1);
-}
-
/*
* Clean up all extra memory used by the parser and exporter
*/
free(pctx);
}
+
/*
* parse a character escaped by '\'
* returns true if the escaped character is to be parsed
return 0;
}
-/*
- * open a stream and go to the right position
- *
- * note: when opening for write and seeking to the beginning of
- * the stream, truncate the file
- */
-static FILE *uci_open_stream(struct uci_context *ctx, const char *filename, int pos, bool write, bool create)
-{
- struct stat statbuf;
- FILE *file = NULL;
- int fd, ret;
- int mode = (write ? O_RDWR : O_RDONLY);
-
- if (create)
- mode |= O_CREAT;
-
- if (!write && ((stat(filename, &statbuf) < 0) ||
- ((statbuf.st_mode & S_IFMT) != S_IFREG))) {
- UCI_THROW(ctx, UCI_ERR_NOTFOUND);
- }
-
- fd = open(filename, mode, UCI_FILEMODE);
- if (fd <= 0)
- goto error;
-
- if (flock(fd, (write ? LOCK_EX : LOCK_SH)) < 0)
- goto error;
-
- ret = lseek(fd, 0, pos);
-
- if (ret < 0)
- goto error;
-
- file = fdopen(fd, (write ? "w+" : "r"));
- if (file)
- goto done;
-
-error:
- UCI_THROW(ctx, UCI_ERR_IO);
-done:
- return file;
-}
-
-static void uci_close_stream(FILE *stream)
-{
- int fd;
-
- if (!stream)
- return;
-
- fd = fileno(stream);
- flock(fd, LOCK_UN);
- fclose(stream);
-}
-
-static void uci_parse_history_line(struct uci_context *ctx, struct uci_package *p, char *buf)
-{
- bool delete = false;
- bool rename = false;
- char *package = NULL;
- char *section = NULL;
- char *option = NULL;
- char *value = NULL;
-
- if (buf[0] == '-') {
- delete = true;
- buf++;
- } else if (buf[0] == '@') {
- rename = true;
- buf++;
- }
-
- UCI_INTERNAL(uci_parse_tuple, ctx, buf, &package, §ion, &option, &value);
- if (!package || (strcmp(package, p->e.name) != 0))
- goto error;
- if (!uci_validate_name(section))
- goto error;
- if (option && !uci_validate_name(option))
- goto error;
- if ((rename || !delete) && !uci_validate_name(value))
- goto error;
-
- if (rename)
- UCI_INTERNAL(uci_rename, ctx, p, section, option, value);
- else if (delete)
- UCI_INTERNAL(uci_delete, ctx, p, section, option);
- else
- UCI_INTERNAL(uci_set, ctx, p, section, option, value);
-
- return;
-error:
- UCI_THROW(ctx, UCI_ERR_PARSE);
-}
-
-static void uci_parse_history(struct uci_context *ctx, FILE *stream, struct uci_package *p)
-{
- struct uci_parse_context *pctx;
-
- /* 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;
- pctx->file = stream;
-
- while (!feof(pctx->file)) {
- uci_getln(ctx, 0);
- if (!pctx->buf[0])
- continue;
-
- /*
- * ignore parse errors in single lines, we want to preserve as much
- * history as possible
- */
- UCI_TRAP_SAVE(ctx, error);
- uci_parse_history_line(ctx, p, pctx->buf);
- UCI_TRAP_RESTORE(ctx);
-error:
- continue;
- }
-
- /* no error happened, we can get rid of the parser context now */
- uci_file_cleanup(ctx);
-}
-
-static void uci_load_history_file(struct uci_context *ctx, struct uci_package *p, char *filename, FILE **f, bool flush)
-{
- FILE *stream = NULL;
-
- UCI_TRAP_SAVE(ctx, done);
- stream = uci_open_stream(ctx, filename, SEEK_SET, flush, false);
- if (p)
- uci_parse_history(ctx, stream, p);
- UCI_TRAP_RESTORE(ctx);
-done:
- if (f)
- *f = stream;
- else if (stream)
- uci_close_stream(stream);
-}
-
-static void uci_load_history(struct uci_context *ctx, struct uci_package *p, bool flush)
-{
- struct uci_element *e;
- char *filename = NULL;
- FILE *f = NULL;
-
- if (!p->confdir)
- return;
-
- uci_foreach_element(&ctx->history_path, e) {
- if ((asprintf(&filename, "%s/%s", e->name, p->e.name) < 0) || !filename)
- UCI_THROW(ctx, UCI_ERR_MEM);
-
- uci_load_history_file(ctx, p, filename, NULL, false);
- free(filename);
- }
-
- if ((asprintf(&filename, "%s/%s", ctx->savedir, p->e.name) < 0) || !filename)
- UCI_THROW(ctx, UCI_ERR_MEM);
-
- uci_load_history_file(ctx, p, filename, &f, flush);
- if (flush && f) {
- rewind(f);
- ftruncate(fileno(f), 0);
- }
- if (filename)
- free(filename);
- uci_close_stream(f);
- ctx->errno = 0;
-}
-
static char *uci_config_path(struct uci_context *ctx, const char *name)
{
return ctx->errno;
}
-int uci_save(struct uci_context *ctx, struct uci_package *p)
-{
- FILE *f = NULL;
- char *filename = NULL;
- struct uci_element *e, *tmp;
-
- UCI_HANDLE_ERR(ctx);
- UCI_ASSERT(ctx, p != NULL);
-
- /*
- * if the config file was outside of the /etc/config path,
- * don't save the history to a file, update the real file
- * directly.
- * does not modify the uci_package pointer
- */
- if (!p->confdir)
- return uci_commit(ctx, &p, false);
-
- if (uci_list_empty(&p->history))
- return 0;
-
- if ((asprintf(&filename, "%s/%s", ctx->savedir, p->e.name) < 0) || !filename)
- UCI_THROW(ctx, UCI_ERR_MEM);
-
- ctx->errno = 0;
- UCI_TRAP_SAVE(ctx, done);
- f = uci_open_stream(ctx, filename, SEEK_END, true, true);
- UCI_TRAP_RESTORE(ctx);
-
- uci_foreach_element_safe(&p->history, tmp, e) {
- struct uci_history *h = uci_to_history(e);
-
- if (h->cmd == UCI_CMD_REMOVE)
- fprintf(f, "-");
- else if (h->cmd == UCI_CMD_RENAME)
- fprintf(f, "@");
-
- fprintf(f, "%s.%s", p->e.name, h->section);
- if (e->name)
- fprintf(f, ".%s", e->name);
-
- if (h->cmd == UCI_CMD_REMOVE)
- fprintf(f, "\n");
- else
- fprintf(f, "=%s\n", h->value);
- uci_free_history(h);
- }
-
-done:
- uci_close_stream(f);
- if (filename)
- free(filename);
- if (ctx->errno)
- UCI_THROW(ctx, ctx->errno);
-
- return 0;
-}
-
int uci_commit(struct uci_context *ctx, struct uci_package **package, bool overwrite)
{
struct uci_package *p;