11590Srgrimes/*
21590Srgrimes * Copyright (c) 1980, 1993
31590Srgrimes *	The Regents of the University of California.  All rights reserved.
41590Srgrimes *
51590Srgrimes * Redistribution and use in source and binary forms, with or without
61590Srgrimes * modification, are permitted provided that the following conditions
71590Srgrimes * are met:
81590Srgrimes * 1. Redistributions of source code must retain the above copyright
91590Srgrimes *    notice, this list of conditions and the following disclaimer.
101590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111590Srgrimes *    notice, this list of conditions and the following disclaimer in the
121590Srgrimes *    documentation and/or other materials provided with the distribution.
131590Srgrimes * 4. Neither the name of the University nor the names of its contributors
141590Srgrimes *    may be used to endorse or promote products derived from this software
151590Srgrimes *    without specific prior written permission.
161590Srgrimes *
171590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
181590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
191590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
201590Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
211590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
221590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
231590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
241590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
251590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
261590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
271590Srgrimes * SUCH DAMAGE.
281590Srgrimes */
291590Srgrimes
301590Srgrimes#ifndef lint
3174769Smikeh#if 0
3288150Smikehstatic char sccsid[] = "@(#)list.c	8.4 (Berkeley) 5/1/95";
3374769Smikeh#endif
341590Srgrimes#endif /* not lint */
3599112Sobrien#include <sys/cdefs.h>
3699112Sobrien__FBSDID("$FreeBSD$");
371590Srgrimes
381590Srgrimes#include "rcv.h"
391590Srgrimes#include <ctype.h>
401590Srgrimes#include "extern.h"
411590Srgrimes
421590Srgrimes/*
431590Srgrimes * Mail -- a mail program
441590Srgrimes *
451590Srgrimes * Message list handling.
461590Srgrimes */
471590Srgrimes
481590Srgrimes/*
491590Srgrimes * Convert the user string of message numbers and
501590Srgrimes * store the numbers into vector.
511590Srgrimes *
521590Srgrimes * Returns the count of messages picked up or -1 on error.
531590Srgrimes */
541590Srgrimesint
55216564Scharniergetmsglist(char *buf, int *vector, int flags)
561590Srgrimes{
5777274Smikeh	int *ip;
5877274Smikeh	struct message *mp;
591590Srgrimes
601590Srgrimes	if (msgCount == 0) {
611590Srgrimes		*vector = 0;
6277274Smikeh		return (0);
631590Srgrimes	}
641590Srgrimes	if (markall(buf, flags) < 0)
6577274Smikeh		return (-1);
661590Srgrimes	ip = vector;
671590Srgrimes	for (mp = &message[0]; mp < &message[msgCount]; mp++)
681590Srgrimes		if (mp->m_flag & MMARK)
691590Srgrimes			*ip++ = mp - &message[0] + 1;
701590Srgrimes	*ip = 0;
7177274Smikeh	return (ip - vector);
721590Srgrimes}
731590Srgrimes
741590Srgrimes/*
751590Srgrimes * Mark all messages that the user wanted from the command
761590Srgrimes * line in the message structure.  Return 0 on success, -1
771590Srgrimes * on error.
781590Srgrimes */
791590Srgrimes
801590Srgrimes/*
811590Srgrimes * Bit values for colon modifiers.
821590Srgrimes */
831590Srgrimes
841590Srgrimes#define	CMNEW		01		/* New messages */
851590Srgrimes#define	CMOLD		02		/* Old messages */
861590Srgrimes#define	CMUNREAD	04		/* Unread messages */
871590Srgrimes#define	CMDELETED	010		/* Deleted messages */
881590Srgrimes#define	CMREAD		020		/* Read messages */
891590Srgrimes
901590Srgrimes/*
911590Srgrimes * The following table describes the letters which can follow
921590Srgrimes * the colon and gives the corresponding modifier bit.
931590Srgrimes */
941590Srgrimes
95173439Sddsstatic struct coltab {
961590Srgrimes	char	co_char;		/* What to find past : */
971590Srgrimes	int	co_bit;			/* Associated modifier bit */
981590Srgrimes	int	co_mask;		/* m_status bits to mask */
991590Srgrimes	int	co_equal;		/* ... must equal this */
1001590Srgrimes} coltab[] = {
10177274Smikeh	{ 'n',		CMNEW,		MNEW,		MNEW	},
10277274Smikeh	{ 'o',		CMOLD,		MNEW,		0	},
10377274Smikeh	{ 'u',		CMUNREAD,	MREAD,		0	},
10477274Smikeh	{ 'd',		CMDELETED,	MDELETED,	MDELETED},
10577274Smikeh	{ 'r',		CMREAD,		MREAD,		MREAD	},
10677274Smikeh	{ 0,		0,		0,		0	}
1071590Srgrimes};
1081590Srgrimes
1091590Srgrimesstatic	int	lastcolmod;
1101590Srgrimes
1111590Srgrimesint
112216564Scharniermarkall(char buf[], int f)
1131590Srgrimes{
11477274Smikeh	char **np;
11577274Smikeh	int i;
11677274Smikeh	struct message *mp;
1171590Srgrimes	char *namelist[NMLSIZE], *bufp;
1181590Srgrimes	int tok, beg, mc, star, other, valdot, colmod, colresult;
1191590Srgrimes
1201590Srgrimes	valdot = dot - &message[0] + 1;
1211590Srgrimes	colmod = 0;
1221590Srgrimes	for (i = 1; i <= msgCount; i++)
1231590Srgrimes		unmark(i);
1241590Srgrimes	bufp = buf;
1251590Srgrimes	mc = 0;
1261590Srgrimes	np = &namelist[0];
1271590Srgrimes	scaninit();
1281590Srgrimes	tok = scan(&bufp);
1291590Srgrimes	star = 0;
1301590Srgrimes	other = 0;
1311590Srgrimes	beg = 0;
1321590Srgrimes	while (tok != TEOL) {
1331590Srgrimes		switch (tok) {
1341590Srgrimes		case TNUMBER:
1351590Srgrimesnumber:
1361590Srgrimes			if (star) {
1371590Srgrimes				printf("No numbers mixed with *\n");
13877274Smikeh				return (-1);
1391590Srgrimes			}
1401590Srgrimes			mc++;
1411590Srgrimes			other++;
1421590Srgrimes			if (beg != 0) {
1431590Srgrimes				if (check(lexnumber, f))
14477274Smikeh					return (-1);
1451590Srgrimes				for (i = beg; i <= lexnumber; i++)
1461590Srgrimes					if (f == MDELETED || (message[i - 1].m_flag & MDELETED) == 0)
1471590Srgrimes						mark(i);
1481590Srgrimes				beg = 0;
1491590Srgrimes				break;
1501590Srgrimes			}
1511590Srgrimes			beg = lexnumber;
1521590Srgrimes			if (check(beg, f))
15377274Smikeh				return (-1);
1541590Srgrimes			tok = scan(&bufp);
1551590Srgrimes			regret(tok);
1561590Srgrimes			if (tok != TDASH) {
1571590Srgrimes				mark(beg);
1581590Srgrimes				beg = 0;
1591590Srgrimes			}
1601590Srgrimes			break;
1611590Srgrimes
1621590Srgrimes		case TPLUS:
1631590Srgrimes			if (beg != 0) {
1641590Srgrimes				printf("Non-numeric second argument\n");
16577274Smikeh				return (-1);
1661590Srgrimes			}
1671590Srgrimes			i = valdot;
1681590Srgrimes			do {
1691590Srgrimes				i++;
1701590Srgrimes				if (i > msgCount) {
1711590Srgrimes					printf("Referencing beyond EOF\n");
17277274Smikeh					return (-1);
1731590Srgrimes				}
1741590Srgrimes			} while ((message[i - 1].m_flag & MDELETED) != f);
1751590Srgrimes			mark(i);
1761590Srgrimes			break;
1771590Srgrimes
1781590Srgrimes		case TDASH:
1791590Srgrimes			if (beg == 0) {
1801590Srgrimes				i = valdot;
1811590Srgrimes				do {
1821590Srgrimes					i--;
1831590Srgrimes					if (i <= 0) {
1841590Srgrimes						printf("Referencing before 1\n");
18577274Smikeh						return (-1);
1861590Srgrimes					}
1871590Srgrimes				} while ((message[i - 1].m_flag & MDELETED) != f);
1881590Srgrimes				mark(i);
1891590Srgrimes			}
1901590Srgrimes			break;
1911590Srgrimes
1921590Srgrimes		case TSTRING:
1931590Srgrimes			if (beg != 0) {
1941590Srgrimes				printf("Non-numeric second argument\n");
19577274Smikeh				return (-1);
1961590Srgrimes			}
1971590Srgrimes			other++;
1981590Srgrimes			if (lexstring[0] == ':') {
1991590Srgrimes				colresult = evalcol(lexstring[1]);
2001590Srgrimes				if (colresult == 0) {
2011590Srgrimes					printf("Unknown colon modifier \"%s\"\n",
2021590Srgrimes					    lexstring);
20377274Smikeh					return (-1);
2041590Srgrimes				}
2051590Srgrimes				colmod |= colresult;
2061590Srgrimes			}
2071590Srgrimes			else
2081590Srgrimes				*np++ = savestr(lexstring);
2091590Srgrimes			break;
2101590Srgrimes
2111590Srgrimes		case TDOLLAR:
2121590Srgrimes		case TUP:
2131590Srgrimes		case TDOT:
2141590Srgrimes			lexnumber = metamess(lexstring[0], f);
2151590Srgrimes			if (lexnumber == -1)
21677274Smikeh				return (-1);
2171590Srgrimes			goto number;
2181590Srgrimes
2191590Srgrimes		case TSTAR:
2201590Srgrimes			if (other) {
2211590Srgrimes				printf("Can't mix \"*\" with anything\n");
22277274Smikeh				return (-1);
2231590Srgrimes			}
2241590Srgrimes			star++;
2251590Srgrimes			break;
2261590Srgrimes
2271590Srgrimes		case TERROR:
22877274Smikeh			return (-1);
2291590Srgrimes		}
2301590Srgrimes		tok = scan(&bufp);
2311590Srgrimes	}
2321590Srgrimes	lastcolmod = colmod;
23377274Smikeh	*np = NULL;
2341590Srgrimes	mc = 0;
2351590Srgrimes	if (star) {
2361590Srgrimes		for (i = 0; i < msgCount; i++)
2371590Srgrimes			if ((message[i].m_flag & MDELETED) == f) {
2381590Srgrimes				mark(i+1);
2391590Srgrimes				mc++;
2401590Srgrimes			}
2411590Srgrimes		if (mc == 0) {
2421590Srgrimes			printf("No applicable messages.\n");
24377274Smikeh			return (-1);
2441590Srgrimes		}
24577274Smikeh		return (0);
2461590Srgrimes	}
2471590Srgrimes
2481590Srgrimes	/*
2491590Srgrimes	 * If no numbers were given, mark all of the messages,
2501590Srgrimes	 * so that we can unmark any whose sender was not selected
2511590Srgrimes	 * if any user names were given.
2521590Srgrimes	 */
2531590Srgrimes
2541590Srgrimes	if ((np > namelist || colmod != 0) && mc == 0)
2551590Srgrimes		for (i = 1; i <= msgCount; i++)
2561590Srgrimes			if ((message[i-1].m_flag & MDELETED) == f)
2571590Srgrimes				mark(i);
2581590Srgrimes
2591590Srgrimes	/*
2601590Srgrimes	 * If any names were given, go through and eliminate any
2611590Srgrimes	 * messages whose senders were not requested.
2621590Srgrimes	 */
2631590Srgrimes
2641590Srgrimes	if (np > namelist) {
2651590Srgrimes		for (i = 1; i <= msgCount; i++) {
26677274Smikeh			for (mc = 0, np = &namelist[0]; *np != NULL; np++)
2671590Srgrimes				if (**np == '/') {
26898803Smikeh					if (matchfield(*np, i)) {
2691590Srgrimes						mc++;
2701590Srgrimes						break;
2711590Srgrimes					}
2721590Srgrimes				}
2731590Srgrimes				else {
2741590Srgrimes					if (matchsender(*np, i)) {
2751590Srgrimes						mc++;
2761590Srgrimes						break;
2771590Srgrimes					}
2781590Srgrimes				}
2791590Srgrimes			if (mc == 0)
2801590Srgrimes				unmark(i);
2811590Srgrimes		}
2821590Srgrimes
2831590Srgrimes		/*
2841590Srgrimes		 * Make sure we got some decent messages.
2851590Srgrimes		 */
2861590Srgrimes
2871590Srgrimes		mc = 0;
2881590Srgrimes		for (i = 1; i <= msgCount; i++)
2891590Srgrimes			if (message[i-1].m_flag & MMARK) {
2901590Srgrimes				mc++;
2911590Srgrimes				break;
2921590Srgrimes			}
2931590Srgrimes		if (mc == 0) {
2941590Srgrimes			printf("No applicable messages from {%s",
2951590Srgrimes				namelist[0]);
29677274Smikeh			for (np = &namelist[1]; *np != NULL; np++)
2971590Srgrimes				printf(", %s", *np);
2981590Srgrimes			printf("}\n");
29977274Smikeh			return (-1);
3001590Srgrimes		}
3011590Srgrimes	}
3021590Srgrimes
3031590Srgrimes	/*
3041590Srgrimes	 * If any colon modifiers were given, go through and
3051590Srgrimes	 * unmark any messages which do not satisfy the modifiers.
3061590Srgrimes	 */
3071590Srgrimes
3081590Srgrimes	if (colmod != 0) {
3091590Srgrimes		for (i = 1; i <= msgCount; i++) {
31077274Smikeh			struct coltab *colp;
3111590Srgrimes
3121590Srgrimes			mp = &message[i - 1];
31377274Smikeh			for (colp = &coltab[0]; colp->co_char != '\0'; colp++)
3141590Srgrimes				if (colp->co_bit & colmod)
3151590Srgrimes					if ((mp->m_flag & colp->co_mask)
3161590Srgrimes					    != colp->co_equal)
3171590Srgrimes						unmark(i);
3188874Srgrimes
3191590Srgrimes		}
3201590Srgrimes		for (mp = &message[0]; mp < &message[msgCount]; mp++)
3211590Srgrimes			if (mp->m_flag & MMARK)
3221590Srgrimes				break;
3231590Srgrimes		if (mp >= &message[msgCount]) {
32477274Smikeh			struct coltab *colp;
3251590Srgrimes
3261590Srgrimes			printf("No messages satisfy");
32777274Smikeh			for (colp = &coltab[0]; colp->co_char != '\0'; colp++)
3281590Srgrimes				if (colp->co_bit & colmod)
3291590Srgrimes					printf(" :%c", colp->co_char);
3301590Srgrimes			printf("\n");
33177274Smikeh			return (-1);
3321590Srgrimes		}
3331590Srgrimes	}
33477274Smikeh	return (0);
3351590Srgrimes}
3361590Srgrimes
3371590Srgrimes/*
3381590Srgrimes * Turn the character after a colon modifier into a bit
3391590Srgrimes * value.
3401590Srgrimes */
3411590Srgrimesint
342216564Scharnierevalcol(int col)
3431590Srgrimes{
34477274Smikeh	struct coltab *colp;
3451590Srgrimes
3461590Srgrimes	if (col == 0)
34777274Smikeh		return (lastcolmod);
34877274Smikeh	for (colp = &coltab[0]; colp->co_char != '\0'; colp++)
3491590Srgrimes		if (colp->co_char == col)
35077274Smikeh			return (colp->co_bit);
35177274Smikeh	return (0);
3521590Srgrimes}
3531590Srgrimes
3541590Srgrimes/*
3551590Srgrimes * Check the passed message number for legality and proper flags.
3561590Srgrimes * If f is MDELETED, then either kind will do.  Otherwise, the message
3571590Srgrimes * has to be undeleted.
3581590Srgrimes */
3591590Srgrimesint
360216564Scharniercheck(int mesg, int f)
3611590Srgrimes{
36277274Smikeh	struct message *mp;
3631590Srgrimes
3641590Srgrimes	if (mesg < 1 || mesg > msgCount) {
3651590Srgrimes		printf("%d: Invalid message number\n", mesg);
36677274Smikeh		return (-1);
3671590Srgrimes	}
3681590Srgrimes	mp = &message[mesg-1];
3691590Srgrimes	if (f != MDELETED && (mp->m_flag & MDELETED) != 0) {
3701590Srgrimes		printf("%d: Inappropriate message\n", mesg);
37177274Smikeh		return (-1);
3721590Srgrimes	}
37377274Smikeh	return (0);
3741590Srgrimes}
3751590Srgrimes
3761590Srgrimes/*
3771590Srgrimes * Scan out the list of string arguments, shell style
3781590Srgrimes * for a RAWLIST.
3791590Srgrimes */
3801590Srgrimesint
381216564Scharniergetrawlist(char line[], char **argv, int argc)
3821590Srgrimes{
38377274Smikeh	char c, *cp, *cp2, quotec;
3841590Srgrimes	int argn;
38574769Smikeh	char *linebuf;
38674769Smikeh	size_t linebufsize = BUFSIZ;
3871590Srgrimes
38877274Smikeh	if ((linebuf = malloc(linebufsize)) == NULL)
38974769Smikeh		err(1, "Out of memory");
39074769Smikeh
3911590Srgrimes	argn = 0;
3921590Srgrimes	cp = line;
3931590Srgrimes	for (;;) {
3941590Srgrimes		for (; *cp == ' ' || *cp == '\t'; cp++)
3951590Srgrimes			;
3961590Srgrimes		if (*cp == '\0')
3971590Srgrimes			break;
3981590Srgrimes		if (argn >= argc - 1) {
3991590Srgrimes			printf(
4001590Srgrimes			"Too many elements in the list; excess discarded.\n");
4011590Srgrimes			break;
4021590Srgrimes		}
4031590Srgrimes		cp2 = linebuf;
4041590Srgrimes		quotec = '\0';
4051590Srgrimes		while ((c = *cp) != '\0') {
40674769Smikeh			/* Allocate more space if necessary */
40774769Smikeh			if (cp2 - linebuf == linebufsize - 1) {
40874769Smikeh				linebufsize += BUFSIZ;
40974769Smikeh				if ((linebuf = realloc(linebuf, linebufsize)) == NULL)
41074769Smikeh					err(1, "Out of memory");
41174769Smikeh				cp2 = linebuf + linebufsize - BUFSIZ - 1;
41274769Smikeh			}
4131590Srgrimes			cp++;
4141590Srgrimes			if (quotec != '\0') {
4151590Srgrimes				if (c == quotec)
4161590Srgrimes					quotec = '\0';
4171590Srgrimes				else if (c == '\\')
4181590Srgrimes					switch (c = *cp++) {
4191590Srgrimes					case '\0':
4201590Srgrimes						*cp2++ = '\\';
4211590Srgrimes						cp--;
4221590Srgrimes						break;
4231590Srgrimes					case '0': case '1': case '2': case '3':
4241590Srgrimes					case '4': case '5': case '6': case '7':
4251590Srgrimes						c -= '0';
4261590Srgrimes						if (*cp >= '0' && *cp <= '7')
4271590Srgrimes							c = c * 8 + *cp++ - '0';
4281590Srgrimes						if (*cp >= '0' && *cp <= '7')
4291590Srgrimes							c = c * 8 + *cp++ - '0';
4301590Srgrimes						*cp2++ = c;
4311590Srgrimes						break;
4321590Srgrimes					case 'b':
4331590Srgrimes						*cp2++ = '\b';
4341590Srgrimes						break;
4351590Srgrimes					case 'f':
4361590Srgrimes						*cp2++ = '\f';
4371590Srgrimes						break;
4381590Srgrimes					case 'n':
4391590Srgrimes						*cp2++ = '\n';
4401590Srgrimes						break;
4411590Srgrimes					case 'r':
4421590Srgrimes						*cp2++ = '\r';
4431590Srgrimes						break;
4441590Srgrimes					case 't':
4451590Srgrimes						*cp2++ = '\t';
4461590Srgrimes						break;
4471590Srgrimes					case 'v':
4481590Srgrimes						*cp2++ = '\v';
4491590Srgrimes						break;
4501590Srgrimes					default:
4511590Srgrimes						*cp2++ = c;
4521590Srgrimes					}
4531590Srgrimes				else if (c == '^') {
4541590Srgrimes					c = *cp++;
4551590Srgrimes					if (c == '?')
4561590Srgrimes						*cp2++ = '\177';
4571590Srgrimes					/* null doesn't show up anyway */
45877274Smikeh					else if ((c >= 'A' && c <= '_') ||
45977274Smikeh					    (c >= 'a' && c <= 'z'))
4601590Srgrimes						*cp2++ = c & 037;
4611590Srgrimes					else {
4621590Srgrimes						*cp2++ = '^';
4631590Srgrimes						cp--;
4641590Srgrimes					}
4651590Srgrimes				} else
4661590Srgrimes					*cp2++ = c;
4671590Srgrimes			} else if (c == '"' || c == '\'')
4681590Srgrimes				quotec = c;
4691590Srgrimes			else if (c == ' ' || c == '\t')
4701590Srgrimes				break;
4711590Srgrimes			else
4721590Srgrimes				*cp2++ = c;
4731590Srgrimes		}
4741590Srgrimes		*cp2 = '\0';
4751590Srgrimes		argv[argn++] = savestr(linebuf);
4761590Srgrimes	}
47777274Smikeh	argv[argn] = NULL;
47877274Smikeh	(void)free(linebuf);
47977274Smikeh	return (argn);
4801590Srgrimes}
4811590Srgrimes
4821590Srgrimes/*
4831590Srgrimes * scan out a single lexical item and return its token number,
4841590Srgrimes * updating the string pointer passed **p.  Also, store the value
4851590Srgrimes * of the number or string scanned in lexnumber or lexstring as
4861590Srgrimes * appropriate.  In any event, store the scanned `thing' in lexstring.
4871590Srgrimes */
4881590Srgrimes
489173439Sddsstatic struct lex {
4901590Srgrimes	char	l_char;
4911590Srgrimes	char	l_token;
4921590Srgrimes} singles[] = {
49377274Smikeh	{ '$',	TDOLLAR	},
49477274Smikeh	{ '.',	TDOT	},
49577274Smikeh	{ '^',	TUP 	},
49677274Smikeh	{ '*',	TSTAR 	},
49777274Smikeh	{ '-',	TDASH 	},
49877274Smikeh	{ '+',	TPLUS 	},
49977274Smikeh	{ '(',	TOPEN 	},
50077274Smikeh	{ ')',	TCLOSE 	},
50177274Smikeh	{ 0,	0 	}
5021590Srgrimes};
5031590Srgrimes
5041590Srgrimesint
505216564Scharnierscan(char **sp)
5061590Srgrimes{
50777274Smikeh	char *cp, *cp2;
50877274Smikeh	int c;
50977274Smikeh	struct lex *lp;
5101590Srgrimes	int quotec;
5111590Srgrimes
5121590Srgrimes	if (regretp >= 0) {
5131590Srgrimes		strcpy(lexstring, string_stack[regretp]);
5141590Srgrimes		lexnumber = numberstack[regretp];
51577274Smikeh		return (regretstack[regretp--]);
5161590Srgrimes	}
5171590Srgrimes	cp = *sp;
5181590Srgrimes	cp2 = lexstring;
5191590Srgrimes	c = *cp++;
5201590Srgrimes
5211590Srgrimes	/*
5221590Srgrimes	 * strip away leading white space.
5231590Srgrimes	 */
5241590Srgrimes
5251590Srgrimes	while (c == ' ' || c == '\t')
5261590Srgrimes		c = *cp++;
5271590Srgrimes
5281590Srgrimes	/*
5291590Srgrimes	 * If no characters remain, we are at end of line,
5301590Srgrimes	 * so report that.
5311590Srgrimes	 */
5321590Srgrimes
5331590Srgrimes	if (c == '\0') {
5341590Srgrimes		*sp = --cp;
53577274Smikeh		return (TEOL);
5361590Srgrimes	}
5371590Srgrimes
5381590Srgrimes	/*
5391590Srgrimes	 * If the leading character is a digit, scan
5401590Srgrimes	 * the number and convert it on the fly.
5411590Srgrimes	 * Return TNUMBER when done.
5421590Srgrimes	 */
5431590Srgrimes
54488227Sache	if (isdigit((unsigned char)c)) {
5451590Srgrimes		lexnumber = 0;
54688227Sache		while (isdigit((unsigned char)c)) {
5471590Srgrimes			lexnumber = lexnumber*10 + c - '0';
5481590Srgrimes			*cp2++ = c;
5491590Srgrimes			c = *cp++;
5501590Srgrimes		}
5511590Srgrimes		*cp2 = '\0';
5521590Srgrimes		*sp = --cp;
55377274Smikeh		return (TNUMBER);
5541590Srgrimes	}
5551590Srgrimes
5561590Srgrimes	/*
5571590Srgrimes	 * Check for single character tokens; return such
5581590Srgrimes	 * if found.
5591590Srgrimes	 */
5601590Srgrimes
56177274Smikeh	for (lp = &singles[0]; lp->l_char != '\0'; lp++)
5621590Srgrimes		if (c == lp->l_char) {
5631590Srgrimes			lexstring[0] = c;
5641590Srgrimes			lexstring[1] = '\0';
5651590Srgrimes			*sp = cp;
56677274Smikeh			return (lp->l_token);
5671590Srgrimes		}
5681590Srgrimes
5691590Srgrimes	/*
5701590Srgrimes	 * We've got a string!  Copy all the characters
5711590Srgrimes	 * of the string into lexstring, until we see
5721590Srgrimes	 * a null, space, or tab.
5731590Srgrimes	 * If the lead character is a " or ', save it
5741590Srgrimes	 * and scan until you get another.
5751590Srgrimes	 */
5761590Srgrimes
5771590Srgrimes	quotec = 0;
5781590Srgrimes	if (c == '\'' || c == '"') {
5791590Srgrimes		quotec = c;
5801590Srgrimes		c = *cp++;
5811590Srgrimes	}
5821590Srgrimes	while (c != '\0') {
5831590Srgrimes		if (c == quotec) {
5841590Srgrimes			cp++;
5851590Srgrimes			break;
5861590Srgrimes		}
5871590Srgrimes		if (quotec == 0 && (c == ' ' || c == '\t'))
5881590Srgrimes			break;
5891590Srgrimes		if (cp2 - lexstring < STRINGLEN-1)
5901590Srgrimes			*cp2++ = c;
5911590Srgrimes		c = *cp++;
5921590Srgrimes	}
59377274Smikeh	if (quotec && c == '\0') {
5941590Srgrimes		fprintf(stderr, "Missing %c\n", quotec);
59577274Smikeh		return (TERROR);
5961590Srgrimes	}
5971590Srgrimes	*sp = --cp;
5981590Srgrimes	*cp2 = '\0';
59977274Smikeh	return (TSTRING);
6001590Srgrimes}
6011590Srgrimes
6021590Srgrimes/*
6031590Srgrimes * Unscan the named token by pushing it onto the regret stack.
6041590Srgrimes */
6051590Srgrimesvoid
606216564Scharnierregret(int token)
6071590Srgrimes{
6081590Srgrimes	if (++regretp >= REGDEP)
60974769Smikeh		errx(1, "Too many regrets");
6101590Srgrimes	regretstack[regretp] = token;
6111590Srgrimes	lexstring[STRINGLEN-1] = '\0';
6121590Srgrimes	string_stack[regretp] = savestr(lexstring);
6131590Srgrimes	numberstack[regretp] = lexnumber;
6141590Srgrimes}
6151590Srgrimes
6161590Srgrimes/*
6171590Srgrimes * Reset all the scanner global variables.
6181590Srgrimes */
6191590Srgrimesvoid
620216564Scharnierscaninit(void)
6211590Srgrimes{
6221590Srgrimes	regretp = -1;
6231590Srgrimes}
6241590Srgrimes
6251590Srgrimes/*
6261590Srgrimes * Find the first message whose flags & m == f  and return
6271590Srgrimes * its message number.
6281590Srgrimes */
6291590Srgrimesint
630216564Scharnierfirst(int f, int m)
6311590Srgrimes{
63277274Smikeh	struct message *mp;
6331590Srgrimes
6341590Srgrimes	if (msgCount == 0)
63577274Smikeh		return (0);
6361590Srgrimes	f &= MDELETED;
6371590Srgrimes	m &= MDELETED;
6381590Srgrimes	for (mp = dot; mp < &message[msgCount]; mp++)
6391590Srgrimes		if ((mp->m_flag & m) == f)
64077274Smikeh			return (mp - message + 1);
6411590Srgrimes	for (mp = dot-1; mp >= &message[0]; mp--)
6421590Srgrimes		if ((mp->m_flag & m) == f)
64377274Smikeh			return (mp - message + 1);
64477274Smikeh	return (0);
6451590Srgrimes}
6461590Srgrimes
6471590Srgrimes/*
6481590Srgrimes * See if the passed name sent the passed message number.  Return true
6491590Srgrimes * if so.
6501590Srgrimes */
6511590Srgrimesint
652216564Scharniermatchsender(char *str, int mesg)
6531590Srgrimes{
65498804Smikeh	char *cp;
6551590Srgrimes
65698804Smikeh	/* null string matches nothing instead of everything */
65798804Smikeh	if (*str == '\0')
65877274Smikeh		return (0);
65998804Smikeh
66098804Smikeh	cp = nameof(&message[mesg - 1], 0);
66198804Smikeh	return (strcasestr(cp, str) != NULL);
6621590Srgrimes}
6631590Srgrimes
6641590Srgrimes/*
66588150Smikeh * See if the passed name received the passed message number.  Return true
66688150Smikeh * if so.
66788150Smikeh */
66888150Smikeh
66988150Smikehstatic char *to_fields[] = { "to", "cc", "bcc", NULL };
67088150Smikeh
671173439Sddsstatic int
672216564Scharniermatchto(char *str, int mesg)
67388150Smikeh{
67488150Smikeh	struct message *mp;
67598804Smikeh	char *cp, **to;
67688150Smikeh
67788150Smikeh	str++;
67888150Smikeh
67988150Smikeh	/* null string matches nothing instead of everything */
68088150Smikeh	if (*str == '\0')
68188150Smikeh		return (0);
68288150Smikeh
68388150Smikeh	mp = &message[mesg - 1];
68488150Smikeh
68588150Smikeh	for (to = to_fields; *to != NULL; to++) {
68698804Smikeh		cp = hfield(*to, mp);
68798804Smikeh		if (cp != NULL && strcasestr(cp, str) != NULL)
68898804Smikeh			return (1);
68988150Smikeh	}
69088150Smikeh	return (0);
69188150Smikeh}
69288150Smikeh
69388150Smikeh/*
69498803Smikeh * See if the given substring is contained within the specified field. If
69598803Smikeh * 'searchheaders' is set, then the form '/x:y' will be accepted and matches
69698803Smikeh * any message with the substring 'y' in field 'x'. If 'x' is omitted or
69798803Smikeh * 'searchheaders' is not set, then the search matches any messages
69898803Smikeh * with the substring 'y' in the 'Subject'. The search is case insensitive.
69998803Smikeh *
70098803Smikeh * The form '/to:y' is a special case, and will match all messages
70198803Smikeh * containing the substring 'y' in the 'To', 'Cc', or 'Bcc' header
70298803Smikeh * fields. The search for 'to' is case sensitive, so that '/To:y' can
70398803Smikeh * be used to limit the search to just the 'To' field.
7041590Srgrimes */
7051590Srgrimes
706173439Sddsstatic char lastscan[STRINGLEN];
7071590Srgrimesint
708216564Scharniermatchfield(char *str, int mesg)
7091590Srgrimes{
71077274Smikeh	struct message *mp;
71198804Smikeh	char *cp, *cp2;
7121590Srgrimes
7131590Srgrimes	str++;
71474769Smikeh	if (*str == '\0')
7151590Srgrimes		str = lastscan;
7161590Srgrimes	else
71774769Smikeh		strlcpy(lastscan, str, sizeof(lastscan));
7181590Srgrimes	mp = &message[mesg-1];
7198874Srgrimes
7201590Srgrimes	/*
7211590Srgrimes	 * Now look, ignoring case, for the word in the string.
7221590Srgrimes	 */
7231590Srgrimes
72477274Smikeh	if (value("searchheaders") && (cp = strchr(str, ':')) != NULL) {
72598803Smikeh		/* Check for special case "/to:" */
72698803Smikeh		if (strncmp(str, "to:", 3) == 0)
72788150Smikeh			return (matchto(cp, mesg));
7281590Srgrimes		*cp++ = '\0';
72988150Smikeh		cp2 = hfield(*str != '\0' ? str : "subject", mp);
7301590Srgrimes		cp[-1] = ':';
7311590Srgrimes		str = cp;
73298804Smikeh		cp = cp2;
73398804Smikeh	} else
73498804Smikeh		cp = hfield("subject", mp);
73598804Smikeh
73698804Smikeh	if (cp == NULL)
73777274Smikeh		return (0);
73898804Smikeh
73998804Smikeh	return (strcasestr(cp, str) != NULL);
7401590Srgrimes}
7411590Srgrimes
7421590Srgrimes/*
7431590Srgrimes * Mark the named message by setting its mark bit.
7441590Srgrimes */
7451590Srgrimesvoid
746216564Scharniermark(int mesg)
7471590Srgrimes{
74877274Smikeh	int i;
7491590Srgrimes
7501590Srgrimes	i = mesg;
7511590Srgrimes	if (i < 1 || i > msgCount)
75274769Smikeh		errx(1, "Bad message number to mark");
7531590Srgrimes	message[i-1].m_flag |= MMARK;
7541590Srgrimes}
7551590Srgrimes
7561590Srgrimes/*
7571590Srgrimes * Unmark the named message.
7581590Srgrimes */
7591590Srgrimesvoid
760216564Scharnierunmark(int mesg)
7611590Srgrimes{
76277274Smikeh	int i;
7631590Srgrimes
7641590Srgrimes	i = mesg;
7651590Srgrimes	if (i < 1 || i > msgCount)
76674769Smikeh		errx(1, "Bad message number to unmark");
7671590Srgrimes	message[i-1].m_flag &= ~MMARK;
7681590Srgrimes}
7691590Srgrimes
7701590Srgrimes/*
7711590Srgrimes * Return the message number corresponding to the passed meta character.
7721590Srgrimes */
7731590Srgrimesint
774216564Scharniermetamess(int meta, int f)
7751590Srgrimes{
77677274Smikeh	int c, m;
77777274Smikeh	struct message *mp;
7781590Srgrimes
7791590Srgrimes	c = meta;
7801590Srgrimes	switch (c) {
7811590Srgrimes	case '^':
7821590Srgrimes		/*
7831590Srgrimes		 * First 'good' message left.
7841590Srgrimes		 */
7851590Srgrimes		for (mp = &message[0]; mp < &message[msgCount]; mp++)
7861590Srgrimes			if ((mp->m_flag & MDELETED) == f)
78777274Smikeh				return (mp - &message[0] + 1);
7881590Srgrimes		printf("No applicable messages\n");
78977274Smikeh		return (-1);
7901590Srgrimes
7911590Srgrimes	case '$':
7921590Srgrimes		/*
7931590Srgrimes		 * Last 'good message left.
7941590Srgrimes		 */
7951590Srgrimes		for (mp = &message[msgCount-1]; mp >= &message[0]; mp--)
7961590Srgrimes			if ((mp->m_flag & MDELETED) == f)
79777274Smikeh				return (mp - &message[0] + 1);
7981590Srgrimes		printf("No applicable messages\n");
79977274Smikeh		return (-1);
8001590Srgrimes
8011590Srgrimes	case '.':
8028874Srgrimes		/*
8031590Srgrimes		 * Current message.
8041590Srgrimes		 */
8051590Srgrimes		m = dot - &message[0] + 1;
8061590Srgrimes		if ((dot->m_flag & MDELETED) != f) {
8071590Srgrimes			printf("%d: Inappropriate message\n", m);
80877274Smikeh			return (-1);
8091590Srgrimes		}
81077274Smikeh		return (m);
8111590Srgrimes
8121590Srgrimes	default:
8131590Srgrimes		printf("Unknown metachar (%c)\n", c);
81477274Smikeh		return (-1);
8151590Srgrimes	}
8161590Srgrimes}
817