[comp.mail.mh] XMH cache

Raeburn@MIT.EDU (Ken Raeburn) (08/23/90)

One of the things I like about xmh is the .xmhcache file it
maintains.  It's helpful when I want to find a message I know is
in a large folder -- I don't have to wait for "scan" to read
through hundreds of files across networked file systems.  (And
with the particular file system used for my home directory, I
believe fetching a new file for reading is expensive -- the
whole file has to be brought over before reading starts.  Lots
of accesses to the same file are much more efficient, so the
cache file looks even better.)

However, it's the only program I know of that maintains that
cache.  Running any other MH program that alters the folder
directory causes xmh to rescan the entire directory to rebuild
that file.  And xmh is, well, an X program, which means that it
requires an X display and a lot of memory, neither of which are
necessarily always available.

Does anyone have patches (hacks, suggestions) that would allow
for maintenance of such a cache?  Does anyone else think it
would be a good idea for an addition to MH proper, as an extra
option (preferably runtime-selectable, I'd say)?

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
++ Ken Raeburn          ++      Raeburn@MIT.EDU         ++
++ systems programmer   ++      MIT Project Athena      ++
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

jdpeek@RODAN.ACS.SYR.EDU (Jerry Peek) (08/23/90)

> One of the things I like about xmh is the .xmhcache file it
> maintains...  Running any other MH program that alters the folder
> directory causes xmh to rescan the entire directory to rebuild
> that file...
> 
> Does anyone have patches (hacks, suggestions) that would allow
> for maintenance of such a cache?

The .xmhcache file basically just holds the output of 'scan -width 100'
for the folder.  (The width comes from the TocWidth resource entry.)
A simple-minded fix would be to run a program that compared the
last-modification time of the .xmhcache file in a folder to the messages
in the folder.  If any message is newer than the .xmhcache file, the cache
needs rebuilding.  I'm not sure I understand your filesystem and network
setup, but I'd bet you could do the rebuilding on the host where the
folder lives... then copy the new .xmhcache file to your local host.

If xmh was running at the time, though, it wouldn't know that the .xmhcache
file had been updated -- so, the Table of Contents display wouldn't be
updated.  This fix would probably only work when you're *not* running xmh.

> Does anyone else think it
> would be a good idea for an addition to MH proper, as an extra
> option (preferably runtime-selectable, I'd say)?

No.  Because MH is a bunch of separate little programs, it's easy to use
in shell programming.  I think a short shell script that runs 'find -newer'
to compare the messages, then does the 'scan', should just about do it.

--Jerry Peek; Syracuse University Academic Computing Services; Syracuse, NY
  jdpeek@rodan.acs.syr.edu, JDPEEK@SUNRISE.BITNET        +1 315 443-3995

raeburn@ATHENA.MIT.EDU (08/24/90)

> Date: Thu, 23 Aug 90 06:28:44 -0500
> From: "Jerry Peek" <jdpeek@rodan.acs.syr.edu>

> The .xmhcache file basically just holds the output of 'scan -width 100'
> for the folder.  (The width comes from the TocWidth resource entry.)
> A simple-minded fix would be to run a program that compared the
> last-modification time of the .xmhcache file in a folder to the messages
> in the folder.  If any message is newer than the .xmhcache file, the cache
> needs rebuilding.  I'm not sure I understand your filesystem and network
> setup, but I'd bet you could do the rebuilding on the host where the
> folder lives... then copy the new .xmhcache file to your local host.

Too simple.  That won't catch messages being deleted or refiled from
the folder in question.

(Regarding the file system: With AFS, files aren't stored on the
server as a normal file system, so logging in there -- even if I were
permitted to log in to our dedicated file servers -- would not be
useful.  Files are, I believe, entirely brought over the net, into a
local file cache, before you can start reading them.  It actually may
be a bit more efficient than that; it might only take 64K chunks of
the file or something, I'm not certain.  It winds up being reasonably
efficient for a lot of things, but "scan" is not among them.)

> If xmh was running at the time, though, it wouldn't know that the .xmhcache
> file had been updated -- so, the Table of Contents display wouldn't be
> updated.  This fix would probably only work when you're *not* running xmh.

Good point.  I'm not sure how this would be best dealt with, but xmh
could be coerced to re-check that file periodically and any time it
had "interesting" operations to perform.

BTW, my idea is to put a cache into MH, then modify xmh to use it, not
to make MH use the current .xmhcache.  I'd want a little more
flexibility in what information is retained.  I would also expect it
to be a compile-time option as well as run-time (when enabled), so
that sites that are actively disinterested (i.e., don't want to permit
their users to use it) wouldn't need to waste the code space.

> > Does anyone else think it
> > would be a good idea for an addition to MH proper, as an extra
> > option (preferably runtime-selectable, I'd say)?
> 
> No.  Because MH is a bunch of separate little programs, it's easy to use
> in shell programming.  I think a short shell script that runs 'find -newer'
> to compare the messages, then does the 'scan', should just about do it.

A runtime-selectable option probably would not inhibit use of these
programs in shell scripts.  If you have to do a lot of message
reshuffling, one message at a time, just disable the cache, and
rebuild it later.  Even if you didn't explicitly disable it, it'd just
make things a little slower.  For larger jobs like gnuemacs mh-rmail
does, you'd probably want to use the cache update, since you'd (I
assume) be moving (or deleting) lots of messages with a single
command.

The "find -newer" solution is, as I mentioned above, too simple.  The
best way I can think of to do it with this approach (and what I
believe xmh does) is to check the modification time of the folder, and
rebuild the entire cache if the folder is newer than the cache file.

Because the MH commands are the only place where it's really known
exactly what's happening, I think that's the only reasonable place to
put this cache management.

ctl@OCF.Berkeley.EDU (Case Larsen) (08/27/90)

In article <9008230524.AA07100@PROMETHEUS.MIT.EDU> Raeburn@MIT.EDU (Ken Raeburn) writes:

   Does anyone have patches (hacks, suggestions) that would allow
   for maintenance of such a cache?  Does anyone else think it
   would be a good idea for an addition to MH proper, as an extra
   option (preferably runtime-selectable, I'd say)?

I've made a few hacks to MH 6.7 to allow caching of both the output
of scan, and the initial directory scan of the folder which gathers
various statistics like highest message, lowest message, deleted
messages, etc.  The caching code uses ndbm to 1) speed access to any
given scan output of a particular message, and 2) make insertions and
deletions of new messages easy.

The only affected commands are 'folders', 'rmm', and 'scan'.  The only
command with a runtime selectable option is 'scan'.  Use 'scan -fast'
to cache the output and make use of cached output.  Caching on the
other two commands is automatic, and doesn't consume very much disk
space (a few Kbytes).  The limit of the scan line length is currently
80 characters.  The limit can be changed, but the longer your scan
line length is, the more space the cache file takes.  For emacs users,
if you use 'mh-e', you will want to change the 'scan' command to 
'scan -fast' in mh-e.el. 

Here is an example of the speedup you can expect.  This was on a
folder of about 1000 messages.
+ /*
+  * The 'fast' option uses ndbm to store the output of scan.  Speedup is
+  * typically around 10 to 20 times on large folders.  As you see, the
+  * first time takes a while to build the cache.  The subsequent times are
+  * much quicker.
+  *
+  * dingo uip [887] time xscan -fast > & /dev/null
+  * 18.380u 15.060s 1:02.58 53.4% 0+26k 1408+316io 1413pf+0w
+  * dingo uip [888] time xscan -fast > & /dev/null
+  * 1.120u 1.420s 0:03.58 70.9% 0+22k 53+0io 63pf+0w
+  */

--
Case Larsen
ctl@OCF.Berkeley.EDU
Open Computing Facility


-------------cut here-------cut here-------cut here--------------------
diff -cr mh-6.7/sbr/m_gmsg.c mh-6.7.new/sbr/m_gmsg.c
*** mh-6.7/sbr/m_gmsg.c	Thu Apr 12 13:29:01 1990
--- mh-6.7.new/sbr/m_gmsg.c	Tue Jul 31 10:28:01 1990
***************
*** 3,11 ****
  #include "../h/mh.h"
  #include "../h/local.h"
  #include <stdio.h>
  
- 
  #define	NINFO	(MAXFOLDER / 5)	/* PLEASE be non-trivial... */
  struct info {
      int     msgno;
      short   stats;
--- 3,13 ----
  #include "../h/mh.h"
  #include "../h/local.h"
  #include <stdio.h>
+ #include <fcntl.h>
  
  #define	NINFO	(MAXFOLDER / 5)	/* PLEASE be non-trivial... */
+ #define GMSG_CACHE ".m_gmsgcache"
+ 
  struct info {
      int     msgno;
      short   stats;
***************
*** 43,49 ****
--- 45,67 ----
      register    DIR * dd;
  #endif SYS5DIR
      struct stat st;
+     int cache;
  
+     if ((cache = open(GMSG_CACHE,O_RDONLY)) >= 0) {
+ 	int len;
+ 
+ 	read(cache,&len,sizeof(len));
+ 	mp = (struct msgs  *) malloc (len);
+ 	if (mp == NULL)
+ 	  adios (NULLCP, "unable to allocate folder storage");
+ 	read(cache,mp,len);
+ 	mp->foldpath = name;
+ 	m_getatr (mp);
+ 	close(cache);
+ 	return mp;
+     }
+     
+     /* else do scan and save */
      if ((dd = opendir (name = m_mailpath (name))) == NULL) {
  	free (name);
  	return NULL;
***************
*** 191,196 ****
--- 209,223 ----
  #endif	MTR
      for (tail = head; tail < rover; tail++)
  	mp -> msgstats[tail -> msgno] = tail -> stats;
+ 
+     if ((cache = open(GMSG_CACHE,O_RDWR | O_CREAT, 0600)) >= 0) {
+ 	int len;
+ 	len = MSIZE(mp,mp->lowoff,mp->hghoff);
+ 
+ 	write(cache,&len,sizeof(len));
+ 	write(cache,mp,len);
+ 	close(cache);
+     }
      m_getatr (mp);
  
      return mp;
diff -cr mh-6.7/sbr/m_sync.c mh-6.7.new/sbr/m_sync.c
*** mh-6.7/sbr/m_sync.c	Thu Apr 12 13:29:03 1990
--- mh-6.7.new/sbr/m_sync.c	Tue Jul 31 13:02:44 1990
***************
*** 1,4 ****
--- 1,5 ----
  /* m_sync.c - synchronize message sequences */
+ static char *RCSid="$Id: m_sync.c,v 1.2 90/07/31 10:27:25 case Exp Locker: case $";
  
  #include "../h/mh.h"
  #include <stdio.h>
***************
*** 7,13 ****
--- 8,17 ----
  #define	sigmask(s)	(1 << ((s) - 1))
  #endif	not sigmask
  
+ #include <fcntl.h>
  
+ #define GMSG_CACHE ".m_gmsgcache"
+ 
  /* decision logic
      1.  public and folder readonly: make it private
      2a. public: add it to the sequences file
***************
*** 14,19 ****
--- 18,31 ----
      2b. private: add it to the profile
   */
  
+ /*
+  *$Log:	m_sync.c,v $
+  * Revision 1.2  90/07/31  10:27:25  case
+  * Save incremental changes to msgs structure to GMSG_CACHE.
+  * 
+  * ctl:  Added logic for caching of message stats to speedup m_gmsg().
+  */
+ void m_sync_cache();
  
  void m_sync (mp)
  register struct msgs *mp;
***************
*** 32,39 ****
  #endif	BSD42
  
      if (!(mp -> msgflags & SEQMOD))
! 	return;
      mp -> msgflags &= ~SEQMOD;
  
      m_getdefs ();
      (void) sprintf (seq, "%s/%s", mp -> foldpath, mh_seq);
--- 44,52 ----
  #endif	BSD42
  
      if (!(mp -> msgflags & SEQMOD))
!       return;
      mp -> msgflags &= ~SEQMOD;
+     m_sync_cache(mp);
  
      m_getdefs ();
      (void) sprintf (seq, "%s/%s", mp -> foldpath, mh_seq);
***************
*** 59,69 ****
  	    if ((cp = m_seq (mp, mp -> msgattrs[i])) == NULL)
  		continue;
  	    if (fp == NULL) {
! 		if ((fp = fopen (seq, "w")) == NULL
! 			&& unlink (seq) != NOTOK 
! 			&& (fp = fopen (seq, "w")) == NULL) {
! 		    admonish (attr, "unable to write");
! 		    goto priv;
  		}
  #ifndef	BSD42
  		hstat = signal (SIGHUP, SIG_IGN);
--- 72,88 ----
  	    if ((cp = m_seq (mp, mp -> msgattrs[i])) == NULL)
  		continue;
  	    if (fp == NULL) {
! 		fp = fopen (seq, "w");
! 		if (fp == NULL) {
! 		    if (unlink (seq) != NOTOK ) {
! 			fp = fopen (seq, "w");
! 		    } else {
! 			fp  = fopen(mh_seq,"w");
! 		    }
! 		    if (fp == NULL) {
! 			admonish (attr, "unable to write");
! 			goto priv;
! 		    }
  		}
  #ifndef	BSD42
  		hstat = signal (SIGHUP, SIG_IGN);
***************
*** 95,98 ****
--- 114,131 ----
  	    (void) unlink (seq);
  
      mp -> msgflags = flags;
+ }
+ 
+ void m_sync_cache(mp)
+      register struct msgs* mp;
+ {
+     int cache;
+     if ((cache = open(GMSG_CACHE,O_RDWR)) >= 0) {
+ 	int len;
+ 	len = MSIZE(mp,mp->lowoff,mp->hghoff);
+ 
+ 	write(cache,&len,sizeof(len));
+ 	write(cache,mp,len);
+ 	close(cache);
+     }
  }
diff -cr mh-6.7/uip/annosbr.c mh-6.7.new/uip/annosbr.c
*** mh-6.7/uip/annosbr.c	Thu Apr 12 13:29:25 1990
--- mh-6.7.new/uip/annosbr.c	Tue Jul 31 13:38:13 1990
***************
*** 1,6 ****
  /* annosbr.c - prepend annotation to messages */
  #ifndef	lint
! static char ident[] = "@(#)$Id: annosbr.c,v 2.4 90/04/05 15:35:09 sources Exp $";
  #endif	lint
  
  #include "../h/mh.h"
--- 1,6 ----
  /* annosbr.c - prepend annotation to messages */
  #ifndef	lint
! static char ident[] = "@(#)$Id: annosbr.c,v 1.1 90/07/31 13:29:50 case Exp Locker: case $";
  #endif	lint
  
  #include "../h/mh.h"
***************
*** 9,15 ****
--- 9,18 ----
  #include <stdio.h>
  #include <sys/types.h>
  #include <sys/stat.h>
+ #include <ndbm.h>
+ #include <fcntl.h>
  
+ #define DBMDIR ".mhcache"
  
  extern int  errno;
  long lseek ();
***************
*** 121,126 ****
  	    return 1;
  	}
      }
! 
      return 0;
  }
--- 124,141 ----
  	    return 1;
  	}
      }
!     {
! 	/* annotate could change fmtsbr()'s output */
! 	int msgnum = atoi(file);
! 	DBM *scache = dbm_open(DBMDIR,O_RDWR, 0600);
! 	datum key;
! 	printf ("file: %s",file);
! 	if (scache != NULL) {
! 	    key.dptr = (char *) &msgnum;
! 	    key.dsize = sizeof(msgnum);
! 	    dbm_delete(scache,key);
! 	    dbm_close(scache);
! 	}
!     }
      return 0;
  }
