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