removed support for -r, -h, and rmail
[unix-history] / usr / src / usr.bin / mail / cmd3.c
CommitLineData
d0aeaf5a
DF
1/*
2 * Copyright (c) 1980 Regents of the University of California.
0c5f72fb
KB
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms are permitted
6 * provided that this notice is preserved and that due credit is given
7 * to the University of California at Berkeley. The name of the University
8 * may not be used to endorse or promote products derived from this
9 * software without specific prior written permission. This software
10 * is provided ``as is'' without express or implied warranty.
d0aeaf5a
DF
11 */
12
0c5f72fb 13#ifdef notdef
09875e8e 14static char sccsid[] = "@(#)cmd3.c 5.10 (Berkeley) %G%";
0c5f72fb 15#endif /* notdef */
ca6bb034
KS
16
17#include "rcv.h"
18#include <sys/stat.h>
828615a1 19#include <sys/wait.h>
ca6bb034
KS
20
21/*
22 * Mail -- a mail program
23 *
24 * Still more user commands.
25 */
26
ca6bb034
KS
27/*
28 * Process a shell escape by saving signals, ignoring signals,
29 * and forking a sh -c
30 */
31
32shell(str)
33 char *str;
34{
828615a1
EW
35 int (*sigint)(), (*sigquit)();
36 union wait stat;
ca6bb034
KS
37 register int t;
38 char *Shell;
e8adee6e 39 char cmd[BUFSIZ];
ca6bb034 40
e8adee6e
KS
41 strcpy(cmd, str);
42 if (bangexp(cmd) < 0)
43 return(-1);
ca6bb034
KS
44 if ((Shell = value("SHELL")) == NOSTR)
45 Shell = SHELL;
828615a1
EW
46 sigint = signal(SIGINT, SIG_IGN);
47 sigquit = signal(SIGQUIT, SIG_IGN);
ca6bb034
KS
48 t = vfork();
49 if (t == 0) {
828615a1
EW
50 if (sigint != SIG_IGN)
51 signal(SIGINT, SIG_DFL);
52 if (sigquit != SIG_IGN)
53 signal(SIGQUIT, SIG_DFL);
397029be 54 execl(Shell, Shell, "-c", cmd, (char *)0);
ca6bb034
KS
55 perror(Shell);
56 _exit(1);
57 }
828615a1 58 while (wait(&stat) != t)
ca6bb034
KS
59 ;
60 if (t == -1)
61 perror("fork");
828615a1
EW
62 signal(SIGINT, sigint);
63 signal(SIGQUIT, sigquit);
ca6bb034
KS
64 printf("!\n");
65 return(0);
66}
67
68/*
69 * Fork an interactive shell.
70 */
71
828615a1 72/*ARGSUSED*/
ca6bb034
KS
73dosh(str)
74 char *str;
75{
828615a1
EW
76 int (*sigint)(), (*sigquit)();
77 union wait stat;
ca6bb034
KS
78 register int t;
79 char *Shell;
828615a1 80
ca6bb034
KS
81 if ((Shell = value("SHELL")) == NOSTR)
82 Shell = SHELL;
828615a1
EW
83 sigint = signal(SIGINT, SIG_IGN);
84 sigquit = signal(SIGQUIT, SIG_IGN);
ca6bb034
KS
85 t = vfork();
86 if (t == 0) {
828615a1
EW
87 if (sigint != SIG_IGN)
88 signal(SIGINT, SIG_DFL);
89 if (sigquit != SIG_IGN)
90 signal(SIGQUIT, SIG_DFL);
397029be 91 execl(Shell, Shell, (char *)0);
ca6bb034
KS
92 perror(Shell);
93 _exit(1);
94 }
828615a1 95 while (wait(&stat) != t)
ca6bb034
KS
96 ;
97 if (t == -1)
98 perror("fork");
828615a1
EW
99 signal(SIGINT, sigint);
100 signal(SIGQUIT, sigquit);
ca6bb034
KS
101 putchar('\n');
102 return(0);
103}
104
e8adee6e
KS
105/*
106 * Expand the shell escape by expanding unescaped !'s into the
107 * last issued command where possible.
108 */
109
110char lastbang[128];
111
112bangexp(str)
113 char *str;
114{
115 char bangbuf[BUFSIZ];
116 register char *cp, *cp2;
117 register int n;
118 int changed = 0;
119
120 cp = str;
121 cp2 = bangbuf;
122 n = BUFSIZ;
123 while (*cp) {
124 if (*cp == '!') {
125 if (n < strlen(lastbang)) {
126overf:
127 printf("Command buffer overflow\n");
128 return(-1);
129 }
130 changed++;
131 strcpy(cp2, lastbang);
132 cp2 += strlen(lastbang);
133 n -= strlen(lastbang);
134 cp++;
135 continue;
136 }
137 if (*cp == '\\' && cp[1] == '!') {
138 if (--n <= 1)
139 goto overf;
140 *cp2++ = '!';
141 cp += 2;
142 changed++;
143 }
144 if (--n <= 1)
145 goto overf;
146 *cp2++ = *cp++;
147 }
148 *cp2 = 0;
149 if (changed) {
150 printf("!%s\n", bangbuf);
151 fflush(stdout);
152 }
153 strcpy(str, bangbuf);
154 strncpy(lastbang, bangbuf, 128);
155 lastbang[127] = 0;
156 return(0);
157}
158
ca6bb034
KS
159/*
160 * Print out a nice help message from some file or another.
161 */
162
163help()
164{
165 register c;
166 register FILE *f;
167
168 if ((f = fopen(HELPFILE, "r")) == NULL) {
7949d400 169 perror(HELPFILE);
ca6bb034
KS
170 return(1);
171 }
172 while ((c = getc(f)) != EOF)
173 putchar(c);
174 fclose(f);
175 return(0);
176}
177
178/*
179 * Change user's working directory.
180 */
181
182schdir(str)
183 char *str;
184{
185 register char *cp;
186
187 for (cp = str; *cp == ' '; cp++)
188 ;
189 if (*cp == '\0')
190 cp = homedir;
191 else
192 if ((cp = expand(cp)) == NOSTR)
193 return(1);
194 if (chdir(cp) < 0) {
195 perror(cp);
196 return(1);
197 }
198 return(0);
199}
200
397029be
S
201respond(msgvec)
202 int *msgvec;
203{
204 if (value("Replyall") == NOSTR)
205 return (_respond(msgvec));
206 else
207 return (_Respond(msgvec));
208}
209
ca6bb034
KS
210/*
211 * Reply to a list of messages. Extract each name from the
212 * message header and send them off to mail1()
213 */
214
397029be 215_respond(msgvec)
ca6bb034
KS
216 int *msgvec;
217{
218 struct message *mp;
828615a1 219 char *cp, *rcv, *replyto;
7949d400 220 char buf[2 * LINESIZE], **ap;
ca6bb034
KS
221 struct name *np;
222 struct header head;
ca6bb034
KS
223
224 if (msgvec[1] != 0) {
225 printf("Sorry, can't reply to multiple messages at once\n");
226 return(1);
227 }
228 mp = &message[msgvec[0] - 1];
229 dot = mp;
a0bc099a 230 rcv = NOSTR;
bdad82b3 231 cp = skin(nameof(mp, 1));
a0bc099a
CL
232 if (cp != NOSTR)
233 rcv = cp;
bdad82b3 234 cp = skin(hfield("from", mp));
a0bc099a
CL
235 if (cp != NOSTR)
236 rcv = cp;
35bddd51 237 replyto = skin(hfield("reply-to", mp));
ca6bb034 238 strcpy(buf, "");
52984be9
KS
239 if (replyto != NOSTR)
240 strcpy(buf, replyto);
241 else {
bdad82b3 242 cp = skin(hfield("to", mp));
52984be9
KS
243 if (cp != NOSTR)
244 strcpy(buf, cp);
245 }
ca6bb034 246 np = elide(extract(buf, GTO));
1c04ff22
KS
247 /*
248 * Delete my name from the reply list,
249 * and with it, all my alternate names.
250 */
7949d400
CL
251 np = delname(np, myname, icequal);
252 if (altnames)
1c04ff22 253 for (ap = altnames; *ap; ap++)
7949d400 254 np = delname(np, *ap, icequal);
ca6bb034 255 head.h_seq = 1;
bdad82b3 256 cp = detract(np, 0);
52984be9 257 if (cp != NOSTR && replyto == NOSTR) {
ca6bb034 258 strcpy(buf, cp);
bdad82b3 259 strcat(buf, " ");
ca6bb034
KS
260 strcat(buf, rcv);
261 }
52984be9
KS
262 else {
263 if (cp == NOSTR && replyto != NOSTR)
264 printf("Empty reply-to field -- replying to author\n");
265 if (cp == NOSTR)
266 strcpy(buf, rcv);
267 else
268 strcpy(buf, cp);
269 }
ca6bb034
KS
270 head.h_to = buf;
271 head.h_subject = hfield("subject", mp);
272 if (head.h_subject == NOSTR)
273 head.h_subject = hfield("subj", mp);
fc8872bb 274 head.h_subject = reedit(head.h_subject);
ca6bb034 275 head.h_cc = NOSTR;
52984be9 276 if (replyto == NOSTR) {
828615a1 277 cp = skin(hfield("cc", mp));
52984be9
KS
278 if (cp != NOSTR) {
279 np = elide(extract(cp, GCC));
7949d400 280 np = delname(np, myname, icequal);
2a8a6697
KS
281 if (altnames != 0)
282 for (ap = altnames; *ap; ap++)
7949d400 283 np = delname(np, *ap, icequal);
52984be9
KS
284 head.h_cc = detract(np, 0);
285 }
ca6bb034
KS
286 }
287 head.h_bcc = NOSTR;
790344bc 288 head.h_smopts = NOSTR;
ca6bb034
KS
289 mail1(&head);
290 return(0);
291}
292
fc8872bb
KS
293/*
294 * Modify the subject we are replying to to begin with Re: if
295 * it does not already.
296 */
297
298char *
299reedit(subj)
300 char *subj;
301{
302 char sbuf[10];
303 register char *newsubj;
304
305 if (subj == NOSTR)
306 return(NOSTR);
307 strncpy(sbuf, subj, 3);
308 sbuf[3] = 0;
309 if (icequal(sbuf, "re:"))
310 return(subj);
311 newsubj = salloc(strlen(subj) + 6);
828615a1 312 sprintf(newsubj, "Re: %s", subj);
fc8872bb
KS
313 return(newsubj);
314}
315
ca6bb034
KS
316/*
317 * Preserve the named messages, so that they will be sent
318 * back to the system mailbox.
319 */
320
321preserve(msgvec)
322 int *msgvec;
323{
324 register struct message *mp;
325 register int *ip, mesg;
326
327 if (edit) {
328 printf("Cannot \"preserve\" in edit mode\n");
329 return(1);
330 }
331 for (ip = msgvec; *ip != NULL; ip++) {
332 mesg = *ip;
333 mp = &message[mesg-1];
334 mp->m_flag |= MPRESERVE;
8fe58892 335 mp->m_flag &= ~MBOX;
ca6bb034
KS
336 dot = mp;
337 }
338 return(0);
339}
340
397029be
S
341/*
342 * Mark all given messages as unread.
343 */
344unread(msgvec)
345 int msgvec[];
346{
347 register int *ip;
348
349 for (ip = msgvec; *ip != NULL; ip++) {
350 dot = &message[*ip-1];
351 dot->m_flag &= ~(MREAD|MTOUCH);
352 dot->m_flag |= MSTATUS;
353 }
354 return(0);
355}
356
ca6bb034
KS
357/*
358 * Print the size of each message.
359 */
360
361messize(msgvec)
362 int *msgvec;
363{
364 register struct message *mp;
365 register int *ip, mesg;
366
367 for (ip = msgvec; *ip != NULL; ip++) {
368 mesg = *ip;
369 mp = &message[mesg-1];
397029be 370 printf("%d: %d/%ld\n", mesg, mp->m_lines, mp->m_size);
ca6bb034
KS
371 }
372 return(0);
373}
374
375/*
376 * Quit quickly. If we are sourcing, just pop the input level
377 * by returning an error.
378 */
379
380rexit(e)
381{
382 if (sourcing)
383 return(1);
384 exit(e);
828615a1 385 /*NOTREACHED*/
ca6bb034
KS
386}
387
388/*
389 * Set or display a variable value. Syntax is similar to that
390 * of csh.
391 */
392
393set(arglist)
394 char **arglist;
395{
396 register struct var *vp;
397 register char *cp, *cp2;
398 char varbuf[BUFSIZ], **ap, **p;
399 int errs, h, s;
400
401 if (argcount(arglist) == 0) {
402 for (h = 0, s = 1; h < HSHSIZE; h++)
403 for (vp = variables[h]; vp != NOVAR; vp = vp->v_link)
404 s++;
405 ap = (char **) salloc(s * sizeof *ap);
406 for (h = 0, p = ap; h < HSHSIZE; h++)
407 for (vp = variables[h]; vp != NOVAR; vp = vp->v_link)
408 *p++ = vp->v_name;
409 *p = NOSTR;
410 sort(ap);
411 for (p = ap; *p != NOSTR; p++)
412 printf("%s\t%s\n", *p, value(*p));
413 return(0);
414 }
415 errs = 0;
416 for (ap = arglist; *ap != NOSTR; ap++) {
417 cp = *ap;
418 cp2 = varbuf;
419 while (*cp != '=' && *cp != '\0')
420 *cp2++ = *cp++;
421 *cp2 = '\0';
422 if (*cp == '\0')
423 cp = "";
424 else
425 cp++;
426 if (equal(varbuf, "")) {
427 printf("Non-null variable name required\n");
428 errs++;
429 continue;
430 }
431 assign(varbuf, cp);
432 }
433 return(errs);
434}
435
436/*
437 * Unset a bunch of variable values.
438 */
439
440unset(arglist)
441 char **arglist;
442{
443 register struct var *vp, *vp2;
ca6bb034
KS
444 int errs, h;
445 char **ap;
446
447 errs = 0;
448 for (ap = arglist; *ap != NOSTR; ap++) {
449 if ((vp2 = lookup(*ap)) == NOVAR) {
450 if (!sourcing) {
451 printf("\"%s\": undefined variable\n", *ap);
452 errs++;
453 }
454 continue;
455 }
456 h = hash(*ap);
457 if (vp2 == variables[h]) {
458 variables[h] = variables[h]->v_link;
459 vfree(vp2->v_name);
460 vfree(vp2->v_value);
828615a1 461 cfree((char *)vp2);
ca6bb034
KS
462 continue;
463 }
464 for (vp = variables[h]; vp->v_link != vp2; vp = vp->v_link)
465 ;
466 vp->v_link = vp2->v_link;
467 vfree(vp2->v_name);
468 vfree(vp2->v_value);
828615a1 469 cfree((char *) vp2);
ca6bb034
KS
470 }
471 return(errs);
472}
473
474/*
475 * Put add users to a group.
476 */
477
478group(argv)
479 char **argv;
480{
481 register struct grouphead *gh;
482 register struct group *gp;
483 register int h;
484 int s;
485 char **ap, *gname, **p;
486
487 if (argcount(argv) == 0) {
488 for (h = 0, s = 1; h < HSHSIZE; h++)
489 for (gh = groups[h]; gh != NOGRP; gh = gh->g_link)
490 s++;
491 ap = (char **) salloc(s * sizeof *ap);
492 for (h = 0, p = ap; h < HSHSIZE; h++)
493 for (gh = groups[h]; gh != NOGRP; gh = gh->g_link)
494 *p++ = gh->g_name;
495 *p = NOSTR;
496 sort(ap);
497 for (p = ap; *p != NOSTR; p++)
498 printgroup(*p);
499 return(0);
500 }
501 if (argcount(argv) == 1) {
502 printgroup(*argv);
503 return(0);
504 }
505 gname = *argv;
506 h = hash(gname);
507 if ((gh = findgroup(gname)) == NOGRP) {
508 gh = (struct grouphead *) calloc(sizeof *gh, 1);
509 gh->g_name = vcopy(gname);
510 gh->g_list = NOGE;
511 gh->g_link = groups[h];
512 groups[h] = gh;
513 }
514
515 /*
516 * Insert names from the command list into the group.
517 * Who cares if there are duplicates? They get tossed
518 * later anyway.
519 */
520
521 for (ap = argv+1; *ap != NOSTR; ap++) {
522 gp = (struct group *) calloc(sizeof *gp, 1);
523 gp->ge_name = vcopy(*ap);
524 gp->ge_link = gh->g_list;
525 gh->g_list = gp;
526 }
527 return(0);
528}
529
530/*
531 * Sort the passed string vecotor into ascending dictionary
532 * order.
533 */
534
535sort(list)
536 char **list;
537{
538 register char **ap;
539 int diction();
540
541 for (ap = list; *ap != NOSTR; ap++)
542 ;
543 if (ap-list < 2)
544 return;
828615a1 545 qsort((char *)list, ap-list, sizeof *list, diction);
ca6bb034
KS
546}
547
548/*
549 * Do a dictionary order comparison of the arguments from
550 * qsort.
551 */
552
553diction(a, b)
554 register char **a, **b;
555{
556 return(strcmp(*a, *b));
557}
558
559/*
560 * The do nothing command for comments.
561 */
562
828615a1 563/*ARGSUSED*/
ca6bb034
KS
564null(e)
565{
828615a1 566 return 0;
ca6bb034
KS
567}
568
569/*
1031a04a 570 * Print out the current edit file, if we are editing.
ca6bb034
KS
571 * Otherwise, print the name of the person who's mail
572 * we are reading.
573 */
574
1031a04a 575file(argv)
2a0f6531 576 register char **argv;
ca6bb034
KS
577{
578 register char *cp;
579
1031a04a 580 if (argv[0] == NOSTR) {
890194fb 581 newfileinfo();
2a0f6531 582 return 0;
1031a04a 583 }
2a0f6531
EW
584 if ((cp = expand(*argv)) == NOSTR)
585 return -1;
586 strcpy(prevfile, mailname);
587 if (setfile(cp, **argv != '%')) {
f3b6f2ce 588 perror(cp);
2a0f6531 589 return -1;
f3b6f2ce 590 }
4d20fb09 591 announce();
828615a1 592 return 0;
1031a04a
KS
593}
594
ca6bb034
KS
595/*
596 * Expand file names like echo
597 */
598
599echo(argv)
600 char **argv;
601{
602 register char **ap;
603 register char *cp;
604
605 for (ap = argv; *ap != NOSTR; ap++) {
606 cp = *ap;
790344bc
EW
607 if ((cp = expand(cp)) != NOSTR) {
608 if (ap != argv)
609 putchar(' ');
610 printf("%s", cp);
611 }
ca6bb034 612 }
790344bc
EW
613 putchar('\n');
614
ca6bb034
KS
615 return(0);
616}
617
397029be
S
618Respond(msgvec)
619 int *msgvec;
620{
621 if (value("Replyall") == NOSTR)
622 return (_Respond(msgvec));
623 else
624 return (_respond(msgvec));
625}
626
ca6bb034
KS
627/*
628 * Reply to a series of messages by simply mailing to the senders
629 * and not messing around with the To: and Cc: lists as in normal
630 * reply.
631 */
632
397029be 633_Respond(msgvec)
ca6bb034
KS
634 int msgvec[];
635{
636 struct header head;
637 struct message *mp;
828615a1 638 register int s, *ap;
a0bc099a 639 register char *cp, *cp2, *subject;
ca6bb034
KS
640
641 for (s = 0, ap = msgvec; *ap != 0; ap++) {
642 mp = &message[*ap - 1];
643 dot = mp;
bdad82b3 644 if ((cp = skin(hfield("from", mp))) != NOSTR)
a0bc099a
CL
645 s+= strlen(cp) + 1;
646 else
bdad82b3 647 s += strlen(skin(nameof(mp, 2))) + 1;
ca6bb034
KS
648 }
649 if (s == 0)
650 return(0);
651 cp = salloc(s + 2);
652 head.h_to = cp;
653 for (ap = msgvec; *ap != 0; ap++) {
654 mp = &message[*ap - 1];
bdad82b3
CL
655 if ((cp2 = skin(hfield("from", mp))) == NOSTR)
656 cp2 = skin(nameof(mp, 2));
a0bc099a 657 cp = copy(cp2, cp);
ca6bb034
KS
658 *cp++ = ' ';
659 }
660 *--cp = 0;
661 mp = &message[msgvec[0] - 1];
662 subject = hfield("subject", mp);
663 head.h_seq = 0;
664 if (subject == NOSTR)
665 subject = hfield("subj", mp);
fc8872bb 666 head.h_subject = reedit(subject);
ca6bb034
KS
667 if (subject != NOSTR)
668 head.h_seq++;
669 head.h_cc = NOSTR;
670 head.h_bcc = NOSTR;
790344bc 671 head.h_smopts = NOSTR;
ca6bb034
KS
672 mail1(&head);
673 return(0);
674}
d344b00c
KS
675
676/*
677 * Conditional commands. These allow one to parameterize one's
678 * .mailrc and do some things if sending, others if receiving.
679 */
680
681ifcmd(argv)
682 char **argv;
683{
684 register char *cp;
685
686 if (cond != CANY) {
687 printf("Illegal nested \"if\"\n");
688 return(1);
689 }
690 cond = CANY;
691 cp = argv[0];
692 switch (*cp) {
693 case 'r': case 'R':
694 cond = CRCV;
695 break;
696
697 case 's': case 'S':
698 cond = CSEND;
699 break;
700
701 default:
702 printf("Unrecognized if-keyword: \"%s\"\n", cp);
703 return(1);
704 }
705 return(0);
706}
707
708/*
709 * Implement 'else'. This is pretty simple -- we just
710 * flip over the conditional flag.
711 */
712
713elsecmd()
714{
715
716 switch (cond) {
717 case CANY:
718 printf("\"Else\" without matching \"if\"\n");
719 return(1);
720
721 case CSEND:
722 cond = CRCV;
723 break;
724
725 case CRCV:
726 cond = CSEND;
727 break;
728
729 default:
730 printf("Mail's idea of conditions is screwed up\n");
731 cond = CANY;
732 break;
733 }
734 return(0);
735}
736
737/*
738 * End of if statement. Just set cond back to anything.
739 */
740
741endifcmd()
742{
743
744 if (cond == CANY) {
745 printf("\"Endif\" without matching \"if\"\n");
746 return(1);
747 }
748 cond = CANY;
749 return(0);
750}
1c04ff22
KS
751
752/*
753 * Set the list of alternate names.
754 */
755alternates(namelist)
756 char **namelist;
757{
758 register int c;
759 register char **ap, **ap2, *cp;
760
761 c = argcount(namelist) + 1;
762 if (c == 1) {
763 if (altnames == 0)
764 return(0);
765 for (ap = altnames; *ap; ap++)
766 printf("%s ", *ap);
767 printf("\n");
768 return(0);
769 }
770 if (altnames != 0)
771 cfree((char *) altnames);
828615a1 772 altnames = (char **) calloc((unsigned) c, sizeof (char *));
1c04ff22 773 for (ap = namelist, ap2 = altnames; *ap; ap++, ap2++) {
828615a1 774 cp = (char *) calloc((unsigned) strlen(*ap) + 1, sizeof (char));
1c04ff22
KS
775 strcpy(cp, *ap);
776 *ap2 = cp;
777 }
778 *ap2 = 0;
779 return(0);
780}