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