(BWDIC!) *user*: enforce it is not empty
[s-mailx.git] / edit.c
blobf499eca9a6e85b0687fa2963c6c2e98fcf2d2ccf
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Perform message editing functions.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2018 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
6 */
7 /*
8 * Copyright (c) 1980, 1993
9 * The Regents of the University of California. All rights reserved.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
35 #undef n_FILE
36 #define n_FILE edit
38 #ifndef HAVE_AMALGAMATION
39 # include "nail.h"
40 #endif
42 /* Edit a message by writing the message into a funnily-named file (which
43 * should not exist) and forking an editor on it */
44 static int edit1(int *msgvec, int viored);
46 static int
47 edit1(int *msgvec, int viored)
49 int c, i;
50 FILE *fp = NULL;
51 struct message *mp;
52 off_t size;
53 bool_t wb, lastnl;
54 NYD_ENTER;
56 wb = ok_blook(writebackedited);
58 /* Deal with each message to be edited... */
59 for (i = 0; msgvec[i] != 0 && i < msgCount; ++i) {
60 sighandler_type sigint;
62 if(i > 0){
63 char prompt[64];
65 snprintf(prompt, sizeof prompt, _("Edit message %d"), msgvec[i]);
66 if(!getapproval(prompt, TRU1))
67 continue;
70 mp = message + msgvec[i] - 1;
71 setdot(mp);
72 n_pstate |= n_PS_DID_PRINT_DOT;
73 touch(mp);
75 sigint = safe_signal(SIGINT, SIG_IGN);
77 --mp->m_size; /* Strip final NL.. TODO MAILVFS->MESSAGE->length() */
78 fp = n_run_editor(fp, -1/*mp->m_size TODO */, viored,
79 ((mb.mb_perm & MB_EDIT) == 0 || !wb), NULL, mp,
80 (wb ? SEND_MBOX : SEND_TODISP_ALL), sigint, NULL);
81 ++mp->m_size; /* And readd it TODO */
83 if (fp != NULL) {
84 fseek(mb.mb_otf, 0L, SEEK_END);
85 size = ftell(mb.mb_otf);
86 mp->m_block = mailx_blockof(size);
87 mp->m_offset = mailx_offsetof(size);
88 mp->m_lines = 0;
89 mp->m_flag |= MODIFY;
90 rewind(fp);
91 lastnl = 0;
92 size = 0;
93 while ((c = getc(fp)) != EOF) {
94 if ((lastnl = (c == '\n')))
95 ++mp->m_lines;
96 if (putc(c, mb.mb_otf) == EOF)
97 break;
98 ++size;
100 if (!lastnl && putc('\n', mb.mb_otf) != EOF)
101 ++size;
102 if (putc('\n', mb.mb_otf) != EOF)
103 ++size;
104 mp->m_size = (size_t)size;
105 if (ferror(mb.mb_otf))
106 n_perr(_("/tmp"), 0);
107 Fclose(fp);
110 safe_signal(SIGINT, sigint);
112 NYD_LEAVE;
113 return 0;
116 FL int
117 c_editor(void *v)
119 int *msgvec = v, rv;
120 NYD_ENTER;
122 rv = edit1(msgvec, 'e');
123 NYD_LEAVE;
124 return rv;
127 FL int
128 c_visual(void *v)
130 int *msgvec = v, rv;
131 NYD_ENTER;
133 rv = edit1(msgvec, 'v');
134 NYD_LEAVE;
135 return rv;
138 FL FILE *
139 n_run_editor(FILE *fp, off_t size, int viored, bool_t readonly,
140 struct header *hp, struct message *mp, enum sendaction action,
141 sighandler_type oldint, char const *pipecmd)
143 struct stat statb;
144 sigset_t cset;
145 int t, ws;
146 time_t modtime;
147 off_t modsize;
148 char *tmp_name;
149 FILE *nf, *nf_pipetmp, *nf_tmp;
150 NYD_ENTER;
152 nf = nf_pipetmp = NULL;
153 tmp_name = NULL;
154 modtime = 0, modsize = 0;
156 if((nf_tmp = Ftmp(&tmp_name, "runed",
157 ((viored == '|' ? OF_RDWR : OF_WRONLY) | OF_REGISTER |
158 OF_REGISTER_UNLINK | OF_FN_AUTOREC))) == NULL){
159 jetempo:
160 n_perr(_("temporary mail edit file"), 0);
161 goto jleave;
164 if(hp != NULL){
165 assert(mp == NULL);
166 if(!n_header_put4compose(nf_tmp, hp))
167 goto jleave;
170 if(mp != NULL){
171 assert(hp == NULL);
172 if(sendmp(mp, nf_tmp, NULL, NULL, action, NULL) < 0){
173 n_err(_("Failed to prepare editable message\n"));
174 goto jleave;
176 }else{
177 if(size >= 0){
178 while(--size >= 0 && (t = getc(fp)) != EOF)
179 if(putc(t, nf_tmp) == EOF)
180 break;
181 }else{
182 while((t = getc(fp)) != EOF)
183 if(putc(t, nf_tmp) == EOF)
184 break;
188 fflush(nf_tmp);
190 if((t = (fp != NULL && ferror(fp))) == 0 && (t = ferror(nf_tmp)) == 0){
191 if(viored != '|'){
192 if(!fstat(fileno(nf_tmp), &statb))
193 modtime = statb.st_mtime, modsize = statb.st_size;
195 if(readonly)
196 t = (fchmod(fileno(nf_tmp), S_IRUSR) != 0);
200 if(t != 0){
201 n_perr(tmp_name, 0);
202 goto jleave;
205 if(viored == '|'){
206 assert(pipecmd != NULL);
207 tmp_name = NULL;
208 if((nf_pipetmp = Ftmp(&tmp_name, "runed", OF_WRONLY | OF_REGISTER |
209 OF_REGISTER_UNLINK | OF_FN_AUTOREC)) == NULL)
210 goto jetempo;
211 really_rewind(nf = nf_tmp);
212 nf_tmp = nf_pipetmp;
213 nf_pipetmp = nf;
214 nf = NULL;
215 if(n_child_run(ok_vlook(SHELL), 0, fileno(nf_pipetmp), fileno(nf_tmp),
216 "-c", pipecmd, NULL, NULL, &ws) < 0 || WEXITSTATUS(ws) != 0)
217 goto jleave;
218 }else{
219 sigemptyset(&cset);
220 if(n_child_run((viored == 'e' ? ok_vlook(EDITOR) : ok_vlook(VISUAL)),
221 (oldint != SIG_IGN ? &cset : NULL),
222 n_CHILD_FD_PASS, n_CHILD_FD_PASS, tmp_name, NULL, NULL, NULL,
223 &ws) < 0 || WEXITSTATUS(ws) != 0)
224 goto jleave;
227 /* If in read only mode or file unchanged, just remove the editor temporary
228 * and return. Otherwise switch to new file */
229 if(viored != '|'){
230 if(readonly)
231 goto jleave;
232 if(stat(tmp_name, &statb) == -1){
233 n_perr(tmp_name, 0);
234 goto jleave;
236 if(modtime == statb.st_mtime && modsize == statb.st_size)
237 goto jleave;
240 if((nf = Fopen(tmp_name, "r+")) == NULL)
241 n_perr(tmp_name, 0);
243 jleave:
244 if(nf_pipetmp != NULL)
245 Fclose(nf_pipetmp);
246 if(nf_tmp != NULL && Fclose(nf_tmp) < 0){
247 if(tmp_name != NULL)
248 n_perr(tmp_name, 0);
249 if(nf != NULL)
250 Fclose(nf);
251 nf = NULL;
253 NYD_LEAVE;
254 return nf;
257 /* s-it-mode */