xref: /dragonfly/usr.bin/mail/cmd3.c (revision 6e285212)
1 /*
2  * Copyright (c) 1980, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *	This product includes software developed by the University of
16  *	California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * @(#)cmd3.c	8.2 (Berkeley) 4/20/95
34  * $FreeBSD: src/usr.bin/mail/cmd3.c,v 1.4.6.4 2003/01/06 05:46:03 mikeh Exp $
35  * $DragonFly: src/usr.bin/mail/cmd3.c,v 1.2 2003/06/17 04:29:28 dillon Exp $
36  */
37 
38 #include "rcv.h"
39 #include "extern.h"
40 
41 /*
42  * Mail -- a mail program
43  *
44  * Still more user commands.
45  */
46 
47 /*
48  * Process a shell escape by saving signals, ignoring signals,
49  * and forking a sh -c
50  */
51 int
52 shell(str)
53 	char *str;
54 {
55 	sig_t sigint = signal(SIGINT, SIG_IGN);
56 	char *sh;
57 	char cmd[BUFSIZ];
58 
59 	if (strlcpy(cmd, str, sizeof(cmd)) >= sizeof(cmd))
60 		return (1);
61 	if (bangexp(cmd, sizeof(cmd)) < 0)
62 		return (1);
63 	if ((sh = value("SHELL")) == NULL)
64 		sh = _PATH_CSHELL;
65 	(void)run_command(sh, 0, -1, -1, "-c", cmd, NULL);
66 	(void)signal(SIGINT, sigint);
67 	printf("!\n");
68 	return (0);
69 }
70 
71 /*
72  * Fork an interactive shell.
73  */
74 /*ARGSUSED*/
75 int
76 dosh(str)
77 	char *str;
78 {
79 	sig_t sigint = signal(SIGINT, SIG_IGN);
80 	char *sh;
81 
82 	if ((sh = value("SHELL")) == NULL)
83 		sh = _PATH_CSHELL;
84 	(void)run_command(sh, 0, -1, -1, NULL, NULL, NULL);
85 	(void)signal(SIGINT, sigint);
86 	printf("\n");
87 	return (0);
88 }
89 
90 /*
91  * Expand the shell escape by expanding unescaped !'s into the
92  * last issued command where possible.
93  */
94 int
95 bangexp(str, strsize)
96 	char *str;
97 	size_t strsize;
98 {
99 	char bangbuf[BUFSIZ];
100 	static char lastbang[BUFSIZ];
101 	char *cp, *cp2;
102 	int n, changed = 0;
103 
104 	cp = str;
105 	cp2 = bangbuf;
106 	n = sizeof(bangbuf);
107 	while (*cp != '\0') {
108 		if (*cp == '!') {
109 			if (n < strlen(lastbang)) {
110 overf:
111 				printf("Command buffer overflow\n");
112 				return (-1);
113 			}
114 			changed++;
115 			if (strlcpy(cp2, lastbang, sizeof(bangbuf) - (cp2 - bangbuf))
116 			    >= sizeof(bangbuf) - (cp2 - bangbuf))
117 				goto overf;
118 			cp2 += strlen(lastbang);
119 			n -= strlen(lastbang);
120 			cp++;
121 			continue;
122 		}
123 		if (*cp == '\\' && cp[1] == '!') {
124 			if (--n <= 1)
125 				goto overf;
126 			*cp2++ = '!';
127 			cp += 2;
128 			changed++;
129 		}
130 		if (--n <= 1)
131 			goto overf;
132 		*cp2++ = *cp++;
133 	}
134 	*cp2 = 0;
135 	if (changed) {
136 		printf("!%s\n", bangbuf);
137 		(void)fflush(stdout);
138 	}
139 	if (strlcpy(str, bangbuf, strsize) >= strsize)
140 		goto overf;
141 	if (strlcpy(lastbang, bangbuf, sizeof(lastbang)) >= sizeof(lastbang))
142 		goto overf;
143 	return (0);
144 }
145 
146 /*
147  * Print out a nice help message from some file or another.
148  */
149 
150 int
151 help()
152 {
153 	int c;
154 	FILE *f;
155 
156 	if ((f = Fopen(_PATH_HELP, "r")) == NULL) {
157 		warn("%s", _PATH_HELP);
158 		return (1);
159 	}
160 	while ((c = getc(f)) != EOF)
161 		putchar(c);
162 	(void)Fclose(f);
163 	return (0);
164 }
165 
166 /*
167  * Change user's working directory.
168  */
169 int
170 schdir(arglist)
171 	char **arglist;
172 {
173 	char *cp;
174 
175 	if (*arglist == NULL) {
176 		if (homedir == NULL)
177 			return (1);
178 		cp = homedir;
179 	} else
180 		if ((cp = expand(*arglist)) == NULL)
181 			return (1);
182 	if (chdir(cp) < 0) {
183 		warn("%s", cp);
184 		return (1);
185 	}
186 	return (0);
187 }
188 
189 int
190 respond(msgvec)
191 	int *msgvec;
192 {
193 	if (value("Replyall") == NULL && value("flipr") == NULL)
194 		return (dorespond(msgvec));
195 	else
196 		return (doRespond(msgvec));
197 }
198 
199 /*
200  * Reply to a list of messages.  Extract each name from the
201  * message header and send them off to mail1()
202  */
203 int
204 dorespond(msgvec)
205 	int *msgvec;
206 {
207 	struct message *mp;
208 	char *cp, *rcv, *replyto;
209 	char **ap;
210 	struct name *np;
211 	struct header head;
212 
213 	if (msgvec[1] != 0) {
214 		printf("Sorry, can't reply to multiple messages at once\n");
215 		return (1);
216 	}
217 	mp = &message[msgvec[0] - 1];
218 	touch(mp);
219 	dot = mp;
220 	if ((rcv = skin(hfield("from", mp))) == NULL)
221 		rcv = skin(nameof(mp, 1));
222 	if ((replyto = skin(hfield("reply-to", mp))) != NULL)
223 		np = extract(replyto, GTO);
224 	else if ((cp = skin(hfield("to", mp))) != NULL)
225 		np = extract(cp, GTO);
226 	else
227 		np = NULL;
228 	np = elide(np);
229 	/*
230 	 * Delete my name from the reply list,
231 	 * and with it, all my alternate names.
232 	 */
233 	np = delname(np, myname);
234 	if (altnames)
235 		for (ap = altnames; *ap != NULL; ap++)
236 			np = delname(np, *ap);
237 	if (np != NULL && replyto == NULL)
238 		np = cat(np, extract(rcv, GTO));
239 	else if (np == NULL) {
240 		if (replyto != NULL)
241 			printf("Empty reply-to field -- replying to author\n");
242 		np = extract(rcv, GTO);
243 	}
244 	head.h_to = np;
245 	if ((head.h_subject = hfield("subject", mp)) == NULL)
246 		head.h_subject = hfield("subj", mp);
247 	head.h_subject = reedit(head.h_subject);
248 	if (replyto == NULL && (cp = skin(hfield("cc", mp))) != NULL) {
249 		np = elide(extract(cp, GCC));
250 		np = delname(np, myname);
251 		if (altnames != 0)
252 			for (ap = altnames; *ap != NULL; ap++)
253 				np = delname(np, *ap);
254 		head.h_cc = np;
255 	} else
256 		head.h_cc = NULL;
257 	head.h_bcc = NULL;
258 	head.h_smopts = NULL;
259 	head.h_replyto = value("REPLYTO");
260 	head.h_inreplyto = skin(hfield("message-id", mp));
261 	mail1(&head, 1);
262 	return (0);
263 }
264 
265 /*
266  * Modify the subject we are replying to to begin with Re: if
267  * it does not already.
268  */
269 char *
270 reedit(subj)
271 	char *subj;
272 {
273 	char *newsubj;
274 
275 	if (subj == NULL)
276 		return (NULL);
277 	if ((subj[0] == 'r' || subj[0] == 'R') &&
278 	    (subj[1] == 'e' || subj[1] == 'E') &&
279 	    subj[2] == ':')
280 		return (subj);
281 	newsubj = salloc(strlen(subj) + 5);
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 int
291 preserve(msgvec)
292 	int *msgvec;
293 {
294 	int *ip, mesg;
295 	struct message *mp;
296 
297 	if (edit) {
298 		printf("Cannot \"preserve\" in edit mode\n");
299 		return (1);
300 	}
301 	for (ip = msgvec; *ip != 0; 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  * Mark all given messages as unread.
313  */
314 int
315 unread(msgvec)
316 	int	msgvec[];
317 {
318 	int *ip;
319 
320 	for (ip = msgvec; *ip != 0; ip++) {
321 		dot = &message[*ip-1];
322 		dot->m_flag &= ~(MREAD|MTOUCH);
323 		dot->m_flag |= MSTATUS;
324 	}
325 	return (0);
326 }
327 
328 /*
329  * Print the size of each message.
330  */
331 int
332 messize(msgvec)
333 	int *msgvec;
334 {
335 	struct message *mp;
336 	int *ip, mesg;
337 
338 	for (ip = msgvec; *ip != 0; ip++) {
339 		mesg = *ip;
340 		mp = &message[mesg-1];
341 		printf("%d: %ld/%ld\n", mesg, mp->m_lines, mp->m_size);
342 	}
343 	return (0);
344 }
345 
346 /*
347  * Quit quickly.  If we are sourcing, just pop the input level
348  * by returning an error.
349  */
350 int
351 rexit(e)
352 	int e;
353 {
354 	if (sourcing)
355 		return (1);
356 	exit(0);
357 	/*NOTREACHED*/
358 }
359 
360 /*
361  * Set or display a variable value.  Syntax is similar to that
362  * of csh.
363  */
364 int
365 set(arglist)
366 	char **arglist;
367 {
368 	struct var *vp;
369 	char *cp, *cp2;
370 	char varbuf[BUFSIZ], **ap, **p;
371 	int errs, h, s;
372 
373 	if (*arglist == NULL) {
374 		for (h = 0, s = 1; h < HSHSIZE; h++)
375 			for (vp = variables[h]; vp != NULL; vp = vp->v_link)
376 				s++;
377 		ap = (char **)salloc(s * sizeof(*ap));
378 		for (h = 0, p = ap; h < HSHSIZE; h++)
379 			for (vp = variables[h]; vp != NULL; vp = vp->v_link)
380 				*p++ = vp->v_name;
381 		*p = NULL;
382 		sort(ap);
383 		for (p = ap; *p != NULL; p++)
384 			printf("%s\t%s\n", *p, value(*p));
385 		return (0);
386 	}
387 	errs = 0;
388 	for (ap = arglist; *ap != NULL; ap++) {
389 		cp = *ap;
390 		cp2 = varbuf;
391 		while (cp2 < varbuf + sizeof(varbuf) - 1 && *cp != '=' && *cp != '\0')
392 			*cp2++ = *cp++;
393 		*cp2 = '\0';
394 		if (*cp == '\0')
395 			cp = "";
396 		else
397 			cp++;
398 		if (equal(varbuf, "")) {
399 			printf("Non-null variable name required\n");
400 			errs++;
401 			continue;
402 		}
403 		assign(varbuf, cp);
404 	}
405 	return (errs);
406 }
407 
408 /*
409  * Unset a bunch of variable values.
410  */
411 int
412 unset(arglist)
413 	char **arglist;
414 {
415 	struct var *vp, *vp2;
416 	int errs, h;
417 	char **ap;
418 
419 	errs = 0;
420 	for (ap = arglist; *ap != NULL; ap++) {
421 		if ((vp2 = lookup(*ap)) == NULL) {
422 			if (getenv(*ap))
423 				unsetenv(*ap);
424 			else if (!sourcing) {
425 				printf("\"%s\": undefined variable\n", *ap);
426 				errs++;
427 			}
428 			continue;
429 		}
430 		h = hash(*ap);
431 		if (vp2 == variables[h]) {
432 			variables[h] = variables[h]->v_link;
433 			vfree(vp2->v_name);
434 			vfree(vp2->v_value);
435 			(void)free(vp2);
436 			continue;
437 		}
438 		for (vp = variables[h]; vp->v_link != vp2; vp = vp->v_link)
439 			;
440 		vp->v_link = vp2->v_link;
441 		vfree(vp2->v_name);
442 		vfree(vp2->v_value);
443 		(void)free(vp2);
444 	}
445 	return (errs);
446 }
447 
448 /*
449  * Put add users to a group.
450  */
451 int
452 group(argv)
453 	char **argv;
454 {
455 	struct grouphead *gh;
456 	struct group *gp;
457 	char **ap, *gname, **p;
458 	int h, s;
459 
460 	if (*argv == NULL) {
461 		for (h = 0, s = 1; h < HSHSIZE; h++)
462 			for (gh = groups[h]; gh != NULL; gh = gh->g_link)
463 				s++;
464 		ap = (char **)salloc(s * sizeof(*ap));
465 		for (h = 0, p = ap; h < HSHSIZE; h++)
466 			for (gh = groups[h]; gh != NULL; gh = gh->g_link)
467 				*p++ = gh->g_name;
468 		*p = NULL;
469 		sort(ap);
470 		for (p = ap; *p != NULL; p++)
471 			printgroup(*p);
472 		return (0);
473 	}
474 	if (argv[1] == NULL) {
475 		printgroup(*argv);
476 		return (0);
477 	}
478 	gname = *argv;
479 	h = hash(gname);
480 	if ((gh = findgroup(gname)) == NULL) {
481 		gh = calloc(sizeof(*gh), 1);
482 		gh->g_name = vcopy(gname);
483 		gh->g_list = NULL;
484 		gh->g_link = groups[h];
485 		groups[h] = gh;
486 	}
487 
488 	/*
489 	 * Insert names from the command list into the group.
490 	 * Who cares if there are duplicates?  They get tossed
491 	 * later anyway.
492 	 */
493 
494 	for (ap = argv+1; *ap != NULL; ap++) {
495 		gp = calloc(sizeof(*gp), 1);
496 		gp->ge_name = vcopy(*ap);
497 		gp->ge_link = gh->g_list;
498 		gh->g_list = gp;
499 	}
500 	return (0);
501 }
502 
503 /*
504  * Sort the passed string vecotor into ascending dictionary
505  * order.
506  */
507 void
508 sort(list)
509 	char **list;
510 {
511 	char **ap;
512 
513 	for (ap = list; *ap != NULL; ap++)
514 		;
515 	if (ap-list < 2)
516 		return;
517 	qsort(list, ap-list, sizeof(*list), diction);
518 }
519 
520 /*
521  * Do a dictionary order comparison of the arguments from
522  * qsort.
523  */
524 int
525 diction(a, b)
526 	const void *a, *b;
527 {
528 	return (strcmp(*(const char **)a, *(const char **)b));
529 }
530 
531 /*
532  * The do nothing command for comments.
533  */
534 
535 /*ARGSUSED*/
536 int
537 null(e)
538 	int e;
539 {
540 	return (0);
541 }
542 
543 /*
544  * Change to another file.  With no argument, print information about
545  * the current file.
546  */
547 int
548 file(argv)
549 	char **argv;
550 {
551 
552 	if (argv[0] == NULL) {
553 		newfileinfo(0);
554 		return (0);
555 	}
556 	if (setfile(*argv) < 0)
557 		return (1);
558 	announce();
559 	return (0);
560 }
561 
562 /*
563  * Expand file names like echo
564  */
565 int
566 echo(argv)
567 	char **argv;
568 {
569 	char **ap, *cp;
570 
571 	for (ap = argv; *ap != NULL; ap++) {
572 		cp = *ap;
573 		if ((cp = expand(cp)) != NULL) {
574 			if (ap != argv)
575 				printf(" ");
576 			printf("%s", cp);
577 		}
578 	}
579 	printf("\n");
580 	return (0);
581 }
582 
583 int
584 Respond(msgvec)
585 	int *msgvec;
586 {
587 	if (value("Replyall") == NULL && value("flipr") == NULL)
588 		return (doRespond(msgvec));
589 	else
590 		return (dorespond(msgvec));
591 }
592 
593 /*
594  * Reply to a series of messages by simply mailing to the senders
595  * and not messing around with the To: and Cc: lists as in normal
596  * reply.
597  */
598 int
599 doRespond(msgvec)
600 	int msgvec[];
601 {
602 	struct header head;
603 	struct message *mp;
604 	int *ap;
605 	char *cp, *mid;
606 
607 	head.h_to = NULL;
608 	for (ap = msgvec; *ap != 0; ap++) {
609 		mp = &message[*ap - 1];
610 		touch(mp);
611 		dot = mp;
612 		if ((cp = skin(hfield("from", mp))) == NULL)
613 			cp = skin(nameof(mp, 2));
614 		head.h_to = cat(head.h_to, extract(cp, GTO));
615 		mid = skin(hfield("message-id", mp));
616 	}
617 	if (head.h_to == NULL)
618 		return (0);
619 	mp = &message[msgvec[0] - 1];
620 	if ((head.h_subject = hfield("subject", mp)) == NULL)
621 		head.h_subject = hfield("subj", mp);
622 	head.h_subject = reedit(head.h_subject);
623 	head.h_cc = NULL;
624 	head.h_bcc = NULL;
625 	head.h_smopts = NULL;
626 	head.h_replyto = value("REPLYTO");
627 	head.h_inreplyto = mid;
628 	mail1(&head, 1);
629 	return (0);
630 }
631 
632 /*
633  * Conditional commands.  These allow one to parameterize one's
634  * .mailrc and do some things if sending, others if receiving.
635  */
636 int
637 ifcmd(argv)
638 	char **argv;
639 {
640 	char *cp;
641 
642 	if (cond != CANY) {
643 		printf("Illegal nested \"if\"\n");
644 		return (1);
645 	}
646 	cond = CANY;
647 	cp = argv[0];
648 	switch (*cp) {
649 	case 'r': case 'R':
650 		cond = CRCV;
651 		break;
652 
653 	case 's': case 'S':
654 		cond = CSEND;
655 		break;
656 
657 	default:
658 		printf("Unrecognized if-keyword: \"%s\"\n", cp);
659 		return (1);
660 	}
661 	return (0);
662 }
663 
664 /*
665  * Implement 'else'.  This is pretty simple -- we just
666  * flip over the conditional flag.
667  */
668 int
669 elsecmd()
670 {
671 
672 	switch (cond) {
673 	case CANY:
674 		printf("\"Else\" without matching \"if\"\n");
675 		return (1);
676 
677 	case CSEND:
678 		cond = CRCV;
679 		break;
680 
681 	case CRCV:
682 		cond = CSEND;
683 		break;
684 
685 	default:
686 		printf("Mail's idea of conditions is screwed up\n");
687 		cond = CANY;
688 		break;
689 	}
690 	return (0);
691 }
692 
693 /*
694  * End of if statement.  Just set cond back to anything.
695  */
696 int
697 endifcmd()
698 {
699 
700 	if (cond == CANY) {
701 		printf("\"Endif\" without matching \"if\"\n");
702 		return (1);
703 	}
704 	cond = CANY;
705 	return (0);
706 }
707 
708 /*
709  * Set the list of alternate names.
710  */
711 int
712 alternates(namelist)
713 	char **namelist;
714 {
715 	int c;
716 	char **ap, **ap2, *cp;
717 
718 	c = argcount(namelist) + 1;
719 	if (c == 1) {
720 		if (altnames == 0)
721 			return (0);
722 		for (ap = altnames; *ap != NULL; ap++)
723 			printf("%s ", *ap);
724 		printf("\n");
725 		return (0);
726 	}
727 	if (altnames != 0)
728 		(void)free(altnames);
729 	altnames = calloc((unsigned)c, sizeof(char *));
730 	for (ap = namelist, ap2 = altnames; *ap != NULL; ap++, ap2++) {
731 		cp = calloc((unsigned)strlen(*ap) + 1, sizeof(char));
732 		strcpy(cp, *ap);
733 		*ap2 = cp;
734 	}
735 	*ap2 = 0;
736 	return (0);
737 }
738