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