Imported Upstream version 1.6.0
[platform/upstream/augeas.git] / src / augtool.c
index c7be484..1aa58ec 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 */
 
@@ -45,12 +47,15 @@ static unsigned int flags = AUG_NONE;
 const char *root = NULL;
 char *loadpath = NULL;
 char *transforms = NULL;
+char *loadonly = NULL;
 size_t transformslen = 0;
+size_t loadonlylen = 0;
 const char *inputfile = NULL;
 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;
 
@@ -136,8 +141,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, "/");
             }
@@ -145,8 +152,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++;
@@ -170,8 +179,8 @@ static char *readline_command_generator(const char *text, int state) {
         "quit", "clear", "defnode", "defvar",
         "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", NULL };
+        "clearm", "span", "store", "retrieve", "transform", "load-file",
+        "help", "touch", "insert", "move", "copy", "errors", NULL };
 
     static int current = 0;
     const char *name;
@@ -196,6 +205,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)
@@ -263,32 +287,34 @@ 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 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, "  --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, "  -l, --load-file FILE   load individual FILE in the tree\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);
 }
@@ -298,7 +324,8 @@ 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' },
@@ -308,6 +335,7 @@ static void parse_opts(int argc, char **argv) {
         { "root",        1, 0, 'r' },
         { "include",     1, 0, 'I' },
         { "transform",   1, 0, 't' },
+        { "load-file",   1, 0, 'l' },
         { "echo",        0, 0, 'e' },
         { "file",        1, 0, 'f' },
         { "autosave",    0, 0, 's' },
@@ -316,12 +344,13 @@ static void parse_opts(int argc, char **argv) {
         { "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:t:ef:siSLA", options, &idx)) != -1) {
+    while ((opt = getopt_long(argc, argv, "hnbcr:I:t:l:ef:siSLA", options, &idx)) != -1) {
         switch(opt) {
         case 'c':
             flags |= AUG_TYPE_CHECK;
@@ -333,7 +362,7 @@ static void parse_opts(int argc, char **argv) {
             flags |= AUG_SAVE_NEWFILE;
             break;
         case 'h':
-            usage();
+            help();
             break;
         case 'r':
             root = optarg;
@@ -344,6 +373,11 @@ static void parse_opts(int argc, char **argv) {
         case 't':
             argz_add(&transforms, &transformslen, optarg);
             break;
+        case 'l':
+            // --load-file implies --noload
+            flags |= AUG_NO_LOAD;
+            argz_add(&loadonly, &loadonlylen, optarg);
+            break;
         case 'e':
             echo_commands = 1;
             break;
@@ -372,8 +406,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;
         }
     }
@@ -389,7 +428,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");
@@ -400,10 +439,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;
@@ -426,6 +479,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;
@@ -446,6 +518,8 @@ static int main_loop(void) {
         }
     }
 
+    install_signal_handlers();
+
     // make readline silent by default
     echo_commands = echo_commands || isatty(fileno(stdin));
     if (echo_commands)
@@ -468,10 +542,11 @@ static int main_loop(void) {
                 echo_commands = true;
 
                 // reopen in stream
-                if ((rl_instream = fopen("/dev/tty", "r")) == NULL) {
+                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
@@ -512,7 +587,7 @@ static int main_loop(void) {
             continue;
         }
 
-        code = run_command(line);
+        code = run_command(line, timing);
         if (code == -2) {
             free(line);
             return ret;
@@ -543,12 +618,12 @@ static int run_args(int argc, char **argv) {
     }
     if (echo_commands)
         printf("%s%s\n", AUGTOOL_PROMPT, line);
-    code = run_command(line);
+    code = run_command(line, timing);
     free(line);
     if (code >= 0 && auto_save)
         if (echo_commands)
             printf("%ssave\n", AUGTOOL_PROMPT);
-        code = run_command("save");
+    code = run_command("save", false);
 
     if (code < 0) {
         code = -1;
@@ -583,20 +658,53 @@ static void add_transforms(char *ts, size_t tslen) {
     }
 }
 
+static void load_files(char *ts, size_t tslen) {
+    char *command;
+    int r;
+    char *t = NULL;
+
+    while ((t = argz_next(ts, tslen, t))) {
+        r = xasprintf(&command, "load-file %s", t);
+        if (r < 0)
+            fprintf(stderr, "error: Failed to load file %s: could not allocate memory\n", t);
+
+        r = aug_srun(aug, stdout, command);
+        if (r < 0)
+            fprintf(stderr, "error: Failed to load file %s: %s\n", t, aug_error_message(aug));
+
+        free(command);
+    }
+}
+
 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);
     }
+    load_files(loadonly, loadonlylen);
     add_transforms(transforms, transformslen);
     if (print_version) {
         print_version_info();