Home | History | Annotate | Line # | Download | only in mail
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
     35 static 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  */
     50 shell(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*/
     72 dosh(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 
     91 char	lastbang[128];
     92 
     93 bangexp(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)) {
    107 overf:
    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 
    144 help()
    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  */
    162 schdir(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 
    179 respond(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  */
    255 char *
    256 reedit(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 
    278 preserve(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  */
    301 unread(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 
    318 messize(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 
    337 rexit(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 
    350 set(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 
    397 unset(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 
    435 group(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 
    492 sort(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 
    510 diction(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*/
    521 null(e)
    522 {
    523 	return 0;
    524 }
    525 
    526 /*
    527  * Change to another file.  With no argument, print information about
    528  * the current file.
    529  */
    530 file(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  */
    547 echo(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 
    565 Respond(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 
    614 ifcmd(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 
    646 elsecmd()
    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 
    674 endifcmd()
    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  */
    688 alternates(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