Switch virt-edit to the common edit_file_editor and edit_file_perl.
---
edit/Makefile.am | 2 +
edit/edit.c | 258 ++++---------------------------------------------------
2 files changed, 19 insertions(+), 241 deletions(-)
diff --git a/edit/Makefile.am b/edit/Makefile.am
index cb19bec..9b1998b 100644
--- a/edit/Makefile.am
+++ b/edit/Makefile.am
@@ -28,6 +28,8 @@ bin_PROGRAMS = virt-edit
SHARED_SOURCE_FILES = \
../fish/config.c \
../fish/domain.c \
+ ../fish/file-edit.h \
+ ../fish/file-edit.c \
../fish/inspect.c \
../fish/keys.c \
../fish/options.h \
diff --git a/edit/edit.c b/edit/edit.c
index 1f5ec7b..70ee129 100644
--- a/edit/edit.c
+++ b/edit/edit.c
@@ -38,6 +38,7 @@
#include "guestfs.h"
#include "options.h"
#include "windows.h"
+#include "file-edit.h"
/* Currently open libguestfs handle. */
guestfs_h *g;
@@ -55,10 +56,6 @@ static const char *perl_expr = NULL;
static void edit_files (int argc, char *argv[]);
static void edit (const char *filename, const char *root);
-static char *edit_interactively (const char *tmpfile);
-static char *edit_non_interactively (const char *tmpfile);
-static char *generate_random_name (const char *filename);
-static char *generate_backup_name (const char *filename);
static void __attribute__((noreturn))
usage (int status)
@@ -324,15 +321,7 @@ static void
edit (const char *filename, const char *root)
{
CLEANUP_FREE char *filename_to_free = NULL;
- CLEANUP_FREE char *tmpdir = guestfs_get_tmpdir (g);
- char tmpfile[strlen (tmpdir) + 32];
- sprintf (tmpfile, "%s/virteditXXXXXX", tmpdir);
-
- int fd;
- char fdbuf[32];
- CLEANUP_FREE char *upload_from = NULL;
- CLEANUP_FREE char *newname = NULL;
- CLEANUP_FREE char *backupname = NULL;
+ int r;
/* Windows? Special handling is required. */
if (root != NULL && is_windows (g, root)) {
@@ -342,239 +331,26 @@ edit (const char *filename, const char *root)
exit (EXIT_FAILURE);
}
- /* Download the file to a temporary. */
- fd = mkstemp (tmpfile);
- if (fd == -1) {
- perror ("mkstemp");
- exit (EXIT_FAILURE);
- }
-
- snprintf (fdbuf, sizeof fdbuf, "/dev/fd/%d", fd);
-
- if (guestfs_download (g, filename, fdbuf) == -1)
- goto error;
-
- if (close (fd) == -1) {
- perror (tmpfile);
- goto error;
- }
-
- if (!perl_expr)
- upload_from = edit_interactively (tmpfile);
- else
- upload_from = edit_non_interactively (tmpfile);
-
- /* We don't always need to upload: upload_from could be NULL because
- * the user closed the editor without changing the file.
- */
- if (upload_from) {
- /* Upload to a new file in the same directory, so if it fails we
- * don't end up with a partially written file. Give the new file
- * a completely random name so we have only a tiny chance of
- * overwriting some existing file.
- */
- newname = generate_random_name (filename);
-
- if (guestfs_upload (g, upload_from, newname) == -1)
- goto error;
-
- /* Set the permissions, UID, GID and SELinux context of the new
- * file to match the old file (RHBZ#788641).
- */
- if (guestfs_copy_attributes (g, filename, newname,
- GUESTFS_COPY_ATTRIBUTES_ALL, 1, -1) == -1)
- goto error;
-
- /* Backup or overwrite the file. */
- if (backup_extension) {
- backupname = generate_backup_name (filename);
- if (guestfs_mv (g, filename, backupname) == -1)
- goto error;
- }
- if (guestfs_mv (g, newname, filename) == -1)
- goto error;
- }
-
- unlink (tmpfile);
- return;
-
- error:
- unlink (tmpfile);
- exit (EXIT_FAILURE);
-}
-
-static char *
-edit_interactively (const char *tmpfile)
-{
- struct utimbuf times;
- struct stat oldstat, newstat;
- const char *editor;
- CLEANUP_FREE char *cmd = NULL;
- int r;
- char *ret;
-
- /* Set the time back a few seconds on the original file. This is so
- * that if the user is very fast at editing, or if EDITOR is an
- * automatic editor, then the edit might happen within the 1 second
- * granularity of mtime, and we would think the file hasn't changed.
- */
- if (stat (tmpfile, &oldstat) == -1) {
- perror (tmpfile);
- exit (EXIT_FAILURE);
- }
-
- times.actime = oldstat.st_atime - 5;
- times.modtime = oldstat.st_mtime - 5;
- if (utime (tmpfile, ×) == -1) {
- perror ("utimes");
- exit (EXIT_FAILURE);
- }
+ if (perl_expr != NULL) {
+ r = edit_file_perl (g, filename, perl_expr, backup_extension);
+ } else {
+ const char *editor;
- if (stat (tmpfile, &oldstat) == -1) {
- perror (tmpfile);
- exit (EXIT_FAILURE);
- }
-
- editor = getenv ("EDITOR");
- if (editor == NULL)
- editor = "vi";
+ editor = getenv ("EDITOR");
+ if (editor == NULL)
+ editor = "vi";
- if (asprintf (&cmd, "%s %s", editor, tmpfile) == -1) {
- perror ("asprintf");
- exit (EXIT_FAILURE);
+ r = edit_file_editor (g, filename, editor, backup_extension);
}
- if (verbose)
- fprintf (stderr, "%s\n", cmd);
-
- r = system (cmd);
- if (r == -1 || WEXITSTATUS (r) != 0)
+ switch (r) {
+ case -1:
exit (EXIT_FAILURE);
-
- if (stat (tmpfile, &newstat) == -1) {
- perror (tmpfile);
- exit (EXIT_FAILURE);
- }
-
- if (oldstat.st_ctime == newstat.st_ctime &&
- oldstat.st_mtime == newstat.st_mtime) {
+ case 1:
printf ("File not changed.\n");
- return NULL;
- }
-
- ret = strdup (tmpfile);
- if (!ret) {
- perror ("strdup");
- exit (EXIT_FAILURE);
+ break;
+ default:
+ /* Success. */
+ break;
}
-
- return ret;
-}
-
-/* Note that virt-builder uses exactly the same code .. in OCaml. */
-static char *
-edit_non_interactively (const char *tmpfile)
-{
- CLEANUP_FREE char *cmd = NULL, *outfile = NULL;
- char *ret;
- int r;
-
- assert (perl_expr != NULL);
-
- /* Pass the expression to Perl via the environment. This sidesteps
- * any quoting problems with the already complex Perl command line.
- */
- setenv ("virt_edit_expr", perl_expr, 1);
-
- /* Call out to a canned Perl script. */
- if (asprintf (&cmd,
- "perl -e '"
- "$lineno = 0; "
- "$expr = $ENV{virt_edit_expr}; "
- "while (<STDIN>) { "
- " $lineno++; "
- " eval $expr; "
- " die if $@; "
- " print STDOUT $_ or die \"print: $!\"; "
- "} "
- "close STDOUT or die \"close: $!\"; "
- "' < %s > %s.out",
- tmpfile, tmpfile) == -1) {
- perror ("asprintf");
- exit (EXIT_FAILURE);
- }
-
- if (verbose)
- fprintf (stderr, "%s\n", cmd);
-
- r = system (cmd);
- if (r == -1 || WEXITSTATUS (r) != 0)
- exit (EXIT_FAILURE);
-
- if (asprintf (&outfile, "%s.out", tmpfile) == -1) {
- perror ("asprintf");
- exit (EXIT_FAILURE);
- }
-
- if (rename (outfile, tmpfile) == -1) {
- perror ("rename");
- exit (EXIT_FAILURE);
- }
-
- ret = strdup (tmpfile);
- if (!ret) {
- perror ("strdup");
- exit (EXIT_FAILURE);
- }
-
- return ret; /* caller will free */
-}
-
-static char
-random_char (void)
-{
- char c[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
- return c[random () % (sizeof c - 1)];
-}
-
-static char *
-generate_random_name (const char *filename)
-{
- char *ret, *p;
- size_t i;
-
- ret = malloc (strlen (filename) + 16);
- if (!ret) {
- perror ("malloc");
- exit (EXIT_FAILURE);
- }
- strcpy (ret, filename);
-
- p = strrchr (ret, '/');
- assert (p);
- p++;
-
- /* Because of "+ 16" above, there should be enough space in the
- * output buffer to write 8 random characters here.
- */
- for (i = 0; i < 8; ++i)
- *p++ = random_char ();
- *p++ = '\0';
-
- return ret; /* caller will free */
-}
-
-static char *
-generate_backup_name (const char *filename)
-{
- char *ret;
-
- assert (backup_extension != NULL);
-
- if (asprintf (&ret, "%s%s", filename, backup_extension) == -1) {
- perror ("asprintf");
- exit (EXIT_FAILURE);
- }
-
- return ret; /* caller will free */
}
--
1.9.3