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