From: Olivier Matz Date: Fri, 24 Dec 2010 11:35:58 +0000 (+0100) Subject: Initial import from http://www.droids-corp.org/hg/libcmdline/rev/db316e4289a1 X-Git-Url: http://git.droids-corp.org/?p=libcmdline.git;a=commitdiff_plain;h=6f32a68393e01b4179592b9f48255179e8ad55f7 Initial import from droids-corp.org/hg/libcmdline/rev/db316e4289a1 Signed-off-by: Olivier Matz --- 6f32a68393e01b4179592b9f48255179e8ad55f7 diff --git a/Kconfig b/Kconfig new file mode 100644 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 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 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 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 index 0000000..523a8a8 --- /dev/null +++ b/build/calculator_server/Makefile @@ -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 index 0000000..523a8a8 --- /dev/null +++ b/build/calculator_standalone/Makefile @@ -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 index 0000000..57a440f --- /dev/null +++ b/build/client/Makefile @@ -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 index 0000000..6839965 --- /dev/null +++ b/build/extension_example/Makefile @@ -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 index 0000000..3e98788 --- /dev/null +++ b/build/genconf/Makefile @@ -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 index 0000000..882b67c --- /dev/null +++ b/build/lib/Makefile @@ -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 index 0000000..be00d5a --- /dev/null +++ b/src/calculator_server/commands.c @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2009, Olivier MATZ + * 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 +#include +#include +#include +#include + +#include +#include +#include +#include + + + +/**********************************************************/ +/* 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 index 0000000..77fbe28 --- /dev/null +++ b/src/calculator_server/main.c @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2009, Olivier MATZ + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +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 index 0000000..be00d5a --- /dev/null +++ b/src/calculator_standalone/commands.c @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2009, Olivier MATZ + * 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 +#include +#include +#include +#include + +#include +#include +#include +#include + + + +/**********************************************************/ +/* 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 index 0000000..de5a752 --- /dev/null +++ b/src/calculator_standalone/main.c @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2009, Olivier MATZ + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/* 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 index 0000000..5f177d2 --- /dev/null +++ b/src/client/main.c @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2009, Olivier MATZ + * 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 +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + + +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 index 0000000..fbef8c9 --- /dev/null +++ b/src/extension_example/commands.c @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2009, Olivier MATZ + * 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#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 index 0000000..9cf1371 --- /dev/null +++ b/src/extension_example/main.c @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2009, Olivier MATZ + * 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 +#include +#include +#include +#include + +#include + +#include +#include + +struct my_cmdline { + struct rdline rdl; + char prompt[RDLINE_PROMPT_SIZE]; +} cmd; + +extern cmdline_parse_ctx_t main_ctx[]; + +#include +#include +#include +#include + +#include +#include + +#include +#include + +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 index 0000000..0781987 --- /dev/null +++ b/src/extension_example/parse_obj_list.c @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2009, Olivier MATZ + * 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 +#include +#include +#include +#include + +#include +#include + +#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 index 0000000..1aeb7eb --- /dev/null +++ b/src/extension_example/parse_obj_list.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2009, Olivier MATZ + * 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 +#include + +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 index 0000000..7978e70 --- /dev/null +++ b/src/genconf/commands.c @@ -0,0 +1,686 @@ +/* + * Copyright (c) 2009, Olivier MATZ + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#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 = " 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 = " 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 = " 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 = " 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 = " 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 = " 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 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 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 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 = " 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 = " 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 = " 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 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 = " 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 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 index 0000000..1a34022 --- /dev/null +++ b/src/genconf/conf_htable.c @@ -0,0 +1,73 @@ +#include +#include +#include +#include +#include + +#include "expression.h" +#include "conf_parser.h" +#include "confnode.h" + +#define HASHTABLE_ORDER 10 +#define HASHTABLE_SIZE (1<> 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 + * 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 +#include +#include +#include +#include +#include + +#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, ¤t)) { + /* 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, ¤t); +#else + parse_conf_file("/Users/zer0/projects/libcmdline/Kconfig", + &parent, ¤t); +#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 index 0000000..3051421 --- /dev/null +++ b/src/genconf/conf_parser.h @@ -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 index 0000000..4882c84 --- /dev/null +++ b/src/genconf/confnode.c @@ -0,0 +1,502 @@ +/* + * Copyright (c) 2010, Olivier MATZ + * 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 +#include +#include +#include + +#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 index 0000000..13de83e --- /dev/null +++ b/src/genconf/confnode.h @@ -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 index 0000000..5a34e7d --- /dev/null +++ b/src/genconf/confnode_choice.c @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2010, Olivier MATZ + * 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 +#include +#include +#include +#include +#include + +#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 index 0000000..7d70d27 --- /dev/null +++ b/src/genconf/confnode_choiceconfig.c @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2010, Olivier MATZ + * 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 +#include +#include +#include +#include +#include + +#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 index 0000000..36607cc --- /dev/null +++ b/src/genconf/confnode_comment.c @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2010, Olivier MATZ + * 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 +#include +#include +#include +#include +#include + +#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 index 0000000..7817ebf --- /dev/null +++ b/src/genconf/confnode_config.c @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2010, Olivier MATZ + * 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 +#include +#include +#include +#include +#include + +#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 index 0000000..f9e5f91 --- /dev/null +++ b/src/genconf/confnode_if.c @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2010, Olivier MATZ + * 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 +#include +#include +#include +#include +#include + +#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 index 0000000..9c31664 --- /dev/null +++ b/src/genconf/confnode_intconfig.c @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2010, Olivier MATZ + * 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 +#include +#include +#include +#include +#include + +#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 index 0000000..4eafa4b --- /dev/null +++ b/src/genconf/confnode_menu.c @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2010, Olivier MATZ + * 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 +#include +#include +#include +#include +#include + +#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 index 0000000..15a5d51 --- /dev/null +++ b/src/genconf/confnode_menuconfig.c @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2010, Olivier MATZ + * 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 +#include +#include +#include +#include +#include + +#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 index 0000000..6d3fbe8 --- /dev/null +++ b/src/genconf/confnode_root.c @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2010, Olivier MATZ + * 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 +#include +#include +#include +#include +#include + +#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 index 0000000..c6dbcd2 --- /dev/null +++ b/src/genconf/confnode_strconfig.c @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2010, Olivier MATZ + * 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 +#include +#include +#include +#include +#include + +#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 index 0000000..b6c496e --- /dev/null +++ b/src/genconf/dotconfig.c @@ -0,0 +1,142 @@ +#include +#include +#include +#include +#include +#include + +#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 index 0000000..8975f59 --- /dev/null +++ b/src/genconf/dotconfig.h @@ -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 index 0000000..91a3a1b --- /dev/null +++ b/src/genconf/expression.c @@ -0,0 +1,397 @@ +#include +#include +#include +#include +#include + +#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 index 0000000..0b8ea50 --- /dev/null +++ b/src/genconf/expression.h @@ -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 index 0000000..3748710 --- /dev/null +++ b/src/genconf/main.c @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2009, Olivier MATZ + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#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 index 0000000..b4da9dd --- /dev/null +++ b/src/genconf/parse_confnode.c @@ -0,0 +1,220 @@ +/* + * Copyright (c) 2009, Olivier MATZ + * 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 +#include +#include +#include +#include +#include + +#include +#include + +#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 index 0000000..e47f679 --- /dev/null +++ b/src/genconf/parse_confnode.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2009, Olivier MATZ + * 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 index 0000000..0afc552 --- /dev/null +++ b/src/genconf/parser_common.c @@ -0,0 +1,53 @@ +#include + +/* + * 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 index 0000000..80b7857 --- /dev/null +++ b/src/genconf/parser_common.h @@ -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 index 0000000..a296c5f --- /dev/null +++ b/src/genconf/strictmalloc.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2010, Olivier MATZ + * 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 index 0000000..f21ba39 --- /dev/null +++ b/src/genconf/todo.txt @@ -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 index 0000000..621ce37 --- /dev/null +++ b/src/lib/cmdline.c @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2009, Olivier MATZ + * 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#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; irdl, 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 index 0000000..d90205a --- /dev/null +++ b/src/lib/cmdline.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2009, Olivier MATZ + * 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 index 0000000..8cd5374 --- /dev/null +++ b/src/lib/cmdline_cirbuf.c @@ -0,0 +1,381 @@ +/* + * Copyright (c) 2009, Olivier MATZ + * 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 +#include + +#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 ; ilen ; 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 ; ilen ; 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 index 0000000..c68e248 --- /dev/null +++ b/src/lib/cmdline_cirbuf.h @@ -0,0 +1,207 @@ +/* + * Copyright (c) 2009, Olivier MATZ + * 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 + +/** + * 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 index 0000000..85958d0 --- /dev/null +++ b/src/lib/cmdline_parse.c @@ -0,0 +1,454 @@ +/* + * Copyright (c) 2009, Olivier MATZ + * 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 +#include +#include +#include + +#include + +#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 || iparse(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 ; icomplete_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 ; icomplete_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 index 0000000..6b7fca9 --- /dev/null +++ b/src/lib/cmdline_parse.h @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2009, Olivier MATZ + * 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 + +#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 index 0000000..69df142 --- /dev/null +++ b/src/lib/cmdline_parse_etheraddr.c @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2009, Olivier MATZ + * 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 +#include +#include +#include +#include +#include +#include +#include +#ifdef __linux__ +#include +#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(ðeraddr, 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 index 0000000..84b14fa --- /dev/null +++ b/src/lib/cmdline_parse_etheraddr.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2009, Olivier MATZ + * 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 index 0000000..96b384d --- /dev/null +++ b/src/lib/cmdline_parse_ipaddr.c @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2009, Olivier MATZ + * 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 +#include +#include +#include +#include +#include +#include +#include + +#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 index 0000000..68576ab --- /dev/null +++ b/src/lib/cmdline_parse_ipaddr.h @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2009, Olivier MATZ + * 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 index 0000000..6a37a76 --- /dev/null +++ b/src/lib/cmdline_parse_num.c @@ -0,0 +1,455 @@ +/* + * Copyright (c) 2009, Olivier MATZ + * 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 +#include +#include +#include + +#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 index 0000000..b149c92 --- /dev/null +++ b/src/lib/cmdline_parse_num.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2009, Olivier MATZ + * 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 index 0000000..eb4553b --- /dev/null +++ b/src/lib/cmdline_parse_string.c @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2009, Olivier MATZ + * 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 +#include +#include +#include + +#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 index 0000000..de34ba6 --- /dev/null +++ b/src/lib/cmdline_parse_string.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2009, Olivier MATZ + * 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 index 0000000..287c7d8 --- /dev/null +++ b/src/lib/cmdline_rdline.c @@ -0,0 +1,600 @@ +/* + * Copyright (c) 2009, Olivier MATZ + * 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 +#include +#include +#include +#include +#include + +#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 ; iprompt_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 ; iprompt_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= 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 index 0000000..f8437a5 --- /dev/null +++ b/src/lib/cmdline_rdline.h @@ -0,0 +1,217 @@ +/* + * Copyright (c) 2009, Olivier MATZ + * 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 +#include + +#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 index 0000000..78a8b31 --- /dev/null +++ b/src/lib/cmdline_socket.c @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2009, Olivier MATZ + * 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#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 index 0000000..4187d58 --- /dev/null +++ b/src/lib/cmdline_socket.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2009, Olivier MATZ + * 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 index 0000000..1245cd0 --- /dev/null +++ b/src/lib/cmdline_vt100.c @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2009, Olivier MATZ + * 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 +#include +#include +#include +#include +#include + +#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 ; ibufpos > 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 index 0000000..968d70e --- /dev/null +++ b/src/lib/cmdline_vt100.h @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2009, Olivier MATZ + * 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