]> granicus.if.org Git - neomutt/commitdiff
PATCHES devel/merge-upstream
authorRichard Russon <rich@flatcap.org>
Sun, 27 Oct 2019 03:14:14 +0000 (03:14 +0000)
committerRichard Russon <rich@flatcap.org>
Sun, 27 Oct 2019 03:14:14 +0000 (03:14 +0000)
19 files changed:
0001-Rename-browser-fields-to-display_name-and-full_path.patch [new file with mode: 0644]
0002-Add-browser_sticky_cursor-default-set.patch [new file with mode: 0644]
0003-Convert-remaining-mutt_encode_path-call-to-use-buffer.patch [new file with mode: 0644]
0004-Change-browser-display-filename-to-show-full_path.patch [new file with mode: 0644]
0005-Improve-sidebar-indentation-and-shortpath-behavior.patch [new file with mode: 0644]
0006-Memcpy-header-cache-fetch-values-to-ensure-alignment.patch [new file with mode: 0644]
0007-Add-sticky-browser-behavior-for-sorting.patch [new file with mode: 0644]
0008-Add-new-browse-mailboxes-function-in-index-and-pager.patch [new file with mode: 0644]
0009-Update-mime-fields-when-piping-a-message-with-pipe_d.patch [new file with mode: 0644]
0010-Reduce-line-buffer-size-in-mx_get_magic.patch [new file with mode: 0644]
0011-Stable-branch-quick-fix-for-pager-change-mailbox-pus.patch [new file with mode: 0644]
0012-Remove-menu-menu-hack-when-redirecting-pager-ops-thr.patch [new file with mode: 0644]
0013-Clean-up-pager-change-folder-aborts-to-return-to-pag.patch [new file with mode: 0644]
0014-Add-typelen-parameter-to-rfc1524_mailcap_lookup.patch [new file with mode: 0644]
0015-Convert-mutt_message_to_7bit-to-use-buffer.patch [new file with mode: 0644]
0016-Convert-transform_to_7bit-to-use-buffer-pool.patch [new file with mode: 0644]
0017-Convert-send_msg-sendmail_wait-to-use-buffer-pool.patch [new file with mode: 0644]
0018-Convert-_mutt_bounce_message-to-use-buffer-pool.patch [new file with mode: 0644]
0019-Convert-mutt_write_fcc-to-use-buffer-pool.patch [new file with mode: 0644]

