xref: /illumos-gate/usr/src/cmd/mailx/cmd3.c (revision 4c28a617e3922d92a58e813a5b955eb526b9c386)
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 
51 static int	bangexp(char *str);
52 static int	diction(const void *a, const void *b);
53 static char	*getfilename(char *name, int *aedit);
54 static int	resp1(int *msgvec, int useauthor);
55 static int	Resp1(int *msgvec, int useauthor);
56 static char	*reedit(char *subj);
57 static int	shell1(char *str);
58 static void	sort(char **list);
59 static char	*replyto(struct message *mp, char **f);
60 static int	reply2sender(void);
61 
62 static char	prevfile[PATHSIZE];
63 static char	origprevfile[PATHSIZE];
64 static char	lastbang[BUFSIZ];
65 
66 /*
67  * Process a shell escape by saving signals, ignoring signals,
68  * and forking a sh -c
69  */
70 
71 int
72 shell(char *str)
73 {
74 	shell1(str);
75 	printf("!\n");
76 	return(0);
77 }
78 
79 static int
80 shell1(char *str)
81 {
82 	void (*sig[2])(int);
83 	register int t;
84 	register 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 
119 int
120 #ifdef	__cplusplus
121 dosh(char *)
122 #else
123 /* ARGSUSED */
124 dosh(char *s)
125 #endif
126 {
127 	void (*sig[2])(int);
128 	register int t;
129 	register 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  */
161 static int
162 bangexp(char *str)
163 {
164 	char bangbuf[BUFSIZ];
165 	register char *cp, *cp2;
166 	register 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)) {
176 overf:
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 
212 int
213 help(void)
214 {
215 	int c;
216 	register 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 
232 int
233 schdir(char *str)
234 {
235 	register 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 
277 int
278 respond(int *msgvec)
279 {
280 	if (reply2sender())
281 		return(resp1(msgvec, 0));
282 	else
283 		return(Resp1(msgvec, 0));
284 }
285 
286 int
287 followup(int *msgvec)
288 {
289 	if (reply2sender())
290 		return(resp1(msgvec, 1));
291 	else
292 		return(Resp1(msgvec, 1));
293 }
294 
295 int
296 replyall(int *msgvec)
297 {
298 	return(resp1(msgvec, 0));
299 }
300 
301 static int
302 resp1(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 
384 void
385 getrecf(char *buf, char *recfile, int useauthor, int sz_recfile)
386 {
387 	register char *bp, *cp;
388 	register char *recf = recfile;
389 	register 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 
437 static char *
438 reedit(char *subj)
439 {
440 	char sbuf[10];
441 	register 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 
459 int
460 preserve(int *msgvec)
461 {
462 	register struct message *mp;
463 	register 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 != NULL; 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  */
482 int
483 unread(int msgvec[])
484 {
485 	register int *ip;
486 
487 	for (ip = msgvec; *ip != NULL; 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 
499 int
500 messize(int *msgvec)
501 {
502 	register struct message *mp;
503 	register int *ip, mesg;
504 
505 	for (ip = msgvec; *ip != NULL; 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 
519 int
520 rexit(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 
538 int
539 set(char **arglist)
540 {
541 	register struct var *vp;
542 	register 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 
588 int
589 unset(char **arglist)
590 {
591 	register int errs;
592 	register 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 
604 int
605 group(char **argv)
606 {
607 	register struct grouphead *gh;
608 	register struct mgroup *gp;
609 	register 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 
666 int
667 ungroup(char **argv)
668 {
669 	register struct grouphead *gh, **ghp;
670 	register struct mgroup *gp, *gpnext;
671 	register 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 
711 static void
712 sort(char **list)
713 {
714 	register 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  */
727 static int
728 diction(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 
737 int
738 #ifdef	__cplusplus
739 null(char *)
740 #else
741 /* ARGSUSED */
742 null(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  */
753 int
754 file(char **argv)
755 {
756 	register 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 
798 static char *
799 getfilename(char *name, int *aedit)
800 {
801 	register 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:
856 regular:
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 
875 int
876 echo(register char **argv)
877 {
878 	register 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 
901 int
902 Respond(int *msgvec)
903 {
904 	if (reply2sender())
905 		return(Resp1(msgvec, 0));
906 	else
907 		return(resp1(msgvec, 0));
908 }
909 
910 int
911 Followup(int *msgvec)
912 {
913 	if (reply2sender())
914 		return(Resp1(msgvec, 1));
915 	else
916 		return(resp1(msgvec, 1));
917 }
918 
919 int
920 replysender(int *msgvec)
921 {
922 	return(Resp1(msgvec, 0));
923 }
924 
925 static int
926 Resp1(int *msgvec, int useauthor)
927 {
928 	struct header head;
929 	struct message *mp;
930 	register int s, *ap;
931 	register 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 
971 int
972 ifcmd(char **argv)
973 {
974 	register 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 
1007 int
1008 elsecmd(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 
1044 int
1045 endifcmd(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  */
1059 int
1060 alternates(char **namelist)
1061 {
1062 	register int c;
1063 	register 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  */
1094 static char *
1095 replyto(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 
1118 static int
1119 reply2sender (void)
1120 {
1121 	register int rep = (value("replyall") != NOSTR);
1122 	register int flp = (value("flipr") != NOSTR);
1123 
1124 	return((rep && !flp)|| (!rep && flp));
1125 }
1126