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