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