xref: /original-bsd/usr.bin/mail/cmd3.c (revision 4f485440)
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 
12 static char *SccsId = "@(#)cmd3.c	2.11 06/05/83";
13 
14 /*
15  * Process a shell escape by saving signals, ignoring signals,
16  * and forking a sh -c
17  */
18 
19 shell(str)
20 	char *str;
21 {
22 	int (*sig[2])(), stat[1];
23 	register int t;
24 	char *Shell;
25 	char cmd[BUFSIZ];
26 
27 	strcpy(cmd, str);
28 	if (bangexp(cmd) < 0)
29 		return(-1);
30 	if ((Shell = value("SHELL")) == NOSTR)
31 		Shell = SHELL;
32 	for (t = 2; t < 4; t++)
33 		sig[t-2] = sigset(t, SIG_IGN);
34 	t = vfork();
35 	if (t == 0) {
36 		sigchild();
37 		for (t = 2; t < 4; t++)
38 			if (sig[t-2] != SIG_IGN)
39 				sigsys(t, SIG_DFL);
40 		execl(Shell, Shell, "-c", cmd, 0);
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++)
49 		sigset(t, sig[t-2]);
50 	printf("!\n");
51 	return(0);
52 }
53 
54 /*
55  * Fork an interactive shell.
56  */
57 
58 dosh(str)
59 	char *str;
60 {
61 	int (*sig[2])(), stat[1];
62 	register int t;
63 	char *Shell;
64 	if ((Shell = value("SHELL")) == NOSTR)
65 		Shell = SHELL;
66 	for (t = 2; t < 4; t++)
67 		sig[t-2] = sigset(t, SIG_IGN);
68 	t = vfork();
69 	if (t == 0) {
70 		sigchild();
71 		for (t = 2; t < 4; t++)
72 			if (sig[t-2] != SIG_IGN)
73 				sigsys(t, SIG_DFL);
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++)
83 		sigsys(t, sig[t-2]);
84 	putchar('\n');
85 	return(0);
86 }
87 
88 /*
89  * Expand the shell escape by expanding unescaped !'s into the
90  * last issued command where possible.
91  */
92 
93 char	lastbang[128];
94 
95 bangexp(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)) {
109 overf:
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 
142 /*
143  * Print out a nice help message from some file or another.
144  */
145 
146 help()
147 {
148 	register c;
149 	register FILE *f;
150 
151 	if ((f = fopen(HELPFILE, "r")) == NULL) {
152 		perror(HELPFILE);
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 
165 schdir(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 
189 respond(msgvec)
190 	int *msgvec;
191 {
192 	struct message *mp;
193 	char *cp, *cp2, *cp3, *rcv, *replyto;
194 	char buf[2 * LINESIZE], **ap;
195 	struct name *np;
196 	struct header head;
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;
204 	rcv = NOSTR;
205 	cp = nameof(mp, 1);
206 	if (cp != NOSTR)
207 	    rcv = cp;
208 	cp = hfield("from", mp);
209 	if (cp != NOSTR)
210 	    rcv = cp;
211 	replyto = skin(hfield("reply-to", mp));
212 	strcpy(buf, "");
213 	if (replyto != NOSTR)
214 		strcpy(buf, replyto);
215 	else {
216 		cp = hfield("to", mp);
217 		if (cp != NOSTR)
218 			strcpy(buf, cp);
219 	}
220 	np = elide(extract(buf, GTO));
221 	/* rcv = rename(rcv); */
222 	mapf(np, rcv);
223 	/*
224 	 * Delete my name from the reply list,
225 	 * and with it, all my alternate names.
226 	 */
227 	np = delname(np, myname, icequal);
228 	if (altnames)
229 		for (ap = altnames; *ap; ap++)
230 			np = delname(np, *ap, icequal);
231 	head.h_seq = 1;
232 	cp = detract(np, 0);
233 	if (cp != NOSTR && replyto == NOSTR) {
234 		strcpy(buf, cp);
235 		strcat(buf, " ");
236 		strcat(buf, rcv);
237 	}
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 	}
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);
250 	head.h_subject = reedit(head.h_subject);
251 	head.h_cc = NOSTR;
252 	if (replyto == NOSTR) {
253 		cp = hfield("cc", mp);
254 		if (cp != NOSTR) {
255 			np = elide(extract(cp, GCC));
256 			mapf(np, rcv);
257 			np = delname(np, myname, icequal);
258 			if (altnames != 0)
259 				for (ap = altnames; *ap; ap++)
260 					np = delname(np, *ap, icequal);
261 			head.h_cc = detract(np, 0);
262 		}
263 	}
264 	head.h_bcc = NOSTR;
265 	mail1(&head);
266 	return(0);
267 }
268 
269 /*
270  * Modify the subject we are replying to to begin with Re: if
271  * it does not already.
272  */
273 
274 char *
275 reedit(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 
292 /*
293  * Preserve the named messages, so that they will be sent
294  * back to the system mailbox.
295  */
296 
297 preserve(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;
311 		mp->m_flag &= ~MBOX;
312 		dot = mp;
313 	}
314 	return(0);
315 }
316 
317 /*
318  * Print the size of each message.
319  */
320 
321 messize(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];
330 		printf("%d: %ld\n", mesg, mp->m_size);
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 
340 rexit(e)
341 {
342 	if (sourcing)
343 		return(1);
344 	if (Tflag != NOSTR)
345 		close(creat(Tflag, 0600));
346 	exit(e);
347 }
348 
349 /*
350  * Set or display a variable value.  Syntax is similar to that
351  * of csh.
352  */
353 
354 set(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 
401 unset(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 
440 group(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 
497 sort(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 
515 diction(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 
525 null(e)
526 {
527 	return(0);
528 }
529 
530 /*
531  * Print out the current edit file, if we are editing.
532  * Otherwise, print the name of the person who's mail
533  * we are reading.
534  */
535 
536 file(argv)
537 	char **argv;
538 {
539 	register char *cp;
540 	char fname[BUFSIZ];
541 	int edit;
542 
543 	if (argv[0] == NOSTR) {
544 		newfileinfo();
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
554 	 *	& -- gets invoker's mbox file
555 	 *	string -- reads the given file
556 	 */
557 
558 	cp = getfilename(argv[0], &edit);
559 	if (cp == NOSTR)
560 		return(-1);
561 	if (setfile(cp, edit)) {
562 		perror(cp);
563 		return(-1);
564 	}
565 	newfileinfo();
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
575  *	& -- get's invoker's mbox file
576  *	file name -- for any other file
577  */
578 
579 char	prevfile[PATHSIZE];
580 
581 char *
582 getfilename(name, aedit)
583 	char *name;
584 	int *aedit;
585 {
586 	register char *cp;
587 	char savename[BUFSIZ];
588 	char oldmailname[BUFSIZ];
589 
590 	/*
591 	 * Assume we will be in "edit file" mode, until
592 	 * proven wrong.
593 	 */
594 	*aedit = 1;
595 	switch (*name) {
596 	case '%':
597 		*aedit = 0;
598 		strcpy(prevfile, mailname);
599 		if (name[1] != 0) {
600 			strcpy(savename, myname);
601 			strcpy(oldmailname, mailname);
602 			strncpy(myname, name+1, PATHSIZE-1);
603 			myname[PATHSIZE-1] = 0;
604 			findmail();
605 			cp = savestr(mailname);
606 			strcpy(myname, savename);
607 			strcpy(mailname, oldmailname);
608 			return(cp);
609 		}
610 		strcpy(oldmailname, mailname);
611 		findmail();
612 		cp = savestr(mailname);
613 		strcpy(mailname, oldmailname);
614 		return(cp);
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 		}
623 		cp = savestr(prevfile);
624 		strcpy(prevfile, mailname);
625 		return(cp);
626 
627 	case '&':
628 		strcpy(prevfile, mailname);
629 		if (name[1] == 0)
630 			return(mbox);
631 		/* Fall into . . . */
632 
633 	default:
634 regular:
635 		strcpy(prevfile, mailname);
636 		cp = expand(name);
637 		return(cp);
638 	}
639 }
640 
641 /*
642  * Expand file names like echo
643  */
644 
645 echo(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)
654 			printf("%s ", cp);
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 
665 Respond(msgvec)
666 	int msgvec[];
667 {
668 	struct header head;
669 	struct message *mp;
670 	register int i, s, *ap;
671 	register char *cp, *cp2, *subject;
672 
673 	for (s = 0, ap = msgvec; *ap != 0; ap++) {
674 		mp = &message[*ap - 1];
675 		dot = mp;
676 		if ((cp = hfield("from", mp)) != NOSTR)
677 		    s+= strlen(cp) + 1;
678 		else
679 		    s += strlen(nameof(mp, 2)) + 1;
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];
687 		if ((cp2 = hfield("from", mp)) == NOSTR)
688 		    cp2 = nameof(mp, 2);
689 		cp = copy(cp2, cp);
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);
698 	head.h_subject = reedit(subject);
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 }
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 
712 ifcmd(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 
744 elsecmd()
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 
772 endifcmd()
773 {
774 
775 	if (cond == CANY) {
776 		printf("\"Endif\" without matching \"if\"\n");
777 		return(1);
778 	}
779 	cond = CANY;
780 	return(0);
781 }
782 
783 /*
784  * Set the list of alternate names.
785  */
786 alternates(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 }
812