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