<<< Date Index >>>     <<< Thread Index >>>

Latest version of my "save history" (savehist) patch



I've attached the latest version of my "save history" (savehist)
patch, whose purpose is to save the history to a file (read when
Mutt is restarted).

Two bugs have been fixed in this version.

  * First, the history no longer gets corrupted when a string to be
    saved contains a newline character; this character is just dropped
    (it has here no meaning, probably comes from buggy mail software
    and Mutt does not handle it correctly anyway [bug 2173]).

  * Also, the history is now stored in the UTF-8 encoding; this fixes
    the problem with non-ASCII characters (accented letters...) that
    occurred when Mutt was started under different locales (based on
    different encodings, e.g. ISO-8859-1 and UTF-8).

-- 
Vincent Lefèvre <vincent@xxxxxxxxxx> - Web: <http://www.vinc17.org/>
100% accessible validated (X)HTML - Blog: <http://www.vinc17.org/blog/>
Work: CR INRIA - computer arithmetic / SPACES project at LORIA
diff -Naurd mutt-cvs/PATCHES mutt-new/PATCHES
--- mutt-cvs/PATCHES    2002-12-09 17:44:54.000000000 +0000
+++ mutt-new/PATCHES    2006-01-30 15:56:23.000000000 +0000
@@ -0,0 +1 @@
+patch-1.5.11.vl.savehist.2
diff -Naurd mutt-cvs/enter.c mutt-new/enter.c
--- mutt-cvs/enter.c    2005-09-17 23:18:45.000000000 +0000
+++ mutt-new/enter.c    2006-01-30 12:33:35.000000000 +0000
@@ -548,7 +548,7 @@
              {
                mutt_pretty_mailbox (buf);
                if (!pass)
-                 mutt_history_add (hclass, buf);
+                 mutt_history_add (hclass, buf, 1);
                rv = 0;
                goto bye;
              }
@@ -653,7 +653,7 @@
        /* Convert from wide characters */
        my_wcstombs (buf, buflen, state->wbuf, state->lastchar);
        if (!pass)
-         mutt_history_add (hclass, buf);
+         mutt_history_add (hclass, buf, 1);
 
        if (multiple)
        {
diff -Naurd mutt-cvs/globals.h mutt-new/globals.h
--- mutt-cvs/globals.h  2006-01-11 10:58:35.000000000 +0000
+++ mutt-new/globals.h  2006-01-30 12:33:35.000000000 +0000
@@ -51,6 +51,7 @@
 WHERE char *ForwFmt;
 WHERE char *Fqdn;
 WHERE char *HdrFmt;
+WHERE char *HistFile;
 WHERE char *Homedir;
 WHERE char *Hostname;
 #ifdef USE_IMAP
@@ -183,6 +184,7 @@
 WHERE short PagerContext;
 WHERE short PagerIndexLines;
 WHERE short ReadInc;
+WHERE short SaveHist;
 WHERE short SendmailWait;
 WHERE short SleepTime INITVAL (1);
 WHERE short Timeout;
diff -Naurd mutt-cvs/history.c mutt-new/history.c
--- mutt-cvs/history.c  2005-09-17 23:18:45.000000000 +0000
+++ mutt-new/history.c  2006-01-30 15:56:11.000000000 +0000
@@ -56,6 +56,144 @@
   h->last = 0;
 }
 
