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
shell(char * str)72 shell(char *str)
73 {
74 shell1(str);
75 printf("!\n");
76 return(0);
77 }
78
79 static int
shell1(char * str)80 shell1(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
119 int
120 #ifdef __cplusplus
dosh(char *)121 dosh(char *)
122 #else
123 /* ARGSUSED */
124 dosh(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 */
161 static int
bangexp(char * str)162 bangexp(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)) {
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
help(void)213 help(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
232 int
schdir(char * str)233 schdir(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
277 int
respond(int * msgvec)278 respond(int *msgvec)
279 {
280 if (reply2sender())
281 return(resp1(msgvec, 0));
282 else
283 return(Resp1(msgvec, 0));
284 }
285
286 int
followup(int * msgvec)287 followup(int *msgvec)
288 {
289 if (reply2sender())
290 return(resp1(msgvec, 1));
291 else
292 return(Resp1(msgvec, 1));
293 }
294
295 int
replyall(int * msgvec)296 replyall(int *msgvec)
297 {
298 return(resp1(msgvec, 0));
299 }
300
301 static int
resp1(int * msgvec,int useauthor)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
getrecf(char * buf,char * recfile,int useauthor,int sz_recfile)385 getrecf(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
437 static char *
reedit(char * subj)438 reedit(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
459 int
preserve(int * msgvec)460 preserve(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 */
482 int
unread(int msgvec[])483 unread(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
499 int
messize(int * msgvec)500 messize(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
519 int
rexit(int e)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
set(char ** arglist)539 set(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
588 int
unset(char ** arglist)589 unset(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
604 int
group(char ** argv)605 group(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
666 int
ungroup(char ** argv)667 ungroup(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
711 static void
sort(char ** list)712 sort(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 */
727 static int
diction(const void * a,const void * b)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
null(char *)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
file(char ** argv)754 file(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
798 static char *
getfilename(char * name,int * aedit)799 getfilename(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:
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
echo(char ** argv)876 echo(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
901 int
Respond(int * msgvec)902 Respond(int *msgvec)
903 {
904 if (reply2sender())
905 return(Resp1(msgvec, 0));
906 else
907 return(resp1(msgvec, 0));
908 }
909
910 int
Followup(int * msgvec)911 Followup(int *msgvec)
912 {
913 if (reply2sender())
914 return(Resp1(msgvec, 1));
915 else
916 return(resp1(msgvec, 1));
917 }
918
919 int
replysender(int * msgvec)920 replysender(int *msgvec)
921 {
922 return(Resp1(msgvec, 0));
923 }
924
925 static int
Resp1(int * msgvec,int useauthor)926 Resp1(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
971 int
ifcmd(char ** argv)972 ifcmd(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
1007 int
elsecmd(void)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
endifcmd(void)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
alternates(char ** namelist)1060 alternates(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 */
1094 static char *
replyto(struct message * mp,char ** f)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
reply2sender(void)1119 reply2sender (void)
1120 {
1121 int rep = (value("replyall") != NOSTR);
1122 int flp = (value("flipr") != NOSTR);
1123
1124 return((rep && !flp)|| (!rep && flp));
1125 }
1126