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