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