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
shell(char * str)74 shell(char *str)
75 {
76 shell1(str);
77 printf("!\n");
78 return(0);
79 }
80
81 static int
shell1(char * str)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
dosh(char *)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
bangexp(char * str)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
help(void)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
schdir(char * str)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
respond(int * msgvec)280 respond(int *msgvec)
281 {
282 if (reply2sender())
283 return(resp1(msgvec, 0));
284 else
285 return(Resp1(msgvec, 0));
286 }
287
288 int
followup(int * msgvec)289 followup(int *msgvec)
290 {
291 if (reply2sender())
292 return(resp1(msgvec, 1));
293 else
294 return(Resp1(msgvec, 1));
295 }
296
297 int
replyall(int * msgvec)298 replyall(int *msgvec)
299 {
300 return(resp1(msgvec, 0));
301 }
302
303 static int
resp1(int * msgvec,int useauthor)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
getrecf(char * buf,char * recfile,int useauthor,int sz_recfile)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 *
reedit(char * subj)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
preserve(int * msgvec)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
unread(int msgvec[])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
messize(int * msgvec)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
rexit(int e)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
set(char ** arglist)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
unset(char ** arglist)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
group(char ** argv)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
ungroup(char ** argv)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
sort(char ** list)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
diction(const void * a,const void * b)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
null(char *)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
file(char ** argv)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 *
getfilename(char * name,int * aedit)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
echo(register char ** argv)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
Respond(int * msgvec)904 Respond(int *msgvec)
905 {
906 if (reply2sender())
907 return(Resp1(msgvec, 0));
908 else
909 return(resp1(msgvec, 0));
910 }
911
912 int
Followup(int * msgvec)913 Followup(int *msgvec)
914 {
915 if (reply2sender())
916 return(Resp1(msgvec, 1));
917 else
918 return(resp1(msgvec, 1));
919 }
920
921 int
replysender(int * msgvec)922 replysender(int *msgvec)
923 {
924 return(Resp1(msgvec, 0));
925 }
926
927 static int
Resp1(int * msgvec,int useauthor)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
ifcmd(char ** argv)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
elsecmd(void)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
endifcmd(void)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
alternates(char ** namelist)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 *
replyto(struct message * mp,char ** f)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
reply2sender(void)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