/*
+ * Copyright notice from original mutt:
* Copyright (C) 1996-2000 Michael R. Elkins <me@mutt.org>, and others
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
- */
-
-#if HAVE_CONFIG_H
-# include "config.h"
-#endif
-
-#include "mutt.h"
-#include "mapping.h"
+ *
+ * This file is part of mutt-ng, see http://www.muttng.org/.
+ * It's licensed under the GNU General Public License,
+ * please see the file GPL in the top level source directory.
+ */
+
+#include <lib-lib/lib-lib.h>
+
+#include <lib-mime/mime.h>
+#include <lib-crypt/crypt.h>
+#include <lib-ui/enter.h>
+#include <lib-ui/curses.h>
+#include <lib-mx/mx.h>
+#include <imap/imap.h>
+
+#include "pattern.h"
+#include "alias.h"
+#include "handler.h"
#include "keymap.h"
-#include "mailbox.h"
#include "copy.h"
-#include <string.h>
-#include <stdlib.h>
-#include <ctype.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <stdarg.h>
-
-#include "mutt_crypt.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 patmatch (const pattern_t* pat, const char* buf);
-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 *);
-
-struct pattern_flags
-{
- int tag; /* character used to represent this op */
- int op; /* operation to perform */
+static struct pattern_flags {
+ int tag; /* character used to represent this op */
+ int op; /* operation to perform */
int class;
- int (*eat_arg) (pattern_t *, BUFFER *, BUFFER *);
-}
-Flags[] =
-{
- { 'A', M_ALL, 0, NULL },
- { 'b', M_BODY, M_FULL_MSG, eat_regexp },
- { 'B', M_WHOLE_MSG, M_FULL_MSG, eat_regexp },
- { 'c', M_CC, 0, eat_regexp },
- { 'C', M_RECIPIENT, 0, eat_regexp },
- { 'd', M_DATE, 0, eat_date },
- { 'D', M_DELETED, 0, NULL },
- { 'e', M_SENDER, 0, eat_regexp },
- { 'E', M_EXPIRED, 0, NULL },
- { 'f', M_FROM, 0, eat_regexp },
- { 'F', M_FLAG, 0, NULL },
- { 'g', M_CRYPT_SIGN, 0, NULL },
- { 'G', M_CRYPT_ENCRYPT, 0, NULL },
- { 'h', M_HEADER, M_FULL_MSG, eat_regexp },
- { 'H', M_HORMEL, 0, eat_regexp },
- { 'i', M_ID, 0, eat_regexp },
- { 'k', M_PGP_KEY, 0, NULL },
- { '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 },
- { 'p', M_PERSONAL_RECIP, 0, NULL },
- { 'P', M_PERSONAL_FROM, 0, NULL },
- { 'Q', M_REPLIED, 0, NULL },
- { 'R', M_READ, 0, NULL },
- { 'r', M_DATE_RECEIVED, 0, eat_date },
- { 's', M_SUBJECT, 0, eat_regexp },
- { 'S', M_SUPERSEDED, 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 },
+ int (*eat_arg)(pattern_t *, BUFFER *, BUFFER *);
+} const Flags[] = {
+ {'A', M_ALL, 0, NULL},
+ {'b', M_BODY, M_FULL_MSG, eat_regexp},
+ {'B', M_WHOLE_MSG, M_FULL_MSG, eat_regexp},
+ {'c', M_CC, 0, eat_regexp},
+ {'C', M_RECIPIENT, 0, eat_regexp},
+ {'d', M_DATE, 0, eat_date},
+ {'D', M_DELETED, 0, NULL},
+ {'e', M_SENDER, 0, eat_regexp},
+ {'E', M_EXPIRED, 0, NULL},
+ {'f', M_FROM, 0, eat_regexp},
+ {'F', M_FLAG, 0, NULL},
+ {'g', M_CRYPT_SIGN, 0, NULL},
+ {'G', M_CRYPT_ENCRYPT, 0, NULL},
+ {'h', M_HEADER, M_FULL_MSG, eat_regexp},
+ {'H', M_HORMEL, 0, eat_regexp},
+ {'i', M_ID, 0, eat_regexp},
+ {'k', M_PGP_KEY, 0, NULL},
+ {'L', M_ADDRESS, 0, eat_regexp},
+ {'l', M_LIST, 0, NULL},
+ {'m', M_MESSAGE, 0, eat_range},
+ {'M', M_MULTIPART, 0, NULL},
+ {'n', M_SCORE, 0, eat_range},
+ {'N', M_NEW, 0, NULL},
+ {'O', M_OLD, 0, NULL},
+ {'p', M_PERSONAL_RECIP, 0, NULL},
+ {'P', M_PERSONAL_FROM, 0, NULL},
+ {'Q', M_REPLIED, 0, NULL},
+ {'R', M_READ, 0, NULL},
+ {'r', M_DATE_RECEIVED, 0, eat_date},
+ {'s', M_SUBJECT, 0, eat_regexp},
+ {'S', M_SUPERSEDED, 0, NULL},
+ {'T', M_TAG, 0, NULL},
+ {'t', M_TO, 0, eat_regexp},
+ {'U', M_UNREAD, 0, NULL},
+ {'u', M_SUBSCRIBED_LIST, 0, NULL},
+ {'v', M_COLLAPSED, 0, NULL},
+ {'V', M_CRYPT_VERIFIED, 0, NULL},
#ifdef USE_NNTP
- { 'w', M_NEWSGROUPS, 0, eat_regexp },
+ {'w', M_NEWSGROUPS, 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 },
- { 0 }
+ {'x', M_REFERENCE, 0, eat_regexp},
+ {'X', M_MIMEATTACH, 0, eat_range},
+ {'y', M_XLABEL, 0, eat_regexp},
+ {'z', M_SIZE, 0, eat_range},
+ {'=', M_DUPLICATED, 0, NULL},
+ {'$', M_UNREFERENCED, 0, NULL},
+ {'*', M_REALNAME, 0, NULL},
+ {0, 0, 0, NULL}
};
static pattern_t *SearchPattern = NULL; /* current search pattern */
-static char LastSearch[STRING] = { 0 }; /* last pattern searched for */
-static char LastSearchExpn[LONG_STRING] = { 0 }; /* expanded version of
- LastSearch */
+static char LastSearch[STRING] = { 0 }; /* last pattern searched for */
+static char LastSearchExpn[LONG_STRING] = { 0 }; /* expanded version of
+ LastSearch */
#define M_MAXRANGE -1
#define M_PDR_ERROR 0x0100
#define M_PDR_ERRORDONE (M_PDR_ERROR | M_PDR_DONE)
-
-int mutt_getvaluebychar (char ch, struct mapping_t *table)
+void pattern_wipe(pattern_t *pat)
{
- int i;
-
- for (i = 0; table[i].name; i++)
- {
- if (ch == table[i].name[0])
- return table[i].value;
- }
-
- return (-1);
+ if (pat->rx) {
+ regfree (pat->rx);
+ p_delete(&pat->rx);
+ }
+ p_delete(&pat->str);
+ pattern_list_wipe(&pat->child);
}
/* if no uppercase letters are given, do a case-insensitive search */
int mutt_which_case (const char *s)
{
- while (*s)
- {
+ while (*s) {
if (isalpha ((unsigned char) *s) && isupper ((unsigned char) *s))
- return 0; /* case-sensitive */
+ return 0; /* case-sensitive */
s++;
}
- return REG_ICASE; /* case-insensitive */
+ return REG_ICASE; /* case-insensitive */
}
static int
-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)
{
char tempfile[_POSIX_PATH_MAX];
MESSAGE *msg = NULL;
long lng = 0;
int match = 0;
HEADER *h = ctx->hdrs[msgno];
+ char* buf;
+ ssize_t blen;
- if ((msg = mx_open_message (ctx, msgno)) != NULL)
- {
- if (option (OPTTHOROUGHSRC))
- {
+ if ((msg = mx_open_message (ctx, msgno)) != NULL) {
+ if (option (OPTTHOROUGHSRC)) {
/* decode the header / body */
- memset (&s, 0, sizeof (s));
+ p_clear(&s, 1);
s.fpin = msg->fp;
s.flags = M_CHARCONV;
- mutt_mktemp (tempfile);
- if ((s.fpout = safe_fopen (tempfile, "w+")) == NULL)
- {
- mutt_perror (tempfile);
- return (0);
+ s.fpout = m_tempfile(tempfile, sizeof(tempfile), NONULL(Tempdir), NULL);
+ if (!s.fpout) {
+ mutt_error(_("Could not create temporary file"));
+ return (0);
}
- if (op != M_BODY)
- mutt_copy_header (msg->fp, h, s.fpout, CH_FROM | CH_DECODE, NULL);
+ if (pat->op != M_BODY)
+ mutt_copy_header (msg->fp, h, s.fpout, CH_FROM | CH_DECODE, NULL);
- if (op != M_HEADER)
- {
- mutt_parse_mime_message (ctx, h);
-
- if (WithCrypto && (h->security & ENCRYPT)
- && !crypt_valid_passphrase(h->security))
- {
- mx_close_message (&msg);
- if (fp)
- {
- fclose (fp);
- unlink (tempfile);
- }
- return (0);
- }
-
- fseek (msg->fp, h->offset, 0);
- mutt_body_handler (h->content, &s);
+ if (pat->op != M_HEADER) {
+ mutt_parse_mime_message (ctx, h);
+
+ if ((h->security & ENCRYPT) && !crypt_valid_passphrase (h->security))
+ {
+ mx_close_message (&msg);
+ if (fp) {
+ m_fclose(&fp);
+ unlink (tempfile);
+ }
+ return (0);
+ }
+
+ fseeko (msg->fp, h->offset, 0);
+ mutt_body_handler (h->content, &s);
}
fp = s.fpout;
fflush (fp);
- fseek (fp, 0, 0);
+ fseeko (fp, 0, 0);
fstat (fileno (fp), &st);
lng = (long) st.st_size;
}
- else
- {
+ else {
/* raw header / body */
fp = msg->fp;
- if (op != M_BODY)
- {
- fseek (fp, h->offset, 0);
- lng = h->content->offset - h->offset;
+ if (pat->op != M_BODY) {
+ fseeko (fp, h->offset, 0);
+ lng = h->content->offset - h->offset;
}
- if (op != M_HEADER)
- {
- if (op == M_BODY)
- fseek (fp, h->content->offset, 0);
- lng += h->content->length;
+ if (pat->op != M_HEADER) {
+ if (pat->op == M_BODY)
+ fseeko (fp, h->content->offset, 0);
+ lng += h->content->length;
}
}
+ buf = p_new(char, blen = STRING);
+
/* search the file "fp" */
- while (lng > 0)
- {
- if (fgets (buf, blen - 1, fp) == NULL)
- break; /* don't loop forever */
- if (regexec (rx, buf, 0, NULL, 0) == 0)
- {
- match = 1;
- break;
+ while (lng > 0) {
+ if (pat->op == M_HEADER) {
+ if (!mutt_read_rfc822_line(fp, &buf, &blen))
+ break;
+ } else if (fgets (buf, blen - 1, fp) == NULL)
+ break; /* don't loop forever */
+ if (patmatch (pat, buf) == 0) {
+ match = 1;
+ break;
}
- lng -= mutt_strlen (buf);
+ lng -= m_strlen(buf);
}
-
+
+ p_delete(&buf);
+
mx_close_message (&msg);
- if (option (OPTTHOROUGHSRC))
- {
- fclose (fp);
+ if (option (OPTTHOROUGHSRC)) {
+ m_fclose(&fp);
unlink (tempfile);
}
}
return match;
}
-int eat_regexp (pattern_t *pat, BUFFER *s, BUFFER *err)
+int eat_regexp (pattern_t * pat, BUFFER * s, BUFFER * err)
{
BUFFER buf;
int r;
- memset (&buf, 0, sizeof (buf));
+ p_clear(&buf, 1);
+
if (mutt_extract_token (&buf, s, M_TOKEN_PATTERN | M_TOKEN_COMMENT) != 0 ||
- !buf.data)
- {
+ !buf.data) {
snprintf (err->data, err->dsize, _("Error in expression: %s"), s->dptr);
return (-1);
}
- pat->rx = safe_malloc (sizeof (regex_t));
- r = REGCOMP (pat->rx, buf.data, REG_NEWLINE | REG_NOSUB | mutt_which_case (buf.data));
- FREE (&buf.data);
- if (r)
- {
- regerror (r, pat->rx, err->data, err->dsize);
- regfree (pat->rx);
- FREE (&pat->rx);
+
+ if (!*buf.data) {
+ snprintf (err->data, err->dsize, _("Empty expression"));
return (-1);
}
+
+ 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);
+ }
+ }
return 0;
}
-int eat_range (pattern_t *pat, BUFFER *s, BUFFER *err)
+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);
+}
+
+int eat_range (pattern_t * pat, BUFFER * s, BUFFER * err __attribute__ ((unused)))
{
char *tmp;
int do_exclusive = 0;
int skip_quote = 0;
-
+
/*
* If simple_search is set to "~m %s", the range will have double quotes
* around it...
*/
- if (*s->dptr == '"')
- {
+ if (*s->dptr == '"') {
s->dptr++;
skip_quote = 1;
}
if (*s->dptr == '<')
do_exclusive = 1;
- if ((*s->dptr != '-') && (*s->dptr != '<'))
- {
+ if ((*s->dptr != '-') && (*s->dptr != '<')) {
/* range minimum */
- if (*s->dptr == '>')
- {
+ if (*s->dptr == '>') {
pat->max = M_MAXRANGE;
- pat->min = strtol (s->dptr + 1, &tmp, 0) + 1; /* exclusive range */
+ pat->min = strtol (s->dptr + 1, &tmp, 0) + 1; /* exclusive range */
}
else
pat->min = strtol (s->dptr, &tmp, 0);
- if (toupper ((unsigned char) *tmp) == 'K') /* is there a prefix? */
- {
+ if (toupper ((unsigned char) *tmp) == 'K') { /* is there a prefix? */
pat->min *= 1024;
tmp++;
}
- else if (toupper ((unsigned char) *tmp) == 'M')
- {
+ else if (toupper ((unsigned char) *tmp) == 'M') {
pat->min *= 1048576;
tmp++;
}
- if (*s->dptr == '>')
- {
+ if (*s->dptr == '>') {
s->dptr = tmp;
return 0;
}
- if (*tmp != '-')
- {
+ if (*tmp != '-') {
/* exact value */
pat->max = pat->min;
s->dptr = tmp;
}
tmp++;
}
- else
- {
+ else {
s->dptr++;
tmp = s->dptr;
}
-
- if (isdigit ((unsigned char) *tmp))
- {
+
+ if (isdigit ((unsigned char) *tmp)) {
/* range maximum */
pat->max = strtol (tmp, &tmp, 0);
- if (toupper ((unsigned char) *tmp) == 'K')
- {
+ if (toupper ((unsigned char) *tmp) == 'K') {
pat->max *= 1024;
tmp++;
}
- else if (toupper ((unsigned char) *tmp) == 'M')
- {
+ else if (toupper ((unsigned char) *tmp) == 'M') {
pat->max *= 1048576;
tmp++;
}
if (skip_quote && *tmp == '"')
tmp++;
- SKIPWS (tmp);
- s->dptr = tmp;
+ s->dptr = vskipspaces(tmp);
return 0;
}
-static const char *getDate (const char *s, struct tm *t, BUFFER *err)
+static const char *getDate (const char *s, struct tm *t, BUFFER * err)
{
char *p;
time_t now = time (NULL);
struct tm *tm = localtime (&now);
t->tm_mday = strtol (s, &p, 10);
- if (t->tm_mday < 1 || t->tm_mday > 31)
- {
+ if (t->tm_mday < 1 || t->tm_mday > 31) {
snprintf (err->data, err->dsize, _("Invalid day of month: %s"), s);
return NULL;
}
- if (*p != '/')
- {
+ if (*p != '/') {
/* fill in today's month and year */
t->tm_mon = tm->tm_mon;
t->tm_year = tm->tm_year;
}
p++;
t->tm_mon = strtol (p, &p, 10) - 1;
- if (t->tm_mon < 0 || t->tm_mon > 11)
- {
+ if (t->tm_mon < 0 || t->tm_mon > 11) {
snprintf (err->data, err->dsize, _("Invalid month: %s"), p);
return NULL;
}
- if (*p != '/')
- {
+ if (*p != '/') {
t->tm_year = tm->tm_year;
return p;
}
p++;
t->tm_year = strtol (p, &p, 10);
- if (t->tm_year < 70) /* year 2000+ */
+ if (t->tm_year < 70) /* year 2000+ */
t->tm_year += 100;
else if (t->tm_year > 1900)
t->tm_year -= 1900;
{
char *ps;
int offset = strtol (s, &ps, 0);
+
if ((sign < 0 && offset > 0) || (sign > 0 && offset < 0))
offset = -offset;
- switch (*ps)
- {
- case 'y':
- tm->tm_year += offset;
- break;
- case 'm':
- tm->tm_mon += offset;
- break;
- case 'w':
- tm->tm_mday += 7 * offset;
- break;
- case 'd':
- tm->tm_mday += offset;
- break;
- default:
- return s;
+ switch (*ps) {
+ case 'y':
+ tm->tm_year += offset;
+ break;
+ case 'm':
+ tm->tm_mon += offset;
+ break;
+ case 'w':
+ tm->tm_mday += 7 * offset;
+ break;
+ case 'd':
+ tm->tm_mday += offset;
+ break;
+ default:
+ return s;
}
mutt_normalize_time (tm);
return (ps + 1);
if (min->tm_year > max->tm_year
|| (min->tm_year == max->tm_year && min->tm_mon > max->tm_mon)
|| (min->tm_year == max->tm_year && min->tm_mon == max->tm_mon
- && min->tm_mday > max->tm_mday))
- {
+ && min->tm_mday > max->tm_mday)) {
int tmp;
-
+
tmp = min->tm_year;
min->tm_year = max->tm_year;
max->tm_year = tmp;
-
+
tmp = min->tm_mon;
min->tm_mon = max->tm_mon;
max->tm_mon = tmp;
-
+
tmp = min->tm_mday;
min->tm_mday = max->tm_mday;
max->tm_mday = tmp;
-
+
min->tm_hour = min->tm_min = min->tm_sec = 0;
max->tm_hour = 23;
max->tm_min = max->tm_sec = 59;
}
}
-static const char * parse_date_range (const char* pc, struct tm *min,
- struct tm *max, int haveMin, struct tm *baseMin, BUFFER *err)
+static const char *parse_date_range (const char *pc, struct tm *min,
+ struct tm *max, int haveMin,
+ struct tm *baseMin, BUFFER * err)
{
- int flag = M_PDR_NONE;
- while (*pc && ((flag & M_PDR_DONE) == 0))
- {
+ int flag = M_PDR_NONE;
+
+ while (*pc && ((flag & M_PDR_DONE) == 0)) {
const char *pt;
char ch = *pc++;
- SKIPWS (pc);
- switch (ch)
- {
- case '-':
+
+ pc = vskipspaces(pc);
+ switch (ch) {
+ case '-':
{
- /* try a range of absolute date minus offset of Ndwmy */
- pt = get_offset (min, pc, -1);
- if (pc == pt)
- {
- if (flag == M_PDR_NONE)
- { /* nothing yet and no offset parsed => absolute date? */
- if (!getDate (pc, max, err))
- flag |= (M_PDR_ABSOLUTE | M_PDR_ERRORDONE); /* done bad */
- else
- {
- /* reestablish initial base minimum if not specified */
- if (!haveMin)
- memcpy (min, baseMin, sizeof(struct tm));
- flag |= (M_PDR_ABSOLUTE | M_PDR_DONE); /* done good */
- }
- }
- else
- flag |= M_PDR_ERRORDONE;
- }
- else
- {
- pc = pt;
- if (flag == M_PDR_NONE && !haveMin)
- { /* the very first "-3d" without a previous absolute date */
- max->tm_year = min->tm_year;
- max->tm_mon = min->tm_mon;
- max->tm_mday = min->tm_mday;
- }
- flag |= M_PDR_MINUS;
- }
+ /* try a range of absolute date minus offset of Ndwmy */
+ pt = get_offset (min, pc, -1);
+ if (pc == pt) {
+ if (flag == M_PDR_NONE) { /* nothing yet and no offset parsed => absolute date? */
+ if (!getDate (pc, max, err))
+ flag |= (M_PDR_ABSOLUTE | M_PDR_ERRORDONE); /* done bad */
+ else {
+ /* reestablish initial base minimum if not specified */
+ if (!haveMin)
+ memcpy (min, baseMin, sizeof (struct tm));
+ flag |= (M_PDR_ABSOLUTE | M_PDR_DONE); /* done good */
+ }
+ }
+ else
+ flag |= M_PDR_ERRORDONE;
+ }
+ else {
+ pc = pt;
+ if (flag == M_PDR_NONE && !haveMin) { /* the very first "-3d" without a previous absolute date */
+ max->tm_year = min->tm_year;
+ max->tm_mon = min->tm_mon;
+ max->tm_mday = min->tm_mday;
+ }
+ flag |= M_PDR_MINUS;
+ }
}
break;
- case '+':
- { /* enlarge plusRange */
- pt = get_offset (max, pc, 1);
- if (pc == pt)
- flag |= M_PDR_ERRORDONE;
- else
- {
- pc = pt;
- flag |= M_PDR_PLUS;
- }
+ case '+':
+ { /* enlarge plusRange */
+ pt = get_offset (max, pc, 1);
+ if (pc == pt)
+ flag |= M_PDR_ERRORDONE;
+ else {
+ pc = pt;
+ flag |= M_PDR_PLUS;
+ }
}
break;
- case '*':
- { /* enlarge window in both directions */
- pt = get_offset (min, pc, -1);
- if (pc == pt)
- flag |= M_PDR_ERRORDONE;
- else
- {
- pc = get_offset (max, pc, 1);
- flag |= M_PDR_WINDOW;
- }
+ case '*':
+ { /* enlarge window in both directions */
+ pt = get_offset (min, pc, -1);
+ if (pc == pt)
+ flag |= M_PDR_ERRORDONE;
+ else {
+ pc = get_offset (max, pc, 1);
+ flag |= M_PDR_WINDOW;
+ }
}
break;
- default:
- flag |= M_PDR_ERRORDONE;
+ default:
+ flag |= M_PDR_ERRORDONE;
}
- SKIPWS (pc);
+ pc = vskipspaces(pc);
}
- 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);
}
return ((flag & M_PDR_ERROR) ? NULL : pc);
}
-static int eat_date (pattern_t *pat, BUFFER *s, BUFFER *err)
+static int eat_date (pattern_t * pat, BUFFER * s, BUFFER * err)
{
BUFFER buffer;
struct tm min, max;
- memset (&buffer, 0, sizeof (buffer));
+ p_clear(&buffer, 1);
if (mutt_extract_token (&buffer, s, M_TOKEN_COMMENT | M_TOKEN_PATTERN) != 0
- || !buffer.data)
- {
- strfcpy (err->data, _("error in expression"), err->dsize);
+ || !buffer.data) {
+ m_strcpy(err->data, err->dsize, _("error in expression"));
return (-1);
}
- memset (&min, 0, sizeof (min));
+ p_clear(&min, 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;
- memset (&max, 0, sizeof (max));
+ p_clear(&max, 1);
/* Arbitrary year in the future. Don't set this too high
or mutt_mktime() returns something larger than will
max.tm_min = 59;
max.tm_sec = 59;
- if (strchr ("<>=", buffer.data[0]))
- {
+ if (strchr ("<>=", buffer.data[0])) {
/* offset from current time
- <3d less than three days ago
- >3d more than three days ago
- =3d exactly three days ago */
+ <3d less than three days ago
+ >3d more than three days ago
+ =3d exactly three days ago */
time_t now = time (NULL);
struct tm *tm = localtime (&now);
int exact = 0;
- if (buffer.data[0] == '<')
- {
+ if (buffer.data[0] == '<') {
memcpy (&min, tm, sizeof (min));
tm = &min;
}
- else
- {
+ else {
memcpy (&max, tm, sizeof (max));
tm = &max;
if (buffer.data[0] == '=')
- exact++;
+ exact++;
}
tm->tm_hour = 23;
tm->tm_min = tm->tm_sec = 59;
/* force negative offset */
get_offset (tm, buffer.data + 1, -1);
- if (exact)
- {
+ if (exact) {
/* start at the beginning of the day in question */
memcpy (&min, &max, sizeof (max));
min.tm_hour = min.tm_sec = min.tm_min = 0;
}
}
- else
- {
+ else {
const char *pc = buffer.data;
int haveMin = FALSE;
int untilNow = FALSE;
- if (isdigit ((unsigned char)*pc))
- {
+
+ if (isdigit ((unsigned char) *pc)) {
/* mininum date specified */
- if ((pc = getDate (pc, &min, err)) == NULL)
- {
- FREE (&buffer.data);
- return (-1);
+ if ((pc = getDate (pc, &min, err)) == NULL) {
+ p_delete(&buffer.data);
+ return (-1);
}
haveMin = TRUE;
- SKIPWS (pc);
- if (*pc == '-')
- {
- const char *pt = pc + 1;
- SKIPWS (pt);
- untilNow = (*pt == '\0');
+ pc = vskipspaces(pc);
+ if (*pc == '-') {
+ const char *pt;
+
+ pt = skipspaces(pc + 1);
+ untilNow = (*pt == '\0');
}
}
- if (!untilNow)
- { /* max date or relative range/window */
+ if (!untilNow) { /* max date or relative range/window */
struct tm baseMin;
- if (!haveMin)
- { /* save base minimum and set current date, e.g. for "-3d+1d" */
- time_t now = time (NULL);
- struct tm *tm = localtime (&now);
- memcpy (&baseMin, &min, sizeof(baseMin));
- memcpy (&min, tm, sizeof (min));
- min.tm_hour = min.tm_sec = min.tm_min = 0;
+ if (!haveMin) { /* save base minimum and set current date, e.g. for "-3d+1d" */
+ time_t now = time (NULL);
+ struct tm *tm = localtime (&now);
+
+ memcpy (&baseMin, &min, sizeof (baseMin));
+ memcpy (&min, tm, sizeof (min));
+ min.tm_hour = min.tm_sec = min.tm_min = 0;
}
-
+
/* preset max date for relative offsets,
- if nothing follows we search for messages on a specific day */
+ if nothing follows we search for messages on a specific day */
max.tm_year = min.tm_year;
max.tm_mon = min.tm_mon;
max.tm_mday = min.tm_mday;
- if (!parse_date_range (pc, &min, &max, haveMin, &baseMin, err))
- { /* bail out on any parsing error */
- FREE (&buffer.data);
- return (-1);
+ if (!parse_date_range (pc, &min, &max, haveMin, &baseMin, err)) { /* bail out on any parsing error */
+ p_delete(&buffer.data);
+ return (-1);
}
}
}
pat->min = mutt_mktime (&min, 1);
pat->max = mutt_mktime (&max, 1);
- FREE (&buffer.data);
+ p_delete(&buffer.data);
return 0;
}
-static struct pattern_flags *lookup_tag (char tag)
+static const struct pattern_flags *lookup_tag (char tag)
{
int i;
return NULL;
}
-static /* const */ char *find_matching_paren (/* const */ char *s)
+static const char *find_matching_paren (const char *s)
{
- int level = 1;
+ int level = 1;
- for (; *s; s++)
- {
- if (*s == '(')
- level++;
- else if (*s == ')')
- {
- level--;
- if (!level)
- break;
+ for (; *s; s++) {
+ level += (*s == '(') - (*s == ')');
+ if (!level)
+ break;
}
- }
- return s;
-}
-
-void mutt_pattern_free (pattern_t **pat)
-{
- pattern_t *tmp;
-
- while (*pat)
- {
- tmp = *pat;
- *pat = (*pat)->next;
-
- if (tmp->rx)
- {
- regfree (tmp->rx);
- FREE (&tmp->rx);
- }
- if (tmp->child)
- mutt_pattern_free (&tmp->child);
- FREE (&tmp);
- }
+ return s;
}
-pattern_t *mutt_pattern_comp (/* const */ char *s, int flags, BUFFER *err)
+pattern_t *mutt_pattern_comp(const char *s, int flags, BUFFER *err)
{
pattern_t *curlist = NULL;
pattern_t *tmp;
int not = 0;
int alladdr = 0;
int or = 0;
- int implicit = 1; /* used to detect logical AND operator */
- struct pattern_flags *entry;
+ int implicit = 1; /* used to detect logical AND operator */
+ const struct pattern_flags *entry;
char *p;
char *buf;
BUFFER ps;
- memset (&ps, 0, sizeof (ps));
- ps.dptr = s;
- ps.dsize = mutt_strlen (s);
+ p_clear(&ps, 1);
+ ps.dptr = s;
+ ps.dsize = m_strlen(s);
- while (*ps.dptr)
- {
- SKIPWS (ps.dptr);
- switch (*ps.dptr)
- {
- case '^':
- ps.dptr++;
- alladdr = !alladdr;
- break;
- case '!':
- ps.dptr++;
- not = !not;
- break;
- case '|':
- if (!or)
- {
- if (!curlist)
- {
- snprintf (err->data, err->dsize, _("error in pattern at: %s"), ps.dptr);
- return NULL;
- }
- if (curlist->next)
- {
- /* A & B | C == (A & B) | C */
- tmp = new_pattern ();
- tmp->op = M_AND;
- tmp->child = curlist;
-
- curlist = tmp;
- last = curlist;
- }
-
- or = 1;
- }
- ps.dptr++;
- implicit = 0;
- not = 0;
- alladdr = 0;
- break;
- case '~':
- if (implicit && or)
- {
- /* A | B & C == (A | B) & C */
- tmp = new_pattern ();
- tmp->op = M_OR;
- tmp->child = curlist;
- curlist = tmp;
- last = tmp;
- or = 0;
- }
-
- tmp = new_pattern ();
- tmp->not = not;
- tmp->alladdr = alladdr;
- not = 0;
- alladdr=0;
-
- if (last)
- last->next = tmp;
- else
- curlist = tmp;
- last = tmp;
-
- ps.dptr++; /* move past the ~ */
- if ((entry = lookup_tag (*ps.dptr)) == NULL)
- {
- snprintf (err->data, err->dsize, _("%c: invalid command"), *ps.dptr);
- mutt_pattern_free (&curlist);
- return NULL;
- }
- if (entry->class && (flags & entry->class) == 0)
- {
- snprintf (err->data, err->dsize, _("%c: not supported in this mode"), *ps.dptr);
- mutt_pattern_free (&curlist);
- return NULL;
- }
- tmp->op = entry->op;
-
- ps.dptr++; /* eat the operator and any optional whitespace */
- SKIPWS (ps.dptr);
-
- if (entry->eat_arg)
- {
- if (!*ps.dptr)
- {
- snprintf (err->data, err->dsize, _("missing parameter"));
- mutt_pattern_free (&curlist);
- return NULL;
- }
- if (entry->eat_arg (tmp, &ps, err) == -1)
- {
- mutt_pattern_free (&curlist);
- return NULL;
- }
- }
- implicit = 1;
- break;
- case '(':
- p = find_matching_paren (ps.dptr + 1);
- if (*p != ')')
- {
- snprintf (err->data, err->dsize, _("mismatched parenthesis: %s"), ps.dptr);
- mutt_pattern_free (&curlist);
- return NULL;
- }
- /* compile the sub-expression */
- buf = mutt_substrdup (ps.dptr + 1, p);
- if ((tmp = mutt_pattern_comp (buf, flags, err)) == NULL)
- {
- FREE (&buf);
- mutt_pattern_free (&curlist);
- return NULL;
- }
- FREE (&buf);
- if (last)
- last->next = tmp;
- else
- curlist = tmp;
- last = tmp;
- tmp->not ^= not;
- tmp->alladdr |= alladdr;
- not = 0;
- alladdr = 0;
- ps.dptr = p + 1; /* restore location */
- break;
- default:
- snprintf (err->data, err->dsize, _("error in pattern at: %s"), ps.dptr);
- mutt_pattern_free (&curlist);
- return NULL;
+ while (*ps.dptr) {
+ ps.dptr = vskipspaces(ps.dptr);
+ switch (*ps.dptr) {
+ case '^':
+ ps.dptr++;
+ alladdr = !alladdr;
+ break;
+ case '!':
+ ps.dptr++;
+ not = !not;
+ break;
+ case '|':
+ if (!or) {
+ if (!curlist) {
+ snprintf (err->data, err->dsize, _("error in pattern at: %s"),
+ ps.dptr);
+ return NULL;
+ }
+ if (curlist->next) {
+ /* A & B | C == (A & B) | C */
+ tmp = pattern_new();
+ tmp->op = M_AND;
+ tmp->child = curlist;
+
+ curlist = tmp;
+ last = curlist;
+ }
+
+ or = 1;
+ }
+ ps.dptr++;
+ implicit = 0;
+ not = 0;
+ alladdr = 0;
+ break;
+ case '=':
+ /* fallthrough */
+ case '~':
+ if (implicit && or) {
+ /* A | B & C == (A | B) & C */
+ tmp = pattern_new();
+ tmp->op = M_OR;
+ tmp->child = curlist;
+ curlist = tmp;
+ last = tmp;
+ or = 0;
+ }
+
+ tmp = pattern_new();
+ tmp->not = not;
+ tmp->alladdr = alladdr;
+ tmp->stringmatch = (*ps.dptr == '=') ? 1 : 0;
+ not = 0;
+ alladdr = 0;
+
+ if (last)
+ last->next = tmp;
+ else
+ curlist = tmp;
+ last = tmp;
+
+ ps.dptr++; /* move past the ~ */
+ if ((entry = lookup_tag (*ps.dptr)) == NULL) {
+ snprintf (err->data, err->dsize, _("%c: invalid command"), *ps.dptr);
+ pattern_list_wipe(&curlist);
+ return NULL;
+ }
+ if (entry->class && (flags & entry->class) == 0) {
+ snprintf (err->data, err->dsize, _("%c: not supported in this mode"),
+ *ps.dptr);
+ pattern_list_wipe(&curlist);
+ return NULL;
+ }
+ tmp->op = entry->op;
+
+ ps.dptr = vskipspaces(ps.dptr + 1);
+
+ if (entry->eat_arg) {
+ if (!*ps.dptr) {
+ snprintf (err->data, err->dsize, _("missing parameter"));
+ pattern_list_wipe(&curlist);
+ return NULL;
+ }
+ if (entry->eat_arg (tmp, &ps, err) == -1) {
+ pattern_list_wipe(&curlist);
+ return NULL;
+ }
+ }
+ implicit = 1;
+ break;
+ case '(':
+ p = find_matching_paren (ps.dptr + 1);
+ if (*p != ')') {
+ snprintf (err->data, err->dsize, _("mismatched parenthesis: %s"),
+ ps.dptr);
+ pattern_list_wipe(&curlist);
+ return NULL;
+ }
+ /* compile the sub-expression */
+ buf = p_dupstr(ps.dptr + 1, p - ps.dptr - 1);
+ if ((tmp = mutt_pattern_comp (buf, flags, err)) == NULL) {
+ p_delete(&buf);
+ pattern_list_wipe(&curlist);
+ return NULL;
+ }
+ p_delete(&buf);
+ if (last)
+ last->next = tmp;
+ else
+ curlist = tmp;
+ last = tmp;
+ tmp->not ^= not;
+ tmp->alladdr |= alladdr;
+ not = 0;
+ alladdr = 0;
+ ps.dptr = p + 1; /* restore location */
+ break;
+ default:
+ snprintf (err->data, err->dsize, _("error in pattern at: %s"), ps.dptr);
+ pattern_list_wipe(&curlist);
+ return NULL;
}
}
- if (!curlist)
- {
- strfcpy (err->data, _("empty pattern"), err->dsize);
+ if (!curlist) {
+ m_strcpy(err->data, err->dsize, _("empty pattern"));
return NULL;
}
- if (curlist->next)
- {
- tmp = new_pattern ();
+ if (curlist->next) {
+ tmp = pattern_new();
tmp->op = or ? M_OR : M_AND;
tmp->child = curlist;
curlist = tmp;
}
static int
-perform_and (pattern_t *pat, pattern_exec_flag flags, CONTEXT *ctx, HEADER *hdr)
+perform_and (pattern_t * pat, pattern_exec_flag flags, CONTEXT * ctx,
+ HEADER * hdr)
{
for (; pat; pat = pat->next)
if (mutt_pattern_exec (pat, flags, ctx, hdr) <= 0)
}
static int
-perform_or (struct pattern_t *pat, pattern_exec_flag flags, CONTEXT *ctx, HEADER *hdr)
+perform_or (struct pattern_t *pat, pattern_exec_flag flags, CONTEXT * ctx,
+ HEADER * hdr)
{
for (; pat; pat = pat->next)
if (mutt_pattern_exec (pat, flags, ctx, hdr) > 0)
return 0;
}
-static int match_adrlist (regex_t *rx, int match_personal, int alladdr,
- int n, ...)
+static int match_adrlist (pattern_t* pat, int match_personal, int alladdr __attribute__ ((unused)),
+ int n, ...)
{
va_list ap;
- ADDRESS *a;
+ address_t *a;
va_start (ap, n);
- for ( ; n ; n --)
- {
- for (a = va_arg (ap, ADDRESS *) ; a ; a = a->next)
- {
- if (alladdr^
- ((a->mailbox && regexec (rx, a->mailbox, 0, NULL, 0) == 0) ||
- (match_personal && a->personal &&
- regexec (rx, a->personal, 0, NULL, 0) == 0)))
- {
- va_end (ap);
- return (! alladdr); /* Found match, or non-match if alladdr */
+ for (; n; n--) {
+ for (a = va_arg (ap, address_t *); a; a = a->next) {
+ if (pat->alladdr ^
+ ((a->mailbox && patmatch (pat, a->mailbox) == 0) ||
+ (match_personal && a->personal &&
+ patmatch (pat, a->personal) == 0))) {
+ va_end (ap);
+ return (!pat->alladdr); /* Found match, or non-match if alladdr */
}
}
}
va_end (ap);
- return alladdr; /* No matches, or all matches if alladdr */
+ return pat->alladdr; /* No matches, or all matches if alladdr */
}
-static int match_reference (regex_t *rx, LIST *refs)
+static int match_reference (pattern_t* pat, string_list_t * refs)
{
for (; refs; refs = refs->next)
- if (regexec (rx, refs->data, 0, NULL, 0) == 0)
+ if (patmatch (pat, refs->data) == 0)
return 1;
return 0;
}
-int mutt_is_list_recipient (int alladdr, ADDRESS *a1, ADDRESS *a2)
+int mutt_is_list_recipient (int alladdr, address_t * a1, address_t * a2)
{
- for (; a1 ; a1 = a1->next)
+ for (; a1; a1 = a1->next)
if (alladdr ^ mutt_is_subscribed_list (a1))
- return (! alladdr);
- for (; a2 ; a2 = a2->next)
+ return (!alladdr);
+ for (; a2; a2 = a2->next)
if (alladdr ^ mutt_is_subscribed_list (a2))
- return (! alladdr);
+ return (!alladdr);
return alladdr;
}
-int mutt_is_list_cc (int alladdr, ADDRESS *a1, ADDRESS *a2)
+int mutt_is_list_cc (int alladdr, address_t * a1, address_t * a2)
{
- for (; a1 ; a1 = a1->next)
+ for (; a1; a1 = a1->next)
if (alladdr ^ mutt_is_mail_list (a1))
- return (! alladdr);
- for (; a2 ; a2 = a2->next)
+ return (!alladdr);
+ for (; a2; a2 = a2->next)
if (alladdr ^ mutt_is_mail_list (a2))
- return (! alladdr);
+ return (!alladdr);
return alladdr;
}
-static int match_user (int alladdr, ADDRESS *a1, ADDRESS *a2)
+static int match_user (int alladdr, address_t * a1, address_t * a2)
{
- for (; a1 ; a1 = a1->next)
+ for (; a1; a1 = a1->next)
if (alladdr ^ mutt_addr_is_user (a1))
- return (! alladdr);
- for (; a2 ; a2 = a2->next)
+ return (!alladdr);
+ for (; a2; a2 = a2->next)
if (alladdr ^ mutt_addr_is_user (a2))
- return (! alladdr);
+ return (!alladdr);
return alladdr;
}
+/* test if name is considered a real name, i.e. consists of at least 2
+ * space-separated words of which none may end in a dot
+ */
+static int valid_realname (const char *name)
+{
+ const char *p = name;
+ int ret = 0;
+
+ while (*p) {
+ if (isspace (*p))
+ ret++;
+ else if (*p == '.')
+ /* skip abbr. parts of names (e.g. 'J. User') */
+ ret--;
+ p++;
+ }
+ return (ret >= 1);
+}
+
/* flags
M_MATCH_FULL_ADDRESS match both personal and machine address */
int
-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)
{
- char buf[STRING];
+ switch (pat->op) {
+ case M_AND:
+ return (pat->not ^ (perform_and (pat->child, flags, ctx, h) > 0));
+ case M_OR:
+ return (pat->not ^ (perform_or (pat->child, flags, ctx, h) > 0));
+ case M_ALL:
+ return (!pat->not);
+ case M_EXPIRED:
+ return (pat->not ^ h->expired);
+ case M_SUPERSEDED:
+ return (pat->not ^ h->superseded);
+ case M_FLAG:
+ return (pat->not ^ h->flagged);
+ case M_TAG:
+ return (pat->not ^ h->tagged);
+ case M_NEW:
+ return (pat->not ? h->old || h->read : !(h->old || h->read));
+ case M_UNREAD:
+ return (pat->not ? h->read : !h->read);
+ case M_REPLIED:
+ return (pat->not ^ h->replied);
+ case M_OLD:
+ return (pat->not ? (!h->old || h->read) : (h->old && !h->read));
+ case M_READ:
+ return (pat->not ^ h->read);
+ case M_DELETED:
+ return (pat->not ^ h->deleted);
+ case M_MESSAGE:
+ return (pat->not ^ (h->msgno >= pat->min - 1 && (pat->max == M_MAXRANGE ||
+ h->msgno <=
+ pat->max - 1)));
+ case M_DATE:
+ return (pat->
+ not ^ (h->date_sent >= pat->min && h->date_sent <= pat->max));
+ case M_DATE_RECEIVED:
+ return (pat->not ^ (h->received >= pat->min && h->received <= pat->max));
+ case M_BODY:
+ case M_HEADER:
+ case M_WHOLE_MSG:
+ /* IMAP search sets h->matched at search compile time */
+ if (ctx->magic == M_IMAP && pat->stringmatch)
+ return (h->matched);
+ return (pat->not ^ msg_search (ctx, pat, h->msgno));
+ case M_SENDER:
+ return (pat->not ^ match_adrlist (pat, flags & M_MATCH_FULL_ADDRESS,
+ pat->alladdr, 1, h->env->sender));
+ case M_FROM:
+ return (pat->not ^ match_adrlist (pat, flags & M_MATCH_FULL_ADDRESS,
+ pat->alladdr, 1, h->env->from));
+ case M_TO:
+ return (pat->not ^ match_adrlist (pat, flags & M_MATCH_FULL_ADDRESS,
+ pat->alladdr, 1, h->env->to));
+ case M_CC:
+ return (pat->not ^ match_adrlist (pat, flags & M_MATCH_FULL_ADDRESS,
+ pat->alladdr, 1, h->env->cc));
+ case M_SUBJECT:
+ return (pat->
+ not ^ (h->env && h->env->subject
+ && patmatch (pat, h->env->subject) == 0));
+ case M_ID:
+ return (pat->
+ not ^ (h->env && h->env->message_id
+ && patmatch (pat, h->env->message_id) == 0));
+ case M_SCORE:
+ return (pat->not ^ (h->score >= pat->min && (pat->max == M_MAXRANGE ||
+ h->score <= pat->max)));
+ case M_SIZE:
+ return (pat->
+ not ^ (h->content->length >= pat->min
+ && (pat->max == M_MAXRANGE
+ || h->content->length <= pat->max)));
+ case M_REFERENCE:
+ return (pat->not ^ match_reference (pat, h->env->references));
+ case M_ADDRESS:
+ return (pat->
+ not ^ (h->env
+ && match_adrlist (pat, flags & M_MATCH_FULL_ADDRESS,
+ pat->alladdr, 4, h->env->from,
+ h->env->sender, h->env->to,
+ h->env->cc)));
+ case M_RECIPIENT:
+ return (pat->
+ not ^ (h->env
+ && match_adrlist (pat, flags & M_MATCH_FULL_ADDRESS,
+ pat->alladdr, 2, h->env->to,
+ h->env->cc)));
+ case M_LIST:
+ return (pat->
+ not ^ (h->env
+ && mutt_is_list_cc (pat->alladdr, h->env->to,
+ h->env->cc)));
+ case M_SUBSCRIBED_LIST:
+ return (pat->
+ not ^ (h->env
+ && mutt_is_list_recipient (pat->alladdr, h->env->to,
+ h->env->cc)));
+ case M_PERSONAL_RECIP:
+ return (pat->
+ not ^ (h->env
+ && match_user (pat->alladdr, h->env->to, h->env->cc)));
+ case M_PERSONAL_FROM:
+ return (pat->
+ not ^ (h->env && match_user (pat->alladdr, h->env->from, NULL)));
+ case M_COLLAPSED:
+ return (pat->not ^ (h->collapsed && h->num_hidden > 1));
+ case M_CRYPT_SIGN:
+ return (pat->not ^ ((h->security & SIGN) ? 1 : 0));
+ case M_CRYPT_VERIFIED:
+ return (pat->not ^ ((h->security & GOODSIGN) ? 1 : 0));
+ case M_CRYPT_ENCRYPT:
+ return (pat->not ^ ((h->security & ENCRYPT) ? 1 : 0));
+ case M_PGP_KEY:
+ return (pat->not ^ ((h->security & APPLICATION_PGP)
+ && (h->security & PGPKEY)));
+ case M_XLABEL:
+ return (pat->
+ not ^ (h->env->x_label
+ && patmatch (pat, h->env->x_label) == 0));
+ case M_HORMEL:
+ return (pat->
+ not ^ (h->env->spam && h->env->spam->data
+ && patmatch (pat, h->env->spam->data) == 0));
+ case M_DUPLICATED:
+ return (pat->not ^ (h->thread && h->thread->duplicate_thread));
+
+ 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);
+ body_list_wipe(&h->content->parts);
+ }
+
+ return (pat->not ^ (count >= pat->min && (pat->max == M_MAXRANGE ||
+ count <= pat->max)));
+ }
- switch (pat->op)
- {
- case M_AND:
- return (pat->not ^ (perform_and (pat->child, flags, ctx, h) > 0));
- case M_OR:
- return (pat->not ^ (perform_or (pat->child, flags, ctx, h) > 0));
- case M_ALL:
- return (!pat->not);
- case M_EXPIRED:
- return (pat->not ^ h->expired);
- case M_SUPERSEDED:
- return (pat->not ^ h->superseded);
- case M_FLAG:
- return (pat->not ^ h->flagged);
- case M_TAG:
- return (pat->not ^ h->tagged);
- case M_NEW:
- return (pat->not ? h->old || h->read : !(h->old || h->read));
- case M_UNREAD:
- return (pat->not ? h->read : !h->read);
- case M_REPLIED:
- return (pat->not ^ h->replied);
- case M_OLD:
- return (pat->not ? (!h->old || h->read) : (h->old && !h->read));
- case M_READ:
- return (pat->not ^ h->read);
- case M_DELETED:
- return (pat->not ^ h->deleted);
- case M_MESSAGE:
- return (pat->not ^ (h->msgno >= pat->min - 1 && (pat->max == M_MAXRANGE ||
- h->msgno <= pat->max - 1)));
- case M_DATE:
- return (pat->not ^ (h->date_sent >= pat->min && h->date_sent <= pat->max));
- case M_DATE_RECEIVED:
- return (pat->not ^ (h->received >= pat->min && h->received <= pat->max));
- case M_BODY:
- case M_HEADER:
- case M_WHOLE_MSG:
- return (pat->not ^ msg_search (ctx, pat->rx, buf, sizeof (buf), pat->op, h->msgno));
- case M_SENDER:
- return (pat->not ^ match_adrlist (pat->rx, flags & M_MATCH_FULL_ADDRESS,
- pat->alladdr, 1, h->env->sender));
- case M_FROM:
- return (pat->not ^ match_adrlist (pat->rx, flags & M_MATCH_FULL_ADDRESS,
- pat->alladdr, 1, h->env->from));
- case M_TO:
- return (pat->not ^ match_adrlist (pat->rx, flags & M_MATCH_FULL_ADDRESS,
- pat->alladdr, 1, h->env->to));
- case M_CC:
- return (pat->not ^ match_adrlist (pat->rx, flags & M_MATCH_FULL_ADDRESS,
- 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));
- case M_ID:
- return (pat->not ^ (h->env && h->env->message_id && regexec (pat->rx, h->env->message_id, 0, NULL, 0) == 0));
- case M_SCORE:
- return (pat->not ^ (h->score >= pat->min && (pat->max == M_MAXRANGE ||
- h->score <= pat->max)));
- case M_SIZE:
- return (pat->not ^ (h->content->length >= pat->min && (pat->max == M_MAXRANGE || h->content->length <= pat->max)));
- case M_REFERENCE:
- return (pat->not ^ match_reference (pat->rx, h->env->references));
- case M_ADDRESS:
- return (pat->not ^ (h->env && match_adrlist (pat->rx, flags & M_MATCH_FULL_ADDRESS,
- pat->alladdr, 4, h->env->from,
- h->env->sender, h->env->to, h->env->cc)));
- case M_RECIPIENT:
- return (pat->not ^ (h->env && match_adrlist (pat->rx, flags & M_MATCH_FULL_ADDRESS,
- pat->alladdr, 2, h->env->to, h->env->cc)));
- case M_LIST:
- return (pat->not ^ (h->env && mutt_is_list_recipient (pat->alladdr, h->env->to, h->env->cc)));
- case M_PERSONAL_RECIP:
- return (pat->not ^ (h->env && match_user (pat->alladdr, h->env->to, h->env->cc)));
- case M_PERSONAL_FROM:
- return (pat->not ^ (h->env && match_user (pat->alladdr, h->env->from, NULL)));
- case M_COLLAPSED:
- return (pat->not ^ (h->collapsed && h->num_hidden > 1));
- case M_CRYPT_SIGN:
- if (!WithCrypto)
- break;
- return (pat->not ^ ((h->security & SIGN) ? 1 : 0));
- case M_CRYPT_VERIFIED:
- if (!WithCrypto)
- break;
- return (pat->not ^ ((h->security & GOODSIGN) ? 1 : 0));
- case M_CRYPT_ENCRYPT:
- if (!WithCrypto)
- break;
- return (pat->not ^ ((h->security & ENCRYPT) ? 1 : 0));
- case M_PGP_KEY:
- if (!(WithCrypto & APPLICATION_PGP))
- break;
- return (pat->not ^ ((h->security & APPLICATION_PGP) && (h->security & PGPKEY)));
- case M_XLABEL:
- return (pat->not ^ (h->env->x_label && regexec (pat->rx, h->env->x_label, 0, NULL, 0) == 0));
- case M_HORMEL:
- return (pat->not ^ (h->env->spam && h->env->spam->data && regexec (pat->rx, h->env->spam->data, 0, NULL, 0) == 0));
- case M_DUPLICATED:
- return (pat->not ^ (h->thread && h->thread->duplicate_thread));
- case M_UNREFERENCED:
- return (pat->not ^ (h->thread && !h->thread->child));
+ case M_UNREFERENCED:
+ return (pat->not ^ (h->thread && !h->thread->child));
+ case M_MULTIPART:
+ return (pat->not ^ (h->content && h->content->type == TYPEMULTIPART));
+ case M_REALNAME:
+ /* realname filter:
+ * we have a match if
+ * - From: matches $alternates
+ * - or we have an alias for current address
+ * - or From: contains valid email address _and_ name has >= 2 fields
+ */
+ return (h->env && h->env->from
+ && (mutt_addr_is_user(h->env->from)
+ || alias_reverse_lookup(h->env->from)
+ || (h->env->from->personal
+ && valid_realname(h->env-> from->personal)
+ && h->env->from->mailbox)
+ )) ^ pat->not;
#ifdef USE_NNTP
- case M_NEWSGROUPS:
- return (pat->not ^ (h->env->newsgroups && regexec (pat->rx, h->env->newsgroups, 0, NULL, 0) == 0));
+ case M_NEWSGROUPS:
+ return (pat->
+ not ^ (h->env->newsgroups
+ && patmatch (pat, h->env->newsgroups) == 0));
#endif
}
mutt_error (_("error: unknown op %d (report this error)."), pat->op);
return (-1);
}
-static void quote_simple(char *tmp, size_t len, const char *p)
+static void quote_simple (char *tmp, ssize_t len, const char *p)
{
- int i = 0;
-
+ ssize_t i = 0;
+
tmp[i++] = '"';
- while (*p && i < len - 3)
- {
+ while (*p && i < len - 3) {
if (*p == '\\' || *p == '"')
tmp[i++] = '\\';
tmp[i++] = *p++;
tmp[i++] = '"';
tmp[i] = 0;
}
-
+
/* convert a simple search into a real request */
-void mutt_check_simple (char *s, size_t len, const char *simple)
+void mutt_check_simple (char *s, ssize_t len, const char *simple)
{
char tmp[LONG_STRING];
/* XXX - is ascii_strcasecmp() right here, or should we use locale's
* equivalences?
*/
-
- if (!strchr (s, '~')) /* yup, so spoof a real request */
- {
+
+ if (!strchr (s, '~') && !strchr (s, '=')) { /* yup, so spoof a real request */
/* convert old tokens into the new format */
- if (ascii_strcasecmp ("all", s) == 0 ||
- !mutt_strcmp ("^", s) || !mutt_strcmp (".", 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 if (ascii_strcasecmp ("del", s) == 0)
- strfcpy (s, "~D", len);
+ m_strcpy(s, len, "~D");
else if (ascii_strcasecmp ("flag", s) == 0)
- strfcpy (s, "~F", len);
+ m_strcpy(s, len, "~F");
else if (ascii_strcasecmp ("new", s) == 0)
- strfcpy (s, "~N", len);
+ m_strcpy(s, len, "~N");
else if (ascii_strcasecmp ("old", s) == 0)
- strfcpy (s, "~O", len);
+ m_strcpy(s, len, "~O");
else if (ascii_strcasecmp ("repl", s) == 0)
- strfcpy (s, "~Q", len);
+ m_strcpy(s, len, "~Q");
else if (ascii_strcasecmp ("read", s) == 0)
- strfcpy (s, "~R", len);
+ m_strcpy(s, len, "~R");
else if (ascii_strcasecmp ("tag", s) == 0)
- strfcpy (s, "~T", len);
+ m_strcpy(s, len, "~T");
else if (ascii_strcasecmp ("unread", s) == 0)
- strfcpy (s, "~U", len);
- else
- {
- quote_simple (tmp, sizeof(tmp), s);
- mutt_expand_fmt (s, len, simple, tmp);
+ m_strcpy(s, len, "~U");
+ else {
+ quote_simple (tmp, sizeof (tmp), s);
+ m_file_fmt(s, len, simple, tmp);
}
}
}
BUFFER err;
int i;
- strfcpy (buf, NONULL (Context->pattern), sizeof (buf));
+ m_strcpy(buf, sizeof(buf), NONULL(Context->pattern));
if (prompt || op != M_LIMIT)
- if (mutt_get_field (prompt, buf, sizeof (buf), M_PATTERN | M_CLEAR) != 0 || !buf[0])
- return (-1);
+ if (mutt_get_field (prompt, buf, sizeof (buf), M_PATTERN | M_CLEAR) != 0 || !buf[0])
+ return (-1);
mutt_message _("Compiling search pattern...");
-
- simple = safe_strdup (buf);
+
+ simple = m_strdup(buf);
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)
- {
- FREE (&simple);
+ if ((pat = mutt_pattern_comp (buf, M_FULL_MSG, &err)) == NULL) {
+ p_delete(&simple);
mutt_error ("%s", err.data);
return (-1);
}
+ if (Context->magic == M_IMAP && imap_search (Context, pat) < 0)
+ return -1;
+
mutt_message _("Executing command on matching messages...");
#define THIS_BODY Context->hdrs[i]->content
- if (op == M_LIMIT)
- {
- Context->vcount = 0;
- Context->vsize = 0;
+ if (op == M_LIMIT) {
+ Context->vcount = 0;
+ Context->vsize = 0;
Context->collapsed = 0;
- for (i = 0; i < Context->msgcount; i++)
- {
+ for (i = 0; i < Context->msgcount; i++) {
/* new limit pattern implicitly uncollapses all threads */
Context->hdrs[i]->virtual = -1;
Context->hdrs[i]->limited = 0;
Context->hdrs[i]->collapsed = 0;
Context->hdrs[i]->num_hidden = 0;
- if (mutt_pattern_exec (pat, M_MATCH_FULL_ADDRESS, Context, Context->hdrs[i]))
- {
- Context->hdrs[i]->virtual = Context->vcount;
- Context->hdrs[i]->limited = 1;
- Context->v2r[Context->vcount] = i;
- Context->vcount++;
- Context->vsize+=THIS_BODY->length + THIS_BODY->offset -
- THIS_BODY->hdr_offset;
+ if (mutt_pattern_exec
+ (pat, M_MATCH_FULL_ADDRESS, Context, Context->hdrs[i])) {
+ Context->hdrs[i]->virtual = Context->vcount;
+ Context->hdrs[i]->limited = 1;
+ Context->v2r[Context->vcount] = i;
+ Context->vcount++;
+ Context->vsize += THIS_BODY->length + THIS_BODY->offset -
+ THIS_BODY->hdr_offset;
}
}
}
- else
- {
- for (i = 0; i < Context->vcount; i++)
- {
- if (mutt_pattern_exec (pat, M_MATCH_FULL_ADDRESS, Context, Context->hdrs[Context->v2r[i]]))
- {
- switch (op)
- {
- case M_UNDELETE:
- mutt_set_flag (Context, Context->hdrs[Context->v2r[i]], M_PURGED,
- 0);
- case M_DELETE:
- mutt_set_flag (Context, Context->hdrs[Context->v2r[i]], M_DELETE,
- (op == M_DELETE));
- break;
- case M_TAG:
- case M_UNTAG:
- mutt_set_flag (Context, Context->hdrs[Context->v2r[i]], M_TAG,
- (op == M_TAG));
- break;
- }
+ else {
+ for (i = 0; i < Context->vcount; i++) {
+ if (mutt_pattern_exec
+ (pat, M_MATCH_FULL_ADDRESS, Context,
+ Context->hdrs[Context->v2r[i]])) {
+ switch (op) {
+ case M_UNDELETE:
+ mutt_set_flag (Context, Context->hdrs[Context->v2r[i]], M_PURGED,
+ 0);
+ case M_DELETE:
+ mutt_set_flag (Context, Context->hdrs[Context->v2r[i]], M_DELETE,
+ (op == M_DELETE));
+ break;
+ case M_TAG:
+ case M_UNTAG:
+ mutt_set_flag (Context, Context->hdrs[Context->v2r[i]], M_TAG,
+ (op == M_TAG));
+ break;
+ }
}
}
}
mutt_clear_error ();
- if (op == M_LIMIT)
- {
- FREE (&Context->pattern);
- if (Context->limit_pattern)
- mutt_pattern_free (&Context->limit_pattern);
- if (!Context->vcount)
- {
+ if (op == M_LIMIT) {
+ /* drop previous limit pattern */
+ p_delete(&Context->pattern);
+ if (Context->limit_pattern)
+ pattern_list_wipe(&Context->limit_pattern);
+ if (Context->msgcount && !Context->vcount) {
mutt_error _("No messages matched criteria.");
-#if 0
- Context->vcount = Context->msgcount;
- /* restore full display */
- for (i = 0; i < Context->msgcount; i++)
- {
- Context->hdrs[i]->virtual = i;
- Context->v2r[i] = i;
- }
-#endif
}
- else if (mutt_strncmp (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 */
+ simple = NULL; /* don't clobber it */
Context->limit_pattern = mutt_pattern_comp (buf, M_FULL_MSG, &err);
}
}
- FREE (&simple);
- mutt_pattern_free (&pat);
+ p_delete(&simple);
+ pattern_list_wipe(&pat);
return 0;
}
BUFFER err;
int incr;
HEADER *h;
-
- if (op != OP_SEARCH_NEXT && op != OP_SEARCH_OPPOSITE)
- {
- strfcpy (buf, LastSearch, sizeof (buf));
+
+ if (op != OP_SEARCH_NEXT && op != OP_SEARCH_OPPOSITE) {
+ m_strcpy(buf, sizeof(buf), LastSearch);
if (mutt_get_field ((op == OP_SEARCH) ? _("Search for: ") :
- _("Reverse search for: "), buf, sizeof (buf),
- M_CLEAR | M_PATTERN) != 0 || !buf[0])
+ _("Reverse search for: "), buf, sizeof (buf),
+ M_CLEAR | M_PATTERN) != 0 || !buf[0])
return (-1);
if (op == OP_SEARCH)
/* compare the *expanded* version of the search pattern in case
$simple_search has changed while we were searching */
- strfcpy (temp, buf, sizeof (temp));
+ m_strcpy(temp, sizeof(temp), buf);
mutt_check_simple (temp, sizeof (temp), NONULL (SimpleSearch));
- if (!SearchPattern || mutt_strcmp (temp, LastSearchExpn))
- {
+ if (!SearchPattern || m_strcmp(temp, LastSearchExpn)) {
set_option (OPTSEARCHINVALID);
- strfcpy (LastSearch, buf, sizeof (LastSearch));
+ m_strcpy(LastSearch, sizeof(LastSearch), buf);
mutt_message _("Compiling search pattern...");
- mutt_pattern_free (&SearchPattern);
+
+ pattern_list_wipe(&SearchPattern);
err.data = error;
err.dsize = sizeof (error);
- if ((SearchPattern = mutt_pattern_comp (temp, M_FULL_MSG, &err)) == NULL)
- {
- mutt_error ("%s", error);
- return (-1);
+ if ((SearchPattern =
+ mutt_pattern_comp (temp, M_FULL_MSG, &err)) == NULL) {
+ mutt_error ("%s", error);
+ return (-1);
}
mutt_clear_error ();
}
}
- else if (!SearchPattern)
- {
+ else if (!SearchPattern) {
mutt_error _("No search pattern.");
+
return (-1);
}
- if (option (OPTSEARCHINVALID))
- {
+ if (option (OPTSEARCHINVALID)) {
for (i = 0; i < Context->msgcount; i++)
Context->hdrs[i]->searched = 0;
+ if (Context->magic == M_IMAP && imap_search (Context, SearchPattern) < 0)
+ return -1;
unset_option (OPTSEARCHINVALID);
}
if (op == OP_SEARCH_OPPOSITE)
incr = -incr;
- for (i = cur + incr, j = 0 ; j != Context->vcount; j++)
- {
- if (i > Context->vcount - 1)
- {
+ for (i = cur + incr, j = 0; j != Context->vcount; j++) {
+ if (i > Context->vcount - 1) {
i = 0;
if (option (OPTWRAPSEARCH))
- mutt_message _("Search wrapped to top.");
- else
- {
+ mutt_message (_("Search wrapped to top."));
+
+ else {
mutt_message _("Search hit bottom without finding match");
- return (-1);
+
+ return (-1);
}
}
- else if (i < 0)
- {
+ else if (i < 0) {
i = Context->vcount - 1;
if (option (OPTWRAPSEARCH))
- mutt_message _("Search wrapped to bottom.");
- else
- {
+ mutt_message (_("Search wrapped to bottom."));
+
+ else {
mutt_message _("Search hit top without finding match");
- return (-1);
+
+ return (-1);
}
}
h = Context->hdrs[Context->v2r[i]];
- if (h->searched)
- {
+ if (h->searched) {
/* if we've already evaulated this message, use the cached value */
if (h->matched)
- return i;
+ return i;
}
- else
- {
+ else {
/* remember that we've already searched this message */
h->searched = 1;
- if ((h->matched = (mutt_pattern_exec (SearchPattern, M_MATCH_FULL_ADDRESS, Context, h) > 0)))
- return i;
+ if ((h->matched =
+ (mutt_pattern_exec
+ (SearchPattern, M_MATCH_FULL_ADDRESS, Context, h) > 0)))
+ return i;
}
- if (SigInt)
- {
+ if (SigInt) {
mutt_error _("Search interrupted.");
+
SigInt = 0;
return (-1);
}
}
mutt_error _("Not found.");
+
return (-1);
}