]> git.cameronkatri.com Git - apple_cmds.git/blob - mail_cmds/mail/cmd3.c
libpcap-98.40.1
[apple_cmds.git] / mail_cmds / mail / cmd3.c
1 /*
2 * Copyright (c) 1980, 1993
3 * The Regents of the University of California. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34 #ifndef lint
35 #if 0
36 static char sccsid[] = "@(#)cmd3.c 8.2 (Berkeley) 4/20/95";
37 #endif
38 static const char rcsid[] =
39 "$FreeBSD: src/usr.bin/mail/cmd3.c,v 1.10 2002/06/30 05:25:06 obrien Exp $";
40 #endif /* not lint */
41
42 #include <sys/cdefs.h>
43
44 #include "rcv.h"
45 #include "extern.h"
46
47 /*
48 * Mail -- a mail program
49 *
50 * Still more user commands.
51 */
52
53 /*
54 * Process a shell escape by saving signals, ignoring signals,
55 * and forking a sh -c
56 */
57 int
58 shell(str)
59 char *str;
60 {
61 sig_t sigint = signal(SIGINT, SIG_IGN);
62 char *sh;
63 char cmd[BUFSIZ];
64
65 if (strlcpy(cmd, str, sizeof(cmd)) >= sizeof(cmd))
66 return (1);
67 if (bangexp(cmd, sizeof(cmd)) < 0)
68 return (1);
69 if ((sh = value("SHELL")) == NULL)
70 sh = _PATH_BSHELL;
71 (void)run_command(sh, 0, -1, -1, "-c", cmd, NULL);
72 (void)signal(SIGINT, sigint);
73 printf("!\n");
74 return (0);
75 }
76
77 /*
78 * Fork an interactive shell.
79 */
80 /*ARGSUSED*/
81 int
82 dosh(str)
83 char *str;
84 {
85 sig_t sigint = signal(SIGINT, SIG_IGN);
86 char *sh;
87
88 if ((sh = value("SHELL")) == NULL)
89 sh = _PATH_BSHELL;
90 (void)run_command(sh, 0, -1, -1, NULL, NULL, NULL);
91 (void)signal(SIGINT, sigint);
92 printf("\n");
93 return (0);
94 }
95
96 /*
97 * Expand the shell escape by expanding unescaped !'s into the
98 * last issued command where possible.
99 */
100 int
101 bangexp(str, strsize)
102 char *str;
103 size_t strsize;
104 {
105 char bangbuf[BUFSIZ];
106 static char lastbang[BUFSIZ];
107 char *cp, *cp2;
108 int n, changed = 0;
109
110 if (value("bang") == NULL)
111 return (0);
112 cp = str;
113 cp2 = bangbuf;
114 n = sizeof(bangbuf);
115 while (*cp != '\0') {
116 if (*cp == '!') {
117 if (n < strlen(lastbang)) {
118 overf:
119 printf("Command buffer overflow\n");
120 return (-1);
121 }
122 changed++;
123 if (strlcpy(cp2, lastbang, sizeof(bangbuf) - (cp2 - bangbuf))
124 >= sizeof(bangbuf) - (cp2 - bangbuf))
125 goto overf;
126 cp2 += strlen(lastbang);
127 n -= strlen(lastbang);
128 cp++;
129 continue;
130 }
131 if (*cp == '\\' && cp[1] == '!') {
132 if (--n <= 1)
133 goto overf;
134 *cp2++ = '!';
135 cp += 2;
136 changed++;
137 }
138 if (--n <= 1)
139 goto overf;
140 *cp2++ = *cp++;
141 }
142 *cp2 = 0;
143 if (changed) {
144 printf("!%s\n", bangbuf);
145 (void)fflush(stdout);
146 }
147 if (strlcpy(str, bangbuf, strsize) >= strsize)
148 goto overf;
149 if (strlcpy(lastbang, bangbuf, sizeof(lastbang)) >= sizeof(lastbang))
150 goto overf;
151 return (0);
152 }
153
154 /*
155 * Print out a nice help message from some file or another.
156 */
157
158 int
159 help()
160 {
161 int c;
162 FILE *f;
163
164 if ((f = Fopen(_PATH_HELP, "r")) == NULL) {
165 warn("%s", _PATH_HELP);
166 return (1);
167 }
168 while ((c = getc(f)) != EOF)
169 putchar(c);
170 (void)Fclose(f);
171 return (0);
172 }
173
174 /*
175 * Change user's working directory.
176 */
177 int
178 schdir(arglist)
179 char **arglist;
180 {
181 char *cp;
182
183 if (*arglist == NULL) {
184 if (homedir == NULL)
185 return (1);
186 cp = homedir;
187 } else
188 if ((cp = expand(*arglist)) == NULL)
189 return (1);
190 if (chdir(cp) < 0) {
191 warn("%s", cp);
192 return (1);
193 }
194 return (0);
195 }
196
197 char *
198 getauthor(str)
199 char * str;
200 {
201 char *atsign;
202 if (str == NULL) {
203 return(NULL);
204 }
205 atsign = strchr(str,'@');
206 if (atsign != NULL) {
207 str = savestr(str); /* copy to modify */
208 atsign = strchr(str,'@');
209 *atsign = '\0';
210 }
211 /*
212 printf("file name=%s\n", str);
213 */
214 return (str);
215 }
216
217 int
218 followup(msgvec)
219 int *msgvec;
220 {
221 int res;
222 int reset = 0;
223 if (value("recordrecip") == NULL) {
224 assign("recordrecip", "");
225 reset = 1;
226 }
227 res = respond(msgvec);
228 if (reset) {
229 char *alist[2];
230 alist[0] = "recordrecip";
231 alist[1] = NULL;
232 unset(alist);
233 }
234 return (res);
235 }
236
237 int
238 Capfollowup(msgvec)
239 int *msgvec;
240 {
241 int res;
242 int reset = 0;
243 if (value("recordrecip") == NULL) {
244 assign("recordrecip", "");
245 reset = 1;
246 }
247 res = Respond(msgvec);
248 if (reset) {
249 char *alist[2];
250 alist[0] = "recordrecip";
251 alist[1] = NULL;
252 unset(alist);
253 }
254 return (res);
255 }
256
257 int
258 respond(msgvec)
259 int *msgvec;
260 {
261 if (value("Replyall") == NULL && value("flipr") == NULL)
262 return (dorespond(msgvec));
263 else
264 return (doRespond(msgvec));
265 }
266
267 /*
268 * Reply to a list of messages. Extract each name from the
269 * message header and send them off to mail1()
270 */
271 int
272 dorespond(msgvec)
273 int *msgvec;
274 {
275 struct message *mp;
276 char *cp, *rcv, *replyto;
277 char **ap;
278 struct name *np;
279 struct header head;
280
281 if (msgvec[1] != 0) {
282 printf("Sorry, can't reply to multiple messages at once\n");
283 return (1);
284 }
285 mp = &message[msgvec[0] - 1];
286 touch(mp);
287 dot = mp;
288 if ((rcv = skin(hfield("from", mp))) == NULL)
289 rcv = skin(nameof(mp, 1));
290 if ((replyto = skin(hfield("reply-to", mp))) != NULL)
291 np = extract(replyto, GTO);
292 else if ((cp = skin(hfield("to", mp))) != NULL)
293 np = extract(cp, GTO);
294 else
295 np = NULL;
296 np = elide(np);
297 /*
298 * Delete my name from the reply list,
299 * and with it, all my alternate names.
300 */
301 np = delname(np, myname);
302 if (altnames)
303 for (ap = altnames; *ap != NULL; ap++)
304 np = delname(np, *ap);
305 if (np != NULL && replyto == NULL)
306 np = cat(np, extract(rcv, GTO));
307 else if (np == NULL) {
308 if (replyto != NULL)
309 printf("Empty reply-to field -- replying to author\n");
310 np = extract(rcv, GTO);
311 }
312 head.h_to = np;
313 if ((head.h_subject = hfield("subject", mp)) == NULL)
314 head.h_subject = hfield("subj", mp);
315 head.h_subject = reedit(head.h_subject);
316 if (replyto == NULL && (cp = skin(hfield("cc", mp))) != NULL) {
317 np = elide(extract(cp, GCC));
318 np = delname(np, myname);
319 if (altnames != 0)
320 for (ap = altnames; *ap != NULL; ap++)
321 np = delname(np, *ap);
322 head.h_cc = np;
323 } else
324 head.h_cc = NULL;
325 head.h_bcc = NULL;
326 head.h_smopts = NULL;
327 head.h_replyto = value("REPLYTO");
328 head.h_inreplyto = skin(hfield("message-id", mp));
329 mail1(&head, 1);
330 return (0);
331 }
332
333 /*
334 * Modify the subject we are replying to to begin with Re: if
335 * it does not already.
336 */
337 char *
338 reedit(subj)
339 char *subj;
340 {
341 char *newsubj;
342
343 if (subj == NULL)
344 return (NULL);
345 if ((subj[0] == 'r' || subj[0] == 'R') &&
346 (subj[1] == 'e' || subj[1] == 'E') &&
347 subj[2] == ':')
348 return (subj);
349 newsubj = salloc(strlen(subj) + 5);
350 sprintf(newsubj, "Re: %s", subj);
351 return (newsubj);
352 }
353
354 /*
355 * Preserve the named messages, so that they will be sent
356 * back to the system mailbox.
357 */
358 int
359 preserve(msgvec)
360 int *msgvec;
361 {
362 int *ip, mesg;
363 struct message *mp;
364
365 if (edit) {
366 printf("Cannot \"preserve\" in edit mode\n");
367 return (1);
368 }
369 for (ip = msgvec; *ip != 0; ip++) {
370 mesg = *ip;
371 mp = &message[mesg-1];
372 mp->m_flag |= MPRESERVE;
373 mp->m_flag &= ~MBOX;
374 dot = mp;
375 }
376 return (0);
377 }
378
379 /*
380 * Mark all given messages as unread.
381 */
382 int
383 unread(msgvec)
384 int msgvec[];
385 {
386 int *ip;
387
388 for (ip = msgvec; *ip != 0; ip++) {
389 dot = &message[*ip-1];
390 dot->m_flag &= ~(MREAD|MTOUCH);
391 dot->m_flag |= MSTATUS;
392 }
393 return (0);
394 }
395
396 /*
397 * Print the size of each message.
398 */
399 int
400 messize(msgvec)
401 int *msgvec;
402 {
403 struct message *mp;
404 int *ip, mesg;
405
406 for (ip = msgvec; *ip != 0; ip++) {
407 mesg = *ip;
408 mp = &message[mesg-1];
409 printf("%d: %ld/%ld\n", mesg, mp->m_lines, mp->m_size);
410 }
411 return (0);
412 }
413
414 /*
415 * Quit quickly. If we are sourcing, just pop the input level
416 * by returning an error.
417 */
418 int
419 rexit(e)
420 int e;
421 {
422 if (sourcing)
423 return (1);
424 exit(0);
425 /*NOTREACHED*/
426 }
427
428 /*
429 * Set or display a variable value. Syntax is similar to that
430 * of csh.
431 */
432 int
433 set(arglist)
434 char **arglist;
435 {
436 struct var *vp;
437 char *cp, *cp2;
438 char varbuf[BUFSIZ], **ap, **p;
439 int errs, h, s;
440 int stringlength;
441
442 if (*arglist == NULL) {
443 char * printval;
444 for (h = 0, s = 1; h < HSHSIZE; h++)
445 for (vp = variables[h]; vp != NULL; vp = vp->v_link)
446 s++;
447 ap = (char **)salloc(s * sizeof(*ap));
448 for (h = 0, p = ap; h < HSHSIZE; h++)
449 for (vp = variables[h]; vp != NULL; vp = vp->v_link)
450 *p++ = vp->v_name;
451 *p = NULL;
452 sort(ap);
453 for (p = ap; *p != NULL; p++) {
454 printf("%s", *p);
455 printval = value(*p);
456 if (printval) {
457 if (printval[0]!='\0') printf("\t\"%s\"", printval);
458 }
459 printf("\n");
460 }
461 return (0);
462 }
463 errs = 0;
464 for (ap = arglist; *ap != NULL; ap++) {
465 cp = *ap;
466 stringlength = strlen(cp);
467 if (stringlength > 2 && cp[0]=='n' && cp[1]=='o') {
468 /* set no<var> means unset <var> */
469 char *alist[2];
470 alist[0] = cp+2;
471 alist[1] = NULL;
472 errs += unset(alist);
473 continue;
474 }
475 if (stringlength == 3 && cp[0]=='a' && cp[1]=='s' && cp[2]=='k') {
476 /* synonym: must convert into "asksub" */
477 cp = "asksub";
478 }
479 cp2 = varbuf;
480 while (cp2 < varbuf + sizeof(varbuf) - 1 && *cp != '=' && *cp != '\0')
481 *cp2++ = *cp++;
482 *cp2 = '\0';
483 if (*cp == '\0')
484 cp = "";
485 else
486 cp++;
487 if (equal(varbuf, "")) {
488 printf("Non-null variable name required\n");
489 errs++;
490 continue;
491 }
492 assign(varbuf, cp);
493 }
494 return (errs);
495 }
496
497 /*
498 * Unset a bunch of variable values.
499 */
500 int
501 unset(arglist)
502 char **arglist;
503 {
504 struct var *vp, *vp2;
505 int errs, h;
506 char **ap;
507 int stringlength;
508
509 errs = 0;
510 for (ap = arglist; *ap != NULL; ap++) {
511 stringlength = strlen(*ap);
512 if (stringlength > 2 && (*ap)[0]=='n' && (*ap)[1]=='o') {
513 /* no<var> is not in db - only <var> can be in table */
514 printf("\"%s\": variable cannot be unset\n", *ap);
515 errs++;
516 continue;
517 }
518 if (stringlength == 3 && (*ap)[0]=='a' && (*ap)[1]=='s' && (*ap)[2]=='k') {
519 /* synonym: must convert into "asksub" */
520 *ap = "asksub";
521 }
522 if ((vp2 = lookup(*ap)) == NULL) {
523 if (getenv(*ap)) {
524 unsetenv(*ap);
525 if (debug)
526 fprintf(stderr,"WARNING: unsetting environment variable: %s\n", *ap);
527 } else if (!sourcing) {
528 printf("\"%s\": undefined variable\n", *ap);
529 errs++;
530 }
531 continue;
532 }
533 h = hash(*ap);
534 if (vp2 == variables[h]) {
535 variables[h] = variables[h]->v_link;
536 v_free(vp2->v_name);
537 v_free(vp2->v_value);
538 (void)free(vp2);
539 continue;
540 }
541 for (vp = variables[h]; vp->v_link != vp2; vp = vp->v_link)
542 ;
543 vp->v_link = vp2->v_link;
544 v_free(vp2->v_name);
545 v_free(vp2->v_value);
546 (void)free(vp2);
547 }
548 return (errs);
549 }
550
551 /*
552 * Put add users to a group.
553 */
554 int
555 group(argv)
556 char **argv;
557 {
558 struct grouphead *gh;
559 struct group *gp;
560 char **ap, *gname, **p;
561 int h, s;
562
563 if (*argv == NULL) {
564 for (h = 0, s = 1; h < HSHSIZE; h++)
565 for (gh = groups[h]; gh != NULL; gh = gh->g_link)
566 s++;
567 ap = (char **)salloc(s * sizeof(*ap));
568 for (h = 0, p = ap; h < HSHSIZE; h++)
569 for (gh = groups[h]; gh != NULL; gh = gh->g_link)
570 *p++ = gh->g_name;
571 *p = NULL;
572 sort(ap);
573 for (p = ap; *p != NULL; p++)
574 printgroup(*p);
575 return (0);
576 }
577 if (argv[1] == NULL) {
578 printgroup(*argv);
579 return (0);
580 }
581 gname = *argv;
582 h = hash(gname);
583 if ((gh = findgroup(gname)) == NULL) {
584 gh = calloc(sizeof(*gh), 1);
585 gh->g_name = vcopy(gname);
586 gh->g_list = NULL;
587 gh->g_link = groups[h];
588 groups[h] = gh;
589 }
590
591 /*
592 * Insert names from the command list into the group.
593 * Who cares if there are duplicates? They get tossed
594 * later anyway.
595 */
596
597 for (ap = argv+1; *ap != NULL; ap++) {
598 gp = calloc(sizeof(*gp), 1);
599 gp->ge_name = vcopy(*ap);
600 gp->ge_link = gh->g_list;
601 gh->g_list = gp;
602 }
603 return (0);
604 }
605
606 /*
607 * Sort the passed string vecotor into ascending dictionary
608 * order.
609 */
610 void
611 sort(list)
612 char **list;
613 {
614 char **ap;
615
616 for (ap = list; *ap != NULL; ap++)
617 ;
618 if (ap-list < 2)
619 return;
620 qsort(list, ap-list, sizeof(*list), diction);
621 }
622
623 /*
624 * Do a dictionary order comparison of the arguments from
625 * qsort.
626 */
627 int
628 diction(a, b)
629 const void *a, *b;
630 {
631 return (strcmp(*(const char **)a, *(const char **)b));
632 }
633
634 /*
635 * The do nothing command for comments.
636 */
637
638 /*ARGSUSED*/
639 int
640 null(e)
641 int e;
642 {
643 return (0);
644 }
645
646 /*
647 * Change to another file. With no argument, print information about
648 * the current file.
649 */
650 int
651 file(argv)
652 char **argv;
653 {
654
655 if (argv[0] == NULL) {
656 newfileinfo(0);
657 return (0);
658 }
659 if (setfile(*argv) < 0)
660 return (1);
661 announce();
662 return (0);
663 }
664
665 /*
666 * Expand file names like echo
667 */
668 int
669 echo(argv)
670 char **argv;
671 {
672 char **ap, *cp;
673
674 for (ap = argv; *ap != NULL; ap++) {
675 cp = *ap;
676 if ((cp = expand(cp)) != NULL) {
677 if (ap != argv)
678 printf(" ");
679 printf("%s", cp);
680 }
681 }
682 printf("\n");
683 return (0);
684 }
685
686 int
687 Respond(msgvec)
688 int *msgvec;
689 {
690 if (value("Replyall") == NULL && value("flipr") == NULL)
691 return (doRespond(msgvec));
692 else
693 return (dorespond(msgvec));
694 }
695
696 /*
697 * Reply to a series of messages by simply mailing to the senders
698 * and not messing around with the To: and Cc: lists as in normal
699 * reply.
700 */
701 int
702 doRespond(msgvec)
703 int msgvec[];
704 {
705 struct header head;
706 struct message *mp;
707 int *ap;
708 char *cp, *mid = NULL;
709
710 head.h_to = NULL;
711 for (ap = msgvec; *ap != 0; ap++) {
712 mp = &message[*ap - 1];
713 touch(mp);
714 dot = mp;
715 if ((cp = skin(hfield("from", mp))) == NULL)
716 cp = skin(nameof(mp, 2));
717 head.h_to = cat(head.h_to, extract(cp, GTO));
718 mid = skin(hfield("message-id", mp));
719 }
720 if (head.h_to == NULL)
721 return (0);
722 mp = &message[msgvec[0] - 1];
723 if ((head.h_subject = hfield("subject", mp)) == NULL)
724 head.h_subject = hfield("subj", mp);
725 head.h_subject = reedit(head.h_subject);
726 head.h_cc = NULL;
727 head.h_bcc = NULL;
728 head.h_smopts = NULL;
729 head.h_replyto = value("REPLYTO");
730 head.h_inreplyto = mid;
731 mail1(&head, 1);
732 return (0);
733 }
734
735 /*
736 * Conditional commands. These allow one to parameterize one's
737 * .mailrc and do some things if sending, others if receiving.
738 */
739 int
740 ifcmd(argv)
741 char **argv;
742 {
743 char *cp;
744
745 if (cond != CANY) {
746 printf("Illegal nested \"if\"\n");
747 return (1);
748 }
749 cond = CANY;
750 cp = argv[0];
751 switch (*cp) {
752 case 'r': case 'R':
753 cond = CRCV;
754 break;
755
756 case 's': case 'S':
757 cond = CSEND;
758 break;
759
760 default:
761 printf("Unrecognized if-keyword: \"%s\"\n", cp);
762 return (1);
763 }
764 return (0);
765 }
766
767 /*
768 * Implement 'else'. This is pretty simple -- we just
769 * flip over the conditional flag.
770 */
771 int
772 elsecmd()
773 {
774
775 switch (cond) {
776 case CANY:
777 printf("\"Else\" without matching \"if\"\n");
778 return (1);
779
780 case CSEND:
781 cond = CRCV;
782 break;
783
784 case CRCV:
785 cond = CSEND;
786 break;
787
788 default:
789 printf("Mail's idea of conditions is screwed up\n");
790 cond = CANY;
791 break;
792 }
793 return (0);
794 }
795
796 /*
797 * End of if statement. Just set cond back to anything.
798 */
799 int
800 endifcmd()
801 {
802
803 if (cond == CANY) {
804 printf("\"Endif\" without matching \"if\"\n");
805 return (1);
806 }
807 cond = CANY;
808 return (0);
809 }
810
811 /*
812 * Set the list of alternate names.
813 */
814 int
815 alternates(namelist)
816 char **namelist;
817 {
818 int c;
819 char **ap, **ap2, *cp;
820
821 c = argcount(namelist) + 1;
822 if (c == 1) {
823 if (altnames == 0)
824 return (0);
825 for (ap = altnames; *ap != NULL; ap++)
826 printf("%s ", *ap);
827 printf("\n");
828 return (0);
829 }
830 if (altnames != 0)
831 (void)free(altnames);
832 altnames = calloc((unsigned)c, sizeof(char *));
833 for (ap = namelist, ap2 = altnames; *ap != NULL; ap++, ap2++) {
834 cp = calloc((unsigned)strlen(*ap) + 1, sizeof(char));
835 strcpy(cp, *ap);
836 *ap2 = cp;
837 }
838 *ap2 = 0;
839 return (0);
840 }