diff --git a/0001-Rename-browser-fields-to-display_name-and-full_path.patch b/0001-Rename-browser-fields-to-display_name-and-full_path.patch
new file mode 100644 (file)
index 0000000..edd0cf5
--- /dev/null
@@ -0,0 +1,459 @@
+From 1600dd189b064341b6579d8b07d0bf5f6ad80f8d Mon Sep 17 00:00:00 2001
+From: Kevin McCarthy <kevin@8t8.us>
+Date: Sun, 8 Sep 2019 12:09:51 -0700
+Subject: Rename browser fields to display_name and full_path
+
+Mailbox and Folder view don't make use of the desc field.  They store
+an abbreviated version in the name field and expand it when selecting
+or testing it.
+
+IMAP stores the full URL in name, and stores an abbreviated
+version(possibly with a trailing slash) in the desc field.
+
+The asymetricity makes it awkward, and interferes with a smart-cursor
+option in the next commit.  So instead rename the fields to
+"display_name" and "full_path".
+
+In Mailfox and Folder view, store the fully expanded path, which we
+have while iterating, in "full_path", and the shortened version in
+"display_name".
+
+Likewise in IMAP, store the full URL in "full_path" and the shortened
+version in "display_name".
+
+Simplify the code in browser.c to use the full_path instead of
+concatenating and expanding.
+
+Upstream-commit: https://gitlab.com/muttmua/mutt/commit/1600dd189b064341b6579d8b07d0bf5f6ad80f8d
+Co-authored-by:
+---
+ browser.c     | 135 ++++++++++++++++----------------------------------
+ browser.h     |   4 +-
+ imap/browse.c |   6 +--
+ 3 files changed, 49 insertions(+), 96 deletions(-)
+
+diff --git a/browser.c b/browser.c
+index 973e17ae..56ce75d2 100644
+--- a/browser.c
++++ b/browser.c
+@@ -72,8 +72,8 @@ static void destroy_state(struct browser_state *state)
+   for (c = 0; c < state->entrylen; c++)
+   {
+-    FREE(& ((state->entry)[c].name));
+-    FREE(& ((state->entry)[c].desc));
++    FREE(& ((state->entry)[c].display_name));
++    FREE(& ((state->entry)[c].full_path));
+   }
+ #ifdef USE_IMAP
+   FREE(&state->folder);
+@@ -86,7 +86,7 @@ static int browser_compare_subject(const void *a, const void *b)
+   struct folder_file *pa =(struct folder_file *) a;
+   struct folder_file *pb =(struct folder_file *) b;
+-  int r = mutt_str_strcoll(pa->name, pb->name);
++  int r = mutt_str_strcoll(pa->display_name, pb->display_name);
+   return ((SortBrowser & SORT_REVERSE) ? -r : r);
+ }
+@@ -159,21 +159,14 @@ static void browser_sort(struct browser_state *state)
+   qsort(state->entry, state->entrylen, sizeof(struct folder_file), f);
+ }
+-static int link_is_dir(const char *folder, const char *path)
++static int link_is_dir(const char *full_path)
+ {
+   struct stat st;
+-  struct Buffer *fullpath = NULL;
+   int retval = 0;
+-  fullpath = mutt_buffer_pool_get();
+-
+-  mutt_buffer_concat_path(fullpath, folder, path);
+-
+-  if (stat(mutt_b2s(fullpath), &st) == 0)
++  if (stat(full_path, &st) == 0)
+     retval = S_ISDIR(st.st_mode);
+-  mutt_buffer_pool_release(&fullpath);
+-
+   return retval;
+ }
+@@ -232,13 +225,7 @@ folder_format_str(char *dest, size_t destlen, size_t col, int cols, char op, co
+     case 'f':
+     {
+-      char *s;
+-#ifdef USE_IMAP
+-      if (folder->ff->imap)
+-        s = NONULL(folder->ff->desc);
+-      else
+-#endif
+-        s = NONULL(folder->ff->name);
++      char *s = NONULL(folder->ff->display_name);
+       snprintf(fn, sizeof(fn), "%s%s", s,
+                 folder->ff->local ?
+@@ -389,7 +376,8 @@ folder_format_str(char *dest, size_t destlen, size_t col, int cols, char op, co
+ }
+ static void add_folder(struct Menu *m, struct browser_state *state,
+-                        const char *name, const struct stat *s, struct Mailbox *b)
++                        const char *display_name, const char *full_path,
++                        const struct stat *s, struct Mailbox *b)
+ {
+   if (state->entrylen == state->entrymax)
+   {
+@@ -422,8 +410,8 @@ static void add_folder(struct Menu *m, struct browser_state *state,
+   (state->entry)[state->entrylen].msg_unread = b->msg_unread;
+   }
+-(state->entry)[state->entrylen].name = mutt_str_strdup(name);
+-(state->entry)[state->entrylen].desc = mutt_str_strdup(name);
++(state->entry)[state->entrylen].display_name = mutt_str_strdup(display_name);
++(state->entry)[state->entrylen].full_path = mutt_str_strdup(full_path);
+ #ifdef USE_IMAP
+ (state->entry)[state->entrylen].imap = 0;
+ #endif
+@@ -448,7 +436,7 @@ static int examine_directory(struct Menu *menu, struct browser_state *state,
+   struct stat s;
+   DIR *dp;
+   struct dirent *de;
+-  struct Buffer *buffer = NULL;
++  struct Buffer *full_path = NULL;
+   struct Mailbox *tmp;
+   while (stat(d, &s) == -1)
+@@ -482,7 +470,7 @@ static int examine_directory(struct Menu *menu, struct browser_state *state,
+     return (-1);
+   }
+-  buffer = mutt_buffer_pool_get();
++  full_path = mutt_buffer_pool_get();
+   init_state(state, menu);
+   while ((de = readdir(dp)) != NULL)
+@@ -495,8 +483,8 @@ static int examine_directory(struct Menu *menu, struct browser_state *state,
+     if (!((mutt/regex.c(C_Mask.regex, de->d_name, 0, NULL, 0) == 0) ^ C_Mask.not))
+       continue;
+-    mutt_buffer_concat_path(buffer, d, de->d_name);
+-    if (lstat(mutt_b2s(buffer), &s) == -1)
++    mutt_buffer_concat_path(full_path, d, de->d_name);
++    if (lstat(mutt_b2s(full_path), &s) == -1)
+       continue;
+     /* No size for directories or symlinks */
+@@ -506,7 +494,7 @@ static int examine_directory(struct Menu *menu, struct browser_state *state,
+       continue;
+     tmp = AllMailboxes;
+-    while (tmp && mutt_str_strcmp(mutt_b2s(buffer), mutt_b2s(tmp->pathbuf)))
++    while (tmp && mutt_str_strcmp(mutt_b2s(full_path), mutt_b2s(tmp->pathbuf)))
+       tmp = tmp->next;
+     if (tmp && Context &&
+         !mutt_str_strcmp(tmp->realpath, Context->realpath))
+@@ -514,12 +502,12 @@ static int examine_directory(struct Menu *menu, struct browser_state *state,
+       tmp->msg_count = Context->msgcount;
+       tmp->msg_unread = Context->unread;
+     }
+-    add_folder(menu, state, de->d_name, &s, tmp);
++    add_folder(menu, state, de->d_name, mutt_b2s(full_path), &s, tmp);
+   }
+   closedir(dp);
+   browser_sort(state);
+-  mutt_buffer_pool_release(&buffer);
++  mutt_buffer_pool_release(&full_path);
+   return 0;
+ }
+@@ -554,14 +542,14 @@ static int examine_mailboxes(struct Menu *menu, struct browser_state *state)
+ #ifdef USE_IMAP
+     if (mx_is_imap(mutt_b2s(tmp->pathbuf)))
+     {
+-      add_folder(menu, state, mutt_b2s(mailbox), NULL, tmp);
++      add_folder(menu, state, mutt_b2s(mailbox), mutt_b2s(tmp->pathbuf), NULL, tmp);
+       continue;
+     }
+ #endif
+ #ifdef USE_POP
+     if (mx_is_pop(mutt_b2s(tmp->pathbuf)))
+     {
+-      add_folder(menu, state, mutt_b2s(mailbox), NULL, tmp);
++      add_folder(menu, state, mutt_b2s(mailbox), mutt_b2s(tmp->pathbuf), NULL, tmp);
+       continue;
+     }
+ #endif
+@@ -586,7 +574,7 @@ static int examine_mailboxes(struct Menu *menu, struct browser_state *state)
+         s.st_mtime = st2.st_mtime;
+     }
+-    add_folder(menu, state, mutt_b2s(mailbox), &s, tmp);
++    add_folder(menu, state, mutt_b2s(mailbox), mutt_b2s(tmp->pathbuf), &s, tmp);
+   }
+   while ((tmp = tmp->next));
+   browser_sort(state);
+@@ -598,7 +586,7 @@ static int examine_mailboxes(struct Menu *menu, struct browser_state *state)
+ static int select_file_search(struct Menu *menu, regex_t *re, int n)
+ {
+-  return (mutt/regex.c(re, ((struct folder_file *) menu->data)[n].name, 0, NULL, 0));
++  return (mutt/regex.c(re, ((struct folder_file *) menu->data)[n].display_name, 0, NULL, 0));
+ }
+ static void folder_entry(char *s, size_t slen, struct Menu *menu, int num)
+@@ -655,7 +643,7 @@ static int file_tag(struct Menu *menu, int n, int m)
+   struct folder_file *ff = & (((struct folder_file *)menu->data)[n]);
+   int ot;
+   if (S_ISDIR(ff->mode) ||
+-    (S_ISLNK(ff->mode) && link_is_dir(mutt_b2s(LastDir), ff->name)))
++    (S_ISLNK(ff->mode) && link_is_dir(ff->full_path)))
+   {
+     mutt_error _("Can't attach a directory!");
+     return 0;
+@@ -832,28 +820,14 @@ void _mutt_buffer_select_file(struct Buffer *f, int flags, char ***files, int *numfile
+         if (S_ISDIR(state.entry[menu->current].mode) ||
+           (S_ISLNK(state.entry[menu->current].mode) &&
+-             link_is_dir(mutt_b2s(LastDir), state.entry[menu->current].name))
++             link_is_dir(state.entry[menu->current].full_path))
+ #ifdef USE_IMAP
+             || state.entry[menu->current].inferiors
+ #endif
+           )
+         {
+-          /* make sure this isn't a MH or maildir mailbox */
+-          if (mailbox)
+-          {
+-            mutt_buffer_strcpy(buf, state.entry[menu->current].name);
+-            mutt_buffer_expand_path(buf);
+-          }
+-#ifdef USE_IMAP
+-          else if (state.imap_browse)
+-          {
+-            mutt_buffer_strcpy(buf, state.entry[menu->current].name);
+-          }
+-#endif
+-          else
+-            mutt_buffer_concat_path(buf, mutt_b2s(LastDir), state.entry[menu->current].name);
+-
+-          if (op == OP_DESCEND_DIRECTORY || (mx_get_magic(mutt_b2s(buf)) <= 0)
++          if (op == OP_DESCEND_DIRECTORY
++              || (mx_get_magic(state.entry[menu->current].full_path) <= 0)
+ #ifdef USE_IMAP
+               || state.entry[menu->current].inferiors
+ #endif
+@@ -862,7 +836,7 @@ void _mutt_buffer_select_file(struct Buffer *f, int flags, char ***files, int *numfile
+             /* save the old directory */
+             mutt_buffer_strcpy(OldLastDir, mutt_b2s(LastDir));
+-            if (mutt_str_strcmp(state.entry[menu->current].name, "..") == 0)
++            if (mutt_str_strcmp(state.entry[menu->current].display_name, "..") == 0)
+             {
+               size_t lastdirlen = mutt_buffer_len(LastDir);
+@@ -893,19 +867,18 @@ void _mutt_buffer_select_file(struct Buffer *f, int flags, char ***files, int *numfile
+             }
+             else if (mailbox)
+             {
+-              mutt_buffer_strcpy(LastDir, state.entry[menu->current].name);
+-              mutt_buffer_expand_path(LastDir);
++              mutt_buffer_strcpy(LastDir, state.entry[menu->current].full_path);
+             }
+ #ifdef USE_IMAP
+             else if (state.imap_browse)
+             {
+               struct Url url;
+-              mutt_buffer_strcpy(LastDir, state.entry[menu->current].name);
++              mutt_buffer_strcpy(LastDir, state.entry[menu->current].full_path);
+               /* tack on delimiter here */
+               /* special case "" needs no delimiter */
+-              url_parse(&url, state.entry[menu->current].name);
++              url_parse(&url, state.entry[menu->current].full_path);
+               if (url.path &&
+                 (state.entry[menu->current].delim != '\0'))
+               {
+@@ -915,8 +888,7 @@ void _mutt_buffer_select_file(struct Buffer *f, int flags, char ***files, int *numfile
+ #endif
+             else
+             {
+-              mutt_buffer_concat_path(tmp, mutt_b2s(LastDir), state.entry[menu->current].name);
+-              mutt_buffer_strcpy(LastDir, mutt_b2s(tmp));
++              mutt_buffer_strcpy(LastDir, state.entry[menu->current].full_path);
+             }
+             destroy_state(&state);
+@@ -955,21 +927,11 @@ void _mutt_buffer_select_file(struct Buffer *f, int flags, char ***files, int *numfile
+         }
+         else if (op == OP_DESCEND_DIRECTORY)
+         {
+-          mutt_error(_("%s is not a directory."), state.entry[menu->current].name);
++          mutt_error(_("%s is not a directory."), state.entry[menu->current].display_name);
+           break;
+         }
+-        if (mailbox)
+-        {
+-          mutt_buffer_strcpy(f, state.entry[menu->current].name);
+-          mutt_buffer_expand_path(f);
+-        }
+-#ifdef USE_IMAP
+-        else if (state.imap_browse)
+-          mutt_buffer_strcpy(f, state.entry[menu->current].name);
+-#endif
+-        else
+-          mutt_buffer_concat_path(f, mutt_b2s(LastDir), state.entry[menu->current].name);
++        mutt_buffer_strcpy(f, state.entry[menu->current].full_path);
+         /* Fall through to OP_EXIT */
+@@ -984,22 +946,14 @@ void _mutt_buffer_select_file(struct Buffer *f, int flags, char ***files, int *numfile
+             *numfiles = menu->tagged;
+             tfiles = mutt_mem_calloc(*numfiles, sizeof(char *));
+             for (i = 0, j = 0; i < state.entrylen; i++)
+-            {
+-              struct folder_file ff = state.entry[i];
+-              if (ff.tagged)
+-              {
+-                mutt_buffer_concat_path(tmp, mutt_b2s(LastDir), ff.name);
+-                mutt_buffer_expand_path(tmp);
+-                tfiles[j++] = mutt_str_strdup(mutt_b2s(tmp));
+-              }
+-            }
++              if (state.entry[i].tagged)
++                tfiles[j++] = mutt_str_strdup(state.entry[i].full_path);
+             *files = tfiles;
+           }
+           else if ((mutt_b2s(f))[0]) /* no tagged entries. return selected entry */
+           {
+             *numfiles = 1;
+             tfiles = mutt_mem_calloc(*numfiles, sizeof(char *));
+-            mutt_buffer_expand_path(f);
+             tfiles[0] = mutt_str_strdup(mutt_b2s(f));
+             *files = tfiles;
+           }
+@@ -1010,16 +964,16 @@ void _mutt_buffer_select_file(struct Buffer *f, int flags, char ***files, int *numfile
+       case OP_BROWSER_TELL:
+         if (state.entrylen)
+-          mutt_message("%s", state.entry[menu->current].name);
++          mutt_message("%s", state.entry[menu->current].display_name);
+         break;
+ #ifdef USE_IMAP
+       case OP_BROWSER_SUBSCRIBE:
+-        imap_subscribe(state.entry[menu->current].name, 1);
++        imap_subscribe(state.entry[menu->current].full_path, 1);
+         break;
+       case OP_BROWSER_UNSUBSCRIBE:
+-        imap_subscribe(state.entry[menu->current].name, 0);
++        imap_subscribe(state.entry[menu->current].full_path, 0);
+         break;
+       case OP_BROWSER_TOGGLE_LSUB:
+@@ -1062,7 +1016,7 @@ void _mutt_buffer_select_file(struct Buffer *f, int flags, char ***files, int *numfile
+         {
+           int nentry = menu->current;
+-          if (imap_mailbox_rename(state.entry[nentry].name) >= 0)
++          if (imap_mailbox_rename(state.entry[nentry].full_path) >= 0)
+           {
+             destroy_state(&state);
+             init_state(&state, NULL);
+@@ -1086,7 +1040,7 @@ void _mutt_buffer_select_file(struct Buffer *f, int flags, char ***files, int *numfile
+           IMAP_MBOX mx;
+           int nentry = menu->current;
+-          imap_parse_path(state.entry[nentry].name, &mx);
++          imap_parse_path(state.entry[nentry].full_path, &mx);
+           if (!mx.mbox)
+           {
+             mutt_error _("Cannot delete root folder");
+@@ -1099,8 +1053,8 @@ void _mutt_buffer_select_file(struct Buffer *f, int flags, char ***files, int *numfile
+             if (!imap_delete_mailbox(Context, mx))
+             {
+               /* free the mailbox from the browser */
+-              FREE(& ((state.entry)[nentry].name));
+-              FREE(& ((state.entry)[nentry].desc));
++              FREE(& ((state.entry)[nentry].display_name));
++              FREE(& ((state.entry)[nentry].full_path));
+               /* and move all other entries up */
+               if (nentry+1 < state.entrylen)
+                 memmove(state.entry + nentry, state.entry + nentry + 1,
+@@ -1367,7 +1321,7 @@ void _mutt_buffer_select_file(struct Buffer *f, int flags, char ***files, int *numfile
+ #ifdef USE_IMAP
+         if (state.entry[menu->current].selectable)
+         {
+-          mutt_buffer_strcpy(f, state.entry[menu->current].name);
++          mutt_buffer_strcpy(f, state.entry[menu->current].full_path);
+           destroy_state(&state);
+           goto bail;
+         }
+@@ -1375,7 +1329,7 @@ void _mutt_buffer_select_file(struct Buffer *f, int flags, char ***files, int *numfile
+ #endif
+           if (S_ISDIR(state.entry[menu->current].mode) ||
+             (S_ISLNK(state.entry[menu->current].mode) &&
+-               link_is_dir(mutt_b2s(LastDir), state.entry[menu->current].name)))
++               link_is_dir(state.entry[menu->current].full_path)))
+           {
+             mutt_error _("Can't view a directory");
+             break;
+@@ -1384,8 +1338,7 @@ void _mutt_buffer_select_file(struct Buffer *f, int flags, char ***files, int *numfile
+           {
+             struct Body *b;
+-            mutt_buffer_concat_path(buf, mutt_b2s(LastDir), state.entry[menu->current].name);
+-            b = mutt_make_file_attach(mutt_b2s(buf));
++            b = mutt_make_file_attach(state.entry[menu->current].full_path);
+             if (b != NULL)
+             {
+               mutt_view_attachment(NULL, b, MUTT_REGULAR, NULL, NULL);
+diff --git a/browser.h b/browser.h
+index a7103743..39c257b6 100644
+--- a/browser.h
++++ b/browser.h
+@@ -28,8 +28,8 @@ struct folder_file
+   gid_t gid;
+   nlink_t nlink;
+-  char *name;
+-  char *desc;
++  char *display_name;
++  char *full_path;
+   short new;               /* true if mailbox has "new mail" */
+   int msg_count;           /* total number of messages */
+diff --git a/imap/browse.c b/imap/browse.c
+index ab965803..277444aa 100644
+--- a/imap/browse.c
++++ b/imap/browse.c
+@@ -426,7 +426,7 @@ static void imap_add_folder(char delim, char *folder, int noselect,
+   }
+   imap_qualify_path(tmp, sizeof(tmp), &mx, folder);
+-(state->entry)[state->entrylen].name = mutt_str_strdup(tmp);
++(state->entry)[state->entrylen].full_path = mutt_str_strdup(tmp);
+   /* mark desc with delim in browser if it can have subfolders */
+   if (!isparent && !noinferiors && strlen(relpath) < sizeof(relpath) - 1)
+@@ -435,7 +435,7 @@ static void imap_add_folder(char delim, char *folder, int noselect,
+     relpath[strlen(relpath)] = delim;
+   }
+-(state->entry)[state->entrylen].desc = mutt_str_strdup(relpath);
++(state->entry)[state->entrylen].display_name = mutt_str_strdup(relpath);
+ (state->entry)[state->entrylen].imap = 1;
+   /* delimiter at the root is useless. */
+@@ -469,5 +469,5 @@ static void imap_add_folder(char delim, char *folder, int noselect,
+ static int compare_names(struct folder_file *a, struct folder_file *b)
+ {
+-  return mutt_str_strcmp(a->name, b->name);
++  return mutt_str_strcmp(a->full_path, b->full_path);
+ }
diff --git a/0002-Add-browser_sticky_cursor-default-set.patch b/0002-Add-browser_sticky_cursor-default-set.patch
new file mode 100644 (file)
index 0000000..2ae3a36
--- /dev/null
@@ -0,0 +1,361 @@
+From 0fa710308f8ad337376bee1af6bd73595a71cadb Mon Sep 17 00:00:00 2001
+From: Kevin McCarthy <kevin@8t8.us>
+Date: Sun, 8 Sep 2019 10:49:37 -0700
+Subject: Add $browser_sticky_cursor default set
+
+This option attempts to keep the browser cursor on the same mailbox.
+
+It does this by keeping track of the current selected mailbox and
+comparing that when regenerating the menu.
+
+Modify imap_mailbox_create() and imap_mailbox_rename() to return the
+new mailbox name so it can be automatically selected.
+
+Upstream-commit: https://gitlab.com/muttmua/mutt/commit/0fa710308f8ad337376bee1af6bd73595a71cadb
+Co-authored-by:
+---
+ browser.c           | 63 +++++++++++++++++++++++++++++++++++----------
+ imap/browse.c       |  8 ++++--
+ imap/imap.h         |  4 +--
+ imap/imap_private.h |  1 +
+ imap/util.c         | 10 +++++++
+ init.h              |  9 +++++++
+ mutt.h              |  1 +
+ 7 files changed, 78 insertions(+), 18 deletions(-)
+
+diff --git a/browser.c b/browser.c
+index 56ce75d2..c75406ca 100644
+--- a/browser.c
++++ b/browser.c
+@@ -601,9 +601,10 @@ static void folder_entry(char *s, size_t slen, struct Menu *menu, int num)
+ }
+ static void init_menu(struct browser_state *state, struct Menu *menu, char *title,
+-                       size_t titlelen, int mailbox)
++                       size_t titlelen, int mailbox, const char *defaultsel)
+ {
+   struct Buffer *path = NULL;
++  int i;
+   path = mutt_buffer_pool_get();
+@@ -635,6 +636,18 @@ static void init_menu(struct browser_state *state, struct Menu *menu, char *title,
+   }
+   menu->redraw = REDRAW_FULL;
++  if (option(OPTBROWSERSTICKYCURSOR) && defaultsel && *defaultsel)
++  {
++    for (i = 0; i < menu->max; i++)
++    {
++      if (!mutt_str_strcmp(defaultsel, state->entry[i].full_path))
++      {
++        menu->current = i;
++        break;
++      }
++    }
++  }
++
+   mutt_buffer_pool_release(&path);
+ }
+@@ -674,6 +687,7 @@ void _mutt_buffer_select_file(struct Buffer *f, int flags, char ***files, int *numfile
+   struct Buffer *prefix = NULL;
+   struct Buffer *tmp = NULL;
+   struct Buffer *OldLastDir = NULL;
++  struct Buffer *defaultsel = NULL;
+   char helpstr[1024];
+   char title[256];
+   struct browser_state state;
+@@ -691,6 +705,7 @@ void _mutt_buffer_select_file(struct Buffer *f, int flags, char ***files, int *numfile
+   prefix     = mutt_buffer_pool_get();
+   tmp        = mutt_buffer_pool_get();
+   OldLastDir = mutt_buffer_pool_get();
++  defaultsel  = mutt_buffer_pool_get();
+   memset(&state, 0, sizeof(struct browser_state));
+@@ -757,6 +772,9 @@ void _mutt_buffer_select_file(struct Buffer *f, int flags, char ***files, int *numfile
+     else if (!*(mutt_b2s(LastDir)))
+       mutt_buffer_strcpy(LastDir, NONULL(Folder));
++    if (Context)
++      mutt_buffer_strcpy(defaultsel, NONULL(Context->path));
++
+ #ifdef USE_IMAP
+     if (!mailbox && mx_is_imap(mutt_b2s(LastDir)))
+     {
+@@ -803,11 +821,16 @@ void _mutt_buffer_select_file(struct Buffer *f, int flags, char ***files, int *numfile
+                                   FolderHelp);
+   mutt_menu_push_current(menu);
+-  init_menu(&state, menu, title, sizeof(title), mailbox);
++  init_menu(&state, menu, title, sizeof(title), mailbox, mutt_b2s(defaultsel));
+   FOREVER
+   {
+-    switch(op = mutt_menu_loop(menu))
++    op = mutt_menu_loop(menu);
++
++    if (state.entrylen)
++      mutt_buffer_strcpy(defaultsel, state.entry[menu->current].full_path);
++
++    switch(op)
+     {
+       case OP_DESCEND_DIRECTORY:
+       case OP_GENERIC_SELECT_ENTRY:
+@@ -836,6 +859,13 @@ void _mutt_buffer_select_file(struct Buffer *f, int flags, char ***files, int *numfile
+             /* save the old directory */
+             mutt_buffer_strcpy(OldLastDir, mutt_b2s(LastDir));
++            mutt_buffer_strcpy(defaultsel, mutt_b2s(OldLastDir));
++            if (mutt_buffer_len(defaultsel) && (*(defaultsel->dptr - 1) == '/'))
++            {
++              defaultsel->dptr--;
++              *(defaultsel->dptr) = '\0';
++            }
++
+             if (mutt_str_strcmp(state.entry[menu->current].display_name, "..") == 0)
+             {
+               size_t lastdirlen = mutt_buffer_len(LastDir);
+@@ -921,7 +951,7 @@ void _mutt_buffer_select_file(struct Buffer *f, int flags, char ***files, int *numfile
+               }
+             menu->current = 0;
+             menu->top = 0;
+-            init_menu(&state, menu, title, sizeof(title), mailbox);
++            init_menu(&state, menu, title, sizeof(title), mailbox, mutt_b2s(defaultsel));
+             break;
+           }
+         }
+@@ -992,7 +1022,7 @@ void _mutt_buffer_select_file(struct Buffer *f, int flags, char ***files, int *numfile
+           break;
+         }
+-        if (!imap_mailbox_create(mutt_b2s(LastDir)))
++        if (!imap_mailbox_create(mutt_b2s(LastDir), defaultsel))
+         {
+           /* TODO: find a way to detect if the new folder would appear in
+            *   this window, and insert it without starting over. */
+@@ -1004,7 +1034,7 @@ void _mutt_buffer_select_file(struct Buffer *f, int flags, char ***files, int *numfile
+           menu->data = state.entry;
+           menu->current = 0;
+           menu->top = 0;
+-          init_menu(&state, menu, title, sizeof(title), mailbox);
++          init_menu(&state, menu, title, sizeof(title), mailbox, mutt_b2s(defaultsel));
+         }
+         /* else leave error on screen */
+         break;
+@@ -1016,7 +1046,7 @@ void _mutt_buffer_select_file(struct Buffer *f, int flags, char ***files, int *numfile
+         {
+           int nentry = menu->current;
+-          if (imap_mailbox_rename(state.entry[nentry].full_path) >= 0)
++          if (imap_mailbox_rename(state.entry[nentry].full_path, defaultsel) >= 0)
+           {
+             destroy_state(&state);
+             init_state(&state, NULL);
+@@ -1026,7 +1056,7 @@ void _mutt_buffer_select_file(struct Buffer *f, int flags, char ***files, int *numfile
+             menu->data = state.entry;
+             menu->current = 0;
+             menu->top = 0;
+-            init_menu(&state, menu, title, sizeof(title), mailbox);
++            init_menu(&state, menu, title, sizeof(title), mailbox, mutt_b2s(defaultsel));
+           }
+         }
+         break;
+@@ -1063,7 +1093,8 @@ void _mutt_buffer_select_file(struct Buffer *f, int flags, char ***files, int *numfile
+                       sizeof(struct folder_file));
+               state.entrylen--;
+               mutt_message _("Mailbox deleted.");
+-              init_menu(&state, menu, title, sizeof(title), mailbox);
++              mutt_buffer_clear(defaultsel);
++              init_menu(&state, menu, title, sizeof(title), mailbox, mutt_b2s(defaultsel));
+             }
+             else
+               mutt_error _("Mailbox deletion failed.");
+@@ -1078,6 +1109,7 @@ void _mutt_buffer_select_file(struct Buffer *f, int flags, char ***files, int *numfile
+       case OP_CHANGE_DIRECTORY:
+         mutt_buffer_strcpy(buf, mutt_b2s(LastDir));
++        mutt_buffer_clear(defaultsel);
+ #ifdef USE_IMAP
+         if (!state.imap_browse)
+ #endif
+@@ -1107,7 +1139,7 @@ void _mutt_buffer_select_file(struct Buffer *f, int flags, char ***files, int *numfile
+             menu->data = state.entry;
+             menu->current = 0;
+             menu->top = 0;
+-            init_menu(&state, menu, title, sizeof(title), mailbox);
++            init_menu(&state, menu, title, sizeof(title), mailbox, mutt_b2s(defaultsel));
+           }
+           else
+ #endif
+@@ -1136,7 +1168,7 @@ void _mutt_buffer_select_file(struct Buffer *f, int flags, char ***files, int *numfile
+                 }
+                 menu->current = 0;
+                 menu->top = 0;
+-                init_menu(&state, menu, title, sizeof(title), mailbox);
++                init_menu(&state, menu, title, sizeof(title), mailbox, mutt_b2s(defaultsel));
+               }
+               else
+                 mutt_error(_("%s is not a directory."), mutt_b2s(buf));
+@@ -1195,12 +1227,12 @@ void _mutt_buffer_select_file(struct Buffer *f, int flags, char ***files, int *numfile
+               imap_browse(mutt_b2s(LastDir), &state);
+               browser_sort(&state);
+               menu->data = state.entry;
+-              init_menu(&state, menu, title, sizeof(title), mailbox);
++              init_menu(&state, menu, title, sizeof(title), mailbox, mutt_b2s(defaultsel));
+             }
+             else
+ #endif
+               if (examine_directory(menu, &state, mutt_b2s(LastDir), NULL) == 0)
+-                init_menu(&state, menu, title, sizeof(title), mailbox);
++                init_menu(&state, menu, title, sizeof(title), mailbox, mutt_b2s(defaultsel));
+               else
+               {
+                 mutt_error _("Error scanning directory.");
+@@ -1268,6 +1300,8 @@ void _mutt_buffer_select_file(struct Buffer *f, int flags, char ***files, int *numfile
+       case OP_TOGGLE_MAILBOXES:
+         mailbox = 1 - mailbox;
++        menu->current = 0;
++        /* fall through */
+       case OP_CHECK_NEW:
+         destroy_state(&state);
+@@ -1291,7 +1325,7 @@ void _mutt_buffer_select_file(struct Buffer *f, int flags, char ***files, int *numfile
+ #endif
+         else if (examine_directory(menu, &state, mutt_b2s(LastDir), mutt_b2s(prefix)) == -1)
+           goto bail;
+-        init_menu(&state, menu, title, sizeof(title), mailbox);
++        init_menu(&state, menu, title, sizeof(title), mailbox, mutt_b2s(defaultsel));
+         break;
+       case OP_MAILBOX_LIST:
+@@ -1356,6 +1390,7 @@ bail:
+   mutt_buffer_pool_release(&prefix);
+   mutt_buffer_pool_release(&tmp);
+   mutt_buffer_pool_release(&OldLastDir);
++  mutt_buffer_pool_release(&defaultsel);
+   if (menu)
+   {
+diff --git a/imap/browse.c b/imap/browse.c
+index 277444aa..fee412d6 100644
+--- a/imap/browse.c
++++ b/imap/browse.c
+@@ -229,7 +229,7 @@ fail:
+ }
+ /* imap_mailbox_create: Prompt for a new mailbox name, and try to create it */
+-int imap_mailbox_create(const char* folder)
++int imap_mailbox_create(const char* folder, struct Buffer *result)
+ {
+   IMAP_DATA* idata;
+   IMAP_MBOX mx;
+@@ -272,6 +272,8 @@ int imap_mailbox_create(const char* folder)
+   if (imap_create_mailbox(idata, buf) < 0)
+     goto fail;
++  imap_buffer_qualify_path(result, &mx, buf);
++
+   mutt_message _("Mailbox created.");
+   mutt_sleep(0);
+@@ -283,7 +285,7 @@ fail:
+   return -1;
+ }
+-int imap_mailbox_rename(const char* mailbox)
++int imap_mailbox_rename(const char* mailbox, struct Buffer *result)
+ {
+   IMAP_DATA* idata;
+   IMAP_MBOX mx;
+@@ -331,6 +333,8 @@ int imap_mailbox_rename(const char* mailbox)
+     goto fail;
+   }
++  imap_buffer_qualify_path(result, &mx, buf);
++
+   mutt_message(_("Mailbox renamed."));
+   mutt_sleep(0);
+diff --git a/imap/imap.h b/imap/imap.h
+index fe99472b..24001c5e 100644
+--- a/imap/imap.h
++++ b/imap/imap.h
+@@ -51,8 +51,8 @@ extern struct mx_ops mx_imap_ops;
+ /* imap/browse.c */
+ int imap_browse(const char* path, struct browser_state* state);
+-int imap_mailbox_create(const char* folder);
+-int imap_mailbox_rename(const char* mailbox);
++int imap_mailbox_create(const char* folder, struct Buffer *result);
++int imap_mailbox_rename(const char* mailbox, struct Buffer *result);
+ /* imap/message.c */
+ int imap_append_message(struct Context* ctx, struct Message* msg);
+diff --git a/imap/imap_private.h b/imap/imap_private.h
+index 7d12ec25..67376d2b 100644
+--- a/imap/imap_private.h
++++ b/imap/imap_private.h
+@@ -323,6 +323,7 @@ char* imap_next_word(char* s);
+ time_t mutt_date_parse_imap(char* s);
+ void mutt_date_make_imap(char* buf, time_t timestamp);
+ void imap_qualify_path(char *dest, size_t len, IMAP_MBOX *mx, char* path);
++void imap_buffer_qualify_path(struct Buffer *dest, IMAP_MBOX *mx, char* path);
+ void imap_quote_string(char* dest, size_t dlen, const char* src);
+ void imap_quote_string_and_backquotes(char *dest, size_t dlen, const char *src);
+ void imap_unquote_string(char* s);
+diff --git a/imap/util.c b/imap/util.c
+index 01b57d08..315e77d8 100644
+--- a/imap/util.c
++++ b/imap/util.c
+@@ -707,6 +707,16 @@ void imap_qualify_path(char *dest, size_t len, IMAP_MBOX *mx, char* path)
+   url_tostring(&url, dest, len, 0);
+ }
++void imap_buffer_qualify_path(struct Buffer *dest, IMAP_MBOX *mx, char* path)
++{
++  struct Url url;
++
++  mutt_account_tourl(&mx->account, &url);
++  url.path = path;
++
++  url_ciss_tobuffer(&url, dest, 0);
++}
++
+ static void _imap_quote_string(char *dest, size_t dlen, const char *src,
+                                 const char *to_quote)
+diff --git a/init.h b/init.h
+index 2f450a43..39e5d8cd 100644
+--- a/init.h
++++ b/init.h
+@@ -422,6 +422,15 @@ struct option_t MuttVars[] = {
+   ** doesn't make intuitive sense.  In those cases, it may be
+   ** desirable to \fIunset\fP this variable.
+   */
++  { "browser_sticky_cursor", DT_BOOL, R_NONE, {.l=OPTBROWSERSTICKYCURSOR}, {.l=1} },
++  /*
++  ** .pp
++  ** When this variable is \fIset\fP, the browser will attempt to keep
++  ** the cursor on the same mailbox when performing various functions.
++  ** These include moving up a directory, toggling between mailboxes
++  ** and directory listing, creating/renaming a mailbox, toggling
++  ** subscribed mailboxes, and entering a new mask.
++  */
+ #if defined(USE_SSL)
+   { "certificate_file",        DT_STRING|DT_PATH, R_NONE, {.p=&CertificateFile}, {.p="~/.mutt_certificates"} },
+   /*
+diff --git a/mutt.h b/mutt.h
+index ce9056d7..549a4dde 100644
+--- a/mutt.h
++++ b/mutt.h
+@@ -392,6 +392,7 @@ enum
+   C_ChangeFolderNext,
+   C_BrailleFriendly,
+   C_BrowserAbbreviateMailboxes,
++  OPTBROWSERSTICKYCURSOR,
+   C_CheckMboxSize,
+   C_CheckNew,
+   C_CollapseUnread,
diff --git a/0003-Convert-remaining-mutt_encode_path-call-to-use-buffer.patch b/0003-Convert-remaining-mutt_encode_path-call-to-use-buffer.patch
new file mode 100644 (file)
index 0000000..5a7dfe1
--- /dev/null
@@ -0,0 +1,105 @@
+From 2e6d4903a1e81f673ba2b66ab372dd09f95d6d5a Mon Sep 17 00:00:00 2001
+From: Kevin McCarthy <kevin@8t8.us>
+Date: Mon, 9 Sep 2019 14:06:31 -0700
+Subject: Convert remaining mutt_encode_path() call to use struct Buffer
+
+Then rename the other uses of mutt_buffer_encode_path() to
+mutt_encode_path().
+
+Upstream-commit: https://gitlab.com/muttmua/mutt/commit/2e6d4903a1e81f673ba2b66ab372dd09f95d6d5a
+Co-authored-by:
+---
+ bcache.c  |  2 +-
+ hcache/hcache.c  | 16 +++++++++-------
+ muttlib.c | 11 +----------
+ protos.h  |  3 +--
+ 4 files changed, 12 insertions(+), 20 deletions(-)
+
+diff --git a/bcache.c b/bcache.c
+index 0fef6c32..b6925d4a 100644
+--- a/bcache.c
++++ b/bcache.c
+@@ -62,7 +62,7 @@ static int bcache_path(struct Account *account, const char *mailbox, struct BodyCache *bcac
+   path = mutt_buffer_pool_get();
+   dst = mutt_buffer_pool_get();
+-  mutt_buffer_encode_path(path, NONULL(mailbox));
++  mutt_encode_path(path, NONULL(mailbox));
+   mutt_buffer_printf(dst, "%s/%s%s", C_MessageCachedir, host, mutt_b2s(path));
+   if (*(dst->dptr - 1) != '/')
+diff --git a/hcache/hcache.c b/hcache/hcache.c
+index 07f3fbd3..b8d7a9da 100644
+--- a/hcache/hcache.c
++++ b/hcache/hcache.c
+@@ -995,25 +995,27 @@ mutt_hcache_store_raw(header_cache_t* h, const char* filename, void* data,
+ #endif
+ }
+-static char* get_foldername(const char *folder)
++static char* get_foldername(const char *folder)
+ {
+   char *p = NULL;
+-  char path[PATH_MAX];
++  struct Buffer *path;
+   struct stat st;
+-  mutt_encode_path(path, sizeof(path), folder);
++  path = mutt_buffer_pool_get();
++  mutt_encode_path(path, folder);
+   /* if the folder is local, canonify the path to avoid
+    * to ensure equivalent paths share the hcache */
+-  if (stat(path, &st) == 0)
++  if (stat(mutt_b2s(path), &st) == 0)
+   {
+     p = mutt_mem_malloc(PATH_MAX+1);
+-    if (!realpath(path, p))
+-      mutt_str_replace(&p, path);
++    if (!realpath(mutt_b2s(path), p))
++      mutt_str_replace(&p, mutt_b2s(path));
+   }
+   else
+-    p = mutt_str_strdup(path);
++    p = mutt_str_strdup(mutt_b2s(path));
++  mutt_buffer_pool_release(&path);
+   return p;
+ }
+diff --git a/muttlib.c b/muttlib.c
+index 82f90faf..69641230 100644
+--- a/muttlib.c
++++ b/muttlib.c
+@@ -2247,16 +2247,7 @@ int mutt_replacelist_match(const char *s, struct ReplaceList *l, char *text, int textsi
+   return 0;
+ }
+-void mutt_encode_path(char *dest, size_t dlen, const char *src)
+-{
+-  char *p = mutt_str_strdup(src);
+-  int rc = mutt_cs_convert_string(&p, C_Charset, "utf-8", 0);
+-  /* `src' may be NULL, such as when called from the pop3 driver. */
+-  mutt_str_strfcpy(dest, (rc == 0) ? NONULL(p) : NONULL(src), dlen);
+-  FREE(&p);
+-}
+-
+-void mutt_buffer_encode_path(struct Buffer *dest, const char *src)
++void mutt_encode_path(struct Buffer *dest, const char *src)
+ {
+   char *p;
+   int rc;
+diff --git a/protos.h b/protos.h
+index 66348bc3..f747bcb9 100644
+--- a/protos.h
++++ b/protos.h
+@@ -204,8 +204,7 @@ int mutt_label_complete(char *, size_t, int);
+ void mutt_curses_error(const char *, ...);
+ void mutt_curses_message(const char *, ...);
+ void mutt_encode_descriptions(struct Body *, short);
+-void mutt_encode_path(char *, size_t, const char *);
+-void mutt_buffer_encode_path(struct Buffer *, const char *);
++void mutt_encode_path(struct Buffer *, const char *);
+ void mutt_enter_command(void);
+ void mutt_error_history_display(void);
+ void mutt_error_history_init(void);
diff --git a/0004-Change-browser-display-filename-to-show-full_path.patch b/0004-Change-browser-display-filename-to-show-full_path.patch
new file mode 100644 (file)
index 0000000..0aa982e
--- /dev/null
@@ -0,0 +1,30 @@
+From 773166e9ef0a2bc3eca93a8e16560fa4571673ad Mon Sep 17 00:00:00 2001
+From: Kevin McCarthy <kevin@8t8.us>
+Date: Mon, 9 Sep 2019 18:19:03 -0700
+Subject: Change browser <display-filename> to show full_path
+
+Prior to the introduction of display_name/full_path, it showed "name",
+which was the full IMAP URL but the shortname for folder/mailbox view.
+
+Now that the full_path is available, it makes sense to show that for
+all cases.
+
+Upstream-commit: https://gitlab.com/muttmua/mutt/commit/773166e9ef0a2bc3eca93a8e16560fa4571673ad
+Co-authored-by:
+---
+ browser.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/browser.c b/browser.c
+index c75406ca..4e45013d 100644
+--- a/browser.c
++++ b/browser.c
+@@ -994,7 +994,7 @@ void _mutt_buffer_select_file(struct Buffer *f, int flags, char ***files, int *numfile
+       case OP_BROWSER_TELL:
+         if (state.entrylen)
+-          mutt_message("%s", state.entry[menu->current].display_name);
++          mutt_message("%s", state.entry[menu->current].full_path);
+         break;
+ #ifdef USE_IMAP
diff --git a/0005-Improve-sidebar-indentation-and-shortpath-behavior.patch b/0005-Improve-sidebar-indentation-and-shortpath-behavior.patch
new file mode 100644 (file)
index 0000000..9abd768
--- /dev/null
@@ -0,0 +1,264 @@
+From a4b53e19ef471efc1938348d1623b811354f1c93 Mon Sep 17 00:00:00 2001
+From: Kevin McCarthy <kevin@8t8.us>
+Date: Sun, 15 Sep 2019 15:41:42 -0700
+Subject: Improve sidebar indentation and shortpath behavior
+
+The previous implementation only enabled $sidebar_folder_indent for
+mailboxes underneath $folder.  Change indentation to be based upon the
+previous common-path mailbox in the list.  Indent one more than the
+common-path mailbox, rather than the number of delimiters after
+$folder.
+
+Change $sidebar_short_path to shorten based on the previous mailbox in
+the list too.
+
+Invoke mutt_buffer_pretty_mailbox() to prefix the mailbox with '~' or
+'=' characters.  This changes the output for the case where mailbox
+equals $folder, but provides uniform display behavior across mutt.
+
+Thanks to Christopher Zimmermann(@madroach) for the original patch,
+which this commit is based upon.
+
+Upstream-commit: https://gitlab.com/muttmua/mutt/commit/a4b53e19ef471efc1938348d1623b811354f1c93
+Co-authored-by:
+---
+ sidebar.c | 194 +++++++++++++++++++++++++++++++++++++++---------------
+ 1 file changed, 140 insertions(+), 54 deletions(-)
+
+diff --git a/sidebar.c b/sidebar.c
+index 9df19872..364b2285 100644
+--- a/sidebar.c
++++ b/sidebar.c
+@@ -520,6 +520,79 @@ static void fill_empty_space(int first_row, int num_rows, int width)
+   }
+ }
++/**
++ * calculate_depth - Calculate depth of path based on C_SidebarDelimChars.
++ *
++ * If lastpath is not NULL, common_depth is also calculated.  These
++ * are used for indentation and short_path calculation.
++ */
++static void calculate_depth(const char *path, const char *lastpath,
++                             int *depth, int *common_depth)
++{
++  int i, has_trailing_delim = 0;
++
++  *depth = *common_depth = 0;
++  if (!C_SidebarDelimChars || !path)
++    return;
++
++  for (i = 0; path[i]; i++)
++  {
++    if (strchr(C_SidebarDelimChars, path[i]))
++    {
++    (*depth)++;
++
++      /* /a/b/c and /a/b/c/ both are a depth of 3.
++       * Only count the final '\0' if the last character wasn't a separator.
++       */
++      if (!path[i+1])
++        has_trailing_delim = 1;
++    }
++
++    if (lastpath)
++    {
++      /* path     /a/b/c/d
++       * lastpath /a/b
++       * lastpath /a/
++       * lastpath /a
++       *            ^
++       */
++      if (strchr(C_SidebarDelimChars, path[i]) &&
++        (strchr(C_SidebarDelimChars, lastpath[i]) || !lastpath[i]))
++      {
++      (*common_depth)++;
++        if (!lastpath[i])
++          lastpath = NULL;
++      }
++
++      /* path     /abc
++       * lastpath /ad
++       * lastpath /a/
++       * lastpath /a
++       *            ^
++       */
++      else if (!lastpath[i] || path[i] != lastpath[i])
++        lastpath = NULL;
++    }
++  }
++
++  if (!has_trailing_delim)
++  {
++  (*depth)++;
++
++    /* path     /a
++     * lastpath /a/b/c
++     * lastpath /a/
++     * lastpath /a
++     *            ^
++     */
++    if (lastpath &&
++      (strchr(C_SidebarDelimChars, lastpath[i]) || !lastpath[i]))
++    (*common_depth)++;
++  }
++}
++
++#define SIDEBAR_MAX_INDENT 32
++
+ /**
+  * draw_sidebar - Write out a list of mailboxes, on the left
+  * @num_rows:   Height of the Sidebar
+@@ -546,9 +619,18 @@ static void draw_sidebar(int num_rows, int num_cols, int div_width)
+   int entryidx;
+   struct SbEntry *entry;
+   struct Mailbox *b;
++  int indent_width = -1;
++  int indent_depths[SIDEBAR_MAX_INDENT];
++  const char *sidebar_folder_name;
++  struct Buffer *pretty_folder_name, *last_folder_name, *indent_folder_name;
++
+   if (TopIndex < 0)
+     return;
++  pretty_folder_name = mutt_buffer_pool_get();
++  last_folder_name = mutt_buffer_pool_get();
++  indent_folder_name = mutt_buffer_pool_get();
++
+   int w = MIN(num_cols, (C_SidebarWidth - div_width));
+   int row = 0;
+   for (entryidx = TopIndex;(entryidx < EntryCount) && (row < num_rows); entryidx++)
+@@ -586,75 +668,79 @@ static void draw_sidebar(int num_rows, int num_cols, int div_width)
+       b->msg_flagged = Context->flagged;
+     }
+-    /* compute length of Folder without trailing separator */
+-    size_t maildirlen = mutt_str_strlen(Folder);
+-    if (maildirlen &&
+-        C_SidebarDelimChars &&
+-        strchr(C_SidebarDelimChars, Folder[maildirlen - 1]))
+-      maildirlen--;
+-
+-    /* check whether Folder is a prefix of the current folder's path */
+-    short maildir_is_prefix = 0;
+-    if ((mutt_buffer_len(b->pathbuf) > maildirlen) &&
+-      (mutt_str_strncmp(Folder, mutt_b2s(b->pathbuf), maildirlen) == 0) &&
+-        C_SidebarDelimChars &&
+-        strchr(C_SidebarDelimChars, mutt_b2s(b->pathbuf)[maildirlen]))
+-      maildir_is_prefix = 1;
+-
+-    /* calculate depth of current folder and generate its display name with indented spaces */
+-    int sidebar_folder_depth = 0;
+-    const char *sidebar_folder_name;
+-    struct Buffer *short_folder_name = NULL;
+-    int i;
+-    if (option(C_SidebarShortPath))
++    mutt_buffer_strcpy(pretty_folder_name, mutt_b2s(b->pathbuf));
++    mutt_buffer_pretty_mailbox(pretty_folder_name);
++    sidebar_folder_name = mutt_b2s(pretty_folder_name);
++
++    if (C_SidebarDelimChars)
+     {
+-      /* disregard a trailing separator, so strlen() - 2 */
+-      sidebar_folder_name = mutt_b2s(b->pathbuf);
+-      for (i = mutt_str_strlen(sidebar_folder_name) - 2; i >= 0; i--)
++      int parent_depth = 0;
++      int i;
++
++      if (option(C_SidebarShortPath) || option(C_SidebarFolderIndent))
+       {
+-        if (C_SidebarDelimChars &&
+-            strchr(C_SidebarDelimChars, sidebar_folder_name[i]))
+-        {
+-          sidebar_folder_name +=(i + 1);
+-          break;
+-        }
++        int depth = 0, common_depth = 0;
++
++        calculate_depth(sidebar_folder_name, mutt_b2s(last_folder_name),
++                         &depth, &common_depth);
++        mutt_buffer_strcpy(last_folder_name, sidebar_folder_name);
++
++        if (indent_width < SIDEBAR_MAX_INDENT)
++          indent_width++;
++
++        /* indent_depths[] hold the path depths at each level of indentation.
++         * Indent based off the longest path that we share in common.
++         *
++         * The 'indent_depths[] >= depth' test below is for a corner case:
++         *
++         * path       depth    common_depth    indent_width
++         * /a           2            0              0
++         * /a/b         3            2              1
++         * /a/b/        3            3              1
++         *
++         * Because the common_depth of /a/b/ matches the depth of
++         * /a/b, we need the additional test to continue popping the
++         * indent_depths[] stack.
++         */
++        while (indent_width &&
++             ((indent_depths[indent_width - 1] > common_depth) ||
++              (indent_depths[indent_width - 1] >= depth)))
++          indent_width--;
++
++        if (indent_width < SIDEBAR_MAX_INDENT)
++          indent_depths[indent_width] = depth;
++        if (indent_width)
++          parent_depth = indent_depths[indent_width - 1];
+       }
+-    }
+-    else
+-      sidebar_folder_name = mutt_b2s(b->pathbuf) + maildir_is_prefix *(maildirlen + 1);
+-    if (maildir_is_prefix && option(C_SidebarFolderIndent))
+-    {
+-      const char *tmp_folder_name;
+-      int lastsep = 0;
+-      tmp_folder_name = mutt_b2s(b->pathbuf) + maildirlen + 1;
+-      int tmplen =(int) mutt_str_strlen(tmp_folder_name) - 1;
+-      for (i = 0; i < tmplen; i++)
++      if (option(C_SidebarShortPath) && parent_depth)
+       {
+-        if (C_SidebarDelimChars && strchr(C_SidebarDelimChars, tmp_folder_name[i]))
+-        {
+-          sidebar_folder_depth++;
+-          lastsep = i + 1;
+-        }
++        for (i = 0; parent_depth && sidebar_folder_name[i]; i++)
++          if (strchr(C_SidebarDelimChars, sidebar_folder_name[i]))
++            parent_depth--;
++        sidebar_folder_name += i;
+       }
+-      if (sidebar_folder_depth > 0)
++
++      if (option(C_SidebarFolderIndent) && indent_width)
+       {
+-        if (option(C_SidebarShortPath))
+-          tmp_folder_name += lastsep;  /* basename */
+-        short_folder_name = mutt_buffer_pool_get();
+-        for (i=0; i < sidebar_folder_depth; i++)
+-          mutt_buffer_addstr(short_folder_name, NONULL(C_SidebarIndentString));
+-        mutt_buffer_addstr(short_folder_name, tmp_folder_name);
+-        sidebar_folder_name = mutt_b2s(short_folder_name);
++        mutt_buffer_clear(indent_folder_name);
++        for (i = 0; i < indent_width; i++)
++          mutt_buffer_addstr(indent_folder_name, NONULL(C_SidebarIndentString));
++        mutt_buffer_addstr(indent_folder_name, sidebar_folder_name);
++        sidebar_folder_name = mutt_b2s(indent_folder_name);
+       }
+     }
++
+     char str[256];
+     make_sidebar_entry(str, sizeof(str), w, sidebar_folder_name, entry);
+     printw("%s", str);
+-    mutt_buffer_pool_release(&short_folder_name);
+     row++;
+   }
++  mutt_buffer_pool_release(&pretty_folder_name);
++  mutt_buffer_pool_release(&last_folder_name);
++  mutt_buffer_pool_release(&indent_folder_name);
++
+   fill_empty_space(row, num_rows - row, w);
+ }
diff --git a/0006-Memcpy-header-cache-fetch-values-to-ensure-alignment.patch b/0006-Memcpy-header-cache-fetch-values-to-ensure-alignment.patch
new file mode 100644 (file)
index 0000000..657d0c0
--- /dev/null
@@ -0,0 +1,218 @@
+From a6bccbe1d1b731e69b2b609278ef0811d547a6e7 Mon Sep 17 00:00:00 2001
+From: Kevin McCarthy <kevin@8t8.us>
+Date: Mon, 30 Sep 2019 18:50:27 -0700
+Subject: Memcpy header cache fetch values to ensure alignment
+
+While testing the hcache buffer pool changes, I noticed a misaligned
+pointer warning when using LMDB.
+
+The other header cache backends must ensure alignment of the pointer
+they return, but LMDB apparently does not.
+
+Instead of directly assigning and dereferencing the pointer fetched,
+use memcpy to the appropriate type, just as the header cache restore
+operation does.
+
+Upstream-commit: https://gitlab.com/muttmua/mutt/commit/a6bccbe1d1b731e69b2b609278ef0811d547a6e7
+Co-authored-by:
+---
+ imap/imap.c    | 40 ++++++++++++++++++++++++----------------
+ imap/message.c | 19 +++++++++++--------
+ imap/util.c    | 18 ++++++++++--------
+ maildir/mh.c           |  7 ++++---
+ 4 files changed, 49 insertions(+), 35 deletions(-)
+
+diff --git a/imap/imap.c b/imap/imap.c
+index ccba3a38..33469f5f 100644
+--- a/imap/imap.c
++++ b/imap/imap.c
+@@ -1800,9 +1800,9 @@ IMAP_STATUS* imap_mboxcache_get(IMAP_DATA* idata, const char* mbox, int create)
+   IMAP_STATUS scache;
+ #ifdef USE_HCACHE
+   header_cache_t *hc = NULL;
+-  unsigned int *uidvalidity = NULL;
+-  unsigned int *uidnext = NULL;
+-  unsigned long long *modseq = NULL;
++  void *puidvalidity = NULL;
++  void *puidnext = NULL;
++  void *pmodseq = NULL;
+ #endif
+   for (cur = idata->mboxcache; cur; cur = cur->next)
+@@ -1829,28 +1829,36 @@ IMAP_STATUS* imap_mboxcache_get(IMAP_DATA* idata, const char* mbox, int create)
+   hc = imap_hcache_open(idata, mbox);
+   if (hc)
+   {
+-    uidvalidity = mutt_hcache_fetch_raw(hc, "/UIDVALIDITY", imap_hcache_keylen);
+-    uidnext = mutt_hcache_fetch_raw(hc, "/UIDNEXT", imap_hcache_keylen);
+-    modseq = mutt_hcache_fetch_raw(hc, "/MODSEQ", imap_hcache_keylen);
+-    if (uidvalidity)
++    puidvalidity = mutt_hcache_fetch_raw(hc, "/UIDVALIDITY", imap_hcache_keylen);
++    puidnext = mutt_hcache_fetch_raw(hc, "/UIDNEXT", imap_hcache_keylen);
++    pmodseq = mutt_hcache_fetch_raw(hc, "/MODSEQ", imap_hcache_keylen);
++    if (puidvalidity)
+     {
+       if (!status)
+       {
+-        mutt_hcache_free((void **)&uidvalidity);
+-        mutt_hcache_free((void **)&uidnext);
+-        mutt_hcache_free((void **)&modseq);
++        mutt_hcache_free((void **)&puidvalidity);
++        mutt_hcache_free((void **)&puidnext);
++        mutt_hcache_free((void **)&pmodseq);
+         mutt_hcache_close(hc);
+         return imap_mboxcache_get(idata, mbox, 1);
+       }
+-      status->uidvalidity = *uidvalidity;
+-      status->uidnext = uidnext ? *uidnext: 0;
+-      status->modseq = modseq ? *modseq: 0;
++      memcpy(&status->uidvalidity, puidvalidity, sizeof(unsigned int));
++
++      if (puidnext)
++        memcpy(&status->uidnext, puidnext, sizeof(unsigned int));
++      else
++        status->uidnext = 0;
++
++      if (pmodseq)
++        memcpy(&status->modseq, pmodseq, sizeof(unsigned long long));
++      else
++        status->modseq = 0;
+       mutt_debug(LL_DEBUG3, "mboxcache: hcache uidvalidity %u, uidnext %u, modseq %llu\n",
+                   status->uidvalidity, status->uidnext, status->modseq);
+     }
+-    mutt_hcache_free((void **)&uidvalidity);
+-    mutt_hcache_free((void **)&uidnext);
+-    mutt_hcache_free((void **)&modseq);
++    mutt_hcache_free((void **)&puidvalidity);
++    mutt_hcache_free((void **)&puidnext);
++    mutt_hcache_free((void **)&pmodseq);
+     mutt_hcache_close(hc);
+   }
+ #endif
+diff --git a/imap/message.c b/imap/message.c
+index f32f5db6..48dc0437 100644
+--- a/imap/message.c
++++ b/imap/message.c
+@@ -228,14 +228,15 @@ int imap_read_headers(IMAP_DATA* idata, unsigned int msn_begin, unsigned int ms
+   int evalhc = 0;
+ #if USE_HCACHE
+-  unsigned int *uid_validity = NULL;
+-  unsigned int *puidnext = NULL;
++  void *puid_validity = NULL;
++  unsigned int uid_validity = 0;
++  void *puidnext = NULL;
+   unsigned int uidnext = 0;
+   int has_condstore = 0;
+   int has_qresync = 0;
+   int eval_condstore = 0;
+   int eval_qresync = 0;
+-  unsigned long long *pmodseq = NULL;
++  void *pmodseq = NULL;
+   unsigned long long hc_modseq = 0;
+   char *uid_seqset = NULL;
+ #endif /* USE_HCACHE */
+@@ -257,11 +258,13 @@ int imap_read_headers(IMAP_DATA* idata, unsigned int msn_begin, unsigned int ms
+   if (idata->hcache && initial_download)
+   {
+-    uid_validity = mutt_hcache_fetch_raw(idata->hcache, "/UIDVALIDITY", imap_hcache_keylen);
++    puid_validity = mutt_hcache_fetch_raw(idata->hcache, "/UIDVALIDITY", imap_hcache_keylen);
++    if (puid_validity)
++      memcpy(&uid_validity, puid_validity, sizeof(unsigned int));
+     puidnext = mutt_hcache_fetch_raw(idata->hcache, "/UIDNEXT", imap_hcache_keylen);
+     if (puidnext)
+     {
+-      uidnext = *puidnext;
++      memcpy(&uidnext, puidnext, sizeof(unsigned int));;
+       mutt_hcache_free((void **)&puidnext);
+     }
+@@ -278,13 +281,13 @@ int imap_read_headers(IMAP_DATA* idata, unsigned int msn_begin, unsigned int ms
+         has_qresync = 1;
+     }
+-    if (uid_validity && uidnext && *uid_validity == idata->uid_validity)
++    if (puid_validity && uidnext && (uid_validity == idata->uid_validity))
+     {
+       evalhc = 1;
+       pmodseq = mutt_hcache_fetch_raw(idata->hcache, "/MODSEQ", imap_hcache_keylen);
+       if (pmodseq)
+       {
+-        hc_modseq = *pmodseq;
++        memcpy(&hc_modseq, pmodseq, sizeof(unsigned long long));;
+         mutt_hcache_free((void **)&pmodseq);
+       }
+       if (hc_modseq)
+@@ -300,7 +303,7 @@ int imap_read_headers(IMAP_DATA* idata, unsigned int msn_begin, unsigned int ms
+           eval_condstore = 1;
+       }
+     }
+-    mutt_hcache_free((void **)&uid_validity);
++    mutt_hcache_free((void **)&puid_validity);
+   }
+   if (evalhc)
+   {
+diff --git a/imap/util.c b/imap/util.c
+index 0812bac3..42f07cc2 100644
+--- a/imap/util.c
++++ b/imap/util.c
+@@ -179,22 +179,24 @@ void imap_hcache_close(IMAP_DATA* idata)
+ struct Email* imap_hcache_get(IMAP_DATA* idata, unsigned int uid)
+ {
+   char key[16];
+-  unsigned int* uv;
++  void *data;
++  unsigned int uv;
+   struct Email* h = NULL;
+   if (!idata->hcache)
+     return NULL;
+   sprintf(key, "/%u", uid);
+-  uv =(unsigned int*)mutt_hcache_fetch(idata->hcache, key,
+-                                         imap_hcache_keylen);
+-  if (uv)
++  data = mutt_hcache_fetch(idata->hcache, key,
++                            imap_hcache_keylen);
++  if (data)
+   {
+-    if (*uv == idata->uid_validity)
+-      h = mutt_hcache_restore((unsigned char*)uv, NULL);
++    memcpy(&uv, data, sizeof(unsigned int));
++    if (uv == idata->uid_validity)
++      h = mutt_hcache_restore((unsigned char *)data, NULL);
+     else
+-      mutt_debug(LL_DEBUG3, "hcache uidvalidity mismatch: %u", *uv);
+-    mutt_hcache_free((void **)&uv);
++      mutt_debug(LL_DEBUG3, "hcache uidvalidity mismatch: %u", uv);
++    mutt_hcache_free((void **)&data);
+   }
+   return h;
+diff --git a/maildir/mh.c b/maildir/mh.c
+index 37cef860..ad3c8253 100644
+--- a/maildir/mh.c
++++ b/maildir/mh.c
+@@ -1145,7 +1145,7 @@ static void maildir_delayed_parsing(struct Context * ctx, struct maildir **md,
+ #if USE_HCACHE
+   header_cache_t *hc = NULL;
+   void *data;
+-  struct timeval *when = NULL;
++  struct timeval when;
+   struct stat lastchanged;
+   int ret;
+ #endif
+@@ -1207,9 +1207,10 @@ static void maildir_delayed_parsing(struct Context * ctx, struct maildir **md,
+       data = mutt_hcache_fetch(hc, p->h->path, strlen);
+     else
+       data = mutt_hcache_fetch(hc, p->h->path + 3, &maildir_hcache_keylen);
+-    when =(struct timeval *) data;
++    if (data)
++      memcpy(&when, data, sizeof(struct timeval));
+-    if (data != NULL && !ret && lastchanged.st_mtime <= when->tv_sec)
++    if (data != NULL && !ret && lastchanged.st_mtime <= when.tv_sec)
+     {
+       p->h = mutt_hcache_restore((unsigned char *)data, &p->h);
+       if (ctx->magic == MUTT_MAILDIR)
diff --git a/0007-Add-sticky-browser-behavior-for-sorting.patch b/0007-Add-sticky-browser-behavior-for-sorting.patch
new file mode 100644 (file)
index 0000000..6c163ff
--- /dev/null
@@ -0,0 +1,77 @@
+From fe69e4d1a30aabb70dc4a5890bb7188841b61421 Mon Sep 17 00:00:00 2001
+From: Kevin McCarthy <kevin@8t8.us>
+Date: Wed, 9 Oct 2019 08:36:59 +0800
+Subject: Add sticky browser behavior for sorting
+
+The menu isn't rebuilt after sorting, so the selected mailbox was not
+sticky for that operation.
+
+Refactor the sticky cursor setting out so it can be explicitly called
+after sorting too.
+
+Upstream-commit: https://gitlab.com/muttmua/mutt/commit/fe69e4d1a30aabb70dc4a5890bb7188841b61421
+Co-authored-by:
+---
+ browser.c | 31 +++++++++++++++++++------------
+ 1 file changed, 19 insertions(+), 12 deletions(-)
+
+diff --git a/browser.c b/browser.c
+index eb212d3e..b560c54d 100644
+--- a/browser.c
++++ b/browser.c
+@@ -600,11 +600,27 @@ static void folder_entry(char *s, size_t slen, struct Menu *menu, int num)
+                    (unsigned long) &folder, MUTT_FORMAT_ARROWCURSOR);
+ }
++static void set_sticky_cursor(struct browser_state *state, struct Menu *menu, const char *defaultsel)
++{
++  int i;
++
++  if (option(OPTBROWSERSTICKYCURSOR) && defaultsel && *defaultsel)
++  {
++    for (i = 0; i < menu->max; i++)
++    {
++      if (!mutt_str_strcmp(defaultsel, state->entry[i].full_path))
++      {
++        menu->current = i;
++        break;
++      }
++    }
++  }
++}
++
+ static void init_menu(struct browser_state *state, struct Menu *menu, char *title,
+                        size_t titlelen, int mailbox, const char *defaultsel)
+ {
+   struct Buffer *path = NULL;
+-  int i;
+   path = mutt_buffer_pool_get();
+@@ -636,17 +652,7 @@ static void init_menu(struct browser_state *state, struct Menu *menu, char *title,
+   }
+   menu->redraw = REDRAW_FULL;
+-  if (option(OPTBROWSERSTICKYCURSOR) && defaultsel && *defaultsel)
+-  {
+-    for (i = 0; i < menu->max; i++)
+-    {
+-      if (!mutt_str_strcmp(defaultsel, state->entry[i].full_path))
+-      {
+-        menu->current = i;
+-        break;
+-      }
+-    }
+-  }
++  set_sticky_cursor(state, menu, defaultsel);
+   mutt_buffer_pool_release(&path);
+ }
+@@ -1290,6 +1296,7 @@ void _mutt_buffer_select_file(struct Buffer *f, int flags, char ***files, int *numfile
+         {
+           SortBrowser |= reverse ? SORT_REVERSE : 0;
+           browser_sort(&state);
++          set_sticky_cursor(&state, menu, mutt_b2s(defaultsel));
+           menu->redraw = REDRAW_FULL;
+         }
+         break;
diff --git a/0008-Add-new-browse-mailboxes-function-in-index-and-pager.patch b/0008-Add-new-browse-mailboxes-function-in-index-and-pager.patch
new file mode 100644 (file)
index 0000000..a3c6273
--- /dev/null
@@ -0,0 +1,139 @@
+From 0e32b977454de79899c497debcd460470e720e7a Mon Sep 17 00:00:00 2001
+From: Kevin McCarthy <kevin@8t8.us>
+Date: Thu, 10 Oct 2019 15:03:05 +0800
+Subject: Add new browse-mailboxes function in index and pager
+
+This allows direct access to the mailboxes list in the folder menu.
+This is useful, for instance, if $browser_sticky_cursor is set and the
+current directory does not contain the open mailbox.  The macro
+version will lose the current mailbox while toggling to the mailboxes
+list.
+
+Upstream-commit: https://gitlab.com/muttmua/mutt/commit/0e32b977454de79899c497debcd460470e720e7a
+Co-authored-by:
+---
+ opcodes.h             |  2 ++
+ index.c     | 31 +++++++++++++++++++------------
+ doc/neomuttrc.head |  5 +++--
+ functions.h     |  4 ++++
+ 4 files changed, 28 insertions(+), 14 deletions(-)
+
+diff --git a/opcodes.h b/opcodes.h
+index a91a14ab..4e3bd1d0 100644
+--- a/opcodes.h
++++ b/opcodes.h
+@@ -109,6 +109,8 @@ OP_LIST_REPLY "reply to specified mailing list"
+ OP_MACRO "execute a macro"
+ OP_MAIL "compose a new mail message"
+ OP_MAIN_BREAK_THREAD "break the thread in two"
++OP_MAIN_BROWSE_MAILBOXES "select a new mailbox from the browser"
++OP_MAIN_BROWSE_MAILBOXES_READONLY "select a new mailbox from the browser in read only mode"
+ OP_MAIN_CHANGE_FOLDER "open a different folder"
+ OP_MAIN_CHANGE_FOLDER_READONLY "open a different folder in read only mode"
+ OP_MAIN_CLEAR_FLAG "clear a status flag from a message"
+diff --git a/index.c b/index.c
+index 919cf154..4e0f6e86 100644
+--- a/index.c
++++ b/index.c
+@@ -1266,8 +1266,13 @@ int mutt_index_menu(void)
+         if (attach_msg)
+           op = OP_MAIN_CHANGE_FOLDER_READONLY;
++      case OP_MAIN_BROWSE_MAILBOXES:
++        if (attach_msg && (op != OP_MAIN_CHANGE_FOLDER_READONLY))
++          op = OP_MAIN_BROWSE_MAILBOXES_READONLY;
++
+         /* fallback to the readonly case */
++      case OP_MAIN_BROWSE_MAILBOXES_READONLY:
+       case OP_MAIN_CHANGE_FOLDER_READONLY:
+       {
+         struct Buffer *folderbuf;
+@@ -1293,13 +1298,13 @@ int mutt_index_menu(void)
+         }
+ #ifdef USE_SIDEBAR
+         else if (op == OP_SIDEBAR_OPEN)
+-        {
+-          const char *path = mutt_sb_get_highlight();
+-          if (!path || !*path)
+-            goto changefoldercleanup;
+-          mutt_buffer_strcpy(folderbuf, path);
+-        }
++          mutt_buffer_strcpy(folderbuf, NONULL(mutt_sb_get_highlight()));
+ #endif
++
++        else if ((op == OP_MAIN_BROWSE_MAILBOXES) ||
++               (op == OP_MAIN_BROWSE_MAILBOXES_READONLY))
++          mutt_buffer_select_file(folderbuf, MUTT_SEL_FOLDER | MUTT_SEL_MAILBOX);
++
+         else
+         {
+           if (option(C_ChangeFolderNext) && Context && Context->path)
+@@ -1318,13 +1323,13 @@ int mutt_index_menu(void)
+             }
+             goto changefoldercleanup;
+           }
+-          if (!mutt_buffer_len(folderbuf))
+-          {
+-            mutt_window_clearline(MuttMessageWindow, 0);
+-            goto changefoldercleanup;
+-          }
+         }
++        if (!mutt_buffer_len(folderbuf))
++        {
++          mutt_window_clearline(MuttMessageWindow, 0);
++          goto changefoldercleanup;
++        }
+         mutt_buffer_expand_path(folderbuf);
+         if (mx_get_magic(mutt_b2s(folderbuf)) <= 0)
+         {
+@@ -1386,7 +1391,9 @@ int mutt_index_menu(void)
+         mutt_folder_hook(mutt_b2s(folderbuf));
+         if ((Context = mx_open_mailbox(mutt_b2s(folderbuf),
+-                                      (option(C_ReadOnly) || op == OP_MAIN_CHANGE_FOLDER_READONLY) ?
++                                      (option(C_ReadOnly) ||
++                                         op == OP_MAIN_CHANGE_FOLDER_READONLY ||
++                                         op == OP_MAIN_BROWSE_MAILBOXES_READONLY) ?
+                                         MUTT_READONLY : 0, NULL)) != NULL)
+         {
+           menu->current = ci_first_message();
+diff --git a/doc/neomuttrc.head b/doc/neomuttrc.head
+index 2b0020f7..0d2b2f52 100644
+--- a/doc/neomuttrc.head
++++ b/doc/neomuttrc.head
+@@ -26,8 +26,9 @@ macro index,pager,attach,compose \cb "\
+ macro generic,pager <F1> "<shell-escape> less @docdir@/manual.txt<Enter>" "show Mutt documentation"
+ # show the incoming mailboxes list(just like "mutt -y") and back when pressing "y"
+-macro index y "<change-folder>?<toggle-mailboxes>" "show incoming mailboxes list"
+-macro pager y "<exit><change-folder>?<toggle-mailboxes>" "show incoming mailboxes list"
++# note: these macros have been subsumed by the <browse-mailboxes> function.
++# macro index y "<change-folder>?<toggle-mailboxes>" "show incoming mailboxes list"
++# macro pager y "<exit><change-folder>?<toggle-mailboxes>" "show incoming mailboxes list"
+ bind browser y exit
+ # Handler for gzip compressed mailboxes
+diff --git a/functions.h b/functions.h
+index 419dfc50..ba8340b8 100644
+--- a/functions.h
++++ b/functions.h
+@@ -91,6 +91,8 @@ const struct binding_t OpMain[] = { /* map: index */
+ #endif
+   { "bounce-message",                OP_BOUNCE_MESSAGE,                "b" },
+   { "break-thread",                OP_MAIN_BREAK_THREAD,                "#" },
++  { "browse-mailboxes",                OP_MAIN_BROWSE_MAILBOXES,        "y" },
++  { "browse-mailboxes-readonly", OP_MAIN_BROWSE_MAILBOXES_READONLY, NULL },
+   { "change-folder",                OP_MAIN_CHANGE_FOLDER,                "c" },
+   { "change-folder-readonly",        OP_MAIN_CHANGE_FOLDER_READONLY,        "\033c" },
+   { "next-unread-mailbox",        OP_MAIN_NEXT_UNREAD_MAILBOX,    NULL },
+@@ -195,6 +197,8 @@ const struct binding_t OpMain[] = { /* map: index */
+ const struct binding_t OpPager[] = { /* map: pager */
+   { "break-thread",        OP_MAIN_BREAK_THREAD,                "#" },
++  { "browse-mailboxes",        OP_MAIN_BROWSE_MAILBOXES,        "y" },
++  { "browse-mailboxes-readonly", OP_MAIN_BROWSE_MAILBOXES_READONLY, NULL },
+   { "create-alias",        OP_CREATE_ALIAS,                "a" },
+   { "bounce-message",        OP_BOUNCE_MESSAGE,                "b" },
+   { "change-folder",        OP_MAIN_CHANGE_FOLDER,                "c" },
diff --git a/0009-Update-mime-fields-when-piping-a-message-with-pipe_d.patch b/0009-Update-mime-fields-when-piping-a-message-with-pipe_d.patch
new file mode 100644 (file)
index 0000000..01fccba
--- /dev/null
@@ -0,0 +1,47 @@
+From 71d6dbea64b3f6d9676867030214236878693bcb Mon Sep 17 00:00:00 2001
+From: Kevin McCarthy <kevin@8t8.us>
+Date: Sun, 13 Oct 2019 16:25:54 +0800
+Subject: Update mime fields when piping a message with $pipe_decode set
+
+Programs that process the message may get confused if the original
+mime fields are in the output.  Add the CH_MIME flag to strip mime
+headers and CH_TXTPLAIN to add decoded text mime headers in their
+place, just as <decode-copy> does.
+
+However, make sure not to add the flags when printing, as printers
+highly likely won't care and users probably don't want to see those
+headers in their printout.
+
+Upstream-commit: https://gitlab.com/muttmua/mutt/commit/71d6dbea64b3f6d9676867030214236878693bcb
+Co-authored-by:
+---
+ commands.c | 9 ++++++++-
+ 1 file changed, 8 insertions(+), 1 deletion(-)
+
+diff --git a/commands.c b/commands.c
+index b88335d1..9a27614f 100644
+--- a/commands.c
++++ b/commands.c
+@@ -440,14 +440,21 @@ static void pipe_set_flags(int decode, int print, int *cmflags, int *chflags)
+ {
+   if (decode)
+   {
+-    *cmflags |= MUTT_CM_DECODE | MUTT_CM_CHARCONV;
+     *chflags |= CH_DECODE | CH_REORDER;
++    *cmflags |= MUTT_CM_DECODE | MUTT_CM_CHARCONV;
+     if (option(C_Weed))
+     {
+       *chflags |= CH_WEED;
+       *cmflags |= MUTT_CM_WEED;
+     }
++
++    /* Just as with copy-decode, we need to update the
++     * mime fields to avoid confusing programs that may
++     * process the email.  However, we don't want to force
++     * those fields to appear in printouts. */
++    if (!print)
++      *chflags |= CH_MIME | CH_TXTPLAIN;
+   }
+   if (print)
diff --git a/0010-Reduce-line-buffer-size-in-mx_get_magic.patch b/0010-Reduce-line-buffer-size-in-mx_get_magic.patch
new file mode 100644 (file)
index 0000000..ab6e480
--- /dev/null
@@ -0,0 +1,34 @@
+From 9506db1e4a01fadf503b63381ab2b60dee32b836 Mon Sep 17 00:00:00 2001
+From: Kevin McCarthy <kevin@8t8.us>
+Date: Tue, 15 Oct 2019 15:26:20 +0800
+Subject: Reduce line buffer size in mx_get_magic()
+
+tmp is only used for mbox and mmdf iniital line examination.  That
+only needs to look at 6 characters from the first line.
+
+Upstream-commit: https://gitlab.com/muttmua/mutt/commit/9506db1e4a01fadf503b63381ab2b60dee32b836
+Co-authored-by:
+---
+ mx.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/mx.c b/mx.c
+index 472e8ee3..f693f0e0 100644
+--- a/mx.c
++++ b/mx.c
+@@ -391,7 +391,6 @@ int mx_get_magic(const char *path)
+ {
+   struct stat st;
+   int magic = 0;
+-  char tmp[PATH_MAX];
+   FILE *f;
+ #ifdef USE_IMAP
+@@ -437,6 +436,7 @@ int mx_get_magic(const char *path)
+     struct utimbuf times;
+ #endif /* HAVE_UTIMENSAT */
+     int ch;
++    char tmp[10];
+     /* Some mailbox creation tools erroneously append a blank line to
+      * a file before appending a mail message.  This allows mutt to
diff --git a/0011-Stable-branch-quick-fix-for-pager-change-mailbox-pus.patch b/0011-Stable-branch-quick-fix-for-pager-change-mailbox-pus.patch
new file mode 100644 (file)
index 0000000..cd18cff
--- /dev/null
@@ -0,0 +1,45 @@
+From a327386c5bf676a8321335fca849159ddd664ab9 Mon Sep 17 00:00:00 2001
+From: Kevin McCarthy <kevin@8t8.us>
+Date: Thu, 17 Oct 2019 15:48:31 +0800
+Subject: Stable branch quick fix for pager change-mailbox push/exec bug
+
+The menu functions mutt_push/pop_current_menu() keep track of the menu
+stack, automatically setting CurrentMenu when exiting menus.
+
+The only gotcha was the function sharing between the index and pager
+menus.  The index uses a hack, setting menu->menu to MENU_PAGER for
+operations redirecting through the index and back to the pager
+afterwards.
+
+I thought this was covered by the restoration of the menu before
+returning to the pager, or when exiting the index switch.  However it
+is not: invoking other menus, such as the browser, will result in
+CurrentMenu being set to the C_Pager when exiting those(by
+mutt_menu_pop_current()).  This can result in folder hooks failing for
+unshared functions.
+
+A better fix is to remove the hack of using menu->menu, because this
+can easily cause a problem in the future in other situations.(I will
+make this fix in master next.)  For the stable branch, I am explicitly
+setting/restoring CurrentMenu before invoking the folder hooks.
+
+Upstream-commit: https://gitlab.com/muttmua/mutt/commit/a327386c5bf676a8321335fca849159ddd664ab9
+Co-authored-by:
+---
+ index.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/index.c b/index.c
+index 32925f29..d57ce985 100644
+--- a/index.c
++++ b/index.c
+@@ -1371,6 +1371,9 @@ int mutt_index_menu(void)
+         mutt_sleep(0);
++        /* XXX: quick fix in stable branch.  Better fix will be in master */
++        CurrentMenu = MENU_MAIN;
++
+         /* Note that menu->menu may be MENU_PAGER if the change folder
+          * operation originated from the pager.
+          *
diff --git a/0012-Remove-menu-menu-hack-when-redirecting-pager-ops-thr.patch b/0012-Remove-menu-menu-hack-when-redirecting-pager-ops-thr.patch
new file mode 100644 (file)
index 0000000..bc37213
--- /dev/null
@@ -0,0 +1,313 @@
+From b15e8659514362e85114a6db4a89f799a9593787 Mon Sep 17 00:00:00 2001
+From: Kevin McCarthy <kevin@8t8.us>
+Date: Thu, 17 Oct 2019 17:30:03 +0800
+Subject: Remove menu->menu hack when redirecting pager ops through index
+
+As noted in commit a327386c, this causes problems when redirecting ops
+that open new menus.  There is no need to change the menu->menu type,
+as a flag works perfectly well with no such side effects.
+
+This also removes the need for comments explaning the hack before
+mutt_folder_hooks() when changing folders, or before displaying a
+message in the pager.
+
+Upstream-commit: https://gitlab.com/muttmua/mutt/commit/b15e8659514362e85114a6db4a89f799a9593787
+Co-authored-by:
+---
+ index.c | 81 ++++++++++++++++++++++-------------------------------
+ 1 file changed, 33 insertions(+), 48 deletions(-)
+
+diff --git a/index.c b/index.c
+index 9a0df384..35f106b0 100644
+--- a/index.c
++++ b/index.c
+@@ -622,6 +622,7 @@ int mutt_index_menu(void)
+   int do_buffy_notify = 1;
+   int close = 0; /* did we OP_QUIT or OP_EXIT out of this menu? */
+   int attach_msg = option(OptAttachMsg);
++  int in_pager = 0;  /* set when pager redirects a function through the index */
+   menu = mutt_menu_new(MENU_MAIN);
+   menu->make_entry = index_make_entry;
+@@ -746,7 +747,7 @@ int mutt_index_menu(void)
+     if (op >= 0)
+       mutt_curs_set(0);
+-    if (menu->menu == MENU_MAIN)
++    if (!in_pager)
+     {
+       index_menu_redraw(menu);
+@@ -908,7 +909,7 @@ int mutt_index_menu(void)
+         if (mutt_get_field(_("Jump to message: "), buf, sizeof(buf), 0) != 0
+             || !buf[0])
+         {
+-          if (menu->menu == MENU_PAGER)
++          if (in_pager)
+           {
+             op = OP_DISPLAY_MESSAGE;
+             continue;
+@@ -941,7 +942,7 @@ int mutt_index_menu(void)
+           if (j >= 0)
+           {
+             menu->current = Context->hdrs[j]->virtual;
+-            if (menu->menu == MENU_PAGER)
++            if (in_pager)
+             {
+               op = OP_DISPLAY_MESSAGE;
+               continue;
+@@ -1094,7 +1095,7 @@ int mutt_index_menu(void)
+             resort_index(menu);
+             set_option(OptSearchInvalid);
+           }
+-          if (menu->menu == MENU_PAGER)
++          if (in_pager)
+           {
+             op = OP_DISPLAY_MESSAGE;
+             continue;
+@@ -1248,7 +1249,7 @@ int mutt_index_menu(void)
+           FREE(&Context);
+         /* if we were in the pager, redisplay the message */
+-        if (menu->menu == MENU_PAGER)
++        if (in_pager)
+         {
+           op = OP_DISPLAY_MESSAGE;
+           continue;
+@@ -1316,7 +1317,7 @@ int mutt_index_menu(void)
+           if (mutt_buffer_enter_fname(cp, folderbuf, 1) == -1)
+           {
+-            if (menu->menu == MENU_PAGER)
++            if (in_pager)
+             {
+               op = OP_DISPLAY_MESSAGE;
+               cont = 1;
+@@ -1380,17 +1381,6 @@ int mutt_index_menu(void)
+         mutt_sleep(0);
+-        /* XXX: quick fix in stable branch.  Better fix will be in master */
+-        CurrentMenu = MENU_MAIN;
+-
+-        /* Note that menu->menu may be MENU_PAGER if the change folder
+-         * operation originated from the pager.
+-         *
+-         * However, exec commands currently use CurrentMenu to determine what
+-         * functions are available, which is automatically set by the
+-         * mutt_push/pop_current_menu() functions.  If that changes, the menu
+-         * would need to be reset here, and the pager cleanup code after the
+-         * switch statement would need to be run. */
+         mutt_folder_hook(mutt_b2s(folderbuf));
+         if ((Context = mx_open_mailbox(mutt_b2s(folderbuf),
+@@ -1451,11 +1441,6 @@ int mutt_index_menu(void)
+         if (option(C_PgpAutoDecode) && (tag || !(CURHDR->security & PGP_TRADITIONAL_CHECKED)))
+           mutt_check_traditional_pgp(tag ? NULL : CURHDR, &menu->redraw);
+-        /* If we are returning to the pager via an index menu redirection, we
+-         * need to reset the menu->menu.  Otherwise mutt_menu_pop_current() will
+-         * set CurrentMenu incorrectly when we return back to the index menu. */
+-        menu->menu = MENU_MAIN;
+-
+         if ((op = mutt_display_message(CURHDR)) < 0)
+         {
+           unset_option(OptNeedResort);
+@@ -1463,22 +1448,22 @@ int mutt_index_menu(void)
+         }
+         /* This is used to redirect a single operation back here afterwards.  If
+-         * mutt_display_message() returns 0, then the menu and pager state will
++         * mutt_display_message() returns 0, then this flag and pager state will
+          * be cleaned up after this switch statement. */
+-        menu->menu = MENU_PAGER;
++        in_pager = 1;
+          menu->oldcurrent = menu->current;
+         continue;
+       case OP_EXIT:
+         close = op;
+-        if (menu->menu == MENU_MAIN && attach_msg)
++        if (!in_pager && attach_msg)
+         {
+           done = 1;
+           break;
+         }
+-        if ((menu->menu == MENU_MAIN)
++        if ((!in_pager)
+             && (query_quadoption(C_Quit,
+                                   _("Exit Mutt without saving?")) == MUTT_YES))
+         {
+@@ -1512,7 +1497,7 @@ int mutt_index_menu(void)
+           Context->changed = 1;
+           mutt_message _("Thread broken");
+-          if (menu->menu == MENU_PAGER)
++          if (in_pager)
+           {
+             op = OP_DISPLAY_MESSAGE;
+             continue;
+@@ -1556,7 +1541,7 @@ int mutt_index_menu(void)
+             mutt_error _("No thread linked");
+         }
+-        if (menu->menu == MENU_PAGER)
++        if (in_pager)
+         {
+           op = OP_DISPLAY_MESSAGE;
+           continue;
+@@ -1573,7 +1558,7 @@ int mutt_index_menu(void)
+         CHECK_ATTACH;
+         mutt_edit_content_type(CURHDR, CURHDR->content, NULL);
+         /* if we were in the pager, redisplay the message */
+-        if (menu->menu == MENU_PAGER)
++        if (in_pager)
+         {
+           op = OP_DISPLAY_MESSAGE;
+           continue;
+@@ -1588,17 +1573,17 @@ int mutt_index_menu(void)
+         CHECK_VISIBLE;
+         if (menu->current >= Context->vcount - 1)
+         {
+-          if (menu->menu == MENU_MAIN)
++          if (!in_pager)
+             mutt_error _("You are on the last message.");
+           break;
+         }
+         if ((menu->current = ci_next_undeleted(menu->current)) == -1)
+         {
+           menu->current = menu->oldcurrent;
+-          if (menu->menu == MENU_MAIN)
++          if (!in_pager)
+             mutt_error _("No undeleted messages.");
+         }
+-        else if (menu->menu == MENU_PAGER)
++        else if (in_pager)
+         {
+           op = OP_DISPLAY_MESSAGE;
+           continue;
+@@ -1613,12 +1598,12 @@ int mutt_index_menu(void)
+         CHECK_VISIBLE;
+         if (menu->current >= Context->vcount - 1)
+         {
+-          if (menu->menu == MENU_MAIN)
++          if (!in_pager)
+             mutt_error _("You are on the last message.");
+           break;
+         }
+         menu->current++;
+-        if (menu->menu == MENU_PAGER)
++        if (in_pager)
+         {
+           op = OP_DISPLAY_MESSAGE;
+           continue;
+@@ -1639,10 +1624,10 @@ int mutt_index_menu(void)
+         if ((menu->current = ci_previous_undeleted(menu->current)) == -1)
+         {
+           menu->current = menu->oldcurrent;
+-          if (menu->menu == MENU_MAIN)
++          if (!in_pager)
+             mutt_error _("No undeleted messages.");
+         }
+-        else if (menu->menu == MENU_PAGER)
++        else if (in_pager)
+         {
+           op = OP_DISPLAY_MESSAGE;
+           continue;
+@@ -1657,11 +1642,11 @@ int mutt_index_menu(void)
+         CHECK_VISIBLE;
+         if (menu->current < 1)
+         {
+-          if (menu->menu == MENU_MAIN) mutt_error _("You are on the first message.");
++          if (!in_pager) mutt_error _("You are on the first message.");
+           break;
+         }
+         menu->current--;
+-        if (menu->menu == MENU_PAGER)
++        if (in_pager)
+         {
+           op = OP_DISPLAY_MESSAGE;
+           continue;
+@@ -1797,7 +1782,7 @@ int mutt_index_menu(void)
+               mutt_error(_("No unread messages."));
+           }
+         }
+-        else if (menu->menu == MENU_PAGER)
++        else if (in_pager)
+         {
+           op = OP_DISPLAY_MESSAGE;
+           continue;
+@@ -1895,7 +1880,7 @@ int mutt_index_menu(void)
+         CHECK_IN_MAILBOX;
+         if (mx_toggle_write(Context) == 0)
+         {
+-          if (menu->menu == MENU_PAGER)
++          if (in_pager)
+           {
+             op = OP_DISPLAY_MESSAGE;
+             continue;
+@@ -1939,7 +1924,7 @@ int mutt_index_menu(void)
+           else
+             mutt_error _("You are on the first thread.");
+         }
+-        else if (menu->menu == MENU_PAGER)
++        else if (in_pager)
+         {
+           op = OP_DISPLAY_MESSAGE;
+           continue;
+@@ -1959,7 +1944,7 @@ int mutt_index_menu(void)
+         {
+           menu->current = menu->oldcurrent;
+         }
+-        else if (menu->menu == MENU_PAGER)
++        else if (in_pager)
+         {
+           op = OP_DISPLAY_MESSAGE;
+           continue;
+@@ -2146,7 +2131,7 @@ int mutt_index_menu(void)
+               menu->current = menu->oldcurrent;
+               menu->redraw |= REDRAW_CURRENT;
+             }
+-            else if (menu->menu == MENU_PAGER)
++            else if (in_pager)
+             {
+               op = OP_DISPLAY_MESSAGE;
+               continue;
+@@ -2285,7 +2270,7 @@ int mutt_index_menu(void)
+         if (tag || !(CURHDR->security & PGP_TRADITIONAL_CHECKED))
+           mutt_check_traditional_pgp(tag ? NULL : CURHDR, &menu->redraw);
+-        if (menu->menu == MENU_PAGER)
++        if (in_pager)
+         {
+           op = OP_DISPLAY_MESSAGE;
+           continue;
+@@ -2347,7 +2332,7 @@ int mutt_index_menu(void)
+             if ((menu->current =(op == OP_MAIN_READ_THREAD ?
+                                   mutt_next_thread(CURHDR) : mutt_next_subthread(CURHDR))) == -1)
+               menu->current = menu->oldcurrent;
+-            else if (menu->menu == MENU_PAGER)
++            else if (in_pager)
+             {
+               op = OP_DISPLAY_MESSAGE;
+               continue;
+@@ -2584,14 +2569,14 @@ int mutt_index_menu(void)
+ #endif
+       default:
+-        if (menu->menu == MENU_MAIN)
++        if (!in_pager)
+           km_error_key(MENU_MAIN);
+     }
+-    if (menu->menu == MENU_PAGER)
++    if (in_pager)
+     {
+       mutt_clear_pager_position();
+-      menu->menu = MENU_MAIN;
++      in_pager = 0;
+       menu->redraw = REDRAW_FULL;
+     }
diff --git a/0013-Clean-up-pager-change-folder-aborts-to-return-to-pag.patch b/0013-Clean-up-pager-change-folder-aborts-to-return-to-pag.patch
new file mode 100644 (file)
index 0000000..4c4a180
--- /dev/null
@@ -0,0 +1,74 @@
+From e750e39c4a80bb98e98c5fc38500e0897a3e0279 Mon Sep 17 00:00:00 2001
+From: Kevin McCarthy <kevin@8t8.us>
+Date: Thu, 17 Oct 2019 18:34:55 +0800
+Subject: Clean up pager change folder aborts to return to pager
+
+Changing folder from within the pager behaved inconsistently when
+aborting, or upon a normal error.  It would sometimes return the pager
+and other times return to the index.
+
+Ensure it returns to the pager, except in the case where
+mx_close_mailbox() fails due to a new mail or reopen event.  In that
+case we likely want to be in the index - the message we were viewing
+could have disappeared or relocated.
+
+Upstream-commit: https://gitlab.com/muttmua/mutt/commit/e750e39c4a80bb98e98c5fc38500e0897a3e0279
+Co-authored-by:
+---
+ index.c | 20 +++++++++-----------
+ 1 file changed, 9 insertions(+), 11 deletions(-)
+
+diff --git a/index.c b/index.c
+index 35f106b0..0f93df3f 100644
+--- a/index.c
++++ b/index.c
+@@ -1277,7 +1277,7 @@ int mutt_index_menu(void)
+       case OP_MAIN_CHANGE_FOLDER_READONLY:
+       {
+         struct Buffer *folderbuf;
+-        int cont = 0;  /* Set if we want to continue instead of break */
++        int pager_return = 1;  /* return to display message in pager */
+         folderbuf = mutt_buffer_pool_get();
+@@ -1316,14 +1316,7 @@ int mutt_index_menu(void)
+           mutt_buffer_buffy(folderbuf);
+           if (mutt_buffer_enter_fname(cp, folderbuf, 1) == -1)
+-          {
+-            if (in_pager)
+-            {
+-              op = OP_DISPLAY_MESSAGE;
+-              cont = 1;
+-            }
+             goto changefoldercleanup;
+-          }
+         }
+         if (!mutt_buffer_len(folderbuf))
+@@ -1338,6 +1331,9 @@ int mutt_index_menu(void)
+           goto changefoldercleanup;
+         }
++        /* past this point, we don't return to the pager on error */
++        pager_return = 0;
++
+         /* keepalive failure in mutt_enter_fname may kill connection. #3028 */
+         if (Context && !Context->path)
+           FREE(&Context);
+@@ -1410,10 +1406,12 @@ int mutt_index_menu(void)
+       changefoldercleanup:
+         mutt_buffer_pool_release(&folderbuf);
+-        if (cont)
++        if (in_pager && pager_return)
++        {
++          op = OP_DISPLAY_MESSAGE;
+           continue;
+-        else
+-          break;
++        }
++        break;
+       }
+       case OP_DISPLAY_MESSAGE:
diff --git a/0014-Add-typelen-parameter-to-rfc1524_mailcap_lookup.patch b/0014-Add-typelen-parameter-to-rfc1524_mailcap_lookup.patch
new file mode 100644 (file)
index 0000000..79fabe7
--- /dev/null
@@ -0,0 +1,195 @@
+From 12cd2d529c8462268c3958e4ae2bab25cee92ae7 Mon Sep 17 00:00:00 2001
+From: Kevin McCarthy <kevin@8t8.us>
+Date: Wed, 23 Oct 2019 19:17:09 +0800
+Subject: Add typelen parameter to rfc1524_mailcap_lookup()
+
+Because of mime_lookup commands, the call to mutt_check_lookup_list()
+inside the function can modify the passed in type.  Add an explicit
+length parameter to the function, rather than assume the parameter
+size.  This also makes it more evident the type parameter can be
+modified to callers.
+
+Change the len parameter to mutt_check_lookup_list() to type size_t,
+just to be correct about it.
+
+Upstream-commit: https://gitlab.com/muttmua/mutt/commit/12cd2d529c8462268c3958e4ae2bab25cee92ae7
+Co-authored-by:
+---
+ email/attach.c     | 14 +++++++-------
+ handler.c    |  6 +++---
+ protos.h     |  2 +-
+ recvattach.c |  5 +++--
+ mailcap.c    |  5 ++---
+ mailcap.h    |  2 +-
+ 6 files changed, 17 insertions(+), 17 deletions(-)
+
+diff --git a/email/attach.c b/email/attach.c
+index de32d1fb..5e9906f5 100644
+--- a/email/attach.c
++++ b/email/attach.c
+@@ -58,7 +58,7 @@ int mutt_get_tmp_attachment(struct Body *a)
+   entry = rfc1524_new_entry();
+   snprintf(type, sizeof(type), "%s/%s", TYPE(a), a->subtype);
+-  rfc1524_mailcap_lookup(a, type, entry, 0);
++  rfc1524_mailcap_lookup(a, type, sizeof(type), entry, 0);
+   mutt_rfc1524_expand_filename(entry->nametemplate, a->filename, tempfile);
+   rfc1524_free_entry(&entry);
+@@ -103,7 +103,7 @@ int mutt_compose_attachment(struct Body *a)
+   int rc = 0;
+   snprintf(type, sizeof(type), "%s/%s", TYPE(a), a->subtype);
+-  if (rfc1524_mailcap_lookup(a, type, entry, MUTT_COMPOSE))
++  if (rfc1524_mailcap_lookup(a, type, sizeof(type), entry, MUTT_COMPOSE))
+   {
+     if (entry->composecommand || entry->composetypecommand)
+     {
+@@ -239,7 +239,7 @@ int mutt_edit_attachment(struct Body *a)
+   int rc = 0;
+   snprintf(type, sizeof(type), "%s/%s", TYPE(a), a->subtype);
+-  if (rfc1524_mailcap_lookup(a, type, entry, MUTT_EDIT))
++  if (rfc1524_mailcap_lookup(a, type, sizeof(type), entry, MUTT_EDIT))
+   {
+     if (entry->editcommand)
+     {
+@@ -303,7 +303,7 @@ bailout:
+ }
+-void mutt_check_lookup_list(struct Body *b, char *type, int len)
++void mutt_check_lookup_list(struct Body *b, char *type, size_t len)
+ {
+   struct ListHead *t = MimeLookupList;
+   int i;
+@@ -374,7 +374,7 @@ int mutt_view_attachment(FILE *fp, struct Body *a, int flag, struct Email *hdr,
+   if (use_mailcap)
+   {
+     entry = rfc1524_new_entry();
+-    if (!rfc1524_mailcap_lookup(a, type, entry, 0))
++    if (!rfc1524_mailcap_lookup(a, type, sizeof(type), entry, 0))
+     {
+       if (flag == MUTT_REGULAR)
+       {
+@@ -929,7 +929,7 @@ int mutt_print_attachment(FILE *fp, struct Body *a)
+   snprintf(type, sizeof(type), "%s/%s", TYPE(a), a->subtype);
+-  if (rfc1524_mailcap_lookup(a, type, NULL, MUTT_PRINT))
++  if (rfc1524_mailcap_lookup(a, type, sizeof(type), NULL, MUTT_PRINT))
+   {
+     struct MailcapEntry *entry = NULL;
+     int piped = FALSE;
+@@ -937,7 +937,7 @@ int mutt_print_attachment(FILE *fp, struct Body *a)
+     mutt_debug(LL_DEBUG2, "Using mailcap...\n"));
+     entry = rfc1524_new_entry();
+-    rfc1524_mailcap_lookup(a, type, entry, MUTT_PRINT);
++    rfc1524_mailcap_lookup(a, type, sizeof(type), entry, MUTT_PRINT);
+     mutt_rfc1524_expand_filename(entry->nametemplate, a->filename,
+                                   newfile);
+diff --git a/handler.c b/handler.c
+index 5a0766a8..5e613d41 100644
+--- a/handler.c
++++ b/handler.c
+@@ -955,7 +955,7 @@ static int is_mmnoask(const char *buf)
+  */
+ static int is_autoview(struct Body *b)
+ {
+-  char type[128];
++  char type[256];
+   int is_autoview = 0;
+   snprintf(type, sizeof(type), "%s/%s", TYPE(b), b->subtype);
+@@ -988,7 +988,7 @@ static int is_autoview(struct Body *b)
+    *
+    * WARNING: type is altered by this call as a result of `mime_lookup' support */
+   if (is_autoview)
+-    return rfc1524_mailcap_lookup(b, type, NULL, MUTT_AUTOVIEW);
++    return rfc1524_mailcap_lookup(b, type, sizeof(type), NULL, MUTT_AUTOVIEW);
+   return 0;
+ }
+@@ -1320,7 +1320,7 @@ static int autoview_handler(struct Body *a, struct State *s)
+   tempfile = mutt_buffer_pool_get();
+   snprintf(type, sizeof(type), "%s/%s", TYPE(a), a->subtype);
+-  rfc1524_mailcap_lookup(a, type, entry, MUTT_AUTOVIEW);
++  rfc1524_mailcap_lookup(a, type, sizeof(type), entry, MUTT_AUTOVIEW);
+   fname = mutt_str_strdup(a->filename);
+   mutt_file_sanitize_filename(fname, 1);
+diff --git a/protos.h b/protos.h
+index 8ee932c8..759acc4f 100644
+--- a/protos.h
++++ b/protos.h
+@@ -236,7 +236,7 @@ const char *mutt_getcwd(struct Buffer *);
+ void mutt_help(int);
+ const char *mutt_idxfmt_hook(const char *, struct Context *, struct Email *);
+ void mutt_draw_tree(struct Context *);
+-void mutt_check_lookup_list(struct Body *, char *, int);
++void mutt_check_lookup_list(struct Body *, char *, size_t);
+ void mutt_make_attribution(struct Context *ctx, struct Email *cur, FILE *out);
+ void mutt_make_forward_subject(struct Envelope *env, struct Context *ctx, struct Email *cur);
+ void mutt_make_help(char *, size_t, const char *, int, int);
+diff --git a/recvattach.c b/recvattach.c
+index 087858c9..c2da1785 100644
+--- a/recvattach.c
++++ b/recvattach.c
+@@ -741,7 +741,7 @@ static int can_print(ATTACH_CONTEXT *actx, struct Body *top, int tag)
+     snprintf(type, sizeof(type), "%s/%s", TYPE(top), top->subtype);
+     if (!tag || top->tagged)
+     {
+-      if (!rfc1524_mailcap_lookup(top, type, NULL, MUTT_PRINT))
++      if (!rfc1524_mailcap_lookup(top, type, sizeof(type), NULL, MUTT_PRINT))
+       {
+         if (mutt_str_strcasecmp("text/plain", top->subtype) &&
+             mutt_str_strcasecmp("application/postscript", top->subtype))
+@@ -775,7 +775,8 @@ static void print_attachment_list(ATTACH_CONTEXT *actx, FILE *fp, int tag, struct Body
+     if (!tag || top->tagged)
+     {
+       snprintf(type, sizeof(type), "%s/%s", TYPE(top), top->subtype);
+-      if (!option(C_AttachSplit) && !rfc1524_mailcap_lookup(top, type, NULL, MUTT_PRINT))
++      if (!option(C_AttachSplit) &&
++          !rfc1524_mailcap_lookup(top, type, sizeof(type), NULL, MUTT_PRINT))
+       {
+         if (!mutt_str_strcasecmp("text/plain", top->subtype) ||
+             !mutt_str_strcasecmp("application/postscript", top->subtype))
+diff --git a/mailcap.c b/mailcap.c
+index 6597df2b..2fc9f175 100644
+--- a/mailcap.c
++++ b/mailcap.c
+@@ -420,7 +420,7 @@ void rfc1524_free_entry(struct MailcapEntry **entry)
+  * in *entry, and returns 1.  On failure(not found), returns 0.
+  * If entry == NULL just return 1 if the given type is found.
+  */
+-int rfc1524_mailcap_lookup(struct Body *a, char *type, struct MailcapEntry *entry, int opt)
++int rfc1524_mailcap_lookup(struct Body *a, char *type, size_t typelen, struct MailcapEntry *entry, int opt)
+ {
+   struct Buffer *path = NULL;
+   int found = FALSE;
+@@ -438,8 +438,7 @@ int rfc1524_mailcap_lookup(struct Body *a, char *type, struct MailcapEntry *entry, int opt)
+     return 0;
+   }
+-  /* FIXME: sizeof type should be passed to rfc1524_mailcap_lookup() */
+-  mutt_check_lookup_list(a, type, 128);
++  mutt_check_lookup_list(a, type, typelen);
+   path = mutt_buffer_pool_get();
+diff --git a/mailcap.h b/mailcap.h
+index 87b06d6d..bdf0468e 100644
+--- a/mailcap.h
++++ b/mailcap.h
+@@ -38,7 +38,7 @@ struct MailcapEntry *rfc1524_new_entry(void);
+ void rfc1524_free_entry(struct MailcapEntry **);
+ int mutt_rfc1524_expand_command(struct Body *, const char *, const char *, struct Buffer *);
+ void mutt_rfc1524_expand_filename(const char *, const char *, struct Buffer *);
+-int rfc1524_mailcap_lookup(struct Body *, char *, struct MailcapEntry *, int);
++int rfc1524_mailcap_lookup(struct Body *, char *, size_t, struct MailcapEntry *, int);
+ int mutt_file_rename(const char *, const char *);
+ #endif /* _RFC1524_H */
diff --git a/0015-Convert-mutt_message_to_7bit-to-use-buffer.patch b/0015-Convert-mutt_message_to_7bit-to-use-buffer.patch
new file mode 100644 (file)
index 0000000..f37e322
--- /dev/null
@@ -0,0 +1,96 @@
+From 671e653c6b5d70adeb644a36961bdc2c8d2f2e9f Mon Sep 17 00:00:00 2001
+From: Kevin McCarthy <kevin@8t8.us>
+Date: Fri, 25 Oct 2019 15:28:25 +0800
+Subject: Convert mutt_message_to_7bit() to use buffer
+
+Clean up the error handling a bit.
+
+Because of the recursive invocation, avoid the buffer pool in this
+case.
+
+Upstream-commit: https://gitlab.com/muttmua/mutt/commit/671e653c6b5d70adeb644a36961bdc2c8d2f2e9f
+Co-authored-by:
+---
+ sendlib.c | 36 ++++++++++++++++++++++--------------
+ 1 file changed, 22 insertions(+), 14 deletions(-)
+
+diff --git a/sendlib.c b/sendlib.c
+index 72ac3a93..de2cf78f 100644
+--- a/sendlib.c
++++ b/sendlib.c
+@@ -1072,8 +1072,7 @@ bye:
+ void mutt_message_to_7bit(struct Body *a, FILE *fp)
+ {
+-  char temp[PATH_MAX];
+-  char *line = NULL;
++  struct Buffer *temp = NULL;
+   FILE *fpin = NULL;
+   FILE *fpout = NULL;
+   struct stat sb;
+@@ -1092,12 +1091,15 @@ void mutt_message_to_7bit(struct Body *a, FILE *fp)
+     {
+       mutt_perror("stat");
+       mutt_file_fclose(&fpin);
++      goto cleanup;
+     }
+     a->length = sb.st_size;
+   }
+-  mutt_mktemp(temp, sizeof(temp));
+-  if (!(fpout = mutt_file_fopen(temp, "w+")))
++  /* Avoid buffer pool due to recursion */
++  temp = mutt_buffer_new();
++  mutt_buffer_mktemp(temp);
++  if (!(fpout = mutt_file_fopen(mutt_b2s(temp), "w+")))
+   {
+     mutt_perror("fopen");
+     goto cleanup;
+@@ -1116,31 +1118,37 @@ void mutt_message_to_7bit(struct Body *a, FILE *fp)
+   fputc('\n', fpout);
+   mutt_write_mime_body(a->parts, fpout);
+-cleanup:
+-  FREE(&line);
+-
+-  if (fpin && fpin != fp)
++  if (fpin != fp)
+     mutt_file_fclose(&fpin);
+-  if (fpout)
+-    mutt_file_fclose(&fpout);
+-  else
+-    return;
++  mutt_file_fclose(&fpout);
+   a->encoding = ENC_7BIT;
+   FREE(&a->d_filename);
+   a->d_filename = a->filename;
+   if (a->filename && a->unlink)
+     unlink(a->filename);
+-  a->filename = mutt_str_strdup(temp);
++  a->filename = mutt_str_strdup(mutt_b2s(temp));
+   a->unlink = 1;
+   if (stat(a->filename, &sb) == -1)
+   {
+     mutt_perror("stat");
+-    return;
++    goto cleanup;
+   }
+   a->length = sb.st_size;
+   mutt_body_free(&a->parts);
+   a->hdr->content = NULL;
++
++cleanup:
++  if (fpin && fpin != fp)
++    mutt_file_fclose(&fpin);
++
++  if (fpout)
++  {
++    mutt_file_fclose(&fpout);
++    mutt_file_unlink(mutt_b2s(temp));
++  }
++
++  mutt_buffer_free(&temp);
+ }
+ static void transform_to_7bit(struct Body *a, FILE *fpin)
diff --git a/0016-Convert-transform_to_7bit-to-use-buffer-pool.patch b/0016-Convert-transform_to_7bit-to-use-buffer-pool.patch
new file mode 100644 (file)
index 0000000..bbb04da
--- /dev/null
@@ -0,0 +1,51 @@
+From 3565f686bf929fe91ca07d683d99125b5a8b2c21 Mon Sep 17 00:00:00 2001
+From: Kevin McCarthy <kevin@8t8.us>
+Date: Fri, 25 Oct 2019 15:55:49 +0800
+Subject: Convert transform_to_7bit() to use buffer pool
+
+Upstream-commit: https://gitlab.com/muttmua/mutt/commit/3565f686bf929fe91ca07d683d99125b5a8b2c21
+Co-authored-by:
+---
+ sendlib.c | 13 +++++++++----
+ 1 file changed, 9 insertions(+), 4 deletions(-)
+
+diff --git a/sendlib.c b/sendlib.c
+index de2cf78f..5850da3e 100644
+--- a/sendlib.c
++++ b/sendlib.c
+@@ -1153,7 +1153,7 @@ cleanup:
+ static void transform_to_7bit(struct Body *a, FILE *fpin)
+ {
+-  char buff[PATH_MAX];
++  struct Buffer *buff;
+   struct State s;
+   struct stat sb;
+@@ -1176,10 +1176,14 @@ static void transform_to_7bit(struct Body *a, FILE *fpin)
+       a->noconv = 1;
+       a->force_charset = 1;
+-      mutt_mktemp(buff, sizeof(buff));
+-      if ((s.fpout = mutt_file_fopen(buff, "w")) == NULL)
++      /* Because of the potential recursion in message types, we
++       * restrict the lifetime of the buffer tightly */
++      buff = mutt_buffer_pool_get();
++      mutt_buffer_mktemp(buff);
++      if ((s.fpout = mutt_file_fopen(mutt_b2s(buff), "w")) == NULL)
+       {
+         mutt_perror("fopen");
++        mutt_buffer_pool_release(&buff);
+         return;
+       }
+       s.fpin = fpin;
+@@ -1187,7 +1191,8 @@ static void transform_to_7bit(struct Body *a, FILE *fpin)
+       mutt_file_fclose(&s.fpout);
+       FREE(&a->d_filename);
+       a->d_filename = a->filename;
+-      a->filename = mutt_str_strdup(buff);
++      a->filename = mutt_str_strdup(mutt_b2s(buff));
++      mutt_buffer_pool_release(&buff);
+       a->unlink = 1;
+       if (stat(a->filename, &sb) == -1)
+       {
diff --git a/0017-Convert-send_msg-sendmail_wait-to-use-buffer-pool.patch b/0017-Convert-send_msg-sendmail_wait-to-use-buffer-pool.patch
new file mode 100644 (file)
index 0000000..455d53a
--- /dev/null
@@ -0,0 +1,31 @@
+From 8de05d5ffc86c1a9dad61e83189a74331c2869b9 Mon Sep 17 00:00:00 2001
+From: Kevin McCarthy <kevin@8t8.us>
+Date: Fri, 25 Oct 2019 19:13:50 +0800
+Subject: Convert send_msg() $sendmail_wait to use buffer pool
+
+Upstream-commit: https://gitlab.com/muttmua/mutt/commit/8de05d5ffc86c1a9dad61e83189a74331c2869b9
+Co-authored-by:
+---
+ sendlib.c | 8 +++++---
+ 1 file changed, 5 insertions(+), 3 deletions(-)
+
+diff --git a/sendlib.c b/sendlib.c
+index 5850da3e..0b1766ce 100644
+--- a/sendlib.c
++++ b/sendlib.c
+@@ -2268,10 +2268,12 @@ send_msg(const char *path, char **args, const char *msg, char **tempfile)
+   if (C_SendmailWait >= 0 && tempfile)
+   {
+-    char tmp[PATH_MAX];
++    struct Buffer *tmp;
+-    mutt_mktemp(tmp, sizeof(tmp));
+-    *tempfile = mutt_str_strdup(tmp);
++    tmp = mutt_buffer_pool_get();
++    mutt_buffer_mktemp(tmp);
++    *tempfile = mutt_str_strdup(mutt_b2s(tmp));
++    mutt_buffer_pool_release(&tmp);
+   }
+   if ((pid = fork()) == 0)
diff --git a/0018-Convert-_mutt_bounce_message-to-use-buffer-pool.patch b/0018-Convert-_mutt_bounce_message-to-use-buffer-pool.patch
new file mode 100644 (file)
index 0000000..00a7b94
--- /dev/null
@@ -0,0 +1,56 @@
+From 58a8189770149329507f4d64224a8c6cc26b09f2 Mon Sep 17 00:00:00 2001
+From: Kevin McCarthy <kevin@8t8.us>
+Date: Fri, 25 Oct 2019 19:29:56 +0800
+Subject: Convert _mutt_bounce_message to use buffer pool
+
+Upstream-commit: https://gitlab.com/muttmua/mutt/commit/58a8189770149329507f4d64224a8c6cc26b09f2
+Co-authored-by:
+---
+ sendlib.c | 14 +++++++++-----
+ 1 file changed, 9 insertions(+), 5 deletions(-)
+
+diff --git a/sendlib.c b/sendlib.c
+index 0b1766ce..2d5d221a 100644
+--- a/sendlib.c
++++ b/sendlib.c
+@@ -2633,7 +2633,8 @@ static int _mutt_bounce_message(FILE *fp, struct Email *h, struct Address *to, const char *r
+ {
+   int i, ret = 0;
+   FILE *f;
+-  char date[128], tempfile[PATH_MAX];
++  char date[128];
++  struct Buffer *tempfile;
+   struct Message *msg = NULL;
+   if (!h)
+@@ -2651,8 +2652,9 @@ static int _mutt_bounce_message(FILE *fp, struct Email *h, struct Address *to, const char *r
+   if (!fp) fp = msg->fp;
+-  mutt_mktemp(tempfile, sizeof(tempfile));
+-  if ((f = mutt_file_fopen(tempfile, "w")) != NULL)
++  tempfile = mutt_buffer_pool_get();
++  mutt_buffer_mktemp(tempfile);
++  if ((f = mutt_file_fopen(mutt_b2s(tempfile), "w")) != NULL)
+   {
+     int ch_flags = CH_XMIT | CH_NONEWLINE | CH_NOQFROM;
+     char* msgid_str;
+@@ -2675,14 +2677,16 @@ static int _mutt_bounce_message(FILE *fp, struct Email *h, struct Address *to, const char *r
+ #if USE_SMTP
+     if (C_SmtpUrl)
+-      ret = mutt_smtp_send(env_from, to, NULL, NULL, tempfile,
++      ret = mutt_smtp_send(env_from, to, NULL, NULL, mutt_b2s(tempfile),
+                             h->content->encoding == ENC_8BIT);
+     else
+ #endif /* USE_SMTP */
+-      ret = mutt_invoke_sendmail(env_from, to, NULL, NULL, tempfile,
++      ret = mutt_invoke_sendmail(env_from, to, NULL, NULL, mutt_b2s(tempfile),
+                                   h->content->encoding == ENC_8BIT);
+   }
++  mutt_buffer_pool_release(&tempfile);
++
+   if (msg)
+     mx_close_message(Context, &msg);
diff --git a/0019-Convert-mutt_write_fcc-to-use-buffer-pool.patch b/0019-Convert-mutt_write_fcc-to-use-buffer-pool.patch
new file mode 100644 (file)
index 0000000..bc8b6cc
--- /dev/null
@@ -0,0 +1,108 @@
+From 7e29eb237f17bd573a93c6e5f71bfe87f92909c7 Mon Sep 17 00:00:00 2001
+From: Kevin McCarthy <kevin@8t8.us>
+Date: Sat, 26 Oct 2019 19:36:33 +0800
+Subject: Convert mutt_write_fcc() to use buffer pool
+
+Upstream-commit: https://gitlab.com/muttmua/mutt/commit/7e29eb237f17bd573a93c6e5f71bfe87f92909c7
+Co-authored-by:
+---
+ sendlib.c | 35 ++++++++++++++++++++++-------------
+ 1 file changed, 22 insertions(+), 13 deletions(-)
+
+diff --git a/sendlib.c b/sendlib.c
+index 2d5d221a..cc7a8145 100644
+--- a/sendlib.c
++++ b/sendlib.c
+@@ -2807,9 +2807,9 @@ int mutt_write_fcc(const char *path, struct Email *hdr, const char *msgid, int post,
+ {
+   struct Context f;
+   struct Message *msg;
+-  char tempfile[PATH_MAX];
++  struct Buffer *tempfile = NULL;
+   FILE *tempfp = NULL;
+-  int r, need_buffy_cleanup = 0;
++  int r = -1, need_buffy_cleanup = 0;
+   struct stat st;
+   char buf[128];
+   int onm_flags;
+@@ -2821,7 +2821,7 @@ int mutt_write_fcc(const char *path, struct Email *hdr, const char *msgid, int post,
+   {
+     mutt_debug(LL_DEBUG1, "mutt_write_fcc(): unable to open mailbox %s in append-mode, aborting.\n",
+                 path));
+-    return (-1);
++    goto cleanup;
+   }
+   /* We need to add a Content-Length field to avoid problems where a line in
+@@ -2829,12 +2829,13 @@ int mutt_write_fcc(const char *path, struct Email *hdr, const char *msgid, int post,
+    */
+   if (f.magic == MUTT_MMDF || f.magic == MUTT_MBOX)
+   {
+-    mutt_mktemp(tempfile, sizeof(tempfile));
+-    if ((tempfp = mutt_file_fopen(tempfile, "w+")) == NULL)
++    tempfile = mutt_buffer_pool_get();
++    mutt_buffer_mktemp(tempfile);
++    if ((tempfp = mutt_file_fopen(mutt_b2s(tempfile), "w+")) == NULL)
+     {
+-      mutt_perror(tempfile);
++      mutt_perror(mutt_b2s(tempfile));
+       mx_close_mailbox(&f, NULL);
+-      return (-1);
++      goto cleanup;
+     }
+     /* remember new mail status before appending message */
+     need_buffy_cleanup = 1;
+@@ -2848,7 +2849,7 @@ int mutt_write_fcc(const char *path, struct Email *hdr, const char *msgid, int post,
+   if ((msg = mx_open_new_message(&f, hdr, onm_flags)) == NULL)
+   {
+     mx_close_mailbox(&f, NULL);
+-    return (-1);
++    goto cleanup;
+   }
+   /* post == 1 => postpone message.
+@@ -2971,13 +2972,13 @@ int mutt_write_fcc(const char *path, struct Email *hdr, const char *msgid, int post,
+     fflush(tempfp);
+     if (ferror(tempfp))
+     {
+-      mutt_debug(LL_DEBUG1, "mutt_write_fcc(): %s: write failed.\n", tempfile));
++      mutt_debug(LL_DEBUG1, "mutt_write_fcc(): %s: write failed.\n", mutt_b2s(tempfile)));
+       mutt_file_fclose(&tempfp);
+-      unlink(tempfile);
++      unlink(mutt_b2s(tempfile));
+       mx_commit_message(msg, &f);        /* XXX - really? */
+       mx_close_message(&f, &msg);
+       mx_close_mailbox(&f, NULL);
+-      return -1;
++      goto cleanup;
+     }
+     /* count the number of lines */
+@@ -2990,11 +2991,11 @@ int mutt_write_fcc(const char *path, struct Email *hdr, const char *msgid, int post,
+     /* copy the body and clean up */
+     rewind(tempfp);
+     r = mutt_file_copy_stream(tempfp, msg->fp);
+-    if (fclose(tempfp) != 0)
++    if (mutt_file_fclose(&tempfp) != 0)
+       r = -1;
+     /* if there was an error, leave the temp version */
+     if (!r)
+-      unlink(tempfile);
++      unlink(mutt_b2s(tempfile));
+   }
+   else
+   {
+@@ -3013,5 +3014,13 @@ int mutt_write_fcc(const char *path, struct Email *hdr, const char *msgid, int post,
+   if (post)
+     set_noconv_flags(hdr->content, 0);
++cleanup:
++  if (tempfp)
++  {
++    mutt_file_fclose(&tempfp);
++    unlink(mutt_b2s(tempfile));
++  }
++  mutt_buffer_pool_release(&tempfile);
++
+   return r;
+ }