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