On Monday, 8th June 1998, Richard Coleman wrote:
I'm beginning a restructuring of many of the commands. This is a
longer term project, so don't expect immediate results. But I've got
a good start on it.
I've been using mh for a decade or so and it seems like the nmh project has
saved it from decaying into nothingness. I've become a fan of nmh, and am
please to see the restructuring, especially to better integrate MIME.
However, I am still looking more towards reliability than features at this
time. I have large mail boxes (10000 or more messages) and frequently
allow my unseen sequence to grow very large as I (ab)use it as a simple
"look at this again" list.
Ever since I switched from plain 'inc' to a complex procmail and rcvstore
mail delivery mechanism, I have been plagued with disappearing sequences.
I have traced this to optimistic handling of context and .mh_sequences
files in nmh. In particular, each .mh_sequences file is truncated before
the new sequences are written to it, and if another nmh command starts in
that small gap it will read an empty set of sequences. When this second
nmh command completes, it will write its version of .mh_sequences, losing
data.
Absolutely perfect sequence manipulation would require complex interlocks,
but I have achieved very good results with the simple method of creating
a temporary file and linking it into place.
Appended is a patch for your consideration. I have been using this patch
for several weeks now and have noticed nothing unusual except that my
sequences no longer disappear. The patch is relative to nmh 0.26:
*** OLD/context_save.c Mon Jan 5 04:15:54 1998
--- context_save.c Fri May 29 14:33:25 1998
***************
*** 19,24 ****
--- 19,25 ----
{
int action;
register struct node *np;
+ char *tmppath;
FILE *out;
sigset_t set, oset;
***************
*** 37,48 ****
sigaddset (&set, SIGTERM);
SIGPROCMASK (SIG_BLOCK, &set, &oset);
! if (!(out = fopen (ctxpath, "w")))
! adios (ctxpath, "unable to write");
for (np = m_defs; np; np = np->n_next)
if (np->n_context)
fprintf (out, "%s: %s\n", np->n_name, np->n_field);
fclose (out);
SIGPROCMASK (SIG_SETMASK, &oset, &set); /* reset the signal mask */
--- 38,64 ----
sigaddset (&set, SIGTERM);
SIGPROCMASK (SIG_BLOCK, &set, &oset);
! if ((tmppath = malloc (strlen (ctxpath) + 10)) == NULL)
! adios (NULL, "unable to allocate string storage in context_save");
! sprintf (tmppath, "%s.%d", ctxpath, getpid());
!
! if (!(out = fopen (tmppath, "w")))
! adios (tmppath, "unable to write");
for (np = m_defs; np; np = np->n_next)
if (np->n_context)
fprintf (out, "%s: %s\n", np->n_name, np->n_field);
+ fflush (out);
+ if (ferror (out)) {
+ unlink (tmppath);
+ adios (tmppath, "write error");
+ }
fclose (out);
+
+ if (rename (tmppath, ctxpath)) {
+ unlink (tmppath);
+ adios (ctxpath, "cannot rename");
+ }
+ free (tmppath);
SIGPROCMASK (SIG_SETMASK, &oset, &set); /* reset the signal mask */
*** OLD/seq_save.c Mon Jan 5 04:22:50 1998
--- seq_save.c Fri May 29 14:33:24 1998
***************
*** 24,29 ****
--- 24,30 ----
{
int i;
char flags, *cp, attr[BUFSIZ], seqfile[PATH_MAX];
+ char *tmpfile;
FILE *fp;
sigset_t set, oset;
***************
*** 73,81 ****
* If that fails (probably because folder is
* readonly), then make sequence private.
*/
! if ((fp = fopen (seqfile, "w")) == NULL
! && (unlink (seqfile) == -1 ||
! (fp = fopen (seqfile, "w")) == NULL)) {
admonish (attr, "unable to write");
goto priv;
}
--- 74,86 ----
* If that fails (probably because folder is
* readonly), then make sequence private.
*/
! if ((tmpfile = malloc (strlen (seqfile) + 10)) == NULL)
! adios (NULL, "unable to allocate string storage");
! sprintf (tmpfile, "%s.%d", seqfile, getpid());
!
! if ((fp = fopen (tmpfile, "w")) == NULL
! && (unlink (tmpfile) == -1 ||
! (fp = fopen (tmpfile, "w")) == NULL)) {
admonish (attr, "unable to write");
goto priv;
}
***************
*** 93,99 ****
--- 98,114 ----
}
if (fp) {
+ fflush (fp);
+ if (ferror (fp)) {
+ unlink (tmpfile);
+ adios (tmpfile, "write error");
+ }
fclose (fp);
+ if (rename (tmpfile, seqfile)) {
+ unlink (tmpfile);
+ adios (seqfile, "cannot rename");
+ }
+ free (tmpfile);
SIGPROCMASK (SIG_SETMASK, &oset, &set); /* reset signal mask */
} else {
/*
The End.