/*
* 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 */
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;
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, "/");
}
/* 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++;
"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;
}
#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)
}
__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);
}
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' },
{ "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' },
{ "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;
flags |= AUG_SAVE_NEWFILE;
break;
case 'h':
- usage();
+ help();
break;
case 'r':
root = optarg;
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;
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;
}
}
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");
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;
}
}
+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;
}
}
+ install_signal_handlers();
+
// make readline silent by default
echo_commands = echo_commands || isatty(fileno(stdin));
if (echo_commands)
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
continue;
}
- code = run_command(line);
+ code = run_command(line, timing);
if (code == -2) {
free(line);
return ret;
}
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;
}
}
+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();