diff -cr mh-6.7/uip/folder.c mh-6.7.new/uip/folder.c
*** mh-6.7/uip/folder.c	Thu Apr 12 13:29:27 1990
--- mh-6.7.new/uip/folder.c	Tue Jul 31 10:25:42 1990
***************
*** 1,6 ****
  /* folder(s).c - report on folders */
  #ifndef	lint
! static char ident[] = "@(#)$Id: folder.c,v 2.4 90/04/05 14:56:54 sources Exp $";
  #endif	lint
  
  #include "../h/mh.h"
--- 1,6 ----
  /* folder(s).c - report on folders */
  #ifndef	lint
! static char ident[] = "@(#)$Id: folder.c,v 1.1 90/07/30 16:53:36 case Exp $";
  #endif	lint
  
  #include "../h/mh.h"
diff -cr mh-6.7/uip/inc.c mh-6.7.new/uip/inc.c
*** mh-6.7/uip/inc.c	Thu Apr 12 13:29:28 1990
--- mh-6.7.new/uip/inc.c	Tue Jul 31 14:16:30 1990
***************
*** 1,6 ****
  /* inc.c - incorporate messages from a maildrop into a folder */
  #ifndef	lint
! static char ident[] = "@(#)$Id: inc.c,v 1.4 90/04/05 14:57:51 sources Exp $";
  #endif	lint
  
  #include "../h/mh.h"
