diff --git a/pkgs/applications/networking/mailreaders/mutt/default.nix b/pkgs/applications/networking/mailreaders/mutt/default.nix index 40d302464f6..a1bfb9f854c 100644 --- a/pkgs/applications/networking/mailreaders/mutt/default.nix +++ b/pkgs/applications/networking/mailreaders/mutt/default.nix @@ -17,14 +17,14 @@ assert saslSupport -> cyrus_sasl != null; assert gpgmeSupport -> gpgme != null; let - version = "1.5.23"; + version = "1.5.24"; in stdenv.mkDerivation rec { name = "mutt${stdenv.lib.optionalString withSidebar "-with-sidebar"}-${version}"; src = fetchurl { - url = "mirror://sourceforge/mutt/mutt-${version}.tar.gz"; - sha256 = "0dzx4qk50pjfsb6cs5jahng96a52k12f7pm0sc78iqdrawg71w1s"; + url = "http://ftp.mutt.org/pub/mutt/mutt-${version}.tar.gz"; + sha256 = "0012njrgxf1barjksqkx7ccid2l0xyikhna9mjs9vcfpbrvcm4m2"; }; buildInputs = with stdenv.lib; @@ -59,11 +59,16 @@ stdenv.mkDerivation rec { ]; # Adding the sidebar - patches = [] ++ - (stdenv.lib.optional withSidebar (fetchurl { - url = http://lunar-linux.org/~tchan/mutt/patch-1.5.23.sidebar.20140412.txt; - sha256 = "1i2r7dj0pd1k0z3jjxn2szi6sf0k28i8dwhr4f65pn8r2lh3wisz"; - })); + patches = stdenv.lib.optional withSidebar [ + ./trash-folder.patch + ./sidebar.patch + ./sidebar-dotpathsep.patch + ./sidebar-utf8.patch + ./sidebar-newonly.patch + ./sidebar-delimnullwide.patch + ./sidebar-compose.patch + ./sidebar-new.patch + ]; meta = with stdenv.lib; { description = "A small but very powerful text-based mail client"; diff --git a/pkgs/applications/networking/mailreaders/mutt/sidebar-compose.patch b/pkgs/applications/networking/mailreaders/mutt/sidebar-compose.patch new file mode 100644 index 00000000000..90d815b841d --- /dev/null +++ b/pkgs/applications/networking/mailreaders/mutt/sidebar-compose.patch @@ -0,0 +1,40 @@ +From: Evgeni Golov +Date: Fri, 14 Mar 2014 08:54:47 +0100 +Subject: sidebar-compose + +draw_sidebar sets SidebarWidth to 0 when sidebar_visible is false. +However, if you start mutt in compose mode, draw_sidebar won't be +called until the next redraw and your header lines will be off by +the width of the sidebar, even when you did not want a sidebar at +all. + +Can be tested with: + HOME=/ LC_ALL=C mutt -e 'unset sidebar_visible' -s test recipient + +Closes: #502627 + +Gbp-Pq: Topic mutt-patched +--- + compose.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/compose.c b/compose.c +index b63695f..0fa6df2 100644 +--- a/compose.c ++++ b/compose.c +@@ -32,6 +32,7 @@ + #include "mailbox.h" + #include "sort.h" + #include "charset.h" ++#include "sidebar.h" + + #ifdef MIXMASTER + #include "remailer.h" +@@ -248,6 +249,7 @@ static void draw_envelope_addr (int line, ADDRESS *addr) + + static void draw_envelope (HEADER *msg, char *fcc) + { ++ draw_sidebar (MENU_COMPOSE); + draw_envelope_addr (HDR_FROM, msg->env->from); + draw_envelope_addr (HDR_TO, msg->env->to); + draw_envelope_addr (HDR_CC, msg->env->cc); diff --git a/pkgs/applications/networking/mailreaders/mutt/sidebar-delimnullwide.patch b/pkgs/applications/networking/mailreaders/mutt/sidebar-delimnullwide.patch new file mode 100644 index 00000000000..11d2ced5a0c --- /dev/null +++ b/pkgs/applications/networking/mailreaders/mutt/sidebar-delimnullwide.patch @@ -0,0 +1,38 @@ +From: Evgeni Golov +Date: Wed, 5 Mar 2014 17:46:07 +0100 +Subject: sidebar-delimnullwide + +SidebarDelim can be NULL and strlen(NULL) is a bad idea, as it will segfault. +Wrap it with NONULL(). + +While at it, change strlen to mbstowcs for better utf8 support. + +Closes: #696145, #663883 + +Gbp-Pq: Topic mutt-patched +--- + sidebar.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/sidebar.c b/sidebar.c +index 51a25ca..c3ea338 100644 +--- a/sidebar.c ++++ b/sidebar.c +@@ -88,7 +88,7 @@ char *make_sidebar_entry(char *box, int size, int new, int flagged) + int box_len, box_bytes; + int int_len; + int right_offset = 0; +- int delim_len = strlen(SidebarDelim); ++ int delim_len = mbstowcs(NULL, NONULL(SidebarDelim), 0); + static char *entry; + + right_width = left_width = 0; +@@ -178,7 +178,7 @@ int draw_sidebar(int menu) { + #ifndef USE_SLANG_CURSES + attr_t attrs; + #endif +- short delim_len = strlen(SidebarDelim); ++ short delim_len = mbstowcs(NULL, NONULL(SidebarDelim), 0); + short color_pair; + + static bool initialized = false; diff --git a/pkgs/applications/networking/mailreaders/mutt/sidebar-dotpathsep.patch b/pkgs/applications/networking/mailreaders/mutt/sidebar-dotpathsep.patch new file mode 100644 index 00000000000..315fa1649bc --- /dev/null +++ b/pkgs/applications/networking/mailreaders/mutt/sidebar-dotpathsep.patch @@ -0,0 +1,98 @@ +From: Fabian Groffen +Date: Tue, 4 Mar 2014 21:12:15 +0100 +Subject: sidebar-dotpathsep + +Make path separators for sidebar folders configurable. + +When using IMAP, a '.' is often used as path separator, hence make the +path separators configurable through sidebar_delim_chars variable. +It defaults to "/." to work for both mboxes as well as IMAP folders. It +can be set to only "/" or "." or whichever character desired as needed. + +Gbp-Pq: Topic mutt-patched +--- + globals.h | 1 + + init.h | 8 ++++++++ + sidebar.c | 31 ++++++++++++++++++++++++------- + 3 files changed, 33 insertions(+), 7 deletions(-) + +diff --git a/globals.h b/globals.h +index 004c795..602f932 100644 +--- a/globals.h ++++ b/globals.h +@@ -119,6 +119,7 @@ WHERE char *SendCharset; + WHERE char *Sendmail; + WHERE char *Shell; + WHERE char *SidebarDelim; ++WHERE char *SidebarDelimChars INITVAL (NULL); + WHERE char *Signature; + WHERE char *SimpleSearch; + #if USE_SMTP +diff --git a/init.h b/init.h +index c664e5f..166671b 100644 +--- a/init.h ++++ b/init.h +@@ -2051,6 +2051,14 @@ struct option_t MuttVars[] = { + ** .pp + ** The width of the sidebar. + */ ++ { "sidebar_delim_chars", DT_STR, R_NONE, UL &SidebarDelimChars, UL "/." }, ++ /* ++ ** .pp ++ ** This contains the list of characters which you would like to treat ++ ** as folder separators for displaying paths in the sidebar. If ++ ** you're not using IMAP folders, you probably prefer setting this to "/" ++ ** alone. ++ */ + { "pgp_use_gpg_agent", DT_BOOL, R_NONE, OPTUSEGPGAGENT, 0}, + /* + ** .pp +diff --git a/sidebar.c b/sidebar.c +index 6098c2a..4356ffc 100644 +--- a/sidebar.c ++++ b/sidebar.c +@@ -249,20 +249,37 @@ int draw_sidebar(int menu) { + // calculate depth of current folder and generate its display name with indented spaces + int sidebar_folder_depth = 0; + char *sidebar_folder_name; +- sidebar_folder_name = basename(tmp->path); ++ int i; ++ sidebar_folder_name = tmp->path; ++ /* disregard a trailing separator, so strlen() - 2 ++ * https://bugs.gentoo.org/show_bug.cgi?id=373197#c16 */ ++ for (i = strlen(sidebar_folder_name) - 2; i >= 0; i--) { ++ if (SidebarDelimChars && ++ strchr(SidebarDelimChars, sidebar_folder_name[i])) ++ { ++ sidebar_folder_name += i + 1; ++ break; ++ } ++ } + if ( maildir_is_prefix ) { + char *tmp_folder_name; +- int i; ++ int lastsep = 0; + tmp_folder_name = tmp->path + strlen(Maildir); +- for (i = 0; i < strlen(tmp->path) - strlen(Maildir); i++) { +- if (tmp_folder_name[i] == '/') sidebar_folder_depth++; +- } ++ for (i = 0; i < strlen(tmp_folder_name) - 1; i++) { ++ if (SidebarDelimChars && ++ strchr(SidebarDelimChars, tmp_folder_name[i])) ++ { ++ sidebar_folder_depth++; ++ lastsep = i + 1; ++ } ++ } + if (sidebar_folder_depth > 0) { +- sidebar_folder_name = malloc(strlen(basename(tmp->path)) + sidebar_folder_depth + 1); ++ tmp_folder_name += lastsep; /* basename */ ++ sidebar_folder_name = malloc(strlen(tmp_folder_name) + sidebar_folder_depth + 1); + for (i=0; i < sidebar_folder_depth; i++) + sidebar_folder_name[i]=' '; + sidebar_folder_name[i]=0; +- strncat(sidebar_folder_name, basename(tmp->path), strlen(basename(tmp->path)) + sidebar_folder_depth); ++ strncat(sidebar_folder_name, tmp_folder_name, strlen(tmp_folder_name) + sidebar_folder_depth); + } + } + printw( "%.*s", SidebarWidth - delim_len + 1, diff --git a/pkgs/applications/networking/mailreaders/mutt/sidebar-new.patch b/pkgs/applications/networking/mailreaders/mutt/sidebar-new.patch new file mode 100644 index 00000000000..fb265346361 --- /dev/null +++ b/pkgs/applications/networking/mailreaders/mutt/sidebar-new.patch @@ -0,0 +1,97 @@ +From 355399bde98203af59d20821f9e840fc056bd383 Mon Sep 17 00:00:00 2001 +From: Julius Haertl +Date: Tue, 9 Sep 2014 22:31:49 +0200 +Subject: Patch for sidebar iteration functionality + +sidebar-new will move the selected folder to the next with new messages. +If the end is reached, it will start at the top. + +Useful macros would be: + + macro index a "" + macro pager a "" +--- + OPS | 1 + + curs_main.c | 1 + + functions.h | 2 ++ + pager.c | 1 + + sidebar.c | 10 ++++++++++ + 5 files changed, 15 insertions(+) + +diff --git a/OPS b/OPS +index 1ed9c96..3ffb82a 100644 +--- a/OPS ++++ b/OPS +@@ -187,3 +187,4 @@ OP_SIDEBAR_PREV "go to previous mailbox" + OP_SIDEBAR_OPEN "open hilighted mailbox" + OP_SIDEBAR_NEXT_NEW "go down to next mailbox with new mail" + OP_SIDEBAR_PREV_NEW "go to previous mailbox with new mail" ++OP_SIDEBAR_NEW "iterate though mailboxes with new mail" +diff --git a/curs_main.c b/curs_main.c +index acb106d..2e35f90 100644 +--- a/curs_main.c ++++ b/curs_main.c +@@ -2328,6 +2328,7 @@ int mutt_index_menu (void) + case OP_SIDEBAR_PREV: + case OP_SIDEBAR_NEXT_NEW: + case OP_SIDEBAR_PREV_NEW: ++ case OP_SIDEBAR_NEW: + scroll_sidebar(op, menu->menu); + break; + default: +diff --git a/functions.h b/functions.h +index 363b4d5..1485080 100644 +--- a/functions.h ++++ b/functions.h +@@ -176,6 +176,7 @@ const struct binding_t OpMain[] = { /* map: index */ + { "sidebar-prev", OP_SIDEBAR_PREV, NULL }, + { "sidebar-next-new", OP_SIDEBAR_NEXT_NEW, NULL}, + { "sidebar-prev-new", OP_SIDEBAR_PREV_NEW, NULL}, ++ { "sidebar-new", OP_SIDEBAR_NEW, NULL }, + { "sidebar-open", OP_SIDEBAR_OPEN, NULL }, + { NULL, 0, NULL } + }; +@@ -287,6 +288,7 @@ const struct binding_t OpPager[] = { /* map: pager */ + { "sidebar-prev", OP_SIDEBAR_PREV, NULL }, + { "sidebar-next-new", OP_SIDEBAR_NEXT_NEW, NULL}, + { "sidebar-prev-new", OP_SIDEBAR_PREV_NEW, NULL}, ++ { "sidebar-new", OP_SIDEBAR_NEW, NULL }, + { "sidebar-open", OP_SIDEBAR_OPEN, NULL }, + { NULL, 0, NULL } + }; +diff --git a/pager.c b/pager.c +index 8d64fe1..696e55c 100644 +--- a/pager.c ++++ b/pager.c +@@ -2791,6 +2791,7 @@ search_next: + case OP_SIDEBAR_PREV: + case OP_SIDEBAR_NEXT_NEW: + case OP_SIDEBAR_PREV_NEW: ++ case OP_SIDEBAR_NEW: + scroll_sidebar(ch, MENU_PAGER); + break; + +diff --git a/sidebar.c b/sidebar.c +index c3ea338..eb8ecd2 100644 +--- a/sidebar.c ++++ b/sidebar.c +@@ -429,6 +429,16 @@ void scroll_sidebar(int op, int menu) + CurBuffy = CurBuffy->next; + } + break; ++ case OP_SIDEBAR_NEW: ++ if ( (tmp = exist_next_new()) == NULL) ++ tmp = TopBuffy; ++ if ( tmp->msg_unread == 0 ) { ++ CurBuffy = tmp; ++ tmp = exist_next_new(); ++ } ++ if ( tmp != NULL ) ++ CurBuffy = tmp; ++ break; + default: + return; + } +-- +2.6.0.rc0.2.g7662973.dirty + diff --git a/pkgs/applications/networking/mailreaders/mutt/sidebar-newonly.patch b/pkgs/applications/networking/mailreaders/mutt/sidebar-newonly.patch new file mode 100644 index 00000000000..d206848026b --- /dev/null +++ b/pkgs/applications/networking/mailreaders/mutt/sidebar-newonly.patch @@ -0,0 +1,198 @@ +From: Steve Kemp +Date: Tue, 4 Mar 2014 22:07:06 +0100 +Subject: sidebar-newonly + +patches written by Steve Kemp, it adds two new functionalities to the sidebar, +so only the mailbox with new messages will be shown (and/or) selected +See Debian bug http://bugs.debian.org/532510 + +Gbp-Pq: Topic mutt-patched +--- + OPS | 2 ++ + curs_main.c | 2 ++ + functions.h | 4 ++++ + init.h | 5 +++++ + mutt.h | 2 ++ + pager.c | 2 ++ + sidebar.c | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- + 7 files changed, 70 insertions(+), 2 deletions(-) + +diff --git a/OPS b/OPS +index b036db9..1ed9c96 100644 +--- a/OPS ++++ b/OPS +@@ -185,3 +185,5 @@ OP_SIDEBAR_SCROLL_DOWN "scroll the mailbox pane down 1 page" + OP_SIDEBAR_NEXT "go down to next mailbox" + OP_SIDEBAR_PREV "go to previous mailbox" + OP_SIDEBAR_OPEN "open hilighted mailbox" ++OP_SIDEBAR_NEXT_NEW "go down to next mailbox with new mail" ++OP_SIDEBAR_PREV_NEW "go to previous mailbox with new mail" +diff --git a/curs_main.c b/curs_main.c +index ea530a6..acb106d 100644 +--- a/curs_main.c ++++ b/curs_main.c +@@ -2326,6 +2326,8 @@ int mutt_index_menu (void) + case OP_SIDEBAR_SCROLL_DOWN: + case OP_SIDEBAR_NEXT: + case OP_SIDEBAR_PREV: ++ case OP_SIDEBAR_NEXT_NEW: ++ case OP_SIDEBAR_PREV_NEW: + scroll_sidebar(op, menu->menu); + break; + default: +diff --git a/functions.h b/functions.h +index ef8937a..363b4d5 100644 +--- a/functions.h ++++ b/functions.h +@@ -174,6 +174,8 @@ const struct binding_t OpMain[] = { /* map: index */ + { "sidebar-scroll-down", OP_SIDEBAR_SCROLL_DOWN, NULL }, + { "sidebar-next", OP_SIDEBAR_NEXT, NULL }, + { "sidebar-prev", OP_SIDEBAR_PREV, NULL }, ++ { "sidebar-next-new", OP_SIDEBAR_NEXT_NEW, NULL}, ++ { "sidebar-prev-new", OP_SIDEBAR_PREV_NEW, NULL}, + { "sidebar-open", OP_SIDEBAR_OPEN, NULL }, + { NULL, 0, NULL } + }; +@@ -283,6 +285,8 @@ const struct binding_t OpPager[] = { /* map: pager */ + { "sidebar-scroll-down", OP_SIDEBAR_SCROLL_DOWN, NULL }, + { "sidebar-next", OP_SIDEBAR_NEXT, NULL }, + { "sidebar-prev", OP_SIDEBAR_PREV, NULL }, ++ { "sidebar-next-new", OP_SIDEBAR_NEXT_NEW, NULL}, ++ { "sidebar-prev-new", OP_SIDEBAR_PREV_NEW, NULL}, + { "sidebar-open", OP_SIDEBAR_OPEN, NULL }, + { NULL, 0, NULL } + }; +diff --git a/init.h b/init.h +index 166671b..a5d4238 100644 +--- a/init.h ++++ b/init.h +@@ -2059,6 +2059,11 @@ struct option_t MuttVars[] = { + ** you're not using IMAP folders, you probably prefer setting this to "/" + ** alone. + */ ++ {"sidebar_newmail_only", DT_BOOL, R_BOTH, OPTSIDEBARNEWMAILONLY, 0 }, ++ /* ++ ** .pp ++ ** Show only new mail in the sidebar. ++ */ + { "pgp_use_gpg_agent", DT_BOOL, R_NONE, OPTUSEGPGAGENT, 0}, + /* + ** .pp +diff --git a/mutt.h b/mutt.h +index 5f25406..d73e514 100644 +--- a/mutt.h ++++ b/mutt.h +@@ -529,6 +529,8 @@ enum + OPTDONTHANDLEPGPKEYS, /* (pseudo) used to extract PGP keys */ + OPTUNBUFFEREDINPUT, /* (pseudo) don't use key buffer */ + ++ OPTSIDEBARNEWMAILONLY, ++ + OPTMAX + }; + +diff --git a/pager.c b/pager.c +index 5cfcb75..8d64fe1 100644 +--- a/pager.c ++++ b/pager.c +@@ -2789,6 +2789,8 @@ search_next: + case OP_SIDEBAR_SCROLL_DOWN: + case OP_SIDEBAR_NEXT: + case OP_SIDEBAR_PREV: ++ case OP_SIDEBAR_NEXT_NEW: ++ case OP_SIDEBAR_PREV_NEW: + scroll_sidebar(ch, MENU_PAGER); + break; + +diff --git a/sidebar.c b/sidebar.c +index 8f58f85..51a25ca 100644 +--- a/sidebar.c ++++ b/sidebar.c +@@ -269,8 +269,21 @@ int draw_sidebar(int menu) { + SETCOLOR(MT_COLOR_NEW); + else if ( tmp->msg_flagged > 0 ) + SETCOLOR(MT_COLOR_FLAGGED); +- else +- SETCOLOR(MT_COLOR_NORMAL); ++ else { ++ /* make sure the path is either: ++ 1. Containing new mail. ++ 2. The inbox. ++ 3. The current box. ++ */ ++ if ((option (OPTSIDEBARNEWMAILONLY)) && ++ ( (tmp->msg_unread <= 0) && ++ ( tmp != Incoming ) && ++ Context && ++ ( strcmp( tmp->path, Context->path ) != 0 ) ) ) ++ continue; ++ else ++ SETCOLOR(MT_COLOR_NORMAL); ++ } + + move( lines, 0 ); + if ( Context && !strcmp( tmp->path, Context->path ) ) { +@@ -336,6 +349,29 @@ int draw_sidebar(int menu) { + return 0; + } + ++BUFFY * exist_next_new() ++{ ++ BUFFY *tmp = CurBuffy; ++ if(tmp == NULL) return NULL; ++ while (tmp->next != NULL) ++ { ++ tmp = tmp->next; ++ if(tmp->msg_unread) return tmp; ++ } ++ return NULL; ++} ++ ++BUFFY * exist_prev_new() ++{ ++ BUFFY *tmp = CurBuffy; ++ if(tmp == NULL) return NULL; ++ while (tmp->prev != NULL) ++ { ++ tmp = tmp->prev; ++ if(tmp->msg_unread) return tmp; ++ } ++ return NULL; ++} + + void set_buffystats(CONTEXT* Context) + { +@@ -352,18 +388,33 @@ void set_buffystats(CONTEXT* Context) + + void scroll_sidebar(int op, int menu) + { ++ BUFFY *tmp; + if(!SidebarWidth) return; + if(!CurBuffy) return; + + switch (op) { + case OP_SIDEBAR_NEXT: ++ if (!option (OPTSIDEBARNEWMAILONLY)) { + if ( CurBuffy->next == NULL ) return; + CurBuffy = CurBuffy->next; + break; ++ } ++ case OP_SIDEBAR_NEXT_NEW: ++ if ( (tmp = exist_next_new()) == NULL) ++ return; ++ else CurBuffy = tmp; ++ break; + case OP_SIDEBAR_PREV: ++ if (!option (OPTSIDEBARNEWMAILONLY)) { + if ( CurBuffy->prev == NULL ) return; + CurBuffy = CurBuffy->prev; + break; ++ } ++ case OP_SIDEBAR_PREV_NEW: ++ if ( (tmp = exist_prev_new()) == NULL) ++ return; ++ else CurBuffy = tmp; ++ break; + case OP_SIDEBAR_SCROLL_UP: + CurBuffy = TopBuffy; + if ( CurBuffy != Incoming ) { diff --git a/pkgs/applications/networking/mailreaders/mutt/sidebar-utf8.patch b/pkgs/applications/networking/mailreaders/mutt/sidebar-utf8.patch new file mode 100644 index 00000000000..d67e43c24b6 --- /dev/null +++ b/pkgs/applications/networking/mailreaders/mutt/sidebar-utf8.patch @@ -0,0 +1,132 @@ +From: Antonio Radici +Date: Tue, 4 Mar 2014 15:39:14 +0100 +Subject: sidebar-utf8 + +This patch fixes a problem with utf-8 strings and the sidebar, +it rewrites entirely make_sidebar_entry so it also fixes some +segfaults due to misallocations and overflows. + +See: + http://bugs.debian.org/584581 + http://bugs.debian.org/603287 + +Gbp-Pq: Topic mutt-patched +--- + sidebar.c | 97 +++++++++++++++++++++++++++++++++++++++++++-------------------- + 1 file changed, 67 insertions(+), 30 deletions(-) + +diff --git a/sidebar.c b/sidebar.c +index 4356ffc..8f58f85 100644 +--- a/sidebar.c ++++ b/sidebar.c +@@ -30,6 +30,7 @@ + #include + #include "keymap.h" + #include ++#include + + /*BUFFY *CurBuffy = 0;*/ + static BUFFY *TopBuffy = 0; +@@ -82,36 +83,72 @@ void calc_boundaries (int menu) + + char *make_sidebar_entry(char *box, int size, int new, int flagged) + { +- static char *entry = 0; +- char *c; +- int i = 0; +- int delim_len = strlen(SidebarDelim); +- +- c = realloc(entry, SidebarWidth - delim_len + 2); +- if ( c ) entry = c; +- entry[SidebarWidth - delim_len + 1] = 0; +- for (; i < SidebarWidth - delim_len + 1; entry[i++] = ' ' ); +- i = strlen(box); +- strncpy( entry, box, i < (SidebarWidth - delim_len + 1) ? i : (SidebarWidth - delim_len + 1) ); +- +- if (size == -1) +- sprintf(entry + SidebarWidth - delim_len - 3, "?"); +- else if ( new ) { +- if (flagged > 0) { +- sprintf( +- entry + SidebarWidth - delim_len - 5 - quick_log10(size) - quick_log10(new) - quick_log10(flagged), +- "% d(%d)[%d]", size, new, flagged); +- } else { +- sprintf( +- entry + SidebarWidth - delim_len - 3 - quick_log10(size) - quick_log10(new), +- "% d(%d)", size, new); +- } +- } else if (flagged > 0) { +- sprintf( entry + SidebarWidth - delim_len - 3 - quick_log10(size) - quick_log10(flagged), "% d[%d]", size, flagged); +- } else { +- sprintf( entry + SidebarWidth - delim_len - 1 - quick_log10(size), "% d", size); +- } +- return entry; ++ char int_store[20]; // up to 64 bits integers ++ int right_width, left_width; ++ int box_len, box_bytes; ++ int int_len; ++ int right_offset = 0; ++ int delim_len = strlen(SidebarDelim); ++ static char *entry; ++ ++ right_width = left_width = 0; ++ box_len = box_bytes = 0; ++ ++ // allocate an entry big enough to contain SidebarWidth wide chars ++ entry = malloc((SidebarWidth*4)+1); // TODO: error check ++ ++ // determine the right space (i.e.: how big are the numbers that we want to print) ++ if ( size > 0 ) { ++ int_len = snprintf(int_store, sizeof(int_store), "%d", size); ++ right_width += int_len; ++ } else { ++ right_width = 1; // to represent 0 ++ } ++ if ( new > 0 ) { ++ int_len = snprintf(int_store, sizeof(int_store), "%d", new); ++ right_width += int_len + 2; // 2 is for () ++ } ++ if ( flagged > 0 ) { ++ int_len = snprintf(int_store, sizeof(int_store), "%d", flagged); ++ right_width += int_len + 2; // 2 is for [] ++ } ++ ++ // determine how much space we have for *box and its padding (if any) ++ left_width = SidebarWidth - right_width - 1 - delim_len; // 1 is for the space ++ //fprintf(stdout, "left_width: %d right_width: %d\n", left_width, right_width); ++ // right side overflow case ++ if ( left_width <= 0 ) { ++ snprintf(entry, SidebarWidth*4, "%-*.*s ...", SidebarWidth-4-delim_len, SidebarWidth-4-delim_len, box); ++ return entry; ++ } ++ right_width -= delim_len; ++ ++ // to support utf-8 chars we need to add enough space padding in case there ++ // are less chars than bytes in *box ++ box_len = mbstowcs(NULL, box, 0); ++ box_bytes = strlen(box); ++ // debug ++ //fprintf(stdout, "box_len: %d box_bytes: %d (diff: %d)\n", box_len, box_bytes, (box_bytes-box_len)); ++ // if there is less string than the space we allow, then we will add the ++ // spaces ++ if ( box_len != -1 && box_len < left_width ) { ++ left_width += (box_bytes - box_len); ++ } ++ // otherwise sprintf will truncate the string for us (therefore, no else case) ++ ++ // print the sidebar entry (without new and flagged messages, at the moment) ++ //fprintf(stdout, "left_width: %d right_width: %d\n", left_width, right_width); ++ right_offset = snprintf(entry, SidebarWidth*4, "%-*.*s %d", left_width, left_width, box, size); ++ ++ // then pad new and flagged messages if any ++ if ( new > 0 ) { ++ right_offset += snprintf(entry+right_offset, SidebarWidth*4-right_offset, "(%d)", new); ++ } ++ if ( flagged > 0 ) { ++ right_offset += snprintf(entry+right_offset, SidebarWidth*4-right_offset, "[%d]", flagged); ++ } ++ ++ return entry; + } + + void set_curbuffy(char buf[LONG_STRING]) diff --git a/pkgs/applications/networking/mailreaders/mutt/sidebar.patch b/pkgs/applications/networking/mailreaders/mutt/sidebar.patch new file mode 100644 index 00000000000..262e8f98a65 --- /dev/null +++ b/pkgs/applications/networking/mailreaders/mutt/sidebar.patch @@ -0,0 +1,1477 @@ +From: Antonio Radici +Date: Tue, 4 Mar 2014 15:21:10 +0100 +Subject: sidebar + +When enabled, mutt will show a list of mailboxes with (new) message counts in a +separate column on the left side of the screen. + +As this feature is still considered to be unstable, this patch is only applied +in the "mutt-patched" package. + +* Configuration variables: + + sidebar_delim (string, default "|") + + This specifies the delimiter between the sidebar (if visible) and + other screens. + + sidebar_visible (boolean, default no) + + This specifies whether or not to show sidebar (left-side list of folders). + + sidebar_width (integer, default 0) +- + The width of the sidebar. + +* Patch source: + - http://www.lunar-linux.org/index.php?page=mutt-sidebar + - http://lunar-linux.org/~tchan/mutt/patch-1.5.19.sidebar.20090522.txt + +* Changes made: + - 2008-08-02 myon: Refreshed patch using quilt push -f to remove hunks we do + not need (Makefile.in). + + - 2014-03-04 evgeni: refresh sidebar patch with the version from OpenBSD + Source: + ftp://ftp.openbsd.org/pub/OpenBSD/distfiles/mutt/sidebar-1.5.22.diff1.gz + +Signed-off-by: Matteo F. Vescovi +Signed-off-by: Evgeni Golov + +Gbp-Pq: Topic mutt-patched +--- + Makefile.am | 1 + + OPS | 5 + + buffy.c | 124 +++++++++++++++++++++ + buffy.h | 4 + + color.c | 2 + + compose.c | 26 ++--- + curs_main.c | 30 +++++- + flags.c | 3 + + functions.h | 10 ++ + globals.h | 4 + + imap/command.c | 7 ++ + imap/imap.c | 2 +- + init.h | 21 ++++ + mailbox.h | 1 + + mbox.c | 2 + + menu.c | 20 ++-- + mh.c | 22 ++++ + mutt.h | 4 + + mutt_curses.h | 3 + + muttlib.c | 48 +++++++++ + mx.c | 15 +++ + mx.h | 1 + + pager.c | 30 ++++-- + sidebar.c | 333 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + sidebar.h | 36 +++++++ + 25 files changed, 720 insertions(+), 34 deletions(-) + create mode 100644 sidebar.c + create mode 100644 sidebar.h + +diff --git a/Makefile.am b/Makefile.am +index 5dfeff6..cf1ac98 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -33,6 +33,7 @@ mutt_SOURCES = \ + rfc822.c rfc1524.c rfc2047.c rfc2231.c rfc3676.c \ + score.c send.c sendlib.c signal.c sort.c \ + status.c system.c thread.c charset.c history.c lib.c \ ++ sidebar.c \ + muttlib.c editmsg.c mbyte.c \ + url.c ascii.c crypt-mod.c crypt-mod.h safe_asprintf.c + +diff --git a/OPS b/OPS +index 02cea8e..b036db9 100644 +--- a/OPS ++++ b/OPS +@@ -180,3 +180,8 @@ OP_WHAT_KEY "display the keycode for a key press" + OP_MAIN_SHOW_LIMIT "show currently active limit pattern" + OP_MAIN_COLLAPSE_THREAD "collapse/uncollapse current thread" + OP_MAIN_COLLAPSE_ALL "collapse/uncollapse all threads" ++OP_SIDEBAR_SCROLL_UP "scroll the mailbox pane up 1 page" ++OP_SIDEBAR_SCROLL_DOWN "scroll the mailbox pane down 1 page" ++OP_SIDEBAR_NEXT "go down to next mailbox" ++OP_SIDEBAR_PREV "go to previous mailbox" ++OP_SIDEBAR_OPEN "open hilighted mailbox" +diff --git a/buffy.c b/buffy.c +index 12a16d1..90ca6db 100644 +--- a/buffy.c ++++ b/buffy.c +@@ -161,6 +161,49 @@ void mutt_buffy_cleanup (const char *buf, struct stat *st) + } + } + ++static int buffy_compare_name(const void *a, const void *b) { ++ const BUFFY *b1 = * (BUFFY * const *) a; ++ const BUFFY *b2 = * (BUFFY * const *) b; ++ ++ return mutt_strcoll(b1->path, b2->path); ++} ++ ++static BUFFY *buffy_sort(BUFFY *b) ++{ ++ BUFFY *tmp = b; ++ int buffycount = 0; ++ BUFFY **ary; ++ int i; ++ ++ if (!option(OPTSIDEBARSORT)) ++ return b; ++ ++ for (; tmp != NULL; tmp = tmp->next) ++ buffycount++; ++ ++ ary = (BUFFY **) safe_calloc(buffycount, sizeof (*ary)); ++ ++ tmp = b; ++ for (i = 0; tmp != NULL; tmp = tmp->next, i++) { ++ ary[i] = tmp; ++ } ++ ++ qsort(ary, buffycount, sizeof(*ary), buffy_compare_name); ++ ++ for (i = 0; i < buffycount - 1; i++) { ++ ary[i]->next = ary[i+1]; ++ } ++ ary[buffycount - 1]->next = NULL; ++ for (i = 1; i < buffycount; i++) { ++ ary[i]->prev = ary[i-1]; ++ } ++ ary[0]->prev = NULL; ++ ++ tmp = ary[0]; ++ free(ary); ++ return tmp; ++} ++ + BUFFY *mutt_find_mailbox (const char *path) + { + BUFFY *tmp = NULL; +@@ -282,6 +325,7 @@ int mutt_parse_mailboxes (BUFFER *path, BUFFER *s, unsigned long data, BUFFER *e + else + (*tmp)->size = 0; + } ++ Incoming = buffy_sort(Incoming); + return 0; + } + +@@ -357,6 +401,69 @@ static int buffy_maildir_hasnew (BUFFY* mailbox) + + return 0; + } ++ ++/* update message counts for the sidebar */ ++void buffy_maildir_update (BUFFY* mailbox) ++{ ++ char path[_POSIX_PATH_MAX]; ++ DIR *dirp; ++ struct dirent *de; ++ char *p; ++ ++ mailbox->msgcount = 0; ++ mailbox->msg_unread = 0; ++ mailbox->msg_flagged = 0; ++ ++ snprintf (path, sizeof (path), "%s/new", mailbox->path); ++ ++ if ((dirp = opendir (path)) == NULL) ++ { ++ mailbox->magic = 0; ++ return; ++ } ++ ++ while ((de = readdir (dirp)) != NULL) ++ { ++ if (*de->d_name == '.') ++ continue; ++ ++ if (!(p = strstr (de->d_name, ":2,")) || !strchr (p + 3, 'T')) { ++ mailbox->new = 1; ++ mailbox->msgcount++; ++ mailbox->msg_unread++; ++ } ++ } ++ ++ closedir (dirp); ++ snprintf (path, sizeof (path), "%s/cur", mailbox->path); ++ ++ if ((dirp = opendir (path)) == NULL) ++ { ++ mailbox->magic = 0; ++ return; ++ } ++ ++ while ((de = readdir (dirp)) != NULL) ++ { ++ if (*de->d_name == '.') ++ continue; ++ ++ if (!(p = strstr (de->d_name, ":2,")) || !strchr (p + 3, 'T')) { ++ mailbox->msgcount++; ++ if ((p = strstr (de->d_name, ":2,"))) { ++ if (!strchr (p + 3, 'T')) { ++ if (!strchr (p + 3, 'S')) ++ mailbox->msg_unread++; ++ if (strchr(p + 3, 'F')) ++ mailbox->msg_flagged++; ++ } ++ } ++ } ++ } ++ ++ closedir (dirp); ++} ++ + /* returns 1 if mailbox has new mail */ + static int buffy_mbox_hasnew (BUFFY* mailbox, struct stat *sb) + { +@@ -388,6 +495,20 @@ static int buffy_mbox_hasnew (BUFFY* mailbox, struct stat *sb) + return rc; + } + ++/* update message counts for the sidebar */ ++void buffy_mbox_update (BUFFY* mailbox) ++{ ++ CONTEXT *ctx = NULL; ++ ++ ctx = mx_open_mailbox(mailbox->path, M_READONLY | M_QUIET | M_NOSORT | M_PEEK, NULL); ++ if(ctx) ++ { ++ mailbox->msgcount = ctx->msgcount; ++ mailbox->msg_unread = ctx->unread; ++ mx_close_mailbox(ctx, 0); ++ } ++} ++ + int mutt_buffy_check (int force) + { + BUFFY *tmp; +@@ -461,16 +582,19 @@ int mutt_buffy_check (int force) + { + case M_MBOX: + case M_MMDF: ++ buffy_mbox_update (tmp); + if (buffy_mbox_hasnew (tmp, &sb) > 0) + BuffyCount++; + break; + + case M_MAILDIR: ++ buffy_maildir_update (tmp); + if (buffy_maildir_hasnew (tmp) > 0) + BuffyCount++; + break; + + case M_MH: ++ mh_buffy_update (tmp->path, &tmp->msgcount, &tmp->msg_unread, &tmp->msg_flagged); + mh_buffy(tmp); + if (tmp->new) + BuffyCount++; +diff --git a/buffy.h b/buffy.h +index f9fc55a..672d178 100644 +--- a/buffy.h ++++ b/buffy.h +@@ -25,7 +25,11 @@ typedef struct buffy_t + char path[_POSIX_PATH_MAX]; + off_t size; + struct buffy_t *next; ++ struct buffy_t *prev; + short new; /* mailbox has new mail */ ++ int msgcount; /* total number of messages */ ++ int msg_unread; /* number of unread messages */ ++ int msg_flagged; /* number of flagged messages */ + short notified; /* user has been notified */ + short magic; /* mailbox type */ + short newly_created; /* mbox or mmdf just popped into existence */ +diff --git a/color.c b/color.c +index 64a46dc..d6f9198 100644 +--- a/color.c ++++ b/color.c +@@ -94,6 +94,8 @@ static const struct mapping_t Fields[] = + { "underline", MT_COLOR_UNDERLINE }, + { "index", MT_COLOR_INDEX }, + { "prompt", MT_COLOR_PROMPT }, ++ { "sidebar_new", MT_COLOR_NEW }, ++ { "sidebar_flagged", MT_COLOR_FLAGGED }, + { NULL, 0 } + }; + +diff --git a/compose.c b/compose.c +index 9d87060..b63695f 100644 +--- a/compose.c ++++ b/compose.c +@@ -72,7 +72,7 @@ enum + + #define HDR_XOFFSET 10 + #define TITLE_FMT "%10s" /* Used for Prompts, which are ASCII */ +-#define W (COLS - HDR_XOFFSET) ++#define W (COLS - HDR_XOFFSET - SidebarWidth) + + static const char * const Prompts[] = + { +@@ -110,7 +110,7 @@ static void snd_entry (char *b, size_t blen, MUTTMENU *menu, int num) + + static void redraw_crypt_lines (HEADER *msg) + { +- mvaddstr (HDR_CRYPT, 0, "Security: "); ++ mvaddstr (HDR_CRYPT, SidebarWidth, "Security: "); + + if ((WithCrypto & (APPLICATION_PGP | APPLICATION_SMIME)) == 0) + { +@@ -145,7 +145,7 @@ static void redraw_crypt_lines (HEADER *msg) + addstr (_(" (OppEnc mode)")); + + clrtoeol (); +- move (HDR_CRYPTINFO, 0); ++ move (HDR_CRYPTINFO, SidebarWidth); + clrtoeol (); + + if ((WithCrypto & APPLICATION_PGP) +@@ -162,7 +162,7 @@ static void redraw_crypt_lines (HEADER *msg) + && (msg->security & ENCRYPT) + && SmimeCryptAlg + && *SmimeCryptAlg) { +- mvprintw (HDR_CRYPTINFO, 40, "%s%s", _("Encrypt with: "), ++ mvprintw (HDR_CRYPTINFO, SidebarWidth + 40, "%s%s", _("Encrypt with: "), + NONULL(SmimeCryptAlg)); + } + } +@@ -175,7 +175,7 @@ static void redraw_mix_line (LIST *chain) + int c; + char *t; + +- mvaddstr (HDR_MIX, 0, " Mix: "); ++ mvaddstr (HDR_MIX, SidebarWidth, " Mix: "); + + if (!chain) + { +@@ -190,7 +190,7 @@ static void redraw_mix_line (LIST *chain) + if (t && t[0] == '0' && t[1] == '\0') + t = ""; + +- if (c + mutt_strlen (t) + 2 >= COLS) ++ if (c + mutt_strlen (t) + 2 >= COLS - SidebarWidth) + break; + + addstr (NONULL(t)); +@@ -242,7 +242,7 @@ static void draw_envelope_addr (int line, ADDRESS *addr) + + buf[0] = 0; + rfc822_write_address (buf, sizeof (buf), addr, 1); +- mvprintw (line, 0, TITLE_FMT, Prompts[line - 1]); ++ mvprintw (line, SidebarWidth, TITLE_FMT, Prompts[line - 1]); + mutt_paddstr (W, buf); + } + +@@ -252,10 +252,10 @@ static void draw_envelope (HEADER *msg, char *fcc) + draw_envelope_addr (HDR_TO, msg->env->to); + draw_envelope_addr (HDR_CC, msg->env->cc); + draw_envelope_addr (HDR_BCC, msg->env->bcc); +- mvprintw (HDR_SUBJECT, 0, TITLE_FMT, Prompts[HDR_SUBJECT - 1]); ++ mvprintw (HDR_SUBJECT, SidebarWidth, TITLE_FMT, Prompts[HDR_SUBJECT - 1]); + mutt_paddstr (W, NONULL (msg->env->subject)); + draw_envelope_addr (HDR_REPLYTO, msg->env->reply_to); +- mvprintw (HDR_FCC, 0, TITLE_FMT, Prompts[HDR_FCC - 1]); ++ mvprintw (HDR_FCC, SidebarWidth, TITLE_FMT, Prompts[HDR_FCC - 1]); + mutt_paddstr (W, fcc); + + if (WithCrypto) +@@ -266,7 +266,7 @@ static void draw_envelope (HEADER *msg, char *fcc) + #endif + + SETCOLOR (MT_COLOR_STATUS); +- mvaddstr (HDR_ATTACH - 1, 0, _("-- Attachments")); ++ mvaddstr (HDR_ATTACH - 1, SidebarWidth, _("-- Attachments")); + clrtoeol (); + + NORMAL_COLOR; +@@ -302,7 +302,7 @@ static int edit_address_list (int line, ADDRESS **addr) + /* redraw the expanded list so the user can see the result */ + buf[0] = 0; + rfc822_write_address (buf, sizeof (buf), *addr, 1); +- move (line, HDR_XOFFSET); ++ move (line, HDR_XOFFSET+SidebarWidth); + mutt_paddstr (W, buf); + + return 0; +@@ -562,7 +562,7 @@ int mutt_compose_menu (HEADER *msg, /* structure for new message */ + if (mutt_get_field ("Subject: ", buf, sizeof (buf), 0) == 0) + { + mutt_str_replace (&msg->env->subject, buf); +- move (HDR_SUBJECT, HDR_XOFFSET); ++ move (HDR_SUBJECT, HDR_XOFFSET + SidebarWidth); + if (msg->env->subject) + mutt_paddstr (W, msg->env->subject); + else +@@ -580,7 +580,7 @@ int mutt_compose_menu (HEADER *msg, /* structure for new message */ + { + strfcpy (fcc, buf, fcclen); + mutt_pretty_mailbox (fcc, fcclen); +- move (HDR_FCC, HDR_XOFFSET); ++ move (HDR_FCC, HDR_XOFFSET + SidebarWidth); + mutt_paddstr (W, fcc); + fccSet = 1; + } +diff --git a/curs_main.c b/curs_main.c +index 9d718ee..ea530a6 100644 +--- a/curs_main.c ++++ b/curs_main.c +@@ -26,7 +26,9 @@ + #include "mailbox.h" + #include "mapping.h" + #include "sort.h" ++#include "buffy.h" + #include "mx.h" ++#include "sidebar.h" + + #ifdef USE_POP + #include "pop.h" +@@ -596,8 +598,12 @@ int mutt_index_menu (void) + menu->redraw |= REDRAW_STATUS; + if (do_buffy_notify) + { +- if (mutt_buffy_notify () && option (OPTBEEPNEW)) +- beep (); ++ if (mutt_buffy_notify ()) ++ { ++ menu->redraw |= REDRAW_FULL; ++ if (option (OPTBEEPNEW)) ++ beep (); ++ } + } + else + do_buffy_notify = 1; +@@ -609,6 +615,7 @@ int mutt_index_menu (void) + if (menu->redraw & REDRAW_FULL) + { + menu_redraw_full (menu); ++ draw_sidebar(menu->menu); + mutt_show_error (); + } + +@@ -631,9 +638,12 @@ int mutt_index_menu (void) + + if (menu->redraw & REDRAW_STATUS) + { ++ DrawFullLine = 1; + menu_status_line (buf, sizeof (buf), menu, NONULL (Status)); ++ DrawFullLine = 0; + move (option (OPTSTATUSONTOP) ? 0 : LINES-2, 0); + SETCOLOR (MT_COLOR_STATUS); ++ set_buffystats(Context); + mutt_paddstr (COLS, buf); + NORMAL_COLOR; + menu->redraw &= ~REDRAW_STATUS; +@@ -653,7 +663,7 @@ int mutt_index_menu (void) + menu->oldcurrent = -1; + + if (option (OPTARROWCURSOR)) +- move (menu->current - menu->top + menu->offset, 2); ++ move (menu->current - menu->top + menu->offset, SidebarWidth + 2); + else if (option (OPTBRAILLEFRIENDLY)) + move (menu->current - menu->top + menu->offset, 0); + else +@@ -1154,6 +1164,7 @@ int mutt_index_menu (void) + menu->redraw = REDRAW_FULL; + break; + ++ case OP_SIDEBAR_OPEN: + case OP_MAIN_CHANGE_FOLDER: + case OP_MAIN_NEXT_UNREAD_MAILBOX: + +@@ -1185,7 +1196,11 @@ int mutt_index_menu (void) + { + mutt_buffy (buf, sizeof (buf)); + +- if (mutt_enter_fname (cp, buf, sizeof (buf), &menu->redraw, 1) == -1) ++ if ( op == OP_SIDEBAR_OPEN ) { ++ if(!CurBuffy) ++ break; ++ strncpy( buf, CurBuffy->path, sizeof(buf) ); ++ } else if (mutt_enter_fname (cp, buf, sizeof (buf), &menu->redraw, 1) == -1) + { + if (menu->menu == MENU_PAGER) + { +@@ -1203,6 +1218,7 @@ int mutt_index_menu (void) + } + + mutt_expand_path (buf, sizeof (buf)); ++ set_curbuffy(buf); + if (mx_get_magic (buf) <= 0) + { + mutt_error (_("%s is not a mailbox."), buf); +@@ -2306,6 +2322,12 @@ int mutt_index_menu (void) + mutt_what_key(); + break; + ++ case OP_SIDEBAR_SCROLL_UP: ++ case OP_SIDEBAR_SCROLL_DOWN: ++ case OP_SIDEBAR_NEXT: ++ case OP_SIDEBAR_PREV: ++ scroll_sidebar(op, menu->menu); ++ break; + default: + if (menu->menu == MENU_MAIN) + km_error_key (MENU_MAIN); +diff --git a/flags.c b/flags.c +index 133fa35..48fb287 100644 +--- a/flags.c ++++ b/flags.c +@@ -22,8 +22,10 @@ + + #include "mutt.h" + #include "mutt_curses.h" ++#include "mutt_menu.h" + #include "sort.h" + #include "mx.h" ++#include "sidebar.h" + + void _mutt_set_flag (CONTEXT *ctx, HEADER *h, int flag, int bf, int upd_ctx) + { +@@ -290,6 +292,7 @@ void _mutt_set_flag (CONTEXT *ctx, HEADER *h, int flag, int bf, int upd_ctx) + */ + if (h->searched && (changed != h->changed || deleted != ctx->deleted || tagged != ctx->tagged || flagged != ctx->flagged)) + h->searched = 0; ++ draw_sidebar(0); + } + + void mutt_tag_set_flag (int flag, int bf) +diff --git a/functions.h b/functions.h +index 26171a0..ef8937a 100644 +--- a/functions.h ++++ b/functions.h +@@ -170,6 +170,11 @@ const struct binding_t OpMain[] = { /* map: index */ + { "decrypt-save", OP_DECRYPT_SAVE, NULL }, + + ++ { "sidebar-scroll-up", OP_SIDEBAR_SCROLL_UP, NULL }, ++ { "sidebar-scroll-down", OP_SIDEBAR_SCROLL_DOWN, NULL }, ++ { "sidebar-next", OP_SIDEBAR_NEXT, NULL }, ++ { "sidebar-prev", OP_SIDEBAR_PREV, NULL }, ++ { "sidebar-open", OP_SIDEBAR_OPEN, NULL }, + { NULL, 0, NULL } + }; + +@@ -274,6 +279,11 @@ const struct binding_t OpPager[] = { /* map: pager */ + + { "what-key", OP_WHAT_KEY, NULL }, + ++ { "sidebar-scroll-up", OP_SIDEBAR_SCROLL_UP, NULL }, ++ { "sidebar-scroll-down", OP_SIDEBAR_SCROLL_DOWN, NULL }, ++ { "sidebar-next", OP_SIDEBAR_NEXT, NULL }, ++ { "sidebar-prev", OP_SIDEBAR_PREV, NULL }, ++ { "sidebar-open", OP_SIDEBAR_OPEN, NULL }, + { NULL, 0, NULL } + }; + +diff --git a/globals.h b/globals.h +index 282fde3..004c795 100644 +--- a/globals.h ++++ b/globals.h +@@ -118,6 +118,7 @@ WHERE short SearchContext; + WHERE char *SendCharset; + WHERE char *Sendmail; + WHERE char *Shell; ++WHERE char *SidebarDelim; + WHERE char *Signature; + WHERE char *SimpleSearch; + #if USE_SMTP +@@ -214,6 +215,9 @@ WHERE short ScoreThresholdDelete; + WHERE short ScoreThresholdRead; + WHERE short ScoreThresholdFlag; + ++WHERE struct buffy_t *CurBuffy INITVAL(0); ++WHERE short DrawFullLine INITVAL(0); ++WHERE short SidebarWidth; + #ifdef USE_IMAP + WHERE short ImapKeepalive; + WHERE short ImapPipelineDepth; +diff --git a/imap/command.c b/imap/command.c +index 32f8417..d68e3ab 100644 +--- a/imap/command.c ++++ b/imap/command.c +@@ -1012,6 +1012,13 @@ static void cmd_parse_status (IMAP_DATA* idata, char* s) + opened */ + status->uidnext = oldun; + ++ /* Added to make the sidebar show the correct numbers */ ++ if (status->messages) ++ { ++ inc->msgcount = status->messages; ++ inc->msg_unread = status->unseen; ++ } ++ + FREE (&value); + return; + } +diff --git a/imap/imap.c b/imap/imap.c +index f476873..af3ac3d 100644 +--- a/imap/imap.c ++++ b/imap/imap.c +@@ -1529,7 +1529,7 @@ int imap_buffy_check (int force) + + imap_munge_mbox_name (munged, sizeof (munged), name); + snprintf (command, sizeof (command), +- "STATUS %s (UIDNEXT UIDVALIDITY UNSEEN RECENT)", munged); ++ "STATUS %s (UIDNEXT UIDVALIDITY UNSEEN RECENT MESSAGES)", munged); + + if (imap_exec (idata, command, IMAP_CMD_QUEUE) < 0) + { +diff --git a/init.h b/init.h +index 35224c1..c664e5f 100644 +--- a/init.h ++++ b/init.h +@@ -2030,6 +2030,27 @@ struct option_t MuttVars[] = { + ** not used. + ** (PGP only) + */ ++ {"sidebar_delim", DT_STR, R_BOTH, UL &SidebarDelim, "|"}, ++ /* ++ ** .pp ++ ** This specifies the delimiter between the sidebar (if visible) and ++ ** other screens. ++ */ ++ { "sidebar_visible", DT_BOOL, R_BOTH, OPTSIDEBAR, 0 }, ++ /* ++ ** .pp ++ ** This specifies whether or not to show sidebar (left-side list of folders). ++ */ ++ { "sidebar_sort", DT_BOOL, R_BOTH, OPTSIDEBARSORT, 0 }, ++ /* ++ ** .pp ++ ** This specifies whether or not to sort the sidebar alphabetically. ++ */ ++ { "sidebar_width", DT_NUM, R_BOTH, UL &SidebarWidth, 0 }, ++ /* ++ ** .pp ++ ** The width of the sidebar. ++ */ + { "pgp_use_gpg_agent", DT_BOOL, R_NONE, OPTUSEGPGAGENT, 0}, + /* + ** .pp +diff --git a/mailbox.h b/mailbox.h +index 2b2c9a1..000503d 100644 +--- a/mailbox.h ++++ b/mailbox.h +@@ -27,6 +27,7 @@ + #define M_NEWFOLDER (1<<4) /* create a new folder - same as M_APPEND, but uses + * safe_fopen() for mbox-style folders. + */ ++#define M_PEEK (1<<5) /* revert atime back after taking a look (if applicable) */ + + /* mx_open_new_message() */ + #define M_ADD_FROM (1<<0) /* add a From_ line */ +diff --git a/mbox.c b/mbox.c +index 6d3b6bd..fa82eb3 100644 +--- a/mbox.c ++++ b/mbox.c +@@ -104,6 +104,7 @@ int mmdf_parse_mailbox (CONTEXT *ctx) + mutt_perror (ctx->path); + return (-1); + } ++ ctx->atime = sb.st_atime; + ctx->mtime = sb.st_mtime; + ctx->size = sb.st_size; + +@@ -255,6 +256,7 @@ int mbox_parse_mailbox (CONTEXT *ctx) + + ctx->size = sb.st_size; + ctx->mtime = sb.st_mtime; ++ ctx->atime = sb.st_atime; + + #ifdef NFS_ATTRIBUTE_HACK + if (sb.st_mtime > sb.st_atime) +diff --git a/menu.c b/menu.c +index 27b5f8e..bc3a02f 100644 +--- a/menu.c ++++ b/menu.c +@@ -24,6 +24,7 @@ + #include "mutt_curses.h" + #include "mutt_menu.h" + #include "mbyte.h" ++#include "sidebar.h" + + extern size_t UngetCount; + +@@ -186,7 +187,7 @@ static void menu_pad_string (char *s, size_t n) + { + char *scratch = safe_strdup (s); + int shift = option (OPTARROWCURSOR) ? 3 : 0; +- int cols = COLS - shift; ++ int cols = COLS - shift - SidebarWidth; + + mutt_format_string (s, n, cols, cols, FMT_LEFT, ' ', scratch, mutt_strlen (scratch), 1); + s[n - 1] = 0; +@@ -239,6 +240,7 @@ void menu_redraw_index (MUTTMENU *menu) + int do_color; + int attr; + ++ draw_sidebar(1); + for (i = menu->top; i < menu->top + menu->pagelen; i++) + { + if (i < menu->max) +@@ -249,7 +251,7 @@ void menu_redraw_index (MUTTMENU *menu) + menu_pad_string (buf, sizeof (buf)); + + ATTRSET(attr); +- move(i - menu->top + menu->offset, 0); ++ move(i - menu->top + menu->offset, SidebarWidth); + do_color = 1; + + if (i == menu->current) +@@ -272,7 +274,7 @@ void menu_redraw_index (MUTTMENU *menu) + else + { + NORMAL_COLOR; +- CLEARLINE(i - menu->top + menu->offset); ++ CLEARLINE_WIN(i - menu->top + menu->offset); + } + } + NORMAL_COLOR; +@@ -289,7 +291,7 @@ void menu_redraw_motion (MUTTMENU *menu) + return; + } + +- move (menu->oldcurrent + menu->offset - menu->top, 0); ++ move (menu->oldcurrent + menu->offset - menu->top, SidebarWidth); + ATTRSET(menu->color (menu->oldcurrent)); + + if (option (OPTARROWCURSOR)) +@@ -301,13 +303,13 @@ void menu_redraw_motion (MUTTMENU *menu) + { + menu_make_entry (buf, sizeof (buf), menu, menu->oldcurrent); + menu_pad_string (buf, sizeof (buf)); +- move (menu->oldcurrent + menu->offset - menu->top, 3); ++ move (menu->oldcurrent + menu->offset - menu->top, SidebarWidth + 3); + print_enriched_string (menu->color(menu->oldcurrent), (unsigned char *) buf, 1); + } + + /* now draw it in the new location */ + SETCOLOR(MT_COLOR_INDICATOR); +- mvaddstr(menu->current + menu->offset - menu->top, 0, "->"); ++ mvaddstr(menu->current + menu->offset - menu->top, SidebarWidth, "->"); + } + else + { +@@ -320,7 +322,7 @@ void menu_redraw_motion (MUTTMENU *menu) + menu_make_entry (buf, sizeof (buf), menu, menu->current); + menu_pad_string (buf, sizeof (buf)); + SETCOLOR(MT_COLOR_INDICATOR); +- move(menu->current - menu->top + menu->offset, 0); ++ move(menu->current - menu->top + menu->offset, SidebarWidth); + print_enriched_string (menu->color(menu->current), (unsigned char *) buf, 0); + } + menu->redraw &= REDRAW_STATUS; +@@ -332,7 +334,7 @@ void menu_redraw_current (MUTTMENU *menu) + char buf[LONG_STRING]; + int attr = menu->color (menu->current); + +- move (menu->current + menu->offset - menu->top, 0); ++ move (menu->current + menu->offset - menu->top, SidebarWidth); + menu_make_entry (buf, sizeof (buf), menu, menu->current); + menu_pad_string (buf, sizeof (buf)); + +@@ -881,7 +883,7 @@ int mutt_menuLoop (MUTTMENU *menu) + + + if (option (OPTARROWCURSOR)) +- move (menu->current - menu->top + menu->offset, 2); ++ move (menu->current - menu->top + menu->offset, SidebarWidth + 2); + else if (option (OPTBRAILLEFRIENDLY)) + move (menu->current - menu->top + menu->offset, 0); + else +diff --git a/mh.c b/mh.c +index 63e12d2..4a84a99 100644 +--- a/mh.c ++++ b/mh.c +@@ -295,6 +295,28 @@ void mh_buffy(BUFFY *b) + mhs_free_sequences (&mhs); + } + ++void mh_buffy_update (const char *path, int *msgcount, int *msg_unread, int *msg_flagged) ++{ ++ int i; ++ struct mh_sequences mhs; ++ memset (&mhs, 0, sizeof (mhs)); ++ ++ if (mh_read_sequences (&mhs, path) < 0) ++ return; ++ ++ msgcount = 0; ++ msg_unread = 0; ++ msg_flagged = 0; ++ for (i = 0; i <= mhs.max; i++) ++ msgcount++; ++ if (mhs_check (&mhs, i) & MH_SEQ_UNSEEN) { ++ msg_unread++; ++ } ++ if (mhs_check (&mhs, i) & MH_SEQ_FLAGGED) ++ msg_flagged++; ++ mhs_free_sequences (&mhs); ++} ++ + static int mh_mkstemp (CONTEXT * dest, FILE ** fp, char **tgt) + { + int fd; +diff --git a/mutt.h b/mutt.h +index 01d47de..5f25406 100644 +--- a/mutt.h ++++ b/mutt.h +@@ -435,6 +435,8 @@ enum + OPTSAVEEMPTY, + OPTSAVENAME, + OPTSCORE, ++ OPTSIDEBAR, ++ OPTSIDEBARSORT, + OPTSIGDASHES, + OPTSIGONTOP, + OPTSORTRE, +@@ -880,6 +882,7 @@ typedef struct _context + { + char *path; + FILE *fp; ++ time_t atime; + time_t mtime; + off_t size; + off_t vsize; +@@ -920,6 +923,7 @@ typedef struct _context + unsigned int quiet : 1; /* inhibit status messages? */ + unsigned int collapsed : 1; /* are all threads collapsed? */ + unsigned int closing : 1; /* mailbox is being closed */ ++ unsigned int peekonly : 1; /* just taking a glance, revert atime */ + + /* driver hooks */ + void *data; /* driver specific data */ +diff --git a/mutt_curses.h b/mutt_curses.h +index f8bc47c..ef9884e 100644 +--- a/mutt_curses.h ++++ b/mutt_curses.h +@@ -64,6 +64,7 @@ + #undef lines + #endif /* lines */ + ++#define CLEARLINE_WIN(x) move(x,SidebarWidth), clrtoeol() + #define CLEARLINE(x) move(x,0), clrtoeol() + #define CENTERLINE(x,y) move(y, (COLS-strlen(x))/2), addstr(x) + #define BEEP() do { if (option (OPTBEEP)) beep(); } while (0) +@@ -121,6 +122,8 @@ enum + MT_COLOR_UNDERLINE, + MT_COLOR_INDEX, + MT_COLOR_PROMPT, ++ MT_COLOR_NEW, ++ MT_COLOR_FLAGGED, + MT_COLOR_MAX + }; + +diff --git a/muttlib.c b/muttlib.c +index c1d565f..039e7c3 100644 +--- a/muttlib.c ++++ b/muttlib.c +@@ -1279,6 +1279,8 @@ void mutt_FormatString (char *dest, /* output buffer */ + pl = pw = 1; + + /* see if there's room to add content, else ignore */ ++ if ( DrawFullLine ) ++ { + if ((col < COLS && wlen < destlen) || soft) + { + int pad; +@@ -1322,6 +1324,52 @@ void mutt_FormatString (char *dest, /* output buffer */ + col += wid; + src += pl; + } ++ } ++ else ++ { ++ if ((col < COLS-SidebarWidth && wlen < destlen) || soft) ++ { ++ int pad; ++ ++ /* get contents after padding */ ++ mutt_FormatString (buf, sizeof (buf), 0, src + pl, callback, data, flags); ++ len = mutt_strlen (buf); ++ wid = mutt_strwidth (buf); ++ ++ /* try to consume as many columns as we can, if we don't have ++ * memory for that, use as much memory as possible */ ++ pad = (COLS - SidebarWidth - col - wid) / pw; ++ if (pad > 0 && wlen + (pad * pl) + len > destlen) ++ pad = ((signed)(destlen - wlen - len)) / pl; ++ if (pad > 0) ++ { ++ while (pad--) ++ { ++ memcpy (wptr, src, pl); ++ wptr += pl; ++ wlen += pl; ++ col += pw; ++ } ++ } ++ else if (soft && pad < 0) ++ { ++ /* \0-terminate dest for length computation in mutt_wstr_trunc() */ ++ *wptr = 0; ++ /* make sure right part is at most as wide as display */ ++ len = mutt_wstr_trunc (buf, destlen, COLS, &wid); ++ /* truncate left so that right part fits completely in */ ++ wlen = mutt_wstr_trunc (dest, destlen - len, col + pad, &col); ++ wptr = dest + wlen; ++ } ++ if (len + wlen > destlen) ++ len = mutt_wstr_trunc (buf, destlen - wlen, COLS - SidebarWidth - col, NULL); ++ memcpy (wptr, buf, len); ++ wptr += len; ++ wlen += len; ++ col += wid; ++ src += pl; ++ } ++ } + break; /* skip rest of input */ + } + else if (ch == '|') +diff --git a/mx.c b/mx.c +index 0a1a80e..e80b8ff 100644 +--- a/mx.c ++++ b/mx.c +@@ -595,6 +595,7 @@ static int mx_open_mailbox_append (CONTEXT *ctx, int flags) + * M_APPEND open mailbox for appending + * M_READONLY open mailbox in read-only mode + * M_QUIET only print error messages ++ * M_PEEK revert atime where applicable + * ctx if non-null, context struct to use + */ + CONTEXT *mx_open_mailbox (const char *path, int flags, CONTEXT *pctx) +@@ -617,6 +618,8 @@ CONTEXT *mx_open_mailbox (const char *path, int flags, CONTEXT *pctx) + ctx->quiet = 1; + if (flags & M_READONLY) + ctx->readonly = 1; ++ if (flags & M_PEEK) ++ ctx->peekonly = 1; + + if (flags & (M_APPEND|M_NEWFOLDER)) + { +@@ -721,9 +724,21 @@ CONTEXT *mx_open_mailbox (const char *path, int flags, CONTEXT *pctx) + void mx_fastclose_mailbox (CONTEXT *ctx) + { + int i; ++#ifndef BUFFY_SIZE ++ struct utimbuf ut; ++#endif + + if(!ctx) + return; ++#ifndef BUFFY_SIZE ++ /* fix up the times so buffy won't get confused */ ++ if (ctx->peekonly && ctx->path && ctx->mtime > ctx->atime) ++ { ++ ut.actime = ctx->atime; ++ ut.modtime = ctx->mtime; ++ utime (ctx->path, &ut); ++ } ++#endif + + /* never announce that a mailbox we've just left has new mail. #3290 + * XXX: really belongs in mx_close_mailbox, but this is a nice hook point */ +diff --git a/mx.h b/mx.h +index 2ef4ec7..4aabadf 100644 +--- a/mx.h ++++ b/mx.h +@@ -60,6 +60,7 @@ void mbox_reset_atime (CONTEXT *, struct stat *); + int mh_read_dir (CONTEXT *, const char *); + int mh_sync_mailbox (CONTEXT *, int *); + int mh_check_mailbox (CONTEXT *, int *); ++void mh_buffy_update (const char *, int *, int *, int *); + int mh_check_empty (const char *); + + int maildir_read_dir (CONTEXT *); +diff --git a/pager.c b/pager.c +index c99f1e4..5cfcb75 100644 +--- a/pager.c ++++ b/pager.c +@@ -29,6 +29,7 @@ + #include "pager.h" + #include "attach.h" + #include "mbyte.h" ++#include "sidebar.h" + + #include "mutt_crypt.h" + +@@ -1095,6 +1096,7 @@ static int format_line (struct line_t **lineInfo, int n, unsigned char *buf, + wchar_t wc; + mbstate_t mbstate; + int wrap_cols = mutt_term_width ((flags & M_PAGER_NOWRAP) ? 0 : Wrap); ++ wrap_cols -= SidebarWidth; + + if (check_attachment_marker ((char *)buf) == 0) + wrap_cols = COLS; +@@ -1746,7 +1748,7 @@ mutt_pager (const char *banner, const char *fname, int flags, pager_t *extra) + if ((redraw & REDRAW_BODY) || topline != oldtopline) + { + do { +- move (bodyoffset, 0); ++ move (bodyoffset, SidebarWidth); + curline = oldtopline = topline; + lines = 0; + force_redraw = 0; +@@ -1759,6 +1761,7 @@ mutt_pager (const char *banner, const char *fname, int flags, pager_t *extra) + &QuoteList, &q_level, &force_redraw, &SearchRE) > 0) + lines++; + curline++; ++ move(lines + bodyoffset, SidebarWidth); + } + last_offset = lineInfo[curline].offset; + } while (force_redraw); +@@ -1771,6 +1774,7 @@ mutt_pager (const char *banner, const char *fname, int flags, pager_t *extra) + addch ('~'); + addch ('\n'); + lines++; ++ move(lines + bodyoffset, SidebarWidth); + } + NORMAL_COLOR; + +@@ -1794,22 +1798,22 @@ mutt_pager (const char *banner, const char *fname, int flags, pager_t *extra) + strfcpy(pager_progress_str, (topline == 0) ? "all" : "end", sizeof(pager_progress_str)); + + /* print out the pager status bar */ +- move (statusoffset, 0); ++ move (statusoffset, SidebarWidth); + SETCOLOR (MT_COLOR_STATUS); + + if (IsHeader (extra) || IsMsgAttach (extra)) + { +- size_t l1 = COLS * MB_LEN_MAX; ++ size_t l1 = (COLS-SidebarWidth) * MB_LEN_MAX; + size_t l2 = sizeof (buffer); + hfi.hdr = (IsHeader (extra)) ? extra->hdr : extra->bdy->hdr; + mutt_make_string_info (buffer, l1 < l2 ? l1 : l2, NONULL (PagerFmt), &hfi, M_FORMAT_MAKEPRINT); +- mutt_paddstr (COLS, buffer); ++ mutt_paddstr (COLS-SidebarWidth, buffer); + } + else + { + char bn[STRING]; + snprintf (bn, sizeof (bn), "%s (%s)", banner, pager_progress_str); +- mutt_paddstr (COLS, bn); ++ mutt_paddstr (COLS-SidebarWidth, bn); + } + NORMAL_COLOR; + if (option(OPTTSENABLED) && TSSupported) +@@ -1826,16 +1830,21 @@ mutt_pager (const char *banner, const char *fname, int flags, pager_t *extra) + /* redraw the pager_index indicator, because the + * flags for this message might have changed. */ + menu_redraw_current (index); ++ draw_sidebar(MENU_PAGER); + + /* print out the index status bar */ + menu_status_line (buffer, sizeof (buffer), index, NONULL(Status)); + +- move (indexoffset + (option (OPTSTATUSONTOP) ? 0 : (indexlen - 1)), 0); ++ move (indexoffset + (option (OPTSTATUSONTOP) ? 0 : (indexlen - 1)), SidebarWidth); + SETCOLOR (MT_COLOR_STATUS); +- mutt_paddstr (COLS, buffer); ++ mutt_paddstr (COLS-SidebarWidth, buffer); + NORMAL_COLOR; + } + ++ /* if we're not using the index, update every time */ ++ if ( index == 0 ) ++ draw_sidebar(MENU_PAGER); ++ + redraw = 0; + + if (option(OPTBRAILLEFRIENDLY)) { +@@ -2776,6 +2785,13 @@ search_next: + mutt_what_key (); + break; + ++ case OP_SIDEBAR_SCROLL_UP: ++ case OP_SIDEBAR_SCROLL_DOWN: ++ case OP_SIDEBAR_NEXT: ++ case OP_SIDEBAR_PREV: ++ scroll_sidebar(ch, MENU_PAGER); ++ break; ++ + default: + ch = -1; + break; +diff --git a/sidebar.c b/sidebar.c +new file mode 100644 +index 0000000..6098c2a +--- /dev/null ++++ b/sidebar.c +@@ -0,0 +1,333 @@ ++/* ++ * Copyright (C) ????-2004 Justin Hibbits ++ * Copyright (C) 2004 Thomer M. Gil ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. ++ */ ++ ++ ++#if HAVE_CONFIG_H ++# include "config.h" ++#endif ++ ++#include "mutt.h" ++#include "mutt_menu.h" ++#include "mutt_curses.h" ++#include "sidebar.h" ++#include "buffy.h" ++#include ++#include "keymap.h" ++#include ++ ++/*BUFFY *CurBuffy = 0;*/ ++static BUFFY *TopBuffy = 0; ++static BUFFY *BottomBuffy = 0; ++static int known_lines = 0; ++ ++static int quick_log10(int n) ++{ ++ char string[32]; ++ sprintf(string, "%d", n); ++ return strlen(string); ++} ++ ++void calc_boundaries (int menu) ++{ ++ BUFFY *tmp = Incoming; ++ ++ if ( known_lines != LINES ) { ++ TopBuffy = BottomBuffy = 0; ++ known_lines = LINES; ++ } ++ for ( ; tmp->next != 0; tmp = tmp->next ) ++ tmp->next->prev = tmp; ++ ++ if ( TopBuffy == 0 && BottomBuffy == 0 ) ++ TopBuffy = Incoming; ++ if ( BottomBuffy == 0 ) { ++ int count = LINES - 2 - (menu != MENU_PAGER || option(OPTSTATUSONTOP)); ++ BottomBuffy = TopBuffy; ++ while ( --count && BottomBuffy->next ) ++ BottomBuffy = BottomBuffy->next; ++ } ++ else if ( TopBuffy == CurBuffy->next ) { ++ int count = LINES - 2 - (menu != MENU_PAGER); ++ BottomBuffy = CurBuffy; ++ tmp = BottomBuffy; ++ while ( --count && tmp->prev) ++ tmp = tmp->prev; ++ TopBuffy = tmp; ++ } ++ else if ( BottomBuffy == CurBuffy->prev ) { ++ int count = LINES - 2 - (menu != MENU_PAGER); ++ TopBuffy = CurBuffy; ++ tmp = TopBuffy; ++ while ( --count && tmp->next ) ++ tmp = tmp->next; ++ BottomBuffy = tmp; ++ } ++} ++ ++char *make_sidebar_entry(char *box, int size, int new, int flagged) ++{ ++ static char *entry = 0; ++ char *c; ++ int i = 0; ++ int delim_len = strlen(SidebarDelim); ++ ++ c = realloc(entry, SidebarWidth - delim_len + 2); ++ if ( c ) entry = c; ++ entry[SidebarWidth - delim_len + 1] = 0; ++ for (; i < SidebarWidth - delim_len + 1; entry[i++] = ' ' ); ++ i = strlen(box); ++ strncpy( entry, box, i < (SidebarWidth - delim_len + 1) ? i : (SidebarWidth - delim_len + 1) ); ++ ++ if (size == -1) ++ sprintf(entry + SidebarWidth - delim_len - 3, "?"); ++ else if ( new ) { ++ if (flagged > 0) { ++ sprintf( ++ entry + SidebarWidth - delim_len - 5 - quick_log10(size) - quick_log10(new) - quick_log10(flagged), ++ "% d(%d)[%d]", size, new, flagged); ++ } else { ++ sprintf( ++ entry + SidebarWidth - delim_len - 3 - quick_log10(size) - quick_log10(new), ++ "% d(%d)", size, new); ++ } ++ } else if (flagged > 0) { ++ sprintf( entry + SidebarWidth - delim_len - 3 - quick_log10(size) - quick_log10(flagged), "% d[%d]", size, flagged); ++ } else { ++ sprintf( entry + SidebarWidth - delim_len - 1 - quick_log10(size), "% d", size); ++ } ++ return entry; ++} ++ ++void set_curbuffy(char buf[LONG_STRING]) ++{ ++ BUFFY* tmp = CurBuffy = Incoming; ++ ++ if (!Incoming) ++ return; ++ ++ while(1) { ++ if(!strcmp(tmp->path, buf)) { ++ CurBuffy = tmp; ++ break; ++ } ++ ++ if(tmp->next) ++ tmp = tmp->next; ++ else ++ break; ++ } ++} ++ ++int draw_sidebar(int menu) { ++ ++ int lines = option(OPTHELP) ? 1 : 0; ++ BUFFY *tmp; ++#ifndef USE_SLANG_CURSES ++ attr_t attrs; ++#endif ++ short delim_len = strlen(SidebarDelim); ++ short color_pair; ++ ++ static bool initialized = false; ++ static int prev_show_value; ++ static short saveSidebarWidth; ++ ++ /* initialize first time */ ++ if(!initialized) { ++ prev_show_value = option(OPTSIDEBAR); ++ saveSidebarWidth = SidebarWidth; ++ if(!option(OPTSIDEBAR)) SidebarWidth = 0; ++ initialized = true; ++ } ++ ++ /* save or restore the value SidebarWidth */ ++ if(prev_show_value != option(OPTSIDEBAR)) { ++ if(prev_show_value && !option(OPTSIDEBAR)) { ++ saveSidebarWidth = SidebarWidth; ++ SidebarWidth = 0; ++ } else if(!prev_show_value && option(OPTSIDEBAR)) { ++ SidebarWidth = saveSidebarWidth; ++ } ++ prev_show_value = option(OPTSIDEBAR); ++ } ++ ++ ++// if ( SidebarWidth == 0 ) return 0; ++ if (SidebarWidth > 0 && option (OPTSIDEBAR) ++ && delim_len >= SidebarWidth) { ++ unset_option (OPTSIDEBAR); ++ /* saveSidebarWidth = SidebarWidth; */ ++ if (saveSidebarWidth > delim_len) { ++ SidebarWidth = saveSidebarWidth; ++ mutt_error (_("Value for sidebar_delim is too long. Disabling sidebar.")); ++ sleep (2); ++ } else { ++ SidebarWidth = 0; ++ mutt_error (_("Value for sidebar_delim is too long. Disabling sidebar. Please set your sidebar_width to a sane value.")); ++ sleep (4); /* the advise to set a sane value should be seen long enough */ ++ } ++ saveSidebarWidth = 0; ++ return (0); ++ } ++ ++ if ( SidebarWidth == 0 || !option(OPTSIDEBAR)) { ++ if (SidebarWidth > 0) { ++ saveSidebarWidth = SidebarWidth; ++ SidebarWidth = 0; ++ } ++ unset_option(OPTSIDEBAR); ++ return 0; ++ } ++ ++ /* get attributes for divider */ ++ SETCOLOR(MT_COLOR_STATUS); ++#ifndef USE_SLANG_CURSES ++ attr_get(&attrs, &color_pair, 0); ++#else ++ color_pair = attr_get(); ++#endif ++ SETCOLOR(MT_COLOR_NORMAL); ++ ++ /* draw the divider */ ++ ++ for ( ; lines < LINES-1-(menu != MENU_PAGER || option(OPTSTATUSONTOP)); lines++ ) { ++ move(lines, SidebarWidth - delim_len); ++ addstr(NONULL(SidebarDelim)); ++#ifndef USE_SLANG_CURSES ++ mvchgat(lines, SidebarWidth - delim_len, delim_len, 0, color_pair, NULL); ++#endif ++ } ++ ++ if ( Incoming == 0 ) return 0; ++ lines = option(OPTHELP) ? 1 : 0; /* go back to the top */ ++ ++ if ( known_lines != LINES || TopBuffy == 0 || BottomBuffy == 0 ) ++ calc_boundaries(menu); ++ if ( CurBuffy == 0 ) CurBuffy = Incoming; ++ ++ tmp = TopBuffy; ++ ++ SETCOLOR(MT_COLOR_NORMAL); ++ ++ for ( ; tmp && lines < LINES-1 - (menu != MENU_PAGER || option(OPTSTATUSONTOP)); tmp = tmp->next ) { ++ if ( tmp == CurBuffy ) ++ SETCOLOR(MT_COLOR_INDICATOR); ++ else if ( tmp->msg_unread > 0 ) ++ SETCOLOR(MT_COLOR_NEW); ++ else if ( tmp->msg_flagged > 0 ) ++ SETCOLOR(MT_COLOR_FLAGGED); ++ else ++ SETCOLOR(MT_COLOR_NORMAL); ++ ++ move( lines, 0 ); ++ if ( Context && !strcmp( tmp->path, Context->path ) ) { ++ tmp->msg_unread = Context->unread; ++ tmp->msgcount = Context->msgcount; ++ tmp->msg_flagged = Context->flagged; ++ } ++ // check whether Maildir is a prefix of the current folder's path ++ short maildir_is_prefix = 0; ++ if ( (strlen(tmp->path) > strlen(Maildir)) && ++ (strncmp(Maildir, tmp->path, strlen(Maildir)) == 0) ) ++ maildir_is_prefix = 1; ++ // calculate depth of current folder and generate its display name with indented spaces ++ int sidebar_folder_depth = 0; ++ char *sidebar_folder_name; ++ sidebar_folder_name = basename(tmp->path); ++ if ( maildir_is_prefix ) { ++ char *tmp_folder_name; ++ int i; ++ tmp_folder_name = tmp->path + strlen(Maildir); ++ for (i = 0; i < strlen(tmp->path) - strlen(Maildir); i++) { ++ if (tmp_folder_name[i] == '/') sidebar_folder_depth++; ++ } ++ if (sidebar_folder_depth > 0) { ++ sidebar_folder_name = malloc(strlen(basename(tmp->path)) + sidebar_folder_depth + 1); ++ for (i=0; i < sidebar_folder_depth; i++) ++ sidebar_folder_name[i]=' '; ++ sidebar_folder_name[i]=0; ++ strncat(sidebar_folder_name, basename(tmp->path), strlen(basename(tmp->path)) + sidebar_folder_depth); ++ } ++ } ++ printw( "%.*s", SidebarWidth - delim_len + 1, ++ make_sidebar_entry(sidebar_folder_name, tmp->msgcount, ++ tmp->msg_unread, tmp->msg_flagged)); ++ if (sidebar_folder_depth > 0) ++ free(sidebar_folder_name); ++ lines++; ++ } ++ SETCOLOR(MT_COLOR_NORMAL); ++ for ( ; lines < LINES-1 - (menu != MENU_PAGER || option(OPTSTATUSONTOP)); lines++ ) { ++ int i = 0; ++ move( lines, 0 ); ++ for ( ; i < SidebarWidth - delim_len; i++ ) ++ addch(' '); ++ } ++ return 0; ++} ++ ++ ++void set_buffystats(CONTEXT* Context) ++{ ++ BUFFY *tmp = Incoming; ++ while(tmp) { ++ if(Context && !strcmp(tmp->path, Context->path)) { ++ tmp->msg_unread = Context->unread; ++ tmp->msgcount = Context->msgcount; ++ break; ++ } ++ tmp = tmp->next; ++ } ++} ++ ++void scroll_sidebar(int op, int menu) ++{ ++ if(!SidebarWidth) return; ++ if(!CurBuffy) return; ++ ++ switch (op) { ++ case OP_SIDEBAR_NEXT: ++ if ( CurBuffy->next == NULL ) return; ++ CurBuffy = CurBuffy->next; ++ break; ++ case OP_SIDEBAR_PREV: ++ if ( CurBuffy->prev == NULL ) return; ++ CurBuffy = CurBuffy->prev; ++ break; ++ case OP_SIDEBAR_SCROLL_UP: ++ CurBuffy = TopBuffy; ++ if ( CurBuffy != Incoming ) { ++ calc_boundaries(menu); ++ CurBuffy = CurBuffy->prev; ++ } ++ break; ++ case OP_SIDEBAR_SCROLL_DOWN: ++ CurBuffy = BottomBuffy; ++ if ( CurBuffy->next ) { ++ calc_boundaries(menu); ++ CurBuffy = CurBuffy->next; ++ } ++ break; ++ default: ++ return; ++ } ++ calc_boundaries(menu); ++ draw_sidebar(menu); ++} ++ +diff --git a/sidebar.h b/sidebar.h +new file mode 100644 +index 0000000..d195f11 +--- /dev/null ++++ b/sidebar.h +@@ -0,0 +1,36 @@ ++/* ++ * Copyright (C) ????-2004 Justin Hibbits ++ * Copyright (C) 2004 Thomer M. Gil ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. ++ */ ++ ++#ifndef SIDEBAR_H ++#define SIDEBAR_H ++ ++struct MBOX_LIST { ++ char *path; ++ int msgcount; ++ int new; ++} MBLIST; ++ ++/* parameter is whether or not to go to the status line */ ++/* used for omitting the last | that covers up the status bar in the index */ ++int draw_sidebar(int); ++void scroll_sidebar(int, int); ++void set_curbuffy(char*); ++void set_buffystats(CONTEXT*); ++ ++#endif /* SIDEBAR_H */ diff --git a/pkgs/applications/networking/mailreaders/mutt/trash-folder.patch b/pkgs/applications/networking/mailreaders/mutt/trash-folder.patch new file mode 100644 index 00000000000..ce47b0c30e5 --- /dev/null +++ b/pkgs/applications/networking/mailreaders/mutt/trash-folder.patch @@ -0,0 +1,316 @@ +From: Cedric Duval +Date: Thu, 27 Feb 2014 12:27:41 +0100 +Subject: trash-folder + +With this patch, if the trash variable is set to a path (unset by default), the +deleted mails will be moved to a trash folder instead of being irremediably +purged when syncing the mailbox. + +For instance, set trash="~/Mail/trash" will cause every deleted mail to go to +this folder. + +Note that the append to the trash folder doesn't occur until the resync is +done. This allows you to change your mind and undo deletes, and thus the moves +to the trash folder are unnecessary. + +Notes + + * You might also want to have a look at the purge message feature below + which is related to this patch. + * IMAP is now supported. To retain the previous behavior, add this to your + muttrc: + folder-hook ^imap:// 'unset trash' + +FAQ + +Every once in a while, someone asks what are the advantages of this patch over +a macro based solution. Here's an attempt to answer this question: + + * The folder history doesn't clutter up with unwanted trash entries. + * Delayed move to the trash allows to change one's mind. + * No need to treat the case of "normal folders" and trash folders + separately with folder-hooks, and to create two sets of macros (one for + the index, one for the pager). + * Works not only with delete-message, but also with every deletion + functions like delete-pattern, delete-thread or delete-subthread. + +To sum up, it's more integrated and transparent to the user. + +* Patch last synced with upstream: + - Date: 2007-02-15 + - File: http://cedricduval.free.fr/mutt/patches/download/patch-1.5.5.1.cd.trash_folder.3.4 + +* Changes made: + - Updated to 1.5.13: + - structure of _mutt_save_message changed (commands.c) + - context of option (OPTCONFIRMAPPEND) changed (muttlib.c) + - Fixed indentation of "appended" in mutt.h. + +Signed-off-by: Matteo F. Vescovi + +Gbp-Pq: Topic features +--- + commands.c | 1 + + flags.c | 19 +++++++++++++++++- + globals.h | 1 + + imap/message.c | 2 ++ + init.h | 10 ++++++++++ + mutt.h | 3 +++ + muttlib.c | 4 +++- + mx.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + postpone.c | 3 +++ + 9 files changed, 103 insertions(+), 2 deletions(-) + +diff --git a/commands.c b/commands.c +index 5dbd100..7fd014b 100644 +--- a/commands.c ++++ b/commands.c +@@ -720,6 +720,7 @@ int _mutt_save_message (HEADER *h, CONTEXT *ctx, int delete, int decode, int dec + if (option (OPTDELETEUNTAG)) + mutt_set_flag (Context, h, M_TAG, 0); + } ++ mutt_set_flag (Context, h, M_APPENDED, 1); + + return 0; + } +diff --git a/flags.c b/flags.c +index f0f3d81..dfa6a50 100644 +--- a/flags.c ++++ b/flags.c +@@ -65,7 +65,13 @@ void _mutt_set_flag (CONTEXT *ctx, HEADER *h, int flag, int bf, int upd_ctx) + { + h->deleted = 0; + update = 1; +- if (upd_ctx) ctx->deleted--; ++ if (upd_ctx) ++ { ++ ctx->deleted--; ++ if (h->appended) ++ ctx->appended--; ++ } ++ h->appended = 0; /* when undeleting, also reset the appended flag */ + #ifdef USE_IMAP + /* see my comment above */ + if (ctx->magic == M_IMAP) +@@ -87,6 +93,17 @@ void _mutt_set_flag (CONTEXT *ctx, HEADER *h, int flag, int bf, int upd_ctx) + } + break; + ++ case M_APPENDED: ++ if (bf) ++ { ++ if (!h->appended) ++ { ++ h->appended = 1; ++ if (upd_ctx) ctx->appended++; ++ } ++ } ++ break; ++ + case M_NEW: + + if (!mutt_bit_isset(ctx->rights,M_ACL_SEEN)) +diff --git a/globals.h b/globals.h +index e77030c..6a1b8da 100644 +--- a/globals.h ++++ b/globals.h +@@ -144,6 +144,7 @@ WHERE char *Tochars; + WHERE char *TSStatusFormat; + WHERE char *TSIconFormat; + WHERE short TSSupported; ++WHERE char *TrashPath; + WHERE char *Username; + WHERE char *Visual; + +diff --git a/imap/message.c b/imap/message.c +index 3877381..039fda6 100644 +--- a/imap/message.c ++++ b/imap/message.c +@@ -884,6 +884,7 @@ int imap_copy_messages (CONTEXT* ctx, HEADER* h, char* dest, int delete) + if (ctx->hdrs[n]->tagged) + { + mutt_set_flag (ctx, ctx->hdrs[n], M_DELETE, 1); ++ mutt_set_flag (ctx, ctx->hdrs[n], M_APPENDED, 1); + if (option (OPTDELETEUNTAG)) + mutt_set_flag (ctx, ctx->hdrs[n], M_TAG, 0); + } +@@ -891,6 +892,7 @@ int imap_copy_messages (CONTEXT* ctx, HEADER* h, char* dest, int delete) + else + { + mutt_set_flag (ctx, h, M_DELETE, 1); ++ mutt_set_flag (ctx, h, M_APPENDED, 1); + if (option (OPTDELETEUNTAG)) + mutt_set_flag (ctx, h, M_TAG, 0); + } +diff --git a/init.h b/init.h +index 6b49341..d3206f9 100644 +--- a/init.h ++++ b/init.h +@@ -3341,6 +3341,16 @@ struct option_t MuttVars[] = { + ** provided that ``$$ts_enabled'' has been set. This string is identical in + ** formatting to the one used by ``$$status_format''. + */ ++ { "trash", DT_PATH, R_NONE, UL &TrashPath, 0 }, ++ /* ++ ** .pp ++ ** If set, this variable specifies the path of the trash folder where the ++ ** mails marked for deletion will be moved, instead of being irremediably ++ ** purged. ++ ** .pp ++ ** NOTE: When you delete a message in the trash folder, it is really ++ ** deleted, so that you have a way to clean the trash. ++ */ + #ifdef USE_SOCKET + { "tunnel", DT_STR, R_NONE, UL &Tunnel, UL 0 }, + /* +diff --git a/mutt.h b/mutt.h +index f8565fa..29bb6c2 100644 +--- a/mutt.h ++++ b/mutt.h +@@ -185,6 +185,7 @@ enum + M_DELETE, + M_UNDELETE, + M_DELETED, ++ M_APPENDED, + M_FLAG, + M_TAG, + M_UNTAG, +@@ -713,6 +714,7 @@ typedef struct header + unsigned int mime : 1; /* has a MIME-Version header? */ + unsigned int flagged : 1; /* marked important? */ + unsigned int tagged : 1; ++ unsigned int appended : 1; /* has been saved */ + unsigned int deleted : 1; + unsigned int changed : 1; + unsigned int attach_del : 1; /* has an attachment marked for deletion */ +@@ -885,6 +887,7 @@ typedef struct _context + int new; /* how many new messages? */ + int unread; /* how many unread messages? */ + int deleted; /* how many deleted messages */ ++ int appended; /* how many saved messages? */ + int flagged; /* how many flagged messages */ + int msgnotreadyet; /* which msg "new" in pager, -1 if none */ + +diff --git a/muttlib.c b/muttlib.c +index 02067cc..0fd9766 100644 +--- a/muttlib.c ++++ b/muttlib.c +@@ -1505,7 +1505,9 @@ int mutt_save_confirm (const char *s, struct stat *st) + + if (magic > 0 && !mx_access (s, W_OK)) + { +- if (option (OPTCONFIRMAPPEND)) ++ if (option (OPTCONFIRMAPPEND) && ++ (!TrashPath || (mutt_strcmp (s, TrashPath) != 0))) ++ /* if we're appending to the trash, there's no point in asking */ + { + snprintf (tmp, sizeof (tmp), _("Append messages to %s?"), s); + if ((rc = mutt_yesorno (tmp, M_YES)) == M_NO) +diff --git a/mx.c b/mx.c +index 4c5cb07..c0a6d30 100644 +--- a/mx.c ++++ b/mx.c +@@ -776,6 +776,53 @@ static int sync_mailbox (CONTEXT *ctx, int *index_hint) + return rc; + } + ++/* move deleted mails to the trash folder */ ++static int trash_append (CONTEXT *ctx) ++{ ++ CONTEXT *ctx_trash; ++ int i = 0; ++ struct stat st, stc; ++ ++ if (!TrashPath || !ctx->deleted || ++ (ctx->magic == M_MAILDIR && option (OPTMAILDIRTRASH))) ++ return 0; ++ ++ for (;i < ctx->msgcount && (!ctx->hdrs[i]->deleted || ++ ctx->hdrs[i]->appended); i++); ++ if (i == ctx->msgcount) ++ return 0; /* nothing to be done */ ++ ++ if (mutt_save_confirm (TrashPath, &st) != 0) ++ { ++ mutt_error _("message(s) not deleted"); ++ return -1; ++ } ++ ++ if (lstat (ctx->path, &stc) == 0 && stc.st_ino == st.st_ino ++ && stc.st_dev == st.st_dev && stc.st_rdev == st.st_rdev) ++ return 0; /* we are in the trash folder: simple sync */ ++ ++ if ((ctx_trash = mx_open_mailbox (TrashPath, M_APPEND, NULL)) != NULL) ++ { ++ for (i = 0 ; i < ctx->msgcount ; i++) ++ if (ctx->hdrs[i]->deleted && !ctx->hdrs[i]->appended ++ && mutt_append_message (ctx_trash, ctx, ctx->hdrs[i], 0, 0) == -1) ++ { ++ mx_close_mailbox (ctx_trash, NULL); ++ return -1; ++ } ++ ++ mx_close_mailbox (ctx_trash, NULL); ++ } ++ else ++ { ++ mutt_error _("Can't open trash folder"); ++ return -1; ++ } ++ ++ return 0; ++} ++ + /* save changes and close mailbox */ + int mx_close_mailbox (CONTEXT *ctx, int *index_hint) + { +@@ -912,6 +959,7 @@ int mx_close_mailbox (CONTEXT *ctx, int *index_hint) + if (mutt_append_message (&f, ctx, ctx->hdrs[i], 0, CH_UPDATE_LEN) == 0) + { + mutt_set_flag (ctx, ctx->hdrs[i], M_DELETE, 1); ++ mutt_set_flag (ctx, ctx->hdrs[i], M_APPENDED, 1); + } + else + { +@@ -936,6 +984,14 @@ int mx_close_mailbox (CONTEXT *ctx, int *index_hint) + return 0; + } + ++ /* copy mails to the trash before expunging */ ++ if (purge && ctx->deleted && mutt_strcmp(ctx->path, TrashPath)) ++ if (trash_append (ctx) != 0) ++ { ++ ctx->closing = 0; ++ return -1; ++ } ++ + #ifdef USE_IMAP + /* allow IMAP to preserve the deleted flag across sessions */ + if (ctx->magic == M_IMAP) +@@ -1133,6 +1189,12 @@ int mx_sync_mailbox (CONTEXT *ctx, int *index_hint) + msgcount = ctx->msgcount; + deleted = ctx->deleted; + ++ if (purge && ctx->deleted && mutt_strcmp(ctx->path, TrashPath)) ++ { ++ if (trash_append (ctx) == -1) ++ return -1; ++ } ++ + #ifdef USE_IMAP + if (ctx->magic == M_IMAP) + rc = imap_sync_mailbox (ctx, purge, index_hint); +diff --git a/postpone.c b/postpone.c +index a703161..7a4cbb1 100644 +--- a/postpone.c ++++ b/postpone.c +@@ -277,6 +277,9 @@ int mutt_get_postponed (CONTEXT *ctx, HEADER *hdr, HEADER **cur, char *fcc, size + /* finished with this message, so delete it. */ + mutt_set_flag (PostContext, h, M_DELETE, 1); + ++ /* and consider it saved, so that it won't be moved to the trash folder */ ++ mutt_set_flag (PostContext, h, M_APPENDED, 1); ++ + /* update the count for the status display */ + PostCount = PostContext->msgcount - PostContext->deleted; +