xref: /netbsd-src/usr.bin/mail/cmd3.c (revision 4fe1ef32f37334167b71a62286b21da7d9542909)
1*4fe1ef32Schristos /*	$NetBSD: cmd3.c,v 1.44 2017/11/09 20:27:50 christos Exp $	*/
288b833a7Schristos 
361f28255Scgd /*
42cb5542fSderaadt  * Copyright (c) 1980, 1993
52cb5542fSderaadt  *	The Regents of the University of California.  All rights reserved.
661f28255Scgd  *
761f28255Scgd  * Redistribution and use in source and binary forms, with or without
861f28255Scgd  * modification, are permitted provided that the following conditions
961f28255Scgd  * are met:
1061f28255Scgd  * 1. Redistributions of source code must retain the above copyright
1161f28255Scgd  *    notice, this list of conditions and the following disclaimer.
1261f28255Scgd  * 2. Redistributions in binary form must reproduce the above copyright
1361f28255Scgd  *    notice, this list of conditions and the following disclaimer in the
1461f28255Scgd  *    documentation and/or other materials provided with the distribution.
1589aaa1bbSagc  * 3. Neither the name of the University nor the names of its contributors
1661f28255Scgd  *    may be used to endorse or promote products derived from this software
1761f28255Scgd  *    without specific prior written permission.
1861f28255Scgd  *
1961f28255Scgd  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2061f28255Scgd  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2161f28255Scgd  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2261f28255Scgd  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2361f28255Scgd  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2461f28255Scgd  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2561f28255Scgd  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2661f28255Scgd  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2761f28255Scgd  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2861f28255Scgd  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2961f28255Scgd  * SUCH DAMAGE.
3061f28255Scgd  */
3161f28255Scgd 
327c81c8f3Slukem #include <sys/cdefs.h>
3361f28255Scgd #ifndef lint
3488b833a7Schristos #if 0
3519d35cbcStls static char sccsid[] = "@(#)cmd3.c	8.2 (Berkeley) 4/20/95";
3688b833a7Schristos #else
37*4fe1ef32Schristos __RCSID("$NetBSD: cmd3.c,v 1.44 2017/11/09 20:27:50 christos Exp $");
3888b833a7Schristos #endif
3961f28255Scgd #endif /* not lint */
4061f28255Scgd 
4161f28255Scgd #include "rcv.h"
42349d9781Schristos #include <assert.h>
438207b28aSchristos #include <util.h>
442cb5542fSderaadt #include "extern.h"
458207b28aSchristos #include "mime.h"
46ca13337dSchristos #include "sig.h"
47f3098750Schristos #include "thread.h"
4861f28255Scgd 
4961f28255Scgd /*
5061f28255Scgd  * Mail -- a mail program
5161f28255Scgd  *
5261f28255Scgd  * Still more user commands.
5361f28255Scgd  */
54f3098750Schristos 
5561f28255Scgd 
5661f28255Scgd /*
57f3098750Schristos  * Do a dictionary order comparison of the arguments from
58f3098750Schristos  * qsort.
5961f28255Scgd  */
60f3098750Schristos static int
diction(const void * a,const void * b)61f3098750Schristos diction(const void *a, const void *b)
6261f28255Scgd {
63ca13337dSchristos 
64f3098750Schristos 	return strcmp(*(const char *const *)a, *(const char *const *)b);
6561f28255Scgd }
6661f28255Scgd 
6761f28255Scgd /*
68f3098750Schristos  * Sort the passed string vector into ascending dictionary
69f3098750Schristos  * order.
7061f28255Scgd  */
71f3098750Schristos PUBLIC void
sort(const char ** list)72f3098750Schristos sort(const char **list)
7361f28255Scgd {
74f3098750Schristos 	const char **ap;
7561f28255Scgd 
76f3098750Schristos 	for (ap = list; *ap != NULL; ap++)
77f3098750Schristos 		continue;
78f3098750Schristos 	if (ap - list < 2)
79f3098750Schristos 		return;
80f3098750Schristos 	qsort(list, (size_t)(ap - list), sizeof(*list), diction);
8161f28255Scgd }
8261f28255Scgd 
8361f28255Scgd /*
8461f28255Scgd  * Expand the shell escape by expanding unescaped !'s into the
8561f28255Scgd  * last issued command where possible.
8661f28255Scgd  */
87f3098750Schristos static int
bangexp(char * str)88b127ccccSwiz bangexp(char *str)
8961f28255Scgd {
90f3098750Schristos 	static char lastbang[128];
91f3098750Schristos 	char bangbuf[LINESIZE];
927c81c8f3Slukem 	char *cp, *cp2;
93ca13337dSchristos 	ssize_t n;
94ca13337dSchristos 	int changed;
9561f28255Scgd 
96ca13337dSchristos 	changed = 0;
9761f28255Scgd 	cp = str;
9861f28255Scgd 	cp2 = bangbuf;
99f3098750Schristos 	n = sizeof(bangbuf);	/* bytes left in bangbuf */
10061f28255Scgd 	while (*cp) {
10161f28255Scgd 		if (*cp == '!') {
102f3098750Schristos 			if (n < (int)strlen(lastbang)) {
10361f28255Scgd  overf:
104ca286310Schristos 				(void)printf("Command buffer overflow\n");
105f3098750Schristos 				return -1;
10661f28255Scgd 			}
10761f28255Scgd 			changed++;
108ca286310Schristos 			(void)strcpy(cp2, lastbang);
10961f28255Scgd 			cp2 += strlen(lastbang);
11061f28255Scgd 			n -= strlen(lastbang);
11161f28255Scgd 			cp++;
11261f28255Scgd 			continue;
11361f28255Scgd 		}
11461f28255Scgd 		if (*cp == '\\' && cp[1] == '!') {
11561f28255Scgd 			if (--n <= 1)
11661f28255Scgd 				goto overf;
11761f28255Scgd 			*cp2++ = '!';
11861f28255Scgd 			cp += 2;
11961f28255Scgd 			changed++;
12061f28255Scgd 		}
12161f28255Scgd 		if (--n <= 1)
12261f28255Scgd 			goto overf;
12361f28255Scgd 		*cp2++ = *cp++;
12461f28255Scgd 	}
12561f28255Scgd 	*cp2 = 0;
12661f28255Scgd 	if (changed) {
127ca286310Schristos 		(void)printf("!%s\n", bangbuf);
128ca286310Schristos 		(void)fflush(stdout);
12961f28255Scgd 	}
130ca286310Schristos 	(void)strcpy(str, bangbuf);
131f3098750Schristos 	(void)strlcpy(lastbang, bangbuf, sizeof(lastbang));
132f3098750Schristos 	return 0;
133f3098750Schristos }
134f3098750Schristos 
135f3098750Schristos /*
136f3098750Schristos  * Process a shell escape by saving signals, ignoring signals,
137f3098750Schristos  * and forking a sh -c
138f3098750Schristos  */
139f3098750Schristos PUBLIC int
shell(void * v)140f3098750Schristos shell(void *v)
141f3098750Schristos {
142ca13337dSchristos 	struct sigaction osa;
143ca13337dSchristos 	sigset_t oset;
144ca13337dSchristos 	char *str;
145f3098750Schristos 	const char *shellcmd;
146f3098750Schristos 	char cmd[LINESIZE];
147f3098750Schristos 
148ca13337dSchristos 	str = v;
149ca13337dSchristos 	sig_check();
150ca13337dSchristos 	(void)sig_ignore(SIGINT, &osa, &oset);
151f3098750Schristos 	(void)strcpy(cmd, str);
152f3098750Schristos 	if (bangexp(cmd) < 0)
153f3098750Schristos 		return 1;
154f3098750Schristos 	if ((shellcmd = value(ENAME_SHELL)) == NULL)
155f3098750Schristos 		shellcmd = _PATH_CSHELL;
156ca13337dSchristos 	(void)run_command(shellcmd, NULL, 0, 1, "-c", cmd, NULL);
157ca13337dSchristos 	(void)sig_restore(SIGINT, &osa, &oset);
158f3098750Schristos 	(void)printf("!\n");
159ca13337dSchristos 	sig_check();
160f3098750Schristos 	return 0;
161f3098750Schristos }
162f3098750Schristos 
163f3098750Schristos /*
164f3098750Schristos  * Fork an interactive shell.
165f3098750Schristos  */
166f3098750Schristos /*ARGSUSED*/
167f3098750Schristos PUBLIC int
dosh(void * v __unused)168f3098750Schristos dosh(void *v __unused)
169f3098750Schristos {
170ca13337dSchristos 	struct sigaction osa;
171ca13337dSchristos 	sigset_t oset;
172f3098750Schristos 	const char *shellcmd;
173f3098750Schristos 
174ca13337dSchristos 	sig_check();
175ca13337dSchristos 	(void)sig_ignore(SIGINT, &osa, &oset);
176f3098750Schristos 	if ((shellcmd = value(ENAME_SHELL)) == NULL)
177f3098750Schristos 		shellcmd = _PATH_CSHELL;
178ca13337dSchristos 	(void)run_command(shellcmd, NULL, 0, 1, NULL);
179ca13337dSchristos 	(void)sig_restore(SIGINT, &osa, &oset);
180f3098750Schristos 	(void)putchar('\n');
181ca13337dSchristos 	sig_check();
182f3098750Schristos 	return 0;
18361f28255Scgd }
18461f28255Scgd 
18561f28255Scgd /*
18661f28255Scgd  * Print out a nice help message from some file or another.
18761f28255Scgd  */
18861f28255Scgd 
189ca286310Schristos /*ARGSUSED*/
190f3098750Schristos PUBLIC int
help(void * v __unused)1918207b28aSchristos help(void *v __unused)
19261f28255Scgd {
193ca13337dSchristos 
19441101b58Schristos 	cathelp(_PATH_HELP);
195f3098750Schristos 	return 0;
19661f28255Scgd }
19761f28255Scgd 
19861f28255Scgd /*
19961f28255Scgd  * Change user's working directory.
20061f28255Scgd  */
201f3098750Schristos PUBLIC int
schdir(void * v)202b127ccccSwiz schdir(void *v)
20361f28255Scgd {
204ca13337dSchristos 	char **arglist;
205ece0fd5cSchristos 	const char *cp;
20661f28255Scgd 
207ca13337dSchristos 	arglist = v;
208ab850155Swiz 	if (*arglist == NULL)
20961f28255Scgd 		cp = homedir;
21061f28255Scgd 	else
211ab850155Swiz 		if ((cp = expand(*arglist)) == NULL)
212f3098750Schristos 			return 1;
21361f28255Scgd 	if (chdir(cp) < 0) {
214ae38aa87Swiz 		warn("%s", cp);
215f3098750Schristos 		return 1;
21661f28255Scgd 	}
21761f28255Scgd 	return 0;
21861f28255Scgd }
21961f28255Scgd 
220f3098750Schristos /*
221f3098750Schristos  * Return the smopts field if "ReplyAsRecipient" is defined.
222f3098750Schristos  */
22385c81c58Schristos static struct name *
set_smopts(struct message * mp)22485c81c58Schristos set_smopts(struct message *mp)
22585c81c58Schristos {
22685c81c58Schristos 	char *cp;
227ca13337dSchristos 	struct name *np;
228ca13337dSchristos 	char *reply_as_recipient;
22985c81c58Schristos 
230ca13337dSchristos 	np = NULL;
231ca13337dSchristos 	reply_as_recipient = value(ENAME_REPLYASRECIPIENT);
232e7d816a1Schristos 	if (reply_as_recipient &&
23385c81c58Schristos 	    (cp = skin(hfield("to", mp))) != NULL &&
234e7d816a1Schristos 	    extract(cp, GTO)->n_flink == NULL) {  /* check for one recipient */
23585c81c58Schristos 		char *p, *q;
2368207b28aSchristos 		size_t len = strlen(cp);
2378207b28aSchristos 		/*
2388207b28aSchristos 		 * XXX - perhaps we always want to ignore
2398207b28aSchristos 		 *       "undisclosed-recipients:;" ?
2408207b28aSchristos 		 */
241e7d816a1Schristos 		for (p = q = reply_as_recipient; *p; p = q) {
242d727506fSchristos 			while (*q != '\0' && *q != ',' && !is_WSP(*q))
24385c81c58Schristos 				q++;
2448207b28aSchristos 			if (p + len == q && strncasecmp(cp, p, len) == 0)
24585c81c58Schristos 				return np;
246d727506fSchristos 			while (*q == ',' || is_WSP(*q))
24785c81c58Schristos 				q++;
24885c81c58Schristos 		}
24985c81c58Schristos 		np = extract(__UNCONST("-f"), GSMOPTS);
25085c81c58Schristos 		np = cat(np, extract(cp, GSMOPTS));
25185c81c58Schristos 	}
25285c81c58Schristos 
25385c81c58Schristos 	return np;
25485c81c58Schristos }
25585c81c58Schristos 
25661f28255Scgd /*
257f3098750Schristos  * Modify the subject we are replying to to begin with Re: if
258f3098750Schristos  * it does not already.
259f3098750Schristos  */
260f3098750Schristos static char *
reedit(char * subj,const char * pref)261349d9781Schristos reedit(char *subj, const char *pref)
262f3098750Schristos {
263f3098750Schristos 	char *newsubj;
264349d9781Schristos 	size_t preflen;
265f3098750Schristos 
266349d9781Schristos 	assert(pref != NULL);
267f3098750Schristos 	if (subj == NULL)
268349d9781Schristos 		return __UNCONST(pref);
269349d9781Schristos 	preflen = strlen(pref);
270349d9781Schristos 	if (strncasecmp(subj, pref, preflen) == 0)
271f3098750Schristos 		return subj;
272349d9781Schristos 	newsubj = salloc(strlen(subj) + preflen + 1 + 1);
273349d9781Schristos 	(void)sprintf(newsubj, "%s %s", pref, subj);
274f3098750Schristos 	return newsubj;
275f3098750Schristos }
276f3098750Schristos 
277f3098750Schristos /*
278f3098750Schristos  * Set the "In-Reply-To" and "References" header fields appropriately.
279f3098750Schristos  * Used in replies.
280f3098750Schristos  */
281f3098750Schristos static void
set_ident_fields(struct header * hp,struct message * mp)282f3098750Schristos set_ident_fields(struct header *hp, struct message *mp)
283f3098750Schristos {
284f3098750Schristos 	char *in_reply_to;
285f3098750Schristos 	char *references;
286f3098750Schristos 
287f3098750Schristos 	in_reply_to = hfield("message-id", mp);
288f3098750Schristos 	hp->h_in_reply_to = in_reply_to;
289f3098750Schristos 
290f3098750Schristos 	references = hfield("references", mp);
291f3098750Schristos 	hp->h_references = extract(references, GMISC);
292f3098750Schristos 	hp->h_references = cat(hp->h_references, extract(in_reply_to, GMISC));
293f3098750Schristos }
294f3098750Schristos 
295f3098750Schristos /*
29661f28255Scgd  * Reply to a list of messages.  Extract each name from the
29761f28255Scgd  * message header and send them off to mail1()
29861f28255Scgd  */
299f3098750Schristos static int
respond_core(int * msgvec)3004556f89aSchristos respond_core(int *msgvec)
30161f28255Scgd {
30261f28255Scgd 	struct message *mp;
30361f28255Scgd 	char *cp, *rcv, *replyto;
30461f28255Scgd 	char **ap;
30561f28255Scgd 	struct name *np;
30661f28255Scgd 	struct header head;
30761f28255Scgd 
308f3098750Schristos 	/* ensure that all header fields are initially NULL */
309f3098750Schristos 	(void)memset(&head, 0, sizeof(head));
310f3098750Schristos 
31161f28255Scgd 	if (msgvec[1] != 0) {
312ca286310Schristos 		(void)printf("Sorry, can't reply to multiple messages at once\n");
313f3098750Schristos 		return 1;
31461f28255Scgd 	}
315f3098750Schristos 	mp = get_message(msgvec[0]);
31661f28255Scgd 	touch(mp);
31761f28255Scgd 	dot = mp;
318ab850155Swiz 	if ((rcv = skin(hfield("from", mp))) == NULL)
31961f28255Scgd 		rcv = skin(nameof(mp, 1));
320ab850155Swiz 	if ((replyto = skin(hfield("reply-to", mp))) != NULL)
32161f28255Scgd 		np = extract(replyto, GTO);
322ab850155Swiz 	else if ((cp = skin(hfield("to", mp))) != NULL)
32361f28255Scgd 		np = extract(cp, GTO);
32461f28255Scgd 	else
325cb6786d4Swiz 		np = NULL;
32661f28255Scgd 	np = elide(np);
32761f28255Scgd 	/*
32861f28255Scgd 	 * Delete my name from the reply list,
32961f28255Scgd 	 * and with it, all my alternate names.
33061f28255Scgd 	 */
33161f28255Scgd 	np = delname(np, myname);
33261f28255Scgd 	if (altnames)
33361f28255Scgd 		for (ap = altnames; *ap; ap++)
33461f28255Scgd 			np = delname(np, *ap);
335cb6786d4Swiz 	if (np != NULL && replyto == NULL)
33661f28255Scgd 		np = cat(np, extract(rcv, GTO));
337cb6786d4Swiz 	else if (np == NULL) {
338ab850155Swiz 		if (replyto != NULL)
339ca286310Schristos 			(void)printf("Empty reply-to field -- replying to author\n");
34061f28255Scgd 		np = extract(rcv, GTO);
34161f28255Scgd 	}
34261f28255Scgd 	head.h_to = np;
343ab850155Swiz 	if ((head.h_subject = hfield("subject", mp)) == NULL)
34461f28255Scgd 		head.h_subject = hfield("subj", mp);
345349d9781Schristos 	head.h_subject = reedit(head.h_subject, "Re:");
346ab850155Swiz 	if (replyto == NULL && (cp = skin(hfield("cc", mp))) != NULL) {
34761f28255Scgd 		np = elide(extract(cp, GCC));
34861f28255Scgd 		np = delname(np, myname);
34961f28255Scgd 		if (altnames != 0)
35061f28255Scgd 			for (ap = altnames; *ap; ap++)
35161f28255Scgd 				np = delname(np, *ap);
35261f28255Scgd 		head.h_cc = np;
35361f28255Scgd 	} else
354cb6786d4Swiz 		head.h_cc = NULL;
355cb6786d4Swiz 	head.h_bcc = NULL;
35685c81c58Schristos 	head.h_smopts = set_smopts(mp);
3578207b28aSchristos #ifdef MIME_SUPPORT
3588207b28aSchristos 	head.h_attach = NULL;
3598207b28aSchristos #endif
360f3098750Schristos 	set_ident_fields(&head, mp);
36161f28255Scgd 	mail1(&head, 1);
362f3098750Schristos 	return 0;
36361f28255Scgd }
36461f28255Scgd 
36561f28255Scgd /*
366f3098750Schristos  * Reply to a series of messages by simply mailing to the senders
367f3098750Schristos  * and not messing around with the To: and Cc: lists as in normal
368f3098750Schristos  * reply.
36961f28255Scgd  */
370f3098750Schristos static int
Respond_core(int msgvec[])3714556f89aSchristos Respond_core(int msgvec[])
37261f28255Scgd {
373f3098750Schristos 	struct header head;
374f3098750Schristos 	struct message *mp;
375f3098750Schristos 	int *ap;
376f3098750Schristos 	char *cp;
37761f28255Scgd 
378f3098750Schristos 	/* ensure that all header fields are initially NULL */
379f3098750Schristos 	(void)memset(&head, 0, sizeof(head));
380f3098750Schristos 
381f3098750Schristos 	head.h_to = NULL;
382f3098750Schristos 	for (ap = msgvec; *ap != 0; ap++) {
383f3098750Schristos 		mp = get_message(*ap);
384f3098750Schristos 		touch(mp);
385f3098750Schristos 		dot = mp;
386f3098750Schristos 		if ((cp = skin(hfield("from", mp))) == NULL)
387f3098750Schristos 			cp = skin(nameof(mp, 2));
388f3098750Schristos 		head.h_to = cat(head.h_to, extract(cp, GTO));
389f3098750Schristos 	}
390f3098750Schristos 	if (head.h_to == NULL)
391f3098750Schristos 		return 0;
392f3098750Schristos 	mp = get_message(msgvec[0]);
393f3098750Schristos 	if ((head.h_subject = hfield("subject", mp)) == NULL)
394f3098750Schristos 		head.h_subject = hfield("subj", mp);
395349d9781Schristos 	head.h_subject = reedit(head.h_subject, "Re:");
396f3098750Schristos 	head.h_cc = NULL;
397f3098750Schristos 	head.h_bcc = NULL;
398f3098750Schristos 	head.h_smopts = set_smopts(mp);
399f3098750Schristos #ifdef MIME_SUPPORT
400f3098750Schristos 	head.h_attach = NULL;
401f3098750Schristos #endif
402f3098750Schristos 	set_ident_fields(&head, mp);
403f3098750Schristos 	mail1(&head, 1);
404f3098750Schristos 	return 0;
405f3098750Schristos }
406f3098750Schristos 
407f3098750Schristos PUBLIC int
respond(void * v)408f3098750Schristos respond(void *v)
409f3098750Schristos {
410f3098750Schristos 	int *msgvec = v;
411f3098750Schristos 	if (value(ENAME_REPLYALL) == NULL)
4124556f89aSchristos 		return respond_core(msgvec);
413f3098750Schristos 	else
4144556f89aSchristos 		return Respond_core(msgvec);
415f3098750Schristos }
416f3098750Schristos 
417f3098750Schristos PUBLIC int
Respond(void * v)418f3098750Schristos Respond(void *v)
419f3098750Schristos {
420f3098750Schristos 	int *msgvec = v;
421f3098750Schristos 	if (value(ENAME_REPLYALL) == NULL)
4224556f89aSchristos 		return Respond_core(msgvec);
423f3098750Schristos 	else
4244556f89aSchristos 		return respond_core(msgvec);
4254556f89aSchristos }
4264556f89aSchristos 
427fef32d86Schristos #ifdef MIME_SUPPORT
428349d9781Schristos static int
forward_one(int msgno,struct name * h_to)429349d9781Schristos forward_one(int msgno, struct name *h_to)
430349d9781Schristos {
431349d9781Schristos 	struct attachment attach;
432349d9781Schristos 	struct message *mp;
433349d9781Schristos 	struct header hdr;
434349d9781Schristos 
435349d9781Schristos 	mp = get_message(msgno);
436349d9781Schristos 	if (mp == NULL) {
437349d9781Schristos 		(void)printf("no such message %d\n", msgno);
438349d9781Schristos 		return 1;
439349d9781Schristos 	}
440349d9781Schristos 	(void)printf("message %d\n", msgno);
441349d9781Schristos 
442349d9781Schristos 	(void)memset(&attach, 0, sizeof(attach));
443349d9781Schristos 	attach.a_type = ATTACH_MSG;
444349d9781Schristos 	attach.a_msg = mp;
445349d9781Schristos 	attach.a_Content = get_mime_content(&attach, 0);
446349d9781Schristos 
447349d9781Schristos 	(void)memset(&hdr, 0, sizeof(hdr));
448349d9781Schristos 	hdr.h_to = h_to;
449349d9781Schristos 	if ((hdr.h_subject = hfield("subject", mp)) == NULL)
450349d9781Schristos 		hdr.h_subject = hfield("subj", mp);
451349d9781Schristos 	hdr.h_subject = reedit(hdr.h_subject, "Fwd:");
452349d9781Schristos 	hdr.h_attach = &attach;
453349d9781Schristos 	hdr.h_smopts = set_smopts(mp);
454349d9781Schristos 
455349d9781Schristos 	set_ident_fields(&hdr, mp);
456349d9781Schristos 	mail1(&hdr, 1);
457349d9781Schristos 	return 0;
458349d9781Schristos }
459349d9781Schristos 
4604556f89aSchristos PUBLIC int
forward(void * v)4614556f89aSchristos forward(void *v)
4624556f89aSchristos {
4634556f89aSchristos 	int *msgvec = v;
4644556f89aSchristos 	int *ip;
465349d9781Schristos 	struct header hdr;
466349d9781Schristos 	int rval;
467349d9781Schristos 
468349d9781Schristos 	if (forwardtab[0].i_count == 0) {
469349d9781Schristos 		/* setup the forward tab */
470349d9781Schristos 		add_ignore("Status", forwardtab);
471349d9781Schristos 	}
472349d9781Schristos 	(void)memset(&hdr, 0, sizeof(hdr));
473349d9781Schristos 	if ((rval = grabh(&hdr, GTO)) != 0)
474349d9781Schristos 		return rval;
475349d9781Schristos 
476349d9781Schristos 	if (hdr.h_to == NULL) {
477349d9781Schristos 		(void)printf("address missing!\n");
478349d9781Schristos 		return 1;
479349d9781Schristos 	}
480349d9781Schristos 	for (ip = msgvec; *ip; ip++) {
481349d9781Schristos 		int e;
482349d9781Schristos 		if ((e = forward_one(*ip, hdr.h_to)) != 0)
483349d9781Schristos 			return e;
484349d9781Schristos 	}
4854556f89aSchristos 	return 0;
4864556f89aSchristos }
487fef32d86Schristos #endif /* MIME_SUPPORT */
4884556f89aSchristos 
4894556f89aSchristos static int
bounce_one(int msgno,const char ** smargs,struct name * h_to)4904556f89aSchristos bounce_one(int msgno, const char **smargs, struct name *h_to)
4914556f89aSchristos {
4924556f89aSchristos 	char mailtempname[PATHSIZE];
4934556f89aSchristos 	struct message *mp;
4944556f89aSchristos 	int fd;
4954556f89aSchristos 	FILE *obuf;
4964556f89aSchristos 	int rval;
4974556f89aSchristos 
4984556f89aSchristos 	rval = 0;
4994556f89aSchristos 
5004556f89aSchristos 	obuf = NULL;
5014556f89aSchristos 	(void)snprintf(mailtempname, sizeof(mailtempname),
5024556f89aSchristos 	    "%s/mail.RsXXXXXXXXXX", tmpdir);
5034556f89aSchristos 	if ((fd = mkstemp(mailtempname)) == -1 ||
504*4fe1ef32Schristos 	    (obuf = Fdopen(fd, "wef+")) == NULL) {
5054556f89aSchristos 		if (fd != -1)
5064556f89aSchristos 			(void)close(fd);
5074556f89aSchristos 		warn("%s", mailtempname);
5084556f89aSchristos 		rval = 1;
5094556f89aSchristos 		goto done;
5104556f89aSchristos 	}
5114556f89aSchristos 	(void)rm(mailtempname);
5124556f89aSchristos 
5134556f89aSchristos 	mp = get_message(msgno);
5144556f89aSchristos 
5154556f89aSchristos 	if (mp == NULL) {
5164556f89aSchristos 		(void)printf("no such message %d\n", msgno);
5174556f89aSchristos 		rval = 1;
5184556f89aSchristos 		goto done;
5194556f89aSchristos 	}
5204556f89aSchristos 	else {
5214556f89aSchristos 		char *cp;
5224556f89aSchristos 		char **ap;
5234556f89aSchristos 		struct name *np;
5244556f89aSchristos 		struct header hdr;
5254556f89aSchristos 
5264556f89aSchristos 		/*
5274556f89aSchristos 		 * Construct and output a new "To:" field:
5284556f89aSchristos 		 * Remove our address from anything in the old "To:" field
5294556f89aSchristos 		 * and append that list to the bounce address(es).
5304556f89aSchristos 		 */
5314556f89aSchristos 		np = NULL;
5324556f89aSchristos 		if ((cp = skin(hfield("to", mp))) != NULL)
5334556f89aSchristos 			np = extract(cp, GTO);
5344556f89aSchristos 		np = delname(np, myname);
5354556f89aSchristos 		if (altnames)
5364556f89aSchristos 			for (ap = altnames; *ap; ap++)
5374556f89aSchristos 				np = delname(np, *ap);
5384556f89aSchristos 		np = cat(h_to, np);
5394556f89aSchristos 		(void)memset(&hdr, 0, sizeof(hdr));
5404556f89aSchristos 		hdr.h_to = elide(np);
5414556f89aSchristos 		(void)puthead(&hdr, obuf, GTO | GCOMMA);
5424556f89aSchristos 	}
5434556f89aSchristos 	if (sendmessage(mp, obuf, bouncetab, NULL, NULL)) {
5444556f89aSchristos 		(void)printf("bounce failed for message %d\n", msgno);
5454556f89aSchristos 		rval = 1;
5464556f89aSchristos 		goto done;
5474556f89aSchristos 	}
5484556f89aSchristos 	rewind(obuf);	/* XXX - here or inside mail2() */
5494556f89aSchristos 	mail2(obuf, smargs);
5504556f89aSchristos  done:
5514556f89aSchristos 	if (obuf)
5524556f89aSchristos 		(void)Fclose(obuf);
5534556f89aSchristos 	return rval;
5544556f89aSchristos }
5554556f89aSchristos 
5564556f89aSchristos PUBLIC int
bounce(void * v)5574556f89aSchristos bounce(void *v)
5584556f89aSchristos {
559ca13337dSchristos 	int *msgvec;
5604556f89aSchristos 	int *ip;
5614556f89aSchristos 	const char **smargs;
5624556f89aSchristos 	struct header hdr;
5634556f89aSchristos 	int rval;
5644556f89aSchristos 
565ca13337dSchristos 	msgvec = v;
5664556f89aSchristos 	if (bouncetab[0].i_count == 0) {
5674556f89aSchristos 		/* setup the bounce tab */
5684556f89aSchristos 		add_ignore("Status", bouncetab);
5694556f89aSchristos 		add_ignore("Delivered-To", bouncetab);
5704556f89aSchristos 		add_ignore("To", bouncetab);
5714556f89aSchristos 		add_ignore("X-Original-To", bouncetab);
5724556f89aSchristos 	}
5734556f89aSchristos 	(void)memset(&hdr, 0, sizeof(hdr));
5744556f89aSchristos 	if ((rval = grabh(&hdr, GTO)) != 0)
5754556f89aSchristos 		return rval;
5764556f89aSchristos 
5774556f89aSchristos 	if (hdr.h_to == NULL)
5784556f89aSchristos 		return 1;
5794556f89aSchristos 
5802a8765d5Schristos 	smargs = unpack(NULL, hdr.h_to);
5814556f89aSchristos 	for (ip = msgvec; *ip; ip++) {
5824556f89aSchristos 		int e;
5834556f89aSchristos 		if ((e = bounce_one(*ip, smargs, hdr.h_to)) != 0)
5844556f89aSchristos 			return e;
5854556f89aSchristos 	}
5864556f89aSchristos 	return 0;
58761f28255Scgd }
58861f28255Scgd 
58961f28255Scgd /*
59061f28255Scgd  * Preserve the named messages, so that they will be sent
59161f28255Scgd  * back to the system mailbox.
59261f28255Scgd  */
593f3098750Schristos PUBLIC int
preserve(void * v)594b127ccccSwiz preserve(void *v)
59561f28255Scgd {
596ca13337dSchristos 	int *msgvec;
597f3098750Schristos 	int *ip;
59861f28255Scgd 
599ca13337dSchristos 	msgvec = v;
60061f28255Scgd 	if (edit) {
601ca286310Schristos 		(void)printf("Cannot \"preserve\" in edit mode\n");
602f3098750Schristos 		return 1;
60361f28255Scgd 	}
604f3098750Schristos 	for (ip = msgvec; *ip != 0; ip++)
605f3098750Schristos 		dot = set_m_flag(*ip, ~(MBOX | MPRESERVE), MPRESERVE);
606f3098750Schristos 
607f3098750Schristos 	return 0;
60861f28255Scgd }
60961f28255Scgd 
61061f28255Scgd /*
611f3098750Schristos  * Mark all given messages as unread, preserving the new status.
61261f28255Scgd  */
613f3098750Schristos PUBLIC int
unread(void * v)614b127ccccSwiz unread(void *v)
61561f28255Scgd {
616ca13337dSchristos 	int *msgvec;
6177c81c8f3Slukem 	int *ip;
61861f28255Scgd 
619ca13337dSchristos 	msgvec = v;
620f3098750Schristos 	for (ip = msgvec; *ip != 0; ip++)
621f3098750Schristos 		dot = set_m_flag(*ip, ~(MREAD | MTOUCH | MSTATUS), MSTATUS);
622f3098750Schristos 
623f3098750Schristos 	return 0;
62461f28255Scgd }
62561f28255Scgd 
62661f28255Scgd /*
627798fbc60Schristos  * Mark all given messages as read.
628798fbc60Schristos  */
629f3098750Schristos PUBLIC int
markread(void * v)630798fbc60Schristos markread(void *v)
631798fbc60Schristos {
632ca13337dSchristos 	int *msgvec;
633798fbc60Schristos 	int *ip;
634798fbc60Schristos 
635ca13337dSchristos 	msgvec = v;
636f3098750Schristos 	for (ip = msgvec; *ip != 0; ip++)
637f3098750Schristos 		dot = set_m_flag(*ip,
638f3098750Schristos 		    ~(MNEW | MTOUCH | MREAD | MSTATUS), MREAD | MSTATUS);
639f3098750Schristos 
640f3098750Schristos 	return 0;
641798fbc60Schristos }
642798fbc60Schristos 
643798fbc60Schristos /*
64461f28255Scgd  * Print the size of each message.
64561f28255Scgd  */
646f3098750Schristos PUBLIC int
messize(void * v)647b127ccccSwiz messize(void *v)
64861f28255Scgd {
649ca13337dSchristos 	int *msgvec;
6507c81c8f3Slukem 	struct message *mp;
6517c81c8f3Slukem 	int *ip, mesg;
65261f28255Scgd 
653ca13337dSchristos 	msgvec = v;
654f890b048Spk 	for (ip = msgvec; *ip != 0; ip++) {
65561f28255Scgd 		mesg = *ip;
656f3098750Schristos 		mp = get_message(mesg);
657ca286310Schristos 		(void)printf("%d: %ld/%llu\n", mesg, mp->m_blines,
658ca286310Schristos 		    (unsigned long long)mp->m_size);
65961f28255Scgd 	}
660f3098750Schristos 	return 0;
66161f28255Scgd }
66261f28255Scgd 
66361f28255Scgd /*
66461f28255Scgd  * Quit quickly.  If we are sourcing, just pop the input level
66561f28255Scgd  * by returning an error.
66661f28255Scgd  */
667ca286310Schristos /*ARGSUSED*/
668f3098750Schristos PUBLIC int
rexit(void * v __unused)6698207b28aSchristos rexit(void *v __unused)
67061f28255Scgd {
67161f28255Scgd 	if (sourcing)
672f3098750Schristos 		return 1;
67388b833a7Schristos 	exit(0);
67461f28255Scgd 	/*NOTREACHED*/
67561f28255Scgd }
67661f28255Scgd 
67761f28255Scgd /*
67861f28255Scgd  * Set or display a variable value.  Syntax is similar to that
67961f28255Scgd  * of csh.
68061f28255Scgd  */
681f3098750Schristos PUBLIC int
set(void * v)682b127ccccSwiz set(void *v)
68361f28255Scgd {
684f3098750Schristos 	const char **arglist = v;
6857c81c8f3Slukem 	struct var *vp;
686ece0fd5cSchristos 	const char *cp;
687f3098750Schristos 	char varbuf[LINESIZE];
688f3098750Schristos 	const char **ap, **p;
68961f28255Scgd 	int errs, h, s;
690405db788Sross 	size_t l;
69161f28255Scgd 
692ab850155Swiz 	if (*arglist == NULL) {
69361f28255Scgd 		for (h = 0, s = 1; h < HSHSIZE; h++)
694cb6786d4Swiz 			for (vp = variables[h]; vp != NULL; vp = vp->v_link)
69561f28255Scgd 				s++;
6962283d346Schristos 		ap = salloc(s * sizeof(*ap));
69761f28255Scgd 		for (h = 0, p = ap; h < HSHSIZE; h++)
698cb6786d4Swiz 			for (vp = variables[h]; vp != NULL; vp = vp->v_link)
69961f28255Scgd 				*p++ = vp->v_name;
700ab850155Swiz 		*p = NULL;
70161f28255Scgd 		sort(ap);
702ab850155Swiz 		for (p = ap; *p != NULL; p++)
703ca286310Schristos 			(void)printf("%s\t%s\n", *p, value(*p));
704f3098750Schristos 		return 0;
70561f28255Scgd 	}
70661f28255Scgd 	errs = 0;
707ab850155Swiz 	for (ap = arglist; *ap != NULL; ap++) {
70861f28255Scgd 		cp = *ap;
70961f28255Scgd 		while (*cp != '=' && *cp != '\0')
710405db788Sross 			++cp;
711405db788Sross 		l = cp - *ap;
7122283d346Schristos 		if (l >= sizeof(varbuf))
713f3098750Schristos 			l = sizeof(varbuf) - 1;
714ca286310Schristos 		(void)strncpy(varbuf, *ap, l);
715714ccc9cSross 		varbuf[l] = '\0';
71661f28255Scgd 		if (*cp == '\0')
71761f28255Scgd 			cp = "";
71861f28255Scgd 		else
71961f28255Scgd 			cp++;
72061f28255Scgd 		if (equal(varbuf, "")) {
721ca286310Schristos 			(void)printf("Non-null variable name required\n");
72261f28255Scgd 			errs++;
72361f28255Scgd 			continue;
72461f28255Scgd 		}
72561f28255Scgd 		assign(varbuf, cp);
72661f28255Scgd 	}
727f3098750Schristos 	return errs;
72861f28255Scgd }
72961f28255Scgd 
73061f28255Scgd /*
73161f28255Scgd  * Unset a bunch of variable values.
73261f28255Scgd  */
733f3098750Schristos PUBLIC int
unset(void * v)734b127ccccSwiz unset(void *v)
73561f28255Scgd {
73688b833a7Schristos 	char **arglist = v;
7377c81c8f3Slukem 	struct var *vp, *vp2;
73861f28255Scgd 	int errs, h;
73961f28255Scgd 	char **ap;
74061f28255Scgd 
74161f28255Scgd 	errs = 0;
742ab850155Swiz 	for (ap = arglist; *ap != NULL; ap++) {
743cb6786d4Swiz 		if ((vp2 = lookup(*ap)) == NULL) {
74411976ebbSdean 			if (getenv(*ap)) {
745ca286310Schristos 				(void)unsetenv(*ap);
74611976ebbSdean 			} else if (!sourcing) {
747ca286310Schristos 				(void)printf("\"%s\": undefined variable\n", *ap);
74861f28255Scgd 				errs++;
74961f28255Scgd 			}
75061f28255Scgd 			continue;
75161f28255Scgd 		}
75261f28255Scgd 		h = hash(*ap);
75361f28255Scgd 		if (vp2 == variables[h]) {
75461f28255Scgd 			variables[h] = variables[h]->v_link;
75555ce51b2Swsanchez 			v_free(vp2->v_name);
75655ce51b2Swsanchez                         v_free(vp2->v_value);
757ca286310Schristos 			free(vp2);
75861f28255Scgd 			continue;
75961f28255Scgd 		}
76061f28255Scgd 		for (vp = variables[h]; vp->v_link != vp2; vp = vp->v_link)
761f3098750Schristos 			continue;
76261f28255Scgd 		vp->v_link = vp2->v_link;
76355ce51b2Swsanchez                 v_free(vp2->v_name);
76455ce51b2Swsanchez                 v_free(vp2->v_value);
765ca286310Schristos 		free(vp2);
76661f28255Scgd 	}
767f3098750Schristos 	return errs;
76861f28255Scgd }
76961f28255Scgd 
77061f28255Scgd /*
77185c81c58Schristos  * Show a variable value.
77285c81c58Schristos  */
773f3098750Schristos PUBLIC int
show(void * v)77485c81c58Schristos show(void *v)
77585c81c58Schristos {
776f3098750Schristos 	const char **arglist = v;
77785c81c58Schristos 	struct var *vp;
778f3098750Schristos 	const char **ap, **p;
77985c81c58Schristos 	int h, s;
78085c81c58Schristos 
78185c81c58Schristos 	if (*arglist == NULL) {
78285c81c58Schristos 		for (h = 0, s = 1; h < HSHSIZE; h++)
78385c81c58Schristos 			for (vp = variables[h]; vp != NULL; vp = vp->v_link)
78485c81c58Schristos 				s++;
7852283d346Schristos 		ap = salloc(s * sizeof(*ap));
78685c81c58Schristos 		for (h = 0, p = ap; h < HSHSIZE; h++)
78785c81c58Schristos 			for (vp = variables[h]; vp != NULL; vp = vp->v_link)
78885c81c58Schristos 				*p++ = vp->v_name;
78985c81c58Schristos 		*p = NULL;
79085c81c58Schristos 		sort(ap);
79185c81c58Schristos 		for (p = ap; *p != NULL; p++)
79285c81c58Schristos 			(void)printf("%s=%s\n", *p, value(*p));
793f3098750Schristos 		return 0;
79485c81c58Schristos 	}
79585c81c58Schristos 
79685c81c58Schristos 	for (ap = arglist; *ap != NULL; ap++) {
79785c81c58Schristos 		char *val = value(*ap);
79885c81c58Schristos 		(void)printf("%s=%s\n", *ap, val ? val : "<null>");
79985c81c58Schristos 	}
80085c81c58Schristos 	return 0;
80185c81c58Schristos }
80285c81c58Schristos 
803798fbc60Schristos 
80485c81c58Schristos /*
80561f28255Scgd  * Put add users to a group.
80661f28255Scgd  */
807f3098750Schristos PUBLIC int
group(void * v)808b127ccccSwiz group(void *v)
80961f28255Scgd {
810f3098750Schristos 	const char **argv = v;
8117c81c8f3Slukem 	struct grouphead *gh;
8127c81c8f3Slukem 	struct group *gp;
8137c81c8f3Slukem 	int h;
81461f28255Scgd 	int s;
815f3098750Schristos 	const char *gname;
816f3098750Schristos 	const char **ap, **p;
81761f28255Scgd 
818ab850155Swiz 	if (*argv == NULL) {
81961f28255Scgd 		for (h = 0, s = 1; h < HSHSIZE; h++)
820cb6786d4Swiz 			for (gh = groups[h]; gh != NULL; gh = gh->g_link)
82161f28255Scgd 				s++;
8222283d346Schristos 		ap = salloc(s * sizeof(*ap));
82361f28255Scgd 		for (h = 0, p = ap; h < HSHSIZE; h++)
824cb6786d4Swiz 			for (gh = groups[h]; gh != NULL; gh = gh->g_link)
82561f28255Scgd 				*p++ = gh->g_name;
826ab850155Swiz 		*p = NULL;
82761f28255Scgd 		sort(ap);
828ab850155Swiz 		for (p = ap; *p != NULL; p++)
82961f28255Scgd 			printgroup(*p);
830f3098750Schristos 		return 0;
83161f28255Scgd 	}
832ab850155Swiz 	if (argv[1] == NULL) {
83361f28255Scgd 		printgroup(*argv);
834f3098750Schristos 		return 0;
83561f28255Scgd 	}
83661f28255Scgd 	gname = *argv;
83761f28255Scgd 	h = hash(gname);
838cb6786d4Swiz 	if ((gh = findgroup(gname)) == NULL) {
8392283d346Schristos 		gh = ecalloc(1, sizeof(*gh));
84061f28255Scgd 		gh->g_name = vcopy(gname);
841cb6786d4Swiz 		gh->g_list = NULL;
84261f28255Scgd 		gh->g_link = groups[h];
84361f28255Scgd 		groups[h] = gh;
84461f28255Scgd 	}
84561f28255Scgd 
84661f28255Scgd 	/*
84761f28255Scgd 	 * Insert names from the command list into the group.
84861f28255Scgd 	 * Who cares if there are duplicates?  They get tossed
84961f28255Scgd 	 * later anyway.
85061f28255Scgd 	 */
85161f28255Scgd 
852ab850155Swiz 	for (ap = argv + 1; *ap != NULL; ap++) {
8532283d346Schristos 		gp = ecalloc(1, sizeof(*gp));
85461f28255Scgd 		gp->ge_name = vcopy(*ap);
85561f28255Scgd 		gp->ge_link = gh->g_list;
85661f28255Scgd 		gh->g_list = gp;
85761f28255Scgd 	}
858463f84daSchristos 	return 0;
859463f84daSchristos }
860463f84daSchristos 
861463f84daSchristos /*
862463f84daSchristos  * Delete the named group alias. Return zero if the group was
863463f84daSchristos  * successfully deleted, or -1 if there was no such group.
864463f84daSchristos  */
865463f84daSchristos static int
delgroup(const char * name)866463f84daSchristos delgroup(const char *name)
867463f84daSchristos {
868463f84daSchristos 	struct grouphead *gh, *p;
869463f84daSchristos 	struct group *g;
870463f84daSchristos 	int h;
871463f84daSchristos 
872463f84daSchristos 	h = hash(name);
873463f84daSchristos 	for (gh = groups[h], p = NULL; gh != NULL; p = gh, gh = gh->g_link)
874463f84daSchristos 		if (strcmp(gh->g_name, name) == 0) {
875463f84daSchristos 			if (p == NULL)
876463f84daSchristos 				groups[h] = gh->g_link;
877463f84daSchristos 			else
878463f84daSchristos 				p->g_link = gh->g_link;
879463f84daSchristos 			while (gh->g_list != NULL) {
880463f84daSchristos 				g = gh->g_list;
881463f84daSchristos 				gh->g_list = g->ge_link;
882463f84daSchristos 				free(g->ge_name);
883463f84daSchristos 				free(g);
884463f84daSchristos 			}
885463f84daSchristos 			free(gh->g_name);
886463f84daSchristos 			free(gh);
887463f84daSchristos 			return 0;
888463f84daSchristos 		}
889463f84daSchristos 	return -1;
890463f84daSchristos }
891463f84daSchristos 
892463f84daSchristos /*
893f3098750Schristos  * The unalias command takes a list of alises
894f3098750Schristos  * and discards the remembered groups of users.
89561f28255Scgd  */
896f3098750Schristos PUBLIC int
unalias(void * v)897f3098750Schristos unalias(void *v)
89861f28255Scgd {
8997c81c8f3Slukem 	char **ap;
90061f28255Scgd 
901f3098750Schristos 	for (ap = v; *ap != NULL; ap++)
902f3098750Schristos 		(void)delgroup(*ap);
903f3098750Schristos 	return 0;
90461f28255Scgd }
90561f28255Scgd 
90661f28255Scgd /*
90761f28255Scgd  * The do nothing command for comments.
90861f28255Scgd  */
90961f28255Scgd /*ARGSUSED*/
910f3098750Schristos PUBLIC int
null(void * v __unused)9118207b28aSchristos null(void *v __unused)
91261f28255Scgd {
91361f28255Scgd 	return 0;
91461f28255Scgd }
91561f28255Scgd 
91661f28255Scgd /*
91761f28255Scgd  * Change to another file.  With no argument, print information about
91861f28255Scgd  * the current file.
91961f28255Scgd  */
920f3098750Schristos PUBLIC int
file(void * v)921b127ccccSwiz file(void *v)
92261f28255Scgd {
92388b833a7Schristos 	char **argv = v;
92461f28255Scgd 
925ab850155Swiz 	if (argv[0] == NULL) {
926ca286310Schristos 		(void)newfileinfo(0);
92761f28255Scgd 		return 0;
92861f28255Scgd 	}
92961f28255Scgd 	if (setfile(*argv) < 0)
93061f28255Scgd 		return 1;
93161f28255Scgd 	announce();
932f3098750Schristos 
93361f28255Scgd 	return 0;
93461f28255Scgd }
93561f28255Scgd 
93661f28255Scgd /*
93761f28255Scgd  * Expand file names like echo
93861f28255Scgd  */
939f3098750Schristos PUBLIC int
echo(void * v)940b127ccccSwiz echo(void *v)
94161f28255Scgd {
94288b833a7Schristos 	char **argv = v;
9437c81c8f3Slukem 	char **ap;
944ece0fd5cSchristos 	const char *cp;
94561f28255Scgd 
946ab850155Swiz 	for (ap = argv; *ap != NULL; ap++) {
94761f28255Scgd 		cp = *ap;
948ab850155Swiz 		if ((cp = expand(cp)) != NULL) {
94961f28255Scgd 			if (ap != argv)
950ca286310Schristos 				(void)putchar(' ');
951ca286310Schristos 			(void)printf("%s", cp);
95261f28255Scgd 		}
95361f28255Scgd 	}
954ca286310Schristos 	(void)putchar('\n');
95561f28255Scgd 	return 0;
95661f28255Scgd }
95761f28255Scgd 
95861f28255Scgd /*
959f3098750Schristos  * Routines to push and pop the condition code to support nested
960f3098750Schristos  * if/else/endif statements.
96161f28255Scgd  */
962f3098750Schristos static void
push_cond(int c_cond)963f3098750Schristos push_cond(int c_cond)
96461f28255Scgd {
965f3098750Schristos 	struct cond_stack_s *csp;
966f3098750Schristos 	csp = emalloc(sizeof(*csp));
967f3098750Schristos 	csp->c_cond = c_cond;
968f3098750Schristos 	csp->c_next = cond_stack;
969f3098750Schristos 	cond_stack = csp;
97061f28255Scgd }
971f3098750Schristos 
972f3098750Schristos static int
pop_cond(void)973f3098750Schristos pop_cond(void)
974f3098750Schristos {
975f3098750Schristos 	int c_cond;
976f3098750Schristos 	struct cond_stack_s *csp;
977f3098750Schristos 
978f3098750Schristos 	if ((csp = cond_stack) == NULL)
979f3098750Schristos 		return -1;
980f3098750Schristos 
981f3098750Schristos 	c_cond = csp->c_cond;
982f3098750Schristos 	cond_stack = csp->c_next;
983f3098750Schristos 	free(csp);
984f3098750Schristos 	return c_cond;
98561f28255Scgd }
98661f28255Scgd 
98761f28255Scgd /*
98861f28255Scgd  * Conditional commands.  These allow one to parameterize one's
98961f28255Scgd  * .mailrc and do some things if sending, others if receiving.
99061f28255Scgd  */
991f3098750Schristos static int
if_push(void)992f3098750Schristos if_push(void)
993f3098750Schristos {
994f3098750Schristos 	push_cond(cond);
995f3098750Schristos 	cond &= ~CELSE;
996f3098750Schristos 	if ((cond & (CIF | CSKIP)) == (CIF | CSKIP)) {
997f3098750Schristos 		cond |= CIGN;
998f3098750Schristos 		return 1;
999f3098750Schristos 	}
1000f3098750Schristos 	return 0;
1001f3098750Schristos }
1002f3098750Schristos 
1003f3098750Schristos PUBLIC int
ifcmd(void * v)1004b127ccccSwiz ifcmd(void *v)
100561f28255Scgd {
100688b833a7Schristos 	char **argv = v;
1007f3098750Schristos 	char *keyword = argv[0];
1008f3098750Schristos 	static const struct modetbl_s {
1009f3098750Schristos 		const char *m_name;
1010f3098750Schristos 		enum mailmode_e m_mode;
1011f3098750Schristos 	} modetbl[] = {
1012f3098750Schristos 		{ "receiving",		mm_receiving },
1013f3098750Schristos 		{ "sending",		mm_sending },
1014f3098750Schristos 		{ "headersonly",	mm_hdrsonly },
1015f3098750Schristos 		{ NULL,			0 },
1016f3098750Schristos 	};
1017f3098750Schristos 	const struct modetbl_s *mtp;
101861f28255Scgd 
1019f3098750Schristos 	if (if_push())
1020f3098750Schristos 		return 0;
1021f3098750Schristos 
1022f3098750Schristos 	cond = CIF;
1023f3098750Schristos 	for (mtp = modetbl; mtp->m_name; mtp++)
1024f3098750Schristos 		if (strcasecmp(keyword, mtp->m_name) == 0)
102561f28255Scgd 			break;
102661f28255Scgd 
1027f3098750Schristos 	if (mtp->m_name == NULL) {
1028f3098750Schristos 		cond = CNONE;
1029f3098750Schristos 		(void)printf("Unrecognized if-keyword: \"%s\"\n", keyword);
1030f3098750Schristos 		return 1;
103161f28255Scgd 	}
1032f3098750Schristos 	if (mtp->m_mode != mailmode)
1033f3098750Schristos 		cond |= CSKIP;
1034f3098750Schristos 
1035f3098750Schristos 	return 0;
1036f3098750Schristos }
1037f3098750Schristos 
1038f3098750Schristos PUBLIC int
ifdefcmd(void * v)1039f3098750Schristos ifdefcmd(void *v)
1040f3098750Schristos {
1041f3098750Schristos 	char **argv = v;
1042f3098750Schristos 
1043f3098750Schristos 	if (if_push())
1044f3098750Schristos 		return 0;
1045f3098750Schristos 
1046f3098750Schristos 	cond = CIF;
1047f3098750Schristos 	if (value(argv[0]) == NULL)
1048f3098750Schristos 		cond |= CSKIP;
1049f3098750Schristos 
1050f3098750Schristos 	return 0;
1051f3098750Schristos }
1052f3098750Schristos 
1053f3098750Schristos PUBLIC int
ifndefcmd(void * v)1054f3098750Schristos ifndefcmd(void *v)
1055f3098750Schristos {
1056f3098750Schristos 	int rval;
1057f3098750Schristos 	rval = ifdefcmd(v);
1058f3098750Schristos 	cond ^= CSKIP;
1059f3098750Schristos 	return rval;
106061f28255Scgd }
106161f28255Scgd 
106261f28255Scgd /*
106361f28255Scgd  * Implement 'else'.  This is pretty simple -- we just
106461f28255Scgd  * flip over the conditional flag.
106561f28255Scgd  */
1066ca286310Schristos /*ARGSUSED*/
1067f3098750Schristos PUBLIC int
elsecmd(void * v __unused)10688207b28aSchristos elsecmd(void *v __unused)
106961f28255Scgd {
1070f3098750Schristos 	if (cond_stack == NULL || (cond & (CIF | CELSE)) != CIF) {
1071f3098750Schristos 		(void)printf("\"else\" without matching \"if\"\n");
1072f3098750Schristos 		cond = CNONE;
1073f3098750Schristos 		return 1;
107461f28255Scgd 	}
1075f3098750Schristos 	if ((cond & CIGN) == 0) {
1076f3098750Schristos 		cond ^= CSKIP;
1077f3098750Schristos 		cond |= CELSE;
1078f3098750Schristos 	}
1079f3098750Schristos 	return 0;
108061f28255Scgd }
108161f28255Scgd 
108261f28255Scgd /*
108361f28255Scgd  * End of if statement.  Just set cond back to anything.
108461f28255Scgd  */
1085ca286310Schristos /*ARGSUSED*/
1086f3098750Schristos PUBLIC int
endifcmd(void * v __unused)10878207b28aSchristos endifcmd(void *v __unused)
108861f28255Scgd {
1089f3098750Schristos 	if (cond_stack == NULL || (cond & CIF) != CIF) {
1090f3098750Schristos 		(void)printf("\"endif\" without matching \"if\"\n");
1091f3098750Schristos 		cond = CNONE;
1092f3098750Schristos 		return 1;
109361f28255Scgd 	}
1094f3098750Schristos 	cond = pop_cond();
1095f3098750Schristos 	return 0;
109661f28255Scgd }
109761f28255Scgd 
109861f28255Scgd /*
109961f28255Scgd  * Set the list of alternate names.
110061f28255Scgd  */
1101f3098750Schristos PUBLIC int
alternates(void * v)1102b127ccccSwiz alternates(void *v)
110361f28255Scgd {
110488b833a7Schristos 	char **namelist = v;
11054556f89aSchristos 	size_t c;
11067c81c8f3Slukem 	char **ap, **ap2, *cp;
110761f28255Scgd 
110861f28255Scgd 	c = argcount(namelist) + 1;
110961f28255Scgd 	if (c == 1) {
111061f28255Scgd 		if (altnames == 0)
1111f3098750Schristos 			return 0;
111261f28255Scgd 		for (ap = altnames; *ap; ap++)
1113ca286310Schristos 			(void)printf("%s ", *ap);
1114ca286310Schristos 		(void)printf("\n");
1115f3098750Schristos 		return 0;
111661f28255Scgd 	}
111761f28255Scgd 	if (altnames != 0)
1118ca286310Schristos 		free(altnames);
11194556f89aSchristos 	altnames = ecalloc(c, sizeof(char *));
112061f28255Scgd 	for (ap = namelist, ap2 = altnames; *ap; ap++, ap2++) {
11214556f89aSchristos 		cp = ecalloc(strlen(*ap) + 1, sizeof(char));
1122ca286310Schristos 		(void)strcpy(cp, *ap);
112361f28255Scgd 		*ap2 = cp;
112461f28255Scgd 	}
112561f28255Scgd 	*ap2 = 0;
1126f3098750Schristos 	return 0;
112761f28255Scgd }
1128