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