list.c revision 88150
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 * 3. All advertising materials mentioning features or use of this software
141590Srgrimes *    must display the following acknowledgement:
151590Srgrimes *	This product includes software developed by the University of
161590Srgrimes *	California, Berkeley and its contributors.
171590Srgrimes * 4. Neither the name of the University nor the names of its contributors
181590Srgrimes *    may be used to endorse or promote products derived from this software
191590Srgrimes *    without specific prior written permission.
201590Srgrimes *
211590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
221590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
231590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
241590Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
251590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
261590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
271590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
281590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
291590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
301590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
311590Srgrimes * SUCH DAMAGE.
321590Srgrimes */
331590Srgrimes
341590Srgrimes#ifndef lint
3574769Smikeh#if 0
3688150Smikehstatic char sccsid[] = "@(#)list.c	8.4 (Berkeley) 5/1/95";
3774769Smikeh#endif
3874769Smikehstatic const char rcsid[] =
3974769Smikeh  "$FreeBSD: head/usr.bin/mail/list.c 88150 2001-12-18 20:52:09Z mikeh $";
401590Srgrimes#endif /* not lint */
411590Srgrimes
421590Srgrimes#include "rcv.h"
431590Srgrimes#include <ctype.h>
441590Srgrimes#include "extern.h"
451590Srgrimes
461590Srgrimes/*
471590Srgrimes * Mail -- a mail program
481590Srgrimes *
491590Srgrimes * Message list handling.
501590Srgrimes */
511590Srgrimes
521590Srgrimes/*
531590Srgrimes * Convert the user string of message numbers and
541590Srgrimes * store the numbers into vector.
551590Srgrimes *
561590Srgrimes * Returns the count of messages picked up or -1 on error.
571590Srgrimes */
581590Srgrimesint
591590Srgrimesgetmsglist(buf, vector, flags)
601590Srgrimes	char *buf;
611590Srgrimes	int *vector, flags;
621590Srgrimes{
6377274Smikeh	int *ip;
6477274Smikeh	struct message *mp;
651590Srgrimes
661590Srgrimes	if (msgCount == 0) {
671590Srgrimes		*vector = 0;
6877274Smikeh		return (0);
691590Srgrimes	}
701590Srgrimes	if (markall(buf, flags) < 0)
7177274Smikeh		return (-1);
721590Srgrimes	ip = vector;
731590Srgrimes	for (mp = &message[0]; mp < &message[msgCount]; mp++)
741590Srgrimes		if (mp->m_flag & MMARK)
751590Srgrimes			*ip++ = mp - &message[0] + 1;
761590Srgrimes	*ip = 0;
7777274Smikeh	return (ip - vector);
781590Srgrimes}
791590Srgrimes
801590Srgrimes/*
811590Srgrimes * Mark all messages that the user wanted from the command
821590Srgrimes * line in the message structure.  Return 0 on success, -1
831590Srgrimes * on error.
841590Srgrimes */
851590Srgrimes
861590Srgrimes/*
871590Srgrimes * Bit values for colon modifiers.
881590Srgrimes */
891590Srgrimes
901590Srgrimes#define	CMNEW		01		/* New messages */
911590Srgrimes#define	CMOLD		02		/* Old messages */
921590Srgrimes#define	CMUNREAD	04		/* Unread messages */
931590Srgrimes#define	CMDELETED	010		/* Deleted messages */
941590Srgrimes#define	CMREAD		020		/* Read messages */
951590Srgrimes
961590Srgrimes/*
971590Srgrimes * The following table describes the letters which can follow
981590Srgrimes * the colon and gives the corresponding modifier bit.
991590Srgrimes */
1001590Srgrimes
1011590Srgrimesstruct coltab {
1021590Srgrimes	char	co_char;		/* What to find past : */
1031590Srgrimes	int	co_bit;			/* Associated modifier bit */
1041590Srgrimes	int	co_mask;		/* m_status bits to mask */
1051590Srgrimes	int	co_equal;		/* ... must equal this */
1061590Srgrimes} coltab[] = {
10777274Smikeh	{ 'n',		CMNEW,		MNEW,		MNEW	},
10877274Smikeh	{ 'o',		CMOLD,		MNEW,		0	},
10977274Smikeh	{ 'u',		CMUNREAD,	MREAD,		0	},
11077274Smikeh	{ 'd',		CMDELETED,	MDELETED,	MDELETED},
11177274Smikeh	{ 'r',		CMREAD,		MREAD,		MREAD	},
11277274Smikeh	{ 0,		0,		0,		0	}
1131590Srgrimes};
1141590Srgrimes
1151590Srgrimesstatic	int	lastcolmod;
1161590Srgrimes
1171590Srgrimesint
1181590Srgrimesmarkall(buf, f)
1191590Srgrimes	char buf[];
1201590Srgrimes	int f;
1211590Srgrimes{
12277274Smikeh	char **np;
12377274Smikeh	int i;
12477274Smikeh	struct message *mp;
1251590Srgrimes	char *namelist[NMLSIZE], *bufp;
1261590Srgrimes	int tok, beg, mc, star, other, valdot, colmod, colresult;
1271590Srgrimes
1281590Srgrimes	valdot = dot - &message[0] + 1;
1291590Srgrimes	colmod = 0;
1301590Srgrimes	for (i = 1; i <= msgCount; i++)
1311590Srgrimes		unmark(i);
1321590Srgrimes	bufp = buf;
1331590Srgrimes	mc = 0;
1341590Srgrimes	np = &namelist[0];
1351590Srgrimes	scaninit();
1361590Srgrimes	tok = scan(&bufp);
1371590Srgrimes	star = 0;
1381590Srgrimes	other = 0;
1391590Srgrimes	beg = 0;
1401590Srgrimes	while (tok != TEOL) {
1411590Srgrimes		switch (tok) {
1421590Srgrimes		case TNUMBER:
1431590Srgrimesnumber:
1441590Srgrimes			if (star) {
1451590Srgrimes				printf("No numbers mixed with *\n");
14677274Smikeh				return (-1);
1471590Srgrimes			}
1481590Srgrimes			mc++;
1491590Srgrimes			other++;
1501590Srgrimes			if (beg != 0) {
1511590Srgrimes				if (check(lexnumber, f))
15277274Smikeh					return (-1);
1531590Srgrimes				for (i = beg; i <= lexnumber; i++)
1541590Srgrimes					if (f == MDELETED || (message[i - 1].m_flag & MDELETED) == 0)
1551590Srgrimes						mark(i);
1561590Srgrimes				beg = 0;
1571590Srgrimes				break;
1581590Srgrimes			}
1591590Srgrimes			beg = lexnumber;
1601590Srgrimes			if (check(beg, f))
16177274Smikeh				return (-1);
1621590Srgrimes			tok = scan(&bufp);
1631590Srgrimes			regret(tok);
1641590Srgrimes			if (tok != TDASH) {
1651590Srgrimes				mark(beg);
1661590Srgrimes				beg = 0;
1671590Srgrimes			}
1681590Srgrimes			break;
1691590Srgrimes
1701590Srgrimes		case TPLUS:
1711590Srgrimes			if (beg != 0) {
1721590Srgrimes				printf("Non-numeric second argument\n");
17377274Smikeh				return (-1);
1741590Srgrimes			}
1751590Srgrimes			i = valdot;
1761590Srgrimes			do {
1771590Srgrimes				i++;
1781590Srgrimes				if (i > msgCount) {
1791590Srgrimes					printf("Referencing beyond EOF\n");
18077274Smikeh					return (-1);
1811590Srgrimes				}
1821590Srgrimes			} while ((message[i - 1].m_flag & MDELETED) != f);
1831590Srgrimes			mark(i);
1841590Srgrimes			break;
1851590Srgrimes
1861590Srgrimes		case TDASH:
1871590Srgrimes			if (beg == 0) {
1881590Srgrimes				i = valdot;
1891590Srgrimes				do {
1901590Srgrimes					i--;
1911590Srgrimes					if (i <= 0) {
1921590Srgrimes						printf("Referencing before 1\n");
19377274Smikeh						return (-1);
1941590Srgrimes					}
1951590Srgrimes				} while ((message[i - 1].m_flag & MDELETED) != f);
1961590Srgrimes				mark(i);
1971590Srgrimes			}
1981590Srgrimes			break;
1991590Srgrimes
2001590Srgrimes		case TSTRING:
2011590Srgrimes			if (beg != 0) {
2021590Srgrimes				printf("Non-numeric second argument\n");
20377274Smikeh				return (-1);
2041590Srgrimes			}
2051590Srgrimes			other++;
2061590Srgrimes			if (lexstring[0] == ':') {
2071590Srgrimes				colresult = evalcol(lexstring[1]);
2081590Srgrimes				if (colresult == 0) {
2091590Srgrimes					printf("Unknown colon modifier \"%s\"\n",
2101590Srgrimes					    lexstring);
21177274Smikeh					return (-1);
2121590Srgrimes				}
2131590Srgrimes				colmod |= colresult;
2141590Srgrimes			}
2151590Srgrimes			else
2161590Srgrimes				*np++ = savestr(lexstring);
2171590Srgrimes			break;
2181590Srgrimes
2191590Srgrimes		case TDOLLAR:
2201590Srgrimes		case TUP:
2211590Srgrimes		case TDOT:
2221590Srgrimes			lexnumber = metamess(lexstring[0], f);
2231590Srgrimes			if (lexnumber == -1)
22477274Smikeh				return (-1);
2251590Srgrimes			goto number;
2261590Srgrimes
2271590Srgrimes		case TSTAR:
2281590Srgrimes			if (other) {
2291590Srgrimes				printf("Can't mix \"*\" with anything\n");
23077274Smikeh				return (-1);
2311590Srgrimes			}
2321590Srgrimes			star++;
2331590Srgrimes			break;
2341590Srgrimes
2351590Srgrimes		case TERROR:
23677274Smikeh			return (-1);
2371590Srgrimes		}
2381590Srgrimes		tok = scan(&bufp);
2391590Srgrimes	}
2401590Srgrimes	lastcolmod = colmod;
24177274Smikeh	*np = NULL;
2421590Srgrimes	mc = 0;
2431590Srgrimes	if (star) {
2441590Srgrimes		for (i = 0; i < msgCount; i++)
2451590Srgrimes			if ((message[i].m_flag & MDELETED) == f) {
2461590Srgrimes				mark(i+1);
2471590Srgrimes				mc++;
2481590Srgrimes			}
2491590Srgrimes		if (mc == 0) {
2501590Srgrimes			printf("No applicable messages.\n");
25177274Smikeh			return (-1);
2521590Srgrimes		}
25377274Smikeh		return (0);
2541590Srgrimes	}
2551590Srgrimes
2561590Srgrimes	/*
2571590Srgrimes	 * If no numbers were given, mark all of the messages,
2581590Srgrimes	 * so that we can unmark any whose sender was not selected
2591590Srgrimes	 * if any user names were given.
2601590Srgrimes	 */
2611590Srgrimes
2621590Srgrimes	if ((np > namelist || colmod != 0) && mc == 0)
2631590Srgrimes		for (i = 1; i <= msgCount; i++)
2641590Srgrimes			if ((message[i-1].m_flag & MDELETED) == f)
2651590Srgrimes				mark(i);
2661590Srgrimes
2671590Srgrimes	/*
2681590Srgrimes	 * If any names were given, go through and eliminate any
2691590Srgrimes	 * messages whose senders were not requested.
2701590Srgrimes	 */
2711590Srgrimes
2721590Srgrimes	if (np > namelist) {
2731590Srgrimes		for (i = 1; i <= msgCount; i++) {
27477274Smikeh			for (mc = 0, np = &namelist[0]; *np != NULL; np++)
2751590Srgrimes				if (**np == '/') {
2761590Srgrimes					if (matchsubj(*np, i)) {
2771590Srgrimes						mc++;
2781590Srgrimes						break;
2791590Srgrimes					}
2801590Srgrimes				}
2811590Srgrimes				else {
2821590Srgrimes					if (matchsender(*np, i)) {
2831590Srgrimes						mc++;
2841590Srgrimes						break;
2851590Srgrimes					}
2861590Srgrimes				}
2871590Srgrimes			if (mc == 0)
2881590Srgrimes				unmark(i);
2891590Srgrimes		}
2901590Srgrimes
2911590Srgrimes		/*
2921590Srgrimes		 * Make sure we got some decent messages.
2931590Srgrimes		 */
2941590Srgrimes
2951590Srgrimes		mc = 0;
2961590Srgrimes		for (i = 1; i <= msgCount; i++)
2971590Srgrimes			if (message[i-1].m_flag & MMARK) {
2981590Srgrimes				mc++;
2991590Srgrimes				break;
3001590Srgrimes			}
3011590Srgrimes		if (mc == 0) {
3021590Srgrimes			printf("No applicable messages from {%s",
3031590Srgrimes				namelist[0]);
30477274Smikeh			for (np = &namelist[1]; *np != NULL; np++)
3051590Srgrimes				printf(", %s", *np);
3061590Srgrimes			printf("}\n");
30777274Smikeh			return (-1);
3081590Srgrimes		}
3091590Srgrimes	}
3101590Srgrimes
3111590Srgrimes	/*
3121590Srgrimes	 * If any colon modifiers were given, go through and
3131590Srgrimes	 * unmark any messages which do not satisfy the modifiers.
3141590Srgrimes	 */
3151590Srgrimes
3161590Srgrimes	if (colmod != 0) {
3171590Srgrimes		for (i = 1; i <= msgCount; i++) {
31877274Smikeh			struct coltab *colp;
3191590Srgrimes
3201590Srgrimes			mp = &message[i - 1];
32177274Smikeh			for (colp = &coltab[0]; colp->co_char != '\0'; colp++)
3221590Srgrimes				if (colp->co_bit & colmod)
3231590Srgrimes					if ((mp->m_flag & colp->co_mask)
3241590Srgrimes					    != colp->co_equal)
3251590Srgrimes						unmark(i);
3268874Srgrimes
3271590Srgrimes		}
3281590Srgrimes		for (mp = &message[0]; mp < &message[msgCount]; mp++)
3291590Srgrimes			if (mp->m_flag & MMARK)
3301590Srgrimes				break;
3311590Srgrimes		if (mp >= &message[msgCount]) {
33277274Smikeh			struct coltab *colp;
3331590Srgrimes
3341590Srgrimes			printf("No messages satisfy");
33577274Smikeh			for (colp = &coltab[0]; colp->co_char != '\0'; colp++)
3361590Srgrimes				if (colp->co_bit & colmod)
3371590Srgrimes					printf(" :%c", colp->co_char);
3381590Srgrimes			printf("\n");
33977274Smikeh			return (-1);
3401590Srgrimes		}
3411590Srgrimes	}
34277274Smikeh	return (0);
3431590Srgrimes}
3441590Srgrimes
3451590Srgrimes/*
3461590Srgrimes * Turn the character after a colon modifier into a bit
3471590Srgrimes * value.
3481590Srgrimes */
3491590Srgrimesint
3501590Srgrimesevalcol(col)
3511590Srgrimes	int col;
3521590Srgrimes{
35377274Smikeh	struct coltab *colp;
3541590Srgrimes
3551590Srgrimes	if (col == 0)
35677274Smikeh		return (lastcolmod);
35777274Smikeh	for (colp = &coltab[0]; colp->co_char != '\0'; colp++)
3581590Srgrimes		if (colp->co_char == col)
35977274Smikeh			return (colp->co_bit);
36077274Smikeh	return (0);
3611590Srgrimes}
3621590Srgrimes
3631590Srgrimes/*
3641590Srgrimes * Check the passed message number for legality and proper flags.
3651590Srgrimes * If f is MDELETED, then either kind will do.  Otherwise, the message
3661590Srgrimes * has to be undeleted.
3671590Srgrimes */
3681590Srgrimesint
3691590Srgrimescheck(mesg, f)
3701590Srgrimes	int mesg, f;
3711590Srgrimes{
37277274Smikeh	struct message *mp;
3731590Srgrimes
3741590Srgrimes	if (mesg < 1 || mesg > msgCount) {
3751590Srgrimes		printf("%d: Invalid message number\n", mesg);
37677274Smikeh		return (-1);
3771590Srgrimes	}
3781590Srgrimes	mp = &message[mesg-1];
3791590Srgrimes	if (f != MDELETED && (mp->m_flag & MDELETED) != 0) {
3801590Srgrimes		printf("%d: Inappropriate message\n", mesg);
38177274Smikeh		return (-1);
3821590Srgrimes	}
38377274Smikeh	return (0);
3841590Srgrimes}
3851590Srgrimes
3861590Srgrimes/*
3871590Srgrimes * Scan out the list of string arguments, shell style
3881590Srgrimes * for a RAWLIST.
3891590Srgrimes */
3901590Srgrimesint
3911590Srgrimesgetrawlist(line, argv, argc)
3921590Srgrimes	char line[];
3931590Srgrimes	char **argv;
3941590Srgrimes	int  argc;
3951590Srgrimes{
39677274Smikeh	char c, *cp, *cp2, quotec;
3971590Srgrimes	int argn;
39874769Smikeh	char *linebuf;
39974769Smikeh	size_t linebufsize = BUFSIZ;
4001590Srgrimes
40177274Smikeh	if ((linebuf = malloc(linebufsize)) == NULL)
40274769Smikeh		err(1, "Out of memory");
40374769Smikeh
4041590Srgrimes	argn = 0;
4051590Srgrimes	cp = line;
4061590Srgrimes	for (;;) {
4071590Srgrimes		for (; *cp == ' ' || *cp == '\t'; cp++)
4081590Srgrimes			;
4091590Srgrimes		if (*cp == '\0')
4101590Srgrimes			break;
4111590Srgrimes		if (argn >= argc - 1) {
4121590Srgrimes			printf(
4131590Srgrimes			"Too many elements in the list; excess discarded.\n");
4141590Srgrimes			break;
4151590Srgrimes		}
4161590Srgrimes		cp2 = linebuf;
4171590Srgrimes		quotec = '\0';
4181590Srgrimes		while ((c = *cp) != '\0') {
41974769Smikeh			/* Allocate more space if necessary */
42074769Smikeh			if (cp2 - linebuf == linebufsize - 1) {
42174769Smikeh				linebufsize += BUFSIZ;
42274769Smikeh				if ((linebuf = realloc(linebuf, linebufsize)) == NULL)
42374769Smikeh					err(1, "Out of memory");
42474769Smikeh				cp2 = linebuf + linebufsize - BUFSIZ - 1;
42574769Smikeh			}
4261590Srgrimes			cp++;
4271590Srgrimes			if (quotec != '\0') {
4281590Srgrimes				if (c == quotec)
4291590Srgrimes					quotec = '\0';
4301590Srgrimes				else if (c == '\\')
4311590Srgrimes					switch (c = *cp++) {
4321590Srgrimes					case '\0':
4331590Srgrimes						*cp2++ = '\\';
4341590Srgrimes						cp--;
4351590Srgrimes						break;
4361590Srgrimes					case '0': case '1': case '2': case '3':
4371590Srgrimes					case '4': case '5': case '6': case '7':
4381590Srgrimes						c -= '0';
4391590Srgrimes						if (*cp >= '0' && *cp <= '7')
4401590Srgrimes							c = c * 8 + *cp++ - '0';
4411590Srgrimes						if (*cp >= '0' && *cp <= '7')
4421590Srgrimes							c = c * 8 + *cp++ - '0';
4431590Srgrimes						*cp2++ = c;
4441590Srgrimes						break;
4451590Srgrimes					case 'b':
4461590Srgrimes						*cp2++ = '\b';
4471590Srgrimes						break;
4481590Srgrimes					case 'f':
4491590Srgrimes						*cp2++ = '\f';
4501590Srgrimes						break;
4511590Srgrimes					case 'n':
4521590Srgrimes						*cp2++ = '\n';
4531590Srgrimes						break;
4541590Srgrimes					case 'r':
4551590Srgrimes						*cp2++ = '\r';
4561590Srgrimes						break;
4571590Srgrimes					case 't':
4581590Srgrimes						*cp2++ = '\t';
4591590Srgrimes						break;
4601590Srgrimes					case 'v':
4611590Srgrimes						*cp2++ = '\v';
4621590Srgrimes						break;
4631590Srgrimes					default:
4641590Srgrimes						*cp2++ = c;
4651590Srgrimes					}
4661590Srgrimes				else if (c == '^') {
4671590Srgrimes					c = *cp++;
4681590Srgrimes					if (c == '?')
4691590Srgrimes						*cp2++ = '\177';
4701590Srgrimes					/* null doesn't show up anyway */
47177274Smikeh					else if ((c >= 'A' && c <= '_') ||
47277274Smikeh					    (c >= 'a' && c <= 'z'))
4731590Srgrimes						*cp2++ = c & 037;
4741590Srgrimes					else {
4751590Srgrimes						*cp2++ = '^';
4761590Srgrimes						cp--;
4771590Srgrimes					}
4781590Srgrimes				} else
4791590Srgrimes					*cp2++ = c;
4801590Srgrimes			} else if (c == '"' || c == '\'')
4811590Srgrimes				quotec = c;
4821590Srgrimes			else if (c == ' ' || c == '\t')
4831590Srgrimes				break;
4841590Srgrimes			else
4851590Srgrimes				*cp2++ = c;
4861590Srgrimes		}
4871590Srgrimes		*cp2 = '\0';
4881590Srgrimes		argv[argn++] = savestr(linebuf);
4891590Srgrimes	}
49077274Smikeh	argv[argn] = NULL;
49177274Smikeh	(void)free(linebuf);
49277274Smikeh	return (argn);
4931590Srgrimes}
4941590Srgrimes
4951590Srgrimes/*
4961590Srgrimes * scan out a single lexical item and return its token number,
4971590Srgrimes * updating the string pointer passed **p.  Also, store the value
4981590Srgrimes * of the number or string scanned in lexnumber or lexstring as
4991590Srgrimes * appropriate.  In any event, store the scanned `thing' in lexstring.
5001590Srgrimes */
5011590Srgrimes
5021590Srgrimesstruct lex {
5031590Srgrimes	char	l_char;
5041590Srgrimes	char	l_token;
5051590Srgrimes} singles[] = {
50677274Smikeh	{ '$',	TDOLLAR	},
50777274Smikeh	{ '.',	TDOT	},
50877274Smikeh	{ '^',	TUP 	},
50977274Smikeh	{ '*',	TSTAR 	},
51077274Smikeh	{ '-',	TDASH 	},
51177274Smikeh	{ '+',	TPLUS 	},
51277274Smikeh	{ '(',	TOPEN 	},
51377274Smikeh	{ ')',	TCLOSE 	},
51477274Smikeh	{ 0,	0 	}
5151590Srgrimes};
5161590Srgrimes
5171590Srgrimesint
5181590Srgrimesscan(sp)
5191590Srgrimes	char **sp;
5201590Srgrimes{
52177274Smikeh	char *cp, *cp2;
52277274Smikeh	int c;
52377274Smikeh	struct lex *lp;
5241590Srgrimes	int quotec;
5251590Srgrimes
5261590Srgrimes	if (regretp >= 0) {
5271590Srgrimes		strcpy(lexstring, string_stack[regretp]);
5281590Srgrimes		lexnumber = numberstack[regretp];
52977274Smikeh		return (regretstack[regretp--]);
5301590Srgrimes	}
5311590Srgrimes	cp = *sp;
5321590Srgrimes	cp2 = lexstring;
5331590Srgrimes	c = *cp++;
5341590Srgrimes
5351590Srgrimes	/*
5361590Srgrimes	 * strip away leading white space.
5371590Srgrimes	 */
5381590Srgrimes
5391590Srgrimes	while (c == ' ' || c == '\t')
5401590Srgrimes		c = *cp++;
5411590Srgrimes
5421590Srgrimes	/*
5431590Srgrimes	 * If no characters remain, we are at end of line,
5441590Srgrimes	 * so report that.
5451590Srgrimes	 */
5461590Srgrimes
5471590Srgrimes	if (c == '\0') {
5481590Srgrimes		*sp = --cp;
54977274Smikeh		return (TEOL);
5501590Srgrimes	}
5511590Srgrimes
5521590Srgrimes	/*
5531590Srgrimes	 * If the leading character is a digit, scan
5541590Srgrimes	 * the number and convert it on the fly.
5551590Srgrimes	 * Return TNUMBER when done.
5561590Srgrimes	 */
5571590Srgrimes
5581590Srgrimes	if (isdigit(c)) {
5591590Srgrimes		lexnumber = 0;
5601590Srgrimes		while (isdigit(c)) {
5611590Srgrimes			lexnumber = lexnumber*10 + c - '0';
5621590Srgrimes			*cp2++ = c;
5631590Srgrimes			c = *cp++;
5641590Srgrimes		}
5651590Srgrimes		*cp2 = '\0';
5661590Srgrimes		*sp = --cp;
56777274Smikeh		return (TNUMBER);
5681590Srgrimes	}
5691590Srgrimes
5701590Srgrimes	/*
5711590Srgrimes	 * Check for single character tokens; return such
5721590Srgrimes	 * if found.
5731590Srgrimes	 */
5741590Srgrimes
57577274Smikeh	for (lp = &singles[0]; lp->l_char != '\0'; lp++)
5761590Srgrimes		if (c == lp->l_char) {
5771590Srgrimes			lexstring[0] = c;
5781590Srgrimes			lexstring[1] = '\0';
5791590Srgrimes			*sp = cp;
58077274Smikeh			return (lp->l_token);
5811590Srgrimes		}
5821590Srgrimes
5831590Srgrimes	/*
5841590Srgrimes	 * We've got a string!  Copy all the characters
5851590Srgrimes	 * of the string into lexstring, until we see
5861590Srgrimes	 * a null, space, or tab.
5871590Srgrimes	 * If the lead character is a " or ', save it
5881590Srgrimes	 * and scan until you get another.
5891590Srgrimes	 */
5901590Srgrimes
5911590Srgrimes	quotec = 0;
5921590Srgrimes	if (c == '\'' || c == '"') {
5931590Srgrimes		quotec = c;
5941590Srgrimes		c = *cp++;
5951590Srgrimes	}
5961590Srgrimes	while (c != '\0') {
5971590Srgrimes		if (c == quotec) {
5981590Srgrimes			cp++;
5991590Srgrimes			break;
6001590Srgrimes		}
6011590Srgrimes		if (quotec == 0 && (c == ' ' || c == '\t'))
6021590Srgrimes			break;
6031590Srgrimes		if (cp2 - lexstring < STRINGLEN-1)
6041590Srgrimes			*cp2++ = c;
6051590Srgrimes		c = *cp++;
6061590Srgrimes	}
60777274Smikeh	if (quotec && c == '\0') {
6081590Srgrimes		fprintf(stderr, "Missing %c\n", quotec);
60977274Smikeh		return (TERROR);
6101590Srgrimes	}
6111590Srgrimes	*sp = --cp;
6121590Srgrimes	*cp2 = '\0';
61377274Smikeh	return (TSTRING);
6141590Srgrimes}
6151590Srgrimes
6161590Srgrimes/*
6171590Srgrimes * Unscan the named token by pushing it onto the regret stack.
6181590Srgrimes */
6191590Srgrimesvoid
6201590Srgrimesregret(token)
6211590Srgrimes	int token;
6221590Srgrimes{
6231590Srgrimes	if (++regretp >= REGDEP)
62474769Smikeh		errx(1, "Too many regrets");
6251590Srgrimes	regretstack[regretp] = token;
6261590Srgrimes	lexstring[STRINGLEN-1] = '\0';
6271590Srgrimes	string_stack[regretp] = savestr(lexstring);
6281590Srgrimes	numberstack[regretp] = lexnumber;
6291590Srgrimes}
6301590Srgrimes
6311590Srgrimes/*
6321590Srgrimes * Reset all the scanner global variables.
6331590Srgrimes */
6341590Srgrimesvoid
6351590Srgrimesscaninit()
6361590Srgrimes{
6371590Srgrimes	regretp = -1;
6381590Srgrimes}
6391590Srgrimes
6401590Srgrimes/*
6411590Srgrimes * Find the first message whose flags & m == f  and return
6421590Srgrimes * its message number.
6431590Srgrimes */
6441590Srgrimesint
6451590Srgrimesfirst(f, m)
6461590Srgrimes	int f, m;
6471590Srgrimes{
64877274Smikeh	struct message *mp;
6491590Srgrimes
6501590Srgrimes	if (msgCount == 0)
65177274Smikeh		return (0);
6521590Srgrimes	f &= MDELETED;
6531590Srgrimes	m &= MDELETED;
6541590Srgrimes	for (mp = dot; mp < &message[msgCount]; mp++)
6551590Srgrimes		if ((mp->m_flag & m) == f)
65677274Smikeh			return (mp - message + 1);
6571590Srgrimes	for (mp = dot-1; mp >= &message[0]; mp--)
6581590Srgrimes		if ((mp->m_flag & m) == f)
65977274Smikeh			return (mp - message + 1);
66077274Smikeh	return (0);
6611590Srgrimes}
6621590Srgrimes
6631590Srgrimes/*
6641590Srgrimes * See if the passed name sent the passed message number.  Return true
6651590Srgrimes * if so.
6661590Srgrimes */
6671590Srgrimesint
6681590Srgrimesmatchsender(str, mesg)
6691590Srgrimes	char *str;
6701590Srgrimes	int mesg;
6711590Srgrimes{
67277274Smikeh	char *cp, *cp2, *backup;
6731590Srgrimes
67477274Smikeh	if (*str == '\0')	/* null string matches nothing instead of everything */
67577274Smikeh		return (0);
6761590Srgrimes	backup = cp2 = nameof(&message[mesg - 1], 0);
6771590Srgrimes	cp = str;
67877274Smikeh	while (*cp2 != '\0') {
67977274Smikeh		if (*cp == '\0')
68077274Smikeh			return (1);
68174769Smikeh		if (toupper(*cp++) != toupper(*cp2++)) {
6821590Srgrimes			cp2 = ++backup;
6831590Srgrimes			cp = str;
6841590Srgrimes		}
6851590Srgrimes	}
68677274Smikeh	return (*cp == '\0');
6871590Srgrimes}
6881590Srgrimes
6891590Srgrimes/*
69088150Smikeh * See if the passed name received the passed message number.  Return true
69188150Smikeh * if so.
69288150Smikeh */
69388150Smikeh
69488150Smikehstatic char *to_fields[] = { "to", "cc", "bcc", NULL };
69588150Smikeh
69688150Smikehint
69788150Smikehmatchto(str, mesg)
69888150Smikeh	char *str;
69988150Smikeh	int mesg;
70088150Smikeh{
70188150Smikeh	struct message *mp;
70288150Smikeh	char *cp, *cp2, *backup, **to;
70388150Smikeh
70488150Smikeh	str++;
70588150Smikeh
70688150Smikeh	/* null string matches nothing instead of everything */
70788150Smikeh	if (*str == '\0')
70888150Smikeh		return (0);
70988150Smikeh
71088150Smikeh	mp = &message[mesg - 1];
71188150Smikeh
71288150Smikeh	for (to = to_fields; *to != NULL; to++) {
71388150Smikeh		cp = str;
71488150Smikeh		cp2 = hfield(*to, mp);
71588150Smikeh		if (cp2 != NULL) {
71688150Smikeh			backup = cp2;
71788150Smikeh			while (*cp2 != '\0') {
71888150Smikeh				if (*cp == '\0')
71988150Smikeh					return (1);
72088150Smikeh				if (toupper(*cp++) != toupper(*cp2++)) {
72188150Smikeh					cp2 = ++backup;
72288150Smikeh					cp = str;
72388150Smikeh				}
72488150Smikeh			}
72588150Smikeh			if (*cp == '\0')
72688150Smikeh				return (1);
72788150Smikeh		}
72888150Smikeh	}
72988150Smikeh	return (0);
73088150Smikeh}
73188150Smikeh
73288150Smikeh/*
7331590Srgrimes * See if the given string matches inside the subject field of the
7341590Srgrimes * given message.  For the purpose of the scan, we ignore case differences.
7351590Srgrimes * If it does, return true.  The string search argument is assumed to
7361590Srgrimes * have the form "/search-string."  If it is of the form "/," we use the
7371590Srgrimes * previous search string.
7381590Srgrimes */
7391590Srgrimes
74074769Smikehchar lastscan[STRINGLEN];
7411590Srgrimesint
7421590Srgrimesmatchsubj(str, mesg)
7431590Srgrimes	char *str;
7441590Srgrimes	int mesg;
7451590Srgrimes{
74677274Smikeh	struct message *mp;
74777274Smikeh	char *cp, *cp2, *backup;
7481590Srgrimes
7491590Srgrimes	str++;
75074769Smikeh	if (*str == '\0')
7511590Srgrimes		str = lastscan;
7521590Srgrimes	else
75374769Smikeh		strlcpy(lastscan, str, sizeof(lastscan));
7541590Srgrimes	mp = &message[mesg-1];
7558874Srgrimes
7561590Srgrimes	/*
7571590Srgrimes	 * Now look, ignoring case, for the word in the string.
7581590Srgrimes	 */
7591590Srgrimes
76077274Smikeh	if (value("searchheaders") && (cp = strchr(str, ':')) != NULL) {
76188150Smikeh		/* Check for special case "/To:" */
76288150Smikeh		if (strncasecmp(str, "To:", 3) == 0)
76388150Smikeh			return (matchto(cp, mesg));
7641590Srgrimes		*cp++ = '\0';
76588150Smikeh		cp2 = hfield(*str != '\0' ? str : "subject", mp);
7661590Srgrimes		cp[-1] = ':';
7671590Srgrimes		str = cp;
7681590Srgrimes	} else {
7691590Srgrimes		cp = str;
7701590Srgrimes		cp2 = hfield("subject", mp);
7711590Srgrimes	}
77277274Smikeh	if (cp2 == NULL)
77377274Smikeh		return (0);
7741590Srgrimes	backup = cp2;
77577274Smikeh	while (*cp2 != '\0') {
77677274Smikeh		if (*cp == '\0')
77777274Smikeh			return (1);
77874769Smikeh		if (toupper(*cp++) != toupper(*cp2++)) {
7791590Srgrimes			cp2 = ++backup;
7801590Srgrimes			cp = str;
7811590Srgrimes		}
7821590Srgrimes	}
78377274Smikeh	return (*cp == 0);
7841590Srgrimes}
7851590Srgrimes
7861590Srgrimes/*
7871590Srgrimes * Mark the named message by setting its mark bit.
7881590Srgrimes */
7891590Srgrimesvoid
7901590Srgrimesmark(mesg)
7911590Srgrimes	int mesg;
7921590Srgrimes{
79377274Smikeh	int i;
7941590Srgrimes
7951590Srgrimes	i = mesg;
7961590Srgrimes	if (i < 1 || i > msgCount)
79774769Smikeh		errx(1, "Bad message number to mark");
7981590Srgrimes	message[i-1].m_flag |= MMARK;
7991590Srgrimes}
8001590Srgrimes
8011590Srgrimes/*
8021590Srgrimes * Unmark the named message.
8031590Srgrimes */
8041590Srgrimesvoid
8051590Srgrimesunmark(mesg)
8061590Srgrimes	int mesg;
8071590Srgrimes{
80877274Smikeh	int i;
8091590Srgrimes
8101590Srgrimes	i = mesg;
8111590Srgrimes	if (i < 1 || i > msgCount)
81274769Smikeh		errx(1, "Bad message number to unmark");
8131590Srgrimes	message[i-1].m_flag &= ~MMARK;
8141590Srgrimes}
8151590Srgrimes
8161590Srgrimes/*
8171590Srgrimes * Return the message number corresponding to the passed meta character.
8181590Srgrimes */
8191590Srgrimesint
8201590Srgrimesmetamess(meta, f)
8211590Srgrimes	int meta, f;
8221590Srgrimes{
82377274Smikeh	int c, m;
82477274Smikeh	struct message *mp;
8251590Srgrimes
8261590Srgrimes	c = meta;
8271590Srgrimes	switch (c) {
8281590Srgrimes	case '^':
8291590Srgrimes		/*
8301590Srgrimes		 * First 'good' message left.
8311590Srgrimes		 */
8321590Srgrimes		for (mp = &message[0]; mp < &message[msgCount]; mp++)
8331590Srgrimes			if ((mp->m_flag & MDELETED) == f)
83477274Smikeh				return (mp - &message[0] + 1);
8351590Srgrimes		printf("No applicable messages\n");
83677274Smikeh		return (-1);
8371590Srgrimes
8381590Srgrimes	case '$':
8391590Srgrimes		/*
8401590Srgrimes		 * Last 'good message left.
8411590Srgrimes		 */
8421590Srgrimes		for (mp = &message[msgCount-1]; mp >= &message[0]; mp--)
8431590Srgrimes			if ((mp->m_flag & MDELETED) == f)
84477274Smikeh				return (mp - &message[0] + 1);
8451590Srgrimes		printf("No applicable messages\n");
84677274Smikeh		return (-1);
8471590Srgrimes
8481590Srgrimes	case '.':
8498874Srgrimes		/*
8501590Srgrimes		 * Current message.
8511590Srgrimes		 */
8521590Srgrimes		m = dot - &message[0] + 1;
8531590Srgrimes		if ((dot->m_flag & MDELETED) != f) {
8541590Srgrimes			printf("%d: Inappropriate message\n", m);
85577274Smikeh			return (-1);
8561590Srgrimes		}
85777274Smikeh		return (m);
8581590Srgrimes
8591590Srgrimes	default:
8601590Srgrimes		printf("Unknown metachar (%c)\n", c);
86177274Smikeh		return (-1);
8621590Srgrimes	}
8631590Srgrimes}
864