#if !defined(lint) && !defined(DOS)
static char rcsid[] = "$Id: folder.c,v 4.418 1999/09/29 02:56:17 hubert Exp $";
#endif
/*----------------------------------------------------------------------

            T H E    P I N E    M A I L   S Y S T E M

   Laurence Lundblade and Mike Seibel
   Networks and Distributed Computing
   Computing and Communications
   University of Washington
   Administration Builiding, AG-44
   Seattle, Washington, 98195, USA
   Internet: lgl@CAC.Washington.EDU
             mikes@CAC.Washington.EDU

   Please address all bugs and comments to "pine-bugs@cac.washington.edu"


   Pine and Pico are registered trademarks of the University of Washington.
   No commercial use of these trademarks may be made without prior written
   permission of the University of Washington.

   Pine, Pico, and Pilot software and its included text are Copyright
   1989-1999 by the University of Washington.

   The full text of our legal notices is contained in the file called
   CPYRIGHT, included with this distribution.


   USENET News reading additions in part by L Lundblade / NorthWestNet, 1993
   lgl@nwnet.net

   Pine is in part based on The Elm Mail System:
    ***********************************************************************
    *  The Elm Mail System  -  Revision: 2.13                             *
    *                                                                     *
    * 			Copyright (c) 1986, 1987 Dave Taylor              *
    * 			Copyright (c) 1988, 1989 USENET Community Trust   *
    ***********************************************************************
 

  ----------------------------------------------------------------------*/

/*======================================================================
     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"


#define	CLICKHERE	"[ Select Here to See Expanded List ]"
#define	CLICKHERETOO	"[ ** Empty List **  Select Here to Try Re-Expanding ]"
#define	CLICKHERETOONEWS \
	"[ ** Empty List **  Use \"A Subscribe\" to subscribe to a newsgroup ]"
#define	ALL_FOUND(X)	(((X)->dir->status & CNTXT_NOFIND) == 0 && \
			  ((X)->dir->status & CNTXT_PARTFIND) == 0)
#define	FLDR_NAME(X)	((X) ? ((X)->nickname ? (X)->nickname : (X)->name) :"")
#define	FLDR_ENT(S)	(((S) && (S)->text.handles) \
			 ? folder_entry((S)->text.handles->h.f.index, \
				FOLDERS((S)->text.handles->h.f.context)) \
			 : NULL)
#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 by the same name *MAY* get deleted as well.  Continue"

#define	mail_list(S, R, N)	mail_list_internal(S, R, N)

/*
 * folder name completion routine flags
 */
#define	FC_NONE		0
#define	FC_FORCE_LIST	1


/*
 * folder_list_write
 */
#define	FLW_NONE	0x00
#define	FLW_LUNK	0x01
#define	FLW_SLCT	0x02
#define	FLW_LIST	0x04




/*----------------------------------------------------------------------
   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		  */
    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;
    struct key_menu *km;		/* key label/command bindings	  */
    struct _func_dispatch {
	int	 (*valid) PROTO((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)


/*
 * Structs to ease c-client LIST/LSUB interaction
 */
typedef struct _listargs {
    char *reference,		/* IMAP LIST "reference" arg	    */
	 *name,			/* IMAP LIST "name" arg		    */
	 *tail;			/* Pine "context" after "name" part */
} LISTARGS_S;


typedef struct _listresponse {
    long     count;
    int	     delim;
    unsigned isfile:1,
	     isdir:1,
	     ismarked:1,
	     unmarked:1;
    
} LISTRES_S;


typedef struct _build_folder_list_data {
    long	 mask;			/* bitmap of responses to ignore    */
    LISTARGS_S	 args;
    LISTRES_S	 response;
    void	*list;
} BFL_DATA_S;


typedef struct _existdata {
    LISTARGS_S	 args;
    LISTRES_S	 response;
    char       **fullname;
} EXISTDATA_S;


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;



typedef enum {NotChecked, NotInCache, Found, Missing, End} NgCacheReturns;


/* short definition to keep compilers happy */
typedef    int (*QSFunc) PROTO((const QSType *, const QSType *));


/*
 * Internal prototypes
 */
CONTEXT_S *context_screen PROTO((CONTEXT_S *, struct key_menu *, int));
STRLIST_S *folder_lister PROTO((struct pine *, FSTATE_S *));
int	   folder_list_text PROTO((struct pine *, FPROC_S *,
				   gf_io_t, HANDLE_S **, int));
int	   folder_list_write_folder PROTO((gf_io_t, CONTEXT_S *,
					   int, char *, int));
int	   folder_list_write_prefix PROTO((FOLDER_S *, int, gf_io_t));
int	   folder_list_ith PROTO((int, CONTEXT_S *));
char	  *folder_list_center_space PROTO((char *, int));
HANDLE_S  *folder_list_handle PROTO((FSTATE_S *, HANDLE_S *));
int	   folder_processor PROTO((int, MSGNO_S *, SCROLL_S *));
int	   folder_lister_choice PROTO((SCROLL_S *));
int        folder_lister_clickclick PROTO((SCROLL_S *));
int	   folder_lister_finish PROTO((SCROLL_S *, CONTEXT_S *, int));
void	   folder_lister_km_manager PROTO((SCROLL_S *, int));
void	   folder_lister_km_sel_manager PROTO((SCROLL_S *, int));
void	   folder_lister_km_sub_manager PROTO((SCROLL_S *, int));
int	   folder_lister_select PROTO((FSTATE_S *, CONTEXT_S *, int));
int	   folder_lister_parent PROTO((FSTATE_S *, CONTEXT_S *, int, int));
char	  *folder_lister_desc PROTO((CONTEXT_S *, FDIR_S *));
char	  *folder_lister_fullname PROTO((FSTATE_S *, char *));
void	   folder_sublist_context PROTO((char *, CONTEXT_S *, CONTEXT_S *,
					 FDIR_S **, int));
int	   folder_selector PROTO((struct pine *, FSTATE_S *,
				  char *, CONTEXT_S **));
char	  *exit_collection_add PROTO((struct headerentry *, void (*)()));
char	  *cancel_collection_add PROTO((char *, void (*)()));
int	   build_namespace PROTO((char *, char **, char **,
				  BUILDER_ARG *, int *));
int	   fl_val_gen PROTO((FOLDER_S *, FSTATE_S *));
int	   fl_val_writable PROTO((FOLDER_S *, FSTATE_S *));
int	   fl_val_subscribe PROTO((FOLDER_S *, FSTATE_S *));
void       redraw_folder_lister PROTO((void));
void       display_folder PROTO((FSTATE_S *, int));
int	   folder_select PROTO((struct pine *, CONTEXT_S *, int));
int	   folder_select_restore PROTO((CONTEXT_S *));
void	   folder_select_preserve PROTO((CONTEXT_S *));
int	   selected_folders PROTO((CONTEXT_S *));
int	   folder_insert_sorted PROTO((int, int, int, FOLDER_S *, void *,
				       int (*) PROTO((FOLDER_S *,
						      FOLDER_S *))));
void       folder_insert_index PROTO((FOLDER_S *, int, void *));
int        folder_total PROTO((void *));
int	   add_new_folder PROTO((CONTEXT_S *, char *));
int	   group_subscription PROTO((char *, CONTEXT_S *));
STRLIST_S *folders_for_subscribe PROTO((struct pine *, CONTEXT_S *, char *));
int	   rename_folder PROTO((CONTEXT_S *, int, char *));
int        delete_folder PROTO((CONTEXT_S *, int, char *));
void       print_folders PROTO((FPROC_S *));
int        search_folders PROTO((FSTATE_S *, int));
int        compare_names PROTO((const QSType *, const QSType *));
int	   compare_folders_alpha PROTO((FOLDER_S *, FOLDER_S *));
int	   compare_folders_dir_alpha PROTO((FOLDER_S *, FOLDER_S *));
int	   compare_folders_alpha_dir PROTO((FOLDER_S *, FOLDER_S *));
void      *init_folder_entries PROTO(());
void	   refresh_folder_list PROTO((CONTEXT_S *, int, int));
void       free_folder_entries PROTO((void **));
FOLDER_S  *new_folder PROTO((char *));
FDIR_S	  *new_fdir PROTO((char *, char *, int));
void	   free_fdir PROTO((FDIR_S **, int));
int	   update_bboard_spec PROTO((char *, char *));
void       folder_delete PROTO((int, void *));
int	   folder_complete_internal PROTO((CONTEXT_S *, char *, int *, int));
char      *get_post_list PROTO((char **));
NgCacheReturns chk_newsgrp_cache PROTO((char *));
void       add_newsgrp_cache PROTO((char *, NgCacheReturns));
char	  *folder_last_cmpnt PROTO((char *, int));
int	   folder_select_toggle PROTO((CONTEXT_S *, int,
				       int (*) PROTO((CONTEXT_S *, int))));
MAILSTREAM *mail_cmd_stream PROTO((CONTEXT_S *, int *));
FDIR_S	  *next_folder_dir PROTO((CONTEXT_S *, char *, int));
void	   reset_context_folders PROTO((CONTEXT_S *));
#ifdef	_WINDOWS
int	   folder_scroll_callback PROTO((int,long));
int	   folder_list_popup PROTO((SCROLL_S *, int));
int	   folder_list_select_popup PROTO((SCROLL_S *, int));
void	   folder_popup_config PROTO((FSTATE_S *, struct key_menu *,MPopup *));
#endif
int	   mail_list_in_collection PROTO((char **, char *, char *, char *));
void	   mail_list_filter PROTO((MAILSTREAM *, char *, int, long, void *));
void	   mail_lsub_filter PROTO((MAILSTREAM *, char *, int, long, void *));
void	   mail_list_exists PROTO((MAILSTREAM *, char *, int, long, void *));
void	   mail_list_response PROTO((MAILSTREAM *, char *, int, long, void *));
int	   folder_delimiter PROTO((char *));
int	   folder_select_props PROTO((struct pine *, CONTEXT_S *, int));
int	   folder_select_count PROTO((long *, int *));
int	   folder_select_text PROTO((struct pine *, CONTEXT_S *, int));
int	   foreach_folder PROTO((CONTEXT_S *, int,
				 int (*) PROTO((FOLDER_S *, void *)), void *));
int	   foreach_do_scan PROTO((FOLDER_S *, void *));
int	   scan_get_pattern PROTO((char *, char *, int));
int	   scan_scan_folder PROTO((MAILSTREAM *, CONTEXT_S *,
				   FOLDER_S *, char *));
int	   foreach_do_stat PROTO((FOLDER_S *, void *));
void	   mail_list_internal PROTO((MAILSTREAM *, char *, char *));
int        percent_formatted PROTO((void));
#ifdef	DEBUG
void	   dump_contexts PROTO((FILE *fp));
#endif


/*
 * Various screen keymenu/command binding s.
 */
#define PREVC_MENU {"P", "PrevCltn",   {MC_PREVITEM, 1, {'p'}}, KS_NONE}
#define NEXTC_MENU {"N", "NextCltn",   {MC_NEXTITEM, 2, {'n',TAB}}, KS_NONE}
#define	DELC_MENU  {"D", "Del Cltn",   {MC_DELETE,2,{'d',KEY_DEL}}, KS_NONE}
#define PREVF_MENU {"P", "PrevFldr",   {MC_PREV_HANDLE, 3, \
					  {'p', ctrl('B'), KEY_LEFT}}, KS_NONE}
#define NEXTF_MENU {"N", "NextFldr",   {MC_NEXT_HANDLE, 4, \
					  {'n', ctrl('F'), TAB, KEY_RIGHT}}, \
					  KS_NONE}
#define	CIND_MENU  {"I", "CurIndex",   {MC_INDEX,1,{'i'}}, KS_FLDRINDEX}

static struct {
       int num_done;
       int total;
} percent;
  
static struct key context_mgr_keys[] = 
       {HELP_MENU,
	OTHER_MENU,
        {"<", "Main Menu", {MC_MAIN,3,{'m','<',','}}, KS_EXITMODE},
        {">", "[View Cltn]",
	 {MC_CHOICE,5,{'v','>','.',ctrl('M'),ctrl('J')}}, KS_NONE},
	PREVC_MENU,
	NEXTC_MENU,
	PREVPAGE_MENU,
	NEXTPAGE_MENU,
	NULL_MENU,
	NULL_MENU,
	NULL_MENU,
	WHEREIS_MENU,

	HELP_MENU,
	OTHER_MENU,
	QUIT_MENU,
	NULL_MENU,
	NULL_MENU,
	GOTO_MENU,
	CIND_MENU,
	COMPOSE_MENU,
	PRYNTTXT_MENU,
	RCOMPOSE_MENU,
	NULL_MENU,
	NULL_MENU};
INST_KEY_MENU(c_mgr_km, context_mgr_keys);
#define	CM_KEY_ADD	8
#define	CM_KEY_DELETE	9
#define	CM_KEY_RENAME	10


static struct key context_cfg_keys[] = 
       {HELP_MENU,
	OTHER_MENU,
	{"E", "Exit Setup", {MC_EXIT,1,{'e'}}, KS_EXITMODE},
	{"C", "[Change]", {MC_EDIT,3,{'c',ctrl('M'),ctrl('J')}}, KS_NONE},
	PREVC_MENU,
	NEXTC_MENU,
	PREVPAGE_MENU,
	NEXTPAGE_MENU,
	{"A", "Add Cltn", {MC_ADD,1,{'a'}}, KS_NONE},
	DELC_MENU,
	{"$", "Shuffle", {MC_SHUFFLE,1,{'$'}},KS_NONE},
	WHEREIS_MENU,

	HELP_MENU,
	OTHER_MENU,
	NULL_MENU,
	NULL_MENU,
	NULL_MENU,
	NULL_MENU,
	NULL_MENU,
	NULL_MENU,
	PRYNTTXT_MENU,
	NULL_MENU,
	NULL_MENU,
	NULL_MENU};
INST_KEY_MENU(c_cfg_km, context_cfg_keys);

static struct key context_select_keys[] = 
       {HELP_MENU,
	{"E", "ExitSelect", {MC_EXIT,1,{'e'}}, KS_EXITMODE},
	NULL_MENU,
	{">", "[View Cltn]",
	 {MC_CHOICE, 5, {'v','>','.',ctrl('M'),ctrl('J')}}, KS_NONE},
	PREVC_MENU,
	NEXTC_MENU,
	PREVPAGE_MENU,
	NEXTPAGE_MENU,
	NULL_MENU,
	NULL_MENU,
	NULL_MENU,
	WHEREIS_MENU};
INST_KEY_MENU(c_sel_km, context_select_keys);

static struct key context_fcc_keys[] = 
       {HELP_MENU,
	{"E", "ExitSelect", {MC_EXIT,1,{'e'}}, KS_EXITMODE},
	NULL_MENU,
	{">", "[View Cltn]",
	 {MC_CHOICE, 5, {'v','>','.',ctrl('M'),ctrl('J')}}, KS_NONE},
	PREVC_MENU,
	NEXTC_MENU,
	PREVPAGE_MENU,
	NEXTPAGE_MENU,
	NULL_MENU,
	NULL_MENU,
	NULL_MENU,
	WHEREIS_MENU};
INST_KEY_MENU(c_fcc_km, context_fcc_keys);


static struct key folder_keys[] =
       {HELP_MENU,
  	OTHER_MENU,
	{"<", NULL, {MC_EXIT,3,{0,'<',','}}, KS_NONE},
        {">", NULL, {MC_CHOICE,2,{'>','.'}}, KS_NONE},
	PREVF_MENU,
	NEXTF_MENU,
	PREVPAGE_MENU,
	NEXTPAGE_MENU,
	{"A","Add",{MC_ADDFLDR,1,{'a'}},KS_NONE},
	DELETE_MENU,
	{"R","Rename",{MC_RENAMEFLDR,1,{'r'}}, KS_NONE},
	WHEREIS_MENU,

	HELP_MENU,
	OTHER_MENU,
	QUIT_MENU,
	MAIN_MENU,
	{"V", "[View Fldr]", {MC_OPENFLDR}, KS_NONE},
	GOTO_MENU,
	CIND_MENU,
	COMPOSE_MENU,
	{"%", "Print", {MC_PRINTFLDR,1,{'%'}}, KS_PRINT},
	{"Z", "ZoomMode", {MC_ZOOM,1,{'z'}}, KS_NONE},
	{";","Select",{MC_SELECT,1,{';'}},KS_SELECT},
	{":","SelectCur",{MC_SELCUR,1,{':'}},KS_SELECT},

	HELP_MENU,
	OTHER_MENU,
	NULL_MENU,
	NULL_MENU,
	NULL_MENU,
	NULL_MENU,
	NULL_MENU,
	RCOMPOSE_MENU,
	NULL_MENU,
	NULL_MENU,
	NULL_MENU,
	NULL_MENU};
INST_KEY_MENU(folder_km, folder_keys);
#define	KM_COL_KEY	2
#define	KM_SEL_KEY	3
#define	KM_MAIN_KEY	15
#define	KM_ALTVIEW_KEY	16
#define	KM_ZOOM_KEY	21
#define	KM_SELECT_KEY	22
#define	KM_SELCUR_KEY	23


static struct key folder_sel_keys[] =
       {HELP_MENU,
	{"E", "ExitSelect", {MC_EXIT,1,{'e'}}, KS_EXITMODE},
	NULL_MENU,
	{NULL, NULL, {MC_CHOICE,3,{0,ctrl('M'),ctrl('J')}},
	 KS_NONE},
	PREVF_MENU,
	NEXTF_MENU,
	PREVPAGE_MENU,
	NEXTPAGE_MENU,
	NULL_MENU,
	NULL_MENU,
	NULL_MENU,
	WHEREIS_MENU};
INST_KEY_MENU(folder_sel_km, folder_sel_keys);
#define	FC_EXIT_KEY	1
#define	FC_COL_KEY	2
#define	FC_SEL_KEY	3

static struct key folder_sub_keys[] =
       {HELP_MENU,
	{"S", "Subscribe", {MC_CHOICE,1,{'s'}}, KS_NONE},
  	{"E", "ExitSubscb", {MC_EXIT,1,{'e'}}, KS_EXITMODE},
        {NULL, "[Select]", {MC_CHOICE,3,{'s',ctrl('M'),ctrl('J')}}, KS_NONE},
	PREVF_MENU,
	NEXTF_MENU,
	PREVPAGE_MENU,
	NEXTPAGE_MENU,
	{"L", "List Mode", {MC_LISTMODE, 1, {'l'}}, KS_NONE},
	NULL_MENU,
	NULL_MENU,
	WHEREIS_MENU};
INST_KEY_MENU(folder_sub_km, folder_sub_keys);
#define	SB_SUB_KEY	1
#define SB_EXIT_KEY     2
#define	SB_SEL_KEY	3
#define	SB_LIST_KEY	8

static struct key folder_post_keys[] =
       {HELP_MENU,
 	NULL_MENU,
	{"E", "ExitSelect", {MC_EXIT,1,{'e'}}, KS_EXITMODE},
	{"S", "[Select]", {MC_CHOICE, 3, {'s',ctrl('M'),ctrl('J')}}, KS_NONE},
	PREVF_MENU,
	NEXTF_MENU,
	PREVPAGE_MENU,
	NEXTPAGE_MENU,
	NULL_MENU,
	NULL_MENU,
	NULL_MENU,
	WHEREIS_MENU};
INST_KEY_MENU(folder_post_km, folder_post_keys);





