1/*
2 * Copyright (c) 1980, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *	This product includes software developed by the University of
16 *	California, Berkeley and its contributors.
17 * 4. 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#ifndef lint
35#if 0
36static char sccsid[] = "@(#)cmd2.c	8.1 (Berkeley) 6/6/93";
37#endif
38static const char rcsid[] =
39  "$FreeBSD: src/usr.bin/mail/cmd2.c,v 1.9 2002/06/30 05:25:06 obrien Exp $";
40#endif /* not lint */
41
42#include <sys/cdefs.h>
43
44#include "rcv.h"
45#include <sys/wait.h>
46#include "extern.h"
47
48/*
49 * Mail -- a mail program
50 *
51 * More user commands.
52 */
53
54extern int wait_status;
55
56/*
57 * If any arguments were given, go to the next applicable argument
58 * following dot, otherwise, go to the next applicable message.
59 * If given as first command with no arguments, print first message.
60 */
61int
62next(msgvec)
63	int *msgvec;
64{
65	struct message *mp;
66	int *ip, *ip2, list[2], mdot;
67
68	if (*msgvec != 0) {
69
70		/*
71		 * If some messages were supplied, find the
72		 * first applicable one following dot using
73		 * wrap around.
74		 */
75
76		mdot = dot - &message[0] + 1;
77
78		/*
79		 * Find the first message in the supplied
80		 * message list which follows dot.
81		 */
82
83		for (ip = msgvec; *ip != 0; ip++) {
84			if (!sawcom) /* don't skip current message if first command */
85				break;
86			if (*ip > mdot)
87				break;
88		}
89		if (*ip == 0)
90			ip = msgvec;
91		ip2 = ip;
92		do {
93			mp = &message[*ip2 - 1];
94			if ((mp->m_flag & MDELETED) == 0) {
95				dot = mp;
96				goto hitit;
97			}
98			if (*ip2 != 0)
99				ip2++;
100			if (*ip2 == 0)
101				ip2 = msgvec;
102		} while (ip2 != ip);
103		printf("No messages applicable\n");
104		return (1);
105	}
106
107	/*
108	 * If this is the first command, select message 1.
109	 * Note that this must exist for us to get here at all.
110	 */
111
112	if (!sawcom)
113		goto hitit;
114
115	/*
116	 * Just find the next good message after dot, no
117	 * wraparound.
118	 */
119
120	for (mp = dot+1; mp < &message[msgCount]; mp++)
121		if ((mp->m_flag & (MDELETED|MSAVED)) == 0)
122			break;
123	if (mp >= &message[msgCount]) {
124		printf("At EOF\n");
125		return (0);
126	}
127	dot = mp;
128hitit:
129	/*
130	 * Print dot.
131	 */
132
133	list[0] = dot - &message[0] + 1;
134	list[1] = 0;
135	return (type(list));
136}
137
138/*
139 * Save a message in a file.  Mark the message as saved
140 * so we can discard when the user quits.
141 */
142int
143save(str)
144	char str[];
145{
146
147	return (save1(str, 1, "save", saveignore));
148}
149
150/*
151 * Save a message list in a file named after author.  Mark the message as saved
152 * so we can discard when the user quits.
153 */
154int
155Capsave(str)
156	char str[];
157{
158
159	return (save1(str, 1, "Save", saveignore));
160}
161
162/*
163 * Copy a message to a file without affected its saved-ness
164 */
165int
166copycmd(str)
167	char str[];
168{
169
170	return (save1(str, 0, "copy", saveignore));
171}
172
173/*
174 * Copy a message to a file named after author without affected its saved-ness
175 */
176int
177Capcopycmd(str)
178	char str[];
179{
180
181#if 1
182	printf("Copy command is not yet implemented\n");
183	return (1);
184#else
185	return (save1(str, 0, "Copy", saveignore));
186#endif
187}
188
189/*
190 * Save/copy the indicated messages at the end of the passed file name.
191 * If mark is true, mark the message "saved."
192 */
193int
194save1(str, mark, cmd, ignore)
195	char str[];
196	int mark;
197	const char *cmd;
198	struct ignoretab *ignore;
199{
200	struct message *mp;
201	char *file;
202	const char *disp;
203	int f, *msgvec, *ip;
204	FILE *obuf;
205	int doing_Save = !strcmp(cmd,"Save");
206
207	msgvec = (int *)salloc((msgCount + 2) * sizeof(*msgvec));
208	if ((file = snarf(str, &f, doing_Save)) == NULL)
209		return (1);
210	if (!f) {
211		*msgvec = first(0, MMNORM);
212		if (*msgvec == 0) {
213			printf("No messages to %s.\n", cmd);
214			return (1);
215		}
216		msgvec[1] = 0;
217	}
218	if (f && getmsglist(str, msgvec, 0) < 0)
219		return (1);
220	/* Save derives file name from author of first message: */
221	if (doing_Save) {
222	        char *rcv;
223		mp = &message[msgvec[0] - 1];
224	        touch(mp);
225		if ((rcv = skin(hfield("from", mp))) == NULL)
226       			rcv = skin(nameof(mp, 1));
227		rcv = getauthor(rcv);
228		if (rcv == NULL) {
229			printf("Failed to find sender: Save not performed\n");
230			return(1);
231		}
232		file = rcv; /* use it as file name */
233	}
234	if ((file = expand(file)) == NULL)
235		return (1);
236	printf("\"%s\" ", file);
237	(void)fflush(stdout);
238	if (access(file, 0) >= 0)
239		disp = "[Appended]";
240	else
241		disp = "[New file]";
242	if ((obuf = Fopen(file, "a")) == NULL) {
243		warn((char *)NULL);
244		return (1);
245	}
246	for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
247		mp = &message[*ip - 1];
248		touch(mp);
249		if (sendmessage(mp, obuf, ignore, NULL) < 0) {
250			warnx("%s", file);
251			(void)Fclose(obuf);
252			return (1);
253		}
254		if (mark)
255			mp->m_flag |= MSAVED;
256	}
257	(void)fflush(obuf);
258	if (ferror(obuf))
259		warn("%s", file);
260	(void)Fclose(obuf);
261	printf("%s\n", disp);
262	return (0);
263}
264
265/*
266 * Write the indicated messages at the end of the passed
267 * file name, minus header and trailing blank line.
268 */
269int
270swrite(str)
271	char str[];
272{
273
274	return (save1(str, 1, "write", ignoreall));
275}
276
277/*
278 * Snarf the file from the end of the command line and
279 * return a pointer to it.  If there is no file attached,
280 * just return NULL.  Put a null in front of the file
281 * name so that the message list processing won't see it,
282 * unless the file name is the only thing on the line, in
283 * which case, return 0 in the reference flag variable.
284 */
285
286char *
287snarf(linebuf, flag, doing_Save)
288	char linebuf[];
289	int *flag;
290	int doing_Save;
291{
292	char *cp;
293
294	*flag = 1;
295	cp = strlen(linebuf) + linebuf - 1;
296
297	/*
298	 * Strip away trailing blanks.
299	 */
300
301	while (cp > linebuf && isspace((unsigned char)*cp))
302		cp--;
303	*++cp = '\0';
304
305	/*
306	 * Now search for the beginning of the file name.
307	 */
308
309	while (cp > linebuf && !isspace((unsigned char)*cp))
310		cp--;
311	if (*cp == '\0') {
312		if (!doing_Save)
313			printf("No file specified: using MBOX.\n");
314		*flag = 0;	/* no message list found either */
315		return ("&");
316	}
317
318	if (isspace((unsigned char)*cp))
319		*cp++ = '\0';
320	else {
321		if (!doing_Save) *flag = 0;
322	}
323	return (cp);
324}
325
326/*
327 * Delete messages.
328 */
329int
330delete(msgvec)
331	int msgvec[];
332{
333
334	delm(msgvec);
335	return (0);
336}
337
338/*
339 * Delete messages, then type the new dot.
340 */
341int
342deltype(msgvec)
343	int msgvec[];
344{
345	int list[2];
346	int lastdot;
347
348	lastdot = dot - &message[0] + 1;
349	if (delm(msgvec) >= 0) {
350		list[0] = dot - &message[0] + 1;
351		if (list[0] > lastdot) {
352			touch(dot);
353			list[1] = 0;
354			return (type(list));
355		}
356		printf("At EOF\n");
357	} else
358		printf("No more messages\n");
359	return (0);
360}
361
362/*
363 * Delete the indicated messages.
364 * Set dot to some nice place afterwards.
365 * Internal interface.
366 */
367int
368delm(msgvec)
369	int *msgvec;
370{
371	struct message *mp;
372	int *ip, last;
373
374	last = 0;
375	for (ip = msgvec; *ip != 0; ip++) {
376		mp = &message[*ip - 1];
377		touch(mp);
378		mp->m_flag |= MDELETED|MTOUCH;
379		mp->m_flag &= ~(MPRESERVE|MSAVED|MBOX);
380		last = *ip;
381	}
382	if (last != 0) {
383		dot = &message[last-1];
384		last = first(0, MDELETED);
385		if (last != 0) {
386			dot = &message[last-1];
387			return (0);
388		}
389		else {
390			dot = &message[0];
391			return (-1);
392		}
393	}
394
395	/*
396	 * Following can't happen -- it keeps lint happy
397	 */
398
399	return (-1);
400}
401
402/*
403 * Undelete the indicated messages.
404 */
405int
406undelete_messages(msgvec)
407	int *msgvec;
408{
409	struct message *mp;
410	int *ip;
411
412	for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
413		mp = &message[*ip - 1];
414		touch(mp);
415		dot = mp;
416		mp->m_flag &= ~MDELETED;
417	}
418	return (0);
419}
420
421/*
422 * Interactively dump core on "core"
423 */
424int
425core()
426{
427	int pid;
428
429	switch (pid = fork()) {
430	case -1:
431		warn("fork");
432		return (1);
433	case 0:
434		abort();
435		_exit(1);
436	}
437	printf("Okie dokie");
438	(void)fflush(stdout);
439	wait_child(pid);
440	if (WIFSIGNALED(wait_status) && WCOREDUMP(wait_status))
441		printf(" -- Core dumped.\n");
442	else
443		printf(" -- Can't dump core.\n");
444	return (0);
445}
446
447/*
448 * Clobber as many bytes of stack as the user requests.
449 */
450int
451clobber(argv)
452	char **argv;
453{
454	int times;
455
456	if (argv[0] == 0)
457		times = 1;
458	else
459		times = (atoi(argv[0]) + 511) / 512;
460	clob1(times);
461	return (0);
462}
463
464/*
465 * Clobber the stack.
466 */
467void
468clob1(n)
469	int n;
470{
471	char buf[512];
472	char *cp;
473
474	if (n <= 0)
475		return;
476	for (cp = buf; cp < &buf[512]; *cp++ = 0xFF)
477		;
478	clob1(n - 1);
479}
480
481/*
482 * Add the given header fields to the retained list.
483 * If no arguments, print the current list of retained fields.
484 */
485int
486retfield(list)
487	char *list[];
488{
489
490	return (ignore1(list, ignore + 1, "retained"));
491}
492
493/*
494 * Add the given header fields to the ignored list.
495 * If no arguments, print the current list of ignored fields.
496 */
497int
498igfield(list)
499	char *list[];
500{
501
502	return (ignore1(list, ignore, "ignored"));
503}
504
505int
506saveretfield(list)
507	char *list[];
508{
509
510	return (ignore1(list, saveignore + 1, "retained"));
511}
512
513int
514saveigfield(list)
515	char *list[];
516{
517
518	return (ignore1(list, saveignore, "ignored"));
519}
520
521int
522ignore1(list, tab, which)
523	char *list[];
524	struct ignoretab *tab;
525	const char *which;
526{
527	char field[LINESIZE];
528	int h;
529	struct ignore *igp;
530	char **ap;
531
532	if (*list == NULL)
533		return (igshow(tab, which));
534	for (ap = list; *ap != 0; ap++) {
535		istrncpy(field, *ap, sizeof(field));
536		if (member(field, tab))
537			continue;
538		h = hash(field);
539		igp = calloc(1, sizeof(struct ignore));
540		igp->i_field = calloc((unsigned)strlen(field) + 1,
541		    sizeof(char));
542		strcpy(igp->i_field, field);
543		igp->i_link = tab->i_head[h];
544		tab->i_head[h] = igp;
545		tab->i_count++;
546	}
547	return (0);
548}
549
550/*
551 * Print out all currently retained fields.
552 */
553int
554igshow(tab, which)
555	struct ignoretab *tab;
556	const char *which;
557{
558	int h;
559	struct ignore *igp;
560	char **ap, **ring;
561
562	if (tab->i_count == 0) {
563		printf("No fields currently being %s.\n", which);
564		return (0);
565	}
566	ring = (char **)salloc((tab->i_count + 1) * sizeof(char *));
567	ap = ring;
568	for (h = 0; h < HSHSIZE; h++)
569		for (igp = tab->i_head[h]; igp != NULL; igp = igp->i_link)
570			*ap++ = igp->i_field;
571	*ap = 0;
572	qsort(ring, tab->i_count, sizeof(char *), igcomp);
573	for (ap = ring; *ap != 0; ap++)
574		printf("%s\n", *ap);
575	return (0);
576}
577
578/*
579 * Compare two names for sorting ignored field list.
580 */
581int
582igcomp(l, r)
583	const void *l, *r;
584{
585
586	return (strcmp(*(const char **)l, *(const char **)r));
587}
588