Imported Upstream version 1.6.0
[platform/upstream/augeas.git] / src / augrun.c
1 /*
2  * augrun.c: command interpreter for augeas
3  *
4  * Copyright (C) 2011-2015 David Lutterkort
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
19  *
20  * Author: David Lutterkort <lutter@redhat.com>
21  */
22
23 #include <config.h>
24 #include "augeas.h"
25 #include "internal.h"
26 #include "memory.h"
27 #include "errcode.h"
28
29 #include <ctype.h>
30 #include <libxml/tree.h>
31
32 /*
33  * Command handling infrastructure
34  */
35 enum command_opt_type {
36     CMD_NONE,
37     CMD_STR,           /* String argument */
38     CMD_PATH           /* Path expression */
39 };
40
41 struct command_opt_def {
42     bool                  optional; /* Optional or mandatory */
43     enum command_opt_type type;
44     const char           *name;
45     const char           *help;
46 };
47
48 #define CMD_OPT_DEF_LAST { .type = CMD_NONE, .name = NULL }
49
50 struct command {
51     const struct command_def *def;
52     struct command_opt       *opt;
53     struct augeas            *aug;
54     struct error             *error; /* Same as aug->error */
55     FILE                     *out;
56     bool                      quit;
57 };
58
59 typedef void (*cmd_handler)(struct command*);
60
61 struct command_def {
62     const char                   *name;
63     const char                   *category;
64     const struct command_opt_def *opts;
65     cmd_handler                   handler;
66     const char                   *synopsis;
67     const char                   *help;
68 };
69
70 static const struct command_def cmd_def_last =
71     { .name = NULL, .opts = NULL, .handler = NULL,
72       .synopsis = NULL, .help = NULL };
73
74 struct command_opt {
75     struct command_opt           *next;
76     const struct command_opt_def *def;
77     char                         *value;
78 };
79
80 struct command_grp_def {
81     const char                     *name;
82     const struct command_def const *commands[];
83 };
84
85 static const struct command_grp_def cmd_grp_def_last =
86     { .name = NULL, .commands = { } };
87
88 static const struct command_def *lookup_cmd_def(const char *name);
89
90 static const struct command_opt_def *
91 find_def(const struct command *cmd, const char *name) {
92     const struct command_opt_def *def;
93     for (def = cmd->def->opts; def->name != NULL; def++) {
94         if (STREQ(def->name, name))
95             return def;
96     }
97     return NULL;
98 }
99
100 static struct command_opt *
101 find_opt(const struct command *cmd, const char *name) {
102     const struct command_opt_def *def = find_def(cmd, name);
103     assert(def != NULL);
104
105     for (struct command_opt *opt = cmd->opt; opt != NULL; opt = opt->next) {
106         if (opt->def == def)
107             return opt;
108     }
109     assert(def->optional);
110     return NULL;
111 }
112
113 static const char *arg_value(const struct command *cmd, const char *name) {
114     struct command_opt *opt = find_opt(cmd, name);
115
116     return (opt == NULL) ? NULL : opt->value;
117 }
118
119 static char *nexttoken(struct command *cmd, char **line, bool path) {
120     char *r, *s, *w;
121     char quot = '\0';
122     int nbracket = 0;
123     int nescaped = 0;
124     bool copy;
125
126     s = *line;
127
128     while (*s && isblank(*s)) s+= 1;
129     r = s;
130     w = s;
131     while (*s) {
132         copy = true;
133         if (*s == '\\') {
134             switch (*(s+1)) {
135                 case '[':
136                 case ']':  /* pass both literally */
137                     nescaped = 2;
138                     break;
139                 case 't':  /* insert tab */
140                     *(s+1) = '\t';
141                     nescaped = 1;
142                     s += 1;
143                     break;
144                 case 'n':  /* insert newline */
145                     *(s+1) = '\n';
146                     nescaped = 1;
147                     s += 1;
148                     break;
149                 case ' ':
150                 case '\t': /* pass both through if quoted, else fall */
151                     if (quot) break;
152                 case '\'':
153                 case '"':  /* pass both through if opposite quote, else fall */
154                     if (quot && quot != *(s+1)) break;
155                 case '\\': /* pass next character through */
156                     nescaped = 1;
157                     s += 1;
158                     break;
159                 default:
160                     ERR_REPORT(cmd, AUG_ECMDRUN, "unknown escape sequence");
161                     return NULL;
162             }
163         }
164
165         if (nescaped == 0) {
166             if (*s == '[') nbracket += 1;
167             if (*s == ']') nbracket -= 1;
168             if (nbracket < 0) {
169                 ERR_REPORT(cmd, AUG_ECMDRUN, "unmatched [");
170                 return NULL;
171             }
172
173             if (!path || nbracket == 0) {
174                 if (!quot && (*s == '\'' || *s == '"')) {
175                     quot = *s;
176                     copy = false;
177                 } else if (quot && *s == quot) {
178                     quot = '\0';
179                     copy = false;
180                 }
181
182                 if (!quot && isblank(*s))
183                     break;
184             }
185         } else {
186             nescaped -= 1;
187         }
188
189         if (copy) {
190             *w = *s;
191             w += 1;
192         }
193         s += 1;
194     }
195     if (*s == '\0' && path && nbracket > 0) {
196         ERR_REPORT(cmd, AUG_ECMDRUN, "unmatched [");
197         return NULL;
198     }
199     if (*s == '\0' && quot) {
200         ERR_REPORT(cmd, AUG_ECMDRUN, "unmatched %c", quot);
201         return NULL;
202     }
203     while (*w && w <= s)
204         *w++ = '\0';
205     *line = w;
206     return r;
207 }
208
209 static struct command_opt *
210 make_command_opt(struct command *cmd, const struct command_opt_def *def) {
211     struct command_opt *copt = NULL;
212     int r;
213
214     r = ALLOC(copt);
215     ERR_NOMEM(r < 0, cmd->aug);
216     copt->def = def;
217     list_append(cmd->opt, copt);
218  error:
219     return copt;
220 }
221
222 static void free_command_opts(struct command *cmd) {
223     struct command_opt *next;
224
225     next = cmd->opt;
226     while (next != NULL) {
227         struct command_opt *del = next;
228         next = del->next;
229         free(del);
230     }
231     cmd->opt = NULL;
232 }
233
234 static int parseline(struct command *cmd, char *line) {
235     char *tok;
236     int narg = 0, nopt = 0;
237     const struct command_opt_def *def;
238
239     tok = nexttoken(cmd, &line, false);
240     if (tok == NULL)
241         return -1;
242     cmd->def = lookup_cmd_def(tok);
243     if (cmd->def == NULL) {
244         ERR_REPORT(cmd, AUG_ECMDRUN, "Unknown command '%s'", tok);
245         return -1;
246     }
247
248     for (def = cmd->def->opts; def->name != NULL; def++) {
249         narg += 1;
250         if (def->optional)
251             nopt += 1;
252     }
253
254     int curarg = 0;
255     def = cmd->def->opts;
256     while (*line != '\0') {
257         while (*line && isblank(*line)) line += 1;
258
259         if (curarg >= narg) {
260             ERR_REPORT(cmd, AUG_ECMDRUN,
261                  "Too many arguments. Command %s takes only %d arguments",
262                   cmd->def->name, narg);
263                 return -1;
264         }
265
266         struct command_opt *opt = make_command_opt(cmd, def);
267         if (opt == NULL)
268             return -1;
269
270         if (def->type == CMD_PATH) {
271             tok = nexttoken(cmd, &line, true);
272             cleanpath(tok);
273         } else {
274             tok = nexttoken(cmd, &line, false);
275         }
276         if (tok == NULL)
277             return -1;
278         opt->value = tok;
279         curarg += 1;
280         def += 1;
281         while (*line && isblank(*line)) line += 1;
282     }
283
284     if (curarg < narg - nopt) {
285         ERR_REPORT(cmd, AUG_ECMDRUN, "Not enough arguments for %s", cmd->def->name);
286         return -1;
287     }
288
289     return 0;
290 }
291
292 /*
293  * Commands
294  */
295 static void format_desc(const char *d) {
296     printf("    ");
297     for (const char *s = d; *s; s++) {
298         if (*s == '\n')
299             printf("\n   ");
300         else
301             putchar(*s);
302     }
303     printf("\n\n");
304 }
305
306 static void format_defname(char *buf, const struct command_opt_def *def,
307                            bool mark_optional) {
308     char *p;
309     if (mark_optional && def->optional)
310         p = stpcpy(buf, " [<");
311     else
312         p = stpcpy(buf, " <");
313     for (int i=0; i < strlen(def->name); i++)
314         *p++ = toupper(def->name[i]);
315     *p++ = '>';
316     if (mark_optional && def->optional)
317         *p++ = ']';
318     *p = '\0';
319 }
320
321 static void cmd_help(struct command *cmd);
322
323 static const struct command_opt_def cmd_help_opts[] = {
324     { .type = CMD_STR, .name = "command", .optional = true,
325       .help = "print help for this command only" },
326     CMD_OPT_DEF_LAST
327 };
328
329 static const struct command_def cmd_help_def = {
330     .name = "help",
331     .opts = cmd_help_opts,
332     .handler = cmd_help,
333     .synopsis = "print help",
334     .help = "list all commands or print details about one command"
335 };
336
337 static void cmd_quit(ATTRIBUTE_UNUSED struct command *cmd) {
338     cmd->quit = true;
339 }
340
341 static const struct command_opt_def cmd_quit_opts[] = {
342     CMD_OPT_DEF_LAST
343 };
344
345 static const struct command_def cmd_quit_def = {
346     .name = "quit",
347     .opts = cmd_quit_opts,
348     .handler = cmd_quit,
349     .synopsis = "exit the program",
350     .help = "Exit the program"
351 };
352
353 static char *ls_pattern(struct command *cmd, const char *path) {
354     char *q = NULL;
355     int r;
356
357     if (path[strlen(path)-1] == SEP)
358         r = xasprintf(&q, "%s*", path);
359     else
360         r = xasprintf(&q, "%s/*", path);
361     ERR_NOMEM(r < 0, cmd->aug);
362  error:
363     return q;
364 }
365
366 static int child_count(struct command *cmd, const char *path) {
367     char *q = ls_pattern(cmd, path);
368     int cnt;
369
370     if (q == NULL)
371         return 0;
372     cnt = aug_match(cmd->aug, q, NULL);
373     if (HAS_ERR(cmd))
374         cnt = -1;
375     free(q);
376     return cnt;
377 }
378
379 static void cmd_ls(struct command *cmd) {
380     int cnt = 0;
381     char *path = NULL;
382     char **paths = NULL;
383
384     path = ls_pattern(cmd, arg_value(cmd, "path"));
385     ERR_BAIL(cmd);
386
387     cnt = aug_match(cmd->aug, path, &paths);
388     ERR_BAIL(cmd);
389     for (int i=0; i < cnt; i++) {
390         const char *val;
391         const char *basnam = strrchr(paths[i], SEP);
392         int dir = child_count(cmd, paths[i]);
393         aug_get(cmd->aug, paths[i], &val);
394         ERR_BAIL(cmd);
395         basnam = (basnam == NULL) ? paths[i] : basnam + 1;
396         if (val == NULL)
397             val = "(none)";
398         fprintf(cmd->out, "%s%s= %s\n", basnam, dir ? "/ " : " ", val);
399         FREE(paths[i]);
400     }
401  error:
402     free(path);
403     for (int i=0; i < cnt; i++)
404         FREE(paths[i]);
405     free(paths);
406 }
407
408 static const struct command_opt_def cmd_ls_opts[] = {
409     { .type = CMD_PATH, .name = "path", .optional = false,
410       .help = "the node whose children to list" },
411     CMD_OPT_DEF_LAST
412 };
413
414 static const struct command_def cmd_ls_def = {
415     .name = "ls",
416     .opts = cmd_ls_opts,
417     .handler = cmd_ls,
418     .synopsis = "list children of a node",
419     .help = "list the direct children of a node"
420 };
421
422 static void cmd_match(struct command *cmd) {
423     int cnt = 0;
424     const char *pattern = arg_value(cmd, "path");
425     const char *value = arg_value(cmd, "value");
426     char **matches = NULL;
427     bool filter = (value != NULL) && (strlen(value) > 0);
428
429     cnt = aug_match(cmd->aug, pattern, &matches);
430     ERR_BAIL(cmd);
431     ERR_THROW(cnt < 0, cmd->aug, AUG_ECMDRUN,
432               "  (error matching %s)\n", pattern);
433     if (cnt == 0) {
434         fprintf(cmd->out, "  (no matches)\n");
435         goto done;
436     }
437
438     for (int i=0; i < cnt; i++) {
439         const char *val;
440         aug_get(cmd->aug, matches[i], &val);
441         ERR_BAIL(cmd);
442         if (val == NULL)
443             val = "(none)";
444         if (filter) {
445             if (STREQ(value, val))
446                 fprintf(cmd->out, "%s\n", matches[i]);
447         } else {
448             fprintf(cmd->out, "%s = %s\n", matches[i], val);
449         }
450     }
451  error:
452  done:
453     for (int i=0; i < cnt; i++)
454         free(matches[i]);
455     free(matches);
456 }
457
458 static const struct command_opt_def cmd_match_opts[] = {
459     { .type = CMD_PATH, .name = "path", .optional = false,
460       .help = "the path expression to match" },
461     { .type = CMD_STR, .name = "value", .optional = true,
462       .help = "only show matches with this value" },
463     CMD_OPT_DEF_LAST
464 };
465
466 static const struct command_def cmd_match_def = {
467     .name = "match",
468     .opts = cmd_match_opts,
469     .handler = cmd_match,
470     .synopsis = "print matches for a path expression",
471     .help = "Find all paths that match the path expression PATH. "
472             "If VALUE is given,\n only the matching paths whose value equals "
473             "VALUE are printed"
474 };
475
476 static void cmd_rm(struct command *cmd) {
477     int cnt;
478     const char *path = arg_value(cmd, "path");
479     cnt = aug_rm(cmd->aug, path);
480     if (! HAS_ERR(cmd))
481         fprintf(cmd->out, "rm : %s %d\n", path, cnt);
482 }
483
484 static const struct command_opt_def cmd_rm_opts[] = {
485     { .type = CMD_PATH, .name = "path", .optional = false,
486       .help = "remove all nodes matching this path expression" },
487     CMD_OPT_DEF_LAST
488 };
489
490 static const struct command_def cmd_rm_def = {
491     .name = "rm",
492     .opts = cmd_rm_opts,
493     .handler = cmd_rm,
494     .synopsis = "delete nodes and subtrees",
495     .help = "Delete PATH and all its children from the tree"
496 };
497
498 static void cmd_mv(struct command *cmd) {
499     const char *src = arg_value(cmd, "src");
500     const char *dst = arg_value(cmd, "dst");
501     int r;
502
503     r = aug_mv(cmd->aug, src, dst);
504     if (r < 0)
505         ERR_REPORT(cmd, AUG_ECMDRUN,
506                    "Moving %s to %s failed", src, dst);
507 }
508
509 static const struct command_opt_def cmd_mv_opts[] = {
510     { .type = CMD_PATH, .name = "src", .optional = false,
511       .help = "the tree to move" },
512     { .type = CMD_PATH, .name = "dst", .optional = false,
513       .help = "where to put the source tree" },
514     CMD_OPT_DEF_LAST
515 };
516
517 static const char const cmd_mv_help[] =
518     "Move node  SRC to DST.  SRC must match  exactly one node in  "
519     "the tree.\n DST  must either  match  exactly one  node  in the  tree,  "
520     "or may  not\n exist  yet. If  DST exists  already, it  and all  its  "
521     "descendants are\n deleted.  If  DST  does  not   exist  yet,  it  and  "
522     "all  its  missing\n ancestors are created.";
523
524 static const struct command_def cmd_mv_def = {
525     .name = "mv",
526     .opts = cmd_mv_opts,
527     .handler = cmd_mv,
528     .synopsis = "move a subtree",
529     .help = cmd_mv_help
530 };
531
532 static const struct command_def cmd_move_def = {
533     .name = "move",
534     .opts = cmd_mv_opts,
535     .handler = cmd_mv,
536     .synopsis = "move a subtree (alias of 'mv')",
537     .help = cmd_mv_help
538 };
539
540 static void cmd_cp(struct command *cmd) {
541     const char *src = arg_value(cmd, "src");
542     const char *dst = arg_value(cmd, "dst");
543     int r;
544
545     r = aug_cp(cmd->aug, src, dst);
546     if (r < 0)
547         ERR_REPORT(cmd, AUG_ECMDRUN,
548                    "Copying %s to %s failed", src, dst);
549 }
550
551 static const struct command_opt_def cmd_cp_opts[] = {
552     { .type = CMD_PATH, .name = "src", .optional = false,
553       .help = "the tree to copy" },
554     { .type = CMD_PATH, .name = "dst", .optional = false,
555       .help = "where to copy the source tree" },
556     CMD_OPT_DEF_LAST
557 };
558
559 static const char const cmd_cp_help[] =
560     "Copy node  SRC to DST.  SRC must match  exactly one node in  "
561     "the tree.\n DST  must either  match  exactly one  node  in the  tree,  "
562     "or may  not\n exist  yet. If  DST exists  already, it  and all  its  "
563     "descendants are\n deleted.  If  DST  does  not   exist  yet,  it  and  "
564     "all  its  missing\n ancestors are created.";
565
566 static const struct command_def cmd_cp_def = {
567     .name = "cp",
568     .opts = cmd_cp_opts,
569     .handler = cmd_cp,
570     .synopsis = "copy a subtree",
571     .help = cmd_cp_help
572 };
573
574 static const struct command_def cmd_copy_def = {
575     .name = "copy",
576     .opts = cmd_cp_opts,
577     .handler = cmd_cp,
578     .synopsis = "copy a subtree (alias of 'cp')",
579     .help = cmd_cp_help
580 };
581
582 static void cmd_rename(struct command *cmd) {
583     const char *src = arg_value(cmd, "src");
584     const char *lbl = arg_value(cmd, "lbl");
585     int cnt;
586
587     cnt = aug_rename(cmd->aug, src, lbl);
588     if (cnt < 0)
589         ERR_REPORT(cmd, AUG_ECMDRUN,
590                    "Renaming %s to %s failed", src, lbl);
591     if (! HAS_ERR(cmd))
592         fprintf(cmd->out, "rename : %s to %s %d\n", src, lbl, cnt);
593 }
594
595 static const struct command_opt_def cmd_rename_opts[] = {
596     { .type = CMD_PATH, .name = "src", .optional = false,
597       .help = "the tree to rename" },
598     { .type = CMD_STR, .name = "lbl", .optional = false,
599       .help = "the new label" },
600     CMD_OPT_DEF_LAST
601 };
602
603 static const char const cmd_rename_help[] =
604     "Rename the label of all nodes matching SRC to LBL.";
605
606 static const struct command_def cmd_rename_def = {
607     .name = "rename",
608     .opts = cmd_rename_opts,
609     .handler = cmd_rename,
610     .synopsis = "rename a subtree label",
611     .help = cmd_rename_help
612 };
613
614 static void cmd_set(struct command *cmd) {
615     const char *path = arg_value(cmd, "path");
616     const char *val = arg_value(cmd, "value");
617     int r;
618
619     r = aug_set(cmd->aug, path, val);
620     if (r < 0)
621         ERR_REPORT(cmd, AUG_ECMDRUN, "Setting %s failed", path);
622 }
623
624 static const struct command_opt_def cmd_set_opts[] = {
625     { .type = CMD_PATH, .name = "path", .optional = false,
626       .help = "set the value of this node" },
627     { .type = CMD_STR, .name = "value", .optional = true,
628       .help = "the new value for the node" },
629     CMD_OPT_DEF_LAST
630 };
631
632 static const struct command_def cmd_set_def = {
633     .name = "set",
634     .opts = cmd_set_opts,
635     .handler = cmd_set,
636     .synopsis = "set the value of a node",
637     .help = "Associate VALUE with PATH.  If PATH is not in the tree yet, "
638     "it and all\n its ancestors will be created. These new tree entries "
639     "will appear last\n amongst their siblings"
640 };
641
642 static void cmd_setm(struct command *cmd) {
643     const char *base = arg_value(cmd, "base");
644     const char *sub  = arg_value(cmd, "sub");
645     const char *val  = arg_value(cmd, "value");
646
647     aug_setm(cmd->aug, base, sub, val);
648 }
649
650 static const struct command_opt_def cmd_setm_opts[] = {
651     { .type = CMD_PATH, .name = "base", .optional = false,
652       .help = "the base node" },
653     { .type = CMD_PATH, .name = "sub", .optional = false,
654       .help = "the subtree relative to the base" },
655     { .type = CMD_STR, .name = "value", .optional = true,
656       .help = "the value for the nodes" },
657     CMD_OPT_DEF_LAST
658 };
659
660 static const struct command_def cmd_setm_def = {
661     .name = "setm",
662     .opts = cmd_setm_opts,
663     .handler = cmd_setm,
664     .synopsis = "set the value of multiple nodes",
665     .help = "Set multiple nodes in one operation.  Find or create a node"
666     " matching SUB\n by interpreting SUB as a  path expression relative"
667     " to each node matching\n BASE. If SUB is '.', the nodes matching "
668     "BASE will be modified."
669 };
670
671 static void cmd_clearm(struct command *cmd) {
672     const char *base = arg_value(cmd, "base");
673     const char *sub  = arg_value(cmd, "sub");
674
675     aug_setm(cmd->aug, base, sub, NULL);
676 }
677
678 static const struct command_opt_def cmd_clearm_opts[] = {
679     { .type = CMD_PATH, .name = "base", .optional = false,
680       .help = "the base node" },
681     { .type = CMD_PATH, .name = "sub", .optional = false,
682       .help = "the subtree relative to the base" },
683     CMD_OPT_DEF_LAST
684 };
685
686 static const struct command_def cmd_clearm_def = {
687     .name = "clearm",
688     .opts = cmd_clearm_opts,
689     .handler = cmd_clearm,
690     .synopsis = "clear the value of multiple nodes",
691     .help = "Clear multiple nodes values in one operation. Find or create a"
692     " node matching SUB\n by interpreting SUB as a path expression relative"
693     " to each node matching\n BASE. If SUB is '.', the nodes matching "
694     "BASE will be modified."
695 };
696
697 static void cmd_span(struct command *cmd) {
698     const char *path = arg_value(cmd, "path");
699     int r;
700     uint label_start, label_end, value_start, value_end, span_start, span_end;
701     char *filename = NULL;
702     const char *option = NULL;
703
704     if (aug_get(cmd->aug, AUGEAS_SPAN_OPTION, &option) != 1) {
705         printf("Error: option " AUGEAS_SPAN_OPTION " not found\n");
706         return;
707     }
708     if (streqv(AUG_DISABLE, option)) {
709         ERR_REPORT(cmd, AUG_ECMDRUN,
710                    "Span is not enabled. To enable, run the commands:\n"
711                    "    set %s %s\n    rm %s\n    load\n",
712                    AUGEAS_SPAN_OPTION, AUG_ENABLE, AUGEAS_FILES_TREE);
713         return;
714     } else if (! streqv(AUG_ENABLE, option)) {
715         ERR_REPORT(cmd, AUG_ECMDRUN,
716                    "option %s must be %s or %s\n", AUGEAS_SPAN_OPTION,
717                    AUG_ENABLE, AUG_DISABLE);
718         return;
719     }
720     r = aug_span(cmd->aug, path, &filename, &label_start, &label_end,
721                  &value_start, &value_end, &span_start, &span_end);
722     ERR_THROW(r == -1, cmd, AUG_ECMDRUN, "failed to retrieve span");
723
724     fprintf(cmd->out, "%s label=(%i:%i) value=(%i:%i) span=(%i,%i)\n",
725             filename, label_start, label_end,
726             value_start, value_end, span_start, span_end);
727  error:
728     free(filename);
729 }
730
731 static const struct command_opt_def cmd_span_opts[] = {
732     { .type = CMD_PATH, .name = "path", .optional = false,
733       .help = "path matching exactly one node" },
734     CMD_OPT_DEF_LAST
735 };
736
737 static const struct command_def cmd_span_def = {
738     .name = "span",
739     .opts = cmd_span_opts,
740     .handler = cmd_span,
741     .synopsis = "print position in input file corresponding to tree",
742     .help = "Print the name of the file from which the node PATH was generated, as\n well as information about the positions in the file  corresponding to\n the label, the value, and the  entire  node. PATH must match  exactly\n one node.\n\n You need to run 'set /augeas/span enable' prior to  loading files to\n enable recording of span information. It is disabled by default."
743 };
744
745 static void cmd_defvar(struct command *cmd) {
746     const char *name = arg_value(cmd, "name");
747     const char *path = arg_value(cmd, "expr");
748
749     aug_defvar(cmd->aug, name, path);
750 }
751
752 static const struct command_opt_def cmd_defvar_opts[] = {
753     { .type = CMD_STR, .name = "name", .optional = false,
754       .help = "the name of the variable" },
755     { .type = CMD_PATH, .name = "expr", .optional = false,
756       .help = "the path expression" },
757     CMD_OPT_DEF_LAST
758 };
759
760 static const struct command_def cmd_defvar_def = {
761     .name = "defvar",
762     .opts = cmd_defvar_opts,
763     .handler = cmd_defvar,
764     .synopsis = "set a variable",
765     .help = "Evaluate EXPR and set the variable NAME to the resulting "
766     "nodeset. The\n variable can be used in path expressions as $NAME.  "
767     "Note that EXPR is\n evaluated when the variable is defined, not when "
768     "it is used."
769 };
770
771 static void cmd_defnode(struct command *cmd) {
772     const char *name = arg_value(cmd, "name");
773     const char *path = arg_value(cmd, "expr");
774     const char *value = arg_value(cmd, "value");
775
776     /* Make 'defnode foo ""' mean the same as 'defnode foo' */
777     if (value != NULL && strlen(value) == 0)
778         value = NULL;
779     aug_defnode(cmd->aug, name, path, value, NULL);
780 }
781
782 static const struct command_opt_def cmd_defnode_opts[] = {
783     { .type = CMD_STR, .name = "name", .optional = false,
784       .help = "the name of the variable" },
785     { .type = CMD_PATH, .name = "expr", .optional = false,
786       .help = "the path expression" },
787     { .type = CMD_STR, .name = "value", .optional = true,
788       .help = "the value for the new node" },
789     CMD_OPT_DEF_LAST
790 };
791
792 static const struct command_def cmd_defnode_def = {
793     .name = "defnode",
794     .opts = cmd_defnode_opts,
795     .handler = cmd_defnode,
796     .synopsis = "set a variable, possibly creating a new node",
797     .help = "Define the variable NAME to the result of evaluating EXPR, "
798     " which must\n be a nodeset.  If no node matching EXPR exists yet,  one "
799     "is created and\n NAME will refer to it.   When a node is created and "
800     "VALUE is given, the\n new node's value is set to VALUE."
801 };
802
803 static void cmd_clear(struct command *cmd) {
804     const char *path = arg_value(cmd, "path");
805     int r;
806
807     r = aug_set(cmd->aug, path, NULL);
808     if (r < 0)
809         ERR_REPORT(cmd, AUG_ECMDRUN, "Clearing %s failed", path);
810 }
811
812 static const struct command_opt_def cmd_clear_opts[] = {
813     { .type = CMD_PATH, .name = "path", .optional = false,
814       .help = "clear the value of this node" },
815     CMD_OPT_DEF_LAST
816 };
817
818 static const struct command_def cmd_clear_def = {
819     .name = "clear",
820     .opts = cmd_clear_opts,
821     .handler = cmd_clear,
822     .synopsis = "clear the value of a node",
823     .help = "Set the value for PATH to NULL. If PATH is not in the tree yet, "
824     "it and\n all its ancestors will be created.  These new tree entries "
825     "will appear\n last amongst their siblings"
826 };
827
828 static void cmd_touch(struct command *cmd) {
829     const char *path = arg_value(cmd, "path");
830     int r;
831
832     r = aug_match(cmd->aug, path, NULL);
833     if (r == 0) {
834         r = aug_set(cmd->aug, path, NULL);
835         if (r < 0)
836             ERR_REPORT(cmd, AUG_ECMDRUN, "Touching %s failed", path);
837     }
838 }
839
840 static const struct command_opt_def cmd_touch_opts[] = {
841     { .type = CMD_PATH, .name = "path", .optional = false,
842       .help = "touch this node" },
843     CMD_OPT_DEF_LAST
844 };
845
846 static const struct command_def cmd_touch_def = {
847     .name = "touch",
848     .opts = cmd_touch_opts,
849     .handler = cmd_touch,
850     .synopsis = "create a new node",
851     .help = "Create PATH with the value NULL if it is not in the tree yet.  "
852     "All its\n ancestors will also be created.  These new tree entries will "
853     "appear\n last amongst their siblings."
854 };
855
856 static void cmd_get(struct command *cmd) {
857     const char *path = arg_value(cmd, "path");
858     const char *val;
859     int r;
860
861     r = aug_get(cmd->aug, path, &val);
862     ERR_RET(cmd);
863     fprintf(cmd->out, "%s", path);
864     if (r == 0) {
865         fprintf(cmd->out, " (o)\n");
866     } else if (val == NULL) {
867         fprintf(cmd->out, " (none)\n");
868     } else {
869         fprintf(cmd->out, " = %s\n", val);
870     }
871 }
872
873 static const struct command_opt_def cmd_get_opts[] = {
874     { .type = CMD_PATH, .name = "path", .optional = false,
875       .help = "get the value of this node" },
876     CMD_OPT_DEF_LAST
877 };
878
879 static const struct command_def cmd_get_def = {
880     .name = "get",
881     .opts = cmd_get_opts,
882     .handler = cmd_get,
883     .synopsis = "get the value of a node",
884     .help = "Get and print the value associated with PATH"
885 };
886
887 static void cmd_label(struct command *cmd) {
888     const char *path = arg_value(cmd, "path");
889     const char *lbl;
890     int r;
891
892     r = aug_label(cmd->aug, path, &lbl);
893     ERR_RET(cmd);
894     fprintf(cmd->out, "%s", path);
895     if (r == 0) {
896         fprintf(cmd->out, " (o)\n");
897     } else if (lbl == NULL) {
898         fprintf(cmd->out, " (none)\n");
899     } else {
900         fprintf(cmd->out, " = %s\n", lbl);
901     }
902 }
903
904 static const struct command_opt_def cmd_label_opts[] = {
905     { .type = CMD_PATH, .name = "path", .optional = false,
906       .help = "get the label of this node" },
907     CMD_OPT_DEF_LAST
908 };
909
910 static const struct command_def cmd_label_def = {
911     .name = "label",
912     .opts = cmd_label_opts,
913     .handler = cmd_label,
914     .synopsis = "get the label of a node",
915     .help = "Get and print the label associated with PATH"
916 };
917
918 static void cmd_print(struct command *cmd) {
919     const char *path = arg_value(cmd, "path");
920
921     aug_print(cmd->aug, cmd->out, path);
922 }
923
924 static const struct command_opt_def cmd_print_opts[] = {
925     { .type = CMD_PATH, .name = "path", .optional = true,
926       .help = "print this subtree" },
927     CMD_OPT_DEF_LAST
928 };
929
930 static const struct command_def cmd_print_def = {
931     .name = "print",
932     .opts = cmd_print_opts,
933     .handler = cmd_print,
934     .synopsis = "print a subtree",
935     .help = "Print entries in the tree.  If PATH is given, printing starts there,\n otherwise the whole tree is printed"
936 };
937
938 static void cmd_dump_xml(struct command *cmd) {
939     const char *path = arg_value(cmd, "path");
940     xmlNodePtr xmldoc;
941     int r;
942
943     r = aug_to_xml(cmd->aug, path, &xmldoc, 0);
944     if (r < 0)
945         ERR_REPORT(cmd, AUG_ECMDRUN,
946                    "XML export of path %s failed", path);
947
948     xmlElemDump(stdout, NULL, xmldoc);
949     printf("\n");
950
951     xmlFreeNode(xmldoc);
952 }
953
954 static const struct command_opt_def cmd_dump_xml_opts[] = {
955     { .type = CMD_PATH, .name = "path", .optional = true,
956       .help = "print this subtree" },
957     CMD_OPT_DEF_LAST
958 };
959
960 static const struct command_def cmd_dump_xml_def = {
961     .name = "dump-xml",
962     .opts = cmd_dump_xml_opts,
963     .handler = cmd_dump_xml,
964     .synopsis = "print a subtree as XML",
965     .help = "Export entries in the tree as XML. If PATH is given, printing starts there,\n otherwise the whole tree is printed."
966 };
967
968 static void cmd_transform(struct command *cmd) {
969     const char *lens = arg_value(cmd, "lens");
970     const char *filter = arg_value(cmd, "filter");
971     const char *file = arg_value(cmd, "file");
972     int r, excl = 0;
973
974     if (STREQ("excl", filter))
975         excl = 1;
976     else if (STREQ("incl", filter))
977         excl = 0;
978     else
979         ERR_REPORT(cmd, AUG_ECMDRUN,
980                    "FILTER must be \"incl\" or \"excl\"");
981
982     r = aug_transform(cmd->aug, lens, file, excl);
983     if (r < 0)
984         ERR_REPORT(cmd, AUG_ECMDRUN,
985                    "Adding transform for %s on lens %s failed", lens, file);
986 }
987
988 static const struct command_opt_def cmd_transform_opts[] = {
989     { .type = CMD_PATH, .name = "lens", .optional = false,
990       .help = "the lens to use" },
991     { .type = CMD_PATH, .name = "filter", .optional = false,
992       .help = "the type of filter, either \"incl\" or \"excl\"" },
993     { .type = CMD_PATH, .name = "file", .optional = false,
994       .help = "the file to associate to the lens" },
995     CMD_OPT_DEF_LAST
996 };
997
998 static const char const cmd_transform_help[] =
999     "Add a transform for FILE using LENS. The LENS may be a module name or a\n"
1000     " full lens name.  If a module name is given, then \"lns\" will be the lens\n"
1001     " assumed.  The FILTER must be either \"incl\" or \"excl\".  If the filter is\n"
1002     " \"incl\",  the FILE will be parsed by the LENS.  If the filter is \"excl\",\n"
1003     " the FILE will be excluded from the LENS. FILE may contain wildcards." ;
1004
1005 static const struct command_def cmd_transform_def = {
1006     .name = "transform",
1007     .opts = cmd_transform_opts,
1008     .handler = cmd_transform,
1009     .synopsis = "add a file transform",
1010     .help = cmd_transform_help
1011 };
1012
1013 static void cmd_load_file(struct command *cmd) {
1014     const char *file = arg_value(cmd, "file");
1015     int r = 0;
1016
1017     r = aug_load_file(cmd->aug, file);
1018     if (r < 0)
1019       ERR_REPORT(cmd, AUG_ECMDRUN,
1020           "Failed to load file %s", file);
1021 }
1022
1023 static const struct command_opt_def cmd_load_file_opts[] = {
1024     { .type = CMD_PATH, .name = "file", .optional = false,
1025       .help = "the file to load" },
1026     CMD_OPT_DEF_LAST
1027 };
1028
1029 static const char const cmd_load_file_help[] =
1030     "Load a specific FILE, using autoload statements.\n";
1031
1032 static const struct command_def cmd_load_file_def = {
1033     .name = "load-file",
1034     .opts = cmd_load_file_opts,
1035     .handler = cmd_load_file,
1036     .synopsis = "load a specific file",
1037     .help = cmd_load_file_help
1038 };
1039
1040 static void cmd_save(struct command *cmd) {
1041     int r;
1042     r = aug_save(cmd->aug);
1043     if (r == -1) {
1044         ERR_REPORT(cmd, AUG_ECMDRUN,
1045                    "saving failed (run 'errors' for details)");
1046     } else {
1047         r = aug_match(cmd->aug, "/augeas/events/saved", NULL);
1048         if (r > 0) {
1049             fprintf(cmd->out, "Saved %d file(s)\n", r);
1050         }
1051     }
1052 }
1053
1054 static const struct command_opt_def cmd_save_opts[] = {
1055     CMD_OPT_DEF_LAST
1056 };
1057
1058 static const struct command_def cmd_save_def = {
1059     .name = "save",
1060     .opts = cmd_save_opts,
1061     .handler = cmd_save,
1062     .synopsis = "save all pending changes",
1063     .help = "Save all pending changes to disk. How exactly that is done depends on\n the value of the node /augeas/save, which can be changed by the user.\n The possible values for it are\n \n   noop      - do not write files; useful for finding errors that\n               might happen during a save\n   backup    - save the original file in a file by appending the extension\n               '.augsave' and overwrite the original with new content\n   newfile   - leave the original file untouched and write new content to\n               a file with extension '.augnew' next to the original file\n   overwrite - overwrite the original file with new content\n \n Save always tries to save all files for which entries in the tree have\n changed. When saving fails, some files will be written.  Details about\n why a save failed can by found by running the 'errors' command"
1064 };
1065
1066 static void cmd_load(struct command *cmd) {
1067     int r;
1068     r = aug_load(cmd->aug);
1069     if (r == -1) {
1070         ERR_REPORT(cmd, AUG_ECMDRUN,
1071                    "loading failed (run 'errors' for details)");
1072     }
1073 }
1074
1075 static const struct command_opt_def cmd_load_opts[] = {
1076     CMD_OPT_DEF_LAST
1077 };
1078
1079 static const struct command_def cmd_load_def = {
1080     .name = "load",
1081     .opts = cmd_load_opts,
1082     .handler = cmd_load,
1083     .synopsis = "(re)load files under /files",
1084     .help = "Load files  according to the  transforms in /augeas/load.  "
1085     "A transform\n Foo  is  represented  with  a  subtree  /augeas/load/Foo."
1086     "   Underneath\n /augeas/load/Foo, one node labelled  'lens' must exist,"
1087     " whose value is\n the  fully  qualified name  of  a  lens,  for example  "
1088     "'Foo.lns',  and\n multiple nodes 'incl' and 'excl' whose values are "
1089     "globs that determine\n which files are  transformed by that lens. It "
1090     "is an  error if one file\n can be processed by multiple transforms."
1091 };
1092
1093 static void cmd_ins(struct command *cmd) {
1094     const char *label = arg_value(cmd, "label");
1095     const char *where = arg_value(cmd, "where");
1096     const char *path = arg_value(cmd, "path");
1097     int before;
1098
1099     if (STREQ(where, "after"))
1100         before = 0;
1101     else if (STREQ(where, "before"))
1102         before = 1;
1103     else {
1104         ERR_REPORT(cmd, AUG_ECMDRUN,
1105           "the <WHERE> argument for ins must be either 'before' or 'after'.");
1106         return;
1107     }
1108
1109     aug_insert(cmd->aug, path, label, before);
1110 }
1111
1112 static const struct command_opt_def cmd_ins_opts[] = {
1113     { .type = CMD_STR, .name = "label", .optional = false,
1114       .help = "the label for the new node" },
1115     { .type = CMD_STR, .name = "where", .optional = false,
1116       .help = "either 'before' or 'after'" },
1117     { .type = CMD_PATH, .name = "path", .optional = false,
1118       .help = "the node before/after which to insert" },
1119     CMD_OPT_DEF_LAST
1120 };
1121
1122 static const char const cmd_ins_help[] =
1123     "Insert a new node with label LABEL right before or after "
1124     "PATH into the\n tree. WHERE must be either 'before' or 'after'.";
1125
1126 static const struct command_def cmd_ins_def = {
1127     .name = "ins",
1128     .opts = cmd_ins_opts,
1129     .handler = cmd_ins,
1130     .synopsis = "insert new node",
1131     .help = cmd_ins_help
1132 };
1133
1134 static const struct command_def cmd_insert_def = {
1135     .name = "insert",
1136     .opts = cmd_ins_opts,
1137     .handler = cmd_ins,
1138     .synopsis = "insert new node (alias of 'ins')",
1139     .help = cmd_ins_help
1140 };
1141
1142 static void cmd_store(struct command *cmd) {
1143     const char *lens = arg_value(cmd, "lens");
1144     const char *path = arg_value(cmd, "path");
1145     const char *node = arg_value(cmd, "node");
1146
1147     aug_text_store(cmd->aug, lens, node, path);
1148 }
1149
1150 static const struct command_opt_def cmd_store_opts[] = {
1151     { .type = CMD_STR, .name = "lens", .optional = false,
1152       .help = "the name of the lens" },
1153     { .type = CMD_PATH, .name = "node", .optional = false,
1154       .help = "where to find the input text" },
1155     { .type = CMD_PATH, .name = "path", .optional = false,
1156       .help = "where to store parsed text" },
1157     CMD_OPT_DEF_LAST
1158 };
1159
1160 static const char const cmd_store_help[] =
1161     "Parse NODE using LENS and store the resulting tree at PATH.";
1162
1163 static const struct command_def cmd_store_def = {
1164     .name = "store",
1165     .opts = cmd_store_opts,
1166     .handler = cmd_store,
1167     .synopsis = "parse text into tree",
1168     .help = cmd_store_help
1169 };
1170
1171 static void cmd_retrieve(struct command *cmd) {
1172     const char *lens = arg_value(cmd, "lens");
1173     const char *node_in = arg_value(cmd, "node_in");
1174     const char *path = arg_value(cmd, "path");
1175     const char *node_out = arg_value(cmd, "node_out");
1176
1177     aug_text_retrieve(cmd->aug, lens, node_in, path, node_out);
1178 }
1179
1180 static const struct command_opt_def cmd_retrieve_opts[] = {
1181     { .type = CMD_STR, .name = "lens", .optional = false,
1182       .help = "the name of the lens" },
1183     { .type = CMD_PATH, .name = "node_in", .optional = false,
1184       .help = "the node containing the initial text (path expression)" },
1185     { .type = CMD_PATH, .name = "path", .optional = false,
1186       .help = "the tree to transform (path expression)" },
1187     { .type = CMD_PATH, .name = "node_out", .optional = false,
1188       .help = "where to store the resulting text (path expression)" },
1189     CMD_OPT_DEF_LAST
1190 };
1191
1192 static const char const cmd_retrieve_help[] =
1193     "Transform tree at PATH back into text using lens LENS and store the\n"
1194     " resulting string at NODE_OUT. Assume that the tree was initially read in\n"
1195     " with the same lens and the string stored at NODE_IN as input.";
1196
1197 static const struct command_def cmd_retrieve_def = {
1198     .name = "retrieve",
1199     .opts = cmd_retrieve_opts,
1200     .handler = cmd_retrieve,
1201     .synopsis = "transform tree into text",
1202     .help = cmd_retrieve_help
1203 };
1204
1205 /* Given a path "/augeas/files/FILENAME/error", return FILENAME */
1206 static char *err_filename(const char *match) {
1207     int noise = strlen(AUGEAS_META_FILES) + strlen("/error");
1208     if (strlen(match) < noise + 1)
1209         goto error;
1210     return strndup(match + strlen(AUGEAS_META_FILES), strlen(match) - noise);
1211  error:
1212     return strdup("(no filename)");
1213 }
1214
1215 static const char *err_get(struct augeas *aug,
1216                            const char *match, const char *child) {
1217     char *path = NULL;
1218     const char *value = "";
1219     int r;
1220
1221     r = pathjoin(&path, 2, match, child);
1222     ERR_NOMEM(r < 0, aug);
1223
1224     aug_get(aug, path, &value);
1225     ERR_BAIL(aug);
1226
1227  error:
1228     free(path);
1229     return value;
1230 }
1231
1232 static void cmd_errors(struct command *cmd) {
1233     char **matches = NULL;
1234     int cnt = 0;
1235     char *filename = NULL;
1236     struct augeas *aug = cmd->aug;
1237
1238     cnt = aug_match(aug, "/augeas//error", &matches);
1239     ERR_BAIL(cmd);
1240     ERR_THROW(cnt < 0, aug, AUG_ECMDRUN,
1241               "  (problem retrieving error messages)\n");
1242     if (cnt == 0) {
1243         fprintf(cmd->out, "  (no errors)\n");
1244         goto done;
1245     }
1246
1247     for (int i=0; i < cnt; i++) {
1248         const char *match = matches[i];
1249         const char *line  = err_get(aug, match, "line");
1250         const char *char_pos = err_get(aug, match, "char");
1251         const char *lens     = err_get(aug, match, "lens");
1252         const char *last     = err_get(aug, match, "lens/last_matched");
1253         const char *next     = err_get(aug, match, "lens/next_not_matched");
1254         const char *msg      = err_get(aug, match, "message");
1255         const char *path     = err_get(aug, match, "path");
1256         const char *kind     = NULL;
1257
1258         aug_get(aug, match, &kind);
1259         ERR_BAIL(aug);
1260
1261         filename = err_filename(match);
1262         ERR_NOMEM(filename == NULL, aug);
1263
1264         if (i>0)
1265             fprintf(cmd->out, "\n");
1266
1267         if (line != NULL) {
1268             fprintf(cmd->out, "Error in %s:%s.%s (%s)\n",
1269                     filename, line, char_pos, kind);
1270         } else if (path != NULL) {
1271             fprintf(cmd->out, "Error in %s at node %s (%s)\n", filename, path, kind);
1272         } else {
1273             fprintf(cmd->out, "Error in %s (%s)\n", filename, kind);
1274         }
1275
1276         if (msg != NULL)
1277             fprintf(cmd->out, "  %s\n", msg);
1278         if (lens != NULL)
1279             fprintf(cmd->out, "  Lens: %s\n", lens);
1280         if (last != NULL)
1281             fprintf(cmd->out, "    Last matched: %s\n", last);
1282         if (next != NULL)
1283             fprintf(cmd->out, "    Next (no match): %s\n", next);
1284     }
1285
1286  done:
1287  error:
1288     for (int i=0; i < cnt; i++)
1289         free(matches[i]);
1290     free(matches);
1291     free(filename);
1292 }
1293
1294 static const struct command_opt_def cmd_errors_opts[] = {
1295     CMD_OPT_DEF_LAST
1296 };
1297
1298 static const char const cmd_errors_help[] =
1299     "Show all the errors encountered in processing files. For each error,\n"
1300     " print detailed information about where it happened and how. The same\n"
1301     " information can be retrieved by running 'print /augeas//error'\n\n"
1302     " For each error, the file in which the error occurred together with the\n"
1303     " line number and position in that line is shown, as well as information\n"
1304     " about the lens that encountered the error. For some errors, the last\n"
1305     " lens that matched successfully and the next lens that should have\n"
1306     " matched but didn't are also shown\n";
1307
1308 static const struct command_def cmd_errors_def = {
1309     .name = "errors",
1310     .opts = cmd_errors_opts,
1311     .handler = cmd_errors,
1312     .synopsis = "show all errors encountered in processing files",
1313     .help = cmd_errors_help
1314 };
1315
1316 /* Groups of commands */
1317 static const struct command_grp_def cmd_grp_admin_def = {
1318     .name = "Admin",
1319     .commands = {
1320         &cmd_help_def,
1321         &cmd_load_def,
1322         &cmd_quit_def,
1323         &cmd_retrieve_def,
1324         &cmd_save_def,
1325         &cmd_store_def,
1326         &cmd_transform_def,
1327         &cmd_load_file_def,
1328         &cmd_def_last
1329     }
1330 };
1331
1332 static const struct command_grp_def cmd_grp_read_def = {
1333     .name = "Read",
1334     .commands = {
1335         &cmd_dump_xml_def,
1336         &cmd_get_def,
1337         &cmd_label_def,
1338         &cmd_ls_def,
1339         &cmd_match_def,
1340         &cmd_print_def,
1341         &cmd_errors_def,
1342         &cmd_span_def,
1343         &cmd_def_last
1344     }
1345 };
1346
1347 static const struct command_grp_def cmd_grp_write_def = {
1348     .name = "Write",
1349     .commands = {
1350         &cmd_clear_def,
1351         &cmd_clearm_def,
1352         &cmd_ins_def,
1353         &cmd_insert_def,
1354         &cmd_mv_def,
1355         &cmd_move_def,
1356         &cmd_cp_def,
1357         &cmd_copy_def,
1358         &cmd_rename_def,
1359         &cmd_rm_def,
1360         &cmd_set_def,
1361         &cmd_setm_def,
1362         &cmd_touch_def,
1363         &cmd_def_last
1364     }
1365 };
1366
1367 static const struct command_grp_def cmd_grp_pathx_def = {
1368     .name = "Path expression",
1369     .commands = {
1370         &cmd_defnode_def,
1371         &cmd_defvar_def,
1372         &cmd_def_last
1373     }
1374 };
1375
1376 static const struct command_grp_def const *cmd_groups[] = {
1377     &cmd_grp_admin_def,
1378     &cmd_grp_read_def,
1379     &cmd_grp_write_def,
1380     &cmd_grp_pathx_def,
1381     &cmd_grp_def_last
1382 };
1383
1384 static const struct command_def *lookup_cmd_def(const char *name) {
1385     for (int i = 0; cmd_groups[i]->name != NULL; i++) {
1386         for (int j = 0; cmd_groups[i]->commands[j]->name != NULL; j++) {
1387             if (STREQ(name, cmd_groups[i]->commands[j]->name))
1388                 return cmd_groups[i]->commands[j];
1389         }
1390     }
1391     return NULL;
1392 }
1393
1394 static void cmd_help(struct command *cmd) {
1395     const char *name = arg_value(cmd, "command");
1396     char buf[100];
1397
1398     if (name == NULL) {
1399         //fprintf(cmd->out, "Commands:\n\n");
1400         fprintf(cmd->out, "\n");
1401         for (int i=0; cmd_groups[i]->name != NULL; i++) {
1402             fprintf(cmd->out, "%s commands:\n", cmd_groups[i]->name);
1403             for (int j=0; cmd_groups[i]->commands[j]->name != NULL; j++) {
1404                 const struct command_def *def = cmd_groups[i]->commands[j];
1405                 fprintf(cmd->out, "  %-10s - %s\n", def->name, def->synopsis);
1406             }
1407             fprintf(cmd->out, "\n");
1408         }
1409         fprintf(cmd->out,
1410            "Type 'help <command>' for more information on a command\n\n");
1411     } else {
1412         const struct command_def *def = lookup_cmd_def(name);
1413         const struct command_opt_def *odef = NULL;
1414
1415         ERR_THROW(def == NULL, cmd->aug, AUG_ECMDRUN,
1416                   "unknown command %s\n", name);
1417         fprintf(cmd->out, "  COMMAND\n");
1418         fprintf(cmd->out, "    %s - %s\n\n", name, def->synopsis);
1419         fprintf(cmd->out, "  SYNOPSIS\n");
1420         fprintf(cmd->out, "    %s", name);
1421
1422         for (odef = def->opts; odef->name != NULL; odef++) {
1423             format_defname(buf, odef, true);
1424             fprintf(cmd->out, "%s", buf);
1425         }
1426         fprintf(cmd->out, "\n\n");
1427         fprintf(cmd->out, "  DESCRIPTION\n");
1428         format_desc(def->help);
1429         if (def->opts->name != NULL) {
1430             fprintf(cmd->out, "  OPTIONS\n");
1431             for (odef = def->opts; odef->name != NULL; odef++) {
1432                 const char *help = odef->help;
1433                 if (help == NULL)
1434                     help = "";
1435                 format_defname(buf, odef, false);
1436                 fprintf(cmd->out, "    %-10s %s\n", buf, help);
1437             }
1438         }
1439         fprintf(cmd->out, "\n");
1440     }
1441  error:
1442     return;
1443 }
1444
1445 int aug_srun(augeas *aug, FILE *out, const char *text) {
1446     char *line = NULL;
1447     const char *eol;
1448     struct command cmd;
1449     int result = 0;
1450
1451     api_entry(aug);
1452
1453     if (text == NULL)
1454         goto done;
1455
1456     MEMZERO(&cmd, 1);
1457     cmd.aug = aug;
1458     cmd.error = aug->error;
1459     cmd.out = out;
1460
1461     while (*text != '\0' && result >= 0) {
1462         eol = strchrnul(text, '\n');
1463         while (isspace(*text) && text < eol) text++;
1464         if (*text == '\0')
1465             break;
1466         if (*text == '#' || text == eol) {
1467             text = (*eol == '\0') ? eol : eol + 1;
1468             continue;
1469         }
1470
1471         line = strndup(text, eol - text);
1472         ERR_NOMEM(line == NULL, aug);
1473
1474         if (parseline(&cmd, line) == 0) {
1475             cmd.def->handler(&cmd);
1476             result += 1;
1477         } else {
1478             result = -1;
1479         }
1480
1481         ERR_BAIL(aug);
1482         if (result >= 0 && cmd.quit) {
1483             result = -2;
1484             goto done;
1485         }
1486
1487         free_command_opts(&cmd);
1488         FREE(line);
1489         text = (*eol == '\0') ? eol : eol + 1;
1490     }
1491  done:
1492     FREE(line);
1493
1494     api_exit(aug);
1495     return result;
1496  error:
1497     result = -1;
1498     goto done;
1499 }
1500
1501 /*
1502  * Local variables:
1503  *  indent-tabs-mode: nil
1504  *  c-indent-level: 4
1505  *  c-basic-offset: 4
1506  *  tab-width: 4
1507  * End:
1508  */