/*----------------------------------------------------------------------
      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(ps)
    struct pine *ps;
{
    int		n = 1;
    CONTEXT_S  *cntxt = ps->context_current;
    STRLIST_S  *folders;
    FSTATE_S    fs;

    dprint(1, (debugfile, "=== 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.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.f.valid		= fl_val_gen;
    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)){
	if(strlen(ps_global->cur_folder) < MAXFOLDER - 1)
	  strcpy(fs.first_folder, ps_global->cur_folder);

	/*
	 * 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, p - q);
		    tmp[p - q] = '\0';

		    fp = next_folder_dir(cntxt, tmp, FALSE);

		    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(folders = folder_lister(ps, &fs)){

	    if(do_broach_folder((char *) folders->name, fs.context) == 1){
		reset_context_folders(ps->context_list);
		ps->next_screen = mail_index_screen;
	    }

	    free_strlist(&folders);
	}
    }

    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(ps)
    struct pine *ps;
{
    CONT_SCR_S css;

    dprint(1, (debugfile, "=== folder_config_screen called ====\n"));
    mailcap_free(); /* free resources we won't be using for a while */

    memset(&css, 0, sizeof(CONT_SCR_S));
    css.title	     = "SETUP COLLECTION LIST";
    css.print_string = "contexts ";
    css.start        = ps->context_current;
    css.contexts     = &ps_global->context_list;
    css.help.text    = h_collection_maint;
    css.help.title   = "HELP FOR SETUP COLLECTION";
    css.keymenu	     = &c_cfg_km;
    css.edit	     = 1;

    /*
     * Use conf_scroll_screen to manage display/selection
     * of contexts
     */
    (void) context_select_screen(ps_global, &css);
}



/*----------------------------------------------------------------------
 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(ps, cntxtp, folder, sublist)
    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, (debugfile, "=== 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(ps, cntxtp, folder, sublist)
    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, (debugfile, "=== 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_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 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(ps, cntxt, folder)
    struct pine	 *ps;
    CONTEXT_S	 *cntxt;
    char	 *folder;
{
    STRLIST_S	*folders = NULL;
    FSTATE_S	 fs;
    void       (*redraw)();

    dprint(1, (debugfile, "=== 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(ps, cntxtp, folder)
    struct pine	 *ps;
    CONTEXT_S	**cntxtp;
    char	 *folder;
{
    FSTATE_S fs;

    dprint(1, (debugfile, "=== 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(ps, fs, folder, cntxtp)
    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)){
	    strcpy(folder, (char *) folders->name);
	    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));

    return(rv);
}


void
folder_sublist_context(folder, cntxt, new_cntxt, new_dir, lev)
    char       *folder;
    CONTEXT_S  *cntxt, *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)){
	sprintf(tmp_20k_buf, "%s%s", p + 1, wildcard);
	(*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{
	sprintf(tmp_20k_buf, "%s%s%s",
		(lev > 1) ? wildcard : "", folder, wildcard);
	(*new_dir)->view.internal = cpystr(tmp_20k_buf);
	/* leave (*new_dir)->ref == NULL */
    }

    sprintf(tmp_20k_buf, "List of folders matching \"%s*\"", folder);
    (*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(errmsg)
    char **errmsg;
{
    char      *rs = NULL;
    STRLIST_S *folders;
    FSTATE_S   fs;

    dprint(1, (debugfile, "=== 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_sel_km;

    /* start in the default save context */
    fs.context = default_save_context(ps_global->context_list);

    do{
	if(folders = folder_lister(ps_global, &fs)){
	    char *name;

	    /* replace nickname with full name */
	    if(!(name = folder_is_nick((char *) folders->name,
				       FOLDERS(fs.context))))
	      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);
		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);
		    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));

    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 *
folders_for_roles(flags)
    int flags;
{
    char      *rs = NULL;
    STRLIST_S *folders;
    FSTATE_S   fs;

    dprint(1, (debugfile, "=== folders_for_roles 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_sel_km;
    if(flags & FOR_PATTERN){
	fs.f.help.text   = h_folder_pattern_roles;
	fs.f.help.title  = "HELP FOR SELECTING CURRENT 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)){
	    char *name = NULL;

	    /* replace nickname with full name */
	    if(!(flags & FOR_PATTERN))
	      name = folder_is_nick((char *) folders->name,
				    FOLDERS(fs.context));

	    if(!name)
	      name = (char *) folders->name;

	    if(context_isambig(name) &&
	       !(flags & FOR_PATTERN &&
	         folder_is_nick(name, FOLDERS(fs.context)))){
		char path_in_context[MAILTMPLEN];

		context_apply(path_in_context, fs.context, name);

		/*
		 * 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(!(flags & FOR_PATTERN) && !(IS_REMOTE(path_in_context) ||
				     path_in_context[0] == '#')){
		    if(strlen(path_in_context) < (MAILTMPLEN/2)){
			char  tmp[MAILTMPLEN];
			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);
			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));

    return(rs);
}



/*
 * offer screen with list of contexts to select and some sort
 * of descriptions
 */
CONTEXT_S *
context_screen(start, km, edit_config)
    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));
    }

    return(ps_global->context_list);
}




static struct headerentry headents_templ[]={
  {"Nickname  : ",  "Nickname",  h_composer_cntxt_nick, 12, 0, NULL,
   NULL, NULL, NULL, NULL, NULL, NULL,
   1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
  {"Server    : ",  "Server",  h_composer_cntxt_server, 12, 0, NULL,
   NULL, NULL, NULL, NULL, NULL, NULL,
   1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
  {"Path      : ",  "Path",  h_composer_cntxt_path, 12, 0, NULL,
   NULL, NULL, NULL, NULL, NULL, NULL,
   1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
  {"View      : ",  "View",  h_composer_cntxt_view, 12, 0, NULL,
   NULL, NULL, NULL, NULL, NULL, NULL,
   1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
  {NULL, NULL, NO_HELP, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
   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(ps, func, def_nick, def_serv, def_path, def_view)
    struct pine  *ps;
    char	 *func;
    char	 *def_nick, *def_serv, *def_path, *def_view;
{
    int	       editor_result, i, j, quote;
    char       tmp[MAILTMPLEN], stmp[MAILTMPLEN], *nick, *serv, *path, *view,
	      *new_cntxt = NULL;
    PICO       pbuf;
    STORE_S   *msgso;
    NETMBX     mb;

    memset((void *) &pbuf, 0, sizeof(pbuf));
    pbuf.tty_fix       = ttyfix;
    pbuf.showmsg       = display_message_for_pico;
    pbuf.newmail       = new_mail_for_pico;
    pbuf.keybinput     = cmd_input_for_pico;
    pbuf.ckptdir       = checkpoint_dir_for_pico;
    pbuf.exittest      = exit_collection_add;
    pbuf.canceltest    = cancel_collection_add;
    pbuf.resize	       = resize_for_pico;
    pbuf.winch_cleanup = winch_cleanup;
    pbuf.suspend       = do_suspend;
    pbuf.helper        = helper;
    pbuf.fillcolumn    = ps_global->composer_fillcol;
    pbuf.menu_rows     = FOOTER_ROWS(ps_global) - 1;
    pbuf.ins_help      = h_composer_ins;
    pbuf.ins_m_help    = h_composer_ins_m;
    pbuf.search_help   = h_composer_search;
    pbuf.browse_help   = h_composer_browse;
    pbuf.attach_help   = h_composer_ctrl_j;
    pbuf.composer_help = h_composer;
    sprintf(tmp, "FOLDER COLLECTION %s", func);
    pbuf.pine_anchor   = set_titlebar(tmp, ps_global->mail_stream,
				      ps_global->context_current,
				      ps_global->cur_folder,ps_global->msgmap, 
				      0, FolderName, 0, 0);
    pbuf.pine_version  = pine_version;
    pbuf.pine_flags    = flags_for_pico(ps_global);
    pbuf.colors        = colors_for_pico();
    pbuf.pine_flags   |= P_NOBODY;

    /* An informational message */
    if(msgso = so_get(PicoText, NULL, EDIT_ACCESS)){
	pbuf.msgtext = (void *) so_text(msgso);
	so_puts(msgso,
       "\n   Fill in the fields above to create a new Folder Collection.");
	so_puts(msgso,
       "\n   Use \"^G\" command to get help specific help for each item, and");
	so_puts(msgso,
       "\n   use \"^X\" when finished.");
    }


    pbuf.headents = (struct headerentry *)fs_get((sizeof(headents_templ)
						  /sizeof(struct headerentry))
						 * sizeof(struct headerentry));
    memset((void *) pbuf.headents, 0,
	   (sizeof(headents_templ)/sizeof(struct headerentry))
	   * sizeof(struct headerentry));

    for(i = 0; headents_templ[i].prompt; i++)
      pbuf.headents[i] = headents_templ[i];

    nick = cpystr(def_nick ? def_nick : "");
    pbuf.headents[AC_NICK].realaddr = &nick;

    serv = cpystr(def_serv ? def_serv : "");
    pbuf.headents[AC_SERV].realaddr = &serv;

    path = cpystr(def_path ? def_path : "");
    pbuf.headents[AC_PATH].realaddr = &path;
    pbuf.headents[AC_PATH].bldr_private = (void *) 0;

    view = cpystr(def_view ? def_view : "");
    pbuf.headents[AC_VIEW].realaddr = &view;

    /*
     * If this is new context, setup to query IMAP server
     * for location of personal namespace.
     */
    if(!(def_nick || def_serv || def_path || def_view)){
	pbuf.headents[AC_SERV].builder	      = build_namespace;
	pbuf.headents[AC_SERV].affected_entry = &pbuf.headents[AC_PATH];
	pbuf.headents[AC_SERV].bldr_private   = (void *) 0;
    }

    /* pass to pico and let user change them */
    editor_result = pico(&pbuf);

    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){
	if(serv && *serv){
	    if(serv[0] == '{'  && serv[strlen(serv)-1] == '}')
	      strcpy(stmp, serv);
	    else
	      sprintf(stmp, "{%s}", serv);

	    if(mail_valid_net_parse(stmp, &mb)){
		if(!struncmp(mb.service, "nntp", 4)){
		    if(!path || strncmp(path, "#news.", 6))
		      strcat(stmp, "#news.");
		}
	    }
	    else
	      panic("Unexpected invalid server");
	}
	else
	  stmp[0] = '\0';

	i = 0;
	if(nick && *nick){
	    if(quote = *nick != '\"' && strpbrk(nick, " \t"))
	      tmp[i++] = '\"';

	    for(j = 0; tmp[i] = nick[j]; i++, j++)
	      ;

	    if(quote)
	      tmp[i++] = '\"';

	    tmp[i++] = ' ';
	}

	if(quote = (strchr(stmp, ' ')
		    || strpbrk(path, " \t")
		    || strpbrk(view, " \t")))
	  tmp[i++] = '\"';

	for(j = 0; tmp[i] = stmp[j]; i++, j++)
	  ;

	for(j = 0; tmp[i] = path[j]; i++, j++)
	  ;

	if(pbuf.headents[AC_PATH].bldr_private != (void *) 0)
	  for(j = 0;
	      tmp[i] = ((char *) pbuf.headents[AC_PATH].bldr_private)[j];
	      i++, j++)
	    ;

	if(view[j = 0] != '[')
	  tmp[i++] = '[';

	for(; tmp[i] = view[j]; i++, j++)
	  ;

	if(j < 2 || view[j - 1] != ']')
	  tmp[i++] = ']';

	if(quote)
	  tmp[i++] = '\"';

	tmp[i] = '\0';

	new_cntxt = cpystr(tmp);
    }

    if(pbuf.colors)
      free_pcolors(&pbuf.colors);

    for(i = 0; headents_templ[i].prompt; i++)
      fs_give((void **) pbuf.headents[i].realaddr);

    if(pbuf.headents[AC_PATH].bldr_private != (void *) 0)
      fs_give(&pbuf.headents[AC_PATH].bldr_private);

    fs_give((void **) &pbuf.headents);

    if(msgso)
      so_give(&msgso);

    return(new_cntxt);
}


/*
 *  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.
 */      
char *
exit_collection_add(he, redraw_pico)
    struct headerentry *he;
    void (*redraw_pico)();
{
    char     prompt[256], tmp[MAILTMPLEN], *server, *path,
	     delim = '\0', *rstr = NULL, *p;
    int	     exists = 0, i;
    void   (*redraw)() = 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] == '}')
	  strcpy(tmp, server);
	else				/* add them */
	  sprintf(tmp, "{%s}", server);

	if(mail_valid_net_parse(tmp, &mb)){ /* news? verify namespace */
	    if(!struncmp(mb.service, "nntp", 4)){
		if(strncmp(path, "#news.", 6))
		  strcat(tmp, "#news.");
	    }
	}
	else
	  rstr = "Invalid Server entry";
    }
    else
      tmp[0] = '\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 existance.  The
	 * second's to make sure the server (and any requested service)
	 * name we were given exists.  It should be handled by the folder
	 * existance test futher below, but it doesn't work with news...
	 */
	strcat(tmp, path);

	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)){
	    if(*path){
		if(tmp[(i = strlen(tmp)) - 1] != delim){
		    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, (debugfile, "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, tmp)) & FEX_ERROR){
	    if(!(rstr = ps_global->last_error))
	      rstr = "Problem testing for directory existance";
	}
	else
	  exists = (i & FEX_ISDIR);

	sprintf(prompt, "Exit%s" ,
		exists
		  ? " and save changes"
		  : ", saving changes and creating Path");
	if(want_to(prompt, 'y', 0, NO_HELP, WT_NORM) == 'y'){
	    if(!exists && !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";
    }

    ps_global->redrawer = redraw;
    return(rstr);
}



/*
 *  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.
 */      
char *
cancel_collection_add(word, redraw_pico)
    char *word;
    void (*redraw_pico)();
{
    char *rstr = NULL;
    void (*redraw)() = ps_global->redrawer;
#define	CCA_PROMPT	\
		"Cancel Add (answering \"Yes\" will abandon any changes made) "

    ps_global->redrawer = redraw_pico;
    fix_windsize(ps_global);
    switch(want_to(CCA_PROMPT, 'y', 'x', NO_HELP, WT_NORM)){
      case 'y':
	rstr = "Add Cancelled";
	break;

      case 'n':
      case 'x':
	break;
    }

    ps_global->redrawer = redraw;
    return(rstr);
}


int
build_namespace(server, server_too, error, barg, mangled)
    char	 *server,
		**server_too,
		**error;
    BUILDER_ARG	 *barg;
    int          *mangled;
{
    char	 *p, *name;
    int		  we_cancel = 0;
    MAILSTREAM	 *stream;
    NAMESPACE  ***namespace;

    dprint(5, (debugfile, "- 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)		/* empty string? */
      while(*p && isspace((unsigned char) *p))
	p++;

    if(p && *p){
	if(server_too)
	  *server_too = cpystr(p);

	name = (char *) fs_get((strlen(p) + 3) * sizeof(char));
	sprintf(name, "{%s}", p);
    }
    else{
	if(server_too)
	  *server_too = cpystr("");

	return(0);
    }

    *mangled = 1;
    fix_windsize(ps_global);
    init_sigwinch();
    clear_cursor_pos();

    we_cancel = busy_alarm(1, "Fetching default directory", NULL, 0);

    if(stream = pine_mail_open(NULL, name, OP_HALFOPEN | OP_SILENT)){
	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);
	}

	mail_close(stream);
    }

    if(we_cancel)
      cancel_busy_alarm(-1);

    fs_give((void **) &name);

    return(1);
}


int
fl_val_gen (f, fs)
    FOLDER_S *f;
    FSTATE_S *fs;
{
    return(f && FLDR_NAME(f));
}


int
fl_val_writable (f, fs)
    FOLDER_S *f;
    FSTATE_S *fs;
{
    return(1);
}


int
fl_val_subscribe (f, fs)
    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(ps, fs)
    struct pine	*ps;
    FSTATE_S	*fs;
{
    int		cmd, fltrv, we_cancel = 0;
    SCROLL_S	sargs;
    HANDLE_S   *handles = NULL;
    STORE_S    *screen_text = NULL;
    FPROC_S	folder_proc_data;
    gf_io_t	pc;

    dprint(1, (debugfile, "\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)){
	    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_alarm(1, "Formatting scroll text", percent_formatted, 0);
	fltrv = folder_list_text(ps, &folder_proc_data, pc, &handles, 
			    ps_global->ttyo->screen_cols);
	if(we_cancel){
	  cancel_busy_alarm(-1);
	  percent.num_done = 0;
	}

	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))
	      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.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)
		  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(cmd = 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(strlen(fp->name) < MAXFOLDER -1)
		      strcpy(fs->first_folder, fp->name);

		    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;
	    }


	    if(F_ON(F_BLANK_KEYMENU,ps_global))
	      FOOTER_ROWS(ps_global) = 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;

    return(folder_proc_data.rv);
}



/*
 * folder_list_text - format collection's contents for display
 */
int
folder_list_text(ps, fp, pc, handlesp, cols)
    struct pine	 *ps;
    FPROC_S	 *fp;
    gf_io_t	  pc;
    HANDLE_S	**handlesp;
    int		  cols;
{
    int	       rv = 1, i, j, ftotal, fcount, slot_size, slot_rows,
	       slot_cols, index, findex, len, shown, selected;
    CONTEXT_S *c_list;

    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{
	/* 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))){
	    /*
	     * 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);
	}

	/* Insert any introductory text here */
	if(c_list->next
	   || c_list->prev
	   || c_list->dir->prev
	   || fp->fs->force_intro){

	    /* Leading vert 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[MAX_SCREEN_COLS + 1];

		sprintf(buf, "%s-Collection <%s>",
			NEWS_TEST(c_list) ? "News" : "Folder",
			strsquish(tmp_20k_buf,
				  (c_list->nickname)
				    ? c_list->nickname
				    : (c_list->label ? c_list->label : ""),
				  cols - 22));
		gf_puts(buf, pc);
		gf_puts("\n", pc);
	    }
	    else if(c_list->label){
		gf_puts(folder_list_center_space(c_list->label, cols), pc);
		gf_puts(c_list->label, pc);
		gf_puts("\n", pc);
	    }

	    if(c_list->comment){
		gf_puts(folder_list_center_space(c_list->comment,cols), pc);
		gf_puts(c_list->comment, pc);
		gf_puts("\n", pc);
	    }

	    if(c_list->dir->desc){
		char buf[MAX_SCREEN_COLS + 1];

		strcpy(buf, strsquish(tmp_20k_buf,c_list->dir->desc,cols-10));
		gf_puts(folder_list_center_space(buf, cols), pc);
		gf_puts(buf, pc);
		gf_puts("\n", pc);
	    }

	    if(c_list->use & CNTXT_ZOOM){
		sprintf(tmp_20k_buf, "[ ZOOMED on %d (of %d) %ss ]",
			selected_folders(c_list),
			folder_total(FOLDERS(c_list)),
			(c_list->use & CNTXT_NEWS) ? "Newsgroup" : "Folder");

		gf_puts(folder_list_center_space(tmp_20k_buf, cols), pc);
		gf_puts(tmp_20k_buf, 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))){
		/* If previously selected, mark members of new list */
		selected = selected_folders(c_list);

		/* How many chars per cell for each folder name? */
		slot_size = 1;
		for(fcount = i = 0; i < ftotal; i++){
		    FOLDER_S *f = folder_entry(i, FOLDERS(c_list));

		    if((c_list->use & CNTXT_ZOOM) && !f->selected)
		      continue;

		    fcount++;

		    /* length of folder name plus blank */
		    len = strlen(FLDR_NAME(f)) + 1;
		    if(f->isdir)
		      len += (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 */
		      len += 4;    
		    if(selected){
			if(F_OFF(F_SELECTED_SHOWN_BOLD, ps_global))
			  len += 4;		/* " X  " */
		    }
		    else if(c_list == fp->fs->list_cntxt)
		      len += 4;			/* "[X] " */

		    if(slot_size < len)
		      slot_size = len;
		}

		if(F_ON(F_SINGLE_FOLDER_LIST, ps_global)){
		    slot_cols = 1;
		    slot_rows = fcount;
		}
		else{
		    switch(slot_cols = (cols / slot_size)){
		      case 0 :
			slot_cols = 1;
		      case 1 :
			slot_rows = fcount;
			break;

		      default :
			while(slot_cols * (slot_size + 1) < cols)
			  slot_size++;

			slot_rows = (fcount / slot_cols)
					      + ((fcount % slot_cols) ? 1 : 0);
			break;
		    }
		}

		percent.total = slot_rows;
		for(i = index = 0; i < slot_rows; i++){
		    percent.num_done = i;
		    if(i)
		      gf_puts("\n", pc);

		    for(j = len = 0; j < slot_cols; j++, index++){
			if(len){
			    gf_puts(repeat_char(slot_size - len, ' '), pc);
			    len = 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);

			    len = folder_list_write(pc, c_list,
						    findex, NULL, flags);
			}
		    }
		}
	    }
	    else if(fp->fs->combined_view
		    && (F_ON(F_CMBND_SUBDIR_DISP, ps_global)
			|| !c_list->dir->prev)){
		static char *emptiness = "[No Folders in Collection]";

		gf_puts(folder_list_center_space(emptiness, cols), pc);
		len = folder_list_write(pc, c_list, -1, emptiness,
					(handlesp) ? FLW_LUNK : FLW_NONE);
	    }
	}
	else if(fp->fs->combined_view
		&& (F_ON(F_CMBND_SUBDIR_DISP, ps_global)
		    || !c_list->dir->prev)){
	    static char *unexpanded = "[Select Here to See Expanded List]";

	    gf_puts(folder_list_center_space(unexpanded, cols), pc);
	    len = folder_list_write(pc, c_list, -1, unexpanded,
				    (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(pc, ctxt, fnum, alt_name, flags)
    gf_io_t    pc;
    CONTEXT_S *ctxt;
    int	       fnum;
    char      *alt_name;
    int	       flags;
{
    char      buf[256];
    int	      l = 0;
    FOLDER_S *fp;
    HANDLE_S *h;

    if(flags & FLW_LUNK){
	h		 = new_handle();
	h->type		 = Folder;
	h->h.f.index	 = fnum;
	h->h.f.context	 = ctxt;
	h->force_display = 1;

	sprintf(buf, "%d", h->key);
    }
    else
      h = NULL;

    fp = (fnum < 0) ? NULL : folder_entry(fnum, FOLDERS(ctxt));

    /* embed handle pointer */
    if((h ? ((*pc)(TAG_EMBED) && (*pc)(TAG_HANDLE)
	     && (*pc)(strlen(buf)) && gf_puts(buf, pc)) : 1)
       && (fp ? ((l = folder_list_write_prefix(fp, flags, pc)) >= 0
		 && gf_puts(FLDR_NAME(fp), pc)
		 && ((fp->isdir && fp->isfolder) ? (*pc)('[') : 1)
		 && ((fp->isdir) ? (*pc)(ctxt->dir->delim) : 1)
		 && ((fp->isdir && fp->isfolder) ? (*pc)(']') : 1))
	      : (alt_name ? gf_puts(alt_name, pc) : 0))
       && (h ? ((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDOFF)
		&& (*pc)(TAG_EMBED) && (*pc)(TAG_INVOFF)) : 1)){
	if(fp){
	    l += strlen(FLDR_NAME(fp));
	    if(fp->isdir)
	      l += (fp->isfolder) ? 3 : 1;
	}
	else if(alt_name)
	  l = strlen(alt_name);
    }

    return(l);
}


int
folder_list_write_prefix(f, flags, pc)
    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;
	gf_puts(f->subscribed ? "SUB " : (f->selected ? "[X] " : "[ ] "), pc);
    }

    return(rv);
}



int
folder_list_ith(n, cntxt)
    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(s, width)
    char *s;
    int   width;
{
    int l;

    return(((l = strlen(s)) < width) ? repeat_char((width - l)/2, ' ') : "");
}



/*
 * folder_list_handle - return pointer in handle list
 *			corresponding to "start"
 */
HANDLE_S *
folder_list_handle(fs, handles)
    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
		     && !strcmp(name ? name : fs->first_folder,
				FLDR_NAME(fp)))){
		  h_found = h;
		  break;
	      }
	  }

	fs->first_folder[0] = '\0';
    }

    return(h_found ? h_found : handles);
}



int
folder_processor(cmd, msgmap, sparms)
    int	      cmd;
    MSGNO_S  *msgmap;
    SCROLL_S *sparms;
{
    int	      rv = 0, i, j;
    HANDLE_S *h;

    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 = "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;


	/*--------- 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 :
	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[MAXFOLDER+1];
	    int	       r;

	    if(NEWS_TEST(cntxt))
	      r = group_subscription(new_file, cntxt);
	    else
	      r = add_new_folder(cntxt, new_file);

	    if(r && context_isambig(new_file)){
		rv = 1;			/* rebuild display! */
		FPROC(sparms)->fs->context = cntxt;
		if(strlen(new_file) < MAXFOLDER - 1)
		  strcpy(FPROC(sparms)->fs->first_folder, new_file);
	    }
	    else
	      ps_global->mangled_footer++;
	}

      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);

	    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)
		      strcpy(FPROC(sparms)->fs->first_folder, new_file);
		}
	    }

	    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)){

		/* repaint, placing cursor on adjacent folder! */
		rv = 1;
		if(next_folder[0] && strlen(next_folder) < MAXFOLDER - 1){
		  strcpy(FPROC(sparms)->fs->first_folder, next_folder);
		}
		else
		  sparms->text.handles->h.f.context->use &= ~CNTXT_ZOOM;
	    }
	    else
	      ps_global->mangled_footer++;
	}

	break;


	/*-------------- Goto Folder Prompt --------------------*/
      case MC_GOTO :
      {
	  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, &c);

	  if(new_fold && do_broach_folder(new_fold, c) > 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);
      }

      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))){
		    if(strlen(FLDR_NAME(fp)) < MAXFOLDER - 1){
			strcpy(FPROC(sparms)->fs->first_folder, FLDR_NAME(fp));
			FPROC(sparms)->fs->first_dir = fp->isdir;
		    }
		}
	    }
	}
	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)){
		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)
		  strcpy(FPROC(sparms)->fs->first_folder, FLDR_NAME(fp));
	    }
	    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;
  
            /*--------------- Invalid Command --------------*/
      default: 
	q_status_message1(SM_ORDER, 0, 2, "MIKE:  fix cmd = %x", (void *) cmd);
	break;
    }

    return(rv);
}


