Home | History | Annotate | Line # | Download | only in mail
list.c revision 1.18
      1  1.16  christos /*	$NetBSD: list.c,v 1.18 2006/11/28 18:45:32 christos Exp $	*/
      2   1.4  christos 
      3   1.1       cgd /*
      4   1.3   deraadt  * Copyright (c) 1980, 1993
      5   1.3   deraadt  *	The Regents of the University of California.  All rights reserved.
      6   1.1       cgd  *
      7   1.1       cgd  * Redistribution and use in source and binary forms, with or without
      8   1.1       cgd  * modification, are permitted provided that the following conditions
      9   1.1       cgd  * are met:
     10   1.1       cgd  * 1. Redistributions of source code must retain the above copyright
     11   1.1       cgd  *    notice, this list of conditions and the following disclaimer.
     12   1.1       cgd  * 2. Redistributions in binary form must reproduce the above copyright
     13   1.1       cgd  *    notice, this list of conditions and the following disclaimer in the
     14   1.1       cgd  *    documentation and/or other materials provided with the distribution.
     15  1.13       agc  * 3. Neither the name of the University nor the names of its contributors
     16   1.1       cgd  *    may be used to endorse or promote products derived from this software
     17   1.1       cgd  *    without specific prior written permission.
     18   1.1       cgd  *
     19   1.1       cgd  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     20   1.1       cgd  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     21   1.1       cgd  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     22   1.1       cgd  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     23   1.1       cgd  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     24   1.1       cgd  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     25   1.1       cgd  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     26   1.1       cgd  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     27   1.1       cgd  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     28   1.1       cgd  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     29   1.1       cgd  * SUCH DAMAGE.
     30   1.1       cgd  */
     31   1.1       cgd 
     32   1.8     lukem #include <sys/cdefs.h>
     33   1.1       cgd #ifndef lint
     34   1.4  christos #if 0
     35   1.5       tls static char sccsid[] = "@(#)list.c	8.4 (Berkeley) 5/1/95";
     36   1.4  christos #else
     37  1.16  christos __RCSID("$NetBSD: list.c,v 1.18 2006/11/28 18:45:32 christos Exp $");
     38   1.4  christos #endif
     39   1.1       cgd #endif /* not lint */
     40   1.1       cgd 
     41  1.18  christos #include <assert.h>
     42  1.18  christos #include <regex.h>
     43  1.18  christos #include <util.h>
     44  1.18  christos 
     45   1.1       cgd #include "rcv.h"
     46   1.3   deraadt #include "extern.h"
     47  1.17  christos #include "format.h"
     48  1.18  christos #include "thread.h"
     49   1.8     lukem 
     50   1.1       cgd /*
     51   1.1       cgd  * Mail -- a mail program
     52   1.1       cgd  *
     53   1.1       cgd  * Message list handling.
     54   1.1       cgd  */
     55   1.1       cgd 
     56   1.1       cgd /*
     57  1.18  christos  * Token values returned by the scanner used for argument lists.
     58  1.18  christos  * Also, sizes of scanner-related things.
     59   1.1       cgd  */
     60  1.18  christos enum token_e {
     61  1.18  christos 	TEOL,			/* End of the command line */
     62  1.18  christos 	TNUMBER,		/* A message number or range of numbers */
     63  1.18  christos 	TDASH,			/* A simple dash */
     64  1.18  christos 	TSTRING,		/* A string (possibly containing '-') */
     65  1.18  christos 	TDOT,			/* A "." */
     66  1.18  christos 	TUP,			/* An "^" */
     67  1.18  christos 	TDOLLAR,		/* A "$" */
     68  1.18  christos 	TSTAR,			/* A "*" */
     69  1.18  christos 	TOPEN,			/* An '(' */
     70  1.18  christos 	TCLOSE,			/* A ')' */
     71  1.18  christos 	TPLUS,			/* A '+' */
     72  1.18  christos 	TAND,			/* A '&' */
     73  1.18  christos 	TOR,			/* A '|' */
     74  1.18  christos 	TXOR,			/* A logical '^' */
     75  1.18  christos 	TNOT,			/* A '!' */
     76  1.18  christos 	TERROR			/* A lexical error */
     77   1.1       cgd };
     78   1.1       cgd 
     79  1.18  christos #define	REGDEP		2		/* Maximum regret depth. */
     80  1.18  christos #define	STRINGLEN	1024		/* Maximum length of string token */
     81   1.1       cgd 
     82  1.18  christos static int	lexnumber;		/* Number of TNUMBER from scan() */
     83  1.18  christos static char	lexstring[STRINGLEN];	/* String from TSTRING, scan() */
     84  1.18  christos static int	regretp;		/* Pointer to TOS of regret tokens */
     85  1.18  christos static int	regretstack[REGDEP];	/* Stack of regretted tokens */
     86  1.18  christos static char	*string_stack[REGDEP];	/* Stack of regretted strings */
     87  1.18  christos static int	numberstack[REGDEP];	/* Stack of regretted numbers */
     88   1.1       cgd 
     89   1.1       cgd /*
     90   1.1       cgd  * Scan out the list of string arguments, shell style
     91   1.1       cgd  * for a RAWLIST.
     92   1.1       cgd  */
     93  1.18  christos PUBLIC int
     94  1.14  christos getrawlist(const char line[], char **argv, int argc)
     95   1.1       cgd {
     96  1.14  christos 	char c, *cp2, quotec;
     97  1.14  christos 	const char *cp;
     98   1.1       cgd 	int argn;
     99  1.18  christos 	char linebuf[LINESIZE];
    100   1.1       cgd 
    101   1.1       cgd 	argn = 0;
    102   1.1       cgd 	cp = line;
    103   1.1       cgd 	for (;;) {
    104   1.1       cgd 		for (; *cp == ' ' || *cp == '\t'; cp++)
    105  1.18  christos 			continue;
    106   1.1       cgd 		if (*cp == '\0')
    107   1.1       cgd 			break;
    108   1.1       cgd 		if (argn >= argc - 1) {
    109  1.15  christos 			(void)printf(
    110   1.1       cgd 			"Too many elements in the list; excess discarded.\n");
    111   1.1       cgd 			break;
    112   1.1       cgd 		}
    113   1.1       cgd 		cp2 = linebuf;
    114   1.1       cgd 		quotec = '\0';
    115   1.1       cgd 		while ((c = *cp) != '\0') {
    116   1.1       cgd 			cp++;
    117   1.1       cgd 			if (quotec != '\0') {
    118   1.1       cgd 				if (c == quotec)
    119   1.1       cgd 					quotec = '\0';
    120   1.1       cgd 				else if (c == '\\')
    121   1.1       cgd 					switch (c = *cp++) {
    122   1.1       cgd 					case '\0':
    123   1.3   deraadt 						*cp2++ = '\\';
    124   1.3   deraadt 						cp--;
    125   1.1       cgd 						break;
    126   1.1       cgd 					case '0': case '1': case '2': case '3':
    127   1.1       cgd 					case '4': case '5': case '6': case '7':
    128   1.1       cgd 						c -= '0';
    129   1.1       cgd 						if (*cp >= '0' && *cp <= '7')
    130   1.1       cgd 							c = c * 8 + *cp++ - '0';
    131   1.1       cgd 						if (*cp >= '0' && *cp <= '7')
    132   1.1       cgd 							c = c * 8 + *cp++ - '0';
    133   1.1       cgd 						*cp2++ = c;
    134   1.1       cgd 						break;
    135   1.1       cgd 					case 'b':
    136   1.1       cgd 						*cp2++ = '\b';
    137   1.1       cgd 						break;
    138   1.1       cgd 					case 'f':
    139   1.1       cgd 						*cp2++ = '\f';
    140   1.1       cgd 						break;
    141   1.1       cgd 					case 'n':
    142   1.1       cgd 						*cp2++ = '\n';
    143   1.1       cgd 						break;
    144   1.1       cgd 					case 'r':
    145   1.1       cgd 						*cp2++ = '\r';
    146   1.1       cgd 						break;
    147   1.1       cgd 					case 't':
    148   1.1       cgd 						*cp2++ = '\t';
    149   1.1       cgd 						break;
    150   1.1       cgd 					case 'v':
    151   1.1       cgd 						*cp2++ = '\v';
    152   1.1       cgd 						break;
    153   1.3   deraadt 					default:
    154   1.3   deraadt 						*cp2++ = c;
    155   1.1       cgd 					}
    156   1.1       cgd 				else if (c == '^') {
    157   1.1       cgd 					c = *cp++;
    158   1.1       cgd 					if (c == '?')
    159   1.1       cgd 						*cp2++ = '\177';
    160   1.1       cgd 					/* null doesn't show up anyway */
    161   1.4  christos 					else if ((c >= 'A' && c <= '_') ||
    162   1.4  christos 						 (c >= 'a' && c <= 'z'))
    163   1.3   deraadt 						*cp2++ = c & 037;
    164   1.3   deraadt 					else {
    165   1.3   deraadt 						*cp2++ = '^';
    166   1.3   deraadt 						cp--;
    167   1.3   deraadt 					}
    168   1.1       cgd 				} else
    169   1.1       cgd 					*cp2++ = c;
    170   1.1       cgd 			} else if (c == '"' || c == '\'')
    171   1.1       cgd 				quotec = c;
    172   1.1       cgd 			else if (c == ' ' || c == '\t')
    173   1.1       cgd 				break;
    174   1.1       cgd 			else
    175   1.1       cgd 				*cp2++ = c;
    176   1.1       cgd 		}
    177   1.1       cgd 		*cp2 = '\0';
    178   1.1       cgd 		argv[argn++] = savestr(linebuf);
    179   1.1       cgd 	}
    180  1.12       wiz 	argv[argn] = NULL;
    181   1.1       cgd 	return argn;
    182   1.1       cgd }
    183   1.1       cgd 
    184   1.1       cgd /*
    185  1.18  christos  * Mark all messages that the user wanted from the command
    186  1.18  christos  * line in the message structure.  Return 0 on success, -1
    187  1.18  christos  * on error.
    188   1.1       cgd  */
    189   1.1       cgd 
    190  1.18  christos /*
    191  1.18  christos  * Bit values for colon modifiers.
    192  1.18  christos  */
    193  1.18  christos #define	CMBOX		0x001		/* Unread messages */
    194  1.18  christos #define	CMDELETED	0x002		/* Deleted messages */
    195  1.18  christos #define	CMMODIFY	0x004		/* Unread messages */
    196  1.18  christos #define	CMNEW		0x008		/* New messages */
    197  1.18  christos #define	CMOLD		0x010		/* Old messages */
    198  1.18  christos #define	CMPRESERVE	0x020		/* Unread messages */
    199  1.18  christos #define	CMREAD		0x040		/* Read messages */
    200  1.18  christos #define	CMSAVED		0x080		/* Saved messages */
    201  1.18  christos #define	CMTAGGED	0x100		/* Tagged messages */
    202  1.18  christos #define	CMUNREAD	0x200		/* Unread messages */
    203  1.18  christos #define CMNEGATE	0x400		/* Negate the match */
    204  1.18  christos #define CMMASK		0x7ff		/* Mask the valid bits */
    205   1.1       cgd 
    206  1.18  christos /*
    207  1.18  christos  * The following table describes the letters which can follow
    208  1.18  christos  * the colon and gives the corresponding modifier bit.
    209  1.18  christos  */
    210  1.18  christos 
    211  1.18  christos static const struct coltab {
    212  1.18  christos 	char	co_char;		/* What to find past : */
    213  1.18  christos 	int	co_bit;			/* Associated modifier bit */
    214  1.18  christos 	int	co_mask;		/* m_status bits to mask */
    215  1.18  christos 	int	co_equal;		/* ... must equal this */
    216  1.18  christos } coltab[] = {
    217  1.18  christos 	{ '!',		CMNEGATE,	0,		0 },
    218  1.18  christos 	{ 'd',		CMDELETED,	MDELETED,	MDELETED },
    219  1.18  christos 	{ 'e',		CMMODIFY,	MMODIFY,	MMODIFY },
    220  1.18  christos 	{ 'm',		CMBOX,		MBOX,		MBOX },
    221  1.18  christos 	{ 'n',		CMNEW,		MNEW,		MNEW },
    222  1.18  christos 	{ 'o',		CMOLD,		MNEW,		0 },
    223  1.18  christos 	{ 'p',		CMPRESERVE,	MPRESERVE,	MPRESERVE },
    224  1.18  christos 	{ 'r',		CMREAD,		MREAD,		MREAD },
    225  1.18  christos 	{ 's',		CMSAVED,	MSAVED,		MSAVED },
    226  1.18  christos 	{ 't',		CMTAGGED,	MTAGGED,	MTAGGED },
    227  1.18  christos 	{ 'u',		CMUNREAD,	MREAD|MNEW,	0 },
    228  1.18  christos 	{ 0,		0,		0,		0 }
    229  1.18  christos };
    230  1.18  christos 
    231  1.18  christos static	int	lastcolmod;
    232  1.18  christos 
    233  1.18  christos static int
    234  1.18  christos ignore_message(int m_flag, int colmod)
    235  1.18  christos {
    236  1.18  christos 	int ignore_msg;
    237  1.18  christos 	const struct coltab *colp;
    238  1.18  christos 
    239  1.18  christos 	ignore_msg = !(colmod & CMNEGATE);
    240  1.18  christos 	colmod &= (~CMNEGATE & CMMASK);
    241  1.18  christos 
    242  1.18  christos 	for (colp = &coltab[0]; colp->co_char; colp++)
    243  1.18  christos 		if (colp->co_bit & colmod &&
    244  1.18  christos 		    (m_flag & colp->co_mask) == colp->co_equal)
    245  1.18  christos 				return !ignore_msg;
    246  1.18  christos 	return ignore_msg;
    247  1.18  christos }
    248  1.18  christos 
    249  1.18  christos /*
    250  1.18  christos  * Turn the character after a colon modifier into a bit
    251  1.18  christos  * value.
    252  1.18  christos  */
    253  1.18  christos static int
    254  1.18  christos evalcol(int col)
    255  1.18  christos {
    256  1.18  christos 	const struct coltab *colp;
    257  1.18  christos 
    258  1.18  christos 	if (col == 0)
    259  1.18  christos 		return lastcolmod;
    260  1.18  christos 	for (colp = &coltab[0]; colp->co_char; colp++)
    261  1.18  christos 		if (colp->co_char == col)
    262  1.18  christos 			return colp->co_bit;
    263  1.18  christos 	return 0;
    264  1.18  christos }
    265  1.18  christos 
    266  1.18  christos static int
    267  1.18  christos get_colmod(int colmod, char *cp)
    268  1.18  christos {
    269  1.18  christos 	if ((cp[0] == '\0') ||
    270  1.18  christos 	    (cp[0] == '!' && cp[1] == '\0'))
    271  1.18  christos 		colmod |= lastcolmod;
    272  1.18  christos 
    273  1.18  christos 	for (/*EMPTY*/; *cp; cp++) {
    274  1.18  christos 		int colresult;
    275  1.18  christos 		if ((colresult = evalcol(*cp)) == 0) {
    276  1.18  christos 			(void)printf("Unknown colon modifier \"%s\"\n", lexstring);
    277  1.18  christos 			return -1;
    278  1.18  christos 		}
    279  1.18  christos 		if (colresult == CMNEGATE)
    280  1.18  christos 			colmod ^= CMNEGATE;
    281  1.18  christos 		else
    282  1.18  christos 			colmod |= colresult;
    283  1.18  christos 	}
    284  1.18  christos 	return colmod;
    285  1.18  christos }
    286  1.18  christos 
    287  1.18  christos static int
    288  1.18  christos syntax_error(const char *msg)
    289  1.18  christos {
    290  1.18  christos 	(void)printf("Syntax error: %s\n", msg);
    291  1.18  christos 	return -1;
    292  1.18  christos }
    293  1.18  christos 
    294  1.18  christos /*
    295  1.18  christos  * scan out a single lexical item and return its token number,
    296  1.18  christos  * updating the string pointer passed **p.  Also, store the value
    297  1.18  christos  * of the number or string scanned in lexnumber or lexstring as
    298  1.18  christos  * appropriate.  In any event, store the scanned `thing' in lexstring.
    299  1.18  christos  */
    300  1.18  christos static enum token_e
    301  1.18  christos scan(char **sp)
    302  1.18  christos {
    303  1.18  christos 	static const struct lex {
    304  1.18  christos 		char	l_char;
    305  1.18  christos 		enum token_e l_token;
    306  1.18  christos 	} singles[] = {
    307  1.18  christos 		{ '$',	TDOLLAR },
    308  1.18  christos 		{ '.',	TDOT },
    309  1.18  christos 		{ '^',	TUP },
    310  1.18  christos 		{ '*',	TSTAR },
    311  1.18  christos 		{ '-',	TDASH },
    312  1.18  christos 		{ '+',	TPLUS },
    313  1.18  christos 		{ '(',	TOPEN },
    314  1.18  christos 		{ ')',	TCLOSE },
    315  1.18  christos 		{ '&',	TAND },
    316  1.18  christos 		{ '|',	TOR },
    317  1.18  christos 		{ '!',	TNOT },
    318  1.18  christos 		{ 0,	0 }
    319  1.18  christos 	};
    320  1.18  christos 	const struct lex *lp;
    321  1.18  christos 	char *cp, *cp2;
    322  1.18  christos 	int c;
    323  1.18  christos 	int quotec;
    324   1.1       cgd 
    325   1.1       cgd 	if (regretp >= 0) {
    326  1.15  christos 		(void)strcpy(lexstring, string_stack[regretp]);
    327   1.1       cgd 		lexnumber = numberstack[regretp];
    328  1.18  christos 		return regretstack[regretp--];
    329   1.1       cgd 	}
    330   1.1       cgd 	cp = *sp;
    331   1.1       cgd 	cp2 = lexstring;
    332  1.18  christos 	lexstring[0] = '\0';
    333   1.1       cgd 
    334   1.1       cgd 	/*
    335   1.1       cgd 	 * strip away leading white space.
    336   1.1       cgd 	 */
    337  1.18  christos 	cp = skip_blank(cp);
    338  1.18  christos 	c = *cp++;
    339   1.1       cgd 
    340   1.1       cgd 	/*
    341   1.1       cgd 	 * If no characters remain, we are at end of line,
    342   1.1       cgd 	 * so report that.
    343   1.1       cgd 	 */
    344   1.1       cgd 	if (c == '\0') {
    345   1.1       cgd 		*sp = --cp;
    346  1.18  christos 		return TEOL;
    347   1.1       cgd 	}
    348   1.1       cgd 
    349   1.1       cgd 	/*
    350   1.1       cgd 	 * If the leading character is a digit, scan
    351   1.1       cgd 	 * the number and convert it on the fly.
    352   1.1       cgd 	 * Return TNUMBER when done.
    353   1.1       cgd 	 */
    354   1.1       cgd 	if (isdigit(c)) {
    355   1.1       cgd 		lexnumber = 0;
    356   1.1       cgd 		while (isdigit(c)) {
    357  1.18  christos 			lexnumber = lexnumber * 10 + c - '0';
    358   1.1       cgd 			*cp2++ = c;
    359   1.1       cgd 			c = *cp++;
    360   1.1       cgd 		}
    361   1.1       cgd 		*cp2 = '\0';
    362   1.1       cgd 		*sp = --cp;
    363  1.18  christos 		return TNUMBER;
    364   1.1       cgd 	}
    365   1.1       cgd 
    366   1.1       cgd 	/*
    367   1.1       cgd 	 * Check for single character tokens; return such
    368   1.1       cgd 	 * if found.
    369   1.1       cgd 	 */
    370   1.1       cgd 	for (lp = &singles[0]; lp->l_char != 0; lp++)
    371   1.1       cgd 		if (c == lp->l_char) {
    372   1.1       cgd 			lexstring[0] = c;
    373   1.1       cgd 			lexstring[1] = '\0';
    374   1.1       cgd 			*sp = cp;
    375  1.18  christos 			return lp->l_token;
    376   1.1       cgd 		}
    377   1.1       cgd 
    378   1.1       cgd 	/*
    379   1.1       cgd 	 * We've got a string!  Copy all the characters
    380   1.1       cgd 	 * of the string into lexstring, until we see
    381   1.1       cgd 	 * a null, space, or tab.
    382  1.18  christos 	 * Respect quoting and quoted pairs.
    383   1.1       cgd 	 */
    384   1.1       cgd 	quotec = 0;
    385   1.1       cgd 	while (c != '\0') {
    386   1.1       cgd 		if (c == quotec) {
    387  1.18  christos 			quotec = 0;
    388  1.18  christos 			c = *cp++;
    389  1.18  christos 			continue;
    390  1.18  christos 		}
    391  1.18  christos 		if (quotec) {
    392  1.18  christos 			if (c == '\\' && (*cp == quotec || *cp == '\\'))
    393  1.18  christos 				c = *cp++;
    394  1.18  christos 		}
    395  1.18  christos 		else {
    396  1.18  christos 			switch (c) {
    397  1.18  christos 			case '\'':
    398  1.18  christos 			case '"':
    399  1.18  christos 				quotec = c;
    400  1.18  christos 				c = *cp++;
    401  1.18  christos 				continue;
    402  1.18  christos 			case ' ':
    403  1.18  christos 			case '\t':
    404  1.18  christos 				c = '\0';	/* end of token! */
    405  1.18  christos 				continue;
    406  1.18  christos 			default:
    407  1.18  christos 				break;
    408  1.18  christos 			}
    409   1.1       cgd 		}
    410  1.16  christos 		if (cp2 - lexstring < STRINGLEN - 1)
    411   1.1       cgd 			*cp2++ = c;
    412   1.1       cgd 		c = *cp++;
    413   1.1       cgd 	}
    414   1.1       cgd 	if (quotec && c == 0) {
    415  1.15  christos 		(void)fprintf(stderr, "Missing %c\n", quotec);
    416   1.1       cgd 		return TERROR;
    417   1.1       cgd 	}
    418   1.1       cgd 	*sp = --cp;
    419   1.1       cgd 	*cp2 = '\0';
    420  1.18  christos 	return TSTRING;
    421   1.1       cgd }
    422   1.1       cgd 
    423   1.1       cgd /*
    424   1.1       cgd  * Unscan the named token by pushing it onto the regret stack.
    425   1.1       cgd  */
    426  1.18  christos static void
    427  1.11       wiz regret(int token)
    428   1.1       cgd {
    429   1.1       cgd 	if (++regretp >= REGDEP)
    430   1.8     lukem 		errx(1, "Too many regrets");
    431   1.1       cgd 	regretstack[regretp] = token;
    432  1.18  christos 	lexstring[sizeof(lexstring) - 1] = '\0';
    433   1.1       cgd 	string_stack[regretp] = savestr(lexstring);
    434   1.1       cgd 	numberstack[regretp] = lexnumber;
    435   1.1       cgd }
    436   1.1       cgd 
    437   1.1       cgd /*
    438   1.1       cgd  * Reset all the scanner global variables.
    439   1.1       cgd  */
    440  1.18  christos static void
    441  1.11       wiz scaninit(void)
    442   1.1       cgd {
    443   1.1       cgd 	regretp = -1;
    444   1.1       cgd }
    445   1.1       cgd 
    446  1.18  christos #define DELIM " \t,"  /* list of string delimiters */
    447  1.18  christos static int
    448  1.18  christos is_substr(const char *big, const char *little)
    449  1.18  christos {
    450  1.18  christos 	const char *cp;
    451  1.18  christos 	if ((cp = strstr(big, little)) == NULL)
    452  1.18  christos 		return 0;
    453  1.18  christos 
    454  1.18  christos 	return strchr(DELIM, cp[strlen(little)]) != 0 &&
    455  1.18  christos 	    (cp == big || strchr(DELIM, cp[-1]) != 0);
    456  1.18  christos }
    457  1.18  christos #undef DELIM
    458  1.18  christos 
    459   1.1       cgd /*
    460  1.18  christos  * Compile a regular expression, taking into account the value of the
    461  1.18  christos  * "regex-search" variable.
    462   1.1       cgd  */
    463  1.18  christos static int
    464  1.18  christos compile_regex(regex_t *preg, const char *str)
    465   1.1       cgd {
    466  1.18  christos 	int e;
    467  1.18  christos 	char *val;
    468  1.18  christos 	if ((val = value(ENAME_REGEX_SEARCH)) != NULL) {
    469  1.18  christos 		int cflags;
    470  1.18  christos 		cflags = REG_NOSUB;
    471  1.18  christos 		val = skip_blank(val);
    472  1.18  christos 		if (*val) {
    473  1.18  christos 			if (is_substr(val, "icase"))
    474  1.18  christos 				cflags |= REG_ICASE;
    475  1.18  christos 			if (is_substr(val, "extended"))
    476  1.18  christos 				cflags |= REG_EXTENDED;
    477  1.18  christos 		}
    478  1.18  christos 		if ((e = regcomp(preg, str, cflags)) != 0) {
    479  1.18  christos 			char errbuf[LINESIZE];
    480  1.18  christos 			(void)regerror(e, preg, errbuf, sizeof(errbuf));
    481  1.18  christos 			(void)printf("regcomp failed: '%s': %s\n", str, errbuf);
    482  1.18  christos 			return -1;
    483  1.18  christos 		}
    484  1.18  christos 		return 1;
    485  1.18  christos 	}
    486   1.1       cgd 	return 0;
    487   1.1       cgd }
    488   1.1       cgd 
    489   1.1       cgd /*
    490   1.1       cgd  * See if the passed name sent the passed message number.  Return true
    491   1.1       cgd  * if so.
    492   1.1       cgd  */
    493  1.18  christos static int
    494  1.11       wiz matchsender(char *str, int mesg)
    495   1.1       cgd {
    496  1.18  christos 	regex_t preg;
    497  1.18  christos 	int doregex;
    498  1.18  christos 	char *field;
    499   1.1       cgd 
    500  1.18  christos 	if (*str == '\0') /* null string matches nothing instead of everything */
    501   1.1       cgd 		return 0;
    502  1.18  christos 
    503  1.18  christos 	field = nameof(get_message(mesg), 0);
    504  1.18  christos 
    505  1.18  christos 	if ((doregex = compile_regex(&preg, str)) == -1)
    506  1.18  christos 		return -1;
    507  1.18  christos 
    508  1.18  christos 	if (doregex)
    509  1.18  christos 		return regexec(&preg, field, 0, NULL, 0) == 0;
    510  1.18  christos 	else
    511  1.18  christos 		return strcasestr(field, str) != NULL;
    512   1.1       cgd }
    513   1.1       cgd 
    514   1.1       cgd /*
    515   1.5       tls  * See if the passed name received the passed message number.  Return true
    516   1.5       tls  * if so.
    517   1.5       tls  */
    518  1.18  christos static int
    519  1.11       wiz matchto(char *str, int mesg)
    520   1.5       tls {
    521  1.18  christos 	static const char *to_fields[] = { "to", "cc", "bcc", 0 };
    522  1.18  christos 	const char **to;
    523  1.18  christos 	regex_t preg;
    524  1.18  christos 	int doregex;
    525   1.8     lukem 	struct message *mp;
    526   1.5       tls 
    527  1.18  christos 	if (*str == 0)	/* null string matches nothing instead of everything */
    528  1.18  christos 		return 0;
    529   1.5       tls 
    530  1.18  christos 	if ((doregex = compile_regex(&preg, str)) == -1)
    531  1.18  christos 		return -1;
    532   1.5       tls 
    533  1.18  christos 	mp = get_message(mesg);
    534   1.5       tls 
    535   1.5       tls 	for (to = to_fields; *to; to++) {
    536  1.18  christos 		char *field;
    537  1.18  christos 		field = hfield(*to, mp);
    538  1.18  christos 		if (field != NULL) {
    539  1.18  christos 			if (doregex) {
    540  1.18  christos 				if (regexec(&preg, field, 0, NULL, 0) == 0)
    541  1.18  christos 					return 1;
    542  1.18  christos 			}
    543  1.18  christos 			else {
    544  1.18  christos 				if (strcasestr(field, str) != NULL)
    545  1.18  christos 					return 1;
    546   1.5       tls 			}
    547   1.5       tls 		}
    548   1.5       tls 	}
    549  1.18  christos 	return 0;
    550   1.5       tls }
    551   1.5       tls 
    552   1.5       tls /*
    553   1.1       cgd  * See if the given string matches inside the subject field of the
    554   1.1       cgd  * given message.  For the purpose of the scan, we ignore case differences.
    555   1.1       cgd  * If it does, return true.  The string search argument is assumed to
    556  1.18  christos  * have the form "/search-string."  If it is of the form "/", we use the
    557   1.1       cgd  * previous search string.
    558   1.1       cgd  */
    559  1.18  christos static int
    560  1.11       wiz matchsubj(char *str, int mesg)
    561   1.1       cgd {
    562  1.18  christos 	regex_t preg;
    563  1.18  christos 	int doregex;
    564  1.18  christos 	char *field;
    565  1.18  christos 	char *cp;
    566  1.18  christos 
    567  1.18  christos 	static char lastscan[STRINGLEN];
    568   1.8     lukem 	struct message *mp;
    569   1.1       cgd 
    570   1.6     mikel 	if (*str == '\0')
    571   1.1       cgd 		str = lastscan;
    572  1.18  christos 	else
    573  1.18  christos 		(void)strlcpy(lastscan, str, sizeof(lastscan));
    574  1.18  christos 
    575  1.18  christos 	mp = get_message(mesg);
    576  1.18  christos 
    577   1.1       cgd 	/*
    578   1.1       cgd 	 * Now look, ignoring case, for the word in the string.
    579   1.1       cgd 	 */
    580   1.1       cgd 
    581  1.18  christos 	if (value(ENAME_SEARCHHEADERS) && (cp = strchr(str, ':')) != NULL) {
    582  1.18  christos 		/*
    583  1.18  christos 		 * Check for special cases!
    584  1.18  christos 		 * These checks are case sensitive so the true fields
    585  1.18  christos 		 * can be grabbed as mentioned in the manpage.
    586  1.18  christos 		 */
    587  1.18  christos 		if (strncmp(str, "to:", 3) == 0)
    588  1.18  christos 			return matchto(cp + 1, mesg);
    589  1.18  christos 
    590  1.18  christos 		if (strncmp(str, "from:", 5) == 0) {
    591  1.18  christos 			char headline[LINESIZE];
    592  1.18  christos 			(void)mail_readline(setinput(mp), headline, sizeof(headline));
    593  1.18  christos 			field = savestr(headline);
    594  1.18  christos 		}
    595  1.18  christos 		else {
    596  1.18  christos 			*cp = '\0';
    597  1.18  christos 			field = hfield(*str ? str : "subject", mp);
    598  1.18  christos 			*cp = ':';
    599  1.18  christos 		}
    600  1.18  christos 		str = cp + 1;
    601   1.3   deraadt 	} else {
    602  1.18  christos 		field = hfield("subject", mp);
    603   1.3   deraadt 	}
    604  1.18  christos 	if (field == NULL)
    605  1.18  christos 		return 0;
    606   1.1       cgd 
    607  1.18  christos 	if ((doregex = compile_regex(&preg, str)) == -1)
    608  1.18  christos 		return -1;
    609   1.1       cgd 
    610  1.18  christos 	if (doregex)
    611  1.18  christos 		return regexec(&preg, field, 0, NULL, 0) == 0;
    612  1.18  christos 	else
    613  1.18  christos 		return strcasestr(field, str) != NULL;
    614   1.1       cgd }
    615   1.1       cgd 
    616   1.1       cgd /*
    617   1.1       cgd  * Return the message number corresponding to the passed meta character.
    618   1.1       cgd  */
    619  1.18  christos static int
    620  1.11       wiz metamess(int meta, int f)
    621   1.1       cgd {
    622   1.8     lukem 	int c, m;
    623   1.8     lukem 	struct message *mp;
    624   1.1       cgd 
    625   1.1       cgd 	c = meta;
    626   1.1       cgd 	switch (c) {
    627   1.1       cgd 	case '^':
    628   1.1       cgd 		/*
    629   1.1       cgd 		 * First 'good' message left.
    630   1.1       cgd 		 */
    631  1.18  christos 		for (mp = get_message(1); mp; mp = next_message(mp))
    632   1.1       cgd 			if ((mp->m_flag & MDELETED) == f)
    633  1.18  christos 				return get_msgnum(mp);
    634  1.15  christos 		(void)printf("No applicable messages\n");
    635  1.18  christos 		return -1;
    636   1.1       cgd 
    637   1.1       cgd 	case '$':
    638   1.1       cgd 		/*
    639   1.1       cgd 		 * Last 'good message left.
    640   1.1       cgd 		 */
    641  1.18  christos 		for (mp = get_message(get_msgCount()); mp; mp = prev_message(mp))
    642   1.1       cgd 			if ((mp->m_flag & MDELETED) == f)
    643  1.18  christos 				return get_msgnum(mp);
    644  1.15  christos 		(void)printf("No applicable messages\n");
    645  1.18  christos 		return -1;
    646   1.1       cgd 
    647   1.1       cgd 	case '.':
    648   1.1       cgd 		/*
    649   1.1       cgd 		 * Current message.
    650   1.1       cgd 		 */
    651  1.18  christos 		if (dot == NULL) {
    652  1.18  christos 			(void)printf("No applicable messages\n");
    653  1.18  christos 			return -1;
    654  1.18  christos 		}
    655  1.18  christos 		m = get_msgnum(dot);
    656   1.1       cgd 		if ((dot->m_flag & MDELETED) != f) {
    657  1.15  christos 			(void)printf("%d: Inappropriate message\n", m);
    658  1.18  christos 			return -1;
    659   1.1       cgd 		}
    660  1.18  christos 		return m;
    661   1.1       cgd 
    662   1.1       cgd 	default:
    663  1.15  christos 		(void)printf("Unknown metachar (%c)\n", c);
    664  1.18  christos 		return -1;
    665  1.18  christos 	}
    666  1.18  christos }
    667  1.18  christos 
    668  1.18  christos /*
    669  1.18  christos  * Check the passed message number for legality and proper flags.
    670  1.18  christos  * If f is MDELETED, then either kind will do.  Otherwise, the message
    671  1.18  christos  * has to be undeleted.
    672  1.18  christos  */
    673  1.18  christos static int
    674  1.18  christos check(int mesg, int f)
    675  1.18  christos {
    676  1.18  christos 	struct message *mp;
    677  1.18  christos 
    678  1.18  christos 	if ((mp = get_message(mesg)) == NULL) {
    679  1.18  christos 		(void)printf("%d: Invalid message number\n", mesg);
    680  1.18  christos 		return -1;
    681  1.18  christos 	}
    682  1.18  christos 	if (f != MDELETED && (mp->m_flag & MDELETED) != 0) {
    683  1.18  christos 		(void)printf("%d: Inappropriate message\n", mesg);
    684  1.18  christos 		return -1;
    685  1.18  christos 	}
    686  1.18  christos 	return 0;
    687  1.18  christos }
    688  1.18  christos 
    689  1.18  christos static int
    690  1.18  christos match_string(int *markarray, char *str, int msgCount)
    691  1.18  christos {
    692  1.18  christos 	int i;
    693  1.18  christos 	int rval;
    694  1.18  christos 	int (*matchfn)(char *, int);
    695  1.18  christos 
    696  1.18  christos 	matchfn = *str == '/' ? matchsubj : matchsender;
    697  1.18  christos 	if (*str == '/')
    698  1.18  christos 		str++;
    699  1.18  christos 
    700  1.18  christos 	for (i = 1; i <= msgCount; i++) {
    701  1.18  christos 		rval = matchfn(str, i);
    702  1.18  christos 		if (rval == -1)
    703  1.18  christos 			return -1;
    704  1.18  christos 		if (rval)
    705  1.18  christos 			markarray[i - 1] = 1;
    706  1.18  christos 	}
    707  1.18  christos 	return 0;
    708  1.18  christos }
    709  1.18  christos 
    710  1.18  christos 
    711  1.18  christos static int
    712  1.18  christos markall_core(int *markarray, char **bufp, int f, int level)
    713  1.18  christos {
    714  1.18  christos 	enum token_e tok;
    715  1.18  christos 	enum logic_op_e {
    716  1.18  christos 		LOP_AND,
    717  1.18  christos 		LOP_OR,
    718  1.18  christos 		LOP_XOR
    719  1.18  christos 	} logic_op;		/* binary logic operation */
    720  1.18  christos 	int logic_invert;	/* invert the result */
    721  1.18  christos 	int *tmparray;	/* temporarly array with result */
    722  1.18  christos 	int msgCount;	/* tmparray length and message count */
    723  1.18  christos 	int beg;	/* first value of a range */
    724  1.18  christos 	int colmod;	/* the colon-modifier for this group */
    725  1.18  christos 	int got_not;	/* for syntax checking of '!' */
    726  1.18  christos 	int got_one;	/* we have a message spec, valid or not */
    727  1.18  christos 	int got_bin;	/* we have a pending binary operation */
    728  1.18  christos 	int i;
    729  1.18  christos 
    730  1.18  christos 	logic_op = LOP_OR;
    731  1.18  christos 	logic_invert = 0;
    732  1.18  christos 	colmod = 0;
    733  1.18  christos 
    734  1.18  christos 	msgCount = get_msgCount();
    735  1.18  christos 	tmparray = csalloc((size_t)msgCount, sizeof(*tmparray));
    736  1.18  christos 
    737  1.18  christos 	beg = 0;
    738  1.18  christos 	got_one = 0;
    739  1.18  christos 	got_not = 0;
    740  1.18  christos 	got_bin = 0;
    741  1.18  christos 
    742  1.18  christos 	while ((tok = scan(bufp)) != TEOL) {
    743  1.18  christos 		if (tok == TERROR)
    744  1.18  christos 			return -1;
    745  1.18  christos 
    746  1.18  christos 		/*
    747  1.18  christos 		 * Do some syntax checking.
    748  1.18  christos 		 */
    749  1.18  christos 		switch (tok) {
    750  1.18  christos 		case TDASH:
    751  1.18  christos 		case TPLUS:
    752  1.18  christos 		case TDOLLAR:
    753  1.18  christos 		case TUP:
    754  1.18  christos 		case TDOT:
    755  1.18  christos 		case TNUMBER:
    756  1.18  christos 			break;
    757  1.18  christos 
    758  1.18  christos 		case TAND:
    759  1.18  christos 		case TOR:
    760  1.18  christos 		case TXOR:
    761  1.18  christos 			if (!got_one)
    762  1.18  christos 				return syntax_error("missing left operand");
    763  1.18  christos 			/*FALLTHROUGH*/
    764  1.18  christos 		default:
    765  1.18  christos 			if (beg)
    766  1.18  christos 				return syntax_error("end of range missing");
    767  1.18  christos 			break;
    768  1.18  christos 		}
    769  1.18  christos 
    770  1.18  christos 		/*
    771  1.18  christos 		 * The main tok switch.
    772  1.18  christos 		 */
    773  1.18  christos 		switch (tok) {
    774  1.18  christos 			struct message *mp;
    775  1.18  christos 
    776  1.18  christos 		case TERROR:	/* trapped above */
    777  1.18  christos 		case TEOL:
    778  1.18  christos 			assert(/*CONSTCOND*/0);
    779  1.18  christos 			break;
    780  1.18  christos 
    781  1.18  christos 		case TUP:
    782  1.18  christos 			if (got_one) {	/* a possible logical xor */
    783  1.18  christos 				enum token_e t;
    784  1.18  christos 				t = scan(bufp); /* peek ahead */
    785  1.18  christos 				regret(t);
    786  1.18  christos 				lexstring[0] = '^';  /* restore lexstring */
    787  1.18  christos 				lexstring[1] = '\0';
    788  1.18  christos 				if (t != TDASH && t != TEOL && t != TCLOSE) {
    789  1.18  christos 					/* convert tok to TXOR and put
    790  1.18  christos 					 * it back on the stack so we
    791  1.18  christos 					 * can handle it consistently */
    792  1.18  christos 					tok = TXOR;
    793  1.18  christos 					regret(tok);
    794  1.18  christos 					continue;
    795  1.18  christos 				}
    796  1.18  christos 			}
    797  1.18  christos 			/* FALLTHROUGH */
    798  1.18  christos 		case TDOLLAR:
    799  1.18  christos 		case TDOT:
    800  1.18  christos 			lexnumber = metamess(lexstring[0], f);
    801  1.18  christos 			if (lexnumber == -1)
    802  1.18  christos 				return -1;
    803  1.18  christos 			/* FALLTHROUGH */
    804  1.18  christos 		case TNUMBER:
    805  1.18  christos 			if (check(lexnumber, f))
    806  1.18  christos 				return -1;
    807  1.18  christos 	number:
    808  1.18  christos 			got_one = 1;
    809  1.18  christos 			if (beg != 0) {
    810  1.18  christos 				if (lexnumber < beg) {
    811  1.18  christos 					(void)printf("invalid range: %d-%d\n", beg, lexnumber);
    812  1.18  christos 					return -1;
    813  1.18  christos 				}
    814  1.18  christos 				for (i = beg; i <= lexnumber; i++)
    815  1.18  christos 					tmparray[i - 1] = 1;
    816  1.18  christos 
    817  1.18  christos 				beg = 0;
    818  1.18  christos 				break;
    819  1.18  christos 			}
    820  1.18  christos 			beg = lexnumber;	/* start of a range */
    821  1.18  christos 			tok = scan(bufp);
    822  1.18  christos 			if (tok == TDASH) {
    823  1.18  christos 				continue;
    824  1.18  christos 			}
    825  1.18  christos 			else {
    826  1.18  christos 				regret(tok);
    827  1.18  christos 				tmparray[beg - 1] = 1;
    828  1.18  christos 				beg = 0;
    829  1.18  christos 			}
    830  1.18  christos 			break;
    831  1.18  christos 
    832  1.18  christos 		case TDASH:
    833  1.18  christos 			for (mp = prev_message(dot); mp; mp = prev_message(mp)) {
    834  1.18  christos 				if ((mp->m_flag & MDELETED) == 0)
    835  1.18  christos 					break;
    836  1.18  christos 			}
    837  1.18  christos 			if (mp == NULL) {
    838  1.18  christos 				(void)printf("Referencing before 1\n");
    839  1.18  christos 				return -1;
    840  1.18  christos 			}
    841  1.18  christos 			lexnumber = get_msgnum(mp);
    842  1.18  christos 			goto number;
    843  1.18  christos 
    844  1.18  christos 		case TPLUS:
    845  1.18  christos 			for (mp = next_message(dot); mp; mp = next_message(mp)) {
    846  1.18  christos 				if ((mp->m_flag & MDELETED) == 0)
    847  1.18  christos 					break;
    848  1.18  christos 			}
    849  1.18  christos 			if (mp == NULL) {
    850  1.18  christos 				(void)printf("Referencing beyond EOF\n");
    851  1.18  christos 				return -1;
    852  1.18  christos 			}
    853  1.18  christos 			lexnumber = get_msgnum(mp);
    854  1.18  christos 			goto number;
    855  1.18  christos 
    856  1.18  christos 		case TSTRING:
    857  1.18  christos 			if (lexstring[0] == ':') { /* colon modifier! */
    858  1.18  christos 				colmod = get_colmod(colmod, lexstring + 1);
    859  1.18  christos 				if (colmod == -1)
    860  1.18  christos 					return -1;
    861  1.18  christos 				continue;
    862  1.18  christos 			}
    863  1.18  christos 			got_one = 1;
    864  1.18  christos 			if (match_string(tmparray, lexstring, msgCount) == -1)
    865  1.18  christos 				return -1;
    866  1.18  christos 			break;
    867  1.18  christos 
    868  1.18  christos 		case TSTAR:
    869  1.18  christos 			got_one = 1;
    870  1.18  christos 			for (i = 1; i <= msgCount; i++)
    871  1.18  christos 				tmparray[i - 1] = 1;
    872  1.18  christos 			break;
    873  1.18  christos 
    874  1.18  christos 
    875  1.18  christos 			/**************
    876  1.18  christos 			 * Parentheses.
    877  1.18  christos 			 */
    878  1.18  christos 		case TOPEN:
    879  1.18  christos 			if (markall_core(tmparray, bufp, f, level + 1) == -1)
    880  1.18  christos 				return -1;
    881  1.18  christos 			break;
    882  1.18  christos 
    883  1.18  christos 		case TCLOSE:
    884  1.18  christos 			if (level == 0)
    885  1.18  christos 				return syntax_error("extra ')'");
    886  1.18  christos 			goto done;
    887  1.18  christos 
    888  1.18  christos 
    889  1.18  christos 			/*********************
    890  1.18  christos 			 * Logical operations.
    891  1.18  christos 			 */
    892  1.18  christos 		case TNOT:
    893  1.18  christos 			got_not = 1;
    894  1.18  christos 			logic_invert = ! logic_invert;
    895  1.18  christos 			continue;
    896  1.18  christos 
    897  1.18  christos 			/*
    898  1.18  christos 			 * Binary operations.
    899  1.18  christos 			 */
    900  1.18  christos 		case TAND:
    901  1.18  christos 			if (got_not)
    902  1.18  christos 				return syntax_error("'!' precedes '&'");
    903  1.18  christos 			got_bin = 1;
    904  1.18  christos 			logic_op = LOP_AND;
    905  1.18  christos 			continue;
    906  1.18  christos 
    907  1.18  christos 		case TOR:
    908  1.18  christos 			if (got_not)
    909  1.18  christos 				return syntax_error("'!' precedes '|'");
    910  1.18  christos 			got_bin = 1;
    911  1.18  christos 			logic_op = LOP_OR;
    912  1.18  christos 			continue;
    913  1.18  christos 
    914  1.18  christos 		case TXOR:
    915  1.18  christos 			if (got_not)
    916  1.18  christos 				return syntax_error("'!' precedes logical '^'");
    917  1.18  christos 			got_bin = 1;
    918  1.18  christos 			logic_op = LOP_XOR;
    919  1.18  christos 			continue;
    920  1.18  christos 		}
    921  1.18  christos 
    922  1.18  christos 		/*
    923  1.18  christos 		 * Do the logic operations.
    924  1.18  christos 		 */
    925  1.18  christos 		if (logic_invert)
    926  1.18  christos 			for (i = 0; i < msgCount; i++)
    927  1.18  christos 				tmparray[i] = ! tmparray[i];
    928  1.18  christos 
    929  1.18  christos 		switch (logic_op) {
    930  1.18  christos 		case LOP_AND:
    931  1.18  christos 			for (i = 0; i < msgCount; i++)
    932  1.18  christos 				markarray[i] &= tmparray[i];
    933  1.18  christos 			break;
    934  1.18  christos 
    935  1.18  christos 		case LOP_OR:
    936  1.18  christos 			for (i = 0; i < msgCount; i++)
    937  1.18  christos 				markarray[i] |= tmparray[i];
    938  1.18  christos 			break;
    939  1.18  christos 
    940  1.18  christos 		case LOP_XOR:
    941  1.18  christos 			for (i = 0; i < msgCount; i++)
    942  1.18  christos 				markarray[i] ^= tmparray[i];
    943  1.18  christos 			break;
    944  1.18  christos 		}
    945  1.18  christos 
    946  1.18  christos 		/*
    947  1.18  christos 		 * Clear the temporary array and reset the logic
    948  1.18  christos 		 * operations.
    949  1.18  christos 		 */
    950  1.18  christos 		for (i = 0; i < msgCount; i++)
    951  1.18  christos 			tmparray[i] = 0;
    952  1.18  christos 
    953  1.18  christos 		logic_op = LOP_OR;
    954  1.18  christos 		logic_invert = 0;
    955  1.18  christos 		got_not = 0;
    956  1.18  christos 		got_bin = 0;
    957  1.18  christos 	}
    958  1.18  christos 
    959  1.18  christos 	if (beg)
    960  1.18  christos 		return syntax_error("end of range missing");
    961  1.18  christos 
    962  1.18  christos 	if (level)
    963  1.18  christos 		return syntax_error("missing ')'");
    964  1.18  christos 
    965  1.18  christos  done:
    966  1.18  christos 	if (got_not)
    967  1.18  christos 		return syntax_error("trailing '!'");
    968  1.18  christos 
    969  1.18  christos 	if (got_bin)
    970  1.18  christos 		return syntax_error("missing right operand");
    971  1.18  christos 
    972  1.18  christos 	if (colmod != 0) {
    973  1.18  christos 		/*
    974  1.18  christos 		 * If we have colon-modifiers but no messages
    975  1.18  christos 		 * specifiec, then assume '*' was given.
    976  1.18  christos 		 */
    977  1.18  christos 		if (got_one == 0)
    978  1.18  christos 			for (i = 1; i <= msgCount; i++)
    979  1.18  christos 				markarray[i - 1] = 1;
    980  1.18  christos 
    981  1.18  christos 		for (i = 1; i <= msgCount; i++) {
    982  1.18  christos 			struct message *mp;
    983  1.18  christos 			if ((mp = get_message(i)) != NULL &&
    984  1.18  christos 			    ignore_message(mp->m_flag, colmod))
    985  1.18  christos 				markarray[i - 1] = 0;
    986  1.18  christos 		}
    987  1.18  christos 	}
    988  1.18  christos 	return 0;
    989  1.18  christos }
    990  1.18  christos 
    991  1.18  christos static int
    992  1.18  christos markall(char buf[], int f)
    993  1.18  christos {
    994  1.18  christos 	int i;
    995  1.18  christos 	int mc;
    996  1.18  christos 	int *markarray;
    997  1.18  christos 	int msgCount;
    998  1.18  christos 	struct message *mp;
    999  1.18  christos 
   1000  1.18  christos 	msgCount = get_msgCount();
   1001  1.18  christos 
   1002  1.18  christos 	/*
   1003  1.18  christos 	 * Clear all the previous message marks.
   1004  1.18  christos 	 */
   1005  1.18  christos 	for (i = 1; i <= msgCount; i++)
   1006  1.18  christos 		if ((mp = get_message(i)) != NULL)
   1007  1.18  christos 			mp->m_flag &= ~MMARK;
   1008  1.18  christos 
   1009  1.18  christos 	buf = skip_blank(buf);
   1010  1.18  christos 	if (*buf == '\0')
   1011  1.18  christos 		return 0;
   1012  1.18  christos 
   1013  1.18  christos 	scaninit();
   1014  1.18  christos 	markarray = csalloc((size_t)msgCount, sizeof(*markarray));
   1015  1.18  christos 	if (markall_core(markarray, &buf, f, 0) == -1)
   1016  1.18  christos 		return -1;
   1017  1.18  christos 
   1018  1.18  christos 	/*
   1019  1.18  christos 	 * Transfer the markarray values to the messages.
   1020  1.18  christos 	 */
   1021  1.18  christos 	mc = 0;
   1022  1.18  christos 	for (i = 1; i <= msgCount; i++) {
   1023  1.18  christos 		if (markarray[i - 1] &&
   1024  1.18  christos 		    (mp = get_message(i)) != NULL &&
   1025  1.18  christos 		    (f == MDELETED || (mp->m_flag & MDELETED) == 0)) {
   1026  1.18  christos 			mp->m_flag |= MMARK;
   1027  1.18  christos 			mc++;
   1028  1.18  christos 		}
   1029  1.18  christos 	}
   1030  1.18  christos 
   1031  1.18  christos 	if (mc == 0) {
   1032  1.18  christos 		(void)printf("No applicable messages.\n");
   1033  1.18  christos 		return -1;
   1034  1.18  christos 	}
   1035  1.18  christos 	return 0;
   1036  1.18  christos }
   1037  1.18  christos 
   1038  1.18  christos /*
   1039  1.18  christos  * Convert the user string of message numbers and
   1040  1.18  christos  * store the numbers into vector.
   1041  1.18  christos  *
   1042  1.18  christos  * Returns the count of messages picked up or -1 on error.
   1043  1.18  christos  */
   1044  1.18  christos PUBLIC int
   1045  1.18  christos getmsglist(char *buf, int *vector, int flags)
   1046  1.18  christos {
   1047  1.18  christos 	int *ip;
   1048  1.18  christos 	struct message *mp;
   1049  1.18  christos 
   1050  1.18  christos 	if (get_msgCount() == 0) {
   1051  1.18  christos 		*vector = 0;
   1052  1.18  christos 		return 0;
   1053   1.1       cgd 	}
   1054  1.18  christos 	if (markall(buf, flags) < 0)
   1055  1.18  christos 		return -1;
   1056  1.18  christos 	ip = vector;
   1057  1.18  christos 	for (mp = get_message(1); mp; mp = next_message(mp))
   1058  1.18  christos 		if (mp->m_flag & MMARK)
   1059  1.18  christos 			*ip++ = get_msgnum(mp);
   1060  1.18  christos 	*ip = 0;
   1061  1.18  christos 	return ip - vector;
   1062  1.18  christos }
   1063  1.18  christos 
   1064  1.18  christos /*
   1065  1.18  christos  * Find the first message whose flags & m == f  and return
   1066  1.18  christos  * its message number.
   1067  1.18  christos  */
   1068  1.18  christos PUBLIC int
   1069  1.18  christos first(int f, int m)
   1070  1.18  christos {
   1071  1.18  christos 	struct message *mp;
   1072  1.18  christos 
   1073  1.18  christos 	if (get_msgCount() == 0)
   1074  1.18  christos 		return 0;
   1075  1.18  christos 	f &= MDELETED;
   1076  1.18  christos 	m &= MDELETED;
   1077  1.18  christos 	for (mp = dot; mp; mp = next_message(mp))
   1078  1.18  christos 		if ((mp->m_flag & m) == f)
   1079  1.18  christos 			return get_msgnum(mp);
   1080  1.18  christos 	for (mp = prev_message(dot); mp; mp = prev_message(mp))
   1081  1.18  christos 		if ((mp->m_flag & m) == f)
   1082  1.18  christos 			return get_msgnum(mp);
   1083  1.18  christos 	return 0;
   1084  1.18  christos }
   1085  1.18  christos 
   1086  1.18  christos /*
   1087  1.18  christos  * Show all headers without paging.  (-H flag)
   1088  1.18  christos  */
   1089  1.18  christos __attribute__((__noreturn__))
   1090  1.18  christos PUBLIC int
   1091  1.18  christos show_headers_and_exit(int flags)
   1092  1.18  christos {
   1093  1.18  christos 	struct message *mp;
   1094  1.18  christos 
   1095  1.18  christos 	flags &= CMMASK;
   1096  1.18  christos 	for (mp = get_message(1); mp; mp = next_message(mp))
   1097  1.18  christos 		if (flags == 0 || !ignore_message(mp->m_flag, flags))
   1098  1.18  christos 			printhead(get_msgnum(mp));
   1099  1.18  christos 
   1100  1.18  christos 	exit(0);
   1101  1.18  christos 	/* NOTREACHED */
   1102  1.18  christos }
   1103  1.18  christos 
   1104  1.18  christos /*
   1105  1.18  christos  * A hack so -H can have an optional modifier as -H[:flags].
   1106  1.18  christos  *
   1107  1.18  christos  * This depends a bit on the internals of getopt().  In particular,
   1108  1.18  christos  * for flags expecting an argument, argv[optind-1] must contain the
   1109  1.18  christos  * optarg and optarg must point to a substring of argv[optind-1] not a
   1110  1.18  christos  * copy of it.
   1111  1.18  christos  */
   1112  1.18  christos PUBLIC int
   1113  1.18  christos get_Hflag(char **argv)
   1114  1.18  christos {
   1115  1.18  christos 	int flags;
   1116  1.18  christos 
   1117  1.18  christos 	flags = ~CMMASK;
   1118  1.18  christos 
   1119  1.18  christos 	if (optarg == NULL)  /* We had an error, just get the flags. */
   1120  1.18  christos 		return flags;
   1121  1.18  christos 
   1122  1.18  christos 	if (*optarg != ':' || optarg == argv[optind - 1]) {
   1123  1.18  christos 		optind--;
   1124  1.18  christos 		optreset = 1;
   1125  1.18  christos 		if (optarg != argv[optind]) {
   1126  1.18  christos 			static char temparg[LINESIZE];
   1127  1.18  christos 			size_t optlen;
   1128  1.18  christos 			size_t arglen;
   1129  1.18  christos 			char *p;
   1130  1.18  christos 
   1131  1.18  christos 			optlen = strlen(optarg);
   1132  1.18  christos 			arglen = strlen(argv[optind]);
   1133  1.18  christos 			p = argv[optind] + arglen - optlen;
   1134  1.18  christos 			optlen = MIN(optlen, sizeof(temparg) - 1);
   1135  1.18  christos 			temparg[0] = '-';
   1136  1.18  christos 			(void)memmove(temparg + 1, p, optlen + 1);
   1137  1.18  christos 			argv[optind] = temparg;
   1138  1.18  christos 		}
   1139  1.18  christos 	}
   1140  1.18  christos 	else {
   1141  1.18  christos 		flags = get_colmod(flags, optarg + 1);
   1142  1.18  christos 	}
   1143  1.18  christos 	return flags;
   1144   1.1       cgd }
   1145