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