date and time created 83/10/02 21:41:04 by sam
[unix-history] / usr / src / usr.bin / mail / cmd3.c
CommitLineData
2ae9f53f
SL
1#ifndef lint
2static char sccsid[] = "@(#)cmd3.c 2.14 (Berkeley) %G%";
3#endif
ca6bb034
KS
4
5#include "rcv.h"
6#include <sys/stat.h>
7
8/*
9 * Mail -- a mail program
10 *
11 * Still more user commands.
12 */
13
ca6bb034
KS
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) {
7949d400 152 perror(HELPFILE);
ca6bb034
KS
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;
7949d400
CL
193 char *cp, *cp2, *cp3, *rcv, *replyto;
194 char buf[2 * LINESIZE], **ap;
ca6bb034
KS
195 struct name *np;
196 struct header head;
ca6bb034
KS
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;
a0bc099a 204 rcv = NOSTR;
bdad82b3 205 cp = skin(nameof(mp, 1));
a0bc099a
CL
206 if (cp != NOSTR)
207 rcv = cp;
bdad82b3 208 cp = skin(hfield("from", mp));
a0bc099a
CL
209 if (cp != NOSTR)
210 rcv = cp;
35bddd51 211 replyto = skin(hfield("reply-to", mp));
ca6bb034 212 strcpy(buf, "");
52984be9
KS
213 if (replyto != NOSTR)
214 strcpy(buf, replyto);
215 else {
bdad82b3 216 cp = skin(hfield("to", mp));
52984be9
KS
217 if (cp != NOSTR)
218 strcpy(buf, cp);
219 }
ca6bb034
KS
220 np = elide(extract(buf, GTO));
221 /* rcv = rename(rcv); */
222 mapf(np, rcv);
1c04ff22
KS
223 /*
224 * Delete my name from the reply list,
225 * and with it, all my alternate names.
226 */
7949d400
CL
227 np = delname(np, myname, icequal);
228 if (altnames)
1c04ff22 229 for (ap = altnames; *ap; ap++)
7949d400 230 np = delname(np, *ap, icequal);
ca6bb034 231 head.h_seq = 1;
bdad82b3 232 cp = detract(np, 0);
52984be9 233 if (cp != NOSTR && replyto == NOSTR) {
ca6bb034 234 strcpy(buf, cp);
bdad82b3 235 strcat(buf, " ");
ca6bb034
KS
236 strcat(buf, rcv);
237 }
52984be9
KS
238 else {
239 if (cp == NOSTR && replyto != NOSTR)
240 printf("Empty reply-to field -- replying to author\n");
241 if (cp == NOSTR)
242 strcpy(buf, rcv);
243 else
244 strcpy(buf, cp);
245 }
ca6bb034
KS
246 head.h_to = buf;
247 head.h_subject = hfield("subject", mp);
248 if (head.h_subject == NOSTR)
249 head.h_subject = hfield("subj", mp);
fc8872bb 250 head.h_subject = reedit(head.h_subject);
ca6bb034 251 head.h_cc = NOSTR;
52984be9
KS
252 if (replyto == NOSTR) {
253 cp = hfield("cc", mp);
254 if (cp != NOSTR) {
255 np = elide(extract(cp, GCC));
256 mapf(np, rcv);
7949d400 257 np = delname(np, myname, icequal);
2a8a6697
KS
258 if (altnames != 0)
259 for (ap = altnames; *ap; ap++)
7949d400 260 np = delname(np, *ap, icequal);
52984be9
KS
261 head.h_cc = detract(np, 0);
262 }
ca6bb034
KS
263 }
264 head.h_bcc = NOSTR;
265 mail1(&head);
266 return(0);
267}
268
fc8872bb
KS
269/*
270 * Modify the subject we are replying to to begin with Re: if
271 * it does not already.
272 */
273
274char *
275reedit(subj)
276 char *subj;
277{
278 char sbuf[10];
279 register char *newsubj;
280
281 if (subj == NOSTR)
282 return(NOSTR);
283 strncpy(sbuf, subj, 3);
284 sbuf[3] = 0;
285 if (icequal(sbuf, "re:"))
286 return(subj);
287 newsubj = salloc(strlen(subj) + 6);
288 sprintf(newsubj, "Re: %s", subj);
289 return(newsubj);
290}
291
ca6bb034
KS
292/*
293 * Preserve the named messages, so that they will be sent
294 * back to the system mailbox.
295 */
296
297preserve(msgvec)
298 int *msgvec;
299{
300 register struct message *mp;
301 register int *ip, mesg;
302
303 if (edit) {
304 printf("Cannot \"preserve\" in edit mode\n");
305 return(1);
306 }
307 for (ip = msgvec; *ip != NULL; ip++) {
308 mesg = *ip;
309 mp = &message[mesg-1];
310 mp->m_flag |= MPRESERVE;
8fe58892 311 mp->m_flag &= ~MBOX;
ca6bb034
KS
312 dot = mp;
313 }
314 return(0);
315}
316
317/*
318 * Print the size of each message.
319 */
320
321messize(msgvec)
322 int *msgvec;
323{
324 register struct message *mp;
325 register int *ip, mesg;
326
327 for (ip = msgvec; *ip != NULL; ip++) {
328 mesg = *ip;
329 mp = &message[mesg-1];
7667ac30 330 printf("%d: %ld\n", mesg, mp->m_size);
ca6bb034
KS
331 }
332 return(0);
333}
334
335/*
336 * Quit quickly. If we are sourcing, just pop the input level
337 * by returning an error.
338 */
339
340rexit(e)
341{
342 if (sourcing)
343 return(1);
c179b6f2
KS
344 if (Tflag != NOSTR)
345 close(creat(Tflag, 0600));
ca6bb034
KS
346 exit(e);
347}
348
349/*
350 * Set or display a variable value. Syntax is similar to that
351 * of csh.
352 */
353
354set(arglist)
355 char **arglist;
356{
357 register struct var *vp;
358 register char *cp, *cp2;
359 char varbuf[BUFSIZ], **ap, **p;
360 int errs, h, s;
361
362 if (argcount(arglist) == 0) {
363 for (h = 0, s = 1; h < HSHSIZE; h++)
364 for (vp = variables[h]; vp != NOVAR; vp = vp->v_link)
365 s++;
366 ap = (char **) salloc(s * sizeof *ap);
367 for (h = 0, p = ap; h < HSHSIZE; h++)
368 for (vp = variables[h]; vp != NOVAR; vp = vp->v_link)
369 *p++ = vp->v_name;
370 *p = NOSTR;
371 sort(ap);
372 for (p = ap; *p != NOSTR; p++)
373 printf("%s\t%s\n", *p, value(*p));
374 return(0);
375 }
376 errs = 0;
377 for (ap = arglist; *ap != NOSTR; ap++) {
378 cp = *ap;
379 cp2 = varbuf;
380 while (*cp != '=' && *cp != '\0')
381 *cp2++ = *cp++;
382 *cp2 = '\0';
383 if (*cp == '\0')
384 cp = "";
385 else
386 cp++;
387 if (equal(varbuf, "")) {
388 printf("Non-null variable name required\n");
389 errs++;
390 continue;
391 }
392 assign(varbuf, cp);
393 }
394 return(errs);
395}
396
397/*
398 * Unset a bunch of variable values.
399 */
400
401unset(arglist)
402 char **arglist;
403{
404 register struct var *vp, *vp2;
405 register char *cp;
406 int errs, h;
407 char **ap;
408
409 errs = 0;
410 for (ap = arglist; *ap != NOSTR; ap++) {
411 if ((vp2 = lookup(*ap)) == NOVAR) {
412 if (!sourcing) {
413 printf("\"%s\": undefined variable\n", *ap);
414 errs++;
415 }
416 continue;
417 }
418 h = hash(*ap);
419 if (vp2 == variables[h]) {
420 variables[h] = variables[h]->v_link;
421 vfree(vp2->v_name);
422 vfree(vp2->v_value);
423 cfree(vp2);
424 continue;
425 }
426 for (vp = variables[h]; vp->v_link != vp2; vp = vp->v_link)
427 ;
428 vp->v_link = vp2->v_link;
429 vfree(vp2->v_name);
430 vfree(vp2->v_value);
431 cfree(vp2);
432 }
433 return(errs);
434}
435
436/*
437 * Put add users to a group.
438 */
439
440group(argv)
441 char **argv;
442{
443 register struct grouphead *gh;
444 register struct group *gp;
445 register int h;
446 int s;
447 char **ap, *gname, **p;
448
449 if (argcount(argv) == 0) {
450 for (h = 0, s = 1; h < HSHSIZE; h++)
451 for (gh = groups[h]; gh != NOGRP; gh = gh->g_link)
452 s++;
453 ap = (char **) salloc(s * sizeof *ap);
454 for (h = 0, p = ap; h < HSHSIZE; h++)
455 for (gh = groups[h]; gh != NOGRP; gh = gh->g_link)
456 *p++ = gh->g_name;
457 *p = NOSTR;
458 sort(ap);
459 for (p = ap; *p != NOSTR; p++)
460 printgroup(*p);
461 return(0);
462 }
463 if (argcount(argv) == 1) {
464 printgroup(*argv);
465 return(0);
466 }
467 gname = *argv;
468 h = hash(gname);
469 if ((gh = findgroup(gname)) == NOGRP) {
470 gh = (struct grouphead *) calloc(sizeof *gh, 1);
471 gh->g_name = vcopy(gname);
472 gh->g_list = NOGE;
473 gh->g_link = groups[h];
474 groups[h] = gh;
475 }
476
477 /*
478 * Insert names from the command list into the group.
479 * Who cares if there are duplicates? They get tossed
480 * later anyway.
481 */
482
483 for (ap = argv+1; *ap != NOSTR; ap++) {
484 gp = (struct group *) calloc(sizeof *gp, 1);
485 gp->ge_name = vcopy(*ap);
486 gp->ge_link = gh->g_list;
487 gh->g_list = gp;
488 }
489 return(0);
490}
491
492/*
493 * Sort the passed string vecotor into ascending dictionary
494 * order.
495 */
496
497sort(list)
498 char **list;
499{
500 register char **ap;
501 int diction();
502
503 for (ap = list; *ap != NOSTR; ap++)
504 ;
505 if (ap-list < 2)
506 return;
507 qsort(list, ap-list, sizeof *list, diction);
508}
509
510/*
511 * Do a dictionary order comparison of the arguments from
512 * qsort.
513 */
514
515diction(a, b)
516 register char **a, **b;
517{
518 return(strcmp(*a, *b));
519}
520
521/*
522 * The do nothing command for comments.
523 */
524
525null(e)
526{
527 return(0);
528}
529
530/*
1031a04a 531 * Print out the current edit file, if we are editing.
ca6bb034
KS
532 * Otherwise, print the name of the person who's mail
533 * we are reading.
534 */
535
1031a04a
KS
536file(argv)
537 char **argv;
ca6bb034
KS
538{
539 register char *cp;
1031a04a 540 char fname[BUFSIZ];
84995525 541 int edit;
ca6bb034 542
1031a04a 543 if (argv[0] == NOSTR) {
890194fb 544 newfileinfo();
1031a04a
KS
545 return(0);
546 }
547
548 /*
549 * Acker's! Must switch to the new file.
550 * We use a funny interpretation --
551 * # -- gets the previous file
552 * % -- gets the invoker's post office box
553 * %user -- gets someone else's post office box
c953e015 554 * & -- gets invoker's mbox file
1031a04a
KS
555 * string -- reads the given file
556 */
557
84995525 558 cp = getfilename(argv[0], &edit);
1031a04a
KS
559 if (cp == NOSTR)
560 return(-1);
f3b6f2ce
KS
561 if (setfile(cp, edit)) {
562 perror(cp);
c45bca48 563 return(-1);
f3b6f2ce 564 }
c45bca48 565 newfileinfo();
1031a04a
KS
566}
567
568/*
569 * Evaluate the string given as a new mailbox name.
570 * Ultimately, we want this to support a number of meta characters.
571 * Possibly:
572 * % -- for my system mail box
573 * %user -- for user's system mail box
574 * # -- for previous file
c953e015 575 * & -- get's invoker's mbox file
1031a04a
KS
576 * file name -- for any other file
577 */
578
d344b00c 579char prevfile[PATHSIZE];
c953e015 580
1031a04a 581char *
84995525 582getfilename(name, aedit)
1031a04a 583 char *name;
84995525 584 int *aedit;
1031a04a
KS
585{
586 register char *cp;
c953e015 587 char savename[BUFSIZ];
2b6cb640 588 char oldmailname[BUFSIZ];
c953e015 589
84995525
KS
590 /*
591 * Assume we will be in "edit file" mode, until
592 * proven wrong.
593 */
594 *aedit = 1;
c953e015
KS
595 switch (*name) {
596 case '%':
84995525 597 *aedit = 0;
d344b00c 598 strcpy(prevfile, mailname);
c953e015
KS
599 if (name[1] != 0) {
600 strcpy(savename, myname);
2b6cb640 601 strcpy(oldmailname, mailname);
c953e015
KS
602 strncpy(myname, name+1, PATHSIZE-1);
603 myname[PATHSIZE-1] = 0;
604 findmail();
2b6cb640 605 cp = savestr(mailname);
c953e015 606 strcpy(myname, savename);
2b6cb640 607 strcpy(mailname, oldmailname);
c953e015
KS
608 return(cp);
609 }
2b6cb640 610 strcpy(oldmailname, mailname);
c953e015 611 findmail();
2b6cb640
KS
612 cp = savestr(mailname);
613 strcpy(mailname, oldmailname);
614 return(cp);
c953e015
KS
615
616 case '#':
617 if (name[1] != 0)
618 goto regular;
619 if (prevfile[0] == 0) {
620 printf("No previous file\n");
621 return(NOSTR);
622 }
2b6cb640 623 cp = savestr(prevfile);
d344b00c 624 strcpy(prevfile, mailname);
2b6cb640 625 return(cp);
1031a04a 626
c953e015 627 case '&':
d344b00c 628 strcpy(prevfile, mailname);
c953e015
KS
629 if (name[1] == 0)
630 return(mbox);
631 /* Fall into . . . */
632
633 default:
634regular:
d344b00c 635 strcpy(prevfile, mailname);
c953e015
KS
636 cp = expand(name);
637 return(cp);
638 }
ca6bb034
KS
639}
640
641/*
642 * Expand file names like echo
643 */
644
645echo(argv)
646 char **argv;
647{
648 register char **ap;
649 register char *cp;
650
651 for (ap = argv; *ap != NOSTR; ap++) {
652 cp = *ap;
653 if ((cp = expand(cp)) != NOSTR)
9e60d092 654 printf("%s ", cp);
ca6bb034
KS
655 }
656 return(0);
657}
658
659/*
660 * Reply to a series of messages by simply mailing to the senders
661 * and not messing around with the To: and Cc: lists as in normal
662 * reply.
663 */
664
665Respond(msgvec)
666 int msgvec[];
667{
668 struct header head;
669 struct message *mp;
a0bc099a
CL
670 register int i, s, *ap;
671 register char *cp, *cp2, *subject;
ca6bb034
KS
672
673 for (s = 0, ap = msgvec; *ap != 0; ap++) {
674 mp = &message[*ap - 1];
675 dot = mp;
bdad82b3 676 if ((cp = skin(hfield("from", mp))) != NOSTR)
a0bc099a
CL
677 s+= strlen(cp) + 1;
678 else
bdad82b3 679 s += strlen(skin(nameof(mp, 2))) + 1;
ca6bb034
KS
680 }
681 if (s == 0)
682 return(0);
683 cp = salloc(s + 2);
684 head.h_to = cp;
685 for (ap = msgvec; *ap != 0; ap++) {
686 mp = &message[*ap - 1];
bdad82b3
CL
687 if ((cp2 = skin(hfield("from", mp))) == NOSTR)
688 cp2 = skin(nameof(mp, 2));
a0bc099a 689 cp = copy(cp2, cp);
ca6bb034
KS
690 *cp++ = ' ';
691 }
692 *--cp = 0;
693 mp = &message[msgvec[0] - 1];
694 subject = hfield("subject", mp);
695 head.h_seq = 0;
696 if (subject == NOSTR)
697 subject = hfield("subj", mp);
fc8872bb 698 head.h_subject = reedit(subject);
ca6bb034
KS
699 if (subject != NOSTR)
700 head.h_seq++;
701 head.h_cc = NOSTR;
702 head.h_bcc = NOSTR;
703 mail1(&head);
704 return(0);
705}
d344b00c
KS
706
707/*
708 * Conditional commands. These allow one to parameterize one's
709 * .mailrc and do some things if sending, others if receiving.
710 */
711
712ifcmd(argv)
713 char **argv;
714{
715 register char *cp;
716
717 if (cond != CANY) {
718 printf("Illegal nested \"if\"\n");
719 return(1);
720 }
721 cond = CANY;
722 cp = argv[0];
723 switch (*cp) {
724 case 'r': case 'R':
725 cond = CRCV;
726 break;
727
728 case 's': case 'S':
729 cond = CSEND;
730 break;
731
732 default:
733 printf("Unrecognized if-keyword: \"%s\"\n", cp);
734 return(1);
735 }
736 return(0);
737}
738
739/*
740 * Implement 'else'. This is pretty simple -- we just
741 * flip over the conditional flag.
742 */
743
744elsecmd()
745{
746
747 switch (cond) {
748 case CANY:
749 printf("\"Else\" without matching \"if\"\n");
750 return(1);
751
752 case CSEND:
753 cond = CRCV;
754 break;
755
756 case CRCV:
757 cond = CSEND;
758 break;
759
760 default:
761 printf("Mail's idea of conditions is screwed up\n");
762 cond = CANY;
763 break;
764 }
765 return(0);
766}
767
768/*
769 * End of if statement. Just set cond back to anything.
770 */
771
772endifcmd()
773{
774
775 if (cond == CANY) {
776 printf("\"Endif\" without matching \"if\"\n");
777 return(1);
778 }
779 cond = CANY;
780 return(0);
781}
1c04ff22
KS
782
783/*
784 * Set the list of alternate names.
785 */
786alternates(namelist)
787 char **namelist;
788{
789 register int c;
790 register char **ap, **ap2, *cp;
791
792 c = argcount(namelist) + 1;
793 if (c == 1) {
794 if (altnames == 0)
795 return(0);
796 for (ap = altnames; *ap; ap++)
797 printf("%s ", *ap);
798 printf("\n");
799 return(0);
800 }
801 if (altnames != 0)
802 cfree((char *) altnames);
803 altnames = (char **) calloc(c, sizeof (char *));
804 for (ap = namelist, ap2 = altnames; *ap; ap++, ap2++) {
805 cp = (char *) calloc(strlen(*ap) + 1, sizeof (char));
806 strcpy(cp, *ap);
807 *ap2 = cp;
808 }
809 *ap2 = 0;
810 return(0);
811}