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