1 /* $OpenBSD: edit.c,v 1.19 2009/06/07 13:29:50 ray Exp $ */
2
3 /*
4 * Written by Raymond Lai <[email protected]>.
5 * Public domain.
6 */
7
8 #include <sys/cdefs.h>
9 #include <sys/types.h>
10 #include <sys/wait.h>
11
12 #include <ctype.h>
13 #include <err.h>
14 #include <errno.h>
15 #include <paths.h>
16 #include <signal.h>
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <unistd.h>
21
22 #include "extern.h"
23
24 static void
cleanup(const char * filename)25 cleanup(const char *filename)
26 {
27
28 if (unlink(filename))
29 err(2, "could not delete: %s", filename);
30 exit(2);
31 }
32
33 /*
34 * Execute an editor on the specified pathname, which is interpreted
35 * from the shell. This means flags may be included.
36 *
37 * Returns -1 on error, or the exit value on success.
38 */
39 static int
editit(const char * pathname)40 editit(const char *pathname)
41 {
42 sig_t sighup, sigint, sigquit, sigchld;
43 pid_t pid;
44 int saved_errno, st, ret = -1;
45 const char *ed;
46
47 ed = getenv("VISUAL");
48 if (ed == NULL)
49 ed = getenv("EDITOR");
50 if (ed == NULL)
51 ed = _PATH_VI;
52
53 sighup = signal(SIGHUP, SIG_IGN);
54 sigint = signal(SIGINT, SIG_IGN);
55 sigquit = signal(SIGQUIT, SIG_IGN);
56 sigchld = signal(SIGCHLD, SIG_DFL);
57 if ((pid = fork()) == -1)
58 goto fail;
59 if (pid == 0) {
60 execlp(ed, ed, pathname, (char *)NULL);
61 _exit(127);
62 }
63 while (waitpid(pid, &st, 0) == -1)
64 if (errno != EINTR)
65 goto fail;
66 if (!WIFEXITED(st))
67 errno = EINTR;
68 else
69 ret = WEXITSTATUS(st);
70
71 fail:
72 saved_errno = errno;
73 (void)signal(SIGHUP, sighup);
74 (void)signal(SIGINT, sigint);
75 (void)signal(SIGQUIT, sigquit);
76 (void)signal(SIGCHLD, sigchld);
77 errno = saved_errno;
78 return (ret);
79 }
80
81 /*
82 * Parse edit command. Returns 0 on success, -1 on error.
83 */
84 int
eparse(const char * cmd,const char * left,const char * right)85 eparse(const char *cmd, const char *left, const char *right)
86 {
87 FILE *file;
88 size_t nread;
89 int fd;
90 char *filename;
91 char buf[BUFSIZ], *text;
92
93 /* Skip whitespace. */
94 while (isspace(*cmd))
95 ++cmd;
96
97 text = NULL;
98 switch (*cmd) {
99 case '\0':
100 /* Edit empty file. */
101 break;
102
103 case 'b':
104 /* Both strings. */
105 if (left == NULL)
106 goto RIGHT;
107 if (right == NULL)
108 goto LEFT;
109
110 /* Neither column is blank, so print both. */
111 if (asprintf(&text, "%s\n%s\n", left, right) == -1)
112 err(2, "could not allocate memory");
113 break;
114
115 case 'l':
116 LEFT:
117 /* Skip if there is no left column. */
118 if (left == NULL)
119 break;
120
121 if (asprintf(&text, "%s\n", left) == -1)
122 err(2, "could not allocate memory");
123
124 break;
125
126 case 'r':
127 RIGHT:
128 /* Skip if there is no right column. */
129 if (right == NULL)
130 break;
131
132 if (asprintf(&text, "%s\n", right) == -1)
133 err(2, "could not allocate memory");
134
135 break;
136
137 default:
138 return (-1);
139 }
140
141 /* Create temp file. */
142 if (asprintf(&filename, "%s/sdiff.XXXXXXXXXX", tmpdir) == -1)
143 err(2, "asprintf");
144 if ((fd = mkstemp(filename)) == -1)
145 err(2, "mkstemp");
146 if (text != NULL) {
147 size_t len;
148 ssize_t nwritten;
149
150 len = strlen(text);
151 if ((nwritten = write(fd, text, len)) == -1 ||
152 (size_t)nwritten != len) {
153 warn("error writing to temp file");
154 cleanup(filename);
155 }
156 }
157 close(fd);
158
159 /* text is no longer used. */
160 free(text);
161
162 /* Edit temp file. */
163 if (editit(filename) == -1) {
164 warn("error editing %s", filename);
165 cleanup(filename);
166 }
167
168 /* Open temporary file. */
169 if (!(file = fopen(filename, "r"))) {
170 warn("could not open edited file: %s", filename);
171 cleanup(filename);
172 }
173
174 /* Copy temporary file contents to output file. */
175 for (nread = sizeof(buf); nread == sizeof(buf);) {
176 size_t nwritten;
177
178 nread = fread(buf, sizeof(*buf), sizeof(buf), file);
179 /* Test for error or end of file. */
180 if (nread != sizeof(buf) &&
181 (ferror(file) || !feof(file))) {
182 warnx("error reading edited file: %s", filename);
183 cleanup(filename);
184 }
185
186 /*
187 * If we have nothing to read, break out of loop
188 * instead of writing nothing.
189 */
190 if (!nread)
191 break;
192
193 /* Write data we just read. */
194 nwritten = fwrite(buf, sizeof(*buf), nread, outfp);
195 if (nwritten != nread) {
196 warnx("error writing to output file");
197 cleanup(filename);
198 }
199 }
200
201 /* We've reached the end of the temporary file, so remove it. */
202 if (unlink(filename))
203 warn("could not delete: %s", filename);
204 fclose(file);
205
206 free(filename);
207
208 return (0);
209 }
210