list.c revision 8874
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
351590Srgrimesstatic char sccsid[] = "@(#)list.c	8.2 (Berkeley) 4/19/94";
361590Srgrimes#endif /* not lint */
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
551590Srgrimesgetmsglist(buf, vector, flags)
561590Srgrimes	char *buf;
571590Srgrimes	int *vector, flags;
581590Srgrimes{
591590Srgrimes	register int *ip;
601590Srgrimes	register struct message *mp;
611590Srgrimes
621590Srgrimes	if (msgCount == 0) {
631590Srgrimes		*vector = 0;
641590Srgrimes		return 0;
651590Srgrimes	}
661590Srgrimes	if (markall(buf, flags) < 0)
671590Srgrimes		return(-1);
681590Srgrimes	ip = vector;
691590Srgrimes	for (mp = &message[0]; mp < &message[msgCount]; mp++)
701590Srgrimes		if (mp->m_flag & MMARK)
711590Srgrimes			*ip++ = mp - &message[0] + 1;
721590Srgrimes	*ip = 0;
731590Srgrimes	return(ip - vector);
741590Srgrimes}
751590Srgrimes
761590Srgrimes/*
771590Srgrimes * Mark all messages that the user wanted from the command
781590Srgrimes * line in the message structure.  Return 0 on success, -1
791590Srgrimes * on error.
801590Srgrimes */
811590Srgrimes
821590Srgrimes/*
831590Srgrimes * Bit values for colon modifiers.
841590Srgrimes */
851590Srgrimes
861590Srgrimes#define	CMNEW		01		/* New messages */
871590Srgrimes#define	CMOLD		02		/* Old messages */
881590Srgrimes#define	CMUNREAD	04		/* Unread messages */
891590Srgrimes#define	CMDELETED	010		/* Deleted messages */
901590Srgrimes#define	CMREAD		020		/* Read messages */
911590Srgrimes
921590Srgrimes/*
931590Srgrimes * The following table describes the letters which can follow
941590Srgrimes * the colon and gives the corresponding modifier bit.
951590Srgrimes */
961590Srgrimes
971590Srgrimesstruct coltab {
981590Srgrimes	char	co_char;		/* What to find past : */
991590Srgrimes	int	co_bit;			/* Associated modifier bit */
1001590Srgrimes	int	co_mask;		/* m_status bits to mask */
1011590Srgrimes	int	co_equal;		/* ... must equal this */
1021590Srgrimes} coltab[] = {
1031590Srgrimes	'n',		CMNEW,		MNEW,		MNEW,
1041590Srgrimes	'o',		CMOLD,		MNEW,		0,
1051590Srgrimes	'u',		CMUNREAD,	MREAD,		0,
1061590Srgrimes	'd',		CMDELETED,	MDELETED,	MDELETED,
1071590Srgrimes	'r',		CMREAD,		MREAD,		MREAD,
1081590Srgrimes	0,		0,		0,		0
1091590Srgrimes};
1101590Srgrimes
1111590Srgrimesstatic	int	lastcolmod;
1121590Srgrimes
1131590Srgrimesint
1141590Srgrimesmarkall(buf, f)
1151590Srgrimes	char buf[];
1161590Srgrimes	int f;
1171590Srgrimes{
1181590Srgrimes	register char **np;
1191590Srgrimes	register int i;
1201590Srgrimes	register struct message *mp;
1211590Srgrimes	char *namelist[NMLSIZE], *bufp;
1221590Srgrimes	int tok, beg, mc, star, other, valdot, colmod, colresult;
1231590Srgrimes
1241590Srgrimes	valdot = dot - &message[0] + 1;
1251590Srgrimes	colmod = 0;
1261590Srgrimes	for (i = 1; i <= msgCount; i++)
1271590Srgrimes		unmark(i);
1281590Srgrimes	bufp = buf;
1291590Srgrimes	mc = 0;
1301590Srgrimes	np = &namelist[0];
1311590Srgrimes	scaninit();
1321590Srgrimes	tok = scan(&bufp);
1331590Srgrimes	star = 0;
1341590Srgrimes	other = 0;
1351590Srgrimes	beg = 0;
1361590Srgrimes	while (tok != TEOL) {
1371590Srgrimes		switch (tok) {
1381590Srgrimes		case TNUMBER:
1391590Srgrimesnumber:
1401590Srgrimes			if (star) {
1411590Srgrimes				printf("No numbers mixed with *\n");
1421590Srgrimes				return(-1);
1431590Srgrimes			}
1441590Srgrimes			mc++;
1451590Srgrimes			other++;
1461590Srgrimes			if (beg != 0) {
1471590Srgrimes				if (check(lexnumber, f))
1481590Srgrimes					return(-1);
1491590Srgrimes				for (i = beg; i <= lexnumber; i++)
1501590Srgrimes					if (f == MDELETED || (message[i - 1].m_flag & MDELETED) == 0)
1511590Srgrimes						mark(i);
1521590Srgrimes				beg = 0;
1531590Srgrimes				break;
1541590Srgrimes			}
1551590Srgrimes			beg = lexnumber;
1561590Srgrimes			if (check(beg, f))
1571590Srgrimes				return(-1);
1581590Srgrimes			tok = scan(&bufp);
1591590Srgrimes			regret(tok);
1601590Srgrimes			if (tok != TDASH) {
1611590Srgrimes				mark(beg);
1621590Srgrimes				beg = 0;
1631590Srgrimes			}
1641590Srgrimes			break;
1651590Srgrimes
1661590Srgrimes		case TPLUS:
1671590Srgrimes			if (beg != 0) {
1681590Srgrimes				printf("Non-numeric second argument\n");
1691590Srgrimes				return(-1);
1701590Srgrimes			}
1711590Srgrimes			i = valdot;
1721590Srgrimes			do {
1731590Srgrimes				i++;
1741590Srgrimes				if (i > msgCount) {
1751590Srgrimes					printf("Referencing beyond EOF\n");
1761590Srgrimes					return(-1);
1771590Srgrimes				}
1781590Srgrimes			} while ((message[i - 1].m_flag & MDELETED) != f);
1791590Srgrimes			mark(i);
1801590Srgrimes			break;
1811590Srgrimes
1821590Srgrimes		case TDASH:
1831590Srgrimes			if (beg == 0) {
1841590Srgrimes				i = valdot;
1851590Srgrimes				do {
1861590Srgrimes					i--;
1871590Srgrimes					if (i <= 0) {
1881590Srgrimes						printf("Referencing before 1\n");
1891590Srgrimes						return(-1);
1901590Srgrimes					}
1911590Srgrimes				} while ((message[i - 1].m_flag & MDELETED) != f);
1921590Srgrimes				mark(i);
1931590Srgrimes			}
1941590Srgrimes			break;
1951590Srgrimes
1961590Srgrimes		case TSTRING:
1971590Srgrimes			if (beg != 0) {
1981590Srgrimes				printf("Non-numeric second argument\n");
1991590Srgrimes				return(-1);
2001590Srgrimes			}
2011590Srgrimes			other++;
2021590Srgrimes			if (lexstring[0] == ':') {
2031590Srgrimes				colresult = evalcol(lexstring[1]);
2041590Srgrimes				if (colresult == 0) {
2051590Srgrimes					printf("Unknown colon modifier \"%s\"\n",
2061590Srgrimes					    lexstring);
2071590Srgrimes					return(-1);
2081590Srgrimes				}
2091590Srgrimes				colmod |= colresult;
2101590Srgrimes			}
2111590Srgrimes			else
2121590Srgrimes				*np++ = savestr(lexstring);
2131590Srgrimes			break;
2141590Srgrimes
2151590Srgrimes		case TDOLLAR:
2161590Srgrimes		case TUP:
2171590Srgrimes		case TDOT:
2181590Srgrimes			lexnumber = metamess(lexstring[0], f);
2191590Srgrimes			if (lexnumber == -1)
2201590Srgrimes				return(-1);
2211590Srgrimes			goto number;
2221590Srgrimes
2231590Srgrimes		case TSTAR:
2241590Srgrimes			if (other) {
2251590Srgrimes				printf("Can't mix \"*\" with anything\n");
2261590Srgrimes				return(-1);
2271590Srgrimes			}
2281590Srgrimes			star++;
2291590Srgrimes			break;
2301590Srgrimes
2311590Srgrimes		case TERROR:
2321590Srgrimes			return -1;
2331590Srgrimes		}
2341590Srgrimes		tok = scan(&bufp);
2351590Srgrimes	}
2361590Srgrimes	lastcolmod = colmod;
2371590Srgrimes	*np = NOSTR;
2381590Srgrimes	mc = 0;
2391590Srgrimes	if (star) {
2401590Srgrimes		for (i = 0; i < msgCount; i++)
2411590Srgrimes			if ((message[i].m_flag & MDELETED) == f) {
2421590Srgrimes				mark(i+1);
2431590Srgrimes				mc++;
2441590Srgrimes			}
2451590Srgrimes		if (mc == 0) {
2461590Srgrimes			printf("No applicable messages.\n");
2471590Srgrimes			return(-1);
2481590Srgrimes		}
2491590Srgrimes		return(0);
2501590Srgrimes	}
2511590Srgrimes
2521590Srgrimes	/*
2531590Srgrimes	 * If no numbers were given, mark all of the messages,
2541590Srgrimes	 * so that we can unmark any whose sender was not selected
2551590Srgrimes	 * if any user names were given.
2561590Srgrimes	 */
2571590Srgrimes
2581590Srgrimes	if ((np > namelist || colmod != 0) && mc == 0)
2591590Srgrimes		for (i = 1; i <= msgCount; i++)
2601590Srgrimes			if ((message[i-1].m_flag & MDELETED) == f)
2611590Srgrimes				mark(i);
2621590Srgrimes
2631590Srgrimes	/*
2641590Srgrimes	 * If any names were given, go through and eliminate any
2651590Srgrimes	 * messages whose senders were not requested.
2661590Srgrimes	 */
2671590Srgrimes
2681590Srgrimes	if (np > namelist) {
2691590Srgrimes		for (i = 1; i <= msgCount; i++) {
2701590Srgrimes			for (mc = 0, np = &namelist[0]; *np != NOSTR; np++)
2711590Srgrimes				if (**np == '/') {
2721590Srgrimes					if (matchsubj(*np, i)) {
2731590Srgrimes						mc++;
2741590Srgrimes						break;
2751590Srgrimes					}
2761590Srgrimes				}
2771590Srgrimes				else {
2781590Srgrimes					if (matchsender(*np, i)) {
2791590Srgrimes						mc++;
2801590Srgrimes						break;
2811590Srgrimes					}
2821590Srgrimes				}
2831590Srgrimes			if (mc == 0)
2841590Srgrimes				unmark(i);
2851590Srgrimes		}
2861590Srgrimes
2871590Srgrimes		/*
2881590Srgrimes		 * Make sure we got some decent messages.
2891590Srgrimes		 */
2901590Srgrimes
2911590Srgrimes		mc = 0;
2921590Srgrimes		for (i = 1; i <= msgCount; i++)
2931590Srgrimes			if (message[i-1].m_flag & MMARK) {
2941590Srgrimes				mc++;
2951590Srgrimes				break;
2961590Srgrimes			}
2971590Srgrimes		if (mc == 0) {
2981590Srgrimes			printf("No applicable messages from {%s",
2991590Srgrimes				namelist[0]);
3001590Srgrimes			for (np = &namelist[1]; *np != NOSTR; np++)
3011590Srgrimes				printf(", %s", *np);
3021590Srgrimes			printf("}\n");
3031590Srgrimes			return(-1);
3041590Srgrimes		}
3051590Srgrimes	}
3061590Srgrimes
3071590Srgrimes	/*
3081590Srgrimes	 * If any colon modifiers were given, go through and
3091590Srgrimes	 * unmark any messages which do not satisfy the modifiers.
3101590Srgrimes	 */
3111590Srgrimes
3121590Srgrimes	if (colmod != 0) {
3131590Srgrimes		for (i = 1; i <= msgCount; i++) {
3141590Srgrimes			register struct coltab *colp;
3151590Srgrimes
3161590Srgrimes			mp = &message[i - 1];
3171590Srgrimes			for (colp = &coltab[0]; colp->co_char; colp++)
3181590Srgrimes				if (colp->co_bit & colmod)
3191590Srgrimes					if ((mp->m_flag & colp->co_mask)
3201590Srgrimes					    != colp->co_equal)
3211590Srgrimes						unmark(i);
3228874Srgrimes
3231590Srgrimes		}
3241590Srgrimes		for (mp = &message[0]; mp < &message[msgCount]; mp++)
3251590Srgrimes			if (mp->m_flag & MMARK)
3261590Srgrimes				break;
3271590Srgrimes		if (mp >= &message[msgCount]) {
3281590Srgrimes			register struct coltab *colp;
3291590Srgrimes
3301590Srgrimes			printf("No messages satisfy");
3311590Srgrimes			for (colp = &coltab[0]; colp->co_char; colp++)
3321590Srgrimes				if (colp->co_bit & colmod)
3331590Srgrimes					printf(" :%c", colp->co_char);
3341590Srgrimes			printf("\n");
3351590Srgrimes			return(-1);
3361590Srgrimes		}
3371590Srgrimes	}
3381590Srgrimes	return(0);
3391590Srgrimes}
3401590Srgrimes
3411590Srgrimes/*
3421590Srgrimes * Turn the character after a colon modifier into a bit
3431590Srgrimes * value.
3441590Srgrimes */
3451590Srgrimesint
3461590Srgrimesevalcol(col)
3471590Srgrimes	int col;
3481590Srgrimes{
3491590Srgrimes	register struct coltab *colp;
3501590Srgrimes
3511590Srgrimes	if (col == 0)
3521590Srgrimes		return(lastcolmod);
3531590Srgrimes	for (colp = &coltab[0]; colp->co_char; colp++)
3541590Srgrimes		if (colp->co_char == col)
3551590Srgrimes			return(colp->co_bit);
3561590Srgrimes	return(0);
3571590Srgrimes}
3581590Srgrimes
3591590Srgrimes/*
3601590Srgrimes * Check the passed message number for legality and proper flags.
3611590Srgrimes * If f is MDELETED, then either kind will do.  Otherwise, the message
3621590Srgrimes * has to be undeleted.
3631590Srgrimes */
3641590Srgrimesint
3651590Srgrimescheck(mesg, f)
3661590Srgrimes	int mesg, f;
3671590Srgrimes{
3681590Srgrimes	register struct message *mp;
3691590Srgrimes
3701590Srgrimes	if (mesg < 1 || mesg > msgCount) {
3711590Srgrimes		printf("%d: Invalid message number\n", mesg);
3721590Srgrimes		return(-1);
3731590Srgrimes	}
3741590Srgrimes	mp = &message[mesg-1];
3751590Srgrimes	if (f != MDELETED && (mp->m_flag & MDELETED) != 0) {
3761590Srgrimes		printf("%d: Inappropriate message\n", mesg);
3771590Srgrimes		return(-1);
3781590Srgrimes	}
3791590Srgrimes	return(0);
3801590Srgrimes}
3811590Srgrimes
3821590Srgrimes/*
3831590Srgrimes * Scan out the list of string arguments, shell style
3841590Srgrimes * for a RAWLIST.
3851590Srgrimes */
3861590Srgrimesint
3871590Srgrimesgetrawlist(line, argv, argc)
3881590Srgrimes	char line[];
3891590Srgrimes	char **argv;
3901590Srgrimes	int  argc;
3911590Srgrimes{
3921590Srgrimes	register char c, *cp, *cp2, quotec;
3931590Srgrimes	int argn;
3941590Srgrimes	char linebuf[BUFSIZ];
3951590Srgrimes
3961590Srgrimes	argn = 0;
3971590Srgrimes	cp = line;
3981590Srgrimes	for (;;) {
3991590Srgrimes		for (; *cp == ' ' || *cp == '\t'; cp++)
4001590Srgrimes			;
4011590Srgrimes		if (*cp == '\0')
4021590Srgrimes			break;
4031590Srgrimes		if (argn >= argc - 1) {
4041590Srgrimes			printf(
4051590Srgrimes			"Too many elements in the list; excess discarded.\n");
4061590Srgrimes			break;
4071590Srgrimes		}
4081590Srgrimes		cp2 = linebuf;
4091590Srgrimes		quotec = '\0';
4101590Srgrimes		while ((c = *cp) != '\0') {
4111590Srgrimes			cp++;
4121590Srgrimes			if (quotec != '\0') {
4131590Srgrimes				if (c == quotec)
4141590Srgrimes					quotec = '\0';
4151590Srgrimes				else if (c == '\\')
4161590Srgrimes					switch (c = *cp++) {
4171590Srgrimes					case '\0':
4181590Srgrimes						*cp2++ = '\\';
4191590Srgrimes						cp--;
4201590Srgrimes						break;
4211590Srgrimes					case '0': case '1': case '2': case '3':
4221590Srgrimes					case '4': case '5': case '6': case '7':
4231590Srgrimes						c -= '0';
4241590Srgrimes						if (*cp >= '0' && *cp <= '7')
4251590Srgrimes							c = c * 8 + *cp++ - '0';
4261590Srgrimes						if (*cp >= '0' && *cp <= '7')
4271590Srgrimes							c = c * 8 + *cp++ - '0';
4281590Srgrimes						*cp2++ = c;
4291590Srgrimes						break;
4301590Srgrimes					case 'b':
4311590Srgrimes						*cp2++ = '\b';
4321590Srgrimes						break;
4331590Srgrimes					case 'f':
4341590Srgrimes						*cp2++ = '\f';
4351590Srgrimes						break;
4361590Srgrimes					case 'n':
4371590Srgrimes						*cp2++ = '\n';
4381590Srgrimes						break;
4391590Srgrimes					case 'r':
4401590Srgrimes						*cp2++ = '\r';
4411590Srgrimes						break;
4421590Srgrimes					case 't':
4431590Srgrimes						*cp2++ = '\t';
4441590Srgrimes						break;
4451590Srgrimes					case 'v':
4461590Srgrimes						*cp2++ = '\v';
4471590Srgrimes						break;
4481590Srgrimes					default:
4491590Srgrimes						*cp2++ = c;
4501590Srgrimes					}
4511590Srgrimes				else if (c == '^') {
4521590Srgrimes					c = *cp++;
4531590Srgrimes					if (c == '?')
4541590Srgrimes						*cp2++ = '\177';
4551590Srgrimes					/* null doesn't show up anyway */
4561590Srgrimes					else if (c >= 'A' && c <= '_' ||
4571590Srgrimes						 c >= 'a' && c <= 'z')
4581590Srgrimes						*cp2++ = c & 037;
4591590Srgrimes					else {
4601590Srgrimes						*cp2++ = '^';
4611590Srgrimes						cp--;
4621590Srgrimes					}
4631590Srgrimes				} else
4641590Srgrimes					*cp2++ = c;
4651590Srgrimes			} else if (c == '"' || c == '\'')
4661590Srgrimes				quotec = c;
4671590Srgrimes			else if (c == ' ' || c == '\t')
4681590Srgrimes				break;
4691590Srgrimes			else
4701590Srgrimes				*cp2++ = c;
4711590Srgrimes		}
4721590Srgrimes		*cp2 = '\0';
4731590Srgrimes		argv[argn++] = savestr(linebuf);
4741590Srgrimes	}
4751590Srgrimes	argv[argn] = NOSTR;
4761590Srgrimes	return argn;
4771590Srgrimes}
4781590Srgrimes
4791590Srgrimes/*
4801590Srgrimes * scan out a single lexical item and return its token number,
4811590Srgrimes * updating the string pointer passed **p.  Also, store the value
4821590Srgrimes * of the number or string scanned in lexnumber or lexstring as
4831590Srgrimes * appropriate.  In any event, store the scanned `thing' in lexstring.
4841590Srgrimes */
4851590Srgrimes
4861590Srgrimesstruct lex {
4871590Srgrimes	char	l_char;
4881590Srgrimes	char	l_token;
4891590Srgrimes} singles[] = {
4901590Srgrimes	'$',	TDOLLAR,
4911590Srgrimes	'.',	TDOT,
4921590Srgrimes	'^',	TUP,
4931590Srgrimes	'*',	TSTAR,
4941590Srgrimes	'-',	TDASH,
4951590Srgrimes	'+',	TPLUS,
4961590Srgrimes	'(',	TOPEN,
4971590Srgrimes	')',	TCLOSE,
4981590Srgrimes	0,	0
4991590Srgrimes};
5001590Srgrimes
5011590Srgrimesint
5021590Srgrimesscan(sp)
5031590Srgrimes	char **sp;
5041590Srgrimes{
5051590Srgrimes	register char *cp, *cp2;
5061590Srgrimes	register int c;
5071590Srgrimes	register struct lex *lp;
5081590Srgrimes	int quotec;
5091590Srgrimes
5101590Srgrimes	if (regretp >= 0) {
5111590Srgrimes		strcpy(lexstring, string_stack[regretp]);
5121590Srgrimes		lexnumber = numberstack[regretp];
5131590Srgrimes		return(regretstack[regretp--]);
5141590Srgrimes	}
5151590Srgrimes	cp = *sp;
5161590Srgrimes	cp2 = lexstring;
5171590Srgrimes	c = *cp++;
5181590Srgrimes
5191590Srgrimes	/*
5201590Srgrimes	 * strip away leading white space.
5211590Srgrimes	 */
5221590Srgrimes
5231590Srgrimes	while (c == ' ' || c == '\t')
5241590Srgrimes		c = *cp++;
5251590Srgrimes
5261590Srgrimes	/*
5271590Srgrimes	 * If no characters remain, we are at end of line,
5281590Srgrimes	 * so report that.
5291590Srgrimes	 */
5301590Srgrimes
5311590Srgrimes	if (c == '\0') {
5321590Srgrimes		*sp = --cp;
5331590Srgrimes		return(TEOL);
5341590Srgrimes	}
5351590Srgrimes
5361590Srgrimes	/*
5371590Srgrimes	 * If the leading character is a digit, scan
5381590Srgrimes	 * the number and convert it on the fly.
5391590Srgrimes	 * Return TNUMBER when done.
5401590Srgrimes	 */
5411590Srgrimes
5421590Srgrimes	if (isdigit(c)) {
5431590Srgrimes		lexnumber = 0;
5441590Srgrimes		while (isdigit(c)) {
5451590Srgrimes			lexnumber = lexnumber*10 + c - '0';
5461590Srgrimes			*cp2++ = c;
5471590Srgrimes			c = *cp++;
5481590Srgrimes		}
5491590Srgrimes		*cp2 = '\0';
5501590Srgrimes		*sp = --cp;
5511590Srgrimes		return(TNUMBER);
5521590Srgrimes	}
5531590Srgrimes
5541590Srgrimes	/*
5551590Srgrimes	 * Check for single character tokens; return such
5561590Srgrimes	 * if found.
5571590Srgrimes	 */
5581590Srgrimes
5591590Srgrimes	for (lp = &singles[0]; lp->l_char != 0; lp++)
5601590Srgrimes		if (c == lp->l_char) {
5611590Srgrimes			lexstring[0] = c;
5621590Srgrimes			lexstring[1] = '\0';
5631590Srgrimes			*sp = cp;
5641590Srgrimes			return(lp->l_token);
5651590Srgrimes		}
5661590Srgrimes
5671590Srgrimes	/*
5681590Srgrimes	 * We've got a string!  Copy all the characters
5691590Srgrimes	 * of the string into lexstring, until we see
5701590Srgrimes	 * a null, space, or tab.
5711590Srgrimes	 * If the lead character is a " or ', save it
5721590Srgrimes	 * and scan until you get another.
5731590Srgrimes	 */
5741590Srgrimes
5751590Srgrimes	quotec = 0;
5761590Srgrimes	if (c == '\'' || c == '"') {
5771590Srgrimes		quotec = c;
5781590Srgrimes		c = *cp++;
5791590Srgrimes	}
5801590Srgrimes	while (c != '\0') {
5811590Srgrimes		if (c == quotec) {
5821590Srgrimes			cp++;
5831590Srgrimes			break;
5841590Srgrimes		}
5851590Srgrimes		if (quotec == 0 && (c == ' ' || c == '\t'))
5861590Srgrimes			break;
5871590Srgrimes		if (cp2 - lexstring < STRINGLEN-1)
5881590Srgrimes			*cp2++ = c;
5891590Srgrimes		c = *cp++;
5901590Srgrimes	}
5911590Srgrimes	if (quotec && c == 0) {
5921590Srgrimes		fprintf(stderr, "Missing %c\n", quotec);
5931590Srgrimes		return TERROR;
5941590Srgrimes	}
5951590Srgrimes	*sp = --cp;
5961590Srgrimes	*cp2 = '\0';
5971590Srgrimes	return(TSTRING);
5981590Srgrimes}
5991590Srgrimes
6001590Srgrimes/*
6011590Srgrimes * Unscan the named token by pushing it onto the regret stack.
6021590Srgrimes */
6031590Srgrimesvoid
6041590Srgrimesregret(token)
6051590Srgrimes	int token;
6061590Srgrimes{
6071590Srgrimes	if (++regretp >= REGDEP)
6081590Srgrimes		panic("Too many regrets");
6091590Srgrimes	regretstack[regretp] = token;
6101590Srgrimes	lexstring[STRINGLEN-1] = '\0';
6111590Srgrimes	string_stack[regretp] = savestr(lexstring);
6121590Srgrimes	numberstack[regretp] = lexnumber;
6131590Srgrimes}
6141590Srgrimes
6151590Srgrimes/*
6161590Srgrimes * Reset all the scanner global variables.
6171590Srgrimes */
6181590Srgrimesvoid
6191590Srgrimesscaninit()
6201590Srgrimes{
6211590Srgrimes	regretp = -1;
6221590Srgrimes}
6231590Srgrimes
6241590Srgrimes/*
6251590Srgrimes * Find the first message whose flags & m == f  and return
6261590Srgrimes * its message number.
6271590Srgrimes */
6281590Srgrimesint
6291590Srgrimesfirst(f, m)
6301590Srgrimes	int f, m;
6311590Srgrimes{
6321590Srgrimes	register struct message *mp;
6331590Srgrimes
6341590Srgrimes	if (msgCount == 0)
6351590Srgrimes		return 0;
6361590Srgrimes	f &= MDELETED;
6371590Srgrimes	m &= MDELETED;
6381590Srgrimes	for (mp = dot; mp < &message[msgCount]; mp++)
6391590Srgrimes		if ((mp->m_flag & m) == f)
6401590Srgrimes			return mp - message + 1;
6411590Srgrimes	for (mp = dot-1; mp >= &message[0]; mp--)
6421590Srgrimes		if ((mp->m_flag & m) == f)
6431590Srgrimes			return mp - message + 1;
6441590Srgrimes	return 0;
6451590Srgrimes}
6461590Srgrimes
6471590Srgrimes/*
6481590Srgrimes * See if the passed name sent the passed message number.  Return true
6491590Srgrimes * if so.
6501590Srgrimes */
6511590Srgrimesint
6521590Srgrimesmatchsender(str, mesg)
6531590Srgrimes	char *str;
6541590Srgrimes	int mesg;
6551590Srgrimes{
6561590Srgrimes	register char *cp, *cp2, *backup;
6571590Srgrimes
6581590Srgrimes	if (!*str)	/* null string matches nothing instead of everything */
6591590Srgrimes		return 0;
6601590Srgrimes	backup = cp2 = nameof(&message[mesg - 1], 0);
6611590Srgrimes	cp = str;
6621590Srgrimes	while (*cp2) {
6631590Srgrimes		if (*cp == 0)
6641590Srgrimes			return(1);
6651590Srgrimes		if (raise(*cp++) != raise(*cp2++)) {
6661590Srgrimes			cp2 = ++backup;
6671590Srgrimes			cp = str;
6681590Srgrimes		}
6691590Srgrimes	}
6701590Srgrimes	return(*cp == 0);
6711590Srgrimes}
6721590Srgrimes
6731590Srgrimes/*
6741590Srgrimes * See if the given string matches inside the subject field of the
6751590Srgrimes * given message.  For the purpose of the scan, we ignore case differences.
6761590Srgrimes * If it does, return true.  The string search argument is assumed to
6771590Srgrimes * have the form "/search-string."  If it is of the form "/," we use the
6781590Srgrimes * previous search string.
6791590Srgrimes */
6801590Srgrimes
6811590Srgrimeschar lastscan[128];
6821590Srgrimesint
6831590Srgrimesmatchsubj(str, mesg)
6841590Srgrimes	char *str;
6851590Srgrimes	int mesg;
6861590Srgrimes{
6871590Srgrimes	register struct message *mp;
6881590Srgrimes	register char *cp, *cp2, *backup;
6891590Srgrimes
6901590Srgrimes	str++;
6911590Srgrimes	if (strlen(str) == 0)
6921590Srgrimes		str = lastscan;
6931590Srgrimes	else
6941590Srgrimes		strcpy(lastscan, str);
6951590Srgrimes	mp = &message[mesg-1];
6968874Srgrimes
6971590Srgrimes	/*
6981590Srgrimes	 * Now look, ignoring case, for the word in the string.
6991590Srgrimes	 */
7001590Srgrimes
7011590Srgrimes	if (value("searchheaders") && (cp = index(str, ':'))) {
7021590Srgrimes		*cp++ = '\0';
7031590Srgrimes		cp2 = hfield(str, mp);
7041590Srgrimes		cp[-1] = ':';
7051590Srgrimes		str = cp;
7061590Srgrimes	} else {
7071590Srgrimes		cp = str;
7081590Srgrimes		cp2 = hfield("subject", mp);
7091590Srgrimes	}
7101590Srgrimes	if (cp2 == NOSTR)
7111590Srgrimes		return(0);
7121590Srgrimes	backup = cp2;
7131590Srgrimes	while (*cp2) {
7141590Srgrimes		if (*cp == 0)
7151590Srgrimes			return(1);
7161590Srgrimes		if (raise(*cp++) != raise(*cp2++)) {
7171590Srgrimes			cp2 = ++backup;
7181590Srgrimes			cp = str;
7191590Srgrimes		}
7201590Srgrimes	}
7211590Srgrimes	return(*cp == 0);
7221590Srgrimes}
7231590Srgrimes
7241590Srgrimes/*
7251590Srgrimes * Mark the named message by setting its mark bit.
7261590Srgrimes */
7271590Srgrimesvoid
7281590Srgrimesmark(mesg)
7291590Srgrimes	int mesg;
7301590Srgrimes{
7311590Srgrimes	register int i;
7321590Srgrimes
7331590Srgrimes	i = mesg;
7341590Srgrimes	if (i < 1 || i > msgCount)
7351590Srgrimes		panic("Bad message number to mark");
7361590Srgrimes	message[i-1].m_flag |= MMARK;
7371590Srgrimes}
7381590Srgrimes
7391590Srgrimes/*
7401590Srgrimes * Unmark the named message.
7411590Srgrimes */
7421590Srgrimesvoid
7431590Srgrimesunmark(mesg)
7441590Srgrimes	int mesg;
7451590Srgrimes{
7461590Srgrimes	register int i;
7471590Srgrimes
7481590Srgrimes	i = mesg;
7491590Srgrimes	if (i < 1 || i > msgCount)
7501590Srgrimes		panic("Bad message number to unmark");
7511590Srgrimes	message[i-1].m_flag &= ~MMARK;
7521590Srgrimes}
7531590Srgrimes
7541590Srgrimes/*
7551590Srgrimes * Return the message number corresponding to the passed meta character.
7561590Srgrimes */
7571590Srgrimesint
7581590Srgrimesmetamess(meta, f)
7591590Srgrimes	int meta, f;
7601590Srgrimes{
7611590Srgrimes	register int c, m;
7621590Srgrimes	register struct message *mp;
7631590Srgrimes
7641590Srgrimes	c = meta;
7651590Srgrimes	switch (c) {
7661590Srgrimes	case '^':
7671590Srgrimes		/*
7681590Srgrimes		 * First 'good' message left.
7691590Srgrimes		 */
7701590Srgrimes		for (mp = &message[0]; mp < &message[msgCount]; mp++)
7711590Srgrimes			if ((mp->m_flag & MDELETED) == f)
7721590Srgrimes				return(mp - &message[0] + 1);
7731590Srgrimes		printf("No applicable messages\n");
7741590Srgrimes		return(-1);
7751590Srgrimes
7761590Srgrimes	case '$':
7771590Srgrimes		/*
7781590Srgrimes		 * Last 'good message left.
7791590Srgrimes		 */
7801590Srgrimes		for (mp = &message[msgCount-1]; mp >= &message[0]; mp--)
7811590Srgrimes			if ((mp->m_flag & MDELETED) == f)
7821590Srgrimes				return(mp - &message[0] + 1);
7831590Srgrimes		printf("No applicable messages\n");
7841590Srgrimes		return(-1);
7851590Srgrimes
7861590Srgrimes	case '.':
7878874Srgrimes		/*
7881590Srgrimes		 * Current message.
7891590Srgrimes		 */
7901590Srgrimes		m = dot - &message[0] + 1;
7911590Srgrimes		if ((dot->m_flag & MDELETED) != f) {
7921590Srgrimes			printf("%d: Inappropriate message\n", m);
7931590Srgrimes			return(-1);
7941590Srgrimes		}
7951590Srgrimes		return(m);
7961590Srgrimes
7971590Srgrimes	default:
7981590Srgrimes		printf("Unknown metachar (%c)\n", c);
7991590Srgrimes		return(-1);
8001590Srgrimes	}
8011590Srgrimes}
802