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