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