1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22
23/*
24 * Copyright 2002 Sun Microsystems, Inc.  All rights reserved.
25 * Use is subject to license terms.
26 */
27
28/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
29/*	  All Rights Reserved  	*/
30
31/*
32 * University Copyright- Copyright (c) 1982, 1986, 1988
33 * The Regents of the University of California
34 * All Rights Reserved
35 *
36 * University Acknowledgment- Portions of this document are derived from
37 * software developed by the University of California, Berkeley, and its
38 * contributors.
39 */
40
41#include "rcv.h"
42#include <locale.h>
43
44/*
45 * mailx -- a modified version of a University of California at Berkeley
46 *	mail program
47 *
48 * Still more user commands.
49 */
50
51static int	bangexp(char *str);
52static int	diction(const void *a, const void *b);
53static char	*getfilename(char *name, int *aedit);
54static int	resp1(int *msgvec, int useauthor);
55static int	Resp1(int *msgvec, int useauthor);
56static char	*reedit(char *subj);
57static int	shell1(char *str);
58static void	sort(char **list);
59static char	*replyto(struct message *mp, char **f);
60static int	reply2sender(void);
61
62static char	prevfile[PATHSIZE];
63static char	origprevfile[PATHSIZE];
64static char	lastbang[BUFSIZ];
65
66/*
67 * Process a shell escape by saving signals, ignoring signals,
68 * and forking a sh -c
69 */
70
71int
72shell(char *str)
73{
74	shell1(str);
75	printf("!\n");
76	return(0);
77}
78
79static int
80shell1(char *str)
81{
82	void (*sig[2])(int);
83	int t;
84	pid_t p;
85	char *Shell;
86	char cmd[BUFSIZ];
87
88	nstrcpy(cmd, sizeof (cmd), str);
89	if (bangexp(cmd) < 0)
90		return(-1);
91	if ((Shell = value("SHELL")) == NOSTR || *Shell=='\0')
92		Shell = SHELL;
93	for (t = SIGINT; t <= SIGQUIT; t++)
94		sig[t-SIGINT] = sigset(t, SIG_IGN);
95	p = vfork();
96	if (p == 0) {
97		setuid(getuid());
98		sigchild();
99		for (t = SIGINT; t <= SIGQUIT; t++)
100			if (sig[t-SIGINT] != SIG_IGN)
101				sigsys(t, SIG_DFL);
102		execlp(Shell, Shell, "-c", cmd, (char *)0);
103		perror(Shell);
104		_exit(1);
105	}
106	while (wait(0) != p)
107		;
108	if (p == (pid_t)-1)
109		perror("fork");
110	for (t = SIGINT; t <= SIGQUIT; t++)
111		sigset(t, sig[t-SIGINT]);
112	return(0);
113}
114
115/*
116 * Fork an interactive shell.
117 */
118
119int
120#ifdef	__cplusplus
121dosh(char *)
122#else
123/* ARGSUSED */
124dosh(char *s)
125#endif
126{
127	void (*sig[2])(int);
128	int t;
129	pid_t p;
130	char *Shell;
131
132	if ((Shell = value("SHELL")) == NOSTR || *Shell=='\0')
133		Shell = SHELL;
134	for (t = SIGINT; t <= SIGQUIT; t++)
135		sig[t-SIGINT] = sigset(t, SIG_IGN);
136	p = vfork();
137	if (p == 0) {
138		setuid(getuid());
139		sigchild();
140		for (t = SIGINT; t <= SIGQUIT; t++)
141			if (sig[t-SIGINT] != SIG_IGN)
142				sigset(t, SIG_DFL);
143		execlp(Shell, Shell, (char *)0);
144		perror(Shell);
145		_exit(1);
146	}
147	while (wait(0) != p)
148		;
149	if (p == (pid_t)-1)
150		perror("fork");
151	for (t = SIGINT; t <= SIGQUIT; t++)
152		sigset(t, sig[t-SIGINT]);
153	putchar('\n');
154	return(0);
155}
156
157/*
158 * Expand the shell escape by expanding unescaped !'s into the
159 * last issued command where possible.
160 */
161static int
162bangexp(char *str)
163{
164	char bangbuf[BUFSIZ];
165	char *cp, *cp2;
166	int n;
167	int changed = 0;
168	int bangit = (value("bang")!=NOSTR);
169
170	cp = str;
171	cp2 = bangbuf;
172	n = BUFSIZ;
173	while (*cp) {
174		if (*cp=='!' && bangit) {
175			if (n < (int)strlen(lastbang)) {
176overf:
177				printf(gettext("Command buffer overflow\n"));
178				return(-1);
179			}
180			changed++;
181			strcpy(cp2, lastbang);
182			cp2 += strlen(lastbang);
183			n -= strlen(lastbang);
184			cp++;
185			continue;
186		}
187		if (*cp == '\\' && cp[1] == '!') {
188			if (--n <= 1)
189				goto overf;
190			*cp2++ = '!';
191			cp += 2;
192			changed++;
193		}
194		if (--n <= 1)
195			goto overf;
196		*cp2++ = *cp++;
197	}
198	*cp2 = 0;
199	if (changed) {
200		printf("!%s\n", bangbuf);
201		fflush(stdout);
202	}
203	nstrcpy(str, BUFSIZ, bangbuf);
204	nstrcpy(lastbang, sizeof (lastbang), bangbuf);
205	return(0);
206}
207
208/*
209 * Print out a nice help message from some file or another.
210 */
211
212int
213help(void)
214{
215	int c;
216	FILE *f;
217
218	if ((f = fopen(HELPFILE, "r")) == NULL) {
219		printf(gettext("No help just now.\n"));
220		return(1);
221	}
222	while ((c = getc(f)) != EOF)
223		putchar(c);
224	fclose(f);
225	return(0);
226}
227
228/*
229 * Change user's working directory.
230 */
231
232int
233schdir(char *str)
234{
235	char *cp;
236	char cwd[PATHSIZE], file[PATHSIZE];
237	static char efile[PATHSIZE];
238
239	for (cp = str; *cp == ' '; cp++)
240		;
241	if (*cp == '\0')
242		cp = homedir;
243	else
244		if ((cp = expand(cp)) == NOSTR)
245			return(1);
246	if (editfile != NOSTR && (*editfile != '/' || mailname[0] != '/')) {
247		if (getcwd(cwd, (int)sizeof (cwd)) == 0) {
248			fprintf(stderr,
249			    gettext("Can't get current directory: %s\n"), cwd);
250			return(1);
251		}
252	}
253	if (chdir(cp) < 0) {
254		perror(cp);
255		return(1);
256	}
257	/*
258	 * Convert previously relative names to absolute names.
259	 */
260	if (editfile != NOSTR && *editfile != '/') {
261		snprintf(file, sizeof (file), "%s/%s", cwd, editfile);
262		nstrcpy(efile, sizeof (efile), file);
263		editfile = efile;
264	}
265	if (mailname[0] != '/') {
266		snprintf(file, sizeof (file), "%s/%s", cwd, mailname);
267		nstrcpy(mailname, PATHSIZE, file);
268	}
269	return(0);
270}
271
272/*
273 * Two versions of reply.  Reply to all names in message or reply
274 * to only sender of message, depending on setting of "replyall".
275 */
276
277int
278respond(int *msgvec)
279{
280	if (reply2sender())
281		return(resp1(msgvec, 0));
282	else
283		return(Resp1(msgvec, 0));
284}
285
286int
287followup(int *msgvec)
288{
289	if (reply2sender())
290		return(resp1(msgvec, 1));
291	else
292		return(Resp1(msgvec, 1));
293}
294
295int
296replyall(int *msgvec)
297{
298	return(resp1(msgvec, 0));
299}
300
301static int
302resp1(int *msgvec, int useauthor)
303{
304	struct message *mp;
305	char *cp, *buf, *rcv, *skin_rcv, *reply2, **ap, *returnaddr;
306	struct name *np;
307	struct header head;
308	char mylocalname[BUFSIZ], mydomname[BUFSIZ];
309
310	if (msgvec[1] != 0) {
311		printf(gettext(
312		    "Sorry, can't reply to multiple messages at once\n"));
313		return(1);
314	}
315	snprintf(mydomname, sizeof (mydomname), "%s@%s", myname, domain);
316	snprintf(mylocalname, sizeof (mylocalname), "%s@%s", myname, host);
317	returnaddr = value("returnaddr");
318
319	mp = &message[msgvec[0] - 1];
320	dot = mp;
321	reply2 = replyto(mp, &rcv);
322	cp = skin(hfield("to", mp, addto));
323	if (cp != NOSTR) {
324		buf = (char *)salloc(strlen(reply2) + strlen(cp) + 2);
325		strcpy(buf, reply2);
326		strcat(buf, " ");
327		strcat(buf, cp);
328	} else
329		buf = reply2;
330	np = elide(extract(buf, GTO));
331#ifdef	OPTIM
332	/* rcv = netrename(rcv); */
333#endif	/* OPTIM */
334	/*
335	 * Delete my name from the reply list,
336	 * and with it, all my alternate names.
337	 */
338	skin_rcv = skin(rcv);
339	mapf(np, skin_rcv);
340	np = delname(np, myname);
341	np = delname(np, mylocalname);
342	np = delname(np, mydomname);
343	if (returnaddr && *returnaddr)
344		np = delname(np, returnaddr);
345	if (altnames != 0)
346		for (ap = altnames; *ap; ap++)
347			np = delname(np, *ap);
348	head.h_seq = 1;
349	cp = detract(np, 0);
350	if (cp == NOSTR) {
351		if (reply2)
352			cp = unuucp(reply2);
353		else
354			cp = unuucp(rcv);
355	}
356	head.h_to = cp;
357	head.h_subject = hfield("subject", mp, addone);
358	if (head.h_subject == NOSTR)
359		head.h_subject = hfield("subj", mp, addone);
360	head.h_subject = reedit(head.h_subject);
361	head.h_cc = NOSTR;
362	cp = skin(hfield("cc", mp, addto));
363	if (cp != NOSTR) {
364		np = elide(extract(cp, GCC));
365		mapf(np, skin_rcv);
366		np = delname(np, myname);
367		np = delname(np, mylocalname);
368		np = delname(np, mydomname);
369		if (returnaddr && *returnaddr)
370			np = delname(np, returnaddr);
371		np = delname(np, skin_rcv);
372		if (altnames != 0)
373			for (ap = altnames; *ap; ap++)
374				np = delname(np, *ap);
375		head.h_cc = detract(np, 0);
376	}
377	head.h_bcc = NOSTR;
378	head.h_defopt = NOSTR;
379	head.h_others = NOSTRPTR;
380	mail1(&head, useauthor, useauthor ? rcv : NOSTR);
381	return(0);
382}
383
384void
385getrecf(char *buf, char *recfile, int useauthor, int sz_recfile)
386{
387	char *bp, *cp;
388	char *recf = recfile;
389	int folderize;
390	char fldr[BUFSIZ];
391
392	folderize = (value("outfolder")!=NOSTR && getfold(fldr) == 0);
393
394	if (useauthor) {
395		if (folderize)
396			*recf++ = '+';
397		if (debug) fprintf(stderr, "buf='%s'\n", buf);
398		for (bp=skin(buf), cp=recf; *bp && !any(*bp, ", "); bp++) {
399			if (*bp=='!')
400				cp = recf;
401			else
402				*cp++ = *bp;
403
404			if (cp >= &recfile[sz_recfile - 1]) {
405				printf(gettext("File name buffer overflow\n"));
406				break;
407			}
408		}
409		*cp = '\0';
410		if (cp==recf)
411			*recfile = '\0';
412		/* now strip off any Internet host names */
413		if ((cp = strchr(recf, '%')) == NOSTR)
414			cp = strchr(recf, '@');
415		if (cp != NOSTR)
416			*cp = '\0';
417	} else {
418		if (cp = value("record")) {
419			int sz = PATHSIZE;
420			if (folderize && *cp!='+' && *cp!='/'
421			 && *safeexpand(cp)!='/') {
422				*recf++ = '+';
423				sz--;
424			}
425			nstrcpy(recf, sz, cp);
426		} else
427			*recf = '\0';
428	}
429	if (debug) fprintf(stderr, "recfile='%s'\n", recfile);
430}
431
432/*
433 * Modify the subject we are replying to to begin with Re: if
434 * it does not already.
435 */
436
437static char *
438reedit(char *subj)
439{
440	char sbuf[10];
441	char *newsubj;
442
443	if (subj == NOSTR)
444		return(NOSTR);
445	strncpy(sbuf, subj, 3);
446	sbuf[3] = 0;
447	if (icequal(sbuf, "re:"))
448		return(subj);
449	newsubj = (char *)salloc((unsigned)(strlen(subj) + 5));
450	sprintf(newsubj, "Re: %s", subj);
451	return(newsubj);
452}
453
454/*
455 * Preserve the named messages, so that they will be sent
456 * back to the system mailbox.
457 */
458
459int
460preserve(int *msgvec)
461{
462	struct message *mp;
463	int *ip, mesg;
464
465	if (edit) {
466		printf(gettext("Cannot \"preserve\" in edit mode\n"));
467		return(1);
468	}
469	for (ip = msgvec; *ip != 0; ip++) {
470		mesg = *ip;
471		mp = &message[mesg-1];
472		mp->m_flag |= MPRESERVE;
473		mp->m_flag &= ~MBOX;
474		dot = mp;
475	}
476	return(0);
477}
478
479/*
480 * Mark all given messages as unread.
481 */
482int
483unread(int msgvec[])
484{
485	int *ip;
486
487	for (ip = msgvec; *ip != 0; ip++) {
488		dot = &message[*ip-1];
489		dot->m_flag &= ~(MREAD|MTOUCH);
490		dot->m_flag |= MSTATUS;
491	}
492	return(0);
493}
494
495/*
496 * Print the size of each message.
497 */
498
499int
500messize(int *msgvec)
501{
502	struct message *mp;
503	int *ip, mesg;
504
505	for (ip = msgvec; *ip != 0; ip++) {
506		mesg = *ip;
507		mp = &message[mesg-1];
508		dot = mp;
509		printf("%d: %ld\n", mesg, mp->m_size);
510	}
511	return(0);
512}
513
514/*
515 * Quit quickly.  If we are sourcing, just pop the input level
516 * by returning an error.
517 */
518
519int
520rexit(int e)
521{
522	if (sourcing)
523		return(1);
524	if (Tflag != NOSTR)
525		close(creat(Tflag, TEMPPERM));
526	if (!edit)
527		Verhogen();
528	exit(e ? e : rpterr);
529	/* NOTREACHED */
530	return (0);	/* shut up lint and CC */
531}
532
533/*
534 * Set or display a variable value.  Syntax is similar to that
535 * of csh.
536 */
537
538int
539set(char **arglist)
540{
541	struct var *vp;
542	char *cp, *cp2;
543	char varbuf[BUFSIZ], **ap, **p;
544	int errs, h, s;
545
546	if (argcount(arglist) == 0) {
547		for (h = 0, s = 1; h < HSHSIZE; h++)
548			for (vp = variables[h]; vp != NOVAR; vp = vp->v_link)
549				s++;
550		ap = (char **) salloc(s * sizeof *ap);
551		for (h = 0, p = ap; h < HSHSIZE; h++)
552			for (vp = variables[h]; vp != NOVAR; vp = vp->v_link)
553				*p++ = vp->v_name;
554		*p = NOSTR;
555		sort(ap);
556		for (p = ap; *p != NOSTR; p++)
557			if (((cp = value(*p)) != 0) && *cp)
558				printf("%s=\"%s\"\n", *p, cp);
559			else
560				printf("%s\n", *p);
561		return(0);
562	}
563	errs = 0;
564	for (ap = arglist; *ap != NOSTR; ap++) {
565		cp = *ap;
566		cp2 = varbuf;
567		while (*cp != '=' && *cp != '\0')
568			*cp2++ = *cp++;
569		*cp2 = '\0';
570		if (*cp == '\0')
571			cp = "";
572		else
573			cp++;
574		if (equal(varbuf, "")) {
575			printf(gettext("Non-null variable name required\n"));
576			errs++;
577			continue;
578		}
579		assign(varbuf, cp);
580	}
581	return(errs);
582}
583
584/*
585 * Unset a bunch of variable values.
586 */
587
588int
589unset(char **arglist)
590{
591	int errs;
592	char **ap;
593
594	errs = 0;
595	for (ap = arglist; *ap != NOSTR; ap++)
596		errs += deassign(*ap);
597	return(errs);
598}
599
600/*
601 * Add users to a group.
602 */
603
604int
605group(char **argv)
606{
607	struct grouphead *gh;
608	struct mgroup *gp;
609	int h;
610	int s;
611	char **ap, *gname, **p;
612
613	if (argcount(argv) == 0) {
614		for (h = 0, s = 1; h < HSHSIZE; h++)
615			for (gh = groups[h]; gh != NOGRP; gh = gh->g_link)
616				s++;
617		ap = (char **) salloc(s * sizeof *ap);
618		for (h = 0, p = ap; h < HSHSIZE; h++)
619			for (gh = groups[h]; gh != NOGRP; gh = gh->g_link)
620				*p++ = gh->g_name;
621		*p = NOSTR;
622		sort(ap);
623		for (p = ap; *p != NOSTR; p++)
624			printgroup(*p);
625		return(0);
626	}
627	if (argcount(argv) == 1) {
628		printgroup(*argv);
629		return(0);
630	}
631	gname = *argv;
632	h = hash(gname);
633	if ((gh = findgroup(gname)) == NOGRP) {
634		if ((gh = (struct grouphead *)
635		    calloc(sizeof (*gh), 1)) == NULL) {
636			panic("Failed to allocate memory for group");
637	}
638		gh->g_name = vcopy(gname);
639		gh->g_list = NOGE;
640		gh->g_link = groups[h];
641		groups[h] = gh;
642	}
643
644	/*
645	 * Insert names from the command list into the group.
646	 * Who cares if there are duplicates?  They get tossed
647	 * later anyway.
648	 */
649
650	for (ap = argv+1; *ap != NOSTR; ap++) {
651		if ((gp = (struct mgroup *)
652		    calloc(sizeof (*gp), 1)) == NULL) {
653			panic("Failed to allocate memory for group");
654	}
655	gp->ge_name = vcopy(*ap);
656		gp->ge_link = gh->g_list;
657		gh->g_list = gp;
658	}
659	return(0);
660}
661
662/*
663 * Remove users from a group.
664 */
665
666int
667ungroup(char **argv)
668{
669	struct grouphead *gh, **ghp;
670	struct mgroup *gp, *gpnext;
671	int h;
672	char **ap, *gname;
673
674	if (argcount(argv) == 0) {
675		printf("Must specify alias or group to remove\n");
676		return(1);
677	}
678
679	/*
680	 * Remove names on the command list from the group list.
681	 */
682
683	for (ap = argv; *ap != NOSTR; ap++) {
684		gname = *ap;
685		h = hash(gname);
686		for (ghp = &groups[h]; *ghp != NOGRP; ghp = &((*ghp)->g_link)) {
687			gh = *ghp;
688			if (equal(gh->g_name, gname)) {
689				/* remove from list */
690				*ghp = gh->g_link;
691				/* free each member of gorup */
692				for (gp = gh->g_list; gp != NOGE; gp = gpnext) {
693					gpnext = gp->ge_link;
694					vfree(gp->ge_name);
695					free(gp);
696				}
697				vfree(gh->g_name);
698				free(gh);
699				break;
700			}
701		}
702	}
703	return(0);
704}
705
706/*
707 * Sort the passed string vecotor into ascending dictionary
708 * order.
709 */
710
711static void
712sort(char **list)
713{
714	char **ap;
715
716	for (ap = list; *ap != NOSTR; ap++)
717		;
718	if (ap-list < 2)
719		return;
720	qsort((char *) list, (unsigned) (ap-list), sizeof *list, diction);
721}
722
723/*
724 * Do a dictionary order comparison of the arguments from
725 * qsort.
726 */
727static int
728diction(const void *a, const void *b)
729{
730	return(strcmp(*(char **)a, *(char **)b));
731}
732
733/*
734 * The do nothing command for comments.
735 */
736
737int
738#ifdef	__cplusplus
739null(char *)
740#else
741/* ARGSUSED */
742null(char *s)
743#endif
744{
745	return(0);
746}
747
748/*
749 * Print out the current edit file, if we are editing.
750 * Otherwise, print the name of the person who's mail
751 * we are reading.
752 */
753int
754file(char **argv)
755{
756	char *cp;
757	int editing, mdot;
758
759	if (argv[0] == NOSTR) {
760		mdot = newfileinfo(1);
761		dot = &message[mdot - 1];
762		return(0);
763	}
764
765	/*
766	 * Acker's!  Must switch to the new file.
767	 * We use a funny interpretation --
768	 *	# -- gets the previous file
769	 *	% -- gets the invoker's post office box
770	 *	%user -- gets someone else's post office box
771	 *	& -- gets invoker's mbox file
772	 *	string -- reads the given file
773	 */
774
775	cp = getfilename(argv[0], &editing);
776	if (cp == NOSTR)
777		return(-1);
778	if (setfile(cp, editing)) {
779		nstrcpy(origname, PATHSIZE, origprevfile);
780		return(-1);
781	}
782	mdot = newfileinfo(1);
783	dot = &message[mdot - 1];
784	return(0);
785}
786
787/*
788 * Evaluate the string given as a new mailbox name.
789 * Ultimately, we want this to support a number of meta characters.
790 * Possibly:
791 *	% -- for my system mail box
792 *	%user -- for user's system mail box
793 *	# -- for previous file
794 *	& -- get's invoker's mbox file
795 *	file name -- for any other file
796 */
797
798static char *
799getfilename(char *name, int *aedit)
800{
801	char *cp;
802	char savename[BUFSIZ];
803	char oldmailname[BUFSIZ];
804	char tmp[BUFSIZ];
805
806	/*
807	 * Assume we will be in "edit file" mode, until
808	 * proven wrong.
809	 */
810	*aedit = 1;
811	switch (*name) {
812	case '%':
813		*aedit = 0;
814		nstrcpy(prevfile, sizeof (prevfile), editfile);
815		nstrcpy(origprevfile, sizeof (origprevfile), origname);
816		if (name[1] != 0) {
817			nstrcpy(oldmailname, sizeof (oldmailname), mailname);
818			findmail(name+1);
819			cp = savestr(mailname);
820			nstrcpy(origname, PATHSIZE, cp);
821			nstrcpy(mailname, PATHSIZE, oldmailname);
822			return(cp);
823		}
824		nstrcpy(oldmailname, sizeof (oldmailname), mailname);
825		findmail(NULL);
826		cp = savestr(mailname);
827		nstrcpy(mailname, PATHSIZE, oldmailname);
828		nstrcpy(origname, PATHSIZE, cp);
829		return(cp);
830
831	case '#':
832		if (name[1] != 0)
833			goto regular;
834		if (prevfile[0] == 0) {
835			printf(gettext("No previous file\n"));
836			return(NOSTR);
837		}
838		cp = savestr(prevfile);
839		nstrcpy(prevfile, sizeof (prevfile), editfile);
840		nstrcpy(tmp, sizeof (tmp), origname);
841		nstrcpy(origname, PATHSIZE, origprevfile);
842		nstrcpy(origprevfile, sizeof (origprevfile), tmp);
843		return(cp);
844
845	case '&':
846		nstrcpy(prevfile, sizeof (prevfile), editfile);
847		nstrcpy(origprevfile, sizeof (origprevfile), origname);
848		if (name[1] == 0) {
849			cp=Getf("MBOX");
850			nstrcpy(origname, PATHSIZE, cp);
851			return(cp);
852		}
853		/* FALLTHROUGH */
854
855	default:
856regular:
857		nstrcpy(prevfile, sizeof (prevfile), editfile);
858		nstrcpy(origprevfile, sizeof (origprevfile), origname);
859		cp = safeexpand(name);
860		nstrcpy(origname, PATHSIZE, cp);
861		if (cp[0] != '/') {
862			name = getcwd(NOSTR, PATHSIZE);
863			nstrcat(name, PATHSIZE, "/");
864			nstrcat(name, PATHSIZE, cp);
865			cp = name;
866		}
867		return(cp);
868	}
869}
870
871/*
872 * Expand file names like echo
873 */
874
875int
876echo(char **argv)
877{
878	char *cp;
879	int neednl = 0;
880
881	while (*argv != NOSTR) {
882		cp = *argv++;
883		if ((cp = expand(cp)) != NOSTR) {
884			neednl++;
885			printf("%s", cp);
886			if (*argv!=NOSTR)
887				putchar(' ');
888		}
889	}
890	if (neednl)
891		putchar('\n');
892	return(0);
893}
894
895/*
896 * Reply to a series of messages by simply mailing to the senders
897 * and not messing around with the To: and Cc: lists as in normal
898 * reply.
899 */
900
901int
902Respond(int *msgvec)
903{
904	if (reply2sender())
905		return(Resp1(msgvec, 0));
906	else
907		return(resp1(msgvec, 0));
908}
909
910int
911Followup(int *msgvec)
912{
913	if (reply2sender())
914		return(Resp1(msgvec, 1));
915	else
916		return(resp1(msgvec, 1));
917}
918
919int
920replysender(int *msgvec)
921{
922	return(Resp1(msgvec, 0));
923}
924
925static int
926Resp1(int *msgvec, int useauthor)
927{
928	struct header head;
929	struct message *mp;
930	int s, *ap;
931	char *cp, *cp2, *subject;
932
933	for (s = 0, ap = msgvec; *ap != 0; ap++) {
934		mp = &message[*ap - 1];
935		dot = mp;
936		cp = replyto(mp, NOSTRPTR);
937		s += strlen(cp) + 1;
938	}
939	if (s == 0)
940		return(0);
941	cp = (char *)salloc(s + 2);
942	head.h_to = cp;
943	for (ap = msgvec; *ap != 0; ap++) {
944		mp = &message[*ap - 1];
945		cp2 = replyto(mp, NOSTRPTR);
946		cp = copy(cp2, cp);
947		*cp++ = ' ';
948	}
949	*--cp = 0;
950	mp = &message[msgvec[0] - 1];
951	subject = hfield("subject", mp, addone);
952	head.h_seq = 1;
953	if (subject == NOSTR)
954		subject = hfield("subj", mp, addone);
955	head.h_subject = reedit(subject);
956	if (subject != NOSTR)
957		head.h_seq++;
958	head.h_cc = NOSTR;
959	head.h_bcc = NOSTR;
960	head.h_defopt = NOSTR;
961	head.h_others = NOSTRPTR;
962	mail1(&head, useauthor, NOSTR);
963	return(0);
964}
965
966/*
967 * Conditional commands.  These allow one to parameterize one's
968 * .mailrc and do some things if sending, others if receiving.
969 */
970
971int
972ifcmd(char **argv)
973{
974	char *cp;
975
976	if (cond != CANY) {
977		printf(gettext("Illegal nested \"if\"\n"));
978		return(1);
979	}
980	cond = CANY;
981	cp = argv[0];
982	switch (*cp) {
983	case 'r': case 'R':
984		cond = CRCV;
985		break;
986
987	case 's': case 'S':
988		cond = CSEND;
989		break;
990
991	case 't': case 'T':
992		cond = CTTY;
993		break;
994
995	default:
996		printf(gettext("Unrecognized if-keyword: \"%s\"\n"), cp);
997		return(1);
998	}
999	return(0);
1000}
1001
1002/*
1003 * Implement 'else'.  This is pretty simple -- we just
1004 * flip over the conditional flag.
1005 */
1006
1007int
1008elsecmd(void)
1009{
1010
1011	switch (cond) {
1012	case CANY:
1013		printf(gettext("\"Else\" without matching \"if\"\n"));
1014		return(1);
1015
1016	case CSEND:
1017		cond = CRCV;
1018		break;
1019
1020	case CRCV:
1021		cond = CSEND;
1022		break;
1023
1024	case CTTY:
1025		cond = CNOTTY;
1026		break;
1027
1028	case CNOTTY:
1029		cond = CTTY;
1030		break;
1031
1032	default:
1033		printf(gettext("invalid condition encountered\n"));
1034		cond = CANY;
1035		break;
1036	}
1037	return(0);
1038}
1039
1040/*
1041 * End of if statement.  Just set cond back to anything.
1042 */
1043
1044int
1045endifcmd(void)
1046{
1047
1048	if (cond == CANY) {
1049		printf(gettext("\"Endif\" without matching \"if\"\n"));
1050		return(1);
1051	}
1052	cond = CANY;
1053	return(0);
1054}
1055
1056/*
1057 * Set the list of alternate names.
1058 */
1059int
1060alternates(char **namelist)
1061{
1062	int c;
1063	char **ap, **ap2, *cp;
1064
1065	c = argcount(namelist) + 1;
1066	if (c == 1) {
1067		if (altnames == 0)
1068			return(0);
1069		for (ap = altnames; *ap; ap++)
1070			printf("%s ", *ap);
1071		printf("\n");
1072		return (0);
1073	}
1074	if (altnames != 0)
1075		free((char *)altnames);
1076	if ((altnames = (char **)
1077	    calloc((unsigned)c, sizeof (char *))) == NULL)
1078		panic("Failed to allocate memory");
1079	for (ap = namelist, ap2 = altnames; *ap; ap++, ap2++) {
1080		if ((cp = (char *)
1081		    calloc((unsigned)strlen(*ap) + 1, sizeof (char))) == NULL)
1082			panic("Failed to allocate memory");
1083		strcpy(cp, *ap);
1084		*ap2 = cp;
1085	}
1086	*ap2 = 0;
1087	return(0);
1088}
1089
1090/*
1091 * Figure out who to reply to.
1092 * Return the real sender in *f.
1093 */
1094static char *
1095replyto(struct message *mp, char **f)
1096{
1097	char *r, *rf;
1098
1099	if ((rf = skin(hfield("from", mp, addto)))==NOSTR)
1100		rf = skin(addto(NOSTR, nameof(mp)));
1101	if ((r = skin(hfield("reply-to", mp, addto)))==NOSTR)
1102		r = rf;
1103	if (f)
1104		*f = rf;
1105	return (r);
1106}
1107
1108/*
1109 * reply2sender - determine whether a "reply" command should reply to the
1110 *                sender of the messages, or to all the recipients of the
1111 *                message.
1112 *
1113 *                With the advent of POSIX.2 compliance, this has become
1114 *                a bit more complicated, and so should be done in one
1115 *                place, for all to use.
1116 */
1117
1118static int
1119reply2sender (void)
1120{
1121	int rep = (value("replyall") != NOSTR);
1122	int flp = (value("flipr") != NOSTR);
1123
1124	return((rep && !flp)|| (!rep && flp));
1125}
1126