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