int
folder_lister_clickclick(sparms)
     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(sparms)
    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)){
		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();
			    (*slp)->name = folder_lister_fullname(
				FPROC(sparms)->fs, FLDR_NAME(fp));

			    slp = &(*slp)->next;
			}
			else{
			    free_strlist(&sl);
			    break;
			}
		    }
		}

		if(FPROC(sparms)->rv = sl)
		  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(sparms, cntxt, index)
    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)->rv = new_strlist();
	FPROC(sparms)->rv->name = folder_lister_fullname(FPROC(sparms)->fs,
							 FLDR_NAME(f));
	FPROC(sparms)->done = rv = 1;
    }

    return(rv);
}


void
folder_lister_km_manager(sparms, handle_hidden)
    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[KM_COL_KEY].bind.ch[0] = 'e';
	sparms->keys.menu->keys[KM_COL_KEY].label      = "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].bind.ch[0] = 'e';
	sparms->keys.menu->keys[KM_COL_KEY].label      = "ClctnList";
	sparms->keys.menu->keys[KM_COL_KEY].bind.cmd   = MC_EXIT;
    }
    else{
	/*
	 * turn off "Main Menu" in keymenu and protect
	 * from rebinding below
	 */
	clrbitn(KM_MAIN_KEY, sparms->keys.bitmap);
	sparms->keys.menu->keys[KM_MAIN_KEY].bind.cmd = MC_NONE;
	sparms->keys.menu->keys[KM_MAIN_KEY].bind.nch = 0;

	sparms->keys.menu->keys[KM_COL_KEY].label      = "Main Menu";
	sparms->keys.menu->keys[KM_COL_KEY].bind.cmd   = MC_MAIN;
	sparms->keys.menu->keys[KM_COL_KEY].bind.ch[0] = 'm';
    }

    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){
	    if(fp->isfolder){
		sparms->keys.menu->keys[KM_SEL_KEY].label = "View Dir";
		menu_clear_binding(sparms->keys.menu, 'v');
		menu_clear_binding(sparms->keys.menu, ctrl('M'));
		menu_clear_binding(sparms->keys.menu, ctrl('J'));
		menu_add_binding(sparms->keys.menu, 'v', MC_OPENFLDR);
		menu_add_binding(sparms->keys.menu, ctrl('M'), MC_OPENFLDR);
		menu_add_binding(sparms->keys.menu, ctrl('J'), MC_OPENFLDR);
		setbitn(KM_ALTVIEW_KEY, sparms->keys.bitmap);
	    }
	    else{
		sparms->keys.menu->keys[KM_SEL_KEY].label = "[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);
		clrbitn(KM_ALTVIEW_KEY, sparms->keys.bitmap);
	    }
	}
	else{
	    sparms->keys.menu->keys[KM_SEL_KEY].label = "[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);
	    clrbitn(KM_ALTVIEW_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 = "[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);
	clrbitn(KM_ALTVIEW_KEY, sparms->keys.bitmap);
    }
    else{
	clrbitn(KM_SEL_KEY, sparms->keys.bitmap);
	clrbitn(KM_ALTVIEW_KEY, sparms->keys.bitmap);
    }
      
    /* May have to "undo" what scrolltool "did" */
    if(F_ON(F_ARROW_NAV, ps_global)){
	int cmd;

	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(sparms, handle_hidden)
    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       = "<";
	sparms->keys.menu->keys[FC_COL_KEY].label      = "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;
	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       = "<";
	sparms->keys.menu->keys[FC_COL_KEY].label      = "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;
	setbitn(FC_EXIT_KEY, sparms->keys.bitmap);
    }
    else if(FPROC(sparms)->fs->combined_view){
	/*
	 * turn off "ExitSelect" in first slot
	 */
	sparms->keys.menu->keys[FC_COL_KEY].name       = "E";
	sparms->keys.menu->keys[FC_COL_KEY].label      = "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';
	clrbitn(FC_EXIT_KEY, sparms->keys.bitmap);
    }

    if(sparms->text.handles
       && (fp = folder_entry(sparms->text.handles->h.f.index,
			     FOLDERS(sparms->text.handles->h.f.context)))
       && fp->isdir){
	sparms->keys.menu->keys[FC_SEL_KEY].name  = ">";
	sparms->keys.menu->keys[FC_SEL_KEY].label = "[View Dir]";
	menu_clear_binding(sparms->keys.menu, 's');
	menu_add_binding(sparms->keys.menu, 'v', MC_CHOICE);
	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);
    }
    else{
	sparms->keys.menu->keys[FC_SEL_KEY].name       = "S";
	sparms->keys.menu->keys[FC_SEL_KEY].label      = "[Select]";
	menu_clear_binding(sparms->keys.menu, 'v');
	menu_clear_binding(sparms->keys.menu, '>');
	menu_clear_binding(sparms->keys.menu, '.');
	menu_add_binding(sparms->keys.menu, 's', MC_CHOICE);
	if(F_ON(F_ARROW_NAV,ps_global))
	  menu_add_binding(sparms->keys.menu, KEY_RIGHT, MC_CHOICE);
    }

    /* May have to "undo" what scrolltool "did" */
    if(F_ON(F_ARROW_NAV, ps_global)){
	int cmd;

	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(sparms, handle_hidden)
    SCROLL_S *sparms;
    int	      handle_hidden;
{
    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 = "[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 = "[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)){
	int cmd;

	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(ps, context, cur_index)
    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[] = {
	{'a', 'a', "A", "select All"},
	{'c', 'c', "C", "select Cur"},
	{'p', 'p', "P", "Properties"},
	{'t', 't', "T", "Text"},
	{-1, 0, NULL, NULL}
    };
    extern     ESCKEY_S sel_opts1[];
    extern     char *sel_pmt2;

    f = folder_entry(cur_index, FOLDERS(context));

    sel_opts = self_opts2;
    if(old_tot = selected_folders(context)){
	sel_opts1[1].label = "unselect Cur" + (f->selected ? 0 : 2);
	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)))
		f->selected = !f->selected;

	    return(1);			/* repaint */

	  case 'n' :			/* narrow selection */
	    narrow++;
	  case 'b' :			/* broaden selection */
	    q = 0;			/* but don't offer criteria prompt */
	    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(!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;

      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;
    
    if(i < n)					/* any matches at all? */
      for(; i < n; i++)
	if(f = folder_entry(i, FOLDERS(context))){
	    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){
	sprintf(tmp_20k_buf,
		"Select matched %ld 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");
	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
selected_folders(context)
    CONTEXT_S *context;
{
    int	      i, n, total;
    FOLDER_S *fp;

    n = folder_total(FOLDERS(context));
    for(total = i = 0; i < n; i++)
      if(folder_entry(i, FOLDERS(context))->selected)
	total++;

    return(total);
}


SELECTED_S *
new_selected()
{
  SELECTED_S *selp;
  
  selp = (SELECTED_S *)fs_get(sizeof(SELECTED_S));
  selp->sub = NULL;
  selp->reference = NULL;
  selp->folders = NULL;
  selp->zoomed = 0;

  return selp;
}

/*
 *  Free the current selected struct and all of the 
 *  following structs in the list
 */
void
free_selected(selp)
    SELECTED_S **selp;
{
    if(!selp || !(*selp))
      return;
    if((*selp)->sub)
      free_selected(&((*selp)->sub));

    free_strlist(&(*selp)->folders);
    if((*selp)->reference)
      fs_give((void **) &(*selp)->reference);

    fs_give((void **) selp);
}

void
folder_select_preserve(context)
    CONTEXT_S *context;
{
    if(context
       && !(context->use & CNTXT_PARTFIND)){
	FOLDER_S   *fp;
	STRLIST_S **slpp;
	SELECTED_S *selp = &context->selected;
	int	    i, folder_n;

	if(!context->dir->ref){
	  if(!context->selected.folders)
	    slpp     = &context->selected.folders;
	  else
	    return;
	}
	else{
	  if(!selected_folders(context))
	    return;
	  else{
	    while(selp->sub){
	      selp = selp->sub;
	      if(!strcmp(selp->reference, context->dir->ref))
		return;
	    }
	    selp->sub = new_selected();
	    selp = selp->sub;
	    slpp = &(selp->folders);
	  }
	}
	folder_n = folder_total(FOLDERS(context));

	for(i = 0; i < folder_n; i++)
	  if((fp = folder_entry(i, FOLDERS(context)))->selected){
	      *slpp = new_strlist();
	      (*slpp)->name = cpystr(fp->name);
	      slpp = &(*slpp)->next;
	  }

	/* Only remember "ref" if any folders were selected */
	if(selp->folders && context->dir->ref)
	  selp->reference = cpystr(context->dir->ref);

	selp->zoomed = (context->use & CNTXT_ZOOM) != 0;
    }
}



int
folder_select_restore(context)
    CONTEXT_S *context;
{
    int rv = 0;

    if(context
       && !(context->use & CNTXT_PARTFIND)){
	STRLIST_S *slp;
	SELECTED_S *selp, *pselp = NULL;
	int	   i, found = 0;

	selp = &(context->selected);

	if(context->dir->ref){
	  pselp = selp;
	  selp = selp->sub;
	  while(selp && strcmp(selp->reference, context->dir->ref)){
	    pselp = selp;
	    selp = selp->sub;
	  }
	  if (selp)
	    found = 1;
	}
	else 
	  found = selp->folders != 0;
	if(found){
	  for(slp = selp->folders; slp; slp = slp->next)
	    if(slp->name
	       && (i = folder_index(slp->name, context, FI_FOLDER)) >= 0){
		folder_entry(i, FOLDERS(context))->selected = 1;
		rv++;
	    }

	  /* Used, always clean them up */
	  free_strlist(&selp->folders);
	  if(selp->reference)
	    fs_give((void **) &selp->reference);

	  if(selp->zoomed){
	    context->use |= CNTXT_ZOOM;
	    selp->zoomed = 0;
	  }
	  if(!(selp == &context->selected)){
	    if(pselp){
	      pselp->sub = selp->sub;
	      fs_give((void **) &selp);
	    }
	  }
	}
    }

    return(rv);
}



int
folder_lister_select(fs, context, index)
    FSTATE_S  *fs;
    CONTEXT_S *context;
    int	       index;
{
    int       rv = 0;
    char     *p;
    FDIR_S   *fp;
    FOLDER_S  *f = folder_entry(index, FOLDERS(context));

    /*--- Entering a directory?  ---*/
    if(f->isdir){
	fp = next_folder_dir(context, f->name, TRUE);

	/* 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);
}



char *
folder_lister_desc(cntxt, fdp)
    CONTEXT_S *cntxt;
    FDIR_S    *fdp;
{
    char *p;

    /* Provide context in new collection header */
    sprintf(tmp_20k_buf, "Dir: %s",
	    ((p = strstr(cntxt->context, "%s")) && !*(p+2)
	     && !strncmp(fdp->ref, cntxt->context, p - cntxt->context))
	      ? fdp->ref + (p - cntxt->context) : fdp->ref);
    return(cpystr(tmp_20k_buf));
}



int
folder_lister_parent(fs, context, index, force_parent)
    FSTATE_S  *fs;
    CONTEXT_S *context;
    int	       index;
    int	       force_parent;
{
    int       rv = 0;
    FDIR_S   *fp;
    FOLDER_S *f = context ? folder_entry(index, FOLDERS(context)) : NULL;

    if(((force_parent && f->parent) || !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)){
	    *s = '\0';
	    if(s = strrindex(context->dir->ref, context->dir->delim))
	      strcpy(oldir, s+1);
	}

	if(*oldir){
	    /* remember current directory for hiliting in new list */
	    fs->context = context;
	    if(strlen(oldir) < MAXFOLDER - 1){
		strcpy(fs->first_folder, oldir);
		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, 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(fs, name)
    FSTATE_S *fs;
    char     *name;
{
    if(fs->context->dir->status & CNTXT_SUBDIR){
	char tmp[MAILTMPLEN], tmp2[MAILTMPLEN], *p;

	if(fs->context->dir->ref)
	  sprintf(tmp, "%s%s",
		  ((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,
		  name);

	/*
	 * 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, '}')))
		  sprintf(tmp2, "%.*s%s",
			  p - fs->context->context + 1,
			  fs->context->context, tmp);
		else
		  build_path(tmp2, ps_global->ui.homedir, tmp);
	    }
	    else
	      (void) context_apply(tmp2, fs->context, tmp);

	    return(cpystr(tmp2));
	}

	return(cpystr(tmp));
    }

    return(cpystr(name));
}



/*
 *
 */
void
reset_context_folders(cntxt)
    CONTEXT_S *cntxt;
{
    CONTEXT_S *tc;

    for(tc = cntxt; tc && tc->prev ; tc = tc->prev)
      ;				/* start at beginning */

    for( ; tc ; tc = tc->next){
	free_folder_list(tc);

	while(tc->dir->prev){
	    FDIR_S *tp = tc->dir->prev;
	    free_fdir(&tc->dir, 0);
	    tc->dir = tp;
	}
    }
}



/*
 * next_folder_dir - return a directory structure with the folders it
 *		     contains
 */
FDIR_S *
next_folder_dir(context, new_dir, build_list)
    CONTEXT_S *context;
    char      *new_dir;
    int	       build_list;
{
    char      tmp[MAILTMPLEN], dir[3];
    FDIR_S   *tmp_fp, *fp;

    fp = (FDIR_S *) fs_get(sizeof(FDIR_S));
    memset(fp, 0, sizeof(FDIR_S));
    (void) context_apply(tmp, context, new_dir);
    dir[0] = context->dir->delim;
    dir[1] = '\0';
    strcat(tmp, dir);
    fp->ref	      = cpystr(tmp);
    fp->delim	      = context->dir->delim;
    fp->view.internal = cpystr(NEWS_TEST(context) ? "*" : "%");
    fp->folders	      = init_folder_entries();
    fp->status	      = CNTXT_NOFIND;
    tmp_fp	      = context->dir;	/* temporarily rebind */
    context->dir      = fp;

    if(build_list)
      build_folder_list(NULL, context, NULL, NULL,
			NEWS_TEST(context) ? BFL_LSUB : BFL_NONE);

    context->dir = tmp_fp;
    return(fp);
}



int
folder_select_toggle(context, index, func)
    CONTEXT_S *context;
    int	       index;
    int      (*func) PROTO((CONTEXT_S *, int));
{
    FOLDER_S *f;

    if(f = folder_entry(index, FOLDERS(context))){
      f->selected = !f->selected;
      return((*func)(context, index));
    }
    return 1;
}



/*
 * mail_cmd_stream - return a stream suitable for mail_lsub,
 *		      mail_subscribe, and mail_unsubscribe
 *
 */
MAILSTREAM *
mail_cmd_stream(context, closeit)
    CONTEXT_S *context;
    int	      *closeit;
{
    MAILSTREAM *stream;
    char	tmp[MAILTMPLEN];

    *closeit = 1;
    (void) context_apply(tmp, context, "x");

    return(pine_mail_open(NULL, tmp, OP_HALFOPEN | OP_SILENT));
}



/*----------------------------------------------------------------------
      Create a new folder

   Args: add_folder  -- Folder name added
         folder_list -- The current list of folders

 Result: returns the name of the folder created

  ----*/
int
add_new_folder(context, add_folder)
    CONTEXT_S *context;
    char      *add_folder;
{
    char	 tmp[max(MAXFOLDER,MAX_SCREEN_COLS)+1], nickname[32], 
		*p = NULL, *return_val = NULL;
    HelpType     help;
    int          rc, offset, exists, cnt = 0, isdir = 0;
    MAILSTREAM  *create_stream;
    FOLDER_S    *f;
    static ESCKEY_S add_key[] = {{ctrl('X'),12,"^X", NULL},
				 {-1, 0, NULL, NULL}};

    dprint(4, (debugfile, "\n - add_new_folder - \n"));
    
    add_folder[0] = '\0';
    nickname[0]   = '\0';
    if(context->use & CNTXT_INCMNG){
	char inbox_host[MAXPATH], *beg, *end = NULL;
	ESCKEY_S *special_key;
	static ESCKEY_S host_key[] = {{ctrl('X'),12,"^X","Use Inbox Host"},
				      {-1, 0, NULL, NULL}};

	if(ps_global->readonly_pinerc){
	    q_status_message(SM_ORDER,3,5,
		"Addition cancelled: config file not editable");
	    return(FALSE);
	}

	/*
	 * Prompt for the full pathname (with possible "news" subcommand),
	 * then fall thru to prompt for foldername, then prompt for 
	 * nick name.
	 * NOTE : Don't put it in a "[<default]" prompt since we 
	 * need a way to chose a local folder!
	 */
	inbox_host[0] = '\0';
	if((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';
	    special_key = host_key;
	}
	else
	  special_key = NULL;

	sprintf(tmp, "Name of server to contain added folder : ");
	help = NO_HELP;
	while(1){
	    int flags = OE_APPEND_CURRENT;

	    rc = optionally_enter(add_folder, -FOOTER_ROWS(ps_global), 0,
				  MAXFOLDER, tmp, special_key, help, 0);
	    removing_trailing_white_space(add_folder);
	    removing_leading_white_space(add_folder);
	    if(rc == 3){
		help = help == NO_HELP ? h_incoming_add_folder_host : NO_HELP;
	    }
	    else if(rc == 12){
		strcpy(add_folder, inbox_host);
		break;
	    }
	    else if(rc == 1){
		q_status_message(SM_ORDER,0,2,
		    "Addition of new folder cancelled");
		return(FALSE);
	    }
	    else if(rc == 0)
	      break;
	}
    }

    if(offset = strlen(add_folder)){		/* must be host for incoming */
	int i;
	sprintf(tmp, "Folder on \"%s\" to add : ", add_folder);
	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
      sprintf(tmp, "Folder name to add : ");

    help = NO_HELP;
    while(1){
	int flags;

	p = NULL;
	if(isdir){
	    add_key[0].label = "Create Folder";
	    if(tmp[0] == 'F')
	      rplstr(tmp, 6, "Directory");
	}
	else{
	    add_key[0].label = "Create Directory";
	    if(tmp[0] == 'D')
	      rplstr(tmp, 9, "Folder");
	}

	flags = OE_APPEND_CURRENT;
        rc = optionally_enter(&add_folder[offset], -FOOTER_ROWS(ps_global), 0, 
			      MAXFOLDER - offset, tmp,
			      (context->dir->delim) ? add_key : NULL,
			      help, &flags);
	removing_trailing_white_space(&add_folder[offset]);
	removing_leading_white_space(&add_folder[offset]);
        if(rc == 0 && add_folder[offset]){
	    if(!ps_global->show_dot_names && 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",
		      feature_list_name(F_ENABLE_DOT_FOLDERS));

                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';
		}
	    }
	    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;
	    }

	    if(strucmp(ps_global->inbox_name, nickname))
	      break;
	    else
	      Writechar(BELL, 0);
	}

	if(rc == 12){
	    isdir = !isdir;		/* toggle directory creation */
	}
        else if(rc == 3){
	    help = (help == NO_HELP)
			? ((context->use & CNTXT_INCMNG)
			    ? h_incoming_add_folder_name
			    : h_oe_foldadd)
			: NO_HELP;
	}
	else if(rc == 1 || add_folder[0] == '\0') {
	    q_status_message(SM_ORDER,0,2, "Addition of new folder cancelled");
	    return(FALSE);
	}
    }

    if(*add_folder == '{'			/* remote? */
       || (*add_folder == '*' && *(add_folder+1) == '{')){
	create_stream = context_same_stream(context, add_folder,
					    ps_global->mail_stream);
	    if(!create_stream 
	       && ps_global->mail_stream != ps_global->inbox_stream)
	      create_stream = context_same_stream(context, add_folder,
						  ps_global->inbox_stream);
    }
    else
      create_stream = NULL;

    help = NO_HELP;
    if(context->use & CNTXT_INCMNG){
	sprintf(tmp, "Nickname for folder \"%s\" : ", &add_folder[offset]);
	while(1){
	    int flags = OE_APPEND_CURRENT;

	    rc = optionally_enter(nickname, -FOOTER_ROWS(ps_global), 0, 31,
				  tmp, NULL, help, &flags);
	    removing_leading_white_space(nickname);
	    removing_trailing_white_space(nickname);
	    if(rc == 0){
		if(strucmp(ps_global->inbox_name, nickname))
		  break;
		else
		  Writechar(BELL, 0);
	    }

	    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,
		    "Addition of new folder cancelled");
		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);
		return(FALSE);
	    }
	}

	exists = folder_exists(context, add_folder);
    }
    else
      exists = FEX_NOENT;

    if(exists == FEX_ERROR
       || (exists == FEX_NOENT
	   && !context_create(context, create_stream, add_folder)
	   && !((context->use & CNTXT_INCMNG)
		&& !context_isambig(add_folder))))
      return(FALSE);		/* c-client should've reported error */

    if(isdir && p)				/* whack off trailing delim */
      *p = '\0';

    if(context->use & CNTXT_INCMNG){
	f = new_folder(add_folder);
	if(nickname[0]){
	    f->nickname = cpystr(nickname);
	    f->name_len = strlen(f->nickname);
	}

	folder_insert(folder_total(FOLDERS(context)), f, FOLDERS(context));
	if(!ps_global->USR_INCOMING_FOLDERS){
	    offset = 0;
	    ps_global->USR_INCOMING_FOLDERS =
					     (char **)fs_get(2*sizeof(char *));
	}
	else{
	    for(offset=0;  ps_global->USR_INCOMING_FOLDERS[offset]; offset++)
	      ;

	    fs_resize((void **)&(ps_global->USR_INCOMING_FOLDERS),
		      (offset + 2) * sizeof(char *));
	}

	sprintf(tmp, "%s%s%s%s%s", nickname[0] ? "\"" : "",
		nickname[0] ? nickname : "", nickname[0] ? "\"" : "",
		nickname[0] ? " " : "", add_folder);
	ps_global->USR_INCOMING_FOLDERS[offset]   = cpystr(tmp);
	ps_global->USR_INCOMING_FOLDERS[offset+1] = NULL;
	set_current_val(&ps_global->vars[V_INCOMING_FOLDERS], TRUE, FALSE);
	write_pinerc(ps_global);		/* save new element */

	if(nickname[0])
	  strcpy(add_folder, nickname);		/* known by new name */

	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);

    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(folder, cntxt)
     char      *folder;
     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, *tc;
    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 = "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 = "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, MAXFOLDER,
			      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_alarm(1, "Fetching newsgroup list", NULL, 0);

	    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, &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(i = strlen(folder)){
		char tmp[MAILTMPLEN];

		sprintf(tmp, "%s%s*", (rc == 11) ? "" : "*", folder);
		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))){
		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 */
			strcpy(folder, f);
			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_alarm(-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;

		strcpy(folder, (char *) folders->name);
		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{
	if(folders){		/*------ Actually do the subscription -----*/
	    STRLIST_S *flp;
	    int	       n = 0, errors = 0;

	    /* subscribe one at a time */
	    folder[0] = '\0';
	    for(flp = folders; flp; flp = flp->next){
		(void) context_apply(tmp_20k_buf, &subscribe_cntxt,
				     (char *) flp->name);
		if(mail_subscribe(NULL, 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])
		      strcpy(folder, (char *) flp->name);

		    /*---- 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),
				      FOLDERS(cntxt));
		    }
		}
	    }

	    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{
	    (void) context_apply(tmp_20k_buf, &subscribe_cntxt, folder);
	    if(mail_subscribe(NULL, tmp_20k_buf) == 0L){
		q_status_message1(SM_ORDER | SM_DING, 3, 3,
				  "Error subscribing to \"%s\"", folder);
	    }
	    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), FOLDERS(cntxt));
	    }
	}

	if(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_file   -- buffer to contain new file name
         index      -- index of folder in folder list to rename
         cntxt      -- collection of folders making up folder list

 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, index, new_name)
    CONTEXT_S *context;
    int	       index;
    char      *new_name;
{
    char        *folder, prompt[64], *name_p = NULL;
    HelpType     help;
    FOLDER_S	*new_f;
    int          rc, ren_cur, cnt = 0, isdir = 0;
    MAILSTREAM  *ren_stream = NULL;

    dprint(4, (debugfile, "\n - rename folder -\n"));

    if(NEWS_TEST(context)){
	q_status_message(SM_ORDER | SM_DING, 3, 3,
			 "Can't rename bulletin boards or 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)))
	    && (!strucmp(FLDR_NAME(new_f), ps_global->inbox_name)
		|| new_f->parent)) {
        q_status_message1(SM_ORDER | SM_DING, 3, 4,
			  "Can't change special folder name \"%s\"",
			  new_f->parent
			    ? new_f->nickname
			    : ps_global->inbox_name);
        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;

    sprintf(prompt, "Rename %s to : ",
	    (context->use & CNTXT_INCMNG)
	      ? "nickname"
	      : (isdir = new_f->isdir)
		  ? "directory" : "folder");
    help   = NO_HELP;
    strcpy(new_name, folder);
    while(1) {
	int flags = OE_APPEND_CURRENT;

        rc = optionally_enter(new_name, -FOOTER_ROWS(ps_global), 0,
			      MAXFOLDER, prompt, NULL, help, &flags);
        if(rc == 3) {
            help = help == NO_HELP ? h_oe_foldrename : NO_HELP;
            continue;
        }

	removing_trailing_white_space(new_name);
	removing_leading_white_space(new_name);

        if(rc == 0 && (*new_name || (context->use & CNTXT_INCMNG))) {
	    /* verify characters */
	    if(!ps_global->show_dot_names && *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",
		      feature_list_name(F_ENABLE_DOT_FOLDERS));

                display_message(NO_OP_COMMAND);
                continue;
	    }

	    if(folder_index(new_name, context, FI_ANY) >= 0){
                q_status_message1(SM_ORDER, 3, 3,
				  "Folder \"%s\" already exists",
                                  pretty_fn(new_name));
                display_message(NO_OP_COMMAND);
                continue;
            }
	    else if(!strucmp(new_name, ps_global->inbox_name)){
                q_status_message1(SM_ORDER, 3, 3, "Can't rename folder to %s",
				  ps_global->inbox_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;
	int   i;

	for(i = 0; ps_global->USR_INCOMING_FOLDERS[i]; i++)
	  ;

	new_list = (char **) fs_get((i + 1) * sizeof(char *));

	for(lp = new_list, i = 0; ps_global->USR_INCOMING_FOLDERS[i]; i++)
	  if(i == index - 1){
	      FOLDER_S *fp;
	      char     *folder_string = NULL, *nickname = NULL;

	      fp = folder_entry(index, FOLDERS(context));
	      if(fp->nickname)
		fs_give((void **) &fp->nickname);

	      if(*new_name)
		fp->nickname = cpystr(new_name);

	      /*
	       * Parse folder line for nickname and folder name.
	       * No nickname on line is OK.
	       */
	      get_pair(ps_global->VAR_INCOMING_FOLDERS[i],
		       &nickname, &folder_string, 0);

	      if(nickname)
		fs_give((void **)&nickname);

	      *lp = (char *)fs_get((strlen(new_name)
				    + strlen(folder_string)
				    + 4)
				   * sizeof(char));
	      
	      sprintf(*lp++, "%s%s%s%s", *new_name ? "\"" : "", new_name,
		      *new_name ? "\" " : "", folder_string);
	  }
	  else
	    *lp++ = cpystr(ps_global->USR_INCOMING_FOLDERS[i]);

	*lp = NULL;

	set_variable_list(V_INCOMING_FOLDERS, new_list, TRUE);

	return(1);
    }

    if(ren_cur && ps_global->mail_stream) {
        pine_close_stream(ps_global->mail_stream);
	ps_global->mail_stream = NULL;
    }

    ren_stream = context_same_stream(context, new_name,
				     ps_global->mail_stream);

    if(!ren_stream && ps_global->mail_stream != ps_global->inbox_stream)
      ren_stream = context_same_stream(context, new_name,
				       ps_global->inbox_stream);
      
    if(rc = context_rename(context, ren_stream, folder, new_name)){
	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);
	    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(2, (debugfile, "Error creating \"%s\" in %s context\n",
			   folder, 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) {
        /* No reopen the folder we just had open */
        do_broach_folder(new_name, context);
    }

    return(rc);
}



/*----------------------------------------------------------------------
   Confirm and delete a folder

   Args: fs -- folder screen state

 Result: return 0 if not delete, 1 if deleted.

 NOTE: Currently disallows deleting open folder...
  ----*/
int
delete_folder(context, index, next_folder)
    CONTEXT_S *context;
    int	       index;
    char      *next_folder;
{
    char       *folder, ques_buf[MAX_SCREEN_COLS+1],
	       *fnamep, fname[MAILTMPLEN];
    MAILSTREAM *del_stream;
    FOLDER_S   *fp;
    int         ret, close_opened = 0, blast_folder = 1;

    if(NEWS_TEST(context)){
	static char fmt[] = "Really unsubscribe from \"%.*s\"";
         
        folder = folder_entry(index, FOLDERS(context))->name;
	/* 4 is strlen("%.*s") */
        sprintf(ques_buf, fmt, sizeof(ques_buf) - (sizeof(fmt)-4), folder);
    
        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, (debugfile, "deleting folder \"%s\" in context \"%s\"\n",
	       folder, context->context));

	(void) context_apply(tmp_20k_buf, context, folder);
	if(!mail_unsubscribe(NULL, tmp_20k_buf)){
            q_status_message1(SM_ORDER | SM_DING, 3, 3,
			      "Error unsubscribing from \"%s\"", folder);
            return(0);
        }

	/*
	 * Fix  up the displayed list
	 */
	folder_delete(index, FOLDERS(context));
	return(1);
    }

    fp     = folder_entry(index, FOLDERS(context));
    folder = FLDR_NAME(fp);
    dprint(4, (debugfile, "=== delete_folder(%s) ===\n", folder));

    if(ps_global->readonly_pinerc && (context->use & CNTXT_INCMNG)){
	q_status_message(SM_ORDER,3,5,
			 "Deletion cancelled: config file not editable");
	return(0);
    }
    else if(strucmp(folder, ps_global->inbox_name) == 0 || fp->parent) {
	q_status_message1(SM_ORDER | SM_DING, 3, 4,
		 "Can't delete special folder \"%s\".", ps_global->inbox_name);
	return(0);
    }
    else if(context == ps_global->context_current
	    && strcmp(folder, ps_global->cur_folder) == 0){
	close_opened++;
    }
    else if(fp->isdir){		/* NO DELETE if directory isn't EMPTY */
	FDIR_S *fdirp = next_folder_dir(context, folder, TRUE);

	ret = folder_total(fdirp->folders) > 0;
	free_fdir(&fdirp, 1);

	if(ret){
	    q_status_message1(SM_ORDER | SM_DING, 3, 4,
			      "Can't delete non-empty directory \"%s\".",
			      folder);
	    return(0);
	}

	/*
	 * Folder by the same name exist?  If so, server's probably going
	 * to delete it as well.  Punt?
	 */
	if(folder_index(folder, context, FI_FOLDER) >= 0
	   && (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", "Nickname only"},
	    {'b', 'b', "B", "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{
	sprintf(ques_buf, "DELETE \"%s\"%s", folder, 
		close_opened ? " (the currently open folder)"
			     : fp->isdir ? " (a directory)" : "");

	if((ret = want_to(ques_buf, 'n', 'x', NO_HELP, WT_NORM)) != 'y'){
	    q_status_message(SM_ORDER,0,3, (ret == 'x') ? "Delete cancelled" 
			     : "No folder deleted");
	    return(0);
	}
    }

    if(blast_folder){
	dprint(2, (debugfile,"deleting folder \"%s\" (%s) in context \"%s\"\n",
		   fp->name,fp->nickname ? fp->nickname : "",
		   context->context));
	/*
	 * Use fp->name since "folder" may be a nickname...
	 */
	if(close_opened){
	    /*
	     * There *better* be a stream, but check just in case.  Then
	     * close it, NULL the pointer, and let do_broach_folder fixup
	     * the rest...
	     */
	    if(ps_global->mail_stream){
		pine_close_stream(ps_global->mail_stream);
		ps_global->mail_stream = NULL;
		ps_global->mangled_header = 1;
		do_broach_folder(ps_global->inbox_name,
				 ps_global->context_list);
	    }

	    del_stream = NULL;
	}
	else
	  del_stream = context_same_stream(context, fp->name,
					   ps_global->mail_stream);

	if(!del_stream && ps_global->mail_stream != ps_global->inbox_stream)
	  del_stream = context_same_stream(context, fp->name,
					   ps_global->inbox_stream);

	if(!fp->isfolder)
	  sprintf(fnamep = fname, "%s%c", fp->name, context->dir->delim);
	else
	  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);
	}
    }

    q_status_message2(SM_ORDER, 0, 3, "%s \"%s\" deleted.",
		      blast_folder ? "Folder" : "Nickname", folder);


    if(context->use & CNTXT_INCMNG){
	char **new_list, **lp, *p;
	int   i;

	for(i = 0; ps_global->USR_INCOMING_FOLDERS[i]; i++)
	  ;

	new_list = (char **) fs_get((i + 1) * sizeof(char *));

	for(lp = new_list, i = 0; ps_global->USR_INCOMING_FOLDERS[i]; i++)
	  if(i != index - 1)
	    *lp++ = cpystr(ps_global->USR_INCOMING_FOLDERS[i]);

	*lp = NULL;

	set_variable_list(V_INCOMING_FOLDERS, new_list, TRUE);
	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)) < MAXFOLDER - 1)
	  strcpy(next_folder, FLDR_NAME(fp));
    }

    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(fp)
    FPROC_S  *fp;
{
    int	       row, index, ftotal, n, order = 0;
    char       buf[MAILTMPLEN], *bp;
    FOLDER_S  *f;

    if(open_printer("folder list ") == 0){
	(void) folder_list_text(ps_global, fp, print_char, NULL, 80);

	close_printer();
    }
}
                     

int
scan_get_pattern(kind, pat, len)
    char *kind, *pat;
    int   len;
{
    char prompt[256];
    int  flags;

    pat[0] = '\0';
    sprintf(prompt, "String in folder %s to match : ", kind);

    while(1){
	flags = OE_APPEND_CURRENT | OE_DISALLOW_HELP;
	switch(optionally_enter(pat, -FOOTER_ROWS(ps_global), 0, len - 1,
				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(ps, context, selected)
    struct pine *ps;
    CONTEXT_S	*context;
    int		 selected;
{
    char	     pattern[MAILTMPLEN], type = '\0';
    static ESCKEY_S  scan_opts[] = {
	{'n', 'n', "N", "Name Select"},
	{'c', 'c', "C", "Content Select"},
	{-1, 0, NULL, NULL}
    };

    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, MAILTMPLEN))
	  type = 'n';

	break;

      case 'c' :				/* content search */
	if(scan_get_pattern("CONTENTS", pattern, MAILTMPLEN))
	  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'){
	    context_apply(tmp, context, "xxx");
	    if(!(args.stream = same_stream(tmp, ps_global->mail_stream))
	       && ps_global->inbox_stream != ps_global->mail_stream
	       && !(args.stream = same_stream(tmp, ps_global->inbox_stream))){
		args.stream    = pine_mail_open(NULL, tmp,
						OP_SILENT | OP_HALFOPEN);
		args.newstream = 1;
	    }
	}

	rv = foreach_folder(context, selected,
			    foreach_do_scan, (void *) &args);

	if(args.newstream)
	  mail_close(args.stream);

	if(rv)
	  return(1);
    }

    cmd_cancelled("Select");
    return(0);
}


int
foreach_do_scan(f, d)
    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(stream, context, f, pattern)
    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];

    if(!strucmp(folder = f->name, ps_global->inbox_name))
      return(FEX_ISFILE);

    sprintf(tmp, "Scanning \"%.*s\"", 40, FLDR_NAME(f));
    we_cancel = busy_alarm(1, tmp, NULL, 0);

    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")){
	    if(!*(p+2)){
		sprintf(tmp, "%.*s", p - context->context, context->context);
		ref = tmp;
	    }
	    else{
		sprintf(tmp, context->context, folder);
		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_alarm(-1);

    return(((response.isfile) ? FEX_ISFILE : 0)
	   | ((response.isdir) ? FEX_ISDIR : 0));
}


/*----------------------------------------------------------------------
  

    ----*/
void
mail_list_response(stream, mailbox, delim, attribs, data)
    MAILSTREAM *stream;
    char       *mailbox;
    int		delim;
    long	attribs;
    void       *data;
{
    if(delim)
      ((LISTRES_S *) data)->delim = delim;

    if(mailbox && *mailbox){
	if(!(attribs & LATT_NOSELECT)){
	    ((LISTRES_S *) data)->isfile  = 1;
	    ((LISTRES_S *) data)->count	 += 1;
	}

	if(!(attribs & LATT_NOINFERIORS)){
	    ((LISTRES_S *) data)->isdir  = 1;
	    ((LISTRES_S *) data)->count += 1;
	}
    }
}



int
folder_select_props(ps, context, selected)
    struct pine *ps;
    CONTEXT_S	*context;
    int		 selected;
{
    int		       cmp = 0;
    long	       flags = 0L, count;
    FOLDER_S	      *fp;
    static ESCKEY_S    prop_opts[] = {
	{'u', 'u', "U", "Unseen msgs"},
	{'n', 'n', "N", "New msgs"},
	{'c', 'c', "C", "msg Count"},
	{-1, 0, NULL, NULL}
    };

    switch(radio_buttons(SEL_PROP_PMT, -FOOTER_ROWS(ps_global),
			 prop_opts, 'n', 'x', NO_HELP, 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;
 
	if(!(args.stream = same_stream(context_apply(tmp, context, "xxx"),
				       ps_global->mail_stream))
	   && ps_global->inbox_stream != ps_global->mail_stream
	   && !(args.stream = same_stream(tmp, ps_global->inbox_stream))){
	    args.stream	   = pine_mail_open(NULL, tmp, OP_SILENT|OP_HALFOPEN);
	    args.newstream = 1;
	}

	rv = foreach_folder(context, selected,
			    foreach_do_stat, (void *) &args);

	if(args.newstream)
	  mail_close(args.stream);

	if(rv)
	  return(1);
    }

    cmd_cancelled("Select");
    return(0);
}


int
folder_select_count(count, cmp)
    long *count;
    int	 *cmp;
{
    int		r, flags, we_cancel = 0;
    char	number[32], prompt[128];
    static char *tense[] = {"EQUAL TO", "LESS THAN", "GREATER THAN"};
    static ESCKEY_S sel_num_opt[] = {
	{ctrl('W'), 14, "^W", "Toggle Comparison"},
	{-1, 0, NULL, NULL}
    };

    *count = 0L;
    while(1){
	flags = OE_APPEND_CURRENT | OE_DISALLOW_HELP;
	sprintf(number, "%ld", *count);
	sprintf(prompt, "Select folders with messages %s : ", tense[*cmp]);
	r = optionally_enter(number, -FOOTER_ROWS(ps_global), 0, 31,
			     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(f, d)
    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_SCREEN_COLS+1];
	MAILSTREAM *stat_stream = NULL;
	extern MAILSTATUS mm_status_result;

	strcat(strncat(strcpy(msg_buf, "Checking "), FLDR_NAME(f), 50),
	       " for recent messages");
	we_cancel = busy_alarm(1, msg_buf, NULL, 1);

	if(!context_status(sa->context, sa->stream, f->name, sa->flags))
	  mm_status_result.flags = 0L;

	if(we_cancel)
	  cancel_busy_alarm(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, selected, test, args)
    CONTEXT_S  *context;
    int		selected;
    int	      (*test) PROTO((FOLDER_S *, void *));
    void       *args;
{
    int		     i, n, rv = 1;
    FOLDER_S	    *fp;

#ifndef	DOS
    intr_handling_on();
#endif

    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;
    }

#ifndef	DOS
    intr_handling_off();
#endif

    return(rv);
}



/*----------------------------------------------------------------------
      compare two names for qsort, case independent

   Args: pointers to strings to compare

 Result: integer result of strcmp of the names.  Uses simple 
         efficiency hack to speed the string comparisons up a bit.

  ----------------------------------------------------------------------*/
int
compare_names(x, y)
    const QSType *x, *y;
{
    char *a = *(char **)x, *b = *(char **)y;
    int r;
#define	CMPI(X,Y)	((X)[0] - (Y)[0])
#define	UCMPI(X,Y)	((isupper((unsigned char)((X)[0]))	\
				? (X)[0] - 'A' + 'a' : (X)[0])	\
			  - (isupper((unsigned char)((Y)[0]))	\
				? (Y)[0] - 'A' + 'a' : (Y)[0]))

    /*---- Inbox always sorts to the top ----*/
    if(UCMPI(a, ps_global->inbox_name) == 0
       && strucmp(a, ps_global->inbox_name) == 0)
      return((CMPI(a, b) == 0 && strcmp(a, b) == 0) ? 0 : -1);
    else if((UCMPI(b, ps_global->inbox_name)) == 0
       && strucmp(b, ps_global->inbox_name) == 0)
      return((CMPI(a, b) == 0 && strcmp(a, b) == 0) ? 0 : 1);
    /*----- The sent-mail folder, is always next ---*/
    else if(CMPI(a, ps_global->VAR_DEFAULT_FCC) == 0
	    && strcmp(a, ps_global->VAR_DEFAULT_FCC) == 0)
      return((CMPI(a, b) == 0 && strcmp(a, b) == 0) ? 0 : -1);
    else if(CMPI(b, ps_global->VAR_DEFAULT_FCC) == 0
	    && strcmp(b, ps_global->VAR_DEFAULT_FCC) == 0)
      return((CMPI(a, b) == 0 && strcmp(a, b) == 0) ? 0 : 1);
    /*----- The saved-messages folder, is always next ---*/
    else if(CMPI(a, ps_global->VAR_DEFAULT_SAVE_FOLDER) == 0
	    && strcmp(a, ps_global->VAR_DEFAULT_SAVE_FOLDER) == 0)
      return((CMPI(a, b) == 0 && strcmp(a, b) == 0) ? 0 : -1);
    else if(CMPI(b, ps_global->VAR_DEFAULT_SAVE_FOLDER) == 0
	    && strcmp(b, ps_global->VAR_DEFAULT_SAVE_FOLDER) == 0)
      return((CMPI(a, b) == 0 && strcmp(a, b) == 0) ? 0 : 1);
    else
      return((r = CMPI(a, b)) ? r : strcmp(a, b));
}



/*----------------------------------------------------------------------
      compare two folder structs for ordering alphabetically

   Args: pointers to folder structs to compare

 Result: integer result of dir-bit and strcmp of the folders.
  ----------------------------------------------------------------------*/
int
compare_folders_alpha(f1, f2)
    FOLDER_S *f1, *f2;
{
    int	  i;
    char *f1name = FLDR_NAME(f1),
	 *f2name = FLDR_NAME(f2);

    return(((i = compare_names(&f1name, &f2name)) != 0)
	       ? i : (f2->isdir - f1->isdir));
}


/*----------------------------------------------------------------------
      compare two folder structs alphabetically with dirs first

   Args: pointers to folder structs to compare

 Result: integer result of dir-bit and strcmp of the folders.
  ----------------------------------------------------------------------*/
int
compare_folders_dir_alpha(f1, f2)
    FOLDER_S *f1, *f2;
{
    int	  i;

    if((i = (f2->isdir - f1->isdir)) == 0){
	char *f1name = FLDR_NAME(f1),
	     *f2name = FLDR_NAME(f2);

	return(compare_names(&f1name, &f2name));
    }

    return(i);
}


/*----------------------------------------------------------------------
      compare two folder structs alphabetically with dirs last

   Args: pointers to folder structs to compare

 Result: integer result of dir-bit and strcmp of the folders.
  ----------------------------------------------------------------------*/
int
compare_folders_alpha_dir(f1, f2)
    FOLDER_S *f1, *f2;
{
    int	  i;

    if((i = (f1->isdir - f2->isdir)) == 0){
	char *f1name = FLDR_NAME(f1),
	     *f2name = FLDR_NAME(f2);

	return(compare_names(&f1name, &f2name));
    }

    return(i);
}




/*----------------------------------------------------------------------
      Format the given folder name for display for the user

   Args: folder -- The folder name to fix up

Not sure this always makes it prettier. It could do nice truncation if we
passed in a length. Right now it adds the path name of the mail 
subdirectory if appropriate.
 ----*/
      
char *
pretty_fn(folder)
     char *folder;
{
    if(ps_global->nr_mode){
	char *p;

	if((p = strindex(folder, '}')) != NULL)
	  return(p + 1);
	else if((p = strindex(folder, '/')) != NULL) 
	  return(p+1);
	else
	  return(folder);
    }
    else if(!strucmp(folder, ps_global->inbox_name))
      return(ps_global->inbox_name);
    else
      return(folder);
}


/*----------------------------------------------------------------------
       Check to see if folder exists in given context

  Args: cntxt -- context inwhich to interpret "file" arg
        file  -- name of folder to check
 
 Result: returns FEX_ISFILE if the folder exists and is a folder
		 FEX_ISDIR  if the folder exists and is a directory
                 FEX_NOENT  if it doesn't exist
		 FEX_ERROR  on error

  The two existance return values above may be logically OR'd

  Uses mail_list to sniff out the existance of the requested folder.
  The context string is just here for convenience.  Checking for
  folder's existance within a given context is probably more efficiently
  handled outside this function for now using build_folder_list().

    ----*/
int
folder_exists(cntxt, file)
    CONTEXT_S *cntxt;
    char      *file;
{
    return(folder_name_exists(cntxt, file, NULL));
}


/*----------------------------------------------------------------------
       Check to see if folder exists in given context

  Args: cntxt -- context inwhich to interpret "file" arg
        file  -- name of folder to check
	name  -- name of folder folder with context applied
 
 Result: returns FEX_ISFILE if the folder exists and is a folder
		 FEX_ISDIR  if the folder exists and is a directory
                 FEX_NOENT  if it doesn't exist
		 FEX_ERROR  on error

  The two existance return values above may be logically OR'd

  Uses mail_list to sniff out the existance of the requested folder.
  The context string is just here for convenience.  Checking for
  folder's existance within a given context is probably more efficiently
  handled outside this function for now using build_folder_list().

    ----*/
int
folder_name_exists(cntxt, file, fullpath)
    CONTEXT_S *cntxt;
    char      *file, **fullpath;
{
    MM_LIST_S    ldata;
    EXISTDATA_S	 parms;
    NETMBX	 mb;
    int          we_cancel = 0;
    char	*p, reference[MAILTMPLEN], tmp[MAILTMPLEN];

    /*
     * No folder means "inbox", and it has to exist, also
     * look explicitly for inbox...
     */
    if(!*file
       || !strucmp(file, ps_global->inbox_name)
       || ((*file == '{' && (p = strchr(file, '}')))
	   && (!*(p+1)
	       || !strucmp(p, ps_global->inbox_name)))){
	file  = ps_global->VAR_INBOX_PATH;
	cntxt = NULL;
    }

    mm_list_info = &ldata;		/* tie down global reference */
    ldata.filter = mail_list_exists;

    if(!(ldata.stream = same_stream(context_apply(tmp, cntxt, file),
				    ps_global->mail_stream))
       && ps_global->inbox_stream != ps_global->mail_stream)
      ldata.stream = same_stream(tmp, ps_global->inbox_stream);

    memset(ldata.data = &parms, 0, sizeof(EXISTDATA_S));

    /*
     * If no preset reference string, must be at top of context
     */
    if(cntxt && context_isambig(file)){
	if(!(parms.args.reference = cntxt->dir->ref)){
	    char *p;

	    if(p = strstr(cntxt->context, "%s")){
		strncpy(parms.args.reference = reference,
			cntxt->context, p - cntxt->context);
		reference[p - cntxt->context] = '\0';
		if(*(p += 2))
		  parms.args.tail = p;
	    }
	    else
	      parms.args.reference = cntxt->context;
	}

	parms.fullname = fullpath;
    }

    ps_global->mm_log_error = 0;
    ps_global->noshow_error = 1;

    we_cancel = busy_alarm(1, NULL, NULL, 0);

    mail_list(ldata.stream, parms.args.reference, parms.args.name = file);

    if(we_cancel)
      cancel_busy_alarm(-1);

    ps_global->noshow_error = 0;

    if(cntxt && cntxt->dir && parms.response.delim)
      cntxt->dir->delim = parms.response.delim;

    return((ps_global->mm_log_error)
	    ? FEX_ERROR
	    : (((parms.response.isfile)
		  ? FEX_ISFILE : 0 )
	       | ((parms.response.isdir)
		  ? FEX_ISDIR : 0)
	       | ((parms.response.ismarked)
		  ? FEX_ISMARKED : 0)
	       | ((parms.response.unmarked)
		  ? FEX_UNMARKED : 0)));
}



/*----------------------------------------------------------------------
  

    ----*/
void
mail_list_exists(stream, mailbox, delim, attribs, data)
    MAILSTREAM *stream;
    char       *mailbox;
    int		delim;
    long	attribs;
    void       *data;
{
    if(delim)
      ((EXISTDATA_S *) data)->response.delim = delim;

    if(mailbox && *mailbox){
	if(!(attribs & LATT_NOSELECT)){
	    ((EXISTDATA_S *) data)->response.isfile  = 1;
	    ((EXISTDATA_S *) data)->response.count  += 1;
	}

	if(!(attribs & LATT_NOINFERIORS)){
	    ((EXISTDATA_S *) data)->response.isdir  = 1;
	    ((EXISTDATA_S *) data)->response.count  += 1;
	}
    
	if(attribs & LATT_MARKED)
	  ((EXISTDATA_S *) data)->response.ismarked = 1;

	if(attribs & LATT_UNMARKED)
	  ((EXISTDATA_S *) data)->response.unmarked = 1;

	if(((EXISTDATA_S *) data)->fullname
	   && ((((EXISTDATA_S *) data)->args.reference[0] != '\0')
	         ? struncmp(((EXISTDATA_S *) data)->args.reference, mailbox,
			    strlen(((EXISTDATA_S *)data)->args.reference))
		 : struncmp(((EXISTDATA_S *) data)->args.name, mailbox,
			    strlen(((EXISTDATA_S *) data)->args.name)))){
	    char   *p;
	    size_t  len = (((stream && stream->mailbox)
			      ? strlen(stream->mailbox) : 0)
			   + strlen(((EXISTDATA_S *) data)->args.reference)
			   + strlen(((EXISTDATA_S *) data)->args.name)
			   + strlen(mailbox)) * sizeof(char);

	    /*
	     * Fully qualify (in the c-client name structure sense)
	     * anything that's not in the context of the "reference"...
	     */
	    if(*((EXISTDATA_S *) data)->fullname)
	      fs_give((void **) ((EXISTDATA_S *) data)->fullname);

	    *((EXISTDATA_S *) data)->fullname = (char *) fs_get(len);
	    if(*mailbox != '{'
	       && stream && stream->mailbox && *stream->mailbox == '{'
	       && (p = strindex(stream->mailbox, '}'))){
		strncpy(*((EXISTDATA_S *) data)->fullname,
			stream->mailbox, len = (p - stream->mailbox) + 1);
		p = *((EXISTDATA_S *) data)->fullname + len;
	    }
	    else
	      p = *((EXISTDATA_S *) data)->fullname;

	    strcpy(p, mailbox);
	}
    }
}



/*----------------------------------------------------------------------
  Return the path delimiter for the given folder on the given server

  Args: folder -- folder type for delimiter

    ----*/
int
folder_delimiter(folder)
    char *folder;
{
    MM_LIST_S    ldata;
    LISTRES_S	 response;
    int          ourstream = 0, we_cancel = 0;

    memset(mm_list_info = &ldata, 0, sizeof(MM_LIST_S));
    ldata.filter = mail_list_response;
    memset(ldata.data = &response, 0, sizeof(LISTRES_S));

    we_cancel = busy_alarm(1, NULL, NULL, 0);

    if(*folder == '{'
       && !(ldata.stream = same_stream(folder, ps_global->mail_stream))
       && !(ldata.stream = same_stream(folder, ps_global->inbox_stream))){
	if(ldata.stream = pine_mail_open(NULL,folder,OP_HALFOPEN|OP_SILENT)){
	    ourstream++;
	}
	else{
	    if(we_cancel)
	      cancel_busy_alarm(-1);

	    return(FEX_ERROR);
	}
    }

    mail_list(ldata.stream, folder, "");

    if(ourstream)
      mail_close(ldata.stream);

    if(we_cancel)
      cancel_busy_alarm(-1);

    return(response.delim);
}


/*
 *
 */
char *
folder_as_breakout(cntxt, name)
    CONTEXT_S *cntxt;
    char      *name;
{
    if(context_isambig(name)){		/* if simple check doesn't pan out */
	char tmp[MAILTMPLEN], *p, *f;	/* look harder */
	int  exists;

	if(!cntxt->dir->delim){
	    (void) context_apply(tmp, cntxt, "");
	    cntxt->dir->delim = folder_delimiter(tmp);
	}

	if(p = strindex(name, cntxt->dir->delim)){
	    if(p == name){		/* assumption 6,321: delim is root */
		if(cntxt->context[0] == '{'
		   && (p = strindex(cntxt->context, '}'))){
		    strncpy(tmp, cntxt->context, (p - cntxt->context) + 1);
		    strcpy(&tmp[(p - cntxt->context) + 1], name);
		    return(cpystr(tmp));
		}
		else
		  return(cpystr(name));
	    }
	    else{			/* assumption 6,322: no create ~foo */
		strncpy(tmp, name, p - name);
		tmp[p - name] = '\0';	/* lop off trailingpath */
		f	      = NULL;
		exists = folder_name_exists(cntxt, tmp, &f);
		if(f){
		    sprintf(tmp, "%s%s", f, p);
		    fs_give((void **) &f);
		    return(cpystr(tmp));
		}
	    }
	}
    }

    return(NULL);
}



/*----------------------------------------------------------------------
  

    ----*/
void
mail_list_delim(stream, mailbox, delim, attribs, data)
    MAILSTREAM *stream; 
    char       *mailbox;
    int		delim;
    long	attribs;
    void       *data;
{
    if(delim)
      ((LISTRES_S *) data)->delim = delim;
}



/*----------------------------------------------------------------------
 Initialize global list of contexts for folder collections.

 Interprets collections defined in the pinerc and orders them for
 pine's use.  Parses user-provided context labels and sets appropriate 
 use flags and the default prototype for that collection. 
 (See find_folders for how the actual folder list is found).

  ----*/
void
init_folders(ps)
    struct pine *ps;
{
    CONTEXT_S  *tc, *top = NULL, **clist;
    FOLDER_S   *f;
    int         i, prime = 0;

    clist = &top;

    /*
     * If no incoming folders are config'd, but the user asked for
     * them via feature, make sure at least "inbox" ends up there...
     */
    if(F_ON(F_ENABLE_INCOMING, ps) && !ps->VAR_INCOMING_FOLDERS){
	ps->VAR_INCOMING_FOLDERS    = (char **)fs_get(2 * sizeof(char *));
	ps->VAR_INCOMING_FOLDERS[0] = cpystr(ps->inbox_name);
	ps->VAR_INCOMING_FOLDERS[1] = NULL;
    }

    /*
     * Build context that's a list of folders the user's defined
     * as receiveing new messages.  At some point, this should
     * probably include adding a prefix with the new message count.
     * fake new context...
     */
    if(ps->VAR_INCOMING_FOLDERS && ps->VAR_INCOMING_FOLDERS[0]
       && (tc = new_context("Incoming-Folders []", NULL))){
	tc->dir->status &= ~CNTXT_NOFIND;
	tc->use    |= CNTXT_INCMNG;	/* mark this as incoming collection */
	if(tc->label)
	  fs_give((void **) &tc->label);

	tc->label = cpystr("Incoming Message Folders");

	*clist = tc;
	clist  = &tc->next;

	for(i = 0; ps->VAR_INCOMING_FOLDERS[i] ; i++){
	    char *folder_string, *nickname;

	    /*
	     * Parse folder line for nickname and folder name.
	     * No nickname on line is OK.
	     */
	    get_pair(ps->VAR_INCOMING_FOLDERS[i], &nickname, &folder_string,0);

	    /*
	     * Allow for inbox to be specified in the incoming list, but
	     * don't let it show up along side the one magically inserted
	     * above!
	     */
	    if(!folder_string || !strucmp(ps->inbox_name, folder_string)){
		if(folder_string)
		  fs_give((void **)&folder_string);

		if(nickname)
		  fs_give((void **)&nickname);

		continue;
	    }
	    else if(update_bboard_spec(folder_string, tmp_20k_buf)){
		fs_give((void **) &folder_string);
		folder_string = cpystr(tmp_20k_buf);
	    }

	    f = new_folder(folder_string);
	    fs_give((void **)&folder_string);
	    if(nickname){
		if(strucmp(ps->inbox_name, nickname)){
		    f->nickname = nickname;
		    f->name_len = strlen(f->nickname);
		}
		else
		  fs_give((void **)&nickname);
	    }

	    folder_insert(f->nickname 
			  && (strucmp(f->nickname, ps->inbox_name) == 0)
				? -1 : folder_total(FOLDERS(tc)),
			  f, FOLDERS(tc));
	}
    }

    /*
     * Build list of folder collections.  Because of the way init.c
     * works, we're guaranteed at least a default.  Also write any
     * "bogus format" messages...
     */
    for(i = 0; ps->VAR_FOLDER_SPEC && ps->VAR_FOLDER_SPEC[i] ; i++)
      if(tc = new_context(ps->VAR_FOLDER_SPEC[i], &prime)){
	  *clist    = tc;			/* add it to list   */
	  clist	    = &tc->next;		/* prepare for next */
	  tc->var.v = &ps->vars[V_FOLDER_SPEC];
	  tc->var.i = i;
      }


    /*
     * Whoah cowboy!!!  Guess we couldn't find a valid folder
     * collection???
     */
    if(!prime)
      panic("No folder collections defined");

    /*
     * At this point, insert the INBOX mapping as the leading
     * folder entry of the first collection...
     */
    init_inbox_mapping(ps->VAR_INBOX_PATH, top);

    /*
     * If no news collections spec'd, but an nntp-server defined, 
     * fake a default news collection.  Check only "user" and "global"
     * nntp_server values as the "current" value is influenced by
     *  env vars and other news config files (see init.c)...
     */
    if(!ps->VAR_NEWS_SPEC && !init_news_in_folders(top)
       && ps->VAR_NNTP_SERVER && ps->VAR_NNTP_SERVER[0]){
	char buf[MAXPATH];

	/*
	 * Also require that there aren't any news collections yet 
	 * defined...
	 */
	for(tc = top; tc && !NEWS_TEST(tc); tc = tc->next)
	  ;

	if(!tc){
	    ps->VAR_NEWS_SPEC    = (char **)fs_get(2 * sizeof(char *));
	    sprintf(buf, "{%s/nntp}#news.[]", ps->VAR_NNTP_SERVER[0]);
	    ps->VAR_NEWS_SPEC[0] = cpystr(buf);
	    ps->VAR_NEWS_SPEC[1] = NULL;
	}
    }

    /*
     * If news groups, loop thru list adding to collection list
     */
    for(i = 0; ps->VAR_NEWS_SPEC && ps->VAR_NEWS_SPEC[i] ; i++)
      if(ps->VAR_NEWS_SPEC[i][0]
	 && (tc = new_context(ps->VAR_NEWS_SPEC[i], NULL))){
	  *clist      = tc;			/* add it to list   */
	  clist       = &tc->next;		/* prepare for next */
	  tc->var.v   = &ps->vars[V_NEWS_SPEC];
	  tc->var.i   = i;
      }

    ps->context_list    = top;	/* init pointers */
    ps->context_current = (top->use & CNTXT_INCMNG) ? top->next : top;
    /* Tie up all the previous pointers */
    for(; top; top = top->next)
      if(top->next)
	top->next->prev = top;

#ifdef	DEBUG
    if(debug > 7 && do_debug(debugfile))
      dump_contexts(debugfile);
#endif
}



int
init_news_in_folders(clist)
    CONTEXT_S *clist;
{
    for(; clist; clist = clist->next)
      if(clist->use & CNTXT_NEWS)
	return(1);

    return(0);
}



#ifdef	DEBUG
void
dump_contexts(fp)
    FILE *fp;
{
    int i = 0;
    CONTEXT_S *c = ps_global->context_list;

    while(fp && c != NULL){
	fprintf(fp, "\n***** context %s\n", c->context);
	if(c->label)
	  fprintf(fp,"LABEL: %s\n", c->label);

	if(c->comment)
	  fprintf(fp,"COMMENT: %s\n", c->comment);
	
	for(i = 0; i < folder_total(FOLDERS(c)); i++)
	  fprintf(fp, "  %d) %s\n", i + 1, folder_entry(i,FOLDERS(c))->name);
	
	c = c->next;
    }
}
#endif


/*
 *  Release resources associated with global context list
 */
void
free_contexts(ctxt)
    CONTEXT_S **ctxt;
{
    if(*ctxt){
	free_contexts(&(*ctxt)->next);
	free_context(ctxt);
    }
}


/*
 *  Release resources associated with the given context structure
 */
void
free_context(cntxt)
    CONTEXT_S **cntxt;
{
    if((*cntxt)->context)
      fs_give((void **) &(*cntxt)->context);

    if((*cntxt)->server)
      fs_give((void **) &(*cntxt)->server);

    if((*cntxt)->nickname)
      fs_give((void **)&(*cntxt)->nickname);

    if((*cntxt)->label)
      fs_give((void **) &(*cntxt)->label);

    if((*cntxt)->comment)
      fs_give((void **) &(*cntxt)->comment);

    if((*cntxt)->selected.reference)
      fs_give((void **) &(*cntxt)->selected.reference);

    if((*cntxt)->selected.sub)
      free_selected(&(*cntxt)->selected.sub);
    free_strlist(&(*cntxt)->selected.folders);

    free_fdir(&(*cntxt)->dir, 1);

    fs_give((void **)cntxt);
}


/*
 *
 */
void
init_inbox_mapping(path, cntxt)
     char      *path;
     CONTEXT_S *cntxt;
{
    FOLDER_S *f;

    /*
     * If mapping already exists, blast it and replace it below...
     */
    if((f = folder_entry(0, FOLDERS(cntxt)))
       && f->nickname && !strcmp(f->nickname, ps_global->inbox_name))
      folder_delete(0, FOLDERS(cntxt));

    if(path){
	f = new_folder(path);
	f->nickname = cpystr(ps_global->inbox_name);
	f->name_len = strlen(f->nickname);
    }
    else
      f = new_folder(ps_global->inbox_name);

    folder_insert(0, f, FOLDERS(cntxt));
}


/*
 *
 */
FDIR_S *
new_fdir(ref, view, wildcard)
    char *ref, *view;
    int   wildcard;
{
    FDIR_S *rv = (FDIR_S *) fs_get(sizeof(FDIR_S));

    memset((void *) rv, 0, sizeof(FDIR_S));

    /* Monkey with the view to make sure it has wildcard? */
    if(view && *view){
	rv->view.user = cpystr(view);

	/*
	 * This is sorta hairy since, for simplicity we allow
	 * users to use '*' in the view, but for mail
	 * we really mean '%' as def'd in 2060...
	 */
	if(wildcard == '*'){	/* must be #news. */
	    if(strindex(view, wildcard))
	      rv->view.internal = cpystr(view);
	}
	else{			/* must be mail */
	    char *p;

	    if(p = strpbrk(view, "*%")){
		rv->view.internal = p = cpystr(view);
		while(p = strpbrk(p, "*%"))
		  *p++ = '%';	/* convert everything to '%' */
	    }
	}

	if(!rv->view.internal){
	    rv->view.internal = (char *) fs_get((strlen(view)+3)*sizeof(char));
	    sprintf(rv->view.internal, "%c%s%c", wildcard, view, wildcard);
	}
    }
    else{
	rv->view.internal = (char *) fs_get(2 * sizeof(char));
	sprintf(rv->view.internal, "%c", wildcard);
    }

    if(ref)
      rv->ref = ref;

    rv->folders = init_folder_entries();
    rv->status = CNTXT_NOFIND;
    return(rv);
}


/*
 *
 */
void
free_fdir(f, recur)
    FDIR_S **f;
    int    recur;
{
    if(f && *f){
	if((*f)->prev && recur)
	  free_fdir(&(*f)->prev, 1);

	if((*f)->ref)
	  fs_give((void **)&(*f)->ref);

	if((*f)->view.user)
	  fs_give((void **)&(*f)->view.user);

	if((*f)->view.internal)
	  fs_give((void **)&(*f)->view.internal);

	if((*f)->desc)
	  fs_give((void **)&(*f)->desc);

	free_folder_entries(&(*f)->folders);
	fs_give((void **) f);
    }
}


/*
 *
 */
int
update_bboard_spec(bboard, buf)
      char *bboard, *buf;
{
    char *p = NULL;
    int	  nntp = 0, bracket = 0;

    if(*bboard == '*'
       || (*bboard == '{' && (p = strindex(bboard, '}')) && *(p+1) == '*')){
	/* server name ? */
	if(p || (*(bboard+1) == '{' && (p = strindex(++bboard, '}'))))
	  while(bboard <= p)		/* copy it */
	    if((*buf++ = *bboard++) == '/' && !strncmp(bboard, "nntp", 4))
	      nntp++;

	if(*bboard == '*')
	  bboard++;

	if(!nntp)
	  /*
	   * See if path portion looks newsgroup-ish while being aware
	   * of the "view" portion of the spec...
	   */
	  for(p = bboard; *p; p++)
	    if(*p == '['){
		if(bracket)		/* only one set allowed! */
		  break;
		else
		  bracket++;
	    }
	    else if(*p == ']'){
		if(bracket != 1)	/* must be closing bracket */
		  break;
		else
		  bracket++;
	    }
	    else if(!(isalnum((unsigned char) *p) || strindex(".-", *p)))
	      break;

	sprintf(buf, "%s%s%s",
		(!nntp && *p) ? "#public" : "#news.",
		(!nntp && *p && *bboard != '/') ? "/" : "",
		bboard);

	return(1);
    }

    return(0);
}


/*
 * new_context - creates and fills in a new context structure, leaving
 *               blank the actual folder list (to be filled in later as
 *               needed).  Also, parses the context string provided 
 *               picking out any user defined label.  Context lines are
 *               of the form:
 *
 *               [ ["] <string> ["] <white-space>] <context>
 *
 */
CONTEXT_S *
new_context(cntxt_string, prime)
    char *cntxt_string;
    int  *prime;
{
    CONTEXT_S  *c;
    char        host[MAXPATH], rcontext[MAXPATH],
		view[MAXPATH], dcontext[MAXPATH],
	       *nickname = NULL, *c_string = NULL,
	       *wildcard, *p;

    /*
     * do any context string parsing (like splitting user-supplied 
     * label from actual context)...
     */
    get_pair(cntxt_string, &nickname, &c_string, 0);

    if(update_bboard_spec(c_string, tmp_20k_buf)){
	fs_give((void **) &c_string);
	c_string = cpystr(tmp_20k_buf);
    }

    if(c_string && *c_string == '\"')
      removing_double_quotes(c_string);

    if(p = context_digest(c_string, dcontext, host, rcontext, view)){
	q_status_message2(SM_ORDER | SM_DING, 3, 4,
			  "Bad context, %s : %s", p, c_string);
	fs_give((void **) &c_string);
	if(nickname)
	  fs_give((void **)&nickname);

	return(NULL);
    }
    else
      fs_give((void **) &c_string);

    c = (CONTEXT_S *) fs_get(sizeof(CONTEXT_S)); /* get new context   */
    memset((void *) c, 0, sizeof(CONTEXT_S));	/* and initialize it */
    if(*host)
      c->server  = cpystr(host);		/* server + params */

    c->context = cpystr(dcontext);

    if(strstr(c->context, "#news."))
      c->use |= CNTXT_NEWS;

    c->dir = new_fdir(NULL, view, (c->use & CNTXT_NEWS) ? '*' : '%');

    /* fix up nickname */
    if(!(c->nickname = nickname)){			/* make one up! */
	sprintf(tmp_20k_buf, "%s%s%s",
		(c->use & CNTXT_NEWS)
		  ? "News"
		  : (c->dir->ref)
		      ? (c->dir->ref) :"Mail",
		(c->server) ? " on " : "",
		(c->server) ? c->server : "");
	c->nickname = cpystr(tmp_20k_buf);
    }

    if(prime && !*prime){
	*prime = 1;
	c->use |= CNTXT_SAVEDFLT;
    }

    /* fix up label */
    if(NEWS_TEST(c)){
	sprintf(tmp_20k_buf, "%sews groups%s%s",
		(*host) ? "N" : "Local n", (*host) ? " on " : "",
		(*host) ? host : "");
    }
    else{
	p = srchstr(rcontext, "[]");
	sprintf(tmp_20k_buf, "%solders%s%s in %.*s%s",
		(*host) ? "F" : "Local f", (*host) ? " on " : "",
		(*host) ? host : "", p ? p - rcontext : 0,
		rcontext, (p && (p - rcontext) > 0) ? "" : "home directory");
    }

    c->label = cpystr(tmp_20k_buf);

    dprint(2, (debugfile, "Context %s: serv:%s, ref:%s, view: %s\n",
	       c->context,
	       (c->server) ? c->server : "\"\"",
	       (c->dir->ref) ? c->dir->ref : "\"\"",
	       (c->dir->view.user) ? c->dir->view.user : "\"\""));

    return(c);
}



/*
 * build_folder_list - call mail_list to fetch us a list of folders
 *		       from the given context.
 */
void
build_folder_list(stream, context, pat, content, flags)
    MAILSTREAM **stream;
    CONTEXT_S   *context;
    char        *pat, *content;
    int		 flags;
{
    MM_LIST_S  ldata;
    BFL_DATA_S response;
    int	       local_open = 0, we_cancel = 0;
    char       reference[MAILTMPLEN], *p;

    if(!(context->dir->status & CNTXT_NOFIND)
       || (context->dir->status & CNTXT_PARTFIND))
      return;				/* find already done! */

    dprint(7, (debugfile, "build_folder_list: %s %s\n",
	       context ? context->context : "NULL",
	       pat ? pat : "NULL"));

    we_cancel = busy_alarm(1, NULL, NULL, 0);

    /*
     * Set up the pattern of folder name's to match within the
     * given context.
     */
    if(!pat || ((*pat == '*' || *pat == '%') && *(pat+1) == '\0')){
	context->dir->status &= ~CNTXT_NOFIND;	/* let'em know we tried */
	pat = context->dir->view.internal;
    }
    else
      context->use |= CNTXT_PARTFIND;	/* or are in a partial find */

    memset(mm_list_info = &ldata, 0, sizeof(MM_LIST_S));
    ldata.filter = (NEWS_TEST(context)) ? mail_lsub_filter : mail_list_filter;
    memset(ldata.data = &response, 0, sizeof(BFL_DATA_S));
    response.list = FOLDERS(context);

    if(flags & BFL_FLDRONLY)
      response.mask = LATT_NOSELECT;

    /*
     * if context is associated with a server, prepare a stream for
     * sending our request.
     */
    if(*context->context == '{'){
	/*
	 * Try using a stream we've already got open...
	 */
	if(stream && *stream
	   && !(ldata.stream = same_stream(context->context, *stream))){
	    mail_close(*stream);
	    *stream = NULL;
	}

	if(!ldata.stream)
	  ldata.stream = same_stream(context->context, ps_global->mail_stream);

	if(!ldata.stream && ps_global->inbox_stream
	   && ps_global->mail_stream != ps_global->inbox_stream)
	  ldata.stream = same_stream(context->context,ps_global->inbox_stream);

	/* gotta open a new one? */
	if(!ldata.stream){
	    ldata.stream = mail_cmd_stream(context, &local_open);
	    if(stream)
	      *stream = ldata.stream;
	}

	dprint(ldata.stream ? 7 : 1,
	       (debugfile, "build_folder_list: mail_open(%s) %s.\n",
		context->server, ldata.stream ? "OK" : "FAILED"));

	if(!ldata.stream){
	    context->use &= ~CNTXT_PARTFIND;	/* unset partial find bit */
	    if(we_cancel)
	      cancel_busy_alarm(-1);

	    return;
	}
    }
    else if(stream && *stream){			/* no server, simple case */
	if(*stream != ps_global->mail_stream
	   && *stream != ps_global->inbox_stream)
	  mail_close(*stream);

	*stream = NULL;
    }

    /*
     * If preset reference string, we're somewhere in the hierarchy.
     * ELSE we must be at top of context...
     */
    response.args.name = pat;
    if(!(response.args.reference = context->dir->ref)){
	if(p = strstr(context->context, "%s")){
	    strncpy(response.args.reference = reference,
		    context->context, p - context->context);
	    reference[p - context->context] = '\0';
	    if(*(p += 2))
	      response.args.tail = p;
	}
	else
	  response.args.reference = context->context;
    }

    if(flags & BFL_SCAN)
      mail_scan(ldata.stream, response.args.reference,
		response.args.name, content);
    else if(flags & BFL_LSUB)
      mail_lsub(ldata.stream, response.args.reference, response.args.name);
    else{
      set_read_predicted(1);
      mail_list(ldata.stream, response.args.reference, response.args.name);
      set_read_predicted(0);
    }

    if(context->dir && response.response.delim)
      context->dir->delim = response.response.delim;

    if(!(flags & (BFL_LSUB|BFL_SCAN)) && F_ON(F_QUELL_EMPTY_DIRS, ps_global)){
	LISTRES_S  listres;
	FOLDER_S  *f;
	int	   i;

	for(i = 0; i < folder_total(response.list); i++)
	  if((f = folder_entry(i, FOLDERS(context)))->isdir){
	      memset(ldata.data = &listres, 0, sizeof(LISTRES_S));
	      ldata.filter = mail_list_response;

	      if(context->dir->ref)
		sprintf(reference, "%s%s", context->dir->ref, f->name);
	      else
		context_apply(reference, context, f->name);

	      /* append the delimiter to the reference */
	      for(p = reference; *p; p++)
		;

	      *p++ = context->dir->delim;
	      *p   = '\0';

	      mail_list(ldata.stream, reference, "%");

	      /* anything interesting inside? */
	      if(listres.count <= 1L){
		  if(f->isfolder){
		      f->isdir = 0;
		  }
		  else{
		      folder_delete(i, FOLDERS(context));
		      i--;	/* offset inc'ing */
		  }
	      }
	  }
    }

    if(local_open && !stream)
      mail_close(ldata.stream);

    if(context->use & CNTXT_PRESRV)
      folder_select_restore(context);

    context->use &= ~CNTXT_PARTFIND;	/* unset partial list bit */
    if(we_cancel)
      cancel_busy_alarm(-1);
}


/*
 * Validate LIST response to command issued from build_folder_list
 *
 */
void
mail_list_filter(stream, mailbox, delim, attribs, data)
    MAILSTREAM *stream;
    char       *mailbox;
    int		delim;
    long	attribs;
    void       *data;
{
    BFL_DATA_S *ld = (BFL_DATA_S *) data;
    FOLDER_S   *new_f = NULL;
    int		boxlen, reflen, taillen;

    if(delim)
      ld->response.delim = delim;

    /* test against mask of DIS-allowed attributes */
    if((ld->mask & attribs)){
	dprint(3, (debugfile, "mail_list_filter: failed attribute test"));
	return;
    }

    /*
     * First, make sure response fits our "reference" arg 
     * NOTE: build_folder_list can't supply breakout?
     */
    if(!mail_list_in_collection(&mailbox, ld->args.reference,
				ld->args.name, ld->args.tail))
      return;

    /* Does result  */
    if(F_OFF(F_ENABLE_DOT_FOLDERS, ps_global) && *mailbox == '.'){
	dprint(3, (debugfile, "mail_list_filter: dotfolder disallowed"));
	return;
    }

    /*
     * "Inbox" is filtered out here, since pine only supports one true
     * inbox...
     */
    if(!(attribs & LATT_NOSELECT) && strucmp(mailbox, "inbox")){
	ld->response.count++;
	ld->response.isfile = 1;
	new_f = new_folder(mailbox);
	new_f->isfolder = 1;

	if(F_ON(F_SEPARATE_FLDR_AS_DIR, ps_global)){
	    folder_insert(-1, new_f, ld->list);
	    new_f = NULL;
	}
    }

    /* directory? */
    if(delim && !(attribs & LATT_NOINFERIORS)){
	ld->response.count++;
	ld->response.isdir = 1;

	if(F_OFF(F_SEPARATE_FLDR_AS_DIR, ps_global)
	   && !strucmp(mailbox, "inbox")){
	    FOLDER_S *f;
	    int	      i;

	    for(i = 0; i < folder_total(ld->list); i++)
	      if(!strucmp((f = folder_entry(i, ld->list))->name, "inbox")){
		  f->isdir = 1;
		  break;
	      }
	}
	else{
	    if(!new_f)
	      new_f = new_folder(mailbox);

	    new_f->isdir = 1;
	}
    }

    if(new_f)
      folder_insert(-1, new_f, ld->list);

    if(attribs & LATT_MARKED)
      ld->response.ismarked = 1;

    if(attribs & LATT_UNMARKED)
      ld->response.unmarked = 1;
}


/*
 * Validate LSUB response to command issued from build_folder_list
 *
 */
void
mail_lsub_filter(stream, mailbox, delim, attribs, data)
    MAILSTREAM *stream;
    char       *mailbox;
    int		delim;
    long	attribs;
    void       *data;
{
    BFL_DATA_S *ld = (BFL_DATA_S *) data;
    FOLDER_S   *new_f = NULL;
    char       *ref;

    if(delim)
      ld->response.delim = delim;

    /* test against mask of DIS-allowed attributes */
    if((ld->mask & attribs)){
	dprint(3, (debugfile, "mail_list_filter: failed attribute test"));
	return;
    }

    /* Normalize mailbox and reference strings re: namespace */
    if(!strncmp(mailbox, "#news.", 6))
      mailbox += 6;

    if(!strncmp(ref = ld->args.reference, "#news.", 6))
      ref += 6;

    if(!mail_list_in_collection(&mailbox, ref, ld->args.name, ld->args.tail))
      return;

    if(!(attribs & LATT_NOSELECT)){
	ld->response.count++;
	ld->response.isfile = 1;
	new_f = new_folder(mailbox);
	new_f->isfolder = 1;
	folder_insert(F_ON(F_READ_IN_NEWSRC_ORDER, ps_global)
			? folder_total(ld->list) : -1,
		      new_f, ld->list);
    }

    /* We don't support directories in #news */
}


/*
 * 
 */
int
mail_list_in_collection(mailbox, ref, name, tail)
    char **mailbox, *ref, *name, *tail;
{
    int	  boxlen, reflen, taillen;
    char *p;

    boxlen  = strlen(*mailbox);
    reflen  = ref ? strlen(ref) : 0;
    taillen = tail ? strlen(tail) : 0;

    if(boxlen
       && (reflen ? !struncmp(*mailbox, ref, reflen)
		  : (p = strpbrk(name, "%*"))
		       ? !struncmp(*mailbox, name, reflen = p - name)
		       : 0)
       && (!taillen
	   || (taillen < boxlen - reflen
	       && !strucmp(&(*mailbox)[boxlen - taillen], tail)))){
	if(taillen)
	  (*mailbox)[boxlen - taillen] = '\0';

	if(*(*mailbox += reflen))
	  return(TRUE);
    }
    /*
     * else don't worry about context "breakouts" since
     * build_folder_list doesn't let the user introduce
     * one...
     */

    return(FALSE);
}


/*
 * rebuild_folder_list -- free up old list and re-issue commands to build
 *			  a new list.
 */
void
refresh_folder_list(context, nodirs, startover)
    CONTEXT_S *context;
    int	       nodirs, startover;
{
    if(startover)
      free_folder_list(context);

    build_folder_list(NULL, context, NULL, NULL,
		      (NEWS_TEST(context) ? BFL_LSUB : BFL_NONE)
		        | ((nodirs) ? BFL_FLDRONLY : BFL_NONE));
}


/*
 * free_folder_list - loop thru the context's lists of folders
 *                     clearing all entries without nicknames
 *                     (as those were user provided) AND reset the 
 *                     context's find flag.
 *
 * NOTE: if fetched() information (e.g., like message counts come back
 *       in bboard collections), we may want to have a check before
 *       executing the loop and setting the FIND flag.
 */
void
free_folder_list(cntxt)
    CONTEXT_S *cntxt;
{
    int n, i;

    /*
     * In this case, don't blast the list as it was given to us by the
     * user and not the result of a mail_list call...
     */
    if(cntxt->use & CNTXT_INCMNG)
      return;

    if(cntxt->use & CNTXT_PRESRV)
      folder_select_preserve(cntxt);

    for(n = folder_total(FOLDERS(cntxt)), i = 0; n > 0; n--)
      if(folder_entry(i, FOLDERS(cntxt))->nickname)
	i++;					/* entry wasn't from LIST */
      else
	folder_delete(i, FOLDERS(cntxt));

    cntxt->dir->status |= CNTXT_NOFIND;		/* do find next time...  */
						/* or add the fake entry */
    cntxt->use &= ~(CNTXT_PSEUDO | CNTXT_PRESRV | CNTXT_ZOOM);
}


/*
 * default_save_context - return the default context for saved messages
 */
CONTEXT_S *
default_save_context(cntxt)
    CONTEXT_S *cntxt;
{
    while(cntxt)
      if((cntxt->use) & CNTXT_SAVEDFLT)
	return(cntxt);
      else
	cntxt = cntxt->next;

    return(NULL);
}



/*
 * folder_complete - foldername completion routine
 *
 *   Result: returns 0 if the folder doesn't have a any completetion
 *		     1 if the folder has a completion (*AND* "name" is
 *		       replaced with the completion)
 *
 */
folder_complete(context, name, completions)
    CONTEXT_S *context;
    char      *name;
    int	      *completions;
{
    return(folder_complete_internal(context, name, completions, FC_NONE));
}


/*
 * 
 */
int
folder_complete_internal(context, name, completions, flags)
    CONTEXT_S *context;
    char      *name;
    int	      *completions;
    int	       flags;
{
    int	      i, match = -1, ftotal;
    char      tmp[MAXFOLDER+2], *a, *b, *fn, *pat;
    FOLDER_S *f;

    if(completions)
      *completions = 0;

    if(*name == '\0' || !context_isambig(name))
      return(0);

    if(!((context->use & CNTXT_INCMNG) || ALL_FOUND(context))){
	/*
	 * Build the folder list from scratch since we may need to
	 * traverse hierarchy...
	 */
	
	free_folder_list(context);
	sprintf(tmp, "%s%c", name, NEWS_TEST(context) ? '*' : '%');
	build_folder_list(NULL, context, tmp, NULL,
			  (NEWS_TEST(context) & !(flags & FC_FORCE_LIST))
			    ? BFL_LSUB : BFL_NONE);
    }

    *tmp = '\0';			/* find uniq substring */
    ftotal = folder_total(FOLDERS(context));
    for(i = 0; i < ftotal; i++){
	f   = folder_entry(i, FOLDERS(context));
	fn  = FLDR_NAME(f);
	pat = name;
	if(!(NEWS_TEST(context) || (context->use & CNTXT_INCMNG))){
	    fn  = folder_last_cmpnt(fn, context->dir->delim);
	    pat = folder_last_cmpnt(pat, context->dir->delim);
	}

	if(!strncmp(fn, pat, strlen(pat))){
	    if(match != -1){		/* oh well, do best we can... */
		a = fn;
		if(match >= 0){
		    f  = folder_entry(match, FOLDERS(context));
		    fn = FLDR_NAME(f);
		    if(!NEWS_TEST(context))
		      fn = folder_last_cmpnt(fn, context->dir->delim);

		    strcpy(tmp, fn);
		}

		match = -2;
		b     = tmp;		/* remember largest common text */
		while(*a && *b && *a == *b)
		  *b++ = *a++;

		*b = '\0';
	    }
	    else		
	      match = i;		/* bingo?? */
	}
    }

    if(match >= 0){			/* found! */
	f  = folder_entry(match, FOLDERS(context));
	fn = FLDR_NAME(f);
	if(!(NEWS_TEST(context) || (context->use & CNTXT_INCMNG)))
	  fn = folder_last_cmpnt(fn, context->dir->delim);

	strcpy(pat, fn);
	if(f->isdir){
	    name[i = strlen(name)] = context->dir->delim;
	    name[i+1] = '\0';
	}
    }
    else if(match == -2)		/* closest we could find */
      strcpy(pat, tmp);

    if(completions)
      *completions = ftotal;

    if(!((context->use & CNTXT_INCMNG) || ALL_FOUND(context)))
      free_folder_list(context);

    return((match >= 0) ? ftotal : 0);
}


/*
 *
 */
char *
folder_last_cmpnt(s, d)
    char *s;
    int   d;
{
    register char *p;
    
    if(d)
      for(p = s; p = strindex(p, d); s = ++p)
	;

    return(s);
}


/*
 *           FOLDER MANAGEMENT ROUTINES
 */


/*
 * Folder List Structure - provides for two ways to access and manage
 *                         folder list data.  One as an array of pointers
 *                         to folder structs or
 */
typedef struct folder_list {
    unsigned   used;
    unsigned   allocated;
#ifdef	DOSXXX
    FILE      *folders;		/* tmpfile of binary */
#else
    FOLDER_S **folders;		/* array of pointers to folder structs */
#endif
} FLIST;

#define FCHUNK  64


/*
 * init_folder_entries - return a piece of memory suitable for attaching 
 *                   a list of folders...
 *
 */
void *
init_folder_entries()
{
    FLIST *flist = (FLIST *) fs_get(sizeof(FLIST));
    flist->folders = (FOLDER_S **) fs_get(FCHUNK * sizeof(FOLDER_S *));
    memset((void *)flist->folders, 0, (FCHUNK * sizeof(FOLDER_S *)));
    flist->allocated = FCHUNK;
    flist->used      = 0;
    return((void *)flist);
}



/*
 * new_folder - return a brand new folder entry, with the given name
 *              filled in.
 *
 * NOTE: THIS IS THE ONLY WAY TO PUT A NAME INTO A FOLDER ENTRY!!!
 * STRCPY WILL NOT WORK!!!
 */
FOLDER_S *
new_folder(name)
    char *name;
{
#ifdef	DOSXXX
#else
    FOLDER_S *tmp;
    size_t    l = strlen(name);

    tmp = (FOLDER_S *)fs_get(sizeof(FOLDER_S) + (l * sizeof(char)));
    memset((void *)tmp, 0, sizeof(FOLDER_S));
    strcpy(tmp->name, name);
    tmp->name_len = (unsigned char) l;
    return(tmp);
#endif
}



/*
 * folder_entry - folder struct access routine.  Permit reference to
 *                folder structs via index number. Serves two purposes:
 *            1) easy way for callers to access folder data 
 *               conveniently
 *            2) allows for a custom manager to limit memory use
 *               under certain rather limited "operating systems"
 *               who shall renameless, but whose initials are DOS
 *
 *
 */
FOLDER_S *
folder_entry(i, flist)
    int   i;
    void *flist;
{
#ifdef	DOSXXX
    /*
     * Manage entries within a limited amount of core (what a drag).
     */

    fseek((FLIST *)flist->folders, i * sizeof(FOLDER_S) + MAXPATH, 0);
    fread(&folder, sizeof(FOLDER_S) + MAXPATH, (FLIST *)flist->folders);
#else
    return((i >= ((FLIST *)flist)->used) ? NULL:((FLIST *)flist)->folders[i]);
#endif
}



/*
 * free_folder_entries - release all resources associated with the given 
 *			 list of folder entries
 */
void
free_folder_entries(flist)
    void **flist;
{
#ifdef	DOSXXX
    fclose((*((FLIST **)flist))->folders); 	/* close folder tmpfile */
#else
    register int i;

    if(!(flist && *flist))
      return;

    i = (*((FLIST **)flist))->used;
    while(i--){
	if((*((FLIST **)flist))->folders[i]->nickname)
	  fs_give((void **)&(*((FLIST **)flist))->folders[i]->nickname);

	fs_give((void **)&((*((FLIST **)flist))->folders[i]));
    }

    fs_give((void **)&((*((FLIST **)flist))->folders));
#endif
    fs_give(flist);
}



/*
 * return the number of folders associated with the given folder list
 */
int
folder_total(flist)
    void *flist;
{
    return((int)((FLIST *)flist)->used);
}


/*
 * return the index number of the given name in the given folder list
 */
int
folder_index(name, cntxt, flags)
    char      *name;
    CONTEXT_S *cntxt;
    int	       flags;
{
    register  int i = 0;
    FOLDER_S *f;
    char     *fname;

    for(i = 0; f = folder_entry(i, FOLDERS(cntxt)); i++)
      if(((flags & FI_DIR) && f->isdir) || ((flags & FI_FOLDER) && !f->isdir)){
	  fname = FLDR_NAME(f);
#if defined(DOS) || defined(OS2)
	  if(toupper((unsigned char)(*name))
	     == toupper((unsigned char)(*fname)) && strucmp(name, fname) == 0)
#else
	  if(*name == *fname && strcmp(name, fname) == 0)
#endif
	    return(i);
      }

    return(-1);
}



/*
 * 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.
 */
char *
next_folder(streamp, next, current, cntxt, find_recent)
    MAILSTREAM **streamp;
    char	*current, *next;
    CONTEXT_S	*cntxt;
    long	*find_recent;
{
    int       index, recent = 0;
    FOLDER_S *f = NULL;

    /* note: find_folders may assign "stream" */
    build_folder_list(streamp, cntxt, NULL, NULL,
		      NEWS_TEST(cntxt) ? BFL_LSUB : BFL_NONE);

    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;
	  char        msg_buf[MAX_SCREEN_COLS+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;

	  strcat(strncat(strcpy(msg_buf, "Checking "), FLDR_NAME(f), 50),
		 " for recent messages");
	  we_cancel = busy_alarm(1, msg_buf, NULL, 1);

	  /* First, get a stream for the test */
	  if(streamp && *streamp){
	      if(context_same_stream(cntxt, f->name, *streamp)){
		  stream = *streamp;
	      }
	      else{
		  mail_close(*streamp);
		  *streamp = NULL;
	      }
	  }

	  if(!stream){
	      if(context_same_stream(cntxt, f->name, ps_global->mail_stream))
		stream = ps_global->mail_stream;
	      else if(ps_global->mail_stream != ps_global->inbox_stream
		      && context_same_stream(cntxt, f->name,
					     ps_global->inbox_stream))
		stream = ps_global->inbox_stream;
	  }

	  /* If interestingness is indeterminate or we're
	   * told to explicitly, look harder...
	   */
	  if(F_OFF(F_ENABLE_FAST_RECENT, ps_global)
	     || !((rv = folder_exists(cntxt,f->name))
					     & (FEX_ISMARKED | FEX_UNMARKED))){
	      extern MAILSTATUS mm_status_result;

	      /* No \Marked from LSUB in the non/older/proxy IMAP case */
	      if(!context_status(cntxt, stream, f->name, SA_RECENT))
		mm_status_result.flags = 0L;

	      rv = ((mm_status_result.flags & SA_RECENT)
		    && (*find_recent = mm_status_result.recent))
		     ? FEX_ISMARKED : 0;

	      if(F_ON(F_ENABLE_FAST_RECENT, ps_global))
		*find_recent = 0L;	/* consistency, boy! */
	  }

	  if(we_cancel)
	    cancel_busy_alarm(0);

	  if(rv & FEX_ISMARKED){
	      recent++;
	      break;
	  }
      }

    if(f && (!find_recent || recent))
      strcpy(next, FLDR_NAME(f));
    else
      *next = '\0';

    /* BUG: how can this be made smarter so we cache the list? */
    free_folder_list(cntxt);
    return((*next) ? next : NULL);
}



/*
 * folder_is_nick - check to see if the given name is a nickname
 *                  for some folder in the given context...
 *
 *  NOTE: no need to check if mm_list_names has been done as 
 *        nicknames can only be set by configuration...
 */
char *
folder_is_nick(nickname, flist)
    char *nickname;
    void *flist;
{
    register  int  i = 0;
    FOLDER_S *f;

    while(f = folder_entry(i, flist)){
	if(f->nickname && strcmp(nickname, f->nickname) == 0)
	  return(f->name);
	else
	  i++;
    }

    return(NULL);
}



/*----------------------------------------------------------------------
  Insert the given folder name into the sorted folder list
  associated with the given context.  Only allow ambiguous folder
  names IF associated with a nickname.

   Args: index  -- Index to insert at, OR insert in sorted order if -1
         folder -- folder structure to insert into list
	 flist  -- folder list to insert folder into

  **** WARNING ****
  DON'T count on the folder pointer being valid after this returns
  *** ALL FOLDER ELEMENT READS SHOULD BE THRU folder_entry() ***

  ----*/
int
folder_insert(index, folder, flist)
    int       index;
    FOLDER_S *folder;
    void     *flist;
{
    /* requested index < 0 means add to sorted list */
    if(index < 0 && (index = folder_total(flist)) > 0)
      index = folder_insert_sorted(index / 2, 0, index, folder, flist,
		     (ps_global->fld_sort_rule == FLD_SORT_ALPHA_DIR_FIRST)
			? compare_folders_dir_alpha
			: (ps_global->fld_sort_rule == FLD_SORT_ALPHA_DIR_LAST)
			     ? compare_folders_alpha_dir
			     : compare_folders_alpha);

    folder_insert_index(folder, index, flist);
    return(index);
}


/*
 * folder_insert_sorted - Insert given folder struct into given list
 *			  observing sorting specified by given
 *			  comparison function
 */
int
folder_insert_sorted(index, min_index, max_index, folder, flist, compf)
    int	       index, min_index, max_index;
    FOLDER_S  *folder;
    void      *flist;
    int	     (*compf) PROTO((FOLDER_S *, FOLDER_S *));
{
    int	      i;

    return(((i = (*compf)(folder_entry(index, flist), folder)) == 0)
	     ? index
	     : (i < 0)
		? ((++index >= max_index)
		    ? max_index
		    : ((*compf)(folder_entry(index, flist), folder) > 0)
		       ? index
		       : folder_insert_sorted((index + max_index) / 2, index,
					      max_index, folder, flist, compf))
		: ((index <= min_index)
		    ? min_index
		    : folder_insert_sorted((min_index + index) / 2, min_index, index,
					   folder, flist, compf)));
}


/* 
 * folder_insert_index - Insert the given folder struct into the global list
 *                 at the given index.
 */
void
folder_insert_index(folder, index, flist)
    FOLDER_S *folder;
    int       index;
    void     *flist;
{
#ifdef	DOSXXX
    FOLDER *tmp;

    tmp = (FOLDER_S *)fs_get(sizeof(FOLDER_S) + (MAXFOLDER * sizeof(char)));


#else
    register FOLDER_S **flp, **iflp;

    /* if index is beyond size, place at end of list */
    index = min(index, ((FLIST *)flist)->used);

    /* grow array ? */
    if(((FLIST *)flist)->used + 1 > ((FLIST *)flist)->allocated){
	((FLIST *)flist)->allocated += FCHUNK;
	fs_resize((void **)&(((FLIST *)flist)->folders),
		  (((FLIST *)flist)->allocated) * sizeof(FOLDER_S *));
    }

    /* shift array left */
    iflp = &(((FOLDER_S **)((FLIST *)flist)->folders)[index]);
    for(flp = &(((FOLDER_S **)((FLIST *)flist)->folders)[((FLIST *)flist)->used]); 
	flp > iflp; flp--)
      flp[0] = flp[-1];

    ((FLIST *)flist)->folders[index] = folder;
    ((FLIST *)flist)->used          += 1;
#endif
}


/*----------------------------------------------------------------------
    Removes a folder at the given index in the given context's
    list.

Args: index -- Index in folder list of folder to be removed
      flist -- folder list
 ----*/
void
folder_delete(index, flist)
    int   index;
    void *flist;
{
    register int  i;
    FOLDER_S     *f;

    if(((FLIST *)flist)->used 
       && (index < 0 || index >= ((FLIST *)flist)->used))
      return;				/* bogus call! */

    if((f = folder_entry(index, flist))->nickname)
      fs_give((void **)&(f->nickname));
      
#ifdef	DOSXXX
    /* shift all entries after index up one.... */
#else
    fs_give((void **)&(((FLIST *)flist)->folders[index]));
    for(i = index; i < ((FLIST *)flist)->used - 1; i++)
      ((FLIST *)flist)->folders[i] = ((FLIST *)flist)->folders[i+1];


    ((FLIST *)flist)->used -= 1;
#endif
}



/*----------------------------------------------------------------------
    Find an entry in the folder list by matching names
  ----*/
search_folder_list(list, name)
     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;

/*----------------------------------------------------------------------
    Verify and canonicalize news groups names. 
    Called from the message composer

Args:  given_group    -- List of groups typed by user
       expanded_group -- pointer to point to expanded list, which will be
			 allocated here and freed in caller.  If this is
			 NULL, don't attempt to validate.
       error          -- pointer to store error message
       fcc            -- pointer to point to fcc, which will be
			 allocated here and freed in caller

Returns:  0 if all is OK
         -1 if addresses weren't valid

Test the given list of newstroups against those recognized by our nntp
servers.  Testing by actually trying to open the list is much cheaper, both
in bandwidth and memory, than yanking the whole list across the wire.
  ----*/
int
news_build(given_group, expanded_group, error, fcc, mangled)
    char	 *given_group,
		**expanded_group,
		**error;
    BUILDER_ARG	 *fcc;
    int		 *mangled;
{
    char	 ng_error[90], *p1, *p2, *name, *end, *ep, **server,
		 ng_ref[MAILTMPLEN];
    int          expanded_len = 0, num_in_error = 0, cnt_errs, we_cancel = 0;
    MAILSTREAM  *stream = NULL;
    struct ng_list {
	char  *groupname;
	NgCacheReturns  found;
	struct ng_list *next;
    }*nglist = NULL, **ntmpp, *ntmp;
#ifdef SENDNEWS
    static int no_servers = 0;
#endif

    clear_cursor_pos();

    dprint(2, (debugfile,
	"- news_build - (%s)\n", given_group ? given_group : "nul"));

    if(error)
      *error = NULL;

    ng_ref[0] = '\0';

    /*------ parse given entries into a list ----*/
    ntmpp = &nglist;
    for(name = given_group; *name; name = end){

	/* find start of next group name */
        while(*name && (isspace((unsigned char)*name) || *name == ','))
	  name++;

	/* find end of group name */
	end = name;
	while(*end && !isspace((unsigned char)*end) && *end != ',')
	  end++;

        if(end != name){
	    *ntmpp = (struct ng_list *)fs_get(sizeof(struct ng_list));
	    (*ntmpp)->next      = NULL;
	    (*ntmpp)->found     = NotChecked;
            (*ntmpp)->groupname = fs_get(end - name + 1);
            strncpy((*ntmpp)->groupname, name, end - name);
            (*ntmpp)->groupname[end - name] = '\0';
	    ntmpp = &(*ntmpp)->next;
	    if(!expanded_group)
	      break;  /* no need to continue if just doing fcc */
        }
    }

    /*
     * If fcc is not set or is set to default, then replace it if
     * one of the recipient rules is in effect.
     */
    if(fcc){
	if((ps_global->fcc_rule == FCC_RULE_RECIP ||
	    ps_global->fcc_rule == FCC_RULE_NICK_RECIP) &&
	       (nglist && nglist->groupname)){
	  if(fcc->tptr)
	    fs_give((void **)&fcc->tptr);

	  fcc->tptr = cpystr(nglist->groupname);
	}
	else if(!fcc->tptr) /* already default otherwise */
	  fcc->tptr = cpystr(ps_global->VAR_DEFAULT_FCC);
    }

    if(!nglist){
	if(expanded_group)
	  *expanded_group = cpystr("");
        return 0;
    }

    if(!expanded_group)
      return 0;

#ifdef	DEBUG
    for(ntmp = nglist; debug >= 9 && ntmp; ntmp = ntmp->next)
      dprint(9, (debugfile, "Parsed group: --[%s]--\n", ntmp->groupname));
#endif

    /* If we are doing validation */
    if(F_OFF(F_NO_NEWS_VALIDATION, ps_global)){
	int need_to_talk_to_server = 0;

	/*
	 * First check our cache of validated newsgroups to see if we even
	 * have to open a stream.
	 */
	for(ntmp = nglist; ntmp; ntmp = ntmp->next){
	    ntmp->found = chk_newsgrp_cache(ntmp->groupname);
	    if(ntmp->found == NotInCache)
	      need_to_talk_to_server++;
	}

	if(need_to_talk_to_server){

#ifdef SENDNEWS
	  if(no_servers == 0)
#endif
	    we_cancel = busy_alarm(1, "Validating newsgroup(s)", NULL, 1);

	    /*
	     * Build a stream to the first server that'll talk to us...
	     */
	    for(server = ps_global->VAR_NNTP_SERVER;
		server && *server && **server;
		server++){
		sprintf(ng_ref, "{%s/nntp}#news.", *server);
		if(stream = mail_open(stream, ng_ref, OP_HALFOPEN))
		  break;
	    }

	    if(!server || !stream){
		if(error)
#ifdef SENDNEWS
		{
		 /* don't say this over and over */
		 if(no_servers == 0){
		    if(!server || !*server || !**server)
		      no_servers++;

		    *error = cpystr(no_servers
			    ? "Can't validate groups.  No servers defined"
			    : "Can't validate groups.  No servers responding");
		 }
		}
#else
		  *error = cpystr((!server || !*server || !**server)
			    ? "No servers defined for posting to newsgroups"
			    : "Can't validate groups.  No servers responding");
#endif
		*expanded_group = cpystr(given_group);
		goto done;
	    }
	}

	/*
	 * Now, go thru the list, making sure we can at least open each one...
	 */
	for(ntmp = nglist; ntmp; ntmp = ntmp->next){
	    /*
	     * It's faster and easier right now just to open the stream and
	     * do our own finds than to use the current folder_exists()
	     * interface...
	     */
	    if(ntmp->found == NotInCache){
		char	  tmp[MAILTMPLEN];
		MM_LIST_S ldata;
		LISTRES_S response;

		memset(mm_list_info = &ldata, 0, sizeof(MM_LIST_S));
		ldata.stream = stream;
		ldata.filter = mail_list_response;
		memset(ldata.data = &response, 0, sizeof(LISTRES_S));

		mail_list(stream,
			  ng_ref[0] ? ng_ref : "#news.",
			  ntmp->groupname);
		ntmp->found = (response.count > 0L) ? Found : Missing;
	    }

	    add_newsgrp_cache(ntmp->groupname, ntmp->found);
	}

	mail_close(stream);
    }

    /* figure length of string for matching groups */
    for(ntmp = nglist; ntmp; ntmp = ntmp->next){
      if(ntmp->found == Found || F_ON(F_NO_NEWS_VALIDATION, ps_global))
	expanded_len += strlen(ntmp->groupname) + 2;
      else
	num_in_error++;
    }

    /*
     * allocate and write the allowed, and error lists...
     */
    p1 = *expanded_group = fs_get((expanded_len + 1) * sizeof(char));
    if(error && num_in_error){
	cnt_errs = num_in_error;
	memset((void *)ng_error, 0, (size_t)90);
	sprintf(ng_error, "Unknown news group%s: ", plural(num_in_error));
	ep = ng_error + strlen(ng_error);
    }
    for(ntmp = nglist; ntmp; ntmp = ntmp->next){
	p2 = ntmp->groupname;
	if(ntmp->found == Found || F_ON(F_NO_NEWS_VALIDATION, ps_global)){
	    while(*p2)
	      *p1++ = *p2++;

	    if(ntmp->next){
		*p1++ = ',';
		*p1++ = ' ';
	    }
	}
	else if (error){
	    while(*p2 && (ep - ng_error < 89))
	      *ep++ = *p2++;

	    if(--cnt_errs > 0 && (ep - ng_error < 87)){
		strcpy(ep, ", ");
		ep += 2;
	    }
	}
    }

    *p1 = '\0';

    if(error && num_in_error)
      *error = cpystr(ng_error);

done:
    while(ntmp = nglist){
	nglist = nglist->next;
	fs_give((void **)&ntmp->groupname);
	fs_give((void **)&ntmp);
    }

    if(we_cancel){
	cancel_busy_alarm(0);
	mark_status_dirty();
	display_message('x');
	if(mangled)
	  *mangled |= BUILDER_MESSAGE_DISPLAYED;
    }

    return(num_in_error ? -1 : 0);
}


typedef struct ng_cache {
    char          *name;
    NgCacheReturns val;
}NgCache;
static NgCache *ng_cache_ptr;
#if defined(DOS) && !defined(_WINDOWS)
#define MAX_NGCACHE_ENTRIES 15
#else
#define MAX_NGCACHE_ENTRIES 40
#endif
/*
 * Simple newsgroup validity cache.  Opening a newsgroup to see if it
 * exists can be very slow on a heavily loaded NNTP server, so we cache
 * the results.
 */
NgCacheReturns
chk_newsgrp_cache(group)
char *group;
{
    register NgCache *ngp;
    
    for(ngp = ng_cache_ptr; ngp && ngp->name; ngp++){
	if(strcmp(group, ngp->name) == 0)
	  return(ngp->val);
    }

    return NotInCache;
}


/*
 * Add an entry to the newsgroup validity cache.
 *
 * LRU entry is the one on the bottom, oldest on the top.
 * A slot has an entry in it if name is not NULL.
 */
void
add_newsgrp_cache(group, result)
char *group;
NgCacheReturns result;
{
    register NgCache *ngp;
    NgCache save_ngp;

    /* first call, initialize cache */
    if(!ng_cache_ptr){
	int i;

	ng_cache_ptr =
	    (NgCache *)fs_get((MAX_NGCACHE_ENTRIES+1)*sizeof(NgCache));
	for(i = 0; i <= MAX_NGCACHE_ENTRIES; i++){
	    ng_cache_ptr[i].name = NULL;
	    ng_cache_ptr[i].val  = NotInCache;
	}
	ng_cache_ptr[MAX_NGCACHE_ENTRIES].val  = End;
    }

    if(chk_newsgrp_cache(group) == NotInCache){
	/* find first empty slot or End */
	for(ngp = ng_cache_ptr; ngp->name; ngp++)
	  ;/* do nothing */
	if(ngp->val == End){
	    /*
	     * Cache is full, throw away top entry, move everything up,
	     * and put new entry on the bottom.
	     */
	    ngp = ng_cache_ptr;
	    if(ngp->name) /* just making sure */
	      fs_give((void **)&ngp->name);

	    for(; (ngp+1)->name; ngp++){
		ngp->name = (ngp+1)->name;
		ngp->val  = (ngp+1)->val;
	    }
	}
	ngp->name = cpystr(group);
	ngp->val  = result;
    }
    else{
	/*
	 * Move this entry from current location to last to preserve
	 * LRU order.
	 */
	for(ngp = ng_cache_ptr; ngp && ngp->name; ngp++){
	    if(strcmp(group, ngp->name) == 0) /* found it */
	      break;
	}
	save_ngp.name = ngp->name;
	save_ngp.val  = ngp->val;
	for(; (ngp+1)->name; ngp++){
	    ngp->name = (ngp+1)->name;
	    ngp->val  = (ngp+1)->val;
	}
	ngp->name = save_ngp.name;
	ngp->val  = save_ngp.val;
    }
}


void
free_newsgrp_cache()
{
    register NgCache *ngp;

    for(ngp = ng_cache_ptr; ngp && ngp->name; ngp++)
      fs_give((void **)&ngp->name);
    if(ng_cache_ptr)
      fs_give((void **)&ng_cache_ptr);
}


/*----------------------------------------------------------------------
    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(error_mess)
    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)500);

    /*--- 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_alarm(-1);
        return(NULL);
    }

    /*----- Call the browser -------*/
    tc = post_cntxt;
    if(rc = folders_for_post(ps_global, &tc, post_folder))
      post_cntxt = tc;

    cancel_busy_alarm(-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(post_host)
     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;

	we_cancel = busy_alarm(1, "Getting full list of groups for posting",
			       NULL, 0);

        post_context_string = fs_get(strlen(post_host[0]) + 20);
        sprintf(post_context_string, "{%s/nntp}#news.[]", post_host[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_alarm(-1);
    }
    return(NULL);
}


/*
 * mail_list_internal -- A monument to software religion and those who
 *			 would force it upon us.
 */
#undef	mail_list
void
mail_list_internal(s, r, p)
    MAILSTREAM *s;
    char       *r, *p;
{
    if(F_ON(F_FIX_BROKEN_LIST, ps_global)
       && ((s && s->mailbox && *s->mailbox == '{')
	   || (!s && ((r && *r == '{') || (p && *p == '{'))))){
	char tmp[MAILTMPLEN];

	sprintf(tmp, "%s%s", r ? r : "", p);
	mail_list(s, "", tmp);
    }
    else
      mail_list(s, r, p);
}

int
percent_formatted()
{
  if (percent.total == 0)
    return percent.total;
  else
    return ((int)(100*percent.num_done/percent.total));
}

#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(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, 'e', "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 */
