1181834Sroberto/*
2181834Sroberto * Copyright (c) 1980, 1993
3181834Sroberto *	The Regents of the University of California.  All rights reserved.
4181834Sroberto *
5285612Sdelphij * Redistribution and use in source and binary forms, with or without
6181834Sroberto * modification, are permitted provided that the following conditions
7181834Sroberto * are met:
8181834Sroberto * 1. Redistributions of source code must retain the above copyright
9285612Sdelphij *    notice, this list of conditions and the following disclaimer.
10285612Sdelphij * 2. Redistributions in binary form must reproduce the above copyright
11285612Sdelphij *    notice, this list of conditions and the following disclaimer in the
12285612Sdelphij *    documentation and/or other materials provided with the distribution.
13285612Sdelphij * 4. Neither the name of the University nor the names of its contributors
14181834Sroberto *    may be used to endorse or promote products derived from this software
15181834Sroberto *    without specific prior written permission.
16181834Sroberto *
17285612Sdelphij * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18285612Sdelphij * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19285612Sdelphij * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20285612Sdelphij * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21181834Sroberto * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22285612Sdelphij * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23181834Sroberto * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24181834Sroberto * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25181834Sroberto * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26285612Sdelphij * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27285612Sdelphij * SUCH DAMAGE.
28285612Sdelphij */
29181834Sroberto
30285612Sdelphij#ifndef lint
31285612Sdelphij#if 0
32285612Sdelphijstatic char sccsid[] = "@(#)edit.c	8.1 (Berkeley) 6/6/93";
33285612Sdelphij#endif
34285612Sdelphij#endif /* not lint */
35285612Sdelphij#include <sys/cdefs.h>
36285612Sdelphij__FBSDID("$FreeBSD: releng/11.0/usr.bin/mail/edit.c 270256 2014-08-21 02:40:33Z pfg $");
37285612Sdelphij
38285612Sdelphij#include "rcv.h"
39285612Sdelphij#include <fcntl.h>
40285612Sdelphij#include "extern.h"
41181834Sroberto
42285612Sdelphij/*
43285612Sdelphij * Mail -- a mail program
44285612Sdelphij *
45285612Sdelphij * Perform message editing functions.
46181834Sroberto */
47181834Sroberto
48181834Sroberto/*
49181834Sroberto * Edit a message list.
50181834Sroberto */
51181834Srobertoint
52181834Srobertoeditor(int *msgvec)
53181834Sroberto{
54181834Sroberto
55285612Sdelphij	return (edit1(msgvec, 'e'));
56285612Sdelphij}
57181834Sroberto
58181834Sroberto/*
59181834Sroberto * Invoke the visual editor on a message list.
60181834Sroberto */
61181834Srobertoint
62181834Srobertovisual(int *msgvec)
63181834Sroberto{
64181834Sroberto
65181834Sroberto	return (edit1(msgvec, 'v'));
66181834Sroberto}
67181834Sroberto
68181834Sroberto/*
69181834Sroberto * Edit a message by writing the message into a funnily-named file
70181834Sroberto * (which should not exist) and forking an editor on it.
71181834Sroberto * We get the editor from the stuff above.
72285612Sdelphij */
73181834Srobertoint
74181834Srobertoedit1(int *msgvec, int type)
75181834Sroberto{
76181834Sroberto	int c, i;
77181834Sroberto	FILE *fp;
78285612Sdelphij	struct message *mp;
79285612Sdelphij	off_t size;
80181834Sroberto
81181834Sroberto	/*
82181834Sroberto	 * Deal with each message to be edited . . .
83181834Sroberto	 */
84285612Sdelphij	for (i = 0; i < msgCount && msgvec[i]; i++) {
85181834Sroberto		sig_t sigint;
86181834Sroberto
87181834Sroberto		if (i > 0) {
88285612Sdelphij			char buf[100];
89181834Sroberto			char *p;
90181834Sroberto
91285612Sdelphij			printf("Edit message %d [ynq]? ", msgvec[i]);
92181834Sroberto			if (fgets(buf, sizeof(buf), stdin) == NULL)
93181834Sroberto				break;
94181834Sroberto			for (p = buf; *p == ' ' || *p == '\t'; p++)
95181834Sroberto				;
96181834Sroberto			if (*p == 'q')
97181834Sroberto				break;
98285612Sdelphij			if (*p == 'n')
99181834Sroberto				continue;
100181834Sroberto		}
101181834Sroberto		dot = mp = &message[msgvec[i] - 1];
102285612Sdelphij		touch(mp);
103181834Sroberto		sigint = signal(SIGINT, SIG_IGN);
104181834Sroberto		fp = run_editor(setinput(mp), mp->m_size, type, readonly);
105181834Sroberto		if (fp != NULL) {
106181834Sroberto			(void)fseeko(otf, (off_t)0, SEEK_END);
107181834Sroberto			size = ftello(otf);
108181834Sroberto			mp->m_block = blockof(size);
109181834Sroberto			mp->m_offset = boffsetof(size);
110285612Sdelphij			mp->m_size = (long)fsize(fp);
111285612Sdelphij			mp->m_lines = 0;
112181834Sroberto			mp->m_flag |= MODIFY;
113285612Sdelphij			rewind(fp);
114181834Sroberto			while ((c = getc(fp)) != EOF) {
115181834Sroberto				if (c == '\n')
116181834Sroberto					mp->m_lines++;
117181834Sroberto				if (putc(c, otf) == EOF)
118289997Sglebius					break;
119289997Sglebius			}
120289997Sglebius			if (ferror(otf))
121181834Sroberto				warnx("/tmp");
122181834Sroberto			(void)Fclose(fp);
123181834Sroberto		}
124289997Sglebius		(void)signal(SIGINT, sigint);
125289997Sglebius	}
126289997Sglebius	return (0);
127181834Sroberto}
128285612Sdelphij
129181834Sroberto/*
130181834Sroberto * Run an editor on the file at "fpp" of "size" bytes,
131181834Sroberto * and return a new file pointer.
132181834Sroberto * Signals must be handled by the caller.
133181834Sroberto * "Type" is 'e' for _PATH_EX, 'v' for _PATH_VI.
134181834Sroberto */
135289997SglebiusFILE *
136289997Sglebiusrun_editor(FILE *fp, off_t size, int type, int readonly)
137289997Sglebius{
138181834Sroberto	FILE *nf = NULL;
139181834Sroberto	int t;
140181834Sroberto	time_t modtime;
141181834Sroberto	char *edit, tempname[PATHSIZE];
142181834Sroberto	struct stat statb;
143181834Sroberto
144181834Sroberto	(void)snprintf(tempname, sizeof(tempname),
145181834Sroberto	    "%s/mail.ReXXXXXXXXXX", tmpdir);
146181834Sroberto	if ((t = mkstemp(tempname)) == -1 ||
147181834Sroberto	    (nf = Fdopen(t, "w")) == NULL) {
148181834Sroberto		warn("%s", tempname);
149181834Sroberto		goto out;
150181834Sroberto	}
151181834Sroberto	if (readonly && fchmod(t, 0400) == -1) {
152181834Sroberto		warn("%s", tempname);
153181834Sroberto		(void)rm(tempname);
154181834Sroberto		goto out;
155181834Sroberto	}
156181834Sroberto	if (size >= 0)
157181834Sroberto		while (--size >= 0 && (t = getc(fp)) != EOF)
158181834Sroberto			(void)putc(t, nf);
159285612Sdelphij	else
160181834Sroberto		while ((t = getc(fp)) != EOF)
161181834Sroberto			(void)putc(t, nf);
162181834Sroberto	(void)fflush(nf);
163181834Sroberto	if (fstat(fileno(nf), &statb) < 0)
164181834Sroberto		modtime = 0;
165181834Sroberto	else
166181834Sroberto		modtime = statb.st_mtime;
167289997Sglebius	if (ferror(nf)) {
168289997Sglebius		(void)Fclose(nf);
169289997Sglebius		warnx("%s", tempname);
170181834Sroberto		(void)rm(tempname);
171181834Sroberto		nf = NULL;
172181834Sroberto		goto out;
173181834Sroberto	}
174181834Sroberto	if (Fclose(nf) < 0) {
175181834Sroberto		warn("%s", tempname);
176181834Sroberto		(void)rm(tempname);
177181834Sroberto		nf = NULL;
178181834Sroberto		goto out;
179181834Sroberto	}
180181834Sroberto	nf = NULL;
181181834Sroberto	if ((edit = value(type == 'e' ? "EDITOR" : "VISUAL")) == NULL)
182181834Sroberto		edit = type == 'e' ? _PATH_EX : _PATH_VI;
183181834Sroberto	if (run_command(edit, 0, -1, -1, tempname, NULL, NULL) < 0) {
184181834Sroberto		(void)rm(tempname);
185181834Sroberto		goto out;
186181834Sroberto	}
187181834Sroberto	/*
188181834Sroberto	 * If in read only mode or file unchanged, just remove the editor
189181834Sroberto	 * temporary and return.
190181834Sroberto	 */
191181834Sroberto	if (readonly) {
192181834Sroberto		(void)rm(tempname);
193181834Sroberto		goto out;
194181834Sroberto	}
195181834Sroberto	if (stat(tempname, &statb) < 0) {
196181834Sroberto		warn("%s", tempname);
197181834Sroberto		goto out;
198181834Sroberto	}
199181834Sroberto	if (modtime == statb.st_mtime) {
200181834Sroberto		(void)rm(tempname);
201181834Sroberto		goto out;
202181834Sroberto	}
203181834Sroberto	/*
204181834Sroberto	 * Now switch to new file.
205181834Sroberto	 */
206181834Sroberto	if ((nf = Fopen(tempname, "a+")) == NULL) {
207181834Sroberto		warn("%s", tempname);
208181834Sroberto		(void)rm(tempname);
209181834Sroberto		goto out;
210181834Sroberto	}
211181834Sroberto	(void)rm(tempname);
212181834Srobertoout:
213181834Sroberto	return (nf);
214181834Sroberto}
215181834Sroberto