--- 1,6 ----
  /* inc.c - incorporate messages from a maildrop into a folder */
  #ifndef	lint
! static char ident[] = "@(#)$Id: inc.c,v 1.1 90/07/31 14:15:49 case Exp Locker: case $";
  #endif	lint
  
  #include "../h/mh.h"
***************
*** 625,630 ****
--- 625,631 ----
  		    (void) fflush (stdout);
  
  		msgnum++, mp -> hghmsg++;
+ 		mp->nummsg++;
  		mp -> msgstats[msgnum] = EXISTS;
  #ifdef	TMA
  		if (i == SCNENC) {
diff -cr mh-6.7/uip/rmm.c mh-6.7.new/uip/rmm.c
*** mh-6.7/uip/rmm.c	Thu Apr 12 13:29:37 1990
--- mh-6.7.new/uip/rmm.c	Mon Jul 30 16:54:30 1990
***************
*** 2,7 ****
--- 2,9 ----
  
  #include "../h/mh.h"
  #include <stdio.h>
+ #include <ndbm.h>
+ #include <fcntl.h>
  
  /*  */
  
***************
*** 11,16 ****
--- 13,19 ----
  
      NULL, NULL
  };
+ #define DBMDIR ".mhcache"
  
  /*  */
  
***************
*** 34,40 ****
             *arguments[MAXARGS],
             *msgs[MAXARGS];
      struct msgs *mp;
