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