Bump to 1.14.1
[platform/upstream/augeas.git] / src / augmatch.c
1 /*
2  * augrp.c: utility for printing and reading files as parsed by Augeas
3  *
4  * Copyright (C) 2007-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 <dlutter@redhat.com>
21  */
22
23 #include <config.h>
24 #include <argz.h>
25 #include <getopt.h>
26 #include <stdbool.h>
27 #include <ctype.h>
28 #include <libgen.h>
29
30 #include "memory.h"
31 #include "augeas.h"
32 #include <locale.h>
33
34 #define EXIT_TROUBLE 2
35
36 #define cleanup(_x) __attribute__((__cleanup__(_x)))
37
38 const char *progname;
39 bool print_all = false;
40 bool print_only_values = false;
41 bool print_exact = false;
42
43 static void freep(void *p) {
44     free(*(void **)p);
45 }
46
47 static void aug_closep(struct augeas **p) {
48     aug_close(*p);
49 }
50
51 __attribute__((noreturn))
52 static void usage(void) {
53     fprintf(stderr, "Usage: %s [OPTIONS] FILE\n", progname);
54     fprintf(stderr,
55 "Print the contents of a file as parsed by augeas.\n\n"
56 "Options:\n\n"
57 "  -l, --lens LENS    use LENS to transform the file\n"
58 "  -L, --print-lens   print the lens that will be used for a file an exit\n"
59 "  -a, --all          print all nodes, even ones without a value\n"
60 "  -m, --match EXPR   start printing where nodes match EXPR\n"
61 "  -e, --exact        print only exact matches instead of the entire tree\n"
62 "                     starting at a match\n"
63 "  -o, --only-value   print only the values of tree nodes, but no path\n"
64 "  -q, --quiet        do not print anything. Exit with zero status if a\n"
65 "                     match was found\n"
66 "  -r, --root ROOT    use ROOT as the root of the filesystem\n"
67 "  -I, --include DIR  search DIR for modules; can be given mutiple times\n"
68 "  -S, --nostdinc     do not search the builtin default directories\n"
69 "                     for modules\n\n"
70 "Examples:\n\n"
71 "  Print how augeas sees /etc/exports:\n"
72 "    augmatch /etc/exports\n\n"
73 "  Show only the entry for a specific mount:\n"
74 "    augmatch -m 'dir[\"/home\"]' /etc/exports\n\n"
75 "  Show all the clients to which we are exporting /home:\n"
76 "    augmatch -eom 'dir[\"/home\"]/client' /etc/exports\n\n");
77     exit(EXIT_SUCCESS);
78 }
79
80 /* Exit with a failure if COND is true. Use FORMAT and the remaining
81  * arguments like printf to print an error message on stderr before
82  * exiting */
83 static void die(bool cond, const char *format, ...) {
84     if (cond) {
85         fputs("error: ", stderr);
86         va_list args;
87         va_start(args, format);
88         vfprintf(stderr, format, args);
89         va_end(args);
90         exit(EXIT_TROUBLE);
91     }
92 }
93
94 static void oom_when(bool cond) {
95     die(cond, "out of memory.\n");
96 }
97
98 /* Format a string with vasprintf and return it. The caller is responsible
99  * for freeing the result. */
100 static char *format(const char *format, ...) {
101   va_list args;
102   char *result;
103   int r;
104
105   va_start(args, format);
106   r = vasprintf(&result, format, args);
107   va_end(args);
108   oom_when(r < 0);
109   return result;
110 }
111
112 /* Check for an Augeas error. If there is one, print it and all its detail
113  * and exit with failure. If there is no error, do nothing */
114 static void check_error(struct augeas *aug) {
115     die(aug == NULL, "could not initialize augeas\n");
116     oom_when(aug_error(aug) == AUG_ENOMEM);
117     if (aug_error(aug) != AUG_NOERROR) {
118         fprintf(stderr, "error: %s\n", aug_error_message(aug));
119         const char *msg = aug_error_minor_message(aug);
120         if (msg != NULL) {
121             fprintf(stderr, "%s\n", msg);
122         }
123         msg = aug_error_details(aug);
124         if (msg != NULL) {
125             fprintf(stderr, "%s\n", msg);
126         }
127         exit(EXIT_TROUBLE);
128     }
129 }
130
131 /* Check for an error trying to load FILE (e.g., a parse error) If there
132  * was one, print details and exit with failure. If there was none, do
133  * nothing. */
134 static void check_load_error(struct augeas *aug, const char *file) {
135     char *info = format("/augeas/files%s", file);
136     const char *msg, *line, *col;
137
138     aug_defvar(aug, "info", info);
139     free(info);
140     die(aug_ns_count(aug, "info") == 0, "file %s does not exist\n", file);
141
142     aug_defvar(aug, "error", "$info/error");
143     if (aug_ns_count(aug, "error") == 0)
144         return;
145
146     aug_get(aug, "$error", &msg);
147     aug_get(aug, "$error/line", &line);
148     aug_get(aug, "$error/char", &col);
149
150     if (streqv(msg, "parse_failed")) {
151         msg = "parsing failed";
152     } else if (streqv(msg, "read_failed")) {
153         aug_get(aug, "$error/message", &msg);
154     }
155
156     if ((line != NULL) && (col != NULL)) {
157         fprintf(stderr, "error reading %s: %s on line %s, column %s\n",
158                 file, msg, line, col);
159     } else {
160         fprintf(stderr, "error reading %s: %s\n", file, msg);
161     }
162     exit(EXIT_TROUBLE);
163 }
164
165 /* We keep track of where we are in the tree when we are printing it by
166  * assigning to augeas variables and using one struct node for each level
167  * in the tree. To keep things simple, we just preallocate a lot of them
168  * (up to max_nodes many). If we ever have a tree deeper than this, we are
169  * in trouble and will simply abort the program. */
170 static const size_t max_nodes = 256;
171
172 struct node {
173     char *var;         /* The variable where we store the nodes for this level */
174     const char *label; /* The label, index, and value of the current node */
175     int   index;       /* at the level that this struct node is for */
176     const char *value;
177 };
178
179 /* Print information about NODES[LEVEL] by printing the path to it going
180  * from NODES[0] to NODES[LEVEL]. PREFIX is the path from the start of the
181  * file to the current match (if the user specified --match) or the empty
182  * string (if we are printing the entire file. */
183 static void print_one(int level, const char *prefix, struct node *nodes) {
184     if (nodes[level].value == NULL && ! print_all)
185         return;
186
187     if (print_only_values && nodes[level].value != NULL) {
188         printf("%s\n", nodes[level].value);
189         return;
190     }
191
192     if (*prefix) {
193         if (level > 0)
194             printf("%s/", prefix);
195         else
196             printf("%s", prefix);
197     }
198
199     for (int i=1; i <= level; i++) {
200         if (nodes[i].index > 0) {
201             printf("%s[%d]", nodes[i].label, nodes[i].index);
202         } else {
203             printf("%s", nodes[i].label);
204         }
205         if (i < level) {
206             printf("/");
207         }
208     }
209
210     if (nodes[level].value) {
211         printf(" = %s\n", nodes[level].value);
212     } else {
213         printf("\n");
214     }
215 }
216
217 /* Recursively print the tree starting at NODES[LEVEL] */
218 static void print_tree(struct augeas *aug, int level,
219                        const char *prefix, struct node *nodes) {
220     die(level + 1 >= max_nodes,
221         "tree has more than %d levels, which is more than we can handle\n",
222         max_nodes);
223
224     struct node *cur = nodes + level;
225     struct node *next = cur + 1;
226
227     int count = aug_ns_count(aug, cur->var);
228     for (int i=0; i < count; i++) {
229         cleanup(freep) char *pattern = NULL;
230
231         aug_ns_label(aug, cur->var, i, &(cur->label), &(cur->index));
232         aug_ns_value(aug, cur->var, i, &(cur->value));
233         print_one(level, prefix, nodes);
234
235         if (! print_exact) {
236             pattern = format("$%s[%d]/*", cur->var, i+1);
237             aug_defvar(aug, next->var, pattern);
238             check_error(aug);
239             print_tree(aug, level+1, prefix, nodes);
240         }
241     }
242 }
243
244 /* Print the tree for file PATH (which must already start with /files), but
245  * only the nodes matching MATCH.
246  *
247  * Return EXIT_SUCCESS if there was at least one match, and EXIT_FAILURE
248  * if there was none.
249  */
250 static int print(struct augeas *aug, const char *path, const char *match) {
251     static const char *const match_var = "match";
252
253     struct node *nodes = NULL;
254
255     nodes = calloc(max_nodes, sizeof(struct node));
256     oom_when(nodes == NULL);
257
258     for (int i=0; i < max_nodes; i++) {
259         nodes[i].var = format("var%d", i);
260     }
261
262     /* Set $match to the nodes matching the user's match expression */
263     aug_defvar(aug, match_var, match);
264     check_error(aug);
265
266     /* Go through the matches in MATCH_VAR one by one. We need to do it
267      * this way, since the prefix we need to print for each entry in
268      * MATCH_VAR is different for each entry. */
269     int count = aug_ns_count(aug, match_var);
270     for (int i=0; i < count; i++) {
271         cleanup(freep) char *prefix = NULL;
272         aug_ns_path(aug, match_var, i, &prefix);
273         aug_defvar(aug, nodes[0].var, prefix);
274         print_tree(aug, 0, prefix + strlen(path) + 1, nodes);
275     }
276     for (int i=0; i < max_nodes; i++) {
277         free(nodes[i].var);
278     }
279     free(nodes);
280
281     return (count == 0) ? EXIT_FAILURE : EXIT_SUCCESS;
282 }
283
284 /* Look at the filename and try to guess based on the extension. The
285  * builtin filters for lenses do not do that, as that would force augtool
286  * to scan everything on start
287  */
288 static char *guess_lens_name(const char *file) {
289     const char *ext = strrchr(file, '.');
290
291     if (ext == NULL)
292         return NULL;
293
294     if (streqv(ext, ".json")) {
295         return strdup("Json.lns");
296     } else if (streqv(ext, ".xml")) {
297         return strdup("Xml.lns");
298     }
299
300     return NULL;
301 }
302
303 int main(int argc, char **argv) {
304     int opt;
305     cleanup(aug_closep) struct augeas *aug;
306     cleanup(freep) char *loadpath = NULL;
307     size_t loadpath_len = 0;
308     cleanup(freep) char *root = NULL;
309     cleanup(freep) char *lens = NULL;
310     cleanup(freep) char *matches = NULL;
311     size_t matches_len = 0;
312     const char *match = "*";
313     bool print_lens = false;
314     bool quiet = false;
315     int result = EXIT_SUCCESS;
316
317     struct option options[] = {
318         { "help",       0, 0, 'h' },
319         { "include",    1, 0, 'I' },
320         { "lens",       1, 0, 'l' },
321         { "all",        0, 0, 'a' },
322         { "index",      0, 0, 'i' },
323         { "match",      1, 0, 'm' },
324         { "only-value", 0, 0, 'o' },
325         { "nostdinc",   0, 0, 'S' },
326         { "root",       1, 0, 'r' },
327         { "print-lens", 0, 0, 'L' },
328         { "exact",      0, 0, 'e' },
329         { "quiet",      0, 0, 'q' },
330         { 0, 0, 0, 0}
331     };
332     unsigned int flags = AUG_NO_LOAD|AUG_NO_ERR_CLOSE;
333     progname = basename(argv[0]);
334
335     setlocale(LC_ALL, "");
336     while ((opt = getopt_long(argc, argv, "ahI:l:m:oSr:eLq", options, NULL)) != -1) {
337         switch(opt) {
338         case 'I':
339             argz_add(&loadpath, &loadpath_len, optarg);
340             break;
341         case 'l':
342             lens = strdup(optarg);
343             break;
344         case 'L':
345             print_lens = true;
346             break;
347         case 'h':
348             usage();
349             break;
350         case 'a':
351             print_all = true;
352             break;
353         case 'm':
354             // If optarg is a numeric string like '1', it is not a legal
355             // part of a path by itself, and so we need to prefix it with
356             // an explicit axis
357             die(optarg[0] == '/',
358                 "matches can only be relative paths, not %s\n", optarg);
359             argz_add(&matches, &matches_len, format("child::%s", optarg));
360             break;
361         case 'o':
362             print_only_values = true;
363             break;
364         case 'r':
365             root = strdup(optarg);
366             break;
367         case 'S':
368             flags |= AUG_NO_STDINC;
369             break;
370         case 'e':
371             print_exact = true;
372             break;
373         case 'q':
374             quiet = true;
375             break;
376         default:
377             fprintf(stderr, "Try '%s --help' for more information.\n",
378                     progname);
379             exit(EXIT_TROUBLE);
380             break;
381         }
382     }
383
384     if (optind >= argc) {
385         fprintf(stderr, "Expected an input file\n");
386         fprintf(stderr, "Try '%s --help' for more information.\n",
387                 progname);
388         exit(EXIT_TROUBLE);
389     }
390
391     const char *file = argv[optind];
392
393     argz_stringify(loadpath, loadpath_len, ':');
394
395     if (lens == NULL) {
396         lens = guess_lens_name(file);
397     }
398
399     if (lens != NULL) {
400         /* We know which lens we want, we do not need to load all of them */
401         flags |= AUG_NO_MODL_AUTOLOAD;
402     }
403
404     aug = aug_init(root, loadpath, flags|AUG_NO_ERR_CLOSE);
405     check_error(aug);
406
407     if (lens == NULL) {
408         aug_load_file(aug, file);
409     } else {
410         aug_transform(aug, lens, file, false);
411         aug_load(aug);
412     }
413     check_error(aug);
414
415     /* The user just wants the lens name */
416     if (print_lens) {
417         char *info = format("/augeas/files%s", file);
418         const char *lens_name;
419         aug_defvar(aug, "info", info);
420         die(aug_ns_count(aug, "info") == 0,
421             "file %s does not exist\n", file);
422         aug_get(aug, "$info/lens", &lens_name);
423         /* We are being extra careful here - the check_error above would
424            have already aborted the program if we could not determine a
425            lens; dieing here indicates some sort of bug */
426         die(lens_name == NULL, "could not find lens for %s\n",
427             file);
428         if (lens_name[0] == '@')
429             lens_name += 1;
430         printf("%s\n", lens_name);
431         exit(EXIT_SUCCESS);
432     }
433
434     check_load_error(aug, file);
435
436     char *path = format("/files%s", file);
437     aug_set(aug, "/augeas/context", path);
438
439
440     if (matches_len > 0) {
441         argz_stringify(matches, matches_len, '|');
442         match = matches;
443     }
444
445     if (quiet) {
446         int n = aug_match(aug, match, NULL);
447         check_error(aug);
448         result = (n == 0) ? EXIT_FAILURE : EXIT_SUCCESS;
449     } else {
450         result = print(aug, path, match);
451     }
452     free(path);
453
454     return result;
455 }
456
457 /*
458  * Local variables:
459  *  indent-tabs-mode: nil
460  *  c-indent-level: 4
461  *  c-basic-offset: 4
462  *  tab-width: 4
463  * End:
464  */