Annotation of embedaddon/confuse/examples/cli.c, revision 1.1.1.1
1.1 misho 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>