xref: /original-bsd/usr.bin/mail/cmd3.c (revision 0b685140)
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.6 02/20/82";
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 		for (t = 2; t < 4; t++)
37 			if (sig[t-2] != SIG_IGN)
38 				sigsys(t, SIG_DFL);
39 		execl(Shell, Shell, "-c", cmd, 0);
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++)
48 		sigset(t, sig[t-2]);
49 	printf("!\n");
50 	return(0);
51 }
52 
53 /*
54  * Fork an interactive shell.
55  */
56 
57 dosh(str)
58 	char *str;
59 {
60 	int (*sig[2])(), stat[1];
61 	register int t;
62 	char *Shell;
63 	if ((Shell = value("SHELL")) == NOSTR)
64 		Shell = SHELL;
65 	for (t = 2; t < 4; t++)
66 		sig[t-2] = sigset(t, SIG_IGN);
67 	t = vfork();
68 	if (t == 0) {
69 		for (t = 2; t < 4; t++)
70 			if (sig[t-2] != SIG_IGN)
71 				sigsys(t, SIG_DFL);
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++)
81 		sigsys(t, sig[t-2]);
82 	putchar('\n');
83 	return(0);
84 }
85 
86 /*
87  * Expand the shell escape by expanding unescaped !'s into the
88  * last issued command where possible.
89  */
90 
91 char	lastbang[128];
92 
93 bangexp(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)) {
107 overf:
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 
140 /*
141  * Print out a nice help message from some file or another.
142  */
143 
144 help()
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 
163 schdir(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 
187 respond(msgvec)
188 	int *msgvec;
189 {
190 	struct message *mp;
191 	char *cp, buf[2 * LINESIZE], *rcv, *replyto, **ap;
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;
202 	rcv = nameof(mp, 1);
203 	replyto = skin(hfield("reply-to", mp));
204 	strcpy(buf, "");
205 	if (replyto != NOSTR)
206 		strcpy(buf, replyto);
207 	else {
208 		cp = hfield("to", mp);
209 		if (cp != NOSTR)
210 			strcpy(buf, cp);
211 	}
212 	np = elide(extract(buf, GTO));
213 	/* rcv = rename(rcv); */
214 	mapf(np, rcv);
215 	/*
216 	 * Delete my name from the reply list,
217 	 * and with it, all my alternate names.
218 	 */
219 	np = delname(np, myname);
220 	if (altnames != 0)
221 		for (ap = altnames; *ap; ap++)
222 			np = delname(np, *ap);
223 	head.h_seq = 1;
224 	cp = detract(np, 0);
225 	if (cp != NOSTR && replyto == NOSTR) {
226 		strcpy(buf, cp);
227 		strcat(buf, " ");
228 		strcat(buf, rcv);
229 	}
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 	}
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);
242 	head.h_subject = reedit(head.h_subject);
243 	head.h_cc = NOSTR;
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);
250 			if (altnames != 0)
251 				for (ap = altnames; *ap; ap++)
252 					np = delname(np, *ap);
253 			head.h_cc = detract(np, 0);
254 		}
255 	}
256 	head.h_bcc = NOSTR;
257 	mail1(&head);
258 	return(0);
259 }
260 
261 /*
262  * Modify the subject we are replying to to begin with Re: if
263  * it does not already.
264  */
265 
266 char *
267 reedit(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 
284 /*
285  * Preserve the named messages, so that they will be sent
286  * back to the system mailbox.
287  */
288 
289 preserve(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;
303 		mp->m_flag &= ~MBOX;
304 		dot = mp;
305 	}
306 	return(0);
307 }
308 
309 /*
310  * Print the size of each message.
311  */
312 
313 messize(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 
332 rexit(e)
333 {
334 	if (sourcing)
335 		return(1);
336 	if (Tflag != NOSTR)
337 		close(creat(Tflag, 0600));
338 	exit(e);
339 }
340 
341 /*
342  * Set or display a variable value.  Syntax is similar to that
343  * of csh.
344  */
345 
346 set(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 
393 unset(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 
432 group(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 
489 sort(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 
507 diction(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 
517 null(e)
518 {
519 	return(0);
520 }
521 
522 /*
523  * Print out the current edit file, if we are editing.
524  * Otherwise, print the name of the person who's mail
525  * we are reading.
526  */
527 
528 file(argv)
529 	char **argv;
530 {
531 	register char *cp;
532 	char fname[BUFSIZ];
533 	int edit;
534 
535 	if (argv[0] == NOSTR) {
536 		newfileinfo();
537 		return(0);
538 	}
539 
540 	/*
541 	 * Acker's!  Must switch to the new file.
542 	 * We use a funny interpretation --
543 	 *	# -- gets the previous file
544 	 *	% -- gets the invoker's post office box
545 	 *	%user -- gets someone else's post office box
546 	 *	& -- gets invoker's mbox file
547 	 *	string -- reads the given file
548 	 */
549 
550 	cp = getfilename(argv[0], &edit);
551 	if (cp == NOSTR)
552 		return(-1);
553 	if (setfile(cp, edit))
554 		return(-1);
555 	newfileinfo();
556 }
557 
558 /*
559  * Evaluate the string given as a new mailbox name.
560  * Ultimately, we want this to support a number of meta characters.
561  * Possibly:
562  *	% -- for my system mail box
563  *	%user -- for user's system mail box
564  *	# -- for previous file
565  *	& -- get's invoker's mbox file
566  *	file name -- for any other file
567  */
568 
569 char	prevfile[PATHSIZE];
570 
571 char *
572 getfilename(name, aedit)
573 	char *name;
574 	int *aedit;
575 {
576 	register char *cp;
577 	char savename[BUFSIZ];
578 	char oldmailname[BUFSIZ];
579 
580 	/*
581 	 * Assume we will be in "edit file" mode, until
582 	 * proven wrong.
583 	 */
584 	*aedit = 1;
585 	switch (*name) {
586 	case '%':
587 		*aedit = 0;
588 		strcpy(prevfile, mailname);
589 		if (name[1] != 0) {
590 			strcpy(savename, myname);
591 			strcpy(oldmailname, mailname);
592 			strncpy(myname, name+1, PATHSIZE-1);
593 			myname[PATHSIZE-1] = 0;
594 			findmail();
595 			cp = savestr(mailname);
596 			strcpy(myname, savename);
597 			strcpy(mailname, oldmailname);
598 			return(cp);
599 		}
600 		strcpy(oldmailname, mailname);
601 		findmail();
602 		cp = savestr(mailname);
603 		strcpy(mailname, oldmailname);
604 		return(cp);
605 
606 	case '#':
607 		if (name[1] != 0)
608 			goto regular;
609 		if (prevfile[0] == 0) {
610 			printf("No previous file\n");
611 			return(NOSTR);
612 		}
613 		cp = savestr(prevfile);
614 		strcpy(prevfile, mailname);
615 		return(cp);
616 
617 	case '&':
618 		strcpy(prevfile, mailname);
619 		if (name[1] == 0)
620 			return(mbox);
621 		/* Fall into . . . */
622 
623 	default:
624 regular:
625 		strcpy(prevfile, mailname);
626 		cp = expand(name);
627 		return(cp);
628 	}
629 }
630 
631 /*
632  * Expand file names like echo
633  */
634 
635 echo(argv)
636 	char **argv;
637 {
638 	register char **ap;
639 	register char *cp;
640 
641 	for (ap = argv; *ap != NOSTR; ap++) {
642 		cp = *ap;
643 		if ((cp = expand(cp)) != NOSTR)
644 			printf("%s ", cp);
645 	}
646 	return(0);
647 }
648 
649 /*
650  * Reply to a series of messages by simply mailing to the senders
651  * and not messing around with the To: and Cc: lists as in normal
652  * reply.
653  */
654 
655 Respond(msgvec)
656 	int msgvec[];
657 {
658 	struct header head;
659 	struct message *mp;
660 	register int s, *ap;
661 	register char *cp, *subject;
662 
663 	for (s = 0, ap = msgvec; *ap != 0; ap++) {
664 		mp = &message[*ap - 1];
665 		dot = mp;
666 		s += strlen(nameof(mp, 2)) + 1;
667 	}
668 	if (s == 0)
669 		return(0);
670 	cp = salloc(s + 2);
671 	head.h_to = cp;
672 	for (ap = msgvec; *ap != 0; ap++) {
673 		mp = &message[*ap - 1];
674 		cp = copy(nameof(mp, 2), cp);
675 		*cp++ = ' ';
676 	}
677 	*--cp = 0;
678 	mp = &message[msgvec[0] - 1];
679 	subject = hfield("subject", mp);
680 	head.h_seq = 0;
681 	if (subject == NOSTR)
682 		subject = hfield("subj", mp);
683 	head.h_subject = reedit(subject);
684 	if (subject != NOSTR)
685 		head.h_seq++;
686 	head.h_cc = NOSTR;
687 	head.h_bcc = NOSTR;
688 	mail1(&head);
689 	return(0);
690 }
691 
692 /*
693  * Conditional commands.  These allow one to parameterize one's
694  * .mailrc and do some things if sending, others if receiving.
695  */
696 
697 ifcmd(argv)
698 	char **argv;
699 {
700 	register char *cp;
701 
702 	if (cond != CANY) {
703 		printf("Illegal nested \"if\"\n");
704 		return(1);
705 	}
706 	cond = CANY;
707 	cp = argv[0];
708 	switch (*cp) {
709 	case 'r': case 'R':
710 		cond = CRCV;
711 		break;
712 
713 	case 's': case 'S':
714 		cond = CSEND;
715 		break;
716 
717 	default:
718 		printf("Unrecognized if-keyword: \"%s\"\n", cp);
719 		return(1);
720 	}
721 	return(0);
722 }
723 
724 /*
725  * Implement 'else'.  This is pretty simple -- we just
726  * flip over the conditional flag.
727  */
728 
729 elsecmd()
730 {
731 
732 	switch (cond) {
733 	case CANY:
734 		printf("\"Else\" without matching \"if\"\n");
735 		return(1);
736 
737 	case CSEND:
738 		cond = CRCV;
739 		break;
740 
741 	case CRCV:
742 		cond = CSEND;
743 		break;
744 
745 	default:
746 		printf("Mail's idea of conditions is screwed up\n");
747 		cond = CANY;
748 		break;
749 	}
750 	return(0);
751 }
752 
753 /*
754  * End of if statement.  Just set cond back to anything.
755  */
756 
757 endifcmd()
758 {
759 
760 	if (cond == CANY) {
761 		printf("\"Endif\" without matching \"if\"\n");
762 		return(1);
763 	}
764 	cond = CANY;
765 	return(0);
766 }
767 
768 /*
769  * Set the list of alternate names.
770  */
771 alternates(namelist)
772 	char **namelist;
773 {
774 	register int c;
775 	register char **ap, **ap2, *cp;
776 
777 	c = argcount(namelist) + 1;
778 	if (c == 1) {
779 		if (altnames == 0)
780 			return(0);
781 		for (ap = altnames; *ap; ap++)
782 			printf("%s ", *ap);
783 		printf("\n");
784 		return(0);
785 	}
786 	if (altnames != 0)
787 		cfree((char *) altnames);
788 	altnames = (char **) calloc(c, sizeof (char *));
789 	for (ap = namelist, ap2 = altnames; *ap; ap++, ap2++) {
790 		cp = (char *) calloc(strlen(*ap) + 1, sizeof (char));
791 		strcpy(cp, *ap);
792 		*ap2 = cp;
793 	}
794 	*ap2 = 0;
795 	return(0);
796 }
797