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