Move the implementation of file editing using editor to an own file, so that can be shared by different tools. Mostly code motion. --- fish/Makefile.am | 2 + fish/edit.c | 125 ++----------------------------------------- fish/file-edit.c | 158 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ fish/file-edit.h | 32 +++++++++++ po/POTFILES | 1 + 5 files changed, 198 insertions(+), 120 deletions(-) create mode 100644 fish/file-edit.c create mode 100644 fish/file-edit.h diff --git a/fish/Makefile.am b/fish/Makefile.am index c02f703..d28a94b 100644 --- a/fish/Makefile.am +++ b/fish/Makefile.am @@ -87,6 +87,8 @@ guestfish_SOURCES = \ echo.c \ edit.c \ events.c \ + file-edit.c \ + file-edit.h \ fish.c \ fish.h \ glob.c \ diff --git a/fish/edit.c b/fish/edit.c index bd02f4b..5b851e7 100644 --- a/fish/edit.c +++ b/fish/edit.c @@ -22,29 +22,20 @@ #include <stdlib.h> #include <string.h> #include <unistd.h> -#include <fcntl.h> #include <inttypes.h> #include <libintl.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <assert.h> #include "fish.h" - -static char *generate_random_name (const char *filename); +#include "file-edit.h" /* guestfish edit command, suggested by Ján Ondrej, implemented by RWMJ */ int run_edit (const char *cmd, size_t argc, char *argv[]) { - CLEANUP_FREE char *tmpdir = guestfs_get_tmpdir (g); - CLEANUP_UNLINK_FREE char *filename = NULL; - char buf[256]; const char *editor; - CLEANUP_FREE char *remotefilename = NULL, *newname = NULL; - struct stat oldstat, newstat; - int r, fd; + CLEANUP_FREE char *remotefilename = NULL; + int r; if (argc != 1) { fprintf (stderr, _("use '%s filename' to edit a file\n"), cmd); @@ -67,113 +58,7 @@ run_edit (const char *cmd, size_t argc, char *argv[]) if (remotefilename == NULL) return -1; - /* Download the file and write it to a temporary. */ - if (asprintf (&filename, "%s/guestfishXXXXXX", tmpdir) == -1) { - perror ("asprintf"); - return -1; - } - - fd = mkstemp (filename); - if (fd == -1) { - perror ("mkstemp"); - return -1; - } - - snprintf (buf, sizeof buf, "/dev/fd/%d", fd); - - if (guestfs_download (g, remotefilename, buf) == -1) { - close (fd); - return -1; - } - - if (close (fd) == -1) { - perror (filename); - return -1; - } - - /* Get the old stat. */ - if (stat (filename, &oldstat) == -1) { - perror (filename); - return -1; - } - - /* Edit it. */ - /* XXX Safe? */ - snprintf (buf, sizeof buf, "%s %s", editor, filename); - - r = system (buf); - if (r != 0) { - perror (buf); - return -1; - } - - /* Get the new stat. */ - if (stat (filename, &newstat) == -1) { - perror (filename); - return -1; - } - - /* Changed? */ - if (oldstat.st_ctime == newstat.st_ctime && - oldstat.st_size == newstat.st_size) - return 0; - - /* 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 (remotefilename); - if (!newname) - return -1; - - /* Write new content. */ - if (guestfs_upload (g, filename, newname) == -1) - return -1; - - /* Set the permissions, UID, GID and SELinux context of the new - * file to match the old file (RHBZ#788641). - */ - if (guestfs_copy_attributes (g, remotefilename, newname, - GUESTFS_COPY_ATTRIBUTES_ALL, 1, -1) == -1) - return -1; - - if (guestfs_mv (g, newname, remotefilename) == -1) - return -1; - - return 0; -} - -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"); - return NULL; - } - 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'; + r = edit_file_editor (g, remotefilename, editor); - return ret; /* caller will free */ + return r; } diff --git a/fish/file-edit.c b/fish/file-edit.c new file mode 100644 index 0000000..ec707a9 --- /dev/null +++ b/fish/file-edit.c @@ -0,0 +1,158 @@ +/* libguestfs - shared file editing + * Copyright (C) 2009-2014 Red Hat Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <config.h> + +#include "file-edit.h" + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <locale.h> +#include <langinfo.h> +#include <libintl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <assert.h> + +#include "guestfs-internal-frontend.h" + +static char *generate_random_name (const char *filename); + +int +edit_file_editor (guestfs_h *g, const char *filename, const char *editor) +{ + CLEANUP_FREE char *tmpdir = guestfs_get_tmpdir (g); + CLEANUP_UNLINK_FREE char *tmpfilename = NULL; + char buf[256]; + CLEANUP_FREE char *newname = NULL; + struct stat oldstat, newstat; + int r, fd; + + /* Download the file and write it to a temporary. */ + if (asprintf (&tmpfilename, "%s/libguestfsXXXXXX", tmpdir) == -1) { + perror ("asprintf"); + return -1; + } + + fd = mkstemp (tmpfilename); + if (fd == -1) { + perror ("mkstemp"); + return -1; + } + + snprintf (buf, sizeof buf, "/dev/fd/%d", fd); + + if (guestfs_download (g, filename, buf) == -1) { + close (fd); + return -1; + } + + if (close (fd) == -1) { + perror (tmpfilename); + return -1; + } + + /* Get the old stat. */ + if (stat (tmpfilename, &oldstat) == -1) { + perror (tmpfilename); + return -1; + } + + /* Edit it. */ + /* XXX Safe? */ + snprintf (buf, sizeof buf, "%s %s", editor, filename); + + r = system (buf); + if (r != 0) { + perror (buf); + return -1; + } + + /* Get the new stat. */ + if (stat (tmpfilename, &newstat) == -1) { + perror (tmpfilename); + return -1; + } + + /* Changed? */ + if (oldstat.st_ctime == newstat.st_ctime && + oldstat.st_size == newstat.st_size) + return 0; + + /* 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 (!newname) + return -1; + + /* Write new content. */ + if (guestfs_upload (g, tmpfilename, newname) == -1) + return -1; + + /* 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) + return -1; + + if (guestfs_mv (g, newname, filename) == -1) + return -1; + + return 0; +} + +static char +random_char (void) +{ + const 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"); + return NULL; + } + 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 */ +} diff --git a/fish/file-edit.h b/fish/file-edit.h new file mode 100644 index 0000000..eff1c7d --- /dev/null +++ b/fish/file-edit.h @@ -0,0 +1,32 @@ +/* libguestfs - shared file editing + * Copyright (C) 2009-2014 Red Hat Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef FISH_FILE_EDIT_H +#define FISH_FILE_EDIT_H + +#include <guestfs.h> + +/** + * Edit 'filename' using the specified 'editor' application. + * + * Returns -1 for failure, 0 otherwise. + */ +extern int edit_file_editor (guestfs_h *g, const char *filename, + const char *editor); + +#endif diff --git a/po/POTFILES b/po/POTFILES index d40cf9b..a31cc88 100644 --- a/po/POTFILES +++ b/po/POTFILES @@ -135,6 +135,7 @@ fish/echo.c fish/edit.c fish/event-names.c fish/events.c +fish/file-edit.c fish/fish.c fish/glob.c fish/help.c -- 1.9.3