Imported Upstream version 1.5.0
[platform/upstream/augeas.git] / src / augtool.c
index e9c1422..0603c6d 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * augtool.c:
  *
- * Copyright (C) 2007-2011 David Lutterkort
+ * Copyright (C) 2007-2015 David Lutterkort
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
 #include <limits.h>
 #include <ctype.h>
 #include <locale.h>
+#include <signal.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <pwd.h>
 #include <stdarg.h>
+#include <sys/time.h>
 
 /* Global variables */
 
@@ -44,11 +46,14 @@ static const char *const progname = "augtool";
 static unsigned int flags = AUG_NONE;
 const char *root = NULL;
 char *loadpath = NULL;
+char *transforms = NULL;
+size_t transformslen = 0;
 const char *inputfile = NULL;
-int echo = 0;         /* Gets also changed in main_loop */
+int echo_commands = 0;         /* Gets also changed in main_loop */
 bool print_version = false;
 bool auto_save = false;
 bool interactive = false;
+bool timing = false;
 /* History file is ~/.augeas/history */
 char *history_file = NULL;
 
@@ -134,8 +139,10 @@ static char *readline_path_generator(const char *text, int state) {
         if (STREQLEN(chend, end, strlen(end))) {
             if (child_count(child) > 0) {
                 char *c = realloc(child, strlen(child)+2);
-                if (c == NULL)
+                if (c == NULL) {
+                    free(child);
                     return NULL;
+                }
                 child = c;
                 strcat(child, "/");
             }
@@ -143,8 +150,10 @@ static char *readline_path_generator(const char *text, int state) {
             /* strip off context if the user didn't give it */
             if (ctx != NULL) {
                 char *c = realloc(child, strlen(child)-strlen(ctx)+1);
-                if (c == NULL)
+                if (c == NULL) {
+                    free(child);
                     return NULL;
+                }
                 int ctxidx = strlen(ctx);
                 if (child[ctxidx] == SEP)
                     ctxidx++;
@@ -166,9 +175,10 @@ static char *readline_command_generator(const char *text, int state) {
     // FIXME: expose somewhere under /augeas
     static const char *const commands[] = {
         "quit", "clear", "defnode", "defvar",
-        "get", "ins", "load", "ls", "match",
-        "mv", "print", "dump-xml", "rm", "save", "set", "setm",
-        "clearm", "span", "help", NULL };
+        "get", "label", "ins", "load", "ls", "match",
+        "mv", "cp", "rename", "print", "dump-xml", "rm", "save", "set", "setm",
+        "clearm", "span", "store", "retrieve", "transform",
+        "help", "touch", "insert", "move", "copy", "errors", NULL };
 
     static int current = 0;
     const char *name;
@@ -193,6 +203,21 @@ static char **rl_completion_matches(ATTRIBUTE_UNUSED const char *text,
 }
 #endif
 
+#ifndef HAVE_RL_CRLF
+static int rl_crlf(void) {
+    if (rl_outstream != NULL)
+        putc('\n', rl_outstream);
+    return 0;
+}
+#endif
+
+#ifndef HAVE_RL_REPLACE_LINE
+static void rl_replace_line(ATTRIBUTE_UNUSED const char *text,
+                              ATTRIBUTE_UNUSED int clear_undo) {
+    return;
+}
+#endif
+
 static char **readline_completion(const char *text, int start,
                                   ATTRIBUTE_UNUSED int end) {
     if (start == 0)
@@ -260,28 +285,33 @@ static void readline_init(void) {
 }
 
 __attribute__((noreturn))
-static void usage(void) {
+static void help(void) {
     fprintf(stderr, "Usage: %s [OPTIONS] [COMMAND]\n", progname);
     fprintf(stderr, "Load the Augeas tree and modify it. If no COMMAND is given, run interactively\n");
     fprintf(stderr, "Run '%s help' to get a list of possible commands.\n",
             progname);
     fprintf(stderr, "\nOptions:\n\n");
-    fprintf(stderr, "  -c, --typecheck    typecheck lenses\n");
-    fprintf(stderr, "  -b, --backup       preserve originals of modified files with\n"
-                    "                     extension '.augsave'\n");
-    fprintf(stderr, "  -n, --new          save changes in files with extension '.augnew',\n"
-                    "                     leave original unchanged\n");
-    fprintf(stderr, "  -r, --root ROOT    use ROOT as the root of the filesystem\n");
-    fprintf(stderr, "  -I, --include DIR  search DIR for modules; can be given mutiple times\n");
-    fprintf(stderr, "  -e, --echo         echo commands when reading from a file\n");
-    fprintf(stderr, "  -f, --file FILE    read commands from FILE\n");
-    fprintf(stderr, "  -s, --autosave     automatically save at the end of instructions\n");
-    fprintf(stderr, "  -i, --interactive  run an interactive shell after evaluating the commands in STDIN and FILE\n");
-    fprintf(stderr, "  -S, --nostdinc     do not search the builtin default directories for modules\n");
-    fprintf(stderr, "  -L, --noload       do not load any files into the tree on startup\n");
-    fprintf(stderr, "  -A, --noautoload   do not autoload modules from the search path\n");
-    fprintf(stderr, "  --span             load span positions for nodes related to a file\n");
-    fprintf(stderr, "  --version          print version information and exit.\n");
+    fprintf(stderr, "  -c, --typecheck      typecheck lenses\n");
+    fprintf(stderr, "  -b, --backup         preserve originals of modified files with\n"
+                    "                       extension '.augsave'\n");
+    fprintf(stderr, "  -n, --new            save changes in files with extension '.augnew',\n"
+                    "                       leave original unchanged\n");
+    fprintf(stderr, "  -r, --root ROOT      use ROOT as the root of the filesystem\n");
+    fprintf(stderr, "  -I, --include DIR    search DIR for modules; can be given multiple times\n");
+    fprintf(stderr, "  -t, --transform XFM  add a file transform; uses the 'transform' command\n"
+                    "                       syntax, e.g. -t 'Fstab incl /etc/fstab.bak'\n");
+    fprintf(stderr, "  -e, --echo           echo commands when reading from a file\n");
+    fprintf(stderr, "  -f, --file FILE      read commands from FILE\n");
+    fprintf(stderr, "  -s, --autosave       automatically save at the end of instructions\n");
+    fprintf(stderr, "  -i, --interactive    run an interactive shell after evaluating\n"
+                    "                       the commands in STDIN and FILE\n");
+    fprintf(stderr, "  -S, --nostdinc       do not search the builtin default directories\n"
+                    "                       for modules\n");
+    fprintf(stderr, "  -L, --noload         do not load any files into the tree on startup\n");
+    fprintf(stderr, "  -A, --noautoload     do not autoload modules from the search path\n");
+    fprintf(stderr, "  --span               load span positions for nodes related to a file\n");
+    fprintf(stderr, "  --timing             after executing each command, show how long it took\n");
+    fprintf(stderr, "  --version            print version information and exit.\n");
 
     exit(EXIT_FAILURE);
 }
@@ -291,29 +321,32 @@ static void parse_opts(int argc, char **argv) {
     size_t loadpathlen = 0;
     enum {
         VAL_VERSION = CHAR_MAX + 1,
-        VAL_SPAN = VAL_VERSION + 1
+        VAL_SPAN = VAL_VERSION + 1,
+        VAL_TIMING = VAL_SPAN + 1
     };
     struct option options[] = {
-        { "help",      0, 0, 'h' },
-        { "typecheck", 0, 0, 'c' },
-        { "backup",    0, 0, 'b' },
-        { "new",       0, 0, 'n' },
-        { "root",      1, 0, 'r' },
-        { "include",   1, 0, 'I' },
-        { "echo",      0, 0, 'e' },
-        { "file",      1, 0, 'f' },
-        { "autosave",  0, 0, 's' },
-        { "interactive",  0, 0, 'i' },
-        { "nostdinc",  0, 0, 'S' },
-        { "noload",    0, 0, 'L' },
-        { "noautoload", 0, 0, 'A' },
-        { "span",      0, 0, VAL_SPAN },
-        { "version",   0, 0, VAL_VERSION },
+        { "help",        0, 0, 'h' },
+        { "typecheck",   0, 0, 'c' },
+        { "backup",      0, 0, 'b' },
+        { "new",         0, 0, 'n' },
+        { "root",        1, 0, 'r' },
+        { "include",     1, 0, 'I' },
+        { "transform",   1, 0, 't' },
+        { "echo",        0, 0, 'e' },
+        { "file",        1, 0, 'f' },
+        { "autosave",    0, 0, 's' },
+        { "interactive", 0, 0, 'i' },
+        { "nostdinc",    0, 0, 'S' },
+        { "noload",      0, 0, 'L' },
+        { "noautoload",  0, 0, 'A' },
+        { "span",        0, 0, VAL_SPAN },
+        { "timing",      0, 0, VAL_TIMING },
+        { "version",     0, 0, VAL_VERSION },
         { 0, 0, 0, 0}
     };
     int idx;
 
-    while ((opt = getopt_long(argc, argv, "hnbcr:I:ef:siSLA", options, &idx)) != -1) {
+    while ((opt = getopt_long(argc, argv, "hnbcr:I:t:ef:siSLA", options, &idx)) != -1) {
         switch(opt) {
         case 'c':
             flags |= AUG_TYPE_CHECK;
@@ -325,7 +358,7 @@ static void parse_opts(int argc, char **argv) {
             flags |= AUG_SAVE_NEWFILE;
             break;
         case 'h':
-            usage();
+            help();
             break;
         case 'r':
             root = optarg;
@@ -333,8 +366,11 @@ static void parse_opts(int argc, char **argv) {
         case 'I':
             argz_add(&loadpath, &loadpathlen, optarg);
             break;
+        case 't':
+            argz_add(&transforms, &transformslen, optarg);
+            break;
         case 'e':
-            echo = 1;
+            echo_commands = 1;
             break;
         case 'f':
             inputfile = optarg;
@@ -361,8 +397,13 @@ static void parse_opts(int argc, char **argv) {
         case VAL_SPAN:
             flags |= AUG_ENABLE_SPAN;
             break;
+        case VAL_TIMING:
+            timing = true;
+            break;
         default:
-            usage();
+            fprintf(stderr, "Try '%s --help' for more information.\n",
+                    progname);
+            exit(EXIT_FAILURE);
             break;
         }
     }
@@ -378,7 +419,7 @@ static void print_version_info(void) {
         goto error;
 
     fprintf(stderr, "augtool %s <http://augeas.net/>\n", version);
-    fprintf(stderr, "Copyright (C) 2007-2011 David Lutterkort\n");
+    fprintf(stderr, "Copyright (C) 2007-2015 David Lutterkort\n");
     fprintf(stderr, "License LGPLv2+: GNU LGPL version 2.1 or later\n");
     fprintf(stderr, "                 <http://www.gnu.org/licenses/lgpl-2.1.html>\n");
     fprintf(stderr, "This is free software: you are free to change and redistribute it.\n");
@@ -389,10 +430,24 @@ static void print_version_info(void) {
     fprintf(stderr, "Something went terribly wrong internally - please file a bug\n");
 }
 
-static int run_command(const char *line) {
+static void print_time_taken(const struct timeval *start,
+                             const struct timeval *stop) {
+    time_t elapsed = (stop->tv_sec - start->tv_sec)*1000
+                   + (stop->tv_usec - start->tv_usec)/1000;
+    printf("Time: %ld ms\n", elapsed);
+}
+
+static int run_command(const char *line, bool with_timing) {
     int result;
+    struct timeval stop, start;
 
+    gettimeofday(&start, NULL);
     result = aug_srun(aug, stdout, line);
+    gettimeofday(&stop, NULL);
+    if (with_timing && result >= 0) {
+        print_time_taken(&start, &stop);
+    }
+
     if (isatty(fileno(stdin)))
         add_history(line);
     return result;
@@ -415,6 +470,25 @@ static void print_aug_error(void) {
     }
 }
 
+static void sigint_handler(ATTRIBUTE_UNUSED int signum) {
+    // Cancel the current line of input, along with undo info for that line.
+    rl_replace_line("", 1);
+
+    // Move the cursor to the next screen line, then force a re-display.
+    rl_crlf();
+    rl_forced_update_display();
+}
+
+static void install_signal_handlers(void) {
+    // On Ctrl-C, cancel the current line (rather than exit the program).
+    struct sigaction sigint_action;
+    MEMZERO(&sigint_action, 1);
+    sigint_action.sa_handler = sigint_handler;
+    sigemptyset(&sigint_action.sa_mask);
+    sigint_action.sa_flags = 0;
+    sigaction(SIGINT, &sigint_action, NULL);
+}
+
 static int main_loop(void) {
     char *line = NULL;
     int ret = 0;
@@ -423,8 +497,6 @@ static int main_loop(void) {
     bool end_reached = false;
     bool get_line = true;
     bool in_interactive = false;
-    // make readline silent by default
-    rl_outstream = fopen("/dev/null", "w");
 
     if (inputfile) {
         if (freopen(inputfile, "r", stdin) == NULL) {
@@ -437,10 +509,14 @@ static int main_loop(void) {
         }
     }
 
-    echo = echo || isatty(fileno(stdin));
+    install_signal_handlers();
 
-    if (echo)
+    // make readline silent by default
+    echo_commands = echo_commands || isatty(fileno(stdin));
+    if (echo_commands)
         rl_outstream = NULL;
+    else
+        rl_outstream = fopen("/dev/null", "w");
 
     while(1) {
         if (get_line) {
@@ -451,28 +527,38 @@ static int main_loop(void) {
 
         if (line == NULL) {
             if (!isatty(fileno(stdin)) && interactive && !in_interactive) {
-               in_interactive = true;
-               echo = true;
-               // reopen in and out streams
-               if ((rl_instream = fopen("/dev/tty", "r")) == NULL) {
-                   perror("Failed to open terminal for reading");
-                   return -1;
-               }
-               if (rl_outstream != NULL) {
-                   fclose(rl_outstream);
-                   rl_outstream = NULL;
-               }
-               if ((rl_outstream = fopen("/dev/stdout", "w")) == NULL) {
-                   perror("Failed to reopen stdout");
-                   return -1;
-               }
-               continue;
+                in_interactive = true;
+                if (echo_commands)
+                    printf("\n");
+                echo_commands = true;
+
+                // reopen in stream
+                if (freopen("/dev/tty", "r", stdin) == NULL) {
+                    perror("Failed to open terminal for reading");
+                    return -1;
+                }
+                rl_instream = stdin;
+
+                // reopen stdout and stream to a tty if originally silenced or
+                // not connected to a tty, for full interactive mode
+                if (rl_outstream == NULL || !isatty(fileno(rl_outstream))) {
+                    if (rl_outstream != NULL) {
+                        fclose(rl_outstream);
+                        rl_outstream = NULL;
+                    }
+                    if (freopen("/dev/tty", "w", stdout) == NULL) {
+                        perror("Failed to reopen stdout");
+                        return -1;
+                    }
+                    rl_outstream = stdout;
+                }
+                continue;
             }
 
             if (auto_save) {
                 strncpy(inputline, "save", sizeof(inputline));
                 line = inputline;
-                if (echo)
+                if (echo_commands)
                     printf("%s\n", line);
                 auto_save = false;
             } else {
@@ -482,21 +568,29 @@ static int main_loop(void) {
         }
 
         if (end_reached) {
-            if (echo)
+            if (echo_commands)
                 printf("\n");
             return ret;
         }
 
-        if (*line == '\0' || *line == '#')
+        if (*line == '\0' || *line == '#') {
+            free(line);
             continue;
+        }
 
-        code = run_command(line);
-        if (code == -2)
+        code = run_command(line, timing);
+        if (code == -2) {
+            free(line);
             return ret;
+        }
+
         if (code < 0) {
             ret = -1;
             print_aug_error();
         }
+
+        if (line != inputline)
+            free(line);
     }
 }
 
@@ -513,27 +607,77 @@ static int run_args(int argc, char **argv) {
         strcat(line, argv[i]);
         strcat(line, " ");
     }
-    code = run_command(line);
+    if (echo_commands)
+        printf("%s%s\n", AUGTOOL_PROMPT, line);
+    code = run_command(line, timing);
     free(line);
     if (code >= 0 && auto_save)
-        code = run_command("save");
-    return (code == 0 || code == -2) ? 0 : -1;
+        if (echo_commands)
+            printf("%ssave\n", AUGTOOL_PROMPT);
+    code = run_command("save", false);
+
+    if (code < 0) {
+        code = -1;
+        print_aug_error();
+    }
+    return (code >= 0 || code == -2) ? 0 : -1;
+}
+
+static void add_transforms(char *ts, size_t tslen) {
+    char *command;
+    int r;
+    char *t = NULL;
+    bool added_transform = false;
+
+    while ((t = argz_next(ts, tslen, t))) {
+        r = xasprintf(&command, "transform %s", t);
+        if (r < 0)
+            fprintf(stderr, "error: Failed to add transform %s: could not allocate memory\n", t);
+
+        r = aug_srun(aug, stdout, command);
+        if (r < 0)
+            fprintf(stderr, "error: Failed to add transform %s: %s\n", t, aug_error_message(aug));
+
+        free(command);
+        added_transform = true;
+    }
+
+    if (added_transform) {
+        r = aug_load(aug);
+        if (r < 0)
+            fprintf(stderr, "error: Failed to load with new transforms: %s\n", aug_error_message(aug));
+    }
 }
 
 int main(int argc, char **argv) {
     int r;
+    struct timeval start, stop;
 
     setlocale(LC_ALL, "");
 
     parse_opts(argc, argv);
 
+    if (timing) {
+        printf("Initializing augeas ... ");
+        fflush(stdout);
+    }
+    gettimeofday(&start, NULL);
+
     aug = aug_init(root, loadpath, flags|AUG_NO_ERR_CLOSE);
+
+    gettimeofday(&stop, NULL);
+    if (timing) {
+        printf("done\n");
+        print_time_taken(&start, &stop);
+    }
+
     if (aug == NULL || aug_error(aug) != AUG_NOERROR) {
         fprintf(stderr, "Failed to initialize Augeas\n");
         if (aug != NULL)
             print_aug_error();
         exit(EXIT_FAILURE);
     }
+    add_transforms(transforms, transformslen);
     if (print_version) {
         print_version_info();
         return EXIT_SUCCESS;