155714Skris/*-
255714Skris * SPDX-License-Identifier: BSD-3-Clause
355714Skris *
455714Skris * Copyright (c) 1980, 1993
555714Skris *	The Regents of the University of California.  All rights reserved.
655714Skris *
755714Skris * Redistribution and use in source and binary forms, with or without
855714Skris * modification, are permitted provided that the following conditions
955714Skris * are met:
1055714Skris * 1. Redistributions of source code must retain the above copyright
1155714Skris *    notice, this list of conditions and the following disclaimer.
1255714Skris * 2. Redistributions in binary form must reproduce the above copyright
1355714Skris *    notice, this list of conditions and the following disclaimer in the
1455714Skris *    documentation and/or other materials provided with the distribution.
1555714Skris * 3. Neither the name of the University nor the names of its contributors
1655714Skris *    may be used to endorse or promote products derived from this software
1755714Skris *    without specific prior written permission.
1855714Skris *
1955714Skris * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2055714Skris * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2155714Skris * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2255714Skris * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2355714Skris * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2455714Skris * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2555714Skris * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2655714Skris * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2755714Skris * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2855714Skris * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2955714Skris * SUCH DAMAGE.
3055714Skris */
3155714Skris
3255714Skris#include "rcv.h"
3355714Skris#include <sys/wait.h>
3455714Skris#include "extern.h"
3555714Skris
3655714Skris/*
3755714Skris * Mail -- a mail program
3855714Skris *
3955714Skris * More user commands.
4055714Skris */
4155714Skris
4255714Skrisextern int wait_status;
4355714Skris
4455714Skris/*
4555714Skris * If any arguments were given, go to the next applicable argument
4655714Skris * following dot, otherwise, go to the next applicable message.
4755714Skris * If given as first command with no arguments, print first message.
4855714Skris */
4955714Skrisint
5055714Skrisnext(void *v)
5155714Skris{
5255714Skris	struct message *mp;
5355714Skris	int *msgvec = v;
5455714Skris	int *ip, *ip2, list[2], mdot;
5555714Skris
5655714Skris	if (*msgvec != 0) {
5755714Skris
5855714Skris		/*
5955714Skris		 * If some messages were supplied, find the
6055714Skris		 * first applicable one following dot using
6155714Skris		 * wrap around.
6255714Skris		 */
6355714Skris
6455714Skris		mdot = dot - &message[0] + 1;
6555714Skris
6655714Skris		/*
6755714Skris		 * Find the first message in the supplied
6855714Skris		 * message list which follows dot.
6955714Skris		 */
7055714Skris
7155714Skris		for (ip = msgvec; *ip != 0; ip++)
7255714Skris			if (*ip > mdot)
7355714Skris				break;
7455714Skris		if (*ip == 0)
7555714Skris			ip = msgvec;
7655714Skris		ip2 = ip;
7755714Skris		do {
7855714Skris			mp = &message[*ip2 - 1];
7955714Skris			if ((mp->m_flag & MDELETED) == 0) {
8055714Skris				dot = mp;
8155714Skris				goto hitit;
8255714Skris			}
8355714Skris			if (*ip2 != 0)
8455714Skris				ip2++;
8555714Skris			if (*ip2 == 0)
8655714Skris				ip2 = msgvec;
8755714Skris		} while (ip2 != ip);
8855714Skris		printf("No messages applicable\n");
8955714Skris		return (1);
9055714Skris	}
9155714Skris
9255714Skris	/*
9355714Skris	 * If this is the first command, select message 1.
9455714Skris	 * Note that this must exist for us to get here at all.
9555714Skris	 */
9655714Skris
9755714Skris	if (!sawcom)
9855714Skris		goto hitit;
9955714Skris
10055714Skris	/*
10155714Skris	 * Just find the next good message after dot, no
10255714Skris	 * wraparound.
10355714Skris	 */
10455714Skris
10555714Skris	for (mp = dot+1; mp < &message[msgCount]; mp++)
10655714Skris		if ((mp->m_flag & (MDELETED|MSAVED)) == 0)
10755714Skris			break;
10855714Skris	if (mp >= &message[msgCount]) {
10955714Skris		printf("At EOF\n");
11055714Skris		return (0);
11155714Skris	}
11255714Skris	dot = mp;
11355714Skrishitit:
11455714Skris	/*
11555714Skris	 * Print dot.
11655714Skris	 */
11755714Skris
11855714Skris	list[0] = dot - &message[0] + 1;
11955714Skris	list[1] = 0;
12055714Skris	return (type(list));
12155714Skris}
12255714Skris
12355714Skris/*
12455714Skris * Save a message in a file.  Mark the message as saved
12555714Skris * so we can discard when the user quits.
12655714Skris */
12755714Skrisint
12855714Skrissave(void *v)
12955714Skris{
13055714Skris	char *str = v;
13155714Skris
13255714Skris	return (save1(str, 1, "save", saveignore));
13355714Skris}
13455714Skris
13555714Skris/*
13655714Skris * Copy a message to a file without affected its saved-ness
13755714Skris */
13855714Skrisint
13955714Skriscopycmd(void *v)
14055714Skris{
14155714Skris	char *str = v;
14255714Skris
14355714Skris	return (save1(str, 0, "copy", saveignore));
14455714Skris}
14555714Skris
14655714Skris/*
14755714Skris * Save/copy the indicated messages at the end of the passed file name.
14855714Skris * If mark is true, mark the message "saved."
149109998Smarkm */
150109998Smarkmint
151109998Smarkmsave1(char str[], int mark, const char *cmd, struct ignoretab *ignore)
152109998Smarkm{
153246772Sjkim	struct message *mp;
154109998Smarkm	char *file;
15555714Skris	const char *disp;
15655714Skris	int f, *msgvec, *ip;
157109998Smarkm	FILE *obuf;
15855714Skris
15955714Skris	msgvec = (int *)salloc((msgCount + 2) * sizeof(*msgvec));
16055714Skris	if ((file = snarf(str, &f)) == NULL)
16155714Skris		return (1);
16255714Skris	if (!f) {
16355714Skris		*msgvec = first(0, MMNORM);
16455714Skris		if (*msgvec == 0) {
16555714Skris			printf("No messages to %s.\n", cmd);
16655714Skris			return (1);
16755714Skris		}
16855714Skris		msgvec[1] = 0;
16955714Skris	}
17055714Skris	if (f && getmsglist(str, msgvec, 0) < 0)
17155714Skris		return (1);
17255714Skris	if ((file = expand(file)) == NULL)
17355714Skris		return (1);
17455714Skris	printf("\"%s\" ", file);
17555714Skris	(void)fflush(stdout);
17655714Skris	if (access(file, 0) >= 0)
17755714Skris		disp = "[Appended]";
17855714Skris	else
17955714Skris		disp = "[New file]";
18055714Skris	if ((obuf = Fopen(file, "a")) == NULL) {
18155714Skris		warn((char *)NULL);
18255714Skris		return (1);
18355714Skris	}
18455714Skris	for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
18555714Skris		mp = &message[*ip - 1];
18655714Skris		touch(mp);
18755714Skris		if (sendmessage(mp, obuf, ignore, NULL) < 0) {
18855714Skris			warnx("%s", file);
18955714Skris			(void)Fclose(obuf);
19055714Skris			return (1);
19155714Skris		}
19255714Skris		if (mark)
19355714Skris			mp->m_flag |= MSAVED;
19455714Skris	}
19555714Skris	(void)fflush(obuf);
19655714Skris	if (ferror(obuf))
19755714Skris		warn("%s", file);
19855714Skris	(void)Fclose(obuf);
19955714Skris	printf("%s\n", disp);
20055714Skris	return (0);
20155714Skris}
20255714Skris
20355714Skris/*
20455714Skris * Write the indicated messages at the end of the passed
20555714Skris * file name, minus header and trailing blank line.
20655714Skris */
20755714Skrisint
20855714Skrisswrite(void *v)
20955714Skris{
21055714Skris	char *str = v;
21155714Skris
21255714Skris	return (save1(str, 1, "write", ignoreall));
21355714Skris}
21455714Skris
21555714Skris/*
21655714Skris * Snarf the file from the end of the command line and
21755714Skris * return a pointer to it.  If there is no file attached,
21855714Skris * just return NULL.  Put a null in front of the file
21955714Skris * name so that the message list processing won't see it,
22055714Skris * unless the file name is the only thing on the line, in
22155714Skris * which case, return 0 in the reference flag variable.
22255714Skris */
22355714Skris
22455714Skrischar *
22555714Skrissnarf(char *linebuf, int *flag)
22655714Skris{
22755714Skris	char *cp;
22855714Skris
22955714Skris	*flag = 1;
23055714Skris	cp = strlen(linebuf) + linebuf - 1;
23155714Skris
23255714Skris	/*
23355714Skris	 * Strip away trailing blanks.
23455714Skris	 */
23555714Skris
23655714Skris	while (cp > linebuf && isspace((unsigned char)*cp))
23755714Skris		cp--;
23855714Skris	*++cp = '\0';
23955714Skris
24055714Skris	/*
24155714Skris	 * Now search for the beginning of the file name.
24255714Skris	 */
24355714Skris
24455714Skris	while (cp > linebuf && !isspace((unsigned char)*cp))
24555714Skris		cp--;
24655714Skris	if (*cp == '\0') {
24755714Skris		printf("No file specified.\n");
24855714Skris		return (NULL);
24955714Skris	}
25055714Skris	if (isspace((unsigned char)*cp))
25155714Skris		*cp++ = '\0';
25255714Skris	else
25355714Skris		*flag = 0;
25455714Skris	return (cp);
25555714Skris}
25655714Skris
25755714Skris/*
25855714Skris * Delete messages.
25955714Skris */
26055714Skrisint
26155714Skrisdeletecmd(void *v)
26255714Skris{
26355714Skris	int *msgvec = v;
26455714Skris
26555714Skris	delm(msgvec);
26655714Skris	return (0);
26755714Skris}
26855714Skris
26955714Skris/*
27055714Skris * Delete messages, then type the new dot.
27155714Skris */
27255714Skrisint
27355714Skrisdeltype(void *v)
27455714Skris{
27555714Skris	int *msgvec = v;
27655714Skris	int list[2];
27755714Skris	int lastdot;
27855714Skris
27955714Skris	lastdot = dot - &message[0] + 1;
28055714Skris	if (delm(msgvec) >= 0) {
28155714Skris		list[0] = dot - &message[0] + 1;
28255714Skris		if (list[0] > lastdot) {
28355714Skris			touch(dot);
28455714Skris			list[1] = 0;
28555714Skris			return (type(list));
28655714Skris		}
28755714Skris		printf("At EOF\n");
28855714Skris	} else
28955714Skris		printf("No more messages\n");
29055714Skris	return (0);
29155714Skris}
29255714Skris
29355714Skris/*
29455714Skris * Delete the indicated messages.
29555714Skris * Set dot to some nice place afterwards.
29655714Skris * Internal interface.
29755714Skris */
29855714Skrisint
29955714Skrisdelm(int *msgvec)
30055714Skris{
30155714Skris	struct message *mp;
30255714Skris	int *ip, last;
30355714Skris
30455714Skris	last = 0;
30555714Skris	for (ip = msgvec; *ip != 0; ip++) {
30655714Skris		mp = &message[*ip - 1];
30755714Skris		touch(mp);
30855714Skris		mp->m_flag |= MDELETED|MTOUCH;
30955714Skris		mp->m_flag &= ~(MPRESERVE|MSAVED|MBOX);
31055714Skris		last = *ip;
31155714Skris	}
31255714Skris	if (last != 0) {
31355714Skris		dot = &message[last-1];
31455714Skris		last = first(0, MDELETED);
31555714Skris		if (last != 0) {
31655714Skris			dot = &message[last-1];
31755714Skris			return (0);
31855714Skris		}
31955714Skris		else {
32055714Skris			dot = &message[0];
32155714Skris			return (-1);
32255714Skris		}
323160814Ssimon	}
324160814Ssimon
32555714Skris	/*
326109998Smarkm	 * Following can't happen -- it keeps lint happy
327109998Smarkm	 */
328109998Smarkm
329109998Smarkm	return (-1);
330109998Smarkm}
331109998Smarkm
332109998Smarkm/*
333109998Smarkm * Undelete the indicated messages.
334109998Smarkm */
335109998Smarkmint
336109998Smarkmundeletecmd(void *v)
337109998Smarkm{
338109998Smarkm	int *msgvec = v;
339109998Smarkm	int *ip;
340109998Smarkm	struct message *mp;
341109998Smarkm
342109998Smarkm	for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
343160814Ssimon		mp = &message[*ip - 1];
34455714Skris		touch(mp);
34555714Skris		dot = mp;
34655714Skris		mp->m_flag &= ~MDELETED;
34755714Skris	}
34855714Skris	return (0);
34955714Skris}
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