+#include <lib-lib/mem.h>
+#include <lib-lib/ascii.h>
+#include <lib-lib/str.h>
+#include <lib-lib/macros.h>
+#include <lib-lib/file.h>
+#include <lib-lib/buffer.h>
+#include <lib-lib/mapping.h>
+
+#include <lib-mime/mime.h>
+
static int eat_regexp (pattern_t * pat, BUFFER *, BUFFER *);
static int eat_date (pattern_t * pat, BUFFER *, BUFFER *);
static int eat_range (pattern_t * pat, BUFFER *, BUFFER *);
static int eat_regexp (pattern_t * pat, BUFFER *, BUFFER *);
static int eat_date (pattern_t * pat, BUFFER *, BUFFER *);
static int eat_range (pattern_t * pat, BUFFER *, BUFFER *);
'L', M_ADDRESS, 0, eat_regexp}, {
'l', M_LIST, 0, NULL}, {
'm', M_MESSAGE, 0, eat_range}, {
'L', M_ADDRESS, 0, eat_regexp}, {
'l', M_LIST, 0, NULL}, {
'm', M_MESSAGE, 0, eat_range}, {
'n', M_SCORE, 0, eat_range}, {
'N', M_NEW, 0, NULL}, {
'O', M_OLD, 0, NULL}, {
'n', M_SCORE, 0, eat_range}, {
'N', M_NEW, 0, NULL}, {
'O', M_OLD, 0, NULL}, {
'T', M_TAG, 0, NULL}, {
't', M_TO, 0, eat_regexp}, {
'U', M_UNREAD, 0, NULL}, {
'T', M_TAG, 0, NULL}, {
't', M_TO, 0, eat_regexp}, {
'U', M_UNREAD, 0, NULL}, {
'v', M_COLLAPSED, 0, NULL}, {
'V', M_CRYPT_VERIFIED, 0, NULL},
#ifdef USE_NNTP
'v', M_COLLAPSED, 0, NULL}, {
'V', M_CRYPT_VERIFIED, 0, NULL},
#ifdef USE_NNTP
#endif
{
'x', M_REFERENCE, 0, eat_regexp}, {
#endif
{
'x', M_REFERENCE, 0, eat_regexp}, {
'y', M_XLABEL, 0, eat_regexp}, {
'z', M_SIZE, 0, eat_range}, {
'=', M_DUPLICATED, 0, NULL}, {
'$', M_UNREFERENCED, 0, NULL}, {
'*', M_REALNAME, 0, NULL}, {
'y', M_XLABEL, 0, eat_regexp}, {
'z', M_SIZE, 0, eat_range}, {
'=', M_DUPLICATED, 0, NULL}, {
'$', M_UNREFERENCED, 0, NULL}, {
'*', M_REALNAME, 0, NULL}, {
-msg_search (CONTEXT * ctx, regex_t * rx, char *buf, size_t blen, int op,
- int msgno)
+msg_search (CONTEXT *ctx, pattern_t* pat, int msgno)
if ((msg = mx_open_message (ctx, msgno)) != NULL) {
if (option (OPTTHOROUGHSRC)) {
/* decode the header / body */
if ((msg = mx_open_message (ctx, msgno)) != NULL) {
if (option (OPTTHOROUGHSRC)) {
/* decode the header / body */
mutt_copy_header (msg->fp, h, s.fpout, CH_FROM | CH_DECODE, NULL);
mutt_copy_header (msg->fp, h, s.fpout, CH_FROM | CH_DECODE, NULL);
mutt_parse_mime_message (ctx, h);
if (WithCrypto && (h->security & ENCRYPT)
mutt_parse_mime_message (ctx, h);
if (WithCrypto && (h->security & ENCRYPT)
- if (op != M_BODY) {
- fseek (fp, h->offset, 0);
+ if (pat->op != M_BODY) {
+ fseeko (fp, h->offset, 0);
- if (op != M_HEADER) {
- if (op == M_BODY)
- fseek (fp, h->content->offset, 0);
+ if (pat->op != M_HEADER) {
+ if (pat->op == M_BODY)
+ fseeko (fp, h->content->offset, 0);
- if (fgets (buf, blen - 1, fp) == NULL)
+ if (pat->op == M_HEADER) {
+ if (*(buf = mutt_read_rfc822_line (fp, buf, &blen)) == '\0')
+ break;
+ } else if (fgets (buf, blen - 1, fp) == NULL)
if (mutt_extract_token (&buf, s, M_TOKEN_PATTERN | M_TOKEN_COMMENT) != 0 ||
!buf.data) {
snprintf (err->data, err->dsize, _("Error in expression: %s"), s->dptr);
return (-1);
}
if (mutt_extract_token (&buf, s, M_TOKEN_PATTERN | M_TOKEN_COMMENT) != 0 ||
!buf.data) {
snprintf (err->data, err->dsize, _("Error in expression: %s"), s->dptr);
return (-1);
}
- pat->rx = mem_malloc (sizeof (regex_t));
- r =
- REGCOMP (pat->rx, buf.data,
- REG_NEWLINE | REG_NOSUB | mutt_which_case (buf.data));
- mem_free (&buf.data);
- if (r) {
- regerror (r, pat->rx, err->data, err->dsize);
- regfree (pat->rx);
- mem_free (&pat->rx);
+
+ if (!*buf.data) {
+ snprintf (err->data, err->dsize, _("Empty expression"));
+
+#if 0
+ /* If there are no RE metacharacters, use simple search anyway */
+ if (!pat->stringmatch && !strpbrk (buf.data, "|[{.*+?^$"))
+ pat->stringmatch = 1;
+#endif
+
+ if (pat->stringmatch) {
+ pat->str = m_strdup(buf.data);
+ p_delete(&buf.data);
+ } else {
+ pat->rx = p_new(regex_t, 1);
+ r = REGCOMP (pat->rx, buf.data, REG_NEWLINE | REG_NOSUB | mutt_which_case (buf.data));
+ p_delete(&buf.data);
+ if (r) {
+ regerror (r, pat->rx, err->data, err->dsize);
+ regfree (pat->rx);
+ p_delete(&pat->rx);
+ return (-1);
+ }
+ }
+static int patmatch (const pattern_t* pat, const char* buf) {
+ if (pat->stringmatch)
+ return !strstr (buf, pat->str);
+ else
+ return regexec (pat->rx, buf, 0, NULL, 0);
+}
+
}
if ((flag & M_PDR_ERROR) && !(flag & M_PDR_ABSOLUTE)) { /* getDate has its own error message, don't overwrite it here */
snprintf (err->data, err->dsize, _("Invalid relative date: %s"), pc - 1);
}
if ((flag & M_PDR_ERROR) && !(flag & M_PDR_ABSOLUTE)) { /* getDate has its own error message, don't overwrite it here */
snprintf (err->data, err->dsize, _("Invalid relative date: %s"), pc - 1);
/* the `0' time is Jan 1, 1970 UTC, so in order to prevent a negative time
when doing timezone conversion, we use Jan 2, 1970 UTC as the base
here */
min.tm_mday = 2;
min.tm_year = 70;
/* the `0' time is Jan 1, 1970 UTC, so in order to prevent a negative time
when doing timezone conversion, we use Jan 2, 1970 UTC as the base
here */
min.tm_mday = 2;
min.tm_year = 70;
/* Arbitrary year in the future. Don't set this too high
or mutt_mktime() returns something larger than will
/* Arbitrary year in the future. Don't set this too high
or mutt_mktime() returns something larger than will
if (isdigit ((unsigned char) *pc)) {
/* mininum date specified */
if ((pc = getDate (pc, &min, err)) == NULL) {
if (isdigit ((unsigned char) *pc)) {
/* mininum date specified */
if ((pc = getDate (pc, &min, err)) == NULL) {
max.tm_mday = min.tm_mday;
if (!parse_date_range (pc, &min, &max, haveMin, &baseMin, err)) { /* bail out on any parsing error */
max.tm_mday = min.tm_mday;
if (!parse_date_range (pc, &min, &max, haveMin, &baseMin, err)) { /* bail out on any parsing error */
pat->min = mutt_mktime (&min, 1);
pat->max = mutt_mktime (&max, 1);
pat->min = mutt_mktime (&min, 1);
pat->max = mutt_mktime (&max, 1);
/* compile the sub-expression */
buf = str_substrdup (ps.dptr + 1, p);
if ((tmp = mutt_pattern_comp (buf, flags, err)) == NULL) {
/* compile the sub-expression */
buf = str_substrdup (ps.dptr + 1, p);
if ((tmp = mutt_pattern_comp (buf, flags, err)) == NULL) {
-static int match_adrlist (regex_t * rx, int match_personal, int alladdr,
+static int match_adrlist (pattern_t* pat, int match_personal, int alladdr,
- for (a = va_arg (ap, ADDRESS *); a; a = a->next) {
- if (alladdr ^
- ((a->mailbox && regexec (rx, a->mailbox, 0, NULL, 0) == 0) ||
+ for (a = va_arg (ap, address_t *); a; a = a->next) {
+ if (pat->alladdr ^
+ ((a->mailbox && patmatch (pat, a->mailbox) == 0) ||
mutt_pattern_exec (struct pattern_t *pat, pattern_exec_flag flags,
CONTEXT * ctx, HEADER * h)
{
mutt_pattern_exec (struct pattern_t *pat, pattern_exec_flag flags,
CONTEXT * ctx, HEADER * h)
{
switch (pat->op) {
case M_AND:
return (pat->not ^ (perform_and (pat->child, flags, ctx, h) > 0));
switch (pat->op) {
case M_AND:
return (pat->not ^ (perform_and (pat->child, flags, ctx, h) > 0));
- return (pat->
- not ^ msg_search (ctx, pat->rx, buf, sizeof (buf), pat->op,
- h->msgno));
+#ifdef USE_IMAP
+ /* IMAP search sets h->matched at search compile time */
+ if (ctx->magic == M_IMAP && pat->stringmatch)
+ return (h->matched);
+#endif
+ return (pat->not ^ msg_search (ctx, pat, h->msgno));
pat->alladdr, 1, h->env->cc));
case M_SUBJECT:
return (pat->
not ^ (h->env && h->env->subject
pat->alladdr, 1, h->env->cc));
case M_SUBJECT:
return (pat->
not ^ (h->env && h->env->subject
- && regexec (pat->rx, h->env->subject, 0, NULL, 0) == 0));
+ && patmatch (pat, h->env->subject) == 0));
pat->alladdr, 4, h->env->from,
h->env->sender, h->env->to,
h->env->cc)));
case M_RECIPIENT:
return (pat->
not ^ (h->env
pat->alladdr, 4, h->env->from,
h->env->sender, h->env->to,
h->env->cc)));
case M_RECIPIENT:
return (pat->
not ^ (h->env
pat->alladdr, 2, h->env->to,
h->env->cc)));
case M_LIST:
pat->alladdr, 2, h->env->to,
h->env->cc)));
case M_LIST:
- && regexec (pat->rx, h->env->x_label, 0, NULL, 0) == 0));
+ && patmatch (pat, h->env->x_label) == 0));
+
+ case M_MIMEATTACH:
+ {
+ int count;
+
+ if (h->content->parts)
+ count = mutt_count_body_parts(h, 0);
+ else {
+ mutt_parse_mime_message(ctx, h);
+ count = mutt_count_body_parts(h, 0);
+ mutt_free_body(&h->content->parts);
+ }
+
+ return (pat->not ^ (count >= pat->min && (pat->max == M_MAXRANGE ||
+ count <= pat->max)));
+ }
+
- if (!strchr (s, '~')) { /* yup, so spoof a real request */
+ if (!strchr (s, '~') && !strchr (s, '=')) { /* yup, so spoof a real request */
- if (ascii_strcasecmp ("all", s) == 0 || !str_cmp ("^", s) || !str_cmp (".", s)) /* ~A is more efficient */
- strfcpy (s, "~A", len);
+ if (ascii_strcasecmp ("all", s) == 0 || !m_strcmp("^", s) || !m_strcmp(".", s)) /* ~A is more efficient */
+ m_strcpy(s, len, "~A");
else {
quote_simple (tmp, sizeof (tmp), s);
mutt_expand_fmt (s, len, simple, tmp);
else {
quote_simple (tmp, sizeof (tmp), s);
mutt_expand_fmt (s, len, simple, tmp);
- if (mutt_get_field (prompt, buf, sizeof (buf), M_PATTERN | M_CLEAR) != 0)
+ if (mutt_get_field (prompt, buf, sizeof (buf), M_PATTERN | M_CLEAR) != 0 || !buf[0])
mutt_check_simple (buf, sizeof (buf), NONULL (SimpleSearch));
err.data = error;
err.dsize = sizeof (error);
if ((pat = mutt_pattern_comp (buf, M_FULL_MSG, &err)) == NULL) {
mutt_check_simple (buf, sizeof (buf), NONULL (SimpleSearch));
err.data = error;
err.dsize = sizeof (error);
if ((pat = mutt_pattern_comp (buf, M_FULL_MSG, &err)) == NULL) {
mutt_message _("Executing command on matching messages...");
#define THIS_BODY Context->hdrs[i]->content
mutt_message _("Executing command on matching messages...");
#define THIS_BODY Context->hdrs[i]->content
- else if (str_ncmp (buf, "~A", 2) != 0) {
+
+ /* record new limit pattern, unless match all */
+ if (m_strncmp(buf, "~A", 2) != 0) {
Context->pattern = simple;
simple = NULL; /* don't clobber it */
Context->limit_pattern = mutt_pattern_comp (buf, M_FULL_MSG, &err);
}
}
Context->pattern = simple;
simple = NULL; /* don't clobber it */
Context->limit_pattern = mutt_pattern_comp (buf, M_FULL_MSG, &err);
}
}
if (mutt_get_field ((op == OP_SEARCH) ? _("Search for: ") :
_("Reverse search for: "), buf, sizeof (buf),
M_CLEAR | M_PATTERN) != 0 || !buf[0])
if (mutt_get_field ((op == OP_SEARCH) ? _("Search for: ") :
_("Reverse search for: "), buf, sizeof (buf),
M_CLEAR | M_PATTERN) != 0 || !buf[0])
/* compare the *expanded* version of the search pattern in case
$simple_search has changed while we were searching */
/* compare the *expanded* version of the search pattern in case
$simple_search has changed while we were searching */
if (option (OPTSEARCHINVALID)) {
for (i = 0; i < Context->msgcount; i++)
Context->hdrs[i]->searched = 0;
if (option (OPTSEARCHINVALID)) {
for (i = 0; i < Context->msgcount; i++)
Context->hdrs[i]->searched = 0;