1 /** $MirOS: src/usr.bin/mail/cmd2.c,v 1.4 2005/11/23 20:38:22 tg Exp $ */
2 /* $OpenBSD: cmd2.c,v 1.15 2004/09/15 22:21:40 deraadt Exp $ */
3 /* $NetBSD: cmd2.c,v 1.7 1997/05/17 19:55:10 pk Exp $ */
4
5 /*
6 * Copyright (c) 1980, 1993
7 * The Regents of the University of California. All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34 #include "rcv.h"
35 #include "extern.h"
36 #include <sys/wait.h>
37
38 __SCCSID("@(#)cmd2.c 8.1 (Berkeley) 6/6/93");
39 __RCSID("$MirOS: src/usr.bin/mail/cmd2.c,v 1.4 2005/11/23 20:38:22 tg Exp $");
40
41 /*
42 * Mail -- a mail program
43 *
44 * More user commands.
45 */
46 static int igcomp(const void *, const void *);
47
48 /*
49 * If any arguments were given, go to the next applicable argument
50 * following dot, otherwise, go to the next applicable message.
51 * If given as first command with no arguments, print first message.
52 */
53 int
next(void * v)54 next(void *v)
55 {
56 struct message *mp;
57 int *msgvec = v;
58 int *ip, *ip2, list[2], mdot;
59
60 if (*msgvec) {
61 /*
62 * If some messages were supplied, find the
63 * first applicable one following dot using
64 * wrap around.
65 */
66 mdot = dot - &message[0] + 1;
67
68 /*
69 * Find the first message in the supplied
70 * message list which follows dot.
71 */
72 for (ip = msgvec; *ip; ip++)
73 if (*ip > mdot)
74 break;
75 if (*ip == 0)
76 ip = msgvec;
77 ip2 = ip;
78 do {
79 mp = &message[*ip2 - 1];
80 if ((mp->m_flag & MDELETED) == 0) {
81 dot = mp;
82 goto hitit;
83 }
84 if (*ip2)
85 ip2++;
86 if (!(*ip2))
87 ip2 = msgvec;
88 } while (ip2 != ip);
89 puts("No messages applicable");
90 return(1);
91 }
92
93 /*
94 * If this is the first command, select message 1.
95 * Note that this must exist for us to get here at all.
96 */
97 if (!sawcom)
98 goto hitit;
99
100 /*
101 * Just find the next good message after dot, no
102 * wraparound.
103 */
104 for (mp = dot+1; mp < &message[msgCount]; mp++)
105 if ((mp->m_flag & (MDELETED|MSAVED)) == 0)
106 break;
107 if (mp >= &message[msgCount]) {
108 puts("At EOF");
109 return(0);
110 }
111 dot = mp;
112 hitit:
113 /*
114 * Print dot.
115 */
116 list[0] = dot - &message[0] + 1;
117 list[1] = 0;
118 return(type(list));
119 }
120
121 /*
122 * Save a message in a file. Mark the message as saved
123 * so we can discard when the user quits.
124 */
125 int
save(void * v)126 save(void *v)
127 {
128 char *str = v;
129
130 return(save1(str, 1, "save", saveignore));
131 }
132
133 /*
134 * Copy a message to a file without affected its saved-ness
135 */
136 int
copycmd(void * v)137 copycmd(void *v)
138 {
139 char *str = v;
140
141 return(save1(str, 0, "copy", saveignore));
142 }
143
144 /*
145 * Save/copy the indicated messages at the end of the passed file name.
146 * If mark is true, mark the message "saved."
147 */
148 int
save1(char * str,int mark,char * cmd,struct ignoretab * ignore)149 save1(char *str, int mark, char *cmd, struct ignoretab *ignore)
150 {
151 struct message *mp;
152 char *file, *disp;
153 int f, *msgvec, *ip;
154 FILE *obuf;
155
156 msgvec = (int *)salloc((msgCount + 2) * sizeof(*msgvec));
157 if ((file = snarf(str, &f)) == NULL)
158 return(1);
159 if (!f) {
160 *msgvec = first(0, MMNORM);
161 if (*msgvec == 0) {
162 printf("No messages to %s.\n", cmd);
163 return(1);
164 }
165 msgvec[1] = 0;
166 }
167 if (f && getmsglist(str, msgvec, 0) < 0)
168 return(1);
169 if ((file = expand(file)) == NULL)
170 return(1);
171 printf("\"%s\" ", file);
172 fflush(stdout);
173 if (access(file, 0) >= 0)
174 disp = "[Appended]";
175 else
176 disp = "[New file]";
177 if ((obuf = Fopen(file, "a")) == NULL) {
178 warn(NULL);
179 return(1);
180 }
181 for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
182 mp = &message[*ip - 1];
183 touch(mp);
184 if (sendmessage(mp, obuf, ignore, NULL) < 0) {
185 warn("%s", file);
186 (void)Fclose(obuf);
187 return(1);
188 }
189 if (mark)
190 mp->m_flag |= MSAVED;
191 }
192 fflush(obuf);
193 if (ferror(obuf))
194 warn("%s", file);
195 (void)Fclose(obuf);
196 printf("%s\n", disp);
197 return(0);
198 }
199
200 /*
201 * Write the indicated messages at the end of the passed
202 * file name, minus header and trailing blank line.
203 */
204 int
swrite(void * v)205 swrite(void *v)
206 {
207 char *str = v;
208
209 return(save1(str, 1, "write", ignoreall));
210 }
211
212 /*
213 * Snarf the file from the end of the command line and
214 * return a pointer to it. If there is no file attached,
215 * just return NULL. Put a null in front of the file
216 * name so that the message list processing won't see it,
217 * unless the file name is the only thing on the line, in
218 * which case, return 0 in the reference flag variable.
219 */
220 char *
snarf(char * linebuf,int * flag)221 snarf(char *linebuf, int *flag)
222 {
223 char *cp;
224
225 *flag = 1;
226 cp = strlen(linebuf) + linebuf - 1;
227
228 /*
229 * Strip away trailing blanks.
230 */
231 while (cp > linebuf && isspace(*cp))
232 cp--;
233 *++cp = 0;
234
235 /*
236 * Now search for the beginning of the file name.
237 */
238 while (cp > linebuf && !isspace(*cp))
239 cp--;
240 if (*cp == '\0') {
241 puts("No file specified.");
242 return(NULL);
243 }
244 if (isspace(*cp))
245 *cp++ = 0;
246 else
247 *flag = 0;
248 return(cp);
249 }
250
251 /*
252 * Delete messages.
253 */
254 int
deletecmd(void * v)255 deletecmd(void *v)
256 {
257 int *msgvec = v;
258
259 delm(msgvec);
260 return(0);
261 }
262
263 /*
264 * Delete messages, then type the new dot.
265 */
266 int
deltype(void * v)267 deltype(void *v)
268 {
269 int *msgvec = v;
270 int list[2];
271 int lastdot;
272
273 lastdot = dot - &message[0] + 1;
274 if (delm(msgvec) >= 0) {
275 list[0] = dot - &message[0] + 1;
276 if (list[0] > lastdot) {
277 touch(dot);
278 list[1] = 0;
279 return(type(list));
280 }
281 puts("At EOF");
282 } else
283 puts("No more messages");
284 return(0);
285 }
286
287 /*
288 * Delete the indicated messages.
289 * Set dot to some nice place afterwards.
290 * Internal interface.
291 */
292 int
delm(int * msgvec)293 delm(int *msgvec)
294 {
295 struct message *mp;
296 int *ip, last;
297
298 last = 0;
299 for (ip = msgvec; *ip; ip++) {
300 mp = &message[*ip - 1];
301 touch(mp);
302 mp->m_flag |= MDELETED|MTOUCH;
303 mp->m_flag &= ~(MPRESERVE|MSAVED|MBOX);
304 last = *ip;
305 }
306 if (last) {
307 dot = &message[last-1];
308 last = first(0, MDELETED);
309 if (last) {
310 dot = &message[last-1];
311 return(0);
312 }
313 else {
314 dot = &message[0];
315 return(-1);
316 }
317 }
318
319 /*
320 * Following can't happen -- it keeps lint happy
321 */
322 return(-1);
323 }
324
325 /*
326 * Undelete the indicated messages.
327 */
328 int
undeletecmd(void * v)329 undeletecmd(void *v)
330 {
331 int *msgvec = v;
332 int *ip;
333 struct message *mp;
334
335 for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
336 mp = &message[*ip - 1];
337 touch(mp);
338 dot = mp;
339 mp->m_flag &= ~MDELETED;
340 }
341 return(0);
342 }
343
344 /*
345 * Interactively dump core on "core"
346 */
347 int
core(void * v)348 core(void *v)
349 {
350 pid_t pid;
351 extern int wait_status;
352
353 switch (pid = vfork()) {
354 case -1:
355 warn("vfork");
356 return(1);
357 case 0:
358 abort();
359 _exit(1);
360 }
361 fputs("Okie dokie", stdout);
362 fflush(stdout);
363 wait_child(pid);
364 if (WIFSIGNALED(wait_status) && WCOREDUMP(wait_status))
365 puts(" -- Core dumped.");
366 else
367 puts(" -- Can't dump core.");
368 return(0);
369 }
370
371 /*
372 * Add the given header fields to the retained list.
373 * If no arguments, print the current list of retained fields.
374 */
375 int
retfield(void * v)376 retfield(void *v)
377 {
378 char **list = v;
379
380 return(ignore1(list, ignore + 1, "retained"));
381 }
382
383 /*
384 * Add the given header fields to the ignored list.
385 * If no arguments, print the current list of ignored fields.
386 */
387 int
igfield(void * v)388 igfield(void *v)
389 {
390 char **list = v;
391
392 return(ignore1(list, ignore, "ignored"));
393 }
394
395 int
saveretfield(void * v)396 saveretfield(void *v)
397 {
398 char **list = v;
399
400 return(ignore1(list, saveignore + 1, "retained"));
401 }
402
403 int
saveigfield(void * v)404 saveigfield(void *v)
405 {
406 char **list = v;
407
408 return(ignore1(list, saveignore, "ignored"));
409 }
410
411 int
ignore1(char ** list,struct ignoretab * tab,char * which)412 ignore1(char **list, struct ignoretab *tab, char *which)
413 {
414 char field[LINESIZE];
415 char **ap;
416 struct ignore *igp;
417 int h;
418
419 if (*list == NULL)
420 return(igshow(tab, which));
421 for (ap = list; *ap != 0; ap++) {
422 istrlcpy(field, *ap, sizeof(field));
423 if (member(field, tab))
424 continue;
425 h = hash(field);
426 igp = (struct ignore *)calloc(1, sizeof(struct ignore));
427 if (igp == NULL)
428 errx(1, "Out of memory");
429 igp->i_field = strdup(field);
430 if (igp->i_field == NULL)
431 errx(1, "Out of memory");
432 igp->i_link = tab->i_head[h];
433 tab->i_head[h] = igp;
434 tab->i_count++;
435 }
436 return(0);
437 }
438
439 /*
440 * Print out all currently retained fields.
441 */
442 int
igshow(struct ignoretab * tab,char * which)443 igshow(struct ignoretab *tab, char *which)
444 {
445 int h;
446 struct ignore *igp;
447 char **ap, **ring;
448
449 if (tab->i_count == 0) {
450 printf("No fields currently being %s.\n", which);
451 return(0);
452 }
453 ring = (char **)salloc((tab->i_count + 1) * sizeof(char *));
454 ap = ring;
455 for (h = 0; h < HSHSIZE; h++)
456 for (igp = tab->i_head[h]; igp != 0; igp = igp->i_link)
457 *ap++ = igp->i_field;
458 *ap = 0;
459 qsort(ring, tab->i_count, sizeof(char *), igcomp);
460 for (ap = ring; *ap != 0; ap++)
461 puts(*ap);
462 return(0);
463 }
464
465 /*
466 * Compare two names for sorting ignored field list.
467 */
468 static int
igcomp(const void * l,const void * r)469 igcomp(const void *l, const void *r)
470 {
471
472 return(strcmp(*(char **)l, *(char **)r));
473 }
474