expand() filenames given to the ~@ tilde escape..
[s-mailx.git] / edit.c
blob39c719b02b760215b6fd78414bc622adb85866ce
1 /*
2 * Heirloom mailx - a mail user agent derived from Berkeley Mail.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 */
6 /*
7 * Copyright (c) 1980, 1993
8 * The Regents of the University of California. All rights reserved.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the University of
21 * California, Berkeley and its contributors.
22 * 4. Neither the name of the University nor the names of its contributors
23 * may be used to endorse or promote products derived from this software
24 * without specific prior written permission.
26 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * SUCH DAMAGE.
39 #ifndef lint
40 #ifdef DOSCCS
41 static char sccsid[] = "@(#)edit.c 2.24 (gritter) 3/4/06";
42 #endif
43 #endif /* not lint */
45 #include "rcv.h"
46 #include "extern.h"
47 #include <sys/stat.h>
48 #include <unistd.h>
51 * Mail -- a mail program
53 * Perform message editing functions.
56 static int edit1(int *msgvec, int type);
59 * Edit a message list.
61 int
62 editor(void *v)
64 int *msgvec = v;
66 return edit1(msgvec, 'e');
70 * Invoke the visual editor on a message list.
72 int
73 visual(void *v)
75 int *msgvec = v;
77 return edit1(msgvec, 'v');
81 * Edit a message by writing the message into a funnily-named file
82 * (which should not exist) and forking an editor on it.
83 * We get the editor from the stuff above.
85 static int
86 edit1(int *msgvec, int type)
88 int c;
89 int i;
90 FILE *fp = NULL;
91 struct message *mp;
92 off_t size;
93 char *line = NULL;
94 size_t linesize;
95 int wb;
98 * Deal with each message to be edited . . .
100 wb = value("writebackedited") != NULL;
101 for (i = 0; msgvec[i] && i < msgCount; i++) {
102 sighandler_type sigint;
104 if (i > 0) {
105 char *p;
107 printf(catgets(catd, CATSET, 72,
108 "Edit message %d [ynq]? "), msgvec[i]);
109 fflush(stdout);
110 if (readline(stdin, &line, &linesize) < 0)
111 break;
112 for (p = line; blankchar(*p & 0377); p++);
113 if (*p == 'q')
114 break;
115 if (*p == 'n')
116 continue;
118 setdot(mp = &message[msgvec[i] - 1]);
119 did_print_dot = 1;
120 touch(mp);
121 sigint = safe_signal(SIGINT, SIG_IGN);
122 fp = run_editor(fp, mp->m_size, type,
123 (mb.mb_perm & MB_EDIT) == 0 || !wb,
124 NULL, mp, wb ? SEND_MBOX : SEND_TODISP_ALL,
125 sigint);
126 if (fp != NULL) {
127 fseek(mb.mb_otf, 0L, SEEK_END);
128 size = ftell(mb.mb_otf);
129 mp->m_block = mailx_blockof(size);
130 mp->m_offset = mailx_offsetof(size);
131 mp->m_size = fsize(fp);
132 mp->m_lines = 0;
133 mp->m_flag |= MODIFY;
134 rewind(fp);
135 while ((c = getc(fp)) != EOF) {
136 if (c == '\n')
137 mp->m_lines++;
138 if (putc(c, mb.mb_otf) == EOF)
139 break;
141 if (ferror(mb.mb_otf))
142 perror("/tmp");
143 Fclose(fp);
145 safe_signal(SIGINT, sigint);
147 if (line)
148 free(line);
149 return 0;
153 * Run an editor on the file at "fpp" of "size" bytes,
154 * and return a new file pointer.
155 * Signals must be handled by the caller.
156 * "Type" is 'e' for ed, 'v' for vi.
158 FILE *
159 run_editor(FILE *fp, off_t size, int type, int readonly,
160 struct header *hp, struct message *mp, enum sendaction action,
161 sighandler_type oldint)
163 FILE *nf = NULL;
164 int t;
165 time_t modtime;
166 char *edit;
167 struct stat statb;
168 char *tempEdit;
169 sigset_t set;
171 if ((nf = Ftemp(&tempEdit, "Re", "w", readonly ? 0400 : 0600, 1))
172 == NULL) {
173 perror(catgets(catd, CATSET, 73, "temporary mail edit file"));
174 goto out;
176 if (hp) {
177 t = GTO|GSUBJECT|GCC|GBCC|GNL|GCOMMA;
178 if (hp->h_from || hp->h_replyto || hp->h_sender ||
179 hp->h_organization)
180 t |= GIDENT;
181 puthead(hp, nf, t, SEND_TODISP, CONV_NONE, NULL, NULL);
183 if (mp) {
184 send(mp, nf, 0, NULL, action, NULL);
185 } else {
186 if (size >= 0)
187 while (--size >= 0 && (t = getc(fp)) != EOF)
188 putc(t, nf);
189 else
190 while ((t = getc(fp)) != EOF)
191 putc(t, nf);
193 fflush(nf);
194 if (fstat(fileno(nf), &statb) < 0)
195 modtime = 0;
196 else
197 modtime = statb.st_mtime;
198 if (ferror(nf)) {
199 Fclose(nf);
200 perror(tempEdit);
201 unlink(tempEdit);
202 Ftfree(&tempEdit);
203 nf = NULL;
204 goto out;
206 if (Fclose(nf) < 0) {
207 perror(tempEdit);
208 unlink(tempEdit);
209 Ftfree(&tempEdit);
210 nf = NULL;
211 goto out;
213 nf = NULL;
214 if ((edit = value(type == 'e' ? "EDITOR" : "VISUAL")) == NULL)
215 edit = type == 'e' ? "ed" : "vi";
216 sigemptyset(&set);
217 if (run_command(edit, oldint != SIG_IGN ? &set : NULL, -1, -1,
218 tempEdit, NULL, NULL) < 0) {
219 unlink(tempEdit);
220 Ftfree(&tempEdit);
221 goto out;
224 * If in read only mode or file unchanged, just remove the editor
225 * temporary and return.
227 if (readonly) {
228 unlink(tempEdit);
229 Ftfree(&tempEdit);
230 goto out;
232 if (stat(tempEdit, &statb) < 0) {
233 perror(tempEdit);
234 Ftfree(&tempEdit);
235 goto out;
237 if (modtime == statb.st_mtime) {
238 unlink(tempEdit);
239 Ftfree(&tempEdit);
240 goto out;
243 * Now switch to new file.
245 if ((nf = Fopen(tempEdit, "a+")) == NULL)
246 perror(tempEdit);
247 out:
248 if (tempEdit) {
249 unlink(tempEdit);
250 Ftfree(&tempEdit);
252 return nf;