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