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 |
33 |
|
|
editit(const char *pathname) |
34 |
|
|
{ |
35 |
|
84 |
char *argp[] = {"sh", "-c", NULL, NULL}, *ed, *p; |
36 |
|
|
sig_t sighup, sigint, sigquit, sigchld; |
37 |
|
|
pid_t pid; |
38 |
|
42 |
int saved_errno, st, ret = -1; |
39 |
|
|
|
40 |
|
42 |
ed = getenv("VISUAL"); |
41 |
✓✗✗✓
|
84 |
if (ed == NULL || ed[0] == '\0') |
42 |
|
|
ed = getenv("EDITOR"); |
43 |
✓✗✗✓
|
84 |
if (ed == NULL || ed[0] == '\0') |
44 |
|
|
ed = _PATH_VI; |
45 |
✗✓ |
42 |
if (asprintf(&p, "%s %s", ed, pathname) == -1) |
46 |
|
|
return (-1); |
47 |
|
42 |
argp[2] = p; |
48 |
|
|
|
49 |
|
42 |
sighup = signal(SIGHUP, SIG_IGN); |
50 |
|
42 |
sigint = signal(SIGINT, SIG_IGN); |
51 |
|
42 |
sigquit = signal(SIGQUIT, SIG_IGN); |
52 |
|
42 |
sigchld = signal(SIGCHLD, SIG_DFL); |
53 |
✓✗ |
42 |
if ((pid = fork()) == -1) |
54 |
|
|
goto fail; |
55 |
✗✓ |
42 |
if (pid == 0) { |
56 |
|
|
execv(_PATH_BSHELL, argp); |
57 |
|
|
_exit(127); |
58 |
|
|
} |
59 |
✗✓ |
42 |
while (waitpid(pid, &st, 0) == -1) |
60 |
|
|
if (errno != EINTR) |
61 |
|
|
goto fail; |
62 |
✗✓ |
42 |
if (!WIFEXITED(st)) |
63 |
|
|
errno = EINTR; |
64 |
|
|
else |
65 |
|
42 |
ret = WEXITSTATUS(st); |
66 |
|
|
|
67 |
|
|
fail: |
68 |
|
42 |
saved_errno = errno; |
69 |
|
42 |
(void)signal(SIGHUP, sighup); |
70 |
|
42 |
(void)signal(SIGINT, sigint); |
71 |
|
42 |
(void)signal(SIGQUIT, sigquit); |
72 |
|
42 |
(void)signal(SIGCHLD, sigchld); |
73 |
|
42 |
free(p); |
74 |
|
42 |
errno = saved_errno; |
75 |
|
42 |
return (ret); |
76 |
|
42 |
} |
77 |
|
|
|
78 |
|
|
/* |
79 |
|
|
* Parse edit command. Returns 0 on success, -1 on error. |
80 |
|
|
*/ |
81 |
|
|
int |
82 |
|
|
eparse(const char *cmd, const char *left, const char *right) |
83 |
|
|
{ |
84 |
|
|
FILE *file; |
85 |
|
|
size_t nread; |
86 |
|
|
int fd; |
87 |
|
84 |
char *filename; |
88 |
|
42 |
char buf[BUFSIZ], *text; |
89 |
|
|
|
90 |
|
|
/* Skip whitespace. */ |
91 |
✗✓ |
84 |
while (isspace((unsigned char)*cmd)) |
92 |
|
|
++cmd; |
93 |
|
|
|
94 |
|
84 |
text = NULL; |
95 |
✗✓✓✗ ✓ |
84 |
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 |
✓✓ |
21 |
if (left == NULL) |
116 |
|
|
break; |
117 |
|
|
|
118 |
✗✓ |
14 |
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 |
✓✓ |
21 |
if (right == NULL) |
127 |
|
|
break; |
128 |
|
|
|
129 |
✗✓ |
14 |
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 |
✗✓ |
42 |
if (asprintf(&filename, "%s/sdiff.XXXXXXXXXX", tmpdir) == -1) |
140 |
|
|
err(2, "asprintf"); |
141 |
✗✓ |
42 |
if ((fd = mkstemp(filename)) == -1) |
142 |
|
|
err(2, "mkstemp"); |
143 |
✓✓ |
42 |
if (text != NULL) { |
144 |
|
|
size_t len; |
145 |
|
|
ssize_t nwritten; |
146 |
|
|
|
147 |
|
28 |
len = strlen(text); |
148 |
✓✗✗✓
|
56 |
if ((nwritten = write(fd, text, len)) == -1 || |
149 |
|
28 |
nwritten != len) { |
150 |
|
|
warn("error writing to temp file"); |
151 |
|
|
cleanup(filename); |
152 |
|
|
} |
153 |
|
28 |
} |
154 |
|
42 |
close(fd); |
155 |
|
|
|
156 |
|
|
/* text is no longer used. */ |
157 |
|
42 |
free(text); |
158 |
|
|
|
159 |
|
|
/* Edit temp file. */ |
160 |
✗✓ |
42 |
if (editit(filename) == -1) { |
161 |
|
|
warn("error editing %s", filename); |
162 |
|
|
cleanup(filename); |
163 |
|
|
} |
164 |
|
|
|
165 |
|
|
/* Open temporary file. */ |
166 |
✗✓ |
42 |
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 |
✓✗✗✓
|
84 |
for (nread = sizeof(buf); nread == sizeof(buf);) { |
173 |
|
|
size_t nwritten; |
174 |
|
|
|
175 |
|
42 |
nread = fread(buf, sizeof(*buf), sizeof(buf), file); |
176 |
|
|
/* Test for error or end of file. */ |
177 |
✓✗✓✗ ✗✗ |
84 |
if (nread != sizeof(buf) && |
178 |
✓✗✓✗ ✗✗✗✓
|
168 |
(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 |
✓✓ |
42 |
if (!nread) |
188 |
|
14 |
break; |
189 |
|
|
|
190 |
|
|
/* Write data we just read. */ |
191 |
|
28 |
nwritten = fwrite(buf, sizeof(*buf), nread, outfp); |
192 |
✗✓ |
28 |
if (nwritten != nread) { |
193 |
|
|
warnx("error writing to output file"); |
194 |
|
|
cleanup(filename); |
195 |
|
|
} |
196 |
|
28 |
} |
197 |
|
|
|
198 |
|
|
/* We've reached the end of the temporary file, so remove it. */ |
199 |
✗✓ |
42 |
if (unlink(filename)) |
200 |
|
|
warn("could not delete: %s", filename); |
201 |
|
42 |
fclose(file); |
202 |
|
|
|
203 |
|
42 |
free(filename); |
204 |
|
|
|
205 |
|
42 |
return (0); |
206 |
|
42 |
} |