Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
att
GitHub Repository: att/ast
Path: blob/master/src/cmd/mailx/edit.c
1808 views
1
/***********************************************************************
2
* *
3
* This software is part of the BSD package *
4
*Copyright (c) 1978-2010 The Regents of the University of California an*
5
* *
6
* Redistribution and use in source and binary forms, with or *
7
* without modification, are permitted provided that the following *
8
* conditions are met: *
9
* *
10
* 1. Redistributions of source code must retain the above *
11
* copyright notice, this list of conditions and the *
12
* following disclaimer. *
13
* *
14
* 2. Redistributions in binary form must reproduce the above *
15
* copyright notice, this list of conditions and the *
16
* following disclaimer in the documentation and/or other *
17
* materials provided with the distribution. *
18
* *
19
* 3. Neither the name of The Regents of the University of California*
20
* names of its contributors may be used to endorse or *
21
* promote products derived from this software without *
22
* specific prior written permission. *
23
* *
24
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND *
25
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, *
26
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF *
27
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE *
28
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS *
29
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, *
30
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED *
31
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, *
32
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON *
33
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, *
34
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY *
35
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE *
36
* POSSIBILITY OF SUCH DAMAGE. *
37
* *
38
* Redistribution and use in source and binary forms, with or without *
39
* modification, are permitted provided that the following conditions *
40
* are met: *
41
* 1. Redistributions of source code must retain the above copyright *
42
* notice, this list of conditions and the following disclaimer. *
43
* 2. Redistributions in binary form must reproduce the above copyright *
44
* notice, this list of conditions and the following disclaimer in *
45
* the documentation and/or other materials provided with the *
46
* distribution. *
47
* 3. Neither the name of the University nor the names of its *
48
* contributors may be used to endorse or promote products derived *
49
* from this software without specific prior written permission. *
50
* *
51
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" *
52
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED *
53
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
54
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS *
55
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, *
56
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT *
57
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF *
58
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND *
59
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, *
60
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT *
61
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF *
62
* SUCH DAMAGE. *
63
* *
64
* Kurt Shoens (UCB) *
65
* gsf *
66
* *
67
***********************************************************************/
68
#pragma prototyped
69
/*
70
* Mail -- a mail program
71
*
72
* Perform message editing functions.
73
*/
74
75
#include "mailx.h"
76
77
/*
78
* Edit a message list.
79
*/
80
static int
81
edit1(struct msg* msgvec, int type)
82
{
83
register struct msg* ip;
84
register struct msg* mp;
85
FILE* fp;
86
off_t size;
87
sig_t sigint;
88
89
/*
90
* Deal with each message to be edited . . .
91
*/
92
for (ip = msgvec; ip->m_index; ip++) {
93
if (ip > msgvec) {
94
char buf[100];
95
char* p;
96
97
note(PROMPT, "Edit message %d [ynq]? ", ip->m_index);
98
if (!fgets(buf, sizeof buf, stdin))
99
break;
100
for (p = buf; isspace(*p); p++);
101
if (*p == 'q' || *p == 'Q')
102
break;
103
if (*p == 'n' || *p == 'N')
104
continue;
105
}
106
state.msg.dot = mp = state.msg.list + ip->m_index - 1;
107
touchmsg(mp);
108
sigint = signal(SIGINT, SIG_IGN);
109
if (fp = run_editor(setinput(mp), mp->m_size, NiL, type, state.readonly)) {
110
if (!state.msg.op)
111
settmp(NiL, 0);
112
fseek(state.msg.op, (off_t)0, SEEK_END);
113
size = ftell(state.msg.op);
114
mp->m_block = blocknumber(size);
115
mp->m_offset = blockoffset(size);
116
msgflags(mp, MODIFY, 0);
117
rewind(fp);
118
filecopy(NiL, fp, state.tmp.dir, state.msg.op, NiL, (off_t)0, &mp->m_lines, &mp->m_size, 0);
119
fileclose(fp);
120
}
121
signal(SIGINT, sigint);
122
}
123
return 0;
124
}
125
126
/*
127
* Edit a message list.
128
*/
129
int
130
editor(struct msg* msgvec)
131
{
132
return edit1(msgvec, 'e');
133
}
134
135
/*
136
* Invoke the visual editor on a message list.
137
*/
138
int
139
visual(struct msg* msgvec)
140
{
141
return edit1(msgvec, 'v');
142
}
143
144
/*
145
* Run an editor on the file at "fp" of "size" bytes,
146
* and return a new file pointer.
147
* Signals must be handled by the caller.
148
* "Type" is 'e' for state.var.editor, 'v' for state.var.visual.
149
*/
150
FILE*
151
run_editor(register FILE* fp, off_t size, struct header* hp, int type, int readonly)
152
{
153
FILE* ep;
154
time_t modtime;
155
int lc;
156
int err;
157
char* edit;
158
unsigned long editheaders = 0;
159
struct parse pp;
160
161
/*
162
* Create and copy to the temporary file.
163
*/
164
if (!(ep = fileopen(state.tmp.edit, "EMw")))
165
goto ret1;
166
if (size) {
167
if (size < 0)
168
size = 0;
169
if (hp && state.var.editheaders) {
170
editheaders = GEDIT|GRULE;
171
headout(ep, hp, editheaders|GNL);
172
}
173
if (filecopy(NiL, fp, state.tmp.edit, ep, NiL, size, NiL, NiL, 0))
174
goto ret2;
175
}
176
modtime = state.openstat.st_mtime;
177
fileclose(ep);
178
ep = 0;
179
/*
180
* Edit the file.
181
*/
182
edit = type == 'e' ? state.var.editor : state.var.visual;
183
err = run_command(edit, 0, -1, -1, state.tmp.edit, NiL, NiL) < 0;
184
/*
185
* If in readonly mode or file unchanged, clean up and return.
186
*/
187
if (readonly)
188
goto ret2;
189
if (stat(state.tmp.edit, &state.openstat) < 0)
190
goto ret1;
191
if (modtime == state.openstat.st_mtime)
192
goto ret2;
193
if (err)
194
note(0, "%s did not exit normally but did do some changes -- you may want to re-edit", edit);
195
/*
196
* Now, switch to the temporary file.
197
*/
198
if (!(ep = fileopen(state.tmp.edit, "Ea+")))
199
goto ret2;
200
if (editheaders && headset(&pp, NiL, ep, hp, NiL, editheaders|GTO|GMETOO)) {
201
while (headget(&pp));
202
remove(state.tmp.edit);
203
if (!(ep = fileopen(state.tmp.edit, "EMa+")))
204
goto ret1;
205
filecopy(NiL, pp.fp, state.tmp.edit, ep, NiL, (off_t)0, NiL, NiL, 0);
206
fileclose(pp.fp);
207
}
208
/*
209
* Ensure that the tempEdit file ends with two newlines.
210
*
211
* XXX
212
* Probably ought to have a `From' line, as well.
213
*
214
* XXX
215
* If the file is only a single byte long, the seek is going to
216
* fail, but I'm not sure we care. In the case of any error, we
217
* pass back the file descriptor -- if we fail because the disk
218
* is too full, reads should continue to work and I see no reason
219
* to discard the user's work.
220
*/
221
if (fseek(ep, (off_t)-2, SEEK_END) < 0)
222
return ep;
223
lc = getc(ep) == '\n' ? 1 : 0;
224
if (getc(ep) == '\n')
225
++lc;
226
else
227
lc = 0;
228
229
switch (lc) {
230
case 0:
231
if (putc('\n', ep) == EOF)
232
break;
233
/* FALLTHROUGH */
234
case 1:
235
putc('\n', ep);
236
/* FALLTHROUGH */
237
case 2:
238
break;
239
default:
240
abort();
241
break;
242
}
243
/*
244
* XXX: fflush() is necessary, so future stat(2) succeeds.
245
*/
246
fflush(ep);
247
remove(state.tmp.edit);
248
return ep;
249
250
ret1: note(SYSTEM, "%s", state.tmp.edit);
251
ret2: remove(state.tmp.edit);
252
if (ep)
253
fileclose(ep);
254
return 0;
255
}
256
257