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