! 
      invo_name = r1bindex (argv[0], '/');
      if ((cp = m_find (invo_name)) != NULL) {
  	ap = brkstring (cp = getcpy (cp), " ", "\n");
--- 37,44 ----
             *arguments[MAXARGS],
             *msgs[MAXARGS];
      struct msgs *mp;
!     DBM  *scache;
!     datum key;
      invo_name = r1bindex (argv[0], '/');
      if ((cp = m_find (invo_name)) != NULL) {
  	ap = brkstring (cp = getcpy (cp), " ", "\n");
***************
*** 124,129 ****
--- 128,134 ----
  	execvp (rmmproc, vec);
  	adios (rmmproc, "unable to exec");
      }
+     scache = dbm_open(DBMDIR,O_RDWR, 0600);
  
      for (msgnum = mp -> lowsel; msgnum <= mp -> hghsel; msgnum++)
  	if (mp -> msgstats[msgnum] & SELECTED) {
***************
*** 130,136 ****
  	    (void) strcpy (buf, m_backup (dp = m_name (msgnum)));
  	    if (rename (dp, buf) == NOTOK)
  		admonish (buf, "unable to rename %s to", dp);
  	}
! 
      done (0);
  }
--- 135,146 ----
  	    (void) strcpy (buf, m_backup (dp = m_name (msgnum)));
  	    if (rename (dp, buf) == NOTOK)
  		admonish (buf, "unable to rename %s to", dp);
