Implement private data area.
[libguestfs.git] / fish / edit.c
1 /* guestfish - the filesystem interactive shell
2  * Copyright (C) 2009 Red Hat Inc.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18
19 #include <config.h>
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <unistd.h>
25 #include <fcntl.h>
26 #include <inttypes.h>
27
28 #include "fish.h"
29
30 /* guestfish edit command, suggested by Ján Ondrej, implemented by RWMJ */
31
32 static char *
33 load_file (const char *filename, size_t *len_r)
34 {
35   int fd, r, start;
36   char *content = NULL, *p;
37   char buf[65536];
38
39   *len_r = 0;
40
41   fd = open (filename, O_RDONLY);
42   if (fd == -1) {
43     perror (filename);
44     return NULL;
45   }
46
47   while ((r = read (fd, buf, sizeof buf)) > 0) {
48     start = *len_r;
49     *len_r += r;
50     p = realloc (content, *len_r + 1);
51     if (p == NULL) {
52       perror ("realloc");
53       free (content);
54       return NULL;
55     }
56     content = p;
57     memcpy (content + start, buf, r);
58     content[start+r] = '\0';
59   }
60
61   if (r == -1) {
62     perror (filename);
63     free (content);
64     return NULL;
65   }
66
67   if (close (fd) == -1) {
68     perror (filename);
69     free (content);
70     return NULL;
71   }
72
73   return content;
74 }
75
76 int
77 do_edit (const char *cmd, int argc, char *argv[])
78 {
79   char filename[] = "/tmp/guestfishXXXXXX";
80   char buf[256];
81   const char *editor;
82   char *content, *content_new;
83   int r, fd;
84
85   if (argc != 1) {
86     fprintf (stderr, _("use '%s filename' to edit a file\n"), cmd);
87     return -1;
88   }
89
90   /* Choose an editor. */
91   if (STRCASEEQ (cmd, "vi"))
92     editor = "vi";
93   else if (STRCASEEQ (cmd, "emacs"))
94     editor = "emacs -nw";
95   else {
96     editor = getenv ("EDITOR");
97     if (editor == NULL)
98       editor = "vi"; /* could be cruel here and choose ed(1) */
99   }
100
101   /* Download the file and write it to a temporary. */
102   fd = mkstemp (filename);
103   if (fd == -1) {
104     perror ("mkstemp");
105     return -1;
106   }
107
108   if ((content = guestfs_cat (g, argv[0])) == NULL) {
109     close (fd);
110     unlink (filename);
111     return -1;
112   }
113
114   if (xwrite (fd, content, strlen (content)) == -1) {
115     close (fd);
116     unlink (filename);
117     free (content);
118     return -1;
119   }
120
121   if (close (fd) == -1) {
122     perror (filename);
123     unlink (filename);
124     free (content);
125     return -1;
126   }
127
128   /* Edit it. */
129   /* XXX Safe? */
130   snprintf (buf, sizeof buf, "%s %s", editor, filename);
131
132   r = system (buf);
133   if (r != 0) {
134     perror (buf);
135     unlink (filename);
136     free (content);
137     return -1;
138   }
139
140   /* Reload it. */
141   size_t size;
142   content_new = load_file (filename, &size);
143   if (content_new == NULL) {
144     unlink (filename);
145     free (content);
146     return -1;
147   }
148
149   unlink (filename);
150
151   /* Changed? */
152   if (strlen (content) == size && STREQLEN (content, content_new, size)) {
153     free (content);
154     free (content_new);
155     return 0;
156   }
157
158   /* Write new content. */
159   if (guestfs_write (g, argv[0], content_new, size) == -1) {
160     free (content);
161     free (content_new);
162     return -1;
163   }
164
165   free (content);
166   free (content_new);
167   return 0;
168 }