Home | History | Annotate | Line # | Download | only in mail
cmd3.c revision 1.41.6.1
      1  1.41.6.1      yamt /*	$NetBSD: cmd3.c,v 1.41.6.1 2012/05/23 10:08:25 yamt Exp $	*/
      2       1.5  christos 
      3       1.1       cgd /*
      4       1.4   deraadt  * Copyright (c) 1980, 1993
      5       1.4   deraadt  *	The Regents of the University of California.  All rights reserved.
      6       1.1       cgd  *
      7       1.1       cgd  * Redistribution and use in source and binary forms, with or without
      8       1.1       cgd  * modification, are permitted provided that the following conditions
      9       1.1       cgd  * are met:
     10       1.1       cgd  * 1. Redistributions of source code must retain the above copyright
     11       1.1       cgd  *    notice, this list of conditions and the following disclaimer.
     12       1.1       cgd  * 2. Redistributions in binary form must reproduce the above copyright
     13       1.1       cgd  *    notice, this list of conditions and the following disclaimer in the
     14       1.1       cgd  *    documentation and/or other materials provided with the distribution.
     15      1.21       agc  * 3. Neither the name of the University nor the names of its contributors
     16       1.1       cgd  *    may be used to endorse or promote products derived from this software
     17       1.1       cgd  *    without specific prior written permission.
     18       1.1       cgd  *
     19       1.1       cgd  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     20       1.1       cgd  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     21       1.1       cgd  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     22       1.1       cgd  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     23       1.1       cgd  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     24       1.1       cgd  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     25       1.1       cgd  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     26       1.1       cgd  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     27       1.1       cgd  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     28       1.1       cgd  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     29       1.1       cgd  * SUCH DAMAGE.
     30       1.1       cgd  */
     31       1.1       cgd 
     32       1.9     lukem #include <sys/cdefs.h>
     33       1.1       cgd #ifndef lint
     34       1.5  christos #if 0
     35       1.6       tls static char sccsid[] = "@(#)cmd3.c	8.2 (Berkeley) 4/20/95";
     36       1.5  christos #else
     37  1.41.6.1      yamt __RCSID("$NetBSD: cmd3.c,v 1.41.6.1 2012/05/23 10:08:25 yamt Exp $");
     38       1.5  christos #endif
     39       1.1       cgd #endif /* not lint */
     40       1.1       cgd 
     41       1.1       cgd #include "rcv.h"
     42      1.38  christos #include <assert.h>
     43      1.31  christos #include <util.h>
     44       1.4   deraadt #include "extern.h"
     45      1.31  christos #include "mime.h"
     46      1.40  christos #include "sig.h"
     47      1.33  christos #include "thread.h"
     48       1.1       cgd 
     49       1.1       cgd /*
     50       1.1       cgd  * Mail -- a mail program
     51       1.1       cgd  *
     52       1.1       cgd  * Still more user commands.
     53       1.1       cgd  */
     54      1.33  christos 
     55       1.1       cgd 
     56       1.1       cgd /*
     57      1.33  christos  * Do a dictionary order comparison of the arguments from
     58      1.33  christos  * qsort.
     59       1.1       cgd  */
     60      1.33  christos static int
     61      1.33  christos diction(const void *a, const void *b)
     62       1.1       cgd {
     63      1.40  christos 
     64      1.33  christos 	return strcmp(*(const char *const *)a, *(const char *const *)b);
     65       1.1       cgd }
     66       1.1       cgd 
     67       1.1       cgd /*
     68      1.33  christos  * Sort the passed string vector into ascending dictionary
     69      1.33  christos  * order.
     70       1.1       cgd  */
     71      1.33  christos PUBLIC void
     72      1.33  christos sort(const char **list)
     73       1.1       cgd {
     74      1.33  christos 	const char **ap;
     75       1.1       cgd 
     76      1.33  christos 	for (ap = list; *ap != NULL; ap++)
     77      1.33  christos 		continue;
     78      1.40  christos 	if (ap - list < 2)
     79      1.33  christos 		return;
     80      1.40  christos 	qsort(list, (size_t)(ap - list), sizeof(*list), diction);
     81       1.1       cgd }
     82       1.1       cgd 
     83       1.1       cgd /*
     84       1.1       cgd  * Expand the shell escape by expanding unescaped !'s into the
     85       1.1       cgd  * last issued command where possible.
     86       1.1       cgd  */
     87      1.33  christos static int
     88      1.12       wiz bangexp(char *str)
     89       1.1       cgd {
     90      1.33  christos 	static char lastbang[128];
     91      1.33  christos 	char bangbuf[LINESIZE];
     92       1.9     lukem 	char *cp, *cp2;
     93      1.40  christos 	ssize_t n;
     94      1.40  christos 	int changed;
     95       1.1       cgd 
     96      1.40  christos 	changed = 0;
     97       1.1       cgd 	cp = str;
     98       1.1       cgd 	cp2 = bangbuf;
     99      1.33  christos 	n = sizeof(bangbuf);	/* bytes left in bangbuf */
    100       1.1       cgd 	while (*cp) {
    101       1.1       cgd 		if (*cp == '!') {
    102      1.33  christos 			if (n < (int)strlen(lastbang)) {
    103      1.40  christos  overf:
    104      1.27  christos 				(void)printf("Command buffer overflow\n");
    105      1.33  christos 				return -1;
    106       1.1       cgd 			}
    107       1.1       cgd 			changed++;
    108      1.27  christos 			(void)strcpy(cp2, lastbang);
    109       1.1       cgd 			cp2 += strlen(lastbang);
    110       1.1       cgd 			n -= strlen(lastbang);
    111       1.1       cgd 			cp++;
    112       1.1       cgd 			continue;
    113       1.1       cgd 		}
    114       1.1       cgd 		if (*cp == '\\' && cp[1] == '!') {
    115       1.1       cgd 			if (--n <= 1)
    116       1.1       cgd 				goto overf;
    117       1.1       cgd 			*cp2++ = '!';
    118       1.1       cgd 			cp += 2;
    119       1.1       cgd 			changed++;
    120       1.1       cgd 		}
    121       1.1       cgd 		if (--n <= 1)
    122       1.1       cgd 			goto overf;
    123       1.1       cgd 		*cp2++ = *cp++;
    124       1.1       cgd 	}
    125       1.1       cgd 	*cp2 = 0;
    126       1.1       cgd 	if (changed) {
    127      1.27  christos 		(void)printf("!%s\n", bangbuf);
    128      1.27  christos 		(void)fflush(stdout);
    129       1.1       cgd 	}
    130      1.27  christos 	(void)strcpy(str, bangbuf);
    131      1.33  christos 	(void)strlcpy(lastbang, bangbuf, sizeof(lastbang));
    132      1.33  christos 	return 0;
    133      1.33  christos }
    134      1.33  christos 
    135      1.33  christos /*
    136      1.33  christos  * Process a shell escape by saving signals, ignoring signals,
    137      1.33  christos  * and forking a sh -c
    138      1.33  christos  */
    139      1.33  christos PUBLIC int
    140      1.33  christos shell(void *v)
    141      1.33  christos {
    142      1.40  christos 	struct sigaction osa;
    143      1.40  christos 	sigset_t oset;
    144      1.40  christos 	char *str;
    145      1.33  christos 	const char *shellcmd;
    146      1.33  christos 	char cmd[LINESIZE];
    147      1.33  christos 
    148      1.40  christos 	str = v;
    149      1.40  christos 	sig_check();
    150      1.40  christos 	(void)sig_ignore(SIGINT, &osa, &oset);
    151      1.33  christos 	(void)strcpy(cmd, str);
    152      1.33  christos 	if (bangexp(cmd) < 0)
    153      1.33  christos 		return 1;
    154      1.33  christos 	if ((shellcmd = value(ENAME_SHELL)) == NULL)
    155      1.33  christos 		shellcmd = _PATH_CSHELL;
    156      1.40  christos 	(void)run_command(shellcmd, NULL, 0, 1, "-c", cmd, NULL);
    157      1.40  christos 	(void)sig_restore(SIGINT, &osa, &oset);
    158      1.33  christos 	(void)printf("!\n");
    159      1.40  christos 	sig_check();
    160      1.33  christos 	return 0;
    161      1.33  christos }
    162      1.33  christos 
    163      1.33  christos /*
    164      1.33  christos  * Fork an interactive shell.
    165      1.33  christos  */
    166      1.33  christos /*ARGSUSED*/
    167      1.33  christos PUBLIC int
    168      1.33  christos dosh(void *v __unused)
    169      1.33  christos {
    170      1.40  christos 	struct sigaction osa;
    171      1.40  christos 	sigset_t oset;
    172      1.33  christos 	const char *shellcmd;
    173      1.33  christos 
    174      1.40  christos 	sig_check();
    175      1.40  christos 	(void)sig_ignore(SIGINT, &osa, &oset);
    176      1.33  christos 	if ((shellcmd = value(ENAME_SHELL)) == NULL)
    177      1.33  christos 		shellcmd = _PATH_CSHELL;
    178      1.40  christos 	(void)run_command(shellcmd, NULL, 0, 1, NULL);
    179      1.40  christos 	(void)sig_restore(SIGINT, &osa, &oset);
    180      1.33  christos 	(void)putchar('\n');
    181      1.40  christos 	sig_check();
    182      1.33  christos 	return 0;
    183       1.1       cgd }
    184       1.1       cgd 
    185       1.1       cgd /*
    186       1.1       cgd  * Print out a nice help message from some file or another.
    187       1.1       cgd  */
    188       1.1       cgd 
    189      1.27  christos /*ARGSUSED*/
    190      1.33  christos PUBLIC int
    191      1.31  christos help(void *v __unused)
    192       1.1       cgd {
    193      1.40  christos 
    194      1.34  christos 	cathelp(_PATH_HELP);
    195      1.33  christos 	return 0;
    196       1.1       cgd }
    197       1.1       cgd 
    198       1.1       cgd /*
    199       1.1       cgd  * Change user's working directory.
    200       1.1       cgd  */
    201      1.33  christos PUBLIC int
    202      1.12       wiz schdir(void *v)
    203       1.1       cgd {
    204      1.40  christos 	char **arglist;
    205      1.26  christos 	const char *cp;
    206       1.1       cgd 
    207      1.40  christos 	arglist = v;
    208      1.14       wiz 	if (*arglist == NULL)
    209       1.1       cgd 		cp = homedir;
    210       1.1       cgd 	else
    211      1.14       wiz 		if ((cp = expand(*arglist)) == NULL)
    212      1.33  christos 			return 1;
    213       1.1       cgd 	if (chdir(cp) < 0) {
    214      1.17       wiz 		warn("%s", cp);
    215      1.33  christos 		return 1;
    216       1.1       cgd 	}
    217       1.1       cgd 	return 0;
    218       1.1       cgd }
    219       1.1       cgd 
    220      1.33  christos /*
    221      1.33  christos  * Return the smopts field if "ReplyAsRecipient" is defined.
    222      1.33  christos  */
    223      1.29  christos static struct name *
    224      1.29  christos set_smopts(struct message *mp)
    225      1.29  christos {
    226      1.29  christos 	char *cp;
    227      1.40  christos 	struct name *np;
    228      1.40  christos 	char *reply_as_recipient;
    229      1.29  christos 
    230      1.40  christos 	np = NULL;
    231      1.40  christos 	reply_as_recipient = value(ENAME_REPLYASRECIPIENT);
    232      1.30  christos 	if (reply_as_recipient &&
    233      1.29  christos 	    (cp = skin(hfield("to", mp))) != NULL &&
    234      1.30  christos 	    extract(cp, GTO)->n_flink == NULL) {  /* check for one recipient */
    235      1.29  christos 		char *p, *q;
    236      1.31  christos 		size_t len = strlen(cp);
    237      1.31  christos 		/*
    238      1.31  christos 		 * XXX - perhaps we always want to ignore
    239      1.31  christos 		 *       "undisclosed-recipients:;" ?
    240      1.31  christos 		 */
    241      1.31  christos 		for (p = q = reply_as_recipient; *p; p = q) {
    242      1.35  christos 			while (*q != '\0' && *q != ',' && !is_WSP(*q))
    243      1.29  christos 				q++;
    244      1.31  christos 			if (p + len == q && strncasecmp(cp, p, len) == 0)
    245      1.29  christos 				return np;
    246      1.35  christos 			while (*q == ',' || is_WSP(*q))
    247      1.29  christos 				q++;
    248      1.29  christos 		}
    249      1.29  christos 		np = extract(__UNCONST("-f"), GSMOPTS);
    250      1.29  christos 		np = cat(np, extract(cp, GSMOPTS));
    251      1.29  christos 	}
    252      1.29  christos 
    253      1.29  christos 	return np;
    254      1.29  christos }
    255      1.29  christos 
    256       1.1       cgd /*
    257      1.33  christos  * Modify the subject we are replying to to begin with Re: if
    258      1.33  christos  * it does not already.
    259      1.33  christos  */
    260      1.33  christos static char *
    261      1.38  christos reedit(char *subj, const char *pref)
    262      1.33  christos {
    263      1.33  christos 	char *newsubj;
    264      1.38  christos 	size_t preflen;
    265      1.33  christos 
    266      1.38  christos 	assert(pref != NULL);
    267      1.33  christos 	if (subj == NULL)
    268      1.38  christos 		return __UNCONST(pref);
    269      1.38  christos 	preflen = strlen(pref);
    270      1.38  christos 	if (strncasecmp(subj, pref, preflen) == 0)
    271      1.33  christos 		return subj;
    272      1.38  christos 	newsubj = salloc(strlen(subj) + preflen + 1 + 1);
    273      1.38  christos 	(void)sprintf(newsubj, "%s %s", pref, subj);
    274      1.33  christos 	return newsubj;
    275      1.33  christos }
    276      1.33  christos 
    277      1.33  christos /*
    278      1.33  christos  * Set the "In-Reply-To" and "References" header fields appropriately.
    279      1.33  christos  * Used in replies.
    280      1.33  christos  */
    281      1.33  christos static void
    282      1.33  christos set_ident_fields(struct header *hp, struct message *mp)
    283      1.33  christos {
    284      1.33  christos 	char *in_reply_to;
    285      1.33  christos 	char *references;
    286      1.33  christos 
    287      1.33  christos 	in_reply_to = hfield("message-id", mp);
    288      1.33  christos 	hp->h_in_reply_to = in_reply_to;
    289      1.33  christos 
    290      1.33  christos 	references = hfield("references", mp);
    291      1.33  christos 	hp->h_references = extract(references, GMISC);
    292      1.33  christos 	hp->h_references = cat(hp->h_references, extract(in_reply_to, GMISC));
    293      1.33  christos }
    294      1.33  christos 
    295      1.33  christos /*
    296       1.1       cgd  * Reply to a list of messages.  Extract each name from the
    297       1.1       cgd  * message header and send them off to mail1()
    298       1.1       cgd  */
    299      1.33  christos static int
    300      1.36  christos respond_core(int *msgvec)
    301       1.1       cgd {
    302       1.1       cgd 	struct message *mp;
    303       1.1       cgd 	char *cp, *rcv, *replyto;
    304       1.1       cgd 	char **ap;
    305       1.1       cgd 	struct name *np;
    306       1.1       cgd 	struct header head;
    307       1.1       cgd 
    308      1.33  christos 	/* ensure that all header fields are initially NULL */
    309      1.33  christos 	(void)memset(&head, 0, sizeof(head));
    310      1.33  christos 
    311       1.1       cgd 	if (msgvec[1] != 0) {
    312      1.27  christos 		(void)printf("Sorry, can't reply to multiple messages at once\n");
    313      1.33  christos 		return 1;
    314       1.1       cgd 	}
    315      1.33  christos 	mp = get_message(msgvec[0]);
    316       1.1       cgd 	touch(mp);
    317       1.1       cgd 	dot = mp;
    318      1.14       wiz 	if ((rcv = skin(hfield("from", mp))) == NULL)
    319       1.1       cgd 		rcv = skin(nameof(mp, 1));
    320      1.14       wiz 	if ((replyto = skin(hfield("reply-to", mp))) != NULL)
    321       1.1       cgd 		np = extract(replyto, GTO);
    322      1.14       wiz 	else if ((cp = skin(hfield("to", mp))) != NULL)
    323       1.1       cgd 		np = extract(cp, GTO);
    324       1.1       cgd 	else
    325      1.15       wiz 		np = NULL;
    326       1.1       cgd 	np = elide(np);
    327       1.1       cgd 	/*
    328       1.1       cgd 	 * Delete my name from the reply list,
    329       1.1       cgd 	 * and with it, all my alternate names.
    330       1.1       cgd 	 */
    331       1.1       cgd 	np = delname(np, myname);
    332       1.1       cgd 	if (altnames)
    333       1.1       cgd 		for (ap = altnames; *ap; ap++)
    334       1.1       cgd 			np = delname(np, *ap);
    335      1.15       wiz 	if (np != NULL && replyto == NULL)
    336       1.1       cgd 		np = cat(np, extract(rcv, GTO));
    337      1.15       wiz 	else if (np == NULL) {
    338      1.14       wiz 		if (replyto != NULL)
    339      1.27  christos 			(void)printf("Empty reply-to field -- replying to author\n");
    340       1.1       cgd 		np = extract(rcv, GTO);
    341       1.1       cgd 	}
    342       1.1       cgd 	head.h_to = np;
    343      1.14       wiz 	if ((head.h_subject = hfield("subject", mp)) == NULL)
    344       1.1       cgd 		head.h_subject = hfield("subj", mp);
    345      1.38  christos 	head.h_subject = reedit(head.h_subject, "Re:");
    346      1.14       wiz 	if (replyto == NULL && (cp = skin(hfield("cc", mp))) != NULL) {
    347       1.1       cgd 		np = elide(extract(cp, GCC));
    348       1.1       cgd 		np = delname(np, myname);
    349       1.1       cgd 		if (altnames != 0)
    350       1.1       cgd 			for (ap = altnames; *ap; ap++)
    351       1.1       cgd 				np = delname(np, *ap);
    352       1.1       cgd 		head.h_cc = np;
    353       1.1       cgd 	} else
    354      1.15       wiz 		head.h_cc = NULL;
    355      1.15       wiz 	head.h_bcc = NULL;
    356      1.29  christos 	head.h_smopts = set_smopts(mp);
    357      1.31  christos #ifdef MIME_SUPPORT
    358      1.31  christos 	head.h_attach = NULL;
    359      1.31  christos #endif
    360      1.33  christos 	set_ident_fields(&head, mp);
    361       1.1       cgd 	mail1(&head, 1);
    362      1.33  christos 	return 0;
    363       1.1       cgd }
    364       1.1       cgd 
    365       1.1       cgd /*
    366      1.33  christos  * Reply to a series of messages by simply mailing to the senders
    367      1.33  christos  * and not messing around with the To: and Cc: lists as in normal
    368      1.33  christos  * reply.
    369       1.1       cgd  */
    370      1.33  christos static int
    371      1.36  christos Respond_core(int msgvec[])
    372      1.33  christos {
    373      1.33  christos 	struct header head;
    374      1.33  christos 	struct message *mp;
    375      1.33  christos 	int *ap;
    376      1.33  christos 	char *cp;
    377      1.33  christos 
    378      1.33  christos 	/* ensure that all header fields are initially NULL */
    379      1.33  christos 	(void)memset(&head, 0, sizeof(head));
    380      1.33  christos 
    381      1.33  christos 	head.h_to = NULL;
    382      1.33  christos 	for (ap = msgvec; *ap != 0; ap++) {
    383      1.33  christos 		mp = get_message(*ap);
    384      1.33  christos 		touch(mp);
    385      1.33  christos 		dot = mp;
    386      1.33  christos 		if ((cp = skin(hfield("from", mp))) == NULL)
    387      1.33  christos 			cp = skin(nameof(mp, 2));
    388      1.33  christos 		head.h_to = cat(head.h_to, extract(cp, GTO));
    389      1.33  christos 	}
    390      1.33  christos 	if (head.h_to == NULL)
    391      1.33  christos 		return 0;
    392      1.33  christos 	mp = get_message(msgvec[0]);
    393      1.33  christos 	if ((head.h_subject = hfield("subject", mp)) == NULL)
    394      1.33  christos 		head.h_subject = hfield("subj", mp);
    395      1.38  christos 	head.h_subject = reedit(head.h_subject, "Re:");
    396      1.33  christos 	head.h_cc = NULL;
    397      1.33  christos 	head.h_bcc = NULL;
    398      1.33  christos 	head.h_smopts = set_smopts(mp);
    399      1.33  christos #ifdef MIME_SUPPORT
    400      1.33  christos 	head.h_attach = NULL;
    401      1.33  christos #endif
    402      1.33  christos 	set_ident_fields(&head, mp);
    403      1.33  christos 	mail1(&head, 1);
    404      1.33  christos 	return 0;
    405      1.33  christos }
    406      1.33  christos 
    407      1.33  christos PUBLIC int
    408      1.33  christos respond(void *v)
    409       1.1       cgd {
    410      1.33  christos 	int *msgvec = v;
    411      1.33  christos 	if (value(ENAME_REPLYALL) == NULL)
    412      1.36  christos 		return respond_core(msgvec);
    413      1.33  christos 	else
    414      1.36  christos 		return Respond_core(msgvec);
    415      1.33  christos }
    416       1.1       cgd 
    417      1.33  christos PUBLIC int
    418      1.33  christos Respond(void *v)
    419      1.33  christos {
    420      1.33  christos 	int *msgvec = v;
    421      1.33  christos 	if (value(ENAME_REPLYALL) == NULL)
    422      1.36  christos 		return Respond_core(msgvec);
    423      1.33  christos 	else
    424      1.36  christos 		return respond_core(msgvec);
    425      1.36  christos }
    426      1.36  christos 
    427      1.39  christos #ifdef MIME_SUPPORT
    428      1.38  christos static int
    429      1.38  christos forward_one(int msgno, struct name *h_to)
    430      1.38  christos {
    431      1.38  christos 	struct attachment attach;
    432      1.38  christos 	struct message *mp;
    433      1.38  christos 	struct header hdr;
    434      1.38  christos 
    435      1.38  christos 	mp = get_message(msgno);
    436      1.38  christos 	if (mp == NULL) {
    437      1.38  christos 		(void)printf("no such message %d\n", msgno);
    438      1.38  christos 		return 1;
    439      1.38  christos 	}
    440      1.38  christos 	(void)printf("message %d\n", msgno);
    441      1.38  christos 
    442      1.38  christos 	(void)memset(&attach, 0, sizeof(attach));
    443      1.38  christos 	attach.a_type = ATTACH_MSG;
    444      1.38  christos 	attach.a_msg = mp;
    445      1.38  christos 	attach.a_Content = get_mime_content(&attach, 0);
    446      1.38  christos 
    447      1.38  christos 	(void)memset(&hdr, 0, sizeof(hdr));
    448      1.38  christos 	hdr.h_to = h_to;
    449      1.38  christos 	if ((hdr.h_subject = hfield("subject", mp)) == NULL)
    450      1.38  christos 		hdr.h_subject = hfield("subj", mp);
    451      1.38  christos 	hdr.h_subject = reedit(hdr.h_subject, "Fwd:");
    452      1.38  christos 	hdr.h_attach = &attach;
    453      1.38  christos 	hdr.h_smopts = set_smopts(mp);
    454      1.38  christos 
    455      1.38  christos 	set_ident_fields(&hdr, mp);
    456      1.38  christos 	mail1(&hdr, 1);
    457      1.38  christos 	return 0;
    458      1.38  christos }
    459      1.38  christos 
    460      1.36  christos PUBLIC int
    461      1.36  christos forward(void *v)
    462      1.36  christos {
    463      1.36  christos 	int *msgvec = v;
    464      1.36  christos 	int *ip;
    465      1.38  christos 	struct header hdr;
    466      1.38  christos 	int rval;
    467      1.38  christos 
    468      1.38  christos 	if (forwardtab[0].i_count == 0) {
    469      1.38  christos 		/* setup the forward tab */
    470      1.38  christos 		add_ignore("Status", forwardtab);
    471      1.38  christos 	}
    472      1.38  christos 	(void)memset(&hdr, 0, sizeof(hdr));
    473      1.38  christos 	if ((rval = grabh(&hdr, GTO)) != 0)
    474      1.38  christos 		return rval;
    475      1.38  christos 
    476      1.38  christos 	if (hdr.h_to == NULL) {
    477      1.38  christos 		(void)printf("address missing!\n");
    478      1.38  christos 		return 1;
    479      1.38  christos 	}
    480      1.41  christos 	for (ip = msgvec; *ip; ip++) {
    481      1.38  christos 		int e;
    482      1.38  christos 		if ((e = forward_one(*ip, hdr.h_to)) != 0)
    483      1.38  christos 			return e;
    484      1.38  christos 	}
    485      1.36  christos 	return 0;
    486      1.36  christos }
    487      1.39  christos #endif /* MIME_SUPPORT */
    488      1.36  christos 
    489      1.36  christos static int
    490      1.36  christos bounce_one(int msgno, const char **smargs, struct name *h_to)
    491      1.36  christos {
    492      1.36  christos 	char mailtempname[PATHSIZE];
    493      1.36  christos 	struct message *mp;
    494      1.36  christos 	int fd;
    495      1.36  christos 	FILE *obuf;
    496      1.36  christos 	int rval;
    497      1.36  christos 
    498      1.36  christos 	rval = 0;
    499      1.36  christos 
    500      1.36  christos 	obuf = NULL;
    501      1.36  christos 	(void)snprintf(mailtempname, sizeof(mailtempname),
    502      1.36  christos 	    "%s/mail.RsXXXXXXXXXX", tmpdir);
    503      1.36  christos 	if ((fd = mkstemp(mailtempname)) == -1 ||
    504  1.41.6.1      yamt 	    (obuf = Fdopen(fd, "we+")) == NULL) {
    505      1.36  christos 		if (fd != -1)
    506      1.36  christos 			(void)close(fd);
    507      1.36  christos 		warn("%s", mailtempname);
    508      1.36  christos 		rval = 1;
    509      1.36  christos 		goto done;
    510      1.36  christos 	}
    511      1.36  christos 	(void)rm(mailtempname);
    512      1.36  christos 
    513      1.36  christos 	mp = get_message(msgno);
    514      1.36  christos 
    515      1.36  christos 	if (mp == NULL) {
    516      1.36  christos 		(void)printf("no such message %d\n", msgno);
    517      1.36  christos 		rval = 1;
    518      1.36  christos 		goto done;
    519      1.36  christos 	}
    520      1.36  christos 	else {
    521      1.36  christos 		char *cp;
    522      1.36  christos 		char **ap;
    523      1.36  christos 		struct name *np;
    524      1.36  christos 		struct header hdr;
    525      1.36  christos 
    526      1.36  christos 		/*
    527      1.36  christos 		 * Construct and output a new "To:" field:
    528      1.36  christos 		 * Remove our address from anything in the old "To:" field
    529      1.36  christos 		 * and append that list to the bounce address(es).
    530      1.36  christos 		 */
    531      1.36  christos 		np = NULL;
    532      1.36  christos 		if ((cp = skin(hfield("to", mp))) != NULL)
    533      1.36  christos 			np = extract(cp, GTO);
    534      1.36  christos 		np = delname(np, myname);
    535      1.36  christos 		if (altnames)
    536      1.36  christos 			for (ap = altnames; *ap; ap++)
    537      1.36  christos 				np = delname(np, *ap);
    538      1.36  christos 		np = cat(h_to, np);
    539      1.36  christos 		(void)memset(&hdr, 0, sizeof(hdr));
    540      1.36  christos 		hdr.h_to = elide(np);
    541      1.36  christos 		(void)puthead(&hdr, obuf, GTO | GCOMMA);
    542      1.36  christos 	}
    543      1.36  christos 	if (sendmessage(mp, obuf, bouncetab, NULL, NULL)) {
    544      1.36  christos 		(void)printf("bounce failed for message %d\n", msgno);
    545      1.36  christos 		rval = 1;
    546      1.36  christos 		goto done;
    547      1.36  christos 	}
    548      1.36  christos 	rewind(obuf);	/* XXX - here or inside mail2() */
    549      1.36  christos 	mail2(obuf, smargs);
    550      1.36  christos  done:
    551      1.36  christos 	if (obuf)
    552      1.36  christos 		(void)Fclose(obuf);
    553      1.36  christos 	return rval;
    554      1.36  christos }
    555      1.36  christos 
    556      1.36  christos PUBLIC int
    557      1.36  christos bounce(void *v)
    558      1.36  christos {
    559      1.40  christos 	int *msgvec;
    560      1.36  christos 	int *ip;
    561      1.36  christos 	const char **smargs;
    562      1.36  christos 	struct header hdr;
    563      1.36  christos 	int rval;
    564      1.36  christos 
    565      1.40  christos 	msgvec = v;
    566      1.36  christos 	if (bouncetab[0].i_count == 0) {
    567      1.36  christos 		/* setup the bounce tab */
    568      1.36  christos 		add_ignore("Status", bouncetab);
    569      1.36  christos 		add_ignore("Delivered-To", bouncetab);
    570      1.36  christos 		add_ignore("To", bouncetab);
    571      1.36  christos 		add_ignore("X-Original-To", bouncetab);
    572      1.36  christos 	}
    573      1.36  christos 	(void)memset(&hdr, 0, sizeof(hdr));
    574      1.36  christos 	if ((rval = grabh(&hdr, GTO)) != 0)
    575      1.36  christos 		return rval;
    576      1.36  christos 
    577      1.36  christos 	if (hdr.h_to == NULL)
    578      1.36  christos 		return 1;
    579      1.36  christos 
    580      1.36  christos 	smargs = unpack(hdr.h_to);
    581      1.41  christos 	for (ip = msgvec; *ip; ip++) {
    582      1.36  christos 		int e;
    583      1.36  christos 		if ((e = bounce_one(*ip, smargs, hdr.h_to)) != 0)
    584      1.36  christos 			return e;
    585      1.36  christos 	}
    586      1.36  christos 	return 0;
    587       1.1       cgd }
    588       1.1       cgd 
    589       1.1       cgd /*
    590       1.1       cgd  * Preserve the named messages, so that they will be sent
    591       1.1       cgd  * back to the system mailbox.
    592       1.1       cgd  */
    593      1.33  christos PUBLIC int
    594      1.12       wiz preserve(void *v)
    595       1.1       cgd {
    596      1.40  christos 	int *msgvec;
    597      1.33  christos 	int *ip;
    598       1.1       cgd 
    599      1.40  christos 	msgvec = v;
    600       1.1       cgd 	if (edit) {
    601      1.27  christos 		(void)printf("Cannot \"preserve\" in edit mode\n");
    602      1.33  christos 		return 1;
    603       1.1       cgd 	}
    604      1.33  christos 	for (ip = msgvec; *ip != 0; ip++)
    605      1.33  christos 		dot = set_m_flag(*ip, ~(MBOX | MPRESERVE), MPRESERVE);
    606      1.33  christos 
    607      1.33  christos 	return 0;
    608       1.1       cgd }
    609       1.1       cgd 
    610       1.1       cgd /*
    611      1.33  christos  * Mark all given messages as unread, preserving the new status.
    612       1.1       cgd  */
    613      1.33  christos PUBLIC int
    614      1.12       wiz unread(void *v)
    615       1.1       cgd {
    616      1.40  christos 	int *msgvec;
    617       1.9     lukem 	int *ip;
    618       1.1       cgd 
    619      1.40  christos 	msgvec = v;
    620      1.33  christos 	for (ip = msgvec; *ip != 0; ip++)
    621      1.33  christos 		dot = set_m_flag(*ip, ~(MREAD | MTOUCH | MSTATUS), MSTATUS);
    622      1.33  christos 
    623      1.33  christos 	return 0;
    624       1.1       cgd }
    625       1.1       cgd 
    626       1.1       cgd /*
    627      1.32  christos  * Mark all given messages as read.
    628      1.32  christos  */
    629      1.33  christos PUBLIC int
    630      1.32  christos markread(void *v)
    631      1.32  christos {
    632      1.40  christos 	int *msgvec;
    633      1.32  christos 	int *ip;
    634      1.32  christos 
    635      1.40  christos 	msgvec = v;
    636      1.33  christos 	for (ip = msgvec; *ip != 0; ip++)
    637      1.33  christos 		dot = set_m_flag(*ip,
    638      1.33  christos 		    ~(MNEW | MTOUCH | MREAD | MSTATUS), MREAD | MSTATUS);
    639      1.33  christos 
    640      1.33  christos 	return 0;
    641      1.32  christos }
    642      1.32  christos 
    643      1.32  christos /*
    644       1.1       cgd  * Print the size of each message.
    645       1.1       cgd  */
    646      1.33  christos PUBLIC int
    647      1.12       wiz messize(void *v)
    648       1.1       cgd {
    649      1.40  christos 	int *msgvec;
    650       1.9     lukem 	struct message *mp;
    651       1.9     lukem 	int *ip, mesg;
    652       1.1       cgd 
    653      1.40  christos 	msgvec = v;
    654       1.7        pk 	for (ip = msgvec; *ip != 0; ip++) {
    655       1.1       cgd 		mesg = *ip;
    656      1.33  christos 		mp = get_message(mesg);
    657      1.27  christos 		(void)printf("%d: %ld/%llu\n", mesg, mp->m_blines,
    658      1.27  christos 		    (unsigned long long)mp->m_size);
    659       1.1       cgd 	}
    660      1.33  christos 	return 0;
    661       1.1       cgd }
    662       1.1       cgd 
    663       1.1       cgd /*
    664       1.1       cgd  * Quit quickly.  If we are sourcing, just pop the input level
    665       1.1       cgd  * by returning an error.
    666       1.1       cgd  */
    667      1.27  christos /*ARGSUSED*/
    668      1.33  christos PUBLIC int
    669      1.31  christos rexit(void *v __unused)
    670       1.1       cgd {
    671       1.1       cgd 	if (sourcing)
    672      1.33  christos 		return 1;
    673       1.5  christos 	exit(0);
    674       1.1       cgd 	/*NOTREACHED*/
    675       1.1       cgd }
    676       1.1       cgd 
    677       1.1       cgd /*
    678       1.1       cgd  * Set or display a variable value.  Syntax is similar to that
    679       1.1       cgd  * of csh.
    680       1.1       cgd  */
    681      1.33  christos PUBLIC int
    682      1.12       wiz set(void *v)
    683       1.1       cgd {
    684      1.33  christos 	const char **arglist = v;
    685       1.9     lukem 	struct var *vp;
    686      1.26  christos 	const char *cp;
    687      1.33  christos 	char varbuf[LINESIZE];
    688      1.33  christos 	const char **ap, **p;
    689       1.1       cgd 	int errs, h, s;
    690      1.23      ross 	size_t l;
    691       1.1       cgd 
    692      1.14       wiz 	if (*arglist == NULL) {
    693       1.1       cgd 		for (h = 0, s = 1; h < HSHSIZE; h++)
    694      1.15       wiz 			for (vp = variables[h]; vp != NULL; vp = vp->v_link)
    695       1.1       cgd 				s++;
    696      1.37  christos 		ap = salloc(s * sizeof(*ap));
    697       1.1       cgd 		for (h = 0, p = ap; h < HSHSIZE; h++)
    698      1.15       wiz 			for (vp = variables[h]; vp != NULL; vp = vp->v_link)
    699       1.1       cgd 				*p++ = vp->v_name;
    700      1.14       wiz 		*p = NULL;
    701       1.1       cgd 		sort(ap);
    702      1.14       wiz 		for (p = ap; *p != NULL; p++)
    703      1.27  christos 			(void)printf("%s\t%s\n", *p, value(*p));
    704      1.33  christos 		return 0;
    705       1.1       cgd 	}
    706       1.1       cgd 	errs = 0;
    707      1.14       wiz 	for (ap = arglist; *ap != NULL; ap++) {
    708       1.1       cgd 		cp = *ap;
    709       1.1       cgd 		while (*cp != '=' && *cp != '\0')
    710      1.23      ross 			++cp;
    711      1.23      ross 		l = cp - *ap;
    712      1.37  christos 		if (l >= sizeof(varbuf))
    713      1.33  christos 			l = sizeof(varbuf) - 1;
    714      1.27  christos 		(void)strncpy(varbuf, *ap, l);
    715      1.24      ross 		varbuf[l] = '\0';
    716       1.1       cgd 		if (*cp == '\0')
    717       1.1       cgd 			cp = "";
    718       1.1       cgd 		else
    719       1.1       cgd 			cp++;
    720       1.1       cgd 		if (equal(varbuf, "")) {
    721      1.27  christos 			(void)printf("Non-null variable name required\n");
    722       1.1       cgd 			errs++;
    723       1.1       cgd 			continue;
    724       1.1       cgd 		}
    725       1.1       cgd 		assign(varbuf, cp);
    726       1.1       cgd 	}
    727      1.33  christos 	return errs;
    728       1.1       cgd }
    729       1.1       cgd 
    730       1.1       cgd /*
    731       1.1       cgd  * Unset a bunch of variable values.
    732       1.1       cgd  */
    733      1.33  christos PUBLIC int
    734      1.12       wiz unset(void *v)
    735       1.1       cgd {
    736       1.5  christos 	char **arglist = v;
    737       1.9     lukem 	struct var *vp, *vp2;
    738       1.1       cgd 	int errs, h;
    739       1.1       cgd 	char **ap;
    740       1.1       cgd 
    741       1.1       cgd 	errs = 0;
    742      1.14       wiz 	for (ap = arglist; *ap != NULL; ap++) {
    743      1.15       wiz 		if ((vp2 = lookup(*ap)) == NULL) {
    744      1.11      dean 			if (getenv(*ap)) {
    745      1.27  christos 				(void)unsetenv(*ap);
    746      1.11      dean 			} else if (!sourcing) {
    747      1.27  christos 				(void)printf("\"%s\": undefined variable\n", *ap);
    748       1.1       cgd 				errs++;
    749       1.1       cgd 			}
    750       1.1       cgd 			continue;
    751       1.1       cgd 		}
    752       1.1       cgd 		h = hash(*ap);
    753       1.1       cgd 		if (vp2 == variables[h]) {
    754       1.1       cgd 			variables[h] = variables[h]->v_link;
    755      1.10  wsanchez 			v_free(vp2->v_name);
    756      1.10  wsanchez                         v_free(vp2->v_value);
    757      1.27  christos 			free(vp2);
    758       1.1       cgd 			continue;
    759       1.1       cgd 		}
    760       1.1       cgd 		for (vp = variables[h]; vp->v_link != vp2; vp = vp->v_link)
    761      1.33  christos 			continue;
    762       1.1       cgd 		vp->v_link = vp2->v_link;
    763      1.10  wsanchez                 v_free(vp2->v_name);
    764      1.10  wsanchez                 v_free(vp2->v_value);
    765      1.27  christos 		free(vp2);
    766       1.1       cgd 	}
    767      1.33  christos 	return errs;
    768       1.1       cgd }
    769       1.1       cgd 
    770       1.1       cgd /*
    771      1.29  christos  * Show a variable value.
    772      1.29  christos  */
    773      1.33  christos PUBLIC int
    774      1.29  christos show(void *v)
    775      1.29  christos {
    776      1.33  christos 	const char **arglist = v;
    777      1.29  christos 	struct var *vp;
    778      1.33  christos 	const char **ap, **p;
    779      1.29  christos 	int h, s;
    780      1.29  christos 
    781      1.29  christos 	if (*arglist == NULL) {
    782      1.29  christos 		for (h = 0, s = 1; h < HSHSIZE; h++)
    783      1.29  christos 			for (vp = variables[h]; vp != NULL; vp = vp->v_link)
    784      1.29  christos 				s++;
    785      1.37  christos 		ap = salloc(s * sizeof(*ap));
    786      1.29  christos 		for (h = 0, p = ap; h < HSHSIZE; h++)
    787      1.29  christos 			for (vp = variables[h]; vp != NULL; vp = vp->v_link)
    788      1.29  christos 				*p++ = vp->v_name;
    789      1.29  christos 		*p = NULL;
    790      1.29  christos 		sort(ap);
    791      1.29  christos 		for (p = ap; *p != NULL; p++)
    792      1.29  christos 			(void)printf("%s=%s\n", *p, value(*p));
    793      1.33  christos 		return 0;
    794      1.29  christos 	}
    795      1.29  christos 
    796      1.29  christos 	for (ap = arglist; *ap != NULL; ap++) {
    797      1.29  christos 		char *val = value(*ap);
    798      1.29  christos 		(void)printf("%s=%s\n", *ap, val ? val : "<null>");
    799      1.29  christos 	}
    800      1.29  christos 	return 0;
    801      1.29  christos }
    802      1.29  christos 
    803      1.32  christos 
    804      1.29  christos /*
    805       1.1       cgd  * Put add users to a group.
    806       1.1       cgd  */
    807      1.33  christos PUBLIC int
    808      1.12       wiz group(void *v)
    809       1.1       cgd {
    810      1.33  christos 	const char **argv = v;
    811       1.9     lukem 	struct grouphead *gh;
    812       1.9     lukem 	struct group *gp;
    813       1.9     lukem 	int h;
    814       1.1       cgd 	int s;
    815      1.33  christos 	const char *gname;
    816      1.33  christos 	const char **ap, **p;
    817       1.1       cgd 
    818      1.14       wiz 	if (*argv == NULL) {
    819       1.1       cgd 		for (h = 0, s = 1; h < HSHSIZE; h++)
    820      1.15       wiz 			for (gh = groups[h]; gh != NULL; gh = gh->g_link)
    821       1.1       cgd 				s++;
    822      1.37  christos 		ap = salloc(s * sizeof(*ap));
    823       1.1       cgd 		for (h = 0, p = ap; h < HSHSIZE; h++)
    824      1.15       wiz 			for (gh = groups[h]; gh != NULL; gh = gh->g_link)
    825       1.1       cgd 				*p++ = gh->g_name;
    826      1.14       wiz 		*p = NULL;
    827       1.1       cgd 		sort(ap);
    828      1.14       wiz 		for (p = ap; *p != NULL; p++)
    829       1.1       cgd 			printgroup(*p);
    830      1.33  christos 		return 0;
    831       1.1       cgd 	}
    832      1.14       wiz 	if (argv[1] == NULL) {
    833       1.1       cgd 		printgroup(*argv);
    834      1.33  christos 		return 0;
    835       1.1       cgd 	}
    836       1.1       cgd 	gname = *argv;
    837       1.1       cgd 	h = hash(gname);
    838      1.15       wiz 	if ((gh = findgroup(gname)) == NULL) {
    839      1.37  christos 		gh = ecalloc(1, sizeof(*gh));
    840       1.1       cgd 		gh->g_name = vcopy(gname);
    841      1.15       wiz 		gh->g_list = NULL;
    842       1.1       cgd 		gh->g_link = groups[h];
    843       1.1       cgd 		groups[h] = gh;
    844       1.1       cgd 	}
    845       1.1       cgd 
    846       1.1       cgd 	/*
    847       1.1       cgd 	 * Insert names from the command list into the group.
    848       1.1       cgd 	 * Who cares if there are duplicates?  They get tossed
    849       1.1       cgd 	 * later anyway.
    850       1.1       cgd 	 */
    851       1.1       cgd 
    852      1.31  christos 	for (ap = argv + 1; *ap != NULL; ap++) {
    853      1.37  christos 		gp = ecalloc(1, sizeof(*gp));
    854       1.1       cgd 		gp->ge_name = vcopy(*ap);
    855       1.1       cgd 		gp->ge_link = gh->g_list;
    856       1.1       cgd 		gh->g_list = gp;
    857       1.1       cgd 	}
    858      1.28  christos 	return 0;
    859      1.28  christos }
    860      1.28  christos 
    861      1.28  christos /*
    862      1.28  christos  * Delete the named group alias. Return zero if the group was
    863      1.28  christos  * successfully deleted, or -1 if there was no such group.
    864      1.28  christos  */
    865      1.28  christos static int
    866      1.28  christos delgroup(const char *name)
    867      1.28  christos {
    868      1.28  christos 	struct grouphead *gh, *p;
    869      1.28  christos 	struct group *g;
    870      1.28  christos 	int h;
    871      1.28  christos 
    872      1.28  christos 	h = hash(name);
    873      1.28  christos 	for (gh = groups[h], p = NULL; gh != NULL; p = gh, gh = gh->g_link)
    874      1.28  christos 		if (strcmp(gh->g_name, name) == 0) {
    875      1.28  christos 			if (p == NULL)
    876      1.28  christos 				groups[h] = gh->g_link;
    877      1.28  christos 			else
    878      1.28  christos 				p->g_link = gh->g_link;
    879      1.28  christos 			while (gh->g_list != NULL) {
    880      1.28  christos 				g = gh->g_list;
    881      1.28  christos 				gh->g_list = g->ge_link;
    882      1.28  christos 				free(g->ge_name);
    883      1.28  christos 				free(g);
    884      1.28  christos 			}
    885      1.28  christos 			free(gh->g_name);
    886      1.28  christos 			free(gh);
    887      1.28  christos 			return 0;
    888      1.28  christos 		}
    889      1.28  christos 	return -1;
    890      1.28  christos }
    891      1.28  christos 
    892      1.28  christos /*
    893      1.33  christos  * The unalias command takes a list of alises
    894      1.33  christos  * and discards the remembered groups of users.
    895       1.1       cgd  */
    896      1.33  christos PUBLIC int
    897      1.33  christos unalias(void *v)
    898       1.1       cgd {
    899       1.9     lukem 	char **ap;
    900       1.1       cgd 
    901      1.33  christos 	for (ap = v; *ap != NULL; ap++)
    902      1.33  christos 		(void)delgroup(*ap);
    903      1.33  christos 	return 0;
    904       1.1       cgd }
    905       1.1       cgd 
    906       1.1       cgd /*
    907       1.1       cgd  * The do nothing command for comments.
    908       1.1       cgd  */
    909       1.1       cgd /*ARGSUSED*/
    910      1.33  christos PUBLIC int
    911      1.31  christos null(void *v __unused)
    912       1.1       cgd {
    913       1.1       cgd 	return 0;
    914       1.1       cgd }
    915       1.1       cgd 
    916       1.1       cgd /*
    917       1.1       cgd  * Change to another file.  With no argument, print information about
    918       1.1       cgd  * the current file.
    919       1.1       cgd  */
    920      1.33  christos PUBLIC int
    921      1.12       wiz file(void *v)
    922       1.1       cgd {
    923       1.5  christos 	char **argv = v;
    924       1.1       cgd 
    925      1.14       wiz 	if (argv[0] == NULL) {
    926      1.27  christos 		(void)newfileinfo(0);
    927       1.1       cgd 		return 0;
    928       1.1       cgd 	}
    929       1.1       cgd 	if (setfile(*argv) < 0)
    930       1.1       cgd 		return 1;
    931       1.1       cgd 	announce();
    932      1.33  christos 
    933       1.1       cgd 	return 0;
    934       1.1       cgd }
    935       1.1       cgd 
    936       1.1       cgd /*
    937       1.1       cgd  * Expand file names like echo
    938       1.1       cgd  */
    939      1.33  christos PUBLIC int
    940      1.12       wiz echo(void *v)
    941       1.1       cgd {
    942       1.5  christos 	char **argv = v;
    943       1.9     lukem 	char **ap;
    944      1.26  christos 	const char *cp;
    945       1.1       cgd 
    946      1.14       wiz 	for (ap = argv; *ap != NULL; ap++) {
    947       1.1       cgd 		cp = *ap;
    948      1.14       wiz 		if ((cp = expand(cp)) != NULL) {
    949       1.1       cgd 			if (ap != argv)
    950      1.27  christos 				(void)putchar(' ');
    951      1.27  christos 			(void)printf("%s", cp);
    952       1.1       cgd 		}
    953       1.1       cgd 	}
    954      1.27  christos 	(void)putchar('\n');
    955       1.1       cgd 	return 0;
    956       1.1       cgd }
    957       1.1       cgd 
    958      1.33  christos /*
    959      1.33  christos  * Routines to push and pop the condition code to support nested
    960      1.33  christos  * if/else/endif statements.
    961      1.33  christos  */
    962      1.33  christos static void
    963      1.33  christos push_cond(int c_cond)
    964       1.1       cgd {
    965      1.33  christos 	struct cond_stack_s *csp;
    966      1.33  christos 	csp = emalloc(sizeof(*csp));
    967      1.33  christos 	csp->c_cond = c_cond;
    968      1.33  christos 	csp->c_next = cond_stack;
    969      1.33  christos 	cond_stack = csp;
    970       1.1       cgd }
    971       1.1       cgd 
    972      1.33  christos static int
    973      1.33  christos pop_cond(void)
    974       1.1       cgd {
    975      1.33  christos 	int c_cond;
    976      1.33  christos 	struct cond_stack_s *csp;
    977      1.33  christos 
    978      1.33  christos 	if ((csp = cond_stack) == NULL)
    979      1.33  christos 		return -1;
    980       1.1       cgd 
    981      1.33  christos 	c_cond = csp->c_cond;
    982      1.33  christos 	cond_stack = csp->c_next;
    983      1.33  christos 	free(csp);
    984      1.35  christos 	return c_cond;
    985       1.1       cgd }
    986       1.1       cgd 
    987       1.1       cgd /*
    988       1.1       cgd  * Conditional commands.  These allow one to parameterize one's
    989       1.1       cgd  * .mailrc and do some things if sending, others if receiving.
    990       1.1       cgd  */
    991      1.33  christos static int
    992      1.33  christos if_push(void)
    993      1.33  christos {
    994      1.33  christos 	push_cond(cond);
    995      1.33  christos 	cond &= ~CELSE;
    996      1.33  christos 	if ((cond & (CIF | CSKIP)) == (CIF | CSKIP)) {
    997      1.33  christos 		cond |= CIGN;
    998      1.33  christos 		return 1;
    999      1.33  christos 	}
   1000      1.33  christos 	return 0;
   1001      1.33  christos }
   1002      1.33  christos 
   1003      1.33  christos PUBLIC int
   1004      1.12       wiz ifcmd(void *v)
   1005       1.1       cgd {
   1006       1.5  christos 	char **argv = v;
   1007      1.33  christos 	char *keyword = argv[0];
   1008      1.33  christos 	static const struct modetbl_s {
   1009      1.33  christos 		const char *m_name;
   1010      1.33  christos 		enum mailmode_e m_mode;
   1011      1.33  christos 	} modetbl[] = {
   1012      1.33  christos 		{ "receiving",		mm_receiving },
   1013      1.33  christos 		{ "sending",		mm_sending },
   1014      1.33  christos 		{ "headersonly",	mm_hdrsonly },
   1015      1.33  christos 		{ NULL,			0 },
   1016      1.33  christos 	};
   1017      1.33  christos 	const struct modetbl_s *mtp;
   1018      1.33  christos 
   1019      1.33  christos 	if (if_push())
   1020      1.33  christos 		return 0;
   1021       1.1       cgd 
   1022      1.33  christos 	cond = CIF;
   1023      1.33  christos 	for (mtp = modetbl; mtp->m_name; mtp++)
   1024      1.33  christos 		if (strcasecmp(keyword, mtp->m_name) == 0)
   1025      1.33  christos 			break;
   1026      1.33  christos 
   1027      1.33  christos 	if (mtp->m_name == NULL) {
   1028      1.33  christos 		cond = CNONE;
   1029      1.33  christos 		(void)printf("Unrecognized if-keyword: \"%s\"\n", keyword);
   1030      1.33  christos 		return 1;
   1031       1.1       cgd 	}
   1032      1.33  christos 	if (mtp->m_mode != mailmode)
   1033      1.33  christos 		cond |= CSKIP;
   1034      1.33  christos 
   1035      1.33  christos 	return 0;
   1036      1.33  christos }
   1037      1.33  christos 
   1038      1.33  christos PUBLIC int
   1039      1.33  christos ifdefcmd(void *v)
   1040      1.33  christos {
   1041      1.33  christos 	char **argv = v;
   1042      1.33  christos 
   1043      1.33  christos 	if (if_push())
   1044      1.33  christos 		return 0;
   1045      1.33  christos 
   1046      1.33  christos 	cond = CIF;
   1047      1.33  christos 	if (value(argv[0]) == NULL)
   1048      1.33  christos 		cond |= CSKIP;
   1049      1.33  christos 
   1050      1.33  christos 	return 0;
   1051      1.33  christos }
   1052      1.33  christos 
   1053      1.33  christos PUBLIC int
   1054      1.33  christos ifndefcmd(void *v)
   1055      1.33  christos {
   1056      1.33  christos 	int rval;
   1057      1.33  christos 	rval = ifdefcmd(v);
   1058      1.33  christos 	cond ^= CSKIP;
   1059      1.33  christos 	return rval;
   1060       1.1       cgd }
   1061       1.1       cgd 
   1062       1.1       cgd /*
   1063       1.1       cgd  * Implement 'else'.  This is pretty simple -- we just
   1064       1.1       cgd  * flip over the conditional flag.
   1065       1.1       cgd  */
   1066      1.27  christos /*ARGSUSED*/
   1067      1.33  christos PUBLIC int
   1068      1.31  christos elsecmd(void *v __unused)
   1069       1.1       cgd {
   1070      1.33  christos 	if (cond_stack == NULL || (cond & (CIF | CELSE)) != CIF) {
   1071      1.33  christos 		(void)printf("\"else\" without matching \"if\"\n");
   1072      1.33  christos 		cond = CNONE;
   1073      1.33  christos 		return 1;
   1074      1.33  christos 	}
   1075      1.33  christos 	if ((cond & CIGN) == 0) {
   1076      1.33  christos 		cond ^= CSKIP;
   1077      1.33  christos 		cond |= CELSE;
   1078       1.1       cgd 	}
   1079      1.33  christos 	return 0;
   1080       1.1       cgd }
   1081       1.1       cgd 
   1082       1.1       cgd /*
   1083       1.1       cgd  * End of if statement.  Just set cond back to anything.
   1084       1.1       cgd  */
   1085      1.27  christos /*ARGSUSED*/
   1086      1.33  christos PUBLIC int
   1087      1.31  christos endifcmd(void *v __unused)
   1088       1.1       cgd {
   1089      1.33  christos 	if (cond_stack == NULL || (cond & CIF) != CIF) {
   1090      1.33  christos 		(void)printf("\"endif\" without matching \"if\"\n");
   1091      1.33  christos 		cond = CNONE;
   1092      1.33  christos 		return 1;
   1093       1.1       cgd 	}
   1094      1.33  christos 	cond = pop_cond();
   1095      1.33  christos 	return 0;
   1096       1.1       cgd }
   1097       1.1       cgd 
   1098       1.1       cgd /*
   1099       1.1       cgd  * Set the list of alternate names.
   1100       1.1       cgd  */
   1101      1.33  christos PUBLIC int
   1102      1.12       wiz alternates(void *v)
   1103       1.1       cgd {
   1104       1.5  christos 	char **namelist = v;
   1105      1.36  christos 	size_t c;
   1106       1.9     lukem 	char **ap, **ap2, *cp;
   1107       1.1       cgd 
   1108       1.1       cgd 	c = argcount(namelist) + 1;
   1109       1.1       cgd 	if (c == 1) {
   1110       1.1       cgd 		if (altnames == 0)
   1111      1.33  christos 			return 0;
   1112       1.1       cgd 		for (ap = altnames; *ap; ap++)
   1113      1.27  christos 			(void)printf("%s ", *ap);
   1114      1.27  christos 		(void)printf("\n");
   1115      1.33  christos 		return 0;
   1116       1.1       cgd 	}
   1117       1.1       cgd 	if (altnames != 0)
   1118      1.27  christos 		free(altnames);
   1119      1.36  christos 	altnames = ecalloc(c, sizeof(char *));
   1120       1.1       cgd 	for (ap = namelist, ap2 = altnames; *ap; ap++, ap2++) {
   1121      1.36  christos 		cp = ecalloc(strlen(*ap) + 1, sizeof(char));
   1122      1.27  christos 		(void)strcpy(cp, *ap);
   1123       1.1       cgd 		*ap2 = cp;
   1124       1.1       cgd 	}
   1125       1.1       cgd 	*ap2 = 0;
   1126      1.33  christos 	return 0;
   1127       1.1       cgd }
   1128