edit.c revision 1.1 1 /* $NetBSD: edit.c,v 1.1 2007/02/18 22:13:42 rmind Exp $ */
2 /* $OpenBSD: edit.c,v 1.14 2006/05/25 03:20:32 ray Exp $ */
3
4 /*
5 * Written by Raymond Lai <ray (at) cyth.net>.
6 * Public domain.
7 */
8
9 #include <sys/types.h>
10 #include <sys/wait.h>
11
12 #include <ctype.h>
13 #include <err.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <unistd.h>
18
19 #include "common.h"
20 #include "extern.h"
21
22 static void edit(const char *);
23
24 /*
25 * Takes the name of a file and opens it with an editor.
26 */
27 static void
28 edit(const char *filename)
29 {
30 int status;
31 pid_t pid;
32 const char *editor;
33
34 editor = getenv("VISUAL");
35 if (editor == NULL)
36 editor = getenv("EDITOR");
37 if (editor == NULL)
38 editor = "vi";
39
40 /* Start editor on temporary file. */
41 switch (pid = fork()) {
42 case 0:
43 /* child */
44 execlp(editor, editor, filename, (void *)NULL);
45 warn("could not execute editor: %s", editor);
46 cleanup(filename);
47 case -1:
48 warn("could not fork");
49 cleanup(filename);
50 }
51
52 /* parent */
53 /* Wait for editor to exit. */
54 if (waitpid(pid, &status, 0) == -1) {
55 warn("waitpid");
56 cleanup(filename);
57 }
58
59 /* Check that editor terminated normally. */
60 if (!WIFEXITED(status)) {
61 warn("%s terminated abnormally", editor);
62 cleanup(filename);
63 }
64 }
65
66 /*
67 * Parse edit command. Returns 0 on success, -1 on error.
68 */
69 int
70 eparse(const char *cmd, const char *left, const char *right)
71 {
72 FILE *file;
73 size_t nread, nwritten;
74 int fd;
75 char *filename;
76 char buf[BUFSIZ], *text;
77
78 /* Skip whitespace. */
79 while (isspace((int)(*cmd)))
80 ++cmd;
81
82 text = NULL;
83 switch (*cmd) {
84 case '\0':
85 /* Edit empty file. */
86 break;
87
88 case 'b':
89 /* Both strings. */
90 if (left == NULL)
91 goto RIGHT;
92 if (right == NULL)
93 goto LEFT;
94
95 /* Neither column is blank, so print both. */
96 if (asprintf(&text, "%s\n%s\n", left, right) == -1)
97 err(2, "could not allocate memory");
98 break;
99
100 case 'l':
101 LEFT:
102 /* Skip if there is no left column. */
103 if (left == NULL)
104 break;
105
106 if (asprintf(&text, "%s\n", left) == -1)
107 err(2, "could not allocate memory");
108
109 break;
110
111 case 'r':
112 RIGHT:
113 /* Skip if there is no right column. */
114 if (right == NULL)
115 break;
116
117 if (asprintf(&text, "%s\n", right) == -1)
118 err(2, "could not allocate memory");
119
120 break;
121
122 default:
123 return (-1);
124 }
125
126 /* Create temp file. */
127 if (asprintf(&filename, "%s/sdiff.XXXXXXXXXX", tmpdir) == -1)
128 err(2, "asprintf");
129 if ((fd = mkstemp(filename)) == -1)
130 err(2, "mkstemp");
131 if (text != NULL) {
132 size_t len;
133
134 len = strlen(text);
135 if ((nwritten = write(fd, text, len)) == -1 ||
136 nwritten != len) {
137 warn("error writing to temp file");
138 cleanup(filename);
139 }
140 }
141 close(fd);
142
143 /* text is no longer used. */
144 free(text);
145
146 /* Edit temp file. */
147 edit(filename);
148
149 /* Open temporary file. */
150 if (!(file = fopen(filename, "r"))) {
151 warn("could not open edited file: %s", filename);
152 cleanup(filename);
153 }
154
155 /* Copy temporary file contents to output file. */
156 for (nread = sizeof(buf); nread == sizeof(buf);) {
157 nread = fread(buf, sizeof(*buf), sizeof(buf), file);
158 /* Test for error or end of file. */
159 if (nread != sizeof(buf) &&
160 (ferror(file) || !feof(file))) {
161 warnx("error reading edited file: %s", filename);
162 cleanup(filename);
163 }
164
165 /*
166 * If we have nothing to read, break out of loop
167 * instead of writing nothing.
168 */
169 if (!nread)
170 break;
171
172 /* Write data we just read. */
173 nwritten = fwrite(buf, sizeof(*buf), nread, outfile);
174 if (nwritten != nread) {
175 warnx("error writing to output file");
176 cleanup(filename);
177 }
178 }
179
180 /* We've reached the end of the temporary file, so remove it. */
181 if (unlink(filename))
182 warn("could not delete: %s", filename);
183 fclose(file);
184
185 free(filename);
186
187 return (0);
188 }
189