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