From 544ac66853afe69a7bda8511d3b52cf61f4999842d0ec08449efb0d045e7c583 Mon Sep 17 00:00:00 2001 From: Stephan Kulow Date: Wed, 4 Sep 2013 11:16:41 +0000 Subject: [PATCH] Accepting request 197243 from server:mail - Update to new upstream release 2.11 (forwarded request 197185 from jengelh) OBS-URL: https://build.opensuse.org/request/show/197243 OBS-URL: https://build.opensuse.org/package/show/openSUSE:Factory/alpine?expand=0&rev=33 --- alpine-2.00-as_needed.patch | 10 - alpine-2.10.clean.tar.lzma | 3 - alpine-2.11.tar.xz | 3 + alpine-gcc44.diff | 23 +- alpine-maildir-closedir.patch | 39 - alpine-month_name-utf8.patch | 16 +- alpine-no-add-needed.patch | 14 +- alpine.changes | 24 + alpine.spec | 59 +- chappa-WrtAcc.patch | 757 ++- chappa-colortext.patch | 1061 ++-- chappa-fancy.patch | 7200 ++++++++++------------ chappa-ignoresize.patch | 223 +- chappa-insertpat.patch | 83 +- chappa-maildir.patch | 7347 +++++++++++------------ chappa-unixnullbug.patch | 80 +- chappa-unverified.patch | 111 - fix-implicit.patch | 10 +- operation-may-be-undefined-warning.diff | 21 +- 19 files changed, 7966 insertions(+), 9118 deletions(-) delete mode 100644 alpine-2.00-as_needed.patch delete mode 100644 alpine-2.10.clean.tar.lzma create mode 100644 alpine-2.11.tar.xz delete mode 100644 alpine-maildir-closedir.patch delete mode 100644 chappa-unverified.patch diff --git a/alpine-2.00-as_needed.patch b/alpine-2.00-as_needed.patch deleted file mode 100644 index b74199c..0000000 --- a/alpine-2.00-as_needed.patch +++ /dev/null @@ -1,10 +0,0 @@ ---- configure.ac -+++ configure.ac -@@ -1307,6 +1307,7 @@ - AC_CHECK_LIB(pam, pam_start, - [ - alpine_c_client_target="lnp" -+ LIBS="$LIBS -lpam" - ], - [ - if test -f /etc/shadow ; then diff --git a/alpine-2.10.clean.tar.lzma b/alpine-2.10.clean.tar.lzma deleted file mode 100644 index 707b689..0000000 --- a/alpine-2.10.clean.tar.lzma +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ee9a0a08e2bdeafb42448c759fcc38f1d960ed9485a7c7178d8c4016b13898cc -size 4849936 diff --git a/alpine-2.11.tar.xz b/alpine-2.11.tar.xz new file mode 100644 index 0000000..1f47462 --- /dev/null +++ b/alpine-2.11.tar.xz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fecc9ca5df03e0f368edec65cd61444325157b5635b5c92380cedf9c4ce8fbf6 +size 4403188 diff --git a/alpine-gcc44.diff b/alpine-gcc44.diff index 78dd423..7d29626 100644 --- a/alpine-gcc44.diff +++ b/alpine-gcc44.diff @@ -1,6 +1,13 @@ ---- ./imap/src/osdep/unix/dummy.c 2009/06/02 09:53:30 1.1 -+++ ./imap/src/osdep/unix/dummy.c 2009/06/02 10:04:31 -@@ -723,6 +723,17 @@ +--- + imap/src/osdep/unix/dummy.c | 14 ++++++++++++-- + pith/send.c | 1 + + 2 files changed, 13 insertions(+), 2 deletions(-) + +Index: alpine-2.11/imap/src/osdep/unix/dummy.c +=================================================================== +--- alpine-2.11.orig/imap/src/osdep/unix/dummy.c ++++ alpine-2.11/imap/src/osdep/unix/dummy.c +@@ -736,6 +736,17 @@ long dummy_copy (MAILSTREAM *stream,char return NIL; } @@ -18,7 +25,7 @@ /* Dummy append message string * Accepts: mail stream -@@ -742,8 +753,7 @@ +@@ -755,8 +766,7 @@ long dummy_append (MAILSTREAM *stream,ch /* append to INBOX? */ if (!compare_cstring (mailbox,"INBOX")) { /* yes, if no empty proto try creating */ @@ -28,9 +35,11 @@ } else if (dummy_file (tmp,mailbox) && ((fd = open (tmp,O_RDONLY,NIL)) < 0)) { if ((e = errno) == ENOENT) /* failed, was it no such file? */ ---- pith/send.c 2009/06/02 10:04:13 1.11 -+++ pith/send.c 2009/06/02 10:04:31 -@@ -1569,6 +1569,7 @@ +Index: alpine-2.11/pith/send.c +=================================================================== +--- alpine-2.11.orig/pith/send.c ++++ alpine-2.11/pith/send.c +@@ -1570,6 +1570,7 @@ set_priority_header(METAENV *header, cha pf->textbuf = cpystr(value); } } diff --git a/alpine-maildir-closedir.patch b/alpine-maildir-closedir.patch deleted file mode 100644 index a847917..0000000 --- a/alpine-maildir-closedir.patch +++ /dev/null @@ -1,39 +0,0 @@ ---- imap/src/osdep/unix/maildir.c 2011-10-17 10:11:55.000000000 +0200 -+++ imap/src/osdep/unix/maildir.c 2011-10-17 10:39:31.000000000 +0200 -@@ -2396,7 +2396,6 @@ - || !strncmp(d->d_name, MDUIDTEMP, strlen(MDUIDTEMP))) - break; - } -- closedir(dir); - rv = d ? !strncmp(d->d_name, tmp, strlen(tmp)) : 1; - createtemp = d ? 0 : 1; - if (d && rv == 0){ /* is there a temp file that is not ours? */ -@@ -2410,6 +2409,7 @@ - unlink(tmp); - } - } -+ closedir(dir); - if(createtemp){ - FILE *fp; - sprintf(tmp,"%s/%s.%d.%lu", LOCAL->dir, MDUIDTEMP, getpid(), time(0)); -@@ -2442,7 +2442,6 @@ - if(!strncmp(d->d_name, MDUIDLAST, strlen(MDUIDLAST))) - break; - } -- closedir(dir); - createuid = d == NULL ? 1 : 0; - if(uid_last == NULL) - deleteuid++; -@@ -2473,9 +2472,10 @@ - } - } - if(deleteuid){ -- sprintf(tmp,"%s/%s", LOCAL->dir, d->d_name); -+ snprintf(tmp, sizeof(tmp), "%s/%s", LOCAL->dir, d->d_name); - unlink(tmp); - } -+ closedir(dir); - if(createuid) - maildir_write_uid(stream, (uid_last ? *uid_last : stream->uid_last), - uid_validity ? *uid_validity : time(0)); -Nur in alpine-2.00/imap/src/osdep/unix: maildir.c~. diff --git a/alpine-month_name-utf8.patch b/alpine-month_name-utf8.patch index cba912f..d2128bb 100644 --- a/alpine-month_name-utf8.patch +++ b/alpine-month_name-utf8.patch @@ -1,6 +1,12 @@ ---- pith/string.c -+++ pith/string.c -@@ -769,6 +769,7 @@ +--- + pith/string.c | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +Index: alpine-2.11/pith/string.c +=================================================================== +--- alpine-2.11.orig/pith/string.c ++++ alpine-2.11/pith/string.c +@@ -770,6 +770,7 @@ month_abbrev_locale(int month_num) return("xxx"); else{ static char buf[20]; @@ -8,7 +14,7 @@ struct tm tm; memset(&tm, 0, sizeof(tm)); -@@ -810,6 +811,12 @@ +@@ -811,6 +812,12 @@ month_abbrev_locale(int month_num) buf[0] = ' '; } @@ -21,7 +27,7 @@ return(buf); } -@@ -841,12 +848,19 @@ +@@ -842,12 +849,19 @@ month_name_locale(int month_num) return(""); else{ static char buf[20]; diff --git a/alpine-no-add-needed.patch b/alpine-no-add-needed.patch index 6b39c8c..7188b53 100644 --- a/alpine-no-add-needed.patch +++ b/alpine-no-add-needed.patch @@ -3,10 +3,10 @@ configure.ac | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) -Index: alpine-2.10/alpine/Makefile.am +Index: alpine-2.11/alpine/Makefile.am =================================================================== ---- alpine-2.10.orig/alpine/Makefile.am -+++ alpine-2.10/alpine/Makefile.am +--- alpine-2.11.orig/alpine/Makefile.am ++++ alpine-2.11/alpine/Makefile.am @@ -35,7 +35,7 @@ BUILT_SOURCES = date.c LDADD = ../pico/libpico.a ../pico/osdep/libpicoosd.a \ ../pith/libpith.a ../pith/osdep/libpithosd.a \ @@ -16,11 +16,11 @@ Index: alpine-2.10/alpine/Makefile.am AM_CPPFLAGS = -I@top_builddir@/include -I@top_srcdir@/include -DLOCALEDIR=\"$(localedir)\" -Index: alpine-2.10/configure.ac +Index: alpine-2.11/configure.ac =================================================================== ---- alpine-2.10.orig/configure.ac -+++ alpine-2.10/configure.ac -@@ -37,8 +37,9 @@ WEB_BUILD=web/src/alpined.d +--- alpine-2.11.orig/configure.ac ++++ alpine-2.11/configure.ac +@@ -38,8 +38,9 @@ WEB_BUILD=web/src/alpined.d dnl CHECK PROGRAMS diff --git a/alpine.changes b/alpine.changes index 094b3d4..41b4e12 100644 --- a/alpine.changes +++ b/alpine.changes @@ -1,3 +1,27 @@ +------------------------------------------------------------------- +Mon Sep 2 14:07:32 UTC 2013 - jengelh@inai.de + +- Update to new upstream release 2.11 +* Increase encryption of S/MIME encrypted messages. +* Pico: Improvements in justification of paragraphs: lines that + begin with a quote string, followed by a space were considered + individual paragraphs, now they are considered part of a paragraph. +* Unix Alpine: Allow local .pinerc file to be a symbolic link. +* Experimental extended support of recognition of UTF-8 in urls +* Added recognition of ws and wss URIs. +* Add ability to color folder names, directory names, and text in + the FOLDER SCREEN. +* Add the ability to color any token used in the display of the + INDEX SCREEN. +* New option preserve-original-fields that adds the ability to + preserve To: and Cc: fields when replying to a message, as + specified by original sender. +* Added Quota subcommands for printing, forwarding, saving, etc. +- Refresh from homepage (and save as unified): chappa-*.patch +- Refresh for -p1: operation-may-be-undefined-warning.diff, + fix-implicit.patch, alpine-gcc44.diff +- Drop quilt-patches/alpine-2.00-as_needed.patch (merged upstream) + ------------------------------------------------------------------- Wed May 22 20:37:21 UTC 2013 - jengelh@inai.de diff --git a/alpine.spec b/alpine.spec index 58fa2d7..07513c3 100644 --- a/alpine.spec +++ b/alpine.spec @@ -17,34 +17,16 @@ Name: alpine -# # For debugging only: %define build_vanilla 0 -# Summary: Mail User Agent License: Apache-2.0 Group: Productivity/Networking/Email/Clients -BuildRequires: imap-devel -BuildRequires: krb5-devel -BuildRequires: libgssapi -BuildRequires: libtool -BuildRequires: ncurses-devel -BuildRequires: openldap2-devel -BuildRequires: openssl-devel -BuildRequires: pam-devel -BuildRequires: update-desktop-files -BuildRequires: xz -# pgp4pine requires pine: -Provides: pine -# -Obsoletes: pine4 -Provides: pine4 -# -Version: 2.10 +Version: 2.11 Release: 0 -Url: http://www.washington.edu/alpine/ -Source: http://patches.freeiz.com/alpine/patches/alpine-2.10/alpine-2.10.clean.tar.lzma -BuildRoot: %{_tmppath}/%{name}-%{version}-build +Url: http://patches.freeiz.com/alpine/ + +Source: http://patches.freeiz.com/alpine/release/src/%name-%version.tar.xz Source1: %name.png Source2: %name.desktop Patch1: pine-nonvoid-function.patch @@ -52,7 +34,6 @@ Patch2: make-use-of-strncat-safer.diff Patch3: operation-may-be-undefined-warning.diff Patch4: fix-implicit.patch Patch5: alpine-gcc44.diff -Patch6: alpine-2.00-as_needed.patch Patch7: alpine-month_name-utf8.patch Patch10: pico-fix-spurious-undef-warnings.diff Patch20: pine-expression-warnings.diff @@ -70,6 +51,22 @@ Patch604: chappa-maildir.patch Patch605: chappa-WrtAcc.patch Patch606: chappa-unixnullbug.patch Patch613: alpine-no-add-needed.patch +BuildRoot: %{_tmppath}/%{name}-%{version}-build +BuildRequires: autoconf >= 2.69 +BuildRequires: imap-devel +BuildRequires: krb5-devel +BuildRequires: libgssapi +BuildRequires: libtool +BuildRequires: ncurses-devel +BuildRequires: openldap2-devel +BuildRequires: openssl-devel +BuildRequires: pam-devel +BuildRequires: update-desktop-files +BuildRequires: xz +# pgp4pine requires pine: +Provides: pine +Obsoletes: pine4 +Provides: pine4 %description Alpine is a display-oriented email client that is suitable for both @@ -133,11 +130,10 @@ fi # %patch1 -p1 %patch2 -p1 -%patch3 -%patch4 -p0 -%patch5 -p0 -%patch6 -p0 -%patch7 +%patch3 -p1 +%patch4 -p1 +%patch5 -p1 +%patch7 -p1 %patch10 -p1 %patch20 -p1 %patch40 -p1 @@ -188,6 +184,13 @@ autoreconf -fiv grep -B9 -A20 'failed program was' config.log | grep -A24 checking exit 5 } +%{?fedora_version: tag=LFD} +%{?mandriva_version: tag=LMD} +%{?redhat_version: tag=LRH} +%{?centos_version: tag=LRH} +%{?suse_version: tag=LSU} +perl -i -pe 's{(define SYSTYPE) "LNX"}{$1 "'"$tag"'"}g' include/config.h + # # imap does not use CFLAGS from configure, needs EXTRACFLAGS/EXTRALDFLAGS: # diff --git a/chappa-WrtAcc.patch b/chappa-WrtAcc.patch index 8cc7b4e..4e089d3 100644 --- a/chappa-WrtAcc.patch +++ b/chappa-WrtAcc.patch @@ -1,408 +1,351 @@ -diff -rc alpine-2.10/pico/basic.c alpine-2.10.WrtAcc/pico/basic.c -*** alpine-2.10/pico/basic.c 2013-01-11 11:25:28.000000000 -0700 ---- alpine-2.10.WrtAcc/pico/basic.c 2013-01-11 20:43:11.000000000 -0700 -*************** -*** 344,349 **** ---- 344,532 ---- - return(TRUE); - } - -+ unsigned char GetAccent() -+ { -+ UCS c,d; -+ c = GetKey(); -+ if ((c == '?') || (c == '!')) { -+ d = c; -+ c = '\\'; -+ } -+ else -+ if ((c == 's') || (c == 'S')){ -+ c = d = 's'; +--- + pico/basic.c | 183 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + pico/composer.c | 9 ++ + pico/display.c | 7 +- + pico/ebind.h | 8 +- + pico/efunc.h | 3 + pico/main.c | 6 + + pico/search.c | 6 - + 7 files changed, 214 insertions(+), 8 deletions(-) + +Index: alpine-2.11/pico/basic.c +=================================================================== +--- alpine-2.11.orig/pico/basic.c ++++ alpine-2.11/pico/basic.c +@@ -344,6 +344,189 @@ gotobop(int f, int n) + return(TRUE); + } + ++unsigned char GetAccent() ++{ ++ UCS c,d; ++ c = GetKey(); ++ if ((c == '?') || (c == '!')) { ++ d = c; ++ c = '\\'; ++ } ++ else ++ if ((c == 's') || (c == 'S')){ ++ c = d = 's'; ++ } ++ else ++ if ((c == 'l') || (c == 'L')){ ++ c = d = 'l'; ++ } ++ else ++ d = GetKey(); ++ return accent(c,d); ++} ++ ++int pineaccent(f,n) ++ int f,n; ++{ unsigned char e; ++ ++ if (e = GetAccent()) ++ execute(e, 0, 1); ++ return 1; ++} ++ ++unsigned char accent(f,n) ++UCS f,n; ++{ UCS c,d; ++ ++ c = f; ++ d = n; ++ switch(c){ ++ case '~' : ++ switch(d){ ++ case 'a' : return '\343'; ++ case 'n' : return '\361'; ++ case 'o' : return '\365'; ++ case 'A' : return '\303'; ++ case 'N' : return '\321'; ++ case 'O' : return '\325'; ++ } ++ break; ++ case '\047' : ++ switch(d){ ++ case 'a' : return '\341'; ++ case 'e' : return '\351'; ++ case 'i' : return '\355'; ++ case 'o' : return '\363'; ++ case 'u' : return '\372'; ++ case 'y' : return '\375'; ++ case 'A' : return '\301'; ++ case 'E' : return '\311'; ++ case 'I' : return '\315'; ++ case 'O' : return '\323'; ++ case 'U' : return '\332'; ++ case 'Y' : return '\335'; ++ } ++ break; ++ case '"' : ++ switch(d){ ++ case 'a' : return '\344'; ++ case 'e' : return '\353'; ++ case 'i' : return '\357'; ++ case 'o' : return '\366'; ++ case 'u' : return '\374'; ++ case 'y' : return '\377'; ++ case 'A' : return '\304'; ++ case 'E' : return '\313'; ++ case 'I' : return '\317'; ++ case 'O' : return '\326'; ++ case 'U' : return '\334'; ++ } ++ break; ++ case '^' : ++ switch(d){ ++ case 'a' : return '\342'; ++ case 'e' : return '\352'; ++ case 'i' : return '\356'; ++ case 'o' : return '\364'; ++ case 'u' : return '\373'; ++ case 'A' : return '\302'; ++ case 'E' : return '\312'; ++ case 'I' : return '\316'; ++ case 'O' : return '\324'; ++ case 'U' : return '\333'; ++ case '0' : return '\260'; ++ case '1' : return '\271'; ++ case '2' : return '\262'; ++ case '3' : return '\263'; ++ } ++ break; ++ case '`' : ++ switch(d){ ++ case 'a' : return '\340'; ++ case 'e' : return '\350'; ++ case 'i' : return '\354'; ++ case 'o' : return '\362'; ++ case 'u' : return '\371'; ++ case 'A' : return '\300'; ++ case 'E' : return '\310'; ++ case 'I' : return '\314'; ++ case 'O' : return '\322'; ++ case 'U' : return '\331'; ++ } ++ break; ++ case 'o' : ++ switch(d){ ++ case 'a' : return '\345'; ++ case 'A' : return '\305'; ++ case '/' : return '\370'; ++ case 'r' : return '\256'; ++ case 'R' : return '\256'; ++ case 'c' : return '\251'; ++ case 'C' : return '\251'; ++ } ++ break; ++ case '-' : ++ switch(d){ ++ case 'o' : return '\272'; ++ case 'O' : return '\272'; ++ case '0' : return '\272'; ++ case 'a' : return '\252'; ++ case 'A' : return '\252'; ++ case 'l' : return '\243'; ++ case 'L' : return '\243'; ++ } ++ break; ++ case 'O' : ++ switch(d){ ++ case '/' : return '\330'; ++ case 'r' : return '\256'; ++ case 'R' : return '\256'; ++ case 'c' : return '\251'; ++ case 'C' : return '\251'; ++ } ++ case '/' : ++ switch(d){ ++ case 'o' : return '\370'; ++ case 'O' : return '\330'; ++ } ++ break; ++ case 'a' : ++ switch(d){ ++ case 'e' : return '\346'; ++ case 'E' : return '\346'; ++ } ++ break; ++ case 'A' : ++ switch(d){ ++ case 'E' : return '\306'; ++ case 'e' : return '\306'; ++ } ++ break; ++ case ',' : ++ switch(d){ ++ case 'c' : return '\347'; ++ case 'C' : return '\307'; ++ } ++ break; ++ case '\\' : ++ switch(d){ ++ case '?' : return '\277'; ++ case '!' : return '\241'; ++ } ++ break; ++ case 's' : ++ switch(d){ ++ case 's' : return '\337'; ++ } ++ break; ++ case 'l' : ++ switch(d){ ++ case 'l' : return '\243'; ++ } ++ break; + } -+ else -+ if ((c == 'l') || (c == 'L')){ -+ c = d = 'l'; -+ } -+ else -+ d = GetKey(); -+ return accent(c,d); -+ } -+ -+ int pineaccent(f,n) -+ int f,n; -+ { unsigned char e; -+ -+ if (e = GetAccent()) -+ execute(e, 0, 1); -+ return 1; -+ } -+ -+ unsigned char accent(f,n) -+ UCS f,n; -+ { UCS c,d; -+ -+ c = f; -+ d = n; -+ switch(c){ -+ case '~' : -+ switch(d){ -+ case 'a' : return '\343'; -+ case 'n' : return '\361'; -+ case 'o' : return '\365'; -+ case 'A' : return '\303'; -+ case 'N' : return '\321'; -+ case 'O' : return '\325'; -+ } -+ break; -+ case '\047' : -+ switch(d){ -+ case 'a' : return '\341'; -+ case 'e' : return '\351'; -+ case 'i' : return '\355'; -+ case 'o' : return '\363'; -+ case 'u' : return '\372'; -+ case 'y' : return '\375'; -+ case 'A' : return '\301'; -+ case 'E' : return '\311'; -+ case 'I' : return '\315'; -+ case 'O' : return '\323'; -+ case 'U' : return '\332'; -+ case 'Y' : return '\335'; -+ } -+ break; -+ case '"' : -+ switch(d){ -+ case 'a' : return '\344'; -+ case 'e' : return '\353'; -+ case 'i' : return '\357'; -+ case 'o' : return '\366'; -+ case 'u' : return '\374'; -+ case 'y' : return '\377'; -+ case 'A' : return '\304'; -+ case 'E' : return '\313'; -+ case 'I' : return '\317'; -+ case 'O' : return '\326'; -+ case 'U' : return '\334'; -+ } -+ break; -+ case '^' : -+ switch(d){ -+ case 'a' : return '\342'; -+ case 'e' : return '\352'; -+ case 'i' : return '\356'; -+ case 'o' : return '\364'; -+ case 'u' : return '\373'; -+ case 'A' : return '\302'; -+ case 'E' : return '\312'; -+ case 'I' : return '\316'; -+ case 'O' : return '\324'; -+ case 'U' : return '\333'; -+ case '0' : return '\260'; -+ case '1' : return '\271'; -+ case '2' : return '\262'; -+ case '3' : return '\263'; -+ } -+ break; -+ case '`' : -+ switch(d){ -+ case 'a' : return '\340'; -+ case 'e' : return '\350'; -+ case 'i' : return '\354'; -+ case 'o' : return '\362'; -+ case 'u' : return '\371'; -+ case 'A' : return '\300'; -+ case 'E' : return '\310'; -+ case 'I' : return '\314'; -+ case 'O' : return '\322'; -+ case 'U' : return '\331'; -+ } -+ break; -+ case 'o' : -+ switch(d){ -+ case 'a' : return '\345'; -+ case 'A' : return '\305'; -+ case '/' : return '\370'; -+ case 'r' : return '\256'; -+ case 'R' : return '\256'; -+ case 'c' : return '\251'; -+ case 'C' : return '\251'; -+ } -+ break; -+ case '-' : -+ switch(d){ -+ case 'o' : return '\272'; -+ case 'O' : return '\272'; -+ case '0' : return '\272'; -+ case 'a' : return '\252'; -+ case 'A' : return '\252'; -+ case 'l' : return '\243'; -+ case 'L' : return '\243'; -+ } -+ break; -+ case 'O' : -+ switch(d){ -+ case '/' : return '\330'; -+ case 'r' : return '\256'; -+ case 'R' : return '\256'; -+ case 'c' : return '\251'; -+ case 'C' : return '\251'; -+ } -+ case '/' : -+ switch(d){ -+ case 'o' : return '\370'; -+ case 'O' : return '\330'; -+ } -+ break; -+ case 'a' : -+ switch(d){ -+ case 'e' : return '\346'; -+ case 'E' : return '\346'; -+ } -+ break; -+ case 'A' : -+ switch(d){ -+ case 'E' : return '\306'; -+ case 'e' : return '\306'; -+ } -+ break; -+ case ',' : -+ switch(d){ -+ case 'c' : return '\347'; -+ case 'C' : return '\307'; -+ } -+ break; -+ case '\\' : -+ switch(d){ -+ case '?' : return '\277'; -+ case '!' : return '\241'; -+ } -+ break; -+ case 's' : -+ switch(d){ -+ case 's' : return '\337'; -+ } -+ break; -+ case 'l' : -+ switch(d){ -+ case 'l' : return '\243'; -+ } -+ break; -+ } -+ return '\0'; -+ } - - /* - * go forword to the end of the current paragraph -diff -rc alpine-2.10/pico/composer.c alpine-2.10.WrtAcc/pico/composer.c -*** alpine-2.10/pico/composer.c 2013-01-11 19:45:41.000000000 -0700 ---- alpine-2.10.WrtAcc/pico/composer.c 2013-01-11 20:43:11.000000000 -0700 -*************** -*** 2015,2021 **** - tbufp = &strng[ods.p_len]; - - if(VALID_KEY(ch)){ /* char input */ -! /* - * if we are allowing editing, insert the new char - * end up leaving tbufp pointing to newly - * inserted character in string, and offset to the ---- 2015,2021 ---- - tbufp = &strng[ods.p_len]; - - if(VALID_KEY(ch)){ /* char input */ -! insert_char:/* - * if we are allowing editing, insert the new char - * end up leaving tbufp pointing to newly - * inserted character in string, and offset to the -*************** -*** 2095,2100 **** ---- 2095,2107 ---- - } - else { /* interpret ch as a command */ - switch (ch = normalize_cmd(ch, ckm, 2)) { -+ case (CTRL|'\\') : -+ if (ch = GetAccent()) -+ goto insert_char; -+ else -+ clearcursor(); -+ break; -+ - case (CTRL|KEY_LEFT): /* word skip left */ - if(ods.p_ind > 0) /* Scoot one char left if possible */ - ods.p_ind--; -diff -rc alpine-2.10/pico/display.c alpine-2.10.WrtAcc/pico/display.c -*** alpine-2.10/pico/display.c 2013-01-11 11:25:29.000000000 -0700 ---- alpine-2.10.WrtAcc/pico/display.c 2013-01-11 20:43:11.000000000 -0700 -*************** -*** 1751,1756 **** ---- 1751,1761 ---- - b = &buf[ucs4_strlen(buf)]; - continue; - -+ case (CTRL|'\\'): -+ if (c = GetAccent()) -+ goto text; -+ continue; -+ - case (CTRL|'F') : /* CTRL-F forward a char*/ - case KEY_RIGHT : - if(*b == '\0') -*************** -*** 1869,1875 **** - #endif - - default : -! - /* look for match in extra_v */ - for(i = 0; i < 12; i++) - if(c && c == extra_v[i]){ ---- 1874,1880 ---- - #endif - - default : -! text: - /* look for match in extra_v */ - for(i = 0; i < 12; i++) - if(c && c == extra_v[i]){ -diff -rc alpine-2.10/pico/ebind.h alpine-2.10.WrtAcc/pico/ebind.h -*** alpine-2.10/pico/ebind.h 2013-01-11 11:25:29.000000000 -0700 ---- alpine-2.10.WrtAcc/pico/ebind.h 2013-01-11 20:43:11.000000000 -0700 -*************** -*** 61,67 **** - #ifdef MOUSE - {KEY_MOUSE, mousepress}, - #ifndef _WINDOWS -! {CTRL|'\\', toggle_xterm_mouse}, - #endif - #endif - {CTRL|'A', gotobol}, ---- 61,67 ---- - #ifdef MOUSE - {KEY_MOUSE, mousepress}, - #ifndef _WINDOWS -! {CTRL|'|', toggle_xterm_mouse}, - #endif - #endif - {CTRL|'A', gotobol}, -*************** -*** 100,106 **** - {CTRL|KEY_HOME, gotobob}, - {CTRL|KEY_END, gotoeob}, - {0x7F, backdel}, -! {0, NULL} - }; - - ---- 100,108 ---- - {CTRL|KEY_HOME, gotobob}, - {CTRL|KEY_END, gotoeob}, - {0x7F, backdel}, -! {CTRL|'\\', pineaccent}, -! {0, -! NULL} - }; - - -*************** -*** 123,129 **** - #ifdef MOUSE - {KEY_MOUSE, mousepress}, - #ifndef _WINDOWS -! {CTRL|'\\', toggle_xterm_mouse}, - #endif - #endif - {CTRL|'A', gotobol}, ---- 125,131 ---- - #ifdef MOUSE - {KEY_MOUSE, mousepress}, - #ifndef _WINDOWS -! {CTRL|'|', toggle_xterm_mouse}, - #endif - #endif - {CTRL|'A', gotobol}, -diff -rc alpine-2.10/pico/efunc.h alpine-2.10.WrtAcc/pico/efunc.h -*** alpine-2.10/pico/efunc.h 2013-01-11 11:25:29.000000000 -0700 ---- alpine-2.10.WrtAcc/pico/efunc.h 2013-01-11 20:43:11.000000000 -0700 -*************** -*** 54,59 **** ---- 54,62 ---- - extern int backline(int, int); - extern int gotobop(int, int); - extern int gotoeop(int, int); -+ extern int pineaccent(int, int); -+ extern unsigned char accent(UCS, UCS); -+ extern unsigned char GetAccent(void); - extern int forwpage(int, int); - extern int backpage(int, int); - extern int scrollupline(int, int); -diff -rc alpine-2.10/pico/main.c alpine-2.10.WrtAcc/pico/main.c -*** alpine-2.10/pico/main.c 2013-01-11 11:25:29.000000000 -0700 ---- alpine-2.10.WrtAcc/pico/main.c 2013-01-11 20:43:11.000000000 -0700 -*************** -*** 416,421 **** ---- 416,427 ---- - emlwrite(_("You may possibly have new mail."), NULL); - } - -+ if (c == (CTRL|'\\')){ -+ c = GetAccent(); -+ if (!c) -+ c = NODATA; -+ } -+ - if(km_popped) - switch(c){ - case NODATA: -diff -rc alpine-2.10/pico/search.c alpine-2.10.WrtAcc/pico/search.c -*** alpine-2.10/pico/search.c 2013-01-11 11:25:29.000000000 -0700 ---- alpine-2.10.WrtAcc/pico/search.c 2013-01-11 20:43:11.000000000 -0700 -*************** -*** 274,280 **** - } - - if(status + curwp->w_doto >= llength(curwp->w_dotp) || -! !eq(defpat[status],lgetc(curwp->w_dotp, curwp->w_doto + status).c)) - break; /* do nothing! */ - status++; - } ---- 274,280 ---- - } - - if(status + curwp->w_doto >= llength(curwp->w_dotp) || -! !eq((unsigned char)defpat[status],lgetc(curwp->w_dotp, curwp->w_doto + status).c)) - break; /* do nothing! */ - status++; - } -*************** -*** 927,933 **** - c = lgetc(curline, curoff++).c; /* get the char */ - - /* test it against first char in pattern */ -! if (eq(c, patrn[0]) != FALSE) { /* if we find it..*/ - /* setup match pointers */ - matchline = curline; - matchoff = curoff; ---- 927,933 ---- - c = lgetc(curline, curoff++).c; /* get the char */ - - /* test it against first char in pattern */ -! if (eq(c, (unsigned char)patrn[0]) != FALSE) { /* if we find it..*/ - /* setup match pointers */ - matchline = curline; - matchoff = curoff; -*************** -*** 948,954 **** - return(FALSE); - - /* and test it against the pattern */ -! if (eq(*patptr, c) == FALSE) - goto fail; - } - ---- 948,954 ---- - return(FALSE); - - /* and test it against the pattern */ -! if (eq((unsigned char) *patptr, c) == FALSE) - goto fail; - } - ++ return '\0'; ++} + + /* + * go forword to the end of the current paragraph +Index: alpine-2.11/pico/composer.c +=================================================================== +--- alpine-2.11.orig/pico/composer.c ++++ alpine-2.11/pico/composer.c +@@ -2015,7 +2015,7 @@ LineEdit(int allowedit, UCS *lastch) + tbufp = &strng[ods.p_len]; + + if(VALID_KEY(ch)){ /* char input */ +- /* ++insert_char:/* + * if we are allowing editing, insert the new char + * end up leaving tbufp pointing to newly + * inserted character in string, and offset to the +@@ -2095,6 +2095,13 @@ LineEdit(int allowedit, UCS *lastch) + } + else { /* interpret ch as a command */ + switch (ch = normalize_cmd(ch, ckm, 2)) { ++ case (CTRL|'\\') : ++ if (ch = GetAccent()) ++ goto insert_char; ++ else ++ clearcursor(); ++ break; ++ + case (CTRL|KEY_LEFT): /* word skip left */ + if(ods.p_ind > 0) /* Scoot one char left if possible */ + ods.p_ind--; +Index: alpine-2.11/pico/display.c +=================================================================== +--- alpine-2.11.orig/pico/display.c ++++ alpine-2.11/pico/display.c +@@ -1751,6 +1751,11 @@ mlreplyd(UCS *prompt, UCS *buf, int nbuf + b = &buf[ucs4_strlen(buf)]; + continue; + ++ case (CTRL|'\\'): ++ if (c = GetAccent()) ++ goto text; ++ continue; ++ + case (CTRL|'F') : /* CTRL-F forward a char*/ + case KEY_RIGHT : + if(*b == '\0') +@@ -1881,7 +1886,7 @@ mlreplyd(UCS *prompt, UCS *buf, int nbuf + #endif + + default : +- ++text: + /* look for match in extra_v */ + for(i = 0; i < 12; i++) + if(c && c == extra_v[i]){ +Index: alpine-2.11/pico/ebind.h +=================================================================== +--- alpine-2.11.orig/pico/ebind.h ++++ alpine-2.11/pico/ebind.h +@@ -61,7 +61,7 @@ KEYTAB keytab[NBINDS] = { + #ifdef MOUSE + {KEY_MOUSE, mousepress}, + #ifndef _WINDOWS +- {CTRL|'\\', toggle_xterm_mouse}, ++ {CTRL|'|', toggle_xterm_mouse}, + #endif + #endif + {CTRL|'A', gotobol}, +@@ -100,7 +100,9 @@ KEYTAB keytab[NBINDS] = { + {CTRL|KEY_HOME, gotobob}, + {CTRL|KEY_END, gotoeob}, + {0x7F, backdel}, +- {0, NULL} ++ {CTRL|'\\', pineaccent}, ++ {0, ++NULL} + }; + + +@@ -123,7 +125,7 @@ KEYTAB pkeytab[NBINDS] = { + #ifdef MOUSE + {KEY_MOUSE, mousepress}, + #ifndef _WINDOWS +- {CTRL|'\\', toggle_xterm_mouse}, ++ {CTRL|'|', toggle_xterm_mouse}, + #endif + #endif + {CTRL|'A', gotobol}, +Index: alpine-2.11/pico/efunc.h +=================================================================== +--- alpine-2.11.orig/pico/efunc.h ++++ alpine-2.11/pico/efunc.h +@@ -54,6 +54,9 @@ extern int forwline(int, int); + extern int backline(int, int); + extern int gotobop(int, int); + extern int gotoeop(int, int); ++extern int pineaccent(int, int); ++extern unsigned char accent(UCS, UCS); ++extern unsigned char GetAccent(void); + extern int forwpage(int, int); + extern int backpage(int, int); + extern int scrollupline(int, int); +Index: alpine-2.11/pico/main.c +=================================================================== +--- alpine-2.11.orig/pico/main.c ++++ alpine-2.11/pico/main.c +@@ -416,6 +416,12 @@ main(int argc, char *argv[]) + emlwrite(_("You may possibly have new mail."), NULL); + } + ++ if (c == (CTRL|'\\')){ ++ c = GetAccent(); ++ if (!c) ++ c = NODATA; ++ } ++ + if(km_popped) + switch(c){ + case NODATA: +Index: alpine-2.11/pico/search.c +=================================================================== +--- alpine-2.11.orig/pico/search.c ++++ alpine-2.11/pico/search.c +@@ -278,7 +278,7 @@ forwsearch(int f, int n) + } + + if(status + curwp->w_doto >= llength(curwp->w_dotp) || +- !eq(defpat[status],lgetc(curwp->w_dotp, curwp->w_doto + status).c)) ++ !eq((unsigned char)defpat[status],lgetc(curwp->w_dotp, curwp->w_doto + status).c)) + break; /* do nothing! */ + status++; + } +@@ -931,7 +931,7 @@ forscan(int *wrapt, /* boolean indicatin + c = lgetc(curline, curoff++).c; /* get the char */ + + /* test it against first char in pattern */ +- if (eq(c, patrn[0]) != FALSE) { /* if we find it..*/ ++ if (eq(c, (unsigned char)patrn[0]) != FALSE) { /* if we find it..*/ + /* setup match pointers */ + matchline = curline; + matchoff = curoff; +@@ -952,7 +952,7 @@ forscan(int *wrapt, /* boolean indicatin + return(FALSE); + + /* and test it against the pattern */ +- if (eq(*patptr, c) == FALSE) ++ if (eq((unsigned char) *patptr, c) == FALSE) + goto fail; + } + diff --git a/chappa-colortext.patch b/chappa-colortext.patch index 44235ac..4333adb 100644 --- a/chappa-colortext.patch +++ b/chappa-colortext.patch @@ -1,545 +1,522 @@ -diff -rc alpine-2.10/alpine/confscroll.c alpine-2.10.colortext/alpine/confscroll.c -*** alpine-2.10/alpine/confscroll.c 2013-01-11 15:21:34.000000000 -0700 ---- alpine-2.10.colortext/alpine/confscroll.c 2013-01-11 20:43:16.000000000 -0700 -*************** -*** 5180,5185 **** ---- 5180,5188 ---- - - clear_index_cache(ps->mail_stream, 0); - } -+ else if(var == &ps->vars[V_SPECIAL_TEXT]){ -+ regex_pattern(ps->VAR_SPECIAL_TEXT); -+ } - else if(var == &ps->vars[V_INIT_CMD_LIST]){ - if(!revert) - q_status_message(SM_ASYNC, 0, 3, -diff -rc alpine-2.10/pith/conf.c alpine-2.10.colortext/pith/conf.c -*** alpine-2.10/pith/conf.c 2013-01-11 19:45:41.000000000 -0700 ---- alpine-2.10.colortext/pith/conf.c 2013-01-11 20:43:16.000000000 -0700 -*************** -*** 226,231 **** ---- 226,233 ---- - - CONF_TXT_T cf_text_fillcol[] = "Specifies the column of the screen where the composer should wrap."; - -+ CONF_TXT_T cf_special_text_color[] = "Specifies a comma separated list of text and regular expresions that Pine\n# will highlight"; -+ - CONF_TXT_T cf_text_replystr[] = "Specifies the string to insert when replying to a message."; - - CONF_TXT_T cf_text_quotereplstr[] = "Specifies the string to replace quotes with when viewing a message."; -*************** -*** 558,563 **** ---- 560,567 ---- - NULL, cf_text_speller}, - {"composer-wrap-column", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, - NULL, cf_text_fillcol}, -+ {"special-text-color", 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, -+ NULL, cf_special_text_color}, - {"reply-indent-string", 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, - NULL, cf_text_replystr}, - {"reply-leadin", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, -*************** -*** 809,814 **** ---- 813,820 ---- - {"incoming-unseen-background-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0}, - {"signature-foreground-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0}, - {"signature-background-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0}, -+ {"special-text-foreground-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0}, -+ {"special-text-background-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0}, - {"prompt-foreground-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0}, - {"prompt-background-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0}, - {"header-general-foreground-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0}, -*************** -*** 1972,1977 **** ---- 1978,1985 ---- - set_current_val(&vars[V_FORM_FOLDER], TRUE, TRUE); - set_current_val(&vars[V_EDITOR], TRUE, TRUE); - set_current_val(&vars[V_SPELLER], TRUE, TRUE); -+ set_current_val(&vars[V_SPECIAL_TEXT], TRUE, TRUE); -+ regex_pattern(VAR_SPECIAL_TEXT); - set_current_val(&vars[V_IMAGE_VIEWER], TRUE, TRUE); - set_current_val(&vars[V_BROWSER], TRUE, TRUE); - set_current_val(&vars[V_SMTP_SERVER], TRUE, TRUE); -*************** -*** 6424,6429 **** ---- 6432,6438 ---- - set_color_val(&vars[V_IND_OP_FORE_COLOR], 0); - set_color_val(&vars[V_INCUNSEEN_FORE_COLOR], 0); - set_color_val(&vars[V_SIGNATURE_FORE_COLOR], 0); -+ set_color_val(&vars[V_SPECIAL_TEXT_FORE_COLOR], 0); - - set_current_val(&ps->vars[V_VIEW_HDR_COLORS], TRUE, TRUE); - set_current_val(&ps->vars[V_KW_COLORS], TRUE, TRUE); -*************** -*** 7599,7604 **** ---- 7608,7615 ---- - return(h_config_scroll_margin); - case V_DEADLETS : - return(h_config_deadlets); -+ case V_SPECIAL_TEXT : -+ return(h_config_special_text_to_color); - case V_FILLCOL : - return(h_config_composer_wrap_column); - case V_TCPOPENTIMEO : -*************** -*** 7758,7763 **** ---- 7769,7777 ---- - case V_SIGNATURE_FORE_COLOR : - case V_SIGNATURE_BACK_COLOR : - return(h_config_signature_color); -+ case V_SPECIAL_TEXT_FORE_COLOR : -+ case V_SPECIAL_TEXT_BACK_COLOR : -+ return(h_config_special_text_color); - case V_PROMPT_FORE_COLOR : - case V_PROMPT_BACK_COLOR : - return(h_config_prompt_color); -diff -rc alpine-2.10/pith/conf.h alpine-2.10.colortext/pith/conf.h -*** alpine-2.10/pith/conf.h 2013-01-11 11:26:44.000000000 -0700 ---- alpine-2.10.colortext/pith/conf.h 2013-01-11 20:43:16.000000000 -0700 -*************** -*** 161,166 **** ---- 161,168 ---- - #define GLO_EDITOR vars[V_EDITOR].global_val.l - #define VAR_SPELLER vars[V_SPELLER].current_val.p - #define GLO_SPELLER vars[V_SPELLER].global_val.p -+ #define VAR_SPECIAL_TEXT vars[V_SPECIAL_TEXT].current_val.l -+ #define GLO_SPECIAL_TEXT vars[V_SPECIAL_TEXT].global_val.l - #define VAR_FILLCOL vars[V_FILLCOL].current_val.p - #define GLO_FILLCOL vars[V_FILLCOL].global_val.p - #define VAR_DEADLETS vars[V_DEADLETS].current_val.p -*************** -*** 444,449 **** ---- 446,453 ---- - #define GLO_SIGNATURE_FORE_COLOR vars[V_SIGNATURE_FORE_COLOR].global_val.p - #define VAR_SIGNATURE_BACK_COLOR vars[V_SIGNATURE_BACK_COLOR].current_val.p - #define GLO_SIGNATURE_BACK_COLOR vars[V_SIGNATURE_BACK_COLOR].global_val.p -+ #define VAR_SPECIAL_TEXT_FORE_COLOR vars[V_SPECIAL_TEXT_FORE_COLOR].current_val.p -+ #define VAR_SPECIAL_TEXT_BACK_COLOR vars[V_SPECIAL_TEXT_BACK_COLOR].current_val.p - #define VAR_PROMPT_FORE_COLOR vars[V_PROMPT_FORE_COLOR].current_val.p - #define VAR_PROMPT_BACK_COLOR vars[V_PROMPT_BACK_COLOR].current_val.p - #define VAR_VIEW_HDR_COLORS vars[V_VIEW_HDR_COLORS].current_val.l -diff -rc alpine-2.10/pith/conftype.h alpine-2.10.colortext/pith/conftype.h -*** alpine-2.10/pith/conftype.h 2013-01-11 19:45:41.000000000 -0700 ---- alpine-2.10.colortext/pith/conftype.h 2013-01-11 20:43:16.000000000 -0700 -*************** -*** 80,85 **** ---- 80,86 ---- - , V_EDITOR - , V_SPELLER - , V_FILLCOL -+ , V_SPECIAL_TEXT - , V_REPLY_STRING - , V_REPLY_INTRO - , V_QUOTE_REPLACE_STRING -*************** -*** 224,229 **** ---- 225,232 ---- - , V_INCUNSEEN_BACK_COLOR - , V_SIGNATURE_FORE_COLOR - , V_SIGNATURE_BACK_COLOR -+ , V_SPECIAL_TEXT_FORE_COLOR -+ , V_SPECIAL_TEXT_BACK_COLOR - , V_PROMPT_FORE_COLOR - , V_PROMPT_BACK_COLOR - , V_HEADER_GENERAL_FORE_COLOR -diff -rc alpine-2.10/pith/mailview.c alpine-2.10.colortext/pith/mailview.c -*** alpine-2.10/pith/mailview.c 2013-01-11 17:43:09.000000000 -0700 ---- alpine-2.10.colortext/pith/mailview.c 2013-01-11 20:43:16.000000000 -0700 -*************** -*** 282,287 **** ---- 282,295 ---- - if((flgs & FM_DISPLAY) - && !(flgs & FM_NOCOLOR) - && pico_usingcolor() -+ && ps_global->VAR_SPECIAL_TEXT_FORE_COLOR -+ && ps_global->VAR_SPECIAL_TEXT_BACK_COLOR){ -+ gf_link_filter(gf_line_test, gf_line_test_opt(color_this_text, NULL)); -+ } -+ -+ if((flgs & FM_DISPLAY) -+ && !(flgs & FM_NOCOLOR) -+ && pico_usingcolor() - && ps_global->VAR_SIGNATURE_FORE_COLOR - && ps_global->VAR_SIGNATURE_BACK_COLOR){ - gf_link_filter(gf_line_test, gf_line_test_opt(color_signature, &is_in_sig)); -*************** -*** 2503,2508 **** ---- 2511,2700 ---- - return(color_pair); - } - -+ void -+ interval_free(IVAL_S **ival) -+ { -+ if (!(*ival)) -+ return; -+ -+ if ((*ival)->next) -+ interval_free(&((*ival)->next)); -+ -+ fs_give((void **)(ival)); -+ } -+ -+ IVAL_S * -+ compute_interval (char *string, int endm) -+ { -+ IVAL_S *ival = NULL; -+ regmatch_t pmatch; -+ -+ if(ps_global->paterror == 0 && -+ regexec(&ps_global->colorpat, string + endm, 1, &pmatch, 0) == 0){ -+ ival = (IVAL_S *) fs_get(sizeof(IVAL_S)); -+ ival->start = endm + pmatch.rm_so; -+ ival->end = endm + pmatch.rm_eo; -+ ival->next = compute_interval(string, ival->end); -+ } -+ return ival; -+ } -+ -+ void -+ regex_pattern(char **plist) -+ { -+ int i = 0, j = 0, len = 0; -+ char *pattern = NULL; -+ regex_t preg; -+ -+ if(ps_global->paterror == 0) -+ regfree(&ps_global->colorpat); -+ -+ if(plist && *plist && *plist){ -+ for (i = 0; plist[i] && plist[i][0]; i++) -+ len += strlen(plist[i]) + 1; -+ pattern = (char *) fs_get(len * sizeof(char)); -+ *pattern = '\0'; -+ for (j = 0; j < i; j++){ -+ strcat(pattern, plist[j]); -+ strcat(pattern, (j < i - 1) ? "|" : ""); -+ } -+ if ((ps_global->paterror = regcomp(&preg, pattern, REG_EXTENDED)) != 0) -+ regfree(&preg); -+ else -+ ps_global->colorpat = preg; -+ } -+ if(pattern) -+ fs_give((void **)&pattern); -+ } -+ -+ LT_INS_S ** -+ insert_color_special_text(LT_INS_S **ins, char **p, IVAL_S *ival, int last_end, -+ COLOR_PAIR *col) -+ { -+ struct variable *vars = ps_global->vars; -+ -+ if (ival){ -+ *p += ival->start - last_end; -+ ins = gf_line_test_new_ins(ins, *p, color_embed(col->fg, col->bg), -+ (2 * RGBLEN) + 4); -+ *p += ival->end - ival->start; -+ ins = gf_line_test_new_ins(ins, *p, color_embed(VAR_NORM_FORE_COLOR, -+ VAR_NORM_BACK_COLOR), (2 * RGBLEN) + 4); -+ ins = insert_color_special_text(ins, p, ival->next, ival->end, col); +--- + alpine/confscroll.c | 3 + pith/conf.c | 14 +++ + pith/conf.h | 4 + + pith/conftype.h | 3 + pith/mailview.c | 192 ++++++++++++++++++++++++++++++++++++++++++++++++++++ + pith/mailview.h | 15 ++++ + pith/pine.hlp | 63 +++++++++++++++++ + pith/state.c | 3 + pith/state.h | 2 + pith/text.c | 9 ++ + 10 files changed, 308 insertions(+) + +Index: alpine-2.11/alpine/confscroll.c +=================================================================== +--- alpine-2.11.orig/alpine/confscroll.c ++++ alpine-2.11/alpine/confscroll.c +@@ -5183,6 +5183,9 @@ fix_side_effects(struct pine *ps, struct + + clear_index_cache(ps->mail_stream, 0); + } ++ else if(var == &ps->vars[V_SPECIAL_TEXT]){ ++ regex_pattern(ps->VAR_SPECIAL_TEXT); + } -+ return ins; -+ } -+ -+ int -+ length_color(char *p, int begin_color) -+ { -+ int len = 0, done = begin_color ? 0 : -1; + else if(var == &ps->vars[V_INIT_CMD_LIST]){ + if(!revert) + q_status_message(SM_ASYNC, 0, 3, +Index: alpine-2.11/pith/conf.c +=================================================================== +--- alpine-2.11.orig/pith/conf.c ++++ alpine-2.11/pith/conf.c +@@ -228,6 +228,8 @@ CONF_TXT_T cf_text_deadlets[] = "Specif + + CONF_TXT_T cf_text_fillcol[] = "Specifies the column of the screen where the composer should wrap."; + ++CONF_TXT_T cf_special_text_color[] = "Specifies a comma separated list of text and regular expresions that Pine\n# will highlight"; ++ + CONF_TXT_T cf_text_replystr[] = "Specifies the string to insert when replying to a message."; + + CONF_TXT_T cf_text_quotereplstr[] = "Specifies the string to replace quotes with when viewing a message."; +@@ -560,6 +562,8 @@ static struct variable variables[] = { + NULL, cf_text_speller}, + {"composer-wrap-column", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, + NULL, cf_text_fillcol}, ++{"special-text-color", 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, ++ NULL, cf_special_text_color}, + {"reply-indent-string", 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, + NULL, cf_text_replystr}, + {"reply-leadin", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, +@@ -817,6 +821,8 @@ static struct variable variables[] = { + {"incoming-unseen-background-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0}, + {"signature-foreground-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0}, + {"signature-background-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0}, ++{"special-text-foreground-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0}, ++{"special-text-background-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0}, + {"prompt-foreground-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0}, + {"prompt-background-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0}, + {"header-general-foreground-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0}, +@@ -1985,6 +1991,8 @@ init_vars(struct pine *ps, void (*cmds_f + set_current_val(&vars[V_FORM_FOLDER], TRUE, TRUE); + set_current_val(&vars[V_EDITOR], TRUE, TRUE); + set_current_val(&vars[V_SPELLER], TRUE, TRUE); ++ set_current_val(&vars[V_SPECIAL_TEXT], TRUE, TRUE); ++ regex_pattern(VAR_SPECIAL_TEXT); + set_current_val(&vars[V_IMAGE_VIEWER], TRUE, TRUE); + set_current_val(&vars[V_BROWSER], TRUE, TRUE); + set_current_val(&vars[V_SMTP_SERVER], TRUE, TRUE); +@@ -6483,6 +6491,7 @@ set_current_color_vals(struct pine *ps) + set_color_val(&vars[V_IND_OP_FORE_COLOR], 0); + set_color_val(&vars[V_INCUNSEEN_FORE_COLOR], 0); + set_color_val(&vars[V_SIGNATURE_FORE_COLOR], 0); ++ set_color_val(&vars[V_SPECIAL_TEXT_FORE_COLOR], 0); + + set_current_val(&ps->vars[V_INDEX_TOKEN_COLORS], TRUE, TRUE); + set_current_val(&ps->vars[V_VIEW_HDR_COLORS], TRUE, TRUE); +@@ -7664,6 +7673,8 @@ config_help(int var, int feature) + return(h_config_scroll_margin); + case V_DEADLETS : + return(h_config_deadlets); ++ case V_SPECIAL_TEXT : ++ return(h_config_special_text_to_color); + case V_FILLCOL : + return(h_config_composer_wrap_column); + case V_TCPOPENTIMEO : +@@ -7829,6 +7840,9 @@ config_help(int var, int feature) + case V_SIGNATURE_FORE_COLOR : + case V_SIGNATURE_BACK_COLOR : + return(h_config_signature_color); ++ case V_SPECIAL_TEXT_FORE_COLOR : ++ case V_SPECIAL_TEXT_BACK_COLOR : ++ return(h_config_special_text_color); + case V_PROMPT_FORE_COLOR : + case V_PROMPT_BACK_COLOR : + return(h_config_prompt_color); +Index: alpine-2.11/pith/conf.h +=================================================================== +--- alpine-2.11.orig/pith/conf.h ++++ alpine-2.11/pith/conf.h +@@ -161,6 +161,8 @@ + #define GLO_EDITOR vars[V_EDITOR].global_val.l + #define VAR_SPELLER vars[V_SPELLER].current_val.p + #define GLO_SPELLER vars[V_SPELLER].global_val.p ++#define VAR_SPECIAL_TEXT vars[V_SPECIAL_TEXT].current_val.l ++#define GLO_SPECIAL_TEXT vars[V_SPECIAL_TEXT].global_val.l + #define VAR_FILLCOL vars[V_FILLCOL].current_val.p + #define GLO_FILLCOL vars[V_FILLCOL].global_val.p + #define VAR_DEADLETS vars[V_DEADLETS].current_val.p +@@ -456,6 +458,8 @@ + #define GLO_SIGNATURE_FORE_COLOR vars[V_SIGNATURE_FORE_COLOR].global_val.p + #define VAR_SIGNATURE_BACK_COLOR vars[V_SIGNATURE_BACK_COLOR].current_val.p + #define GLO_SIGNATURE_BACK_COLOR vars[V_SIGNATURE_BACK_COLOR].global_val.p ++#define VAR_SPECIAL_TEXT_FORE_COLOR vars[V_SPECIAL_TEXT_FORE_COLOR].current_val.p ++#define VAR_SPECIAL_TEXT_BACK_COLOR vars[V_SPECIAL_TEXT_BACK_COLOR].current_val.p + #define VAR_PROMPT_FORE_COLOR vars[V_PROMPT_FORE_COLOR].current_val.p + #define VAR_PROMPT_BACK_COLOR vars[V_PROMPT_BACK_COLOR].current_val.p + #define VAR_VIEW_HDR_COLORS vars[V_VIEW_HDR_COLORS].current_val.l +Index: alpine-2.11/pith/conftype.h +=================================================================== +--- alpine-2.11.orig/pith/conftype.h ++++ alpine-2.11/pith/conftype.h +@@ -80,6 +80,7 @@ typedef enum { V_PERSONAL_NAME = 0 + , V_EDITOR + , V_SPELLER + , V_FILLCOL ++ , V_SPECIAL_TEXT + , V_REPLY_STRING + , V_REPLY_INTRO + , V_QUOTE_REPLACE_STRING +@@ -230,6 +231,8 @@ typedef enum { V_PERSONAL_NAME = 0 + , V_INCUNSEEN_BACK_COLOR + , V_SIGNATURE_FORE_COLOR + , V_SIGNATURE_BACK_COLOR ++ , V_SPECIAL_TEXT_FORE_COLOR ++ , V_SPECIAL_TEXT_BACK_COLOR + , V_PROMPT_FORE_COLOR + , V_PROMPT_BACK_COLOR + , V_HEADER_GENERAL_FORE_COLOR +Index: alpine-2.11/pith/mailview.c +=================================================================== +--- alpine-2.11.orig/pith/mailview.c ++++ alpine-2.11/pith/mailview.c +@@ -282,6 +282,14 @@ format_body(long int msgno, BODY *body, + if((flgs & FM_DISPLAY) + && !(flgs & FM_NOCOLOR) + && pico_usingcolor() ++ && ps_global->VAR_SPECIAL_TEXT_FORE_COLOR ++ && ps_global->VAR_SPECIAL_TEXT_BACK_COLOR){ ++ gf_link_filter(gf_line_test, gf_line_test_opt(color_this_text, NULL)); ++ } ++ ++ if((flgs & FM_DISPLAY) ++ && !(flgs & FM_NOCOLOR) ++ && pico_usingcolor() + && ps_global->VAR_SIGNATURE_FORE_COLOR + && ps_global->VAR_SIGNATURE_BACK_COLOR){ + gf_link_filter(gf_line_test, gf_line_test_opt(color_signature, &is_in_sig)); +@@ -2503,6 +2511,190 @@ hdr_color(char *fieldname, char *value, + return(color_pair); + } + ++void ++interval_free(IVAL_S **ival) ++{ ++ if (!(*ival)) ++ return; ++ ++ if ((*ival)->next) ++ interval_free(&((*ival)->next)); ++ ++ fs_give((void **)(ival)); ++} ++ ++IVAL_S * ++compute_interval (char *string, int endm) ++{ ++ IVAL_S *ival = NULL; ++ regmatch_t pmatch; ++ ++ if(ps_global->paterror == 0 && ++ regexec(&ps_global->colorpat, string + endm, 1, &pmatch, 0) == 0){ ++ ival = (IVAL_S *) fs_get(sizeof(IVAL_S)); ++ ival->start = endm + pmatch.rm_so; ++ ival->end = endm + pmatch.rm_eo; ++ ival->next = compute_interval(string, ival->end); ++ } ++ return ival; ++} ++ ++void ++regex_pattern(char **plist) ++{ ++ int i = 0, j = 0, len = 0; ++ char *pattern = NULL; ++ regex_t preg; ++ ++ if(ps_global->paterror == 0) ++ regfree(&ps_global->colorpat); ++ ++ if(plist && *plist && *plist){ ++ for (i = 0; plist[i] && plist[i][0]; i++) ++ len += strlen(plist[i]) + 1; ++ pattern = (char *) fs_get(len * sizeof(char)); ++ *pattern = '\0'; ++ for (j = 0; j < i; j++){ ++ strcat(pattern, plist[j]); ++ strcat(pattern, (j < i - 1) ? "|" : ""); ++ } ++ if ((ps_global->paterror = regcomp(&preg, pattern, REG_EXTENDED)) != 0) ++ regfree(&preg); ++ else ++ ps_global->colorpat = preg; ++ } ++ if(pattern) ++ fs_give((void **)&pattern); ++} ++ ++LT_INS_S ** ++insert_color_special_text(LT_INS_S **ins, char **p, IVAL_S *ival, int last_end, ++ COLOR_PAIR *col) ++{ ++ struct variable *vars = ps_global->vars; ++ ++ if (ival){ ++ *p += ival->start - last_end; ++ ins = gf_line_test_new_ins(ins, *p, color_embed(col->fg, col->bg), ++ (2 * RGBLEN) + 4); ++ *p += ival->end - ival->start; ++ ins = gf_line_test_new_ins(ins, *p, color_embed(VAR_NORM_FORE_COLOR, ++ VAR_NORM_BACK_COLOR), (2 * RGBLEN) + 4); ++ ins = insert_color_special_text(ins, p, ival->next, ival->end, col); ++ } ++ return ins; ++} ++ ++int ++length_color(char *p, int begin_color) ++{ ++ int len = 0, done = begin_color ? 0 : -1; ++ char *orig = p; ++ ++ while (*p && done <= 0){ ++ switch(*p++){ ++ case TAG_HANDLE : ++ p += *p + 1; ++ done++; ++ break; ++ ++ case TAG_FGCOLOR : ++ case TAG_BGCOLOR : ++ p += RGBLEN; ++ if (!begin_color) ++ done++; ++ break; ++ ++ default : ++ break; ++ } ++ } ++ len = p - orig; ++ return len; ++} ++ ++int ++any_color_in_string(char *p) ++{ ++ int rv = 0; + char *orig = p; -+ -+ while (*p && done <= 0){ -+ switch(*p++){ -+ case TAG_HANDLE : -+ p += *p + 1; -+ done++; -+ break; -+ -+ case TAG_FGCOLOR : -+ case TAG_BGCOLOR : -+ p += RGBLEN; -+ if (!begin_color) -+ done++; -+ break; -+ -+ default : -+ break; -+ } ++ while (*p && !rv) ++ if (*p++ == TAG_EMBED) ++ rv = p - orig; ++ return rv; ++} ++ ++void ++remove_spaces_ival(IVAL_S **ivalp, char *p) ++{ ++ IVAL_S *ival; ++ int i; ++ if (!ivalp || !*ivalp) ++ return; ++ ival = *ivalp; ++ for (i = 0; isspace((unsigned char) p[ival->start + i]); i++); ++ if (ival->start + i < ival->end) /* do not do this if match only spaces */ ++ ival->start += i; ++ else ++ return; ++ for (i = 0; isspace((unsigned char) p[ival->end - i - 1]); i++); ++ ival->end -= i; ++ if (ival->next) ++ remove_spaces_ival(&(ival->next), p); ++} ++ ++int ++color_this_text(long linenum, char *line, LT_INS_S **ins, void *local) ++{ ++ struct variable *vars = ps_global->vars; ++ COLOR_PAIR *col = NULL; ++ char *p; ++ int i = 0; ++ static char *pattern = NULL; ++ ++/* select_quote(linenum, line, ins, (void *) &i); ++ for (i = 0; tmp_20k_buf[i] != '\0'; i++); */ ++ p = line + i; ++ ++ if(VAR_SPECIAL_TEXT_FORE_COLOR && VAR_SPECIAL_TEXT_BACK_COLOR ++ && (col = new_color_pair(VAR_SPECIAL_TEXT_FORE_COLOR, ++ VAR_SPECIAL_TEXT_BACK_COLOR)) ++ && !pico_is_good_colorpair(col)) ++ free_color_pair(&col); ++ ++ if(ps_global->VAR_SPECIAL_TEXT && *ps_global->VAR_SPECIAL_TEXT ++ && **ps_global->VAR_SPECIAL_TEXT && col){ ++ IVAL_S *ival; ++ int done = 0, begin_color = 0; ++ ++ while (!done){ ++ if (i = any_color_in_string(p)){ ++ begin_color = (begin_color + 1) % 2; ++ if (begin_color){ ++ p[i - 1] = '\0'; ++ ival = compute_interval(p, 0); ++ remove_spaces_ival(&ival, p); ++ p[i - 1] = TAG_EMBED; ++ ins = insert_color_special_text(ins, &p, ival, 0, col); ++ } ++ for (;*p++ != TAG_EMBED; ); ++ p += length_color(p, begin_color); ++ } ++ else{ ++ ival = compute_interval(p, 0); ++ remove_spaces_ival(&ival, p); ++ ins = insert_color_special_text(ins, &p, ival, 0, col); ++ done++; ++ } ++ interval_free(&ival); ++ if (!*p) ++ done++; ++ } ++ free_color_pair(&col); + } -+ len = p - orig; -+ return len; -+ } -+ -+ int -+ any_color_in_string(char *p) -+ { -+ int rv = 0; -+ char *orig = p; -+ while (*p && !rv) -+ if (*p++ == TAG_EMBED) -+ rv = p - orig; -+ return rv; -+ } -+ -+ void -+ remove_spaces_ival(IVAL_S **ivalp, char *p) -+ { -+ IVAL_S *ival; -+ int i; -+ if (!ivalp || !*ivalp) -+ return; -+ ival = *ivalp; -+ for (i = 0; isspace((unsigned char) p[ival->start + i]); i++); -+ if (ival->start + i < ival->end) /* do not do this if match only spaces */ -+ ival->start += i; -+ else -+ return; -+ for (i = 0; isspace((unsigned char) p[ival->end - i - 1]); i++); -+ ival->end -= i; -+ if (ival->next) -+ remove_spaces_ival(&(ival->next), p); -+ } -+ -+ int -+ color_this_text(long linenum, char *line, LT_INS_S **ins, void *local) -+ { -+ struct variable *vars = ps_global->vars; -+ COLOR_PAIR *col = NULL; -+ char *p; -+ int i = 0; -+ static char *pattern = NULL; -+ -+ /* select_quote(linenum, line, ins, (void *) &i); -+ for (i = 0; tmp_20k_buf[i] != '\0'; i++); */ -+ p = line + i; -+ -+ if(VAR_SPECIAL_TEXT_FORE_COLOR && VAR_SPECIAL_TEXT_BACK_COLOR -+ && (col = new_color_pair(VAR_SPECIAL_TEXT_FORE_COLOR, -+ VAR_SPECIAL_TEXT_BACK_COLOR)) -+ && !pico_is_good_colorpair(col)) -+ free_color_pair(&col); -+ -+ if(ps_global->VAR_SPECIAL_TEXT && *ps_global->VAR_SPECIAL_TEXT -+ && **ps_global->VAR_SPECIAL_TEXT && col){ -+ IVAL_S *ival; -+ int done = 0, begin_color = 0; -+ -+ while (!done){ -+ if (i = any_color_in_string(p)){ -+ begin_color = (begin_color + 1) % 2; -+ if (begin_color){ -+ p[i - 1] = '\0'; -+ ival = compute_interval(p, 0); -+ remove_spaces_ival(&ival, p); -+ p[i - 1] = TAG_EMBED; -+ ins = insert_color_special_text(ins, &p, ival, 0, col); -+ } -+ for (;*p++ != TAG_EMBED; ); -+ p += length_color(p, begin_color); -+ } -+ else{ -+ ival = compute_interval(p, 0); -+ remove_spaces_ival(&ival, p); -+ ins = insert_color_special_text(ins, &p, ival, 0, col); -+ done++; -+ } -+ interval_free(&ival); -+ if (!*p) -+ done++; -+ } -+ free_color_pair(&col); -+ } -+ -+ return 0; -+ } - - /* - * The argument fieldname is something like "Subject:..." or "Subject". -diff -rc alpine-2.10/pith/mailview.h alpine-2.10.colortext/pith/mailview.h -*** alpine-2.10/pith/mailview.h 2013-01-11 11:26:44.000000000 -0700 ---- alpine-2.10.colortext/pith/mailview.h 2013-01-11 20:43:16.000000000 -0700 -*************** -*** 30,35 **** ---- 30,41 ---- - #include "../pith/color.h" - - -+ typedef struct IVAL { -+ int start; -+ int end; -+ struct IVAL *next; -+ } IVAL_S; -+ - /* format_message flags */ - #define FM_DISPLAY 0x0001 /* result is headed for display */ - #define FM_NEW_MESS 0x0002 /* a new message so zero out attachment descrip */ -*************** -*** 126,131 **** ---- 132,146 ---- - int url_hilite(long, char *, LT_INS_S **, void *); - int handle_start_color(char *, size_t, int *, int); - int handle_end_color(char *, size_t, int *); -+ IVAL_S *compute_interval(char *, int); -+ void remove_spaces_ival(IVAL_S **, char *); -+ void interval_free(IVAL_S **); -+ void regex_pattern(char **); -+ LT_INS_S **insert_color_special_text(LT_INS_S **, char **, IVAL_S *, -+ int, COLOR_PAIR *); -+ int any_color_in_string(char *); -+ int length_color(char *, int); -+ int color_this_text(long, char *, LT_INS_S **, void *); - - /* - * BUG: BELOW IS UNIX/PC ONLY since config'd browser means nothing to webpine -diff -rc alpine-2.10/pith/pine.hlp alpine-2.10.colortext/pith/pine.hlp -*** alpine-2.10/pith/pine.hlp 2013-01-11 20:33:27.000000000 -0700 ---- alpine-2.10.colortext/pith/pine.hlp 2013-01-11 20:43:16.000000000 -0700 -*************** -*** 3517,3522 **** ---- 3517,3523 ---- -
  • OPTION: -
  • OPTION: -
  • OPTION: -+
  • OPTION: -
  • OPTION: -
  • OPTION: Print-Font-Char-Set -
  • OPTION: Print-Font-Name -*************** -*** 3545,3550 **** ---- 3546,3552 ---- -
  • OPTION: -
  • OPTION: -
  • OPTION: Signature Color -+
  • OPTION: Special Text Color -
  • OPTION: -
  • OPTION: -
  • OPTION: -*************** -*** 22792,22797 **** ---- 22794,22836 ---- - <End of help on this topic> - - -+ ====== h_config_special_text_to_color ===== -+ -+ -+ OPTION: <!--#echo var="VAR_special-text-color"--> -+ -+ -+

    OPTION:

    -+ -+ Use this option to enter patterns (text or regular expressions) that -+ Alpine will highlight in the body of the text that is not part of a handle -+ (an internal or external link that Alpine paints in a different color). -+ -+

    -+ Enter each pattern in a different line. Pine will internally merge these -+ patterns (by adding a "|" character), or you can add them all in one line -+ by separating them by a "|" character. There is only a set of regular expressions that are matched. -+ -+

    -+ Pine will use the colors defined in the -+ Special Text Color variable. -+ to paint any match. -+ -+

    -+ If the Special Text Color is not set, setting this variable will not -+ cause that special text to be indicated in any special way. It will look -+ like any normal text. You must set those colors in order to make Pine -+ paint the screen differently when it finds the patterns specified in this -+ variable. -+ -+

    -+

    -+ <End of help on this topic> -+ -+ - ====== h_config_display_filters ===== - - -*************** -*** 31396,31401 **** ---- 31435,31464 ---- -

    - Descriptions of the available commands -

    -+ Look here -+ to see the available Editing and Navigation commands. -+

    -+ <End of help on this topic> -+ -+ -+ ====== h_config_special_text_color ===== -+ -+ -+ OPTION: Special Text Color -+ -+ -+

    OPTION: Special Text Color

    -+ -+ Sets the color Pine uses for coloring any text in the body of the message -+ that is not part of a handle (and internal or external link that Pine -+ paints in a different color). By default, this variable is not defined, -+ which means that text that matches the pattern is not painted in any -+ particular way. This variable must be set in a special form if you -+ want text to be painted. -+ -+

    -+ Descriptions of the available commands -+

    - Look here - to see the available Editing and Navigation commands. -

    -diff -rc alpine-2.10/pith/state.c alpine-2.10.colortext/pith/state.c -*** alpine-2.10/pith/state.c 2013-01-11 11:26:44.000000000 -0700 ---- alpine-2.10.colortext/pith/state.c 2013-01-11 20:43:16.000000000 -0700 -*************** -*** 131,136 **** ---- 131,139 ---- - if((*pps)->folders_dir != NULL) - fs_give((void **)&(*pps)->folders_dir); - -+ if((*pps)->paterror == 0) -+ regfree(&(*pps)->colorpat); -+ - if((*pps)->ui.homedir) - fs_give((void **)&(*pps)->ui.homedir); - -diff -rc alpine-2.10/pith/state.h alpine-2.10.colortext/pith/state.h -*** alpine-2.10/pith/state.h 2013-01-11 19:45:41.000000000 -0700 ---- alpine-2.10.colortext/pith/state.h 2013-01-11 20:43:16.000000000 -0700 -*************** -*** 320,325 **** ---- 320,327 ---- - char *display_charmap; /* needs to be freed */ - char *keyboard_charmap; /* needs to be freed */ - void *input_cs; -+ regex_t colorpat; -+ int paterror; - - char *posting_charmap; /* needs to be freed */ - -diff -rc alpine-2.10/pith/text.c alpine-2.10.colortext/pith/text.c -*** alpine-2.10/pith/text.c 2013-01-11 11:26:44.000000000 -0700 ---- alpine-2.10.colortext/pith/text.c 2013-01-11 20:43:16.000000000 -0700 -*************** -*** 171,176 **** ---- 171,185 ---- - gf_url_hilite_opt(&uh,handlesp,0)); - } - -+ if((flags & FM_DISPLAY) -+ && !(flags & FM_NOCOLOR) -+ && pico_usingcolor() -+ && VAR_SPECIAL_TEXT_FORE_COLOR -+ && VAR_SPECIAL_TEXT_BACK_COLOR){ -+ filters[filtcnt].filter = gf_line_test; -+ filters[filtcnt++].data = gf_line_test_opt(color_this_text, NULL); -+ } -+ - /* - * First, paint the signature. - * Disclaimers noted below for coloring quotes apply here as well. ++ ++ return 0; ++} + + /* + * The argument fieldname is something like "Subject:..." or "Subject". +Index: alpine-2.11/pith/mailview.h +=================================================================== +--- alpine-2.11.orig/pith/mailview.h ++++ alpine-2.11/pith/mailview.h +@@ -30,6 +30,12 @@ + #include "../pith/color.h" + + ++typedef struct IVAL { ++ int start; ++ int end; ++ struct IVAL *next; ++} IVAL_S; ++ + /* format_message flags */ + #define FM_DISPLAY 0x0001 /* result is headed for display */ + #define FM_NEW_MESS 0x0002 /* a new message so zero out attachment descrip */ +@@ -126,6 +132,15 @@ char *format_body(long int, BODY *, HAND + int url_hilite(long, char *, LT_INS_S **, void *); + int handle_start_color(char *, size_t, int *, int); + int handle_end_color(char *, size_t, int *); ++IVAL_S *compute_interval(char *, int); ++void remove_spaces_ival(IVAL_S **, char *); ++void interval_free(IVAL_S **); ++void regex_pattern(char **); ++LT_INS_S **insert_color_special_text(LT_INS_S **, char **, IVAL_S *, ++ int, COLOR_PAIR *); ++int any_color_in_string(char *); ++int length_color(char *, int); ++int color_this_text(long, char *, LT_INS_S **, void *); + + /* + * BUG: BELOW IS UNIX/PC ONLY since config'd browser means nothing to webpine +Index: alpine-2.11/pith/pine.hlp +=================================================================== +--- alpine-2.11.orig/pith/pine.hlp ++++ alpine-2.11/pith/pine.hlp +@@ -3563,6 +3563,7 @@ There are also additional details on +

  • OPTION: +
  • OPTION: +
  • OPTION: ++
  • OPTION: +
  • OPTION: +
  • OPTION: Print-Font-Char-Set +
  • OPTION: Print-Font-Name +@@ -3591,6 +3592,7 @@ There are also additional details on +
  • OPTION: +
  • OPTION: +
  • OPTION: Signature Color ++
  • OPTION: Special Text Color +
  • OPTION: +
  • OPTION: +
  • OPTION: +@@ -22838,6 +22840,43 @@ That won't work because spell works in a + <End of help on this topic> + + ++====== h_config_special_text_to_color ===== ++ ++ ++OPTION: <!--#echo var="VAR_special-text-color"--> ++ ++ ++

    OPTION:

    ++ ++Use this option to enter patterns (text or regular expressions) that ++Alpine will highlight in the body of the text that is not part of a handle ++(an internal or external link that Alpine paints in a different color). ++ ++

    ++Enter each pattern in a different line. Pine will internally merge these ++patterns (by adding a "|" character), or you can add them all in one line ++by separating them by a "|" character. There is only a set of regular expressions that are matched. ++ ++

    ++Pine will use the colors defined in the ++Special Text Color variable. ++to paint any match. ++ ++

    ++If the Special Text Color is not set, setting this variable will not ++cause that special text to be indicated in any special way. It will look ++like any normal text. You must set those colors in order to make Pine ++paint the screen differently when it finds the patterns specified in this ++variable. ++ ++

    ++

    ++<End of help on this topic> ++ ++ + ====== h_config_display_filters ===== + + +@@ -31455,6 +31494,30 @@ the Quote3 Color is black characters on +

    + Descriptions of the available commands +

    ++Look here ++to see the available Editing and Navigation commands. ++

    ++<End of help on this topic> ++ ++ ++====== h_config_special_text_color ===== ++ ++ ++OPTION: Special Text Color ++ ++ ++

    OPTION: Special Text Color

    ++ ++Sets the color Pine uses for coloring any text in the body of the message ++that is not part of a handle (and internal or external link that Pine ++paints in a different color). By default, this variable is not defined, ++which means that text that matches the pattern is not painted in any ++particular way. This variable must be set in a special form if you ++want text to be painted. ++ ++

    ++Descriptions of the available commands ++

    + Look here + to see the available Editing and Navigation commands. +

    +Index: alpine-2.11/pith/state.c +=================================================================== +--- alpine-2.11.orig/pith/state.c ++++ alpine-2.11/pith/state.c +@@ -131,6 +131,9 @@ free_pine_struct(struct pine **pps) + if((*pps)->folders_dir != NULL) + fs_give((void **)&(*pps)->folders_dir); + ++ if((*pps)->paterror == 0) ++ regfree(&(*pps)->colorpat); ++ + if((*pps)->ui.homedir) + fs_give((void **)&(*pps)->ui.homedir); + +Index: alpine-2.11/pith/state.h +=================================================================== +--- alpine-2.11.orig/pith/state.h ++++ alpine-2.11/pith/state.h +@@ -326,6 +326,8 @@ struct pine { + char *display_charmap; /* needs to be freed */ + char *keyboard_charmap; /* needs to be freed */ + void *input_cs; ++ regex_t colorpat; ++ int paterror; + + char *posting_charmap; /* needs to be freed */ + +Index: alpine-2.11/pith/text.c +=================================================================== +--- alpine-2.11.orig/pith/text.c ++++ alpine-2.11/pith/text.c +@@ -171,6 +171,15 @@ decode_text(ATTACH_S *att, + gf_url_hilite_opt(&uh,handlesp,0)); + } + ++ if((flags & FM_DISPLAY) ++ && !(flags & FM_NOCOLOR) ++ && pico_usingcolor() ++ && VAR_SPECIAL_TEXT_FORE_COLOR ++ && VAR_SPECIAL_TEXT_BACK_COLOR){ ++ filters[filtcnt].filter = gf_line_test; ++ filters[filtcnt++].data = gf_line_test_opt(color_this_text, NULL); ++ } ++ + /* + * First, paint the signature. + * Disclaimers noted below for coloring quotes apply here as well. diff --git a/chappa-fancy.patch b/chappa-fancy.patch index ba2e7c0..ec1db52 100644 --- a/chappa-fancy.patch +++ b/chappa-fancy.patch @@ -1,4050 +1,3198 @@ -diff -rc alpine-2.10/alpine/arg.c alpine-2.10.fancy/alpine/arg.c -*** alpine-2.10/alpine/arg.c 2013-01-11 11:19:33.000000000 -0700 ---- alpine-2.10.fancy/alpine/arg.c 2013-01-11 20:43:05.000000000 -0700 -*************** -*** 60,65 **** ---- 60,66 ---- - static char args_err_non_abs_passfile[] = N_("argument to \"-passfile\" should be fully-qualified"); - #endif - static char args_err_missing_sort[] = N_("missing argument for option \"-sort\""); -+ static char args_err_missing_thread_sort[] = N_("missing argument for option \"-threadsort\""); - static char args_err_missing_flag_arg[] = N_("missing argument for flag \"%c\""); - static char args_err_missing_flag_num[] = N_("Non numeric argument for flag \"%c\""); - static char args_err_missing_debug_num[] = N_("Non numeric argument for \"%s\""); -*************** -*** 103,108 **** ---- 104,110 ---- - N_(" -z \t\tSuspend - allow use of ^Z suspension"), - N_(" -r \t\tRestricted - can only send mail to oneself"), - N_(" -sort \tSort - Specify sort order of folder:"), -+ N_(" -threadsort \tSort - Specify sort order of thread index screen:"), - N_("\t\t\tarrival, subject, threaded, orderedsubject, date,"), - N_("\t\t\tfrom, size, score, to, cc, /reverse"), - N_(" -i\t\tIndex - Go directly to index, bypassing main menu"), -*************** -*** 192,197 **** ---- 194,200 ---- - char *cmd_list = NULL; - char *debug_str = NULL; - char *sort = NULL; -+ char *threadsort = NULL; - char *pinerc_file = NULL; - char *lc = NULL; - int do_help = 0; -*************** -*** 363,368 **** ---- 366,382 ---- - - goto Loop; - } -+ else if(strcmp(*av, "threadsort") == 0){ -+ if(--ac){ -+ threadsort = *++av; -+ COM_THREAD_SORT_KEY = cpystr(threadsort); -+ } -+ else{ -+ display_args_err(_(args_err_missing_thread_sort), NULL, 1); -+ ++usage; -+ } -+ goto Loop; -+ } - else if(strcmp(*av, "url") == 0){ - if(args->action == aaFolder && !args->data.folder){ - args->action = aaURL; -diff -rc alpine-2.10/alpine/confscroll.c alpine-2.10.fancy/alpine/confscroll.c -*** alpine-2.10/alpine/confscroll.c 2013-01-11 15:21:34.000000000 -0700 ---- alpine-2.10.fancy/alpine/confscroll.c 2013-01-11 20:43:05.000000000 -0700 -*************** -*** 139,145 **** - char *radio_pretty_value(struct pine *, CONF_S *); - char *sigfile_pretty_value(struct pine *, CONF_S *); - char *color_pretty_value(struct pine *, CONF_S *); -! char *sort_pretty_value(struct pine *, CONF_S *); - int longest_feature_name(void); - COLOR_PAIR *sample_color(struct pine *, struct variable *); - COLOR_PAIR *sampleexc_color(struct pine *, struct variable *); ---- 139,145 ---- - char *radio_pretty_value(struct pine *, CONF_S *); - char *sigfile_pretty_value(struct pine *, CONF_S *); - char *color_pretty_value(struct pine *, CONF_S *); -! char *sort_pretty_value(struct pine *, CONF_S *, int); - int longest_feature_name(void); - COLOR_PAIR *sample_color(struct pine *, struct variable *); - COLOR_PAIR *sampleexc_color(struct pine *, struct variable *); -*************** -*** 287,293 **** - CONF_S *ctmp; - - if(!(cl && *cl && -! ((*cl)->var == &ps->vars[V_SORT_KEY] || - standard_radio_var(ps, (*cl)->var) || - (*cl)->var == startup_ptr))) - return; ---- 287,294 ---- - CONF_S *ctmp; - - if(!(cl && *cl && -! (((*cl)->var == &ps->vars[V_SORT_KEY]) || -! ((*cl)->var == &ps->vars[V_THREAD_SORT_KEY]) || - standard_radio_var(ps, (*cl)->var) || - (*cl)->var == startup_ptr))) - return; -*************** -*** 2923,2929 **** - } - - set_current_val((*cl)->var, TRUE, TRUE); -! if(decode_sort(ps->VAR_SORT_KEY, &def_sort, &def_sort_rev) != -1){ - ps->def_sort = def_sort; - ps->def_sort_rev = def_sort_rev; - } ---- 2924,2930 ---- - } - - set_current_val((*cl)->var, TRUE, TRUE); -! if(decode_sort(ps->VAR_SORT_KEY, &def_sort, &def_sort_rev,0) != -1){ - ps->def_sort = def_sort; - ps->def_sort_rev = def_sort_rev; - } -*************** -*** 2932,2937 **** ---- 2933,2969 ---- - ps->mangled_body = 1; /* BUG: redraw it all for now? */ - rv = 1; - } -+ else if((*cl)->var == &ps->vars[V_THREAD_SORT_KEY]){ -+ SortOrder thread_def_sort; -+ int thread_def_sort_rev; -+ -+ thread_def_sort_rev = (*cl)->varmem >= (short) EndofList; -+ thread_def_sort = (SortOrder) ((*cl)->varmem - (thread_def_sort_rev -+ * EndofList)); -+ sprintf(tmp_20k_buf, "%s%s", sort_name(thread_def_sort), -+ (thread_def_sort_rev) ? "/Reverse" : ""); -+ -+ if((*cl)->var->cmdline_val.p) -+ fs_give((void **)&(*cl)->var->cmdline_val.p); -+ -+ if(apval){ -+ if(*apval) -+ fs_give((void **)apval); -+ -+ *apval = cpystr(tmp_20k_buf); -+ } -+ -+ set_current_val((*cl)->var, TRUE, TRUE); -+ if(decode_sort(ps->VAR_THREAD_SORT_KEY, &thread_def_sort, -+ &thread_def_sort_rev, 1) != -1){ -+ ps->thread_def_sort = thread_def_sort; -+ ps->thread_def_sort_rev = thread_def_sort_rev; -+ } -+ -+ set_radio_pretty_vals(ps, cl); -+ ps->mangled_body = 1; /* BUG: redraw it all for now? */ -+ rv = 1; -+ } - else - q_status_message(SM_ORDER | SM_DING, 3, 6, - "Programmer botch! Unknown radiobutton type."); -*************** -*** 3791,3797 **** - else if(standard_radio_var(ps, v) || v == startup_ptr) - return(radio_pretty_value(ps, cl)); - else if(v == &ps->vars[V_SORT_KEY]) -! return(sort_pretty_value(ps, cl)); - else if(v == &ps->vars[V_SIGNATURE_FILE]) - return(sigfile_pretty_value(ps, cl)); - else if(v == &ps->vars[V_USE_ONLY_DOMAIN_NAME]) ---- 3823,3831 ---- - else if(standard_radio_var(ps, v) || v == startup_ptr) - return(radio_pretty_value(ps, cl)); - else if(v == &ps->vars[V_SORT_KEY]) -! return(sort_pretty_value(ps, cl, 0)); -! else if(v == &ps->vars[V_THREAD_SORT_KEY]) -! return(sort_pretty_value(ps, cl, 1)); - else if(v == &ps->vars[V_SIGNATURE_FILE]) - return(sigfile_pretty_value(ps, cl)); - else if(v == &ps->vars[V_USE_ONLY_DOMAIN_NAME]) -*************** -*** 4322,4335 **** - - - char * -! sort_pretty_value(struct pine *ps, CONF_S *cl) - { -! return(generalized_sort_pretty_value(ps, cl, 1)); - } - - - char * -! generalized_sort_pretty_value(struct pine *ps, CONF_S *cl, int default_ok) - { - char tmp[6*MAXPATH]; - char *pvalnorm, *pvalexc, *pval; ---- 4356,4369 ---- - - - char * -! sort_pretty_value(struct pine *ps, CONF_S *cl, int thread) - { -! return(generalized_sort_pretty_value(ps, cl, 1, thread)); - } - - - char * -! generalized_sort_pretty_value(struct pine *ps, CONF_S *cl, int default_ok, int thread) - { - char tmp[6*MAXPATH]; - char *pvalnorm, *pvalexc, *pval; -*************** -*** 4379,4385 **** - } - else if(fixed){ - pval = v->fixed_val.p; -! decode_sort(pval, &var_sort, &var_sort_rev); - is_the_one = (var_sort_rev == line_sort_rev && var_sort == line_sort); - - utf8_snprintf(tmp, sizeof(tmp), "(%c) %s%-*w%*s%s", ---- 4413,4419 ---- - } - else if(fixed){ - pval = v->fixed_val.p; -! decode_sort(pval, &var_sort, &var_sort_rev, thread); - is_the_one = (var_sort_rev == line_sort_rev && var_sort == line_sort); - - utf8_snprintf(tmp, sizeof(tmp), "(%c) %s%-*w%*s%s", -*************** -*** 4390,4398 **** - is_the_one ? " (value is fixed)" : ""); - } - else if(is_set_for_this_level){ -! decode_sort(pval, &var_sort, &var_sort_rev); - is_the_one = (var_sort_rev == line_sort_rev && var_sort == line_sort); -! decode_sort(pvalexc, &exc_sort, &exc_sort_rev); - the_exc_one = (editing_normal_which_isnt_except && pvalexc && - exc_sort_rev == line_sort_rev && exc_sort == line_sort); - utf8_snprintf(tmp, sizeof(tmp), "(%c) %s%-*w%*s%s", ---- 4424,4432 ---- - is_the_one ? " (value is fixed)" : ""); - } - else if(is_set_for_this_level){ -! decode_sort(pval, &var_sort, &var_sort_rev, thread); - is_the_one = (var_sort_rev == line_sort_rev && var_sort == line_sort); -! decode_sort(pvalexc, &exc_sort, &exc_sort_rev, thread); - the_exc_one = (editing_normal_which_isnt_except && pvalexc && - exc_sort_rev == line_sort_rev && exc_sort == line_sort); - utf8_snprintf(tmp, sizeof(tmp), "(%c) %s%-*w%*s%s", -*************** -*** 4410,4416 **** - } - else{ - if(pvalexc){ -! decode_sort(pvalexc, &exc_sort, &exc_sort_rev); - is_the_one = (exc_sort_rev == line_sort_rev && - exc_sort == line_sort); - utf8_snprintf(tmp, sizeof(tmp), "( ) %s%-*w%*s%s", ---- 4444,4450 ---- - } - else{ - if(pvalexc){ -! decode_sort(pvalexc, &exc_sort, &exc_sort_rev, thread); - is_the_one = (exc_sort_rev == line_sort_rev && - exc_sort == line_sort); - utf8_snprintf(tmp, sizeof(tmp), "( ) %s%-*w%*s%s", -*************** -*** 4421,4427 **** - } - else{ - pval = v->current_val.p; -! decode_sort(pval, &var_sort, &var_sort_rev); - is_the_one = ((pval || default_ok) && - var_sort_rev == line_sort_rev && - var_sort == line_sort); ---- 4455,4461 ---- - } - else{ - pval = v->current_val.p; -! decode_sort(pval, &var_sort, &var_sort_rev, thread); - is_the_one = ((pval || default_ok) && - var_sort_rev == line_sort_rev && - var_sort == line_sort); -*************** -*** 5542,5550 **** - else if(revert && var == &ps->vars[V_SORT_KEY]){ - int def_sort_rev; - -! decode_sort(VAR_SORT_KEY, &ps->def_sort, &def_sort_rev); - ps->def_sort_rev = def_sort_rev; - } - else if(var == &ps->vars[V_THREAD_MORE_CHAR] || - var == &ps->vars[V_THREAD_EXP_CHAR] || - var == &ps->vars[V_THREAD_LASTREPLY_CHAR]){ ---- 5576,5590 ---- - else if(revert && var == &ps->vars[V_SORT_KEY]){ - int def_sort_rev; - -! decode_sort(VAR_SORT_KEY, &ps->def_sort, &def_sort_rev, 0); - ps->def_sort_rev = def_sort_rev; - } -+ else if(revert && var == &ps->vars[V_THREAD_SORT_KEY]){ -+ int thread_def_sort_rev; -+ -+ decode_sort(VAR_THREAD_SORT_KEY, &ps->thread_def_sort, &thread_def_sort_rev, 1); -+ ps->thread_def_sort_rev = thread_def_sort_rev; -+ } - else if(var == &ps->vars[V_THREAD_MORE_CHAR] || - var == &ps->vars[V_THREAD_EXP_CHAR] || - var == &ps->vars[V_THREAD_LASTREPLY_CHAR]){ -diff -rc alpine-2.10/alpine/confscroll.h alpine-2.10.fancy/alpine/confscroll.h -*** alpine-2.10/alpine/confscroll.h 2013-01-11 11:19:33.000000000 -0700 ---- alpine-2.10.fancy/alpine/confscroll.h 2013-01-11 20:43:05.000000000 -0700 -*************** -*** 95,101 **** - int radiobutton_tool(struct pine *, int, CONF_S **, unsigned); - int yesno_tool(struct pine *, int, CONF_S **, unsigned); - int text_toolit(struct pine *, int, CONF_S **, unsigned, int); -! char *generalized_sort_pretty_value(struct pine *, CONF_S *, int); - int exclude_config_var(struct pine *, struct variable *, int); - int config_exit_cmd(unsigned); - int simple_exit_cmd(unsigned); ---- 95,101 ---- - int radiobutton_tool(struct pine *, int, CONF_S **, unsigned); - int yesno_tool(struct pine *, int, CONF_S **, unsigned); - int text_toolit(struct pine *, int, CONF_S **, unsigned, int); -! char *generalized_sort_pretty_value(struct pine *, CONF_S *, int, int); - int exclude_config_var(struct pine *, struct variable *, int); - int config_exit_cmd(unsigned); - int simple_exit_cmd(unsigned); -diff -rc alpine-2.10/alpine/keymenu.c alpine-2.10.fancy/alpine/keymenu.c -*** alpine-2.10/alpine/keymenu.c 2013-01-11 17:42:47.000000000 -0700 ---- alpine-2.10.fancy/alpine/keymenu.c 2013-01-11 20:43:05.000000000 -0700 -*************** -*** 665,674 **** - RCOMPOSE_MENU, - HOMEKEY_MENU, - ENDKEY_MENU, -! NULL_MENU, - /* TRANSLATORS: toggles a collapsed view or an expanded view - of a message thread on and off */ - {"/",N_("Collapse/Expand"),{MC_COLLAPSE,1,{'/'}},KS_NONE}, - {"@", N_("Quota"), {MC_QUOTA,1,{'@'}}, KS_NONE}, - NULL_MENU}; - INST_KEY_MENU(index_keymenu, index_keys); ---- 665,689 ---- - RCOMPOSE_MENU, - HOMEKEY_MENU, - ENDKEY_MENU, -! {"K","Sort Thread",{MC_SORTHREAD,1,{'k'}},KS_NONE}, - /* TRANSLATORS: toggles a collapsed view or an expanded view - of a message thread on and off */ - {"/",N_("Collapse/Expand"),{MC_COLLAPSE,1,{'/'}},KS_NONE}, -+ /* TRANSLATORS: Collapse all threads */ -+ {"{",N_("Collapse All"),{MC_KOLAPSE,1,{'{'}},KS_NONE}, -+ /* TRANSLATORS: Expand all threads */ -+ {"}",N_("Expand All"), {MC_EXPTHREAD,1,{'}'}},KS_NONE}, -+ -+ HELP_MENU, -+ OTHER_MENU, -+ {")","Next Threa",{MC_NEXTHREAD,1,{')'}},KS_NONE}, -+ {"(","Prev Threa",{MC_PRETHREAD,1,{'('}},KS_NONE}, -+ {"^R","Remove Thr",{MC_DELTHREAD,1,{ctrl('r')}},KS_NONE}, -+ {"^U","Undel Thre",{MC_UNDTHREAD,1,{ctrl('u')}},KS_NONE}, -+ {"^T","Select Thr",{MC_SELTHREAD,1,{ctrl('t')}},KS_NONE}, -+ NULL_MENU, -+ {"[","Close Thre",{MC_CTHREAD,1,{'['}},KS_NONE}, -+ {"]","Open Threa",{MC_OTHREAD,1,{']'}},KS_NONE}, - {"@", N_("Quota"), {MC_QUOTA,1,{'@'}}, KS_NONE}, - NULL_MENU}; - INST_KEY_MENU(index_keymenu, index_keys); -*************** -*** 743,751 **** - RCOMPOSE_MENU, - HOMEKEY_MENU, - ENDKEY_MENU, -! NULL_MENU, - {"/",N_("Collapse/Expand"),{MC_COLLAPSE,1,{'/'}},KS_NONE}, - {"@", N_("Quota"), {MC_QUOTA,1,{'@'}}, KS_NONE}, - NULL_MENU}; - INST_KEY_MENU(thread_keymenu, thread_keys); - ---- 758,779 ---- - RCOMPOSE_MENU, - HOMEKEY_MENU, - ENDKEY_MENU, -! {"]",N_("Open Thread"),{MC_OTHREAD,1,{']'}},KS_NONE}, - {"/",N_("Collapse/Expand"),{MC_COLLAPSE,1,{'/'}},KS_NONE}, -+ {")",N_("Next Thread"),{MC_NEXTHREAD,1,{')'}},KS_NONE}, -+ {"(",N_("Prev Thread"),{MC_PRETHREAD,1,{'('}},KS_NONE}, -+ -+ HELP_MENU, -+ OTHER_MENU, - {"@", N_("Quota"), {MC_QUOTA,1,{'@'}}, KS_NONE}, -+ NULL_MENU, -+ {"^R",N_("Remove Thread"),{MC_DELTHREAD,1,{ctrl('r')}},KS_NONE}, -+ {"^U",N_("Undelete Thread"),{MC_UNDTHREAD,1,{ctrl('u')}},KS_NONE}, -+ {"^T",N_("SelecT Thread"),{MC_SELTHREAD,1,{ctrl('t')}},KS_NONE}, -+ NULL_MENU, -+ NULL_MENU, -+ NULL_MENU, -+ {"K","Sort Thread",{MC_SORTHREAD,1,{'k'}},KS_NONE}, - NULL_MENU}; - INST_KEY_MENU(thread_keymenu, thread_keys); - -*************** -*** 895,901 **** - NULL_MENU, - NULL_MENU, - NULL_MENU, -! NULL_MENU}; - INST_KEY_MENU(view_keymenu, view_keys); - - ---- 923,942 ---- - NULL_MENU, - NULL_MENU, - NULL_MENU, -! NULL_MENU, -! -! HELP_MENU, -! OTHER_MENU, -! NULL_MENU, -! NULL_MENU, -! NULL_MENU, -! NULL_MENU, -! NULL_MENU, -! {"(",N_("Prev Thread"),{MC_PRETHREAD,1,{'('}},KS_NONE}, -! {")",N_("Next Thread"),{MC_NEXTHREAD,1,{')'}},KS_NONE}, -! {"^R",N_("Remove Thread"),{MC_DELTHREAD,1,{ctrl('r')}},KS_NONE}, -! {"^U",N_("Undelete Thread"),{MC_UNDTHREAD,1,{ctrl('u')}},KS_NONE}, -! {"^T",N_("selecT Thread"),{MC_SELTHREAD,1,{ctrl('t')}},KS_NONE}}; - INST_KEY_MENU(view_keymenu, view_keys); - - -diff -rc alpine-2.10/alpine/keymenu.h alpine-2.10.fancy/alpine/keymenu.h -*** alpine-2.10/alpine/keymenu.h 2013-01-11 15:21:34.000000000 -0700 ---- alpine-2.10.fancy/alpine/keymenu.h 2013-01-11 20:43:05.000000000 -0700 -*************** -*** 214,219 **** ---- 214,232 ---- - #define MC_CHK_RECENT 801 - #define MC_DECRYPT 802 - #define MC_QUOTA 803 -+ #define MC_OTHREAD 804 -+ #define MC_DELTHREAD 805 -+ #define MC_UNDTHREAD 806 -+ #define MC_SELTHREAD 807 -+ #define MC_SSUTHREAD 808 -+ #define MC_DSUTHREAD 809 -+ #define MC_USUTHREAD 810 -+ #define MC_SORTHREAD 811 -+ #define MC_NEXTHREAD 812 -+ #define MC_KOLAPSE 813 -+ #define MC_EXPTHREAD 814 -+ #define MC_PRETHREAD 815 -+ #define MC_CTHREAD 816 - - /* - * Some standard Key/Command Bindings -diff -rc alpine-2.10/alpine/mailcmd.c alpine-2.10.fancy/alpine/mailcmd.c -*** alpine-2.10/alpine/mailcmd.c 2013-01-11 15:21:34.000000000 -0700 ---- alpine-2.10.fancy/alpine/mailcmd.c 2013-01-11 20:43:05.000000000 -0700 -*************** -*** 113,119 **** - char *choose_a_rule(int); - int select_by_keyword(MAILSTREAM *, SEARCHSET **); - char *choose_a_keyword(void); -! int select_sort(struct pine *, int, SortOrder *, int *); - int print_index(struct pine *, MSGNO_S *, int); - - ---- 113,119 ---- - char *choose_a_rule(int); - int select_by_keyword(MAILSTREAM *, SEARCHSET **); - char *choose_a_keyword(void); -! int select_sort(struct pine *, int, SortOrder *, int *, int); - int print_index(struct pine *, MSGNO_S *, int); - - -*************** -*** 1335,1341 **** - if(any_messages(msgmap, NULL, NULL)){ - if(any_lflagged(msgmap, MN_SLCT) > 0L){ - if(apply_command(state, stream, msgmap, 0, -! AC_NONE, question_line)){ - if(F_ON(F_AUTO_UNSELECT, state)){ - agg_select_all(stream, msgmap, NULL, 0); - unzoom_index(state, stream, msgmap); ---- 1335,1341 ---- - if(any_messages(msgmap, NULL, NULL)){ - if(any_lflagged(msgmap, MN_SLCT) > 0L){ - if(apply_command(state, stream, msgmap, 0, -! AC_NONE, question_line, 1)){ - if(F_ON(F_AUTO_UNSELECT, state)){ - agg_select_all(stream, msgmap, NULL, 0); - unzoom_index(state, stream, msgmap); -*************** -*** 1353,1375 **** - - /*-------- Sort command -------*/ - case MC_SORT : - { - int were_threading = THREADING(); - SortOrder sort = mn_get_sort(msgmap); - int rev = mn_get_revsort(msgmap); - - dprint((1,"MAIL_CMD: sort\n")); -! if(select_sort(state, question_line, &sort, &rev)){ - /* $ command reinitializes threading collapsed/expanded info */ - if(SORT_IS_THREADED(msgmap) && !SEP_THRDINDX()) - erase_threading_info(stream, msgmap); - - if(ps_global && ps_global->ttyo){ - blank_keymenu(ps_global->ttyo->screen_rows - 2, 0); - ps_global->mangled_footer = 1; - } - -! sort_folder(stream, msgmap, sort, rev, SRT_VRB|SRT_MAN); - } - - state->mangled_footer = 1; ---- 1353,1387 ---- - - /*-------- Sort command -------*/ - case MC_SORT : -+ case MC_SORTHREAD: - { - int were_threading = THREADING(); - SortOrder sort = mn_get_sort(msgmap); - int rev = mn_get_revsort(msgmap); -+ int thread = (command == MC_SORT) ? 0 : 1; - - dprint((1,"MAIL_CMD: sort\n")); -! if(sort == SortThread) -! sort = ps_global->thread_cur_sort; -! if(select_sort(state, question_line, &sort, &rev, thread)){ - /* $ command reinitializes threading collapsed/expanded info */ - if(SORT_IS_THREADED(msgmap) && !SEP_THRDINDX()) - erase_threading_info(stream, msgmap); - -+ if(command == MC_SORTHREAD){ -+ ps_global->thread_cur_sort = sort; -+ sort = SortThread; -+ } -+ else if(sort == SortThread) /* command = MC_SORT */ -+ ps_global->thread_cur_sort = F_ON(F_THREAD_SORTS_BY_ARRIVAL, ps_global) -+ ? SortArrival : ps_global->thread_def_sort; -+ - if(ps_global && ps_global->ttyo){ - blank_keymenu(ps_global->ttyo->screen_rows - 2, 0); - ps_global->mangled_footer = 1; - } - -! sort_folder(stream, msgmap, sort, rev, SRT_VRB|SRT_MAN, 1); - } - - state->mangled_footer = 1; -*************** -*** 3174,3179 **** ---- 3186,3195 ---- - if(SORT_IS_THREADED(msgmap)) - refresh_sort(stream, msgmap, SRT_NON); - -+ if (msgmap->nmsgs -+ && F_ON(F_ENHANCED_THREAD, state) && COLL_THRDS()) -+ kolapse_thread(state, stream, msgmap, '[', 0); -+ - state->mangled_body = 1; - state->mangled_header = 1; - q_status_message2(SM_ORDER, 0, 4, -*************** -*** 3268,3273 **** ---- 3284,3292 ---- - */ - if(SORT_IS_THREADED(msgmap)) - refresh_sort(stream, msgmap, SRT_NON); -+ if (msgmap->nmsgs -+ && F_ON(F_ENHANCED_THREAD, state) && COLL_THRDS()) -+ kolapse_thread(state, stream, msgmap, '[', 0); - } - else{ - if(del_count) -*************** -*** 6945,6951 **** - * Maybe it makes sense to zoom after a select but not after a colon - * command even though they are very similar. - */ -! thread_command(state, state->mail_stream, msgmap, ':', -FOOTER_ROWS(state)); - } - else{ - if((all_selected = ---- 6964,6970 ---- - * Maybe it makes sense to zoom after a select but not after a colon - * command even though they are very similar. - */ -! thread_command(state, state->mail_stream, msgmap, ':', -FOOTER_ROWS(state), 1); - } - else{ - if((all_selected = -*************** -*** 7001,7007 **** - ----*/ - int - apply_command(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap, -! UCS preloadkeystroke, int flags, int q_line) - { - int i = 8, /* number of static entries in sel_opts3 */ - rv = 0, ---- 7020,7026 ---- - ----*/ - int - apply_command(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap, -! UCS preloadkeystroke, int flags, int q_line, int display) - { - int i = 8, /* number of static entries in sel_opts3 */ - rv = 0, -*************** -*** 7153,7161 **** - collapse_or_expand(state, stream, msgmap, - F_ON(F_SLASH_COLL_ENTIRE, ps_global) - ? 0L -! : mn_get_cur(msgmap)); - break; - - case ':' : - select_thread_stmp(state, stream, msgmap); - break; ---- 7172,7190 ---- - collapse_or_expand(state, stream, msgmap, - F_ON(F_SLASH_COLL_ENTIRE, ps_global) - ? 0L -! : mn_get_cur(msgmap), -! display); - break; - -+ case '[' : -+ collapse_this_thread(state, stream, msgmap, display, 0); -+ break; -+ -+ case ']' : -+ expand_this_thread(state, stream, msgmap, display, 0); -+ break; -+ -+ - case ':' : - select_thread_stmp(state, stream, msgmap); - break; -*************** -*** 9014,9023 **** - Returns 0 if it was cancelled, 1 otherwise. - ----*/ - int -! select_sort(struct pine *state, int ql, SortOrder *sort, int *rev) - { - char prompt[200], tmp[3], *p; -! int s, i; - int deefault = 'a', retval = 1; - HelpType help; - ESCKEY_S sorts[14]; ---- 9043,9052 ---- - Returns 0 if it was cancelled, 1 otherwise. - ----*/ - int -! select_sort(struct pine *state, int ql, SortOrder *sort, int *rev, int thread) - { - char prompt[200], tmp[3], *p; -! int s, i, j; - int deefault = 'a', retval = 1; - HelpType help; - ESCKEY_S sorts[14]; -*************** -*** 9050,9066 **** - strncpy(prompt, _("Choose type of sort, or 'R' to reverse current sort : "), - sizeof(prompt)); - -! for(i = 0; state->sort_types[i] != EndofList; i++) { -! sorts[i].rval = i; -! p = sorts[i].label = sort_name(state->sort_types[i]); -! while(*(p+1) && islower((unsigned char)*p)) -! p++; -! -! sorts[i].ch = tolower((unsigned char)(tmp[0] = *p)); -! sorts[i].name = cpystr(tmp); -! -! if(mn_get_sort(state->msgmap) == state->sort_types[i]) -! deefault = sorts[i].rval; - } - - sorts[i].ch = 'r'; ---- 9079,9104 ---- - strncpy(prompt, _("Choose type of sort, or 'R' to reverse current sort : "), - sizeof(prompt)); - -! for(i = 0, j = 0; state->sort_types[i] != EndofList; i++) { -! sorts[i].rval = i; -! sorts[i].name = cpystr(""); -! sorts[i].label = ""; -! sorts[i].ch = -2; -! if (!thread || allowed_thread_key(state->sort_types[i])){ -! p = sorts[j].label = sort_name(state->sort_types[i]); -! while(*(p+1) && islower((unsigned char)*p)) -! p++; -! sorts[j].ch = tolower((unsigned char)(tmp[0] = *p)); -! sorts[j++].name = cpystr(tmp); -! } -! -! if (thread){ -! if (state->thread_def_sort == state->sort_types[i]) -! deefault = sorts[j-1].rval; -! } -! else -! if(mn_get_sort(state->msgmap) == state->sort_types[i]) -! deefault = sorts[i].rval; - } - - sorts[i].ch = 'r'; -*************** -*** 9084,9091 **** - state->mangled_body = 1; /* signal screen's changed */ - if(s == 'r') - *rev = !mn_get_revsort(state->msgmap); -! else - *sort = state->sort_types[s]; - - if(F_ON(F_SHOW_SORT, ps_global)) - ps_global->mangled_header = 1; ---- 9122,9138 ---- - state->mangled_body = 1; /* signal screen's changed */ - if(s == 'r') - *rev = !mn_get_revsort(state->msgmap); -! else{ -! if(thread){ -! for(i = 0; state->sort_types[i] != EndofList; i++){ -! if(struncmp(sort_name(state->sort_types[i]), -! sorts[s].label, strlen(sorts[s].label)) == 0) -! break; -! } -! s = i; -! } - *sort = state->sort_types[s]; -+ } - - if(F_ON(F_SHOW_SORT, ps_global)) - ps_global->mangled_header = 1; -*************** -*** 9470,9472 **** ---- 9517,9894 ---- - } - - #endif /* _WINDOWS */ -+ -+ void -+ cmd_delete_this_thread(state, stream, msgmap) -+ struct pine *state; -+ MAILSTREAM *stream; -+ MSGNO_S *msgmap; -+ { -+ unsigned long rawno, top, save_kolapsed; -+ PINETHRD_S *thrd = NULL, *nxthrd; -+ -+ if(!stream) -+ return; -+ -+ rawno = mn_m2raw(msgmap, mn_get_cur(msgmap)); -+ move_top_this_thread(stream, msgmap, rawno); -+ top = mn_m2raw(msgmap, mn_get_cur(msgmap)); -+ if(top) -+ thrd = fetch_thread(stream, top); -+ -+ if(!thrd) -+ return; -+ -+ save_kolapsed = this_thread_is_kolapsed(state, stream, msgmap, top); -+ collapse_this_thread(state, stream, msgmap, 0, 0); -+ thread_command(state, stream, msgmap, 'd', -FOOTER_ROWS(state), 1); -+ if (!save_kolapsed) -+ expand_this_thread(state, stream, msgmap, 0, 0); -+ } -+ -+ void -+ cmd_delete_thread(state, stream, msgmap) -+ struct pine *state; -+ MAILSTREAM *stream; -+ MSGNO_S *msgmap; -+ { -+ unsigned long rawno, top, orig_top, topnxt, save_kolapsed; -+ PINETHRD_S *thrd = NULL, *nxthrd; -+ int done = 0, count; -+ -+ if(!stream) -+ return; -+ -+ rawno = mn_m2raw(msgmap, mn_get_cur(msgmap)); -+ move_top_thread(stream, msgmap, rawno); -+ top = orig_top = mn_m2raw(msgmap, mn_get_cur(msgmap)); -+ if(top) -+ thrd = fetch_thread(stream, top); -+ -+ if(!thrd) -+ return; -+ -+ while (!done){ -+ cmd_delete_this_thread(state, stream, msgmap); -+ if (F_OFF(F_ENHANCED_THREAD, state) -+ || (move_next_this_thread(state, stream, msgmap, 0) <= 0) -+ || !(top = mn_m2raw(msgmap, mn_get_cur(msgmap))) -+ || (orig_top != top_thread(stream, top))) -+ done++; -+ } -+ mn_set_cur(msgmap,mn_raw2m(msgmap, rawno)); -+ cmd_delete(state, msgmap, MCMD_NONE, cmd_delete_index); -+ count = count_thread(state, stream, msgmap, rawno); -+ q_status_message2(SM_ORDER, 0, 1, "%s message%s marked deleted", -+ int2string(count), plural(count)); -+ } -+ -+ int -+ collapse_this_thread(state, stream, msgmap, display, special) -+ struct pine *state; -+ MAILSTREAM *stream; -+ MSGNO_S *msgmap; -+ int display; -+ int special; -+ { -+ int collapsed, rv = 1, done = 0; -+ PINETHRD_S *thrd = NULL, *nthrd; -+ unsigned long rawno, orig, msgno; -+ -+ if(!stream) -+ return 0; -+ -+ rawno = mn_m2raw(msgmap, mn_get_cur(msgmap)); -+ -+ if(rawno) -+ thrd = fetch_thread(stream, rawno); -+ -+ if(!thrd) -+ return rv; -+ -+ collapsed = this_thread_is_kolapsed(state, stream, msgmap, rawno); -+ -+ if (special && collapsed){ -+ expand_this_thread(state, stream, msgmap, 0, 0); -+ collapsed = 0; -+ } -+ -+ clear_index_cache_ent(stream, rawno, 0); -+ -+ if (!collapsed && thrd->next){ -+ if (thrd->rawno == top_thread(stream, thrd->rawno)) -+ collapse_or_expand(state, stream, msgmap, mn_get_cur(msgmap), display); -+ else{ -+ set_lflag(stream, msgmap, mn_raw2m(msgmap,thrd->rawno), MN_COLL, 1); -+ set_thread_subtree(stream, thrd, msgmap, 1, MN_CHID); +--- + alpine/arg.c | 14 + alpine/confscroll.c | 66 +++ + alpine/confscroll.h | 2 + alpine/keymenu.c | 47 ++ + alpine/keymenu.h | 13 + alpine/mailcmd.c | 464 +++++++++++++++++++++++++- + alpine/mailcmd.h | 13 + alpine/mailindx.c | 124 ++++++- + alpine/mailindx.h | 2 + alpine/mailview.c | 46 ++ + alpine/roleconf.c | 10 + alpine/setup.c | 60 +++ + pith/conf.c | 24 + + pith/conf.h | 3 + pith/conftype.h | 3 + pith/flag.c | 6 + pith/indxtype.h | 2 + pith/mailindx.c | 58 ++- + pith/pattern.c | 2 + pith/pine.hlp | 222 ++++++++++++ + pith/sort.c | 33 + + pith/sort.h | 6 + pith/state.c | 1 + pith/state.h | 5 + pith/thread.c | 772 ++++++++++++++++++++++++++++++++++++++++---- + pith/thread.h | 23 + + web/src/alpined.d/alpined.c | 4 + 27 files changed, 1870 insertions(+), 155 deletions(-) + +Index: alpine-2.11/alpine/arg.c +=================================================================== +--- alpine-2.11.orig/alpine/arg.c ++++ alpine-2.11/alpine/arg.c +@@ -60,6 +60,7 @@ static char args_err_missing_passfile[] + static char args_err_non_abs_passfile[] = N_("argument to \"-passfile\" should be fully-qualified"); + #endif + static char args_err_missing_sort[] = N_("missing argument for option \"-sort\""); ++static char args_err_missing_thread_sort[] = N_("missing argument for option \"-threadsort\""); + static char args_err_missing_flag_arg[] = N_("missing argument for flag \"%c\""); + static char args_err_missing_flag_num[] = N_("Non numeric argument for flag \"%c\""); + static char args_err_missing_debug_num[] = N_("Non numeric argument for \"%s\""); +@@ -103,6 +104,7 @@ N_(" -k \t\tKeys - Force use of function + N_(" -z \t\tSuspend - allow use of ^Z suspension"), + N_(" -r \t\tRestricted - can only send mail to oneself"), + N_(" -sort \tSort - Specify sort order of folder:"), ++N_(" -threadsort \tSort - Specify sort order of thread index screen:"), + N_("\t\t\tarrival, subject, threaded, orderedsubject, date,"), + N_("\t\t\tfrom, size, score, to, cc, /reverse"), + N_(" -i\t\tIndex - Go directly to index, bypassing main menu"), +@@ -192,6 +194,7 @@ pine_args(struct pine *pine_state, int a + char *cmd_list = NULL; + char *debug_str = NULL; + char *sort = NULL; ++ char *threadsort = NULL; + char *pinerc_file = NULL; + char *lc = NULL; + int do_help = 0; +@@ -363,6 +366,17 @@ Loop: while(--ac > 0) + + goto Loop; + } ++ else if(strcmp(*av, "threadsort") == 0){ ++ if(--ac){ ++ threadsort = *++av; ++ COM_THREAD_SORT_KEY = cpystr(threadsort); ++ } ++ else{ ++ display_args_err(_(args_err_missing_thread_sort), NULL, 1); ++ ++usage; ++ } ++ goto Loop; ++ } + else if(strcmp(*av, "url") == 0){ + if(args->action == aaFolder && !args->data.folder){ + args->action = aaURL; +Index: alpine-2.11/alpine/confscroll.c +=================================================================== +--- alpine-2.11.orig/alpine/confscroll.c ++++ alpine-2.11/alpine/confscroll.c +@@ -139,7 +139,7 @@ char *yesno_pretty_value(struct pine + char *radio_pretty_value(struct pine *, CONF_S *); + char *sigfile_pretty_value(struct pine *, CONF_S *); + char *color_pretty_value(struct pine *, CONF_S *); +-char *sort_pretty_value(struct pine *, CONF_S *); ++char *sort_pretty_value(struct pine *, CONF_S *, int); + int longest_feature_name(void); + COLOR_PAIR *sample_color(struct pine *, struct variable *); + COLOR_PAIR *sampleexc_color(struct pine *, struct variable *); +@@ -287,7 +287,8 @@ set_radio_pretty_vals(struct pine *ps, C + CONF_S *ctmp; + + if(!(cl && *cl && +- ((*cl)->var == &ps->vars[V_SORT_KEY] || ++ (((*cl)->var == &ps->vars[V_SORT_KEY]) || ++ ((*cl)->var == &ps->vars[V_THREAD_SORT_KEY]) || + standard_radio_var(ps, (*cl)->var) || + (*cl)->var == startup_ptr))) + return; +@@ -2923,7 +2924,7 @@ radiobutton_tool(struct pine *ps, int cm + } + + set_current_val((*cl)->var, TRUE, TRUE); +- if(decode_sort(ps->VAR_SORT_KEY, &def_sort, &def_sort_rev) != -1){ ++ if(decode_sort(ps->VAR_SORT_KEY, &def_sort, &def_sort_rev,0) != -1){ + ps->def_sort = def_sort; + ps->def_sort_rev = def_sort_rev; + } +@@ -2932,6 +2933,37 @@ radiobutton_tool(struct pine *ps, int cm + ps->mangled_body = 1; /* BUG: redraw it all for now? */ + rv = 1; + } ++ else if((*cl)->var == &ps->vars[V_THREAD_SORT_KEY]){ ++ SortOrder thread_def_sort; ++ int thread_def_sort_rev; ++ ++ thread_def_sort_rev = (*cl)->varmem >= (short) EndofList; ++ thread_def_sort = (SortOrder) ((*cl)->varmem - (thread_def_sort_rev ++ * EndofList)); ++ sprintf(tmp_20k_buf, "%s%s", sort_name(thread_def_sort), ++ (thread_def_sort_rev) ? "/Reverse" : ""); ++ ++ if((*cl)->var->cmdline_val.p) ++ fs_give((void **)&(*cl)->var->cmdline_val.p); ++ ++ if(apval){ ++ if(*apval) ++ fs_give((void **)apval); ++ ++ *apval = cpystr(tmp_20k_buf); ++ } ++ ++ set_current_val((*cl)->var, TRUE, TRUE); ++ if(decode_sort(ps->VAR_THREAD_SORT_KEY, &thread_def_sort, ++ &thread_def_sort_rev, 1) != -1){ ++ ps->thread_def_sort = thread_def_sort; ++ ps->thread_def_sort_rev = thread_def_sort_rev; ++ } ++ ++ set_radio_pretty_vals(ps, cl); ++ ps->mangled_body = 1; /* BUG: redraw it all for now? */ ++ rv = 1; + } + else + q_status_message(SM_ORDER | SM_DING, 3, 6, + "Programmer botch! Unknown radiobutton type."); +@@ -3794,7 +3826,9 @@ pretty_value(struct pine *ps, CONF_S *cl + else if(standard_radio_var(ps, v) || v == startup_ptr) + return(radio_pretty_value(ps, cl)); + else if(v == &ps->vars[V_SORT_KEY]) +- return(sort_pretty_value(ps, cl)); ++ return(sort_pretty_value(ps, cl, 0)); ++ else if(v == &ps->vars[V_THREAD_SORT_KEY]) ++ return(sort_pretty_value(ps, cl, 1)); + else if(v == &ps->vars[V_SIGNATURE_FILE]) + return(sigfile_pretty_value(ps, cl)); + else if(v == &ps->vars[V_USE_ONLY_DOMAIN_NAME]) +@@ -4325,14 +4359,14 @@ color_pretty_value(struct pine *ps, CONF + + + char * +-sort_pretty_value(struct pine *ps, CONF_S *cl) ++sort_pretty_value(struct pine *ps, CONF_S *cl, int thread) + { +- return(generalized_sort_pretty_value(ps, cl, 1)); ++ return(generalized_sort_pretty_value(ps, cl, 1, thread)); + } + + + char * +-generalized_sort_pretty_value(struct pine *ps, CONF_S *cl, int default_ok) ++generalized_sort_pretty_value(struct pine *ps, CONF_S *cl, int default_ok, int thread) + { + char tmp[6*MAXPATH]; + char *pvalnorm, *pvalexc, *pval; +@@ -4382,7 +4416,7 @@ generalized_sort_pretty_value(struct pin + } + else if(fixed){ + pval = v->fixed_val.p; +- decode_sort(pval, &var_sort, &var_sort_rev); ++ decode_sort(pval, &var_sort, &var_sort_rev, thread); + is_the_one = (var_sort_rev == line_sort_rev && var_sort == line_sort); + + utf8_snprintf(tmp, sizeof(tmp), "(%c) %s%-*w%*s%s", +@@ -4393,9 +4427,9 @@ generalized_sort_pretty_value(struct pin + is_the_one ? " (value is fixed)" : ""); + } + else if(is_set_for_this_level){ +- decode_sort(pval, &var_sort, &var_sort_rev); ++ decode_sort(pval, &var_sort, &var_sort_rev, thread); + is_the_one = (var_sort_rev == line_sort_rev && var_sort == line_sort); +- decode_sort(pvalexc, &exc_sort, &exc_sort_rev); ++ decode_sort(pvalexc, &exc_sort, &exc_sort_rev, thread); + the_exc_one = (editing_normal_which_isnt_except && pvalexc && + exc_sort_rev == line_sort_rev && exc_sort == line_sort); + utf8_snprintf(tmp, sizeof(tmp), "(%c) %s%-*w%*s%s", +@@ -4413,7 +4447,7 @@ generalized_sort_pretty_value(struct pin + } + else{ + if(pvalexc){ +- decode_sort(pvalexc, &exc_sort, &exc_sort_rev); ++ decode_sort(pvalexc, &exc_sort, &exc_sort_rev, thread); + is_the_one = (exc_sort_rev == line_sort_rev && + exc_sort == line_sort); + utf8_snprintf(tmp, sizeof(tmp), "( ) %s%-*w%*s%s", +@@ -4424,7 +4458,7 @@ generalized_sort_pretty_value(struct pin + } + else{ + pval = v->current_val.p; +- decode_sort(pval, &var_sort, &var_sort_rev); ++ decode_sort(pval, &var_sort, &var_sort_rev, thread); + is_the_one = ((pval || default_ok) && + var_sort_rev == line_sort_rev && + var_sort == line_sort); +@@ -5548,9 +5582,15 @@ fix_side_effects(struct pine *ps, struct + else if(revert && var == &ps->vars[V_SORT_KEY]){ + int def_sort_rev; + +- decode_sort(VAR_SORT_KEY, &ps->def_sort, &def_sort_rev); ++ decode_sort(VAR_SORT_KEY, &ps->def_sort, &def_sort_rev, 0); + ps->def_sort_rev = def_sort_rev; + } ++ else if(revert && var == &ps->vars[V_THREAD_SORT_KEY]){ ++ int thread_def_sort_rev; ++ ++ decode_sort(VAR_THREAD_SORT_KEY, &ps->thread_def_sort, &thread_def_sort_rev, 1); ++ ps->thread_def_sort_rev = thread_def_sort_rev; ++ } + else if(var == &ps->vars[V_THREAD_MORE_CHAR] || + var == &ps->vars[V_THREAD_EXP_CHAR] || + var == &ps->vars[V_THREAD_LASTREPLY_CHAR]){ +Index: alpine-2.11/alpine/confscroll.h +=================================================================== +--- alpine-2.11.orig/alpine/confscroll.h ++++ alpine-2.11/alpine/confscroll.h +@@ -95,7 +95,7 @@ int checkbox_tool(struct pine *, int, C + int radiobutton_tool(struct pine *, int, CONF_S **, unsigned); + int yesno_tool(struct pine *, int, CONF_S **, unsigned); + int text_toolit(struct pine *, int, CONF_S **, unsigned, int); +-char *generalized_sort_pretty_value(struct pine *, CONF_S *, int); ++char *generalized_sort_pretty_value(struct pine *, CONF_S *, int, int); + int exclude_config_var(struct pine *, struct variable *, int); + int config_exit_cmd(unsigned); + int simple_exit_cmd(unsigned); +Index: alpine-2.11/alpine/keymenu.c +=================================================================== +--- alpine-2.11.orig/alpine/keymenu.c ++++ alpine-2.11/alpine/keymenu.c +@@ -650,10 +650,25 @@ struct key index_keys[] = + RCOMPOSE_MENU, + HOMEKEY_MENU, + ENDKEY_MENU, +- NULL_MENU, ++ {"K","Sort Thread",{MC_SORTHREAD,1,{'k'}},KS_NONE}, + /* TRANSLATORS: toggles a collapsed view or an expanded view + of a message thread on and off */ + {"/",N_("Collapse/Expand"),{MC_COLLAPSE,1,{'/'}},KS_NONE}, ++ /* TRANSLATORS: Collapse all threads */ ++ {"{",N_("Collapse All"),{MC_KOLAPSE,1,{'{'}},KS_NONE}, ++ /* TRANSLATORS: Expand all threads */ ++ {"}",N_("Expand All"), {MC_EXPTHREAD,1,{'}'}},KS_NONE}, ++ ++ HELP_MENU, ++ OTHER_MENU, ++ {")","Next Threa",{MC_NEXTHREAD,1,{')'}},KS_NONE}, ++ {"(","Prev Threa",{MC_PRETHREAD,1,{'('}},KS_NONE}, ++ {"^R","Remove Thr",{MC_DELTHREAD,1,{ctrl('r')}},KS_NONE}, ++ {"^U","Undel Thre",{MC_UNDTHREAD,1,{ctrl('u')}},KS_NONE}, ++ {"^T","Select Thr",{MC_SELTHREAD,1,{ctrl('t')}},KS_NONE}, ++ NULL_MENU, ++ {"[","Close Thre",{MC_CTHREAD,1,{'['}},KS_NONE}, ++ {"]","Open Threa",{MC_OTHREAD,1,{']'}},KS_NONE}, + {"@", N_("Quota"), {MC_QUOTA,1,{'@'}}, KS_NONE}, + NULL_MENU}; + INST_KEY_MENU(index_keymenu, index_keys); +@@ -728,9 +743,22 @@ struct key thread_keys[] = + RCOMPOSE_MENU, + HOMEKEY_MENU, + ENDKEY_MENU, +- NULL_MENU, ++ {"]",N_("Open Thread"),{MC_OTHREAD,1,{']'}},KS_NONE}, + {"/",N_("Collapse/Expand"),{MC_COLLAPSE,1,{'/'}},KS_NONE}, ++ {")",N_("Next Thread"),{MC_NEXTHREAD,1,{')'}},KS_NONE}, ++ {"(",N_("Prev Thread"),{MC_PRETHREAD,1,{'('}},KS_NONE}, ++ ++ HELP_MENU, ++ OTHER_MENU, + {"@", N_("Quota"), {MC_QUOTA,1,{'@'}}, KS_NONE}, ++ NULL_MENU, ++ {"^R",N_("Remove Thread"),{MC_DELTHREAD,1,{ctrl('r')}},KS_NONE}, ++ {"^U",N_("Undelete Thread"),{MC_UNDTHREAD,1,{ctrl('u')}},KS_NONE}, ++ {"^T",N_("SelecT Thread"),{MC_SELTHREAD,1,{ctrl('t')}},KS_NONE}, ++ NULL_MENU, ++ NULL_MENU, ++ NULL_MENU, ++ {"K","Sort Thread",{MC_SORTHREAD,1,{'k'}},KS_NONE}, + NULL_MENU}; + INST_KEY_MENU(thread_keymenu, thread_keys); + +@@ -880,7 +908,20 @@ struct key view_keys[] = + NULL_MENU, + NULL_MENU, + NULL_MENU, +- NULL_MENU}; ++ NULL_MENU, ++ ++ HELP_MENU, ++ OTHER_MENU, ++ NULL_MENU, ++ NULL_MENU, ++ NULL_MENU, ++ NULL_MENU, ++ NULL_MENU, ++ {"(",N_("Prev Thread"),{MC_PRETHREAD,1,{'('}},KS_NONE}, ++ {")",N_("Next Thread"),{MC_NEXTHREAD,1,{')'}},KS_NONE}, ++ {"^R",N_("Remove Thread"),{MC_DELTHREAD,1,{ctrl('r')}},KS_NONE}, ++ {"^U",N_("Undelete Thread"),{MC_UNDTHREAD,1,{ctrl('u')}},KS_NONE}, ++ {"^T",N_("selecT Thread"),{MC_SELTHREAD,1,{ctrl('t')}},KS_NONE}}; + INST_KEY_MENU(view_keymenu, view_keys); + + +Index: alpine-2.11/alpine/keymenu.h +=================================================================== +--- alpine-2.11.orig/alpine/keymenu.h ++++ alpine-2.11/alpine/keymenu.h +@@ -215,6 +215,19 @@ struct key_menu { + #define MC_DECRYPT 802 + #define MC_QUOTA 803 + #define MC_ADDHEADER 804 ++#define MC_DELTHREAD 805 ++#define MC_UNDTHREAD 806 ++#define MC_SELTHREAD 807 ++#define MC_SSUTHREAD 808 ++#define MC_DSUTHREAD 809 ++#define MC_USUTHREAD 810 ++#define MC_SORTHREAD 811 ++#define MC_NEXTHREAD 812 ++#define MC_KOLAPSE 813 ++#define MC_EXPTHREAD 814 ++#define MC_PRETHREAD 815 ++#define MC_CTHREAD 816 ++#define MC_OTHREAD 817 + + /* + * Some standard Key/Command Bindings +Index: alpine-2.11/alpine/mailcmd.c +=================================================================== +--- alpine-2.11.orig/alpine/mailcmd.c ++++ alpine-2.11/alpine/mailcmd.c +@@ -113,7 +113,7 @@ int select_by_thread(MAILSTREAM *, MSG + char *choose_a_rule(int); + int select_by_keyword(MAILSTREAM *, SEARCHSET **); + char *choose_a_keyword(void); +-int select_sort(struct pine *, int, SortOrder *, int *); ++int select_sort(struct pine *, int, SortOrder *, int *, int); + int print_index(struct pine *, MSGNO_S *, int); + + +@@ -1335,7 +1335,7 @@ get_out: + if(any_messages(msgmap, NULL, NULL)){ + if(any_lflagged(msgmap, MN_SLCT) > 0L){ + if(apply_command(state, stream, msgmap, 0, +- AC_NONE, question_line)){ ++ AC_NONE, question_line, 1)){ + if(F_ON(F_AUTO_UNSELECT, state)){ + agg_select_all(stream, msgmap, NULL, 0); + unzoom_index(state, stream, msgmap); +@@ -1353,23 +1353,35 @@ get_out: + + /*-------- Sort command -------*/ + case MC_SORT : ++ case MC_SORTHREAD: + { + int were_threading = THREADING(); + SortOrder sort = mn_get_sort(msgmap); + int rev = mn_get_revsort(msgmap); ++ int thread = (command == MC_SORT) ? 0 : 1; + + dprint((1,"MAIL_CMD: sort\n")); +- if(select_sort(state, question_line, &sort, &rev)){ ++ if(sort == SortThread) ++ sort = ps_global->thread_cur_sort; ++ if(select_sort(state, question_line, &sort, &rev, thread)){ + /* $ command reinitializes threading collapsed/expanded info */ + if(SORT_IS_THREADED(msgmap) && !SEP_THRDINDX()) + erase_threading_info(stream, msgmap); + ++ if(command == MC_SORTHREAD){ ++ ps_global->thread_cur_sort = sort; ++ sort = SortThread; ++ } ++ else if(sort == SortThread) /* command = MC_SORT */ ++ ps_global->thread_cur_sort = F_ON(F_THREAD_SORTS_BY_ARRIVAL, ps_global) ++ ? SortArrival : ps_global->thread_def_sort; ++ + if(ps_global && ps_global->ttyo){ + blank_keymenu(ps_global->ttyo->screen_rows - 2, 0); + ps_global->mangled_footer = 1; + } + +- sort_folder(stream, msgmap, sort, rev, SRT_VRB|SRT_MAN); ++ sort_folder(stream, msgmap, sort, rev, SRT_VRB|SRT_MAN, 1); + } + + state->mangled_footer = 1; +@@ -3174,6 +3186,10 @@ cmd_expunge(struct pine *state, MAILSTRE + if(SORT_IS_THREADED(msgmap)) + refresh_sort(stream, msgmap, SRT_NON); + ++ if (msgmap->nmsgs ++ && F_ON(F_ENHANCED_THREAD, state) && COLL_THRDS()) ++ kolapse_thread(state, stream, msgmap, '[', 0); ++ + state->mangled_body = 1; + state->mangled_header = 1; + q_status_message2(SM_ORDER, 0, 4, +@@ -3268,6 +3284,9 @@ cmd_expunge(struct pine *state, MAILSTRE + */ + if(SORT_IS_THREADED(msgmap)) + refresh_sort(stream, msgmap, SRT_NON); ++ if (msgmap->nmsgs ++ && F_ON(F_ENHANCED_THREAD, state) && COLL_THRDS()) ++ kolapse_thread(state, stream, msgmap, '[', 0); + } + else{ + if(del_count) +@@ -6945,7 +6964,7 @@ select_by_current(struct pine *state, MS + * Maybe it makes sense to zoom after a select but not after a colon + * command even though they are very similar. + */ +- thread_command(state, state->mail_stream, msgmap, ':', -FOOTER_ROWS(state)); ++ thread_command(state, state->mail_stream, msgmap, ':', -FOOTER_ROWS(state), 1); + } + else{ + if((all_selected = +@@ -7001,7 +7020,7 @@ select_by_current(struct pine *state, MS + ----*/ + int + apply_command(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap, +- UCS preloadkeystroke, int flags, int q_line) ++ UCS preloadkeystroke, int flags, int q_line, int display) + { + int i = 8, /* number of static entries in sel_opts3 */ + rv = 0, +@@ -7153,9 +7172,19 @@ apply_command(struct pine *state, MAILST + collapse_or_expand(state, stream, msgmap, + F_ON(F_SLASH_COLL_ENTIRE, ps_global) + ? 0L +- : mn_get_cur(msgmap)); ++ : mn_get_cur(msgmap), ++ display); + break; + ++ case '[' : ++ collapse_this_thread(state, stream, msgmap, display, 0); ++ break; ++ ++ case ']' : ++ expand_this_thread(state, stream, msgmap, display, 0); ++ break; ++ ++ + case ':' : + select_thread_stmp(state, stream, msgmap); + break; +@@ -9014,10 +9043,10 @@ Args: state -- pine state pointer + Returns 0 if it was cancelled, 1 otherwise. + ----*/ + int +-select_sort(struct pine *state, int ql, SortOrder *sort, int *rev) ++select_sort(struct pine *state, int ql, SortOrder *sort, int *rev, int thread) + { + char prompt[200], tmp[3], *p; +- int s, i; ++ int s, i, j; + int deefault = 'a', retval = 1; + HelpType help; + ESCKEY_S sorts[14]; +@@ -9050,17 +9079,26 @@ select_sort(struct pine *state, int ql, + strncpy(prompt, _("Choose type of sort, or 'R' to reverse current sort : "), + sizeof(prompt)); + +- for(i = 0; state->sort_types[i] != EndofList; i++) { +- sorts[i].rval = i; +- p = sorts[i].label = sort_name(state->sort_types[i]); +- while(*(p+1) && islower((unsigned char)*p)) +- p++; +- +- sorts[i].ch = tolower((unsigned char)(tmp[0] = *p)); +- sorts[i].name = cpystr(tmp); +- +- if(mn_get_sort(state->msgmap) == state->sort_types[i]) +- deefault = sorts[i].rval; ++ for(i = 0, j = 0; state->sort_types[i] != EndofList; i++) { ++ sorts[i].rval = i; ++ sorts[i].name = cpystr(""); ++ sorts[i].label = ""; ++ sorts[i].ch = -2; ++ if (!thread || allowed_thread_key(state->sort_types[i])){ ++ p = sorts[j].label = sort_name(state->sort_types[i]); ++ while(*(p+1) && islower((unsigned char)*p)) ++ p++; ++ sorts[j].ch = tolower((unsigned char)(tmp[0] = *p)); ++ sorts[j++].name = cpystr(tmp); ++ } ++ ++ if (thread){ ++ if (state->thread_def_sort == state->sort_types[i]) ++ deefault = sorts[j-1].rval; ++ } ++ else ++ if(mn_get_sort(state->msgmap) == state->sort_types[i]) ++ deefault = sorts[i].rval; + } + + sorts[i].ch = 'r'; +@@ -9084,8 +9122,17 @@ select_sort(struct pine *state, int ql, + state->mangled_body = 1; /* signal screen's changed */ + if(s == 'r') + *rev = !mn_get_revsort(state->msgmap); +- else ++ else{ ++ if(thread){ ++ for(i = 0; state->sort_types[i] != EndofList; i++){ ++ if(struncmp(sort_name(state->sort_types[i]), ++ sorts[s].label, strlen(sorts[s].label)) == 0) ++ break; ++ } ++ s = i; ++ } + *sort = state->sort_types[s]; ++ } + + if(F_ON(F_SHOW_SORT, ps_global)) + ps_global->mangled_header = 1; +@@ -9470,3 +9517,378 @@ flag_submenu(mc) + } + + #endif /* _WINDOWS */ ++ ++void ++cmd_delete_this_thread(state, stream, msgmap) ++ struct pine *state; ++ MAILSTREAM *stream; ++ MSGNO_S *msgmap; ++{ ++ unsigned long rawno, top, save_kolapsed; ++ PINETHRD_S *thrd = NULL, *nxthrd; ++ ++ if(!stream) ++ return; ++ ++ rawno = mn_m2raw(msgmap, mn_get_cur(msgmap)); ++ move_top_this_thread(stream, msgmap, rawno); ++ top = mn_m2raw(msgmap, mn_get_cur(msgmap)); ++ if(top) ++ thrd = fetch_thread(stream, top); ++ ++ if(!thrd) ++ return; ++ ++ save_kolapsed = this_thread_is_kolapsed(state, stream, msgmap, top); ++ collapse_this_thread(state, stream, msgmap, 0, 0); ++ thread_command(state, stream, msgmap, 'd', -FOOTER_ROWS(state), 1); ++ if (!save_kolapsed) ++ expand_this_thread(state, stream, msgmap, 0, 0); ++} ++ ++void ++cmd_delete_thread(state, stream, msgmap) ++ struct pine *state; ++ MAILSTREAM *stream; ++ MSGNO_S *msgmap; ++{ ++ unsigned long rawno, top, orig_top, topnxt, save_kolapsed; ++ PINETHRD_S *thrd = NULL, *nxthrd; ++ int done = 0, count; ++ ++ if(!stream) ++ return; ++ ++ rawno = mn_m2raw(msgmap, mn_get_cur(msgmap)); ++ move_top_thread(stream, msgmap, rawno); ++ top = orig_top = mn_m2raw(msgmap, mn_get_cur(msgmap)); ++ if(top) ++ thrd = fetch_thread(stream, top); ++ ++ if(!thrd) ++ return; ++ ++ while (!done){ ++ cmd_delete_this_thread(state, stream, msgmap); ++ if (F_OFF(F_ENHANCED_THREAD, state) ++ || (move_next_this_thread(state, stream, msgmap, 0) <= 0) ++ || !(top = mn_m2raw(msgmap, mn_get_cur(msgmap))) ++ || (orig_top != top_thread(stream, top))) ++ done++; ++ } ++ mn_set_cur(msgmap,mn_raw2m(msgmap, rawno)); ++ cmd_delete(state, msgmap, MCMD_NONE, cmd_delete_index); ++ count = count_thread(state, stream, msgmap, rawno); ++ q_status_message2(SM_ORDER, 0, 1, "%s message%s marked deleted", ++ int2string(count), plural(count)); ++} ++ ++int ++collapse_this_thread(state, stream, msgmap, display, special) ++ struct pine *state; ++ MAILSTREAM *stream; ++ MSGNO_S *msgmap; ++ int display; ++ int special; ++{ ++ int collapsed, rv = 1, done = 0; ++ PINETHRD_S *thrd = NULL, *nthrd; ++ unsigned long rawno, orig, msgno; ++ ++ if(!stream) ++ return 0; ++ ++ rawno = mn_m2raw(msgmap, mn_get_cur(msgmap)); ++ ++ if(rawno) ++ thrd = fetch_thread(stream, rawno); ++ ++ if(!thrd) ++ return rv; ++ ++ collapsed = this_thread_is_kolapsed(state, stream, msgmap, rawno); ++ ++ if (special && collapsed){ ++ expand_this_thread(state, stream, msgmap, 0, 0); ++ collapsed = 0; ++ } ++ ++ clear_index_cache_ent(stream, rawno, 0); ++ ++ if (!collapsed && thrd->next){ ++ if (thrd->rawno == top_thread(stream, thrd->rawno)) ++ collapse_or_expand(state, stream, msgmap, mn_get_cur(msgmap), display); ++ else{ ++ set_lflag(stream, msgmap, mn_raw2m(msgmap,thrd->rawno), MN_COLL, 1); ++ set_thread_subtree(stream, thrd, msgmap, 1, MN_CHID); ++ } ++ } ++ else{ ++ if (!collapsed && special ++ && ((F_OFF(F_ENHANCED_THREAD, state) && !thrd->next) ++ || F_ON(F_ENHANCED_THREAD, state))){ ++ if (thrd->toploose){ ++ if (thrd->rawno != thrd->toploose) ++ set_lflag(stream, msgmap, mn_raw2m(msgmap,thrd->rawno),MN_CHID, ++ 1); ++ else ++ set_lflag(stream, msgmap, mn_raw2m(msgmap,thrd->rawno),MN_COLL, ++ 1); ++ } ++ } ++ else{ ++ rv = 0; ++ if (display) ++ q_status_message(SM_ORDER, 0, 1, "Thread already collapsed"); ++ } ++ } ++ return rv; ++} ++ ++void ++collapse_thread(state, stream, msgmap, display) ++ struct pine *state; ++ MAILSTREAM *stream; ++ MSGNO_S *msgmap; ++ int display; ++{ ++ int collapsed, rv = 1, done = 0; ++ PINETHRD_S *thrd = NULL; ++ unsigned long orig, orig_top, top; ++ ++ if(!stream) ++ return; ++ ++ expand_this_thread(state, stream, msgmap, display, 1); ++ orig = mn_m2raw(msgmap, mn_get_cur(msgmap)); ++ move_top_thread(stream, msgmap,orig); ++ top = orig_top = mn_m2raw(msgmap, mn_get_cur(msgmap)); ++ ++ if(top) ++ thrd = fetch_thread(stream, top); ++ ++ if(!thrd) ++ return; ++ ++ while (!done){ ++ collapse_this_thread(state, stream, msgmap, display, 1); ++ if (F_OFF(F_ENHANCED_THREAD, state) ++ || (move_next_this_thread(state, stream, msgmap, 0) <= 0) ++ || !(top = mn_m2raw(msgmap, mn_get_cur(msgmap))) ++ || (orig_top != top_thread(stream, top))) ++ done++; ++ } ++ mn_set_cur(msgmap,mn_raw2m(msgmap, orig_top)); ++} ++ ++int ++expand_this_thread(state, stream, msgmap, display, special) ++ struct pine *state; ++ MAILSTREAM *stream; ++ MSGNO_S *msgmap; ++ int display; ++ int special; ++{ ++ int collapsed, rv = 1, done = 0; ++ PINETHRD_S *thrd = NULL, *nthrd; ++ unsigned long rawno, orig, msgno; ++ ++ if(!stream) ++ return 0; ++ ++ orig = mn_m2raw(msgmap, mn_get_cur(msgmap)); ++ move_top_this_thread(stream, msgmap,orig); ++ rawno = mn_m2raw(msgmap, mn_get_cur(msgmap)); ++ ++ if(rawno) ++ thrd = fetch_thread(stream, rawno); ++ ++ if(!thrd) ++ return rv; ++ ++ collapsed = this_thread_is_kolapsed(state, stream, msgmap, rawno); ++ ++ if (special && !collapsed){ ++ collapse_this_thread(state, stream, msgmap, 0, 0); ++ collapsed = 1; ++ } ++ ++ clear_index_cache_ent(stream, rawno, 0); ++ ++ if (collapsed && thrd->next){ ++ if (thrd->rawno == top_thread(stream, thrd->rawno)) ++ collapse_or_expand(state, stream, msgmap, mn_get_cur(msgmap), display); ++ else{ ++ set_lflag(stream, msgmap, mn_raw2m(msgmap,thrd->rawno), MN_COLL, 0); ++ set_thread_subtree(stream, thrd, msgmap, 0, MN_CHID); ++ } + } + else{ -+ if (!collapsed && special -+ && ((F_OFF(F_ENHANCED_THREAD, state) && !thrd->next) -+ || F_ON(F_ENHANCED_THREAD, state))){ -+ if (thrd->toploose){ -+ if (thrd->rawno != thrd->toploose) -+ set_lflag(stream, msgmap, mn_raw2m(msgmap,thrd->rawno),MN_CHID, -+ 1); -+ else -+ set_lflag(stream, msgmap, mn_raw2m(msgmap,thrd->rawno),MN_COLL, -+ 1); -+ } -+ } -+ else{ -+ rv = 0; -+ if (display) -+ q_status_message(SM_ORDER, 0, 1, "Thread already collapsed"); -+ } ++ if (collapsed && special ++ && ((F_OFF(F_ENHANCED_THREAD, state) && !thrd->next) ++ || F_ON(F_ENHANCED_THREAD, state))){ ++ if (thrd->toploose) ++ if (thrd->rawno != thrd->toploose) ++ set_lflag(stream, msgmap, mn_raw2m(msgmap,thrd->rawno),MN_CHID, 0); ++ else ++ set_lflag(stream, msgmap, mn_raw2m(msgmap,thrd->rawno),MN_COLL, 0); ++ } ++ else{ ++ rv = 0; ++ if (display) ++ q_status_message(SM_ORDER, 0, 1, "Thread already expanded"); ++ } + } -+ return rv; -+ } -+ -+ void -+ collapse_thread(state, stream, msgmap, display) -+ struct pine *state; -+ MAILSTREAM *stream; -+ MSGNO_S *msgmap; -+ int display; -+ { -+ int collapsed, rv = 1, done = 0; -+ PINETHRD_S *thrd = NULL; -+ unsigned long orig, orig_top, top; -+ -+ if(!stream) -+ return; -+ -+ expand_this_thread(state, stream, msgmap, display, 1); -+ orig = mn_m2raw(msgmap, mn_get_cur(msgmap)); -+ move_top_thread(stream, msgmap,orig); -+ top = orig_top = mn_m2raw(msgmap, mn_get_cur(msgmap)); -+ -+ if(top) -+ thrd = fetch_thread(stream, top); -+ -+ if(!thrd) -+ return; -+ -+ while (!done){ -+ collapse_this_thread(state, stream, msgmap, display, 1); -+ if (F_OFF(F_ENHANCED_THREAD, state) -+ || (move_next_this_thread(state, stream, msgmap, 0) <= 0) -+ || !(top = mn_m2raw(msgmap, mn_get_cur(msgmap))) -+ || (orig_top != top_thread(stream, top))) -+ done++; -+ } -+ mn_set_cur(msgmap,mn_raw2m(msgmap, orig_top)); -+ } -+ -+ int -+ expand_this_thread(state, stream, msgmap, display, special) -+ struct pine *state; -+ MAILSTREAM *stream; -+ MSGNO_S *msgmap; -+ int display; -+ int special; -+ { -+ int collapsed, rv = 1, done = 0; -+ PINETHRD_S *thrd = NULL, *nthrd; -+ unsigned long rawno, orig, msgno; -+ -+ if(!stream) -+ return 0; -+ -+ orig = mn_m2raw(msgmap, mn_get_cur(msgmap)); -+ move_top_this_thread(stream, msgmap,orig); -+ rawno = mn_m2raw(msgmap, mn_get_cur(msgmap)); -+ -+ if(rawno) -+ thrd = fetch_thread(stream, rawno); -+ -+ if(!thrd) -+ return rv; -+ -+ collapsed = this_thread_is_kolapsed(state, stream, msgmap, rawno); -+ -+ if (special && !collapsed){ -+ collapse_this_thread(state, stream, msgmap, 0, 0); -+ collapsed = 1; -+ } -+ -+ clear_index_cache_ent(stream, rawno, 0); -+ -+ if (collapsed && thrd->next){ -+ if (thrd->rawno == top_thread(stream, thrd->rawno)) -+ collapse_or_expand(state, stream, msgmap, mn_get_cur(msgmap), display); -+ else{ -+ set_lflag(stream, msgmap, mn_raw2m(msgmap,thrd->rawno), MN_COLL, 0); -+ set_thread_subtree(stream, thrd, msgmap, 0, MN_CHID); -+ } -+ } -+ else{ -+ if (collapsed && special -+ && ((F_OFF(F_ENHANCED_THREAD, state) && !thrd->next) -+ || F_ON(F_ENHANCED_THREAD, state))){ -+ if (thrd->toploose) -+ if (thrd->rawno != thrd->toploose) -+ set_lflag(stream, msgmap, mn_raw2m(msgmap,thrd->rawno),MN_CHID, 0); -+ else -+ set_lflag(stream, msgmap, mn_raw2m(msgmap,thrd->rawno),MN_COLL, 0); -+ } -+ else{ -+ rv = 0; -+ if (display) -+ q_status_message(SM_ORDER, 0, 1, "Thread already expanded"); -+ } -+ } -+ return rv; -+ } -+ -+ void -+ expand_thread(state, stream, msgmap, display) -+ struct pine *state; -+ MAILSTREAM *stream; -+ MSGNO_S *msgmap; -+ int display; -+ { -+ int collapsed, rv = 1, done = 0; -+ PINETHRD_S *thrd = NULL; -+ unsigned long orig, orig_top, top; -+ -+ if(!stream) -+ return; -+ -+ orig = mn_m2raw(msgmap, mn_get_cur(msgmap)); -+ top = orig_top = mn_m2raw(msgmap, mn_get_cur(msgmap)); -+ -+ if(top) -+ thrd = fetch_thread(stream, top); -+ -+ if(!thrd) -+ return; -+ -+ while (!done){ -+ expand_this_thread(state, stream, msgmap, display, 1); -+ if (F_OFF(F_ENHANCED_THREAD, state) -+ || (move_next_this_thread(state, stream, msgmap, 0) <= 0) -+ || !(top = mn_m2raw(msgmap, mn_get_cur(msgmap))) -+ || (orig_top != top_thread(stream, top))) -+ done++; -+ } -+ mn_set_cur(msgmap,mn_raw2m(msgmap, orig_top)); -+ } -+ -+ -+ void -+ cmd_undelete_this_thread(state, stream, msgmap) -+ struct pine *state; -+ MAILSTREAM *stream; -+ MSGNO_S *msgmap; -+ { -+ unsigned long rawno; -+ int save_kolapsed; -+ -+ rawno = mn_m2raw(msgmap, mn_get_cur(msgmap)); -+ save_kolapsed = this_thread_is_kolapsed(state, stream, msgmap, rawno); -+ collapse_this_thread(state, stream, msgmap, 0, 0); -+ thread_command(state, stream, msgmap, 'u', -FOOTER_ROWS(state), 1); -+ if (!save_kolapsed) -+ expand_this_thread(state, stream, msgmap, 0, 0); -+ } -+ -+ void -+ cmd_undelete_thread(state, stream, msgmap) -+ struct pine *state; -+ MAILSTREAM *stream; -+ MSGNO_S *msgmap; -+ { -+ PINETHRD_S *thrd = NULL; -+ unsigned long rawno, top, orig_top; -+ int done = 0, count; -+ -+ rawno = mn_m2raw(msgmap, mn_get_cur(msgmap)); -+ move_top_thread(stream, msgmap, rawno); -+ top = orig_top = mn_m2raw(msgmap, mn_get_cur(msgmap)); -+ if(top) -+ thrd = fetch_thread(stream, top); -+ -+ if(!thrd) -+ return; -+ -+ while (!done){ -+ cmd_undelete_this_thread(state, stream, msgmap); -+ if (F_OFF(F_ENHANCED_THREAD, state) -+ || (move_next_this_thread(state, stream, msgmap, 0) <= 0) -+ || !(top = mn_m2raw(msgmap, mn_get_cur(msgmap))) -+ || (orig_top != top_thread(stream, top))) -+ done++; -+ } -+ mn_set_cur(msgmap,mn_raw2m(msgmap, rawno)); -+ count = count_thread(state, stream, msgmap, rawno); -+ q_status_message2(SM_ORDER, 0, 1, "Deletion mark removed from %s message%s", -+ int2string(count), plural(count)); -+ } -+ -+ void -+ kolapse_thread(state, stream, msgmap, ch, display) -+ struct pine *state; -+ MAILSTREAM *stream; -+ MSGNO_S *msgmap; -+ char ch; -+ int display; -+ { -+ PINETHRD_S *thrd = NULL; -+ unsigned long rawno; -+ int rv = 1, done = 0; -+ -+ if(!stream) -+ return; -+ -+ rawno = mn_m2raw(msgmap, mn_get_cur(msgmap)); -+ if(rawno) -+ thrd = fetch_thread(stream, rawno); -+ -+ if(!thrd) -+ return; -+ -+ clear_index_cache(stream, 0); -+ mn_set_cur(msgmap,1); /* go to the first message */ -+ while (!done){ -+ if (ch == '[') -+ collapse_thread(state, stream, msgmap, display); -+ else -+ expand_thread(state, stream, msgmap, display); -+ if ((rv = move_next_thread(state, stream, msgmap, 0)) <= 0) -+ done++; -+ } -+ -+ if (rv < 0){ -+ if (display) -+ q_status_message(SM_ORDER, 0, 1, (ch == '[') -+ ? "Error while collapsing thread" -+ : "Error while expanding thread"); -+ } -+ else -+ if(display) -+ q_status_message(SM_ORDER, 0, 1, (ch == '[') -+ ? "All threads collapsed. Use \"}\" to expand them" -+ : "All threads expanded. Use \"{\" to collapse them"); -+ -+ mn_set_cur(msgmap,mn_raw2m(msgmap, top_thread(stream,rawno))); -+ } -+ -+ void -+ cmd_select_thread(state, stream, msgmap) -+ struct pine *state; -+ MAILSTREAM *stream; -+ MSGNO_S *msgmap; -+ { -+ unsigned long rawno; -+ int save_kolapsed; -+ -+ rawno = mn_m2raw(msgmap, mn_get_cur(msgmap)); -+ save_kolapsed = thread_is_kolapsed(state, stream, msgmap, rawno); -+ collapse_thread(state, stream, msgmap, 0); -+ thread_command(state, stream, msgmap, ':', -FOOTER_ROWS(state), 1); -+ if (!save_kolapsed) -+ expand_thread(state, stream, msgmap, 0); -+ } -+ -diff -rc alpine-2.10/alpine/mailcmd.h alpine-2.10.fancy/alpine/mailcmd.h -*** alpine-2.10/alpine/mailcmd.h 2013-01-11 11:19:33.000000000 -0700 ---- alpine-2.10.fancy/alpine/mailcmd.h 2013-01-11 20:43:05.000000000 -0700 -*************** -*** 84,90 **** - int ask_mailbox_reopen(struct pine *, int *); - void visit_folder(struct pine *, char *, CONTEXT_S *, MAILSTREAM *, unsigned long); - int select_by_current(struct pine *, MSGNO_S *, CmdWhere); -! int apply_command(struct pine *, MAILSTREAM *, MSGNO_S *, UCS, int, int); - char **choose_list_of_keywords(void); - char *choose_a_charset(int); - char **choose_list_of_charsets(void); ---- 84,90 ---- - int ask_mailbox_reopen(struct pine *, int *); - void visit_folder(struct pine *, char *, CONTEXT_S *, MAILSTREAM *, unsigned long); - int select_by_current(struct pine *, MSGNO_S *, CmdWhere); -! int apply_command(struct pine *, MAILSTREAM *, MSGNO_S *, UCS, int, int, int); - char **choose_list_of_keywords(void); - char *choose_a_charset(int); - char **choose_list_of_charsets(void); -*************** -*** 102,107 **** - int flag_callback(int, long); - MPopup *flag_submenu(MESSAGECACHE *); - #endif -! - - #endif /* PINE_MAILCMD_INCLUDED */ ---- 102,116 ---- - int flag_callback(int, long); - MPopup *flag_submenu(MESSAGECACHE *); - #endif -! void cmd_delete_thread(struct pine *, MAILSTREAM *, MSGNO_S *); -! void cmd_delete_this_thread(struct pine *, MAILSTREAM *, MSGNO_S *); -! void cmd_undelete_this_thread(struct pine *, MAILSTREAM *, MSGNO_S *); -! void cmd_undelete_thread(struct pine *, MAILSTREAM *, MSGNO_S *); -! void cmd_select_thread(struct pine *, MAILSTREAM *, MSGNO_S *); -! void kolapse_thread(struct pine *, MAILSTREAM *, MSGNO_S *, char, int); -! void collapse_thread(struct pine *, MAILSTREAM *, MSGNO_S *, int); -! void expand_thread(struct pine *, MAILSTREAM *, MSGNO_S *, int); -! int collapse_this_thread(struct pine *, MAILSTREAM *, MSGNO_S *, int, int); -! int expand_this_thread(struct pine *, MAILSTREAM *, MSGNO_S *, int, int); - - #endif /* PINE_MAILCMD_INCLUDED */ -diff -rc alpine-2.10/alpine/mailindx.c alpine-2.10.fancy/alpine/mailindx.c -*** alpine-2.10/alpine/mailindx.c 2013-01-11 15:21:34.000000000 -0700 ---- alpine-2.10.fancy/alpine/mailindx.c 2013-01-11 20:43:05.000000000 -0700 -*************** -*** 561,566 **** ---- 561,567 ---- - - /*---------- Scroll line up ----------*/ - case MC_CHARUP : -+ previtem: - (void) process_cmd(state, stream, msgmap, MC_PREVITEM, - (style == MsgIndex - || style == MultiMsgIndex -*************** -*** 578,583 **** ---- 579,585 ---- - - /*---------- Scroll line down ----------*/ - case MC_CHARDOWN : -+ nextitem: - /* - * Special Page framing handling here. If we - * did something that should scroll-by-a-line, frame -*************** -*** 795,800 **** ---- 797,803 ---- - - - case MC_THRDINDX : -+ mc_thrdindx: - msgmap->top = msgmap->top_after_thrd; - if(unview_thread(state, stream, msgmap)){ - state->next_screen = mail_index_screen; -*************** -*** 845,851 **** - && mp.col == id.plus_col - && style != ThreadIndex){ - collapse_or_expand(state, stream, msgmap, -! mn_get_cur(msgmap)); - } - else if (mp.doubleclick){ - if(mp.button == M_BUTTON_LEFT){ ---- 848,854 ---- - && mp.col == id.plus_col - && style != ThreadIndex){ - collapse_or_expand(state, stream, msgmap, -! mn_get_cur(msgmap), 1); - } - else if (mp.doubleclick){ - if(mp.button == M_BUTTON_LEFT){ -*************** -*** 954,962 **** - - - case MC_COLLAPSE : -! thread_command(state, stream, msgmap, ch, -FOOTER_ROWS(state)); - break; - - case MC_DELETE : - case MC_UNDELETE : - case MC_REPLY : ---- 957,1061 ---- - - - case MC_COLLAPSE : -! thread_command(state, stream, msgmap, ch, -FOOTER_ROWS(state), 1); - break; - -+ case MC_CTHREAD : -+ if (SEP_THRDINDX()) -+ goto mc_thrdindx; -+ else -+ if (THREADING()){ -+ if (any_messages(ps_global->msgmap, NULL, -+ "to collapse a thread")) -+ collapse_thread(state, stream,msgmap, 1); -+ } -+ else -+ q_status_message(SM_ORDER, 0, 1, -+ "Command available in threaded mode only"); -+ break; -+ -+ case MC_OTHREAD : -+ if (SEP_THRDINDX()) -+ goto view_a_thread; -+ else -+ if (THREADING()){ -+ if (any_messages(ps_global->msgmap, NULL, "to expand a thread")) -+ expand_thread(state, stream,msgmap, 1); -+ } -+ else -+ q_status_message(SM_ORDER, 0, 1, -+ "Command available in threaded mode only"); -+ break; -+ -+ case MC_NEXTHREAD: -+ case MC_PRETHREAD: -+ if (THRD_INDX()){ -+ if (cmd == MC_NEXTHREAD) -+ goto nextitem; -+ else -+ goto previtem; -+ } -+ else -+ if (THREADING()){ -+ if (any_messages(ps_global->msgmap, NULL, -+ "to move to other thread")) -+ move_thread(state, stream, msgmap, -+ cmd == MC_NEXTHREAD ? 1 : -1); -+ } -+ else -+ q_status_message(SM_ORDER, 0, 1, -+ "Command available in threaded mode only"); -+ break; -+ -+ case MC_KOLAPSE: -+ case MC_EXPTHREAD: -+ if (SEP_THRDINDX()){ -+ q_status_message(SM_ORDER, 0, 1, -+ "Command not available in this screen"); -+ } -+ else{ -+ if (THREADING()){ -+ if (any_messages(ps_global->msgmap, NULL, -+ cmd == MC_KOLAPSE ? "to collapse" : "to expand")) -+ kolapse_thread(state, stream, msgmap, -+ (cmd == MC_KOLAPSE) ? '[' : ']', 1); -+ } -+ else -+ q_status_message(SM_ORDER, 0, 1, -+ "Command available in threaded mode only"); -+ } -+ break; -+ -+ case MC_DELTHREAD: -+ if (THREADING()){ -+ if (any_messages(ps_global->msgmap, NULL, "to delete")) -+ cmd_delete_thread(state, stream, msgmap); -+ } -+ else -+ q_status_message(SM_ORDER, 0, 1, -+ "Command available in threaded mode only"); -+ break; -+ -+ case MC_UNDTHREAD: -+ if (THREADING()){ -+ if (any_messages(ps_global->msgmap, NULL, "to undelete")) -+ cmd_undelete_thread(state, stream, msgmap); -+ } -+ else -+ q_status_message(SM_ORDER, 0, 1, -+ "Command available in threaded mode only"); -+ break; -+ -+ case MC_SELTHREAD: -+ if (THREADING()){ -+ if (any_messages(ps_global->msgmap, NULL, "to undelete")) -+ cmd_select_thread(state, stream, msgmap); -+ } -+ else -+ q_status_message(SM_ORDER, 0, 1, -+ "Command available in threaded mode only"); -+ break; -+ - case MC_DELETE : - case MC_UNDELETE : - case MC_REPLY : -*************** -*** 977,989 **** - if(rawno) - thrd = fetch_thread(stream, rawno); - -! collapsed = thrd && thrd->next -! && get_lflag(stream, NULL, rawno, MN_COLL); - } - - if(collapsed){ - thread_command(state, stream, msgmap, -! ch, -FOOTER_ROWS(state)); - /* increment current */ - if(cmd == MC_DELETE){ - advance_cur_after_delete(state, stream, msgmap, ---- 1076,1087 ---- - if(rawno) - thrd = fetch_thread(stream, rawno); - -! collapsed = thread_is_kolapsed(ps_global, stream, msgmap, rawno); - } - - if(collapsed){ - thread_command(state, stream, msgmap, -! ch, -FOOTER_ROWS(state),1); - /* increment current */ - if(cmd == MC_DELETE){ - advance_cur_after_delete(state, stream, msgmap, -*************** -*** 2675,2680 **** ---- 2773,2779 ---- - n = mn_raw2m(msgs, thrd->rawno); - - while(thrd){ -+ unsigned long branch; - if(!msgline_hidden(stream, msgs, n, 0) - && (++m % lines_per_page) == 1L) - t = n; -*************** -*** 2743,2753 **** - - /* n is the end of this thread */ - while(thrd){ - n = mn_raw2m(msgs, thrd->rawno); -! if(thrd->branch) -! thrd = fetch_thread(stream, thrd->branch); -! else if(thrd->next) -! thrd = fetch_thread(stream, thrd->next); - else - thrd = NULL; - } ---- 2842,2853 ---- - - /* n is the end of this thread */ - while(thrd){ -+ unsigned long next = 0L, branch = 0L; - n = mn_raw2m(msgs, thrd->rawno); -! if(branch = get_branch(stream,thrd)) -! thrd = fetch_thread(stream, branch); -! else if(next = get_next(stream,thrd)) -! thrd = fetch_thread(stream, next); - else - thrd = NULL; - } -*************** -*** 2855,2861 **** - - void - thread_command(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap, -! UCS preloadkeystroke, int q_line) - { - PINETHRD_S *thrd = NULL; - unsigned long rawno, save_branch; ---- 2955,2961 ---- - - void - thread_command(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap, -! UCS preloadkeystroke, int q_line, int display) - { - PINETHRD_S *thrd = NULL; - unsigned long rawno, save_branch; -*************** -*** 2904,2910 **** - cancel_busy_cue(0); - - (void ) apply_command(state, stream, msgmap, preloadkeystroke, flags, -! q_line); - - /* restore the original flags */ - copy_lflags(stream, msgmap, MN_STMP, MN_SLCT); ---- 3004,3010 ---- - cancel_busy_cue(0); - - (void ) apply_command(state, stream, msgmap, preloadkeystroke, flags, -! q_line, display); - - /* restore the original flags */ - copy_lflags(stream, msgmap, MN_STMP, MN_SLCT); -*************** -*** 3398,3404 **** - if(set){ - sort_folder(ps_global->mail_stream, ps_global->msgmap, - order & 0x000000ff, -! (order & 0x00000100) != 0, SRT_VRB); - mswin_beginupdate(); - update_titlebar_message(); - update_titlebar_status(); ---- 3498,3504 ---- - if(set){ - sort_folder(ps_global->mail_stream, ps_global->msgmap, - order & 0x000000ff, -! (order & 0x00000100) != 0, SRT_VRB, 1); - mswin_beginupdate(); - update_titlebar_message(); - update_titlebar_status(); -diff -rc alpine-2.10/alpine/mailindx.h alpine-2.10.fancy/alpine/mailindx.h -*** alpine-2.10/alpine/mailindx.h 2013-01-11 11:19:33.000000000 -0700 ---- alpine-2.10.fancy/alpine/mailindx.h 2013-01-11 20:43:05.000000000 -0700 -*************** -*** 103,109 **** - void paint_index_hline(MAILSTREAM *, long, ICE_S *); - void setup_index_state(int); - void warn_other_cmds(void); -! void thread_command(struct pine *, MAILSTREAM *, MSGNO_S *, UCS, int); - COLOR_PAIR *apply_rev_color(COLOR_PAIR *, int); - #ifdef _WINDOWS - int index_sort_callback(int, long); ---- 103,109 ---- - void paint_index_hline(MAILSTREAM *, long, ICE_S *); - void setup_index_state(int); - void warn_other_cmds(void); -! void thread_command(struct pine *, MAILSTREAM *, MSGNO_S *, UCS, int, int); - COLOR_PAIR *apply_rev_color(COLOR_PAIR *, int); - #ifdef _WINDOWS - int index_sort_callback(int, long); -diff -rc alpine-2.10/alpine/mailview.c alpine-2.10.fancy/alpine/mailview.c -*** alpine-2.10/alpine/mailview.c 2013-01-11 17:42:47.000000000 -0700 ---- alpine-2.10.fancy/alpine/mailview.c 2013-01-11 20:43:05.000000000 -0700 -*************** -*** 3462,3467 **** ---- 3462,3513 ---- - print_to_printer(sparms); - break; - -+ case MC_NEXTHREAD: -+ case MC_PRETHREAD: ++ return rv; ++} ++ ++void ++expand_thread(state, stream, msgmap, display) ++ struct pine *state; ++ MAILSTREAM *stream; ++ MSGNO_S *msgmap; ++ int display; ++{ ++ int collapsed, rv = 1, done = 0; ++ PINETHRD_S *thrd = NULL; ++ unsigned long orig, orig_top, top; ++ ++ if(!stream) ++ return; ++ ++ orig = mn_m2raw(msgmap, mn_get_cur(msgmap)); ++ top = orig_top = mn_m2raw(msgmap, mn_get_cur(msgmap)); ++ ++ if(top) ++ thrd = fetch_thread(stream, top); ++ ++ if(!thrd) ++ return; ++ ++ while (!done){ ++ expand_this_thread(state, stream, msgmap, display, 1); ++ if (F_OFF(F_ENHANCED_THREAD, state) ++ || (move_next_this_thread(state, stream, msgmap, 0) <= 0) ++ || !(top = mn_m2raw(msgmap, mn_get_cur(msgmap))) ++ || (orig_top != top_thread(stream, top))) ++ done++; ++ } ++ mn_set_cur(msgmap,mn_raw2m(msgmap, orig_top)); ++} ++ ++ ++void ++cmd_undelete_this_thread(state, stream, msgmap) ++ struct pine *state; ++ MAILSTREAM *stream; ++ MSGNO_S *msgmap; ++{ ++ unsigned long rawno; ++ int save_kolapsed; ++ ++ rawno = mn_m2raw(msgmap, mn_get_cur(msgmap)); ++ save_kolapsed = this_thread_is_kolapsed(state, stream, msgmap, rawno); ++ collapse_this_thread(state, stream, msgmap, 0, 0); ++ thread_command(state, stream, msgmap, 'u', -FOOTER_ROWS(state), 1); ++ if (!save_kolapsed) ++ expand_this_thread(state, stream, msgmap, 0, 0); ++} ++ ++void ++cmd_undelete_thread(state, stream, msgmap) ++ struct pine *state; ++ MAILSTREAM *stream; ++ MSGNO_S *msgmap; ++{ ++ PINETHRD_S *thrd = NULL; ++ unsigned long rawno, top, orig_top; ++ int done = 0, count; ++ ++ rawno = mn_m2raw(msgmap, mn_get_cur(msgmap)); ++ move_top_thread(stream, msgmap, rawno); ++ top = orig_top = mn_m2raw(msgmap, mn_get_cur(msgmap)); ++ if(top) ++ thrd = fetch_thread(stream, top); ++ ++ if(!thrd) ++ return; ++ ++ while (!done){ ++ cmd_undelete_this_thread(state, stream, msgmap); ++ if (F_OFF(F_ENHANCED_THREAD, state) ++ || (move_next_this_thread(state, stream, msgmap, 0) <= 0) ++ || !(top = mn_m2raw(msgmap, mn_get_cur(msgmap))) ++ || (orig_top != top_thread(stream, top))) ++ done++; ++ } ++ mn_set_cur(msgmap,mn_raw2m(msgmap, rawno)); ++ count = count_thread(state, stream, msgmap, rawno); ++ q_status_message2(SM_ORDER, 0, 1, "Deletion mark removed from %s message%s", ++ int2string(count), plural(count)); ++} ++ ++void ++kolapse_thread(state, stream, msgmap, ch, display) ++ struct pine *state; ++ MAILSTREAM *stream; ++ MSGNO_S *msgmap; ++ char ch; ++ int display; ++{ ++ PINETHRD_S *thrd = NULL; ++ unsigned long rawno; ++ int rv = 1, done = 0; ++ ++ if(!stream) ++ return; ++ ++ rawno = mn_m2raw(msgmap, mn_get_cur(msgmap)); ++ if(rawno) ++ thrd = fetch_thread(stream, rawno); ++ ++ if(!thrd) ++ return; ++ ++ clear_index_cache(stream, 0); ++ mn_set_cur(msgmap,1); /* go to the first message */ ++ while (!done){ ++ if (ch == '[') ++ collapse_thread(state, stream, msgmap, display); ++ else ++ expand_thread(state, stream, msgmap, display); ++ if ((rv = move_next_thread(state, stream, msgmap, 0)) <= 0) ++ done++; ++ } ++ ++ if (rv < 0){ ++ if (display) ++ q_status_message(SM_ORDER, 0, 1, (ch == '[') ++ ? "Error while collapsing thread" ++ : "Error while expanding thread"); ++ } ++ else ++ if(display) ++ q_status_message(SM_ORDER, 0, 1, (ch == '[') ++ ? "All threads collapsed. Use \"}\" to expand them" ++ : "All threads expanded. Use \"{\" to collapse them"); ++ ++ mn_set_cur(msgmap,mn_raw2m(msgmap, top_thread(stream,rawno))); ++} ++ ++void ++cmd_select_thread(state, stream, msgmap) ++ struct pine *state; ++ MAILSTREAM *stream; ++ MSGNO_S *msgmap; ++{ ++ unsigned long rawno; ++ int save_kolapsed; ++ ++ rawno = mn_m2raw(msgmap, mn_get_cur(msgmap)); ++ save_kolapsed = thread_is_kolapsed(state, stream, msgmap, rawno); ++ collapse_thread(state, stream, msgmap, 0); ++ thread_command(state, stream, msgmap, ':', -FOOTER_ROWS(state), 1); ++ if (!save_kolapsed) ++ expand_thread(state, stream, msgmap, 0); ++} ++ +Index: alpine-2.11/alpine/mailcmd.h +=================================================================== +--- alpine-2.11.orig/alpine/mailcmd.h ++++ alpine-2.11/alpine/mailcmd.h +@@ -84,7 +84,7 @@ char *broach_folder(int, int, int *, + int ask_mailbox_reopen(struct pine *, int *); + void visit_folder(struct pine *, char *, CONTEXT_S *, MAILSTREAM *, unsigned long); + int select_by_current(struct pine *, MSGNO_S *, CmdWhere); +-int apply_command(struct pine *, MAILSTREAM *, MSGNO_S *, UCS, int, int); ++int apply_command(struct pine *, MAILSTREAM *, MSGNO_S *, UCS, int, int, int); + char **choose_list_of_keywords(void); + char *choose_a_charset(int); + char **choose_list_of_charsets(void); +@@ -102,6 +102,15 @@ int any_selected_callback(int, long) + int flag_callback(int, long); + MPopup *flag_submenu(MESSAGECACHE *); + #endif +- ++void cmd_delete_thread(struct pine *, MAILSTREAM *, MSGNO_S *); ++void cmd_delete_this_thread(struct pine *, MAILSTREAM *, MSGNO_S *); ++void cmd_undelete_this_thread(struct pine *, MAILSTREAM *, MSGNO_S *); ++void cmd_undelete_thread(struct pine *, MAILSTREAM *, MSGNO_S *); ++void cmd_select_thread(struct pine *, MAILSTREAM *, MSGNO_S *); ++void kolapse_thread(struct pine *, MAILSTREAM *, MSGNO_S *, char, int); ++void collapse_thread(struct pine *, MAILSTREAM *, MSGNO_S *, int); ++void expand_thread(struct pine *, MAILSTREAM *, MSGNO_S *, int); ++int collapse_this_thread(struct pine *, MAILSTREAM *, MSGNO_S *, int, int); ++int expand_this_thread(struct pine *, MAILSTREAM *, MSGNO_S *, int, int); + + #endif /* PINE_MAILCMD_INCLUDED */ +Index: alpine-2.11/alpine/mailindx.c +=================================================================== +--- alpine-2.11.orig/alpine/mailindx.c ++++ alpine-2.11/alpine/mailindx.c +@@ -561,6 +561,7 @@ index_lister(struct pine *state, CONTEXT + + /*---------- Scroll line up ----------*/ + case MC_CHARUP : ++previtem: + (void) process_cmd(state, stream, msgmap, MC_PREVITEM, + (style == MsgIndex + || style == MultiMsgIndex +@@ -578,6 +579,7 @@ index_lister(struct pine *state, CONTEXT + + /*---------- Scroll line down ----------*/ + case MC_CHARDOWN : ++nextitem: + /* + * Special Page framing handling here. If we + * did something that should scroll-by-a-line, frame +@@ -795,6 +797,7 @@ view_a_thread: + + + case MC_THRDINDX : ++mc_thrdindx: + msgmap->top = msgmap->top_after_thrd; + if(unview_thread(state, stream, msgmap)){ + state->next_screen = mail_index_screen; +@@ -845,7 +848,7 @@ view_a_thread: + && mp.col == id.plus_col + && style != ThreadIndex){ + collapse_or_expand(state, stream, msgmap, +- mn_get_cur(msgmap)); ++ mn_get_cur(msgmap), 1); + } + else if (mp.doubleclick){ + if(mp.button == M_BUTTON_LEFT){ +@@ -954,9 +957,105 @@ view_a_thread: + + + case MC_COLLAPSE : +- thread_command(state, stream, msgmap, ch, -FOOTER_ROWS(state)); ++ thread_command(state, stream, msgmap, ch, -FOOTER_ROWS(state), 1); + break; + ++ case MC_CTHREAD : ++ if (SEP_THRDINDX()) ++ goto mc_thrdindx; ++ else ++ if (THREADING()){ ++ if (any_messages(ps_global->msgmap, NULL, ++ "to collapse a thread")) ++ collapse_thread(state, stream,msgmap, 1); ++ } ++ else ++ q_status_message(SM_ORDER, 0, 1, ++ "Command available in threaded mode only"); ++ break; ++ ++ case MC_OTHREAD : ++ if (SEP_THRDINDX()) ++ goto view_a_thread; ++ else ++ if (THREADING()){ ++ if (any_messages(ps_global->msgmap, NULL, "to expand a thread")) ++ expand_thread(state, stream,msgmap, 1); ++ } ++ else ++ q_status_message(SM_ORDER, 0, 1, ++ "Command available in threaded mode only"); ++ break; ++ ++ case MC_NEXTHREAD: ++ case MC_PRETHREAD: ++ if (THRD_INDX()){ ++ if (cmd == MC_NEXTHREAD) ++ goto nextitem; ++ else ++ goto previtem; ++ } ++ else ++ if (THREADING()){ ++ if (any_messages(ps_global->msgmap, NULL, ++ "to move to other thread")) ++ move_thread(state, stream, msgmap, ++ cmd == MC_NEXTHREAD ? 1 : -1); ++ } ++ else ++ q_status_message(SM_ORDER, 0, 1, ++ "Command available in threaded mode only"); ++ break; ++ ++ case MC_KOLAPSE: ++ case MC_EXPTHREAD: ++ if (SEP_THRDINDX()){ ++ q_status_message(SM_ORDER, 0, 1, ++ "Command not available in this screen"); ++ } ++ else{ + if (THREADING()){ + if (any_messages(ps_global->msgmap, NULL, -+ "to move to other thread")) -+ move_thread(ps_global, ps_global->mail_stream, ps_global->msgmap, -+ cmd == MC_NEXTHREAD ? 1 : -1); -+ done = 1; ++ cmd == MC_KOLAPSE ? "to collapse" : "to expand")) ++ kolapse_thread(state, stream, msgmap, ++ (cmd == MC_KOLAPSE) ? '[' : ']', 1); + } + else -+ q_status_message(SM_ORDER, 0, 1, -+ "Command available in threaded mode only"); -+ break; -+ -+ case MC_DELTHREAD: -+ if (THREADING()){ -+ if (any_messages(ps_global->msgmap, NULL, "to delete")) -+ cmd_delete_thread(ps_global, ps_global->mail_stream, ps_global->msgmap); -+ done = 1; -+ } -+ else -+ q_status_message(SM_ORDER, 0, 1, -+ "Command available in threaded mode only"); -+ break; -+ -+ case MC_UNDTHREAD: -+ if (THREADING()){ -+ if (any_messages(ps_global->msgmap, NULL, "to undelete")) -+ cmd_undelete_thread(ps_global, ps_global->mail_stream, ps_global->msgmap); -+ done = 1; -+ } -+ else -+ q_status_message(SM_ORDER, 0, 1, -+ "Command available in threaded mode only"); -+ break; -+ -+ case MC_SELTHREAD: -+ if (THREADING()){ -+ if (any_messages(ps_global->msgmap, NULL, "to undelete")) -+ cmd_select_thread(ps_global, ps_global->mail_stream, ps_global->msgmap); -+ done = 1; -+ } -+ else -+ q_status_message(SM_ORDER, 0, 1, -+ "Command available in threaded mode only"); -+ break; - - /* ------- First handle on Line ------ */ - case MC_GOTOBOL : -diff -rc alpine-2.10/alpine/roleconf.c alpine-2.10.fancy/alpine/roleconf.c -*** alpine-2.10/alpine/roleconf.c 2013-01-11 11:19:33.000000000 -0700 ---- alpine-2.10.fancy/alpine/roleconf.c 2013-01-11 20:43:05.000000000 -0700 -*************** -*** 4478,4488 **** - ctmp->tool = role_sort_tool; - ctmp->valoffset = rindent; - ctmp->flags |= CF_NOSELECT; -! ctmp->value = cpystr(set_choose); \ - - pval = PVAL(&sort_act_var, ew); - if(pval) -! decode_sort(pval, &def_sort, &def_sort_rev); - - /* allow user to set their default sort order */ - new_confline(&ctmp)->var = &sort_act_var; ---- 4478,4488 ---- - ctmp->tool = role_sort_tool; - ctmp->valoffset = rindent; - ctmp->flags |= CF_NOSELECT; -! ctmp->value = cpystr(set_choose); - - pval = PVAL(&sort_act_var, ew); - if(pval) -! decode_sort(pval, &def_sort, &def_sort_rev, 0); - - /* allow user to set their default sort order */ - new_confline(&ctmp)->var = &sort_act_var; -*************** -*** 4492,4498 **** - ctmp->tool = role_sort_tool; - ctmp->valoffset = rindent; - ctmp->varmem = -1; -! ctmp->value = generalized_sort_pretty_value(ps, ctmp, 0); - - for(j = 0; j < 2; j++){ - for(i = 0; ps->sort_types[i] != EndofList; i++){ ---- 4492,4498 ---- - ctmp->tool = role_sort_tool; - ctmp->valoffset = rindent; - ctmp->varmem = -1; -! ctmp->value = generalized_sort_pretty_value(ps, ctmp, 0, 0); - - for(j = 0; j < 2; j++){ - for(i = 0; ps->sort_types[i] != EndofList; i++){ -*************** -*** 4504,4510 **** - ctmp->valoffset = rindent; - ctmp->varmem = i + (j * EndofList); - ctmp->value = generalized_sort_pretty_value(ps, ctmp, -! 0); - } - } - ---- 4504,4510 ---- - ctmp->valoffset = rindent; - ctmp->varmem = i + (j * EndofList); - ctmp->value = generalized_sort_pretty_value(ps, ctmp, -! 0, 0); - } - } - -*************** -*** 5437,5443 **** - (*result)->patgrp->stat_boy = PAT_STAT_EITHER; - - if(sort_act){ -! decode_sort(sort_act, &def_sort, &def_sort_rev); - (*result)->action->sort_is_set = 1; - (*result)->action->sortorder = def_sort; - (*result)->action->revsort = (def_sort_rev ? 1 : 0); ---- 5437,5443 ---- - (*result)->patgrp->stat_boy = PAT_STAT_EITHER; - - if(sort_act){ -! decode_sort(sort_act, &def_sort, &def_sort_rev, 0); - (*result)->action->sort_is_set = 1; - (*result)->action->sortorder = def_sort; - (*result)->action->revsort = (def_sort_rev ? 1 : 0); -diff -rc alpine-2.10/alpine/setup.c alpine-2.10.fancy/alpine/setup.c -*** alpine-2.10/alpine/setup.c 2013-01-11 11:19:33.000000000 -0700 ---- alpine-2.10.fancy/alpine/setup.c 2013-01-11 20:43:05.000000000 -0700 -*************** -*** 258,264 **** - ctmpa->flags |= CF_NOSELECT; - ctmpa->value = cpystr("--- ----------------------"); - -! decode_sort(pval, &def_sort, &def_sort_rev); - - for(j = 0; j < 2; j++){ - for(i = 0; ps->sort_types[i] != EndofList; i++){ ---- 258,264 ---- - ctmpa->flags |= CF_NOSELECT; - ctmpa->value = cpystr("--- ----------------------"); - -! decode_sort(pval, &def_sort, &def_sort_rev, 0); - - for(j = 0; j < 2; j++){ - for(i = 0; ps->sort_types[i] != EndofList; i++){ -*************** -*** 273,278 **** ---- 273,327 ---- - } - } - } -+ else if(vtmp == &ps->vars[V_THREAD_SORT_KEY]){ /* radio case */ -+ SortOrder thread_def_sort; -+ int thread_def_sort_rev, lv; -+ -+ ctmpa->flags |= CF_NOSELECT; -+ ctmpa->keymenu = &config_radiobutton_keymenu; -+ ctmpa->tool = NULL; -+ -+ /* put a nice delimiter before list */ -+ new_confline(&ctmpa)->var = NULL; -+ ctmpa->varnamep = ctmpb; -+ ctmpa->keymenu = &config_radiobutton_keymenu; -+ ctmpa->help = NO_HELP; -+ ctmpa->tool = radiobutton_tool; -+ ctmpa->valoffset = 12; -+ ctmpa->flags |= CF_NOSELECT; -+ ctmpa->value = cpystr("Set Thread Sort Options"); -+ -+ new_confline(&ctmpa)->var = NULL; -+ ctmpa->varnamep = ctmpb; -+ ctmpa->keymenu = &config_radiobutton_keymenu; -+ ctmpa->help = NO_HELP; -+ ctmpa->tool = radiobutton_tool; -+ ctmpa->valoffset = 12; -+ ctmpa->flags |= CF_NOSELECT; -+ ctmpa->value = cpystr("--- ----------------------"); -+ -+ /* find longest value's name */ -+ for(lv = 0, i = 0; ps->sort_types[i] != EndofList; i++) -+ if(lv < (j = strlen(sort_name(ps->sort_types[i])))) -+ lv = j; -+ -+ decode_sort(pval, &thread_def_sort, &thread_def_sort_rev, 1); -+ -+ for(j = 0; j < 2; j++){ -+ for(i = 0; ps->sort_types[i] != EndofList; i++){ -+ if (allowed_thread_key(ps->sort_types[i])){ -+ new_confline(&ctmpa)->var = vtmp; -+ ctmpa->varnamep = ctmpb; -+ ctmpa->keymenu = &config_radiobutton_keymenu; -+ ctmpa->help = config_help(vtmp - ps->vars, 0); -+ ctmpa->tool = radiobutton_tool; -+ ctmpa->valoffset = 12; -+ ctmpa->varmem = i + (j * EndofList); -+ ctmpa->value = pretty_value(ps, ctmpa); -+ } -+ } ++ q_status_message(SM_ORDER, 0, 1, ++ "Command available in threaded mode only"); ++ } ++ break; ++ ++ case MC_DELTHREAD: ++ if (THREADING()){ ++ if (any_messages(ps_global->msgmap, NULL, "to delete")) ++ cmd_delete_thread(state, stream, msgmap); ++ } ++ else ++ q_status_message(SM_ORDER, 0, 1, ++ "Command available in threaded mode only"); ++ break; ++ ++ case MC_UNDTHREAD: ++ if (THREADING()){ ++ if (any_messages(ps_global->msgmap, NULL, "to undelete")) ++ cmd_undelete_thread(state, stream, msgmap); ++ } ++ else ++ q_status_message(SM_ORDER, 0, 1, ++ "Command available in threaded mode only"); ++ break; ++ ++ case MC_SELTHREAD: ++ if (THREADING()){ ++ if (any_messages(ps_global->msgmap, NULL, "to undelete")) ++ cmd_select_thread(state, stream, msgmap); ++ } ++ else ++ q_status_message(SM_ORDER, 0, 1, ++ "Command available in threaded mode only"); ++ break; ++ + case MC_DELETE : + case MC_UNDELETE : + case MC_REPLY : +@@ -977,13 +1076,12 @@ view_a_thread: + if(rawno) + thrd = fetch_thread(stream, rawno); + +- collapsed = thrd && thrd->next +- && get_lflag(stream, NULL, rawno, MN_COLL); ++ collapsed = thread_is_kolapsed(ps_global, stream, msgmap, rawno); + } + + if(collapsed){ + thread_command(state, stream, msgmap, +- ch, -FOOTER_ROWS(state)); ++ ch, -FOOTER_ROWS(state),1); + /* increment current */ + if(cmd == MC_DELETE){ + advance_cur_after_delete(state, stream, msgmap, +@@ -2675,6 +2773,7 @@ top_ent_calc(MAILSTREAM *stream, MSGNO_S + n = mn_raw2m(msgs, thrd->rawno); + + while(thrd){ ++ unsigned long branch; + if(!msgline_hidden(stream, msgs, n, 0) + && (++m % lines_per_page) == 1L) + t = n; +@@ -2743,11 +2842,12 @@ top_ent_calc(MAILSTREAM *stream, MSGNO_S + + /* n is the end of this thread */ + while(thrd){ ++ unsigned long next = 0L, branch = 0L; + n = mn_raw2m(msgs, thrd->rawno); +- if(thrd->branch) +- thrd = fetch_thread(stream, thrd->branch); +- else if(thrd->next) +- thrd = fetch_thread(stream, thrd->next); ++ if(branch = get_branch(stream,thrd)) ++ thrd = fetch_thread(stream, branch); ++ else if(next = get_next(stream,thrd)) ++ thrd = fetch_thread(stream, next); + else + thrd = NULL; + } +@@ -2855,7 +2955,7 @@ warn_other_cmds(void) + + void + thread_command(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap, +- UCS preloadkeystroke, int q_line) ++ UCS preloadkeystroke, int q_line, int display) + { + PINETHRD_S *thrd = NULL; + unsigned long rawno, save_branch; +@@ -2904,7 +3004,7 @@ thread_command(struct pine *state, MAILS + cancel_busy_cue(0); + + (void ) apply_command(state, stream, msgmap, preloadkeystroke, flags, +- q_line); ++ q_line, display); + + /* restore the original flags */ + copy_lflags(stream, msgmap, MN_STMP, MN_SLCT); +@@ -3398,7 +3498,7 @@ index_sort_callback(set, order) + if(set){ + sort_folder(ps_global->mail_stream, ps_global->msgmap, + order & 0x000000ff, +- (order & 0x00000100) != 0, SRT_VRB); ++ (order & 0x00000100) != 0, SRT_VRB, 1); + mswin_beginupdate(); + update_titlebar_message(); + update_titlebar_status(); +Index: alpine-2.11/alpine/mailindx.h +=================================================================== +--- alpine-2.11.orig/alpine/mailindx.h ++++ alpine-2.11/alpine/mailindx.h +@@ -103,7 +103,7 @@ int truncate_subj_and_from_strings(voi + void paint_index_hline(MAILSTREAM *, long, ICE_S *); + void setup_index_state(int); + void warn_other_cmds(void); +-void thread_command(struct pine *, MAILSTREAM *, MSGNO_S *, UCS, int); ++void thread_command(struct pine *, MAILSTREAM *, MSGNO_S *, UCS, int, int); + COLOR_PAIR *apply_rev_color(COLOR_PAIR *, int); + #ifdef _WINDOWS + int index_sort_callback(int, long); +Index: alpine-2.11/alpine/mailview.c +=================================================================== +--- alpine-2.11.orig/alpine/mailview.c ++++ alpine-2.11/alpine/mailview.c +@@ -3364,6 +3364,52 @@ scrolltool(SCROLL_S *sparms) + print_to_printer(sparms); + break; + ++ case MC_NEXTHREAD: ++ case MC_PRETHREAD: ++ if (THREADING()){ ++ if (any_messages(ps_global->msgmap, NULL, ++ "to move to other thread")) ++ move_thread(ps_global, ps_global->mail_stream, ps_global->msgmap, ++ cmd == MC_NEXTHREAD ? 1 : -1); ++ done = 1; + } -+ } - else if(vtmp == &ps->vars[V_USE_ONLY_DOMAIN_NAME]){ /* yesno case */ - ctmpa->keymenu = &config_yesno_keymenu; - ctmpa->tool = yesno_tool; -*************** -*** 464,469 **** ---- 513,527 ---- - } - } - -+ pval = PVAL(&ps->vars[V_THREAD_SORT_KEY], ew); -+ if(vsave[V_THREAD_SORT_KEY].saved_user_val.p && pval -+ && strcmp(vsave[V_THREAD_SORT_KEY].saved_user_val.p, pval)){ -+ if(!mn_get_mansort(ps_global->msgmap)){ -+ clear_index_cache(ps_global->mail_stream, 0); -+ reset_sort_order(SRT_VRB); -+ } -+ } -+ - treat_color_vars_as_text = 0; - free_saved_config(ps, &vsave, expose_hidden_config); - #ifdef _WINDOWS -diff -rc alpine-2.10/pith/conf.c alpine-2.10.fancy/pith/conf.c -*** alpine-2.10/pith/conf.c 2013-01-11 19:45:41.000000000 -0700 ---- alpine-2.10.fancy/pith/conf.c 2013-01-11 20:43:05.000000000 -0700 -*************** -*** 204,209 **** ---- 204,211 ---- - - CONF_TXT_T cf_text_sort_key[] = "Sets presentation order of messages in Index. Choices:\n# Subject, From, Arrival, Date, Size, To, Cc, OrderedSubj, Score, and Thread.\n# Order may be reversed by appending /Reverse. Default: \"Arrival\"."; - -+ CONF_TXT_T cf_text_thread_sort_key[] = "#Sets presentation order of threads in thread index. Choices:\n#arrival, and thread."; -+ - CONF_TXT_T cf_text_addrbook_sort_rule[] = "Sets presentation order of address book entries. Choices: dont-sort,\n# fullname-with-lists-last, fullname, nickname-with-lists-last, nickname\n# Default: \"fullname-with-lists-last\"."; - - CONF_TXT_T cf_text_folder_sort_rule[] = "Sets presentation order of folder list entries. Choices: alphabetical,\n# alpha-with-dirs-last, alpha-with-dirs-first.\n# Default: \"alpha-with-directories-last\"."; -*************** -*** 518,523 **** ---- 520,527 ---- - NULL, cf_text_fcc_name_rule}, - {"sort-key", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, - NULL, cf_text_sort_key}, -+ {"thread-sort-key", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, -+ NULL, cf_text_thread_sort_key}, - {"addrbook-sort-rule", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, - "Address Book Sort Rule", cf_text_addrbook_sort_rule}, - {"folder-sort-rule", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, -*************** -*** 1556,1562 **** - register struct variable *vars = ps->vars; - int obs_header_in_reply = 0, /* the obs_ variables are to */ - obs_old_style_reply = 0, /* support backwards compatibility */ -! obs_save_by_sender, i, def_sort_rev; - long rvl; - PINERC_S *fixedprc = NULL; - FeatureLevel obs_feature_level; ---- 1560,1566 ---- - register struct variable *vars = ps->vars; - int obs_header_in_reply = 0, /* the obs_ variables are to */ - obs_old_style_reply = 0, /* support backwards compatibility */ -! obs_save_by_sender, i, def_sort_rev, thread_def_sort_rev; - long rvl; - PINERC_S *fixedprc = NULL; - FeatureLevel obs_feature_level; -*************** -*** 1581,1586 **** ---- 1585,1591 ---- - GLO_FEATURE_LEVEL = cpystr("sappling"); - GLO_OLD_STYLE_REPLY = cpystr(DF_OLD_STYLE_REPLY); - GLO_SORT_KEY = cpystr(DF_SORT_KEY); -+ GLO_THREAD_SORT_KEY = cpystr(DF_THREAD_SORT_KEY); - GLO_SAVED_MSG_NAME_RULE = cpystr(DF_SAVED_MSG_NAME_RULE); - GLO_FCC_RULE = cpystr(DF_FCC_RULE); - GLO_AB_SORT_RULE = cpystr(DF_AB_SORT_RULE); -*************** -*** 2484,2490 **** - set_current_val(&vars[V_ARCHIVED_FOLDERS], TRUE, TRUE); - set_current_val(&vars[V_INCOMING_FOLDERS], TRUE, TRUE); - set_current_val(&vars[V_SORT_KEY], TRUE, TRUE); -! if(decode_sort(VAR_SORT_KEY, &ps->def_sort, &def_sort_rev) == -1){ - snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Sort type \"%.200s\" is invalid", VAR_SORT_KEY); - init_error(ps, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf); - ps->def_sort = SortArrival; ---- 2489,2495 ---- - set_current_val(&vars[V_ARCHIVED_FOLDERS], TRUE, TRUE); - set_current_val(&vars[V_INCOMING_FOLDERS], TRUE, TRUE); - set_current_val(&vars[V_SORT_KEY], TRUE, TRUE); -! if(decode_sort(VAR_SORT_KEY, &ps->def_sort, &def_sort_rev,0) == -1){ - snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Sort type \"%.200s\" is invalid", VAR_SORT_KEY); - init_error(ps, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf); - ps->def_sort = SortArrival; -*************** -*** 2493,2498 **** ---- 2498,2514 ---- - else - ps->def_sort_rev = def_sort_rev; - -+ set_current_val(&vars[V_THREAD_SORT_KEY], TRUE, TRUE); -+ if(decode_sort(VAR_THREAD_SORT_KEY, &ps->thread_def_sort, -+ &thread_def_sort_rev, 1) == -1){ -+ sprintf(tmp_20k_buf, "Sort type \"%s\" is invalid", VAR_THREAD_SORT_KEY); -+ init_error(ps, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf); -+ ps->thread_def_sort = SortThread; -+ ps->thread_def_sort_rev = 0; -+ } -+ else -+ ps->thread_def_sort_rev = thread_def_sort_rev; -+ - cur_rule_value(&vars[V_SAVED_MSG_NAME_RULE], TRUE, TRUE); - {NAMEVAL_S *v; int i; - for(i = 0; (v = save_msg_rules(i)); i++) -*************** -*** 2914,2919 **** ---- 2930,2937 ---- - F_COLOR_LINE_IMPORTANT, h_config_color_thrd_import, PREF_INDX, 0}, - {"thread-sorts-by-arrival", "Thread Sorts by Arrival", - F_THREAD_SORTS_BY_ARRIVAL, h_config_thread_sorts_by_arrival, PREF_INDX, 0}, -+ {"enhanced-fancy-thread-support", "Enhanced Fancy Thread Support", -+ F_ENHANCED_THREAD, h_config_enhanced_thread, PREF_INDX, 0}, - - /* Viewer prefs */ - {"enable-msg-view-addresses", "Enable Message View Address Links", -*************** -*** 7545,7550 **** ---- 7563,7570 ---- - return(h_config_fcc_rule); - case V_SORT_KEY : - return(h_config_sort_key); -+ case V_THREAD_SORT_KEY : -+ return(h_config_thread_sort_key); - case V_AB_SORT_RULE : - return(h_config_ab_sort_rule); - case V_FLD_SORT_RULE : -diff -rc alpine-2.10/pith/conf.h alpine-2.10.fancy/pith/conf.h -*** alpine-2.10/pith/conf.h 2013-01-11 11:26:44.000000000 -0700 ---- alpine-2.10.fancy/pith/conf.h 2013-01-11 20:43:05.000000000 -0700 -*************** -*** 144,149 **** ---- 144,152 ---- - #define VAR_SORT_KEY vars[V_SORT_KEY].current_val.p - #define GLO_SORT_KEY vars[V_SORT_KEY].global_val.p - #define COM_SORT_KEY vars[V_SORT_KEY].cmdline_val.p -+ #define VAR_THREAD_SORT_KEY vars[V_THREAD_SORT_KEY].current_val.p -+ #define GLO_THREAD_SORT_KEY vars[V_THREAD_SORT_KEY].global_val.p -+ #define COM_THREAD_SORT_KEY vars[V_THREAD_SORT_KEY].cmdline_val.p - #define VAR_AB_SORT_RULE vars[V_AB_SORT_RULE].current_val.p - #define GLO_AB_SORT_RULE vars[V_AB_SORT_RULE].global_val.p - #define VAR_FLD_SORT_RULE vars[V_FLD_SORT_RULE].current_val.p -diff -rc alpine-2.10/pith/conftype.h alpine-2.10.fancy/pith/conftype.h -*** alpine-2.10/pith/conftype.h 2013-01-11 19:45:41.000000000 -0700 ---- alpine-2.10.fancy/pith/conftype.h 2013-01-11 20:43:05.000000000 -0700 -*************** -*** 59,64 **** ---- 59,65 ---- - , V_SAVED_MSG_NAME_RULE - , V_FCC_RULE - , V_SORT_KEY -+ , V_THREAD_SORT_KEY - , V_AB_SORT_RULE - , V_FLD_SORT_RULE - , V_GOTO_DEFAULT_RULE -*************** -*** 499,504 **** ---- 500,506 ---- - F_QUELL_TIMEZONE, - F_QUELL_USERAGENT, - F_COLOR_LINE_IMPORTANT, -+ F_ENHANCED_THREAD, - F_SLASH_COLL_ENTIRE, - F_ENABLE_FULL_HDR_AND_TEXT, - F_QUELL_FULL_HDR_RESET, -*************** -*** 705,709 **** ---- 707,712 ---- - - /* exported protoypes */ - -+ #define DF_THREAD_SORT_KEY "thread" - - #endif /* PITH_CONFTYPE_INCLUDED */ -diff -rc alpine-2.10/pith/flag.c alpine-2.10.fancy/pith/flag.c -*** alpine-2.10/pith/flag.c 2013-01-11 11:26:43.000000000 -0700 ---- alpine-2.10.fancy/pith/flag.c 2013-01-11 20:43:05.000000000 -0700 -*************** -*** 594,607 **** - - was_invisible = (pelt->hidden || pelt->colhid) ? 1 : 0; - - if((chk_thrd_cnt = ((msgs->visible_threads >= 0L) - && THRD_INDX_ENABLED() && (f & MN_HIDE) && (pelt->hidden != v))) != 0){ - thrd = fetch_thread(stream, rawno); - if(thrd && thrd->top){ -! if(thrd->top == thrd->rawno) - topthrd = thrd; - else -! topthrd = fetch_thread(stream, thrd->top); - } - - if(topthrd){ ---- 594,609 ---- - - was_invisible = (pelt->hidden || pelt->colhid) ? 1 : 0; - -+ thrd = fetch_thread(stream, rawno); -+ - if((chk_thrd_cnt = ((msgs->visible_threads >= 0L) - && THRD_INDX_ENABLED() && (f & MN_HIDE) && (pelt->hidden != v))) != 0){ - thrd = fetch_thread(stream, rawno); - if(thrd && thrd->top){ -! if(top_thread(stream, thrd->top) == thrd->rawno) - topthrd = thrd; - else -! topthrd = fetch_thread(stream, top_thread(stream, thrd->top)); - } - - if(topthrd){ -diff -rc alpine-2.10/pith/indxtype.h alpine-2.10.fancy/pith/indxtype.h -*** alpine-2.10/pith/indxtype.h 2013-01-11 11:26:44.000000000 -0700 ---- alpine-2.10.fancy/pith/indxtype.h 2013-01-11 20:43:05.000000000 -0700 -*************** -*** 76,82 **** - iKey, iKeyInit, - iPrefDate, iPrefTime, iPrefDateTime, - iCurPrefDate, iCurPrefTime, iCurPrefDateTime, -! iSize, iSizeComma, iSizeNarrow, iDescripSize, - iNewsAndTo, iToAndNews, iNewsAndRecips, iRecipsAndNews, - iFromTo, iFromToNotNews, iFrom, iTo, iSender, iCc, iNews, iRecips, - iCurNews, iArrow, ---- 76,82 ---- - iKey, iKeyInit, - iPrefDate, iPrefTime, iPrefDateTime, - iCurPrefDate, iCurPrefTime, iCurPrefDateTime, -! iSize, iSizeComma, iSizeNarrow, iDescripSize, iSizeThread, - iNewsAndTo, iToAndNews, iNewsAndRecips, iRecipsAndNews, - iFromTo, iFromToNotNews, iFrom, iTo, iSender, iCc, iNews, iRecips, - iCurNews, iArrow, -diff -rc alpine-2.10/pith/mailindx.c alpine-2.10.fancy/pith/mailindx.c -*** alpine-2.10/pith/mailindx.c 2013-01-11 15:21:34.000000000 -0700 ---- alpine-2.10.fancy/pith/mailindx.c 2013-01-11 20:43:05.000000000 -0700 -*************** -*** 228,233 **** ---- 228,234 ---- - case iSTime: - case iKSize: - case iSize: -+ case iSizeThread: - case iPrioAlpha: - (*answer)[column].req_width = 7; - break; -*************** -*** 452,457 **** ---- 453,459 ---- - {"FROMORTONOTNEWS", iFromToNotNews, FOR_INDEX}, - {"SIZE", iSize, FOR_INDEX}, - {"SIZECOMMA", iSizeComma, FOR_INDEX}, -+ {"SIZETHREAD", iSizeThread, FOR_INDEX}, - {"SIZENARROW", iSizeNarrow, FOR_INDEX}, - {"KSIZE", iKSize, FOR_INDEX}, - {"SUBJECT", iSubject, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, -*************** -*** 932,938 **** - iSDateTimeS1, iSDateTimeS2, iSDateTimeS3, iSDateTimeS4, - iSDateTimeIso24, iSDateTimeIsoS24, - iSDateTimeS124, iSDateTimeS224, iSDateTimeS324, iSDateTimeS424, -! iSize, iSizeComma, iSizeNarrow, iKSize, iDescripSize, - iPrio, iPrioBang, iPrioAlpha, iInit, - iAtt, iTime24, iTime12, iTimezone, iMonAbb, iYear, iYear2Digit, - iDay2Digit, iMon2Digit, iDayOfWeekAbb, iScore, iMonLong, iDayOfWeek ---- 934,940 ---- - iSDateTimeS1, iSDateTimeS2, iSDateTimeS3, iSDateTimeS4, - iSDateTimeIso24, iSDateTimeIsoS24, - iSDateTimeS124, iSDateTimeS224, iSDateTimeS324, iSDateTimeS424, -! iSize, iSizeComma, iSizeNarrow, iKSize, iDescripSize, iSizeThread, - iPrio, iPrioBang, iPrioAlpha, iInit, - iAtt, iTime24, iTime12, iTimezone, iMonAbb, iYear, iYear2Digit, - iDay2Digit, iMon2Digit, iDayOfWeekAbb, iScore, iMonLong, iDayOfWeek -*************** -*** 1125,1130 **** ---- 1127,1133 ---- - case iTime12: - case iSize: - case iKSize: -+ case iSizeThread: - cdesc->actual_length = 7; - cdesc->adjustment = Right; - break; -*************** -*** 1218,1224 **** - cdesc->ctype != iNothing; - cdesc++) - if(cdesc->ctype == iSize || cdesc->ctype == iKSize || -! cdesc->ctype == iSizeNarrow || - cdesc->ctype == iSizeComma || cdesc->ctype == iDescripSize){ - if(cdesc->actual_length == 0){ - if((fix=cdesc->width) > 0){ /* had this reserved */ ---- 1221,1227 ---- - cdesc->ctype != iNothing; - cdesc++) - if(cdesc->ctype == iSize || cdesc->ctype == iKSize || -! cdesc->ctype == iSizeNarrow || cdesc->ctype == iSizeThread || - cdesc->ctype == iSizeComma || cdesc->ctype == iDescripSize){ - if(cdesc->actual_length == 0){ - if((fix=cdesc->width) > 0){ /* had this reserved */ -*************** -*** 1601,1610 **** - - /* find next thread which is visible */ - do{ - if(mn_get_revsort(msgmap) && thrd->prevthd) - thrd = fetch_thread(stream, thrd->prevthd); -! else if(!mn_get_revsort(msgmap) && thrd->nextthd) -! thrd = fetch_thread(stream, thrd->nextthd); - else - thrd = NULL; - } while(thrd ---- 1604,1615 ---- - - /* find next thread which is visible */ - do{ -+ unsigned long branch; - if(mn_get_revsort(msgmap) && thrd->prevthd) - thrd = fetch_thread(stream, thrd->prevthd); -! /*branch = get_branch(stream,thrd)*/ -! else if(!mn_get_revsort(msgmap) && thrd->branch) -! thrd = fetch_thread(stream, thrd->branch); - else - thrd = NULL; - } while(thrd -*************** -*** 2015,2027 **** - */ - ice = copy_ice(ice); - - /* is this a collapsed thread index line? */ -! if(!idata->bogus && THREADING()){ -! thrd = fetch_thread(idata->stream, idata->rawno); -! collapsed = thrd && thrd->next -! && get_lflag(idata->stream, NULL, -! idata->rawno, MN_COLL); -! } - - /* calculate contents of the required fields */ - for(cdesc = ps_global->index_disp_format; cdesc->ctype != iNothing; cdesc++) ---- 2020,2029 ---- - */ - ice = copy_ice(ice); - -+ thrd = fetch_thread(idata->stream, idata->rawno); - /* is this a collapsed thread index line? */ -! if(!idata->bogus && THREADING()) -! collapsed = thrd && thread_is_kolapsed(ps_global, idata->stream, ps_global->msgmap, idata->rawno); - - /* calculate contents of the required fields */ - for(cdesc = ps_global->index_disp_format; cdesc->ctype != iNothing; cdesc++) -*************** -*** 2519,2525 **** ---- 2521,2550 ---- - - break; - -+ case iSizeThread: -+ if (!THREADING()){ -+ goto getsize; -+ } else if (collapsed){ -+ l = count_flags_in_thread(idata->stream, thrd, F_NONE); -+ snprintf(str, sizeof(str), "(%lu)", l); -+ } -+ else{ -+ thrd = fetch_thread(idata->stream, idata->rawno); -+ if(!thrd) -+ snprintf(str, sizeof(str), "%s", "Error"); -+ else{ -+ long lengthb; -+ lengthb = get_length_branch(idata->stream, idata->rawno); -+ if (lengthb > 0L) -+ snprintf(str, sizeof(str), "(%lu)", lengthb); -+ else -+ snprintf(str,sizeof(str), "%s", " "); -+ } -+ } -+ break; -+ - case iSize: -+ getsize: - /* 0 ... 9999 */ - if((l = fetch_size(idata)) < 10*1000L) - snprintf(str, sizeof(str), "(%lu)", l); -*************** -*** 5392,5401 **** - - if(pith_opt_condense_thread_cue) - width = (*pith_opt_condense_thread_cue)(thd, ice, &str, &strsize, width, -! thd && thd->next -! && get_lflag(idata->stream, -! NULL,idata->rawno, -! MN_COLL)); - - /* - * width is < available strsize and ---- 5417,5424 ---- - - if(pith_opt_condense_thread_cue) - width = (*pith_opt_condense_thread_cue)(thd, ice, &str, &strsize, width, -! this_thread_is_kolapsed(ps_global, idata->stream, ps_global->msgmap, idata->rawno) && -! (count_thread(ps_global,idata->stream, ps_global->msgmap, idata->rawno) != 1)); - - /* - * width is < available strsize and -*************** -*** 6023,6033 **** - border = str + width; - if(pith_opt_condense_thread_cue) - width = (*pith_opt_condense_thread_cue)(thd, ice, &str, &strsize, width, -! thd && thd->next -! && get_lflag(idata->stream, -! NULL,idata->rawno, -! MN_COLL)); -! - fptr = str; - - if(thd) ---- 6046,6053 ---- - border = str + width; - if(pith_opt_condense_thread_cue) - width = (*pith_opt_condense_thread_cue)(thd, ice, &str, &strsize, width, -! this_thread_is_kolapsed(ps_global, idata->stream, ps_global->msgmap, idata->rawno) && -! (count_thread(ps_global,idata->stream, ps_global->msgmap, idata->rawno) != 1)); - fptr = str; - - if(thd) -diff -rc alpine-2.10/pith/pattern.c alpine-2.10.fancy/pith/pattern.c -*** alpine-2.10/pith/pattern.c 2013-01-11 11:26:44.000000000 -0700 ---- alpine-2.10.fancy/pith/pattern.c 2013-01-11 20:43:05.000000000 -0700 -*************** -*** 1756,1762 **** - SortOrder def_sort; - int def_sort_rev; - -! if(decode_sort(p, &def_sort, &def_sort_rev) != -1){ - action->sort_is_set = 1; - action->sortorder = def_sort; - action->revsort = (def_sort_rev ? 1 : 0); ---- 1756,1762 ---- - SortOrder def_sort; - int def_sort_rev; - -! if(decode_sort(p, &def_sort, &def_sort_rev, 0) != -1){ - action->sort_is_set = 1; - action->sortorder = def_sort; - action->revsort = (def_sort_rev ? 1 : 0); -diff -rc alpine-2.10/pith/pine.hlp alpine-2.10.fancy/pith/pine.hlp -*** alpine-2.10/pith/pine.hlp 2013-01-11 20:33:27.000000000 -0700 ---- alpine-2.10.fancy/pith/pine.hlp 2013-01-11 20:43:05.000000000 -0700 -*************** -*** 3548,3553 **** ---- 3548,3554 ---- -

  • OPTION: -
  • OPTION: -
  • OPTION: -+
  • OPTION: -
  • OPTION: -
  • OPTION: -
  • OPTION: -*************** -*** 5480,5485 **** ---- 5481,5643 ---- - <End of help on this topic> - - -+ ======= h_thread_index_sort_arrival ======= -+ -+ -+ SORT OPTION: Arrival -+ -+ -+

    SORT OPTION: Arrival

    -+ -+ The Arrival sort option arranges threads according to the last -+ time that a message was added to it. In this order the last thread -+ contains the most recent message in the folder. -+ -+

    -+ <End of help on this topic> -+ -+ -+ ======= h_thread_index_sort_date ======= -+ -+ -+ SORT OPTION: Date -+ -+ -+

    SORT OPTION: Date

    -+ -+ The Date sort option in the THREAD INDEX screen sorts -+ threads by the date in which messages were sent. The thread containing the -+ last message in this order is displayed last. -+

    -+ <End of help on this topic> -+ -+ -+ ======= h_thread_index_sort_subj ======= -+ -+ -+ SORT OPTION: Subject -+ -+ -+

    SORT OPTION: Subject

    -+ -+ The Subject sort option has not been defined yet. -+ -+

    -+ <End of help on this topic> -+ -+ -+ ======= h_thread_index_sort_ordsubj ======= -+ -+ -+ SORT OPTION: OrderedSubject -+ -+ -+

    SORT OPTION: OrderedSubject

    -+ -+ The OrderedSubject sort option in the THREAD INDEX screen is -+ the same as sorting by Subject. -+ -+

    -+ <End of help on this topic> -+ -+ -+ ======= h_thread_index_sort_thread ======= -+ -+ -+ SORT OPTION: Thread -+ -+ -+

    SORT OPTION: Thread

    -+ -+ The Thread sort option in the THREAD INDEX screen sorts all -+ messages by the proposed algorithm by Crispin and Murchison. In this -+ method of sorting once threads have been isolated they are sorted by the -+ date of their parents, or if that is missing, the first message in that -+ thread. -+ -+

    -+ <End of help on this topic> -+ -+ -+ ======= h_thread_index_sort_from ======= -+ -+ -+ SORT OPTION: From -+ -+ -+

    SORT OPTION: From

    -+ -+ The From sort option has not been defined yet. -+ -+

    -+ <End of help on this topic> -+ -+ -+ ======= h_thread_index_sort_size ======= -+ -+ -+ SORT OPTION: Size -+ -+ -+

    SORT OPTION: Size

    -+ -+ The Size sort option sorts threads by their size (the number -+ of messages in the thread). This could be used to find conversations -+ where no reply has been sent by any of the participants in the thread -+ (e.g. those whose length is equal to one). Longer threads appear -+ below shorter ones. -+ -+

    -+ <End of help on this topic> -+ -+ -+ ======= h_thread_index_sort_score ======= -+ -+ -+ SORT OPTION: Score -+ -+ -+

    SORT OPTION: Score

    -+ -+ The Score sort option means that threads are sorted according to -+ the maximum score of a message in that thread. A thread all of whose -+ messages contain a smaller score than a message in some other thread is -+ placed in an earlier place in the list of messages for that folder; that -+ is, threads with the highest scores appear at the bottom of the index -+ list. -+ -+

    -+ <End of help on this topic> -+ -+ -+ ======= h_thread_index_sort_to ======= -+ -+ -+ SORT OPTION: To -+ -+ -+

    SORT OPTION: To

    -+ -+ The To sort option has not been defined yet. -+ -+

    -+ <End of help on this topic> -+ -+ -+ ======= h_thread_index_sort_cc ======= -+ -+ -+ SORT OPTION: Cc -+ -+ -+

    SORT OPTION: Cc

    -+ -+ The Cc sort option has not been defined yet. -+ -+

    -+ <End of help on this topic> -+ -+ - ======= h_index_cmd_whereis ======= - - -*************** -*** 18846,18851 **** ---- 19004,19017 ---- -

    - - -+

    SIZETHREAD
    -+
    -+ This token represents the total size of the thread for a collapsed thread -+ or the size of the branch for an expanded thread. The field is omitted for -+ messages that are not top of threads nor branches and it defaults to -+ the SIZE token when your folders is not sorted by thread. -+
    -+ -
    SIZENARROW
    -
    - This token represents the total size, in bytes, of the message. -*************** -*** 22211,22216 **** ---- 22377,22421 ---- - <End of help on this topic> - - -+ ====== h_config_thread_sort_key ===== -+ -+ -+ OPTION: <!--#echo var="VAR_thread-sort-key--> -+ -+ -+

    OPTION:

    -+ -+ This option determines the order in which threads will be displayed. You -+ can choose from the options listed below. Each folder is sorted in one of -+ the sort orders displayed below first, then the thread containing the last -+ message of that sorted list is put at the end of the index. All messages -+ of that thread are "removed" from the sorted list and the -+ process is repeated with the remaining messages in that list. -+ -+

    -+

    -+ -+

    Each type of sort may also be reversed. Normal default is by -+ "Thread". -+ -+

    -+

    -+ <End of help on this topic> -+ -+ - ====== h_config_other_startup ===== - - -*************** -*** 29961,29966 **** ---- 30166,30188 ---- - <End of help on this topic> - - -+ ====== h_config_enhanced_thread ===== -+ -+ -+ FEATURE: <!--#echo var="FEAT_enhanced-fancy-thread-support"--> -+ -+ -+

    FEATURE:

    -+ -+ If this option is set certain commands in Pine will operate in loose -+ threads too. For example, the command ^R marks a thread deleted, but if -+ this feature is set, it will remove all threads that share the same missing -+ parent with this thread. -+ -+

    -+ <End of help on this topic> -+ -+ - ====== h_config_news_cross_deletes ===== - - -diff -rc alpine-2.10/pith/sort.c alpine-2.10.fancy/pith/sort.c -*** alpine-2.10/pith/sort.c 2013-01-11 11:26:44.000000000 -0700 ---- alpine-2.10.fancy/pith/sort.c 2013-01-11 20:43:05.000000000 -0700 -*************** -*** 91,97 **** - ----*/ - void - sort_folder(MAILSTREAM *stream, MSGNO_S *msgmap, SortOrder new_sort, -! int new_rev, unsigned int flags) - { - long raw_current, i, j; - unsigned long *sort = NULL; ---- 91,97 ---- - ----*/ - void - sort_folder(MAILSTREAM *stream, MSGNO_S *msgmap, SortOrder new_sort, -! int new_rev, unsigned int flags, int first) - { - long raw_current, i, j; - unsigned long *sort = NULL; -*************** -*** 101,106 **** ---- 101,115 ---- - int current_rev; - MESSAGECACHE *mc; - -+ if (first){ -+ if (new_sort == SortThread) -+ find_msgmap(stream, msgmap, flags, -+ ps_global->thread_cur_sort, new_rev); -+ else -+ sort_folder(stream, msgmap, new_sort, new_rev, flags, 0); -+ return; -+ } -+ - dprint((2, "Sorting by %s%s\n", - sort_name(new_sort), new_rev ? "/reverse" : "")); - -*************** -*** 530,549 **** - * argument also means arrival/reverse. - */ - int -! decode_sort(char *sort_spec, SortOrder *def_sort, int *def_sort_rev) - { - char *sep; - char *fix_this = NULL; -! int x, reverse; - - if(!sort_spec || !*sort_spec){ -! *def_sort = SortArrival; - *def_sort_rev = 0; - return(0); - } - - if(struncmp(sort_spec, "reverse", strlen(sort_spec)) == 0){ -! *def_sort = SortArrival; - *def_sort_rev = 1; - return(0); - } ---- 539,558 ---- - * argument also means arrival/reverse. - */ - int -! decode_sort(char *sort_spec, SortOrder *def_sort, int *def_sort_rev, int thread) - { - char *sep; - char *fix_this = NULL; -! int x = 0, reverse; - - if(!sort_spec || !*sort_spec){ -! *def_sort = thread ? SortThread : SortArrival; - *def_sort_rev = 0; - return(0); - } - - if(struncmp(sort_spec, "reverse", strlen(sort_spec)) == 0){ -! *def_sort = thread ? SortThread : SortArrival; - *def_sort_rev = 1; - return(0); - } -*************** -*** 572,578 **** - if(ps_global->sort_types[x] == EndofList) - return(-1); - -! *def_sort = ps_global->sort_types[x]; - *def_sort_rev = reverse; - return(0); - } ---- 581,587 ---- - if(ps_global->sort_types[x] == EndofList) - return(-1); - -! *def_sort = ps_global->sort_types[x]; - *def_sort_rev = reverse; - return(0); - } -*************** -*** 689,695 **** - - /* set default order */ - the_sort_order = ps_global->def_sort; -! sort_is_rev = ps_global->def_sort_rev; - - if(ps_global->mail_stream && nonempty_patterns(rflags, &pstate)){ - for(pat = first_pattern(&pstate); pat; pat = next_pattern(&pstate)){ ---- 698,706 ---- - - /* set default order */ - the_sort_order = ps_global->def_sort; -! sort_is_rev = the_sort_order == SortThread -! ? (ps_global->thread_def_sort_rev + ps_global->def_sort_rev) % 2 -! : ps_global->def_sort_rev; - - if(ps_global->mail_stream && nonempty_patterns(rflags, &pstate)){ - for(pat = first_pattern(&pstate); pat; pat = next_pattern(&pstate)){ -*************** -*** 702,710 **** - && pat->action->sort_is_set){ - the_sort_order = pat->action->sortorder; - sort_is_rev = pat->action->revsort; - } - } - - sort_folder(ps_global->mail_stream, ps_global->msgmap, -! the_sort_order, sort_is_rev, flags); - } ---- 713,727 ---- - && pat->action->sort_is_set){ - the_sort_order = pat->action->sortorder; - sort_is_rev = pat->action->revsort; -+ sort_is_rev = the_sort_order == SortThread -+ ? (ps_global->thread_def_sort_rev + pat->action->revsort) % 2 -+ : pat->action->revsort; - } - } - -+ if(the_sort_order == SortThread && !(flags & SRT_MAN)) -+ ps_global->thread_cur_sort = ps_global->thread_def_sort; -+ - sort_folder(ps_global->mail_stream, ps_global->msgmap, -! the_sort_order, sort_is_rev, flags, 1); - } -diff -rc alpine-2.10/pith/sort.h alpine-2.10.fancy/pith/sort.h -*** alpine-2.10/pith/sort.h 2013-01-11 11:26:44.000000000 -0700 ---- alpine-2.10.fancy/pith/sort.h 2013-01-11 20:43:05.000000000 -0700 -*************** -*** 22,28 **** - - - #define refresh_sort(S,M,F) sort_folder((S), (M), mn_get_sort(M), \ -! mn_get_revsort(M), (F)) - - struct global_sort_data { - MSGNO_S *msgmap; ---- 22,28 ---- - - - #define refresh_sort(S,M,F) sort_folder((S), (M), mn_get_sort(M), \ -! mn_get_revsort(M), (F), 1) - - struct global_sort_data { - MSGNO_S *msgmap; -*************** -*** 41,48 **** - - /* exported protoypes */ - char *sort_name(SortOrder); -! void sort_folder(MAILSTREAM *, MSGNO_S *, SortOrder, int, unsigned); -! int decode_sort(char *, SortOrder *, int *); - void reset_sort_order(unsigned); - - ---- 41,48 ---- - - /* exported protoypes */ - char *sort_name(SortOrder); -! void sort_folder(MAILSTREAM *, MSGNO_S *, SortOrder, int, unsigned, int); -! int decode_sort(char *, SortOrder *, int *, int); - void reset_sort_order(unsigned); - - -diff -rc alpine-2.10/pith/state.c alpine-2.10.fancy/pith/state.c -*** alpine-2.10/pith/state.c 2013-01-11 11:26:44.000000000 -0700 ---- alpine-2.10.fancy/pith/state.c 2013-01-11 20:43:05.000000000 -0700 -*************** -*** 74,79 **** ---- 74,80 ---- - - p = (struct pine *)fs_get(sizeof (struct pine)); - memset((void *) p, 0, sizeof(struct pine)); -+ p->thread_def_sort = SortDate; - p->def_sort = SortArrival; - p->sort_types[0] = SortSubject; - p->sort_types[1] = SortArrival; -diff -rc alpine-2.10/pith/state.h alpine-2.10.fancy/pith/state.h -*** alpine-2.10/pith/state.h 2013-01-11 19:45:41.000000000 -0700 ---- alpine-2.10.fancy/pith/state.h 2013-01-11 20:43:05.000000000 -0700 -*************** -*** 137,142 **** ---- 137,144 ---- - unsigned unseen_in_view:1; - unsigned start_in_context:1; /* start fldr_scrn in current cntxt */ - unsigned def_sort_rev:1; /* true if reverse sort is default */ -+ unsigned thread_def_sort_rev:1; /* true if reverse sort is default in thread screen */ -+ unsigned msgmap_thread_def_sort_rev:1; /* true if reverse sort is being used in thread screen */ - unsigned restricted:1; - - unsigned save_msg_rule:5; -*************** -*** 284,289 **** ---- 286,294 ---- - EditWhich ew_for_srch_take; - - SortOrder def_sort, /* Default sort type */ -+ thread_def_sort, /* Default Sort Type in Thread Screen */ -+ thread_cur_sort, /* current sort style for threads */ -+ msgmap_thread_sort, - sort_types[22]; - - int last_expire_year, last_expire_month; -diff -rc alpine-2.10/pith/thread.c alpine-2.10.fancy/pith/thread.c -*** alpine-2.10/pith/thread.c 2013-01-11 11:26:44.000000000 -0700 ---- alpine-2.10.fancy/pith/thread.c 2013-01-11 20:43:05.000000000 -0700 -*************** -*** 30,41 **** - #include "../pith/mailcmd.h" - #include "../pith/ablookup.h" - - - /* - * Internal prototypes - */ - long *sort_thread_flatten(THREADNODE *, MAILSTREAM *, long *, -! char *, long, PINETHRD_S *, unsigned); - void make_thrdflags_consistent(MAILSTREAM *, MSGNO_S *, PINETHRD_S *, int); - THREADNODE *collapse_threadnode_tree(THREADNODE *); - THREADNODE *collapse_threadnode_tree_sorted(THREADNODE *); ---- 30,47 ---- - #include "../pith/mailcmd.h" - #include "../pith/ablookup.h" - -+ static int erase_thread_info = 1; -+ -+ typedef struct sizethread_t { -+ int count; -+ long pos; -+ } SIZETHREAD_T; - - /* - * Internal prototypes - */ - long *sort_thread_flatten(THREADNODE *, MAILSTREAM *, long *, -! char *, long, PINETHRD_S *, unsigned, int, long, long); - void make_thrdflags_consistent(MAILSTREAM *, MSGNO_S *, PINETHRD_S *, int); - THREADNODE *collapse_threadnode_tree(THREADNODE *); - THREADNODE *collapse_threadnode_tree_sorted(THREADNODE *); -*************** -*** 43,48 **** ---- 49,55 ---- - THREADNODE *insert_tree_in_place(THREADNODE *, THREADNODE *); - unsigned long branch_greatest_num(THREADNODE *, int); - long calculate_visible_threads(MAILSTREAM *); -+ int pine_compare_size_thread(const qsort_t *, const qsort_t *); - - - PINETHRD_S * -*************** -*** 95,114 **** - set_flags_for_thread(MAILSTREAM *stream, MSGNO_S *msgmap, int f, PINETHRD_S *thrd, int v) - { - PINETHRD_S *nthrd, *bthrd; - - if(!(stream && thrd && msgmap)) - return; - - set_lflag(stream, msgmap, mn_raw2m(msgmap, thrd->rawno), f, v); - -! if(thrd->next){ -! nthrd = fetch_thread(stream, thrd->next); - if(nthrd) - set_flags_for_thread(stream, msgmap, f, nthrd, v); - } - -! if(thrd->branch){ -! bthrd = fetch_thread(stream, thrd->branch); - if(bthrd) - set_flags_for_thread(stream, msgmap, f, bthrd, v); - } ---- 102,123 ---- - set_flags_for_thread(MAILSTREAM *stream, MSGNO_S *msgmap, int f, PINETHRD_S *thrd, int v) - { - PINETHRD_S *nthrd, *bthrd; -+ unsigned long next = 0L, branch = 0L; - - if(!(stream && thrd && msgmap)) - return; - - set_lflag(stream, msgmap, mn_raw2m(msgmap, thrd->rawno), f, v); - -! if(next = get_next(stream,thrd)){ -! nthrd = fetch_thread(stream, next); - if(nthrd) - set_flags_for_thread(stream, msgmap, f, nthrd, v); - } - -! -! if(branch = get_branch(stream, thrd)){ -! bthrd = fetch_thread(stream, branch); - if(bthrd) - set_flags_for_thread(stream, msgmap, f, bthrd, v); - } -*************** -*** 122,128 **** - MESSAGECACHE *mc; - PINELT_S *peltp; - -! if(!(stream && stream->spare)) - return; - - ps_global->view_skipped_index = 0; ---- 131,137 ---- - MESSAGECACHE *mc; - PINELT_S *peltp; - -! if(!(stream && stream->spare) || !erase_thread_info) - return; - - ps_global->view_skipped_index = 0; -*************** -*** 155,161 **** - PINETHRD_S *thrd = NULL; - unsigned long msgno, rawno; - int un_view_thread = 0; -! long raw_current; - char *dup_chk = NULL; - - ---- 164,170 ---- - PINETHRD_S *thrd = NULL; - unsigned long msgno, rawno; - int un_view_thread = 0; -! long raw_current, branch; - char *dup_chk = NULL; - - -*************** -*** 168,177 **** - * way. If the dummy node is at the top-level, then its children are - * promoted to the top-level as separate threads. - */ -! if(F_ON(F_THREAD_SORTS_BY_ARRIVAL, ps_global)) -! collapsed_tree = collapse_threadnode_tree_sorted(tree); -! else -! collapsed_tree = collapse_threadnode_tree(tree); - - /* dup_chk is like sort with an origin of 1 */ - dup_chk = (char *) fs_get((mn_get_nmsgs(g_sort.msgmap)+1) * sizeof(char)); ---- 177,187 ---- - * way. If the dummy node is at the top-level, then its children are - * promoted to the top-level as separate threads. - */ -! collapsed_tree = F_ON(F_ENHANCED_THREAD, ps_global) -! ? copy_tree(tree) -! : (F_ON(F_THREAD_SORTS_BY_ARRIVAL, ps_global) -! ? collapse_threadnode_tree_sorted(tree) -! : collapse_threadnode_tree(tree)); - - /* dup_chk is like sort with an origin of 1 */ - dup_chk = (char *) fs_get((mn_get_nmsgs(g_sort.msgmap)+1) * sizeof(char)); -*************** -*** 182,188 **** - (void) sort_thread_flatten(collapsed_tree, stream, - &g_sort.msgmap->sort[1], - dup_chk, mn_get_nmsgs(g_sort.msgmap), -! NULL, THD_TOP); - - /* reset the inverse array */ - msgno_reset_isort(g_sort.msgmap); ---- 192,198 ---- - (void) sort_thread_flatten(collapsed_tree, stream, - &g_sort.msgmap->sort[1], - dup_chk, mn_get_nmsgs(g_sort.msgmap), -! NULL, THD_TOP, 0, 1L, 0L); - - /* reset the inverse array */ - msgno_reset_isort(g_sort.msgmap); -*************** -*** 340,351 **** - else{ - thrd = fetch_head_thread(stream); - while(thrd){ - /* - * The top-level threads aren't hidden by collapse. - */ - msgno = mn_raw2m(g_sort.msgmap, thrd->rawno); -! if(msgno) -! set_lflag(stream, g_sort.msgmap, msgno, MN_CHID, 0); - - if(thrd->next){ - PINETHRD_S *nthrd; ---- 350,363 ---- - else{ - thrd = fetch_head_thread(stream); - while(thrd){ -+ unsigned long raw = thrd->rawno; -+ unsigned long top = top_thread(stream, raw); - /* - * The top-level threads aren't hidden by collapse. - */ - msgno = mn_raw2m(g_sort.msgmap, thrd->rawno); -! if(msgno && !get_lflag(stream, NULL,thrd->rawno, MN_COLL)) -! set_lflag(stream, g_sort.msgmap, msgno, MN_CHID, 0); - - if(thrd->next){ - PINETHRD_S *nthrd; -*************** -*** 359,367 **** - MN_COLL)); - } - -! if(thrd->nextthd) -! thrd = fetch_thread(stream, thrd->nextthd); -! else - thrd = NULL; - } - } ---- 371,380 ---- - MN_COLL)); - } - -! while (thrd && top_thread(stream, thrd->rawno) == top -! && thrd->nextthd) -! thrd = fetch_thread(stream, thrd->nextthd); -! if (!(thrd && thrd->nextthd)) - thrd = NULL; - } - } -*************** -*** 412,418 **** - int a_parent_is_collapsed) - { - PINETHRD_S *nthrd, *bthrd; -! unsigned long msgno; - - if(!thrd) - return; ---- 425,431 ---- - int a_parent_is_collapsed) - { - PINETHRD_S *nthrd, *bthrd; -! unsigned long msgno, next, branch; - - if(!thrd) - return; -*************** -*** 430,437 **** - set_lflag(stream, msgmap, msgno, MN_CHID, 0); - } - -! if(thrd->next){ -! nthrd = fetch_thread(stream, thrd->next); - if(nthrd) - make_thrdflags_consistent(stream, msgmap, nthrd, - a_parent_is_collapsed ---- 443,450 ---- - set_lflag(stream, msgmap, msgno, MN_CHID, 0); - } - -! if(next = get_next(stream, thrd)){ -! nthrd = fetch_thread(stream, next); - if(nthrd) - make_thrdflags_consistent(stream, msgmap, nthrd, - a_parent_is_collapsed -*************** -*** 440,447 **** - MN_COLL)); - } - -! if(thrd->branch){ -! bthrd = fetch_thread(stream, thrd->branch); - if(bthrd) - make_thrdflags_consistent(stream, msgmap, bthrd, - a_parent_is_collapsed); ---- 453,460 ---- - MN_COLL)); - } - -! if(branch = get_branch(stream, thrd)){ -! bthrd = fetch_thread(stream, branch); - if(bthrd) - make_thrdflags_consistent(stream, msgmap, bthrd, - a_parent_is_collapsed); -*************** -*** 488,496 **** - long * - sort_thread_flatten(THREADNODE *node, MAILSTREAM *stream, - long *entry, char *dup_chk, long maxno, -! PINETHRD_S *thrd, unsigned int flags) - { -! PINETHRD_S *newthrd = NULL; - - if(node){ - if(node->num > 0L && node->num <= maxno){ /* holes happen */ ---- 501,510 ---- - long * - sort_thread_flatten(THREADNODE *node, MAILSTREAM *stream, - long *entry, char *dup_chk, long maxno, -! PINETHRD_S *thrd, unsigned int flags, -! int adopted, long top, long threadno) - { -! PINETHRD_S *newthrd = NULL, *save_thread = NULL; - - if(node){ - if(node->num > 0L && node->num <= maxno){ /* holes happen */ -*************** -*** 498,503 **** ---- 512,520 ---- - *entry = node->num; - dup_chk[node->num] = 1; - -+ if(adopted == 2) -+ top = node->num; -+ - /* - * Build a richer threading structure that will help us paint - * and operate on threads and subthreads. -*************** -*** 506,525 **** - if(newthrd){ - entry++; - - if(node->next) - entry = sort_thread_flatten(node->next, stream, - entry, dup_chk, maxno, -! newthrd, THD_NEXT); - - if(node->branch) - entry = sort_thread_flatten(node->branch, stream, - entry, dup_chk, maxno, - newthrd, -! (flags == THD_TOP) ? THD_TOP -! : THD_BRANCH); - } - } - } - } - - return(entry); ---- 523,573 ---- - if(newthrd){ - entry++; - -+ if(adopted == 2) -+ threadno = newthrd->thrdno; -+ if(adopted){ -+ newthrd->toploose = top; -+ newthrd->thrdno = threadno; -+ } -+ adopted = adopted ? 1 : 0; - if(node->next) - entry = sort_thread_flatten(node->next, stream, - entry, dup_chk, maxno, -! newthrd, THD_NEXT, adopted, top, threadno); - - if(node->branch) - entry = sort_thread_flatten(node->branch, stream, - entry, dup_chk, maxno, - newthrd, -! ((flags == THD_TOP) ? THD_TOP -! : THD_BRANCH), -! adopted, top, threadno); - } - } - } -+ else{ -+ adopted = 2; -+ if(node->next) -+ entry = sort_thread_flatten(node->next, stream, entry, dup_chk, -+ maxno, thrd, THD_TOP, adopted, top, threadno); -+ adopted = 0; -+ if(node->branch){ -+ if(entry){ -+ long *last_entry = entry; -+ -+ do{ -+ last_entry--; -+ save_thread = ((PINELT_S *)mail_elt(stream, *last_entry)->sparep)->pthrd; -+ } while (save_thread->parent != 0L); -+ entry = sort_thread_flatten(node->branch, stream, entry, dup_chk, -+ maxno, save_thread, (flags == THD_TOP ? THD_TOP : THD_BRANCH), -+ adopted, top, threadno); -+ } -+ else -+ entry = sort_thread_flatten(node->branch, stream, entry, dup_chk, -+ maxno, NULL, THD_TOP, adopted, top, threadno); -+ } -+ } - } - - return(entry); -*************** -*** 788,794 **** - */ - void - collapse_or_expand(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap, -! long unsigned int msgno) - { - int collapsed, adjust_current = 0; - PINETHRD_S *thrd = NULL, *nthrd; ---- 836,842 ---- - */ - void - collapse_or_expand(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap, -! long unsigned int msgno, int display) - { - int collapsed, adjust_current = 0; - PINETHRD_S *thrd = NULL, *nthrd; -*************** -*** 841,847 **** - if(!thrd) - return; - -! collapsed = get_lflag(stream, NULL, thrd->rawno, MN_COLL) && thrd->next; - - if(collapsed){ - msgno = mn_raw2m(msgmap, thrd->rawno); ---- 889,895 ---- - if(!thrd) - return; - -! collapsed = this_thread_is_kolapsed(ps_global, stream, msgmap, thrd->rawno); - - if(collapsed){ - msgno = mn_raw2m(msgmap, thrd->rawno); -*************** -*** 859,871 **** - msgno = mn_raw2m(msgmap, thrd->rawno); - if(msgno > 0L && msgno <= mn_get_total(msgmap)){ - set_lflag(stream, msgmap, msgno, MN_COLL, 1); -! if((nthrd = fetch_thread(stream, thrd->next)) != NULL) - set_thread_subtree(stream, nthrd, msgmap, 1, MN_CHID); - - clear_index_cache_ent(stream, msgno, 0); - } - } -! else - q_status_message(SM_ORDER, 0, 1, - _("No thread to collapse or expand on this line")); - ---- 907,919 ---- - msgno = mn_raw2m(msgmap, thrd->rawno); - if(msgno > 0L && msgno <= mn_get_total(msgmap)){ - set_lflag(stream, msgmap, msgno, MN_COLL, 1); -! if((thrd->next) && ((nthrd = fetch_thread(stream, thrd->next)) != NULL)) - set_thread_subtree(stream, nthrd, msgmap, 1, MN_CHID); - - clear_index_cache_ent(stream, msgno, 0); - } - } -! else if(display) - q_status_message(SM_ORDER, 0, 1, - _("No thread to collapse or expand on this line")); - -*************** -*** 952,969 **** - unsigned long count = 0; - PINETHRD_S *nthrd, *bthrd; - MESSAGECACHE *mc; - - if(!thrd || !stream || thrd->rawno < 1L || thrd->rawno > stream->nmsgs) - return count; - -! if(thrd->next){ -! nthrd = fetch_thread(stream, thrd->next); - if(nthrd) - count += count_flags_in_thread(stream, nthrd, flags); - } - -! if(thrd->branch){ -! bthrd = fetch_thread(stream, thrd->branch); - if(bthrd) - count += count_flags_in_thread(stream, bthrd, flags); - } ---- 1000,1018 ---- - unsigned long count = 0; - PINETHRD_S *nthrd, *bthrd; - MESSAGECACHE *mc; -+ unsigned long next = 0L, branch = 0L; - - if(!thrd || !stream || thrd->rawno < 1L || thrd->rawno > stream->nmsgs) - return count; - -! if(next = get_next(stream, thrd)){ -! nthrd = fetch_thread(stream, next); - if(nthrd) - count += count_flags_in_thread(stream, nthrd, flags); - } - -! if(branch = get_branch(stream, thrd)){ -! bthrd = fetch_thread(stream, branch); - if(bthrd) - count += count_flags_in_thread(stream, bthrd, flags); - } -*************** -*** 1051,1070 **** - mark_msgs_in_thread(MAILSTREAM *stream, PINETHRD_S *thrd, MSGNO_S *msgmap) - { - int count = 0; - PINETHRD_S *nthrd, *bthrd; - MESSAGECACHE *mc; - - if(!thrd || !stream || thrd->rawno < 1L || thrd->rawno > stream->nmsgs) - return count; - -! if(thrd->next){ -! nthrd = fetch_thread(stream, thrd->next); - if(nthrd) - count += mark_msgs_in_thread(stream, nthrd, msgmap); - } - -! if(thrd->branch){ -! bthrd = fetch_thread(stream, thrd->branch); - if(bthrd) - count += mark_msgs_in_thread(stream, bthrd, msgmap); - } ---- 1100,1120 ---- - mark_msgs_in_thread(MAILSTREAM *stream, PINETHRD_S *thrd, MSGNO_S *msgmap) - { - int count = 0; -+ long next, branch; - PINETHRD_S *nthrd, *bthrd; - MESSAGECACHE *mc; - - if(!thrd || !stream || thrd->rawno < 1L || thrd->rawno > stream->nmsgs) - return count; - -! if(next = get_next(stream, thrd)){ -! nthrd = fetch_thread(stream, next); - if(nthrd) - count += mark_msgs_in_thread(stream, nthrd, msgmap); - } - -! if(branch = get_branch(stream, thrd)){ -! bthrd = fetch_thread(stream, branch); - if(bthrd) - count += mark_msgs_in_thread(stream, bthrd, msgmap); - } -*************** -*** 1098,1104 **** - /* flags to set or clear */ - /* set or clear? */ - { -! unsigned long msgno; - PINETHRD_S *nthrd, *bthrd; - - if(!thrd || !stream || thrd->rawno < 1L || thrd->rawno > stream->nmsgs) ---- 1148,1154 ---- - /* flags to set or clear */ - /* set or clear? */ - { -! unsigned long msgno, next, branch; - PINETHRD_S *nthrd, *bthrd; - - if(!thrd || !stream || thrd->rawno < 1L || thrd->rawno > stream->nmsgs) -*************** -*** 1122,1135 **** - if(msgno > 0L && flags == MN_CHID2 && v == 1) - clear_index_cache_ent(stream, msgno, 0); - -! if(thrd->next){ -! nthrd = fetch_thread(stream, thrd->next); - if(nthrd) - set_thread_lflags(stream, nthrd, msgmap, flags, v); - } - -! if(thrd->branch){ -! bthrd = fetch_thread(stream, thrd->branch); - if(bthrd) - set_thread_lflags(stream, bthrd, msgmap, flags, v); - } ---- 1172,1185 ---- - if(msgno > 0L && flags == MN_CHID2 && v == 1) - clear_index_cache_ent(stream, msgno, 0); - -! if(next = get_next(stream, thrd)){ -! nthrd = fetch_thread(stream, next); - if(nthrd) - set_thread_lflags(stream, nthrd, msgmap, flags, v); - } - -! if(branch = get_branch(stream,thrd)){ -! bthrd = fetch_thread(stream, branch); - if(bthrd) - set_thread_lflags(stream, bthrd, msgmap, flags, v); - } -*************** -*** 1218,1236 **** - char to_us = ' '; - char branch_to_us = ' '; - PINETHRD_S *nthrd, *bthrd; - MESSAGECACHE *mc; - - if(!thrd || !stream || thrd->rawno < 1L || thrd->rawno > stream->nmsgs) - return to_us; - -! if(thrd->next){ -! nthrd = fetch_thread(stream, thrd->next); - if(nthrd) - to_us = to_us_symbol_for_thread(stream, nthrd, consider_flagged); - } - - if(((consider_flagged && to_us != '*') || (!consider_flagged && to_us != '+')) -! && thrd->branch){ - bthrd = fetch_thread(stream, thrd->branch); - if(bthrd) - branch_to_us = to_us_symbol_for_thread(stream, bthrd, consider_flagged); ---- 1268,1287 ---- - char to_us = ' '; - char branch_to_us = ' '; - PINETHRD_S *nthrd, *bthrd; -+ unsigned long next = 0L, branch = 0L; - MESSAGECACHE *mc; - - if(!thrd || !stream || thrd->rawno < 1L || thrd->rawno > stream->nmsgs) - return to_us; - -! if(next = get_next(stream,thrd)){ -! nthrd = fetch_thread(stream, next); - if(nthrd) - to_us = to_us_symbol_for_thread(stream, nthrd, consider_flagged); - } - - if(((consider_flagged && to_us != '*') || (!consider_flagged && to_us != '+')) -! && (branch = get_branch(stream, thrd))){ - bthrd = fetch_thread(stream, thrd->branch); - if(bthrd) - branch_to_us = to_us_symbol_for_thread(stream, bthrd, consider_flagged); -*************** -*** 1280,1286 **** - break; - } - -! if(to_us != '+' && resent_to_us(&idata)) - to_us = '+'; - - if(to_us == ' ' && F_ON(F_MARK_FOR_CC,ps_global)) ---- 1331,1337 ---- - break; - } - -! if(to_us != '+' && !idata.bogus && resent_to_us(&idata)) - to_us = '+'; - - if(to_us == ' ' && F_ON(F_MARK_FOR_CC,ps_global)) -*************** -*** 1328,1334 **** - - set_lflag(stream, msgmap, msgno, flags, v); - -! if(thrd->next && (hiding || !get_lflag(stream,NULL,thrd->rawno,MN_COLL))){ - nthrd = fetch_thread(stream, thrd->next); - if(nthrd) - set_thread_subtree(stream, nthrd, msgmap, v, flags); ---- 1379,1386 ---- - - set_lflag(stream, msgmap, msgno, flags, v); - -! if(thrd->next -! && (hiding || !get_lflag(stream,NULL,thrd->rawno,MN_COLL))){ - nthrd = fetch_thread(stream, thrd->next); - if(nthrd) - set_thread_subtree(stream, nthrd, msgmap, v, flags); -*************** -*** 1368,1375 **** - if(rawno) - thrd = fetch_thread(stream, rawno); - -! if(thrd && thrd->top && thrd->top != thrd->rawno) -! thrd = fetch_thread(stream, thrd->top); - - if(!thrd) - return 0; ---- 1420,1427 ---- - if(rawno) - thrd = fetch_thread(stream, rawno); - -! if(thrd && thrd->top && top_thread(stream,thrd->top) != thrd->rawno) -! thrd = fetch_thread(stream, top_thread(stream,thrd->top)); - - if(!thrd) - return 0; -*************** -*** 1433,1439 **** - thrd = fetch_thread(stream, rawno); - - if(thrd && thrd->top) -! topthrd = fetch_thread(stream, thrd->top); - - if(!topthrd) - return 0; ---- 1485,1491 ---- - thrd = fetch_thread(stream, rawno); - - if(thrd && thrd->top) -! topthrd = fetch_thread(stream, top_thread(stream,thrd->top)); - - if(!topthrd) - return 0; -*************** -*** 1539,1544 **** ---- 1591,1597 ---- - set_search_bit_for_thread(MAILSTREAM *stream, PINETHRD_S *thrd, SEARCHSET **msgset) - { - PINETHRD_S *nthrd, *bthrd; -+ unsigned long next, branch; - - if(!(stream && thrd)) - return; -*************** -*** 1547,1561 **** - && (!(msgset && *msgset) || in_searchset(*msgset, thrd->rawno))) - mm_searched(stream, thrd->rawno); - -! if(thrd->next){ -! nthrd = fetch_thread(stream, thrd->next); - if(nthrd) - set_search_bit_for_thread(stream, nthrd, msgset); - } - -! if(thrd->branch){ -! bthrd = fetch_thread(stream, thrd->branch); - if(bthrd) - set_search_bit_for_thread(stream, bthrd, msgset); - } - } ---- 1600,2221 ---- - && (!(msgset && *msgset) || in_searchset(*msgset, thrd->rawno))) - mm_searched(stream, thrd->rawno); - -! if(next= get_next(stream, thrd)){ -! nthrd = fetch_thread(stream, next); - if(nthrd) - set_search_bit_for_thread(stream, nthrd, msgset); - } - -! if(branch = get_branch(stream, thrd)){ -! bthrd = fetch_thread(stream, branch); - if(bthrd) - set_search_bit_for_thread(stream, bthrd, msgset); - } - } -+ -+ /* -+ * Make a copy of c-client's THREAD tree -+ */ -+ THREADNODE * -+ copy_tree(THREADNODE *tree) -+ { -+ THREADNODE *newtree = NULL; -+ -+ if(tree){ -+ newtree = mail_newthreadnode(NULL); -+ newtree->num = tree->num; -+ if(tree->next) -+ newtree->next = copy_tree(tree->next); -+ -+ if(tree->branch) -+ newtree->branch = copy_tree(tree->branch); -+ } -+ return(newtree); -+ } -+ -+ long -+ top_thread(MAILSTREAM *stream, long rawmsgno) -+ { -+ PINETHRD_S *thrd = NULL; -+ unsigned long rawno; -+ -+ if(!stream) -+ return -1L; -+ -+ if(rawmsgno) -+ thrd = fetch_thread(stream, rawmsgno); -+ -+ if(!thrd) -+ return -1L; -+ -+ return F_ON(F_ENHANCED_THREAD, ps_global) -+ ? (thrd->toploose ? thrd->toploose : thrd->top) -+ : thrd->top; -+ } -+ -+ void -+ move_top_thread(MAILSTREAM *stream, MSGNO_S *msgmap, long rawmsgno) -+ { -+ mn_set_cur(msgmap,mn_raw2m(msgmap, top_thread(stream, rawmsgno))); -+ } -+ -+ long -+ top_this_thread(MAILSTREAM *stream, long rawmsgno) -+ { -+ PINETHRD_S *thrd = NULL; -+ unsigned long rawno; -+ -+ if(!stream) -+ return -1L; -+ -+ if(rawmsgno) -+ thrd = fetch_thread(stream, rawmsgno); -+ -+ if(!thrd) -+ return -1L; -+ -+ return thrd->top; -+ } -+ -+ void -+ move_top_this_thread(MAILSTREAM *stream, MSGNO_S *msgmap, long rawmsgno) -+ { -+ mn_set_cur(msgmap,mn_raw2m(msgmap, top_this_thread(stream, rawmsgno))); -+ } -+ -+ int -+ thread_is_kolapsed(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap, long rawmsgno) -+ { -+ int collapsed; -+ PINETHRD_S *thrd = NULL; -+ unsigned long rawno, orig, orig_rawno; -+ -+ if(!stream) -+ return -1; -+ -+ orig = mn_get_cur(msgmap); -+ move_top_thread(stream, msgmap, rawmsgno); -+ rawno = orig_rawno = mn_m2raw(msgmap, mn_get_cur(msgmap)); -+ if(rawno) -+ thrd = fetch_thread(stream, rawno); -+ -+ if(!thrd) -+ return -1; -+ -+ while(collapsed = this_thread_is_kolapsed(state, stream, msgmap, rawno)) -+ if (F_OFF(F_ENHANCED_THREAD, state) -+ || (move_next_this_thread(state, stream, msgmap, 0) <= 0) -+ || !(rawno = mn_m2raw(msgmap, mn_get_cur(msgmap))) -+ || (orig_rawno != top_thread(stream, rawno))) -+ break; -+ -+ mn_set_cur(msgmap,orig); /* return home */ -+ -+ return collapsed; -+ } -+ -+ /* this function tells us if the thread (or branch in the case of loose threads) -+ * is collapsed -+ */ -+ -+ int -+ this_thread_is_kolapsed(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap, long rawmsgno) -+ { -+ int collapsed; -+ PINETHRD_S *thrd = NULL; -+ unsigned long rawno, orig; -+ -+ if(!stream) -+ return -1; -+ -+ rawno = rawmsgno; -+ if(rawno) -+ thrd = fetch_thread(stream, rawno); -+ -+ if(!thrd) -+ return -1; -+ -+ collapsed = get_lflag(stream, NULL, rawno, MN_COLL | MN_CHID); -+ -+ if (!thrd->next){ -+ if (thrd->rawno != top_thread(stream, thrd->rawno)) -+ collapsed = get_lflag(stream, NULL, rawno, MN_CHID); ++ else ++ q_status_message(SM_ORDER, 0, 1, ++ "Command available in threaded mode only"); ++ break; ++ ++ case MC_DELTHREAD: ++ if (THREADING()){ ++ if (any_messages(ps_global->msgmap, NULL, "to delete")) ++ cmd_delete_thread(ps_global, ps_global->mail_stream, ps_global->msgmap); ++ done = 1; ++ } ++ else ++ q_status_message(SM_ORDER, 0, 1, ++ "Command available in threaded mode only"); ++ break; ++ ++ case MC_UNDTHREAD: ++ if (THREADING()){ ++ if (any_messages(ps_global->msgmap, NULL, "to undelete")) ++ cmd_undelete_thread(ps_global, ps_global->mail_stream, ps_global->msgmap); ++ done = 1; ++ } ++ else ++ q_status_message(SM_ORDER, 0, 1, ++ "Command available in threaded mode only"); ++ break; ++ ++ case MC_SELTHREAD: ++ if (THREADING()){ ++ if (any_messages(ps_global->msgmap, NULL, "to undelete")) ++ cmd_select_thread(ps_global, ps_global->mail_stream, ps_global->msgmap); ++ done = 1; ++ } ++ else ++ q_status_message(SM_ORDER, 0, 1, ++ "Command available in threaded mode only"); ++ break; + + /* ------- First handle on Line ------ */ + case MC_GOTOBOL : +Index: alpine-2.11/alpine/roleconf.c +=================================================================== +--- alpine-2.11.orig/alpine/roleconf.c ++++ alpine-2.11/alpine/roleconf.c +@@ -4478,11 +4478,11 @@ role_config_edit_screen(struct pine *ps, + ctmp->tool = role_sort_tool; + ctmp->valoffset = rindent; + ctmp->flags |= CF_NOSELECT; +- ctmp->value = cpystr(set_choose); \ ++ ctmp->value = cpystr(set_choose); + + pval = PVAL(&sort_act_var, ew); + if(pval) +- decode_sort(pval, &def_sort, &def_sort_rev); ++ decode_sort(pval, &def_sort, &def_sort_rev, 0); + + /* allow user to set their default sort order */ + new_confline(&ctmp)->var = &sort_act_var; +@@ -4492,7 +4492,7 @@ role_config_edit_screen(struct pine *ps, + ctmp->tool = role_sort_tool; + ctmp->valoffset = rindent; + ctmp->varmem = -1; +- ctmp->value = generalized_sort_pretty_value(ps, ctmp, 0); ++ ctmp->value = generalized_sort_pretty_value(ps, ctmp, 0, 0); + + for(j = 0; j < 2; j++){ + for(i = 0; ps->sort_types[i] != EndofList; i++){ +@@ -4504,7 +4504,7 @@ role_config_edit_screen(struct pine *ps, + ctmp->valoffset = rindent; + ctmp->varmem = i + (j * EndofList); + ctmp->value = generalized_sort_pretty_value(ps, ctmp, +- 0); ++ 0, 0); + } + } + +@@ -5437,7 +5437,7 @@ role_config_edit_screen(struct pine *ps, + (*result)->patgrp->stat_boy = PAT_STAT_EITHER; + + if(sort_act){ +- decode_sort(sort_act, &def_sort, &def_sort_rev); ++ decode_sort(sort_act, &def_sort, &def_sort_rev, 0); + (*result)->action->sort_is_set = 1; + (*result)->action->sortorder = def_sort; + (*result)->action->revsort = (def_sort_rev ? 1 : 0); +Index: alpine-2.11/alpine/setup.c +=================================================================== +--- alpine-2.11.orig/alpine/setup.c ++++ alpine-2.11/alpine/setup.c +@@ -258,7 +258,7 @@ option_screen(struct pine *ps, int edit_ + ctmpa->flags |= CF_NOSELECT; + ctmpa->value = cpystr("--- ----------------------"); + +- decode_sort(pval, &def_sort, &def_sort_rev); ++ decode_sort(pval, &def_sort, &def_sort_rev, 0); + + for(j = 0; j < 2; j++){ + for(i = 0; ps->sort_types[i] != EndofList; i++){ +@@ -273,6 +273,55 @@ option_screen(struct pine *ps, int edit_ + } + } + } ++ else if(vtmp == &ps->vars[V_THREAD_SORT_KEY]){ /* radio case */ ++ SortOrder thread_def_sort; ++ int thread_def_sort_rev, lv; ++ ++ ctmpa->flags |= CF_NOSELECT; ++ ctmpa->keymenu = &config_radiobutton_keymenu; ++ ctmpa->tool = NULL; ++ ++ /* put a nice delimiter before list */ ++ new_confline(&ctmpa)->var = NULL; ++ ctmpa->varnamep = ctmpb; ++ ctmpa->keymenu = &config_radiobutton_keymenu; ++ ctmpa->help = NO_HELP; ++ ctmpa->tool = radiobutton_tool; ++ ctmpa->valoffset = 12; ++ ctmpa->flags |= CF_NOSELECT; ++ ctmpa->value = cpystr("Set Thread Sort Options"); ++ ++ new_confline(&ctmpa)->var = NULL; ++ ctmpa->varnamep = ctmpb; ++ ctmpa->keymenu = &config_radiobutton_keymenu; ++ ctmpa->help = NO_HELP; ++ ctmpa->tool = radiobutton_tool; ++ ctmpa->valoffset = 12; ++ ctmpa->flags |= CF_NOSELECT; ++ ctmpa->value = cpystr("--- ----------------------"); ++ ++ /* find longest value's name */ ++ for(lv = 0, i = 0; ps->sort_types[i] != EndofList; i++) ++ if(lv < (j = strlen(sort_name(ps->sort_types[i])))) ++ lv = j; ++ ++ decode_sort(pval, &thread_def_sort, &thread_def_sort_rev, 1); ++ ++ for(j = 0; j < 2; j++){ ++ for(i = 0; ps->sort_types[i] != EndofList; i++){ ++ if (allowed_thread_key(ps->sort_types[i])){ ++ new_confline(&ctmpa)->var = vtmp; ++ ctmpa->varnamep = ctmpb; ++ ctmpa->keymenu = &config_radiobutton_keymenu; ++ ctmpa->help = config_help(vtmp - ps->vars, 0); ++ ctmpa->tool = radiobutton_tool; ++ ctmpa->valoffset = 12; ++ ctmpa->varmem = i + (j * EndofList); ++ ctmpa->value = pretty_value(ps, ctmpa); ++ } ++ } ++ } ++ } + else if(vtmp == &ps->vars[V_USE_ONLY_DOMAIN_NAME]){ /* yesno case */ + ctmpa->keymenu = &config_yesno_keymenu; + ctmpa->tool = yesno_tool; +@@ -464,6 +513,15 @@ option_screen(struct pine *ps, int edit_ + } + } + ++ pval = PVAL(&ps->vars[V_THREAD_SORT_KEY], ew); ++ if(vsave[V_THREAD_SORT_KEY].saved_user_val.p && pval ++ && strcmp(vsave[V_THREAD_SORT_KEY].saved_user_val.p, pval)){ ++ if(!mn_get_mansort(ps_global->msgmap)){ ++ clear_index_cache(ps_global->mail_stream, 0); ++ reset_sort_order(SRT_VRB); ++ } ++ } ++ + treat_color_vars_as_text = 0; + free_saved_config(ps, &vsave, expose_hidden_config); + #ifdef _WINDOWS +Index: alpine-2.11/pith/conf.c +=================================================================== +--- alpine-2.11.orig/pith/conf.c ++++ alpine-2.11/pith/conf.c +@@ -206,6 +206,8 @@ CONF_TXT_T cf_text_fcc_name_rule[] = "De + + CONF_TXT_T cf_text_sort_key[] = "Sets presentation order of messages in Index. Choices:\n# Subject, From, Arrival, Date, Size, To, Cc, OrderedSubj, Score, and Thread.\n# Order may be reversed by appending /Reverse. Default: \"Arrival\"."; + ++CONF_TXT_T cf_text_thread_sort_key[] = "#Sets presentation order of threads in thread index. Choices:\n#arrival, and thread."; ++ + CONF_TXT_T cf_text_addrbook_sort_rule[] = "Sets presentation order of address book entries. Choices: dont-sort,\n# fullname-with-lists-last, fullname, nickname-with-lists-last, nickname\n# Default: \"fullname-with-lists-last\"."; + + CONF_TXT_T cf_text_folder_sort_rule[] = "Sets presentation order of folder list entries. Choices: alphabetical,\n# alpha-with-dirs-last, alpha-with-dirs-first.\n# Default: \"alpha-with-directories-last\"."; +@@ -522,6 +524,8 @@ static struct variable variables[] = { + NULL, cf_text_fcc_name_rule}, + {"sort-key", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, + NULL, cf_text_sort_key}, ++{"thread-sort-key", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, ++ NULL, cf_text_thread_sort_key}, + {"addrbook-sort-rule", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, + "Address Book Sort Rule", cf_text_addrbook_sort_rule}, + {"folder-sort-rule", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, +@@ -1572,7 +1576,7 @@ init_vars(struct pine *ps, void (*cmds_f + register struct variable *vars = ps->vars; + int obs_header_in_reply = 0, /* the obs_ variables are to */ + obs_old_style_reply = 0, /* support backwards compatibility */ +- obs_save_by_sender, i, def_sort_rev; ++ obs_save_by_sender, i, def_sort_rev, thread_def_sort_rev; + long rvl; + PINERC_S *fixedprc = NULL; + FeatureLevel obs_feature_level; +@@ -1597,6 +1601,7 @@ init_vars(struct pine *ps, void (*cmds_f + GLO_FEATURE_LEVEL = cpystr("sappling"); + GLO_OLD_STYLE_REPLY = cpystr(DF_OLD_STYLE_REPLY); + GLO_SORT_KEY = cpystr(DF_SORT_KEY); ++ GLO_THREAD_SORT_KEY = cpystr(DF_THREAD_SORT_KEY); + GLO_SAVED_MSG_NAME_RULE = cpystr(DF_SAVED_MSG_NAME_RULE); + GLO_FCC_RULE = cpystr(DF_FCC_RULE); + GLO_AB_SORT_RULE = cpystr(DF_AB_SORT_RULE); +@@ -2505,7 +2510,7 @@ init_vars(struct pine *ps, void (*cmds_f + set_current_val(&vars[V_ARCHIVED_FOLDERS], TRUE, TRUE); + set_current_val(&vars[V_INCOMING_FOLDERS], TRUE, TRUE); + set_current_val(&vars[V_SORT_KEY], TRUE, TRUE); +- if(decode_sort(VAR_SORT_KEY, &ps->def_sort, &def_sort_rev) == -1){ ++ if(decode_sort(VAR_SORT_KEY, &ps->def_sort, &def_sort_rev,0) == -1){ + snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Sort type \"%.200s\" is invalid", VAR_SORT_KEY); + init_error(ps, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf); + ps->def_sort = SortArrival; +@@ -2514,6 +2519,17 @@ init_vars(struct pine *ps, void (*cmds_f + else + ps->def_sort_rev = def_sort_rev; + ++ set_current_val(&vars[V_THREAD_SORT_KEY], TRUE, TRUE); ++ if(decode_sort(VAR_THREAD_SORT_KEY, &ps->thread_def_sort, ++ &thread_def_sort_rev, 1) == -1){ ++ sprintf(tmp_20k_buf, "Sort type \"%s\" is invalid", VAR_THREAD_SORT_KEY); ++ init_error(ps, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf); ++ ps->thread_def_sort = SortThread; ++ ps->thread_def_sort_rev = 0; ++ } ++ else ++ ps->thread_def_sort_rev = thread_def_sort_rev; ++ + cur_rule_value(&vars[V_SAVED_MSG_NAME_RULE], TRUE, TRUE); + {NAMEVAL_S *v; int i; + for(i = 0; (v = save_msg_rules(i)); i++) +@@ -2937,6 +2953,8 @@ feature_list(int index) + F_COLOR_LINE_IMPORTANT, h_config_color_thrd_import, PREF_INDX, 0}, + {"thread-sorts-by-arrival", "Thread Sorts by Arrival", + F_THREAD_SORTS_BY_ARRIVAL, h_config_thread_sorts_by_arrival, PREF_INDX, 0}, ++ {"enhanced-fancy-thread-support", "Enhanced Fancy Thread Support", ++ F_ENHANCED_THREAD, h_config_enhanced_thread, PREF_INDX, 0}, + + /* Viewer prefs */ + {"enable-msg-view-addresses", "Enable Message View Address Links", +@@ -7619,6 +7637,8 @@ config_help(int var, int feature) + return(h_config_fcc_rule); + case V_SORT_KEY : + return(h_config_sort_key); ++ case V_THREAD_SORT_KEY : ++ return(h_config_thread_sort_key); + case V_AB_SORT_RULE : + return(h_config_ab_sort_rule); + case V_FLD_SORT_RULE : +Index: alpine-2.11/pith/conf.h +=================================================================== +--- alpine-2.11.orig/pith/conf.h ++++ alpine-2.11/pith/conf.h +@@ -144,6 +144,9 @@ + #define VAR_SORT_KEY vars[V_SORT_KEY].current_val.p + #define GLO_SORT_KEY vars[V_SORT_KEY].global_val.p + #define COM_SORT_KEY vars[V_SORT_KEY].cmdline_val.p ++#define VAR_THREAD_SORT_KEY vars[V_THREAD_SORT_KEY].current_val.p ++#define GLO_THREAD_SORT_KEY vars[V_THREAD_SORT_KEY].global_val.p ++#define COM_THREAD_SORT_KEY vars[V_THREAD_SORT_KEY].cmdline_val.p + #define VAR_AB_SORT_RULE vars[V_AB_SORT_RULE].current_val.p + #define GLO_AB_SORT_RULE vars[V_AB_SORT_RULE].global_val.p + #define VAR_FLD_SORT_RULE vars[V_FLD_SORT_RULE].current_val.p +Index: alpine-2.11/pith/conftype.h +=================================================================== +--- alpine-2.11.orig/pith/conftype.h ++++ alpine-2.11/pith/conftype.h +@@ -59,6 +59,7 @@ typedef enum { V_PERSONAL_NAME = 0 + , V_SAVED_MSG_NAME_RULE + , V_FCC_RULE + , V_SORT_KEY ++ , V_THREAD_SORT_KEY + , V_AB_SORT_RULE + , V_FLD_SORT_RULE + , V_GOTO_DEFAULT_RULE +@@ -509,6 +510,7 @@ typedef enum { + F_QUELL_TIMEZONE, + F_QUELL_USERAGENT, + F_COLOR_LINE_IMPORTANT, ++ F_ENHANCED_THREAD, + F_SLASH_COLL_ENTIRE, + F_ENABLE_FULL_HDR_AND_TEXT, + F_QUELL_FULL_HDR_RESET, +@@ -716,5 +718,6 @@ typedef struct smime_stuff { + + /* exported protoypes */ + ++#define DF_THREAD_SORT_KEY "thread" + + #endif /* PITH_CONFTYPE_INCLUDED */ +Index: alpine-2.11/pith/flag.c +=================================================================== +--- alpine-2.11.orig/pith/flag.c ++++ alpine-2.11/pith/flag.c +@@ -594,14 +594,16 @@ set_lflag(MAILSTREAM *stream, MSGNO_S *m + + was_invisible = (pelt->hidden || pelt->colhid) ? 1 : 0; + ++ thrd = fetch_thread(stream, rawno); ++ + if((chk_thrd_cnt = ((msgs->visible_threads >= 0L) + && THRD_INDX_ENABLED() && (f & MN_HIDE) && (pelt->hidden != v))) != 0){ + thrd = fetch_thread(stream, rawno); + if(thrd && thrd->top){ +- if(thrd->top == thrd->rawno) ++ if(top_thread(stream, thrd->top) == thrd->rawno) + topthrd = thrd; + else +- topthrd = fetch_thread(stream, thrd->top); ++ topthrd = fetch_thread(stream, top_thread(stream, thrd->top)); + } + + if(topthrd){ +Index: alpine-2.11/pith/indxtype.h +=================================================================== +--- alpine-2.11.orig/pith/indxtype.h ++++ alpine-2.11/pith/indxtype.h +@@ -76,7 +76,7 @@ typedef enum {iNothing, iStatus, iFStatu + iKey, iKeyInit, + iPrefDate, iPrefTime, iPrefDateTime, + iCurPrefDate, iCurPrefTime, iCurPrefDateTime, +- iSize, iSizeComma, iSizeNarrow, iDescripSize, ++ iSize, iSizeComma, iSizeNarrow, iDescripSize, iSizeThread, + iNewsAndTo, iToAndNews, iNewsAndRecips, iRecipsAndNews, + iFromTo, iFromToNotNews, iFrom, iTo, iSender, iCc, iNews, iRecips, + iCurNews, iArrow, +Index: alpine-2.11/pith/mailindx.c +=================================================================== +--- alpine-2.11.orig/pith/mailindx.c ++++ alpine-2.11/pith/mailindx.c +@@ -228,6 +228,7 @@ init_index_format(char *format, INDEX_CO + case iSTime: + case iKSize: + case iSize: ++ case iSizeThread: + case iPrioAlpha: + (*answer)[column].req_width = 7; + break; +@@ -452,6 +453,7 @@ static INDEX_PARSE_T itokens[] = { + {"FROMORTONOTNEWS", iFromToNotNews, FOR_INDEX}, + {"SIZE", iSize, FOR_INDEX}, + {"SIZECOMMA", iSizeComma, FOR_INDEX}, ++ {"SIZETHREAD", iSizeThread, FOR_INDEX}, + {"SIZENARROW", iSizeNarrow, FOR_INDEX}, + {"KSIZE", iKSize, FOR_INDEX}, + {"SUBJECT", iSubject, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, +@@ -943,7 +945,7 @@ static IndexColType fixed_ctypes[] = { + iSDateTimeS1, iSDateTimeS2, iSDateTimeS3, iSDateTimeS4, + iSDateTimeIso24, iSDateTimeIsoS24, + iSDateTimeS124, iSDateTimeS224, iSDateTimeS324, iSDateTimeS424, +- iSize, iSizeComma, iSizeNarrow, iKSize, iDescripSize, ++ iSize, iSizeComma, iSizeNarrow, iKSize, iDescripSize, iSizeThread, + iPrio, iPrioBang, iPrioAlpha, iInit, + iAtt, iTime24, iTime12, iTimezone, iMonAbb, iYear, iYear2Digit, + iDay2Digit, iMon2Digit, iDayOfWeekAbb, iScore, iMonLong, iDayOfWeek +@@ -1136,6 +1138,7 @@ setup_index_header_widths(MAILSTREAM *st + case iTime12: + case iSize: + case iKSize: ++ case iSizeThread: + cdesc->actual_length = 7; + cdesc->adjustment = Right; + break; +@@ -1229,7 +1232,7 @@ setup_index_header_widths(MAILSTREAM *st + cdesc->ctype != iNothing; + cdesc++) + if(cdesc->ctype == iSize || cdesc->ctype == iKSize || +- cdesc->ctype == iSizeNarrow || ++ cdesc->ctype == iSizeNarrow || cdesc->ctype == iSizeThread || + cdesc->ctype == iSizeComma || cdesc->ctype == iDescripSize){ + if(cdesc->actual_length == 0){ + if((fix=cdesc->width) > 0){ /* had this reserved */ +@@ -1612,10 +1615,12 @@ build_header_work(struct pine *state, MA + + /* find next thread which is visible */ + do{ ++ unsigned long branch; + if(mn_get_revsort(msgmap) && thrd->prevthd) + thrd = fetch_thread(stream, thrd->prevthd); +- else if(!mn_get_revsort(msgmap) && thrd->nextthd) +- thrd = fetch_thread(stream, thrd->nextthd); ++ /*branch = get_branch(stream,thrd)*/ ++ else if(!mn_get_revsort(msgmap) && thrd->branch) ++ thrd = fetch_thread(stream, thrd->branch); + else + thrd = NULL; + } while(thrd +@@ -2027,13 +2032,10 @@ format_index_index_line(INDEXDATA_S *ida + */ + ice = copy_ice(ice); + ++ thrd = fetch_thread(idata->stream, idata->rawno); + /* is this a collapsed thread index line? */ +- if(!idata->bogus && THREADING()){ +- thrd = fetch_thread(idata->stream, idata->rawno); +- collapsed = thrd && thrd->next +- && get_lflag(idata->stream, NULL, +- idata->rawno, MN_COLL); +- } ++ if(!idata->bogus && THREADING()) ++ collapsed = thrd && thread_is_kolapsed(ps_global, idata->stream, ps_global->msgmap, idata->rawno); + + /* calculate contents of the required fields */ + for(cdesc = ps_global->index_disp_format; cdesc->ctype != iNothing; cdesc++) +@@ -2531,7 +2533,30 @@ format_index_index_line(INDEXDATA_S *ida + + break; + ++ case iSizeThread: ++ if (!THREADING()){ ++ goto getsize; ++ } else if (collapsed){ ++ l = count_flags_in_thread(idata->stream, thrd, F_NONE); ++ snprintf(str, sizeof(str), "(%lu)", l); ++ } ++ else{ ++ thrd = fetch_thread(idata->stream, idata->rawno); ++ if(!thrd) ++ snprintf(str, sizeof(str), "%s", "Error"); ++ else{ ++ long lengthb; ++ lengthb = get_length_branch(idata->stream, idata->rawno); ++ if (lengthb > 0L) ++ snprintf(str, sizeof(str), "(%lu)", lengthb); ++ else ++ snprintf(str,sizeof(str), "%s", " "); ++ } ++ } ++ break; ++ + case iSize: ++getsize: + /* 0 ... 9999 */ + if((l = fetch_size(idata)) < 10*1000L) + snprintf(str, sizeof(str), "(%lu)", l); +@@ -5412,10 +5437,8 @@ subj_str(INDEXDATA_S *idata, char *str, + + if(pith_opt_condense_thread_cue) + width = (*pith_opt_condense_thread_cue)(thd, ice, &str, &strsize, width, +- thd && thd->next +- && get_lflag(idata->stream, +- NULL,idata->rawno, +- MN_COLL)); ++ this_thread_is_kolapsed(ps_global, idata->stream, ps_global->msgmap, idata->rawno) && ++ (count_thread(ps_global,idata->stream, ps_global->msgmap, idata->rawno) != 1)); + + /* + * width is < available strsize and +@@ -6043,11 +6066,8 @@ from_str(IndexColType ctype, INDEXDATA_S + border = str + width; + if(pith_opt_condense_thread_cue) + width = (*pith_opt_condense_thread_cue)(thd, ice, &str, &strsize, width, +- thd && thd->next +- && get_lflag(idata->stream, +- NULL,idata->rawno, +- MN_COLL)); +- ++ this_thread_is_kolapsed(ps_global, idata->stream, ps_global->msgmap, idata->rawno) && ++ (count_thread(ps_global,idata->stream, ps_global->msgmap, idata->rawno) != 1)); + fptr = str; + + if(thd) +Index: alpine-2.11/pith/pattern.c +=================================================================== +--- alpine-2.11.orig/pith/pattern.c ++++ alpine-2.11/pith/pattern.c +@@ -1756,7 +1756,7 @@ parse_action_slash(char *str, ACTION_S * + SortOrder def_sort; + int def_sort_rev; + +- if(decode_sort(p, &def_sort, &def_sort_rev) != -1){ ++ if(decode_sort(p, &def_sort, &def_sort_rev, 0) != -1){ + action->sort_is_set = 1; + action->sortorder = def_sort; + action->revsort = (def_sort_rev ? 1 : 0); +Index: alpine-2.11/pith/pine.hlp +=================================================================== +--- alpine-2.11.orig/pith/pine.hlp ++++ alpine-2.11/pith/pine.hlp +@@ -3596,6 +3596,7 @@ There are also additional details on +

  • OPTION: +
  • OPTION: +
  • OPTION: ++
  • OPTION: +
  • OPTION: +
  • OPTION: +
  • OPTION: +@@ -5528,6 +5529,163 @@ the names of the carbon copy addresses o + <End of help on this topic> + + ++======= h_thread_index_sort_arrival ======= ++ ++ ++SORT OPTION: Arrival ++ ++ ++

    SORT OPTION: Arrival

    ++ ++The Arrival sort option arranges threads according to the last ++time that a message was added to it. In this order the last thread ++contains the most recent message in the folder. ++ ++

    ++<End of help on this topic> ++ ++ ++======= h_thread_index_sort_date ======= ++ ++ ++SORT OPTION: Date ++ ++ ++

    SORT OPTION: Date

    ++ ++The Date sort option in the THREAD INDEX screen sorts ++threads by the date in which messages were sent. The thread containing the ++last message in this order is displayed last. ++

    ++<End of help on this topic> ++ ++ ++======= h_thread_index_sort_subj ======= ++ ++ ++SORT OPTION: Subject ++ ++ ++

    SORT OPTION: Subject

    ++ ++The Subject sort option has not been defined yet. ++ ++

    ++<End of help on this topic> ++ ++ ++======= h_thread_index_sort_ordsubj ======= ++ ++ ++SORT OPTION: OrderedSubject ++ ++ ++

    SORT OPTION: OrderedSubject

    ++ ++The OrderedSubject sort option in the THREAD INDEX screen is ++the same as sorting by Subject. ++ ++

    ++<End of help on this topic> ++ ++ ++======= h_thread_index_sort_thread ======= ++ ++ ++SORT OPTION: Thread ++ ++ ++

    SORT OPTION: Thread

    ++ ++The Thread sort option in the THREAD INDEX screen sorts all ++messages by the proposed algorithm by Crispin and Murchison. In this ++method of sorting once threads have been isolated they are sorted by the ++date of their parents, or if that is missing, the first message in that ++thread. ++ ++

    ++<End of help on this topic> ++ ++ ++======= h_thread_index_sort_from ======= ++ ++ ++SORT OPTION: From ++ ++ ++

    SORT OPTION: From

    ++ ++The From sort option has not been defined yet. ++ ++

    ++<End of help on this topic> ++ ++ ++======= h_thread_index_sort_size ======= ++ ++ ++SORT OPTION: Size ++ ++ ++

    SORT OPTION: Size

    ++ ++The Size sort option sorts threads by their size (the number ++of messages in the thread). This could be used to find conversations ++where no reply has been sent by any of the participants in the thread ++(e.g. those whose length is equal to one). Longer threads appear ++below shorter ones. ++ ++

    ++<End of help on this topic> ++ ++ ++======= h_thread_index_sort_score ======= ++ ++ ++SORT OPTION: Score ++ ++ ++

    SORT OPTION: Score

    ++ ++The Score sort option means that threads are sorted according to ++the maximum score of a message in that thread. A thread all of whose ++messages contain a smaller score than a message in some other thread is ++placed in an earlier place in the list of messages for that folder; that ++is, threads with the highest scores appear at the bottom of the index ++list. ++ ++

    ++<End of help on this topic> ++ ++ ++======= h_thread_index_sort_to ======= ++ ++ ++SORT OPTION: To ++ ++ ++

    SORT OPTION: To

    ++ ++The To sort option has not been defined yet. ++ ++

    ++<End of help on this topic> ++ ++ ++======= h_thread_index_sort_cc ======= ++ ++ ++SORT OPTION: Cc ++ ++ ++

    SORT OPTION: Cc

    ++ ++The Cc sort option has not been defined yet. ++ ++

    ++<End of help on this topic> ++ ++ + ======= h_index_cmd_whereis ======= + + +@@ -18894,6 +19052,14 @@ The progression of sizes used looks like +

    +

  • + ++
    SIZETHREAD
    ++
    ++This token represents the total size of the thread for a collapsed thread ++or the size of the branch for an expanded thread. The field is omitted for ++messages that are not top of threads nor branches and it defaults to ++the SIZE token when your folders is not sorted by thread. ++
    ++ +
    SIZENARROW
    +
    + This token represents the total size, in bytes, of the message. +@@ -22259,6 +22425,45 @@ command, then it will not be re-sorted u + <End of help on this topic> + + ++====== h_config_thread_sort_key ===== ++ ++ ++OPTION: <!--#echo var="VAR_thread-sort-key--> ++ ++ ++

    OPTION:

    ++ ++This option determines the order in which threads will be displayed. You ++can choose from the options listed below. Each folder is sorted in one of ++the sort orders displayed below first, then the thread containing the last ++message of that sorted list is put at the end of the index. All messages ++of that thread are "removed" from the sorted list and the ++process is repeated with the remaining messages in that list. ++ ++

    ++

    ++ ++

    Each type of sort may also be reversed. Normal default is by ++"Thread". ++ ++

    ++

    ++<End of help on this topic> ++ ++ + ====== h_config_other_startup ===== + + +@@ -30089,6 +30294,23 @@ Reply Use, Forward Use, and Compose Use. + <End of help on this topic> + + ++====== h_config_enhanced_thread ===== ++ ++ ++FEATURE: <!--#echo var="FEAT_enhanced-fancy-thread-support"--> ++ ++ ++

    FEATURE:

    ++ ++If this option is set certain commands in Pine will operate in loose ++threads too. For example, the command ^R marks a thread deleted, but if ++this feature is set, it will remove all threads that share the same missing ++parent with this thread. ++ ++

    ++<End of help on this topic> ++ ++ + ====== h_config_news_cross_deletes ===== + + +Index: alpine-2.11/pith/sort.c +=================================================================== +--- alpine-2.11.orig/pith/sort.c ++++ alpine-2.11/pith/sort.c +@@ -91,7 +91,7 @@ Args: msgmap -- + ----*/ + void + sort_folder(MAILSTREAM *stream, MSGNO_S *msgmap, SortOrder new_sort, +- int new_rev, unsigned int flags) ++ int new_rev, unsigned int flags, int first) + { + long raw_current, i, j; + unsigned long *sort = NULL; +@@ -101,6 +101,15 @@ sort_folder(MAILSTREAM *stream, MSGNO_S + int current_rev; + MESSAGECACHE *mc; + ++ if (first){ ++ if (new_sort == SortThread) ++ find_msgmap(stream, msgmap, flags, ++ ps_global->thread_cur_sort, new_rev); + else -+ collapsed = get_lflag(stream, NULL, rawno, MN_COLL); -+ } -+ -+ return collapsed; -+ } -+ -+ /* -+ * This function assumes that it is called at a top of a thread in its -+ * first call -+ */ -+ -+ int -+ count_this_thread(MAILSTREAM *stream, unsigned long rawno) -+ { -+ unsigned long top, orig_top, topnxt; ++ sort_folder(stream, msgmap, new_sort, new_rev, flags, 0); ++ return; ++ } ++ + dprint((2, "Sorting by %s%s\n", + sort_name(new_sort), new_rev ? "/reverse" : "")); + +@@ -530,20 +539,20 @@ percent_sorted(void) + * argument also means arrival/reverse. + */ + int +-decode_sort(char *sort_spec, SortOrder *def_sort, int *def_sort_rev) ++decode_sort(char *sort_spec, SortOrder *def_sort, int *def_sort_rev, int thread) + { + char *sep; + char *fix_this = NULL; +- int x, reverse; ++ int x = 0, reverse; + + if(!sort_spec || !*sort_spec){ +- *def_sort = SortArrival; ++ *def_sort = thread ? SortThread : SortArrival; + *def_sort_rev = 0; + return(0); + } + + if(struncmp(sort_spec, "reverse", strlen(sort_spec)) == 0){ +- *def_sort = SortArrival; ++ *def_sort = thread ? SortThread : SortArrival; + *def_sort_rev = 1; + return(0); + } +@@ -572,7 +581,7 @@ decode_sort(char *sort_spec, SortOrder * + if(ps_global->sort_types[x] == EndofList) + return(-1); + +- *def_sort = ps_global->sort_types[x]; ++ *def_sort = ps_global->sort_types[x]; + *def_sort_rev = reverse; + return(0); + } +@@ -689,7 +698,9 @@ reset_sort_order(unsigned int flags) + + /* set default order */ + the_sort_order = ps_global->def_sort; +- sort_is_rev = ps_global->def_sort_rev; ++ sort_is_rev = the_sort_order == SortThread ++ ? (ps_global->thread_def_sort_rev + ps_global->def_sort_rev) % 2 ++ : ps_global->def_sort_rev; + + if(ps_global->mail_stream && nonempty_patterns(rflags, &pstate)){ + for(pat = first_pattern(&pstate); pat; pat = next_pattern(&pstate)){ +@@ -702,9 +713,15 @@ reset_sort_order(unsigned int flags) + && pat->action->sort_is_set){ + the_sort_order = pat->action->sortorder; + sort_is_rev = pat->action->revsort; ++ sort_is_rev = the_sort_order == SortThread ++ ? (ps_global->thread_def_sort_rev + pat->action->revsort) % 2 ++ : pat->action->revsort; + } + } + ++ if(the_sort_order == SortThread && !(flags & SRT_MAN)) ++ ps_global->thread_cur_sort = ps_global->thread_def_sort; ++ + sort_folder(ps_global->mail_stream, ps_global->msgmap, +- the_sort_order, sort_is_rev, flags); ++ the_sort_order, sort_is_rev, flags, 1); + } +Index: alpine-2.11/pith/sort.h +=================================================================== +--- alpine-2.11.orig/pith/sort.h ++++ alpine-2.11/pith/sort.h +@@ -22,7 +22,7 @@ + + + #define refresh_sort(S,M,F) sort_folder((S), (M), mn_get_sort(M), \ +- mn_get_revsort(M), (F)) ++ mn_get_revsort(M), (F), 1) + + struct global_sort_data { + MSGNO_S *msgmap; +@@ -41,8 +41,8 @@ extern struct global_sort_data g_sort; + + /* exported protoypes */ + char *sort_name(SortOrder); +-void sort_folder(MAILSTREAM *, MSGNO_S *, SortOrder, int, unsigned); +-int decode_sort(char *, SortOrder *, int *); ++void sort_folder(MAILSTREAM *, MSGNO_S *, SortOrder, int, unsigned, int); ++int decode_sort(char *, SortOrder *, int *, int); + void reset_sort_order(unsigned); + + +Index: alpine-2.11/pith/state.c +=================================================================== +--- alpine-2.11.orig/pith/state.c ++++ alpine-2.11/pith/state.c +@@ -74,6 +74,7 @@ new_pine_struct(void) + + p = (struct pine *)fs_get(sizeof (struct pine)); + memset((void *) p, 0, sizeof(struct pine)); ++ p->thread_def_sort = SortDate; + p->def_sort = SortArrival; + p->sort_types[0] = SortSubject; + p->sort_types[1] = SortArrival; +Index: alpine-2.11/pith/state.h +=================================================================== +--- alpine-2.11.orig/pith/state.h ++++ alpine-2.11/pith/state.h +@@ -137,6 +137,8 @@ struct pine { + unsigned unseen_in_view:1; + unsigned start_in_context:1; /* start fldr_scrn in current cntxt */ + unsigned def_sort_rev:1; /* true if reverse sort is default */ ++ unsigned thread_def_sort_rev:1; /* true if reverse sort is default in thread screen */ ++ unsigned msgmap_thread_def_sort_rev:1; /* true if reverse sort is being used in thread screen */ + unsigned restricted:1; + + unsigned tcptimeout:1; /* a tcp timeout is in progress */ +@@ -288,6 +290,9 @@ struct pine { + EditWhich ew_for_srch_take; + + SortOrder def_sort, /* Default sort type */ ++ thread_def_sort, /* Default Sort Type in Thread Screen */ ++ thread_cur_sort, /* current sort style for threads */ ++ msgmap_thread_sort, + sort_types[22]; + + int preserve; +Index: alpine-2.11/pith/thread.c +=================================================================== +--- alpine-2.11.orig/pith/thread.c ++++ alpine-2.11/pith/thread.c +@@ -30,12 +30,18 @@ static char rcsid[] = "$Id: thread.c 942 + #include "../pith/mailcmd.h" + #include "../pith/ablookup.h" + ++static int erase_thread_info = 1; ++ ++typedef struct sizethread_t { ++ int count; ++ long pos; ++} SIZETHREAD_T; + + /* + * Internal prototypes + */ + long *sort_thread_flatten(THREADNODE *, MAILSTREAM *, long *, +- char *, long, PINETHRD_S *, unsigned); ++ char *, long, PINETHRD_S *, unsigned, int, long, long); + void make_thrdflags_consistent(MAILSTREAM *, MSGNO_S *, PINETHRD_S *, int); + THREADNODE *collapse_threadnode_tree(THREADNODE *); + THREADNODE *collapse_threadnode_tree_sorted(THREADNODE *); +@@ -43,6 +49,7 @@ THREADNODE *sort_threads_and_collapse( + THREADNODE *insert_tree_in_place(THREADNODE *, THREADNODE *); + unsigned long branch_greatest_num(THREADNODE *, int); + long calculate_visible_threads(MAILSTREAM *); ++int pine_compare_size_thread(const qsort_t *, const qsort_t *); + + + PINETHRD_S * +@@ -95,20 +102,22 @@ void + set_flags_for_thread(MAILSTREAM *stream, MSGNO_S *msgmap, int f, PINETHRD_S *thrd, int v) + { + PINETHRD_S *nthrd, *bthrd; ++ unsigned long next = 0L, branch = 0L; + + if(!(stream && thrd && msgmap)) + return; + + set_lflag(stream, msgmap, mn_raw2m(msgmap, thrd->rawno), f, v); + +- if(thrd->next){ +- nthrd = fetch_thread(stream, thrd->next); ++ if(next = get_next(stream,thrd)){ ++ nthrd = fetch_thread(stream, next); + if(nthrd) + set_flags_for_thread(stream, msgmap, f, nthrd, v); + } + +- if(thrd->branch){ +- bthrd = fetch_thread(stream, thrd->branch); ++ ++ if(branch = get_branch(stream, thrd)){ ++ bthrd = fetch_thread(stream, branch); + if(bthrd) + set_flags_for_thread(stream, msgmap, f, bthrd, v); + } +@@ -122,7 +131,7 @@ erase_threading_info(MAILSTREAM *stream, + MESSAGECACHE *mc; + PINELT_S *peltp; + +- if(!(stream && stream->spare)) ++ if(!(stream && stream->spare) || !erase_thread_info) + return; + + ps_global->view_skipped_index = 0; +@@ -155,7 +164,7 @@ sort_thread_callback(MAILSTREAM *stream, + PINETHRD_S *thrd = NULL; + unsigned long msgno, rawno; + int un_view_thread = 0; +- long raw_current; ++ long raw_current, branch; + char *dup_chk = NULL; + + +@@ -168,10 +177,11 @@ sort_thread_callback(MAILSTREAM *stream, + * way. If the dummy node is at the top-level, then its children are + * promoted to the top-level as separate threads. + */ +- if(F_ON(F_THREAD_SORTS_BY_ARRIVAL, ps_global)) +- collapsed_tree = collapse_threadnode_tree_sorted(tree); +- else +- collapsed_tree = collapse_threadnode_tree(tree); ++ collapsed_tree = F_ON(F_ENHANCED_THREAD, ps_global) ++ ? copy_tree(tree) ++ : (F_ON(F_THREAD_SORTS_BY_ARRIVAL, ps_global) ++ ? collapse_threadnode_tree_sorted(tree) ++ : collapse_threadnode_tree(tree)); + + /* dup_chk is like sort with an origin of 1 */ + dup_chk = (char *) fs_get((mn_get_nmsgs(g_sort.msgmap)+1) * sizeof(char)); +@@ -182,7 +192,7 @@ sort_thread_callback(MAILSTREAM *stream, + (void) sort_thread_flatten(collapsed_tree, stream, + &g_sort.msgmap->sort[1], + dup_chk, mn_get_nmsgs(g_sort.msgmap), +- NULL, THD_TOP); ++ NULL, THD_TOP, 0, 1L, 0L); + + /* reset the inverse array */ + msgno_reset_isort(g_sort.msgmap); +@@ -340,12 +350,14 @@ sort_thread_callback(MAILSTREAM *stream, + else{ + thrd = fetch_head_thread(stream); + while(thrd){ ++ unsigned long raw = thrd->rawno; ++ unsigned long top = top_thread(stream, raw); + /* + * The top-level threads aren't hidden by collapse. + */ + msgno = mn_raw2m(g_sort.msgmap, thrd->rawno); +- if(msgno) +- set_lflag(stream, g_sort.msgmap, msgno, MN_CHID, 0); ++ if(msgno && !get_lflag(stream, NULL,thrd->rawno, MN_COLL)) ++ set_lflag(stream, g_sort.msgmap, msgno, MN_CHID, 0); + + if(thrd->next){ + PINETHRD_S *nthrd; +@@ -359,9 +371,10 @@ sort_thread_callback(MAILSTREAM *stream, + MN_COLL)); + } + +- if(thrd->nextthd) +- thrd = fetch_thread(stream, thrd->nextthd); +- else ++ while (thrd && top_thread(stream, thrd->rawno) == top ++ && thrd->nextthd) ++ thrd = fetch_thread(stream, thrd->nextthd); ++ if (!(thrd && thrd->nextthd)) + thrd = NULL; + } + } +@@ -412,7 +425,7 @@ make_thrdflags_consistent(MAILSTREAM *st + int a_parent_is_collapsed) + { + PINETHRD_S *nthrd, *bthrd; +- unsigned long msgno; ++ unsigned long msgno, next, branch; + + if(!thrd) + return; +@@ -430,8 +443,8 @@ make_thrdflags_consistent(MAILSTREAM *st + set_lflag(stream, msgmap, msgno, MN_CHID, 0); + } + +- if(thrd->next){ +- nthrd = fetch_thread(stream, thrd->next); ++ if(next = get_next(stream, thrd)){ ++ nthrd = fetch_thread(stream, next); + if(nthrd) + make_thrdflags_consistent(stream, msgmap, nthrd, + a_parent_is_collapsed +@@ -440,8 +453,8 @@ make_thrdflags_consistent(MAILSTREAM *st + MN_COLL)); + } + +- if(thrd->branch){ +- bthrd = fetch_thread(stream, thrd->branch); ++ if(branch = get_branch(stream, thrd)){ ++ bthrd = fetch_thread(stream, branch); + if(bthrd) + make_thrdflags_consistent(stream, msgmap, bthrd, + a_parent_is_collapsed); +@@ -488,9 +501,10 @@ calculate_visible_threads(MAILSTREAM *st + long * + sort_thread_flatten(THREADNODE *node, MAILSTREAM *stream, + long *entry, char *dup_chk, long maxno, +- PINETHRD_S *thrd, unsigned int flags) ++ PINETHRD_S *thrd, unsigned int flags, ++ int adopted, long top, long threadno) + { +- PINETHRD_S *newthrd = NULL; ++ PINETHRD_S *newthrd = NULL, *save_thread = NULL; + + if(node){ + if(node->num > 0L && node->num <= maxno){ /* holes happen */ +@@ -498,6 +512,9 @@ sort_thread_flatten(THREADNODE *node, MA + *entry = node->num; + dup_chk[node->num] = 1; + ++ if(adopted == 2) ++ top = node->num; ++ + /* + * Build a richer threading structure that will help us paint + * and operate on threads and subthreads. +@@ -506,20 +523,51 @@ sort_thread_flatten(THREADNODE *node, MA + if(newthrd){ + entry++; + ++ if(adopted == 2) ++ threadno = newthrd->thrdno; ++ if(adopted){ ++ newthrd->toploose = top; ++ newthrd->thrdno = threadno; ++ } ++ adopted = adopted ? 1 : 0; + if(node->next) + entry = sort_thread_flatten(node->next, stream, + entry, dup_chk, maxno, +- newthrd, THD_NEXT); ++ newthrd, THD_NEXT, adopted, top, threadno); + + if(node->branch) + entry = sort_thread_flatten(node->branch, stream, + entry, dup_chk, maxno, + newthrd, +- (flags == THD_TOP) ? THD_TOP +- : THD_BRANCH); ++ ((flags == THD_TOP) ? THD_TOP ++ : THD_BRANCH), ++ adopted, top, threadno); + } + } + } ++ else{ ++ adopted = 2; ++ if(node->next) ++ entry = sort_thread_flatten(node->next, stream, entry, dup_chk, ++ maxno, thrd, THD_TOP, adopted, top, threadno); ++ adopted = 0; ++ if(node->branch){ ++ if(entry){ ++ long *last_entry = entry; ++ ++ do{ ++ last_entry--; ++ save_thread = ((PINELT_S *)mail_elt(stream, *last_entry)->sparep)->pthrd; ++ } while (save_thread->parent != 0L); ++ entry = sort_thread_flatten(node->branch, stream, entry, dup_chk, ++ maxno, save_thread, (flags == THD_TOP ? THD_TOP : THD_BRANCH), ++ adopted, top, threadno); ++ } ++ else ++ entry = sort_thread_flatten(node->branch, stream, entry, dup_chk, ++ maxno, NULL, THD_TOP, adopted, top, threadno); ++ } ++ } + } + + return(entry); +@@ -788,7 +836,7 @@ msgno_thread_info(MAILSTREAM *stream, lo + */ + void + collapse_or_expand(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap, +- long unsigned int msgno) ++ long unsigned int msgno, int display) + { + int collapsed, adjust_current = 0; + PINETHRD_S *thrd = NULL, *nthrd; +@@ -841,7 +889,7 @@ collapse_or_expand(struct pine *state, M + if(!thrd) + return; + +- collapsed = get_lflag(stream, NULL, thrd->rawno, MN_COLL) && thrd->next; ++ collapsed = this_thread_is_kolapsed(ps_global, stream, msgmap, thrd->rawno); + + if(collapsed){ + msgno = mn_raw2m(msgmap, thrd->rawno); +@@ -859,13 +907,13 @@ collapse_or_expand(struct pine *state, M + msgno = mn_raw2m(msgmap, thrd->rawno); + if(msgno > 0L && msgno <= mn_get_total(msgmap)){ + set_lflag(stream, msgmap, msgno, MN_COLL, 1); +- if((nthrd = fetch_thread(stream, thrd->next)) != NULL) ++ if((thrd->next) && ((nthrd = fetch_thread(stream, thrd->next)) != NULL)) + set_thread_subtree(stream, nthrd, msgmap, 1, MN_CHID); + + clear_index_cache_ent(stream, msgno, 0); + } + } +- else ++ else if(display) + q_status_message(SM_ORDER, 0, 1, + _("No thread to collapse or expand on this line")); + +@@ -952,18 +1000,19 @@ count_flags_in_thread(MAILSTREAM *stream + unsigned long count = 0; + PINETHRD_S *nthrd, *bthrd; + MESSAGECACHE *mc; ++ unsigned long next = 0L, branch = 0L; + + if(!thrd || !stream || thrd->rawno < 1L || thrd->rawno > stream->nmsgs) + return count; + +- if(thrd->next){ +- nthrd = fetch_thread(stream, thrd->next); ++ if(next = get_next(stream, thrd)){ ++ nthrd = fetch_thread(stream, next); + if(nthrd) + count += count_flags_in_thread(stream, nthrd, flags); + } + +- if(thrd->branch){ +- bthrd = fetch_thread(stream, thrd->branch); ++ if(branch = get_branch(stream, thrd)){ ++ bthrd = fetch_thread(stream, branch); + if(bthrd) + count += count_flags_in_thread(stream, bthrd, flags); + } +@@ -1051,20 +1100,21 @@ int + mark_msgs_in_thread(MAILSTREAM *stream, PINETHRD_S *thrd, MSGNO_S *msgmap) + { + int count = 0; ++ long next, branch; + PINETHRD_S *nthrd, *bthrd; + MESSAGECACHE *mc; + + if(!thrd || !stream || thrd->rawno < 1L || thrd->rawno > stream->nmsgs) + return count; + +- if(thrd->next){ +- nthrd = fetch_thread(stream, thrd->next); ++ if(next = get_next(stream, thrd)){ ++ nthrd = fetch_thread(stream, next); + if(nthrd) + count += mark_msgs_in_thread(stream, nthrd, msgmap); + } + +- if(thrd->branch){ +- bthrd = fetch_thread(stream, thrd->branch); ++ if(branch = get_branch(stream, thrd)){ ++ bthrd = fetch_thread(stream, branch); + if(bthrd) + count += mark_msgs_in_thread(stream, bthrd, msgmap); + } +@@ -1098,7 +1148,7 @@ set_thread_lflags(MAILSTREAM *stream, PI + /* flags to set or clear */ + /* set or clear? */ + { +- unsigned long msgno; ++ unsigned long msgno, next, branch; + PINETHRD_S *nthrd, *bthrd; + + if(!thrd || !stream || thrd->rawno < 1L || thrd->rawno > stream->nmsgs) +@@ -1122,14 +1172,14 @@ set_thread_lflags(MAILSTREAM *stream, PI + if(msgno > 0L && flags == MN_CHID2 && v == 1) + clear_index_cache_ent(stream, msgno, 0); + +- if(thrd->next){ +- nthrd = fetch_thread(stream, thrd->next); ++ if(next = get_next(stream, thrd)){ ++ nthrd = fetch_thread(stream, next); + if(nthrd) + set_thread_lflags(stream, nthrd, msgmap, flags, v); + } + +- if(thrd->branch){ +- bthrd = fetch_thread(stream, thrd->branch); ++ if(branch = get_branch(stream,thrd)){ ++ bthrd = fetch_thread(stream, branch); + if(bthrd) + set_thread_lflags(stream, bthrd, msgmap, flags, v); + } +@@ -1218,19 +1268,20 @@ to_us_symbol_for_thread(MAILSTREAM *stre + char to_us = ' '; + char branch_to_us = ' '; + PINETHRD_S *nthrd, *bthrd; ++ unsigned long next = 0L, branch = 0L; + MESSAGECACHE *mc; + + if(!thrd || !stream || thrd->rawno < 1L || thrd->rawno > stream->nmsgs) + return to_us; + +- if(thrd->next){ +- nthrd = fetch_thread(stream, thrd->next); ++ if(next = get_next(stream,thrd)){ ++ nthrd = fetch_thread(stream, next); + if(nthrd) + to_us = to_us_symbol_for_thread(stream, nthrd, consider_flagged); + } + + if(((consider_flagged && to_us != '*') || (!consider_flagged && to_us != '+')) +- && thrd->branch){ ++ && (branch = get_branch(stream, thrd))){ + bthrd = fetch_thread(stream, thrd->branch); + if(bthrd) + branch_to_us = to_us_symbol_for_thread(stream, bthrd, consider_flagged); +@@ -1280,7 +1331,7 @@ to_us_symbol_for_thread(MAILSTREAM *stre + break; + } + +- if(to_us != '+' && resent_to_us(&idata)) ++ if(to_us != '+' && !idata.bogus && resent_to_us(&idata)) + to_us = '+'; + + if(to_us == ' ' && F_ON(F_MARK_FOR_CC,ps_global)) +@@ -1328,7 +1379,8 @@ set_thread_subtree(MAILSTREAM *stream, P + + set_lflag(stream, msgmap, msgno, flags, v); + +- if(thrd->next && (hiding || !get_lflag(stream,NULL,thrd->rawno,MN_COLL))){ ++ if(thrd->next ++ && (hiding || !get_lflag(stream,NULL,thrd->rawno,MN_COLL))){ + nthrd = fetch_thread(stream, thrd->next); + if(nthrd) + set_thread_subtree(stream, nthrd, msgmap, v, flags); +@@ -1368,8 +1420,8 @@ view_thread(struct pine *state, MAILSTRE + if(rawno) + thrd = fetch_thread(stream, rawno); + +- if(thrd && thrd->top && thrd->top != thrd->rawno) +- thrd = fetch_thread(stream, thrd->top); ++ if(thrd && thrd->top && top_thread(stream,thrd->top) != thrd->rawno) ++ thrd = fetch_thread(stream, top_thread(stream,thrd->top)); + + if(!thrd) + return 0; +@@ -1433,7 +1485,7 @@ unview_thread(struct pine *state, MAILST + thrd = fetch_thread(stream, rawno); + + if(thrd && thrd->top) +- topthrd = fetch_thread(stream, thrd->top); ++ topthrd = fetch_thread(stream, top_thread(stream,thrd->top)); + + if(!topthrd) + return 0; +@@ -1539,6 +1591,7 @@ void + set_search_bit_for_thread(MAILSTREAM *stream, PINETHRD_S *thrd, SEARCHSET **msgset) + { + PINETHRD_S *nthrd, *bthrd; ++ unsigned long next, branch; + + if(!(stream && thrd)) + return; +@@ -1547,15 +1600,622 @@ set_search_bit_for_thread(MAILSTREAM *st + && (!(msgset && *msgset) || in_searchset(*msgset, thrd->rawno))) + mm_searched(stream, thrd->rawno); + +- if(thrd->next){ +- nthrd = fetch_thread(stream, thrd->next); ++ if(next= get_next(stream, thrd)){ ++ nthrd = fetch_thread(stream, next); + if(nthrd) + set_search_bit_for_thread(stream, nthrd, msgset); + } + +- if(thrd->branch){ +- bthrd = fetch_thread(stream, thrd->branch); ++ if(branch = get_branch(stream, thrd)){ ++ bthrd = fetch_thread(stream, branch); + if(bthrd) + set_search_bit_for_thread(stream, bthrd, msgset); + } + } ++ ++/* ++ * Make a copy of c-client's THREAD tree ++ */ ++THREADNODE * ++copy_tree(THREADNODE *tree) ++{ ++ THREADNODE *newtree = NULL; ++ ++ if(tree){ ++ newtree = mail_newthreadnode(NULL); ++ newtree->num = tree->num; ++ if(tree->next) ++ newtree->next = copy_tree(tree->next); ++ ++ if(tree->branch) ++ newtree->branch = copy_tree(tree->branch); ++ } ++ return(newtree); ++} ++ ++long ++top_thread(MAILSTREAM *stream, long rawmsgno) ++{ + PINETHRD_S *thrd = NULL; -+ int count = 1; -+ ++ unsigned long rawno; ++ + if(!stream) -+ return 0; -+ -+ if(rawno) -+ thrd = fetch_thread(stream, rawno); -+ ++ return -1L; ++ ++ if(rawmsgno) ++ thrd = fetch_thread(stream, rawmsgno); ++ + if(!thrd) -+ return 0; -+ -+ if (thrd->next) -+ count += count_this_thread(stream, thrd->next); -+ -+ if (thrd->branch) -+ count += count_this_thread(stream, thrd->branch); -+ -+ return count; -+ } -+ -+ int -+ count_thread(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap, long rawno) -+ { -+ unsigned long top, orig, orig_top; ++ return -1L; ++ ++ return F_ON(F_ENHANCED_THREAD, ps_global) ++ ? (thrd->toploose ? thrd->toploose : thrd->top) ++ : thrd->top; ++} ++ ++void ++move_top_thread(MAILSTREAM *stream, MSGNO_S *msgmap, long rawmsgno) ++{ ++ mn_set_cur(msgmap,mn_raw2m(msgmap, top_thread(stream, rawmsgno))); ++} ++ ++long ++top_this_thread(MAILSTREAM *stream, long rawmsgno) ++{ + PINETHRD_S *thrd = NULL; -+ int done = 0, count = 0; -+ ++ unsigned long rawno; ++ + if(!stream) -+ return 0; -+ -+ orig = mn_m2raw(msgmap, mn_get_cur(msgmap)); -+ move_top_thread(stream, msgmap,rawno); -+ top = orig_top = top_thread(stream, rawno); -+ if(top) -+ thrd = fetch_thread(stream, top); -+ ++ return -1L; ++ ++ if(rawmsgno) ++ thrd = fetch_thread(stream, rawmsgno); ++ + if(!thrd) -+ return 0; -+ -+ while (!done){ -+ count += count_this_thread(stream, top); ++ return -1L; ++ ++ return thrd->top; ++} ++ ++void ++move_top_this_thread(MAILSTREAM *stream, MSGNO_S *msgmap, long rawmsgno) ++{ ++ mn_set_cur(msgmap,mn_raw2m(msgmap, top_this_thread(stream, rawmsgno))); ++} ++ ++int ++thread_is_kolapsed(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap, long rawmsgno) ++{ ++ int collapsed; ++ PINETHRD_S *thrd = NULL; ++ unsigned long rawno, orig, orig_rawno; ++ ++ if(!stream) ++ return -1; ++ ++ orig = mn_get_cur(msgmap); ++ move_top_thread(stream, msgmap, rawmsgno); ++ rawno = orig_rawno = mn_m2raw(msgmap, mn_get_cur(msgmap)); ++ if(rawno) ++ thrd = fetch_thread(stream, rawno); ++ ++ if(!thrd) ++ return -1; ++ ++ while(collapsed = this_thread_is_kolapsed(state, stream, msgmap, rawno)) + if (F_OFF(F_ENHANCED_THREAD, state) + || (move_next_this_thread(state, stream, msgmap, 0) <= 0) -+ || !(top = mn_m2raw(msgmap, mn_get_cur(msgmap))) -+ || (orig_top != top_thread(stream, top))) -+ done++; ++ || !(rawno = mn_m2raw(msgmap, mn_get_cur(msgmap))) ++ || (orig_rawno != top_thread(stream, rawno))) ++ break; ++ ++ mn_set_cur(msgmap,orig); /* return home */ ++ ++ return collapsed; ++} ++ ++/* this function tells us if the thread (or branch in the case of loose threads) ++ * is collapsed ++ */ ++ ++int ++this_thread_is_kolapsed(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap, long rawmsgno) ++{ ++ int collapsed; ++ PINETHRD_S *thrd = NULL; ++ unsigned long rawno, orig; ++ ++ if(!stream) ++ return -1; ++ ++ rawno = rawmsgno; ++ if(rawno) ++ thrd = fetch_thread(stream, rawno); ++ ++ if(!thrd) ++ return -1; ++ ++ collapsed = get_lflag(stream, NULL, rawno, MN_COLL | MN_CHID); ++ ++ if (!thrd->next){ ++ if (thrd->rawno != top_thread(stream, thrd->rawno)) ++ collapsed = get_lflag(stream, NULL, rawno, MN_CHID); ++ else ++ collapsed = get_lflag(stream, NULL, rawno, MN_COLL); ++ } ++ ++ return collapsed; ++} ++ ++/* ++ * This function assumes that it is called at a top of a thread in its ++ * first call ++ */ ++ ++int ++count_this_thread(MAILSTREAM *stream, unsigned long rawno) ++{ ++ unsigned long top, orig_top, topnxt; ++ PINETHRD_S *thrd = NULL; ++ int count = 1; ++ ++ if(!stream) ++ return 0; ++ ++ if(rawno) ++ thrd = fetch_thread(stream, rawno); ++ ++ if(!thrd) ++ return 0; ++ ++ if (thrd->next) ++ count += count_this_thread(stream, thrd->next); ++ ++ if (thrd->branch) ++ count += count_this_thread(stream, thrd->branch); ++ ++ return count; ++} ++ ++int ++count_thread(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap, long rawno) ++{ ++ unsigned long top, orig, orig_top; ++ PINETHRD_S *thrd = NULL; ++ int done = 0, count = 0; ++ ++ if(!stream) ++ return 0; ++ ++ orig = mn_m2raw(msgmap, mn_get_cur(msgmap)); ++ move_top_thread(stream, msgmap,rawno); ++ top = orig_top = top_thread(stream, rawno); ++ if(top) ++ thrd = fetch_thread(stream, top); ++ ++ if(!thrd) ++ return 0; ++ ++ while (!done){ ++ count += count_this_thread(stream, top); ++ if (F_OFF(F_ENHANCED_THREAD, state) ++ || (move_next_this_thread(state, stream, msgmap, 0) <= 0) ++ || !(top = mn_m2raw(msgmap, mn_get_cur(msgmap))) ++ || (orig_top != top_thread(stream, top))) ++ done++; ++ } ++ mn_set_cur(msgmap,mn_raw2m(msgmap, orig)); ++ return count; ++} ++ ++unsigned long ++get_branch(MAILSTREAM *stream, PINETHRD_S *thrd) ++{ ++ PINETHRD_S *nthrd = NULL; ++ unsigned long top; ++ ++ if (thrd->toploose && thrd->nextthd) ++ nthrd = fetch_thread(stream, thrd->nextthd); ++ if (!nthrd) ++ return thrd->branch; ++ top = top_thread(stream, thrd->rawno); ++ return thrd->branch ++ ? thrd->branch ++ : (F_ON(F_ENHANCED_THREAD, ps_global) ++ ? (top == top_thread(stream, nthrd->rawno) ? thrd->nextthd : 0L) ++ : 0L); ++} ++ ++unsigned long ++get_next(MAILSTREAM *stream, PINETHRD_S *thrd) ++{ ++ return thrd->next; ++} ++ ++long ++get_length_branch(MAILSTREAM *stream, long rawno) ++{ ++ int branchp = 0, done = 0; ++ long top, count = 1L, raw; ++ PINETHRD_S *thrd, *pthrd = NULL, *nthrd; ++ ++ thrd = fetch_thread(stream, rawno); ++ ++ if (!thrd) ++ return -1L; ++ ++ top = thrd->top; ++ ++ if (thrd->parent) ++ pthrd = fetch_thread(stream, thrd->parent); ++ ++ if (thrd->rawno == top) ++ branchp++; ++ ++ if (!branchp && !pthrd){ /* what!!?? */ ++ raw = top; ++ while (!done){ ++ pthrd = fetch_thread(stream, raw); ++ if ((pthrd->next == rawno) || (pthrd->branch == rawno)) ++ done++; ++ else{ ++ if (pthrd->next) ++ raw = pthrd->next; ++ else if (pthrd->branch) ++ raw = pthrd->branch; ++ } + } -+ mn_set_cur(msgmap,mn_raw2m(msgmap, orig)); -+ return count; -+ } -+ -+ unsigned long -+ get_branch(MAILSTREAM *stream, PINETHRD_S *thrd) -+ { -+ PINETHRD_S *nthrd = NULL; -+ unsigned long top; -+ -+ if (thrd->toploose && thrd->nextthd) -+ nthrd = fetch_thread(stream, thrd->nextthd); -+ if (!nthrd) -+ return thrd->branch; -+ top = top_thread(stream, thrd->rawno); -+ return thrd->branch -+ ? thrd->branch -+ : (F_ON(F_ENHANCED_THREAD, ps_global) -+ ? (top == top_thread(stream, nthrd->rawno) ? thrd->nextthd : 0L) -+ : 0L); -+ } -+ -+ unsigned long -+ get_next(MAILSTREAM *stream, PINETHRD_S *thrd) -+ { -+ return thrd->next; -+ } -+ -+ long -+ get_length_branch(MAILSTREAM *stream, long rawno) -+ { -+ int branchp = 0, done = 0; -+ long top, count = 1L, raw; -+ PINETHRD_S *thrd, *pthrd = NULL, *nthrd; -+ -+ thrd = fetch_thread(stream, rawno); -+ -+ if (!thrd) -+ return -1L; -+ -+ top = thrd->top; -+ -+ if (thrd->parent) -+ pthrd = fetch_thread(stream, thrd->parent); -+ -+ if (thrd->rawno == top) -+ branchp++; -+ -+ if (!branchp && !pthrd){ /* what!!?? */ -+ raw = top; -+ while (!done){ -+ pthrd = fetch_thread(stream, raw); -+ if ((pthrd->next == rawno) || (pthrd->branch == rawno)) -+ done++; -+ else{ -+ if (pthrd->next) -+ raw = pthrd->next; -+ else if (pthrd->branch) -+ raw = pthrd->branch; -+ } -+ } -+ } -+ -+ if (pthrd && pthrd->next == thrd->rawno && thrd->branch) -+ branchp++; -+ -+ if (pthrd && pthrd->next && pthrd->next != thrd->rawno){ -+ nthrd = fetch_thread(stream, pthrd->next); -+ while (nthrd && nthrd->branch && nthrd->branch != thrd->rawno) -+ nthrd = fetch_thread(stream, nthrd->branch); -+ if(nthrd && nthrd->branch && nthrd->branch == thrd->rawno) -+ branchp++; -+ } -+ -+ if(branchp){ -+ int entry = 0; -+ while(thrd && thrd->next){ -+ entry = 1; -+ count++; -+ thrd = fetch_thread(stream, thrd->next); -+ if (thrd->branch) -+ break; -+ } -+ if (entry && thrd->branch) -+ count--; -+ } -+ return branchp ? (count ? count : 1L) : 0L; -+ } -+ -+ int pine_compare_size_thread(const qsort_t *a, const qsort_t *b) -+ { -+ SIZETHREAD_T *s = (SIZETHREAD_T *) a, *t = (SIZETHREAD_T *) b; -+ -+ return s->count == t->count ? s->pos - t->pos : s->count - t->count; -+ } -+ -+ -+ -+ void -+ find_msgmap(MAILSTREAM *stream, MSGNO_S *msgmap, int flags, SortOrder ordersort, unsigned is_rev) -+ { -+ long *old_arrival,*new_arrival; -+ long init_thread, end_thread, current; -+ long i, j, k; -+ long tmsg, ntmsg, nthreads; -+ SIZETHREAD_T *l; -+ PINETHRD_S *thrd; -+ -+ erase_thread_info = 0; -+ current = mn_m2raw(msgmap, mn_get_cur(msgmap)); -+ -+ switch(ordersort){ -+ case SortSize: -+ sort_folder(stream, msgmap, SortThread, 0, SRT_VRB, 0); -+ tmsg = mn_get_total(msgmap) + 1; -+ -+ if(tmsg <= 1) -+ return; -+ -+ for (i= 1L, k = 0L; i <= mn_get_total(msgmap); i += count_thread(ps_global, stream, msgmap, msgmap->sort[i]), k++); -+ l = (SIZETHREAD_T *) fs_get(k*sizeof(SIZETHREAD_T)); -+ for (j = 0L, i=1L; j < k && i<= mn_get_total(msgmap); ){ -+ l[j].count = count_thread(ps_global, stream, msgmap, msgmap->sort[i]); -+ l[j].pos = i; -+ i += l[j].count; -+ j++; -+ } -+ qsort((void *)l, (size_t) k, sizeof(SIZETHREAD_T), pine_compare_size_thread); -+ old_arrival = (long *) fs_get(tmsg * sizeof(long)); -+ for(i = 1L, j = 0; j < k; j++){ /* copy thread of length .count */ -+ int p; -+ for(p = 0; p < l[j].count; p++) -+ old_arrival[i++] = msgmap->sort[l[j].pos + p]; -+ } -+ fs_give((void **)&l); -+ break; -+ default: -+ sort_folder(stream, msgmap, ordersort, 0, SRT_VRB, 0); -+ tmsg = mn_get_total(msgmap) + 1; -+ -+ if (tmsg <= 1) -+ return; -+ -+ old_arrival = (long *) fs_get(tmsg * sizeof(long)); -+ for (i= 1L;(i <= mn_get_total(msgmap)) && (old_arrival[i] = msgmap->sort[i]); i++); -+ /* sort by thread */ -+ sort_folder(stream, msgmap, SortThread, 0, SRT_VRB, 0); -+ break; -+ ++ } ++ ++ if (pthrd && pthrd->next == thrd->rawno && thrd->branch) ++ branchp++; ++ ++ if (pthrd && pthrd->next && pthrd->next != thrd->rawno){ ++ nthrd = fetch_thread(stream, pthrd->next); ++ while (nthrd && nthrd->branch && nthrd->branch != thrd->rawno) ++ nthrd = fetch_thread(stream, nthrd->branch); ++ if(nthrd && nthrd->branch && nthrd->branch == thrd->rawno) ++ branchp++; ++ } ++ ++ if(branchp){ ++ int entry = 0; ++ while(thrd && thrd->next){ ++ entry = 1; ++ count++; ++ thrd = fetch_thread(stream, thrd->next); ++ if (thrd->branch) ++ break; + } -+ -+ ntmsg = mn_get_total(msgmap) + 1; -+ if (tmsg != ntmsg){ /* oh oh, something happened, we better try again */ -+ fs_give((void **)&old_arrival); -+ find_msgmap(stream, msgmap, flags, ordersort, is_rev); -+ return; -+ } -+ -+ /* reconstruct the msgmap */ -+ -+ new_arrival = (long *) fs_get(tmsg * sizeof(long)); -+ memset(new_arrival, 0, tmsg*sizeof(long)); -+ i = mn_get_total(msgmap); -+ /* we copy from the bottom, the last one to be filled is new_arrival[1] */ -+ while (new_arrival[1] == 0){ -+ int done = 0; -+ long n; -+ -+ init_thread = top_thread(stream, old_arrival[i]); -+ thrd = fetch_thread(stream, init_thread); -+ for (n = mn_get_total(msgmap); new_arrival[n] != 0 && !done; n--) -+ done = (new_arrival[n] == init_thread); -+ if (!done){ -+ mn_set_cur(msgmap, mn_raw2m(msgmap, init_thread)); -+ if(move_next_thread(ps_global, stream, msgmap, 0) <= 0) -+ j = mn_get_total(msgmap) - mn_raw2m(msgmap, init_thread) + 1; -+ else -+ j = mn_get_cur(msgmap) - mn_raw2m(msgmap, init_thread); -+ end_thread = mn_raw2m(msgmap, init_thread) + j; -+ for(k = 1L; k <= j; k++) -+ new_arrival[tmsg - k] = msgmap->sort[end_thread - k]; -+ tmsg -= j; -+ } -+ i--; -+ } -+ relink_threads(stream, msgmap, new_arrival); -+ for (i = 1; (i <= mn_get_total(msgmap)) -+ && (msgmap->sort[i] = new_arrival[i]); i++); -+ msgno_reset_isort(msgmap); -+ -+ fs_give((void **)&new_arrival); -+ fs_give((void **)&old_arrival); -+ -+ -+ if(is_rev && (mn_get_total(msgmap) > 1L)){ -+ long *rev_sort; -+ long i = 1L, l = mn_get_total(msgmap); -+ -+ rev_sort = (long *) fs_get((mn_get_total(msgmap)+1L) * sizeof(long)); -+ memset(rev_sort, 0, (mn_get_total(msgmap)+1L)*sizeof(long)); -+ while (l > 0L){ -+ if (top_thread(stream, msgmap->sort[l]) == msgmap->sort[l]){ -+ long init_thread = msgmap->sort[l]; -+ long j, k; -+ -+ mn_set_cur(msgmap, mn_raw2m(msgmap, init_thread)); -+ if (move_next_thread(ps_global, stream, msgmap, 0) <= 0) -+ j = mn_get_total(msgmap) - mn_raw2m(msgmap, init_thread) + 1; -+ else -+ j = mn_get_cur(msgmap) - mn_raw2m(msgmap, init_thread); -+ for (k = 0L; (k < j) && (rev_sort[i+k] = msgmap->sort[l+k]); k++); -+ i += j; -+ } -+ l--; -+ } -+ relink_threads(stream, msgmap, rev_sort); -+ for (i = 1L; i <= mn_get_total(msgmap); i++) -+ msgmap->sort[i] = rev_sort[i]; -+ msgno_reset_isort(msgmap); -+ fs_give((void **)&rev_sort); -+ } -+ mn_reset_cur(msgmap, first_sorted_flagged(is_rev ? F_NONE : F_SRCHBACK, -+ stream, mn_raw2m(msgmap, current), FSF_SKIP_CHID)); -+ msgmap->top = -1L; -+ -+ sp_set_unsorted_newmail(ps_global->mail_stream, 0); -+ -+ for(i = 1L; i <= ps_global->mail_stream->nmsgs; i++) -+ mail_elt(ps_global->mail_stream, i)->spare7 = 0; -+ -+ mn_set_sort(msgmap, SortThread); -+ mn_set_revsort(msgmap, is_rev); -+ erase_thread_info = 1; -+ clear_index_cache(stream, 0); -+ } -+ -+ void -+ move_thread(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap, int direction) -+ { -+ long new_cursor, old_cursor = mn_get_cur(msgmap); -+ int rv; ++ if (entry && thrd->branch) ++ count--; ++ } ++ return branchp ? (count ? count : 1L) : 0L; ++} ++ ++int pine_compare_size_thread(const qsort_t *a, const qsort_t *b) ++{ ++ SIZETHREAD_T *s = (SIZETHREAD_T *) a, *t = (SIZETHREAD_T *) b; ++ ++ return s->count == t->count ? s->pos - t->pos : s->count - t->count; ++} ++ ++ ++ ++void ++find_msgmap(MAILSTREAM *stream, MSGNO_S *msgmap, int flags, SortOrder ordersort, unsigned is_rev) ++{ ++ long *old_arrival,*new_arrival; ++ long init_thread, end_thread, current; ++ long i, j, k; ++ long tmsg, ntmsg, nthreads; ++ SIZETHREAD_T *l; + PINETHRD_S *thrd; + -+ rv = direction > 0 ? move_next_thread(state, stream, msgmap, 1): -+ move_prev_thread(state, stream, msgmap, 1); -+ if (rv > 0 && THRD_INDX_ENABLED()){ -+ new_cursor = mn_get_cur(msgmap); -+ mn_set_cur(msgmap, old_cursor); -+ unview_thread(state, stream, msgmap); -+ thrd = fetch_thread(stream,mn_m2raw(msgmap, new_cursor)); -+ mn_set_cur(msgmap, new_cursor); -+ view_thread(state, stream, msgmap, 1); -+ state->next_screen = SCREEN_FUN_NULL; -+ } -+ } -+ -+ void -+ relink_threads(MAILSTREAM *stream, MSGNO_S *msgmap, long *new_arrival) -+ { -+ long last_thread = 0L; -+ long i = 0L, j = 1L, k; -+ PINETHRD_S *thrd, *nthrd; -+ -+ while (j <= mn_get_total(msgmap)){ -+ i++; -+ thrd = fetch_thread(stream, new_arrival[j]); -+ if (!thrd) /* sort failed!, better leave from here now!!! */ -+ break; -+ thrd->prevthd = last_thread; -+ thrd->thrdno = i; -+ thrd->head = new_arrival[1]; -+ last_thread = thrd->rawno; -+ mn_set_cur(msgmap, mn_raw2m(msgmap,thrd->top)); -+ k = mn_get_cur(msgmap); -+ if (move_next_thread(ps_global, stream, msgmap, 0) <= 0) -+ j += mn_get_total(msgmap) + 1 - k; -+ else -+ j += mn_get_cur(msgmap) - k; -+ if (!thrd->toploose) -+ thrd->nextthd = (j <= mn_get_total(msgmap)) ? new_arrival[j] : 0L; -+ else{ -+ int done = 0; -+ while(thrd->nextthd && !done){ -+ thrd->thrdno = i; -+ thrd->head = new_arrival[1]; -+ if (thrd->nextthd) -+ nthrd = fetch_thread(stream, thrd->nextthd); -+ else -+ done++; -+ if(top_thread(stream, thrd->rawno) == top_thread(stream, nthrd->rawno)) -+ thrd = nthrd; -+ else -+ done++; -+ } -+ thrd->nextthd = (j <= mn_get_total(msgmap)) ? new_arrival[j] : 0L; -+ last_thread = thrd->rawno; -+ } -+ } -+ } -+ -+ int -+ move_next_this_thread(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap, int display) -+ { -+ PINETHRD_S *thrd = NULL, *thrdnxt; -+ unsigned long rawno, top; -+ int rv = 1; -+ -+ if(!stream) -+ return -1; -+ -+ rawno = mn_m2raw(msgmap, mn_get_cur(msgmap)); -+ if(rawno) -+ thrd = fetch_thread(stream, rawno); -+ -+ if(!thrd) ++ erase_thread_info = 0; ++ current = mn_m2raw(msgmap, mn_get_cur(msgmap)); ++ ++ switch(ordersort){ ++ case SortSize: ++ sort_folder(stream, msgmap, SortThread, 0, SRT_VRB, 0); ++ tmsg = mn_get_total(msgmap) + 1; ++ ++ if(tmsg <= 1) ++ return; ++ ++ for (i= 1L, k = 0L; i <= mn_get_total(msgmap); i += count_thread(ps_global, stream, msgmap, msgmap->sort[i]), k++); ++ l = (SIZETHREAD_T *) fs_get(k*sizeof(SIZETHREAD_T)); ++ for (j = 0L, i=1L; j < k && i<= mn_get_total(msgmap); ){ ++ l[j].count = count_thread(ps_global, stream, msgmap, msgmap->sort[i]); ++ l[j].pos = i; ++ i += l[j].count; ++ j++; ++ } ++ qsort((void *)l, (size_t) k, sizeof(SIZETHREAD_T), pine_compare_size_thread); ++ old_arrival = (long *) fs_get(tmsg * sizeof(long)); ++ for(i = 1L, j = 0; j < k; j++){ /* copy thread of length .count */ ++ int p; ++ for(p = 0; p < l[j].count; p++) ++ old_arrival[i++] = msgmap->sort[l[j].pos + p]; ++ } ++ fs_give((void **)&l); ++ break; ++ default: ++ sort_folder(stream, msgmap, ordersort, 0, SRT_VRB, 0); ++ tmsg = mn_get_total(msgmap) + 1; ++ ++ if (tmsg <= 1) ++ return; ++ ++ old_arrival = (long *) fs_get(tmsg * sizeof(long)); ++ for (i= 1L;(i <= mn_get_total(msgmap)) && (old_arrival[i] = msgmap->sort[i]); i++); ++ /* sort by thread */ ++ sort_folder(stream, msgmap, SortThread, 0, SRT_VRB, 0); ++ break; ++ ++ } ++ ++ ntmsg = mn_get_total(msgmap) + 1; ++ if (tmsg != ntmsg){ /* oh oh, something happened, we better try again */ ++ fs_give((void **)&old_arrival); ++ find_msgmap(stream, msgmap, flags, ordersort, is_rev); ++ return; ++ } ++ ++ /* reconstruct the msgmap */ ++ ++ new_arrival = (long *) fs_get(tmsg * sizeof(long)); ++ memset(new_arrival, 0, tmsg*sizeof(long)); ++ i = mn_get_total(msgmap); ++ /* we copy from the bottom, the last one to be filled is new_arrival[1] */ ++ while (new_arrival[1] == 0){ ++ int done = 0; ++ long n; ++ ++ init_thread = top_thread(stream, old_arrival[i]); ++ thrd = fetch_thread(stream, init_thread); ++ for (n = mn_get_total(msgmap); new_arrival[n] != 0 && !done; n--) ++ done = (new_arrival[n] == init_thread); ++ if (!done){ ++ mn_set_cur(msgmap, mn_raw2m(msgmap, init_thread)); ++ if(move_next_thread(ps_global, stream, msgmap, 0) <= 0) ++ j = mn_get_total(msgmap) - mn_raw2m(msgmap, init_thread) + 1; ++ else ++ j = mn_get_cur(msgmap) - mn_raw2m(msgmap, init_thread); ++ end_thread = mn_raw2m(msgmap, init_thread) + j; ++ for(k = 1L; k <= j; k++) ++ new_arrival[tmsg - k] = msgmap->sort[end_thread - k]; ++ tmsg -= j; ++ } ++ i--; ++ } ++ relink_threads(stream, msgmap, new_arrival); ++ for (i = 1; (i <= mn_get_total(msgmap)) ++ && (msgmap->sort[i] = new_arrival[i]); i++); ++ msgno_reset_isort(msgmap); ++ ++ fs_give((void **)&new_arrival); ++ fs_give((void **)&old_arrival); ++ ++ ++ if(is_rev && (mn_get_total(msgmap) > 1L)){ ++ long *rev_sort; ++ long i = 1L, l = mn_get_total(msgmap); ++ ++ rev_sort = (long *) fs_get((mn_get_total(msgmap)+1L) * sizeof(long)); ++ memset(rev_sort, 0, (mn_get_total(msgmap)+1L)*sizeof(long)); ++ while (l > 0L){ ++ if (top_thread(stream, msgmap->sort[l]) == msgmap->sort[l]){ ++ long init_thread = msgmap->sort[l]; ++ long j, k; ++ ++ mn_set_cur(msgmap, mn_raw2m(msgmap, init_thread)); ++ if (move_next_thread(ps_global, stream, msgmap, 0) <= 0) ++ j = mn_get_total(msgmap) - mn_raw2m(msgmap, init_thread) + 1; ++ else ++ j = mn_get_cur(msgmap) - mn_raw2m(msgmap, init_thread); ++ for (k = 0L; (k < j) && (rev_sort[i+k] = msgmap->sort[l+k]); k++); ++ i += j; ++ } ++ l--; ++ } ++ relink_threads(stream, msgmap, rev_sort); ++ for (i = 1L; i <= mn_get_total(msgmap); i++) ++ msgmap->sort[i] = rev_sort[i]; ++ msgno_reset_isort(msgmap); ++ fs_give((void **)&rev_sort); ++ } ++ mn_reset_cur(msgmap, first_sorted_flagged(is_rev ? F_NONE : F_SRCHBACK, ++ stream, mn_raw2m(msgmap, current), FSF_SKIP_CHID)); ++ msgmap->top = -1L; ++ ++ sp_set_unsorted_newmail(ps_global->mail_stream, 0); ++ ++ for(i = 1L; i <= ps_global->mail_stream->nmsgs; i++) ++ mail_elt(ps_global->mail_stream, i)->spare7 = 0; ++ ++ mn_set_sort(msgmap, SortThread); ++ mn_set_revsort(msgmap, is_rev); ++ erase_thread_info = 1; ++ clear_index_cache(stream, 0); ++} ++ ++void ++move_thread(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap, int direction) ++{ ++ long new_cursor, old_cursor = mn_get_cur(msgmap); ++ int rv; ++ PINETHRD_S *thrd; ++ ++ rv = direction > 0 ? move_next_thread(state, stream, msgmap, 1): ++ move_prev_thread(state, stream, msgmap, 1); ++ if (rv > 0 && THRD_INDX_ENABLED()){ ++ new_cursor = mn_get_cur(msgmap); ++ mn_set_cur(msgmap, old_cursor); ++ unview_thread(state, stream, msgmap); ++ thrd = fetch_thread(stream,mn_m2raw(msgmap, new_cursor)); ++ mn_set_cur(msgmap, new_cursor); ++ view_thread(state, stream, msgmap, 1); ++ state->next_screen = SCREEN_FUN_NULL; ++ } ++} ++ ++void ++relink_threads(MAILSTREAM *stream, MSGNO_S *msgmap, long *new_arrival) ++{ ++ long last_thread = 0L; ++ long i = 0L, j = 1L, k; ++ PINETHRD_S *thrd, *nthrd; ++ ++ while (j <= mn_get_total(msgmap)){ ++ i++; ++ thrd = fetch_thread(stream, new_arrival[j]); ++ if (!thrd) /* sort failed!, better leave from here now!!! */ ++ break; ++ thrd->prevthd = last_thread; ++ thrd->thrdno = i; ++ thrd->head = new_arrival[1]; ++ last_thread = thrd->rawno; ++ mn_set_cur(msgmap, mn_raw2m(msgmap,thrd->top)); ++ k = mn_get_cur(msgmap); ++ if (move_next_thread(ps_global, stream, msgmap, 0) <= 0) ++ j += mn_get_total(msgmap) + 1 - k; ++ else ++ j += mn_get_cur(msgmap) - k; ++ if (!thrd->toploose) ++ thrd->nextthd = (j <= mn_get_total(msgmap)) ? new_arrival[j] : 0L; ++ else{ ++ int done = 0; ++ while(thrd->nextthd && !done){ ++ thrd->thrdno = i; ++ thrd->head = new_arrival[1]; ++ if (thrd->nextthd) ++ nthrd = fetch_thread(stream, thrd->nextthd); ++ else ++ done++; ++ if(top_thread(stream, thrd->rawno) == top_thread(stream, nthrd->rawno)) ++ thrd = nthrd; ++ else ++ done++; ++ } ++ thrd->nextthd = (j <= mn_get_total(msgmap)) ? new_arrival[j] : 0L; ++ last_thread = thrd->rawno; ++ } ++ } ++} ++ ++int ++move_next_this_thread(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap, int display) ++{ ++ PINETHRD_S *thrd = NULL, *thrdnxt; ++ unsigned long rawno, top; ++ int rv = 1; ++ ++ if(!stream) + return -1; -+ -+ top = top_thread(stream, rawno); -+ -+ thrdnxt = (top == rawno) ? fetch_thread(stream, top) : thrd; -+ if (thrdnxt->nextthd) -+ mn_set_cur(msgmap,mn_raw2m(msgmap, thrdnxt->nextthd)); -+ else{ -+ rv = 0; -+ if (display) -+ q_status_message(SM_ORDER, 0, 1, "No more Threads to advance"); -+ } -+ return rv; -+ } -+ -+ int -+ move_next_thread(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap, int display) -+ { -+ int collapsed, rv = 1, done = 0; -+ PINETHRD_S *thrd = NULL; -+ unsigned long orig, orig_top, top; -+ -+ if(!stream) -+ return 0; -+ -+ orig = mn_m2raw(msgmap, mn_get_cur(msgmap)); -+ move_top_thread(stream, msgmap,orig); -+ top = orig_top = mn_m2raw(msgmap, mn_get_cur(msgmap)); -+ -+ if(top) -+ thrd = fetch_thread(stream, top); -+ -+ if(!thrd) -+ return 0; -+ -+ while (rv > 0 && !done){ -+ rv = move_next_this_thread(state, stream, msgmap, display); -+ if (F_OFF(F_ENHANCED_THREAD, state) -+ || !(top = mn_m2raw(msgmap, mn_get_cur(msgmap))) -+ || (orig_top != top_thread(stream, top))) -+ done++; -+ } -+ if (display){ -+ if (rv > 0 && SEP_THRDINDX()) -+ q_status_message(SM_ORDER, 0, 2, "Viewing next thread"); -+ if (!rv) -+ q_status_message(SM_ORDER, 0, 2, "No more threads to advance"); -+ } -+ if(rv <= 0){ -+ rv = 0; -+ mn_set_cur(msgmap, mn_raw2m(msgmap, orig)); -+ } -+ -+ return rv; -+ } -+ -+ int -+ move_prev_thread(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap, int display) -+ { -+ PINETHRD_S *thrd = NULL; -+ unsigned long rawno, top; -+ int rv = 1; -+ -+ if(!stream) -+ return -1; -+ -+ rawno = mn_m2raw(msgmap, mn_get_cur(msgmap)); -+ if(rawno) -+ thrd = fetch_thread(stream, rawno); -+ -+ if(!thrd) -+ return -1; -+ -+ top = top_thread(stream, rawno); -+ -+ if (top != rawno) -+ mn_set_cur(msgmap,mn_raw2m(msgmap, top)); -+ else if (thrd->prevthd) -+ mn_set_cur(msgmap,mn_raw2m(msgmap, top_thread(stream,thrd->prevthd))); -+ else ++ ++ rawno = mn_m2raw(msgmap, mn_get_cur(msgmap)); ++ if(rawno) ++ thrd = fetch_thread(stream, rawno); ++ ++ if(!thrd) ++ return -1; ++ ++ top = top_thread(stream, rawno); ++ ++ thrdnxt = (top == rawno) ? fetch_thread(stream, top) : thrd; ++ if (thrdnxt->nextthd) ++ mn_set_cur(msgmap,mn_raw2m(msgmap, thrdnxt->nextthd)); ++ else{ + rv = 0; -+ if (display){ -+ if (rv && SEP_THRDINDX()) -+ q_status_message(SM_ORDER, 0, 2, "Viewing previous thread"); -+ if (!rv) -+ q_status_message(SM_ORDER, 0, 2, "No more threads to go back"); -+ } -+ -+ return rv; -+ } -+ -+ /* add more keys to this list */ -+ int -+ allowed_thread_key(SortOrder sort) -+ { -+ return sort == SortArrival || sort == SortDate -+ || sort == SortScore || sort == SortThread -+ || sort == SortSize; -+ } -+ -diff -rc alpine-2.10/pith/thread.h alpine-2.10.fancy/pith/thread.h -*** alpine-2.10/pith/thread.h 2013-01-11 11:26:44.000000000 -0700 ---- alpine-2.10.fancy/pith/thread.h 2013-01-11 20:43:05.000000000 -0700 -*************** -*** 37,42 **** ---- 37,43 ---- - unsigned long nextthd; /* next thread, only tops have this */ - unsigned long prevthd; /* previous thread, only tops have this */ - unsigned long top; /* top of this thread */ -+ unsigned long toploose; /* top of this thread, if is loose */ - unsigned long head; /* head of the whole thread list */ - } PINETHRD_S; - -*************** -*** 92,98 **** - void sort_thread_callback(MAILSTREAM *, THREADNODE *); - void collapse_threads(MAILSTREAM *, MSGNO_S *, PINETHRD_S *); - PINETHRD_S *msgno_thread_info(MAILSTREAM *, unsigned long, PINETHRD_S *, unsigned); -! void collapse_or_expand(struct pine *, MAILSTREAM *, MSGNO_S *, unsigned long); - void select_thread_stmp(struct pine *, MAILSTREAM *, MSGNO_S *); - unsigned long count_flags_in_thread(MAILSTREAM *, PINETHRD_S *, long); - unsigned long count_lflags_in_thread(MAILSTREAM *, PINETHRD_S *, MSGNO_S *, int); ---- 93,99 ---- - void sort_thread_callback(MAILSTREAM *, THREADNODE *); - void collapse_threads(MAILSTREAM *, MSGNO_S *, PINETHRD_S *); - PINETHRD_S *msgno_thread_info(MAILSTREAM *, unsigned long, PINETHRD_S *, unsigned); -! void collapse_or_expand(struct pine *, MAILSTREAM *, MSGNO_S *, unsigned long, int); - void select_thread_stmp(struct pine *, MAILSTREAM *, MSGNO_S *); - unsigned long count_flags_in_thread(MAILSTREAM *, PINETHRD_S *, long); - unsigned long count_lflags_in_thread(MAILSTREAM *, PINETHRD_S *, MSGNO_S *, int); -*************** -*** 106,111 **** - int unview_thread(struct pine *, MAILSTREAM *, MSGNO_S *); - PINETHRD_S *find_thread_by_number(MAILSTREAM *, MSGNO_S *, long, PINETHRD_S *); - void set_search_bit_for_thread(MAILSTREAM *, PINETHRD_S *, SEARCHSET **); -! - - #endif /* PITH_THREAD_INCLUDED */ ---- 107,130 ---- - int unview_thread(struct pine *, MAILSTREAM *, MSGNO_S *); - PINETHRD_S *find_thread_by_number(MAILSTREAM *, MSGNO_S *, long, PINETHRD_S *); - void set_search_bit_for_thread(MAILSTREAM *, PINETHRD_S *, SEARCHSET **); -! void find_msgmap(MAILSTREAM *, MSGNO_S *, int, SortOrder, unsigned); -! void move_thread(struct pine *, MAILSTREAM *, MSGNO_S *, int); -! void relink_threads(MAILSTREAM *, MSGNO_S *, long *); -! long top_thread(MAILSTREAM *, long); -! long top_this_thread(MAILSTREAM *, long); -! long get_length_branch(MAILSTREAM *, long); -! unsigned long get_next(MAILSTREAM *,PINETHRD_S *); -! unsigned long get_branch(MAILSTREAM *,PINETHRD_S *); -! int count_thread(struct pine *, MAILSTREAM *, MSGNO_S *, long); -! int count_this_thread(MAILSTREAM *, unsigned long); -! int this_thread_is_kolapsed(struct pine *, MAILSTREAM *, MSGNO_S *, long); -! int thread_is_kolapsed(struct pine *, MAILSTREAM *, MSGNO_S *, long); -! int move_prev_thread(struct pine *, MAILSTREAM *, MSGNO_S *, int); -! int move_next_thread(struct pine *, MAILSTREAM *, MSGNO_S *, int); -! int move_next_this_thread(struct pine *, MAILSTREAM *, MSGNO_S *, int); -! void move_top_thread(MAILSTREAM *, MSGNO_S *, long); -! void move_top_this_thread(MAILSTREAM *, MSGNO_S *, long); -! THREADNODE *copy_tree(THREADNODE *); -! int allowed_thread_key(SortOrder sort); - - #endif /* PITH_THREAD_INCLUDED */ -diff -rc alpine-2.10/web/src/alpined.d/alpined.c alpine-2.10.fancy/web/src/alpined.d/alpined.c -*** alpine-2.10/web/src/alpined.d/alpined.c 2013-01-11 15:21:34.000000000 -0700 ---- alpine-2.10.fancy/web/src/alpined.d/alpined.c 2013-01-11 20:43:05.000000000 -0700 -*************** -*** 2755,2761 **** - init_save_defaults(); - break; - case V_SORT_KEY: -! decode_sort(ps_global->VAR_SORT_KEY, &ps_global->def_sort, &def_sort_rev); - break; - case V_VIEW_HDR_COLORS : - set_custom_spec_colors(ps_global); ---- 2755,2761 ---- - init_save_defaults(); - break; - case V_SORT_KEY: -! decode_sort(ps_global->VAR_SORT_KEY, &ps_global->def_sort, &def_sort_rev, 0); - break; - case V_VIEW_HDR_COLORS : - set_custom_spec_colors(ps_global); -*************** -*** 6331,6337 **** - && mn_get_revsort(sp_msgmap(ps_global->mail_stream)) == reversed)) - sort_folder(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), - ps_global->sort_types[i], -! reversed, 0); - - break; - } ---- 6331,6337 ---- - && mn_get_revsort(sp_msgmap(ps_global->mail_stream)) == reversed)) - sort_folder(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), - ps_global->sort_types[i], -! reversed, 0, 1); - - break; - } ++ if (display) ++ q_status_message(SM_ORDER, 0, 1, "No more Threads to advance"); ++ } ++ return rv; ++} ++ ++int ++move_next_thread(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap, int display) ++{ ++ int collapsed, rv = 1, done = 0; ++ PINETHRD_S *thrd = NULL; ++ unsigned long orig, orig_top, top; ++ ++ if(!stream) ++ return 0; ++ ++ orig = mn_m2raw(msgmap, mn_get_cur(msgmap)); ++ move_top_thread(stream, msgmap,orig); ++ top = orig_top = mn_m2raw(msgmap, mn_get_cur(msgmap)); ++ ++ if(top) ++ thrd = fetch_thread(stream, top); ++ ++ if(!thrd) ++ return 0; ++ ++ while (rv > 0 && !done){ ++ rv = move_next_this_thread(state, stream, msgmap, display); ++ if (F_OFF(F_ENHANCED_THREAD, state) ++ || !(top = mn_m2raw(msgmap, mn_get_cur(msgmap))) ++ || (orig_top != top_thread(stream, top))) ++ done++; ++ } ++ if (display){ ++ if (rv > 0 && SEP_THRDINDX()) ++ q_status_message(SM_ORDER, 0, 2, "Viewing next thread"); ++ if (!rv) ++ q_status_message(SM_ORDER, 0, 2, "No more threads to advance"); ++ } ++ if(rv <= 0){ ++ rv = 0; ++ mn_set_cur(msgmap, mn_raw2m(msgmap, orig)); ++ } ++ ++ return rv; ++} ++ ++int ++move_prev_thread(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap, int display) ++{ ++ PINETHRD_S *thrd = NULL; ++ unsigned long rawno, top; ++ int rv = 1; ++ ++ if(!stream) ++ return -1; ++ ++ rawno = mn_m2raw(msgmap, mn_get_cur(msgmap)); ++ if(rawno) ++ thrd = fetch_thread(stream, rawno); ++ ++ if(!thrd) ++ return -1; ++ ++ top = top_thread(stream, rawno); ++ ++ if (top != rawno) ++ mn_set_cur(msgmap,mn_raw2m(msgmap, top)); ++ else if (thrd->prevthd) ++ mn_set_cur(msgmap,mn_raw2m(msgmap, top_thread(stream,thrd->prevthd))); ++ else ++ rv = 0; ++ if (display){ ++ if (rv && SEP_THRDINDX()) ++ q_status_message(SM_ORDER, 0, 2, "Viewing previous thread"); ++ if (!rv) ++ q_status_message(SM_ORDER, 0, 2, "No more threads to go back"); ++ } ++ ++ return rv; ++} ++ ++/* add more keys to this list */ ++int ++allowed_thread_key(SortOrder sort) ++{ ++ return sort == SortArrival || sort == SortDate ++ || sort == SortScore || sort == SortThread ++ || sort == SortSize; ++} ++ +Index: alpine-2.11/pith/thread.h +=================================================================== +--- alpine-2.11.orig/pith/thread.h ++++ alpine-2.11/pith/thread.h +@@ -37,6 +37,7 @@ typedef struct pine_thrd { + unsigned long nextthd; /* next thread, only tops have this */ + unsigned long prevthd; /* previous thread, only tops have this */ + unsigned long top; /* top of this thread */ ++ unsigned long toploose; /* top of this thread, if is loose */ + unsigned long head; /* head of the whole thread list */ + } PINETHRD_S; + +@@ -92,7 +93,7 @@ void erase_threading_info(MAILSTRE + void sort_thread_callback(MAILSTREAM *, THREADNODE *); + void collapse_threads(MAILSTREAM *, MSGNO_S *, PINETHRD_S *); + PINETHRD_S *msgno_thread_info(MAILSTREAM *, unsigned long, PINETHRD_S *, unsigned); +-void collapse_or_expand(struct pine *, MAILSTREAM *, MSGNO_S *, unsigned long); ++void collapse_or_expand(struct pine *, MAILSTREAM *, MSGNO_S *, unsigned long, int); + void select_thread_stmp(struct pine *, MAILSTREAM *, MSGNO_S *); + unsigned long count_flags_in_thread(MAILSTREAM *, PINETHRD_S *, long); + unsigned long count_lflags_in_thread(MAILSTREAM *, PINETHRD_S *, MSGNO_S *, int); +@@ -106,6 +107,24 @@ int view_thread(struct pine *, MAI + int unview_thread(struct pine *, MAILSTREAM *, MSGNO_S *); + PINETHRD_S *find_thread_by_number(MAILSTREAM *, MSGNO_S *, long, PINETHRD_S *); + void set_search_bit_for_thread(MAILSTREAM *, PINETHRD_S *, SEARCHSET **); +- ++void find_msgmap(MAILSTREAM *, MSGNO_S *, int, SortOrder, unsigned); ++void move_thread(struct pine *, MAILSTREAM *, MSGNO_S *, int); ++void relink_threads(MAILSTREAM *, MSGNO_S *, long *); ++long top_thread(MAILSTREAM *, long); ++long top_this_thread(MAILSTREAM *, long); ++long get_length_branch(MAILSTREAM *, long); ++unsigned long get_next(MAILSTREAM *,PINETHRD_S *); ++unsigned long get_branch(MAILSTREAM *,PINETHRD_S *); ++int count_thread(struct pine *, MAILSTREAM *, MSGNO_S *, long); ++int count_this_thread(MAILSTREAM *, unsigned long); ++int this_thread_is_kolapsed(struct pine *, MAILSTREAM *, MSGNO_S *, long); ++int thread_is_kolapsed(struct pine *, MAILSTREAM *, MSGNO_S *, long); ++int move_prev_thread(struct pine *, MAILSTREAM *, MSGNO_S *, int); ++int move_next_thread(struct pine *, MAILSTREAM *, MSGNO_S *, int); ++int move_next_this_thread(struct pine *, MAILSTREAM *, MSGNO_S *, int); ++void move_top_thread(MAILSTREAM *, MSGNO_S *, long); ++void move_top_this_thread(MAILSTREAM *, MSGNO_S *, long); ++THREADNODE *copy_tree(THREADNODE *); ++int allowed_thread_key(SortOrder sort); + + #endif /* PITH_THREAD_INCLUDED */ +Index: alpine-2.11/web/src/alpined.d/alpined.c +=================================================================== +--- alpine-2.11.orig/web/src/alpined.d/alpined.c ++++ alpine-2.11/web/src/alpined.d/alpined.c +@@ -2755,7 +2755,7 @@ PEConfigCmd(ClientData clientData, Tcl_I + init_save_defaults(); + break; + case V_SORT_KEY: +- decode_sort(ps_global->VAR_SORT_KEY, &ps_global->def_sort, &def_sort_rev); ++ decode_sort(ps_global->VAR_SORT_KEY, &ps_global->def_sort, &def_sort_rev, 0); + break; + case V_VIEW_HDR_COLORS : + set_custom_spec_colors(ps_global); +@@ -6331,7 +6331,7 @@ PEMailboxCmd(ClientData clientData, Tcl_ + && mn_get_revsort(sp_msgmap(ps_global->mail_stream)) == reversed)) + sort_folder(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), + ps_global->sort_types[i], +- reversed, 0); ++ reversed, 0, 1); + + break; + } diff --git a/chappa-ignoresize.patch b/chappa-ignoresize.patch index 97cecb9..e57bc70 100644 --- a/chappa-ignoresize.patch +++ b/chappa-ignoresize.patch @@ -1,111 +1,112 @@ -diff -rc alpine-2.10/alpine/mailcmd.c alpine-2.10.ignoresize/alpine/mailcmd.c -*** alpine-2.10/alpine/mailcmd.c 2013-01-11 15:21:34.000000000 -0700 ---- alpine-2.10.ignoresize/alpine/mailcmd.c 2013-01-11 20:43:20.000000000 -0700 -*************** -*** 3349,3354 **** ---- 3349,3357 ---- - {-1, 0, NULL, NULL} - }; - -+ if(F_ON(F_IGNORE_SIZE, ps_global)) -+ return 'y'; -+ - if(flags & SSCP_INIT || flags & SSCP_END){ - if(flags & SSCP_END && possible_corruption) - q_status_message(SM_ORDER, 3, 3, "There is possible data corruption, check the results"); -diff -rc alpine-2.10/pith/conf.c alpine-2.10.ignoresize/pith/conf.c -*** alpine-2.10/pith/conf.c 2013-01-11 19:45:41.000000000 -0700 ---- alpine-2.10.ignoresize/pith/conf.c 2013-01-11 20:43:20.000000000 -0700 -*************** -*** 3082,3087 **** ---- 3082,3089 ---- - F_FULL_AUTO_EXPUNGE, h_config_full_auto_expunge, PREF_MISC, 0}, - {"force-arrow-cursor", NULL, - F_FORCE_ARROW, h_config_force_arrow, PREF_MISC, 0}, -+ {"ignore-size-changes", NULL, -+ F_IGNORE_SIZE, h_config_ignore_size, PREF_MISC, 0}, - {"maildrops-preserve-state", NULL, - F_MAILDROPS_PRESERVE_STATE, h_config_maildrops_preserve_state, - PREF_MISC, 0}, -diff -rc alpine-2.10/pith/conftype.h alpine-2.10.ignoresize/pith/conftype.h -*** alpine-2.10/pith/conftype.h 2013-01-11 19:45:41.000000000 -0700 ---- alpine-2.10.ignoresize/pith/conftype.h 2013-01-11 20:43:20.000000000 -0700 -*************** -*** 335,340 **** ---- 335,341 ---- - F_FORCE_ARROW, - F_PRUNE_USES_ISO, - F_ALT_ED_NOW, -+ F_IGNORE_SIZE, - F_SHOW_DELAY_CUE, - F_CANCEL_CONFIRM, - F_AUTO_OPEN_NEXT_UNREAD, -diff -rc alpine-2.10/pith/pine.hlp alpine-2.10.ignoresize/pith/pine.hlp -*** alpine-2.10/pith/pine.hlp 2013-01-11 20:33:27.000000000 -0700 ---- alpine-2.10.ignoresize/pith/pine.hlp 2013-01-11 20:43:20.000000000 -0700 -*************** -*** 3251,3256 **** ---- 3251,3257 ---- -

  • FEATURE: -
  • FEATURE: -
  • FEATURE: -+
  • FEATURE: -
  • FEATURE: -
  • FEATURE: -
  • FEATURE: -*************** -*** 30437,30442 **** ---- 30438,30477 ---- - -

    - <End of help on this topic> -+ -+ -+ ====== h_config_ignore_size ===== -+ -+ -+ FEATURE: <!--#echo var="FEAT_ignore-size-changes"--> -+ -+ -+

    FEATURE:

    -+ -+ When you have an account residing in an IMAP server, Alpine gets the size of -+ each message from the server. However, when Alpine saves a message residing -+ in an IMAP server, Alpine computes the size of the message independently. If -+ these two numbers do not match for a message, Alpine asks you if you still -+ want to take the risk of saving the message, since data corruption or loss -+ of data could result of this save. -+ -+

    -+ Sometimes the root of this problem is that the server is defective, and -+ there will not be loss of information when saving such message. Enabling -+ this feature will make Aline ignore such error and continue saving the -+ message. If you can determine that this is the case, enable this feature -+ so that the saving operation will succeed. An example of a defective server -+ is the Gmail IMAP server. Another example is some versions of the Exchange -+ server. -+ -+

    -+ It is recommended that this feature be disabled most of the time and only -+ enabled when you find a server which you can determine that has the above -+ mentioned defect, but be disabled again after making this operation -+ succeed. -+ -+

    -+ <End of help on this topic> - - - ====== h_config_force_low_speed ===== -diff -rc alpine-2.10/pith/save.c alpine-2.10.ignoresize/pith/save.c -*** alpine-2.10/pith/save.c 2013-01-11 11:26:44.000000000 -0700 ---- alpine-2.10.ignoresize/pith/save.c 2013-01-11 20:43:20.000000000 -0700 -*************** -*** 1157,1162 **** ---- 1157,1163 ---- - snprintf(buf, sizeof(buf), - "Message to save shrank: source msg # %ld may be saved incorrectly", - mn_raw2m(pkg->msgmap, raw)); -+ if(F_OFF(F_IGNORE_SIZE, ps_global)) - q_status_message(SM_ORDER, 0, 3, buf); - } - else{ +--- + alpine/mailcmd.c | 3 +++ + pith/conf.c | 2 ++ + pith/conftype.h | 1 + + pith/pine.hlp | 35 +++++++++++++++++++++++++++++++++++ + pith/save.c | 1 + + 5 files changed, 42 insertions(+) + +Index: alpine-2.11/alpine/mailcmd.c +=================================================================== +--- alpine-2.11.orig/alpine/mailcmd.c ++++ alpine-2.11/alpine/mailcmd.c +@@ -3368,6 +3368,9 @@ save_size_changed_prompt(long msgno, int + {-1, 0, NULL, NULL} + }; + ++ if(F_ON(F_IGNORE_SIZE, ps_global)) ++ return 'y'; ++ + if(flags & SSCP_INIT || flags & SSCP_END){ + if(flags & SSCP_END && possible_corruption) + q_status_message(SM_ORDER, 3, 3, "There is possible data corruption, check the results"); +Index: alpine-2.11/pith/conf.c +=================================================================== +--- alpine-2.11.orig/pith/conf.c ++++ alpine-2.11/pith/conf.c +@@ -3123,6 +3123,8 @@ feature_list(int index) + F_FULL_AUTO_EXPUNGE, h_config_full_auto_expunge, PREF_MISC, 0}, + {"force-arrow-cursor", NULL, + F_FORCE_ARROW, h_config_force_arrow, PREF_MISC, 0}, ++ {"ignore-size-changes", NULL, ++ F_IGNORE_SIZE, h_config_ignore_size, PREF_MISC, 0}, + {"maildrops-preserve-state", NULL, + F_MAILDROPS_PRESERVE_STATE, h_config_maildrops_preserve_state, + PREF_MISC, 0}, +Index: alpine-2.11/pith/conftype.h +=================================================================== +--- alpine-2.11.orig/pith/conftype.h ++++ alpine-2.11/pith/conftype.h +@@ -346,6 +346,7 @@ typedef enum { + F_FORCE_ARROW, + F_PRUNE_USES_ISO, + F_ALT_ED_NOW, ++ F_IGNORE_SIZE, + F_SHOW_DELAY_CUE, + F_CANCEL_CONFIRM, + F_AUTO_OPEN_NEXT_UNREAD, +Index: alpine-2.11/pith/pine.hlp +=================================================================== +--- alpine-2.11.orig/pith/pine.hlp ++++ alpine-2.11/pith/pine.hlp +@@ -3296,6 +3296,7 @@ There are also additional details on +

  • FEATURE: +
  • FEATURE: +
  • FEATURE: ++
  • FEATURE: +
  • FEATURE: +
  • FEATURE: +
  • FEATURE: +@@ -30787,6 +30788,40 @@ but that is not implemented. + +

    + <End of help on this topic> ++ ++ ++====== h_config_ignore_size ===== ++ ++ ++FEATURE: <!--#echo var="FEAT_ignore-size-changes"--> ++ ++ ++

    FEATURE:

    ++ ++When you have an account residing in an IMAP server, Alpine gets the size of ++each message from the server. However, when Alpine saves a message residing ++in an IMAP server, Alpine computes the size of the message independently. If ++these two numbers do not match for a message, Alpine asks you if you still ++want to take the risk of saving the message, since data corruption or loss ++of data could result of this save. ++ ++

    ++Sometimes the root of this problem is that the server is defective, and ++there will not be loss of information when saving such message. Enabling ++this feature will make Aline ignore such error and continue saving the ++message. If you can determine that this is the case, enable this feature ++so that the saving operation will succeed. An example of a defective server ++is the Gmail IMAP server. Another example is some versions of the Exchange ++server. ++ ++

    ++It is recommended that this feature be disabled most of the time and only ++enabled when you find a server which you can determine that has the above ++mentioned defect, but be disabled again after making this operation ++succeed. ++ ++

    ++<End of help on this topic> + + + ====== h_config_force_low_speed ===== +Index: alpine-2.11/pith/save.c +=================================================================== +--- alpine-2.11.orig/pith/save.c ++++ alpine-2.11/pith/save.c +@@ -1157,6 +1157,7 @@ long save_fetch_append_cb(MAILSTREAM *st + snprintf(buf, sizeof(buf), + "Message to save shrank: source msg # %ld may be saved incorrectly", + mn_raw2m(pkg->msgmap, raw)); ++ if(F_OFF(F_IGNORE_SIZE, ps_global)) + q_status_message(SM_ORDER, 0, 3, buf); + } + else{ diff --git a/chappa-insertpat.patch b/chappa-insertpat.patch index 7fce541..845882b 100644 --- a/chappa-insertpat.patch +++ b/chappa-insertpat.patch @@ -1,40 +1,43 @@ -diff -rc alpine-2.10/pico/display.c alpine-2.10.insertpat/pico/display.c -*** alpine-2.10/pico/display.c 2013-01-11 11:25:29.000000000 -0700 ---- alpine-2.10.insertpat/pico/display.c 2013-01-11 20:43:15.000000000 -0700 -*************** -*** 1760,1765 **** ---- 1760,1777 ---- - - continue; - -+ case (CTRL|'N'): /* Insert pattern */ -+ if (pat[0] != '\0'){ -+ ucs4_strncpy(buf+ucs4_strlen(buf), pat, NPAT); -+ pputs(pat,1); -+ b = &buf[ucs4_strlen(buf)]; -+ dline.vused += ucs4_strlen(pat); -+ changed = TRUE; -+ } -+ else -+ (*term.t_beep)(); -+ continue; -+ - case (CTRL|'G') : /* CTRL-G help */ - if(term.t_mrow == 0 && km_popped == 0){ - movecursor(term.t_nrow-2, 0); -diff -rc alpine-2.10/pico/search.c alpine-2.10.insertpat/pico/search.c -*** alpine-2.10/pico/search.c 2013-01-11 11:25:29.000000000 -0700 ---- alpine-2.10.insertpat/pico/search.c 2013-01-11 20:43:15.000000000 -0700 -*************** -*** 76,81 **** ---- 76,85 ---- - N_("~ Hitting only ~R~e~t~u~r~n or at the prompt will cause the"), - N_(" search to be made with the default value."), - " ", -+ N_("~ Hitting ~^~N will reinsert the last string you searched for"), -+ N_(" so that you can edit it (in case you made a mistake entering the"), -+ N_(" search pattern the first time)."), -+ " ", - N_(" The text search is not case sensitive, and will examine the"), - N_(" entire message."), - " ", +--- + pico/display.c | 12 ++++++++++++ + pico/search.c | 4 ++++ + 2 files changed, 16 insertions(+) + +Index: alpine-2.11/pico/display.c +=================================================================== +--- alpine-2.11.orig/pico/display.c ++++ alpine-2.11/pico/display.c +@@ -1760,6 +1760,18 @@ mlreplyd(UCS *prompt, UCS *buf, int nbuf + + continue; + ++ case (CTRL|'N'): /* Insert pattern */ ++ if (pat[0] != '\0'){ ++ ucs4_strncpy(buf+ucs4_strlen(buf), pat, NPAT); ++ pputs(pat,1); ++ b = &buf[ucs4_strlen(buf)]; ++ dline.vused += ucs4_strlen(pat); ++ changed = TRUE; ++ } ++ else ++ (*term.t_beep)(); ++ continue; ++ + case (CTRL|'G') : /* CTRL-G help */ + if(term.t_mrow == 0 && km_popped == 0){ + movecursor(term.t_nrow-2, 0); +Index: alpine-2.11/pico/search.c +=================================================================== +--- alpine-2.11.orig/pico/search.c ++++ alpine-2.11/pico/search.c +@@ -76,6 +76,10 @@ N_(" brackets. This string is th + N_("~ Hitting only ~R~e~t~u~r~n or at the prompt will cause the"), + N_(" search to be made with the default value."), + " ", ++N_("~ Hitting ~^~N will reinsert the last string you searched for"), ++N_(" so that you can edit it (in case you made a mistake entering the"), ++N_(" search pattern the first time)."), ++" ", + N_(" The text search is not case sensitive, and will examine the"), + N_(" entire message."), + " ", diff --git a/chappa-maildir.patch b/chappa-maildir.patch index 897e620..4ac251a 100644 --- a/chappa-maildir.patch +++ b/chappa-maildir.patch @@ -1,3762 +1,3651 @@ -diff -rc alpine-2.10/alpine/alpine.c alpine-2.10.maildir/alpine/alpine.c -*** alpine-2.10/alpine/alpine.c 2013-01-11 15:21:34.000000000 -0700 ---- alpine-2.10.maildir/alpine/alpine.c 2013-01-11 20:43:06.000000000 -0700 -*************** -*** 557,562 **** ---- 557,567 ---- - if(F_ON(F_MAILDROPS_PRESERVE_STATE, ps_global)) - mail_parameters(NULL, SET_SNARFPRESERVE, (void *) TRUE); - -+ #ifndef _WINDOWS -+ mail_parameters(NULL,SET_COURIERSTYLE, -+ (void *)(F_ON(F_COURIER_FOLDER_LIST, ps_global) ? 1 : 0)); -+ #endif -+ - rvl = 0L; - if(pine_state->VAR_NNTPRANGE){ - if(!SVAR_NNTPRANGE(pine_state, rvl, tmp_20k_buf, SIZEOF_20KBUF)) -diff -rc alpine-2.10/alpine/confscroll.c alpine-2.10.maildir/alpine/confscroll.c -*** alpine-2.10/alpine/confscroll.c 2013-01-11 15:21:34.000000000 -0700 ---- alpine-2.10.maildir/alpine/confscroll.c 2013-01-11 20:43:06.000000000 -0700 -*************** -*** 5493,5498 **** ---- 5493,5504 ---- - (void *)var->current_val.p); - } - #endif -+ #ifndef _WINDOWS -+ else if(var == &ps->vars[V_MAILDIR_LOCATION]){ -+ if(var->current_val.p && var->current_val.p[0]) -+ mail_parameters(NULL, SET_MDINBOXPATH, (void *)var->current_val.p); -+ } -+ #endif - else if(revert && standard_radio_var(ps, var)){ - - cur_rule_value(var, TRUE, FALSE); -diff -rc alpine-2.10/imap/src/c-client/mail.c alpine-2.10.maildir/imap/src/c-client/mail.c -*** alpine-2.10/imap/src/c-client/mail.c 2013-01-11 15:21:34.000000000 -0700 ---- alpine-2.10.maildir/imap/src/c-client/mail.c 2013-01-11 20:43:06.000000000 -0700 -*************** -*** 991,997 **** - MAILSTREAM *ts; - char *s,*t,tmp[MAILTMPLEN]; - size_t i; -! DRIVER *d; - /* never allow names with newlines */ - if (s = strpbrk (mailbox,"\015\012")) { - MM_LOG ("Can't create mailbox with such a name",ERROR); ---- 991,997 ---- - MAILSTREAM *ts; - char *s,*t,tmp[MAILTMPLEN]; - size_t i; -! DRIVER *d, *md; - /* never allow names with newlines */ - if (s = strpbrk (mailbox,"\015\012")) { - MM_LOG ("Can't create mailbox with such a name",ERROR); -*************** -*** 1015,1020 **** ---- 1015,1022 ---- - return NIL; - } - -+ /* Hack, we should do this better, but it works */ -+ for (md = maildrivers; md && strcmp (md->name, "md"); md = md->next); - /* see if special driver hack */ - if ((mailbox[0] == '#') && ((mailbox[1] == 'd') || (mailbox[1] == 'D')) && - ((mailbox[2] == 'r') || (mailbox[2] == 'R')) && -*************** -*** 1045,1050 **** ---- 1047,1059 ---- - (((*mailbox == '{') || (*mailbox == '#')) && - (stream = mail_open (NIL,mailbox,OP_PROTOTYPE | OP_SILENT)))) - d = stream->dtb; -+ else if(mailbox[0] == '#' -+ && (mailbox[1] == 'm' || mailbox[1] == 'M') -+ && (mailbox[2] == 'd' || mailbox[2] == 'D' -+ || mailbox[2] == 'c' || mailbox[2] == 'C') -+ && mailbox[3] == '/' -+ && mailbox[4] != '\0') -+ return (*md->create)(stream, mailbox); - else if ((*mailbox != '{') && (ts = default_proto (NIL))) d = ts->dtb; - else { /* failed utterly */ - sprintf (tmp,"Can't create mailbox %.80s: indeterminate format",mailbox); -diff -rc alpine-2.10/imap/src/c-client/mail.h alpine-2.10.maildir/imap/src/c-client/mail.h -*** alpine-2.10/imap/src/c-client/mail.h 2013-01-11 17:43:09.000000000 -0700 ---- alpine-2.10.maildir/imap/src/c-client/mail.h 2013-01-11 20:43:06.000000000 -0700 -*************** -*** 353,358 **** ---- 353,362 ---- - #define SET_SCANCONTENTS (long) 573 - #define GET_MHALLOWINBOX (long) 574 - #define SET_MHALLOWINBOX (long) 575 -+ #define GET_COURIERSTYLE (long) 576 -+ #define SET_COURIERSTYLE (long) 577 -+ #define SET_MDINBOXPATH (long) 578 -+ #define GET_MDINBOXPATH (long) 579 - - /* Driver flags */ - -diff -rc alpine-2.10/imap/src/osdep/unix/dummy.c alpine-2.10.maildir/imap/src/osdep/unix/dummy.c -*** alpine-2.10/imap/src/osdep/unix/dummy.c 2011-01-09 21:00:19.000000000 -0700 ---- alpine-2.10.maildir/imap/src/osdep/unix/dummy.c 2013-01-11 20:43:06.000000000 -0700 -*************** -*** 106,118 **** - * Accepts: mailbox name - * Returns: our driver if name is valid, NIL otherwise - */ -! - DRIVER *dummy_valid (char *name) - { -! char *s,tmp[MAILTMPLEN]; - struct stat sbuf; - /* must be valid local mailbox */ -! if (name && *name && (*name != '{') && (s = mailboxfile (tmp,name))) { - /* indeterminate clearbox INBOX */ - if (!*s) return &dummydriver; - else if (!stat (s,&sbuf)) switch (sbuf.st_mode & S_IFMT) { ---- 106,124 ---- - * Accepts: mailbox name - * Returns: our driver if name is valid, NIL otherwise - */ -! char * maildir_remove_root(char *); - DRIVER *dummy_valid (char *name) - { -! char *s,tmp[MAILTMPLEN], *rname; - struct stat sbuf; -+ -+ if(strlen(name) > MAILTMPLEN) -+ name[MAILTMPLEN] = '\0'; -+ -+ strcpy(tmp, name); -+ rname = maildir_remove_root(tmp); - /* must be valid local mailbox */ -! if (rname && *rname && (*rname != '{') && (s = mailboxfile (tmp,rname))) { - /* indeterminate clearbox INBOX */ - if (!*s) return &dummydriver; - else if (!stat (s,&sbuf)) switch (sbuf.st_mode & S_IFMT) { -*************** -*** 121,128 **** - return &dummydriver; - } - /* blackbox INBOX does not exist yet */ -! else if (!compare_cstring (name,"INBOX")) return &dummydriver; - } - return NIL; - } - ---- 127,135 ---- - return &dummydriver; - } - /* blackbox INBOX does not exist yet */ -! else if (!compare_cstring (rname,"INBOX")) return &dummydriver; - } -+ if(rname) fs_give((void **)&rname); - return NIL; - } - -*************** -*** 454,459 **** ---- 461,468 ---- - { - char *s,tmp[MAILTMPLEN]; - long ret = NIL; -+ if(!strncmp(mailbox,"#md/",4) || !strncmp(mailbox,"#mc/", 4)) -+ return maildir_create(stream, mailbox); - /* validate name */ - if (!(compare_cstring (mailbox,"INBOX") && (s = dummy_file (tmp,mailbox)))) { - sprintf (tmp,"Can't create %.80s: invalid name",mailbox); -*************** -*** 519,524 **** ---- 528,541 ---- - { - struct stat sbuf; - char *s,tmp[MAILTMPLEN]; -+ if (!strncmp(mailbox,"#md/",4) || !strncmp(mailbox,"#mc/", 4) -+ || is_valid_maildir(&mailbox)){ -+ char tmp[MAILTMPLEN] = {'\0'}; -+ strcpy(tmp, mailbox); -+ if(tmp[strlen(tmp) - 1] != '/') -+ tmp[strlen(tmp)] = '/'; -+ return maildir_delete(stream, tmp); -+ } - if (!(s = dummy_file (tmp,mailbox))) { - sprintf (tmp,"Can't delete - invalid name: %.80s",s); - MM_LOG (tmp,ERROR); -*************** -*** 544,555 **** - long dummy_rename (MAILSTREAM *stream,char *old,char *newname) - { - struct stat sbuf; -! char c,*s,tmp[MAILTMPLEN],mbx[MAILTMPLEN],oldname[MAILTMPLEN]; - /* no trailing / allowed */ -! if (!dummy_file (oldname,old) || !(s = dummy_file (mbx,newname)) || - stat (oldname,&sbuf) || ((s = strrchr (s,'/')) && !s[1] && - ((sbuf.st_mode & S_IFMT) != S_IFDIR))) { -! sprintf (mbx,"Can't rename %.80s to %.80s: invalid name",old,newname); - MM_LOG (mbx,ERROR); - return NIL; - } ---- 561,583 ---- - long dummy_rename (MAILSTREAM *stream,char *old,char *newname) - { - struct stat sbuf; -! char c,*s,tmp[MAILTMPLEN],mbx[MAILTMPLEN],oldname[MAILTMPLEN], *rold, *rnewname; -! -! if(strlen(old) > MAILTMPLEN) -! old[MAILTMPLEN] = '\0'; -! -! if(strlen(newname) > MAILTMPLEN) -! newname[MAILTMPLEN] = '\0'; -! -! strcpy(tmp, old); -! rold = maildir_remove_root(tmp); -! strcpy(tmp, newname); -! rnewname = maildir_remove_root(tmp); - /* no trailing / allowed */ -! if (!dummy_file (oldname,rold) || !(s = dummy_file (mbx,rnewname)) || - stat (oldname,&sbuf) || ((s = strrchr (s,'/')) && !s[1] && - ((sbuf.st_mode & S_IFMT) != S_IFDIR))) { -! sprintf (mbx,"Can't rename %.80s to %.80s: invalid name",rold,rnewname); - MM_LOG (mbx,ERROR); - return NIL; - } -*************** -*** 565,578 **** - } - } - /* rename of non-ex INBOX creates dest */ -! if (!compare_cstring (old,"INBOX") && stat (oldname,&sbuf)) - return dummy_create (NIL,mbx); - if (rename (oldname,mbx)) { -! sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %.80s",old,newname, - strerror (errno)); - MM_LOG (tmp,ERROR); - return NIL; - } - return T; /* return success */ - } - ---- 593,608 ---- - } - } - /* rename of non-ex INBOX creates dest */ -! if (!compare_cstring (rold,"INBOX") && stat (oldname,&sbuf)) - return dummy_create (NIL,mbx); - if (rename (oldname,mbx)) { -! sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %.80s",rold,rnewname, - strerror (errno)); - MM_LOG (tmp,ERROR); - return NIL; - } -+ if(rold) fs_give((void **)&rold); -+ if(rnewname) fs_give((void **)&rnewname); - return T; /* return success */ - } - -diff -rc alpine-2.10/imap/src/osdep/unix/maildir.c alpine-2.10.maildir/imap/src/osdep/unix/maildir.c -*** alpine-2.10/imap/src/osdep/unix/maildir.c 2013-01-11 20:43:06.000000000 -0700 ---- alpine-2.10.maildir/imap/src/osdep/unix/maildir.c 2013-01-11 20:43:06.000000000 -0700 -*************** -*** 0 **** ---- 1,2638 ---- -+ /* -+ * Maildir driver for Alpine 2.00 -+ * -+ * Written by Eduardo Chappa -+ * Last Update: May 29, 2011. -+ * -+ */ -+ -+ #include -+ #include -+ #include -+ extern int errno; /* just in case */ -+ #include "mail.h" -+ #include -+ #include -+ #include -+ #include "osdep.h" -+ #include "rfc822.h" -+ #include "fdstring.h" -+ #include "misc.h" -+ #include "dummy.h" -+ #include "maildir.h" -+ -+ /* Driver dispatch used by MAIL */ -+ DRIVER maildirdriver = { -+ "md", /* driver name, yes it's md, not maildir */ -+ DR_MAIL|DR_LOCAL|DR_NAMESPACE|DR_DIRFMT, /* driver flags */ -+ (DRIVER *) NIL, /* next driver */ -+ maildir_valid, /* mailbox is valid for us */ -+ maildir_parameters, /* manipulate parameters */ -+ NIL, /* scan mailboxes */ -+ maildir_list, /* find mailboxes */ -+ maildir_lsub, /* find subscribed mailboxes */ -+ maildir_sub, /* subscribe to mailbox */ -+ maildir_unsub, /* unsubscribe from mailbox */ -+ maildir_create, /* create mailbox */ -+ maildir_delete, /* delete mailbox */ -+ maildir_rename, /* rename mailbox */ -+ mail_status_default, /* status of mailbox */ -+ maildir_open, /* open mailbox */ -+ maildir_close, /* close mailbox */ -+ maildir_fast, /* fetch message "fast" attributes */ -+ NIL, /* fetch message flags */ -+ NIL, /* fetch overview */ -+ NIL, /* fetch message structure */ -+ maildir_header, /* fetch message header */ -+ maildir_text, /* fetch message body */ -+ NIL, /* fetch partial message text */ -+ NIL, /* unique identifier */ -+ NIL, /* message number */ -+ NIL, /* modify flags */ -+ maildir_flagmsg, /* per-message modify flags */ -+ NIL, /* search for message based on criteria */ -+ NIL, /* sort messages */ -+ NIL, /* thread messages */ -+ maildir_ping, /* ping mailbox to see if still alive */ -+ maildir_check, /* check for new messages */ -+ maildir_expunge, /* expunge deleted messages */ -+ maildir_copy, /* copy messages to another mailbox */ -+ maildir_append, /* append string message to mailbox */ -+ NIL /* garbage collect stream */ -+ }; -+ -+ -+ DRIVER courierdriver = { -+ "mc", /* Why a separate driver? So that createproto will work */ -+ DR_MAIL|DR_LOCAL|DR_NAMESPACE|DR_DIRFMT, /* driver flags */ -+ (DRIVER *) NIL, /* next driver */ -+ maildir_valid, /* mailbox is valid for us */ -+ maildir_parameters, /* manipulate parameters */ -+ NIL, /* scan mailboxes */ -+ courier_list, /* find mailboxes */ -+ maildir_lsub, /* find subscribed mailboxes */ -+ maildir_sub, /* subscribe to mailbox */ -+ maildir_unsub, /* unsubscribe from mailbox */ -+ maildir_create, /* create mailbox */ -+ maildir_delete, /* delete mailbox */ -+ maildir_rename, /* rename mailbox */ -+ mail_status_default, /* status of mailbox */ -+ maildir_open, /* open mailbox */ -+ maildir_close, /* close mailbox */ -+ maildir_fast, /* fetch message "fast" attributes */ -+ NIL, /* fetch message flags */ -+ NIL, /* fetch overview */ -+ NIL, /* fetch message structure */ -+ maildir_header, /* fetch message header */ -+ maildir_text, /* fetch message body */ -+ NIL, /* fetch partial message text */ -+ NIL, /* unique identifier */ -+ NIL, /* message number */ -+ NIL, /* modify flags */ -+ maildir_flagmsg, /* per-message modify flags */ -+ NIL, /* search for message based on criteria */ -+ NIL, /* sort messages */ -+ NIL, /* thread messages */ -+ maildir_ping, /* ping mailbox to see if still alive */ -+ maildir_check, /* check for new messages */ -+ maildir_expunge, /* expunge deleted messages */ -+ maildir_copy, /* copy messages to another mailbox */ -+ maildir_append, /* append string message to mailbox */ -+ NIL /* garbage collect stream */ -+ }; -+ -+ MAILSTREAM maildirproto = {&maildirdriver}; /* prototype stream */ -+ MAILSTREAM courierproto = {&courierdriver}; /* prototype stream */ -+ -+ long maildir_dirfmttest (char *name) -+ { -+ int i; -+ for (i = 0; mdstruct[i] && strcmp(name, mdstruct[i]); i++); -+ return (i < EndDir) || !strcmp(name, MDDIR) -+ || !strncmp(name, MDUIDLAST, strlen(MDUIDLAST)) -+ || !strncmp(name, MDUIDTEMP, strlen(MDUIDTEMP)) ? LONGT : NIL; -+ } -+ -+ void -+ md_domain_name(void) -+ { -+ int i, j; -+ -+ strcpy(mdlocaldomain, mylocalhost ()); -+ for (i = 0; mdlocaldomain[i] != '\0' ;) -+ if(mdlocaldomain[i] == '/' || mdlocaldomain[i] == ':'){ -+ for(j = strlen(mdlocaldomain); j >= i; j--) -+ mdlocaldomain[j+4] = mdlocaldomain[j]; -+ mdlocaldomain[i++] = '\\'; -+ mdlocaldomain[i++] = '0'; -+ if(mdlocaldomain[i] == '/'){ -+ mdlocaldomain[i++] = '5'; -+ mdlocaldomain[i++] = '7'; -+ } else { -+ mdlocaldomain[i++] = '7'; -+ mdlocaldomain[i++] = '2'; -+ } -+ } -+ else -+ i++; -+ } -+ -+ char * -+ myrootdir(char *name) -+ { -+ return myhomedir(); -+ } -+ -+ char * -+ mdirpath(void) -+ { -+ char *path = maildir_parameters(GET_MDINBOXPATH, NIL); -+ return path ? (path[0] ? path : ".") : "Maildir"; -+ } -+ -+ /* remove the "#md/" or "#mc/" part from a folder name -+ * memory freed by caller -+ */ -+ char * -+ maildir_remove_root (char *name) -+ { -+ int courier = IS_COURIER(name), offset; -+ char realname[MAILTMPLEN]; -+ -+ offset = maildir_valid_name(name) ? (name[3] == '/' ? 4 : 3) : 0; -+ if(courier) -+ courier_realname(name+offset, realname); -+ else -+ strcpy(realname, name+offset); -+ return cpystr(realname); -+ } -+ -+ -+ /* Check validity of the name, we accept: -+ * a) #md/directory/folder -+ * b) #md/inbox -+ * A few considerations: We can only accept as valid -+ * a) names that start with #md/ and the directory exists or -+ * b) names that do not start with #md/ but are maildir directories (have -+ * the /cur, /tmp and /new structure) -+ */ -+ int maildir_valid_name (char *name) -+ { -+ char tmpname[MAILTMPLEN] = {'\0'}; -+ -+ if (mdfpath) -+ fs_give((void **)&mdfpath); -+ if (name && (name[0] != '#')) -+ snprintf(tmpname, sizeof(tmpname), "%s%s",MDPREFIX(CCLIENT), name); -+ mdfpath = cpystr(tmpname[0] ? tmpname : name); -+ -+ return IS_CCLIENT(name) || IS_COURIER(name); -+ } -+ -+ /* Check if the directory whose path is given by name is a valid maildir -+ * directory (contains /cur, /tmp and /new) -+ */ -+ int maildir_valid_dir (char *name) -+ { -+ int len; -+ DirNamesType i; -+ struct stat sbuf; -+ char tmp[MAILTMPLEN]; -+ -+ if(name[strlen(name) - 1] == '/') -+ name[strlen(name) - 1] = '\0'; -+ len = strlen(name); -+ for (i = Cur; i != EndDir; i++){ -+ MDFLD(tmp, name, i); -+ if (stat(tmp, &sbuf) < 0 || !S_ISDIR(sbuf.st_mode)) -+ break; +--- + README.maildir | 149 ++ + alpine/alpine.c | 5 + alpine/confscroll.c | 6 + imap/src/c-client/mail.c | 11 + imap/src/c-client/mail.h | 4 + imap/src/osdep/unix/Makefile | 8 + imap/src/osdep/unix/dummy.c | 48 + imap/src/osdep/unix/maildir.c | 2638 ++++++++++++++++++++++++++++++++++++++++++ + imap/src/osdep/unix/maildir.h | 226 +++ + imap/src/osdep/unix/os_cyg.h | 1 + pith/conf.c | 27 + pith/conf.h | 4 + pith/conftype.h | 6 + pith/init.c | 3 + pith/pattern.c | 26 + pith/pine.hlp | 139 ++ + pith/send.c | 7 + 17 files changed, 3293 insertions(+), 15 deletions(-) + +Index: alpine-2.11/README.maildir +=================================================================== +--- /dev/null ++++ alpine-2.11/README.maildir +@@ -0,0 +1,149 @@ ++--------------------------------------- ++ ++Maildir Driver for Alpine 2.0 ++By Eduardo Chappa ++ ++ ++--------------------------------------- ++1. General Information About This Patch ++--------------------------------------- ++ ++This patch adds support for the maildir format to Alpine. We take the ++approach that this patch is one more driver among the number of formats ++supported by Alpine (more generally c-client). This approach differs from ++older versions of similar patches, in that once a maildir patch was ++applied, it was assumed that all your folders would be created in the ++maildir format. ++ ++This patch does not assume that maildir is a preferred format, instead ++puts maildir in equal footing with other formats (mbox, mbx, mix, etc), ++and so a maildir folder in the mail/ collection is treated in the same way ++as any other folder in any other format. In other words, just by reading ++the name of a folder, or opening it, or doing any operation with it, you ++can not know in which format the folder is. ++ ++This implies that if you want to add a folder in the maildir format to the ++mail/ collection, then you must add by pressing "A" in the folder list ++collection and enter "#driver.md/mail/name_maildir_folder". ++ ++If you only want to use maildir, however, you can do so too. In this case, ++you must create a maildir collection. In that collection, only maildir ++folders will be listed. If there is any folder in any other format, that ++folder will be ignored. In another words, any folder listed there is in ++maildir format and can be accessed through that collection, conversely, ++any folder not listed there is not in maildir format and there is no way ++to access it using this collection. ++ ++In order to create a maildir collection, you could press M S L, and "A" to ++add a collection. Fill in the required fields as follows: ++ ++Nickname : Anything ++Server : ++Path : #md/relative/path/to/maildir/collection/ ++View : ++ ++For example, if "path" is set to "#md/mail/", then Alpine will look for your ++maildir folders that are in ~/mail/. ++ ++The code in this patch is mostly based in code for the unix driver plus ++some combinations of the mh, mbx and nntp drivers for the c-client ++library. Those drivers were designed by Mark Crispin, and bugs in this ++code are not his bugs, but my own. ++ ++ I got all the specification for this patch from ++http://cr.yp.to/proto/maildir.html. If you know of a place with a better ++specification for maildir format please let me know. The method this patch ++uses to create a unique filename for a message is one of the "old ++fashioned" methods. I realize that this is old fashioned, but it is ++portable, and portability is the main reason why I decided to use an old ++fashioned method (most methods are not portable. See the word ++"Unfortunately" in that document). ++ ++-------------- ++2. Other Goals ++-------------- ++ ++ It is intended that this code will work well with any application ++written using the c-client library. Of paramount importance is to make the ++associated imap server work well when the server accesses a folder in ++Maildir format. The program mailutil should also work flawlessly with this ++implemetation of the driver. ++ ++ It is intended that this driver be fast and stable. We intend not to ++patch Alpine to make this driver do its work, unless such patching is for ++fixing bugs in Alpine or to pass parameters to the driver. ++ ++------------------------------------------------------------------------ ++3. What are the known bugs of this implementation of the Maildir driver? ++------------------------------------------------------------------------ ++ ++ I don't know any at this time. There have been bugs before, though, but ++I try to fix bugs as soon as they are reported. ++ ++---------- ++4. On UIDs ++---------- ++ ++ This patch keeps uids in the name of the file that contains the message, ++by adding a ",u=" string to the file name to save the uid of a message. A ++file is kept between sessions to save information on the last uid assigned ++and its time of validity. Only one session with writing access can write ++uids, all others must wait for the other session to assign them. The ++session assigning uids creates a ".uidtemp" file which other sessions must ++not disturb. ++ ++ Uid support appeared in Alpine 1.00 (snapshot 925), and is experimental, ++please report any problems. ++ ++---------------------------------------------- ++5. Configuring Alpine and Setting up a Maildir ++---------------------------------------------- ++ ++Once this approach was chosen, it implied the following: ++ ++ * This patch assumes that your INBOX is located at "$HOME/Maildir". ++ This is a directory which should have three subdirectories "cur", ++ "tmp" and "new". Mail is delivered to 'new' and read from 'cur'. I ++ have added a configuration option "maildir-location" which can be ++ used to tell Alpine where your Maildir inbox is, in case your system ++ does not use the above directory (e.g. your system may use ++ "~/.maildir"). In this case define that variable to be the name of ++ the directory where your e-mail is being delivered (e.g. ++ ".maildir"). ++ ++ * If you want to use the above configuration as your inbox, you must ++ define your inbox-path as "#md/inbox" (no quotes). You can define ++ the inbox-path like above even if you have changed the ++ maildir-location variable. That's the whole point of that variable. ++ ++------------------------------------------- ++6. What about Courier/Dovecot file systems? ++------------------------------------------- ++ ++In a courier file system all folders are subfolders of a root folder ++called INBOX. Normally INBOX is located at ~/Maildir and subfolders are ++"dot" directories in ~/Maildir. For example ~/Maildir/.Trash is a ++subfolder of INBOX and is accessed with the nickname "INBOX.Trash". ++ ++You can not access folders in this way unless you preceed them with the ++string "#mc/". The purpose of the string "#mc/" is to warn Alpine that a ++collection in the Courier format is going to be accessed. Therefore, you ++can SELECT a folder like "#mc/INBOX.Trash", but not "INBOX.Trash" ++ ++You can access a collection through a server, but if you want to access a ++collection of folders created using the Courier server, you MUST edit your ++".pinerc" file and enter the definition of the collection as follows: ++ ++folder-collections="Anything you want" #mc/INBOX.[] ++ ++You can replace the string "#mc/INBOX." by something different, for example ++"#mc/Courier/." will make Alpine search for your collection in ~/Courier. ++ ++You can not add this setting directly into Alpine because Alpine fails to ++accept this value from its input, but it takes it correctly when it is ++added through the ".pinerc" file. ++ ++You can access your inbox as "#mc/INBOX" or "#md/INBOX". Both definitions ++point to the same place. ++ ++Last Updated May 28, 2011 +Index: alpine-2.11/alpine/alpine.c +=================================================================== +--- alpine-2.11.orig/alpine/alpine.c ++++ alpine-2.11/alpine/alpine.c +@@ -558,6 +558,11 @@ main(int argc, char **argv) + if(F_ON(F_MAILDROPS_PRESERVE_STATE, ps_global)) + mail_parameters(NULL, SET_SNARFPRESERVE, (void *) TRUE); + ++#ifndef _WINDOWS ++ mail_parameters(NULL,SET_COURIERSTYLE, ++ (void *)(F_ON(F_COURIER_FOLDER_LIST, ps_global) ? 1 : 0)); ++#endif ++ + rvl = 0L; + if(pine_state->VAR_NNTPRANGE){ + if(!SVAR_NNTPRANGE(pine_state, rvl, tmp_20k_buf, SIZEOF_20KBUF)) +Index: alpine-2.11/alpine/confscroll.c +=================================================================== +--- alpine-2.11.orig/alpine/confscroll.c ++++ alpine-2.11/alpine/confscroll.c +@@ -5533,6 +5533,12 @@ fix_side_effects(struct pine *ps, struct + (void *)var->current_val.p); + } + #endif ++#ifndef _WINDOWS ++ else if(var == &ps->vars[V_MAILDIR_LOCATION]){ ++ if(var->current_val.p && var->current_val.p[0]) ++ mail_parameters(NULL, SET_MDINBOXPATH, (void *)var->current_val.p); + } -+ name[len] = '\0'; -+ return (i == EndDir) ? T : NIL; -+ } -+ -+ void courier_realname(char *name, char *realname) -+ { -+ int i,j; -+ -+ if(!name) -+ return; -+ -+ for (i = 0, j = 0; i < MAILTMPLEN && j < strlen(name); j++, i++){ -+ realname[i] = name[j]; -+ if(name[j] == '/' && name[j+1] != '.' && name[j+1] != '%' -+ && name[j+1] != '*') -+ realname[++i] = '.'; -+ } -+ if(realname[i-1] == '.') -+ i--; -+ realname[i] = '\0'; -+ } -+ -+ -+ /* given a maildir folder, return its path. Memory freed by caller. Directory -+ * does not contain the trailing slash "/". On error NULL is returned. -+ */ -+ int maildir_file_path (char *name, char *tmp, size_t sizeoftmp) -+ { -+ char *maildirpath = mdirpath(), *rname; -+ int courier = IS_COURIER(name); -+ -+ /* There are several ways in which the path can come, so we will handle -+ them here. First we deal with #mc/ or #md/ prefix by removing the -+ prefix, if any */ -+ -+ if(strlen(name) >= MAILTMPLEN) -+ name[MAILTMPLEN] = '\0'; -+ strcpy(tmp, name); -+ rname = maildir_remove_root(tmp); -+ tmp[0] = '\0'; /* just in case something fails */ -+ -+ if (strlen(myrootdir(rname)) + -+ max(strlen(rname), strlen(maildirpath)) > sizeoftmp){ -+ errno = ENAMETOOLONG; -+ snprintf(tmp, sizeoftmp, "Error opening \"%s\": %s", rname, strerror (errno)); -+ mm_log(tmp,ERROR); -+ if(rname) fs_give((void **)&rname); -+ return NIL; -+ } -+ -+ /* There are two ways in which the name can come here, either as a -+ full path or not. If it is not a full path it can come in two ways, -+ either as a file system path (Maildir/.Drafts) or as a maildir path -+ (INBOX.Drafts) -+ */ -+ -+ if(*rname == '/'){ /* full path */ -+ strncpy(tmp, rname, sizeoftmp); /* do nothing */ -+ tmp[sizeoftmp-1] = '\0'; ++#endif + else if(revert && standard_radio_var(ps, var)){ + + cur_rule_value(var, TRUE, FALSE); +Index: alpine-2.11/imap/src/c-client/mail.c +=================================================================== +--- alpine-2.11.orig/imap/src/c-client/mail.c ++++ alpine-2.11/imap/src/c-client/mail.c +@@ -991,7 +991,7 @@ long mail_create (MAILSTREAM *stream,cha + MAILSTREAM *ts; + char *s,*t,tmp[MAILTMPLEN]; + size_t i; +- DRIVER *d; ++ DRIVER *d, *md; + /* never allow names with newlines */ + if (s = strpbrk (mailbox,"\015\012")) { + MM_LOG ("Can't create mailbox with such a name",ERROR); +@@ -1015,6 +1015,8 @@ long mail_create (MAILSTREAM *stream,cha + return NIL; + } + ++ /* Hack, we should do this better, but it works */ ++ for (md = maildrivers; md && strcmp (md->name, "md"); md = md->next); + /* see if special driver hack */ + if ((mailbox[0] == '#') && ((mailbox[1] == 'd') || (mailbox[1] == 'D')) && + ((mailbox[2] == 'r') || (mailbox[2] == 'R')) && +@@ -1045,6 +1047,13 @@ long mail_create (MAILSTREAM *stream,cha + (((*mailbox == '{') || (*mailbox == '#')) && + (stream = mail_open (NIL,mailbox,OP_PROTOTYPE | OP_SILENT)))) + d = stream->dtb; ++ else if(mailbox[0] == '#' ++ && (mailbox[1] == 'm' || mailbox[1] == 'M') ++ && (mailbox[2] == 'd' || mailbox[2] == 'D' ++ || mailbox[2] == 'c' || mailbox[2] == 'C') ++ && mailbox[3] == '/' ++ && mailbox[4] != '\0') ++ return (*md->create)(stream, mailbox); + else if ((*mailbox != '{') && (ts = default_proto (NIL))) d = ts->dtb; + else { /* failed utterly */ + sprintf (tmp,"Can't create mailbox %.80s: indeterminate format",mailbox); +Index: alpine-2.11/imap/src/c-client/mail.h +=================================================================== +--- alpine-2.11.orig/imap/src/c-client/mail.h ++++ alpine-2.11/imap/src/c-client/mail.h +@@ -353,6 +353,10 @@ + #define SET_SCANCONTENTS (long) 573 + #define GET_MHALLOWINBOX (long) 574 + #define SET_MHALLOWINBOX (long) 575 ++#define GET_COURIERSTYLE (long) 576 ++#define SET_COURIERSTYLE (long) 577 ++#define SET_MDINBOXPATH (long) 578 ++#define GET_MDINBOXPATH (long) 579 + + /* Driver flags */ + +Index: alpine-2.11/imap/src/osdep/unix/Makefile +=================================================================== +--- alpine-2.11.orig/imap/src/osdep/unix/Makefile ++++ alpine-2.11/imap/src/osdep/unix/Makefile +@@ -144,7 +144,7 @@ DEFAULTAUTHENTICATORS=ext md5 pla log + # However, mh needs to be before any sysinbox formats (such as mmdf or unix) + # since otherwise INBOX won't work correctly when mh_allow_inbox is set. + # +-DEFAULTDRIVERS=imap nntp pop3 mix mx mbx tenex mtx mh mmdf unix news phile ++DEFAULTDRIVERS=maildir courier imap nntp pop3 mix mx mbx tenex mtx mh mmdf unix news phile + CHUNKSIZE=65536 + + # Normally no need to change any of these +@@ -153,7 +153,7 @@ ARCHIVE=c-client.a + BINARIES=osdep.o mail.o misc.o newsrc.o smanager.o utf8.o utf8aux.o siglocal.o \ + dummy.o pseudo.o netmsg.o flstring.o fdstring.o \ + rfc822.o nntp.o smtp.o imap4r1.o pop3.o \ +- unix.o mbx.o mmdf.o tenex.o mtx.o news.o phile.o mh.o mx.o mix.o ++ unix.o mbx.o mmdf.o tenex.o mtx.o news.o phile.o mh.o mx.o mix.o maildir.o + CFLAGS=-g + + CAT=cat +@@ -290,7 +290,7 @@ cvx: # Convex + + cyg: # Cygwin - note that most local file drivers don't work!! + $(BUILD) `$(CAT) SPECIALS` OS=$@ \ +- DEFAULTDRIVERS="imap nntp pop3 mbx unix phile" \ ++ DEFAULTDRIVERS="imap nntp pop3 mbx unix maildir phile" \ + SIGTYPE=psx CHECKPW=cyg LOGINPW=cyg CRXTYPE=std \ + SPOOLDIR=/var \ + ACTIVEFILE=/usr/local/news/lib/active \ +@@ -900,7 +900,7 @@ tenex.o: mail.h misc.h osdep.h dummy.h + unix.o: mail.h misc.h osdep.h unix.h pseudo.h dummy.h + utf8.o: mail.h misc.h osdep.h utf8.h tmap.c widths.c + utf8aux.o: mail.h misc.h osdep.h utf8.h +- ++maildir.o: mail.h misc.h osdep.h maildir.h dummy.h + + # OS-dependent + +Index: alpine-2.11/imap/src/osdep/unix/dummy.c +=================================================================== +--- alpine-2.11.orig/imap/src/osdep/unix/dummy.c ++++ alpine-2.11/imap/src/osdep/unix/dummy.c +@@ -106,13 +106,19 @@ MAILSTREAM dummyproto = {&dummydriver}; + * Accepts: mailbox name + * Returns: our driver if name is valid, NIL otherwise + */ +- ++char * maildir_remove_root(char *); + DRIVER *dummy_valid (char *name) + { +- char *s,tmp[MAILTMPLEN]; ++ char *s,tmp[MAILTMPLEN], *rname; + struct stat sbuf; ++ ++ if(strlen(name) > MAILTMPLEN) ++ name[MAILTMPLEN] = '\0'; ++ ++ strcpy(tmp, name); ++ rname = maildir_remove_root(tmp); + /* must be valid local mailbox */ +- if (name && *name && (*name != '{') && (s = mailboxfile (tmp,name))) { ++ if (rname && *rname && (*rname != '{') && (s = mailboxfile (tmp,rname))) { + /* indeterminate clearbox INBOX */ + if (!*s) return &dummydriver; + else if (!stat (s,&sbuf)) switch (sbuf.st_mode & S_IFMT) { +@@ -121,8 +127,9 @@ DRIVER *dummy_valid (char *name) + return &dummydriver; + } + /* blackbox INBOX does not exist yet */ +- else if (!compare_cstring (name,"INBOX")) return &dummydriver; ++ else if (!compare_cstring (rname,"INBOX")) return &dummydriver; + } ++ if(rname) fs_give((void **)&rname); + return NIL; + } + +@@ -454,6 +461,8 @@ long dummy_create (MAILSTREAM *stream,ch + { + char *s,tmp[MAILTMPLEN]; + long ret = NIL; ++ if(!strncmp(mailbox,"#md/",4) || !strncmp(mailbox,"#mc/", 4)) ++ return maildir_create(stream, mailbox); + /* validate name */ + if (!(compare_cstring (mailbox,"INBOX") && (s = dummy_file (tmp,mailbox)))) { + sprintf (tmp,"Can't create %.80s: invalid name",mailbox); +@@ -519,6 +528,14 @@ long dummy_delete (MAILSTREAM *stream,ch + { + struct stat sbuf; + char *s,tmp[MAILTMPLEN]; ++ if (!strncmp(mailbox,"#md/",4) || !strncmp(mailbox,"#mc/", 4) ++ || is_valid_maildir(&mailbox)){ ++ char tmp[MAILTMPLEN] = {'\0'}; ++ strcpy(tmp, mailbox); ++ if(tmp[strlen(tmp) - 1] != '/') ++ tmp[strlen(tmp)] = '/'; ++ return maildir_delete(stream, tmp); ++ } + if (!(s = dummy_file (tmp,mailbox))) { + sprintf (tmp,"Can't delete - invalid name: %.80s",s); + MM_LOG (tmp,ERROR); +@@ -544,12 +561,23 @@ long dummy_delete (MAILSTREAM *stream,ch + long dummy_rename (MAILSTREAM *stream,char *old,char *newname) + { + struct stat sbuf; +- char c,*s,tmp[MAILTMPLEN],mbx[MAILTMPLEN],oldname[MAILTMPLEN]; ++ char c,*s,tmp[MAILTMPLEN],mbx[MAILTMPLEN],oldname[MAILTMPLEN], *rold, *rnewname; ++ ++ if(strlen(old) > MAILTMPLEN) ++ old[MAILTMPLEN] = '\0'; ++ ++ if(strlen(newname) > MAILTMPLEN) ++ newname[MAILTMPLEN] = '\0'; ++ ++ strcpy(tmp, old); ++ rold = maildir_remove_root(tmp); ++ strcpy(tmp, newname); ++ rnewname = maildir_remove_root(tmp); + /* no trailing / allowed */ +- if (!dummy_file (oldname,old) || !(s = dummy_file (mbx,newname)) || ++ if (!dummy_file (oldname,rold) || !(s = dummy_file (mbx,rnewname)) || + stat (oldname,&sbuf) || ((s = strrchr (s,'/')) && !s[1] && + ((sbuf.st_mode & S_IFMT) != S_IFDIR))) { +- sprintf (mbx,"Can't rename %.80s to %.80s: invalid name",old,newname); ++ sprintf (mbx,"Can't rename %.80s to %.80s: invalid name",rold,rnewname); + MM_LOG (mbx,ERROR); + return NIL; + } +@@ -565,14 +593,16 @@ long dummy_rename (MAILSTREAM *stream,ch + } + } + /* rename of non-ex INBOX creates dest */ +- if (!compare_cstring (old,"INBOX") && stat (oldname,&sbuf)) ++ if (!compare_cstring (rold,"INBOX") && stat (oldname,&sbuf)) + return dummy_create (NIL,mbx); + if (rename (oldname,mbx)) { +- sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %.80s",old,newname, ++ sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %.80s",rold,rnewname, + strerror (errno)); + MM_LOG (tmp,ERROR); + return NIL; + } ++ if(rold) fs_give((void **)&rold); ++ if(rnewname) fs_give((void **)&rnewname); + return T; /* return success */ + } + +Index: alpine-2.11/imap/src/osdep/unix/maildir.c +=================================================================== +--- /dev/null ++++ alpine-2.11/imap/src/osdep/unix/maildir.c +@@ -0,0 +1,2638 @@ ++/* ++ * Maildir driver for Alpine 2.00 ++ * ++ * Written by Eduardo Chappa ++ * Last Update: May 29, 2011. ++ * ++ */ ++ ++#include ++#include ++#include ++extern int errno; /* just in case */ ++#include "mail.h" ++#include ++#include ++#include ++#include "osdep.h" ++#include "rfc822.h" ++#include "fdstring.h" ++#include "misc.h" ++#include "dummy.h" ++#include "maildir.h" ++ ++/* Driver dispatch used by MAIL */ ++DRIVER maildirdriver = { ++ "md", /* driver name, yes it's md, not maildir */ ++ DR_MAIL|DR_LOCAL|DR_NAMESPACE|DR_DIRFMT, /* driver flags */ ++ (DRIVER *) NIL, /* next driver */ ++ maildir_valid, /* mailbox is valid for us */ ++ maildir_parameters, /* manipulate parameters */ ++ NIL, /* scan mailboxes */ ++ maildir_list, /* find mailboxes */ ++ maildir_lsub, /* find subscribed mailboxes */ ++ maildir_sub, /* subscribe to mailbox */ ++ maildir_unsub, /* unsubscribe from mailbox */ ++ maildir_create, /* create mailbox */ ++ maildir_delete, /* delete mailbox */ ++ maildir_rename, /* rename mailbox */ ++ mail_status_default, /* status of mailbox */ ++ maildir_open, /* open mailbox */ ++ maildir_close, /* close mailbox */ ++ maildir_fast, /* fetch message "fast" attributes */ ++ NIL, /* fetch message flags */ ++ NIL, /* fetch overview */ ++ NIL, /* fetch message structure */ ++ maildir_header, /* fetch message header */ ++ maildir_text, /* fetch message body */ ++ NIL, /* fetch partial message text */ ++ NIL, /* unique identifier */ ++ NIL, /* message number */ ++ NIL, /* modify flags */ ++ maildir_flagmsg, /* per-message modify flags */ ++ NIL, /* search for message based on criteria */ ++ NIL, /* sort messages */ ++ NIL, /* thread messages */ ++ maildir_ping, /* ping mailbox to see if still alive */ ++ maildir_check, /* check for new messages */ ++ maildir_expunge, /* expunge deleted messages */ ++ maildir_copy, /* copy messages to another mailbox */ ++ maildir_append, /* append string message to mailbox */ ++ NIL /* garbage collect stream */ ++}; ++ ++ ++DRIVER courierdriver = { ++ "mc", /* Why a separate driver? So that createproto will work */ ++ DR_MAIL|DR_LOCAL|DR_NAMESPACE|DR_DIRFMT, /* driver flags */ ++ (DRIVER *) NIL, /* next driver */ ++ maildir_valid, /* mailbox is valid for us */ ++ maildir_parameters, /* manipulate parameters */ ++ NIL, /* scan mailboxes */ ++ courier_list, /* find mailboxes */ ++ maildir_lsub, /* find subscribed mailboxes */ ++ maildir_sub, /* subscribe to mailbox */ ++ maildir_unsub, /* unsubscribe from mailbox */ ++ maildir_create, /* create mailbox */ ++ maildir_delete, /* delete mailbox */ ++ maildir_rename, /* rename mailbox */ ++ mail_status_default, /* status of mailbox */ ++ maildir_open, /* open mailbox */ ++ maildir_close, /* close mailbox */ ++ maildir_fast, /* fetch message "fast" attributes */ ++ NIL, /* fetch message flags */ ++ NIL, /* fetch overview */ ++ NIL, /* fetch message structure */ ++ maildir_header, /* fetch message header */ ++ maildir_text, /* fetch message body */ ++ NIL, /* fetch partial message text */ ++ NIL, /* unique identifier */ ++ NIL, /* message number */ ++ NIL, /* modify flags */ ++ maildir_flagmsg, /* per-message modify flags */ ++ NIL, /* search for message based on criteria */ ++ NIL, /* sort messages */ ++ NIL, /* thread messages */ ++ maildir_ping, /* ping mailbox to see if still alive */ ++ maildir_check, /* check for new messages */ ++ maildir_expunge, /* expunge deleted messages */ ++ maildir_copy, /* copy messages to another mailbox */ ++ maildir_append, /* append string message to mailbox */ ++ NIL /* garbage collect stream */ ++}; ++ ++MAILSTREAM maildirproto = {&maildirdriver}; /* prototype stream */ ++MAILSTREAM courierproto = {&courierdriver}; /* prototype stream */ ++ ++long maildir_dirfmttest (char *name) ++{ ++ int i; ++ for (i = 0; mdstruct[i] && strcmp(name, mdstruct[i]); i++); ++ return (i < EndDir) || !strcmp(name, MDDIR) ++ || !strncmp(name, MDUIDLAST, strlen(MDUIDLAST)) ++ || !strncmp(name, MDUIDTEMP, strlen(MDUIDTEMP)) ? LONGT : NIL; ++} ++ ++void ++md_domain_name(void) ++{ ++ int i, j; ++ ++ strcpy(mdlocaldomain, mylocalhost ()); ++ for (i = 0; mdlocaldomain[i] != '\0' ;) ++ if(mdlocaldomain[i] == '/' || mdlocaldomain[i] == ':'){ ++ for(j = strlen(mdlocaldomain); j >= i; j--) ++ mdlocaldomain[j+4] = mdlocaldomain[j]; ++ mdlocaldomain[i++] = '\\'; ++ mdlocaldomain[i++] = '0'; ++ if(mdlocaldomain[i] == '/'){ ++ mdlocaldomain[i++] = '5'; ++ mdlocaldomain[i++] = '7'; ++ } else { ++ mdlocaldomain[i++] = '7'; ++ mdlocaldomain[i++] = '2'; ++ } + } + else -+ snprintf (tmp, sizeoftmp, "%s/%s%s%s", myrootdir (rname), -+ strncmp (ucase (strcpy (tmp, rname)), "INBOX", 5) -+ ? rname : maildirpath, -+ strncmp (ucase (strcpy (tmp, rname)), "INBOX", 5) -+ ? "" : (courier ? "/" : ""), -+ strncmp (ucase (strcpy (tmp, rname)), "INBOX", 5) -+ ? "" : (*(rname+5) == MDSEPARATOR(courier) ? rname+5 : "")); -+ if(rname) fs_give((void **)&rname); -+ return tmp[0] ? T : NIL; -+ } -+ -+ /* This function is given a full path for a mailbox and returns -+ * if it is a valid maildir transformed to canonical notation -+ */ -+ int -+ is_valid_maildir (char **name) -+ { -+ if (!strncmp(*name, myrootdir (*name), strlen(myrootdir(*name)))){ -+ (*name) += strlen(myrootdir(*name)); -+ if (**name == '/') (*name)++; -+ } -+ return maildir_valid(*name) ? T : NIL; -+ } -+ -+ /* Check validity of mailbox. This routine does not send errors to log, other -+ * routines calling this one may do so, though -+ */ -+ -+ DRIVER *maildir_valid (char *name) -+ { -+ char tmpname[MAILTMPLEN]; -+ -+ maildir_file_path(name, tmpname, sizeof(tmpname)); -+ -+ return maildir_valid_dir(tmpname) -+ ? (IS_COURIER(name) ? &courierdriver : &maildirdriver) : NIL; -+ } -+ -+ void maildir_fast (MAILSTREAM *stream,char *sequence,long flags) -+ { -+ unsigned long i; -+ MESSAGECACHE *elt; -+ /* get sequence */ -+ if (stream && LOCAL && ((flags & FT_UID) ? -+ mail_uid_sequence (stream,sequence) : -+ mail_sequence (stream,sequence))) -+ for (i = 1L; i <= stream->nmsgs; i++) { -+ if ((elt = mail_elt (stream,i))->sequence && (elt->valid = T) && -+ !(elt->day && elt->rfc822_size)) { -+ ENVELOPE **env = NIL; -+ ENVELOPE *e = NIL; -+ if (!stream->scache) env = &elt->private.msg.env; -+ else if (stream->msgno == i) env = &stream->env; -+ else env = &e; -+ if (!*env || !elt->rfc822_size) { -+ STRING bs; -+ unsigned long hs; -+ char *ht = (*stream->dtb->header) (stream,i,&hs,NIL); -+ -+ if (!*env) rfc822_parse_msg (env,NIL,ht,hs,NIL,BADHOST, -+ stream->dtb->flags); -+ if (!elt->rfc822_size) { -+ (*stream->dtb->text) (stream,i,&bs,FT_PEEK); -+ elt->rfc822_size = hs + SIZE (&bs) - GETPOS (&bs); -+ } -+ } -+ -+ if (!elt->day && *env && (*env)->date) -+ mail_parse_date (elt,(*env)->date); -+ -+ if (!elt->day) elt->day = elt->month = 1; -+ mail_free_envelope (&e); -+ } -+ } -+ } -+ -+ int -+ maildir_eliminate_duplicate (char *name, struct direct ***flist, unsigned long *nfiles) -+ { -+ int i, j, k, error = 0, scanr; -+ char new[MAILTMPLEN], old[MAILTMPLEN], tmp[MAILTMPLEN], *str; -+ struct direct **names = NIL; -+ -+ if((scanr = maildir_doscandir(name, &names, CCLIENT)) < 0) -+ return -1; -+ -+ if(nfiles) *nfiles = scanr; -+ for(i = 0, j = 1, k = 0; j < scanr; i++, j++){ -+ if(k) -+ names[i] = names[i+k]; -+ if(same_maildir_file(names[i]->d_name, names[j]->d_name)){ -+ int d, f, r, s; -+ maildir_getflag(names[i]->d_name, &d, &f, &r, &s, NIL); -+ snprintf(old, sizeof(old), "%s/%s", name, names[i]->d_name); -+ snprintf(new, sizeof(new), "%s/.%s", name, names[i]->d_name); -+ if(rename(old, new) < 0 && errno != EEXIST) -+ error++; -+ if(!error){ -+ for(; j < scanr -+ && same_maildir_file(names[i]->d_name, names[j]->d_name) -+ ; j++, k++){ -+ maildir_getflag(names[j]->d_name, (d ? NIL : &d), -+ (f ? NIL : &f), (r ? NIL : &r), (s ? NIL : &s), NIL); -+ snprintf(tmp, sizeof(tmp), "%s/%s", name, names[j]->d_name); -+ if(unlink(tmp) < 0){ /* Hmmm... a problem, let's see */ -+ struct stat sbuf; -+ if (stat(tmp, &sbuf) == 0 && (sbuf.st_mode & S_IFMT) == S_IFREG) -+ error++; -+ } -+ } -+ if((str = strrchr(names[i]->d_name,FLAGSEP)) != NULL) *str = '\0'; -+ snprintf (old, sizeof(old), "%s/%s%s%s%s%s%s", name, names[i]->d_name, MDSEP(2), -+ MDFLAG(Draft, d), MDFLAG(Flagged, f), MDFLAG(Replied, r), -+ MDFLAG(Seen, s)); -+ if(rename(new, old) < 0) -+ error++; -+ } -+ } -+ -+ } -+ if(k > 0) -+ fs_give((void **)&names); -+ else -+ *flist = names; -+ return error ? -1 : k; -+ } -+ -+ int -+ maildir_doscandir(char *name, struct direct ***flist, int flag) -+ { -+ return scandir(name, flist, -+ flag == CCLIENT ? maildir_select : courier_dir_select, -+ flag == CCLIENT ? maildir_namesort : courier_dir_sort); -+ } -+ -+ /* -+ * return all files in a given directory. This is a separate call -+ * so that if there are warnings during compilation this only appears once. -+ */ -+ unsigned long -+ maildir_scandir (char *name, struct direct ***flist, -+ unsigned long *nfiles, int *scand, int flag) -+ { -+ struct stat sbuf; -+ int rv = -2; /* impossible value */ -+ -+ if (scand) -+ *scand = -1; /* assume error for safety */ -+ *nfiles = 0; -+ if((stat(name,&sbuf) < 0) -+ || (flag == CCLIENT -+ && ((rv = maildir_eliminate_duplicate(name, flist, nfiles)) < 0))) -+ return 0L; -+ -+ if (scand && (rv > 0 || rv == -2)) -+ *nfiles = maildir_doscandir(name, flist, flag); -+ -+ if(scand) *scand = *nfiles; -+ -+ return (unsigned long) sbuf.st_ctime; -+ } -+ -+ /* Does a message with given name exists (or was it removed)? -+ * Returns: 1 - yes, such message exist, -+ * 0 - No, that message does not exist anymore -+ * -+ * Parameters: stream, name of mailbox, new name if his message does not -+ * exist. -+ */ -+ -+ int maildir_message_exists(MAILSTREAM *stream, char *name, char *newfile) -+ { -+ char tmp[MAILTMPLEN]; -+ int gotit = NIL; -+ DIR *dir; -+ struct direct *d; -+ struct stat sbuf; -+ -+ /* First check directly if it exists, if not there, look for it */ -+ snprintf(tmp, sizeof(tmp), "%s/%s", LOCAL->path[Cur], name); -+ if ((stat(tmp, &sbuf) == 0) && ((sbuf.st_mode & S_IFMT) == S_IFREG)) -+ return T; -+ -+ if (!(dir = opendir (LOCAL->path[Cur]))) -+ return NIL; -+ -+ while ((d = readdir(dir)) && gotit == NIL){ -+ if (d->d_name[0] == '.') -+ continue; -+ if (same_maildir_file(d->d_name, name)){ -+ gotit = T; -+ strcpy(newfile, d->d_name); -+ } -+ } -+ closedir(dir); -+ return gotit; -+ } -+ -+ /* Maildir open */ -+ -+ MAILSTREAM *maildir_open (MAILSTREAM *stream) -+ { -+ char tmp[MAILTMPLEN]; -+ struct stat sbuf; -+ -+ if (!stream) return &maildirproto; -+ if (stream->local) fatal ("maildir recycle stream"); -+ md_domain_name(); /* get domain name for maildir files in mdlocaldomain */ -+ if(mypid == (pid_t) 0) -+ mypid = getpid(); -+ if (!stream->rdonly){ -+ stream->perm_seen = stream->perm_deleted = stream->perm_flagged = -+ stream->perm_answered = stream->perm_draft = T; -+ } -+ stream->local = (MAILDIRLOCAL *) fs_get (sizeof (MAILDIRLOCAL)); -+ memset(LOCAL, 0, sizeof(MAILDIRLOCAL)); -+ LOCAL->fd = -1; -+ -+ LOCAL->courier = IS_COURIER(stream->mailbox); -+ strcpy(tmp, stream->mailbox); -+ if (maildir_file_path (stream->mailbox, tmp, sizeof(tmp))) -+ LOCAL->dir = cpystr (tmp); -+ LOCAL->candouid = maildir_can_assign_uid(stream); -+ maildir_read_uid(stream, &stream->uid_last, &stream->uid_validity); -+ if (LOCAL->dir){ -+ LOCAL->path = (char **) fs_get(EndDir*sizeof(char *)); -+ MDFLD(tmp, LOCAL->dir, Cur); LOCAL->path[Cur] = cpystr (tmp); -+ MDFLD(tmp, LOCAL->dir, New); LOCAL->path[New] = cpystr (tmp); -+ MDFLD(tmp, LOCAL->dir, Tmp); LOCAL->path[Tmp] = cpystr (tmp); -+ if (stat (LOCAL->path[Cur],&sbuf) < 0) { -+ snprintf (tmp, sizeof(tmp), "Can't open folder %s: %s", -+ stream->mailbox,strerror (errno)); -+ mm_log (tmp,ERROR); -+ maildir_close(stream, 0); -+ return NIL; -+ } -+ } -+ -+ if(maildir_file_path (stream->mailbox, tmp, sizeof(tmp))){ -+ fs_give ((void **) &stream->mailbox); -+ stream->mailbox = cpystr(tmp); -+ } -+ -+ LOCAL->buf = (char *) fs_get (CHUNKSIZE); -+ LOCAL->buflen = CHUNKSIZE - 1; -+ stream->sequence++; -+ stream->nmsgs = stream->recent = 0L; -+ -+ maildir_parse_folder(stream, 1); -+ -+ return stream; -+ } -+ -+ /* Maildir initial parsing of the folder */ -+ void -+ maildir_parse_folder (MAILSTREAM *stream, int full) -+ { -+ char tmp[MAILTMPLEN]; -+ struct direct **namescur = NIL, **namesnew = NIL; -+ unsigned long i, nfilescur = 0L, nfilesnew = 0L, oldpos, newpos, total; -+ int scan_err, rescan, loop = 0; -+ -+ if (!stream) /* what??? */ -+ return; -+ -+ MM_CRITICAL(stream); -+ -+ maildir_scandir (LOCAL->path[New], &namesnew, &nfilesnew, &scan_err, CCLIENT); -+ if (scan_err < 0) -+ maildir_abort(stream); -+ -+ /* Scan old messages first, escoba! */ -+ if(stream->rdonly || -+ (LOCAL && ((maildir_initial_check(stream, Cur) == 0) -+ || nfilesnew > 0L))){ -+ LOCAL->scantime = maildir_scandir (LOCAL->path[Cur], &namescur, &nfilescur, -+ &scan_err, CCLIENT); -+ if (scan_err < 0){ -+ if(namesnew){ -+ for(i = 0L; i < nfilesnew; i++) -+ fs_give((void **)&namesnew[i]); -+ fs_give((void **) &namesnew); -+ } -+ maildir_abort(stream); -+ } -+ } -+ if(LOCAL && (maildir_initial_check(stream, New) == 0) -+ && (nfilescur > 0L)){ -+ while(LOCAL && loop < 10){ -+ if(nfilesnew == 0L) -+ maildir_scandir (LOCAL->path[New], &namesnew, &nfilesnew, &scan_err, CCLIENT); -+ if (scan_err < 0){ -+ if(namesnew){ -+ for(i = 0L; i < nfilesnew; i++) -+ fs_give((void **)&namesnew[i]); -+ fs_give((void **) &namesnew); -+ } -+ maildir_abort(stream); -+ break; -+ } -+ for(i = 0L, rescan = 0, newpos = oldpos = 0L; -+ newpos < nfilescur && i < nfilesnew; i++){ -+ if(maildir_message_in_list(namesnew[i]->d_name, namescur, oldpos, -+ nfilescur - 1L, &newpos)){ -+ oldpos = newpos; -+ snprintf(tmp, sizeof(tmp), "%s/%s", LOCAL->path[New], namesnew[i]->d_name); -+ if(unlink(tmp) < 0) -+ scan_err = -1; -+ rescan++; -+ } -+ else -+ newpos = oldpos; -+ } -+ if(scan_err < 0) -+ maildir_abort(stream); -+ if(rescan == 0) -+ break; -+ else{ /* restart */ -+ if(namesnew){ -+ for(i = 0L; i < nfilesnew; i++) -+ fs_give((void **)&namesnew[i]); -+ fs_give((void **) &namesnew); -+ } -+ nfilesnew = 0L; -+ loop++; -+ } -+ } -+ } -+ if(loop == 10) -+ maildir_abort(stream); -+ if(LOCAL){ -+ if(stream->rdonly) -+ stream->recent = 0L; -+ total = namescur || stream->rdonly -+ ? maildir_parse_dir(stream, 0L, Cur, namescur, -+ nfilescur, full) : stream->nmsgs; -+ stream->nmsgs = maildir_parse_dir(stream, total, New, namesnew, -+ nfilesnew, full); -+ } -+ if(namesnew){ -+ for(i = 0L; i < nfilesnew; i++) -+ fs_give((void **)&namesnew[i]); -+ fs_give((void **) &namesnew); -+ } -+ if(namescur){ -+ for(i = 0L; i < nfilescur; i++) -+ fs_give((void **)&namescur[i]); -+ fs_give((void **) &namescur); -+ } -+ MM_NOCRITICAL(stream); -+ } -+ -+ int -+ maildir_initial_check (MAILSTREAM *stream, DirNamesType dirtype) -+ { -+ char *tmp; -+ struct stat sbuf; -+ -+ if (access (LOCAL->path[dirtype], R_OK|W_OK|X_OK) != 0){ -+ maildir_abort(stream); -+ return -1; -+ } -+ -+ if (dirtype != New && -+ (stat(LOCAL->path[Cur], &sbuf) < 0 || sbuf.st_ctime == LOCAL->scantime)) -+ return -1; -+ return 0; -+ } -+ -+ -+ /* Return the number of messages in the directory, while filling the -+ * elt structure. -+ */ -+ -+ unsigned long -+ maildir_parse_dir(MAILSTREAM *stream, unsigned long nmsgs, -+ DirNamesType dirtype, struct direct **names, -+ unsigned long nfiles, int full) -+ { -+ char tmp[MAILTMPLEN], file[MAILTMPLEN], newfile[MAILTMPLEN], *mdstr; -+ struct stat sbuf; -+ unsigned long i, new = 0L, l, uid_last; -+ unsigned long recent = stream ? stream->recent : 0L; -+ int d = 0, f = 0, r = 0, s = 0, t = 0; -+ int we_compute, in_list; -+ int silent = stream ? stream->silent : NIL; -+ MESSAGECACHE *elt; -+ -+ if (dirtype == Cur && !stream->rdonly) -+ for (i = 1L; i <= stream->nmsgs;){ -+ elt = mail_elt(stream, i); -+ in_list = elt && elt->private.spare.ptr && nfiles > 0L -+ ? (MDPOS(elt) < nfiles -+ ? same_maildir_file(MDFILE(elt), names[MDPOS(elt)]->d_name) -+ : NIL) -+ || maildir_message_in_list(MDFILE(elt), names, 0L, -+ nfiles - 1L, &MDPOS(elt)) -+ : NIL; -+ if (!in_list){ -+ if (elt->private.spare.ptr) -+ maildir_free_file ((void **) &elt->private.spare.ptr); -+ -+ if (elt->recent) --recent; -+ mail_expunged(stream,i); -+ } -+ else i++; -+ } -+ -+ stream->silent = T; -+ uid_last = 0L; -+ for (we_compute = 0, i = l = 1L; l <= nfiles; l++){ -+ unsigned long pos, uid; -+ if (dirtype == New && !stream->rdonly){ /* move new messages to cur */ -+ pos = l - 1L; -+ snprintf (file, sizeof(file), "%s/%s", LOCAL->path[New], names[pos]->d_name); -+ if(lstat(file,&sbuf) == 0) -+ switch(sbuf.st_mode & S_IFMT){ -+ case S_IFREG: -+ strcpy(tmp, names[pos]->d_name); -+ if((mdstr = strstr(tmp,MDSEP(3))) -+ || (mdstr = strstr(tmp,MDSEP(2)))) -+ *(mdstr+1) = '2'; -+ else -+ strcat(tmp, MDSEP(2)); -+ snprintf(newfile, sizeof(newfile), "%s/%s", LOCAL->path[Cur], tmp); -+ if(rename (file, newfile) != 0){ -+ mm_log("Unable to read new mail!", WARN); -+ continue; -+ } -+ unlink (file); -+ new++; -+ break; -+ case S_IFLNK: /* clean up, clean up, everybody, everywhere */ -+ if(unlink(file) < 0){ -+ if(LOCAL->link == NIL){ -+ mm_log("Unable to remove symbolic link", WARN); -+ LOCAL->link = T; -+ } -+ } -+ continue; -+ break; -+ default: -+ if(LOCAL && LOCAL->link == NIL){ -+ mm_log("Unrecognized file or link in folder", WARN); -+ LOCAL->link = T; -+ } -+ continue; -+ break; -+ } -+ } -+ mail_exists(stream, i + nmsgs); -+ elt = mail_elt(stream, i + nmsgs); -+ pos = (elt && elt->private.spare.ptr) ? MDPOS(elt) : l - 1L; -+ if (dirtype == New) elt->recent = T; -+ maildir_getflag(names[pos]->d_name, &d, &f, &r ,&s, &t); -+ if (elt->private.spare.ptr) -+ maildir_free_file_only ((void **)&elt->private.spare.ptr); -+ else{ -+ maildir_get_file((MAILDIRFILE **)&elt->private.spare.ptr); -+ we_compute++; -+ } -+ MDFILE(elt) = cpystr(names[pos]->d_name); -+ MDPOS(elt) = pos; -+ MDLOC(elt) = dirtype; -+ if (dirtype == Cur){ /* deal with UIDs */ -+ if(elt->private.uid == 0L) -+ elt->private.uid = maildir_get_uid(MDFILE(elt)); -+ if(elt->private.uid <= uid_last){ -+ uid = (we_compute ? uid_last : stream->uid_last) + 1L; -+ if(LOCAL->candouid) -+ maildir_assign_uid(stream, i + nmsgs, uid); -+ else -+ elt->private.uid = uid; -+ } -+ else -+ uid = elt->private.uid; -+ uid_last = uid; -+ if(uid_last > stream->uid_last) -+ stream->uid_last = uid_last; -+ } -+ if(dirtype == New && !stream->rdonly){ -+ maildir_free_file_only((void **)&elt->private.spare.ptr); -+ MDFILE(elt) = cpystr(tmp); -+ MDSIZE(elt) = sbuf.st_size; -+ MDMTIME(elt) = sbuf.st_mtime; -+ MDLOC(elt) = Cur; -+ } -+ if (elt->draft != d || elt->flagged != f || -+ elt->answered != r || elt->seen != s || elt->deleted != t){ -+ elt->draft = d; elt->flagged = f; elt->answered = r; -+ elt->seen = s; elt->deleted = t; -+ if (!we_compute && !stream->rdonly) -+ MM_FLAGS(stream, i+nmsgs); -+ } -+ maildir_get_date(stream, i+nmsgs); -+ elt->valid = T; -+ i++; -+ } -+ stream->silent = silent; -+ if(LOCAL->candouid && dirtype == Cur) -+ maildir_read_uid(stream, NULL, &stream->uid_validity); -+ if (dirtype == New && stream->rdonly) -+ new = nfiles; -+ mail_exists(stream, nmsgs + ((dirtype == New) ? new : nfiles)); -+ mail_recent(stream, recent + ((dirtype == New) ? new : 0L)); -+ -+ return (nmsgs + (dirtype == New ? new : nfiles)); -+ } -+ -+ long maildir_ping (MAILSTREAM *stream) -+ { -+ maildir_parse_folder(stream, 0); -+ if(stream && LOCAL){ -+ if(LOCAL->candouid < 0) -+ LOCAL->candouid++; -+ else if(LOCAL->candouid) -+ maildir_uid_renew_tempfile(stream); -+ else /* try again to get uids */ -+ LOCAL->candouid = maildir_can_assign_uid(stream); -+ } -+ return stream && LOCAL ? LONGT : NIL; -+ } -+ -+ int maildir_select (const struct direct *name) -+ { -+ return (name->d_name[0] != '.'); -+ } -+ -+ /* -+ * Unfortunately, there is no way to sort by arrival in this driver, this -+ * means that opening a folder in this driver using the scandir function -+ * will always make this driver slower than any driver that has a natural -+ * way of sorting by arrival (like a flat file format, "mbox", "mbx", etc). -+ */ -+ int maildir_namesort (const struct direct **d1, const struct direct **d2) -+ { -+ const struct direct *e1 = *(const struct direct **) d1; -+ const struct direct *e2 = *(const struct direct **) d2; -+ -+ return comp_maildir_file((char *) e1->d_name, (char *) e2->d_name); -+ } -+ -+ /* Maildir close */ -+ -+ void maildir_close (MAILSTREAM *stream, long options) -+ { -+ MESSAGECACHE *elt; -+ unsigned long i; -+ int silent = stream ? stream->silent : 0; -+ mailcache_t mc = (mailcache_t) mail_parameters (NIL,GET_CACHE,NIL); -+ -+ if (!stream) return; -+ -+ for (i = 1L; i <= stream->nmsgs; i++) -+ if((elt = (MESSAGECACHE *) (*mc)(stream,i,CH_ELT)) && elt->private.spare.ptr) -+ maildir_free_file ((void **) &elt->private.spare.ptr); -+ stream->silent = T; -+ if (options & CL_EXPUNGE) maildir_expunge (stream, NIL, NIL); -+ maildir_abort(stream); -+ if (mdfpath) fs_give((void **)&mdfpath); -+ if (mypid) mypid = (pid_t) 0; -+ stream->silent = silent; -+ } -+ -+ void maildir_check (MAILSTREAM *stream) -+ { -+ if (maildir_ping (stream)) mm_log ("Check completed",(long) NIL); -+ } -+ -+ long maildir_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs, long flags) -+ { -+ char tmp[MAILTMPLEN]; -+ unsigned long i; -+ MESSAGECACHE *elt; -+ char *s; -+ /* UID call "impossible" */ -+ if (flags & FT_UID || !LOCAL) return NIL; -+ elt = mail_elt (stream, msgno); -+ -+ if (!(flags & FT_PEEK) && !elt->seen){ -+ elt->seen = T; -+ maildir_flagmsg (stream, elt); -+ MM_FLAGS(stream, elt->msgno); -+ } -+ -+ MSGPATH(tmp, LOCAL->dir, MDFILE(elt), MDLOC(elt)); -+ if (LOCAL->fd < 0) /* if file closed ? */ -+ LOCAL->fd = open(tmp,O_RDONLY,NIL); -+ -+ if (LOCAL->fd < 0 && (errno == EACCES || errno == ENOENT)){ -+ INIT (bs, mail_string, "", 0); -+ elt->rfc822_size = 0L; -+ return NIL; -+ } -+ -+ s = maildir_text_work(stream, elt, &i, flags); -+ INIT (bs, mail_string, s, i); -+ return LONGT; -+ } -+ -+ char *maildir_text_work (MAILSTREAM *stream,MESSAGECACHE *elt, -+ unsigned long *length,long flags) -+ { -+ FDDATA d; -+ STRING bs; -+ char *s,tmp[CHUNK]; -+ unsigned long msgno = elt->msgno; -+ static int try = 0; -+ -+ if (length) -+ *length = 0L; -+ LOCAL->buf[0] = '\0'; -+ -+ MSGPATH(tmp, LOCAL->dir, MDFILE(elt), MDLOC(elt)); -+ if (LOCAL->fd < 0) /* if file closed ? */ -+ LOCAL->fd = open(tmp,O_RDONLY,NIL); -+ -+ if (LOCAL->fd < 0){ /* flag change? */ -+ if (try < 5){ -+ try++; -+ if (maildir_update_elt_maildirp(stream, msgno) > 0) -+ try = 0; -+ return maildir_text_work(stream, mail_elt(stream, msgno),length, flags); -+ } -+ try = 0; -+ return NULL; -+ } -+ -+ lseek (LOCAL->fd, elt->private.msg.text.offset,L_SET); -+ -+ if (flags & FT_INTERNAL) { /* initial data OK? */ -+ if (elt->private.msg.text.text.size > LOCAL->buflen) { -+ fs_give ((void **) &LOCAL->buf); -+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen = -+ elt->private.msg.text.text.size) + 1); -+ } -+ read (LOCAL->fd,LOCAL->buf,elt->private.msg.text.text.size); -+ LOCAL->buf[*length = elt->private.msg.text.text.size] = '\0'; -+ } -+ else { -+ if (elt->rfc822_size > LOCAL->buflen) { -+ fs_give ((void **) &LOCAL->buf); -+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen = elt->rfc822_size) + 1); -+ } -+ d.fd = LOCAL->fd; /* yes, set up file descriptor */ -+ d.pos = elt->private.msg.text.offset; -+ d.chunk = tmp; /* initial buffer chunk */ -+ d.chunksize = CHUNK; -+ INIT (&bs,fd_string,&d,elt->private.msg.text.text.size); -+ for (s = LOCAL->buf; SIZE (&bs);) switch (CHR (&bs)) { -+ case '\r': /* carriage return seen */ -+ *s++ = SNX (&bs); /* copy it and any succeeding LF */ -+ if (SIZE (&bs) && (CHR (&bs) == '\n')) *s++ = SNX (&bs); -+ break; -+ case '\n': -+ *s++ = '\r'; /* insert a CR */ -+ default: -+ *s++ = SNX (&bs); /* copy characters */ -+ } -+ *s = '\0'; /* tie off buffer */ -+ *length = s - (char *) LOCAL->buf; /* calculate length */ -+ } -+ close(LOCAL->fd); LOCAL->fd = -1; -+ return LOCAL->buf; -+ } -+ -+ /* maildir parse, fill the elt structure... well not all of it... */ -+ unsigned long maildir_parse_message(MAILSTREAM *stream, unsigned long msgno, -+ DirNamesType dirtype) -+ { -+ char *b, *s, *t, c; -+ char tmp[MAILTMPLEN]; -+ struct stat sbuf; -+ unsigned long i, len; -+ int d, f, r, se, dt; -+ MESSAGECACHE *elt; -+ -+ elt = mail_elt (stream,msgno); -+ MSGPATH(tmp, LOCAL->dir, MDFILE(elt), dirtype); -+ if(stat(tmp, &sbuf) == 0) -+ MDSIZE(elt) = sbuf.st_size; -+ -+ maildir_get_date(stream, msgno); -+ maildir_getflag(MDFILE(elt), &d, &f, &r ,&se, &dt); -+ elt->draft = d; elt->flagged = f; elt->answered = r; elt->seen = se; -+ elt->deleted = dt; elt->valid = T; -+ if (LOCAL->fd < 0) /* if file closed ? */ -+ LOCAL->fd = open(tmp,O_RDONLY,NIL); -+ -+ if (LOCAL->fd >= 0){ -+ s = (char *) fs_get (MDSIZE(elt) + 1); -+ read (LOCAL->fd,s,MDSIZE(elt)); -+ s[MDSIZE(elt)] = '\0'; -+ t = s + strlen(s); /* make t point to the end of s */ -+ for (i = 0L, b = s; b < t && !(i && (*b == '\n')); i = (*b++ == '\n')); -+ len = (*b ? ++b : b) - s; -+ elt->private.msg.header.text.size = -+ elt->private.msg.text.offset = len; -+ elt->private.msg.text.text.size = MDSIZE(elt) - len; -+ for (i = 0L, b = s, c = *b; b && -+ ((c < '\016' && ((c == '\012' && ++i) -+ ||(c == '\015' && *(b+1) == '\012' && ++b && (i +=2)))) -+ || b < t); i++, c= *++b); -+ elt->rfc822_size = i; -+ fs_give ((void **) &s); -+ close(LOCAL->fd); LOCAL->fd = -1; -+ } -+ return elt->rfc822_size; -+ } -+ -+ int -+ maildir_update_elt_maildirp(MAILSTREAM *stream, unsigned long msgno) -+ { -+ struct direct **names = NIL; -+ unsigned long i, nfiles, pos; -+ int d = 0, f = 0 , r = 0, s = 0, t = 0, in_list, scan_err; -+ MESSAGECACHE *elt; -+ -+ maildir_scandir (LOCAL->path[Cur], &names, &nfiles, &scan_err, CCLIENT); -+ -+ elt = mail_elt (stream,msgno); -+ -+ in_list = nfiles > 0L -+ ? maildir_message_in_list(MDFILE(elt), names, 0L, nfiles - 1L, &pos) -+ : NIL; -+ -+ if (in_list && pos >= 0L && pos < nfiles -+ && !strcmp(MDFILE(elt), names[pos]->d_name)){ -+ in_list = NIL; -+ maildir_abort(stream); -+ } -+ -+ if (in_list && pos >= 0L && pos < nfiles){ -+ maildir_free_file_only((void **)&elt->private.spare.ptr); -+ MDFILE(elt) = cpystr(names[pos]->d_name); -+ maildir_getflag(MDFILE(elt), &d, &f, &r ,&s, &t); -+ if (elt->draft != d || elt->flagged != f || -+ elt->answered != r || elt->seen != s || elt->deleted != t){ -+ elt->draft = d; elt->flagged = f; elt->answered = r; -+ elt->seen = s; elt->deleted = t; -+ MM_FLAGS(stream, msgno); -+ } -+ } -+ for (i = 0L; i < nfiles; i++) -+ fs_give((void **) &names[i]); -+ if (names) -+ fs_give((void **) &names); -+ return in_list ? 1 : -1; -+ } -+ -+ /* Maildir fetch message header */ -+ -+ char *maildir_header (MAILSTREAM *stream,unsigned long msgno, -+ unsigned long *length, long flags) -+ { -+ char tmp[MAILTMPLEN], *s; -+ MESSAGECACHE *elt; -+ static int try = 0; -+ -+ if (length) *length = 0; -+ if (flags & FT_UID || !LOCAL) return ""; /* UID call "impossible" */ -+ elt = mail_elt (stream,msgno); -+ if(elt->private.msg.header.text.size == 0) -+ maildir_parse_message(stream, msgno, MDLOC(elt)); -+ -+ MSGPATH(tmp, LOCAL->dir, MDFILE(elt), MDLOC(elt)); -+ if (LOCAL->fd < 0) -+ LOCAL->fd = open (tmp,O_RDONLY,NIL); -+ -+ if (LOCAL->fd < 0 && errno == EACCES){ -+ mm_log ("Message exists but can not be read. Envelope and body lost!",ERROR); -+ return NULL; -+ } -+ -+ if (LOCAL->fd < 0){ /* flag change? */ -+ if (try < 5){ -+ try++; -+ if (maildir_update_elt_maildirp(stream, msgno) > 0) -+ try = 0; -+ return maildir_header(stream, msgno, length, flags); -+ } -+ try = 0; -+ return NULL; -+ } -+ -+ if (flags & FT_INTERNAL){ -+ if(elt->private.msg.header.text.size > LOCAL->buflen){ -+ fs_give ((void **) &LOCAL->buf); -+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen = -+ elt->private.msg.header.text.size) + 1); -+ } -+ read (LOCAL->fd, (void *)LOCAL->buf, elt->private.msg.header.text.size); -+ LOCAL->buf[*length = elt->private.msg.header.text.size] = '\0'; -+ } -+ else{ -+ s = (char *) fs_get(elt->private.msg.header.text.size+1); -+ read (LOCAL->fd, (void *)s, elt->private.msg.header.text.size); -+ s[elt->private.msg.header.text.size] = '\0'; -+ *length = strcrlfcpy (&LOCAL->buf,&LOCAL->buflen,s, -+ elt->private.msg.header.text.size); -+ fs_give ((void **) &s); -+ } -+ elt->private.msg.text.offset = elt->private.msg.header.text.size; -+ elt->private.msg.text.text.size = MDSIZE(elt) - elt->private.msg.text.offset; -+ close(LOCAL->fd); LOCAL->fd = -1; -+ return LOCAL->buf; -+ } -+ -+ /* Maildir find list of subscribed mailboxes -+ * Accepts: mail stream -+ * pattern to search -+ */ -+ -+ void maildir_list (MAILSTREAM *stream,char *ref, char *pat) -+ { -+ char *s,test[MAILTMPLEN],file[MAILTMPLEN]; -+ long i = 0L; -+ -+ if((!pat || !*pat) && maildir_canonicalize (test,ref,"*") -+ && maildir_valid_name(test)){ /* there is a #md/ leading here */ -+ for (i = 3L; test[i] && test[i] != '/'; i++); -+ if ((s = strchr (test+i+1,'/')) != NULL) *++s = '\0'; -+ else test[0] = '\0'; -+ mm_list (stream,'/',test, LATT_NOSELECT); -+ } -+ else if (maildir_canonicalize (test,ref,pat)) { -+ if (test[3] == '/') { /* looking down levels? */ -+ /* yes, found any wildcards? */ -+ if ((s = strpbrk (test,"%*")) != NULL){ -+ /* yes, copy name up to that point */ -+ strncpy (file,test+4,i = s - (test+4)); -+ file[i] = '\0'; /* tie off */ -+ } -+ else strcpy (file,test+4);/* use just that name then */ -+ /* find directory name */ -+ if ((s = strrchr (file, '/')) != NULL){ -+ *s = '\0'; /* found, tie off at that point */ -+ s = file; -+ } -+ /* do the work */ -+ if(IS_COURIER(test)) -+ courier_list_work (stream,s,test,0); -+ else -+ maildir_list_work (stream,s,test,0); -+ } -+ /* always an INBOX */ -+ if (!compare_cstring (test,"#MD/INBOX")) -+ mm_list (stream,NIL,"#MD/INBOX",LATT_NOINFERIORS); -+ if (!compare_cstring (test,"#MC/INBOX")) -+ mm_list (stream,NIL,"#MC/INBOX",LATT_NOINFERIORS); -+ } -+ } -+ -+ void courier_list (MAILSTREAM *stream,char *ref, char *pat) -+ { -+ /* I am too lazy to do anything. Do you care to ask maildir list, please? -+ The real reason why this is a dummy function is because we do not want to -+ see the same folder listed twice. ++ i++; ++} ++ ++char * ++myrootdir(char *name) ++{ ++return myhomedir(); ++} ++ ++char * ++mdirpath(void) ++{ ++ char *path = maildir_parameters(GET_MDINBOXPATH, NIL); ++ return path ? (path[0] ? path : ".") : "Maildir"; ++} ++ ++/* remove the "#md/" or "#mc/" part from a folder name ++ * memory freed by caller + */ -+ } -+ -+ /* For those that want to hide things, we give them a chance to do so */ -+ void *maildir_parameters (long function, void *value) -+ { -+ void *ret = NIL; -+ switch ((int) function) { -+ case SET_MDINBOXPATH: -+ if(strlen((char *) value ) > 49) -+ strcpy(myMdInboxDir, "Maildir"); -+ else -+ strcpy(myMdInboxDir, (char *) value); -+ case GET_MDINBOXPATH: -+ if (myMdInboxDir[0] == '\0') strcpy(myMdInboxDir,"Maildir"); -+ ret = (void *) myMdInboxDir; -+ break; -+ case SET_COURIERSTYLE: -+ CourierStyle = (long) value; -+ case GET_COURIERSTYLE: -+ ret = (void *) CourierStyle; -+ break; -+ case GET_DIRFMTTEST: -+ ret = (void *) maildir_dirfmttest; -+ break; -+ default: -+ break; -+ } -+ return ret; -+ } -+ -+ int maildir_create_folder(char *mailbox) -+ { -+ char tmp[MAILTMPLEN], err[MAILTMPLEN]; -+ DirNamesType i; -+ ++char * ++maildir_remove_root (char *name) ++{ ++ int courier = IS_COURIER(name), offset; ++ char realname[MAILTMPLEN]; ++ ++ offset = maildir_valid_name(name) ? (name[3] == '/' ? 4 : 3) : 0; ++ if(courier) ++ courier_realname(name+offset, realname); ++ else ++ strcpy(realname, name+offset); ++ return cpystr(realname); ++} ++ ++ ++/* Check validity of the name, we accept: ++ * a) #md/directory/folder ++ * b) #md/inbox ++ * A few considerations: We can only accept as valid ++ * a) names that start with #md/ and the directory exists or ++ * b) names that do not start with #md/ but are maildir directories (have ++ * the /cur, /tmp and /new structure) ++ */ ++int maildir_valid_name (char *name) ++{ ++ char tmpname[MAILTMPLEN] = {'\0'}; ++ ++ if (mdfpath) ++ fs_give((void **)&mdfpath); ++ if (name && (name[0] != '#')) ++ snprintf(tmpname, sizeof(tmpname), "%s%s",MDPREFIX(CCLIENT), name); ++ mdfpath = cpystr(tmpname[0] ? tmpname : name); ++ ++ return IS_CCLIENT(name) || IS_COURIER(name); ++} ++ ++/* Check if the directory whose path is given by name is a valid maildir ++ * directory (contains /cur, /tmp and /new) ++ */ ++int maildir_valid_dir (char *name) ++{ ++ int len; ++ DirNamesType i; ++ struct stat sbuf; ++ char tmp[MAILTMPLEN]; ++ ++ if(name[strlen(name) - 1] == '/') ++ name[strlen(name) - 1] = '\0'; ++ len = strlen(name); + for (i = Cur; i != EndDir; i++){ -+ MDFLD(tmp, mailbox, i); -+ if (mkdir(tmp, 0700) && errno != EEXIST){ /* try to make new dir */ -+ snprintf (err, sizeof(err), "Can't create %s: %s", tmp, strerror(errno)); -+ mm_log (err,ERROR); -+ return NIL; -+ } ++ MDFLD(tmp, name, i); ++ if (stat(tmp, &sbuf) < 0 || !S_ISDIR(sbuf.st_mode)) ++ break; + } -+ return T; -+ } -+ -+ int maildir_create_work(char *mailbox, int loop) -+ { -+ char *s, c, err[MAILTMPLEN], tmp[MAILTMPLEN], tmp2[MAILTMPLEN], mbx[MAILTMPLEN]; -+ int fnlen, create_dir = 0, courier, mv; -+ struct stat sbuf; -+ long style = (long) maildir_parameters(GET_COURIERSTYLE, NIL); -+ -+ courier = IS_COURIER(mailbox); -+ strcpy(mbx, mailbox); -+ mv = maildir_valid(mbx) ? 1 : 0; -+ maildir_file_path(mailbox, tmp, sizeof(tmp)); -+ if (mailbox[strlen(mailbox) - 1] == MDSEPARATOR(courier)){ -+ create_dir++; -+ mailbox[strlen(mailbox) - 1] = '\0'; -+ } -+ -+ if(!loop && courier){ -+ if(mv){ -+ if(create_dir){ -+ if(style == CCLIENT) -+ strcpy (err,"Can not create directory: folder exists. Create subfolder"); -+ else -+ strcpy(err,"Folder and Directory already exist"); ++ name[len] = '\0'; ++ return (i == EndDir) ? T : NIL; ++} ++ ++void courier_realname(char *name, char *realname) ++{ ++ int i,j; ++ ++ if(!name) ++ return; ++ ++ for (i = 0, j = 0; i < MAILTMPLEN && j < strlen(name); j++, i++){ ++ realname[i] = name[j]; ++ if(name[j] == '/' && name[j+1] != '.' && name[j+1] != '%' ++ && name[j+1] != '*') ++ realname[++i] = '.'; ++ } ++ if(realname[i-1] == '.') ++ i--; ++ realname[i] = '\0'; ++} ++ ++ ++/* given a maildir folder, return its path. Memory freed by caller. Directory ++ * does not contain the trailing slash "/". On error NULL is returned. ++ */ ++int maildir_file_path (char *name, char *tmp, size_t sizeoftmp) ++{ ++ char *maildirpath = mdirpath(), *rname; ++ int courier = IS_COURIER(name); ++ ++ /* There are several ways in which the path can come, so we will handle ++ them here. First we deal with #mc/ or #md/ prefix by removing the ++ prefix, if any */ ++ ++ if(strlen(name) >= MAILTMPLEN) ++ name[MAILTMPLEN] = '\0'; ++ strcpy(tmp, name); ++ rname = maildir_remove_root(tmp); ++ tmp[0] = '\0'; /* just in case something fails */ ++ ++ if (strlen(myrootdir(rname)) + ++ max(strlen(rname), strlen(maildirpath)) > sizeoftmp){ ++ errno = ENAMETOOLONG; ++ snprintf(tmp, sizeoftmp, "Error opening \"%s\": %s", rname, strerror (errno)); ++ mm_log(tmp,ERROR); ++ if(rname) fs_give((void **)&rname); ++ return NIL; ++ } ++ ++ /* There are two ways in which the name can come here, either as a ++ full path or not. If it is not a full path it can come in two ways, ++ either as a file system path (Maildir/.Drafts) or as a maildir path ++ (INBOX.Drafts) ++ */ ++ ++ if(*rname == '/'){ /* full path */ ++ strncpy(tmp, rname, sizeoftmp); /* do nothing */ ++ tmp[sizeoftmp-1] = '\0'; ++ } ++ else ++ snprintf (tmp, sizeoftmp, "%s/%s%s%s", myrootdir (rname), ++ strncmp (ucase (strcpy (tmp, rname)), "INBOX", 5) ++ ? rname : maildirpath, ++ strncmp (ucase (strcpy (tmp, rname)), "INBOX", 5) ++ ? "" : (courier ? "/" : ""), ++ strncmp (ucase (strcpy (tmp, rname)), "INBOX", 5) ++ ? "" : (*(rname+5) == MDSEPARATOR(courier) ? rname+5 : "")); ++ if(rname) fs_give((void **)&rname); ++ return tmp[0] ? T : NIL; ++} ++ ++/* This function is given a full path for a mailbox and returns ++ * if it is a valid maildir transformed to canonical notation ++ */ ++int ++is_valid_maildir (char **name) ++{ ++ if (!strncmp(*name, myrootdir (*name), strlen(myrootdir(*name)))){ ++ (*name) += strlen(myrootdir(*name)); ++ if (**name == '/') (*name)++; ++ } ++ return maildir_valid(*name) ? T : NIL; ++} ++ ++/* Check validity of mailbox. This routine does not send errors to log, other ++ * routines calling this one may do so, though ++ */ ++ ++DRIVER *maildir_valid (char *name) ++{ ++ char tmpname[MAILTMPLEN]; ++ ++ maildir_file_path(name, tmpname, sizeof(tmpname)); ++ ++ return maildir_valid_dir(tmpname) ++ ? (IS_COURIER(name) ? &courierdriver : &maildirdriver) : NIL; ++} ++ ++void maildir_fast (MAILSTREAM *stream,char *sequence,long flags) ++{ ++ unsigned long i; ++ MESSAGECACHE *elt; ++ /* get sequence */ ++ if (stream && LOCAL && ((flags & FT_UID) ? ++ mail_uid_sequence (stream,sequence) : ++ mail_sequence (stream,sequence))) ++ for (i = 1L; i <= stream->nmsgs; i++) { ++ if ((elt = mail_elt (stream,i))->sequence && (elt->valid = T) && ++ !(elt->day && elt->rfc822_size)) { ++ ENVELOPE **env = NIL; ++ ENVELOPE *e = NIL; ++ if (!stream->scache) env = &elt->private.msg.env; ++ else if (stream->msgno == i) env = &stream->env; ++ else env = &e; ++ if (!*env || !elt->rfc822_size) { ++ STRING bs; ++ unsigned long hs; ++ char *ht = (*stream->dtb->header) (stream,i,&hs,NIL); ++ ++ if (!*env) rfc822_parse_msg (env,NIL,ht,hs,NIL,BADHOST, ++ stream->dtb->flags); ++ if (!elt->rfc822_size) { ++ (*stream->dtb->text) (stream,i,&bs,FT_PEEK); ++ elt->rfc822_size = hs + SIZE (&bs) - GETPOS (&bs); ++ } + } -+ else -+ strcpy (err, "Can't create mailbox: mailbox already exists"); -+ } -+ else{ -+ if(create_dir) -+ strcpy(err, "Can not create directory. Cread folder instead"); -+ else -+ err[0] = '\0'; -+ } -+ if(err[0]){ -+ mm_log (err,ERROR); ++ ++ if (!elt->day && *env && (*env)->date) ++ mail_parse_date (elt,(*env)->date); ++ ++ if (!elt->day) elt->day = elt->month = 1; ++ mail_free_envelope (&e); ++ } ++ } ++} ++ ++int ++maildir_eliminate_duplicate (char *name, struct direct ***flist, unsigned long *nfiles) ++{ ++ int i, j, k, error = 0, scanr; ++ char new[MAILTMPLEN], old[MAILTMPLEN], tmp[MAILTMPLEN], *str; ++ struct direct **names = NIL; ++ ++ if((scanr = maildir_doscandir(name, &names, CCLIENT)) < 0) ++ return -1; ++ ++ if(nfiles) *nfiles = scanr; ++ for(i = 0, j = 1, k = 0; j < scanr; i++, j++){ ++ if(k) ++ names[i] = names[i+k]; ++ if(same_maildir_file(names[i]->d_name, names[j]->d_name)){ ++ int d, f, r, s; ++ maildir_getflag(names[i]->d_name, &d, &f, &r, &s, NIL); ++ snprintf(old, sizeof(old), "%s/%s", name, names[i]->d_name); ++ snprintf(new, sizeof(new), "%s/.%s", name, names[i]->d_name); ++ if(rename(old, new) < 0 && errno != EEXIST) ++ error++; ++ if(!error){ ++ for(; j < scanr ++ && same_maildir_file(names[i]->d_name, names[j]->d_name) ++ ; j++, k++){ ++ maildir_getflag(names[j]->d_name, (d ? NIL : &d), ++ (f ? NIL : &f), (r ? NIL : &r), (s ? NIL : &s), NIL); ++ snprintf(tmp, sizeof(tmp), "%s/%s", name, names[j]->d_name); ++ if(unlink(tmp) < 0){ /* Hmmm... a problem, let's see */ ++ struct stat sbuf; ++ if (stat(tmp, &sbuf) == 0 && (sbuf.st_mode & S_IFMT) == S_IFREG) ++ error++; ++ } ++ } ++ if((str = strrchr(names[i]->d_name,FLAGSEP)) != NULL) *str = '\0'; ++ snprintf (old, sizeof(old), "%s/%s%s%s%s%s%s", name, names[i]->d_name, MDSEP(2), ++ MDFLAG(Draft, d), MDFLAG(Flagged, f), MDFLAG(Replied, r), ++ MDFLAG(Seen, s)); ++ if(rename(new, old) < 0) ++ error++; ++ } ++ } ++ ++ } ++ if(k > 0) ++ fs_give((void **)&names); ++ else ++ *flist = names; ++ return error ? -1 : k; ++} ++ ++int ++maildir_doscandir(char *name, struct direct ***flist, int flag) ++{ ++return scandir(name, flist, ++ flag == CCLIENT ? maildir_select : courier_dir_select, ++ flag == CCLIENT ? maildir_namesort : courier_dir_sort); ++} ++ ++/* ++ * return all files in a given directory. This is a separate call ++ * so that if there are warnings during compilation this only appears once. ++ */ ++unsigned long ++maildir_scandir (char *name, struct direct ***flist, ++ unsigned long *nfiles, int *scand, int flag) ++{ ++ struct stat sbuf; ++ int rv = -2; /* impossible value */ ++ ++ if (scand) ++ *scand = -1; /* assume error for safety */ ++ *nfiles = 0; ++ if((stat(name,&sbuf) < 0) ++ || (flag == CCLIENT ++ && ((rv = maildir_eliminate_duplicate(name, flist, nfiles)) < 0))) ++ return 0L; ++ ++ if (scand && (rv > 0 || rv == -2)) ++ *nfiles = maildir_doscandir(name, flist, flag); ++ ++ if(scand) *scand = *nfiles; ++ ++ return (unsigned long) sbuf.st_ctime; ++} ++ ++/* Does a message with given name exists (or was it removed)? ++ * Returns: 1 - yes, such message exist, ++ * 0 - No, that message does not exist anymore ++ * ++ * Parameters: stream, name of mailbox, new name if his message does not ++ * exist. ++ */ ++ ++int maildir_message_exists(MAILSTREAM *stream, char *name, char *newfile) ++{ ++ char tmp[MAILTMPLEN]; ++ int gotit = NIL; ++ DIR *dir; ++ struct direct *d; ++ struct stat sbuf; ++ ++ /* First check directly if it exists, if not there, look for it */ ++ snprintf(tmp, sizeof(tmp), "%s/%s", LOCAL->path[Cur], name); ++ if ((stat(tmp, &sbuf) == 0) && ((sbuf.st_mode & S_IFMT) == S_IFREG)) ++ return T; ++ ++ if (!(dir = opendir (LOCAL->path[Cur]))) ++ return NIL; ++ ++ while ((d = readdir(dir)) && gotit == NIL){ ++ if (d->d_name[0] == '.') ++ continue; ++ if (same_maildir_file(d->d_name, name)){ ++ gotit = T; ++ strcpy(newfile, d->d_name); ++ } ++ } ++ closedir(dir); ++ return gotit; ++} ++ ++/* Maildir open */ ++ ++MAILSTREAM *maildir_open (MAILSTREAM *stream) ++{ ++ char tmp[MAILTMPLEN]; ++ struct stat sbuf; ++ ++ if (!stream) return &maildirproto; ++ if (stream->local) fatal ("maildir recycle stream"); ++ md_domain_name(); /* get domain name for maildir files in mdlocaldomain */ ++ if(mypid == (pid_t) 0) ++ mypid = getpid(); ++ if (!stream->rdonly){ ++ stream->perm_seen = stream->perm_deleted = stream->perm_flagged = ++ stream->perm_answered = stream->perm_draft = T; ++ } ++ stream->local = (MAILDIRLOCAL *) fs_get (sizeof (MAILDIRLOCAL)); ++ memset(LOCAL, 0, sizeof(MAILDIRLOCAL)); ++ LOCAL->fd = -1; ++ ++ LOCAL->courier = IS_COURIER(stream->mailbox); ++ strcpy(tmp, stream->mailbox); ++ if (maildir_file_path (stream->mailbox, tmp, sizeof(tmp))) ++ LOCAL->dir = cpystr (tmp); ++ LOCAL->candouid = maildir_can_assign_uid(stream); ++ maildir_read_uid(stream, &stream->uid_last, &stream->uid_validity); ++ if (LOCAL->dir){ ++ LOCAL->path = (char **) fs_get(EndDir*sizeof(char *)); ++ MDFLD(tmp, LOCAL->dir, Cur); LOCAL->path[Cur] = cpystr (tmp); ++ MDFLD(tmp, LOCAL->dir, New); LOCAL->path[New] = cpystr (tmp); ++ MDFLD(tmp, LOCAL->dir, Tmp); LOCAL->path[Tmp] = cpystr (tmp); ++ if (stat (LOCAL->path[Cur],&sbuf) < 0) { ++ snprintf (tmp, sizeof(tmp), "Can't open folder %s: %s", ++ stream->mailbox,strerror (errno)); ++ mm_log (tmp,ERROR); ++ maildir_close(stream, 0); + return NIL; + } -+ } -+ -+ fnlen = strlen(tmp); -+ if ((s = strrchr(mailbox,MDSEPARATOR(courier))) != NULL){ -+ c = *++s; -+ *s = '\0'; -+ if ((stat(tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) && -+ !maildir_create_work (mailbox, ++loop)) -+ return NIL; -+ *s = c; -+ } -+ tmp[fnlen] = '\0'; -+ -+ if (mkdir(tmp,0700) && errno != EEXIST) -+ return NIL; -+ -+ if (create_dir) -+ mailbox[fnlen] = '/'; -+ -+ if (create_dir){ -+ if(style == CCLIENT){ -+ if(!courier){ -+ FILE *fp = NULL; -+ snprintf(tmp2, sizeof(tmp2), "%s%s", tmp, MDDIR); -+ if ((fp = fopen(tmp2,"w")) == NULL){ -+ snprintf (err, sizeof(err), "Problem creating %s: %s", tmp2, strerror(errno)); -+ mm_log (err,ERROR); -+ return NIL; -+ } -+ fclose(fp); -+ } -+ } -+ return T; -+ } -+ else -+ return maildir_create_folder(tmp); -+ } -+ -+ long maildir_create (MAILSTREAM *stream,char *mailbox) -+ { -+ char tmp[MAILTMPLEN], err[MAILTMPLEN]; -+ int rv, create_dir; -+ -+ create_dir = mailbox ? -+ (mailbox[strlen(mailbox) - 1] == -+ MDSEPARATOR(IS_COURIER(mailbox))) : 0; -+ maildir_file_path(mailbox, tmp, sizeof(tmp)); -+ strcpy(tmp, mailbox); -+ rv = maildir_create_work(mailbox, 0); -+ strcpy(mailbox, tmp); -+ if (rv == 0){ -+ snprintf (err, sizeof(err), "Can't create %s %s", -+ (create_dir ? "directory" : "mailbox"), mailbox); -+ mm_log (err,ERROR); -+ } -+ return rv ? LONGT : NIL; -+ } -+ -+ #define MAXTRY 10000 -+ void maildir_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt) -+ { -+ char oldfile[MAILTMPLEN],newfile[MAILTMPLEN],fn[MAILTMPLEN]; -+ char *s; -+ int ren, try = 0; -+ -+ if (elt->valid){ -+ for (try = 1; try > 0 && try < MAXTRY; try++){ -+ /* build the new filename */ -+ snprintf (oldfile, sizeof(oldfile), "%s/%s",LOCAL->path[Cur], MDFILE(elt)); -+ fn[0] = '\0'; -+ if ((ren = maildir_message_exists(stream, MDFILE(elt), fn)) == 0){ -+ errno = ENOENT; -+ try = MAXTRY; -+ } -+ if (*fn) /* new oldfile! */ -+ snprintf (oldfile,sizeof(oldfile),"%s/%s", LOCAL->path[Cur], fn); -+ if ((s = strrchr (MDFILE(elt), FLAGSEP))) *s = '\0'; -+ snprintf (fn, sizeof(fn), "%s%s%s%s%s%s%s", MDFILE(elt), MDSEP(2), -+ MDFLAG(Draft, elt->draft), MDFLAG(Flagged, elt->flagged), -+ MDFLAG(Replied, elt->answered), MDFLAG(Seen, elt->seen), -+ MDFLAG(Trashed, elt->deleted)); -+ snprintf (newfile, sizeof(newfile), "%s/%s",LOCAL->path[Cur],fn); -+ if (ren != 0 && rename (oldfile,newfile) >= 0) -+ try = -1; -+ } -+ -+ if (try > 0){ -+ snprintf(oldfile, sizeof(oldfile), "Unable to write flags to disk: %s", -+ (errno == ENOENT) ? "message is gone!" : strerror (errno)); -+ mm_log(oldfile,ERROR); -+ return; -+ } -+ #ifdef __CYGWIN__ -+ utime(LOCAL->path[Cur], NIL); /* make sure next scan will catch the change */ -+ #endif -+ maildir_free_file_only ((void **) &elt->private.spare.ptr); -+ MDFILE(elt) = cpystr (fn); -+ } -+ } -+ -+ long maildir_expunge (MAILSTREAM *stream, char *sequence, long options) -+ { -+ long ret; -+ MESSAGECACHE *elt; -+ unsigned long i, n = 0L; -+ unsigned long recent = stream->recent; ++ } ++ ++ if(maildir_file_path (stream->mailbox, tmp, sizeof(tmp))){ ++ fs_give ((void **) &stream->mailbox); ++ stream->mailbox = cpystr(tmp); ++ } ++ ++ LOCAL->buf = (char *) fs_get (CHUNKSIZE); ++ LOCAL->buflen = CHUNKSIZE - 1; ++ stream->sequence++; ++ stream->nmsgs = stream->recent = 0L; ++ ++ maildir_parse_folder(stream, 1); ++ ++ return stream; ++} ++ ++/* Maildir initial parsing of the folder */ ++void ++maildir_parse_folder (MAILSTREAM *stream, int full) ++{ + char tmp[MAILTMPLEN]; -+ -+ mm_critical (stream); /* go critical */ -+ ret = sequence ? ((options & EX_UID) ? -+ mail_uid_sequence (stream,sequence) : -+ mail_sequence (stream,sequence)) : LONGT; -+ if(ret == 0L) -+ return 0L; -+ for (i = 1L; i <= stream->nmsgs;){ -+ elt = mail_elt (stream,i); -+ if (elt->deleted && (sequence ? elt->sequence : T)){ -+ snprintf (tmp, sizeof(tmp), "%s/%s", LOCAL->path[Cur], MDFILE(elt)); -+ if (unlink (tmp) < 0) {/* try to delete the message */ -+ snprintf (tmp, sizeof(tmp), "Expunge of message %ld failed, aborted: %s",i, -+ strerror (errno)); -+ if (!stream->silent) -+ mm_log (tmp,WARN); -+ break; -+ } -+ if (elt->private.spare.ptr) -+ maildir_free_file ((void **) &elt->private.spare.ptr); -+ if (elt->recent) --recent;/* if recent, note one less recent message */ -+ mail_expunged (stream,i); /* notify upper levels */ -+ n++; /* count up one more expunged message */ -+ } -+ else i++; ++ struct direct **namescur = NIL, **namesnew = NIL; ++ unsigned long i, nfilescur = 0L, nfilesnew = 0L, oldpos, newpos, total; ++ int scan_err, rescan, loop = 0; ++ ++ if (!stream) /* what??? */ ++ return; ++ ++ MM_CRITICAL(stream); ++ ++ maildir_scandir (LOCAL->path[New], &namesnew, &nfilesnew, &scan_err, CCLIENT); ++ if (scan_err < 0) ++ maildir_abort(stream); ++ ++ /* Scan old messages first, escoba! */ ++ if(stream->rdonly || ++ (LOCAL && ((maildir_initial_check(stream, Cur) == 0) ++ || nfilesnew > 0L))){ ++ LOCAL->scantime = maildir_scandir (LOCAL->path[Cur], &namescur, &nfilescur, ++ &scan_err, CCLIENT); ++ if (scan_err < 0){ ++ if(namesnew){ ++ for(i = 0L; i < nfilesnew; i++) ++ fs_give((void **)&namesnew[i]); ++ fs_give((void **) &namesnew); ++ } ++ maildir_abort(stream); ++ } + } -+ if(n){ /* output the news if any expunged */ -+ snprintf (tmp, sizeof(tmp), "Expunged %ld messages", n); -+ if (!stream->silent) -+ mm_log (tmp,(long) NIL); ++ if(LOCAL && (maildir_initial_check(stream, New) == 0) ++ && (nfilescur > 0L)){ ++ while(LOCAL && loop < 10){ ++ if(nfilesnew == 0L) ++ maildir_scandir (LOCAL->path[New], &namesnew, &nfilesnew, &scan_err, CCLIENT); ++ if (scan_err < 0){ ++ if(namesnew){ ++ for(i = 0L; i < nfilesnew; i++) ++ fs_give((void **)&namesnew[i]); ++ fs_give((void **) &namesnew); ++ } ++ maildir_abort(stream); ++ break; ++ } ++ for(i = 0L, rescan = 0, newpos = oldpos = 0L; ++ newpos < nfilescur && i < nfilesnew; i++){ ++ if(maildir_message_in_list(namesnew[i]->d_name, namescur, oldpos, ++ nfilescur - 1L, &newpos)){ ++ oldpos = newpos; ++ snprintf(tmp, sizeof(tmp), "%s/%s", LOCAL->path[New], namesnew[i]->d_name); ++ if(unlink(tmp) < 0) ++ scan_err = -1; ++ rescan++; ++ } ++ else ++ newpos = oldpos; ++ } ++ if(scan_err < 0) ++ maildir_abort(stream); ++ if(rescan == 0) ++ break; ++ else{ /* restart */ ++ if(namesnew){ ++ for(i = 0L; i < nfilesnew; i++) ++ fs_give((void **)&namesnew[i]); ++ fs_give((void **) &namesnew); ++ } ++ nfilesnew = 0L; ++ loop++; ++ } ++ } + } -+ else -+ if (!stream->silent) -+ mm_log ("No messages deleted, so no update needed",(long) NIL); -+ mm_nocritical (stream); /* release critical */ -+ /* notify upper level of new mailbox size */ -+ mail_exists (stream, stream->nmsgs); -+ mail_recent (stream, recent); -+ return ret; -+ } -+ -+ long maildir_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options) -+ { -+ STRING st; -+ MESSAGECACHE *elt; -+ unsigned long len; -+ int fd; -+ unsigned long i; ++ if(loop == 10) ++ maildir_abort(stream); ++ if(LOCAL){ ++ if(stream->rdonly) ++ stream->recent = 0L; ++ total = namescur || stream->rdonly ++ ? maildir_parse_dir(stream, 0L, Cur, namescur, ++ nfilescur, full) : stream->nmsgs; ++ stream->nmsgs = maildir_parse_dir(stream, total, New, namesnew, ++ nfilesnew, full); ++ } ++ if(namesnew){ ++ for(i = 0L; i < nfilesnew; i++) ++ fs_give((void **)&namesnew[i]); ++ fs_give((void **) &namesnew); ++ } ++ if(namescur){ ++ for(i = 0L; i < nfilescur; i++) ++ fs_give((void **)&namescur[i]); ++ fs_give((void **) &namescur); ++ } ++ MM_NOCRITICAL(stream); ++} ++ ++int ++maildir_initial_check (MAILSTREAM *stream, DirNamesType dirtype) ++{ ++ char *tmp; + struct stat sbuf; -+ char tmp[MAILTMPLEN], flags[MAILTMPLEN], path[MAILTMPLEN], *s; -+ /* copy the messages */ -+ if ((options & CP_UID) ? mail_uid_sequence (stream, sequence) : -+ mail_sequence (stream,sequence)) -+ for (i = 1L; i <= stream->nmsgs; i++) -+ if ((elt = mail_elt (stream,i))->sequence){ -+ MSGPATH(path, LOCAL->dir, MDFILE(elt), MDLOC(elt)); -+ if (((fd = open (path,O_RDONLY,NIL)) < 0) -+ ||((!elt->rfc822_size && -+ ((stat(path, &sbuf) < 0) || !S_ISREG (sbuf.st_mode))))) -+ return NIL; -+ if(!elt->rfc822_size) -+ MDSIZE(elt) = sbuf.st_size; -+ s = (char *) fs_get(MDSIZE(elt) + 1); -+ read (fd,s,MDSIZE(elt)); -+ s[MDSIZE(elt)] = '\0'; -+ close (fd); -+ len = strcrlfcpy (&LOCAL->buf,&LOCAL->buflen, s, MDSIZE(elt)); -+ INIT (&st,mail_string, LOCAL->buf, len); -+ elt->rfc822_size = len; -+ fs_give ((void **)&s); -+ -+ flags[0] = flags[1] = '\0'; -+ if (elt->seen) strcat (flags," \\Seen"); -+ if (elt->draft) strcat (flags," \\Draft"); -+ if (elt->deleted) strcat (flags," \\Deleted"); -+ if (elt->flagged) strcat (flags," \\Flagged"); -+ if (elt->answered) strcat (flags," \\Answered"); -+ flags[0] = '('; /* open list */ -+ strcat (flags,")"); /* close list */ -+ mail_date (tmp,elt); /* generate internal date */ -+ if (!mail_append_full (NIL, mailbox, flags, tmp, &st)) -+ return NIL; -+ if (options & CP_MOVE) elt->deleted = T; ++ ++ if (access (LOCAL->path[dirtype], R_OK|W_OK|X_OK) != 0){ ++ maildir_abort(stream); ++ return -1; ++ } ++ ++ if (dirtype != New && ++ (stat(LOCAL->path[Cur], &sbuf) < 0 || sbuf.st_ctime == LOCAL->scantime)) ++ return -1; ++ return 0; ++} ++ ++ ++/* Return the number of messages in the directory, while filling the ++ * elt structure. ++ */ ++ ++unsigned long ++maildir_parse_dir(MAILSTREAM *stream, unsigned long nmsgs, ++ DirNamesType dirtype, struct direct **names, ++ unsigned long nfiles, int full) ++{ ++ char tmp[MAILTMPLEN], file[MAILTMPLEN], newfile[MAILTMPLEN], *mdstr; ++ struct stat sbuf; ++ unsigned long i, new = 0L, l, uid_last; ++ unsigned long recent = stream ? stream->recent : 0L; ++ int d = 0, f = 0, r = 0, s = 0, t = 0; ++ int we_compute, in_list; ++ int silent = stream ? stream->silent : NIL; ++ MESSAGECACHE *elt; ++ ++ if (dirtype == Cur && !stream->rdonly) ++ for (i = 1L; i <= stream->nmsgs;){ ++ elt = mail_elt(stream, i); ++ in_list = elt && elt->private.spare.ptr && nfiles > 0L ++ ? (MDPOS(elt) < nfiles ++ ? same_maildir_file(MDFILE(elt), names[MDPOS(elt)]->d_name) ++ : NIL) ++ || maildir_message_in_list(MDFILE(elt), names, 0L, ++ nfiles - 1L, &MDPOS(elt)) ++ : NIL; ++ if (!in_list){ ++ if (elt->private.spare.ptr) ++ maildir_free_file ((void **) &elt->private.spare.ptr); ++ ++ if (elt->recent) --recent; ++ mail_expunged(stream,i); ++ } ++ else i++; ++ } ++ ++ stream->silent = T; ++ uid_last = 0L; ++ for (we_compute = 0, i = l = 1L; l <= nfiles; l++){ ++ unsigned long pos, uid; ++ if (dirtype == New && !stream->rdonly){ /* move new messages to cur */ ++ pos = l - 1L; ++ snprintf (file, sizeof(file), "%s/%s", LOCAL->path[New], names[pos]->d_name); ++ if(lstat(file,&sbuf) == 0) ++ switch(sbuf.st_mode & S_IFMT){ ++ case S_IFREG: ++ strcpy(tmp, names[pos]->d_name); ++ if((mdstr = strstr(tmp,MDSEP(3))) ++ || (mdstr = strstr(tmp,MDSEP(2)))) ++ *(mdstr+1) = '2'; ++ else ++ strcat(tmp, MDSEP(2)); ++ snprintf(newfile, sizeof(newfile), "%s/%s", LOCAL->path[Cur], tmp); ++ if(rename (file, newfile) != 0){ ++ mm_log("Unable to read new mail!", WARN); ++ continue; ++ } ++ unlink (file); ++ new++; ++ break; ++ case S_IFLNK: /* clean up, clean up, everybody, everywhere */ ++ if(unlink(file) < 0){ ++ if(LOCAL->link == NIL){ ++ mm_log("Unable to remove symbolic link", WARN); ++ LOCAL->link = T; ++ } ++ } ++ continue; ++ break; ++ default: ++ if(LOCAL && LOCAL->link == NIL){ ++ mm_log("Unrecognized file or link in folder", WARN); ++ LOCAL->link = T; ++ } ++ continue; ++ break; ++ } ++ } ++ mail_exists(stream, i + nmsgs); ++ elt = mail_elt(stream, i + nmsgs); ++ pos = (elt && elt->private.spare.ptr) ? MDPOS(elt) : l - 1L; ++ if (dirtype == New) elt->recent = T; ++ maildir_getflag(names[pos]->d_name, &d, &f, &r ,&s, &t); ++ if (elt->private.spare.ptr) ++ maildir_free_file_only ((void **)&elt->private.spare.ptr); ++ else{ ++ maildir_get_file((MAILDIRFILE **)&elt->private.spare.ptr); ++ we_compute++; ++ } ++ MDFILE(elt) = cpystr(names[pos]->d_name); ++ MDPOS(elt) = pos; ++ MDLOC(elt) = dirtype; ++ if (dirtype == Cur){ /* deal with UIDs */ ++ if(elt->private.uid == 0L) ++ elt->private.uid = maildir_get_uid(MDFILE(elt)); ++ if(elt->private.uid <= uid_last){ ++ uid = (we_compute ? uid_last : stream->uid_last) + 1L; ++ if(LOCAL->candouid) ++ maildir_assign_uid(stream, i + nmsgs, uid); ++ else ++ elt->private.uid = uid; ++ } ++ else ++ uid = elt->private.uid; ++ uid_last = uid; ++ if(uid_last > stream->uid_last) ++ stream->uid_last = uid_last; ++ } ++ if(dirtype == New && !stream->rdonly){ ++ maildir_free_file_only((void **)&elt->private.spare.ptr); ++ MDFILE(elt) = cpystr(tmp); ++ MDSIZE(elt) = sbuf.st_size; ++ MDMTIME(elt) = sbuf.st_mtime; ++ MDLOC(elt) = Cur; ++ } ++ if (elt->draft != d || elt->flagged != f || ++ elt->answered != r || elt->seen != s || elt->deleted != t){ ++ elt->draft = d; elt->flagged = f; elt->answered = r; ++ elt->seen = s; elt->deleted = t; ++ if (!we_compute && !stream->rdonly) ++ MM_FLAGS(stream, i+nmsgs); ++ } ++ maildir_get_date(stream, i+nmsgs); ++ elt->valid = T; ++ i++; ++ } ++ stream->silent = silent; ++ if(LOCAL->candouid && dirtype == Cur) ++ maildir_read_uid(stream, NULL, &stream->uid_validity); ++ if (dirtype == New && stream->rdonly) ++ new = nfiles; ++ mail_exists(stream, nmsgs + ((dirtype == New) ? new : nfiles)); ++ mail_recent(stream, recent + ((dirtype == New) ? new : 0L)); ++ ++ return (nmsgs + (dirtype == New ? new : nfiles)); ++} ++ ++long maildir_ping (MAILSTREAM *stream) ++{ ++ maildir_parse_folder(stream, 0); ++ if(stream && LOCAL){ ++ if(LOCAL->candouid < 0) ++ LOCAL->candouid++; ++ else if(LOCAL->candouid) ++ maildir_uid_renew_tempfile(stream); ++ else /* try again to get uids */ ++ LOCAL->candouid = maildir_can_assign_uid(stream); ++ } ++ return stream && LOCAL ? LONGT : NIL; ++} ++ ++int maildir_select (const struct direct *name) ++{ ++ return (name->d_name[0] != '.'); ++} ++ ++/* ++ * Unfortunately, there is no way to sort by arrival in this driver, this ++ * means that opening a folder in this driver using the scandir function ++ * will always make this driver slower than any driver that has a natural ++ * way of sorting by arrival (like a flat file format, "mbox", "mbx", etc). ++ */ ++int maildir_namesort (const struct direct **d1, const struct direct **d2) ++{ ++ const struct direct *e1 = *(const struct direct **) d1; ++ const struct direct *e2 = *(const struct direct **) d2; ++ ++ return comp_maildir_file((char *) e1->d_name, (char *) e2->d_name); ++} ++ ++/* Maildir close */ ++ ++void maildir_close (MAILSTREAM *stream, long options) ++{ ++ MESSAGECACHE *elt; ++ unsigned long i; ++ int silent = stream ? stream->silent : 0; ++ mailcache_t mc = (mailcache_t) mail_parameters (NIL,GET_CACHE,NIL); ++ ++ if (!stream) return; ++ ++ for (i = 1L; i <= stream->nmsgs; i++) ++ if((elt = (MESSAGECACHE *) (*mc)(stream,i,CH_ELT)) && elt->private.spare.ptr) ++ maildir_free_file ((void **) &elt->private.spare.ptr); ++ stream->silent = T; ++ if (options & CL_EXPUNGE) maildir_expunge (stream, NIL, NIL); ++ maildir_abort(stream); ++ if (mdfpath) fs_give((void **)&mdfpath); ++ if (mypid) mypid = (pid_t) 0; ++ stream->silent = silent; ++} ++ ++void maildir_check (MAILSTREAM *stream) ++{ ++ if (maildir_ping (stream)) mm_log ("Check completed",(long) NIL); ++} ++ ++long maildir_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs, long flags) ++{ ++ char tmp[MAILTMPLEN]; ++ unsigned long i; ++ MESSAGECACHE *elt; ++ char *s; ++ /* UID call "impossible" */ ++ if (flags & FT_UID || !LOCAL) return NIL; ++ elt = mail_elt (stream, msgno); ++ ++ if (!(flags & FT_PEEK) && !elt->seen){ ++ elt->seen = T; ++ maildir_flagmsg (stream, elt); ++ MM_FLAGS(stream, elt->msgno); ++ } ++ ++ MSGPATH(tmp, LOCAL->dir, MDFILE(elt), MDLOC(elt)); ++ if (LOCAL->fd < 0) /* if file closed ? */ ++ LOCAL->fd = open(tmp,O_RDONLY,NIL); ++ ++ if (LOCAL->fd < 0 && (errno == EACCES || errno == ENOENT)){ ++ INIT (bs, mail_string, "", 0); ++ elt->rfc822_size = 0L; ++ return NIL; ++ } ++ ++ s = maildir_text_work(stream, elt, &i, flags); ++ INIT (bs, mail_string, s, i); ++ return LONGT; ++} ++ ++char *maildir_text_work (MAILSTREAM *stream,MESSAGECACHE *elt, ++ unsigned long *length,long flags) ++{ ++ FDDATA d; ++ STRING bs; ++ char *s,tmp[CHUNK]; ++ unsigned long msgno = elt->msgno; ++ static int try = 0; ++ ++ if (length) ++ *length = 0L; ++ LOCAL->buf[0] = '\0'; ++ ++ MSGPATH(tmp, LOCAL->dir, MDFILE(elt), MDLOC(elt)); ++ if (LOCAL->fd < 0) /* if file closed ? */ ++ LOCAL->fd = open(tmp,O_RDONLY,NIL); ++ ++ if (LOCAL->fd < 0){ /* flag change? */ ++ if (try < 5){ ++ try++; ++ if (maildir_update_elt_maildirp(stream, msgno) > 0) ++ try = 0; ++ return maildir_text_work(stream, mail_elt(stream, msgno),length, flags); ++ } ++ try = 0; ++ return NULL; ++ } ++ ++ lseek (LOCAL->fd, elt->private.msg.text.offset,L_SET); ++ ++ if (flags & FT_INTERNAL) { /* initial data OK? */ ++ if (elt->private.msg.text.text.size > LOCAL->buflen) { ++ fs_give ((void **) &LOCAL->buf); ++ LOCAL->buf = (char *) fs_get ((LOCAL->buflen = ++ elt->private.msg.text.text.size) + 1); ++ } ++ read (LOCAL->fd,LOCAL->buf,elt->private.msg.text.text.size); ++ LOCAL->buf[*length = elt->private.msg.text.text.size] = '\0'; ++ } ++ else { ++ if (elt->rfc822_size > LOCAL->buflen) { ++ fs_give ((void **) &LOCAL->buf); ++ LOCAL->buf = (char *) fs_get ((LOCAL->buflen = elt->rfc822_size) + 1); ++ } ++ d.fd = LOCAL->fd; /* yes, set up file descriptor */ ++ d.pos = elt->private.msg.text.offset; ++ d.chunk = tmp; /* initial buffer chunk */ ++ d.chunksize = CHUNK; ++ INIT (&bs,fd_string,&d,elt->private.msg.text.text.size); ++ for (s = LOCAL->buf; SIZE (&bs);) switch (CHR (&bs)) { ++ case '\r': /* carriage return seen */ ++ *s++ = SNX (&bs); /* copy it and any succeeding LF */ ++ if (SIZE (&bs) && (CHR (&bs) == '\n')) *s++ = SNX (&bs); ++ break; ++ case '\n': ++ *s++ = '\r'; /* insert a CR */ ++ default: ++ *s++ = SNX (&bs); /* copy characters */ ++ } ++ *s = '\0'; /* tie off buffer */ ++ *length = s - (char *) LOCAL->buf; /* calculate length */ ++ } ++ close(LOCAL->fd); LOCAL->fd = -1; ++ return LOCAL->buf; ++} ++ ++/* maildir parse, fill the elt structure... well not all of it... */ ++unsigned long maildir_parse_message(MAILSTREAM *stream, unsigned long msgno, ++ DirNamesType dirtype) ++{ ++ char *b, *s, *t, c; ++ char tmp[MAILTMPLEN]; ++ struct stat sbuf; ++ unsigned long i, len; ++ int d, f, r, se, dt; ++ MESSAGECACHE *elt; ++ ++ elt = mail_elt (stream,msgno); ++ MSGPATH(tmp, LOCAL->dir, MDFILE(elt), dirtype); ++ if(stat(tmp, &sbuf) == 0) ++ MDSIZE(elt) = sbuf.st_size; ++ ++ maildir_get_date(stream, msgno); ++ maildir_getflag(MDFILE(elt), &d, &f, &r ,&se, &dt); ++ elt->draft = d; elt->flagged = f; elt->answered = r; elt->seen = se; ++ elt->deleted = dt; elt->valid = T; ++ if (LOCAL->fd < 0) /* if file closed ? */ ++ LOCAL->fd = open(tmp,O_RDONLY,NIL); ++ ++ if (LOCAL->fd >= 0){ ++ s = (char *) fs_get (MDSIZE(elt) + 1); ++ read (LOCAL->fd,s,MDSIZE(elt)); ++ s[MDSIZE(elt)] = '\0'; ++ t = s + strlen(s); /* make t point to the end of s */ ++ for (i = 0L, b = s; b < t && !(i && (*b == '\n')); i = (*b++ == '\n')); ++ len = (*b ? ++b : b) - s; ++ elt->private.msg.header.text.size = ++ elt->private.msg.text.offset = len; ++ elt->private.msg.text.text.size = MDSIZE(elt) - len; ++ for (i = 0L, b = s, c = *b; b && ++ ((c < '\016' && ((c == '\012' && ++i) ++ ||(c == '\015' && *(b+1) == '\012' && ++b && (i +=2)))) ++ || b < t); i++, c= *++b); ++ elt->rfc822_size = i; ++ fs_give ((void **) &s); ++ close(LOCAL->fd); LOCAL->fd = -1; ++ } ++ return elt->rfc822_size; ++} ++ ++int ++maildir_update_elt_maildirp(MAILSTREAM *stream, unsigned long msgno) ++{ ++ struct direct **names = NIL; ++ unsigned long i, nfiles, pos; ++ int d = 0, f = 0 , r = 0, s = 0, t = 0, in_list, scan_err; ++ MESSAGECACHE *elt; ++ ++ maildir_scandir (LOCAL->path[Cur], &names, &nfiles, &scan_err, CCLIENT); ++ ++ elt = mail_elt (stream,msgno); ++ ++ in_list = nfiles > 0L ++ ? maildir_message_in_list(MDFILE(elt), names, 0L, nfiles - 1L, &pos) ++ : NIL; ++ ++ if (in_list && pos >= 0L && pos < nfiles ++ && !strcmp(MDFILE(elt), names[pos]->d_name)){ ++ in_list = NIL; ++ maildir_abort(stream); + } -+ return LONGT; /* return success */ ++ ++ if (in_list && pos >= 0L && pos < nfiles){ ++ maildir_free_file_only((void **)&elt->private.spare.ptr); ++ MDFILE(elt) = cpystr(names[pos]->d_name); ++ maildir_getflag(MDFILE(elt), &d, &f, &r ,&s, &t); ++ if (elt->draft != d || elt->flagged != f || ++ elt->answered != r || elt->seen != s || elt->deleted != t){ ++ elt->draft = d; elt->flagged = f; elt->answered = r; ++ elt->seen = s; elt->deleted = t; ++ MM_FLAGS(stream, msgno); ++ } ++ } ++ for (i = 0L; i < nfiles; i++) ++ fs_give((void **) &names[i]); ++ if (names) ++ fs_give((void **) &names); ++ return in_list ? 1 : -1; ++} ++ ++/* Maildir fetch message header */ ++ ++char *maildir_header (MAILSTREAM *stream,unsigned long msgno, ++ unsigned long *length, long flags) ++{ ++ char tmp[MAILTMPLEN], *s; ++ MESSAGECACHE *elt; ++ static int try = 0; ++ ++ if (length) *length = 0; ++ if (flags & FT_UID || !LOCAL) return ""; /* UID call "impossible" */ ++ elt = mail_elt (stream,msgno); ++ if(elt->private.msg.header.text.size == 0) ++ maildir_parse_message(stream, msgno, MDLOC(elt)); ++ ++ MSGPATH(tmp, LOCAL->dir, MDFILE(elt), MDLOC(elt)); ++ if (LOCAL->fd < 0) ++ LOCAL->fd = open (tmp,O_RDONLY,NIL); ++ ++ if (LOCAL->fd < 0 && errno == EACCES){ ++ mm_log ("Message exists but can not be read. Envelope and body lost!",ERROR); ++ return NULL; ++ } ++ ++ if (LOCAL->fd < 0){ /* flag change? */ ++ if (try < 5){ ++ try++; ++ if (maildir_update_elt_maildirp(stream, msgno) > 0) ++ try = 0; ++ return maildir_header(stream, msgno, length, flags); ++ } ++ try = 0; ++ return NULL; ++ } ++ ++ if (flags & FT_INTERNAL){ ++ if(elt->private.msg.header.text.size > LOCAL->buflen){ ++ fs_give ((void **) &LOCAL->buf); ++ LOCAL->buf = (char *) fs_get ((LOCAL->buflen = ++ elt->private.msg.header.text.size) + 1); ++ } ++ read (LOCAL->fd, (void *)LOCAL->buf, elt->private.msg.header.text.size); ++ LOCAL->buf[*length = elt->private.msg.header.text.size] = '\0'; ++ } ++ else{ ++ s = (char *) fs_get(elt->private.msg.header.text.size+1); ++ read (LOCAL->fd, (void *)s, elt->private.msg.header.text.size); ++ s[elt->private.msg.header.text.size] = '\0'; ++ *length = strcrlfcpy (&LOCAL->buf,&LOCAL->buflen,s, ++ elt->private.msg.header.text.size); ++ fs_give ((void **) &s); ++ } ++ elt->private.msg.text.offset = elt->private.msg.header.text.size; ++ elt->private.msg.text.text.size = MDSIZE(elt) - elt->private.msg.text.offset; ++ close(LOCAL->fd); LOCAL->fd = -1; ++ return LOCAL->buf; ++} ++ ++/* Maildir find list of subscribed mailboxes ++ * Accepts: mail stream ++ * pattern to search ++ */ ++ ++void maildir_list (MAILSTREAM *stream,char *ref, char *pat) ++{ ++ char *s,test[MAILTMPLEN],file[MAILTMPLEN]; ++ long i = 0L; ++ ++ if((!pat || !*pat) && maildir_canonicalize (test,ref,"*") ++ && maildir_valid_name(test)){ /* there is a #md/ leading here */ ++ for (i = 3L; test[i] && test[i] != '/'; i++); ++ if ((s = strchr (test+i+1,'/')) != NULL) *++s = '\0'; ++ else test[0] = '\0'; ++ mm_list (stream,'/',test, LATT_NOSELECT); ++ } ++ else if (maildir_canonicalize (test,ref,pat)) { ++ if (test[3] == '/') { /* looking down levels? */ ++ /* yes, found any wildcards? */ ++ if ((s = strpbrk (test,"%*")) != NULL){ ++ /* yes, copy name up to that point */ ++ strncpy (file,test+4,i = s - (test+4)); ++ file[i] = '\0'; /* tie off */ ++ } ++ else strcpy (file,test+4);/* use just that name then */ ++ /* find directory name */ ++ if ((s = strrchr (file, '/')) != NULL){ ++ *s = '\0'; /* found, tie off at that point */ ++ s = file; ++ } ++ /* do the work */ ++ if(IS_COURIER(test)) ++ courier_list_work (stream,s,test,0); ++ else ++ maildir_list_work (stream,s,test,0); ++ } ++ /* always an INBOX */ ++ if (!compare_cstring (test,"#MD/INBOX")) ++ mm_list (stream,NIL,"#MD/INBOX",LATT_NOINFERIORS); ++ if (!compare_cstring (test,"#MC/INBOX")) ++ mm_list (stream,NIL,"#MC/INBOX",LATT_NOINFERIORS); ++ } ++} ++ ++void courier_list (MAILSTREAM *stream,char *ref, char *pat) ++{ ++/* I am too lazy to do anything. Do you care to ask maildir list, please? ++ The real reason why this is a dummy function is because we do not want to ++ see the same folder listed twice. ++*/ ++} ++ ++/* For those that want to hide things, we give them a chance to do so */ ++void *maildir_parameters (long function, void *value) ++{ ++ void *ret = NIL; ++ switch ((int) function) { ++ case SET_MDINBOXPATH: ++ if(strlen((char *) value ) > 49) ++ strcpy(myMdInboxDir, "Maildir"); ++ else ++ strcpy(myMdInboxDir, (char *) value); ++ case GET_MDINBOXPATH: ++ if (myMdInboxDir[0] == '\0') strcpy(myMdInboxDir,"Maildir"); ++ ret = (void *) myMdInboxDir; ++ break; ++ case SET_COURIERSTYLE: ++ CourierStyle = (long) value; ++ case GET_COURIERSTYLE: ++ ret = (void *) CourierStyle; ++ break; ++ case GET_DIRFMTTEST: ++ ret = (void *) maildir_dirfmttest; ++ break; ++ default: ++ break; ++ } ++ return ret; ++} ++ ++int maildir_create_folder(char *mailbox) ++{ ++ char tmp[MAILTMPLEN], err[MAILTMPLEN]; ++ DirNamesType i; ++ ++ for (i = Cur; i != EndDir; i++){ ++ MDFLD(tmp, mailbox, i); ++ if (mkdir(tmp, 0700) && errno != EEXIST){ /* try to make new dir */ ++ snprintf (err, sizeof(err), "Can't create %s: %s", tmp, strerror(errno)); ++ mm_log (err,ERROR); ++ return NIL; ++ } ++ } ++ return T; ++} ++ ++int maildir_create_work(char *mailbox, int loop) ++{ ++ char *s, c, err[MAILTMPLEN], tmp[MAILTMPLEN], tmp2[MAILTMPLEN], mbx[MAILTMPLEN]; ++ int fnlen, create_dir = 0, courier, mv; ++ struct stat sbuf; ++ long style = (long) maildir_parameters(GET_COURIERSTYLE, NIL); ++ ++ courier = IS_COURIER(mailbox); ++ strcpy(mbx, mailbox); ++ mv = maildir_valid(mbx) ? 1 : 0; ++ maildir_file_path(mailbox, tmp, sizeof(tmp)); ++ if (mailbox[strlen(mailbox) - 1] == MDSEPARATOR(courier)){ ++ create_dir++; ++ mailbox[strlen(mailbox) - 1] = '\0'; ++ } ++ ++ if(!loop && courier){ ++ if(mv){ ++ if(create_dir){ ++ if(style == CCLIENT) ++ strcpy (err,"Can not create directory: folder exists. Create subfolder"); ++ else ++ strcpy(err,"Folder and Directory already exist"); ++ } ++ else ++ strcpy (err, "Can't create mailbox: mailbox already exists"); ++ } ++ else{ ++ if(create_dir) ++ strcpy(err, "Can not create directory. Cread folder instead"); ++ else ++ err[0] = '\0'; ++ } ++ if(err[0]){ ++ mm_log (err,ERROR); ++ return NIL; ++ } ++ } ++ ++ fnlen = strlen(tmp); ++ if ((s = strrchr(mailbox,MDSEPARATOR(courier))) != NULL){ ++ c = *++s; ++ *s = '\0'; ++ if ((stat(tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) && ++ !maildir_create_work (mailbox, ++loop)) ++ return NIL; ++ *s = c; ++ } ++ tmp[fnlen] = '\0'; ++ ++ if (mkdir(tmp,0700) && errno != EEXIST) ++ return NIL; ++ ++ if (create_dir) ++ mailbox[fnlen] = '/'; ++ ++ if (create_dir){ ++ if(style == CCLIENT){ ++ if(!courier){ ++ FILE *fp = NULL; ++ snprintf(tmp2, sizeof(tmp2), "%s%s", tmp, MDDIR); ++ if ((fp = fopen(tmp2,"w")) == NULL){ ++ snprintf (err, sizeof(err), "Problem creating %s: %s", tmp2, strerror(errno)); ++ mm_log (err,ERROR); ++ return NIL; ++ } ++ fclose(fp); ++ } ++ } ++ return T; ++ } ++ else ++ return maildir_create_folder(tmp); ++} ++ ++long maildir_create (MAILSTREAM *stream,char *mailbox) ++{ ++ char tmp[MAILTMPLEN], err[MAILTMPLEN]; ++ int rv, create_dir; ++ ++ create_dir = mailbox ? ++ (mailbox[strlen(mailbox) - 1] == ++ MDSEPARATOR(IS_COURIER(mailbox))) : 0; ++ maildir_file_path(mailbox, tmp, sizeof(tmp)); ++ strcpy(tmp, mailbox); ++ rv = maildir_create_work(mailbox, 0); ++ strcpy(mailbox, tmp); ++ if (rv == 0){ ++ snprintf (err, sizeof(err), "Can't create %s %s", ++ (create_dir ? "directory" : "mailbox"), mailbox); ++ mm_log (err,ERROR); ++ } ++ return rv ? LONGT : NIL; ++} ++ ++#define MAXTRY 10000 ++void maildir_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt) ++{ ++ char oldfile[MAILTMPLEN],newfile[MAILTMPLEN],fn[MAILTMPLEN]; ++ char *s; ++ int ren, try = 0; ++ ++ if (elt->valid){ ++ for (try = 1; try > 0 && try < MAXTRY; try++){ ++ /* build the new filename */ ++ snprintf (oldfile, sizeof(oldfile), "%s/%s",LOCAL->path[Cur], MDFILE(elt)); ++ fn[0] = '\0'; ++ if ((ren = maildir_message_exists(stream, MDFILE(elt), fn)) == 0){ ++ errno = ENOENT; ++ try = MAXTRY; ++ } ++ if (*fn) /* new oldfile! */ ++ snprintf (oldfile,sizeof(oldfile),"%s/%s", LOCAL->path[Cur], fn); ++ if ((s = strrchr (MDFILE(elt), FLAGSEP))) *s = '\0'; ++ snprintf (fn, sizeof(fn), "%s%s%s%s%s%s%s", MDFILE(elt), MDSEP(2), ++ MDFLAG(Draft, elt->draft), MDFLAG(Flagged, elt->flagged), ++ MDFLAG(Replied, elt->answered), MDFLAG(Seen, elt->seen), ++ MDFLAG(Trashed, elt->deleted)); ++ snprintf (newfile, sizeof(newfile), "%s/%s",LOCAL->path[Cur],fn); ++ if (ren != 0 && rename (oldfile,newfile) >= 0) ++ try = -1; ++ } ++ ++ if (try > 0){ ++ snprintf(oldfile, sizeof(oldfile), "Unable to write flags to disk: %s", ++ (errno == ENOENT) ? "message is gone!" : strerror (errno)); ++ mm_log(oldfile,ERROR); ++ return; ++ } ++#ifdef __CYGWIN__ ++ utime(LOCAL->path[Cur], NIL); /* make sure next scan will catch the change */ ++#endif ++ maildir_free_file_only ((void **) &elt->private.spare.ptr); ++ MDFILE(elt) = cpystr (fn); ++ } ++} ++ ++long maildir_expunge (MAILSTREAM *stream, char *sequence, long options) ++{ ++ long ret; ++ MESSAGECACHE *elt; ++ unsigned long i, n = 0L; ++ unsigned long recent = stream->recent; ++ char tmp[MAILTMPLEN]; ++ ++ mm_critical (stream); /* go critical */ ++ ret = sequence ? ((options & EX_UID) ? ++ mail_uid_sequence (stream,sequence) : ++ mail_sequence (stream,sequence)) : LONGT; ++ if(ret == 0L) ++ return 0L; ++ for (i = 1L; i <= stream->nmsgs;){ ++ elt = mail_elt (stream,i); ++ if (elt->deleted && (sequence ? elt->sequence : T)){ ++ snprintf (tmp, sizeof(tmp), "%s/%s", LOCAL->path[Cur], MDFILE(elt)); ++ if (unlink (tmp) < 0) {/* try to delete the message */ ++ snprintf (tmp, sizeof(tmp), "Expunge of message %ld failed, aborted: %s",i, ++ strerror (errno)); ++ if (!stream->silent) ++ mm_log (tmp,WARN); ++ break; ++ } ++ if (elt->private.spare.ptr) ++ maildir_free_file ((void **) &elt->private.spare.ptr); ++ if (elt->recent) --recent;/* if recent, note one less recent message */ ++ mail_expunged (stream,i); /* notify upper levels */ ++ n++; /* count up one more expunged message */ ++ } ++ else i++; ++ } ++ if(n){ /* output the news if any expunged */ ++ snprintf (tmp, sizeof(tmp), "Expunged %ld messages", n); ++ if (!stream->silent) ++ mm_log (tmp,(long) NIL); ++ } ++ else ++ if (!stream->silent) ++ mm_log ("No messages deleted, so no update needed",(long) NIL); ++ mm_nocritical (stream); /* release critical */ ++ /* notify upper level of new mailbox size */ ++ mail_exists (stream, stream->nmsgs); ++ mail_recent (stream, recent); ++ return ret; ++} ++ ++long maildir_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options) ++{ ++ STRING st; ++ MESSAGECACHE *elt; ++ unsigned long len; ++ int fd; ++ unsigned long i; ++ struct stat sbuf; ++ char tmp[MAILTMPLEN], flags[MAILTMPLEN], path[MAILTMPLEN], *s; ++ /* copy the messages */ ++ if ((options & CP_UID) ? mail_uid_sequence (stream, sequence) : ++ mail_sequence (stream,sequence)) ++ for (i = 1L; i <= stream->nmsgs; i++) ++ if ((elt = mail_elt (stream,i))->sequence){ ++ MSGPATH(path, LOCAL->dir, MDFILE(elt), MDLOC(elt)); ++ if (((fd = open (path,O_RDONLY,NIL)) < 0) ++ ||((!elt->rfc822_size && ++ ((stat(path, &sbuf) < 0) || !S_ISREG (sbuf.st_mode))))) ++ return NIL; ++ if(!elt->rfc822_size) ++ MDSIZE(elt) = sbuf.st_size; ++ s = (char *) fs_get(MDSIZE(elt) + 1); ++ read (fd,s,MDSIZE(elt)); ++ s[MDSIZE(elt)] = '\0'; ++ close (fd); ++ len = strcrlfcpy (&LOCAL->buf,&LOCAL->buflen, s, MDSIZE(elt)); ++ INIT (&st,mail_string, LOCAL->buf, len); ++ elt->rfc822_size = len; ++ fs_give ((void **)&s); ++ ++ flags[0] = flags[1] = '\0'; ++ if (elt->seen) strcat (flags," \\Seen"); ++ if (elt->draft) strcat (flags," \\Draft"); ++ if (elt->deleted) strcat (flags," \\Deleted"); ++ if (elt->flagged) strcat (flags," \\Flagged"); ++ if (elt->answered) strcat (flags," \\Answered"); ++ flags[0] = '('; /* open list */ ++ strcat (flags,")"); /* close list */ ++ mail_date (tmp,elt); /* generate internal date */ ++ if (!mail_append_full (NIL, mailbox, flags, tmp, &st)) ++ return NIL; ++ if (options & CP_MOVE) elt->deleted = T; ++ } ++ return LONGT; /* return success */ ++} ++ ++long maildir_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data) ++{ ++ int fd, k; ++ STRING *message; ++ char c,*s, *flags, *date; ++ char tmp[MAILTMPLEN],file[MAILTMPLEN],path1[MAILTMPLEN],path2[MAILTMPLEN]; ++ MESSAGECACHE elt; ++ long i, size = 0L, ret = LONGT, f; ++ unsigned long uf, ti; ++ static unsigned int transact = 0; ++ ++ if (!maildir_valid(mailbox)) { ++ snprintf (tmp, sizeof(tmp), "Not a valid Maildir mailbox: %s", mailbox); ++ mm_log (tmp,ERROR); ++ return NIL; ++ } ++ ++ if (!*mdlocaldomain) ++ md_domain_name(); /* get domain name for maildir files in mdlocaldomain now! */ ++ ++ if (mypid == (pid_t) 0) ++ mypid = getpid(); ++ ++ if (!stream){ ++ stream = &maildirproto; ++ ++ for (k = 0; k < NUSERFLAGS && stream->user_flags[k]; ++k) ++ fs_give ((void **) &stream->user_flags[k]); + } -+ -+ long maildir_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data) -+ { -+ int fd, k; -+ STRING *message; -+ char c,*s, *flags, *date; -+ char tmp[MAILTMPLEN],file[MAILTMPLEN],path1[MAILTMPLEN],path2[MAILTMPLEN]; -+ MESSAGECACHE elt; -+ long i, size = 0L, ret = LONGT, f; -+ unsigned long uf, ti; -+ static unsigned int transact = 0; -+ -+ if (!maildir_valid(mailbox)) { -+ snprintf (tmp, sizeof(tmp), "Not a valid Maildir mailbox: %s", mailbox); ++ ++ if (!(*af)(stream, data, &flags, &date, &message)) return NIL; ++ ++ mm_critical (stream); /* go critical */ ++ /* call time(0) only once, use transact to distinguish instead */ ++ ti = time(0); ++ do { ++ if (!SIZE (message)) { /* guard against zero-length */ ++ mm_log ("Append of zero-length message",ERROR); ++ ret = NIL; ++ break; ++ } ++ if (date && !mail_parse_date(&elt,date)){ ++ snprintf (tmp, sizeof(tmp), "Bad date in append: %.80s",date); ++ mm_log (tmp,ERROR); ++ ret = NIL; ++ break; ++ } ++ f = mail_parse_flags (stream,flags,&uf); ++ /* build file name we will use */ ++ snprintf (file, sizeof(file), "%lu.%d_%09u.%s%s%s%s%s%s", ++ ti, mypid, transact++, mdlocaldomain, (f ? MDSEP(2) : ""), ++ MDFLAG(Draft, f&fDRAFT), MDFLAG(Flagged, f&fFLAGGED), ++ MDFLAG(Replied, f&fANSWERED), MDFLAG(Seen, f&fSEEN)); ++ /* build tmp file name */ ++ if (maildir_file_path(mailbox, tmp, sizeof(tmp))) ++ MSGPATH(path1, tmp, file, Tmp); ++ ++ if ((fd = open (path1,O_WRONLY|O_CREAT|O_EXCL,S_IREAD|S_IWRITE)) < 0) { ++ snprintf (tmp, sizeof(tmp), "Can't open append mailbox: %s", strerror (errno)); ++ mm_log (tmp, ERROR); ++ return NIL; ++ } ++ for (size = 0,i = SIZE (message),s = (char *) fs_get (i + 1); i; --i) ++ if ((c = SNX (message)) != '\015') s[size++] = c; ++ if ((write (fd, s, size) < 0) || fsync (fd)) { ++ unlink (path1); /* delete message */ ++ snprintf (tmp, sizeof(tmp), "Message append failed: %s", strerror (errno)); ++ mm_log (tmp, ERROR); ++ ret = NIL; ++ } ++ fs_give ((void **) &s); /* flush the buffer */ ++ close (fd); /* close the file */ ++ /* build final filename to use */ ++ if (maildir_file_path(mailbox, tmp, sizeof(tmp))) ++ MSGPATH(path2, tmp, file, New); ++ if (rename (path1,path2) < 0) { ++ snprintf (tmp, sizeof(tmp), "Message append failed: %s", strerror (errno)); ++ mm_log (tmp, ERROR); ++ ret = NIL; ++ } ++ unlink (path1); ++ ++ if (ret) ++ if (!(*af) (stream,data,&flags,&date,&message)) ret = NIL; ++ ++ } while (ret && message); /* write the data */ ++ mm_nocritical (stream); /* release critical */ ++ return ret; ++} ++ ++long maildir_delete (MAILSTREAM *stream,char *mailbox) ++{ ++ DIR *dirp; ++ struct direct *d; ++ int i, remove_dir = 0, mddir = 0, rv, error = 0; ++ char tmp[MAILTMPLEN],tmp2[MAILTMPLEN], realname[MAILTMPLEN]; ++ struct stat sbuf; ++ int courier = IS_COURIER(mailbox); ++ ++ if (mailbox[strlen(mailbox) - 1] == MDSEPARATOR(courier)){ ++ remove_dir++; ++ mailbox[strlen(mailbox) -1] = '\0'; ++ } ++ ++ if (!maildir_valid(mailbox)){ ++ maildir_file_path(mailbox, tmp, sizeof(tmp)); ++ if (stat(tmp, &sbuf) < 0 || !S_ISDIR(sbuf.st_mode)){ ++ snprintf(tmp, sizeof(tmp), "Can not remove %s", mailbox); ++ error++; ++ } ++ } ++ ++ if (!error && remove_dir && !maildir_dir_is_empty(mailbox)){ ++ snprintf(tmp, sizeof(tmp), "Can not remove directory %s/: directory not empty", mailbox); ++ error++; ++ } ++ ++ if(error){ + mm_log (tmp,ERROR); + return NIL; -+ } -+ -+ if (!*mdlocaldomain) -+ md_domain_name(); /* get domain name for maildir files in mdlocaldomain now! */ -+ -+ if (mypid == (pid_t) 0) -+ mypid = getpid(); -+ -+ if (!stream){ -+ stream = &maildirproto; -+ -+ for (k = 0; k < NUSERFLAGS && stream->user_flags[k]; ++k) -+ fs_give ((void **) &stream->user_flags[k]); + } -+ -+ if (!(*af)(stream, data, &flags, &date, &message)) return NIL; -+ -+ mm_critical (stream); /* go critical */ -+ /* call time(0) only once, use transact to distinguish instead */ -+ ti = time(0); -+ do { -+ if (!SIZE (message)) { /* guard against zero-length */ -+ mm_log ("Append of zero-length message",ERROR); -+ ret = NIL; -+ break; ++ ++ maildir_close(stream,0); /* even if stream was NULL */ ++ ++ maildir_file_path(mailbox, realname, sizeof(realname)); ++ ++ if (remove_dir){ ++ snprintf(tmp, sizeof(tmp), "%s/%s", realname, MDDIR); ++ if ((rv = stat (tmp,&sbuf)) == 0 && S_ISREG(sbuf.st_mode)) ++ rv = unlink(tmp); ++ else if (errno == ENOENT) ++ rv = 0; ++ if (rv != 0){ ++ snprintf(tmp, sizeof(tmp), "Can not remove %s/%s: %s", tmp2, MDDIR, strerror(errno)); ++ mm_log (tmp,ERROR); ++ return NIL; + } -+ if (date && !mail_parse_date(&elt,date)){ -+ snprintf (tmp, sizeof(tmp), "Bad date in append: %.80s",date); -+ mm_log (tmp,ERROR); -+ ret = NIL; -+ break; ++ if (!maildir_valid(realname) && rmdir(realname) != 0){ ++ snprintf(tmp, sizeof(tmp), "Can not remove %s/: %s", mailbox, strerror(errno)); ++ mm_log (tmp, ERROR); ++ return NIL; + } -+ f = mail_parse_flags (stream,flags,&uf); -+ /* build file name we will use */ -+ snprintf (file, sizeof(file), "%lu.%d_%09u.%s%s%s%s%s%s", -+ ti, mypid, transact++, mdlocaldomain, (f ? MDSEP(2) : ""), -+ MDFLAG(Draft, f&fDRAFT), MDFLAG(Flagged, f&fFLAGGED), -+ MDFLAG(Replied, f&fANSWERED), MDFLAG(Seen, f&fSEEN)); -+ /* build tmp file name */ -+ if (maildir_file_path(mailbox, tmp, sizeof(tmp))) -+ MSGPATH(path1, tmp, file, Tmp); -+ -+ if ((fd = open (path1,O_WRONLY|O_CREAT|O_EXCL,S_IREAD|S_IWRITE)) < 0) { -+ snprintf (tmp, sizeof(tmp), "Can't open append mailbox: %s", strerror (errno)); ++ return LONGT; ++ } ++ /* else remove just the folder. Remove all hidden files, except MDDIR */ ++ for (i = Cur; i != EndDir; i++){ ++ MDFLD(tmp, realname, i); ++ ++ if (!(dirp = opendir (tmp))){ ++ snprintf(tmp, sizeof(tmp), "Can not read %s/: %s", mailbox, strerror(errno)); ++ mm_log (tmp, ERROR); ++ return NIL; ++ } ++ ++ while ((d = readdir(dirp)) != NULL){ ++ if (strcmp(d->d_name, ".") && strcmp(d->d_name,"..")){ ++ snprintf(tmp2, sizeof(tmp2), "%s/%s", tmp, d->d_name); ++ if (unlink(tmp2) != 0){ ++ snprintf(tmp2, sizeof(tmp2), "Can not remove %s: %s", mailbox, strerror(errno)); ++ mm_log (tmp2, ERROR); ++ return NIL; ++ } ++ } ++ } ++ closedir(dirp); ++ if (rmdir(tmp) != 0){ ++ snprintf(tmp, sizeof(tmp), "Can not remove %s: %s", mailbox, strerror(errno)); ++ mm_log (tmp, ERROR); ++ return NIL; ++ } ++ } ++ /* ++ * ok we have removed all subdirectories of the folder mailbox, Remove the ++ * hidden files. ++ */ ++ ++ if(!(dirp = opendir (realname))){ ++ snprintf(tmp, sizeof(tmp), "Can not read %s/: %s", realname, strerror(errno)); ++ mm_log (tmp, ERROR); ++ return NIL; ++ } ++ ++ while ((d = readdir(dirp)) != NULL){ ++ if (strcmp(d->d_name, ".") && strcmp(d->d_name,"..") ++ && (!strcmp(d->d_name, MDDIR) ++ || !strncmp(d->d_name, MDUIDLAST, strlen(MDUIDLAST)) ++ || !strncmp(d->d_name, MDUIDTEMP, strlen(MDUIDTEMP)))){ ++ if(strcmp(d->d_name, MDDIR) == 0) ++ mddir++; ++ snprintf(tmp, sizeof(tmp), "%s/%s", realname, d->d_name); ++ if (unlink(tmp) != 0) ++ error++; ++ } ++ } ++ closedir(dirp); ++ if (error || ++ (maildir_dir_is_empty(mailbox) && mddir == 0 && rmdir(realname) < 0)){ ++ snprintf(tmp, sizeof(tmp), "Can not remove folder %s: %s", mailbox, strerror(errno)); + mm_log (tmp, ERROR); + return NIL; -+ } -+ for (size = 0,i = SIZE (message),s = (char *) fs_get (i + 1); i; --i) -+ if ((c = SNX (message)) != '\015') s[size++] = c; -+ if ((write (fd, s, size) < 0) || fsync (fd)) { -+ unlink (path1); /* delete message */ -+ snprintf (tmp, sizeof(tmp), "Message append failed: %s", strerror (errno)); -+ mm_log (tmp, ERROR); -+ ret = NIL; -+ } -+ fs_give ((void **) &s); /* flush the buffer */ -+ close (fd); /* close the file */ -+ /* build final filename to use */ -+ if (maildir_file_path(mailbox, tmp, sizeof(tmp))) -+ MSGPATH(path2, tmp, file, New); -+ if (rename (path1,path2) < 0) { -+ snprintf (tmp, sizeof(tmp), "Message append failed: %s", strerror (errno)); -+ mm_log (tmp, ERROR); -+ ret = NIL; -+ } -+ unlink (path1); -+ -+ if (ret) -+ if (!(*af) (stream,data,&flags,&date,&message)) ret = NIL; -+ -+ } while (ret && message); /* write the data */ -+ mm_nocritical (stream); /* release critical */ -+ return ret; -+ } -+ -+ long maildir_delete (MAILSTREAM *stream,char *mailbox) -+ { -+ DIR *dirp; -+ struct direct *d; -+ int i, remove_dir = 0, mddir = 0, rv, error = 0; -+ char tmp[MAILTMPLEN],tmp2[MAILTMPLEN], realname[MAILTMPLEN]; -+ struct stat sbuf; -+ int courier = IS_COURIER(mailbox); -+ -+ if (mailbox[strlen(mailbox) - 1] == MDSEPARATOR(courier)){ -+ remove_dir++; -+ mailbox[strlen(mailbox) -1] = '\0'; -+ } -+ -+ if (!maildir_valid(mailbox)){ -+ maildir_file_path(mailbox, tmp, sizeof(tmp)); -+ if (stat(tmp, &sbuf) < 0 || !S_ISDIR(sbuf.st_mode)){ -+ snprintf(tmp, sizeof(tmp), "Can not remove %s", mailbox); -+ error++; -+ } -+ } -+ -+ if (!error && remove_dir && !maildir_dir_is_empty(mailbox)){ -+ snprintf(tmp, sizeof(tmp), "Can not remove directory %s/: directory not empty", mailbox); -+ error++; -+ } -+ -+ if(error){ -+ mm_log (tmp,ERROR); -+ return NIL; -+ } -+ -+ maildir_close(stream,0); /* even if stream was NULL */ -+ -+ maildir_file_path(mailbox, realname, sizeof(realname)); -+ -+ if (remove_dir){ -+ snprintf(tmp, sizeof(tmp), "%s/%s", realname, MDDIR); -+ if ((rv = stat (tmp,&sbuf)) == 0 && S_ISREG(sbuf.st_mode)) -+ rv = unlink(tmp); -+ else if (errno == ENOENT) -+ rv = 0; -+ if (rv != 0){ -+ snprintf(tmp, sizeof(tmp), "Can not remove %s/%s: %s", tmp2, MDDIR, strerror(errno)); -+ mm_log (tmp,ERROR); -+ return NIL; -+ } -+ if (!maildir_valid(realname) && rmdir(realname) != 0){ -+ snprintf(tmp, sizeof(tmp), "Can not remove %s/: %s", mailbox, strerror(errno)); -+ mm_log (tmp, ERROR); -+ return NIL; -+ } -+ return LONGT; -+ } -+ /* else remove just the folder. Remove all hidden files, except MDDIR */ -+ for (i = Cur; i != EndDir; i++){ -+ MDFLD(tmp, realname, i); -+ -+ if (!(dirp = opendir (tmp))){ -+ snprintf(tmp, sizeof(tmp), "Can not read %s/: %s", mailbox, strerror(errno)); -+ mm_log (tmp, ERROR); -+ return NIL; -+ } -+ -+ while ((d = readdir(dirp)) != NULL){ -+ if (strcmp(d->d_name, ".") && strcmp(d->d_name,"..")){ -+ snprintf(tmp2, sizeof(tmp2), "%s/%s", tmp, d->d_name); -+ if (unlink(tmp2) != 0){ -+ snprintf(tmp2, sizeof(tmp2), "Can not remove %s: %s", mailbox, strerror(errno)); -+ mm_log (tmp2, ERROR); -+ return NIL; -+ } -+ } -+ } -+ closedir(dirp); -+ if (rmdir(tmp) != 0){ -+ snprintf(tmp, sizeof(tmp), "Can not remove %s: %s", mailbox, strerror(errno)); -+ mm_log (tmp, ERROR); -+ return NIL; -+ } -+ } -+ /* -+ * ok we have removed all subdirectories of the folder mailbox, Remove the -+ * hidden files. -+ */ -+ -+ if(!(dirp = opendir (realname))){ -+ snprintf(tmp, sizeof(tmp), "Can not read %s/: %s", realname, strerror(errno)); -+ mm_log (tmp, ERROR); -+ return NIL; -+ } -+ -+ while ((d = readdir(dirp)) != NULL){ -+ if (strcmp(d->d_name, ".") && strcmp(d->d_name,"..") -+ && (!strcmp(d->d_name, MDDIR) -+ || !strncmp(d->d_name, MDUIDLAST, strlen(MDUIDLAST)) -+ || !strncmp(d->d_name, MDUIDTEMP, strlen(MDUIDTEMP)))){ -+ if(strcmp(d->d_name, MDDIR) == 0) -+ mddir++; -+ snprintf(tmp, sizeof(tmp), "%s/%s", realname, d->d_name); -+ if (unlink(tmp) != 0) -+ error++; -+ } -+ } -+ closedir(dirp); -+ if (error || -+ (maildir_dir_is_empty(mailbox) && mddir == 0 && rmdir(realname) < 0)){ -+ snprintf(tmp, sizeof(tmp), "Can not remove folder %s: %s", mailbox, strerror(errno)); -+ mm_log (tmp, ERROR); -+ return NIL; -+ } -+ return LONGT; -+ } -+ -+ long maildir_rename (MAILSTREAM *stream, char *old, char *new) -+ { -+ char tmp[MAILTMPLEN], tmpnew[MAILTMPLEN], realold[MAILTMPLEN]; -+ char realnew[MAILTMPLEN]; -+ int courier = IS_COURIER(old) && IS_COURIER(new); -+ int i; -+ long rv = LONGT; -+ COURIER_S *cdir; -+ -+ if((IS_COURIER(old) || IS_COURIER(new)) && !courier){ -+ snprintf (tmp, sizeof(tmp), "Can't rename mailbox %s to %s", old, new); -+ mm_log (tmp, ERROR); -+ return NIL; -+ } -+ -+ if (!maildir_valid(old)){ -+ snprintf (tmp, sizeof(tmp), "Can't rename mailbox %s: folder not in maildir format",old); -+ mm_log (tmp, ERROR); -+ return NIL; -+ } -+ maildir_file_path(old, realold, sizeof(realold)); -+ if (!maildir_valid_name(new) && new[0] == '#'){ -+ snprintf (tmp, sizeof(tmp), "Cannot rename mailbox %s: folder not in maildir format", new); -+ mm_log (tmp, ERROR); -+ return NIL; -+ } -+ maildir_file_path(new, realnew, sizeof(realnew)); -+ if (access(tmpnew,F_OK) == 0){ /* new mailbox name must not exist */ -+ snprintf (tmp, sizeof(tmp), "Cannot rename to mailbox %s: destination already exists", new); -+ mm_log (tmp, ERROR); -+ return NIL; -+ } -+ -+ if(!courier){ -+ if (rename(realold, realnew)){ /* try to rename the directory */ -+ snprintf(tmp, sizeof(tmp), "Can't rename mailbox %s to %s: %s", old, new, -+ strerror(errno)); -+ mm_log(tmp,ERROR); -+ return NIL; -+ } -+ return LONGT; /* return success */ -+ } -+ -+ cdir = courier_list_dir(old); -+ for (i = 0; cdir && i < cdir->total; i++){ -+ if(strstr(cdir->data[i]->name, old)){ -+ snprintf(tmp, sizeof(tmp), "%s%s", new, cdir->data[i]->name+strlen(old)); -+ maildir_file_path(cdir->data[i]->name, realold, sizeof(realold)); -+ maildir_file_path(tmp, realnew, sizeof(realnew)); -+ if (rename(realold, realnew)){ -+ snprintf (tmp, sizeof(tmp), "Can't rename mailbox %s to %s: %s", old, new, -+ strerror(errno)); -+ mm_log(tmp,ERROR); -+ rv = NIL; -+ } -+ } -+ } -+ courier_free_cdir(&cdir); -+ return rv; -+ } -+ -+ long maildir_sub(MAILSTREAM *stream,char *mailbox) -+ { -+ return sm_subscribe(mailbox); -+ } -+ -+ long maildir_unsub(MAILSTREAM *stream,char *mailbox) -+ { -+ return sm_unsubscribe(mailbox); -+ } -+ -+ void maildir_lsub (MAILSTREAM *stream,char *ref,char *pat) -+ { -+ void *sdb = NIL; -+ char *s, test[MAILTMPLEN]; -+ /* get canonical form of name */ -+ if (maildir_canonicalize (test, ref, pat) && (s = sm_read (&sdb))) { -+ do if (pmatch_full (s, test, '/')) mm_lsub (stream, '/', s, NIL); -+ while ((s = sm_read (&sdb)) != NULL); /* until no more subscriptions */ -+ } -+ } -+ -+ long maildir_canonicalize (char *pattern,char *ref,char *pat) -+ { -+ if (ref && *ref) { /* have a reference */ -+ strcpy (pattern,ref); /* copy reference to pattern */ -+ /* # overrides mailbox field in reference */ -+ if (*pat == '#') strcpy (pattern,pat); -+ /* pattern starts, reference ends, with / */ -+ else if ((*pat == '/') && (pattern[strlen (pattern) - 1] == '/')) -+ strcat (pattern,pat + 1); /* append, omitting one of the period */ -+ -+ else strcat (pattern,pat); /* anything else is just appended */ -+ } -+ else strcpy (pattern,pat); /* just have basic name */ -+ return maildir_valid_name(pattern) ? LONGT : NIL; -+ } -+ -+ void maildir_list_work (MAILSTREAM *stream,char *dir,char *pat,long level) -+ { -+ DIR *dp; -+ struct direct *d; -+ struct stat sbuf; -+ char curdir[MAILTMPLEN],name[MAILTMPLEN], tmp[MAILTMPLEN]; -+ char realpat[MAILTMPLEN]; -+ long i; -+ char *maildirpath = mdirpath(); -+ -+ snprintf(curdir, sizeof(curdir), "%s/%s/", myrootdir(pat), dir ? dir : maildirpath); -+ if ((dp = opendir (curdir)) != NULL){ -+ if (dir) snprintf (name, sizeof(name), "%s%s/",MDPREFIX(CCLIENT),dir); -+ else strcpy (name, pat); -+ -+ if (level == 0 && !strpbrk(pat,"%*")){ -+ if(maildir_valid(pat)){ -+ i = maildir_contains_folder(pat, NULL) -+ ? LATT_HASCHILDREN -+ : (maildir_is_dir(pat, NULL) -+ ? LATT_HASNOCHILDREN : LATT_NOINFERIORS); -+ maildir_file_path(pat, realpat, sizeof(realpat)); -+ i += maildir_any_new_msgs(realpat) -+ ? LATT_MARKED : LATT_UNMARKED; -+ mm_list (stream,'/', pat, i); -+ } -+ else -+ if(pat[strlen(pat) - 1] == '/') -+ mm_list (stream,'/', pat, LATT_NOSELECT); -+ } -+ -+ while ((d = readdir (dp)) != NULL) -+ if(strcmp(d->d_name, ".") && strcmp(d->d_name,"..") -+ && strcmp(d->d_name, MDNAME(Cur)) -+ && strcmp(d->d_name, MDNAME(Tmp)) -+ && strcmp(d->d_name, MDNAME(New))){ -+ -+ if (dir) snprintf (tmp, sizeof(tmp), "%s%s", name,d->d_name); -+ else strcpy(tmp, d->d_name); -+ -+ if(pmatch_full (tmp, pat,'/')){ -+ snprintf(tmp, sizeof(tmp), "%s/%s/%s", myrootdir(d->d_name), -+ (dir ? dir : maildirpath), d->d_name); -+ if(stat (tmp,&sbuf) == 0 -+ && ((sbuf.st_mode & S_IFMT) == S_IFDIR)){ -+ if (dir) snprintf (tmp, sizeof(tmp), "%s%s", name,d->d_name); -+ else strcpy(tmp, d->d_name); -+ i = maildir_valid(tmp) -+ ? (maildir_contains_folder(dir, d->d_name) -+ ? LATT_HASCHILDREN -+ : (maildir_is_dir(dir, d->d_name) -+ ? LATT_HASNOCHILDREN : LATT_NOINFERIORS)) -+ : LATT_NOSELECT; -+ i += maildir_any_new_msgs(tmp) -+ ? LATT_MARKED : LATT_UNMARKED; -+ mm_list (stream,'/',tmp, i); -+ strcat (tmp, "/"); -+ if(dmatch (tmp, pat,'/') && -+ (level < (long) mail_parameters (NIL,GET_LISTMAXLEVEL,NIL))){ -+ snprintf(tmp, sizeof(tmp), "%s/%s",dir,d->d_name); -+ maildir_list_work (stream,tmp,pat,level+1); -+ } -+ } -+ } -+ } -+ closedir (dp); -+ } -+ } -+ -+ void courier_list_work (MAILSTREAM *stream, char *dir, char *pat, long level) -+ { -+ char c, curdir[MAILTMPLEN], tmp[MAILTMPLEN]; -+ char realname[MAILTMPLEN], realpat[MAILTMPLEN] = {'\0'}; -+ int i, found; -+ long style = (long) maildir_parameters(GET_COURIERSTYLE, NIL), j; -+ char *maildirpath = mdirpath(); -+ COURIER_S *cdir; -+ -+ if(!strpbrk(pat,"%*")){ /* a mailbox */ -+ maildir_file_path(pat, curdir, sizeof(curdir)); -+ i = strlen(curdir) - 1; -+ if(curdir[i] == '/') -+ curdir[i] = '\0'; -+ cdir = courier_list_dir(curdir); -+ if(cdir){ -+ found = 0; j = 0L; -+ if(maildir_valid_name(pat)){ -+ for(i = 0; !found && i < cdir->total; i++) -+ if(strstr(curdir, cdir->data[i]->name)){ -+ if(strlen(curdir) < strlen(cdir->data[i]->name)) -+ found += 2; -+ else if(strlen(curdir) == strlen(cdir->data[i]->name)) -+ found -= 1; -+ } -+ if(found > 0) -+ j = LATT_HASCHILDREN; -+ else if(found == 0) -+ j = (style == COURIER) ? LATT_HASNOCHILDREN : LATT_NOINFERIORS; -+ } -+ else -+ j = LATT_NOSELECT; -+ j += maildir_any_new_msgs(curdir) ? LATT_MARKED : LATT_UNMARKED; -+ if (found) -+ mm_list (stream, '.', pat, j); -+ courier_free_cdir(&cdir); -+ } -+ return; -+ } -+ -+ strcpy(tmp,pat + 4); /* a directory */ -+ j = strlen(pat) - 1; -+ maildir_file_path(pat, realpat, sizeof(realpat)); -+ c = pat[j]; -+ pat[j] = '\0'; -+ realname[0] = '\0'; -+ if(dir) -+ maildir_file_path(dir, realname, sizeof(realname)); -+ snprintf(curdir, sizeof(curdir), "%s%s%s/%s", (dir ? "" : myrootdir(pat)), (dir ? "" : "/"), -+ (dir ? realname : maildirpath), (dir ? "" : ".")); -+ snprintf(tmp, sizeof(tmp), "%s%s/.", MDPREFIX(COURIER), dir ? dir : maildirpath); -+ if (level == 0 && tmp && pmatch_full (tmp, realpat, '.')) -+ mm_list (stream,'.', tmp, LATT_NOSELECT); -+ -+ cdir = courier_list_dir(pat); -+ pat[j] = c; -+ for (i = 0; cdir && i < cdir->total; i++) -+ if(pmatch_full (cdir->data[i]->name, pat, '.')){ -+ snprintf(tmp, sizeof(tmp), "%s.", cdir->data[i]->name); -+ courier_list_info(&cdir, tmp, i); -+ mm_list (stream,'.',cdir->data[i]->name, cdir->data[i]->attribute); -+ } -+ courier_free_cdir(&cdir); -+ } -+ -+ int -+ same_maildir_file(char *name1, char *name2) -+ { -+ char tmp1[MAILTMPLEN], tmp2[MAILTMPLEN]; -+ char *s; -+ -+ strcpy(tmp1, name1 ? name1 : ""); -+ strcpy(tmp2, name2 ? name2 : ""); -+ if ((s = strrchr(tmp1, FLAGSEP)) != NULL) -+ *s = '\0'; -+ if (((s = strrchr(tmp1, SIZESEP)) != NULL) && (strchr(s,'.') == NULL)) -+ *s = '\0'; -+ if ((s = strrchr(tmp2, FLAGSEP)) != NULL) -+ *s = '\0'; -+ if (((s = strrchr(tmp2, SIZESEP)) != NULL) && (strchr(s,'.') == NULL)) -+ *s = '\0'; -+ -+ return !strcmp(tmp1, tmp2); -+ } -+ -+ unsigned long antoul(char *seed) -+ { -+ int i, error = 0; -+ unsigned long val = 0L, rv1 = 0L, t; -+ char c, *p; -+ if(!seed) -+ return 0L; -+ t = strtoul(seed, &p, 10); -+ if(p && (*p == '.' || *p == '_')) -+ return t; -+ /* else */ -+ if((p = strchr(seed,'.')) != NULL) -+ *p = '\0'; -+ error = (strlen(seed) > 6); /* too long */ -+ for(i= strlen(seed)-1; error == 0 && i >= 0; i--){ -+ c = seed[i]; -+ if (c >= 'A' && c <= 'Z') val = c - 'A'; -+ else if (c >= 'a' && c <= 'z') val = c - 'a' + 26; -+ else if (c >= '0' && c <= '9') val = c - '0' + 26 + 26; -+ else if (c == '-') val = c - '-' + 26 + 26 + 10; -+ else if (c == '_') val = c - '_' + 26 + 26 + 10 + 1; -+ else error++; -+ rv1 = val + (rv1 << 6); + } -+ if(p) -+ *p = '.'; -+ return error ? 0L : rv1; -+ } -+ -+ unsigned long mdfntoul (char *name) -+ { -+ unsigned long t; -+ char *r, last; -+ -+ if((*name == '_') && ((r = strpbrk(name,".,%+")) != NULL)){ /* Grrr!!! */ -+ last = *r; -+ *r = '\0'; -+ t = antoul(r+1); -+ *r = last; -+ } -+ else -+ t = antoul(name); -+ return t; -+ } -+ -+ int comp_maildir_file(char *name1, char *name2) -+ { -+ int uset1 = 1, uset2 = 1, i, j, cmp; -+ unsigned long t1, t2; -+ char *s1, *s2; -+ -+ if (!(name1 && *name1)) -+ return (name2 && *name2) ? (*name2 == FLAGSEP ? 0 : -1) : 0; -+ -+ if (!(name2 && *name2)) -+ return (name1 && *name1) ? (*name1 == FLAGSEP ? 0 : 1) : 0; -+ -+ if((cmp = strcmp(name1,name2)) == 0) -+ return 0; -+ -+ t1 = strtoul(name1, &s1, 10); -+ t2 = strtoul(name2, &s2, 10); -+ -+ if(!s1 || *s1 != '.') -+ uset1 = 0; -+ -+ if(!s2 || *s2 != '.') -+ uset2 = 0; -+ -+ if(uset1 && uset2) /* normal sort order */ -+ return (t1 < t2) ? -1 : (t1 > t2 ? 1 : (cmp < 0 ? -1 : 1)); -+ -+ /* If we make it here we say Grrrr.... first, then we try to figure out -+ * how to sort this mess. -+ * These are the rules. -+ * If there is a number at the beginning it is bigger than anything else. -+ * If there are digits, then the number of digits decides which one is bigger. -+ */ -+ -+ for(i = 0; isdigit(name1[i]); i++); -+ for(j = 0; isdigit(name2[j]); j++); -+ -+ return(uset1 ? 1 -+ : (uset2 ? -1 -+ : (i < j ? -1 : (i > j ? 1 : (cmp < 0 ? -1 : 1))))); -+ } -+ -+ void -+ maildir_getflag(char *name, int *d, int *f, int *r ,int *s, int *t) -+ { -+ char tmp[MAILTMPLEN], *b; -+ int offset = 0; -+ int tmpd, tmpf, tmpr, tmps, tmpt; -+ -+ if(d) *d = 0; -+ if(f) *f = 0; -+ if(r) *r = 0; -+ if(s) *s = 0; -+ if(t) *t = 0; -+ -+ tmpd = tmpf = tmpr = tmps = tmpt = NIL; /* no flags set by default */ -+ strcpy(tmp,name); -+ while ((b = strrchr(tmp+offset, FLAGSEP)) != NULL){ -+ char flag,last; -+ int k; -+ if (!++b) break; -+ switch (*b){ -+ case '1': -+ case '2': -+ case '3': flag = *b; b += 2; -+ for (k = 0; b[k] && b[k] != FLAGSEP && b[k] != ','; k++); -+ last = b[k]; -+ b[k] = '\0'; -+ if (flag == '2' || flag == '3'){ -+ tmpd = strchr (b, MDFLAGC(Draft)) ? T : NIL; -+ tmpf = strchr (b, MDFLAGC(Flagged)) ? T : NIL; -+ tmpr = strchr (b, MDFLAGC(Replied)) ? T : NIL; -+ tmps = strchr (b, MDFLAGC(Seen)) ? T : NIL; -+ tmpt = strchr (b, MDFLAGC(Trashed)) ? T : NIL; -+ } -+ b[k] = last; -+ b += k; -+ for (; tmp[offset] && tmp[offset] != FLAGSEP; offset++); -+ offset++; -+ break; -+ default: break; /* Should we crash?... Nahhh */ -+ } -+ } -+ if(d) *d = tmpd; -+ if(f) *f = tmpf; -+ if(r) *r = tmpr; -+ if(s) *s = tmps; -+ if(t) *t = tmpt; -+ } -+ -+ int -+ maildir_message_in_list(char *msgname, struct direct **names, -+ unsigned long bottom, unsigned long top, unsigned long *pos) -+ { -+ unsigned long middle = (bottom + top)/2; -+ int test; -+ -+ if (!msgname) -+ return NIL; -+ -+ if (pos) *pos = middle; -+ -+ if (same_maildir_file(msgname, names[middle]->d_name)) -+ return T; -+ -+ if (middle == bottom){ /* 0 <= 0 < 1 */ -+ int rv = NIL; -+ if (same_maildir_file(msgname, names[middle]->d_name)){ -+ rv = T; -+ if (pos) *pos = middle; -+ } -+ else -+ if (same_maildir_file(msgname, names[top]->d_name)){ -+ rv = T; -+ if (pos) *pos = top; -+ } -+ return rv; -+ } -+ -+ test = comp_maildir_file(msgname, names[middle]->d_name); -+ -+ if (top <= bottom) -+ return test ? NIL : T; -+ -+ if (test < 0 ) /* bottom < msgname < middle */ -+ return maildir_message_in_list(msgname, names, bottom, middle, pos); -+ else if (test > 0) /* middle < msgname < top */ -+ return maildir_message_in_list(msgname, names, middle, top, pos); -+ else return T; -+ } -+ -+ void -+ maildir_abort(MAILSTREAM *stream) -+ { -+ if (LOCAL){ -+ DirNamesType i; -+ -+ if(LOCAL->candouid) -+ maildir_read_uid(stream, NULL, &stream->uid_validity); -+ if (LOCAL->dir) fs_give ((void **) &LOCAL->dir); -+ for (i = Cur; i < EndDir; i++) -+ if(LOCAL->path[i]) fs_give ((void **) &LOCAL->path[i]); -+ fs_give ((void **) &LOCAL->path); -+ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf); -+ if(LOCAL->uidtempfile){ -+ unlink(LOCAL->uidtempfile); -+ fs_give ((void **) &LOCAL->uidtempfile); -+ } -+ fs_give ((void **) &stream->local); -+ } -+ if (mdfpath) fs_give((void **)&mdfpath); -+ stream->dtb = NIL; -+ } -+ -+ int -+ maildir_contains_folder(char *dirname, char *name) -+ { -+ char tmp[MAILTMPLEN], tmp2[MAILTMPLEN]; -+ int rv = 0; -+ DIR *dir; -+ struct direct *d; -+ -+ maildir_file_path(dirname, tmp2, sizeof(tmp2)); -+ if(name){ -+ strcat(tmp2,"/"); -+ strcat(tmp2, name); -+ } -+ -+ if (!(dir = opendir (tmp2))) -+ return NIL; -+ -+ while ((d = readdir(dir)) != NULL){ -+ if (strcmp(d->d_name, ".") && strcmp(d->d_name,"..") -+ && strcmp(d->d_name, MDNAME(Cur)) -+ && strcmp(d->d_name, MDNAME(Tmp)) -+ && strcmp(d->d_name, MDNAME(New))){ -+ -+ snprintf(tmp, sizeof(tmp), "%s/%s", tmp2, d->d_name); -+ if(maildir_valid(tmp)){ -+ rv++; -+ break; -+ } -+ } -+ } -+ closedir(dir); -+ return rv; -+ } -+ -+ int -+ maildir_is_dir(char *dirname, char *name) -+ { -+ char tmp[MAILTMPLEN]; -+ struct stat sbuf; -+ -+ maildir_file_path(dirname, tmp, sizeof(tmp)); -+ if(name){ -+ strcat(tmp, "/"); -+ strcat(tmp, name); -+ } -+ strcat(tmp, "/"); -+ strcat(tmp, MDDIR); -+ -+ return ((stat(tmp, &sbuf) == 0) && S_ISREG (sbuf.st_mode)) ? 1 : 0; -+ } -+ -+ int -+ maildir_dir_is_empty(char *mailbox) -+ { -+ char tmp[MAILTMPLEN], tmp2[MAILTMPLEN], tmp3[MAILTMPLEN],*s; -+ int rv = 1, courier = IS_COURIER(mailbox); -+ DIR *dir; -+ struct direct *d; -+ struct stat sbuf; -+ -+ maildir_file_path(mailbox, tmp2, sizeof(tmp2)); -+ -+ if(courier){ -+ strcpy(tmp3, tmp2); -+ if(s = strrchr(tmp2, '/')) -+ *s = '\0'; -+ } -+ -+ if (!(dir = opendir (tmp2))) -+ return rv; -+ -+ if(courier){ -+ while((d = readdir(dir)) != NULL){ -+ snprintf(tmp, sizeof(tmp), "%s/%s", tmp2, d->d_name); -+ if(!strncmp(tmp, tmp3, strlen(tmp3)) -+ && tmp[strlen(tmp3)] == '.'){ -+ rv = 0; -+ break; -+ } -+ } -+ } -+ else -+ while ((d = readdir(dir)) != NULL){ -+ snprintf(tmp, sizeof(tmp), "%s/%s", tmp2, d->d_name); -+ if (strcmp(d->d_name, ".") -+ && strcmp(d->d_name,"..") -+ && strcmp(d->d_name, MDNAME(Cur)) -+ && strcmp(d->d_name, MDNAME(Tmp)) -+ && strcmp(d->d_name, MDNAME(New)) -+ && strcmp(d->d_name, MDDIR) -+ && strcmp(d->d_name, MDUIDVALIDITY) -+ && !(d->d_name[0] == '.' -+ && stat (tmp,&sbuf) == 0 -+ && S_ISREG(sbuf.st_mode))){ -+ rv = 0; -+ break; -+ } -+ } -+ closedir(dir); -+ return rv; -+ } -+ -+ void -+ maildir_get_file (MAILDIRFILE **mdfile) -+ { -+ MAILDIRFILE *md; -+ -+ md = (MAILDIRFILE *) fs_get(sizeof(MAILDIRFILE)); -+ memset(md, 0, sizeof(MAILDIRFILE)); -+ *mdfile = md; -+ } -+ -+ void -+ maildir_free_file (void **mdfile) -+ { -+ MAILDIRFILE *md = (mdfile && *mdfile) ? (MAILDIRFILE *) *mdfile : NULL; -+ -+ if (md){ -+ if (md->name) fs_give((void **)&md->name); -+ fs_give((void **)&md); -+ } -+ } -+ -+ void -+ maildir_free_file_only (void **mdfile) -+ { -+ MAILDIRFILE *md = (mdfile && *mdfile) ? (MAILDIRFILE *) *mdfile : NULL; -+ -+ if (md && md->name) -+ fs_give((void **)&md->name); -+ } -+ -+ int -+ maildir_any_new_msgs(char *mailbox) -+ { -+ char tmp[MAILTMPLEN]; -+ int rv = NIL; -+ DIR *dir; -+ struct direct *d; -+ -+ MDFLD(tmp, mailbox, New); -+ -+ if (!(dir = opendir (tmp))) -+ return rv; -+ -+ while ((d = readdir(dir)) != NULL){ -+ if (d->d_name[0] == '.') -+ continue; -+ rv = T; -+ break; -+ } -+ closedir(dir); -+ return rv; -+ } -+ -+ -+ void -+ maildir_get_date(MAILSTREAM *stream, unsigned long msgno) -+ { -+ MESSAGECACHE *elt; -+ struct tm *t; -+ time_t ti; -+ int i,k; -+ -+ elt = mail_elt (stream,msgno); -+ if(elt && elt->year != 0) -+ return; -+ if ((ti = mdfntoul(MDFILE(elt))) > 0L && (t = gmtime(&ti))){ -+ i = t->tm_hour * 60 + t->tm_min; -+ k = t->tm_yday; -+ t = localtime(&ti); -+ i = t->tm_hour * 60 + t->tm_min - i; -+ if((k = t->tm_yday - k) != 0) -+ i += ((k < 0) == (abs (k) == 1)) ? -24*60 : 24*60; -+ k = abs (i); -+ elt->hours = t->tm_hour; -+ elt->minutes = t->tm_min; -+ elt->seconds = t->tm_sec; -+ elt->day = t->tm_mday; elt->month = t->tm_mon + 1; -+ elt->year = t->tm_year - (BASEYEAR - 1900); -+ elt->zoccident = (k == i) ? 0 : 1; -+ elt->zhours = k/60; -+ elt->zminutes = k % 60; -+ } -+ } -+ -+ /* Support for Courier Style directories -+ When this code is complete there will be two types of support, which -+ will be configurable. The problem is the following: In Courier style -+ folder structure, a "folder" may have a subfolder called -+ "folder.subfolder", which is not natural in the file system in the -+ sense that I can not stat for "folder.subfolder" wihtout knowing what -+ "subfolder" is. It needs to be guessed. Because of this I need to look -+ in the list of folders if there is a folder with a name -+ "folder.subfolder", before I can say if the folder is dual or not. One -+ can avoid this annoyance if one ignores the problem by declaring that -+ every folder is dual. I will however code as the default the more -+ complicated idea of scaning the containing directory each time it is -+ modified and search for subfolders, and list the entries it found. -+ */ -+ -+ int courier_dir_select (const struct direct *name) -+ { -+ return name->d_name[0] == '.' && (strlen(name->d_name) > 2 -+ || (strlen(name->d_name) == 2 && name->d_name[1] != '.')); -+ } -+ -+ int courier_dir_sort (const struct direct **d1, const struct direct **d2) -+ { -+ const struct direct *e1 = *(const struct direct **) d1; -+ const struct direct *e2 = *(const struct direct **) d2; -+ -+ return strcmp((char *) e1->d_name, (char *) e2->d_name); -+ } -+ -+ void courier_free_cdir (COURIER_S **cdir) -+ { -+ int i; -+ -+ if (!*cdir) -+ return; -+ -+ if ((*cdir)->path) fs_give((void **)&((*cdir)->path)); -+ for (i = 0; i < (*cdir)->total; i++) -+ if((*cdir)->data[i]->name) fs_give((void **)&((*cdir)->data[i]->name)); -+ fs_give((void **)&((*cdir)->data)); -+ fs_give((void **)&(*cdir)); -+ } -+ -+ COURIER_S *courier_get_cdir (int total) -+ { ++ return LONGT; ++} ++ ++long maildir_rename (MAILSTREAM *stream, char *old, char *new) ++{ ++ char tmp[MAILTMPLEN], tmpnew[MAILTMPLEN], realold[MAILTMPLEN]; ++ char realnew[MAILTMPLEN]; ++ int courier = IS_COURIER(old) && IS_COURIER(new); ++ int i; ++ long rv = LONGT; + COURIER_S *cdir; -+ -+ cdir = (COURIER_S *)fs_get(sizeof(COURIER_S)); -+ memset(cdir, 0, sizeof(COURIER_S)); -+ cdir->data = (COURIERLOCAL **) fs_get(total*sizeof(COURIERLOCAL *)); -+ memset(cdir->data, 0, sizeof(COURIERLOCAL *)); -+ cdir->total = total; -+ return cdir; -+ } -+ -+ int courier_search_list(COURIERLOCAL **data, char *name, int first, int last) -+ { -+ int try = (first + last)/2; -+ -+ if(!strstr(data[try]->name, name)){ -+ if(first == try) /* first == last || first + 1 == last */ -+ return strstr(data[last]->name, name) ? 1 : 0; -+ if(strcmp(data[try]->name, name) < 0) /*data[try] < name < data[end] */ -+ return courier_search_list(data, name, try, last); -+ else /* data[begin] < name < data[try] */ -+ return courier_search_list(data, name, first, try); -+ } -+ return 1; -+ } -+ -+ /* Lists all directories that are subdirectories of a given directory */ -+ -+ COURIER_S *courier_list_dir(char *curdir) -+ { -+ struct direct **names = NIL; -+ struct stat sbuf; -+ unsigned long ndir; -+ COURIER_S *cdir = NULL; -+ char tmp[MAILTMPLEN], tmp2[MAILTMPLEN], pathname[MAILTMPLEN], -+ realname[MAILTMPLEN]; -+ int i, j, scand, td; -+ -+ /* There are two cases, either curdir is -+ #mc/INBOX. #mc/INBOX.foo -+ or -+ #mc/Maildir/. #mc/Maildir/.foo -+ */ -+ strcpy(tmp,curdir + 4); -+ if(!strncmp(ucase(tmp), "INBOX", 5)) -+ strcpy(tmp, "#mc/INBOX."); -+ else{ -+ strcpy(tmp, curdir); -+ for (i = strlen(tmp) - 1; tmp[i] && tmp[i] != '/'; i--); -+ tmp[i+2] = '\0'; /* keep the last "." intact */ -+ } -+ maildir_file_path(tmp, realname, sizeof(realname)); -+ maildir_scandir (realname, &names, &ndir, &scand, COURIER); -+ -+ if (scand > 0){ -+ cdir = courier_get_cdir(ndir); -+ cdir->path = cpystr(realname); -+ for(i = 0, j = 0; i < ndir; i++){ -+ td = realname[strlen(realname) - 1] == '.' -+ && *names[i]->d_name == '.'; -+ snprintf(tmp2, sizeof(tmp2), "%s%s", tmp, names[i]->d_name+1); -+ snprintf(pathname, sizeof(pathname), "%s%s", realname, names[i]->d_name + td); -+ if(stat(pathname, &sbuf) == 0 && S_ISDIR(sbuf.st_mode)){ -+ cdir->data[j] = (COURIERLOCAL *) fs_get(sizeof(COURIERLOCAL)); -+ cdir->data[j++]->name = cpystr(tmp2); -+ } -+ fs_give((void **)&names[i]); -+ } -+ cdir->total = j; -+ if(cdir->total == 0) -+ courier_free_cdir(&cdir); -+ } -+ if(names) -+ fs_give((void **) &names); -+ return cdir; -+ } -+ -+ void -+ courier_list_info(COURIER_S **cdirp, char *data, int i) -+ { -+ long style = (long) maildir_parameters(GET_COURIERSTYLE, NIL); -+ COURIER_S *cdir = *cdirp; -+ -+ if(maildir_valid(cdir->data[i]->name)){ -+ if(courier_search_list(cdir->data, data, 0, cdir->total - 1)) -+ cdir->data[i]->attribute = LATT_HASCHILDREN; -+ else -+ cdir->data[i]->attribute = (style == COURIER) -+ ? LATT_HASNOCHILDREN : LATT_NOINFERIORS; ++ ++ if((IS_COURIER(old) || IS_COURIER(new)) && !courier){ ++ snprintf (tmp, sizeof(tmp), "Can't rename mailbox %s to %s", old, new); ++ mm_log (tmp, ERROR); ++ return NIL; ++ } ++ ++ if (!maildir_valid(old)){ ++ snprintf (tmp, sizeof(tmp), "Can't rename mailbox %s: folder not in maildir format",old); ++ mm_log (tmp, ERROR); ++ return NIL; ++ } ++ maildir_file_path(old, realold, sizeof(realold)); ++ if (!maildir_valid_name(new) && new[0] == '#'){ ++ snprintf (tmp, sizeof(tmp), "Cannot rename mailbox %s: folder not in maildir format", new); ++ mm_log (tmp, ERROR); ++ return NIL; ++ } ++ maildir_file_path(new, realnew, sizeof(realnew)); ++ if (access(tmpnew,F_OK) == 0){ /* new mailbox name must not exist */ ++ snprintf (tmp, sizeof(tmp), "Cannot rename to mailbox %s: destination already exists", new); ++ mm_log (tmp, ERROR); ++ return NIL; ++ } ++ ++ if(!courier){ ++ if (rename(realold, realnew)){ /* try to rename the directory */ ++ snprintf(tmp, sizeof(tmp), "Can't rename mailbox %s to %s: %s", old, new, ++ strerror(errno)); ++ mm_log(tmp,ERROR); ++ return NIL; + } -+ else -+ cdir->data[i]->attribute = LATT_NOSELECT; -+ cdir->data[i]->attribute += maildir_any_new_msgs(cdir->data[i]->name) -+ ? LATT_MARKED : LATT_UNMARKED; -+ } -+ -+ /* UID Support */ -+ /* Yes, I know I procastinated a lot about this, but here it is finally */ -+ -+ /* return code: -+ bigger than zero: this session can assign uids -+ zero: this session will not assign uid -+ smaller than zero: this session temporarily suspends assigning uids -+ */ -+ int -+ maildir_can_assign_uid (MAILSTREAM *stream) -+ { -+ unsigned int rv = 0; -+ int ownuid, existuid; -+ unsigned long t; -+ char tmp[MAILTMPLEN], tmp2[MAILTMPLEN], *p, *s; -+ DIR *dir; -+ struct direct *d; -+ -+ if(!stream || stream->rdonly -+ || !LOCAL || !LOCAL->dir || !(dir = opendir(LOCAL->dir))) -+ return 0; -+ -+ if(mypid == (pid_t) 0) -+ mypid = getpid(); -+ -+ snprintf(tmp, sizeof(tmp), "%s.%d", MDUIDTEMP, mypid); -+ -+ ownuid = existuid = 0; -+ s = NULL; -+ while ((d = readdir(dir)) != NULL){ -+ if(strncmp(d->d_name, tmp, strlen(tmp)) == 0){ -+ existuid++; ownuid++; -+ if(ownuid > 1){ -+ snprintf(tmp2, sizeof(tmp), "%s/%s", LOCAL->dir, d->d_name); -+ unlink(tmp2); -+ if(s){ -+ snprintf(tmp2, sizeof(tmp2), "%s/%s", LOCAL->dir, s); -+ unlink(tmp2); -+ fs_give((void **)&s); -+ } -+ } -+ else -+ s = cpystr(d->d_name); ++ return LONGT; /* return success */ ++ } ++ ++ cdir = courier_list_dir(old); ++ for (i = 0; cdir && i < cdir->total; i++){ ++ if(strstr(cdir->data[i]->name, old)){ ++ snprintf(tmp, sizeof(tmp), "%s%s", new, cdir->data[i]->name+strlen(old)); ++ maildir_file_path(cdir->data[i]->name, realold, sizeof(realold)); ++ maildir_file_path(tmp, realnew, sizeof(realnew)); ++ if (rename(realold, realnew)){ ++ snprintf (tmp, sizeof(tmp), "Can't rename mailbox %s to %s: %s", old, new, ++ strerror(errno)); ++ mm_log(tmp,ERROR); ++ rv = NIL; ++ } ++ } ++ } ++ courier_free_cdir(&cdir); ++ return rv; ++} ++ ++long maildir_sub(MAILSTREAM *stream,char *mailbox) ++{ ++ return sm_subscribe(mailbox); ++} ++ ++long maildir_unsub(MAILSTREAM *stream,char *mailbox) ++{ ++ return sm_unsubscribe(mailbox); ++} ++ ++void maildir_lsub (MAILSTREAM *stream,char *ref,char *pat) ++{ ++ void *sdb = NIL; ++ char *s, test[MAILTMPLEN]; ++ /* get canonical form of name */ ++ if (maildir_canonicalize (test, ref, pat) && (s = sm_read (&sdb))) { ++ do if (pmatch_full (s, test, '/')) mm_lsub (stream, '/', s, NIL); ++ while ((s = sm_read (&sdb)) != NULL); /* until no more subscriptions */ ++ } ++} ++ ++long maildir_canonicalize (char *pattern,char *ref,char *pat) ++{ ++ if (ref && *ref) { /* have a reference */ ++ strcpy (pattern,ref); /* copy reference to pattern */ ++ /* # overrides mailbox field in reference */ ++ if (*pat == '#') strcpy (pattern,pat); ++ /* pattern starts, reference ends, with / */ ++ else if ((*pat == '/') && (pattern[strlen (pattern) - 1] == '/')) ++ strcat (pattern,pat + 1); /* append, omitting one of the period */ ++ ++ else strcat (pattern,pat); /* anything else is just appended */ ++ } ++ else strcpy (pattern,pat); /* just have basic name */ ++ return maildir_valid_name(pattern) ? LONGT : NIL; ++} ++ ++void maildir_list_work (MAILSTREAM *stream,char *dir,char *pat,long level) ++{ ++ DIR *dp; ++ struct direct *d; ++ struct stat sbuf; ++ char curdir[MAILTMPLEN],name[MAILTMPLEN], tmp[MAILTMPLEN]; ++ char realpat[MAILTMPLEN]; ++ long i; ++ char *maildirpath = mdirpath(); ++ ++ snprintf(curdir, sizeof(curdir), "%s/%s/", myrootdir(pat), dir ? dir : maildirpath); ++ if ((dp = opendir (curdir)) != NULL){ ++ if (dir) snprintf (name, sizeof(name), "%s%s/",MDPREFIX(CCLIENT),dir); ++ else strcpy (name, pat); ++ ++ if (level == 0 && !strpbrk(pat,"%*")){ ++ if(maildir_valid(pat)){ ++ i = maildir_contains_folder(pat, NULL) ++ ? LATT_HASCHILDREN ++ : (maildir_is_dir(pat, NULL) ++ ? LATT_HASNOCHILDREN : LATT_NOINFERIORS); ++ maildir_file_path(pat, realpat, sizeof(realpat)); ++ i += maildir_any_new_msgs(realpat) ++ ? LATT_MARKED : LATT_UNMARKED; ++ mm_list (stream,'/', pat, i); ++ } ++ else ++ if(pat[strlen(pat) - 1] == '/') ++ mm_list (stream,'/', pat, LATT_NOSELECT); + } -+ else if(strncmp(d->d_name, MDUIDTEMP, strlen(MDUIDTEMP)) == 0) -+ existuid++; -+ } -+ -+ closedir(dir); -+ if(s) -+ fs_give((void **)&s); -+ -+ if(ownuid == 1 && existuid == 1) -+ rv = 1; -+ -+ if(ownuid == 0 && existuid == 0){ /* nobody owns the uid? */ -+ FILE *fp; -+ snprintf(tmp, sizeof(tmp), "%s/%s.%d.%lu", LOCAL->dir, MDUIDTEMP, mypid, time(0)); -+ if(fp = fopen(tmp, "w")){ -+ fclose(fp); -+ if(LOCAL->uidtempfile) -+ fs_give((void **)&LOCAL->uidtempfile); -+ LOCAL->uidtempfile = cpystr(tmp); -+ } -+ rv = 1; -+ } -+ -+ if(ownuid == 0 && existuid > 0) /* someone else owns uid assignment */ -+ return 0; -+ -+ /* if we own the uid, check that we do not own it more than once -+ * or that we share ownership. If any of these situations happens, -+ * give up the ownership until we can recover it -+ */ -+ -+ if(ownuid > 0){ -+ if(ownuid > 1) /* impossible, two lock files for the same session */ -+ return (-1)*ownuid; -+ -+ if(ownuid != existuid){ /* lock files for different sessions */ -+ if(LOCAL->uidtempfile){ -+ unlink(LOCAL->uidtempfile); -+ fs_give((void **)&LOCAL->uidtempfile); ++ ++ while ((d = readdir (dp)) != NULL) ++ if(strcmp(d->d_name, ".") && strcmp(d->d_name,"..") ++ && strcmp(d->d_name, MDNAME(Cur)) ++ && strcmp(d->d_name, MDNAME(Tmp)) ++ && strcmp(d->d_name, MDNAME(New))){ ++ ++ if (dir) snprintf (tmp, sizeof(tmp), "%s%s", name,d->d_name); ++ else strcpy(tmp, d->d_name); ++ ++ if(pmatch_full (tmp, pat,'/')){ ++ snprintf(tmp, sizeof(tmp), "%s/%s/%s", myrootdir(d->d_name), ++ (dir ? dir : maildirpath), d->d_name); ++ if(stat (tmp,&sbuf) == 0 ++ && ((sbuf.st_mode & S_IFMT) == S_IFDIR)){ ++ if (dir) snprintf (tmp, sizeof(tmp), "%s%s", name,d->d_name); ++ else strcpy(tmp, d->d_name); ++ i = maildir_valid(tmp) ++ ? (maildir_contains_folder(dir, d->d_name) ++ ? LATT_HASCHILDREN ++ : (maildir_is_dir(dir, d->d_name) ++ ? LATT_HASNOCHILDREN : LATT_NOINFERIORS)) ++ : LATT_NOSELECT; ++ i += maildir_any_new_msgs(tmp) ++ ? LATT_MARKED : LATT_UNMARKED; ++ mm_list (stream,'/',tmp, i); ++ strcat (tmp, "/"); ++ if(dmatch (tmp, pat,'/') && ++ (level < (long) mail_parameters (NIL,GET_LISTMAXLEVEL,NIL))){ ++ snprintf(tmp, sizeof(tmp), "%s/%s",dir,d->d_name); ++ maildir_list_work (stream,tmp,pat,level+1); ++ } ++ } ++ } + } -+ return (-1)*ownuid; ++ closedir (dp); ++ } ++} ++ ++void courier_list_work (MAILSTREAM *stream, char *dir, char *pat, long level) ++{ ++ char c, curdir[MAILTMPLEN], tmp[MAILTMPLEN]; ++ char realname[MAILTMPLEN], realpat[MAILTMPLEN] = {'\0'}; ++ int i, found; ++ long style = (long) maildir_parameters(GET_COURIERSTYLE, NIL), j; ++ char *maildirpath = mdirpath(); ++ COURIER_S *cdir; ++ ++ if(!strpbrk(pat,"%*")){ /* a mailbox */ ++ maildir_file_path(pat, curdir, sizeof(curdir)); ++ i = strlen(curdir) - 1; ++ if(curdir[i] == '/') ++ curdir[i] = '\0'; ++ cdir = courier_list_dir(curdir); ++ if(cdir){ ++ found = 0; j = 0L; ++ if(maildir_valid_name(pat)){ ++ for(i = 0; !found && i < cdir->total; i++) ++ if(strstr(curdir, cdir->data[i]->name)){ ++ if(strlen(curdir) < strlen(cdir->data[i]->name)) ++ found += 2; ++ else if(strlen(curdir) == strlen(cdir->data[i]->name)) ++ found -= 1; ++ } ++ if(found > 0) ++ j = LATT_HASCHILDREN; ++ else if(found == 0) ++ j = (style == COURIER) ? LATT_HASNOCHILDREN : LATT_NOINFERIORS; ++ } ++ else ++ j = LATT_NOSELECT; ++ j += maildir_any_new_msgs(curdir) ? LATT_MARKED : LATT_UNMARKED; ++ if (found) ++ mm_list (stream, '.', pat, j); ++ courier_free_cdir(&cdir); + } -+ } -+ -+ return rv; -+ } -+ -+ void -+ maildir_read_uid(MAILSTREAM *stream, unsigned long *uid_last, -+ unsigned long *uid_validity) -+ { -+ int createuid, deleteuid = 0; -+ char tmp[MAILTMPLEN], *s = NULL; -+ DIR *dir; -+ struct direct *d; -+ -+ if(uid_last) *uid_last = 0L; -+ if(uid_last && uid_validity) *uid_validity = time(0); -+ if(!stream || !LOCAL || !LOCAL->dir || !(dir = opendir(LOCAL->dir))) + return; -+ -+ while ((d = readdir(dir)) != NULL){ -+ if(!strncmp(d->d_name, MDUIDLAST, strlen(MDUIDLAST))) -+ break; ++ } ++ ++ strcpy(tmp,pat + 4); /* a directory */ ++ j = strlen(pat) - 1; ++ maildir_file_path(pat, realpat, sizeof(realpat)); ++ c = pat[j]; ++ pat[j] = '\0'; ++ realname[0] = '\0'; ++ if(dir) ++ maildir_file_path(dir, realname, sizeof(realname)); ++ snprintf(curdir, sizeof(curdir), "%s%s%s/%s", (dir ? "" : myrootdir(pat)), (dir ? "" : "/"), ++ (dir ? realname : maildirpath), (dir ? "" : ".")); ++ snprintf(tmp, sizeof(tmp), "%s%s/.", MDPREFIX(COURIER), dir ? dir : maildirpath); ++ if (level == 0 && tmp && pmatch_full (tmp, realpat, '.')) ++ mm_list (stream,'.', tmp, LATT_NOSELECT); ++ ++ cdir = courier_list_dir(pat); ++ pat[j] = c; ++ for (i = 0; cdir && i < cdir->total; i++) ++ if(pmatch_full (cdir->data[i]->name, pat, '.')){ ++ snprintf(tmp, sizeof(tmp), "%s.", cdir->data[i]->name); ++ courier_list_info(&cdir, tmp, i); ++ mm_list (stream,'.',cdir->data[i]->name, cdir->data[i]->attribute); + } -+ createuid = d == NULL ? 1 : 0; -+ if(uid_last == NULL) -+ deleteuid++; -+ if(d){ -+ if(uid_last){ -+ s = d->d_name + strlen(MDUIDLAST) + 1; -+ *uid_last = strtoul(s, &s, 10); -+ if(!s || *s != '.'){ -+ deleteuid++; -+ createuid++; -+ *uid_last = 0L; -+ } -+ } -+ if(s && *s == '.'){ -+ if(uid_validity){ -+ s++; -+ *uid_validity = strtoul(s, &s, 10); -+ if(s && *s != '\0'){ -+ *uid_validity = time(0); -+ deleteuid++; -+ createuid++; -+ } -+ } -+ } -+ else{ -+ deleteuid++; -+ createuid++; -+ } -+ } -+ if(deleteuid){ -+ snprintf(tmp, sizeof(tmp), "%s/%s", LOCAL->dir, d->d_name); -+ unlink(tmp); -+ } -+ if(createuid) -+ maildir_write_uid(stream, (uid_last ? *uid_last : stream->uid_last), -+ uid_validity ? *uid_validity : time(0)); -+ closedir(dir); ++ courier_free_cdir(&cdir); ++} ++ ++int ++same_maildir_file(char *name1, char *name2) ++{ ++ char tmp1[MAILTMPLEN], tmp2[MAILTMPLEN]; ++ char *s; ++ ++ strcpy(tmp1, name1 ? name1 : ""); ++ strcpy(tmp2, name2 ? name2 : ""); ++ if ((s = strrchr(tmp1, FLAGSEP)) != NULL) ++ *s = '\0'; ++ if (((s = strrchr(tmp1, SIZESEP)) != NULL) && (strchr(s,'.') == NULL)) ++ *s = '\0'; ++ if ((s = strrchr(tmp2, FLAGSEP)) != NULL) ++ *s = '\0'; ++ if (((s = strrchr(tmp2, SIZESEP)) != NULL) && (strchr(s,'.') == NULL)) ++ *s = '\0'; ++ ++ return !strcmp(tmp1, tmp2); ++} ++ ++unsigned long antoul(char *seed) ++{ ++ int i, error = 0; ++ unsigned long val = 0L, rv1 = 0L, t; ++ char c, *p; ++ if(!seed) ++ return 0L; ++ t = strtoul(seed, &p, 10); ++ if(p && (*p == '.' || *p == '_')) ++ return t; ++ /* else */ ++ if((p = strchr(seed,'.')) != NULL) ++ *p = '\0'; ++ error = (strlen(seed) > 6); /* too long */ ++ for(i= strlen(seed)-1; error == 0 && i >= 0; i--){ ++ c = seed[i]; ++ if (c >= 'A' && c <= 'Z') val = c - 'A'; ++ else if (c >= 'a' && c <= 'z') val = c - 'a' + 26; ++ else if (c >= '0' && c <= '9') val = c - '0' + 26 + 26; ++ else if (c == '-') val = c - '-' + 26 + 26 + 10; ++ else if (c == '_') val = c - '_' + 26 + 26 + 10 + 1; ++ else error++; ++ rv1 = val + (rv1 << 6); + } -+ -+ void -+ maildir_write_uid(MAILSTREAM *stream, unsigned long uid_last, -+ unsigned long uid_validity) -+ { -+ char tmp[MAILTMPLEN]; -+ FILE *fp; -+ -+ if(!stream || stream->rdonly || !LOCAL || !LOCAL->dir) -+ return; -+ -+ snprintf(tmp, sizeof(tmp), "%s/%s.%010lu.%010lu", LOCAL->dir, MDUIDLAST, -+ uid_last, uid_validity); -+ if(fp = fopen(tmp, "w")) -+ fclose(fp); -+ } -+ -+ unsigned long -+ maildir_get_uid(char *name) -+ { -+ char *s; -+ unsigned long rv = 0L; -+ -+ if(!name || (s = strstr(name,MDUIDSEP)) == NULL) -+ return rv; -+ -+ s += strlen(MDUIDSEP); -+ rv = strtoul(s, NULL, 10); -+ return rv; -+ } -+ -+ -+ void -+ maildir_delete_uid(MAILSTREAM *stream, unsigned long msgno) -+ { -+ char old[MAILTMPLEN], new[MAILTMPLEN], *s, *t; -+ MESSAGECACHE *elt; -+ -+ elt = mail_elt(stream, msgno); -+ if(!stream || !elt || !elt->private.spare.ptr || !LOCAL || !LOCAL->dir) -+ return; -+ -+ snprintf(old, sizeof(old), "%s/%s/%s", LOCAL->dir, MDNAME(Cur), MDFILE(elt)); -+ t = MDFILE(elt); -+ if(s = strstr(MDFILE(elt), MDUIDSEP)){ -+ *s = '\0'; -+ s += strlen(MDUIDSEP); -+ strtoul(s, &s, 10); -+ snprintf(new, sizeof(new), "%s/%s/%s%s", LOCAL->dir, MDNAME(Cur), t, s); -+ if(rename(old, new) == 0){ -+ maildir_free_file_only ((void **)&elt->private.spare.ptr); -+ s = strrchr(new, '/'); -+ MDFILE(elt) = cpystr(s+1); -+ } -+ elt->private.uid = 0L; -+ } -+ } -+ -+ void -+ maildir_assign_uid(MAILSTREAM *stream, unsigned long msgno, unsigned long uid) -+ { -+ int createuid, deleteuid = 0; -+ char old[MAILTMPLEN], new[MAILTMPLEN], *s, *t; -+ MESSAGECACHE *elt; -+ -+ elt = mail_elt(stream, msgno); -+ if(!stream || !elt || !elt->private.spare.ptr || !LOCAL || !LOCAL->dir) -+ return; -+ -+ maildir_delete_uid(stream, msgno); -+ snprintf(old, sizeof(old), "%s/%s/%s", LOCAL->dir, MDNAME(Cur), MDFILE(elt)); -+ t = MDFILE(elt); -+ if((s = strrchr(MDFILE(elt),FLAGSEP)) != NULL){ -+ *s++ = '\0'; -+ snprintf(new, sizeof(new), "%s/%s/%s%s%lu%c%s", -+ LOCAL->dir, MDNAME(Cur), t, MDUIDSEP, uid, FLAGSEP, s); -+ if(rename(old, new) == 0){ -+ maildir_free_file_only ((void **)&elt->private.spare.ptr); -+ s = strrchr(new, '/'); -+ MDFILE(elt) = cpystr(s+1); -+ stream->uid_validity = time(0); -+ } -+ elt->private.uid = uid; -+ } -+ } -+ -+ void -+ maildir_uid_renew_tempfile(MAILSTREAM *stream) -+ { -+ char tmp[MAILTMPLEN]; -+ -+ if(!stream || stream->rdonly -+ || !LOCAL || !LOCAL->candouid || !LOCAL->dir || !LOCAL->uidtempfile) -+ return; -+ -+ if(mypid == (pid_t) 0) -+ mypid = getpid(); -+ -+ snprintf(tmp, sizeof(tmp), "%s/%s.%d.%lu", LOCAL->dir, MDUIDTEMP, mypid, time(0)); -+ if(rename(LOCAL->uidtempfile, tmp) == 0){ -+ fs_give((void **)&LOCAL->uidtempfile); -+ LOCAL->uidtempfile = cpystr(tmp); -+ } -+ } -diff -rc alpine-2.10/imap/src/osdep/unix/maildir.h alpine-2.10.maildir/imap/src/osdep/unix/maildir.h -*** alpine-2.10/imap/src/osdep/unix/maildir.h 2013-01-11 20:43:06.000000000 -0700 ---- alpine-2.10.maildir/imap/src/osdep/unix/maildir.h 2013-01-11 20:43:06.000000000 -0700 -*************** -*** 0 **** ---- 1,226 ---- -+ /* -+ * A few definitions that try to make this module portable to other -+ * platforms (e.g. Cygwin). This module is based on the information from -+ * http://cr.yp.to/proto/maildir.html -+ */ -+ -+ /* First we deal with the separator character */ -+ #ifndef FLAGSEP -+ #define FLAGSEP ':' -+ #endif -+ #define SIZESEP ',' -+ -+ const char sep1[] = {FLAGSEP, '1', ',', '\0'}; /* experimental semantics*/ -+ const char sep2[] = {FLAGSEP, '2', ',', '\0'}; /* Flags Information */ -+ const char sep3[] = {FLAGSEP, '3', ',', '\0'}; /* Grrrr.... */ -+ -+ const char *sep[] = { sep1, sep2, sep3, NULL}; -+ -+ #define MDSEP(i) sep[((i) - 1)] -+ -+ /* Now we deal with flags. Woohoo! */ -+ typedef enum {Draft, Flagged, Passed, Replied, Seen, Trashed, -+ EmptyFlag, EndFlags} MdFlagNamesType; -+ const int mdimapflags[] = {Draft, Flagged, Replied, Seen, Trashed, EmptyFlag, EndFlags}; -+ const int mdkwdflags[] = {Passed, EmptyFlag, EndFlags}; -+ -+ /* this array lists the codes for mdflgnms (maildir flag names) above */ -+ const char *mdflags[] = { "D", "F", "P", "R", "S", "T", "", NULL}; -+ /* and as characters too */ -+ const char cmdflags[] = { 'D', 'F', 'P', 'R', 'S', 'T', '0', '\0'}; -+ -+ /* MDFLAG(Seen, elt->seen) */ -+ #define MDFLAG(i,j) mdflags[j ? (i) : EmptyFlag] -+ /* MDFLAGC(Seen) */ -+ #define MDFLAGC(i) cmdflags[(i)] -+ -+ /* Now we deal with the directory structure */ -+ typedef enum {Cur, Tmp, New, EndDir} DirNamesType; -+ char *mdstruct[] = {"cur", "tmp", "new", NULL}; -+ #define MDNAME(i) mdstruct[(i)] -+ #define MDFLD(tmp, dir, i) sprintf((tmp),"%s/%s", (dir), mdstruct[(i)]) -+ #define MSGPATH(tmp, dir, msg,i) sprintf((tmp),"%s/%s/%s", (dir), mdstruct[(i)],(msg)) -+ -+ /* Files associated to a maildir directory */ -+ -+ #define MDUIDVALIDITY ".uidvalidity" /* support for old maildirs */ -+ #define MDDIR ".mdir" /* this folder is a directory */ -+ #define MDUIDLAST ".uidlast" /* last assigned uid */ -+ #define MDUIDTEMP ".uidtemp" /* We assign uid's no one else */ -+ -+ -+ -+ /* Support of Courier Structure */ -+ #define CCLIENT 0 -+ #define COURIER 1 -+ #define IS_CCLIENT(t) \ -+ (((t) && (t)[0] == '#' && ((t)[1] == 'm' || (t)[1] == 'M')\ -+ && ((t)[2] == 'd' || (t)[2] == 'D')\ -+ && (t)[3] == '/' && (t)[4] != '\0') ? 1 : 0) -+ -+ #define IS_COURIER(t) \ -+ (((t) && (t)[0] == '#' && ((t)[1] == 'm' || (t)[1] == 'M')\ -+ && ((t)[2] == 'c' || (t)[2] == 'C')\ -+ && (t)[3] == '/' && (t)[4] != '\0') ? 1 : 0) -+ #define MDPREFIX(s) ((s) ? "#mc/" : "#md/") -+ #define MDSEPARATOR(s) ((s) ? '.' : '/') -+ -+ /* UID Support */ -+ -+ #define MAXTEMPUID (unsigned long) 180L -+ const char mduid[] = {',','u','=','\0'}; -+ #define MDUIDSEP mduid -+ -+ -+ /* Now we deal with messages filenames */ -+ char mdlocaldomain[MAILTMPLEN+1] = {'\0'}; -+ pid_t mypid = (pid_t) 0; -+ static char *mdfpath = NULL; -+ static char myMdInboxDir[50] = { '\0' };/* Location of the Maildir INBOX */ -+ static long CourierStyle = CCLIENT; -+ -+ #define CHUNK 16384 /* from unix.h */ -+ -+ typedef struct courier_local { -+ char *name; /* name of directory/folder */ -+ int attribute; /* attributes (children/marked/etc) */ -+ } COURIERLOCAL; -+ -+ typedef struct courier { -+ char *path; /* Path to collection */ -+ time_t scantime; /* time at which information was generated */ -+ int total; /* total number of elements in data */ -+ COURIERLOCAL **data; -+ } COURIER_S; -+ -+ /* In gdb this is the *(struct maildir_local *)stream->local structure */ -+ typedef struct maildir_local { -+ unsigned int dirty : 1; /* diskcopy needs updating */ -+ unsigned int courier : 1; /* It is Courier style file system */ -+ unsigned int link : 1; /* There is a symbolic link */ -+ int candouid; /* we can assign uids and no one else */ -+ char *uidtempfile; /* path to uid temp file */ -+ int fd; /* fd of open message */ -+ char *dir; /* mail directory name */ -+ char **path; /* path to directories cur, new and tmp */ -+ unsigned char *buf; /* temporary buffer */ -+ unsigned long buflen; /* current size of temporary buffer */ -+ time_t scantime; /* last time directory scanned */ -+ } MAILDIRLOCAL; -+ -+ /* Convenient access to local data */ -+ #define LOCAL ((MAILDIRLOCAL *) stream->local) -+ -+ typedef struct maildir_file_info { -+ char *name; /* name of the file */ -+ DirNamesType loc; /* location of this file */ -+ unsigned long pos; /* place in list where this file is listed */ -+ off_t size; /* size in bytes, on disk */ -+ time_t atime; /* last access time */ -+ time_t mtime; /* last modified time */ -+ time_t ctime; /* last changed time */ -+ } MAILDIRFILE; -+ -+ #define MDFILE(F) (((MAILDIRFILE *)((F)->private.spare.ptr))->name) -+ #define MDLOC(F) (((MAILDIRFILE *)((F)->private.spare.ptr))->loc) -+ #define MDPOS(F) (((MAILDIRFILE *)((F)->private.spare.ptr))->pos) -+ #define MDSIZE(F) (((MAILDIRFILE *)((F)->private.spare.ptr))->size) -+ #define MDATIME(F) (((MAILDIRFILE *)((F)->private.spare.ptr))->atime) -+ #define MDMTIME(F) (((MAILDIRFILE *)((F)->private.spare.ptr))->mtime) -+ #define MDCTIME(F) (((MAILDIRFILE *)((F)->private.spare.ptr))->ctime) -+ -+ /* Function prototypes */ -+ -+ DRIVER *maildir_valid (char *name); -+ MAILSTREAM *maildir_open (MAILSTREAM *stream); -+ void maildir_close (MAILSTREAM *stream, long options); -+ long maildir_ping (MAILSTREAM *stream); -+ void maildir_check (MAILSTREAM *stream); -+ long maildir_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags); -+ char *maildir_header (MAILSTREAM *stream,unsigned long msgno, -+ unsigned long *length, long flags); -+ void maildir_list (MAILSTREAM *stream,char *ref,char *pat); -+ void *maildir_parameters (long function,void *value); -+ int maildir_create_folder (char *mailbox); -+ long maildir_create (MAILSTREAM *stream,char *mailbox); -+ void maildir_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt); /*check */ -+ long maildir_expunge (MAILSTREAM *stream, char *sequence, long options); -+ long maildir_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options); -+ long maildir_append (MAILSTREAM *stream,char *mailbox, append_t af, void *data); -+ long maildir_delete (MAILSTREAM *stream,char *mailbox); -+ long maildir_rename (MAILSTREAM *stream,char *old,char *new); -+ long maildir_sub (MAILSTREAM *stream,char *mailbox); -+ long maildir_unsub (MAILSTREAM *stream,char *mailbox); -+ void maildir_lsub (MAILSTREAM *stream,char *ref,char *pat); -+ void courier_list (MAILSTREAM *stream,char *ref, char *pat); -+ -+ /* utility functions */ -+ void courier_realname (char *name, char *realname); -+ long maildir_dirfmttest (char *name); -+ char *maildir_file (char *dst,char *name); -+ int maildir_select (const struct direct *name); -+ int maildir_namesort (const struct direct **d1, const struct direct **d2); -+ unsigned long antoul (char *seed); -+ unsigned long mdfntoul (char *name); -+ int courier_dir_select (const struct direct *name); -+ int courier_dir_sort (const struct direct **d1, const struct direct **d2); -+ long maildir_canonicalize (char *pattern,char *ref,char *pat); -+ void maildir_list_work (MAILSTREAM *stream,char *subdir,char *pat,long level); -+ void courier_list_work (MAILSTREAM *stream,char *subdir,char *pat,long level); -+ int maildir_file_path(char *name, char *tmp, size_t sizeoftmp); -+ int maildir_valid_name (char *name); -+ int maildir_valid_dir (char *name); -+ int is_valid_maildir (char **name); -+ int maildir_message_exists(MAILSTREAM *stream,char *name, char *tmp); -+ char *maildir_remove_root(char *name); -+ char *maildir_text_work (MAILSTREAM *stream,MESSAGECACHE *elt, unsigned long *length,long flags); -+ unsigned long maildir_parse_message(MAILSTREAM *stream, unsigned long msgno, -+ DirNamesType dirtype); -+ int maildir_eliminate_duplicate (char *name, struct direct ***flist, -+ unsigned long *nfiles); -+ int maildir_doscandir (char *name, struct direct ***flist, int flag); -+ unsigned long maildir_scandir (char *name, struct direct ***flist, -+ unsigned long *nfiles, int *scand, int flag); -+ void maildir_parse_folder (MAILSTREAM *stream, int full); -+ void md_domain_name (void); -+ char *myrootdir (char *name); -+ char *mdirpath (void); -+ int maildir_initial_check (MAILSTREAM *stream, DirNamesType dirtype); -+ unsigned long maildir_parse_dir(MAILSTREAM *stream, unsigned long nmsgs, -+ DirNamesType dirtype, struct direct **names, unsigned long nfiles, int full); -+ int same_maildir_file(char *name1, char *name2); -+ int comp_maildir_file(char *name1, char *name2); -+ int maildir_message_in_list(char *msgname, struct direct **names, -+ unsigned long bottom, unsigned long top, unsigned long *pos); -+ void maildir_getflag(char *name, int *d, int *f, int *r ,int *s, int *t); -+ int maildir_update_elt_maildirp(MAILSTREAM *stream, unsigned long msgno); -+ void maildir_abort (MAILSTREAM *stream); -+ int maildir_contains_folder(char *dirname, char *name); -+ int maildir_is_dir(char *dirname, char *name); -+ int maildir_dir_is_empty(char *mailbox); -+ int maildir_create_work (char *mailbox, int loop); -+ void maildir_get_file (MAILDIRFILE **mdfile); -+ void maildir_free_file (void **mdfile); -+ void maildir_free_file_only (void **mdfile); -+ int maildir_any_new_msgs(char *mailbox); -+ void maildir_get_date(MAILSTREAM *stream, unsigned long msgno); -+ void maildir_fast (MAILSTREAM *stream,char *sequence,long flags); -+ -+ /* Courier server support */ -+ void courier_free_cdir (COURIER_S **cdir); -+ COURIER_S *courier_get_cdir (int total); -+ int courier_search_list(COURIERLOCAL **data, char *name, int first, int last); -+ COURIER_S *courier_list_dir(char *curdir); -+ void courier_list_info(COURIER_S **cdirp, char *data, int i); -+ -+ /* UID Support */ -+ int maildir_can_assign_uid (MAILSTREAM *stream); -+ void maildir_read_uid(MAILSTREAM *stream, unsigned long *uid_last, -+ unsigned long *uid_validity); -+ void maildir_write_uid(MAILSTREAM *stream, unsigned long uid_last, -+ unsigned long uid_validity); -+ unsigned long maildir_get_uid(char *name); -+ void maildir_delete_uid(MAILSTREAM *stream, unsigned long msgno); -+ void maildir_assign_uid(MAILSTREAM *stream, unsigned long msgno, unsigned long uid); -+ void maildir_uid_renew_tempfile(MAILSTREAM *stream); -+ -diff -rc alpine-2.10/imap/src/osdep/unix/Makefile alpine-2.10.maildir/imap/src/osdep/unix/Makefile -*** alpine-2.10/imap/src/osdep/unix/Makefile 2011-01-09 21:00:19.000000000 -0700 ---- alpine-2.10.maildir/imap/src/osdep/unix/Makefile 2013-01-11 20:43:06.000000000 -0700 -*************** -*** 144,150 **** - # However, mh needs to be before any sysinbox formats (such as mmdf or unix) - # since otherwise INBOX won't work correctly when mh_allow_inbox is set. - # -! DEFAULTDRIVERS=imap nntp pop3 mix mx mbx tenex mtx mh mmdf unix news phile - CHUNKSIZE=65536 - - # Normally no need to change any of these ---- 144,150 ---- - # However, mh needs to be before any sysinbox formats (such as mmdf or unix) - # since otherwise INBOX won't work correctly when mh_allow_inbox is set. - # -! DEFAULTDRIVERS=maildir courier imap nntp pop3 mix mx mbx tenex mtx mh mmdf unix news phile - CHUNKSIZE=65536 - - # Normally no need to change any of these -*************** -*** 153,159 **** - BINARIES=osdep.o mail.o misc.o newsrc.o smanager.o utf8.o utf8aux.o siglocal.o \ - dummy.o pseudo.o netmsg.o flstring.o fdstring.o \ - rfc822.o nntp.o smtp.o imap4r1.o pop3.o \ -! unix.o mbx.o mmdf.o tenex.o mtx.o news.o phile.o mh.o mx.o mix.o - CFLAGS=-g - - CAT=cat ---- 153,159 ---- - BINARIES=osdep.o mail.o misc.o newsrc.o smanager.o utf8.o utf8aux.o siglocal.o \ - dummy.o pseudo.o netmsg.o flstring.o fdstring.o \ - rfc822.o nntp.o smtp.o imap4r1.o pop3.o \ -! unix.o mbx.o mmdf.o tenex.o mtx.o news.o phile.o mh.o mx.o mix.o maildir.o - CFLAGS=-g - - CAT=cat -*************** -*** 290,296 **** - - cyg: # Cygwin - note that most local file drivers don't work!! - $(BUILD) `$(CAT) SPECIALS` OS=$@ \ -! DEFAULTDRIVERS="imap nntp pop3 mbx unix phile" \ - SIGTYPE=psx CHECKPW=cyg LOGINPW=cyg CRXTYPE=std \ - SPOOLDIR=/var \ - ACTIVEFILE=/usr/local/news/lib/active \ ---- 290,296 ---- - - cyg: # Cygwin - note that most local file drivers don't work!! - $(BUILD) `$(CAT) SPECIALS` OS=$@ \ -! DEFAULTDRIVERS="imap nntp pop3 mbx unix maildir phile" \ - SIGTYPE=psx CHECKPW=cyg LOGINPW=cyg CRXTYPE=std \ - SPOOLDIR=/var \ - ACTIVEFILE=/usr/local/news/lib/active \ -*************** -*** 900,906 **** - unix.o: mail.h misc.h osdep.h unix.h pseudo.h dummy.h - utf8.o: mail.h misc.h osdep.h utf8.h tmap.c widths.c - utf8aux.o: mail.h misc.h osdep.h utf8.h -! - - # OS-dependent - ---- 900,906 ---- - unix.o: mail.h misc.h osdep.h unix.h pseudo.h dummy.h - utf8.o: mail.h misc.h osdep.h utf8.h tmap.c widths.c - utf8aux.o: mail.h misc.h osdep.h utf8.h -! maildir.o: mail.h misc.h osdep.h maildir.h dummy.h - - # OS-dependent - -diff -rc alpine-2.10/imap/src/osdep/unix/os_cyg.h alpine-2.10.maildir/imap/src/osdep/unix/os_cyg.h -*** alpine-2.10/imap/src/osdep/unix/os_cyg.h 2011-01-09 21:00:19.000000000 -0700 ---- alpine-2.10.maildir/imap/src/osdep/unix/os_cyg.h 2013-01-11 20:43:06.000000000 -0700 -*************** -*** 47,52 **** ---- 47,53 ---- - #define setpgrp setpgid - - #define SYSTEMUID 18 /* Cygwin returns this for SYSTEM */ -+ #define FLAGSEP ';' - #define geteuid Geteuid - uid_t Geteuid (void); - -diff -rc alpine-2.10/pith/conf.c alpine-2.10.maildir/pith/conf.c -*** alpine-2.10/pith/conf.c 2013-01-11 19:45:41.000000000 -0700 ---- alpine-2.10.maildir/pith/conf.c 2013-01-11 20:43:06.000000000 -0700 -*************** -*** 428,433 **** ---- 428,436 ---- - - CONF_TXT_T cf_text_newsrc_path[] = "Full path and name of NEWSRC file"; - -+ #ifndef _WINDOWS -+ CONF_TXT_T cf_text_maildir_location[] = "Location relative to your HOME directory of the directory where your INBOX\n# for the maildir format is located. Default value is \"Maildir\". If your\n# inbox is located at \"~/Maildir\" you do not need to change this value.\n# A common value is also \".maildir\""; -+ #endif - - /*---------------------------------------------------------------------- - These are the variables that control a number of pine functions. They -*************** -*** 628,633 **** ---- 631,640 ---- - NULL, cf_text_news_active}, - {"news-spool-directory", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, - NULL, cf_text_news_spooldir}, -+ #ifndef _WINDOWS -+ {"maildir-location", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, -+ "Maildir Location", cf_text_maildir_location}, -+ #endif - {"upload-command", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, - NULL, cf_text_upload_cmd}, - {"upload-command-prefix", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, -*************** -*** 2245,2250 **** ---- 2252,2263 ---- - mail_parameters(NULL, SET_NEWSSPOOL, - (void *)VAR_NEWS_SPOOL_DIR); - -+ #ifndef _WINDOWS -+ set_current_val(&vars[V_MAILDIR_LOCATION], TRUE, TRUE); -+ if(VAR_MAILDIR_LOCATION && VAR_MAILDIR_LOCATION[0]) -+ mail_parameters(NULL, SET_MDINBOXPATH, (void *)VAR_MAILDIR_LOCATION); -+ #endif -+ - /* guarantee a save default */ - set_current_val(&vars[V_DEFAULT_SAVE_FOLDER], TRUE, TRUE); - if(!VAR_DEFAULT_SAVE_FOLDER || !VAR_DEFAULT_SAVE_FOLDER[0]) -*************** -*** 2870,2875 **** ---- 2883,2892 ---- - F_SORT_DEFAULT_SAVE_ALPHA, h_config_sort_save_alpha, PREF_FLDR, 0}, - {"vertical-folder-list", "Use Vertical Folder List", - F_VERTICAL_FOLDER_LIST, h_config_vertical_list, PREF_FLDR, 0}, -+ #ifndef _WINDOWS -+ {"use-courier-folder-list", "Courier Style Folder List", -+ F_COURIER_FOLDER_LIST, h_config_courier_list, PREF_FLDR, 0}, -+ #endif - - /* Addr book */ - {"combined-addrbook-display", "Combined Address Book Display", -*************** -*** 6940,6945 **** ---- 6957,6968 ---- - - break; - -+ #ifndef _WINDOWS -+ case F_COURIER_FOLDER_LIST: -+ mail_parameters(NULL,SET_COURIERSTYLE,(void *)(F_ON(f->id ,ps)? 1 : 0)); -+ break; /* COURIER == 1, CCLIENT == 0, see maildir.h */ -+ #endif -+ - case F_COLOR_LINE_IMPORTANT : - case F_DATES_TO_LOCAL : - clear_index_cache(ps->mail_stream, 0); -*************** -*** 7721,7726 **** ---- 7744,7753 ---- - return(h_config_newmailwidth); - case V_NEWSRC_PATH : - return(h_config_newsrc_path); -+ #ifndef _WINDOWS -+ case V_MAILDIR_LOCATION : -+ return(h_config_maildir_location); -+ #endif - case V_BROWSER : - return(h_config_browser); - #if defined(DOS) || defined(OS2) -diff -rc alpine-2.10/pith/conf.h alpine-2.10.maildir/pith/conf.h -*** alpine-2.10/pith/conf.h 2013-01-11 11:26:44.000000000 -0700 ---- alpine-2.10.maildir/pith/conf.h 2013-01-11 20:43:06.000000000 -0700 -*************** -*** 250,255 **** ---- 250,259 ---- - #define GLO_NEWS_ACTIVE_PATH vars[V_NEWS_ACTIVE_PATH].global_val.p - #define VAR_NEWS_SPOOL_DIR vars[V_NEWS_SPOOL_DIR].current_val.p - #define GLO_NEWS_SPOOL_DIR vars[V_NEWS_SPOOL_DIR].global_val.p -+ #ifndef _WINDOWS -+ #define VAR_MAILDIR_LOCATION vars[V_MAILDIR_LOCATION].current_val.p -+ #define GLO_MAILDIR_LOCATION vars[V_MAILDIR_LOCATION].global_val.p -+ #endif - #define VAR_DISABLE_DRIVERS vars[V_DISABLE_DRIVERS].current_val.l - #define VAR_DISABLE_AUTHS vars[V_DISABLE_AUTHS].current_val.l - #define VAR_REMOTE_ABOOK_METADATA vars[V_REMOTE_ABOOK_METADATA].current_val.p -diff -rc alpine-2.10/pith/conftype.h alpine-2.10.maildir/pith/conftype.h -*** alpine-2.10/pith/conftype.h 2013-01-11 19:45:41.000000000 -0700 ---- alpine-2.10.maildir/pith/conftype.h 2013-01-11 20:43:06.000000000 -0700 -*************** -*** 115,120 **** ---- 115,123 ---- - , V_NEWSRC_PATH - , V_NEWS_ACTIVE_PATH - , V_NEWS_SPOOL_DIR -+ #ifndef _WINDOWS -+ , V_MAILDIR_LOCATION -+ #endif - , V_UPLOAD_CMD - , V_UPLOAD_CMD_PREFIX - , V_DOWNLOAD_CMD -*************** -*** 381,386 **** ---- 384,392 ---- - F_PASS_C1_CONTROL_CHARS, - F_SINGLE_FOLDER_LIST, - F_VERTICAL_FOLDER_LIST, -+ #ifndef _WINDOWS -+ F_COURIER_FOLDER_LIST, -+ #endif - F_TAB_CHK_RECENT, - F_AUTO_REPLY_TO, - F_VERBOSE_POST, -diff -rc alpine-2.10/pith/init.c alpine-2.10.maildir/pith/init.c -*** alpine-2.10/pith/init.c 2013-01-11 11:26:43.000000000 -0700 ---- alpine-2.10.maildir/pith/init.c 2013-01-11 20:43:06.000000000 -0700 -*************** -*** 408,413 **** ---- 408,416 ---- - && stricmp(filename, folder_base)){ - #else - if(strncmp(filename, folder_base, folder_base_len) == 0 -+ #ifndef _WINDOWS -+ && filename[folder_base_len] != list_cntxt->dir->delim -+ #endif - && strcmp(filename, folder_base)){ - #endif - #endif -diff -rc alpine-2.10/pith/pattern.c alpine-2.10.maildir/pith/pattern.c -*** alpine-2.10/pith/pattern.c 2013-01-11 11:26:44.000000000 -0700 ---- alpine-2.10.maildir/pith/pattern.c 2013-01-11 20:43:06.000000000 -0700 -*************** -*** 5483,5488 **** ---- 5483,5497 ---- - break; - - case '#': -+ #ifndef _WINDOWS -+ if(!struncmp(patfolder, "#md/", 4) -+ || !struncmp(patfolder, "#mc/", 4)){ -+ maildir_file_path(patfolder, tmp1, sizeof(tmp1)); -+ if(!strcmp(patfolder, stream->mailbox)) -+ match++; -+ break; -+ } -+ #endif - if(!strcmp(patfolder, stream->mailbox)) - match++; - -*************** -*** 7903,7909 **** - int we_cancel = 0, width; - CONTEXT_S *save_context = NULL; - char buf[MAX_SCREEN_COLS+1], sbuf[MAX_SCREEN_COLS+1]; -! char *save_ref = NULL; - #define FILTMSG_MAX 30 - - if(!stream) ---- 7912,7918 ---- - int we_cancel = 0, width; - CONTEXT_S *save_context = NULL; - char buf[MAX_SCREEN_COLS+1], sbuf[MAX_SCREEN_COLS+1]; -! char *save_ref = NULL, *save_dstfldr = NULL, *save_dstfldr2 = NULL; - #define FILTMSG_MAX 30 - - if(!stream) -*************** -*** 7937,7942 **** ---- 7946,7961 ---- - if(F_OFF(F_QUELL_FILTER_MSGS, ps_global)) - we_cancel = busy_cue(buf, NULL, 0); - -+ #ifndef _WINDOWS -+ if(!struncmp(dstfldr, "#md/", 4) || !struncmp(dstfldr, "#mc/", 4)){ -+ char tmp1[MAILTMPLEN]; -+ maildir_file_path(dstfldr, tmp1, sizeof(tmp1)); -+ save_dstfldr2 = dstfldr; -+ save_dstfldr = cpystr(tmp1); -+ dstfldr = save_dstfldr; ++ if(p) ++ *p = '.'; ++ return error ? 0L : rv1; ++} ++ ++unsigned long mdfntoul (char *name) ++{ ++ unsigned long t; ++ char *r, last; ++ ++ if((*name == '_') && ((r = strpbrk(name,".,%+")) != NULL)){ /* Grrr!!! */ ++ last = *r; ++ *r = '\0'; ++ t = antoul(r+1); ++ *r = last; ++ } ++ else ++ t = antoul(name); ++ return t; ++} ++ ++int comp_maildir_file(char *name1, char *name2) ++{ ++ int uset1 = 1, uset2 = 1, i, j, cmp; ++ unsigned long t1, t2; ++ char *s1, *s2; ++ ++ if (!(name1 && *name1)) ++ return (name2 && *name2) ? (*name2 == FLAGSEP ? 0 : -1) : 0; ++ ++ if (!(name2 && *name2)) ++ return (name1 && *name1) ? (*name1 == FLAGSEP ? 0 : 1) : 0; ++ ++ if((cmp = strcmp(name1,name2)) == 0) ++ return 0; ++ ++ t1 = strtoul(name1, &s1, 10); ++ t2 = strtoul(name2, &s2, 10); ++ ++ if(!s1 || *s1 != '.') ++ uset1 = 0; ++ ++ if(!s2 || *s2 != '.') ++ uset2 = 0; ++ ++ if(uset1 && uset2) /* normal sort order */ ++ return (t1 < t2) ? -1 : (t1 > t2 ? 1 : (cmp < 0 ? -1 : 1)); ++ ++ /* If we make it here we say Grrrr.... first, then we try to figure out ++ * how to sort this mess. ++ * These are the rules. ++ * If there is a number at the beginning it is bigger than anything else. ++ * If there are digits, then the number of digits decides which one is bigger. ++ */ ++ ++ for(i = 0; isdigit(name1[i]); i++); ++ for(j = 0; isdigit(name2[j]); j++); ++ ++ return(uset1 ? 1 ++ : (uset2 ? -1 ++ : (i < j ? -1 : (i > j ? 1 : (cmp < 0 ? -1 : 1))))); ++} ++ ++void ++maildir_getflag(char *name, int *d, int *f, int *r ,int *s, int *t) ++{ ++ char tmp[MAILTMPLEN], *b; ++ int offset = 0; ++ int tmpd, tmpf, tmpr, tmps, tmpt; ++ ++ if(d) *d = 0; ++ if(f) *f = 0; ++ if(r) *r = 0; ++ if(s) *s = 0; ++ if(t) *t = 0; ++ ++ tmpd = tmpf = tmpr = tmps = tmpt = NIL; /* no flags set by default */ ++ strcpy(tmp,name); ++ while ((b = strrchr(tmp+offset, FLAGSEP)) != NULL){ ++ char flag,last; ++ int k; ++ if (!++b) break; ++ switch (*b){ ++ case '1': ++ case '2': ++ case '3': flag = *b; b += 2; ++ for (k = 0; b[k] && b[k] != FLAGSEP && b[k] != ','; k++); ++ last = b[k]; ++ b[k] = '\0'; ++ if (flag == '2' || flag == '3'){ ++ tmpd = strchr (b, MDFLAGC(Draft)) ? T : NIL; ++ tmpf = strchr (b, MDFLAGC(Flagged)) ? T : NIL; ++ tmpr = strchr (b, MDFLAGC(Replied)) ? T : NIL; ++ tmps = strchr (b, MDFLAGC(Seen)) ? T : NIL; ++ tmpt = strchr (b, MDFLAGC(Trashed)) ? T : NIL; ++ } ++ b[k] = last; ++ b += k; ++ for (; tmp[offset] && tmp[offset] != FLAGSEP; offset++); ++ offset++; ++ break; ++ default: break; /* Should we crash?... Nahhh */ + } -+ #endif -+ - if(!is_absolute_path(dstfldr) - && !(save_context = default_save_context(ps_global->context_list))) - save_context = ps_global->context_list; -*************** -*** 8000,8005 **** ---- 8019,8029 ---- - if(we_cancel) - cancel_busy_cue(buf[0] ? 0 : -1); - -+ if(save_dstfldr){ -+ fs_give((void **)&save_dstfldr); -+ dstfldr = save_dstfldr2; ++ } ++ if(d) *d = tmpd; ++ if(f) *f = tmpf; ++ if(r) *r = tmpr; ++ if(s) *s = tmps; ++ if(t) *t = tmpt; ++} ++ ++int ++maildir_message_in_list(char *msgname, struct direct **names, ++ unsigned long bottom, unsigned long top, unsigned long *pos) ++{ ++ unsigned long middle = (bottom + top)/2; ++ int test; ++ ++ if (!msgname) ++ return NIL; ++ ++ if (pos) *pos = middle; ++ ++ if (same_maildir_file(msgname, names[middle]->d_name)) ++ return T; ++ ++ if (middle == bottom){ /* 0 <= 0 < 1 */ ++ int rv = NIL; ++ if (same_maildir_file(msgname, names[middle]->d_name)){ ++ rv = T; ++ if (pos) *pos = middle; + } -+ - return(buf[0] != '\0'); - } - -diff -rc alpine-2.10/pith/pine.hlp alpine-2.10.maildir/pith/pine.hlp -*** alpine-2.10/pith/pine.hlp 2013-01-11 20:33:27.000000000 -0700 ---- alpine-2.10.maildir/pith/pine.hlp 2013-01-11 20:43:06.000000000 -0700 -*************** -*** 21449,21454 **** ---- 21449,21550 ---- - <End of help on this topic> - - -+ ====== h_config_maildir_location ====== -+ -+ -+ OPTION: <!--#echo var="VAR_maildir-location"--> -+ -+ -+

    OPTION:

    -+ -+

    -+ This option should be used only if you have a Maildir folder which you -+ want to use as your INBOX. If this is not your case (or don't know what -+ this is), you can safely ignore this option. -+ -+

    -+ This option overrides the default directory Pine uses to find the location of -+ your INBOX, in case this is in Maildir format. The default value of this -+ option is "Maildir", but in some systems, this directory could have been -+ renamed (e.g. to ".maildir"). If this is your case use this option to change -+ the default. -+ -+

    -+ The value of this option is prefixed with the "~/" string to determine the -+ full path to your INBOX. -+ -+

    -+ You should probably read a few tips that -+ teach you how to configure your maildir for optimal performance. This -+ version also has support for the -+ Courier style file system when a maildir collection is accessed locally. -+ -+

    -+

    -+ <End of help on this topic> -+ -+ -+ ====== h_config_maildir ===== -+ -+ -+ Maildir Support -+ -+ -+

    Maildir Support

    -+ -+ This version of Alpine has been enhanced with Maildir support. This text is -+ intended to be a reference on its support. -+

    -+ -+ A Maildir folder is a directory that contains three directories called -+ cur, tmp and new. A program that delivers mail (e.g. postfix) will put new -+ mail in the new directory. A program that reads mail will look for for old -+ messages in the cur directory, while it will look for new mail in the new -+ directory. -+

    -+ -+ In order to use maildir support it is better to set your inbox-path to the -+ value "#md/inbox" (without quotes). This assumes that your mail -+ delivery agent is delivering new mail to ~/Maildir/new. If the directory -+ where new mail is being delivered is not called "Maildir", you can set the -+ name of the subdirectory of home where it is being delivered in the configuration -+ variable. Most of the time you will not have to worry about the -+ variable, because it will probably be set by your -+ administrator in the pine.conf configuration file. -+

    -+ -+ One of the advantages of the Maildir support of this version of Alpine is -+ that you do not have to stop using folders in another styles (mbox, mbx, -+ etc.). This is desirable since the usage of a specific mail storage system -+ is a personal decision. Folders in the maildir format that are part of the -+ Mail collection will be recognized without any extra configuration of your -+ part. If your mail/ collection is located under the mail/ directory, then -+ creating a new maildir folder in this collection is done by pressing "A" -+ and entering the string "#driver.md/mail/newfolder". Observe that adding a -+ new folder as "newfolder" may not create such folder in maildir format. -+ -+

    -+ If you would like to have all folders created in the maildir format by -+ default, you do so by adding a Maildir Collection. In order to convert -+ your current mail/ collection into a maildir collection, edit the -+ collection and change the path variable from "mail/" to -+ "#md/mail". In a maildir collection folders of any other format -+ are ignored. -+ -+

    Finally, This version also has -+ support for the Courier style file system -+ when a maildir collection is accessed locally. -+ -+

    -+

    -+ <End of help on this topic> -+ -+ - ====== h_config_literal_sig ===== - - -*************** -*** 29356,29361 **** ---- 29452,29500 ---- -

    - <End of help on this topic> - -+ -+ ====== h_config_courier_list ===== -+ -+ -+ FEATURE: <!--#echo var="FEAT_courier-folder-list"--> -+ -+ -+

    FEATURE:

    -+ -+ In a maildir collection, a folder could be used as a directory to store -+ folders. In the Courier server if you create a folder, then a directory -+ with the same name is created. If you use this patch to access a -+ collection created by the Courier server, then the display of such -+ collection will look confusing. The best way to access a maildir -+ collection created by the Courier server is by using the "#mc/" -+ prefix instead of the "#md/" prefix. If you use this alternate -+ prefix, then this feature applies to you, otherwise you can safely ignore -+ the text that follows. -+

    -+ Depending on if you have enabled the option -+ -+ a folder may be listed as "folder[.]", or as two entries in the -+ list by "folder" and "folder.". -+

    -+ If this option is disabled, Pine will list local folders that are in Courier -+ style format, as "folder", and those that are also directories as -+ "folder[.]". This makes the default display cleaner. -+

    -+ If this feature is enabled then creating folders in a maildir collection -+ will create a directory with the same name. If this feature is disabled, then -+ a folder is considered a directory only if it contains subfolders, so you can -+ not create a directory with the same name as an exisiting folder unless -+ you create a subfolder of that folder first (e.g. if you have a folder -+ called "foo" simply add "foo.bar" directly. This will -+ create the directory "foo" and the subfolder "bar" of it). -+

    -+ Observe that this feature works only for maildir collections that are accessed -+ locally. If a collection is accessed remotely then this feature has no value, -+ as the report is created in a server, and Pine only reports what received -+ from the server in this case. -+

    -+ <End of help on this topic> -+ - - ====== h_config_verbose_post ===== - -diff -rc alpine-2.10/pith/send.c alpine-2.10.maildir/pith/send.c -*** alpine-2.10/pith/send.c 2013-01-11 11:26:44.000000000 -0700 ---- alpine-2.10.maildir/pith/send.c 2013-01-11 20:43:06.000000000 -0700 -*************** -*** 257,262 **** ---- 257,269 ---- - - if(exists & FEX_ISFILE){ - context_apply(tmp, p_cntxt, mbox, sizeof(tmp)); -+ #ifndef _WINDOWS -+ if (!struncmp(tmp, "#md/",4) || !struncmp(tmp, "#mc/", 4)){ -+ char tmp2[MAILTMPLEN]; -+ maildir_file_path(tmp, tmp2, sizeof(tmp2)); -+ strcpy(tmp, tmp2); -+ } -+ #endif - if(!(IS_REMOTE(tmp) || is_absolute_path(tmp))){ - /* - * The mbox is relative to the home directory. -diff -rc alpine-2.10/README.maildir alpine-2.10.maildir/README.maildir -*** alpine-2.10/README.maildir 2013-01-11 20:43:06.000000000 -0700 ---- alpine-2.10.maildir/README.maildir 2013-01-11 20:43:06.000000000 -0700 -*************** -*** 0 **** ---- 1,149 ---- -+ --------------------------------------- -+ -+ Maildir Driver for Alpine 2.0 -+ By Eduardo Chappa -+ -+ -+ --------------------------------------- -+ 1. General Information About This Patch -+ --------------------------------------- -+ -+ This patch adds support for the maildir format to Alpine. We take the -+ approach that this patch is one more driver among the number of formats -+ supported by Alpine (more generally c-client). This approach differs from -+ older versions of similar patches, in that once a maildir patch was -+ applied, it was assumed that all your folders would be created in the -+ maildir format. -+ -+ This patch does not assume that maildir is a preferred format, instead -+ puts maildir in equal footing with other formats (mbox, mbx, mix, etc), -+ and so a maildir folder in the mail/ collection is treated in the same way -+ as any other folder in any other format. In other words, just by reading -+ the name of a folder, or opening it, or doing any operation with it, you -+ can not know in which format the folder is. -+ -+ This implies that if you want to add a folder in the maildir format to the -+ mail/ collection, then you must add by pressing "A" in the folder list -+ collection and enter "#driver.md/mail/name_maildir_folder". -+ -+ If you only want to use maildir, however, you can do so too. In this case, -+ you must create a maildir collection. In that collection, only maildir -+ folders will be listed. If there is any folder in any other format, that -+ folder will be ignored. In another words, any folder listed there is in -+ maildir format and can be accessed through that collection, conversely, -+ any folder not listed there is not in maildir format and there is no way -+ to access it using this collection. -+ -+ In order to create a maildir collection, you could press M S L, and "A" to -+ add a collection. Fill in the required fields as follows: -+ -+ Nickname : Anything -+ Server : -+ Path : #md/relative/path/to/maildir/collection/ -+ View : -+ -+ For example, if "path" is set to "#md/mail/", then Alpine will look for your -+ maildir folders that are in ~/mail/. -+ -+ The code in this patch is mostly based in code for the unix driver plus -+ some combinations of the mh, mbx and nntp drivers for the c-client -+ library. Those drivers were designed by Mark Crispin, and bugs in this -+ code are not his bugs, but my own. -+ -+ I got all the specification for this patch from -+ http://cr.yp.to/proto/maildir.html. If you know of a place with a better -+ specification for maildir format please let me know. The method this patch -+ uses to create a unique filename for a message is one of the "old -+ fashioned" methods. I realize that this is old fashioned, but it is -+ portable, and portability is the main reason why I decided to use an old -+ fashioned method (most methods are not portable. See the word -+ "Unfortunately" in that document). -+ -+ -------------- -+ 2. Other Goals -+ -------------- -+ -+ It is intended that this code will work well with any application -+ written using the c-client library. Of paramount importance is to make the -+ associated imap server work well when the server accesses a folder in -+ Maildir format. The program mailutil should also work flawlessly with this -+ implemetation of the driver. -+ -+ It is intended that this driver be fast and stable. We intend not to -+ patch Alpine to make this driver do its work, unless such patching is for -+ fixing bugs in Alpine or to pass parameters to the driver. -+ -+ ------------------------------------------------------------------------ -+ 3. What are the known bugs of this implementation of the Maildir driver? -+ ------------------------------------------------------------------------ -+ -+ I don't know any at this time. There have been bugs before, though, but -+ I try to fix bugs as soon as they are reported. -+ -+ ---------- -+ 4. On UIDs -+ ---------- -+ -+ This patch keeps uids in the name of the file that contains the message, -+ by adding a ",u=" string to the file name to save the uid of a message. A -+ file is kept between sessions to save information on the last uid assigned -+ and its time of validity. Only one session with writing access can write -+ uids, all others must wait for the other session to assign them. The -+ session assigning uids creates a ".uidtemp" file which other sessions must -+ not disturb. -+ -+ Uid support appeared in Alpine 1.00 (snapshot 925), and is experimental, -+ please report any problems. -+ -+ ---------------------------------------------- -+ 5. Configuring Alpine and Setting up a Maildir -+ ---------------------------------------------- -+ -+ Once this approach was chosen, it implied the following: -+ -+ * This patch assumes that your INBOX is located at "$HOME/Maildir". -+ This is a directory which should have three subdirectories "cur", -+ "tmp" and "new". Mail is delivered to 'new' and read from 'cur'. I -+ have added a configuration option "maildir-location" which can be -+ used to tell Alpine where your Maildir inbox is, in case your system -+ does not use the above directory (e.g. your system may use -+ "~/.maildir"). In this case define that variable to be the name of -+ the directory where your e-mail is being delivered (e.g. -+ ".maildir"). -+ -+ * If you want to use the above configuration as your inbox, you must -+ define your inbox-path as "#md/inbox" (no quotes). You can define -+ the inbox-path like above even if you have changed the -+ maildir-location variable. That's the whole point of that variable. -+ -+ ------------------------------------------- -+ 6. What about Courier/Dovecot file systems? -+ ------------------------------------------- -+ -+ In a courier file system all folders are subfolders of a root folder -+ called INBOX. Normally INBOX is located at ~/Maildir and subfolders are -+ "dot" directories in ~/Maildir. For example ~/Maildir/.Trash is a -+ subfolder of INBOX and is accessed with the nickname "INBOX.Trash". -+ -+ You can not access folders in this way unless you preceed them with the -+ string "#mc/". The purpose of the string "#mc/" is to warn Alpine that a -+ collection in the Courier format is going to be accessed. Therefore, you -+ can SELECT a folder like "#mc/INBOX.Trash", but not "INBOX.Trash" -+ -+ You can access a collection through a server, but if you want to access a -+ collection of folders created using the Courier server, you MUST edit your -+ ".pinerc" file and enter the definition of the collection as follows: -+ -+ folder-collections="Anything you want" #mc/INBOX.[] -+ -+ You can replace the string "#mc/INBOX." by something different, for example -+ "#mc/Courier/." will make Alpine search for your collection in ~/Courier. -+ -+ You can not add this setting directly into Alpine because Alpine fails to -+ accept this value from its input, but it takes it correctly when it is -+ added through the ".pinerc" file. -+ -+ You can access your inbox as "#mc/INBOX" or "#md/INBOX". Both definitions -+ point to the same place. -+ -+ Last Updated May 28, 2011 ++ else ++ if (same_maildir_file(msgname, names[top]->d_name)){ ++ rv = T; ++ if (pos) *pos = top; ++ } ++ return rv; ++ } ++ ++ test = comp_maildir_file(msgname, names[middle]->d_name); ++ ++ if (top <= bottom) ++ return test ? NIL : T; ++ ++ if (test < 0 ) /* bottom < msgname < middle */ ++ return maildir_message_in_list(msgname, names, bottom, middle, pos); ++ else if (test > 0) /* middle < msgname < top */ ++ return maildir_message_in_list(msgname, names, middle, top, pos); ++ else return T; ++} ++ ++void ++maildir_abort(MAILSTREAM *stream) ++{ ++ if (LOCAL){ ++ DirNamesType i; ++ ++ if(LOCAL->candouid) ++ maildir_read_uid(stream, NULL, &stream->uid_validity); ++ if (LOCAL->dir) fs_give ((void **) &LOCAL->dir); ++ for (i = Cur; i < EndDir; i++) ++ if(LOCAL->path[i]) fs_give ((void **) &LOCAL->path[i]); ++ fs_give ((void **) &LOCAL->path); ++ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf); ++ if(LOCAL->uidtempfile){ ++ unlink(LOCAL->uidtempfile); ++ fs_give ((void **) &LOCAL->uidtempfile); ++ } ++ fs_give ((void **) &stream->local); ++ } ++ if (mdfpath) fs_give((void **)&mdfpath); ++ stream->dtb = NIL; ++} ++ ++int ++maildir_contains_folder(char *dirname, char *name) ++{ ++ char tmp[MAILTMPLEN], tmp2[MAILTMPLEN]; ++ int rv = 0; ++ DIR *dir; ++ struct direct *d; ++ ++ maildir_file_path(dirname, tmp2, sizeof(tmp2)); ++ if(name){ ++ strcat(tmp2,"/"); ++ strcat(tmp2, name); ++ } ++ ++ if (!(dir = opendir (tmp2))) ++ return NIL; ++ ++ while ((d = readdir(dir)) != NULL){ ++ if (strcmp(d->d_name, ".") && strcmp(d->d_name,"..") ++ && strcmp(d->d_name, MDNAME(Cur)) ++ && strcmp(d->d_name, MDNAME(Tmp)) ++ && strcmp(d->d_name, MDNAME(New))){ ++ ++ snprintf(tmp, sizeof(tmp), "%s/%s", tmp2, d->d_name); ++ if(maildir_valid(tmp)){ ++ rv++; ++ break; ++ } ++ } ++ } ++ closedir(dir); ++ return rv; ++} ++ ++int ++maildir_is_dir(char *dirname, char *name) ++{ ++ char tmp[MAILTMPLEN]; ++ struct stat sbuf; ++ ++ maildir_file_path(dirname, tmp, sizeof(tmp)); ++ if(name){ ++ strcat(tmp, "/"); ++ strcat(tmp, name); ++ } ++ strcat(tmp, "/"); ++ strcat(tmp, MDDIR); ++ ++ return ((stat(tmp, &sbuf) == 0) && S_ISREG (sbuf.st_mode)) ? 1 : 0; ++} ++ ++int ++maildir_dir_is_empty(char *mailbox) ++{ ++ char tmp[MAILTMPLEN], tmp2[MAILTMPLEN], tmp3[MAILTMPLEN],*s; ++ int rv = 1, courier = IS_COURIER(mailbox); ++ DIR *dir; ++ struct direct *d; ++ struct stat sbuf; ++ ++ maildir_file_path(mailbox, tmp2, sizeof(tmp2)); ++ ++ if(courier){ ++ strcpy(tmp3, tmp2); ++ if(s = strrchr(tmp2, '/')) ++ *s = '\0'; ++ } ++ ++ if (!(dir = opendir (tmp2))) ++ return rv; ++ ++ if(courier){ ++ while((d = readdir(dir)) != NULL){ ++ snprintf(tmp, sizeof(tmp), "%s/%s", tmp2, d->d_name); ++ if(!strncmp(tmp, tmp3, strlen(tmp3)) ++ && tmp[strlen(tmp3)] == '.'){ ++ rv = 0; ++ break; ++ } ++ } ++ } ++ else ++ while ((d = readdir(dir)) != NULL){ ++ snprintf(tmp, sizeof(tmp), "%s/%s", tmp2, d->d_name); ++ if (strcmp(d->d_name, ".") ++ && strcmp(d->d_name,"..") ++ && strcmp(d->d_name, MDNAME(Cur)) ++ && strcmp(d->d_name, MDNAME(Tmp)) ++ && strcmp(d->d_name, MDNAME(New)) ++ && strcmp(d->d_name, MDDIR) ++ && strcmp(d->d_name, MDUIDVALIDITY) ++ && !(d->d_name[0] == '.' ++ && stat (tmp,&sbuf) == 0 ++ && S_ISREG(sbuf.st_mode))){ ++ rv = 0; ++ break; ++ } ++ } ++ closedir(dir); ++ return rv; ++} ++ ++void ++maildir_get_file (MAILDIRFILE **mdfile) ++{ ++ MAILDIRFILE *md; ++ ++ md = (MAILDIRFILE *) fs_get(sizeof(MAILDIRFILE)); ++ memset(md, 0, sizeof(MAILDIRFILE)); ++ *mdfile = md; ++} ++ ++void ++maildir_free_file (void **mdfile) ++{ ++ MAILDIRFILE *md = (mdfile && *mdfile) ? (MAILDIRFILE *) *mdfile : NULL; ++ ++ if (md){ ++ if (md->name) fs_give((void **)&md->name); ++ fs_give((void **)&md); ++ } ++} ++ ++void ++maildir_free_file_only (void **mdfile) ++{ ++ MAILDIRFILE *md = (mdfile && *mdfile) ? (MAILDIRFILE *) *mdfile : NULL; ++ ++ if (md && md->name) ++ fs_give((void **)&md->name); ++} ++ ++int ++maildir_any_new_msgs(char *mailbox) ++{ ++ char tmp[MAILTMPLEN]; ++ int rv = NIL; ++ DIR *dir; ++ struct direct *d; ++ ++ MDFLD(tmp, mailbox, New); ++ ++ if (!(dir = opendir (tmp))) ++ return rv; ++ ++ while ((d = readdir(dir)) != NULL){ ++ if (d->d_name[0] == '.') ++ continue; ++ rv = T; ++ break; ++ } ++ closedir(dir); ++ return rv; ++} ++ ++ ++void ++maildir_get_date(MAILSTREAM *stream, unsigned long msgno) ++{ ++ MESSAGECACHE *elt; ++ struct tm *t; ++ time_t ti; ++ int i,k; ++ ++ elt = mail_elt (stream,msgno); ++ if(elt && elt->year != 0) ++ return; ++ if ((ti = mdfntoul(MDFILE(elt))) > 0L && (t = gmtime(&ti))){ ++ i = t->tm_hour * 60 + t->tm_min; ++ k = t->tm_yday; ++ t = localtime(&ti); ++ i = t->tm_hour * 60 + t->tm_min - i; ++ if((k = t->tm_yday - k) != 0) ++ i += ((k < 0) == (abs (k) == 1)) ? -24*60 : 24*60; ++ k = abs (i); ++ elt->hours = t->tm_hour; ++ elt->minutes = t->tm_min; ++ elt->seconds = t->tm_sec; ++ elt->day = t->tm_mday; elt->month = t->tm_mon + 1; ++ elt->year = t->tm_year - (BASEYEAR - 1900); ++ elt->zoccident = (k == i) ? 0 : 1; ++ elt->zhours = k/60; ++ elt->zminutes = k % 60; ++ } ++} ++ ++/* Support for Courier Style directories ++ When this code is complete there will be two types of support, which ++ will be configurable. The problem is the following: In Courier style ++ folder structure, a "folder" may have a subfolder called ++ "folder.subfolder", which is not natural in the file system in the ++ sense that I can not stat for "folder.subfolder" wihtout knowing what ++ "subfolder" is. It needs to be guessed. Because of this I need to look ++ in the list of folders if there is a folder with a name ++ "folder.subfolder", before I can say if the folder is dual or not. One ++ can avoid this annoyance if one ignores the problem by declaring that ++ every folder is dual. I will however code as the default the more ++ complicated idea of scaning the containing directory each time it is ++ modified and search for subfolders, and list the entries it found. ++ */ ++ ++int courier_dir_select (const struct direct *name) ++{ ++ return name->d_name[0] == '.' && (strlen(name->d_name) > 2 ++ || (strlen(name->d_name) == 2 && name->d_name[1] != '.')); ++} ++ ++int courier_dir_sort (const struct direct **d1, const struct direct **d2) ++{ ++ const struct direct *e1 = *(const struct direct **) d1; ++ const struct direct *e2 = *(const struct direct **) d2; ++ ++ return strcmp((char *) e1->d_name, (char *) e2->d_name); ++} ++ ++void courier_free_cdir (COURIER_S **cdir) ++{ ++ int i; ++ ++ if (!*cdir) ++ return; ++ ++ if ((*cdir)->path) fs_give((void **)&((*cdir)->path)); ++ for (i = 0; i < (*cdir)->total; i++) ++ if((*cdir)->data[i]->name) fs_give((void **)&((*cdir)->data[i]->name)); ++ fs_give((void **)&((*cdir)->data)); ++ fs_give((void **)&(*cdir)); ++} ++ ++COURIER_S *courier_get_cdir (int total) ++{ ++ COURIER_S *cdir; ++ ++ cdir = (COURIER_S *)fs_get(sizeof(COURIER_S)); ++ memset(cdir, 0, sizeof(COURIER_S)); ++ cdir->data = (COURIERLOCAL **) fs_get(total*sizeof(COURIERLOCAL *)); ++ memset(cdir->data, 0, sizeof(COURIERLOCAL *)); ++ cdir->total = total; ++ return cdir; ++} ++ ++int courier_search_list(COURIERLOCAL **data, char *name, int first, int last) ++{ ++ int try = (first + last)/2; ++ ++ if(!strstr(data[try]->name, name)){ ++ if(first == try) /* first == last || first + 1 == last */ ++ return strstr(data[last]->name, name) ? 1 : 0; ++ if(strcmp(data[try]->name, name) < 0) /*data[try] < name < data[end] */ ++ return courier_search_list(data, name, try, last); ++ else /* data[begin] < name < data[try] */ ++ return courier_search_list(data, name, first, try); ++ } ++ return 1; ++} ++ ++/* Lists all directories that are subdirectories of a given directory */ ++ ++COURIER_S *courier_list_dir(char *curdir) ++{ ++ struct direct **names = NIL; ++ struct stat sbuf; ++ unsigned long ndir; ++ COURIER_S *cdir = NULL; ++ char tmp[MAILTMPLEN], tmp2[MAILTMPLEN], pathname[MAILTMPLEN], ++ realname[MAILTMPLEN]; ++ int i, j, scand, td; ++ ++ /* There are two cases, either curdir is ++ #mc/INBOX. #mc/INBOX.foo ++ or ++ #mc/Maildir/. #mc/Maildir/.foo ++ */ ++ strcpy(tmp,curdir + 4); ++ if(!strncmp(ucase(tmp), "INBOX", 5)) ++ strcpy(tmp, "#mc/INBOX."); ++ else{ ++ strcpy(tmp, curdir); ++ for (i = strlen(tmp) - 1; tmp[i] && tmp[i] != '/'; i--); ++ tmp[i+2] = '\0'; /* keep the last "." intact */ ++ } ++ maildir_file_path(tmp, realname, sizeof(realname)); ++ maildir_scandir (realname, &names, &ndir, &scand, COURIER); ++ ++ if (scand > 0){ ++ cdir = courier_get_cdir(ndir); ++ cdir->path = cpystr(realname); ++ for(i = 0, j = 0; i < ndir; i++){ ++ td = realname[strlen(realname) - 1] == '.' ++ && *names[i]->d_name == '.'; ++ snprintf(tmp2, sizeof(tmp2), "%s%s", tmp, names[i]->d_name+1); ++ snprintf(pathname, sizeof(pathname), "%s%s", realname, names[i]->d_name + td); ++ if(stat(pathname, &sbuf) == 0 && S_ISDIR(sbuf.st_mode)){ ++ cdir->data[j] = (COURIERLOCAL *) fs_get(sizeof(COURIERLOCAL)); ++ cdir->data[j++]->name = cpystr(tmp2); ++ } ++ fs_give((void **)&names[i]); ++ } ++ cdir->total = j; ++ if(cdir->total == 0) ++ courier_free_cdir(&cdir); ++ } ++ if(names) ++ fs_give((void **) &names); ++ return cdir; ++} ++ ++void ++courier_list_info(COURIER_S **cdirp, char *data, int i) ++{ ++ long style = (long) maildir_parameters(GET_COURIERSTYLE, NIL); ++ COURIER_S *cdir = *cdirp; ++ ++ if(maildir_valid(cdir->data[i]->name)){ ++ if(courier_search_list(cdir->data, data, 0, cdir->total - 1)) ++ cdir->data[i]->attribute = LATT_HASCHILDREN; ++ else ++ cdir->data[i]->attribute = (style == COURIER) ++ ? LATT_HASNOCHILDREN : LATT_NOINFERIORS; ++ } ++ else ++ cdir->data[i]->attribute = LATT_NOSELECT; ++ cdir->data[i]->attribute += maildir_any_new_msgs(cdir->data[i]->name) ++ ? LATT_MARKED : LATT_UNMARKED; ++} ++ ++/* UID Support */ ++/* Yes, I know I procastinated a lot about this, but here it is finally */ ++ ++/* return code: ++ bigger than zero: this session can assign uids ++ zero: this session will not assign uid ++ smaller than zero: this session temporarily suspends assigning uids ++ */ ++int ++maildir_can_assign_uid (MAILSTREAM *stream) ++{ ++ unsigned int rv = 0; ++ int ownuid, existuid; ++ unsigned long t; ++ char tmp[MAILTMPLEN], tmp2[MAILTMPLEN], *p, *s; ++ DIR *dir; ++ struct direct *d; ++ ++ if(!stream || stream->rdonly ++ || !LOCAL || !LOCAL->dir || !(dir = opendir(LOCAL->dir))) ++ return 0; ++ ++ if(mypid == (pid_t) 0) ++ mypid = getpid(); ++ ++ snprintf(tmp, sizeof(tmp), "%s.%d", MDUIDTEMP, mypid); ++ ++ ownuid = existuid = 0; ++ s = NULL; ++ while ((d = readdir(dir)) != NULL){ ++ if(strncmp(d->d_name, tmp, strlen(tmp)) == 0){ ++ existuid++; ownuid++; ++ if(ownuid > 1){ ++ snprintf(tmp2, sizeof(tmp), "%s/%s", LOCAL->dir, d->d_name); ++ unlink(tmp2); ++ if(s){ ++ snprintf(tmp2, sizeof(tmp2), "%s/%s", LOCAL->dir, s); ++ unlink(tmp2); ++ fs_give((void **)&s); ++ } ++ } ++ else ++ s = cpystr(d->d_name); ++ } ++ else if(strncmp(d->d_name, MDUIDTEMP, strlen(MDUIDTEMP)) == 0) ++ existuid++; ++ } ++ ++ closedir(dir); ++ if(s) ++ fs_give((void **)&s); ++ ++ if(ownuid == 1 && existuid == 1) ++ rv = 1; ++ ++ if(ownuid == 0 && existuid == 0){ /* nobody owns the uid? */ ++ FILE *fp; ++ snprintf(tmp, sizeof(tmp), "%s/%s.%d.%lu", LOCAL->dir, MDUIDTEMP, mypid, time(0)); ++ if(fp = fopen(tmp, "w")){ ++ fclose(fp); ++ if(LOCAL->uidtempfile) ++ fs_give((void **)&LOCAL->uidtempfile); ++ LOCAL->uidtempfile = cpystr(tmp); ++ } ++ rv = 1; ++ } ++ ++ if(ownuid == 0 && existuid > 0) /* someone else owns uid assignment */ ++ return 0; ++ ++ /* if we own the uid, check that we do not own it more than once ++ * or that we share ownership. If any of these situations happens, ++ * give up the ownership until we can recover it ++ */ ++ ++ if(ownuid > 0){ ++ if(ownuid > 1) /* impossible, two lock files for the same session */ ++ return (-1)*ownuid; ++ ++ if(ownuid != existuid){ /* lock files for different sessions */ ++ if(LOCAL->uidtempfile){ ++ unlink(LOCAL->uidtempfile); ++ fs_give((void **)&LOCAL->uidtempfile); ++ } ++ return (-1)*ownuid; ++ } ++ } ++ ++ return rv; ++} ++ ++void ++maildir_read_uid(MAILSTREAM *stream, unsigned long *uid_last, ++ unsigned long *uid_validity) ++{ ++ int createuid, deleteuid = 0; ++ char tmp[MAILTMPLEN], *s = NULL; ++ DIR *dir; ++ struct direct *d; ++ ++ if(uid_last) *uid_last = 0L; ++ if(uid_last && uid_validity) *uid_validity = time(0); ++ if(!stream || !LOCAL || !LOCAL->dir || !(dir = opendir(LOCAL->dir))) ++ return; ++ ++ while ((d = readdir(dir)) != NULL){ ++ if(!strncmp(d->d_name, MDUIDLAST, strlen(MDUIDLAST))) ++ break; ++ } ++ createuid = d == NULL ? 1 : 0; ++ if(uid_last == NULL) ++ deleteuid++; ++ if(d){ ++ if(uid_last){ ++ s = d->d_name + strlen(MDUIDLAST) + 1; ++ *uid_last = strtoul(s, &s, 10); ++ if(!s || *s != '.'){ ++ deleteuid++; ++ createuid++; ++ *uid_last = 0L; ++ } ++ } ++ if(s && *s == '.'){ ++ if(uid_validity){ ++ s++; ++ *uid_validity = strtoul(s, &s, 10); ++ if(s && *s != '\0'){ ++ *uid_validity = time(0); ++ deleteuid++; ++ createuid++; ++ } ++ } ++ } ++ else{ ++ deleteuid++; ++ createuid++; ++ } ++ } ++ if(deleteuid){ ++ snprintf(tmp, sizeof(tmp), "%s/%s", LOCAL->dir, d->d_name); ++ unlink(tmp); ++ } ++ if(createuid) ++ maildir_write_uid(stream, (uid_last ? *uid_last : stream->uid_last), ++ uid_validity ? *uid_validity : time(0)); ++ closedir(dir); ++} ++ ++void ++maildir_write_uid(MAILSTREAM *stream, unsigned long uid_last, ++ unsigned long uid_validity) ++{ ++ char tmp[MAILTMPLEN]; ++ FILE *fp; ++ ++ if(!stream || stream->rdonly || !LOCAL || !LOCAL->dir) ++ return; ++ ++ snprintf(tmp, sizeof(tmp), "%s/%s.%010lu.%010lu", LOCAL->dir, MDUIDLAST, ++ uid_last, uid_validity); ++ if(fp = fopen(tmp, "w")) ++ fclose(fp); ++} ++ ++unsigned long ++maildir_get_uid(char *name) ++{ ++ char *s; ++ unsigned long rv = 0L; ++ ++ if(!name || (s = strstr(name,MDUIDSEP)) == NULL) ++ return rv; ++ ++ s += strlen(MDUIDSEP); ++ rv = strtoul(s, NULL, 10); ++ return rv; ++} ++ ++ ++void ++maildir_delete_uid(MAILSTREAM *stream, unsigned long msgno) ++{ ++ char old[MAILTMPLEN], new[MAILTMPLEN], *s, *t; ++ MESSAGECACHE *elt; ++ ++ elt = mail_elt(stream, msgno); ++ if(!stream || !elt || !elt->private.spare.ptr || !LOCAL || !LOCAL->dir) ++ return; ++ ++ snprintf(old, sizeof(old), "%s/%s/%s", LOCAL->dir, MDNAME(Cur), MDFILE(elt)); ++ t = MDFILE(elt); ++ if(s = strstr(MDFILE(elt), MDUIDSEP)){ ++ *s = '\0'; ++ s += strlen(MDUIDSEP); ++ strtoul(s, &s, 10); ++ snprintf(new, sizeof(new), "%s/%s/%s%s", LOCAL->dir, MDNAME(Cur), t, s); ++ if(rename(old, new) == 0){ ++ maildir_free_file_only ((void **)&elt->private.spare.ptr); ++ s = strrchr(new, '/'); ++ MDFILE(elt) = cpystr(s+1); ++ } ++ elt->private.uid = 0L; ++ } ++} ++ ++void ++maildir_assign_uid(MAILSTREAM *stream, unsigned long msgno, unsigned long uid) ++{ ++ int createuid, deleteuid = 0; ++ char old[MAILTMPLEN], new[MAILTMPLEN], *s, *t; ++ MESSAGECACHE *elt; ++ ++ elt = mail_elt(stream, msgno); ++ if(!stream || !elt || !elt->private.spare.ptr || !LOCAL || !LOCAL->dir) ++ return; ++ ++ maildir_delete_uid(stream, msgno); ++ snprintf(old, sizeof(old), "%s/%s/%s", LOCAL->dir, MDNAME(Cur), MDFILE(elt)); ++ t = MDFILE(elt); ++ if((s = strrchr(MDFILE(elt),FLAGSEP)) != NULL){ ++ *s++ = '\0'; ++ snprintf(new, sizeof(new), "%s/%s/%s%s%lu%c%s", ++ LOCAL->dir, MDNAME(Cur), t, MDUIDSEP, uid, FLAGSEP, s); ++ if(rename(old, new) == 0){ ++ maildir_free_file_only ((void **)&elt->private.spare.ptr); ++ s = strrchr(new, '/'); ++ MDFILE(elt) = cpystr(s+1); ++ stream->uid_validity = time(0); ++ } ++ elt->private.uid = uid; ++ } ++} ++ ++void ++maildir_uid_renew_tempfile(MAILSTREAM *stream) ++{ ++ char tmp[MAILTMPLEN]; ++ ++ if(!stream || stream->rdonly ++ || !LOCAL || !LOCAL->candouid || !LOCAL->dir || !LOCAL->uidtempfile) ++ return; ++ ++ if(mypid == (pid_t) 0) ++ mypid = getpid(); ++ ++ snprintf(tmp, sizeof(tmp), "%s/%s.%d.%lu", LOCAL->dir, MDUIDTEMP, mypid, time(0)); ++ if(rename(LOCAL->uidtempfile, tmp) == 0){ ++ fs_give((void **)&LOCAL->uidtempfile); ++ LOCAL->uidtempfile = cpystr(tmp); ++ } ++} +Index: alpine-2.11/imap/src/osdep/unix/maildir.h +=================================================================== +--- /dev/null ++++ alpine-2.11/imap/src/osdep/unix/maildir.h +@@ -0,0 +1,226 @@ ++/* ++ * A few definitions that try to make this module portable to other ++ * platforms (e.g. Cygwin). This module is based on the information from ++ * http://cr.yp.to/proto/maildir.html ++ */ ++ ++/* First we deal with the separator character */ ++#ifndef FLAGSEP ++#define FLAGSEP ':' ++#endif ++#define SIZESEP ',' ++ ++const char sep1[] = {FLAGSEP, '1', ',', '\0'}; /* experimental semantics*/ ++const char sep2[] = {FLAGSEP, '2', ',', '\0'}; /* Flags Information */ ++const char sep3[] = {FLAGSEP, '3', ',', '\0'}; /* Grrrr.... */ ++ ++const char *sep[] = { sep1, sep2, sep3, NULL}; ++ ++#define MDSEP(i) sep[((i) - 1)] ++ ++/* Now we deal with flags. Woohoo! */ ++typedef enum {Draft, Flagged, Passed, Replied, Seen, Trashed, ++ EmptyFlag, EndFlags} MdFlagNamesType; ++const int mdimapflags[] = {Draft, Flagged, Replied, Seen, Trashed, EmptyFlag, EndFlags}; ++const int mdkwdflags[] = {Passed, EmptyFlag, EndFlags}; ++ ++/* this array lists the codes for mdflgnms (maildir flag names) above */ ++const char *mdflags[] = { "D", "F", "P", "R", "S", "T", "", NULL}; ++/* and as characters too */ ++const char cmdflags[] = { 'D', 'F', 'P', 'R', 'S', 'T', '0', '\0'}; ++ ++/* MDFLAG(Seen, elt->seen) */ ++#define MDFLAG(i,j) mdflags[j ? (i) : EmptyFlag] ++/* MDFLAGC(Seen) */ ++#define MDFLAGC(i) cmdflags[(i)] ++ ++/* Now we deal with the directory structure */ ++typedef enum {Cur, Tmp, New, EndDir} DirNamesType; ++char *mdstruct[] = {"cur", "tmp", "new", NULL}; ++#define MDNAME(i) mdstruct[(i)] ++#define MDFLD(tmp, dir, i) sprintf((tmp),"%s/%s", (dir), mdstruct[(i)]) ++#define MSGPATH(tmp, dir, msg,i) sprintf((tmp),"%s/%s/%s", (dir), mdstruct[(i)],(msg)) ++ ++/* Files associated to a maildir directory */ ++ ++#define MDUIDVALIDITY ".uidvalidity" /* support for old maildirs */ ++#define MDDIR ".mdir" /* this folder is a directory */ ++#define MDUIDLAST ".uidlast" /* last assigned uid */ ++#define MDUIDTEMP ".uidtemp" /* We assign uid's no one else */ ++ ++ ++ ++/* Support of Courier Structure */ ++#define CCLIENT 0 ++#define COURIER 1 ++#define IS_CCLIENT(t) \ ++ (((t) && (t)[0] == '#' && ((t)[1] == 'm' || (t)[1] == 'M')\ ++ && ((t)[2] == 'd' || (t)[2] == 'D')\ ++ && (t)[3] == '/' && (t)[4] != '\0') ? 1 : 0) ++ ++#define IS_COURIER(t) \ ++ (((t) && (t)[0] == '#' && ((t)[1] == 'm' || (t)[1] == 'M')\ ++ && ((t)[2] == 'c' || (t)[2] == 'C')\ ++ && (t)[3] == '/' && (t)[4] != '\0') ? 1 : 0) ++#define MDPREFIX(s) ((s) ? "#mc/" : "#md/") ++#define MDSEPARATOR(s) ((s) ? '.' : '/') ++ ++/* UID Support */ ++ ++#define MAXTEMPUID (unsigned long) 180L ++const char mduid[] = {',','u','=','\0'}; ++#define MDUIDSEP mduid ++ ++ ++/* Now we deal with messages filenames */ ++char mdlocaldomain[MAILTMPLEN+1] = {'\0'}; ++pid_t mypid = (pid_t) 0; ++static char *mdfpath = NULL; ++static char myMdInboxDir[50] = { '\0' };/* Location of the Maildir INBOX */ ++static long CourierStyle = CCLIENT; ++ ++#define CHUNK 16384 /* from unix.h */ ++ ++typedef struct courier_local { ++ char *name; /* name of directory/folder */ ++ int attribute; /* attributes (children/marked/etc) */ ++} COURIERLOCAL; ++ ++typedef struct courier { ++ char *path; /* Path to collection */ ++ time_t scantime; /* time at which information was generated */ ++ int total; /* total number of elements in data */ ++ COURIERLOCAL **data; ++} COURIER_S; ++ ++/* In gdb this is the *(struct maildir_local *)stream->local structure */ ++typedef struct maildir_local { ++ unsigned int dirty : 1; /* diskcopy needs updating */ ++ unsigned int courier : 1; /* It is Courier style file system */ ++ unsigned int link : 1; /* There is a symbolic link */ ++ int candouid; /* we can assign uids and no one else */ ++ char *uidtempfile; /* path to uid temp file */ ++ int fd; /* fd of open message */ ++ char *dir; /* mail directory name */ ++ char **path; /* path to directories cur, new and tmp */ ++ unsigned char *buf; /* temporary buffer */ ++ unsigned long buflen; /* current size of temporary buffer */ ++ time_t scantime; /* last time directory scanned */ ++} MAILDIRLOCAL; ++ ++/* Convenient access to local data */ ++#define LOCAL ((MAILDIRLOCAL *) stream->local) ++ ++typedef struct maildir_file_info { ++ char *name; /* name of the file */ ++ DirNamesType loc; /* location of this file */ ++ unsigned long pos; /* place in list where this file is listed */ ++ off_t size; /* size in bytes, on disk */ ++ time_t atime; /* last access time */ ++ time_t mtime; /* last modified time */ ++ time_t ctime; /* last changed time */ ++} MAILDIRFILE; ++ ++#define MDFILE(F) (((MAILDIRFILE *)((F)->private.spare.ptr))->name) ++#define MDLOC(F) (((MAILDIRFILE *)((F)->private.spare.ptr))->loc) ++#define MDPOS(F) (((MAILDIRFILE *)((F)->private.spare.ptr))->pos) ++#define MDSIZE(F) (((MAILDIRFILE *)((F)->private.spare.ptr))->size) ++#define MDATIME(F) (((MAILDIRFILE *)((F)->private.spare.ptr))->atime) ++#define MDMTIME(F) (((MAILDIRFILE *)((F)->private.spare.ptr))->mtime) ++#define MDCTIME(F) (((MAILDIRFILE *)((F)->private.spare.ptr))->ctime) ++ ++/* Function prototypes */ ++ ++DRIVER *maildir_valid (char *name); ++MAILSTREAM *maildir_open (MAILSTREAM *stream); ++void maildir_close (MAILSTREAM *stream, long options); ++long maildir_ping (MAILSTREAM *stream); ++void maildir_check (MAILSTREAM *stream); ++long maildir_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags); ++char *maildir_header (MAILSTREAM *stream,unsigned long msgno, ++ unsigned long *length, long flags); ++void maildir_list (MAILSTREAM *stream,char *ref,char *pat); ++void *maildir_parameters (long function,void *value); ++int maildir_create_folder (char *mailbox); ++long maildir_create (MAILSTREAM *stream,char *mailbox); ++void maildir_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt); /*check */ ++long maildir_expunge (MAILSTREAM *stream, char *sequence, long options); ++long maildir_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options); ++long maildir_append (MAILSTREAM *stream,char *mailbox, append_t af, void *data); ++long maildir_delete (MAILSTREAM *stream,char *mailbox); ++long maildir_rename (MAILSTREAM *stream,char *old,char *new); ++long maildir_sub (MAILSTREAM *stream,char *mailbox); ++long maildir_unsub (MAILSTREAM *stream,char *mailbox); ++void maildir_lsub (MAILSTREAM *stream,char *ref,char *pat); ++void courier_list (MAILSTREAM *stream,char *ref, char *pat); ++ ++/* utility functions */ ++void courier_realname (char *name, char *realname); ++long maildir_dirfmttest (char *name); ++char *maildir_file (char *dst,char *name); ++int maildir_select (const struct direct *name); ++int maildir_namesort (const struct direct **d1, const struct direct **d2); ++unsigned long antoul (char *seed); ++unsigned long mdfntoul (char *name); ++int courier_dir_select (const struct direct *name); ++int courier_dir_sort (const struct direct **d1, const struct direct **d2); ++long maildir_canonicalize (char *pattern,char *ref,char *pat); ++void maildir_list_work (MAILSTREAM *stream,char *subdir,char *pat,long level); ++void courier_list_work (MAILSTREAM *stream,char *subdir,char *pat,long level); ++int maildir_file_path(char *name, char *tmp, size_t sizeoftmp); ++int maildir_valid_name (char *name); ++int maildir_valid_dir (char *name); ++int is_valid_maildir (char **name); ++int maildir_message_exists(MAILSTREAM *stream,char *name, char *tmp); ++char *maildir_remove_root(char *name); ++char *maildir_text_work (MAILSTREAM *stream,MESSAGECACHE *elt, unsigned long *length,long flags); ++unsigned long maildir_parse_message(MAILSTREAM *stream, unsigned long msgno, ++ DirNamesType dirtype); ++int maildir_eliminate_duplicate (char *name, struct direct ***flist, ++ unsigned long *nfiles); ++int maildir_doscandir (char *name, struct direct ***flist, int flag); ++unsigned long maildir_scandir (char *name, struct direct ***flist, ++ unsigned long *nfiles, int *scand, int flag); ++void maildir_parse_folder (MAILSTREAM *stream, int full); ++void md_domain_name (void); ++char *myrootdir (char *name); ++char *mdirpath (void); ++int maildir_initial_check (MAILSTREAM *stream, DirNamesType dirtype); ++unsigned long maildir_parse_dir(MAILSTREAM *stream, unsigned long nmsgs, ++ DirNamesType dirtype, struct direct **names, unsigned long nfiles, int full); ++int same_maildir_file(char *name1, char *name2); ++int comp_maildir_file(char *name1, char *name2); ++int maildir_message_in_list(char *msgname, struct direct **names, ++ unsigned long bottom, unsigned long top, unsigned long *pos); ++void maildir_getflag(char *name, int *d, int *f, int *r ,int *s, int *t); ++int maildir_update_elt_maildirp(MAILSTREAM *stream, unsigned long msgno); ++void maildir_abort (MAILSTREAM *stream); ++int maildir_contains_folder(char *dirname, char *name); ++int maildir_is_dir(char *dirname, char *name); ++int maildir_dir_is_empty(char *mailbox); ++int maildir_create_work (char *mailbox, int loop); ++void maildir_get_file (MAILDIRFILE **mdfile); ++void maildir_free_file (void **mdfile); ++void maildir_free_file_only (void **mdfile); ++int maildir_any_new_msgs(char *mailbox); ++void maildir_get_date(MAILSTREAM *stream, unsigned long msgno); ++void maildir_fast (MAILSTREAM *stream,char *sequence,long flags); ++ ++/* Courier server support */ ++void courier_free_cdir (COURIER_S **cdir); ++COURIER_S *courier_get_cdir (int total); ++int courier_search_list(COURIERLOCAL **data, char *name, int first, int last); ++COURIER_S *courier_list_dir(char *curdir); ++void courier_list_info(COURIER_S **cdirp, char *data, int i); ++ ++/* UID Support */ ++int maildir_can_assign_uid (MAILSTREAM *stream); ++void maildir_read_uid(MAILSTREAM *stream, unsigned long *uid_last, ++ unsigned long *uid_validity); ++void maildir_write_uid(MAILSTREAM *stream, unsigned long uid_last, ++ unsigned long uid_validity); ++unsigned long maildir_get_uid(char *name); ++void maildir_delete_uid(MAILSTREAM *stream, unsigned long msgno); ++void maildir_assign_uid(MAILSTREAM *stream, unsigned long msgno, unsigned long uid); ++void maildir_uid_renew_tempfile(MAILSTREAM *stream); ++ +Index: alpine-2.11/imap/src/osdep/unix/os_cyg.h +=================================================================== +--- alpine-2.11.orig/imap/src/osdep/unix/os_cyg.h ++++ alpine-2.11/imap/src/osdep/unix/os_cyg.h +@@ -47,6 +47,7 @@ + #define setpgrp setpgid + + #define SYSTEMUID 18 /* Cygwin returns this for SYSTEM */ ++#define FLAGSEP ';' + #define geteuid Geteuid + uid_t Geteuid (void); + +Index: alpine-2.11/pith/conf.c +=================================================================== +--- alpine-2.11.orig/pith/conf.c ++++ alpine-2.11/pith/conf.c +@@ -434,6 +434,9 @@ CONF_TXT_T cf_text_window_position[] = " + + CONF_TXT_T cf_text_newsrc_path[] = "Full path and name of NEWSRC file"; + ++#ifndef _WINDOWS ++CONF_TXT_T cf_text_maildir_location[] = "Location relative to your HOME directory of the directory where your INBOX\n# for the maildir format is located. Default value is \"Maildir\". If your\n# inbox is located at \"~/Maildir\" you do not need to change this value.\n# A common value is also \".maildir\""; ++#endif + + /*---------------------------------------------------------------------- + These are the variables that control a number of pine functions. They +@@ -638,6 +641,10 @@ static struct variable variables[] = { + NULL, cf_text_news_active}, + {"news-spool-directory", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, + NULL, cf_text_news_spooldir}, ++#ifndef _WINDOWS ++{"maildir-location", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, ++ "Maildir Location", cf_text_maildir_location}, ++#endif + {"upload-command", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, + NULL, cf_text_upload_cmd}, + {"upload-command-prefix", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, +@@ -2271,6 +2278,12 @@ init_vars(struct pine *ps, void (*cmds_f + mail_parameters(NULL, SET_NEWSSPOOL, + (void *)VAR_NEWS_SPOOL_DIR); + ++#ifndef _WINDOWS ++ set_current_val(&vars[V_MAILDIR_LOCATION], TRUE, TRUE); ++ if(VAR_MAILDIR_LOCATION && VAR_MAILDIR_LOCATION[0]) ++ mail_parameters(NULL, SET_MDINBOXPATH, (void *)VAR_MAILDIR_LOCATION); ++#endif ++ + /* guarantee a save default */ + set_current_val(&vars[V_DEFAULT_SAVE_FOLDER], TRUE, TRUE); + if(!VAR_DEFAULT_SAVE_FOLDER || !VAR_DEFAULT_SAVE_FOLDER[0]) +@@ -2909,6 +2922,10 @@ feature_list(int index) + F_SORT_DEFAULT_SAVE_ALPHA, h_config_sort_save_alpha, PREF_FLDR, 0}, + {"vertical-folder-list", "Use Vertical Folder List", + F_VERTICAL_FOLDER_LIST, h_config_vertical_list, PREF_FLDR, 0}, ++#ifndef _WINDOWS ++ {"use-courier-folder-list", "Courier Style Folder List", ++ F_COURIER_FOLDER_LIST, h_config_courier_list, PREF_FLDR, 0}, ++#endif + + /* Addr book */ + {"combined-addrbook-display", "Combined Address Book Display", +@@ -7034,6 +7051,12 @@ toggle_feature(struct pine *ps, struct v + + break; + ++#ifndef _WINDOWS ++ case F_COURIER_FOLDER_LIST: ++ mail_parameters(NULL,SET_COURIERSTYLE,(void *)(F_ON(f->id ,ps)? 1 : 0)); ++ break; /* COURIER == 1, CCLIENT == 0, see maildir.h */ ++#endif ++ + case F_COLOR_LINE_IMPORTANT : + case F_DATES_TO_LOCAL : + clear_index_cache(ps->mail_stream, 0); +@@ -7819,6 +7842,10 @@ config_help(int var, int feature) + return(h_config_newmailwidth); + case V_NEWSRC_PATH : + return(h_config_newsrc_path); ++#ifndef _WINDOWS ++ case V_MAILDIR_LOCATION : ++ return(h_config_maildir_location); ++#endif + case V_BROWSER : + return(h_config_browser); + #if defined(DOS) || defined(OS2) +Index: alpine-2.11/pith/conf.h +=================================================================== +--- alpine-2.11.orig/pith/conf.h ++++ alpine-2.11/pith/conf.h +@@ -255,6 +255,10 @@ + #define GLO_NEWS_ACTIVE_PATH vars[V_NEWS_ACTIVE_PATH].global_val.p + #define VAR_NEWS_SPOOL_DIR vars[V_NEWS_SPOOL_DIR].current_val.p + #define GLO_NEWS_SPOOL_DIR vars[V_NEWS_SPOOL_DIR].global_val.p ++#ifndef _WINDOWS ++#define VAR_MAILDIR_LOCATION vars[V_MAILDIR_LOCATION].current_val.p ++#define GLO_MAILDIR_LOCATION vars[V_MAILDIR_LOCATION].global_val.p ++#endif + #define VAR_DISABLE_DRIVERS vars[V_DISABLE_DRIVERS].current_val.l + #define VAR_DISABLE_AUTHS vars[V_DISABLE_AUTHS].current_val.l + #define VAR_REMOTE_ABOOK_METADATA vars[V_REMOTE_ABOOK_METADATA].current_val.p +Index: alpine-2.11/pith/conftype.h +=================================================================== +--- alpine-2.11.orig/pith/conftype.h ++++ alpine-2.11/pith/conftype.h +@@ -117,6 +117,9 @@ typedef enum { V_PERSONAL_NAME = 0 + , V_NEWSRC_PATH + , V_NEWS_ACTIVE_PATH + , V_NEWS_SPOOL_DIR ++#ifndef _WINDOWS ++ , V_MAILDIR_LOCATION ++#endif + , V_UPLOAD_CMD + , V_UPLOAD_CMD_PREFIX + , V_DOWNLOAD_CMD +@@ -393,6 +396,9 @@ typedef enum { + F_PASS_C1_CONTROL_CHARS, + F_SINGLE_FOLDER_LIST, + F_VERTICAL_FOLDER_LIST, ++#ifndef _WINDOWS ++ F_COURIER_FOLDER_LIST, ++#endif + F_TAB_CHK_RECENT, + F_AUTO_REPLY_TO, + F_VERBOSE_POST, +Index: alpine-2.11/pith/init.c +=================================================================== +--- alpine-2.11.orig/pith/init.c ++++ alpine-2.11/pith/init.c +@@ -408,6 +408,9 @@ get_mail_list(CONTEXT_S *list_cntxt, cha + && stricmp(filename, folder_base)){ + #else + if(strncmp(filename, folder_base, folder_base_len) == 0 ++#ifndef _WINDOWS ++ && filename[folder_base_len] != list_cntxt->dir->delim ++#endif + && strcmp(filename, folder_base)){ + #endif + #endif +Index: alpine-2.11/pith/pattern.c +=================================================================== +--- alpine-2.11.orig/pith/pattern.c ++++ alpine-2.11/pith/pattern.c +@@ -5483,6 +5483,15 @@ match_pattern_folder_specific(PATTERN_S + break; + + case '#': ++#ifndef _WINDOWS ++ if(!struncmp(patfolder, "#md/", 4) ++ || !struncmp(patfolder, "#mc/", 4)){ ++ maildir_file_path(patfolder, tmp1, sizeof(tmp1)); ++ if(!strcmp(patfolder, stream->mailbox)) ++ match++; ++ break; ++ } ++#endif + if(!strcmp(patfolder, stream->mailbox)) + match++; + +@@ -7903,7 +7912,7 @@ move_filtered_msgs(MAILSTREAM *stream, M + int we_cancel = 0, width; + CONTEXT_S *save_context = NULL; + char buf[MAX_SCREEN_COLS+1], sbuf[MAX_SCREEN_COLS+1]; +- char *save_ref = NULL; ++ char *save_ref = NULL, *save_dstfldr = NULL, *save_dstfldr2 = NULL; + #define FILTMSG_MAX 30 + + if(!stream) +@@ -7937,6 +7946,16 @@ move_filtered_msgs(MAILSTREAM *stream, M + if(F_OFF(F_QUELL_FILTER_MSGS, ps_global)) + we_cancel = busy_cue(buf, NULL, 0); + ++#ifndef _WINDOWS ++ if(!struncmp(dstfldr, "#md/", 4) || !struncmp(dstfldr, "#mc/", 4)){ ++ char tmp1[MAILTMPLEN]; ++ maildir_file_path(dstfldr, tmp1, sizeof(tmp1)); ++ save_dstfldr2 = dstfldr; ++ save_dstfldr = cpystr(tmp1); ++ dstfldr = save_dstfldr; ++ } ++#endif ++ + if(!is_absolute_path(dstfldr) + && !(save_context = default_save_context(ps_global->context_list))) + save_context = ps_global->context_list; +@@ -8000,6 +8019,11 @@ move_filtered_msgs(MAILSTREAM *stream, M + if(we_cancel) + cancel_busy_cue(buf[0] ? 0 : -1); + ++ if(save_dstfldr){ ++ fs_give((void **)&save_dstfldr); ++ dstfldr = save_dstfldr2; ++ } ++ + return(buf[0] != '\0'); + } + +Index: alpine-2.11/pith/pine.hlp +=================================================================== +--- alpine-2.11.orig/pith/pine.hlp ++++ alpine-2.11/pith/pine.hlp +@@ -21664,6 +21664,102 @@ your account's home directory). + <End of help on this topic> + + ++====== h_config_maildir_location ====== ++ ++ ++OPTION: <!--#echo var="VAR_maildir-location"--> ++ ++ ++

    OPTION:

    ++ ++

    ++This option should be used only if you have a Maildir folder which you ++want to use as your INBOX. If this is not your case (or don't know what ++this is), you can safely ignore this option. ++ ++

    ++This option overrides the default directory Pine uses to find the location of ++your INBOX, in case this is in Maildir format. The default value of this ++option is "Maildir", but in some systems, this directory could have been ++renamed (e.g. to ".maildir"). If this is your case use this option to change ++the default. ++ ++

    ++The value of this option is prefixed with the "~/" string to determine the ++full path to your INBOX. ++ ++

    ++You should probably read a few tips that ++teach you how to configure your maildir for optimal performance. This ++version also has support for the ++Courier style file system when a maildir collection is accessed locally. ++ ++

    ++

    ++<End of help on this topic> ++ ++ ++====== h_config_maildir ===== ++ ++ ++Maildir Support ++ ++ ++

    Maildir Support

    ++ ++This version of Alpine has been enhanced with Maildir support. This text is ++intended to be a reference on its support. ++

    ++ ++A Maildir folder is a directory that contains three directories called ++cur, tmp and new. A program that delivers mail (e.g. postfix) will put new ++mail in the new directory. A program that reads mail will look for for old ++messages in the cur directory, while it will look for new mail in the new ++directory. ++

    ++ ++In order to use maildir support it is better to set your inbox-path to the ++value "#md/inbox" (without quotes). This assumes that your mail ++delivery agent is delivering new mail to ~/Maildir/new. If the directory ++where new mail is being delivered is not called "Maildir", you can set the ++name of the subdirectory of home where it is being delivered in the configuration ++variable. Most of the time you will not have to worry about the ++ variable, because it will probably be set by your ++administrator in the pine.conf configuration file. ++

    ++ ++One of the advantages of the Maildir support of this version of Alpine is ++that you do not have to stop using folders in another styles (mbox, mbx, ++etc.). This is desirable since the usage of a specific mail storage system ++is a personal decision. Folders in the maildir format that are part of the ++Mail collection will be recognized without any extra configuration of your ++part. If your mail/ collection is located under the mail/ directory, then ++creating a new maildir folder in this collection is done by pressing "A" ++and entering the string "#driver.md/mail/newfolder". Observe that adding a ++new folder as "newfolder" may not create such folder in maildir format. ++ ++

    ++If you would like to have all folders created in the maildir format by ++default, you do so by adding a Maildir Collection. In order to convert ++your current mail/ collection into a maildir collection, edit the ++collection and change the path variable from "mail/" to ++"#md/mail". In a maildir collection folders of any other format ++are ignored. ++ ++

    Finally, This version also has ++support for the Courier style file system ++when a maildir collection is accessed locally. ++ ++

    ++

    ++<End of help on this topic> ++ ++ + ====== h_config_literal_sig ===== + + +@@ -29690,6 +29786,49 @@ than across the columns as is the defaul +

    + <End of help on this topic> + ++ ++====== h_config_courier_list ===== ++ ++ ++FEATURE: <!--#echo var="FEAT_courier-folder-list"--> ++ ++ ++

    FEATURE:

    ++ ++In a maildir collection, a folder could be used as a directory to store ++folders. In the Courier server if you create a folder, then a directory ++with the same name is created. If you use this patch to access a ++collection created by the Courier server, then the display of such ++collection will look confusing. The best way to access a maildir ++collection created by the Courier server is by using the "#mc/" ++prefix instead of the "#md/" prefix. If you use this alternate ++prefix, then this feature applies to you, otherwise you can safely ignore ++the text that follows. ++

    ++Depending on if you have enabled the option ++ ++a folder may be listed as "folder[.]", or as two entries in the ++list by "folder" and "folder.". ++

    ++If this option is disabled, Pine will list local folders that are in Courier ++style format, as "folder", and those that are also directories as ++"folder[.]". This makes the default display cleaner. ++

    ++If this feature is enabled then creating folders in a maildir collection ++will create a directory with the same name. If this feature is disabled, then ++a folder is considered a directory only if it contains subfolders, so you can ++not create a directory with the same name as an exisiting folder unless ++you create a subfolder of that folder first (e.g. if you have a folder ++called "foo" simply add "foo.bar" directly. This will ++create the directory "foo" and the subfolder "bar" of it). ++

    ++Observe that this feature works only for maildir collections that are accessed ++locally. If a collection is accessed remotely then this feature has no value, ++as the report is created in a server, and Pine only reports what received ++from the server in this case. ++

    ++<End of help on this topic> ++ + + ====== h_config_verbose_post ===== + +Index: alpine-2.11/pith/send.c +=================================================================== +--- alpine-2.11.orig/pith/send.c ++++ alpine-2.11/pith/send.c +@@ -257,6 +257,13 @@ postponed_stream(MAILSTREAM **streamp, c + + if(exists & FEX_ISFILE){ + context_apply(tmp, p_cntxt, mbox, sizeof(tmp)); ++#ifndef _WINDOWS ++ if (!struncmp(tmp, "#md/",4) || !struncmp(tmp, "#mc/", 4)){ ++ char tmp2[MAILTMPLEN]; ++ maildir_file_path(tmp, tmp2, sizeof(tmp2)); ++ strcpy(tmp, tmp2); ++ } ++#endif + if(!(IS_REMOTE(tmp) || is_absolute_path(tmp))){ + /* + * The mbox is relative to the home directory. diff --git a/chappa-unixnullbug.patch b/chappa-unixnullbug.patch index 8e353a7..0201085 100644 --- a/chappa-unixnullbug.patch +++ b/chappa-unixnullbug.patch @@ -1,45 +1,35 @@ -diff -rc alpine-2.10/imap/src/c-client/mail.c alpine-2.10.unixnullbug/imap/src/c-client/mail.c -*** alpine-2.10/imap/src/c-client/mail.c 2013-01-11 15:21:34.000000000 -0700 ---- alpine-2.10.unixnullbug/imap/src/c-client/mail.c 2013-01-11 20:45:05.000000000 -0700 -*************** -*** 3352,3364 **** - long flags) - { - STRINGLIST *hdrs; -! int notfound; - unsigned long i; - char c,*s,*e,*t,tmp[MAILTMPLEN]; - char *src = text; - char *dst = src; - char *end = text + len; -! text[len] = '\012'; /* guard against running off buffer */ - while (src < end) { /* process header */ - /* slurp header line name */ - for (s = src,e = s + MAILTMPLEN - 1,e = (e < end ? e : end),t = tmp; ---- 3352,3364 ---- - long flags) - { - STRINGLIST *hdrs; -! int notfound, fix = text[len - 1] == '\0'; - unsigned long i; - char c,*s,*e,*t,tmp[MAILTMPLEN]; - char *src = text; - char *dst = src; - char *end = text + len; -! text[fix ? len - 1 : len] = '\012'; /* guard against running off buffer */ - while (src < end) { /* process header */ - /* slurp header line name */ - for (s = src,e = s + MAILTMPLEN - 1,e = (e < end ? e : end),t = tmp; -*************** -*** 3397,3402 **** ---- 3397,3406 ---- - } - } - *dst = '\0'; /* tie off destination */ -+ if(fix){ -+ text[len] = '\012'; -+ text[len-1] = '\0'; -+ } - return dst - text; - } - +--- + imap/src/c-client/mail.c | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +Index: alpine-2.11/imap/src/c-client/mail.c +=================================================================== +--- alpine-2.11.orig/imap/src/c-client/mail.c ++++ alpine-2.11/imap/src/c-client/mail.c +@@ -3361,13 +3361,13 @@ unsigned long mail_filter (char *text,un + long flags) + { + STRINGLIST *hdrs; +- int notfound; ++ int notfound, fix = text[len - 1] == '\0'; + unsigned long i; + char c,*s,*e,*t,tmp[MAILTMPLEN]; + char *src = text; + char *dst = src; + char *end = text + len; +- text[len] = '\012'; /* guard against running off buffer */ ++ text[fix ? len - 1 : len] = '\012'; /* guard against running off buffer */ + while (src < end) { /* process header */ + /* slurp header line name */ + for (s = src,e = s + MAILTMPLEN - 1,e = (e < end ? e : end),t = tmp; +@@ -3406,6 +3406,10 @@ unsigned long mail_filter (char *text,un + } + } + *dst = '\0'; /* tie off destination */ ++ if(fix){ ++ text[len] = '\012'; ++ text[len-1] = '\0'; ++ } + return dst - text; + } + diff --git a/chappa-unverified.patch b/chappa-unverified.patch deleted file mode 100644 index 029a93c..0000000 --- a/chappa-unverified.patch +++ /dev/null @@ -1,111 +0,0 @@ -diff -rc alpine-2.10/alpine/reply.c alpine-2.10.unverified/alpine/reply.c -*** alpine-2.10/alpine/reply.c 2013-01-11 11:19:33.000000000 -0700 ---- alpine-2.10.unverified/alpine/reply.c 2013-01-11 16:20:11.000000000 -0700 -*************** -*** 1165,1173 **** - } - else if(!outgoing->newsgroups) - outgoing->newsgroups = cpystr(env->newsgroups); -- if(!IS_NEWS(ps_global->mail_stream)) -- q_status_message(SM_ORDER, 2, 3, -- _("Replying to message that MAY or MAY NOT have been posted to newsgroup")); - } - - return(ret); ---- 1165,1170 ---- -diff -rc alpine-2.10/imap/src/c-client/imap4r1.c alpine-2.10.unverified/imap/src/c-client/imap4r1.c -*** alpine-2.10/imap/src/c-client/imap4r1.c 2011-01-09 21:00:20.000000000 -0700 ---- alpine-2.10.unverified/imap/src/c-client/imap4r1.c 2013-01-11 16:20:11.000000000 -0700 -*************** -*** 4527,4532 **** ---- 4527,4533 ---- - if (*env) { /* need to merge this header into envelope? */ - if (!(*env)->newsgroups) { /* need Newsgroups? */ - (*env)->newsgroups = nenv->newsgroups; -+ (*env)->ngpathexists = nenv->ngpathexists; - nenv->newsgroups = NIL; - } - if (!(*env)->followup_to) { /* need Followup-To? */ -*************** -*** 4581,4586 **** ---- 4582,4588 ---- - if (oenv) { /* need to merge old envelope? */ - (*env)->newsgroups = oenv->newsgroups; - oenv->newsgroups = NIL; -+ (*env)->ngpathexists = oenv->ngpathexists; - (*env)->followup_to = oenv->followup_to; - oenv->followup_to = NIL; - (*env)->references = oenv->references; -diff -rc alpine-2.10/imap/src/c-client/mail.h alpine-2.10.unverified/imap/src/c-client/mail.h -*** alpine-2.10/imap/src/c-client/mail.h 2013-01-11 15:21:34.000000000 -0700 ---- alpine-2.10.unverified/imap/src/c-client/mail.h 2013-01-11 16:20:11.000000000 -0700 -*************** -*** 685,690 **** ---- 685,691 ---- - /* Message envelope */ - - typedef struct mail_envelope { -+ unsigned int ngpathexists : 1; /* newsgroups may be bogus */ - unsigned int incomplete : 1; /* envelope may be incomplete */ - unsigned int imapenvonly : 1; /* envelope only has IMAP envelope */ - char *remail; /* remail header if any */ -diff -rc alpine-2.10/imap/src/c-client/rfc822.c alpine-2.10.unverified/imap/src/c-client/rfc822.c -*** alpine-2.10/imap/src/c-client/rfc822.c 2011-01-09 21:00:20.000000000 -0700 ---- alpine-2.10.unverified/imap/src/c-client/rfc822.c 2013-01-11 16:20:11.000000000 -0700 -*************** -*** 128,133 **** ---- 128,134 ---- - ENVELOPE *env = (*en = mail_newenvelope ()); - BODY *body = bdy ? (*bdy = mail_newbody ()) : NIL; - long MIMEp = -1; /* flag that MIME semantics are in effect */ -+ long PathP = NIL; /* flag that a Path: was seen */ - parseline_t pl = (parseline_t) mail_parameters (NIL,GET_PARSELINE,NIL); - if (!host) host = BADHOST; /* make sure that host is non-null */ - while (i && *s != '\n') { /* until end of header */ -*************** -*** 230,235 **** ---- 231,239 ---- - *t++ = '\0'; - } - break; -+ case 'P': /* possible Path: */ -+ if (!strcmp (tmp+1,"ATH")) env->ngpathexists = T; -+ break; - case 'R': /* possible Reply-To: */ - if (!strcmp (tmp+1,"EPLY-TO")) - rfc822_parse_adrlist (&env->reply_to,d,host); -diff -rc alpine-2.10/pith/mailview.c alpine-2.10.unverified/pith/mailview.c -*** alpine-2.10/pith/mailview.c 2013-01-11 11:26:43.000000000 -0700 ---- alpine-2.10.unverified/pith/mailview.c 2013-01-11 16:20:11.000000000 -0700 -*************** -*** 2379,2386 **** - format_addr_string(s, n, sect, "Return-Path: ", e->return_path, - flags, oacs, pc); - -! if((which & FE_NEWSGROUPS) && e->newsgroups) - format_newsgroup_string("Newsgroups: ", e->newsgroups, flags, pc); - - if((which & FE_FOLLOWUPTO) && e->followup_to) - format_newsgroup_string("Followup-To: ", e->followup_to, flags, pc); ---- 2379,2399 ---- - format_addr_string(s, n, sect, "Return-Path: ", e->return_path, - flags, oacs, pc); - -! if((which & FE_NEWSGROUPS) && e->newsgroups){ -! int bogus = NIL; - format_newsgroup_string("Newsgroups: ", e->newsgroups, flags, pc); -+ if (!e->ngpathexists && e->message_id && -+ strncmp (e->message_id,"message_id,"message_id,"message_id,"message_id,"message_id,"followup_to) - format_newsgroup_string("Followup-To: ", e->followup_to, flags, pc); diff --git a/fix-implicit.patch b/fix-implicit.patch index 7f8d459..e45519c 100644 --- a/fix-implicit.patch +++ b/fix-implicit.patch @@ -1,5 +1,11 @@ ---- imap/src/mlock/mlock.c -+++ imap/src/mlock/mlock.c +--- + imap/src/mlock/mlock.c | 1 + + 1 file changed, 1 insertion(+) + +Index: alpine-2.11/imap/src/mlock/mlock.c +=================================================================== +--- alpine-2.11.orig/imap/src/mlock/mlock.c ++++ alpine-2.11/imap/src/mlock/mlock.c @@ -28,6 +28,7 @@ #include diff --git a/operation-may-be-undefined-warning.diff b/operation-may-be-undefined-warning.diff index b6eef31..2ef5489 100644 --- a/operation-may-be-undefined-warning.diff +++ b/operation-may-be-undefined-warning.diff @@ -1,6 +1,13 @@ ---- alpine/folder.c -+++ alpine/folder.c -@@ -6013,7 +6013,7 @@ +--- + alpine/folder.c | 2 +- + imap/src/c-client/mail.c | 10 ++++++++-- + 2 files changed, 9 insertions(+), 3 deletions(-) + +Index: alpine-2.11/alpine/folder.c +=================================================================== +--- alpine-2.11.orig/alpine/folder.c ++++ alpine-2.11/alpine/folder.c +@@ -6132,7 +6132,7 @@ folder_select_count(long int *count, int continue; case 14 : /* toggle comparison */ @@ -9,9 +16,11 @@ continue; case -1 : /* cancel */ ---- imap/src/c-client/mail.c -+++ imap/src/c-client/mail.c -@@ -2931,8 +2931,14 @@ +Index: alpine-2.11/imap/src/c-client/mail.c +=================================================================== +--- alpine-2.11.orig/imap/src/c-client/mail.c ++++ alpine-2.11/imap/src/c-client/mail.c +@@ -2940,8 +2940,14 @@ long mail_parse_date (MESSAGECACHE *elt, /* parse time */ d = strtoul (s+1,(char **) &s,10); if (*s != ':') return NIL;