diff -Ndurp mutt-1.5.3/Makefile.in mutt-1.5.3-hcache/Makefile.in --- mutt-1.5.3/Makefile.in 2003-03-06 12:37:43.000000000 +0100 +++ mutt-1.5.3-hcache/Makefile.in 2003-03-06 12:37:52.000000000 +0100 @@ -121,7 +121,7 @@ bin_PROGRAMS = mutt @DOTLOCK_TARGET@ @PG mutt_SOURCES = $(BUILT_SOURCES) addrbook.c alias.c attach.c base64.c browser.c buffy.c color.c commands.c complete.c compress.c compose.c copy.c curs_lib.c curs_main.c date.c edit.c enter.c flags.c init.c filter.c from.c getdomain.c handler.c hash.c hdrline.c headers.c help.c hook.c keymap.c main.c mbox.c menu.c mh.c mx.c pager.c parse.c pattern.c postpone.c query.c recvattach.c recvcmd.c rfc822.c rfc1524.c rfc2047.c rfc2231.c score.c send.c sendlib.c signal.c sort.c status.c system.c thread.c charset.c history.c lib.c muttlib.c editmsg.c utf8.c mbyte.c wcwidth.c url.c ascii.c -mutt_LDADD = @MUTT_LIB_OBJECTS@ @LIBOBJS@ $(LIBIMAP) $(MUTTLIBS) $(INTLLIBS) $(LIBICONV) +mutt_LDADD = @MUTT_LIB_OBJECTS@ @LIBOBJS@ $(LIBIMAP) $(MUTTLIBS) $(INTLLIBS) $(LIBICONV) -ldb mutt_DEPENDENCIES = @MUTT_LIB_OBJECTS@ @LIBOBJS@ $(LIBIMAPDEPS) $(INTLDEPS) @@ -195,7 +195,7 @@ keymap.o main.o mbox.o menu.o mh.o mx.o postpone.o query.o recvattach.o recvcmd.o rfc822.o rfc1524.o rfc2047.o \ rfc2231.o score.o send.o sendlib.o signal.o sort.o status.o system.o \ thread.o charset.o history.o lib.o muttlib.o editmsg.o utf8.o mbyte.o \ -wcwidth.o url.o ascii.o +wcwidth.o url.o ascii.o cache.o mutt_LDFLAGS = SCRIPTS = $(bin_SCRIPTS) diff -Ndurp mutt-1.5.3/cache.c mutt-1.5.3-hcache/cache.c --- mutt-1.5.3/cache.c 1970-01-01 01:00:00.000000000 +0100 +++ mutt-1.5.3-hcache/cache.c 2003-01-20 10:12:54.000000000 +0100 @@ -0,0 +1,497 @@ +/* + * Copyright (C) 2002 Michael R. Elkins + * + * 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 /* HAVE_CONFIG_H */ + +#if USE_CACHE + +#if HAVE_LIBDB +#define DB_DBM_HSEARCH 1 +#include +#else +#include +#endif + +#include +#include "mutt.h" +#include "mime.h" +#include "mx.h" + +/* Wrapper around the DBM struct. This is necessary to acheive proper + * exclusive locking using the mx_lock_file() + */ +typedef struct +{ + DBM *db; + char *path; + int fd; +} +hcache_t; + +/****************************************************************************** + * NDBM Wrapper Functions + * + * The following section contains code which is a thin layer over their + * counterparts in NDBM. NDBM lacks field access inside of each database + * record, and to avoid using a full blown database I define a structure + * for each record that allows arbitrary number of fields. + * + * Each record consists of length byte(s) indicating how much data is in the + * field, followed by the field bytes. If the length of the field is less + * than 128 bytes, only a single length byte is required. Otherwise, the + * length is broken into 7-bit pieces and the high order bit of each piece + * except for the last is set. NOTE: currently there is only support for up + * to 14-bits of length, which should be more than sufficient for our + * purposes with header caching in Mutt. + *****************************************************************************/ + +/* Stores a record under the index defined by `key' consisting of the + * elements in `field' which is `nfields' long. `flag' is as with + * dbm_store and is passed directly. + */ +static int +field_store (DBM * db, datum key, datum * field, size_t nfields, int flag) +{ + datum d; + unsigned char *dptr; + size_t i; + int r; + + d.dsize = 0; + for (i = 0; i < nfields; i++) + d.dsize += ((field[i].dsize > 127) ? 2 : 1) + field[i].dsize; + + d.dptr = safe_malloc (d.dsize); + for (i = 0, dptr = (unsigned char *) d.dptr; i < nfields; i++) + { + if (field[i].dsize > 127) + *dptr++ = 0x80 | (field[i].dsize >> 7); + *dptr++ = field[i].dsize & 0x7f; + memcpy (dptr, field[i].dptr, field[i].dsize); + dptr += field[i].dsize; + } + + r = dbm_store (db, key, d, flag); + + FREE (&d.dptr); + + return r; +} + +/* Retreives the record associated with the index `key'. The returned + * record is an array consisting of `nfields' elements. `nfields' is + * set to the number of elements, or 0 upon failure--which could be due to + * a malformed cache entry, or missing entry. + */ +static datum * +field_fetch (DBM * db, datum key, size_t * nfields) +{ + datum d = dbm_fetch (db, key); + datum *field = 0; + size_t ofst = 0; + size_t n; + + *nfields = 0; + + while (ofst < d.dsize) + { + field = realloc (field, sizeof (datum) * (1 + *nfields)); + n = (unsigned char) d.dptr[ofst++]; + if (n > 127) + { + n &= ~0x80; + n <<= 7; + n |= (unsigned char) d.dptr[ofst++]; + } + field[*nfields].dsize = n; + if (field[*nfields].dsize) + { + field[*nfields].dptr = safe_malloc (field[*nfields].dsize); + memcpy (field[*nfields].dptr, d.dptr + ofst, + field[*nfields].dsize); + ofst += field[*nfields].dsize; + } + else + field[*nfields].dptr = NULL; + ++*nfields; + } + + /* `d' contains static storage? */ + + return field; +} + +/* Helper function to free() all the elements of a record. It expects `d' + * to be an array of `nfields' elements. + */ +static void +field_free (datum * d, size_t nfields) +{ + size_t i; + + for (i = 0; i < nfields; i++) + FREE (&d[i].dptr); + FREE (&d); +} + +/****************************************************************************** + * Header Caching Code + *****************************************************************************/ + +/* These are the fields of each record, in order. Note that you can add + * fields before hc_max, but if you change the order of any of these + * constants, existing cache databases will be invalide. Furthurmore, there + * is no support for detection of older database so don't mess with this + * unless it is absolutely critical. + * + * Note: anything checked in mbox_strict_cmp_headers() should be cached here + * or else the check will fail for valid matches. + */ +enum +{ + hc_from, + hc_to, + hc_cc, + hc_subj, + hc_msgid, + hc_date, + hc_replyto, + hc_refs, + hc_inreplyto, + hc_contenttype, + hc_encoding, /*content-transfer-encoding */ + hc_contentlength, + hc_contentoffset, + hc_lines, + hc_returnpath, /* return-path: value */ + hc_sender, /* sender: value */ + hc_received, /* received time */ + hc_max /* MUST BE LAST--Indicates the number of fields per record */ +}; + +void * +mutt_hcache_open (const char *path) +{ + char dbfile[_POSIX_PATH_MAX]; + hcache_t *h = safe_calloc (1, sizeof (hcache_t)); + + /* Lock the db file in a NFS-safe manner. Since we can't guarantee + * what the real name of the db file will be, create a temp file + * in place of it. Too bad we can't juse use a lock file by itself + * as a mutex without the requirement for a source file. + */ + snprintf (dbfile, sizeof (dbfile), "%s-lock-hack", path); + h->fd = open (dbfile, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR); + if (h->fd < 0) + { + FREE (&h); + return NULL; + } + if (mx_lock_file (dbfile, h->fd, 1, 1, 5)) + { + close (h->fd); + FREE (&h); + return NULL; + } + + h->db = dbm_open (path, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); + if (h->db == NULL) + { + mx_unlock_file (path, h->fd, 1); + /* XXX perhaps we should use mx_unlink_empty() instead? */ + unlink (path); + close (h->fd); + FREE (&h); + return NULL; + } + + h->path = safe_strdup (dbfile); + + return h; +} + +void +mutt_hcache_close (void *ref) +{ + hcache_t *h = (hcache_t *) ref; + + if (h) + { + dbm_close (h->db); + /* XXX I think there is no problem with unlinking here. There + * could be other writers waiting that still have the file open, + * and if another writer comes along and creates a new file they + * will still try to create a lockfile with the same name. This + * is why we don't bother with O_EXCL in the open() call. + */ + mx_unlock_file (h->path, h->fd, 1); + unlink (h->path); + close (h->fd); + FREE (&h->path); + FREE (&h); + } +} + +static ADDRESS * +field_to_address (datum * d) +{ + ADDRESS *addr = 0; + + if (d->dptr) + addr = rfc822_parse_adrlist (NULL, d->dptr); + return addr; +} + +static char * +field_to_string (datum * d) +{ + char *s = d->dptr; + + d->dptr = 0; + return s; +} + +static LIST * +field_to_list (datum * d) +{ + LIST *head = NULL; + LIST **cur = &head; + char *p = d->dptr; + char *q; + + while (p) + { + q = strchr (p, ' '); + if (q) + *q++ = 0; + *cur = safe_calloc (1, sizeof (LIST)); + (*cur)->data = safe_strdup (p); + cur = &(*cur)->next; + p = q; + } + + return head; +} + +#define field_to_int(d) ((d).dptr ? atol((d).dptr) : 0) + +HEADER * +mutt_hcache_fetch (void *ref, const char *key, size_t klen) +{ + datum *rec; + size_t nrec; + datum d; + HEADER *h; + hcache_t *cache = (hcache_t *) ref; + + d.dptr = (char *) key; + d.dsize = klen; + + rec = field_fetch (cache->db, d, &nrec); + if (nrec < hc_max) + { + if (rec) + field_free (rec, nrec); + return NULL; /* missing/invalid cache entry */ + } + + h = mutt_new_header (); + h->env = mutt_new_envelope (); + h->content = mutt_new_body (); + + /* address fields */ + h->env->from = field_to_address (&rec[hc_from]); + h->env->to = field_to_address (&rec[hc_to]); + h->env->cc = field_to_address (&rec[hc_cc]); + h->env->reply_to = field_to_address (&rec[hc_replyto]); + h->env->return_path = field_to_address (&rec[hc_returnpath]); + h->env->sender = field_to_address (&rec[hc_sender]); + + /* string fields */ + h->env->subject = field_to_string (&rec[hc_subj]); + if (h->env->subject) + { + regmatch_t pmatch[1]; + + if (regexec (ReplyRegexp.rx, h->env->subject, 1, pmatch, 0) == 0) + h->env->real_subj = h->env->subject + pmatch[0].rm_eo; + else + h->env->real_subj = h->env->subject; + } + h->env->message_id = field_to_string (&rec[hc_msgid]); + h->env->date = field_to_string (&rec[hc_date]); + + /* list fields */ + h->env->references = field_to_list (&rec[hc_refs]); + h->env->in_reply_to = field_to_list (&rec[hc_inreplyto]); + + /* integer fields */ + h->content->length = field_to_int (rec[hc_contentlength]); + h->content->offset = field_to_int (rec[hc_contentoffset]); + h->lines = field_to_int (rec[hc_lines]); + h->received = field_to_int (rec[hc_received]); + + h->content->encoding = mutt_check_encoding (rec[hc_encoding].dptr); + + /* special case - content-type */ + if (rec[hc_contenttype].dptr) + mutt_parse_content_type (rec[hc_contenttype].dptr, h->content); + + if (h->env->date) + h->date_sent = mutt_parse_date (h->env->date, h); + + field_free (rec, nrec); + + return h; +} + +/* stores an integer in ascii representation to avoid interoperability + * problems among different architectures. + */ +static void +int_to_field (datum * d, size_t n) +{ + char tmp[16]; + + snprintf (tmp, sizeof (tmp), "%u", n); + d->dptr = safe_strdup (tmp); + d->dsize = strlen (tmp) + 1; +} + +static void +string_to_field (datum * d, const char *s) +{ + if (s) + { + d->dptr = safe_strdup (s); + d->dsize = strlen (s) + 1; + } +} + +static void +address_to_field (datum * d, ADDRESS * a) +{ + char tmp[2048]; + + if (a) + { + tmp[0] = 0; + rfc822_write_address (tmp, sizeof (tmp), a); + d->dptr = safe_strdup (tmp); + d->dsize = strlen (tmp) + 1; + } +} + +static void +list_to_field (datum * d, LIST * l) +{ + if (l) + { + while (l) + { + size_t i = strlen (l->data) + 1; + d->dptr = realloc (d->dptr, d->dsize + i + 1); + sprintf (d->dptr + d->dsize, "%s%s", d->dsize > 0 ? " " : "", + l->data); + d->dsize += i; + l = l->next; + } + d->dsize++; + } +} + +int +mutt_hcache_store (void *ref, const char *key, size_t klen, HEADER * h) +{ + datum d; + int r; + size_t i; + datum rec[hc_max]; + char buf[1024]; /* icky */ + hcache_t *cache = (hcache_t *) ref; + + memset (rec, 0, sizeof (rec)); + + /* address fields */ + address_to_field (&rec[hc_from], h->env->from); + address_to_field (&rec[hc_to], h->env->to); + address_to_field (&rec[hc_cc], h->env->cc); + address_to_field (&rec[hc_replyto], h->env->reply_to); + address_to_field (&rec[hc_returnpath], h->env->return_path); + address_to_field (&rec[hc_sender], h->env->sender); + + /* string fields */ + string_to_field (&rec[hc_subj], h->env->subject); + string_to_field (&rec[hc_msgid], h->env->message_id); + string_to_field (&rec[hc_date], h->env->date); + string_to_field (&rec[hc_encoding], ENCODING (h->content->encoding)); + + /* list fields */ + list_to_field (&rec[hc_refs], h->env->references); + list_to_field (&rec[hc_inreplyto], h->env->in_reply_to); + + /* integer fields */ + int_to_field (&rec[hc_contentlength], h->content->length); + int_to_field (&rec[hc_contentoffset], h->content->offset); + int_to_field (&rec[hc_lines], h->lines); + int_to_field (&rec[hc_received], h->received); + + /* special case for content-type */ + { + PARAMETER *p; + + /* XXX TYPEOTHER is not handled here */ + snprintf (buf, sizeof (buf), "%s/%s", TYPE (h->content), + h->content->subtype); + for (p = h->content->parameter; p; p = p->next) + { + snprintf (buf + strlen (buf), sizeof (buf) - strlen (buf), + "; %s=\"%s\"", p->attribute, p->value); + } + + rec[hc_contenttype].dptr = safe_strdup (buf); + rec[hc_contenttype].dsize = strlen (buf) + 1; + } + + d.dptr = (char *) key; + d.dsize = klen; + r = field_store (cache->db, d, rec, hc_max, DBM_REPLACE); + + for (i = 0; i < hc_max; i++) + if (rec[i].dptr) + FREE (&rec[i].dptr); + + return r; +} + +int +mutt_hcache_delete (void *ref, const char *key, size_t klen) +{ + datum d; + hcache_t *cache = (hcache_t *) ref; + + d.dptr = (char *) key; + d.dsize = klen; + + return dbm_delete (cache->db, d); +} +#endif /* USE_CACHE */ + +/* vim: set sw=2: */ diff -Ndurp mutt-1.5.3/main.c mutt-1.5.3-hcache/main.c --- mutt-1.5.3/main.c 2003-01-20 10:14:47.000000000 +0100 +++ mutt-1.5.3-hcache/main.c 2003-01-20 10:12:54.000000000 +0100 @@ -412,6 +412,12 @@ static void show_version (void) "-HAVE_GETADDRINFO " #endif +#if USE_CACHE + "+USE_CACHE " +#else + "-USE_CACHE " +#endif + ); #ifdef ISPELL diff -Ndurp mutt-1.5.3/Makefile.am mutt-1.5.3-hcache/Makefile.am --- mutt-1.5.3/Makefile.am 2002-12-09 20:06:22.000000000 +0100 +++ mutt-1.5.3-hcache/Makefile.am 2003-01-20 10:12:54.000000000 +0100 @@ -60,7 +60,7 @@ CPPFLAGS=@CPPFLAGS@ -I$(includedir) EXTRA_mutt_SOURCES = account.c md5c.c mutt_sasl.c mutt_socket.c mutt_ssl.c \ mutt_tunnel.c pop.c pop_auth.c pop_lib.c crypt.c smime.c pgp.c pgpinvoke.c pgpkey.c \ pgplib.c sha1.c pgpmicalg.c gnupgparse.c resize.c dotlock.c remailer.c \ - browser.h mbyte.h remailer.h url.h mutt_ssl_nss.c pgppacket.c + browser.h mbyte.h remailer.h url.h mutt_ssl_nss.c pgppacket.c cache.c EXTRA_DIST = COPYRIGHT GPL OPS OPS.PGP OPS.CRYPT OPS.SMIME TODO \ configure acconfig.h account.h \ diff -Ndurp mutt-1.5.3/mbox.c mutt-1.5.3-hcache/mbox.c --- mutt-1.5.3/mbox.c 2003-01-20 10:14:46.000000000 +0100 +++ mutt-1.5.3-hcache/mbox.c 2003-01-20 10:12:54.000000000 +0100 @@ -539,7 +539,6 @@ int mbox_strict_cmp_headers (const HEADE h1->zhours != h2->zhours || h1->zminutes != h2->zminutes || h1->zoccident != h2->zoccident || - h1->mime != h2->mime || !strict_cmp_envelopes (h1->env, h2->env) || !strict_cmp_bodies (h1->content, h2->content)) return (0); diff -Ndurp mutt-1.5.3/mh.c mutt-1.5.3-hcache/mh.c --- mutt-1.5.3/mh.c 2003-01-20 10:14:47.000000000 +0100 +++ mutt-1.5.3-hcache/mh.c 2003-01-20 10:13:30.000000000 +0100 @@ -46,7 +46,6 @@ struct maildir { HEADER *h; char *canon_fname; - unsigned header_parsed:1; struct maildir *next; }; @@ -573,8 +572,7 @@ static void maildir_update_mtime (CONTEX * Actually parse a maildir message. This may also be used to fill * out a fake header structure generated by lazy maildir parsing. */ -static HEADER *maildir_parse_message (int magic, const char *fname, - int is_old, HEADER * _h) +static HEADER *maildir_parse_message (int magic, const char *fname, HEADER * _h) { FILE *f; HEADER *h = _h; @@ -597,16 +595,6 @@ static HEADER *maildir_parse_message (in h->index = -1; - if (magic == M_MAILDIR) - { - /* - * maildir stores its flags in the filename, so ignore the - * flags in the header of the message - */ - - h->old = is_old; - maildir_parse_flags (h, fname); - } return h; } return NULL; @@ -639,7 +627,7 @@ static int maildir_parse_entry (CONTEXT snprintf (buf, sizeof (buf), "%s/%s", ctx->path, fname); if (ctx->magic == M_MH) - h = maildir_parse_message (ctx->magic, buf, is_old, NULL); + h = maildir_parse_message (ctx->magic, buf, NULL); else { h = mutt_new_header (); @@ -666,7 +654,6 @@ static int maildir_parse_entry (CONTEXT entry = safe_calloc (sizeof (struct maildir), 1); entry->h = h; - entry->header_parsed = (ctx->magic == M_MH); **last = entry; *last = &entry->next; @@ -780,6 +767,18 @@ static int maildir_move_to_context (CONT return r; } +#if USE_CACHE +/* + * Calculates the length of the filename without the flags. This is the + * part of the filename we use as the key for the header cache lookup. + */ + +size_t maildir_cache_keylen (const char *fn) +{ + const char * p = strchr (fn, ':'); + return p ? (size_t) (p - fn) : strlen (fn); +} +#endif /* * This function does the second parsing pass for a maildir-style @@ -790,20 +789,73 @@ void maildir_delayed_parsing (CONTEXT * { struct maildir *p; char fn[_POSIX_PATH_MAX]; +#if USE_CACHE + void *db; + HEADER *h = NULL; + + snprintf (fn, sizeof (fn), "%s/hcache", ctx->path); + db = mutt_hcache_open (fn); +#endif for (p = md; p; p = p->next) - if (p && p->h && !p->header_parsed) + if (p && p->h) { snprintf (fn, sizeof (fn), "%s/%s", ctx->path, p->h->path); - if (maildir_parse_message (ctx->magic, fn, p->h->old, p->h)) - p->header_parsed = 1; + +#if USE_CACHE + /* See if we get a cache hit for this message */ + if (db) + { + /* + * The key for the lookup is the basename of the file sans flags. + * We don't include the subdirectory so that the cache line is still + * valid when the message is moved from new to cur + */ + + h = mutt_hcache_fetch (db, p->h->path + 4, + maildir_cache_keylen (p->h->path + 4)); + if (h) + { + /* + * Replace the fake HEADER struct with the full value restored + * from the cache. + */ + h->old = p->h->old; + h->path = safe_strdup (p->h->path); + maildir_parse_flags (h, fn); + mutt_free_header (&p->h); + p->h = h; + continue; + } + } +#endif + + if (maildir_parse_message (ctx->magic, fn, p->h)) + { + /* + * maildir stores its flags in the filename, so ignore the + * flags in the header of the message + */ + + maildir_parse_flags (p->h, fn); + +#if USE_CACHE + /* + * This header is not cached. Attempt to do it now if the DB + * is open. + */ + if (db) + mutt_hcache_store (db, p->h->path + 4, maildir_cache_keylen (p->h->path + 4), p->h); +#endif + } else mutt_free_header (&p->h); } +#if USE_CACHE + mutt_hcache_close (db); +#endif } - - /* Read a MH/maildir style mailbox. * * args: @@ -835,8 +887,7 @@ int mh_read_dir (CONTEXT * ctx, const ch mh_update_maildir (md, &mhs); mhs_free_sequences (&mhs); } - - if (ctx->magic == M_MAILDIR) + else if (ctx->magic == M_MAILDIR) maildir_delayed_parsing (ctx, md); maildir_move_to_context (ctx, &md); @@ -1294,15 +1345,23 @@ int mh_sync_mailbox (CONTEXT * ctx, int { char path[_POSIX_PATH_MAX], tmp[_POSIX_PATH_MAX]; int i, j; +#if USE_CACHE + void *db; +#endif if (ctx->magic == M_MH) i = mh_check_mailbox (ctx, index_hint); else i = maildir_check_mailbox (ctx, index_hint); - + if (i != 0) return i; +#if USE_CACHE + snprintf (tmp, sizeof (tmp), "%s/hcache", ctx->path); + db = mutt_hcache_open (tmp); +#endif + for (i = 0; i < ctx->msgcount; i++) { if (ctx->hdrs[i]->deleted @@ -1311,7 +1370,14 @@ int mh_sync_mailbox (CONTEXT * ctx, int snprintf (path, sizeof (path), "%s/%s", ctx->path, ctx->hdrs[i]->path); if (ctx->magic == M_MAILDIR || (option (OPTMHPURGE) && ctx->magic == M_MH)) + { unlink (path); +#if USE_CACHE + if (db) + mutt_hcache_delete (db, ctx->hdrs[i]->path + 4, + maildir_cache_keylen (ctx->hdrs[i]->path + 4)); +#endif + } else if (ctx->magic == M_MH) { /* MH just moves files out of the way when you delete them */ @@ -1333,15 +1399,18 @@ int mh_sync_mailbox (CONTEXT * ctx, int if (ctx->magic == M_MAILDIR) { if (maildir_sync_message (ctx, i) == -1) - return -1; + goto err; } else { if (mh_sync_message (ctx, i) == -1) - return -1; + goto err; } } } +#if USE_CACHE + mutt_hcache_close (db); +#endif if (ctx->magic == M_MH) mh_update_sequences (ctx); @@ -1363,6 +1432,12 @@ int mh_sync_mailbox (CONTEXT * ctx, int } return 0; + +err: +#if USE_CACHE + mutt_hcache_close (db); +#endif + return -1; } static char *maildir_canon_filename (char *dest, const char *src, size_t l) @@ -1461,6 +1536,9 @@ int maildir_check_mailbox (CONTEXT * ctx int i; HASH *fnames; /* hash table for quickly looking up the base filename for a maildir message */ +#if USE_CACHE + void *db; +#endif /* XXX seems like this check belongs in mx_check_mailbox() * rather than here. @@ -1512,6 +1590,11 @@ int maildir_check_mailbox (CONTEXT * ctx hash_insert (fnames, p->canon_fname, p, 0); } +#if USE_CACHE + snprintf (buf, sizeof (buf), "%s/hcache", ctx->path); + db = mutt_hcache_open (buf); +#endif + /* check for modifications and adjust flags */ for (i = 0; i < ctx->msgcount; i++) { @@ -1554,6 +1637,19 @@ int maildir_check_mailbox (CONTEXT * ctx * subdirectory it used to reside in. */ occult = 1; +#if USE_CACHE + /* + * Since this message doesn't appear to be around any longer, + * go ahead and purge its info from the cache. This won't + * catch any messages that are removed from the mailbox while + * Mutt is running, however. A separate utility will have to + * be written, perhaps run as a cron job periodically to + * compress the cache by removing stale entries. + */ + if (db) + mutt_hcache_delete (db, ctx->hdrs[i]->path + 4, + maildir_cache_keylen (ctx->hdrs[i]->path + 4)); +#endif } else { @@ -1565,13 +1661,17 @@ int maildir_check_mailbox (CONTEXT * ctx } } +#if USE_CACHE + mutt_hcache_close (db); +#endif + /* destroy the file name hash */ hash_destroy (&fnames, NULL); /* If we didn't just get new mail, update the tables. */ if (occult) maildir_update_tables (ctx, index_hint); - + /* do any delayed parsing we need to do. */ maildir_delayed_parsing (ctx, md); @@ -1842,3 +1942,5 @@ int mh_check_empty (const char *path) return r; } + +/* vim: set sw=2: */ diff -Ndurp mutt-1.5.3/mutt.h mutt-1.5.3-hcache/mutt.h --- mutt-1.5.3/mutt.h 2003-01-20 10:14:47.000000000 +0100 +++ mutt-1.5.3-hcache/mutt.h 2003-01-20 10:13:51.000000000 +0100 @@ -657,7 +657,7 @@ typedef struct header see: crypt.h pgplib.h, smime.h */ #endif - unsigned int mime : 1; /* has a Mime-Version header? */ + unsigned int xxx : 1; /* has a Mime-Version header? */ unsigned int flagged : 1; /* marked important? */ unsigned int tagged : 1; unsigned int deleted : 1; diff -Ndurp mutt-1.5.3/parse.c mutt-1.5.3-hcache/parse.c --- mutt-1.5.3/parse.c 2002-12-11 12:20:05.000000000 +0100 +++ mutt-1.5.3-hcache/parse.c 2003-01-20 10:12:56.000000000 +0100 @@ -1080,9 +1080,7 @@ int mutt_parse_rfc822_line (ENVELOPE *e, case 'm': if (!ascii_strcasecmp (line + 1, "ime-version")) { - if (hdr) - hdr->mime = 1; - matched = 1; + matched = 1; } else if (!ascii_strcasecmp (line + 1, "essage-id")) { diff -Ndurp mutt-1.5.3/PATCHES mutt-1.5.3-hcache/PATCHES --- mutt-1.5.3/PATCHES 2003-01-20 10:14:47.000000000 +0100 +++ mutt-1.5.3-hcache/PATCHES 2003-01-20 10:12:54.000000000 +0100 @@ -1,3 +1,4 @@ +patch-1.5.3.Z.hcache.8 patch-1.5.3.Md.gpg_status_fd patch-1.4.Md.gpg-agent patch-1.5.1.cd.edit_threads.9.2 diff -Ndurp mutt-1.5.3/protos.h mutt-1.5.3-hcache/protos.h --- mutt-1.5.3/protos.h 2003-01-20 10:14:47.000000000 +0100 +++ mutt-1.5.3-hcache/protos.h 2003-01-20 10:14:10.000000000 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (C) 1996-2000 Michael R. Elkins + * Copyright (C) 1996-2002 Michael R. Elkins * * 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 @@ -98,6 +98,7 @@ LIST *mutt_make_references(ENVELOPE *e); ENVELOPE *mutt_read_rfc822_header (FILE *, HEADER *, short, short); HEADER *mutt_dup_header (HEADER *); +HEADER *mutt_hcache_fetch (void *, const char *, size_t); ATTACHPTR **mutt_gen_attach_list (BODY *, int, ATTACHPTR **, short *, short *, int, int); @@ -185,6 +186,8 @@ void mutt_free_envelope (ENVELOPE **); void mutt_free_header (HEADER **); void mutt_free_parameter (PARAMETER **); void mutt_generate_header (char *, size_t, HEADER *, int); +void mutt_hcache_close (void *); +void *mutt_hcache_open (const char *); void mutt_help (int); void mutt_draw_tree (CONTEXT *); void mutt_check_lookup_list (BODY *, char *, int); @@ -279,6 +282,8 @@ int mutt_get_hook_type (const char *); int mutt_get_password (char *, char *, size_t); int mutt_get_postponed (CONTEXT *, HEADER *, HEADER **, char *, size_t); int mutt_get_tmp_attachment (BODY *); +int mutt_hcache_delete (void *, const char *, size_t); +int mutt_hcache_store (void *, const char *, size_t, HEADER *); int mutt_index_menu (void); int mutt_invoke_sendmail (ADDRESS *, ADDRESS *, ADDRESS *, ADDRESS *, const char *, int); int mutt_is_autoview (BODY *, const char *); diff -Ndurp mutt-1.5.3/config.h.in mutt-1.5.3-hcache/config.h.in --- mutt-1.5.3/config.h.in 2003-03-06 14:37:38.000000000 +0100 +++ mutt-1.5.3-hcache/config.h.in 2003-03-06 14:37:49.000000000 +0100 @@ -533,3 +533,6 @@ #define USE_COMPRESSED #undef ISPELL #define ISPELL "ispell" + +#define USE_CACHE 1 +#define HAVE_LIBDB 1