+/*
+ * Copyright (c) 2009, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the University of California, Berkeley nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <math.h>
+#include <stdio.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/queue.h>
+
+#include <cmdline_parse.h>
+#include <cmdline_parse_ipaddr.h>
+#include <cmdline_parse_num.h>
+#include <cmdline_parse_string.h>
+#include <cmdline_rdline.h>
+#include <cmdline.h>
+
+#include "parse_confnode.h"
+#include "expression.h"
+#include "conf_parser.h"
+#include "confnode.h"
+#include "conf_htable.h"
+#include "dotconfig.h"
+
+extern struct confnode *conf_cur;
+extern struct confnode *conf_root;
+
+/**********************************************************/
+/* ls */
+/**********************************************************/
+
+struct cmd_ls_result {
+ cmdline_fixed_string_t ls;
+};
+
+static void do_ls(const struct confnode *n)
+{
+ const struct confnode *c;
+
+ TAILQ_FOREACH(c, &n->children, next) {
+ if (c->flags & CONFNODE_F_INVISIBLE)
+ do_ls(c);
+ else
+ confnode_display_short(c);
+ }
+}
+
+static void cmd_ls_parsed(void *parsed_result,
+ struct cmdline *cl,
+ void *data)
+{
+ do_ls(conf_cur);
+}
+
+cmdline_parse_token_string_t cmd_ls_ls =
+ TOKEN_STRING_INITIALIZER(struct cmd_ls_result, ls, "ls");
+
+cmdline_parse_inst_t cmd_ls = {
+ .f = cmd_ls_parsed, /* function to call */
+ .data = NULL, /* 2nd arg of func */
+ .help_str = "<ls> list all config options in current directory",
+ .tokens = { /* token list, NULL terminated */
+ (void *)&cmd_ls_ls,
+ NULL,
+ },
+};
+
+/**********************************************************/
+/* ls node */
+/**********************************************************/
+
+struct cmd_ls_node_result {
+ cmdline_fixed_string_t ls;
+ struct confnode *node;
+};
+
+static void cmd_ls_node_parsed(void *parsed_result,
+ struct cmdline *cl,
+ void *data)
+{
+ struct cmd_ls_node_result *res = parsed_result;
+ struct confnode *n = res->node;
+
+ while (n) {
+ //display_one_confnode(n);
+ n = TAILQ_NEXT(n, user_next);
+ }
+}
+
+cmdline_parse_token_string_t cmd_ls_node_ls =
+ TOKEN_STRING_INITIALIZER(struct cmd_ls_node_result, ls, "ls");
+parse_token_conf_node_t cmd_ls_node_node =
+ TOKEN_CONF_NODE_INITIALIZER(struct cmd_ls_node_result, node,
+ &conf_root, &conf_cur, 0, CONFNODE_F_INVISIBLE);
+
+cmdline_parse_inst_t cmd_ls_node = {
+ .f = cmd_ls_node_parsed, /* function to call */
+ .data = NULL, /* 2nd arg of func */
+ .help_str = "<ls NODE> list config node(s) given in argument",
+ .tokens = { /* token list, NULL terminated */
+ (void *)&cmd_ls_node_ls,
+ (void *)&cmd_ls_node_node,
+ NULL,
+ },
+};
+
+/**********************************************************/
+/* pwd */
+/**********************************************************/
+
+struct cmd_pwd_result {
+ cmdline_fixed_string_t pwd;
+};
+
+static void cmd_pwd_parsed(void *parsed_result,
+ struct cmdline *cl,
+ void *data)
+{
+ conf_display_path(conf_cur);
+}
+
+cmdline_parse_token_string_t cmd_pwd_pwd =
+ TOKEN_STRING_INITIALIZER(struct cmd_pwd_result, pwd, "pwd");
+
+cmdline_parse_inst_t cmd_pwd = {
+ .f = cmd_pwd_parsed, /* function to call */
+ .data = NULL, /* 2nd arg of func */
+ .help_str = "<pwd> display current directory",
+ .tokens = { /* token list, NULL terminated */
+ (void *)&cmd_pwd_pwd,
+ NULL,
+ },
+};
+
+/**********************************************************/
+/* cd prev */
+/**********************************************************/
+
+struct cmd_cd_prev_result {
+ cmdline_fixed_string_t cd;
+ cmdline_fixed_string_t prev;
+};
+
+static void cmd_cd_prev_parsed(void *parsed_result,
+ struct cmdline *cl,
+ void *data)
+{
+ struct confnode *parent = conf_cur->parent;
+ char prompt[RDLINE_PROMPT_SIZE];
+
+ if (parent == NULL) {
+ printf("already at top level\n");
+ return;
+ }
+ while ((parent->flags & CONFNODE_F_INVISIBLE) && parent->parent != NULL)
+ parent = parent->parent;
+
+ conf_cur = parent;
+ snprintf(prompt, sizeof(prompt), "%s> ", conf_cur->name);
+ cmdline_set_prompt(cl, prompt);
+}
+
+cmdline_parse_token_string_t cmd_cd_prev_cd =
+ TOKEN_STRING_INITIALIZER(struct cmd_cd_prev_result, cd, "cd");
+cmdline_parse_token_string_t cmd_cd_prev_prev =
+ TOKEN_STRING_INITIALIZER(struct cmd_cd_prev_result, prev, "..");
+
+cmdline_parse_inst_t cmd_cd_prev = {
+ .f = cmd_cd_prev_parsed, /* function to call */
+ .data = NULL, /* 2nd arg of func */
+ .help_str = "<cd ..> go up in configuration tree",
+ .tokens = { /* token list, NULL terminated */
+ (void *)&cmd_cd_prev_cd,
+ (void *)&cmd_cd_prev_prev,
+ NULL,
+ },
+};
+
+/**********************************************************/
+/* cd root */
+/**********************************************************/
+
+struct cmd_cd_root_result {
+ cmdline_fixed_string_t cd;
+ cmdline_fixed_string_t root;
+};
+
+static void cmd_cd_root_parsed(void *parsed_result,
+ struct cmdline *cl,
+ void *data)
+{
+ char prompt[RDLINE_PROMPT_SIZE];
+
+ conf_cur = conf_root;
+ snprintf(prompt, sizeof(prompt), "%s> ", conf_cur->name);
+ cmdline_set_prompt(cl, prompt);
+}
+
+cmdline_parse_token_string_t cmd_cd_root_cd =
+ TOKEN_STRING_INITIALIZER(struct cmd_cd_root_result, cd, "cd");
+cmdline_parse_token_string_t cmd_cd_root_root =
+ TOKEN_STRING_INITIALIZER(struct cmd_cd_root_result, root, "/");
+
+cmdline_parse_inst_t cmd_cd_root = {
+ .f = cmd_cd_root_parsed, /* function to call */
+ .data = NULL, /* 2nd arg of func */
+ .help_str = "<cd /> go to root of configuration",
+ .tokens = { /* token list, NULL terminated */
+ (void *)&cmd_cd_root_cd,
+ (void *)&cmd_cd_root_root,
+ NULL,
+ },
+};
+
+/**********************************************************/
+/* cd node */
+/**********************************************************/
+
+struct cmd_cd_node_result {
+ cmdline_fixed_string_t cd;
+ struct confnode *node;
+};
+
+static void cmd_cd_node_parsed(void *parsed_result,
+ struct cmdline *cl,
+ void *data)
+{
+ struct cmd_cd_node_result *res = parsed_result;
+ char prompt[RDLINE_PROMPT_SIZE];
+
+ if (TAILQ_NEXT(res->node, user_next)) {
+ printf("ambiguous directory\n");
+ return;
+ }
+ conf_cur = res->node;
+ snprintf(prompt, sizeof(prompt), "%s> ", conf_cur->name);
+ cmdline_set_prompt(cl, prompt);
+}
+
+cmdline_parse_token_string_t cmd_cd_node_cd =
+ TOKEN_STRING_INITIALIZER(struct cmd_cd_node_result, cd, "cd");
+parse_token_conf_node_t cmd_cd_node_node =
+ TOKEN_CONF_NODE_INITIALIZER(struct cmd_cd_node_result, node,
+ &conf_root, &conf_cur,
+ CONFNODE_F_IS_DIR,
+ CONFNODE_F_IS_DIR|CONFNODE_F_INVISIBLE);
+
+cmdline_parse_inst_t cmd_cd_node = {
+ .f = cmd_cd_node_parsed, /* function to call */
+ .data = NULL, /* 2nd arg of func */
+ .help_str = "<cd CONFNODE> go down in a configuration sub-tree",
+ .tokens = { /* token list, NULL terminated */
+ (void *)&cmd_cd_node_cd,
+ (void *)&cmd_cd_node_node,
+ NULL,
+ },
+};
+
+/**********************************************************/
+/* set bool node */
+/**********************************************************/
+
+struct cmd_set_bool_result {
+ cmdline_fixed_string_t set;
+ struct confnode *node;
+ cmdline_fixed_string_t val;
+};
+
+static void cmd_set_bool_parsed(void *parsed_result,
+ struct cmdline *cl,
+ void *data)
+{
+ struct cmd_set_bool_result *res = parsed_result;
+ struct confnode *n = res->node;
+ char value[BUFSIZ];
+
+ while (n) {
+ if (confnode_set_user_strvalue(n, res->val) < 0)
+ printf("Error, cannot set value\n");
+ else {
+ if (strcmp(res->val, "y") == 0)
+ confnode_check_deps(n, 1);
+ if (confnode_get_value(n, value,
+ sizeof(value)) < 0)
+ printf("Error, cannot re-read value\n");
+ }
+ printf("<%s> value set to %s\n", n->name, value);
+ n = TAILQ_NEXT(n, user_next);
+ }
+}
+
+cmdline_parse_token_string_t cmd_set_bool_set =
+ TOKEN_STRING_INITIALIZER(struct cmd_set_bool_result, set, "set");
+parse_token_conf_node_t cmd_set_bool_node =
+ TOKEN_CONF_NODE_INITIALIZER(struct cmd_set_bool_result, node,
+ &conf_root, &conf_cur,
+ CONFNODE_F_BOOL,
+ CONFNODE_F_BOOL|CONFNODE_F_INVISIBLE);
+cmdline_parse_token_string_t cmd_set_bool_val =
+ TOKEN_STRING_INITIALIZER(struct cmd_set_bool_result, val, "y#n");
+
+cmdline_parse_inst_t cmd_set_bool = {
+ .f = cmd_set_bool_parsed, /* function to call */
+ .data = NULL, /* 2nd arg of func */
+ .help_str = "<set CONFNODE y|n> set boolean value",
+ .tokens = { /* token list, NULL terminated */
+ (void *)&cmd_set_bool_set,
+ (void *)&cmd_set_bool_node,
+ (void *)&cmd_set_bool_val,
+ NULL,
+ },
+};
+
+/**********************************************************/
+/* set int node */
+/**********************************************************/
+
+struct cmd_set_int_result {
+ cmdline_fixed_string_t set;
+ struct confnode *node;
+ int32_t val;
+};
+
+static void cmd_set_int_parsed(void *parsed_result,
+ struct cmdline *cl,
+ void *data)
+{
+ struct cmd_set_int_result *res = parsed_result;
+ struct confnode *n = res->node;
+ char value[BUFSIZ];
+
+ while (n) {
+ snprintf(value, sizeof(value), "%"PRIi32, res->val);
+ if (confnode_set_user_strvalue(n, value) < 0)
+ printf("Error, cannot set value\n");
+ else {
+ if (res->val)
+ confnode_check_deps(n, 1);
+ if (confnode_get_value(n, value,
+ sizeof(value)) < 0)
+ printf("Error, cannot re-read value\n");
+ }
+ printf("<%s> value set to %s\n", n->name, value);
+ n = TAILQ_NEXT(n, user_next);
+ }
+}
+
+cmdline_parse_token_string_t cmd_set_int_set =
+ TOKEN_STRING_INITIALIZER(struct cmd_set_int_result, set, "set");
+parse_token_conf_node_t cmd_set_int_node =
+ TOKEN_CONF_NODE_INITIALIZER(struct cmd_set_int_result, node,
+ &conf_root, &conf_cur,
+ CONFNODE_F_INT,
+ CONFNODE_F_INT|CONFNODE_F_INVISIBLE);
+cmdline_parse_token_num_t cmd_set_int_val =
+ TOKEN_NUM_INITIALIZER(struct cmd_set_int_result, val, INT32);
+
+cmdline_parse_inst_t cmd_set_int = {
+ .f = cmd_set_int_parsed, /* function to call */
+ .data = NULL, /* 2nd arg of func */
+ .help_str = "<set CONFNODE INT> set integer value",
+ .tokens = { /* token list, NULL terminated */
+ (void *)&cmd_set_int_set,
+ (void *)&cmd_set_int_node,
+ (void *)&cmd_set_int_val,
+ NULL,
+ },
+};
+
+/**********************************************************/
+/* set str node */
+/**********************************************************/
+
+struct cmd_set_str_result {
+ cmdline_fixed_string_t set;
+ struct confnode *node;
+};
+
+static int get_str(struct cmdline *cl, char *buf, int len)
+{
+ struct rdline rl;
+ int ret = 0, n;
+ char c;
+ char *s;
+
+ buf[0] = '\0';
+
+ rdline_init(&rl, cmdline_write_char, NULL, NULL);
+ rl.opaque = cl;
+ rdline_newline(&rl, "edit> ");
+
+ while (ret == 0 || ret == 2) {
+ n = read(cl->s_in, &c, 1);
+ if (n <= 0)
+ return -1;
+ ret = rdline_char_in(&rl, c);
+ if (ret == 1) {
+ snprintf(buf, len, "%s", rdline_get_buffer(&rl));
+ s = strchr(buf, '\n');
+ if (s)
+ *s = '\0';
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+static void cmd_set_str_parsed(void *parsed_result,
+ struct cmdline *cl,
+ void *data)
+{
+ struct cmd_set_str_result *res = parsed_result;
+ struct confnode *n = res->node;
+ char value[BUFSIZ];
+
+ if (confnode_get_value(res->node, value, sizeof(value)) < 0) {
+ printf("Error, cannot read value\n");
+ return;
+ }
+ printf("Previous value is %s\n", value);
+ printf("type CTRL-d on an empty line to abort\n");
+ if (get_str(cl, value, sizeof(value))) {
+ printf("Aborted\n");
+ return;
+ }
+ while (n) {
+ snprintf(res->node->value, sizeof(res->node->value),
+ "%s", value);
+ if (strcmp(value, ""))
+ confnode_check_deps(n, 1);
+ if (confnode_get_value(n, value,
+ sizeof(value)) < 0)
+ printf("Error, cannot re-read value\n");
+ printf("<%s> value set to %s\n", n->name, value);
+ n = TAILQ_NEXT(n, user_next);
+ }
+}
+
+cmdline_parse_token_string_t cmd_set_str_set =
+ TOKEN_STRING_INITIALIZER(struct cmd_set_str_result, set, "set");
+parse_token_conf_node_t cmd_set_str_node =
+ TOKEN_CONF_NODE_INITIALIZER(struct cmd_set_str_result, node,
+ &conf_root, &conf_cur,
+ CONFNODE_F_STR,
+ CONFNODE_F_STR|CONFNODE_F_INVISIBLE);
+
+cmdline_parse_inst_t cmd_set_str = {
+ .f = cmd_set_str_parsed, /* function to call */
+ .data = NULL, /* 2nd arg of func */
+ .help_str = "<set CONFNODE> set str value",
+ .tokens = { /* token list, NULL terminated */
+ (void *)&cmd_set_str_set,
+ (void *)&cmd_set_str_node,
+ NULL,
+ },
+};
+
+/**********************************************************/
+/* show node */
+/**********************************************************/
+
+struct cmd_show_node_result {
+ cmdline_fixed_string_t show;
+ cmdline_fixed_string_t all;
+ struct confnode *node;
+};
+
+static void cmd_show_node_parsed(void *parsed_result,
+ struct cmdline *cl,
+ void *data)
+{
+ struct cmd_show_node_result *res = parsed_result;
+ struct confnode *n = res->node;
+ int details = 0;
+
+ if (data)
+ details = 1;
+
+ while (n) {
+ //dump_one_confnode(n);
+ n = TAILQ_NEXT(n, user_next);
+ }
+}
+
+cmdline_parse_token_string_t cmd_show_node_show =
+ TOKEN_STRING_INITIALIZER(struct cmd_show_node_result, show, "show");
+cmdline_parse_token_string_t cmd_show_node_all =
+ TOKEN_STRING_INITIALIZER(struct cmd_show_node_result, all, "-a");
+parse_token_conf_node_t cmd_show_node_node =
+ TOKEN_CONF_NODE_INITIALIZER(struct cmd_show_node_result, node,
+ &conf_root, &conf_cur, 0, CONFNODE_F_INVISIBLE);
+
+cmdline_parse_inst_t cmd_show_node = {
+ .f = cmd_show_node_parsed, /* function to call */
+ .data = NULL, /* 2nd arg of func */
+ .help_str = "<show CONFNODE> display infos on the config option",
+ .tokens = { /* token list, NULL terminated */
+ (void *)&cmd_show_node_show,
+ (void *)&cmd_show_node_node,
+ NULL,
+ },
+};
+
+cmdline_parse_inst_t cmd_show_node_a = {
+ .f = cmd_show_node_parsed, /* function to call */
+ .data = (void *)1, /* 2nd arg of func */
+ .help_str = "<show -a CONFNODE> display detailed infos on the config option",
+ .tokens = { /* token list, NULL terminated */
+ (void *)&cmd_show_node_show,
+ (void *)&cmd_show_node_all,
+ (void *)&cmd_show_node_node,
+ NULL,
+ },
+};
+
+/**********************************************************/
+/* load / load file */
+/**********************************************************/
+
+struct cmd_load_result {
+ cmdline_fixed_string_t load;
+ cmdline_fixed_string_t file;
+};
+
+static void cmd_load_parsed(void *parsed_result,
+ struct cmdline *cl,
+ void *data)
+{
+ char prompt[RDLINE_PROMPT_SIZE];
+ struct cmd_load_result *res = parsed_result;
+ const char *filename;
+
+ if (data)
+ filename = data;
+ else
+ filename = res->file;
+
+ conf_cur = conf_reset_and_read(conf_root, filename);
+ if (conf_root == NULL) {
+ printf("error loading <%s>\n", filename);
+ return;
+ }
+ conf_cur = conf_root;
+ snprintf(prompt, sizeof(prompt), "root> ");
+
+ printf("%s loaded\n", filename);
+}
+
+cmdline_parse_token_string_t cmd_load_load =
+ TOKEN_STRING_INITIALIZER(struct cmd_load_result, load, "load");
+
+cmdline_parse_inst_t cmd_load = {
+ .f = cmd_load_parsed, /* function to call */
+ .data = ".config", /* 2nd arg of func */
+ .help_str = "<load> reload the .config file",
+ .tokens = { /* token list, NULL terminated */
+ (void *)&cmd_load_load,
+ NULL,
+ },
+};
+
+cmdline_parse_token_string_t cmd_load_file =
+ TOKEN_STRING_INITIALIZER(struct cmd_load_result, file, NULL);
+
+cmdline_parse_inst_t cmd_loadfile = {
+ .f = cmd_load_parsed, /* function to call */
+ .data = NULL, /* 2nd arg of func */
+ .help_str = "<load FILE> load another .config file",
+ .tokens = { /* token list, NULL terminated */
+ (void *)&cmd_load_load,
+ (void *)&cmd_load_file,
+ NULL,
+ },
+};
+
+/**********************************************************/
+/* save / save file */
+/**********************************************************/
+
+struct cmd_save_result {
+ cmdline_fixed_string_t save;
+ cmdline_fixed_string_t file;
+};
+
+static void cmd_save_parsed(void *parsed_result,
+ struct cmdline *cl,
+ void *data)
+{
+ struct cmd_save_result *res = parsed_result;
+ const char *filename;
+
+ if (data)
+ filename = data;
+ else
+ filename = res->file;
+
+ if (dotconfig_write(filename, conf_root) < 0) {
+ printf("error saving <%s>\n", filename);
+ return;
+ }
+ printf("%s saved\n", filename);
+}
+
+cmdline_parse_token_string_t cmd_save_save =
+ TOKEN_STRING_INITIALIZER(struct cmd_save_result, save, "save");
+
+cmdline_parse_inst_t cmd_save = {
+ .f = cmd_save_parsed, /* function to call */
+ .data = ".config", /* 2nd arg of func */
+ .help_str = "<save> write the .config file",
+ .tokens = { /* token list, NULL terminated */
+ (void *)&cmd_save_save,
+ NULL,
+ },
+};
+
+cmdline_parse_token_string_t cmd_save_file =
+ TOKEN_STRING_INITIALIZER(struct cmd_save_result, file, NULL);
+
+cmdline_parse_inst_t cmd_savefile = {
+ .f = cmd_save_parsed, /* function to call */
+ .data = NULL, /* 2nd arg of func */
+ .help_str = "<save FILE> save in another .config file",
+ .tokens = { /* token list, NULL terminated */
+ (void *)&cmd_save_save,
+ (void *)&cmd_save_file,
+ NULL,
+ },
+};
+
+/**********************************************************/
+
+
+/**********************************************************/
+/**********************************************************/
+/****** CONTEXT (list of instruction) */
+
+/* in progmem */
+cmdline_parse_ctx_t main_ctx[] = {
+ (cmdline_parse_inst_t *)&cmd_ls,
+ (cmdline_parse_inst_t *)&cmd_ls_node,
+ (cmdline_parse_inst_t *)&cmd_pwd,
+ (cmdline_parse_inst_t *)&cmd_cd_prev,
+ (cmdline_parse_inst_t *)&cmd_cd_root,
+ (cmdline_parse_inst_t *)&cmd_cd_node,
+ (cmdline_parse_inst_t *)&cmd_set_bool,
+ (cmdline_parse_inst_t *)&cmd_set_int,
+ (cmdline_parse_inst_t *)&cmd_set_str,
+ (cmdline_parse_inst_t *)&cmd_show_node,
+ (cmdline_parse_inst_t *)&cmd_show_node_a,
+ (cmdline_parse_inst_t *)&cmd_load,
+ (cmdline_parse_inst_t *)&cmd_loadfile,
+ (cmdline_parse_inst_t *)&cmd_save,
+ (cmdline_parse_inst_t *)&cmd_savefile,
+ NULL,
+};
+