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