Home | History | Annotate | Line # | Download | only in mail
list.c revision 1.17
      1 /*	$NetBSD: list.c,v 1.17 2006/10/31 20:07:32 christos Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1980, 1993
      5  *	The Regents of the University of California.  All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  * 3. Neither the name of the University nor the names of its contributors
     16  *    may be used to endorse or promote products derived from this software
     17  *    without specific prior written permission.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     29  * SUCH DAMAGE.
     30  */
     31 
     32 #include <sys/cdefs.h>
     33 #ifndef lint
     34 #if 0
     35 static char sccsid[] = "@(#)list.c	8.4 (Berkeley) 5/1/95";
     36 #else
     37 __RCSID("$NetBSD: list.c,v 1.17 2006/10/31 20:07:32 christos Exp $");
     38 #endif
     39 #endif /* not lint */
     40 
     41 #include "rcv.h"
     42 #include "extern.h"
     43 #include "format.h"
     44 
     45 int	matchto(char *, int);
     46 
     47 /*
     48  * Mail -- a mail program
     49  *
     50  * Message list handling.
     51  */
     52 
     53 /*
     54  * Convert the user string of message numbers and
     55  * store the numbers into vector.
     56  *
     57  * Returns the count of messages picked up or -1 on error.
     58  */
     59 int
     60 getmsglist(char *buf, int *vector, int flags)
     61 {
     62 	int *ip;
     63 	struct message *mp;
     64 
     65 	if (msgCount == 0) {
     66 		*vector = 0;
     67 		return 0;
     68 	}
     69 	if (markall(buf, flags) < 0)
     70 		return(-1);
     71 	ip = vector;
     72 	for (mp = &message[0]; mp < &message[msgCount]; mp++)
     73 		if (mp->m_flag & MMARK)
     74 			*ip++ = mp - &message[0] + 1;
     75 	*ip = 0;
     76 	return(ip - vector);
     77 }
     78 
     79 /*
     80  * Mark all messages that the user wanted from the command
     81  * line in the message structure.  Return 0 on success, -1
     82  * on error.
     83  */
     84 
     85 /*
     86  * Bit values for colon modifiers.
     87  */
     88 
     89 #define	CMNEW		01		/* New messages */
     90 #define	CMOLD		02		/* Old messages */
     91 #define	CMUNREAD	04		/* Unread messages */
     92 #define	CMDELETED	010		/* Deleted messages */
     93 #define	CMREAD		020		/* Read messages */
     94 #define CMMASK		037		/* Mask the valid bits */
     95 
     96 /*
     97  * The following table describes the letters which can follow
     98  * the colon and gives the corresponding modifier bit.
     99  */
    100 
    101 struct coltab {
    102 	char	co_char;		/* What to find past : */
    103 	int	co_bit;			/* Associated modifier bit */
    104 	int	co_mask;		/* m_status bits to mask */
    105 	int	co_equal;		/* ... must equal this */
    106 } coltab[] = {
    107 	{ 'n',		CMNEW,		MNEW,		MNEW },
    108 	{ 'o',		CMOLD,		MNEW,		0 },
    109 	{ 'u',		CMUNREAD,	MREAD,		0 },
    110 	{ 'd',		CMDELETED,	MDELETED,	MDELETED },
    111 	{ 'r',		CMREAD,		MREAD,		MREAD },
    112 	{ 0,		0,		0,		0 }
    113 };
    114 
    115 static	int	lastcolmod;
    116 
    117 
    118 static int
    119 ignore_message(int m_flag, int colmod)
    120 {
    121 	struct coltab *colp;
    122 	for (colp = &coltab[0]; colp->co_char; colp++)
    123 		if (colp->co_bit & colmod)
    124 			if ((m_flag & colp->co_mask)
    125 			    != colp->co_equal)
    126 				return 1;
    127 	return 0;
    128 }
    129 
    130 /*
    131  * Turn the character after a colon modifier into a bit
    132  * value.
    133  */
    134 static int
    135 evalcol(int col)
    136 {
    137 	struct coltab *colp;
    138 
    139 	if (col == 0)
    140 		return(lastcolmod);
    141 	for (colp = &coltab[0]; colp->co_char; colp++)
    142 		if (colp->co_char == col)
    143 			return(colp->co_bit);
    144 	return(0);
    145 }
    146 
    147 int
    148 markall(char buf[], int f)
    149 {
    150 	char **np;
    151 	int i;
    152 	struct message *mp;
    153 	char *namelist[NMLSIZE], *bufp;
    154 	int tok, beg, mc, star, other, valdot, colmod, colresult;
    155 
    156 	valdot = dot - &message[0] + 1;
    157 	colmod = 0;
    158 	for (i = 1; i <= msgCount; i++)
    159 		unmark(i);
    160 	bufp = buf;
    161 	mc = 0;
    162 	np = &namelist[0];
    163 	scaninit();
    164 	tok = scan(&bufp);
    165 	star = 0;
    166 	other = 0;
    167 	beg = 0;
    168 	while (tok != TEOL) {
    169 		switch (tok) {
    170 		case TNUMBER:
    171 number:
    172 			if (star) {
    173 				(void)printf("No numbers mixed with *\n");
    174 				return(-1);
    175 			}
    176 			mc++;
    177 			other++;
    178 			if (beg != 0) {
    179 				if (check(lexnumber, f))
    180 					return(-1);
    181 				for (i = beg; i <= lexnumber; i++)
    182 					if (f == MDELETED || (message[i - 1].m_flag & MDELETED) == 0)
    183 						mark(i);
    184 				beg = 0;
    185 				break;
    186 			}
    187 			beg = lexnumber;
    188 			if (check(beg, f))
    189 				return(-1);
    190 			tok = scan(&bufp);
    191 			regret(tok);
    192 			if (tok != TDASH) {
    193 				mark(beg);
    194 				beg = 0;
    195 			}
    196 			break;
    197 
    198 		case TPLUS:
    199 			if (beg != 0) {
    200 				(void)printf("Non-numeric second argument\n");
    201 				return(-1);
    202 			}
    203 			i = valdot;
    204 			do {
    205 				i++;
    206 				if (i > msgCount) {
    207 					(void)printf("Referencing beyond EOF\n");
    208 					return(-1);
    209 				}
    210 			} while ((message[i - 1].m_flag & MDELETED) != f);
    211 			mark(i);
    212 			break;
    213 
    214 		case TDASH:
    215 			if (beg == 0) {
    216 				i = valdot;
    217 				do {
    218 					i--;
    219 					if (i <= 0) {
    220 						(void)printf("Referencing before 1\n");
    221 						return(-1);
    222 					}
    223 				} while ((message[i - 1].m_flag & MDELETED) != f);
    224 				mark(i);
    225 			}
    226 			break;
    227 
    228 		case TSTRING:
    229 			if (beg != 0) {
    230 				(void)printf("Non-numeric second argument\n");
    231 				return(-1);
    232 			}
    233 			other++;
    234 			if (lexstring[0] == ':') {
    235 				colresult = evalcol(lexstring[1]);
    236 				if (colresult == 0) {
    237 					(void)printf("Unknown colon modifier \"%s\"\n",
    238 					    lexstring);
    239 					return(-1);
    240 				}
    241 				colmod |= colresult;
    242 			}
    243 			else
    244 				*np++ = savestr(lexstring);
    245 			break;
    246 
    247 		case TDOLLAR:
    248 		case TUP:
    249 		case TDOT:
    250 			lexnumber = metamess(lexstring[0], f);
    251 			if (lexnumber == -1)
    252 				return(-1);
    253 			goto number;
    254 
    255 		case TSTAR:
    256 			if (other) {
    257 				(void)printf("Can't mix \"*\" with anything\n");
    258 				return(-1);
    259 			}
    260 			star++;
    261 			break;
    262 
    263 		case TERROR:
    264 			return -1;
    265 		}
    266 		tok = scan(&bufp);
    267 	}
    268 	lastcolmod = colmod;
    269 	*np = NULL;
    270 	mc = 0;
    271 	if (star) {
    272 		for (i = 0; i < msgCount; i++)
    273 			if ((message[i].m_flag & MDELETED) == f) {
    274 				mark(i + 1);
    275 				mc++;
    276 			}
    277 		if (mc == 0) {
    278 			(void)printf("No applicable messages.\n");
    279 			return(-1);
    280 		}
    281 		return(0);
    282 	}
    283 
    284 	/*
    285 	 * If no numbers were given, mark all of the messages,
    286 	 * so that we can unmark any whose sender was not selected
    287 	 * if any user names were given.
    288 	 */
    289 
    290 	if ((np > namelist || colmod != 0) && mc == 0)
    291 		for (i = 1; i <= msgCount; i++)
    292 			if ((message[i - 1].m_flag & MDELETED) == f)
    293 				mark(i);
    294 
    295 	/*
    296 	 * If any names were given, go through and eliminate any
    297 	 * messages whose senders were not requested.
    298 	 */
    299 
    300 	if (np > namelist) {
    301 		for (i = 1; i <= msgCount; i++) {
    302 			for (mc = 0, np = &namelist[0]; *np != NULL; np++)
    303 				if (**np == '/') {
    304 					if (matchsubj(*np, i)) {
    305 						mc++;
    306 						break;
    307 					}
    308 				}
    309 				else {
    310 					if (matchsender(*np, i)) {
    311 						mc++;
    312 						break;
    313 					}
    314 				}
    315 			if (mc == 0)
    316 				unmark(i);
    317 		}
    318 
    319 		/*
    320 		 * Make sure we got some decent messages.
    321 		 */
    322 
    323 		mc = 0;
    324 		for (i = 1; i <= msgCount; i++)
    325 			if (message[i - 1].m_flag & MMARK) {
    326 				mc++;
    327 				break;
    328 			}
    329 		if (mc == 0) {
    330 			(void)printf("No applicable messages from {%s",
    331 				namelist[0]);
    332 			for (np = &namelist[1]; *np != NULL; np++)
    333 				(void)printf(", %s", *np);
    334 			(void)printf("}\n");
    335 			return(-1);
    336 		}
    337 	}
    338 
    339 	/*
    340 	 * If any colon modifiers were given, go through and
    341 	 * unmark any messages which do not satisfy the modifiers.
    342 	 */
    343 
    344 	if (colmod != 0) {
    345 		for (i = 1; i <= msgCount; i++)
    346 			if (ignore_message(message[i - 1].m_flag, colmod))
    347 				unmark(i);
    348 		for (mp = &message[0]; mp < &message[msgCount]; mp++)
    349 			if (mp->m_flag & MMARK)
    350 				break;
    351 		if (mp >= &message[msgCount]) {
    352 			struct coltab *colp;
    353 
    354 			(void)printf("No messages satisfy");
    355 			for (colp = &coltab[0]; colp->co_char; colp++)
    356 				if (colp->co_bit & colmod)
    357 					(void)printf(" :%c", colp->co_char);
    358 			(void)printf("\n");
    359 			return(-1);
    360 		}
    361 	}
    362 	return(0);
    363 }
    364 
    365 /*
    366  * Show all headers without paging.  (-H flag)
    367  */
    368 __attribute__((__noreturn__))
    369 int
    370 show_headers(int flags)
    371 {
    372 	int mesg;
    373 	char *cp;
    374 
    375 	if ((cp = value(ENAME_HDRONLY_SCRNWIDTH)) != NULL) {
    376 		int width;
    377 		width = *cp ? atoi(cp) : 0;
    378 		if (width >= 0)
    379 			screenwidth = width;
    380 	}
    381 	if ((cp = value(ENAME_HDRONLY_FORMAT)) != NULL)
    382 		assign(ENAME_HEADER_FORMAT, cp);
    383 
    384 	flags &= CMMASK;
    385 	for (mesg = 1; mesg <= msgCount; mesg++)
    386 		if (!ignore_message(message[mesg - 1].m_flag, flags))
    387 			printhead(mesg);
    388 	exit(0);
    389 	/* NOTREACHED */
    390 }
    391 
    392 /*
    393  * A hack so -H can have an optional modifier as -H[:flags].
    394  *
    395  * This depends a bit on the internals of getopt().  In particular,
    396  * for flags expecting an argument, argv[optind-1] must contain the
    397  * optarg and optarg must point to a substring of argv[optind-1] not a
    398  * copy of it.
    399  */
    400 int
    401 get_Hflag(char **argv)
    402 {
    403 	int flags;
    404 
    405 	flags = ~CMMASK;
    406 
    407 	if (optarg == NULL)  /* We had an error, just get the flags. */
    408 		return flags;
    409 
    410 	if (*optarg != ':' || optarg == argv[optind - 1]) {
    411 		optind--;
    412 		optreset = 1;
    413 		if (optarg != argv[optind]) {
    414 			static char temparg[LINE_MAX];
    415 			size_t optlen;
    416 			size_t arglen;
    417 			char *p;
    418 
    419 			optlen = strlen(optarg);
    420 			arglen = strlen(argv[optind]);
    421 			p = argv[optind] + arglen - optlen;
    422 			optlen = MIN(optlen, sizeof(temparg) - 1);
    423 			temparg[0] = '-';
    424 			(void)memmove(temparg + 1, p, optlen + 1);
    425 			argv[optind] = temparg;
    426 		}
    427 	}
    428 	else {
    429 		char *p;
    430 		for (p = optarg + 1; *p; p++) {
    431 			int bit;
    432 			if ((bit = evalcol(*p)) == 0)
    433 				errx(EXIT_FAILURE,
    434 				    "Unknown -H modifier '%c'", *p);
    435 			flags |= bit;
    436 		}
    437 	}
    438 	return flags;
    439 }
    440 
    441 /*
    442  * Check the passed message number for legality and proper flags.
    443  * If f is MDELETED, then either kind will do.  Otherwise, the message
    444  * has to be undeleted.
    445  */
    446 int
    447 check(int mesg, int f)
    448 {
    449 	struct message *mp;
    450 
    451 	if (mesg < 1 || mesg > msgCount) {
    452 		(void)printf("%d: Invalid message number\n", mesg);
    453 		return(-1);
    454 	}
    455 	mp = &message[mesg - 1];
    456 	if (f != MDELETED && (mp->m_flag & MDELETED) != 0) {
    457 		(void)printf("%d: Inappropriate message\n", mesg);
    458 		return(-1);
    459 	}
    460 	return(0);
    461 }
    462 
    463 /*
    464  * Scan out the list of string arguments, shell style
    465  * for a RAWLIST.
    466  */
    467 int
    468 getrawlist(const char line[], char **argv, int argc)
    469 {
    470 	char c, *cp2, quotec;
    471 	const char *cp;
    472 	int argn;
    473 	char linebuf[BUFSIZ];
    474 
    475 	argn = 0;
    476 	cp = line;
    477 	for (;;) {
    478 		for (; *cp == ' ' || *cp == '\t'; cp++)
    479 			;
    480 		if (*cp == '\0')
    481 			break;
    482 		if (argn >= argc - 1) {
    483 			(void)printf(
    484 			"Too many elements in the list; excess discarded.\n");
    485 			break;
    486 		}
    487 		cp2 = linebuf;
    488 		quotec = '\0';
    489 		while ((c = *cp) != '\0') {
    490 			cp++;
    491 			if (quotec != '\0') {
    492 				if (c == quotec)
    493 					quotec = '\0';
    494 				else if (c == '\\')
    495 					switch (c = *cp++) {
    496 					case '\0':
    497 						*cp2++ = '\\';
    498 						cp--;
    499 						break;
    500 					case '0': case '1': case '2': case '3':
    501 					case '4': case '5': case '6': case '7':
    502 						c -= '0';
    503 						if (*cp >= '0' && *cp <= '7')
    504 							c = c * 8 + *cp++ - '0';
    505 						if (*cp >= '0' && *cp <= '7')
    506 							c = c * 8 + *cp++ - '0';
    507 						*cp2++ = c;
    508 						break;
    509 					case 'b':
    510 						*cp2++ = '\b';
    511 						break;
    512 					case 'f':
    513 						*cp2++ = '\f';
    514 						break;
    515 					case 'n':
    516 						*cp2++ = '\n';
    517 						break;
    518 					case 'r':
    519 						*cp2++ = '\r';
    520 						break;
    521 					case 't':
    522 						*cp2++ = '\t';
    523 						break;
    524 					case 'v':
    525 						*cp2++ = '\v';
    526 						break;
    527 					default:
    528 						*cp2++ = c;
    529 					}
    530 				else if (c == '^') {
    531 					c = *cp++;
    532 					if (c == '?')
    533 						*cp2++ = '\177';
    534 					/* null doesn't show up anyway */
    535 					else if ((c >= 'A' && c <= '_') ||
    536 						 (c >= 'a' && c <= 'z'))
    537 						*cp2++ = c & 037;
    538 					else {
    539 						*cp2++ = '^';
    540 						cp--;
    541 					}
    542 				} else
    543 					*cp2++ = c;
    544 			} else if (c == '"' || c == '\'')
    545 				quotec = c;
    546 			else if (c == ' ' || c == '\t')
    547 				break;
    548 			else
    549 				*cp2++ = c;
    550 		}
    551 		*cp2 = '\0';
    552 		argv[argn++] = savestr(linebuf);
    553 	}
    554 	argv[argn] = NULL;
    555 	return argn;
    556 }
    557 
    558 /*
    559  * scan out a single lexical item and return its token number,
    560  * updating the string pointer passed **p.  Also, store the value
    561  * of the number or string scanned in lexnumber or lexstring as
    562  * appropriate.  In any event, store the scanned `thing' in lexstring.
    563  */
    564 
    565 struct lex {
    566 	char	l_char;
    567 	char	l_token;
    568 } singles[] = {
    569 	{ '$',	TDOLLAR },
    570 	{ '.',	TDOT },
    571 	{ '^',	TUP },
    572 	{ '*',	TSTAR },
    573 	{ '-',	TDASH },
    574 	{ '+',	TPLUS },
    575 	{ '(',	TOPEN },
    576 	{ ')',	TCLOSE },
    577 	{ 0,	0 }
    578 };
    579 
    580 int
    581 scan(char **sp)
    582 {
    583 	char *cp, *cp2;
    584 	int c;
    585 	struct lex *lp;
    586 	int quotec;
    587 
    588 	if (regretp >= 0) {
    589 		(void)strcpy(lexstring, string_stack[regretp]);
    590 		lexnumber = numberstack[regretp];
    591 		return(regretstack[regretp--]);
    592 	}
    593 	cp = *sp;
    594 	cp2 = lexstring;
    595 	c = *cp++;
    596 
    597 	/*
    598 	 * strip away leading white space.
    599 	 */
    600 
    601 	while (c == ' ' || c == '\t')
    602 		c = *cp++;
    603 
    604 	/*
    605 	 * If no characters remain, we are at end of line,
    606 	 * so report that.
    607 	 */
    608 
    609 	if (c == '\0') {
    610 		*sp = --cp;
    611 		return(TEOL);
    612 	}
    613 
    614 	/*
    615 	 * If the leading character is a digit, scan
    616 	 * the number and convert it on the fly.
    617 	 * Return TNUMBER when done.
    618 	 */
    619 
    620 	if (isdigit(c)) {
    621 		lexnumber = 0;
    622 		while (isdigit(c)) {
    623 			lexnumber = lexnumber*10 + c - '0';
    624 			*cp2++ = c;
    625 			c = *cp++;
    626 		}
    627 		*cp2 = '\0';
    628 		*sp = --cp;
    629 		return(TNUMBER);
    630 	}
    631 
    632 	/*
    633 	 * Check for single character tokens; return such
    634 	 * if found.
    635 	 */
    636 
    637 	for (lp = &singles[0]; lp->l_char != 0; lp++)
    638 		if (c == lp->l_char) {
    639 			lexstring[0] = c;
    640 			lexstring[1] = '\0';
    641 			*sp = cp;
    642 			return(lp->l_token);
    643 		}
    644 
    645 	/*
    646 	 * We've got a string!  Copy all the characters
    647 	 * of the string into lexstring, until we see
    648 	 * a null, space, or tab.
    649 	 * If the lead character is a " or ', save it
    650 	 * and scan until you get another.
    651 	 */
    652 
    653 	quotec = 0;
    654 	if (c == '\'' || c == '"') {
    655 		quotec = c;
    656 		c = *cp++;
    657 	}
    658 	while (c != '\0') {
    659 		if (c == quotec) {
    660 			cp++;
    661 			break;
    662 		}
    663 		if (quotec == 0 && (c == ' ' || c == '\t'))
    664 			break;
    665 		if (cp2 - lexstring < STRINGLEN - 1)
    666 			*cp2++ = c;
    667 		c = *cp++;
    668 	}
    669 	if (quotec && c == 0) {
    670 		(void)fprintf(stderr, "Missing %c\n", quotec);
    671 		return TERROR;
    672 	}
    673 	*sp = --cp;
    674 	*cp2 = '\0';
    675 	return(TSTRING);
    676 }
    677 
    678 /*
    679  * Unscan the named token by pushing it onto the regret stack.
    680  */
    681 void
    682 regret(int token)
    683 {
    684 	if (++regretp >= REGDEP)
    685 		errx(1, "Too many regrets");
    686 	regretstack[regretp] = token;
    687 	lexstring[STRINGLEN - 1] = '\0';
    688 	string_stack[regretp] = savestr(lexstring);
    689 	numberstack[regretp] = lexnumber;
    690 }
    691 
    692 /*
    693  * Reset all the scanner global variables.
    694  */
    695 void
    696 scaninit(void)
    697 {
    698 	regretp = -1;
    699 }
    700 
    701 /*
    702  * Find the first message whose flags & m == f  and return
    703  * its message number.
    704  */
    705 int
    706 first(int f, int m)
    707 {
    708 	struct message *mp;
    709 
    710 	if (msgCount == 0)
    711 		return 0;
    712 	f &= MDELETED;
    713 	m &= MDELETED;
    714 	for (mp = dot; mp < &message[msgCount]; mp++)
    715 		if ((mp->m_flag & m) == f)
    716 			return mp - message + 1;
    717 	for (mp = dot - 1; mp >= &message[0]; mp--)
    718 		if ((mp->m_flag & m) == f)
    719 			return mp - message + 1;
    720 	return 0;
    721 }
    722 
    723 /*
    724  * See if the passed name sent the passed message number.  Return true
    725  * if so.
    726  */
    727 int
    728 matchsender(char *str, int mesg)
    729 {
    730 	char *cp, *cp2, *backup;
    731 
    732 	if (!*str)	/* null string matches nothing instead of everything */
    733 		return 0;
    734 	backup = cp2 = nameof(&message[mesg - 1], 0);
    735 	cp = str;
    736 	while (*cp2) {
    737 		if (*cp == 0)
    738 			return(1);
    739 		if (upcase(*cp++) != upcase(*cp2++)) {
    740 			cp2 = ++backup;
    741 			cp = str;
    742 		}
    743 	}
    744 	return(*cp == 0);
    745 }
    746 
    747 /*
    748  * See if the passed name received the passed message number.  Return true
    749  * if so.
    750  */
    751 
    752 static const char *to_fields[] = { "to", "cc", "bcc", 0 };
    753 
    754 int
    755 matchto(char *str, int mesg)
    756 {
    757 	struct message *mp;
    758 	char *cp, *cp2, *backup;
    759 	const char **to;
    760 
    761 	str++;
    762 
    763 	if (*str == 0)	/* null string matches nothing instead of everything */
    764 		return(0);
    765 
    766 	mp = &message[mesg - 1];
    767 
    768 	for (to = to_fields; *to; to++) {
    769 		cp = str;
    770 		cp2 = hfield(*to, mp);
    771 		if (cp2 != NULL) {
    772 			backup = cp2;
    773 			while (*cp2) {
    774 				if (*cp == 0)
    775 					return(1);
    776 				if (upcase(*cp++) != upcase(*cp2++)) {
    777 					cp2 = ++backup;
    778 					cp = str;
    779 				}
    780 			}
    781 			if (*cp == 0)
    782 				return(1);
    783 		}
    784 	}
    785 	return(0);
    786 }
    787 
    788 /*
    789  * See if the given string matches inside the subject field of the
    790  * given message.  For the purpose of the scan, we ignore case differences.
    791  * If it does, return true.  The string search argument is assumed to
    792  * have the form "/search-string."  If it is of the form "/," we use the
    793  * previous search string.
    794  */
    795 
    796 char lastscan[STRINGLEN];
    797 int
    798 matchsubj(char *str, int mesg)
    799 {
    800 	struct message *mp;
    801 	char *cp, *cp2, *backup;
    802 
    803 	str++;
    804 	if (*str == '\0')
    805 		str = lastscan;
    806 	else {
    807 		(void)strncpy(lastscan, str, STRINGLEN - 1);
    808 		lastscan[STRINGLEN - 1] = '\0';
    809 	}
    810 	mp = &message[mesg - 1];
    811 
    812 	/*
    813 	 * Now look, ignoring case, for the word in the string.
    814 	 */
    815 
    816 	if (value("searchheaders") && (cp = strchr(str, ':'))) {
    817 		/* Check for special case "/To:" */
    818 		if (upcase(str[0]) == 'T' && upcase(str[1]) == 'O' &&
    819 		    str[2] == ':')
    820 			return(matchto(cp, mesg));
    821 		*cp++ = '\0';
    822 		cp2 = hfield(*str ? str : "subject", mp);
    823 		cp[-1] = ':';
    824 		str = cp;
    825 	} else {
    826 		cp = str;
    827 		cp2 = hfield("subject", mp);
    828 	}
    829 	if (cp2 == NULL)
    830 		return(0);
    831 	backup = cp2;
    832 	while (*cp2) {
    833 		if (*cp == 0)
    834 			return(1);
    835 		if (upcase(*cp++) != upcase(*cp2++)) {
    836 			cp2 = ++backup;
    837 			cp = str;
    838 		}
    839 	}
    840 	return(*cp == 0);
    841 }
    842 
    843 /*
    844  * Mark the named message by setting its mark bit.
    845  */
    846 void
    847 mark(int mesg)
    848 {
    849 	int i;
    850 
    851 	i = mesg;
    852 	if (i < 1 || i > msgCount)
    853 		errx(1, "Bad message number to mark");
    854 	message[i - 1].m_flag |= MMARK;
    855 }
    856 
    857 /*
    858  * Unmark the named message.
    859  */
    860 void
    861 unmark(int mesg)
    862 {
    863 	int i;
    864 
    865 	i = mesg;
    866 	if (i < 1 || i > msgCount)
    867 		errx(1, "Bad message number to unmark");
    868 	message[i - 1].m_flag &= ~MMARK;
    869 }
    870 
    871 /*
    872  * Return the message number corresponding to the passed meta character.
    873  */
    874 int
    875 metamess(int meta, int f)
    876 {
    877 	int c, m;
    878 	struct message *mp;
    879 
    880 	c = meta;
    881 	switch (c) {
    882 	case '^':
    883 		/*
    884 		 * First 'good' message left.
    885 		 */
    886 		for (mp = &message[0]; mp < &message[msgCount]; mp++)
    887 			if ((mp->m_flag & MDELETED) == f)
    888 				return(mp - &message[0] + 1);
    889 		(void)printf("No applicable messages\n");
    890 		return(-1);
    891 
    892 	case '$':
    893 		/*
    894 		 * Last 'good message left.
    895 		 */
    896 		for (mp = &message[msgCount - 1]; mp >= &message[0]; mp--)
    897 			if ((mp->m_flag & MDELETED) == f)
    898 				return(mp - &message[0] + 1);
    899 		(void)printf("No applicable messages\n");
    900 		return(-1);
    901 
    902 	case '.':
    903 		/*
    904 		 * Current message.
    905 		 */
    906 		m = dot - &message[0] + 1;
    907 		if ((dot->m_flag & MDELETED) != f) {
    908 			(void)printf("%d: Inappropriate message\n", m);
    909 			return(-1);
    910 		}
    911 		return(m);
    912 
    913 	default:
    914 		(void)printf("Unknown metachar (%c)\n", c);
    915 		return(-1);
    916 	}
    917 }
    918