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