+ 	    if (scache != NULL) {
+ 		key.dptr = (char *)&msgnum;
+ 		key.dsize = sizeof(msgnum);
+ 		dbm_delete(scache,key);
+ 	    }
  	}
!     if (scache != NULL) dbm_close(scache);
      done (0);
  }
diff -cr mh-6.7/uip/scan.c mh-6.7.new/uip/scan.c
*** mh-6.7/uip/scan.c	Thu Apr 12 13:29:37 1990
--- mh-6.7.new/uip/scan.c	Wed Aug  8 11:06:10 1990
***************
*** 1,6 ****
  /* scan.c - display a one-line "scan" listing */
  #ifndef	lint
! static char ident[] = "@(#)$Id: scan.c,v 1.8 90/04/05 14:59:58 sources Exp $";
  #endif	lint
  
  #include "../h/mh.h"
--- 1,6 ----
  /* scan.c - display a one-line "scan" listing */
  #ifndef	lint
! static char ident[] = "@(#)$Id: scan.c,v 1.3 90/07/31 10:24:27 case Exp Locker: case $";
  #endif	lint
  
  #include "../h/mh.h"
***************
*** 9,14 ****
--- 9,16 ----
  #include "../zotnet/tws.h"
  #include <errno.h>
  #include <stdio.h>
