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 *
find_def(const struct command * cmd,const char * name)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 *
find_opt(const struct command * cmd,const char * name)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 
arg_value(const struct command * cmd,const char * name)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 
nexttoken(struct command * cmd,char ** line,bool path)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 *
make_command_opt(struct command * cmd,const struct command_opt_def * def)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 
free_command_opts(struct command * cmd)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 
parseline(struct command * cmd,char * line)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  */
format_desc(const char * d)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 
format_defname(char * buf,const struct command_opt_def * def,bool mark_optional)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 
cmd_quit(ATTRIBUTE_UNUSED struct command * cmd)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 
ls_pattern(struct command * cmd,const char * path)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 
child_count(struct command * cmd,const char * path)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 
cmd_ls(struct command * cmd)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 
cmd_match(struct command * cmd)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 
cmd_count(struct command * cmd)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 
cmd_rm(struct command * cmd)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 
cmd_mv(struct command * cmd)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 
cmd_cp(struct command * cmd)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 
cmd_rename(struct command * cmd)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 
cmd_set(struct command * cmd)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 
cmd_setm(struct command * cmd)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 
cmd_clearm(struct command * cmd)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 
cmd_span(struct command * cmd)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 
cmd_defvar(struct command * cmd)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 
cmd_defnode(struct command * cmd)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 
cmd_clear(struct command * cmd)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 
cmd_touch(struct command * cmd)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 
cmd_get(struct command * cmd)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 
cmd_label(struct command * cmd)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 
cmd_print(struct command * cmd)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 
cmd_source(struct command * cmd)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 
cmd_context(struct command * cmd)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 
cmd_dump_xml(struct command * cmd)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 
cmd_transform(struct command * cmd)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 
cmd_load_file(struct command * cmd)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 
cmd_save(struct command * cmd)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 
cmd_load(struct command * cmd)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 
cmd_info(struct command * cmd)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 
cmd_ins(struct command * cmd)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 
cmd_store(struct command * cmd)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 
cmd_retrieve(struct command * cmd)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 */
err_filename(const char * match)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 
err_get(struct augeas * aug,const char * match,const char * child)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 
cmd_errors(struct command * cmd)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 
lookup_cmd_def(const char * name)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 
cmd_help(struct command * cmd)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 
aug_srun(augeas * aug,FILE * out,const char * text)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  */
1672