1/*-
2 * SPDX-License-Identifier: BSD-3-Clause
3 *
4 * Copyright (c) 1980, 1993
5 *	The Regents of the University of California.  All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 *    may be used to endorse or promote products derived from this software
17 *    without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32#ifndef lint
33#if 0
34static char sccsid[] = "@(#)edit.c	8.1 (Berkeley) 6/6/93";
35#endif
36#endif /* not lint */
37#include <sys/cdefs.h>
38__FBSDID("$FreeBSD$");
39
40#include "rcv.h"
41#include <fcntl.h>
42#include "extern.h"
43
44/*
45 * Mail -- a mail program
46 *
47 * Perform message editing functions.
48 */
49
50/*
51 * Edit a message list.
52 */
53int
54editor(int *msgvec)
55{
56
57	return (edit1(msgvec, 'e'));
58}
59
60/*
61 * Invoke the visual editor on a message list.
62 */
63int
64visual(int *msgvec)
65{
66
67	return (edit1(msgvec, 'v'));
68}
69
70/*
71 * Edit a message by writing the message into a funnily-named file
72 * (which should not exist) and forking an editor on it.
73 * We get the editor from the stuff above.
74 */
75int
76edit1(int *msgvec, int type)
77{
78	int c, i;
79	FILE *fp;
80	struct message *mp;
81	off_t size;
82
83	/*
84	 * Deal with each message to be edited . . .
85	 */
86	for (i = 0; i < msgCount && msgvec[i]; i++) {
87		sig_t sigint;
88
89		if (i > 0) {
90			char buf[100];
91			char *p;
92
93			printf("Edit message %d [ynq]? ", msgvec[i]);
94			if (fgets(buf, sizeof(buf), stdin) == NULL)
95				break;
96			for (p = buf; *p == ' ' || *p == '\t'; p++)
97				;
98			if (*p == 'q')
99				break;
100			if (*p == 'n')
101				continue;
102		}
103		dot = mp = &message[msgvec[i] - 1];
104		touch(mp);
105		sigint = signal(SIGINT, SIG_IGN);
106		fp = run_editor(setinput(mp), mp->m_size, type, readonly);
107		if (fp != NULL) {
108			(void)fseeko(otf, (off_t)0, SEEK_END);
109			size = ftello(otf);
110			mp->m_block = blockof(size);
111			mp->m_offset = boffsetof(size);
112			mp->m_size = (long)fsize(fp);
113			mp->m_lines = 0;
114			mp->m_flag |= MODIFY;
115			rewind(fp);
116			while ((c = getc(fp)) != EOF) {
117				if (c == '\n')
118					mp->m_lines++;
119				if (putc(c, otf) == EOF)
120					break;
121			}
122			if (ferror(otf))
123				warnx("/tmp");
124			(void)Fclose(fp);
125		}
126		(void)signal(SIGINT, sigint);
127	}
128	return (0);
129}
130
131/*
132 * Run an editor on the file at "fpp" of "size" bytes,
133 * and return a new file pointer.
134 * Signals must be handled by the caller.
135 * "Type" is 'e' for _PATH_EX, 'v' for _PATH_VI.
136 */
137FILE *
138run_editor(FILE *fp, off_t size, int type, int readonly)
139{
140	FILE *nf = NULL;
141	int t;
142	time_t modtime;
143	char *edit, tempname[PATHSIZE];
144	struct stat statb;
145
146	(void)snprintf(tempname, sizeof(tempname),
147	    "%s/mail.ReXXXXXXXXXX", tmpdir);
148	if ((t = mkstemp(tempname)) == -1 ||
149	    (nf = Fdopen(t, "w")) == NULL) {
150		warn("%s", tempname);
151		goto out;
152	}
153	if (readonly && fchmod(t, 0400) == -1) {
154		warn("%s", tempname);
155		(void)rm(tempname);
156		goto out;
157	}
158	if (size >= 0)
159		while (--size >= 0 && (t = getc(fp)) != EOF)
160			(void)putc(t, nf);
161	else
162		while ((t = getc(fp)) != EOF)
163			(void)putc(t, nf);
164	(void)fflush(nf);
165	if (fstat(fileno(nf), &statb) < 0)
166		modtime = 0;
167	else
168		modtime = statb.st_mtime;
169	if (ferror(nf)) {
170		(void)Fclose(nf);
171		warnx("%s", tempname);
172		(void)rm(tempname);
173		nf = NULL;
174		goto out;
175	}
176	if (Fclose(nf) < 0) {
177		warn("%s", tempname);
178		(void)rm(tempname);
179		nf = NULL;
180		goto out;
181	}
182	nf = NULL;
183	if ((edit = value(type == 'e' ? "EDITOR" : "VISUAL")) == NULL)
184		edit = type == 'e' ? _PATH_EX : _PATH_VI;
185	if (run_command(edit, 0, -1, -1, tempname, NULL) < 0) {
186		(void)rm(tempname);
187		goto out;
188	}
189	/*
190	 * If in read only mode or file unchanged, just remove the editor
191	 * temporary and return.
192	 */
193	if (readonly) {
194		(void)rm(tempname);
195		goto out;
196	}
197	if (stat(tempname, &statb) < 0) {
198		warn("%s", tempname);
199		goto out;
200	}
201	if (modtime == statb.st_mtime) {
202		(void)rm(tempname);
203		goto out;
204	}
205	/*
206	 * Now switch to new file.
207	 */
208	if ((nf = Fopen(tempname, "a+")) == NULL) {
209		warn("%s", tempname);
210		(void)rm(tempname);
211		goto out;
212	}
213	(void)rm(tempname);
214out:
215	return (nf);
216}
217