+static void read_histfile (void)
+{
+  FILE *f;
+  int line = 0, hclass, read;
+  char *linebuf = NULL, *p;
+  size_t buflen;
+
+  if ((f = fopen (HistFile, "r")) == NULL)
+    return;
+
+  while ((linebuf = mutt_read_line (linebuf, &buflen, f, &line)) != NULL)
+  {
+    read = 0;
+    if (sscanf (linebuf, "%d:%n", &hclass, &read) < 1 || read == 0 ||
+        *(p = linebuf + strlen (linebuf) - 1) != '|')
+    {
+      mutt_error (_("Bad history file format (line %d)"), line);
+      break;
+    }
+    *p = '\0';
+    p = safe_strdup (linebuf + read);
+    if (p)
+    {
+      mutt_convert_string (&p, "utf-8", Charset, M_ICONV_HOOK_TO);
+      mutt_history_add (hclass, p, 0);
+      FREE (&p);
+    }
+  }
+
+  fclose (f);
+  FREE (&linebuf);
+}
+
+static void shrink_histfile (void)
+{
+  char tmpfname[_POSIX_PATH_MAX];
+  FILE *f, *tmp = NULL;
+  int n[HC_LAST] = { 0 };
+  int line, hclass;
+  char *linebuf = NULL;
+  size_t buflen;
+
+  if ((f = fopen (HistFile, "r")) == NULL)
+    return;
+
+  line = 0;
+  while ((linebuf = mutt_read_line (linebuf, &buflen, f, &line)) != NULL)
+  {
+    if (sscanf (linebuf, "%d", &hclass) < 1)
+    {
+      mutt_error (_("Bad history file format (line %d)"), line);
+      goto cleanup;
+    }
+    n[hclass]++;
+  }
+
+  for(hclass = HC_FIRST; hclass < HC_LAST; hclass++)
+    if (n[hclass] > SaveHist)
+    {
+      mutt_mktemp (tmpfname);
+      if ((tmp = safe_fopen (tmpfname, "w+")) == NULL)
+        mutt_perror (tmpfname);
+      break;
+    }
+
+  if (tmp != NULL)
+  {
+    rewind (f);
+    line = 0;
+    while ((linebuf = mutt_read_line (linebuf, &buflen, f, &line)) != NULL)
+    {
+      if (sscanf (linebuf, "%d", &hclass) < 1)
+      {
+        mutt_error (_("Bad history file format (line %d)"), line);
+        goto cleanup;
+      }
+      if (n[hclass]-- <= SaveHist)
+        fprintf (tmp, "%s\n", linebuf);
+    }
+  }
+
+cleanup:
+  fclose (f);
+  FREE (&linebuf);
+  if (tmp != NULL)
+  {
+    if (fflush (tmp) == 0 && (f = fopen (HistFile, "w")) != NULL)
+    {
+      rewind (tmp);
+      mutt_copy_stream (tmp, f);
+      fclose (f);
+    }
+    fclose (tmp);
+    unlink (tmpfname);
+  }
+}
+
+static void save_history (history_class_t hclass, const char *s)
+{
+  static int n = 0;
+  FILE *f;
+  char *tmp, *p;
+
+  if (!s || !*s)  /* This shouldn't happen, but it's safer. */
+    return;
+
+  if ((f = fopen (HistFile, "a")) == NULL)
+  {
+    mutt_perror ("fopen");
+    return;
+  }
+
+  tmp = safe_strdup (s);
+  mutt_convert_string (&tmp, Charset, "utf-8", M_ICONV_HOOK_FROM);
+
+  /* Format of a history item (1 line): "<histclass>:<string>|".
+     We add a '|' in order to avoid lines ending with '\'. */
+  fprintf (f, "%d:", (int) hclass);
+  for (p = tmp; *p; p++)
+  {
+    /* Don't copy \n as a history item must fit on one line. The string
+       shouldn't contain such a character anyway, but as this can happen
+       in practice, we must deal with that. */
+    if (*p != '\n')
+      putc ((unsigned char) *p, f);
+  }
+  fputs ("|\n", f);
+
+  fclose (f);
+  FREE (&tmp);
+
+  if (--n < 0)
+  {
+    n = SaveHist;
+    shrink_histfile();
+  }
+}
+
 void mutt_init_history(void)
 {
   history_class_t hclass;
@@ -67,9 +205,10 @@
     init_history(&History[hclass]);
 
   OldSize = HistSize;
+  read_histfile();
 }
   
-void mutt_history_add (history_class_t hclass, const char *s)
+void mutt_history_add (history_class_t hclass, const char *s, int save)
 {
   int prev;
   struct history *h = &History[hclass];
@@ -83,6 +222,8 @@
     if (prev < 0) prev = HistSize - 1;
     if (!h->hist[prev] || mutt_strcmp (h->hist[prev], s) != 0)
     {
+      if (save && SaveHist)
+        save_history (hclass, s);
       mutt_str_replace (&h->hist[h->last++], s);
       if (h->last > HistSize - 1)
        h->last = 0;
diff -Naurd mutt-cvs/history.h mutt-new/history.h
--- mutt-cvs/history.h  2005-09-17 23:18:45.000000000 +0000
+++ mutt-new/history.h  2006-01-30 12:33:35.000000000 +0000
@@ -35,7 +35,7 @@
 typedef enum history_class history_class_t;
 
 void mutt_init_history(void);
-void mutt_history_add(history_class_t, const char *);
+void mutt_history_add(history_class_t, const char *, int);
 char *mutt_history_next(history_class_t);
 char *mutt_history_prev(history_class_t);
 
diff -Naurd mutt-cvs/init.h mutt-new/init.h
--- mutt-cvs/init.h     2006-01-11 10:58:35.000000000 +0000
+++ mutt-new/init.h     2006-01-30 12:33:35.000000000 +0000
@@ -775,6 +775,11 @@
   ** the string history buffer. The buffer is cleared each time the
   ** variable is set.
   */
+  { "history_file",     DT_PATH, R_NONE, UL &HistFile, UL "~/.mutthistory" },
+  /*
+  ** .pp
+  ** The file in which Mutt will save its history.
+  */
   { "honor_followup_to", DT_QUAD, R_NONE, OPT_MFUPTO, M_YES },
   /*
   ** .pp
@@ -2377,6 +2382,12 @@
   ** \fBNote:\fP This only applies to mbox and MMDF folders, Mutt does not
   ** delete MH and Maildir directories.
   */
+  { "save_history",     DT_NUM,  R_NONE, UL &SaveHist, 0 },
+  /*
+  ** .pp
+  ** This variable controls the size of the history saved in the
+  ** ``$$history_file'' file.
+  */
   { "save_name",       DT_BOOL, R_NONE, OPTSAVENAME, 0 },
   /*
   ** .pp