]> granicus.if.org Git - mutt/commitdiff
Add $history_file and $save_history, for saving command history across
authorVincent Lefevre <vincent@vinc17.net>
Mon, 26 Feb 2007 17:17:13 +0000 (17:17 +0000)
committerVincent Lefevre <vincent@vinc17.net>
Mon, 26 Feb 2007 17:17:13 +0000 (17:17 +0000)
sessions.

UPDATING
enter.c
globals.h
history.c
history.h
init.c
init.h

index fc1624f8f8cd2cc67b6d38de36762a817a9ee585..44a20281f809315d2731ad19b55437d866a15757 100644 (file)
--- a/UPDATING
+++ b/UPDATING
@@ -4,6 +4,7 @@ mutt. Please read this file carefully when upgrading your installation.
 The keys used are:
   !: modified feature, -: deleted feature, +: new feature
 
+  + $save_history, $history_file (save history across sessions)
   + $assumed_charset, $file_charset, $strict_mime
   + $smtp_url (ESMTP relay support)
   + $crypt_use_pka (use GPGME PKA signature verification)
diff --git a/enter.c b/enter.c
index fd7da9d06d192794526e67ba2443d21c36241b6d..bfbbd3ca2a31124e48194c0f05cdb65880031357 100644 (file)
--- a/enter.c
+++ b/enter.c
@@ -562,7 +562,7 @@ int _mutt_enter_string (char *buf, size_t buflen, int y, int x,
              {
                mutt_pretty_mailbox (buf);
                if (!pass)
-                 mutt_history_add (hclass, buf);
+                 mutt_history_add (hclass, buf, 1);
                rv = 0;
                goto bye;
              }
@@ -667,7 +667,7 @@ self_insert:
        /* 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)
        {
index d4c9b7c9e94497c0d44e88acdb51470aaa73d0a0..0b87488f0e08c289b155d2087c7e56836134a839 100644 (file)
--- a/globals.h
+++ b/globals.h
@@ -53,6 +53,7 @@ WHERE char *FolderFormat;
 WHERE char *ForwFmt;
 WHERE char *Fqdn;
 WHERE char *HdrFmt;
+WHERE char *HistFile;
 WHERE char *Homedir;
 WHERE char *Hostname;
 #ifdef USE_IMAP
@@ -195,6 +196,7 @@ WHERE short MenuContext;
 WHERE short PagerContext;
 WHERE short PagerIndexLines;
 WHERE short ReadInc;
+WHERE short SaveHist;
 WHERE short SendmailWait;
 WHERE short SleepTime INITVAL (1);
 WHERE short Timeout;
index 4bee9ab950584917aeb3f999a8aad433ab1cfee8..78c8f46d21dab003151c64604727c7bc607573bf 100644 (file)
--- a/history.c
+++ b/history.c
@@ -56,6 +56,145 @@ static void init_history (struct history *h)
   h->last = 0;
 }
 
+void mutt_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, 0);
+      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) /* __FOPEN_CHECKED__ */
+    {
+      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", 0);
+
+  /* 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;
@@ -69,7 +208,7 @@ void mutt_init_history(void)
   OldSize = HistSize;
 }
   
-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];
@@ -88,6 +227,8 @@ void mutt_history_add (history_class_t hclass, const char *s)
      */
     if (*s != ' ' && (!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;
index bcb87a57416ea096b459449928c350117e2493b7..5c9220cbfc2e7d9e70b3d32280d0cef1ee208ea1 100644 (file)
--- a/history.h
+++ b/history.h
@@ -35,7 +35,8 @@ enum history_class
 typedef enum history_class history_class_t;
 
 void mutt_init_history(void);
-void mutt_history_add(history_class_t, const char *);
+void mutt_read_histfile(void);
+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 --git a/init.c b/init.c
index 3cce5aaf03cac549800690e099d4858831ca42fb..ce69d1662c4f013384c72046147b285398bc05d5 100644 (file)
--- a/init.c
+++ b/init.c
@@ -3012,6 +3012,8 @@ void mutt_init (int skip_sys_rc, LIST *commands)
       mutt_exit(1);
   }
 
+  mutt_read_histfile ();
+
 #if 0
   set_option (OPTWEED); /* turn weeding on by default */
 #endif
diff --git a/init.h b/init.h
index f0280cac396fd0ae1b1beb00b1a135a12b90def1..f5e2057f9dbd027cb6607f3a51b7a287fcdcc9e9 100644 (file)
--- a/init.h
+++ b/init.h
@@ -828,6 +828,11 @@ struct option_t MuttVars[] = {
   ** 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
@@ -2438,6 +2443,12 @@ struct option_t MuttVars[] = {
   ** \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