Initial import from http://www.droids-corp.org/hg/libcmdline/rev/db316e4289a1
authorOlivier Matz <zer0@droids-corp.org>
Fri, 24 Dec 2010 11:35:58 +0000 (12:35 +0100)
committerOlivier Matz <zer0@droids-corp.org>
Fri, 24 Dec 2010 11:35:58 +0000 (12:35 +0100)
Signed-off-by: Olivier Matz <zer0@droids-corp.org>
67 files changed:
Kconfig [new file with mode: 0644]
Kconfig2 [new file with mode: 0644]
Makefile [new file with mode: 0644]
TODO.txt [new file with mode: 0644]
build/calculator_server/Makefile [new file with mode: 0755]
build/calculator_standalone/Makefile [new file with mode: 0755]
build/client/Makefile [new file with mode: 0755]
build/extension_example/Makefile [new file with mode: 0755]
build/genconf/Makefile [new file with mode: 0644]
build/lib/Makefile [new file with mode: 0755]
src/calculator_server/commands.c [new file with mode: 0644]
src/calculator_server/main.c [new file with mode: 0644]
src/calculator_standalone/commands.c [new file with mode: 0644]
src/calculator_standalone/main.c [new file with mode: 0644]
src/client/main.c [new file with mode: 0644]
src/extension_example/commands.c [new file with mode: 0644]
src/extension_example/main.c [new file with mode: 0644]
src/extension_example/parse_obj_list.c [new file with mode: 0644]
src/extension_example/parse_obj_list.h [new file with mode: 0644]
src/genconf/commands.c [new file with mode: 0644]
src/genconf/conf_htable.c [new file with mode: 0644]
src/genconf/conf_htable.h [new file with mode: 0644]
src/genconf/conf_parser.c [new file with mode: 0644]
src/genconf/conf_parser.h [new file with mode: 0644]
src/genconf/confnode.c [new file with mode: 0644]
src/genconf/confnode.h [new file with mode: 0644]
src/genconf/confnode_choice.c [new file with mode: 0644]
src/genconf/confnode_choiceconfig.c [new file with mode: 0644]
src/genconf/confnode_comment.c [new file with mode: 0644]
src/genconf/confnode_config.c [new file with mode: 0644]
src/genconf/confnode_if.c [new file with mode: 0644]
src/genconf/confnode_intconfig.c [new file with mode: 0644]
src/genconf/confnode_menu.c [new file with mode: 0644]
src/genconf/confnode_menuconfig.c [new file with mode: 0644]
src/genconf/confnode_root.c [new file with mode: 0644]
src/genconf/confnode_strconfig.c [new file with mode: 0644]
src/genconf/dotconfig.c [new file with mode: 0644]
src/genconf/dotconfig.h [new file with mode: 0644]
src/genconf/expression.c [new file with mode: 0644]
src/genconf/expression.h [new file with mode: 0644]
src/genconf/main.c [new file with mode: 0644]
src/genconf/parse_confnode.c [new file with mode: 0644]
src/genconf/parse_confnode.h [new file with mode: 0644]
src/genconf/parser_common.c [new file with mode: 0644]
src/genconf/parser_common.h [new file with mode: 0644]
src/genconf/strictmalloc.h [new file with mode: 0644]
src/genconf/todo.txt [new file with mode: 0644]
src/lib/cmdline.c [new file with mode: 0644]
src/lib/cmdline.h [new file with mode: 0644]
src/lib/cmdline_cirbuf.c [new file with mode: 0644]
src/lib/cmdline_cirbuf.h [new file with mode: 0644]
src/lib/cmdline_parse.c [new file with mode: 0644]
src/lib/cmdline_parse.h [new file with mode: 0644]
src/lib/cmdline_parse_etheraddr.c [new file with mode: 0644]
src/lib/cmdline_parse_etheraddr.h [new file with mode: 0644]
src/lib/cmdline_parse_ipaddr.c [new file with mode: 0644]
src/lib/cmdline_parse_ipaddr.h [new file with mode: 0644]
src/lib/cmdline_parse_num.c [new file with mode: 0644]
src/lib/cmdline_parse_num.h [new file with mode: 0644]
src/lib/cmdline_parse_string.c [new file with mode: 0644]
src/lib/cmdline_parse_string.h [new file with mode: 0644]
src/lib/cmdline_rdline.c [new file with mode: 0644]
src/lib/cmdline_rdline.h [new file with mode: 0644]
src/lib/cmdline_socket.c [new file with mode: 0644]
src/lib/cmdline_socket.h [new file with mode: 0644]
src/lib/cmdline_vt100.c [new file with mode: 0644]
src/lib/cmdline_vt100.h [new file with mode: 0644]

