From: Felix Fietkau Date: Sat, 19 Jan 2008 16:56:13 +0000 (+0100) Subject: initial import (incomplete) X-Git-Url: http://git.archive.openwrt.org/?p=project%2Fuci.git;a=commitdiff_plain;h=d5b661a39ea9320c58f44cb4b32691deffc7cb30 initial import (incomplete) --- d5b661a39ea9320c58f44cb4b32691deffc7cb30 diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..3b69678 --- /dev/null +++ b/Makefile @@ -0,0 +1,12 @@ +CC=gcc +CFLAGS=-O2 -Wall -pedantic -std=gnu99 + +all: parsetest +parsetest: libuci.o test.o + $(CC) $(CFLAGS) -o $@ $^ + +libuci.o: libuci.c parse.c libuci.h +test.o: test.c libuci.h + +clean: + rm -f parsetest *.o diff --git a/hash.c b/hash.c new file mode 100644 index 0000000..43b58f4 --- /dev/null +++ b/hash.c @@ -0,0 +1,14 @@ +/* + * libuci - Library for the Unified Configuration Interface + * Copyright (C) 2008 Felix Fietkau + * + * 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. + */ + diff --git a/libuci.c b/libuci.c new file mode 100644 index 0000000..66b702d --- /dev/null +++ b/libuci.c @@ -0,0 +1,155 @@ +/* + * libuci - Library for the Unified Configuration Interface + * Copyright (C) 2008 Felix Fietkau + * + * 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 some common code for the uci library + */ + +#include +#include +#include +#include +#include "libuci.h" + +#define DEBUG + +#ifdef DEBUG +#define DPRINTF(...) fprintf(stderr, __VA_ARGS__) +#else +#define DPRINTF(...) +#endif +/* + * throw an uci exception and store the error number + * in the context. + */ +#define UCI_THROW(ctx, err) do { \ + longjmp(ctx->trap, err); \ +} while (0) + +/* + * store the return address for handling exceptions + * needs to be called in every externally visible library function + * + * NB: this does not handle recursion at all. Calling externally visible + * functions from other uci functions is only allowed at the end of the + * calling function. + */ +#define UCI_HANDLE_ERR(ctx) do { \ + int __val; \ + if (!ctx) \ + return UCI_ERR_INVAL; \ + __val = setjmp(ctx->trap); \ + if (__val) { \ + ctx->errno = __val; \ + return __val; \ + } \ +} while (0) + +/* + * check the specified condition. + * throw an invalid argument exception if it's false + */ +#define UCI_ASSERT(ctx, expr) do { \ + if (!(expr)) { \ + DPRINTF("[%s:%d] Assertion failed\n", __FILE__, __LINE__); \ + UCI_THROW(ctx, UCI_ERR_INVAL); \ + } \ +} while (0) + + +static char *uci_errstr[] = { + [UCI_OK] = "Success", + [UCI_ERR_MEM] = "Out of memory", + [UCI_ERR_INVAL] = "Invalid argument", + [UCI_ERR_NOTFOUND] = "Entry not found", + [UCI_ERR_PARSE] = "Parse error", + [UCI_ERR_UNKNOWN] = "Unknown error", +}; + + +/* + * UCI wrapper for malloc, which uses exception handling + */ +static void *uci_malloc(struct uci_context *ctx, size_t size) +{ + void *ptr; + + ptr = malloc(size); + if (!ptr) + UCI_THROW(ctx, UCI_ERR_MEM); + memset(ptr, 0, size); + + return ptr; +} + +/* + * UCI wrapper for realloc, which uses exception handling + */ +static void *uci_realloc(struct uci_context *ctx, void *ptr, size_t size) +{ + ptr = realloc(ptr, size); + if (!ptr) + UCI_THROW(ctx, UCI_ERR_MEM); + + return ptr; +} + +#include "hash.c" +#include "parse.c" + +/* externally visible functions */ + +struct uci_context *uci_alloc(void) +{ + struct uci_context *ctx; + + ctx = (struct uci_context *) malloc(sizeof(struct uci_context)); + memset(ctx, 0, sizeof(struct uci_context)); + + return ctx; +} + +int uci_cleanup(struct uci_context *ctx) +{ + UCI_HANDLE_ERR(ctx); + uci_parse_cleanup(ctx); + return 0; +} + +void uci_perror(struct uci_context *ctx, const char *str) +{ + int err; + + if (!ctx) + err = UCI_ERR_INVAL; + else + err = ctx->errno; + + if ((err < 0) || (err >= UCI_ERR_LAST)) + err = UCI_ERR_UNKNOWN; + + switch (err) { + case UCI_ERR_PARSE: + if (ctx->pctx) { + fprintf(stderr, "%s: %s at line %d, byte %d\n", str, uci_errstr[err], ctx->pctx->line, ctx->pctx->byte); + break; + } + /* fall through */ + default: + fprintf(stderr, "%s: %s\n", str, uci_errstr[err]); + break; + } +} + + diff --git a/libuci.h b/libuci.h new file mode 100644 index 0000000..9411b54 --- /dev/null +++ b/libuci.h @@ -0,0 +1,86 @@ +/* + * libuci - Library for the Unified Configuration Interface + * Copyright (C) 2008 Felix Fietkau + * + * 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. + */ + +#ifndef __LIBUCI_H +#define __LIBUCI_H + +#include +#include + +enum +{ + UCI_OK = 0, + UCI_ERR_MEM, + UCI_ERR_INVAL, + UCI_ERR_NOTFOUND, + UCI_ERR_PARSE, + UCI_ERR_UNKNOWN, + UCI_ERR_LAST +}; + +struct uci_config; +struct uci_parse_context; + +struct uci_context +{ + struct uci_config *root; + + /* for error handling only */ + struct uci_parse_context *pctx; + + /* private: */ + int errno; + jmp_buf trap; +}; + +struct uci_parse_context +{ + int line; + int byte; + + /* private: */ + FILE *file; + char *buf; + int bufsz; +}; + + +/** + * uci_alloc: Allocate a new uci context + */ +extern struct uci_context *uci_alloc(void); + +/** + * uci_perror: Print the last uci error that occured + * @ctx: uci context + * @str: string to print before the error message + */ +extern void uci_perror(struct uci_context *ctx, const char *str); + +/** + * uci_parse: Parse an uci config file and store it in the uci context + * + * @ctx: uci context + * @name: name of the config file (relative to the config directory) + */ +int uci_parse(struct uci_context *ctx, const char *name); + +/** + * uci_cleanup: Clean up after an error + * + * @ctx: uci context + */ +int uci_cleanup(struct uci_context *ctx); + +#endif diff --git a/parse.c b/parse.c new file mode 100644 index 0000000..b6ee734 --- /dev/null +++ b/parse.c @@ -0,0 +1,252 @@ +/* + * libuci - Library for the Unified Configuration Interface + * Copyright (C) 2008 Felix Fietkau + * + * 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 parsing uci config files + */ + +#include + +#define LINEBUF 128 +#define LINEBUF_MAX 4096 + +/* + * Fetch a new line from the input stream and resize buffer if necessary + */ +static void uci_getln(struct uci_context *ctx) +{ + 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 = 0; + do { + p = &pctx->buf[ofs]; + p[ofs] = 0; + + p = fgets(p, pctx->bufsz - ofs, pctx->file); + if (!p || !p[ofs]) + return; + + ofs += strlen(p); + if (pctx->buf[ofs - 1] == '\n') { + pctx->line++; + pctx->buf[ofs - 1] = 0; + return; + } + + if (pctx->bufsz > LINEBUF_MAX/2) { + pctx->byte = LINEBUF_MAX; + UCI_THROW(ctx, UCI_ERR_PARSE); + } + + pctx->bufsz *= 2; + pctx->buf = uci_realloc(ctx, pctx->buf, pctx->bufsz); + } while (1); +} + +/* + * Clean up all extra memory used by the parser + */ +static void uci_parse_cleanup(struct uci_context *ctx) +{ + struct uci_parse_context *pctx; + + pctx = ctx->pctx; + if (!pctx) + return; + + if (pctx->buf) + free(pctx->buf); + if (pctx->file) + fclose(pctx->file); + + free(pctx); +} + +static void skip_whitespace(char **str) +{ + while (**str && isspace(**str)) + *str += 1; +} + +static char *parse_double_quote(char **str) +{ + char *val; + + *str += 1; + val = *str; + while (**str) { + + /* skip escaped characters */ + if (**str == '\\') { + *str += 2; + continue; + } + + /* check for the end of the quoted string */ + if (**str == '"') { + **str = 0; + *str += 1; + return val; + } + *str += 1; + } + + return NULL; +} + +static char *parse_single_quote(char **str) +{ + /* TODO: implement */ + return NULL; +} + +static char *parse_unquoted(char **str) +{ + char *val; + + val = *str; + + while (**str && !isspace(**str)) + *str += 1; + **str = 0; + *str += 1; + + return val; +} + +static char *next_arg(char **str) +{ + skip_whitespace(str); + + switch (**str) { + case '"': + return parse_double_quote(str); + case '\'': + return parse_single_quote(str); + case 0: + return NULL; + default: + return parse_unquoted(str); + } +} + +static void uci_parse_config(struct uci_context *ctx, char **str) +{ + char *type, *name; + + *str += strlen(*str) + 1; + + if (!*str) { + ctx->pctx->byte = *str - ctx->pctx->buf; + UCI_THROW(ctx, UCI_ERR_PARSE); + } + + type = next_arg(str); + if (!type) { + ctx->pctx->byte = *str - ctx->pctx->buf; + UCI_THROW(ctx, UCI_ERR_PARSE); + } + + name = next_arg(str); + DPRINTF("Section<%s>: %s\n", type, name); +} + +static void uci_parse_option(struct uci_context *ctx, char **str) +{ + char *name, *value; + + *str += strlen(*str) + 1; + + name = next_arg(str); + if (!name) { + ctx->pctx->byte = *str - ctx->pctx->buf; + UCI_THROW(ctx, UCI_ERR_PARSE); + } + + value = next_arg(str); + if (!value) { + ctx->pctx->byte = *str - ctx->pctx->buf; + UCI_THROW(ctx, UCI_ERR_PARSE); + } + + DPRINTF("\tOption: %s=\"%s\"\n", name, value); +} + +static void uci_parse_line(struct uci_context *ctx) +{ + struct uci_parse_context *pctx = ctx->pctx; + char *word, *brk; + + for (word = strtok_r(pctx->buf, ";", &brk); + word; + word = strtok_r(NULL, ";", &brk)) { + + char *pbrk; + word = strtok_r(word, " \t", &pbrk); + + switch(word[0]) { + case 'c': + if ((word[1] == 0) || !strcmp(word + 1, "onfig")) + uci_parse_config(ctx, &word); + break; + case 'o': + if ((word[1] == 0) || !strcmp(word + 1, "ption")) + uci_parse_option(ctx, &word); + break; + default: + pctx->byte = word - pctx->buf; + UCI_THROW(ctx, UCI_ERR_PARSE); + break; + } + } +} + +int uci_parse(struct uci_context *ctx, const char *name) +{ + struct uci_parse_context *pctx; + + UCI_HANDLE_ERR(ctx); + UCI_ASSERT(ctx, name != NULL); + + /* make sure no memory from previous parse attempts is leaked */ + uci_parse_cleanup(ctx); + + pctx = (struct uci_parse_context *) uci_malloc(ctx, sizeof(struct uci_parse_context)); + ctx->pctx = pctx; + + /* TODO: use /etc/config/ */ + pctx->file = fopen(name, "r"); + if (!pctx->file) + UCI_THROW(ctx, UCI_ERR_NOTFOUND); + + while (!feof(pctx->file)) { + uci_getln(ctx); + if (*(pctx->buf)) + uci_parse_line(ctx); + } + + /* if no error happened, we can get rid of the parser context now */ + uci_parse_cleanup(ctx); + + return 0; +} + + diff --git a/test.c b/test.c new file mode 100644 index 0000000..9b0b106 --- /dev/null +++ b/test.c @@ -0,0 +1,31 @@ +/* + * libuci - Library for the Unified Configuration Interface + * Copyright (C) 2008 Felix Fietkau + * + * 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. + */ +#include "libuci.h" + +int main(int argc, char **argv) +{ + struct uci_context *ctx = uci_alloc(); + + if (!ctx) { + fprintf(stderr, "Failed to allocate uci context"); + return 1; + } + + if (uci_parse(ctx, argv[1])) { + uci_perror(ctx, "uci_parse"); + return 1; + } + + return 0; +}