1 /* $OpenBSD: edit.c,v 1.21 2019/06/28 13:35:01 deraadt Exp $ */
2 /* $NetBSD: edit.c,v 1.5 1996/06/08 19:48:20 christos Exp $ */
3
4 /*
5 * Copyright (c) 1980, 1993
6 * The Regents of the University of California. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the University nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33 #include <sys/types.h>
34 #include <sys/wait.h>
35
36 #include "rcv.h"
37 #include <errno.h>
38 #include <fcntl.h>
39 #include "extern.h"
40
41 int editit(const char *, const char *);
42
43 /*
44 * Mail -- a mail program
45 *
46 * Perform message editing functions.
47 */
48
49 /*
50 * Edit a message list.
51 */
52 int
editor(void * v)53 editor(void *v)
54 {
55 int *msgvec = v;
56
57 return(edit1(msgvec, 'e'));
58 }
59
60 /*
61 * Invoke the visual editor on a message list.
62 */
63 int
visual(void * v)64 visual(void *v)
65 {
66 int *msgvec = v;
67
68 return(edit1(msgvec, 'v'));
69 }
70
71 /*
72 * Edit a message by writing the message into a funnily-named file
73 * (which should not exist) and forking an editor on it.
74 * We get the editor from the stuff above.
75 */
76 int
edit1(int * msgvec,int type)77 edit1(int *msgvec, int type)
78 {
79 int nl = 0, c, i;
80 FILE *fp;
81 struct sigaction oact;
82 sigset_t oset;
83 struct message *mp;
84 off_t size;
85
86 /*
87 * Deal with each message to be edited . . .
88 */
89 for (i = 0; msgvec[i] && i < msgCount; i++) {
90 if (i > 0) {
91 char buf[100];
92 char *p;
93
94 printf("Edit message %d [ynq]? ", msgvec[i]);
95 if (fgets(buf, sizeof(buf), stdin) == NULL)
96 break;
97 for (p = buf; *p == ' ' || *p == '\t'; p++)
98 ;
99 if (*p == 'q')
100 break;
101 if (*p == 'n')
102 continue;
103 }
104 dot = mp = &message[msgvec[i] - 1];
105 touch(mp);
106 (void)ignoresig(SIGINT, &oact, &oset);
107 fp = run_editor(setinput(mp), (off_t)mp->m_size, type, readonly);
108 if (fp != NULL) {
109 (void)fseek(otf, 0L, SEEK_END);
110 size = ftell(otf);
111 mp->m_block = blockof(size);
112 mp->m_offset = offsetof(size);
113 mp->m_size = fsize(fp);
114 mp->m_lines = 0;
115 mp->m_flag |= MODIFY;
116 rewind(fp);
117 while ((c = getc(fp)) != EOF) {
118 if (c == '\n') {
119 mp->m_lines++;
120 nl++;
121 } else
122 nl = 0;
123 if (putc(c, otf) == EOF)
124 break;
125 }
126 for (; nl < 2; nl++) {
127 mp->m_lines++;
128 mp->m_size++;
129 putc('\n', otf);
130 }
131 if (ferror(otf))
132 warn("%s", tmpdir);
133 (void)Fclose(fp);
134 }
135 (void)sigprocmask(SIG_SETMASK, &oset, NULL);
136 (void)sigaction(SIGINT, &oact, NULL);
137 }
138 return(0);
139 }
140
141 /*
142 * Run an editor on the file at "fpp" of "size" bytes,
143 * and return a new file pointer.
144 * Signals must be handled by the caller.
145 * "Type" is 'e' for _PATH_EX, 'v' for _PATH_VI.
146 */
147 FILE *
run_editor(FILE * fp,off_t size,int type,int readonly)148 run_editor(FILE *fp, off_t size, int type, int readonly)
149 {
150 FILE *nf = NULL;
151 int t;
152 time_t modtime;
153 char *edit, tempname[PATHSIZE];
154 struct stat statb;
155
156 (void)snprintf(tempname, sizeof(tempname),
157 "%s/mail.ReXXXXXXXXXX", tmpdir);
158 if ((t = mkstemp(tempname)) == -1 ||
159 (nf = Fdopen(t, "w")) == NULL) {
160 warn("%s", tempname);
161 goto out;
162 }
163 if (readonly && fchmod(t, 0400) == -1) {
164 warn("%s", tempname);
165 (void)rm(tempname);
166 goto out;
167 }
168 if (size >= 0)
169 while (--size >= 0 && (t = getc(fp)) != EOF)
170 (void)putc(t, nf);
171 else
172 while ((t = getc(fp)) != EOF)
173 (void)putc(t, nf);
174 (void)fflush(nf);
175 if (fstat(fileno(nf), &statb) == -1)
176 modtime = 0;
177 else
178 modtime = statb.st_mtime;
179 if (ferror(nf)) {
180 (void)Fclose(nf);
181 warn("%s", tempname);
182 (void)rm(tempname);
183 nf = NULL;
184 goto out;
185 }
186 if (Fclose(nf) < 0) {
187 warn("%s", tempname);
188 (void)rm(tempname);
189 nf = NULL;
190 goto out;
191 }
192 nf = NULL;
193 if (type == 'e') {
194 edit = value("EDITOR");
195 if (edit == NULL || edit[0] == '\0')
196 edit = _PATH_EX;
197 } else {
198 edit = value("VISUAL");
199 if (edit == NULL || edit[0] == '\0')
200 edit = _PATH_VI;
201 }
202 if (editit(edit, tempname) == -1) {
203 (void)rm(tempname);
204 goto out;
205 }
206 /*
207 * If in read only mode or file unchanged, just remove the editor
208 * temporary and return.
209 */
210 if (readonly) {
211 (void)rm(tempname);
212 goto out;
213 }
214 if (stat(tempname, &statb) == -1) {
215 warn("%s", tempname);
216 goto out;
217 }
218 if (modtime == statb.st_mtime) {
219 (void)rm(tempname);
220 goto out;
221 }
222 /*
223 * Now switch to new file.
224 */
225 if ((nf = Fopen(tempname, "a+")) == NULL) {
226 warn("%s", tempname);
227 (void)rm(tempname);
228 goto out;
229 }
230 (void)rm(tempname);
231 out:
232 return(nf);
233 }
234
235 /*
236 * Execute an editor on the specified pathname, which is interpreted
237 * from the shell. This means flags may be included.
238 *
239 * Returns -1 on error, or the exit value on success.
240 */
241 int
editit(const char * ed,const char * pathname)242 editit(const char *ed, const char *pathname)
243 {
244 char *argp[] = {"sh", "-c", NULL, NULL}, *p;
245 sig_t sighup, sigint, sigquit, sigchld;
246 pid_t pid;
247 int saved_errno, st, ret = -1;
248
249 if (ed == NULL)
250 ed = getenv("VISUAL");
251 if (ed == NULL || ed[0] == '\0')
252 ed = getenv("EDITOR");
253 if (ed == NULL || ed[0] == '\0')
254 ed = _PATH_VI;
255 if (asprintf(&p, "%s %s", ed, pathname) == -1)
256 return (-1);
257 argp[2] = p;
258
259 sighup = signal(SIGHUP, SIG_IGN);
260 sigint = signal(SIGINT, SIG_IGN);
261 sigquit = signal(SIGQUIT, SIG_IGN);
262 sigchld = signal(SIGCHLD, SIG_DFL);
263 if ((pid = fork()) == -1)
264 goto fail;
265 if (pid == 0) {
266 execv(_PATH_BSHELL, argp);
267 _exit(127);
268 }
269 while (waitpid(pid, &st, 0) == -1)
270 if (errno != EINTR)
271 goto fail;
272 if (!WIFEXITED(st))
273 errno = EINTR;
274 else
275 ret = WEXITSTATUS(st);
276
277 fail:
278 saved_errno = errno;
279 (void)signal(SIGHUP, sighup);
280 (void)signal(SIGINT, sigint);
281 (void)signal(SIGQUIT, sigquit);
282 (void)signal(SIGCHLD, sigchld);
283 free(p);
284 errno = saved_errno;
285 return (ret);
286 }
287