diff --git a/Kconfig b/Kconfig
new file mode 100644 (file)
index 0000000..021e669
--- /dev/null
+++ b/Kconfig
@@ -0,0 +1,131 @@
+#########################
+
+# This is aversive2 configuration
+
+menu GENERAL
+       prompt "General configuration"
+
+config MATH_LIB
+       prompt "Include Math lib"
+       default n
+
+choice GENERAL_PRINTF_TYPE
+       prompt "Printf type"
+       default "standard"
+       ---help---
+       Choose printf style. The minimal printf cannot handle
+       floats. The advanced printf can, but it requires the math
+       lib. Disable it if you don't use printf, you'll win some prog
+       space.
+
+choiceconfig GENERAL_PRINTF_NONE
+       prompt "none"
+
+choiceconfig GENERAL_PRINTF_MINIMAL
+       prompt "minimal"
+
+choiceconfig GENERAL_PRINTF_STANDARD
+       prompt "standard"
+
+choiceconfig GENERAL_PRINTF_ADVANCED
+       prompt "advanced"
+
+endchoice # GENERAL_PRINTF_TYPE
+
+intconfig QUARTZ
+       prompt "Quartz Frequency (Hz)"
+       default 12000000
+       ---help---
+       Choose the speed of your target device. This frequency is
+       specified in Hertz.
+
+strconfig EXTRA_CFLAGS
+       prompt "Extra CFLAGS"
+       default ""
+       ---help---
+       These flags will be added when compiling sources.
+
+endmenu # GENERAL
+
+#########################
+
+config MODULES
+       prompt "Enable modules"
+       default y
+       ---help---
+       Enable modules
+
+#########################
+
+if MODULES
+
+menu BASE
+       prompt "Basic modules"
+       ---help---
+       Contains basic modules
+
+config BASE_CIRBUF
+       prompt 'Circular buffer'
+       default y
+       ---help---
+       This module provides a circular buffer implementation (fifo or
+       lifo).
+
+config BASE_CIRBUF_LARGE
+       prompt 'Large circular buffer'
+       requires BASE_CIRBUF
+       default n
+       ---help---
+       Allow buffer larger than 127
+
+menuconfig BASE_SCHEDULER
+       prompt "Scheduler"
+       ---help---
+       The 'scheduler' module is NOT a scheduler in the same way than
+       in a multitask kernel. This module allow to schedule functions
+       in the future (only a call, or periodical call). If
+       CONFIG_MODULE_SCHEDULER_USE_TIMERS option is not enabled, the
+       functions are called from TIMER0 interrupt, else you can
+       choose which timer to use. This module is able to handle
+       priority between events.
+
+config BASE_SCHEDULER_STATS
+       prompt "Enable debug statistics"
+
+config BASE_SCHEDULER_CREATE_CONFIG
+       prompt "Create Default scheduler config"
+
+choice BASE_SCHEDULER_MODE
+       prompt "Scheduler mode"
+       ---help---
+       Use either the hardware/timer module, the timer0 or a manual
+       call for the to call the scheduler. The generic timer module
+       support many archs and timers. If you don't enable it, you
+       must use TIMER0 overflow interrupt, and only some AVR are
+       supported, or you should can call the scheduler manually. In
+       this case, the SCHEDULER_UNIT macro has to be defined in
+       configuration file.
+
+choiceconfig BASE_SCHEDULER_MODE_MANUAL
+       prompt "manual"
+
+choiceconfig BASE_SCHEDULER_MODE_USE_TIMERS
+       prompt "timer_module"
+       requires HW_TIMER
+
+choiceconfig BASE_SCHEDULER_MODE_USE_TIMER0
+       prompt "use_timer0"
+
+endchoice # BASE_SCHEDULER_MODE
+
+endmenuconfig # BASE_SCHEDULER
+
+endmenu # BASE
+
+#########################
+
+source Kconfig2
+
+endif # MODULES
+
+#########################
diff --git a/Kconfig2 b/Kconfig2
new file mode 100644 (file)
index 0000000..9bdcfba
--- /dev/null
+++ b/Kconfig2
@@ -0,0 +1,39 @@
+
+menu COMM
+       prompt "Communication modules"
+       ---help---
+       Contains communication modules: uart, spi, i2c, ...
+
+comment "List of all drivers"
+
+config COMM_UART
+       prompt "Uart module"
+       requires BASE_CIRBUF
+       ---help---
+       This module provide functions to use the embedded UART or USART in
+       the AVR. With this module, it is more easy to configure it, and
+       data can be emited on interruption. The uart module also contains
+       a fifo for emission and reception (configurable in uart_config.h).
+
+menuconfig COMM_I2C
+       prompt "i2c"
+       ---help---
+       This module provide functions to use the embedded I2C (TWI
+       interface) in the AVR.
+
+config COMM_I2C_CREATE_CONFIG
+       prompt "Create Default i2c config"
+       ---help---
+       Create a i2c_config.h file if it does not exist, with a default
+       configuration.
+
+config COMM_I2C_MASTER
+       prompt "Allow master mode"
+
+config COMM_I2C_MULTIMASTER
+       prompt "Allow multimaster mode"
+       requires COMM_I2C_MASTER
+
+endmenuconfig # COMM_I2C
+
+endmenu # COMM
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..29d4861
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,34 @@
+BUILDDIR = $(PWD)/build
+SRCDIR = $(PWD)/src
+
+SUBDIRS  = lib
+SUBDIRS += extension_example
+SUBDIRS += calculator_standalone
+SUBDIRS += calculator_server
+SUBDIRS += client
+SUBDIRS += genconf
+
+# configuration for lib, not adviced to modify it except for
+# really small devices, because it changes the API.
+# CFLAGS += -DNO_RDLINE_HISTORY
+# CFLAGS += -DNO_RDLINE_KILL_BUF
+# CFLAGS += -DNO_PARSE_FLOAT
+
+CFLAGS += -Wall -Werror
+CFLAGS += -O0 -g
+
+CFLAGS += -I$(SRCDIR)/lib
+
+# needed if you want to parse float
+LDFLAGS += -lm
+
+
+export CFLAGS LDFLAGS
+
+_all: all
+
+all clean:
+       for i in $(SUBDIRS) ; do \
+               make -C ${BUILDDIR}/$$i \
+                       VPATH=${SRCDIR}/$$i $@ || exit $$? ; \
+       done
diff --git a/TODO.txt b/TODO.txt
new file mode 100644 (file)
index 0000000..2e8fe54
--- /dev/null
+++ b/TODO.txt
@@ -0,0 +1,7 @@
+- token file/path
+- libevent example
+- better example than calculator
+- check all XXX in code
+- set prompt example
+- too much possible completion
+- token string entre guillemets ?
diff --git a/build/calculator_server/Makefile b/build/calculator_server/Makefile
new file mode 100755 (executable)
index 0000000..523a8a8
--- /dev/null
@@ -0,0 +1,17 @@
+SRC = main.c commands.c
+
+OBJS = $(SRC:%.c=%.o)
+
+PROG = server
+LDLIB = ../lib/libcmdline.a
+
+all: $(PROG)
+
+clean:
+       rm -f $(PROG) $(OBJS)
+
+$(PROG): $(OBJS) $(LDLIB)
+       $(CC) $(LDFLAGS) -o $@ $(OBJS) $(LDLIB)
+
+%.o: %.c
+       $(CC) $(CFLAGS) -c $< -o $@
diff --git a/build/calculator_standalone/Makefile b/build/calculator_standalone/Makefile
new file mode 100755 (executable)
index 0000000..523a8a8
--- /dev/null
@@ -0,0 +1,17 @@
+SRC = main.c commands.c
+
+OBJS = $(SRC:%.c=%.o)
+
+PROG = server
+LDLIB = ../lib/libcmdline.a
+
+all: $(PROG)
+
+clean:
+       rm -f $(PROG) $(OBJS)
+
+$(PROG): $(OBJS) $(LDLIB)
+       $(CC) $(LDFLAGS) -o $@ $(OBJS) $(LDLIB)
+
+%.o: %.c
+       $(CC) $(CFLAGS) -c $< -o $@
diff --git a/build/client/Makefile b/build/client/Makefile
new file mode 100755 (executable)
index 0000000..57a440f
--- /dev/null
@@ -0,0 +1,16 @@
+SRC = main.c
+
+OBJS = $(SRC:%.c=%.o)
+
+PROG = client
+
+all: $(PROG)
+
+clean:
+       rm -f $(PROG) $(OBJS)
+
+$(PROG): $(OBJS) $(LDLIB)
+       $(CC) $(LDFLAGS) -o $@ $(OBJS)
+
+%.o: %.c
+       $(CC) $(CFLAGS) -c $< -o $@
diff --git a/build/extension_example/Makefile b/build/extension_example/Makefile
new file mode 100755 (executable)
index 0000000..6839965
--- /dev/null
@@ -0,0 +1,17 @@
+SRC = main.c commands.c parse_obj_list.c
+
+OBJS = $(SRC:%.c=%.o)
+
+PROG = extension_example
+LDLIB = ../lib/libcmdline.a
+
+all: $(PROG)
+
+clean:
+       rm -f $(PROG) $(OBJS)
+
+$(PROG): $(OBJS) $(LDLIB)
+       $(CC) $(LDFLAGS) -o $@ $(OBJS) $(LDLIB)
+
+%.o: %.c
+       $(CC) $(CFLAGS) -c $< -o $@
diff --git a/build/genconf/Makefile b/build/genconf/Makefile
new file mode 100644 (file)
index 0000000..3e98788
--- /dev/null
@@ -0,0 +1,22 @@
+SRC := conf_parser.c conf_htable.c expression.c parser_common.c dotconfig.c
+SRC += main.c parse_confnode.c commands.c
+SRC += confnode.c confnode_comment.c confnode_config.c
+SRC += confnode_menu.c confnode_menuconfig.c confnode_if.c
+SRC += confnode_choice.c confnode_intconfig.c confnode_strconfig.c
+SRC += confnode_choiceconfig.c confnode_root.c
+
+OBJS = $(SRC:%.c=%.o)
+
+PROG = genconf
+LDLIB = ../lib/libcmdline.a
+
+all: $(PROG)
+
+clean:
+       rm -f $(PROG) $(OBJS)
+
+$(PROG): $(OBJS) $(LDLIB)
+       $(CC) $(LDFLAGS) -o $@ $(OBJS) $(LDLIB)
+
+%.o: %.c
+       $(CC) $(CFLAGS) -c $< -o $@
diff --git a/build/lib/Makefile b/build/lib/Makefile
new file mode 100755 (executable)
index 0000000..882b67c
--- /dev/null
@@ -0,0 +1,22 @@
+SRC  = cmdline_vt100.c cmdline_cirbuf.c
+SRC += cmdline.c cmdline_socket.c
+SRC += cmdline_parse.c cmdline_parse_num.c
+SRC += cmdline_parse_string.c cmdline_rdline.c
+SRC += cmdline_parse_ipaddr.c
+SRC += cmdline_parse_etheraddr.c
+
+OBJS = $(SRC:%.c=%.o)
+
+LIB = libcmdline.a
+
+all: $(LIB)
+
+clean:
+       rm -f $(OBJS) $(LIB)
+
+$(LIB): $(OBJS)
+       $(AR) cru $(LIB) $(OBJS)
+       ranlib $(LIB)
+
+%.o: %.c
+       $(CC) $(CFLAGS) -c $< -o $@
diff --git a/src/calculator_server/commands.c b/src/calculator_server/commands.c
new file mode 100644 (file)
index 0000000..be00d5a
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+ * 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 <stdint.h>
+#include <string.h>
+#include <netinet/in.h>
+
+#include <cmdline_parse.h>
+#include <cmdline_parse_num.h>
+#include <cmdline_parse_string.h>
+#include <cmdline.h>
+
+
+
+/**********************************************************/
+/* operations on float */
+
+/* this structure is filled when cmd_float is parsed successfully */
+struct cmd_float_result {
+       float a;
+       cmdline_fixed_string_t op;
+       float b;
+};
+
+/* function called when cmd_float is parsed successfully */
+static void cmd_float_parsed(void *parsed_result, struct cmdline *cl, void *data)
+{
+       struct cmd_float_result *cmd = (struct cmd_float_result *) parsed_result;
+       float res=0.;
+
+       switch(cmd->op[0]) {
+       case '+': res = cmd->a + cmd->b; break;
+       case '-': res = cmd->a - cmd->b; break;
+       case '*': res = cmd->a * cmd->b; break;
+       case '/': res = cmd->a / cmd->b; break;
+       default: break;
+       }
+       cmdline_printf(cl, "%f\n", res);
+}
+
+cmdline_parse_token_num_t cmd_float_a = TOKEN_NUM_INITIALIZER(struct cmd_float_result, a, FLOAT);
+cmdline_parse_token_string_t cmd_float_op = TOKEN_STRING_INITIALIZER(struct cmd_float_result, op, "+#-#*#/");
+cmdline_parse_token_num_t cmd_float_b = TOKEN_NUM_INITIALIZER(struct cmd_float_result, b, FLOAT);
+
+cmdline_parse_inst_t cmd_float = {
+       .f = cmd_float_parsed,  /* function to call */
+       .data = NULL,      /* 2nd arg of func */
+       .help_str = "Operation on float (ex: '2 + 5.4')",
+       .tokens = {        /* token list, NULL terminated */
+               (void *)&cmd_float_a, 
+               (void *)&cmd_float_op, 
+               (void *)&cmd_float_b, 
+               NULL,
+       },
+};
+
+
+/**********************************************************/
+/* operations trigo */
+
+/* this structure is filled when cmd_trigo is parsed successfully */
+struct cmd_trigo_result {
+       cmdline_fixed_string_t op;
+       float a;
+};
+
+/* function called when cmd_trigo is parsed successfully */
+static void cmd_trigo_parsed(void *parsed_result, struct cmdline *cl, void *data)
+{
+       struct cmd_trigo_result *cmd = (struct cmd_trigo_result *) parsed_result;
+       float res=0.;
+       
+       if (!strcmp(cmd->op, "sin")) {
+               res = sin(cmd->a);
+       }
+
+       else if (!strcmp(cmd->op, "cos")) {
+               res = cos(cmd->a);
+       }
+
+       else if (!strcmp(cmd->op, "tan")) {
+               res = tan(cmd->a);
+       }
+
+       cmdline_printf(cl, "%f\n", res);
+}
+
+cmdline_parse_token_string_t cmd_trigo_op = TOKEN_STRING_INITIALIZER(struct cmd_trigo_result, op, "sin#tan#cos");
+cmdline_parse_token_num_t cmd_trigo_a = TOKEN_NUM_INITIALIZER(struct cmd_trigo_result, a, FLOAT);
+
+cmdline_parse_inst_t cmd_trigo = {
+       .f = cmd_trigo_parsed,  /* function to call */
+       .data = NULL,      /* 2nd arg of func */
+       .help_str = "Trigonometric operations (ex: 'sin 2.03')",
+       .tokens = {        /* token list, NULL terminated */
+               (void *)&cmd_trigo_op, 
+               (void *)&cmd_trigo_a, 
+               NULL,
+       },
+};
+
+
+/**********************************************************/
+/* Help */
+
+/* this structure is filled when cmd_help is parsed successfully */
+struct cmd_help_result {
+       cmdline_fixed_string_t arg0;
+};
+
+/* function called when cmd_help is parsed successfully */
+static void cmd_help_parsed(void *parsed_result, struct cmdline *cl, void *data)
+{
+       cmdline_printf(cl, "== Simple calculator program ==\n"
+                      "You can do simple operations on floats, like '1 + 3'\n"
+                      "or '4.4 * 2.' (space is important).\n"
+                      "Some trigonometric operations are available, like\n"
+                      "'sin 4.5'.\n");
+}
+
+cmdline_parse_token_string_t cmd_help_arg0 = TOKEN_STRING_INITIALIZER(struct cmd_help_result, arg0, "help");
+
+cmdline_parse_inst_t cmd_help = {
+       .f = cmd_help_parsed,  /* function to call */
+       .data = NULL,      /* 2nd arg of func */
+       .help_str = "Display help",
+       .tokens = {        /* token list, NULL terminated */
+               (void *)&cmd_help_arg0, 
+               NULL,
+       },
+};
+
+
+
+
+/**********************************************************/
+/**********************************************************/
+/****** CONTEXT (list of instruction) */
+
+/* in progmem */
+cmdline_parse_ctx_t main_ctx[] = {
+       (cmdline_parse_inst_t *)&cmd_float, 
+       (cmdline_parse_inst_t *)&cmd_trigo, 
+       (cmdline_parse_inst_t *)&cmd_help, 
+       NULL,
+};
+
diff --git a/src/calculator_server/main.c b/src/calculator_server/main.c
new file mode 100644 (file)
index 0000000..77fbe28
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * 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 <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <math.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <inttypes.h>
+#include <ctype.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <termios.h>
+#include <netinet/in.h>
+
+#include <cmdline_rdline.h>
+#include <cmdline_parse.h>
+#include <cmdline_socket.h>
+#include <cmdline.h>
+
+extern cmdline_parse_ctx_t main_ctx[];
+
+struct cmdline *cl;
+
+/*** main */
+
+void basic_char_loop(void)
+{
+       char c;
+
+       c = -1;
+       while (1) {
+               read(cl->s_in, &c, 1);
+               if (cmdline_in(cl, &c, 1) < 0)
+                       break;
+       }
+       cmdline_free(cl);
+       printf("\n");
+}
+
+/* #define STANDALONE */
+
+int main(void) 
+{
+       int s;
+       s = cmdline_tcpv4_listen(INADDR_ANY, 1234);
+       cl = cmdline_accept(main_ctx, "example> ", s);
+       basic_char_loop();
+       return 0;
+}
+
+
diff --git a/src/calculator_standalone/commands.c b/src/calculator_standalone/commands.c
new file mode 100644 (file)
index 0000000..be00d5a
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+ * 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 <stdint.h>
+#include <string.h>
+#include <netinet/in.h>
+
+#include <cmdline_parse.h>
+#include <cmdline_parse_num.h>
+#include <cmdline_parse_string.h>
+#include <cmdline.h>
+
+
+
+/**********************************************************/
+/* operations on float */
+
+/* this structure is filled when cmd_float is parsed successfully */
+struct cmd_float_result {
+       float a;
+       cmdline_fixed_string_t op;
+       float b;
+};
+
+/* function called when cmd_float is parsed successfully */
+static void cmd_float_parsed(void *parsed_result, struct cmdline *cl, void *data)
+{
+       struct cmd_float_result *cmd = (struct cmd_float_result *) parsed_result;
+       float res=0.;
+
+       switch(cmd->op[0]) {
+       case '+': res = cmd->a + cmd->b; break;
+       case '-': res = cmd->a - cmd->b; break;
+       case '*': res = cmd->a * cmd->b; break;
+       case '/': res = cmd->a / cmd->b; break;
+       default: break;
+       }
+       cmdline_printf(cl, "%f\n", res);
+}
+
+cmdline_parse_token_num_t cmd_float_a = TOKEN_NUM_INITIALIZER(struct cmd_float_result, a, FLOAT);
+cmdline_parse_token_string_t cmd_float_op = TOKEN_STRING_INITIALIZER(struct cmd_float_result, op, "+#-#*#/");
+cmdline_parse_token_num_t cmd_float_b = TOKEN_NUM_INITIALIZER(struct cmd_float_result, b, FLOAT);
+
+cmdline_parse_inst_t cmd_float = {
+       .f = cmd_float_parsed,  /* function to call */
+       .data = NULL,      /* 2nd arg of func */
+       .help_str = "Operation on float (ex: '2 + 5.4')",
+       .tokens = {        /* token list, NULL terminated */
+               (void *)&cmd_float_a, 
+               (void *)&cmd_float_op, 
+               (void *)&cmd_float_b, 
+               NULL,
+       },
+};
+
+
+/**********************************************************/
+/* operations trigo */
+
+/* this structure is filled when cmd_trigo is parsed successfully */
+struct cmd_trigo_result {
+       cmdline_fixed_string_t op;
+       float a;
+};
+
+/* function called when cmd_trigo is parsed successfully */
+static void cmd_trigo_parsed(void *parsed_result, struct cmdline *cl, void *data)
+{
+       struct cmd_trigo_result *cmd = (struct cmd_trigo_result *) parsed_result;
+       float res=0.;
+       
+       if (!strcmp(cmd->op, "sin")) {
+               res = sin(cmd->a);
+       }
+
+       else if (!strcmp(cmd->op, "cos")) {
+               res = cos(cmd->a);
+       }
+
+       else if (!strcmp(cmd->op, "tan")) {
+               res = tan(cmd->a);
+       }
+
+       cmdline_printf(cl, "%f\n", res);
+}
+
+cmdline_parse_token_string_t cmd_trigo_op = TOKEN_STRING_INITIALIZER(struct cmd_trigo_result, op, "sin#tan#cos");
+cmdline_parse_token_num_t cmd_trigo_a = TOKEN_NUM_INITIALIZER(struct cmd_trigo_result, a, FLOAT);
+
+cmdline_parse_inst_t cmd_trigo = {
+       .f = cmd_trigo_parsed,  /* function to call */
+       .data = NULL,      /* 2nd arg of func */
+       .help_str = "Trigonometric operations (ex: 'sin 2.03')",
+       .tokens = {        /* token list, NULL terminated */
+               (void *)&cmd_trigo_op, 
+               (void *)&cmd_trigo_a, 
+               NULL,
+       },
+};
+
+
+/**********************************************************/
+/* Help */
+
+/* this structure is filled when cmd_help is parsed successfully */
+struct cmd_help_result {
+       cmdline_fixed_string_t arg0;
+};
+
+/* function called when cmd_help is parsed successfully */
+static void cmd_help_parsed(void *parsed_result, struct cmdline *cl, void *data)
+{
+       cmdline_printf(cl, "== Simple calculator program ==\n"
+                      "You can do simple operations on floats, like '1 + 3'\n"
+                      "or '4.4 * 2.' (space is important).\n"
+                      "Some trigonometric operations are available, like\n"
+                      "'sin 4.5'.\n");
+}
+
+cmdline_parse_token_string_t cmd_help_arg0 = TOKEN_STRING_INITIALIZER(struct cmd_help_result, arg0, "help");
+
+cmdline_parse_inst_t cmd_help = {
+       .f = cmd_help_parsed,  /* function to call */
+       .data = NULL,      /* 2nd arg of func */
+       .help_str = "Display help",
+       .tokens = {        /* token list, NULL terminated */
+               (void *)&cmd_help_arg0, 
+               NULL,
+       },
+};
+
+
+
+
+/**********************************************************/
+/**********************************************************/
+/****** CONTEXT (list of instruction) */
+
+/* in progmem */
+cmdline_parse_ctx_t main_ctx[] = {
+       (cmdline_parse_inst_t *)&cmd_float, 
+       (cmdline_parse_inst_t *)&cmd_trigo, 
+       (cmdline_parse_inst_t *)&cmd_help, 
+       NULL,
+};
+
diff --git a/src/calculator_standalone/main.c b/src/calculator_standalone/main.c
new file mode 100644 (file)
index 0000000..de5a752
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * 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 <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <math.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <inttypes.h>
+#include <ctype.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <termios.h>
+#include <netinet/in.h>
+
+#include <cmdline_rdline.h>
+#include <cmdline_parse.h>
+#include <cmdline_socket.h>
+#include <cmdline.h>
+
+/* XXX */
+struct my_cmdline {
+       struct rdline rdl;
+       char prompt[RDLINE_PROMPT_SIZE];
+} cmd;
+
+extern cmdline_parse_ctx_t main_ctx[];
+
+struct cmdline *cl;
+
+/*** main */
+
+void basic_char_loop(void)
+{
+       char c;
+
+       c = -1;
+       while (1) {
+               read(cl->s_in, &c, 1);
+               if (cmdline_in(cl, &c, 1) < 0)
+                       break;
+       }
+       cmdline_free(cl);
+       printf("\n");
+}
+
+/* #define STANDALONE */
+
+int main(void) 
+{
+       struct termios oldterm, term;
+
+       tcgetattr(0, &oldterm);
+       memcpy(&term, &oldterm, sizeof(term));
+       term.c_lflag &= ~(ICANON | ECHO | ISIG);
+       tcsetattr(0, TCSANOW, &term);
+       setbuf(stdin, NULL);
+       cl = cmdline_stdin_new(main_ctx, "calc> ");
+       basic_char_loop();
+       tcsetattr(0, TCSANOW, &oldterm);
+       return 0;
+}
+
+
diff --git a/src/client/main.c b/src/client/main.c
new file mode 100644 (file)
index 0000000..5f177d2
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * 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 <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <math.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+#include <unistd.h>
+#include <termios.h>
+
+
+int main(void) 
+{
+       struct termios oldterm, term;
+       struct sockaddr_in sin_ci;
+       fd_set fds;
+       int s;
+       char c;
+
+       s = socket(PF_INET, SOCK_STREAM, 0);
+       if (s < 0) {
+               printf("socket() failed\n");
+               return s;
+       }
+
+       /* XXX specify address */
+       memset(&sin_ci, 0, sizeof(sin_ci));
+       sin_ci.sin_family = AF_INET;
+       inet_pton(AF_INET, "127.0.0.1", &sin_ci.sin_addr.s_addr);
+       sin_ci.sin_port = htons(1234);
+#ifndef __linux__
+       sin_ci.sin_len = sizeof(sin_ci);
+#endif
+
+       if (connect(s, (struct sockaddr *)&sin_ci, sizeof(sin_ci)) < 0) {
+               printf("connect() failed\n");
+               return s;
+       }
+
+       tcgetattr(0, &oldterm);
+       memcpy(&term, &oldterm, sizeof(term));
+       term.c_lflag &= ~(ICANON | ECHO | ISIG);
+       tcsetattr(0, TCSANOW, &term);
+       setbuf(stdin, NULL);
+
+       while (1) {
+               FD_ZERO(&fds);
+               FD_SET(0, &fds);
+               FD_SET(s, &fds);
+               if (select(s+1, &fds, NULL, NULL, NULL) < 0)
+                       break;
+               if (FD_ISSET(0, &fds)) {
+                       if (read(0, &c, 1) <= 0)
+                               break;
+                       write(s, &c, 1);
+               }
+               if (FD_ISSET(s, &fds)) {
+                       if (read(s, &c, 1) <= 0)
+                               break;
+                       write(1, &c, 1);
+               }
+       }
+       tcsetattr(0, TCSANOW, &oldterm);
+       printf("\n");
+
+       return 0;
+}
+
+
diff --git a/src/extension_example/commands.c b/src/extension_example/commands.c
new file mode 100644 (file)
index 0000000..fbef8c9
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ * 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 <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <cmdline_parse.h>
+#include <cmdline_parse_ipaddr.h>
+#include <cmdline_parse_num.h>
+#include <cmdline_parse_string.h>
+#include <cmdline.h>
+
+#include "parse_obj_list.h"
+
+struct object_list global_obj_list;
+
+/**********************************************************/
+
+struct cmd_obj_del_show_result {
+       cmdline_fixed_string_t action;
+       struct object *obj;
+};
+
+static void cmd_obj_del_show_parsed(void *parsed_result, 
+                               struct cmdline *cl,
+                               void *data)
+{
+       struct cmd_obj_del_show_result *res = parsed_result;
+       char ip_str[INET6_ADDRSTRLEN];
+       
+       inet_ntop(res->obj->ip.family, (void *)&res->obj->ip.addr,
+                 ip_str, sizeof(ip_str));
+
+       if (strcmp(res->action, "del") == 0) {
+               SLIST_REMOVE(&global_obj_list, res->obj, object, next);
+               cmdline_printf(cl, "Object %s removed, ip=%s\r\n", 
+                              res->obj->name, ip_str);
+               free(res->obj);
+       }
+       else if (strcmp(res->action, "show") == 0) {
+               cmdline_printf(cl, "Object %s, ip=%s\r\n", 
+                              res->obj->name, ip_str);
+       }
+}
+
+cmdline_parse_token_string_t cmd_obj_action = TOKEN_STRING_INITIALIZER(struct cmd_obj_del_show_result, action, "show#del");
+parse_token_obj_list_t cmd_obj_obj = TOKEN_OBJ_LIST_INITIALIZER(struct cmd_obj_del_show_result, obj, &global_obj_list);
+
+cmdline_parse_inst_t cmd_obj_del_show = {
+       .f = cmd_obj_del_show_parsed,  /* function to call */
+       .data = NULL,      /* 2nd arg of func */
+       .help_str = "Show/del an object",
+       .tokens = {        /* token list, NULL terminated */
+               (void *)&cmd_obj_action, 
+               (void *)&cmd_obj_obj, 
+               NULL,
+       },
+};
+
+/**********************************************************/
+
+struct cmd_obj_add_result {
+       cmdline_fixed_string_t action;
+       cmdline_fixed_string_t name;
+       cmdline_ipaddr_t ip;
+};
+
+static void cmd_obj_add_parsed(void *parsed_result, 
+                               struct cmdline *cl,
+                               void *data)
+{
+       struct cmd_obj_add_result *res = parsed_result;
+       struct object *o;
+       char ip_str[INET6_ADDRSTRLEN];
+       
+       SLIST_FOREACH(o, &global_obj_list, next) {
+               if (!strcmp(res->name, o->name)) {
+                       cmdline_printf(cl, "Object %s already exist\n", res->name);
+                       return;
+               }
+               break;
+       }
+
+       o = malloc(sizeof(*o));
+       if (!o) {
+               cmdline_printf(cl, "mem error\r\n");
+               return;
+       }
+       strcpy(o->name, res->name);
+       o->ip = res->ip;
+       SLIST_INSERT_HEAD(&global_obj_list, o, next);
+       inet_ntop(o->ip.family, (void *)&o->ip.addr,
+                 ip_str, sizeof(ip_str));
+       cmdline_printf(cl, "Object %s added, ip=%s\r\n", 
+                      o->name, ip_str);
+}
+
+cmdline_parse_token_string_t cmd_obj_action_add = TOKEN_STRING_INITIALIZER(struct cmd_obj_add_result, action, "add");
+cmdline_parse_token_string_t cmd_obj_name = TOKEN_STRING_INITIALIZER(struct cmd_obj_add_result, name, NULL);
+cmdline_parse_token_ipaddr_t cmd_obj_ip = TOKEN_IPADDR_INITIALIZER(struct cmd_obj_add_result, ip);
+
+cmdline_parse_inst_t cmd_obj_add = {
+       .f = cmd_obj_add_parsed,  /* function to call */
+       .data = NULL,      /* 2nd arg of func */
+       .help_str = "Add an object (name, val)",
+       .tokens = {        /* token list, NULL terminated */
+               (void *)&cmd_obj_action_add, 
+               (void *)&cmd_obj_name, 
+               (void *)&cmd_obj_ip,
+               NULL,
+       },
+};
+
+
+/**********************************************************/
+/**********************************************************/
+/****** CONTEXT (list of instruction) */
+
+/* in progmem */
+cmdline_parse_ctx_t main_ctx[] = {
+       (cmdline_parse_inst_t *)&cmd_obj_del_show, 
+       (cmdline_parse_inst_t *)&cmd_obj_add, 
+       NULL,
+};
+
diff --git a/src/extension_example/main.c b/src/extension_example/main.c
new file mode 100644 (file)
index 0000000..9cf1371
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * 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 <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+#include <math.h>
+
+#include <netinet/in.h>
+
+#include <cmdline_rdline.h>
+#include <cmdline_parse.h>
+
+struct my_cmdline {
+       struct rdline rdl;
+       char prompt[RDLINE_PROMPT_SIZE];
+} cmd;
+
+extern cmdline_parse_ctx_t main_ctx[];
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <inttypes.h>
+#include <ctype.h>
+
+#include <unistd.h>
+#include <termios.h>
+
+#include <cmdline.h>
+#include <cmdline_socket.h>
+
+struct cmdline *cl;
+
+/*** main */
+
+void basic_char_loop(void)
+{
+       char c;
+
+       c = -1;
+       while (1) {
+               read(cl->s_in, &c, 1);
+               if (cmdline_in(cl, &c, 1) < 0) {
+                       break;
+               }
+       }
+       cmdline_free(cl);
+       printf("\n");
+}
+
+int main(void) 
+{
+       struct termios oldterm, term;
+
+       tcgetattr(0, &oldterm);
+       memcpy(&term, &oldterm, sizeof(term));
+       term.c_lflag &= ~(ICANON | ECHO | ISIG);
+       tcsetattr(0, TCSANOW, &term);
+       setbuf(stdin, NULL);
+
+       cl = cmdline_stdin_new(main_ctx, "example> ");
+       basic_char_loop();
+
+       tcsetattr(fileno(stdin), TCSANOW, &oldterm);
+       return 0;
+}
+
+
diff --git a/src/extension_example/parse_obj_list.c b/src/extension_example/parse_obj_list.c
new file mode 100644 (file)
index 0000000..0781987
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * 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 <stdio.h>
+#include <inttypes.h>
+#include <ctype.h>
+#include <string.h>
+#include <netinet/in.h>
+
+#include <cmdline_parse_ipaddr.h>
+#include <cmdline_parse.h>
+
+#include "parse_obj_list.h"
+
+/* This file is an example of extension of libcmdline. It provides an
+ * example of objects stored in a list. */
+
+struct cmdline_token_ops token_obj_list_ops = {
+       .parse = parse_obj_list,
+       .complete_get_nb = complete_get_nb_obj_list,
+       .complete_get_elt = complete_get_elt_obj_list,
+       .get_help = get_help_obj_list,
+};
+
+int 
+parse_obj_list(cmdline_parse_token_hdr_t *tk, const char *buf, void *res)
+{
+       struct token_obj_list *tk2 = (struct token_obj_list *)tk;
+       struct token_obj_list_data *tkd = &tk2->obj_list_data;
+       struct object *o;
+       unsigned int token_len = 0;
+
+       if (*buf == 0)
+               return -1;
+
+       while(!cmdline_isendoftoken(buf[token_len]))
+               token_len++;
+
+       SLIST_FOREACH(o, tkd->list, next) {
+               if (token_len != strlen(o->name))
+                       continue;
+               if (strncmp(buf, o->name, token_len))
+                       continue;
+               break;
+       }
+       if (!o) /* not found */
+               return -1;
+        
+       /* store the address of object in structure */
+       if (res)
+               *(struct object **)res = o;
+
+        return token_len;
+}
+
+int complete_get_nb_obj_list(cmdline_parse_token_hdr_t *tk)
+{
+       struct token_obj_list *tk2 = (struct token_obj_list *)tk;
+       struct token_obj_list_data *tkd = &tk2->obj_list_data;
+       struct object *o;
+       int ret = 0;
+
+       SLIST_FOREACH(o, tkd->list, next) {
+               ret ++;
+       }
+       return ret;
+}
+
+int complete_get_elt_obj_list(cmdline_parse_token_hdr_t *tk, int idx, 
+                             char *dstbuf, unsigned int size)
+{
+       struct token_obj_list *tk2 = (struct token_obj_list *)tk;
+       struct token_obj_list_data *tkd = &tk2->obj_list_data;
+       struct object *o;
+       unsigned int i = 0, len;
+
+       SLIST_FOREACH(o, tkd->list, next) {
+               if (i++ == idx)
+                       break;
+       }
+       if (!o)
+               return -1;
+
+       len = strlen(o->name) + 1;
+       if (len > size)
+               return -1;
+
+       strcpy(dstbuf, o->name);
+       return 0;
+}
+
+
+int get_help_obj_list(cmdline_parse_token_hdr_t *tk, char *dstbuf, unsigned int size)
+{
+       snprintf(dstbuf, size, "Obj-List");
+       return 0;
+}
diff --git a/src/extension_example/parse_obj_list.h b/src/extension_example/parse_obj_list.h
new file mode 100644 (file)
index 0000000..1aeb7eb
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * 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.
+ */
+
+#ifndef _PARSE_OBJ_LIST_H_
+#define _PARSE_OBJ_LIST_H_
+
+/* This file is an example of extension of libcmdline. It provides an
+ * example of objects stored in a list. */
+
+#include <sys/queue.h>
+#include <cmdline_parse.h>
+
+struct object {
+       SLIST_ENTRY(object) next;
+       char name[64];
+       cmdline_ipaddr_t ip;
+};
+
+/* define struct object_list */
+SLIST_HEAD(object_list, object);
+
+/* data is a pointer to a list */
+struct token_obj_list_data {
+       struct object_list *list;
+};
+
+struct token_obj_list {
+       struct cmdline_token_hdr hdr;
+       struct token_obj_list_data obj_list_data;
+};
+typedef struct token_obj_list parse_token_obj_list_t;
+
+extern struct cmdline_token_ops token_obj_list_ops;
+
+int parse_obj_list(cmdline_parse_token_hdr_t *tk, const char *srcbuf, void *res);
+int complete_get_nb_obj_list(cmdline_parse_token_hdr_t *tk);
+int complete_get_elt_obj_list(cmdline_parse_token_hdr_t *tk, int idx, 
+                             char *dstbuf, unsigned int size);
+int get_help_obj_list(cmdline_parse_token_hdr_t *tk, char *dstbuf, unsigned int size);
+
+#define TOKEN_OBJ_LIST_INITIALIZER(structure, field, obj_list_ptr)  \
+{                                                                  \
+       .hdr = {                                                    \
+               .ops = &token_obj_list_ops,                         \
+               .offset = offsetof(structure, field),               \
+       },                                                          \
+               .obj_list_data = {                                  \
+               .list = obj_list_ptr,                               \
+       },                                                          \
+}
+
+#endif /* _PARSE_OBJ_LIST_H_ */
diff --git a/src/genconf/commands.c b/src/genconf/commands.c
new file mode 100644 (file)
index 0000000..7978e70
--- /dev/null
@@ -0,0 +1,686 @@
+/*
+ * 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,
+};
+
diff --git a/src/genconf/conf_htable.c b/src/genconf/conf_htable.c
new file mode 100644 (file)
index 0000000..1a34022
--- /dev/null
@@ -0,0 +1,73 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <sys/queue.h>
+
+#include "expression.h"
+#include "conf_parser.h"
+#include "confnode.h"
+
+#define HASHTABLE_ORDER 10
+#define HASHTABLE_SIZE  (1<<HASHTABLE_ORDER)
+#define HASHTABLE_MASK  (HASHTABLE_ORDER-1)
+
+static struct confnode_list conf_htable[HASHTABLE_SIZE];
+
+/* return the hash from name */
+static inline uint32_t hash_name(const char *name)
+{
+        uint32_t h = 0;
+        const unsigned char *c;
+
+        for (c = (const unsigned char *)name; *c; c++)
+                h += ((((uint32_t)*c) << 4) + (((uint32_t)*c) >> 4)) * 11;
+
+        return h & HASHTABLE_MASK;
+}
+
+/*
+ * Return the configuration node associated to the given name. If not
+ * found, return NULL.
+ */
+struct confnode *conf_htable_lookup(const char *name)
+{
+       struct confnode *c;
+       uint32_t h = hash_name(name);
+
+       TAILQ_FOREACH(c, &conf_htable[h], hnext) {
+               if (!strncmp(name, c->name, sizeof(c->name)))
+                       break;
+       }
+       return c;
+}
+
+/*
+ * Parse a configuration tree and add all the nodes in the hashtable.
+ */
+int conf_htable_fill(struct confnode *root)
+{
+       struct confnode *c;
+       uint32_t h = hash_name(root->name);
+
+       TAILQ_INSERT_TAIL(&conf_htable[h], root, hnext);
+
+       TAILQ_FOREACH(c, &root->children, next) {
+               conf_htable_fill(c);
+       }
+       return 0;
+}
+
+/*
+ * Initialize the htable to be empty.
+ */
+int conf_htable_init(void)
+{
+       unsigned i;
+
+       for (i=0; i<HASHTABLE_SIZE; i++) {
+               TAILQ_INIT(&conf_htable[i]);
+       }
+       return 0;
+}
+
diff --git a/src/genconf/conf_htable.h b/src/genconf/conf_htable.h
new file mode 100644 (file)
index 0000000..389725b
--- /dev/null
@@ -0,0 +1,3 @@
+struct confnode *conf_htable_lookup(const char *name);
+int conf_htable_fill(struct confnode *root);
+int conf_htable_init(void);
diff --git a/src/genconf/conf_parser.c b/src/genconf/conf_parser.c
new file mode 100644 (file)
index 0000000..ba1ca0a
--- /dev/null
@@ -0,0 +1,384 @@
+/*
+ * 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 <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <sys/queue.h>
+
+#include "strictmalloc.h"
+#include "expression.h"
+#include "conf_parser.h"
+#include "confnode.h"
+#include "conf_htable.h"
+#include "parser_common.h"
+#include "dotconfig.h"
+
+
+/*
+ * Add a token in the line structure given as argument.
+ * 'linestart' parameter is the beginning of the line buffer.
+ * 'linecur' 
+ * Return a pointer to the first token copied from buf in a allocated
+ * buffer which is correctly nil-terminated. Also fill *eatlen
+ * argument the length of eaten bytes. The user should free the token
+ * structure and the string after use. On error, return NULL.
+ */
+static const char *append_token(struct line *line, const char *linestart,
+                               const char *linecur)
+{
+       struct token *tok;
+       const char *start, *s;
+       unsigned len;
+       char *retbuf;
+
+       tok = strictmalloc(sizeof(struct token));
+
+       /* skip spaces */
+       s = linecur;
+       while (*s != '\0' && isspace(*s))
+               s++;
+       if (*s == '\0' || *s == '#')
+               goto free;
+
+       tok->offset = linecur - linestart;
+
+       /* if it's a double quote, wait next double quote */
+       if (*s == '"') {
+               s++;
+               start = s;
+               while (*s != '\0') {
+                       if (*s == '"' && *(s-1) != '\\')
+                               break;
+                       s++;
+               }
+               if (*s == '\0')
+                       return NULL; /* XXX free */
+               linecur = s + 1;
+       }
+       /* if it's a simple quote, wait next simple quote */
+       else if (*s == '\'') {
+               s++;
+               start = s;
+               while (*s != '\0') {
+                       if (*s == '\'' && *(s-1) != '\\')
+                               break;
+                       s++;
+               }
+               if (*s == '\0')
+                       goto free;
+               linecur = s + 1;
+       }
+       /* else wait a space */
+       else {
+               start = s;
+               while (*s != '\0' && !isspace(*s)) {
+                       if (*s == '#')
+                               break;
+                       s++;
+               }
+               linecur = s;
+       }
+       len = s - start;
+
+       /* allocate string */
+       retbuf = strictmalloc(len + 1);
+       memcpy(retbuf, start, len);
+       retbuf[len] = '\0';
+
+       /* fill token structure and append it in line */
+       tok->str = retbuf;
+       //printf("Append <%s>\n", tok->str);
+       TAILQ_INSERT_TAIL(&line->token_list, tok, next);
+       line->n_token ++;
+
+       return linecur;
+
+ free:
+       free(tok);
+       return NULL;
+}
+
+/*
+ * Parse the line contained in buffer 'buf'. This function fills a
+ * line structure, composed of several tokens.
+ */
+static struct line *parse_conf_line(const char *linebuf)
+{
+       struct line *line;
+       const char *curbuf = linebuf;
+
+       line = strictmalloc(sizeof(struct line));
+       memset(line, 0, sizeof(struct line));
+       TAILQ_INIT(&line->token_list);
+
+       if (isspace(*linebuf))
+               line->start_with_space = 1;
+
+       /* read tokens from buf and append it to line structure */
+       while (curbuf)
+               curbuf = append_token(line, linebuf, curbuf);
+
+       return line;
+}
+
+int check_opt_name(const char *name)
+{
+       /* XXX todo */
+       (void)name;
+       return 0;
+}
+
+/* return true if all dependancies are met to set the enable the
+ * node */
+
+
+
+/* parse a line to get a "help" token */
+int parse_help(const struct line *line)
+{
+       struct token *tok;
+
+       tok = TAILQ_FIRST(&line->token_list);
+       if (tok == NULL)
+               return -1; /* should not happen */
+
+       if (!strcmp(tok->str, "help") && line->n_token == 1)
+               return 0;
+       else if (!strcmp(tok->str, "---help---") && line->n_token == 1)
+               return 0;
+       return -1;
+}
+
+/* look if given line contains a "source" command, and execute the
+ * command if it's the case. Return 0 on success and -1 on error. XXX */
+const char *source_file(const struct line *line)
+{
+       struct token *tok;
+
+       tok = TAILQ_FIRST(&line->token_list);
+       if (tok == NULL)
+               return NULL; /* should not happen */
+
+       /* syntax is: source FILE_NAME */
+       if (!strcmp(tok->str, "source") && line->n_token == 2) {
+               tok = TAILQ_NEXT(tok, next);
+               //printf("source <%s>\n", tok->str);
+               return tok->str;
+       }
+       return NULL;
+}
+
+/*
+ * Parse the file given as argument and return a tree describing the
+ * configuration. XXX wrong
+ */
+int parse_conf_file(const char *filename, struct confnode **pparent,
+                   struct confnode **pcurrent)
+{
+       char buf[BUFSIZ];
+       char *c;
+       FILE *f;
+       struct line *line;
+       struct token *tok;
+       int read_help = 0;
+       struct confnode *current = *pcurrent;
+       struct confnode *parent = *pparent;
+       const char *sourcefilename;
+       int linenum = 0;
+
+       /* XXX lookup in several PATHs */
+       f = fopen(filename, "r");
+       if (f == NULL) {
+               printf("cannot find file <%s>\n", filename);
+               return -1;
+       }
+       while (fgets(buf, sizeof(buf), f) != NULL) {
+               linenum ++;
+
+               /* printf("parent=%s: %s", parent->name, buf); */
+
+               /* we are reading help of an option, do it until the
+                * line does not starts with space */
+               if (read_help && (isspace(*buf) || line_is_empty(buf))) {
+
+                       /* should not happen */
+                       if (current == NULL) {
+                               /* remove \n, display line # */
+                               c = strchr(buf, '\n');
+                               if (c != NULL)
+                                       *c = '\0';
+                               printf("%s:%d invalid line <%s>\n", filename, linenum, buf);
+                               exit(1);
+                       }
+
+                       /* append string in buffer */
+                       strncat(current->help, buf,
+                               sizeof(current->help) - strlen(current->help) - 1);
+                       continue;
+               }
+
+               read_help = 0;
+
+               /* skip empty / comments lines */
+               if (line_is_empty(buf))
+                       continue;
+
+               /* parse line */
+               line = parse_conf_line(buf);
+
+               /* XXX put all the following in a function */
+
+
+               /* if the line does not start with a space, it's a new
+                * keyword, so it's a new node */
+               if (!line->start_with_space) {
+
+                       /* end of previous node */
+                       if (current && current->flags & CONFNODE_F_IS_DIR) {
+                               parent = current;
+                               current = NULL;
+                       }
+
+                       /* if it's a source command, parse the new
+                        * file */
+                       sourcefilename = source_file(line);
+                       if (sourcefilename) {
+                               if (parse_conf_file(sourcefilename, &parent, &current)) {
+                                       /* remove \n, display line # */
+                                       c = strchr(buf, '\n');
+                                       if (c != NULL)
+                                               *c = '\0';
+                                       printf("%s:%d invalid line <%s>\n", filename,
+                                              linenum, buf);
+                                       exit(1);
+                               }
+                               goto free_line;
+                       }
+
+                       /* if the command closes a menu */
+                       if (confnode_close_dir(parent, line) == 0) {
+                               parent = parent->parent;
+                               goto free_line;
+                       }
+
+                       /* if it's a new node command */
+                       current = confnode_new(line);
+                       if (current) {
+                               current->parent = parent;
+                               TAILQ_INSERT_TAIL(&parent->children, current, next);
+                               goto free_line;
+                       }
+
+                       /* remove \n, display line # */
+                       c = strchr(buf, '\n');
+                       if (c != NULL)
+                               *c = '\0';
+                       printf("%s:%d invalid line <%s>\n", filename, linenum, buf);
+                       exit(1);
+               }
+
+               else {
+                       /* see if it's a help indication */
+                       if (parse_help(line) == 0) {
+                               read_help = 1;
+                               strcpy(current->help, "");
+                               goto free_line;
+                       }
+
+                       /* parse node attributes */
+                       if (confnode_add_attr(current, line, buf) == 0)
+                               goto free_line;
+
+                       /* remove \n, display line # */
+                       c = strchr(buf, '\n');
+                       if (c != NULL)
+                               *c = '\0';
+                       printf("%s:%d invalid line <%s>\n", filename, linenum, buf);
+                       exit(1);
+               }
+
+       free_line:
+               while ( (tok = TAILQ_FIRST(&line->token_list)) ) {
+                       TAILQ_REMOVE(&line->token_list, tok, next);
+                       free(tok);
+               }
+               free(line);
+       }
+       fclose(f);
+
+       *pparent = parent;
+       *pcurrent = current;
+       /* XXX */
+       return 0;
+}
+
+/* delete all stored values in conf tree */
+void conf_reset(struct confnode *conf)
+{
+       struct confnode *c;
+       conf->value[0] = '\0';
+       TAILQ_FOREACH(c, &conf->children, next) {
+               conf_reset(c);
+       }
+}
+
+/*
+ * Free previous conf 'prev' if not NULL, then parse the Kconfig and
+ * the .config files. Fill the htable. Return the top node of the
+ * configuration tree. Return NULL on error.
+ */
+struct confnode *conf_reset_and_read(struct confnode *prev,
+                                    const char *dotconfig_filename)
+{
+       struct confnode *current, *parent;
+       struct confnode *root;
+       /* XXX free prev */
+
+       confnode_register_all();
+       conf_htable_init();
+       root = confnode_new_root();
+       if (root == NULL)
+               return NULL;
+
+       parent = root;
+       current = NULL;
+#if 1
+       parse_conf_file("/home/zer0/projects/libcmdline/Kconfig",
+                       &parent, &current);
+#else
+       parse_conf_file("/Users/zer0/projects/libcmdline/Kconfig",
+                       &parent, &current);
+#endif
+       conf_htable_fill(root);
+       if (dotconfig_read(dotconfig_filename, root) < 0)
+               printf("Cannot parse <%s>, use defaults\n", dotconfig_filename);
+
+       return root;
+}
diff --git a/src/genconf/conf_parser.h b/src/genconf/conf_parser.h
new file mode 100644 (file)
index 0000000..3051421
--- /dev/null
@@ -0,0 +1,33 @@
+#ifndef _CONF_PARSER_H_
+#define _CONF_PARSER_H_
+
+/*
+ * This is the structure defining a token. A token is either a word or
+ * a string surrounded by double quotes.
+ */
+struct token {
+       TAILQ_ENTRY(token) next;
+       const char *str;
+       unsigned offset; /* offset in line */
+};
+
+/* A line is a list of token, plus a flag indicating if the line
+ * starts with space/tab */
+TAILQ_HEAD(token_list, token);
+struct line {
+       int start_with_space;
+       struct token_list token_list;
+       unsigned n_token;
+};
+
+struct confnode;
+
+int confnode_check_deps(const struct confnode *n, int verbose);
+struct confnode *conf_reset_and_read(struct confnode *prev,
+                                    const char *dotconfig_filename);
+
+int parse_conf_file(const char *filename, struct confnode **pparent,
+                   struct confnode **pcurrent);
+int confnode_get_value(const struct confnode *n, char *buf, unsigned buflen);
+
+#endif /* _CONF_PARSER_H_ */
diff --git a/src/genconf/confnode.c b/src/genconf/confnode.c
new file mode 100644 (file)
index 0000000..4882c84
--- /dev/null
@@ -0,0 +1,502 @@
+/*
+ * Copyright (c) 2010, 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 <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/queue.h>
+
+#include "strictmalloc.h"
+#include "parser_common.h"
+#include "expression.h"
+#include "conf_parser.h"
+#include "confnode.h"
+
+extern void confnode_root_register(void);
+extern void confnode_menu_register(void);
+extern void confnode_config_register(void);
+extern void confnode_intconfig_register(void);
+extern void confnode_menuconfig_register(void);
+extern void confnode_strconfig_register(void);
+extern void confnode_choice_register(void);
+extern void confnode_choiceconfig_register(void);
+extern void confnode_if_register(void);
+extern void confnode_comment_register(void);
+
+/* global list of all configuration node types */
+struct confnode_type_list confnode_type_list;
+
+/* alloc a new confnode */
+static struct confnode *confnode_alloc(void)
+{
+       struct confnode *n;
+
+       n = strictmalloc(sizeof(struct confnode));
+       TAILQ_INIT(&n->children);
+       TAILQ_INIT(&n->depend);
+       snprintf(n->name, sizeof(n->name), "NONAME");
+       snprintf(n->name, sizeof(n->name), "No prompt");
+       snprintf(n->help, sizeof(n->help), "No help available\n");
+
+       return n;
+}
+
+/* alloc a new root node */
+struct confnode *confnode_new_root(void)
+{
+       struct line l;
+       struct token t;
+
+       /* forge a dummy line */
+       t.str = "root";
+       t.offset = 0;
+       l.start_with_space = 0;
+       l.n_token = 1;
+       TAILQ_INIT(&l.token_list);
+       TAILQ_INSERT_TAIL(&l.token_list, &t, next);
+
+       return confnode_new(&l);
+}
+
+/* Parse the line, and return a new confnode if the line is a valid
+ * new confnode line (example: "menuconfig FOO\n"). Else, return
+ * NULL. */
+struct confnode *confnode_new(const struct line *line)
+{
+       struct confnode_type *cnt;
+       struct confnode *n;
+
+       n = confnode_alloc();
+
+       TAILQ_FOREACH(cnt, &confnode_type_list, next) {
+               if (cnt->ops.new == NULL) {
+                       printf("No new() for confnode type\n");
+                       exit(1);
+               }
+               if (cnt->ops.new(n, line) == 0)
+                       return n;
+       }
+
+       /* fail */
+       free(n);
+       return NULL;
+}
+
+/* Free the node given as argument, and all its associated data */
+void confnode_free(struct confnode *n)
+{
+       struct expr_node *e;
+
+       /* specific free */
+       if (n->ops->free) {
+               n->ops->free(n);
+               return;
+       }
+
+       while ( (e = TAILQ_FIRST(&n->depend)) ) {
+               expression_free(e);
+       }
+       free(n);
+}
+
+/* Parse a line structure (and its associated line buffer) to match an
+ * attribute of a confnode. Return 0 if the line matches, else return
+ * -1 if we cannot match a valid attribute. */
+int confnode_add_attr(struct confnode *n, const struct line *line,
+                     const char *linebuf)
+{
+       struct token *tok;
+
+       tok = TAILQ_FIRST(&line->token_list);
+       if (tok == NULL)
+               return -1; /* should not happen */
+
+       /* specific attribute */
+       if (n->ops->add_attr)
+               return n->ops->add_attr(n, line, linebuf);
+
+       /* syntax is: prompt "prompt content" */
+       if (!(n->flags & CONFNODE_F_NO_SET_PROMPT) &&
+           !strcmp(tok->str, "prompt") &&
+           line->n_token <= 2) {
+               if (line->n_token == 2) {
+                       tok = TAILQ_NEXT(tok, next);
+                       snprintf(n->prompt, sizeof(n->prompt),
+                                "%s", tok->str);
+               }
+               return 0;
+       }
+       /* syntax is: prompt "requires EXPRESSION" */
+       else if (!(n->flags & CONFNODE_F_NO_SET_DEPS) &&
+                !strcmp(tok->str, "requires") &&
+                line->n_token > 1) {
+               struct expr_node *e;
+               char *tmp;
+               tok = TAILQ_NEXT(tok, next);
+               tmp = strdup(linebuf + tok->offset);
+               e = parse_expression(tmp);
+               free(tmp);
+               if (e == NULL)
+                       return -1;
+               TAILQ_INSERT_TAIL(&n->depend, e, next);
+               return 0;
+       }
+       /* XXX we could support expression here ? */
+       /* syntax is: prompt "default VALUE" */
+       else if (!(n->flags & CONFNODE_F_NO_SET_DEFAULT) &&
+                !strcmp(tok->str, "default") &&
+                line->n_token == 2) {
+               tok = TAILQ_NEXT(tok, next);
+               snprintf(n->default_value,
+                        sizeof(n->default_value),
+                        "%s", tok->str);
+               return 0;
+       }
+
+       return -1;
+}
+
+/* Parse a line and check if it closes the current node ,
+ * "endmenu" closes "menu" node): in this case return 0. Else, return
+ * -1. */
+int confnode_close_dir(struct confnode *parent, const struct line *line)
+{
+       if (parent->ops->close_dir)
+               return parent->ops->close_dir(parent, line);
+
+       return -1;
+}
+
+/* write config value in file f in a dotconfig-like manner. Return 0
+ * on success. */
+int confnode_dotconfig_write(const struct confnode *n, FILE *f)
+{
+       int val;
+       const struct confnode *c;
+       char buf[BUFSIZ];
+       const char *quote = "";
+
+       if (n->ops->dotconfig_write)
+               return n->ops->dotconfig_write(n, f);
+
+       if (confnode_get_value(n, buf, sizeof(buf)) < 0)
+               return -1;
+       val = confnode_strvalue_to_boolvalue(n, buf);
+       if (val < 1)
+               return -1;
+
+       if (val == 0) {
+               if (fprintf(f, "# CONFIG_%s is not set\n", n->name) < 0)
+                       return -1;
+               return 0;
+       }
+
+       if (n->flags & CONFNODE_F_QUOTE_VALUE)
+               quote = "\"";
+       if (fprintf(f, "CONFIG_%s=%s%s%s\n", n->name, quote, buf, quote) < 0)
+               return -1;
+
+       TAILQ_FOREACH(c, &n->children, next) {
+               if (confnode_dotconfig_write(c, f) < 0)
+                       return -1;
+       }
+       return 0;
+}
+
+/* Convert string value into boolean value (mainly used for
+ * dependancies). Return -1 on error, else return the boolean value (0
+ * or 1). */
+int confnode_strvalue_to_boolvalue(const struct confnode *n, const char *strvalue)
+{
+       if (n->ops->strvalue_to_boolvalue)
+               return n->ops->strvalue_to_boolvalue(n, strvalue);
+
+       if (strcmp("n", strvalue) == 0)
+               return 0;
+       if (strcmp("y", strvalue) == 0)
+               return 1;
+       return -1;
+}
+
+/* Set the string value of the node n. Return 0 on success, or -1 if
+ * the value cannot be set. */
+int confnode_set_user_strvalue(struct confnode *n, const char *strvalue)
+{
+       int start = 0, end;
+       char buf[BUFSIZ];
+
+       buf[BUFSIZ-1] = '\0';
+       strncpy(buf, strvalue, BUFSIZ-1);
+
+       if (remove_quote(buf, &start, &end) == 0)
+               buf[end] = '\0';
+
+       if (n->ops->set_user_strvalue)
+               return n->ops->set_user_strvalue(n, buf);
+
+       return -1;
+}
+
+/* Get the string value that the user asked to set for this node
+ * 'n'. This function does not check if dependancies are met. Return 0
+ * on success, in this case the string is copied in buffer 'buf' of
+ * len 'buflen'. Return -1 if the value cannot be read. */
+int confnode_get_user_strvalue(const struct confnode *n, char *buf, unsigned buflen)
+{
+       if (n->ops->get_user_strvalue)
+               return n->ops->get_user_strvalue(n, buf, buflen);
+
+       return -1;
+}
+
+/* Get user boolean value of the node. Return 0 if disabled, 1 if
+ * enabled, and -1 on error. */
+int confnode_get_user_boolvalue(const struct confnode *n)
+{
+       char buf[BUFSIZ];
+       int ret;
+
+       ret = confnode_get_user_strvalue(n, buf, sizeof(buf));
+       if (ret < 0)
+               return -1;
+       ret = confnode_strvalue_to_boolvalue(n, buf);
+       if (ret < 0)
+               return -1;
+
+       return ret;
+}
+
+/* Set the default string value of the node n. Return 0 on success, or
+ * -1 if the value cannot be set. */
+int confnode_set_default_strvalue(struct confnode *n, const char *strvalue)
+{
+       int start = 0, end;
+       char buf[BUFSIZ];
+
+       buf[BUFSIZ-1] = '\0';
+       strncpy(buf, strvalue, BUFSIZ-1);
+
+       if (remove_quote(buf, &start, &end) == 0)
+               buf[end] = '\0';
+
+       if (n->ops->set_default_strvalue)
+               return n->ops->set_default_strvalue(n, buf);
+
+       return -1;
+}
+
+/* Get the default string value for this node 'n'. This function does
+ * not check if dependancies are met. Return 0 on success, in this
+ * case the string is copied in buffer 'buf' of len 'buflen'. Return
+ * -1 if the value cannot be read. */
+int confnode_get_default_strvalue(const struct confnode *n, char *buf, unsigned buflen)
+{
+       if (n->ops->get_default_strvalue)
+               return n->ops->get_default_strvalue(n, buf, buflen);
+
+       return -1;
+}
+
+/*
+ * Check deps to see if a node 'n' can be enabled. If yes, return 1,
+ * else return 0. On error, return -1. If 'verbose' is true, dump an
+ * error for each condition preventing node to be enabled.
+ */
+int confnode_check_deps(const struct confnode *n, int verbose)
+{
+       char buf[BUFSIZ];
+       const struct confnode *pn = n->parent;
+       struct expr_node *e;
+       int ret = 1;
+       const char *parent_name;
+
+       /* bypass dependency checks if this flag is set */
+       if (n->flags & CONFNODE_F_NO_DEPS)
+               return 1;
+
+       /* check that there is no diabled "if" or"menuconfig" above
+        * our node */
+       for (pn = n->parent; pn; pn = pn->parent) {
+               if (pn->flags & CONFNODE_F_IS_ROOT)
+                       break;
+               if (!(pn->flags & CONFNODE_F_FORCE_CHILD_DEPS))
+                       continue;
+               if (confnode_get_boolvalue(pn) != 0)
+                       continue;
+
+               if (verbose) {
+                       /* use value if node has no name */
+                       if (pn->flags & CONFNODE_F_NO_NAME)
+                               parent_name = pn->value;
+                       else
+                               parent_name = pn->name;
+                       printf("cannot enable %s, depends on "
+                              "%s parent %s\n",
+                              n->name,
+                              confnode_get_type_str(pn),
+                              parent_name);
+               }
+               ret = 0;
+       }
+
+       /* check deps */
+       TAILQ_FOREACH(e, &n->depend, next) {
+               if (expression_eval(e))
+                       continue;
+
+               if (expression_to_str(e, buf, sizeof(buf)) < 0)
+                       return -1;
+               if (verbose)
+                       printf("cannot enable %s, depends on %s\n", n->name, buf);
+               ret = 0;
+       }
+
+       return ret;
+}
+
+/* Get string value of a node, including dep check. Return -1 on
+ * error. Return the boolean value of the node (0 or 1) on success: in
+ * this case, and if buf is no NULL, buf is filled with the strvalue. */
+int confnode_get_value(const struct confnode *n, char *buf, unsigned buflen)
+{
+       int dep, ret, strvalue_len;
+       char localbuf[BUFSIZ];
+       char *strvalue;
+
+       if (buf) {
+               strvalue = buf;
+               strvalue_len = buflen;
+       }
+       else {
+               strvalue = localbuf;
+               strvalue_len = sizeof(localbuf);
+       }
+
+       /* check deps first */
+       dep = confnode_check_deps(n, 0);
+       if (dep < 0)
+               return -1;
+       if (dep == 0)
+               return 0;
+
+       ret = confnode_get_user_strvalue(n, strvalue, strvalue_len);
+       if (ret < 0)
+               ret = confnode_get_default_strvalue(n, strvalue, strvalue_len);
+       if (ret < 0)
+               return 0; /* consider the value as false */
+
+       ret = confnode_strvalue_to_boolvalue(n, strvalue);
+       if (ret < 0)
+               return -1;
+
+       return ret;
+}
+
+/* Get boolean value of a node, including dep check. Return -1 on
+ * error. Return the boolean value of the node (0 or 1) on success. */
+int confnode_get_boolvalue(const struct confnode *n)
+{
+       return confnode_get_value(n, NULL, 0);
+}
+
+
+/* Return a string identifying the node type ("config", "menuconfig",
+ * "choice", ...) */
+const char *confnode_get_type_str(const struct confnode *n)
+{
+       if (n->ops->get_type_str)
+               return n->ops->get_type_str(n);
+       return "none";
+}
+
+/* Print a one-line summary of the node. Used in case of 'ls'. */
+void confnode_display_short(const struct confnode *n)
+{
+       if (n->ops->display_short)
+               n->ops->display_short(n);
+       else
+               printf("------ %s\n", n->prompt);
+}
+
+/* Print a detailed view of the node. */
+void confnode_display_long(const struct confnode *n)
+{
+       const char *nodetype;
+       char value[MAX_VALUE_SIZE];
+       const char *quote = "";
+
+       if (n->ops->display_long) {
+               n->ops->display_long(n);
+               return;
+       }
+
+       value[0] = '\0'; /* XXX get_value */
+       nodetype = confnode_get_type_str(n);
+       printf("%s <%s>\n", nodetype, n->name);
+       printf("    path ");
+       conf_display_path(n);
+
+       if (n->flags & CONFNODE_F_QUOTE_VALUE)
+               quote = "\"";
+       printf("    value %s%s%s\n", quote, value, quote);
+       printf("    wanted %s%s%s\n", quote, n->value, quote);
+       printf("    default %s%s%s\n", quote, n->default_value, quote);
+       confnode_check_deps(n, 1);
+}
+
+void __conf_display_path(const struct confnode *n)
+{
+       if (n->parent)
+               __conf_display_path(n->parent);
+       if (n->flags & CONFNODE_F_IS_ROOT)
+               printf("/");
+       else if (!(n->flags & CONFNODE_F_INVISIBLE))
+               printf("%s/", n->name);
+}
+
+/* show path: used by the 'pwd' command. Nodes like 'if' are
+ * ignored. */
+void conf_display_path(const struct confnode *n)
+{
+       __conf_display_path(n);
+       printf("\n");
+}
+
+void confnode_register_all(void)
+{
+       TAILQ_INIT(&confnode_type_list);
+       confnode_root_register();
+       confnode_menu_register();
+       confnode_config_register();
+       confnode_intconfig_register();
+       confnode_menuconfig_register();
+       confnode_strconfig_register();
+       confnode_choice_register();
+       confnode_choiceconfig_register();
+       confnode_if_register();
+       confnode_comment_register();
+}
diff --git a/src/genconf/confnode.h b/src/genconf/confnode.h
new file mode 100644 (file)
index 0000000..13de83e
--- /dev/null
@@ -0,0 +1,200 @@
+#ifndef _CONFNODE_H_
+#define _CONFNODE_H_
+
+/***************/
+/* node type specific functions */
+/***************/
+
+typedef int (confnode_new_t)(struct confnode *n, const struct line *line);
+typedef void (confnode_free_t)(struct confnode *n);
+typedef int (confnode_add_attr_t)(struct confnode *n, const struct line *line,
+                                 const char *linebuf);
+typedef int (confnode_close_dir_t)(struct confnode *n, const struct line *line);
+typedef int (confnode_dotconfig_write_t)(const struct confnode *n, FILE *f);
+typedef int (confnode_strvalue_to_boolvalue_t)(const struct confnode *n,
+                                              const char *strvalue);
+typedef int (confnode_set_user_strvalue_t)(struct confnode *n,
+                                          const char *strvalue);
+typedef int (confnode_get_user_strvalue_t)(const struct confnode *n, char *buf,
+                                          unsigned buflen);
+typedef int (confnode_set_default_strvalue_t)(struct confnode *n,
+                                             const char *strvalue);
+typedef int (confnode_get_default_strvalue_t)(const struct confnode *n, char *buf,
+                                             unsigned buflen);
+typedef const char *(confnode_get_type_str_t)(const struct confnode *n);
+typedef void (confnode_display_short_t)(const struct confnode *n);
+typedef void (confnode_display_long_t)(const struct confnode *n);
+
+/* all node-specific operations */
+struct confnode_ops {
+       /* alloc / free */
+       confnode_new_t *new;
+       confnode_free_t *free;
+
+       /* conf parse */
+       confnode_add_attr_t *add_attr;
+       confnode_close_dir_t *close_dir;
+
+       /* dotconfig */
+       confnode_dotconfig_write_t *dotconfig_write;
+
+       /* convert value */
+       confnode_strvalue_to_boolvalue_t *strvalue_to_boolvalue;
+
+       /* user value */
+       confnode_set_user_strvalue_t *set_user_strvalue;
+       confnode_get_user_strvalue_t *get_user_strvalue;
+
+       /* default value */
+       confnode_set_default_strvalue_t *set_default_strvalue;
+       confnode_get_default_strvalue_t *get_default_strvalue;
+
+       /* display */
+       confnode_get_type_str_t *get_type_str;
+       confnode_display_short_t *display_short;
+       confnode_display_long_t *display_long;
+};
+
+/***************/
+/* generic node functions */
+/***************/
+
+/* alloc a new root node */
+struct confnode *confnode_new_root(void);
+
+/* Parse the line, and return a new confnode if the line is a valid
+ * new confnode line (example: "menuconfig FOO\n"). Else, return
+ * NULL. */
+struct confnode *confnode_new(const struct line *line);
+
+/* Free the node given as argument, and all its associated data */
+void confnode_free(struct confnode *n);
+
+/* Parse a line to get an attribute of confnode n. Return 0 if it
+ * matches an attribute for this node, else return -1. */
+int confnode_add_attr(struct confnode *n, const struct line *line,
+                     const char *linebuf);
+
+/* Parse a line and check if it closes the current node,
+ * "endmenu" closes "menu" node): in this case return 0. Else, return
+ * -1. */
+int confnode_close_dir(struct confnode *n, const struct line *line);
+
+/* write config value in file f in a dotconfig-like manner. Return 0
+ * on success. */
+int confnode_dotconfig_write(const struct confnode *n, FILE *f);
+
+/* Convert string value into boolean value (mainly used for
+ * dependancies). Return -1 on error, else return the boolean value (0
+ * or 1). */
+int confnode_strvalue_to_boolvalue(const struct confnode *n, const char *strvalue);
+
+/* Get the string value that the user asked to set for this node
+ * 'n'. This function does not check if dependancies are met. Return 0
+ * on success, in this case the string is copied in buffer 'buf' of
+ * len 'buflen'. Return -1 if the value cannot be read. */
+int confnode_get_user_strvalue(const struct confnode *n, char *buf, unsigned buflen);
+
+/* Set the string value of the node n. Return 0 on success, or -1 if
+ * the value cannot be set. */
+int confnode_set_user_strvalue(struct confnode *n, const char *strvalue);
+
+/* Get user boolean value of the node. Return 0 if disabled, 1 if
+ * enabled, and -1 on error. */
+int confnode_get_user_boolvalue(const struct confnode *n);
+
+/* Set the default string value of the node n. Return 0 on success, or
+ * -1 if the value cannot be set. */
+int confnode_set_default_strvalue(struct confnode *n, const char *strvalue);
+
+/* Get the default string value for this node 'n'. This function does
+ * not check if dependancies are met. Return 0 on success, in this
+ * case the string is copied in buffer 'buf' of len 'buflen'. Return
+ * -1 if the value cannot be read. */
+int confnode_get_default_strvalue(const struct confnode *n, char *buf, unsigned buflen);
+
+/* Get string value of a node, including dep check. Return -1 on
+ * error. Return the boolean value of the node (0 or 1) on success: in
+ * this case, and if buf is no NULL, buf is filled with the strvalue. */
+int confnode_get_value(const struct confnode *n, char *buf, unsigned buflen);
+
+/* Get real boolean value of the node, checking deps first. Return 0
+ * if disabled, 1 if enabled, and -1 on error. */
+int confnode_get_boolvalue(const struct confnode *n);
+
+/* Return a string identifying the node type ("config", "menuconfig",
+ * "choice", ...) */
+const char *confnode_get_type_str(const struct confnode *n);
+
+/* Print one-line view of the node. */
+void confnode_display_short(const struct confnode *n);
+
+/* Print a detailed view of the node. */
+void confnode_display_long(const struct confnode *n);
+
+/* display the path of the current node */
+void conf_display_path(const struct confnode *n);
+
+/* register all nodes types */
+void confnode_register_all(void);
+
+/* structure describing a confnode type */
+TAILQ_HEAD(confnode_type_list, confnode_type);
+struct confnode_type {
+       TAILQ_ENTRY(confnode_type) next;
+       struct confnode_ops ops;
+};
+
+/* global list of all configuration node types */
+extern struct confnode_type_list confnode_type_list;
+
+#define MAX_NAME_SIZE 128
+#define MAX_PROMPT_SIZE MAX_NAME_SIZE
+#define MAX_VALUE_SIZE MAX_NAME_SIZE
+#define MAX_HELP_SIZE 1024
+
+TAILQ_HEAD(expr_node_list, expr_node);
+
+
+TAILQ_HEAD(optname_list, optname);
+struct optname {
+       TAILQ_ENTRY(optname) next;
+       char *name;
+};
+
+/*
+ * This structure defines a configuration node, for instance a "menu"
+ * entry, or a "bool" entry.
+ */
+TAILQ_HEAD(confnode_list, confnode);
+struct confnode {
+       TAILQ_ENTRY(confnode) next;  /* next in children list */
+       TAILQ_ENTRY(confnode) hnext; /* next in hash table */
+       TAILQ_ENTRY(confnode) user_next; /* next in user list */
+#define CONFNODE_F_IS_DIR           0x0001 /* node can contain other nodes*/
+#define CONFNODE_F_NO_SET_DEPS      0x0002 /* does not support the "requires" attribute */
+#define CONFNODE_F_NO_SET_PROMPT    0x0004 /* does not support the "prompt" attribute */
+#define CONFNODE_F_NO_SET_DEFAULT   0x0008 /* does not support the "default" attribute */
+#define CONFNODE_F_IS_ROOT          0x0010 /* the root node */
+#define CONFNODE_F_QUOTE_VALUE      0x0020 /* value needs quote to be displayed */
+#define CONFNODE_F_INVISIBLE        0x0040 /* never seen from cmdline (like the IF node) */
+#define CONFNODE_F_BOOL             0x0080 /* Stored value is a boolean */
+#define CONFNODE_F_INT              0x0100 /* Stored value is a integer */
+#define CONFNODE_F_STR              0x0200 /* Stored value is a string */
+#define CONFNODE_F_CHOICE           0x0400 /* Stored value is a choice */
+#define CONFNODE_F_FORCE_CHILD_DEPS 0x0800 /* if node is disabled, disable all children */
+#define CONFNODE_F_NO_DEPS          0x1000 /* bypass dependency checks */
+#define CONFNODE_F_NO_NAME          0x2000 /* node has no name (if, comments, ...) */
+       unsigned flags;
+       const struct confnode_ops *ops;
+       char name[MAX_NAME_SIZE];
+       char prompt[MAX_PROMPT_SIZE];
+       char help[MAX_HELP_SIZE];
+       char default_value[MAX_VALUE_SIZE];
+       char value[MAX_VALUE_SIZE];
+       struct confnode_list children;
+       struct confnode *parent;
+       struct expr_node_list depend;
+};
+
+#endif /* _CONFNODE_H_ */
diff --git a/src/genconf/confnode_choice.c b/src/genconf/confnode_choice.c
new file mode 100644 (file)
index 0000000..5a34e7d
--- /dev/null
@@ -0,0 +1,218 @@
+/*
+ * Copyright (c) 2010, 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 <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <sys/queue.h>
+
+#include "expression.h"
+#include "conf_parser.h"
+#include "conf_htable.h"
+#include "parser_common.h"
+#include "dotconfig.h"
+#include "confnode.h"
+
+#define MAX_DISP_SIZE 16
+
+static int confnode_choice_new(struct confnode *n, const struct line *line);
+static int confnode_choice_close_dir(struct confnode *parent,
+                                    const struct line *line);
+static int confnode_choice_strvalue_to_boolvalue(const struct confnode *n,
+                                                const char *strvalue);
+static int confnode_choice_set_user_strvalue(struct confnode *n,
+                                             const char *strvalue);
+static int confnode_choice_get_user_strvalue(const struct confnode *n, char *buf,
+                                             unsigned buflen);
+static int confnode_choice_set_default_strvalue(struct confnode *n,
+                                             const char *strvalue);
+static int confnode_choice_get_default_strvalue(const struct confnode *n, char *buf,
+                                             unsigned buflen);
+static const char *confnode_choice_get_type_str(const struct confnode *n);
+static void confnode_choice_display_short(const struct confnode *n);
+
+static struct confnode_type confnode_choice = {
+       .ops = {
+               .new = confnode_choice_new,
+               .free = NULL,
+               .add_attr = NULL,
+               .close_dir = confnode_choice_close_dir,
+               .dotconfig_write = NULL,
+               .strvalue_to_boolvalue = confnode_choice_strvalue_to_boolvalue,
+               .set_user_strvalue = confnode_choice_set_user_strvalue,
+               .get_user_strvalue = confnode_choice_get_user_strvalue,
+               .set_default_strvalue = confnode_choice_set_default_strvalue,
+               .get_default_strvalue = confnode_choice_get_default_strvalue,
+               .get_type_str = confnode_choice_get_type_str,
+               .display_short = confnode_choice_display_short,
+               .display_long = NULL,
+       }
+};
+
+/* Create a new node */
+static int confnode_choice_new(struct confnode *n, const struct line *line)
+{
+       struct token *tok;
+       tok = TAILQ_FIRST(&line->token_list);
+
+       if (strcmp(tok->str, "choice") || line->n_token != 2)
+               return -1;
+
+       tok = TAILQ_NEXT(tok, next);
+       snprintf(n->name, sizeof(n->name), "%s", tok->str);
+       n->ops = &confnode_choice.ops;
+       n->flags = CONFNODE_F_IS_DIR |
+               /* CONFNODE_F_NO_SET_DEPS | */ /* XXX ? */
+               CONFNODE_F_CHOICE |
+               CONFNODE_F_QUOTE_VALUE;
+       return 0;
+}
+
+/* Parse a line and check if it closes the current node ,
+ * "endmenu" closes "menu" node): in this case return 0. Else, return
+ * -1. */
+static int confnode_choice_close_dir(struct confnode *parent,
+                                    const struct line *line)
+{
+       struct token *tok;
+
+       tok = TAILQ_FIRST(&line->token_list);
+       if (tok == NULL)
+               return -1; /* should not happen */
+
+       /* syntax is: endmenu */
+       if (!strcmp(tok->str, "endchoice") && line->n_token == 1) {
+               //printf("endchoice\n");
+               return 0;
+       }
+       return -1;
+}
+
+/* Convert string value into boolean value (mainly used for
+ * dependancies). Return -1 on error, else return the boolean value (0
+ * or 1). */
+static int confnode_choice_strvalue_to_boolvalue(const struct confnode *n,
+                                                const char *strvalue)
+{
+       /* always true */
+       return 1;
+}
+
+/* Set the string value of the node n. Return 0 on success, or -1 if
+ * the value cannot be set. */
+static int confnode_choice_set_user_strvalue(struct confnode *n,
+                                            const char *strvalue)
+{
+       struct confnode *c;
+
+       TAILQ_FOREACH(c, &n->children, next) {
+               if (strcmp(c->prompt, strvalue) == 0)
+                       break;
+       }
+       /* not found */
+       if (c == NULL)
+               return -1;
+
+       snprintf(n->value, sizeof(n->value), "%s", strvalue);
+       return 0;
+}
+
+/* Get the string value that the user asked to set for this node
+ * 'n'. This function does not check if dependancies are met. Return 0
+ * on success, in this case the string is copied in buffer 'buf' of
+ * len 'buflen'. Return -1 if the value cannot be read. */
+static int confnode_choice_get_user_strvalue(const struct confnode *n, char *buf,
+                                             unsigned buflen)
+{
+       snprintf(buf, buflen, n->value);
+       return 0;
+}
+
+/* Set the default string value of the node n. Return 0 on success, or
+ * -1 if the value cannot be set. */
+static int confnode_choice_set_default_strvalue(struct confnode *n,
+                                             const char *strvalue)
+{
+       snprintf(n->default_value, sizeof(n->default_value), "%s", strvalue);
+       return 0;
+}
+
+/* Get the default string value for this node 'n'. This function does
+ * not check if dependancies are met. Return 0 on success, in this
+ * case the string is copied in buffer 'buf' of len 'buflen'. Return
+ * -1 if the value cannot be read. */
+static int confnode_choice_get_default_strvalue(const struct confnode *n, char *buf,
+                                               unsigned buflen)
+{
+       struct confnode *c;
+
+       if (TAILQ_EMPTY(&n->children))
+               return -1;
+
+       /* this is the "default" default :) */
+       snprintf(buf, buflen, "%s", TAILQ_FIRST(&n->children)->prompt);
+
+       TAILQ_FOREACH(c, &n->children, next) {
+               if (strcmp(c->prompt, buf) == 0) {
+                       snprintf(buf, buflen, "%s", c->prompt);
+                       break;
+               }
+       }
+       return 0;
+}
+
+/* Return a string identifying the node type ("config", "menuconfig",
+ * "choice", ...) */
+static const char *confnode_choice_get_type_str(const struct confnode *n)
+{
+       return "choice";
+}
+
+/* Print a one-line summary of the node. Used in case of 'ls'. */
+static void confnode_choice_display_short(const struct confnode *n)
+{
+       char buf[BUFSIZ];
+
+       printf("choice %s ", n->name);
+       if (confnode_get_value(n, buf, sizeof(buf)) >= 0) {
+               if (strlen(buf) > MAX_DISP_SIZE) {
+                       buf[MAX_DISP_SIZE-3] = '.';
+                       buf[MAX_DISP_SIZE-2] = '.';
+                       buf[MAX_DISP_SIZE-1] = '.';
+                       buf[MAX_DISP_SIZE] = '\0';
+               }
+       }
+       printf("(%s): %s\n", buf, n->prompt);
+}
+
+/* register the node type */
+void confnode_choice_register(void)
+{
+       TAILQ_INSERT_TAIL(&confnode_type_list, &confnode_choice, next);
+}
diff --git a/src/genconf/confnode_choiceconfig.c b/src/genconf/confnode_choiceconfig.c
new file mode 100644 (file)
index 0000000..7d70d27
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 2010, 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 <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <sys/queue.h>
+
+#include "expression.h"
+#include "conf_parser.h"
+#include "conf_htable.h"
+#include "parser_common.h"
+#include "dotconfig.h"
+#include "confnode.h"
+
+static int confnode_choiceconfig_new(struct confnode *n, const struct line *line);
+static int confnode_choiceconfig_set_user_strvalue(struct confnode *n,
+                                             const char *strvalue);
+static int confnode_choiceconfig_get_user_strvalue(const struct confnode *n, char *buf,
+                                             unsigned buflen);
+static int confnode_choiceconfig_set_default_strvalue(struct confnode *n,
+                                             const char *strvalue);
+static int confnode_choiceconfig_get_default_strvalue(const struct confnode *n, char *buf,
+                                             unsigned buflen);
+static const char *confnode_choiceconfig_get_type_str(const struct confnode *n);
+static void confnode_choiceconfig_display_short(const struct confnode *n);
+
+static struct confnode_type confnode_choiceconfig = {
+       .ops = {
+               .new = confnode_choiceconfig_new,
+               .free = NULL,
+               .add_attr = NULL,
+               .close_dir = NULL,
+               .dotconfig_write = NULL,
+               .strvalue_to_boolvalue = NULL,
+               .set_user_strvalue = confnode_choiceconfig_set_user_strvalue,
+               .get_user_strvalue = confnode_choiceconfig_get_user_strvalue,
+               .set_default_strvalue = confnode_choiceconfig_set_default_strvalue,
+               .get_default_strvalue = confnode_choiceconfig_get_default_strvalue,
+               .get_type_str = confnode_choiceconfig_get_type_str,
+               .display_short = confnode_choiceconfig_display_short,
+               .display_long = NULL,
+       }
+};
+
+/* Create a new node */
+static int confnode_choiceconfig_new(struct confnode *n, const struct line *line)
+{
+       struct token *tok;
+       tok = TAILQ_FIRST(&line->token_list);
+
+       /* XXX we should have the parent node */
+
+       if (strcmp(tok->str, "choiceconfig") || line->n_token != 2)
+               return -1;
+
+       tok = TAILQ_NEXT(tok, next);
+       snprintf(n->name, sizeof(n->name), "%s", tok->str);
+       n->ops = &confnode_choiceconfig.ops;
+       n->flags = CONFNODE_F_BOOL |
+               CONFNODE_F_NO_SET_DEFAULT;
+
+       snprintf(n->default_value, sizeof(n->default_value), "n");
+       return 0;
+}
+
+/* Set the string value of the node n. Return 0 on success, or -1 if
+ * the value cannot be set. */
+static int confnode_choiceconfig_set_user_strvalue(struct confnode *n,
+                                             const char *strvalue)
+{
+       if (strcmp(strvalue, "y") && strcmp(strvalue, "n"))
+               return -1;
+       return confnode_set_user_strvalue(n->parent, n->prompt);
+}
+
+/* Get the string value that the user asked to set for this node
+ * 'n'. This function does not check if dependancies are met. Return 0
+ * on success, in this case the string is copied in buffer 'buf' of
+ * len 'buflen'. Return -1 if the value cannot be read. */
+static int confnode_choiceconfig_get_user_strvalue(const struct confnode *n, char *buf,
+                                             unsigned buflen)
+{
+       if (confnode_get_user_strvalue(n->parent, buf, buflen) < 0)
+               return -1;
+       if (strcmp(buf, n->prompt) == 0)
+               snprintf(buf, buflen, "y");
+       else
+               snprintf(buf, buflen, "n");
+       return 0;
+}
+
+/* Set the default string value of the node n. Return 0 on success, or
+ * -1 if the value cannot be set. */
+static int confnode_choiceconfig_set_default_strvalue(struct confnode *n,
+                                             const char *strvalue)
+{
+       if (strcmp(strvalue, "y") && strcmp(strvalue, "n"))
+               return -1;
+       return confnode_set_default_strvalue(n->parent, n->prompt);
+}
+
+/* Get the default string value for this node 'n'. This function does
+ * not check if dependancies are met. Return 0 on success, in this
+ * case the string is copied in buffer 'buf' of len 'buflen'. Return
+ * -1 if the value cannot be read. */
+static int confnode_choiceconfig_get_default_strvalue(const struct confnode *n, char *buf,
+                                             unsigned buflen)
+{
+       if (confnode_get_default_strvalue(n->parent, buf, buflen) < 0)
+               return -1;
+       if (strcmp(buf, n->prompt) == 0)
+               snprintf(buf, buflen, "y");
+       else
+               snprintf(buf, buflen, "n");
+       return 0;
+}
+
+/* Return a string identifying the node type ("config", "menuconfig",
+ * "choice", ...) */
+static const char *confnode_choiceconfig_get_type_str(const struct confnode *n)
+{
+       return "choiceconfig";
+}
+
+/* Print a one-line summary of the node. Used in case of 'ls'. */
+static void confnode_choiceconfig_display_short(const struct confnode *n)
+{
+       char c;
+       if (confnode_get_boolvalue(n) == 0) {
+               if (confnode_get_user_boolvalue(n) <= 0)
+                       c = ' ';
+               else
+                       c = 'd';
+       }
+       else {
+               c = '*';
+       }
+
+       printf("[%c]    %s: %s\n", c, n->name, n->prompt);
+}
+
+/* register the node type */
+void confnode_choiceconfig_register(void)
+{
+       TAILQ_INSERT_TAIL(&confnode_type_list, &confnode_choiceconfig, next);
+}
diff --git a/src/genconf/confnode_comment.c b/src/genconf/confnode_comment.c
new file mode 100644 (file)
index 0000000..36607cc
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2010, 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 <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <sys/queue.h>
+
+#include "expression.h"
+#include "conf_parser.h"
+#include "conf_htable.h"
+#include "parser_common.h"
+#include "dotconfig.h"
+#include "confnode.h"
+
+static int confnode_comment_new(struct confnode *n, const struct line *line);
+static int confnode_comment_dotconfig_write(const struct confnode *n, FILE *f);
+static const char *confnode_comment_get_type_str(const struct confnode *n);
+static void confnode_comment_display_long(const struct confnode *n);
+
+static struct confnode_type confnode_comment = {
+       .ops = {
+               .new = confnode_comment_new,
+               .free = NULL,
+               .add_attr = NULL,
+               .close_dir = NULL,
+               .dotconfig_write = confnode_comment_dotconfig_write,
+               .strvalue_to_boolvalue = NULL,
+               .set_user_strvalue = NULL,
+               .get_user_strvalue = NULL,
+               .set_default_strvalue = NULL,
+               .get_default_strvalue = NULL,
+               .get_type_str = confnode_comment_get_type_str,
+               .display_short = NULL,
+               .display_long = confnode_comment_display_long,
+       }
+};
+
+/* Create a new node */
+static int confnode_comment_new(struct confnode *n, const struct line *line)
+{
+       struct token *tok;
+       tok = TAILQ_FIRST(&line->token_list);
+
+       if (strcmp(tok->str, "comment") || line->n_token != 2)
+               return -1;
+
+       tok = TAILQ_NEXT(tok, next);
+       /* comment string is stored in prompt field, not in name */
+       snprintf(n->prompt, sizeof(n->prompt), "%s", tok->str);
+       n->ops = &confnode_comment.ops;
+       n->flags = CONFNODE_F_NO_SET_DEFAULT |
+               CONFNODE_F_NO_SET_PROMPT;
+       return 0;
+}
+
+/* write config value in file f in a dotconfig-like manner. Return 0
+ * on success. */
+static int confnode_comment_dotconfig_write(const struct confnode *n, FILE *f)
+{
+       if (fprintf(f, "# %s\n", n->prompt) < 0)
+               return -1;
+
+       /* this node has no children, no need to dump them */
+       return 0;
+}
+
+/* Return a string identifying the node type ("config", "menuconfig",
+ * "choice", ...) */
+static const char *confnode_comment_get_type_str(const struct confnode *n)
+{
+       return "comment";
+}
+
+/* Print a detailed view of the node. */
+static void confnode_comment_display_long(const struct confnode *n)
+{
+       /* nothing */
+}
+
+/* register the node type */
+void confnode_comment_register(void)
+{
+       TAILQ_INSERT_TAIL(&confnode_type_list, &confnode_comment, next);
+}
diff --git a/src/genconf/confnode_config.c b/src/genconf/confnode_config.c
new file mode 100644 (file)
index 0000000..7817ebf
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+ * Copyright (c) 2010, 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 <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <sys/queue.h>
+
+#include "expression.h"
+#include "conf_parser.h"
+#include "conf_htable.h"
+#include "parser_common.h"
+#include "dotconfig.h"
+#include "confnode.h"
+
+static int confnode_config_new(struct confnode *n, const struct line *line);
+static int confnode_config_set_user_strvalue(struct confnode *n,
+                                             const char *strvalue);
+static int confnode_config_get_user_strvalue(const struct confnode *n, char *buf,
+                                             unsigned buflen);
+static int confnode_config_set_default_strvalue(struct confnode *n,
+                                             const char *strvalue);
+static int confnode_config_get_default_strvalue(const struct confnode *n, char *buf,
+                                             unsigned buflen);
+static const char *confnode_config_get_type_str(const struct confnode *n);
+static void confnode_config_display_short(const struct confnode *n);
+
+static struct confnode_type confnode_config = {
+       .ops = {
+               .new = confnode_config_new,
+               .free = NULL,
+               .add_attr = NULL,
+               .close_dir = NULL,
+               .dotconfig_write = NULL,
+               .strvalue_to_boolvalue = NULL,
+               .set_user_strvalue = confnode_config_set_user_strvalue,
+               .get_user_strvalue = confnode_config_get_user_strvalue,
+               .set_default_strvalue = confnode_config_set_default_strvalue,
+               .get_default_strvalue = confnode_config_get_default_strvalue,
+               .get_type_str = confnode_config_get_type_str,
+               .display_short = confnode_config_display_short,
+               .display_long = NULL,
+       }
+};
+
+/* Create a new node */
+static int confnode_config_new(struct confnode *n, const struct line *line)
+{
+       struct token *tok;
+       tok = TAILQ_FIRST(&line->token_list);
+
+       if (strcmp(tok->str, "config") || line->n_token != 2)
+               return -1;
+
+       tok = TAILQ_NEXT(tok, next);
+       snprintf(n->name, sizeof(n->name), "%s", tok->str);
+       n->ops = &confnode_config.ops;
+       n->flags = CONFNODE_F_BOOL;
+       snprintf(n->default_value, sizeof(n->default_value), "n");
+       return 0;
+}
+
+/* Set the string value of the node n. Return 0 on success, or -1 if
+ * the value cannot be set. */
+static int confnode_config_set_user_strvalue(struct confnode *n,
+                                             const char *strvalue)
+{
+       if (strcmp(strvalue, "y") && strcmp(strvalue, "n"))
+               return -1;
+       snprintf(n->value, sizeof(n->value), "%s", strvalue);
+       return 0;
+}
+
+/* Get the string value that the user asked to set for this node
+ * 'n'. This function does not check if dependancies are met. Return 0
+ * on success, in this case the string is copied in buffer 'buf' of
+ * len 'buflen'. Return -1 if the value cannot be read. */
+static int confnode_config_get_user_strvalue(const struct confnode *n, char *buf,
+                                             unsigned buflen)
+{
+       if (strcmp(n->value, "") == 0)
+               return -1;
+       if (!strcmp(n->value, "y")) {
+               snprintf(buf, buflen, "y");
+               return 0;
+       }
+       else {
+               snprintf(buf, buflen, "n");
+               return 0;
+       }
+}
+
+/* Set the default string value of the node n. Return 0 on success, or
+ * -1 if the value cannot be set. */
+static int confnode_config_set_default_strvalue(struct confnode *n,
+                                             const char *strvalue)
+{
+       if (strcmp(strvalue, "y") && strcmp(strvalue, "n"))
+               return -1;
+       snprintf(n->default_value, sizeof(n->default_value), "%s", strvalue);
+       return 0;
+}
+
+/* Get the default string value for this node 'n'. This function does
+ * not check if dependancies are met. Return 0 on success, in this
+ * case the string is copied in buffer 'buf' of len 'buflen'. Return
+ * -1 if the value cannot be read. */
+static int confnode_config_get_default_strvalue(const struct confnode *n, char *buf,
+                                             unsigned buflen)
+{
+       if (!strcmp(n->default_value, "y")) {
+               snprintf(buf, buflen, "y");
+               return 0;
+       }
+       else {
+               snprintf(buf, buflen, "n");
+               return 0;
+       }
+}
+
+/* Return a string identifying the node type ("config", "menuconfig",
+ * "choice", ...) */
+static const char *confnode_config_get_type_str(const struct confnode *n)
+{
+       return "config";
+}
+
+/* Print a one-line summary of the node. Used in case of 'ls'. */
+static void confnode_config_display_short(const struct confnode *n)
+{
+       char c;
+       if (confnode_get_boolvalue(n) == 0) {
+               if (confnode_get_user_boolvalue(n) <= 0)
+                       c = ' ';
+               else
+                       c = 'd';
+       }
+       else {
+               c = '*';
+       }
+
+       printf("[%c]    %s: %s\n", c, n->name, n->prompt);
+}
+
+/* register the node type */
+void confnode_config_register(void)
+{
+       TAILQ_INSERT_TAIL(&confnode_type_list, &confnode_config, next);
+}
diff --git a/src/genconf/confnode_if.c b/src/genconf/confnode_if.c
new file mode 100644 (file)
index 0000000..f9e5f91
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2010, 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 <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <sys/queue.h>
+
+#include "expression.h"
+#include "conf_parser.h"
+#include "conf_htable.h"
+#include "parser_common.h"
+#include "dotconfig.h"
+#include "confnode.h"
+
+static int confnode_if_new(struct confnode *n, const struct line *line);
+static int confnode_if_close_dir(struct confnode *parent,
+                                const struct line *line);
+static int confnode_if_dotconfig_write(const struct confnode *n, FILE *f);
+static const char *confnode_if_get_type_str(const struct confnode *n);
+static void confnode_if_display_short(const struct confnode *n);
+static void confnode_if_display_long(const struct confnode *n);
+
+static struct confnode_type confnode_if = {
+       .ops = {
+               .new = confnode_if_new,
+               .free = NULL,
+               .add_attr = NULL,
+               .close_dir = confnode_if_close_dir,
+               .dotconfig_write = confnode_if_dotconfig_write,
+               .strvalue_to_boolvalue = NULL,
+               .set_user_strvalue = NULL,
+               .get_user_strvalue = NULL,
+               .set_default_strvalue = NULL,
+               .get_default_strvalue = NULL,
+               .get_type_str = confnode_if_get_type_str,
+               .display_short = confnode_if_display_short,
+               .display_long = confnode_if_display_long,
+       }
+};
+
+/* Create a new node */
+static int confnode_if_new(struct confnode *n, const struct line *line)
+{
+       struct token *tok;
+       tok = TAILQ_FIRST(&line->token_list);
+
+       if (strcmp(tok->str, "if") || line->n_token != 2)
+               return -1;
+
+       tok = TAILQ_NEXT(tok, next);
+       /* XXX only supports an OPTNAME, not an expression */
+       /* condition is stored in value field, not in name */
+       snprintf(n->value, sizeof(n->value), "%s", tok->str);
+       n->ops = &confnode_if.ops;
+       n->flags = CONFNODE_F_INVISIBLE |
+               CONFNODE_F_FORCE_CHILD_DEPS |
+               CONFNODE_F_NO_NAME |
+               CONFNODE_F_IS_DIR; /* XXX */
+       return 0;
+}
+
+/* Parse a line and check if it closes the current node ,
+ * "endmenu" closes "menu" node): in this case return 0. Else, return
+ * -1. */
+static int confnode_if_close_dir(struct confnode *parent,
+                                const struct line *line)
+{
+       struct token *tok;
+
+       tok = TAILQ_FIRST(&line->token_list);
+       if (tok == NULL)
+               return -1; /* should not happen */
+
+       /* syntax is: endif */
+       if (!strcmp(tok->str, "endif") && line->n_token == 1) {
+               //printf("endif\n");
+               return 0;
+       }
+       return -1;
+}
+
+/* write config value in file f in a dotconfig-like manner. Return -1
+ * on error. */
+static int confnode_if_dotconfig_write(const struct confnode *n, FILE *f)
+{
+       const struct confnode *c;
+       int val;
+
+       val = confnode_get_value(n, NULL, 0);
+       if (val < 0)
+               return -1;
+
+       /* condition is wrong, nothing to dump */
+       if (val == 0)
+               return 0;
+
+       /* else, just dump children */
+       TAILQ_FOREACH(c, &n->children, next) {
+               if (confnode_dotconfig_write(c, f) < 0)
+                       return -1;
+       }
+       return 0;
+}
+
+/* Return a string identifying the node type ("config", "menuconfig",
+ * "choice", ...) */
+static const char *confnode_if_get_type_str(const struct confnode *n)
+{
+       return "if";
+}
+
+/* Print a one-line summary of the node. Used in case of 'ls'. */
+static void confnode_if_display_short(const struct confnode *n)
+{
+       /* nothing */
+}
+
+/* Print a detailed view of the node. */
+static void confnode_if_display_long(const struct confnode *n)
+{
+       /* nothing */
+}
+
+/* register the node type */
+void confnode_if_register(void)
+{
+       TAILQ_INSERT_TAIL(&confnode_type_list, &confnode_if, next);
+}
diff --git a/src/genconf/confnode_intconfig.c b/src/genconf/confnode_intconfig.c
new file mode 100644 (file)
index 0000000..9c31664
--- /dev/null
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 2010, 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 <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <sys/queue.h>
+
+#include "expression.h"
+#include "conf_parser.h"
+#include "conf_htable.h"
+#include "parser_common.h"
+#include "dotconfig.h"
+#include "confnode.h"
+
+static int confnode_intconfig_new(struct confnode *n, const struct line *line);
+static int confnode_intconfig_set_user_strvalue(struct confnode *n,
+                                             const char *strvalue);
+static int confnode_intconfig_get_user_strvalue(const struct confnode *n, char *buf,
+                                             unsigned buflen);
+static int confnode_intconfig_set_default_strvalue(struct confnode *n,
+                                             const char *strvalue);
+static int confnode_intconfig_get_default_strvalue(const struct confnode *n, char *buf,
+                                             unsigned buflen);
+static const char *confnode_intconfig_get_type_str(const struct confnode *n);
+static void confnode_intconfig_display_short(const struct confnode *n);
+
+static struct confnode_type confnode_intconfig = {
+       .ops = {
+               .new = confnode_intconfig_new,
+               .free = NULL,
+               .add_attr = NULL, /* XXX range, displayhex */
+               .close_dir = NULL,
+               .dotconfig_write = NULL,
+               .strvalue_to_boolvalue = NULL,
+               .set_user_strvalue = confnode_intconfig_set_user_strvalue,
+               .get_user_strvalue = confnode_intconfig_get_user_strvalue,
+               .set_default_strvalue = confnode_intconfig_set_default_strvalue,
+               .get_default_strvalue = confnode_intconfig_get_default_strvalue,
+               .get_type_str = confnode_intconfig_get_type_str,
+               .display_short = confnode_intconfig_display_short,
+               .display_long = NULL,
+       }
+};
+
+/* Create a new node */
+static int confnode_intconfig_new(struct confnode *n, const struct line *line)
+{
+       struct token *tok;
+       tok = TAILQ_FIRST(&line->token_list);
+
+       if (strcmp(tok->str, "intconfig") || line->n_token != 2)
+               return -1;
+
+       tok = TAILQ_NEXT(tok, next);
+       snprintf(n->name, sizeof(n->name), "%s", tok->str);
+       snprintf(n->default_value, sizeof(n->default_value), "0");
+
+       n->ops = &confnode_intconfig.ops;
+       n->flags = CONFNODE_F_INT;
+       snprintf(n->default_value, sizeof(n->default_value), "0");
+       return 0;
+}
+
+/* Set the string value of the node n. Return 0 on success, or -1 if
+ * the value cannot be set. */
+static int confnode_intconfig_set_user_strvalue(struct confnode *n,
+                                               const char *strvalue)
+{
+       char *end = NULL;
+       int val;
+       /* XXX support hex and bin */
+       val = strtoul(strvalue, &end, 0);
+       if ((strvalue[0] == '\0') || (end == NULL) || (*end != '\0'))
+               return -1;
+       snprintf(n->value, sizeof(n->value), "%s", strvalue);
+       return 0;
+}
+
+/* Get the string value that the user asked to set for this node
+ * 'n'. This function does not check if dependancies are met. Return 0
+ * on success, in this case the string is copied in buffer 'buf' of
+ * len 'buflen'. Return -1 if the value cannot be read. */
+static int confnode_intconfig_get_user_strvalue(const struct confnode *n, char *buf,
+                                               unsigned buflen)
+{
+       if (strcmp(n->value, "") == 0)
+               return -1;
+       snprintf(buf, buflen, "%s", n->value);
+       return 0;
+}
+
+/* Set the default string value of the node n. Return 0 on success, or
+ * -1 if the value cannot be set. */
+static int confnode_intconfig_set_default_strvalue(struct confnode *n,
+                                             const char *strvalue)
+{
+       char *end = NULL;
+       int val;
+       /* XXX support hex and bin */
+       val = strtoul(strvalue, &end, 0);
+       if ((strvalue[0] == '\0') || (end == NULL) || (*end != '\0'))
+               return -1;
+       snprintf(n->default_value, sizeof(n->default_value), "%s", strvalue);
+       return 0;
+}
+
+/* Get the default string value for this node 'n'. This function does
+ * not check if dependancies are met. Return 0 on success, in this
+ * case the string is copied in buffer 'buf' of len 'buflen'. Return
+ * -1 if the value cannot be read. */
+static int confnode_intconfig_get_default_strvalue(const struct confnode *n, char *buf,
+                                             unsigned buflen)
+{
+       if (strcmp(n->default_value, "") == 0)
+               return -1;
+       snprintf(buf, buflen, "%s", n->default_value);
+       return 0;
+}
+
+/* Return a string identifying the node type ("config", "menuconfig",
+ * "choice", ...) */
+static const char *confnode_intconfig_get_type_str(const struct confnode *n)
+{
+       return "intconfig";
+}
+
+/* Print a one-line summary of the node. Used in case of 'ls'. */
+static void confnode_intconfig_display_short(const struct confnode *n)
+{
+       int val;
+       val = confnode_get_value(n, NULL, 0);
+       printf("[%4d] %s: %s\n", val, n->name, n->prompt);
+}
+
+/* register the node type */
+void confnode_intconfig_register(void)
+{
+       TAILQ_INSERT_TAIL(&confnode_type_list, &confnode_intconfig, next);
+}
diff --git a/src/genconf/confnode_menu.c b/src/genconf/confnode_menu.c
new file mode 100644 (file)
index 0000000..4eafa4b
--- /dev/null
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 2010, 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 <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <sys/queue.h>
+
+#include "expression.h"
+#include "conf_parser.h"
+#include "conf_htable.h"
+#include "parser_common.h"
+#include "dotconfig.h"
+#include "confnode.h"
+
+static int confnode_menu_new(struct confnode *n, const struct line *line);
+static int confnode_menu_close_dir(struct confnode *parent,
+                                  const struct line *line);
+static int confnode_menu_dotconfig_write(const struct confnode *n, FILE *f);
+static const char *confnode_menu_get_type_str(const struct confnode *n);
+static void confnode_menu_display_short(const struct confnode *n);
+static void confnode_menu_display_long(const struct confnode *n);
+
+static struct confnode_type confnode_menu = {
+       .ops = {
+               .new = confnode_menu_new,
+               .free = NULL,
+               .add_attr = NULL,
+               .close_dir = confnode_menu_close_dir,
+               .dotconfig_write = confnode_menu_dotconfig_write,
+               .strvalue_to_boolvalue = NULL,
+               .set_user_strvalue = NULL,
+               .get_user_strvalue = NULL,
+               .set_default_strvalue = NULL,
+               .get_default_strvalue = NULL,
+               .get_type_str = confnode_menu_get_type_str,
+               .display_short = confnode_menu_display_short,
+               .display_long = confnode_menu_display_long,
+       }
+};
+
+/* Create a new node */
+static int confnode_menu_new(struct confnode *n, const struct line *line)
+{
+       struct token *tok;
+       tok = TAILQ_FIRST(&line->token_list);
+
+       if (strcmp(tok->str, "menu") || line->n_token != 2)
+               return -1;
+
+       tok = TAILQ_NEXT(tok, next);
+       snprintf(n->name, sizeof(n->name), "%s", tok->str);
+       n->ops = &confnode_menu.ops;
+       n->flags = CONFNODE_F_IS_DIR;
+       return 0;
+}
+
+/* Parse a line and check if it closes the current node ,
+ * "endmenu" closes "menu" node): in this case return 0. Else, return
+ * -1. */
+static int confnode_menu_close_dir(struct confnode *parent,
+                                  const struct line *line)
+{
+       struct token *tok;
+
+       tok = TAILQ_FIRST(&line->token_list);
+       if (tok == NULL)
+               return -1; /* should not happen */
+
+       /* syntax is: endmenu */
+       if (!strcmp(tok->str, "endmenu") && line->n_token == 1) {
+               //printf("endmenu\n");
+               return 0;
+       }
+       return -1;
+}
+
+/* write config value in file f in a dotconfig-like manner. Return 0
+ * on success. */
+static int confnode_menu_dotconfig_write(const struct confnode *n, FILE *f)
+{
+       const struct confnode *c;
+       int val;
+
+       val = confnode_get_value(n, NULL, 0);
+       if (val < 0)
+               return -1;
+       if (val == 0)
+               return 0;
+
+       TAILQ_FOREACH(c, &n->children, next) {
+               if (confnode_dotconfig_write(c, f) < 0)
+                       return -1;
+       }
+       return 0;
+}
+
+/* Return a string identifying the node type ("config", "menuconfig",
+ * "choice", ...) */
+static const char *confnode_menu_get_type_str(const struct confnode *n)
+{
+       return "menu";
+}
+
+/* Print a one-line summary of the node. Used in case of 'ls'. */
+static void confnode_menu_display_short(const struct confnode *n)
+{
+       printf("    -> %s: %s\n", n->name, n->prompt);
+}
+
+/* Print a detailed view of the node. */
+static void confnode_menu_display_long(const struct confnode *n)
+{
+       printf("menu <%s>\n", n->name);
+       printf("    path ");
+       conf_display_path(n);
+       printf("    \"%s\"\n", n->prompt);
+       printf("    no value\n");
+}
+
+/* register the node type */
+void confnode_menu_register(void)
+{
+       TAILQ_INSERT_TAIL(&confnode_type_list, &confnode_menu, next);
+}
diff --git a/src/genconf/confnode_menuconfig.c b/src/genconf/confnode_menuconfig.c
new file mode 100644 (file)
index 0000000..15a5d51
--- /dev/null
@@ -0,0 +1,198 @@
+/*
+ * Copyright (c) 2010, 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 <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <sys/queue.h>
+
+#include "expression.h"
+#include "conf_parser.h"
+#include "conf_htable.h"
+#include "parser_common.h"
+#include "dotconfig.h"
+#include "confnode.h"
+
+static int confnode_menuconfig_new(struct confnode *n, const struct line *line);
+static int confnode_menuconfig_close_dir(struct confnode *parent,
+                                        const struct line *line);
+static int confnode_menuconfig_set_user_strvalue(struct confnode *n,
+                                             const char *strvalue);
+static int confnode_menuconfig_get_user_strvalue(const struct confnode *n, char *buf,
+                                             unsigned buflen);
+static int confnode_menuconfig_set_default_strvalue(struct confnode *n,
+                                             const char *strvalue);
+static int confnode_menuconfig_get_default_strvalue(const struct confnode *n, char *buf,
+                                             unsigned buflen);
+static const char *confnode_menuconfig_get_type_str(const struct confnode *n);
+static void confnode_menuconfig_display_short(const struct confnode *n);
+
+static struct confnode_type confnode_menuconfig = {
+       .ops = {
+               .new = confnode_menuconfig_new,
+               .free = NULL,
+               .add_attr = NULL,
+               .close_dir = confnode_menuconfig_close_dir,
+               .dotconfig_write = NULL,
+               .strvalue_to_boolvalue = NULL,
+               .set_user_strvalue = confnode_menuconfig_set_user_strvalue,
+               .get_user_strvalue = confnode_menuconfig_get_user_strvalue,
+               .set_default_strvalue = confnode_menuconfig_set_default_strvalue,
+               .get_default_strvalue = confnode_menuconfig_get_default_strvalue,
+               .get_type_str = confnode_menuconfig_get_type_str,
+               .display_short = confnode_menuconfig_display_short,
+               .display_long = NULL,
+       }
+};
+
+/* Create a new node */
+static int confnode_menuconfig_new(struct confnode *n, const struct line *line)
+{
+       struct token *tok;
+       tok = TAILQ_FIRST(&line->token_list);
+
+       if (strcmp(tok->str, "menuconfig") || line->n_token != 2)
+               return -1;
+
+       tok = TAILQ_NEXT(tok, next);
+       snprintf(n->name, sizeof(n->name), "%s", tok->str);
+       n->ops = &confnode_menuconfig.ops;
+       n->flags = CONFNODE_F_IS_DIR |
+               CONFNODE_F_FORCE_CHILD_DEPS |
+               CONFNODE_F_BOOL;
+       return 0;
+}
+
+/* Parse a line and check if it closes the current node ,
+ * "endmenu" closes "menu" node): in this case return 0. Else, return
+ * -1. */
+static int confnode_menuconfig_close_dir(struct confnode *parent,
+                                        const struct line *line)
+{
+       struct token *tok;
+
+       tok = TAILQ_FIRST(&line->token_list);
+       if (tok == NULL)
+               return -1; /* should not happen */
+
+       /* syntax is: endmenuconfig */
+       if (!strcmp(tok->str, "endmenuconfig") && line->n_token == 1) {
+               //printf("endmenuconfig\n");
+               return 0;
+       }
+       return -1;
+}
+
+/* Set the string value of the node n. Return 0 on success, or -1 if
+ * the value cannot be set. */
+static int confnode_menuconfig_set_user_strvalue(struct confnode *n,
+                                             const char *strvalue)
+{
+       if (strcmp(strvalue, "y") && strcmp(strvalue, "n"))
+               return -1;
+       snprintf(n->value, sizeof(n->value), "%s", strvalue);
+       return 0;
+}
+
+/* Get the string value that the user asked to set for this node
+ * 'n'. This function does not check if dependancies are met. Return 0
+ * on success, in this case the string is copied in buffer 'buf' of
+ * len 'buflen'. Return -1 if the value cannot be read. */
+static int confnode_menuconfig_get_user_strvalue(const struct confnode *n, char *buf,
+                                             unsigned buflen)
+{
+       if (strcmp(n->value, "") == 0)
+               return -1;
+       if (!strcmp(n->value, "y")) {
+               snprintf(buf, buflen, "y");
+               return 0;
+       }
+       else {
+               snprintf(buf, buflen, "n");
+               return 0;
+       }
+}
+
+/* Set the default string value of the node n. Return 0 on success, or
+ * -1 if the value cannot be set. */
+static int confnode_menuconfig_set_default_strvalue(struct confnode *n,
+                                             const char *strvalue)
+{
+       if (strcmp(strvalue, "y") && strcmp(strvalue, "n"))
+               return -1;
+       snprintf(n->default_value, sizeof(n->default_value), "%s", strvalue);
+       return 0;
+}
+
+/* Get the default string value for this node 'n'. This function does
+ * not check if dependancies are met. Return 0 on success, in this
+ * case the string is copied in buffer 'buf' of len 'buflen'. Return
+ * -1 if the value cannot be read. */
+static int confnode_menuconfig_get_default_strvalue(const struct confnode *n, char *buf,
+                                             unsigned buflen)
+{
+       if (!strcmp(n->default_value, "y")) {
+               snprintf(buf, buflen, "y");
+               return 0;
+       }
+       else {
+               snprintf(buf, buflen, "n");
+               return 0;
+       }
+}
+
+/* Return a string identifying the node type ("config", "menuconfig",
+ * "choice", ...) */
+static const char *confnode_menuconfig_get_type_str(const struct confnode *n)
+{
+       return "menuconfig";
+}
+
+/* Print a one-line summary of the node. Used in case of 'ls'. */
+static void confnode_menuconfig_display_short(const struct confnode *n)
+{
+       char c;
+       if (confnode_get_boolvalue(n) == 0) {
+               if (confnode_get_user_boolvalue(n) <= 0)
+                       c = ' ';
+               else
+                       c = 'd';
+       }
+       else {
+               c = '*';
+       }
+
+       printf("[%c] -> %s: %s\n", c, n->name, n->prompt);
+}
+
+/* register the node type */
+void confnode_menuconfig_register(void)
+{
+       TAILQ_INSERT_TAIL(&confnode_type_list, &confnode_menuconfig, next);
+}
diff --git a/src/genconf/confnode_root.c b/src/genconf/confnode_root.c
new file mode 100644 (file)
index 0000000..6d3fbe8
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2010, 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 <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <sys/queue.h>
+
+#include "expression.h"
+#include "conf_parser.h"
+#include "conf_htable.h"
+#include "parser_common.h"
+#include "dotconfig.h"
+#include "confnode.h"
+
+static int confnode_root_new(struct confnode *n,
+                            const struct line *line);
+
+static struct confnode_type confnode_root = {
+       .ops = {
+               .new = confnode_root_new,
+               .free = NULL,
+               .add_attr = NULL,
+               .close_dir = NULL,
+               .dotconfig_write = NULL,
+               .strvalue_to_boolvalue = NULL,
+               .set_user_strvalue = NULL,
+               .get_user_strvalue = NULL,
+               .set_default_strvalue = NULL,
+               .get_default_strvalue = NULL,
+               .get_type_str = NULL,
+               .display_short = NULL,
+               .display_long = NULL,
+       }
+};
+
+/* Create a new node */
+static int confnode_root_new(struct confnode *n, const struct line *line)
+{
+       struct token *tok;
+       tok = TAILQ_FIRST(&line->token_list);
+
+       if (strcmp(tok->str, "root") || line->n_token != 1)
+               return -1;
+
+       snprintf(n->name, sizeof(n->name), "root");
+       n->ops = &confnode_root.ops;
+       n->flags = CONFNODE_F_IS_ROOT;
+       return 0;
+}
+
+/* register the node type */
+void confnode_root_register(void)
+{
+       TAILQ_INSERT_TAIL(&confnode_type_list, &confnode_root, next);
+}
diff --git a/src/genconf/confnode_strconfig.c b/src/genconf/confnode_strconfig.c
new file mode 100644 (file)
index 0000000..c6dbcd2
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 2010, 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 <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <sys/queue.h>
+
+#include "expression.h"
+#include "conf_parser.h"
+#include "conf_htable.h"
+#include "parser_common.h"
+#include "dotconfig.h"
+#include "confnode.h"
+
+static int confnode_strconfig_new(struct confnode *n, const struct line *line);
+static int confnode_strconfig_strvalue_to_boolvalue(const struct confnode *n,
+                                                   const char *strvalue);
+static int confnode_strconfig_set_user_strvalue(struct confnode *n,
+                                             const char *strvalue);
+static int confnode_strconfig_get_user_strvalue(const struct confnode *n, char *buf,
+                                             unsigned buflen);
+static int confnode_strconfig_set_default_strvalue(struct confnode *n,
+                                             const char *strvalue);
+static int confnode_strconfig_get_default_strvalue(const struct confnode *n, char *buf,
+                                             unsigned buflen);
+static const char *confnode_strconfig_get_type_str(const struct confnode *n);
+static void confnode_strconfig_display_short(const struct confnode *n);
+
+static struct confnode_type confnode_strconfig = {
+       .ops = {
+               .new = confnode_strconfig_new,
+               .free = NULL,
+               .add_attr = NULL,
+               .close_dir = NULL,
+               .dotconfig_write = NULL,
+               .strvalue_to_boolvalue = confnode_strconfig_strvalue_to_boolvalue,
+               .set_user_strvalue = confnode_strconfig_set_user_strvalue,
+               .get_user_strvalue = confnode_strconfig_get_user_strvalue,
+               .set_default_strvalue = confnode_strconfig_set_default_strvalue,
+               .get_default_strvalue = confnode_strconfig_get_default_strvalue,
+               .get_type_str = confnode_strconfig_get_type_str,
+               .display_short = confnode_strconfig_display_short,
+               .display_long = NULL,
+       }
+};
+
+/* Create a new node */
+static int confnode_strconfig_new(struct confnode *n, const struct line *line)
+{
+       struct token *tok;
+       tok = TAILQ_FIRST(&line->token_list);
+
+       if (strcmp(tok->str, "strconfig") || line->n_token != 2)
+               return -1;
+
+       tok = TAILQ_NEXT(tok, next);
+       snprintf(n->name, sizeof(n->name), "%s", tok->str);
+       n->ops = &confnode_strconfig.ops;
+       n->flags = CONFNODE_F_STR |
+               CONFNODE_F_QUOTE_VALUE;
+       n->default_value[0] = '\0';
+       return 0;
+}
+
+/* Convert string value into boolean value (mainly used for
+ * dependancies). Return -1 on error, else return the boolean value (0
+ * or 1). */
+static int confnode_strconfig_strvalue_to_boolvalue(const struct confnode *n,
+                                                   const char *strvalue)
+{
+       if (strcmp("", strvalue) == 0)
+               return 0;
+       return 1;
+}
+
+/* Set the string value of the node n. Return 0 on success, or -1 if
+ * the value cannot be set. */
+static int confnode_strconfig_set_user_strvalue(struct confnode *n,
+                                             const char *strvalue)
+{
+       snprintf(n->value, sizeof(n->value), "%s", strvalue);
+       return 0;
+}
+
+/* Get the string value that the user asked to set for this node
+ * 'n'. This function does not check if dependancies are met. Return 0
+ * on success, in this case the string is copied in buffer 'buf' of
+ * len 'buflen'. Return -1 if the value cannot be read. */
+static int confnode_strconfig_get_user_strvalue(const struct confnode *n, char *buf,
+                                             unsigned buflen)
+{
+       snprintf(buf, buflen, "%s", n->value);
+       return 0;
+}
+
+/* Set the default string value of the node n. Return 0 on success, or
+ * -1 if the value cannot be set. */
+static int confnode_strconfig_set_default_strvalue(struct confnode *n,
+                                             const char *strvalue)
+{
+       snprintf(n->default_value, sizeof(n->default_value), "%s", strvalue);
+       return 0;
+}
+
+/* Get the default string value for this node 'n'. This function does
+ * not check if dependancies are met. Return 0 on success, in this
+ * case the string is copied in buffer 'buf' of len 'buflen'. Return
+ * -1 if the value cannot be read. */
+static int confnode_strconfig_get_default_strvalue(const struct confnode *n, char *buf,
+                                             unsigned buflen)
+{
+       snprintf(buf, buflen, "%s", n->default_value);
+       return 0;
+}
+
+/* Return a string identifying the node type ("config", "menuconfig",
+ * "choice", ...) */
+static const char *confnode_strconfig_get_type_str(const struct confnode *n)
+{
+       return "strconfig";
+}
+
+/* Print a one-line summary of the node. Used in case of 'ls'. */
+static void confnode_strconfig_display_short(const struct confnode *n)
+{
+       /* just display prompt */
+       printf("    %s\n", n->prompt);
+}
+
+/* register the node type */
+void confnode_strconfig_register(void)
+{
+       TAILQ_INSERT_TAIL(&confnode_type_list, &confnode_strconfig, next);
+}
diff --git a/src/genconf/dotconfig.c b/src/genconf/dotconfig.c
new file mode 100644 (file)
index 0000000..b6c496e
--- /dev/null
@@ -0,0 +1,142 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <sys/queue.h>
+
+#include "expression.h"
+#include "parser_common.h"
+#include "conf_parser.h"
+#include "confnode.h"
+#include "conf_htable.h"
+
+int dotconfig_parse_line(const char *line)
+{
+       struct confnode *opt;
+       char buf[BUFSIZ];
+       char *nodename, *s;
+       const char *optval;
+       char wait_char = '=';
+
+       /* copy line in a writable buffer */
+       buf[sizeof(buf) - 1] = '\0';
+       strncpy(buf, line, sizeof(buf) - 1);
+       s = buf;
+
+       while (*s != '\0' && isspace(*s))
+               s++;
+       /* nothing to do, empty line */
+       if (*s == '\0')
+               return 0;
+
+       /* skip the first "#", expecting "is not set" */
+       if (*s == '#') {
+               wait_char = ' ';
+               s++;
+               while (*s != '\0' && isspace(*s))
+                       s++;
+               /* nothing to do, empty line */
+               if (*s == '\0')
+                       return 0;
+       }
+
+       /* get node name, ignore invalid names */
+       nodename = s;
+       if (strncmp("CONFIG_", nodename, strlen("CONFIG_")))
+               return 0;
+       nodename += strlen("CONFIG_");
+
+       while (*s != '\0' && *s != wait_char)
+               s++;
+       /* invalid line */
+       if (*s != wait_char)
+               return -1;
+
+       *s = '\0';
+
+       /* find the node in the conf htable */
+       opt = conf_htable_lookup(nodename);
+       if (opt == NULL) {
+               printf("ignore unknown symbol <%s>\n", nodename);
+               return 0;
+       }
+
+       s++;
+
+       /* "is not set" -> "n" */
+       if (wait_char == ' ') {
+               if (strcmp(s, "is not set\n"))
+                       return 0;
+               optval = "n";
+       }
+       else {
+               if (*s == '"')
+                       s ++;
+               optval = s;
+               /* erase the cariage return */
+               while (*s != '\0' && *s != '\n') {
+                       if (*s == '"' && *(s-1) != '\\') {
+                               *s = '\0';
+                               break;
+                       }
+                       s++;
+               }
+               *s = '\0';
+       }
+
+       /* set its value in the conf tree */
+       if (confnode_set_user_strvalue(opt, optval) < 0) {
+               printf("cannot assign value to node <%s>: %s\n", nodename, optval);
+               return -1;
+       }
+
+       return 0;
+}
+
+/* parse .config */
+int dotconfig_read(const char *filename, struct confnode *conf)
+{
+       FILE *f;
+       char buf[BUFSIZ];
+       int err = 0;
+
+       f = fopen(filename, "r");
+       if (f == NULL) {
+               printf("cannot find file <%s>\n", filename);
+               return -1;
+       }
+       while (fgets(buf, sizeof(buf), f) != NULL) {
+
+               if (dotconfig_parse_line(buf) < 0) {
+                       err = -1;
+                       break;
+               }
+       }
+       fclose(f);
+
+       return err;
+}
+
+/* write .config */
+int dotconfig_write(const char *filename, const struct confnode *conf)
+{
+       FILE *f;
+       const struct confnode *c;
+
+       f = fopen(filename, "w");
+       if (f == NULL) {
+               printf("cannot open file <%s>\n", filename);
+               return -1;
+       }
+
+       /* dump all the children of root node */
+       TAILQ_FOREACH(c, &conf->children, next) {
+               confnode_dotconfig_write(c, f);
+       }
+
+       fclose(f);
+
+       return 0;
+}
+
diff --git a/src/genconf/dotconfig.h b/src/genconf/dotconfig.h
new file mode 100644 (file)
index 0000000..8975f59
--- /dev/null
@@ -0,0 +1,2 @@
+int dotconfig_read(const char *filename, struct confnode *conf);
+int dotconfig_write(const char *filename, const struct confnode *conf);
diff --git a/src/genconf/expression.c b/src/genconf/expression.c
new file mode 100644 (file)
index 0000000..91a3a1b
--- /dev/null
@@ -0,0 +1,397 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <sys/queue.h>
+
+#include "strictmalloc.h"
+#include "expression.h"
+#include "conf_parser.h"
+#include "confnode.h"
+#include "conf_htable.h"
+
+/* XXX prefix all with "expression_" */
+
+struct expr_node *node_create(void)
+{
+       struct expr_node *n;
+       n = strictmalloc(sizeof(struct expr_node));
+       return n;
+}
+
+/* free an expression */
+void expression_free(struct expr_node *exp)
+{
+       if (exp == NULL)
+               return;
+
+       if (exp->left) {
+               expression_free(exp->left);
+               exp->left = NULL;
+       }
+
+       if (exp->right) {
+               expression_free(exp->right);
+               exp->right = NULL;
+       }
+
+       free(exp);
+}
+
+const char *op_print(const struct expr_op *op)
+{
+       switch (op->optype) {
+       case OP_OR:
+               return "||";
+       case OP_AND:
+               return "&&";
+       case OP_EQUAL:
+               return "=";
+       case OP_OBRACKET:
+               return "(";
+       case OP_NOT:
+               return "!";
+       case OP_CBRACKET:
+               return ")";
+       case OP_VAR:
+               return op->name;
+       default:
+               return NULL;
+       }
+}
+
+void __dump(struct expr_node *n)
+{
+       if (n == NULL)
+               return;
+
+       if (n->left) {
+               printf("\"%p <%s>\" -> \"%p <%s>\"\n",
+                      n, op_print(&n->op),
+                      n->left, op_print(&n->left->op));
+               __dump(n->left);
+       }
+
+       if (n->right) {
+               printf("\"%p <%s>\" -> \"%p <%s>\"\n",
+                      n, op_print(&n->op),
+                      n->right, op_print(&n->right->op));
+               __dump(n->right);
+       }
+}
+
+void dump(struct expr_node *n)
+{
+       printf("digraph unix {\n"
+              "size=\"6,6\";\n"
+              "node [color=lightblue2, style=filled];\n");
+       __dump(n);
+       printf("}\n");
+}
+
+int isbiop(enum op_type optype)
+{
+       if (optype == OP_AND ||
+           optype == OP_OR ||
+           optype == OP_EQUAL)
+               return 1;
+       return 0;
+}
+
+/*
+ * Dump the expression 'node' as a string into the buffer 'buf' of
+ * length 'len'. The string is nul-terminated. Return the number of
+ * written bytes on success (not including \0), else return -1.
+ */
+int expression_to_str(struct expr_node *node, char *buf, int len)
+{
+       int n, orig_len;
+
+       orig_len = len;
+
+       if (isbiop(node->op.optype) || node->op.optype == OP_OBRACKET) {
+               n = snprintf(buf, len, "(");
+               if (n == -1 || n >= len || len <= 0)
+                       return -1;
+               buf += n;
+               len -= n;
+       }
+
+       if (isbiop(node->op.optype)) {
+               n = expression_to_str(node->left, buf, len);
+               if (n == -1 || n >= len || len <= 0)
+                       return -1;
+               buf += n;
+               len -= n;
+       }
+
+       if (node->op.optype != OP_OBRACKET) {
+               if (isbiop(node->op.optype))
+                       n = snprintf(buf, len, " %s ", op_print(&node->op));
+               else
+                       n = snprintf(buf, len, "%s", op_print(&node->op));
+               if (n == -1 || n >= len || len <= 0)
+                       return -1;
+               buf += n;
+               len -= n;
+       }
+
+       if (node->op.optype != OP_VAR) {
+               n = expression_to_str(node->right, buf, len);
+               if (n == -1 || n >= len || len <= 0)
+                       return -1;
+               buf += n;
+               len -= n;
+       }
+
+       if (isbiop(node->op.optype) || node->op.optype == OP_OBRACKET) {
+               n = snprintf(buf, len, ")");
+               if (n == -1 || n >= len || len <= 0)
+                       return -1;
+               buf += n;
+               len -= n;
+       }
+
+       return orig_len - len;
+}
+
+/*
+ * Evaluate an expression. Return -1 on error, else the value of the
+ * expression (greater or equal than 0).
+ */
+int expression_eval(struct expr_node *node)
+{
+       struct confnode *conf;
+
+       switch (node->op.optype) {
+       case OP_OR:
+               return expression_eval(node->left) || expression_eval(node->right);
+       case OP_AND:
+               return expression_eval(node->left) && expression_eval(node->right);
+       case OP_EQUAL:
+               return expression_eval(node->left) == expression_eval(node->right);
+       case OP_OBRACKET:
+               return expression_eval(node->right);
+       case OP_NOT:
+               return !expression_eval(node->right);
+       case OP_VAR:
+                conf = conf_htable_lookup(node->op.name);
+                if (conf == NULL)
+                        return 0;
+                return confnode_get_boolvalue(conf);
+       default:
+               printf("%s(): bad operator\n", __FUNCTION__);
+               return 0;
+       }
+}
+
+int get_brac_len(const char *buf)
+{
+       const char *s = buf;
+       int i = 1;
+
+       if (*s != '(')
+               return -1;
+       s++;
+
+       while(*s != '\0' && i != 0) {
+               if (*s == ')')
+                       i--;
+               if (*s == '(')
+                       i++;
+               s++;
+       }
+       if (i != 0)
+               return -1;
+       return s-buf;
+}
+
+/*
+ * XXX
+ */
+ static struct expr_op *get_operand(const char *buf, unsigned *eatlen)
+{
+       struct expr_op *op;
+       const char *s;
+       unsigned len;
+
+       //printf("%s\n", buf);
+       op = strictmalloc(sizeof(struct expr_op));
+
+       s = buf;
+
+       switch (s[0]) {
+       case '#':
+               op->optype = OP_EOF;
+               *eatlen = s - buf + 1;
+               return op;
+       case '\0':
+               op->optype = OP_EOF;
+               *eatlen = s - buf + 1;
+               return op;
+       case '(':
+               op->optype = OP_OBRACKET;
+               *eatlen = s - buf + 1;
+               return op;
+       case ')':
+               op->optype = OP_CBRACKET;
+               *eatlen = s - buf + 1;
+               return op;
+       case '!':
+               op->optype = OP_NOT;
+               *eatlen = s - buf + 1;
+               return op;
+       case '&':
+               op->optype = OP_AND;
+               if (s[1] != '&') {
+                       free(op);
+                       return NULL;
+               }
+               *eatlen = s - buf + 2;
+               return op;
+       case '|':
+               op->optype = OP_OR;
+               if (s[1] != '|') {
+                       free(op);
+                       return NULL;
+               }
+               *eatlen = s - buf + 2;
+               return op;
+       case '=':
+               op->optype = OP_EQUAL;
+               *eatlen = s - buf + 1;
+               return op;
+       default:
+               break;
+       }
+
+       /* It's a variable, get name */
+       while (*s != '\0' &&
+              *s != '(' &&
+              *s != ')' &&
+              *s != '&' &&
+              *s != '|' &&
+              *s != '!' &&
+              *s != '=' &&
+              *s != '#' &&
+              !isspace(*s))
+               s++;
+
+       op->optype = OP_VAR;
+       *eatlen = s - buf;
+
+       /* alloc string for variable name */
+       len = s - buf;
+       op->name = strictmalloc(len+1);
+       memcpy(op->name, buf, len);
+       op->name[len+1] = '\0';
+       return op;
+}
+
+struct expr_node *parse_expression(char *buf)
+{
+       struct expr_node *top = NULL, *n, *tmp;
+       char *s = buf;
+       struct expr_op *op;
+       int len;
+       unsigned eatlen;
+
+       //printf("parse <%s>\n", buf);
+
+       if (*s == '\0')
+               return NULL;
+
+       while (1) {
+
+               /* skip spaces */
+               while (isspace(*s))
+                       s++;
+
+               op = get_operand(s, &eatlen);
+               if (op == NULL) {
+                       printf("Parse error\n");
+                       return NULL; /* XXX */
+               }
+               if (op->optype == OP_EOF)
+                       break;
+
+               //printf("%s\n", op_print(op));
+
+               n = node_create();
+               n->op = *op;
+               free(op);
+
+               switch (n->op.optype) {
+               case OP_OR:
+               case OP_AND:
+               case OP_EQUAL:
+                       if (top == NULL ||
+                           (!isbiop(top->op.optype) ||
+                            top->op.optype > n->op.optype)) {
+                               n->left = top;
+                               top = n;
+                       }
+                       else {
+                               tmp = top->right;
+                               top->right = n;
+                               n->left = tmp;
+                       }
+                       break;
+               case OP_OBRACKET:
+                       len = get_brac_len(s);
+                       if (len < 0) {
+                               printf("Parse error, cannot find closing bracket\n");
+                               return NULL; /* XXX */
+                       }
+                       s[len-1] = '\0';
+                       n->right = parse_expression(s+1);
+                       if (n->right == NULL)
+                               return NULL;
+                       s[len-1] = ')';
+                       s += len - 1;
+
+                       if (top == NULL)
+                               top = n;
+                       else {
+                               tmp = top;
+                               while (tmp->right)
+                                       tmp = tmp->right;
+                               tmp->right = n;
+                       }
+
+                       break;
+               case OP_NOT:
+               case OP_VAR:
+                       if (top == NULL)
+                               top = n;
+                       else {
+                               tmp = top;
+                               while (tmp->right)
+                                       tmp = tmp->right;
+                               tmp->right = n;
+                       }
+                       break;
+               default:
+                       printf("Parse error\n");
+                       return NULL; /* XXX */
+               }
+
+               s += eatlen;
+       }
+
+       return top;
+}
+
+#if 0
+int main(void)
+{
+       char s[] = "!(A&&! (B || CONFIG_C ) )";
+       struct expr_node *n;
+
+       n = parse_expression(s);
+
+       //printf("--------------\n");
+       dump(n);
+       return 0; /* XXX free ! */
+}
+#endif
diff --git a/src/genconf/expression.h b/src/genconf/expression.h
new file mode 100644 (file)
index 0000000..0b8ea50
--- /dev/null
@@ -0,0 +1,33 @@
+#ifndef _EXPRESSION_H_
+#define _EXPRESSION_H_
+
+enum op_type {
+       OP_VAR,
+       OP_OBRACKET,
+       OP_CBRACKET,
+       OP_OR,
+       OP_AND,
+       OP_NOT,
+       OP_EQUAL,
+       OP_EOF,
+};
+
+struct expr_op {
+       enum op_type optype;
+       char *name; /* only valid if op==VAR */
+};
+
+struct expr_node {
+       TAILQ_ENTRY(expr_node) next;
+       struct expr_op op;
+       struct expr_node *left;
+       struct expr_node *right;
+};
+
+struct expr_node *parse_expression(char *buf);
+
+void expression_free(struct expr_node *expression);
+int expression_to_str(struct expr_node *n, char *buf, int len);
+int expression_eval(struct expr_node *node);
+
+#endif /* _EXPRESSION_H_ */
diff --git a/src/genconf/main.c b/src/genconf/main.c
new file mode 100644 (file)
index 0000000..3748710
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * 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 <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <inttypes.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <termios.h>
+#include <sys/queue.h>
+#include <netinet/in.h>
+
+#include <cmdline_rdline.h>
+#include <cmdline_parse.h>
+#include <cmdline.h>
+#include <cmdline_socket.h>
+
+#include "conf_parser.h"
+#include "conf_htable.h"
+
+struct my_cmdline {
+       struct rdline rdl;
+       char prompt[RDLINE_PROMPT_SIZE];
+} cmd;
+
+extern cmdline_parse_ctx_t main_ctx[];
+
+struct confnode *conf_root;
+struct confnode *conf_cur;
+struct cmdline *cl;
+
+/*** main */
+
+void basic_char_loop(void)
+{
+       char c;
+
+       c = -1;
+       /* XXX */
+       while (1) {
+               read(cl->s_in, &c, 1);
+               if (cmdline_in(cl, &c, 1) < 0) {
+                       break;
+               }
+       }
+       cmdline_free(cl);
+       printf("\n");
+}
+
+int main(void)
+{
+       struct termios oldterm, term;
+
+       /* init conf structures */
+       conf_root = conf_reset_and_read(NULL, ".config");
+       if (conf_root == NULL)
+               return -1;
+       conf_cur = conf_root;
+
+       tcgetattr(0, &oldterm);
+       memcpy(&term, &oldterm, sizeof(term));
+       term.c_lflag &= ~(ICANON | ECHO | ISIG);
+       tcsetattr(0, TCSANOW, &term);
+       setbuf(stdin, NULL);
+
+       cl = cmdline_stdin_new(main_ctx, "root> ");
+       basic_char_loop();
+
+       tcsetattr(fileno(stdin), TCSANOW, &oldterm);
+       return 0;
+}
+
+
diff --git a/src/genconf/parse_confnode.c b/src/genconf/parse_confnode.c
new file mode 100644 (file)
index 0000000..b4da9dd
--- /dev/null
@@ -0,0 +1,220 @@
+/*
+ * 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 <stdio.h>
+#include <inttypes.h>
+#include <ctype.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <sys/queue.h>
+
+#include <cmdline_parse_ipaddr.h>
+#include <cmdline_parse.h>
+
+#include "expression.h"
+#include "conf_parser.h"
+#include "confnode.h"
+#include "conf_htable.h"
+#include "parse_confnode.h"
+
+struct cmdline_token_ops token_conf_node_ops = {
+       .parse = parse_conf_node,
+       .complete_get_nb = complete_get_nb_conf_node,
+       .complete_get_elt = complete_get_elt_conf_node,
+       .get_help = get_help_conf_node,
+};
+
+/* return 0 if string matches pattern (string must not contain '*')*/
+static int strcmp_joker(const char *s, const char *pattern)
+{
+       if (s == pattern)
+               return 0;
+
+       while (*s == *pattern && *pattern != '*') {
+               if (*s == '\0')
+                       return 0;
+               s ++;
+               pattern ++;
+       }
+       if (*pattern != '*')
+               return -1;
+       if (*(pattern + 1) == '\0')
+               return 0;
+       while (*s) {
+               if (strcmp_joker(s, pattern + 1) == 0)
+                       return 0;
+               s ++;
+       }
+       return -1;
+}
+
+static void
+find_in_children(struct confnode_list *l, struct confnode *n,
+                const char *pattern, int flags, int mask, int recurs)
+{
+       struct confnode *c;
+
+       TAILQ_FOREACH(c, &n->children, next) {
+               /* if it's a "if" node, parse children */
+               if (c->flags & CONFNODE_F_INVISIBLE)
+                       find_in_children(l, c, pattern, flags, mask, recurs);
+
+               else {
+                       if ((c->flags & mask) == flags &&
+                           !strcmp_joker(c->name, pattern))
+                               TAILQ_INSERT_TAIL(l, c, user_next);
+
+                       if (recurs)
+                               find_in_children(l, c, pattern, flags, mask, recurs);
+               }
+       }
+}
+
+static int
+count_children(struct confnode *n, int flags, int mask)
+{
+       struct confnode *c;
+       int i = 0;
+
+       TAILQ_FOREACH(c, &n->children, next) {
+               /* if it's a "if" node, parse children */
+               if (c->flags & CONFNODE_F_INVISIBLE)
+                       i += count_children(c, flags, mask);
+               else if ((c->flags & mask) == flags)
+                       i ++;
+       }
+       return i;
+}
+
+static struct confnode *
+get_from_idx(struct confnode *n, unsigned int *cur, unsigned int idx,
+            int flags, int mask)
+{
+       struct confnode *c, *tmp;
+
+       TAILQ_FOREACH(c, &n->children, next) {
+               /* if it's a "if" node, parse children */
+               if (c->flags & CONFNODE_F_INVISIBLE) {
+                       tmp = get_from_idx(c, cur, idx, flags, mask);
+                       if (tmp)
+                               return tmp;
+               }
+               else {
+                       if ((c->flags & mask) != flags)
+                               continue;
+                       if (*cur == idx)
+                               return c;
+                       *cur = *cur + 1;
+               }
+       }
+       return NULL;
+}
+
+int
+parse_conf_node(cmdline_parse_token_hdr_t *tk, const char *buf, void *res)
+{
+       struct token_conf_node *tk2 = (struct token_conf_node *)tk;
+       struct token_conf_node_data *tkd = &tk2->conf_node_data;
+       struct confnode *n;
+       struct confnode_list l;
+       unsigned int token_len = 0;
+       char token[BUFSIZ];
+
+       if (*buf == 0)
+               return -1;
+
+       while(!cmdline_isendoftoken(buf[token_len]))
+               token_len++;
+
+       if (token_len > sizeof(token) - 1)
+               return -1;
+
+       memcpy(token, buf, token_len);
+       token[token_len] = '\0';
+
+       TAILQ_INIT(&l);
+
+       /* absolute path */
+       if (token[0] == '/' && token[1] == '/') {
+               /* if contains joker */
+               if (strchr(token + 2, '*'))
+                       find_in_children(&l, *tkd->root, token+2, tkd->flags,
+                                        tkd->mask, 1);
+               else {
+                       n = conf_htable_lookup(token + 2);
+                       if ((n->flags & tkd->mask) != tkd->flags)
+                               return -1;
+                       TAILQ_INSERT_TAIL(&l, n, user_next);
+               }
+       }
+       /* relative path */
+       else
+               find_in_children(&l, *tkd->cur, token, tkd->flags, tkd->mask, 0);
+       if (TAILQ_EMPTY(&l))
+               return -1;
+
+       /* store the address of object in structure */
+       if (res)
+               *(struct confnode **)res = TAILQ_FIRST(&l);
+
+        return token_len;
+}
+
+int complete_get_nb_conf_node(cmdline_parse_token_hdr_t *tk)
+{
+       struct token_conf_node *tk2 = (struct token_conf_node *)tk;
+       struct token_conf_node_data *tkd = &tk2->conf_node_data;
+
+       return count_children(*tkd->cur, tkd->flags, tkd->mask);
+}
+
+int complete_get_elt_conf_node(cmdline_parse_token_hdr_t *tk, int idx,
+                              char *dstbuf, unsigned int size)
+{
+       struct token_conf_node *tk2 = (struct token_conf_node *)tk;
+       struct token_conf_node_data *tkd = &tk2->conf_node_data;
+       struct confnode *n;
+       unsigned int i = 0, len;
+
+       n = get_from_idx(*tkd->cur, &i, idx, tkd->flags, tkd->mask);
+       if (n == NULL)
+               return -1;
+
+       len = strlen(n->name) + 1;
+       if (len > size)
+               return -1;
+
+       strcpy(dstbuf, n->name);
+       return 0;
+}
+
+
+int get_help_conf_node(cmdline_parse_token_hdr_t *tk, char *dstbuf, unsigned int size)
+{
+       snprintf(dstbuf, size, "conf-node");
+       return 0;
+}
diff --git a/src/genconf/parse_confnode.h b/src/genconf/parse_confnode.h
new file mode 100644 (file)
index 0000000..e47f679
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ */
+
+#ifndef _PARSE_CONF_NODE_H_
+#define _PARSE_CONF_NODE_H_
+
+/* data is a 'node **' */
+struct token_conf_node_data {
+       struct confnode **root;
+       struct confnode **cur;
+       int flags;
+       int mask;
+};
+
+struct token_conf_node {
+       struct cmdline_token_hdr hdr;
+       struct token_conf_node_data conf_node_data;
+};
+typedef struct token_conf_node parse_token_conf_node_t;
+
+extern struct cmdline_token_ops token_conf_node_ops;
+
+int parse_conf_node(cmdline_parse_token_hdr_t *tk, const char *srcbuf, void *res);
+int complete_get_nb_conf_node(cmdline_parse_token_hdr_t *tk);
+int complete_get_elt_conf_node(cmdline_parse_token_hdr_t *tk, int idx,
+                             char *dstbuf, unsigned int size);
+int get_help_conf_node(cmdline_parse_token_hdr_t *tk, char *dstbuf, unsigned int size);
+
+#define TOKEN_CONF_NODE_INITIALIZER(structure, field,              \
+                                   root_node_ptr, cur_node_ptr,    \
+                                   node_flags, node_mask)          \
+{                                                                  \
+       .hdr = {                                                    \
+               .ops = &token_conf_node_ops,                        \
+               .offset = offsetof(structure, field),               \
+       },                                                          \
+               .conf_node_data = {                                 \
+               .root = root_node_ptr,                              \
+               .cur = cur_node_ptr,                                \
+               .flags = node_flags,                                \
+               .mask = node_mask,                                  \
+       },                                                          \
+}
+
+#endif /* _PARSE_CONF_NODE_H_ */
diff --git a/src/genconf/parser_common.c b/src/genconf/parser_common.c
new file mode 100644 (file)
index 0000000..0afc552
--- /dev/null
@@ -0,0 +1,53 @@
+#include <ctype.h>
+
+/*
+ * return true if line is empty or contains only spaces/comments
+ */
+int line_is_empty(const char *buf)
+{
+       while (*buf != '\0' && *buf != '#') {
+               if (!isspace(*buf))
+                       return 0;
+               buf++;
+       }
+       return 1;
+}
+
+/* if buf is a string surrounded by simple or double quotes, return 0
+ * and fill *start and *end to the position of quotes. Else, return
+ * -1. */
+int remove_quote(const char *buf, int *start, int *end)
+{
+       const char *c = buf;
+
+       while (isspace(*c))
+               c ++;
+
+       /* if it starts with double quote, remove them */
+       if (c[0] == '"') {
+               *start = c - buf;
+               c ++;
+               while (*c != '\0' ) {
+                       if ((*c == '"') && (*(c-1) != '\\')) {
+                               *end = c - buf;
+                               return 0;
+                       }
+                       c ++;
+               }
+               return -1;
+       }
+       /* if it starts with simple quote, remove them */
+       if (c[0] == '\'') {
+               *start = c - buf;
+               c ++;
+               while (*c != '\0' ) {
+                       if ((*c == '\'') && (*(c-1) != '\\')) {
+                               *end = c - buf;
+                               return 0;
+                       }
+                       c ++;
+               }
+               return -1;
+       }
+       return -1;
+}
diff --git a/src/genconf/parser_common.h b/src/genconf/parser_common.h
new file mode 100644 (file)
index 0000000..80b7857
--- /dev/null
@@ -0,0 +1,9 @@
+/*
+ * return true if line is empty or contains only spaces/comments
+ */
+int line_is_empty(const char *buf);
+
+/* if buf is a string surrounded by simple or double quotes, return 0
+ * and fill *start and *end to the position of quotes. Else, return
+ * -1. */
+int remove_quote(const char *buf, int *start, int *end);
diff --git a/src/genconf/strictmalloc.h b/src/genconf/strictmalloc.h
new file mode 100644 (file)
index 0000000..a296c5f
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2010, 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.
+ */
+
+/* We don't want to check return of malloc, so use this function. If
+ * we lack memory, just exit. By the way, always do a memset on the
+ * allocated memory. */
+static inline void *strictmalloc(size_t size)
+{
+       void *p = malloc(size);
+       if (p == NULL) {
+               printf("Not enough memory, exiting\n");
+               exit(1);
+       }
+       memset(p, 0, size);
+       return p;
+}
diff --git a/src/genconf/todo.txt b/src/genconf/todo.txt
new file mode 100644 (file)
index 0000000..f21ba39
--- /dev/null
@@ -0,0 +1,30 @@
+- dans la commande source, gerer les variable d'env
+- commande: ls [-r] ou tree
+- commande: help command
+- commande: exit
+- commande: check_deps
+- commande: display_new
+- gestion des choice
+- attention aux dependances circulaires: a tester
+- doc d'utilisation
+- scrolling type | more
+- differences avec le kconfig
+- differents modes de fonctionnement (sans cli, auto, ...)
+- cmdline: parser les tokens entre guillemets
+- prendre les commandes dans un fichier plutot que dans
+  la cli, permet de faire un jeu de test complet, et aussi
+  d'appliquer des confs plus schnell un peu Ã  la set_sds_truc
+- verifier qu'il n'y a pas de symbole dupliqué
+- option non accessible par l'utilisateur qui est setté ou non
+  en fonction des deps.
+- couleurs
+- Le chemin du Kconfig de depart dans les parametres
+- Avoir des options pour specifier le comportement: où sauver
+  le .config, couleurs ou non, tty ou non, fichier...
+
+NET: relative to current directory
+NET*IPSEC*: relative to current directory
+//NET: in any directory
+//NET*: in any directory
+
+
diff --git a/src/lib/cmdline.c b/src/lib/cmdline.c
new file mode 100644 (file)
index 0000000..621ce37
--- /dev/null
@@ -0,0 +1,164 @@
+/*
+ * 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 <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <inttypes.h>
+#include <fcntl.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include "cmdline_parse.h"
+#include "cmdline_rdline.h"
+#include "cmdline.h"
+
+
+#define PROMPT_SUFFIX "> "
+
+
+/**********************/
+
+void 
+cmdline_valid_buffer(struct rdline *rdl, const char *buf, unsigned int size) 
+{
+       struct cmdline *cl = rdl->opaque;
+       int ret;
+       ret = cmdline_parse(cl, buf);
+       if (ret == CMDLINE_PARSE_AMBIGUOUS)
+               cmdline_printf(cl, "Ambiguous command\n");
+       else if (ret == CMDLINE_PARSE_NOMATCH)
+               cmdline_printf(cl, "Command not found\n");
+       else if (ret == CMDLINE_PARSE_BAD_ARGS)
+               cmdline_printf(cl, "Bad arguments\n");
+}
+
+int 
+cmdline_complete_buffer(struct rdline *rdl, const char *buf,
+                       char *dstbuf, unsigned int dstsize,
+                       int *state)
+{
+       struct cmdline *cl = rdl->opaque;
+       return cmdline_complete(cl, buf, state, dstbuf, dstsize);
+}
+
+void
+cmdline_write_char(struct rdline *rdl, char c)
+{
+       struct cmdline *cl = rdl->opaque;
+       if (cl->s_out >= 0) {
+               write(cl->s_out, &c, 1);
+       }
+}
+
+
+void
+cmdline_set_prompt(struct cmdline *cl, const char *prompt)
+{
+       snprintf(cl->prompt, sizeof(cl->prompt), prompt);       
+}
+
+struct cmdline *
+cmdline_new(cmdline_parse_ctx_t *ctx, const char *prompt, int s_in, int s_out)
+{
+       struct cmdline *cl;
+       cl = malloc(sizeof(struct cmdline));
+       if (cl == NULL)
+               return NULL;
+       memset(cl, 0, sizeof(struct cmdline));
+       cl->s_in = s_in;
+       cl->s_out = s_out;
+       cl->ctx = ctx;
+
+       rdline_init(&cl->rdl, cmdline_write_char, 
+                   cmdline_valid_buffer, cmdline_complete_buffer);
+       cl->rdl.opaque = cl;
+       cmdline_set_prompt(cl, prompt);
+       rdline_newline(&cl->rdl, cl->prompt);
+
+       return cl;
+}
+
+void
+cmdline_free(struct cmdline *cl)
+{
+       dprintf("called\n");
+       if (cl->s_in > 2)
+               close(cl->s_in);
+       if (cl->s_out != cl->s_in && cl->s_out > 2)
+               close(cl->s_out);
+       free(cl);
+}
+
+void
+cmdline_printf(const struct cmdline *cl, const char *fmt, ...)
+{
+       va_list ap;
+       char buffer[BUFSIZ];
+       
+       va_start(ap, fmt);
+       vsnprintf(buffer, 512, fmt, ap);
+       va_end(ap);
+       if (cl->s_out >= 0) {
+               write(cl->s_out, buffer, strlen(buffer));
+       }
+}
+
+int
+cmdline_in(struct cmdline *cl, const char *buf, int size)
+{
+       const char *history, *buffer;
+       int ret = 0;
+       int i, same;
+
+       /* XXX use defines instead of hardcoded values */
+       for (i=0; i<size; i++) {
+               ret = rdline_char_in(&cl->rdl, buf[i]);
+
+               if (ret == 1) {
+                       buffer = rdline_get_buffer(&cl->rdl);
+                       history = rdline_get_history_item(&cl->rdl, 0);
+                       if (history) {
+                               same = !memcmp(buffer, history, strlen(history)) &&
+                                       buffer[strlen(history)] == '\n';
+                       }
+                       else
+                               same = 0;
+                       if (strlen(buffer) > 1 && !same)
+                               rdline_add_history(&cl->rdl, buffer);
+                       rdline_newline(&cl->rdl, cl->prompt);
+               }
+               else if (ret == -2)
+                       return -1;
+       }
+       return i;
+}
diff --git a/src/lib/cmdline.h b/src/lib/cmdline.h
new file mode 100644 (file)
index 0000000..d90205a
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+
+#ifndef _CMDLINE_H_
+#define _CMDLINE_H_
+
+#include "cmdline_rdline.h"
+
+struct cmdline {
+       int s_in;
+       int s_out;
+       cmdline_parse_ctx_t *ctx;
+       struct rdline rdl;
+       char prompt[RDLINE_PROMPT_SIZE];
+};
+
+struct cmdline *cmdline_new(cmdline_parse_ctx_t *ctx, const char *prompt, int s_in, int s_out);
+void cmdline_set_prompt(struct cmdline *cl, const char *prompt);
+void cmdline_free(struct cmdline *cl);
+void cmdline_printf(const struct cmdline *cl, const char *fmt, ...);
+int cmdline_in(struct cmdline *cl, const char *buf, int size);
+void cmdline_write_char(struct rdline *rdl, char c);
+
+#endif /* _CMDLINE_SOCKET_H_ */
diff --git a/src/lib/cmdline_cirbuf.c b/src/lib/cmdline_cirbuf.c
new file mode 100644 (file)
index 0000000..8cd5374
--- /dev/null
@@ -0,0 +1,381 @@
+/*
+ * 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 <string.h>
+#include <errno.h>
+
+#include "cmdline_cirbuf.h"
+
+
+void 
+cirbuf_init(struct cirbuf *cbuf, char *buf, unsigned int start, unsigned int maxlen)
+{
+       cbuf->maxlen = maxlen;
+       cbuf->len = 0;
+       cbuf->start = start;
+       cbuf->end = start;
+       cbuf->buf = buf;
+}
+
+/* multiple add */
+
+int
+cirbuf_add_buf_head(struct cirbuf *cbuf, const char *c, unsigned int n)
+{
+       unsigned int e;
+
+       if (!n || n > CIRBUF_GET_FREELEN(cbuf))
+               return -EINVAL;
+
+       e = CIRBUF_IS_EMPTY(cbuf) ? 1 : 0;
+       
+       if (n < cbuf->start + e) {
+               dprintf("s[%d] -> d[%d] (%d)\n", 0, cbuf->start - n + e, n);
+               memcpy(cbuf->buf + cbuf->start - n + e, c, n);
+       }
+       else {
+               dprintf("s[%d] -> d[%d] (%d)\n", + n - (cbuf->start + e), 0, cbuf->start + e);
+               dprintf("s[%d] -> d[%d] (%d)\n", cbuf->maxlen - n + (cbuf->start + e), 0, n - (cbuf->start + e));
+               memcpy(cbuf->buf, c  + n - (cbuf->start + e) , cbuf->start + e);
+               memcpy(cbuf->buf + cbuf->maxlen - n + (cbuf->start + e), c, n - (cbuf->start + e));
+       }
+       cbuf->len += n;
+       cbuf->start += (cbuf->maxlen - n + e);
+       cbuf->start %= cbuf->maxlen;
+       return n;
+}
+
+/* multiple add */
+
+int
+cirbuf_add_buf_tail(struct cirbuf *cbuf, const char *c, unsigned int n)
+{
+       unsigned int e;
+
+       if (!n || n > CIRBUF_GET_FREELEN(cbuf))
+               return -EINVAL;
+
+       e = CIRBUF_IS_EMPTY(cbuf) ? 1 : 0;
+       
+       if (n < cbuf->maxlen - cbuf->end - 1 + e) {
+               dprintf("s[%d] -> d[%d] (%d)\n", 0, cbuf->end + !e, n);
+               memcpy(cbuf->buf + cbuf->end + !e, c, n);
+       }
+       else {
+               dprintf("s[%d] -> d[%d] (%d)\n", cbuf->end + !e, 0, cbuf->maxlen - cbuf->end - 1 + e);
+               dprintf("s[%d] -> d[%d] (%d)\n", cbuf->maxlen - cbuf->end - 1 + e, 0, n - cbuf->maxlen + cbuf->end + 1 - e);
+               memcpy(cbuf->buf + cbuf->end + !e, c, cbuf->maxlen - cbuf->end - 1 + e);
+               memcpy(cbuf->buf, c + cbuf->maxlen - cbuf->end - 1 + e, n - cbuf->maxlen + cbuf->end + 1 - e);
+       }
+       cbuf->len += n;
+       cbuf->end += n - e;
+       cbuf->end %= cbuf->maxlen;
+       return n;
+}
+
+/* add at head */
+
+static inline void 
+__cirbuf_add_head(struct cirbuf * cbuf, char c)
+{
+       if (!CIRBUF_IS_EMPTY(cbuf)) {
+               cbuf->start += (cbuf->maxlen - 1);
+               cbuf->start %= cbuf->maxlen;
+       }
+       cbuf->buf[cbuf->start] = c;
+       cbuf->len ++;
+}
+
+int 
+cirbuf_add_head_safe(struct cirbuf * cbuf, char c)
+{
+       if (cbuf && !CIRBUF_IS_FULL(cbuf)) {
+               __cirbuf_add_head(cbuf, c);     
+               return 0;
+       }
+       return -EINVAL;
+}
+
+void
+cirbuf_add_head(struct cirbuf * cbuf, char c)
+{
+       __cirbuf_add_head(cbuf, c);     
+}
+
+/* add at tail */
+
+static inline void 
+__cirbuf_add_tail(struct cirbuf * cbuf, char c)
+{
+       if (!CIRBUF_IS_EMPTY(cbuf)) {
+               cbuf->end ++;
+               cbuf->end %= cbuf->maxlen;
+       }
+       cbuf->buf[cbuf->end] = c;
+       cbuf->len ++;
+}
+
+int 
+cirbuf_add_tail_safe(struct cirbuf * cbuf, char c)
+{
+       if (cbuf && !CIRBUF_IS_FULL(cbuf)) {
+               __cirbuf_add_tail(cbuf, c);     
+               return 0;
+       }
+       return -EINVAL;
+}
+
+void
+cirbuf_add_tail(struct cirbuf * cbuf, char c)
+{
+       __cirbuf_add_tail(cbuf, c);     
+}
+
+
+static inline void
+__cirbuf_shift_left(struct cirbuf *cbuf)
+{
+       unsigned int i;
+       char tmp = cbuf->buf[cbuf->start];
+
+       for (i=0 ; i<cbuf->len ; i++) {
+               cbuf->buf[(cbuf->start+i)%cbuf->maxlen] = 
+                       cbuf->buf[(cbuf->start+i+1)%cbuf->maxlen];
+       }
+       cbuf->buf[(cbuf->start-1+cbuf->maxlen)%cbuf->maxlen] = tmp;
+       cbuf->start += (cbuf->maxlen - 1);
+       cbuf->start %= cbuf->maxlen;
+       cbuf->end += (cbuf->maxlen - 1);
+       cbuf->end %= cbuf->maxlen;
+}
+
+static inline void
+__cirbuf_shift_right(struct cirbuf *cbuf)
+{
+       unsigned int i;
+       char tmp = cbuf->buf[cbuf->end];
+
+       for (i=0 ; i<cbuf->len ; i++) {
+               cbuf->buf[(cbuf->end+cbuf->maxlen-i)%cbuf->maxlen] = 
+                       cbuf->buf[(cbuf->end+cbuf->maxlen-i-1)%cbuf->maxlen];
+       }
+       cbuf->buf[(cbuf->end+1)%cbuf->maxlen] = tmp;
+       cbuf->start += 1;
+       cbuf->start %= cbuf->maxlen;
+       cbuf->end += 1;
+       cbuf->end %= cbuf->maxlen;
+}
+
+/* XXX we could do a better algorithm here... */
+void cirbuf_align_left(struct cirbuf * cbuf)
+{
+       if (cbuf->start < cbuf->maxlen/2) {
+               while (cbuf->start != 0) {
+                       __cirbuf_shift_left(cbuf);
+               }
+       }
+       else {
+               while (cbuf->start != 0) {
+                       __cirbuf_shift_right(cbuf);
+               }
+       }
+}
+
+/* XXX we could do a better algorithm here... */
+void cirbuf_align_right(struct cirbuf * cbuf)
+{
+       if (cbuf->start >= cbuf->maxlen/2) {
+               while (cbuf->end != cbuf->maxlen-1) {
+                       __cirbuf_shift_left(cbuf);
+               }
+       }
+       else {
+               while (cbuf->start != cbuf->maxlen-1) {
+                       __cirbuf_shift_right(cbuf);
+               }
+       }
+}
+
+/* buffer del */
+
+int
+cirbuf_del_buf_head(struct cirbuf *cbuf, unsigned int size)
+{
+       if (!size || size > CIRBUF_GET_LEN(cbuf))
+               return -EINVAL;
+
+       cbuf->len -= size;
+       if (CIRBUF_IS_EMPTY(cbuf)) {
+               cbuf->start += size - 1;
+               cbuf->start %= cbuf->maxlen;
+       }
+       else {
+               cbuf->start += size;
+               cbuf->start %= cbuf->maxlen;
+       }
+       return 0;
+}
+
+/* buffer del */
+
+int
+cirbuf_del_buf_tail(struct cirbuf *cbuf, unsigned int size)
+{
+       if (!size || size > CIRBUF_GET_LEN(cbuf))
+               return -EINVAL;
+
+       cbuf->len -= size;
+       if (CIRBUF_IS_EMPTY(cbuf)) {
+               cbuf->end  += (cbuf->maxlen - size + 1);
+               cbuf->end %= cbuf->maxlen;
+       }
+       else {
+               cbuf->end  += (cbuf->maxlen - size);
+               cbuf->end %= cbuf->maxlen;
+       }
+       return 0;
+}
+
+/* del at head */
+
+static inline void
+__cirbuf_del_head(struct cirbuf * cbuf)
+{
+       cbuf->len --;
+       if (!CIRBUF_IS_EMPTY(cbuf)) {
+               cbuf->start ++;
+               cbuf->start %= cbuf->maxlen;
+       }
+}
+
+int
+cirbuf_del_head_safe(struct cirbuf * cbuf)
+{
+       if (cbuf && !CIRBUF_IS_EMPTY(cbuf)) {
+               __cirbuf_del_head(cbuf);
+               return 0;
+       }
+       return -EINVAL;
+}
+
+void
+cirbuf_del_head(struct cirbuf * cbuf)
+{
+       __cirbuf_del_head(cbuf);
+}
+
+/* del at tail */
+
+static inline void
+__cirbuf_del_tail(struct cirbuf * cbuf)
+{
+       cbuf->len --;
+       if (!CIRBUF_IS_EMPTY(cbuf)) {
+               cbuf->end  += (cbuf->maxlen - 1);
+               cbuf->end %= cbuf->maxlen;
+       }
+}
+
+int
+cirbuf_del_tail_safe(struct cirbuf * cbuf)
+{
+       if (cbuf && !CIRBUF_IS_EMPTY(cbuf)) {
+               __cirbuf_del_tail(cbuf);
+               return 0;
+       }
+       return -EINVAL;
+}
+
+void
+cirbuf_del_tail(struct cirbuf * cbuf)
+{
+       __cirbuf_del_tail(cbuf);
+}
+
+/* convert to buffer */
+
+int
+cirbuf_get_buf_head(struct cirbuf *cbuf, char *c, unsigned int size)
+{
+       unsigned int n = (size < CIRBUF_GET_LEN(cbuf)) ? size : CIRBUF_GET_LEN(cbuf);
+       
+       if (!n)
+               return 0;
+
+       if (cbuf->start <= cbuf->end) {
+               dprintf("s[%d] -> d[%d] (%d)\n", cbuf->start, 0, n);
+               memcpy(c, cbuf->buf + cbuf->start , n);
+       }
+       else {
+               dprintf("s[%d] -> d[%d] (%d)\n", cbuf->start, 0, cbuf->maxlen - cbuf->start);
+               dprintf("s[%d] -> d[%d] (%d)\n", 0, cbuf->maxlen - cbuf->start, n - cbuf->maxlen + cbuf->start);
+               memcpy(c, cbuf->buf + cbuf->start , cbuf->maxlen - cbuf->start);
+               memcpy(c + cbuf->maxlen - cbuf->start, cbuf->buf, n - cbuf->maxlen + cbuf->start);
+       }
+       return n;
+}
+
+/* convert to buffer */
+
+int
+cirbuf_get_buf_tail(struct cirbuf *cbuf, char *c, unsigned int size)
+{
+       unsigned int n = (size < CIRBUF_GET_LEN(cbuf)) ? size : CIRBUF_GET_LEN(cbuf);
+
+       if (!n)
+               return 0;
+
+       if (cbuf->start <= cbuf->end) {
+               dprintf("s[%d] -> d[%d] (%d)\n", cbuf->end - n + 1, 0, n);
+               memcpy(c, cbuf->buf + cbuf->end - n + 1, n);
+       }
+       else {
+               dprintf("s[%d] -> d[%d] (%d)\n", 0,  cbuf->maxlen - cbuf->start, cbuf->end + 1);
+               dprintf("s[%d] -> d[%d] (%d)\n", cbuf->maxlen - n + cbuf->end + 1, 0, n - cbuf->end - 1);
+
+               memcpy(c + cbuf->maxlen - cbuf->start, cbuf->buf, cbuf->end + 1);
+               memcpy(c, cbuf->buf + cbuf->maxlen - n + cbuf->end +1, n - cbuf->end - 1);
+       }
+       return n;
+}
+
+/* get head or get tail */
+
+char 
+cirbuf_get_head(struct cirbuf * cbuf)
+{
+       return cbuf->buf[cbuf->start];
+}
+
+/* get head or get tail */
+
+char 
+cirbuf_get_tail(struct cirbuf * cbuf)
+{
+       return cbuf->buf[cbuf->end];
+}
+
diff --git a/src/lib/cmdline_cirbuf.h b/src/lib/cmdline_cirbuf.h
new file mode 100644 (file)
index 0000000..c68e248
--- /dev/null
@@ -0,0 +1,207 @@
+/*
+ * 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.
+ */
+
+#ifndef _CIRBUF_H_
+#define _CIRBUF_H_
+
+#include <stdio.h>
+
+/**
+ * This structure is the header of a cirbuf type.
+ */
+struct cirbuf {
+       unsigned int maxlen;    /**< total len of the fifo (number of elements) */
+       unsigned int start;     /**< indice of the first elt */
+       unsigned int end;       /**< indice of the last elt */
+       unsigned int len;       /**< current len of fifo */
+       char *buf;
+};
+
+/* #define CIRBUF_DEBUG */
+
+#ifdef CIRBUF_DEBUG
+#define dprintf(fmt, ...) printf("line %3.3d - " fmt, __LINE__, ##__VA_ARGS__)
+#else
+#define dprintf(args...) do {} while(0)
+#endif
+
+
+/**
+ * Init the circular buffer
+ */
+void cirbuf_init(struct cirbuf *cbuf, char *buf, unsigned int start, unsigned int maxlen);
+
+
+/**
+ * Return 1 if the circular buffer is full
+ */
+#define CIRBUF_IS_FULL(cirbuf) ((cirbuf)->maxlen == (cirbuf)->len)
+
+/**
+ * Return 1 if the circular buffer is empty
+ */
+#define CIRBUF_IS_EMPTY(cirbuf) ((cirbuf)->len == 0)
+
+/**
+ * return current size of the circular buffer (number of used elements)
+ */
+#define CIRBUF_GET_LEN(cirbuf) ((cirbuf)->len)
+
+/**
+ * return size of the circular buffer (used + free elements)
+ */
+#define CIRBUF_GET_MAXLEN(cirbuf) ((cirbuf)->maxlen)
+
+/**
+ * return the number of free elts 
+ */
+#define CIRBUF_GET_FREELEN(cirbuf) ((cirbuf)->maxlen - (cirbuf)->len)
+
+/**
+ * Iterator for a circular buffer
+ *   c: struct cirbuf pointer
+ *   i: an integer type internally used in the macro
+ *   e: char that takes the value for each iteration
+ */
+#define CIRBUF_FOREACH(c, i, e)                                 \
+       for ( i=0, e=(c)->buf[(c)->start] ;                     \
+             i<((c)->len) ;                                    \
+              i ++,  e=(c)->buf[((c)->start+i)%((c)->maxlen)])
+
+
+/** 
+ * Add a character at head of the circular buffer. Return 0 on success, or
+ * a negative value on error.
+ */
+int cirbuf_add_head_safe(struct cirbuf *cbuf, char c);
+
+/** 
+ * Add a character at head of the circular buffer. You _must_ check that you
+ * have enough free space in the buffer before calling this func.
+ */
+void cirbuf_add_head(struct cirbuf *cbuf, char c);
+
+/** 
+ * Add a character at tail of the circular buffer. Return 0 on success, or
+ * a negative value on error.
+ */
+int cirbuf_add_tail_safe(struct cirbuf *cbuf, char c);
+
+/** 
+ * Add a character at tail of the circular buffer. You _must_ check that you
+ * have enough free space in the buffer before calling this func.
+ */
+void cirbuf_add_tail(struct cirbuf *cbuf, char c);
+
+/** 
+ * Remove a char at the head of the circular buffer. Return 0 on
+ * success, or a negative value on error.
+ */
+int cirbuf_del_head_safe(struct cirbuf *cbuf);
+
+/** 
+ * Remove a char at the head of the circular buffer. You _must_ check
+ * that buffer is not empty before calling the function.
+ */
+void cirbuf_del_head(struct cirbuf *cbuf);
+
+/** 
+ * Remove a char at the tail of the circular buffer. Return 0 on
+ * success, or a negative value on error.
+ */
+int cirbuf_del_tail_safe(struct cirbuf *cbuf);
+
+/** 
+ * Remove a char at the tail of the circular buffer. You _must_ check
+ * that buffer is not empty before calling the function.
+ */
+void cirbuf_del_tail(struct cirbuf *cbuf);
+
+/**
+ * Return the head of the circular buffer. You _must_ check that
+ * buffer is not empty before calling the function.
+ */
+char cirbuf_get_head(struct cirbuf *cbuf);
+
+/**
+ * Return the tail of the circular buffer. You _must_ check that
+ * buffer is not empty before calling the function.
+ */
+char cirbuf_get_tail(struct cirbuf *cbuf);
+
+/** 
+ * Add a buffer at head of the circular buffer. 'c' is a pointer to a
+ * buffer, and n is the number of char to add. Return the number of
+ * copied bytes on success, or a negative value on error.
+ */
+int cirbuf_add_buf_head(struct cirbuf *cbuf, const char *c, unsigned int n);
+
+/** 
+ * Add a buffer at tail of the circular buffer. 'c' is a pointer to a
+ * buffer, and n is the number of char to add. Return the number of
+ * copied bytes on success, or a negative value on error.
+ */
+int cirbuf_add_buf_tail(struct cirbuf *cbuf, const char *c, unsigned int n);
+
+/** 
+ * Remove chars at the head of the circular buffer. Return 0 on
+ * success, or a negative value on error.
+ */
+int cirbuf_del_buf_head(struct cirbuf *cbuf, unsigned int size);
+
+/** 
+ * Remove chars at the tail of the circular buffer. Return 0 on
+ * success, or a negative value on error.
+ */
+int cirbuf_del_buf_tail(struct cirbuf *cbuf, unsigned int size);
+
+/** 
+ * Copy a maximum of 'size' characters from the head of the circular
+ * buffer to a flat one pointed by 'c'. Return the number of copied
+ * chars.
+ */
+int cirbuf_get_buf_head(struct cirbuf *cbuf, char *c, unsigned int size);
+
+/** 
+ * Copy a maximum of 'size' characters from the tail of the circular
+ * buffer to a flat one pointed by 'c'. Return the number of copied
+ * chars.
+ */
+int cirbuf_get_buf_tail(struct cirbuf *cbuf, char *c, unsigned int size);
+
+
+/** 
+ * Set the start of the data to the index 0 of the internal buffer.
+ */
+void cirbuf_align_left(struct cirbuf *cbuf);
+
+/** 
+ * Set the end of the data to the last index of the internal buffer.
+ */
+void cirbuf_align_right(struct cirbuf *cbuf);
+
+#endif /* _CIRBUF_H_ */
diff --git a/src/lib/cmdline_parse.c b/src/lib/cmdline_parse.c
new file mode 100644 (file)
index 0000000..85958d0
--- /dev/null
@@ -0,0 +1,454 @@
+/*
+ * 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 <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include <ctype.h>
+
+#include <netinet/in.h>
+
+#include "cmdline_parse.h"
+#include "cmdline.h"
+
+//#define CMDLINE_DEBUG
+//#define debug_printf printf
+#define debug_printf(args...) do {} while(0)
+
+/* isblank() needs _XOPEN_SOURCE >= 600 || _ISOC99_SOURCE, so use our
+ * own. */
+static int
+isblank2(char c)
+{
+       if (c == ' ' || 
+           c == '\t' )
+               return 1;
+       return 0;
+}
+
+static int
+isendofline(char c)
+{
+       if (c == '\n' || 
+           c == '\r' )
+               return 1;
+       return 0;
+}
+
+static int
+iscomment(char c)
+{
+       if (c == '#')
+               return 1;
+       return 0;
+}
+
+int
+cmdline_isendoftoken(char c)
+{
+       if (!c || iscomment(c) || isblank2(c) || isendofline(c))
+               return 1;
+       return 0;
+}
+
+static unsigned int
+nb_common_chars(const char * s1, const char * s2)
+{
+       unsigned int i=0;
+
+       while (*s1==*s2 && *s1 && *s2) {
+               s1++;
+               s2++;
+               i++;
+       }
+       return i;
+}
+
+/** 
+ * try to match the buffer with an instruction (only the first
+ * nb_match_token tokens if != 0). Return 0 if we match all the
+ * tokens, else the number of matched tokens, else -1.
+ */
+static int
+match_inst(cmdline_parse_inst_t *inst, const char *buf, unsigned int nb_match_token, 
+          void * result_buf)
+{
+       unsigned int token_num=0;
+       cmdline_parse_token_hdr_t * token_p;
+       unsigned int i=0;
+       int n = 0;
+       struct cmdline_token_hdr token_hdr;
+
+       token_p = inst->tokens[token_num];
+       if (token_p)
+               memcpy(&token_hdr, token_p, sizeof(token_hdr));
+       
+       /* check if we match all tokens of inst */
+       while (token_p && (!nb_match_token || i<nb_match_token)) {
+               debug_printf("TK\n");
+               /* skip spaces */
+               while (isblank2(*buf)) {
+                       buf++;
+               }
+               
+               /* end of buf */
+               if ( isendofline(*buf) || iscomment(*buf) )
+                       break;
+               
+               n = token_hdr.ops->parse(token_p, buf, (result_buf ? result_buf+token_hdr.offset : NULL));
+               if ( n < 0 )
+                       break;
+               debug_printf("TK parsed (len=%d)\n", n);
+               i++;
+               buf += n;
+               
+               token_num ++;
+               token_p = inst->tokens[token_num];
+               if (token_p)
+                       memcpy(&token_hdr, token_p, sizeof(token_hdr));
+       }
+       
+       /* does not match */
+       if (i==0)
+               return -1;
+       
+       /* in case we want to match a specific num of token */
+       if (nb_match_token) {
+               if (i == nb_match_token) {
+                       return 0;
+               }
+               return i;
+       }
+
+       /* we don't match all the tokens */
+       if (token_p) {
+               return i;
+       }
+
+       /* are there are some tokens more */
+       while (isblank2(*buf)) {
+               buf++;
+       }
+       
+       /* end of buf */
+       if ( isendofline(*buf) || iscomment(*buf) )
+               return 0;
+
+       /* garbage after inst */
+       return i;
+}
+
+
+int
+cmdline_parse(struct cmdline *cl, const char * buf)
+{
+       unsigned int inst_num=0;
+       cmdline_parse_inst_t *inst;
+       const char *curbuf;
+       char result_buf[BUFSIZ]; /* XXX align, size zé in broblém */
+       void (*f)(void *, struct cmdline *, void *) = NULL;
+       void *data = NULL;
+       int comment = 0;
+       int linelen = 0;
+       int parse_it = 0;
+       int err = CMDLINE_PARSE_NOMATCH;
+       int tok;
+       cmdline_parse_ctx_t *ctx = cl->ctx;
+#ifdef CMDLINE_DEBUG
+       char debug_buf[BUFSIZ];
+#endif
+
+       /* 
+        * - look if the buffer contains at least one line
+        * - look if line contains only spaces or comments 
+        * - count line length
+        */
+       curbuf = buf;
+       while (! isendofline(*curbuf)) {
+               if ( *curbuf == '\0' ) {
+                       debug_printf("Incomplete buf (len=%d)\n", linelen);
+                       return 0;
+               }
+               if ( iscomment(*curbuf) ) {
+                       comment = 1;
+               }
+               if ( ! isblank2(*curbuf) && ! comment) {
+                       parse_it = 1;
+               }
+               curbuf++;
+               linelen++;
+       }
+
+       /* skip all endofline chars */
+       while (isendofline(buf[linelen])) {
+               linelen++;
+       }
+
+       /* empty line */
+       if ( parse_it == 0 ) {
+               debug_printf("Empty line (len=%d)\n", linelen);
+               return linelen;
+       }
+
+#ifdef CMDLINE_DEBUG
+       snprintf(debug_buf, (linelen>64 ? 64 : linelen), "%s", buf);
+       debug_printf("Parse line : len=%d, <%s>\n", linelen, debug_buf);
+#endif
+
+       /* parse it !! */
+       inst = ctx[inst_num];
+       while (inst) {
+               debug_printf("INST %d\n", inst_num);
+
+               /* fully parsed */
+               tok = match_inst(inst, buf, 0, result_buf);
+
+               if (tok > 0) /* we matched at least one token */
+                       err = CMDLINE_PARSE_BAD_ARGS;
+
+               else if (!tok) {
+                       debug_printf("INST fully parsed\n");
+                       /* skip spaces */
+                       while (isblank2(*curbuf)) {
+                               curbuf++;
+                       }
+                       
+                       /* if end of buf -> there is no garbage after inst */
+                       if (isendofline(*curbuf) || iscomment(*curbuf)) {
+                               if (!f) {
+                                       memcpy(&f, &inst->f, sizeof(f));
+                                       memcpy(&data, &inst->data, sizeof(data));
+                               }
+                               else {
+                                       /* more than 1 inst matches */
+                                       err = CMDLINE_PARSE_AMBIGUOUS;
+                                       f=NULL;
+                                       debug_printf("Ambiguous cmd\n");
+                                       break;
+                               }
+                       }
+               }
+                       
+               inst_num ++;
+               inst = ctx[inst_num];
+       }
+       
+       /* call func */
+       if (f) {
+               f(result_buf, cl, data);
+       }
+
+       /* no match */
+       else {
+               debug_printf("No match err=%d\n", err);
+               return err;
+       }
+       
+       return linelen;
+}
+
+int 
+cmdline_complete(struct cmdline *cl, const char *buf, int *state, 
+                char *dst, unsigned int size)
+{
+       const char *incomplete_token = buf;
+       unsigned int inst_num = 0;
+       cmdline_parse_inst_t *inst;
+       cmdline_parse_token_hdr_t *token_p;
+       struct cmdline_token_hdr token_hdr;
+       char tmpbuf[64], completion_buf[64];
+       unsigned int incomplete_token_len;
+       int completion_len = -1;
+       int nb_token = 0;
+       unsigned int i, n;
+       int l;
+       unsigned int nb_completable;
+       unsigned int nb_non_completable;
+       unsigned int local_state=0;
+       char *help_str;
+       cmdline_parse_ctx_t *ctx = cl->ctx;
+
+       debug_printf("%s called\n", __FUNCTION__);
+       /* count the number of complete token to parse */
+       for (i=0 ; buf[i] ; i++) {
+               if (!isblank2(buf[i]) && isblank2(buf[i+1]))
+                       nb_token++;
+               if (isblank2(buf[i]) && !isblank2(buf[i+1]))
+                       incomplete_token = buf+i+1;
+       }
+       incomplete_token_len = strlen(incomplete_token);
+
+       /* first call -> do a first pass */
+       if (*state <= 0) {
+               debug_printf("try complete <%s>\n", buf);
+               debug_printf("there is %d complete tokens, <%s> is incomplete\n", nb_token, incomplete_token);
+
+               nb_completable = 0;
+               nb_non_completable = 0;
+               
+               inst = ctx[inst_num];
+               while (inst) {
+                       /* parse the first tokens of the inst */
+                       if (nb_token && match_inst(inst, buf, nb_token, NULL))
+                               goto next;
+                       
+                       debug_printf("instruction match \n");
+                       token_p = inst->tokens[nb_token];
+                       if (token_p)
+                               memcpy(&token_hdr, token_p, sizeof(token_hdr));
+
+                       /* non completable */
+                       if (!token_p || 
+                           !token_hdr.ops->complete_get_nb || 
+                           !token_hdr.ops->complete_get_elt || 
+                           (n = token_hdr.ops->complete_get_nb(token_p)) == 0) {
+                               nb_non_completable++;
+                               goto next;
+                       }
+
+                       debug_printf("%d choices for this token\n", n);
+                       for (i=0 ; i<n ; i++) {
+                               if (token_hdr.ops->complete_get_elt(token_p, i, tmpbuf, sizeof(tmpbuf)) < 0)
+                                       continue;
+                               strcat(tmpbuf, " "); /* we have at least room for one char */
+                               debug_printf("   choice <%s>\n", tmpbuf);
+                               /* does the completion match the beginning of the word ? */
+                               if (!strncmp(incomplete_token, tmpbuf, incomplete_token_len)) {
+                                       if (completion_len == -1) {
+                                               strcpy(completion_buf, tmpbuf+incomplete_token_len);
+                                               completion_len = strlen(tmpbuf+incomplete_token_len);
+                                               
+                                       }
+                                       else {
+                                               completion_len = nb_common_chars(completion_buf, 
+                                                                                tmpbuf+incomplete_token_len);
+                                               completion_buf[completion_len] = 0;
+                                       }
+                                       nb_completable++;
+                               }
+                       }               
+               next:
+                       inst_num ++;
+                       inst = ctx[inst_num];
+               }
+
+               debug_printf("total choices %d for this completion\n", nb_completable);
+
+               /* no possible completion */
+               if (nb_completable == 0 && nb_non_completable == 0)
+                       return 0;
+               
+               /* if multichoice is not required */
+               if (*state == 0 && incomplete_token_len > 0) {
+                       /* one or several choices starting with the
+                          same chars */
+                       if (completion_len > 0) { 
+                               if (completion_len + 1 > size)
+                                       return 0;
+                               
+                               strcpy(dst, completion_buf);
+                               return 2;
+                       }
+               }
+       }
+
+       /* init state correctly */
+       if (*state == -1)
+               *state = 0;
+
+       debug_printf("Multiple choice STATE=%d\n", *state);
+
+       inst_num = 0;
+       inst = ctx[inst_num];
+       while (inst) {
+               /* we need to redo it */
+               inst = ctx[inst_num];
+               
+               if (nb_token && match_inst(inst, buf, nb_token, NULL))
+                       goto next2;
+               
+               token_p = inst->tokens[nb_token];
+               if (token_p)
+                       memcpy(&token_hdr, token_p, sizeof(token_hdr));
+
+               /* one choice for this token */
+               if (!token_p || 
+                   !token_hdr.ops->complete_get_nb || 
+                   !token_hdr.ops->complete_get_elt || 
+                   (n = token_hdr.ops->complete_get_nb(token_p)) == 0) {
+                       if (local_state < *state) {
+                               local_state++;
+                               goto next2;
+                       }
+                       (*state)++;
+                       if (token_p && token_hdr.ops->get_help) {
+                               token_hdr.ops->get_help(token_p, tmpbuf, sizeof(tmpbuf));
+                               help_str = inst->help_str;
+                               if (help_str)
+                                       snprintf(dst, size, "[%s]: %s", tmpbuf, help_str);
+                               else
+                                       snprintf(dst, size, "[%s]: No help", tmpbuf);
+                       }
+                       else {
+                               snprintf(dst, size, "[RETURN]");
+                       }
+                       return 1;
+               }
+
+               /* several choices */
+               for (i=0 ; i<n ; i++) {
+                       if (token_hdr.ops->complete_get_elt(token_p, i, tmpbuf, sizeof(tmpbuf)) < 0)
+                               continue;
+                       strcat(tmpbuf, " "); /* we have at least room for one char */
+                       debug_printf("   choice <%s>\n", tmpbuf);
+                       /* does the completion match the beginning of the word ? */
+                       if (!strncmp(incomplete_token, tmpbuf, incomplete_token_len)) {
+                               if (local_state < *state) {
+                                       local_state++;
+                                       continue;
+                               }
+                               (*state)++;
+                               l=snprintf(dst, size, "%s", tmpbuf);
+                               if (l>=0 && token_hdr.ops->get_help) {
+                                       token_hdr.ops->get_help(token_p, tmpbuf, sizeof(tmpbuf));
+                                       help_str = inst->help_str;
+                                       if (help_str)
+                                               snprintf(dst+l, size-l, "[%s]: %s", tmpbuf, help_str);
+                                       else
+                                               snprintf(dst+l, size-l, "[%s]: No help", tmpbuf);
+                               }
+                                                             
+                               return 1;
+                       }
+               }
+       next2:
+               inst_num ++;
+               inst = ctx[inst_num];
+       }
+       return 0;
+}
+
diff --git a/src/lib/cmdline_parse.h b/src/lib/cmdline_parse.h
new file mode 100644 (file)
index 0000000..6b7fca9
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ * 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.
+ */
+
+#ifndef _CMDLINE_PARSE_H_
+#define _CMDLINE_PARSE_H_
+
+#include <sys/types.h>
+
+#ifndef offsetof
+#define offsetof(type, field)  ((size_t) &( ((type *)0)->field) )
+#endif
+
+/* return status for parsing */
+#define CMDLINE_PARSE_SUCCESS        0
+#define CMDLINE_PARSE_AMBIGUOUS     -1
+#define CMDLINE_PARSE_NOMATCH       -2
+#define CMDLINE_PARSE_BAD_ARGS      -3
+
+/* return status for completion */
+#define CMDLINE_PARSE_COMPLETE_FINISHED 0
+#define CMDLINE_PARSE_COMPLETE_AGAIN    1
+#define CMDLINE_PARSE_COMPLETED_BUFFER  2
+
+/**
+ * Stores a pointer to the ops struct, and the offset: the place to
+ * write the parsed result in the destination structure.
+ */
+struct cmdline_token_hdr {
+       struct cmdline_token_ops *ops;
+       unsigned int offset;
+};
+typedef struct cmdline_token_hdr cmdline_parse_token_hdr_t;
+
+/**
+ * A token is defined by this structure.
+ *
+ * parse() takes the token as first argument, then the source buffer
+ * starting at the token we want to parse. The 3rd arg is a pointer
+ * where we store the parsed data (as binary). It returns the number of
+ * parsed chars on success and a negative value on error.
+ *
+ * complete_get_nb() returns the number of possible values for this
+ * token if completion is possible. If it is NULL or if it returns 0, 
+ * no completion is possible.
+ *
+ * complete_get_elt() copy in dstbuf (the size is specified in the
+ * parameter) the i-th possible completion for this token.  returns 0
+ * on success or and a negative value on error.
+ *
+ * get_help() fills the dstbuf with the help for the token. It returns
+ * -1 on error and 0 on success.
+ */
+struct cmdline_token_ops {
+       /** parse(token ptr, buf, res pts) */
+       int (*parse)(cmdline_parse_token_hdr_t *, const char *, void *);
+       /** return the num of possible choices for this token */
+       int (*complete_get_nb)(cmdline_parse_token_hdr_t *);
+       /** return the elt x for this token (token, idx, dstbuf, size) */
+       int (*complete_get_elt)(cmdline_parse_token_hdr_t *, int, char *, unsigned int);
+       /** get help for this token (token, dstbuf, size) */
+       int (*get_help)(cmdline_parse_token_hdr_t *, char *, unsigned int);
+};     
+
+struct cmdline;
+/**
+ * Store a instruction, which is a pointer to a callback function and
+ * its parameter that is called when the instruction is parsed, a help
+ * string, and a list of token composing this instruction.
+ */
+struct cmdline_inst {
+       /* f(parsed_struct, data) */
+       void (*f)(void *, struct cmdline *, void *);
+       void *data;
+       char *help_str;
+       cmdline_parse_token_hdr_t *tokens[];
+};
+typedef struct cmdline_inst cmdline_parse_inst_t;
+
+/**
+ * A context is identified by its name, and contains a list of
+ * instruction 
+ *
+ */
+typedef cmdline_parse_inst_t *cmdline_parse_ctx_t;
+
+/**
+ * Try to parse a buffer according to the specified context. The
+ * argument buf must ends with "\n\0". The function returns
+ * CMDLINE_PARSE_AMBIGUOUS, CMDLINE_PARSE_NOMATCH or
+ * CMDLINE_PARSE_BAD_ARGS on error. Else it calls the associated
+ * function (defined in the context) and returns 0
+ * (CMDLINE_PARSE_SUCCESS).
+ */
+int cmdline_parse(struct cmdline *cl, const char *buf);
+
+/**
+ * complete() must be called with *state==0 (try to complete) or
+ * with *state==-1 (just display choices), then called without
+ * modifying *state until it returns CMDLINE_PARSE_COMPLETED_BUFFER or
+ * CMDLINE_PARSE_COMPLETED_BUFFER.
+ *
+ * It returns < 0 on error. 
+ *
+ * Else it returns:
+ *   - CMDLINE_PARSE_COMPLETED_BUFFER on completion (one possible
+ *     choice). In this case, the chars are appended in dst buffer.
+ *   - CMDLINE_PARSE_COMPLETE_AGAIN if there is several possible
+ *     choices. In this case, you must call the function again,
+ *     keeping the value of state intact.
+ *   - CMDLINE_PARSE_COMPLETED_BUFFER when the iteration is
+ *     finished. The dst is not valid for this last call.
+ *
+ * The returned dst buf ends with \0.
+ */
+int cmdline_complete(struct cmdline *cl, const char *buf, int *state,
+                    char *dst, unsigned int size);
+
+
+/* return true if(!c || iscomment(c) || isblank(c) ||
+ * isendofline(c)) */
+int cmdline_isendoftoken(char c);
+
+#endif /* _CMDLINE_PARSE_H_ */
diff --git a/src/lib/cmdline_parse_etheraddr.c b/src/lib/cmdline_parse_etheraddr.c
new file mode 100644 (file)
index 0000000..69df142
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <ctype.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <net/ethernet.h>
+#ifdef __linux__
+#include <netinet/ether.h>
+#endif
+
+#include "cmdline_parse.h"
+#include "cmdline_parse_etheraddr.h"
+
+struct cmdline_token_ops cmdline_token_etheraddr_ops = {
+       .parse = cmdline_parse_etheraddr,
+       .complete_get_nb = NULL,
+       .complete_get_elt = NULL,
+       .get_help = cmdline_get_help_etheraddr,
+};
+
+
+#define ETHER_ADDRSTRLEN 18
+
+int 
+cmdline_parse_etheraddr(cmdline_parse_token_hdr_t *tk, const char *buf, void *res)
+{
+       unsigned int token_len = 0;
+       char ether_str[ETHER_ADDRSTRLEN];
+       struct ether_addr *etheraddr = res;
+       struct ether_addr *tmp;
+
+       if (! *buf)
+               return -1;
+
+       while (!cmdline_isendoftoken(buf[token_len]))
+               token_len++;
+                        
+       /* if token is too big... */
+       if (token_len >= ETHER_ADDRSTRLEN)
+               return -1;
+
+       snprintf(ether_str, token_len+1, "%s", buf);
+
+       tmp = ether_aton(ether_str);
+       if (tmp == NULL)
+               return -1;
+
+       memcpy(&etheraddr, tmp, sizeof(etheraddr));
+       return token_len;
+}
+
+int cmdline_get_help_etheraddr(cmdline_parse_token_hdr_t *tk, char *dstbuf,
+                           unsigned int size)
+{
+       snprintf(dstbuf, size, "Ethernet address");
+       return 0;
+}
diff --git a/src/lib/cmdline_parse_etheraddr.h b/src/lib/cmdline_parse_etheraddr.h
new file mode 100644 (file)
index 0000000..84b14fa
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ */
+
+#ifndef _PARSE_ETHERADDR_H_
+#define _PARSE_ETHERADDR_H_
+
+#include "cmdline_parse.h"
+
+struct cmdline_token_etheraddr_data {
+       uint8_t flags;
+};
+
+struct cmdline_token_etheraddr {
+       struct cmdline_token_hdr hdr;
+};
+typedef struct cmdline_token_etheraddr cmdline_parse_token_etheraddr_t;
+
+extern struct cmdline_token_ops cmdline_token_etheraddr_ops;
+
+int cmdline_parse_etheraddr(cmdline_parse_token_hdr_t *tk, const char *srcbuf,
+                           void *res);
+int cmdline_get_help_etheraddr(cmdline_parse_token_hdr_t *tk, char *dstbuf,
+                              unsigned int size);
+
+#define TOKEN_ETHERADDR_INITIALIZER(structure, field)      \
+{                                                          \
+       .hdr = {                                            \
+               .ops = &cmdline_token_etheraddr_ops,        \
+               .offset = offsetof(structure, field),       \
+       },                                                  \
+}
+
+
+#endif /* _PARSE_ETHERADDR_H_ */
diff --git a/src/lib/cmdline_parse_ipaddr.c b/src/lib/cmdline_parse_ipaddr.c
new file mode 100644 (file)
index 0000000..96b384d
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <ctype.h>
+#include <string.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "cmdline_parse.h"
+#include "cmdline_parse_ipaddr.h"
+
+struct cmdline_token_ops cmdline_token_ipaddr_ops = {
+       .parse = cmdline_parse_ipaddr,
+       .complete_get_nb = NULL,
+       .complete_get_elt = NULL,
+       .get_help = cmdline_get_help_ipaddr,
+};
+
+
+int 
+cmdline_parse_ipaddr(cmdline_parse_token_hdr_t *tk, const char *buf, void *res)
+{
+       struct cmdline_token_ipaddr *tk2 = (struct cmdline_token_ipaddr *)tk;
+       unsigned int token_len = 0;
+       char ip_str[INET6_ADDRSTRLEN+4]; /* '+4' is for prefixlen (if any) */
+       cmdline_ipaddr_t *ipaddr = res;
+       char *prefix, *prefix_end;
+       long prefixlen;
+
+       if (! *buf)
+               return -1;
+
+       while (!cmdline_isendoftoken(buf[token_len]))
+               token_len++;
+                        
+       /* if token is too big... */
+       if (token_len >= INET6_ADDRSTRLEN+4)
+               return -1;
+
+       snprintf(ip_str, token_len+1, "%s", buf);
+
+       /* convert the network prefix */
+       if (tk2->ipaddr_data.flags & CMDLINE_IPADDR_NETWORK) {
+               prefix = strrchr(ip_str, '/');
+               if (prefix == NULL)
+                       return -1;
+               *prefix = '\0';
+               prefix ++;
+               errno = 0;
+               prefixlen = strtol(prefix, &prefix_end, 10);
+               if (errno || (*prefix_end != '\0') )
+                       return -1;
+               ipaddr->prefixlen = prefixlen;
+       }
+       else {
+               ipaddr->prefixlen = 0;  
+       }
+
+       /* convert the IP addr */
+       if ((tk2->ipaddr_data.flags & CMDLINE_IPADDR_V4) &&
+           inet_pton(AF_INET, ip_str, &ipaddr->addr.ipv4) == 1) {
+               ipaddr->family = AF_INET;
+               return token_len;
+       }
+       if ((tk2->ipaddr_data.flags & CMDLINE_IPADDR_V6) &&
+           inet_pton(AF_INET6, ip_str, &ipaddr->addr.ipv6) == 1) {
+               ipaddr->family = AF_INET6;
+               return token_len;
+       }
+       return -1;
+                        
+}
+
+int cmdline_get_help_ipaddr(cmdline_parse_token_hdr_t *tk, char *dstbuf,
+                           unsigned int size)
+{
+       struct cmdline_token_ipaddr *tk2 = (struct cmdline_token_ipaddr *)tk;
+
+       switch (tk2->ipaddr_data.flags) {
+       case CMDLINE_IPADDR_V4:
+               snprintf(dstbuf, size, "IPv4");
+               break;
+       case CMDLINE_IPADDR_V6:
+               snprintf(dstbuf, size, "IPv6");
+               break;
+       case CMDLINE_IPADDR_V4|CMDLINE_IPADDR_V6:
+               snprintf(dstbuf, size, "IPv4/IPv6");
+               break;
+       case CMDLINE_IPADDR_NETWORK|CMDLINE_IPADDR_V4:
+               snprintf(dstbuf, size, "IPv4 network");
+               break;
+       case CMDLINE_IPADDR_NETWORK|CMDLINE_IPADDR_V6:
+               snprintf(dstbuf, size, "IPv6 network");
+               break;
+       case CMDLINE_IPADDR_NETWORK|CMDLINE_IPADDR_V4|CMDLINE_IPADDR_V6:
+               snprintf(dstbuf, size, "IPv4/IPv6 network");
+               break;
+       default:
+               snprintf(dstbuf, size, "IPaddr (bad flags)");
+               break;
+       }
+       return 0;
+}
diff --git a/src/lib/cmdline_parse_ipaddr.h b/src/lib/cmdline_parse_ipaddr.h
new file mode 100644 (file)
index 0000000..68576ab
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * 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.
+ */
+
+#ifndef _PARSE_IPADDR_H_
+#define _PARSE_IPADDR_H_
+
+#include "cmdline_parse.h"
+
+#define CMDLINE_IPADDR_V4      0x01
+#define CMDLINE_IPADDR_V6      0x02
+#define CMDLINE_IPADDR_NETWORK 0x04
+
+struct cmdline_ipaddr {
+       uint8_t family;
+       union {
+               struct in_addr ipv4;
+               struct in6_addr ipv6;
+       } addr;
+       unsigned int prefixlen; /* in case of network only */
+};
+typedef struct cmdline_ipaddr cmdline_ipaddr_t;
+
+struct cmdline_token_ipaddr_data {
+       uint8_t flags;
+};
+
+struct cmdline_token_ipaddr {
+       struct cmdline_token_hdr hdr;
+       struct cmdline_token_ipaddr_data ipaddr_data;
+};
+typedef struct cmdline_token_ipaddr cmdline_parse_token_ipaddr_t;
+
+extern struct cmdline_token_ops cmdline_token_ipaddr_ops;
+
+int cmdline_parse_ipaddr(cmdline_parse_token_hdr_t *tk, const char *srcbuf,
+                        void *res);
+int cmdline_get_help_ipaddr(cmdline_parse_token_hdr_t *tk, char *dstbuf,
+                           unsigned int size);
+
+#define TOKEN_IPADDR_INITIALIZER(structure, field)         \
+{                                                          \
+       .hdr = {                                            \
+               .ops = &cmdline_token_ipaddr_ops,           \
+               .offset = offsetof(structure, field),       \
+       },                                                  \
+       .ipaddr_data = {                                    \
+                .flags = CMDLINE_IPADDR_V4 |               \
+                        CMDLINE_IPADDR_V6,                 \
+       },                                                  \
+}
+
+#define TOKEN_IPV4_INITIALIZER(structure, field)           \
+{                                                          \
+       .hdr = {                                            \
+               .ops = &cmdline_token_ipaddr_ops,           \
+               .offset = offsetof(structure, field),       \
+       },                                                  \
+       .ipaddr_data = {                                    \
+                .flags = CMDLINE_IPADDR_V4,                \
+       },                                                  \
+}
+
+#define TOKEN_IPV6_INITIALIZER(structure, field)           \
+{                                                          \
+       .hdr = {                                            \
+               .ops = &cmdline_token_ipaddr_ops,           \
+               .offset = offsetof(structure, field),       \
+       },                                                  \
+       .ipaddr_data = {                                    \
+                .flags = CMDLINE_IPADDR_V6,                \
+       },                                                  \
+}
+
+#define TOKEN_IPNET_INITIALIZER(structure, field)          \
+{                                                          \
+       .hdr = {                                            \
+               .ops = &cmdline_token_ipaddr_ops,           \
+               .offset = offsetof(structure, field),       \
+       },                                                  \
+       .ipaddr_data = {                                    \
+                .flags = CMDLINE_IPADDR_V4 |               \
+                        CMDLINE_IPADDR_V6 |                \
+                        CMDLINE_IPADDR_NETWORK,            \
+       },                                                  \
+}
+
+#define TOKEN_IPV4NET_INITIALIZER(structure, field)        \
+{                                                          \
+       .hdr = {                                            \
+               .ops = &cmdline_token_ipaddr_ops,           \
+               .offset = offsetof(structure, field),       \
+       },                                                  \
+       .ipaddr_data = {                                    \
+                .flags = CMDLINE_IPADDR_V4 |               \
+                        CMDLINE_IPADDR_NETWORK,            \
+       },                                                  \
+}
+
+#define TOKEN_IPV6NET_INITIALIZER(structure, field)        \
+{                                                          \
+       .hdr = {                                            \
+               .ops = &cmdline_token_ipaddr_ops,           \
+               .offset = offsetof(structure, field),       \
+       },                                                  \
+       .ipaddr_data = {                                    \
+                .flags = CMDLINE_IPADDR_V4 |               \
+                        CMDLINE_IPADDR_NETWORK,            \
+       },                                                  \
+}
+
+
+
+#endif /* _PARSE_IPADDR_H_ */
diff --git a/src/lib/cmdline_parse_num.c b/src/lib/cmdline_parse_num.c
new file mode 100644 (file)
index 0000000..6a37a76
--- /dev/null
@@ -0,0 +1,455 @@
+/*
+ * 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 <stdio.h>
+#include <inttypes.h>
+#include <ctype.h>
+#include <string.h>
+
+#include "cmdline_parse.h"
+#include "cmdline_parse_num.h"
+
+//#define debug_printf(args...) printf(args)
+#define debug_printf(args...) do {} while(0)
+
+/* XXX to remove ? there should be something better */
+#define U08_MIN 0x00
+#define U08_MAX 0xFF
+#define U16_MIN 0x0000
+#define U16_MAX 0xFFFF
+#define U32_MIN 0x00000000
+#define U32_MAX 0xFFFFFFFF
+#define S08_MIN 0x80
+#define S08_MAX 0x7F
+#define S16_MIN 0x8000
+#define S16_MAX 0x7FFF
+#define S32_MIN 0x80000000
+#define S32_MAX 0x7FFFFFFF
+
+
+struct cmdline_token_ops cmdline_token_num_ops = {
+       .parse = cmdline_parse_num,
+       .complete_get_nb = NULL,
+       .complete_get_elt = NULL,
+       .get_help = cmdline_get_help_num,
+};
+
+
+enum num_parse_state_t {
+       START,
+       DEC_NEG,
+       BIN,
+       HEX,
+       FLOAT_POS,
+       FLOAT_NEG,
+
+       ERROR,
+
+       FIRST_OK, /* not used */
+       ZERO_OK,
+       HEX_OK,
+       OCTAL_OK,
+       BIN_OK,
+       DEC_NEG_OK,
+       DEC_POS_OK,
+       FLOAT_POS_OK,
+       FLOAT_NEG_OK,
+};
+
+/* Keep it sync with enum in .h */
+static const char * num_help[] = {
+       "UINT8", "UINT16", "UINT32",
+       "INT8", "INT16", "INT32",
+#ifndef NO_PARSE_FLOAT
+       "FLOAT",
+#endif
+};
+
+static inline int 
+add_to_res(unsigned int c, unsigned int *res, unsigned int base)
+{
+       /* overflow */
+       if ( (U32_MAX - c) / base < *res ) {
+               return -1;
+       }
+
+       *res = *res * base + c ;
+       return 0;
+}
+
+
+/* parse an int or a float */
+int 
+cmdline_parse_num(cmdline_parse_token_hdr_t *tk, const char *srcbuf, void *res)
+{
+       struct cmdline_token_num_data nd;
+       enum num_parse_state_t st = START;
+       const char * buf = srcbuf;
+       char c = *buf;
+       uint32_t res1=0, res2=0, res3=1;
+
+       memcpy(&nd, &((struct cmdline_token_num *)tk)->num_data, sizeof(nd));
+
+       while ( st != ERROR && c && ! cmdline_isendoftoken(c) ) {
+               debug_printf("%c %x -> ", c, c);
+               switch (st) {
+               case START:
+                       if (c == '-') {
+                               st = DEC_NEG;
+                       }
+                       else if (c == '0') {
+                               st = ZERO_OK;
+                       }
+#ifndef NO_PARSE_FLOAT
+                       else if (c == '.') {
+                               st = FLOAT_POS;
+                               res1 = 0;
+                       }
+#endif
+                       else if (c >= '1' && c <= '9') {
+                               if (add_to_res(c - '0', &res1, 10) < 0)
+                                       st = ERROR;
+                               else
+                                       st = DEC_POS_OK;
+                       }
+                       else  {
+                               st = ERROR;
+                       }
+                       break;
+
+               case ZERO_OK:
+                       if (c == 'x') {
+                               st = HEX;
+                       }
+                       else if (c == 'b') {
+                               st = BIN;
+                       }
+#ifndef NO_PARSE_FLOAT
+                       else if (c == '.') {
+                               st = FLOAT_POS;
+                               res1 = 0;
+                       }
+#endif
+                       else if (c >= '0' && c <= '7') {
+                               if (add_to_res(c - '0', &res1, 10) < 0)
+                                       st = ERROR;
+                               else
+                                       st = OCTAL_OK;
+                       }                       
+                       else  {
+                               st = ERROR;
+                       }
+                       break;
+
+               case DEC_NEG:
+                       if (c >= '0' && c <= '9') {
+                               if (add_to_res(c - '0', &res1, 10) < 0)
+                                       st = ERROR;
+                               else
+                                       st = DEC_NEG_OK;
+                       }
+#ifndef NO_PARSE_FLOAT
+                       else if (c == '.') {
+                               res1 = 0;
+                               st = FLOAT_NEG;
+                       }
+#endif
+                       else {
+                               st = ERROR;
+                       }
+                       break;
+
+               case DEC_NEG_OK:
+                       if (c >= '0' && c <= '9') {
+                               if (add_to_res(c - '0', &res1, 10) < 0)
+                                       st = ERROR;
+                       }
+#ifndef NO_PARSE_FLOAT
+                       else if (c == '.') {
+                               st = FLOAT_NEG;
+                       }
+#endif
+                       else {
+                               st = ERROR;
+                       }
+                       break;
+
+               case DEC_POS_OK:
+                       if (c >= '0' && c <= '9') {
+                               if (add_to_res(c - '0', &res1, 10) < 0)
+                                       st = ERROR;
+                       }
+#ifndef NO_PARSE_FLOAT
+                       else if (c == '.') {
+                               st = FLOAT_POS;
+                       }
+#endif
+                       else {
+                               st = ERROR;
+                       }
+                       break;
+
+               case HEX:
+                       st = HEX_OK;
+                       /* no break */
+               case HEX_OK:
+                       if (c >= '0' && c <= '9') {
+                               if (add_to_res(c - '0', &res1, 16) < 0)
+                                       st = ERROR;
+                       }
+                       else if (c >= 'a' && c <= 'f') {
+                               if (add_to_res(c - 'a' + 10, &res1, 16) < 0)
+                                       st = ERROR;
+                       }
+                       else if (c >= 'A' && c <= 'F') {
+                               if (add_to_res(c - 'A' + 10, &res1, 16) < 0)
+                                       st = ERROR;
+                       }
+                       else {
+                               st = ERROR;
+                       }
+                       break;
+
+
+               case OCTAL_OK:
+                       if (c >= '0' && c <= '7') {
+                               if (add_to_res(c - '0', &res1, 8) < 0)
+                                       st = ERROR;
+                       }
+                       else {
+                               st = ERROR;
+                       }
+                       break;
+
+               case BIN:
+                       st = BIN_OK; 
+                       /* no break */
+               case BIN_OK:
+                       if (c >= '0' && c <= '1') {
+                               if (add_to_res(c - '0', &res1, 2) < 0)
+                                       st = ERROR;
+                       }
+                       else {
+                               st = ERROR;
+                       }
+                       break;
+
+#ifndef NO_PARSE_FLOAT
+               case FLOAT_POS:
+                       if (c >= '0' && c <= '9') {
+                               if (add_to_res(c - '0', &res2, 10) < 0)
+                                       st = ERROR;
+                               else 
+                                       st = FLOAT_POS_OK;
+                               res3 = 10;
+                       }
+                       else {
+                               st = ERROR;
+                       }
+                       break;
+
+               case FLOAT_NEG:
+                       if (c >= '0' && c <= '9') {
+                               if (add_to_res(c - '0', &res2, 10) < 0)
+                                       st = ERROR;
+                               else 
+                                       st = FLOAT_NEG_OK;
+                               res3 = 10;
+                       }
+                       else {
+                               st = ERROR;
+                       }
+                       break;
+
+               case FLOAT_POS_OK:
+                       if (c >= '0' && c <= '9') {
+                               if (add_to_res(c - '0', &res2, 10) < 0)
+                                       st = ERROR;
+                               if (add_to_res(0, &res3, 10) < 0)
+                                       st = ERROR;
+                       }
+                       else {
+                               st = ERROR;
+                       }
+                       break;
+
+               case FLOAT_NEG_OK:
+                       if (c >= '0' && c <= '9') {
+                               if (add_to_res(c - '0', &res2, 10) < 0)
+                                       st = ERROR;
+                               if (add_to_res(0, &res3, 10) < 0)
+                                       st = ERROR;
+                       }
+                       else {
+                               st = ERROR;
+                       }
+                       break;
+#endif
+
+               default:
+                       debug_printf("not impl ");
+                       
+               }
+
+               /* XXX uint32_t et %d */
+               debug_printf("(%d)  (%d)  (%d)\n", res1, res2, res3);
+
+               buf ++;
+               c = *buf;
+
+               /* token too long */
+               if (buf-srcbuf > 127)
+                       return -1;
+       }
+       
+       switch (st) {
+       case ZERO_OK:
+       case DEC_POS_OK:
+       case HEX_OK:
+       case OCTAL_OK:
+       case BIN_OK:
+               if ( nd.type == INT8 && res1 <= S08_MAX ) {
+                       if (res)
+                               *(int8_t *)res = (int8_t) res1;
+                       return (buf-srcbuf);
+               }
+               else if ( nd.type == INT16 && res1 <= S16_MAX ) {
+                       if (res)
+                               *(int16_t *)res = (int16_t) res1;
+                       return (buf-srcbuf);
+               }
+               else if ( nd.type == INT32 && res1 <= S32_MAX ) {
+                       if (res)
+                               *(int32_t *)res = (int32_t) res1;
+                       return (buf-srcbuf);
+               }
+               else if ( nd.type == UINT8 && res1 <= U08_MAX ) {
+                       if (res)
+                               *(uint8_t *)res = (uint8_t) res1;
+                       return (buf-srcbuf);
+               }
+               else if (nd.type == UINT16  && res1 <= U16_MAX ) {
+                       if (res)
+                               *(uint16_t *)res = (uint16_t) res1;
+                       return (buf-srcbuf);
+               }
+               else if ( nd.type == UINT32 ) {
+                       if (res)
+                               *(uint32_t *)res = (uint32_t) res1;
+                       return (buf-srcbuf);
+               }
+#ifndef NO_PARSE_FLOAT
+               else if ( nd.type == FLOAT ) {
+                       if (res)
+                               *(float *)res = (float)res1;
+                       return (buf-srcbuf);
+               }
+#endif
+               else {
+                       return -1;
+               }
+               break;
+
+       case DEC_NEG_OK:
+               if ( nd.type == INT8 && res1 <= S08_MAX + 1 ) {
+                       if (res)
+                               *(int8_t *)res = - (int8_t) res1;
+                       return (buf-srcbuf);
+               }
+               else if ( nd.type == INT16 && res1 <= (uint16_t)S16_MAX + 1 ) {
+                       if (res)
+                               *(int16_t *)res = - (int16_t) res1;
+                       return (buf-srcbuf);
+               }
+               else if ( nd.type == INT32 && res1 <= (uint32_t)S32_MAX + 1 ) {
+                       if (res)
+                               *(int32_t *)res = - (int32_t) res1;
+                       return (buf-srcbuf);
+               }
+#ifndef NO_PARSE_FLOAT
+               else if ( nd.type == FLOAT ) {
+                       if (res)
+                               *(float *)res = - (float)res1;
+                       return (buf-srcbuf);
+               }
+#endif
+               else {
+                       return -1;
+               }
+               break;
+
+#ifndef NO_PARSE_FLOAT
+       case FLOAT_POS:
+       case FLOAT_POS_OK:
+               if ( nd.type == FLOAT ) {
+                       if (res)
+                               *(float *)res = (float)res1 + ((float)res2 / (float)res3);
+                       return (buf-srcbuf);
+                       
+               }
+               else {
+                       return -1;
+               }
+               break;
+
+       case FLOAT_NEG:
+       case FLOAT_NEG_OK:
+               if ( nd.type == FLOAT ) {
+                       if (res)
+                               *(float *)res = - ((float)res1 + ((float)res2 / (float)res3));
+                       return (buf-srcbuf);
+                       
+               }
+               else {
+                       return -1;
+               }
+               break;
+#endif
+       default:
+               debug_printf("error\n");
+               return -1;
+       }
+       return -1;
+}
+
+
+/* parse an int or a float */
+int 
+cmdline_get_help_num(cmdline_parse_token_hdr_t *tk, char *dstbuf, unsigned int size)
+{
+       struct cmdline_token_num_data nd;
+
+       memcpy(&nd, &((struct cmdline_token_num *)tk)->num_data, sizeof(nd));
+       
+       /* should not happen.... don't so this test */
+/*     if (nd.type >= (sizeof(num_help)/sizeof(const char *))) */
+/*             return -1; */
+
+       strncpy(dstbuf, num_help[nd.type], size);
+       dstbuf[size-1] = '\0';
+       return 0;
+}
diff --git a/src/lib/cmdline_parse_num.h b/src/lib/cmdline_parse_num.h
new file mode 100644 (file)
index 0000000..b149c92
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * 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.
+ */
+
+#ifndef _PARSE_NUM_H_
+#define _PARSE_NUM_H_
+
+#include "cmdline_parse.h"
+
+enum cmdline_numtype {
+       UINT8 = 0,
+       UINT16,
+       UINT32,
+       INT8,
+       INT16,
+       INT32,
+#ifndef NO_PARSE_FLOAT
+       FLOAT,
+#endif
+};
+
+struct cmdline_token_num_data {
+       enum cmdline_numtype type;
+};
+
+struct cmdline_token_num {
+       struct cmdline_token_hdr hdr;
+       struct cmdline_token_num_data num_data;
+};
+typedef struct cmdline_token_num cmdline_parse_token_num_t;
+
+extern struct cmdline_token_ops cmdline_token_num_ops;
+
+int cmdline_parse_num(cmdline_parse_token_hdr_t *tk, 
+                     const char *srcbuf, void *res);
+int cmdline_get_help_num(cmdline_parse_token_hdr_t *tk, 
+                        char *dstbuf, unsigned int size);
+
+#define TOKEN_NUM_INITIALIZER(structure, field, numtype)   \
+{                                                         \
+       .hdr = {                                           \
+               .ops = &cmdline_token_num_ops,             \
+               .offset = offsetof(structure, field),      \
+       },                                                 \
+       .num_data = {                                      \
+               .type = numtype,                           \
+       },                                                 \
+}
+
+#endif /* _PARSE_NUM_H_ */
diff --git a/src/lib/cmdline_parse_string.c b/src/lib/cmdline_parse_string.c
new file mode 100644 (file)
index 0000000..eb4553b
--- /dev/null
@@ -0,0 +1,196 @@
+/*
+ * 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 <stdio.h>
+#include <inttypes.h>
+#include <ctype.h>
+#include <string.h>
+
+#include "cmdline_parse.h"
+#include "cmdline_parse_string.h"
+
+struct cmdline_token_ops cmdline_token_string_ops = {
+       .parse = cmdline_parse_string,
+       .complete_get_nb = cmdline_complete_get_nb_string,
+       .complete_get_elt = cmdline_complete_get_elt_string,
+       .get_help = cmdline_get_help_string,
+};
+
+#define MULTISTRING_HELP "Mul-choice STRING"
+#define ANYSTRING_HELP   "Any STRING"
+#define FIXEDSTRING_HELP "Fixed STRING"
+
+static unsigned int
+get_token_len(const char *s)
+{
+        char c;
+       unsigned int i=0;
+
+       c = s[i];
+        while (c!='#' && c!='\0') {
+                i++;
+               c = s[i];
+       }
+        return i;
+}
+
+static const char *
+get_next_token(const char *s)
+{
+       unsigned int i;
+       i = get_token_len(s);
+       if (s[i] == '#')
+               return s+i+1;
+       return NULL;
+}
+
+int 
+cmdline_parse_string(cmdline_parse_token_hdr_t *tk, const char *buf, void *res)
+{
+       struct cmdline_token_string *tk2 = (struct cmdline_token_string *)tk;
+       struct cmdline_token_string_data *sd = &tk2->string_data;;
+       unsigned int token_len;
+       const char *str;
+
+       if (! *buf)
+               return -1;
+
+        /* fixed string */
+        if (sd->str) {
+               str = sd->str;
+                do {
+                        token_len = get_token_len(str);
+                        
+                        /* if token is too big... */
+                        if (token_len >= STR_TOKEN_SIZE - 1) {
+                               continue;
+                        }
+                        
+                        if ( strncmp(buf, str, token_len) ) {
+                               continue;
+                        }
+                        
+                        if ( !cmdline_isendoftoken(*(buf+token_len)) ) {
+                               continue;
+                        }
+
+                        break;
+                } while ( (str = get_next_token(str)) != NULL );
+
+               if (!str)
+                       return -1;
+        }
+        /* unspecified string */
+        else {
+                token_len=0;
+                while(!cmdline_isendoftoken(buf[token_len]) && 
+                      token_len < (STR_TOKEN_SIZE-1))
+                        token_len++;
+
+                /* return if token too long */
+                if (token_len >= STR_TOKEN_SIZE - 1) {
+                        return -1;
+                }
+        }
+        
+       if (res) {
+               /* we are sure that token_len is < STR_TOKEN_SIZE-1 */
+               strncpy(res, buf, token_len);
+               *((char *)res + token_len) = 0;
+       }
+
+        return token_len;
+}
+
+int cmdline_complete_get_nb_string(cmdline_parse_token_hdr_t *tk)
+{
+       struct cmdline_token_string *tk2 = (struct cmdline_token_string *)tk;
+       struct cmdline_token_string_data *sd = &tk2->string_data;;
+       int ret=1;
+       const char *str;
+
+       if (!sd->str)
+               return 0;
+
+       str = sd->str;
+       while( (str = get_next_token(str)) != NULL ) {
+               ret++;
+       }
+       return ret;
+}
+
+int cmdline_complete_get_elt_string(cmdline_parse_token_hdr_t *tk, int idx, 
+                                   char *dstbuf, unsigned int size)
+{
+       struct cmdline_token_string *tk2 = (struct cmdline_token_string *)tk;
+       struct cmdline_token_string_data *sd = &tk2->string_data;;
+       const char *s;
+       unsigned int len;
+
+       s = sd->str;
+
+       while (idx-- && s)
+               s = get_next_token(s);
+
+       if (!s)
+               return -1;
+
+       len = get_token_len(s);
+       if (len > size - 1)
+               return -1;
+
+       memcpy(dstbuf, s, len);
+       dstbuf[len] = '\0';
+       return 0;
+}
+
+
+int cmdline_get_help_string(cmdline_parse_token_hdr_t *tk, char *dstbuf,
+                           unsigned int size)
+{
+       struct cmdline_token_string *tk2 = (struct cmdline_token_string *)tk;
+       struct cmdline_token_string_data *sd = &tk2->string_data;;
+       const char *s;
+       
+       s = sd->str;
+
+       if (s) {
+               if (get_next_token(s)) {
+                       strncpy(dstbuf, MULTISTRING_HELP, size);
+               }
+               else {
+                       strncpy(dstbuf, FIXEDSTRING_HELP, size);
+               }
+       }
+       else {
+               strncpy(dstbuf, ANYSTRING_HELP, size);
+       }
+       
+       dstbuf[size-1] = '\0';
+
+       return 0;
+}
diff --git a/src/lib/cmdline_parse_string.h b/src/lib/cmdline_parse_string.h
new file mode 100644 (file)
index 0000000..de34ba6
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ */
+
+#ifndef _PARSE_STRING_H_
+#define _PARSE_STRING_H_
+
+#include "cmdline_parse.h"
+
+/* size of a parsed string */
+#define STR_TOKEN_SIZE 128
+
+typedef char cmdline_fixed_string_t[STR_TOKEN_SIZE];
+
+struct cmdline_token_string_data {
+       const char *str;
+};
+
+struct cmdline_token_string {
+       struct cmdline_token_hdr hdr;
+       struct cmdline_token_string_data string_data;
+};
+typedef struct cmdline_token_string cmdline_parse_token_string_t;
+
+extern struct cmdline_token_ops cmdline_token_string_ops;
+
+int cmdline_parse_string(cmdline_parse_token_hdr_t *tk, const char *srcbuf,
+                        void *res);
+int cmdline_complete_get_nb_string(cmdline_parse_token_hdr_t *tk);
+int cmdline_complete_get_elt_string(cmdline_parse_token_hdr_t *tk, int idx, 
+                                   char *dstbuf, unsigned int size);
+int cmdline_get_help_string(cmdline_parse_token_hdr_t *tk, char *dstbuf,
+                           unsigned int size);
+
+#define TOKEN_STRING_INITIALIZER(structure, field, string)  \
+{                                                          \
+       .hdr = {                                            \
+               .ops = &cmdline_token_string_ops,           \
+               .offset = offsetof(structure, field),       \
+       },                                                  \
+       .string_data = {                                    \
+                .str = string,                             \
+       },                                                  \
+}
+
+#endif /* _PARSE_STRING_H_ */
diff --git a/src/lib/cmdline_rdline.c b/src/lib/cmdline_rdline.c
new file mode 100644 (file)
index 0000000..287c7d8
--- /dev/null
@@ -0,0 +1,600 @@
+/*
+ * 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 <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdarg.h>
+#include <ctype.h>
+
+#include "cmdline_cirbuf.h"
+#include "cmdline_rdline.h"
+
+static void rdline_puts(struct rdline *rdl, const char *buf);
+static void rdline_miniprintf(struct rdline *rdl, 
+                             const char *buf, unsigned int val);
+
+#ifndef NO_RDLINE_HISTORY
+static void rdline_remove_old_history_item(struct rdline *rdl);
+static void rdline_remove_first_history_item(struct rdline *rdl);
+static unsigned int rdline_get_history_size(struct rdline *rdl);
+#endif /* !NO_RDLINE_HISTORY */
+
+
+/* isblank() needs _XOPEN_SOURCE >= 600 || _ISOC99_SOURCE, so use our
+ * own. */
+static int
+isblank2(char c)
+{
+       if (c == ' ' || 
+           c == '\t' )
+               return 1;
+       return 0;
+}
+
+void rdline_init(struct rdline *rdl, 
+                rdline_write_char_t *write_char,
+                rdline_validate_t *validate,
+                rdline_complete_t *complete)
+{
+       memset(rdl, 0, sizeof(*rdl));
+       rdl->validate = validate;
+       rdl->complete = complete;
+       rdl->write_char = write_char;
+       rdl->status = RDLINE_INIT;
+#ifndef NO_RDLINE_HISTORY
+       cirbuf_init(&rdl->history, rdl->history_buf, 0, RDLINE_HISTORY_BUF_SIZE);
+#endif /* !NO_RDLINE_HISTORY */
+}
+
+void
+rdline_newline(struct rdline *rdl, const char *prompt)
+{
+       unsigned int i;
+
+       vt100_init(&rdl->vt100);
+       cirbuf_init(&rdl->left, rdl->left_buf, 0, RDLINE_BUF_SIZE);
+       cirbuf_init(&rdl->right, rdl->right_buf, 0, RDLINE_BUF_SIZE);
+
+       if (prompt != rdl->prompt)
+               memcpy(rdl->prompt, prompt, sizeof(rdl->prompt)-1);
+       rdl->prompt_size = strlen(prompt);
+
+       for (i=0 ; i<rdl->prompt_size ; i++)
+               rdl->write_char(rdl, rdl->prompt[i]);
+       rdl->status = RDLINE_RUNNING;
+
+#ifndef NO_RDLINE_HISTORY 
+       rdl->history_cur_line = -1;
+#endif /* !NO_RDLINE_HISTORY */
+}
+
+void 
+rdline_stop(struct rdline *rdl)
+{
+       rdl->status = RDLINE_INIT;
+}
+
+void
+rdline_restart(struct rdline *rdl)
+{
+       rdl->status = RDLINE_RUNNING;
+}
+
+const char *
+rdline_get_buffer(struct rdline *rdl)
+{
+       unsigned int len_l, len_r;
+       cirbuf_align_left(&rdl->left);
+       cirbuf_align_left(&rdl->right);
+
+       len_l = CIRBUF_GET_LEN(&rdl->left);
+       len_r = CIRBUF_GET_LEN(&rdl->right);
+       memcpy(rdl->left_buf+len_l, rdl->right_buf, len_r);
+
+       rdl->left_buf[len_l + len_r] = '\n';
+       rdl->left_buf[len_l + len_r + 1] = '\0';
+       return rdl->left_buf;
+}
+
+static void
+display_right_buffer(struct rdline *rdl)
+{
+       unsigned int i;
+       char tmp;
+
+       rdline_puts(rdl, vt100_clear_right);
+       if (!CIRBUF_IS_EMPTY(&rdl->right)) {
+               CIRBUF_FOREACH(&rdl->right, i, tmp) {
+                       rdl->write_char(rdl, tmp);
+               }
+               rdline_miniprintf(rdl, vt100_multi_left, 
+                                 CIRBUF_GET_LEN(&rdl->right));
+       }
+}
+
+void rdline_redisplay(struct rdline *rdl)
+{
+       unsigned int i;
+       char tmp;
+
+       rdline_puts(rdl, vt100_home);
+       for (i=0 ; i<rdl->prompt_size ; i++)
+               rdl->write_char(rdl, rdl->prompt[i]);
+       CIRBUF_FOREACH(&rdl->left, i, tmp) {
+               rdl->write_char(rdl, tmp);
+       }
+       display_right_buffer(rdl);
+}
+
+int
+rdline_char_in(struct rdline *rdl, char c)
+{
+       unsigned int i;
+       int cmd;
+       char tmp;
+#ifndef NO_RDLINE_HISTORY
+       char *buf;
+#endif
+       
+       if (rdl->status != RDLINE_RUNNING)
+               return -1;
+
+       cmd = vt100_parser(&rdl->vt100, c);
+       if (cmd == -2)
+               return 0;
+
+       if (cmd >= 0) {
+               switch (cmd) {
+               case CMDLINE_KEY_CTRL_B:
+               case CMDLINE_KEY_LEFT_ARR:
+                       if (CIRBUF_IS_EMPTY(&rdl->left))
+                               break;
+                       tmp = cirbuf_get_tail(&rdl->left);
+                       cirbuf_del_tail(&rdl->left);
+                       cirbuf_add_head(&rdl->right, tmp);
+                       rdline_puts(rdl, vt100_left_arr);
+                       break;
+
+               case CMDLINE_KEY_CTRL_F:
+               case CMDLINE_KEY_RIGHT_ARR:
+                       if (CIRBUF_IS_EMPTY(&rdl->right))
+                               break;
+                       tmp = cirbuf_get_head(&rdl->right);
+                       cirbuf_del_head(&rdl->right);
+                       cirbuf_add_tail(&rdl->left, tmp);
+                       rdline_puts(rdl, vt100_right_arr);
+                       break;
+
+               case CMDLINE_KEY_WLEFT:
+                       while (! CIRBUF_IS_EMPTY(&rdl->left) && 
+                              (tmp = cirbuf_get_tail(&rdl->left)) && 
+                              isblank2(tmp)) {
+                               rdline_puts(rdl, vt100_left_arr);
+                               cirbuf_del_tail(&rdl->left);
+                               cirbuf_add_head(&rdl->right, tmp);
+                       }
+                       while (! CIRBUF_IS_EMPTY(&rdl->left) && 
+                              (tmp = cirbuf_get_tail(&rdl->left)) && 
+                              !isblank2(tmp)) {
+                               rdline_puts(rdl, vt100_left_arr);
+                               cirbuf_del_tail(&rdl->left);
+                               cirbuf_add_head(&rdl->right, tmp);
+                       }                       
+                       break;
+
+               case CMDLINE_KEY_WRIGHT:
+                       while (! CIRBUF_IS_EMPTY(&rdl->right) && 
+                              (tmp = cirbuf_get_head(&rdl->right)) && 
+                              isblank2(tmp)) {
+                               rdline_puts(rdl, vt100_right_arr);
+                               cirbuf_del_head(&rdl->right);
+                               cirbuf_add_tail(&rdl->left, tmp);
+                       }
+                       while (! CIRBUF_IS_EMPTY(&rdl->right) && 
+                              (tmp = cirbuf_get_head(&rdl->right)) && 
+                              !isblank2(tmp)) {
+                               rdline_puts(rdl, vt100_right_arr);
+                               cirbuf_del_head(&rdl->right);
+                               cirbuf_add_tail(&rdl->left, tmp);
+                       }                       
+                       break;
+
+               case CMDLINE_KEY_BKSPACE:
+                       if(!cirbuf_del_tail_safe(&rdl->left)) {
+                               rdline_puts(rdl, vt100_bs);
+                               display_right_buffer(rdl);
+                       }
+                       break;
+
+               case CMDLINE_KEY_META_BKSPACE:
+                       while (! CIRBUF_IS_EMPTY(&rdl->left) && isblank2(cirbuf_get_tail(&rdl->left))) {
+                               rdline_puts(rdl, vt100_bs);
+                               cirbuf_del_tail(&rdl->left);
+                       }
+                       while (! CIRBUF_IS_EMPTY(&rdl->left) && !isblank2(cirbuf_get_tail(&rdl->left))) {
+                               rdline_puts(rdl, vt100_bs);
+                               cirbuf_del_tail(&rdl->left);
+                       }
+                       display_right_buffer(rdl);
+                       break;
+
+               case CMDLINE_KEY_SUPPR:
+               case CMDLINE_KEY_CTRL_D:
+                       if (cmd == CMDLINE_KEY_CTRL_D && 
+                           CIRBUF_IS_EMPTY(&rdl->left) &&
+                           CIRBUF_IS_EMPTY(&rdl->right)) {
+                               return -2;
+                       }
+                       if (!cirbuf_del_head_safe(&rdl->right)) {
+                               display_right_buffer(rdl);
+                       }
+                       break;
+
+               case CMDLINE_KEY_CTRL_A:
+                       if (CIRBUF_IS_EMPTY(&rdl->left))
+                               break;
+                       rdline_miniprintf(rdl, vt100_multi_left, 
+                                           CIRBUF_GET_LEN(&rdl->left));
+                       while (! CIRBUF_IS_EMPTY(&rdl->left)) {
+                               tmp = cirbuf_get_tail(&rdl->left);
+                               cirbuf_del_tail(&rdl->left);
+                               cirbuf_add_head(&rdl->right, tmp);
+                       }
+                       break;
+
+               case CMDLINE_KEY_CTRL_E:
+                       if (CIRBUF_IS_EMPTY(&rdl->right))
+                               break;
+                       rdline_miniprintf(rdl, vt100_multi_right, 
+                                           CIRBUF_GET_LEN(&rdl->right));
+                       while (! CIRBUF_IS_EMPTY(&rdl->right)) {
+                               tmp = cirbuf_get_head(&rdl->right);
+                               cirbuf_del_head(&rdl->right);
+                               cirbuf_add_tail(&rdl->left, tmp);
+                       }
+                       break;
+
+#ifndef NO_RDLINE_KILL_BUF
+               case CMDLINE_KEY_CTRL_K:
+                       cirbuf_get_buf_head(&rdl->right, rdl->kill_buf, RDLINE_BUF_SIZE);
+                       rdl->kill_size = CIRBUF_GET_LEN(&rdl->right);
+                       cirbuf_del_buf_head(&rdl->right, rdl->kill_size);
+                       rdline_puts(rdl, vt100_clear_right);
+                       break;
+
+               case CMDLINE_KEY_CTRL_Y:
+                       i=0;
+                       while(CIRBUF_GET_LEN(&rdl->right) + CIRBUF_GET_LEN(&rdl->left) <
+                             RDLINE_BUF_SIZE && 
+                             i < rdl->kill_size) {
+                               cirbuf_add_tail(&rdl->left, rdl->kill_buf[i]);
+                               rdl->write_char(rdl, rdl->kill_buf[i]);
+                               i++;
+                       }
+                       display_right_buffer(rdl);
+                       break;
+#endif /* !NO_RDLINE_KILL_BUF */
+
+               case CMDLINE_KEY_CTRL_C:
+                       rdline_puts(rdl, "\r\n");
+                       rdline_newline(rdl, rdl->prompt);
+                       break;
+
+               case CMDLINE_KEY_CTRL_L:
+                       rdline_redisplay(rdl);
+                       break;
+
+               case CMDLINE_KEY_TAB:
+               case CMDLINE_KEY_HELP:
+                       cirbuf_align_left(&rdl->left);
+                       rdl->left_buf[CIRBUF_GET_LEN(&rdl->left)] = '\0'; 
+                       if (rdl->complete) {
+                               char tmp_buf[BUFSIZ];
+                               int complete_state;
+                               int ret;
+                               int tmp_size;
+
+                               if (cmd == CMDLINE_KEY_TAB)
+                                       complete_state = 0;
+                               else
+                                       complete_state = -1;
+
+                               /* see in parse.h for help on complete() */
+                               ret = rdl->complete(rdl, rdl->left_buf,
+                                                   tmp_buf, sizeof(tmp_buf),
+                                                   &complete_state);
+                               /* no completion or error */
+                               if (ret <= 0) {
+                                       return 2;
+                               }
+
+                               tmp_size = strlen(tmp_buf);
+                               /* add chars */
+                               if (ret == 2) {
+                                       i=0;
+                                       while(CIRBUF_GET_LEN(&rdl->right) + CIRBUF_GET_LEN(&rdl->left) <
+                                             RDLINE_BUF_SIZE && 
+                                             i < tmp_size) {
+                                               cirbuf_add_tail(&rdl->left, tmp_buf[i]);
+                                               rdl->write_char(rdl, tmp_buf[i]);
+                                               i++;
+                                       }
+                                       display_right_buffer(rdl);
+                                       return 2; /* ?? */
+                               }
+
+                               /* choice */
+                               rdline_puts(rdl, "\r\n");
+                               while (ret) {
+                                       rdl->write_char(rdl, ' ');
+                                       for (i=0 ; tmp_buf[i] ; i++)
+                                               rdl->write_char(rdl, tmp_buf[i]);
+                                       rdline_puts(rdl, "\r\n");
+                                       ret = rdl->complete(rdl, rdl->left_buf,
+                                                           tmp_buf, sizeof(tmp_buf),
+                                                           &complete_state);
+                               }
+
+                               rdline_redisplay(rdl);
+                       }
+                       return 2;
+
+               case CMDLINE_KEY_RETURN:
+               case CMDLINE_KEY_RETURN2:
+                       rdline_get_buffer(rdl);
+                       rdl->status = RDLINE_INIT;
+                       rdline_puts(rdl, "\r\n");
+#ifndef NO_RDLINE_HISTORY
+                       if (rdl->history_cur_line != -1)
+                               rdline_remove_first_history_item(rdl);
+#endif
+
+                       if (rdl->validate)
+                               rdl->validate(rdl, rdl->left_buf, CIRBUF_GET_LEN(&rdl->left)+2);
+                       return 1;
+                       
+#ifndef NO_RDLINE_HISTORY
+               case CMDLINE_KEY_UP_ARR:
+                       if (rdl->history_cur_line == 0) {
+                               rdline_remove_first_history_item(rdl);
+                       }
+                       if (rdl->history_cur_line <= 0) {
+                               rdline_add_history(rdl, rdline_get_buffer(rdl));
+                               rdl->history_cur_line = 0;
+                       }
+                       
+                       buf = rdline_get_history_item(rdl, rdl->history_cur_line + 1);
+                       if (!buf)
+                               break;
+                       
+                       rdl->history_cur_line ++;
+                       vt100_init(&rdl->vt100);
+                       cirbuf_init(&rdl->left, rdl->left_buf, 0, RDLINE_BUF_SIZE);
+                       cirbuf_init(&rdl->right, rdl->right_buf, 0, RDLINE_BUF_SIZE);
+                       cirbuf_add_buf_tail(&rdl->left, buf, strlen(buf));
+                       rdline_redisplay(rdl);
+                       break;
+
+               case CMDLINE_KEY_DOWN_ARR:
+                       if (rdl->history_cur_line - 1 < 0)
+                               break;
+                       
+                       rdl->history_cur_line --;
+                       buf = rdline_get_history_item(rdl, rdl->history_cur_line);
+                       if (!buf)
+                               break;
+                       vt100_init(&rdl->vt100);
+                       cirbuf_init(&rdl->left, rdl->left_buf, 0, RDLINE_BUF_SIZE);
+                       cirbuf_init(&rdl->right, rdl->right_buf, 0, RDLINE_BUF_SIZE);
+                       cirbuf_add_buf_tail(&rdl->left, buf, strlen(buf));
+                       rdline_redisplay(rdl);
+
+                       break;
+#endif /* !NO_RDLINE_HISTORY */
+
+
+               default:
+                       break;
+               }
+
+               return 0;
+       }
+       
+       if (! isprint(c))
+               return 0;
+
+       /* standard chars */
+       if (CIRBUF_GET_LEN(&rdl->left) + CIRBUF_GET_LEN(&rdl->right) >= RDLINE_BUF_SIZE)
+               return 0;
+               
+       if (cirbuf_add_tail_safe(&rdl->left, c))
+               return 0;
+
+       rdl->write_char(rdl, c);
+       display_right_buffer(rdl);
+
+       return 0;
+}
+
+
+/* HISTORY */
+
+#ifndef NO_RDLINE_HISTORY
+static void
+rdline_remove_old_history_item(struct rdline * rdl)
+{
+       char tmp;
+
+       while (! CIRBUF_IS_EMPTY(&rdl->history) ) {
+               tmp = cirbuf_get_head(&rdl->history);
+               cirbuf_del_head(&rdl->history);
+               if (!tmp)
+                       break;
+       }
+}
+
+static void
+rdline_remove_first_history_item(struct rdline * rdl)
+{
+       char tmp;
+
+       if ( CIRBUF_IS_EMPTY(&rdl->history) ) {
+               return;
+       }
+       else {
+               cirbuf_del_tail(&rdl->history);
+       }
+
+       while (! CIRBUF_IS_EMPTY(&rdl->history) ) {
+               tmp = cirbuf_get_tail(&rdl->history);
+               if (!tmp)
+                       break;
+               cirbuf_del_tail(&rdl->history);
+       }
+}
+
+static unsigned int
+rdline_get_history_size(struct rdline * rdl)
+{
+       unsigned int i, tmp, ret=0;
+
+       CIRBUF_FOREACH(&rdl->history, i, tmp) {
+               if (tmp == 0)
+                       ret ++;
+       }
+
+       return ret;
+}
+
+char *
+rdline_get_history_item(struct rdline * rdl, unsigned int idx)
+{
+       unsigned int len, i, tmp;
+
+       len = rdline_get_history_size(rdl);
+       if ( idx >= len ) {
+               return NULL;
+       }
+
+       cirbuf_align_left(&rdl->history);
+
+       CIRBUF_FOREACH(&rdl->history, i, tmp) {
+               if ( idx == len - 1) {
+                       return rdl->history_buf + i;
+               }
+               if (tmp == 0)
+                       len --;
+       }
+
+       return NULL;
+}
+
+int 
+rdline_add_history(struct rdline * rdl, const char * buf)
+{
+       unsigned int len, i;
+
+       len = strlen(buf);
+       for (i=0; i<len ; i++) {
+               if (buf[i] == '\n') {
+                       len = i;
+                       break;
+               }
+       }
+
+       if ( len >= RDLINE_HISTORY_BUF_SIZE )
+               return -1;
+
+       while ( len >= CIRBUF_GET_FREELEN(&rdl->history) ) {
+               rdline_remove_old_history_item(rdl);
+       }
+
+       cirbuf_add_buf_tail(&rdl->history, buf, len);
+       cirbuf_add_tail(&rdl->history, 0);
+       
+       return 0;
+}
+
+void
+rdline_clear_history(struct rdline * rdl)
+{
+       cirbuf_init(&rdl->history, rdl->history_buf, 0, RDLINE_HISTORY_BUF_SIZE);
+}
+
+#else /* !NO_RDLINE_HISTORY */
+
+int rdline_add_history(struct rdline * rdl, const char * buf) {return -1;}
+void rdline_clear_history(struct rdline * rdl) {}
+char * rdline_get_history_item(struct rdline * rdl, unsigned int i) {return NULL;}
+
+
+#endif /* !NO_RDLINE_HISTORY */
+
+
+/* STATIC USEFUL FUNCS */
+
+static void 
+rdline_puts(struct rdline * rdl, const char * buf)
+{
+       char c;
+       while ( (c = *(buf++)) != '\0' ) {
+               rdl->write_char(rdl, c);
+       }
+}
+
+/* a very very basic printf with one arg and one format 'u' */
+static void 
+rdline_miniprintf(struct rdline *rdl, const char * buf, unsigned int val)
+{
+       char c, started=0, div=100;
+
+       while ( (c=*(buf++)) ) {
+               if (c != '%') {
+                       rdl->write_char(rdl, c);
+                       continue;
+               }
+               c = *(buf++);
+               if (c != 'u') {
+                       rdl->write_char(rdl, '%');
+                       rdl->write_char(rdl, c);
+                       continue;
+               }
+               /* val is never more than 255 */
+               while (div) {
+                       c = val / div;
+                       if (c || started) {
+                               rdl->write_char(rdl, c+'0');
+                               started = 1;
+                       }
+                       val %= div;
+                       div /= 10;
+               }
+       }
+}
+
diff --git a/src/lib/cmdline_rdline.h b/src/lib/cmdline_rdline.h
new file mode 100644 (file)
index 0000000..f8437a5
--- /dev/null
@@ -0,0 +1,217 @@
+/*
+ * 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.
+ */
+
+#ifndef _RDLINE_H_
+#define _RDLINE_H_
+
+/**
+ * This file is a small equivalent to the GNU readline library, but it
+ * was originally designed for small systems, like Atmel AVR
+ * microcontrollers (8 bits). Indeed, we don't use any malloc that is
+ * sometimes not implemented (or just not recommended) on such
+ * systems.
+ * 
+ * Obviously, it does not support as many things as the GNU readline,
+ * but at least it supports some interresting features like a kill
+ * buffer and a command history.
+ *
+ * It also have a feature that does not have the GNU readline (as far
+ * as I know): we can have several instances of it running at the same
+ * time, even on a monothread program, since it works with callbacks.
+ *
+ * The lib is designed for a client-side or a server-side use:
+ * - server-side: the server receives all data from a socket, including 
+ *   control chars, like arrows, tabulations, ... The client is 
+ *   very simple, it can be a telnet or a minicom through a serial line.
+ * - client-side: the client receives its data through its stdin for
+ *   instance. 
+ */
+
+#include <cmdline_cirbuf.h>
+#include <cmdline_vt100.h>
+
+#define vt100_bell         "\007"
+#define vt100_bs           "\010"
+#define vt100_bs_clear     "\010 \010"
+#define vt100_tab          "\011"
+#define vt100_crnl         "\012\015"
+#define vt100_clear_right  "\033[0K"
+#define vt100_clear_left   "\033[1K"
+#define vt100_clear_down   "\033[0J"
+#define vt100_clear_up     "\033[1J"
+#define vt100_clear_line   "\033[2K"
+#define vt100_clear_screen "\033[2J"
+#define vt100_up_arr       "\033\133\101"
+#define vt100_down_arr     "\033\133\102"
+#define vt100_right_arr    "\033\133\103"
+#define vt100_left_arr     "\033\133\104"
+#define vt100_multi_right  "\033\133%uC"
+#define vt100_multi_left   "\033\133%uD"
+#define vt100_suppr        "\033\133\063\176"
+#define vt100_home         "\033M\033E"
+#define vt100_word_left    "\033\142"
+#define vt100_word_right   "\033\146"
+
+/* configuration */
+#define RDLINE_BUF_SIZE 256
+#define RDLINE_PROMPT_SIZE  32
+#define RDLINE_VT100_BUF_SIZE  8
+#define RDLINE_HISTORY_BUF_SIZE BUFSIZ
+#define RDLINE_HISTORY_MAX_LINE 64
+
+enum rdline_status {
+       RDLINE_INIT,
+       RDLINE_RUNNING,
+};
+
+struct rdline;
+
+typedef void (rdline_write_char_t)(struct rdline *rdl, char);
+typedef void (rdline_validate_t)(struct rdline *rdl,
+                                const char *buf, unsigned int size);
+typedef int (rdline_complete_t)(struct rdline *rdl, const char *buf,
+                               char *dstbuf, unsigned int dstsize,
+                               int *state);
+
+struct rdline {
+       enum rdline_status status;
+       /* rdline bufs */
+       struct cirbuf left;
+       struct cirbuf right;
+       char left_buf[RDLINE_BUF_SIZE+2]; /* reserve 2 chars for the \n\0 */
+       char right_buf[RDLINE_BUF_SIZE];
+
+       char prompt[RDLINE_PROMPT_SIZE];
+       unsigned int prompt_size;
+
+#ifndef NO_RDLINE_KILL_BUF
+       char kill_buf[RDLINE_BUF_SIZE];
+       unsigned int kill_size;
+#endif
+
+#ifndef NO_RDLINE_HISTORY
+       /* history */
+       struct cirbuf history;
+       char history_buf[RDLINE_HISTORY_BUF_SIZE];
+       int history_cur_line;
+#endif
+
+       /* callbacks and func pointers */
+       rdline_write_char_t *write_char;
+       rdline_validate_t *validate;
+       rdline_complete_t *complete;
+
+       /* vt100 parser */
+       struct cmdline_vt100 vt100;
+
+       /* opaque pointer */
+       void *opaque;
+};
+
+/**
+ * Init fields for a struct rdline. Call this only once at the beginning
+ * of your program.
+ * \param rdl A pointer to an uninitialized struct rdline
+ * \param write_char The function used by the function to write a character
+ * \param validate A pointer to the function to execute when the 
+ *                 user validates the buffer.
+ * \param complete A pointer to the function to execute when the 
+ *                 user completes the buffer.
+ */
+void rdline_init(struct rdline *rdl, 
+                rdline_write_char_t *write_char,
+                rdline_validate_t *validate,
+                rdline_complete_t *complete);
+
+
+/**
+ * Init the current buffer, and display a prompt.
+ * \param rdl A pointer to a struct rdline
+ * \param prompt A string containing the prompt
+ */
+void rdline_newline(struct rdline *rdl, const char *prompt);
+
+/**
+ * Call it and all received chars will be ignored.
+ * \param rdl A pointer to a struct rdline
+ */
+void rdline_stop(struct rdline *rdl);
+
+/**
+ * Restart after a call to rdline_stop()
+ * \param rdl A pointer to a struct rdline
+ */
+void rdline_restart(struct rdline *rdl);
+
+/**
+ * Redisplay the current buffer
+ * \param rdl A pointer to a struct rdline
+ */
+void rdline_redisplay(struct rdline *rdl);
+
+
+/**
+ * append a char to the readline buffer. 
+ * Return 1 when the line has been validated.
+ * Return 2 when the user asked to complete the buffer.
+ * Return -1 if it is not running.
+ * Return -2 if EOF (ctrl-d on an empty line).
+ * Else return 0.
+ * XXX error case when the buffer is full ?
+ *
+ * \param rdl A pointer to a struct rdline
+ * \param c The character to append
+ */
+int rdline_char_in(struct rdline *rdl, char c);
+
+/**
+ * Return the current buffer, terminated by '\0'.
+ * \param rdl A pointer to a struct rdline
+ */
+const char *rdline_get_buffer(struct rdline *rdl);
+
+
+/**
+ * Add the buffer to history.
+ * return < 0 on error.
+ * \param rdl A pointer to a struct rdline
+ * \param buf A buffer that is terminated by '\0'
+ */
+int rdline_add_history(struct rdline *rdl, const char *buf);
+
+/**
+ * Clear current history
+ * \param rdl A pointer to a struct rdline
+ */
+void rdline_clear_history(struct rdline *rdl);
+
+/**
+ * Get the i-th history item
+ */
+char *rdline_get_history_item(struct rdline *rdl, unsigned int i);
+
+#endif /* _RDLINE_H_ */
diff --git a/src/lib/cmdline_socket.c b/src/lib/cmdline_socket.c
new file mode 100644 (file)
index 0000000..78a8b31
--- /dev/null
@@ -0,0 +1,191 @@
+/*
+ * 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 <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <inttypes.h>
+#include <fcntl.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include "cmdline_parse.h"
+#include "cmdline_rdline.h"
+#include "cmdline.h"
+
+/**********************/
+
+int
+cmdline_tcpv4_listen(in_addr_t addr, uint16_t port)
+{
+       int s;
+       struct sockaddr_in sin_ci;
+       int optval = 1;
+
+       s = socket(PF_INET, SOCK_STREAM, 0);
+       if (s < 0) {
+               dprintf("socket() failed\n");
+               return s;
+       }
+
+       if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
+                      &optval, sizeof(optval)) == -1) {
+               dprintf("setsockopt() failed\n");
+               goto end;
+       }
+
+       memset(&sin_ci, 0, sizeof(sin_ci));
+       sin_ci.sin_family = AF_INET;
+       sin_ci.sin_addr.s_addr = addr;
+       sin_ci.sin_port = htons(port);
+#ifndef __linux__
+       sin_ci.sin_len = sizeof(sin_ci);
+#endif
+       if (bind(s, (struct sockaddr *)&sin_ci, sizeof(sin_ci)) < 0) {
+               dprintf("bind() failed\n");
+               goto end;
+       }
+
+       if (listen(s, 1) < 0) {
+               dprintf("listen() failed\n");
+               goto end;
+       }
+
+       return s;
+end:
+       close(s);
+       return -1;
+}
+
+int
+cmdline_tcpv6_listen(struct in6_addr addr6, uint16_t port)
+{
+       int s;
+       struct sockaddr_in6 sin6_ci;
+
+       s = socket(PF_INET6, SOCK_STREAM, 0);
+       if (s < 0) {
+               dprintf("socket() failed\n");
+               return s;
+       }
+
+       bzero(&sin6_ci, sizeof(sin6_ci));
+       sin6_ci.sin6_family = AF_INET6;
+#ifndef __linux__
+       sin6_ci.sin6_len = sizeof(sin6_ci);
+#endif
+       memcpy(&sin6_ci.sin6_addr, &addr6, sizeof(sin6_ci.sin6_addr));
+       sin6_ci.sin6_port = htons(port);
+       if (bind(s, (struct sockaddr *) &sin6_ci, sizeof(sin6_ci)) < 0) {
+               dprintf("bind() failed\n");
+               goto end;
+       }
+
+       if (listen(s, 1) < 0) {
+               dprintf("listen() failed\n");
+               goto end;
+       }
+
+       return s;
+end:
+       close(s);
+       return -1;
+}
+
+int
+cmdline_unix_listen(char *filename)
+{
+       int s;
+       struct sockaddr_un servAddr;
+
+       s = socket(AF_UNIX, SOCK_STREAM, 0);
+       if (s < 0) {
+               dprintf("socket() failed\n");
+               return s;
+       }
+
+       bzero(&servAddr, sizeof(servAddr));
+       servAddr.sun_family = AF_UNIX;
+       memcpy(servAddr.sun_path, filename , strlen(filename));
+       
+       unlink(filename);
+       if(bind(s, (struct sockaddr *) &servAddr, sizeof(servAddr)) < 0) {
+               dprintf("bind() failed\n");
+               goto end;
+       }
+
+       if (listen(s, 1) < 0) {
+               dprintf("listen() failed\n");
+               goto end;
+       }
+
+       return s;
+end:
+       close(s);
+       return -1;
+}
+
+struct cmdline *
+cmdline_stdin_new(cmdline_parse_ctx_t *ctx, const char *prompt)
+{
+       return (cmdline_new(ctx, prompt, 0, 1));
+}
+
+struct cmdline *
+cmdline_file_new(cmdline_parse_ctx_t *ctx, const char *prompt, const char *path)
+{
+       int fd;
+       fd = open(path, O_RDONLY, 0);
+       if (fd < 0) {
+               dprintf("open() failed\n");
+               return NULL;
+       }
+       return (cmdline_new(ctx, prompt, fd, -1));
+}
+
+struct cmdline *
+cmdline_accept(cmdline_parse_ctx_t *ctx, const char *prompt, int s)
+{
+       int s2;
+       struct sockaddr sin;
+       socklen_t sinlen;
+
+       sinlen = sizeof(struct sockaddr);
+
+       if ((s2 = accept(s, &sin, &sinlen)) < 0) {
+               dprintf("accept() failed\n");
+               return NULL;
+       }
+
+       return (cmdline_new(ctx, prompt, s2, s2));
+}
+
diff --git a/src/lib/cmdline_socket.h b/src/lib/cmdline_socket.h
new file mode 100644 (file)
index 0000000..4187d58
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+#ifndef _CMDLINE_SOCKET_H_
+#define _CMDLINE_SOCKET_H_
+
+int cmdline_tcpv4_listen(in_addr_t addr, uint16_t port);
+int cmdline_tcpv6_listen(uint16_t port);
+int cmdline_unix_listen(char *filename);
+struct cmdline *cmdline_stdin_new(cmdline_parse_ctx_t *ctx, const char *prompt);
+struct cmdline *cmdline_file_new(cmdline_parse_ctx_t *ctx, const char *prompt, const char *path);
+struct cmdline *cmdline_accept(cmdline_parse_ctx_t *ctx, const char *prompt, int s);
+
+#endif /* _CMDLINE_SOCKET_H_ */
diff --git a/src/lib/cmdline_vt100.c b/src/lib/cmdline_vt100.c
new file mode 100644 (file)
index 0000000..1245cd0
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * 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 <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <ctype.h>
+
+#include "cmdline_vt100.h"
+
+const char *cmdline_vt100_commands[] = {
+       vt100_up_arr,
+       vt100_down_arr,
+       vt100_right_arr,
+       vt100_left_arr,
+       "\177",
+       "\n",
+       "\001",
+       "\005",
+       "\013",
+       "\031",
+       "\003",
+       "\006",
+       "\002",
+       vt100_suppr,
+       vt100_tab,
+       "\004",
+       "\014",
+       "\r",
+       "\033\177",
+       vt100_word_left,
+       vt100_word_right,
+       "?",
+};
+
+void
+vt100_init(struct cmdline_vt100 *vt)
+{
+       vt->state = CMDLINE_VT100_INIT;
+}
+
+
+static int
+match_command(char *buf, unsigned int size)
+{
+       const char *cmd;
+       unsigned int i = 0;
+       
+       for (i=0 ; i<sizeof(cmdline_vt100_commands)/sizeof(const char *) ; i++) {
+               cmd = *(cmdline_vt100_commands + i);
+
+               if (size == strlen(cmd) &&
+                   !strncmp(buf, cmd, strlen(cmd))) {
+                       return i;
+               }
+       }
+
+       return -1;
+}
+
+int
+vt100_parser(struct cmdline_vt100 *vt, char ch)
+{
+       unsigned int size;
+       uint8_t c = (uint8_t) ch;
+
+       if (vt->bufpos > CMDLINE_VT100_BUF_SIZE) {
+               vt->state = CMDLINE_VT100_INIT;
+               vt->bufpos = 0;
+       }
+
+       vt->buf[vt->bufpos++] = c;
+       size = vt->bufpos;
+
+       switch (vt->state) {
+       case CMDLINE_VT100_INIT:
+               if (c == 033) {
+                       vt->state = CMDLINE_VT100_ESCAPE;
+               }
+               else {
+                       vt->bufpos = 0;
+                       goto match_command;
+               }
+               break;
+
+       case CMDLINE_VT100_ESCAPE:
+               if (c == 0133) {
+                       vt->state = CMDLINE_VT100_ESCAPE_CSI;
+               }
+               else if (c >= 060 && c <= 0177) { /* XXX 0177 ? */
+                       vt->bufpos = 0;
+                       vt->state = CMDLINE_VT100_INIT;
+                       goto match_command;
+               }
+               break;
+
+       case CMDLINE_VT100_ESCAPE_CSI:
+               if (c >= 0100 && c <= 0176) {
+                       vt->bufpos = 0;
+                       vt->state = CMDLINE_VT100_INIT;
+                       goto match_command;
+               }
+               break;
+               
+       default:
+               vt->bufpos = 0;
+               break;
+       }
+
+       return -2;
+       
+ match_command:
+       return match_command(vt->buf, size);
+}
diff --git a/src/lib/cmdline_vt100.h b/src/lib/cmdline_vt100.h
new file mode 100644 (file)
index 0000000..968d70e
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * 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.
+ */
+
+#ifndef _CMDLINE_VT100_H_
+#define _CMDLINE_VT100_H_
+
+#define vt100_bell         "\007"
+#define vt100_bs           "\010"
+#define vt100_bs_clear     "\010 \010"
+#define vt100_tab          "\011"
+#define vt100_crnl         "\012\015"
+#define vt100_clear_right  "\033[0K"
+#define vt100_clear_left   "\033[1K"
+#define vt100_clear_down   "\033[0J"
+#define vt100_clear_up     "\033[1J"
+#define vt100_clear_line   "\033[2K"
+#define vt100_clear_screen "\033[2J"
+#define vt100_up_arr       "\033\133\101"
+#define vt100_down_arr     "\033\133\102"
+#define vt100_right_arr    "\033\133\103"
+#define vt100_left_arr     "\033\133\104"
+#define vt100_multi_right  "\033\133%uC"
+#define vt100_multi_left   "\033\133%uD"
+#define vt100_suppr        "\033\133\063\176"
+#define vt100_home         "\033M\033E"
+#define vt100_word_left    "\033\142"
+#define vt100_word_right   "\033\146"
+
+
+/* Result of parsing : it must be synchronized with
+ * cmdline_vt100_commands[] in vt100.c */
+#define CMDLINE_KEY_UP_ARR 0
+#define CMDLINE_KEY_DOWN_ARR 1
+#define CMDLINE_KEY_RIGHT_ARR 2
+#define CMDLINE_KEY_LEFT_ARR 3
+#define CMDLINE_KEY_BKSPACE 4
+#define CMDLINE_KEY_RETURN 5
+#define CMDLINE_KEY_CTRL_A 6
+#define CMDLINE_KEY_CTRL_E 7
+#define CMDLINE_KEY_CTRL_K 8
+#define CMDLINE_KEY_CTRL_Y 9
+#define CMDLINE_KEY_CTRL_C 10
+#define CMDLINE_KEY_CTRL_F 11
+#define CMDLINE_KEY_CTRL_B 12
+#define CMDLINE_KEY_SUPPR 13
+#define CMDLINE_KEY_TAB 14
+#define CMDLINE_KEY_CTRL_D 15
+#define CMDLINE_KEY_CTRL_L 16
+#define CMDLINE_KEY_RETURN2 17
+#define CMDLINE_KEY_META_BKSPACE 18
+#define CMDLINE_KEY_WLEFT 19
+#define CMDLINE_KEY_WRIGHT 20
+#define CMDLINE_KEY_HELP 21
+
+extern const char *cmdline_vt100_commands[];
+
+enum cmdline_vt100_parser_state {
+       CMDLINE_VT100_INIT,
+       CMDLINE_VT100_ESCAPE,
+       CMDLINE_VT100_ESCAPE_CSI,
+};
+
+#define CMDLINE_VT100_BUF_SIZE 8
+struct cmdline_vt100 {
+       uint8_t bufpos;
+       char buf[CMDLINE_VT100_BUF_SIZE];
+       enum cmdline_vt100_parser_state state;
+};
+
+/**
+ * Init
+ */
+void vt100_init(struct cmdline_vt100 *vt);
+
+/**
+ * Input a new character. 
+ * Return -1 if the character is not part of a control sequence
+ * Return -2 if c is not the last char of a control sequence
+ * Else return the index in vt100_commands[]
+ */
+int vt100_parser(struct cmdline_vt100 *vt, char c);
+
+#endif