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