cmd3.c revision 32189
179455Sobrien/*
279455Sobrien * Copyright (c) 1980, 1993
379455Sobrien *	The Regents of the University of California.  All rights reserved.
479455Sobrien *
579455Sobrien * Redistribution and use in source and binary forms, with or without
679455Sobrien * modification, are permitted provided that the following conditions
779455Sobrien * are met:
879455Sobrien * 1. Redistributions of source code must retain the above copyright
979455Sobrien *    notice, this list of conditions and the following disclaimer.
1079455Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1179455Sobrien *    notice, this list of conditions and the following disclaimer in the
1279455Sobrien *    documentation and/or other materials provided with the distribution.
1379455Sobrien * 3. All advertising materials mentioning features or use of this software
1479455Sobrien *    must display the following acknowledgement:
1579455Sobrien *	This product includes software developed by the University of
1679455Sobrien *	California, Berkeley and its contributors.
1779455Sobrien * 4. Neither the name of the University nor the names of its contributors
1879455Sobrien *    may be used to endorse or promote products derived from this software
1979455Sobrien *    without specific prior written permission.
2079455Sobrien *
2179455Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2279455Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2379455Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2479455Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2579455Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2679455Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2779455Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2879455Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29241806Suqs * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3079455Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3179455Sobrien * SUCH DAMAGE.
3279455Sobrien */
3379455Sobrien
3479455Sobrien#ifndef lint
3579455Sobrienstatic char sccsid[] = "@(#)cmd3.c	8.1 (Berkeley) 6/6/93";
3679455Sobrien#endif /* not lint */
3779455Sobrien
3879455Sobrien#include "rcv.h"
3979455Sobrien#include "extern.h"
4079455Sobrien
4179455Sobrien/*
4279455Sobrien * Mail -- a mail program
4379455Sobrien *
44121726Strhodes * Still more user commands.
4579455Sobrien */
4679455Sobrien
4779455Sobrien/*
4879455Sobrien * Process a shell escape by saving signals, ignoring signals,
49203872Skib * and forking a sh -c
50203872Skib */
5179455Sobrienint
5279455Sobrienshell(str)
5379455Sobrien	char *str;
5479455Sobrien{
5579455Sobrien	sig_t sigint = signal(SIGINT, SIG_IGN);
5679455Sobrien	char *shell;
5779455Sobrien	char cmd[BUFSIZ];
5879455Sobrien
5979455Sobrien	(void) strcpy(cmd, str);
6079455Sobrien	if (bangexp(cmd) < 0)
6179455Sobrien		return 1;
6279455Sobrien	if ((shell = value("SHELL")) == NOSTR)
6379455Sobrien		shell = _PATH_CSHELL;
6479455Sobrien	(void) run_command(shell, 0, -1, -1, "-c", cmd, NOSTR);
6579455Sobrien	(void) signal(SIGINT, sigint);
6679455Sobrien	printf("!\n");
6779455Sobrien	return 0;
6879455Sobrien}
6979455Sobrien
70241806Suqs/*
7179455Sobrien * Fork an interactive shell.
7279455Sobrien */
7379455Sobrien/*ARGSUSED*/
7479455Sobrienint
7579455Sobriendosh(str)
7679455Sobrien	char *str;
7779455Sobrien{
7879455Sobrien	sig_t sigint = signal(SIGINT, SIG_IGN);
7979455Sobrien	char *shell;
80125486Sbde
81125486Sbde	if ((shell = value("SHELL")) == NOSTR)
82123873Strhodes		shell = _PATH_CSHELL;
83123873Strhodes	(void) run_command(shell, 0, -1, -1, NOSTR, NOSTR, NOSTR);
84123873Strhodes	(void) signal(SIGINT, sigint);
85123873Strhodes	putchar('\n');
86123873Strhodes	return 0;
8779455Sobrien}
8879455Sobrien
8979455Sobrien/*
9079455Sobrien * Expand the shell escape by expanding unescaped !'s into the
9179455Sobrien * last issued command where possible.
9279455Sobrien */
9379455Sobrien
9479455Sobrienchar	lastbang[128];
9579455Sobrien
9679455Sobrienint
9779455Sobrienbangexp(str)
9879455Sobrien	char *str;
9979455Sobrien{
10079455Sobrien	char bangbuf[BUFSIZ];
101209364Sbrian	register char *cp, *cp2;
10279455Sobrien	register int n;
10379455Sobrien	int changed = 0;
10479455Sobrien
10579455Sobrien	cp = str;
10679455Sobrien	cp2 = bangbuf;
10779455Sobrien	n = BUFSIZ;
10879455Sobrien	while (*cp) {
10979455Sobrien		if (*cp == '!') {
11079455Sobrien			if (n < strlen(lastbang)) {
11179455Sobrienoverf:
11279455Sobrien				printf("Command buffer overflow\n");
11379455Sobrien				return(-1);
11479455Sobrien			}
11579455Sobrien			changed++;
11679455Sobrien			strcpy(cp2, lastbang);
11779455Sobrien			cp2 += strlen(lastbang);
11879455Sobrien			n -= strlen(lastbang);
11979455Sobrien			cp++;
12079455Sobrien			continue;
12179455Sobrien		}
12279455Sobrien		if (*cp == '\\' && cp[1] == '!') {
12379455Sobrien			if (--n <= 1)
12479455Sobrien				goto overf;
12579455Sobrien			*cp2++ = '!';
12679455Sobrien			cp += 2;
12779455Sobrien			changed++;
12879455Sobrien		}
12979455Sobrien		if (--n <= 1)
13079455Sobrien			goto overf;
13179455Sobrien		*cp2++ = *cp++;
13279455Sobrien	}
13379455Sobrien	*cp2 = 0;
13479455Sobrien	if (changed) {
13579455Sobrien		printf("!%s\n", bangbuf);
13679455Sobrien		fflush(stdout);
13779455Sobrien	}
13879455Sobrien	strcpy(str, bangbuf);
13979455Sobrien	strncpy(lastbang, bangbuf, 128);
14079455Sobrien	lastbang[127] = 0;
14179455Sobrien	return(0);
14279455Sobrien}
14379455Sobrien
14479455Sobrien/*
14579455Sobrien * Print out a nice help message from some file or another.
14679455Sobrien */
14779455Sobrien
14879455Sobrienint
14979455Sobrienhelp()
15079455Sobrien{
15179455Sobrien	register c;
15279455Sobrien	register FILE *f;
15379455Sobrien
15479455Sobrien	if ((f = Fopen(_PATH_HELP, "r")) == NULL) {
15579455Sobrien		perror(_PATH_HELP);
15679455Sobrien		return(1);
15779455Sobrien	}
15879455Sobrien	while ((c = getc(f)) != EOF)
15979455Sobrien		putchar(c);
16079455Sobrien	Fclose(f);
16179455Sobrien	return(0);
16279455Sobrien}
16379455Sobrien
16479455Sobrien/*
16579455Sobrien * Change user's working directory.
16679455Sobrien */
16779455Sobrienint
16879455Sobrienschdir(arglist)
16979455Sobrien	char **arglist;
17079455Sobrien{
17179455Sobrien	char *cp;
17279455Sobrien
173102231Strhodes	if (*arglist == NOSTR)
17479455Sobrien		cp = homedir;
17579455Sobrien	else
17679455Sobrien		if ((cp = expand(*arglist)) == NOSTR)
17779455Sobrien			return(1);
17879455Sobrien	if (chdir(cp) < 0) {
17979455Sobrien		perror(cp);
18079455Sobrien		return(1);
18179455Sobrien	}
18279455Sobrien	return 0;
18379455Sobrien}
18479455Sobrien
18579455Sobrienint
18679455Sobrienrespond(msgvec)
18779455Sobrien	int *msgvec;
18879455Sobrien{
18979455Sobrien	if (value("Replyall") == NOSTR)
19079455Sobrien		return (dorespond(msgvec));
19179455Sobrien	else
19279455Sobrien		return (doRespond(msgvec));
19379455Sobrien}
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
200dorespond(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	if ((head.h_replyto = getenv("REPLYTO")) == NULL)
256		head.h_replyto = NOSTR;
257	head.h_inreplyto = skin(hfield("message-id", mp));
258	mail1(&head, 1);
259	return(0);
260}
261
262/*
263 * Modify the subject we are replying to to begin with Re: if
264 * it does not already.
265 */
266char *
267reedit(subj)
268	register char *subj;
269{
270	char *newsubj;
271
272	if (subj == NOSTR)
273		return NOSTR;
274	if ((subj[0] == 'r' || subj[0] == 'R') &&
275	    (subj[1] == 'e' || subj[1] == 'E') &&
276	    subj[2] == ':')
277		return subj;
278	newsubj = salloc(strlen(subj) + 5);
279	strcpy(newsubj, "Re: ");
280	strcpy(newsubj + 4, subj);
281	return newsubj;
282}
283
284/*
285 * Preserve the named messages, so that they will be sent
286 * back to the system mailbox.
287 */
288int
289preserve(msgvec)
290	int *msgvec;
291{
292	register struct message *mp;
293	register int *ip, mesg;
294
295	if (edit) {
296		printf("Cannot \"preserve\" in edit mode\n");
297		return(1);
298	}
299	for (ip = msgvec; *ip != 0; ip++) {
300		mesg = *ip;
301		mp = &message[mesg-1];
302		mp->m_flag |= MPRESERVE;
303		mp->m_flag &= ~MBOX;
304		dot = mp;
305	}
306	return(0);
307}
308
309/*
310 * Mark all given messages as unread.
311 */
312int
313unread(msgvec)
314	int	msgvec[];
315{
316	register int *ip;
317
318	for (ip = msgvec; *ip != 0; ip++) {
319		dot = &message[*ip-1];
320		dot->m_flag &= ~(MREAD|MTOUCH);
321		dot->m_flag |= MSTATUS;
322	}
323	return(0);
324}
325
326/*
327 * Print the size of each message.
328 */
329int
330messize(msgvec)
331	int *msgvec;
332{
333	register struct message *mp;
334	register int *ip, mesg;
335
336	for (ip = msgvec; *ip != 0; ip++) {
337		mesg = *ip;
338		mp = &message[mesg-1];
339		printf("%d: %d/%ld\n", mesg, mp->m_lines, mp->m_size);
340	}
341	return(0);
342}
343
344/*
345 * Quit quickly.  If we are sourcing, just pop the input level
346 * by returning an error.
347 */
348int
349rexit(e)
350	int e;
351{
352	if (sourcing)
353		return(1);
354	exit(e);
355	/*NOTREACHED*/
356}
357
358/*
359 * Set or display a variable value.  Syntax is similar to that
360 * of csh.
361 */
362int
363set(arglist)
364	char **arglist;
365{
366	register struct var *vp;
367	register char *cp, *cp2;
368	char varbuf[BUFSIZ], **ap, **p;
369	int errs, h, s;
370
371	if (*arglist == NOSTR) {
372		for (h = 0, s = 1; h < HSHSIZE; h++)
373			for (vp = variables[h]; vp != NOVAR; vp = vp->v_link)
374				s++;
375		ap = (char **) salloc(s * sizeof *ap);
376		for (h = 0, p = ap; h < HSHSIZE; h++)
377			for (vp = variables[h]; vp != NOVAR; vp = vp->v_link)
378				*p++ = vp->v_name;
379		*p = NOSTR;
380		sort(ap);
381		for (p = ap; *p != NOSTR; p++)
382			printf("%s\t%s\n", *p, value(*p));
383		return(0);
384	}
385	errs = 0;
386	for (ap = arglist; *ap != NOSTR; ap++) {
387		cp = *ap;
388		cp2 = varbuf;
389		while (*cp != '=' && *cp != '\0')
390			*cp2++ = *cp++;
391		*cp2 = '\0';
392		if (*cp == '\0')
393			cp = "";
394		else
395			cp++;
396		if (equal(varbuf, "")) {
397			printf("Non-null variable name required\n");
398			errs++;
399			continue;
400		}
401		assign(varbuf, cp);
402	}
403	return(errs);
404}
405
406/*
407 * Unset a bunch of variable values.
408 */
409int
410unset(arglist)
411	char **arglist;
412{
413	register struct var *vp, *vp2;
414	int errs, h;
415	char **ap;
416
417	errs = 0;
418	for (ap = arglist; *ap != NOSTR; ap++) {
419		if ((vp2 = lookup(*ap)) == NOVAR) {
420			if (!sourcing) {
421				printf("\"%s\": undefined variable\n", *ap);
422				errs++;
423			}
424			continue;
425		}
426		h = hash(*ap);
427		if (vp2 == variables[h]) {
428			variables[h] = variables[h]->v_link;
429			vfree(vp2->v_name);
430			vfree(vp2->v_value);
431			free((char *)vp2);
432			continue;
433		}
434		for (vp = variables[h]; vp->v_link != vp2; vp = vp->v_link)
435			;
436		vp->v_link = vp2->v_link;
437		vfree(vp2->v_name);
438		vfree(vp2->v_value);
439		free((char *) vp2);
440	}
441	return(errs);
442}
443
444/*
445 * Put add users to a group.
446 */
447int
448group(argv)
449	char **argv;
450{
451	register struct grouphead *gh;
452	register struct group *gp;
453	register int h;
454	int s;
455	char **ap, *gname, **p;
456
457	if (*argv == NOSTR) {
458		for (h = 0, s = 1; h < HSHSIZE; h++)
459			for (gh = groups[h]; gh != NOGRP; gh = gh->g_link)
460				s++;
461		ap = (char **) salloc(s * sizeof *ap);
462		for (h = 0, p = ap; h < HSHSIZE; h++)
463			for (gh = groups[h]; gh != NOGRP; gh = gh->g_link)
464				*p++ = gh->g_name;
465		*p = NOSTR;
466		sort(ap);
467		for (p = ap; *p != NOSTR; p++)
468			printgroup(*p);
469		return(0);
470	}
471	if (argv[1] == NOSTR) {
472		printgroup(*argv);
473		return(0);
474	}
475	gname = *argv;
476	h = hash(gname);
477	if ((gh = findgroup(gname)) == NOGRP) {
478		gh = (struct grouphead *) calloc(sizeof *gh, 1);
479		gh->g_name = vcopy(gname);
480		gh->g_list = NOGE;
481		gh->g_link = groups[h];
482		groups[h] = gh;
483	}
484
485	/*
486	 * Insert names from the command list into the group.
487	 * Who cares if there are duplicates?  They get tossed
488	 * later anyway.
489	 */
490
491	for (ap = argv+1; *ap != NOSTR; ap++) {
492		gp = (struct group *) calloc(sizeof *gp, 1);
493		gp->ge_name = vcopy(*ap);
494		gp->ge_link = gh->g_list;
495		gh->g_list = gp;
496	}
497	return(0);
498}
499
500/*
501 * Sort the passed string vecotor into ascending dictionary
502 * order.
503 */
504void
505sort(list)
506	char **list;
507{
508	register char **ap;
509	int diction();
510
511	for (ap = list; *ap != NOSTR; ap++)
512		;
513	if (ap-list < 2)
514		return;
515	qsort(list, ap-list, sizeof(*list), diction);
516}
517
518/*
519 * Do a dictionary order comparison of the arguments from
520 * qsort.
521 */
522int
523diction(a, b)
524	const void *a, *b;
525{
526	return(strcmp(*(char **)a, *(char **)b));
527}
528
529/*
530 * The do nothing command for comments.
531 */
532
533/*ARGSUSED*/
534int
535null(e)
536	int e;
537{
538	return 0;
539}
540
541/*
542 * Change to another file.  With no argument, print information about
543 * the current file.
544 */
545int
546file(argv)
547	register char **argv;
548{
549
550	if (argv[0] == NOSTR) {
551		newfileinfo();
552		return 0;
553	}
554	if (setfile(*argv) < 0)
555		return 1;
556	announce();
557	return 0;
558}
559
560/*
561 * Expand file names like echo
562 */
563int
564echo(argv)
565	char **argv;
566{
567	register char **ap;
568	register char *cp;
569
570	for (ap = argv; *ap != NOSTR; ap++) {
571		cp = *ap;
572		if ((cp = expand(cp)) != NOSTR) {
573			if (ap != argv)
574				putchar(' ');
575			printf("%s", cp);
576		}
577	}
578	putchar('\n');
579	return 0;
580}
581
582int
583Respond(msgvec)
584	int *msgvec;
585{
586	if (value("Replyall") == NOSTR)
587		return (doRespond(msgvec));
588	else
589		return (dorespond(msgvec));
590}
591
592/*
593 * Reply to a series of messages by simply mailing to the senders
594 * and not messing around with the To: and Cc: lists as in normal
595 * reply.
596 */
597int
598doRespond(msgvec)
599	int msgvec[];
600{
601	struct header head;
602	struct message *mp;
603	register int *ap;
604	register char *cp;
605	char *mid;
606
607	head.h_to = NIL;
608	for (ap = msgvec; *ap != 0; ap++) {
609		mp = &message[*ap - 1];
610		touch(mp);
611		dot = mp;
612		if ((cp = skin(hfield("from", mp))) == NOSTR)
613			cp = skin(nameof(mp, 2));
614		head.h_to = cat(head.h_to, extract(cp, GTO));
615		mid = skin(hfield("message-id", mp));
616	}
617	if (head.h_to == NIL)
618		return 0;
619	mp = &message[msgvec[0] - 1];
620	if ((head.h_subject = hfield("subject", mp)) == NOSTR)
621		head.h_subject = hfield("subj", mp);
622	head.h_subject = reedit(head.h_subject);
623	head.h_cc = NIL;
624	head.h_bcc = NIL;
625	head.h_smopts = NIL;
626	if ((head.h_replyto = getenv("REPLYTO")) == NULL)
627		head.h_replyto = NOSTR;
628	head.h_inreplyto = mid;
629	mail1(&head, 1);
630	return 0;
631}
632
633/*
634 * Conditional commands.  These allow one to parameterize one's
635 * .mailrc and do some things if sending, others if receiving.
636 */
637int
638ifcmd(argv)
639	char **argv;
640{
641	register char *cp;
642
643	if (cond != CANY) {
644		printf("Illegal nested \"if\"\n");
645		return(1);
646	}
647	cond = CANY;
648	cp = argv[0];
649	switch (*cp) {
650	case 'r': case 'R':
651		cond = CRCV;
652		break;
653
654	case 's': case 'S':
655		cond = CSEND;
656		break;
657
658	default:
659		printf("Unrecognized if-keyword: \"%s\"\n", cp);
660		return(1);
661	}
662	return(0);
663}
664
665/*
666 * Implement 'else'.  This is pretty simple -- we just
667 * flip over the conditional flag.
668 */
669int
670elsecmd()
671{
672
673	switch (cond) {
674	case CANY:
675		printf("\"Else\" without matching \"if\"\n");
676		return(1);
677
678	case CSEND:
679		cond = CRCV;
680		break;
681
682	case CRCV:
683		cond = CSEND;
684		break;
685
686	default:
687		printf("Mail's idea of conditions is screwed up\n");
688		cond = CANY;
689		break;
690	}
691	return(0);
692}
693
694/*
695 * End of if statement.  Just set cond back to anything.
696 */
697int
698endifcmd()
699{
700
701	if (cond == CANY) {
702		printf("\"Endif\" without matching \"if\"\n");
703		return(1);
704	}
705	cond = CANY;
706	return(0);
707}
708
709/*
710 * Set the list of alternate names.
711 */
712int
713alternates(namelist)
714	char **namelist;
715{
716	register int c;
717	register char **ap, **ap2, *cp;
718
719	c = argcount(namelist) + 1;
720	if (c == 1) {
721		if (altnames == 0)
722			return(0);
723		for (ap = altnames; *ap; ap++)
724			printf("%s ", *ap);
725		printf("\n");
726		return(0);
727	}
728	if (altnames != 0)
729		free((char *) altnames);
730	altnames = (char **) calloc((unsigned) c, sizeof (char *));
731	for (ap = namelist, ap2 = altnames; *ap; ap++, ap2++) {
732		cp = (char *) calloc((unsigned) strlen(*ap) + 1, sizeof (char));
733		strcpy(cp, *ap);
734		*ap2 = cp;
735	}
736	*ap2 = 0;
737	return(0);
738}
739