diff --git a/src/3P/uci/.gitignore b/src/3P/uci/.gitignore new file mode 100644 index 00000000..0407feb4 --- /dev/null +++ b/src/3P/uci/.gitignore @@ -0,0 +1,13 @@ +Makefile +CMakeCache.txt +CMakeFiles +*.cmake +*.o +*.a +*.so +*.dylib +install_manifest.txt + +uci +uci_config.h +test/save diff --git a/src/3P/uci/CMakeLists.txt b/src/3P/uci/CMakeLists.txt new file mode 100644 index 00000000..a900a156 --- /dev/null +++ b/src/3P/uci/CMakeLists.txt @@ -0,0 +1,47 @@ +cmake_minimum_required(VERSION 2.6) + +PROJECT(uci C) + +SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") +ADD_DEFINITIONS(-Os -Wall -Werror --std=gnu99 -g3 -I. -DUCI_PREFIX="${CMAKE_INSTALL_PREFIX}") + +OPTION(UCI_DEBUG "debugging support" OFF) +OPTION(UCI_DEBUG_TYPECAST "typecast debugging support" OFF) +OPTION(BUILD_LUA "build Lua binding" ON) + +IF(BUILD_STATIC) + FIND_LIBRARY(ubox_library NAMES ubox.a) +ELSE(BUILD_STATIC) + FIND_LIBRARY(ubox_library NAMES ubox) +ENDIF(BUILD_STATIC) + +FIND_PATH(ubox_include_dir libubox/usock.h) + +CONFIGURE_FILE( ${CMAKE_SOURCE_DIR}/uci_config.h.in ${CMAKE_SOURCE_DIR}/uci_config.h ) + +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR} ${ubox_include_dir}) + +SET(LIB_SOURCES libuci.c file.c util.c delta.c parse.c blob.c) + +ADD_LIBRARY(uci SHARED ${LIB_SOURCES}) +TARGET_LINK_LIBRARIES(uci ${ubox_library}) +SET_TARGET_PROPERTIES(uci PROPERTIES OUTPUT_NAME uci) + +ADD_EXECUTABLE(cli cli.c) +SET_TARGET_PROPERTIES(cli PROPERTIES OUTPUT_NAME uci) +TARGET_LINK_LIBRARIES(cli uci) + +ADD_LIBRARY(ucimap STATIC ucimap.c) + +ADD_SUBDIRECTORY(lua) + +INSTALL(FILES uci.h uci_config.h uci_blob.h ucimap.h + DESTINATION include +) + +INSTALL(TARGETS uci cli + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib + RUNTIME DESTINATION bin +) + diff --git a/src/3P/uci/blob.c b/src/3P/uci/blob.c new file mode 100644 index 00000000..c8e5dc90 --- /dev/null +++ b/src/3P/uci/blob.c @@ -0,0 +1,238 @@ +/* + * blob.c - uci <-> blobmsg conversion layer + * Copyright (C) 2012-2013 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 Lesser General Public License for more details. + */ +#include +#include +#include + +#include +#include "uci.h" +#include "uci_blob.h" + +static bool +uci_attr_to_blob(struct blob_buf *b, const char *str, + const char *name, enum blobmsg_type type) +{ + char *err; + int intval; + long long llval; + + switch (type) { + case BLOBMSG_TYPE_STRING: + blobmsg_add_string(b, name, str); + break; + case BLOBMSG_TYPE_BOOL: + if (!strcmp(str, "true") || !strcmp(str, "1")) + intval = 1; + else if (!strcmp(str, "false") || !strcmp(str, "0")) + intval = 0; + else + return false; + + blobmsg_add_u8(b, name, intval); + break; + case BLOBMSG_TYPE_INT32: + intval = strtol(str, &err, 0); + if (*err) + return false; + + blobmsg_add_u32(b, name, intval); + break; + case BLOBMSG_TYPE_INT64: + llval = strtoll(str, &err, 0); + if (*err) + return false; + + blobmsg_add_u64(b, name, llval); + break; + default: + return false; + } + return true; +} + +static void +uci_array_to_blob(struct blob_buf *b, struct uci_option *o, + enum blobmsg_type type) +{ + struct uci_element *e; + char *str, *next, *word; + + if (o->type == UCI_TYPE_LIST) { + uci_foreach_element(&o->v.list, e) { + uci_attr_to_blob(b, e->name, NULL, type); + } + return; + } + + str = strdup(o->v.string); + next = str; + + while ((word = strsep(&next, " \t")) != NULL) { + if (!*word) + continue; + + uci_attr_to_blob(b, word, NULL, type); + } + + free(str); +} + +static int +__uci_element_to_blob(struct blob_buf *b, struct uci_element *e, + const struct uci_blob_param_list *p) +{ + const struct blobmsg_policy *attr = NULL; + struct uci_option *o = uci_to_option(e); + unsigned int types = 0; + void *array; + int i, ret = 0; + + for (i = 0; i < p->n_params; i++) { + attr = &p->params[i]; + + if (strcmp(attr->name, e->name) != 0) + continue; + + if (attr->type > BLOBMSG_TYPE_LAST) + continue; + + if (types & (1 << attr->type)) + continue; + + types |= 1 << attr->type; + + if (attr->type == BLOBMSG_TYPE_ARRAY) { + int element_type = 0; + + if (p->info) + element_type = p->info[i].type; + + if (!element_type) + element_type = BLOBMSG_TYPE_STRING; + + array = blobmsg_open_array(b, attr->name); + uci_array_to_blob(b, o, element_type); + blobmsg_close_array(b, array); + ret++; + continue; + } + + if (o->type == UCI_TYPE_LIST) + continue; + + ret += uci_attr_to_blob(b, o->v.string, attr->name, attr->type); + } + return ret; +} + +static int +__uci_to_blob(struct blob_buf *b, struct uci_section *s, + const struct uci_blob_param_list *p) +{ + struct uci_element *e; + int ret = 0; + + uci_foreach_element(&s->options, e) + ret += __uci_element_to_blob(b, e, p); + + return ret; +} + +int +uci_to_blob(struct blob_buf *b, struct uci_section *s, + const struct uci_blob_param_list *p) +{ + int ret = 0; + int i; + + ret += __uci_to_blob(b, s, p); + for (i = 0; i < p->n_next; i++) + ret += uci_to_blob(b, s, p->next[i]); + + return ret; +} + +bool +uci_blob_diff(struct blob_attr **tb1, struct blob_attr **tb2, + const struct uci_blob_param_list *config, unsigned long *diff) +{ + bool ret = false; + int i; + + for (i = 0; i < config->n_params; i++) { + if (!tb1[i] && !tb2[i]) + continue; + + if (!!tb1[i] != !!tb2[i]) + goto mark; + + if (blob_len(tb1[i]) != blob_len(tb2[i])) + goto mark; + + if (memcmp(tb1[i], tb2[i], blob_raw_len(tb1[i])) != 0) + goto mark; + + continue; + +mark: + ret = true; + if (diff) + bitfield_set(diff, i); + else + return ret; + } + + return ret; +} + + +static bool +__uci_blob_check_equal(struct blob_attr *c1, struct blob_attr *c2, + const struct uci_blob_param_list *config) +{ + struct blob_attr **tb1, **tb2; + + if (!!c1 ^ !!c2) + return false; + + if (!c1 && !c2) + return true; + + tb1 = alloca(config->n_params * sizeof(struct blob_attr *)); + blobmsg_parse(config->params, config->n_params, tb1, + blob_data(c1), blob_len(c1)); + + tb2 = alloca(config->n_params * sizeof(struct blob_attr *)); + blobmsg_parse(config->params, config->n_params, tb2, + blob_data(c2), blob_len(c2)); + + return !uci_blob_diff(tb1, tb2, config, NULL); +} + +bool +uci_blob_check_equal(struct blob_attr *c1, struct blob_attr *c2, + const struct uci_blob_param_list *config) +{ + int i; + + if (!__uci_blob_check_equal(c1, c2, config)) + return false; + + for (i = 0; i < config->n_next; i++) { + if (!__uci_blob_check_equal(c1, c2, config->next[i])) + return false; + } + + return true; +} diff --git a/src/3P/uci/cli.c b/src/3P/uci/cli.c new file mode 100644 index 00000000..f8b45dba --- /dev/null +++ b/src/3P/uci/cli.c @@ -0,0 +1,777 @@ +/* + * cli - Command Line Interface 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 General Public License version 2 + * 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 +#include +#include +#include +#include +#include +#include "uci.h" + +#define MAX_ARGS 4 /* max command line arguments for batch mode */ + +static const char *delimiter = " "; +static const char *appname; +static enum { + CLI_FLAG_MERGE = (1 << 0), + CLI_FLAG_QUIET = (1 << 1), + CLI_FLAG_NOCOMMIT = (1 << 2), + CLI_FLAG_BATCH = (1 << 3), + CLI_FLAG_SHOW_EXT = (1 << 4), +} flags; + +static FILE *input; + +static struct uci_context *ctx; +enum { + /* section cmds */ + CMD_GET, + CMD_SET, + CMD_ADD_LIST, + CMD_DEL_LIST, + CMD_DEL, + CMD_RENAME, + CMD_REVERT, + CMD_REORDER, + /* package cmds */ + CMD_SHOW, + CMD_CHANGES, + CMD_EXPORT, + CMD_COMMIT, + /* other cmds */ + CMD_ADD, + CMD_IMPORT, + CMD_HELP, +}; + +struct uci_type_list { + unsigned int idx; + const char *name; + struct uci_type_list *next; +}; + +static struct uci_type_list *type_list = NULL; +static char *typestr = NULL; +static const char *cur_section_ref = NULL; + +static int uci_cmd(int argc, char **argv); + +static void +uci_reset_typelist(void) +{ + struct uci_type_list *type; + while (type_list != NULL) { + type = type_list; + type_list = type_list->next; + free(type); + } + if (typestr) { + free(typestr); + typestr = NULL; + } + cur_section_ref = NULL; +} + +static char * +uci_lookup_section_ref(struct uci_section *s) +{ + struct uci_type_list *ti = type_list; + char *ret; + int maxlen; + + if (!(flags & CLI_FLAG_SHOW_EXT)) + return s->e.name; + + /* look up in section type list */ + while (ti) { + if (strcmp(ti->name, s->type) == 0) + break; + ti = ti->next; + } + if (!ti) { + ti = malloc(sizeof(struct uci_type_list)); + if (!ti) + return NULL; + memset(ti, 0, sizeof(struct uci_type_list)); + ti->next = type_list; + type_list = ti; + ti->name = s->type; + } + + if (s->anonymous) { + maxlen = strlen(s->type) + 1 + 2 + 10; + if (!typestr) { + typestr = malloc(maxlen); + } else { + typestr = realloc(typestr, maxlen); + } + + if (typestr) + sprintf(typestr, "@%s[%d]", ti->name, ti->idx); + + ret = typestr; + } else { + ret = s->e.name; + } + + ti->idx++; + + return ret; +} + +static void uci_usage(void) +{ + fprintf(stderr, + "Usage: %s [] []\n\n" + "Commands:\n" + "\tbatch\n" + "\texport []\n" + "\timport []\n" + "\tchanges []\n" + "\tcommit []\n" + "\tadd \n" + "\tadd_list .
.