summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.maildir149
-rw-r--r--alpine/Makefile.am1
-rw-r--r--alpine/Makefile.in1
-rw-r--r--alpine/adrbkcmd.c3
-rw-r--r--alpine/alpine.c56
-rw-r--r--alpine/arg.c31
-rw-r--r--alpine/arg.h1
-rw-r--r--alpine/busy.c14
-rw-r--r--alpine/confscroll.c132
-rw-r--r--alpine/confscroll.h2
-rw-r--r--alpine/dispfilt.c73
-rw-r--r--alpine/dispfilt.h2
-rw-r--r--alpine/folder.c34
-rw-r--r--alpine/imap.c79
-rw-r--r--alpine/imap.h3
-rw-r--r--alpine/keymenu.c47
-rw-r--r--alpine/keymenu.h13
-rw-r--r--alpine/mailcmd.c513
-rw-r--r--alpine/mailcmd.h13
-rw-r--r--alpine/mailindx.c130
-rw-r--r--alpine/mailindx.h2
-rw-r--r--alpine/mailpart.c7
-rw-r--r--alpine/mailview.c151
-rw-r--r--alpine/osdep/debuging.c3
-rw-r--r--alpine/osdep/execview.c2
-rw-r--r--alpine/osdep/termin.gen.c62
-rw-r--r--alpine/osdep/termin.gen.h1
-rw-r--r--alpine/osdep/termin.unx.c8
-rw-r--r--alpine/osdep/termout.unx.c8
-rw-r--r--alpine/radio.c2
-rw-r--r--alpine/reply.c280
-rw-r--r--alpine/reply.h2
-rw-r--r--alpine/roleconf.c20
-rw-r--r--alpine/send.c118
-rw-r--r--alpine/setup.c61
-rw-r--r--alpine/status.c6
-rw-r--r--autom4te.cache/output.020
-rw-r--r--autom4te.cache/output.120
-rw-r--r--autom4te.cache/requests158
-rw-r--r--autom4te.cache/traces.12
-rwxr-xr-xconfigure20
-rw-r--r--doc/alpine.12
-rw-r--r--imap/src/c-client/imap4r1.c2
-rw-r--r--imap/src/c-client/mail.c35
-rw-r--r--imap/src/c-client/mail.h9
-rw-r--r--imap/src/c-client/nntp.c5
-rw-r--r--imap/src/c-client/pop3.c3
-rw-r--r--imap/src/osdep/unix/Makefile8
-rw-r--r--imap/src/osdep/unix/dummy.c48
-rw-r--r--imap/src/osdep/unix/maildir.c2638
-rw-r--r--imap/src/osdep/unix/maildir.h226
-rw-r--r--imap/src/osdep/unix/os_cyg.h1
-rw-r--r--pico/basic.c378
-rw-r--r--pico/blddate.c8
-rw-r--r--pico/composer.c20
-rw-r--r--pico/display.c54
-rw-r--r--pico/ebind.h8
-rw-r--r--pico/edef.h2
-rw-r--r--pico/efunc.h12
-rw-r--r--pico/fileio.c6
-rw-r--r--pico/line.c8
-rw-r--r--pico/main.c24
-rw-r--r--pico/osdep/color.h4
-rw-r--r--pico/osdep/getkey.c10
-rw-r--r--pico/osdep/terminal.c13
-rw-r--r--pico/pico.c37
-rw-r--r--pico/pico.h3
-rw-r--r--pico/random.c5
-rw-r--r--pico/search.c67
-rw-r--r--pico/word.c334
-rw-r--r--pith/Makefile.am2
-rw-r--r--pith/Makefile.in5
-rw-r--r--pith/adrbklib.c10
-rw-r--r--pith/charconv/utf8.c50
-rw-r--r--pith/charconv/utf8.h1
-rw-r--r--pith/color.c113
-rw-r--r--pith/color.h19
-rw-r--r--pith/conf.c183
-rw-r--r--pith/conf.h57
-rw-r--r--pith/conftype.h34
-rw-r--r--pith/detoken.c33
-rw-r--r--pith/filter.c378
-rw-r--r--pith/filter.h1
-rw-r--r--pith/filttype.h2
-rw-r--r--pith/flag.c6
-rw-r--r--pith/imap.c11
-rw-r--r--pith/imap.h1
-rw-r--r--pith/indxtype.h34
-rw-r--r--pith/init.c3
-rw-r--r--pith/mailcap.c66
-rw-r--r--pith/mailcap.h1
-rw-r--r--pith/mailcmd.c435
-rw-r--r--pith/mailcmd.h8
-rw-r--r--pith/mailindx.c440
-rw-r--r--pith/mailindx.h3
-rw-r--r--pith/mailview.c465
-rw-r--r--pith/mailview.h16
-rw-r--r--pith/makefile.wnt4
-rw-r--r--pith/msgno.c3
-rw-r--r--pith/osdep/color.c1256
-rw-r--r--pith/osdep/color.h25
-rw-r--r--pith/pattern.c28
-rw-r--r--pith/pine.hlp1825
-rw-r--r--pith/pineelt.h1
-rw-r--r--pith/reply.c264
-rw-r--r--pith/save.c3
-rw-r--r--pith/send.c71
-rw-r--r--pith/send.h2
-rw-r--r--pith/sort.c88
-rw-r--r--pith/sort.h8
-rw-r--r--pith/state.c14
-rw-r--r--pith/state.h37
-rw-r--r--pith/store.c8
-rw-r--r--pith/string.c58
-rw-r--r--pith/string.h2
-rw-r--r--pith/text.c45
-rw-r--r--pith/thread.c787
-rw-r--r--pith/thread.h23
-rw-r--r--pith/url.c7
-rw-r--r--po/Makefile.in2
-rw-r--r--web/src/alpined.d/Makefile.am1
-rw-r--r--web/src/alpined.d/Makefile.in2
-rw-r--r--web/src/alpined.d/alpined.c4
123 files changed, 12257 insertions, 908 deletions
diff --git a/README.maildir b/README.maildir
new file mode 100644
index 00000000..b4ac49b1
--- /dev/null
+++ b/README.maildir
@@ -0,0 +1,149 @@
+---------------------------------------
+
+Maildir Driver for Alpine 2.0
+By Eduardo Chappa
+<chappa@gmx.com>
+
+---------------------------------------
+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
diff --git a/alpine/Makefile.am b/alpine/Makefile.am
index c80a97ff..76bef8e3 100644
--- a/alpine/Makefile.am
+++ b/alpine/Makefile.am
@@ -46,3 +46,4 @@ CLEANFILES = date.c
date.c:
echo "char datestamp[]="\"`date`\"";" > date.c
echo "char hoststamp[]="\"`hostname`\"";" >> date.c
+ cat ../patchlevel >> date.c
diff --git a/alpine/Makefile.in b/alpine/Makefile.in
index 4bb886ec..09719265 100644
--- a/alpine/Makefile.in
+++ b/alpine/Makefile.in
@@ -817,6 +817,7 @@ uninstall-am: uninstall-binPROGRAMS
date.c:
echo "char datestamp[]="\"`date`\"";" > date.c
echo "char hoststamp[]="\"`hostname`\"";" >> date.c
+ cat ../patchlevel >> date.c
# Tell versions [3.59,3.63) of GNU make to not export all variables.
# Otherwise a system limit (for SysV at least) may be exceeded.
diff --git a/alpine/adrbkcmd.c b/alpine/adrbkcmd.c
index 9320160d..ab3c981b 100644
--- a/alpine/adrbkcmd.c
+++ b/alpine/adrbkcmd.c
@@ -4128,6 +4128,8 @@ ab_compose_internal(BuildTo bldto, int allow_role)
* won't do anything, but will cause compose_mail to think there's
* already a role so that it won't try to confirm the default.
*/
+ if (ps_global->role)
+ fs_give((void **)&ps_global->role);
if(role)
role = copy_action(role);
else{
@@ -4135,6 +4137,7 @@ ab_compose_internal(BuildTo bldto, int allow_role)
memset((void *)role, 0, sizeof(*role));
role->nick = cpystr("Default Role");
}
+ ps_global->role = cpystr(role->nick);
}
compose_mail(addr, fcc, role, NULL, NULL);
diff --git a/alpine/alpine.c b/alpine/alpine.c
index 9e7298f2..838473ec 100644
--- a/alpine/alpine.c
+++ b/alpine/alpine.c
@@ -308,6 +308,7 @@ main(int argc, char **argv)
mail_parameters(NULL, SET_SENDCOMMAND, (void *) pine_imap_cmd_happened);
mail_parameters(NULL, SET_FREESTREAMSPAREP, (void *) sp_free_callback);
mail_parameters(NULL, SET_FREEELTSPAREP, (void *) free_pine_elt);
+ mail_parameters(NULL, SET_ERASEPASSWORD, (void *) pine_delete_pwd);
#ifdef SMIME
mail_parameters(NULL, SET_FREEBODYSPAREP, (void *) free_smime_body_sparep);
#endif
@@ -459,6 +460,11 @@ main(int argc, char **argv)
convert_args_to_utf8(pine_state, &args);
+ if (args.action == aaFolder && !args.data.folder &&
+ ps_global->send_immediately){
+ printf(_("No value for To: field specified\n"));
+ exit(-1);
+ }
if(args.action == aaFolder){
pine_state->beginning_of_month = first_run_of_month();
pine_state->beginning_of_year = first_run_of_year();
@@ -467,6 +473,7 @@ main(int argc, char **argv)
/* Set up optional for user-defined display filtering */
pine_state->tools.display_filter = dfilter;
pine_state->tools.display_filter_trigger = dfilter_trigger;
+ pine_state->tools.exec_rule = exec_function_rule;
#ifdef _WINDOWS
if(ps_global->install_flag){
@@ -558,6 +565,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))
@@ -677,6 +689,7 @@ main(int argc, char **argv)
/*--- output side ---*/
+ if (!ps_global->send_immediately){
rv = config_screen(&(pine_state->ttyo));
#ifndef _WINDOWS /* always succeeds under _WINDOWS */
if(rv){
@@ -717,12 +730,6 @@ main(int argc, char **argv)
/* initialize titlebar in case we use it */
set_titlebar("", NULL, NULL, NULL, NULL, 0, FolderName, 0, 0, NULL);
- /*
- * Prep storage object driver for PicoText
- */
- so_register_external_driver(pine_pico_get, pine_pico_give, pine_pico_writec, pine_pico_readc,
- pine_pico_puts, pine_pico_seek, NULL, NULL);
-
#ifdef DEBUG
if(ps_global->debug_imap > 4 || debug > 9){
q_status_message(SM_ORDER | SM_DING, 5, 9,
@@ -730,6 +737,19 @@ main(int argc, char **argv)
flush_status_messages(0);
}
#endif
+ }
+ else{
+ fake_config_screen(&(pine_state->ttyo));
+ init_folders(pine_state); /* digest folder spec's */
+ }
+
+ /*
+ * Prep storage object driver for PicoText
+ */
+ so_register_external_driver(pine_pico_get, pine_pico_give,
+ (args.noutf8 == 0 ? pine_pico_writec : pine_pico_writec_noucs),
+ (args.noutf8 == 0 ? pine_pico_readc : pine_pico_readc_noucs),
+ (args.noutf8 == 0 ? pine_pico_puts : pine_pico_puts_noucs), pine_pico_seek, NULL, NULL);
if(args.action == aaPrcCopy || args.action == aaAbookCopy){
int exit_val = -1;
@@ -905,6 +925,12 @@ main(int argc, char **argv)
int len, good_addr = 1;
int exit_val = 0;
BUILDER_ARG fcc;
+ ACTION_S *role = NULL;
+
+ if (pine_state->in_init_seq && pine_state->send_immediately
+ && (char) *pine_state->initial_cmds++ == '#'
+ && ++pine_state->initial_cmds_offset)
+ role_select_screen(pine_state, &role, 1);
if(pine_state->in_init_seq){
pine_state->in_init_seq = pine_state->save_in_init_seq = 0;
@@ -943,7 +969,7 @@ main(int argc, char **argv)
memset(&fcc, 0, sizeof(fcc));
if(good_addr){
- compose_mail(addr, fcc.tptr, NULL,
+ compose_mail(addr, fcc.tptr, role,
args.data.mail.attachlist, stdin_getc);
}
else{
@@ -977,6 +1003,7 @@ main(int argc, char **argv)
pine_state->mail_stream = NULL;
pine_state->mangled_screen = 1;
+ pine_state->subject = NULL;
if(args.action == aaURL){
url_tool_t f;
@@ -1092,6 +1119,7 @@ main(int argc, char **argv)
}
}
+ if (!pine_state->send_immediately)
fflush(stdout);
#if !defined(_WINDOWS) && !defined(LEAVEOUTFIFO)
@@ -2980,10 +3008,15 @@ process_init_cmds(struct pine *ps, char **list)
if(i > 0){
ps->initial_cmds = (int *)fs_get((i+1) * sizeof(int));
ps->free_initial_cmds = ps->initial_cmds;
+ ps->initial_cmds_backup = (int *)fs_get((i+1) * sizeof(int));
+ ps->free_initial_cmds_backup = ps->initial_cmds_backup;
for(j = 0; j < i; j++)
- ps->initial_cmds[j] = i_cmds[j];
-
- ps->initial_cmds[i] = 0;
+ ps->initial_cmds[j] = ps->initial_cmds_backup[j] = i_cmds[j];
+#define ctrl_x 24
+ if (i > 1)
+ ps->send_immediately = i_cmds[i - 2] == ctrl_x
+ && ((i_cmds[i - 1] == 'y') || (i_cmds[i-1] == 'Y'));
+ ps->initial_cmds[i] = ps->initial_cmds_backup[i] = 0;
ps->in_init_seq = ps->save_in_init_seq = 1;
}
}
@@ -3139,6 +3172,9 @@ goodnight_gracey(struct pine *pine_state, int exit_val)
extern KBESC_T *kbesc;
dprint((2, "goodnight_gracey:\n"));
+ strncpy(pine_state->cur_folder, pine_state->inbox_name,
+ sizeof(pine_state->cur_folder));
+ pine_state->cur_folder[sizeof(pine_state->cur_folder) - 1] = '\0';
/* We want to do this here before we close up the streams */
trim_remote_adrbks();
diff --git a/alpine/arg.c b/alpine/arg.c
index e23853c4..315649ba 100644
--- a/alpine/arg.c
+++ b/alpine/arg.c
@@ -60,6 +60,7 @@ static char args_err_missing_passfile[] = N_("missing argument for option \"-pas
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,11 +104,13 @@ N_(" -k \t\tKeys - Force use of function keys"),
N_(" -z \t\tSuspend - allow use of ^Z suspension"),
N_(" -r \t\tRestricted - can only send mail to oneself"),
N_(" -sort <sort>\tSort - Specify sort order of folder:"),
+N_(" -threadsort <sort>\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"),
N_(" -I <keystroke_list> Initial keystrokes to be executed"),
N_(" -n <number>\tEntry in index to begin on"),
+N_(" -noutf8\t Warns Alpine that piped input is not encoded in utf-8"),
N_(" -o \t\tReadOnly - Open first folder read-only"),
N_(" -conf\t\tConfiguration - Print out fresh global configuration. The"),
N_("\t\tvalues of your global configuration affect all Alpine users"),
@@ -192,6 +195,7 @@ pine_args(struct pine *pine_state, int argc, char **argv, ARGDATA_S *args)
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 +367,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;
@@ -469,6 +484,10 @@ Loop: while(--ac > 0)
goto Loop;
}
+ else if(strcmp(*av, "noutf8") == 0){
+ args->noutf8++;
+ goto Loop;
+ }
else if(strcmp(*av, "bail") == 0){
pine_state->exit_if_no_pinerc = 1;
goto Loop;
@@ -477,6 +496,12 @@ Loop: while(--ac > 0)
do_version = 1;
goto Loop;
}
+ else if(strcmp(*av, "subject") == 0){
+ if(--ac){
+ pine_state->subject = cpystr(*++av);
+ }
+ goto Loop;
+ }
#ifdef _WINDOWS
else if(strcmp(*av, "install") == 0){
pine_state->install_flag = 1;
@@ -827,14 +852,14 @@ Loop: while(--ac > 0)
exit(-1);
if(do_version){
- extern char datestamp[], hoststamp[];
+ extern char datestamp[], hoststamp[], plevstamp[];
char rev[128];
- snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Alpine %s (%s %s) built %s on %s",
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Alpine %s (%s %s) built %s on %s, using patchlevel %s.",
ALPINE_VERSION,
SYSTYPE ? SYSTYPE : "?",
get_alpine_revision_string(rev, sizeof(rev)),
- datestamp, hoststamp);
+ datestamp, hoststamp, plevstamp);
tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
display_args_err(tmp_20k_buf, NULL, 0);
exit(0);
diff --git a/alpine/arg.h b/alpine/arg.h
index 339e793b..4faddfe9 100644
--- a/alpine/arg.h
+++ b/alpine/arg.h
@@ -27,6 +27,7 @@
typedef struct argdata {
enum {aaFolder = 0, aaMore, aaURL, aaMail,
aaPrcCopy, aaAbookCopy} action;
+ int noutf8;
union {
char *folder;
char *file;
diff --git a/alpine/busy.c b/alpine/busy.c
index a0c71ab0..b2fd1869 100644
--- a/alpine/busy.c
+++ b/alpine/busy.c
@@ -226,7 +226,7 @@ busy_cue(char *msg, percent_done_t pc_f, int delay)
add_review_message(buf, -1);
}
- else{
+ else if (!ps_global->send_immediately){
q_status_message(SM_ORDER, 0, 1, progress);
/*
@@ -238,8 +238,8 @@ busy_cue(char *msg, percent_done_t pc_f, int delay)
*/
display_message('x');
}
-
- fflush(stdout);
+ if (!ps_global->send_immediately)
+ fflush(stdout);
}
/*
@@ -287,7 +287,8 @@ busy_cue(char *msg, percent_done_t pc_f, int delay)
(*ap)->cf = done_busy_cue;
ap = &(*ap)->next;
- start_after(a); /* launch cue handler */
+ if(!ps_global->send_immediately)
+ start_after(a); /* launch cue handler */
#ifdef _WINDOWS
mswin_setcursor(MSWIN_CURSOR_BUSY);
@@ -436,6 +437,11 @@ done_busy_cue(void *data)
{
int space_left, slots_used;
+ if (ps_global->send_immediately){
+ mark_status_dirty();
+ return;
+ }
+
if(final_message && final_message_pri >= 0){
char progress[MAX_SCREEN_COLS+1];
diff --git a/alpine/confscroll.c b/alpine/confscroll.c
index b4aa3218..11473d27 100644
--- a/alpine/confscroll.c
+++ b/alpine/confscroll.c
@@ -51,6 +51,7 @@ static char rcsid[] = "$Id: confscroll.c 1169 2008-08-27 06:42:06Z hubert@u.wash
#include "../pith/tempfile.h"
#include "../pith/pattern.h"
#include "../pith/charconv/utf8.h"
+#include "../pith/rules.h"
#define CONFIG_SCREEN_HELP_TITLE _("HELP FOR SETUP CONFIGURATION")
@@ -139,7 +140,7 @@ char *yesno_pretty_value(struct pine *, CONF_S *);
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 +288,8 @@ set_radio_pretty_vals(struct pine *ps, CONF_S **cl)
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;
@@ -463,7 +465,7 @@ conf_scroll_screen(struct pine *ps, OPT_SCREEN_S *screen, CONF_S *start_line, ch
char tmp[MAXPATH+1];
char *utf8str;
UCS ch = 'x';
- int cmd, i, j, done = 0, changes = 0;
+ int cmd, i, j, k = 1, done = 0, changes = 0;
int retval = 0;
int km_popped = 0, stay_in_col = 0;
struct key_menu *km = NULL;
@@ -512,6 +514,7 @@ conf_scroll_screen(struct pine *ps, OPT_SCREEN_S *screen, CONF_S *start_line, ch
}
/*----------- Check for new mail -----------*/
+ if (!ps->send_immediately){
if(new_mail(0, NM_TIMING(ch), NM_STATUS_MSG | NM_DEFER_SORT) >= 0)
ps->mangled_header = 1;
@@ -541,6 +544,7 @@ conf_scroll_screen(struct pine *ps, OPT_SCREEN_S *screen, CONF_S *start_line, ch
mark_status_unknown();
}
+ } /* send_immediately */
if(ps->mangled_footer || km != screen->current->keymenu){
bitmap_t bitmap;
@@ -612,6 +616,7 @@ conf_scroll_screen(struct pine *ps, OPT_SCREEN_S *screen, CONF_S *start_line, ch
}
}
+ if(!ps_global->send_immediately){
MoveCursor(cursor_pos.row, cursor_pos.col);
#ifdef MOUSE
mouse_in_content(KEY_MOUSE, -1, -1, 0, 0); /* prime the handler */
@@ -630,6 +635,14 @@ conf_scroll_screen(struct pine *ps, OPT_SCREEN_S *screen, CONF_S *start_line, ch
#ifdef _WINDOWS
mswin_setscrollcallback(NULL);
#endif
+ } /* send_immediately */
+
+ if (ps->send_immediately){
+ ps_global->dont_use_init_cmds = 0;
+ process_config_input(&ch);
+ if (ch == '\030') /* ^X, bye */
+ goto end;
+ }
cmd = menu_command(ch, km);
@@ -1383,7 +1396,7 @@ no_down:
break;
}
}
-
+end:
screen->current = first_confline(screen->current);
free_conflines(&screen->current);
return(retval);
@@ -1552,6 +1565,10 @@ text_toolit(struct pine *ps, int cmd, CONF_S **cl, unsigned int flags, int look_
lowrange = 1;
hirange = MAX_FILLCOL;
}
+ else if((*cl)->var == &ps->vars[V_SLEEP]){
+ lowrange = 0;
+ hirange = 120;
+ }
else if((*cl)->var == &ps->vars[V_OVERLAP]
|| (*cl)->var == &ps->vars[V_MARGIN]){
lowrange = 0;
@@ -2427,6 +2444,9 @@ delete:
* Now go and set the current_val based on user_val changes
* above. Turn off command line settings...
*/
+ set_current_val((*cl)->var,
+ (strcmp((*cl)->var->name,"key-definition-rules") ? TRUE : FALSE),
+ FALSE);
set_current_val((*cl)->var, TRUE, FALSE);
fix_side_effects(ps, (*cl)->var, 0);
@@ -2923,7 +2943,7 @@ radiobutton_tool(struct pine *ps, int cmd, CONF_S **cl, unsigned int flags)
}
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 +2952,37 @@ radiobutton_tool(struct pine *ps, int cmd, CONF_S **cl, unsigned int flags)
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 +3845,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 +4378,14 @@ color_pretty_value(struct pine *ps, CONF_S *cl)
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 +4435,7 @@ generalized_sort_pretty_value(struct pine *ps, CONF_S *cl, int default_ok)
}
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 +4446,9 @@ generalized_sort_pretty_value(struct pine *ps, CONF_S *cl, int default_ok)
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 +4466,7 @@ generalized_sort_pretty_value(struct pine *ps, CONF_S *cl, int default_ok)
}
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 +4477,7 @@ generalized_sort_pretty_value(struct pine *ps, CONF_S *cl, int default_ok)
}
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);
@@ -5161,6 +5214,30 @@ fix_side_effects(struct pine *ps, struct variable *var, int revert)
var == &ps->vars[V_ABOOK_FORMATS]){
addrbook_reset();
}
+ else if(var == &ps->vars[V_INDEX_RULES]){
+ if(ps_global->rule_list)
+ free_parsed_rule_list(&ps_global->rule_list);
+ create_rule_list(ps->vars);
+ reset_index_format();
+ clear_index_cache(ps->mail_stream, 0);
+ }
+ else if(var == &ps->vars[V_COMPOSE_RULES] ||
+ var == &ps->vars[V_FORWARD_RULES] ||
+ var == &ps->vars[V_KEY_RULES] ||
+ var == &ps->vars[V_REPLACE_RULES] ||
+ var == &ps->vars[V_REPLY_INDENT_RULES] ||
+ var == &ps->vars[V_REPLY_LEADIN_RULES] ||
+ var == &ps->vars[V_RESUB_RULES] ||
+ var == &ps->vars[V_SAVE_RULES] ||
+ var == &ps->vars[V_SMTP_RULES] ||
+ var == &ps->vars[V_SORT_RULES] ||
+ var == &ps->vars[V_STARTUP_RULES] ||
+ var == &ps->vars[V_THREAD_DISP_STYLE_RULES] ||
+ var == &ps->vars[V_THREAD_INDEX_STYLE_RULES]){
+ if(ps_global->rule_list)
+ free_parsed_rule_list(&ps_global->rule_list);
+ create_rule_list(ps->vars);
+ }
else if(var == &ps->vars[V_INDEX_FORMAT]){
reset_index_format();
clear_index_cache(ps->mail_stream, 0);
@@ -5183,6 +5260,9 @@ fix_side_effects(struct pine *ps, struct variable *var, int revert)
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,
@@ -5215,6 +5295,16 @@ fix_side_effects(struct pine *ps, struct variable *var, int revert)
else
ps->viewer_overlap = old_value;
}
+ else if(var == &ps->vars[V_SLEEP]){
+ int old_value = ps->sleep;
+
+ if(SVAR_SLEEP(ps, old_value, tmp_20k_buf, SIZEOF_20KBUF)){
+ if(!revert)
+ q_status_message(SM_ORDER, 3, 5, tmp_20k_buf);
+ }
+ else
+ ps->sleep = old_value;
+ }
#ifdef SMIME
else if(smime_related_var(ps, var)){
smime_deinit();
@@ -5496,6 +5586,12 @@ fix_side_effects(struct pine *ps, struct variable *var, int revert)
(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);
@@ -5545,9 +5641,15 @@ fix_side_effects(struct pine *ps, struct variable *var, int revert)
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]){
diff --git a/alpine/confscroll.h b/alpine/confscroll.h
index 4315c8e1..95925cc2 100644
--- a/alpine/confscroll.h
+++ b/alpine/confscroll.h
@@ -95,7 +95,7 @@ int checkbox_tool(struct pine *, int, CONF_S **, unsigned);
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);
diff --git a/alpine/dispfilt.c b/alpine/dispfilt.c
index 7c93938b..7a386a1c 100644
--- a/alpine/dispfilt.c
+++ b/alpine/dispfilt.c
@@ -243,6 +243,19 @@ expand_filter_tokens(char *filter, ENVELOPE *env, char **tmpf, char **resultf,
fs_give((void **)q);
*q = rl ? rl : cpystr("");
}
+ else if(!strcmp(*q, "_ADDRESS_")){
+ char *r = NULL;
+
+ if(env && env->from && env->from->mailbox && env->from->host){
+ size_t l;
+ l = strlen(env->from->mailbox) + strlen(env->from->host) + 1;
+ r = (char *) fs_get((l+1) * sizeof(char));
+ snprintf(r, l+1, "%s@%s", env->from->mailbox, env->from->host);
+ }
+
+ fs_give((void **)q);
+ *q = r ? r : cpystr("");
+ }
else if(!strcmp(*q, "_TMPFILE_")){
if(!tfn){
tfn = temp_nam(NULL, "sf"); /* send filter file */
@@ -461,3 +474,63 @@ df_valid_test(struct mail_bodystruct *body, char *test)
return(passed);
}
+
+char *
+exec_function_rule(char *rawcmd, gf_io_t input_gc, gf_io_t output_pc)
+{
+ char *status = NULL, *cmd, *tmpfile = NULL;
+
+ if((cmd = expand_filter_tokens(rawcmd,NULL,&tmpfile,NULL,NULL,NULL,NULL, NULL)) != NULL){
+ suspend_busy_cue();
+ ps_global->mangled_screen = 1;
+ if(tmpfile){
+ PIPE_S *filter_pipe;
+ FILE *fp;
+ gf_io_t gc, pc;
+ STORE_S *tmpf_so;
+
+ /* write the tmp file */
+ if((tmpf_so = so_get(FileStar, tmpfile, WRITE_ACCESS|OWNER_ONLY|WRITE_TO_LOCALE)) != NULL){
+ /* copy input to tmp file */
+ gf_set_so_writec(&pc, tmpf_so);
+ gf_filter_init();
+ status = gf_pipe(input_gc, pc);
+ gf_clear_so_writec(tmpf_so);
+ if(so_give(&tmpf_so) != 0 && status == NULL)
+ status = error_description(errno);
+
+ /* prepare the terminal in case the filter uses it */
+ if(status == NULL){
+ if((filter_pipe = open_system_pipe(cmd, NULL, NULL,
+ PIPE_USER|PIPE_PROT|PIPE_NOSHELL|PIPE_SILENT,
+ 0, pipe_callback, NULL)) != NULL){
+ if(close_system_pipe(&filter_pipe, NULL, pipe_callback) == 0){
+ /* pull result out of tmp file */
+ if((fp = our_fopen(tmpfile, "rb")) != NULL){
+ gf_set_readc(&gc, fp, 0L, FileStar, READ_FROM_LOCALE);
+ gf_filter_init();
+ status = gf_pipe(gc, output_pc);
+ fclose(fp);
+ }
+ else
+ status = "Can't read result of EXEC command";
+ }
+ else
+ status = "EXEC command command returned error.";
+ }
+ else
+ status = "Can't open pipe for EXEC command";
+ }
+
+ our_unlink(tmpfile);
+ }
+ else
+ status = "Can't open EXEC command tmp file";
+ }
+
+ resume_busy_cue(0);
+ fs_give((void **)&cmd);
+ }
+
+ return(status);
+}
diff --git a/alpine/dispfilt.h b/alpine/dispfilt.h
index 0b42010d..a890b19c 100644
--- a/alpine/dispfilt.h
+++ b/alpine/dispfilt.h
@@ -24,7 +24,7 @@ char *dfilter_trigger(BODY *, char *, size_t);
char *expand_filter_tokens(char *, ENVELOPE *, char **, char **, char **, int *, int *, int *);
char *filter_session_key(void);
char *filter_data_file(int);
-
+char *exec_function_rule(char *, gf_io_t, gf_io_t);
#endif /* PINE_DISPFILT_INCLUDED */
diff --git a/alpine/folder.c b/alpine/folder.c
index 31d2031e..59589011 100644
--- a/alpine/folder.c
+++ b/alpine/folder.c
@@ -247,7 +247,7 @@ folder_screen(struct pine *ps)
dprint((1, "=== folder_screen called ====\n"));
mailcap_free(); /* free resources we won't be using for a while */
ps->next_screen = SCREEN_FUN_NULL;
-
+ strcpy(ps->screen_name, "folder");
/* Initialize folder state and dispatches */
memset(&fs, 0, sizeof(FSTATE_S));
fs.context = cntxt;
@@ -344,6 +344,7 @@ folder_screen(struct pine *ps)
pine_mail_close(*fs.cache_streamp);
ps->prev_screen = folder_screen;
+ strcpy(ps->screen_name, "unknown");
}
@@ -6342,11 +6343,17 @@ folder_delimiter(char *folder)
char *
next_folder(MAILSTREAM **streamp, char *next, size_t nextlen, char *current, CONTEXT_S *cntxt, long int *find_recent, int *did_cancel)
{
- int index, recent = 0, failed_status = 0, try_fast;
+ int index, recent = 0, failed_status = 0, try_fast, done = 0;
char prompt[128];
FOLDER_S *f = NULL;
char tmp[MAILTMPLEN];
-
+ char *test_current = cpystr(current);
+ int cur_indx = folder_index(ps_global->cur_folder, cntxt, FI_FOLDER);
+ int loop = !strcmp(next, ps_global->cur_folder) ? 0 :
+ (folder_index(test_current, cntxt, FI_FOLDER) <= cur_indx
+ ? 1 : 0);
+ int last = !strcmp(ps_global->cur_folder, ps_global->inbox_name)
+ ? 1 : cur_indx;
/* note: find_folders may assign "stream" */
build_folder_list(streamp, cntxt, NULL, NULL,
@@ -6357,7 +6364,9 @@ next_folder(MAILSTREAM **streamp, char *next, size_t nextlen, char *current, CON
if(find_recent)
*find_recent = 0L;
- for(index = folder_index(current, cntxt, FI_FOLDER) + 1;
+
+find_new_message:
+ for(index = folder_index(test_current, cntxt, FI_FOLDER) + 1;
index > 0
&& index < folder_total(FOLDERS(cntxt))
&& (f = folder_entry(index, FOLDERS(cntxt)))
@@ -6368,6 +6377,11 @@ next_folder(MAILSTREAM **streamp, char *next, size_t nextlen, char *current, CON
int rv, we_cancel = 0, match;
char msg_buf[MAX_BM+1];
+ if (loop && (index == last)){
+ done++;
+ break;
+ }
+
/* must be a folder and it can't be the current one */
if(ps_global->context_current == ps_global->context_list
&& !strcmp(ps_global->cur_folder, FLDR_NAME(f)))
@@ -6535,12 +6549,24 @@ next_folder(MAILSTREAM **streamp, char *next, size_t nextlen, char *current, CON
if(f && (!find_recent || recent)){
strncpy(next, FLDR_NAME(f), nextlen);
next[nextlen-1] = '\0';
+ done++;
}
else if(nextlen > 0)
*next = '\0';
+ if (!done && F_ON(F_AUTO_CIRCULAR_TAB,ps_global)
+ && strcmp(test_current,ps_global->inbox_name)){
+ done++; loop++;
+ if (test_current)
+ fs_give((void **)&test_current);
+ test_current = cpystr(ps_global->inbox_name);
+ goto find_new_message;
+ }
+
/* BUG: how can this be made smarter so we cache the list? */
free_folder_list(cntxt);
+ if (test_current)
+ fs_give((void **)&test_current);
return((*next) ? next : NULL);
}
diff --git a/alpine/imap.c b/alpine/imap.c
index 074b9f6d..e43d3e2e 100644
--- a/alpine/imap.c
+++ b/alpine/imap.c
@@ -2040,6 +2040,83 @@ passfile_name(char *pinerc, char *path, size_t len)
#endif /* PASSFILE */
+void
+pine_delete_pwd(NETMBX *mb, char *user)
+{
+ char hostlist0[MAILTMPLEN], hostlist1[MAILTMPLEN];
+ char port[20], non_def_port[20], insecure[20];
+ STRLIST_S hostlist[2];
+ MMLOGIN_S *l;
+ struct servent *sv;
+
+
+ dprint((9, "pine_delete_pwd\n"));
+
+ /* do not invalidate password on cancel */
+ if(ps_global->user_says_cancel != 0)
+ return;
+
+ /* setup hostlist */
+ non_def_port[0] = '\0';
+ if(mb->port && mb->service &&
+ (sv = getservbyname(mb->service, "tcp")) &&
+ (mb->port != ntohs(sv->s_port))){
+ snprintf(non_def_port, sizeof(non_def_port), ":%lu", mb->port);
+ non_def_port[sizeof(non_def_port)-1] = '\0';
+ dprint((9, "mm_login: using non-default port=%s\n",
+ non_def_port ? non_def_port : "?"));
+ }
+
+ if(*non_def_port){
+ strncpy(hostlist0, mb->host, sizeof(hostlist0)-1);
+ hostlist0[sizeof(hostlist0)-1] = '\0';
+ strncat(hostlist0, non_def_port, sizeof(hostlist0)-strlen(hostlist0)-1);
+ hostlist0[sizeof(hostlist0)-1] = '\0';
+ hostlist[0].name = hostlist0;
+ if(mb->orighost && mb->orighost[0] && strucmp(mb->host, mb->orighost)){
+ strncpy(hostlist1, mb->orighost, sizeof(hostlist1)-1);
+ hostlist1[sizeof(hostlist1)-1] = '\0';
+ strncat(hostlist1, non_def_port, sizeof(hostlist1)-strlen(hostlist1)-1);
+ hostlist1[sizeof(hostlist1)-1] = '\0';
+ hostlist[0].next = &hostlist[1];
+ hostlist[1].name = hostlist1;
+ hostlist[1].next = NULL;
+ }
+ else
+ hostlist[0].next = NULL;
+ }
+ else{
+ hostlist[0].name = mb->host;
+ if(mb->orighost && mb->orighost[0] && strucmp(mb->host, mb->orighost)){
+ hostlist[0].next = &hostlist[1];
+ hostlist[1].name = mb->orighost;
+ hostlist[1].next = NULL;
+ }
+ else
+ hostlist[0].next = NULL;
+ }
+
+ /* delete it from all lists */
+
+ for(l = mm_login_list; l; l = l->next)
+ if(imap_same_host(l->hosts, hostlist)
+ && !strcmp(l->user, user ? user : "")
+ && l->passwd){
+ l->invalidpwd = 1;
+ break;
+ }
+
+#ifdef LOCAL_PASSWD_CACHE
+ for(l = passfile_cache; l; l = l->next)
+ if(imap_same_host(l->hosts, hostlist)
+ && !strcmp(l->user, user ? user : "")
+ && l->passwd){
+ l->invalidpwd = 1;
+ break;
+ }
+ write_passfile(ps_global->pinerc, passfile_cache); /* blast it from passfile */
+#endif
+}
#ifdef LOCAL_PASSWD_CACHE
@@ -2602,6 +2679,8 @@ write_passfile(pinerc, l)
#endif /* SMIME */
for(n = 0; l; l = l->next, n++){
+ if(l->invalidpwd == 1) /* if password is invalid, do not save it */
+ continue;
/*** do any necessary ENcryption here ***/
snprintf(tmp, sizeof(tmp), "%s\t%s\t%s\t%d%s%s\n", l->passwd, l->user,
l->hosts->name, l->altflag,
diff --git a/alpine/imap.h b/alpine/imap.h
index b254fb53..378e687a 100644
--- a/alpine/imap.h
+++ b/alpine/imap.h
@@ -31,7 +31,8 @@ char *pine_newsrcquery(MAILSTREAM *, char *, char *);
int url_local_certdetails(char *);
void pine_sslfailure(char *, char *, unsigned long);
void mm_expunged_current(long unsigned int);
-
+void pine_delete_pwd(NETMBX *mb, char *user);
+
#ifdef LOCAL_PASSWD_CACHE
int get_passfile_passwd(char *, char *, char *, STRLIST_S *, int);
int is_using_passfile();
diff --git a/alpine/keymenu.c b/alpine/keymenu.c
index 195ae73c..a37304f9 100644
--- a/alpine/keymenu.c
+++ b/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);
diff --git a/alpine/keymenu.h b/alpine/keymenu.h
index 0e1f7618..84fc0422 100644
--- a/alpine/keymenu.h
+++ b/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
diff --git a/alpine/mailcmd.c b/alpine/mailcmd.c
index 7388005c..fc25795d 100644
--- a/alpine/mailcmd.c
+++ b/alpine/mailcmd.c
@@ -73,6 +73,7 @@ static char rcsid[] = "$Id: mailcmd.c 1266 2009-07-14 18:39:12Z hubert@u.washing
#include "../pith/tempfile.h"
#include "../pith/search.h"
#include "../pith/margin.h"
+#include "../pith/rules.h"
#ifdef _WINDOWS
#include "../pico/osdep/mswin.h"
#endif
@@ -113,7 +114,7 @@ int select_by_thread(MAILSTREAM *, MSGNO_S *, SEARCHSET **);
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);
@@ -974,7 +975,7 @@ nfolder:
state->context_current, &recent_cnt,
F_ON(F_TAB_NO_CONFIRM,state)
? NULL : &did_cancel))){
- if(!in_inbox){
+ if(!in_inbox && F_OFF(F_AUTO_CIRCULAR_TAB,state)){
static ESCKEY_S inbox_opt[] = {
{'y', 'y', "Y", N_("Yes")},
{'n', 'n', "N", N_("No")},
@@ -1335,7 +1336,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 +1354,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;
@@ -2594,6 +2607,9 @@ role_compose(struct pine *state)
role->nick = cpystr("Default Role");
}
+ if(state->role)
+ fs_give((void **)&state->role);
+ state->role = cpystr(role->nick); /* remember the role */
state->redrawer = NULL;
switch(action){
case 'c':
@@ -2644,12 +2660,12 @@ save_prompt(struct pine *state, CONTEXT_S **cntxt, char *nfldr, size_t len_nfldr
char *nmsgs, ENVELOPE *env, long int rawmsgno, char *section,
SaveDel *dela, SavePreserveOrder *prea)
{
- int rc, ku = -1, n, flags, last_rc = 0, saveable_count = 0, done = 0;
+ int rc, ku = -1, n = 0, flags, last_rc = 0, saveable_count = 0, done = 0;
int delindex, preindex, r;
char prompt[6*MAX_SCREEN_COLS+1], *p, expanded[MAILTMPLEN];
char *buf = tmp_20k_buf;
char shortbuf[200];
- char *folder;
+ char *folder, folder2[MAXPATH];
HelpType help;
SaveDel del = DontAsk;
SavePreserveOrder pre = DontAskPreserve;
@@ -2657,6 +2673,7 @@ save_prompt(struct pine *state, CONTEXT_S **cntxt, char *nfldr, size_t len_nfldr
static HISTORY_S *history = NULL;
CONTEXT_S *tc;
ESCKEY_S ekey[10];
+ RULE_RESULT *rule;
if(!cntxt)
panic("no context ptr in save_prompt");
@@ -2666,6 +2683,15 @@ save_prompt(struct pine *state, CONTEXT_S **cntxt, char *nfldr, size_t len_nfldr
if(!(folder = save_get_default(state, env, rawmsgno, section, cntxt)))
return(0); /* message expunged! */
+ if (rule = get_result_rule(V_SAVE_RULES, FOR_SAVE, env)){
+ strncpy(folder2,rule->result,sizeof(folder2)-1);
+ folder2[sizeof(folder2)-1] = '\0';
+ folder = folder2;
+ if (rule->result)
+ fs_give((void **)&rule->result);
+ fs_give((void **)&rule);
+ }
+
/* how many context's can be saved to... */
for(tc = state->context_list; tc; tc = tc->next)
if(!NEWS_TEST(tc))
@@ -3174,6 +3200,10 @@ cmd_expunge(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap)
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 +3298,9 @@ cmd_expunge(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap)
*/
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)
@@ -3349,6 +3382,9 @@ save_size_changed_prompt(long msgno, int flags)
{-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");
@@ -6945,7 +6981,7 @@ select_by_current(struct pine *state, MSGNO_S *msgmap, CmdWhere in_index)
* 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 +7037,7 @@ select_by_current(struct pine *state, MSGNO_S *msgmap, CmdWhere in_index)
----*/
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 +7189,19 @@ apply_command(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap,
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;
@@ -7189,7 +7235,7 @@ apply_command(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap,
int
select_by_number(MAILSTREAM *stream, MSGNO_S *msgmap, SEARCHSET **limitsrch)
{
- int r, end;
+ int r, end, cur;
long n1, n2, raw;
char number1[16], number2[16], numbers[80], *p, *t;
HelpType help;
@@ -7236,7 +7282,7 @@ select_by_number(MAILSTREAM *stream, MSGNO_S *msgmap, SEARCHSET **limitsrch)
*t = '\0';
- end = 0;
+ end = cur = 0;
if(number1[0] == '\0'){
if(*p == '-'){
q_status_message1(SM_ORDER | SM_DING, 0, 2,
@@ -7248,6 +7294,14 @@ select_by_number(MAILSTREAM *stream, MSGNO_S *msgmap, SEARCHSET **limitsrch)
end = 1;
p += strlen("end");
}
+ else if(!strucmp(p, "$")){
+ end = 1;
+ p++;
+ }
+ else if(!struncmp(p, ".",1)){
+ cur = 1;
+ p++;
+ }
else{
q_status_message1(SM_ORDER | SM_DING, 0, 2,
_("Invalid message number: %s"), numbers);
@@ -7257,6 +7311,8 @@ select_by_number(MAILSTREAM *stream, MSGNO_S *msgmap, SEARCHSET **limitsrch)
if(end)
n1 = mn_get_total(msgmap);
+ else if(cur)
+ n1 = mn_get_cur(msgmap);
else if((n1 = atol(number1)) < 1L || n1 > mn_get_total(msgmap)){
q_status_message1(SM_ORDER | SM_DING, 0, 2,
_("\"%s\" out of message number range"),
@@ -7271,12 +7327,20 @@ select_by_number(MAILSTREAM *stream, MSGNO_S *msgmap, SEARCHSET **limitsrch)
*t = '\0';
- end = 0;
+ end = cur = 0;
if(number2[0] == '\0'){
if(!strucmp("end", p)){
end = 1;
p += strlen("end");
}
+ else if(!strucmp(p, "$")){
+ end = 1;
+ p++;
+ }
+ else if(!struncmp(p, ".",1)){
+ cur = 1;
+ p++;
+ }
else{
q_status_message1(SM_ORDER | SM_DING, 0, 2,
_("Invalid number range, missing number after \"-\": %s"),
@@ -7287,6 +7351,8 @@ select_by_number(MAILSTREAM *stream, MSGNO_S *msgmap, SEARCHSET **limitsrch)
if(end)
n2 = mn_get_total(msgmap);
+ else if(cur)
+ n2 = mn_get_cur(msgmap);
else if((n2 = atol(number2)) < 1L || n2 > mn_get_total(msgmap)){
q_status_message1(SM_ORDER | SM_DING, 0, 2,
_("\"%s\" out of message number range"),
@@ -9014,10 +9080,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 +9116,26 @@ select_sort(struct pine *state, int ql, SortOrder *sort, int *rev)
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 +9159,17 @@ select_sort(struct pine *state, int ql, SortOrder *sort, int *rev)
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 +9554,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, 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 --git a/alpine/mailcmd.h b/alpine/mailcmd.h
index c86ef7cd..2be89e40 100644
--- a/alpine/mailcmd.h
+++ b/alpine/mailcmd.h
@@ -84,7 +84,7 @@ char *broach_folder(int, int, int *, CONTEXT_S **);
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 */
diff --git a/alpine/mailindx.c b/alpine/mailindx.c
index 01e01558..263825d2 100644
--- a/alpine/mailindx.c
+++ b/alpine/mailindx.c
@@ -229,6 +229,8 @@ mail_index_screen(struct pine *state)
state->prev_screen = mail_index_screen;
state->next_screen = SCREEN_FUN_NULL;
+ setup_threading_display_style();
+
if(THRD_AUTO_VIEW()
&& sp_viewing_a_thread(state->mail_stream)
&& state->view_skipped_index
@@ -240,10 +242,14 @@ mail_index_screen(struct pine *state)
adjust_cur_to_visible(state->mail_stream, state->msgmap);
+ strcpy(state->screen_name,"index");
+
if(THRD_INDX())
thread_index_screen(state);
else
index_index_screen(state);
+
+ strcpy(state->screen_name,"unknown");
}
@@ -561,6 +567,7 @@ index_lister(struct pine *state, CONTEXT_S *cntxt, char *folder, MAILSTREAM *str
/*---------- Scroll line up ----------*/
case MC_CHARUP :
+previtem:
(void) process_cmd(state, stream, msgmap, MC_PREVITEM,
(style == MsgIndex
|| style == MultiMsgIndex
@@ -578,6 +585,7 @@ index_lister(struct pine *state, CONTEXT_S *cntxt, char *folder, MAILSTREAM *str
/*---------- 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 +803,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 +854,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 +963,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,
+ 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,13 +1082,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 +2779,7 @@ top_ent_calc(MAILSTREAM *stream, MSGNO_S *msgs, long int at_top, long int lines_
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 +2848,12 @@ top_ent_calc(MAILSTREAM *stream, MSGNO_S *msgs, long int at_top, long int lines_
/* 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 +2961,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 +3010,7 @@ thread_command(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap,
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 +3504,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();
diff --git a/alpine/mailindx.h b/alpine/mailindx.h
index 3b4291c2..a1627c2f 100644
--- a/alpine/mailindx.h
+++ b/alpine/mailindx.h
@@ -103,7 +103,7 @@ int truncate_subj_and_from_strings(void);
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);
diff --git a/alpine/mailpart.c b/alpine/mailpart.c
index 8286d79d..63427744 100644
--- a/alpine/mailpart.c
+++ b/alpine/mailpart.c
@@ -2146,6 +2146,11 @@ display_attachment(long int msgno, ATTACH_S *a, int flags)
return(1);
}
+ /* ok, we have a filename. Now check if there is a template, and if
+ * so, rename the file accordingly
+ */
+ filename = mc_template(filename, a->body, a->can_display & MCD_EXT_PROMPT);
+
if((store = so_get(FileStar, filename, WRITE_ACCESS|OWNER_ONLY)) == NULL){
q_status_message2(SM_ORDER | SM_DING, 3, 5,
_("Error \"%s\", Can't write file %s"),
@@ -3308,7 +3313,7 @@ reply_msg_att(MAILSTREAM *stream, long int msgno, ATTACH_S *a)
/*
* For consistency, the first question is always "include text?"
*/
- if((include_text = reply_text_query(ps_global, 1, &prefix)) >= 0
+ if((include_text = reply_text_query(ps_global, 1, NULL, &prefix)) >= 0
&& reply_news_test(a->body->nested.msg->env, outgoing) > 0
&& reply_harvest(ps_global, msgno, a->number,
a->body->nested.msg->env, &saved_from,
diff --git a/alpine/mailview.c b/alpine/mailview.c
index 5193118f..41aeea5a 100644
--- a/alpine/mailview.c
+++ b/alpine/mailview.c
@@ -45,6 +45,7 @@ static char rcsid[] = "$Id: mailview.c 1266 2009-07-14 18:39:12Z hubert@u.washin
#include "dispfilt.h"
#include "busy.h"
#include "smime.h"
+#include "roleconf.h"
#include "../pith/conf.h"
#include "../pith/filter.h"
#include "../pith/msgno.h"
@@ -130,6 +131,7 @@ struct view_write_s {
#define SS_CUR 2
#define SS_FREE 3
+static ACTION_S *role_chosen = NULL;
/*
* Handle hints.
@@ -204,7 +206,15 @@ char *pcpine_help_scroll(char *);
int pcpine_view_cursor(int, long);
#endif
+static char *prefix;
+#define NO_FLOWED 0x0000
+#define IS_FLOWED 0x0001
+#define DELETEQUO 0x0010
+#define COLORAQUO 0x0100
+#define RAWSTRING 0x1000
+int is_word (char *, int, int);
+int is_mailbox(char *, int, int);
/*----------------------------------------------------------------------
Format a buffer with the text of the current message for browser
@@ -243,6 +253,8 @@ mail_view_screen(struct pine *ps)
ps->prev_screen = mail_view_screen;
ps->force_prefer_plain = ps->force_no_prefer_plain = 0;
+ strcpy(ps->screen_name, "text");
+
if(ps->ttyo->screen_rows - HEADER_ROWS(ps) - FOOTER_ROWS(ps) < 1){
q_status_message(SM_ORDER | SM_DING, 0, 3,
_("Screen too small to view message"));
@@ -295,6 +307,17 @@ mail_view_screen(struct pine *ps)
else
ps->unseen_in_view = !mc->seen;
+ prefix = reply_quote_str(env);
+ /* Make sure the prefix is not only made of spaces, so that we do not
+ * paint the screen incorrectly
+ */
+ if (prefix && *prefix){
+ int i;
+ for (i = 0; isspace((unsigned char) prefix[i]); i++);
+ if (i == strlen(prefix))
+ fs_give((void **)&prefix);
+ }
+
init_handles(&handles);
store = so_get(src, NULL, EDIT_ACCESS);
@@ -479,6 +502,10 @@ mail_view_screen(struct pine *ps)
}
while(ps->next_screen == SCREEN_FUN_NULL);
+ strcpy(ps->screen_name, "unknown");
+
+ if (prefix && *prefix)
+ fs_give((void **)&prefix);
if(we_cancel)
cancel_busy_cue(-1);
@@ -733,6 +760,8 @@ scroll_handle_prompt(HANDLE_S *handle, int force)
{0, 'a', "A", N_("editApp")},
{-1, 0, NULL, NULL}};
+ if (role_chosen)
+ free_action(&role_chosen);
if(handle->type == URL){
launch_opts[4].ch = 'u';
@@ -847,12 +876,42 @@ scroll_handle_prompt(HANDLE_S *handle, int force)
* sense if you just say View selected URL ...
*/
if(handle->type == URL &&
- !struncmp(handle->h.url.path, "mailto:", 7))
- snprintf(prompt, sizeof(prompt), "Compose mail to \"%.*s%s\" ? ",
- MIN(MAX(0,sc - 25), sizeof(prompt)-50), handle->h.url.path+7,
- (strlen(handle->h.url.path+7) > MAX(0,sc-25)) ? "..." : "");
- else
- snprintf(prompt, sizeof(prompt), "View selected %s %s%.*s%s ? ",
+ !struncmp(handle->h.url.path, "mailto:", 7)){
+ int rolenick = role_chosen ? strlen(role_chosen->nick) : 0;
+ int offset = 25 + (role_chosen ? 20 : 0);
+ int offset2 = max(0, sc - offset) - strlen(handle->h.url.path+7);
+ int offset3 = sc - strlen(handle->h.url.path+7) - rolenick - offset;
+ int laddress = min(max(0,sc - offset), sizeof(prompt)-50);
+ int lrole = rolenick;
+
+ if (offset3 < 0){
+ lrole = rolenick;
+ laddress = sc - offset - lrole;
+ offset3 = laddress - 20; /* redefine offset3 */
+ if (offset3 < 0){
+ laddress = 20;
+ lrole = sc - offset - laddress;
+ }
+ }
+ launch_opts[2].ch = 'r';
+ launch_opts[2].rval = 'r';
+ launch_opts[2].name = "R";
+ launch_opts[2].label = N_("Set Role");
+ snprintf(prompt, sizeof(prompt), "Compose mail to \"%.*s%s\" %s%.*s%s%s? ",
+ laddress, handle->h.url.path+7,
+ (offset2 < 0 ? "..." : ""),
+ (role_chosen ? "using role \"" : ""),
+ (role_chosen ? lrole : 0),
+ (role_chosen ? role_chosen->nick : ""),
+ (role_chosen ? (rolenick > lrole ? "..." : "") : ""),
+ (role_chosen ? "\" " : ""));
+ }
+ else{
+ launch_opts[2].ch = -2;
+ launch_opts[2].rval = 0;
+ launch_opts[2].name = NULL;
+ launch_opts[2].label = NULL;
+ snprintf(prompt, sizeof(prompt), "View selected %s %s%.*s%s ? ",
(handle->type == URL) ? "URL" : "Attachment",
(handle->type == URL) ? "\"" : "",
MIN(MAX(0,sc-27), sizeof(prompt)-50),
@@ -860,6 +919,7 @@ scroll_handle_prompt(HANDLE_S *handle, int force)
(handle->type == URL)
? ((strlen(handle->h.url.path) > MAX(0,sc-27))
? "...\"" : "\"") : "");
+ }
prompt[sizeof(prompt)-1] = '\0';
@@ -868,6 +928,29 @@ scroll_handle_prompt(HANDLE_S *handle, int force)
case 'y' :
return(1);
+ case 'r':
+ {
+ void (*prev_screen)(struct pine *) = ps_global->prev_screen,
+ (*redraw)(void) = ps_global->redrawer;
+ ps_global->redrawer = NULL;
+ ps_global->next_screen = SCREEN_FUN_NULL;
+ if(role_select_screen(ps_global, &role_chosen, 1) < 0){
+ cmd_cancelled("Compose");
+ ps_global->next_screen = prev_screen;
+ ps_global->redrawer = redraw;
+ if(ps_global->redrawer)
+ (*ps_global->redrawer)();
+ return 0;
+ }
+ ps_global->next_screen = prev_screen;
+ ps_global->redrawer = redraw;
+ if(role_chosen)
+ role_chosen = combine_inherited_role(role_chosen);
+ if(ps_global->redrawer)
+ (*ps_global->redrawer)();
+ break;
+ }
+
case 'u' :
strncpy(tmp, handle->h.url.path, sizeof(tmp)-1);
tmp[sizeof(tmp)-1] = '\0';
@@ -1432,7 +1515,7 @@ url_launch(HANDLE_S *handle)
else
#endif
/* quote shell specials */
- if(strpbrk(handle->h.url.path, "&*!;<>?[]|~$(){}'\"") != NULL){
+ if(strpbrk(handle->h.url.path, "&*;<>?[]|~$(){}'\"") != NULL){
escape_single_quotes++;
if((p = strstr(toolp, "_URL_")) != NULL){ /* explicit arg? */
int in_quote = 0;
@@ -1510,8 +1593,7 @@ url_launch(HANDLE_S *handle)
*cmdp++ = '\''; /* closing quote */
*cmdp++ = '\\';
*cmdp++ = '\''; /* opening quote comes from p below */
- } else if (strchr("&*!;<>?[]|~$(){}\"", *p) != NULL)
- *cmdp++ = '\\';
+ }
*cmdp++ = *p;
}
@@ -1539,7 +1621,7 @@ url_launch(HANDLE_S *handle)
if(cmdp-cmd >= URL_MAX_LAUNCH)
return(url_launch_too_long(rv));
- mode = PIPE_RESET | PIPE_USER | PIPE_RUNNOW ;
+ mode = PIPE_RESET | PIPE_USER | PIPE_RUNNOW | PIPE_NOSHELL;
if((syspipe = open_system_pipe(cmd, NULL, NULL, mode, 0, pipe_callback, pipe_report_error)) != NULL){
close_system_pipe(&syspipe, NULL, pipe_callback);
q_status_message(SM_ORDER, 0, 4, _("VIEWER command completed"));
@@ -1817,7 +1899,7 @@ url_local_mailto_and_atts(char *url, PATMT *attachlist)
fs_give((void **) &urlp);
rflags = ROLE_COMPOSE;
- if(nonempty_patterns(rflags, &dummy)){
+ if(!(role = copy_action(role_chosen)) && nonempty_patterns(rflags, &dummy)){
role = set_role_from_msg(ps_global, rflags, -1L, NULL);
if(confirm_role(rflags, &role))
role = combine_inherited_role(role);
@@ -1893,6 +1975,7 @@ outta_here:
free_redraft_pos(&redraft_pos);
free_action(&role);
+ free_action(&role_chosen);
return(rv);
}
@@ -3463,6 +3546,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
+ 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 --git a/alpine/osdep/debuging.c b/alpine/osdep/debuging.c
index 0310f5b5..4767ca57 100644
--- a/alpine/osdep/debuging.c
+++ b/alpine/osdep/debuging.c
@@ -105,6 +105,7 @@ init_debug(void)
if(debugfile != NULL){
char rev[128];
+ extern char plevstamp[];
time_t now = time((time_t *)0);
if(ps_global->debug_flush)
setvbuf(debugfile, (char *)NULL, _IOLBF, BUFSIZ);
@@ -127,6 +128,8 @@ init_debug(void)
get_alpine_revision_string(rev, sizeof(rev)),
ctime(&now)));
+ dprint((0, "This version uses all.patch:\n%s\n\n", plevstamp));
+
dprint((0, "Starting after the reading_pinerc calls, the data in this file should\nbe encoded as UTF-8. Before that it will be in the user's native charset.\n"));
if(dfile && (debug > DEFAULT_DEBUG ||
ps_global->debug_imap > 0 ||
diff --git a/alpine/osdep/execview.c b/alpine/osdep/execview.c
index da7310df..d7087caa 100644
--- a/alpine/osdep/execview.c
+++ b/alpine/osdep/execview.c
@@ -261,7 +261,7 @@ exec_mailcap_cmd(MCAP_CMD_S *mc_cmd, char *image_file, int needsterminal)
p = command = (char *)fs_get((l+1) * sizeof(char));
if(!needsterminal) /* put in background if it doesn't need terminal */
*p++ = '(';
- snprintf(p, l+1-(p-command), "%s ; rm -f %s", cmd, image_file);
+ snprintf(p, l+1-(p-command), "%s ; sleep %d ; rm -f %s", cmd, ps_global->sleep, image_file);
command[l] = '\0';
p += strlen(p);
if(!needsterminal && (p-command)+5 < l){
diff --git a/alpine/osdep/termin.gen.c b/alpine/osdep/termin.gen.c
index fb106be1..74d2a37f 100644
--- a/alpine/osdep/termin.gen.c
+++ b/alpine/osdep/termin.gen.c
@@ -33,6 +33,8 @@ static char rcsid[] = "$Id: termin.gen.c 1025 2008-04-08 22:59:38Z hubert@u.wash
#include "../../pith/newmail.h"
#include "../../pith/conf.h"
#include "../../pith/busy.h"
+#include "../../pith/list.h"
+#include "../../pith/rules.h"
#include "../../pico/estruct.h"
#include "../../pico/pico.h"
@@ -67,12 +69,30 @@ static int g_mc_row, g_mc_col;
int pcpine_oe_cursor(int, long);
#endif
+void
+fake_config_screen(tt)
+ struct ttyo **tt;
+{
+ struct ttyo *ttyo;
+
+ ttyo = (struct ttyo *)fs_get(sizeof (struct ttyo));
+
+ ttyo->header_rows = 2;
+ ttyo->footer_rows = 3;
+ ttyo->screen_rows = 24;
+ ttyo->screen_cols = 80;
+
+ *tt = ttyo;
+
+}
+
/*
* Generic tty input routines
*/
-
+void process_init_cmds(struct pine *, char **);
+void queue_init_errors(struct pine *);
/*----------------------------------------------------------------------
Read a character from keyboard with timeout
Input: none
@@ -114,6 +134,41 @@ read_command(char **utf8str)
*utf8str = NULL;
ucs = read_char(tm);
+ if(!ps_global->initial_cmds){
+ RULE_RESULT *rule;
+ char **list = NULL, *error = NULL;
+ int commas = 0, k; /* From args.c */
+
+ ps_global->pressed_key = cpystr(pretty_command(ucs));
+ rule = (RULE_RESULT *)get_result_rule(V_KEY_RULES, FOR_KEY, NULL);
+ if(ps_global->pressed_key)
+ fs_give((void **)&ps_global->pressed_key);
+ if (rule){
+ for(k = 0; rule->result[k]; k++)
+ if(rule->result[k] == ',') commas++;
+ list = parse_list(rule->result, commas+1, 0, &error);
+ if(error)
+ sprintf(tmp_20k_buf, "Error in parsing command list: %s, %s",
+ rule->result, error);
+ if (rule->result)
+ fs_give((void **)&rule->result);
+ fs_give((void **)&rule);
+ if(error){
+ q_status_message(SM_ORDER | SM_DING, 0, 2, tmp_20k_buf);
+ return (NO_OP_COMMAND);
+ }
+ process_init_cmds(ps_global, list);
+ if(ps_global->init_errs){
+ queue_init_errors(ps_global);
+ return (NO_OP_COMMAND);
+ }
+ ucs = read_char(tm);
+ ps_global->in_init_seq = 1; /* no output please */
+ for(k = 0; k < commas; k++)
+ if(list[k]) fs_give((void **)&list[k]);
+ if (list) fs_give((void **)list);
+ }
+ }
if(ucs != NO_OP_COMMAND && ucs != NO_OP_IDLE && ucs != KEY_RESIZE)
zero_new_mail_count();
@@ -307,7 +362,7 @@ optionally_enter(char *utf8string, int y_base, int x_base, int utf8string_size,
(escape_list && escape_list[0].ch != -1 && escape_list[0].label)
? escape_list[0].label: ""));
- if(!ps_global->ttyo)
+ if(!ps_global->ttyo || ps_global->send_immediately)
return(pre_screen_config_opt_enter(utf8string, utf8string_size, utf8prompt,
escape_list, help, flags));
@@ -1154,10 +1209,11 @@ process_config_input(UCS *ch)
}
}
}
-
+ ps_global->initial_cmds_offset++;
if(!*ps_global->initial_cmds && ps_global->free_initial_cmds){
fs_give((void **) &ps_global->free_initial_cmds);
ps_global->initial_cmds = NULL;
+ firsttime = (char) 1;
}
return(ret);
diff --git a/alpine/osdep/termin.gen.h b/alpine/osdep/termin.gen.h
index 18c7c0de..5309eba2 100644
--- a/alpine/osdep/termin.gen.h
+++ b/alpine/osdep/termin.gen.h
@@ -33,6 +33,7 @@ int process_config_input(UCS *);
int key_recorder(int);
int key_playback(int);
int recent_keystroke(int *, char *, size_t);
+void fake_config_screen(struct ttyo **);
int init_tty_driver(struct pine *);
void end_tty_driver(struct pine *);
int PineRaw(int);
diff --git a/alpine/osdep/termin.unx.c b/alpine/osdep/termin.unx.c
index 451d18c2..b23c6f22 100644
--- a/alpine/osdep/termin.unx.c
+++ b/alpine/osdep/termin.unx.c
@@ -111,6 +111,8 @@ open_mailer for details.
int
init_tty_driver(struct pine *ps)
{
+ if(ps->send_immediately)
+ return 0;
#ifdef MOUSE
if(F_ON(F_ENABLE_MOUSE, ps_global))
init_mouse();
@@ -677,6 +679,9 @@ a lot of at UW
void
init_keyboard(int use_fkeys)
{
+ if (ps_global->send_immediately)
+ return;
+
if(use_fkeys && (!strucmp(term_name,"vt102")
|| !strucmp(term_name,"vt100")))
printf("\033\133\071\071\150");
@@ -694,6 +699,9 @@ init_keyboard(int use_fkeys)
void
end_keyboard(int use_fkeys)
{
+ if(ps_global->send_immediately)
+ return;
+
if(use_fkeys && (!strcmp(term_name, "vt102")
|| !strcmp(term_name, "vt100"))){
printf("\033\133\071\071\154");
diff --git a/alpine/osdep/termout.unx.c b/alpine/osdep/termout.unx.c
index faf8c7f9..88df57ea 100644
--- a/alpine/osdep/termout.unx.c
+++ b/alpine/osdep/termout.unx.c
@@ -206,6 +206,9 @@ config_screen(struct ttyo **tt)
void
init_screen(void)
{
+ if(ps_global->send_immediately)
+ return;
+
if(_termcap_init) /* init using termcap's rule */
tputs(_termcap_init, 1, outchar);
@@ -313,6 +316,9 @@ end_screen(char *message, int exit_val)
{
int footer_rows_was_one = 0;
+ if(ps_global->send_immediately)
+ return;
+
if(!panicking()){
dprint((9, "end_screen called\n"));
@@ -367,7 +373,7 @@ ClearScreen(void)
_line = 0; /* clear leaves us at top... */
_col = 0;
- if(ps_global->in_init_seq)
+ if(ps_global->in_init_seq || ps_global->send_immediately)
return;
mark_status_unknown();
diff --git a/alpine/radio.c b/alpine/radio.c
index fc5c9ab9..436be052 100644
--- a/alpine/radio.c
+++ b/alpine/radio.c
@@ -122,7 +122,7 @@ want_to(char *question, int dflt, int on_ctrl_C, HelpType help, int flags)
int rv, width;
size_t len;
- if(!ps_global->ttyo)
+ if(!ps_global->ttyo || ps_global->send_immediately)
return(pre_screen_config_want_to(question, dflt, on_ctrl_C));
#ifdef _WINDOWS
if (mswin_usedialog ()) {
diff --git a/alpine/reply.c b/alpine/reply.c
index f0a40651..e72268c0 100644
--- a/alpine/reply.c
+++ b/alpine/reply.c
@@ -62,7 +62,8 @@ The evolution continues...
#include "../pith/tempfile.h"
#include "../pith/busy.h"
#include "../pith/ablookup.h"
-
+#include "../pith/copyaddr.h"
+#include "../pith/rules.h"
/*
* Internal Prototypes
@@ -109,11 +110,12 @@ reply(struct pine *pine_state, ACTION_S *role_arg)
long msgno, j, totalm, rflags, *seq = NULL;
int i, include_text = 0, times = -1, warned = 0, rv = 0,
flags = RSF_QUERY_REPLY_ALL, reply_raw_body = 0;
- int rolemsg = 0, copytomsg = 0;
+ int rolemsg = 0, copytomsg = 0, do_role_early = 0;
gf_io_t pc;
PAT_STATE dummy;
REDRAFT_POS_S *redraft_pos = NULL;
ACTION_S *role = NULL, *nrole;
+ RULE_RESULT *rule;
#if defined(DOS) && !defined(_WINDOWS)
char *reserve;
#endif
@@ -139,6 +141,69 @@ reply(struct pine *pine_state, ACTION_S *role_arg)
&& F_ON(F_ENABLE_FULL_HDR_AND_TEXT, ps_global))
reply_raw_body = 1;
+ /* Setup possible role */
+ if(role_arg)
+ role = copy_action(role_arg);
+
+ if(!role && F_ON(F_ENABLE_EDIT_REPLY_INDENT, pine_state)){
+ for(msgno = mn_first_cur(pine_state->msgmap);
+ msgno > 0L; msgno = mn_next_cur(pine_state->msgmap)){
+
+ env = pine_mail_fetchstructure(pine_state->mail_stream,
+ mn_m2raw(pine_state->msgmap, msgno),
+ NULL);
+ if(!env) {
+ q_status_message1(SM_ORDER,3,4,
+ _("Error fetching message %s. Can't reply to it."),
+ long2string(msgno));
+ goto done_early;
+ }
+
+ if(rule = get_result_rule(V_REPLY_INDENT_RULES, FOR_COMPOSE , env)){
+ RULELIST *list = get_rulelist_from_code(V_REPLY_INDENT_RULES,
+ ps_global->rule_list);
+ RULE_S *prule = get_rule(list, rule->number);
+ if(condition_contains_token(prule->condition, ROLE_TOKEN))
+ do_role_early++;
+ if(rule->result)
+ fs_give((void **)&rule->result);
+ fs_give((void **)&rule);
+ }
+ }
+ }
+
+ if(do_role_early){
+ rflags = ROLE_REPLY;
+ if(nonempty_patterns(rflags, &dummy)){
+ /* setup default role */
+ nrole = NULL;
+ j = mn_first_cur(pine_state->msgmap);
+ do {
+ role = nrole;
+ nrole = set_role_from_msg(pine_state, rflags,
+ mn_m2raw(pine_state->msgmap, j),
+ NULL);
+ } while(nrole && (!role || nrole == role)
+ && (j=mn_next_cur(pine_state->msgmap)) > 0L);
+
+ if(!role || nrole == role)
+ role = nrole;
+ else
+ role = NULL;
+
+ if(confirm_role(rflags, &role))
+ role = combine_inherited_role(role);
+ else{ /* cancel reply */
+ role = NULL;
+ cmd_cancelled("Reply");
+ goto done_early;
+ }
+ }
+ }
+
+ if (role)
+ ps_global->role = cpystr(role->nick); /* remember the role */
+
/*
* We may have to loop through first to figure out what default
* reply-indent-string to offer...
@@ -230,7 +295,7 @@ reply(struct pine *pine_state, ACTION_S *role_arg)
if(!times){ /* only first time */
char *p = cpystr(prefix);
- if((include_text=reply_text_query(pine_state,totalm,&prefix)) < 0)
+ if((include_text=reply_text_query(pine_state,totalm,env,&prefix)) < 0)
goto done_early;
/* edited prefix? */
@@ -286,8 +351,18 @@ reply(struct pine *pine_state, ACTION_S *role_arg)
outgoing->subject = cpystr("Re: several messages");
}
}
- else
- outgoing->subject = reply_subject(env->subject, NULL, 0);
+ else{
+ RULE_RESULT *rule;
+ rule = get_result_rule(V_RESUB_RULES,FOR_RESUB|FOR_TRIM , env);
+ if (rule){
+ outgoing->subject = reply_subject(rule->result, NULL, 0);
+ if (rule->result)
+ fs_give((void **)&rule->result);
+ fs_give((void **)&rule);
+ }
+ else
+ outgoing->subject = reply_subject(env->subject, NULL, 0);
+ }
}
/* fill reply header */
@@ -306,13 +381,15 @@ reply(struct pine *pine_state, ACTION_S *role_arg)
if(sp_expunge_count(pine_state->mail_stream)) /* cur msg expunged */
goto done_early;
- /* Setup possible role */
- if(role_arg)
- role = copy_action(role_arg);
+ if (ps_global->reply.role_chosen){
+ if(role_arg)
+ free_action(&role);
+ role = ps_global->reply.role_chosen;
+ }
- if(!role){
+ if(!do_role_early){
rflags = ROLE_REPLY;
- if(nonempty_patterns(rflags, &dummy)){
+ if(!ps_global->reply.role_chosen && nonempty_patterns(rflags, &dummy)){
/* setup default role */
nrole = NULL;
j = mn_first_cur(pine_state->msgmap);
@@ -548,7 +625,7 @@ reply(struct pine *pine_state, ACTION_S *role_arg)
if(orig_body == NULL || orig_body->type == TYPETEXT || reply_raw_body) {
reply_delimiter(env, role, pc);
- if(F_ON(F_INCLUDE_HEADER, pine_state))
+ if(ps_global->reply.inchdr)
reply_forward_header(pine_state->mail_stream,
mn_m2raw(pine_state->msgmap,msgno),
NULL, env, pc, prefix);
@@ -567,7 +644,7 @@ reply(struct pine *pine_state, ACTION_S *role_arg)
&& orig_body->nested.part->body.type == TYPETEXT) {
/*---- First part of the message is text -----*/
reply_delimiter(env, role, pc);
- if(F_ON(F_INCLUDE_HEADER, pine_state))
+ if(ps_global->reply.inchdr)
reply_forward_header(pine_state->mail_stream,
mn_m2raw(pine_state->msgmap,
msgno),
@@ -721,6 +798,9 @@ reply(struct pine *pine_state, ACTION_S *role_arg)
if(prefix)
fs_give((void **)&prefix);
+ if (ps_global->role)
+ fs_give((void **)&ps_global->role);
+
if(fcc)
fs_give((void **) &fcc);
@@ -876,7 +956,8 @@ confirm_role(long int rflags, ACTION_S **role)
prompt[sizeof(prompt)-1] = '\0';
- cmd = radio_buttons(prompt, -FOOTER_ROWS(ps_global), ekey,
+ cmd = ps_global->send_immediately ? 'n' :
+ radio_buttons(prompt, -FOOTER_ROWS(ps_global), ekey,
'y', 'x', help, RB_NORM);
switch(cmd){
@@ -1003,48 +1084,113 @@ reply_using_replyto_query(void)
* 0 if we're NOT to include the text
* -1 on cancel or error
*/
+#define MAX_REPLY_OPTIONS 8
int
-reply_text_query(struct pine *ps, long int many, char **prefix)
+reply_text_query(struct pine *ps, long int many, ENVELOPE *env, char **prefix)
{
int ret, edited = 0;
- static ESCKEY_S rtq_opts[] = {
- {'y', 'y', "Y", N_("Yes")},
- {'n', 'n', "N", N_("No")},
- {-1, 0, NULL, NULL}, /* may be overridden below */
- {-1, 0, NULL, NULL}
- };
+ static ESCKEY_S compose_style[MAX_REPLY_OPTIONS];
+ int ekey_num;
+ int orig_sf;
+
+ orig_sf = *prefix && **prefix ? (F_OFF(F_QUELL_FLOWED_TEXT, ps)
+ && F_OFF(F_STRIP_WS_BEFORE_SEND, ps)
+ && (strcmp(*prefix, "> ") == 0
+ || strcmp(*prefix, ">") == 0)) : 0;
+
+ ps_global->reply.no_send_flowed = !orig_sf;
+ ps_global->reply.role_chosen = NULL;
+ ps_global->reply.strip = ps->full_header == 0
+ && (F_ON(F_ENABLE_STRIP_SIGDASHES, ps)
+ || F_ON(F_ENABLE_SIGDASHES, ps));
+ ps_global->reply.attach = F_ON(F_ATTACHMENTS_IN_REPLY, ps);
+ ps_global->reply.inchdr = F_ON(F_INCLUDE_HEADER, ps);
if(F_ON(F_AUTO_INCLUDE_IN_REPLY, ps)
- && F_OFF(F_ENABLE_EDIT_REPLY_INDENT, ps))
+ && F_OFF(F_ENABLE_EDIT_REPLY_INDENT, ps) && F_OFF(F_ALT_REPLY_MENU,ps))
return(1);
- while(1){
- if(many > 1L)
- /* TRANSLATORS: The final three %s's can probably be safely ignored */
- snprintf(tmp_20k_buf, SIZEOF_20KBUF, _("Include %s original messages in Reply%s%s%s? "),
- comatose(many),
- F_ON(F_ENABLE_EDIT_REPLY_INDENT, ps) ? " (using \"" : "",
- F_ON(F_ENABLE_EDIT_REPLY_INDENT, ps) ? *prefix : "",
- F_ON(F_ENABLE_EDIT_REPLY_INDENT, ps) ? "\")" : "");
- else
- snprintf(tmp_20k_buf, SIZEOF_20KBUF, _("Include original message in Reply%s%s%s? "),
+ while(1){
+ /* TRANSLATORS: The final five %s's can probably be safely ignored */
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, _("Include %s%soriginal message%s in Reply%s%s%s%s%s%s? "),
+ (many > 1L) ? comatose(many) : "",
+ (many > 1L) ? " " : "",
+ (many > 1L) ? "s" : "",
+ (many > 1L) ? "s" : "",
F_ON(F_ENABLE_EDIT_REPLY_INDENT, ps) ? " (using \"" : "",
F_ON(F_ENABLE_EDIT_REPLY_INDENT, ps) ? *prefix : "",
+ ps_global->reply.role_chosen ? "\" and role \"" : "",
+ ps_global->reply.role_chosen ? ps_global->reply.role_chosen->nick : "",
F_ON(F_ENABLE_EDIT_REPLY_INDENT, ps) ? "\")" : "");
- if(F_ON(F_ENABLE_EDIT_REPLY_INDENT, ps)){
- rtq_opts[2].ch = ctrl('R');
- rtq_opts[2].rval = 'r';
- rtq_opts[2].name = "^R";
- rtq_opts[2].label = N_("Edit Indent String");
+ ekey_num = 0;
+ compose_style[ekey_num].ch = 'y';
+ compose_style[ekey_num].rval = 'y';
+ compose_style[ekey_num].name = "Y";
+ compose_style[ekey_num++].label = N_("Yes");
+
+ compose_style[ekey_num].ch = 'n';
+ compose_style[ekey_num].rval = 'n';
+ compose_style[ekey_num].name = "N";
+ compose_style[ekey_num++].label = N_("No");
+
+ if (F_ON(F_ENABLE_EDIT_REPLY_INDENT, ps)){
+ compose_style[ekey_num].ch = ctrl('R');
+ compose_style[ekey_num].rval = 'r';
+ compose_style[ekey_num].name = "^R";
+ compose_style[ekey_num++].label = N_("Edit Indent String");
}
- else
- rtq_opts[2].ch = -1;
+
+ /***** Alternate Reply Menu ********/
+
+ if (F_ON(F_ALT_REPLY_MENU, ps)){
+ unsigned which_help;
+
+ if (F_ON(F_ENABLE_STRIP_SIGDASHES, ps) ||
+ F_ON(F_ENABLE_SIGDASHES, ps)){
+ compose_style[ekey_num].ch = 's';
+ compose_style[ekey_num].rval = 'S';
+ compose_style[ekey_num].name = "S";
+ compose_style[ekey_num++].label = ps_global->reply.strip
+ ? N_("No Strip"): N_("Strip Sig");
+ }
+
+ compose_style[ekey_num].ch = 'r';
+ compose_style[ekey_num].rval = 'R';
+ compose_style[ekey_num].name = "R";
+ compose_style[ekey_num++].label = N_("Set Role");
+
+ if(orig_sf){
+ compose_style[ekey_num].ch = 'f';
+ compose_style[ekey_num].rval = 'F';
+ compose_style[ekey_num].name = "F";
+ compose_style[ekey_num++].label = ps_global->reply.no_send_flowed
+ ? N_("Quell Flow") : N_("Send Flowd");
+ }
+
+ compose_style[ekey_num].ch = 'a';
+ compose_style[ekey_num].rval = 'A';
+ compose_style[ekey_num].name = "A";
+ compose_style[ekey_num++].label = ps_global->reply.attach
+ ? N_("No Attach"): N_("Inc Attac");
+
+ compose_style[ekey_num].ch = 'h';
+ compose_style[ekey_num].rval = 'H';
+ compose_style[ekey_num].name = "H";
+ compose_style[ekey_num++].label = ps_global->reply.inchdr
+ ? N_("No Header") : N_("Inc Head");
+
+ }
+ compose_style[ekey_num].ch = -1;
+ compose_style[ekey_num].name = NULL;
+ compose_style[ekey_num].label = NULL;
+
+ /***** End Alt Reply Menu *********/
switch(ret = radio_buttons(tmp_20k_buf,
ps->ttyo->screen_rows > 4
? -FOOTER_ROWS(ps_global) : -1,
- rtq_opts,
+ compose_style,
(edited || F_ON(F_AUTO_INCLUDE_IN_REPLY, ps))
? 'y' : 'n',
'x', NO_HELP, RB_SEQ_SENSITIVE)){
@@ -1052,6 +1198,46 @@ reply_text_query(struct pine *ps, long int many, char **prefix)
cmd_cancelled("Reply");
return(-1);
+ case 'F':
+ ps_global->reply.no_send_flowed = (ps_global->reply.no_send_flowed + 1) % 2;
+ break;
+
+ case 'S':
+ ps_global->reply.strip = (ps_global->reply.strip + 1) % 2;
+ break;
+
+ case 'A':
+ ps_global->reply.attach = (ps_global->reply.attach + 1) % 2;
+ break;
+
+ case 'H':
+ ps_global->reply.inchdr = (ps_global->reply.inchdr + 1) % 2;
+ break;
+
+
+ case 'R':
+ {
+ void (*prev_screen)(struct pine *) = ps->prev_screen,
+ (*redraw)(void) = ps->redrawer;
+ ps->redrawer = NULL;
+ ps->next_screen = SCREEN_FUN_NULL;
+ if(role_select_screen(ps, &ps_global->reply.role_chosen, 1) < 0){
+ cmd_cancelled("Reply");
+ ps->next_screen = prev_screen;
+ ps->redrawer = redraw;
+ if (ps->redrawer)
+ (*ps->redrawer)();
+ continue;
+ }
+ ps->next_screen = prev_screen;
+ ps->redrawer = redraw;
+ if(ps_global->reply.role_chosen)
+ ps_global->reply.role_chosen = combine_inherited_role(ps_global->reply.role_chosen);
+ }
+ if (ps->redrawer)
+ (*ps->redrawer)();
+ break;
+
case 'r':
if(prefix && *prefix){
int done = 0;
@@ -1075,6 +1261,12 @@ reply_text_query(struct pine *ps, long int many, char **prefix)
if(flags & OE_USER_MODIFIED){
fs_give((void **)prefix);
*prefix = removing_quotes(cpystr(buf));
+ orig_sf = *prefix && **prefix ?
+ (F_OFF(F_QUELL_FLOWED_TEXT, ps)
+ && F_OFF(F_STRIP_WS_BEFORE_SEND, ps)
+ && (strcmp(*prefix, "> ") == 0
+ || strcmp(*prefix, ">") == 0)) : 0;
+ ps_global->reply.no_send_flowed = !orig_sf;
edited = 1;
}
@@ -1471,9 +1663,14 @@ forward(struct pine *ps, ACTION_S *role_arg)
}
}
- if(role)
+ if (ps_global->role)
+ fs_give((void **)&ps_global->role);
+
+ if(role){
q_status_message1(SM_ORDER, 3, 4,
_("Forwarding using role \"%s\""), role->nick);
+ ps_global->role = cpystr(role->nick);
+ }
if(role && role->template){
char *filtered;
@@ -1705,6 +1902,7 @@ forward(struct pine *ps, ACTION_S *role_arg)
#if defined(DOS) && !defined(_WINDOWS)
free((void *)reserve);
#endif
+ outgoing->sparep = env && env->from ? copyaddr(env->from) : NULL;
pine_send(outgoing, &body, "FORWARD MESSAGE",
role, NULL, &reply, redraft_pos,
NULL, NULL, 0);
@@ -2457,6 +2655,8 @@ display_message_for_pico(int x)
{
int rv;
+ if(ps_global->send_immediately)
+ return 0;
clear_cursor_pos(); /* can't know where cursor is */
mark_status_dirty(); /* don't count on cached text */
fix_windsize(ps_global);
diff --git a/alpine/reply.h b/alpine/reply.h
index 2c239071..134f8fc2 100644
--- a/alpine/reply.h
+++ b/alpine/reply.h
@@ -28,7 +28,7 @@ int reply(struct pine *, ACTION_S *);
int confirm_role(long, ACTION_S **);
int reply_to_all_query(int *);
int reply_using_replyto_query(void);
-int reply_text_query(struct pine *, long, char **);
+int reply_text_query(struct pine *, long, ENVELOPE *, char **);
int reply_news_test(ENVELOPE *, ENVELOPE *);
char *get_signature_file(char *, int, int, int);
int forward(struct pine *, ACTION_S *);
diff --git a/alpine/roleconf.c b/alpine/roleconf.c
index fad1e661..be046acf 100644
--- a/alpine/roleconf.c
+++ b/alpine/roleconf.c
@@ -140,8 +140,13 @@ role_select_screen(struct pine *ps, ACTION_S **role, int alt_compose)
if(!(nonempty_patterns(rflags, &pstate) &&
first_pattern(&pstate))){
+ if(!ps->send_immediately)
q_status_message(SM_ORDER, 3, 3,
_("No roles available. Use Setup/Rules to add roles."));
+ else{
+ printf( _("No roles available. Use Setup/Rules to add roles."));
+ exit(-1);
+ }
return(ret);
}
@@ -4478,11 +4483,11 @@ role_config_edit_screen(struct pine *ps, PAT_S *def, char *title, long int rflag
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 +4497,7 @@ role_config_edit_screen(struct pine *ps, PAT_S *def, char *title, long int rflag
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 +4509,7 @@ role_config_edit_screen(struct pine *ps, PAT_S *def, char *title, long int rflag
ctmp->valoffset = rindent;
ctmp->varmem = i + (j * EndofList);
ctmp->value = generalized_sort_pretty_value(ps, ctmp,
- 0);
+ 0, 0);
}
}
@@ -5437,7 +5442,7 @@ role_config_edit_screen(struct pine *ps, PAT_S *def, char *title, long int rflag
(*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);
@@ -7706,6 +7711,11 @@ role_text_tool_inick(struct pine *ps, int cmd, CONF_S **cl, unsigned int flags)
if(apval)
*apval = (role && role->nick) ? cpystr(role->nick) : NULL;
+ if (ps_global->role)
+ fs_give((void **)&ps_global->role);
+ if (role && role->nick)
+ ps_global->role = cpystr(role->nick);
+
if((*cl)->value)
fs_give((void **)&((*cl)->value));
diff --git a/alpine/send.c b/alpine/send.c
index 852b611c..06c43b65 100644
--- a/alpine/send.c
+++ b/alpine/send.c
@@ -63,7 +63,7 @@ static char rcsid[] = "$Id: send.c 1142 2008-08-13 17:22:21Z hubert@u.washington
#include "../pith/mimetype.h"
#include "../pith/send.h"
#include "../pith/smime.h"
-
+#include "../pith/rules.h"
typedef struct body_particulars {
unsigned short type, encoding, had_csp;
@@ -239,6 +239,11 @@ alt_compose_screen(struct pine *pine_state)
role->nick = cpystr("Default Role");
}
+ if (ps_global->role)
+ fs_give((void **)&ps_global->role);
+
+ ps_global->role = cpystr(role->nick);
+
pine_state->redrawer = NULL;
compose_mail(NULL, NULL, role, NULL, NULL);
free_action(&role);
@@ -448,8 +453,12 @@ compose_mail(char *given_to, char *fcc_arg, ACTION_S *role_arg,
ps_global->next_screen = prev_screen;
ps_global->redrawer = redraw;
- if(role)
+ if (ps_global->role)
+ fs_give((void **)&ps_global->role);
+ if(role){
role = combine_inherited_role(role);
+ ps_global->role = cpystr(role->nick);
+ }
}
break;
@@ -614,6 +623,7 @@ compose_mail(char *given_to, char *fcc_arg, ACTION_S *role_arg,
if(given_to)
rfc822_parse_adrlist(&outgoing->to, given_to, ps_global->maildomain);
+ outgoing->subject = cpystr(ps_global->subject);
outgoing->message_id = generate_message_id();
/*
@@ -644,9 +654,14 @@ compose_mail(char *given_to, char *fcc_arg, ACTION_S *role_arg,
}
}
- if(role)
+ if (ps_global->role)
+ fs_give((void **)&ps_global->role);
+
+ if(role){
q_status_message1(SM_ORDER, 3, 4, _("Composing using role \"%s\""),
role->nick);
+ ps_global->role = cpystr(role->nick);
+ }
/*
* The type of storage object allocated below is vitally
@@ -912,7 +927,7 @@ static struct headerentry he_template[]={
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
{"From : ", "From", h_composer_from, 10, 0, NULL,
build_address, NULL, NULL, addr_book_compose, "To AddrBk", NULL, abook_nickname_complete,
- 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_TOADDRBOOK},
+ 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_TOADDRBOOK},
{"Reply-To: ", "Reply To", h_composer_reply_to, 10, 0, NULL,
build_address, NULL, NULL, addr_book_compose, "To AddrBk", NULL, abook_nickname_complete,
0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_TOADDRBOOK},
@@ -1782,6 +1797,9 @@ pine_send(ENVELOPE *outgoing, struct mail_bodystruct **body,
pbf = &pbuf1;
standard_picobuf_setup(pbf);
+ pbf->auto_cmds = ps_global->initial_cmds_backup +
+ ps_global->initial_cmds_offset;
+
/*
* Cancel any pending initial commands since pico uses a different
* input routine. If we didn't cancel them, they would happen after
@@ -2305,6 +2323,11 @@ pine_send(ENVELOPE *outgoing, struct mail_bodystruct **body,
he->rich_header = 0;
}
}
+ if (F_ON(F_ALLOW_CHANGING_FROM, ps_global) &&
+ !ps_global->never_allow_changing_from){
+ he->display_it = 1; /* show it */
+ he->rich_header = 0;
+ }
he_from = he;
break;
@@ -2414,6 +2437,26 @@ pine_send(ENVELOPE *outgoing, struct mail_bodystruct **body,
removing_trailing_white_space(pf->textbuf);
(void)removing_double_quotes(pf->textbuf);
build_address(pf->textbuf, &addr, NULL, NULL, NULL);
+ if (!strncmp(pf->name,"Lcc",3) && addr && *addr){
+ RULE_RESULT *rule;
+
+ outgoing->date = (unsigned char *) cpystr(addr);
+ ps_global->procid = cpystr("fwd-lcc");
+ rule = get_result_rule(V_FORWARD_RULES,
+ FOR_COMPOSE|FOR_TRIM, outgoing);
+ if (rule){
+ addr = cpystr(rule->result);
+ removing_trailing_white_space(addr);
+ (void)removing_extra_stuff(addr);
+ if (rule->result)
+ fs_give((void **)&rule->result);
+ fs_give((void **)&rule);
+ }
+ fs_give((void **)&ps_global->procid);
+ if (outgoing->date)
+ fs_give((void **)&outgoing->date);
+ }
+
rfc822_parse_adrlist(pf->addr, addr,
ps_global->maildomain);
fs_give((void **)&addr);
@@ -2983,7 +3026,12 @@ pine_send(ENVELOPE *outgoing, struct mail_bodystruct **body,
#ifdef _WINDOWS
mswin_setwindowmenu (MENU_DEFAULT);
#endif
- fix_windsize(ps_global);
+ if (ps_global->send_immediately){
+ if(ps_global->free_initial_cmds_backup)
+ fs_give((void **)&ps_global->free_initial_cmds_backup);
+ }
+ else
+ fix_windsize(ps_global);
/*
* Only reinitialize signals if we didn't receive an interesting
@@ -3042,7 +3090,9 @@ pine_send(ENVELOPE *outgoing, struct mail_bodystruct **body,
if(outgoing->return_path)
mail_free_address(&outgoing->return_path);
- outgoing->return_path = rfc822_cpy_adr(outgoing->from);
+ outgoing->return_path = F_ON(F_USE_DOMAIN_NAME,ps_global)
+ ? rfc822_cpy_adr(generate_from())
+ : rfc822_cpy_adr(outgoing->from);
/*
* Don't ever believe the sender that is there.
@@ -3719,10 +3769,16 @@ pine_send(ENVELOPE *outgoing, struct mail_bodystruct **body,
if(sending_filter_requested
&& !filter_message_text(sending_filter_requested, outgoing,
*body, &orig_so, &header)){
- q_status_message1(SM_ORDER, 3, 3,
+ if (!ps_global->send_immediately){
+ q_status_message1(SM_ORDER, 3, 3,
_("Problem filtering! Nothing sent%s."),
fcc ? " or saved to fcc" : "");
- continue;
+ continue;
+ }
+ else{
+ fprintf(stderr, _("Problem filtering! Nothing sent or saved to Fcc\n"));
+ exit(-1);
+ }
}
/*------ Actually post -------*/
@@ -3966,6 +4022,8 @@ pine_send(ENVELOPE *outgoing, struct mail_bodystruct **body,
/*----- Mail Post FAILED, back to composer -----*/
if(result & (P_MAIL_LOSE | P_FCC_LOSE)){
dprint((1, "Send failed, continuing\n"));
+ if (ps_global->send_immediately)
+ exit(1);
if(result & P_FCC_LOSE){
/*
@@ -4000,6 +4058,7 @@ pine_send(ENVELOPE *outgoing, struct mail_bodystruct **body,
update_answered_flags(reply);
/*----- Signed, sealed, delivered! ------*/
+ if (!ps_global->send_immediately)
q_status_message(SM_ORDER, 0, 3,
pine_send_status(result, fcc, tmp_20k_buf, SIZEOF_20KBUF, NULL));
@@ -4466,7 +4525,7 @@ send_exit_for_pico(struct headerentry *he, void (*redraw_pico)(void), int allow_
return(1);
}
- if(F_ON(F_SEND_WO_CONFIRM, ps_global)){
+ if(!ps_global->send_immediately && F_ON(F_SEND_WO_CONFIRM, ps_global)){
if(result)
*result = NULL;
@@ -4646,7 +4705,8 @@ send_exit_for_pico(struct headerentry *he, void (*redraw_pico)(void), int allow_
opts[i].ch = -1;
- fix_windsize(ps_global);
+ if (!ps_global->send_immediately)
+ fix_windsize(ps_global);
while(1){
if(filters && filters->filter && (p = strindex(filters->filter, ' ')))
@@ -4828,7 +4888,8 @@ send_exit_for_pico(struct headerentry *he, void (*redraw_pico)(void), int allow_
if(double_rad +
((call_mailer_flags & CM_DSN_SHOW)
? 4 : F_ON(F_DSN, ps_global) ? 1 : 0) > 11)
- rv = double_radio_buttons(tmp_20k_buf, -FOOTER_ROWS(ps_global), opts,
+ rv = ps_global->send_immediately ? 'y' :
+ double_radio_buttons(tmp_20k_buf, -FOOTER_ROWS(ps_global), opts,
'y', 'z',
(F_ON(F_DSN, ps_global) && allow_flowed)
? h_send_prompt_dsn_flowed :
@@ -4837,7 +4898,8 @@ send_exit_for_pico(struct headerentry *he, void (*redraw_pico)(void), int allow_
h_send_prompt,
RB_NORM);
else
- rv = radio_buttons(tmp_20k_buf, -FOOTER_ROWS(ps_global), opts,
+ rv = ps_global->send_immediately ? 'y' :
+ radio_buttons(tmp_20k_buf, -FOOTER_ROWS(ps_global), opts,
'y', 'z',
(double_rad +
((call_mailer_flags & CM_DSN_SHOW)
@@ -5174,11 +5236,13 @@ cancel_for_pico(void (*redraw_pico)(void))
{'c', 'c', "C", N_("Confirm")},
{'n', 'n', "N", N_("No")},
{'y', 'y', "", ""},
+ {'t', 't', "T", N_("CounT")},
{-1, 0, NULL, NULL}
};
ps_global->redrawer = redraw_pico;
fix_windsize(ps_global);
+ pbf->curpos[0] = '\0';
while(1){
rv = radio_buttons(prompt, -FOOTER_ROWS(ps_global), opts,
@@ -5191,12 +5255,16 @@ cancel_for_pico(void (*redraw_pico)(void))
q_status_message(SM_INFO, 1, 3, _(" Type \"C\" to cancel message "));
display_message('x');
}
+ else if(rv == 't'){
+ showcpos(1,0);
+ break;
+ }
else
break;
}
ps_global->redrawer = redraw;
- return(rstr);
+ return(pbf->curpos[0] ? pbf->curpos : rstr);
}
@@ -5300,9 +5368,11 @@ filter_message_text(char *fcmd, ENVELOPE *outgoing, struct mail_bodystruct *body
if((tmp_so = so_get(PicoText, NULL, EDIT_ACCESS)) != NULL){
gf_set_so_writec(&pc, tmp_so);
ps_global->mangled_screen = 1;
- suspend_busy_cue();
- ClearScreen();
- fflush(stdout);
+ if (!ps_global->send_immediately){
+ suspend_busy_cue();
+ ClearScreen();
+ fflush(stdout);
+ }
if(tmpf){
PIPE_S *fpipe;
@@ -5414,8 +5484,10 @@ filter_message_text(char *fcmd, ENVELOPE *outgoing, struct mail_bodystruct *body
set_mime_type_by_grope(b);
}
- ClearScreen();
- resume_busy_cue(0);
+ if (!ps_global->send_immediately){
+ ClearScreen();
+ resume_busy_cue(0);
+ }
}
else
errstr = "Can't create space for filtered text.";
@@ -5446,10 +5518,16 @@ filter_message_text(char *fcmd, ENVELOPE *outgoing, struct mail_bodystruct *body
if(tmp_so)
so_give(&tmp_so);
- q_status_message1(SM_ORDER | SM_DING, 3, 6, _("Problem filtering: %s"),
+ if (!ps_global->send_immediately){
+ q_status_message1(SM_ORDER | SM_DING, 3, 6, _("Problem filtering: %s"),
errstr);
- dprint((1, "Filter FAILED: %s\n",
+ dprint((1, "Filter FAILED: %s\n",
errstr ? errstr : "?"));
+ }
+ else{
+ fprintf(stderr, _("Filter FAILED: %s\n"), errstr ? errstr : "?");
+ exit(-1);
+ }
}
return(errstr == NULL);
diff --git a/alpine/setup.c b/alpine/setup.c
index 3ac90f21..4a30bec9 100644
--- a/alpine/setup.c
+++ b/alpine/setup.c
@@ -258,7 +258,7 @@ option_screen(struct pine *ps, int edit_exceptions)
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_exceptions)
}
}
}
+ 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;
@@ -335,6 +384,7 @@ option_screen(struct pine *ps, int edit_exceptions)
}
else{
if(vtmp == &ps->vars[V_FILLCOL]
+ || vtmp == &ps->vars[V_SLEEP]
|| vtmp == &ps->vars[V_QUOTE_SUPPRESSION]
|| vtmp == &ps->vars[V_OVERLAP]
|| vtmp == &ps->vars[V_MAXREMSTREAM]
@@ -464,6 +514,15 @@ option_screen(struct pine *ps, int edit_exceptions)
}
}
+ 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 --git a/alpine/status.c b/alpine/status.c
index b51c640f..4dd44817 100644
--- a/alpine/status.c
+++ b/alpine/status.c
@@ -111,6 +111,9 @@ q_status_message(int flags, int min_time, int max_time, char *message)
char *clean_msg;
size_t mlen;
+ if (ps_global->send_immediately)
+ return;
+
status_message_lock();
/*
@@ -605,6 +608,9 @@ flush_status_messages(int skip_last_pause)
SMQ_T *q, *copy_of_q;
int ding;
+ if(ps_global->send_immediately)
+ return;
+
start_over:
status_message_lock();
diff --git a/autom4te.cache/output.0 b/autom4te.cache/output.0
index 22b69dee..09a3082f 100644
--- a/autom4te.cache/output.0
+++ b/autom4te.cache/output.0
@@ -1,7 +1,7 @@
@%:@! /bin/sh
@%:@ From configure.ac Rev:1 by chappa@washington.edu.
@%:@ Guess values for system-dependent variables and create Makefiles.
-@%:@ Generated by GNU Autoconf 2.69 for alpine 2.10.9.
+@%:@ Generated by GNU Autoconf 2.69 for alpine 2.11.
@%:@
@%:@ Report bugs to <chappa@washington.edu>.
@%:@
@@ -730,8 +730,8 @@ MAKEFLAGS=
# Identity of this package.
PACKAGE_NAME='alpine'
PACKAGE_TARNAME='alpine'
-PACKAGE_VERSION='2.10.9'
-PACKAGE_STRING='alpine 2.10.9'
+PACKAGE_VERSION='2.11'
+PACKAGE_STRING='alpine 2.11'
PACKAGE_BUGREPORT='chappa@washington.edu'
PACKAGE_URL=''
@@ -1593,7 +1593,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF
-\`configure' configures alpine 2.10.9 to adapt to many kinds of systems.
+\`configure' configures alpine 2.11 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1663,7 +1663,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
- short | recursive ) echo "Configuration of alpine 2.10.9:";;
+ short | recursive ) echo "Configuration of alpine 2.11:";;
esac
cat <<\_ACEOF
@@ -1947,7 +1947,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
-alpine configure 2.10.9
+alpine configure 2.11
generated by GNU Autoconf 2.69
Copyright (C) 2012 Free Software Foundation, Inc.
@@ -2553,7 +2553,7 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
-It was created by alpine $as_me 2.10.9, which was
+It was created by alpine $as_me 2.11, which was
generated by GNU Autoconf 2.69. Invocation command line was
$ $0 $@
@@ -3374,7 +3374,7 @@ fi
# Define the identity of the package.
PACKAGE='alpine'
- VERSION='2.10.9'
+ VERSION='2.11'
cat >>confdefs.h <<_ACEOF
@@ -19929,7 +19929,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
-This file was extended by alpine $as_me 2.10.9, which was
+This file was extended by alpine $as_me 2.11, which was
generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -19995,7 +19995,7 @@ _ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\
-alpine config.status 2.10.9
+alpine config.status 2.11
configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\"
diff --git a/autom4te.cache/output.1 b/autom4te.cache/output.1
index 22b69dee..09a3082f 100644
--- a/autom4te.cache/output.1
+++ b/autom4te.cache/output.1
@@ -1,7 +1,7 @@
@%:@! /bin/sh
@%:@ From configure.ac Rev:1 by chappa@washington.edu.
@%:@ Guess values for system-dependent variables and create Makefiles.
-@%:@ Generated by GNU Autoconf 2.69 for alpine 2.10.9.
+@%:@ Generated by GNU Autoconf 2.69 for alpine 2.11.
@%:@
@%:@ Report bugs to <chappa@washington.edu>.
@%:@
@@ -730,8 +730,8 @@ MAKEFLAGS=
# Identity of this package.
PACKAGE_NAME='alpine'
PACKAGE_TARNAME='alpine'
-PACKAGE_VERSION='2.10.9'
-PACKAGE_STRING='alpine 2.10.9'
+PACKAGE_VERSION='2.11'
+PACKAGE_STRING='alpine 2.11'
PACKAGE_BUGREPORT='chappa@washington.edu'
PACKAGE_URL=''
@@ -1593,7 +1593,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF
-\`configure' configures alpine 2.10.9 to adapt to many kinds of systems.
+\`configure' configures alpine 2.11 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1663,7 +1663,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
- short | recursive ) echo "Configuration of alpine 2.10.9:";;
+ short | recursive ) echo "Configuration of alpine 2.11:";;
esac
cat <<\_ACEOF
@@ -1947,7 +1947,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
-alpine configure 2.10.9
+alpine configure 2.11
generated by GNU Autoconf 2.69
Copyright (C) 2012 Free Software Foundation, Inc.
@@ -2553,7 +2553,7 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
-It was created by alpine $as_me 2.10.9, which was
+It was created by alpine $as_me 2.11, which was
generated by GNU Autoconf 2.69. Invocation command line was
$ $0 $@
@@ -3374,7 +3374,7 @@ fi
# Define the identity of the package.
PACKAGE='alpine'
- VERSION='2.10.9'
+ VERSION='2.11'
cat >>confdefs.h <<_ACEOF
@@ -19929,7 +19929,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
-This file was extended by alpine $as_me 2.10.9, which was
+This file was extended by alpine $as_me 2.11, which was
generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -19995,7 +19995,7 @@ _ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\
-alpine config.status 2.10.9
+alpine config.status 2.11
configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\"
diff --git a/autom4te.cache/requests b/autom4te.cache/requests
index e819e61d..3e12765c 100644
--- a/autom4te.cache/requests
+++ b/autom4te.cache/requests
@@ -86,13 +86,13 @@
'gt_PRINTF_POSIX' => 1,
'AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH' => 1,
'_LT_AC_LANG_C_CONFIG' => 1,
- 'AM_PROG_INSTALL_STRIP' => 1,
'gl_LOCK_EARLY' => 1,
+ 'AM_PROG_INSTALL_STRIP' => 1,
'_m4_warn' => 1,
'AC_LIBTOOL_OBJDIR' => 1,
'gl_FUNC_ARGZ' => 1,
- 'AM_SANITY_CHECK' => 1,
'LTOBSOLETE_VERSION' => 1,
+ 'AM_SANITY_CHECK' => 1,
'AC_LIBTOOL_LANG_GCJ_CONFIG' => 1,
'AC_LIBTOOL_PROG_COMPILER_PIC' => 1,
'LT_LIB_M' => 1,
@@ -104,21 +104,21 @@
'_AM_PROG_TAR' => 1,
'LT_SYS_SYMBOL_USCORE' => 1,
'AC_LIBTOOL_GCJ' => 1,
- 'LT_FUNC_DLSYM_USCORE' => 1,
'LT_SYS_DLOPEN_DEPLIBS' => 1,
- '_LT_AC_LANG_F77' => 1,
- 'AC_LIBTOOL_CONFIG' => 1,
+ 'LT_FUNC_DLSYM_USCORE' => 1,
'AC_LIB_ARG_WITH' => 1,
- '_AM_SUBST_NOTMAKE' => 1,
+ 'AC_LIBTOOL_CONFIG' => 1,
+ '_LT_AC_LANG_F77' => 1,
'AC_LTDL_DLLIB' => 1,
+ '_AM_SUBST_NOTMAKE' => 1,
'_AM_AUTOCONF_VERSION' => 1,
'AM_DISABLE_SHARED' => 1,
'_LTDL_SETUP' => 1,
- '_LT_AC_LANG_CXX' => 1,
'AM_PROG_LIBTOOL' => 1,
- 'AC_LIB_LTDL' => 1,
- '_LT_AC_FILE_LTDLL_C' => 1,
+ '_LT_AC_LANG_CXX' => 1,
'AM_PROG_LD' => 1,
+ '_LT_AC_FILE_LTDLL_C' => 1,
+ 'AC_LIB_LTDL' => 1,
'gt_INTL_MACOSX' => 1,
'AM_ICONV_LINK' => 1,
'AC_LIB_PREPARE_MULTILIB' => 1,
@@ -135,60 +135,60 @@
'LTDL_CONVENIENCE' => 1,
'_AM_SET_OPTION' => 1,
'AC_LTDL_PREOPEN' => 1,
- '_LT_LINKER_BOILERPLATE' => 1,
'gl_LOCK_EARLY_BODY' => 1,
- 'AC_LIBTOOL_PROG_CC_C_O' => 1,
+ '_LT_LINKER_BOILERPLATE' => 1,
'AC_LIBTOOL_LANG_CXX_CONFIG' => 1,
+ 'AC_LIBTOOL_PROG_CC_C_O' => 1,
'gl_PREREQ_ARGZ' => 1,
'AC_LIB_PREFIX' => 1,
'gt_TYPE_LONGDOUBLE' => 1,
- 'AM_OUTPUT_DEPENDENCY_COMMANDS' => 1,
'LT_SUPPORTED_TAG' => 1,
- 'LT_SYS_MODULE_EXT' => 1,
+ 'AM_OUTPUT_DEPENDENCY_COMMANDS' => 1,
'LT_PROG_RC' => 1,
+ 'LT_SYS_MODULE_EXT' => 1,
'AC_DEFUN_ONCE' => 1,
'_LT_AC_LANG_GCJ' => 1,
'AC_' => 1,
'AC_LTDL_OBJDIR' => 1,
- '_LT_PATH_TOOL_PREFIX' => 1,
'gt_INTDIV0' => 1,
- 'AC_LIBTOOL_RC' => 1,
+ '_LT_PATH_TOOL_PREFIX' => 1,
'AM_ICONV' => 1,
- 'AM_SILENT_RULES' => 1,
- 'AC_DISABLE_FAST_INSTALL' => 1,
+ 'AC_LIBTOOL_RC' => 1,
'_LT_AC_PROG_ECHO_BACKSLASH' => 1,
- '_LT_AC_SYS_LIBPATH_AIX' => 1,
- '_LT_AC_TRY_DLOPEN_SELF' => 1,
+ 'AC_DISABLE_FAST_INSTALL' => 1,
+ 'AM_SILENT_RULES' => 1,
'include' => 1,
+ '_LT_AC_TRY_DLOPEN_SELF' => 1,
+ '_LT_AC_SYS_LIBPATH_AIX' => 1,
'LT_AC_PROG_SED' => 1,
'AM_ENABLE_SHARED' => 1,
- 'gl_AC_TYPE_UNSIGNED_LONG_LONG' => 1,
'AC_LIB_APPENDTOVAR' => 1,
- 'LTDL_INSTALLABLE' => 1,
+ 'gl_AC_TYPE_UNSIGNED_LONG_LONG' => 1,
'AM_GNU_GETTEXT' => 1,
+ 'LTDL_INSTALLABLE' => 1,
'_LT_AC_LANG_GCJ_CONFIG' => 1,
- 'AC_ENABLE_SHARED' => 1,
'AM_POSTPROCESS_PO_MAKEFILE' => 1,
+ 'AC_ENABLE_SHARED' => 1,
'AC_LIB_WITH_FINAL_PREFIX' => 1,
'gt_TYPE_WINT_T' => 1,
- 'AC_ENABLE_STATIC' => 1,
'AC_LIBTOOL_SYS_HARD_LINK_LOCKS' => 1,
+ 'AC_ENABLE_STATIC' => 1,
'_LT_AC_TAGVAR' => 1,
'AC_LIBTOOL_LANG_F77_CONFIG' => 1,
'AM_CONDITIONAL' => 1,
'LT_LIB_DLLOAD' => 1,
'AM_LANGINFO_CODESET' => 1,
'gl_AC_HEADER_INTTYPES_H' => 1,
- 'LTDL_INIT' => 1,
'LTVERSION_VERSION' => 1,
- 'AM_PROG_INSTALL_SH' => 1,
+ 'LTDL_INIT' => 1,
'm4_include' => 1,
+ 'AM_PROG_INSTALL_SH' => 1,
'AC_PROG_EGREP' => 1,
- '_AC_AM_CONFIG_HEADER_HOOK' => 1,
- 'AC_PATH_MAGIC' => 1,
'PINEVAR' => 1,
- 'AC_LTDL_SYSSEARCHPATH' => 1,
+ 'AC_PATH_MAGIC' => 1,
+ '_AC_AM_CONFIG_HEADER_HOOK' => 1,
'gl_PREREQ_LOCK' => 1,
+ 'AC_LTDL_SYSSEARCHPATH' => 1,
'AM_MAKE_INCLUDE' => 1,
'LT_CMD_MAX_LEN' => 1,
'_LT_AC_TAGCONFIG' => 1,
@@ -200,96 +200,96 @@
'AC_LIBTOOL_COMPILER_OPTION' => 1,
'AC_DISABLE_SHARED' => 1,
'_LT_COMPILER_BOILERPLATE' => 1,
- 'AC_LIBTOOL_SETUP' => 1,
'AC_LIBTOOL_WIN32_DLL' => 1,
+ 'AC_LIBTOOL_SETUP' => 1,
'AC_PROG_LD_RELOAD_FLAG' => 1,
'AC_LTDL_DLSYM_USCORE' => 1,
- 'LT_LANG' => 1,
'AM_MISSING_HAS_RUN' => 1,
- 'LT_SYS_DLSEARCH_PATH' => 1,
+ 'LT_LANG' => 1,
'AC_TYPE_LONG_LONG_INT' => 1,
+ 'LT_SYS_DLSEARCH_PATH' => 1,
'LT_CONFIG_LTDL_DIR' => 1,
- 'LT_OUTPUT' => 1,
- 'AC_LIBTOOL_DLOPEN_SELF' => 1,
'AM_GNU_GETTEXT_VERSION' => 1,
+ 'AC_LIBTOOL_DLOPEN_SELF' => 1,
+ 'LT_OUTPUT' => 1,
'AC_LIB_PROG_LD_GNU' => 1,
- 'AC_LIBTOOL_PROG_LD_SHLIBS' => 1,
'AM_NLS' => 1,
- 'AC_LIBTOOL_LINKER_OPTION' => 1,
+ 'AC_LIBTOOL_PROG_LD_SHLIBS' => 1,
'AC_WITH_LTDL' => 1,
+ 'AC_LIBTOOL_LINKER_OPTION' => 1,
'gt_INTL_SUBDIR_CORE' => 1,
- 'AC_LIBTOOL_CXX' => 1,
'LT_AC_PROG_RC' => 1,
+ 'AC_LIBTOOL_CXX' => 1,
'LT_INIT' => 1,
- 'LT_SYS_DLOPEN_SELF' => 1,
'LT_AC_PROG_GCJ' => 1,
- 'AM_DISABLE_STATIC' => 1,
+ 'LT_SYS_DLOPEN_SELF' => 1,
'AM_DEP_TRACK' => 1,
+ 'AM_DISABLE_STATIC' => 1,
'_AC_PROG_LIBTOOL' => 1,
'_AM_IF_OPTION' => 1,
'AC_PATH_TOOL_PREFIX' => 1,
- 'AC_LIBTOOL_F77' => 1,
'm4_pattern_allow' => 1,
+ 'AC_LIBTOOL_F77' => 1,
'AM_PATH_PROG_WITH_TEST' => 1,
'AM_SET_LEADING_DOT' => 1,
'LT_AC_PROG_EGREP' => 1,
'_AM_DEPENDENCIES' => 1,
- 'AC_LIBTOOL_LANG_C_CONFIG' => 1,
'gt_CHECK_DECL' => 1,
+ 'AC_LIBTOOL_LANG_C_CONFIG' => 1,
'LTOPTIONS_VERSION' => 1,
- '_LT_AC_SYS_COMPILER' => 1,
'AC_LIB_LINKFLAGS' => 1,
+ '_LT_AC_SYS_COMPILER' => 1,
'AM_PROG_NM' => 1,
'AC_LIBLTDL_CONVENIENCE' => 1,
- 'AC_DEPLIBS_CHECK_METHOD' => 1,
'AM_GNU_GETTEXT_NEED' => 1,
- 'AM_SET_CURRENT_AUTOMAKE_VERSION' => 1,
- 'jm_MAINTAINER_MODE' => 1,
+ 'AC_DEPLIBS_CHECK_METHOD' => 1,
'AC_LIBLTDL_INSTALLABLE' => 1,
- 'AC_LTDL_ENABLE_INSTALL' => 1,
+ 'jm_MAINTAINER_MODE' => 1,
+ 'AM_SET_CURRENT_AUTOMAKE_VERSION' => 1,
'gl_XSIZE' => 1,
- 'AC_LIBTOOL_SYS_DYNAMIC_LINKER' => 1,
+ 'AC_LTDL_ENABLE_INSTALL' => 1,
'LT_PROG_GCJ' => 1,
+ 'AC_LIBTOOL_SYS_DYNAMIC_LINKER' => 1,
'gt_GLIBC2' => 1,
'AM_INIT_AUTOMAKE' => 1,
'gl_AC_TYPE_UINTMAX_T' => 1,
'gl_LOCK' => 1,
'AM_INTL_SUBDIR' => 1,
- 'AC_DISABLE_STATIC' => 1,
'gl_VISIBILITY' => 1,
- 'gt_TYPE_WCHAR_T' => 1,
+ 'AC_DISABLE_STATIC' => 1,
'PINEVAR_UNQUOTED' => 1,
+ 'gt_TYPE_WCHAR_T' => 1,
'LT_PATH_NM' => 1,
'AM_MAINTAINER_MODE' => 1,
'AC_LTDL_SHLIBEXT' => 1,
'_LT_AC_LOCK' => 1,
'_LT_AC_LANG_RC_CONFIG' => 1,
'LT_SYS_MODULE_PATH' => 1,
- 'AC_LIBTOOL_POSTDEP_PREDEP' => 1,
'LT_WITH_LTDL' => 1,
+ 'AC_LIBTOOL_POSTDEP_PREDEP' => 1,
'AC_LTDL_SHLIBPATH' => 1,
'AM_AUX_DIR_EXPAND' => 1,
- 'gl_GLIBC21' => 1,
'AC_LIB_LINKFLAGS_FROM_LIBS' => 1,
- '_LT_AC_LANG_F77_CONFIG' => 1,
+ 'gl_GLIBC21' => 1,
'AC_LIBTOOL_PROG_COMPILER_NO_RTTI' => 1,
- '_AM_SET_OPTIONS' => 1,
+ '_LT_AC_LANG_F77_CONFIG' => 1,
'_LT_COMPILER_OPTION' => 1,
- '_AM_OUTPUT_DEPENDENCY_COMMANDS' => 1,
+ '_AM_SET_OPTIONS' => 1,
'AM_RUN_LOG' => 1,
- 'AC_LIBTOOL_SYS_OLD_ARCHIVE' => 1,
- 'AC_LTDL_SYS_DLOPEN_DEPLIBS' => 1,
+ '_AM_OUTPUT_DEPENDENCY_COMMANDS' => 1,
'AC_LIBTOOL_PICMODE' => 1,
+ 'AC_LTDL_SYS_DLOPEN_DEPLIBS' => 1,
+ 'AC_LIBTOOL_SYS_OLD_ARCHIVE' => 1,
'AC_LIB_LINKFLAGS_BODY' => 1,
- 'LT_PATH_LD' => 1,
- 'AC_CHECK_LIBM' => 1,
'ACX_PTHREAD' => 1,
+ 'AC_CHECK_LIBM' => 1,
+ 'LT_PATH_LD' => 1,
'AC_LIBTOOL_SYS_LIB_STRIP' => 1,
- '_AM_MANGLE_OPTION' => 1,
- 'gt_LC_MESSAGES' => 1,
'AC_TYPE_UNSIGNED_LONG_LONG_INT' => 1,
- 'AC_LTDL_SYMBOL_USCORE' => 1,
+ 'gt_LC_MESSAGES' => 1,
+ '_AM_MANGLE_OPTION' => 1,
'AC_LIBTOOL_SYS_MAX_CMD_LEN' => 1,
+ 'AC_LTDL_SYMBOL_USCORE' => 1,
'AM_SET_DEPDIR' => 1,
'_LT_CC_BASENAME' => 1,
'gl_LOCK_BODY' => 1,
@@ -309,57 +309,57 @@
'configure.ac'
],
{
- '_LT_AC_TAGCONFIG' => 1,
'AM_PROG_F77_C_O' => 1,
- 'm4_pattern_forbid' => 1,
+ '_LT_AC_TAGCONFIG' => 1,
'AC_INIT' => 1,
- '_AM_COND_IF' => 1,
+ 'm4_pattern_forbid' => 1,
'AC_CANONICAL_TARGET' => 1,
- 'AC_SUBST' => 1,
+ '_AM_COND_IF' => 1,
'AC_CONFIG_LIBOBJ_DIR' => 1,
- 'AC_FC_SRCEXT' => 1,
+ 'AC_SUBST' => 1,
'AC_CANONICAL_HOST' => 1,
+ 'AC_FC_SRCEXT' => 1,
'AC_PROG_LIBTOOL' => 1,
'AM_INIT_AUTOMAKE' => 1,
- 'AM_PATH_GUILE' => 1,
'AC_CONFIG_SUBDIRS' => 1,
+ 'AM_PATH_GUILE' => 1,
'AM_AUTOMAKE_VERSION' => 1,
'LT_CONFIG_LTDL_DIR' => 1,
- 'AC_REQUIRE_AUX_FILE' => 1,
'AC_CONFIG_LINKS' => 1,
- 'm4_sinclude' => 1,
+ 'AC_REQUIRE_AUX_FILE' => 1,
'LT_SUPPORTED_TAG' => 1,
+ 'm4_sinclude' => 1,
'AM_MAINTAINER_MODE' => 1,
'AM_NLS' => 1,
'AC_FC_PP_DEFINE' => 1,
'AM_GNU_GETTEXT_INTL_SUBDIR' => 1,
- 'AM_MAKEFILE_INCLUDE' => 1,
'_m4_warn' => 1,
+ 'AM_MAKEFILE_INCLUDE' => 1,
'AM_PROG_CXX_C_O' => 1,
- '_AM_COND_ENDIF' => 1,
'_AM_MAKEFILE_INCLUDE' => 1,
+ '_AM_COND_ENDIF' => 1,
'AM_ENABLE_MULTILIB' => 1,
'AM_SILENT_RULES' => 1,
'AM_PROG_MOC' => 1,
'AC_CONFIG_FILES' => 1,
- 'LT_INIT' => 1,
'include' => 1,
- 'AM_PROG_AR' => 1,
+ 'LT_INIT' => 1,
'AM_GNU_GETTEXT' => 1,
+ 'AM_PROG_AR' => 1,
'AC_LIBSOURCE' => 1,
- 'AM_PROG_FC_C_O' => 1,
'AC_CANONICAL_BUILD' => 1,
+ 'AM_PROG_FC_C_O' => 1,
'AC_FC_FREEFORM' => 1,
- 'AH_OUTPUT' => 1,
'AC_FC_PP_SRCEXT' => 1,
- '_AM_SUBST_NOTMAKE' => 1,
+ 'AH_OUTPUT' => 1,
'AC_CONFIG_AUX_DIR' => 1,
- 'sinclude' => 1,
- 'AM_PROG_CC_C_O' => 1,
+ '_AM_SUBST_NOTMAKE' => 1,
'm4_pattern_allow' => 1,
- 'AM_XGETTEXT_OPTION' => 1,
- 'AC_CANONICAL_SYSTEM' => 1,
+ 'AM_PROG_CC_C_O' => 1,
+ 'sinclude' => 1,
'AM_CONDITIONAL' => 1,
+ 'AC_CANONICAL_SYSTEM' => 1,
+ 'AM_XGETTEXT_OPTION' => 1,
'AC_CONFIG_HEADERS' => 1,
'AC_DEFINE_TRACE_LITERAL' => 1,
'AM_POT_TOOLS' => 1,
diff --git a/autom4te.cache/traces.1 b/autom4te.cache/traces.1
index a6a4ae01..ff7f24a9 100644
--- a/autom4te.cache/traces.1
+++ b/autom4te.cache/traces.1
@@ -13,7 +13,7 @@ m4trace:aclocal.m4:1003: -1- m4_include([m4/nls.m4])
m4trace:aclocal.m4:1004: -1- m4_include([m4/po.m4])
m4trace:aclocal.m4:1005: -1- m4_include([m4/progtest.m4])
m4trace:configure.ac:21: -3- m4_include([VERSION])
-m4trace:configure.ac:21: -1- AC_INIT([alpine], [2.10.9], [chappa@washington.edu])
+m4trace:configure.ac:21: -1- AC_INIT([alpine], [2.11], [chappa@washington.edu])
m4trace:configure.ac:21: -1- m4_pattern_forbid([^_?A[CHUM]_])
m4trace:configure.ac:21: -1- m4_pattern_forbid([_AC_])
m4trace:configure.ac:21: -1- m4_pattern_forbid([^LIBOBJS$], [do not use LIBOBJS directly, use AC_LIBOBJ (see section `AC_LIBOBJ vs LIBOBJS'])
diff --git a/configure b/configure
index dbed91f7..4a1c385e 100755
--- a/configure
+++ b/configure
@@ -1,7 +1,7 @@
#! /bin/sh
# From configure.ac Rev:1 by chappa@washington.edu.
# Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.69 for alpine 2.10.9.
+# Generated by GNU Autoconf 2.69 for alpine 2.11.
#
# Report bugs to <chappa@washington.edu>.
#
@@ -730,8 +730,8 @@ MAKEFLAGS=
# Identity of this package.
PACKAGE_NAME='alpine'
PACKAGE_TARNAME='alpine'
-PACKAGE_VERSION='2.10.9'
-PACKAGE_STRING='alpine 2.10.9'
+PACKAGE_VERSION='2.11'
+PACKAGE_STRING='alpine 2.11'
PACKAGE_BUGREPORT='chappa@washington.edu'
PACKAGE_URL=''
@@ -1593,7 +1593,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF
-\`configure' configures alpine 2.10.9 to adapt to many kinds of systems.
+\`configure' configures alpine 2.11 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1663,7 +1663,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
- short | recursive ) echo "Configuration of alpine 2.10.9:";;
+ short | recursive ) echo "Configuration of alpine 2.11:";;
esac
cat <<\_ACEOF
@@ -1947,7 +1947,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
-alpine configure 2.10.9
+alpine configure 2.11
generated by GNU Autoconf 2.69
Copyright (C) 2012 Free Software Foundation, Inc.
@@ -2553,7 +2553,7 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
-It was created by alpine $as_me 2.10.9, which was
+It was created by alpine $as_me 2.11, which was
generated by GNU Autoconf 2.69. Invocation command line was
$ $0 $@
@@ -3374,7 +3374,7 @@ fi
# Define the identity of the package.
PACKAGE='alpine'
- VERSION='2.10.9'
+ VERSION='2.11'
cat >>confdefs.h <<_ACEOF
@@ -19929,7 +19929,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
-This file was extended by alpine $as_me 2.10.9, which was
+This file was extended by alpine $as_me 2.11, which was
generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -19995,7 +19995,7 @@ _ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\
-alpine config.status 2.10.9
+alpine config.status 2.11
configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\"
diff --git a/doc/alpine.1 b/doc/alpine.1
index 6ae54416..96bc462d 100644
--- a/doc/alpine.1
+++ b/doc/alpine.1
@@ -164,6 +164,8 @@ Use function keys for commands. This is the same as running the command
.IP \fB-n\ \fInumber\fR 20
Start up with current message-number set to
.I number.
+.IP \fB-noutf8\fR 20
+Warns Alpine that piped input is not encoded in UTF-8.
.IP \fB-o\fR 20
Open first folder read-only.
.IP \fB-p\ \fIconfig-file\fR 20
diff --git a/imap/src/c-client/imap4r1.c b/imap/src/c-client/imap4r1.c
index 1409b37d..96d1fa19 100644
--- a/imap/src/c-client/imap4r1.c
+++ b/imap/src/c-client/imap4r1.c
@@ -1156,6 +1156,7 @@ long imap_auth (MAILSTREAM *stream,NETMBX *mb,char *tmp,char *usr)
}
/* no error if protocol-initiated cancel */
lsterr = cpystr (reply->text);
+ delete_password(mb, usr);
}
}
while (LOCAL->netstream && !LOCAL->byeseen && trial &&
@@ -1207,6 +1208,7 @@ long imap_login (MAILSTREAM *stream,NETMBX *mb,char *pwd,char *usr)
if (imap_OK (stream,reply = imap_send (stream,"LOGIN",args)))
ret = LONGT; /* success */
else {
+ delete_password(mb, usr);
mm_log (reply->text,WARN);
if (!LOCAL->referral && (trial == imap_maxlogintrials))
mm_log ("Too many login failures",ERROR);
diff --git a/imap/src/c-client/mail.c b/imap/src/c-client/mail.c
index d80a01f6..8fd55265 100644
--- a/imap/src/c-client/mail.c
+++ b/imap/src/c-client/mail.c
@@ -52,6 +52,8 @@ static mailcache_t mailcache = mm_cache;
static rfc822out_t mail822out = NIL;
/* RFC-822 output generator (new style) */
static rfc822outfull_t mail822outfull = NIL;
+ /* Erase password (client side) */
+static deletepwd_t erase_password = NIL;
/* SMTP verbose callback */
static smtpverbose_t mailsmtpverbose = mm_dlog;
/* proxy copy routine */
@@ -544,6 +546,11 @@ void *mail_parameters (MAILSTREAM *stream,long function,void *value)
case GET_SENDCOMMAND:
ret = (void *) mailsendcommand;
break;
+ case SET_ERASEPASSWORD:
+ erase_password = (deletepwd_t) value;
+ case GET_ERASEPASSWORD:
+ ret = (void *) erase_password;
+ break;
case SET_SERVICENAME:
servicename = (char *) value;
@@ -991,7 +998,7 @@ long mail_create (MAILSTREAM *stream,char *mailbox)
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 +1022,8 @@ long mail_create (MAILSTREAM *stream,char *mailbox)
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 +1054,13 @@ long mail_create (MAILSTREAM *stream,char *mailbox)
(((*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);
@@ -3352,13 +3368,13 @@ unsigned long mail_filter (char *text,unsigned long len,STRINGLIST *lines,
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;
@@ -3397,6 +3413,10 @@ unsigned long mail_filter (char *text,unsigned long len,STRINGLIST *lines,
}
}
*dst = '\0'; /* tie off destination */
+ if(fix){
+ text[len] = '\012';
+ text[len-1] = '\0';
+ }
return dst - text;
}
@@ -6121,6 +6141,15 @@ unsigned int mail_lookup_auth_name (char *mechanism,long flags)
return i;
return 0;
}
+/* Client side callback warning to delete wrong password
+ *
+ */
+void delete_password(NETMBX *mb, char *user)
+{
+ deletepwd_t ep = mail_parameters(NULL, GET_ERASEPASSWORD, NULL);
+ if (ep) (ep)(mb, user);
+}
+
/* Standard TCP/IP network driver */
diff --git a/imap/src/c-client/mail.h b/imap/src/c-client/mail.h
index 174993e1..0af362e6 100644
--- a/imap/src/c-client/mail.h
+++ b/imap/src/c-client/mail.h
@@ -177,6 +177,8 @@
#define SET_EXTERNALAUTHID (long) 230
#define GET_SSLCAPATH (long) 231
#define SET_SSLCAPATH (long) 232
+#define GET_ERASEPASSWORD (long) 233
+#define SET_ERASEPASSWORD (long) 234
/* 3xx: TCP/IP */
#define GET_OPENTIMEOUT (long) 300
@@ -353,6 +355,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 */
@@ -1326,6 +1332,7 @@ typedef ADDRESS *(*parsephrase_t) (char *phrase,char *end,char *host);
typedef void *(*blocknotify_t) (int reason,void *data);
typedef long (*kinit_t) (char *host,char *reason);
typedef void (*sendcommand_t) (MAILSTREAM *stream,char *cmd,long flags);
+typedef void (*deletepwd_t) (NETMBX *mb,char *user);
typedef char *(*newsrcquery_t) (MAILSTREAM *stream,char *mulname,char *name);
typedef void (*getacl_t) (MAILSTREAM *stream,char *mailbox,ACLLIST *acl);
typedef void (*listrights_t) (MAILSTREAM *stream,char *mailbox,char *id,
@@ -1605,6 +1612,8 @@ long mm_diskerror (MAILSTREAM *stream,long errcode,long serious);
void mm_fatal (char *string);
void *mm_cache (MAILSTREAM *stream,unsigned long msgno,long op);
+void delete_password (NETMBX *mb, char *user);
+
extern STRINGDRIVER mail_string;
void mail_versioncheck (char *version);
void mail_link (DRIVER *driver);
diff --git a/imap/src/c-client/nntp.c b/imap/src/c-client/nntp.c
index fe90edba..8fbcb9b7 100644
--- a/imap/src/c-client/nntp.c
+++ b/imap/src/c-client/nntp.c
@@ -2031,6 +2031,7 @@ long nntp_send_auth_work (SENDSTREAM *stream,NETMBX *mb,char *pwd,long flags)
sprintf (tmp,"Retrying using %s authentication after %.80s",
at->name,lsterr);
mm_log (tmp,NIL);
+ delete_password(mb, mb ? mb->user : NULL);
fs_give ((void **) &lsterr);
}
trial = 0; /* initial trial count */
@@ -2039,6 +2040,7 @@ long nntp_send_auth_work (SENDSTREAM *stream,NETMBX *mb,char *pwd,long flags)
if (lsterr) {
sprintf (tmp,"Retrying %s authentication after %.80s",at->name,lsterr);
mm_log (tmp,WARN);
+ delete_password(mb, mb ? mb->user : NULL);
fs_give ((void **) &lsterr);
}
stream->saslcancel = NIL;
@@ -2064,6 +2066,7 @@ long nntp_send_auth_work (SENDSTREAM *stream,NETMBX *mb,char *pwd,long flags)
sprintf (tmp,"Can not authenticate to NNTP server: %.80s",lsterr);
mm_log (tmp,ERROR);
}
+ delete_password(mb, mb ? mb->user : NULL);
fs_give ((void **) &lsterr);
}
else if (mb->secflag) /* no SASL, can't do /secure */
@@ -2092,6 +2095,8 @@ long nntp_send_auth_work (SENDSTREAM *stream,NETMBX *mb,char *pwd,long flags)
stream->sensitive = T; /* hide this command */
if (nntp_send_work (stream,"AUTHINFO PASS",pwd) == NNTPAUTHED)
ret = LONGT; /* password OK */
+ else
+ delete_password(mb, mb ? mb->user : NULL);
stream->sensitive = NIL; /* unhide */
if (ret) break; /* OK if successful */
default: /* authentication failed */
diff --git a/imap/src/c-client/pop3.c b/imap/src/c-client/pop3.c
index 58a9ceb6..1b411546 100644
--- a/imap/src/c-client/pop3.c
+++ b/imap/src/c-client/pop3.c
@@ -615,6 +615,7 @@ long pop3_auth (MAILSTREAM *stream,NETMBX *mb,char *pwd,char *usr)
sprintf (pwd,"Retrying using %.80s authentication after %.80s",
at->name,t);
mm_log (pwd,NIL);
+ delete_password(mb, usr);
fs_give ((void **) &t);
}
trial = 0; /* initial trial count */
@@ -622,6 +623,7 @@ long pop3_auth (MAILSTREAM *stream,NETMBX *mb,char *pwd,char *usr)
if (t) {
sprintf (pwd,"Retrying %s authentication after %.80s",at->name,t);
mm_log (pwd,WARN);
+ delete_password(mb, usr);
fs_give ((void **) &t);
}
LOCAL->saslcancel = NIL;
@@ -667,6 +669,7 @@ long pop3_auth (MAILSTREAM *stream,NETMBX *mb,char *pwd,char *usr)
LOCAL->sensitive=NIL; /* unhide */
}
if (!ret) { /* failure */
+ delete_password(mb, usr);
mm_log (LOCAL->reply,WARN);
if (trial == pop3_maxlogintrials)
mm_log ("Too many login failures",ERROR);
diff --git a/imap/src/osdep/unix/Makefile b/imap/src/osdep/unix/Makefile
index 78913acc..ae7b5b9e 100644
--- a/imap/src/osdep/unix/Makefile
+++ b/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
diff --git a/imap/src/osdep/unix/dummy.c b/imap/src/osdep/unix/dummy.c
index b003a0ba..92a2c463 100644
--- a/imap/src/osdep/unix/dummy.c
+++ b/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,char *mailbox)
{
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,char *mailbox)
{
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,char *mailbox)
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,char *old,char *newname)
}
}
/* 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 */
}
diff --git a/imap/src/osdep/unix/maildir.c b/imap/src/osdep/unix/maildir.c
new file mode 100644
index 00000000..b15adc87
--- /dev/null
+++ b/imap/src/osdep/unix/maildir.c
@@ -0,0 +1,2638 @@
+/*
+ * Maildir driver for Alpine 2.11
+ *
+ * Written by Eduardo Chappa <chappa@gmx.com>
+ * Last Update: May 29, 2011.
+ *
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include "mail.h"
+#include <pwd.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#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;
+ }
+ 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);
+ }
+ }
+
+ 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.
+*/
+}
+
+/* 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]);
+ }
+
+ 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;
+ }
+
+ 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)
+{
+ 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);
+ }
+}
diff --git a/imap/src/osdep/unix/maildir.h b/imap/src/osdep/unix/maildir.h
new file mode 100644
index 00000000..c1eef9e4
--- /dev/null
+++ b/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);
+
diff --git a/imap/src/osdep/unix/os_cyg.h b/imap/src/osdep/unix/os_cyg.h
index 061db332..81397225 100644
--- a/imap/src/osdep/unix/os_cyg.h
+++ b/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);
diff --git a/pico/basic.c b/pico/basic.c
index 49b04bd6..4479d099 100644
--- a/pico/basic.c
+++ b/pico/basic.c
@@ -26,9 +26,10 @@ static char rcsid[] = "$Id: basic.c 831 2007-11-27 01:04:19Z hubert@u.washington
* framing, are hard.
*/
#include "headers.h"
-
+#include "../pith/osdep/color.h"
#include "osdep/terminal.h"
+int indent_match(char **, LINE *, char *, int, int);
/*
* Move the cursor to the
@@ -285,7 +286,7 @@ int
gotobop(int f, int n)
{
int quoted, qlen;
- UCS qstr[NLINE], qstr2[NLINE];
+ char qstr[NLINE], qstr2[NLINE], ind_str[NLINE], pqstr[NLINE];;
if (n < 0) /* the other way...*/
return(gotoeop(f, -n));
@@ -297,6 +298,14 @@ gotobop(int f, int n)
curwp->w_dotp = lback(curwp->w_dotp);
curwp->w_doto = 0;
}
+
+ if (indent_match(default_qstr(glo_quote_str, 1), curwp->w_dotp,ind_str, NLINE, 0)){
+ if (n){ /* look for another paragraph ? */
+ curwp->w_dotp = lback(curwp->w_dotp);
+ continue;
+ }
+ break;
+ }
/* scan line by line until we come to a line ending with
* a <NL><NL> or <NL><TAB> or <NL><SPACE>
@@ -304,20 +313,58 @@ gotobop(int f, int n)
* PLUS: if there's a quote string, a quoted-to-non-quoted
* line transition.
*/
- quoted = glo_quote_str ? quote_match(glo_quote_str, curwp->w_dotp, qstr, NLINE) : 0;
- qlen = quoted ? ucs4_strlen(qstr) : 0;
+ quoted = quote_match(default_qstr(glo_quote_str, 1), curwp->w_dotp, qstr, NLINE, 0);
+ qlen = quoted ? strlen(qstr) : 0;
while(lback(curwp->w_dotp) != curbp->b_linep
&& llength(lback(curwp->w_dotp)) > qlen
- && (glo_quote_str
- ? (quoted == quote_match(glo_quote_str,
- lback(curwp->w_dotp),
- qstr2, NLINE)
- && !ucs4_strcmp(qstr, qstr2))
- : 1)
- && lgetc(curwp->w_dotp, qlen).c != TAB
- && lgetc(curwp->w_dotp, qlen).c != ' ')
+ && (quoted == quote_match(default_qstr(glo_quote_str, 1),
+ lback(curwp->w_dotp), qstr2, NLINE, 0))
+ && !strcmp(qstr, qstr2) /* processed string */
+ && (quoted == quote_match(default_qstr(glo_quote_str, 1),
+ lback(curwp->w_dotp), qstr2, NLINE, 1))
+ && !strcmp(qstr, qstr2) /* raw string */
+ && !indent_match(default_qstr(glo_quote_str, 1),
+ lback(curwp->w_dotp),ind_str, NLINE, 0)
+ && !ISspace(lgetc(curwp->w_dotp, qlen).c))
curwp->w_dotp = lback(curwp->w_dotp);
+ /*
+ * Ok, we made it here and we assume that we are at the begining
+ * of the paragraph. Let's double check this now. In order to do
+ * so we shell check if the first line was indented in a special
+ * way.
+ */
+ if(lback(curwp->w_dotp) == curbp->b_linep)
+ break;
+ else{
+ int i, j;
+
+ /*
+ * First we test if the preceding line is indented.
+ * for the following test we need to have the raw values,
+ * not the processed values
+ */
+ quote_match(default_qstr(glo_quote_str, 1), curwp->w_dotp, qstr, NLINE, 1);
+ quote_match(default_qstr(glo_quote_str, 1), lback(curwp->w_dotp), qstr2, NLINE, 1);
+ for (i = 0, j = 0;
+ qstr[i] && qstr2[i] && (qstr[i] == qstr2[i]); i++, j++);
+ for (; ISspace(qstr2[i]); i++);
+ for (; ISspace(qstr[j]); j++);
+ if ((indent_match(default_qstr(glo_quote_str, 1), lback(curwp->w_dotp),
+ ind_str, NLINE, 1)
+ && (strlenis(qstr2)
+ + strlenis(ind_str) >= strlenis(qstr)))
+ || (lback(curwp->w_dotp) != curbp->b_linep
+ && llength(lback(curwp->w_dotp)) > qlen
+ && (quoted == quote_match(default_qstr(glo_quote_str, 1),
+ lback(curwp->w_dotp), pqstr, NLINE, 0))
+ && !strcmp(qstr, pqstr)
+ && !ISspace(lgetc(curwp->w_dotp, qlen).c)
+ && (strlenis(qstr2) > strlenis(qstr)))
+ && !qstr2[i] && !qstr[j])
+ curwp->w_dotp = lback(curwp->w_dotp);
+ }
+
if(n){
/* keep looking */
if(lback(curwp->w_dotp) == curbp->b_linep)
@@ -330,7 +377,7 @@ gotobop(int f, int n)
else{
/* leave cursor on first word in para */
curwp->w_doto = 0;
- while(ucs4_isspace(lgetc(curwp->w_dotp, curwp->w_doto).c))
+ while(ISspace(lgetc(curwp->w_dotp, curwp->w_doto).c))
if(++curwp->w_doto >= llength(curwp->w_dotp)){
curwp->w_doto = 0;
curwp->w_dotp = lforw(curwp->w_dotp);
@@ -344,6 +391,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;
+ }
+ return '\0';
+}
/*
* go forword to the end of the current paragraph
@@ -353,8 +583,9 @@ gotobop(int f, int n)
int
gotoeop(int f, int n)
{
- int quoted, qlen;
- UCS qstr[NLINE], qstr2[NLINE];
+ int quoted, qlen, indented, changeqstr = 0;
+ int i,j, fli = 0; /* fli = first line indented a boolean variable */
+ char qstr[NLINE], qstr2[NLINE], ind_str[NLINE];
if (n < 0) /* the other way...*/
return(gotobop(f, -n));
@@ -367,27 +598,70 @@ gotoeop(int f, int n)
break;
}
+ /*
+ * We need to figure out if this line is the first line of
+ * a paragraph that has been indented in a special way. If this
+ * is the case, we advance one more line before we use the
+ * algorithm below
+ */
+
+ if(curwp->w_dotp != curbp->b_linep){
+ quote_match(default_qstr(glo_quote_str, 1), curwp->w_dotp, qstr, NLINE, 1);
+ quote_match(default_qstr(glo_quote_str, 1), lforw(curwp->w_dotp), qstr2, NLINE, 1);
+ indented = indent_match(default_qstr(glo_quote_str, 1), curwp->w_dotp, ind_str,
+ NLINE, 1);
+ if (strlenis(qstr)
+ + strlenis(ind_str) < strlenis(qstr2)){
+ curwp->w_doto = llength(curwp->w_dotp);
+ if(n){ /* this line is a paragraph by itself */
+ curwp->w_dotp = lforw(curwp->w_dotp);
+ continue;
+ }
+ break;
+ }
+ for (i=0,j=0; qstr[i] && qstr2[i] && (qstr[i] == qstr2[i]);i++,j++);
+ for (; ISspace(qstr[i]); i++);
+ for (; ISspace(qstr2[j]); j++);
+ if (!qstr[i] && !qstr2[j] && indented){
+ fli++;
+ if (indent_match(default_qstr(glo_quote_str, 1), lforw(curwp->w_dotp),
+ ind_str, NLINE, 0)){
+ if (n){ /* look for another paragraph ? */
+ curwp->w_dotp = lforw(curwp->w_dotp);
+ continue;
+ }
+ }
+ else{
+ if (!lisblank(lforw(curwp->w_dotp)))
+ curwp->w_dotp = lforw(curwp->w_dotp);
+ }
+ }
+ }
+
/* scan line by line until we come to a line ending with
* a <NL><NL> or <NL><TAB> or <NL><SPACE>
*
* PLUS: if there's a quote string, a quoted-to-non-quoted
* line transition.
*/
- quoted = glo_quote_str
- ? quote_match(glo_quote_str,
- curwp->w_dotp, qstr, NLINE) : 0;
- qlen = quoted ? ucs4_strlen(qstr) : 0;
+ /* if the first line is indented (fli == 1), then the test below
+ is on the second line, and in that case we will need the raw
+ string, not the processed string
+ */
+ quoted = quote_match(default_qstr(glo_quote_str, 1), curwp->w_dotp, qstr, NLINE, fli);
+ qlen = quoted ? strlen(qstr) : 0;
while(curwp->w_dotp != curbp->b_linep
&& llength(lforw(curwp->w_dotp)) > qlen
- && (glo_quote_str
- ? (quoted == quote_match(glo_quote_str,
- lforw(curwp->w_dotp),
- qstr2, NLINE)
- && !ucs4_strcmp(qstr, qstr2))
- : 1)
- && lgetc(lforw(curwp->w_dotp), qlen).c != TAB
- && lgetc(lforw(curwp->w_dotp), qlen).c != ' ')
+ && (quoted == quote_match(default_qstr(glo_quote_str, 1),
+ lforw(curwp->w_dotp), qstr2, NLINE, fli))
+ && !strcmp(qstr, qstr2)
+ && (quoted == quote_match(default_qstr(glo_quote_str, 1),
+ lforw(curwp->w_dotp), qstr2, NLINE, 1))
+ && !strcmp(qstr, qstr2)
+ && !indent_match(default_qstr(glo_quote_str, 1),
+ lforw(curwp->w_dotp), ind_str, NLINE, 0)
+ && !ISspace(lgetc(lforw(curwp->w_dotp), qlen).c))
curwp->w_dotp = lforw(curwp->w_dotp);
curwp->w_doto = llength(curwp->w_dotp);
@@ -684,7 +958,57 @@ scrolldownline(int f, int n)
return (scrollforw (1, FALSE));
}
+/* deltext deletes from the specified position until the end of the file
+ * or until the signature (when called from Pine), whichever comes first.
+ */
+int
+deltext (f,n)
+int f,n;
+{
+ LINE *currline = curwp->w_dotp;
+ static int firsttime = 0;
+
+ if ((lastflag&CFKILL) == 0)
+ kdelete();
+
+ curwp->w_markp = curwp->w_dotp;
+ curwp->w_marko = curwp->w_doto;
+
+ while (curwp->w_dotp != curbp->b_linep){
+ if ((Pmaster)
+ && (llength(curwp->w_dotp) == 3)
+ && (lgetc(curwp->w_dotp, 0).c == '-')
+ && (lgetc(curwp->w_dotp, 1).c == '-')
+ && (lgetc(curwp->w_dotp, 2).c == ' ')){
+ if (curwp->w_dotp == currline){
+ if (curwp->w_doto)
+ curwp->w_dotp = lforw(curwp->w_dotp);
+ else
+ break;
+ }
+ else{
+ curwp->w_dotp = lback(curwp->w_dotp);
+ curwp->w_doto = llength(curwp->w_dotp);
+ break;
+ }
+ }
+ else{
+ if(lforw(curwp->w_dotp) != curbp->b_linep)
+ curwp->w_dotp = lforw(curwp->w_dotp);
+ else{
+ curwp->w_doto = llength(curwp->w_dotp);
+ break;
+ }
+ }
+ }
+ killregion(FALSE,1);
+ lastflag |= CFKILL;
+ if(firsttime == 0)
+ emlwrite("Deleted text can be recovered with the ^U command", NULL);
+ firsttime = 1;
+ return TRUE;
+}
/*
* Scroll to a position.
diff --git a/pico/blddate.c b/pico/blddate.c
index 00f0b558..5797c3c5 100644
--- a/pico/blddate.c
+++ b/pico/blddate.c
@@ -19,7 +19,7 @@ main(argc, argv)
char **argv;
{
struct tm *t;
- FILE *outfile=stdout;
+ FILE *outfile=stdout, *infile;
time_t ltime;
if(argc > 1 && (outfile = fopen(argv[1], "w")) == NULL){
@@ -46,6 +46,12 @@ main(argc, argv)
1900 + t->tm_year);
fprintf(outfile, "char hoststamp[]=\"random-pc\";\n");
+ if((infile = fopen("../patchlevel", "r")) != NULL){
+ int c;
+ while ((c = getc(infile)) != EOF) putc(c, outfile);
+ fclose(infile);
+ }
+ else fprintf(outfile, "char plevstamp[]=\"No information available\";\n");
fclose(outfile);
diff --git a/pico/composer.c b/pico/composer.c
index c801d385..e709cd67 100644
--- a/pico/composer.c
+++ b/pico/composer.c
@@ -1872,6 +1872,8 @@ AppendAttachment(char *fn, char *sz, char *cmt)
}
UpdateHeader(0);
+ if(sendnow)
+ return(status !=0);
PaintHeader(COMPOSER_TOP_LINE, status != 0);
PaintBody(1);
return(status != 0);
@@ -2015,7 +2017,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 +2097,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--;
@@ -3362,6 +3371,9 @@ display_delimiter(int state)
{
UCS *bufp, *buf;
+ if (sendnow)
+ return;
+
if(ComposerTopLine - 1 >= BOTTOM()) /* silently forget it */
return;
@@ -3418,6 +3430,9 @@ InvertPrompt(int entry, int state)
UCS *end;
int i;
+ if (sendnow)
+ return(TRUE);
+
buf = utf8_to_ucs4_cpystr(headents[entry].prompt); /* fresh prompt paint */
if(!buf)
return(-1);
@@ -4373,6 +4388,9 @@ is_blank(int row, int col, int n)
void
ShowPrompt(void)
{
+ if (sendnow)
+ return;
+
if(headents[ods.cur_e].key_label){
menu_header[TO_KEY].name = "^T";
menu_header[TO_KEY].label = headents[ods.cur_e].key_label;
diff --git a/pico/display.c b/pico/display.c
index 2741be3e..6923e156 100644
--- a/pico/display.c
+++ b/pico/display.c
@@ -387,6 +387,9 @@ update(void)
int scroll = 0;
CELL c;
+ if (sendnow)
+ return;
+
#if TYPEAH
if (typahead())
return;
@@ -916,7 +919,7 @@ updateline(int row, /* row on screen */
int nbflag; /* non-blanks to the right flag? */
int cleartoeol = 0;
- if(row < 0 || row > term.t_nrow)
+ if(row < 0 || row > term.t_nrow || sendnow)
return;
/* set up pointers to virtual and physical lines */
@@ -1285,7 +1288,7 @@ get_cursor(int *row, int *col)
void
mlerase(void)
{
- if (term.t_nrow < term.t_mrow)
+ if (term.t_nrow < term.t_mrow || sendnow)
return;
movecursor(term.t_nrow - term.t_mrow, 0);
@@ -1360,6 +1363,10 @@ mlyesno(UCS *prompt, int dflt)
menu_yesno[6].label = N_("Cancel");
menu_yesno[7].name = "N";
menu_yesno[7].label = (dflt == FALSE) ? "[" N_("No") "]" : N_("No");
+ if(Pmaster && Pmaster->onctrlc){
+ menu_yesno[8].name = "T";
+ menu_yesno[8].label = N_("counT");
+ }
wkeyhelp(menu_yesno); /* paint generic menu */
sgarbk = TRUE; /* mark menu dirty */
if(Pmaster && curwp)
@@ -1438,6 +1445,14 @@ mlyesno(UCS *prompt, int dflt)
km_popped++;
break;
}
+
+ case 'T':
+ case 't':
+ if(Pmaster && Pmaster->onctrlc){
+ pputs_utf8(_("counT"), 1);
+ rv = COUNT;
+ break;
+ }
/* else fall through */
default:
@@ -1751,6 +1766,11 @@ mlreplyd(UCS *prompt, UCS *buf, int nbuf, int flg, EXTRAKEYS *extras)
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')
@@ -1760,6 +1780,18 @@ mlreplyd(UCS *prompt, UCS *buf, int nbuf, int flg, EXTRAKEYS *extras)
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);
@@ -1869,7 +1901,7 @@ mlreplyd(UCS *prompt, UCS *buf, int nbuf, int flg, EXTRAKEYS *extras)
#endif
default :
-
+text:
/* look for match in extra_v */
for(i = 0; i < 12; i++)
if(c && c == extra_v[i]){
@@ -1963,7 +1995,7 @@ emlwrite_ucs4(UCS *message, EML *eml)
mlerase();
- if(!(message && *message) || term.t_nrow < 2)
+ if(!(message && *message) || term.t_nrow < 2 || sendnow)
return; /* nothing to write or no space to write, bag it */
bufp = message;
@@ -2152,8 +2184,9 @@ mlwrite(UCS *fmt, void *arg)
}
ret = ttcol;
- while(ttcol < term.t_ncol)
- pputc(' ', 0);
+ if(sendnow == 0)
+ while(ttcol < term.t_ncol)
+ pputc(' ', 0);
movecursor(term.t_nrow - term.t_mrow, ret);
@@ -2632,6 +2665,8 @@ pputc(UCS c, /* char to write */
{
int ind, width, printable_ascii = 0;
+ if(sendnow)
+ return;
/*
* This is necessary but not sufficient to allow us to draw. Note that
* ttrow runs from 0 to t_nrow (so total number of rows is t_nrow+1)
@@ -2686,6 +2721,8 @@ void
pputs(UCS *s, /* string to write */
int a) /* and its attribute */
{
+ if(sendnow)
+ return;
while (*s != '\0')
pputc(*s++, a);
}
@@ -2696,6 +2733,8 @@ pputs_utf8(char *s, int a)
{
UCS *ucsstr = NULL;
+ if(sendnow)
+ return;
if(s && *s){
ucsstr = utf8_to_ucs4_cpystr(s);
if(ucsstr){
@@ -2996,6 +3035,9 @@ wkeyhelp(KEYMENU *keymenu)
char nbuf[NLINE];
#endif
+ if(sendnow)
+ return;
+
#ifdef _WINDOWS
pico_config_menu_items (keymenu);
#endif
diff --git a/pico/ebind.h b/pico/ebind.h
index 4f1687b4..f8cbbbc3 100644
--- a/pico/ebind.h
+++ b/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},
diff --git a/pico/edef.h b/pico/edef.h
index 209a4600..bea0ed1e 100644
--- a/pico/edef.h
+++ b/pico/edef.h
@@ -32,6 +32,7 @@
/* initialized global definitions */
+int sendnow = 0; /* should we send now */
int fillcol = 72; /* Current fill column */
int userfillcol = -1; /* Fillcol set from cmd line */
UCS pat[NPAT]; /* Search pattern */
@@ -84,6 +85,7 @@ void *input_cs; /* passed to mbtow() via kbseq() */
/* initialized global external declarations */
+extern int sendnow; /* should we send now */
extern int fillcol; /* Fill column */
extern int userfillcol; /* Fillcol set from cmd line */
extern UCS pat[]; /* Search pattern */
diff --git a/pico/efunc.h b/pico/efunc.h
index b551dfc7..52b2bcc5 100644
--- a/pico/efunc.h
+++ b/pico/efunc.h
@@ -54,8 +54,12 @@ 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 deltext (int, int);
extern int scrollupline(int, int);
extern int scrolldownline(int, int);
extern int scrollto(int, int);
@@ -249,10 +253,16 @@ extern int forwword(int, int);
extern int fillpara(int, int);
extern int fillbuf(int, int);
extern int inword(void);
-extern int quote_match(UCS *, LINE *, UCS *, size_t);
+extern int quote_match(char **, LINE *, char *, size_t, int);
+extern void flatten_qstring(QSTRING_S *, char *, int);
+extern void free_qs(QSTRING_S **);
+extern QSTRING_S *do_quote_match (char **, char *, char *, char *, char *, int, int);
+extern QSTRING_S *do_raw_quote_match(char **, char *, char *, char *, QSTRING_S **, QSTRING_S **);
+extern int indent_match(char **, LINE *, char *, int, int);
extern int ucs4_isalnum(UCS);
extern int ucs4_isalpha(UCS);
extern int ucs4_isspace(UCS);
extern int ucs4_ispunct(UCS);
#endif /* EFUNC_H */
+
diff --git a/pico/fileio.c b/pico/fileio.c
index 5cf124c0..2b598e0c 100644
--- a/pico/fileio.c
+++ b/pico/fileio.c
@@ -95,6 +95,7 @@ ffgetline(UCS buf[], size_t nbuf, size_t *charsreturned, int msg)
{
size_t i;
UCS ucs;
+ static int shown = 0;
if(charsreturned)
*charsreturned = 0;
@@ -121,8 +122,10 @@ ffgetline(UCS buf[], size_t nbuf, size_t *charsreturned, int msg)
if(charsreturned)
*charsreturned = nbuf - 1;
- if(msg)
+ if(msg && !shown){
+ shown = 1;
emlwrite("File has long line", NULL);
+ }
return FIOLNG;
}
@@ -131,6 +134,7 @@ ffgetline(UCS buf[], size_t nbuf, size_t *charsreturned, int msg)
}
if(ucs == CCONV_EOF){
+ shown = 0; /* warn the next time, again, only once */
if(ferror(g_pico_fio.fp)){
emlwrite("File read error", NULL);
if(charsreturned)
diff --git a/pico/line.c b/pico/line.c
index e5df3670..fe6847d3 100644
--- a/pico/line.c
+++ b/pico/line.c
@@ -608,14 +608,12 @@ int
lisblank(LINE *line)
{
int n = 0;
- UCS qstr[NLINE];
+ char qstr[NLINE];
- n = (glo_quote_str
- && quote_match(glo_quote_str, line, qstr, NLINE))
- ? ucs4_strlen(qstr) : 0;
+ n = quote_match(default_qstr(glo_quote_str, 1), line, qstr, NLINE, 1);
for(; n < llength(line); n++)
- if(!ucs4_isspace(lgetc(line, n).c))
+ if(!ISspace(lgetc(line, n).c))
return(FALSE);
return(TRUE);
diff --git a/pico/main.c b/pico/main.c
index ac5bc388..3036b69a 100644
--- a/pico/main.c
+++ b/pico/main.c
@@ -163,6 +163,7 @@ main(int argc, char *argv[])
char *file_to_edit = NULL;
char *display_charmap = NULL, *dc;
char *keyboard_charmap = NULL;
+ int line_information_on = FALSE;
int use_system = 0;
char *err = NULL;
@@ -416,6 +417,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:
@@ -437,14 +444,29 @@ main(int argc, char *argv[])
mlerase();
}
- f = FALSE;
+ f = (c == (CTRL|'J'));
n = 1;
+ if (!line_information_on)
+ line_information_on = (c == (CTRL|'C'));
+ else
+ line_information_on = ((c == KEY_DOWN) || (c == KEY_UP) ||
+ (c == KEY_RIGHT) || (c == KEY_LEFT) ||
+ (c == (CTRL|'V')) || (c == (CTRL|'Y')) ||
+ (c == (CTRL|'D')) || (c == (CTRL|'F')) ||
+ (c == (CTRL|'B')) || (c == (CTRL|'N')) ||
+ (c == (CTRL|'P')) || (c == (CTRL|'A')) ||
+ (c == (CTRL|'E')) || (c == (CTRL|'U')))
+ && (c != (CTRL|'C'));
#ifdef MOUSE
clear_mfunc(mouse_in_content);
#endif
/* Do it. */
execute(normalize_cmd(c, fkm, 1), f, n);
+ if (line_information_on){
+ c = (CTRL|'C');
+ execute(normalize_cmd(c, fkm, 1), f, n);
+ }
}
}
diff --git a/pico/osdep/color.h b/pico/osdep/color.h
index 0dced80c..dce05118 100644
--- a/pico/osdep/color.h
+++ b/pico/osdep/color.h
@@ -33,6 +33,10 @@ void pico_endcolor(void);
void pico_toggle_color(int);
void pico_set_nfg_color(void);
void pico_set_nbg_color(void);
+char **default_qstr(void *, int);
+void add_allowed_qstr(void *, int);
+void free_allowed_qstr(void);
+void record_quote_string (QSTRING_S *);
#endif /* PICO_OSDEP_COLOR_INCLUDED */
diff --git a/pico/osdep/getkey.c b/pico/osdep/getkey.c
index b55dd092..827b7da0 100644
--- a/pico/osdep/getkey.c
+++ b/pico/osdep/getkey.c
@@ -131,6 +131,16 @@ GetKey(void)
{
UCS ch, status, cc;
+ if(sendnow){
+ ch = Pmaster && Pmaster->auto_cmds && *Pmaster->auto_cmds
+ ? *Pmaster->auto_cmds++ : NODATA;
+
+ if (ch >= 0x00 && ch <= 0x1F)
+ ch = CTRL | (ch+'@');
+
+ return(ch);
+ }
+
if(!ReadyForKey(FUDGE-5))
return(NODATA);
diff --git a/pico/osdep/terminal.c b/pico/osdep/terminal.c
index 72206c01..eb24d90e 100644
--- a/pico/osdep/terminal.c
+++ b/pico/osdep/terminal.c
@@ -26,6 +26,7 @@ static char rcsid[] = "$Id: terminal.c 921 2008-01-31 02:09:25Z hubert@u.washing
#include "../keydefs.h"
#include "../pico.h"
#include "../mode.h"
+#include "../edef.h"
#include "raw.h"
#include "color.h"
@@ -478,6 +479,12 @@ tinfoopen(void)
{
int row, col;
+ if (sendnow){
+ term.t_nrow = 23;
+ term.t_ncol = 80;
+ return 0;
+ }
+
/*
* determine the terminal's communication speed and decide
* if we need to do optimization ...
@@ -1253,6 +1260,12 @@ tcapopen(void)
{
int row, col;
+ if (sendnow){
+ term.t_nrow = 23;
+ term.t_ncol = 80;
+ return 0;
+ }
+
/*
* determine the terminal's communication speed and decide
* if we need to do optimization ...
diff --git a/pico/pico.c b/pico/pico.c
index 166a3d3d..0a47d7f9 100644
--- a/pico/pico.c
+++ b/pico/pico.c
@@ -138,6 +138,15 @@ pico(PICO *pm)
pico_all_done = 0;
km_popped = 0;
+ if (pm->auto_cmds){
+ int i;
+#define CTRL_X 24
+ for (i = 0; pm->auto_cmds[i]; i++);
+ if ((i > 1) && (pm->auto_cmds[i - 2] == CTRL_X) &&
+ ((pm->auto_cmds[i - 1] == 'y') || (pm->auto_cmds[i-1] == 'Y')))
+ sendnow++;
+ }
+
if(!vtinit()) /* Init Displays. */
return(COMP_CANCEL);
@@ -638,12 +647,19 @@ abort_composer(int f, int n)
result = "";
Pmaster->arm_winch_cleanup++;
+ Pmaster->onctrlc++;
if(Pmaster->canceltest){
if(((Pmaster->pine_flags & MDHDRONLY) && !any_header_changes())
|| (result = (*Pmaster->canceltest)(redraw_pico_for_callback))){
- pico_all_done = COMP_CANCEL;
emlwrite(result, NULL);
Pmaster->arm_winch_cleanup--;
+ if(Pmaster->curpos[0]){
+ curwp->w_flag |= WFMODE; /* and modeline so we */
+ sgarbk = TRUE; /* redraw the keymenu */
+ pclear(term.t_nrow - 1, term.t_nrow + 1);
+ return(FALSE);
+ }
+ pico_all_done = COMP_CANCEL;
return(TRUE);
}
else{
@@ -672,6 +688,12 @@ abort_composer(int f, int n)
emlwrite(_("\007Cancel Cancelled"), NULL);
break;
+ case COUNT:
+ showcpos(1,0);
+ emlwrite(Pmaster->curpos, NULL);
+ Pmaster->onctrlc--;
+ break;
+
default:
mlerase();
}
@@ -714,6 +736,19 @@ wquit(int f, int n)
return(FALSE);
}
+ /* When we send a message using the command line we are going to
+ ignore if the user wants to spell check, we assume he already
+ did */
+ if (sendnow){
+ ret = (*Pmaster->exittest)(Pmaster->headents,
+ redraw_pico_for_callback,
+ Pmaster->allow_flowed_text,
+ &result);
+ if (!ret)
+ pico_all_done = COMP_EXIT;
+ return(result ? FALSE : TRUE);
+ }
+
#ifdef SPELLER
if(Pmaster->always_spell_check)
if(spell(0, 0) == -1)
diff --git a/pico/pico.h b/pico/pico.h
index cf25abea..51bc26ff 100644
--- a/pico/pico.h
+++ b/pico/pico.h
@@ -199,11 +199,13 @@ typedef struct pico_struct {
PCOLORS *colors; /* colors for titlebar and keymenu */
void *input_cs; /* passed to mbtow() via kbseq() */
long pine_flags; /* entry mode flags */
+ char curpos[80]; /* where are we now? */
/* The next few bits are features that don't fit in pine_flags */
/* If we had this to do over, it would probably be one giant bitmap */
unsigned always_spell_check:1; /* always spell-checking upon quit */
unsigned strip_ws_before_send:1; /* don't default strip bc of flowed */
unsigned allow_flowed_text:1; /* clean text when done to keep flowed */
+ unsigned onctrlc; /* are we on ctrl-c command? */
int (*helper)(); /* Pine's help function */
int (*showmsg)(); /* Pine's display_message */
UCS (*suspend)(); /* Pine's suspend */
@@ -222,6 +224,7 @@ typedef struct pico_struct {
void (*winch_cleanup)(); /* callback handling screen resize */
void (*newthread)(); /* callback to create new thread */
int arm_winch_cleanup; /* do the winch_cleanup if resized */
+ int *auto_cmds; /* Initial keystroke commands */
HELP_T search_help;
HELP_T ins_help;
HELP_T ins_m_help;
diff --git a/pico/random.c b/pico/random.c
index 780c083e..4b52bcac 100644
--- a/pico/random.c
+++ b/pico/random.c
@@ -74,7 +74,10 @@ showcpos(int f, int n)
thisline+1, lines+1, (int)((100L*(thisline+1))/(lines+1)),
nbc, nch, (nch) ? (int)((100L*nbc)/nch) : 0);
- emlwrite(buffer, NULL);
+ if(Pmaster)
+ strcpy(Pmaster->curpos, buffer);
+ else
+ emlwrite(buffer, NULL);
return (TRUE);
}
diff --git a/pico/search.c b/pico/search.c
index 0886b24c..70829082 100644
--- a/pico/search.c
+++ b/pico/search.c
@@ -36,7 +36,7 @@ int srpat(char *, UCS *, size_t, int);
int readpattern(char *, int);
int replace_pat(UCS *, int *);
int replace_all(UCS *, UCS *);
-
+int deletepara(int, int);
#define FWS_RETURN(RV) { \
thisflag |= CFSRCH; \
@@ -76,6 +76,10 @@ N_(" brackets. This string is the default search prompt."),
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."),
" ",
@@ -232,10 +236,19 @@ forwsearch(int f, int n)
mlerase();
FWS_RETURN(TRUE);
+ case (CTRL|'P'):
+ deletepara(0, 1);
+ mlerase();
+ FWS_RETURN(TRUE);
+
case (CTRL|'R'): /* toggle replacement option */
repl_mode = !repl_mode;
break;
+ case (CTRL|'X'):
+ deltext(f,n);
+ FWS_RETURN(TRUE);
+
default:
if(status == ABORT)
emlwrite(_("Search Cancelled"), NULL);
@@ -274,7 +287,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++;
}
@@ -600,7 +613,7 @@ srpat(char *utf8prompt, UCS *defpat, size_t defpatlen, int repl_mode)
UCS *b;
UCS prompt[NPMT];
UCS *promptp;
- EXTRAKEYS menu_pat[8];
+ EXTRAKEYS menu_pat[10];
menu_pat[i = 0].name = "^Y";
menu_pat[i].label = N_("FirstLine");
@@ -618,6 +631,11 @@ srpat(char *utf8prompt, UCS *defpat, size_t defpatlen, int repl_mode)
KS_OSDATASET(&menu_pat[i], KS_NONE);
if(!repl_mode){
+ menu_pat[++i].name = "^X";
+ menu_pat[i].label = N_("DelEnd");
+ menu_pat[i].key = (CTRL|'X');
+ KS_OSDATASET(&menu_pat[i], KS_NONE);
+
menu_pat[++i].name = "^T";
menu_pat[i].label = N_("LineNumber");
menu_pat[i].key = (CTRL|'T');
@@ -634,6 +652,11 @@ srpat(char *utf8prompt, UCS *defpat, size_t defpatlen, int repl_mode)
menu_pat[i].key = (CTRL|'O');
KS_OSDATASET(&menu_pat[i], KS_NONE);
+ menu_pat[++i].name = "^P";
+ menu_pat[i].label = N_("Delete Para");
+ menu_pat[i].key = (CTRL|'P');
+ KS_OSDATASET(&menu_pat[i], KS_NONE);
+
menu_pat[++i].name = "^U";
/* TRANSLATORS: Instead of justifying (formatting) just a
single paragraph, Full Justify justifies the entire
@@ -769,7 +792,7 @@ readpattern(char *utf8prompt, int text_mode)
UCS *b;
UCS tpat[NPAT+20];
UCS *tpatp;
- EXTRAKEYS menu_pat[7];
+ EXTRAKEYS menu_pat[9];
menu_pat[i = 0].name = "^Y";
menu_pat[i].label = N_("FirstLine");
@@ -782,6 +805,11 @@ readpattern(char *utf8prompt, int text_mode)
KS_OSDATASET(&menu_pat[i], KS_NONE);
if(text_mode){
+ menu_pat[++i].name = "^X";
+ menu_pat[i].label = N_("DelEnd");
+ menu_pat[i].key = (CTRL|'X');
+ KS_OSDATASET(&menu_pat[i], KS_NONE);
+
menu_pat[++i].name = "^T";
menu_pat[i].label = N_("LineNumber");
menu_pat[i].key = (CTRL|'T');
@@ -797,6 +825,11 @@ readpattern(char *utf8prompt, int text_mode)
menu_pat[i].key = (CTRL|'O');
KS_OSDATASET(&menu_pat[i], KS_NONE);
+ menu_pat[++i].name = "^P";
+ menu_pat[i].label = N_("Delete Para");
+ menu_pat[i].key = (CTRL|'P');
+ KS_OSDATASET(&menu_pat[i], KS_NONE);
+
menu_pat[++i].name = "^U";
menu_pat[i].label = N_("FullJustify");
menu_pat[i].key = (CTRL|'U');
@@ -927,7 +960,7 @@ forscan(int *wrapt, /* boolean indicating search wrapped */
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;
@@ -948,7 +981,7 @@ forscan(int *wrapt, /* boolean indicating search wrapped */
return(FALSE);
/* and test it against the pattern */
- if (eq(*patptr, c) == FALSE)
+ if (eq((unsigned char) *patptr, c) == FALSE)
goto fail;
}
@@ -1035,3 +1068,25 @@ chword(UCS *wb, UCS *cb)
curwp->w_flag |= WFEDIT;
}
+
+int
+deletepara(int f, int n) /* Delete the current paragraph */
+{
+ if(curbp->b_mode&MDVIEW) /* don't allow this command if */
+ return(rdonly()); /* we are in read only mode */
+
+ if(!lisblank(curwp->w_dotp))
+ gotobop(FALSE, 1);
+
+ curwp->w_markp = curwp->w_dotp;
+ curwp->w_marko = 0;
+
+ gotoeop(FALSE, 1);
+ if (curwp->w_dotp != curbp->b_linep){ /* if we are not at the end of buffer */
+ curwp->w_dotp = lforw(curwp->w_dotp); /* get one more line */
+ curwp->w_doto = 0; /* but only the beginning */
+ }
+ killregion(f,n);
+ return(TRUE);
+}
+
diff --git a/pico/word.c b/pico/word.c
index 145fc1d1..62fa84ea 100644
--- a/pico/word.c
+++ b/pico/word.c
@@ -25,10 +25,10 @@ static char rcsid[] = "$Id: word.c 769 2007-10-24 00:15:40Z hubert@u.washington.
*/
#include "headers.h"
-
+#include "../pith/osdep/color.h"
int fpnewline(UCS *quote);
-int fillregion(UCS *qstr, REGION *addedregion);
+int fillregion(UCS *qstr, UCS *istr, REGION *addedregion);
int setquotelevelinregion(int quotelevel, REGION *addedregion);
int is_user_separator(UCS c);
@@ -431,42 +431,156 @@ is_user_separator(UCS c)
return 0;
}
+/* Support of indentation of paragraphs */
+#define is_indent_char(c) (((c) == '.' || (c) == '}' || (c) == RPAREN || \
+ (c) == '*' || (c) == '+' || is_a_digit(c) || \
+ ISspace(c) || (c) == '-' || \
+ (c) == ']') ? 1 : 0)
+#define allowed_after_digit(c,word,k) ((((c) == '.' && \
+ allowed_after_period(next((word),(k)))) ||\
+ (c) == RPAREN || (c) == '}' || (c) == ']' ||\
+ ISspace(c) || is_a_digit(c) || \
+ ((c) == '-' ) && \
+ allowed_after_dash(next((word),(k)))) \
+ ? 1 : 0)
+#define allowed_after_period(c) (((c) == RPAREN || (c) == '}' || (c) == ']' ||\
+ ISspace(c) || (c) == '-' || \
+ is_a_digit(c)) ? 1 : 0)
+#define allowed_after_parenth(c) (ISspace(c) ? 1 : 0)
+#define allowed_after_space(c) (ISspace(c) ? 1 : 0)
+#define allowed_after_braces(c) (ISspace(c) ? 1 : 0)
+#define allowed_after_star(c) ((ISspace(c) || (c) == RPAREN ||\
+ (c) == ']' || (c) == '}') ? 1 : 0)
+#define allowed_after_dash(c) ((ISspace(c) || is_a_digit(c)) ? 1 : 0)
+#define EOLchar(c) (((c) == '.' || (c) == ':' || (c) == '?' ||\
+ (c) == '!') ? 1 : 0)
+
+int indent_match(char **, LINE *, char *, int, int);
+
+/* Extended justification support */
+#define is_cquote(c) ((c) == '>' || (c) == '|' || (c) == ']' || (c) == ':')
+#define is_cword(c) ((((c) >= 'a') && ((c) <= 'z')) || \
+ (((c) >= 'A') && ((c) <= 'Z')) || \
+ (((c) >= '0') && ((c) <= '9')) || \
+ ((c) == ' ') || ((c) == '?') || \
+ ((c) == '@') || ((c) == '.') || \
+ ((c) == '!') || ((c) == '\'') || \
+ ((c) == ',') || ((c) == '\"') ? 1 : 0)
+#define isaquote(c) ((c) == '\"' || (c) == '\'')
+#define is8bit(c) ((((int) (c)) & 0x80) ? 1 : 0)
+#define iscontrol(c) (iscntrl(((int) (c)) & 0x7f) ? 1 : 0)
+#define forbidden(c) (((c) == '\"') || ((c) == '\'') || ((c) == '$') ||\
+ ((c) == ',') || ((c) == '.') || ((c) == '-') ||\
+ ((c) == LPAREN) || ((c) == '/')|| ((c) == '`') ||\
+ ((c) == '{') || ((c) == '\\') || (iscontrol((c))) ||\
+ (((c) >= '0') && ((c) <= '9')) || ((c) == '?'))
+#define is_cletter(c) ((((c) >= 'a') && ((c) <= 'z'))) ||\
+ ((((c) >= 'A') && ((c) <= 'Z'))||\
+ is8bit(c))
+#define is_cnumber(c) ((c) >= '0' && (c) <= '9')
+#define allwd_after_word(c) (((c) == ' ') || ((c) == '>') || is_cletter(c))
+#define allwd_after_qsword(c) (((c) != '\\') && ((c) != RPAREN))
+#define before(word,i) (((i) > 0) ? (word)[(i) - 1] : 0)
+#define next(w,i) ((((w)[(i)]) != 0) ? ((w)[(i) + 1]) : 0)
+#define now(w,i) ((w)[(i)])
+#define is_qsword(c) (((c) == ':') || ((c) == RPAREN) ? 1 : 0)
+#define is_colon(c) (((c) == ':') ? 1 : 0)
+#define is_rarrow(c) (((c) == '>') ? 1 : 0)
+#define is_tilde(c) (((c) == '~') ? 1 : 0)
+#define is_dash(c) (((c) == '-') ? 1 : 0)
+#define is_pound(c) (((c) == '#') ? 1 : 0)
+#define is_a_digit(c) ((((c) >= '0') && ((c) <= '9')) ? 1 : 0)
+#define is_allowed(c) (is_cquote(c) || is_cword(c) || is_dash(c) || \
+ is_pound(c))
+#define qs_allowed(a) (((a)->qstype != qsGdb) && ((a)->qstype != qsProg))
+
+/* Internal justification functions */
+QSTRING_S *qs_quote_match(char **, LINE *, char *, int);
+int ucs4_strlenis(UCS *);
+void linencpy(char *, LINE *, int);
+
+void
+linencpy(word, l, buflen)
+ char word[NSTRING];
+ LINE *l;
+ int buflen;
+{
+ int i;
+ UCS ucs_word[NSTRING];
+ char *utf_word;
+
+ word[0] = '\0';
+ if(l){
+ for (i = 0; i < buflen && i < llength(l)
+ && (ucs_word[i] = lgetc(l,i).c); i++);
+ ucs_word[i == buflen ? i-1 : i] = '\0';
+ utf_word = ucs4_to_utf8_cpystr(ucs_word);
+ strncpy(word, utf_word, (NSTRING < buflen ? NSTRING : buflen));
+ word[NSTRING-1] = '\0';
+ if(utf_word) fs_give((void **)&utf_word);
+ }
+}
+
+ /*
+ * This function returns the quote string as a structure. In this way we
+ * have two ways to get the quote string: as a char * or as a QSTRING_S *
+ * directly.
+ */
+QSTRING_S *
+qs_quote_match(char **q, LINE *l, char *rqstr, int rqstrlen)
+{
+ char GLine[NSTRING], NLine[NSTRING], PLine[NSTRING];
+ LINE *nl = l != curbp->b_linep ? lforw(l) : NULL;
+ LINE *pl = lback(l) != curbp->b_linep ? lback(l) : NULL;
+ int plb = 1;
+
+ linencpy(GLine, l, NSTRING);
+ linencpy(NLine, nl, NSTRING);
+
+ if (pl){
+ linencpy(PLine, pl, NSTRING);
+ if(lback(pl) != curbp->b_linep){
+ char PPLine[NSTRING];
+
+ linencpy(PPLine, lback(pl), NSTRING);
+ plb = line_isblank(q, PLine, GLine, PPLine, NSTRING);
+ }
+ }
+ return do_quote_match(q, GLine, NLine, PLine, rqstr, rqstrlen, plb);
+}
/*
* Return number of quotes if whatever starts the line matches the quote string
+ * rqstr is a pointer to raw qstring; buf points to processed qstring
*/
int
-quote_match(UCS *q, LINE *l, UCS *buf, size_t buflen)
+quote_match(char **q, LINE *l, char *buf, size_t buflen, int raw)
{
- register int i, n, j, qb;
-
- *buf = '\0';
- if(*q == '\0')
- return(1);
-
- qb = (ucs4_strlen(q) > 1 && q[ucs4_strlen(q)-1] == ' ') ? 1 : 0;
- for(n = 0, j = 0; ;){
- for(i = 0; j <= llength(l) && qb ? q[i+1] : q[i]; i++, j++)
- if(q[i] != lgetc(l, j).c)
- return(n);
-
- n++;
- if((!qb && q[i] == '\0') || (qb && q[i+1] == '\0')){
- if(ucs4_strlen(buf) + ucs4_strlen(q) + 1 < buflen){
- ucs4_strncat(buf, q, buflen-ucs4_strlen(q)-1);
- buf[buflen-1] = '\0';
- if(qb && (j > llength(l) || lgetc(l, j).c != ' '))
- buf[ucs4_strlen(buf)-1] = '\0';
- }
- }
- if(j > llength(l))
- return(n);
- else if(qb && lgetc(l, j).c == ' ')
- j++;
+ QSTRING_S *qs;
+ char rqstr[NSTRING];
+
+ qs = qs_quote_match(q, l, rqstr, NSTRING);
+ if(qs)
+ record_quote_string(qs);
+ flatten_qstring(qs, buf, buflen);
+ if (qs) free_qs(&qs);
+
+ if(raw){
+ strncpy(buf, rqstr, buflen < NSTRING ? buflen : NSTRING);
+ buf[buflen-1] = '\0';
}
- return(n); /* never reached */
+
+ return buf && buf[0] ? strlen(buf) : 0;
}
+int ucs4_strlenis(UCS *ucs_qstr)
+{
+ char *str = ucs4_to_utf8_cpystr(ucs_qstr);
+ int i = (int) strlenis(str);
+
+ if(str) fs_give((void **)&str);
+ return i;
+}
/* Justify the entire buffer instead of just a paragraph */
int
@@ -721,6 +835,7 @@ fillpara(int f, int n)
}
if(action == 'R' && curwp->w_markp){
+ char qstrfl[NSTRING];
/* let yank() know that it may be restoring a paragraph */
thisflag |= CFFILL;
@@ -733,21 +848,25 @@ fillpara(int f, int n)
/* determine if we're justifying quoted text or not */
qstr = (glo_quote_str
- && quote_match(glo_quote_str,
- curwp->w_doto > 0 ? curwp->w_dotp->l_fp : curwp->w_dotp,
- qstr2, NSTRING)
- && *qstr2) ? qstr2 : NULL;
-
+ && quote_match(default_qstr(glo_quote_str, 1),
+ (curwp->w_doto > 0 ? curwp->w_dotp->l_fp : curwp->w_dotp),
+ qstrfl, NSTRING, 0)
+ && *qstrfl) ? utf8_to_ucs4_cpystr(qstrfl) : NULL;
/*
* Fillregion moves dot to the end of the filled region.
*/
- if(!fillregion(qstr, &addedregion))
+ if(!fillregion(qstr, NULL, &addedregion))
return(FALSE);
set_last_region_added(&addedregion);
+
+ if(qstr)
+ fs_give((void **)&qstr);
}
else if(action == 'P'){
+ char ind_str[NSTRING], qstrfl[NSTRING];
+ UCS *istr;
/*
* Justfiy the current paragraph.
@@ -759,17 +878,16 @@ fillpara(int f, int n)
if(gotoeop(FALSE, 1) == FALSE)
return(FALSE);
- /* determine if we're justifying quoted text or not */
- qstr = (glo_quote_str
- && quote_match(glo_quote_str,
- curwp->w_dotp, qstr2, NSTRING)
- && *qstr2) ? qstr2 : NULL;
-
setmark(0,0); /* mark last line of para */
/* jump back to the beginning of the paragraph */
gotobop(FALSE, 1);
+ istr = indent_match(default_qstr(glo_quote_str, 1), curwp->w_dotp, ind_str, NSTRING, 0)
+ && *ind_str ? utf8_to_ucs4_cpystr(ind_str) : NULL;
+ qstr = (quote_match(default_qstr(glo_quote_str, 1), curwp->w_dotp, qstrfl, NSTRING, 0)
+ && *qstrfl) ? utf8_to_ucs4_cpystr(qstrfl) : NULL;
+
/* let yank() know that it may be restoring a paragraph */
thisflag |= (CFFILL | CFFLPA);
@@ -783,9 +901,15 @@ fillpara(int f, int n)
/*
* Fillregion moves dot to the end of the filled region.
*/
- if(!fillregion(qstr, &addedregion))
+ if(!fillregion(qstr, istr, &addedregion))
return(FALSE);
+ if(qstr)
+ fs_give((void **)&qstr);
+
+ if(istr)
+ fs_give((void **)&istr);
+
set_last_region_added(&addedregion);
/* Leave cursor on first char of first line after justified region */
@@ -827,16 +951,16 @@ fillpara(int f, int n)
* can delete it and restore the saved part.
*/
int
-fillregion(UCS *qstr, REGION *addedregion)
+fillregion(UCS *qstr, UCS *istr, REGION *addedregion)
{
long c, sz, last_char = 0;
- int i, j, qlen, same_word,
+ int i, j, qlen, same_word, qi, pqi, qlenis,
spaces, word_len, word_ind, line_len, ww;
int starts_midline = 0;
int ends_midline = 0;
int offset_into_start;
LINE *line_before_start, *lp;
- UCS line_last, word[NSTRING];
+ UCS line_last, word[NSTRING], quoid[NSTRING], qstr2[NSTRING];
REGION region;
/* if region starts midline insert a newline */
@@ -847,6 +971,35 @@ fillregion(UCS *qstr, REGION *addedregion)
if(curwp->w_marko > 0 && curwp->w_marko < llength(curwp->w_markp))
ends_midline++;
+ for (i = 0; (i < NSTRING) && qstr && (quoid[i] = qstr[i]); i++);
+ for (j = 0; ((i + j) < NSTRING) && istr && (quoid[i] = istr[j]); i++,j++);
+ quoid[i] = '\0';
+ qi = ucs4_strlen(quoid);
+ if (istr) /* strip trailing spaces */
+ for (;ISspace(quoid[qi - 1]); qi--);
+ quoid[qi] = '\0'; /* we have closed quoid at "X" in the first line */
+
+ if (ucs4_strlenis(quoid) > fillcol)
+ return FALSE; /* Too wide, we can't justify this! */
+
+ if (qstr && istr){
+ for (i = ucs4_strlen(qstr) - 1; ISspace(qstr[i]); i--);
+ qstr[i + 1] = '\0'; /* qstrfl */
+ }
+ qlen = ucs4_strlen(qstr); /* qstrfl*/
+ qlenis = ucs4_strlenis(qstr);
+
+ for(i = 0, qstr2[0] = '\0'; qstr && qstr[i] && (qstr2[i] = qstr[i]); i++);
+
+ if (istr && ((j = ucs4_strlenis(quoid) - ucs4_strlenis(qstr)) > 0)){
+ pqi = ucs4_strlen(qstr);
+ for (i = 0; (i < j) && (qstr2[pqi + i] = ' '); i++);
+ if (ISspace(istr[ucs4_strlen(istr) - 1]))
+ qstr2[pqi + i++] = ' ';
+ qstr2[pqi + i] = '\0';
+ qstr = qstr2;
+ }
+
/* cut the paragraph into our fill buffer */
fdelete();
if(!getregion(&region, curwp->w_markp, curwp->w_marko))
@@ -863,28 +1016,36 @@ fillregion(UCS *qstr, REGION *addedregion)
/* Now insert it back wrapped */
spaces = word_len = word_ind = line_len = same_word = 0;
- qlen = qstr ? ucs4_strlen(qstr) : 0;
/* Beginning with leading quoting... */
- if(qstr){
- i = 0;
- while(qstr[i]){
- ww = wcellwidth(qstr[i]);
- line_len += (ww >= 0 ? ww : 1);
- linsert(1, qstr[i++]);
- }
+ if(qstr || istr){
+ for(i = 0; quoid[i] != '\0' ; i++)
+ linsert(1, quoid[i]);
line_last = ' '; /* no word-flush space! */
+ line_len = ucs4_strlenis(quoid); /* we demand a recount! */
}
/* remove first leading quotes if any */
if(starts_midline)
i = 0;
- else
- for(i = qlen; (c = fremove(i)) == ' ' || c == TAB; i++){
+ else{
+ if(qstr || istr){
+ for (i = 0; (c = fremove(i)) != '\0'; i++){
+ word[i] = c;
+ word[i+1] = '\0';
+ if(ucs4_strlenis(word) >= ucs4_strlenis(quoid))
+ break;
+ }
+ i++;
+ }
+ else
+ i = 0;
+ for(; ISspace(c = fremove(i)); i++){
linsert(1, line_last = (UCS) c);
line_len += ((c == TAB) ? (~line_len & 0x07) + 1 : 1);
}
+ }
/* then digest the rest... */
while((c = fremove(i++)) >= 0){
@@ -905,21 +1066,22 @@ fillregion(UCS *qstr, REGION *addedregion)
case TAB :
case ' ' :
+ case NBSP:
spaces++;
break;
default :
if(spaces){ /* flush word? */
- if((line_len - qlen > 0)
+ if((line_len - qlenis > 0)
&& line_len + word_len + 1 > fillcol
- && ((ucs4_isspace(line_last))
+ && ((ISspace(line_last))
|| (linsert(1, ' ')))
&& same_word == 0
&& (line_len = fpnewline(qstr)))
line_last = ' '; /* no word-flush space! */
if(word_len){ /* word to write? */
- if(line_len && !ucs4_isspace(line_last)){
+ if(line_len && !ISspace(line_last)){
linsert(1, ' '); /* need padding? */
line_len++;
}
@@ -941,8 +1103,8 @@ fillregion(UCS *qstr, REGION *addedregion)
if(word_ind + 1 >= NSTRING){
/* Magic! Fake that we output a wrapped word */
- if((line_len - qlen > 0) && same_word == 0){
- if(!ucs4_isspace(line_last))
+ if((line_len - qlenis > 0) && same_word == 0){
+ if(!ISspace(line_last))
linsert(1, ' ');
line_len = fpnewline(qstr);
}
@@ -964,12 +1126,12 @@ fillregion(UCS *qstr, REGION *addedregion)
}
if(word_len){
- if((line_len - qlen > 0) && (line_len + word_len + 1 > fillcol) && same_word == 0){
- if(!ucs4_isspace(line_last))
+ if((line_len - qlenis > 0) && (line_len + word_len + 1 > fillcol) && same_word == 0){
+ if(!ISspace(line_last))
linsert(1, ' ');
(void) fpnewline(qstr);
}
- else if(line_len && !ucs4_isspace(line_last))
+ else if(line_len && !ISspace(line_last))
linsert(1, ' ');
for(j = 0; j < word_ind; j++)
@@ -1027,11 +1189,11 @@ fpnewline(UCS *quote)
int len;
lnewline();
- for(len = 0; quote && *quote; quote++){
+ for(len = ucs4_strlenis(quote); quote && *quote; quote++){
int ww;
- ww = wcellwidth(*quote);
- len += (ww >= 0 ? ww : 1);
+/* ww = wcellwidth(*quote);
+ len += (ww >= 0 ? ww : 1);*/
linsert(1, *quote);
}
@@ -1175,5 +1337,45 @@ setquotelevelinregion(int quotelevel, REGION *addedregion)
markregion(1);
}
+ /*
+ * This puts us at the end of the quoted region instead
+ * of on the following line. This makes it convenient
+ * for the user to follow a quotelevel adjustment with
+ * a Justify if desired.
+ */
+ if(backuptoprevline){
+ curwp->w_doto = 0;
+ backchar(0, 1);
+ }
+
+ if(ends_midline){ /* doesn't need fixing otherwise */
+ unmarkbuffer();
+ markregion(1);
+ }
+
return (TRUE);
}
+
+/*
+ * If there is an indent string this function returns
+ * its length
+ */
+int
+indent_match(char **q, LINE *l, char *buf, int buflen, int raw)
+{
+ char GLine[NSTRING];
+ int i, k, plb;
+
+ k = quote_match(q,l, buf, buflen, raw);
+ linencpy(GLine, l, NSTRING);
+ plb = (lback(l) != curbp->b_linep) ? lisblank(lback(l)) : 1;
+ if (!plb){
+ i = llength(lback(l)) - 1;
+ for (; i >= 0 && ISspace(lgetc(lback(l), i).c); i--);
+ if (EOLchar(lgetc(lback(l), i).c))
+ plb++;
+ }
+
+ return get_indent_raw_line(q, GLine, buf, buflen, k, plb);
+}
+
diff --git a/pith/Makefile.am b/pith/Makefile.am
index ce6c78a3..7def45ef 100644
--- a/pith/Makefile.am
+++ b/pith/Makefile.am
@@ -25,7 +25,7 @@ libpith_a_SOURCES = ablookup.c abdlc.c addrbook.c addrstring.c adrbklib.c bldadd
filter.c flag.c folder.c handle.c help.c helpindx.c hist.c icache.c imap.c init.c \
keyword.c ldap.c list.c mailcap.c mailcmd.c mailindx.c maillist.c mailview.c \
margin.c mimedesc.c mimetype.c msgno.c newmail.c news.c pattern.c pipe.c \
- readfile.c remote.c reply.c rfc2231.c save.c search.c sequence.c send.c sort.c \
+ readfile.c remote.c reply.c rfc2231.c rules.c save.c search.c sequence.c send.c sort.c \
state.c status.c store.c stream.c string.c strlst.c takeaddr.c tempfile.c text.c \
thread.c adjtime.c url.c util.c helptext.c smkeys.c smime.c
diff --git a/pith/Makefile.in b/pith/Makefile.in
index 322291b0..708abccf 100644
--- a/pith/Makefile.in
+++ b/pith/Makefile.in
@@ -83,7 +83,7 @@ am_libpith_a_OBJECTS = ablookup.$(OBJEXT) abdlc.$(OBJEXT) \
margin.$(OBJEXT) mimedesc.$(OBJEXT) mimetype.$(OBJEXT) \
msgno.$(OBJEXT) newmail.$(OBJEXT) news.$(OBJEXT) \
pattern.$(OBJEXT) pipe.$(OBJEXT) readfile.$(OBJEXT) \
- remote.$(OBJEXT) reply.$(OBJEXT) rfc2231.$(OBJEXT) \
+ remote.$(OBJEXT) reply.$(OBJEXT) rfc2231.$(OBJEXT) rules.$(OBJEXT) \
save.$(OBJEXT) search.$(OBJEXT) sequence.$(OBJEXT) \
send.$(OBJEXT) sort.$(OBJEXT) state.$(OBJEXT) status.$(OBJEXT) \
store.$(OBJEXT) stream.$(OBJEXT) string.$(OBJEXT) \
@@ -319,7 +319,7 @@ libpith_a_SOURCES = ablookup.c abdlc.c addrbook.c addrstring.c adrbklib.c bldadd
filter.c flag.c folder.c handle.c help.c helpindx.c hist.c icache.c imap.c init.c \
keyword.c ldap.c list.c mailcap.c mailcmd.c mailindx.c maillist.c mailview.c \
margin.c mimedesc.c mimetype.c msgno.c newmail.c news.c pattern.c pipe.c \
- readfile.c remote.c reply.c rfc2231.c save.c search.c sequence.c send.c sort.c \
+ readfile.c remote.c reply.c rfc2231.c rules.c save.c search.c sequence.c send.c sort.c \
state.c status.c store.c stream.c string.c strlst.c takeaddr.c tempfile.c text.c \
thread.c adjtime.c url.c util.c helptext.c smkeys.c smime.c
@@ -451,6 +451,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/thread.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/url.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/util.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rules.Po@am__quote@
.c.o:
@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
diff --git a/pith/adrbklib.c b/pith/adrbklib.c
index 01d00353..904928b8 100644
--- a/pith/adrbklib.c
+++ b/pith/adrbklib.c
@@ -5136,8 +5136,14 @@ init_addrbooks(OpenStatus want_status, int reset_to_top, int open_if_only_one, i
if(as.cur >= as.how_many_personals)
pab->type |= GLOBAL;
- pab->access = adrbk_access(pab);
-
+ if(ps_global->mail_stream &&
+ ps_global->mail_stream->lock && (pab->type & REMOTE_VIA_IMAP)){
+ as.initialized = 0;
+ pab->access = NoAccess;
+ }
+ else{
+ pab->access = adrbk_access(pab);
+ }
/* global address books are forced readonly */
if(pab->type & GLOBAL && pab->access != NoAccess)
pab->access = ReadOnly;
diff --git a/pith/charconv/utf8.c b/pith/charconv/utf8.c
index 411e1ddd..bca0d26b 100644
--- a/pith/charconv/utf8.c
+++ b/pith/charconv/utf8.c
@@ -1049,6 +1049,56 @@ utf8_width(char *str)
/*
+ * Returns the screen cells width of the UTF-8 string argument, treating tabs
+ * in a special way.
+ */
+unsigned
+utf8_widthis(char *str)
+{
+ unsigned width = 0;
+ int this_width;
+ UCS ucs;
+ unsigned long remaining_octets;
+ char *readptr;
+
+ if(!(str && *str))
+ return(width);
+
+ readptr = str;
+ remaining_octets = readptr ? strlen(readptr) : 0;
+
+ while(remaining_octets > 0 && *readptr){
+
+ ucs = (UCS) utf8_get((unsigned char **) &readptr, &remaining_octets);
+
+ if(ucs & U8G_ERROR){
+ /*
+ * This should not happen, but do something to handle it anyway.
+ * Treat each character as a single width character, which is what should
+ * probably happen when we actually go to write it out.
+ */
+ remaining_octets--;
+ readptr++;
+ this_width = 1;
+ }
+ else{
+ this_width = (ucs == TAB) ? ((~width & 0x07) + 1) : wcellwidth(ucs);
+
+ /*
+ * If this_width is -1 that means we can't print this character
+ * with our current locale. Writechar will print a '?'.
+ */
+ if(this_width < 0)
+ this_width = 1;
+ }
+
+ width += (unsigned) this_width;
+ }
+
+ return(width);
+}
+
+/*
* Copy UTF-8 characters from src into dst.
* This is intended to be used if you want to truncate a string at
* the start instead of the end. For example, you have a long string
diff --git a/pith/charconv/utf8.h b/pith/charconv/utf8.h
index d22a8a7c..09a2a95c 100644
--- a/pith/charconv/utf8.h
+++ b/pith/charconv/utf8.h
@@ -81,6 +81,7 @@ UCS *ucs4_strncat(UCS *ucs4dst, UCS *ucs4src, size_t n);
UCS *ucs4_strchr(UCS *s, UCS c);
UCS *ucs4_strrchr(UCS *s, UCS c);
unsigned utf8_width(char *);
+unsigned utf8_widthis(char *);
size_t utf8_to_width_rhs(char *, char *, size_t, unsigned);
int utf8_snprintf(char *, size_t, char *, ...);
size_t utf8_to_width(char *, char *, size_t, unsigned, unsigned *);
diff --git a/pith/color.c b/pith/color.c
index 9794294b..b5dc320c 100644
--- a/pith/color.c
+++ b/pith/color.c
@@ -21,7 +21,8 @@ static char rcsid[] = "$Id: color.c 769 2007-10-24 00:15:40Z hubert@u.washington
#include "../pith/state.h"
#include "../pith/conf.h"
#include "../pith/filter.h"
-
+#include "../pith/mailview.h"
+#include "../pico/estruct.h"
char *
color_embed(char *fg, char *bg)
@@ -70,23 +71,110 @@ struct quote_colors {
struct quote_colors *next;
};
+int
+is_word (buf, i, j)
+ char buf[NSTRING];
+ int i, j;
+{
+ return i <= j && is_letter(buf[i]) ?
+ (i < j ? is_word(buf,i+1,j) : 1) : 0;
+}
+
+int
+is_mailbox(buf,i,j)
+char buf[NSTRING];
+ int i, j;
+{
+ return i <= j && (is_letter(buf[i]) || is_digit(buf[i]) || buf[i] == '.')
+ ? (i < j ? is_mailbox(buf,i+1,j) : 1) : 0;
+}
+
+int
+next_level_quote(buf, line, i, is_flowed)
+ char *buf;
+ char **line;
+ int i;
+ int is_flowed;
+{
+ int j;
+
+ if (!single_level(buf[i])){
+ if(is_mailbox(buf,i,i)){
+ for (j = i; buf[j] && !isspace(buf[j]); j++);
+ if (is_word(buf,i,j-1) || is_mailbox(buf,i,j-1))
+ j += isspace(buf[j]) ? 2 : 1;
+ }
+ else{
+ switch(buf[i]){
+ case ':' :
+ if (next(buf,i) != RPAREN)
+ j = i + 1;
+ else
+ j = i + 2;
+ break;
+
+ case '-' :
+ if (next(buf,i) != '-')
+ j = i + 2;
+ else
+ j = i + 3;
+ break;
+
+ case '+' :
+ case '*' :
+ if (next(buf,i) != ' ')
+ j = i + 2;
+ else
+ j = i + 3;
+ break;
+
+ default :
+ for (j = i; buf[j] && !isspace(buf[j])
+ && (!single_level(buf[i]) && !is_letter(buf[j])); j++);
+
+ j += isspace(buf[j]) ? 1 : 0;
+ break;
+ }
+ }
+ if (line && *line)
+ (*line) += j - i;
+ }
+ else{
+ j = i+1;
+ if (line && *line)
+ (*line)++;
+ }
+ if(!is_flowed){
+ if(line && *line)
+ for(; isspace((unsigned char)*(*line)); (*line)++);
+ for (i = j; isspace((unsigned char) buf[i]); i++);
+ }
+ else i = j;
+ if (is_flowed && i != j)
+ buf[i] = '\0';
+ return i;
+}
int
color_a_quote(long int linenum, char *line, LT_INS_S **ins, void *is_flowed_msg)
{
- int countem = 0;
+ int countem = 0, i, j = 0;
struct variable *vars = ps_global->vars;
- char *p;
+ char *p, buf[NSTRING] = {'\0'};
struct quote_colors *colors = NULL, *cp, *next;
COLOR_PAIR *col = NULL;
int is_flowed = is_flowed_msg ? *((int *)is_flowed_msg) : 0;
+ int code;
+
+ code = (is_flowed ? IS_FLOWED : NO_FLOWED) | COLORAQUO;
+ select_quote(linenum, line, ins, (void *) &code);
+ strncpy(buf, tmp_20k_buf, NSTRING < SIZEOF_20KBUF ? NSTRING : SIZEOF_20KBUF);
+ buf[sizeof(buf)-1] = '\0';
p = line;
- if(!is_flowed)
- while(isspace((unsigned char)*p))
- p++;
+ for(i = 0; isspace((unsigned char)buf[i]); i++, p++);
- if(p[0] == '>'){
+ if(buf[i]){
struct quote_colors *c;
/*
@@ -135,7 +223,7 @@ color_a_quote(long int linenum, char *line, LT_INS_S **ins, void *is_flowed_msg)
free_color_pair(&col);
cp = NULL;
- while(*p == '>'){
+ while(buf[i]){
cp = (cp && cp->next) ? cp->next : colors;
if(countem > 0)
@@ -145,10 +233,9 @@ color_a_quote(long int linenum, char *line, LT_INS_S **ins, void *is_flowed_msg)
countem = (countem == 1) ? 0 : countem;
- p++;
- if(!is_flowed)
- for(; isspace((unsigned char)*p); p++)
- ;
+ i = next_level_quote(buf, &p, i, is_flowed);
+ for (; isspace((unsigned char)*p); p++);
+ for (; isspace((unsigned char)buf[i]); i++);
}
if(colors){
@@ -211,7 +298,7 @@ color_a_quote(long int linenum, char *line, LT_INS_S **ins, void *is_flowed_msg)
}
}
- return(0);
+ return(1);
}
diff --git a/pith/color.h b/pith/color.h
index b90d82cf..01bbbb58 100644
--- a/pith/color.h
+++ b/pith/color.h
@@ -21,6 +21,24 @@
#include "../pith/pattern.h"
#include "../pith/osdep/color.h"
+#define NO_FLOWED 0x0000
+#define IS_FLOWED 0x0001
+#define DELETEQUO 0x0010
+#define COLORAQUO 0x0100
+#define RAWSTRING 0x1000
+
+/* This is needed for justification, I will move it to a better place later
+ * or maybe not
+ */
+#define is_digit(c) ((((c) >= '0') && ((c) <= '9')) ? 1 : 0)
+
+#define is_letter(c) (((c) >= 'a' && (c) <= 'z') || \
+ ((c) >= 'A' && (c) <= 'Z'))
+
+#define next(w,i) ((((w)[(i)]) != 0) ? ((w)[(i) + 1]) : 0)
+
+#define single_level(c) (((c) == '>') || ((c) == '|') || ((c) == '~') || \
+ ((c) == ']'))
typedef struct spec_color_s {
int inherit; /* this isn't a color, it is INHERIT */
@@ -80,6 +98,7 @@ typedef struct spec_color_s {
/* exported protoypes */
char *color_embed(char *, char *);
int colorcmp(char *, char *);
+int next_level_quote(char *, char **, int, int);
int color_a_quote(long, char *, LT_INS_S **, void *);
void free_spec_colors(SPEC_COLOR_S **);
diff --git a/pith/conf.c b/pith/conf.c
index 18eaf8f0..876667e5 100644
--- a/pith/conf.c
+++ b/pith/conf.c
@@ -29,6 +29,7 @@ static char rcsid[] = "$Id: conf.c 1266 2009-07-14 18:39:12Z hubert@u.washington
#include "../pith/remote.h"
#include "../pith/keyword.h"
#include "../pith/mailview.h"
+#include "../pith/rules.h"
#include "../pith/list.h"
#include "../pith/status.h"
#include "../pith/ldap.h"
@@ -206,6 +207,8 @@ CONF_TXT_T cf_text_fcc_name_rule[] = "Determines default name for Fcc...\n# Choi
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\".";
@@ -222,12 +225,44 @@ CONF_TXT_T cf_text_unk_character_set[] = "Defaults to nothing, which is equivale
CONF_TXT_T cf_text_editor[] = "Specifies the program invoked by ^_ in the Composer,\n# or the \"enable-alternate-editor-implicitly\" feature.";
+CONF_TXT_T cf_text_compose_rules[] = "Allows a user to set rules when composing messages.";
+
+CONF_TXT_T cf_text_forward_rules[] = "Allows a user to set rules when forwarding messages.";
+
+CONF_TXT_T cf_text_reply_rules[] = "Allows a user to set rules when replying messages.";
+
+CONF_TXT_T cf_text_index_rules[] = "Allows a user to supersede global index format variable in designated folders.";
+
+CONF_TXT_T cf_text_key_def_rules[] = "Allows a user to override keystrokes in certain screens.";
+
+CONF_TXT_T cf_text_replace_rules[] = "Allows a user to change the form a specify field in the index-format is \n# displayed.";
+
+CONF_TXT_T cf_text_reply_indent_rules[] = "Allows a user to change the form a specify a reply-indent-string\n# based of rules.";
+
+CONF_TXT_T cf_text_reply_leadin_rules[] = "Allows a user to replace the reply-leadin message based on different parameters.";
+
+CONF_TXT_T cf_text_reply_subject_rules[] = "Allows a user to replace the subject of a message in a customs based way";
+
+CONF_TXT_T cf_text_thread_displaystyle_rule[] = "Allows a user to specify the threading style of specific folders";
+
+CONF_TXT_T cf_text_thread_indexstyle_rule[] = "Allows a user to specify the threading index style of specific folders";
+
+CONF_TXT_T cf_text_save_rules[] = "Allows a user to specify a save folder message for specific senders or folders.";
+
+CONF_TXT_T cf_text_smtp_rules[] = "Allows a user to specify a smtp server to be used when sending e-mail,\n# according to the rules specified here.";
+
+CONF_TXT_T cf_text_sort_rules[] = "Allows a user to specify the sort default order of a specific folder.";
+
+CONF_TXT_T cf_text_startup_rules[] = "Allows a user to specify the position of a highlighted message when opening a \n# folder.";
+
CONF_TXT_T cf_text_speller[] = "Specifies the program invoked by ^T in the Composer.";
CONF_TXT_T cf_text_deadlets[] = "Specifies the number of dead letter files to keep when canceling.";
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.";
@@ -340,6 +375,8 @@ CONF_TXT_T cf_text_stat_msg_delay[] = "The number of seconds to sleep after writ
CONF_TXT_T cf_text_busy_cue_rate[] = "Number of times per-second to update busy cue messages";
+CONF_TXT_T cf_text_sleep[] = "The number of seconds between a viewer finishing opening a file and removing\n#it. See more details in configuration screen. Default: 0";
+
CONF_TXT_T cf_text_mailcheck[] = "The approximate number of seconds between checks for new mail";
CONF_TXT_T cf_text_mailchecknoncurr[] = "The approximate number of seconds between checks for new mail in folders\n# other than the current folder and inbox.\n# Default is same as mail-check-interval";
@@ -430,6 +467,9 @@ CONF_TXT_T cf_text_window_position[] = "Window position in the format: CxR+X+Y\n
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
@@ -520,6 +560,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,
@@ -542,6 +584,34 @@ static struct variable variables[] = {
NULL, cf_text_thread_exp_char},
{"threading-lastreply-character", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
"Threading Last Reply Character", cf_text_thread_lastreply_char},
+{"threading-display-style-rule", 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0,
+ "Threading Display Style Rule", cf_text_thread_displaystyle_rule},
+{"threading-index-style-rule", 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0,
+ "Threading Index Style Rule", cf_text_thread_indexstyle_rule},
+{"compose-rules", 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0,
+ "Compose Rules", cf_text_compose_rules},
+{"forward-rules", 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0,
+ "Forward Rules", cf_text_forward_rules},
+{"index-rules", 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0,
+ "Index Rules", cf_text_index_rules},
+{"key-definition-rules", 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0,
+ "Key Definition Rules", cf_text_key_def_rules},
+{"replace-rules", 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0,
+ "Replace Rules", cf_text_replace_rules},
+{"reply-indent-rules", 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0,
+ "Reply Indent Rules", cf_text_reply_indent_rules},
+{"reply-leadin-rules", 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0,
+ "Reply Leadin Rules", cf_text_reply_leadin_rules},
+{"reply-subject-rules", 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0,
+ "Reply Subject Rules", cf_text_reply_subject_rules},
+{"save-rules", 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0,
+ "Save Rules", cf_text_save_rules},
+{"smtp-rules", 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0,
+ "Smtp Rules", cf_text_smtp_rules},
+{"sort-rules", 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0,
+ "Sort Rules", cf_text_sort_rules},
+{"startup-rules", 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0,
+ "Startup Rules", cf_text_startup_rules},
#ifndef _WINDOWS
{"display-character-set", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
NULL, cf_text_disp_char_set},
@@ -560,6 +630,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,
@@ -616,6 +688,8 @@ static struct variable variables[] = {
NULL, cf_text_stat_msg_delay},
{"busy-cue-rate", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
NULL, cf_text_busy_cue_rate},
+{"sleep-interval-length", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_sleep},
{"mail-check-interval", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
NULL, cf_text_mailcheck},
{"mail-check-interval-noncurrent", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
@@ -630,6 +704,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,
@@ -817,6 +895,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},
@@ -1566,7 +1646,7 @@ init_vars(struct pine *ps, void (*cmds_f) (struct pine *, char **))
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;
@@ -1591,6 +1671,7 @@ init_vars(struct pine *ps, void (*cmds_f) (struct pine *, char **))
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);
@@ -1615,6 +1696,7 @@ init_vars(struct pine *ps, void (*cmds_f) (struct pine *, char **))
GLO_LOCAL_FULLNAME = cpystr(DF_LOCAL_FULLNAME);
GLO_LOCAL_ADDRESS = cpystr(DF_LOCAL_ADDRESS);
GLO_OVERLAP = cpystr(DF_OVERLAP);
+ GLO_SLEEP = cpystr("0");
GLO_MAXREMSTREAM = cpystr(DF_MAXREMSTREAM);
GLO_MARGIN = cpystr(DF_MARGIN);
GLO_FILLCOL = cpystr(DF_FILLCOL);
@@ -1985,6 +2067,8 @@ init_vars(struct pine *ps, void (*cmds_f) (struct pine *, char **))
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);
@@ -2069,6 +2153,13 @@ init_vars(struct pine *ps, void (*cmds_f) (struct pine *, char **))
}
}
+ set_current_val(&vars[V_SLEEP], TRUE, TRUE);
+ ps->sleep = i = 0;
+ if(SVAR_SLEEP(ps, i, tmp_20k_buf, SIZEOF_20KBUF))
+ init_error(ps, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
+ else
+ ps->sleep = i;
+
set_current_val(&vars[V_OVERLAP], TRUE, TRUE);
ps->viewer_overlap = i = atoi(DF_OVERLAP);
if(SVAR_OVERLAP(ps, i, tmp_20k_buf, SIZEOF_20KBUF))
@@ -2258,6 +2349,12 @@ init_vars(struct pine *ps, void (*cmds_f) (struct pine *, char **))
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])
@@ -2497,7 +2594,7 @@ init_vars(struct pine *ps, void (*cmds_f) (struct pine *, char **))
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;
@@ -2506,6 +2603,17 @@ init_vars(struct pine *ps, void (*cmds_f) (struct pine *, char **))
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++)
@@ -2592,6 +2700,7 @@ init_vars(struct pine *ps, void (*cmds_f) (struct pine *, char **))
if(cmds_f)
(*cmds_f)(ps, VAR_INIT_CMD_LIST);
+ (void)create_rule_list(ps_global->vars);
#ifdef _WINDOWS
mswin_set_quit_confirm (F_OFF(F_QUIT_WO_CONFIRM, ps_global));
#endif /* _WINDOWS */
@@ -2795,6 +2904,8 @@ feature_list(int index)
F_ALWAYS_SPELL_CHECK, h_config_always_spell_check, PREF_COMP, 0},
/* Reply Prefs */
+ {"alternate-reply-menu", NULL,
+ F_ALT_REPLY_MENU, h_config_alt_reply_menu, PREF_RPLY, 0},
{"copy-to-address-to-from-if-it-is-us", "Copy To Address to From if it is Us",
F_COPY_TO_TO_FROM, h_config_copy_to_to_from, PREF_RPLY, 0},
{"enable-reply-indent-string-editing", NULL,
@@ -2839,6 +2950,8 @@ feature_list(int index)
F_NO_FCC_ATTACH, h_config_no_fcc_attach, PREF_SEND, 0},
{"fcc-on-bounce", "Include Fcc When Bouncing Messages",
F_FCC_ON_BOUNCE, h_config_fcc_on_bounce, PREF_SEND, 0},
+ {"return-path-uses-domain-name", NULL,
+ F_USE_DOMAIN_NAME, h_config_use_domain, PREF_SEND, 0},
{"mark-fcc-seen", NULL,
F_MARK_FCC_SEEN, h_config_mark_fcc_seen, PREF_SEND, 0},
{"fcc-only-without-confirm", "Send to Fcc Only Without Confirming",
@@ -2885,6 +2998,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",
@@ -2901,6 +3018,8 @@ feature_list(int index)
/* Index prefs */
{"auto-open-next-unread", NULL,
F_AUTO_OPEN_NEXT_UNREAD, h_config_auto_open_unread, PREF_INDX, 0},
+ {"enable-circular-tab", NULL,
+ F_AUTO_CIRCULAR_TAB, h_config_circular_tab, PREF_INDX, 0},
{"continue-tab-without-confirm", "Continue NextNew Without Confirming",
F_TAB_NO_CONFIRM, h_config_tab_no_prompt, PREF_INDX, 0},
{"convert-dates-to-localtime", NULL,
@@ -2913,6 +3032,8 @@ feature_list(int index)
F_ENABLE_SPACE_AS_TAB, h_config_cruise_mode, PREF_INDX, 0},
{"enable-cruise-mode-delete", "Enable Cruise Mode With Deleting",
F_ENABLE_TAB_DELETES, h_config_cruise_mode_delete, PREF_INDX, 0},
+ {"mark-for-me-in-group", "Mark for Group Message to Me",
+ F_MARK_FOR_GROUP, h_config_mark_for_group, PREF_INDX, 1},
{"mark-for-cc", "Mark for CC",
F_MARK_FOR_CC, h_config_mark_for_cc, PREF_INDX, 1},
{"next-thread-without-confirm", "Read Next Thread Without Confirming",
@@ -2929,6 +3050,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",
@@ -2939,6 +3062,8 @@ feature_list(int index)
F_VIEW_SEL_URL, h_config_enable_view_url, PREF_VIEW, 1},
{"enable-msg-view-web-hostnames", "Enable Message View Web Hostname Links",
F_VIEW_SEL_URL_HOST, h_config_enable_view_web_host, PREF_VIEW, 1},
+ {"enable-msg-view-long-url", "Enable Recognition of Long URLS without Delimiter",
+ F_VIEW_LONG_URL, h_config_enable_long_url, PREF_VIEW, 0},
{"enable-msg-view-forced-arrows", "Enable Message View Forced Arrows",
F_FORCE_ARROWS, h_config_enable_view_arrows, PREF_VIEW, 0},
/* set to TRUE for windows */
@@ -3036,6 +3161,8 @@ feature_list(int index)
F_FORCE_LOW_SPEED, h_config_force_low_speed, PREF_OS_LWSD, 0},
{"auto-move-read-msgs", "Auto Move Read Messages",
F_AUTO_READ_MSGS, h_config_auto_read_msgs, PREF_MISC, 0},
+ {"auto-move-read-msgs-using-rules", "Auto Move Read Messages Using Rules",
+ F_AUTO_READ_MSGS_RULES, h_config_auto_read_msgs_rules, PREF_MISC, 0},
{"auto-unselect-after-apply", NULL,
F_AUTO_UNSELECT, h_config_auto_unselect, PREF_MISC, 0},
{"auto-unzoom-after-apply", NULL,
@@ -3097,6 +3224,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},
@@ -6442,6 +6571,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);
@@ -6964,6 +7094,12 @@ toggle_feature(struct pine *ps, struct variable *var, FEATURE_S *f,
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);
@@ -6975,6 +7111,7 @@ toggle_feature(struct pine *ps, struct variable *var, FEATURE_S *f,
break;
case F_MARK_FOR_CC :
+ case F_MARK_FOR_GROUP :
clear_index_cache(ps->mail_stream, 0);
if(THREADING() && sp_viewing_a_thread(ps->mail_stream))
unview_thread(ps, ps->mail_stream, ps->msgmap);
@@ -7569,10 +7706,40 @@ 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 :
return(h_config_fld_sort_rule);
+ case V_THREAD_DISP_STYLE_RULES:
+ return(h_config_thread_display_style_rule);
+ case V_THREAD_INDEX_STYLE_RULES:
+ return(h_config_thread_index_style_rule);
+ case V_COMPOSE_RULES:
+ return(h_config_compose_rules);
+ case V_FORWARD_RULES:
+ return(h_config_forward_rules);
+ case V_INDEX_RULES:
+ return(h_config_index_rules);
+ case V_KEY_RULES:
+ return(h_config_key_macro_rules);
+ case V_REPLACE_RULES:
+ return(h_config_replace_rules);
+ case V_REPLY_INDENT_RULES:
+ return(h_config_reply_indent_rules);
+ case V_REPLY_LEADIN_RULES:
+ return(h_config_reply_leadin_rules);
+ case V_RESUB_RULES:
+ return(h_config_resub_rules);
+ case V_SAVE_RULES:
+ return(h_config_save_rules);
+ case V_SMTP_RULES:
+ return(h_config_smtp_rules);
+ case V_SORT_RULES:
+ return(h_config_sort_rules);
+ case V_STARTUP_RULES:
+ return(h_config_startup_rules);
case V_POST_CHAR_SET :
return(h_config_post_char_set);
case V_UNK_CHAR_SET :
@@ -7613,6 +7780,8 @@ config_help(int var, int feature)
return(h_config_incoming_second_interv);
case V_INCCHECKLIST :
return(h_config_incoming_list);
+ case V_SLEEP :
+ return(h_config_sleep);
case V_OVERLAP :
return(h_config_viewer_overlap);
case V_MAXREMSTREAM :
@@ -7623,6 +7792,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 :
@@ -7745,6 +7916,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)
@@ -7788,6 +7963,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);
@@ -8271,3 +8449,4 @@ pcpine_general_help(titlebuf)
}
#endif /* _WINDOWS */
+
diff --git a/pith/conf.h b/pith/conf.h
index 54c23ff5..2761d65b 100644
--- a/pith/conf.h
+++ b/pith/conf.h
@@ -144,10 +144,53 @@
#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
#define GLO_FLD_SORT_RULE vars[V_FLD_SORT_RULE].global_val.p
+#define VAR_COMPOSE_RULES vars[V_COMPOSE_RULES].current_val.l
+#define GLO_COMPOSE_RULES vars[V_COMPOSE_RULES].global_val.l
+#define USR_COMPOSE_RULES vars[V_COMPOSE_RULES].user_val.l
+#define VAR_FORWARD_RULES vars[V_FORWARD_RULES].current_val.l
+#define GLO_FORWARD_RULES vars[V_FORWARD_RULES].global_val.l
+#define USR_FORWARD_RULES vars[V_FORWARD_RULES].user_val.l
+#define VAR_INDEX_RULES vars[V_INDEX_RULES].current_val.l
+#define GLO_INDEX_RULES vars[V_INDEX_RULES].global_val.l
+#define USR_INDEX_RULES vars[V_INDEX_RULES].user_val.l
+#define VAR_KEY_RULES vars[V_KEY_RULES].current_val.l
+#define GLO_KEY_RULES vars[V_KEY_RULES].global_val.l
+#define USR_KEY_RULES vars[V_KEY_RULES].user_val.l
+#define VAR_REPLACE_RULES vars[V_REPLACE_RULES].current_val.l
+#define GLO_REPLACE_RULES vars[V_REPLACE_RULES].global_val.l
+#define USR_REPLACE_RULES vars[V_REPLACE_RULES].user_val.l
+#define VAR_REPLY_INDENT_RULES vars[V_REPLY_INDENT_RULES].current_val.l
+#define GLO_REPLY_INDENT_RULES vars[V_REPLY_INDENT_RULES].global_val.l
+#define USR_REPLY_INDENT_RULES vars[V_REPLY_INDENT_RULES].user_val.l
+#define VAR_REPLY_LEADIN_RULES vars[V_REPLY_LEADIN_RULES].current_val.l
+#define GLO_REPLY_LEADIN_RULES vars[V_REPLY_LEADIN_RULES].global_val.l
+#define USR_REPLY_LEADIN_RULES vars[V_REPLY_LEADIN_RULES].user_val.l
+#define VAR_RESUB_RULES vars[V_RESUB_RULES].current_val.l
+#define GLO_RESUB_RULES vars[V_RESUB_RULES].global_val.l
+#define USR_RESUB_RULES vars[V_RESUB_RULES].user_val.l
+#define VAR_THREAD_DISP_STYLE_RULES vars[V_THREAD_DISP_STYLE_RULES].current_val.l
+#define GLO_THREAD_DISP_STYLE_RULES vars[V_THREAD_DISP_STYLE_RULES].global_val.l
+#define VAR_THREAD_INDEX_STYLE_RULES vars[V_THREAD_INDEX_STYLE_RULES].current_val.l
+#define GLO_THREAD_INDEX_STYLE_RULES vars[V_THREAD_INDEX_STYLE_RULES].global_val.l
+#define VAR_SAVE_RULES vars[V_SAVE_RULES].current_val.l
+#define GLO_SAVE_RULES vars[V_SAVE_RULES].global_val.l
+#define USR_SAVE_RULES vars[V_SAVE_RULES].user_val.l
+#define VAR_SMTP_RULES vars[V_SMTP_RULES].current_val.l
+#define GLO_SMTP_RULES vars[V_SMTP_RULES].global_val.l
+#define USR_SMTP_RULES vars[V_SMTP_RULES].user_val.l
+#define VAR_SORT_RULES vars[V_SORT_RULES].current_val.l
+#define GLO_SORT_RULES vars[V_SORT_RULES].global_val.l
+#define USR_SORT_RULES vars[V_SORT_RULES].user_val.l
+#define VAR_STARTUP_RULES vars[V_STARTUP_RULES].current_val.l
+#define GLO_STARTUP_RULES vars[V_STARTUP_RULES].global_val.l
+#define USR_STARTUP_RULES vars[V_STARTUP_RULES].user_val.l
#ifndef _WINDOWS
#define VAR_CHAR_SET vars[V_CHAR_SET].current_val.p
#define GLO_CHAR_SET vars[V_CHAR_SET].global_val.p
@@ -161,6 +204,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
@@ -229,6 +274,8 @@
#define GLO_OPENING_SEP vars[V_OPENING_SEP].global_val.p
#define VAR_ABOOK_FORMATS vars[V_ABOOK_FORMATS].current_val.l
#define VAR_INDEX_FORMAT vars[V_INDEX_FORMAT].current_val.p
+#define VAR_SLEEP vars[V_SLEEP].current_val.p
+#define GLO_SLEEP vars[V_SLEEP].global_val.p
#define VAR_OVERLAP vars[V_OVERLAP].current_val.p
#define GLO_OVERLAP vars[V_OVERLAP].global_val.p
#define VAR_MAXREMSTREAM vars[V_MAXREMSTREAM].current_val.p
@@ -250,6 +297,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
@@ -456,6 +507,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
@@ -671,6 +724,10 @@
*/
#define Q_SUPP_LIMIT (4)
#define Q_DEL_ALL (-10)
+#define SVAR_SLEEP(ps,n,e,el) strtoval((ps)->VAR_SLEEP, \
+ &(n), 0, 120, 0, (e), \
+ (el), \
+ "Sleep-Interval-Length")
#define SVAR_OVERLAP(ps,n,e,el) strtoval((ps)->VAR_OVERLAP, \
&(n), 0, 20, 0, (e), \
(el), \
diff --git a/pith/conftype.h b/pith/conftype.h
index c654f6c5..496a616f 100644
--- a/pith/conftype.h
+++ b/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
@@ -70,6 +71,20 @@ typedef enum { V_PERSONAL_NAME = 0
, V_THREAD_MORE_CHAR
, V_THREAD_EXP_CHAR
, V_THREAD_LASTREPLY_CHAR
+ , V_THREAD_DISP_STYLE_RULES
+ , V_THREAD_INDEX_STYLE_RULES
+ , V_COMPOSE_RULES
+ , V_FORWARD_RULES
+ , V_INDEX_RULES
+ , V_KEY_RULES
+ , V_REPLACE_RULES
+ , V_REPLY_INDENT_RULES
+ , V_REPLY_LEADIN_RULES
+ , V_RESUB_RULES
+ , V_SAVE_RULES
+ , V_SMTP_RULES
+ , V_SORT_RULES
+ , V_STARTUP_RULES
#ifndef _WINDOWS
, V_CHAR_SET
, V_OLD_CHAR_SET
@@ -80,6 +95,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
@@ -108,6 +124,7 @@ typedef enum { V_PERSONAL_NAME = 0
, V_MARGIN
, V_STATUS_MSG_DELAY
, V_ACTIVE_MSG_INTERVAL
+ , V_SLEEP
, V_MAILCHECK
, V_MAILCHECKNONCURR
, V_MAILDROPCHECK
@@ -115,6 +132,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
@@ -230,6 +250,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
@@ -327,6 +349,7 @@ typedef enum {
F_FULL_AUTO_EXPUNGE,
F_EXPUNGE_MANUALLY,
F_AUTO_READ_MSGS,
+ F_AUTO_READ_MSGS_RULES,
F_AUTO_FCC_ONLY,
F_READ_IN_NEWSRC_ORDER,
F_SELECT_WO_CONFIRM,
@@ -342,9 +365,11 @@ 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,
+ F_AUTO_CIRCULAR_TAB,
F_DISABLE_INDEX_LOCALE_DATES,
F_SELECTED_SHOWN_BOLD,
F_QUOTE_ALL_FROMS,
@@ -388,10 +413,14 @@ 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,
F_FCC_ON_BOUNCE,
+ F_USE_DOMAIN_NAME,
F_SEND_WO_CONFIRM,
F_USE_SENDER_NOT_X,
F_BLANK_KEYMENU,
@@ -408,6 +437,7 @@ typedef enum {
F_FIRST_SEND_FILTER_DFLT,
F_ALWAYS_LAST_FLDR_DFLT,
F_TAB_TO_NEW,
+ F_MARK_FOR_GROUP,
F_MARK_FOR_CC,
F_WARN_ABOUT_NO_SUBJECT,
F_WARN_ABOUT_NO_FCC,
@@ -443,6 +473,7 @@ typedef enum {
F_VIEW_SEL_ATTACH,
F_VIEW_SEL_URL,
F_VIEW_SEL_URL_HOST,
+ F_VIEW_LONG_URL,
F_SCAN_ADDR,
F_FORCE_ARROWS,
F_PREFER_PLAIN_TEXT,
@@ -501,11 +532,13 @@ typedef enum {
F_MAILDROPS_PRESERVE_STATE,
F_EXPOSE_HIDDEN_CONFIG,
F_ALT_COMPOSE_MENU,
+ F_ALT_REPLY_MENU,
F_ALT_ROLE_MENU,
F_ALWAYS_SPELL_CHECK,
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,
@@ -713,5 +746,6 @@ typedef struct smime_stuff {
/* exported protoypes */
+#define DF_THREAD_SORT_KEY "thread"
#endif /* PITH_CONFTYPE_INCLUDED */
diff --git a/pith/detoken.c b/pith/detoken.c
index 6f0584ab..0546cf04 100644
--- a/pith/detoken.c
+++ b/pith/detoken.c
@@ -24,7 +24,7 @@ static char rcsid[] = "$Id: detoken.c 769 2007-10-24 00:15:40Z hubert@u.washingt
#include "../pith/reply.h"
#include "../pith/mailindx.h"
#include "../pith/options.h"
-
+#include "../pith/rules.h"
/*
* Hook to read signature from local file
@@ -90,6 +90,8 @@ detoken(ACTION_S *role, ENVELOPE *env, int prenewlines, int postnewlines,
if(is_sig){
/*
+ * First we check if there is a rule about signatures, if there is
+ * use it, otherwise keep going and do the following:
* If role->litsig is set, we use it;
* Else, if VAR_LITERAL_SIG is set, we use that;
* Else, if role->sig is set, we use that;
@@ -103,14 +105,25 @@ detoken(ACTION_S *role, ENVELOPE *env, int prenewlines, int postnewlines,
* there is no reason to mix them, so we don't provide support to
* do so.
*/
- if(role && role->litsig)
- literal_sig = role->litsig;
- else if(ps_global->VAR_LITERAL_SIG)
- literal_sig = ps_global->VAR_LITERAL_SIG;
- else if(role && role->sig)
- sigfile = role->sig;
- else
- sigfile = ps_global->VAR_SIGNATURE_FILE;
+ { RULE_RESULT *rule;
+ rule = get_result_rule(V_COMPOSE_RULES, FOR_COMPOSE, env);
+ if (rule){
+ sigfile = cpystr(rule->result);
+ if (rule->result)
+ fs_give((void **)&rule->result);
+ fs_give((void **)&rule);
+ }
+ }
+ if (!sigfile){
+ if(role && role->litsig)
+ literal_sig = role->litsig;
+ else if(ps_global->VAR_LITERAL_SIG)
+ literal_sig = ps_global->VAR_LITERAL_SIG;
+ else if(role && role->sig)
+ sigfile = role->sig;
+ else
+ sigfile = ps_global->VAR_SIGNATURE_FILE;
+ }
}
else if(role && role->template)
sigfile = role->template;
@@ -301,7 +314,7 @@ top:
}
}
}
- else if(pt->what_for & FOR_REPLY_INTRO)
+ else if(pt->what_for & (FOR_REPLY_INTRO | FOR_RULE))
repl = get_reply_data(env, role, pt->ctype,
subbuf, sizeof(subbuf)-1);
diff --git a/pith/filter.c b/pith/filter.c
index 692d6319..32309882 100644
--- a/pith/filter.c
+++ b/pith/filter.c
@@ -46,6 +46,7 @@ static char rcsid[] = "$Id: filter.c 1266 2009-07-14 18:39:12Z hubert@u.washingt
#include "../pith/conf.h"
#include "../pith/store.h"
#include "../pith/color.h"
+#include "../pith/osdep/color.h"
#include "../pith/escapes.h"
#include "../pith/pipe.h"
#include "../pith/status.h"
@@ -1375,14 +1376,24 @@ gf_b64_binary(FILTER_S *f, int flg)
register unsigned char t = f->t;
register int n = (int) f->n;
register int state = f->f1;
+ register unsigned char lastc;
while(GF_GETC(f, c)){
+ lastc = c;
+ if(f->f2){
+ GF_PUTC(f->next, c);
+ continue;
+ }
+
if(state){
state = 0;
if (c != '=') {
- gf_error("Illegal '=' in base64 text");
- /* NO RETURN */
+ f->f2++;
+ GF_PUTC(f->next, c);
+ q_status_message(SM_ORDER,3,3,
+ _("Warning: Illegal '=' in base64 text"));
+ continue;
}
}
@@ -1399,8 +1410,11 @@ gf_b64_binary(FILTER_S *f, int flg)
break;
default: /* impossible quantum position */
- gf_error("Internal base64 decoder error");
- /* NO RETURN */
+ f->f2++;
+ GF_PUTC(f->next, lastc);
+ q_status_message(SM_ORDER,3,3,
+ _("Warning: Internal base64 decode error"));
+ break;
}
}
}
@@ -1441,6 +1455,7 @@ gf_b64_binary(FILTER_S *f, int flg)
dprint((9, "-- gf_reset b64_binary\n"));
f->n = 0L; /* quantum position */
f->f1 = 0; /* state holder: equal seen? */
+ f->f2 = 0; /* No errors when we start */
}
}
@@ -7599,6 +7614,7 @@ html_element_comment(FILTER_S *f, char *s)
char *p, buf[MAILTMPLEN];
ADDRESS *adr;
extern char datestamp[];
+ extern char plevstamp[];
if(!strcmp(s = removing_quotes(s + 4), "ALPINE_VERSION")){
p = ALPINE_VERSION;
@@ -7612,6 +7628,9 @@ html_element_comment(FILTER_S *f, char *s)
else if(!strcmp(s, "ALPINE_COMPILE_DATE")){
p = datestamp;
}
+ else if(!strcmp(s, "ALPINE_PATCHLEVEL")){
+ p = plevstamp;
+ }
else if(!strcmp(s, "ALPINE_TODAYS_DATE")){
rfc822_date(p = buf);
}
@@ -9167,6 +9186,11 @@ typedef struct wrap_col_s {
margin_r,
indent;
char special[256];
+ long curlinenum; /* current line number */
+ int curqstrpos; /* current position in quote string */
+ long linenum; /* line number */
+ long qstrlen; /* multiples of 100 */
+ char **qstrln; /* qstrln[i] = quote string line i - 1 */
} WRAP_S;
#define WRAP_MARG_L(F) (((WRAP_S *)(F)->opt)->margin_l)
@@ -9208,6 +9232,12 @@ typedef struct wrap_col_s {
#define WRAP_COLOR(F) (((WRAP_S *)(F)->opt)->color)
#define WRAP_COLOR_SET(F) ((WRAP_COLOR(F)) && (WRAP_COLOR(F)->fg[0]))
#define WRAP_SPACES(F) (((WRAP_S *)(F)->opt)->spaces)
+#define WRAP_CURLINE(F) (((WRAP_S *)(F)->opt)->curlinenum)
+#define WRAP_CURPOS(F) (((WRAP_S *)(F)->opt)->curqstrpos)
+#define WRAP_LINENUM(F) (((WRAP_S *)(F)->opt)->linenum)
+#define WRAP_QSTRLEN(F) (((WRAP_S *)(F)->opt)->qstrlen)
+#define WRAP_QSTRN(F) (((WRAP_S *)(F)->opt)->qstrln)
+#define WRAP_QSTR(F, N) (((WRAP_S *)(F)->opt)->qstrln[(N)])
#define WRAP_PUTC(F,C,W) { \
if((F)->linep == WRAP_LASTC(F)){ \
size_t offset = (F)->linep - (F)->line; \
@@ -9285,6 +9315,8 @@ gf_wrap(FILTER_S *f, int flg)
case CCR : /* CRLF or CR in text ? */
state = BOL; /* either way, handle start */
+ WRAP_CURLINE(f)++;
+ WRAP_CURPOS(f) = 0;
if(WRAP_FLOW(f)){
/* wrapped line? */
if(f->f2 == 0 && WRAP_SPC_LEN(f) && WRAP_TRL_SPC(f)){
@@ -9378,7 +9410,11 @@ gf_wrap(FILTER_S *f, int flg)
case BOL :
if(WRAP_FLOW(f)){
- if(c == '>'){
+ if(WRAP_CURLINE(f) < WRAP_QSTRLEN(f)
+ && WRAP_QSTR(f, WRAP_CURLINE(f))
+ && WRAP_QSTR(f, WRAP_CURLINE(f))[WRAP_CURPOS(f)]
+ && WRAP_QSTR(f, WRAP_CURLINE(f))[WRAP_CURPOS(f)] == c){
+ WRAP_CURPOS(f)++;
WRAP_FL_QC(f) = 1; /* init it */
state = FL_QLEV; /* go collect it */
}
@@ -9392,7 +9428,16 @@ gf_wrap(FILTER_S *f, int flg)
}
/* quote level change implies new paragraph */
- if(WRAP_FL_QD(f)){
+ if (WRAP_CURLINE(f) > 0
+ && WRAP_CURLINE(f) < WRAP_QSTRLEN(f)
+ && (WRAP_QSTR(f, WRAP_CURLINE(f)) != NULL
+ || WRAP_QSTR(f, WRAP_CURLINE(f) - 1) != NULL)
+ && ((WRAP_QSTR(f, WRAP_CURLINE(f)) != NULL &&
+ WRAP_QSTR(f, WRAP_CURLINE(f) - 1) == NULL)
+ || (WRAP_QSTR(f, WRAP_CURLINE(f)) == NULL &&
+ WRAP_QSTR(f, WRAP_CURLINE(f) - 1) != NULL)
+ || strcmp(WRAP_QSTR(f, WRAP_CURLINE(f)),
+ WRAP_QSTR(f, WRAP_CURLINE(f) - 1)))){
WRAP_FL_QD(f) = 0;
if(WRAP_HARD(f) == 0){
WRAP_HARD(f) = 1;
@@ -9444,8 +9489,12 @@ gf_wrap(FILTER_S *f, int flg)
break;
case FL_QLEV :
- if(c == '>'){ /* another level */
- WRAP_FL_QC(f)++;
+ if(WRAP_CURLINE(f) < WRAP_QSTRLEN(f)
+ && WRAP_QSTR(f, WRAP_CURLINE(f))
+ && WRAP_QSTR(f, WRAP_CURLINE(f))[WRAP_CURPOS(f)]
+ && WRAP_QSTR(f, WRAP_CURLINE(f))[WRAP_CURPOS(f)] == c){
+ WRAP_CURPOS(f)++;
+ WRAP_FL_QC(f)++; /* another level */
}
else {
/* if EMBEDed, process it and return here */
@@ -9457,7 +9506,16 @@ gf_wrap(FILTER_S *f, int flg)
}
/* quote level change signals new paragraph */
- if(WRAP_FL_QC(f) != WRAP_FL_QD(f)){
+ if (WRAP_CURLINE(f) > 0
+ && WRAP_CURLINE(f) < WRAP_QSTRLEN(f)
+ && (WRAP_QSTR(f, WRAP_CURLINE(f))
+ || WRAP_QSTR(f, WRAP_CURLINE(f) - 1))
+ && ((WRAP_QSTR(f, WRAP_CURLINE(f)) &&
+ !WRAP_QSTR(f, WRAP_CURLINE(f) - 1))
+ || (!WRAP_QSTR(f, WRAP_CURLINE(f)) &&
+ WRAP_QSTR(f, WRAP_CURLINE(f) - 1))
+ || strcmp(WRAP_QSTR(f, WRAP_CURLINE(f)),
+ WRAP_QSTR(f, WRAP_CURLINE(f) - 1)))){
WRAP_FL_QD(f) = WRAP_FL_QC(f);
if(WRAP_HARD(f) == 0){ /* add hard newline */
WRAP_HARD(f) = 1; /* hard newline */
@@ -9514,6 +9572,13 @@ gf_wrap(FILTER_S *f, int flg)
state = FL_SIG;
break;
+ case ' ' : /* what? */
+ if (WRAP_CURLINE(f) < WRAP_QSTRLEN(f)
+ && WRAP_QSTR(f, WRAP_CURLINE(f))){
+ WRAP_SPC_LEN(f)++;
+ so_writec(' ', WRAP_SPACES(f));
+ }
+
default : /* something else */
state = DFL;
goto case_dfl; /* handle c like DFL */
@@ -9530,7 +9595,7 @@ gf_wrap(FILTER_S *f, int flg)
&eob); /* note any embedded*/
wrap_eol(f, 1, &ip, &eib,
&op, &eob); /* plunk down newline */
- wrap_bol(f, 1, 1, &ip, &eib,
+ wrap_bol(f, 1, WRAP_FLOW(f), &ip, &eib,
&op, &eob); /* write any prefix */
}
@@ -10027,7 +10092,7 @@ gf_wrap(FILTER_S *f, int flg)
wrap_flush_embed(f, &ip, &eib, &op, &eob);
wrap_eol(f, 1, &ip, &eib, &op,
&eob); /* plunk down newline */
- wrap_bol(f,1,1, &ip, &eib, &op,
+ wrap_bol(f,1,WRAP_FLOW(f), &ip, &eib, &op,
&eob); /* write any prefix */
}
@@ -10100,6 +10165,13 @@ gf_wrap(FILTER_S *f, int flg)
if(WRAP_COLOR(f))
free_color_pair(&WRAP_COLOR(f));
+ { long i;
+ for (i = 0L; i < WRAP_QSTRLEN(f); i++)
+ if (WRAP_QSTR(f,i))
+ fs_give((void **) &(WRAP_QSTR(f,i)));
+ fs_give((void **)&WRAP_QSTRN(f));
+ }
+
fs_give((void **) &f->line); /* free temp line buffer */
so_give(&WRAP_SPACES(f));
fs_give((void **) &f->opt); /* free wrap widths struct */
@@ -10450,7 +10522,8 @@ wrap_quote_insert(FILTER_S *f, unsigned char **ipp, unsigned char **eibp,
{
int j, i;
COLOR_PAIR *col = NULL;
- char *prefix = NULL, *last_prefix = NULL;
+ char *prefix = NULL, *last_prefix = NULL, *wrap_qstr = NULL;
+ int level = 0, oldj, len;
if(ps_global->VAR_QUOTE_REPLACE_STRING){
get_pair(ps_global->VAR_QUOTE_REPLACE_STRING, &prefix, &last_prefix, 0, 0);
@@ -10459,10 +10532,22 @@ wrap_quote_insert(FILTER_S *f, unsigned char **ipp, unsigned char **eibp,
last_prefix = NULL;
}
}
+
+ if(WRAP_CURLINE(f) < WRAP_QSTRLEN(f) && WRAP_QSTR(f, WRAP_CURLINE(f)))
+ wrap_qstr = cpystr(WRAP_QSTR(f, WRAP_CURLINE(f)));
+ len = wrap_qstr ? strlen(wrap_qstr) : 0;
- for(j = 0; j < WRAP_FL_QD(f); j++){
+ for (j = wrap_qstr && *wrap_qstr == ' ' ? 1 : 0;
+ j < len && isspace((unsigned char)wrap_qstr[j]); j++){
+ GF_PUTC_GLO(f->next, wrap_qstr[j]);
+ f->n += ((wrap_qstr[j] == TAB) ? (~f->n & 0x07) + 1 : 1);
+ }
+
+ for(; j < len && level < len; level++){
+ oldj = j;
+ j = next_level_quote(wrap_qstr, (char **)NULL, j, WRAP_FLOW(f));
if(WRAP_USE_CLR(f)){
- if((j % 3) == 0
+ if((level % 3) == 0
&& ps_global->VAR_QUOTE1_FORE_COLOR
&& ps_global->VAR_QUOTE1_BACK_COLOR
&& (col = new_color_pair(ps_global->VAR_QUOTE1_FORE_COLOR,
@@ -10470,7 +10555,7 @@ wrap_quote_insert(FILTER_S *f, unsigned char **ipp, unsigned char **eibp,
&& pico_is_good_colorpair(col)){
GF_COLOR_PUTC(f, col);
}
- else if((j % 3) == 1
+ else if((level % 3) == 1
&& ps_global->VAR_QUOTE2_FORE_COLOR
&& ps_global->VAR_QUOTE2_BACK_COLOR
&& (col = new_color_pair(ps_global->VAR_QUOTE2_FORE_COLOR,
@@ -10478,7 +10563,7 @@ wrap_quote_insert(FILTER_S *f, unsigned char **ipp, unsigned char **eibp,
&& pico_is_good_colorpair(col)){
GF_COLOR_PUTC(f, col);
}
- else if((j % 3) == 2
+ else if((level % 3) == 2
&& ps_global->VAR_QUOTE3_FORE_COLOR
&& ps_global->VAR_QUOTE3_BACK_COLOR
&& (col = new_color_pair(ps_global->VAR_QUOTE3_FORE_COLOR,
@@ -10492,43 +10577,60 @@ wrap_quote_insert(FILTER_S *f, unsigned char **ipp, unsigned char **eibp,
}
}
+ if (j > 1 && wrap_qstr[j-1] == ' ')
+ j -= 1;
+
if(!WRAP_LV_FLD(f)){
if(!WRAP_FOR_CMPS(f) && ps_global->VAR_QUOTE_REPLACE_STRING && prefix){
for(i = 0; prefix[i]; i++)
GF_PUTC_GLO(f->next, prefix[i]);
- f->n += utf8_width(prefix);
- }
- else if(ps_global->VAR_REPLY_STRING
- && (!strcmp(ps_global->VAR_REPLY_STRING, ">")
- || !strcmp(ps_global->VAR_REPLY_STRING, "\">\""))){
- GF_PUTC_GLO(f->next, '>');
- f->n += 1;
+ f->n += utf8_widthis(prefix);
}
else{
- GF_PUTC_GLO(f->next, '>');
- GF_PUTC_GLO(f->next, ' ');
- f->n += 2;
+ for (i = oldj; i < j; i++)
+ GF_PUTC_GLO(f->next, wrap_qstr[i]);
+ f->n += j - oldj;
}
}
else{
- GF_PUTC_GLO(f->next, '>');
- f->n += 1;
- }
+ for (i = oldj; i < j; i++)
+ GF_PUTC_GLO(f->next, wrap_qstr[i]);
+ f->n += j - oldj;
+ }
+ for (i = j; isspace((unsigned char)wrap_qstr[i]); i++);
+ if(!wrap_qstr[i]){
+ f->n += i - j;
+ for (; j < i; j++)
+ GF_PUTC_GLO(f->next, ' ');
+ }
+ else{
+ if((WRAP_LV_FLD(f)
+ || !ps_global->VAR_QUOTE_REPLACE_STRING || !prefix)
+ || !ps_global->VAR_REPLY_STRING
+ || (strcmp(ps_global->VAR_REPLY_STRING, ">")
+ && strcmp(ps_global->VAR_REPLY_STRING, "\">\""))){
+ GF_PUTC_GLO(f->next, ' ');
+ f->n += 1;
+ }
+ }
+ for (; isspace((unsigned char)wrap_qstr[j]); j++);
}
if(j && WRAP_LV_FLD(f)){
GF_PUTC_GLO(f->next, ' ');
f->n++;
}
- else if(j && last_prefix){
+ else if(j && !value_is_space(wrap_qstr) && last_prefix){
for(i = 0; last_prefix[i]; i++)
GF_PUTC_GLO(f->next, last_prefix[i]);
- f->n += utf8_width(last_prefix);
+ f->n += utf8_widthis(last_prefix);
}
if(prefix)
fs_give((void **)&prefix);
if(last_prefix)
fs_give((void **)&last_prefix);
+ if (wrap_qstr)
+ fs_give((void **)&wrap_qstr);
return 0;
}
@@ -10560,6 +10662,12 @@ gf_wrap_filter_opt(int width, int width_max, int *margin, int indent, int flags)
wrap->hdr_color = (GFW_HDRCOLOR & flags) == GFW_HDRCOLOR;
wrap->for_compose = (GFW_FORCOMPOSE & flags) == GFW_FORCOMPOSE;
wrap->handle_soft_hyphen = (GFW_SOFTHYPHEN & flags) == GFW_SOFTHYPHEN;
+ wrap->curlinenum = 0L;
+ wrap->curqstrpos = 0;
+ wrap->linenum = 0L;
+ wrap->qstrlen = 100L;
+ wrap->qstrln = (char **) fs_get(100*sizeof(char *));
+ memset(wrap->qstrln, 0, 100*sizeof(char *));
return((void *) wrap);
}
@@ -11003,7 +11111,215 @@ typedef struct _linetest_s {
} \
}
+#define ADD_QUOTE_STRING(F) { \
+ int len = tmp_20k_buf[0] ? strlen(tmp_20k_buf) + 1 : 0; \
+ FILTER_S *fltr; \
+ \
+ for(fltr = (F); fltr && fltr->f != gf_wrap; fltr = fltr->next); \
+ if (fltr){ \
+ if (WRAP_LINENUM(fltr) >= WRAP_QSTRLEN(fltr)){ \
+ fs_resize((void **)&WRAP_QSTRN(fltr), \
+ (WRAP_QSTRLEN(fltr) + 100) * sizeof(char *)); \
+ memset(WRAP_QSTRN(fltr)+WRAP_QSTRLEN(fltr), 0, \
+ 100*sizeof(char*)); \
+ WRAP_QSTRLEN(fltr) += 100L; \
+ } \
+ if (len){ \
+ WRAP_QSTR(fltr, WRAP_LINENUM(fltr)) = \
+ (char *) fs_get(len*sizeof(char)); \
+ WRAP_QSTR(fltr, WRAP_LINENUM(fltr)) = cpystr(tmp_20k_buf);\
+ } \
+ WRAP_LINENUM(fltr)++; \
+ } \
+}
+
+int end_of_line(char *line)
+{
+ int i;
+
+ for(i= 0; line && line[i]; i++){
+ if((line[i] == '\015' && line[i+1] == '\012') || line[i] == '\012')
+ break;
+ }
+ return i;
+}
+
+/* This macro is used in gf_quote_test. It receives a return code
+ from a filter. All filters that will print something must send
+ return code 0, except color_a_quote which must send return code
+ 1
+ */
+
+#define GF_ADD_QUOTED_LINE(F, line) \
+{ \
+ LT_INS_S *ins = NULL, *insp; \
+ int done; \
+ char *gline, *cline;\
+ unsigned char ch;\
+ register char *cp;\
+ register int l;\
+ \
+ for (gline = cline = line; gline && cline; ){\
+ if(cline = strchr(gline,'\012'))\
+ *cline = '\0';\
+ done = (*((LINETEST_S *) (F)->opt)->f)((F)->n++, gline, &ins,\
+ ((LINETEST_S *) (F)->opt)->local);\
+ if (done < 2){ \
+ if(done == 1)\
+ ADD_QUOTE_STRING((F));\
+ for(insp = ins, cp = gline; *cp ; ){\
+ if(insp && cp == insp->where){\
+ if(insp->len > 0){ \
+ for(l = 0; l < insp->len; l++){\
+ ch = (unsigned char) insp->text[l];\
+ GF_PUTC((F)->next, ch);\
+ }\
+ insp = insp->next;\
+ continue; \
+ } else if(insp->len < 0){ \
+ cp -= insp->len; \
+ insp = insp->next; \
+ continue; \
+ } \
+ }\
+ GF_PUTC((F)->next, *cp);\
+ cp++;\
+ }\
+ while(insp){\
+ for(l = 0; l < insp->len; l++){\
+ ch = (unsigned char) insp->text[l];\
+ GF_PUTC((F)->next, ch);\
+ }\
+ insp = insp->next;\
+ }\
+ gf_line_test_free_ins(&ins);\
+ if(cline){ \
+ *cline = '\012';\
+ gline += cline - gline + 1;\
+ }\
+ GF_PUTC((F)->next, '\015');\
+ GF_PUTC((F)->next, '\012');\
+ }\
+ }\
+}
+/* test second line of old line first */
+#define SECOND_LINE_QUOTE_TEST(line, F) \
+{\
+ *p = '\0';\
+ i = end_of_line((F)->oldline); \
+ if (((F)->oldline)[i]){\
+ i += (((F)->oldline)[i] == '\015') ? 2 : 1;\
+ line = (F)->oldline + i;\
+ i = end_of_line(line); \
+ if(line[i])\
+ line[i] = '\0'; \
+ }\
+ for (i = 0; ((F)->line) \
+ && (i < LINE_TEST_BLOCK) \
+ && (i < SIZEOF_20KBUF)\
+ && ((F)->line)[i] \
+ && (((F)->line)[i] != '\015')\
+ && (((F)->line)[i] != '\012')\
+ && (tmp_20k_buf[i] = ((F)->line)[i]); i++);\
+ tmp_20k_buf[i] = '\0';\
+ GF_ADD_QUOTED_LINE((F), line);\
+}
+
+#define FIRST_LINE_QUOTE_TEST(line, F)\
+{\
+ *p = '\0';\
+ line = (F)->line;\
+ if ((F)->oldline)\
+ fs_give((void **)&(F)->oldline);\
+ (F)->oldline = cpystr(line);\
+ i = end_of_line(line); \
+ if (line[i]){ \
+ j = (line[i] == '\015') ? 2 : 1;\
+ line[i] = '\0'; \
+ i += j; \
+ }\
+ for (j = 0; ((F)->line) \
+ && ((i + j) < LINE_TEST_BLOCK) \
+ && (j < SIZEOF_20KBUF) \
+ && ((F)->line)[i + j] \
+ && (((F)->line)[i + j] != '\015')\
+ && (((F)->line)[i + j] != '\012')\
+ && (tmp_20k_buf[j] = ((F)->line)[i + j]); j++);\
+ tmp_20k_buf[j] = '\0';\
+ GF_ADD_QUOTED_LINE((F), line);\
+}
+
+
+void
+gf_quote_test(f, flg)
+ FILTER_S *f;
+ int flg;
+{
+ register char *p = f->linep;
+ register char *eobuf = GF_LINE_TEST_EOB(f);
+ char *line = NULL;
+ int i, j;
+ GF_INIT(f, f->next);
+
+ if(flg == GF_DATA){
+ register unsigned char c;
+ register int state = f->f1;
+
+ while(GF_GETC(f, c)){
+
+ GF_LINE_TEST_ADD(f, c);
+ if(c == '\012')
+ state++;
+ if(state == 2){ /* two full lines read */
+ state = 0;
+
+ /* first process the second line of an old line */
+ if (f->oldline && f->oldline[0])
+ SECOND_LINE_QUOTE_TEST(line, f);
+
+ /* now we process the first line */
+ FIRST_LINE_QUOTE_TEST(line, f);
+
+ p = f->line;
+ }
+ }
+
+ f->f1 = state;
+ GF_END(f, f->next);
+ }
+ else if(flg == GF_EOD){
+ /* first process the second line of an old line */
+ if (f->oldline && f->oldline[0])
+ SECOND_LINE_QUOTE_TEST(line, f);
+
+ /* now we process the first line */
+ FIRST_LINE_QUOTE_TEST(line, f);
+ /* We are out of data. In this case we have processed the second
+ * line of an oldline, then the first line of a line, but we need
+ * to process the second line of the given line. We do this by
+ * processing it now!.
+ */
+ if (line[i]){
+ tmp_20k_buf[0] = '\0'; /* No next line */
+ GF_ADD_QUOTED_LINE(f, line+i);
+ }
+
+ fs_give((void **) &f->oldline); /* free old line buffer */
+ fs_give((void **) &f->line); /* free line buffer */
+ fs_give((void **) &f->opt); /* free test struct */
+ GF_FLUSH(f->next);
+ (*f->next->f)(f->next, GF_EOD);
+ }
+ else if(flg == GF_RESET){
+ f->f1 = 0; /* state */
+ f->n = 0L; /* line number */
+ f->f2 = LINE_TEST_BLOCK; /* size of alloc'd line */
+ f->line = p = (char *) fs_get(f->f2 * sizeof(char));
+ }
+
+ f->linep = p;
+}
/*
* this simple filter accumulates characters until a newline, offers it
diff --git a/pith/filter.h b/pith/filter.h
index 9916803d..e4021f23 100644
--- a/pith/filter.h
+++ b/pith/filter.h
@@ -216,6 +216,7 @@ void gf_prepend_editorial(FILTER_S *, int);
void *gf_prepend_editorial_opt(prepedtest_t, char *);
void gf_nvtnl_local(FILTER_S *, int);
void gf_local_nvtnl(FILTER_S *, int);
+void gf_quote_test(FILTER_S *, int);
void *gf_url_hilite_opt(URL_HILITE_S *, HANDLE_S **, int);
diff --git a/pith/filttype.h b/pith/filttype.h
index 21a1bec5..684299ca 100644
--- a/pith/filttype.h
+++ b/pith/filttype.h
@@ -35,6 +35,8 @@ typedef struct filter_s { /* type to hold data for filter function */
unsigned char t; /* temporary char */
char *line; /* place for temporary storage */
char *linep; /* pointer into storage space */
+ char *oldline; /* the previous line to "line" */
+ char *oldlinep; /* the previous line to "line" */
void *opt; /* optional per instance data */
void *data; /* misc internal data pointer */
unsigned char queue[1 + GF_MAXBUF];
diff --git a/pith/flag.c b/pith/flag.c
index b1bbf9c4..cf0ea39a 100644
--- a/pith/flag.c
+++ b/pith/flag.c
@@ -594,14 +594,16 @@ set_lflag(MAILSTREAM *stream, MSGNO_S *msgs, long int n, int f, int v)
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){
diff --git a/pith/imap.c b/pith/imap.c
index ea4c5b1f..f0b93c9a 100644
--- a/pith/imap.c
+++ b/pith/imap.c
@@ -967,8 +967,18 @@ imap_get_passwd(MMLOGIN_S *m_list, char *passwd, char *user, STRLIST_S *hostlist
&& !strcmp(user, l->user)
&& l->altflag == altflag){
if(passwd){
+ if(l->invalidpwd == 0){
strncpy(passwd, l->passwd, NETMAXPASSWD);
passwd[NETMAXPASSWD-1] = '\0';
+ }
+ else{
+ q_status_message(SM_ORDER | SM_DING, 3, 4,
+ "Failed to login!. Re-enter password.");
+ dprint((9, "imap_get_passwd: reseting password due to login failure.\n"));
+ dprint((10, "imap_get_passwd: Old passwd=\"%s\"\n",
+ passwd ? passwd : "?"));
+ return FALSE;
+ }
}
dprint((9, "imap_get_passwd: match\n"));
dprint((10, "imap_get_passwd: trying passwd=\"%s\"\n",
@@ -1016,6 +1026,7 @@ imap_set_passwd(MMLOGIN_S **l, char *passwd, char *user, STRLIST_S *hostlist,
(*l)->altflag = altflag;
(*l)->ok_novalidate = ok_novalidate;
(*l)->warned = warned;
+ (*l)->invalidpwd = 0; /* assume correct password for now */
if(!(*l)->user)
(*l)->user = cpystr(user);
diff --git a/pith/imap.h b/pith/imap.h
index 86a0b533..a57400b9 100644
--- a/pith/imap.h
+++ b/pith/imap.h
@@ -35,6 +35,7 @@ typedef struct _mmlogin_s {
unsigned altflag:1;
unsigned ok_novalidate:1;
unsigned warned:1;
+ unsigned invalidpwd:1; /* password is invalid, assume valid */
STRLIST_S *hosts;
struct _mmlogin_s *next;
} MMLOGIN_S;
diff --git a/pith/indxtype.h b/pith/indxtype.h
index ee01a9bb..031e5ff5 100644
--- a/pith/indxtype.h
+++ b/pith/indxtype.h
@@ -76,12 +76,15 @@ typedef enum {iNothing, iStatus, iFStatus, iIStatus, iSIStatus,
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,
iMailbox, iAddress, iInit, iCursorPos,
iDay2Digit, iMon2Digit, iYear2Digit,
+ iFolder, iFlag, iCollection, iRole, iProcid, iScreen, iPkey,
+ iNick, iAddressTo, iAddressCc, iAddressRecip, iBcc, iLcc,
+ iFfrom, iFadd,
iSTime, iKSize,
iRoleNick, iNewLine,
iHeader, iText,
@@ -103,15 +106,26 @@ typedef struct index_parse_tokens {
/* these are flags for the what_for field in INDEX_PARSE_T */
-#define FOR_NOTHING 0x00
-#define FOR_INDEX 0x01
-#define FOR_REPLY_INTRO 0x02
-#define FOR_TEMPLATE 0x04 /* or for signature */
-#define FOR_FILT 0x08
-#define DELIM_USCORE 0x10
-#define DELIM_PAREN 0x20
-#define DELIM_COLON 0x40
-
+#define FOR_NOTHING 0x00000
+#define FOR_INDEX 0x00001
+#define FOR_REPLY_INTRO 0x00002
+#define FOR_TEMPLATE 0x00004 /* or for signature */
+#define FOR_FILT 0x00008
+#define DELIM_USCORE 0x00010
+#define DELIM_PAREN 0x00020
+#define DELIM_COLON 0x00040
+#define FOR_FOLDER 0x00080 /* for rules */
+#define FOR_RULE 0x00100 /* for rules */
+#define FOR_TRIM 0x00200 /* for rules */
+#define FOR_RESUB 0x00400 /* for rules */
+#define FOR_REPLACE 0x00800 /* for rules */
+#define FOR_SORT 0x01000 /* for rules */
+#define FOR_FLAG 0x02000 /* for rules */
+#define FOR_COMPOSE 0x04000 /* for rules */
+#define FOR_THREAD 0x08000 /* for rules */
+#define FOR_STARTUP 0x10000 /* for rules */
+#define FOR_KEY 0x20000 /* for rules */
+#define FOR_SAVE 0x40000 /* for rules */
#define DEFAULT_REPLY_INTRO "default"
diff --git a/pith/init.c b/pith/init.c
index d7942dcb..69379bf8 100644
--- a/pith/init.c
+++ b/pith/init.c
@@ -408,6 +408,9 @@ get_mail_list(CONTEXT_S *list_cntxt, char *folder_base)
&& 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 --git a/pith/mailcap.c b/pith/mailcap.c
index 34dce329..44285a8a 100644
--- a/pith/mailcap.c
+++ b/pith/mailcap.c
@@ -53,6 +53,7 @@ typedef struct mcap_entry {
int needsterminal;
char *contenttype;
char *command;
+ char *nametemplate;
char *testcommand;
char *label; /* unused */
char *printcommand; /* unused */
@@ -213,6 +214,9 @@ mc_init(void)
if(mc->printcommand)
dprint((11, " printcommand: %s",
mc->printcommand ? mc->printcommand : "?"));
+ if(mc->nametemplate)
+ dprint((11, " nametemplate: %s",
+ mc->nametemplate ? mc->nametemplate : "?"));
dprint((11, " needsterminal %d\n", mc->needsterminal));
}
}
@@ -488,6 +492,11 @@ mc_build_entry(char **tokens)
dprint((9, "mailcap: printcommand=%s\n",
mc->printcommand ? mc->printcommand : "?"));
}
+ else if(arg && !strucmp(*tokens, "nametemplate")){
+ mc->nametemplate = arg;
+ dprint((9, "mailcap: nametemplate=%s\n",
+ arg ? arg : "?"));
+ }
else if(arg && !strucmp(*tokens, "compose")){
/* not used */
dprint((9, "mailcap: not using compose=%s\n",
@@ -974,3 +983,60 @@ mailcap_free(void)
mail_free_stringlist(&MailcapData.raw);
mc_free_entry(&MailcapData.head);
}
+
+char *
+mc_template(char *tmp_file, BODY *body, int chk_extension)
+{
+ MailcapEntry *mc;
+ int quoted = 0;
+ char *s, *to, *namefile = NULL;
+
+ mc = mc_get_command(body->type, body->subtype, body, chk_extension, NULL);
+ if(!mc || !mc->nametemplate || !tmp_file)
+ return tmp_file;
+
+ /* remove extension if requested for a specific extension */
+ if(mc->nametemplate && tmp_file && (s = strrchr(tmp_file, '.')) != NULL
+ && strchr(s, C_FILESEP) == NULL)
+ *s = '\0';
+
+ to = tmp_20k_buf;
+ if((s = strrchr(tmp_file, C_FILESEP)) != NULL){
+ *s++ = '\0';
+ sstrncpy(&to, tmp_file, SIZEOF_20KBUF-(to-tmp_20k_buf));
+ if(to-tmp_20k_buf < SIZEOF_20KBUF)
+ *to++ = C_FILESEP;
+ namefile = s;
+ }
+
+ for(s = mc->nametemplate; *s; s++)
+ if(quoted){
+ quoted = 0;
+ switch(*s){
+ case '%':
+ if(to-tmp_20k_buf < SIZEOF_20KBUF)
+ *to++ = '%';
+ break;
+
+ case 's':
+ sstrncpy(&to, namefile ? namefile : tmp_file, SIZEOF_20KBUF-(to-tmp_20k_buf));
+ break;
+
+ default:
+ dprint((9,
+ "Ignoring unercognized format code in nametemplate: %%%c\n", *s ));
+ break;
+ }
+ }
+ else if(*s == '%')
+ quoted = 1;
+ else if(to-tmp_20k_buf < SIZEOF_20KBUF)
+ *to++ = *s;
+
+ *to++ = '\0';
+
+ fs_give((void **)&tmp_file);
+ tmp_file = cpystr(tmp_20k_buf);
+ return tmp_file;
+}
+
diff --git a/pith/mailcap.h b/pith/mailcap.h
index 25ccd115..c9f7a256 100644
--- a/pith/mailcap.h
+++ b/pith/mailcap.h
@@ -29,6 +29,7 @@ char *mc_conf_path(char *, char *, char *, int, char *);
int mailcap_can_display(int, char *, BODY *, int);
MCAP_CMD_S *mailcap_build_command(int, char *, BODY *, char *, int *, int);
void mailcap_free(void);
+char *mc_template(char *, BODY *, int);
/* currently mandatory to implement stubs */
diff --git a/pith/mailcmd.c b/pith/mailcmd.c
index 78ba98ad..3838c65d 100644
--- a/pith/mailcmd.c
+++ b/pith/mailcmd.c
@@ -39,6 +39,7 @@ static char rcsid[] = "$Id: mailcmd.c 1142 2008-08-13 17:22:21Z hubert@u.washing
#include "../pith/ablookup.h"
#include "../pith/search.h"
#include "../pith/charconv/utf8.h"
+#include "../pith/rules.h"
#ifdef _WINDOWS
#include "../pico/osdep/mswin.h"
@@ -665,6 +666,7 @@ do_broach_folder(char *newfolder, CONTEXT_S *new_context, MAILSTREAM **streamp,
strncpy(ps_global->cur_folder, p, sizeof(ps_global->cur_folder)-1);
ps_global->cur_folder[sizeof(ps_global->cur_folder)-1] = '\0';
ps_global->context_current = ps_global->context_list;
+ setup_threading_index_style();
reset_index_format();
clear_index_cache(ps_global->mail_stream, 0);
/* MUST sort before restoring msgno! */
@@ -990,6 +992,7 @@ do_broach_folder(char *newfolder, CONTEXT_S *new_context, MAILSTREAM **streamp,
clear_index_cache(ps_global->mail_stream, 0);
reset_index_format();
+ setup_threading_index_style();
/*
* Start news reading with messages the user's marked deleted
@@ -1113,7 +1116,10 @@ do_broach_folder(char *newfolder, CONTEXT_S *new_context, MAILSTREAM **streamp,
if(!cur_already_set && mn_get_total(ps_global->msgmap) > 0L){
- perfolder_startup_rule = reset_startup_rule(ps_global->mail_stream);
+ perfolder_startup_rule = get_perfolder_startup_rule(ps_global->mail_stream,
+ V_STARTUP_RULES, newfolder);
+
+ reset_startup_rule(ps_global->mail_stream);
if(ps_global->start_entry > 0){
mn_set_cur(ps_global->msgmap, mn_get_revsort(ps_global->msgmap)
@@ -1135,124 +1141,7 @@ do_broach_folder(char *newfolder, CONTEXT_S *new_context, MAILSTREAM **streamp,
else
use_this_startup_rule = ps_global->inc_startup_rule;
- switch(use_this_startup_rule){
- /*
- * For news in incoming collection we're doing the same thing
- * for first-unseen and first-recent. In both those cases you
- * get first-unseen if FAKE_NEW is off and first-recent if
- * FAKE_NEW is on. If FAKE_NEW is on, first unseen is the
- * same as first recent because all recent msgs are unseen
- * and all unrecent msgs are seen (see pine_mail_open).
- */
- case IS_FIRST_UNSEEN:
-first_unseen:
- mn_set_cur(ps_global->msgmap,
- (sp_first_unseen(m)
- && mn_get_sort(ps_global->msgmap) == SortArrival
- && !mn_get_revsort(ps_global->msgmap)
- && !get_lflag(ps_global->mail_stream, NULL,
- sp_first_unseen(m), MN_EXLD)
- && (n = mn_raw2m(ps_global->msgmap,
- sp_first_unseen(m))))
- ? n
- : first_sorted_flagged(F_UNSEEN | F_UNDEL, m, pc,
- THREADING() ? 0 : FSF_SKIP_CHID));
- break;
-
- case IS_FIRST_RECENT:
-first_recent:
- /*
- * We could really use recent for news but this is the way
- * it has always worked, so we'll leave it. That is, if
- * the FAKE_NEW feature is on, recent and unseen are
- * equivalent, so it doesn't matter. If the feature isn't
- * on, all the undeleted messages are unseen and we start
- * at the first one. User controls with the FAKE_NEW feature.
- */
- if(IS_NEWS(ps_global->mail_stream)){
- mn_set_cur(ps_global->msgmap,
- first_sorted_flagged(F_UNSEEN|F_UNDEL, m, pc,
- THREADING() ? 0 : FSF_SKIP_CHID));
- }
- else{
- mn_set_cur(ps_global->msgmap,
- first_sorted_flagged(F_RECENT | F_UNSEEN
- | F_UNDEL,
- m, pc,
- THREADING() ? 0 : FSF_SKIP_CHID));
- }
- break;
-
- case IS_FIRST_IMPORTANT:
- mn_set_cur(ps_global->msgmap,
- first_sorted_flagged(F_FLAG|F_UNDEL, m, pc,
- THREADING() ? 0 : FSF_SKIP_CHID));
- break;
-
- case IS_FIRST_IMPORTANT_OR_UNSEEN:
-
- if(IS_NEWS(ps_global->mail_stream))
- goto first_unseen;
-
- {
- MsgNo flagged, first_unseen;
-
- flagged = first_sorted_flagged(F_FLAG|F_UNDEL, m, pc,
- THREADING() ? 0 : FSF_SKIP_CHID);
- first_unseen = (sp_first_unseen(m)
- && mn_get_sort(ps_global->msgmap) == SortArrival
- && !mn_get_revsort(ps_global->msgmap)
- && !get_lflag(ps_global->mail_stream, NULL,
- sp_first_unseen(m), MN_EXLD)
- && (n = mn_raw2m(ps_global->msgmap,
- sp_first_unseen(m))))
- ? n
- : first_sorted_flagged(F_UNSEEN|F_UNDEL, m, pc,
- THREADING() ? 0 : FSF_SKIP_CHID);
- mn_set_cur(ps_global->msgmap,
- (MsgNo) MIN((int) flagged, (int) first_unseen));
-
- }
-
- break;
-
- case IS_FIRST_IMPORTANT_OR_RECENT:
-
- if(IS_NEWS(ps_global->mail_stream))
- goto first_recent;
-
- {
- MsgNo flagged, first_recent;
-
- flagged = first_sorted_flagged(F_FLAG|F_UNDEL, m, pc,
- THREADING() ? 0 : FSF_SKIP_CHID);
- first_recent = first_sorted_flagged(F_RECENT | F_UNSEEN
- | F_UNDEL,
- m, pc,
- THREADING() ? 0 : FSF_SKIP_CHID);
- mn_set_cur(ps_global->msgmap,
- (MsgNo) MIN((int) flagged, (int) first_recent));
- }
-
- break;
-
- case IS_FIRST:
- mn_set_cur(ps_global->msgmap,
- first_sorted_flagged(F_UNDEL, m, pc,
- THREADING() ? 0 : FSF_SKIP_CHID));
- break;
-
- case IS_LAST:
- mn_set_cur(ps_global->msgmap,
- first_sorted_flagged(F_UNDEL, m, pc,
- FSF_LAST | (THREADING() ? 0 : FSF_SKIP_CHID)));
- break;
-
- default:
- panic("Unexpected incoming startup case");
- break;
-
- }
+ find_startup_position(use_this_startup_rule, m, pc);
}
else if(IS_NEWS(ps_global->mail_stream)){
/*
@@ -1430,9 +1319,11 @@ expunge_and_close(MAILSTREAM *stream, char **final_msg, long unsigned int flags)
/* Save read messages? */
if(VAR_READ_MESSAGE_FOLDER && VAR_READ_MESSAGE_FOLDER[0]
&& sp_flagged(stream, SP_INBOX)
- && (seen_not_del = count_flagged(stream, F_SEEN | F_UNDEL))){
+ && (F_ON(F_AUTO_READ_MSGS_RULES, ps_global) ||
+ (seen_not_del = count_flagged(stream, F_SEEN | F_UNDEL)))){
if(F_ON(F_AUTO_READ_MSGS,ps_global)
+ || F_ON(F_AUTO_READ_MSGS_RULES, ps_global)
|| (pith_opt_read_msg_prompt
&& (*pith_opt_read_msg_prompt)(seen_not_del, VAR_READ_MESSAGE_FOLDER)))
/* move inbox's read messages */
@@ -1703,6 +1594,9 @@ move_read_msgs(MAILSTREAM *stream, char *dstfldr, char *buf, size_t buflen, long
char *bufp = NULL;
MESSAGECACHE *mc;
+ if (F_ON(F_AUTO_READ_MSGS_RULES, ps_global))
+ return move_read_msgs_using_rules(stream, dstfldr, buf);
+
if(!is_absolute_path(dstfldr)
&& !(save_context = default_save_context(ps_global->context_list)))
save_context = ps_global->context_list;
@@ -1742,8 +1636,9 @@ move_read_msgs(MAILSTREAM *stream, char *dstfldr, char *buf, size_t buflen, long
snprintf(buf, buflen, "Moving %s read message%s to \"%s\"",
comatose(searched), plural(searched), dstfldr);
we_cancel = busy_cue(buf, NULL, 0);
- if(save(ps_global, stream, save_context, dstfldr, msgmap,
- SV_DELETE | SV_FIX_DELS | SV_INBOXWOCNTXT) == searched)
+ ps_global->exiting = 1;
+ if((save(ps_global, stream, save_context, dstfldr, msgmap,
+ SV_DELETE | SV_FIX_DELS | SV_INBOXWOCNTXT) == searched))
strncpy(bufp = buf + 1, "Moved", MIN(5,buflen)); /* change Moving to Moved */
buf[buflen-1] = '\0';
@@ -1781,7 +1676,9 @@ move_read_incoming(MAILSTREAM *stream, CONTEXT_S *context, char *folder,
&& ((context_isambig(folder)
&& folder_is_nick(folder, FOLDERS(context), 0))
|| folder_index(folder, context, FI_FOLDER) > 0)
- && (seen_undel = count_flagged(stream, F_SEEN | F_UNDEL))){
+ && ((seen_undel = count_flagged(stream, F_SEEN | F_UNDEL))
+ || (F_ON(F_AUTO_READ_MSGS,ps_global) &&
+ F_ON(F_AUTO_READ_MSGS_RULES, ps_global)))){
for(; f && *archive; archive++){
char *p;
@@ -2739,3 +2636,295 @@ get_uname(char *mailbox, char *target, int len)
return(*target ? target : NULL);
}
+
+char *
+move_read_msgs_using_rules(MAILSTREAM *stream, char *dstfldr, char *buf)
+{
+ CONTEXT_S *save_context = NULL;
+ char **folder_to_save = NULL;
+ int num, we_cancel;
+ long i, j, success;
+ MSGNO_S *msgmap = NULL;
+ unsigned long nmsgs = 0L, stream_nmsgs;
+
+ if(!is_absolute_path(dstfldr)
+ && !(save_context = default_save_context(ps_global->context_list)))
+ save_context = ps_global->context_list;
+
+ folder_to_save = (char **)fs_get((stream->nmsgs + 1)*sizeof(char *));
+ folder_to_save[0] = NULL;
+ mn_init(&msgmap, stream->nmsgs);
+ stream_nmsgs = stream->nmsgs;
+ for (i = 1L; i <= stream_nmsgs ; i++){
+ set_lflag(stream, msgmap, i, MN_SLCT, 0);
+ folder_to_save[i] = get_lflag(stream, NULL, i, MN_EXLD)
+ ? NULL : get_folder_to_save(stream, i, dstfldr);
+ }
+ for (i = 1L; i <= stream_nmsgs; i++){
+ num = 0;
+ if (folder_to_save[i]){
+ mn_init(&msgmap, stream_nmsgs);
+ for (j = i; j <= stream_nmsgs ; j++){
+ if (folder_to_save[j]){
+ if (!strcmp(folder_to_save[i], folder_to_save[j])){
+ set_lflag(stream, msgmap, j, MN_SLCT, 1);
+ num++;
+ if (j != i)
+ fs_give((void **)&folder_to_save[j]);
+ }
+ }
+ }
+ pseudo_selected(stream, msgmap);
+ sprintf(buf, "Moving %s read message%s to \"%.45s\"",
+ comatose(num), plural(num), folder_to_save[i]);
+ we_cancel = busy_cue(buf, NULL, 1);
+ ps_global->exiting = 1;
+ if(success = save(ps_global, stream,save_context, folder_to_save[i],
+ msgmap, SV_DELETE | SV_FIX_DELS))
+ nmsgs += success;
+ if(we_cancel)
+ cancel_busy_cue(success ? 0 : -1);
+ for (j = i; j <= stream_nmsgs ; j++)
+ set_lflag(stream, msgmap, j, MN_SLCT, 0);
+ fs_give((void **)&folder_to_save[i]);
+ mn_give(&msgmap);
+ }
+ }
+ ps_global->exiting = 0; /* useful if we call from aggregate operations */
+ sprintf(buf, "Moved automatically %s message%s",
+ comatose(nmsgs), plural(nmsgs));
+ if (folder_to_save)
+ fs_give((void **)folder_to_save);
+ rule_curpos = 0L;
+ return buf;
+}
+
+char *
+get_folder_to_save(MAILSTREAM *stream, long i, char *dstfldr)
+{
+ MESSAGECACHE *mc = NULL;
+ RULE_RESULT *rule;
+ MSGNO_S *msgmap = NULL;
+ char *folder_to_save = NULL, *save_folder = NULL;
+ int n;
+ long msgno;
+
+ /* The plan is as follows: Select each message of the folder. We
+ * need to set the cursor correctly so that iFlag gets the value
+ * correctly too, otherwise iFlag will get the value of the position
+ * of the cursor. After that we need to look for a rule that applies
+ * to the message and get the saving folder. If we get a saving folder,
+ * and we used the _FLAG_ token, use that folder, if no
+ * _FLAG_ token was used, move only if seen and not deleted, to the
+ * folder specified in the saving rule. If we did not get a saving
+ * folder from the rule, just save in the default folder.
+ */
+ mn_init(&msgmap, stream->nmsgs);
+ rule_curpos = i;
+ msgno = mn_m2raw(msgmap, i);
+ if (msgno > 0L){
+ mc = mail_elt(stream, msgno);
+ rule = (RULE_RESULT *)
+ get_result_rule(V_SAVE_RULES, FOR_SAVE, mc->private.msg.env);
+ if (rule){
+ folder_to_save = cpystr(rule->result);
+ n = rule->number;
+ fs_give((void **)&rule->result);
+ fs_give((void **)&rule);
+ }
+ }
+
+ if (folder_to_save && *folder_to_save){
+ RULELIST *list = get_rulelist_from_code(V_SAVE_RULES,
+ ps_global->rule_list);
+ RULE_S *prule = get_rule(list, n);
+ if (condition_contains_token(prule->condition, "_FLAG_")
+ || (mc->valid && mc->seen && !mc->deleted)
+ || (!mc->valid && mc->searched))
+ save_folder = cpystr(folder_to_save);
+ else
+ save_folder = NULL;
+ }
+ else
+ if (!mc || (mc->seen && !mc->deleted))
+ save_folder = cpystr(dstfldr);
+ mn_give(&msgmap);
+ rule_curpos = 0L;
+ return save_folder;
+}
+
+unsigned long
+rules_cursor_pos(MAILSTREAM *stream)
+{
+ MSGNO_S *msgmap = sp_msgmap(stream);
+ return rule_curpos != 0L ? rule_curpos : mn_m2raw(msgmap,mn_get_cur(msgmap));
+}
+
+void
+setup_threading_index_style(void)
+{
+ RULE_RESULT *rule;
+ NAMEVAL_S *v;
+ int i;
+
+ rule = get_result_rule(V_THREAD_INDEX_STYLE_RULES, FOR_THREAD, NULL);
+ if (rule || ps_global->VAR_THREAD_INDEX_STYLE){
+ for(i = 0; v = thread_index_styles(i); i++)
+ if(!strucmp(rule ? rule->result : ps_global->VAR_THREAD_INDEX_STYLE,
+ rule ? (v ? v->name : "" ) : S_OR_L(v))){
+ ps_global->thread_index_style = v->value;
+ break;
+ }
+ if (rule){
+ if (rule->result)
+ fs_give((void **)&rule->result);
+ fs_give((void **)&rule);
+ }
+ }
+}
+
+unsigned
+get_perfolder_startup_rule(MAILSTREAM *stream, int rule_type, char *folder)
+{
+ unsigned startup_rule;
+ char *rule_result;
+
+ startup_rule = reset_startup_rule(stream);
+ rule_result = get_rule_result(FOR_STARTUP, folder, rule_type);
+ if (rule_result && *rule_result){
+ int i;
+ NAMEVAL_S *v;
+
+ for(i = 0; v = incoming_startup_rules(i); i++)
+ if(!strucmp(rule_result, v->name)){
+ startup_rule = v->value;
+ break;
+ }
+ fs_give((void **)&rule_result);
+ }
+ return startup_rule;
+}
+
+void
+find_startup_position(int rule, MAILSTREAM *m, long pc)
+{
+ long n;
+ switch(rule){
+ /*
+ * For news in incoming collection we're doing the same thing
+ * for first-unseen and first-recent. In both those cases you
+ * get first-unseen if FAKE_NEW is off and first-recent if
+ * FAKE_NEW is on. If FAKE_NEW is on, first unseen is the
+ * same as first recent because all recent msgs are unseen
+ * and all unrecent msgs are seen (see pine_mail_open).
+ */
+ case IS_FIRST_UNSEEN:
+first_unseen:
+ mn_set_cur(ps_global->msgmap,
+ (sp_first_unseen(m)
+ && mn_get_sort(ps_global->msgmap) == SortArrival
+ && !mn_get_revsort(ps_global->msgmap)
+ && !get_lflag(ps_global->mail_stream, NULL,
+ sp_first_unseen(m), MN_EXLD)
+ && (n = mn_raw2m(ps_global->msgmap,
+ sp_first_unseen(m))))
+ ? n
+ : first_sorted_flagged(F_UNSEEN | F_UNDEL, m, pc,
+ THREADING() ? 0 : FSF_SKIP_CHID));
+ break;
+
+ case IS_FIRST_RECENT:
+first_recent:
+ /*
+ * We could really use recent for news but this is the way
+ * it has always worked, so we'll leave it. That is, if
+ * the FAKE_NEW feature is on, recent and unseen are
+ * equivalent, so it doesn't matter. If the feature isn't
+ * on, all the undeleted messages are unseen and we start
+ * at the first one. User controls with the FAKE_NEW feature.
+ */
+ if(IS_NEWS(ps_global->mail_stream)){
+ mn_set_cur(ps_global->msgmap,
+ first_sorted_flagged(F_UNSEEN|F_UNDEL, m, pc,
+ THREADING() ? 0 : FSF_SKIP_CHID));
+ }
+ else{
+ mn_set_cur(ps_global->msgmap,
+ first_sorted_flagged(F_RECENT | F_UNSEEN
+ | F_UNDEL,
+ m, pc,
+ THREADING() ? 0 : FSF_SKIP_CHID));
+ }
+ break;
+
+ case IS_FIRST_IMPORTANT:
+ mn_set_cur(ps_global->msgmap,
+ first_sorted_flagged(F_FLAG|F_UNDEL, m, pc,
+ THREADING() ? 0 : FSF_SKIP_CHID));
+ break;
+
+ case IS_FIRST_IMPORTANT_OR_UNSEEN:
+
+ if(IS_NEWS(ps_global->mail_stream))
+ goto first_unseen;
+
+ {
+ MsgNo flagged, first_unseen;
+
+ flagged = first_sorted_flagged(F_FLAG|F_UNDEL, m, pc,
+ THREADING() ? 0 : FSF_SKIP_CHID);
+ first_unseen = (sp_first_unseen(m)
+ && mn_get_sort(ps_global->msgmap) == SortArrival
+ && !mn_get_revsort(ps_global->msgmap)
+ && !get_lflag(ps_global->mail_stream, NULL,
+ sp_first_unseen(m), MN_EXLD)
+ && (n = mn_raw2m(ps_global->msgmap,
+ sp_first_unseen(m))))
+ ? n
+ : first_sorted_flagged(F_UNSEEN|F_UNDEL, m, pc,
+ THREADING() ? 0 : FSF_SKIP_CHID);
+ mn_set_cur(ps_global->msgmap,
+ (MsgNo) MIN((int) flagged, (int) first_unseen));
+
+ }
+
+ break;
+
+ case IS_FIRST_IMPORTANT_OR_RECENT:
+
+ if(IS_NEWS(ps_global->mail_stream))
+ goto first_recent;
+
+ {
+ MsgNo flagged, first_recent;
+
+ flagged = first_sorted_flagged(F_FLAG|F_UNDEL, m, pc,
+ THREADING() ? 0 : FSF_SKIP_CHID);
+ first_recent = first_sorted_flagged(F_RECENT | F_UNSEEN
+ | F_UNDEL,
+ m, pc,
+ THREADING() ? 0 : FSF_SKIP_CHID);
+ mn_set_cur(ps_global->msgmap,
+ (MsgNo) MIN((int) flagged, (int) first_recent));
+ }
+
+ break;
+
+ case IS_FIRST:
+ mn_set_cur(ps_global->msgmap,
+ first_sorted_flagged(F_UNDEL, m, pc,
+ THREADING() ? 0 : FSF_SKIP_CHID));
+ break;
+
+ case IS_LAST:
+ mn_set_cur(ps_global->msgmap,
+ first_sorted_flagged(F_UNDEL, m, pc,
+ FSF_LAST | (THREADING() ? 0 : FSF_SKIP_CHID)));
+ break;
+
+ default:
+ panic("Unexpected incoming startup case");
+ break;
+
+ }
+}
diff --git a/pith/mailcmd.h b/pith/mailcmd.h
index 9e99c6f3..d590b7c1 100644
--- a/pith/mailcmd.h
+++ b/pith/mailcmd.h
@@ -42,6 +42,8 @@
#define DB_FROMTAB 0x02 /* opening because of TAB command */
#define DB_INBOXWOCNTXT 0x04 /* interpret inbox as one true inbox */
+static MAILSTREAM *saved_stream;
+static unsigned long rule_curpos = 0L;
/*
* generic "is aggregate message command?" test
@@ -63,7 +65,13 @@ int do_broach_folder(char *, CONTEXT_S *, MAILSTREAM **, unsigned long);
void expunge_and_close(MAILSTREAM *, char **, unsigned long);
void agg_select_all(MAILSTREAM *, MSGNO_S *, long *, int);
char *move_read_msgs(MAILSTREAM *, char *, char *, size_t, long);
+char *move_read_msgs_using_rules (MAILSTREAM *, char *, char *);
+unsigned get_perfolder_startup_rule (MAILSTREAM *, int, char *);
+void setup_threading_index_style (void);
+void find_startup_position (int, MAILSTREAM *, long);
+char *get_folder_to_save (MAILSTREAM *, long, char *);
char *move_read_incoming(MAILSTREAM *, CONTEXT_S *, char *, char **, char *, size_t);
+unsigned long rules_cursor_pos (MAILSTREAM *);
void cross_delete_crossposts(MAILSTREAM *);
long zoom_index(struct pine *, MAILSTREAM *, MSGNO_S *, int);
int unzoom_index(struct pine *, MAILSTREAM *, MSGNO_S *);
diff --git a/pith/mailindx.c b/pith/mailindx.c
index 09cdc2c8..12fbb5d2 100644
--- a/pith/mailindx.c
+++ b/pith/mailindx.c
@@ -17,6 +17,7 @@ static char rcsid[] = "$Id: mailindx.c 1266 2009-07-14 18:39:12Z hubert@u.washin
#include "../pith/headers.h"
#include "../pith/mailindx.h"
+#include "../pith/pineelt.h"
#include "../pith/mailview.h"
#include "../pith/flag.h"
#include "../pith/icache.h"
@@ -40,6 +41,7 @@ static char rcsid[] = "$Id: mailindx.c 1266 2009-07-14 18:39:12Z hubert@u.washin
#include "../pith/send.h"
#include "../pith/options.h"
#include "../pith/ablookup.h"
+#include "../pith/rules.h"
#ifdef _WINDOWS
#include "../pico/osdep/mswin.h"
#endif
@@ -104,7 +106,6 @@ char *copy_format_str(int, int, char *, int);
void set_print_format(IELEM_S *, int, int);
void set_ielem_widths_in_field(IFIELD_S *);
-
#define BIGWIDTH 2047
@@ -228,6 +229,7 @@ init_index_format(char *format, INDEX_COL_S **answer)
case iSTime:
case iKSize:
case iSize:
+ case iSizeThread:
case iPrioAlpha:
(*answer)[column].req_width = 7;
break;
@@ -374,6 +376,13 @@ reset_index_format(void)
PAT_STATE pstate;
PAT_S *pat;
int we_set_it = 0;
+ char *rule;
+
+ if(rule = get_rule_result(FOR_INDEX, ps_global->cur_folder, V_INDEX_RULES)){
+ init_index_format(rule, &ps_global->index_disp_format);
+ fs_give((void **)&rule);
+ return;
+ }
if(ps_global->mail_stream && nonempty_patterns(rflags, &pstate)){
for(pat = first_pattern(&pstate); pat; pat = next_pattern(&pstate)){
@@ -447,14 +456,15 @@ free_hdrtok(HEADER_TOK_S **hdrtok)
static INDEX_PARSE_T itokens[] = {
{"STATUS", iStatus, FOR_INDEX},
{"MSGNO", iMessNo, FOR_INDEX},
- {"DATE", iDate, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"DATE", iDate, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
{"FROMORTO", iFromTo, FOR_INDEX},
{"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},
+ {"SUBJECT", iSubject, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE|FOR_TRIM},
{"FULLSTATUS", iFStatus, FOR_INDEX},
{"IMAPSTATUS", iIStatus, FOR_INDEX},
{"SHORTIMAPSTATUS", iSIStatus, FOR_INDEX},
@@ -463,55 +473,58 @@ static INDEX_PARSE_T itokens[] = {
{"SUBJECTTEXT", iSubjectText, FOR_INDEX},
{"SUBJKEYTEXT", iSubjKeyText, FOR_INDEX},
{"SUBJKEYINITTEXT", iSubjKeyInitText, FOR_INDEX},
- {"OPENINGTEXT", iOpeningText, FOR_INDEX},
- {"OPENINGTEXTNQ", iOpeningTextNQ, FOR_INDEX},
- {"KEY", iKey, FOR_INDEX},
- {"KEYINIT", iKeyInit, FOR_INDEX},
+ {"OPENINGTEXT", iOpeningText, FOR_INDEX|FOR_RULE|FOR_SAVE|FOR_TRIM},
+ {"OPENINGTEXTNQ", iOpeningTextNQ, FOR_INDEX|FOR_RULE|FOR_SAVE|FOR_TRIM},
+ {"KEY", iKey, FOR_INDEX|FOR_RULE|FOR_SAVE|FOR_COMPOSE},
+ {"KEYINIT", iKeyInit, FOR_INDEX|FOR_RULE|FOR_SAVE|FOR_COMPOSE},
{"DESCRIPSIZE", iDescripSize, FOR_INDEX},
{"ATT", iAtt, FOR_INDEX},
{"SCORE", iScore, FOR_INDEX},
{"PRIORITY", iPrio, FOR_INDEX},
{"PRIORITYALPHA", iPrioAlpha, FOR_INDEX},
- {"PRIORITY!", iPrioBang, FOR_INDEX},
- {"LONGDATE", iLDate, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
- {"SHORTDATE1", iS1Date, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
- {"SHORTDATE2", iS2Date, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
- {"SHORTDATE3", iS3Date, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
- {"SHORTDATE4", iS4Date, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
- {"DATEISO", iDateIso, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
- {"SHORTDATEISO", iDateIsoS, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
- {"SMARTDATE", iSDate, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
- {"SMARTTIME", iSTime, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
- {"SMARTDATEISO", iSDateIso, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
- {"SMARTDATESHORTISO",iSDateIsoS, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
- {"SMARTDATES1", iSDateS1, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
- {"SMARTDATES2", iSDateS2, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
- {"SMARTDATES3", iSDateS3, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
- {"SMARTDATES4", iSDateS4, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
- {"SMARTDATETIME", iSDateTime, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
- {"SMARTDATETIMEISO",iSDateTimeIso, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
- {"SMARTDATETIMESHORTISO",iSDateTimeIsoS,FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
- {"SMARTDATETIMES1", iSDateTimeS1, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
- {"SMARTDATETIMES2", iSDateTimeS2, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
- {"SMARTDATETIMES3", iSDateTimeS3, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
- {"SMARTDATETIMES4", iSDateTimeS4, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
- {"SMARTDATETIME24", iSDateTime24, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
- {"SMARTDATETIMEISO24", iSDateTimeIso24,FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
- {"SMARTDATETIMESHORTISO24",iSDateTimeIsoS24,FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
- {"SMARTDATETIMES124", iSDateTimeS124, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
- {"SMARTDATETIMES224", iSDateTimeS224, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
- {"SMARTDATETIMES324", iSDateTimeS324, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
- {"SMARTDATETIMES424", iSDateTimeS424, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
- {"TIME24", iTime24, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
- {"TIME12", iTime12, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
- {"TIMEZONE", iTimezone, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
- {"MONTHABBREV", iMonAbb, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
- {"DAYOFWEEKABBREV", iDayOfWeekAbb, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
- {"DAYOFWEEK", iDayOfWeek, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
- {"FROM", iFrom, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
- {"TO", iTo, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
- {"SENDER", iSender, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
- {"CC", iCc, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"PRIORITY!", iPrioBang, FOR_INDEX},
+ {"LONGDATE", iLDate, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+ {"SHORTDATE1", iS1Date, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+ {"SHORTDATE2", iS2Date, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+ {"SHORTDATE3", iS3Date, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+ {"SHORTDATE4", iS4Date, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+ {"DATEISO", iDateIso, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+ {"SHORTDATEISO", iDateIsoS, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+ {"SMARTDATE", iSDate, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+ {"SMARTTIME", iSTime, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+ {"SMARTDATEISO", iSDateIso, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+ {"SMARTDATESHORTISO",iSDateIsoS, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+ {"SMARTDATES1", iSDateS1, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+ {"SMARTDATES2", iSDateS2, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+ {"SMARTDATES3", iSDateS3, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+ {"SMARTDATES4", iSDateS4, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+ {"SMARTDATETIME", iSDateTime, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+ {"SMARTDATETIMEISO",iSDateTimeIso, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+ {"SMARTDATETIMESHORTISO",iSDateTimeIsoS,FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+ {"SMARTDATETIMES1", iSDateTimeS1, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+ {"SMARTDATETIMES2", iSDateTimeS2, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+ {"SMARTDATETIMES3", iSDateTimeS3, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+ {"SMARTDATETIMES4", iSDateTimeS4, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+ {"SMARTDATETIME24", iSDateTime24, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+ {"SMARTDATETIMEISO24", iSDateTimeIso24,FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+ {"SMARTDATETIMESHORTISO24",iSDateTimeIsoS24,FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+ {"SMARTDATETIMES124", iSDateTimeS124, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+ {"SMARTDATETIMES224", iSDateTimeS224, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+ {"SMARTDATETIMES324", iSDateTimeS324, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+ {"SMARTDATETIMES424", iSDateTimeS424, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+ {"TIME24", iTime24, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+ {"TIME12", iTime12, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+ {"TIMEZONE", iTimezone, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+ {"MONTHABBREV", iMonAbb, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+ {"DAYOFWEEKABBREV", iDayOfWeekAbb, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+ {"DAYOFWEEK", iDayOfWeek, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+ {"FROM", iFrom, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_COMPOSE},
+ {"TO", iTo, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_COMPOSE},
+ {"SENDER", iSender, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+ {"CC", iCc, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_SAVE|FOR_SAVE},
+ {"ADDRESSTO", iAddressTo, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+ {"ADDRESSCC", iAddressCc, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+ {"ADDRESSRECIPS", iAddressRecip, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
{"RECIPS", iRecips, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
{"NEWS", iNews, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
{"TOANDNEWS", iToAndNews, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
@@ -520,56 +533,68 @@ static INDEX_PARSE_T itokens[] = {
{"NEWSANDRECIPS", iNewsAndRecips, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
{"MSGID", iMsgID, FOR_REPLY_INTRO|FOR_TEMPLATE},
{"CURNEWS", iCurNews, FOR_REPLY_INTRO|FOR_TEMPLATE},
- {"DAYDATE", iRDate, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
- {"PREFDATE", iPrefDate, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
- {"PREFTIME", iPrefTime, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
- {"PREFDATETIME", iPrefDateTime, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
- {"DAY", iDay, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
- {"DAYORDINAL", iDayOrdinal, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
- {"DAY2DIGIT", iDay2Digit, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
- {"MONTHLONG", iMonLong, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
- {"MONTH", iMon, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
- {"MONTH2DIGIT", iMon2Digit, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
- {"YEAR", iYear, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
- {"YEAR2DIGIT", iYear2Digit, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
- {"ADDRESS", iAddress, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"DAYDATE", iRDate, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+ {"PREFDATE", iPrefDate, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+ {"PREFTIME", iPrefTime, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+ {"PREFDATETIME", iPrefDateTime, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+ {"DAY", iDay, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+ {"DAYORDINAL", iDayOrdinal, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+ {"DAY2DIGIT", iDay2Digit, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+ {"MONTHLONG", iMonLong, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+ {"MONTH", iMon, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+ {"MONTH2DIGIT", iMon2Digit, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+ {"YEAR", iYear, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+ {"YEAR2DIGIT", iYear2Digit, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE},
+ {"ADDRESS", iAddress, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE},
{"MAILBOX", iMailbox, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
{"ROLENICK", iRoleNick, FOR_REPLY_INTRO|FOR_TEMPLATE},
{"INIT", iInit, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
- {"CURDATE", iCurDate, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
- {"CURDATEISO", iCurDateIso, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
- {"CURDATEISOS", iCurDateIsoS, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
- {"CURTIME24", iCurTime24, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
- {"CURTIME12", iCurTime12, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
- {"CURDAY", iCurDay, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
- {"CURDAY2DIGIT", iCurDay2Digit, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
- {"CURDAYOFWEEK", iCurDayOfWeek, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
+ {"CURDATE", iCurDate, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE},
+ {"CURDATEISO", iCurDateIso, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE},
+ {"CURDATEISOS", iCurDateIsoS, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE},
+ {"CURTIME24", iCurTime24, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE},
+ {"CURTIME12", iCurTime12, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE},
+ {"CURDAY", iCurDay, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE},
+ {"CURDAY2DIGIT", iCurDay2Digit, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE},
+ {"CURDAYOFWEEK", iCurDayOfWeek, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE},
{"CURDAYOFWEEKABBREV", iCurDayOfWeekAbb,
- FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
- {"CURMONTH", iCurMon, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
- {"CURMONTH2DIGIT", iCurMon2Digit, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
- {"CURMONTHLONG", iCurMonLong, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
- {"CURMONTHABBREV", iCurMonAbb, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
- {"CURYEAR", iCurYear, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
- {"CURYEAR2DIGIT", iCurYear2Digit, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
- {"CURPREFDATE", iCurPrefDate, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
- {"CURPREFTIME", iCurPrefTime, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
+ FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE},
+ {"CURMONTH", iCurMon, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE},
+ {"CURMONTH2DIGIT", iCurMon2Digit, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE},
+ {"CURMONTHLONG", iCurMonLong, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE},
+ {"CURMONTHABBREV", iCurMonAbb, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE},
+ {"CURYEAR", iCurYear, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE},
+ {"CURYEAR2DIGIT", iCurYear2Digit, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE},
+ {"CURPREFDATE", iCurPrefDate, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE},
+ {"CURPREFTIME", iCurPrefTime, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE},
{"CURPREFDATETIME", iCurPrefDateTime,
- FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
- {"LASTMONTH", iLstMon, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
- {"LASTMONTH2DIGIT", iLstMon2Digit, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
- {"LASTMONTHLONG", iLstMonLong, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
- {"LASTMONTHABBREV", iLstMonAbb, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
- {"LASTMONTHYEAR", iLstMonYear, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
+ FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE},
+ {"LASTMONTH", iLstMon, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE},
+ {"LASTMONTH2DIGIT", iLstMon2Digit, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE},
+ {"LASTMONTHLONG", iLstMonLong, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE},
+ {"LASTMONTHABBREV", iLstMonAbb, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE},
+ {"LASTMONTHYEAR", iLstMonYear, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE},
{"LASTMONTHYEAR2DIGIT", iLstMonYear2Digit,
- FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
- {"LASTYEAR", iLstYear, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
- {"LASTYEAR2DIGIT", iLstYear2Digit, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
+ FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE},
+ {"LASTYEAR", iLstYear, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE},
+ {"LASTYEAR2DIGIT", iLstYear2Digit, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT|FOR_RULE|FOR_SAVE},
{"HEADER", iHeader, FOR_INDEX},
{"TEXT", iText, FOR_INDEX},
{"ARROW", iArrow, FOR_INDEX},
{"NEWLINE", iNewLine, FOR_REPLY_INTRO},
{"CURSORPOS", iCursorPos, FOR_TEMPLATE},
+ {"NICK", iNick, FOR_RULE|FOR_SAVE},
+ {"FOLDER", iFolder, FOR_RULE|FOR_SAVE|FOR_FOLDER},
+ {"ROLE", iRole, FOR_RULE|FOR_RESUB|FOR_TRIM|FOR_TEMPLATE},
+ {"PROCID", iProcid, FOR_RULE|FOR_RESUB|FOR_FLAG|FOR_COMPOSE|FOR_TRIM|FOR_TEMPLATE},
+ {"PKEY", iPkey, FOR_RULE|FOR_KEY},
+ {"SCREEN", iScreen, FOR_RULE|FOR_KEY},
+ {"FLAG", iFlag, FOR_RULE|FOR_SAVE|FOR_FLAG},
+ {"COLLECTION", iCollection, FOR_RULE|FOR_SAVE|FOR_COMPOSE|FOR_FOLDER},
+ {"BCC", iBcc, FOR_COMPOSE|FOR_RULE},
+ {"LCC", iLcc, FOR_COMPOSE|FOR_RULE},
+ {"FORWARDFROM", iFfrom, FOR_COMPOSE|FOR_RULE},
+ {"FORWARDADDRESS", iFadd, FOR_COMPOSE|FOR_RULE},
{NULL, iNothing, FOR_NOTHING}
};
@@ -943,7 +968,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 +1161,7 @@ setup_index_header_widths(MAILSTREAM *stream)
case iTime12:
case iSize:
case iKSize:
+ case iSizeThread:
cdesc->actual_length = 7;
cdesc->adjustment = Right;
break;
@@ -1229,7 +1255,7 @@ setup_index_header_widths(MAILSTREAM *stream)
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 +1638,12 @@ build_header_work(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap,
/* 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 +2055,10 @@ format_index_index_line(INDEXDATA_S *idata)
*/
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++)
@@ -2075,10 +2100,15 @@ format_index_index_line(INDEXDATA_S *idata)
if(to_us == ' ')
to_us = '+';
+ if(to_us == '+'
+ && F_ON(F_MARK_FOR_GROUP,ps_global) &&
+ (addr->next || addr != fetch_to(idata)))
+ to_us = '.';
+
break;
}
- if(to_us != '+' && resent_to_us(idata)){
+ if(to_us == ' ' && resent_to_us(idata)){
ice->to_us = 1;
if(to_us == ' ')
to_us = '+';
@@ -2132,7 +2162,7 @@ format_index_index_line(INDEXDATA_S *idata)
ielem->color = new_color_pair(VAR_IND_IMP_FORE_COLOR, VAR_IND_IMP_BACK_COLOR);
}
}
- else if(str[0] == '+' || str[0] == '-'){
+ else if(str[0] == '+' || str[0] == '-' || str[0] == '.'){
if(VAR_IND_PLUS_FORE_COLOR && VAR_IND_PLUS_BACK_COLOR){
ielem = ifield->ielem;
ielem->freecolor = 1;
@@ -2188,6 +2218,12 @@ format_index_index_line(INDEXDATA_S *idata)
for(addr = fetch_to(idata); addr; addr = addr->next)
if(address_is_us(addr, ps_global)){
to_us = '+';
+
+ if(to_us == '+'
+ && F_ON(F_MARK_FOR_GROUP,ps_global) &&
+ (addr->next || addr != fetch_to(idata)))
+ to_us = '.';
+
break;
}
@@ -2283,7 +2319,7 @@ format_index_index_line(INDEXDATA_S *idata)
if(pico_usingcolor()){
- if(str[0] == '+' || str[0] == '-'){
+ if(str[0] == '+' || str[0] == '-' || str[0] == '.'){
if(start == 0
&& VAR_IND_PLUS_FORE_COLOR
&& VAR_IND_PLUS_BACK_COLOR){
@@ -2465,6 +2501,24 @@ format_index_index_line(INDEXDATA_S *idata)
from_str(cdesc->ctype, idata, str, sizeof(str), ice);
break;
+ case iAddressTo:
+ case iAddressCc:
+ case iAddressRecip:
+ {ENVELOPE *env;
+ int we_clear;
+ env = rules_fetchenvelope(idata, &we_clear);
+ sprintf(str, "%-*.*s", ifield->width, ifield->width,
+ detoken_src((cdesc->ctype == iAddressTo
+ ? "_ADDRESSTO_"
+ : (cdesc->ctype == iAddressCc
+ ? "_ADRESSCC_"
+ : "_ADRESSRECIPS_")), FOR_INDEX,
+ env, NULL, NULL, NULL));
+ if(we_clear)
+ mail_free_envelope(&env);
+ }
+ break;
+
case iTo:
if(((field = ((addr = fetch_to(idata))
? "To"
@@ -2531,7 +2585,30 @@ format_index_index_line(INDEXDATA_S *idata)
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);
@@ -2785,7 +2862,6 @@ format_index_index_line(INDEXDATA_S *idata)
if(first_text){
strncpy(str, first_text, BIGWIDTH);
str[BIGWIDTH] = '\0';
- fs_give((void **) &first_text);
}
}
@@ -3107,7 +3183,7 @@ format_thread_index_line(INDEXDATA_S *idata)
tice->linecolor = new_color_pair(VAR_IND_IMP_FORE_COLOR,
VAR_IND_IMP_BACK_COLOR);
}
- else if((to_us == '+' || to_us == '-')
+ else if((to_us == '+' || to_us == '-' || to_us == '.')
&& VAR_IND_PLUS_FORE_COLOR && VAR_IND_PLUS_BACK_COLOR){
ielem = ifield->ielem;
ielem->freecolor = 1;
@@ -3710,6 +3786,26 @@ fetch_firsttext(INDEXDATA_S *idata, int delete_quotes)
gf_io_t pc;
long partial_fetch_len = 0L;
SEARCHSET *ss, **sset;
+ MESSAGECACHE *mc;
+ PINELT_S *pelt;
+
+ /* we cache the result we get from this function, so that we do not have to
+ * refetch the text in case there is a change. We could cache in the envelope
+ * but c-client does not have a special field for that, nor we want to use the
+ * sparep pointer, since there could be other uses for sparep later, and even
+ * if we add a pointer to the ENVELOPE structure, we would be caching the same
+ * text twice (one in a private pointer, and the new pointer) and that would
+ * not make sense. Instead we will use an elt for this
+ */
+
+ if((mc = mail_elt(idata->stream, idata->rawno))
+ && ((pelt = (PINELT_S *) mc->sparep) == NULL)){
+ pelt = (PINELT_S *) fs_get(sizeof(PINELT_S));
+ memset(pelt, 0, sizeof(PINELT_S));
+ }
+
+ if(pelt && pelt->firsttext != NULL)
+ return(pelt->firsttext);
try_again:
@@ -3803,7 +3899,17 @@ try_again:
if(p > buf){
size_t l;
-
+ ENVELOPE *env;
+ char *rule_result;
+
+ if(rule_result = find_value((delete_quotes
+ ? "_OPENINGTEXTNQ_" : "_OPENINGTEXT_"),
+ buf, PROCESS_SP, idata, 4)){
+ collspaces(rule_result);
+ strncpy(buf, rule_result, sizeof(buf));
+ buf[sizeof(buf) - 1] = '\0';
+ fs_give((void **) &rule_result);
+ }
l = strlen(buf);
l += 100;
firsttext = fs_get((l+1) * sizeof(char));
@@ -3827,6 +3933,8 @@ try_again:
goto try_again;
}
}
+ if(mc && pelt)
+ pelt->firsttext = firsttext;
}
}
}
@@ -5267,10 +5375,10 @@ subj_str(INDEXDATA_S *idata, char *str, size_t strsize, SubjKW kwtype, int openi
{
char *subject, *origsubj, *origstr, *rawsubj, *sptr = NULL;
char *p, *border, *q = NULL, *free_subj = NULL;
- char *sp;
+ char *sp, *rule_result;
size_t len;
int width = -1;
- int depth = 0, mult = 2;
+ int depth = 0, mult = 2, collapsed, i, we_clear = 0;
int save;
int do_subj = 0, truncated_tree = 0;
PINETHRD_S *thd, *thdorig;
@@ -5324,7 +5432,13 @@ subj_str(INDEXDATA_S *idata, char *str, size_t strsize, SubjKW kwtype, int openi
* origsubj is the original subject but it has been decoded. We need
* to free it at the end of this routine.
*/
-
+ if (rule_result = find_value("_SUBJECT_", origsubj, PROCESS_SP, idata, 4)){
+ if(origsubj)
+ fs_give((void **)&origsubj);
+ we_clear++;
+ origsubj = cpystr(rule_result);
+ fs_give((void **)&rule_result);
+ }
/*
* prepend_keyword will put the keyword stuff before the subject
@@ -5412,10 +5526,8 @@ subj_str(INDEXDATA_S *idata, char *str, size_t strsize, SubjKW kwtype, int openi
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
@@ -5765,6 +5877,9 @@ subj_str(INDEXDATA_S *idata, char *str, size_t strsize, SubjKW kwtype, int openi
if(free_subj)
fs_give((void **) &free_subj);
+
+ if (we_clear && origsubj)
+ fs_give((void **)&origsubj);
}
@@ -6043,11 +6158,8 @@ from_str(IndexColType ctype, INDEXDATA_S *idata, char *str, size_t strsize, ICE_
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)
@@ -6133,16 +6245,33 @@ from_str(IndexColType ctype, INDEXDATA_S *idata, char *str, size_t strsize, ICE_
? "To"
: (addr = fetch_cc(idata))
? "Cc"
- : NULL))
- && set_index_addr(idata, field, addr, "To: ",
- strsize-1, fptr))
- break;
+ : NULL))){
+ char *rule_result;
+ rule_result = find_value("_FROM_", NULL, 0, idata, 1);
+ if (!rule_result)
+ set_index_addr(idata, field, addr, "To: ",
+ strsize-1, fptr);
+ else{
+ sprintf(str, "%-*.*s", strsize-1, strsize-1,
+ rule_result);
+ fs_give((void **)&rule_result);
+ }
+ break;
+ }
if(ctype == iFromTo &&
(newsgroups = fetch_newsgroups(idata)) &&
*newsgroups){
- snprintf(fptr, strsize, "To: %-*.*s", strsize-1-4, strsize-1-4,
- newsgroups);
+ char *rule_result;
+ rule_result = find_value("_FROM_", NULL, 0, idata, 1);
+ if (!rule_result)
+ sprintf(str, "To: %-*.*s", strsize-1-4,
+ strsize-1-4, newsgroups);
+ else{
+ sprintf(str, "%-*.*s", strsize-1, strsize-1,
+ rule_result);
+ fs_give((void **)&rule_result);
+ }
break;
}
@@ -6155,7 +6284,15 @@ from_str(IndexColType ctype, INDEXDATA_S *idata, char *str, size_t strsize, ICE_
break;
case iFrom:
- set_index_addr(idata, "From", fetch_from(idata), NULL, strsize-1, fptr);
+ { char *rule_result;
+ rule_result = find_value("_FROM_", NULL, 0, idata, 4);
+ if (!rule_result)
+ set_index_addr(idata, "From", fetch_from(idata), NULL, strsize-1, fptr);
+ else{
+ sprintf(str, "%-*.*s", strsize-1, strsize-1, rule_result);
+ fs_give((void **)&rule_result);
+ }
+ }
break;
case iAddress:
@@ -6452,3 +6589,64 @@ set_print_format(IELEM_S *ielem, int width, int leftadj)
}
}
}
+
+void
+setup_threading_display_style(void)
+{
+ RULE_RESULT *rule;
+ NAMEVAL_S *v;
+ int i;
+
+ rule = get_result_rule(V_THREAD_DISP_STYLE_RULES, FOR_THREAD, NULL);
+ if (rule || ps_global->VAR_THREAD_DISP_STYLE){
+ for(i = 0; v = thread_disp_styles(i); i++)
+ if(!strucmp(rule ? rule->result : ps_global->VAR_THREAD_DISP_STYLE,
+ rule ? (v ? v->name : "" ) : S_OR_L(v))){
+ ps_global->thread_disp_style = v->value;
+ break;
+ }
+ if (rule){
+ if (rule->result)
+ fs_give((void **)&rule->result);
+ fs_give((void **)&rule);
+ }
+ }
+}
+
+char *
+find_value(char *token, char *use_this, int flag, INDEXDATA_S *idata, int nfcn)
+{
+ int n = 0, i, rule_context, we_clear;
+ char *rule_result = NULL, **list;
+ ENVELOPE *env;
+ RULELIST *rule;
+ RULE_S *prule;
+
+ env = rules_fetchenvelope(idata, &we_clear);
+ if(env && env->sparep)
+ fs_give((void **)&env->sparep);
+ if(we_clear)
+ mail_free_envelope(&env);
+ if(rule = get_rulelist_from_code(V_REPLACE_RULES, ps_global->rule_list)){
+ list = functions_for_token(token);
+ while(rule_result == NULL && (prule = get_rule(rule,n++))){
+ rule_context = 0;
+ if (prule->action->token && !strcmp(prule->action->token, token)){
+ for (i = 0; i < nfcn; i++)
+ if(list[i+1] && !strcmp(prule->action->function, list[i+1]))
+ rule_context |= context_for_function(list[i+1]);
+ if (rule_context){
+ env = rules_fetchenvelope(idata, &we_clear);
+ if(use_this)
+ env->sparep = get_sparep_for_rule(use_this, flag);
+ rule_result = process_rule(prule, rule_context, env);
+ if(env->sparep)
+ free_sparep_for_rule(&env->sparep);
+ if(we_clear)
+ mail_free_envelope(&env);
+ }
+ }
+ }
+ }
+ return rule_result;
+}
diff --git a/pith/mailindx.h b/pith/mailindx.h
index 8eb23033..fdea552c 100644
--- a/pith/mailindx.h
+++ b/pith/mailindx.h
@@ -30,6 +30,9 @@ extern void (*setup_header_widths)(MAILSTREAM *);
/* exported prototypes */
+SortOrder translate (char *, int);
+char *find_value (char *, char *, int, INDEXDATA_S *, int);
+void setup_threading_display_style (void);
int msgline_hidden(MAILSTREAM *, MSGNO_S *, long, int);
void adjust_cur_to_visible(MAILSTREAM *, MSGNO_S *);
unsigned long line_hash(char *);
diff --git a/pith/mailview.c b/pith/mailview.c
index 40728aab..5df0dbdb 100644
--- a/pith/mailview.c
+++ b/pith/mailview.c
@@ -52,7 +52,11 @@ static char rcsid[] = "$Id: mailview.c 1266 2009-07-14 18:39:12Z hubert@u.washin
#include "../pith/escapes.h"
#include "../pith/keyword.h"
#include "../pith/smime.h"
-
+#include "../pith/osdep/color.h"
+#include "../pico/osdep/color.h"
+#include "../pico/estruct.h"
+#include "../pico/pico.h"
+#include "../pico/efunc.h"
#define FBUF_LEN (50)
@@ -282,9 +286,17 @@ format_body(long int msgno, BODY *body, HANDLE_S **handlesp, HEADER_S *hp, int f
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));
+ gf_link_filter(gf_quote_test, gf_line_test_opt(color_signature, &is_in_sig));
}
if((flgs & FM_DISPLAY)
@@ -292,8 +304,10 @@ format_body(long int msgno, BODY *body, HANDLE_S **handlesp, HEADER_S *hp, int f
&& pico_usingcolor()
&& ps_global->VAR_QUOTE1_FORE_COLOR
&& ps_global->VAR_QUOTE1_BACK_COLOR){
- gf_link_filter(gf_line_test, gf_line_test_opt(color_a_quote, NULL));
+ gf_link_filter(gf_quote_test, gf_line_test_opt(color_a_quote, NULL));
}
+ else
+ gf_link_filter(gf_quote_test,gf_line_test_opt(select_quote, NULL));
if(!(flgs & FM_NOWRAP)){
wrapflags = (flgs & FM_DISPLAY) ? (GFW_HANDLES|GFW_SOFTHYPHEN) : GFW_NONE;
@@ -1098,27 +1112,88 @@ int
color_signature(long int linenum, char *line, LT_INS_S **ins, void *is_in_sig)
{
struct variable *vars = ps_global->vars;
- int *in_sig_block;
+ int *in_sig_block, i, j,same_qstr = 0, plb;
COLOR_PAIR *col = NULL;
+ static char GLine[NSTRING] = {'\0'};
+ static char PLine[NSTRING] = {'\0'};
+ static char PPLine[NSTRING] = {'\0'};
+ char NLine[NSTRING] = {'\0'};
+ char rqstr[NSTRING] = {'\0'};
+ char *p, *q;
+ static char *buf, buf2[NSTRING] = {'\0'};
+ QSTRING_S *qs;
+ static int qstrlen = 0;
if(is_in_sig == NULL)
return 0;
+ if (linenum > 0){
+ strncpy(PLine, GLine, sizeof(PLine));
+ PLine[sizeof(PLine)-1] = '\0';
+ }
+
+ if(p = strchr(tmp_20k_buf, '\015')) *p = '\0';
+ strncpy(NLine, tmp_20k_buf, sizeof(NLine));
+ NLine[sizeof(NLine) - 1] = '\0';
+ if (p) *p = '\015';
+
+ strncpy(GLine, line, sizeof(GLine));
+ GLine[sizeof(GLine) - 1] = '\0';
+
+ ps_global->list_qstr = default_qstr(ps_global->prefix && *ps_global->prefix
+ ? (void *) ps_global->prefix : (void *) ">", 0);
+ plb = line_isblank(ps_global->list_qstr, PLine, GLine, PPLine, NSTRING);
+ qs = do_quote_match(ps_global->list_qstr, GLine, NLine, PLine, rqstr, NSTRING, plb);
+ if(linenum > 0)
+ strncpy(PPLine, PLine, NSTRING);
+ strncpy(buf2, rqstr, NSTRING);
+ i = buf2 && buf2[0] ? strlen(buf2) : 0;
+ free_qs(&qs);
+
+ /* determine if buf and buf2 are the same quote string */
+ if (!struncmp(buf, buf2, qstrlen)){
+ for (j = qstrlen; buf2[j] && isspace((unsigned char)buf2[j]); j++);
+ if (!buf2[j] || buf2[j] == '|' || (buf2[j] == '*' && buf2[j+1] != '>'))
+ same_qstr++;
+ }
+
in_sig_block = (int *) is_in_sig;
- if(!strcmp(line, SIGDASHES))
- *in_sig_block = START_SIG_BLOCK;
- else if(*line == '\0')
+ if (*in_sig_block != OUT_SIG_BLOCK){
+ if (line && *line && (strlen(line) >= qstrlen) && same_qstr)
+ line += qstrlen;
+ else if (strlen(line) < qstrlen)
+ line += i;
+ else if (!same_qstr)
+ *in_sig_block = OUT_SIG_BLOCK;
+ }
+ else
+ line += i;
+
+ if(!strcmp(line, SIGDASHES) || !strcmp(line, "--")){
+ *in_sig_block = START_SIG_BLOCK;
+ buf = (char *) fs_get((i + 1)*sizeof(char));
+ buf = cpystr(buf2);
+ qstrlen = i;
+ }
+ else if(*line == '\0'){
/*
* Suggested by Eduardo: allow for a blank line right after
* the sigdashes.
*/
*in_sig_block = (*in_sig_block == START_SIG_BLOCK)
? IN_SIG_BLOCK : OUT_SIG_BLOCK;
+ }
else
*in_sig_block = (*in_sig_block != OUT_SIG_BLOCK)
? IN_SIG_BLOCK : OUT_SIG_BLOCK;
+ if (*in_sig_block == OUT_SIG_BLOCK){
+ qstrlen = 0; /* reset back in case there's another paragraph */
+ if (buf)
+ fs_give((void **)&buf);
+ }
+
if(*in_sig_block != OUT_SIG_BLOCK
&& VAR_SIGNATURE_FORE_COLOR && VAR_SIGNATURE_BACK_COLOR
&& (col = new_color_pair(VAR_SIGNATURE_FORE_COLOR,
@@ -1482,18 +1557,78 @@ color_headers(long int linenum, char *line, LT_INS_S **ins, void *local)
return(0);
}
+int
+incomplete_url(char *up, int n, int delim)
+{
+ char *line, *line2;
+ int rv = 0, len;
+
+ if(*(up + n) != '\0')
+ return 0;
+
+ if(delim > 0)
+ return 1;
+
+ if(F_ON(F_VIEW_LONG_URL, ps_global)){
+ line = up;
+ if(!strncmp(line, "http://", 7))
+ line += 7;
+ else if(!strncmp(line, "https://", 8))
+ line += 8;
+ if(strchr(line, '/') != NULL && (line = strrchr(line, '/')) != NULL){
+ line++;
+ line2 = strrchr(line, '.');
+ rv = (strpbrk(line,"+#?=&") != NULL)
+ || (!line2 || line-line2 > 4);
+ }
+ }
+ return rv;
+}
+
int
url_hilite(long int linenum, char *line, LT_INS_S **ins, void *local)
{
register char *lp, *up = NULL, *urlp = NULL,
*weburlp = NULL, *mailurlp = NULL;
- int n, n1, n2, n3, l;
+ char *use_this_line, c, *begin_line, *end_line;
+ static int scannextline, delim = -1;
+ int n, n1, n2, n3, l, len;
+ int we_clear = 0, newhandle = 1, tie_off = 0;
char buf[256], color[256];
HANDLE_S *h;
URL_HILITE_S *uh;
- for(lp = line; ; lp = up + n){
+ uh = (URL_HILITE_S *) local;
+ if((uh && uh->handlesp && ((h = *(uh->handlesp)) == NULL) || h->key == 0) ||
+ (!line || !*line) || linenum == 0)
+ scannextline = 0; /* initialize scannextline */
+
+ if(scannextline != 0){
+ up = rfc1738_scan(line, &n1);
+
+ /* if we found a url in the current line, but it is not at the beginning of
+ * the next line, or if there is no url in this line, we check if the url
+ * in the previous line continues in this line.
+ */
+
+ if(line != up){
+ if(*uh->handlesp == NULL)
+ h = new_handle(uh->handlesp);
+ for(h = *uh->handlesp; h->next; h = h->next); /* get last handle */
+ len = h->h.url.path ? strlen(h->h.url.path) : 0;
+ use_this_line = (char *) fs_get((len + strlen(line) + 1)*sizeof(char));
+ sprintf(use_this_line,"%s%s", (h->h.url.path ? h->h.url.path : ""), line);
+ we_clear++;
+ newhandle = 0;
+ }
+ else
+ use_this_line = line;
+ }
+ else
+ use_this_line = line;
+
+ for(lp = use_this_line; ; lp = up + n){
/* scan for all of them so we can choose the first */
if(F_ON(F_VIEW_SEL_URL,ps_global))
urlp = rfc1738_scan(lp, &n1);
@@ -1503,6 +1638,10 @@ url_hilite(long int linenum, char *line, LT_INS_S **ins, void *local)
mailurlp = mail_addr_scan(lp, &n3);
if(urlp || weburlp || mailurlp){
+ if(scannextline == 0){
+ newhandle++;
+ delim = -1;
+ }
up = urlp ? urlp :
weburlp ? weburlp : mailurlp;
if(up == urlp && weburlp && weburlp < up)
@@ -1511,7 +1650,16 @@ url_hilite(long int linenum, char *line, LT_INS_S **ins, void *local)
up = mailurlp;
if(up == urlp){
+ if(delim < 0)
+ delim = up > use_this_line && *(up - 1) == '<';
n = n1;
+ if(incomplete_url(up,n, delim))
+ scannextline++;
+ else{
+ if(scannextline)
+ tie_off++;
+ scannextline = 0;
+ }
weburlp = mailurlp = NULL;
}
else if(up == weburlp){
@@ -1528,36 +1676,58 @@ url_hilite(long int linenum, char *line, LT_INS_S **ins, void *local)
uh = (URL_HILITE_S *) local;
- h = new_handle(uh->handlesp);
- h->type = URL;
- h->h.url.path = (char *) fs_get((n + 10) * sizeof(char));
- snprintf(h->h.url.path, n+10, "%s%.*s",
+ if(tie_off){
+ tie_off = 0; /* do only once */
+ begin_line = line;
+ end_line = line + n - strlen(h->h.url.path);
+ fs_give((void **)&h->h.url.path);
+ c = *(use_this_line + n);
+ *(use_this_line+n) = '\0';
+ h->h.url.path = cpystr(use_this_line);
+ *(use_this_line+n) = c;
+ }
+ else{
+ if(newhandle){
+ h = new_handle(uh->handlesp);
+ h->type = URL;
+ }
+ begin_line = newhandle ? (we_clear ? line + strlen(line) - strlen(up)
+ : up) : line;
+ end_line = newhandle ? begin_line + n : line + strlen(line);
+ if(scannextline && h->h.url.path)
+ fs_give((void **)&h->h.url.path);
+ h->h.url.path = (char *) fs_get((n + 10) * sizeof(char));
+ snprintf(h->h.url.path, n+10, "%s%.*s",
weburlp ? "http://" : (mailurlp ? "mailto:" : ""), n, up);
- h->h.url.path[n+10-1] = '\0';
+ h->h.url.path[n+10-1] = '\0';
+ }
if(handle_start_color(color, sizeof(color), &l, uh->hdr_color))
- ins = gf_line_test_new_ins(ins, up, color, l);
+ ins = gf_line_test_new_ins(ins, begin_line, color, l);
else if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global))
- ins = gf_line_test_new_ins(ins, up, url_embed(TAG_BOLDON), 2);
+ ins = gf_line_test_new_ins(ins, begin_line, url_embed(TAG_BOLDON), 2);
buf[0] = TAG_EMBED;
buf[1] = TAG_HANDLE;
snprintf(&buf[3], sizeof(buf)-3, "%d", h->key);
buf[sizeof(buf)-1] = '\0';
buf[2] = strlen(&buf[3]);
- ins = gf_line_test_new_ins(ins, up, buf, (int) buf[2] + 3);
+ ins = gf_line_test_new_ins(ins, begin_line, buf, (int) buf[2] + 3);
/* in case it was the current selection */
- ins = gf_line_test_new_ins(ins, up + n, url_embed(TAG_INVOFF), 2);
+ ins = gf_line_test_new_ins(ins, end_line, url_embed(TAG_INVOFF), 2);
if(scroll_handle_end_color(color, sizeof(color), &l, uh->hdr_color))
- ins = gf_line_test_new_ins(ins, up + n, color, l);
+ ins = gf_line_test_new_ins(ins, end_line, color, l);
else
- ins = gf_line_test_new_ins(ins, up + n, url_embed(TAG_BOLDOFF), 2);
+ ins = gf_line_test_new_ins(ins, end_line, url_embed(TAG_BOLDOFF), 2);
urlp = weburlp = mailurlp = NULL;
}
+ if(we_clear)
+ fs_give((void **)&use_this_line);
+
return(0);
}
@@ -1678,6 +1848,77 @@ pad_to_right_edge(long int linenum, char *line, LT_INS_S **ins, void *local)
}
+/* This filter gives a quote string of a line. It sends its reply back to the
+ calling filter in the tmp_20k_buf variable. This filter replies with
+ the full quote string including tailing spaces if any. It is the
+ responsibility of the calling filter to figure out if thos spaces are
+ useful for that filter or if they should be removed before doing any
+ useful work. For example, color_a_quote does not require the trailing
+ spaces, but gf_wrap does.
+ */
+int
+select_quote(long linenum, char *line, LT_INS_S **ins, void *local)
+{
+ int i, plb, *code;
+ char rqstr[NSTRING] = {'\0'}, buf[NSTRING] = {'\0'};
+ char GLine[NSTRING] = {'\0'}, PLine[NSTRING] = {'\0'};
+ char PPLine[NSTRING] = {'\0'}, NLine[NSTRING] = {'\0'};
+ static char GLine1[NSTRING] = {'\0'};
+ static char PLine1[NSTRING] = {'\0'};
+ static char PPLine1[NSTRING] = {'\0'};
+ static char GLine2[NSTRING] = {'\0'};
+ static char PLine2[NSTRING] = {'\0'};
+ static char PPLine2[NSTRING] = {'\0'};
+ QSTRING_S *qs;
+ int buflen = NSTRING < SIZEOF_20KBUF ? NSTRING - 1: SIZEOF_20KBUF - 1;
+ int who, raw;
+
+ code = (int *)local;
+ who = code ? (*code & COLORAQUO) : 0; /* may I ask who is calling? */
+ raw = code ? (*code & RAWSTRING) : 0; /* return raw string */
+ strncpy(GLine, (who ? GLine1 : GLine2), buflen);
+ strncpy(PLine, (who ? PLine1 : PLine2), buflen);
+ strncpy(PPLine, (who ? PPLine1 : PPLine2), buflen);
+
+ if (linenum > 0)
+ strncpy(PLine, GLine, buflen);
+
+ strncpy(NLine, tmp_20k_buf, buflen);
+
+ if (line)
+ strncpy(GLine, line, buflen);
+ else
+ GLine[0] = '\0';
+
+ ps_global->list_qstr = default_qstr(ps_global->prefix && *ps_global->prefix
+ ? (void *) ps_global->prefix : (void *) ">", 0);
+ plb = line_isblank(ps_global->list_qstr, PLine, GLine, PPLine, NSTRING);
+
+ qs = do_quote_match(ps_global->list_qstr, GLine, NLine, PLine, rqstr, NSTRING, plb);
+ if (raw)
+ strncpy(buf, rqstr, NSTRING);
+ else
+ flatten_qstring(qs, buf, NSTRING);
+ if(qs)
+ record_quote_string(qs);
+ free_qs(&qs);
+
+ /* do not paint an extra level for a line with a >From string at the
+ * begining of it
+ */
+ if (buf[0]){
+ i = strlen(buf);
+ if (strlen(line) >= i + 6 && !strncmp(line+i-1,">From ", 6))
+ buf[i - 1] = '\0';
+ }
+ strncpy(tmp_20k_buf, buf, buflen);
+ if (linenum > 0)
+ strncpy((who ? PPLine1 : PPLine2), PLine, buflen);
+ strncpy((who ? GLine1 : GLine2), GLine, buflen);
+ strncpy((who ? PLine1 : PLine2), PLine, buflen);
+ return 1;
+}
+
#define UES_LEN 12
#define UES_MAX 32
@@ -2503,6 +2744,190 @@ hdr_color(char *fieldname, char *value, SPEC_COLOR_S *speccolor)
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 && !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 --git a/pith/mailview.h b/pith/mailview.h
index a89f3f95..5d2fe171 100644
--- a/pith/mailview.h
+++ b/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 *, HANDLE_S **, HEADER_S *, int, int, gf_io_t);
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
@@ -142,6 +157,7 @@ COLOR_PAIR *hdr_color(char *, char *, SPEC_COLOR_S *);
char *display_parameters(PARAMETER *);
char *pine_fetch_header(MAILSTREAM *, long, char *, char **, long);
int color_signature(long, char *, LT_INS_S **, void *);
+int select_quote(long, char *, LT_INS_S **, void *);
int scroll_handle_start_color(char *, size_t, int *);
int scroll_handle_end_color(char *, size_t, int *, int);
int width_at_this_position(unsigned char *, unsigned long);
diff --git a/pith/makefile.wnt b/pith/makefile.wnt
index d9316dba..b9464cd4 100644
--- a/pith/makefile.wnt
+++ b/pith/makefile.wnt
@@ -44,7 +44,7 @@ HFILES= ../include/system.h ../include/general.h \
init.h keyword.h ldap.h list.h mailcap.h mailcmd.h mailindx.h maillist.h \
mailpart.h mailview.h margin.h mimedesc.h mimetype.h msgno.h newmail.h news.h \
options.h pattern.h pineelt.h pipe.h readfile.h remote.h remtype.h repltype.h reply.h \
- rfc2231.h save.h savetype.h search.h send.h sequence.h signal.h sort.h sorttype.h \
+ rfc2231.h rules.h save.h savetype.h search.h send.h sequence.h signal.h sort.h sorttype.h \
state.h status.h store.h stream.h string.h strlst.h takeaddr.h tempfile.h text.h \
thread.h url.h user.h util.h
@@ -53,7 +53,7 @@ OFILES= ablookup.obj abdlc.obj addrbook.obj addrstring.obj adrbklib.obj bldaddr.
filter.obj flag.obj folder.obj handle.obj help.obj helptext.obj hist.obj icache.obj imap.obj init.obj \
keyword.obj ldap.obj list.obj mailcap.obj mailcmd.obj mailindx.obj maillist.obj mailview.obj \
margin.obj mimedesc.obj mimetype.obj msgno.obj newmail.obj news.obj pattern.obj pipe.obj \
- readfile.obj remote.obj reply.obj rfc2231.obj save.obj search.obj sequence.obj send.obj sort.obj state.obj \
+ readfile.obj remote.obj reply.obj rfc2231.obj rules.obj save.obj search.obj sequence.obj send.obj sort.obj state.obj \
status.obj store.obj stream.obj string.obj strlst.obj takeaddr.obj tempfile.obj text.obj \
thread.obj adjtime.obj url.obj util.obj
diff --git a/pith/msgno.c b/pith/msgno.c
index 465a42e0..e72ee0d3 100644
--- a/pith/msgno.c
+++ b/pith/msgno.c
@@ -933,6 +933,9 @@ free_pine_elt(void **sparep)
if((*peltp)->pthrd)
fs_give((void **) &(*peltp)->pthrd);
+ if((*peltp)->firsttext)
+ fs_give((void **) &(*peltp)->firsttext);
+
if((*peltp)->ice)
free_ice(&(*peltp)->ice);
diff --git a/pith/osdep/color.c b/pith/osdep/color.c
index faf3c675..cad5502e 100644
--- a/pith/osdep/color.c
+++ b/pith/osdep/color.c
@@ -31,7 +31,7 @@ static char rcsid[] = "$Id: color.c 761 2007-10-23 22:35:18Z hubert@u.washington
#include <system.h>
#include "./color.h"
-
+#include "./collate.h"
/*
@@ -91,3 +91,1257 @@ pico_set_colorp(COLOR_PAIR *col, int flags)
{
return(pico_set_colors(col ? col->fg : NULL, col ? col->bg : NULL, flags));
}
+
+
+ /*
+ * Extended Justification support also does not belong here
+ * but otherwise webpine will not build, so we move everything
+ * here. Hopefully this will be the permanent place for these
+ * routines. These routines used to be in pico/word.c
+ */
+#define NSTRING 256
+#include "../../include/general.h"
+
+/* Support of indentation of paragraphs */
+#define is_indent_char(c) (((c) == '.' || (c) == '}' || (c) == RPAREN || \
+ (c) == '*' || (c) == '+' || is_a_digit(c) || \
+ ISspace(c) || (c) == '-' || \
+ (c) == ']') ? 1 : 0)
+#define allowed_after_digit(c,word,k) ((((c) == '.' && \
+ allowed_after_period(next((word),(k)))) ||\
+ (c) == RPAREN || (c) == '}' || (c) == ']' ||\
+ ISspace(c) || is_a_digit(c) || \
+ ((c) == '-' ) && \
+ allowed_after_dash(next((word),(k)))) \
+ ? 1 : 0)
+#define allowed_after_period(c) (((c) == RPAREN || (c) == '}' || (c) == ']' ||\
+ ISspace(c) || (c) == '-' || \
+ is_a_digit(c)) ? 1 : 0)
+#define allowed_after_parenth(c) (ISspace(c) ? 1 : 0)
+#define allowed_after_space(c) (ISspace(c) ? 1 : 0)
+#define allowed_after_braces(c) (ISspace(c) ? 1 : 0)
+#define allowed_after_star(c) ((ISspace(c) || (c) == RPAREN ||\
+ (c) == ']' || (c) == '}') ? 1 : 0)
+#define allowed_after_dash(c) ((ISspace(c) || is_a_digit(c)) ? 1 : 0)
+#define EOLchar(c) (((c) == '.' || (c) == ':' || (c) == '?' ||\
+ (c) == '!') ? 1 : 0)
+
+
+/* Extended justification support */
+#define is_cquote(c) ((c) == '>' || (c) == '|' || (c) == ']' || (c) == ':')
+#define is_cword(c) ((((c) >= 'a') && ((c) <= 'z')) || \
+ (((c) >= 'A') && ((c) <= 'Z')) || \
+ (((c) >= '0') && ((c) <= '9')) || \
+ ((c) == ' ') || ((c) == '?') || \
+ ((c) == '@') || ((c) == '.') || \
+ ((c) == '!') || ((c) == '\'') || \
+ ((c) == ',') || ((c) == '\"') ? 1 : 0)
+#define isaquote(c) ((c) == '\"' || (c) == '\'')
+#define is8bit(c) ((((int) (c)) & 0x80) ? 1 : 0)
+#define iscontrol(c) (iscntrl(((int) (c)) & 0x7f) ? 1 : 0)
+#define forbidden(c) (((c) == '\"') || ((c) == '\'') || ((c) == '$') ||\
+ ((c) == ',') || ((c) == '.') || ((c) == '-') ||\
+ ((c) == LPAREN) || ((c) == '/')|| ((c) == '`') ||\
+ ((c) == '{') || ((c) == '\\') || (iscontrol((c))) ||\
+ (((c) >= '0') && ((c) <= '9')) || ((c) == '?'))
+#define is_cletter(c) ((((c) >= 'a') && ((c) <= 'z'))) ||\
+ ((((c) >= 'A') && ((c) <= 'Z'))||\
+ is8bit(c))
+#define is_cnumber(c) ((c) >= '0' && (c) <= '9')
+#define allwd_after_word(c) (((c) == ' ') || ((c) == '>') || is_cletter(c))
+#define allwd_after_qsword(c) (((c) != '\\') && ((c) != RPAREN))
+#define before(word,i) (((i) > 0) ? (word)[(i) - 1] : 0)
+#define next(w,i) ((((w)[(i)]) != 0) ? ((w)[(i) + 1]) : 0)
+#define now(w,i) ((w)[(i)])
+#define is_qsword(c) (((c) == ':') || ((c) == RPAREN) ? 1 : 0)
+#define is_colon(c) (((c) == ':') ? 1 : 0)
+#define is_rarrow(c) (((c) == '>') ? 1 : 0)
+#define is_tilde(c) (((c) == '~') ? 1 : 0)
+#define is_dash(c) (((c) == '-') ? 1 : 0)
+#define is_pound(c) (((c) == '#') ? 1 : 0)
+#define is_a_digit(c) ((((c) >= '0') && ((c) <= '9')) ? 1 : 0)
+#define is_allowed(c) (is_cquote(c) || is_cword(c) || is_dash(c) || \
+ is_pound(c))
+#define qs_allowed(a) (((a)->qstype != qsGdb) && ((a)->qstype != qsProg))
+
+/* Internal justification functions */
+QSTRING_S *is_quote(char **, char *, int);
+QSTRING_S *qs_normal_part(QSTRING_S *);
+QSTRING_S *qs_remove_trailing_spaces(QSTRING_S *);
+QSTRING_S *trim_qs_from_cl(QSTRING_S *, QSTRING_S *, QSTRING_S *);
+QSTRING_S *fix_qstring(QSTRING_S *, QSTRING_S *, QSTRING_S *);
+QSTRING_S *fix_qstring_allowed(QSTRING_S *, QSTRING_S *, QSTRING_S *);
+QSTRING_S *qs_add(char **, char *, QStrType, int, int, int, int);
+QSTRING_S *remove_qsword(QSTRING_S *);
+QSTRING_S *do_raw_quote_match(char **, char *, char *, char *, QSTRING_S **, QSTRING_S **);
+void free_qs(QSTRING_S **);
+int word_is_prog(char *);
+int qstring_is_normal(QSTRING_S *);
+int exists_good_part(QSTRING_S *);
+int strcmp_qs(char *, char *);
+int count_levels_qstring(QSTRING_S *);
+int same_qstring(QSTRING_S *, QSTRING_S *);
+int isaword(char *,int ,int);
+int isamailbox(char *,int ,int);
+int double_check_qstr(char *);
+
+int
+word_is_prog(char *word)
+{
+ static char *list1[] = {"#include",
+ "#define",
+ "#ifdef",
+ "#ifndef",
+ "#elif",
+ "#if",
+ NULL};
+ static char *list2[] = {"#else",
+ "#endif",
+ NULL};
+ int i, j = strlen(word), k, rv = 0;
+
+ for(i = 0; rv == 0 && list1[i] && (k = strlen(list1[i])) && k < j; i++)
+ if(!strncmp(list1[i], word, k) && ISspace(word[k]))
+ rv++;
+
+ if(rv)
+ return rv;
+
+ for(i = 0; rv == 0 && list2[i] && (k = strlen(list2[i])) && k <= j; i++)
+ if(!strncmp(list2[i], word, k) && (!word[k] || ISspace(word[k])))
+ rv++;
+
+ return rv;
+}
+
+/*
+ * This function creates a qstring pointer with the information that
+ * is_quote handles to it.
+ * Parameters:
+ * qs - User supplied quote string
+ * word - The line of text that the user is trying to read/justify
+ * beginw - Where we need to start copying from
+ * endw - Where we end copying
+ * offset - Any offset in endw that we need to account for
+ * typeqs - type of the string to be created
+ * neednext - boolean, indicating if we need to compute the next field
+ * of leave it NULL
+ *
+ * It is a mistake to call this function if beginw >= endw + offset.
+ * Please note the equality sign in the above inequality (this is because
+ * we always assume that qstring->value != "").
+ */
+QSTRING_S *
+qs_add(char **qs, char word[NSTRING], QStrType typeqs, int beginw, int endw,
+ int offset, int neednext)
+{
+ QSTRING_S *qstring, *nextqs;
+ int i;
+
+ qstring = (QSTRING_S *) malloc (sizeof(QSTRING_S));
+ memset (qstring, 0, sizeof(QSTRING_S));
+ qstring->qstype = qsNormal;
+
+ if (beginw == 0){
+ beginw = endw + offset;
+ qstring->qstype = typeqs;
+ }
+
+ nextqs = neednext ? is_quote(qs, word+beginw, 1) : NULL;
+
+ qstring->value = (char *) malloc((beginw+1)*sizeof(char));
+ strncpy(qstring->value, word, beginw);
+ qstring->value[beginw] = '\0';
+
+ qstring->next = nextqs;
+
+ return qstring;
+}
+
+int
+qstring_is_normal(QSTRING_S *cl)
+{
+ for (;cl && (cl->qstype == qsNormal); cl = cl->next);
+ return cl ? 0 : 1;
+}
+
+/*
+ * Given a quote string, this function returns the part that is the leading
+ * normal part of it. (the normal part is the part that is tagged qsNormal,
+ * that is to say, the one that is not controversial at all (like qsString
+ * for example).
+ */
+QSTRING_S *
+qs_normal_part(QSTRING_S *cl)
+{
+
+ if (!cl) /* nothing in, nothing out */
+ return cl;
+
+ if (cl->qstype != qsNormal)
+ free_qs(&cl);
+
+ if (cl)
+ cl->next = qs_normal_part(cl->next);
+
+ return cl;
+}
+
+/*
+ * this function removes trailing spaces from a quote string, but leaves the
+ * last one if there are trailing spaces
+ */
+QSTRING_S *
+qs_remove_trailing_spaces(QSTRING_S *cl)
+{
+ QSTRING_S *rl = cl;
+ if (!cl) /* nothing in, nothing out */
+ return cl;
+
+ if (cl->next)
+ cl->next = qs_remove_trailing_spaces(cl->next);
+ else{
+ if (value_is_space(cl->value))
+ free_qs(&cl);
+ else{
+ int i, l;
+ i = l = strlen(cl->value) - 1;
+ while (cl->value && cl->value[i]
+ && ISspace(cl->value[i]))
+ i--;
+ i += (i < l) ? 2 : 1;
+ cl->value[i] = '\0';
+ }
+ }
+ return cl;
+}
+
+/*
+ * This function returns if two strings are the same quote string.
+ * The call is not symmetric. cl must preceed the line nl. This function
+ * should be called for comparing the last part of cl and nl.
+ */
+int
+strcmp_qs(char *valuecl, char *valuenl)
+{
+ int j;
+
+ for (j = 0; valuecl[j] && (valuecl[j] == valuenl[j]); j++);
+ return !strcmp(valuecl, valuenl)
+ || (valuenl[j] && value_is_space(valuenl+j)
+ && value_is_space(valuecl+j)
+ && strlenis(valuecl+j) >= strlenis(valuenl+j))
+ || (!valuenl[j] && value_is_space(valuecl+j));
+}
+
+int
+count_levels_qstring(QSTRING_S *cl)
+{
+ int count;
+ for (count = 0; cl ; count++, cl = cl->next);
+
+ return count;
+}
+
+int
+value_is_space(char *value)
+{
+ for (; value && *value && ISspace(*value); value++);
+
+ return value && *value ? 0 : 1;
+}
+
+void
+free_qs(QSTRING_S **cl)
+{
+ if (!(*cl))
+ return;
+
+ if ((*cl)->next)
+ free_qs(&((*cl)->next));
+
+ (*cl)->next = (QSTRING_S *) NULL;
+
+ if ((*cl)->value)
+ free((void *)(*cl)->value);
+ (*cl)->value = (char *) NULL;
+ free((void *)(*cl));
+ *cl = (QSTRING_S *) NULL;
+}
+
+/*
+ * This function returns the number of agreements between
+ * cl and nl. The call is not symmetric. cl must be the line
+ * preceding nl.
+ */
+int
+same_qstring(QSTRING_S *cl, QSTRING_S *nl)
+{
+ int same = 0, done = 0;
+
+ for (;cl && nl && !done; cl = cl->next, nl = nl->next)
+ if (cl->qstype == nl->qstype
+ && (!strcmp(cl->value, nl->value)
+ || (!cl->next && strcmp_qs(cl->value, nl->value))))
+ same++;
+ else
+ done++;
+ return same;
+}
+
+QSTRING_S *
+trim_qs_from_cl(QSTRING_S *cl, QSTRING_S *nl, QSTRING_S *pl)
+{
+ QSTRING_S *cqstring = pl ? pl : nl;
+ QSTRING_S *tl = pl ? pl : nl;
+ int p, c;
+
+ if (qstring_is_normal(tl))
+ return tl;
+
+ p = same_qstring(pl ? pl : cl, pl ? cl : nl);
+
+ for (c = 1; c < p; c++, cl = cl->next, tl = tl->next);
+
+ /*
+ * cl->next and tl->next differ, it may be because cl->next does not
+ * exist or tl->next does not exist or simply both exist but are
+ * different. In this last case, it may be that cl->next->value is made
+ * of spaces. If this is the case, tl advances once more.
+ */
+
+ if (tl->next){
+ if (cl && cl->next && value_is_space(cl->next->value))
+ tl = tl->next;
+ if (tl->next)
+ free_qs(&(tl->next));
+ }
+
+ if (!p)
+ free_qs(&cqstring);
+
+ return cqstring;
+}
+
+/* This function trims cl so that it returns a real quote string based
+ * on information gathered from the previous and next lines. pl and cl are
+ * also trimmed, but that is done in another function, not here.
+ */
+QSTRING_S *
+fix_qstring(QSTRING_S *cl, QSTRING_S *nl, QSTRING_S *pl)
+{
+ QSTRING_S *cqstring = cl, *nqstring = nl, *pqstring = pl;
+ int c, n;
+
+ if (qstring_is_normal(cl))
+ return cl;
+
+ c = count_levels_qstring(cl);
+ n = same_qstring(cl,nl);
+
+ if (!n){ /* no next line or no agreement with next line */
+ int p = same_qstring(pl, cl); /* number of agreements between pl and cl */
+ QSTRING_S *tl; /* test line */
+
+ /*
+ * Here p <= c, so either p < c or p == c. If p == c, we are done,
+ * and return cl. If not, there are two cases, either p == 0 or
+ * 0 < p < c. In the first case, we do not have enough evidence
+ * to return anything other than the normal part of cl, in the second
+ * case we can only return p levels of cl.
+ */
+
+ if (p == c)
+ tl = cqstring;
+ else{
+ if (p){
+ for (c = 1; c < p; c++)
+ cl = cl->next;
+ free_qs(&(cl->next));
+ tl = cqstring;
+ }
+ else{
+ int done = 0;
+ QSTRING_S *al = cl; /* another line */
+ /*
+ * Ok, we really don't have enough evidence to return anything,
+ * different from the normal part of cl, but it could be possible
+ * that we may want to accept the not-normal part, so we better
+ * make an extra test to determine what needs to be freed
+ */
+ while (pl && cl && cl->qstype == pl->qstype
+ && !strucmp(cl->value, pl->value)){
+ cl = cl->next;
+ pl = pl->next;
+ }
+ if (pl && cl && cl->qstype == pl->qstype
+ && strcmp_qs(pl->value, cl->value))
+ cl = cl->next; /* next level differs only in spaces */
+ while (!done){
+ while (cl && cl->qstype == qsNormal)
+ cl = cl->next;
+ if (cl){
+ if ((cl->qstype == qsString)
+ && (cl->value[strlen(cl->value) - 1] == '>'))
+ cl = cl->next;
+ else done++;
+ }
+ else done++;
+ }
+ if (al == cl){
+ free_qs(&(cl));
+ tl = cl;
+ }
+ else {
+ while (al && (al->next != cl))
+ al = al->next;
+ cl = al;
+ if (cl && cl->next)
+ free_qs(&(cl->next));
+ tl = cqstring;
+ }
+ }
+ }
+ return tl;
+ }
+ if (n + 1 < c){ /* if there are not enough agreements */
+ int p = same_qstring(pl, cl); /* number of agreement between pl and cl */
+ QSTRING_S *tl; /* test line */
+ /*
+ * There's no way we can use cl in this case, but we can use
+ * part of cl, this is if pl does not have more agreements
+ * with cl.
+ */
+ if (p == c)
+ tl = cqstring;
+ else{
+ int m = p < n ? n : p;
+ for (c = 1; c < m; c++){
+ pl = pl ? pl->next : (QSTRING_S *) NULL;
+ nl = nl ? nl->next : (QSTRING_S *) NULL;
+ cl = cl->next;
+ }
+ if (p == n && pl && pl->next && nl && nl->next
+ && ((cl->next->qstype == pl->next->qstype)
+ || (cl->next->qstype == nl->next->qstype))
+ && (strcmp_qs(cl->next->value, pl->next->value)
+ || strcmp_qs(pl->next->value, cl->next->value)
+ || strcmp_qs(cl->next->value, nl->next->value)
+ || strcmp_qs(nl->next->value, cl->next->value)))
+ cl = cl->next; /* next level differs only in spaces */
+ if (cl->next)
+ free_qs(&(cl->next));
+ tl = cqstring;
+ }
+ return tl;
+ }
+ if (n + 1 == c){
+ int p = same_qstring(pl, cl);
+ QSTRING_S *tl; /* test line */
+
+ /*
+ * p <= c, so p <= n+1, which means p < n + 1 or p == n + 1.
+ * If p < n + 1, then p <= n.
+ * so we have three possibilities:
+ * p == n + 1 or p == n or p < n.
+ * In the first case we copy p == n + 1 == c levels, in the second
+ * and third case we copy n levels, and check if we can copy the
+ * n + 1 == c level.
+ */
+ if (p == n + 1) /* p == c, in the above sense of c */
+ tl = cl; /* use cl, this is enough evidence */
+ else{
+ for (c = 1; c < n; c++)
+ cl = cl->next;
+ /*
+ * Here c == n, we only have one more level of cl, and at least one
+ * more level of nl
+ */
+ if (cl->next->qstype == qsNormal)
+ cl = cl->next;
+ if (cl->next)
+ free_qs(&(cl->next));
+ tl = cqstring;
+ }
+ return tl;
+ }
+ if (n == c) /* Yeah!!! */
+ return cqstring;
+}
+
+QSTRING_S *
+fix_qstring_allowed(QSTRING_S *cl, QSTRING_S *nl, QSTRING_S *pl)
+{
+ if(!cl)
+ return (QSTRING_S *) NULL;
+
+ if (qs_allowed(cl))
+ cl->next = fix_qstring_allowed(cl->next, (nl ? nl->next : NULL),
+ (pl ? pl->next : NULL));
+ else
+ if((nl && cl->qstype == nl->qstype) || (pl && cl->qstype == pl->qstype)
+ || (!nl && !pl))
+ free_qs(&cl);
+ return cl;
+}
+
+/*
+ * This function flattens the quote string returned to us by is_quote. A
+ * crash in this function implies a bug elsewhere.
+ */
+void
+flatten_qstring(QSTRING_S *qs, char *buff, int bufflen)
+{
+ int i, j;
+ if(!buff || bufflen <= 0)
+ return;
+
+ for (i = 0; qs; qs = qs->next)
+ for (j = 0; i < bufflen - 1
+ && (qs->value[j]) && (buff[i++] = qs->value[j]); j++);
+ buff[i] = '\0';
+}
+
+extern int list_len;
+
+
+int
+double_check_qstr(char *q)
+{
+ if(!q || !*q)
+ return 0;
+
+ return (*q == '#') ? 1 : 0;
+}
+
+/*
+ * Given a string, we return the position where the function thinks that
+ * the quote string is over, if you are ever thinking of fixing something,
+ * you got to the right place. Memory freed by caller. Experience shows
+ * that it only makes sense to initialize memory when we need it, not at
+ * the start of this function.
+ */
+QSTRING_S *
+is_quote (char **qs,char *word, int been_here)
+{
+ int i = 0, j, nxt, prev, finished = 0, offset;
+ unsigned char c;
+ QSTRING_S *qstring = (QSTRING_S *) NULL;
+
+ if (word == NULL || word[0] == '\0')
+ return (QSTRING_S *) NULL;
+
+ while (!finished){
+ /*
+ * Before we apply our rules, let's advance past the quote string
+ * given by the user, this will avoid not recognition of the
+ * user's indent string and application of the arbitrary rules
+ * below. Notice that this step may bring bugs into this
+ * procedure, but these bugs will only appear if the indent string
+ * is really really strange and the text to be justified
+ * cooperates a lot too, so in general this will not be a problem.
+ * If you are concerned about this bug, simply remove the
+ * following lines after this comment and before the "switch"
+ * command below and use a more normal quote string!.
+ */
+ for(j = 0; j < list_len; j++){
+ if(!double_check_qstr(qs[j])){
+ i += advance_quote_string(qs[j], word, i);
+ if (!word[i]) /* went too far? */
+ return qs_add(qs, word, qsNormal, 0, i, 0, 0);
+ }
+ else
+ break;
+ }
+
+ switch (c = (unsigned char) now(word,i)){
+ case NBSP:
+ case TAB :
+ case ' ' : { QSTRING_S *nextqs, *d;
+
+ for (; ISspace(word[i]); i++); /* FIX ME */
+ nextqs = is_quote(qs,word+i, 1);
+ /*
+ * Merge qstring and nextqs, since this is an artificial
+ * separation, unless nextqs is of different type.
+ * What this means in practice is that if
+ * qs->qstype == qsNormal and qs->next != NULL, then
+ * qs->next->qstype != qsNormal.
+ *
+ * Can't use qs_add to merge because it could lead
+ * to an infinite loop (e.g a line "^ ^").
+ */
+ i += nextqs && nextqs->qstype == qsNormal
+ ? strlen(nextqs->value) : 0;
+ qstring = (QSTRING_S *) malloc (sizeof(QSTRING_S));
+ memset (qstring, 0, sizeof(QSTRING_S));
+ qstring->value = (char *) malloc((i+1)*sizeof(char));
+ strncpy(qstring->value, word, i);
+ qstring->value[i] = '\0';
+ qstring->qstype = qsNormal;
+ if(nextqs && nextqs->qstype == qsNormal){
+ d = nextqs->next;
+ nextqs->next = NULL;
+ qstring->next = d;
+ free_qs(&nextqs);
+ }
+ else
+ qstring->next = nextqs;
+
+ return qstring;
+ }
+ break;
+ case RPAREN: /* parenthesis ')' */
+ if ((i != 0) || ((i == 0) && been_here))
+ i++;
+ else
+ if (i == 0)
+ return qs_add(qs, word, qsChar, i, i, 1, 1);
+ else
+ finished++;
+ break;
+
+ case ':': /* colon */
+ case '~': nxt = next(word,i);
+ if ((is_tilde(c) && (nxt == '/'))
+ || (is_colon(c) && !is_cquote(nxt)
+ && !is_cword(nxt) && nxt != RPAREN))
+ finished++;
+ else if (is_cquote(c)
+ || is_cquote(nxt)
+ || (c != '~' && nxt == RPAREN)
+ || (i != 0 && ISspace(nxt))
+ || is_cquote(prev = before(word,i))
+ || (ISspace(prev) && !is_tilde(c))
+ || (is_tilde(c) && nxt != '/'))
+ i++;
+ else if (i == 0 && been_here)
+ return qs_add(qs, word, qsChar, i, i, 1, 1);
+ else
+ finished++;
+ break;
+
+ case '<' :
+ case '=' :
+ case '-' : offset = is_cquote(nxt = next(word,i)) ? 2
+ : (nxt == c && is_cquote(next(word,i+1))) ? 3 : -1;
+
+ if (offset > 0)
+ return qs_add(qs, word, qsString, i, i, offset, 1);
+ else
+ finished++;
+ break;
+
+ case '[' :
+ case '+' : /* accept +>, *> */
+ case '*' : if (is_rarrow(nxt = next(word, i)) || /* stars */
+ (ISspace(nxt) && is_rarrow(next(word,i+1))))
+ i++;
+ else
+ finished++;
+ break;
+
+ case '^' :
+ case '!' :
+ case '%' : if (next(word,i) != c)
+ return qs_add(qs, word, qsChar, i, i+1, 0, 1);
+ else
+ finished++;
+ break;
+
+ case '_' : if(ISspace(next(word, i)))
+ return qs_add(qs, word, qsChar, i, i+1, 0, 1);
+ else
+ finished++;
+ break;
+
+ case '#' : { QStrType qstype = qsChar;
+ if((nxt = next(word, i)) != c){
+ if(isdigit((int) nxt))
+ qstype = qsGdb;
+ else
+ if(word_is_prog(word))
+ qstype = qsProg;
+ return qs_add(qs, word, qstype, i, i+1, 0, 1);
+ }
+ else
+ finished++;
+ break;
+ }
+
+ default:
+ if (is_cquote(c))
+ i++;
+ else if (is_cletter(c)){
+ for (j = i; (is_cletter(nxt = next(word,j)) || is_cnumber(nxt))
+ && !(ISspace(nxt));j++);
+ /*
+ * The whole reason why we are splitting the quote
+ * string is so that we will be able to accept quote
+ * strings that are strange in some way. Here we got to
+ * a point in which a quote string might exist, but it
+ * could be strange, so we need to create a "next" field
+ * for the quote string to warn us that something
+ * strange is coming. We need to confirm if this is a
+ * good choice later. For now we will let it pass.
+ */
+ if (isaword(word,i,j) || isamailbox(word,i,j)){
+ int offset;
+ QStrType qstype;
+
+ offset = (is_cquote(c = next(word,j))
+ || (c == RPAREN)) ? 2
+ : ((ISspace(c)
+ && is_cquote(next(word,j+1))) ? 3 : -1);
+
+ qstype = (is_cquote(c) || (c == RPAREN))
+ ? (is_qsword(c) ? qsWord : qsString)
+ : ((ISspace(c) && is_cquote(next(word,j+1)))
+ ? (is_qsword(next(word,j+1))
+ ? qsWord : qsString)
+ : qsString);
+
+ /*
+ * qsWords are valid quote strings only when
+ * they are followed by text.
+ */
+ if (offset > 0 && qstype == qsWord &&
+ !allwd_after_qsword(now(word,j + offset)))
+ offset = -1;
+
+ if (offset > 0)
+ return qs_add(qs, word, qstype, i, j, offset, 1);
+ }
+ finished++;
+ }
+ else{
+ if(i > 0)
+ return qs_add(qs, word, qsNormal, 0, i, 0, 1);
+ else if(!forbidden(c))
+ return qs_add(qs, word, qsChar, 0, 1, 0, 1);
+ else /* chao pescao */
+ finished++;
+ }
+ break;
+ } /* End Switch */
+ } /* End while */
+ if (i > 0)
+ qstring = qs_add(qs, word, qsNormal, 0, i, 0, 0);
+ return qstring;
+}
+
+int
+isaword(char word[NSTRING], int i, int j)
+{
+ return i <= j && is_cletter(word[i]) ?
+ (i < j ? isaword(word,i+1,j) : 1) : 0;
+}
+
+int
+isamailbox(char word[NSTRING], int i, int j)
+{
+ return i <= j && (is_cletter(word[i]) || is_a_digit(word[i])
+ || word[i] == '.')
+ ? (i < j ? isamailbox(word,i+1,j) : 1) : 0;
+}
+
+/*
+ This routine removes the last part that is qsword or qschar that is not
+ followed by a normal part. This means that if a qsword or qschar is
+ followed by a qsnormal (or qsstring), we accept the qsword (or qschar)
+ as part of a quote string.
+ */
+QSTRING_S *
+remove_qsword(QSTRING_S *cl)
+{
+ QSTRING_S *np = cl;
+ QSTRING_S *cp = np; /* this variable trails cl */
+
+ while(1){
+ while (cl && cl->qstype == qsNormal)
+ cl = cl->next;
+
+ if (cl){
+ if (((cl->qstype == qsWord) || (cl->qstype == qsChar))
+ && !exists_good_part(cl)){
+ if (np == cl) /* qsword or qschar at the beginning */
+ free_qs(&cp);
+ else{
+ while (np->next != cl)
+ np = np->next;
+ free_qs(&(np->next));
+ }
+ break;
+ }
+ else
+ cl = cl->next;
+ }
+ else
+ break;
+ }
+ return cp;
+}
+
+int
+exists_good_part (QSTRING_S *cl)
+{
+ return (cl ? (((cl->qstype != qsWord) && (cl->qstype != qsChar)
+ && qs_allowed(cl) && !value_is_space(cl->value))
+ ? 1 : exists_good_part(cl->next))
+ : 0);
+}
+
+int
+line_isblank(char **q, char *GLine, char *NLine, char *PLine, int buflen)
+{
+ int n = 0;
+ QSTRING_S *cl;
+ char qstr[NSTRING];
+
+ cl = do_raw_quote_match(q, GLine, NLine, PLine, NULL, NULL);
+
+ flatten_qstring(cl, qstr, NSTRING);
+
+ free_qs(&cl);
+
+ for(n = strlen(qstr); n < buflen && GLine[n]; n++)
+ if(!ISspace((unsigned char) GLine[n]))
+ return(FALSE);
+
+ return(TRUE);
+}
+
+QSTRING_S *
+do_raw_quote_match(char **q, char *GLine, char *NLine, char *PLine, QSTRING_S **nlp, QSTRING_S **plp)
+{
+ QSTRING_S *cl, *nl = NULL, *pl = NULL;
+ char nbuf[NSTRING], pbuf[NSTRING], buf[NSTRING];
+ int emptypl = 0, emptynl = 0;
+
+ if (!(cl = is_quote(q, GLine, 0))) /* if nothing in, nothing out */
+ return cl;
+
+ nl = is_quote(q, NLine, 0); /* Next Line */
+ if (nlp) *nlp = nl;
+ pl = is_quote(q, PLine, 0); /* Previous Line */
+ if (plp) *plp = pl;
+ /*
+ * If there's nothing in the preceeding or following line
+ * there is not enough information to accept it or discard it. In this
+ * case it's likely to be an isolated line, so we better accept it
+ * if it does not look like a word.
+ */
+ flatten_qstring(pl, pbuf, NSTRING);
+ emptypl = (!PLine || !PLine[0] ||
+ (pl && value_is_space(pbuf)) && !PLine[strlen(pbuf)]) ? 1 : 0;
+ if (emptypl){
+ flatten_qstring(nl, nbuf, NSTRING);
+ emptynl = (!NLine || !NLine[0] ||
+ (nl && value_is_space(nbuf) && !NLine[strlen(nbuf)])) ? 1 : 0;
+ if (emptynl){
+ cl = remove_qsword(cl);
+ if((cl = fix_qstring_allowed(cl, NULL, NULL)) != NULL)
+ cl = qs_remove_trailing_spaces(cl);
+ free_qs(&nl);
+ free_qs(&pl);
+ if(nlp) *nlp = NULL;
+ if(plp) *plp = NULL;
+
+ return cl;
+ }
+ }
+
+ /*
+ * If either cl, nl or pl contain suspicious characters that may make
+ * them (or not) be quote strings, we need to fix them, so that the
+ * next pass will be done correctly.
+ */
+
+ cl = fix_qstring(cl, nl, pl);
+ nl = trim_qs_from_cl(cl, nl, NULL);
+ pl = trim_qs_from_cl(cl, NULL, pl);
+ if((cl = fix_qstring_allowed(cl, nl, pl)) != NULL){
+ nl = trim_qs_from_cl(cl, nl, NULL);
+ pl = trim_qs_from_cl(cl, NULL, pl);
+ }
+ else{
+ free_qs(&nl);
+ free_qs(&pl);
+ }
+ if(nlp)
+ *nlp = nl;
+ else
+ free_qs(&nl);
+ if(plp)
+ *plp = pl;
+ else
+ free_qs(&pl);
+ return cl;
+}
+
+QSTRING_S *
+do_quote_match(char **q, char *GLine, char *NLine, char *PLine, char *rqstr,
+int rqstrlen, int plb)
+{
+ QSTRING_S *cl, *nl = NULL, *pl = NULL;
+ int c, n, p,i, j, NewP, NewC, NewN, clength, same = 0;
+ char nbuf[NSTRING], pbuf[NSTRING], buf[NSTRING];
+
+ if(rqstr)
+ *rqstr = '\0';
+
+ /* if nothing in, nothing out */
+ cl = do_raw_quote_match(q, GLine, NLine, PLine, &nl, &pl);
+ if(cl == NULL){
+ free_qs(&nl);
+ free_qs(&pl);
+ return cl;
+ }
+
+ flatten_qstring(cl, rqstr, rqstrlen);
+ flatten_qstring(cl, buf, NSTRING);
+ flatten_qstring(nl, nbuf, NSTRING);
+ flatten_qstring(pl, pbuf, NSTRING);
+
+ /*
+ * Once upon a time, is_quote used to return the length of the quote
+ * string that it had found. One day, not long ago, black hand came
+ * and changed all that, and made is_quote return a quote string
+ * divided in several fields, making the algorithm much more
+ * complicated. Fortunately black hand left a few comments in the
+ * source code to make it more understandable. Because of this change
+ * we need to compute the lengths of the quote strings separately
+ */
+ c = buf && buf[0] ? strlen(buf) : 0;
+ n = nbuf && nbuf[0] ? strlen(nbuf) : 0;
+ p = pbuf && pbuf[0] ? strlen(pbuf) : 0;
+ /*
+ * When quote strings contain only blank spaces (ascii code 32) the
+ * above count is equal to the length of the quote string, but if
+ * there are TABS, the length of the quote string as seen by the user
+ * is different than the number that was just computed. Because of
+ * this we demand a recount (hmm.. unless you are in Florida, where
+ * recounts are forbidden)
+ */
+ NewP = strlenis(pbuf);
+ NewC = strlenis(buf);
+ NewN = strlenis(nbuf);
+
+ /*
+ * For paragraphs with spaces in the first line, but no space in the
+ * quote string of the second line, we make sure we choose the quote
+ * string without a space at the end of it.
+ */
+ if ((NLine && !NLine[0])
+ && ((PLine && !PLine[0])
+ || (((same = same_qstring(pl, cl)) != 0)
+ && (same != count_levels_qstring(cl)))))
+ cl = qs_remove_trailing_spaces(cl);
+ else
+ if (NewC > NewN){
+ int agree = 0;
+ for (j = 0; (j < n) && (GLine[j] == NLine[j]); j++);
+ clength = j;
+ /* clength is the common length in which Gline and Nline agree */
+ /* j < n means that they do not agree fully */
+ /* GLine = " \tText"
+ NLine = " Text" */
+ if(j == n)
+ agree++;
+ if (clength < n){ /* see if buf and nbuf are padded with spaces and tabs */
+ for (i = clength; i < n && ISspace(NLine[i]); i++);
+ if (i == n){/* padded NLine until the end of spaces? */
+ for (i = clength; i < c && ISspace(GLine[i]); i++);
+ if (i == c) /* Padded CLine until the end of spaces? */
+ agree++;
+ }
+ }
+ if (agree){
+ for (j = clength; j < c && ISspace(GLine[j]); j++);
+ if (j == c){
+ /*
+ * If we get here, it means that the current line has the same
+ * quote string (visually) than the next line, but both of them
+ * are padded with different amount of TABS or spaces at the end.
+ * The current line (GLine) has more spaces/TABs than the next
+ * line. This is the typical situation that is found at the
+ * begining of a paragraph. We need to check this, however, by
+ * checking the previous line. This avoids that we confuse
+ * ourselves with being in the last line of a paragraph.
+ * Example when it should not free_qs(cl)
+ * " Text in Paragraph 1" (PLine)
+ * " Text in Paragraph 1" (GLine)
+ * " Other Paragraph Number 2" (NLine)
+ *
+ * Example when it should free_qs(cl):
+ * ":) " (PLine) p = 3, j = 3
+ * ":) Text" (GLine) c = 5
+ * ":) More text" (NLine) n = 3
+ *
+ * Example when it should free_qs(cl):
+ * ":) " (PLine) p = 3, j = 3
+ * ":) > > > Text" (GLine) c = 11
+ * ":) > > > More text" (NLine) n = 9
+ *
+ * Example when it should free_qs(cl):
+ * ":) :) " (PLine) p = 6, j = 3
+ * ":) > > > Text" (GLine) c = 11
+ * ":) > > > More text" (NLine) n = 9
+ *
+ * Example when it should free_qs(cl):
+ * ":) > > > " (PLine) p = 13, j = 11
+ * ":) > > > Text" (GLine) c = 11
+ * ":) > > > More text" (NLine) n = 9
+ *
+ * The following example is very interesting. The "Other Text"
+ * line below should free the quote string an make it equal to the
+ * quote string of the line below it, but any algorithm trying
+ * to advance past that line should make it stop there, so
+ * we need one more check, to check the raw quote string and the
+ * processed quote string at the same time.
+ * FREE qs in this example.
+ * " Some Text" (PLine) p = 3, j = 0
+ * "\tOther Text" (GLine) c = 1
+ * " More Text" (NLine) n = 3
+ *
+ */
+ for (j = 0; (j < p) && (GLine[j] == PLine[j]); j++);
+ if ((p != c || j != p) && NLine[n])
+ if(!get_indent_raw_line(q, PLine, nbuf, NSTRING, p, plb)
+ || NewP + strlenis(nbuf) != NewC){
+ free_qs(&cl);
+ free_qs(&pl);
+ return nl;
+ }
+ }
+ }
+ }
+
+ free_qs(&nl);
+ free_qs(&pl);
+
+ return cl;
+}
+
+/*
+ * Given a line, an initial position, and a quote string, we advance the
+ * current line past the quote string, including arbitraty spaces
+ * contained in the line, except that it removes trailing spaces. We do
+ * not handle TABs, if any, contained in the quote string. At least not
+ * yet.
+ *
+ * Arguments: q - quote string
+ * l - a line to process
+ * i - position in the line to start processing. i = 0 is the
+ * begining of that line.
+ */
+int
+advance_quote_string(char *q, char l[NSTRING], int i)
+{
+ int n = 0, j = 0, is = 0, es = 0;
+ int k, m, p, adv;
+ char qs[NSTRING] = {'\0'};
+ if(!q || !*q)
+ return(0);
+ for (p = strlen(q); (p > 0) && (q[p - 1] == ' '); p--, es++);
+ if (!p){ /* string contains only spaces */
+ for (k = 0; ISspace(l[i + k]); k++);
+ k -= k % es;
+ return k;
+ }
+ for (is = 0; ISspace(q[is]); is++); /* count initial spaces */
+ for (m = 0 ; is + m < p ; m++)
+ qs[m] = q[is + m]; /* qs = quote string without any space at the end */
+ /* advance as many spaces as there are at the begining */
+ for (k = 0; ISspace(l[i + j]); k++, j++);
+ /* now find the visible string in the line */
+ for (m = 0; qs[m] && l[i + j] == qs[m]; m++, j++);
+ if (!qs[m]){ /* no match */
+ /*
+ * So far we have advanced at least "is" spaces, plus the visible
+ * string "qs". Now we need to advance the trailing number of
+ * spaces "es". If we can do that, we have found the quote string.
+ */
+ for (p = 0; ISspace(l[i + j + p]); p++);
+ adv = advance_quote_string(q, l, i + j + ((p < es) ? p : es));
+ n = ((p < es) ? 0 : es) + k + m + adv;
+ }
+ return n;
+}
+
+/*
+ * This function returns the effective length in screen of the quote
+ * string. If the string contains a TAB character, it is added here, if
+ * not, the length returned is the length of the string
+ */
+int strlenis(char *qstr)
+{
+ int i, rv = 0;
+ for (i = 0; qstr && qstr[i]; i++)
+ rv += ((qstr[i] == TAB) ? (~rv & 0x07) + 1 : 1);
+ return rv;
+}
+
+int
+is_indent (char word[NSTRING], int plb)
+{
+ int i = 0, finished = 0, c, nxt, j, k, digit = 0, bdigits = -1, alpha = 0;
+
+ if (!word || !word[0])
+ return i;
+
+ for (i = 0, j = 0; ISspace(word[i]); i++, j++);
+ while ((i < NSTRING - 2) && !finished){
+ switch (c = now(word,i)){
+ case NBSP:
+ case TAB :
+ case ' ' : for (; ISspace(word[i]); i++);
+ if (!is_indent_char(now(word,i)))
+ finished++;
+ break;
+
+ case '+' :
+ case '.' :
+ case ']' :
+ case '*' :
+ case '}' :
+ case '-' :
+ case RPAREN:
+ nxt = next(word,i);
+ if ((c == '.' && allowed_after_period(nxt) && alpha)
+ || (c == '*' && allowed_after_star(nxt))
+ || (c == '}' && allowed_after_braces(nxt))
+ || (c == '-' && allowed_after_dash(nxt))
+ || (c == '+' && allowed_after_dash(nxt))
+ || (c == RPAREN && allowed_after_parenth(nxt))
+ || (c == ']' && allowed_after_parenth(nxt)))
+ i++;
+ else
+ finished++;
+ break;
+
+ default : if (is_a_digit(c) && plb){
+ if (bdigits < 0)
+ bdigits = i; /* first digit */
+ for (k = i; is_a_digit(now(word,k)); k++);
+ if (k - bdigits > 2){ /* more than 2 digits? */
+ i = bdigits; /* too many! */
+ finished++;
+ }
+ else{
+ if(allowed_after_digit(now(word,k),word,k)){
+ alpha++;
+ i = k;
+ }
+ else{
+ i = bdigits;
+ finished++;
+ }
+ }
+ }
+ else
+ finished++;
+ break;
+
+ }
+ }
+ if (i == j)
+ i = 0; /* there must be something more than spaces in an indent string */
+ return i;
+}
+
+int
+get_indent_raw_line(char **q, char *GLine, char *buf, int buflen, int k, int plb)
+{
+ int i, j;
+ char testline[1024];
+
+ if(k > 0){
+ for(j = 0; GLine[j] != '\0'; j++){
+ testline[j] = GLine[j];
+ testline[j+1] = '\0';
+ if(strlenis(testline) >= strlenis(buf))
+ break;
+ }
+ k = ++j; /* reset k */
+ }
+ i = is_indent(GLine+k, plb);
+
+ for (j = 0; j < i && j < buflen && (buf[j] = GLine[j + k]); j++);
+ buf[j] = '\0';
+
+ return i;
+}
+
+/* support for remembering quote strings across messages */
+char **allowed_qstr = NULL;
+int list_len = 0;
+
+void
+free_allowed_qstr(void)
+{
+ int i;
+ char **q = allowed_qstr;
+
+ if(q == NULL)
+ return;
+
+ for(i = 0; i < list_len; i++)
+ fs_give((void **)&q[i]);
+
+ fs_give((void **)q);
+ list_len = 0;
+}
+
+void
+add_allowed_qstr(void *q, int type)
+{
+ int i;
+
+ if(allowed_qstr == NULL){
+ allowed_qstr = malloc(sizeof(char *));
+ list_len = 0;
+ }
+
+ if(type == 0){
+ allowed_qstr[list_len] = malloc((1+strlen((char *)q))*sizeof(char));
+ strcpy(allowed_qstr[list_len], (char *)q);
+ }
+ else
+ allowed_qstr[list_len] = (char *) ucs4_to_utf8_cpystr((UCS *)q);
+
+ fs_resize((void **)&allowed_qstr, (++list_len + 1)*sizeof(char *));
+ allowed_qstr[list_len] = NULL;
+}
+
+void
+record_quote_string (QSTRING_S *qs)
+{
+ int i, j, k;
+
+ for(; qs && qs->value; qs = qs->next){
+ j = 0;
+ for (; ;){
+ k = j;
+ for(i = 0; i < list_len; i++){
+ j += advance_quote_string(allowed_qstr[i], qs->value, j);
+ for(; ISspace(qs->value[j]); j++);
+ }
+ if(k == j)
+ break;
+ }
+ if(qs->value[j] != '\0')
+ add_allowed_qstr((void *)(qs->value + j), 0);
+ }
+}
+
+/* type utf8: code 0; ucs4: code 1. */
+char **
+default_qstr(void *q, int type)
+{
+ if(allowed_qstr == NULL)
+ add_allowed_qstr(q, type);
+
+ return allowed_qstr;
+}
+
diff --git a/pith/osdep/color.h b/pith/osdep/color.h
index 32c86242..929389a2 100644
--- a/pith/osdep/color.h
+++ b/pith/osdep/color.h
@@ -17,6 +17,24 @@
#ifndef PITH_OSDEP_COLOR_INCLUDED
#define PITH_OSDEP_COLOR_INCLUDED
+/*
+ * struct that will help us determine what the quote string of a line
+ * is. The "next" field indicates the presence of a possible continuation.
+ * The idea is that if a continuation fails, we free it and check for the
+ * remaining structure left
+ */
+
+typedef enum {qsNormal, qsString, qsWord, qsChar, qsGdb, qsProg, qsText} QStrType;
+
+typedef struct QSTRING {
+ char *value; /* possible quote string */
+ QStrType qstype; /* type of quote string */
+ struct QSTRING *next; /* possible continuation */
+} QSTRING_S;
+
+#define UCH(c) ((unsigned char) (c))
+#define NBSP UCH('\240')
+#define ISspace(c) (UCH(c) == ' ' || UCH(c) == TAB || UCH(c) == NBSP)
#define RGBLEN 11
#define MAXCOLORLEN 11 /* longest string a color can be */
@@ -93,6 +111,11 @@ char *pico_get_last_fg_color(void);
char *pico_get_last_bg_color(void);
char *color_to_canonical_name(char *);
int pico_count_in_color_table(void);
-
+int is_indent(char *, int);
+int get_indent_raw_line (char **, char *, char *, int, int, int);
+int line_isblank(char **, char *, char *, char *, int);
+int strlenis(char *);
+int value_is_space(char *);
+int advance_quote_string(char *, char *, int);
#endif /* PITH_OSDEP_COLOR_INCLUDED */
diff --git a/pith/pattern.c b/pith/pattern.c
index 84a32c41..9d09462a 100644
--- a/pith/pattern.c
+++ b/pith/pattern.c
@@ -1756,7 +1756,7 @@ parse_action_slash(char *str, ACTION_S *action)
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);
@@ -5483,6 +5483,15 @@ match_pattern_folder_specific(PATTERN_S *folders, MAILSTREAM *stream, int flags)
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, MSGNO_S *msgmap, char *dstfldr,
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, MSGNO_S *msgmap, char *dstfldr,
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, MSGNO_S *msgmap, char *dstfldr,
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');
}
diff --git a/pith/pine.hlp b/pith/pine.hlp
index 82872f19..6fbe15ac 100644
--- a/pith/pine.hlp
+++ b/pith/pine.hlp
@@ -89,6 +89,7 @@ Where "variable" is one of either:
ALPINE_VERSION
ALPINE_REVISION
ALPINE_COMPILE_DATE
+ ALPINE_PATCHLEVEL
ALPINE_TODAYS_DATE
C_CLIENT_VERSION
_LOCAL_FULLNAME_
@@ -159,6 +160,14 @@ Version <!--#echo var="ALPINE_VERSION"-->(<!--#echo var="ALPINE_REVISION"-->)
</DIV>
<P>
+This version was modified from its original source code. More information
+about some of the patches applied to this version can be found <A HREF="h_patches">here</A>.
+<!--chtml if pinemode="running"-->
+The patch level of this version, including creation date of the patch is:
+<!--#echo var=ALPINE_PATCHLEVEL-->.
+<!--chtml endif-->
+
+<P>
Alpine is an &quot;Alternatively Licensed Program for Internet
News and Email&quot; produced until 2008 by the University of Washington.
It is intended to be an easy-to-use program for
@@ -198,7 +207,7 @@ message, as specified by original sender.
Bugs that have been addressed include:
<P>
<UL>
- <LI> Proper quote of shell characters in urls.
+ <LI> Do not use a shell to open a browser.
<LI> Configure script did not test for crypto or pam libraries.
<LI> Fix Cygwin separator to &quot;/&quot;.
<LI> Crash when a non-compliant SMTP server closes a connection without a QUIT command.
@@ -3183,9 +3192,11 @@ There are also additional details on
<li><a href="h_config_alt_role_menu">FEATURE: <!--#echo var="FEAT_alternate-role-menu"--></a>
<li><a href="h_config_force_low_speed">FEATURE: <!--#echo var="FEAT_assume-slow-link"--></a>
<li><a href="h_config_auto_read_msgs">FEATURE: <!--#echo var="FEAT_auto-move-read-msgs"--></a>
+<li><a href="h_config_auto_read_msgs_rules">FEATURE: <!--#echo var="FEAT_auto-move-read-msgs-using-rules"--></a>
<li><a href="h_config_auto_open_unread">FEATURE: <!--#echo var="FEAT_auto-open-next-unread"--></a>
<li><a href="h_config_auto_unselect">FEATURE: <!--#echo var="FEAT_auto-unselect-after-apply"--></a>
<li><a href="h_config_auto_unzoom">FEATURE: <!--#echo var="FEAT_auto-unzoom-after-apply"--></a>
+<li><a href="h_config_circular_tab">FEATURE: <!--#echo var="FEAT_enable-circular-tab"--></a>
<li><a href="h_config_auto_zoom">FEATURE: <!--#echo var="FEAT_auto-zoom-after-select"--></a>
<li><a href="h_config_use_boring_spinner">FEATURE: <!--#echo var="FEAT_busy-cue-spinner-only"--></a>
<li><a href="h_config_check_mail_onquit">FEATURE: <!--#echo var="FEAT_check-newmail-when-quitting"--></a>
@@ -3285,6 +3296,7 @@ There are also additional details on
<li><a href="h_config_full_auto_expunge">FEATURE: <!--#echo var="FEAT_expunge-without-confirm-everywhere"--></a>
<li><a href="h_config_no_fcc_attach">FEATURE: <!--#echo var="FEAT_fcc-without-attachments"--></a>
<li><a href="h_config_force_arrow">FEATURE: <!--#echo var="FEAT_force-arrow-cursor"--></a>
+<li><a href="h_config_ignore_size">FEATURE: <!--#echo var="FEAT_ignore-size-changes"--></a>
<li><a href="h_config_forward_as_attachment">FEATURE: <!--#echo var="FEAT_forward-as-attachment"--></a>
<li><a href="h_config_preserve_field">FEATURE: <!--#echo var="FEAT_preserve-original-fields"--></a>
<li><a href="h_config_quell_empty_dirs">FEATURE: <!--#echo var="FEAT_quell-empty-directories"--></a>
@@ -3298,6 +3310,7 @@ There are also additional details on
<li><a href="h_config_add_ldap">FEATURE: <!--#echo var="FEAT_ldap-result-to-addrbook-add"--></a>
<li><a href="h_config_maildrops_preserve_state">FEATURE: <!--#echo var="FEAT_maildrops-preserve-state"--></a>
<li><a href="h_config_mark_fcc_seen">FEATURE: <!--#echo var="FEAT_mark-fcc-seen"--></a>
+<li><a href="h_config_mark_for_group">FEATURE: <!--#echo var="FEAT_mark-for-me-in-group"--></a>
<li><a href="h_config_mark_for_cc">FEATURE: <!--#echo var="FEAT_mark-for-cc"--></a>
<li><a href="h_config_mulnews_as_typed">FEATURE: <!--#echo var="FEAT_mult-newsrc-hostnames-as-typed"--></a>
<li><a href="h_config_news_uses_recent">FEATURE: <!--#echo var="FEAT_news-approximates-new-status"--></a>
@@ -3500,6 +3513,7 @@ There are also additional details on
<li><a href="h_config_image_viewer">OPTION: <!--#echo var="VAR_image-viewer"--></a>
<li><a href="h_config_inbox_path">OPTION: <!--#echo var="VAR_inbox-path"--></a>
<li><a href="h_config_archived_folders">OPTION: <!--#echo var="VAR_incoming-archive-folders"--></a>
+<li><a href="h_config_sleep">OPTION: <!--#echo var="VAR_sleep-interval-length"--></a>
<li><a href="h_config_incoming_interv">OPTION: <!--#echo var="VAR_incoming-check-interval"--></a>
<li><a href="h_config_incoming_second_interv">OPTION: <!--#echo var="VAR_incoming-check-interval-secondary"--></a>
<li><a href="h_config_incoming_list">OPTION: <!--#echo var="VAR_incoming-check-list"--></a>
@@ -3552,6 +3566,7 @@ There are also additional details on
<li><a href="h_config_print_cat">OPTION: <!--#echo var="VAR_personal-print-category"--></a>
<li><a href="h_config_print_command">OPTION: <!--#echo var="VAR_personal-print-command"--></a>
<li><a href="h_config_post_char_set">OPTION: <!--#echo var="VAR_posting-character-set"--></a>
+<li><a href="h_config_special_text_to_color">OPTION: <!--#echo var="VAR_h_config_special_text_to_color"--></a>
<li><a href="h_config_postponed_folder">OPTION: <!--#echo var="VAR_postponed-folder"--></a>
<li><a href="h_config_print_font_char_set">OPTION: Print-Font-Char-Set</a>
<li><a href="h_config_print_font_name">OPTION: Print-Font-Name</a>
@@ -3580,9 +3595,11 @@ There are also additional details on
<li><a href="h_config_sending_filter">OPTION: <!--#echo var="VAR_sending-filters"--></a>
<li><a href="h_config_sendmail_path">OPTION: <!--#echo var="VAR_sendmail-path"--></a>
<li><a href="h_config_signature_color">OPTION: Signature Color</a>
+<li><a href="h_config_special_text_color">OPTION: Special Text Color</a>
<li><a href="h_config_signature_file">OPTION: <!--#echo var="VAR_signature-file"--></a>
<li><a href="h_config_smtp_server">OPTION: <!--#echo var="VAR_smtp-server"--></a>
<li><a href="h_config_sort_key">OPTION: <!--#echo var="VAR_sort-key"--></a>
+<li><a href="h_config_thread_sort_key">OPTION: <!--#echo var="VAR_thread-sort-key"--></a>
<li><a href="h_config_speller">OPTION: <!--#echo var="VAR_speller"--></a>
<li><a href="h_config_sshcmd">OPTION: <!--#echo var="VAR_ssh-command"--></a>
<li><a href="h_config_ssh_open_timeo">OPTION: <!--#echo var="VAR_ssh-open-timeout"--></a>
@@ -4442,6 +4459,10 @@ thread:
message in the thread was sent to you as a cc:. This symbol will only show up if
the feature
&quot;<A HREF="h_config_mark_for_cc"><!--#echo var="FEAT_mark-for-cc"--></A>&quot; is turned on (which is the default).
+ <LI> &quot;.&quot; for messages that were sent to you as part of a group message, regardless
+ of if all addresses in the To: field are yours or not. This symbol will only show up if
+ the feature
+ &quot;<A HREF="h_config_mark_for_group"><!--#echo var="FEAT_mark-for-me-in-group"--></A>&quot; is turned on (which is the default).
<LI> &quot;X&quot; for selected. You have selected at least one message in the thread by using the
&quot;select&quot; command. (Some systems may optionally allow selected
messages to be denoted by the index line being displayed in bold
@@ -4601,6 +4622,10 @@ message:
message was sent to you as a cc:. This symbol will only show up if
the feature
&quot;<A HREF="h_config_mark_for_cc"><!--#echo var="FEAT_mark-for-cc"--></A>&quot; is turned on (which is the default).
+ <LI> &quot;.&quot; for messages that were sent to you as part of a group message, regardless
+ of if all addresses in the To: field are yours or not. This symbol will only show up if
+ the feature
+ &quot;<A HREF="h_config_mark_for_group"><!--#echo var="FEAT_mark-for-me-in-group"--></A>&quot; is turned on (which is the default).
<LI> &quot;X&quot; for selected. You have selected the message by using the
&quot;select&quot; command. (Some systems may optionally allow selected
messages to be denoted by the index line being displayed in bold
@@ -5515,6 +5540,163 @@ the names of the carbon copy addresses of the message.
&lt;End of help on this topic&gt;
</BODY>
</HTML>
+======= h_thread_index_sort_arrival =======
+<HTML>
+<HEAD>
+<TITLE>SORT OPTION: Arrival</TITLE>
+</HEAD>
+<BODY>
+<H1>SORT OPTION: Arrival</H1>
+
+The <EM>Arrival</EM> 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.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_thread_index_sort_date =======
+<HTML>
+<HEAD>
+<TITLE>SORT OPTION: Date</TITLE>
+</HEAD>
+<BODY>
+<H1>SORT OPTION: Date</H1>
+
+The <EM>Date</EM> sort option in the THREAD&nbsp;INDEX screen sorts
+threads by the date in which messages were sent. The thread containing the
+last message in this order is displayed last.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_thread_index_sort_subj =======
+<HTML>
+<HEAD>
+<TITLE>SORT OPTION: Subject</TITLE>
+</HEAD>
+<BODY>
+<H1>SORT OPTION: Subject</H1>
+
+The <EM>Subject</EM> sort option has not been defined yet.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_thread_index_sort_ordsubj =======
+<HTML>
+<HEAD>
+<TITLE>SORT OPTION: OrderedSubject</TITLE>
+</HEAD>
+<BODY>
+<H1>SORT OPTION: OrderedSubject</H1>
+
+The <EM>OrderedSubject</EM> sort option in the THREAD&nbsp;INDEX screen is
+the same as sorting by <A HREF="h_thread_index_sort_subj">Subject</A>.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_thread_index_sort_thread =======
+<HTML>
+<HEAD>
+<TITLE>SORT OPTION: Thread</TITLE>
+</HEAD>
+<BODY>
+<H1>SORT OPTION: Thread</H1>
+
+The <EM>Thread</EM> sort option in the THREAD&nbsp;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.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_thread_index_sort_from =======
+<HTML>
+<HEAD>
+<TITLE>SORT OPTION: From</TITLE>
+</HEAD>
+<BODY>
+<H1>SORT OPTION: From</H1>
+
+The <EM>From</EM> sort option has not been defined yet.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_thread_index_sort_size =======
+<HTML>
+<HEAD>
+<TITLE>SORT OPTION: Size</TITLE>
+</HEAD>
+<BODY>
+<H1>SORT OPTION: Size</H1>
+
+The <EM>Size</EM> 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.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_thread_index_sort_score =======
+<HTML>
+<HEAD>
+<TITLE>SORT OPTION: Score</TITLE>
+</HEAD>
+<BODY>
+<H1>SORT OPTION: Score</H1>
+
+The <EM>Score</EM> 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.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_thread_index_sort_to =======
+<HTML>
+<HEAD>
+<TITLE>SORT OPTION: To</TITLE>
+</HEAD>
+<BODY>
+<H1>SORT OPTION: To</H1>
+
+The <EM>To</EM> sort option has not been defined yet.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_thread_index_sort_cc =======
+<HTML>
+<HEAD>
+<TITLE>SORT OPTION: Cc</TITLE>
+</HEAD>
+<BODY>
+<H1>SORT OPTION: Cc</H1>
+
+The <EM>Cc</EM> sort option has not been defined yet.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
======= h_index_cmd_whereis =======
<HTML>
<HEAD>
@@ -6738,6 +6920,46 @@ hold down the &quot;Control&quot; key on your keyboard. The second &quot;^&quot
&quot;type the character ^&quot;.
<P>
+This version of Alpine contains an enhanced algorithm for justification,
+which allows you to justify text that contains more complicated quote
+strings. This algorithm is based on pragmatics, rather than on a theory,
+and seems to work well with most messages. Below you will find technical
+information on how this algorithm works.
+
+<P>
+When justifying, Alpine goes through each line of the text and tries to
+determine for each line what the quote string of that line is. The quote
+string you provided is always recognized. Among other characters
+recognized is &quot;&gt;&quot;.
+
+<P>
+Some other constructions of quote strings are recognized only if they
+appear enough in the text. For example &quot;Peter :&quot; is only
+recognized if it appears in two consecutive lines.
+
+<P>
+Additionaly, Alpine recognizes indent-strings and justifies text in a
+paragraph to the right of indent-string, padding with spaces if necessary.
+An indent string is one which you use to delimit elements of a list. For
+example, if you were to write a list of groceries, one may write:
+
+<UL>
+<LI> Fruit
+<LI> Bread
+<LI> Eggs
+</UL>
+
+<P>
+In this case the character &quot;*&quot; is the indent-string. Aline
+recognizes numbers (0, 1, 2.5, etc) also as indent-strings, and certain
+combinations of spaces, periods, and parenthesis. In any case, numbers are
+recognized <B>ONLY</B> if the line preceeding the given line is empty or
+ends in one of the characters &quot;.&quot; or &quot;:&quot;.
+In addition to the explanation of what constitutes a paragraph above, a
+new paragraph is recognized when an indent-string is found in it (and
+validated according to the above stated rules).
+
+<P>
&lt;End of help on this topic&gt;
</BODY>
</HTML>
@@ -18300,6 +18522,7 @@ This set of special tokens may be used in the
<A HREF="h_config_index_format">&quot;<!--#echo var="VAR_index-format"-->&quot;</A> option,
in the <A HREF="h_config_reply_intro">&quot;<!--#echo var="VAR_reply-leadin"-->&quot;</A> option,
in signature files,
+in the <A HREF="h_config_reply_leadin_rules">&quot;new-rules&quot; option</A>,
in template files used in
<A HREF="h_rules_roles">&quot;roles&quot;</A>, and in the folder name
that is the target of a Filter Rule.
@@ -18312,7 +18535,7 @@ and in the target of Filter Rules.
<P>
<P>
-<H1><EM>Tokens Available for all Cases (except Filter Rules)</EM></H1>
+<H1><EM>Tokens Available for all Cases (except Filter Rules or in some cases for new-rules)</EM></H1>
<DL>
<DT>SUBJECT</DT>
@@ -18345,6 +18568,15 @@ email address, never the personal name.
For example, &quot;mailbox@domain&quot;.
</DD>
+<DT>ADDRESSTO</DT>
+<DD>
+This is similar to the &quot;TO&quot; token, only it is always the
+email address of all people listed in the TO: field of the messages. Addresses
+are separated by a blank space. Example, &quot;mailbox@domain&quot; when
+the e-mail message contains only one person in the To: field, or
+&quot;peter@flintstones.com president@world.com&quot;.
+</DD>
+
<DT>MAILBOX</DT>
<DD>
This is the same as the &quot;ADDRESS&quot; except that the
@@ -18392,6 +18624,15 @@ are unavailable) of the persons specified in the
message's &quot;Cc:&quot; header field.
</DD>
+<DT>ADDRESSCC</DT>
+<DD>
+This is similar to the &quot;CC&quot; token, only it is always the
+email address of all people listed in the Cc: field of the messages. Addresses
+are separated by a blank space. Example: &quot;mailbox@domain&quot; when
+the e-mail message contains only one person in the Cc: field, or
+&quot;peter@flintstones.com president@world.com&quot;.
+</DD>
+
<DT>RECIPS</DT>
<DD>
This token represents the personal names (or email addresses if the names
@@ -18400,6 +18641,14 @@ message's &quot;To:&quot; header field and
the message's &quot;Cc:&quot; header field.
</DD>
+<DT>ADDRESSRECIPS</DT>
+<DD>
+This token represent the e-mail addresses of the people in the To: and
+Cc: fields, exactly in that order separated by a space. It is almost obtained
+by concatenating the ADDRESSTO and ADDRESSCC tokens.
+</DD>
+
+
<DT>NEWSANDRECIPS</DT>
<DD>
This token represents the newsgroups from the
@@ -18745,7 +18994,11 @@ aspects of the message's state.
The first character is either blank,
a '*' for message marked Important, or a '+' indicating a message
addressed directly to you (as opposed to your having received it via a
-mailing list, for example).
+mailing list, for example). The symbol '.' will be used
+for messages that were sent to you as part of a group message, regardless
+of if all addresses in the To: field are yours or not. This symbol will only show up if
+the feature
+&quot;<A HREF="h_config_mark_for_group"><!--#echo var="FEAT_mark-for-me-in-group"--></A>&quot; is turned on (which is the default).
When the feature
&quot;<A HREF="h_config_mark_for_cc"><!--#echo var="FEAT_mark-for-cc"--></A>&quot;
is set, if the first character would have been
@@ -18881,6 +19134,14 @@ The progression of sizes used looks like:
<P>
</DD>
+<DT>SIZETHREAD</DT>
+<DD>
+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.
+</DD>
+
<DT>SIZENARROW</DT>
<DD>
This token represents the total size, in bytes, of the message.
@@ -19496,6 +19757,78 @@ This is an end of line marker.
</DL>
<P>
+<H1><EM>Tokens Available Only for New-Rules</EM></H1>
+
+<DL>
+<DT>FOLDER</DT>
+<DD>
+Name of the folder where the rule will be applied
+</DD>
+</DL>
+
+<DL>
+<DT>COLLECTION</DT>
+<DD>
+Name of the collection list where the rule will be applied.
+</DD>
+</DL>
+
+<DL>
+<DT>ROLE</DT>
+<DD>
+Name of the Role used to reply a message.
+</DD>
+</DL>
+
+<DL>
+<DT>BCC</DT>
+<DD>
+Not implemented yet, but it will be implemented in future versions. It will
+be used for <A HREF="h_config_compose_rules">compose</A>
+<A HREF="h_config_reply_rules">reply</A>
+<A HREF="h_config_forward_rules">forward</A>
+rules.
+</DD>
+</DL>
+
+<DL>
+<DT>LCC</DT>
+<DD>
+This is the value of the Lcc: field at the moment that you start the composition.
+</DD>
+</DL>
+
+<DL>
+<DT>FORWARDFROM</DT>
+<DD>
+This corresponds to the personal name (or address if there's no personal
+name) of the person who sent the message that you are forwarding.
+</DD>
+</DL>
+
+<DL>
+<DT>FORWARDADDRESS</DT>
+<DD>
+This is the address of the person that sent the message that you
+are forwarding.
+</DD>
+</DL>
+
+
+
+
+<DL>
+<DT>FLAG</DT>
+<DD>
+A string containing the value of all the flags associated to a specific
+message. The possible values of allowed flags are "*" for Important, "N"
+for recent or new, "U" for unseen or unread, "R" for seen or read, "A" for
+answered and "D" for deleted. See an example of its use in the
+<A HREF="h_config_new_rules">new rules</A> explanation and example help.
+</DD>
+</DL>
+
+<P>
<H1><EM>Token Available Only for Templates and Signatures</EM></H1>
<DL>
@@ -20420,6 +20753,53 @@ give up and consider it a failed connection.
&lt;End of help on this topic&gt;
</BODY>
</HTML>
+====== h_config_sleep ======
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_sleep-interval-length"--> (UNIX Alpine only)</TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_sleep-interval-length"--> (UNIX Alpine only)</H1>
+
+<P>
+When you open an attachment, Alpine goes through a list of viewers either
+in your .mailcap file, or some other mailcap file in your system. The
+normal behavior is that Alpine opens a local copy of the attachment, which
+is removed from the system <B>after</B> the viewer has completed
+displaying it. For example, if you open an attachment on a viewer and
+later close the viewer, then control will return to the system and the
+copy of the attachment will be removed from the system. This is the normal
+behavior and has been accepted for years as the correct behavior.
+
+<P>
+However, if an instance of the viewer is already open, the viewer may
+return control to the system <B>before</B> it reads the copy of the
+attachment. Given that Alpine removes the copy of the attachment after
+control is returned to the system, this may cause Alpine to remove the
+copy of the attachment <B>before</B> it is actually opened by the viewer.
+
+<P>
+Since Alpine has no control over when a viewer returns from opening a file,
+and viewers should not return before they read the file, Alpine has no control
+over when the viewer will read the file and avoid the problem described above.
+
+<P>
+The value of this option is the number of seconds that Alpine will wait
+between the time that the viewer returns control to the system and the
+when it will remove it from the system. You will not notice this
+delay, since this will happen in the background. The default value is
+0 which means that no delay will occur, and it is a value which may trigger
+the problem described above with some viewers. By modifying the value of
+this option you can set Alpine to wait longer. A reasonable small value is 5,
+which works with all viewers tested to date. The maximum value is 120
+(2 minutes).
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
====== h_config_incoming_interv ======
<HTML>
<HEAD>
@@ -21484,6 +21864,102 @@ your account's home directory).
&lt;End of help on this topic&gt;
</BODY>
</HTML>
+====== h_config_maildir_location ======
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_maildir-location"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_maildir-location"--></H1>
+
+<P>
+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.
+
+<P>
+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.
+
+<P>
+The value of this option is prefixed with the "~/" string to determine the
+full path to your INBOX.
+
+<P>
+You should probably <A HREF="h_config_maildir">read</A> a few tips that
+teach you how to configure your maildir for optimal performance. This
+version also has <A HREF="h_config_courier_list">support</A> for the
+Courier style file system when a maildir collection is accessed locally.
+
+<P><UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_maildir =====
+<HTML>
+<HEAD>
+<TITLE>Maildir Support</TITLE>
+</HEAD>
+<BODY>
+<H1>Maildir Support</H1>
+
+This version of Alpine has been enhanced with Maildir support. This text is
+intended to be a reference on its support.
+<P>
+
+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.
+<P>
+
+In order to use maildir support it is better to set your inbox-path to the
+value &quot;#md/inbox&quot; (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 <A
+HREF="h_config_maildir_location"><!--#echo var="VAR_maildir-location"--></A> configuration
+variable. Most of the time you will not have to worry about the
+<!--#echo var="VAR_maildirlocation"--> variable, because it will probably be set by your
+administrator in the pine.conf configuration file.
+<P>
+
+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.
+
+<P>
+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 &quot;mail/&quot; to
+&quot;#md/mail&quot;. In a maildir collection folders of any other format
+are ignored.
+
+<P> Finally, This version also has
+<A HREF="h_config_courier_list">support</A> for the Courier style file system
+when a maildir collection is accessed locally.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
====== h_config_literal_sig =====
<HTML>
<HEAD>
@@ -22246,6 +22722,45 @@ command, then it will not be re-sorted until the next time it is opened.
&lt;End of help on this topic&gt;
</BODY>
</HTML>
+====== h_config_thread_sort_key =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_thread-sort-key--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_thread-sort-key--></TITLE></H1>
+
+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 &quot;removed&quot; from the sorted list and the
+process is repeated with the remaining messages in that list.
+
+<P>
+<UL>
+ <LI> <A HREF="h_thread_index_sort_arrival">Arrival</A>
+ <LI> <A HREF="h_thread_index_sort_date">Date</A>
+<!-- <LI> <A HREF="h_thread_index_sort_subj">Subject</A>
+ <LI> <A HREF="h_thread_index_sort_ordsubj">OrderedSubj</A>-->
+ <LI> <A HREF="h_thread_index_sort_thread">Thread</A>
+<!-- <LI> <A HREF="h_thread_index_sort_from">From</A> -->
+ <LI> <A HREF="h_thread_index_sort_size">Size</A>
+ <LI> <A HREF="h_thread_index_sort_score">Score</A>
+<!-- <LI> <A HREF="h_thread_index_sort_to">To</A>
+ <LI> <A HREF="h_thread_index_sort_cc">Cc</A>-->
+</UL>
+
+<P> Each type of sort may also be reversed. Normal default is by
+&quot;Thread&quot;.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
====== h_config_other_startup =====
<HTML>
<HEAD>
@@ -22516,6 +23031,898 @@ character sets Alpine knows about by using the &quot;T&quot; ToCharsets command.
&lt;End of help on this topic&gt;
</BODY>
</HTML>
+====== h_config_procid =====
+<HTML>
+<HEAD>
+<TITLE>Token: PROCID</TITLE>
+</HEAD>
+<BODY>
+<H1>TOKEN: PROCID explained</H1>
+
+<P>
+The PROCID token is a way in which the user and the program can differentiate
+between different parts of a program. It allows the user to tell the
+program when to use a specific rule, and only use it at that specific
+moment.
+
+<P> The normal way in which this is done is by adding a new configuration
+variable. The idea behind the PROCID token is that instead of adding a new
+configuration variable (which means the user has to go through more
+configuration variables just to tune the program to his liking), we reuse
+an old variable and let the user look inside that variable for the desired
+behavior, which is actually set by setting the PROCID token.
+
+<P>
+Consider the following examples for forward-rules:
+
+<P>
+_ROLE_ == {work} =&gt; _SUBJECT_ := _COPY_{[tag] _SUBJECT_}
+
+<P>
+and
+
+<P>
+_ROLE_ == {work} =&gt; _LCC_ := _TRIM_{_FORWARDFROM_ &lt;_FORWARDADDRESS_&gt;}
+
+<P>
+both are triggered by the same condition. Since both are configured in the
+same variable, only one of them will be executed all the time (whichever
+is first). Therefore in order to differentiate, we add a _PROCID_ token.
+So, for example, the first example above will be executed only when we are
+determining the subject. In this case, the following rule will accomplish
+this task
+
+<P>
+_PROCID_ == {fwd-subject} && _ROLE_ == {work} =&gt; _SUBJECT_ := _COPY_{[tag] _SUBJECT_}
+
+<P>
+In this case, this rule will be tested fully only when we are determining
+the subject line of a forwarded message, not otherwise.
+
+<P>
+It is wise to add the _PROCID_ token as the first condition in a rule, so
+that other conditions will not be tested in a long list of rules.
+
+<P>&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_compose_rules =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_compose-rules"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_compose-rules"--></H1>
+
+<P> At this time, this option is used to generate values for signature
+files that is not possible to do with the use of
+<A HREF="h_rules_roles">roles</A>.
+
+<P> For example, you can have a rule like:<BR>
+_TO_ >> {Peter Flinstones} => _SIGNATURE_{~/.petersignature}
+
+<P> This configuration option is just one of many that allow you to
+override the value of some global configurations within Alpine. There is a
+help text explaining how to define all of them, which you can read by
+following this <A HREF="h_config_new_rules">link</A>.
+
+<P>&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_forward_rules =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_forward-rules"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_forward-rules"--></H1>
+
+<P> This option has several uses. This feature uses the PROCID function
+to identify different features of forwarding. You can read more about PROCID
+by following <A HREF="h_config_procid">this link</A>.
+
+<P> If you want to edit the subject of a forwarded message, use the
+PROCID <I>fwd-subject</I>. For example you could have a rule like
+
+<P>
+_ROLE_ == {admin} && _SUBJECT_ !&gt; {[tag] } =&gt; _COPY_{[tag] _SUBJECT_}
+
+<P> Another way in which this option can be used, is to trim the values of
+some fields. For this application the PROCID is <I>fwd-lcc</I>. For
+example it can be used in the following way:
+
+<P>
+_ROLE_ == {work} => _LCC_ := _TRIM_{_FORWARDFROM_ &lt;_FORWARDADDRESS_&gt;}
+
+<P> Other functions that can be used in this option are _EXEC_ and _REXTRIM_.
+
+<P> You can also use the _EXEC_ function. The documentation for this function
+is in the
+<A HREF="h_config_resub_rules"><!--#echo var="VAR_reply-subject-rules"--></A>
+help text.
+
+<P> This configuration option is just one of many that allow you to
+override the value of some global configurations within Alpine. There is a
+help text explaining how to define all of them, which you can read by
+following this <A HREF="h_config_new_rules">link</A>.
+
+<P>&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_index_rules =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_index-rules"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_index-rules"--></H1>
+
+<P> This option is used to supersede the value of the option <A
+HREF="h_config_index_format"><!--#echo var="VAR_index-format"--></A> for specific folders. In
+this form you can have different index-formats for different folders. For
+example an entry here may be:
+
+<P>
+_FOLDER_ == {INBOX} => _INDEX_{IMAPSTATUS DATE FROM(33%) SIZE SUBJECT(67%)}
+
+<P> This configuration option is just one of many that allow you to
+override the value of some global configurations within Alpine. There is a
+help text explaining how to define all of them, which you can read by
+following this <A HREF="h_config_new_rules">link</A>.
+
+<P>&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_pretty_command =====
+<HTML>
+<HEAD>
+<TITLE>Pretty-Command Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>Pretty Command Explained</H1>
+
+<P> This text explains how to encode keys so that they will be recognized
+by Alpine in the _PKEY_ token. Most direct keystrokes are recognized in the
+same way. For example, the key ~ is recognized by the same character. The
+issue is how control, or functions keys are recognized. The internal code
+is most times easy to find out. If the key you want to use is not already
+recognized by Alpine simply press it. Alpine will print its code. For example,
+the return key is not recognized in this screen, so if you press it, you
+will see the following message.
+
+<P> [Command &quot;RETURN&quot; not defined for this screen. Use ? for help]
+
+<P> from here you can guess that the code for the return command is
+RETURN. You can try other commands, like Control-C, the TAB key, F4, etc.
+to see their codes.
+
+<P>&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_key_macro_rules =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_key-definition-rules"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_key-definition-rules"--></H1>
+
+<P> This option can be used to define macros, that is, to define a key that
+when pressed executes a group of predetermined keystrokes. Since Alpine is
+a menu driven program, sometimes the same key may have different meanings
+in different screens, so a global redefinition of a key although possible
+is not advisable.
+
+<P> <B>Always use the _SCREEN_ token as defined below.</B>. You have been
+warned!
+
+<P> In each screen, every time you press a recognized key a command is
+activated. In order to understand this feature, think of commands instead
+of keystrokes. For example, you can think of the sort by thread command.
+This command is associated to the keystrokes $ and h. You may want to
+associate this command to a specific keystroke, like ~, so every time you
+press the ~ key, Alpine understand the $ and h keystrokes, which activates
+the sort by thread command.
+
+<P> Therefore, in order to use this option you must think of three
+components. The screen where you will use the macro, the keystroke you
+want to use and the set of keystrokes used by Alpine to accomplish the task
+you want to accomplish. We will talk about these three components in what
+follows.
+
+<P> First you must decide in which screen the macro will be used. This
+feature is currently only available for the screen where your messages
+are listed in index form (<A HREF="h_mail_index">MESSAGE INDEX</A>),
+the screen where your message is displayed
+(<A HREF="h_mail_view">MESSAGE TEXT</A>) and the screen where the list of
+folders is displayed (<A HREF="h_folder_maint">FOLDER LIST</A>). The
+internal names of these screens for this patch are &quot;index&quot;,
+&quot;text&quot; and
+&quot;folder&quot; respectively. Please note that the internal names are
+all in lowercase are are case sensitive.
+
+<P> In order to define the screen, you use the _SCREEN_ token, so for
+example, you can write _SCREEN_ == {index}.
+
+<P> Second you must think of which key you will use to activate the macro.
+Here you can use any key of your choice. The token you use to designate a
+key is the _PKEY_ token (PKEY stands for &quot;pressed key&quot;). For
+example you could use _PKEY_ == {~}, to designate the &quot;~&quot;
+character to activate the command. Some keystrokes (like control, or
+function keys) are encoded in special ways. You should read the
+<A HREF="h_config_pretty_command">full explanation</A> on how to find
+out the encoding for each keystroke.
+
+<P> Last, you must think of the list of keys you will use to accomplish
+the task you want Alpine to perform. Say for example you want to have the
+folder sorted by thread. That means you want Aline to execute the keys
+&quot;$&quot; and &quot;h&quot;. You use the _COMMAND_ function to specify
+this. The syntax in this case is _COMMAND_{$,h}.
+
+<P> Observe that in the above example the different inputs are separated
+by commas. This is the standard way in which the
+<A HREF="h_config_init_cmd_list"><!--#echo var="VAR_initial-keystroke-list"--></A> command works from
+the command line. Due to restrictions in the way Alpine works, a comma is a
+special character, which when added to a configuration option like this
+will cause the configuration to split into several lines in the
+configuration screen. This has the effect of producing several
+configuration options, all of which are incorrect. This is undesirable
+because what you want is to have it all in one line. In order to force the
+configuration into one line you must quote the comma. The best way to
+accomplish this is by quoting the full definition of the rule. For
+example.
+
+<P>
+&quot;_SCREEN_ == {index} && _PKEY_ == {~} => _COMMAND_{$,h}&quot;
+
+<P> Another way to accomplish the same effect is by quoting the command and
+not using quotes for the full command, nor commas to separate the
+keystrokes in the command, for example
+
+<P>
+_SCREEN_ == {index} && _PKEY_ == {~} => _COMMAND_{&quot;$h&quot;}
+
+<P> For more information on how to define the argument of the _COMMAND_
+token see the help of
+<A HREF="h_config_init_cmd_list"><!--#echo var="VAR_initial-keystroke-list"--></A>.
+
+<P> Because the $ command can also be used as the first character in the
+definition of an environemnt variable, no expansion of environment variables
+is done when parsing this variable. The $ character does not need quoting
+and quoting it will make Alpine fail to produce the correct result.
+
+<P> This configuration option is just one of many that allow you to
+override the value of some global configurations within Alpine. There is a
+help text explaining how to define all of them, which you can read by
+following this <A HREF="h_config_new_rules">link</A>.
+
+<P>&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_replace_rules =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_replace-rules"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_replace-rules"--></H1>
+
+<P> This option is used to have Alpine print different values for specific
+tokens in the <A HREF="h_config_index_format"><!--#echo var="VAR_index-format"--></A>. For example you
+can replace strings like "To: newsgroup" by your name.
+
+<P> Here are examples of possible rules:<BR>
+_FOLDER_ != {sent-mail} && _NICK_ != {} => _FROM_ := _REPLACE_{_FROM_ (_NICK_)}
+
+<P> or if you receive messages with tags that contain arbitrary numbers, and
+you want them removed from the index (but not from the subject), use a rule
+like the following<BR>
+_FOLDER_ == {INBOX} => _SUBJECT_ := _REXTRIM_{&#92;[some-tag-here #[0-9].*&#92;]}
+
+<P> You can also use this configuration option to remove specific strings of
+the index display screen, so that you can trim unnecessary information in
+your index, like the reply leadin string in the OPENINGTEXTNQ token of the index.
+<BR>
+_FOLDER_ == {mailing-list} => _OPENINGTEXTNQ_ := _REXTRIM_{On.*wrote: }
+
+<P> or if you receive messages with tags that contain arbitrary numbers, and
+you want them removed from the index (but not from the subject), use a rule
+like the following<BR>
+
+<P> You can also use the _EXEC_ function. The documentation for this function
+is in the
+<A HREF="h_config_resub_rules"><!--#echo var="VAR_reply-subject-rules"--></A>
+help text.
+
+<P> This configuration option is just one of many that allow you to
+override the value of some global configurations within Alpine. There is a
+help text explaining how to define all of them, which you can read by
+following this <A HREF="h_config_new_rules">link</A>.
+
+<P>&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_reply_leadin_rules =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_reply-leadin-rules"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_reply-leadin-rules"--></H1>
+
+<P> This option is used to have Alpine generate a different
+<A HREF="h_config_reply_intro"><!--#echo var="VAR_reply-leadin"--></A> string dependent either on
+the person you are replying to, or the folder where the message is being
+replied is in, or both.
+
+<P> Here there are examples of how this can be used. One can use the definition
+below to post to newsgroups and the pine-info mailing list, say:
+<P>
+_FOLDER_ << {pine-info;_NEWS_} => _REPLY_{*** _FROM_ _ADDRESS_("_FROM_" "" "(_ADDRESS_) ")wrote in_NEWS_("" " the" "") _FOLDER_ _NEWS_("" "list " "")_SMARTDATE_("Today" "today" "on _LONGDATE_"):}
+
+<P> Here there is an example that one can use to change the reply indent string
+to reply people that speak spanish.
+<P>
+_FROM_{Condorito;Quico} => _REPLY_{*** _FROM_ (_ADDRESS_) escribi&oacute; _SMARTDATE_("Today" "hoy" "en _LONGDATE_"):}
+
+<P> This configuration option is just one of many that allow you to
+override the value of some global configurations within Alpine. There is a
+help text explaining how to define all of them, which you can read by
+following this <A HREF="h_config_new_rules">link</A>.
+
+<P>&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_resub_rules =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_reply-subject-rules"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_reply-subject-rules"--></H1>
+
+<P> This option is used to have Alpine generate a different subject when
+replying rather than the one Alpine would generate automatically.
+
+<P> Here there are a couple of examples about how to use this
+configuration option:
+
+<P> In order to have messages with empty subject to be replied with the message
+"your message" use the rule<BR>
+<center>_SUBJECT_ == {} => _RESUB_{Re: your message}</center>
+
+<P> If you want to trim some parts of the subject when you reply use the
+rule<BR>
+<center>_SUBJECT_ >> {[one];two} => _SUBJECT_ := _TRIM_{[;];two}</center>
+
+<P>this rule removes the brackets "[" and "]" whenever the string "[one]"
+appears in it, it also removes the word "two" from it.
+
+<P>Another example where you may want to use this rule is when you
+correspond with people that change the reply string from &quot;Re:&quot;
+to &quot;AW:&quot; or &quot;Sv:&quot;. In this case a rule like<BR>
+<center>_SUBJECT_ >> {Sv: ;AW: } => _SUBJECT_ := _TRIM_{Sv: ;AW: }</center>
+<P>
+would eliminate undesired strings in replies.
+
+<P> Another interesting use of this option is the use of the _EXEC_ function.
+This function takes as an argument a program or a script. This program
+must take as the input a file, and write its output to that file. For example,
+below is a sample of a script that removes the letter &quot;a&quot; of a file.
+
+<PRE>
+#!/bin/sh
+sed 's/a//g' $1 > /tmp/mytest
+mv /tmp/mytest $1
+</PRE>
+
+<P>
+As you can see this script took &quot;$1&quot; as input file, the sed program
+wrote its output to /tmp/mytest, and then the move program moved the file
+/tmp/mytest to the input file &quot;$1&quot;. This is the kind of behavior
+that your program is expected to have.
+
+<P>
+The content of the input file (&quot;$1&quot; above) is the value of a token
+like _SUBJECT_. In order to indicate this, we use the notation
+
+<P>
+_SUBJECT_ := _EXEC_{/path/to/script}
+
+<P> for the action. So for example
+
+<P>
+_FOLDER_ := {sent-mail} =&gt; _SUBJECT_ := _EXEC_{/path/to/script}
+
+<P> is a valid rule.
+
+<P> You can also use this configuration option to customize reply subjects
+according to the sender of the message.
+
+<P> This configuration option is just one of many that allow you to
+override the value of some global configurations within Alpine. There is a
+help text explaining how to define all of them, which you can read by
+following this <A HREF="h_config_new_rules">link</A>.
+
+<P>&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_sort_rules =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_sort-rules"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_sort-rules"--></H1>
+
+<P> This option is used to have Alpine sort different folders in different orders
+and thus override the value already set in the
+<A HREF="h_config_sort_key"><!--#echo var="VAR_sort-key"--></A> configuration option.
+
+<P> Here's an example of the way it can be used. In this case all incoming
+folders are mailing lists, except for INBOX, so we sort INBOX by arrival
+(which is the default type of sort), but we want all the rest of mailing
+lists and newsgroups to be sorted by thread.
+
+<P>
+_COLLECTION_ >> {Incoming-Folders;News} && _FOLDER_ != {INBOX} => _SORT_{tHread}
+
+<P> Another example could be<BR>
+_FOLDER_ == {Mailing List} => _SORT_{Reverse tHread}
+
+<P> This configuration option is just one of many that allow you to
+override the value of some global configurations within Alpine. There is a
+help text explaining how to define all of them, which you can read by
+following this <A HREF="h_config_new_rules">link</A>.
+
+<P>&lt;End of help on this topic&gt;
+
+</BODY>
+</HTML>
+====== h_config_save_rules =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_save-rules"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_save-rules"--></H1>
+
+<P> This option is used to specify which folder should be used to save a
+message depending either on the folder the message is in, who the message
+is from, or text that the message contains in specific headers (Cc:,
+Subject:, etc).
+
+<P> If this option is set and the
+<A HREF="h_config_auto_read_msgs"><!--#echo var="FEAT_auto-move-read-msgs"--></A> configuration
+option is also enabled then these definitions will be used to move messages
+from your INBOX when exiting Alpine.
+
+<P>Here there are some examples<BR>
+_FLAG_ >> {D} -> Trash<BR>
+_FROM_ == {U2} -> Bono<BR>
+_FOLDER_ == {comp.mail.pine} -> pine-stuff<BR>
+_NICK_ != {} -> _NICK_/_NICK_<BR>
+_DATEISO_ >> {02-10;02-11} -> archive-oct-nov-2002
+
+<P> This configuration option is just one of many that allow you to
+override the value of some global configurations within Alpine. There is a
+help text explaining how to define all of them, which you can read by
+following this <A HREF="h_config_new_rules">link</A>.
+
+<P>&lt;End of help on this topic&gt;
+
+</BODY>
+</HTML>
+====== h_config_reply_indent_rules =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_reply-indent-rules"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_reply-indent-rules"--></H1>
+
+<P> This option is used to specify which reply-indent-string is to be used
+when replying to an e-mail. If none of the rules are successful, the result in
+the variable <a href="h_config_reply_indent_string"><!--#echo var="VAR_reply-indent-string"--></a>
+is used.
+
+<P> The associated function to this configuration option is called "RESTR" (for
+REply STRing). Some examples of its use are:<BR>
+_FROM_ == {Your Boss} => _RESTR_{"> "}<BR>
+_FROM_ == {My Wife} => _RESTR_{":* "}<BR>
+_FROM_ == {Perter Flinstone;Wilma Flinstone} => _RESTR_{"_INIT_ > "}<BR>
+
+<P> This configuration option is just one of many that allow you to
+override the value of some global configurations within Alpine. There is a
+help text explaining how to define all of them, which you can read by
+following this <A HREF="h_config_new_rules">link</A>.
+
+<P>&lt;End of help on this topic&gt;
+
+</BODY>
+</HTML>
+====== h_config_smtp_rules =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_smtp-rules"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_smtp-rules"--></H1>
+
+<P> This option is used to specify which SMTP server should be used when
+sending a message, if this rule is not defined, or the execution of the rule
+results in no server selected, then Alpine will look for
+the value from the role that is being used to compose the message. If no smtp
+server is defined in that role or you are not using a role, then Alpine will get
+the name of the server from the
+<A HREF="h_config_smtp_server">&quot;<!--#echo var="VAR_smtp-server"-->&quot;</A> configuration
+option according to the rules used in that variable.
+
+<P> The function associated to this configuration option is _SMTP_, an example
+of the use of this function is<BR>
+_ADDRESSTO_ == {peter@bedrock.com} => _SMTP_{smtp.bedrock.com}
+
+<P> This configuration option is just one of many that allow you to
+override the value of some global configurations within Alpine. There is a
+help text explaining how to define all of them, which you can read by
+following this <A HREF="h_config_new_rules">link</A>.
+
+<P>&lt;End of help on this topic&gt;
+
+</BODY>
+</HTML>
+====== h_config_startup_rules =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_startup-rules"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_startup-rules"--></H1>
+
+<P> This option is used when a folder is being opened. You can use it to specify its <A
+HREF="h_config_inc_startup"><!--#echo var="VAR_incoming-startup-rule"--></A> and override
+Alpine's global value set for all folders.
+
+<P> An example of the usage of this option is:<BR>
+_FOLDER_ == {Lynx;pine-info;_NEWS_} => _STARTUP_{first-unseen}
+
+<P> This configuration option is just one of many that allow you to
+override the value of some global configurations within Alpine. There is a
+help text explaining how to define all of them, which you can read by
+following this <A HREF="h_config_new_rules">link</A>.
+
+<P>&lt;End of help on this topic&gt;
+
+</BODY>
+</HTML>
+====== h_config_new_rules =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: New Rules Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: New Rules Explained</H1>
+
+This is a quite powerful option. Here you can define rules that override
+the values of any other option you have set in Alpine.
+
+<P>
+ For example, you can set your folders to be sorted in a certain way when
+you open them (say by Arrival). You may want, however, your newsgroups to
+be sorted by thread. The set of &quot;rules&quot; options allows you to
+configure this and many other options, including the index-format for
+specific folders, the way the subject is displayed in the index screen or
+the reply-leadin-string, to name a few.
+
+<P>
+ Every rule has three parts: a condition, a separator and an action. The
+action is what will happen if the condition of the rule is satisified.
+
+<P>
+ Here is an example:
+
+<P>
+ _FROM_ == {Fred Flinstone} =&gt; _SAVE_{Fred}
+
+<P>
+ Here the separator is "=&gt;". Whatever is to the left of the separator
+is the condition (that is _FROM_ == {Fred Flinstone}) and to the right is
+the action (_SAVE_{Fred}). The condition means that the rule will be
+applied only if the message that you are reading is from &quot;Fred
+Flinstone&quot;, and the action will be that you will be offered to save
+it in the folder &quot;Fred&quot;, whenever you press the letter
+&quot;S&quot; to save a message.
+
+<P>
+ The separator is always &quot;=&gt;&quot;, with one exception to be seen
+later. But for the most part this will be the only one you will ever need.
+
+<P>
+ Now let us see how to do it. There are 13 functions already defined for
+you. These are: _EXEC_, _INDEX_, _REPLACE_, _REPLY_, _RESUB_, _SAVE_,
+_SIGNATURE_, _SORT_, _STARTUP_, _TRIM_, _REXTRIM_, _THREADSTYLE and
+_THREADINDEX_. The parameter of a function has to be enclosed between
+&quot;{&quot; and &quot;}&quot;, so for example you can specify
+_SAVE_{saved-messages} as a valid sentence.
+
+<P>
+ Later in the document you will find examples. Here is a short
+description of what each function does:
+
+<P>
+<UL>
+<LI> _EXEC_ : This function takes as an argument a program. This program
+gets as the input a file and must rewrite its output to that file, which
+is then taken as the value to replace from the contents of that file. You
+can use this function with
+<A HREF="h_config_resub_rules"><!--#echo var="VAR_reply-subject-rules"--></A>,
+<A HREF="h_config_replace_rules"><!--#echo var="VAR_replace-rules"--></A> and
+<A HREF="h_config_forward_rules"><!--#echo var="VAR_forward-rules"--></A>.
+See the help of those options for examples of how to use this function
+and configure these rules.
+<BR>&nbsp;<BR>
+<LI> _INDEX_ : This function takes as an argument an index-format, and
+makes that the index-format for the specified folder.
+<BR>&nbsp;<BR>
+<LI> _REPLACE_ : This function replaces the subject/from of the given e-mail by
+another subject/from only when displaying the index.
+<BR>&nbsp;<BR>
+<LI> _REPLY_ : This function takes as an argument a definition of a
+reply-leadin-string and makes this the reply-leading-string of the
+specified folder or person.
+<BR>&nbsp;<BR>
+<LI> _RESTR_ : This function takes as an argument the value of the
+reply-indent-string to be used to answer the message being replied to.
+<BR>&nbsp;<BR>
+<LI> _RESUB_ : This function replaces the subject of the given e-mail by
+another subject only when replying to a message.
+<BR>&nbsp;<BR>
+<LI> _SAVE_ : The save function takes as an argument the name of a
+possibly non existing folder, whenever you want to save a message, that
+folder will be offered for you to save.
+<BR>&nbsp;<BR>
+<LI> _SIGNATURE_ : This function takes as an argument a signature file and
+uses that file as the signature for the message you are about to
+compose/reply/forward.
+<BR>&nbsp;<BR>
+<LI> _SMTP_ : This function takes as an argument the definition of a
+SMTP server.
+<BR>&nbsp;<BR>
+<LI> _SORT_ : This function takes as an argument a Sort Style, and sorts a
+specified folder in that sort order.
+<BR>&nbsp;<BR>
+<LI> _TRIM_ : This function takes as an argument a list of strings that
+you want removed from another string. At this time this only works for
+_FROM_ and _SUBJECT_.
+<BR>&nbsp;<BR>
+<LI> _REXTRIM_ : Same as _TRIM_ but its argument is one and
+only one extended regular expression.
+<BR>&nbsp;<BR>
+<LI> _STARTUP_ : This function takes as an argument an
+incoming-startup-rule, and open an specified folder using that rule.
+<BR>&nbsp;<BR>
+<LI> _THREADSTYLE_ : This function takes as an argument a
+threading-display-style and uses it to display threads in a folder.
+<BR>&nbsp;<BR>
+<LI> _THREADINDEX_ : This function takes as an argument a
+threading-index-style and uses it to display threads in a folder.
+</UL>
+
+<P>
+You must me wondering how to define the person/folder over who to apply
+the action. This is done in the condition. When you specify a rule, the
+rule is only executed if the condition is satisfied. In another words for
+the rule:
+
+<P>
+ _FROM_ == {Fred Flinstone} =&gt; _SAVE_{Fred}
+
+<P> it will only be applied if the from is &quot;Fred Flinstone&quot;. If
+the From is &quot;Wilma Flinstone&quot; the rule will be skipped.
+
+<P> In order to test a condition you can use the following tokens (in
+alphabetical order): _ADDRESS_, _CC_, _FOLDER_, _FROM_,_NICK_, _ROLE,
+_SENDER_, _SUBJECT_ and _TO_. The token will always be tested against what
+it is between &quot;{&quot; and &quot;}&quot; in the condition, this part
+of the condition is called the &quot;condition set&quot;. The definition
+of each token can be found <A HREF="h_index_tokens">here</A>.
+
+<P> A special testing token called _PROCID_ can be used to differentiate
+inside a rule, between two rules that are triggered by the same condition.
+A full explanation of the _PROCID_ token can be found in
+<A HREF="h_config_procid">this link</A>.
+
+<P> There are two more tokens related to the option
+<A HREF="h_config_key_macro_rules">key-definition-rules</A>. Those tokens
+are only specific to that option, and hence are not explained here.
+
+<P> You can also test in different ways, you can use the following
+&quot;test operands&quot;: &lt;&lt;, !&lt;, &gt;&gt;, !&gt;, == and !=.
+All of them are two strings long. Here is the meaning of them:
+
+<P>
+<UL>
+<LI> &lt;&lt; : It tests if the value of the token is contained in
+the condition set. Here for example if the condition set were equal to
+&quot;Freddy&quot;, then the condition: _NICK_ &lt;&lt; {Freddy}, would be true if
+the value of _NICK_ were &quot;Fred&quot;, &quot;red&quot; or &quot;Freddy&quot;. You are just looking
+for substrings here.
+<LI> &gt;&gt; : It tests if the value of the token contains the value of
+the condition set. Here for example if the condittion set were equal to
+&quot;Fred&quot;, then the condition: _FROM_ &gt;&gt; {Fred}, would be true if
+the value of _FROM_ were &quot;Fred Flinstone&quot; or &quot;Fred P. Flinstone&quot; or &quot;Freddy&quot;.
+<LI> == : It tests if the value of the token is exactly equal to the value
+of the set condition. For example _NICK_ == {Fred} will be false if the value
+of _NICK_ is &quot;Freddy&quot; or &quot;red&quot;.
+<LI> !&lt; : This is true only when &lt;&lt; is false and viceversa.
+<LI> !&gt; : This is true only when &gt;&gt; is false and viceversa.
+<LI> != : This is true only when == is false and viceversa.
+</UL>
+
+<P>
+ Now let us say that you want the same action to be applied to more than
+one person or folder, say you want &quot;folder1&quot; and &quot;folder2&quot; to be sorted by
+Ordered Subject upon entering. Then you can list them all of them in the
+condition part separting them by a &quot;;&quot;. Here is the way to do it.
+
+<P>
+ _FOLDER_ &lt;&lt; {folder1; folder2} =&gt; _SORT_{OrderedSubj}
+
+<P>
+ Here is the first subtelty about these definitions. Notice that the
+following rule:
+
+<P>
+ _FOLDER_ == {folder1; folder2} =&gt; _SORT_{Reverse OrderedSubj}
+
+<P> works only for &quot;folder1&quot; but not for &quot;folder2&quot;. This is because the
+comparison of the name of the folder is done with whatever is in between
+&quot;{&quot;, &quot;;&quot; or &quot;}&quot;, so in the above rule you would be testing <BR>
+&quot;folder2&quot; == &quot; folder2&quot;. The extra space makes the difference.
+The reason why the first rule does not fail is because
+&quot;folder2&quot; &lt;&lt; &quot; folder2&quot; is actually
+true. If something ever fails this may be something to look into.
+
+<P>
+ Here are a few examples of what we have talked about before.
+
+<P>
+_NICK_ == {lisa;kika} =&gt; _SAVE_{_NICK_/_NICK_} <BR>
+This means that if the nick is lisa, it will
+save the message in the folder &quot;lisa/lisa&quot;, and if the nick
+is &quot;kika&quot;, it will save the message in the folder &quot;kika/kika&quot;
+
+<P>
+_FOLDER_ == {Lynx} -&gt; lynx <BR>
+This, is an abreviation of the following rule:<BR>
+_FOLDER_ == {Lynx} =&gt; _SAVE_{lynx} <BR>
+(note the change in separator from &quot;=&gt;&quot; to &quot;-&gt;&quot;). In the future
+I will use that abreviation.
+
+<P> _FOLDER_ &lt;&lt; {comp.mail.pine; pine-info; pine-alpha} -&gt; pine <BR>
+Any message in the folders &quot;comp.mail.pine&quot;, &quot;pine-info&quot; or &quot;pine-alpha&quot;
+will be saved to the folder &quot;pine&quot;.
+
+<P> _FROM_ &lt;&lt; {Pine Master} -&gt; pine <BR>
+Any message whose From field contains
+&quot;Pine Master&quot; will be saved in the folder pine.
+
+<P> _FOLDER_ &lt;&lt; {Lynx; pine-info; comp.mail.pine} =&gt;
+_INDEX_{IMAPSTATUS MSGNO DATE FROMORTO(33%) SUBJECT(66%)} <BR> Use a
+different index-format for the folders &quot;Lynx&quot;, &quot;pine-info&quot; and
+&quot;comp.mail.pine&quot;, where the size is not present.
+
+<P> _FOLDER_ == {Lynx;pine-info} =&gt; _REPLY_{*** _FROM_ (_ADDRESS_)
+wrote in the _FOLDER_ list _SMARTDATE_(&quot;Today&quot; &quot;today&quot; &quot;on
+_LONGDATE_&quot;):}<BR> If a message is in one of the incoming folders &quot;Lynx&quot;
+or &quot;pine-info&quot;, create a reply-leadin-string that acknowledges that. Note
+the absence of &quot;,&quot; in the function _SMARTDATE_. For example answering to a
+message in the pine-info list would look like:
+
+<P>
+*** Steve Hubert (hubert@cac.washington.edu) wrote in the pine-info list today:
+
+<P>
+However replying for a message in the Lynx list would look:
+
+<P>
+*** mattack@area.com (mattack@area.com) wrote in the Lynx list today:
+
+<P>
+If you write in more than one language you can use this feature to create
+Reply-leadin-strings in different languages.
+
+<P> Note that at least for people you can create particular
+reply-leadin-string using the role features, but it does not work as this
+one does. This seems to be the right way to do it.
+
+<P> _FOLDER_ &lt;&lt; {Lynx; comp.mail.pine; pine_info; pine-alpha} =&gt;
+_SORT_{OrderedSubj}<BR> This means upon opening, sort the folders &quot;Lynx&quot;,
+&quot;comp.mail.pine&quot;, etc in ordered subject. All the others use the default
+sort order. You can not sort in reverse in this form. The possible
+arguments of this function are listed in the definition of the
+default-sort-rule (Arrival, scorE, siZe, etc).
+
+<P> The last examples use the function _TRIM_ which has a special form.
+This function can only be used in the index list.
+
+<P> _FOLDER_ &lt;&lt; {Lynx} =&gt; _SUBJECT_ := _TRIM_{lynx-dev }<BR> In
+the folder &quot;Lynx&quot; eliminate from the subject the string &quot;lynx-dev &quot; (with
+the space at the end). For example a message whose subject is &quot;Re:
+lynx-dev unvisited Visited Links&quot;, would be shown in the index with
+subject: &quot;Re: unvisited Visited Links&quot;, making the subject shorter and
+giving the same information.
+
+<P> _FROM_ &gt;&gt; {Name (Comment)} =&gt; _FROM_ :=
+_TRIM_{ (Comment)}<BR> Remove the part &quot; (Comment)&quot;
+from the _FROM_, so when displaying in the index the real From &quot;Name&quot;
+will appear.
+
+<P> _SUBJECT_ == {} =&gt; _RESUB_{Re: your mail without subject}
+If there is no subject in the message, use the subject &quot;Re: your mail
+wiyhout subject&quot; as a subject for the reply message.
+
+<P> You can add more complexity to your rules by checking more than one
+conditions before a rule is executed. For example: Assume that you want to
+answer every email that contains the string &quot;bug report&quot;, with the subject
+&quot;Re: About your bug report&quot;, you could make
+
+<P>
+_SUBJECT_ == {bug report} =&gt; _RESUB_{Re: About your _SUBJECT_}
+
+<P> The problem with this construction is that if the person emails you
+back, then the next time you answer the message the subject will be: &quot;Re:
+About your Re: About your bug report&quot;, so it grew. You may want to avoid
+this growth by using the following rule:
+
+<P>
+_SUBJECT_ &gt;&gt; {bug report} && _SUBJECT_ !&gt; {Re: } =&gt; _RESUB_{Re: About your _SUBJECT_}<BR>
+
+<P>
+which will only add the string &quot;Re: About your&quot; only the first time the
+message is replied.
+
+<P>
+ Say your personal name is &quot;Fred Flinstones&quot;, and assume that you don't
+like to see &quot;To: comp.mail.pine&quot; in every post you make to this newsgroup,
+but instead would like to see it as everyone else sees it. <BR>
+_FOLDER_ == {comp.mail.pine} && _FROM_ == {Fred Flinstones} => _FROM_ := _REPLACE_{_FROM_}
+
+<P>
+ You can also list your index by nick, in the following way:<BR>
+_NICK_ != {} => _FROM_ := _REPLACE_{_NICK_}
+
+<P>
+ If you want to open the folder &quot;pine-info&quot; in the first non-read message
+use the rule:<BR>
+_FOLDER_ == {pine-info} => _STARTUP_{first-unseen}
+
+<P>
+ If you want to move your deleted messages to a folder, called &quot;Trash&quot;, use
+the following rule:<BR>
+_FLAG_ >> {D} -> Trash
+
+<P>
+The reason why the above test is not &quot;_FLAG_ == {D}&quot; is because that would mean
+that this is the only flag set in the message. It's better to test by containment in this case.
+
+<P> If you want to use a specific signature when you are in a specific collection
+use the following rule:<BR>
+_COLLECTION_ == {Mail} => _SIGNATURE_{/full/path/to/.signature}
+
+<P> Finally about the question of which rule will be executed. Only the
+first rule that matches will be executed. It is important to notice though
+that &quot;saving&quot; rules do not compete with &quot;sorting&quot; rules. So the first
+&quot;saving&quot; rule that matches will be executed in the case of saving and so
+on.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
====== h_config_char_set =====
<HTML>
<HEAD>
@@ -22827,6 +24234,43 @@ That won't work because spell works in a different way.
&lt;End of help on this topic&gt;
</BODY>
</HTML>
+====== h_config_special_text_to_color =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_special-text-color"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_special-text-color"--></H1>
+
+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).
+
+<P>
+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 <A
+HREF="h_regex_text">set</A> of regular expressions that are matched.
+
+<P>
+Pine will use the colors defined in the
+<A HREF="h_config_special_text_color">Special Text Color</A> variable.
+to paint any match.
+
+<P>
+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.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
====== h_config_display_filters =====
<HTML>
<HEAD>
@@ -22991,6 +24435,12 @@ specified.
Command Modifying Tokens:
<DL>
+<DT>_ADDRESS_</DT>
+<DD>When the command is executed, this token is replaced
+with the address of the person sending the message in the format
+mailbox@host.
+</DD>
+
<DT>_RECIPIENTS_</DT>
<DD>When the command is executed, this token is replaced
with the space delimited list of recipients of the
@@ -26103,6 +27553,76 @@ the From field is used to show the relationships instead of the Subject field.
&lt;End of help on this topic&gt;
</BODY>
</HTML>
+====== h_config_thread_display_style_rule =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: Threading-Display-Style-Rule</TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: Threading-Display-Style-Rule</H1>
+
+This option is very similar to <A HREF="h_config_thread_disp_style">
+<!--#echo var="VAR_threading-display-style"--></A>, but it is a rule which specifies the
+display styles for a thread that you want displayed in a specific
+folder or collection.
+<P>
+The token to be used in this function is _THREADSTYLE_. Here there is
+an example of its use
+<P>
+_FOLDER_ == {pine-info} => _THREADSTYLE_{mutt-like}
+<P>
+The values that can be given for the _THREADSTYLE_ function are the
+values of the threading-display-style function, which can be found
+listed in the <A HREF="h_config_thread_disp_style">threading-display-style</A>
+configuration option.
+
+<P> This configuration option is just one of many that allow you to
+override the value of some global configurations within Alpine. There is a
+help text explaining how to define all of them, which you can read by
+following this <A HREF="h_config_new_rules">link</A>.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_thread_index_style_rule =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: Threading-Index-Style-Rule</TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: Threading-Index-Style-Rule</H1>
+
+This option is very similar to <A HREF="h_config_thread_index_style">
+<!--#echo var="VAR_threading-index-style"--></A>, but it is a rule which specifies the
+index styles for a thread that you want displayed in a specific
+folder or collection.
+<P>
+The token to be used in this function is _THREADINDEX_. Here there is
+an example of its use
+<P>
+_FOLDER_ == {pine-info} => _THREADINDEX_{regular-index-with-expanded-threads}
+<P>
+The values that can be given for the _THREADINDEX_ function are the
+values of the threading-index-display function, which can be found
+listed in the <A HREF="h_config_thread_index_style"><!--#echo var="VAR_threading-index-style"--></A>
+configuration option.
+
+<P> This configuration option is just one of many that allow you to
+override the value of some global configurations within Alpine. There is a
+help text explaining how to define all of them, which you can read by
+following this <A HREF="h_config_new_rules">link</A>.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
====== h_config_pruning_rule =====
<HTML>
<HEAD>
@@ -28009,6 +29529,22 @@ See also <A HREF="h_config_allow_chg_from">&quot;<!--#echo var="FEAT_allow-chang
&lt;End of help on this topic&gt;
</BODY>
</HTML>
+====== h_config_use_domain =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_return-path-uses-domain-name"--> </TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_return-path-uses-domain-name"--></H1>
+
+If you enable this configuration option Pine will use your domain name and your
+username in that domain name to construct your Return-Path header, if not Pine
+will use the address that you have set in the From: field to construct it.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
====== h_config_use_sender_not_x =====
<HTML>
<HEAD>
@@ -28605,6 +30141,71 @@ of flowed text.
&lt;End of help on this topic&gt;
</BODY>
</HTML>
+====== h_config_alt_reply_menu =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_alternate-reply-menu"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_alternate-reply-menu"--></H1>
+
+This feature controls the menu that is displayed when Reply is selected.
+If set, a list of options will be presented, with each option representing
+the type of composition that could be used. This feature is most useful
+for users who want to avoid being prompted with each option separately, or
+would like to override some defaults set in your configuration for the
+message that you are replying (e.g. you may have set the option to strip
+signatures, but for the message you are answering you would like not to do
+that)
+
+<P>
+The way this feature works is as follows. Initially you get the question
+if you want to include the message, and in the menu you will see several
+options, each option is accompanied by some text explaining what will
+happen if you press the associated command. For example, if you read the
+text &quot;S Strip Sig&quot;, it means that if you press the letter
+&quot;S&quot; the signature will be stripped off the message you are
+replying. Observer that the menu will change to
+&quot;S No Strip&quot;, which means that if you press &quot;S&quot;, the
+signature will not be stripped off from the message. Your choices are
+activated when you press RETURN.
+
+<P>
+Another way to remember what Pine will do, is that what will be done is
+exactly the opposite of what you read in the menu.
+
+<P>
+The possible options are:
+
+<OL>
+<LI> A: This determines if Pine will include or not the attachments sent to
+you in the message that you are replying. By default Pine will use the value
+of the configuration option
+<A HREF="h_config_attach_in_reply"><!--#echo var="FEAT_include-attachments-in-reply"--></A>, but
+you can use this option to override such behavior in a per message basis.
+
+<LI> F: To decide if you want to send flowed text or not. This option appears
+unless you have quelled sending flowed text.
+
+<LI> H: This option determines if the headers of a message are to be
+included in the body of the message that is being replied. By default Pine
+will use the value of the configuration option
+<A HREF="h_config_include_header"><!--#echo var="FEAT_include-header-in-reply"--></A>, but
+you can use this option to override such behavior in a per message basis.
+
+<LI> R: To set a role, if you do not want Pine to set one automatically for you
+or would like to set one when you can not select any.
+
+<LI> S: To strip the signature from a message, only available is the feature
+ <a href="h_config_sigdashes"><!--#echo var="FEAT_enable-sigdashes"--></a> or the
+<a href="h_config_strip_sigdashes"><!--#echo var="FEAT_strip-from-sigdashes-on-reply"--></a> option are
+enabled.
+
+</OL>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
====== h_config_del_from_dot =====
<HTML>
<HEAD>
@@ -29128,6 +30729,38 @@ Ctrl-B key can be used to select the previous web hostnames in the same way.
&lt;End of help on this topic&gt;
</BODY>
</HTML>
+====== h_config_enable_long_url =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_enable-msg-view-long-url"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_enable-msg-view-long-url"--></H1>
+
+This feature modifies the behavior of Alpine's MESSAGE TEXT screen. When this feature
+is set alpine will attempt to recognize long urls (those that spread over several
+lines in the text) for the HTTP protocol, even when they have not been enclosed between
+delimiters &quot;&lt;&quot; and &quot;&gt;&quot;.
+
+<P>The normal behavior in Alpine is that if a URL is preceeded by the &quot;&lt;&quot;
+character and this URL was not finished before the end of the line, then a
+continuation of the URL is searched in the following line(s). Normally, this type of
+URLs will be ended by the &quot;&gt;&quot; character, and if it is not, there is a
+possibility of including erroneous text into the URL.
+
+<P>Enabling this feature will make Alpine search for a continuation of certain URLs in
+lines following its location. This will be of great help most times, but in some cases
+the algorithm will catch some text into the URL that is not part of the URL.
+
+<P>If you find that Alpine failed to recognize correctly a URL simply edit the URL before
+passing it to your browser.
+
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
====== h_config_enable_view_addresses =====
<HTML>
<HEAD>
@@ -29162,6 +30795,27 @@ Ctrl-B key can be used to select the previous web hostnames in the same way.
&lt;End of help on this topic&gt;
</BODY>
</HTML>
+====== h_config_circular_tab =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_enable-circular-tab"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_enable-circular-tab"--></H1>
+
+<P>
+This Feature is like
+<A HREF="h_config_auto_open_unread">&quot;<!--#echo var="FEAT_auto-open-next-unread"-->&quot;</A>,
+in the sense that you can use TAB to browse through all of your Incoming
+Folders checking for new mail. Once it gets to the last folder of the
+collection it goes back to check again until it returns to the original
+folder where it started.
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
====== h_config_enable_view_arrows =====
<HTML>
<HEAD>
@@ -29435,6 +31089,49 @@ than across the columns as is the default.
&lt;End of help on this topic&gt;
</BODY>
</HTML>
+====== h_config_courier_list =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_courier-folder-list"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_courier-folder-list"--></H1>
+
+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 &quot;#mc/&quot;
+prefix instead of the &quot;#md/&quot; prefix. If you use this alternate
+prefix, then this feature applies to you, otherwise you can safely ignore
+the text that follows.
+<P>
+Depending on if you have enabled the option
+<a href="h_config_separate_fold_dir_view"><!--#echo var="FEAT_separate-folder-and-directory-entries"--></a>
+a folder may be listed as &quot;folder[.]&quot;, or as two entries in the
+list by &quot;folder&quot; and &quot;folder.&quot;.
+<P>
+If this option is disabled, Pine will list local folders that are in Courier
+style format, as &quot;folder&quot;, and those that are also directories as
+&quot;folder[.]&quot;. This makes the default display cleaner.
+<P>
+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 &quot;foo&quot; simply add &quot;foo.bar&quot; directly. This will
+create the directory &quot;foo&quot; and the subfolder &quot;bar&quot; of it).
+<P>
+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.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
====== h_config_verbose_post =====
<HTML>
<HEAD>
@@ -29589,6 +31286,29 @@ them as deleted in the INBOX. Messages in the INBOX marked with an
&lt;End of help on this topic&gt;
</BODY>
</HTML>
+====== h_config_auto_read_msgs_rules =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: auto-move-read-msgs-using-rules</TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: auto-move-read-msgs-using-rules</H1>
+This feature controls an aspect of Alpine's behavior upon quitting. If set,
+and the
+<A HREF="h_config_read_message_folder">&quot;<!--#echo var="VAR_read-message-folder"-->&quot;</A>
+option is also set, then Alpine will automatically transfer all read
+messages to the designated folder using the rules that you have defined in
+your
+<A HREF="h_config_save_rules">&quot;<!--#echo var="VAR_save-rules"-->&quot;</A> and mark
+them as deleted in the INBOX. Messages in the INBOX marked with an
+&quot;N&quot; (meaning New, or unseen) are not affected.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
====== h_config_auto_fcc_only =====
<HTML>
<HEAD>
@@ -30039,6 +31759,23 @@ Reply Use, Forward Use, and Compose Use.
&lt;End of help on this topic&gt;
</BODY>
</HTML>
+====== h_config_enhanced_thread =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_enhanced-fancy-thread-support"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_enhanced-fancy-thread-support"--></H1>
+
+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.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
====== h_config_news_cross_deletes =====
<HTML>
<HEAD>
@@ -30517,6 +32254,40 @@ but that is not implemented.
&lt;End of help on this topic&gt;
</BODY>
</HTML>
+====== h_config_ignore_size =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_ignore-size-changes"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_ignore-size-changes"--></H1>
+
+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.
+
+<P>
+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.
+
+<P>
+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.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
====== h_config_force_low_speed =====
<HTML>
<HEAD>
@@ -31204,6 +32975,30 @@ to see the available Editing and Navigation commands.
&lt;End of help on this topic&gt;
</BODY>
</HTML>
+====== h_config_special_text_color =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: Special Text Color</TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: Special Text Color</H1>
+
+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.
+
+<P>
+<A HREF="h_color_setup">Descriptions of the available commands</A>
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
====== h_config_index_arrow_color =====
<HTML>
<HEAD>
@@ -33013,6 +34808,28 @@ messages.
&lt;End of help on this topic&gt;
</BODY>
</HTML>
+====== h_config_mark_for_group =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_mark-for-me-in-group"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_mark-for-me-in-group"--></H1>
+
+This feature affects Alpine's MESSAGE INDEX display.
+By default, a '+' is displayed in the first column if the
+message is addressed directly to you.
+When this feature is set and the message is addressed to you as part of a group message
+(that is, your address appears in the To: field, but there is more than one recipient), then a
+'.' character is displayed instead.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
====== h_config_mark_for_cc =====
<HTML>
<HEAD>
@@ -33022,7 +34839,7 @@ messages.
<H1>FEATURE: <!--#echo var="FEAT_mark-for-cc"--></H1>
This feature affects Alpine's MESSAGE INDEX display.
-By default, a '+' is displayed in the first column if the
+By default, a '+' or a '.' is displayed in the first column if the
message is addressed directly to you.
When this feature is set and the message is not addressed to you, then a
'-' character is displayed if the message is instead Cc'd directly
diff --git a/pith/pineelt.h b/pith/pineelt.h
index e44ae37a..d88f5fb9 100644
--- a/pith/pineelt.h
+++ b/pith/pineelt.h
@@ -40,6 +40,7 @@ typedef struct pine_elt {
PINETHRD_S *pthrd;
PARTEX_S *exceptions;
ICE_S *ice;
+ char *firsttext;
/* per-message pine state bits */
unsigned int hidden:1;
unsigned int excluded:1;
diff --git a/pith/reply.c b/pith/reply.c
index 3445097f..348ebf26 100644
--- a/pith/reply.c
+++ b/pith/reply.c
@@ -46,6 +46,8 @@ static char rcsid[] = "$Id: reply.c 1074 2008-06-04 00:08:43Z hubert@u.washingto
#include "../pith/ablookup.h"
#include "../pith/mailcmd.h"
#include "../pith/margin.h"
+#include "../pith/copyaddr.h"
+#include "../pith/rules.h"
/*
@@ -814,8 +816,27 @@ char *
reply_quote_str(ENVELOPE *env)
{
char *prefix, *repl, *p, buf[MAX_PREFIX+1], pbf[MAX_SUBSTITUTION+1];
+ char reply_string[MAX_PREFIX+1];
+
+ { RULE_RESULT *rule;
+ rule = get_result_rule(V_REPLY_INDENT_RULES, FOR_COMPOSE , env);
+ if (rule){
+ strncpy(reply_string,rule->result,sizeof(reply_string));
+ reply_string[sizeof(reply_string)-1] = '\0';
+ if (rule->result)
+ fs_give((void **)&rule->result);
+ fs_give((void **)&rule);
+ }
+ else
+ if ((ps_global->VAR_REPLY_STRING) && (ps_global->VAR_REPLY_STRING[0])){
+ strncpy(reply_string,ps_global->VAR_REPLY_STRING, sizeof(reply_string)-1);
+ reply_string[sizeof(reply_string)-1] = '\0';
+ }
+ else
+ strncpy(reply_string,"> ",sizeof("> "));
+ }
- strncpy(buf, ps_global->VAR_REPLY_STRING, sizeof(buf)-1);
+ strncpy(buf, reply_string, sizeof(buf)-1);
buf[sizeof(buf)-1] = '\0';
/* set up the prefix to quote included text */
@@ -867,10 +888,29 @@ reply_quote_str(ENVELOPE *env)
int
reply_quote_str_contains_tokens(void)
{
- return(ps_global->VAR_REPLY_STRING && ps_global->VAR_REPLY_STRING[0] &&
- (strstr(ps_global->VAR_REPLY_STRING, from_token) ||
- strstr(ps_global->VAR_REPLY_STRING, nick_token) ||
- strstr(ps_global->VAR_REPLY_STRING, init_token)));
+ char *reply_string;
+
+ reply_string = (char *) malloc( 80*sizeof(char));
+ { RULE_RESULT *rule;
+ rule = get_result_rule(V_REPLY_INDENT_RULES, FOR_COMPOSE, NULL);
+ if (rule){
+ reply_string = cpystr(rule->result);
+ if (rule->result)
+ fs_give((void **)&rule->result);
+ fs_give((void **)&rule);
+ }
+ else
+ if ((ps_global->VAR_REPLY_STRING) && (ps_global->VAR_REPLY_STRING[0])){
+ strncpy(reply_string,ps_global->VAR_REPLY_STRING, sizeof(reply_string)-1);
+ reply_string[sizeof(reply_string)-1] = '\0';
+ }
+ else
+ reply_string = cpystr("> ");
+ }
+ return(reply_string && reply_string[0] &&
+ (strstr(reply_string, from_token) ||
+ strstr(reply_string, nick_token) ||
+ strstr(reply_string, init_token)));
}
@@ -972,7 +1012,7 @@ reply_body(MAILSTREAM *stream, ENVELOPE *env, struct mail_bodystruct *orig_body,
if(!orig_body
|| orig_body->type == TYPETEXT
|| reply_raw_body
- || F_OFF(F_ATTACHMENTS_IN_REPLY, ps_global)){
+ || !ps_global->reply.attach){
char *charset = NULL;
/*------ Simple text-only message ----*/
@@ -980,7 +1020,7 @@ reply_body(MAILSTREAM *stream, ENVELOPE *env, struct mail_bodystruct *orig_body,
body->type = TYPETEXT;
body->contents.text.data = msgtext;
reply_delimiter(env, role, pc);
- if(F_ON(F_INCLUDE_HEADER, ps_global))
+ if(ps_global->reply.inchdr)
reply_forward_header(stream, msgno, sect_prefix,
env, pc, prefix);
@@ -1038,7 +1078,7 @@ reply_body(MAILSTREAM *stream, ENVELOPE *env, struct mail_bodystruct *orig_body,
if(reply_body_text(orig_body, &tmp_body)){
reply_delimiter(env, role, pc);
- if(F_ON(F_INCLUDE_HEADER, ps_global))
+ if(ps_global->reply.inchdr)
reply_forward_header(stream, msgno, sect_prefix,
env, pc, prefix);
@@ -1076,7 +1116,7 @@ reply_body(MAILSTREAM *stream, ENVELOPE *env, struct mail_bodystruct *orig_body,
body->nested.part->body.subtype = cpystr("Plain");
}
reply_delimiter(env, role, pc);
- if(F_ON(F_INCLUDE_HEADER, ps_global))
+ if(ps_global->reply.inchdr)
reply_forward_header(stream, msgno, sect_prefix,
env, pc, prefix);
@@ -1099,7 +1139,7 @@ reply_body(MAILSTREAM *stream, ENVELOPE *env, struct mail_bodystruct *orig_body,
int partnum;
reply_delimiter(env, role, pc);
- if(F_ON(F_INCLUDE_HEADER, ps_global))
+ if(ps_global->reply.inchdr)
reply_forward_header(stream, msgno, sect_prefix,
env, pc, prefix);
@@ -1334,6 +1374,10 @@ get_addr_data(ENVELOPE *env, IndexColType type, char *buf, size_t maxlen)
buf[0] = '\0';
switch(type){
+ case iFfrom:
+ addr = env && env->sparep ? env->sparep : NULL;
+ break;
+
case iFrom:
addr = env ? env->from : NULL;
break;
@@ -1719,21 +1763,150 @@ get_reply_data(ENVELOPE *env, ACTION_S *role, IndexColType type, char *buf, size
break;
+ case iProcid:
+ if(ps_global->procid){
+ strncpy(buf, ps_global->procid, maxlen);
+ buf[maxlen] = '\0';
+ }
+ break;
+
+ case iRole:
+ if (ps_global->role){
+ strncpy(buf, ps_global->role, maxlen);
+ buf[maxlen] = '\0';
+ }
+ break;
+
+ case iRoleNick:
+ if(role && role->nick){
+ strncpy(buf, role->nick, maxlen);
+ buf[maxlen] = '\0';
+ }
+ break;
+
+ case iPkey:
+ if(ps_global->pressed_key){
+ strcpy(buf, ps_global->pressed_key);
+ buf[maxlen] = '\0';
+ }
+ break;
+
+ case iScreen:
+ if(ps_global->screen_name){
+ strncpy(buf, ps_global->screen_name, maxlen);
+ buf[maxlen] = '\0';
+ }
+ break;
+
+ case iFfrom:
case iFrom:
case iTo:
case iCc:
case iSender:
case iRecips:
case iInit:
+ if (env)
get_addr_data(env, type, buf, maxlen);
break;
- case iRoleNick:
- if(role && role->nick){
- strncpy(buf, role->nick, maxlen);
- buf[maxlen] = '\0';
- }
- break;
+ case iFolder:
+ if(ps_global->cur_folder){
+ strncpy(buf,ps_global->cur_folder, maxlen);
+ buf[maxlen] = '\0';
+ }
+ break;
+
+ case iCollection:
+ if(ps_global->context_current->nickname){
+ strncpy(buf,ps_global->context_current->nickname, maxlen);
+ buf[maxlen] = '\0';
+ }
+ break;
+
+ case iFlag:
+ {MAILSTREAM *stream = ps_global->mail_stream;
+ MSGNO_S *msgmap = NULL;
+ long msgno;
+ MESSAGECACHE *mc;
+ strncpy(buf, "_FLAG_", maxlen); /* default value */
+ if (stream){
+ msgmap = sp_msgmap(stream);
+ msgno = mn_m2raw(msgmap, rules_cursor_pos(stream));
+ if (msgno > 0L) mc = stream ? mail_elt(stream, msgno) : NULL;
+ if (mc)
+ sprintf(buf,"%s%s%s%s",mc->flagged ? "*" : "",
+ mc->recent ? (mc->seen ? "R" : "N") : (mc->seen) ? "R" : "U",
+ mc->answered ? "A" : "",
+ mc->deleted ? "D" : "" );
+ }
+ buf[maxlen] = '\0';
+ }
+ break;
+
+ case iNick:
+ {
+ ADDRESS *tmp_adr = NULL;
+ if (env){
+ tmp_adr = env->from ? copyaddr(env->from)
+ : env->sender ? copyaddr(env->sender) : NULL;
+ get_nickname_from_addr(tmp_adr,buf,maxlen);
+ mail_free_address(&tmp_adr);
+ }
+ }
+ break;
+
+ case iAddressCc:
+ case iAddressRecip:
+ case iAddressTo:
+ case iFadd:
+ {
+ int plen = 0; /* partial length */
+ ADDRESS *sparep2 = (type == iAddressTo || type == iAddressRecip)
+ ? ((env && env->to)
+ ? copyaddrlist(env->to)
+ : NULL)
+ : (type == iAddressCc)
+ ? ((env && env->cc)
+ ? copyaddrlist(env->cc)
+ : NULL)
+ : ((env && env->sparep)
+ ? copyaddr((ADDRESS *)env->sparep)
+ : NULL);
+ ADDRESS *sparep;
+
+ if (type == iAddressRecip){
+ ADDRESS *last_to = NULL;
+
+ for(last_to = sparep2;last_to && last_to->next; last_to= last_to->next);
+
+ /* Make the end of To list point to cc list */
+ if(last_to)
+ last_to->next = (env && env->cc ? copyaddrlist(env->cc) : NULL);
+
+ }
+ sparep = sparep2;
+ for(; sparep ; sparep = sparep->next)
+ if(sparep && sparep->mailbox && sparep->mailbox[0] &&
+ (plen ? plen + 1 : plen) + strlen(sparep->mailbox) <= maxlen){
+ if (plen == 0)
+ strcpy(buf, sparep->mailbox);
+ else{
+ strcat(buf, " ");
+ strcat(buf, sparep->mailbox);
+ }
+ if(sparep->host &&
+ sparep->host[0] &&
+ sparep->host[0] != '.' &&
+ strlen(buf) + strlen(sparep->host) + 1 <= maxlen){
+ strcat(buf, "@");
+ strcat(buf, sparep->host);
+ }
+ plen = strlen(buf);
+ }
+ mail_free_address(&sparep2);
+ }
+
+ break;
case iNewLine:
if(maxlen >= strlen(NEWLINE)){
@@ -1762,6 +1935,11 @@ get_reply_data(ENVELOPE *env, ACTION_S *role, IndexColType type, char *buf, size
break;
+ case iLcc: /* fake it, there are not enough spare pointers */
+ if (env && env->date)
+ sprintf(buf,"%s",env->date);
+ break;
+
case iNews:
case iCurNews:
get_news_data(env, type, buf, maxlen);
@@ -1811,6 +1989,14 @@ get_reply_data(ENVELOPE *env, ACTION_S *role, IndexColType type, char *buf, size
break;
+ case iOpeningText:
+ case iOpeningTextNQ:
+ if(env && env->sparep){
+ strncpy(buf, ((SPAREP_S *)env->sparep)->value, maxlen);
+ buf[maxlen] = '\0';
+ }
+ break;
+
case iSubject:
if(env && env->subject){
size_t n, len;
@@ -1869,7 +2055,18 @@ reply_delimiter(ENVELOPE *env, ACTION_S *role, gf_io_t pc)
if(!env)
return;
- strncpy(buf, ps_global->VAR_REPLY_INTRO, MAX_DELIM);
+ { RULE_RESULT *rule;
+ rule = get_result_rule(V_REPLY_LEADIN_RULES, FOR_REPLY_INTRO, env);
+ if(rule){
+ strncpy(buf, rule->result, MAX_DELIM);
+ if (rule->result)
+ fs_give((void **)&rule->result);
+ fs_give((void **)&rule);
+ }
+ else
+ strncpy(buf, ps_global->VAR_REPLY_INTRO, MAX_DELIM);
+ }
+
buf[MAX_DELIM] = '\0';
/* preserve exact default behavior from before */
if(!strcmp(buf, DEFAULT_REPLY_INTRO)){
@@ -2128,6 +2325,7 @@ forward_subject(ENVELOPE *env, int flags)
{
size_t l;
char *p, buftmp[MAILTMPLEN];
+ RULE_RESULT *rule;
if(!env)
return(NULL);
@@ -2135,9 +2333,20 @@ forward_subject(ENVELOPE *env, int flags)
dprint((9, "checking subject: \"%s\"\n",
env->subject ? env->subject : "NULL"));
- if(env->subject && env->subject[0]){ /* add (fwd)? */
- snprintf(buftmp, sizeof(buftmp), "%s", env->subject);
- buftmp[sizeof(buftmp)-1] = '\0';
+ buftmp[0] = '\0';
+ ps_global->procid = cpystr("fwd-subject");
+ if (rule = get_result_rule(V_FORWARD_RULES,FOR_COMPOSE, env)){
+ sprintf(buftmp, "%.200s", rule->result);
+ if(rule->result)
+ fs_give((void **)&rule->result);
+ fs_give((void **)&rule);
+ }
+ else if(env->subject)
+ sprintf(buftmp, "%.200s", env->subject);
+ buftmp[sizeof(buftmp)-1] = '\0';
+ fs_give((void **)&ps_global->procid);
+
+ if(buftmp[0]){ /* add (fwd)? */
/* decode any 8bit (copy to the temp buffer if decoding doesn't) */
if(rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf,
SIZEOF_20KBUF, buftmp) == (unsigned char *) buftmp)
@@ -2638,9 +2847,12 @@ get_body_part_text(MAILSTREAM *stream, struct mail_bodystruct *body,
* tied our hands, alter the prefix to continue flowed
* formatting...
*/
- if(flow_res)
+ if(flow_res && !ps_global->reply.no_send_flowed)
wrapflags |= GFW_FLOW_RESULT;
+ filters[filtcnt].filter = gf_quote_test;
+ filters[filtcnt++].data = gf_line_test_opt(select_quote, NULL);
+
filters[filtcnt].filter = gf_wrap;
/*
* The 80 will cause longer lines than what is likely
@@ -2674,9 +2886,9 @@ get_body_part_text(MAILSTREAM *stream, struct mail_bodystruct *body,
* We also want to fold "> " quotes so we get the
* attributions correct.
*/
- if(flow_res && prefix && !strucmp(prefix, "> "))
+ if(flow_res && !ps_global->reply.no_send_flowed && prefix && !strucmp(prefix, "> "))
*(prefix_p = prefix + 1) = '\0';
-
+ ps_global->reply.no_send_flowed = 0; /* reset for next call */
if(!(wrapflags & GFW_FLOWED)
&& flow_res){
filters[filtcnt].filter = gf_line_test;
@@ -2709,9 +2921,7 @@ get_body_part_text(MAILSTREAM *stream, struct mail_bodystruct *body,
}
if(prefix){
- if(ps_global->full_header != 2
- && (F_ON(F_ENABLE_SIGDASHES, ps_global)
- || F_ON(F_ENABLE_STRIP_SIGDASHES, ps_global))){
+ if(ps_global->reply.strip){
dashdata = 0;
filters[filtcnt].filter = gf_line_test;
filters[filtcnt++].data = gf_line_test_opt(sigdash_strip, &dashdata);
@@ -2736,7 +2946,7 @@ get_body_part_text(MAILSTREAM *stream, struct mail_bodystruct *body,
dq.do_color = 0;
dq.delete_all = 1;
- filters[filtcnt].filter = gf_line_test;
+ filters[filtcnt].filter = gf_quote_test;
filters[filtcnt++].data = gf_line_test_opt(delete_quotes, &dq);
}
diff --git a/pith/save.c b/pith/save.c
index 957e163b..22cfa4a6 100644
--- a/pith/save.c
+++ b/pith/save.c
@@ -954,7 +954,7 @@ save(struct pine *state, MAILSTREAM *stream, CONTEXT_S *context, char *folder,
*date = '\0';
rv = save_fetch_append(stream, mn_m2raw(msgmap, i),
- NULL, save_stream, save_folder, context,
+ NULL, save_stream, folder, context,
mc ? mc->rfc822_size : 0L, flags, date, so);
if(flags)
@@ -1157,6 +1157,7 @@ long save_fetch_append_cb(MAILSTREAM *stream, void *data, char **flags,
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/pith/send.c b/pith/send.c
index a0c60439..3cca3365 100644
--- a/pith/send.c
+++ b/pith/send.c
@@ -44,6 +44,7 @@ static char rcsid[] = "$Id: send.c 1204 2009-02-02 19:54:23Z hubert@u.washington
#include "../pith/ablookup.h"
#include "../pith/sort.h"
#include "../pith/smime.h"
+#include "../pith/rules.h"
#include "../c-client/smtp.h"
#include "../c-client/nntp.h"
@@ -53,7 +54,7 @@ static char rcsid[] = "$Id: send.c 1204 2009-02-02 19:54:23Z hubert@u.washington
/* name::type::canedit::writehdr::localcopy::rcptto */
PINEFIELD pf_template[] = {
{"X-Auth-Received", FreeText, 0, 1, 1, 0}, /* N_AUTHRCVD */
- {"From", Address, 0, 1, 1, 0},
+ {"From", Address, 1, 1, 1, 0},
{"Reply-To", Address, 0, 1, 1, 0},
{TONAME, Address, 1, 1, 1, 1},
{CCNAME, Address, 1, 1, 1, 1},
@@ -257,6 +258,13 @@ postponed_stream(MAILSTREAM **streamp, char *mbox, char *type, int checknmsgs)
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.
@@ -1229,7 +1237,7 @@ pine_new_env(ENVELOPE *outgoing, char **fccp, char ***tobufpp, PINEFIELD *custom
*p = *(p+4);
pf->type = pf_template[i].type;
- pf->canedit = pf_template[i].canedit;
+ pf->canedit = (i == N_FROM) ? CAN_EDIT(ps_global) : pf_template[i].canedit;
pf->rcptto = pf_template[i].rcptto;
pf->writehdr = pf_template[i].writehdr;
pf->localcopy = pf_template[i].localcopy;
@@ -1738,9 +1746,9 @@ call_mailer(METAENV *header, struct mail_bodystruct *body, char **alt_smtp_serve
char error_buf[200], *error_mess = NULL, *postcmd;
ADDRESS *a;
ENVELOPE *fake_env = NULL;
- int addr_error_count, we_cancel = 0;
+ int addr_error_count, we_cancel = 0, choice, num_rules = 0, added_rules = -1;
long smtp_opts = 0L;
- char *verbose_file = NULL;
+ char *verbose_file = NULL, **smtp_list;
BODY *bp = NULL;
PINEFIELD *pf;
BODY *origBody = body;
@@ -1893,20 +1901,49 @@ call_mailer(METAENV *header, struct mail_bodystruct *body, char **alt_smtp_serve
* OK, who posts what? We tried an mta_handoff above, but there
* was either none specified or we decided not to use it. So,
* if there's an smtp-server defined anywhere,
+ * First we check for rules and make a list using the rules.
*/
- if(alt_smtp_servers && alt_smtp_servers[0] && alt_smtp_servers[0][0]){
- /*---------- SMTP ----------*/
- dprint((4, "call_mailer: via TCP (%s)\n",
- alt_smtp_servers[0]));
- TIME_STAMP("smtp-open start (tcp)", 1);
- sending_stream = smtp_open(alt_smtp_servers, smtp_opts);
+ if(ps_global->VAR_SMTP_RULES && ps_global->VAR_SMTP_RULES[0]
+ && ps_global->VAR_SMTP_RULES[0][0])
+ while (ps_global->VAR_SMTP_RULES[num_rules]) num_rules++;
+
+ if(num_rules){
+ int i, j;
+
+ added_rules = 0;
+ smtp_list = (char **) fs_get ((num_rules + 1)*sizeof(char*));
+ for (i = 0, j = 0; i < num_rules; i++){
+ RULELIST *rule = get_rulelist_from_code(V_SMTP_RULES,
+ ps_global->rule_list);
+ RULE_S *prule = get_rule(rule, i);
+ if(prule){
+ char *rule_result = process_rule(prule, FOR_COMPOSE, header->env);
+ if(rule_result && *rule_result){
+ smtp_list[j++] = cpystr(rule_result);
+ added_rules++;
+ }
+ }
+ }
+ }
+
+ if (added_rules < 0){
+ smtp_list = (char **) fs_get (sizeof(char*));
+ added_rules = 0;
}
- else if(ps_global->VAR_SMTP_SERVER && ps_global->VAR_SMTP_SERVER[0]
- && ps_global->VAR_SMTP_SERVER[0][0]){
- /*---------- SMTP ----------*/
- dprint((4, "call_mailer: via TCP\n"));
- TIME_STAMP("smtp-open start (tcp)", 1);
- sending_stream = smtp_open(ps_global->VAR_SMTP_SERVER, smtp_opts);
+ smtp_list[added_rules] = NULL;
+
+ choice = smtp_list && smtp_list[0] && smtp_list[0][0] ? 3 :
+ (alt_smtp_servers && alt_smtp_servers[0] && alt_smtp_servers[0][0] ? 2 :
+ (ps_global->VAR_SMTP_SERVER && ps_global->VAR_SMTP_SERVER[0]
+ && ps_global->VAR_SMTP_SERVER[0][0] ? 1 : -1));
+
+ if(choice > 0){
+ /*---------- SMTP ----------*/
+ dprint((4, "call_mailer: via TCP (%s)\n",smtp_list[0]));
+ TIME_STAMP("smtp-open start (tcp)", 1);
+ sending_stream = smtp_open(choice == 3 ? smtp_list
+ : (choice == 2 ? alt_smtp_servers
+ : ps_global->VAR_SMTP_SERVER), smtp_opts);
}
else if((postcmd = smtp_command(ps_global->c_client_error, sizeof(ps_global->c_client_error))) != NULL){
char *cmdlist[2];
@@ -2142,6 +2179,8 @@ call_mailer(METAENV *header, struct mail_bodystruct *body, char **alt_smtp_serve
if(error_mess){
q_status_message(SM_ORDER | SM_DING, 4, 7, error_mess);
dprint((1, "call_mailer ERROR: %s\n", error_mess));
+ if (ps_global->send_immediately)
+ printf("%s\n",error_mess);
}
return(-1);
diff --git a/pith/send.h b/pith/send.h
index 69d763fc..ee3245e5 100644
--- a/pith/send.h
+++ b/pith/send.h
@@ -159,6 +159,8 @@ struct local_message_copy {
unsigned text_written:1;
};
+#define CAN_EDIT(x) (!((x)->never_allow_changing_from) && \
+ F_ON(F_ALLOW_CHANGING_FROM, (x)))
#define TONAME "To"
#define CCNAME "cc"
diff --git a/pith/sort.c b/pith/sort.c
index 68a9c10c..4d75012f 100644
--- a/pith/sort.c
+++ b/pith/sort.c
@@ -30,7 +30,7 @@ static char rcsid[] = "$Id: sort.c 1142 2008-08-13 17:22:21Z hubert@u.washington
#include "../pith/signal.h"
#include "../pith/busy.h"
#include "../pith/icache.h"
-
+#include "../pith/rules.h"
/*
* global place to store mail_sort and mail_thread results
@@ -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 *msgmap, SortOrder new_sort,
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,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 *def_sort, int *def_sort_rev)
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);
}
@@ -686,10 +695,26 @@ reset_sort_order(unsigned int flags)
PAT_S *pat;
SortOrder the_sort_order;
int sort_is_rev;
-
+ char *rule_result;
+ SortOrder new_sort = EndofList;
+ int is_rev;
+
+ rule_result = get_rule_result(FOR_SORT, ps_global->cur_folder, V_SORT_RULES);
+ if (rule_result && *rule_result){
+ new_sort = (SortOrder) translate(rule_result, 1);
+ is_rev = (SortOrder) translate(rule_result, 0) == EndofList ? 0 : 1;
+ fs_give((void **)&rule_result);
+ }
+ if (new_sort != EndofList){
+ the_sort_order = new_sort;
+ sort_is_rev = is_rev;
+ }
+ else{
/* 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 +727,52 @@ 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);
}
+
+SortOrder translate(char *order, int is_rev)
+{
+ int rev = 0;
+ if (!strncmp(order,"tHread", 6)
+ || (rev = !strncmp(order,"Reverse tHread", 14)))
+ return is_rev || rev ? SortThread : EndofList;
+ if (!strncmp(order,"OrderedSubj", 11)
+ || (rev = !strncmp(order,"Reverse OrderedSubj", 19)))
+ return is_rev || rev ? SortSubject2 : EndofList;
+ if (!strncmp(order,"Subject", 7)
+ || (rev = !strncmp(order,"Reverse SortSubject", 15)))
+ return is_rev || rev ? SortSubject : EndofList;
+ if (!strncmp(order,"Arrival", 7)
+ || (rev = !strncmp(order,"Reverse Arrival", 15)))
+ return is_rev || rev ? SortArrival : EndofList;
+ if (!strncmp(order,"From", 4)
+ || (rev = !strncmp(order,"Reverse From", 12)))
+ return is_rev || rev ? SortFrom : EndofList;
+ if (!strncmp(order,"To", 2)
+ || (rev = !strncmp(order,"Reverse To", 10)))
+ return is_rev || rev ? SortTo : EndofList;
+ if (!strncmp(order,"Cc", 2)
+ || (rev = !strncmp(order,"Reverse Cc", 10)))
+ return is_rev || rev ? SortCc : EndofList;
+ if (!strncmp(order,"Date", 4)
+ || (rev = !strncmp(order,"Reverse Date", 12)))
+ return is_rev || rev ? SortDate : EndofList;
+ if (!strncmp(order,"siZe", 4)
+ || (rev = !strncmp(order,"Reverse siZe", 12)))
+ return is_rev || rev ? SortSize : EndofList;
+ if (!strncmp(order,"scorE", 5)
+ || (rev = !strncmp(order,"Reverse scorE", 13)))
+ return is_rev || rev ? SortScore : EndofList;
+ return EndofList;
+}
+
diff --git a/pith/sort.h b/pith/sort.h
index ce383a04..2dfcde71 100644
--- a/pith/sort.h
+++ b/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,9 +41,9 @@ 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);
-
+SortOrder translate(char *, int);
#endif /* PITH_SORT_INCLUDED */
diff --git a/pith/state.c b/pith/state.c
index fd2b4f71..6d1d8455 100644
--- a/pith/state.c
+++ b/pith/state.c
@@ -33,7 +33,7 @@ static char rcsid[] = "$Id: state.c 1074 2008-06-04 00:08:43Z hubert@u.washingto
#include "../pith/remote.h"
#include "../pith/list.h"
#include "../pith/smime.h"
-
+#include "../pith/rules.h"
/*
* Globals referenced throughout pine...
@@ -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;
@@ -116,6 +117,9 @@ free_pine_struct(struct pine **pps)
if(!(pps && (*pps)))
return;
+ if((*pps)->subject != NULL)
+ fs_give((void **)&(*pps)->subject);
+
if((*pps)->hostname != NULL)
fs_give((void **)&(*pps)->hostname);
@@ -131,6 +135,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);
@@ -192,6 +199,8 @@ free_pine_struct(struct pine **pps)
if((*pps)->kw_colors)
free_spec_colors(&(*pps)->kw_colors);
+ free_allowed_qstr();
+
if((*pps)->atmts){
int i;
@@ -206,6 +215,9 @@ free_pine_struct(struct pine **pps)
if((*pps)->msgmap)
msgno_give(&(*pps)->msgmap);
+ if((*pps)->rule_list)
+ free_parsed_rule_list(&(*pps)->rule_list);
+
free_vars(*pps);
fs_give((void **) pps);
diff --git a/pith/state.h b/pith/state.h
index 47e97ce4..6dbdd025 100644
--- a/pith/state.h
+++ b/pith/state.h
@@ -33,7 +33,7 @@
#include "../pith/stream.h"
#include "../pith/color.h"
#include "../pith/user.h"
-
+#include "../pith/rulestype.h"
/*
* Printing control structure
@@ -105,6 +105,11 @@ struct pine {
MAILSTREAM *mail_stream; /* ptr to current folder stream */
MSGNO_S *msgmap; /* ptr to current message map */
+ char screen_name[10]; /* name of current screen */
+ char *role; /* role used when composing */
+ char *procid; /* procedure id when needed */
+ int exiting;
+
unsigned read_predicted:1;
char cur_folder[MAXPATH+1];
@@ -137,6 +142,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 save_msg_rule:5;
@@ -244,10 +251,23 @@ struct pine {
SPEC_COLOR_S *hdr_colors; /* list of configed colors for view */
SPEC_COLOR_S *index_token_colors; /* list of configed colors for index */
+ char *prefix; /* prefix for fillpara */
+ char **list_qstr; /* list of known quote strings */
short init_context;
+ struct {
+ ACTION_S *role_chosen;
+ int attach;
+ int strip;
+ int no_send_flowed;
+ int inchdr;
+ } reply;
+
int *initial_cmds; /* cmds to execute on startup */
int *free_initial_cmds; /* used to free when done */
+ int *initial_cmds_backup; /* keep a copy in case they are freed */
+ int *free_initial_cmds_backup; /* free the copy */
+ int initial_cmds_offset; /* how many commands we have executed */
char c_client_error[300]; /* when nowhow_error is set and PARSE */
@@ -285,6 +305,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;
@@ -301,10 +324,16 @@ struct pine {
int nmw_width;
+ char *subject;
+ int send_immediately;
+ int failed_read;
+
int hours_to_timeout;
int tcp_query_timeout;
+ int sleep; /* time in seconds to sleep before removing temp file */
+
int inc_check_timeout;
int inc_check_interval; /* for local and IMAP */
int inc_second_check_interval; /* for other */
@@ -323,6 +352,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 */
@@ -334,6 +365,7 @@ struct pine {
struct {
char *(*display_filter)(char *, STORE_S *, gf_io_t, FILTLIST_S *);
char *(*display_filter_trigger)(BODY *, char *, size_t);
+ char *(*exec_rule)(char *, gf_io_t, gf_io_t);
} tools;
KEYWORD_S *keywords;
@@ -344,6 +376,9 @@ struct pine {
char last_error[500];
INIT_ERR_S *init_errs;
+ PRULELIST_S *rule_list;
+ char *pressed_key;
+
PRINT_S *print;
#ifdef SMIME
diff --git a/pith/store.c b/pith/store.c
index e8508257..58154c53 100644
--- a/pith/store.c
+++ b/pith/store.c
@@ -171,6 +171,14 @@ so_get(SourceType source, char *name, int rtype)
if(source == TmpFileStar)
our_unlink(so->name);
+ if (ps_global->send_immediately){
+ ps_global->failed_read++;
+ if(ps_global->failed_read == 5){
+ printf("No configurationf file found. Where is your .pinerc file?\n");
+ exit(1);
+ }
+ }
+
fs_give((void **)&so->name);
fs_give((void **)&so); /* so freed & set to NULL */
}
diff --git a/pith/string.c b/pith/string.c
index 84717c3e..2cd43d7d 100644
--- a/pith/string.c
+++ b/pith/string.c
@@ -20,6 +20,7 @@ static char rcsid[] = "$Id: string.c 910 2008-01-14 22:28:38Z hubert@u.washingto
string.c
Misc extra and useful string functions
- rplstr replace a substring with another string
+ - collspaces consecutive spaces are reduced to one space.
- sqzspaces Squeeze out the extra blanks in a string
- sqznewlines Squeeze out \n and \r.
- removing_trailing_white_space
@@ -132,6 +133,31 @@ rplstr(char *os, size_t oslen, int dl, char *is)
return(x3);
}
+/*----------------------------------------------------------------------
+ collapse blank space
+ ----------------------------------------------------------------------*/
+void
+collspaces(char *string)
+{
+ char *p = string;
+ int only_one_space = 0;
+
+ if(!string)
+ return;
+
+ for(;isspace(*p); p++);
+
+ while(*string = *p++)
+ if(!isspace((unsigned char)*string)){
+ only_one_space = 0;
+ string++;
+ }
+ else if(!only_one_space){
+ string++;
+ only_one_space++;
+ }
+ *string = '\0';
+}
/*----------------------------------------------------------------------
@@ -2860,3 +2886,35 @@ free_strlist(STRLIST_S **strp)
fs_give((void **) strp);
}
}
+
+
+void
+removing_extra_stuff(string)
+ char *string;
+{
+ char *p = NULL;
+ int change = 0, length = 0;
+
+
+ if(!string)
+ return;
+
+ for(; *string; string++, length++)
+ p = ((unsigned char)*string != ',') ? NULL : (!p) ? string : p;
+
+ if(p)
+ *p = '\0';
+
+ string -= length;
+ for (; *string; string++){
+ if (change){
+ *string = ' ';
+ change = 0;
+ }
+ if ((((unsigned char)*string == ' ') ||
+ ((unsigned char)*string == ',')) &&
+ ((unsigned char)*(string + 1) == ','))
+ change++;
+ }
+}
+
diff --git a/pith/string.h b/pith/string.h
index 11c4d45f..e6ee6497 100644
--- a/pith/string.h
+++ b/pith/string.h
@@ -86,12 +86,14 @@ struct date {
/* exported protoypes */
char *rplstr(char *, size_t, int, char *);
+void collspaces(char *);
void sqzspaces(char *);
void sqznewlines(char *);
void removing_leading_white_space(char *);
void removing_trailing_white_space(char *);
void removing_leading_and_trailing_white_space(char *);
int removing_double_quotes(char *);
+void removing_extra_stuff (char *);
char *skip_white_space(char *);
char *skip_to_white_space(char *);
char *removing_quotes(char *);
diff --git a/pith/text.c b/pith/text.c
index 5de53e51..2857ddad 100644
--- a/pith/text.c
+++ b/pith/text.c
@@ -92,7 +92,7 @@ decode_text(ATTACH_S *att,
char *err, *charset;
int filtcnt = 0, error_found = 0, column, wrapit;
int is_in_sig = OUT_SIG_BLOCK;
- int is_flowed_msg = 0;
+ int is_flowed_msg = 0, add_me = 1, doraw = RAWSTRING;
int is_delsp_yes = 0;
int filt_only_c0 = 0;
char *parmval;
@@ -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.
@@ -180,7 +189,7 @@ decode_text(ATTACH_S *att,
&& pico_usingcolor()
&& VAR_SIGNATURE_FORE_COLOR
&& VAR_SIGNATURE_BACK_COLOR){
- filters[filtcnt].filter = gf_line_test;
+ filters[filtcnt].filter = gf_quote_test;
filters[filtcnt++].data = gf_line_test_opt(color_signature,
&is_in_sig);
}
@@ -198,9 +207,9 @@ decode_text(ATTACH_S *att,
&& pico_usingcolor()
&& VAR_QUOTE1_FORE_COLOR
&& VAR_QUOTE1_BACK_COLOR){
- filters[filtcnt].filter = gf_line_test;
- filters[filtcnt++].data = gf_line_test_opt(color_a_quote,
- &is_flowed_msg);
+ add_me = 0;
+ filters[filtcnt].filter = gf_quote_test;
+ filters[filtcnt++].data = gf_line_test_opt(color_a_quote, &is_flowed_msg);
}
}
else if(!strucmp(att->body->subtype, "richtext")){
@@ -281,6 +290,11 @@ decode_text(ATTACH_S *att,
}
}
+ if (add_me){
+ filters[filtcnt].filter = gf_quote_test;
+ filters[filtcnt++].data = gf_line_test_opt(select_quote, &doraw);
+ }
+
/*
* If the message is not flowed, we do the quote suppression before
* the wrapping, because the wrapping does not preserve the quote
@@ -305,7 +319,7 @@ decode_text(ATTACH_S *att,
dq.handlesp = handlesp;
dq.do_color = (!(flags & FM_NOCOLOR) && pico_usingcolor());
- filters[filtcnt].filter = gf_line_test;
+ filters[filtcnt].filter = gf_quote_test;
filters[filtcnt++].data = gf_line_test_opt(delete_quotes, &dq);
}
if(ps_global->VAR_QUOTE_REPLACE_STRING
@@ -364,7 +378,7 @@ decode_text(ATTACH_S *att,
dq.handlesp = handlesp;
dq.do_color = (!(flags & FM_NOCOLOR) && pico_usingcolor());
- filters[filtcnt].filter = gf_line_test;
+ filters[filtcnt].filter = gf_quote_test;
filters[filtcnt++].data = gf_line_test_opt(delete_quotes, &dq);
}
@@ -569,7 +583,7 @@ delete_quotes(long int linenum, char *line, LT_INS_S **ins, void *local)
{
DELQ_S *dq;
char *lp;
- int i, lines, not_a_quote = 0;
+ int i, lines, not_a_quote = 0, code;
size_t len;
dq = (DELQ_S *) local;
@@ -589,6 +603,8 @@ delete_quotes(long int linenum, char *line, LT_INS_S **ins, void *local)
for(i = dq->indent_length; i > 0 && !not_a_quote && *lp; i--)
if(*lp++ != SPACE)
not_a_quote++;
+ while(isspace((unsigned char) *lp))
+ lp++;
/* skip over leading tags */
while(!not_a_quote
@@ -628,13 +644,12 @@ delete_quotes(long int linenum, char *line, LT_INS_S **ins, void *local)
}
}
- /* skip over whitespace */
- if(!dq->is_flowed)
- while(isspace((unsigned char) *lp))
- lp++;
-
- /* check first character to see if it is a quote */
- if(!not_a_quote && *lp != '>')
+ len = lp - line;
+ if(strlen(tmp_20k_buf) > len)
+ strcpy(tmp_20k_buf, tmp_20k_buf+len);
+ code = (dq->is_flowed ? IS_FLOWED : NO_FLOWED) | DELETEQUO;
+ select_quote(linenum, lp, ins, &code);
+ if (!not_a_quote && !tmp_20k_buf[0])
not_a_quote++;
if(not_a_quote){
diff --git a/pith/thread.c b/pith/thread.c
index ff9bff54..fc0e32b3 100644
--- a/pith/thread.c
+++ b/pith/thread.c
@@ -30,12 +30,18 @@ static char rcsid[] = "$Id: thread.c 942 2008-03-04 18:21:33Z hubert@u.washingto
#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 *);
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, MSGNO_S *msgmap)
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, THREADNODE *tree)
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, THREADNODE *tree)
* 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, THREADNODE *tree)
(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, THREADNODE *tree)
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, THREADNODE *tree)
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 *stream, MSGNO_S *msgmap, PINETHRD_S *thrd,
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 *stream, MSGNO_S *msgmap, PINETHRD_S *thrd,
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 *stream, MSGNO_S *msgmap, PINETHRD_S *thrd,
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 *stream)
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, MAILSTREAM *stream,
*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, MAILSTREAM *stream,
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, long unsigned int rawno,
*/
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, MAILSTREAM *stream, MSGNO_S *msgmap,
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, MAILSTREAM *stream, MSGNO_S *msgmap,
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, PINETHRD_S *thrd, long int flags)
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, PINETHRD_S *thrd, MSGNO_S *msgmap, int fla
/* 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, PINETHRD_S *thrd, MSGNO_S *msgmap, int fla
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);
}
@@ -1210,7 +1260,8 @@ status_symbol_for_thread(MAILSTREAM *stream, PINETHRD_S *thrd, IndexColType type
/*
* Symbol is * if some message in thread is important,
* + if some message is to us,
- * - if mark-for-cc and some message is cc to us, else blank.
+ * - if mark-for-cc and some message is cc to us,
+ * . if mark-for-group and some message is to us in a group, else blank.
*/
char
to_us_symbol_for_thread(MAILSTREAM *stream, PINETHRD_S *thrd, int consider_flagged)
@@ -1218,45 +1269,48 @@ to_us_symbol_for_thread(MAILSTREAM *stream, PINETHRD_S *thrd, int consider_flagg
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);
/* use branch to_us symbol if it has higher priority than what we have so far */
if(to_us == ' '){
- if(branch_to_us == '-' || branch_to_us == '+' || branch_to_us == '*')
+ if(branch_to_us == '-' || branch_to_us == '+'
+ || branch_to_us == '.' || branch_to_us == '*')
to_us = branch_to_us;
}
else if(to_us == '-'){
- if(branch_to_us == '+' || branch_to_us == '*')
+ if(branch_to_us == '+' || branch_to_us == '.' || branch_to_us == '*')
to_us = branch_to_us;
}
- else if(to_us == '+'){
+ else if(to_us == '+' || to_us == '.'){
if(branch_to_us == '*')
to_us = branch_to_us;
}
}
- if((consider_flagged && to_us != '*') || (!consider_flagged && to_us != '+')){
+ if((consider_flagged && to_us != '*')
+ || (!consider_flagged && to_us != '+' && to_us != '.')){
if(consider_flagged && thrd && thrd->rawno > 0L
&& stream && thrd->rawno <= stream->nmsgs
&& (mc = mail_elt(stream, thrd->rawno))
&& FLAG_MATCH(F_FLAG, mc, stream))
to_us = '*';
- else if(to_us != '+' && !IS_NEWS(stream)){
+ else if(to_us != '+' && to_us != '.' && !IS_NEWS(stream)){
INDEXDATA_S idata;
MESSAGECACHE *mc;
ADDRESS *addr;
@@ -1280,7 +1334,7 @@ to_us_symbol_for_thread(MAILSTREAM *stream, PINETHRD_S *thrd, int consider_flagg
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 +1382,8 @@ set_thread_subtree(MAILSTREAM *stream, PINETHRD_S *thrd, MSGNO_S *msgmap, int v,
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 +1423,8 @@ view_thread(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap, int set_lfl
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 +1488,7 @@ unview_thread(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap)
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 +1594,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 +1603,622 @@ set_search_bit_for_thread(MAILSTREAM *stream, PINETHRD_S *thrd, SEARCHSET **msgs
&& (!(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;
+ 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
+ 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;
+ }
+ }
+ }
+
+ 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;
+
+ }
+
+ 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;
+
+ 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)
+ 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;
+}
+
diff --git a/pith/thread.h b/pith/thread.h
index 40b09bb0..4c5f1a97 100644
--- a/pith/thread.h
+++ b/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(MAILSTREAM *, MSGNO_S *);
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 *, MAILSTREAM *, MSGNO_S *, int);
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 --git a/pith/url.c b/pith/url.c
index 173cb879..114c60bf 100644
--- a/pith/url.c
+++ b/pith/url.c
@@ -53,7 +53,7 @@ char *
rfc1738_scan(char *line, int *len)
{
char *colon, *start, *end;
- int n;
+ int n, delim;
/* process each : in the line */
for(; (colon = strindex(line, ':')) != NULL; line = end){
@@ -137,6 +137,7 @@ rfc1738_scan(char *line, int *len)
if(i != j){
*len = end - start;
+ delim = start > line && *(start - 1) == '<';
/*
* Special case handling for comma.
@@ -146,8 +147,8 @@ rfc1738_scan(char *line, int *len)
* In most cases any way, that's why we have the
* exception.
*/
- if(*(end - 1) == ','
- || (*(end - 1) == '.' && (!*end || *end == ' ')))
+ if(delim == 0 && (*(end - 1) == ','
+ || (*(end - 1) == '.' && (!*end || *end == ' '))))
(*len)--;
if(*len - (colon - start) > 0)
diff --git a/po/Makefile.in b/po/Makefile.in
index 9aa7c9f1..b0300698 100644
--- a/po/Makefile.in
+++ b/po/Makefile.in
@@ -11,7 +11,7 @@
# Origin: gettext-0.16
PACKAGE = alpine
-VERSION = 2.10.9
+VERSION = 2.11
PACKAGE_BUGREPORT = chappa@washington.edu
SHELL = /bin/sh
diff --git a/web/src/alpined.d/Makefile.am b/web/src/alpined.d/Makefile.am
index 9101e4fb..149271a2 100644
--- a/web/src/alpined.d/Makefile.am
+++ b/web/src/alpined.d/Makefile.am
@@ -50,3 +50,4 @@ local.c: alpineldap.c color.c imap.c ldap.c remote.c signal.c \
debug.c status.c stubs.c alpined.h color.h ldap.h
echo "char datestamp[]="\"`date`\"";" > local.c
echo "char hoststamp[]="\"`hostname`\"";" >> local.c
+ cat ../../../patchlevel >> local.c
diff --git a/web/src/alpined.d/Makefile.in b/web/src/alpined.d/Makefile.in
index 644fd530..c219193c 100644
--- a/web/src/alpined.d/Makefile.in
+++ b/web/src/alpined.d/Makefile.in
@@ -689,7 +689,7 @@ local.c: alpineldap.c color.c imap.c ldap.c remote.c signal.c \
debug.c status.c stubs.c alpined.h color.h ldap.h
echo "char datestamp[]="\"`date`\"";" > local.c
echo "char hoststamp[]="\"`hostname`\"";" >> local.c
-
+ cat ../../../patchlevel >> local.c
# Tell versions [3.59,3.63) of GNU make to not export all variables.
# Otherwise a system limit (for SysV at least) may be exceeded.
.NOEXPORT:
diff --git a/web/src/alpined.d/alpined.c b/web/src/alpined.d/alpined.c
index e35ba9e6..b1ab636e 100644
--- a/web/src/alpined.d/alpined.c
+++ b/web/src/alpined.d/alpined.c
@@ -2755,7 +2755,7 @@ PEConfigCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST
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_Interp *interp, int objc, Tcl_Obj *CONST
&& 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;
}