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