endif
SHLIB_FILE=libuci.$(SHLIB_EXT).$(VERSION)
+LIBUCI_DEPS=file.c history.c list.c util.c err.h uci.h
+
all: uci-static uci libuci.$(SHLIB_EXT)
cli.o: cli.c uci.h
uci-static: cli.o libuci.a
$(CC) $(CFLAGS) -o $@ $^
-libuci-static.o: libuci.c file.c uci.h list.c err.h util.c
+libuci-static.o: libuci.c $(LIBUCI_DEPS)
$(CC) $(CFLAGS) -c -o $@ $<
-libuci-shared.o: libuci.c file.c uci.h list.c err.h util.c
+libuci-shared.o: libuci.c $(LIBUCI_DEPS)
$(CC) $(CFLAGS) $(FPIC) -c -o $@ $<
libuci.a: libuci-static.o
#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;
--- /dev/null
+/*
+ * libuci - Library for the Unified Configuration Interface
+ * Copyright (C) 2008 Felix Fietkau <nbd@openwrt.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * This file contains the code for handling uci config history files
+ */
+
+#define _GNU_SOURCE
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <ctype.h>
+
+int uci_set_savedir(struct uci_context *ctx, const char *dir)
+{
+ char *sdir;
+
+ UCI_HANDLE_ERR(ctx);
+ UCI_ASSERT(ctx, dir != NULL);
+
+ sdir = uci_strdup(ctx, dir);
+ if (ctx->savedir != uci_savedir)
+ free(ctx->savedir);
+ ctx->savedir = sdir;
+ return 0;
+}
+
+int uci_add_history_path(struct uci_context *ctx, const char *dir)
+{
+ struct uci_element *e;
+
+ UCI_HANDLE_ERR(ctx);
+ UCI_ASSERT(ctx, dir != NULL);
+ e = uci_alloc_generic(ctx, UCI_TYPE_PATH, dir, sizeof(struct uci_element));
+ uci_list_add(&ctx->history_path, &e->list);
+
+ return 0;
+}
+
+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 */
+ ctx->internal = true;
+ uci_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 */
+ ctx->internal = true;
+ uci_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;
+}
+
+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;
+}
+
+
#include "util.c"
#include "list.c"
+#include "history.c"
#include "file.c"
/* exported functions */
return;
}
-int uci_add_history_path(struct uci_context *ctx, const char *dir)
-{
- struct uci_element *e;
-
- UCI_HANDLE_ERR(ctx);
- UCI_ASSERT(ctx, dir != NULL);
- e = uci_alloc_generic(ctx, UCI_TYPE_PATH, dir, sizeof(struct uci_element));
- uci_list_add(&ctx->history_path, &e->list);
-
- return 0;
-}
-
int uci_set_confdir(struct uci_context *ctx, const char *dir)
{
char *cdir;
return 0;
}
-int uci_set_savedir(struct uci_context *ctx, const char *dir)
-{
- char *sdir;
-
- UCI_HANDLE_ERR(ctx);
- UCI_ASSERT(ctx, dir != NULL);
-
- sdir = uci_strdup(ctx, dir);
- if (ctx->savedir != uci_savedir)
- free(ctx->savedir);
- ctx->savedir = sdir;
- return 0;
-}
-
int uci_cleanup(struct uci_context *ctx)
{
UCI_HANDLE_ERR(ctx);
* This file contains wrappers to standard functions, which
* throw exceptions upon failure.
*/
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/file.h>
#include <stdbool.h>
+#include <unistd.h>
#include <ctype.h>
+#include <fcntl.h>
+
+#define LINEBUF 32
+#define LINEBUF_MAX 4096
static void *uci_malloc(struct uci_context *ctx, size_t size)
{
return 0;
}
+
+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);
+}
+
+/*
+ * 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);
+}
+
+