+ #include <ndbm.h>
+ #include <fcntl.h>
  
  /*  */
  
***************
*** 42,47 ****
--- 44,62 ----
  #define	HELPSW	10
      "help", 4,
  
+ #define FASTSW  11
+     "fast", 4,
+ /*
+  * The 'fast' option uses ndbm to store the output of scan.  Speedup is
+  * typically around 10 to 20 times on large folders.  As you see, the
+  * first time takes a while to build the cache.  The subsequent times are
+  * much quicker.
+  *
+  * dingo uip [887] time xscan -fast > & /dev/null
+  * 18.380u 15.060s 1:02.58 53.4% 0+26k 1408+316io 1413pf+0w
+  * dingo uip [888] time xscan -fast > & /dev/null
+  * 1.120u 1.420s 0:03.58 70.9% 0+22k 53+0io 63pf+0w
+  */
      NULL, NULL
  };
  
***************
*** 52,58 ****
--- 67,81 ----
  extern struct msgs *fmt_current_folder;	
  #endif
  
+ typedef struct record {
+     char scanout[80];
+ } Record;
+ #define SBUFSIZ 256
+ #define DBMDIR ".mhcache"
+ #define MAXPATHLEN 2048+1
  
+ static struct format *fmt;
+ 
  void	clear_screen ();
  
  /*  */
***************
*** 68,73 ****
--- 91,97 ----
  	    revflag = 0, 	/* used to be #ifdef BERK */
  	    width = 0,
              msgp = 0,
+             fast = 0,
  	    ontty,
  	    state,
              msgnum;
***************
*** 86,92 ****
             *msgs[MAXARGS];
      struct msgs *mp;
      FILE * in;
