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