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