cmd3.c revision 74769
11590Srgrimes/*
21590Srgrimes * Copyright (c) 1980, 1993
31590Srgrimes *	The Regents of the University of California.  All rights reserved.
41590Srgrimes *
51590Srgrimes * Redistribution and use in source and binary forms, with or without
61590Srgrimes * modification, are permitted provided that the following conditions
71590Srgrimes * are met:
81590Srgrimes * 1. Redistributions of source code must retain the above copyright
91590Srgrimes *    notice, this list of conditions and the following disclaimer.
101590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111590Srgrimes *    notice, this list of conditions and the following disclaimer in the
121590Srgrimes *    documentation and/or other materials provided with the distribution.
131590Srgrimes * 3. All advertising materials mentioning features or use of this software
141590Srgrimes *    must display the following acknowledgement:
151590Srgrimes *	This product includes software developed by the University of
161590Srgrimes *	California, Berkeley and its contributors.
171590Srgrimes * 4. Neither the name of the University nor the names of its contributors
181590Srgrimes *    may be used to endorse or promote products derived from this software
191590Srgrimes *    without specific prior written permission.
201590Srgrimes *
211590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
221590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
231590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
241590Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
251590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
261590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
271590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
281590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
291590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
301590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
311590Srgrimes * SUCH DAMAGE.
321590Srgrimes */
331590Srgrimes
341590Srgrimes#ifndef lint
3574769Smikeh#if 0
361590Srgrimesstatic char sccsid[] = "@(#)cmd3.c	8.1 (Berkeley) 6/6/93";
3774769Smikeh#endif
3874769Smikehstatic const char rcsid[] =
3974769Smikeh  "$FreeBSD: head/usr.bin/mail/cmd3.c 74769 2001-03-25 04:57:05Z mikeh $";
401590Srgrimes#endif /* not lint */
411590Srgrimes
421590Srgrimes#include "rcv.h"
431590Srgrimes#include "extern.h"
441590Srgrimes
451590Srgrimes/*
461590Srgrimes * Mail -- a mail program
471590Srgrimes *
481590Srgrimes * Still more user commands.
491590Srgrimes */
501590Srgrimes
511590Srgrimes/*
521590Srgrimes * Process a shell escape by saving signals, ignoring signals,
531590Srgrimes * and forking a sh -c
541590Srgrimes */
551590Srgrimesint
561590Srgrimesshell(str)
571590Srgrimes	char *str;
581590Srgrimes{
591590Srgrimes	sig_t sigint = signal(SIGINT, SIG_IGN);
601590Srgrimes	char *shell;
611590Srgrimes	char cmd[BUFSIZ];
621590Srgrimes
6374769Smikeh	if (strlcpy(cmd, str, sizeof(cmd)) >= sizeof(cmd))
641590Srgrimes		return 1;
6574769Smikeh	if (bangexp(cmd, sizeof(cmd)) < 0)
6674769Smikeh		return 1;
671590Srgrimes	if ((shell = value("SHELL")) == NOSTR)
681590Srgrimes		shell = _PATH_CSHELL;
691590Srgrimes	(void) run_command(shell, 0, -1, -1, "-c", cmd, NOSTR);
701590Srgrimes	(void) signal(SIGINT, sigint);
711590Srgrimes	printf("!\n");
721590Srgrimes	return 0;
731590Srgrimes}
741590Srgrimes
751590Srgrimes/*
761590Srgrimes * Fork an interactive shell.
771590Srgrimes */
781590Srgrimes/*ARGSUSED*/
791590Srgrimesint
801590Srgrimesdosh(str)
811590Srgrimes	char *str;
821590Srgrimes{
831590Srgrimes	sig_t sigint = signal(SIGINT, SIG_IGN);
841590Srgrimes	char *shell;
851590Srgrimes
861590Srgrimes	if ((shell = value("SHELL")) == NOSTR)
871590Srgrimes		shell = _PATH_CSHELL;
881590Srgrimes	(void) run_command(shell, 0, -1, -1, NOSTR, NOSTR, NOSTR);
891590Srgrimes	(void) signal(SIGINT, sigint);
901590Srgrimes	putchar('\n');
911590Srgrimes	return 0;
921590Srgrimes}
931590Srgrimes
941590Srgrimes/*
951590Srgrimes * Expand the shell escape by expanding unescaped !'s into the
961590Srgrimes * last issued command where possible.
971590Srgrimes */
981590Srgrimesint
9974769Smikehbangexp(str, strsize)
1001590Srgrimes	char *str;
10174769Smikeh	size_t strsize;
1021590Srgrimes{
1031590Srgrimes	char bangbuf[BUFSIZ];
10474769Smikeh	static char lastbang[BUFSIZ];
1051590Srgrimes	register char *cp, *cp2;
1061590Srgrimes	register int n;
1071590Srgrimes	int changed = 0;
1081590Srgrimes
1091590Srgrimes	cp = str;
1101590Srgrimes	cp2 = bangbuf;
11174769Smikeh	n = sizeof(bangbuf);
1121590Srgrimes	while (*cp) {
1131590Srgrimes		if (*cp == '!') {
1141590Srgrimes			if (n < strlen(lastbang)) {
1151590Srgrimesoverf:
1161590Srgrimes				printf("Command buffer overflow\n");
1171590Srgrimes				return(-1);
1181590Srgrimes			}
1191590Srgrimes			changed++;
12074769Smikeh			if (strlcpy(cp2, lastbang, sizeof(bangbuf) - (cp2 - bangbuf))
12174769Smikeh			    >= sizeof(bangbuf) - (cp2 - bangbuf))
12274769Smikeh				goto overf;
1231590Srgrimes			cp2 += strlen(lastbang);
1241590Srgrimes			n -= strlen(lastbang);
1251590Srgrimes			cp++;
1261590Srgrimes			continue;
1271590Srgrimes		}
1281590Srgrimes		if (*cp == '\\' && cp[1] == '!') {
1291590Srgrimes			if (--n <= 1)
1301590Srgrimes				goto overf;
1311590Srgrimes			*cp2++ = '!';
1321590Srgrimes			cp += 2;
1331590Srgrimes			changed++;
1341590Srgrimes		}
1351590Srgrimes		if (--n <= 1)
1361590Srgrimes			goto overf;
1371590Srgrimes		*cp2++ = *cp++;
1381590Srgrimes	}
1391590Srgrimes	*cp2 = 0;
1401590Srgrimes	if (changed) {
1411590Srgrimes		printf("!%s\n", bangbuf);
1421590Srgrimes		fflush(stdout);
1431590Srgrimes	}
14474769Smikeh	if (strlcpy(str, bangbuf, strsize) >= strsize)
14574769Smikeh		goto overf;
14674769Smikeh	if (strlcpy(lastbang, bangbuf, sizeof(lastbang)) >= sizeof(lastbang))
14774769Smikeh		goto overf;
1481590Srgrimes	return(0);
1491590Srgrimes}
1501590Srgrimes
1511590Srgrimes/*
1521590Srgrimes * Print out a nice help message from some file or another.
1531590Srgrimes */
1541590Srgrimes
1551590Srgrimesint
1561590Srgrimeshelp()
1571590Srgrimes{
1581590Srgrimes	register c;
1591590Srgrimes	register FILE *f;
1601590Srgrimes
1611590Srgrimes	if ((f = Fopen(_PATH_HELP, "r")) == NULL) {
16274769Smikeh		warn("%s", _PATH_HELP);
1631590Srgrimes		return(1);
1641590Srgrimes	}
1651590Srgrimes	while ((c = getc(f)) != EOF)
1661590Srgrimes		putchar(c);
1671590Srgrimes	Fclose(f);
1681590Srgrimes	return(0);
1691590Srgrimes}
1701590Srgrimes
1711590Srgrimes/*
1721590Srgrimes * Change user's working directory.
1731590Srgrimes */
1741590Srgrimesint
1751590Srgrimesschdir(arglist)
1761590Srgrimes	char **arglist;
1771590Srgrimes{
1781590Srgrimes	char *cp;
1791590Srgrimes
18074769Smikeh	if (*arglist == NOSTR) {
18174769Smikeh		if (homedir == NOSTR)
18274769Smikeh			return(1);
1831590Srgrimes		cp = homedir;
18474769Smikeh	} else
1851590Srgrimes		if ((cp = expand(*arglist)) == NOSTR)
1861590Srgrimes			return(1);
1871590Srgrimes	if (chdir(cp) < 0) {
18874769Smikeh		warn("%s", cp);
1891590Srgrimes		return(1);
1901590Srgrimes	}
1911590Srgrimes	return 0;
1921590Srgrimes}
1931590Srgrimes
1941590Srgrimesint
1951590Srgrimesrespond(msgvec)
1961590Srgrimes	int *msgvec;
1971590Srgrimes{
1981590Srgrimes	if (value("Replyall") == NOSTR)
19932189Sjoerg		return (dorespond(msgvec));
2001590Srgrimes	else
20132189Sjoerg		return (doRespond(msgvec));
2021590Srgrimes}
2031590Srgrimes
2041590Srgrimes/*
2051590Srgrimes * Reply to a list of messages.  Extract each name from the
2061590Srgrimes * message header and send them off to mail1()
2071590Srgrimes */
2081590Srgrimesint
20932189Sjoergdorespond(msgvec)
2101590Srgrimes	int *msgvec;
2111590Srgrimes{
2121590Srgrimes	struct message *mp;
2131590Srgrimes	char *cp, *rcv, *replyto;
2141590Srgrimes	char **ap;
2151590Srgrimes	struct name *np;
2161590Srgrimes	struct header head;
2171590Srgrimes
2181590Srgrimes	if (msgvec[1] != 0) {
2191590Srgrimes		printf("Sorry, can't reply to multiple messages at once\n");
2201590Srgrimes		return(1);
2211590Srgrimes	}
2221590Srgrimes	mp = &message[msgvec[0] - 1];
2231590Srgrimes	touch(mp);
2241590Srgrimes	dot = mp;
2251590Srgrimes	if ((rcv = skin(hfield("from", mp))) == NOSTR)
2261590Srgrimes		rcv = skin(nameof(mp, 1));
2271590Srgrimes	if ((replyto = skin(hfield("reply-to", mp))) != NOSTR)
2281590Srgrimes		np = extract(replyto, GTO);
2291590Srgrimes	else if ((cp = skin(hfield("to", mp))) != NOSTR)
2301590Srgrimes		np = extract(cp, GTO);
2311590Srgrimes	else
2321590Srgrimes		np = NIL;
2331590Srgrimes	np = elide(np);
2341590Srgrimes	/*
2351590Srgrimes	 * Delete my name from the reply list,
2361590Srgrimes	 * and with it, all my alternate names.
2371590Srgrimes	 */
2381590Srgrimes	np = delname(np, myname);
2391590Srgrimes	if (altnames)
2401590Srgrimes		for (ap = altnames; *ap; ap++)
2411590Srgrimes			np = delname(np, *ap);
2421590Srgrimes	if (np != NIL && replyto == NOSTR)
2431590Srgrimes		np = cat(np, extract(rcv, GTO));
2441590Srgrimes	else if (np == NIL) {
2451590Srgrimes		if (replyto != NOSTR)
2461590Srgrimes			printf("Empty reply-to field -- replying to author\n");
2471590Srgrimes		np = extract(rcv, GTO);
2481590Srgrimes	}
2491590Srgrimes	head.h_to = np;
2501590Srgrimes	if ((head.h_subject = hfield("subject", mp)) == NOSTR)
2511590Srgrimes		head.h_subject = hfield("subj", mp);
2521590Srgrimes	head.h_subject = reedit(head.h_subject);
2531590Srgrimes	if (replyto == NOSTR && (cp = skin(hfield("cc", mp))) != NOSTR) {
2541590Srgrimes		np = elide(extract(cp, GCC));
2551590Srgrimes		np = delname(np, myname);
2561590Srgrimes		if (altnames != 0)
2571590Srgrimes			for (ap = altnames; *ap; ap++)
2581590Srgrimes				np = delname(np, *ap);
2591590Srgrimes		head.h_cc = np;
2601590Srgrimes	} else
2611590Srgrimes		head.h_cc = NIL;
2621590Srgrimes	head.h_bcc = NIL;
2631590Srgrimes	head.h_smopts = NIL;
26432189Sjoerg	if ((head.h_replyto = getenv("REPLYTO")) == NULL)
26532189Sjoerg		head.h_replyto = NOSTR;
26632189Sjoerg	head.h_inreplyto = skin(hfield("message-id", mp));
2671590Srgrimes	mail1(&head, 1);
2681590Srgrimes	return(0);
2691590Srgrimes}
2701590Srgrimes
2711590Srgrimes/*
2721590Srgrimes * Modify the subject we are replying to to begin with Re: if
2731590Srgrimes * it does not already.
2741590Srgrimes */
2751590Srgrimeschar *
2761590Srgrimesreedit(subj)
2771590Srgrimes	register char *subj;
2781590Srgrimes{
2791590Srgrimes	char *newsubj;
2801590Srgrimes
2811590Srgrimes	if (subj == NOSTR)
2821590Srgrimes		return NOSTR;
2831590Srgrimes	if ((subj[0] == 'r' || subj[0] == 'R') &&
2841590Srgrimes	    (subj[1] == 'e' || subj[1] == 'E') &&
2851590Srgrimes	    subj[2] == ':')
2861590Srgrimes		return subj;
2871590Srgrimes	newsubj = salloc(strlen(subj) + 5);
28874769Smikeh	sprintf(newsubj, "Re: %s", subj);
2891590Srgrimes	return newsubj;
2901590Srgrimes}
2911590Srgrimes
2921590Srgrimes/*
2931590Srgrimes * Preserve the named messages, so that they will be sent
2941590Srgrimes * back to the system mailbox.
2951590Srgrimes */
2961590Srgrimesint
2971590Srgrimespreserve(msgvec)
2981590Srgrimes	int *msgvec;
2991590Srgrimes{
3001590Srgrimes	register struct message *mp;
3011590Srgrimes	register int *ip, mesg;
3021590Srgrimes
3031590Srgrimes	if (edit) {
3041590Srgrimes		printf("Cannot \"preserve\" in edit mode\n");
3051590Srgrimes		return(1);
3061590Srgrimes	}
30729574Sphk	for (ip = msgvec; *ip != 0; ip++) {
3081590Srgrimes		mesg = *ip;
3091590Srgrimes		mp = &message[mesg-1];
3101590Srgrimes		mp->m_flag |= MPRESERVE;
3111590Srgrimes		mp->m_flag &= ~MBOX;
3121590Srgrimes		dot = mp;
3131590Srgrimes	}
3141590Srgrimes	return(0);
3151590Srgrimes}
3161590Srgrimes
3171590Srgrimes/*
3181590Srgrimes * Mark all given messages as unread.
3191590Srgrimes */
3201590Srgrimesint
3211590Srgrimesunread(msgvec)
3221590Srgrimes	int	msgvec[];
3231590Srgrimes{
3241590Srgrimes	register int *ip;
3251590Srgrimes
32629574Sphk	for (ip = msgvec; *ip != 0; ip++) {
3271590Srgrimes		dot = &message[*ip-1];
3281590Srgrimes		dot->m_flag &= ~(MREAD|MTOUCH);
3291590Srgrimes		dot->m_flag |= MSTATUS;
3301590Srgrimes	}
3311590Srgrimes	return(0);
3321590Srgrimes}
3331590Srgrimes
3341590Srgrimes/*
3351590Srgrimes * Print the size of each message.
3361590Srgrimes */
3371590Srgrimesint
3381590Srgrimesmessize(msgvec)
3391590Srgrimes	int *msgvec;
3401590Srgrimes{
3411590Srgrimes	register struct message *mp;
3421590Srgrimes	register int *ip, mesg;
3431590Srgrimes
34429574Sphk	for (ip = msgvec; *ip != 0; ip++) {
3451590Srgrimes		mesg = *ip;
3461590Srgrimes		mp = &message[mesg-1];
34737453Sbde		printf("%d: %ld/%ld\n", mesg, mp->m_lines, mp->m_size);
3481590Srgrimes	}
3491590Srgrimes	return(0);
3501590Srgrimes}
3511590Srgrimes
3521590Srgrimes/*
3531590Srgrimes * Quit quickly.  If we are sourcing, just pop the input level
3541590Srgrimes * by returning an error.
3551590Srgrimes */
3561590Srgrimesint
3571590Srgrimesrexit(e)
3581590Srgrimes	int e;
3591590Srgrimes{
3601590Srgrimes	if (sourcing)
3611590Srgrimes		return(1);
3621590Srgrimes	exit(e);
3631590Srgrimes	/*NOTREACHED*/
3641590Srgrimes}
3651590Srgrimes
3661590Srgrimes/*
3671590Srgrimes * Set or display a variable value.  Syntax is similar to that
3681590Srgrimes * of csh.
3691590Srgrimes */
3701590Srgrimesint
3711590Srgrimesset(arglist)
3721590Srgrimes	char **arglist;
3731590Srgrimes{
3741590Srgrimes	register struct var *vp;
3751590Srgrimes	register char *cp, *cp2;
3761590Srgrimes	char varbuf[BUFSIZ], **ap, **p;
3771590Srgrimes	int errs, h, s;
3781590Srgrimes
3791590Srgrimes	if (*arglist == NOSTR) {
3801590Srgrimes		for (h = 0, s = 1; h < HSHSIZE; h++)
3811590Srgrimes			for (vp = variables[h]; vp != NOVAR; vp = vp->v_link)
3821590Srgrimes				s++;
3831590Srgrimes		ap = (char **) salloc(s * sizeof *ap);
3841590Srgrimes		for (h = 0, p = ap; h < HSHSIZE; h++)
3851590Srgrimes			for (vp = variables[h]; vp != NOVAR; vp = vp->v_link)
3861590Srgrimes				*p++ = vp->v_name;
3871590Srgrimes		*p = NOSTR;
3881590Srgrimes		sort(ap);
3891590Srgrimes		for (p = ap; *p != NOSTR; p++)
3901590Srgrimes			printf("%s\t%s\n", *p, value(*p));
3911590Srgrimes		return(0);
3921590Srgrimes	}
3931590Srgrimes	errs = 0;
3941590Srgrimes	for (ap = arglist; *ap != NOSTR; ap++) {
3951590Srgrimes		cp = *ap;
3961590Srgrimes		cp2 = varbuf;
39774769Smikeh		while (cp2 < varbuf + sizeof(varbuf) - 1 && *cp != '=' && *cp != '\0')
3981590Srgrimes			*cp2++ = *cp++;
3991590Srgrimes		*cp2 = '\0';
4001590Srgrimes		if (*cp == '\0')
4011590Srgrimes			cp = "";
4021590Srgrimes		else
4031590Srgrimes			cp++;
4041590Srgrimes		if (equal(varbuf, "")) {
4051590Srgrimes			printf("Non-null variable name required\n");
4061590Srgrimes			errs++;
4071590Srgrimes			continue;
4081590Srgrimes		}
4091590Srgrimes		assign(varbuf, cp);
4101590Srgrimes	}
4111590Srgrimes	return(errs);
4121590Srgrimes}
4131590Srgrimes
4141590Srgrimes/*
4151590Srgrimes * Unset a bunch of variable values.
4161590Srgrimes */
4171590Srgrimesint
4181590Srgrimesunset(arglist)
4191590Srgrimes	char **arglist;
4201590Srgrimes{
4211590Srgrimes	register struct var *vp, *vp2;
4221590Srgrimes	int errs, h;
4231590Srgrimes	char **ap;
4241590Srgrimes
4251590Srgrimes	errs = 0;
4261590Srgrimes	for (ap = arglist; *ap != NOSTR; ap++) {
4271590Srgrimes		if ((vp2 = lookup(*ap)) == NOVAR) {
4281590Srgrimes			if (!sourcing) {
4291590Srgrimes				printf("\"%s\": undefined variable\n", *ap);
4301590Srgrimes				errs++;
4311590Srgrimes			}
4321590Srgrimes			continue;
4331590Srgrimes		}
4341590Srgrimes		h = hash(*ap);
4351590Srgrimes		if (vp2 == variables[h]) {
4361590Srgrimes			variables[h] = variables[h]->v_link;
4371590Srgrimes			vfree(vp2->v_name);
4381590Srgrimes			vfree(vp2->v_value);
4391590Srgrimes			free((char *)vp2);
4401590Srgrimes			continue;
4411590Srgrimes		}
4421590Srgrimes		for (vp = variables[h]; vp->v_link != vp2; vp = vp->v_link)
4431590Srgrimes			;
4441590Srgrimes		vp->v_link = vp2->v_link;
4451590Srgrimes		vfree(vp2->v_name);
4461590Srgrimes		vfree(vp2->v_value);
4471590Srgrimes		free((char *) vp2);
4481590Srgrimes	}
4491590Srgrimes	return(errs);
4501590Srgrimes}
4511590Srgrimes
4521590Srgrimes/*
4531590Srgrimes * Put add users to a group.
4541590Srgrimes */
4551590Srgrimesint
4561590Srgrimesgroup(argv)
4571590Srgrimes	char **argv;
4581590Srgrimes{
4591590Srgrimes	register struct grouphead *gh;
4601590Srgrimes	register struct group *gp;
4611590Srgrimes	register int h;
4621590Srgrimes	int s;
4631590Srgrimes	char **ap, *gname, **p;
4641590Srgrimes
4651590Srgrimes	if (*argv == NOSTR) {
4661590Srgrimes		for (h = 0, s = 1; h < HSHSIZE; h++)
4671590Srgrimes			for (gh = groups[h]; gh != NOGRP; gh = gh->g_link)
4681590Srgrimes				s++;
4691590Srgrimes		ap = (char **) salloc(s * sizeof *ap);
4701590Srgrimes		for (h = 0, p = ap; h < HSHSIZE; h++)
4711590Srgrimes			for (gh = groups[h]; gh != NOGRP; gh = gh->g_link)
4721590Srgrimes				*p++ = gh->g_name;
4731590Srgrimes		*p = NOSTR;
4741590Srgrimes		sort(ap);
4751590Srgrimes		for (p = ap; *p != NOSTR; p++)
4761590Srgrimes			printgroup(*p);
4771590Srgrimes		return(0);
4781590Srgrimes	}
4791590Srgrimes	if (argv[1] == NOSTR) {
4801590Srgrimes		printgroup(*argv);
4811590Srgrimes		return(0);
4821590Srgrimes	}
4831590Srgrimes	gname = *argv;
4841590Srgrimes	h = hash(gname);
4851590Srgrimes	if ((gh = findgroup(gname)) == NOGRP) {
4861590Srgrimes		gh = (struct grouphead *) calloc(sizeof *gh, 1);
4871590Srgrimes		gh->g_name = vcopy(gname);
4881590Srgrimes		gh->g_list = NOGE;
4891590Srgrimes		gh->g_link = groups[h];
4901590Srgrimes		groups[h] = gh;
4911590Srgrimes	}
4921590Srgrimes
4931590Srgrimes	/*
4941590Srgrimes	 * Insert names from the command list into the group.
4951590Srgrimes	 * Who cares if there are duplicates?  They get tossed
4961590Srgrimes	 * later anyway.
4971590Srgrimes	 */
4981590Srgrimes
4991590Srgrimes	for (ap = argv+1; *ap != NOSTR; ap++) {
5001590Srgrimes		gp = (struct group *) calloc(sizeof *gp, 1);
5011590Srgrimes		gp->ge_name = vcopy(*ap);
5021590Srgrimes		gp->ge_link = gh->g_list;
5031590Srgrimes		gh->g_list = gp;
5041590Srgrimes	}
5051590Srgrimes	return(0);
5061590Srgrimes}
5071590Srgrimes
5081590Srgrimes/*
5091590Srgrimes * Sort the passed string vecotor into ascending dictionary
5101590Srgrimes * order.
5111590Srgrimes */
5121590Srgrimesvoid
5131590Srgrimessort(list)
5141590Srgrimes	char **list;
5151590Srgrimes{
5161590Srgrimes	register char **ap;
5171590Srgrimes	int diction();
5181590Srgrimes
5191590Srgrimes	for (ap = list; *ap != NOSTR; ap++)
5201590Srgrimes		;
5211590Srgrimes	if (ap-list < 2)
5221590Srgrimes		return;
5231590Srgrimes	qsort(list, ap-list, sizeof(*list), diction);
5241590Srgrimes}
5251590Srgrimes
5261590Srgrimes/*
5271590Srgrimes * Do a dictionary order comparison of the arguments from
5281590Srgrimes * qsort.
5291590Srgrimes */
5301590Srgrimesint
5311590Srgrimesdiction(a, b)
5321590Srgrimes	const void *a, *b;
5331590Srgrimes{
5341590Srgrimes	return(strcmp(*(char **)a, *(char **)b));
5351590Srgrimes}
5361590Srgrimes
5371590Srgrimes/*
5381590Srgrimes * The do nothing command for comments.
5391590Srgrimes */
5401590Srgrimes
5411590Srgrimes/*ARGSUSED*/
5421590Srgrimesint
5431590Srgrimesnull(e)
5441590Srgrimes	int e;
5451590Srgrimes{
5461590Srgrimes	return 0;
5471590Srgrimes}
5481590Srgrimes
5491590Srgrimes/*
5501590Srgrimes * Change to another file.  With no argument, print information about
5511590Srgrimes * the current file.
5521590Srgrimes */
5531590Srgrimesint
5541590Srgrimesfile(argv)
5551590Srgrimes	register char **argv;
5561590Srgrimes{
5571590Srgrimes
5581590Srgrimes	if (argv[0] == NOSTR) {
5591590Srgrimes		newfileinfo();
5601590Srgrimes		return 0;
5611590Srgrimes	}
5621590Srgrimes	if (setfile(*argv) < 0)
5631590Srgrimes		return 1;
5641590Srgrimes	announce();
5651590Srgrimes	return 0;
5661590Srgrimes}
5671590Srgrimes
5681590Srgrimes/*
5691590Srgrimes * Expand file names like echo
5701590Srgrimes */
5711590Srgrimesint
5721590Srgrimesecho(argv)
5731590Srgrimes	char **argv;
5741590Srgrimes{
5751590Srgrimes	register char **ap;
5761590Srgrimes	register char *cp;
5771590Srgrimes
5781590Srgrimes	for (ap = argv; *ap != NOSTR; ap++) {
5791590Srgrimes		cp = *ap;
5801590Srgrimes		if ((cp = expand(cp)) != NOSTR) {
5811590Srgrimes			if (ap != argv)
5821590Srgrimes				putchar(' ');
5831590Srgrimes			printf("%s", cp);
5841590Srgrimes		}
5851590Srgrimes	}
5861590Srgrimes	putchar('\n');
5871590Srgrimes	return 0;
5881590Srgrimes}
5891590Srgrimes
5901590Srgrimesint
5911590SrgrimesRespond(msgvec)
5921590Srgrimes	int *msgvec;
5931590Srgrimes{
5941590Srgrimes	if (value("Replyall") == NOSTR)
59532189Sjoerg		return (doRespond(msgvec));
5961590Srgrimes	else
59732189Sjoerg		return (dorespond(msgvec));
5981590Srgrimes}
5991590Srgrimes
6001590Srgrimes/*
6011590Srgrimes * Reply to a series of messages by simply mailing to the senders
6021590Srgrimes * and not messing around with the To: and Cc: lists as in normal
6031590Srgrimes * reply.
6041590Srgrimes */
6051590Srgrimesint
60632189SjoergdoRespond(msgvec)
6071590Srgrimes	int msgvec[];
6081590Srgrimes{
6091590Srgrimes	struct header head;
6101590Srgrimes	struct message *mp;
6111590Srgrimes	register int *ap;
6121590Srgrimes	register char *cp;
61332189Sjoerg	char *mid;
6141590Srgrimes
6151590Srgrimes	head.h_to = NIL;
6161590Srgrimes	for (ap = msgvec; *ap != 0; ap++) {
6171590Srgrimes		mp = &message[*ap - 1];
6181590Srgrimes		touch(mp);
6191590Srgrimes		dot = mp;
6201590Srgrimes		if ((cp = skin(hfield("from", mp))) == NOSTR)
6211590Srgrimes			cp = skin(nameof(mp, 2));
6221590Srgrimes		head.h_to = cat(head.h_to, extract(cp, GTO));
62332189Sjoerg		mid = skin(hfield("message-id", mp));
6241590Srgrimes	}
6251590Srgrimes	if (head.h_to == NIL)
6261590Srgrimes		return 0;
6271590Srgrimes	mp = &message[msgvec[0] - 1];
6281590Srgrimes	if ((head.h_subject = hfield("subject", mp)) == NOSTR)
6291590Srgrimes		head.h_subject = hfield("subj", mp);
6301590Srgrimes	head.h_subject = reedit(head.h_subject);
6311590Srgrimes	head.h_cc = NIL;
6321590Srgrimes	head.h_bcc = NIL;
6331590Srgrimes	head.h_smopts = NIL;
63432189Sjoerg	if ((head.h_replyto = getenv("REPLYTO")) == NULL)
63532189Sjoerg		head.h_replyto = NOSTR;
63632189Sjoerg	head.h_inreplyto = mid;
6371590Srgrimes	mail1(&head, 1);
6381590Srgrimes	return 0;
6391590Srgrimes}
6401590Srgrimes
6411590Srgrimes/*
6421590Srgrimes * Conditional commands.  These allow one to parameterize one's
6431590Srgrimes * .mailrc and do some things if sending, others if receiving.
6441590Srgrimes */
6451590Srgrimesint
6461590Srgrimesifcmd(argv)
6471590Srgrimes	char **argv;
6481590Srgrimes{
6491590Srgrimes	register char *cp;
6501590Srgrimes
6511590Srgrimes	if (cond != CANY) {
6521590Srgrimes		printf("Illegal nested \"if\"\n");
6531590Srgrimes		return(1);
6541590Srgrimes	}
6551590Srgrimes	cond = CANY;
6561590Srgrimes	cp = argv[0];
6571590Srgrimes	switch (*cp) {
6581590Srgrimes	case 'r': case 'R':
6591590Srgrimes		cond = CRCV;
6601590Srgrimes		break;
6611590Srgrimes
6621590Srgrimes	case 's': case 'S':
6631590Srgrimes		cond = CSEND;
6641590Srgrimes		break;
6651590Srgrimes
6661590Srgrimes	default:
6671590Srgrimes		printf("Unrecognized if-keyword: \"%s\"\n", cp);
6681590Srgrimes		return(1);
6691590Srgrimes	}
6701590Srgrimes	return(0);
6711590Srgrimes}
6721590Srgrimes
6731590Srgrimes/*
6741590Srgrimes * Implement 'else'.  This is pretty simple -- we just
6751590Srgrimes * flip over the conditional flag.
6761590Srgrimes */
6771590Srgrimesint
6781590Srgrimeselsecmd()
6791590Srgrimes{
6801590Srgrimes
6811590Srgrimes	switch (cond) {
6821590Srgrimes	case CANY:
6831590Srgrimes		printf("\"Else\" without matching \"if\"\n");
6841590Srgrimes		return(1);
6851590Srgrimes
6861590Srgrimes	case CSEND:
6871590Srgrimes		cond = CRCV;
6881590Srgrimes		break;
6891590Srgrimes
6901590Srgrimes	case CRCV:
6911590Srgrimes		cond = CSEND;
6921590Srgrimes		break;
6931590Srgrimes
6941590Srgrimes	default:
6951590Srgrimes		printf("Mail's idea of conditions is screwed up\n");
6961590Srgrimes		cond = CANY;
6971590Srgrimes		break;
6981590Srgrimes	}
6991590Srgrimes	return(0);
7001590Srgrimes}
7011590Srgrimes
7021590Srgrimes/*
7031590Srgrimes * End of if statement.  Just set cond back to anything.
7041590Srgrimes */
7051590Srgrimesint
7061590Srgrimesendifcmd()
7071590Srgrimes{
7081590Srgrimes
7091590Srgrimes	if (cond == CANY) {
7101590Srgrimes		printf("\"Endif\" without matching \"if\"\n");
7111590Srgrimes		return(1);
7121590Srgrimes	}
7131590Srgrimes	cond = CANY;
7141590Srgrimes	return(0);
7151590Srgrimes}
7161590Srgrimes
7171590Srgrimes/*
7181590Srgrimes * Set the list of alternate names.
7191590Srgrimes */
7201590Srgrimesint
7211590Srgrimesalternates(namelist)
7221590Srgrimes	char **namelist;
7231590Srgrimes{
7241590Srgrimes	register int c;
7251590Srgrimes	register char **ap, **ap2, *cp;
7261590Srgrimes
7271590Srgrimes	c = argcount(namelist) + 1;
7281590Srgrimes	if (c == 1) {
7291590Srgrimes		if (altnames == 0)
7301590Srgrimes			return(0);
7311590Srgrimes		for (ap = altnames; *ap; ap++)
7321590Srgrimes			printf("%s ", *ap);
7331590Srgrimes		printf("\n");
7341590Srgrimes		return(0);
7351590Srgrimes	}
7361590Srgrimes	if (altnames != 0)
7371590Srgrimes		free((char *) altnames);
7381590Srgrimes	altnames = (char **) calloc((unsigned) c, sizeof (char *));
7391590Srgrimes	for (ap = namelist, ap2 = altnames; *ap; ap++, ap2++) {
7401590Srgrimes		cp = (char *) calloc((unsigned) strlen(*ap) + 1, sizeof (char));
7411590Srgrimes		strcpy(cp, *ap);
7421590Srgrimes		*ap2 = cp;
7431590Srgrimes	}
7441590Srgrimes	*ap2 = 0;
7451590Srgrimes	return(0);
7461590Srgrimes}
747