! 
      invo_name = r1bindex (argv[0], '/');
      mts_init (invo_name);
      if ((cp = m_find (invo_name)) != NULL) {
--- 110,118 ----
             *msgs[MAXARGS];
      struct msgs *mp;
      FILE * in;
!     DBM  *scache;
!     datum key, rec;
!      
      invo_name = r1bindex (argv[0], '/');
      mts_init (invo_name);
      if ((cp = m_find (invo_name)) != NULL) {
***************
*** 150,155 ****
--- 176,184 ----
  		case NREVSW:
  		    revflag = 0;
  		    continue;
+ 		case FASTSW:
+ 		    fast = 1;
+ 		    continue;
  
  		case FILESW:
  		    if (!(cp = *argp++) || *cp == '-')
***************
*** 217,256 ****
  #endif
  
  /*  */
! 
      for (msgnum = revflag ? mp -> hghsel : mp -> lowsel;
  	    (revflag ? msgnum >= mp -> lowsel : msgnum <= mp -> hghsel);
  	    msgnum += revflag ? (-1) : 1)
! 	if (mp -> msgstats[msgnum] & SELECTED) {
! 	    if ((in = fopen (cp = m_name (msgnum), "r")) == NULL) {
  #ifdef	notdef
! 		if (errno != EACCES)
  #endif
! 		    admonish (cp, "unable to open message");
  #ifdef	notdef
! 		else
! 		    printf ("%*d  unreadable\n", DMAXFOLDER, msgnum);
  #endif
! 		continue;
! 	    }
  
! 	    if (hdrflag) {
! 		(void) time (&clock);
! 		printf ("Folder %-32s%s\n\n", folder,
! 			dasctime (dlocaltime (&clock), TW_NULL));
! 	    }
! 	    switch (state = scan (in, msgnum, 0, nfs, width,
! 			msgnum == mp -> curmsg,
! 			hdrflag, 0L, 1)) {
! 		case SCNMSG: 
! 		case SCNENC: 
! 		case SCNERR: 
  		    break;
  
! 		default: 
  		    adios (NULLCP, "scan() botch (%d)", state);
  
! 		case SCNEOF: 
  #ifdef	notdef
  		    printf ("%*d  empty\n", DMAXFOLDER, msgnum);
  #else
--- 246,350 ----
  #endif
  
  /*  */
!      if (fast) {
! 	 scache = dbm_open(DBMDIR,O_RDWR | O_CREAT, 0600);
! 	 if (scache == NULL) {
! 	     perror("dbm");
! 	     exit(1);
! 	 }
!      }
      for (msgnum = revflag ? mp -> hghsel : mp -> lowsel;
  	    (revflag ? msgnum >= mp -> lowsel : msgnum <= mp -> hghsel);
  	    msgnum += revflag ? (-1) : 1)
! 	if ((mp -> msgstats[msgnum] & (SELECTED | EXISTS)) == (SELECTED | EXISTS)) {
! 	    if (fast) {
! 		/* caching stuff here using (n)dbm 
! 		   key on message number
! 		   just keep a few of the fields around */
! 		key.dptr = (char *)&msgnum;
! 		key.dsize = sizeof(msgnum);
! 		rec = dbm_fetch(scache,key);
! 		if (hdrflag) {
! 		    (void) time (&clock);
! 		    printf ("Folder %-32s%s\n\n", folder,
! 			    dasctime (dlocaltime (&clock), TW_NULL));
! 		}
! 		if (rec.dptr == NULL) {
! 		    int compnum;
! 		    char    name[NAMESZ];
! 		    char tmpbuf[NAMESZ];
! 		    Record rectmp;
! 		    char *scanout;
! 
! 		    rec.dptr = (char *)&rectmp;
! 		    rec.dsize = sizeof(rectmp);
! 		    rectmp.scanout[0] = '\0';
! 
! 		    if ((in = fopen (cp = m_name (msgnum), "r")) == NULL) {
! 			admonish (cp, "unable to open message");
! 			continue;
! 		    }
! 		    switch (state = scan (in, msgnum, 0, nfs, width,
! 					  msgnum == mp -> curmsg,
! 					  hdrflag, 0L, 0, 1, &scanout)) {
! 		      case SCNMSG: 
! 		      case SCNENC: 
! 		      case SCNERR: 
! 			break;
! 			
! 			default: 
! 			adios (NULLCP, "scan() botch (%d)", state);
! 			
! 		      case SCNEOF: 
  #ifdef	notdef
! 			printf ("%*d  empty\n", DMAXFOLDER, msgnum);
! #else
! 			advise (NULLCP, "message %d: empty", msgnum);
  #endif
! 			break;
! 		    }
! 		    hdrflag = 0;
! 		    (void) fclose (in);
! 		    strncpy(rectmp.scanout,scanout,sizeof(rectmp.scanout));
! 		    if (strlen(scanout) >= sizeof(rectmp.scanout))
! 			rectmp.scanout[sizeof(rectmp.scanout)-1] = '\n';
! 		    dbm_store(scache,key,rec,DBM_INSERT);
! 		}
! 		((Record  *)rec.dptr)->scanout[4] =
! 		  (mp->curmsg == msgnum) ? '+' : ' ';
! 		fputs(((Record *)rec.dptr)->scanout,stdout);
! 		if (ontty)
! 		  (void) fflush (stdout);
! 	    } else {
! 		if ((in = fopen (cp = m_name (msgnum), "r")) == NULL) {
  #ifdef	notdef
! 		    if (errno != EACCES)
  #endif
! 		      admonish (cp, "unable to open message");
! #ifdef	notdef
! 		    else
! 		      printf ("%*d  unreadable\n", DMAXFOLDER, msgnum);
! #endif
! 		    continue;
! 		}
  
! 		if (hdrflag) {
! 		    (void) time (&clock);
! 		    printf ("Folder %-32s%s\n\n", folder,
! 			    dasctime (dlocaltime (&clock), TW_NULL));
! 		}
! 		switch (state = scan (in, msgnum, 0, nfs, width,
! 				      msgnum == mp -> curmsg,
! 				      hdrflag, 0L, 1, 0, NULL)) {
! 		  case SCNMSG: 
! 		  case SCNENC: 
! 		  case SCNERR: 
  		    break;
  
! 		    default: 
  		    adios (NULLCP, "scan() botch (%d)", state);
  
! 		  case SCNEOF: 
  #ifdef	notdef
  		    printf ("%*d  empty\n", DMAXFOLDER, msgnum);
  #else
***************
*** 257,268 ****
  		    advise (NULLCP, "message %d: empty", msgnum);
  #endif
  		    break;
  	    }
- 	    hdrflag = 0;
- 	    (void) fclose (in);
- 	    if (ontty)
- 		(void) fflush (stdout);
  	}
  #ifdef	VAN
      m_sync (mp);	/* because formatsbr might have made changes */
  #endif
--- 351,365 ----
  		    advise (NULLCP, "message %d: empty", msgnum);
  #endif
  		    break;
+ 		}
+ 		hdrflag = 0;
+ 		(void) fclose (in);
+ 		if (ontty)
+ 		  (void) fflush (stdout);
  	    }
  	}
+      if (fast && scache != NULL) dbm_close(scache);
+ 
  #ifdef	VAN
      m_sync (mp);	/* because formatsbr might have made changes */
  #endif
***************
*** 274,276 ****
--- 371,375 ----
  
      done (0);
  }
+ 
+ 
diff -cr mh-6.7/uip/scansbr.c mh-6.7.new/uip/scansbr.c
*** mh-6.7/uip/scansbr.c	Thu Apr 12 13:29:37 1990
--- mh-6.7.new/uip/scansbr.c	Mon Jul 30 16:54:30 1990
***************
*** 1,6 ****
  /* scansbr.c - routines to help scan along... */
  #ifndef	lint
! static char ident[] = "@(#)$Id: scansbr.c,v 1.5 90/04/05 14:57:59 sources Exp $";
  #endif	lint
  
  #include "../h/mh.h"
--- 1,6 ----
  /* scansbr.c - routines to help scan along... */
  #ifndef	lint
! static char ident[] = "@(#)$Id: scansbr.c,v 1.2 90/07/30 16:54:18 case Exp $";
  #endif	lint
  
  #include "../h/mh.h"
***************
*** 53,59 ****
  
  /* ARGSUSED */
  
! int     scan (inb, innum, outnum, nfs, width, curflg, header, size, noisy)
  char	*nfs;
  int     innum,
          outnum,
--- 53,59 ----
  
  /* ARGSUSED */
  
! int     scan (inb, innum, outnum, nfs, width, curflg, header, size, noisy,fast,scanout)
  char	*nfs;
  int     innum,
          outnum,
***************
*** 60,66 ****
  	width,
          curflg,
          header,
! 	noisy;
  long	size;
  register FILE   *inb;
  {
--- 60,68 ----
  	width,
          curflg,
          header,
! 	noisy,
!         fast;
! char **scanout;
  long	size;
  register FILE   *inb;
  {
***************
*** 292,297 ****
--- 294,301 ----
      }
      if (noisy)
  	(void) fputs (scanl, stdout);
+     if (fast)
+         *scanout = scanl; /* return formatted buffer */
  
      FINDCOMP (cptr, "encrypted");
      encrypted = cptr && cptr -> c_text;
diff -cr mh-6.7/uip/sendsbr.c mh-6.7.new/uip/sendsbr.c
*** mh-6.7/uip/sendsbr.c	Thu Apr 12 13:29:37 1990
--- mh-6.7.new/uip/sendsbr.c	Tue Jul 31 13:28:55 1990
***************
*** 1,6 ****
  /* sendsbr.c - routines to help WhatNow/Send along */
  #ifndef	lint
! static char ident[] = "@(#)$Id: sendsbr.c,v 2.3 90/04/05 14:57:18 sources Exp $";
  #endif	lint
  
  #include "../h/mh.h"
--- 1,6 ----
  /* sendsbr.c - routines to help WhatNow/Send along */
  #ifndef	lint
! static char ident[] = "@(#)$Id: sendsbr.c,v 1.1 90/07/31 13:28:44 case Exp $";
  #endif	lint
  
  #include "../h/mh.h"
--
Case Larsen
ctl@OCF.berkeley.edu

ctl@OCF.Berkeley.EDU (Case Larsen) (08/28/90)

The MH patches I posted had a few unresolved bugs.  I'll be posting
replacement patches in a few days.

--
Case Larsen
ctl@OCF.berkeley.edu