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