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