File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / confuse / examples / cli.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Mar 17 00:49:17 2021 UTC (3 years, 3 months ago) by misho
Branches: confuse, MAIN
CVS tags: v3_3, HEAD
confuse 3.3

    1: /*
    2:  * Copyright (c) 2015 Peter Rosin <peda@lysator.liu.se>
    3:  *
    4:  * Permission to use, copy, modify, and/or distribute this software for any
    5:  * purpose with or without fee is hereby granted, provided that the above
    6:  * copyright notice and this permission notice appear in all copies.
    7:  *
    8:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
    9:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
   10:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
   11:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
   12:  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
   13:  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
   14:  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
   15:  */
   16: 
   17: #include <string.h>
   18: #include <stdlib.h>
   19: #include "confuse.h"
   20: 
   21: static cfg_t *cfg;
   22: static int cmdc;
   23: static char **cmdv;
   24: static int cmdv_max;
   25: 
   26: static int reserve_cmdv(int cmdc)
   27: {
   28: 	int cmd_max = cmdv_max;
   29: 	char **tmp;
   30: 
   31: 	if (cmdc < cmd_max)
   32: 		return 0;
   33: 
   34: 	do
   35: 		cmd_max += 10;
   36: 	while (cmdc >= cmd_max);
   37: 
   38: 	tmp = realloc(cmdv, cmd_max * sizeof(*tmp));
   39: 	if (!tmp)
   40: 		return -1;
   41: 
   42: 	cmdv = tmp;
   43: 	cmdv_max = cmd_max;
   44: 	return 0;
   45: }
   46: 
   47: static int split_cmd(char *cmd)
   48: {
   49: 	char *out = cmd;
   50: 	int arg;
   51: 
   52: 	cmdc = 0;
   53: 
   54: 	for (;;) {
   55: 		char ch;
   56: 
   57: 		arg = 0;
   58: 		while (*cmd == ' ')
   59: 			++cmd;
   60:  next_char:
   61: 		ch = *cmd++;
   62: 		if (ch == '"' || ch == '\'') {
   63: 			char end = ch;
   64: 
   65: 			if (!arg) {
   66: 				if (reserve_cmdv(cmdc + 1))
   67: 					return -1;
   68: 				cmdv[cmdc++] = out;
   69: 				arg = 1;
   70: 			}
   71: 			while (*cmd && *cmd != end)
   72: 				*out++ = *cmd++;
   73: 			if (!*cmd++)
   74: 				return -1;
   75: 			goto next_char;
   76: 		}
   77: 		if (ch && ch != ' ') {
   78: 			if (!arg) {
   79: 				if (reserve_cmdv(cmdc + 1))
   80: 					return -1;
   81: 				cmdv[cmdc++] = out;
   82: 				arg = 1;
   83: 			}
   84: 			*out++ = ch;
   85: 			goto next_char;
   86: 		}
   87: 		*out++ = 0;
   88: 		if (!ch)
   89: 			break;
   90: 	}
   91: 	return cmdc;
   92: }
   93: 
   94: static char *input_cmd(void)
   95: {
   96: 	int ch;
   97: 	char *cmd = malloc(128);
   98: 	int len = 128;
   99: 	int pos = 0;
  100: 
  101: 	if (!cmd)
  102: 		return NULL;
  103: 
  104: 	for (;;) {
  105: 		ch = fgetc(stdin);
  106: 		if (ch < 0)
  107: 			break;
  108: 		switch (ch) {
  109: 		case '\r':
  110: 		case '\n':
  111: 			ch = 0;
  112: 			/* fall through */
  113: 		default:
  114: 			if (pos == len) {
  115: 				char *tmp = realloc(cmd, len * 2);
  116: 
  117: 				if (!tmp)
  118: 					goto cleanup;
  119: 				cmd = tmp;
  120: 				len *= 2;
  121: 			}
  122: 			cmd[pos++] = ch;
  123: 			if (!ch)
  124: 				return cmd;
  125: 		}
  126: 	}
  127:  cleanup:
  128: 	free(cmd);
  129: 	return NULL;
  130: }
  131: 
  132: static const char *help(int argc, char *argv[])
  133: {
  134: 	return  "Available commands:\n"
  135: 		"\n"
  136: 		"help\n"
  137: 		"set <option> <value> ...\n"
  138: 		"subset <section> <option> <value>\n"
  139: 		"create <section>\n"
  140: 		"destroy <section>\n"
  141: 		"dump [mod|modified]\n"
  142: 		"quit\n"
  143: 		"\n"
  144: 		"<option> is one of 'bool', 'int' 'string' and 'float'.\n";
  145: }
  146: 
  147: static const char *set(int argc, char *argv[])
  148: {
  149: 	if (argc < 3)
  150: 		return "Need more args\n";
  151: 
  152: 	if (!cfg_getopt(cfg, argv[1]))
  153: 		return "Unknown option\n";
  154: 
  155: 	if (cfg_setmulti(cfg, argv[1], argc - 2, &argv[2]))
  156: 		return "Failure\n";
  157: 
  158: 	return "OK\n";
  159: }
  160: 
  161: static const char *subset(int argc, char *argv[])
  162: {
  163: 	cfg_t *sub;
  164: 
  165: 	if (argc < 4)
  166: 		return "Need more args\n";
  167: 	if (argc > 4)
  168: 		return "Too many args\n";
  169: 
  170: 	sub = cfg_gettsec(cfg, "sub", argv[1]);
  171: 	if (!sub)
  172: 		return "No such section\n";
  173: 
  174: 	if (!cfg_getopt(sub, argv[2]))
  175: 		return "Unknown option\n";
  176: 
  177: 	if (cfg_setmulti(sub, argv[2], argc - 3, &argv[3]))
  178: 		return "Failure\n";
  179: 
  180: 	return "OK\n";
  181: }
  182: 
  183: static const char *create(int argc, char *argv[])
  184: {
  185: 	cfg_opt_t *opt;
  186: 
  187: 	if (argc != 2)
  188: 		return "Need one arg\n";
  189: 
  190: 	if (cfg_gettsec(cfg, "sub", argv[1]))
  191: 		return "Section exists already\n";
  192: 
  193: 	opt = cfg_getopt(cfg, "sub");
  194: 	if (!opt || !cfg_setopt(cfg, opt, argv[1]))
  195: 		return "Failure\n";
  196: 
  197: 	return "OK\n";
  198: }
  199: 
  200: static const char *destroy(int argc, char *argv[])
  201: {
  202: 	if (argc < 2)
  203: 		return "Need one arg\n";
  204: 
  205: 	if (!cfg_gettsec(cfg, "sub", argv[1]))
  206: 		return "No such section\n";
  207: 
  208: 	cfg_rmtsec(cfg, "sub", argv[1]);
  209: 	return "OK\n";
  210: }
  211: 
  212: static int print_modified(cfg_t *cfg, cfg_opt_t *opt)
  213: {
  214: 	cfg_t *sec;
  215: 	int i;
  216: 
  217: 	if (opt->type != CFGT_SEC)
  218: 		return !(opt->flags & CFGF_MODIFIED);
  219: 
  220: 	if (opt->flags & CFGF_MULTI)
  221: 		return 0;
  222: 
  223: 	/*
  224: 	 * This cli example does not have any non-multi section
  225: 	 * options, but for completeness, this is a sane way to
  226: 	 * handle "static" sections. I.e. filter them out unless
  227: 	 * they have at least one sub-option that is not filtered
  228: 	 * out.
  229: 	 *
  230: 	 * A generic solution would have to examine if the section
  231: 	 * has its own print filter function and use that, but that
  232: 	 * feels out of scope and is left as an exercise for the
  233: 	 * reader. If it is needed at some point, a new supporting
  234: 	 * API should probably be added to help get it done without
  235: 	 * digging in the library guts...
  236: 	 */
  237: 	sec = cfg_opt_getnsec(opt, 0);
  238: 	if (!sec)
  239: 		return 1;
  240: 
  241: 	for (i = 0; sec->opts[i].name; i++) {
  242: 		if (!print_modified(sec, &sec->opts[i]))
  243: 			return 0;
  244: 	}
  245: 	return 1;
  246: }
  247: 
  248: static const char *dump(int argc, char *argv[])
  249: {
  250: 	if (argc > 2)
  251: 		return "Too many args\n";
  252: 	if (argc == 2) {
  253: 		if (!strcmp(argv[1], "mod") || !strcmp(argv[1], "modified"))
  254: 			cfg_set_print_filter_func(cfg, print_modified);
  255: 		else
  256: 			return "Invalid arg\n";
  257: 	}
  258: 	else
  259: 		cfg_set_print_filter_func(cfg, NULL);
  260: 
  261: 	cfg_print(cfg, stdout);
  262: 	return "";
  263: }
  264: 
  265: static const char *quit(int argc, char *argv[])
  266: {
  267: 	return NULL;
  268: }
  269: 
  270: struct cmd_handler {
  271: 	const char *cmd;
  272: 	const char *(*handler)(int argc, char *argv[]);
  273: };
  274: 
  275: static const struct cmd_handler cmds[] = {
  276: 	{ "help", help },
  277: 	{ "set", set },
  278: 	{ "subset", subset },
  279: 	{ "create", create },
  280: 	{ "destroy", destroy },
  281: 	{ "dump", dump },
  282: 	{ "quit", quit },
  283: 	{ "exit", quit },
  284: 	{ NULL, NULL }
  285: };
  286: 
  287: int main(void)
  288: {
  289: 	cfg_opt_t sub_opts[] = {
  290: 		CFG_BOOL("bool", cfg_false, CFGF_NONE),
  291: 		CFG_STR("string", NULL, CFGF_NONE),
  292: 		CFG_INT("int", 0, CFGF_NONE),
  293: 		CFG_FLOAT("float", 0.0, CFGF_NONE),
  294: 		CFG_END()
  295: 	};
  296: 
  297: 	cfg_opt_t opts[] = {
  298: 		CFG_BOOL_LIST("bool", cfg_false, CFGF_NONE),
  299: 		CFG_STR_LIST("string", NULL, CFGF_NONE),
  300: 		CFG_INT_LIST("int", 0, CFGF_NONE),
  301: 		CFG_FLOAT_LIST("float", "0.0", CFGF_NONE),
  302: 		CFG_SEC("sub", sub_opts,
  303: 			CFGF_MULTI | CFGF_TITLE | CFGF_NO_TITLE_DUPES),
  304: 		CFG_END()
  305: 	};
  306: 
  307: 	char *cmd = NULL;
  308: 	const char *reply;
  309: 	int res;
  310: 	int i;
  311: 
  312: 	cfg = cfg_init(opts, CFGF_NONE);
  313: 
  314: 	for (;;) {
  315: 		printf("cli> ");
  316: 		fflush(stdout);
  317: 
  318: 		if (cmd)
  319: 			free(cmd);
  320: 		cmd = input_cmd();
  321: 		if (!cmd)
  322: 			exit(0);
  323: 		res = split_cmd(cmd);
  324: 		if (res < 0) {
  325: 			printf("Parse error\n");
  326: 			continue;
  327: 		}
  328: 		if (cmdc == 0)
  329: 			continue;
  330: 		for (i = 0; cmds[i].cmd; ++i) {
  331: 			if (strcmp(cmdv[0], cmds[i].cmd))
  332: 				continue;
  333: 			reply = cmds[i].handler(cmdc, cmdv);
  334: 			if (!reply)
  335: 				exit(0);
  336: 			printf("%s", reply);
  337: 			break;
  338: 		}
  339: 		if (!cmds[i].cmd)
  340: 			printf("Unknown command\n");
  341: 	}
  342: 
  343: 	cfg_free(cfg);
  344: 	return 0;
  345: }
  346: 
  347: /**
  348:  * Local Variables:
  349:  *  indent-tabs-mode: t
  350:  *  c-file-style: "linux"
  351:  * End:
  352:  */

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>