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