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