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