Home | History | Annotate | Line # | Download | only in mail
list.c revision 1.3
      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[] = "from: @(#)list.c	8.2 (Berkeley) 4/19/94";
     36 static char rcsid[] = "$Id: list.c,v 1.3 1994/06/29 05:09:30 deraadt Exp $";
     37 #endif /* not lint */
     38 
     39 #include "rcv.h"
     40 #include <ctype.h>
     41 #include "extern.h"
     42 
     43 /*
     44  * Mail -- a mail program
     45  *
     46  * Message list handling.
     47  */
     48 
     49 /*
     50  * Convert the user string of message numbers and
     51  * store the numbers into vector.
     52  *
     53  * Returns the count of messages picked up or -1 on error.
     54  */
     55 int
     56 getmsglist(buf, vector, flags)
     57 	char *buf;
     58 	int *vector, flags;
     59 {
     60 	register int *ip;
     61 	register struct message *mp;
     62 
     63 	if (msgCount == 0) {
     64 		*vector = 0;
     65 		return 0;
     66 	}
     67 	if (markall(buf, flags) < 0)
     68 		return(-1);
     69 	ip = vector;
     70 	for (mp = &message[0]; mp < &message[msgCount]; mp++)
     71 		if (mp->m_flag & MMARK)
     72 			*ip++ = mp - &message[0] + 1;
     73 	*ip = 0;
     74 	return(ip - vector);
     75 }
     76 
     77 /*
     78  * Mark all messages that the user wanted from the command
     79  * line in the message structure.  Return 0 on success, -1
     80  * on error.
     81  */
     82 
     83 /*
     84  * Bit values for colon modifiers.
     85  */
     86 
     87 #define	CMNEW		01		/* New messages */
     88 #define	CMOLD		02		/* Old messages */
     89 #define	CMUNREAD	04		/* Unread messages */
     90 #define	CMDELETED	010		/* Deleted messages */
     91 #define	CMREAD		020		/* Read messages */
     92 
     93 /*
     94  * The following table describes the letters which can follow
     95  * the colon and gives the corresponding modifier bit.
     96  */
     97 
     98 struct coltab {
     99 	char	co_char;		/* What to find past : */
    100 	int	co_bit;			/* Associated modifier bit */
    101 	int	co_mask;		/* m_status bits to mask */
    102 	int	co_equal;		/* ... must equal this */
    103 } coltab[] = {
    104 	'n',		CMNEW,		MNEW,		MNEW,
    105 	'o',		CMOLD,		MNEW,		0,
    106 	'u',		CMUNREAD,	MREAD,		0,
    107 	'd',		CMDELETED,	MDELETED,	MDELETED,
    108 	'r',		CMREAD,		MREAD,		MREAD,
    109 	0,		0,		0,		0
    110 };
    111 
    112 static	int	lastcolmod;
    113 
    114 int
    115 markall(buf, f)
    116 	char buf[];
    117 	int f;
    118 {
    119 	register char **np;
    120 	register int i;
    121 	register struct message *mp;
    122 	char *namelist[NMLSIZE], *bufp;
    123 	int tok, beg, mc, star, other, valdot, colmod, colresult;
    124 
    125 	valdot = dot - &message[0] + 1;
    126 	colmod = 0;
    127 	for (i = 1; i <= msgCount; i++)
    128 		unmark(i);
    129 	bufp = buf;
    130 	mc = 0;
    131 	np = &namelist[0];
    132 	scaninit();
    133 	tok = scan(&bufp);
    134 	star = 0;
    135 	other = 0;
    136 	beg = 0;
    137 	while (tok != TEOL) {
    138 		switch (tok) {
    139 		case TNUMBER:
    140 number:
    141 			if (star) {
    142 				printf("No numbers mixed with *\n");
    143 				return(-1);
    144 			}
    145 			mc++;
    146 			other++;
    147 			if (beg != 0) {
    148 				if (check(lexnumber, f))
    149 					return(-1);
    150 				for (i = beg; i <= lexnumber; i++)
    151 					if (f == MDELETED || (message[i - 1].m_flag & MDELETED) == 0)
    152 						mark(i);
    153 				beg = 0;
    154 				break;
    155 			}
    156 			beg = lexnumber;
    157 			if (check(beg, f))
    158 				return(-1);
    159 			tok = scan(&bufp);
    160 			regret(tok);
    161 			if (tok != TDASH) {
    162 				mark(beg);
    163 				beg = 0;
    164 			}
    165 			break;
    166 
    167 		case TPLUS:
    168 			if (beg != 0) {
    169 				printf("Non-numeric second argument\n");
    170 				return(-1);
    171 			}
    172 			i = valdot;
    173 			do {
    174 				i++;
    175 				if (i > msgCount) {
    176 					printf("Referencing beyond EOF\n");
    177 					return(-1);
    178 				}
    179 			} while ((message[i - 1].m_flag & MDELETED) != f);
    180 			mark(i);
    181 			break;
    182 
    183 		case TDASH:
    184 			if (beg == 0) {
    185 				i = valdot;
    186 				do {
    187 					i--;
    188 					if (i <= 0) {
    189 						printf("Referencing before 1\n");
    190 						return(-1);
    191 					}
    192 				} while ((message[i - 1].m_flag & MDELETED) != f);
    193 				mark(i);
    194 			}
    195 			break;
    196 
    197 		case TSTRING:
    198 			if (beg != 0) {
    199 				printf("Non-numeric second argument\n");
    200 				return(-1);
    201 			}
    202 			other++;
    203 			if (lexstring[0] == ':') {
    204 				colresult = evalcol(lexstring[1]);
    205 				if (colresult == 0) {
    206 					printf("Unknown colon modifier \"%s\"\n",
    207 					    lexstring);
    208 					return(-1);
    209 				}
    210 				colmod |= colresult;
    211 			}
    212 			else
    213 				*np++ = savestr(lexstring);
    214 			break;
    215 
    216 		case TDOLLAR:
    217 		case TUP:
    218 		case TDOT:
    219 			lexnumber = metamess(lexstring[0], f);
    220 			if (lexnumber == -1)
    221 				return(-1);
    222 			goto number;
    223 
    224 		case TSTAR:
    225 			if (other) {
    226 				printf("Can't mix \"*\" with anything\n");
    227 				return(-1);
    228 			}
    229 			star++;
    230 			break;
    231 
    232 		case TERROR:
    233 			return -1;
    234 		}
    235 		tok = scan(&bufp);
    236 	}
    237 	lastcolmod = colmod;
    238 	*np = NOSTR;
    239 	mc = 0;
    240 	if (star) {
    241 		for (i = 0; i < msgCount; i++)
    242 			if ((message[i].m_flag & MDELETED) == f) {
    243 				mark(i+1);
    244 				mc++;
    245 			}
    246 		if (mc == 0) {
    247 			printf("No applicable messages.\n");
    248 			return(-1);
    249 		}
    250 		return(0);
    251 	}
    252 
    253 	/*
    254 	 * If no numbers were given, mark all of the messages,
    255 	 * so that we can unmark any whose sender was not selected
    256 	 * if any user names were given.
    257 	 */
    258 
    259 	if ((np > namelist || colmod != 0) && mc == 0)
    260 		for (i = 1; i <= msgCount; i++)
    261 			if ((message[i-1].m_flag & MDELETED) == f)
    262 				mark(i);
    263 
    264 	/*
    265 	 * If any names were given, go through and eliminate any
    266 	 * messages whose senders were not requested.
    267 	 */
    268 
    269 	if (np > namelist) {
    270 		for (i = 1; i <= msgCount; i++) {
    271 			for (mc = 0, np = &namelist[0]; *np != NOSTR; np++)
    272 				if (**np == '/') {
    273 					if (matchsubj(*np, i)) {
    274 						mc++;
    275 						break;
    276 					}
    277 				}
    278 				else {
    279 					if (matchsender(*np, i)) {
    280 						mc++;
    281 						break;
    282 					}
    283 				}
    284 			if (mc == 0)
    285 				unmark(i);
    286 		}
    287 
    288 		/*
    289 		 * Make sure we got some decent messages.
    290 		 */
    291 
    292 		mc = 0;
    293 		for (i = 1; i <= msgCount; i++)
    294 			if (message[i-1].m_flag & MMARK) {
    295 				mc++;
    296 				break;
    297 			}
    298 		if (mc == 0) {
    299 			printf("No applicable messages from {%s",
    300 				namelist[0]);
    301 			for (np = &namelist[1]; *np != NOSTR; np++)
    302 				printf(", %s", *np);
    303 			printf("}\n");
    304 			return(-1);
    305 		}
    306 	}
    307 
    308 	/*
    309 	 * If any colon modifiers were given, go through and
    310 	 * unmark any messages which do not satisfy the modifiers.
    311 	 */
    312 
    313 	if (colmod != 0) {
    314 		for (i = 1; i <= msgCount; i++) {
    315 			register struct coltab *colp;
    316 
    317 			mp = &message[i - 1];
    318 			for (colp = &coltab[0]; colp->co_char; colp++)
    319 				if (colp->co_bit & colmod)
    320 					if ((mp->m_flag & colp->co_mask)
    321 					    != colp->co_equal)
    322 						unmark(i);
    323 
    324 		}
    325 		for (mp = &message[0]; mp < &message[msgCount]; mp++)
    326 			if (mp->m_flag & MMARK)
    327 				break;
    328 		if (mp >= &message[msgCount]) {
    329 			register struct coltab *colp;
    330 
    331 			printf("No messages satisfy");
    332 			for (colp = &coltab[0]; colp->co_char; colp++)
    333 				if (colp->co_bit & colmod)
    334 					printf(" :%c", colp->co_char);
    335 			printf("\n");
    336 			return(-1);
    337 		}
    338 	}
    339 	return(0);
    340 }
    341 
    342 /*
    343  * Turn the character after a colon modifier into a bit
    344  * value.
    345  */
    346 int
    347 evalcol(col)
    348 	int col;
    349 {
    350 	register struct coltab *colp;
    351 
    352 	if (col == 0)
    353 		return(lastcolmod);
    354 	for (colp = &coltab[0]; colp->co_char; colp++)
    355 		if (colp->co_char == col)
    356 			return(colp->co_bit);
    357 	return(0);
    358 }
    359 
    360 /*
    361  * Check the passed message number for legality and proper flags.
    362  * If f is MDELETED, then either kind will do.  Otherwise, the message
    363  * has to be undeleted.
    364  */
    365 int
    366 check(mesg, f)
    367 	int mesg, f;
    368 {
    369 	register struct message *mp;
    370 
    371 	if (mesg < 1 || mesg > msgCount) {
    372 		printf("%d: Invalid message number\n", mesg);
    373 		return(-1);
    374 	}
    375 	mp = &message[mesg-1];
    376 	if (f != MDELETED && (mp->m_flag & MDELETED) != 0) {
    377 		printf("%d: Inappropriate message\n", mesg);
    378 		return(-1);
    379 	}
    380 	return(0);
    381 }
    382 
    383 /*
    384  * Scan out the list of string arguments, shell style
    385  * for a RAWLIST.
    386  */
    387 int
    388 getrawlist(line, argv, argc)
    389 	char line[];
    390 	char **argv;
    391 	int  argc;
    392 {
    393 	register char c, *cp, *cp2, quotec;
    394 	int argn;
    395 	char linebuf[BUFSIZ];
    396 
    397 	argn = 0;
    398 	cp = line;
    399 	for (;;) {
    400 		for (; *cp == ' ' || *cp == '\t'; cp++)
    401 			;
    402 		if (*cp == '\0')
    403 			break;
    404 		if (argn >= argc - 1) {
    405 			printf(
    406 			"Too many elements in the list; excess discarded.\n");
    407 			break;
    408 		}
    409 		cp2 = linebuf;
    410 		quotec = '\0';
    411 		while ((c = *cp) != '\0') {
    412 			cp++;
    413 			if (quotec != '\0') {
    414 				if (c == quotec)
    415 					quotec = '\0';
    416 				else if (c == '\\')
    417 					switch (c = *cp++) {
    418 					case '\0':
    419 						*cp2++ = '\\';
    420 						cp--;
    421 						break;
    422 					case '0': case '1': case '2': case '3':
    423 					case '4': case '5': case '6': case '7':
    424 						c -= '0';
    425 						if (*cp >= '0' && *cp <= '7')
    426 							c = c * 8 + *cp++ - '0';
    427 						if (*cp >= '0' && *cp <= '7')
    428 							c = c * 8 + *cp++ - '0';
    429 						*cp2++ = c;
    430 						break;
    431 					case 'b':
    432 						*cp2++ = '\b';
    433 						break;
    434 					case 'f':
    435 						*cp2++ = '\f';
    436 						break;
    437 					case 'n':
    438 						*cp2++ = '\n';
    439 						break;
    440 					case 'r':
    441 						*cp2++ = '\r';
    442 						break;
    443 					case 't':
    444 						*cp2++ = '\t';
    445 						break;
    446 					case 'v':
    447 						*cp2++ = '\v';
    448 						break;
    449 					default:
    450 						*cp2++ = c;
    451 					}
    452 				else if (c == '^') {
    453 					c = *cp++;
    454 					if (c == '?')
    455 						*cp2++ = '\177';
    456 					/* null doesn't show up anyway */
    457 					else if (c >= 'A' && c <= '_' ||
    458 						 c >= 'a' && c <= 'z')
    459 						*cp2++ = c & 037;
    460 					else {
    461 						*cp2++ = '^';
    462 						cp--;
    463 					}
    464 				} else
    465 					*cp2++ = c;
    466 			} else if (c == '"' || c == '\'')
    467 				quotec = c;
    468 			else if (c == ' ' || c == '\t')
    469 				break;
    470 			else
    471 				*cp2++ = c;
    472 		}
    473 		*cp2 = '\0';
    474 		argv[argn++] = savestr(linebuf);
    475 	}
    476 	argv[argn] = NOSTR;
    477 	return argn;
    478 }
    479 
    480 /*
    481  * scan out a single lexical item and return its token number,
    482  * updating the string pointer passed **p.  Also, store the value
    483  * of the number or string scanned in lexnumber or lexstring as
    484  * appropriate.  In any event, store the scanned `thing' in lexstring.
    485  */
    486 
    487 struct lex {
    488 	char	l_char;
    489 	char	l_token;
    490 } singles[] = {
    491 	'$',	TDOLLAR,
    492 	'.',	TDOT,
    493 	'^',	TUP,
    494 	'*',	TSTAR,
    495 	'-',	TDASH,
    496 	'+',	TPLUS,
    497 	'(',	TOPEN,
    498 	')',	TCLOSE,
    499 	0,	0
    500 };
    501 
    502 int
    503 scan(sp)
    504 	char **sp;
    505 {
    506 	register char *cp, *cp2;
    507 	register int c;
    508 	register struct lex *lp;
    509 	int quotec;
    510 
    511 	if (regretp >= 0) {
    512 		strcpy(lexstring, string_stack[regretp]);
    513 		lexnumber = numberstack[regretp];
    514 		return(regretstack[regretp--]);
    515 	}
    516 	cp = *sp;
    517 	cp2 = lexstring;
    518 	c = *cp++;
    519 
    520 	/*
    521 	 * strip away leading white space.
    522 	 */
    523 
    524 	while (c == ' ' || c == '\t')
    525 		c = *cp++;
    526 
    527 	/*
    528 	 * If no characters remain, we are at end of line,
    529 	 * so report that.
    530 	 */
    531 
    532 	if (c == '\0') {
    533 		*sp = --cp;
    534 		return(TEOL);
    535 	}
    536 
    537 	/*
    538 	 * If the leading character is a digit, scan
    539 	 * the number and convert it on the fly.
    540 	 * Return TNUMBER when done.
    541 	 */
    542 
    543 	if (isdigit(c)) {
    544 		lexnumber = 0;
    545 		while (isdigit(c)) {
    546 			lexnumber = lexnumber*10 + c - '0';
    547 			*cp2++ = c;
    548 			c = *cp++;
    549 		}
    550 		*cp2 = '\0';
    551 		*sp = --cp;
    552 		return(TNUMBER);
    553 	}
    554 
    555 	/*
    556 	 * Check for single character tokens; return such
    557 	 * if found.
    558 	 */
    559 
    560 	for (lp = &singles[0]; lp->l_char != 0; lp++)
    561 		if (c == lp->l_char) {
    562 			lexstring[0] = c;
    563 			lexstring[1] = '\0';
    564 			*sp = cp;
    565 			return(lp->l_token);
    566 		}
    567 
    568 	/*
    569 	 * We've got a string!  Copy all the characters
    570 	 * of the string into lexstring, until we see
    571 	 * a null, space, or tab.
    572 	 * If the lead character is a " or ', save it
    573 	 * and scan until you get another.
    574 	 */
    575 
    576 	quotec = 0;
    577 	if (c == '\'' || c == '"') {
    578 		quotec = c;
    579 		c = *cp++;
    580 	}
    581 	while (c != '\0') {
    582 		if (c == quotec) {
    583 			cp++;
    584 			break;
    585 		}
    586 		if (quotec == 0 && (c == ' ' || c == '\t'))
    587 			break;
    588 		if (cp2 - lexstring < STRINGLEN-1)
    589 			*cp2++ = c;
    590 		c = *cp++;
    591 	}
    592 	if (quotec && c == 0) {
    593 		fprintf(stderr, "Missing %c\n", quotec);
    594 		return TERROR;
    595 	}
    596 	*sp = --cp;
    597 	*cp2 = '\0';
    598 	return(TSTRING);
    599 }
    600 
    601 /*
    602  * Unscan the named token by pushing it onto the regret stack.
    603  */
    604 void
    605 regret(token)
    606 	int token;
    607 {
    608 	if (++regretp >= REGDEP)
    609 		panic("Too many regrets");
    610 	regretstack[regretp] = token;
    611 	lexstring[STRINGLEN-1] = '\0';
    612 	string_stack[regretp] = savestr(lexstring);
    613 	numberstack[regretp] = lexnumber;
    614 }
    615 
    616 /*
    617  * Reset all the scanner global variables.
    618  */
    619 void
    620 scaninit()
    621 {
    622 	regretp = -1;
    623 }
    624 
    625 /*
    626  * Find the first message whose flags & m == f  and return
    627  * its message number.
    628  */
    629 int
    630 first(f, m)
    631 	int f, m;
    632 {
    633 	register struct message *mp;
    634 
    635 	if (msgCount == 0)
    636 		return 0;
    637 	f &= MDELETED;
    638 	m &= MDELETED;
    639 	for (mp = dot; mp < &message[msgCount]; mp++)
    640 		if ((mp->m_flag & m) == f)
    641 			return mp - message + 1;
    642 	for (mp = dot-1; mp >= &message[0]; mp--)
    643 		if ((mp->m_flag & m) == f)
    644 			return mp - message + 1;
    645 	return 0;
    646 }
    647 
    648 /*
    649  * See if the passed name sent the passed message number.  Return true
    650  * if so.
    651  */
    652 int
    653 matchsender(str, mesg)
    654 	char *str;
    655 	int mesg;
    656 {
    657 	register char *cp, *cp2, *backup;
    658 
    659 	if (!*str)	/* null string matches nothing instead of everything */
    660 		return 0;
    661 	backup = cp2 = nameof(&message[mesg - 1], 0);
    662 	cp = str;
    663 	while (*cp2) {
    664 		if (*cp == 0)
    665 			return(1);
    666 		if (raise(*cp++) != raise(*cp2++)) {
    667 			cp2 = ++backup;
    668 			cp = str;
    669 		}
    670 	}
    671 	return(*cp == 0);
    672 }
    673 
    674 /*
    675  * See if the given string matches inside the subject field of the
    676  * given message.  For the purpose of the scan, we ignore case differences.
    677  * If it does, return true.  The string search argument is assumed to
    678  * have the form "/search-string."  If it is of the form "/," we use the
    679  * previous search string.
    680  */
    681 
    682 char lastscan[128];
    683 int
    684 matchsubj(str, mesg)
    685 	char *str;
    686 	int mesg;
    687 {
    688 	register struct message *mp;
    689 	register char *cp, *cp2, *backup;
    690 
    691 	str++;
    692 	if (strlen(str) == 0)
    693 		str = lastscan;
    694 	else
    695 		strcpy(lastscan, str);
    696 	mp = &message[mesg-1];
    697 
    698 	/*
    699 	 * Now look, ignoring case, for the word in the string.
    700 	 */
    701 
    702 	if (value("searchheaders") && (cp = index(str, ':'))) {
    703 		*cp++ = '\0';
    704 		cp2 = hfield(str, mp);
    705 		cp[-1] = ':';
    706 		str = cp;
    707 	} else {
    708 		cp = str;
    709 		cp2 = hfield("subject", mp);
    710 	}
    711 	if (cp2 == NOSTR)
    712 		return(0);
    713 	backup = cp2;
    714 	while (*cp2) {
    715 		if (*cp == 0)
    716 			return(1);
    717 		if (raise(*cp++) != raise(*cp2++)) {
    718 			cp2 = ++backup;
    719 			cp = str;
    720 		}
    721 	}
    722 	return(*cp == 0);
    723 }
    724 
    725 /*
    726  * Mark the named message by setting its mark bit.
    727  */
    728 void
    729 mark(mesg)
    730 	int mesg;
    731 {
    732 	register int i;
    733 
    734 	i = mesg;
    735 	if (i < 1 || i > msgCount)
    736 		panic("Bad message number to mark");
    737 	message[i-1].m_flag |= MMARK;
    738 }
    739 
    740 /*
    741  * Unmark the named message.
    742  */
    743 void
    744 unmark(mesg)
    745 	int mesg;
    746 {
    747 	register int i;
    748 
    749 	i = mesg;
    750 	if (i < 1 || i > msgCount)
    751 		panic("Bad message number to unmark");
    752 	message[i-1].m_flag &= ~MMARK;
    753 }
    754 
    755 /*
    756  * Return the message number corresponding to the passed meta character.
    757  */
    758 int
    759 metamess(meta, f)
    760 	int meta, f;
    761 {
    762 	register int c, m;
    763 	register struct message *mp;
    764 
    765 	c = meta;
    766 	switch (c) {
    767 	case '^':
    768 		/*
    769 		 * First 'good' message left.
    770 		 */
    771 		for (mp = &message[0]; mp < &message[msgCount]; mp++)
    772 			if ((mp->m_flag & MDELETED) == f)
    773 				return(mp - &message[0] + 1);
    774 		printf("No applicable messages\n");
    775 		return(-1);
    776 
    777 	case '$':
    778 		/*
    779 		 * Last 'good message left.
    780 		 */
    781 		for (mp = &message[msgCount-1]; mp >= &message[0]; mp--)
    782 			if ((mp->m_flag & MDELETED) == f)
    783 				return(mp - &message[0] + 1);
    784 		printf("No applicable messages\n");
    785 		return(-1);
    786 
    787 	case '.':
    788 		/*
    789 		 * Current message.
    790 		 */
    791 		m = dot - &message[0] + 1;
    792 		if ((dot->m_flag & MDELETED) != f) {
    793 			printf("%d: Inappropriate message\n", m);
    794 			return(-1);
    795 		}
    796 		return(m);
    797 
    798 	default:
    799 		printf("Unknown metachar (%c)\n", c);
    800 		return(-1);
    801 	}
    802 }
    803