Home | History | Annotate | Line # | Download | only in mail
cmd3.c revision 1.32
      1 /*	$NetBSD: cmd3.c,v 1.32 2006/10/31 20:07:32 christos Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1980, 1993
      5  *	The Regents of the University of California.  All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  * 3. Neither the name of the University nor the names of its contributors
     16  *    may be used to endorse or promote products derived from this software
     17  *    without specific prior written permission.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     29  * SUCH DAMAGE.
     30  */
     31 
     32 #include <sys/cdefs.h>
     33 #ifndef lint
     34 #if 0
     35 static char sccsid[] = "@(#)cmd3.c	8.2 (Berkeley) 4/20/95";
     36 #else
     37 __RCSID("$NetBSD: cmd3.c,v 1.32 2006/10/31 20:07:32 christos Exp $");
     38 #endif
     39 #endif /* not lint */
     40 
     41 #include "rcv.h"
     42 #include <util.h>
     43 #include "extern.h"
     44 #include "mime.h"
     45 
     46 /*
     47  * Mail -- a mail program
     48  *
     49  * Still more user commands.
     50  */
     51 static int delgroup(const char *);
     52 static int diction(const void *, const void *);
     53 
     54 /*
     55  * Process a shell escape by saving signals, ignoring signals,
     56  * and forking a sh -c
     57  */
     58 int
     59 shell(void *v)
     60 {
     61 	char *str = v;
     62 	sig_t sigint = signal(SIGINT, SIG_IGN);
     63 	const char *shellcmd;
     64 	char cmd[BUFSIZ];
     65 
     66 	(void)strcpy(cmd, str);
     67 	if (bangexp(cmd) < 0)
     68 		return 1;
     69 	if ((shellcmd = value("SHELL")) == NULL)
     70 		shellcmd = _PATH_CSHELL;
     71 	(void)run_command(shellcmd, 0, 0, 1, "-c", cmd, NULL);
     72 	(void)signal(SIGINT, sigint);
     73 	(void)printf("!\n");
     74 	return 0;
     75 }
     76 
     77 /*
     78  * Fork an interactive shell.
     79  */
     80 /*ARGSUSED*/
     81 int
     82 dosh(void *v __unused)
     83 {
     84 	sig_t sigint = signal(SIGINT, SIG_IGN);
     85 	const char *shellcmd;
     86 
     87 	if ((shellcmd = value("SHELL")) == NULL)
     88 		shellcmd = _PATH_CSHELL;
     89 	(void)run_command(shellcmd, 0, 0, 1, NULL);
     90 	(void)signal(SIGINT, sigint);
     91 	(void)putchar('\n');
     92 	return 0;
     93 }
     94 
     95 /*
     96  * Expand the shell escape by expanding unescaped !'s into the
     97  * last issued command where possible.
     98  */
     99 
    100 char	lastbang[128];
    101 
    102 int
    103 bangexp(char *str)
    104 {
    105 	char bangbuf[BUFSIZ];
    106 	char *cp, *cp2;
    107 	int n;
    108 	int changed = 0;
    109 
    110 	cp = str;
    111 	cp2 = bangbuf;
    112 	n = BUFSIZ;
    113 	while (*cp) {
    114 		if (*cp == '!') {
    115 			if (n < strlen(lastbang)) {
    116 overf:
    117 				(void)printf("Command buffer overflow\n");
    118 				return(-1);
    119 			}
    120 			changed++;
    121 			(void)strcpy(cp2, lastbang);
    122 			cp2 += strlen(lastbang);
    123 			n -= strlen(lastbang);
    124 			cp++;
    125 			continue;
    126 		}
    127 		if (*cp == '\\' && cp[1] == '!') {
    128 			if (--n <= 1)
    129 				goto overf;
    130 			*cp2++ = '!';
    131 			cp += 2;
    132 			changed++;
    133 		}
    134 		if (--n <= 1)
    135 			goto overf;
    136 		*cp2++ = *cp++;
    137 	}
    138 	*cp2 = 0;
    139 	if (changed) {
    140 		(void)printf("!%s\n", bangbuf);
    141 		(void)fflush(stdout);
    142 	}
    143 	(void)strcpy(str, bangbuf);
    144 	(void)strncpy(lastbang, bangbuf, 128);
    145 	lastbang[127] = 0;
    146 	return(0);
    147 }
    148 
    149 /*
    150  * Print out a nice help message from some file or another.
    151  */
    152 
    153 int
    154 /*ARGSUSED*/
    155 help(void *v __unused)
    156 {
    157 	int c;
    158 	FILE *f;
    159 
    160 	if ((f = Fopen(_PATH_HELP, "r")) == NULL) {
    161 		warn(_PATH_HELP);
    162 		return(1);
    163 	}
    164 	while ((c = getc(f)) != EOF)
    165 		(void)putchar(c);
    166 	(void)Fclose(f);
    167 	return(0);
    168 }
    169 
    170 /*
    171  * Change user's working directory.
    172  */
    173 int
    174 schdir(void *v)
    175 {
    176 	char **arglist = v;
    177 	const char *cp;
    178 
    179 	if (*arglist == NULL)
    180 		cp = homedir;
    181 	else
    182 		if ((cp = expand(*arglist)) == NULL)
    183 			return(1);
    184 	if (chdir(cp) < 0) {
    185 		warn("%s", cp);
    186 		return(1);
    187 	}
    188 	return 0;
    189 }
    190 
    191 int
    192 respond(void *v)
    193 {
    194 	int *msgvec = v;
    195 	if (value("Replyall") == NULL)
    196 		return (_respond(msgvec));
    197 	else
    198 		return (_Respond(msgvec));
    199 }
    200 
    201 static struct name *
    202 set_smopts(struct message *mp)
    203 {
    204 	char *cp;
    205 	struct name *np = NULL;
    206 	char *reply_as_recipient = value("ReplyAsRecipient");
    207 
    208 	if (reply_as_recipient &&
    209 	    (cp = skin(hfield("to", mp))) != NULL &&
    210 	    extract(cp, GTO)->n_flink == NULL) {  /* check for one recipient */
    211 		char *p, *q;
    212 		size_t len = strlen(cp);
    213 		/*
    214 		 * XXX - perhaps we always want to ignore
    215 		 *       "undisclosed-recipients:;" ?
    216 		 */
    217 		for (p = q = reply_as_recipient; *p; p = q) {
    218 			while (*q != '\0' && *q != ',' && !isblank((unsigned char)*q))
    219 				q++;
    220 			if (p + len == q && strncasecmp(cp, p, len) == 0)
    221 				return np;
    222 			while (*q == ',' || isblank((unsigned char)*q))
    223 				q++;
    224 		}
    225 		np = extract(__UNCONST("-f"), GSMOPTS);
    226 		np = cat(np, extract(cp, GSMOPTS));
    227 	}
    228 
    229 	return np;
    230 }
    231 
    232 /*
    233  * Reply to a list of messages.  Extract each name from the
    234  * message header and send them off to mail1()
    235  */
    236 int
    237 _respond(int *msgvec)
    238 {
    239 	struct message *mp;
    240 	char *cp, *rcv, *replyto;
    241 	char **ap;
    242 	struct name *np;
    243 	struct header head;
    244 
    245 	if (msgvec[1] != 0) {
    246 		(void)printf("Sorry, can't reply to multiple messages at once\n");
    247 		return(1);
    248 	}
    249 	mp = &message[msgvec[0] - 1];
    250 	touch(mp);
    251 	dot = mp;
    252 	if ((rcv = skin(hfield("from", mp))) == NULL)
    253 		rcv = skin(nameof(mp, 1));
    254 	if ((replyto = skin(hfield("reply-to", mp))) != NULL)
    255 		np = extract(replyto, GTO);
    256 	else if ((cp = skin(hfield("to", mp))) != NULL)
    257 		np = extract(cp, GTO);
    258 	else
    259 		np = NULL;
    260 	np = elide(np);
    261 	/*
    262 	 * Delete my name from the reply list,
    263 	 * and with it, all my alternate names.
    264 	 */
    265 	np = delname(np, myname);
    266 	if (altnames)
    267 		for (ap = altnames; *ap; ap++)
    268 			np = delname(np, *ap);
    269 	if (np != NULL && replyto == NULL)
    270 		np = cat(np, extract(rcv, GTO));
    271 	else if (np == NULL) {
    272 		if (replyto != NULL)
    273 			(void)printf("Empty reply-to field -- replying to author\n");
    274 		np = extract(rcv, GTO);
    275 	}
    276 	head.h_to = np;
    277 	if ((head.h_subject = hfield("subject", mp)) == NULL)
    278 		head.h_subject = hfield("subj", mp);
    279 	head.h_subject = reedit(head.h_subject);
    280 	if (replyto == NULL && (cp = skin(hfield("cc", mp))) != NULL) {
    281 		np = elide(extract(cp, GCC));
    282 		np = delname(np, myname);
    283 		if (altnames != 0)
    284 			for (ap = altnames; *ap; ap++)
    285 				np = delname(np, *ap);
    286 		head.h_cc = np;
    287 	} else
    288 		head.h_cc = NULL;
    289 	head.h_bcc = NULL;
    290 	head.h_smopts = set_smopts(mp);
    291 #ifdef MIME_SUPPORT
    292 	head.h_attach = NULL;
    293 #endif
    294 	mail1(&head, 1);
    295 	return(0);
    296 }
    297 
    298 /*
    299  * Modify the subject we are replying to to begin with Re: if
    300  * it does not already.
    301  */
    302 char *
    303 reedit(char *subj)
    304 {
    305 	char *newsubj;
    306 
    307 	if (subj == NULL)
    308 		return NULL;
    309 	if ((subj[0] == 'r' || subj[0] == 'R') &&
    310 	    (subj[1] == 'e' || subj[1] == 'E') &&
    311 	    subj[2] == ':')
    312 		return subj;
    313 	newsubj = salloc(strlen(subj) + 5);
    314 	(void)strcpy(newsubj, "Re: ");
    315 	(void)strcpy(newsubj + 4, subj);
    316 	return newsubj;
    317 }
    318 
    319 /*
    320  * Preserve the named messages, so that they will be sent
    321  * back to the system mailbox.
    322  */
    323 int
    324 preserve(void *v)
    325 {
    326 	int *msgvec = v;
    327 	struct message *mp;
    328 	int *ip, mesg;
    329 
    330 	if (edit) {
    331 		(void)printf("Cannot \"preserve\" in edit mode\n");
    332 		return(1);
    333 	}
    334 	for (ip = msgvec; *ip != 0; ip++) {
    335 		mesg = *ip;
    336 		mp = &message[mesg - 1];
    337 		mp->m_flag |= MPRESERVE;
    338 		mp->m_flag &= ~MBOX;
    339 		dot = mp;
    340 	}
    341 	return(0);
    342 }
    343 
    344 /*
    345  * Mark all given messages as unread.
    346  */
    347 int
    348 unread(void *v)
    349 {
    350 	int *msgvec = v;
    351 	int *ip;
    352 
    353 	for (ip = msgvec; *ip != 0; ip++) {
    354 		dot = &message[*ip - 1];
    355 		dot->m_flag &= ~(MREAD|MTOUCH);
    356 		dot->m_flag |= MSTATUS;
    357 	}
    358 	return(0);
    359 }
    360 
    361 /*
    362  * Mark all given messages as read.
    363  */
    364 int
    365 markread(void *v)
    366 {
    367 	int *msgvec = v;
    368 	int *ip;
    369 
    370 	for (ip = msgvec; *ip != 0; ip++) {
    371 		dot = &message[*ip - 1];
    372 		dot->m_flag &= ~(MNEW|MTOUCH);
    373 		dot->m_flag |= MREAD|MSTATUS;
    374 	}
    375 	return(0);
    376 }
    377 
    378 /*
    379  * Print the size of each message.
    380  */
    381 int
    382 messize(void *v)
    383 {
    384 	int *msgvec = v;
    385 	struct message *mp;
    386 	int *ip, mesg;
    387 
    388 	for (ip = msgvec; *ip != 0; ip++) {
    389 		mesg = *ip;
    390 		mp = &message[mesg - 1];
    391 		(void)printf("%d: %ld/%llu\n", mesg, mp->m_blines,
    392 		    (unsigned long long)mp->m_size);
    393 	}
    394 	return(0);
    395 }
    396 
    397 /*
    398  * Quit quickly.  If we are sourcing, just pop the input level
    399  * by returning an error.
    400  */
    401 int
    402 /*ARGSUSED*/
    403 rexit(void *v __unused)
    404 {
    405 	if (sourcing)
    406 		return(1);
    407 	exit(0);
    408 	/*NOTREACHED*/
    409 }
    410 
    411 /*
    412  * Set or display a variable value.  Syntax is similar to that
    413  * of csh.
    414  */
    415 int
    416 set(void *v)
    417 {
    418 	char **arglist = v;
    419 	struct var *vp;
    420 	const char *cp;
    421 	char varbuf[BUFSIZ], **ap, **p;
    422 	int errs, h, s;
    423 	size_t l;
    424 
    425 	if (*arglist == NULL) {
    426 		for (h = 0, s = 1; h < HSHSIZE; h++)
    427 			for (vp = variables[h]; vp != NULL; vp = vp->v_link)
    428 				s++;
    429 		ap = salloc(s * sizeof *ap);
    430 		for (h = 0, p = ap; h < HSHSIZE; h++)
    431 			for (vp = variables[h]; vp != NULL; vp = vp->v_link)
    432 				*p++ = vp->v_name;
    433 		*p = NULL;
    434 		sort(ap);
    435 		for (p = ap; *p != NULL; p++)
    436 			(void)printf("%s\t%s\n", *p, value(*p));
    437 		return(0);
    438 	}
    439 	errs = 0;
    440 	for (ap = arglist; *ap != NULL; ap++) {
    441 		cp = *ap;
    442 		while (*cp != '=' && *cp != '\0')
    443 			++cp;
    444 		l = cp - *ap;
    445 		if (l >= sizeof varbuf)
    446 			l = sizeof varbuf - 1;
    447 		(void)strncpy(varbuf, *ap, l);
    448 		varbuf[l] = '\0';
    449 		if (*cp == '\0')
    450 			cp = "";
    451 		else
    452 			cp++;
    453 		if (equal(varbuf, "")) {
    454 			(void)printf("Non-null variable name required\n");
    455 			errs++;
    456 			continue;
    457 		}
    458 		assign(varbuf, cp);
    459 	}
    460 	return(errs);
    461 }
    462 
    463 /*
    464  * Unset a bunch of variable values.
    465  */
    466 int
    467 unset(void *v)
    468 {
    469 	char **arglist = v;
    470 	struct var *vp, *vp2;
    471 	int errs, h;
    472 	char **ap;
    473 
    474 	errs = 0;
    475 	for (ap = arglist; *ap != NULL; ap++) {
    476 		if ((vp2 = lookup(*ap)) == NULL) {
    477 			if (getenv(*ap)) {
    478 				(void)unsetenv(*ap);
    479 			} else if (!sourcing) {
    480 				(void)printf("\"%s\": undefined variable\n", *ap);
    481 				errs++;
    482 			}
    483 			continue;
    484 		}
    485 		h = hash(*ap);
    486 		if (vp2 == variables[h]) {
    487 			variables[h] = variables[h]->v_link;
    488 			v_free(vp2->v_name);
    489                         v_free(vp2->v_value);
    490 			free(vp2);
    491 			continue;
    492 		}
    493 		for (vp = variables[h]; vp->v_link != vp2; vp = vp->v_link)
    494 			;
    495 		vp->v_link = vp2->v_link;
    496                 v_free(vp2->v_name);
    497                 v_free(vp2->v_value);
    498 		free(vp2);
    499 	}
    500 	return(errs);
    501 }
    502 
    503 /*
    504  * Show a variable value.
    505  */
    506 int
    507 show(void *v)
    508 {
    509 	char **arglist = v;
    510 	struct var *vp;
    511 	char **ap, **p;
    512 	int h, s;
    513 
    514 	if (*arglist == NULL) {
    515 		for (h = 0, s = 1; h < HSHSIZE; h++)
    516 			for (vp = variables[h]; vp != NULL; vp = vp->v_link)
    517 				s++;
    518 		ap = salloc(s * sizeof *ap);
    519 		for (h = 0, p = ap; h < HSHSIZE; h++)
    520 			for (vp = variables[h]; vp != NULL; vp = vp->v_link)
    521 				*p++ = vp->v_name;
    522 		*p = NULL;
    523 		sort(ap);
    524 		for (p = ap; *p != NULL; p++)
    525 			(void)printf("%s=%s\n", *p, value(*p));
    526 		return(0);
    527 	}
    528 
    529 	for (ap = arglist; *ap != NULL; ap++) {
    530 		char *val = value(*ap);
    531 		(void)printf("%s=%s\n", *ap, val ? val : "<null>");
    532 	}
    533 	return 0;
    534 }
    535 
    536 
    537 /*
    538  * Put add users to a group.
    539  */
    540 int
    541 group(void *v)
    542 {
    543 	char **argv = v;
    544 	struct grouphead *gh;
    545 	struct group *gp;
    546 	int h;
    547 	int s;
    548 	char **ap, *gname, **p;
    549 
    550 	if (*argv == NULL) {
    551 		for (h = 0, s = 1; h < HSHSIZE; h++)
    552 			for (gh = groups[h]; gh != NULL; gh = gh->g_link)
    553 				s++;
    554 		ap = salloc(s * sizeof *ap);
    555 		for (h = 0, p = ap; h < HSHSIZE; h++)
    556 			for (gh = groups[h]; gh != NULL; gh = gh->g_link)
    557 				*p++ = gh->g_name;
    558 		*p = NULL;
    559 		sort(ap);
    560 		for (p = ap; *p != NULL; p++)
    561 			printgroup(*p);
    562 		return(0);
    563 	}
    564 	if (argv[1] == NULL) {
    565 		printgroup(*argv);
    566 		return(0);
    567 	}
    568 	gname = *argv;
    569 	h = hash(gname);
    570 	if ((gh = findgroup(gname)) == NULL) {
    571 		gh = (struct grouphead *) ecalloc(1, sizeof *gh);
    572 		gh->g_name = vcopy(gname);
    573 		gh->g_list = NULL;
    574 		gh->g_link = groups[h];
    575 		groups[h] = gh;
    576 	}
    577 
    578 	/*
    579 	 * Insert names from the command list into the group.
    580 	 * Who cares if there are duplicates?  They get tossed
    581 	 * later anyway.
    582 	 */
    583 
    584 	for (ap = argv + 1; *ap != NULL; ap++) {
    585 		gp = (struct group *) ecalloc(1, sizeof *gp);
    586 		gp->ge_name = vcopy(*ap);
    587 		gp->ge_link = gh->g_list;
    588 		gh->g_list = gp;
    589 	}
    590 	return(0);
    591 }
    592 
    593 /*
    594  * The unalias command takes a list of alises
    595  * and discards the remembered groups of users.
    596  */
    597 int
    598 unalias(void *v)
    599 {
    600 	char **ap;
    601 
    602 	for (ap = v; *ap != NULL; ap++)
    603 		(void)delgroup(*ap);
    604 	return 0;
    605 }
    606 
    607 /*
    608  * Delete the named group alias. Return zero if the group was
    609  * successfully deleted, or -1 if there was no such group.
    610  */
    611 static int
    612 delgroup(const char *name)
    613 {
    614 	struct grouphead *gh, *p;
    615 	struct group *g;
    616 	int h;
    617 
    618 	h = hash(name);
    619 	for (gh = groups[h], p = NULL; gh != NULL; p = gh, gh = gh->g_link)
    620 		if (strcmp(gh->g_name, name) == 0) {
    621 			if (p == NULL)
    622 				groups[h] = gh->g_link;
    623 			else
    624 				p->g_link = gh->g_link;
    625 			while (gh->g_list != NULL) {
    626 				g = gh->g_list;
    627 				gh->g_list = g->ge_link;
    628 				free(g->ge_name);
    629 				free(g);
    630 			}
    631 			free(gh->g_name);
    632 			free(gh);
    633 			return 0;
    634 		}
    635 	return -1;
    636 }
    637 
    638 /*
    639  * Sort the passed string vecotor into ascending dictionary
    640  * order.
    641  */
    642 void
    643 sort(char **list)
    644 {
    645 	char **ap;
    646 
    647 	for (ap = list; *ap != NULL; ap++)
    648 		;
    649 	if (ap-list < 2)
    650 		return;
    651 	qsort(list, (size_t)(ap-list), sizeof(*list), diction);
    652 }
    653 
    654 /*
    655  * Do a dictionary order comparison of the arguments from
    656  * qsort.
    657  */
    658 static int
    659 diction(const void *a, const void *b)
    660 {
    661 	return(strcmp(*(const char *const *)a, *(const char *const *)b));
    662 }
    663 
    664 /*
    665  * The do nothing command for comments.
    666  */
    667 
    668 /*ARGSUSED*/
    669 int
    670 null(void *v __unused)
    671 {
    672 	return 0;
    673 }
    674 
    675 /*
    676  * Change to another file.  With no argument, print information about
    677  * the current file.
    678  */
    679 int
    680 file(void *v)
    681 {
    682 	char **argv = v;
    683 
    684 	if (argv[0] == NULL) {
    685 		(void)newfileinfo(0);
    686 		return 0;
    687 	}
    688 	if (setfile(*argv) < 0)
    689 		return 1;
    690 	announce();
    691 	return 0;
    692 }
    693 
    694 /*
    695  * Expand file names like echo
    696  */
    697 int
    698 echo(void *v)
    699 {
    700 	char **argv = v;
    701 	char **ap;
    702 	const char *cp;
    703 
    704 	for (ap = argv; *ap != NULL; ap++) {
    705 		cp = *ap;
    706 		if ((cp = expand(cp)) != NULL) {
    707 			if (ap != argv)
    708 				(void)putchar(' ');
    709 			(void)printf("%s", cp);
    710 		}
    711 	}
    712 	(void)putchar('\n');
    713 	return 0;
    714 }
    715 
    716 int
    717 Respond(void *v)
    718 {
    719 	int *msgvec = v;
    720 	if (value("Replyall") == NULL)
    721 		return (_Respond(msgvec));
    722 	else
    723 		return (_respond(msgvec));
    724 }
    725 
    726 /*
    727  * Reply to a series of messages by simply mailing to the senders
    728  * and not messing around with the To: and Cc: lists as in normal
    729  * reply.
    730  */
    731 int
    732 _Respond(int msgvec[])
    733 {
    734 	struct header head;
    735 	struct message *mp;
    736 	int *ap;
    737 	char *cp;
    738 
    739 	head.h_to = NULL;
    740 	for (ap = msgvec; *ap != 0; ap++) {
    741 		mp = &message[*ap - 1];
    742 		touch(mp);
    743 		dot = mp;
    744 		if ((cp = skin(hfield("from", mp))) == NULL)
    745 			cp = skin(nameof(mp, 2));
    746 		head.h_to = cat(head.h_to, extract(cp, GTO));
    747 	}
    748 	if (head.h_to == NULL)
    749 		return 0;
    750 	mp = &message[msgvec[0] - 1];
    751 	if ((head.h_subject = hfield("subject", mp)) == NULL)
    752 		head.h_subject = hfield("subj", mp);
    753 	head.h_subject = reedit(head.h_subject);
    754 	head.h_cc = NULL;
    755 	head.h_bcc = NULL;
    756 	head.h_smopts = set_smopts(mp);
    757 #ifdef MIME_SUPPORT
    758 	head.h_attach = NULL;
    759 #endif
    760 	mail1(&head, 1);
    761 	return 0;
    762 }
    763 
    764 /*
    765  * Conditional commands.  These allow one to parameterize one's
    766  * .mailrc and do some things if sending, others if receiving.
    767  */
    768 int
    769 ifcmd(void *v)
    770 {
    771 	char **argv = v;
    772 	char *cp;
    773 
    774 	if (cond != CANY) {
    775 		(void)printf("Illegal nested \"if\"\n");
    776 		return(1);
    777 	}
    778 	cond = CANY;
    779 	cp = argv[0];
    780 	switch (*cp) {
    781 	case 'r': case 'R':
    782 		cond = CRCV;
    783 		break;
    784 
    785 	case 's': case 'S':
    786 		cond = CSEND;
    787 		break;
    788 
    789 	default:
    790 		(void)printf("Unrecognized if-keyword: \"%s\"\n", cp);
    791 		return(1);
    792 	}
    793 	return(0);
    794 }
    795 
    796 /*
    797  * Implement 'else'.  This is pretty simple -- we just
    798  * flip over the conditional flag.
    799  */
    800 int
    801 /*ARGSUSED*/
    802 elsecmd(void *v __unused)
    803 {
    804 
    805 	switch (cond) {
    806 	case CANY:
    807 		(void)printf("\"Else\" without matching \"if\"\n");
    808 		return(1);
    809 
    810 	case CSEND:
    811 		cond = CRCV;
    812 		break;
    813 
    814 	case CRCV:
    815 		cond = CSEND;
    816 		break;
    817 
    818 	default:
    819 		(void)printf("Mail's idea of conditions is screwed up\n");
    820 		cond = CANY;
    821 		break;
    822 	}
    823 	return(0);
    824 }
    825 
    826 /*
    827  * End of if statement.  Just set cond back to anything.
    828  */
    829 int
    830 /*ARGSUSED*/
    831 endifcmd(void *v __unused)
    832 {
    833 
    834 	if (cond == CANY) {
    835 		(void)printf("\"Endif\" without matching \"if\"\n");
    836 		return(1);
    837 	}
    838 	cond = CANY;
    839 	return(0);
    840 }
    841 
    842 /*
    843  * Set the list of alternate names.
    844  */
    845 int
    846 alternates(void *v)
    847 {
    848 	char **namelist = v;
    849 	int c;
    850 	char **ap, **ap2, *cp;
    851 
    852 	c = argcount(namelist) + 1;
    853 	if (c == 1) {
    854 		if (altnames == 0)
    855 			return(0);
    856 		for (ap = altnames; *ap; ap++)
    857 			(void)printf("%s ", *ap);
    858 		(void)printf("\n");
    859 		return(0);
    860 	}
    861 	if (altnames != 0)
    862 		free(altnames);
    863 	altnames = (char **) ecalloc((unsigned) c, sizeof (char *));
    864 	for (ap = namelist, ap2 = altnames; *ap; ap++, ap2++) {
    865 		cp = ecalloc((unsigned) strlen(*ap) + 1, sizeof (char));
    866 		(void)strcpy(cp, *ap);
    867 		*ap2 = cp;
    868 	}
    869 	*ap2 = 0;
    870 	return(0);
    871 }
    872