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