#endif
static struct uci_context *ctx = NULL;
+enum autoload {
+ AUTOLOAD_OFF = 0,
+ AUTOLOAD_ON = 1,
+ AUTOLOAD_FORCE = 2
+};
static struct uci_package *
-find_package(const char *name)
+find_package(lua_State *L, const char *str, enum autoload al)
{
struct uci_package *p = NULL;
struct uci_element *e;
+ char *sep;
+ char *name;
+
+ sep = strchr(str, '.');
+ if (sep) {
+ name = malloc(1 + sep - str);
+ if (!name)
+ luaL_error(L, "out of memory");
+ strncpy(name, str, sep - str);
+ name[sep - str] = 0;
+ } else
+ name = (char *) str;
+
uci_foreach_element(&ctx->root, e) {
if (strcmp(e->name, name) != 0)
continue;
p = uci_to_package(e);
- break;
+ goto done;
+ }
+
+ if (al == AUTOLOAD_FORCE)
+ uci_load(ctx, name, &p);
+ else if (al) {
+ do {
+ lua_getfield(L, LUA_GLOBALSINDEX, "uci");
+ lua_getfield(L, -1, "autoload");
+ if (!lua_isboolean(L, -1))
+ break;
+
+ if (!lua_toboolean(L, -1))
+ break;
+
+ uci_load(ctx, name, &p);
+ } while (0);
+ lua_pop(L, 2);
}
+
+done:
+ if (name != str)
+ free(name);
return p;
}
lua_pop(L, 2);
}
+static int
+lookup_args(lua_State *L, struct uci_ptr *ptr, char **buf)
+{
+ char *s = NULL;
+ int n;
+
+ n = lua_gettop(L);
+ luaL_checkstring(L, 1);
+ s = strdup(lua_tostring(L, 1));
+ if (!s)
+ goto error;
+
+ memset(ptr, 0, sizeof(struct uci_ptr));
+ if (!find_package(L, s, AUTOLOAD_ON))
+ goto error;
+
+ switch (n) {
+ case 4:
+ case 3:
+ ptr->option = luaL_checkstring(L, 3);
+ /* fall through */
+ case 2:
+ ptr->section = luaL_checkstring(L, 2);
+ ptr->package = luaL_checkstring(L, 1);
+ if (uci_lookup_ptr(ctx, ptr, NULL, false) != UCI_OK)
+ goto error;
+ break;
+ case 1:
+ if (uci_lookup_ptr(ctx, ptr, s, false) != UCI_OK)
+ goto error;
+ break;
+ default:
+ luaL_error(L, "invalid argument count");
+ goto error;
+ }
+
+ *buf = s;
+ return 0;
+
+error:
+ if (s)
+ free(s);
+ return 1;
+}
+
+static void uci_push_option(lua_State *L, struct uci_option *o)
+{
+ struct uci_element *e;
+ int i = 0;
+
+ switch(o->type) {
+ case UCI_TYPE_STRING:
+ lua_pushstring(L, o->v.string);
+ break;
+ case UCI_TYPE_LIST:
+ lua_newtable(L);
+ uci_foreach_element(&o->v.list, e) {
+ i++;
+ lua_pushstring(L, e->name);
+ lua_rawseti(L, -2, i);
+ }
+ break;
+ default:
+ lua_pushnil(L);
+ break;
+ }
+}
+
static void uci_push_section(lua_State *L, struct uci_section *s)
{
struct uci_element *e;
lua_newtable(L);
lua_pushstring(L, s->type);
- lua_setfield(L, -2, "type");
+ lua_setfield(L, -2, ".type");
lua_pushstring(L, s->e.name);
- lua_setfield(L, -2, "name");
-
- lua_newtable(L);
- lua_pushvalue(L, -1);
- lua_setfield(L, -3, "options");
+ lua_setfield(L, -2, ".name");
uci_foreach_element(&s->options, e) {
struct uci_option *o = uci_to_option(e);
- lua_pushstring(L, o->value);
+ uci_push_option(L, o);
lua_setfield(L, -2, o->e.name);
}
- lua_pop(L, 1);
}
static void uci_push_package(lua_State *L, struct uci_package *p)
lua_newtable(L);
uci_foreach_element(&p->sections, e) {
i++;
- luaL_setn(L, -1, i);
uci_push_section(L, uci_to_section(e));
- lua_rawseti(L, -2, i);
+ lua_setfield(L, -2, e->name);
}
}
luaL_checkstring(L, 1);
s = lua_tostring(L, -1);
- p = find_package(s);
+ p = find_package(L, s, AUTOLOAD_OFF);
if (p) {
uci_unload(ctx, p);
lua_pushboolean(L, 1);
if (!lua_isfunction(L, 3) || !package)
luaL_error(L, "Invalid argument");
- p = find_package(package);
+ p = find_package(L, package, AUTOLOAD_ON);
if (!p)
goto done;
uci_lua_get_any(lua_State *L, bool all)
{
struct uci_element *e = NULL;
- struct uci_package *p = NULL;
- const char *package = NULL;
- const char *section = NULL;
- const char *option = NULL;
- char *s;
- int err = UCI_ERR_MEM;
- int n;
+ struct uci_ptr ptr;
+ char *s = NULL;
+ int err = UCI_ERR_NOTFOUND;
- n = lua_gettop(L);
-
- luaL_checkstring(L, 1);
- s = strdup(lua_tostring(L, 1));
- if (!s)
+ if (lookup_args(L, &ptr, &s))
goto error;
- if (n > 1) {
- package = luaL_checkstring(L, 1);
- section = luaL_checkstring(L, 2);
- if (n > 2)
- option = luaL_checkstring(L, 3);
- } else {
- if ((err = uci_parse_tuple(ctx, s, (char **) &package, (char **) §ion, (char **) &option, NULL)))
- goto error;
- }
-
- if (!all && (section == NULL)) {
+ uci_lookup_ptr(ctx, &ptr, NULL, false);
+ if (!all && !ptr.s) {
err = UCI_ERR_INVAL;
goto error;
}
- p = find_package(package);
- if (!p) {
- err = UCI_ERR_NOTFOUND;
- goto error;
- }
-
- if (section) {
- if ((err = uci_lookup(ctx, &e, p, section, option)))
- goto error;
- } else {
- e = &p->e;
- }
-
+ err = UCI_OK;
+ e = ptr.last;
switch(e->type) {
case UCI_TYPE_PACKAGE:
- uci_push_package(L, p);
+ uci_push_package(L, ptr.p);
break;
case UCI_TYPE_SECTION:
if (all)
- uci_push_section(L, uci_to_section(e));
+ uci_push_section(L, ptr.s);
else
- lua_pushstring(L, uci_to_section(e)->type);
+ lua_pushstring(L, ptr.s->type);
break;
case UCI_TYPE_OPTION:
- lua_pushstring(L, uci_to_option(e)->value);
+ uci_push_option(L, ptr.o);
break;
default:
err = UCI_ERR_INVAL;
do {
package = luaL_checkstring(L, 1);
type = luaL_checkstring(L, 2);
- p = find_package(package);
+ p = find_package(L, package, AUTOLOAD_ON);
if (!p)
break;
}
static int
+uci_lua_delete(lua_State *L)
+{
+ struct uci_ptr ptr;
+ char *s = NULL;
+ int err = UCI_ERR_NOTFOUND;
+
+ if (lookup_args(L, &ptr, &s))
+ goto error;
+
+ err = uci_delete(ctx, &ptr);
+
+error:
+ if (s)
+ free(s);
+ if (err)
+ uci_lua_perror(L, "uci.delete");
+ lua_pushboolean(L, (err == 0));
+ return 1;
+}
+
+static int
uci_lua_set(lua_State *L)
{
- struct uci_package *p;
- const char *package = NULL;
- const char *section = NULL;
- const char *option = NULL;
- const char *value = NULL;
- const char *s;
+ bool istable = false;
+ struct uci_ptr ptr;
int err = UCI_ERR_MEM;
- int nargs;
+ char *s = NULL;
+ int i, nargs;
nargs = lua_gettop(L);
+ if (lookup_args(L, &ptr, &s))
+ goto error;
- s = luaL_checkstring(L, 1);
switch(nargs) {
case 1:
/* Format: uci.set("p.s.o=v") or uci.set("p.s=v") */
- s = strdup(s);
- if (!s)
- goto error;
-
- if ((err = uci_parse_tuple(ctx, (char *) s, (char **) &package, (char **) §ion, (char **) &option, (char **) &value)))
- goto error;
break;
case 4:
/* Format: uci.set("p", "s", "o", "v") */
- option = luaL_checkstring(L, 3);
- /* fall through */
+ if (lua_istable(L, nargs)) {
+ if (lua_objlen(L, nargs) < 1)
+ luaL_error(L, "Cannot set an uci option to an empty table value");
+ lua_rawgeti(L, nargs, 1);
+ ptr.value = luaL_checkstring(L, -1);
+ lua_pop(L, 1);
+ istable = true;
+ } else {
+ ptr.value = luaL_checkstring(L, nargs);
+ }
+ break;
case 3:
/* Format: uci.set("p", "s", "v") */
- package = s;
- section = luaL_checkstring(L, 2);
- value = luaL_checkstring(L, nargs);
+ ptr.value = ptr.option;
+ ptr.option = NULL;
break;
default:
err = UCI_ERR_INVAL;
goto error;
}
- if ((section == NULL) || (value == NULL)) {
+ err = uci_lookup_ptr(ctx, &ptr, NULL, false);
+ if (err)
+ goto error;
+
+ if ((ptr.s == NULL) || (ptr.value == NULL)) {
err = UCI_ERR_INVAL;
goto error;
}
- p = find_package(package);
- if (!p) {
- err = UCI_ERR_NOTFOUND;
+ err = uci_set(ctx, &ptr);
+ if (err)
goto error;
+
+ if (istable) {
+ for (i = 2; i <= lua_objlen(L, nargs); i++) {
+ lua_rawgeti(L, nargs, i);
+ ptr.value = luaL_checkstring(L, -1);
+ err = uci_add_list(ctx, &ptr);
+ lua_pop(L, 1);
+ if (err)
+ goto error;
+ }
}
- err = uci_set(ctx, p, section, option, value, NULL);
error:
if (err)
uci_lua_package_cmd(lua_State *L, enum pkg_cmd cmd)
{
struct uci_element *e, *tmp;
- const char *s = NULL;
- const char *section = NULL;
- const char *option = NULL;
+ struct uci_ptr ptr;
+ char *s = NULL;
int failed = 0;
int nargs;
nargs = lua_gettop(L);
- switch(nargs) {
- case 3:
- if (cmd != CMD_REVERT)
- goto err;
- luaL_checkstring(L, 1);
- option = lua_tostring(L, -1);
- lua_pop(L, 1);
- /* fall through */
- case 2:
- if (cmd != CMD_REVERT)
- goto err;
- luaL_checkstring(L, 1);
- section = lua_tostring(L, -1);
- lua_pop(L, 1);
- /* fall through */
- case 1:
- luaL_checkstring(L, 1);
- s = lua_tostring(L, -1);
- lua_pop(L, 1);
- break;
- case 0:
- break;
- default:
- err:
- luaL_error(L, "Invalid argument count");
- break;
- }
+ if ((cmd != CMD_REVERT) && (nargs > 1))
+ goto err;
+
+ if (lookup_args(L, &ptr, &s))
+ goto err;
+
+ uci_lookup_ptr(ctx, &ptr, NULL, false);
uci_foreach_element_safe(&ctx->root, tmp, e) {
struct uci_package *p = uci_to_package(e);
int ret = UCI_ERR_INVAL;
- if (s && (strcmp(s, e->name) != 0))
+ if (ptr.p && (ptr.p != p))
continue;
+ ptr.p = p;
switch(cmd) {
case CMD_COMMIT:
ret = uci_commit(ctx, &p, false);
ret = uci_save(ctx, p);
break;
case CMD_REVERT:
- ret = uci_revert(ctx, &p, section, option);
+ ret = uci_revert(ctx, &ptr);
break;
}
failed = 1;
}
+err:
lua_pushboolean(L, !failed);
return 1;
}
return uci_lua_package_cmd(L, CMD_REVERT);
}
+static void
+uci_lua_add_change(lua_State *L, struct uci_element *e)
+{
+ struct uci_history *h;
+ const char *name;
+
+ h = uci_to_history(e);
+ if (!h->section)
+ return;
+
+ lua_getfield(L, -1, h->section);
+ if (lua_isnil(L, -1)) {
+ lua_pop(L, 1);
+ lua_newtable(L);
+ lua_pushvalue(L, -1); /* copy for setfield */
+ lua_setfield(L, -3, h->section);
+ }
+
+ name = (h->e.name ? h->e.name : ".type");
+ if (h->value)
+ lua_pushstring(L, h->value);
+ else
+ lua_pushstring(L, "");
+ lua_setfield(L, -2, name);
+ lua_pop(L, 1);
+}
+
+static void
+uci_lua_changes_pkg(lua_State *L, const char *package)
+{
+ struct uci_package *p = NULL;
+ struct uci_element *e;
+ bool autoload = false;
+
+ p = find_package(L, package, AUTOLOAD_OFF);
+ if (!p) {
+ autoload = true;
+ p = find_package(L, package, AUTOLOAD_FORCE);
+ if (!p)
+ return;
+ }
+
+ if (uci_list_empty(&p->history) && uci_list_empty(&p->saved_history))
+ goto done;
+
+ lua_newtable(L);
+ uci_foreach_element(&p->saved_history, e) {
+ uci_lua_add_change(L, e);
+ }
+ uci_foreach_element(&p->history, e) {
+ uci_lua_add_change(L, e);
+ }
+ lua_setfield(L, -2, p->e.name);
+
+done:
+ if (autoload)
+ uci_unload(ctx, p);
+}
+
+static int
+uci_lua_changes(lua_State *L)
+{
+ const char *package = NULL;
+ char **config = NULL;
+ int nargs;
+ int i;
+
+ nargs = lua_gettop(L);
+ switch(nargs) {
+ case 1:
+ package = luaL_checkstring(L, 1);
+ case 0:
+ break;
+ default:
+ luaL_error(L, "invalid argument count");
+ }
+
+ lua_newtable(L);
+ if (package) {
+ uci_lua_changes_pkg(L, package);
+ } else {
+ if (uci_list_configs(ctx, &config) != 0)
+ goto done;
+
+ for(i = 0; config[i] != NULL; i++) {
+ uci_lua_changes_pkg(L, config[i]);
+ }
+ }
+
+done:
+ return 1;
+}
+
static int
uci_lua_set_confdir(lua_State *L)
{
{ "add", uci_lua_add },
{ "set", uci_lua_set },
{ "save", uci_lua_save },
+ { "delete", uci_lua_delete },
{ "commit", uci_lua_commit },
{ "revert", uci_lua_revert },
+ { "changes", uci_lua_changes },
{ "foreach", uci_lua_foreach },
{ "set_confdir", uci_lua_set_confdir },
{ "set_savedir", uci_lua_set_savedir },
if (!ctx)
luaL_error(L, "Cannot allocate UCI context\n");
luaL_register(L, MODNAME, uci);
+
+ /* enable autoload by default */
+ lua_getfield(L, LUA_GLOBALSINDEX, "uci");
+ lua_pushboolean(L, 1);
+ lua_setfield(L, -2, "autoload");
+ lua_pop(L, 1);
+
return 0;
}