957bbeffd41287e1f0c5def512b6d7e41e8a2ed4
[libcmdline.git] / src / genconf / commands.c
1 /*
2  * Copyright (c) 2009, Olivier MATZ <zer0@droids-corp.org>
3  * All rights reserved.
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *
7  *     * Redistributions of source code must retain the above copyright
8  *       notice, this list of conditions and the following disclaimer.
9  *     * Redistributions in binary form must reproduce the above copyright
10  *       notice, this list of conditions and the following disclaimer in the
11  *       documentation and/or other materials provided with the distribution.
12  *     * Neither the name of the University of California, Berkeley nor the
13  *       names of its contributors may be used to endorse or promote products
14  *       derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
17  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19  * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
20  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27
28 #include <math.h>
29 #include <stdio.h>
30 #include <inttypes.h>
31 #include <stdint.h>
32 #include <string.h>
33 #include <stdlib.h>
34 #include <stdarg.h>
35 #include <unistd.h>
36 #include <netinet/in.h>
37 #include <arpa/inet.h>
38 #include <sys/queue.h>
39
40 #include <cmdline_parse.h>
41 #include <cmdline_parse_ipaddr.h>
42 #include <cmdline_parse_num.h>
43 #include <cmdline_parse_string.h>
44 #include <cmdline_rdline.h>
45 #include <cmdline.h>
46
47 #include "parse_confnode.h"
48 #include "expression.h"
49 #include "conf_parser.h"
50 #include "confnode.h"
51 #include "conf_htable.h"
52 #include "dotconfig.h"
53
54 extern struct confnode *conf_cur;
55 extern struct confnode *conf_root;
56
57 /**********************************************************/
58 /* ls */
59 /**********************************************************/
60
61 struct cmd_ls_result {
62         cmdline_fixed_string_t ls;
63 };
64
65 static void do_ls(const struct confnode *n)
66 {
67         const struct confnode *c;
68
69         TAILQ_FOREACH(c, &n->children, next) {
70                 if (c->flags & CONFNODE_F_INVISIBLE)
71                         do_ls(c);
72                 else
73                         confnode_display_short(c);
74         }
75 }
76
77 static void cmd_ls_parsed(void *parsed_result,
78                           struct cmdline *cl,
79                           void *data)
80 {
81         do_ls(conf_cur);
82 }
83
84 cmdline_parse_token_string_t cmd_ls_ls =
85         TOKEN_STRING_INITIALIZER(struct cmd_ls_result, ls, "ls");
86
87 cmdline_parse_inst_t cmd_ls = {
88         .f = cmd_ls_parsed,  /* function to call */
89         .data = NULL,      /* 2nd arg of func */
90         .help_str = "<ls> list all config options in current directory",
91         .tokens = {        /* token list, NULL terminated */
92                 (void *)&cmd_ls_ls,
93                 NULL,
94         },
95 };
96
97 /**********************************************************/
98 /* ls node */
99 /**********************************************************/
100
101 struct cmd_ls_node_result {
102         cmdline_fixed_string_t ls;
103         struct confnode *node;
104 };
105
106 static void cmd_ls_node_parsed(void *parsed_result,
107                                 struct cmdline *cl,
108                                 void *data)
109 {
110         struct cmd_ls_node_result *res = parsed_result;
111         struct confnode *n = res->node;
112
113         while (n) {
114                 confnode_display_short(n);
115                 n = TAILQ_NEXT(n, user_next);
116         }
117 }
118
119 cmdline_parse_token_string_t cmd_ls_node_ls =
120         TOKEN_STRING_INITIALIZER(struct cmd_ls_node_result, ls, "ls");
121 parse_token_conf_node_t cmd_ls_node_node =
122         TOKEN_CONF_NODE_INITIALIZER(struct cmd_ls_node_result, node,
123                                     &conf_root, &conf_cur, 0, CONFNODE_F_INVISIBLE);
124
125 cmdline_parse_inst_t cmd_ls_node = {
126         .f = cmd_ls_node_parsed,  /* function to call */
127         .data = NULL,      /* 2nd arg of func */
128         .help_str = "<ls NODE> list config node(s) given in argument",
129         .tokens = {        /* token list, NULL terminated */
130                 (void *)&cmd_ls_node_ls,
131                 (void *)&cmd_ls_node_node,
132                 NULL,
133         },
134 };
135
136 /**********************************************************/
137 /* pwd */
138 /**********************************************************/
139
140 struct cmd_pwd_result {
141         cmdline_fixed_string_t pwd;
142 };
143
144 static void cmd_pwd_parsed(void *parsed_result,
145                                 struct cmdline *cl,
146                                 void *data)
147 {
148         conf_display_path(conf_cur);
149 }
150
151 cmdline_parse_token_string_t cmd_pwd_pwd =
152         TOKEN_STRING_INITIALIZER(struct cmd_pwd_result, pwd, "pwd");
153
154 cmdline_parse_inst_t cmd_pwd = {
155         .f = cmd_pwd_parsed,  /* function to call */
156         .data = NULL,      /* 2nd arg of func */
157         .help_str = "<pwd> display current directory",
158         .tokens = {        /* token list, NULL terminated */
159                 (void *)&cmd_pwd_pwd,
160                 NULL,
161         },
162 };
163
164 /**********************************************************/
165 /* cd prev */
166 /**********************************************************/
167
168 struct cmd_cd_prev_result {
169         cmdline_fixed_string_t cd;
170         cmdline_fixed_string_t prev;
171 };
172
173 static void cmd_cd_prev_parsed(void *parsed_result,
174                                struct cmdline *cl,
175                                void *data)
176 {
177         struct confnode *parent = conf_cur->parent;
178         char prompt[RDLINE_PROMPT_SIZE];
179
180         if (parent == NULL) {
181                 printf("already at top level\n");
182                 return;
183         }
184         while ((parent->flags & CONFNODE_F_INVISIBLE) && parent->parent != NULL)
185                 parent = parent->parent;
186
187         conf_cur = parent;
188         snprintf(prompt, sizeof(prompt), "%s> ", conf_cur->name);
189         cmdline_set_prompt(cl, prompt);
190 }
191
192 cmdline_parse_token_string_t cmd_cd_prev_cd =
193         TOKEN_STRING_INITIALIZER(struct cmd_cd_prev_result, cd, "cd");
194 cmdline_parse_token_string_t cmd_cd_prev_prev =
195         TOKEN_STRING_INITIALIZER(struct cmd_cd_prev_result, prev, "..");
196
197 cmdline_parse_inst_t cmd_cd_prev = {
198         .f = cmd_cd_prev_parsed,  /* function to call */
199         .data = NULL,      /* 2nd arg of func */
200         .help_str = "<cd ..> go up in configuration tree",
201         .tokens = {        /* token list, NULL terminated */
202                 (void *)&cmd_cd_prev_cd,
203                 (void *)&cmd_cd_prev_prev,
204                 NULL,
205         },
206 };
207
208 /**********************************************************/
209 /* cd root */
210 /**********************************************************/
211
212 struct cmd_cd_root_result {
213         cmdline_fixed_string_t cd;
214         cmdline_fixed_string_t root;
215 };
216
217 static void cmd_cd_root_parsed(void *parsed_result,
218                                struct cmdline *cl,
219                                void *data)
220 {
221         char prompt[RDLINE_PROMPT_SIZE];
222
223         conf_cur = conf_root;
224         snprintf(prompt, sizeof(prompt), "%s> ", conf_cur->name);
225         cmdline_set_prompt(cl, prompt);
226 }
227
228 cmdline_parse_token_string_t cmd_cd_root_cd =
229         TOKEN_STRING_INITIALIZER(struct cmd_cd_root_result, cd, "cd");
230 cmdline_parse_token_string_t cmd_cd_root_root =
231         TOKEN_STRING_INITIALIZER(struct cmd_cd_root_result, root, "/");
232
233 cmdline_parse_inst_t cmd_cd_root = {
234         .f = cmd_cd_root_parsed,  /* function to call */
235         .data = NULL,      /* 2nd arg of func */
236         .help_str = "<cd /> go to root of configuration",
237         .tokens = {        /* token list, NULL terminated */
238                 (void *)&cmd_cd_root_cd,
239                 (void *)&cmd_cd_root_root,
240                 NULL,
241         },
242 };
243
244 /**********************************************************/
245 /* cd node */
246 /**********************************************************/
247
248 struct cmd_cd_node_result {
249         cmdline_fixed_string_t cd;
250         struct confnode *node;
251 };
252
253 static void cmd_cd_node_parsed(void *parsed_result,
254                                struct cmdline *cl,
255                                void *data)
256 {
257         struct cmd_cd_node_result *res = parsed_result;
258         char prompt[RDLINE_PROMPT_SIZE];
259
260         if (TAILQ_NEXT(res->node, user_next)) {
261                 printf("ambiguous directory\n");
262                 return;
263         }
264         conf_cur = res->node;
265         snprintf(prompt, sizeof(prompt), "%s> ", conf_cur->name);
266         cmdline_set_prompt(cl, prompt);
267 }
268
269 cmdline_parse_token_string_t cmd_cd_node_cd =
270         TOKEN_STRING_INITIALIZER(struct cmd_cd_node_result, cd, "cd");
271 parse_token_conf_node_t cmd_cd_node_node =
272         TOKEN_CONF_NODE_INITIALIZER(struct cmd_cd_node_result, node,
273                                     &conf_root, &conf_cur,
274                                     CONFNODE_F_IS_DIR,
275                                     CONFNODE_F_IS_DIR|CONFNODE_F_INVISIBLE);
276
277 cmdline_parse_inst_t cmd_cd_node = {
278         .f = cmd_cd_node_parsed,  /* function to call */
279         .data = NULL,      /* 2nd arg of func */
280         .help_str = "<cd CONFNODE> go down in a configuration sub-tree",
281         .tokens = {        /* token list, NULL terminated */
282                 (void *)&cmd_cd_node_cd,
283                 (void *)&cmd_cd_node_node,
284                 NULL,
285         },
286 };
287
288 /**********************************************************/
289 /* set bool node */
290 /**********************************************************/
291
292 struct cmd_set_bool_result {
293         cmdline_fixed_string_t set;
294         struct confnode *node;
295         cmdline_fixed_string_t val;
296 };
297
298 static void cmd_set_bool_parsed(void *parsed_result,
299                                struct cmdline *cl,
300                                void *data)
301 {
302         struct cmd_set_bool_result *res = parsed_result;
303         struct confnode *n = res->node;
304         char value[BUFSIZ];
305
306         while (n) {
307                 if (confnode_set_user_strvalue(n, res->val) < 0)
308                         printf("Error, cannot set value\n");
309                 else {
310                         if (strcmp(res->val, "y") == 0)
311                                 confnode_check_deps(n, 1);
312                         if (confnode_get_value(n, value,
313                                                   sizeof(value)) < 0)
314                                 printf("Error, cannot re-read value\n");
315                 }
316                 printf("<%s> value set to %s\n", n->name, value);
317                 n = TAILQ_NEXT(n, user_next);
318         }
319 }
320
321 cmdline_parse_token_string_t cmd_set_bool_set =
322         TOKEN_STRING_INITIALIZER(struct cmd_set_bool_result, set, "set");
323 parse_token_conf_node_t cmd_set_bool_node =
324         TOKEN_CONF_NODE_INITIALIZER(struct cmd_set_bool_result, node,
325                                     &conf_root, &conf_cur,
326                                     CONFNODE_F_BOOL,
327                                     CONFNODE_F_BOOL|CONFNODE_F_INVISIBLE);
328 cmdline_parse_token_string_t cmd_set_bool_val =
329         TOKEN_STRING_INITIALIZER(struct cmd_set_bool_result, val, "y#n");
330
331 cmdline_parse_inst_t cmd_set_bool = {
332         .f = cmd_set_bool_parsed,  /* function to call */
333         .data = NULL,      /* 2nd arg of func */
334         .help_str = "<set CONFNODE y|n> set boolean value",
335         .tokens = {        /* token list, NULL terminated */
336                 (void *)&cmd_set_bool_set,
337                 (void *)&cmd_set_bool_node,
338                 (void *)&cmd_set_bool_val,
339                 NULL,
340         },
341 };
342
343 /**********************************************************/
344 /* set int node */
345 /**********************************************************/
346
347 struct cmd_set_int_result {
348         cmdline_fixed_string_t set;
349         struct confnode *node;
350         int32_t val;
351 };
352
353 static void cmd_set_int_parsed(void *parsed_result,
354                                struct cmdline *cl,
355                                void *data)
356 {
357         struct cmd_set_int_result *res = parsed_result;
358         struct confnode *n = res->node;
359         char value[BUFSIZ];
360
361         while (n) {
362                 snprintf(value, sizeof(value), "%"PRIi32, res->val);
363                 if (confnode_set_user_strvalue(n, value) < 0)
364                         printf("Error, cannot set value\n");
365                 else {
366                         if (res->val)
367                                 confnode_check_deps(n, 1);
368                         if (confnode_get_value(n, value,
369                                                   sizeof(value)) < 0)
370                                 printf("Error, cannot re-read value\n");
371                 }
372                 printf("<%s> value set to %s\n", n->name, value);
373                 n = TAILQ_NEXT(n, user_next);
374         }
375 }
376
377 cmdline_parse_token_string_t cmd_set_int_set =
378         TOKEN_STRING_INITIALIZER(struct cmd_set_int_result, set, "set");
379 parse_token_conf_node_t cmd_set_int_node =
380         TOKEN_CONF_NODE_INITIALIZER(struct cmd_set_int_result, node,
381                                     &conf_root, &conf_cur,
382                                     CONFNODE_F_INT,
383                                     CONFNODE_F_INT|CONFNODE_F_INVISIBLE);
384 cmdline_parse_token_num_t cmd_set_int_val =
385         TOKEN_NUM_INITIALIZER(struct cmd_set_int_result, val, INT32);
386
387 cmdline_parse_inst_t cmd_set_int = {
388         .f = cmd_set_int_parsed,  /* function to call */
389         .data = NULL,      /* 2nd arg of func */
390         .help_str = "<set CONFNODE INT> set integer value",
391         .tokens = {        /* token list, NULL terminated */
392                 (void *)&cmd_set_int_set,
393                 (void *)&cmd_set_int_node,
394                 (void *)&cmd_set_int_val,
395                 NULL,
396         },
397 };
398
399 /**********************************************************/
400 /* set str node */
401 /**********************************************************/
402
403 struct cmd_set_str_result {
404         cmdline_fixed_string_t set;
405         struct confnode *node;
406 };
407
408 static int get_str(struct cmdline *cl, char *buf, int len)
409 {
410         struct rdline rl;
411         int ret = 0;
412         int c;
413         char *s;
414
415         buf[0] = '\0';
416
417         rdline_init(&rl, cl->s_in, cl->s_out, NULL, NULL, NULL);
418         rl.opaque = cl;
419
420         /* XXX use rdline() */
421         rdline_newline(&rl, "edit> ");
422
423         while (ret == RDLINE_RES_SUCCESS) {
424                 read(cl->s_in, &c, 1);
425                 if (c <= 0)
426                         return -1;
427                 ret = rdline_char_in(&rl, c);
428                 if (ret == RDLINE_RES_VALIDATED) {
429                         snprintf(buf, len, "%s", rdline_get_buffer(&rl));
430                         s = strchr(buf, '\n');
431                         if (s)
432                                 *s = '\0';
433                         return 0;
434                 }
435         }
436
437         return -1;
438 }
439
440 static void cmd_set_str_parsed(void *parsed_result,
441                                struct cmdline *cl,
442                                void *data)
443 {
444         struct cmd_set_str_result *res = parsed_result;
445         struct confnode *n = res->node;
446         char value[BUFSIZ];
447
448         if (confnode_get_value(res->node, value, sizeof(value)) < 0) {
449                 printf("Error, cannot read value\n");
450                 return;
451         }
452         printf("Previous value is %s\n", value);
453         printf("type CTRL-d on an empty line to abort\n");
454         if (get_str(cl, value, sizeof(value))) {
455                 printf("Aborted\n");
456                 return;
457         }
458         while (n) {
459                 snprintf(res->node->value, sizeof(res->node->value),
460                          "%s", value);
461                 if (strcmp(value, ""))
462                         confnode_check_deps(n, 1);
463                 if (confnode_get_value(n, value,
464                                        sizeof(value)) < 0)
465                         printf("Error, cannot re-read value\n");
466                 printf("<%s> value set to %s\n", n->name, value);
467                 n = TAILQ_NEXT(n, user_next);
468         }
469 }
470
471 cmdline_parse_token_string_t cmd_set_str_set =
472         TOKEN_STRING_INITIALIZER(struct cmd_set_str_result, set, "set");
473 parse_token_conf_node_t cmd_set_str_node =
474         TOKEN_CONF_NODE_INITIALIZER(struct cmd_set_str_result, node,
475                                     &conf_root, &conf_cur,
476                                     CONFNODE_F_STR,
477                                     CONFNODE_F_STR|CONFNODE_F_INVISIBLE);
478
479 cmdline_parse_inst_t cmd_set_str = {
480         .f = cmd_set_str_parsed,  /* function to call */
481         .data = NULL,      /* 2nd arg of func */
482         .help_str = "<set CONFNODE> set str value",
483         .tokens = {        /* token list, NULL terminated */
484                 (void *)&cmd_set_str_set,
485                 (void *)&cmd_set_str_node,
486                 NULL,
487         },
488 };
489
490 /**********************************************************/
491 /* show node */
492 /**********************************************************/
493
494 struct cmd_show_node_result {
495         cmdline_fixed_string_t show;
496         cmdline_fixed_string_t all;
497         struct confnode *node;
498 };
499
500 static void cmd_show_node_parsed(void *parsed_result,
501                                struct cmdline *cl,
502                                void *data)
503 {
504         struct cmd_show_node_result *res = parsed_result;
505         struct confnode *n = res->node;
506         int details = 0;
507
508         if (data)
509                 details = 1;
510
511         while (n) {
512                 confnode_display_long(n);
513                 n = TAILQ_NEXT(n, user_next);
514         }
515 }
516
517 cmdline_parse_token_string_t cmd_show_node_show =
518         TOKEN_STRING_INITIALIZER(struct cmd_show_node_result, show, "show");
519 cmdline_parse_token_string_t cmd_show_node_all =
520         TOKEN_STRING_INITIALIZER(struct cmd_show_node_result, all, "-a");
521 parse_token_conf_node_t cmd_show_node_node =
522         TOKEN_CONF_NODE_INITIALIZER(struct cmd_show_node_result, node,
523                                     &conf_root, &conf_cur, 0, CONFNODE_F_INVISIBLE);
524
525 cmdline_parse_inst_t cmd_show_node = {
526         .f = cmd_show_node_parsed,  /* function to call */
527         .data = NULL,      /* 2nd arg of func */
528         .help_str = "<show CONFNODE> display infos on the config option",
529         .tokens = {        /* token list, NULL terminated */
530                 (void *)&cmd_show_node_show,
531                 (void *)&cmd_show_node_node,
532                 NULL,
533         },
534 };
535
536 cmdline_parse_inst_t cmd_show_node_a = {
537         .f = cmd_show_node_parsed,  /* function to call */
538         .data = (void *)1,      /* 2nd arg of func */
539         .help_str = "<show -a CONFNODE> display detailed infos on the config option",
540         .tokens = {        /* token list, NULL terminated */
541                 (void *)&cmd_show_node_show,
542                 (void *)&cmd_show_node_all,
543                 (void *)&cmd_show_node_node,
544                 NULL,
545         },
546 };
547
548 /**********************************************************/
549 /* load / load file */
550 /**********************************************************/
551
552 struct cmd_load_result {
553         cmdline_fixed_string_t load;
554         cmdline_fixed_string_t file;
555 };
556
557 static void cmd_load_parsed(void *parsed_result,
558                             struct cmdline *cl,
559                             void *data)
560 {
561         char prompt[RDLINE_PROMPT_SIZE];
562         struct cmd_load_result *res = parsed_result;
563         const char *filename;
564
565         if (data)
566                 filename = data;
567         else
568                 filename = res->file;
569
570         conf_cur = conf_reset_and_read(conf_root, filename);
571         if (conf_root == NULL) {
572                 printf("error loading <%s>\n", filename);
573                 return;
574         }
575         conf_cur = conf_root;
576         snprintf(prompt, sizeof(prompt), "root> ");
577
578         printf("%s loaded\n", filename);
579 }
580
581 cmdline_parse_token_string_t cmd_load_load =
582         TOKEN_STRING_INITIALIZER(struct cmd_load_result, load, "load");
583
584 cmdline_parse_inst_t cmd_load = {
585         .f = cmd_load_parsed,  /* function to call */
586         .data = ".config",      /* 2nd arg of func */
587         .help_str = "<load> reload the .config file",
588         .tokens = {        /* token list, NULL terminated */
589                 (void *)&cmd_load_load,
590                 NULL,
591         },
592 };
593
594 cmdline_parse_token_string_t cmd_load_file =
595         TOKEN_STRING_INITIALIZER(struct cmd_load_result, file, NULL);
596
597 cmdline_parse_inst_t cmd_loadfile = {
598         .f = cmd_load_parsed,  /* function to call */
599         .data = NULL,      /* 2nd arg of func */
600         .help_str = "<load FILE> load another .config file",
601         .tokens = {        /* token list, NULL terminated */
602                 (void *)&cmd_load_load,
603                 (void *)&cmd_load_file,
604                 NULL,
605         },
606 };
607
608 /**********************************************************/
609 /* save / save file */
610 /**********************************************************/
611
612 struct cmd_save_result {
613         cmdline_fixed_string_t save;
614         cmdline_fixed_string_t file;
615 };
616
617 static void cmd_save_parsed(void *parsed_result,
618                             struct cmdline *cl,
619                             void *data)
620 {
621         struct cmd_save_result *res = parsed_result;
622         const char *filename;
623
624         if (data)
625                 filename = data;
626         else
627                 filename = res->file;
628
629         if (dotconfig_write(filename, conf_root) < 0) {
630                 printf("error saving <%s>\n", filename);
631                 return;
632         }
633         printf("%s saved\n", filename);
634 }
635
636 cmdline_parse_token_string_t cmd_save_save =
637         TOKEN_STRING_INITIALIZER(struct cmd_save_result, save, "save");
638
639 cmdline_parse_inst_t cmd_save = {
640         .f = cmd_save_parsed,  /* function to call */
641         .data = ".config",      /* 2nd arg of func */
642         .help_str = "<save> write the .config file",
643         .tokens = {        /* token list, NULL terminated */
644                 (void *)&cmd_save_save,
645                 NULL,
646         },
647 };
648
649 cmdline_parse_token_string_t cmd_save_file =
650         TOKEN_STRING_INITIALIZER(struct cmd_save_result, file, NULL);
651
652 cmdline_parse_inst_t cmd_savefile = {
653         .f = cmd_save_parsed,  /* function to call */
654         .data = NULL,      /* 2nd arg of func */
655         .help_str = "<save FILE> save in another .config file",
656         .tokens = {        /* token list, NULL terminated */
657                 (void *)&cmd_save_save,
658                 (void *)&cmd_save_file,
659                 NULL,
660         },
661 };
662
663 /**********************************************************/
664
665
666 /**********************************************************/
667 /**********************************************************/
668 /****** CONTEXT (list of instruction) */
669
670 /* in progmem */
671 cmdline_parse_ctx_t main_ctx = {
672         .name = "main",
673         .insts = {
674                 &cmd_ls,
675                 &cmd_ls_node,
676                 &cmd_pwd,
677                 &cmd_cd_prev,
678                 &cmd_cd_root,
679                 &cmd_cd_node,
680                 &cmd_set_bool,
681                 &cmd_set_int,
682                 &cmd_set_str,
683                 &cmd_show_node,
684                 &cmd_show_node_a,
685                 &cmd_load,
686                 &cmd_loadfile,
687                 &cmd_save,
688                 &cmd_savefile,
689                 NULL,
690         },
691 };
692