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