summaryrefslogtreecommitdiff
path: root/alpine/folder.c
diff options
context:
space:
mode:
Diffstat (limited to 'alpine/folder.c')
-rw-r--r--alpine/folder.c6922
1 files changed, 6922 insertions, 0 deletions
diff --git a/alpine/folder.c b/alpine/folder.c
new file mode 100644
index 00000000..02a73b71
--- /dev/null
+++ b/alpine/folder.c
@@ -0,0 +1,6922 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: folder.c 1144 2008-08-14 16:53:34Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/*======================================================================
+ folder.c
+
+ Screen to display and manage all the users folders
+
+This puts up a list of all the folders in the users mail directory on
+the screen spacing it nicely. The arrow keys move from one to another
+and the user can delete the folder or select it to change to or copy a
+message to. The dispay lets the user scroll up or down a screen full,
+or search for a folder name.
+ ====*/
+
+
+#include "headers.h"
+#include "folder.h"
+#include "keymenu.h"
+#include "status.h"
+#include "context.h"
+#include "mailview.h"
+#include "mailindx.h"
+#include "mailcmd.h"
+#include "titlebar.h"
+#include "alpine.h"
+#include "send.h"
+#include "help.h"
+#include "imap.h"
+#include "signal.h"
+#include "reply.h"
+#include "setup.h"
+#include "../pith/state.h"
+#include "../pith/conf.h"
+#include "../pith/folder.h"
+#include "../pith/flag.h"
+#include "../pith/filter.h"
+#include "../pith/msgno.h"
+#include "../pith/thread.h"
+#include "../pith/util.h"
+#include "../pith/stream.h"
+#include "../pith/save.h"
+#include "../pith/busy.h"
+#include "../pith/list.h"
+
+
+#define SUBSCRIBE_PMT \
+ _("Enter newsgroup name (or partial name to get a list): ")
+#define LISTMODE_GRIPE _("Use \"X\" to mark selections in list mode")
+#define SEL_ALTER_PMT _("ALTER folder selection : ")
+#define SEL_TEXT_PMT _("Select by folder Name or Contents ? ")
+#define SEL_PROP_PMT _("Select by which folder property ? ")
+#define DIR_FOLD_PMT \
+ _("Folder and directory of the same name will be deleted. Continue")
+
+
+/*
+ * folder_list_write
+ */
+#define FLW_NONE 0x00
+#define FLW_LUNK 0x01 /* Using handles */
+#define FLW_SLCT 0x02 /* Some folder is selected, may need X to show it */
+#define FLW_LIST 0x04 /* Allow for ListMode for subscribing */
+#define FLW_UNSEEN 0x08 /* Add (unseen) */
+
+
+
+/*----------------------------------------------------------------------
+ The data needed to redraw the folders screen, including the case where the
+screen changes size in which case it may recalculate the folder_display.
+ ----*/
+
+
+/*
+ * Struct managing folder_lister arguments and holding state
+ * for various internal methods
+ */
+typedef struct _folder_screen {
+ CONTEXT_S *context; /* current collection */
+ CONTEXT_S *list_cntxt; /* list mode collection */
+ MAILSTREAM **cache_streamp; /* cached mailstream */
+ char first_folder[MAXFOLDER];
+ unsigned first_dir:1; /* first_folder is a dir */
+ unsigned combined_view:1; /* display flat folder list */
+ unsigned no_dirs:1; /* no dirs in this screen */
+ unsigned no_empty_dirs:1; /* no empty dirs on this screen */
+ unsigned relative_path:1; /* return fully-qual'd specs */
+ unsigned save_sel:1;
+ unsigned force_intro:1;
+ unsigned agg_ops:1;
+ unsigned include_unseen_cnt:1;
+ struct key_menu *km; /* key label/command bindings */
+ struct _func_dispatch {
+ int (*valid)(FOLDER_S *, struct _folder_screen *);
+ struct {
+ HelpType text;
+ char *title;
+ } help;
+ struct {
+ char *bar;
+ TitleBarType style;
+ } title;
+ } f;
+} FSTATE_S;
+
+
+/*
+ * Struct mananging folder_lister metadata as it get's passed
+ * in and back up thru scrolltool
+ */
+typedef struct _folder_proc {
+ FSTATE_S *fs;
+ STRLIST_S *rv;
+ unsigned done:1; /* done listing folders?... */
+ unsigned all_done:1; /* ...and will list no more forever */
+} FPROC_S;
+
+#define FPROC(X) ((FPROC_S *)(X)->proc.data.p)
+
+
+typedef struct _scanarg {
+ MAILSTREAM *stream;
+ int newstream;
+ CONTEXT_S *context;
+ char *pattern;
+ char type;
+} SCANARG_S;
+
+
+typedef struct _statarg {
+ MAILSTREAM *stream;
+ int newstream;
+ CONTEXT_S *context;
+ long flags;
+ long nmsgs;
+ int cmp;
+} STATARG_S;
+
+
+/*
+ * Internal prototypes
+ */
+STRLIST_S *folders_for_subscribe(struct pine *, CONTEXT_S *, char *);
+int folders_for_post(struct pine *, CONTEXT_S **, char *);
+int folder_selector(struct pine *, FSTATE_S *, char *, CONTEXT_S **);
+void folder_sublist_context(char *, CONTEXT_S *, CONTEXT_S *, FDIR_S **, int);
+CONTEXT_S *context_screen(CONTEXT_S *, struct key_menu *, int);
+int exit_collection_add(struct headerentry *, void (*)(void), int, char **);
+char *cancel_collection_add(void (*)(void));
+char *cancel_collection_edit(void (*)(void));
+char *cancel_collection_editing(char *, void (*)(void));
+int build_namespace(char *, char **, char **, BUILDER_ARG *, int *);
+int fl_val_gen(FOLDER_S *, FSTATE_S *);
+int fl_val_writable(FOLDER_S *, FSTATE_S *);
+int fl_val_subscribe(FOLDER_S *, FSTATE_S *);
+STRLIST_S *folder_lister(struct pine *, FSTATE_S *);
+int folder_list_text(struct pine *, FPROC_S *, gf_io_t, HANDLE_S **, int);
+int folder_list_write(gf_io_t, HANDLE_S **, CONTEXT_S *, int, char *, int);
+int folder_list_write_prefix(FOLDER_S *, int, gf_io_t);
+int folder_list_write_middle(FOLDER_S *fp, CONTEXT_S *ctxt, gf_io_t pc, HANDLE_S *);
+int folder_list_write_suffix(FOLDER_S *, int, gf_io_t);
+int color_monitored_unseen(FOLDER_S *, int);
+int folder_list_ith(int, CONTEXT_S *);
+char *folder_list_center_space(char *, int);
+HANDLE_S *folder_list_handle(FSTATE_S *, HANDLE_S *);
+int folder_processor(int, MSGNO_S *, SCROLL_S *);
+int folder_lister_clickclick(SCROLL_S *);
+int folder_lister_choice(SCROLL_S *);
+int folder_lister_finish(SCROLL_S *, CONTEXT_S *, int);
+int folder_lister_addmanually(SCROLL_S *);
+void folder_lister_km_manager(SCROLL_S *, int);
+void folder_lister_km_sel_manager(SCROLL_S *, int);
+void folder_lister_km_sub_manager(SCROLL_S *, int);
+int folder_select(struct pine *, CONTEXT_S *, int);
+int folder_lister_select(FSTATE_S *, CONTEXT_S *, int, int);
+int folder_lister_parent(FSTATE_S *, CONTEXT_S *, int, int);
+char *folder_lister_fullname(FSTATE_S *, char *);
+void folder_export(SCROLL_S *);
+int folder_import(SCROLL_S *, char *, size_t);
+int folder_select_toggle(CONTEXT_S *, int, int (*)(CONTEXT_S *, int));
+char *end_bracket_no_nest(char *);
+int group_subscription(char *, size_t, CONTEXT_S *);
+int rename_folder(CONTEXT_S *, int, char *, size_t, MAILSTREAM *);
+int delete_folder(CONTEXT_S *, int, char *, size_t, MAILSTREAM **);
+void print_folders(FPROC_S *);
+int scan_get_pattern(char *, char *, int);
+int folder_select_text(struct pine *, CONTEXT_S *, int);
+int foreach_do_scan(FOLDER_S *, void *);
+int scan_scan_folder(MAILSTREAM *, CONTEXT_S *, FOLDER_S *, char *);
+int folder_select_props(struct pine *, CONTEXT_S *, int);
+int folder_select_count(long *, int *);
+int foreach_do_stat(FOLDER_S *, void *);
+int foreach_folder(CONTEXT_S *, int, int (*)(FOLDER_S *, void *), void *);
+int folder_delimiter(char *);
+int shuffle_incoming_folders(CONTEXT_S *, int);
+int swap_incoming_folders(int, int, FLIST *);
+int search_folder_list(void *, char *);
+char *get_post_list(char **);
+char *quote_brackets_if_needed(char *);
+#ifdef _WINDOWS
+int folder_list_popup(SCROLL_S *, int);
+int folder_list_select_popup(SCROLL_S *, int);
+void folder_popup_config(FSTATE_S *, struct key_menu *,MPopup *);
+#endif
+
+
+/*----------------------------------------------------------------------
+ Front end to folder lister when it's called from the main menu
+
+ Args: ps -- The general pine_state data structure
+
+ Result: runs context and folder listers
+
+ ----*/
+void
+folder_screen(struct pine *ps)
+{
+ int n = 1;
+ CONTEXT_S *cntxt = ps->context_current;
+ STRLIST_S *folders;
+ FSTATE_S fs;
+ MAILSTREAM *cache_stream = NULL;
+
+ dprint((1, "=== folder_screen called ====\n"));
+ mailcap_free(); /* free resources we won't be using for a while */
+ ps->next_screen = SCREEN_FUN_NULL;
+
+ /* Initialize folder state and dispatches */
+ memset(&fs, 0, sizeof(FSTATE_S));
+ fs.context = cntxt;
+ fs.cache_streamp = &cache_stream;
+ fs.combined_view = F_ON(F_CMBND_FOLDER_DISP, ps_global) != 0;
+ fs.agg_ops = F_ON(F_ENABLE_AGG_OPS, ps_global) != 0;
+ fs.relative_path = 1;
+ fs.include_unseen_cnt = 1;
+ fs.f.valid = fl_val_gen;
+ /* TRANSLATORS: The all upper case things are screen titles */
+ fs.f.title.bar = _("FOLDER LIST");
+ fs.f.title.style = FolderName;
+ fs.f.help.text = h_folder_maint;
+ fs.f.help.title = _("HELP FOR FOLDERS");
+ fs.km = &folder_km;
+
+ if(context_isambig(ps->cur_folder)
+ && (IS_REMOTE(ps->cur_folder) || !is_absolute_path(ps->cur_folder)
+ || (cntxt && cntxt->context && cntxt->context[0] == '{'))){
+ if(strlen(ps_global->cur_folder) < MAXFOLDER - 1){
+ strncpy(fs.first_folder, ps_global->cur_folder, MAXFOLDER);
+ fs.first_folder[MAXFOLDER-1] = '\0';
+ }
+
+ /*
+ * If we're asked to start in the folder list of the current
+ * folder and it looks like the current folder is part of the
+ * current context, try to start in the list of folders in the
+ * current context.
+ */
+ if(ps->start_in_context || fs.combined_view){
+ char tmp[MAILTMPLEN], *p, *q;
+ FDIR_S *fp;
+
+ ps->start_in_context = 0;
+ n = 0;
+
+ if(!(NEWS_TEST(cntxt) || (cntxt->use & CNTXT_INCMNG))
+ && cntxt->dir->delim
+ && strchr(ps->cur_folder, cntxt->dir->delim)){
+ for(p = strchr(q = ps->cur_folder, cntxt->dir->delim);
+ p;
+ p = strchr(q = ++p, cntxt->dir->delim)){
+ strncpy(tmp, q, MIN(p - q, sizeof(tmp)-1));
+ tmp[MIN(p - q, sizeof(tmp)-1)] = '\0';
+
+ fp = next_folder_dir(cntxt, tmp, FALSE, fs.cache_streamp);
+
+ fp->desc = folder_lister_desc(cntxt, fp);
+
+ /* Insert new directory into list */
+ fp->delim = cntxt->dir->delim;
+ fp->prev = cntxt->dir;
+ fp->status |= CNTXT_SUBDIR;
+ cntxt->dir = fp;
+ }
+ }
+ }
+ }
+
+ while(ps->next_screen == SCREEN_FUN_NULL
+ && ((n++) ? (cntxt = context_screen(cntxt,&c_mgr_km,1)) != NULL :1)){
+
+ fs.context = cntxt;
+ if(F_ON(F_ENABLE_INCOMING_CHECKING, ps) && ps->VAR_INCOMING_FOLDERS && ps->VAR_INCOMING_FOLDERS[0])
+ ps->in_folder_screen = 1;
+
+ if((folders = folder_lister(ps, &fs)) != NULL){
+
+ ps->in_folder_screen = 0;
+
+ if(ps && ps->ttyo){
+ blank_keymenu(ps->ttyo->screen_rows - 2, 0);
+ ps->mangled_footer = 1;
+ }
+
+ if(do_broach_folder((char *) folders->name,
+ fs.context, fs.cache_streamp
+ && *fs.cache_streamp ? fs.cache_streamp
+ : NULL, 0L) == 1){
+ reset_context_folders(ps->context_list);
+ ps->next_screen = mail_index_screen;
+ }
+
+ if(fs.cache_streamp)
+ *fs.cache_streamp = NULL;
+ free_strlist(&folders);
+ }
+
+ ps->in_folder_screen = 0;
+ }
+
+ if(fs.cache_streamp && *fs.cache_streamp)
+ pine_mail_close(*fs.cache_streamp);
+
+ ps->prev_screen = folder_screen;
+}
+
+
+/*----------------------------------------------------------------------
+ Front end to folder lister when it's called from the main menu
+
+ Args: ps -- The general pine_state data structure
+
+ Result: runs context and folder listers
+
+ ----*/
+void
+folder_config_screen(struct pine *ps, int edit_exceptions)
+{
+ CONT_SCR_S css;
+ char title[50], htitle[50];
+
+ dprint((1, "=== folder_config_screen called ====\n"));
+ mailcap_free(); /* free resources we won't be using for a while */
+
+ if(edit_exceptions){
+ snprintf(title, sizeof(title), _("SETUP EXCEPTIONS COLLECTION LIST"));
+ snprintf(htitle, sizeof(htitle), _("HELP FOR SETUP EXCEPTIONS COLLECTIONS"));
+ }
+ else{
+ snprintf(title, sizeof(title), _("SETUP COLLECTION LIST"));
+ snprintf(htitle, sizeof(htitle), _("HELP FOR SETUP COLLECTIONS"));
+ }
+
+ memset(&css, 0, sizeof(CONT_SCR_S));
+ css.title = title;
+ /* TRANSLATORS: Print something1 using something2.
+ contexts is something1 */
+ css.print_string = _("contexts");
+ css.contexts = &ps_global->context_list;
+ css.help.text = h_collection_maint;
+ css.help.title = htitle;
+ css.keymenu = &c_cfg_km;
+ css.edit = 1;
+
+ /*
+ * Use conf_scroll_screen to manage display/selection
+ * of contexts
+ */
+ context_config_screen(ps_global, &css, edit_exceptions);
+}
+
+
+/*----------------------------------------------------------------------
+ Browse folders for ^T selection from the Goto Prompt
+
+ Args: ps --
+ cntxtp -- pointer to addr of context to start in, list, and return
+ folder -- pointer to buffer inwhich to return selected folder
+
+ Returns: 1 if we have something valid in cntxtp and folder
+ 0 if problem or user cancelled
+
+ ----*/
+int
+folders_for_goto(struct pine *ps, CONTEXT_S **cntxtp, char *folder, int sublist)
+{
+ int rv;
+ CONTEXT_S fake_context;
+ FDIR_S *fake_dir = NULL;
+ FSTATE_S fs;
+
+ dprint((1, "=== folders_for_goto called ====\n"));
+
+ /* Initialize folder state and dispatches */
+ memset(&fs, 0, sizeof(FSTATE_S));
+ fs.context = *cntxtp;
+ fs.combined_view = !sublist && F_ON(F_CMBND_FOLDER_DISP, ps_global) != 0;
+ fs.f.valid = fl_val_gen;
+ fs.f.title.bar = _("GOTO: SELECT FOLDER");
+ fs.f.title.style = FolderName;
+ fs.f.help.text = h_folder_open;
+ fs.f.help.title = _("HELP FOR OPENING FOLDERS");
+ fs.km = &folder_sel_km;
+
+ /* If we were provided a string,
+ * dummy up a context for a substring match
+ */
+ if(sublist && *folder && context_isambig(folder)){
+ if((*cntxtp)->use & CNTXT_INCMNG){
+ q_status_message(SM_ORDER, 0, 3,
+ _("All folders displayed for Incoming Collection"));
+ }
+ else{
+ folder_sublist_context(folder, *cntxtp, &fake_context,
+ &fake_dir, sublist);
+ fs.context = &fake_context;
+ fs.relative_path = 1;
+ fs.force_intro = 1;
+ cntxtp = &fs.context;
+ }
+ }
+
+ rv = folder_selector(ps, &fs, folder, cntxtp);
+
+ if(fake_dir)
+ free_fdir(&fake_dir, TRUE);
+
+ return(rv);
+}
+
+
+/*----------------------------------------------------------------------
+ Browse folders for ^T selection from the Save Prompt
+
+ Args: ps --
+ cntxtp -- pointer to addr of context to start in, list, and return
+ folder -- pointer to buffer inwhich to return selected folder
+
+ Returns: 1 if we have something valid in cntxtp and folder
+ 0 if problem or user cancelled
+
+ ----*/
+int
+folders_for_save(struct pine *ps, CONTEXT_S **cntxtp, char *folder, int sublist)
+{
+ int rv;
+ CONTEXT_S fake_context;
+ FDIR_S *fake_dir = NULL;
+ FSTATE_S fs;
+
+ dprint((1, "=== folders_for_save called ====\n"));
+
+ /* Initialize folder state and dispatches */
+ memset(&fs, 0, sizeof(FSTATE_S));
+ fs.context = *cntxtp;
+ fs.combined_view = F_ON(F_CMBND_FOLDER_DISP, ps_global) != 0;
+ fs.f.valid = fl_val_gen;
+ fs.f.title.bar = _("SAVE: SELECT FOLDER");
+ fs.f.title.style = MessageNumber;
+ fs.f.help.text = h_folder_save;
+ fs.f.help.title = _("HELP FOR SAVING MESSAGES TO FOLDERS");
+ fs.km = &folder_sela_km;
+
+ /* If we were provided a string,
+ * dummy up a context for a substring match
+ */
+ if(sublist && *folder && context_isambig(folder)){
+ if((*cntxtp)->use & CNTXT_INCMNG){
+ q_status_message(SM_ORDER, 0, 3,
+ _("All folders displayed for Incoming Collection"));
+ }
+ else{
+ folder_sublist_context(folder, *cntxtp, &fake_context,
+ &fake_dir, sublist);
+ fs.context = &fake_context;
+ fs.relative_path = 1;
+ fs.force_intro = 1;
+ cntxtp = &fs.context;
+ }
+ }
+
+ rv = folder_selector(ps, &fs, folder, cntxtp);
+
+ if(fake_dir)
+ free_fdir(&fake_dir, TRUE);
+
+ return(rv);
+}
+
+
+/*----------------------------------------------------------------------
+ Browse folders for ^T selection from the Subscribe Prompt
+
+ Args: ps --
+ cntxtp -- pointer to addr of context to start in, list, and return
+ folder -- pointer to buffer inwhich to return selected folder
+
+ Returns: 1 if we have something valid in cntxtp and folder
+ 0 if problem or user cancelled
+
+ ----*/
+STRLIST_S *
+folders_for_subscribe(struct pine *ps, CONTEXT_S *cntxt, char *folder)
+{
+ STRLIST_S *folders = NULL;
+ FSTATE_S fs;
+ void (*redraw)(void);
+
+ dprint((1, "=== folders_for_sub called ====\n"));
+
+ /* Initialize folder state and dispatches */
+ memset(&fs, 0, sizeof(FSTATE_S));
+ fs.context = cntxt;
+ fs.f.valid = fl_val_subscribe;
+ fs.f.title.bar = _("SUBSCRIBE: SELECT FOLDER");
+ fs.f.title.style = FolderName;
+ fs.f.help.text = h_folder_subscribe;
+ fs.f.help.title = _("HELP SELECTING NEWSGROUP TO SUBSCRIBE TO");
+ fs.km = &folder_sub_km;
+ fs.force_intro = 1;
+
+ fs.context = cntxt;
+ redraw = ps_global->redrawer;
+ folders = folder_lister(ps, &fs);
+ ps_global->redrawer = redraw;
+ if(ps_global->redrawer)
+ (*ps_global->redrawer)();
+
+ return(folders);
+}
+
+
+/*----------------------------------------------------------------------
+ Browse folders for ^T selection for posting
+
+ Args: ps --
+ cntxtp -- pointer to addr of context to start in, list, and return
+ folder -- pointer to buffer inwhich to return selected folder
+
+ Returns: 1 if we have something valid in cntxtp and folder
+ 0 if problem or user cancelled
+
+ ----*/
+int
+folders_for_post(struct pine *ps, CONTEXT_S **cntxtp, char *folder)
+{
+ FSTATE_S fs;
+
+ dprint((1, "=== folders_for_post called ====\n"));
+
+ /* Initialize folder state and dispatches */
+ memset(&fs, 0, sizeof(FSTATE_S));
+ fs.context = *cntxtp;
+ fs.f.valid = fl_val_subscribe;
+ fs.f.title.bar = _("NEWS: SELECT GROUP");
+ fs.f.title.style = FolderName;
+ fs.f.help.text = h_folder_postnews;
+ fs.f.help.title = _("HELP FOR SELECTING NEWSGROUP TO POST TO");
+ fs.km = &folder_post_km;
+
+ return(folder_selector(ps, &fs, folder, cntxtp));
+}
+
+
+int
+folder_selector(struct pine *ps, FSTATE_S *fs, char *folder, CONTEXT_S **cntxtp)
+{
+ int rv = 0;
+ STRLIST_S *folders;
+
+ do{
+ fs->context = *cntxtp;
+ if((folders = folder_lister(ps, fs)) != NULL){
+ strncpy(folder, (char *) folders->name, MAILTMPLEN-1);
+ folder[MAILTMPLEN-1] = '\0';
+ free_strlist(&folders);
+ *cntxtp = fs->context;
+ rv++;
+ break;
+ }
+ else if(!(fs->context
+ && (fs->context->next || fs->context->prev))
+ || fs->combined_view)
+ break;
+ }
+ while((*cntxtp = context_screen(*cntxtp, &c_sel_km, 0)) != NULL);
+
+ return(rv);
+}
+
+
+void
+folder_sublist_context(char *folder, CONTEXT_S *cntxt, CONTEXT_S *new_cntxt, FDIR_S **new_dir, int lev)
+{
+ char *p, *q, *ref, *wildcard;
+
+ *new_cntxt = *cntxt;
+ new_cntxt->next = new_cntxt->prev = NULL;
+ new_cntxt->dir = *new_dir = (FDIR_S *) fs_get(sizeof(FDIR_S));
+ memset(*new_dir, 0, sizeof(FDIR_S));
+ (*new_dir)->status |= CNTXT_NOFIND;
+ (*new_dir)->folders = init_folder_entries();
+ if(!((*new_dir)->delim = cntxt->dir->delim)){
+ /* simple LIST to acquire delimiter, doh */
+ build_folder_list(NULL, new_cntxt, "", NULL,
+ NEWS_TEST(new_cntxt) ? BFL_LSUB : BFL_NONE);
+ new_cntxt->dir->status = CNTXT_NOFIND;
+ }
+
+ wildcard = NEWS_TEST(new_cntxt) ? "*" : "%";
+
+ if((p = strrindex(folder, (*new_dir)->delim)) != NULL){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s", p + 1, wildcard);
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ (*new_dir)->view.internal = cpystr(tmp_20k_buf);
+ for(ref = tmp_20k_buf, q = new_cntxt->context;
+ (*ref = *q) != '\0' && !(*q == '%' && *(q+1) == 's');
+ ref++, q++)
+ ;
+
+ for(q = folder; q <= p; q++, ref++)
+ *ref = *q;
+
+ *ref = '\0';
+ (*new_dir)->ref = cpystr(tmp_20k_buf);
+
+ (*new_dir)->status |= CNTXT_SUBDIR;
+ }
+ else{
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s",
+ (lev > 1) ? wildcard : "", folder, wildcard);
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ (*new_dir)->view.internal = cpystr(tmp_20k_buf);
+ /* leave (*new_dir)->ref == NULL */
+ }
+
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, _("List of folders matching \"%s*\""), folder);
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ (*new_dir)->desc = cpystr(tmp_20k_buf);
+}
+
+
+/*----------------------------------------------------------------------
+ Browse folders for ^T selection from the composer
+
+ Args: error_mess -- pointer to place to return an error message
+
+ Returns: result if folder selected, NULL if not
+ Composer expects the result to be alloc'd here
+
+ ----*/
+char *
+folders_for_fcc(char **errmsg)
+{
+ char *rs = NULL;
+ STRLIST_S *folders;
+ FSTATE_S fs;
+
+ dprint((1, "=== folders_for_fcc called ====\n"));
+
+ /* Coming back from composer */
+ fix_windsize(ps_global);
+ init_sigwinch();
+
+ /* Initialize folder state and dispatches */
+ memset(&fs, 0, sizeof(FSTATE_S));
+ fs.combined_view = F_ON(F_CMBND_FOLDER_DISP, ps_global) != 0;
+ fs.f.valid = fl_val_gen;
+ fs.f.title.bar = _("FCC: SELECT FOLDER");
+ fs.f.title.style = FolderName;
+ fs.f.help.text = h_folder_fcc;
+ fs.f.help.title = _("HELP FOR SELECTING THE FCC");
+ fs.km = &folder_sela_km;
+
+ /* start in the default save context */
+ fs.context = default_save_context(ps_global->context_list);
+
+ do{
+ if((folders = folder_lister(ps_global, &fs)) != NULL){
+ char *name;
+
+ /* replace nickname with full name */
+ if(!(name = folder_is_nick((char *) folders->name,
+ FOLDERS(fs.context), 0)))
+ name = (char *) folders->name;
+
+ if(context_isambig(name) && !((fs.context->use) & CNTXT_SAVEDFLT)){
+ char path_in_context[MAILTMPLEN];
+
+ context_apply(path_in_context, fs.context, name,
+ sizeof(path_in_context));
+ if(!(IS_REMOTE(path_in_context)
+ || is_absolute_path(path_in_context))){
+ /*
+ * Name is relative to the home directory,
+ * so have to add that. Otherwise, the sender will
+ * assume it is in the primary collection since it
+ * will still be ambiguous.
+ */
+ build_path(tmp_20k_buf, ps_global->ui.homedir,
+ path_in_context, SIZEOF_20KBUF);
+ rs = cpystr(tmp_20k_buf);
+ }
+ else
+ rs = cpystr(path_in_context);
+ }
+ else
+ rs = cpystr(name);
+
+ free_strlist(&folders);
+ break;
+ }
+ else if(!(fs.context && (fs.context->next || fs.context->prev))
+ || fs.combined_view)
+ break;
+ }
+ while((fs.context = context_screen(fs.context, &c_fcc_km, 0)) != NULL);
+
+ return(rs);
+}
+
+
+/*----------------------------------------------------------------------
+ Browse folders for ^T selection from the role editor
+
+ Returns: result if folder selected, NULL if not
+ Tesult is alloc'd here
+
+ ----*/
+char *
+folder_for_config(int flags)
+{
+ char *rs = NULL;
+ STRLIST_S *folders;
+ FSTATE_S fs;
+
+ dprint((1, "=== folder_for_config called ====\n"));
+
+ /* Initialize folder state and dispatches */
+ memset(&fs, 0, sizeof(FSTATE_S));
+ fs.combined_view = F_ON(F_CMBND_FOLDER_DISP, ps_global) != 0;
+ fs.f.valid = fl_val_gen;
+ fs.f.title.bar = _("SELECT FOLDER");
+ fs.f.title.style = FolderName;
+ fs.km = &folder_sela_km;
+ if(flags & FOR_PATTERN){
+ fs.f.help.text = h_folder_pattern_roles;
+ fs.f.help.title = _("HELP FOR SELECTING CURRENT FOLDER");
+ }
+ else if(flags & FOR_OPTIONSCREEN){
+ fs.f.help.text = h_folder_stayopen_folders;
+ fs.f.help.title = _("HELP FOR SELECTING FOLDER");
+ }
+ else{
+ fs.f.help.text = h_folder_action_roles;
+ fs.f.help.title = _("HELP FOR SELECTING FOLDER");
+ }
+
+ /* start in the current context */
+ fs.context = ps_global->context_current;
+
+ do{
+ if((folders = folder_lister(ps_global, &fs)) != NULL){
+ char *name = NULL;
+
+ /* replace nickname with full name */
+ if(!(flags & (FOR_PATTERN | FOR_OPTIONSCREEN)))
+ name = folder_is_nick((char *) folders->name,
+ FOLDERS(fs.context), 0);
+
+ if(!name)
+ name = (char *) folders->name;
+
+ if(context_isambig(name) &&
+ !(flags & (FOR_PATTERN | FOR_OPTIONSCREEN) &&
+ folder_is_nick(name, FOLDERS(fs.context), 0))){
+ char path_in_context[MAILTMPLEN];
+
+ context_apply(path_in_context, fs.context, name,
+ sizeof(path_in_context));
+
+ /*
+ * We may still have a non-fully-qualified name. In the
+ * action case, that will be interpreted in the primary
+ * collection instead of as a local name.
+ * Qualify that name the same way we
+ * qualify it in match_pattern_folder_specific.
+ */
+ if(!(IS_REMOTE(path_in_context) ||
+ path_in_context[0] == '#')){
+ if(strlen(path_in_context) < (MAILTMPLEN/2)){
+ char tmp[MAX(MAILTMPLEN,NETMAXMBX)];
+ char *t;
+
+ t = mailboxfile(tmp, path_in_context);
+ rs = cpystr(t);
+ }
+ else{ /* the else part should never happen */
+ build_path(tmp_20k_buf, ps_global->ui.homedir,
+ path_in_context, SIZEOF_20KBUF);
+ rs = cpystr(tmp_20k_buf);
+ }
+ }
+ else
+ rs = cpystr(path_in_context);
+ }
+ else
+ rs = cpystr(name);
+
+ free_strlist(&folders);
+ break;
+ }
+ else if(!(fs.context && (fs.context->next || fs.context->prev))
+ || fs.combined_view)
+ break;
+ }
+ while((fs.context = context_screen(fs.context, &c_fcc_km, 0)) != NULL);
+
+ return(rs);
+}
+
+
+/*
+ * offer screen with list of contexts to select and some sort
+ * of descriptions
+ */
+CONTEXT_S *
+context_screen(CONTEXT_S *start, struct key_menu *km, int edit_config)
+{
+ /* If a list, let the user tell us what to do */
+ if(F_OFF(F_CMBND_FOLDER_DISP, ps_global)
+ && ps_global->context_list
+ && ps_global->context_list->next){
+ CONT_SCR_S css;
+
+ memset(&css, 0, sizeof(CONT_SCR_S));
+ css.title = _("COLLECTION LIST");
+ css.print_string = _("contexts");
+ css.start = start;
+ css.contexts = &ps_global->context_list;
+ css.help.text = h_collection_screen;
+ css.help.title = _("HELP FOR COLLECTION LIST");
+ css.keymenu = km;
+ css.edit = edit_config;
+
+ /*
+ * Use conf_scroll_screen to manage display/selection
+ * of contexts
+ */
+ return(context_select_screen(ps_global, &css, 0));
+ }
+
+ return(ps_global->context_list);
+}
+
+
+static struct headerentry headents_templ[]={
+ /* TRANSLATORS: these are the headings for setting up a collection of
+ folders, PATH is a filesystem path, VIEW is sort of a technical
+ term that can be used to restrict the View to fewer folders */
+ {"Nickname : ", N_("Nickname"), h_composer_cntxt_nick, 12, 0, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
+ {"Server : ", N_("Server"), h_composer_cntxt_server, 12, 0, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
+ {"Path : ", N_("Path"), h_composer_cntxt_path, 12, 0, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
+ {"View : ", N_("View"), h_composer_cntxt_view, 12, 0, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
+ {NULL, NULL, NO_HELP, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE}
+};
+#define AC_NICK 0
+#define AC_SERV 1
+#define AC_PATH 2
+#define AC_VIEW 3
+
+
+char *
+context_edit_screen(struct pine *ps, char *func, char *def_nick,
+ char *def_serv, char *def_path, char *def_view)
+{
+ int editor_result, i, j;
+ char nickpart[MAILTMPLEN], servpart[MAILTMPLEN], new_cntxt[MAILTMPLEN];
+ char pathpart[MAILTMPLEN], allbutnick[MAILTMPLEN];
+ char tmp[MAILTMPLEN], *nick, *serv, *path, *view,
+ *return_cntxt = NULL, *val, *p, new[MAILTMPLEN];
+ char nickpmt[100], servpmt[100], pathpmt[100], viewpmt[100];
+ int indent;
+ PICO pbf;
+ STORE_S *msgso;
+ NETMBX mb;
+
+ standard_picobuf_setup(&pbf);
+ pbf.pine_flags |= P_NOBODY;
+ pbf.exittest = exit_collection_add;
+ pbf.canceltest = (func && !strucmp(func, "EDIT")) ? cancel_collection_edit
+ : cancel_collection_add;
+ snprintf(tmp, sizeof(tmp), _("FOLDER COLLECTION %s"), func);
+ tmp[sizeof(tmp)-1] = '\0';
+ pbf.pine_anchor = set_titlebar(tmp, ps_global->mail_stream,
+ ps_global->context_current,
+ ps_global->cur_folder,ps_global->msgmap,
+ 0, FolderName, 0, 0, NULL);
+
+ /* An informational message */
+ if((msgso = so_get(PicoText, NULL, EDIT_ACCESS)) != NULL){
+ pbf.msgtext = (void *) so_text(msgso);
+ so_puts(msgso,
+ _("\n Fill in the fields above to add a Folder Collection to your"));
+ so_puts(msgso,
+ _("\n COLLECTION LIST screen."));
+ so_puts(msgso,
+ _("\n Use the \"^G\" command to get help specific to each item, and"));
+ so_puts(msgso,
+ _("\n use \"^X\" when finished."));
+ }
+
+
+ pbf.headents = (struct headerentry *)fs_get((sizeof(headents_templ)
+ /sizeof(struct headerentry))
+ * sizeof(struct headerentry));
+ memset((void *) pbf.headents, 0,
+ (sizeof(headents_templ)/sizeof(struct headerentry))
+ * sizeof(struct headerentry));
+
+ for(i = 0; headents_templ[i].prompt; i++)
+ pbf.headents[i] = headents_templ[i];
+
+ indent = utf8_width(_("Nickname")) + 2;
+
+ nick = cpystr(def_nick ? def_nick : "");
+ pbf.headents[AC_NICK].realaddr = &nick;
+ pbf.headents[AC_NICK].maxlen = strlen(nick);
+ utf8_snprintf(nickpmt, sizeof(nickpmt), "%-*.*w: ", indent, indent, _("Nickname"));
+ pbf.headents[AC_NICK].prompt = nickpmt;
+ pbf.headents[AC_NICK].prwid = indent+2;
+
+ serv = cpystr(def_serv ? def_serv : "");
+ pbf.headents[AC_SERV].realaddr = &serv;
+ pbf.headents[AC_SERV].maxlen = strlen(serv);
+ utf8_snprintf(servpmt, sizeof(servpmt), "%-*.*w: ", indent, indent, _("Server"));
+ pbf.headents[AC_SERV].prompt = servpmt;
+ pbf.headents[AC_SERV].prwid = indent+2;
+
+ path = cpystr(def_path ? def_path : "");
+ pbf.headents[AC_PATH].realaddr = &path;
+ pbf.headents[AC_PATH].maxlen = strlen(path);
+ pbf.headents[AC_PATH].bldr_private = (void *) 0;
+ utf8_snprintf(pathpmt, sizeof(pathpmt), "%-*.*w: ", indent, indent, _("Path"));
+ pbf.headents[AC_PATH].prompt = pathpmt;
+ pbf.headents[AC_PATH].prwid = indent+2;
+
+ view = cpystr(def_view ? def_view : "");
+ pbf.headents[AC_VIEW].realaddr = &view;
+ pbf.headents[AC_VIEW].maxlen = strlen(view);
+ utf8_snprintf(viewpmt, sizeof(viewpmt), "%-*.*w: ", indent, indent, _("View"));
+ pbf.headents[AC_VIEW].prompt = viewpmt;
+ pbf.headents[AC_VIEW].prwid = indent+2;
+
+ /*
+ * If this is new context, setup to query IMAP server
+ * for location of personal namespace.
+ */
+ if(!(def_nick || def_serv || def_path || def_view)){
+ pbf.headents[AC_SERV].builder = build_namespace;
+ pbf.headents[AC_SERV].affected_entry = &pbf.headents[AC_PATH];
+ pbf.headents[AC_SERV].bldr_private = (void *) 0;
+ }
+
+ /* pass to pico and let user change them */
+ editor_result = pico(&pbf);
+
+ if(editor_result & COMP_GOTHUP){
+ hup_signal();
+ }
+ else{
+ fix_windsize(ps_global);
+ init_signals();
+ }
+
+ if(editor_result & COMP_CANCEL){
+ cmd_cancelled(func);
+ }
+ else if(editor_result & COMP_EXIT){
+ servpart[0] = pathpart[0] = new_cntxt[0] = allbutnick[0] = '\0';
+ if(serv && *serv){
+ if(serv[0] == '{' && serv[strlen(serv)-1] == '}'){
+ strncpy(servpart, serv, sizeof(servpart)-1);
+ servpart[sizeof(servpart)-1] = '\0';
+ }
+ else
+ snprintf(servpart, sizeof(servpart), "{%s}", serv);
+
+ if(mail_valid_net_parse(servpart, &mb)){
+ if(!struncmp(mb.service, "nntp", 4)
+ && (!path || strncmp(path, "#news.", 6)))
+ strncat(servpart, "#news.", sizeof(servpart)-1-strlen(servpart));
+ }
+ else
+ panic("Unexpected invalid server");
+ }
+ else
+ servpart[0] = '\0';
+
+ servpart[sizeof(servpart)-1] = '\0';
+
+ new_cntxt[0] = '\0';
+ if(nick && *nick){
+ val = quote_if_needed(nick);
+ if(val){
+ strncpy(new_cntxt, val, sizeof(new_cntxt)-2);
+ new_cntxt[sizeof(new_cntxt)-2] = '\0';
+ if(val != nick)
+ fs_give((void **)&val);
+
+ strncat(new_cntxt, " ", sizeof(new_cntxt)-strlen(new_cntxt)-1);
+ new_cntxt[sizeof(new_cntxt)-1] = '\0';
+ }
+ }
+
+ p = allbutnick;
+ sstrncpy(&p, servpart, sizeof(allbutnick)-1-(p-allbutnick));
+ allbutnick[sizeof(allbutnick)-1] = '\0';
+
+ if(path){
+ val = quote_brackets_if_needed(path);
+ if(val){
+ strncpy(pathpart, val, sizeof(pathpart)-1);
+ pathpart[sizeof(pathpart)-1] = '\0';
+ if(val != path)
+ fs_give((void **)&val);
+ }
+
+ if(pbf.headents[AC_PATH].bldr_private != (void *) 0){
+ strncat(pathpart, (char *) pbf.headents[AC_PATH].bldr_private,
+ sizeof(pathpart)-strlen(pathpart)-1);
+ pathpart[sizeof(pathpart)-1] = '\0';
+ }
+ }
+
+ sstrncpy(&p, pathpart, sizeof(allbutnick)-1-(p-allbutnick));
+ allbutnick[sizeof(allbutnick)-1] = '\0';
+
+ if(view[0] != '[' && sizeof(allbutnick)-1-(p-allbutnick) > 0){
+ *p++ = '[';
+ *p = '\0';
+ }
+
+ sstrncpy(&p, view, sizeof(allbutnick)-1-(p-allbutnick));
+ allbutnick[sizeof(allbutnick)-1] = '\0';
+ if((j=strlen(view)) < 2 || (view[j-1] != ']' &&
+ sizeof(allbutnick)-1-(p-allbutnick) > 0)){
+ *p++ = ']';
+ *p = '\0';
+ }
+
+ val = quote_if_needed(allbutnick);
+ if(val){
+ strncat(new_cntxt, val, sizeof(new_cntxt)-1-strlen(new_cntxt));
+ new_cntxt[sizeof(new_cntxt)-1] = '\0';
+
+ if(val != allbutnick)
+ fs_give((void **)&val);
+ }
+
+ return_cntxt = cpystr(new_cntxt);
+ }
+
+ for(i = 0; headents_templ[i].prompt; i++)
+ fs_give((void **) pbf.headents[i].realaddr);
+
+ if(pbf.headents[AC_PATH].bldr_private != (void *) 0)
+ fs_give(&pbf.headents[AC_PATH].bldr_private);
+
+ fs_give((void **) &pbf.headents);
+
+ standard_picobuf_teardown(&pbf);
+
+ if(msgso)
+ so_give(&msgso);
+
+ return(return_cntxt);
+}
+
+
+/*
+ * Doubles up '[' and ']' characters and returns either
+ * an allocated string or a pointer to the source string
+ * if no quoting is needed.
+ */
+char *
+quote_brackets_if_needed(char *src)
+{
+ char *step1 = NULL, *step2 = NULL, *ret;
+
+ ret = src;
+
+ if((strpbrk(src, "[]") != NULL)
+ && ((step1 = add_escapes(src, "[", '[', "", "")) != NULL)
+ && ((step2 = add_escapes(step1, "]", ']', "", ""))))
+ ret = step2;
+
+ if(step1)
+ fs_give((void **) &step1);
+
+ return(ret);
+}
+
+
+/*
+ * Call back for pico to prompt the user for exit confirmation
+ *
+ * Returns: either NULL if the user accepts exit, or string containing
+ * reason why the user declined.
+ */
+int
+exit_collection_add(struct headerentry *he, void (*redraw_pico)(void), int allow_flowed,
+ char **result)
+{
+ char prompt[256], tmp[MAILTMPLEN], tmpnodel[MAILTMPLEN], *server, *path,
+ delim = '\0', *rstr = NULL, *p;
+ int exists = 0, i;
+ void (*redraw)(void) = ps_global->redrawer;
+ NETMBX mb;
+
+ ps_global->redrawer = redraw_pico;
+ fix_windsize(ps_global);
+
+ server = *he[AC_SERV].realaddr;
+ removing_trailing_white_space(server);
+ removing_leading_white_space(server);
+
+ path = *he[AC_PATH].realaddr;
+
+ if(*server){
+ /* No brackets? */
+ if(server[0] == '{' && server[strlen(server)-1] == '}'){
+ strncpy(tmp, server, sizeof(tmp)-1);
+ tmp[sizeof(tmp)-1] = '\0';
+ }
+ else /* add them */
+ snprintf(tmp, sizeof(tmp), "{%.*s}", sizeof(tmp)-3, server);
+
+ if(mail_valid_net_parse(tmp, &mb)){ /* news? verify namespace */
+ if(!struncmp(mb.service, "nntp", 4) && strncmp(path, "#news.", 6))
+ strncat(tmp, "#news.", sizeof(tmp)-1-strlen(tmp));
+ }
+ else
+ rstr = "Invalid Server entry";
+ }
+ else
+ tmp[0] = '\0';
+
+ tmp[sizeof(tmp)-1] = '\0';
+
+ if(!rstr){
+ /*
+ * Delimiter acquisition below serves dual purposes. One's to
+ * get the delimiter so we can make sure it's hanging off the end
+ * of the path so we can test for the directory existence. The
+ * second's to make sure the server (and any requested service)
+ * name we were given exists. It should be handled by the folder
+ * existence test futher below, but it doesn't work with news...
+ *
+ * Update. Now we are stripping the delimiter in the tmpnodel version
+ * so that we can pass that to folder_exists. Cyrus does not answer
+ * that the folder exists if we leave the trailing delimiter.
+ * Hubert 2004-12-17
+ */
+ strncat(tmp, path, sizeof(tmp)-1-strlen(tmp));
+ tmp[sizeof(tmp)-1] = '\0';
+ strncpy(tmpnodel, tmp, sizeof(tmpnodel)-1);
+ tmpnodel[sizeof(tmpnodel)-1] = '\0';
+
+ if(he[AC_PATH].bldr_private != (void *) 0)
+ fs_give(&he[AC_PATH].bldr_private);
+
+ ps_global->mm_log_error = 0;
+ if((delim = folder_delimiter(tmp)) != '\0'){
+ if(*path){
+ if(tmp[(i = strlen(tmp)) - 1] == delim)
+ tmpnodel[i-1] = '\0';
+ else{
+ tmp[i] = delim;
+ tmp[i+1] = '\0';
+ he[AC_PATH].bldr_private = (void *) cpystr(&tmp[i]);
+ }
+ }
+ }
+ else if(ps_global->mm_log_error && ps_global->last_error)
+ /* We used to bail, but this was changed with 4.10
+ * as some users wanted to add clctn before the server
+ * was actually around and built.
+ */
+ flush_status_messages(0); /* mail_create gripes */
+ else
+ dprint((1, "exit_col_test: No Server Hierarchy!\n"));
+ }
+
+ if(!rstr){
+ if(!*tmp
+ || !delim
+ || ((*(p = tmp) == '#'
+ || (*tmp == '{' && (p = strchr(tmp, '}')) && *++p))
+ && !struncmp(p, "#news.", 6))
+ || (*tmp == '{' && (p = strchr(tmp, '}')) && !*++p)){
+ exists = 1;
+ }
+ else if((i = folder_exists(NULL, tmpnodel)) & FEX_ERROR){
+ if(!(rstr = ps_global->last_error))
+ rstr = _("Problem testing for directory existence");
+ }
+ else
+ exists = (i & FEX_ISDIR);
+
+ if(exists)
+ snprintf(prompt, sizeof(prompt), _("Exit and save changes"));
+ else
+ snprintf(prompt, sizeof(prompt), _("Exit, saving changes and creating Path"));
+
+ if(want_to(prompt, 'y', 0, NO_HELP, WT_NORM) == 'y'){
+ if(!exists && !pine_mail_create(NULL, tmp)){
+ flush_status_messages(1); /* mail_create gripes */
+ if(!(rstr = ps_global->last_error))
+ rstr = "";
+ }
+ }
+ else
+ rstr = _("Use ^C to abandon changes you've made");
+ }
+
+ if(result)
+ *result = rstr;
+
+ ps_global->redrawer = redraw;
+ return((rstr == NULL) ? 0 : 1);
+}
+
+
+char *
+cancel_collection_add(void (*redraw_pico)(void))
+{
+ return(cancel_collection_editing(_("Add"), redraw_pico));
+}
+
+
+char *
+cancel_collection_edit(void (*redraw_pico)(void))
+{
+ return(cancel_collection_editing(_("Edit"), redraw_pico));
+}
+
+
+char *
+cancel_collection_editing(char *func, void (*redraw_pico)(void))
+{
+ char *rstr = NULL;
+ void (*redraw)(void) = ps_global->redrawer;
+ static char rbuf[20];
+ char prompt[256];
+#define CCA_PROMPT \
+ _("Cancel Add (answering \"Yes\" will abandon any changes made) ")
+
+ snprintf(prompt, sizeof(prompt), _("Cancel %s (answering \"Yes\" will abandon any changes made) "), func ? func : "Add");
+ snprintf(rbuf, sizeof(rbuf), _("%s Cancelled) "), func ? func : "Add");
+
+ ps_global->redrawer = redraw_pico;
+ fix_windsize(ps_global);
+
+ switch(want_to(prompt, 'y', 'x', NO_HELP, WT_NORM)){
+ case 'y':
+ rstr = rbuf;
+ break;
+
+ case 'n':
+ case 'x':
+ break;
+ }
+
+ ps_global->redrawer = redraw;
+ return(rstr);
+}
+
+
+int
+build_namespace(char *server, char **server_too, char **error, BUILDER_ARG *barg, int *mangled)
+{
+ char *p, *name;
+ int we_cancel = 0;
+ MAILSTREAM *stream;
+ NAMESPACE ***namespace;
+ size_t len;
+
+ dprint((5, "- build_namespace - (%s)\n",
+ server ? server : "nul"));
+
+ if(*barg->me){ /* only call this once! */
+ if(server_too)
+ *server_too = cpystr(server ? server : "");
+
+ return(0);
+ }
+ else
+ *barg->me = (void *) 1;
+
+ if((p = server) != NULL) /* empty string? */
+ while(*p && isspace((unsigned char) *p))
+ p++;
+
+ if(p && *p){
+ if(server_too)
+ *server_too = cpystr(p);
+
+ len = strlen(p) + 2;
+ name = (char *) fs_get((len + 1) * sizeof(char));
+ snprintf(name, len+1, "{%s}", p);
+ }
+ else{
+ if(server_too)
+ *server_too = cpystr("");
+
+ return(0);
+ }
+
+ *mangled |= BUILDER_SCREEN_MANGLED;
+ fix_windsize(ps_global);
+ init_sigwinch();
+ clear_cursor_pos();
+
+ we_cancel = busy_cue(_("Fetching default directory"), NULL, 1);
+
+ if((stream = pine_mail_open(NULL, name,
+ OP_HALFOPEN | OP_SILENT | SP_USEPOOL | SP_TEMPUSE,
+ NULL)) != NULL){
+ if((namespace = mail_parameters(stream, GET_NAMESPACE, NULL))
+ && *namespace && (*namespace)[0]
+ && (*namespace)[0]->name && (*namespace)[0]->name[0]){
+ if(barg->tptr)
+ fs_give((void **)&barg->tptr);
+
+ barg->tptr = cpystr((*namespace)[0]->name);
+ }
+
+ pine_mail_close(stream);
+ }
+
+ if(we_cancel)
+ cancel_busy_cue(-1);
+
+ fs_give((void **) &name);
+
+ return(1);
+}
+
+
+int
+fl_val_gen (FOLDER_S *f, FSTATE_S *fs)
+{
+ return(f && FLDR_NAME(f));
+}
+
+
+int
+fl_val_writable (FOLDER_S *f, FSTATE_S *fs)
+{
+ return(1);
+}
+
+
+int
+fl_val_subscribe (FOLDER_S *f, FSTATE_S *fs)
+{
+ if(f->subscribed){
+ q_status_message1(SM_ORDER, 0, 4, _("Already subscribed to \"%s\""),
+ FLDR_NAME(f));
+ return(0);
+ }
+
+ return(1);
+}
+
+
+/*----------------------------------------------------------------------
+ Business end of displaying and operating on a collection of folders
+
+ Args: ps -- The pine_state data structure
+ fs -- Folder screen state structure
+
+ Result: A string list containing folders selected,
+ NULL on Cancel, Error or other problem
+
+ ----*/
+STRLIST_S *
+folder_lister(struct pine *ps, FSTATE_S *fs)
+{
+ int fltrv, we_cancel = 0;
+ int first_time_through = 1;
+ HANDLE_S *handles = NULL;
+ STORE_S *screen_text = NULL;
+ FPROC_S folder_proc_data;
+ gf_io_t pc;
+
+ dprint((1, "\n\n ---- FOLDER LISTER ----\n"));
+
+ memset(&folder_proc_data, 0, sizeof(FPROC_S));
+ folder_proc_data.fs = fs;
+
+ while(!folder_proc_data.done){
+ if((screen_text = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){
+ gf_set_so_writec(&pc, screen_text);
+ }
+ else{
+ q_status_message(SM_ORDER | SM_DING, 3, 3,
+ "Formatting Error: Can't create space for list");
+ return(NULL);
+ }
+
+ we_cancel = busy_cue(_("Fetching folder data"), NULL, 1);
+ fltrv = folder_list_text(ps, &folder_proc_data, pc, &handles,
+ ps->ttyo->screen_cols);
+ if(we_cancel)
+ cancel_busy_cue(-1);
+
+ if(fltrv){
+
+ SCROLL_S sargs;
+ struct key_menu km;
+ struct key keys[36];
+
+ memset(&sargs, 0, sizeof(SCROLL_S));
+ sargs.text.text = so_text(screen_text);
+ sargs.text.src = CharStar;
+ sargs.text.desc = "folder list";
+ if((sargs.text.handles = folder_list_handle(fs, handles)) != NULL)
+ sargs.start.on = Handle;
+
+ sargs.bar.title = fs->f.title.bar;
+ sargs.bar.style = fs->f.title.style;
+
+ sargs.proc.tool = folder_processor;
+ sargs.proc.data.p = (void *) &folder_proc_data;
+
+ sargs.quell_first_view = first_time_through ? 0 : 1;
+ first_time_through = 0;
+
+ sargs.resize_exit = 1;
+ sargs.vert_handle = 1;
+ sargs.srch_handle = 1;
+
+ sargs.help.text = fs->f.help.text;
+ sargs.help.title = fs->f.help.title;
+
+ sargs.keys.menu = &km;
+ km = *fs->km;
+ km.keys = keys;
+ memcpy(&keys[0], fs->km->keys,
+ (km.how_many * 12) * sizeof(struct key));
+ setbitmap(sargs.keys.bitmap);
+
+ if(fs->km == &folder_km){
+ sargs.keys.each_cmd = folder_lister_km_manager;
+#ifdef _WINDOWS
+ sargs.mouse.popup = folder_list_popup;
+#endif
+ }
+ else{
+#ifdef _WINDOWS
+ sargs.mouse.popup = folder_list_select_popup;
+#endif
+ if(fs->km == &folder_sel_km || fs->km == &folder_sela_km)
+ sargs.keys.each_cmd = folder_lister_km_sel_manager;
+ else if(fs->km == &folder_sub_km)
+ sargs.keys.each_cmd = folder_lister_km_sub_manager;
+ }
+
+ sargs.mouse.clickclick = folder_lister_clickclick;
+
+ switch(scrolltool(&sargs)){
+ case MC_MAIN : /* just leave */
+ folder_proc_data.done = 1;
+ break;
+
+ case MC_RESIZE : /* loop around rebuilding screen */
+ if(sargs.text.handles){
+ FOLDER_S *fp;
+
+ fp = folder_entry(sargs.text.handles->h.f.index,
+ FOLDERS(sargs.text.handles->h.f.context));
+ if(fp && strlen(FLDR_NAME(fp)) < MAXFOLDER -1){
+ strncpy(fs->first_folder, FLDR_NAME(fp), MAXFOLDER);
+ fs->first_folder[MAXFOLDER-1] = '\0';
+ }
+
+ fs->context = sargs.text.handles->h.f.context;
+ }
+
+ break;
+
+ /*--------- EXIT menu -----------*/
+ case MC_EXIT :
+ case MC_EXITQUERY :
+ fs->list_cntxt = NULL;
+ folder_proc_data.done = folder_proc_data.all_done = 1;
+ break;
+
+ default :
+ break;
+ }
+
+ ps->noticed_change_in_unseen = 0;
+
+
+ if(F_ON(F_BLANK_KEYMENU,ps))
+ FOOTER_ROWS(ps) = 1;
+
+ gf_clear_so_writec(screen_text);
+ so_give(&screen_text);
+ free_handles(&handles);
+ }
+ else
+ folder_proc_data.done = 1;
+ }
+
+ reset_context_folders(fs->context);
+
+ if(folder_proc_data.all_done)
+ fs->context = NULL;
+
+ if(fs->cache_streamp && *fs->cache_streamp){
+ int i;
+
+ /*
+ * check stream pool to see if currently cached
+ * stream went away
+ */
+ for(i = 0; i < ps->s_pool.nstream; i++)
+ if(ps->s_pool.streams[i] == *fs->cache_streamp)
+ break;
+ if(i == ps->s_pool.nstream)
+ *fs->cache_streamp = NULL;
+ }
+
+ return(folder_proc_data.rv);
+}
+
+
+/*
+ * folder_list_text - format collection's contents for display
+ */
+int
+folder_list_text(struct pine *ps, FPROC_S *fp, gf_io_t pc, HANDLE_S **handlesp, int cols)
+{
+ int rv = 1, i, j, ftotal, fcount, slot_width, slot_rows,
+ slot_cols, index, findex, width, shown, selected;
+ CONTEXT_S *c_list;
+ char lbuf[6*MAX_SCREEN_COLS+1];
+
+ /* disarm this trigger that gets us out of scrolltool */
+ ps->noticed_change_in_unseen = 0;
+
+ if(handlesp)
+ init_handles(handlesp);
+
+ c_list = fp->fs->context;
+ if(fp->fs->combined_view
+ && (F_ON(F_CMBND_SUBDIR_DISP, ps_global) || !c_list->dir->prev))
+ while(c_list->prev) /* rewind to start */
+ c_list = c_list->prev;
+
+ do{
+ ps->user_says_cancel = 0;
+
+ /* If we're displaying folders, fetch the list */
+ if((shown = (c_list == fp->fs->context
+ || (c_list->dir->status & CNTXT_NOFIND) == 0
+ || F_ON(F_EXPANDED_FOLDERS, ps_global))) != 0){
+ /*
+ * if select is allowed, flag context so any that are
+ * are remembered even after the list is destroyed
+ */
+ if(fp->fs->agg_ops)
+ c_list->use |= CNTXT_PRESRV;
+
+ /* Make sure folder list filled in */
+ refresh_folder_list(c_list, fp->fs->no_dirs, FALSE, fp->fs->cache_streamp);
+ }
+
+ /* Insert any introductory text here */
+ if(c_list->next
+ || c_list->prev
+ || c_list->dir->prev
+ || fp->fs->force_intro){
+
+ /* Leading horizontal line? */
+ if(fp->fs->combined_view
+ && (F_ON(F_CMBND_SUBDIR_DISP,ps_global)
+ || !c_list->dir->prev)){
+ if(c_list->prev)
+ gf_puts("\n", pc); /* blank line */
+
+ gf_puts(repeat_char(cols, '-'), pc);
+ gf_puts("\n", pc);
+ }
+
+ /* nickname or description */
+ if(F_ON(F_CMBND_FOLDER_DISP, ps_global)
+ && (!c_list->dir->prev
+ || F_ON(F_CMBND_SUBDIR_DISP, ps_global))){
+ char buf[6*MAX_SCREEN_COLS + 1];
+
+ if(cols < 40){
+ snprintf(buf, sizeof(buf), "%.*s", cols,
+ strsquish(tmp_20k_buf, SIZEOF_20KBUF,
+ (c_list->nickname)
+ ? c_list->nickname
+ : (c_list->label ? c_list->label : ""),
+ cols));
+ gf_puts(folder_list_center_space(buf, cols), pc);
+ }
+ else{
+ int wid;
+
+ snprintf(buf, sizeof(buf), "%s-Collection <",
+ NEWS_TEST(c_list) ? "News" : "Folder");
+ wid = utf8_width(buf)+1;
+ wid = MAX(0, cols-wid);
+ snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "%.*s>", wid,
+ strsquish(tmp_20k_buf, SIZEOF_20KBUF,
+ (c_list->nickname)
+ ? c_list->nickname
+ : (c_list->label ? c_list->label : ""),
+ wid));
+ }
+
+ gf_puts(buf, pc);
+ gf_puts("\n", pc);
+ }
+ else if(c_list->label){
+ if(utf8_width(c_list->label) > ps_global->ttyo->screen_cols)
+ utf8_pad_to_width(lbuf, c_list->label, sizeof(lbuf), ps_global->ttyo->screen_cols, 1);
+ else
+ strncpy(lbuf, c_list->label, sizeof(lbuf));
+
+ lbuf[sizeof(lbuf)-1] = '\0';
+
+ gf_puts(folder_list_center_space(lbuf, cols), pc);
+ gf_puts(lbuf, pc);
+ gf_puts("\n", pc);
+ }
+
+ if(c_list->comment){
+ if(utf8_width(c_list->comment) > ps_global->ttyo->screen_cols)
+ utf8_pad_to_width(lbuf, c_list->comment, sizeof(lbuf), ps_global->ttyo->screen_cols, 1);
+ else
+ strncpy(lbuf, c_list->comment, sizeof(lbuf));
+
+ lbuf[sizeof(lbuf)-1] = '\0';
+
+ gf_puts(folder_list_center_space(lbuf, cols), pc);
+ gf_puts(lbuf, pc);
+ gf_puts("\n", pc);
+ }
+
+ if(c_list->dir->desc){
+ char buf[6*MAX_SCREEN_COLS + 1];
+
+ strncpy(buf, strsquish(tmp_20k_buf,SIZEOF_20KBUF,c_list->dir->desc,cols),
+ sizeof(buf)-1);
+ buf[sizeof(buf)-1] = '\0';
+ gf_puts(folder_list_center_space(buf, cols), pc);
+ gf_puts(buf, pc);
+ gf_puts("\n", pc);
+ }
+
+ if(c_list->use & CNTXT_ZOOM){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "[ ZOOMED on %d (of %d) %ss ]",
+ selected_folders(c_list),
+ folder_total(FOLDERS(c_list)),
+ (c_list->use & CNTXT_NEWS) ? "Newsgroup" : "Folder");
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+
+ if(utf8_width(tmp_20k_buf) > ps_global->ttyo->screen_cols)
+ utf8_pad_to_width(lbuf, tmp_20k_buf, sizeof(lbuf), ps_global->ttyo->screen_cols, 1);
+ else
+ strncpy(lbuf, tmp_20k_buf, sizeof(lbuf));
+
+ lbuf[sizeof(lbuf)-1] = '\0';
+
+ gf_puts(folder_list_center_space(lbuf, cols), pc);
+ gf_puts(lbuf, pc);
+ gf_puts("\n", pc);
+ }
+
+ gf_puts(repeat_char(cols, '-'), pc);
+ gf_puts("\n\n", pc);
+ }
+
+ if(shown){
+ /* Run thru list formatting as necessary */
+ if((ftotal = folder_total(FOLDERS(c_list))) != 0){
+ /* If previously selected, mark members of new list */
+ selected = selected_folders(c_list);
+
+ /* How many screen cells per cell for each folder name? */
+ slot_width = 1;
+ for(fcount = i = 0; i < ftotal; i++){
+ FOLDER_S *f = folder_entry(i, FOLDERS(c_list));
+ unsigned char *fname;
+
+ ps->user_says_cancel = 0;
+
+ if((c_list->use & CNTXT_ZOOM) && !f->selected)
+ continue;
+
+ fcount++;
+
+ fname = folder_name_decoded((unsigned char *)FLDR_NAME(f));
+
+ width = utf8_width(fname ? (char *)fname : FLDR_NAME(f));
+
+ if(fname)
+ fs_give((void **)&fname);
+
+ if(f->isdir)
+ width += (f->isfolder) ? 3 : 1;
+
+ if(NEWS_TEST(c_list) && (c_list->use & CNTXT_FINDALL))
+ /* assume listmode so we don't have to reformat */
+ /* if listmode is actually entered */
+ width += 4;
+
+ if(selected){
+ if(F_OFF(F_SELECTED_SHOWN_BOLD, ps_global))
+ width += 4; /* " X " */
+ }
+ else if(c_list == fp->fs->list_cntxt)
+ width += 4; /* "[X] " */
+
+ if(fp->fs->include_unseen_cnt
+ && c_list->use & CNTXT_INCMNG
+ && F_ON(F_ENABLE_INCOMING_CHECKING, ps_global)
+ && ps_global->VAR_INCOMING_FOLDERS
+ && ps_global->VAR_INCOMING_FOLDERS[0]){
+
+ update_folder_unseen(f, c_list, UFU_ANNOUNCE, NULL);
+ if(F_ON(F_INCOMING_CHECKING_RECENT, ps_global)
+ && f->unseen_valid
+ && (f->new > 0L
+ || F_ON(F_INCOMING_CHECKING_TOTAL, ps_global))){
+ width += (strlen(tose(f->new)) + 3);
+ if(F_ON(F_INCOMING_CHECKING_TOTAL, ps_global))
+ width += (strlen(tose(f->total)) + 1);
+ }
+ else if(F_OFF(F_INCOMING_CHECKING_RECENT, ps_global)
+ && f->unseen_valid
+ && (f->unseen > 0L
+ || F_ON(F_INCOMING_CHECKING_TOTAL, ps_global))){
+ width += (strlen(tose(f->unseen)) + 3);
+ if(F_ON(F_INCOMING_CHECKING_TOTAL, ps_global))
+ width += (strlen(tose(f->total)) + 1);
+ }
+ else if(!f->unseen_valid && f->last_unseen_update != LUU_NEVERCHK)
+ width += 4; /* " (?)" */
+ }
+
+ if(slot_width < width)
+ slot_width = width;
+ }
+
+ if(F_ON(F_SINGLE_FOLDER_LIST, ps_global)){
+ slot_cols = 1;
+ slot_rows = fcount;
+ }
+ else{
+ /* fit as many columns as possible */
+ slot_cols = 1;
+ while(((slot_cols+1) * slot_width) + slot_cols <= cols)
+ slot_cols++;
+
+ switch(slot_cols){
+ case 0 :
+ slot_cols = 1;
+ /* fall through */
+
+ case 1 :
+ slot_rows = fcount;
+ break;
+
+ default :
+ /*
+ * Make the slot_width as large as possible.
+ * Slot_width is the width of the column, not counting
+ * the space between columns.
+ */
+ while((slot_cols * (slot_width+1)) + slot_cols-1 <= cols)
+ slot_width++;
+
+ slot_rows = (fcount / slot_cols)
+ + ((fcount % slot_cols) ? 1 : 0);
+ break;
+ }
+ }
+
+ for(i = index = 0; i < slot_rows; i++){
+ if(i)
+ gf_puts("\n", pc);
+
+ for(j = width = 0; j < slot_cols; j++, index++){
+ if(width){
+ gf_puts(repeat_char(slot_width + 1 - width, ' '), pc);
+ width = 0;
+ }
+
+ if(F_ON(F_VERTICAL_FOLDER_LIST, ps_global))
+ index = i + (j * slot_rows);
+
+ findex = index;
+
+ if(c_list->use & CNTXT_ZOOM)
+ findex = folder_list_ith(index, c_list);
+
+ if(findex < ftotal){
+ int flags = (handlesp) ? FLW_LUNK : FLW_NONE;
+
+ if(c_list == fp->fs->list_cntxt)
+ flags |= FLW_LIST;
+ else if(selected)
+ flags |= FLW_SLCT;
+ else if(F_ON(F_SINGLE_FOLDER_LIST, ps_global)
+ || ((c_list->use & CNTXT_FINDALL)
+ && NEWS_TEST(c_list)))
+ gf_puts(" ", pc);
+
+ if(fp->fs->include_unseen_cnt
+ && c_list->use & CNTXT_INCMNG
+ && F_ON(F_ENABLE_INCOMING_CHECKING, ps_global)
+ && ps_global->VAR_INCOMING_FOLDERS
+ && ps_global->VAR_INCOMING_FOLDERS[0])
+ flags |= FLW_UNSEEN;
+
+ width = folder_list_write(pc, handlesp, c_list,
+ findex, NULL, flags);
+ }
+ }
+ }
+ }
+ else if(fp->fs->combined_view
+ && (F_ON(F_CMBND_SUBDIR_DISP, ps_global)
+ || !c_list->dir->prev)){
+ char *emptiness = N_("[No Folders in Collection]");
+
+ if(utf8_width(_(emptiness)) > ps_global->ttyo->screen_cols)
+ utf8_pad_to_width(lbuf, _(emptiness), sizeof(lbuf), ps_global->ttyo->screen_cols, 1);
+ else
+ strncpy(lbuf, _(emptiness), sizeof(lbuf));
+
+ lbuf[sizeof(lbuf)-1] = '\0';
+
+ gf_puts(folder_list_center_space(lbuf, cols), pc);
+ (void) folder_list_write(pc, handlesp, c_list, -1, lbuf,
+ (handlesp) ? FLW_LUNK : FLW_NONE);
+ }
+ }
+ else if(fp->fs->combined_view
+ && (F_ON(F_CMBND_SUBDIR_DISP, ps_global)
+ || !c_list->dir->prev)){
+ char *unexpanded = N_("[Select Here to See Expanded List]");
+
+ if(utf8_width(_(unexpanded)) > ps_global->ttyo->screen_cols)
+ utf8_pad_to_width(lbuf, _(unexpanded), sizeof(lbuf), ps_global->ttyo->screen_cols, 1);
+ else
+ strncpy(lbuf, _(unexpanded), sizeof(lbuf));
+
+ lbuf[sizeof(lbuf)-1] = '\0';
+
+ gf_puts(folder_list_center_space(lbuf, cols), pc);
+ (void) folder_list_write(pc, handlesp, c_list, -1, lbuf,
+ (handlesp) ? FLW_LUNK : FLW_NONE);
+ }
+
+ gf_puts("\n", pc); /* blank line */
+
+ }
+ while(fp->fs->combined_view
+ && (F_ON(F_CMBND_SUBDIR_DISP, ps_global) || !c_list->dir->prev)
+ && (c_list = c_list->next));
+
+ return(rv);
+}
+
+
+int
+folder_list_write(gf_io_t pc, HANDLE_S **handlesp, CONTEXT_S *ctxt, int fnum, char *alt_name, int flags)
+{
+ char buf[256];
+ int width = 0, lprefix = 0, lmiddle = 0, lsuffix = 0;
+ FOLDER_S *fp;
+ HANDLE_S *h1 = NULL, *h2 = NULL;
+
+ if(flags & FLW_LUNK){
+ h1 = new_handle(handlesp);
+ h1->type = Folder;
+ h1->h.f.index = fnum;
+ h1->h.f.context = ctxt;
+ h1->force_display = 1;
+
+ snprintf(buf, sizeof(buf), "%d", h1->key);
+ buf[sizeof(buf)-1] = '\0';
+ }
+
+ fp = (fnum < 0) ? NULL : folder_entry(fnum, FOLDERS(ctxt));
+
+ if(flags & FLW_LUNK && h1 && fp && fp->isdir && fp->isfolder){
+ h2 = new_handle(handlesp);
+ h2->type = Folder;
+ h2->h.f.index = fnum;
+ h2->h.f.context = ctxt;
+ h2->force_display = 1;
+
+ h1->is_dual_do_open = 1;
+ }
+
+ if(h1){
+ /* color unseen? */
+ if(color_monitored_unseen(fp, flags)){
+ h1->color_unseen = 1;
+ if(h2)
+ h2->color_unseen = 1;
+ }
+ }
+
+ /* embed handle pointer */
+ if(((h1 && h1->color_unseen) ?
+ gf_puts(color_embed(ps_global->VAR_INCUNSEEN_FORE_COLOR,
+ ps_global->VAR_INCUNSEEN_BACK_COLOR), pc) : 1)
+ && (h1 ? ((*pc)(TAG_EMBED) && (*pc)(TAG_HANDLE)
+ && (*pc)(strlen(buf)) && gf_puts(buf, pc)) : 1)
+ && (fp ? ((lprefix = folder_list_write_prefix(fp, flags, pc)) >= 0
+ && (lmiddle = folder_list_write_middle(fp, ctxt, pc, h2)) >= 0
+ && ((lsuffix = folder_list_write_suffix(fp, flags, pc)) >= 0))
+ : (alt_name ? gf_puts(alt_name, pc) : 0))
+ && (h1 ? ((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDOFF)
+ && (*pc)(TAG_EMBED) && (*pc)(TAG_INVOFF)) : 1)
+ && ((h1 && h1->color_unseen) ?
+ gf_puts(color_embed(ps_global->VAR_NORM_FORE_COLOR,
+ ps_global->VAR_NORM_BACK_COLOR), pc) : 1)){
+ if(fp)
+ width = lprefix + lmiddle + lsuffix;
+ else if(alt_name)
+ width = utf8_width(alt_name);
+ }
+
+ return(width);
+}
+
+
+int
+folder_list_write_prefix(FOLDER_S *f, int flags, gf_io_t pc)
+{
+ int rv = 0;
+
+ if(flags & FLW_SLCT){
+ if(F_OFF(F_SELECTED_SHOWN_BOLD, ps_global) || !(flags & FLW_LUNK)){
+ rv = 4;
+ if(f->selected){
+ gf_puts(" X ", pc);
+ }
+ else{
+ gf_puts(" ", pc);
+ }
+ }
+ else
+ rv = ((*pc)(TAG_EMBED)
+ && (*pc)((f->selected) ? TAG_BOLDON : TAG_BOLDOFF)) ? 0 : -1;
+ }
+ else if(flags & FLW_LIST){
+ rv = 4;
+ /* screen width of "SUB " is 4 */
+ gf_puts(f->subscribed ? "SUB " : (f->selected ? "[X] " : "[ ] "), pc);
+ }
+
+ return(rv);
+}
+
+
+int
+folder_list_write_middle(FOLDER_S *fp, CONTEXT_S *ctxt, gf_io_t pc, HANDLE_S *h2)
+{
+ int rv = -1;
+ char buf[256];
+ unsigned char *fname;
+
+ if(h2){
+ snprintf(buf, sizeof(buf), "%d", h2->key);
+ buf[sizeof(buf)-1] = '\0';
+ }
+
+ if(!fp)
+ return(rv);
+
+ fname = folder_name_decoded((unsigned char *)FLDR_NAME(fp));
+
+ if(gf_puts(fname ? (char *)fname : FLDR_NAME(fp), pc)
+ && (h2 ? ((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDOFF) /* tie off handle 1 */
+ && (*pc)(TAG_EMBED) && (*pc)(TAG_INVOFF)) : 1)
+ && (h2 ? ((*pc)(TAG_EMBED) && (*pc)(TAG_HANDLE) /* start handle 2 */
+ && (*pc)(strlen(buf)) && gf_puts(buf, pc)) : 1)
+ && ((fp->isdir && fp->isfolder) ? (*pc)('[') : 1)
+ && ((fp->isdir) ? (*pc)(ctxt->dir->delim) : 1)
+ && ((fp->isdir && fp->isfolder) ? (*pc)(']') : 1)){
+ rv = utf8_width(fname ? (char *)fname : FLDR_NAME(fp));
+ if(fp->isdir)
+ rv += (fp->isfolder) ? 3 : 1;
+ }
+
+ if(fname)
+ fs_give((void **)&fname);
+
+ return(rv);
+}
+
+
+int
+folder_list_write_suffix(FOLDER_S *f, int flags, gf_io_t pc)
+{
+ int rv = 0;
+
+ if(flags & FLW_UNSEEN){
+ char buf[100];
+
+ buf[0] = '\0';
+ if(F_ON(F_INCOMING_CHECKING_RECENT, ps_global)
+ && f->unseen_valid
+ && (f->new > 0L
+ || (F_ON(F_INCOMING_CHECKING_TOTAL, ps_global)
+ && f->total > 0L))){
+ snprintf(buf, sizeof(buf), " (%s%s%s)",
+ tose(f->new),
+ F_ON(F_INCOMING_CHECKING_TOTAL, ps_global) ? "/" : "",
+ F_ON(F_INCOMING_CHECKING_TOTAL, ps_global) ? tose(f->total) : "");
+ }
+ else if(F_OFF(F_INCOMING_CHECKING_RECENT, ps_global)
+ && f->unseen_valid
+ && (f->unseen > 0L
+ || (F_ON(F_INCOMING_CHECKING_TOTAL, ps_global)
+ && f->total > 0L))){
+ snprintf(buf, sizeof(buf), " (%s%s%s)",
+ tose(f->unseen),
+ F_ON(F_INCOMING_CHECKING_TOTAL, ps_global) ? "/" : "",
+ F_ON(F_INCOMING_CHECKING_TOTAL, ps_global) ? tose(f->total) : "");
+ }
+ else if(!f->unseen_valid && f->last_unseen_update != LUU_NEVERCHK){
+ snprintf(buf, sizeof(buf), " (?)");
+ }
+
+ rv = strlen(buf);
+ if(rv)
+ gf_puts(buf, pc);
+ }
+
+ return(rv);
+}
+
+
+int
+color_monitored_unseen(FOLDER_S *f, int flags)
+{
+ return((flags & FLW_UNSEEN) && f && f->unseen_valid
+ && ((F_ON(F_INCOMING_CHECKING_RECENT, ps_global) && f->new > 0L)
+ || (F_OFF(F_INCOMING_CHECKING_RECENT, ps_global) && f->unseen > 0L))
+ && pico_usingcolor()
+ && pico_is_good_color(ps_global->VAR_INCUNSEEN_FORE_COLOR)
+ && pico_is_good_color(ps_global->VAR_INCUNSEEN_BACK_COLOR)
+ && (colorcmp(ps_global->VAR_INCUNSEEN_FORE_COLOR,
+ ps_global->VAR_NORM_FORE_COLOR)
+ || colorcmp(ps_global->VAR_INCUNSEEN_BACK_COLOR,
+ ps_global->VAR_NORM_BACK_COLOR)));
+}
+
+
+int
+folder_list_ith(int n, CONTEXT_S *cntxt)
+{
+ int index, ftotal;
+ FOLDER_S *f;
+
+ for(index = 0, ftotal = folder_total(FOLDERS(cntxt));
+ index < ftotal
+ && (f = folder_entry(index, FOLDERS(cntxt)))
+ && !(f->selected && !n--);
+ index++)
+ ;
+
+ return(index);
+}
+
+
+char *
+folder_list_center_space(char *s, int width)
+{
+ int l;
+
+ return(((l = utf8_width(s)) < width) ? repeat_char((width - l)/2, ' ') : "");
+}
+
+
+/*
+ * folder_list_handle - return pointer in handle list
+ * corresponding to "start"
+ */
+HANDLE_S *
+folder_list_handle(FSTATE_S *fs, HANDLE_S *handles)
+{
+ char *p, *name = NULL;
+ HANDLE_S *h, *h_found = NULL;
+ FOLDER_S *fp;
+
+ if(handles && fs->context){
+ if(!(NEWS_TEST(fs->context) || (fs->context->use & CNTXT_INCMNG))
+ && fs->context->dir->delim)
+ for(p = strchr(fs->first_folder, fs->context->dir->delim);
+ p;
+ p = strchr(p, fs->context->dir->delim))
+ name = ++p;
+
+ for(h = handles; h; h = h->next)
+ if(h->h.f.context == fs->context){
+ if(!h_found) /* match at least given context */
+ h_found = h;
+
+ if(!fs->first_folder[0]
+ || ((fp = folder_entry(h->h.f.index, FOLDERS(h->h.f.context)))
+ && ((fs->first_dir && fp->isdir)
+ || (!fs->first_dir && fp->isfolder))
+ && ((fp->nickname && !strcmp(name ? name : fs->first_folder, fp->nickname))
+ || (fp->name && !strcmp(name ? name : fs->first_folder, fp->name))))){
+ h_found = h;
+ break;
+ }
+ }
+
+ fs->first_folder[0] = '\0';
+ }
+
+ return(h_found ? h_found : handles);
+}
+
+
+int
+folder_processor(int cmd, MSGNO_S *msgmap, SCROLL_S *sparms)
+{
+ int rv = 0;
+ char tmp_output[MAILTMPLEN];
+
+ switch(cmd){
+ case MC_FINISH :
+ FPROC(sparms)->done = rv = 1;;
+ break;
+
+ /*---------- Select or enter a View ----------*/
+ case MC_CHOICE :
+ rv = folder_lister_choice(sparms);
+ break;
+
+ /*--------- Hidden "To Fldrs" command -----------*/
+ case MC_LISTMODE :
+ if(sparms->text.handles
+ && folder_total(FOLDERS(sparms->text.handles->h.f.context))){
+ if(!FPROC(sparms)->fs->list_cntxt){
+ FPROC(sparms)->fs->list_cntxt
+ = sparms->text.handles->h.f.context;
+ rv = scroll_add_listmode(sparms->text.handles->h.f.context,
+ folder_total(FOLDERS(sparms->text.handles->h.f.context)));
+ if(!rv){
+ /* need to set the subscribe key ourselves */
+ sparms->keys.menu->keys[SB_SUB_KEY].name = "S";
+ sparms->keys.menu->keys[SB_SUB_KEY].label = N_("Subscribe");
+ sparms->keys.menu->keys[SB_SUB_KEY].bind.cmd = MC_CHOICE;
+ sparms->keys.menu->keys[SB_SUB_KEY].bind.ch[0] = 's';
+ setbitn(SB_SUB_KEY, sparms->keys.bitmap);
+ ps_global->mangled_screen = 1;
+
+ sparms->keys.menu->keys[SB_EXIT_KEY].bind.cmd = MC_EXITQUERY;
+ }
+ q_status_message(SM_ORDER, 0, 1, LISTMODE_GRIPE);
+ }
+ else
+ q_status_message(SM_ORDER, 0, 4, _("Already in List Mode"));
+ }
+ else
+ q_status_message(SM_ORDER, 0, 4,
+ _("No Folders! Can't enter List Mode"));
+
+ break;
+
+
+ /*--------- Visit parent directory -----------*/
+ case MC_PARENT :
+ if(folder_lister_parent(FPROC(sparms)->fs,
+ (sparms->text.handles)
+ ? sparms->text.handles->h.f.context
+ : FPROC(sparms)->fs->context,
+ (sparms->text.handles)
+ ? sparms->text.handles->h.f.index : -1, 0))
+ rv = 1; /* leave scrolltool to rebuild screen */
+
+ break;
+
+
+ /*--------- Open the selected folder -----------*/
+ case MC_OPENFLDR :
+ if(sparms->text.handles
+ && folder_total(FOLDERS(sparms->text.handles->h.f.context)))
+ rv = folder_lister_finish(sparms, sparms->text.handles->h.f.context,
+ sparms->text.handles->h.f.index);
+ else
+ q_status_message(SM_ORDER, 0, 4,
+ _("No Folders! Nothing to View"));
+
+ break;
+
+
+ /*--------- Export the selected folder -----------*/
+ case MC_EXPORT :
+ folder_export(sparms);
+ break;
+
+
+ /*--------- Import the selected folder -----------*/
+ case MC_IMPORT :
+ {
+ CONTEXT_S *cntxt = (sparms->text.handles)
+ ? sparms->text.handles->h.f.context
+ : FPROC(sparms)->fs->context;
+ char new_file[2*MAXFOLDER+10];
+ int r;
+
+ new_file[0] = '\0';
+
+ r = folder_import(sparms, new_file, sizeof(new_file));
+
+ if(r && (cntxt->use & CNTXT_INCMNG || context_isambig(new_file))){
+ rv = 1; /* rebuild display! */
+ FPROC(sparms)->fs->context = cntxt;
+ if(strlen(new_file) < MAXFOLDER - 1){
+ strncpy(FPROC(sparms)->fs->first_folder, new_file, MAXFOLDER);
+ FPROC(sparms)->fs->first_folder[MAXFOLDER-1] = '\0';
+ }
+ }
+ else
+ ps_global->mangled_footer++;
+ }
+
+ break;
+
+
+ /*--------- Return to the Collections Screen -----------*/
+ case MC_COLLECTIONS :
+ FPROC(sparms)->done = rv = 1;
+ break;
+
+
+ /*--------- QUIT pine -----------*/
+ case MC_QUIT :
+ ps_global->next_screen = quit_screen;
+ FPROC(sparms)->done = rv = 1;
+ break;
+
+
+ /*--------- Compose -----------*/
+ case MC_COMPOSE :
+ ps_global->next_screen = compose_screen;
+ FPROC(sparms)->done = rv = 1;
+ break;
+
+
+ /*--------- Alt Compose -----------*/
+ case MC_ROLE :
+ ps_global->next_screen = alt_compose_screen;
+ FPROC(sparms)->done = rv = 1;
+ break;
+
+
+ /*--------- Message Index -----------*/
+ case MC_INDEX :
+ if(THREADING()
+ && sp_viewing_a_thread(ps_global->mail_stream)
+ && unview_thread(ps_global, ps_global->mail_stream, ps_global->msgmap)){
+ ps_global->next_screen = mail_index_screen;
+ ps_global->view_skipped_index = 0;
+ ps_global->mangled_screen = 1;
+ }
+
+ ps_global->next_screen = mail_index_screen;
+ FPROC(sparms)->done = rv = 1;
+ break;
+
+
+ /*----------------- Add a new folder name -----------*/
+ case MC_ADDFLDR :
+ {
+ CONTEXT_S *cntxt = (sparms->text.handles)
+ ? sparms->text.handles->h.f.context
+ : FPROC(sparms)->fs->context;
+ char new_file[2*MAXFOLDER+10];
+ int r;
+
+ if(NEWS_TEST(cntxt))
+ r = group_subscription(new_file, sizeof(new_file), cntxt);
+ else{
+ r = add_new_folder(cntxt, Main, V_INCOMING_FOLDERS, new_file,
+ sizeof(new_file),
+ FPROC(sparms)->fs->cache_streamp
+ ? *FPROC(sparms)->fs->cache_streamp : NULL,
+ NULL);
+ if(ps_global->prc && ps_global->prc->outstanding_pinerc_changes)
+ write_pinerc(ps_global, Main, WRP_NONE);
+ }
+
+ if(r && (cntxt->use & CNTXT_INCMNG || context_isambig(new_file))){
+ rv = 1; /* rebuild display! */
+ FPROC(sparms)->fs->context = cntxt;
+ if(strlen(new_file) < MAXFOLDER - 1){
+ strncpy(FPROC(sparms)->fs->first_folder, new_file, MAXFOLDER);
+ FPROC(sparms)->fs->first_folder[MAXFOLDER-1] = '\0';
+ }
+ }
+ else
+ ps_global->mangled_footer++;
+ }
+
+ break;
+
+
+ /*------ Type in new folder name, e.g., for save ----*/
+ case MC_ADD :
+ rv = folder_lister_addmanually(sparms);
+ break;
+
+
+ /*--------------- Rename folder ----------------*/
+ case MC_RENAMEFLDR :
+ if(sparms->text.handles
+ && folder_total(FOLDERS(sparms->text.handles->h.f.context))){
+ char new_file[MAXFOLDER+1];
+ int r;
+
+ r = rename_folder(sparms->text.handles->h.f.context,
+ sparms->text.handles->h.f.index, new_file,
+ sizeof(new_file),
+ FPROC(sparms)->fs->cache_streamp
+ ? *FPROC(sparms)->fs->cache_streamp : NULL);
+
+ if(r){
+ /* repaint, placing cursor on new folder! */
+ rv = 1;
+ if(context_isambig(new_file)){
+ FPROC(sparms)->fs->context
+ = sparms->text.handles->h.f.context;
+ if(strlen(new_file) < MAXFOLDER - 1){
+ strncpy(FPROC(sparms)->fs->first_folder, new_file, MAXFOLDER);
+ FPROC(sparms)->fs->first_folder[MAXFOLDER-1] = '\0';
+ }
+ }
+ }
+
+ ps_global->mangled_footer++;
+ }
+ else
+ q_status_message(SM_ORDER | SM_DING, 0, 4,
+ _("Empty folder collection. No folder to rename!"));
+
+ break;
+
+
+ /*-------------- Delete --------------------*/
+ case MC_DELETE :
+ if(!(sparms->text.handles
+ && folder_total(FOLDERS(sparms->text.handles->h.f.context)))){
+ q_status_message(SM_ORDER | SM_DING, 0, 4,
+ _("Empty folder collection. No folder to delete!"));
+ }
+ else{
+ char next_folder[MAILTMPLEN+1];
+
+ next_folder[0] = '\0';
+ if(delete_folder(sparms->text.handles->h.f.context,
+ sparms->text.handles->h.f.index,
+ next_folder, sizeof(next_folder),
+ FPROC(sparms)->fs->cache_streamp
+ && *FPROC(sparms)->fs->cache_streamp
+ ? FPROC(sparms)->fs->cache_streamp : NULL)){
+
+ /* repaint, placing cursor on adjacent folder! */
+ rv = 1;
+ if(next_folder[0] && strlen(next_folder) < MAXFOLDER - 1){
+ strncpy(FPROC(sparms)->fs->first_folder, next_folder, MAXFOLDER);
+ FPROC(sparms)->fs->first_folder[MAXFOLDER-1] = '\0';
+ }
+ else
+ sparms->text.handles->h.f.context->use &= ~CNTXT_ZOOM;
+ }
+ else
+ ps_global->mangled_footer++;
+ }
+
+ break;
+
+
+ /*----------------- Shuffle incoming folder list -----------*/
+ case MC_SHUFFLE :
+ {
+ CONTEXT_S *cntxt = (sparms->text.handles)
+ ? sparms->text.handles->h.f.context : NULL;
+
+ if(!(cntxt && cntxt->use & CNTXT_INCMNG))
+ q_status_message(SM_ORDER, 0, 4,
+ _("May only shuffle Incoming-Folders."));
+ else if(folder_total(FOLDERS(cntxt)) == 0)
+ q_status_message(SM_ORDER, 0, 4,
+ _("No folders to shuffle."));
+ else if(folder_total(FOLDERS(cntxt)) < 2)
+ q_status_message(SM_ORDER, 0, 4,
+ _("Shuffle only makes sense with more than one folder."));
+ else{
+ if(FPROC(sparms) && FPROC(sparms)->fs &&
+ FPROC(sparms)->fs && sparms->text.handles &&
+ sparms->text.handles->h.f.index >= 0 &&
+ sparms->text.handles->h.f.index <
+ folder_total(FOLDERS(cntxt))){
+ strncpy(FPROC(sparms)->fs->first_folder,
+ FLDR_NAME(folder_entry(sparms->text.handles->h.f.index,
+ FOLDERS(cntxt))), MAXFOLDER);
+ FPROC(sparms)->fs->first_folder[MAXFOLDER-1] = '\0';
+ }
+
+ rv = shuffle_incoming_folders(cntxt,
+ sparms->text.handles->h.f.index);
+ }
+ }
+
+ break;
+
+
+ /*-------------- Goto Folder Prompt --------------------*/
+ case MC_GOTO :
+ {
+ int notrealinbox;
+ CONTEXT_S *c = (sparms->text.handles)
+ ? sparms->text.handles->h.f.context
+ : FPROC(sparms)->fs->context;
+ char *new_fold = broach_folder(-FOOTER_ROWS(ps_global), 0, &notrealinbox, &c);
+
+ if(ps_global && ps_global->ttyo){
+ blank_keymenu(ps_global->ttyo->screen_rows - 2, 0);
+ ps_global->mangled_footer = 1;
+ }
+
+ if(new_fold && do_broach_folder(new_fold, c, NULL, notrealinbox ? 0L : DB_INBOXWOCNTXT) > 0){
+ ps_global->next_screen = mail_index_screen;
+ FPROC(sparms)->done = rv = 1;
+ }
+ else
+ ps_global->mangled_footer = 1;
+
+ if((c = ((sparms->text.handles)
+ ? sparms->text.handles->h.f.context
+ : FPROC(sparms)->fs->context))->dir->status & CNTXT_NOFIND)
+ refresh_folder_list(c, FPROC(sparms)->fs->no_dirs, TRUE, FPROC(sparms)->fs->cache_streamp);
+ }
+
+ break;
+
+
+ /*------------- Print list of folders ---------*/
+ case MC_PRINTFLDR :
+ print_folders(FPROC(sparms));
+ ps_global->mangled_footer++;
+ break;
+
+
+ /*----- Select the current folder, or toggle checkbox -----*/
+ case MC_SELCUR :
+ /*---------- Select set of folders ----------*/
+ case MC_SELECT :
+ if(sparms->text.handles
+ && folder_total(FOLDERS(sparms->text.handles->h.f.context))){
+ if(cmd == MC_SELCUR){
+ rv = folder_select_toggle(sparms->text.handles->h.f.context,
+ sparms->text.handles->h.f.index,
+ (NEWS_TEST(sparms->text.handles->h.f.context)
+ && FPROC(sparms)->fs->list_cntxt)
+ ? ng_scroll_edit : folder_select_update);
+ if(!rv) ps_global->mangled_screen = 1;
+ }
+ else
+ switch(folder_select(ps_global,
+ sparms->text.handles->h.f.context,
+ sparms->text.handles->h.f.index)){
+ case 1 :
+ rv = 1; /* rebuild screen */
+
+ case 0 :
+ default :
+ ps_global->mangled_screen++;
+ break;
+ }
+
+ if((sparms->text.handles->h.f.context->use & CNTXT_ZOOM)
+ && !selected_folders(sparms->text.handles->h.f.context)){
+ sparms->text.handles->h.f.context->use &= ~CNTXT_ZOOM;
+ rv = 1; /* make sure to redraw */
+ }
+
+ if(rv){ /* remember where to start */
+ FOLDER_S *fp;
+
+ FPROC(sparms)->fs->context = sparms->text.handles->h.f.context;
+ if((fp = folder_entry(sparms->text.handles->h.f.index,
+ FOLDERS(sparms->text.handles->h.f.context))) != NULL){
+ if(strlen(FLDR_NAME(fp)) < MAXFOLDER - 1){
+ strncpy(FPROC(sparms)->fs->first_folder, FLDR_NAME(fp), MAXFOLDER);
+ FPROC(sparms)->fs->first_folder[MAXFOLDER-1] = '\0';
+ }
+ }
+ }
+ }
+ else
+ q_status_message(SM_ORDER | SM_DING, 0, 4,
+ _("Empty folder collection. No folder to select!"));
+
+ break;
+
+
+ /*---------- Display folders ----------*/
+ case MC_ZOOM :
+ if(sparms->text.handles
+ && folder_total(FOLDERS(sparms->text.handles->h.f.context))){
+ FOLDER_S *fp;
+ int n;
+
+ if((n = selected_folders(sparms->text.handles->h.f.context)) != 0){
+ if(sparms->text.handles->h.f.context->use & CNTXT_ZOOM){
+ sparms->text.handles->h.f.context->use &= ~CNTXT_ZOOM;
+ q_status_message(SM_ORDER, 0, 3,
+ _("Folder List Zoom mode is now off"));
+ }
+ else{
+ q_status_message1(SM_ORDER, 0, 3,
+ _("In Zoomed list of %s folders. Use \"Z\" to restore regular list"),
+ int2string(n));
+ sparms->text.handles->h.f.context->use |= CNTXT_ZOOM;
+ }
+
+ /* exit scrolltool to rebuild screen */
+ rv = 1;
+
+ /* Set where to start after it's rebuilt */
+ FPROC(sparms)->fs->context = sparms->text.handles->h.f.context;
+ FPROC(sparms)->fs->first_folder[0] = '\0';
+ if((fp = folder_entry(sparms->text.handles->h.f.index,
+ FOLDERS(sparms->text.handles->h.f.context)))
+ && !((sparms->text.handles->h.f.context->use & CNTXT_ZOOM)
+ && !fp->selected)
+ && strlen(FLDR_NAME(fp)) < MAXFOLDER - 1){
+ strncpy(FPROC(sparms)->fs->first_folder, FLDR_NAME(fp), MAXFOLDER);
+ FPROC(sparms)->fs->first_folder[MAXFOLDER-1] = '\0';
+ }
+ }
+ else
+ q_status_message(SM_ORDER, 0, 3,
+ _("No selected folders to Zoom on"));
+ }
+ else
+ q_status_message(SM_ORDER, 0, 4, _("No Folders to Zoom on!"));
+
+ break;
+
+ /*----- Ask user to abandon selection before exiting -----*/
+ case MC_EXITQUERY :
+ if(sparms->text.handles
+ && FOLDERS(sparms->text.handles->h.f.context)){
+ int i, folder_n;
+ FOLDER_S *fp;
+
+ folder_n = folder_total(FOLDERS(sparms->text.handles->h.f.context));
+ /* any selected? */
+ for(i = 0; i < folder_n; i++){
+ fp = folder_entry(i, FOLDERS(sparms->text.handles->h.f.context));
+ if(fp->selected)
+ break;
+ }
+
+ if(i < folder_n /* some selections have been made */
+ && want_to(_("Really abandon your selections "),
+ 'y', 'x', NO_HELP, WT_NORM) != 'y'){
+ break;
+ }
+ }
+ rv = 1;
+ break;
+
+ /*------------New Msg command --------------*/
+ case MC_CHK_RECENT:
+ /*
+ * Derived from code provided by
+ * Rostislav Neplokh neplokh@andrew.cmu.edu and
+ * Robert Siemborski (rjs3@andrew).
+ */
+ if(sparms->text.handles
+ && folder_total(FOLDERS(sparms->text.handles->h.f.context))){
+ FOLDER_S *folder;
+
+ folder = folder_entry(sparms->text.handles->h.f.index,
+ FOLDERS(sparms->text.handles->h.f.context));
+
+ if(!folder){
+ strncpy(tmp_output, _("Invalid Folder Name"), sizeof(tmp_output)-1);
+ tmp_output[sizeof(tmp_output)-1] = '\0';
+ }
+ else if(folder->isdir && !folder->isfolder){
+ snprintf(tmp_output, sizeof(tmp_output), _("\"%s\" is a directory"), folder->name);
+ }
+ else{
+ char mailbox_name[MAXPATH+1];
+ unsigned long tot, rec;
+ int we_cancel;
+
+ context_apply(mailbox_name,
+ sparms->text.handles->h.f.context,
+ folder->name, MAXPATH+1);
+
+ we_cancel = busy_cue(NULL, NULL, 1);
+
+ if(get_recent_in_folder(mailbox_name, &rec, NULL, &tot, NULL))
+ snprintf(tmp_output, sizeof(tmp_output),
+ _("%lu total message%s, %lu of them recent"),
+ tot, plural(tot), rec);
+ else
+ snprintf(tmp_output, sizeof(tmp_output),
+ _("%s: Trouble checking for recent mail"), folder->name);
+
+ if(we_cancel)
+ cancel_busy_cue(-1);
+ }
+ }
+ else{
+ strncpy(tmp_output, _("No folder to check! Can't get recent info"),
+ sizeof(tmp_output)-1);
+ tmp_output[sizeof(tmp_output)-1] = '\0';
+ }
+
+ q_status_message(SM_ORDER, 0, 3, tmp_output);
+ break;
+
+
+ /*--------------- Invalid Command --------------*/
+ default:
+ q_status_message1(SM_ORDER, 0, 2, "fix this: cmd = %s", comatose(cmd));
+ break;
+ }
+
+ return(rv);
+}
+
+
+int
+folder_lister_clickclick(SCROLL_S *sparms)
+{
+ if(!FPROC(sparms)->fs->list_cntxt)
+ return(folder_lister_choice(sparms));
+ else
+ return(folder_processor(MC_SELCUR, ps_global->msgmap, sparms));
+}
+
+int
+folder_lister_choice(SCROLL_S *sparms)
+{
+ int rv = 0, empty = 0;
+ int index = (sparms->text.handles)
+ ? sparms->text.handles->h.f.index : 0;
+ CONTEXT_S *cntxt = (sparms->text.handles)
+ ? sparms->text.handles->h.f.context : NULL;
+
+ if(cntxt){
+
+ FPROC(sparms)->fs->context = cntxt;
+
+ if(cntxt->dir->status & CNTXT_NOFIND){
+ rv = 1; /* leave scrolltool to rebuild screen */
+ FPROC(sparms)->fs->context = cntxt;
+ FPROC(sparms)->fs->first_folder[0] = '\0';
+ }
+ else if(folder_total(FOLDERS(cntxt))){
+ if(folder_lister_select(FPROC(sparms)->fs, cntxt, index,
+ sparms->text.handles ?
+ sparms->text.handles->is_dual_do_open : 0)){
+ rv = 1; /* leave scrolltool to rebuild screen */
+ }
+ else if(FPROC(sparms)->fs->list_cntxt == cntxt){
+ int n = 0, i, folder_n;
+ FOLDER_S *fp;
+ STRLIST_S *sl = NULL, **slp;
+
+ /* Scan folder list for selected items */
+ folder_n = folder_total(FOLDERS(cntxt));
+ slp = &sl;
+ for(i = 0; i < folder_n; i++){
+ fp = folder_entry(i, FOLDERS(cntxt));
+ if(fp->selected){
+ n++;
+ if((*FPROC(sparms)->fs->f.valid)(fp,
+ FPROC(sparms)->fs)){
+ *slp = new_strlist(NULL);
+ (*slp)->name = folder_lister_fullname(
+ FPROC(sparms)->fs, FLDR_NAME(fp));
+
+ slp = &(*slp)->next;
+ }
+ else{
+ free_strlist(&sl);
+ break;
+ }
+ }
+ }
+
+ if((FPROC(sparms)->rv = sl) != NULL)
+ FPROC(sparms)->done = rv = 1;
+ else if(!n)
+ q_status_message(SM_ORDER, 0, 1, LISTMODE_GRIPE);
+ }
+ else
+ rv = folder_lister_finish(sparms, cntxt, index);
+ }
+ else
+ empty++;
+ }
+ else
+ empty++;
+
+ if(empty)
+ q_status_message(SM_ORDER | SM_DING, 3, 3, _("Empty folder list!"));
+
+ return(rv);
+}
+
+
+int
+folder_lister_finish(SCROLL_S *sparms, CONTEXT_S *cntxt, int index)
+{
+ FOLDER_S *f = folder_entry(index, FOLDERS(cntxt));
+ int rv = 0;
+
+ if((*FPROC(sparms)->fs->f.valid)(f, FPROC(sparms)->fs)){
+ /*
+ * Package up the selected folder names and return...
+ */
+ FPROC(sparms)->fs->context = cntxt;
+ FPROC(sparms)->rv = new_strlist(NULL);
+ FPROC(sparms)->rv->name = folder_lister_fullname(FPROC(sparms)->fs,
+ FLDR_NAME(f));
+ FPROC(sparms)->done = rv = 1;
+ }
+
+ return(rv);
+}
+
+
+/*
+ * This is so that when you Save and use ^T to go to the folder list, and
+ * you're in a directory with no folders, you have a way to add a new
+ * folder there. The add actually gets done by the caller. This is just a
+ * way to let the user type in the name.
+ */
+int
+folder_lister_addmanually(SCROLL_S *sparms)
+{
+ int rc, flags = OE_APPEND_CURRENT, cnt = 0, rv = 0;
+ char addname[MAXFOLDER+1];
+ HelpType help;
+ CONTEXT_S *cntxt = (sparms->text.handles)
+ ? sparms->text.handles->h.f.context
+ : FPROC(sparms)->fs->context;
+
+ /*
+ * Get the foldername from the user.
+ */
+ addname[0] = '\0';
+ help = NO_HELP;
+ while(1){
+ rc = optionally_enter(addname, -FOOTER_ROWS(ps_global), 0,
+ sizeof(addname), _("Name of new folder : "),
+ NULL, help, &flags);
+ removing_leading_and_trailing_white_space(addname);
+
+ if(rc == 3)
+ help = (help == NO_HELP) ? h_save_addman : NO_HELP;
+ else if(rc == 1)
+ return(rv);
+ else if(rc == 0){
+ if(F_OFF(F_ENABLE_DOT_FOLDERS,ps_global) && *addname == '.'){
+ if(cnt++ <= 0)
+ q_status_message(SM_ORDER,3,3,
+ _("Folder name can't begin with dot"));
+ else
+ q_status_message1(SM_ORDER,3,3,
+ _("Config feature \"%s\" enables names beginning with dot"),
+ pretty_feature_name(feature_list_name(F_ENABLE_DOT_FOLDERS), -1));
+
+ display_message(NO_OP_COMMAND);
+ continue;
+ }
+ else if(!strucmp(addname, ps_global->inbox_name)){
+ q_status_message1(SM_ORDER, 3, 3,
+ _("Can't add folder named %s"),
+ ps_global->inbox_name);
+ continue;
+ }
+
+ break;
+ }
+ }
+
+ if(*addname){
+ FPROC(sparms)->fs->context = cntxt;
+ FPROC(sparms)->rv = new_strlist(NULL);
+ FPROC(sparms)->rv->name = folder_lister_fullname(FPROC(sparms)->fs,
+ addname);
+ FPROC(sparms)->done = rv = 1;
+ }
+
+ return(rv);
+}
+
+
+void
+folder_lister_km_manager(SCROLL_S *sparms, int handle_hidden)
+{
+ FOLDER_S *fp;
+
+ /* if we're "in" a sub-directory, offer way out */
+ if((sparms->text.handles)
+ ? sparms->text.handles->h.f.context->dir->prev
+ : FPROC(sparms)->fs->context->dir->prev){
+
+ /*
+ * Leave the command characters alone and just change
+ * the labels and the bind.cmd for KM_COL_KEY.
+ * Also, leave KM_MAIN_KEY alone instead of trying to
+ * turn it off in the else clause when it is redundant.
+ */
+ sparms->keys.menu->keys[KM_COL_KEY].label = N_("ParentDir");
+ sparms->keys.menu->keys[KM_COL_KEY].bind.cmd = MC_PARENT;
+ }
+ else if((FPROC(sparms)->fs->context->next
+ || FPROC(sparms)->fs->context->prev)
+ && !FPROC(sparms)->fs->combined_view){
+ sparms->keys.menu->keys[KM_COL_KEY].label = N_("ClctnList");
+ sparms->keys.menu->keys[KM_COL_KEY].bind.cmd = MC_EXIT;
+ }
+ else{
+ sparms->keys.menu->keys[KM_COL_KEY].label = N_("Main Menu");
+ sparms->keys.menu->keys[KM_COL_KEY].bind.cmd = MC_MAIN;
+ }
+
+ if(F_OFF(F_ENABLE_AGG_OPS,ps_global)){
+ clrbitn(KM_ZOOM_KEY, sparms->keys.bitmap);
+ clrbitn(KM_SELECT_KEY, sparms->keys.bitmap);
+ clrbitn(KM_SELCUR_KEY, sparms->keys.bitmap);
+ }
+
+ if(sparms->text.handles
+ && (fp = folder_entry(sparms->text.handles->h.f.index,
+ FOLDERS(sparms->text.handles->h.f.context)))){
+ if(fp->isdir && !sparms->text.handles->is_dual_do_open){
+ sparms->keys.menu->keys[KM_SEL_KEY].label = "[" N_("View Dir") "]";
+ menu_add_binding(sparms->keys.menu, 'v', MC_CHOICE);
+ menu_add_binding(sparms->keys.menu, ctrl('M'), MC_CHOICE);
+ menu_add_binding(sparms->keys.menu, ctrl('J'), MC_CHOICE);
+ setbitn(KM_SEL_KEY, sparms->keys.bitmap);
+ clrbitn(KM_ALTVIEW_KEY, sparms->keys.bitmap);
+ clrbitn(KM_EXPORT_KEY, sparms->keys.bitmap);
+ clrbitn(KM_IMPORT_KEY, sparms->keys.bitmap);
+ }
+ else{
+ sparms->keys.menu->keys[KM_SEL_KEY].label = "[" N_("View Fldr") "]";
+ menu_add_binding(sparms->keys.menu, 'v', MC_CHOICE);
+ menu_add_binding(sparms->keys.menu, ctrl('M'), MC_CHOICE);
+ menu_add_binding(sparms->keys.menu, ctrl('J'), MC_CHOICE);
+ setbitn(KM_SEL_KEY, sparms->keys.bitmap);
+ clrbitn(KM_ALTVIEW_KEY, sparms->keys.bitmap);
+ setbitn(KM_EXPORT_KEY, sparms->keys.bitmap);
+ setbitn(KM_IMPORT_KEY, sparms->keys.bitmap);
+ }
+ }
+ else if(FPROC(sparms)->fs->combined_view
+ && sparms->text.handles && sparms->text.handles->h.f.context
+ && !sparms->text.handles->h.f.context->dir->prev){
+ sparms->keys.menu->keys[KM_SEL_KEY].label = "[" N_("View Cltn") "]";
+ menu_add_binding(sparms->keys.menu, 'v', MC_CHOICE);
+ menu_add_binding(sparms->keys.menu, ctrl('M'), MC_CHOICE);
+ menu_add_binding(sparms->keys.menu, ctrl('J'), MC_CHOICE);
+ setbitn(KM_SEL_KEY, sparms->keys.bitmap);
+ clrbitn(KM_ALTVIEW_KEY, sparms->keys.bitmap);
+ clrbitn(KM_EXPORT_KEY, sparms->keys.bitmap);
+ clrbitn(KM_IMPORT_KEY, sparms->keys.bitmap);
+ }
+ else{
+ clrbitn(KM_SEL_KEY, sparms->keys.bitmap);
+ clrbitn(KM_ALTVIEW_KEY, sparms->keys.bitmap);
+ clrbitn(KM_EXPORT_KEY, sparms->keys.bitmap);
+ clrbitn(KM_IMPORT_KEY, sparms->keys.bitmap);
+ }
+
+ if((sparms->text.handles &&
+ sparms->text.handles->h.f.context &&
+ sparms->text.handles->h.f.context->use & CNTXT_INCMNG) ||
+ (FPROC(sparms) && FPROC(sparms)->fs &&
+ FPROC(sparms)->fs->context &&
+ FPROC(sparms)->fs->context->use & CNTXT_INCMNG))
+ setbitn(KM_SHUFFLE_KEY, sparms->keys.bitmap);
+ else
+ clrbitn(KM_SHUFFLE_KEY, sparms->keys.bitmap);
+
+ if(F_ON(F_TAB_CHK_RECENT, ps_global)){
+ menu_clear_binding(sparms->keys.menu, TAB);
+ menu_init_binding(sparms->keys.menu, TAB, MC_CHK_RECENT, "Tab",
+ /* TRANSLATORS: New Messages */
+ N_("NewMsgs"), KM_RECENT_KEY);
+ setbitn(KM_RECENT_KEY, sparms->keys.bitmap);
+ }
+ else{
+ menu_clear_binding(sparms->keys.menu, TAB);
+ menu_add_binding(sparms->keys.menu, TAB, MC_NEXT_HANDLE);
+ clrbitn(KM_RECENT_KEY, sparms->keys.bitmap);
+ }
+
+ /* May have to "undo" what scrolltool "did" */
+ if(F_ON(F_ARROW_NAV, ps_global)){
+ if(F_ON(F_RELAXED_ARROW_NAV, ps_global)){
+ menu_clear_binding(sparms->keys.menu, KEY_LEFT);
+ menu_add_binding(sparms->keys.menu, KEY_LEFT, MC_PREV_HANDLE);
+ menu_clear_binding(sparms->keys.menu, KEY_RIGHT);
+ menu_add_binding(sparms->keys.menu, KEY_RIGHT, MC_NEXT_HANDLE);
+ }
+ else{
+ menu_clear_binding(sparms->keys.menu, KEY_UP);
+ menu_add_binding(sparms->keys.menu, KEY_UP, MC_PREV_HANDLE);
+ menu_clear_binding(sparms->keys.menu, KEY_DOWN);
+ menu_add_binding(sparms->keys.menu, KEY_DOWN, MC_NEXT_HANDLE);
+ }
+ }
+}
+
+
+void
+folder_lister_km_sel_manager(SCROLL_S *sparms, int handle_hidden)
+{
+ FOLDER_S *fp;
+
+ /* if we're "in" a sub-directory, offer way out */
+ if((sparms->text.handles)
+ ? sparms->text.handles->h.f.context->dir->prev
+ : FPROC(sparms)->fs->context->dir->prev){
+ sparms->keys.menu->keys[FC_COL_KEY].name = "<";
+ /* TRANSLATORS: go to parent directory one level up */
+ sparms->keys.menu->keys[FC_COL_KEY].label = N_("ParentDir");
+ sparms->keys.menu->keys[FC_COL_KEY].bind.cmd = MC_PARENT;
+ sparms->keys.menu->keys[FC_COL_KEY].bind.ch[0] = '<';
+ sparms->keys.menu->keys[FC_COL_KEY].bind.ch[1] = ',';
+ if(F_ON(F_ARROW_NAV,ps_global)){
+ sparms->keys.menu->keys[FC_COL_KEY].bind.nch = 3;
+ sparms->keys.menu->keys[FC_COL_KEY].bind.ch[2] = KEY_LEFT;
+ }
+ else
+ sparms->keys.menu->keys[FC_COL_KEY].bind.nch = 2;
+
+ /* ExitSelect in position 1 */
+ setbitn(FC_EXIT_KEY, sparms->keys.bitmap);
+ }
+ else if((FPROC(sparms)->fs->context->next
+ || FPROC(sparms)->fs->context->prev)
+ && !FPROC(sparms)->fs->combined_view){
+ sparms->keys.menu->keys[FC_COL_KEY].name = "<";
+ /* TRANSLATORS: go to Collection List */
+ sparms->keys.menu->keys[FC_COL_KEY].label = N_("ClctnList");
+ sparms->keys.menu->keys[FC_COL_KEY].bind.cmd = MC_COLLECTIONS;
+ sparms->keys.menu->keys[FC_COL_KEY].bind.ch[0] = '<';
+ sparms->keys.menu->keys[FC_COL_KEY].bind.ch[1] = ',';
+ if(F_ON(F_ARROW_NAV,ps_global)){
+ sparms->keys.menu->keys[FC_COL_KEY].bind.nch = 3;
+ sparms->keys.menu->keys[FC_COL_KEY].bind.ch[2] = KEY_LEFT;
+ }
+ else
+ sparms->keys.menu->keys[FC_COL_KEY].bind.nch = 2;
+
+ /* ExitSelect in position 1 */
+ setbitn(FC_EXIT_KEY, sparms->keys.bitmap);
+ }
+ else if(FPROC(sparms)->fs->combined_view){
+ /*
+ * This can't be a menu_init_binding() because we don't want
+ * to remove the ExitSelect command in position FC_EXIT_KEY.
+ * We just turn it off until we need it again.
+ */
+ sparms->keys.menu->keys[FC_COL_KEY].name = "E";
+ sparms->keys.menu->keys[FC_COL_KEY].label = N_("ExitSelect");
+ sparms->keys.menu->keys[FC_COL_KEY].bind.cmd = MC_EXIT;
+ sparms->keys.menu->keys[FC_COL_KEY].bind.nch = 1;
+ sparms->keys.menu->keys[FC_COL_KEY].bind.ch[0] = 'e';
+
+ /* turn off ExitSelect in position 1, it's in 2 now */
+ clrbitn(FC_EXIT_KEY, sparms->keys.bitmap);
+ }
+
+ /* clean up per-entry bindings */
+ clrbitn(FC_SEL_KEY, sparms->keys.bitmap);
+ clrbitn(FC_ALTSEL_KEY, sparms->keys.bitmap);
+ menu_clear_binding(sparms->keys.menu, ctrl('M'));
+ menu_clear_binding(sparms->keys.menu, ctrl('J'));
+ menu_clear_binding(sparms->keys.menu, '>');
+ menu_clear_binding(sparms->keys.menu, '.');
+ menu_clear_binding(sparms->keys.menu, 's');
+ if(F_ON(F_ARROW_NAV,ps_global))
+ menu_clear_binding(sparms->keys.menu, KEY_RIGHT);
+
+ /* and then re-assign them as needed */
+ if(sparms->text.handles
+ && (fp = folder_entry(sparms->text.handles->h.f.index,
+ FOLDERS(sparms->text.handles->h.f.context)))){
+ setbitn(FC_SEL_KEY, sparms->keys.bitmap);
+ if(fp->isdir){
+ sparms->keys.menu->keys[FC_SEL_KEY].name = ">";
+ menu_add_binding(sparms->keys.menu, '>', MC_CHOICE);
+ menu_add_binding(sparms->keys.menu, '.', MC_CHOICE);
+ if(F_ON(F_ARROW_NAV,ps_global))
+ menu_add_binding(sparms->keys.menu, KEY_RIGHT, MC_CHOICE);
+
+ if(fp->isfolder){
+ sparms->keys.menu->keys[FC_SEL_KEY].label = N_("View Dir");
+ setbitn(FC_ALTSEL_KEY, sparms->keys.bitmap);
+ menu_add_binding(sparms->keys.menu, 's', MC_OPENFLDR);
+ menu_add_binding(sparms->keys.menu, ctrl('M'), MC_OPENFLDR);
+ menu_add_binding(sparms->keys.menu, ctrl('J'), MC_OPENFLDR);
+ }
+ else{
+ sparms->keys.menu->keys[FC_SEL_KEY].label = "[" N_("View Dir") "]";
+ menu_add_binding(sparms->keys.menu, ctrl('M'), MC_CHOICE);
+ menu_add_binding(sparms->keys.menu, ctrl('J'), MC_CHOICE);
+ }
+ }
+ else{
+ sparms->keys.menu->keys[FC_SEL_KEY].name = "S";
+ sparms->keys.menu->keys[FC_SEL_KEY].label = "[" N_("Select") "]";
+
+ menu_add_binding(sparms->keys.menu, 's', MC_CHOICE);
+ menu_add_binding(sparms->keys.menu, ctrl('M'), MC_CHOICE);
+ menu_add_binding(sparms->keys.menu, ctrl('J'), MC_CHOICE);
+ }
+
+ if(F_ON(F_ARROW_NAV,ps_global))
+ menu_add_binding(sparms->keys.menu, KEY_RIGHT, MC_CHOICE);
+ }
+ else if(FPROC(sparms)->fs->combined_view
+ && sparms->text.handles && sparms->text.handles->h.f.context
+ && !sparms->text.handles->h.f.context->dir->prev){
+ setbitn(FC_SEL_KEY, sparms->keys.bitmap);
+ sparms->keys.menu->keys[FC_SEL_KEY].name = ">";
+ sparms->keys.menu->keys[FC_SEL_KEY].label = "[" N_("View Cltn") "]";
+ menu_add_binding(sparms->keys.menu, '>', MC_CHOICE);
+ menu_add_binding(sparms->keys.menu, '.', MC_CHOICE);
+ if(F_ON(F_ARROW_NAV,ps_global))
+ menu_add_binding(sparms->keys.menu, KEY_RIGHT, MC_CHOICE);
+
+ menu_add_binding(sparms->keys.menu, ctrl('M'), MC_CHOICE);
+ menu_add_binding(sparms->keys.menu, ctrl('J'), MC_CHOICE);
+ }
+
+ /* May have to "undo" what scrolltool "did" */
+ if(F_ON(F_ARROW_NAV, ps_global)){
+ if(F_ON(F_RELAXED_ARROW_NAV, ps_global)){
+ menu_clear_binding(sparms->keys.menu, KEY_LEFT);
+ menu_add_binding(sparms->keys.menu, KEY_LEFT, MC_PREV_HANDLE);
+ menu_clear_binding(sparms->keys.menu, KEY_RIGHT);
+ menu_add_binding(sparms->keys.menu, KEY_RIGHT, MC_NEXT_HANDLE);
+ }
+ else{
+ menu_clear_binding(sparms->keys.menu, KEY_UP);
+ menu_add_binding(sparms->keys.menu, KEY_UP, MC_PREV_HANDLE);
+ menu_clear_binding(sparms->keys.menu, KEY_DOWN);
+ menu_add_binding(sparms->keys.menu, KEY_DOWN, MC_NEXT_HANDLE);
+ }
+ }
+}
+
+
+void
+folder_lister_km_sub_manager(SCROLL_S *sparms, int handle_hidden)
+{
+ /*
+ * Folder_processor() also modifies the keymenu.
+ */
+ if(FPROC(sparms)->fs->list_cntxt){
+ clrbitn(SB_LIST_KEY, sparms->keys.bitmap);
+ sparms->keys.menu->keys[SB_SEL_KEY].name = "X";
+ sparms->keys.menu->keys[SB_SEL_KEY].label = "[" N_("Set/Unset") "]";
+ sparms->keys.menu->keys[SB_SEL_KEY].bind.cmd = MC_SELCUR;
+ sparms->keys.menu->keys[SB_SEL_KEY].bind.ch[0] = 'x';
+ }
+ else{
+ clrbitn(SB_SUB_KEY, sparms->keys.bitmap);
+ sparms->keys.menu->keys[SB_SEL_KEY].name = "S";
+ sparms->keys.menu->keys[SB_SEL_KEY].label = "[" N_("Subscribe") "]";
+ sparms->keys.menu->keys[SB_SEL_KEY].bind.cmd = MC_CHOICE;
+ sparms->keys.menu->keys[SB_SEL_KEY].bind.ch[0] = 's';
+ }
+
+ /* May have to "undo" what scrolltool "did" */
+ if(F_ON(F_ARROW_NAV, ps_global)){
+ if(F_ON(F_RELAXED_ARROW_NAV, ps_global)){
+ menu_clear_binding(sparms->keys.menu, KEY_LEFT);
+ menu_add_binding(sparms->keys.menu, KEY_LEFT, MC_PREV_HANDLE);
+ menu_clear_binding(sparms->keys.menu, KEY_RIGHT);
+ menu_add_binding(sparms->keys.menu, KEY_RIGHT, MC_NEXT_HANDLE);
+ }
+ else{
+ menu_clear_binding(sparms->keys.menu, KEY_UP);
+ menu_add_binding(sparms->keys.menu, KEY_UP, MC_PREV_HANDLE);
+ menu_clear_binding(sparms->keys.menu, KEY_DOWN);
+ menu_add_binding(sparms->keys.menu, KEY_DOWN, MC_NEXT_HANDLE);
+ }
+ }
+}
+
+
+
+int
+folder_select(struct pine *ps, CONTEXT_S *context, int cur_index)
+{
+ int i, j, n, total, old_tot, diff,
+ q = 0, rv = 0, narrow = 0;
+ HelpType help = NO_HELP;
+ ESCKEY_S *sel_opts;
+ FOLDER_S *f;
+ static ESCKEY_S self_opts2[] = {
+ /* TRANSLATORS: keymenu descriptions, select all folders, current folder, select
+ based on folder properties, or select based on text contents in folders */
+ {'a', 'a', "A", N_("select All")},
+ {'c', 'c', "C", N_("select Cur")},
+ {'p', 'p', "P", N_("Properties")},
+ {'t', 't', "T", N_("Text")},
+ {-1, 0, NULL, NULL},
+ {-1, 0, NULL, NULL}
+ };
+ extern ESCKEY_S sel_opts1[];
+ extern char *sel_pmt2;
+#define N_RECENT 4
+
+ f = folder_entry(cur_index, FOLDERS(context));
+
+ sel_opts = self_opts2;
+ if((old_tot = selected_folders(context)) != 0){
+ sel_opts1[1].label = f->selected ? N_("unselect Cur") : N_("select Cur");
+ sel_opts += 2; /* disable extra options */
+ switch(q = radio_buttons(SEL_ALTER_PMT, -FOOTER_ROWS(ps_global),
+ sel_opts1, 'c', 'x', help, RB_NORM)){
+ case 'f' : /* flip selection */
+ n = folder_total(FOLDERS(context));
+ for(total = i = 0; i < n; i++)
+ if((f = folder_entry(i, FOLDERS(context))) != NULL)
+ f->selected = !f->selected;
+
+ return(1); /* repaint */
+
+ case 'n' : /* narrow selection */
+ narrow++;
+ case 'b' : /* broaden selection */
+ q = 0; /* but don't offer criteria prompt */
+ if(context->use & CNTXT_INCMNG){
+ q_status_message1(SM_ORDER | SM_DING, 3, 3,
+ _("Select \"%s\" not supported in Incoming Folders"),
+ narrow ? "Narrow" : "Broaden");
+ return(0);
+ }
+
+ break;
+
+ case 'c' : /* Un/Select Current */
+ case 'a' : /* Unselect All */
+ case 'x' : /* cancel */
+ break;
+
+ default :
+ q_status_message(SM_ORDER | SM_DING, 3, 3,
+ _("Unsupported Select option"));
+ return(0);
+ }
+ }
+
+ if(context->use & CNTXT_INCMNG && F_ON(F_ENABLE_INCOMING_CHECKING, ps_global)){
+ if(F_ON(F_INCOMING_CHECKING_RECENT, ps_global)){
+ self_opts2[N_RECENT].ch = 'r';
+ self_opts2[N_RECENT].rval = 'r';
+ self_opts2[N_RECENT].name = "R";
+ self_opts2[N_RECENT].label = N_("Recent");
+ }
+ else{
+ self_opts2[N_RECENT].ch = 'u';
+ self_opts2[N_RECENT].rval = 'u';
+ self_opts2[N_RECENT].name = "U";
+ self_opts2[N_RECENT].label = N_("Unseen");
+ }
+ }
+ else{
+ self_opts2[N_RECENT].ch = -1;
+ self_opts2[N_RECENT].rval = 0;
+ self_opts2[N_RECENT].name = NULL;
+ self_opts2[N_RECENT].label = NULL;
+ }
+
+ if(!q)
+ q = radio_buttons(sel_pmt2, -FOOTER_ROWS(ps_global),
+ sel_opts, 'c', 'x', help, RB_NORM);
+
+ /*
+ * NOTE: See note about MESSAGECACHE "searched" bits above!
+ */
+ switch(q){
+ case 'x': /* cancel */
+ cmd_cancelled("Select command");
+ return(0);
+
+ case 'c' : /* toggle current's selected state */
+ return(folder_select_toggle(context, cur_index, folder_select_update));
+
+ case 'a' : /* select/unselect all */
+ n = folder_total(FOLDERS(context));
+ for(total = i = 0; i < n; i++)
+ folder_entry(i, FOLDERS(context))->selected = old_tot == 0;
+
+ q_status_message4(SM_ORDER, 0, 2,
+ "%s%s folder%s %sselected",
+ old_tot ? "" : "All ",
+ comatose(old_tot ? old_tot : n),
+ plural(old_tot ? old_tot : n), old_tot ? "UN" : "");
+ return(1);
+
+ case 't' : /* Text */
+ if(!folder_select_text(ps, context, narrow))
+ rv++;
+
+ break;
+
+ case 'p' : /* Properties */
+ if(!folder_select_props(ps, context, narrow))
+ rv++;
+
+ break;
+
+ case 'r' :
+ n = folder_total(FOLDERS(context));
+ for(i = 0; i < n; i++){
+ f = folder_entry(i, FOLDERS(context));
+ if(f->unseen_valid && f->new > 0L)
+ f->selected = 1;
+ }
+
+ break;
+
+ case 'u' :
+ n = folder_total(FOLDERS(context));
+ for(i = 0; i < n; i++){
+ f = folder_entry(i, FOLDERS(context));
+ if(f->unseen_valid && f->unseen > 0L)
+ f->selected = 1;
+ }
+
+ break;
+
+ default :
+ q_status_message(SM_ORDER | SM_DING, 3, 3,
+ _("Unsupported Select option"));
+ return(0);
+ }
+
+ if(rv)
+ return(0);
+
+ /* rectify the scanned vs. selected folders */
+ n = folder_total(FOLDERS(context));
+ for(total = i = j = 0; i < n; i++)
+ if(folder_entry(i, FOLDERS(context))->scanned)
+ break;
+
+ /*
+ * Any matches at all? If not, the selected set remains the same.
+ * Note that when Narrowing, only matches in the intersection will
+ * show up as scanned. We need to reset i to 0 to erase any already
+ * selected messages which aren't in the intersection.
+ */
+ if(i < n)
+ for(i = 0; i < n; i++)
+ if((f = folder_entry(i, FOLDERS(context))) != NULL){
+ if(narrow){
+ if(f->selected){
+ f->selected = f->scanned;
+ j++;
+ }
+ }
+ else if(f->scanned)
+ f->selected = 1;
+ }
+
+ if(!(diff = (total = selected_folders(context)) - old_tot)){
+ if(narrow)
+ q_status_message4(SM_ORDER, 0, 2,
+ "%s. %s folder%s remain%s selected.",
+ j ? _("No change resulted")
+ : _("No messages in intersection"),
+ comatose(old_tot), plural(old_tot),
+ (old_tot == 1L) ? "s" : "");
+ else if(old_tot && j)
+ q_status_message(SM_ORDER, 0, 2,
+ _("No change resulted. Matching folders already selected."));
+ else
+ q_status_message1(SM_ORDER | SM_DING, 0, 2,
+ "Select failed! No %sfolders selected.",
+ old_tot ? "additional " : "");
+ }
+ else if(old_tot){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF,
+ "Select matched %d folder%s. %s %sfolder%s %sselected.",
+ (diff > 0) ? diff : old_tot + diff,
+ plural((diff > 0) ? diff : old_tot + diff),
+ comatose((diff > 0) ? total : -diff),
+ (diff > 0) ? "total " : "",
+ plural((diff > 0) ? total : -diff),
+ (diff > 0) ? "" : "UN");
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ q_status_message(SM_ORDER, 0, 2, tmp_20k_buf);
+ }
+ else
+ q_status_message2(SM_ORDER, 0, 2, "Select matched %s folder%s.",
+ comatose(diff), plural(diff));
+
+ return(1);
+}
+
+
+
+
+int
+folder_lister_select(FSTATE_S *fs, CONTEXT_S *context, int index, int is_dual_do_open)
+{
+ int rv = 0;
+ FDIR_S *fp;
+ FOLDER_S *f = folder_entry(index, FOLDERS(context));
+
+ /*--- Entering a directory? ---*/
+ if(f->isdir && !is_dual_do_open){
+ fp = next_folder_dir(context, f->name, TRUE, fs->cache_streamp);
+
+ /* Provide context in new collection header */
+ fp->desc = folder_lister_desc(context, fp);
+
+ /* Insert new directory into list */
+ free_folder_list(context);
+ fp->prev = context->dir;
+ fp->status |= CNTXT_SUBDIR;
+ context->dir = fp;
+ q_status_message2(SM_ORDER, 0, 3, "Now in %sdirectory: %s",
+ folder_total(FOLDERS(context))
+ ? "" : "EMPTY ", fp->ref);
+ rv++;
+ }
+ else
+ rv = folder_lister_parent(fs, context, index, 1);
+
+ return(rv);
+}
+
+
+int
+folder_lister_parent(FSTATE_S *fs, CONTEXT_S *context, int index, int force_parent)
+{
+ int rv = 0;
+ FDIR_S *fp;
+
+ if(!force_parent && (fp = context->dir->prev)){
+ char *s, oldir[MAILTMPLEN];
+
+ folder_select_preserve(context);
+ oldir[0] = '\0';
+ if((s = strrindex(context->dir->ref, context->dir->delim)) != NULL){
+ *s = '\0';
+ if((s = strrindex(context->dir->ref, context->dir->delim)) != NULL){
+ strncpy(oldir, s+1, sizeof(oldir)-1);
+ oldir[sizeof(oldir)-1] = '\0';
+ }
+ }
+
+ if(*oldir){
+ /* remember current directory for hiliting in new list */
+ fs->context = context;
+ if(strlen(oldir) < MAXFOLDER - 1){
+ strncpy(fs->first_folder, oldir, MAXFOLDER);
+ fs->first_folder[MAXFOLDER-1] = '\0';
+ fs->first_dir = 1;
+ }
+ }
+
+ free_fdir(&context->dir, 0);
+ fp->status |= CNTXT_NOFIND;
+
+ context->dir = fp;
+
+ if(fp->status & CNTXT_SUBDIR)
+ q_status_message1(SM_ORDER, 0, 3, _("Now in directory: %s"),
+ strsquish(tmp_20k_buf + 500, SIZEOF_20KBUF-500, fp->ref,
+ ps_global->ttyo->screen_cols - 22));
+ else
+ q_status_message(SM_ORDER, 0, 3,
+ _("Returned to collection's top directory"));
+
+ rv++;
+ }
+
+ return(rv);
+}
+
+
+char *
+folder_lister_fullname(FSTATE_S *fs, char *name)
+{
+ if(fs->context->dir->status & CNTXT_SUBDIR){
+ char tmp[2*MAILTMPLEN], tmp2[2*MAILTMPLEN], *p;
+
+ if(fs->context->dir->ref){
+ snprintf(tmp, sizeof(tmp), "%.*s%.*s",
+ sizeof(tmp)/2,
+ ((fs->relative_path || (fs->context->use & CNTXT_SAVEDFLT))
+ && (p = strstr(fs->context->context, "%s")) && !*(p+2)
+ && !strncmp(fs->context->dir->ref, fs->context->context,
+ p - fs->context->context))
+ ? fs->context->dir->ref + (p - fs->context->context)
+ : fs->context->dir->ref,
+ sizeof(tmp)/2, name);
+ tmp[sizeof(tmp)-1] = '\0';
+ }
+
+ /*
+ * If the applied name is still ambiguous (defined
+ * that way by the user (i.e., "mail/[]"), then
+ * we better fix it up...
+ */
+ if(context_isambig(tmp)
+ && !fs->relative_path
+ && !(fs->context->use & CNTXT_SAVEDFLT)){
+ /* if it's in the primary collection, the names relative */
+ if(fs->context->dir->ref){
+ if(IS_REMOTE(fs->context->context)
+ && (p = strrindex(fs->context->context, '}'))){
+ snprintf(tmp2, sizeof(tmp2), "%.*s%.*s",
+ MIN(p - fs->context->context + 1, sizeof(tmp2)/2),
+ fs->context->context,
+ sizeof(tmp2)/2, tmp);
+ tmp2[sizeof(tmp2)-1] = '\0';
+ }
+ else
+ build_path(tmp2, ps_global->ui.homedir, tmp, sizeof(tmp2));
+ }
+ else
+ (void) context_apply(tmp2, fs->context, tmp, sizeof(tmp2));
+
+ return(cpystr(tmp2));
+ }
+
+ return(cpystr(tmp));
+ }
+
+ return(cpystr(name));
+}
+
+
+/*
+ * Export a folder from pine space to user's space. It will still be a regular
+ * mail folder but it will be in the user's home directory or something like
+ * that.
+ */
+void
+folder_export(SCROLL_S *sparms)
+{
+ FOLDER_S *f;
+ MAILSTREAM *stream, *ourstream = NULL;
+ char expanded_file[MAILTMPLEN], *p,
+ tmp[MAILTMPLEN], *fname, *fullname = NULL,
+ filename[MAXPATH+1], full_filename[MAXPATH+1],
+ deefault[MAXPATH+1];
+ int open_inbox = 0, we_cancel = 0, width,
+ index = (sparms && sparms->text.handles)
+ ? sparms->text.handles->h.f.index : 0;
+ CONTEXT_S *savecntxt,
+ *cntxt = (sparms && sparms->text.handles)
+ ? sparms->text.handles->h.f.context : NULL;
+ static HISTORY_S *history = NULL;
+
+ dprint((4, "\n - folder export -\n"));
+
+ if(cntxt){
+ if(folder_total(FOLDERS(cntxt))){
+ f = folder_entry(index, FOLDERS(cntxt));
+ if((*FPROC(sparms)->fs->f.valid)(f, FPROC(sparms)->fs)){
+ savecntxt = FPROC(sparms)->fs->context; /* necessary? */
+ FPROC(sparms)->fs->context = cntxt;
+ strncpy(deefault, FLDR_NAME(f), sizeof(deefault)-1);
+ deefault[sizeof(deefault)-1] = '\0';
+ fname = folder_lister_fullname(FPROC(sparms)->fs, FLDR_NAME(f));
+ FPROC(sparms)->fs->context = savecntxt;
+
+ /*
+ * We have to allow for INBOX and nicknames in
+ * the incoming collection. Mimic what happens in
+ * do_broach_folder.
+ */
+ strncpy(expanded_file, fname, sizeof(expanded_file));
+ expanded_file[sizeof(expanded_file)-1] = '\0';
+
+ if(strucmp(fname, ps_global->inbox_name) == 0
+ || strucmp(fname, ps_global->VAR_INBOX_PATH) == 0)
+ open_inbox++;
+
+ if(!open_inbox && cntxt && context_isambig(fname)){
+ if((p=folder_is_nick(fname, FOLDERS(cntxt), 0)) != NULL){
+ strncpy(expanded_file, p, sizeof(expanded_file));
+ expanded_file[sizeof(expanded_file)-1] = '\0';
+ }
+ else if ((cntxt->use & CNTXT_INCMNG)
+ && (folder_index(fname, cntxt, FI_FOLDER) < 0)
+ && !is_absolute_path(fname)){
+ q_status_message1(SM_ORDER, 3, 4,
+ _("Can't find Incoming Folder %s."), fname);
+ return;
+ }
+ }
+
+ if(open_inbox)
+ fullname = ps_global->VAR_INBOX_PATH;
+ else{
+ /*
+ * We don't want to interpret this name in the context
+ * of the reference string, that was already done
+ * above in folder_lister_fullname(), just in the
+ * regular context. We also don't want to lose the
+ * reference string because we will still be in the
+ * subdirectory after this operation completes. So
+ * temporarily zero out the reference.
+ */
+ FDIR_S *tmpdir;
+
+ tmpdir = (cntxt ? cntxt->dir : NULL);
+ cntxt->dir = NULL;
+ fullname = context_apply(tmp, cntxt, expanded_file,
+ sizeof(tmp));
+ cntxt->dir = tmpdir;
+ }
+
+ width = MAX(20,
+ ps_global->ttyo ? ps_global->ttyo->screen_cols : 80);
+ stream = sp_stream_get(fullname, SP_MATCH | SP_RO_OK);
+ if(!stream && fullname){
+ /*
+ * Just using filename and full_filename as convenient
+ * temporary buffers here.
+ */
+ snprintf(filename, sizeof(filename), "Opening \"%s\"",
+ short_str(fullname, full_filename, sizeof(full_filename),
+ MAX(10, width-17),
+ MidDots));
+ filename[sizeof(filename)-1] = '\0';
+ we_cancel = busy_cue(filename, NULL, 0);
+ stream = pine_mail_open(NULL, fullname,
+ OP_READONLY|SP_USEPOOL|SP_TEMPUSE,
+ NULL);
+ if(we_cancel)
+ cancel_busy_cue(0);
+
+ ourstream = stream;
+ }
+
+ /*
+ * We have a stream for the folder we want to
+ * export.
+ */
+ if(stream && stream->nmsgs > 0L){
+ int r = 1;
+ static ESCKEY_S eopts[] = {
+ {ctrl('T'), 10, "^T", N_("To Files")},
+ {-1, 0, NULL, NULL},
+ {-1, 0, NULL, NULL}};
+
+ if(F_ON(F_ENABLE_TAB_COMPLETE,ps_global)){
+ eopts[r].ch = ctrl('I');
+ eopts[r].rval = 11;
+ eopts[r].name = "TAB";
+ /* TRANSLATORS: Complete is a verb, complete the name of a folder */
+ eopts[r].label = _("Complete");
+ }
+
+ eopts[++r].ch = -1;
+
+ filename[0] = '\0';
+ full_filename[0] = '\0';
+
+ r = get_export_filename(ps_global, filename, deefault,
+ full_filename,
+ sizeof(filename)-20, fname, NULL,
+ eopts, NULL,
+ -FOOTER_ROWS(ps_global),
+ GE_IS_EXPORT | GE_NO_APPEND, &history);
+ if(r < 0){
+ switch(r){
+ default:
+ case -1:
+ cmd_cancelled("Export folder");
+ break;
+
+ case -2:
+ q_status_message1(SM_ORDER, 0, 2,
+ _("Can't export to file outside of %s"),
+ ps_global->VAR_OPER_DIR);
+ break;
+ }
+ }
+ else{
+ int ok;
+ char *ff;
+
+ ps_global->mm_log_error = 0;
+
+ /*
+ * Do the creation in standard unix format, so it
+ * is readable by all.
+ */
+ rplstr(full_filename, sizeof(full_filename), 0, "#driver.unix/");
+ ok = pine_mail_create(NULL, full_filename) != 0L;
+ /*
+ * ff points to the original filename, without the
+ * driver prefix. Only mail_create knows how to
+ * handle driver prefixes.
+ */
+ ff = full_filename + strlen("#driver.unix/");
+
+ if(!ok){
+ if(!ps_global->mm_log_error)
+ q_status_message(SM_ORDER | SM_DING, 3, 3,
+ _("Error creating file"));
+ }
+ else{
+ long l, snmsgs;
+ MSGNO_S *tmpmap = NULL;
+
+ snmsgs = stream->nmsgs;
+ mn_init(&tmpmap, snmsgs);
+ for(l = 1L; l <= snmsgs; l++)
+ if(l == 1L)
+ mn_set_cur(tmpmap, l);
+ else
+ mn_add_cur(tmpmap, l);
+
+ blank_keymenu(ps_global->ttyo->screen_rows-2, 0);
+ we_cancel = busy_cue(_("Copying folder"), NULL, 0);
+ l = save(ps_global, stream, NULL, ff, tmpmap, 0);
+ if(we_cancel)
+ cancel_busy_cue(0);
+
+ mn_give(&tmpmap);
+
+ if(l == snmsgs)
+ q_status_message2(SM_ORDER, 0, 3,
+ "Folder %s exported to %s",
+ fname, filename);
+ else{
+ q_status_message1(SM_ORDER | SM_DING, 3, 3,
+ _("Error exporting to %s"),
+ filename);
+ dprint((2,
+ "Error exporting to %s: expected %ld msgs, saved %ld\n",
+ filename, snmsgs, l));
+ }
+ }
+ }
+ }
+ else if(stream)
+ q_status_message1(SM_ORDER|SM_DING, 3, 3,
+ _("No messages in %s to export"), fname);
+ else
+ q_status_message(SM_ORDER|SM_DING, 3, 3,
+ _("Can't open folder for exporting"));
+
+ if(fname)
+ fs_give((void **) &fname);
+
+ if(ourstream)
+ pine_mail_close(ourstream);
+ }
+ }
+ }
+ else
+ q_status_message(SM_ORDER | SM_DING, 3, 3, _("Empty folder list!"));
+}
+
+
+/*
+ * Import a folder from user's space back to pine space.
+ * We're just importing a regular mail folder, and saving all the messages
+ * to another folder. It may seem more magical to the user but it isn't.
+ * The import folder is a local folder, the new one can be remote or whatever.
+ * Args sparms
+ * add_folder -- return new folder name here
+ * len -- length of add_folder
+ *
+ * Returns 1 if we may have to redraw screen, 0 otherwise
+ */
+int
+folder_import(SCROLL_S *sparms, char *add_folder, size_t len)
+{
+ MAILSTREAM *istream = NULL;
+ int r = 1, rv = 0;
+ char filename[MAXPATH+1], full_filename[MAXPATH+1];
+ static HISTORY_S *history = NULL;
+ static ESCKEY_S eopts[] = {
+ {ctrl('T'), 10, "^T", N_("To Files")},
+ {-1, 0, NULL, NULL},
+ {-1, 0, NULL, NULL}};
+
+ if(F_ON(F_ENABLE_TAB_COMPLETE,ps_global)){
+ eopts[r].ch = ctrl('I');
+ eopts[r].rval = 11;
+ eopts[r].name = "TAB";
+ eopts[r].label = N_("Complete");
+ }
+
+ eopts[++r].ch = -1;
+
+ filename[0] = '\0';
+ full_filename[0] = '\0';
+
+ /* get a folder to import */
+ r = get_export_filename(ps_global, filename, NULL, full_filename,
+ sizeof(filename)-20, "messages", "IMPORT",
+ eopts, NULL,
+ -FOOTER_ROWS(ps_global), GE_IS_IMPORT, &history);
+ if(r < 0){
+ switch(r){
+ default:
+ case -1:
+ cmd_cancelled("Import folder");
+ break;
+
+ case -2:
+ q_status_message1(SM_ORDER, 0, 2,
+ _("Can't import file outside of %s"),
+ ps_global->VAR_OPER_DIR);
+ break;
+ }
+ }
+ else{
+ ps_global->mm_log_error = 0;
+ if(full_filename && full_filename[0])
+ istream = pine_mail_open(NULL, full_filename,
+ OP_READONLY | SP_TEMPUSE, NULL);
+
+ if(istream && istream->nmsgs > 0L){
+ long l;
+ int we_cancel = 0;
+ char newfolder[MAILTMPLEN], nmsgs[32];
+ MSGNO_S *tmpmap = NULL;
+ CONTEXT_S *cntxt, *ourcntxt;
+
+ cntxt = (sparms && sparms->text.handles)
+ ? sparms->text.handles->h.f.context : NULL;
+ ourcntxt = cntxt;
+ newfolder[0] = '\0';
+ snprintf(nmsgs, sizeof(nmsgs), "%s msgs ", comatose(istream->nmsgs));
+ nmsgs[sizeof(nmsgs)-1] = '\0';
+
+ /*
+ * Select a folder to save the messages to.
+ */
+ if(save_prompt(ps_global, &cntxt, newfolder, sizeof(newfolder),
+ nmsgs, NULL, 0L, NULL, NULL, NULL)){
+
+ if((cntxt == ourcntxt) && newfolder[0]){
+ rv = 1;
+ strncpy(add_folder, newfolder, len-1);
+ add_folder[len-1] = '\0';
+ free_folder_list(cntxt);
+ }
+
+ mn_init(&tmpmap, istream->nmsgs);
+ for(l = 1; l <= istream->nmsgs; l++)
+ if(l == 1L)
+ mn_set_cur(tmpmap, l);
+ else
+ mn_add_cur(tmpmap, l);
+
+ blank_keymenu(ps_global->ttyo->screen_rows-2, 0);
+ we_cancel = busy_cue("Importing messages", NULL, 0);
+ l = save(ps_global, istream, cntxt, newfolder, tmpmap,
+ SV_INBOXWOCNTXT);
+ if(we_cancel)
+ cancel_busy_cue(0);
+
+ mn_give(&tmpmap);
+
+ if(l == istream->nmsgs)
+ q_status_message2(SM_ORDER, 0, 3,
+ "Folder %s imported to %s",
+ full_filename, newfolder);
+ else
+ q_status_message1(SM_ORDER | SM_DING, 3, 3,
+ _("Error importing to %s"),
+ newfolder);
+ }
+ }
+ else if(istream)
+ q_status_message1(SM_ORDER | SM_DING, 3, 3,
+ _("No messages in file %s"),
+ full_filename);
+ else if(!ps_global->mm_log_error)
+ q_status_message1(SM_ORDER | SM_DING, 3, 3,
+ _("Can't open file %s for import"), full_filename);
+ }
+
+ if(istream)
+ pine_mail_close(istream);
+
+ return(rv);
+}
+
+
+int
+folder_select_toggle(CONTEXT_S *context, int index, int (*func) (CONTEXT_S *, int))
+{
+ FOLDER_S *f;
+
+ if((f = folder_entry(index, FOLDERS(context))) != NULL){
+ f->selected = !f->selected;
+ return((*func)(context, index));
+ }
+ return 1;
+}
+
+
+/*
+ * Find the next '}' that isn't part of a "${"
+ * that appear for environment variables. We need
+ * this for configuration, because we want to edit
+ * the original pinerc entry, and not the digested
+ * value.
+ */
+char *
+end_bracket_no_nest(char *str)
+{
+ char *p;
+
+ for(p = str; *p; p++){
+ if(*p == '$' && *(p+1) == '{'){
+ while(*p && *p != '}')
+ p++;
+ if(!*p)
+ return NULL;
+ }
+ else if(*p == '}')
+ return p;
+ }
+ /* if we get here then there was no trailing '}' */
+ return NULL;
+}
+
+
+/*----------------------------------------------------------------------
+ Create a new folder
+
+ Args: context -- context we're adding in
+ which -- which config file to operate on
+ varnum -- which varnum to operate on
+ add_folder -- return new folder here
+ add_folderlen -- length of add_folder
+ possible_stream -- possible stream for re-use
+ def -- default value to start out with for add_folder
+ (for now, only for inbox-path editing)
+
+ Result: returns nonzero on successful add, 0 otherwise
+ ----*/
+int
+add_new_folder(CONTEXT_S *context, EditWhich which, int varnum, char *add_folder,
+ size_t add_folderlen, MAILSTREAM *possible_stream, char *def)
+{
+ char tmp[MAX(MAXFOLDER,6*MAX_SCREEN_COLS)+1], nickname[32],
+ *p = NULL, *return_val = NULL, buf[MAILTMPLEN],
+ buf2[MAILTMPLEN], def_in_prompt[MAILTMPLEN];
+ HelpType help;
+ PINERC_S *prc = NULL;
+ int i, rc, offset, exists, cnt = 0, isdir = 0;
+ int maildrop = 0, flags = 0, inbox = 0, require_a_subfolder = 0;
+ char *maildropfolder = NULL, *maildroplongname = NULL;
+ char *default_mail_drop_host = NULL,
+ *default_mail_drop_folder = NULL,
+ *default_dstn_host = NULL,
+ *default_dstn_folder = NULL,
+ *copydef = NULL,
+ *dstnmbox = NULL;
+ char mdmbox[MAILTMPLEN], ctmp[MAILTMPLEN];
+ MAILSTREAM *create_stream = NULL;
+ FOLDER_S *f;
+ static ESCKEY_S add_key[] = {{ctrl('X'),12,"^X", NULL},
+ {-1, 0, NULL, NULL}};
+
+ dprint((4, "\n - add_new_folder - \n"));
+
+ add_folder[0] = '\0';
+ nickname[0] = '\0';
+ inbox = (varnum == V_INBOX_PATH);
+
+ if(inbox || context->use & CNTXT_INCMNG){
+ char inbox_host[MAXPATH], *beg, *end = NULL;
+ int readonly = 0;
+ static ESCKEY_S host_key[4];
+
+ if(ps_global->restricted)
+ readonly = 1;
+ else{
+ switch(which){
+ case Main:
+ prc = ps_global->prc;
+ break;
+ case Post:
+ prc = ps_global->post_prc;
+ break;
+ case None:
+ break;
+ }
+
+ readonly = prc ? prc->readonly : 1;
+ }
+
+ if(prc && prc->quit_to_edit){
+ quit_to_edit_msg(prc);
+ return(FALSE);
+ }
+
+ if(readonly){
+ q_status_message(SM_ORDER,3,5,
+ _("Cancelled: config file not editable"));
+ return(FALSE);
+ }
+
+ /*
+ * When adding an Incoming folder we can't supply the inbox host
+ * as a default, because then the user has no way to type just
+ * a plain RETURN to mean "no host, I want a local folder".
+ * So we supply it as a ^X command instead. We could supply it
+ * as the initial value of the string...
+ *
+ * When editing inbox-path we will supply the default value as an
+ * initial value of the string which can be edited. They can edit it
+ * or erase it entirely to mean no host.
+ */
+
+ if(inbox && def){
+
+ copydef = cpystr(def);
+ (void) removing_double_quotes(copydef);
+
+ if(check_for_move_mbox(copydef, mdmbox, sizeof(mdmbox), &dstnmbox)){
+
+ /*
+ * Current inbox is using a mail drop. Get the default
+ * host value for the maildrop.
+ */
+
+ if(mdmbox
+ && (mdmbox[0] == '{'
+ || (mdmbox[0] == '*' && mdmbox[1] == '{'))
+ && (end = end_bracket_no_nest(mdmbox+1))
+ && end-mdmbox < add_folderlen){
+ *end = '\0';
+ if(mdmbox[0] == '{')
+ default_mail_drop_host = cpystr(mdmbox+1);
+ else
+ default_mail_drop_host = cpystr(mdmbox+2);
+ }
+
+ if(!default_mail_drop_host)
+ default_mail_drop_folder = cpystr(mdmbox);
+ else if(end && *(end+1))
+ default_mail_drop_folder = cpystr(end+1);
+
+ end = NULL;
+ if(dstnmbox
+ && (*dstnmbox == '{'
+ || (*dstnmbox == '*' && *++dstnmbox == '{'))
+ && (end = end_bracket_no_nest(dstnmbox+1))
+ && end-dstnmbox < add_folderlen){
+ *end = '\0';
+ default_dstn_host = cpystr(dstnmbox+1);
+ }
+
+ if(!default_dstn_host)
+ default_dstn_folder = cpystr(dstnmbox);
+ else if(end && *(end+1))
+ default_dstn_folder = cpystr(end+1);
+
+ maildrop++;
+ }
+ else{
+ end = NULL;
+ dstnmbox = copydef;
+ if(dstnmbox
+ && (*dstnmbox == '{'
+ || (*dstnmbox == '*' && *++dstnmbox == '{'))
+ && (end = end_bracket_no_nest(dstnmbox+1))
+ && end-dstnmbox < add_folderlen){
+ *end = '\0';
+ default_dstn_host = cpystr(dstnmbox+1);
+ }
+
+ if(!default_dstn_host)
+ default_dstn_folder = cpystr(dstnmbox);
+ else if(end && *(end+1))
+ default_dstn_folder = cpystr(end+1);
+ }
+
+ if(copydef)
+ fs_give((void **) &copydef);
+ }
+
+get_folder_name:
+
+ i = 0;
+ host_key[i].ch = 0;
+ host_key[i].rval = 0;
+ host_key[i].name = "";
+ host_key[i++].label = "";
+
+ inbox_host[0] = '\0';
+ if(!inbox && (beg = ps_global->VAR_INBOX_PATH)
+ && (*beg == '{' || (*beg == '*' && *++beg == '{'))
+ && (end = strindex(ps_global->VAR_INBOX_PATH, '}'))){
+ strncpy(inbox_host, beg+1, end - beg);
+ inbox_host[end - beg - 1] = '\0';
+ host_key[i].ch = ctrl('X');
+ host_key[i].rval = 12;
+ host_key[i].name = "^X";
+ host_key[i++].label = N_("Use Inbox Host");
+ }
+ else{
+ host_key[i].ch = 0;
+ host_key[i].rval = 0;
+ host_key[i].name = "";
+ host_key[i++].label = "";
+ }
+
+ if(!maildrop && !maildropfolder){
+ host_key[i].ch = ctrl('W');
+ host_key[i].rval = 13;
+ host_key[i].name = "^W";
+ /* TRANSLATORS: a mail drop is a place where mail is copied to so you
+ can read it. */
+ host_key[i++].label = N_("Use a Mail Drop");
+ }
+ else if(maildrop){
+ host_key[i].ch = ctrl('W');
+ host_key[i].rval = 13;
+ host_key[i].name = "^W";
+ host_key[i++].label = N_("Do Not use a Mail Drop");
+ }
+
+ host_key[i].ch = -1;
+ host_key[i].rval = 0;
+ host_key[i].name = NULL;
+ host_key[i].label = NULL;
+
+ if(maildrop)
+ snprintf(tmp, sizeof(tmp), _("Name of Mail Drop server : "));
+ else if(maildropfolder)
+ snprintf(tmp, sizeof(tmp), _("Name of server to contain destination folder : "));
+ else if(inbox)
+ snprintf(tmp, sizeof(tmp), _("Name of Inbox server : "));
+ else
+ snprintf(tmp, sizeof(tmp), _("Name of server to contain added folder : "));
+
+ tmp[sizeof(tmp)-1] = '\0';
+
+ help = NO_HELP;
+
+ /* set up defaults */
+ if(inbox && def){
+ flags = OE_APPEND_CURRENT;
+ if(maildrop && default_mail_drop_host){
+ strncpy(add_folder, default_mail_drop_host, add_folderlen);
+ add_folder[add_folderlen-1] = '\0';
+ }
+ else if(!maildrop && default_dstn_host){
+ strncpy(add_folder, default_dstn_host, add_folderlen);
+ add_folder[add_folderlen-1] = '\0';
+ }
+ else
+ add_folder[0] = '\0';
+ }
+ else{
+ flags = 0;
+ add_folder[0] = '\0';
+ }
+
+ while(1){
+ rc = optionally_enter(add_folder, -FOOTER_ROWS(ps_global), 0,
+ add_folderlen, tmp, host_key, help, &flags);
+ removing_leading_and_trailing_white_space(add_folder);
+
+ /*
+ * User went for the whole enchilada and entered a maildrop
+ * completely without going through the steps.
+ * Split it up as if they did and then skip over
+ * some of the code.
+ */
+ if(check_for_move_mbox(add_folder, mdmbox, sizeof(mdmbox),
+ &dstnmbox)){
+ maildrop = 1;
+ if(maildropfolder)
+ fs_give((void **) &maildropfolder);
+
+ maildropfolder = cpystr(mdmbox);
+
+ strncpy(add_folder, dstnmbox, add_folderlen);
+ add_folder[add_folderlen-1] = '\0';
+ offset = 0;
+ goto skip_over_folder_input;
+ }
+
+ /*
+ * Now check to see if they entered a whole c-client
+ * spec that isn't a mail drop.
+ */
+ if(add_folder[0] == '{'
+ && add_folder[1] != '\0'
+ && (p = srchstr(add_folder, "}"))
+ && *(p+1) != '\0'){
+ offset = p+1 - add_folder;
+ goto skip_over_folder_input;
+ }
+
+ /*
+ * And check to see if they entered "INBOX".
+ */
+ if(!strucmp(add_folder, ps_global->inbox_name)){
+ offset = 0;
+ goto skip_over_folder_input;
+ }
+
+ /* remove surrounding braces */
+ if(add_folder[0] == '{' && add_folder[1] != '\0'){
+ char *q;
+
+ q = add_folder + strlen(add_folder) - 1;
+ if(*q == '}'){
+ *q = '\0';
+ for(q = add_folder; *q; q++)
+ *q = *(q+1);
+ }
+ }
+
+ if(rc == 3){
+ if(maildropfolder && inbox)
+ helper(h_inbox_add_maildrop_destn,
+ _("HELP FOR DESTINATION SERVER "), HLPD_SIMPLE);
+ else if(maildropfolder && !inbox)
+ helper(h_incoming_add_maildrop_destn,
+ _("HELP FOR DESTINATION SERVER "), HLPD_SIMPLE);
+ else if(maildrop && inbox)
+ helper(h_inbox_add_maildrop, _("HELP FOR MAILDROP NAME "),
+ HLPD_SIMPLE);
+ else if(maildrop && !inbox)
+ helper(h_incoming_add_maildrop, _("HELP FOR MAILDROP NAME "),
+ HLPD_SIMPLE);
+ else if(inbox)
+ helper(h_incoming_add_inbox, _("HELP FOR INBOX SERVER "),
+ HLPD_SIMPLE);
+ else
+ helper(h_incoming_add_folder_host, _("HELP FOR SERVER NAME "),
+ HLPD_SIMPLE);
+
+ ps_global->mangled_screen = 1;
+ }
+ else if(rc == 12){
+ strncpy(add_folder, inbox_host, add_folderlen);
+ flags |= OE_APPEND_CURRENT;
+ }
+ else if(rc == 13){
+ if(maildrop){
+ maildrop = 0;
+ if(maildropfolder)
+ fs_give((void **) &maildropfolder);
+ }
+ else{
+ maildrop++;
+ if(inbox && def){
+ default_mail_drop_host = default_dstn_host;
+ default_dstn_host = NULL;
+ default_mail_drop_folder = default_dstn_folder;
+ default_dstn_folder = NULL;
+ }
+ }
+
+ goto get_folder_name;
+ }
+ else if(rc == 1){
+ q_status_message(SM_ORDER,0,2,
+ inbox ? _("INBOX change cancelled")
+ : _("Addition of new folder cancelled"));
+ if(maildropfolder)
+ fs_give((void **) &maildropfolder);
+
+ if(inbox && def){
+ if(default_mail_drop_host)
+ fs_give((void **) &default_mail_drop_host);
+
+ if(default_mail_drop_folder)
+ fs_give((void **) &default_mail_drop_folder);
+
+ if(default_dstn_host)
+ fs_give((void **) &default_dstn_host);
+
+ if(default_dstn_folder)
+ fs_give((void **) &default_dstn_folder);
+ }
+
+ return(FALSE);
+ }
+ else if(rc == 0)
+ break;
+ }
+ }
+
+ /* set up default folder, if any */
+ def_in_prompt[0] = '\0';
+ if(inbox && def){
+ if(maildrop && default_mail_drop_folder){
+ strncpy(def_in_prompt, default_mail_drop_folder,
+ sizeof(def_in_prompt));
+ def_in_prompt[sizeof(def_in_prompt)-1] = '\0';
+ }
+ else if(!maildrop && default_dstn_folder){
+ strncpy(def_in_prompt, default_dstn_folder,
+ sizeof(def_in_prompt));
+ def_in_prompt[sizeof(def_in_prompt)-1] = '\0';
+ }
+ }
+
+ if((offset = strlen(add_folder)) != 0){ /* must be host for incoming */
+ int i;
+ if(maildrop)
+ snprintf(tmp, sizeof(tmp),
+ "Maildrop folder on \"%s\" to copy mail from%s%s%s : ",
+ short_str(add_folder, buf, sizeof(buf), 15, EndDots),
+ def_in_prompt[0] ? " [" : "",
+ short_str(def_in_prompt, buf2, sizeof(buf2), 20, MidDots),
+ def_in_prompt[0] ? "]" : "");
+ else if(maildropfolder)
+ snprintf(tmp, sizeof(tmp),
+ "Folder on \"%s\" to copy mail to%s%s%s : ",
+ short_str(add_folder, buf, sizeof(buf), 20, EndDots),
+ def_in_prompt[0] ? " [" : "",
+ short_str(def_in_prompt, buf2, sizeof(buf2), 20, MidDots),
+ def_in_prompt[0] ? "]" : "");
+ else if(inbox)
+ snprintf(tmp, sizeof(tmp),
+ "Folder on \"%s\" to use for INBOX%s%s%s : ",
+ short_str(add_folder, buf, sizeof(buf), 20, EndDots),
+ def_in_prompt[0] ? " [" : "",
+ short_str(def_in_prompt, buf2, sizeof(buf2), 20, MidDots),
+ def_in_prompt[0] ? "]" : "");
+ else
+ snprintf(tmp, sizeof(tmp),
+ "Folder on \"%s\" to add%s%s%s : ",
+ short_str(add_folder, buf, sizeof(buf), 25, EndDots),
+ def_in_prompt[0] ? " [" : "",
+ short_str(def_in_prompt, buf2, sizeof(buf2), 20, MidDots),
+ def_in_prompt[0] ? "]" : "");
+
+ tmp[sizeof(tmp)-1] = '\0';
+
+ for(i = offset;i >= 0; i--)
+ add_folder[i+1] = add_folder[i];
+
+ add_folder[0] = '{';
+ add_folder[++offset] = '}';
+ add_folder[++offset] = '\0'; /* +2, total */
+ }
+ else{
+ if(maildrop)
+ snprintf(tmp, sizeof(tmp),
+ "Folder to copy mail from%s%s%s : ",
+ def_in_prompt[0] ? " [" : "",
+ short_str(def_in_prompt, buf2, sizeof(buf2), 20, MidDots),
+ def_in_prompt[0] ? "]" : "");
+ else if(maildropfolder)
+ snprintf(tmp, sizeof(tmp),
+ "Folder to copy mail to%s%s%s : ",
+ def_in_prompt[0] ? " [" : "",
+ short_str(def_in_prompt, buf2, sizeof(buf2), 20, MidDots),
+ def_in_prompt[0] ? "]" : "");
+ else if(inbox)
+ snprintf(tmp, sizeof(tmp),
+ "Folder name to use for INBOX%s%s%s : ",
+ def_in_prompt[0] ? " [" : "",
+ short_str(def_in_prompt, buf2, sizeof(buf2), 20, MidDots),
+ def_in_prompt[0] ? "]" : "");
+ else
+ snprintf(tmp, sizeof(tmp),
+ "Folder name to add%s%s%s : ",
+ def_in_prompt[0] ? " [" : "",
+ short_str(def_in_prompt, buf2, sizeof(buf2), 20, MidDots),
+ def_in_prompt[0] ? "]" : "");
+
+ tmp[sizeof(tmp)-1] = '\0';
+ }
+
+ help = NO_HELP;
+ while(1){
+
+ p = NULL;
+ if(isdir){
+ add_key[0].label = N_("Create Folder");
+ if(tmp[0] == 'F')
+ rplstr(tmp, sizeof(tmp), 6, N_("Directory"));
+ }
+ else{
+ add_key[0].label = N_("Create Directory");
+ if(tmp[0] == 'D')
+ rplstr(tmp, sizeof(tmp), 9, N_("Folder"));
+ }
+
+ flags = OE_APPEND_CURRENT;
+ rc = optionally_enter(&add_folder[offset], -FOOTER_ROWS(ps_global), 0,
+ add_folderlen - offset, tmp,
+ ((context->dir->delim) && !maildrop)
+ ? add_key : NULL,
+ help, &flags);
+ /* use default */
+ if(rc == 0 && def_in_prompt[0] && !add_folder[offset]){
+ strncpy(&add_folder[offset], def_in_prompt, add_folderlen-offset);
+ add_folder[add_folderlen-1] = '\0';
+ }
+
+ removing_leading_and_trailing_white_space(&add_folder[offset]);
+
+ if(rc == 0 && !(inbox || context->use & CNTXT_INCMNG)
+ && check_for_move_mbox(add_folder, NULL, 0L, NULL)){
+ q_status_message(SM_ORDER, 6, 6,
+ _("#move folders may only be the INBOX or in the Incoming Collection"));
+ display_message(NO_OP_COMMAND);
+ continue;
+ }
+
+ if(rc == 0 && add_folder[offset]){
+ if(F_OFF(F_ENABLE_DOT_FOLDERS,ps_global)
+ && add_folder[offset] == '.'){
+ if(cnt++ <= 0)
+ q_status_message(SM_ORDER,3,3,
+ _("Folder name can't begin with dot"));
+ else
+ q_status_message1(SM_ORDER,3,3,
+ _("Config feature \"%s\" enables names beginning with dot"),
+ pretty_feature_name(feature_list_name(F_ENABLE_DOT_FOLDERS), -1));
+
+ display_message(NO_OP_COMMAND);
+ continue;
+ }
+
+ /* if last char is delim, blast from new folder */
+ for(p = &add_folder[offset]; *p && *(p + 1); p++)
+ ; /* find last char in folder */
+
+ if(isdir){
+ if(*p && *p != context->dir->delim){
+ *++p = context->dir->delim;
+ *(p+1) = '\0';
+ }
+
+ if(F_ON(F_QUELL_EMPTY_DIRS, ps_global))
+ require_a_subfolder++;
+ }
+ else if(*p == context->dir->delim){
+ q_status_message(SM_ORDER|SM_DING, 3, 3,
+ _("Can't have trailing directory delimiters!"));
+ display_message('X');
+ continue;
+ }
+
+ break;
+ }
+
+ if(rc == 12){
+ isdir = !isdir; /* toggle directory creation */
+ }
+ else if(rc == 3){
+ helper(h_incoming_add_folder_name, _("HELP FOR FOLDER NAME "),
+ HLPD_SIMPLE);
+ }
+ else if(rc == 1 || add_folder[0] == '\0') {
+ q_status_message(SM_ORDER,0,2,
+ inbox ? _("INBOX change cancelled")
+ : _("Addition of new folder cancelled"));
+ if(maildropfolder)
+ fs_give((void **) &maildropfolder);
+
+ if(inbox && def){
+ if(default_mail_drop_host)
+ fs_give((void **) &default_mail_drop_host);
+
+ if(default_mail_drop_folder)
+ fs_give((void **) &default_mail_drop_folder);
+
+ if(default_dstn_host)
+ fs_give((void **) &default_dstn_host);
+
+ if(default_dstn_folder)
+ fs_give((void **) &default_dstn_folder);
+ }
+
+ return(FALSE);
+ }
+ }
+
+ if(maildrop && !maildropfolder){
+ maildropfolder = cpystr(add_folder);
+ maildrop = 0;
+ goto get_folder_name;
+ }
+
+skip_over_folder_input:
+
+ if(require_a_subfolder){
+ /* add subfolder name to directory name */
+ offset = strlen(add_folder);
+ tmp[0] = '\0';
+
+ if(offset > 0){ /* it had better be */
+ char save_delim;
+
+ save_delim = add_folder[offset-1];
+ add_folder[offset-1] = '\0';
+
+ snprintf(tmp, sizeof(tmp),
+ "Name of subfolder to add in \"%s\" : ",
+ short_str(add_folder, buf, sizeof(buf), 15, FrontDots));
+
+ tmp[sizeof(tmp)-1] = '\0';
+ add_folder[offset-1] = save_delim;
+ }
+
+ while(1){
+ flags = OE_APPEND_CURRENT;
+ rc = optionally_enter(&add_folder[offset], -FOOTER_ROWS(ps_global), 0,
+ add_folderlen - offset, tmp,
+ NULL, NO_HELP, &flags);
+
+ removing_leading_and_trailing_white_space(&add_folder[offset]);
+
+ /* use default */
+ if(rc == 0 && !add_folder[offset]){
+ q_status_message(SM_ORDER, 4, 4,
+ _("A subfolder name is required, there is no default subfolder name"));
+ continue;
+ }
+
+ if(rc == 0 && add_folder[offset]){
+ break;
+ }
+
+ if(rc == 3){
+ helper(h_emptydir_subfolder_name, _("HELP FOR SUBFOLDER NAME "),
+ HLPD_SIMPLE);
+ }
+ else if(rc == 1 || add_folder[0] == '\0') {
+ q_status_message(SM_ORDER,0,2, _("Addition of new folder cancelled"));
+ return(FALSE);
+ }
+ }
+
+ /* the directory is implicit now */
+ isdir = 0;
+ }
+
+ if(context == ps_global->context_list
+ && !(context->dir && context->dir->ref)
+ && !strucmp(ps_global->inbox_name, add_folder)){
+ q_status_message1(SM_ORDER,3,3,
+ _("Cannot add folder %s in current context"),
+ add_folder);
+ return(FALSE);
+ }
+
+ create_stream = sp_stream_get(context_apply(ctmp, context, add_folder,
+ sizeof(ctmp)),
+ SP_SAME);
+
+ if(!create_stream && possible_stream)
+ create_stream = context_same_stream(context, add_folder, possible_stream);
+
+ help = NO_HELP;
+ if(!inbox && context->use & CNTXT_INCMNG){
+ snprintf(tmp, sizeof(tmp), _("Nickname for folder \"%s\" : "), &add_folder[offset]);
+ tmp[sizeof(tmp)-1] = '\0';
+ while(1){
+ int flags = OE_APPEND_CURRENT;
+
+ rc = optionally_enter(nickname, -FOOTER_ROWS(ps_global), 0,
+ sizeof(nickname), tmp, NULL, help, &flags);
+ removing_leading_and_trailing_white_space(nickname);
+ if(rc == 0){
+ if(strucmp(ps_global->inbox_name, nickname))
+ break;
+ else{
+ q_status_message1(SM_ORDER | SM_DING, 3, 3,
+ _("Nickname cannot be \"%s\""), nickname);
+ }
+ }
+
+ if(rc == 3){
+ help = help == NO_HELP
+ ? h_incoming_add_folder_nickname : NO_HELP;
+ }
+ else if(rc == 1 || (rc != 3 && !*nickname)){
+ q_status_message(SM_ORDER,0,2,
+ inbox ? _("INBOX change cancelled")
+ : _("Addition of new folder cancelled"));
+ if(maildropfolder)
+ fs_give((void **) &maildropfolder);
+
+ if(inbox && def){
+ if(default_mail_drop_host)
+ fs_give((void **) &default_mail_drop_host);
+
+ if(default_mail_drop_folder)
+ fs_give((void **) &default_mail_drop_folder);
+
+ if(default_dstn_host)
+ fs_give((void **) &default_dstn_host);
+
+ if(default_dstn_folder)
+ fs_give((void **) &default_dstn_folder);
+ }
+
+ return(FALSE);
+ }
+ }
+
+ /*
+ * Already exist? First, make sure this name won't collide with
+ * anything else in the list. Next, quickly test to see if it
+ * the actual mailbox exists so we know any errors from
+ * context_create() are really bad...
+ */
+ for(offset = 0; offset < folder_total(FOLDERS(context)); offset++){
+ f = folder_entry(offset, FOLDERS(context));
+ if(!strucmp(FLDR_NAME(f), nickname[0] ? nickname : add_folder)){
+ q_status_message1(SM_ORDER | SM_DING, 0, 3,
+ _("Incoming folder \"%s\" already exists"),
+ nickname[0] ? nickname : add_folder);
+ if(maildropfolder)
+ fs_give((void **) &maildropfolder);
+
+ if(inbox && def){
+ if(default_mail_drop_host)
+ fs_give((void **) &default_mail_drop_host);
+
+ if(default_mail_drop_folder)
+ fs_give((void **) &default_mail_drop_folder);
+
+ if(default_dstn_host)
+ fs_give((void **) &default_dstn_host);
+
+ if(default_dstn_folder)
+ fs_give((void **) &default_dstn_folder);
+ }
+
+ return(FALSE);
+ }
+ }
+
+ ps_global->c_client_error[0] = '\0';
+ exists = folder_exists(context, add_folder);
+ if(exists == FEX_ERROR){
+ if(ps_global->c_client_error[0] != '\0')
+ q_status_message1(SM_ORDER, 3, 3, "%s",
+ ps_global->c_client_error);
+ else
+ q_status_message1(SM_ORDER, 3, 3, _("Error checking for %s"), add_folder);
+ }
+ }
+ else if(!inbox)
+ exists = FEX_NOENT;
+ else{
+ exists = FEX_ISFILE;
+ /*
+ * If inbox is a maildropfolder, try to create the destination
+ * folder. But it shouldn't cause a fatal error.
+ */
+ if(maildropfolder && (folder_exists(NULL, add_folder) == FEX_NOENT))
+ context_create(NULL, NULL, add_folder);
+ }
+
+ if(exists == FEX_ERROR
+ || (exists == FEX_NOENT
+ && !context_create(context, create_stream, add_folder)
+ && !((context->use & CNTXT_INCMNG)
+ && !context_isambig(add_folder)))){
+ if(maildropfolder)
+ fs_give((void **) &maildropfolder);
+
+ if(inbox && def){
+ if(default_mail_drop_host)
+ fs_give((void **) &default_mail_drop_host);
+
+ if(default_mail_drop_folder)
+ fs_give((void **) &default_mail_drop_folder);
+
+ if(default_dstn_host)
+ fs_give((void **) &default_dstn_host);
+
+ if(default_dstn_folder)
+ fs_give((void **) &default_dstn_folder);
+ }
+
+ return(FALSE); /* c-client should've reported error */
+ }
+
+ if(isdir && p) /* whack off trailing delim */
+ *p = '\0';
+
+ if(inbox || context->use & CNTXT_INCMNG){
+ char **apval;
+ char ***alval;
+
+ if(inbox){
+ apval = APVAL(&ps_global->vars[varnum], which);
+ if(apval && *apval)
+ fs_give((void **) apval);
+ }
+ else{
+ alval = ALVAL(&ps_global->vars[varnum], which);
+ if(!*alval){
+ offset = 0;
+ *alval = (char **) fs_get(2*sizeof(char *));
+ }
+ else{
+ for(offset=0; (*alval)[offset]; offset++)
+ ;
+
+ fs_resize((void **) alval, (offset + 2) * sizeof(char *));
+ }
+ }
+
+ /*
+ * If we're using a Mail Drop we have to assemble the correct
+ * c-client string to do that. Mail drop syntax looks like
+ *
+ * #move <DELIM> <FromMailbox> <DELIM> <ToMailbox>
+ *
+ * DELIM is any character which does not appear in either of
+ * the mailbox names.
+ *
+ * And then the nickname is still in front of that mess.
+ */
+ if(maildropfolder){
+ char *delims = " +-_:!|ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ char *c;
+ size_t len;
+
+ len = 5 + 2 + strlen(maildropfolder) + strlen(add_folder);
+ maildroplongname = (char *) fs_get((len+1) * sizeof(char));
+
+ for(c = delims; *c; c++){
+ if(!strindex(maildropfolder, *c) &&
+ !strindex(add_folder, *c))
+ break;
+ }
+
+ if(*c){
+ snprintf(maildroplongname, len+1, "#move%c%s%c%s",
+ *c, maildropfolder, *c, add_folder);
+ if(strlen(maildroplongname) < add_folderlen){
+ strncpy(add_folder, maildroplongname, add_folderlen);
+ add_folder[add_folderlen-1] = '\0';
+ }
+ }
+ else{
+ q_status_message2(SM_ORDER,0,2,
+ "Can't find delimiter for \"#move %s %s\"",
+ maildropfolder, add_folder);
+ dprint((2,
+ "Can't find delimiter for \"#move %s %s\"",
+ maildropfolder ? maildropfolder : "?",
+ add_folder ? add_folder : "?"));
+
+ if(maildropfolder)
+ fs_give((void **) &maildropfolder);
+
+ if(inbox && def){
+ if(default_mail_drop_host)
+ fs_give((void **) &default_mail_drop_host);
+
+ if(default_mail_drop_folder)
+ fs_give((void **) &default_mail_drop_folder);
+
+ if(default_dstn_host)
+ fs_give((void **) &default_dstn_host);
+
+ if(default_dstn_folder)
+ fs_give((void **) &default_dstn_folder);
+ }
+
+ return(FALSE);
+ }
+
+ if(maildroplongname)
+ fs_give((void **) &maildroplongname);
+ }
+
+ if(inbox)
+ *apval = cpystr(add_folder);
+ else{
+ (*alval)[offset] = put_pair(nickname, add_folder);
+ (*alval)[offset+1] = NULL;
+ }
+
+ set_current_val(&ps_global->vars[varnum], TRUE, FALSE);
+ if(prc)
+ prc->outstanding_pinerc_changes = 1;
+
+ if(context->use & CNTXT_INCMNG){
+ if(!inbox && add_folder && add_folder[0] && alval && *alval && (*alval)[offset]){
+ /*
+ * Instead of re-initing we try to insert the
+ * added folder so that we preserve the last_unseen_update
+ * information.
+ */
+ f = new_folder(add_folder, line_hash((*alval)[offset]));
+ f->isfolder = 1;
+ if(nickname && nickname[0]){
+ f->nickname = cpystr(nickname);
+ f->name_len = strlen(f->nickname);
+ }
+
+ if(F_ON(F_ENABLE_INCOMING_CHECKING, ps_global)
+ && !ps_global->VAR_INCCHECKLIST)
+ f->last_unseen_update = LUU_INIT;
+ else
+ f->last_unseen_update = LUU_NEVERCHK;
+
+ folder_insert(folder_total(FOLDERS(context)), f, FOLDERS(context));
+ }
+ else
+ /* re-init to make sure we got it right */
+ reinit_incoming_folder_list(ps_global, context);
+ }
+
+ if(nickname[0]){
+ strncpy(add_folder, nickname, add_folderlen-1); /* known by new name */
+ add_folder[add_folderlen-1] = '\0';
+ }
+
+ if(!inbox)
+ q_status_message1(SM_ORDER, 0, 3, "Folder \"%s\" created",
+ add_folder);
+ return_val = add_folder;
+ }
+ else if(context_isambig(add_folder)){
+ free_folder_list(context);
+ q_status_message2(SM_ORDER, 0, 3, "%s \"%s\" created",
+ isdir ? "Directory" : "Folder", add_folder);
+ return_val = add_folder;
+ }
+ else
+ q_status_message1(SM_ORDER, 0, 3,
+ "Folder \"%s\" created outside current collection",
+ add_folder);
+
+ if(maildropfolder)
+ fs_give((void **) &maildropfolder);
+
+ if(inbox && def){
+ if(default_mail_drop_host)
+ fs_give((void **) &default_mail_drop_host);
+
+ if(default_mail_drop_folder)
+ fs_give((void **) &default_mail_drop_folder);
+
+ if(default_dstn_host)
+ fs_give((void **) &default_dstn_host);
+
+ if(default_dstn_folder)
+ fs_give((void **) &default_dstn_folder);
+ }
+
+ return(return_val != NULL);
+}
+
+
+/*----------------------------------------------------------------------
+ Subscribe to a news group
+
+ Args: folder -- last folder added
+ cntxt -- The context the subscription is for
+
+ Result: returns the name of the folder subscribed too
+
+
+This builds a complete context for the entire list of possible news groups.
+It also build a context to find the newly created news groups as
+determined by data kept in .pinerc. When the find of these new groups is
+done the subscribed context is searched and the items marked as new.
+A list of new board is never actually created.
+
+ ----*/
+int
+group_subscription(char *folder, size_t len, CONTEXT_S *cntxt)
+{
+ STRLIST_S *folders = NULL;
+ int rc = 0, last_rc, i, n, flags,
+ last_find_partial = 0, we_cancel = 0;
+ CONTEXT_S subscribe_cntxt;
+ HelpType help;
+ ESCKEY_S subscribe_keys[3];
+
+ subscribe_keys[i = 0].ch = ctrl('T');
+ subscribe_keys[i].rval = 12;
+ subscribe_keys[i].name = "^T";
+ subscribe_keys[i++].label = N_("To All Grps");
+
+ if(F_ON(F_ENABLE_TAB_COMPLETE,ps_global)){
+ subscribe_keys[i].ch = ctrl('I');
+ subscribe_keys[i].rval = 11;
+ subscribe_keys[i].name = "TAB";
+ subscribe_keys[i++].label = N_("Complete");
+ }
+
+ subscribe_keys[i].ch = -1;
+
+ /*---- Build a context to find all news groups -----*/
+
+ subscribe_cntxt = *cntxt;
+ subscribe_cntxt.use |= CNTXT_FINDALL;
+ subscribe_cntxt.use &= ~(CNTXT_PSEUDO | CNTXT_ZOOM | CNTXT_PRESRV);
+ subscribe_cntxt.next = NULL;
+ subscribe_cntxt.prev = NULL;
+ subscribe_cntxt.dir = new_fdir(cntxt->dir->ref,
+ cntxt->dir->view.internal, '*');
+ FOLDERS(&subscribe_cntxt) = init_folder_entries();
+ /*
+ * Prompt for group name.
+ */
+ folder[0] = '\0';
+ help = NO_HELP;
+ while(1){
+ flags = OE_APPEND_CURRENT;
+ last_rc = rc;
+ rc = optionally_enter(folder, -FOOTER_ROWS(ps_global), 0, len,
+ SUBSCRIBE_PMT, subscribe_keys, help, &flags);
+ removing_trailing_white_space(folder);
+ removing_leading_white_space(folder);
+ if((rc == 0 && folder[0]) || rc == 11 || rc == 12){
+ we_cancel = busy_cue(_("Fetching newsgroup list"), NULL, 1);
+
+ if(last_find_partial){
+ /* clean up any previous find results */
+ free_folder_list(&subscribe_cntxt);
+ last_find_partial = 0;
+ }
+
+ if(rc == 11){ /* Tab completion! */
+ if(folder_complete_internal(&subscribe_cntxt,
+ folder, len, &n, FC_FORCE_LIST)){
+ continue;
+ }
+ else{
+ if(!(n && last_rc == 11 && !(flags & OE_USER_MODIFIED))){
+ Writechar(BELL, 0);
+ continue;
+ }
+ }
+ }
+
+ if(rc == 12){ /* list the whole enchilada */
+ build_folder_list(NULL, &subscribe_cntxt, NULL, NULL,BFL_NONE);
+ }
+ else if(strlen(folder)){
+ char tmp[MAILTMPLEN];
+
+ snprintf(tmp, sizeof(tmp), "%s%.*s*", (rc == 11) ? "" : "*",
+ sizeof(tmp)-3, folder);
+ tmp[sizeof(tmp)-1] = '\0';
+ build_folder_list(NULL, &subscribe_cntxt, tmp, NULL, BFL_NONE);
+ subscribe_cntxt.dir->status &= ~(CNTXT_PARTFIND|CNTXT_NOFIND);
+ }
+ else{
+ q_status_message(SM_ORDER, 0, 2,
+ _("No group substring to match! Use ^T to list all news groups."));
+ continue;
+ }
+
+ /*
+ * If we did a partial find on matches, then we faked a full
+ * find which will cause this to just return.
+ */
+ if((i = folder_total(FOLDERS(&subscribe_cntxt))) != 0){
+ char *f;
+
+ /*
+ * fake that we've found everything there is to find...
+ */
+ subscribe_cntxt.use &= ~(CNTXT_NOFIND|CNTXT_PARTFIND);
+ last_find_partial = 1;
+
+ if(i == 1){
+ f = folder_entry(0, FOLDERS(&subscribe_cntxt))->name;
+ if(!strcmp(f, folder)){
+ rc = 1; /* success! */
+ break;
+ }
+ else{ /* else complete the group */
+ strncpy(folder, f, len-1);
+ folder[len-1] = '\0';
+ continue;
+ }
+ }
+ else if(!(flags & OE_USER_MODIFIED)){
+ /*
+ * See if there wasn't an exact match in the lot.
+ */
+ while(i-- > 0){
+ f = folder_entry(i,FOLDERS(&subscribe_cntxt))->name;
+ if(!strcmp(f, folder))
+ break;
+ else
+ f = NULL;
+ }
+
+ /* if so, then the user picked it from the list the
+ * last time and didn't change it at the prompt.
+ * Must mean they're accepting it...
+ */
+ if(f){
+ rc = 1; /* success! */
+ break;
+ }
+ }
+ }
+ else{
+ if(rc == 12)
+ q_status_message(SM_ORDER | SM_DING, 3, 3,
+ _("No groups to select from!"));
+ else
+ q_status_message1(SM_ORDER, 3, 3,
+ _("News group \"%s\" didn't match any existing groups"),
+ folder);
+ free_folder_list(&subscribe_cntxt);
+
+ continue;
+ }
+
+ /*----- Mark groups that are currently subscribed too ------*/
+ /* but first make sure they're found */
+ build_folder_list(NULL, cntxt, "*", NULL, BFL_LSUB);
+ for(i = 0 ; i < folder_total(FOLDERS(&subscribe_cntxt)); i++) {
+ FOLDER_S *f = folder_entry(i, FOLDERS(&subscribe_cntxt));
+
+ f->subscribed = search_folder_list(FOLDERS(cntxt),
+ f->name) != 0;
+ }
+
+ if(we_cancel)
+ cancel_busy_cue(-1);
+
+ /*----- Call the folder lister to do all the work -----*/
+ folders = folders_for_subscribe(ps_global,
+ &subscribe_cntxt, folder);
+
+ if(folders){
+ /* Multiple newsgroups OR Auto-select */
+ if(folders->next || F_ON(F_SELECT_WO_CONFIRM,ps_global))
+ break;
+
+ strncpy(folder, (char *) folders->name, len-1);
+ folder[len-1] = '\0';
+ free_strlist(&folders);
+ }
+ }
+ else if(rc == 3){
+ help = help == NO_HELP ? h_news_subscribe : NO_HELP;
+ }
+ else if(rc == 1 || folder[0] == '\0'){
+ rc = -1;
+ break;
+ }
+ }
+
+ if(rc < 0){
+ folder[0] = '\0'; /* make sure not to return partials */
+ if(rc == -1)
+ q_status_message(SM_ORDER, 0, 3, _("Subscribe cancelled"));
+ }
+ else{
+ MAILSTREAM *sub_stream;
+ int sclose = 0, errors = 0;
+
+ if(folders){ /*------ Actually do the subscription -----*/
+ STRLIST_S *flp;
+ int n = 0;
+
+ /* subscribe one at a time */
+ folder[0] = '\0';
+ /*
+ * Open stream before subscribing so c-client knows what newsrc
+ * to use, along with other side-effects.
+ */
+ if((sub_stream = mail_cmd_stream(&subscribe_cntxt, &sclose)) != NULL){
+ for(flp = folders; flp; flp = flp->next){
+ (void) context_apply(tmp_20k_buf, &subscribe_cntxt,
+ (char *) flp->name, SIZEOF_20KBUF);
+ if(mail_subscribe(sub_stream, tmp_20k_buf) == 0L){
+ /*
+ * This message may not make it to the screen,
+ * because a c-client message about the failure
+ * will be there. Probably best not to string
+ * together a whole bunch of errors if there is
+ * something wrong.
+ */
+ q_status_message1(errors ?SM_INFO : SM_ORDER,
+ errors ? 0 : 3, 3,
+ _("Error subscribing to \"%s\""),
+ (char *) flp->name);
+ errors++;
+ }
+ else{
+ n++;
+ if(!folder[0]){
+ strncpy(folder, (char *) flp->name, len-1);
+ folder[len-1] = '\0';
+ }
+
+ /*---- Update the screen display data structures -----*/
+ if(ALL_FOUND(cntxt)){
+ if(cntxt->use & CNTXT_PSEUDO){
+ folder_delete(0, FOLDERS(cntxt));
+ cntxt->use &= ~CNTXT_PSEUDO;
+ }
+
+ folder_insert(-1, new_folder((char *) flp->name, 0),
+ FOLDERS(cntxt));
+ }
+ }
+ }
+ if(sclose)
+ pine_mail_close(sub_stream);
+ }
+ else
+ errors++;
+
+ if(n == 0)
+ q_status_message(SM_ORDER | SM_DING, 3, 5,
+ _("Subscriptions failed, subscribed to no new groups"));
+ else
+ q_status_message3(SM_ORDER | (errors ? SM_DING : 0),
+ errors ? 3 : 0,3,
+ "Subscribed to %s new groups%s%s",
+ comatose((long)n),
+ errors ? ", failed on " : "",
+ errors ? comatose((long)errors) : "");
+
+ free_strlist(&folders);
+ }
+ else{
+ if((sub_stream = mail_cmd_stream(&subscribe_cntxt, &sclose)) != NULL){
+ (void) context_apply(tmp_20k_buf, &subscribe_cntxt, folder,
+ SIZEOF_20KBUF);
+ if(mail_subscribe(sub_stream, tmp_20k_buf) == 0L){
+ q_status_message1(SM_ORDER | SM_DING, 3, 3,
+ _("Error subscribing to \"%s\""), folder);
+ errors++;
+ }
+ else if(ALL_FOUND(cntxt)){
+ /*---- Update the screen display data structures -----*/
+ if(cntxt->use & CNTXT_PSEUDO){
+ folder_delete(0, FOLDERS(cntxt));
+ cntxt->use &= ~CNTXT_PSEUDO;
+ }
+
+ folder_insert(-1, new_folder(folder, 0), FOLDERS(cntxt));
+ }
+ if(sclose)
+ pine_mail_close(sub_stream);
+ }
+ else{
+ q_status_message1(SM_ORDER | SM_DING, 3, 3,
+ _("Error subscribing to \"%s\""), folder);
+ errors++;
+ }
+ }
+
+ if(!errors && folder[0])
+ q_status_message1(SM_ORDER, 0, 3, _("Subscribed to \"%s\""), folder);
+ }
+
+ free_fdir(&subscribe_cntxt.dir, 1);
+ return(folder[0]);
+}
+
+
+/*----------------------------------------------------------------------
+ Rename folder
+
+ Args: new_name -- buffer to contain new file name
+ len -- length of new_name buffer
+ index -- index of folder in folder list to rename
+ context -- collection of folders making up folder list
+ possible_stream -- may be able to use this stream
+
+ Result: returns the new name of the folder, or NULL if nothing happened.
+
+ When either the sent-mail or saved-message folders are renamed, immediately
+create a new one in their place so they always exist. The main loop above also
+detects this and makes the rename look like an add of the sent-mail or
+saved-messages folder. (This behavior may not be optimal, but it keeps things
+consistent.
+
+ ----*/
+int
+rename_folder(CONTEXT_S *context, int index, char *new_name, size_t len, MAILSTREAM *possible_stream)
+{
+ char *folder, prompt[64], *name_p = NULL;
+ HelpType help;
+ FOLDER_S *new_f;
+ PINERC_S *prc = NULL;
+ int rc, ren_cur, cnt = 0, isdir = 0, readonly = 0;
+ EditWhich ew;
+ MAILSTREAM *strm;
+
+ dprint((4, "\n - rename folder -\n"));
+
+ if(NEWS_TEST(context)){
+ q_status_message(SM_ORDER | SM_DING, 3, 3,
+ _("Can't rename news groups!"));
+ return(0);
+ }
+ else if(!folder_total(FOLDERS(context))){
+ q_status_message(SM_ORDER | SM_DING, 0, 4,
+ _("Empty folder collection. No folder to rename!"));
+ return(0);
+ }
+ else if((new_f = folder_entry(index, FOLDERS(context)))
+ && context == ps_global->context_list
+ && !(context->dir && context->dir->ref)
+ && !strucmp(FLDR_NAME(new_f), ps_global->inbox_name)) {
+ q_status_message1(SM_ORDER | SM_DING, 3, 4,
+ _("Can't change special folder name \"%s\""),
+ ps_global->inbox_name);
+ return(0);
+ }
+
+ ew = config_containing_inc_fldr(new_f);
+ if(ps_global->restricted)
+ readonly = 1;
+ else{
+ switch(ew){
+ case Main:
+ prc = ps_global->prc;
+ break;
+ case Post:
+ prc = ps_global->post_prc;
+ break;
+ case None:
+ break;
+ }
+
+ readonly = prc ? prc->readonly : 1;
+ }
+
+ if(prc && prc->quit_to_edit && (context->use & CNTXT_INCMNG)){
+ quit_to_edit_msg(prc);
+ return(0);
+ }
+
+ if(readonly && (context->use & CNTXT_INCMNG)){
+ q_status_message(SM_ORDER,3,5,
+ _("Rename cancelled: folder not in editable config file"));
+ return(0);
+ }
+
+ if(context->use & CNTXT_INCMNG){
+ if(!(folder = new_f->nickname))
+ folder = ""; /* blank nickname */
+ }
+ else
+ folder = FLDR_NAME(new_f);
+
+ ren_cur = strcmp(folder, ps_global->cur_folder) == 0;
+
+ snprintf(prompt, sizeof(prompt), "%s %s to : ", _("Rename"),
+ (context->use & CNTXT_INCMNG)
+ ? _("nickname")
+ : (isdir = new_f->isdir)
+ ? _("directory") : _("folder"));
+ prompt[sizeof(prompt)-1] = '\0';
+ help = NO_HELP;
+ strncpy(new_name, folder, len-1);
+ new_name[len-1] = '\0';
+ while(1) {
+ int flags = OE_APPEND_CURRENT;
+
+ rc = optionally_enter(new_name, -FOOTER_ROWS(ps_global), 0,
+ len, prompt, NULL, help, &flags);
+ if(rc == 3) {
+ help = help == NO_HELP ? h_oe_foldrename : NO_HELP;
+ continue;
+ }
+
+ removing_leading_and_trailing_white_space(new_name);
+
+ if(rc == 0 && (*new_name || (context->use & CNTXT_INCMNG))) {
+ /* verify characters */
+ if(F_OFF(F_ENABLE_DOT_FOLDERS,ps_global) && *new_name == '.'){
+ if(cnt++ <= 0)
+ q_status_message(SM_ORDER,3,3,
+ _("Folder name can't begin with dot"));
+ else
+ q_status_message1(SM_ORDER,3,3,
+ _("Config feature \"s\" enables names beginning with dot"),
+ pretty_feature_name(feature_list_name(F_ENABLE_DOT_FOLDERS), -1));
+
+ display_message(NO_OP_COMMAND);
+ continue;
+ }
+
+ if(folder_index(new_name, context, FI_ANY|FI_RENAME) >= 0){
+ q_status_message1(SM_ORDER, 3, 3,
+ _("Folder \"%s\" already exists"),
+ pretty_fn(new_name));
+ display_message(NO_OP_COMMAND);
+ continue;
+ }
+ else if(context == ps_global->context_list
+ && !(context->dir && context->dir->ref)
+ && !strucmp(new_name, ps_global->inbox_name)){
+ if(context->use & CNTXT_INCMNG)
+ q_status_message1(SM_ORDER, 3, 3, _("Can't rename incoming folder to %s"),
+ new_name);
+ else
+ q_status_message1(SM_ORDER, 3, 3, _("Can't rename folder to %s"),
+ new_name);
+
+ display_message(NO_OP_COMMAND);
+ continue;
+ }
+ }
+
+ if(rc != 4) /* redraw */
+ break; /* no redraw */
+
+ }
+
+ if(rc != 1 && isdir){ /* add trailing delim? */
+ for(name_p = new_name; *name_p && *(name_p+1) ; name_p++)
+ ;
+
+ if(*name_p == context->dir->delim) /* lop off delim */
+ *name_p = '\0';
+ }
+
+ if(rc == 1
+ || !(*new_name || (context->use & CNTXT_INCMNG))
+ || !strcmp(new_name, folder)){
+ q_status_message(SM_ORDER, 0, 2, _("Folder rename cancelled"));
+ return(0);
+ }
+
+ if(context->use & CNTXT_INCMNG){
+ char **new_list, **lp, ***alval;
+ int i;
+
+ alval = ALVAL(&ps_global->vars[V_INCOMING_FOLDERS], ew);
+ for(i = 0; (*alval)[i]; i++)
+ ;
+
+ new_list = (char **) fs_get((i + 1) * sizeof(char *));
+
+ for(lp = new_list, i = 0; (*alval)[i]; i++){
+ /* figure out if this is the one we're renaming */
+ expand_variables(tmp_20k_buf, SIZEOF_20KBUF, (*alval)[i], 0);
+ if(new_f->varhash == line_hash(tmp_20k_buf)){
+ char *folder_string = NULL, *nickname = NULL;
+
+ if(new_f->nickname)
+ fs_give((void **) &new_f->nickname);
+
+ if(*new_name)
+ new_f->nickname = cpystr(new_name);
+
+ /*
+ * Parse folder line for nickname and folder name.
+ * No nickname on line is OK.
+ */
+ get_pair(tmp_20k_buf, &nickname, &folder_string, 0, 0);
+
+ if(nickname)
+ fs_give((void **)&nickname);
+
+ *lp = put_pair(new_name, folder_string);
+
+ new_f->varhash = line_hash(*lp++);
+ }
+ else
+ *lp++ = cpystr((*alval)[i]);
+ }
+
+ *lp = NULL;
+
+ set_variable_list(V_INCOMING_FOLDERS, new_list, TRUE, ew);
+ free_list_array(&new_list);
+
+ return(1);
+ }
+
+ /* Can't rename open streams */
+ if((strm = context_already_open_stream(context, folder, AOS_NONE))
+ || (ren_cur && (strm=ps_global->mail_stream))){
+ if(possible_stream == strm)
+ possible_stream = NULL;
+
+ pine_mail_actually_close(strm);
+ }
+
+ if(possible_stream
+ && !context_same_stream(context, new_name, possible_stream))
+ possible_stream = NULL;
+
+ if((rc = context_rename(context, possible_stream, folder, new_name)) != 0){
+ if(name_p && *name_p == context->dir->delim)
+ *name_p = '\0'; /* blat trailing delim */
+
+ /* insert new name? */
+ if(!strindex(new_name, context->dir->delim)){
+ new_f = new_folder(new_name, 0);
+ new_f->isdir = isdir;
+ folder_insert(-1, new_f, FOLDERS(context));
+ }
+
+ if(strcmp(ps_global->VAR_DEFAULT_FCC, folder) == 0
+ || strcmp(ps_global->VAR_DEFAULT_SAVE_FOLDER, folder) == 0) {
+ /* renaming sent-mail or saved-messages */
+ if(context_create(context, NULL, folder)){
+ q_status_message3(SM_ORDER,0,3,
+ "Folder \"%s\" renamed to \"%s\". New \"%s\" created",
+ folder, new_name,
+ pretty_fn(
+ (strcmp(ps_global->VAR_DEFAULT_SAVE_FOLDER,
+ folder) == 0)
+ ? ps_global->VAR_DEFAULT_SAVE_FOLDER
+ : ps_global->VAR_DEFAULT_FCC));
+
+ }
+ else{
+ q_status_message1(SM_ORDER | SM_DING, 3, 4,
+ "Error creating new \"%s\"", folder);
+
+ dprint((1, "Error creating \"%s\" in %s context\n",
+ folder ? folder : "?",
+ context->context ? context->context : "?"));
+ }
+ }
+ else
+ q_status_message2(SM_ORDER, 0, 3,
+ "Folder \"%s\" renamed to \"%s\"",
+ pretty_fn(folder), pretty_fn(new_name));
+
+ free_folder_list(context);
+ }
+
+ if(ren_cur) {
+ if(ps_global && ps_global->ttyo){
+ blank_keymenu(ps_global->ttyo->screen_rows - 2, 0);
+ ps_global->mangled_footer = 1;
+ }
+
+ /* No reopen the folder we just had open */
+ do_broach_folder(new_name, context, NULL, 0L);
+ }
+
+ return(rc);
+}
+
+
+/*----------------------------------------------------------------------
+ Confirm and delete a folder
+
+ Args: fs -- folder screen state
+
+ Result: return 0 if not delete, 1 if deleted.
+ ----*/
+int
+delete_folder(CONTEXT_S *context, int index, char *next_folder, size_t len, MAILSTREAM **possible_streamp)
+{
+ char *folder, ques_buf[MAX_SCREEN_COLS+1], *target = NULL,
+ *fnamep, buf[1000];
+ MAILSTREAM *del_stream = NULL, *sub_stream, *strm = NULL;
+ FOLDER_S *fp;
+ EditWhich ew;
+ PINERC_S *prc = NULL;
+ int ret, unsub_opened = 0, close_opened = 0, blast_folder = 1,
+ readonly;
+
+ if(!context){
+ cmd_cancelled("Missing context in Delete");
+ return(0);
+ }
+
+ if(NEWS_TEST(context)){
+ folder = folder_entry(index, FOLDERS(context))->name;
+ snprintf(ques_buf, sizeof(ques_buf), _("Really unsubscribe from \"%s\""), folder);
+ ques_buf[sizeof(ques_buf)-1] = '\0';
+
+ ret = want_to(ques_buf, 'n', 'x', NO_HELP, WT_NORM);
+ switch(ret) {
+ /* ^C */
+ case 'x':
+ Writechar(BELL, 0);
+ /* fall through */
+ case 'n':
+ return(0);
+ }
+
+ dprint((2, "deleting folder \"%s\" in context \"%s\"\n",
+ folder ? folder : "?",
+ context->context ? context->context : "?"));
+
+ if((sub_stream = mail_cmd_stream(context, &unsub_opened)) != NULL){
+ (void) context_apply(tmp_20k_buf, context, folder, SIZEOF_20KBUF);
+ if(!mail_unsubscribe(sub_stream, tmp_20k_buf)){
+ q_status_message1(SM_ORDER | SM_DING, 3, 3,
+ _("Error unsubscribing from \"%s\""),folder);
+ if(unsub_opened)
+ pine_mail_close(sub_stream);
+ return(0);
+ }
+ if(unsub_opened)
+ pine_mail_close(sub_stream);
+ }
+
+ /*
+ * Fix up the displayed list
+ */
+ folder_delete(index, FOLDERS(context));
+ return(1);
+ }
+
+ fp = folder_entry(index, FOLDERS(context));
+ if(!fp){
+ cmd_cancelled("Can't find folder to Delete!");
+ return(0);
+ }
+
+ if(!((context->use & CNTXT_INCMNG) && fp->name
+ && check_for_move_mbox(fp->name, NULL, 0, &target))){
+ target = NULL;
+ }
+
+ folder = FLDR_NAME(fp);
+ dprint((4, "=== delete_folder(%s) ===\n",
+ folder ? folder : "?"));
+
+ ew = config_containing_inc_fldr(fp);
+ if(ps_global->restricted)
+ readonly = 1;
+ else{
+ switch(ew){
+ case Main:
+ prc = ps_global->prc;
+ break;
+ case Post:
+ prc = ps_global->post_prc;
+ break;
+ case None:
+ break;
+ }
+
+ readonly = prc ? prc->readonly : 1;
+ }
+
+ if(prc && prc->quit_to_edit && (context->use & CNTXT_INCMNG)){
+ quit_to_edit_msg(prc);
+ return(0);
+ }
+
+ if(context == ps_global->context_list
+ && !(context->dir && context->dir->ref)
+ && strucmp(folder, ps_global->inbox_name) == 0){
+ q_status_message1(SM_ORDER | SM_DING, 3, 4,
+ _("Can't delete special folder \"%s\"."),
+ ps_global->inbox_name);
+ return(0);
+ }
+ else if(readonly && (context->use & CNTXT_INCMNG)){
+ q_status_message(SM_ORDER,3,5,
+ _("Deletion cancelled: folder not in editable config file"));
+ return(0);
+ }
+ else if((fp->name
+ && (strm=context_already_open_stream(context,fp->name,AOS_NONE)))
+ ||
+ (target
+ && (strm=context_already_open_stream(NULL,target,AOS_NONE)))){
+ if(strm == ps_global->mail_stream)
+ close_opened++;
+ }
+ else if(fp->isdir || fp->isdual){ /* NO DELETE if directory isn't EMPTY */
+ FDIR_S *fdirp = next_folder_dir(context,folder,TRUE,possible_streamp);
+
+ if(fp->haschildren)
+ ret = 1;
+ else if(fp->hasnochildren)
+ ret = 0;
+ else{
+ ret = folder_total(fdirp->folders) > 0;
+ free_fdir(&fdirp, 1);
+ }
+
+ if(ret){
+ q_status_message2(SM_ORDER | SM_DING, 3, 4,
+ _("Can't delete non-empty directory \"%s\"%s."),
+ folder, (fp->isfolder && fp->isdual) ? " (or folder of same name)" : "");
+ return(0);
+ }
+
+ /*
+ * Folder by the same name exist? If so, server's probably going
+ * to delete it as well. Punt?
+ */
+ if(fp->isdual
+ && (ret = want_to(DIR_FOLD_PMT,'n','x',NO_HELP,WT_NORM)) != 'y'){
+ q_status_message(SM_ORDER,0,3, (ret == 'x') ? _("Delete cancelled")
+ : _("No folder deleted"));
+ return(0);
+ }
+ }
+
+ if(context->use & CNTXT_INCMNG){
+ static ESCKEY_S delf_opts[] = {
+ {'n', 'n', "N", N_("Nickname only")},
+ {'b', 'b', "B", N_("Both Folder and Nickname")},
+ {-1, 0, NULL, NULL}
+ };
+#define DELF_PROMPT _("DELETE only Nickname or Both nickname and folder? ")
+
+ switch(radio_buttons(DELF_PROMPT, -FOOTER_ROWS(ps_global),
+ delf_opts,'n','x',NO_HELP,RB_NORM)){
+ case 'n' :
+ blast_folder = 0;
+ break;
+
+ case 'x' :
+ cmd_cancelled("Delete");
+ return(0);
+
+ default :
+ break;
+ }
+ }
+ else{
+ unsigned char *fname = folder_name_decoded((unsigned char *)folder);
+ snprintf(ques_buf, sizeof(ques_buf), "DELETE \"%s\"%s", fname ? (char *) fname : folder,
+ close_opened ? " (the currently open folder)" :
+ (fp->isdir && !(fp->isdual || fp->isfolder
+ || (folder_index(folder, context, FI_FOLDER) >= 0)))
+ ? " (a directory)" : "");
+ if(fname) fs_give((void **)&fname);
+ ques_buf[sizeof(ques_buf)-1] = '\0';
+
+ if((ret = want_to(ques_buf, 'n', 'x', NO_HELP, WT_NORM)) != 'y'){
+ q_status_message(SM_ORDER,0,3, (ret == 'x') ? _("Delete cancelled")
+ : _("Nothing deleted"));
+ return(0);
+ }
+ }
+
+ if(blast_folder){
+ dprint((2,"deleting \"%s\" (%s) in context \"%s\"\n",
+ fp->name ? fp->name : "?",
+ fp->nickname ? fp->nickname : "",
+ context->context ? context->context : "?"));
+ if(strm){
+ /*
+ * Close it, NULL the pointer, and let do_broach_folder fixup
+ * the rest...
+ */
+ pine_mail_actually_close(strm);
+ if(close_opened){
+ if(ps_global && ps_global->ttyo){
+ blank_keymenu(ps_global->ttyo->screen_rows - 2, 0);
+ ps_global->mangled_footer = 1;
+ }
+
+ ps_global->mangled_header = 1;
+ do_broach_folder(ps_global->inbox_name,
+ ps_global->context_list, NULL, DB_INBOXWOCNTXT);
+ }
+ }
+
+ /*
+ * Use fp->name since "folder" may be a nickname...
+ */
+ if(possible_streamp && *possible_streamp
+ && context_same_stream(context, fp->name, *possible_streamp))
+ del_stream = *possible_streamp;
+
+ fnamep = fp->name;
+
+ if(!context_delete(context, del_stream, fnamep)){
+/*
+ * BUG: what if sent-mail or saved-messages????
+ */
+ q_status_message1(SM_ORDER,3,3,"Delete of \"%s\" Failed!", folder);
+ return(0);
+ }
+ }
+
+ snprintf(buf, sizeof(buf), "%s\"%s\" deleted.",
+ !blast_folder ? "Nickname " :
+ fp->isdual ? "Folder/Directory " :
+ (fp->isdir && fp->isfolder) ? "Folder " :
+ fp->isdir ? "Directory " :
+ "Folder ",
+ folder);
+ buf[sizeof(buf)-1] = '\0';
+
+ q_status_message(SM_ORDER, 0, 3, buf);
+
+ if(context->use & CNTXT_INCMNG){
+ char **new_list, **lp, ***alval;
+ int i;
+
+ alval = ALVAL(&ps_global->vars[V_INCOMING_FOLDERS], ew);
+ for(i = 0; (*alval)[i]; i++)
+ ;
+
+ /*
+ * Make it one too big in case we don't find the match for
+ * some unknown reason.
+ */
+ new_list = (char **) fs_get((i + 1) * sizeof(char *));
+
+ /*
+ * Copy while figuring out which one to skip.
+ */
+ for(lp = new_list, i = 0; (*alval)[i]; i++){
+ expand_variables(tmp_20k_buf, SIZEOF_20KBUF, (*alval)[i], 0);
+ if(fp->varhash != line_hash(tmp_20k_buf))
+ *lp++ = cpystr((*alval)[i]);
+ }
+
+ *lp = NULL;
+
+ set_variable_list(V_INCOMING_FOLDERS, new_list, TRUE, ew);
+ free_list_array(&new_list);
+ }
+
+ /*
+ * Fix up the displayed list.
+ */
+ folder_delete(index, FOLDERS(context));
+
+ /*
+ * Take a guess at what should get hilited next.
+ */
+ if(index < (ret = folder_total(FOLDERS(context)))
+ || ((index = ret - 1) >= 0)){
+ if((fp = folder_entry(index, FOLDERS(context)))
+ && strlen(FLDR_NAME(fp)) < len - 1)
+ strncpy(next_folder, FLDR_NAME(fp), len-1);
+ next_folder[len-1] = '\0';
+ }
+
+ if(!(context->use & CNTXT_INCMNG)){
+ /*
+ * Then cause the list to get rebuild 'cause we may've blasted
+ * some folder that's also a directory or vice versa...
+ */
+ free_folder_list(context);
+ }
+
+ return(1);
+}
+
+
+/*----------------------------------------------------------------------
+ Print the list of folders on paper
+
+ Args: list -- The current list of folders
+ lens -- The list of lengths of the current folders
+ display -- The current folder display structure
+
+ Result: list printed on paper
+
+If the display list was created for 80 columns it is used, otherwise
+a new list is created for 80 columns
+
+ ----*/
+void
+print_folders(FPROC_S *fp)
+{
+ if(open_printer(_("folder list")) == 0){
+ (void) folder_list_text(ps_global, fp, print_char, NULL, 80);
+
+ close_printer();
+ }
+}
+
+
+int
+scan_get_pattern(char *kind, char *pat, int len)
+{
+ char prompt[256];
+ int flags;
+
+ pat[0] = '\0';
+ snprintf(prompt, sizeof(prompt), _("String in folder %s to match : "), kind);
+ prompt[sizeof(prompt)-1] = '\0';
+
+ while(1){
+ flags = OE_APPEND_CURRENT | OE_DISALLOW_HELP;
+ switch(optionally_enter(pat, -FOOTER_ROWS(ps_global), 0, len,
+ prompt, NULL, NO_HELP, &flags)){
+
+ case 4 :
+ if(ps_global->redrawer)
+ (*ps_global->redrawer)();
+
+ break;
+
+ case 0 :
+ if(*pat)
+ return(1);
+
+ case 1 :
+ cmd_cancelled("Select");
+
+ default :
+ return(0);
+ }
+ }
+}
+
+
+int
+folder_select_text(struct pine *ps, CONTEXT_S *context, int selected)
+{
+ char pattern[MAILTMPLEN], type = '\0';
+ static ESCKEY_S scan_opts[] = {
+ {'n', 'n', "N", N_("Name Select")},
+ {'c', 'c', "C", N_("Content Select")},
+ {-1, 0, NULL, NULL}
+ };
+
+ if(context->use & CNTXT_INCMNG){
+ q_status_message(SM_ORDER | SM_DING, 3, 3,
+ _("Select \"Text\" not supported in Incoming Folders"));
+ return(0);
+ }
+
+ switch(radio_buttons(SEL_TEXT_PMT, -FOOTER_ROWS(ps_global),
+ scan_opts, 'n', 'x', NO_HELP, RB_NORM)){
+ case 'n' : /* Name search */
+ if(scan_get_pattern("NAME", pattern, sizeof(pattern)))
+ type = 'n';
+
+ break;
+
+ case 'c' : /* content search */
+ if(scan_get_pattern("CONTENTS", pattern, sizeof(pattern)))
+ type = 'c';
+
+ break;
+
+ case 'x' :
+ default :
+ break;
+ }
+
+ if(type){
+ int rv;
+ char tmp[MAILTMPLEN];
+ SCANARG_S args;
+
+ memset(&args, 0, sizeof(SCANARG_S));
+ args.pattern = pattern;
+ args.type = type;
+ args.context = context;
+
+ if(type == 'c'){
+ args.stream = sp_stream_get(context_apply(tmp, context,
+ "xxx", sizeof(tmp)),
+ SP_SAME);
+ if(!args.stream){
+ args.stream = pine_mail_open(NULL, tmp,
+ OP_SILENT|OP_HALFOPEN|SP_USEPOOL|SP_TEMPUSE,
+ NULL);
+ args.newstream = (args.stream != NULL);
+ }
+ }
+
+ rv = foreach_folder(context, selected,
+ foreach_do_scan, (void *) &args);
+
+ if(args.newstream)
+ pine_mail_close(args.stream);
+
+ if(rv)
+ return(1);
+ }
+
+ cmd_cancelled("Select");
+ return(0);
+}
+
+
+int
+foreach_do_scan(FOLDER_S *f, void *d)
+{
+ SCANARG_S *sa = (SCANARG_S *) d;
+
+ return((sa->type == 'n' && srchstr(FLDR_NAME(f), sa->pattern))
+ || (sa->type == 'c'
+ && scan_scan_folder(sa->stream, sa->context, f, sa->pattern)));
+}
+
+
+int
+scan_scan_folder(MAILSTREAM *stream, CONTEXT_S *context, FOLDER_S *f, char *pattern)
+{
+ MM_LIST_S ldata;
+ LISTRES_S response;
+ int we_cancel = 0;
+ char *folder, *ref = NULL, tmp[MAILTMPLEN];
+
+ folder = f->name;
+ snprintf(tmp, sizeof(tmp), "Scanning \"%s\"", FLDR_NAME(f));
+ tmp[sizeof(tmp)-1] = '\0';
+ we_cancel = busy_cue(tmp, NULL, 1);
+
+ mm_list_info = &ldata; /* tie down global reference */
+ memset(&ldata, 0, sizeof(MM_LIST_S));
+ ldata.filter = mail_list_response;
+ memset(ldata.data = &response, 0, sizeof(LISTRES_S));
+
+ /*
+ * If no preset reference string, must be at top of context
+ */
+ if(context && context_isambig(folder) && !(ref = context->dir->ref)){
+ char *p;
+
+ if((p = strstr(context->context, "%s")) != NULL){
+ if(!*(p+2)){
+ snprintf(tmp, sizeof(tmp), "%.*s", MIN(p - context->context, sizeof(tmp)-1),
+ context->context);
+ tmp[sizeof(tmp)-1] = '\0';
+ ref = tmp;
+ }
+ else{
+ snprintf(tmp, sizeof(tmp), context->context, folder);
+ tmp[sizeof(tmp)-1] = '\0';
+ folder = tmp;
+ ref = "";
+ }
+ }
+ else
+ ref = context->context;
+ }
+
+ mail_scan(stream, ref, folder, pattern);
+
+ if(context && context->dir && response.delim)
+ context->dir->delim = response.delim;
+
+ if(we_cancel)
+ cancel_busy_cue(-1);
+
+ return(((response.isfile) ? FEX_ISFILE : 0)
+ | ((response.isdir) ? FEX_ISDIR : 0));
+}
+
+
+int
+folder_select_props(struct pine *ps, CONTEXT_S *context, int selected)
+{
+ int cmp = 0;
+ long flags = 0L, count;
+ static ESCKEY_S prop_opts[] = {
+ {'u', 'u', "U", N_("Unseen msgs")},
+ {'n', 'n', "N", N_("New msgs")},
+ {'c', 'c', "C", N_("msg Count")},
+ {-1, 0, NULL, NULL}
+ };
+
+ if(context->use & CNTXT_INCMNG){
+ q_status_message(SM_ORDER | SM_DING, 3, 3,
+ _("Select \"Properties\" not supported in Incoming Folders"));
+ return(0);
+ }
+
+ switch(radio_buttons(SEL_PROP_PMT, -FOOTER_ROWS(ps_global),
+ prop_opts, 'n', 'x', h_folder_prop, RB_NORM)){
+ case 'c' : /* message count */
+ if(folder_select_count(&count, &cmp))
+ flags = SA_MESSAGES;
+
+ break;
+
+ case 'n' : /* folders with new */
+ flags = SA_RECENT;
+ break;
+
+ case 'u' : /* folders with unseen */
+ flags = SA_UNSEEN;
+ break;
+
+ case 'x' :
+ default :
+ break;
+ }
+
+ if(flags){
+ int rv;
+ char tmp[MAILTMPLEN];
+ STATARG_S args;
+
+ memset(&args, 0, sizeof(STATARG_S));
+ args.flags = flags;
+ args.context = context;
+ args.nmsgs = count;
+ args.cmp = cmp;
+
+ args.stream = sp_stream_get(context_apply(tmp, context,
+ "xxx", sizeof(tmp)),
+ SP_SAME);
+ if(!args.stream){
+ args.stream = pine_mail_open(NULL, tmp,
+ OP_SILENT|OP_HALFOPEN|SP_USEPOOL|SP_TEMPUSE,
+ NULL);
+ args.newstream = (args.stream != NULL);
+ }
+
+ rv = foreach_folder(context, selected,
+ foreach_do_stat, (void *) &args);
+
+ if(args.newstream)
+ pine_mail_close(args.stream);
+
+ if(rv)
+ return(1);
+ }
+
+ cmd_cancelled("Select");
+ return(0);
+}
+
+
+int
+folder_select_count(long int *count, int *cmp)
+{
+ int r, flags;
+ char number[32], prompt[128];
+ static char *tense[] = {"EQUAL TO", "LESS THAN", "GREATER THAN"};
+ static ESCKEY_S sel_num_opt[] = {
+ {ctrl('W'), 14, "^W", N_("Toggle Comparison")},
+ {-1, 0, NULL, NULL}
+ };
+
+ *count = 0L;
+ while(1){
+ flags = OE_APPEND_CURRENT | OE_DISALLOW_HELP;
+ snprintf(number, sizeof(number), "%ld", *count);
+ number[sizeof(number)-1] = '\0';
+ snprintf(prompt, sizeof(prompt), "Select folders with messages %s : ", tense[*cmp]);
+ prompt[sizeof(prompt)-1] = '\0';
+ r = optionally_enter(number, -FOOTER_ROWS(ps_global), 0, sizeof(number),
+ prompt, sel_num_opt, NO_HELP, &flags);
+ switch (r){
+ case 0 :
+ if(!*number)
+ break;
+ else if((*count = atol(number)) < 0L)
+ q_status_message(SM_ORDER, 3, 3,
+ "Can't have NEGATIVE message count!");
+ else
+ return(1); /* success */
+
+ case 3 : /* help */
+ case 4 : /* redraw */
+ continue;
+
+ case 14 : /* toggle comparison */
+ *cmp = ++(*cmp) % 3;
+ continue;
+
+ case -1 : /* cancel */
+ default:
+ break;
+ }
+
+ break;
+ }
+
+ return(0); /* return failure */
+}
+
+
+int
+foreach_do_stat(FOLDER_S *f, void *d)
+{
+ STATARG_S *sa = (STATARG_S *) d;
+ int rv = 0;
+
+ if(ps_global->context_current == sa->context
+ && !strcmp(ps_global->cur_folder, FLDR_NAME(f))){
+ switch(sa->flags){
+ case SA_MESSAGES :
+ switch(sa->cmp){
+ case 0 : /* equals */
+ if(ps_global->mail_stream->nmsgs == sa->nmsgs)
+ rv = 1;
+
+ break;
+
+ case 1 : /* less than */
+ if(ps_global->mail_stream->nmsgs < sa->nmsgs)
+ rv = 1;
+
+ break;
+
+ case 2 :
+ if(ps_global->mail_stream->nmsgs > sa->nmsgs)
+ rv = 1;
+
+ break;
+
+ default :
+ break;
+ }
+
+ break;
+
+ case SA_RECENT :
+ if(count_flagged(ps_global->mail_stream, F_RECENT))
+ rv = 1;
+
+ break;
+
+ case SA_UNSEEN :
+ if(count_flagged(ps_global->mail_stream, F_UNSEEN))
+ rv = 1;
+
+ break;
+
+ default :
+ break;
+ }
+ }
+ else{
+ int we_cancel = 0;
+ char msg_buf[MAX_BM+1];
+ extern MAILSTATUS mm_status_result;
+
+ snprintf(msg_buf, sizeof(msg_buf), "Checking %s for %s", FLDR_NAME(f),
+ (sa->flags == SA_UNSEEN)
+ ? "unseen messages"
+ : (sa->flags == SA_MESSAGES) ? "message count"
+ : "recent messages");
+ msg_buf[sizeof(msg_buf)-1] = '\0';
+ we_cancel = busy_cue(msg_buf, NULL, 0);
+
+ if(!context_status(sa->context, sa->stream, f->name, sa->flags))
+ mm_status_result.flags = 0L;
+
+ if(we_cancel)
+ cancel_busy_cue(0);
+
+ if(sa->flags & mm_status_result.flags)
+ switch(sa->flags){
+ case SA_MESSAGES :
+ switch(sa->cmp){
+ case 0 : /* equals */
+ if(mm_status_result.messages == sa->nmsgs)
+ rv = 1;
+
+ break;
+
+ case 1 : /* less than */
+ if(mm_status_result.messages < sa->nmsgs)
+ rv = 1;
+
+ break;
+
+ case 2 :
+ if(mm_status_result.messages > sa->nmsgs)
+ rv = 1;
+
+ break;
+
+ default :
+ break;
+ }
+
+ break;
+
+ case SA_RECENT :
+ if(mm_status_result.recent)
+ rv = 1;
+
+ break;
+
+ case SA_UNSEEN :
+ if(mm_status_result.unseen)
+ rv = 1;
+
+ break;
+
+ default :
+ break;
+ }
+ }
+
+ return(rv);
+}
+
+
+int
+foreach_folder(CONTEXT_S *context, int selected, int (*test) (FOLDER_S *, void *), void *args)
+{
+ int i, n, rv = 1;
+ int we_turned_on = 0;
+ FOLDER_S *fp;
+
+ we_turned_on = intr_handling_on();
+
+ for(i = 0, n = folder_total(FOLDERS(context)); i < n; i++){
+ if(ps_global->intr_pending){
+ for(; i >= 0; i--)
+ folder_entry(i, FOLDERS(context))->scanned = 0;
+
+ cmd_cancelled("Select");
+ rv = 0;
+ break;
+ }
+
+ fp = folder_entry(i, FOLDERS(context));
+ fp->scanned = 0;
+ if((!selected || fp->selected) && fp->isfolder && (*test)(fp, args))
+ fp->scanned = 1;
+ }
+
+ if(we_turned_on)
+ intr_handling_off();
+
+ return(rv);
+}
+
+
+/*----------------------------------------------------------------------
+ Return the path delimiter for the given folder on the given server
+
+ Args: folder -- folder type for delimiter
+
+ ----*/
+int
+folder_delimiter(char *folder)
+{
+ int rv, we_cancel = 0;
+
+ we_cancel = busy_cue(NULL, NULL, 1);
+
+ rv = get_folder_delimiter(folder);
+
+ if(we_cancel)
+ cancel_busy_cue(-1);
+
+ return(rv);
+}
+
+
+/*
+ * next_folder - given a current folder in a context, return the next in
+ * the list, or NULL if no more or there's a problem.
+ *
+ * Args streamp -- If set, try to re-use this stream for checking.
+ * next -- Put return value here, return points to this
+ * nextlen -- Length of next
+ * current -- Current folder, so we know where to start looking
+ * cntxt --
+ * find_recent -- Returns the number of recent here. The presence of
+ * this arg also indicates that we should do the calls
+ * to figure out whether there is a next interesting folder
+ * or not.
+ * did_cancel -- Tell caller if user canceled. Only used if find_recent
+ * is also set. Also, user will only be given the
+ * opportunity to cancel if this is set. If it isn't
+ * set, we just plow ahead when there is an error or
+ * when the folder does not exist.
+ */
+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;
+ char prompt[128];
+ FOLDER_S *f = NULL;
+ char tmp[MAILTMPLEN];
+
+
+ /* note: find_folders may assign "stream" */
+ build_folder_list(streamp, cntxt, NULL, NULL,
+ NEWS_TEST(cntxt) ? BFL_LSUB : BFL_NONE);
+
+ try_fast = (F_ON(F_ENABLE_FAST_RECENT, ps_global) &&
+ F_OFF(F_TAB_USES_UNSEEN, ps_global));
+ if(find_recent)
+ *find_recent = 0L;
+
+ for(index = folder_index(current, cntxt, FI_FOLDER) + 1;
+ index > 0
+ && index < folder_total(FOLDERS(cntxt))
+ && (f = folder_entry(index, FOLDERS(cntxt)))
+ && !f->isdir;
+ index++)
+ if(find_recent){
+ MAILSTREAM *stream = NULL;
+ int rv, we_cancel = 0, match;
+ char msg_buf[MAX_BM+1];
+
+ /* 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)))
+ continue;
+
+ /*
+ * If we already have the folder open, short circuit all this
+ * stuff.
+ */
+ match = 0;
+ if((stream = sp_stream_get(context_apply(tmp, cntxt, f->name,
+ sizeof(tmp)),
+ SP_MATCH)) != NULL
+ || (!IS_REMOTE(tmp) && (stream = already_open_stream(tmp, AOS_NONE)) != NULL)){
+ (void) pine_mail_ping(stream);
+
+ if(F_ON(F_TAB_USES_UNSEEN, ps_global)){
+ /*
+ * Just fall through and let the status call below handle
+ * the already open stream. If we were doing this the
+ * same as the else case, we would figure out how many
+ * unseen are in this open stream by doing a search.
+ * Instead of repeating that code that is already in
+ * pine_mail_status_full, fall through and note the
+ * special case by lighting the match variable.
+ */
+ match++;
+ }
+ else{
+ *find_recent = sp_recent_since_visited(stream);
+ if(*find_recent){
+ recent++;
+ break;
+ }
+
+ continue;
+ }
+ }
+
+ snprintf(msg_buf, sizeof(msg_buf), "Checking %s for %s messages",
+ FLDR_NAME(f), F_ON(F_TAB_USES_UNSEEN, ps_global) ? "unseen" : "recent");
+ msg_buf[sizeof(msg_buf)-1] = '\0';
+ we_cancel = busy_cue(msg_buf, NULL, 0);
+
+ /* First, get a stream for the test */
+ if(!stream && streamp && *streamp){
+ if(context_same_stream(cntxt, f->name, *streamp)){
+ stream = *streamp;
+ }
+ else{
+ pine_mail_close(*streamp);
+ *streamp = NULL;
+ }
+ }
+
+ if(!stream){
+ stream = sp_stream_get(context_apply(tmp, cntxt, f->name,
+ sizeof(tmp)),
+ SP_SAME);
+ }
+
+ /*
+ * If interestingness is indeterminate or we're
+ * told to explicitly, look harder...
+ */
+
+ /*
+ * We could make this more efficient in the cases where we're
+ * opening a new stream or using streamp by having folder_exists
+ * cache the stream. The change would require a folder_exists()
+ * that caches streams, but most of the time folder_exists just
+ * uses the inbox stream or ps->mail_stream.
+ *
+ * Another thing to consider is that maybe there should be an
+ * option to try to LIST a folder before doing a STATUS (or SELECT).
+ * This isn't done by default for the case where a folder is
+ * SELECTable but not LISTable, but on some servers doing an
+ * RLIST first tells the server that we support mailbox referrals.
+ */
+ if(!try_fast
+ || !((rv = folder_exists(cntxt,f->name))
+ & (FEX_ISMARKED | FEX_UNMARKED))){
+ extern MAILSTATUS mm_status_result;
+
+ if(try_fast && (rv == 0 || rv & FEX_ERROR)){
+ failed_status = 1;
+ mm_status_result.flags = 0L;
+ }
+ else{
+ if(stream){
+ if(!context_status_full(cntxt, match ? NULL : stream,
+ f->name,
+ F_ON(F_TAB_USES_UNSEEN, ps_global)
+ ? SA_UNSEEN : SA_RECENT,
+ &f->uidvalidity,
+ &f->uidnext)){
+ failed_status = 1;
+ mm_status_result.flags = 0L;
+ }
+ }
+ else{
+ /* so we can re-use the stream */
+ if(!context_status_streamp_full(cntxt, streamp, f->name,
+ F_ON(F_TAB_USES_UNSEEN, ps_global)
+ ? SA_UNSEEN : SA_RECENT,
+ &f->uidvalidity,
+ &f->uidnext)){
+ failed_status = 1;
+ mm_status_result.flags = 0L;
+ }
+ }
+ }
+
+ if(F_ON(F_TAB_USES_UNSEEN, ps_global)){
+ rv = ((mm_status_result.flags & SA_UNSEEN)
+ && (*find_recent = mm_status_result.unseen))
+ ? FEX_ISMARKED : 0;
+ }
+ else{
+ rv = ((mm_status_result.flags & SA_RECENT)
+ && (*find_recent = mm_status_result.recent))
+ ? FEX_ISMARKED : 0;
+ }
+
+ /* we don't know how many in this case */
+ if(try_fast)
+ *find_recent = 0L; /* consistency, boy! */
+ }
+
+ if(we_cancel)
+ cancel_busy_cue(0);
+
+ if(failed_status && did_cancel){
+ char buf1[6*MAX_SCREEN_COLS+1];
+ int wid1, wid2;
+
+ snprintf(prompt, sizeof(prompt), _("Check of folder %s failed. Continue "), FLDR_NAME(f));
+ if(utf8_width(prompt) > MAXPROMPT){
+ snprintf(prompt, sizeof(prompt), _("Check of %s failed. Continue "), FLDR_NAME(f));
+ if((wid1=utf8_width(prompt)) > MAXPROMPT){
+ if((wid2=utf8_width(FLDR_NAME(f))) > wid1-MAXPROMPT)
+ snprintf(prompt, sizeof(prompt), _("Check of %s failed. Continue "), strsquish(buf1, sizeof(buf1), FLDR_NAME(f), wid2-(wid1-MAXPROMPT)));
+ else
+ snprintf(prompt, sizeof(prompt), _("Check failed. Continue "));
+ }
+ }
+
+ if(want_to(prompt, 'y', 0, NO_HELP, WT_NORM) == 'n'){
+ *did_cancel = 1;
+ break;
+ }
+ else
+ /* have to reset this lower-level cancel marker */
+ ps_global->user_says_cancel = 0;
+ }
+
+ failed_status = 0;
+
+ if(rv & FEX_ISMARKED){
+ recent++;
+ break;
+ }
+ }
+
+ if(f && (!find_recent || recent)){
+ strncpy(next, FLDR_NAME(f), nextlen);
+ next[nextlen-1] = '\0';
+ }
+ else if(nextlen > 0)
+ *next = '\0';
+
+ /* BUG: how can this be made smarter so we cache the list? */
+ free_folder_list(cntxt);
+ return((*next) ? next : NULL);
+}
+
+
+/*----------------------------------------------------------------------
+ Shuffle order of incoming folders
+ ----*/
+int
+shuffle_incoming_folders(CONTEXT_S *context, int index)
+{
+ int tot, i, deefault, rv, inheriting = 0;
+ int new_index, index_within_var, new_index_within_var;
+ int readonly = 0;
+ char tmp[200];
+ HelpType help;
+ ESCKEY_S opts[3];
+ char ***alval;
+ EditWhich ew;
+ FOLDER_S *fp;
+ PINERC_S *prc = NULL;
+
+ dprint((4, "shuffle_incoming_folders\n"));
+
+ if(!(context->use & CNTXT_INCMNG) ||
+ (tot = folder_total(FOLDERS(context))) < 2 ||
+ index < 0 || index >= tot)
+ return(0);
+
+ if(index == 0){
+ q_status_message(SM_ORDER,0,3, _("Cannot shuffle INBOX"));
+ return(0);
+ }
+
+ fp = folder_entry(index, FOLDERS(context));
+ ew = config_containing_inc_fldr(fp);
+
+ if(ps_global->restricted)
+ readonly = 1;
+ else{
+ switch(ew){
+ case Main:
+ prc = ps_global->prc;
+ break;
+ case Post:
+ prc = ps_global->post_prc;
+ break;
+ case None:
+ break;
+ }
+
+ readonly = prc ? prc->readonly : 1;
+ }
+
+ if(prc && prc->quit_to_edit){
+ quit_to_edit_msg(prc);
+ return(0);
+ }
+
+ if(readonly){
+ q_status_message(SM_ORDER,3,5,
+ _("Shuffle cancelled: config file not editable"));
+ return(0);
+ }
+
+ alval = ALVAL(&ps_global->vars[V_INCOMING_FOLDERS], ew);
+
+ if(!(alval && *alval))
+ return(0);
+
+ i = 0;
+ opts[i].ch = 'b';
+ opts[i].rval = 'b';
+ opts[i].name = "B";
+ opts[i++].label = N_("Back");
+
+ opts[i].ch = 'f';
+ opts[i].rval = 'f';
+ opts[i].name = "F";
+ opts[i++].label = N_("Forward");
+
+ opts[i].ch = -1;
+ deefault = 'b';
+
+ /* find where this entry is in the particular config list */
+ index_within_var = -1;
+ for(i = 0; (*alval)[i]; i++){
+ expand_variables(tmp_20k_buf, SIZEOF_20KBUF, (*alval)[i], 0);
+ if(i == 0 && !strcmp(tmp_20k_buf, INHERIT))
+ inheriting = 1;
+ else if(fp->varhash == line_hash(tmp_20k_buf)){
+ index_within_var = i;
+ break;
+ }
+ }
+
+ if(index_within_var == -1){ /* didn't find it */
+ q_status_message(SM_ORDER,3,5,
+ _("Shuffle cancelled: unexpected trouble shuffling"));
+ return(0);
+ }
+
+ if(index_within_var == 0 || (inheriting && index_within_var == 1)){
+ opts[0].ch = -2; /* no back */
+ deefault = 'f';
+ }
+
+ if(!(*alval)[i+1]) /* no forward */
+ opts[1].ch = -2;
+
+ if(opts[0].ch == -2 && opts[1].ch == -2){
+ q_status_message(SM_ORDER, 0, 4,
+ _("Cannot shuffle from one config file to another."));
+ return(0);
+ }
+
+ snprintf(tmp, sizeof(tmp), "Shuffle \"%s\" %s%s%s ? ",
+ FLDR_NAME(folder_entry(index, FOLDERS(context))),
+ (opts[0].ch != -2) ? "BACK" : "",
+ (opts[0].ch != -2 && opts[1].ch != -2) ? " or " : "",
+ (opts[1].ch != -2) ? "FORWARD" : "");
+ tmp[sizeof(tmp)-1] = '\0';
+ help = (opts[0].ch == -2) ? h_incoming_shuf_down
+ : (opts[1].ch == -2) ? h_incoming_shuf_up
+ : h_incoming_shuf;
+
+ rv = radio_buttons(tmp, -FOOTER_ROWS(ps_global), opts, deefault, 'x',
+ help, RB_NORM);
+
+ new_index = index;
+ new_index_within_var = index_within_var;
+
+ switch(rv){
+ case 'x':
+ cmd_cancelled("Shuffle");
+ return(0);
+
+ case 'b':
+ new_index_within_var--;
+ new_index--;
+ break;
+
+ case 'f':
+ new_index_within_var++;
+ new_index++;
+ break;
+ }
+
+ if(swap_incoming_folders(index, new_index, FOLDERS(context))){
+ char *stmp;
+
+ /* swap them in the config variable, too */
+ stmp = (*alval)[index_within_var];
+ (*alval)[index_within_var] = (*alval)[new_index_within_var];
+ (*alval)[new_index_within_var] = stmp;
+
+ set_current_val(&ps_global->vars[V_INCOMING_FOLDERS], TRUE, FALSE);
+ write_pinerc(ps_global, ew, WRP_NONE);
+
+ return(1);
+ }
+ else
+ return(0);
+}
+
+
+int
+swap_incoming_folders(int index1, int index2, FLIST *flist)
+{
+ FOLDER_S *ftmp;
+
+ if(!flist)
+ return(0);
+
+ if(index1 == index2)
+ return(1);
+
+ if(index1 < 0 || index1 >= flist->used){
+ dprint((1, "Error in swap_incoming_folders: index1=%d, used=%d\n", index1, flist->used));
+ return(0);
+ }
+
+ if(index2 < 0 || index2 >= flist->used){
+ dprint((1, "Error in swap_incoming_folders: index2=%d, used=%d\n", index2, flist->used));
+ return(0);
+ }
+
+ ftmp = flist->folders[index1];
+ flist->folders[index1] = flist->folders[index2];
+ flist->folders[index2] = ftmp;
+
+ return(1);
+}
+
+
+/*----------------------------------------------------------------------
+ Find an entry in the folder list by matching names
+ ----*/
+int
+search_folder_list(void *list, char *name)
+{
+ int i;
+ char *n;
+
+ for(i = 0; i < folder_total(list); i++) {
+ n = folder_entry(i, list)->name;
+ if(strucmp(name, n) == 0)
+ return(1); /* Found it */
+ }
+ return(0);
+}
+
+
+static CONTEXT_S *post_cntxt = NULL;
+
+/*----------------------------------------------------------------------
+ Browse list of newsgroups available for posting
+
+ Called from composer when ^T is typed in newsgroups field
+
+Args: none
+
+Returns: pointer to selected newsgroup, or NULL.
+ Selector call in composer expects this to be alloc'd here.
+
+ ----*/
+char *
+news_group_selector(char **error_mess)
+{
+ CONTEXT_S *tc;
+ char *post_folder;
+ int rc;
+ char *em;
+
+ /* Coming back from composer */
+ fix_windsize(ps_global);
+ init_sigwinch();
+
+ post_folder = fs_get((size_t)MAILTMPLEN);
+
+ /*--- build the post_cntxt -----*/
+ em = get_post_list(ps_global->VAR_NNTP_SERVER);
+ if(em != NULL){
+ if(error_mess != NULL)
+ *error_mess = cpystr(em);
+
+ cancel_busy_cue(-1);
+ return(NULL);
+ }
+
+ /*----- Call the browser -------*/
+ tc = post_cntxt;
+ if((rc = folders_for_post(ps_global, &tc, post_folder)) != 0)
+ post_cntxt = tc;
+
+ cancel_busy_cue(-1);
+
+ if(rc <= 0)
+ return(NULL);
+
+ return(post_folder);
+}
+
+
+/*----------------------------------------------------------------------
+ Get the list of news groups that are possible for posting
+
+Args: post_host -- host name for posting
+
+Returns NULL if list is retrieved, pointer to error message if failed
+
+This is kept in a standards "CONTEXT" for a acouple of reasons. First
+it makes it very easy to use the folder browser to display the
+newsgroup for selection on ^T from the composer. Second it will allow
+the same mechanism to be used for all folder lists on memory tight
+systems like DOS. The list is kept for the life of the session because
+fetching it is a expensive.
+
+ ----*/
+char *
+get_post_list(char **post_host)
+{
+ char *post_context_string;
+
+ if(!post_host || !post_host[0]) {
+ /* BUG should assume inews and get this from active file */
+ return(_("Can't post messages, NNTP server needs to be configured"));
+ }
+
+ if(!post_cntxt){
+ int we_cancel;
+ size_t l;
+
+ we_cancel = busy_cue(_("Getting full list of groups for posting"), NULL, 1);
+
+ l = strlen(post_host[0]) + 20;
+ post_context_string = (char *) fs_get((l+1) * sizeof(char));
+ snprintf(post_context_string, l+1, "{%s/nntp}#news.[]", post_host[0]);
+ post_context_string[l] = '\0';
+
+ post_cntxt = new_context(post_context_string, NULL);
+ post_cntxt->use |= CNTXT_FINDALL;
+ post_cntxt->dir->status |= CNTXT_NOFIND;
+ post_cntxt->next = NULL;
+
+ build_folder_list(NULL, post_cntxt, NULL, NULL,
+ NEWS_TEST(post_cntxt) ? BFL_LSUB : BFL_NONE);
+ if(we_cancel)
+ cancel_busy_cue(-1);
+ }
+ return(NULL);
+}
+
+
+#ifdef _WINDOWS
+int
+folder_list_popup(sparms, in_handle)
+ SCROLL_S *sparms;
+ int in_handle;
+{
+ MPopup fldr_popup[20];
+
+ memset(fldr_popup, 0, 20 * sizeof(MPopup));
+ fldr_popup[0].type = tTail;
+ if(in_handle){
+ int i, n = 0;
+ HANDLE_S *h = get_handle(sparms->text.handles, in_handle);
+ FOLDER_S *fp = (h) ? folder_entry(h->h.f.index,
+ FOLDERS(h->h.f.context))
+ : NULL;
+
+ if((i = menu_binding_index(sparms->keys.menu, MC_CHOICE)) >= 0){
+ fldr_popup[n].type = tQueue;
+ fldr_popup[n].data.val = sparms->keys.menu->keys[i].bind.ch[0];
+ fldr_popup[n].label.style = lNormal;
+ fldr_popup[n++].label.string = (fp && fp->isdir)
+ ? "&View Directory"
+ : "&View Folder";
+ }
+
+ if((i = menu_binding_index(sparms->keys.menu, MC_SELCUR)) >= 0
+ && bitnset(i, sparms->keys.bitmap)){
+ fldr_popup[n].type = tQueue;
+ fldr_popup[n].data.val = sparms->keys.menu->keys[i].bind.ch[0];
+ fldr_popup[n].label.style = lNormal;
+ fldr_popup[n++].label.string = (fp && fp->isdir)
+ ? "&Select Directory"
+ : "&Select Folder";
+ }
+
+ if((i = menu_binding_index(sparms->keys.menu, MC_DELETE)) >= 0
+ && bitnset(i, sparms->keys.bitmap)){
+ fldr_popup[n].type = tQueue;
+ fldr_popup[n].data.val = sparms->keys.menu->keys[i].bind.ch[0];
+ fldr_popup[n].label.style = lNormal;
+ fldr_popup[n++].label.string = "Delete Folder";
+ }
+
+ if((i = menu_binding_index(sparms->keys.menu, MC_EXPORT)) >= 0
+ && bitnset(i, sparms->keys.bitmap)){
+ fldr_popup[n].type = tQueue;
+ fldr_popup[n].data.val = sparms->keys.menu->keys[i].bind.ch[0];
+ fldr_popup[n].label.style = lNormal;
+ fldr_popup[n++].label.string = "Export Folder";
+ }
+
+ if((i = menu_binding_index(sparms->keys.menu, MC_CHK_RECENT)) >= 0
+ && bitnset(i, sparms->keys.bitmap)){
+ fldr_popup[n].type = tQueue;
+ fldr_popup[n].data.val = sparms->keys.menu->keys[i].bind.ch[0];
+ fldr_popup[n].label.style = lNormal;
+ fldr_popup[n++].label.string = "Check New Messages";
+ }
+
+ if(n)
+ fldr_popup[n++].type = tSeparator;
+
+ folder_popup_config(((FPROC_S *)sparms->proc.data.p)->fs,
+ sparms->keys.menu, &fldr_popup[n]);
+ }
+ else
+ folder_popup_config(((FPROC_S *)sparms->proc.data.p)->fs,
+ sparms->keys.menu, fldr_popup);
+
+ if(fldr_popup[0].type != tTail)
+ mswin_popup(fldr_popup);
+
+ return(0);
+}
+
+
+int
+folder_list_select_popup(sparms, in_handle)
+ SCROLL_S *sparms;
+ int in_handle;
+{
+ MPopup fldr_popup[20];
+
+ memset(fldr_popup, 0, 20 * sizeof(MPopup));
+ fldr_popup[0].type = tTail;
+ if(in_handle){
+ int i, n = 0;
+ HANDLE_S *h = get_handle(sparms->text.handles, in_handle);
+ FOLDER_S *fp = (h) ? folder_entry(h->h.f.index,FOLDERS(h->h.f.context))
+ : NULL;
+
+ if((i = menu_binding_index(sparms->keys.menu, MC_CHOICE)) >= 0){
+ fldr_popup[n].type = tQueue;
+ fldr_popup[n].data.val = sparms->keys.menu->keys[i].bind.ch[0];
+ fldr_popup[n].label.style = lNormal;
+ fldr_popup[n++].label.string = (fp && fp->isdir)
+ ? "&View Directory"
+ : "&Select";
+
+ fldr_popup[n++].type = tSeparator;
+ }
+
+ folder_popup_config(((FPROC_S *)sparms->proc.data.p)->fs,
+ sparms->keys.menu, &fldr_popup[n]);
+ }
+ else
+ folder_popup_config(((FPROC_S *)sparms->proc.data.p)->fs,
+ sparms->keys.menu, fldr_popup);
+
+ if(fldr_popup[0].type != tTail)
+ mswin_popup(fldr_popup);
+
+ return(0);
+}
+
+
+/*
+ * Just a little something to simplify assignments
+ */
+#define FLDRPOPUP(p, c, s) { \
+ (p)->type = tQueue; \
+ (p)->data.val = c; \
+ (p)->label.style = lNormal; \
+ (p)->label.string = s; \
+ }
+
+
+/*----------------------------------------------------------------------
+ Popup Menu configurator
+
+ ----*/
+void
+folder_popup_config(fs, km, popup)
+ FSTATE_S *fs;
+ struct key_menu *km;
+ MPopup *popup;
+{
+ int i;
+
+ if((i = menu_binding_index(km, MC_PARENT)) >= 0){
+ FLDRPOPUP(popup, km->keys[i].bind.ch[0], "Parent Directory");
+ popup++;
+ }
+
+ if(fs->km == &folder_km){
+ if((fs->context->next || fs->context->prev) && !fs->combined_view){
+ FLDRPOPUP(popup, '<', "Collection List");
+ popup++;
+ }
+ }
+ else if((i = menu_binding_index(km, MC_COLLECTIONS)) >= 0){
+ FLDRPOPUP(popup, km->keys[i].bind.ch[0], "Collection List");
+ popup++;
+ }
+
+ if((i = menu_binding_index(km, MC_INDEX)) >= 0){
+ FLDRPOPUP(popup, km->keys[i].bind.ch[0], "Current Folder Index");
+ popup++;
+ }
+
+ if((i = menu_binding_index(km, MC_MAIN)) >= 0){
+ FLDRPOPUP(popup, km->keys[i].bind.ch[0], "Main Menu");
+ popup++;
+ }
+
+ popup->type = tTail; /* tie off the array */
+}
+#endif /* _WINDOWS */