#if !defined(lint) && !defined(DOS)
static char rcsid[] = "$Id: other.c,v 4.551 1999/11/05 19:59:48 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.


   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   *
    ***********************************************************************
 

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

/*======================================================================
      other.c

      This implements the "setup" screen of miscellaneous commands such
  as keyboard lock, and disk usage

  ====*/

#include "headers.h"

extern PAT_HANDLE *pattern_h;

#define	BODY_LINES(X)	((X)->ttyo->screen_rows -HEADER_ROWS(X)-FOOTER_ROWS(X))

#define	CONFIG_SCREEN_TITLE		"SETUP CONFIGURATION"
#define	CONFIG_SCREEN_HELP_TITLE	"HELP FOR SETUP CONFIGURATION"
#define	R_SELD				'*'
#define	EXIT_PMT "Commit changes (\"Yes\" replaces settings, \"No\" abandons changes)"
static char *empty_val  = "Empty Value";
static char *empty_val2 = "<Empty Value>";
#define EMPTY_VAL_LEN     11
static char *no_val     = "No Value Set";
#define NO_VAL_LEN        12
static char *fixed_val  = "Value is Fixed";

#define ARB_HELP "HELP FOR ARBITRARY HEADER PATTERNS"

typedef struct edit_arb {
    struct variable *v;
    ARBHDR_S        *a;
    struct edit_arb *next;
} EARB_S;

typedef struct conf_line {
    char	     *varname,			/* alloc'd var name string   */
		     *value;			/* alloc'd var value string  */
    short	      varoffset;		/* offset from screen left   */
    short	      valoffset;		/* offset from screen left   */
    short	      val2offset;		/* offset from screen left   */
    struct variable  *var;			/* pointer to pinerc var     */
    long	      varmem;			/* value's index, if list    */
    int		      (*tool)();		/* tool to manipulate values */
    struct key_menu  *keymenu;			/* tool-specific  keymenu    */
    HelpType	      help;			/* variable's help text      */
    char	     *help_title;
    unsigned          flags;
    struct conf_line *varnamep;		/* pointer to varname        */
    struct conf_line *headingp;		/* pointer to heading        */
    struct conf_line *next, *prev;
    union flag_or_context_data {
	struct flag_table *fp;
	struct context_and_screen {
	    CONTEXT_S  *ct;
	    CONT_SCR_S *cs;
	} c;
	struct role_conf {
	    PAT_LINE_S *patline;
	    PAT_S      *pat;
	    PAT_S     **selected;
	} r;
	EARB_S **earb;
#ifdef	ENABLE_LDAP
	struct entry_and_screen {
	    LDAP          *ld;
	    LDAPMessage   *res;
	    LDAP_SERV_S   *info_used;
	    char          *serv;
	    ADDR_CHOOSE_S *ac;
	} a;
#endif
    } d;
} CONF_S;

/*
 * Valid for flags argument of config screen tools or flags field in CONF_S
 */
#define	CF_CHANGES	0x0001		/* Have been earlier changes */
#define	CF_NOSELECT	0x0002		/* This line is unselectable */
#define	CF_NOHILITE	0x0004		/* Don't highlight varname   */
#define	CF_NUMBER	0x0008		/* Input should be numeric   */
#define	CF_INVISIBLEVAR	0x0010		/* Don't show the varname    */
#define CF_PRINTER      0x0020		/* Printer config line       */
#define	CF_H_LINE	0x0040		/* Horizontal line	     */
#define	CF_B_LINE	0x0080		/* Blank line		     */
#define	CF_CENTERED	0x0100		/* Centered text	     */
#define	CF_STARTITEM	0x0200		/* Start of an "item"        */
#define	CF_PRIVATE	0x0400		/* Private flag for tool     */
#define	CF_DOUBLEVAR	0x0800		/* Line has 2 settable vars  */
#define	CF_VAR2		0x1000		/* Cursor on 2nd of 2 vars   */
#define	CF_COLORSAMPLE	0x2000		/* Show color sample here    */
#define	CF_POT_SLCTBL	0x4000		/* Potentially selectable    */

#define SPACE_BETWEEN_DOUBLEVARS 3
#define SAMPLE_LEADER "---------------------------"
#define SAMPLE "Sample"
#define HEADER_WORD "Header "
#define COLOR_BLOB "<    >"
#define COLOR_BLOB_LEN 6
#define EQ_COL 37
#define COLOR_INDENT 3
#define COLORNOSET "  [ Colors below may not be set until color is turned on above ]"

typedef struct save_config {
    union {
	char  *p;
	char **l;
	bitmap_t features;
    } user_val;
} SAVED_CONFIG_S;

/*
 *
 */
typedef struct conf_screen {
    CONF_S  *current,
	    *prev,
	    *top_line;
} OPT_SCREEN_S;


static OPT_SCREEN_S *opt_screen;
static char **def_printer_line;
static char no_ff[] = "-no-formfeed";

static struct variable *score_act_global_ptr, *scorei_pat_global_ptr;
static int role_global_flags;

#define next_confline(p)  ((p) ? (p)->next : NULL)
#define prev_confline(p)  ((p) ? (p)->prev : NULL)

/*
 * Macro's to help with color config support.
 */

/*
 * The CONF_S's varmem field serves dual purposes.  The low two bytes
 * are reserved for the pre-defined color index (though only 8 are 
 * defined for the nonce, and the high order bits are for the index
 * of the particular HDR_COLOR_S this CONF_S is associated with.
 * Capiche?
 */
#define	CFC_ICOLOR(V)		((V)->varmem & 0xff)
#define	CFC_ICUST(V)		((V)->varmem >> 16)
#define	CFC_SET_COLOR(I, C)	(((I) << 16) | (C))
#define	CFC_ICUST_INC(V)	CFC_SET_COLOR(CFC_ICUST(V) + 1, CFC_ICOLOR(V))
#define	CFC_ICUST_DEC(V)	CFC_SET_COLOR(CFC_ICUST(V) - 1, CFC_ICOLOR(V))


#ifdef	_WINDOWS
#define	color_related_var(p, v)	color_holding_var(p, v)
#endif



/*
 * Internal prototypes
 */
void	 draw_klocked_body PROTO((char *, char *));
void	 update_option_screen PROTO((struct pine *, OPT_SCREEN_S *, Pos *));
void	 print_option_screen PROTO((OPT_SCREEN_S *, char *));
void	 option_screen_redrawer PROTO(());
int	 conf_scroll_screen PROTO((struct pine *, OPT_SCREEN_S *, CONF_S *,
				   char *, char *, int, int));
HelpType config_help PROTO((int, int));
int      text_tool PROTO((struct pine *, int, CONF_S **, unsigned));
int	 checkbox_tool PROTO((struct pine *, int, CONF_S **, unsigned));
int	 flag_checkbox_tool PROTO((struct pine *, int, CONF_S **, unsigned));
int	 radiobutton_tool PROTO((struct pine *, int, CONF_S **, unsigned));
int	 color_setting_tool PROTO((struct pine *, int, CONF_S **, unsigned));
int      color_edit_screen PROTO((struct pine *, CONF_S **));
void	 color_update_selected PROTO((CONF_S *, char *, char *, int));
void     color_config_init_display PROTO((struct pine *, CONF_S **, CONF_S **));
void     add_header_color_lines PROTO((struct pine *, CONF_S **,
				       HDR_COLOR_S *, int));
void     add_color_setting_disp PROTO((CONF_S **, struct variable *, CONF_S *,
				       struct key_menu *, struct key_menu *,
				       HelpType, int, int,
				       char *, char *, int));
char	*new_color_line PROTO((char *, int, int, int));
int	 is_rgb_color PROTO((char *));
void     set_color_val PROTO((struct variable *));
int      var_defaults_to_rev PROTO((struct variable *));
void     write_custom_hdr_colors PROTO((struct pine *));
int	 yesno_tool PROTO((struct pine *, int, CONF_S **, unsigned));
int	 print_select_tool PROTO((struct pine *, int, CONF_S **, unsigned));
int	 print_edit_tool PROTO((struct pine *, int, CONF_S **, unsigned));
void	 set_def_printer_value PROTO((char *));
int	 context_select_tool PROTO((struct pine *, int, CONF_S **, unsigned));
void	 context_select_add PROTO((struct pine *, CONF_S **));
void	 context_select_delete PROTO((struct pine *, CONF_S **));
void	 context_select_edit PROTO((struct pine *, CONF_S **));
void	 context_select_shuffle PROTO((struct pine *, CONF_S **));
void	 context_select_swap PROTO((CONF_S *, CONF_S *));
void	 context_select_swap_special PROTO((CONTEXT_S *, CONTEXT_S *));
CONF_S	*context_select_prev PROTO((CONF_S *));
CONF_S	*context_select_next PROTO((CONF_S *));
#ifdef	ENABLE_LDAP
int	 addr_select_tool PROTO((struct pine *, int, CONF_S **, unsigned));
void     dir_init_display PROTO((struct pine *, CONF_S **, char **,
				 struct variable *, CONF_S **));
int	 dir_config_tool PROTO((struct pine *, int, CONF_S **, unsigned));
int      dir_edit_tool PROTO((struct pine *, int, CONF_S **, unsigned));
int      dir_edit_screen PROTO((struct pine *, LDAP_SERV_S *, char *,
				char **));
void	 dir_config_edit PROTO((struct pine *, CONF_S **));
void	 dir_config_add PROTO((struct pine *, CONF_S **));
void	 dir_config_del PROTO((struct pine *, CONF_S **));
void	 dir_config_shuffle PROTO((struct pine *, CONF_S **));
int	 ldap_checkbox_tool PROTO((struct pine *, int, CONF_S **, unsigned));
int	 ldap_radiobutton_tool PROTO((struct pine *, int, CONF_S **, unsigned));
void	 toggle_ldap_option_bit PROTO((struct pine *, int, struct variable *,
				    char *));
void     add_ldap_server_to_display PROTO((struct pine *, CONF_S **, char *,
					   char *, struct variable *, int,
					   struct key_menu *, HelpType,
					   int (*)(), int, CONF_S **));
void     add_ldap_fake_first_server PROTO((struct pine *, CONF_S **,
					   struct variable *,
					   struct key_menu *, HelpType,
					   int (*)()));
NAMEVAL_S *ldap_feature_list PROTO((int));
#endif	/* ENABLE_LDAP */
void	 toggle_feature_bit PROTO((struct pine *, int, struct variable *,
				    char *));
void	 config_add_list PROTO((struct pine *, CONF_S **, char **,
				char ***, int));
void	 config_del_list_item PROTO((CONF_S **, char ***));
char    *pretty_value PROTO((struct pine *, CONF_S *));
int      offer_to_fix_pinerc PROTO((struct pine *));
CONF_S	*new_confline PROTO((CONF_S **));
void	 snip_confline PROTO((CONF_S **));
void	 free_conflines PROTO((CONF_S **));
CONF_S	*first_confline PROTO((CONF_S *));
CONF_S  *first_sel_confline PROTO((CONF_S *));
CONF_S	*last_confline PROTO((CONF_S *));
int	 fixed_var PROTO((struct variable *, char *, char *));
int	 simple_exit_cmd PROTO((unsigned));
int      config_exit_cmd PROTO((unsigned));
int	 screen_exit_cmd PROTO((unsigned, char *));
void	 config_scroll_up PROTO((long));
void	 config_scroll_down PROTO((long));
void	 config_scroll_to_pos PROTO((long));
CONF_S  *config_top_scroll PROTO((struct pine *, CONF_S *));
char	*printer_name PROTO ((char *));
#ifdef	_WINDOWS
int	 config_scroll_callback PROTO((int, long));
#endif
void     fix_side_effects PROTO ((struct pine *, struct variable *, int));
SAVED_CONFIG_S *save_config_vars PROTO((struct pine *));
SAVED_CONFIG_S *save_color_config_vars PROTO((struct pine *));
void            revert_to_saved_config PROTO((struct pine *, SAVED_CONFIG_S *));
void            revert_to_saved_color_config PROTO((struct pine *,
						    SAVED_CONFIG_S *));
void            free_saved_config PROTO((struct pine *, SAVED_CONFIG_S **));
void            free_saved_color_config PROTO((struct pine *,
					       SAVED_CONFIG_S **));
char	*sigedit_exit_for_pico PROTO((struct headerentry *, void (*)()));
int	 exclude_config_var PROTO((struct pine *, struct variable *));
#ifndef	_WINDOWS
int	 color_related_var PROTO((struct pine *, struct variable *));
#endif
int	 color_holding_var PROTO((struct pine *, struct variable *));
int      color_text_tool PROTO((struct pine *, int, CONF_S **, unsigned));
int      save_include PROTO((struct pine *, struct variable *));
char    *role_type_print PROTO((char *, char *, int));
void	 role_process_filters PROTO(());
void     role_config_init_disp PROTO((struct pine *, CONF_S **,CONF_S **, int));
void     add_patline_to_display PROTO((struct pine *, CONF_S **, int, CONF_S **,
				       CONF_S **, PAT_LINE_S *, int));
void     free_earb PROTO((EARB_S **));
void     add_role_to_display PROTO((CONF_S **, PAT_LINE_S *, PAT_S *, int,
				    CONF_S **, int, int));
void     add_fake_first_role PROTO((CONF_S **, int, int));
int      role_select_tool PROTO((struct pine *, int, CONF_S **, unsigned));
int      role_config_tool PROTO((struct pine *, int, CONF_S **, unsigned));
int      role_config_add PROTO((struct pine *, CONF_S **, int));
int      role_config_edit PROTO((struct pine *, CONF_S **, int));
int      role_config_shuffle PROTO((struct pine *, CONF_S **));
int      role_config_del PROTO((struct pine *, CONF_S **, int));
int      role_config_addfile PROTO((struct pine *, CONF_S **, int));
int      role_config_delfile PROTO((struct pine *, CONF_S **, int));
void     swap_literal_roles PROTO((CONF_S *, CONF_S *));
void     swap_file_roles PROTO((CONF_S *, CONF_S *));
void     move_role_around_file PROTO((CONF_S **, int));
void     move_role_into_file PROTO((CONF_S **, int));
void     move_role_outof_file PROTO((CONF_S **, int));
void     delete_a_role PROTO((CONF_S **, int));
PATTERN_S *addrlst_to_pattern PROTO((ADDRESS *));
int      role_config_edit_screen PROTO((struct pine *, PAT_S *,
					char *, int, PAT_S **));
int      role_text_tool PROTO((struct pine *, int, CONF_S **, unsigned));
int	 role_filt_text_tool PROTO((struct pine *, int, CONF_S **, unsigned));
int      role_text_tool_inick PROTO((struct pine *, int, CONF_S **, unsigned));
void     calculate_inick_stuff PROTO((struct pine *));
int	 role_radiobutton_tool PROTO((struct pine *, int, CONF_S **, unsigned));
int	 role_filt_radiobutton_tool PROTO((struct pine *, int, CONF_S **,
					   unsigned));
int	 role_filt_exitcheck PROTO((CONF_S **, unsigned));
int      check_role_folders PROTO((char *, unsigned));
char    *get_role_specific_folder PROTO((CONF_S **));


static char *klockin, *klockame;

void
redraw_kl_body()
{
#ifndef NO_KEYBOARD_LOCK
    ClearScreen();

    set_titlebar("KEYBOARD LOCK", ps_global->mail_stream,
		 ps_global->context_current, ps_global->cur_folder, NULL,
		 1, FolderName, 0, 0);

    PutLine0(6,3 ,
       "You may lock this keyboard so that no one else can access your mail");
    PutLine0(8, 3 ,
       "while you are away.  The screen will be locked after entering the ");
    PutLine0(10, 3 ,
       "password to be used for unlocking the keyboard when you return.");
    fflush(stdout);
#endif
}


void
redraw_klocked_body()
{
#ifndef NO_KEYBOARD_LOCK
    ClearScreen();

    set_titlebar("KEYBOARD LOCK", ps_global->mail_stream,
		 ps_global->context_current, ps_global->cur_folder, NULL,
		 1, FolderName, 0, 0);

    PutLine2(6, 3, "This keyboard is locked by %s <%s>.",klockame, klockin);
    PutLine0(8, 3, "To unlock, enter password used to lock the keyboard.");
    fflush(stdout);
#endif
}


#ifndef NO_KEYBOARD_LOCK
/*----------------------------------------------------------------------
          Execute the lock keyboard command

    Args: None

  Result: keyboard is locked until user gives password
  ---*/

lock_keyboard()
{
    struct pine *ps = ps_global;
    char inpasswd[80], passwd[80], pw[80];
    HelpType help = NO_HELP;
    int i, times, old_suspend, flags;

    passwd[0] = '\0';
    redraw_kl_body();
    ps->redrawer = redraw_kl_body;

    times = atoi(ps->VAR_KBLOCK_PASSWD_COUNT);
    if(times < 1 || times > 5){
	dprint(2, (debugfile,
	"Kblock-passwd-count var out of range (1 to 5) [%d]\n", times));
	times = 1;
    }

    inpasswd[0] = '\0';

    for(i = 0; i < times; i++){
	pw[0] = '\0';
	while(1){			/* input pasword to use for locking */
	    int rc;
	    char prompt[50];

	    sprintf(prompt,
		"%s password to LOCK keyboard %s: ",
		i ? "Retype" : "Enter",
		i > 1 ? "(Yes, again) " : "");

	    flags = OE_PASSWD;
	    rc =  optionally_enter(pw, -FOOTER_ROWS(ps), 0, 30,
				    prompt, NULL, help, &flags);

	    if(rc == 3)
	      help = help == NO_HELP ? h_kb_lock : NO_HELP;
	    else if(rc == 1 || pw[0] == '\0'){
		q_status_message(SM_ORDER, 0, 2, "Keyboard lock cancelled");
		return(-1);
	    }
	    else if(rc != 4)
	      break;
	}

	if(!inpasswd[0])
	  strcpy(inpasswd, pw);
	else if(strcmp(inpasswd, pw)){
	    q_status_message(SM_ORDER, 0, 2,
		"Mismatch with initial password: keyboard lock cancelled");
	    return(-1);
	}
    }

    if(want_to("Really lock keyboard with entered password", 'y', 'n',
	       NO_HELP, WT_NORM) != 'y'){
	q_status_message(SM_ORDER, 0, 2, "Keyboard lock cancelled");
	return(-1);
    }

    draw_klocked_body(ps->VAR_USER_ID ? ps->VAR_USER_ID : "<no-user>",
		  ps->VAR_PERSONAL_NAME ? ps->VAR_PERSONAL_NAME : "<no-name>");

    ps->redrawer = redraw_klocked_body;
    if(old_suspend = F_ON(F_CAN_SUSPEND, ps_global))
      F_TURN_OFF(F_CAN_SUSPEND, ps_global);

    while(strcmp(inpasswd, passwd)){
	if(passwd[0])
	  q_status_message(SM_ORDER | SM_DING, 3, 3,
		     "Password to UNLOCK doesn't match password used to LOCK");
        
        help = NO_HELP;
        while(1){
	    int rc;

	    flags = OE_PASSWD | OE_DISALLOW_CANCEL;
	    rc =  optionally_enter(passwd, -FOOTER_ROWS(ps), 0, 30,
				   "Enter password to UNLOCK keyboard : ",NULL,
				   help, &flags);
	    if(rc == 3) {
		help = help == NO_HELP ? h_oe_keylock : NO_HELP;
		continue;
	    }

	    if(rc != 4)
	      break;
        }
    }

    if(old_suspend)
      F_TURN_ON(F_CAN_SUSPEND, ps_global);

    q_status_message(SM_ORDER, 0, 3, "Keyboard Unlocked");
    return(0);
}


void
draw_klocked_body(login, username)
    char *login, *username;
{
    klockin = login;
    klockame = username;
    redraw_klocked_body();
}
#endif /* !NO_KEYBOARD_LOCK */



/*----------------------------------------------------------------------
    Serve up the current signature within pico for editing

    Args: file to edit

  Result: signature changed or not.
  ---*/
char *
signature_edit(sigfile, title)
    char  *sigfile;
    char  *title;
{
    int	     editor_result;
    char     sig_path[MAXPATH+1], errbuf[2000], *errstr = NULL;
    char    *ret = NULL;
    STORE_S *msgso, *tmpso = NULL;
    gf_io_t  gc, pc;
    PICO     pbuf;
    struct variable *vars = ps_global->vars;

    if(!signature_path(sigfile, sig_path, MAXPATH))
      return(cpystr("No signature file defined."));

    memset(&pbuf, 0, sizeof(PICO));

    pbuf.tty_fix       = PineRaw;
    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      = sigedit_exit_for_pico;
    pbuf.upload	       = (VAR_UPLOAD_CMD && VAR_UPLOAD_CMD[0])
			   ? upload_msg_to_pico : NULL;
    pbuf.suspend       = do_suspend;
    pbuf.helper        = helper;
    pbuf.resize	       = resize_for_pico;
    pbuf.winch_cleanup = winch_cleanup;
    pbuf.alt_ed        = (VAR_EDITOR && VAR_EDITOR[0] && VAR_EDITOR[0][0])
			    ? VAR_EDITOR : NULL;
    pbuf.alt_spell     = (VAR_SPELLER && VAR_SPELLER[0]) ? VAR_SPELLER : NULL;
    pbuf.fillcolumn    = ps_global->composer_fillcol;
    pbuf.menu_rows     = FOOTER_ROWS(ps_global) - 1;
    pbuf.composer_help = h_composer_sigedit;
    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.pine_anchor   = set_titlebar(title,
				      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.colors        = colors_for_pico();
    pbuf.pine_flags    = 0;
    pbuf.pine_flags   |= F_ON(F_CAN_SUSPEND,ps_global)	    ? P_SUSPEND : 0;
    pbuf.pine_flags   |= F_ON(F_USE_FK,ps_global)	    ? P_FKEYS : 0;
    pbuf.pine_flags   |= ps_global->restricted		    ? P_SECURE : 0;
    pbuf.pine_flags   |= (F_ON(F_ENABLE_ALT_ED,ps_global) ||
			  F_ON(F_ALT_ED_NOW,ps_global) ||
			  (VAR_EDITOR && VAR_EDITOR[0] && VAR_EDITOR[0][0]))
			      ? P_ADVANCED : 0;
    pbuf.pine_flags   |= F_ON(F_ALT_ED_NOW,ps_global)	    ? P_ALTNOW : 0;
    pbuf.pine_flags   |= F_ON(F_USE_CURRENT_DIR,ps_global)  ? P_CURDIR : 0;
    pbuf.pine_flags   |= F_ON(F_SUSPEND_SPAWNS,ps_global)   ? P_SUBSHELL : 0;
    pbuf.pine_flags   |= F_ON(F_COMPOSE_MAPS_DEL,ps_global) ? P_DELRUBS : 0;
    pbuf.pine_flags   |= F_ON(F_ENABLE_TAB_COMPLETE,ps_global)
							    ? P_COMPLETE : 0;

    pbuf.pine_flags   |= F_ON(F_SHOW_CURSOR, ps_global)     ? P_SHOCUR : 0;
    pbuf.pine_flags   |= F_ON(F_DEL_FROM_DOT, ps_global)    ? P_DOTKILL : 0;
    pbuf.pine_flags   |= (!VAR_CHAR_SET || !strucmp(VAR_CHAR_SET, "US-ASCII"))
			  ? P_HIBITIGN: 0;
    pbuf.pine_flags   |= F_ON(F_ENABLE_DOT_FILES, ps_global)? P_DOTFILES : 0;
    if(VAR_OPER_DIR){
	pbuf.oper_dir    = VAR_OPER_DIR;
	pbuf.pine_flags |= P_TREE;
    }

    /* NOTE: at this point, alot of pico struct fields are null'd out
     * thanks to the leading memset; in particular "headents" which tells
     * pico to behave like a normal editor (though modified slightly to
     * let the caller dictate the file to edit and such)...
     */

    if(VAR_OPER_DIR && !in_dir(VAR_OPER_DIR, sig_path)){
	ret = (char *)fs_get((strlen(VAR_OPER_DIR) + 100) * sizeof(char));
	sprintf(ret, "Can't edit file outside of %s", VAR_OPER_DIR);
	return(ret);
    }

    /*
     * Now alloc and init the text to pass pico
     */
    if(!(msgso = so_get(PicoText, NULL, EDIT_ACCESS))){
	ret = cpystr("Error allocating space for file");
	dprint(1, (debugfile, "Can't alloc space for signature_edit"));
	return(ret);
    }
    else
      pbuf.msgtext = so_text(msgso);

    if(can_access(sig_path, READ_ACCESS) == 0
       && !(tmpso = so_get(FileStar, sig_path, READ_ACCESS))){
	char *problem = error_description(errno);

	sprintf(errbuf, "Error editing \"%s\": %s",
		sig_path, problem ? problem : "<NULL>");
	ret = cpystr(errbuf);

	dprint(1, (debugfile, "signature_edit: can't open %s: %s", sig_path,
		   problem ? problem : "<NULL>"));
	return(ret);
    }
    else if(tmpso){			/* else, fill pico's edit buffer */
	gf_set_so_readc(&gc, tmpso);	/* read from file, write pico buf */
	gf_set_so_writec(&pc, msgso);
	gf_filter_init();		/* no filters needed */
	if(errstr = gf_pipe(gc, pc)){
	    sprintf(errbuf, "Error reading file: \"%s\"", errstr);
	    ret = cpystr(errbuf);
	}

	gf_clear_so_readc(tmpso);
	gf_clear_so_writec(msgso);
	so_give(&tmpso);
    }

    if(!errstr){
#ifdef _WINDOWS
	mswin_setwindowmenu (MENU_COMPOSER);
#endif

	/*------ OK, Go edit the signature ------*/
	editor_result = pico(&pbuf);

#ifdef _WINDOWS
	mswin_setwindowmenu (MENU_DEFAULT);
#endif
	if(editor_result & COMP_GOTHUP){
	    hup_signal();		/* do what's normal for a hup */
	}
	else{
	    fix_windsize(ps_global);
	    init_signals();
	}

	if(editor_result & (COMP_SUSPEND | COMP_GOTHUP | COMP_CANCEL)){
	}
	else{
            /*------ Must have an edited buffer, write it to .sig -----*/
	    unlink(sig_path);		/* blast old copy */
	    if(tmpso = so_get(FileStar, sig_path, WRITE_ACCESS)){
		so_seek(msgso, 0L, 0);
		gf_set_so_readc(&gc, msgso);	/* read from pico buf */
		gf_set_so_writec(&pc, tmpso);	/* write sig file */
		gf_filter_init();		/* no filters needed */
		if(errstr = gf_pipe(gc, pc)){
		    sprintf(errbuf, "Error writing file: \"%s\"",
				      errstr);
		    ret = cpystr(errbuf);
		}

		gf_clear_so_readc(msgso);
		gf_clear_so_writec(tmpso);
		so_give(&tmpso);
	    }
	    else{
		sprintf(errbuf, "Error writing \"%s\"", sig_path);
		ret = cpystr(errbuf);
		dprint(1, (debugfile, "signature_edit: can't write %s",
			   sig_path));
	    }
	}
    }
    
    if(pbuf.colors)
      free_pcolors(&pbuf.colors);
    
    so_give(&msgso);
    return(ret);
}



/*
 *
 */
char *
sigedit_exit_for_pico(he, redraw_pico)
    struct headerentry *he;
    void (*redraw_pico)();
{
    int	      rv;
    char     *rstr = NULL;
    void    (*redraw)() = ps_global->redrawer;
    static ESCKEY_S opts[] = {
	{'y', 'y', "Y", "Yes"},
	{'n', 'n', "N", "No"},
	{-1, 0, NULL, NULL}
    };

    ps_global->redrawer = redraw_pico;
    fix_windsize(ps_global);

    while(1){
	rv = radio_buttons("Exit editor and apply changes? ",
			   -FOOTER_ROWS(ps_global), opts,
			   'y', 'x', NO_HELP, RB_NORM);
	if(rv == 'y'){				/* user ACCEPTS! */
	    break;
	}
	else if(rv == 'n'){			/* Declined! */
	    rstr = "No Changes Saved";
	    break;
	}
	else if(rv == 'x'){			/* Cancelled! */
	    rstr = "Exit Cancelled";
	    break;
	}
    }

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



/*
 *  * * * * *    Start of Config Screen Support Code   * * * * * 
 */

#define PREV_MENU {"P", "Prev", {MC_PREVITEM, 1, {'p'}}, KS_NONE}
#define NEXT_MENU {"N", "Next", {MC_NEXTITEM, 2, {'n','\t'}}, KS_NONE}
#define EXIT_SETUP_MENU \
	{"E", "Exit Setup", {MC_EXIT,1,{'e'}}, KS_EXITMODE}
#define TOGGLE_MENU \
	{"X", "[Set/Unset]", {MC_TOGGLE,3,{'x',ctrl('M'),ctrl('J')}}, KS_NONE}
#define TOGGLEB_MENU \
	{"X", "[Set/Unset]", {MC_TOGGLEB,3,{'x',ctrl('M'),ctrl('J')}}, KS_NONE}
#define TOGGLEC_MENU \
	{"X", "[Set/Unset]", {MC_TOGGLEC,3,{'x',ctrl('M'),ctrl('J')}}, KS_NONE}

static struct key config_text_keys[] = 
       {HELP_MENU,
	NULL_MENU,
	EXIT_SETUP_MENU,
	{"C", "[Change Val]", {MC_EDIT,3,{'c',ctrl('M'),ctrl('J')}}, KS_NONE},
	PREV_MENU,
	NEXT_MENU,
	PREVPAGE_MENU,
	NEXTPAGE_MENU,
	{"A", "Add Value", {MC_ADD,1,{'a'}}, KS_NONE},
	{"D", "Delete Val", {MC_DELETE,1,{'d'}}, KS_NONE},
	PRYNTTXT_MENU,
	WHEREIS_MENU};
INST_KEY_MENU(config_text_keymenu, config_text_keys);

static struct key color_pattern_keys[] = 
       {HELP_MENU,
	NULL_MENU,
	EXIT_SETUP_MENU,
	{"C", "[Change Val]", {MC_EDIT,3,{'c',ctrl('M'),ctrl('J')}}, KS_NONE},
	PREV_MENU,
	NEXT_MENU,
	PREVPAGE_MENU,
	NEXTPAGE_MENU,
	NULL_MENU,
	{"D", "Delete Val", {MC_DELETE,1,{'d'}}, KS_NONE},
	PRYNTTXT_MENU,
	WHEREIS_MENU};
INST_KEY_MENU(color_pattern_keymenu, color_pattern_keys);

static struct key config_role_file_keys[] = 
       {HELP_MENU,
	OTHER_MENU,
	EXIT_SETUP_MENU,
	{"C", "[Change Val]", {MC_EDIT,3,{'c',ctrl('M'),ctrl('J')}}, KS_NONE},
	PREV_MENU,
	NEXT_MENU,
	PREVPAGE_MENU,
	NEXTPAGE_MENU,
	{"A", "Add Value", {MC_ADD,1,{'a'}}, KS_NONE},
	{"D", "Delete Val", {MC_DELETE,1,{'d'}}, KS_NONE},
	PRYNTTXT_MENU,
	WHEREIS_MENU,

        HELP_MENU,
	OTHER_MENU,
	NULL_MENU,
	NULL_MENU,
	{"T", "ToFiles", {MC_CHOICE, 2, {'t', ctrl('T')}}, KS_NONE},
	{"F", "editFile", {MC_EDITFILE, 1, {'f'}}, KS_NONE},
	NULL_MENU,
	NULL_MENU,
	NULL_MENU,
	NULL_MENU,
	NULL_MENU,
	NULL_MENU};
INST_KEY_MENU(config_role_file_keymenu, config_role_file_keys);

static struct key config_role_file_res_keys[] = 
       {HELP_MENU,
	OTHER_MENU,
	EXIT_SETUP_MENU,
	{"C", "[Change Val]", {MC_EDIT,3,{'c',ctrl('M'),ctrl('J')}}, KS_NONE},
	PREV_MENU,
	NEXT_MENU,
	PREVPAGE_MENU,
	NEXTPAGE_MENU,
	{"A", "Add Value", {MC_ADD,1,{'a'}}, KS_NONE},
	{"D", "Delete Val", {MC_DELETE,1,{'d'}}, KS_NONE},
	PRYNTTXT_MENU,
	WHEREIS_MENU,

        HELP_MENU,
	OTHER_MENU,
	NULL_MENU,
	NULL_MENU,
	{"T", "ToFiles", {MC_CHOICE, 2, {'t', ctrl('T')}}, KS_NONE},
	NULL_MENU,
	NULL_MENU,
	NULL_MENU,
	NULL_MENU,
	NULL_MENU,
	NULL_MENU,
	NULL_MENU};
INST_KEY_MENU(config_role_file_res_keymenu, config_role_file_res_keys);

static struct key config_role_keys[] = 
       {HELP_MENU,
	OTHER_MENU,
	EXIT_SETUP_MENU,
	{"C", "[Change Val]", {MC_EDIT,3,{'c',ctrl('M'),ctrl('J')}}, KS_NONE},
	PREV_MENU,
	NEXT_MENU,
	PREVPAGE_MENU,
	NEXTPAGE_MENU,
	{"A", "Add Value", {MC_ADD,1,{'a'}}, KS_NONE},
	{"D", "Delete Val", {MC_DELETE,1,{'d'}}, KS_NONE},
	PRYNTTXT_MENU,
	WHEREIS_MENU,

        HELP_MENU,
	OTHER_MENU,
	NULL_MENU,
	NULL_MENU,
	NULL_MENU,
	{"X", "eXtraHdr", {MC_ADDHDR, 1, {'x'}}, KS_NONE},
	NULL_MENU,
	NULL_MENU,
	NULL_MENU,
	NULL_MENU,
	NULL_MENU,
	NULL_MENU};
INST_KEY_MENU(config_role_keymenu, config_role_keys);

static struct key config_role_addr_pat_keys[] = 
       {HELP_MENU,
	OTHER_MENU,
	EXIT_SETUP_MENU,
	{"C", "[Change Val]", {MC_EDIT,3,{'c',ctrl('M'),ctrl('J')}}, KS_NONE},
	PREV_MENU,
	NEXT_MENU,
	PREVPAGE_MENU,
	NEXTPAGE_MENU,
	{"A", "Add Value", {MC_ADD,1,{'a'}}, KS_NONE},
	{"D", "Delete Val", {MC_DELETE,1,{'d'}}, KS_NONE},
	PRYNTTXT_MENU,
	WHEREIS_MENU,

        HELP_MENU,
	OTHER_MENU,
	NULL_MENU,
	NULL_MENU,
	{"T", "ToAddrBk", {MC_CHOICEB, 2, {'t', ctrl('T')}}, KS_NONE},
	{"X", "eXtraHdr", {MC_ADDHDR, 1, {'x'}}, KS_NONE},
	NULL_MENU,
	NULL_MENU,
	NULL_MENU,
	NULL_MENU,
	NULL_MENU,
	NULL_MENU};
INST_KEY_MENU(config_role_addr_pat_keymenu, config_role_addr_pat_keys);

static struct key config_role_xtrahdr_keys[] = 
       {HELP_MENU,
	OTHER_MENU,
	EXIT_SETUP_MENU,
	{"C", "[Change Val]", {MC_EDIT,3,{'c',ctrl('M'),ctrl('J')}}, KS_NONE},
	PREV_MENU,
	NEXT_MENU,
	PREVPAGE_MENU,
	NEXTPAGE_MENU,
	{"A", "Add Value", {MC_ADD,1,{'a'}}, KS_NONE},
	{"D", "Delete Val", {MC_DELETE,1,{'d'}}, KS_NONE},
	PRYNTTXT_MENU,
	WHEREIS_MENU,

        HELP_MENU,
	OTHER_MENU,
	NULL_MENU,
	NULL_MENU,
	NULL_MENU,
	{"X", "eXtraHdr", {MC_ADDHDR, 1, {'x'}}, KS_NONE},
	NULL_MENU,
	NULL_MENU,
	{"R", "RemoveHdr", {MC_DELHDR, 1, {'r'}}, KS_NONE},
	NULL_MENU,
	NULL_MENU,
	NULL_MENU};
INST_KEY_MENU(config_role_xtrahdr_keymenu, config_role_xtrahdr_keys);

static struct key config_role_addr_act_keys[] = 
       {HELP_MENU,
	OTHER_MENU,
	EXIT_SETUP_MENU,
	{"C", "[Change Val]", {MC_EDIT,3,{'c',ctrl('M'),ctrl('J')}}, KS_NONE},
	PREV_MENU,
	NEXT_MENU,
	PREVPAGE_MENU,
	NEXTPAGE_MENU,
	{"A", "Add Value", {MC_ADD,1,{'a'}}, KS_NONE},
	{"D", "Delete Val", {MC_DELETE,1,{'d'}}, KS_NONE},
	PRYNTTXT_MENU,
	WHEREIS_MENU,

        HELP_MENU,
	OTHER_MENU,
	NULL_MENU,
	NULL_MENU,
	{"T", "ToAddrBk", {MC_CHOICEC, 2, {'t', ctrl('T')}}, KS_NONE},
	NULL_MENU,
	NULL_MENU,
	NULL_MENU,
	NULL_MENU,
	NULL_MENU,
	NULL_MENU,
	NULL_MENU};
INST_KEY_MENU(config_role_addr_act_keymenu, config_role_addr_act_keys);

static struct key config_role_patfolder_keys[] = 
       {HELP_MENU,
	OTHER_MENU,
	EXIT_SETUP_MENU,
	{"C", "[Change Val]", {MC_EDIT,3,{'c',ctrl('M'),ctrl('J')}}, KS_NONE},
	PREV_MENU,
	NEXT_MENU,
	PREVPAGE_MENU,
	NEXTPAGE_MENU,
	{"A", "Add Value", {MC_ADD,1,{'a'}}, KS_NONE},
	{"D", "Delete Val", {MC_DELETE,1,{'d'}}, KS_NONE},
	PRYNTTXT_MENU,
	WHEREIS_MENU,

        HELP_MENU,
	OTHER_MENU,
	NULL_MENU,
	NULL_MENU,
	{"T", "ToFldrs", {MC_CHOICED, 2, {'t', ctrl('T')}}, KS_NONE},
	NULL_MENU,
	NULL_MENU,
	NULL_MENU,
	NULL_MENU,
	NULL_MENU,
	NULL_MENU,
	NULL_MENU};
INST_KEY_MENU(config_role_patfolder_keymenu, config_role_patfolder_keys);

static struct key config_role_actionfolder_keys[] = 
       {HELP_MENU,
	OTHER_MENU,
	EXIT_SETUP_MENU,
	{"C", "[Change Val]", {MC_EDIT,3,{'c',ctrl('M'),ctrl('J')}}, KS_NONE},
	PREV_MENU,
	NEXT_MENU,
	PREVPAGE_MENU,
	NEXTPAGE_MENU,
	{"A", "Add Value", {MC_ADD,1,{'a'}}, KS_NONE},
	{"D", "Delete Val", {MC_DELETE,1,{'d'}}, KS_NONE},
	PRYNTTXT_MENU,
	WHEREIS_MENU,

        HELP_MENU,
	OTHER_MENU,
	NULL_MENU,
	NULL_MENU,
	{"T", "ToFldrs", {MC_CHOICEE, 2, {'t', ctrl('T')}}, KS_NONE},
	NULL_MENU,
	NULL_MENU,
	NULL_MENU,
	NULL_MENU,
	NULL_MENU,
	NULL_MENU,
	NULL_MENU};
INST_KEY_MENU(config_role_actionfolder_keymenu, config_role_actionfolder_keys);

static struct key config_role_inick_keys[] = 
       {HELP_MENU,
	OTHER_MENU,
	EXIT_SETUP_MENU,
	{"C", "[Change Val]", {MC_EDIT,3,{'c',ctrl('M'),ctrl('J')}}, KS_NONE},
	PREV_MENU,
	NEXT_MENU,
	PREVPAGE_MENU,
	NEXTPAGE_MENU,
	{"A", "Add Value", {MC_ADD,1,{'a'}}, KS_NONE},
	{"D", "Delete Val", {MC_DELETE,1,{'d'}}, KS_NONE},
	PRYNTTXT_MENU,
	WHEREIS_MENU,

        HELP_MENU,
	OTHER_MENU,
	NULL_MENU,
	NULL_MENU,
	{"T", "ToNicks", {MC_CHOICE, 2, {'t', ctrl('T')}}, KS_NONE},
	NULL_MENU,
	NULL_MENU,
	NULL_MENU,
	NULL_MENU,
	NULL_MENU,
	NULL_MENU,
	NULL_MENU};
INST_KEY_MENU(config_role_inick_keymenu, config_role_inick_keys);

static struct key config_checkbox_keys[] = 
       {HELP_MENU,
	NULL_MENU,
	EXIT_SETUP_MENU,
	TOGGLE_MENU,
	PREV_MENU,
	NEXT_MENU,
	PREVPAGE_MENU,
	NEXTPAGE_MENU,
	NULL_MENU,
	NULL_MENU,
	PRYNTTXT_MENU,
	WHEREIS_MENU};
INST_KEY_MENU(config_checkbox_keymenu, config_checkbox_keys);

static struct key hdr_color_checkbox_keys[] = 
       {HELP_MENU,
	NULL_MENU,
	EXIT_SETUP_MENU,
	TOGGLEB_MENU,
	PREV_MENU,
	NEXT_MENU,
	PREVPAGE_MENU,
	NEXTPAGE_MENU,
	NULL_MENU,
	NULL_MENU,
	PRYNTTXT_MENU,
	WHEREIS_MENU};
INST_KEY_MENU(hdr_color_checkbox_keymenu, hdr_color_checkbox_keys);

static struct key selectable_bold_checkbox_keys[] = 
       {HELP_MENU,
	NULL_MENU,
	EXIT_SETUP_MENU,
	TOGGLEC_MENU,
	PREV_MENU,
	NEXT_MENU,
	PREVPAGE_MENU,
	NEXTPAGE_MENU,
	NULL_MENU,
	NULL_MENU,
	PRYNTTXT_MENU,
	WHEREIS_MENU};
INST_KEY_MENU(selectable_bold_checkbox_keymenu, selectable_bold_checkbox_keys);

static struct key config_radiobutton_keys[] = 
       {HELP_MENU,
	NULL_MENU,
	EXIT_SETUP_MENU,
	{"*", "[Select]", {MC_CHOICE,3,{'*',ctrl('M'),ctrl('J')}}, KS_NONE},
	PREV_MENU,
	NEXT_MENU,
	PREVPAGE_MENU,
	NEXTPAGE_MENU,
	NULL_MENU,
	NULL_MENU,
	PRYNTTXT_MENU,
	WHEREIS_MENU};
INST_KEY_MENU(config_radiobutton_keymenu, config_radiobutton_keys);

static struct key config_yesno_keys[] = 
       {HELP_MENU,
	NULL_MENU,
	EXIT_SETUP_MENU,
	{"C", "[Change]", {MC_TOGGLE,3,{'c',ctrl('M'),ctrl('J')}}, KS_NONE},
	PREV_MENU,
	NEXT_MENU,
	PREVPAGE_MENU,
	NEXTPAGE_MENU,
	NULL_MENU,
	NULL_MENU,
	PRYNTTXT_MENU,
	WHEREIS_MENU};
INST_KEY_MENU(config_yesno_keymenu, config_yesno_keys);


static struct key color_changing_keys[] = 
       {HELP_MENU,
	NULL_MENU,
	{"E", "To Colors", {MC_EXIT,1,{'e'}}, KS_EXITMODE},
	{"*", "[Select]", {MC_CHOICE,3,{'*',ctrl('M'),ctrl('J')}}, KS_NONE},
	PREV_MENU,
	NEXT_MENU,
	PREVPAGE_MENU,
	NEXTPAGE_MENU,
	NULL_MENU,
	NULL_MENU,
	PRYNTTXT_MENU,
	WHEREIS_MENU};
INST_KEY_MENU(color_changing_keymenu, color_changing_keys);


static struct key custom_color_changing_keys[] = 
       {HELP_MENU,
	NULL_MENU,
	{"E", "To Colors", {MC_EXIT,1,{'e'}}, KS_EXITMODE},
	{"*", "[Select]", {MC_CHOICEB,3,{'*',ctrl('M'),ctrl('J')}}, KS_NONE},
	PREV_MENU,
	NEXT_MENU,
	PREVPAGE_MENU,
	NEXTPAGE_MENU,
	NULL_MENU,
	NULL_MENU,
	PRYNTTXT_MENU,
	WHEREIS_MENU};
INST_KEY_MENU(custom_color_changing_keymenu, custom_color_changing_keys);


static struct key color_rgb_changing_keys[] = 
       {HELP_MENU,
	NULL_MENU,
	{"E", "To Colors", {MC_EXIT,1,{'e'}}, KS_EXITMODE},
	{"*", "[Select]", {MC_CHOICE,3,{'*',ctrl('M'),ctrl('J')}}, KS_NONE},
	PREV_MENU,
	NEXT_MENU,
	PREVPAGE_MENU,
	NEXTPAGE_MENU,
	{"C", "Customize", {MC_RGB1,1,{'c'}},KS_NONE},
	NULL_MENU,
	PRYNTTXT_MENU,
	WHEREIS_MENU};
INST_KEY_MENU(color_rgb_keymenu, color_rgb_changing_keys);
#define	RGB_CUSTOM_KEY	8


static struct key custom_rgb_changing_keys[] = 
       {HELP_MENU,
	NULL_MENU,
	{"E", "To Colors", {MC_EXIT,1,{'e'}}, KS_EXITMODE},
	{"*", "[Select]", {MC_CHOICEB,3,{'*',ctrl('M'),ctrl('J')}}, KS_NONE},
	PREV_MENU,
	NEXT_MENU,
	PREVPAGE_MENU,
	NEXTPAGE_MENU,
	{"C", "Customize", {MC_RGB2,1,{'c'}},KS_NONE},
	NULL_MENU,
	PRYNTTXT_MENU,
	WHEREIS_MENU};
INST_KEY_MENU(custom_rgb_keymenu, custom_rgb_changing_keys);


static struct key color_setting_keys[] = 
       {HELP_MENU,
	NULL_MENU,
	EXIT_SETUP_MENU,
	{"C", "[Change]", {MC_EDIT,3,{'c',ctrl('M'),ctrl('J')}}, KS_NONE},
	PREV_MENU,
	NEXT_MENU,
	PREVPAGE_MENU,
	NEXTPAGE_MENU,
	{"A", "AddHeader", {MC_ADD,1,{'a'}}, KS_NONE},
	{"R", "RestoreDefs", {MC_DEFAULT,1,{'r'}}, KS_NONE},
	PRYNTTXT_MENU,
	WHEREIS_MENU};
INST_KEY_MENU(color_setting_keymenu, color_setting_keys);

static struct key custom_color_setting_keys[] = 
       {HELP_MENU,
	OTHER_MENU,
	EXIT_SETUP_MENU,
	{"C", "[Change]", {MC_EDIT,3,{'c',ctrl('M'),ctrl('J')}}, KS_NONE},
	PREV_MENU,
	NEXT_MENU,
	PREVPAGE_MENU,
	NEXTPAGE_MENU,
	{"A", "AddHeader", {MC_ADD,1,{'a'}}, KS_NONE},
	{"R", "RestoreDefs", {MC_DEFAULT,1,{'r'}}, KS_NONE},
	PRYNTTXT_MENU,
	WHEREIS_MENU,

        HELP_MENU,
	OTHER_MENU,
	NULL_MENU,
	NULL_MENU,
	{"D", "DeleteHdr", {MC_DELETE,1,{'d'}}, KS_NONE},
	{"$", "ShuffleHdr", {MC_SHUFFLE,1,{'$'}}, KS_NONE},
	NULL_MENU,
	NULL_MENU,
	NULL_MENU,
	NULL_MENU,
	NULL_MENU,
	NULL_MENU};
INST_KEY_MENU(custom_color_setting_keymenu, custom_color_setting_keys);

static struct key role_color_setting_keys[] = 
       {HELP_MENU,
	NULL_MENU,
	EXIT_SETUP_MENU,
	{"*", "[Select]", {MC_CHOICE,3,{'*',ctrl('M'),ctrl('J')}}, KS_NONE},
	PREV_MENU,
	NEXT_MENU,
	PREVPAGE_MENU,
	NEXTPAGE_MENU,
	NULL_MENU,
	NULL_MENU,
	PRYNTTXT_MENU,
	WHEREIS_MENU};
INST_KEY_MENU(role_color_setting_keymenu, role_color_setting_keys);






/*----------------------------------------------------------------------
    Present pinerc data for manipulation

    Args: None

  Result: help edit certain pinerc fields.
  ---*/
void
option_screen(ps)
    struct pine *ps;
{
    char	    tmp[MAXPATH+1];
    int		    i, j, ln = 0, lv;
    struct	    variable  *vtmp;
    CONF_S	   *ctmpa = NULL, *ctmpb, *first_line = NULL;
    NAMEVAL_S	   *f;
    FEATURE_S	   *feature;
    SAVED_CONFIG_S *vsave;
    OPT_SCREEN_S    screen;

    ps->next_screen = SCREEN_FUN_NULL;

    mailcap_free(); /* free resources we won't be using for a while */

    if(ps->fix_fixed_warning && offer_to_fix_pinerc(ps))
      write_pinerc(ps);

    /*
     * First, find longest variable name
     */
    for(vtmp = ps->vars; vtmp->name; vtmp++){
	if(exclude_config_var(ps, vtmp))
	  continue;

	if((i = strlen(vtmp->name)) > ln)
	  ln = i;
    }

    /*
     * Next, allocate and initialize config line list...
     */
    for(vtmp = ps->vars; vtmp->name; vtmp++){
	if(exclude_config_var(ps, vtmp))
	  continue;

	new_confline(&ctmpa)->var = vtmp;
	if(!first_line)
	  first_line = ctmpa;

	ctmpa->valoffset = ln + 3;
	ctmpa->keymenu	 = &config_text_keymenu;
	ctmpa->help	 = config_help(vtmp - ps->vars, 0);
	ctmpa->tool	 = text_tool;

	sprintf(tmp, "%-*s =", ln, vtmp->name);
	ctmpa->varname  = cpystr(tmp);
	ctmpa->varnamep = ctmpb = ctmpa;
	ctmpa->flags   |= CF_STARTITEM;
	if(vtmp == &ps->vars[V_FEATURE_LIST]){	/* special checkbox case */
	    char *this_sect, *new_sect;

	    ctmpa->flags       |= CF_NOSELECT;
	    ctmpa->keymenu      = &config_checkbox_keymenu;
	    ctmpa->tool		= NULL;

	    /* put a nice delimiter before list */
	    new_confline(&ctmpa)->var = NULL;
	    ctmpa->varnamep		  = ctmpb;
	    ctmpa->keymenu		  = &config_checkbox_keymenu;
	    ctmpa->help			  = NO_HELP;
	    ctmpa->tool			  = checkbox_tool;
	    ctmpa->valoffset		  = 12;
	    ctmpa->flags		 |= CF_NOSELECT;
	    ctmpa->value = cpystr("Set    Feature Name");

	    new_confline(&ctmpa)->var = NULL;
	    ctmpa->varnamep		  = ctmpb;
	    ctmpa->keymenu		  = &config_checkbox_keymenu;
	    ctmpa->help			  = NO_HELP;
	    ctmpa->tool			  = checkbox_tool;
	    ctmpa->valoffset		  = 12;
	    ctmpa->flags		 |= CF_NOSELECT;
	    ctmpa->value = cpystr("---  ----------------------");

	    /* find longest value's name */
	    for(lv = 0, i = 0; feature = feature_list(i); i++)
	      if(feature_list_section(feature)
		 && lv < (j = strlen(feature->name)))
		lv = j;
	    
	    for(i = 0, this_sect = NULL; feature = feature_list(i); i++)
	      if(new_sect = feature_list_section(feature)){
		  if(this_sect != new_sect){
		      new_confline(&ctmpa)->var = NULL;
		      ctmpa->varnamep		= ctmpb;
		      ctmpa->keymenu		= &config_checkbox_keymenu;
		      ctmpa->help		= NO_HELP;
		      ctmpa->tool		= checkbox_tool;
		      ctmpa->valoffset		= 2;
		      ctmpa->flags	       |= CF_NOSELECT;
		      sprintf(tmp, "[ %s ]", this_sect = new_sect);
		      ctmpa->value = cpystr(tmp);
		  }

		  new_confline(&ctmpa)->var = vtmp;
		  ctmpa->varnamep	    = ctmpb;
		  ctmpa->keymenu	    = &config_checkbox_keymenu;
		  ctmpa->help		    = config_help(vtmp-ps->vars,
							  feature->id);
		  ctmpa->tool		    = checkbox_tool;
		  ctmpa->valoffset	    = 12;
		  ctmpa->varmem		    = i;
		  sprintf(tmp, "[%c]  %-*.*s",
			  F_ON(feature->id, ps) ? 'X' : ' ',
			  lv, lv, feature->name);
		  ctmpa->value = cpystr(tmp);
	      }
	}
	else if(vtmp == &ps->vars[V_SAVED_MSG_NAME_RULE]){ /* radio case */
	    ctmpa->flags       |= CF_NOSELECT;
	    ctmpa->keymenu      = &config_radiobutton_keymenu;
	    ctmpa->tool		= NULL;

	    /* put a nice delimiter before list */
	    new_confline(&ctmpa)->var = NULL;
	    ctmpa->varnamep		  = ctmpb;
	    ctmpa->keymenu		  = &config_radiobutton_keymenu;
	    ctmpa->help			  = NO_HELP;
	    ctmpa->tool			  = radiobutton_tool;
	    ctmpa->valoffset		  = 12;
	    ctmpa->flags		 |= CF_NOSELECT;
	    ctmpa->value = cpystr("Set    Rule Values");

	    new_confline(&ctmpa)->var = NULL;
	    ctmpa->varnamep		  = ctmpb;
	    ctmpa->keymenu		  = &config_radiobutton_keymenu;
	    ctmpa->help			  = NO_HELP;
	    ctmpa->tool			  = radiobutton_tool;
	    ctmpa->valoffset		  = 12;
	    ctmpa->flags		 |= CF_NOSELECT;
	    ctmpa->value = cpystr("---  ----------------------");

	    /* find longest value's name */
	    for(lv = 0, i = 0; f = save_msg_rules(i); i++)
	      if(lv < (j = strlen(f->name)))
		lv = j;
	    
	    for(i = 0; f = save_msg_rules(i); i++){
		new_confline(&ctmpa)->var = vtmp;
		ctmpa->varnamep		      = ctmpb;
		ctmpa->keymenu		      = &config_radiobutton_keymenu;
		ctmpa->help		      = config_help(vtmp - ps->vars,0);
		ctmpa->tool		      = radiobutton_tool;
		ctmpa->valoffset	      = 12;
		ctmpa->varmem		      = i;
		sprintf(tmp, "(%c)  %-*.*s",
			(ps->save_msg_rule == f->value) ? R_SELD : ' ',
			lv, lv, f->name);
		ctmpa->value = cpystr(tmp);
	    }
	}
	else if(vtmp == &ps->vars[V_FCC_RULE]){		/* radio case */
	    ctmpa->flags       |= CF_NOSELECT;
	    ctmpa->keymenu      = &config_radiobutton_keymenu;
	    ctmpa->tool		= NULL;

	    /* put a nice delimiter before list */
	    new_confline(&ctmpa)->var = NULL;
	    ctmpa->varnamep		  = ctmpb;
	    ctmpa->keymenu		  = &config_radiobutton_keymenu;
	    ctmpa->help			  = NO_HELP;
	    ctmpa->tool			  = radiobutton_tool;
	    ctmpa->valoffset		  = 12;
	    ctmpa->flags		 |= CF_NOSELECT;
	    ctmpa->value = cpystr("Set    Rule Values");

	    new_confline(&ctmpa)->var = NULL;
	    ctmpa->varnamep		  = ctmpb;
	    ctmpa->keymenu		  = &config_radiobutton_keymenu;
	    ctmpa->help			  = NO_HELP;
	    ctmpa->tool			  = radiobutton_tool;
	    ctmpa->valoffset		  = 12;
	    ctmpa->flags		 |= CF_NOSELECT;
	    ctmpa->value = cpystr("---  ----------------------");

	    /* find longest value's name */
	    for(lv = 0, i = 0; f = fcc_rules(i); i++)
	      if(lv < (j = strlen(f->name)))
		lv = j;
	    
	    for(i = 0; f = fcc_rules(i); i++){
		new_confline(&ctmpa)->var = vtmp;
		ctmpa->varnamep		      = ctmpb;
		ctmpa->keymenu		      = &config_radiobutton_keymenu;
		ctmpa->help		      = config_help(vtmp - ps->vars,0);
		ctmpa->tool		      = radiobutton_tool;
		ctmpa->valoffset	      = 12;
		ctmpa->varmem		      = i;
		sprintf(tmp, "(%c)  %-*.*s",
			(ps->fcc_rule == f->value) ? R_SELD : ' ',
			lv, lv, f->name);
		ctmpa->value = cpystr(tmp);
	    }
	}
	else if(vtmp == &ps->vars[V_SORT_KEY]){ /* radio case */
	    ctmpa->flags       |= CF_NOSELECT;
	    ctmpa->keymenu      = &config_radiobutton_keymenu;
	    ctmpa->tool		= NULL;

	    /* put a nice delimiter before list */
	    new_confline(&ctmpa)->var = NULL;
	    ctmpa->varnamep		  = ctmpb;
	    ctmpa->keymenu		  = &config_radiobutton_keymenu;
	    ctmpa->help			  = NO_HELP;
	    ctmpa->tool			  = radiobutton_tool;
	    ctmpa->valoffset		  = 12;
	    ctmpa->flags		 |= CF_NOSELECT;
	    ctmpa->value = cpystr("Set    Sort Options");

	    new_confline(&ctmpa)->var = NULL;
	    ctmpa->varnamep	      = ctmpb;
	    ctmpa->keymenu	      = &config_radiobutton_keymenu;
	    ctmpa->help		      = NO_HELP;
	    ctmpa->tool		      = radiobutton_tool;
	    ctmpa->valoffset	      = 12;
	    ctmpa->flags             |= CF_NOSELECT;
	    ctmpa->value = cpystr("---  ----------------------");

	    /* find longest value's name */
	    for(lv = 0, i = 0; ps->sort_types[i] != EndofList; i++)
	      if(lv < (j = strlen(sort_name(i))))
		lv = j;
	    
	    for(j = 0; j < 2; j++){
		for(i = 0; ps->sort_types[i] != EndofList; i++){
		    new_confline(&ctmpa)->var = vtmp;
		    ctmpa->varnamep  = ctmpb;
		    ctmpa->keymenu   = &config_radiobutton_keymenu;
		    ctmpa->help	     = config_help(vtmp - ps->vars, 0);
		    ctmpa->tool	     = radiobutton_tool;
		    ctmpa->valoffset = 12;

		    /*
		     * varmem == sort_type index (reverse doubles index)
		     */
		    ctmpa->varmem = i + (j * EndofList);
		    sprintf(tmp, "(%c)  %s%-*s%*s",
			    (ps->def_sort == (SortOrder) i
						  && ps->def_sort_rev == j)
			      ? R_SELD : ' ',
			    (j) ? "Reverse " : "",
			    lv, sort_name(i),
			    (j) ? 0 : 8, "");
		    ctmpa->value = cpystr(tmp);
		}
	    }
	}
	else if(vtmp == &ps->vars[V_AB_SORT_RULE]){	/* radio case */
	    ctmpa->flags       |= CF_NOSELECT;
	    ctmpa->keymenu      = &config_radiobutton_keymenu;
	    ctmpa->tool		= NULL;

	    /* put a nice delimiter before list */
	    new_confline(&ctmpa)->var = NULL;
	    ctmpa->varnamep	      = ctmpb;
	    ctmpa->keymenu	      = &config_radiobutton_keymenu;
	    ctmpa->help		      = NO_HELP;
	    ctmpa->tool		      = radiobutton_tool;
	    ctmpa->valoffset	      = 12;
	    ctmpa->flags             |= CF_NOSELECT;
	    ctmpa->value = cpystr("Set    Rule Values");

	    new_confline(&ctmpa)->var = NULL;
	    ctmpa->varnamep	      = ctmpb;
	    ctmpa->keymenu	      = &config_radiobutton_keymenu;
	    ctmpa->help		      = NO_HELP;
	    ctmpa->tool		      = radiobutton_tool;
	    ctmpa->valoffset	      = 12;
	    ctmpa->flags             |= CF_NOSELECT;
	    ctmpa->value = cpystr("---  ----------------------");

	    /* find longest value's name */
	    for(lv = 0, i = 0; f = ab_sort_rules(i); i++)
	      if(lv < (j = strlen(f->name)))
		lv = j;
	    
	    for(i = 0; f = ab_sort_rules(i); i++){
		new_confline(&ctmpa)->var = vtmp;
		ctmpa->varnamep		  = ctmpb;
		ctmpa->keymenu		  = &config_radiobutton_keymenu;
		ctmpa->help		  = config_help(vtmp - ps->vars, 0);
		ctmpa->tool		  = radiobutton_tool;
		ctmpa->valoffset	  = 12;
		ctmpa->varmem		  = i;
		sprintf(tmp, "(%c)  %-*.*s",
			(ps->ab_sort_rule == f->value) ? R_SELD : ' ',
			lv, lv, f->name);
		ctmpa->value = cpystr(tmp);
	    }
	}
	else if(vtmp == &ps->vars[V_FLD_SORT_RULE]){	/* radio case */
	    ctmpa->flags       |= CF_NOSELECT;
	    ctmpa->keymenu      = &config_radiobutton_keymenu;
	    ctmpa->tool		= NULL;

	    /* put a nice delimiter before list */
	    new_confline(&ctmpa)->var = NULL;
	    ctmpa->varnamep	      = ctmpb;
	    ctmpa->keymenu	      = &config_radiobutton_keymenu;
	    ctmpa->help		      = NO_HELP;
	    ctmpa->tool		      = radiobutton_tool;
	    ctmpa->valoffset	      = 12;
	    ctmpa->flags             |= CF_NOSELECT;
	    ctmpa->value = cpystr("Set    Rule Values");

	    new_confline(&ctmpa)->var = NULL;
	    ctmpa->varnamep	      = ctmpb;
	    ctmpa->keymenu	      = &config_radiobutton_keymenu;
	    ctmpa->help		      = NO_HELP;
	    ctmpa->tool		      = radiobutton_tool;
	    ctmpa->valoffset	      = 12;
	    ctmpa->flags             |= CF_NOSELECT;
	    ctmpa->value = cpystr("---  ----------------------");

	    /* find longest value's name */
	    for(lv = 0, i = 0; f = fld_sort_rules(i); i++)
	      if(lv < (j = strlen(f->name)))
		lv = j;
	    
	    for(i = 0; f = fld_sort_rules(i); i++){
		new_confline(&ctmpa)->var = vtmp;
		ctmpa->varnamep		  = ctmpb;
		ctmpa->keymenu		  = &config_radiobutton_keymenu;
		ctmpa->help		  = config_help(vtmp - ps->vars, 0);
		ctmpa->tool		  = radiobutton_tool;
		ctmpa->valoffset	  = 12;
		ctmpa->varmem		  = i;
		sprintf(tmp, "(%c)  %-*.*s",
			(ps->fld_sort_rule == f->value) ? R_SELD : ' ',
			lv, lv, f->name);
		ctmpa->value = cpystr(tmp);
	    }
	}
	else if(vtmp == &ps->vars[V_INCOMING_STARTUP]){ /* radio case */
	    ctmpa->flags       |= CF_NOSELECT;
	    ctmpa->keymenu      = &config_radiobutton_keymenu;
	    ctmpa->tool		= NULL;

	    /* put a nice delimiter before list */
	    new_confline(&ctmpa)->var = NULL;
	    ctmpa->varnamep	      = ctmpb;
	    ctmpa->keymenu	      = &config_radiobutton_keymenu;
	    ctmpa->help		      = NO_HELP;
	    ctmpa->tool		      = radiobutton_tool;
	    ctmpa->valoffset	      = 12;
	    ctmpa->flags             |= CF_NOSELECT;
	    ctmpa->value = cpystr("Set    Rule Values");

	    new_confline(&ctmpa)->var = NULL;
	    ctmpa->varnamep	      = ctmpb;
	    ctmpa->keymenu	      = &config_radiobutton_keymenu;
	    ctmpa->help		      = NO_HELP;
	    ctmpa->tool		      = radiobutton_tool;
	    ctmpa->valoffset	      = 12;
	    ctmpa->flags             |= CF_NOSELECT;
	    ctmpa->value = cpystr("---  ----------------------");

	    /* find longest value's name */
	    for(lv = 0, i = 0; f = incoming_startup_rules(i); i++)
	      if(lv < (j = strlen(f->name)))
		lv = j;
	    
	    for(i = 0; f = incoming_startup_rules(i); i++){
		new_confline(&ctmpa)->var = vtmp;
		ctmpa->varnamep		  = ctmpb;
		ctmpa->keymenu		  = &config_radiobutton_keymenu;
		ctmpa->help		  = config_help(vtmp - ps->vars, 0);
		ctmpa->tool		  = radiobutton_tool;
		ctmpa->valoffset	  = 12;
		ctmpa->varmem		  = i;
		sprintf(tmp, "(%c)  %-*.*s",
			(ps->inc_startup_rule == f->value) ? R_SELD : ' ',
			lv, lv, f->name);
		ctmpa->value = cpystr(tmp);
	    }
	}
	else if(vtmp == &ps->vars[V_GOTO_DEFAULT_RULE]){ /* radio case */
	    ctmpa->flags       |= CF_NOSELECT;
	    ctmpa->keymenu      = &config_radiobutton_keymenu;
	    ctmpa->tool		= NULL;

	    /* put a nice delimiter before list */
	    new_confline(&ctmpa)->var = NULL;
	    ctmpa->varnamep	      = ctmpb;
	    ctmpa->keymenu	      = &config_radiobutton_keymenu;
	    ctmpa->help		      = NO_HELP;
	    ctmpa->tool		      = radiobutton_tool;
	    ctmpa->valoffset	      = 12;
	    ctmpa->flags             |= CF_NOSELECT;
	    ctmpa->value = cpystr("Set    Rule Values");

	    new_confline(&ctmpa)->var = NULL;
	    ctmpa->varnamep	      = ctmpb;
	    ctmpa->keymenu	      = &config_radiobutton_keymenu;
	    ctmpa->help		      = NO_HELP;
	    ctmpa->tool		      = radiobutton_tool;
	    ctmpa->valoffset	      = 12;
	    ctmpa->flags             |= CF_NOSELECT;
	    ctmpa->value = cpystr("---  ----------------------");

	    /* find longest value's name */
	    for(lv = 0, i = 0; f = goto_rules(i); i++)
	      if(lv < (j = strlen(f->name)))
		lv = j;
	    
	    for(i = 0; f = goto_rules(i); i++){
		new_confline(&ctmpa)->var = vtmp;
		ctmpa->varnamep		  = ctmpb;
		ctmpa->keymenu		  = &config_radiobutton_keymenu;
		ctmpa->help		  = config_help(vtmp - ps->vars, 0);
		ctmpa->tool		  = radiobutton_tool;
		ctmpa->valoffset	  = 12;
		ctmpa->varmem		  = i;
		sprintf(tmp, "(%c)  %-*.*s",
			(ps->goto_default_rule == f->value) ? R_SELD : ' ',
			lv, lv, f->name);
		ctmpa->value = cpystr(tmp);
	    }
	}
	else if(vtmp == &ps->vars[V_USE_ONLY_DOMAIN_NAME]){ /* yesno case */
	    ctmpa->keymenu = &config_yesno_keymenu;
	    ctmpa->tool	   = yesno_tool;
	    if(vtmp->user_val.p && !strucmp(vtmp->user_val.p, "yes")
	       || (!vtmp->user_val.p && vtmp->current_val.p
		   && !strucmp(vtmp->current_val.p, "yes")))
	      sprintf(tmp, "Yes%*s", ps->ttyo->screen_cols - ln - 3, "");
	    else
	      sprintf(tmp, "No%*s", ps->ttyo->screen_cols - ln - 2, "");

	    ctmpa->value = cpystr(tmp);
	}
	else if(vtmp->is_list){
	    if(vtmp->user_val.l){
		for(i = 0; vtmp->user_val.l[i]; i++){
		    if(i)
		      (void)new_confline(&ctmpa);

		    ctmpa->var       = vtmp;
		    ctmpa->varmem    = i;
		    ctmpa->valoffset = ln + 3;
		    ctmpa->value     = pretty_value(ps, ctmpa);
		    ctmpa->keymenu   = &config_text_keymenu;
		    ctmpa->help      = config_help(vtmp - ps->vars, 0);
		    ctmpa->tool      = text_tool;
		    ctmpa->varnamep  = ctmpb;
		}
	    }
	    else{
		ctmpa->varmem = 0;
		ctmpa->value  = pretty_value(ps, ctmpa);
	    }
	}
	else{
	    if(vtmp == &ps->vars[V_FILLCOL]
	       || vtmp == &ps->vars[V_OVERLAP]
	       || vtmp == &ps->vars[V_MARGIN]
	       || vtmp == &ps->vars[V_STATUS_MSG_DELAY]
	       || vtmp == &ps->vars[V_MAILCHECK])
	      ctmpa->flags |= CF_NUMBER;

	    ctmpa->value = pretty_value(ps, ctmpa);
	}
    }

    vsave = save_config_vars(ps);
    first_line = first_sel_confline(first_line);
    switch(conf_scroll_screen(ps, &screen, first_line,
			      CONFIG_SCREEN_TITLE, "configuration ", 1, 0)){
      case 0:
	break;

      case 1:
	write_pinerc(ps);
	break;
    
      case 10:
	revert_to_saved_config(ps, vsave);
	break;
      
      default:
	q_status_message(SM_ORDER,7,10,
	    "conf_scroll_screen bad ret, not supposed to happen");
	break;
    }

    if(vsave[V_SORT_KEY].user_val.p && ps->vars[V_SORT_KEY].user_val.p
       && strcmp(vsave[V_SORT_KEY].user_val.p,
		 ps->vars[V_SORT_KEY].user_val.p)){
	clear_index_cache();
	sort_folder(ps_global->msgmap, ps_global->def_sort,
		    ps_global->def_sort_rev, TRUE);
    }

    free_saved_config(ps, &vsave);
#ifdef _WINDOWS
    mswin_set_quit_confirm (F_OFF(F_QUIT_WO_CONFIRM, ps_global));
#endif
}


/*
 * test whether or not a var is 
 *
 * returns:  1 if it should be excluded, 0 otw
 */
int
exclude_config_var(ps, var)
    struct pine *ps;
    struct variable *var;
{
    switch(var - ps->vars){
      case V_MAIL_DIRECTORY :
      case V_INCOMING_FOLDERS :
      case V_FOLDER_SPEC :
      case V_NEWS_SPEC :
      case V_PRINTER :
      case V_PERSONAL_PRINT_COMMAND :
      case V_PERSONAL_PRINT_CATEGORY :
      case V_STANDARD_PRINTER :
      case V_LAST_TIME_PRUNE_QUESTION :
      case V_LAST_VERS_USED :
      case V_ADDRESSBOOK :
      case V_GLOB_ADDRBOOK :
      case V_DISABLE_DRIVERS :
      case V_REMOTE_ABOOK_METADATA :
      case V_REMOTE_ABOOK_HISTORY :
      case V_REMOTE_ABOOK_VALIDITY :
      case V_OPER_DIR :
      case V_USERINPUTTIMEO :
      case V_TCPOPENTIMEO :
      case V_RSHCMD :
      case V_RSHPATH :
      case V_RSHOPENTIMEO :
      case V_SSHCMD :
      case V_SSHPATH :
      case V_SSHOPENTIMEO :
      case V_SENDMAIL_PATH :
      case V_NEW_VER_QUELL :
      case V_PATTERNS :
#if defined(DOS) || defined(OS2)
      case V_UPLOAD_CMD :
      case V_UPLOAD_CMD_PREFIX :
      case V_DOWNLOAD_CMD :
      case V_DOWNLOAD_CMD_PREFIX :
#ifdef	_WINDOWS
      case V_FONT_NAME :
      case V_FONT_SIZE :
      case V_FONT_STYLE :
      case V_PRINT_FONT_NAME :
      case V_PRINT_FONT_SIZE :
      case V_PRINT_FONT_STYLE :
      case V_WINDOW_POSITION :
      case V_CURSOR_STYLE :
#endif	/* _WINDOWS */
#endif	/* DOS */
#ifdef	ENABLE_LDAP
      case V_LDAP_SERVERS :
#endif	/* ENABLE_LDAP */
	return(1);

      default:
	break;
    }

    return(!(var->is_user && var->is_used && !var->is_obsolete &&
	     !color_related_var(ps, var)));
}


/*
 * Test to indicate what should be saved in case user wants to abandon
 * changes.
 */
int
save_include(ps, v)
    struct pine     *ps;
    struct variable *v;
{
    return(!exclude_config_var(ps, v)
	   || (v->is_user
	    && v->is_used
	    && !v->is_obsolete
	    && (v == &ps->vars[V_PERSONAL_PRINT_COMMAND]
#ifdef	ENABLE_LDAP
	     || v == &ps->vars[V_LDAP_SERVERS]
#endif
		)));
}


#ifndef	DOS
static struct key printer_edit_keys[] = 
       {HELP_MENU,
	PRYNTTXT_MENU,
	EXIT_SETUP_MENU,
	{"S", "[Select]", {MC_CHOICE,3,{'s',ctrl('M'),ctrl('J')}}, KS_NONE},
	PREV_MENU,
	NEXT_MENU,
	PREVPAGE_MENU,
	NEXTPAGE_MENU,
	{"A", "Add Printer", {MC_ADD,1,{'a'}}, KS_NONE},
	{"D", "DeletePrint", {MC_DELETE,1,{'d'}}, KS_NONE},
	{"C", "Change", {MC_EDIT,1,{'c'}}, KS_NONE},
	WHEREIS_MENU};
INST_KEY_MENU(printer_edit_keymenu, printer_edit_keys);

static struct key printer_select_keys[] = 
       {HELP_MENU,
	PRYNTTXT_MENU,
	EXIT_SETUP_MENU,
	{"S", "[Select]", {MC_CHOICE,3,{'s',ctrl('M'),ctrl('J')}}, KS_NONE},
	PREV_MENU,
	NEXT_MENU,
	PREVPAGE_MENU,
	NEXTPAGE_MENU,
	NULL_MENU,
	NULL_MENU,
	NULL_MENU,
	WHEREIS_MENU};
INST_KEY_MENU(printer_select_keymenu, printer_select_keys);

/*
 * Information used to paint and maintain a line on the configuration screen
 */
/*----------------------------------------------------------------------
    The printer selection screen

   Draws the screen and prompts for the printer number and the custom
   command if so selected.

 ----*/
void
select_printer(ps) 
    struct pine *ps;
{
    struct        variable  *vtmp;
    CONF_S       *ctmpa = NULL, *ctmpb = NULL, *heading = NULL,
		 *start_line = NULL;
    int i, saved_printer_cat;
    SAVED_CONFIG_S *vsave;
    char *saved_printer;
    OPT_SCREEN_S screen;

    if(fixed_var(&ps_global->vars[V_PRINTER], "change", "printer"))
      return;

    saved_printer = cpystr(ps->VAR_PRINTER);
    saved_printer_cat = ps->printer_category;

    new_confline(&ctmpa);
    ctmpa->valoffset = 2;
    ctmpa->keymenu   = &printer_select_keymenu;
    ctmpa->help      = NO_HELP;
    ctmpa->tool      = print_select_tool;
    ctmpa->flags    |= CF_NOSELECT;
    ctmpa->value
#ifdef OS2
      = cpystr("\"Select\" a port or |pipe-command as your default printer.");
#else
      = cpystr("You may \"Select\" a print command as your default printer.");
#endif

    new_confline(&ctmpa);
    ctmpa->valoffset = 2;
    ctmpa->keymenu   = &printer_select_keymenu;
    ctmpa->help      = NO_HELP;
    ctmpa->tool      = print_select_tool;
    ctmpa->flags    |= CF_NOSELECT;
    ctmpa->value
#ifdef OS2
      = cpystr("You may also add alternative ports or !pipes to the list in the \"Personally\"");
#else
      = cpystr("You may also add custom print commands to the list in the \"Personally\"");
#endif

    new_confline(&ctmpa);
    ctmpa->valoffset = 2;
    ctmpa->keymenu   = &printer_select_keymenu;
    ctmpa->help      = NO_HELP;
    ctmpa->tool      = print_select_tool;
    ctmpa->flags    |= CF_NOSELECT;
    ctmpa->value
#ifdef OS2
      = cpystr("selected port or |pipe\" section below.");
#else
      = cpystr("selected print command\" section below.");
#endif

    new_confline(&ctmpa);
    ctmpa->valoffset = 2;
    ctmpa->keymenu   = &printer_select_keymenu;
    ctmpa->help      = NO_HELP;
    ctmpa->tool      = print_select_tool;
    ctmpa->flags    |= CF_NOSELECT | CF_B_LINE;

    new_confline(&ctmpa);
    ctmpa->valoffset = 4;
    ctmpa->keymenu   = &printer_select_keymenu;
    ctmpa->help      = NO_HELP;
    ctmpa->tool      = print_select_tool;
    ctmpa->flags    |= CF_NOSELECT;
    def_printer_line = &ctmpa->value;
    set_def_printer_value(ps->VAR_PRINTER);

    new_confline(&ctmpa);
    ctmpa->valoffset = 2;
    ctmpa->keymenu   = &printer_select_keymenu;
    ctmpa->help      = NO_HELP;
    ctmpa->tool      = print_select_tool;
    ctmpa->flags    |= CF_NOSELECT | CF_B_LINE;

#ifndef OS2
    new_confline(&ctmpa);
    heading = ctmpa;
    ctmpa->keymenu   = &printer_select_keymenu;
    ctmpa->help      = NO_HELP;
    ctmpa->tool      = print_select_tool;
    ctmpa->varname
	= cpystr(" Printer attached to IBM PC or compatible, Macintosh");
    ctmpa->flags    |= (CF_NOSELECT | CF_STARTITEM);
    ctmpa->value     = cpystr("");
    ctmpa->headingp  = heading;

    new_confline(&ctmpa);
    ctmpa->valoffset = 6;
    ctmpa->keymenu   = &printer_select_keymenu;
    ctmpa->help      = NO_HELP;
    ctmpa->tool      = print_select_tool;
    ctmpa->flags    |= CF_NOSELECT;
    ctmpa->value
      = cpystr("This may not work with all attached printers, and will depend on the");
    ctmpa->headingp  = heading;

    new_confline(&ctmpa);
    ctmpa->valoffset = 6;
    ctmpa->keymenu   = &printer_select_keymenu;
    ctmpa->help      = NO_HELP;
    ctmpa->tool      = print_select_tool;
    ctmpa->flags    |= CF_NOSELECT;
    ctmpa->value
      = cpystr("terminal emulation/communications software in use. It is known to work");
    ctmpa->headingp  = heading;

    new_confline(&ctmpa);
    ctmpa->valoffset = 6;
    ctmpa->keymenu   = &printer_select_keymenu;
    ctmpa->help      = NO_HELP;
    ctmpa->tool      = print_select_tool;
    ctmpa->flags    |= CF_NOSELECT;
    ctmpa->value
      = cpystr("with Kermit and the latest UW version of NCSA telnet on Macs and PCs,");
    ctmpa->headingp  = heading;

    new_confline(&ctmpa);
    ctmpa->valoffset = 6;
    ctmpa->keymenu   = &printer_select_keymenu;
    ctmpa->help      = NO_HELP;
    ctmpa->tool      = print_select_tool;
    ctmpa->flags    |= CF_NOSELECT;
    ctmpa->value
      = cpystr("Versaterm Pro on Macs, and WRQ Reflections on PCs.");
    ctmpa->headingp  = heading;

    new_confline(&ctmpa);
    start_line = ctmpb = ctmpa; /* default start line */
    ctmpa->valoffset = 17;
    ctmpa->keymenu   = &printer_select_keymenu;
    ctmpa->help      = h_config_set_att_ansi;
    ctmpa->tool      = print_select_tool;
    ctmpa->flags    |= CF_NOHILITE;
    ctmpa->varoffset = 8;
    ctmpa->varname   = cpystr("Printer:");
    ctmpa->value
      = cpystr(ANSI_PRINTER);
    ctmpa->varnamep  = ctmpb;
    ctmpa->headingp  = heading;

    new_confline(&ctmpa);
    ctmpa->valoffset = 17;
    ctmpa->keymenu   = &printer_select_keymenu;
    ctmpa->help      = h_config_set_att_ansi2;
    ctmpa->tool      = print_select_tool;
    ctmpa->flags    |= CF_NOHILITE;
    ctmpa->varoffset = 8;
    ctmpa->value     = (char *)fs_get(strlen(ANSI_PRINTER)+strlen(no_ff)+1);
    ctmpa->varnamep  = ctmpb;
    ctmpa->headingp  = heading;
    strcat(strcpy(ctmpa->value, ANSI_PRINTER), no_ff);
#endif

    new_confline(&ctmpa);
    ctmpa->valoffset = 0;
    ctmpa->keymenu   = &printer_select_keymenu;
    ctmpa->help      = NO_HELP;
    ctmpa->tool      = print_select_tool;
    ctmpa->flags    |= CF_NOSELECT | CF_B_LINE;
    ctmpa->var = &ps->vars[V_STANDARD_PRINTER];


    new_confline(&ctmpa);
    heading = ctmpa;
    ctmpa->valoffset = 6;
    ctmpa->keymenu   = &printer_select_keymenu;
    ctmpa->help      = NO_HELP;
    ctmpa->tool      = print_select_tool;
    ctmpa->varname
#ifdef OS2
        = cpystr(" Standard OS/2 printer port");
#else
	= cpystr(" Standard UNIX print command");
#endif
    ctmpa->value = cpystr("");
    ctmpa->flags    |= (CF_NOSELECT | CF_STARTITEM);
    ctmpa->headingp  = heading;
    ctmpa->var = &ps->vars[V_STANDARD_PRINTER];

    new_confline(&ctmpa);
    ctmpa->valoffset = 6;
    ctmpa->keymenu   = &printer_select_keymenu;
    ctmpa->help      = NO_HELP;
    ctmpa->tool      = print_select_tool;
    ctmpa->flags    |= CF_NOSELECT;
    ctmpa->value
#ifdef OS2
      = cpystr("Using this option may require you to use the OS/2 \"MODE\" command to");
#else
      = cpystr("Using this option may require setting your \"PRINTER\" or \"LPDEST\"");
#endif
    ctmpa->headingp  = heading;
    ctmpa->var = &ps->vars[V_STANDARD_PRINTER];

    new_confline(&ctmpa);
    ctmpa->valoffset = 6;
    ctmpa->keymenu   = &printer_select_keymenu;
    ctmpa->help      = NO_HELP;
    ctmpa->tool      = print_select_tool;
    ctmpa->flags    |= CF_NOSELECT;
    ctmpa->value
#ifdef OS2
      = cpystr("direct printer output to the correct port.");
#else
      = cpystr("environment variable using the standard UNIX utilities.");
#endif
    ctmpa->headingp  = heading;
    ctmpa->var = &ps->vars[V_STANDARD_PRINTER];

    vtmp = &ps->vars[V_STANDARD_PRINTER];
    for(i = 0; vtmp->current_val.l[i]; i++){
	new_confline(&ctmpa);
	ctmpa->valoffset = 22;
	ctmpa->keymenu   = &printer_select_keymenu;
	ctmpa->help      = NO_HELP;
	ctmpa->help      = h_config_set_stand_print;
	ctmpa->tool      = print_select_tool;
	if(i == 0){
	    ctmpa->varoffset = 8;
	    ctmpa->varname   = cpystr("Printer List:");
	    ctmpa->flags    |= CF_NOHILITE|CF_PRINTER;
#ifdef OS2
	    start_line = ctmpb = ctmpa; /* default start line */
#else
	    ctmpb = ctmpa;
#endif
	}

	ctmpa->varnamep  = ctmpb;
	ctmpa->headingp  = heading;
	ctmpa->varmem = i;
	ctmpa->var = vtmp;
	ctmpa->value = printer_name(vtmp->current_val.l[i]);
    }

    new_confline(&ctmpa);
    ctmpa->valoffset = 0;
    ctmpa->keymenu   = &printer_select_keymenu;
    ctmpa->help      = NO_HELP;
    ctmpa->tool      = print_select_tool;
    ctmpa->flags    |= CF_NOSELECT | CF_B_LINE;

    new_confline(&ctmpa);
    heading = ctmpa;
    ctmpa->valoffset = 0;
    ctmpa->keymenu   = &printer_edit_keymenu;
    ctmpa->help      = NO_HELP;
    ctmpa->tool      = print_edit_tool;
    ctmpa->varname
#ifdef OS2
        = cpystr(" Personally selected port or |command");
#else
	= cpystr(" Personally selected print command");
#endif
    ctmpa->flags    |= (CF_NOSELECT | CF_STARTITEM);
    ctmpa->value = cpystr("");
    ctmpa->headingp  = heading;
    ctmpa->var = &ps->vars[V_PERSONAL_PRINT_COMMAND];


    new_confline(&ctmpa);
    ctmpa->valoffset = 6;
    ctmpa->keymenu   = &printer_edit_keymenu;
    ctmpa->help      = NO_HELP;
    ctmpa->tool      = print_edit_tool;
    ctmpa->flags    |= CF_NOSELECT;
    ctmpa->value
#ifdef OS2
      = cpystr("The text to be printed will be sent to the printer or command given here.");
#else
      = cpystr("The text to be printed will be piped into the command given here. The");
#endif
    ctmpa->headingp  = heading;
    ctmpa->var = &ps->vars[V_PERSONAL_PRINT_COMMAND];

    new_confline(&ctmpa);
    ctmpa->valoffset = 6;
    ctmpa->keymenu   = &printer_edit_keymenu;
    ctmpa->help      = NO_HELP;
    ctmpa->tool      = print_edit_tool;
    ctmpa->flags    |= CF_NOSELECT;
    ctmpa->value
#ifdef OS2
      = cpystr("The printer port or |pipe is in the 2nd column, the printer name is in");
#else
      = cpystr("command is in the 2nd column, the printer name is in the first column. Some");
#endif
    ctmpa->headingp  = heading;
    ctmpa->var = &ps->vars[V_PERSONAL_PRINT_COMMAND];

    new_confline(&ctmpa);
    ctmpa->valoffset = 6;
    ctmpa->keymenu   = &printer_edit_keymenu;
    ctmpa->help      = NO_HELP;
    ctmpa->tool      = print_edit_tool;
    ctmpa->flags    |= CF_NOSELECT;
    ctmpa->value
#ifdef OS2
      = cpystr("the first column. Examples: \"LPT1\", \"COM2\", \"|enscript\". A command may");
#else
      = cpystr("examples are: \"prt\", \"lpr\", \"lp\", or \"enscript\". The command may be given");
#endif
    ctmpa->headingp  = heading;
    ctmpa->var = &ps->vars[V_PERSONAL_PRINT_COMMAND];

    new_confline(&ctmpa);
    ctmpa->valoffset = 6;
    ctmpa->keymenu   = &printer_edit_keymenu;
    ctmpa->help      = NO_HELP;
    ctmpa->tool      = print_edit_tool;
    ctmpa->flags    |= CF_NOSELECT;
    ctmpa->value
#ifdef OS2
      = cpystr("be given options, for example \"|ascii2ps -p LPT1\" or \"|txt2hplc -2\". Use");
#else
      = cpystr("with options, for example \"enscript -2 -r\" or \"lpr -Plpacc170\". The");
#endif
    ctmpa->headingp  = heading;
    ctmpa->var = &ps->vars[V_PERSONAL_PRINT_COMMAND];

    new_confline(&ctmpa);
    ctmpa->valoffset = 6;
    ctmpa->keymenu   = &printer_edit_keymenu;
    ctmpa->help      = NO_HELP;
    ctmpa->tool      = print_edit_tool;
    ctmpa->flags    |= CF_NOSELECT;
    ctmpa->value
#ifdef OS2
      = cpystr("the |command method for printers that require conversion from ASCII.");
#else
      = cpystr("commands and options on your system may be different from these examples.");
#endif
    ctmpa->headingp  = heading;
    ctmpa->var = &ps->vars[V_PERSONAL_PRINT_COMMAND];

    vtmp = &ps->vars[V_PERSONAL_PRINT_COMMAND];
    if(vtmp->current_val.l){
	for(i = 0; vtmp->current_val.l[i]; i++){
	    new_confline(&ctmpa);
	    ctmpa->valoffset = 22;
	    ctmpa->keymenu   = &printer_edit_keymenu;
	    ctmpa->help      = h_config_set_custom_print;
	    ctmpa->tool      = print_edit_tool;
	    if(i == 0){
		ctmpa->varoffset = 8;
		ctmpa->varname   = cpystr("Printer List:");
		ctmpa->flags    |= CF_NOHILITE|CF_PRINTER;
		ctmpb = ctmpa;
	    }

	    ctmpa->varnamep  = ctmpb;
	    ctmpa->headingp  = heading;
	    ctmpa->varmem = i;
	    ctmpa->var = vtmp;
	    ctmpa->value = printer_name(vtmp->current_val.l[i]);
	}
    }
    else{
	new_confline(&ctmpa);
	ctmpa->valoffset = 22;
	ctmpa->keymenu   = &printer_edit_keymenu;
	ctmpa->help      = h_config_set_custom_print;
	ctmpa->tool      = print_edit_tool;
	ctmpa->flags    |= CF_NOHILITE;
	ctmpa->varoffset = 8;
	ctmpa->varname   = cpystr("Printer List:");
	ctmpa->varnamep  = ctmpa;
	ctmpa->headingp  = heading;
	ctmpa->varmem    = 0;
	ctmpa->var       = vtmp;
	ctmpa->value     = cpystr("");
    }

    vsave = save_config_vars(ps);
    switch(conf_scroll_screen(ps, &screen, start_line,
			      "SETUP PRINTER", "printer config ", 1, 0)){
      case 0:
	break;
    
      case 1:
	write_pinerc(ps);
	break;
    
      case 10:
	revert_to_saved_config(ps, vsave);
	ps->printer_category = saved_printer_cat;
	set_variable(V_PRINTER, saved_printer, 0);
	set_variable(V_PERSONAL_PRINT_CATEGORY, 
	    comatose(ps->printer_category), 0);
	break;
    }

    def_printer_line = NULL;
    free_saved_config(ps, &vsave);
    fs_give((void **)&saved_printer);
}
#endif	/* !DOS */


void
set_def_printer_value(printer)
    char *printer;
{
    char *p, *nick, *cmd;
    int set;

    if(!def_printer_line)
      return;

    parse_printer(printer, &nick, &cmd, NULL, NULL, NULL, NULL);
    p = *nick ? nick : cmd;
    set = *p;
    if(*def_printer_line)
      fs_give((void **)def_printer_line);

    *def_printer_line = fs_get(36 + strlen(p) + 1);
    sprintf(*def_printer_line, "Default printer currently %s%s%s",
	set ? "set to \"" : "unset", set ? p : "", set ? "\"." : "."); 

    fs_give((void **)&nick);
    fs_give((void **)&cmd);
}


static struct key flag_keys[] = 
       {HELP_MENU,
	NULL_MENU,
        {"E", "Exit Flags", {MC_EXIT,1,{'e'}}, KS_EXITMODE},
        TOGGLE_MENU,
	PREV_MENU,
	NEXT_MENU,
	PREVPAGE_MENU,
	NEXTPAGE_MENU,
	NULL_MENU,
	NULL_MENU,
	PRYNTTXT_MENU,
	WHEREIS_MENU};
INST_KEY_MENU(flag_keymenu, flag_keys);

/*----------------------------------------------------------------------
   Function to control flag set/clearing

   Basically, turn the flags into a fake list of features...

 ----*/
void
flag_maintenance_screen(ps, flags)
    struct pine	       *ps;
    struct flag_screen *flags;
{
    int		  i, lv;
    char	  tmp[256], **p;
    CONF_S	 *ctmpa = NULL, *first_line = NULL;
    struct	  flag_table  *fp;
    OPT_SCREEN_S  screen;

    for(p = flags->explanation; p && *p; p++){
	new_confline(&ctmpa);
	ctmpa->keymenu   = &flag_keymenu;
	ctmpa->help      = NO_HELP;
	ctmpa->tool      = flag_checkbox_tool;
	ctmpa->flags    |= CF_NOSELECT;
	ctmpa->valoffset = 0;
	ctmpa->value     = cpystr(*p);
    }

    /* Now wire flags checkboxes together */
    for(lv = 0, fp = flags->flag_table; fp->name; fp++)	/* longest name */
      if(lv < (i = strlen(fp->name)))
	lv = i;

    for(fp = flags->flag_table; fp->name; fp++){	/* build the list */
	new_confline(&ctmpa);
	if(!first_line)
	  first_line = ctmpa;

	ctmpa->keymenu		  = &flag_keymenu;
	ctmpa->help		  = fp->help;
	ctmpa->tool		  = flag_checkbox_tool;
	ctmpa->d.fp		  = fp;
	ctmpa->valoffset	  = 12;

	sprintf(tmp, "[%c]  %-*.*s",
		(fp->set == 0) ? ' ' : (fp->set == 1) ? 'X' : '?',
		lv, lv, fp->name);
	ctmpa->value = cpystr(tmp);
    }
      
    (void) conf_scroll_screen(ps, &screen, first_line,
			      "FLAG MAINTENANCE", "configuration ", 0, 0);
    ps->mangled_screen = 1;
}



/*----------------------------------------------------------------------
   Function to display/manage collections

 ----*/
CONTEXT_S *
context_select_screen(ps, cs)
    struct pine *ps;
    CONT_SCR_S  *cs;
{
    CONTEXT_S	 *cp;
    CONF_S	 *ctmpa = NULL, *first_line = NULL, *heading;
    OPT_SCREEN_S  screen;

    /*
     * Loop thru available contexts, setting up for display
     * (note: if no "cp" we're hosed.  should never happen ;)
     */
    for(cp = *cs->contexts; cp->prev; cp = cp->prev)
      ;
    
    /* delimiter for Mail Collections */
    new_confline(&ctmpa);		/* blank line */
    ctmpa->keymenu    = cs->keymenu;
    ctmpa->help       = cs->help.text;
    ctmpa->help_title = cs->help.title;
    ctmpa->tool       = context_select_tool;
    ctmpa->flags     |= CF_NOSELECT | CF_B_LINE;

    do{
	new_confline(&ctmpa);
	heading		  = ctmpa;
	ctmpa->value	  = cpystr(cp->nickname ? cp->nickname : cp->context);
	ctmpa->var	  = cp->var.v;
	ctmpa->keymenu    = cs->keymenu;
	ctmpa->help       = cs->help.text;
	ctmpa->help_title = cs->help.title;
	ctmpa->tool       = context_select_tool;
	ctmpa->flags	 |= CF_STARTITEM;
	ctmpa->valoffset  = 4;
	ctmpa->d.c.ct     = cp;
	ctmpa->d.c.cs	  = cs;

	if(!first_line || cp == cs->start)
	  first_line = ctmpa;

	/* Add explanatory text */
	new_confline(&ctmpa);
	ctmpa->value	  = cpystr(cp->label ? cp->label : "* * *");
	ctmpa->keymenu	  = cs->keymenu;
	ctmpa->help	  = cs->help.text;
	ctmpa->help_title = cs->help.title;
	ctmpa->tool	  = context_select_tool;
	ctmpa->flags	 |= CF_NOSELECT;
	ctmpa->valoffset  = 8;

	/* Always add blank line, make's shuffling a little easier */
	new_confline(&ctmpa);
	heading->headingp  = ctmpa;
	ctmpa->keymenu	   = cs->keymenu;
	ctmpa->help	   = cs->help.text;
	ctmpa->help_title  = cs->help.title;
	ctmpa->tool	   = context_select_tool;
	ctmpa->flags	  |= CF_NOSELECT | CF_B_LINE;
	ctmpa->valoffset   = 0;
    }
    while(cp = cp->next);


    (void) conf_scroll_screen(ps, &screen, first_line, cs->title,
			      cs->print_string, cs->edit, 0);
    ps->mangled_screen = 1;
    return(cs->selected);
}



#ifdef	ENABLE_LDAP

static char *srch_res_help_title = "HELP FOR SEARCH RESULTS INDEX";
#define ADD_FIRST_LDAP_SERVER "Use Add to add a directory server"
#define ADDR_SELECT_EXIT_VAL 5
#define ADDR_SELECT_GOBACK_VAL 6
#define ADDR_SELECT_FORCED_EXIT_VAL 7

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

static struct key addr_select_with_goback_keys[] = 
       {HELP_MENU,
	NULL_MENU,
        {"<", "AddressBkList", {MC_ADDRBOOK,2,{'<',','}}, KS_NONE},
        {"S", "[Select]", {MC_CHOICE,3,{'s',ctrl('M'),ctrl('J')}}, KS_NONE},
	PREV_MENU,
	NEXT_MENU,
	PREVPAGE_MENU,
	NEXTPAGE_MENU,
	NULL_MENU,
	NULL_MENU,
        {"E", "ExitSelect", {MC_EXIT,1,{'e'}}, KS_EXITMODE},
	WHEREIS_MENU};
INST_KEY_MENU(addr_s_km_with_goback, addr_select_with_goback_keys);

static struct key addr_select_with_view_keys[] = 
       {HELP_MENU,
	RCOMPOSE_MENU,
        {"<", "AddressBkList", {MC_ADDRBOOK,2,{'<',','}}, KS_NONE},
        {">", "[View]",
	   {MC_VIEW_TEXT,5,{'v','>','.',ctrl('M'),ctrl('J')}}, KS_NONE},
	PREV_MENU,
	NEXT_MENU,
	PREVPAGE_MENU,
	NEXTPAGE_MENU,
        {"C", "ComposeTo", {MC_COMPOSE,1,{'c'}}, KS_COMPOSER},
	FWDEMAIL_MENU,
	SAVE_MENU,
	WHEREIS_MENU};
INST_KEY_MENU(addr_s_km_with_view, addr_select_with_view_keys);

static struct key addr_select_for_url_keys[] = 
       {HELP_MENU,
	RCOMPOSE_MENU,
        {"<", "Exit Viewer", {MC_ADDRBOOK,3,{'<',',','e'}}, KS_NONE},
        {">", "[View]",
	   {MC_VIEW_TEXT,5,{'v','>','.',ctrl('M'),ctrl('J')}}, KS_NONE},
	PREV_MENU,
	NEXT_MENU,
	PREVPAGE_MENU,
	NEXTPAGE_MENU,
        {"C", "ComposeTo", {MC_COMPOSE,1,{'c'}}, KS_COMPOSER},
	FWDEMAIL_MENU,
	SAVE_MENU,
	WHEREIS_MENU};
INST_KEY_MENU(addr_s_km_for_url, addr_select_for_url_keys);

static struct key addr_select_exit_keys[] = 
       {NULL_MENU,
	NULL_MENU,
        {"E", "[Exit]", {MC_EXIT,3,{'e',ctrl('M'),ctrl('J')}},
	   KS_EXITMODE},
	NULL_MENU,
	NULL_MENU,
	NULL_MENU,
	NULL_MENU,
	NULL_MENU,
	NULL_MENU,
	NULL_MENU,
	NULL_MENU,
	NULL_MENU};
INST_KEY_MENU(addr_s_km_exit, addr_select_exit_keys);

static struct key addr_select_goback_keys[] = 
       {NULL_MENU,
	NULL_MENU,
        {"E", "[Exit]", {MC_ADDRBOOK,3,{'e',ctrl('M'),ctrl('J')}},
	   KS_EXITMODE},
	NULL_MENU,
	NULL_MENU,
	NULL_MENU,
	NULL_MENU,
	NULL_MENU,
	NULL_MENU,
	NULL_MENU,
	NULL_MENU,
	NULL_MENU};
INST_KEY_MENU(addr_s_km_goback, addr_select_goback_keys);


static int some_selectable;
static char *dserv = "Directory Server on ";

/*
 * Let user choose an ldap entry (or return an entry if user doesn't need
 * to be consulted).
 *
 * Returns  0 if ok,
 *         -1 if Exit was chosen
 *         -2 if none were selectable
 *         -3 if no entries existed at all
 *         -4 go back to Abook List was chosen
 *
 *      When 0 is returned the winner is pointed to by result.
 *         Result is an allocated LDAP_SEARCH_WINNER_S which has pointers
 *         to the ld and entry that were chosen. Those are pointers into
 *         the initial data, not copies. The two-pointer structure is
 *         allocated here and freed by the caller.
 */
int
ldap_addr_select(ps, ac, result, style, wp_err)
    struct pine           *ps;
    ADDR_CHOOSE_S         *ac;
    LDAP_SERV_RES_S      **result;
    LDAPLookupStyle        style;
    WP_ERR_S              *wp_err;
{
    LDAPMessage     *e;
    LDAP_SERV_RES_S *res_list;
    CONF_S          *ctmpa = NULL, *first_line = NULL, *alt_first_line = NULL;
    int              i, retval = 0, got_n_mail = 0, got_n_entries = 0;
    int              need_mail;
    OPT_SCREEN_S     screen;
    struct key_menu *km;
    char             ee[200];
    HelpType         help;

    dprint(4,(debugfile, "ldap_addr_select()\n"));

    need_mail = (style == AlwaysDisplay || style == DisplayForURL) ? 0 : 1;
    if(style == AlwaysDisplay){
	km = &addr_s_km_with_view;
	help = h_address_display;
    }
    else if(style == AlwaysDisplayAndMailRequired){
	km = &addr_s_km_with_goback;
	help = h_address_select;
    }
    else if(style == DisplayForURL){
	km = &addr_s_km_for_url;
	help = h_address_display;
    }
    else{
	km = &addr_s_km;
	help = h_address_select;
    }

    if(result)
      *result = NULL;

    some_selectable = 0;

    for(res_list = ac->res_head; res_list; res_list = res_list->next){
	for(e = ldap_first_entry(res_list->ld, res_list->res);
	    e != NULL;
	    e = ldap_next_entry(res_list->ld, e)){
	    char       *dn, *a;
	    char      **cn, **org, **unit, **title, **mail, **sn;
	    BerElement *ber;
	    static char no_email[] = "<No Email Address Available>";
	    int         indent, have_mail;
	    
	    dn = NULL;
	    cn = org = title = unit = mail = sn = NULL;
	    for(a = ldap_first_attribute(res_list->ld, e, &ber);
		a != NULL;
		a = ldap_next_attribute(res_list->ld, e, ber)){

		dprint(9, (debugfile, " %s", a));
		if(strcmp(a, res_list->info_used->cnattr) == 0){
		    if(!cn)
		      cn = ldap_get_values(res_list->ld, e, a);

		    if(cn && !(cn[0] && cn[0][0])){
			ldap_value_free(cn);
			cn = NULL;
		    }
		}
		else if(strcmp(a, res_list->info_used->mailattr) == 0){
		    if(!mail)
		      mail = ldap_get_values(res_list->ld, e, a);
		}
		else if(strcmp(a, "o") == 0){
		    if(!org)
		      org = ldap_get_values(res_list->ld, e, a);
		}
		else if(strcmp(a, "ou") == 0){
		    if(!unit)
		      unit = ldap_get_values(res_list->ld, e, a);
		}
		else if(strcmp(a, "title") == 0){
		    if(!title)
		      title = ldap_get_values(res_list->ld, e, a);
		}

		our_ldap_memfree(a);
	    }

	    dprint(9, (debugfile, "\n"));

	    if(!cn){
		for(a = ldap_first_attribute(res_list->ld, e, &ber);
		    a != NULL;
		    a = ldap_next_attribute(res_list->ld, e, ber)){

		    if(strcmp(a,  res_list->info_used->snattr) == 0){
			if(!sn)
			  sn = ldap_get_values(res_list->ld, e, a);

			if(sn && !(sn[0] && sn[0][0])){
			    ldap_value_free(sn);
			    sn = NULL;
			}
		    }

		    our_ldap_memfree(a);
		}
	    }

	    if(mail && mail[0] && mail[0][0])
	      have_mail = 1;
	    else
	      have_mail = 0;

	    got_n_mail += have_mail;
	    got_n_entries++;
	    indent = 2;

	    /*
	     * First line is either cn, sn, or dn.
	     */
	    if(cn){
		new_confline(&ctmpa);
		if(!alt_first_line)
		  alt_first_line = ctmpa;

		ctmpa->flags     |= CF_STARTITEM;
		if(need_mail && !have_mail)
		  ctmpa->flags     |= CF_PRIVATE;

		ctmpa->value      = cpystr(cn[0]);
		ldap_value_free(cn);
		ctmpa->valoffset  = indent;
		ctmpa->keymenu    = km;
		ctmpa->help       = help;
		ctmpa->help_title = srch_res_help_title;
		ctmpa->tool       = addr_select_tool;
		ctmpa->d.a.ld     = res_list->ld;
		ctmpa->d.a.res    = e;
		ctmpa->d.a.info_used = res_list->info_used;
		ctmpa->d.a.serv   = res_list->serv;
		ctmpa->d.a.ac     = ac;
		if(!first_line && (have_mail || !need_mail))
		  first_line = ctmpa;
	    }
	    
	    /* only happens if no cn */
	    if(sn){
		new_confline(&ctmpa);
		if(!alt_first_line)
		  alt_first_line = ctmpa;

		ctmpa->flags     |= CF_STARTITEM;
		if(need_mail && !have_mail)
		  ctmpa->flags     |= CF_PRIVATE;

		ctmpa->value      = cpystr(sn[0]);
		ldap_value_free(sn);
		ctmpa->valoffset  = indent;
		ctmpa->keymenu    = km;
		ctmpa->help       = help;
		ctmpa->help_title = srch_res_help_title;
		ctmpa->tool       = addr_select_tool;
		ctmpa->d.a.ld     = res_list->ld;
		ctmpa->d.a.res    = e;
		ctmpa->d.a.info_used = res_list->info_used;
		ctmpa->d.a.serv   = res_list->serv;
		ctmpa->d.a.ac     = ac;
		if(!first_line && (have_mail || !need_mail))
		  first_line = ctmpa;
	    }

	    if(!sn && !cn){
		new_confline(&ctmpa);
		if(!alt_first_line)
		  alt_first_line = ctmpa;

		ctmpa->flags     |= CF_STARTITEM;
		if(need_mail && !have_mail)
		  ctmpa->flags     |= CF_PRIVATE;

		dn = ldap_get_dn(res_list->ld, e);

		if(dn && !dn[0]){
		    our_ldap_dn_memfree(dn);
		    dn = NULL;
		}

		ctmpa->value      = cpystr(dn ? dn : "?");
		if(dn)
		  our_ldap_dn_memfree(dn);

		ctmpa->valoffset  = indent;
		ctmpa->keymenu    = km;
		ctmpa->help       = help;
		ctmpa->help_title = srch_res_help_title;
		ctmpa->tool       = addr_select_tool;
		ctmpa->d.a.ld     = res_list->ld;
		ctmpa->d.a.res    = e;
		ctmpa->d.a.info_used = res_list->info_used;
		ctmpa->d.a.serv   = res_list->serv;
		ctmpa->d.a.ac     = ac;
		if(!first_line && (have_mail || !need_mail))
		  first_line = ctmpa;
	    }

	    if(title){
		for(i = 0; title[i] && title[i][0]; i++){
		    new_confline(&ctmpa);
		    ctmpa->flags     |= CF_NOSELECT;
		    ctmpa->valoffset  = indent + 2;
		    ctmpa->value      = cpystr(title[i]);
		    ctmpa->keymenu    = km;
		    ctmpa->help       = help;
		    ctmpa->help_title = srch_res_help_title;
		    ctmpa->tool       = addr_select_tool;
		    ctmpa->d.a.ld     = res_list->ld;
		    ctmpa->d.a.res    = e;
		    ctmpa->d.a.info_used = res_list->info_used;
		    ctmpa->d.a.serv   = res_list->serv;
		    ctmpa->d.a.ac     = ac;
		}

		ldap_value_free(title);
	    }

	    if(unit){
		for(i = 0; unit[i] && unit[i][0]; i++){
		    new_confline(&ctmpa);
		    ctmpa->flags     |= CF_NOSELECT;
		    ctmpa->valoffset  = indent + 2;
		    ctmpa->value      = cpystr(unit[i]);
		    ctmpa->keymenu    = km;
		    ctmpa->help       = help;
		    ctmpa->help_title = srch_res_help_title;
		    ctmpa->tool       = addr_select_tool;
		    ctmpa->d.a.ld     = res_list->ld;
		    ctmpa->d.a.res    = e;
		    ctmpa->d.a.info_used = res_list->info_used;
		    ctmpa->d.a.serv   = res_list->serv;
		    ctmpa->d.a.ac     = ac;
		}

		ldap_value_free(unit);
	    }

	    if(org){
		for(i = 0; org[i] && org[i][0]; i++){
		    new_confline(&ctmpa);
		    ctmpa->flags     |= CF_NOSELECT;
		    ctmpa->valoffset  = indent + 2;
		    ctmpa->value      = cpystr(org[i]);
		    ctmpa->keymenu    = km;
		    ctmpa->help       = help;
		    ctmpa->help_title = srch_res_help_title;
		    ctmpa->tool       = addr_select_tool;
		    ctmpa->d.a.ld     = res_list->ld;
		    ctmpa->d.a.res    = e;
		    ctmpa->d.a.info_used = res_list->info_used;
		    ctmpa->d.a.serv   = res_list->serv;
		    ctmpa->d.a.ac     = ac;
		}

		ldap_value_free(org);
	    }

	    if(have_mail){
		for(i = 0; mail[i] && mail[i][0]; i++){
		    new_confline(&ctmpa);
		    ctmpa->flags     |= CF_NOSELECT;
		    ctmpa->valoffset  = indent + 2;
		    ctmpa->value      = cpystr(mail[i]);
		    ctmpa->keymenu    = km;
		    ctmpa->help       = help;
		    ctmpa->help_title = srch_res_help_title;
		    ctmpa->tool       = addr_select_tool;
		    ctmpa->d.a.ld     = res_list->ld;
		    ctmpa->d.a.res    = e;
		    ctmpa->d.a.info_used = res_list->info_used;
		    ctmpa->d.a.serv   = res_list->serv;
		    ctmpa->d.a.ac     = ac;
		}
	    }
	    else{
		new_confline(&ctmpa);
		ctmpa->flags     |= CF_NOSELECT;
		ctmpa->valoffset  = indent + 2;
		ctmpa->value      = cpystr(no_email);
		ctmpa->keymenu    = km;
		ctmpa->help       = help;
		ctmpa->help_title = srch_res_help_title;
		ctmpa->tool       = addr_select_tool;
		ctmpa->d.a.ld     = res_list->ld;
		ctmpa->d.a.res    = e;
		ctmpa->d.a.info_used = res_list->info_used;
		ctmpa->d.a.serv   = res_list->serv;
		ctmpa->d.a.ac     = ac;
	    }

	    if(mail)
	      ldap_value_free(mail);

	    new_confline(&ctmpa);		/* blank line */
	    ctmpa->keymenu    = km;
	    ctmpa->help       = help;
	    ctmpa->help_title = srch_res_help_title;
	    ctmpa->tool       = addr_select_tool;
	    ctmpa->flags     |= CF_NOSELECT | CF_B_LINE;
	}
    }

    if(first_line)
      some_selectable++;
    else if(alt_first_line)
      first_line = alt_first_line;
    else{
	new_confline(&ctmpa);		/* blank line */
	ctmpa->keymenu    = need_mail ? &addr_s_km_exit : &addr_s_km_goback;
	ctmpa->help       = help;
	ctmpa->help_title = srch_res_help_title;
	ctmpa->tool       = addr_select_tool;
	ctmpa->flags     |= CF_NOSELECT | CF_B_LINE;

	new_confline(&ctmpa);
	first_line = ctmpa;
	strcpy(ee, "[ ");
	if(wp_err && wp_err->ldap_errno)
	  sprintf(ee+2, "%s, No Matches Returned",
		  ldap_err2string(wp_err->ldap_errno));
	else
	    strcat(ee, "No Matches");

	strcat(ee, " -- Choose Exit ]");
	ctmpa->value      = cpystr(ee);
	ctmpa->valoffset  = 10;
	ctmpa->keymenu    = need_mail ? &addr_s_km_exit : &addr_s_km_goback;
	ctmpa->help       = help;
	ctmpa->help_title = srch_res_help_title;
	ctmpa->tool       = addr_select_tool;
	ctmpa->flags     |= CF_NOSELECT;
    }

    if(style == AlwaysDisplay || style == DisplayForURL ||
       style == AlwaysDisplayAndMailRequired ||
       (style == DisplayIfOne && got_n_mail >= 1) ||
       (style == DisplayIfTwo && got_n_mail >= 1 && got_n_entries >= 2)){
	if(wp_err && wp_err->mangled)
	  *wp_err->mangled = 1;

	switch(conf_scroll_screen(ps,&screen,first_line,ac->title,"this ",0,0)){
	  case ADDR_SELECT_EXIT_VAL:
	    retval = -1;
	    break;

	  case ADDR_SELECT_GOBACK_VAL:
	    retval = -4;
	    break;

	  case ADDR_SELECT_FORCED_EXIT_VAL:
	    if(alt_first_line)	/* some entries, but none suitable */
	      retval = -2;
	    else
	      retval = -3;

	    break;

	  default:
	    retval = 0;
	    break;
	}

	if(result && retval == 0 && ac->selected_ld && ac->selected_entry){
	    (*result) = (LDAP_SERV_RES_S *)fs_get(sizeof(LDAP_SERV_RES_S));
	    (*result)->ld    = ac->selected_ld;
	    (*result)->res   = ac->selected_entry;
	    (*result)->info_used = ac->info_used;
	    (*result)->serv  = ac->selected_serv;
	    (*result)->next  = NULL;
	}
    }
    else if(style == DisplayIfOne && got_n_mail < 1){
	if(alt_first_line)	/* some entries, but none suitable */
	  retval = -2;
	else
	  retval = -3;

	first_line = first_confline(ctmpa);
	free_conflines(&first_line);
    }
    else if(style == DisplayIfTwo && (got_n_mail < 1 || got_n_entries < 2)){
	if(got_n_mail < 1){
	    if(alt_first_line)	/* some entries, but none suitable */
	      retval = -2;
	    else
	      retval = -3;
	}
	else{
	    retval = 0;
	    if(result){
		(*result) = (LDAP_SERV_RES_S *)fs_get(sizeof(LDAP_SERV_RES_S));
		(*result)->ld    = first_line->d.a.ld;
		(*result)->res   = first_line->d.a.res;
		(*result)->info_used = first_line->d.a.info_used;
		(*result)->serv  = first_line->d.a.serv;
		(*result)->next  = NULL;
	    }
	}

	first_line = first_confline(ctmpa);
	free_conflines(&first_line);
    }

    return(retval);
}

int
addr_select_tool(ps, cmd, cl, flags)
    struct pine *ps;
    int          cmd;
    CONF_S     **cl;
    unsigned     flags;
{
    int retval = 0;

    switch(cmd){
      case MC_CHOICE :
	if(flags & CF_PRIVATE){
	    q_status_message(SM_ORDER | SM_DING, 0, 3,
     "No email address available for this entry; choose another or ExitSelect");
	}
	else if(some_selectable){
	    (*cl)->d.a.ac->selected_ld    = (*cl)->d.a.ld;
	    (*cl)->d.a.ac->selected_entry = (*cl)->d.a.res;
	    (*cl)->d.a.ac->info_used      = (*cl)->d.a.info_used;
	    (*cl)->d.a.ac->selected_serv  = (*cl)->d.a.serv;
	    retval = simple_exit_cmd(flags);
	}
	else
	  retval = ADDR_SELECT_FORCED_EXIT_VAL;

	break;

      case MC_VIEW_TEXT :
      case MC_SAVE :
      case MC_FWDTEXT :
      case MC_COMPOSE :
      case MC_ROLE :
	{LDAP_SERV_RES_S *e;

	  if((*cl)->d.a.ld && (*cl)->d.a.res){
	    e = (LDAP_SERV_RES_S *)fs_get(sizeof(LDAP_SERV_RES_S));
	    e->ld    = (*cl)->d.a.ld;
	    e->res   = (*cl)->d.a.res;
	    e->info_used = (*cl)->d.a.info_used;
	    e->serv  = (*cl)->d.a.serv;
	    e->next  = NULL;
	    if(cmd == MC_VIEW_TEXT)
	      view_ldap_entry(ps, e);
	    else if(cmd == MC_SAVE)
	      save_ldap_entry(ps, e, 0);
	    else if(cmd == MC_COMPOSE)
	      compose_to_ldap_entry(ps, e, 0);
	    else if(cmd == MC_ROLE)
	      compose_to_ldap_entry(ps, e, 1);
	    else
	      forward_ldap_entry(ps, e);

	    fs_give((void **)&e);
	  }
	}

	break;

      case MC_ADDRBOOK :
        retval = ADDR_SELECT_GOBACK_VAL;
	break;

      case MC_EXIT :
        retval = ADDR_SELECT_EXIT_VAL;
	break;

      default:
	retval = -1;
	break;
    }

    if(retval > 0)
      ps->mangled_body = 1;

    return(retval);
}


static struct key direct_config_keys[] = 
       {HELP_MENU,
	NULL_MENU,
        {"E", "Exit Setup", {MC_EXIT,1,{'e'}}, KS_EXITMODE},
	{"C", "[Change]", {MC_EDIT,3,{'c',ctrl('M'),ctrl('J')}}, KS_NONE},
	{"P", "PrevDir", {MC_PREVITEM, 1, {'p'}}, KS_NONE},
	{"N", "NextDir", {MC_NEXTITEM, 2, {'n', TAB}}, KS_NONE},
	PREVPAGE_MENU,
	NEXTPAGE_MENU,
	{"A", "Add Dir", {MC_ADD,1,{'a'}}, KS_NONE},
	{"D", "Del Dir", {MC_DELETE,1,{'d'}}, KS_NONE},
	{"$", "Shuffle", {MC_SHUFFLE,1,{'$'}}, KS_NONE},
	WHEREIS_MENU};
INST_KEY_MENU(dir_conf_km, direct_config_keys);


void
dir_init_display(ps, ctmp, servers, var, first_line)
    struct pine     *ps;
    CONF_S         **ctmp;
    char           **servers;
    struct variable *var;
    CONF_S         **first_line;
{
    int   i;
    char *serv;
    char *subtitle;
    LDAP_SERV_S *info;

    if(first_line)
      *first_line = NULL;

    if(servers && servers[0] && servers[0][0]){
	for(i = 0; servers[i]; i++){
	    info = break_up_ldap_server(servers[i]);
	    serv = (info && info->nick && *info->nick) ? cpystr(info->nick) :
		     (info && info->serv && *info->serv) ? cpystr(info->serv) :
		       cpystr("Bad Server Config, Delete this");
	    subtitle = (char *)fs_get((((info && info->serv && *info->serv)
					    ? strlen(info->serv)
					    : 3) +
					       strlen(dserv) + 15) *
					 sizeof(char));
	    if(info && info->port >= 0)
	      sprintf(subtitle, "%s%s:%d",
		      dserv,
		      (info && info->serv && *info->serv) ? info->serv : "<?>",
		      info->port);
	    else
	      sprintf(subtitle, "%s%s",
		      dserv,
		      (info && info->serv && *info->serv) ? info->serv : "<?>");

	    add_ldap_server_to_display(ps, ctmp, serv, subtitle, var,
				       i, &dir_conf_km, h_direct_config,
				       dir_config_tool, 0,
				       (first_line && *first_line == NULL)
					  ? first_line
					  : NULL);

	    free_ldap_server_info(&info);
	}
    }
    else{
	add_ldap_fake_first_server(ps, ctmp, var,
				   &dir_conf_km, h_direct_config,
				   dir_config_tool);
	if(first_line)
	  *first_line = *ctmp;
    }
}


void
directory_config(ps)
    struct pine *ps;
{
    CONF_S   *ctmp = NULL, *first_line = NULL;
    OPT_SCREEN_S  screen;

    if(ps->fix_fixed_warning && offer_to_fix_pinerc(ps))
      write_pinerc(ps);

    dir_init_display(ps, &ctmp, ps->VAR_LDAP_SERVERS,&ps->vars[V_LDAP_SERVERS],
		     &first_line);

    (void)conf_scroll_screen(ps, &screen, first_line,
			     "SETUP DIRECTORY SERVERS", "servers ", 1, 0);
    ps->mangled_screen = 1;
}


int
dir_config_tool(ps, cmd, cl, flags)
    struct pine *ps;
    int          cmd;
    CONF_S     **cl;
    unsigned     flags;
{
    int first_one, rv = 0;

    first_one = (*cl)->value &&
		(strcmp((*cl)->value, ADD_FIRST_LDAP_SERVER) == 0);
    switch(cmd){
      case MC_DELETE :
	if(first_one)
	  q_status_message(SM_ORDER|SM_DING, 0, 3,
			   "Nothing to Delete, use Add");
	else
	  dir_config_del(ps, cl);

	break;

      case MC_ADD :
	if(!fixed_var((*cl)->var, NULL, "directory list"))
	  dir_config_add(ps, cl);

	break;

      case MC_EDIT :
	if(!fixed_var((*cl)->var, NULL, "directory list")){
	    if(first_one)
	      dir_config_add(ps, cl);
	    else
	      dir_config_edit(ps, cl);
	}

	break;

      case MC_SHUFFLE :
	if(!fixed_var((*cl)->var, NULL, "directory list")){
	    if(first_one)
	      q_status_message(SM_ORDER|SM_DING, 0, 3,
			       "Nothing to Shuffle, use Add");
	    else
	      dir_config_shuffle(ps, cl);
	}

	break;

      case MC_EXIT :
	rv = 2;
	break;

      default:
	rv = -1;
	break;
    }

    return(rv);
}


/*
 * Add LDAP directory entry
 */
void
dir_config_add(ps, cl)
    struct pine  *ps;
    CONF_S      **cl;
{
    char *raw_server = NULL;
    LDAP_SERV_S *info = NULL;

    if(dir_edit_screen(ps, NULL, "ADD A", &raw_server) == 1){

	info = break_up_ldap_server(raw_server);

	if(info && info->serv && *info->serv){
	    char  *subtitle;
	    int    i, cnt = 0;
	    char **new_list;
	    CONF_S *cp;

	    if((*cl)->var->current_val.l)
	      while((*cl)->var->current_val.l[cnt])
		cnt++;

	    /* catch the special "" case */
	    if(cnt == 0 ||
	       cnt == 1 && (*cl)->var->current_val.l[0][0] == '\0'){
		new_list = (char **)fs_get((1 + 1) * sizeof(char *));
		new_list[0] = raw_server;
		new_list[1] = NULL;
	    }
	    else{
		/* add one for new value */
		cnt++;
		new_list = (char **)fs_get((cnt + 1) * sizeof(char *));

		for(i = 0; i < (*cl)->varmem; i++)
		  new_list[i] = cpystr((*cl)->var->current_val.l[i]);

		new_list[(*cl)->varmem] = raw_server;

		for(i = (*cl)->varmem; i < cnt; i++)
		  new_list[i+1] = cpystr((*cl)->var->current_val.l[i]);
	    }

	    raw_server = NULL;
	    set_variable_list(V_LDAP_SERVERS, new_list, FALSE);
	    free_list_array(&new_list);
	    set_current_val((*cl)->var, TRUE, FALSE);
	    subtitle = (char *)fs_get((((info && info->serv && *info->serv)
					    ? strlen(info->serv)
					    : 3) +
					       strlen(dserv) + 15) *
					 sizeof(char));
	    if(info && info->port >= 0)
	      sprintf(subtitle, "%s%s:%d",
		      dserv,
		      (info && info->serv && *info->serv) ? info->serv : "<?>",
		      info->port);
	    else
	      sprintf(subtitle, "%s%s",
		      dserv,
		      (info && info->serv && *info->serv) ? info->serv : "<?>");

	    if(cnt < 2){			/* first one */
		struct variable *var;
		struct key_menu *keymenu;
		HelpType         help;
		int            (*tool)();

		var      = (*cl)->var;
		keymenu  = (*cl)->keymenu;
		help     = (*cl)->help;
		tool     = (*cl)->tool;
		*cl = first_confline(*cl);
		free_conflines(cl);
		add_ldap_server_to_display(ps, cl,
					   (info && info->nick && *info->nick)
					     ? cpystr(info->nick)
					     : cpystr(info->serv),
					   subtitle, var, 0, keymenu, help,
					   tool, 0, NULL);

		opt_screen->top_line = NULL;
	    }
	    else{
		/*
		 * Insert new server.
		 */
		add_ldap_server_to_display(ps, cl,
					   (info && info->nick && *info->nick)
					     ? cpystr(info->nick)
					     : cpystr(info->serv),
					   subtitle,
					   (*cl)->var,
					   (*cl)->varmem,
					   (*cl)->keymenu,
					   (*cl)->help,
					   (*cl)->tool,
					   1,
					   NULL);
		/* adjust the rest of the varmems */
		for(cp = (*cl)->next; cp; cp = cp->next)
		  cp->varmem++;
	    }

	    /* because add_ldap advanced cl to its third line */
	    (*cl) = (*cl)->prev->prev;
	    
	    fix_side_effects(ps, (*cl)->var, 0);
	    write_pinerc(ps);
	}
	else
	  q_status_message(SM_ORDER, 0, 3, "Add cancelled, no server name");
    }

    free_ldap_server_info(&info);
    if(raw_server)
      fs_give((void **)&raw_server);
}


/*
 * Shuffle order of LDAP directory entries
 */
void
dir_config_shuffle(ps, cl)
    struct pine  *ps;
    CONF_S      **cl;
{
    int      cnt, rv, current_num, new_num, i, j, deefault;
    char   **new_list;
    char     tmp[200];
    HelpType help;
    ESCKEY_S opts[3];
    CONF_S  *a, *b;

    /* how many are in our current list? */
    for(cnt = 0; (*cl)->var->current_val.l[cnt]; cnt++)
      ;
    
    if(cnt < 2){
	q_status_message(SM_ORDER, 0, 3,
	 "Shuffle only makes sense when there is more than one server defined");
	return;
    }

    current_num = (*cl)->varmem;  /* variable number of highlighted directory */

    /* Move it up or down? */
    i = 0;
    opts[i].ch      = 'u';
    opts[i].rval    = 'u';
    opts[i].name    = "U";
    opts[i++].label = "Up";

    opts[i].ch      = 'd';
    opts[i].rval    = 'd';
    opts[i].name    = "D";
    opts[i++].label = "Down";

    opts[i].ch = -1;
    deefault = 'u';

    if(current_num == 0){			/* no up */
	opts[0].ch = -2;
	deefault = 'd';
    }
    else if(current_num == cnt - 1)		/* no down */
      opts[1].ch = -2;

    sprintf(tmp, "Shuffle \"%s\" %s%s%s ? ",
	    (*cl)->value,
	    (opts[0].ch != -2) ? "UP" : "",
	    (opts[0].ch != -2 && opts[1].ch != -2) ? " or " : "",
	    (opts[1].ch != -2) ? "DOWN" : "");
    help = (opts[0].ch == -2) ? h_dir_shuf_down
			      : (opts[1].ch == -2) ? h_dir_shuf_up
						   : h_dir_shuf;

    rv = radio_buttons(tmp, -FOOTER_ROWS(ps), opts, deefault, 'x',
		       help, RB_NORM);

    switch(rv){
      case 'x':
	cmd_cancelled("Shuffle");
	return;

      case 'u':
	new_num = current_num - 1;
	a = (*cl)->prev->prev->prev;
	b = *cl;
	break;

      case 'd':
	new_num = current_num + 1;
	a = *cl;
	b = (*cl)->next->next->next;
	break;
    }

    /* allocate space for new list */
    new_list = (char **)fs_get((cnt + 1) * sizeof(char *));

    /* fill in new_list */
    for(i = 0; i < cnt; i++){
	if(i == current_num)
	  j = new_num;
	else if (i == new_num)
	  j = current_num;
	else
	  j = i;

	/* notice this works even if we were using default */
	new_list[i] = cpystr((*cl)->var->current_val.l[j]);
    }

    new_list[i] = NULL;

    j = set_variable_list((*cl)->var - ps->vars, new_list, TRUE);
    free_list_array(&new_list);
    if(j){
	q_status_message(SM_ORDER, 0, 3,
			 "Shuffle cancelled: couldn't save configuration file");
	set_current_val((*cl)->var, TRUE, FALSE);
	return;
    }

    set_current_val((*cl)->var, TRUE, FALSE);

    if(a == opt_screen->top_line)
      opt_screen->top_line = b;
    
    j = a->varmem;
    a->varmem = b->varmem;
    b->varmem = j;

    /*
     * Swap display lines. To start with, a is lower in list, b is higher.
     * The fact that there are 3 lines per entry is totally entangled in
     * the code.
     */
    a->next->next->next = b->next->next->next;
    if(b->next->next->next)
      b->next->next->next->prev = a->next->next;
    b->prev = a->prev;
    if(a->prev)
      a->prev->next = b;
    b->next->next->next = a;
    a->prev = b->next->next;

    ps->mangled_body = 1;
    write_pinerc(ps);
}


/*
 * Edit LDAP directory entry
 */
void
dir_config_edit(ps, cl)
    struct pine  *ps;
    CONF_S      **cl;
{
    char        *raw_server = NULL;
    LDAP_SERV_S *info;

    info = break_up_ldap_server((*cl)->var->current_val.l[(*cl)->varmem]);
    
    if(dir_edit_screen(ps, info, "CHANGE THIS", &raw_server) == 1){

	free_ldap_server_info(&info);
	info = break_up_ldap_server(raw_server);

	if(strcmp((*cl)->var->current_val.l[(*cl)->varmem], raw_server) == 0)
	  q_status_message(SM_ORDER, 0, 3, "No change, cancelled");
	else if(!(info && info->serv && *info->serv))
	  q_status_message(SM_ORDER, 0, 3,
	      "Change cancelled, use Delete if you want to remove this server");
	else{
	    char   tmp[900];
	    char  *subtitle;
	    int    i, cnt;
	    char **new_list;

	    for(cnt = 0; (*cl)->var->current_val.l[cnt]; cnt++)
	      ;

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

	    for(i = 0; i < (*cl)->varmem; i++)
	      new_list[i] = cpystr((*cl)->var->current_val.l[i]);

	    new_list[(*cl)->varmem] = raw_server;
	    raw_server = NULL;

	    for(i = (*cl)->varmem + 1; i < cnt; i++)
	      new_list[i] = cpystr((*cl)->var->current_val.l[i]);
	    
	    new_list[cnt] = NULL;
	    set_variable_list(V_LDAP_SERVERS, new_list, FALSE);
	    free_list_array(&new_list);
	    set_current_val((*cl)->var, TRUE, FALSE);

	    if((*cl)->value)
	      fs_give((void **)&(*cl)->value);

	    (*cl)->value = cpystr((info->nick && *info->nick) ? info->nick
							      : info->serv);

	    if((*cl)->next->value)
	      fs_give((void **)&(*cl)->next->value);

	    subtitle = (char *)fs_get((((info && info->serv && *info->serv)
					    ? strlen(info->serv)
					    : 3) +
					       strlen(dserv) + 15) *
					 sizeof(char));
	    if(info && info->port >= 0)
	      sprintf(subtitle, "%s%s:%d",
		      dserv,
		      (info && info->serv && *info->serv) ? info->serv : "<?>",
		      info->port);
	    else
	      sprintf(subtitle, "%s%s",
		      dserv,
		      (info && info->serv && *info->serv) ? info->serv : "<?>");
	    (*cl)->next->value = subtitle;

	    fix_side_effects(ps, (*cl)->var, 0);
	    write_pinerc(ps);
	}
    }

    free_ldap_server_info(&info);
    if(raw_server)
      fs_give((void **)&raw_server);
}


#define   LDAP_F_IMPL  0
#define   LDAP_F_RHS   1
#define   LDAP_F_REF   2
#define   LDAP_F_NOSUB 3
bitmap_t  ldap_option_list;
struct variable *ldap_srch_rule_ptr;

/*
 * Gives user screen to edit config values for ldap server.
 *
 * Args    ps  -- pine struct
 *         def -- default values to start with
 *       title -- part of title at top of screen
 *  raw_server -- This is the returned item, allocated here and freed by caller.
 *
 * Returns:  0 if no change
 *           1 if user requested a change
 *               (change is stored in raw_server and hasn't been acted upon yet)
 *          10 user says abort
 */
int
dir_edit_screen(ps, def, title, raw_server)
    struct pine  *ps;
    LDAP_SERV_S  *def;
    char         *title;
    char        **raw_server;
{
    OPT_SCREEN_S    screen, *saved_screen;
    CONF_S         *ctmp = NULL, *ctmpb, *first_line = NULL;
    char            tmp[MAXPATH+1], custom_scope[MAXPATH];
    int             rv, i, j, lv;
    NAMEVAL_S      *f;
    struct variable server_var, base_var, port_var, nick_var,
		    srch_type_var, srch_rule_var, time_var,
		    size_var, mailattr_var, cnattr_var,
		    snattr_var, gnattr_var, cust_var,
		    opt_var, *v, *varlist[20];
    char           *server = NULL, *base = NULL, *port = NULL, *nick = NULL,
		   *srch_type = NULL, *srch_rule = NULL, *ttime = NULL,
		   *ssize = NULL, *mailattr = NULL, *cnattr = NULL,
		   *snattr = NULL, *gnattr = NULL, *cust = NULL;

    /*
     * We edit by making a nested call to conf_scroll_screen.
     * We use some fake struct variables to get back the results in, and
     * so we can use the existing tools from the config screen.
     */

    custom_scope[0] = '\0';

    varlist[j = 0] = &server_var;
    varlist[++j] = &base_var;
    varlist[++j] = &port_var;
    varlist[++j] = &nick_var;
    varlist[++j] = &srch_type_var;
    varlist[++j] = &srch_rule_var;
    varlist[++j] = &time_var;
    varlist[++j] = &size_var;
    varlist[++j] = &mailattr_var;
    varlist[++j] = &cnattr_var;
    varlist[++j] = &snattr_var;
    varlist[++j] = &gnattr_var;
    varlist[++j] = &cust_var;
    varlist[++j] = &opt_var;
    varlist[++j] = NULL;
    for(j = 0; varlist[j]; j++)
      memset(varlist[j], 0, sizeof(struct variable));

    server_var.name       = cpystr("ldap-server");
    server_var.is_used    = 1;
    server_var.is_user    = 1;
    server_var.user_val.p = (def && def->serv && def->serv[0])
				? cpystr(def->serv) : NULL;
    set_current_val(&server_var, FALSE, FALSE);

    base_var.name       = cpystr("search-base");
    base_var.is_used    = 1;
    base_var.is_user    = 1;
    base_var.user_val.p = (def && def->base && def->base[0])
				? cpystr(def->base) : NULL;
    set_current_val(&base_var, FALSE, FALSE);

    port_var.name       = cpystr("port");
    port_var.is_used    = 1;
    port_var.is_user    = 1;
    if(def && def->port >= 0)
      port_var.user_val.p = cpystr(int2string(def->port));

    port_var.global_val.p = cpystr(int2string(LDAP_PORT));
    set_current_val(&port_var, FALSE, FALSE);

    nick_var.name       = cpystr("nickname");
    nick_var.is_used    = 1;
    nick_var.is_user    = 1;
    nick_var.user_val.p = (def && def->nick && def->nick[0])
				? cpystr(def->nick) : NULL;
    set_current_val(&nick_var, FALSE, FALSE);

    srch_type_var.name       = cpystr("search-type");
    srch_type_var.is_used    = 1;
    srch_type_var.is_user    = 1;
    srch_type_var.user_val.p = (f=ldap_search_types(def ? def->type : -1))
						    ? cpystr(f->name) : NULL;
    srch_type_var.global_val.p =
	(f=ldap_search_types(DEF_LDAP_TYPE)) ? cpystr(f->name) : NULL;
    set_current_val(&srch_type_var, FALSE, FALSE);

    ldap_srch_rule_ptr = &srch_rule_var;	/* so radiobuttons can tell */
    srch_rule_var.name       = cpystr("search-rule");
    srch_rule_var.is_used    = 1;
    srch_rule_var.is_user    = 1;
    srch_rule_var.user_val.p = (f=ldap_search_rules(def ? def->srch : -1))
						    ? cpystr(f->name) : NULL;
    srch_rule_var.global_val.p =
	(f=ldap_search_rules(DEF_LDAP_SRCH)) ? cpystr(f->name) : NULL;
    set_current_val(&srch_rule_var, FALSE, FALSE);

    time_var.name       = cpystr("timelimit");
    time_var.is_used    = 1;
    time_var.is_user    = 1;
    if(def && def->time >= 0)
      time_var.user_val.p = cpystr(int2string(def->time));

    time_var.global_val.p = cpystr(int2string(DEF_LDAP_TIME));
    set_current_val(&time_var, FALSE, FALSE);

    size_var.name       = cpystr("sizelimit");
    size_var.is_used    = 1;
    size_var.is_user    = 1;
    if(def && def->size >= 0)
      size_var.user_val.p = cpystr(int2string(def->size));

    size_var.global_val.p = cpystr(int2string(DEF_LDAP_SIZE));
    set_current_val(&size_var, FALSE, FALSE);

    mailattr_var.name       = cpystr("email-attribute");
    mailattr_var.is_used    = 1;
    mailattr_var.is_user    = 1;
    mailattr_var.user_val.p = (def && def->mailattr && def->mailattr[0])
				? cpystr(def->mailattr) : NULL;
    mailattr_var.global_val.p = cpystr(DEF_LDAP_MAILATTR);
    set_current_val(&mailattr_var, FALSE, FALSE);

    cnattr_var.name       = cpystr("name-attribute");
    cnattr_var.is_used    = 1;
    cnattr_var.is_user    = 1;
    cnattr_var.user_val.p = (def && def->cnattr && def->cnattr[0])
				? cpystr(def->cnattr) : NULL;
    cnattr_var.global_val.p = cpystr(DEF_LDAP_CNATTR);
    set_current_val(&cnattr_var, FALSE, FALSE);

    snattr_var.name       = cpystr("surname-attribute");
    snattr_var.is_used    = 1;
    snattr_var.is_user    = 1;
    snattr_var.user_val.p = (def && def->snattr && def->snattr[0])
				? cpystr(def->snattr) : NULL;
    snattr_var.global_val.p = cpystr(DEF_LDAP_SNATTR);
    set_current_val(&snattr_var, FALSE, FALSE);

    gnattr_var.name       = cpystr("givenname-attribute");
    gnattr_var.is_used    = 1;
    gnattr_var.is_user    = 1;
    gnattr_var.user_val.p = (def && def->gnattr && def->gnattr[0])
				? cpystr(def->gnattr) : NULL;
    gnattr_var.global_val.p = cpystr(DEF_LDAP_GNATTR);
    set_current_val(&gnattr_var, FALSE, FALSE);

    cust_var.name       = cpystr("custom-search-filter");
    cust_var.is_used    = 1;
    cust_var.is_user    = 1;
    cust_var.user_val.p = (def && def->cust && def->cust[0])
				? cpystr(def->cust) : NULL;
    set_current_val(&cust_var, FALSE, FALSE);

    opt_var.name          = cpystr("features");
    opt_var.is_used       = 1;
    opt_var.is_user       = 1;
    opt_var.is_list       = 1;
    clrbitmap(ldap_option_list);
    if(def && def->impl)
      setbitn(LDAP_F_IMPL, ldap_option_list);
    if(def && def->rhs)
      setbitn(LDAP_F_RHS, ldap_option_list);
    if(def && def->ref)
      setbitn(LDAP_F_REF, ldap_option_list);
    if(def && def->nosub)
      setbitn(LDAP_F_NOSUB, ldap_option_list);

    /* save the old opt_screen before calling scroll screen again */
    saved_screen = opt_screen;

    /* Server */
    new_confline(&ctmp);
    ctmp->help_title= "HELP FOR LDAP SERVER";
    ctmp->var       = &server_var;
    ctmp->valoffset = 23;
    ctmp->keymenu   = &config_text_keymenu;
    ctmp->help      = h_config_ldap_server;
    ctmp->tool      = dir_edit_tool;
    sprintf(tmp, "%-20s =", server_var.name);
    ctmp->varname   = cpystr(tmp);
    ctmp->varnamep  = ctmp;
    ctmp->value     = pretty_value(ps, ctmp);

    first_line = ctmp;

    /* Search Base */
    new_confline(&ctmp);
    ctmp->help_title= "HELP FOR SERVER SEARCH BASE";
    ctmp->var       = &base_var;
    ctmp->valoffset = 23;
    ctmp->keymenu   = &config_text_keymenu;
    ctmp->help      = h_config_ldap_base;
    ctmp->tool      = dir_edit_tool;
    sprintf(tmp, "%-20s =", base_var.name);
    ctmp->varname   = cpystr(tmp);
    ctmp->varnamep  = ctmp;
    ctmp->value     = pretty_value(ps, ctmp);

    /* Port */
    new_confline(&ctmp);
    ctmp->help_title= "HELP FOR PORT NUMBER";
    ctmp->var       = &port_var;
    ctmp->valoffset = 23;
    ctmp->keymenu   = &config_text_keymenu;
    ctmp->help      = h_config_ldap_port;
    ctmp->tool      = dir_edit_tool;
    sprintf(tmp, "%-20s =", port_var.name);
    ctmp->varname   = cpystr(tmp);
    ctmp->varnamep  = ctmp;
    ctmp->value     = pretty_value(ps, ctmp);
    ctmp->flags    |= CF_NUMBER;

    /* Nickname */
    new_confline(&ctmp);
    ctmp->help_title= "HELP FOR SERVER NICKNAME";
    ctmp->var       = &nick_var;
    ctmp->valoffset = 23;
    ctmp->keymenu   = &config_text_keymenu;
    ctmp->help      = h_config_ldap_nick;
    ctmp->tool      = dir_edit_tool;
    sprintf(tmp, "%-20s =", nick_var.name);
    ctmp->varname   = cpystr(tmp);
    ctmp->varnamep  = ctmp;
    ctmp->value     = pretty_value(ps, ctmp);

    /* Blank line */
    new_confline(&ctmp);
    ctmp->flags    |= CF_NOSELECT | CF_B_LINE;

    /* Options */
    new_confline(&ctmp);
    ctmp->var       = &opt_var;
    ctmp->valoffset = 23;
    ctmp->keymenu   = &config_checkbox_keymenu;
    ctmp->help      = NO_HELP;
    ctmp->tool      = NULL;
    sprintf(tmp, "%-20s =", opt_var.name);
    ctmp->varname   = cpystr(tmp);
    ctmp->varnamep  = ctmpb = ctmp;
    ctmp->flags    |= (CF_NOSELECT | CF_STARTITEM);

    new_confline(&ctmp);
    ctmp->var       = NULL;
    ctmp->valoffset = 12;
    ctmp->keymenu   = &config_checkbox_keymenu;
    ctmp->help      = NO_HELP;
    ctmp->tool      = ldap_checkbox_tool;
    ctmp->varnamep  = ctmpb;
    ctmp->flags    |= CF_NOSELECT;
    ctmp->value     = cpystr("Set    Feature Name");

    new_confline(&ctmp);
    ctmp->var       = NULL;
    ctmp->valoffset = 12;
    ctmp->keymenu   = &config_checkbox_keymenu;
    ctmp->help      = NO_HELP;
    ctmp->tool      = ldap_checkbox_tool;
    ctmp->varnamep  = ctmpb;
    ctmp->flags    |= CF_NOSELECT;
    ctmp->value     = cpystr("---  ----------------------");

    /*  find longest value's name */
    for(lv = 0, i = 0; f = ldap_feature_list(i); i++)
      if(lv < (j = strlen(f->name)))
	lv = j;

    for(i = 0; f = ldap_feature_list(i); i++){
	new_confline(&ctmp);
	ctmp->var       = &opt_var;
	ctmp->help_title= "HELP FOR LDAP FEATURES";
	ctmp->varnamep  = ctmpb;
	ctmp->keymenu   = &config_checkbox_keymenu;
	switch(i){
	  case LDAP_F_IMPL:
	    ctmp->help      = h_config_ldap_opts_impl;
	    break;
	  case LDAP_F_RHS:
	    ctmp->help      = h_config_ldap_opts_rhs;
	    break;
	  case LDAP_F_REF:
	    ctmp->help      = h_config_ldap_opts_ref;
	    break;
	  case LDAP_F_NOSUB:
	    ctmp->help      = h_config_ldap_opts_nosub;
	    break;
	}
	ctmp->tool      = ldap_checkbox_tool;
	ctmp->valoffset = 12;
	ctmp->varmem    = i;
	sprintf(tmp, "[%c]  %-*.*s", 
		bitnset(f->value, ldap_option_list) ? 'X' : ' ',
		lv, lv, f->name);
	ctmp->value     = cpystr(tmp);
    }

    /* Blank line */
    new_confline(&ctmp);
    ctmp->flags    |= CF_NOSELECT | CF_B_LINE;

    /* Search Type */
    new_confline(&ctmp);
    ctmp->var       = &srch_type_var;
    ctmp->valoffset = 23;
    ctmp->keymenu   = &config_radiobutton_keymenu;
    ctmp->help      = NO_HELP;
    ctmp->tool      = NULL;
    sprintf(tmp, "%-20s =", srch_type_var.name);
    ctmp->varname   = cpystr(tmp);
    ctmp->varnamep  = ctmpb = ctmp;
    ctmp->flags    |= (CF_NOSELECT | CF_STARTITEM);

    new_confline(&ctmp);
    ctmp->var       = NULL;
    ctmp->valoffset = 12;
    ctmp->keymenu   = &config_radiobutton_keymenu;
    ctmp->help      = NO_HELP;
    ctmp->tool      = NULL;
    ctmp->varnamep  = ctmpb;
    ctmp->flags    |= CF_NOSELECT;
    ctmp->value     = cpystr("Set    Rule Values");

    new_confline(&ctmp);
    ctmp->var       = NULL;
    ctmp->valoffset = 12;
    ctmp->keymenu   = &config_radiobutton_keymenu;
    ctmp->help      = NO_HELP;
    ctmp->tool      = ldap_radiobutton_tool;
    ctmp->varnamep  = ctmpb;
    ctmp->flags    |= CF_NOSELECT;
    ctmp->value     = cpystr("---  ----------------------");

    /* find longest value's name */
    for(lv = 0, i = 0; f = ldap_search_types(i); i++)
      if(lv < (j = strlen(f->name)))
	lv = j;
    
    for(i = 0; f = ldap_search_types(i); i++){
	new_confline(&ctmp);
	ctmp->help_title= "HELP FOR SEARCH TYPE";
	ctmp->var       = &srch_type_var;
	ctmp->valoffset = 12;
	ctmp->keymenu   = &config_radiobutton_keymenu;
	ctmp->help      = h_config_ldap_searchtypes;
	ctmp->varmem    = i;
	ctmp->tool      = ldap_radiobutton_tool;
	ctmp->varnamep  = ctmpb;
	sprintf(tmp, "(%c)  %-*.*s", (((!def || def->type == -1) &&
				        f->value == DEF_LDAP_TYPE) ||
				      (def && f->value == def->type))
				         ? R_SELD : ' ',
		lv, lv, f->name);
	ctmp->value     = cpystr(tmp);
    }

    /* Blank line */
    new_confline(&ctmp);
    ctmp->flags    |= CF_NOSELECT | CF_B_LINE;
    ctmp->varname   = cpystr("");

    /* Search Rule */
    new_confline(&ctmp);
    ctmp->var       = &srch_rule_var;
    ctmp->valoffset = 23;
    ctmp->keymenu   = &config_radiobutton_keymenu;
    ctmp->help      = NO_HELP;
    ctmp->tool      = NULL;
    sprintf(tmp, "%-20s =", srch_rule_var.name);
    ctmp->varname   = cpystr(tmp);
    ctmp->varnamep  = ctmpb = ctmp;
    ctmp->flags    |= (CF_NOSELECT | CF_STARTITEM);

    /* Search Rule */
    new_confline(&ctmp);
    ctmp->var       = NULL;
    ctmp->valoffset = 12;
    ctmp->keymenu   = &config_radiobutton_keymenu;
    ctmp->help      = NO_HELP;
    ctmp->tool      = NULL;
    ctmp->varnamep  = ctmpb;
    ctmp->flags    |= CF_NOSELECT;
    ctmp->value     = cpystr("Set    Rule Values");

    new_confline(&ctmp);
    ctmp->var       = NULL;
    ctmp->valoffset = 12;
    ctmp->keymenu   = &config_radiobutton_keymenu;
    ctmp->help      = NO_HELP;
    ctmp->tool      = ldap_radiobutton_tool;
    ctmp->varnamep  = ctmpb;
    ctmp->flags    |= CF_NOSELECT;
    ctmp->value     = cpystr("---  ----------------------");

    /* find longest value's name */
    for(lv = 0, i = 0; f = ldap_search_rules(i); i++)
      if(lv < (j = strlen(f->name)))
	lv = j;
    
    for(i = 0; f = ldap_search_rules(i); i++){
	new_confline(&ctmp);
	ctmp->help_title= "HELP FOR SEARCH RULE";
	ctmp->var       = &srch_rule_var;
	ctmp->valoffset = 12;
	ctmp->keymenu   = &config_radiobutton_keymenu;
	ctmp->help      = h_config_ldap_searchrules;
	ctmp->varmem    = i;
	ctmp->tool      = ldap_radiobutton_tool;
	ctmp->varnamep  = ctmpb;
	sprintf(tmp, "(%c)  %-*.*s", (((!def || def->srch == -1) &&
				        f->value == DEF_LDAP_SRCH) ||
				      (def && f->value == def->srch))
				         ? R_SELD : ' ',
		lv, lv, f->name);
	ctmp->value     = cpystr(tmp);
    }

    /* Blank line */
    new_confline(&ctmp);
    ctmp->flags    |= CF_NOSELECT | CF_B_LINE;
    ctmp->varname   = cpystr("");

    /* Email attribute name */
    new_confline(&ctmp);
    ctmp->help_title= "HELP FOR EMAIL ATTRIBUTE NAME";
    ctmp->var       = &mailattr_var;
    ctmp->valoffset = 23;
    ctmp->keymenu   = &config_text_keymenu;
    ctmp->help      = h_config_ldap_email_attr;
    ctmp->tool      = dir_edit_tool;
    sprintf(tmp, "%-20s =", mailattr_var.name);
    ctmp->varname   = cpystr(tmp);
    ctmp->varnamep  = ctmp;
    ctmp->value     = pretty_value(ps, ctmp);

    /* Name attribute name */
    new_confline(&ctmp);
    ctmp->help_title= "HELP FOR NAME ATTRIBUTE NAME";
    ctmp->var       = &cnattr_var;
    ctmp->valoffset = 23;
    ctmp->keymenu   = &config_text_keymenu;
    ctmp->help      = h_config_ldap_cn_attr;
    ctmp->tool      = dir_edit_tool;
    sprintf(tmp, "%-20s =", cnattr_var.name);
    ctmp->varname   = cpystr(tmp);
    ctmp->varnamep  = ctmp;
    ctmp->value     = pretty_value(ps, ctmp);

    /* Surname attribute name */
    new_confline(&ctmp);
    ctmp->help_title= "HELP FOR SURNAME ATTRIBUTE NAME";
    ctmp->var       = &snattr_var;
    ctmp->valoffset = 23;
    ctmp->keymenu   = &config_text_keymenu;
    ctmp->help      = h_config_ldap_sn_attr;
    ctmp->tool      = dir_edit_tool;
    sprintf(tmp, "%-20s =", snattr_var.name);
    ctmp->varname   = cpystr(tmp);
    ctmp->varnamep  = ctmp;
    ctmp->value     = pretty_value(ps, ctmp);

    /* Givenname attribute name */
    new_confline(&ctmp);
    ctmp->help_title= "HELP FOR GIVEN NAME ATTRIBUTE NAME";
    ctmp->var       = &gnattr_var;
    ctmp->valoffset = 23;
    ctmp->keymenu   = &config_text_keymenu;
    ctmp->help      = h_config_ldap_gn_attr;
    ctmp->tool      = dir_edit_tool;
    sprintf(tmp, "%-20s =", gnattr_var.name);
    ctmp->varname   = cpystr(tmp);
    ctmp->varnamep  = ctmp;
    ctmp->value     = pretty_value(ps, ctmp);

    /* Blank line */
    new_confline(&ctmp);
    ctmp->flags    |= CF_NOSELECT | CF_B_LINE;
    ctmp->varname   = cpystr("");

    /* Time limit */
    new_confline(&ctmp);
    ctmp->help_title= "HELP FOR SERVER TIMELIMIT";
    ctmp->var       = &time_var;
    ctmp->valoffset = 23;
    ctmp->keymenu   = &config_text_keymenu;
    ctmp->help      = h_config_ldap_time;
    ctmp->tool      = dir_edit_tool;
    sprintf(tmp, "%-20s =", time_var.name);
    ctmp->varname   = cpystr(tmp);
    ctmp->varnamep  = ctmp;
    ctmp->value     = pretty_value(ps, ctmp);
    ctmp->flags    |= CF_NUMBER;

    /* Size limit */
    new_confline(&ctmp);
    ctmp->var       = &size_var;
    ctmp->help_title= "HELP FOR SERVER SIZELIMIT";
    ctmp->valoffset = 23;
    ctmp->keymenu   = &config_text_keymenu;
    ctmp->help      = h_config_ldap_size;
    ctmp->tool      = dir_edit_tool;
    sprintf(tmp, "%-20s =", size_var.name);
    ctmp->varname   = cpystr(tmp);
    ctmp->varnamep  = ctmp;
    ctmp->value     = pretty_value(ps, ctmp);
    ctmp->flags    |= CF_NUMBER;

    /* Blank line */
    new_confline(&ctmp);
    ctmp->flags    |= CF_NOSELECT | CF_B_LINE;

    /* Custom Search Filter */
    new_confline(&ctmp);
    ctmp->help_title= "HELP FOR CUSTOM SEARCH FILTER";
    ctmp->var       = &cust_var;
    ctmp->valoffset = 23;
    ctmp->keymenu   = &config_text_keymenu;
    ctmp->help      = h_config_ldap_cust;
    ctmp->tool      = dir_edit_tool;
    sprintf(tmp, "%-20s =", cust_var.name);
    ctmp->varname   = cpystr(tmp);
    ctmp->varnamep  = ctmp;
    ctmp->value     = pretty_value(ps, ctmp);


    sprintf(tmp, "%s DIRECTORY SERVER", title);
    rv = conf_scroll_screen(ps, &screen, first_line, tmp, "servers ", 1, 0);

    /*
     * Now look at the fake variables and extract the information we
     * want from them.
     */

    if(rv == 1 && raw_server){
	char dir_tmp[MAILTMPLEN], *p;
	int portval = -1, timeval = -1, sizeval = -1;

	server = server_var.user_val.p;
	server_var.user_val.p = NULL;
	base = base_var.user_val.p;
	base_var.user_val.p = NULL;
	port = port_var.user_val.p;
	port_var.user_val.p = NULL;
	nick = nick_var.user_val.p;
	nick_var.user_val.p = NULL;
	srch_type = srch_type_var.user_val.p;
	srch_type_var.user_val.p = NULL;
	srch_rule = srch_rule_var.user_val.p;
	srch_rule_var.user_val.p = NULL;
	ttime = time_var.user_val.p;
	time_var.user_val.p = NULL;
	ssize = size_var.user_val.p;
	size_var.user_val.p = NULL;
	cust = cust_var.user_val.p;
	cust_var.user_val.p = NULL;
	mailattr = mailattr_var.user_val.p;
	mailattr_var.user_val.p = NULL;
	snattr = snattr_var.user_val.p;
	snattr_var.user_val.p = NULL;
	gnattr = gnattr_var.user_val.p;
	gnattr_var.user_val.p = NULL;
	cnattr = cnattr_var.user_val.p;
	cnattr_var.user_val.p = NULL;

	if(server)
	  removing_leading_and_trailing_white_space(server);

	if(base){
	    removing_leading_and_trailing_white_space(base);
	    removing_double_quotes(base);
	    p = add_backslash_escapes(base);
	    fs_give((void **)&base);
	    base = p;
	}

	if(port){
	    removing_leading_and_trailing_white_space(port);
	    if(*port)
	      portval = atoi(port);
	}
	
	if(nick){
	    removing_leading_and_trailing_white_space(nick);
	    removing_double_quotes(nick);
	    p = add_backslash_escapes(nick);
	    fs_give((void **)&nick);
	    nick = p;
	}

	if(ttime){
	    removing_leading_and_trailing_white_space(ttime);
	    if(*ttime)
	      timeval = atoi(ttime);
	}
	
	if(ssize){
	    removing_leading_and_trailing_white_space(ssize);
	    if(*ssize)
	      sizeval = atoi(ssize);
	}
	
	if(cust){
	    removing_leading_and_trailing_white_space(cust);
	    p = add_backslash_escapes(cust);
	    fs_give((void **)&cust);
	    cust = p;
	}

	if(mailattr){
	    removing_leading_and_trailing_white_space(mailattr);
	    p = add_backslash_escapes(mailattr);
	    fs_give((void **)&mailattr);
	    mailattr = p;
	}

	if(snattr){
	    removing_leading_and_trailing_white_space(snattr);
	    p = add_backslash_escapes(snattr);
	    fs_give((void **)&snattr);
	    snattr = p;
	}

	if(gnattr){
	    removing_leading_and_trailing_white_space(gnattr);
	    p = add_backslash_escapes(gnattr);
	    fs_give((void **)&gnattr);
	    gnattr = p;
	}

	if(cnattr){
	    removing_leading_and_trailing_white_space(cnattr);
	    p = add_backslash_escapes(cnattr);
	    fs_give((void **)&cnattr);
	    cnattr = p;
	}

	/*
	 * Don't allow user to edit scope but if one is present then we
	 * leave it (so they could edit it by hand).
	 */
	if(def && def->scope != -1 && def->scope != DEF_LDAP_SCOPE){
	    NAMEVAL_S *v;

	    v = ldap_search_scope(def->scope);
	    if(v)
	      sprintf(custom_scope, "/scope=%s", v->name);
	}

	sprintf(dir_tmp, "%s%s%s \"/base=%s/impl=%d/rhs=%d/ref=%d/nosub=%d/type=%s/srch=%s%s/time=%s/size=%s/cust=%s/nick=%s/matr=%s/catr=%s/satr=%s/gatr=%s\"",
		server ? server : "",
		(portval >= 0 && port && *port) ? ":" : "",
		(portval >= 0 && port && *port) ? port : "",
		base ? base : "",
		bitnset(LDAP_F_IMPL, ldap_option_list) ? 1 : 0,
		bitnset(LDAP_F_RHS, ldap_option_list) ? 1 : 0,
		bitnset(LDAP_F_REF, ldap_option_list) ? 1 : 0,
		bitnset(LDAP_F_NOSUB, ldap_option_list) ? 1 : 0,
		srch_type ? srch_type : "",
		srch_rule ? srch_rule : "",
		custom_scope,
		(timeval >= 0 && ttime && *ttime) ? ttime : "",
		(sizeval >= 0 && ssize && *ssize) ? ssize : "",
		cust ? cust : "",
		nick ? nick : "",
		mailattr ? mailattr : "",
		cnattr ? cnattr : "",
		snattr ? snattr : "",
		gnattr ? gnattr : "");
	
	*raw_server = cpystr(dir_tmp);
    }

    for(j = 0; varlist[j]; j++){
	v = varlist[j];
	if(v->current_val.p)
	  fs_give((void **)&v->current_val.p);
	if(v->global_val.p)
	  fs_give((void **)&v->global_val.p);
	if(v->user_val.p)
	  fs_give((void **)&v->user_val.p);
	if(v->name)
	  fs_give((void **)&v->name);
    }

    if(server)
      fs_give((void **)&server);
    if(base)
      fs_give((void **)&base);
    if(port)
      fs_give((void **)&port);
    if(nick)
      fs_give((void **)&nick);
    if(srch_type)
      fs_give((void **)&srch_type);
    if(srch_rule)
      fs_give((void **)&srch_rule);
    if(ttime)
      fs_give((void **)&ttime);
    if(ssize)
      fs_give((void **)&ssize);
    if(mailattr)
      fs_give((void **)&mailattr);
    if(cnattr)
      fs_give((void **)&cnattr);
    if(snattr)
      fs_give((void **)&snattr);
    if(gnattr)
      fs_give((void **)&gnattr);
    if(cust)
      fs_give((void **)&cust);

    opt_screen = saved_screen;
    ps->mangled_screen = 1;
    return(rv);
}


/*
 * Just calls text_tool except for intercepting MC_EXIT.
 */
int
dir_edit_tool(ps, cmd, cl, flags)
    struct pine  *ps;
    int		  cmd;
    CONF_S      **cl;
    unsigned      flags;
{
    if(cmd == MC_EXIT)
      return(config_exit_cmd(flags));
    else
      return(text_tool(ps, cmd, cl, flags));
}


/*
 * Delete LDAP directory entry
 */
void
dir_config_del(ps, cl)
    struct pine  *ps;
    CONF_S      **cl;
{
    char    prompt[81];
    int     rv = 0, i;

    if((*cl)->var->is_fixed){
	if((*cl)->var->user_val.l){
	    sprintf(prompt, "Delete (unused) directory servers (%.20s%s) ",
		   ((*cl)->var->user_val.l[0]) ? (*cl)->var->user_val.l[0] : "",
		   ((*cl)->var->user_val.l[1]) ? "..." : "");
	    if(want_to(prompt, 'n', 'n', NO_HELP, WT_FLUSH_IN) == 'y'){
		rv = 1;
		for(i = 0; (*cl)->var->user_val.l[i]; i++)
		  fs_give((void **)&(*cl)->var->user_val.l[i]);
		
		fs_give((void **)&(*cl)->var->user_val.l);
	    }
	}
	else
	  q_status_message(SM_ORDER, 3, 3,
			   "Can't delete sys-admin defined value");
    }
    else{
	int cnt, ans = 0;
	char  **new_list;

	/* This can't happen, intercepted at caller by first_one case */
	if((*cl)->var->user_val.l && (*cl)->var->user_val.l[0] &&
	   (*cl)->var->user_val.l[0][0] == '\0')
	  ans = 'r';

	/* how many servers defined? */
	for(cnt = 0; (*cl)->var->current_val.l[cnt]; cnt++)
	  ;

	/*
	 * If using default and there is more than one in list, ask if user
	 * wants to ignore them all or delete just this one. If just this
	 * one, copy rest to user_val. If ignore all, copy "" to user_val
	 * to override.
	 */
	if(!(*cl)->var->user_val.l && cnt > 1){
	    static ESCKEY_S opts[] = {
		{'i', 'i', "I", "Ignore All"},
		{'r', 'r', "R", "Remove One"},
		{-1, 0, NULL, NULL}};
	    ans = radio_buttons(
	"Ignore all default directory servers or just remove this one ? ",
				-FOOTER_ROWS(ps), opts, 'i', 'x',
				h_ab_del_dir_ignore, RB_NORM);
	}

	if(ans == 0)
	  sprintf(prompt, "Really delete %s \"%.30s\" from directory servers ",
		  ((*cl)->value && *(*cl)->value)
		      ? "server"
		      : "item",
		  ((*cl)->value && *(*cl)->value)
		      ? (*cl)->value
		      : int2string((*cl)->varmem + 1));
	

	ps->mangled_footer = 1;
	if(ans == 'i'){
	    rv = ps->mangled_body = 1;

	    /*
	     * Ignore all of default by adding an empty string. Make it
	     * look just like there are no servers defined.
	     */

	    new_list = (char **)fs_get((1 + 1) * sizeof(char *));
	    new_list[0] = cpystr("");
	    new_list[1] = NULL;
	    set_variable_list(V_LDAP_SERVERS, new_list, FALSE);
	    free_list_array(&new_list);
	    *cl = first_confline(*cl);
	    free_conflines(cl);
	    opt_screen->top_line = NULL;

	    add_ldap_fake_first_server(ps, cl, &ps->vars[V_LDAP_SERVERS],
				       &dir_conf_km, h_direct_config,
				       dir_config_tool);
	}
	else if(ans == 'r' ||
	       (ans != 'x' &&
	        want_to(prompt, 'n', 'n', NO_HELP, WT_FLUSH_IN) == 'y')){
	    CONF_S *cp;
	    char    **servers;
	    int       move_top = 0, this_one, revert_to_default,
		      default_there_to_revert_to;

	    /*
	     * Remove one from current list.
	     */

	    rv = ps->mangled_body = 1;

	    this_one = (*cl)->varmem;

	    /* might have to re-adjust screen to see new current */
	    move_top = (this_one > 0) &&
		       (this_one == cnt - 1) &&
		       (((*cl)    == opt_screen->top_line) ||
		        ((*cl)->prev == opt_screen->top_line) ||
		        ((*cl)->prev->prev == opt_screen->top_line));

	    /*
	     * If this is last one and there is a default available, revert
	     * to it.
	     */
	    revert_to_default = ((cnt == 1) && (*cl)->var->user_val.l);
	    if(cnt > 1){
		new_list = (char **)fs_get((cnt + 1) * sizeof(char *));
		for(i = 0; i < this_one; i++)
		  new_list[i] = cpystr((*cl)->var->current_val.l[i]);

		for(i = this_one; i < cnt; i++)
		  new_list[i] = cpystr((*cl)->var->current_val.l[i+1]);

		set_variable_list(V_LDAP_SERVERS, new_list, FALSE);
		free_list_array(&new_list);
	    }
	    else if(revert_to_default){
		if((*cl)->var->user_val.l){
		    if((*cl)->var->user_val.l[0])
		      fs_give((void **)&(*cl)->var->user_val.l[0]);

		    fs_give((void **)&(*cl)->var->user_val.l);
		}
	    }
	    else{
		/* cnt is one and we want to hide default */
		new_list = (char **)fs_get((1 + 1) * sizeof(char *));
		new_list[0] = cpystr("");
		new_list[1] = NULL;
		set_variable_list(V_LDAP_SERVERS, new_list, FALSE);
		free_list_array(&new_list);
	    }
		
	    if(cnt == 1){	/* delete display line for this_one */
		if(revert_to_default){
		    servers = (*cl)->var->global_val.l;
		    default_there_to_revert_to = (servers != NULL);
		}

		*cl = first_confline(*cl);
		free_conflines(cl);
		opt_screen->top_line = NULL;
		if(revert_to_default && default_there_to_revert_to){
		    CONF_S   *first_line = NULL;

		    q_status_message(SM_ORDER, 0, 3,
				 "Reverting to default directory server");
		    dir_init_display(ps, cl, servers,
				   &ps->vars[V_LDAP_SERVERS], &first_line);
		    *cl = first_line;
		}
		else{
		    add_ldap_fake_first_server(ps, cl,
					       &ps->vars[V_LDAP_SERVERS],
					       &dir_conf_km, h_direct_config,
					       dir_config_tool);
		}
	    }
	    else if(this_one == cnt - 1){	/* deleted last one */
		/* back up and delete it */
		*cl = (*cl)->prev;
		free_conflines(&(*cl)->next);
		/* now back up to first line of this server */
		*cl = (*cl)->prev->prev;
		if(move_top)
		  opt_screen->top_line = *cl;
	    }
	    else{			/* deleted one out of the middle */
		if(*cl == opt_screen->top_line)
		  opt_screen->top_line = (*cl)->next->next->next;

		cp = *cl;
		*cl = (*cl)->next;	/* move to next line, then */
		snip_confline(&cp);	/* snip 1st deleted line   */
		cp = *cl;
		*cl = (*cl)->next;	/* move to next line, then */
		snip_confline(&cp);	/* snip 2nd deleted line   */
		cp = *cl;
		*cl = (*cl)->next;	/* move to next line, then */
		snip_confline(&cp);	/* snip 3rd deleted line   */
		/* adjust varmems */
		for(cp = *cl; cp; cp = cp->next)
		  cp->varmem--;
	    }
	}
	else
	  q_status_message(SM_ORDER, 0, 3, "Server not deleted");
    }

    if(rv == 1){
	set_current_val((*cl)->var, TRUE, FALSE);
	fix_side_effects(ps, (*cl)->var, 0);
	write_pinerc(ps);
    }
}


/*
 * Utility routine to help set up display
 */
void
add_ldap_fake_first_server(ps, ctmp, var, km, help, tool)
    struct pine     *ps;
    CONF_S         **ctmp;
    struct variable *var;
    struct key_menu *km;
    HelpType	     help;
    int		   (*tool)();
{
    new_confline(ctmp);
    (*ctmp)->help_title= "HELP FOR DIRECTORY SERVER CONFIGURATION";
    (*ctmp)->value     = cpystr(ADD_FIRST_LDAP_SERVER);
    (*ctmp)->var       = var;
    (*ctmp)->varmem    = 0;
    (*ctmp)->keymenu   = km;
    (*ctmp)->help      = help;
    (*ctmp)->tool      = tool;
    (*ctmp)->valoffset = 2;
}


/*
 * Add an ldap server to the display list.
 *
 * Args  before -- Insert it before current, else append it after.
 */
void
add_ldap_server_to_display(ps, ctmp, serv, subtitle, var, member, km, help,
			   tool, before, first_line)
    struct pine     *ps;
    CONF_S         **ctmp;
    char            *serv;
    char            *subtitle;
    struct variable *var;
    int              member;
    struct key_menu *km;
    HelpType	     help;
    int		   (*tool)();
    int              before;
    CONF_S         **first_line;
{
    new_confline(ctmp);
    if(first_line)
      *first_line = *ctmp;

    if(before){
	/*
	 * New_confline appends ctmp after old current instead of inserting
	 * it, so we have to adjust. We have
	 *  <- a <-> b <-> p <-> c -> and want <- a <-> p <-> b <-> c ->
	 */

	CONF_S *a, *b, *c, *p;

	p = *ctmp;
	b = (*ctmp)->prev;
	c = (*ctmp)->next;
	a = b ? b->prev : NULL;
	if(a)
	  a->next = p;

	if(b){
	    b->prev = p;
	    b->next = c;
	}

	if(c)
	  c->prev = b;

	p->prev = a;
	p->next = b;
    }

    (*ctmp)->help_title= "HELP FOR DIRECTORY SERVER CONFIGURATION";
    (*ctmp)->value     = serv;
    (*ctmp)->var       = var;
    (*ctmp)->varmem    = member;
    (*ctmp)->keymenu   = km;
    (*ctmp)->help      = help;
    (*ctmp)->tool      = tool;
    (*ctmp)->flags    |= CF_STARTITEM;
    (*ctmp)->valoffset = 4;

    new_confline(ctmp);
    (*ctmp)->value     = subtitle;
    (*ctmp)->keymenu   = km;
    (*ctmp)->help      = help;
    (*ctmp)->tool      = tool;
    (*ctmp)->flags    |= CF_NOSELECT;
    (*ctmp)->valoffset = 8;

    new_confline(ctmp);
    (*ctmp)->keymenu   = km;
    (*ctmp)->help      = help;
    (*ctmp)->tool      = tool;
    (*ctmp)->flags    |= CF_NOSELECT | CF_B_LINE;
    (*ctmp)->valoffset = 0;
}


/*
 * ldap option list manipulation tool
 * 
 * 
 * returns:  -1 on unrecognized cmd, 0 if no change, 1 if change
 */
int
ldap_checkbox_tool(ps, cmd, cl, flags)
    struct pine  *ps;
    int		  cmd;
    CONF_S	**cl;
    unsigned      flags;
{
    int  rv = 0;

    switch(cmd){
      case MC_TOGGLE:				/* mark/unmark option */
	rv = 1;
	toggle_ldap_option_bit(ps, (*cl)->varmem, (*cl)->var, (*cl)->value);
	break;

      case MC_EXIT:				 /* exit */
        rv = config_exit_cmd(flags);
	break;

      default :
	rv = -1;
	break;
    }

    return(rv);
}


void
toggle_ldap_option_bit(ps, index, var, value)
    struct pine     *ps;
    int		     index;
    struct variable *var;
    char            *value;
{
    NAMEVAL_S  *f;
    char      **vp, *p;

    f  = ldap_feature_list(index);

    /* flip the bit */
    if(bitnset(f->value, ldap_option_list))
      clrbitn(f->value, ldap_option_list);
    else
      setbitn(f->value, ldap_option_list);

    if(value)
      value[1] = bitnset(f->value, ldap_option_list) ? 'X' : ' ';
}


NAMEVAL_S *
ldap_feature_list(index)
    int index;
{
    static NAMEVAL_S ldap_feat_list[] = {
	{"use-implicitly-from-composer",      LDAP_F_IMPL},
	{"lookup-addrbook-contents",          LDAP_F_RHS},
	{"save-search-criteria-not-result",   LDAP_F_REF},
	{"disable-ad-hoc-space-substitution", LDAP_F_NOSUB}
    };

    return((index >= 0 &&
	    index < (sizeof(ldap_feat_list)/sizeof(ldap_feat_list[0])))
		   ? &ldap_feat_list[index] : NULL);
}


NAMEVAL_S *
ldap_search_rules(index)
    int index;
{
    static NAMEVAL_S ldap_search_list[] = {
	{"contains",		LDAP_SRCH_CONTAINS},
	{"equals",		LDAP_SRCH_EQUALS},
	{"begins-with",		LDAP_SRCH_BEGINS},
	{"ends-with",		LDAP_SRCH_ENDS}
    };

    return((index >= 0 &&
	    index < (sizeof(ldap_search_list)/sizeof(ldap_search_list[0])))
		   ? &ldap_search_list[index] : NULL);
}


NAMEVAL_S *
ldap_search_types(index)
    int index;
{
    static NAMEVAL_S ldap_types_list[] = {
	{"name",				LDAP_TYPE_CN},
	{"surname",				LDAP_TYPE_SUR},
	{"givenname",				LDAP_TYPE_GIVEN},
	{"email",				LDAP_TYPE_EMAIL},
	{"name-or-email",			LDAP_TYPE_CN_EMAIL},
	{"surname-or-givenname",		LDAP_TYPE_SUR_GIVEN},
	{"sur-or-given-or-name-or-email",	LDAP_TYPE_SEVERAL}
    };

    return((index >= 0 &&
	    index < (sizeof(ldap_types_list)/sizeof(ldap_types_list[0])))
		   ? &ldap_types_list[index] : NULL);
}


NAMEVAL_S *
ldap_search_scope(index)
    int index;
{
    static NAMEVAL_S ldap_scope_list[] = {
	{"base",		LDAP_SCOPE_BASE},
	{"onelevel",		LDAP_SCOPE_ONELEVEL},
	{"subtree",		LDAP_SCOPE_SUBTREE}
    };

    return((index >= 0 &&
	    index < (sizeof(ldap_scope_list)/sizeof(ldap_scope_list[0])))
		   ? &ldap_scope_list[index] : NULL);
}


/*
 * simple radio-button style variable handler
 */
int
ldap_radiobutton_tool(ps, cmd, cl, flags)
    struct pine  *ps;
    int	          cmd;
    CONF_S      **cl;
    unsigned      flags;
{
    int	       rv = 0;
    CONF_S    *ctmp;
    NAMEVAL_S *rule;

    switch(cmd){
      case MC_CHOICE :				/* set/unset feature */

	/* hunt backwards, turning off old values */
	for(ctmp = *cl; ctmp && !(ctmp->flags & CF_NOSELECT) && !ctmp->varname;
	    ctmp = prev_confline(ctmp))
	  ctmp->value[1] = ' ';

	/* hunt forwards, turning off old values */
	for(ctmp = *cl; ctmp && !(ctmp->flags & CF_NOSELECT) && !ctmp->varname;
	    ctmp = next_confline(ctmp))
	  ctmp->value[1] = ' ';

	/* turn on current value */
	(*cl)->value[1] = R_SELD;

	if((*cl)->var == ldap_srch_rule_ptr)
	  rule = ldap_search_rules((*cl)->varmem);
	else
	  rule = ldap_search_types((*cl)->varmem);

	if((*cl)->var->user_val.p)
	  fs_give((void **)&(*cl)->var->user_val.p);

	(*cl)->var->user_val.p = cpystr(rule->name);

	ps->mangled_body = 1;	/* BUG: redraw it all for now? */
	rv = 1;

	break;

      case MC_EXIT:				/* exit */
	rv = config_exit_cmd(flags);
	break;

      default :
	rv = -1;
	break;
    }

    return(rv);
}
#endif	/* ENABLE_LDAP */


/*
 * Handles screen painting and motion.  Passes other commands to
 * custom tools.
 *
 * Tool return values:  Tools should return the following:
 *     0 nothing changed
 *    -1 unrecognized command
 *     1 something changed, conf_scroll_screen should remember that
 *     2 tells conf_scroll_screen to return with value 1 or 0 depending
 *       on whether or not it has previously gotten a 1 from some tool.
 *     3 tells conf_scroll_screen to return 1 (like 1 and 2 combined)
 *     ? Other tool-specific values can be used.  They will cause
 *       conf_scroll_screen to return that value.
 *
 * Return values:
 *     0 if nothing happened.  That is, a tool returned 2 and we hadn't
 *       previously noted a return of 1
 *     1 if something happened.  That is, a tool returned 2 and we had
 *       previously noted a return of 1
 *     ? Tool-returned value different from -1, 0, 1, or 2.  This is it.
 *
 * Special proviso: If first_line->flags has CF_CHANGES set on entry, then
 * that will cause things to behave like a change was made since entering
 * this function.
 */
int
conf_scroll_screen(ps, screen, start_line, title, pdesc, edit_config, multicol)
    struct pine  *ps;
    OPT_SCREEN_S *screen;
    CONF_S       *start_line;
    char         *title;
    char	 *pdesc;
    int		  edit_config;
    int		  multicol;
{
    char	  tmp[MAXPATH+1];
    int		  cmd, i, j, ch = 'x', done = 0, changes = 0;
    int		  retval = 0;
    int		  km_popped = 0, stay_in_col = 0;
    struct	  key_menu  *km = NULL;
    CONF_S	 *ctmpa = NULL, *ctmpb = NULL;
    Pos           cursor_pos;
    OtherMenu	  what_keymenu = FirstMenu;
    void        (*prev_redrawer) ();

    memset(screen, 0, sizeof(OPT_SCREEN_S));
    screen->current    = start_line;
    if(start_line && start_line->flags & CF_CHANGES)
      changes++;

    opt_screen	       = screen;
    ps->mangled_screen = 1;
    ps->redrawer       = option_screen_redrawer;

    while(!done){
	if(km_popped){
	    km_popped--;
	    if(km_popped == 0){
		clearfooter(ps);
		ps->mangled_body = 1;
	    }
	}

	if(ps->mangled_screen){
	    ps->mangled_header = 1;
	    ps->mangled_footer = 1;
	    ps->mangled_body   = 1;
	    ps->mangled_screen = 0;
	}

	/*----------- Check for new mail -----------*/
        if(new_mail(0, NM_TIMING(ch), NM_STATUS_MSG | NM_DEFER_SORT) >= 0)
          ps->mangled_header = 1;

	if(ps->mangled_header){
	    set_titlebar(title, ps->mail_stream,
			 ps->context_current,
			 ps->cur_folder, ps->msgmap, 1, FolderName, 0, 0);
	    ps->mangled_header = 0;
	}

	update_option_screen(ps, screen, &cursor_pos);

	if(F_OFF(F_SHOW_CURSOR, ps)){
	    cursor_pos.row = ps->ttyo->screen_rows - FOOTER_ROWS(ps);
	    cursor_pos.col = 0;
	}

	/*---- This displays new mail notification, or errors ---*/
	if(km_popped){
	    FOOTER_ROWS(ps) = 3;
	    mark_status_unknown();
	}

        display_message(ch);
	if(km_popped){
	    FOOTER_ROWS(ps) = 1;
	    mark_status_unknown();
	}

	if(ps->mangled_footer || km != screen->current->keymenu){
	    bitmap_t	 bitmap;

	    setbitmap(bitmap);

	    ps->mangled_footer = 0;
	    km                 = screen->current->keymenu;

	    if(multicol &&
	       (F_OFF(F_ARROW_NAV, ps_global)) ||
	        F_ON(F_RELAXED_ARROW_NAV, ps_global)){
		menu_clear_binding(km, KEY_LEFT);
		menu_clear_binding(km, KEY_RIGHT);
		menu_clear_binding(km, KEY_UP);
		menu_clear_binding(km, KEY_DOWN);
		menu_add_binding(km, KEY_UP, MC_CHARUP);
		menu_add_binding(km, KEY_DOWN, MC_CHARDOWN);	
		menu_add_binding(km, KEY_LEFT, MC_PREVITEM);
		menu_add_binding(km, ctrl('B'), MC_PREVITEM);
		menu_add_binding(km, KEY_RIGHT, MC_NEXTITEM);
		menu_add_binding(km, ctrl('F'), MC_NEXTITEM);
	    }
	    else{
		menu_clear_binding(km, KEY_LEFT);
		menu_clear_binding(km, KEY_RIGHT);
		menu_clear_binding(km, KEY_UP);
		menu_clear_binding(km, KEY_DOWN);

		/*
		 * Fix up arrow nav mode if necessary...
		 */
		if(F_ON(F_ARROW_NAV, ps_global)){
		    int cmd;

		    if((cmd = menu_clear_binding(km, '<')) != MC_UNKNOWN){
			menu_add_binding(km, '<', cmd);
			menu_add_binding(km, KEY_LEFT, cmd);
		    }

		    if((cmd = menu_clear_binding(km, '>')) != MC_UNKNOWN){
			menu_add_binding(km, '>', cmd);
			menu_add_binding(km, KEY_RIGHT, cmd);
		    }

		    if((cmd = menu_clear_binding(km, 'p')) != MC_UNKNOWN){
			menu_add_binding(km, 'p', cmd);
			menu_add_binding(km, KEY_UP, cmd);
		    }

		    if((cmd = menu_clear_binding(km, 'n')) != MC_UNKNOWN){
			menu_add_binding(km, 'n', cmd);
			menu_add_binding(km, KEY_DOWN, cmd);
		    }
		}
	    }

	    if(km_popped){
		FOOTER_ROWS(ps) = 3;
		clearfooter(ps);
	    }

	    draw_keymenu(km, bitmap, ps->ttyo->screen_cols,
			 1-FOOTER_ROWS(ps), 0, what_keymenu);
	    what_keymenu = SameMenu;

	    if(km_popped){
		FOOTER_ROWS(ps) = 1;
		mark_keymenu_dirty();
	    }
	}

	MoveCursor(cursor_pos.row, cursor_pos.col);
#ifdef	MOUSE
	mouse_in_content(KEY_MOUSE, -1, -1, 0, 0);	/* prime the handler */
	register_mfunc(mouse_in_content, HEADER_ROWS(ps_global), 0,
		       ps_global->ttyo->screen_rows -(FOOTER_ROWS(ps_global)+1),
		       ps_global->ttyo->screen_cols);
#endif
#ifdef	_WINDOWS
	mswin_setscrollcallback(config_scroll_callback);
#endif
        /*------ Read the command from the keyboard ----*/
	ch = read_command();
#ifdef	MOUSE
	clear_mfunc(mouse_in_content);
#endif
#ifdef	_WINDOWS
	mswin_setscrollcallback(NULL);
#endif

	cmd = menu_command(ch, km);

	if(km_popped)
	  switch(cmd){
	    case MC_NONE:
	    case MC_OTHER: 
	    case MC_RESIZE: 
	    case MC_REPAINT:
	      km_popped++;
	      break;
	    
	    default:
	      clearfooter(ps);
	      break;
	  }

	switch(cmd){
	  case MC_OTHER :
	    what_keymenu = NextMenu;
	    ps->mangled_footer = 1;
	    break;

	  case MC_HELP:					/* help! */
	    if(FOOTER_ROWS(ps) == 1 && km_popped == 0){
		km_popped = 2;
		ps->mangled_footer = 1;
		break;
	    }

	    if(screen->current->help != NO_HELP){
	        prev_redrawer = ps_global->redrawer;
		helper(screen->current->help,
		       (screen->current->help_title)
		         ? screen->current->help_title
		         : CONFIG_SCREEN_HELP_TITLE,
		       HLPD_SIMPLE);
		ps_global->redrawer = prev_redrawer;
		ps->mangled_screen = 1;
	    }
	    else
	      q_status_message(SM_ORDER,0,3,"No help yet.");

	    break;


	  case MC_NEXTITEM:			/* next list element */
	  case MC_CHARDOWN:
	    stay_in_col = 0;
	    if(screen->current->flags & CF_DOUBLEVAR){
		/* if going from col1 to col2, it's simple */
		if(!(screen->current->flags & CF_VAR2) && cmd == MC_NEXTITEM){
		    screen->current->flags |= CF_VAR2;
		    break;
		}

		/* otherwise we fall through to normal next */
		stay_in_col = (screen->current->flags & CF_VAR2 &&
			       cmd == MC_CHARDOWN);
		screen->current->flags &= ~CF_VAR2;
	    }

	    for(ctmpa = next_confline(screen->current), i = 1;
		ctmpa && (ctmpa->flags & CF_NOSELECT);
		ctmpa = next_confline(ctmpa), i++)
	      ;

	    if(ctmpa){
		screen->current = ctmpa;
		if(screen->current->flags & CF_DOUBLEVAR && stay_in_col)
		  screen->current->flags |= CF_VAR2;

		if(cmd == MC_CHARDOWN){
		    for(ctmpa = screen->top_line,
			j = BODY_LINES(ps) - 1 - HS_MARGIN(ps);
			j > 0 && ctmpa && ctmpa != screen->current;
			ctmpa = next_confline(ctmpa), j--)
		      ;

		    if(!j && ctmpa){
			for(i = 0;
			    ctmpa && ctmpa != screen->current;
			    ctmpa = next_confline(ctmpa), i++)
			  ;

			if(i)
			  config_scroll_up(i);
		    }
		}
	    }
	    else{
		/*
		 * Scroll screen a bit so we show the non-selectable
		 * lines at the bottom.
		 */

		/* set ctmpa to the bottom line on the screen */
		for(ctmpa = screen->top_line, j = BODY_LINES(ps) - 1;
		    j > 0 && ctmpa;
		    ctmpa = next_confline(ctmpa), j--)
		  ;

		i = 0;
		if(ctmpa){
		    for(ctmpa = next_confline(ctmpa);
			ctmpa &&
			(ctmpa->flags & (CF_NOSELECT | CF_B_LINE)) ==
								CF_NOSELECT;
			ctmpa = next_confline(ctmpa), i++)
		      ;
		}

		if(i)
		  config_scroll_up(i);
		else
		  q_status_message(SM_ORDER,0,1, "Already at end of screen");
	    }

	    break;

	  case MC_PREVITEM:			/* prev list element */
	  case MC_CHARUP:
	    stay_in_col = 0;
	    if(screen->current->flags & CF_DOUBLEVAR){
		if(screen->current->flags & CF_VAR2 && cmd == MC_PREVITEM){
		    screen->current->flags &= ~CF_VAR2;
		    break;
		}

		/* otherwise we fall through to normal prev */
		stay_in_col = (!(screen->current->flags & CF_VAR2) &&
			       cmd == MC_CHARUP);
		screen->current->flags &= ~CF_VAR2;
	    }
	    else if(cmd == MC_CHARUP)
	      stay_in_col = 1;

	    ctmpa = screen->current;
	    i = 0;
	    do
	      if(ctmpa == config_top_scroll(ps, screen->top_line))
		i = 1;
	      else if(i)
		i++;
	    while((ctmpa = prev_confline(ctmpa))
		  && (ctmpa->flags&CF_NOSELECT));

	    if(ctmpa){
		screen->current = ctmpa;
		if(screen->current->flags & CF_DOUBLEVAR && !stay_in_col)
		  screen->current->flags |= CF_VAR2;

		if((cmd == MC_CHARUP) && i)
		  config_scroll_down(i);
	    }
	    else
	      q_status_message(SM_ORDER, 0, 1,
			       "Already at start of screen");

	    break;

	  case MC_PAGEDN:			/* page forward */
	    screen->current->flags &= ~CF_VAR2;
	    for(ctmpa = screen->top_line, i = BODY_LINES(ps);
		i > 0 && ctmpa;
		ctmpb = ctmpa, ctmpa = next_confline(ctmpa), i--)
	      ;

	    if(ctmpa){			/* first line off bottom of screen */
		ctmpb = ctmpa;
		ps->mangled_body = 1;
		/* find first selectable line on next page */
		for(screen->top_line = ctmpa;
		    ctmpa && (ctmpa->flags & CF_NOSELECT);
		    ctmpa = next_confline(ctmpa))
		  ;
		
		/*
		 * No selectable lines on next page. Slide up to first
		 * selectable.
		 */
		if(!ctmpa){
		    for(ctmpa = prev_confline(ctmpb);
			ctmpa && (ctmpa->flags & CF_NOSELECT);
			ctmpa = prev_confline(ctmpa))
		      ;
		    
		    if(ctmpa)
		      screen->top_line = ctmpa;
		}
	    }
	    else{  /* on last screen */
		/* just move current down to last entry on screen */
		if(ctmpb){		/* last line of data */
		    for(ctmpa = ctmpb, i = BODY_LINES(ps); 
			i > 0 && ctmpa && (ctmpa->flags & CF_NOSELECT);
			ctmpa = prev_confline(ctmpa), i--)
		      ;

		    if(ctmpa == screen->current){
			q_status_message(SM_ORDER,0,1,
					 "Already at end of screen");
			goto no_down;
		    }

		    ps->mangled_body = 1;
		}
	    }

	    if(ctmpa)
	      screen->current = ctmpa;
no_down:
	    break;

	  case MC_PAGEUP:			/* page backward */
	    ps->mangled_body = 1;
	    screen->current->flags &= ~CF_VAR2;
	    if(!(ctmpa=prev_confline(screen->top_line)))
	      ctmpa = screen->current;

	    for(i = BODY_LINES(ps) - 1;
		i > 0 && prev_confline(ctmpa);
		i--, ctmpa = prev_confline(ctmpa))
	      ;

	    for(screen->top_line = ctmpa;
	        ctmpa && (ctmpa->flags & CF_NOSELECT);
		ctmpa = next_confline(ctmpa))
	      ;

	    if(ctmpa){
		if(ctmpa == screen->current)
		  q_status_message(SM_ORDER, 0, 1,
				 "Already at start of screen");

		screen->current = ctmpa;
	    }

	    break;

#ifdef MOUSE	    
	  case MC_MOUSE:
	    {   
		MOUSEPRESS mp;

		mouse_get_last (NULL, &mp);
		mp.row -= HEADER_ROWS(ps);
		ctmpa = screen->top_line;

		while (mp.row && ctmpa != NULL) {
		    --mp.row;
		    ctmpa = ctmpa->next;
		}

		if (ctmpa != NULL && !(ctmpa->flags & CF_NOSELECT)){
		    if(screen->current->flags & CF_DOUBLEVAR)
		      screen->current->flags &= ~CF_VAR2;

		    screen->current = ctmpa;

		    if(screen->current->flags & CF_DOUBLEVAR &&
		       mp.col >= screen->current->val2offset)
		      screen->current->flags |= CF_VAR2;

		    update_option_screen(ps, screen, &cursor_pos);

		    if(mp.button == M_BUTTON_LEFT && mp.doubleclick){
		       
			if(screen->current->tool){
			    unsigned flags;
			    int default_cmd;

			    flags  = screen->current->flags;
			    flags |= (changes ? CF_CHANGES : 0);

			    default_cmd = menu_command(ctrl('M'), km);
			    switch(i=(*screen->current->tool)(ps, default_cmd,
						     &screen->current, flags)){
			      case -1:
			      case 0:
				break;

			      case 1:
				changes = 1;
				break;

			      case 2:
				retval = changes;
				done++;
				break;

			      case 3:
				retval = 1;
				done++;
				break;

			      default:
				retval = i;
				done++;
				break;
			    }
			}
		    }
#ifdef	_WINDOWS
		    else if(mp.button == M_BUTTON_RIGHT) {
			MPopup other_popup[20];
			int    n = -1, cmd, i;
			struct key_menu *sckm = screen->current->keymenu; /* only for popup */

			if((cmd = menu_command(ctrl('M'), sckm)) != MC_UNKNOWN){
			    i = menu_binding_index(sckm, cmd);
			    other_popup[++n].type	= tQueue;
			    other_popup[n].label.style  = lNormal;
			    other_popup[n].label.string = sckm->keys[i].label;
			    other_popup[n].data.val   = ctrl('M');
			}
			else if((cmd = menu_command('>', sckm)) != MC_UNKNOWN){
			    i = menu_binding_index(sckm, cmd);
			    other_popup[++n].type	= tQueue;
			    other_popup[n].label.style  = lNormal;
			    other_popup[n].label.string = sckm->keys[i].label;
			    other_popup[n].data.val	= '>';
			}

			if(((i = menu_binding_index(sckm, MC_RGB1)) >= 0) ||
			   ((i = menu_binding_index(sckm, MC_RGB2)) >= 0)){
			    other_popup[++n].type	= tQueue;
			    other_popup[n].label.style  = lNormal;
			    other_popup[n].label.string = sckm->keys[i].label;
			    other_popup[n].data.val	=
							sckm->keys[i].bind.ch[0];
			}

			if((cmd = menu_command('<', sckm)) != MC_UNKNOWN){
			    i = menu_binding_index(sckm, cmd);
			    other_popup[++n].type	= tQueue;
			    other_popup[n].label.style  = lNormal;
			    other_popup[n].label.string = sckm->keys[i].label;
			    other_popup[n].data.val	= '<';
			}
			else if((i = menu_binding_index(sckm, MC_EXIT)) >= 0){
			    other_popup[++n].type	= tQueue;
			    other_popup[n].label.style  = lNormal;
			    other_popup[n].label.string = sckm->keys[i].label;
			    other_popup[n].data.val	=
							sckm->keys[i].bind.ch[0];
			}

			if((i = menu_binding_index(sckm, MC_HELP)) >= 0){
			    if(n > 0)
			      other_popup[++n].type = tSeparator;

			    other_popup[++n].type	= tQueue;
			    other_popup[n].label.style  = lNormal;
			    other_popup[n].label.string = sckm->keys[i].label;
			    other_popup[n].data.val = sckm->keys[i].bind.ch[0];
			}

			if(n > 0){
			    other_popup[++n].type = tTail;
			    mswin_popup(other_popup);
			}
		    }
		}
		else if(mp.button == M_BUTTON_RIGHT) {
		    MPopup other_popup[20];
		    int    n = -1, cmd, i;
		    struct key_menu *sckm = screen->current->keymenu; /* only for popup */

		    if((cmd = menu_command('<', sckm)) != MC_UNKNOWN){
			i = menu_binding_index(sckm, cmd);
			other_popup[++n].type	    = tQueue;
			other_popup[n].label.style  = lNormal;
			other_popup[n].label.string = sckm->keys[i].label;
			other_popup[n].data.val	    = '<';
		    }
		    else if((i = menu_binding_index(sckm, MC_EXIT)) >= 0){
			other_popup[++n].type	    = tQueue;
			other_popup[n].label.style  = lNormal;
			other_popup[n].label.string = sckm->keys[i].label;
			other_popup[n].data.val	    = sckm->keys[i].bind.ch[0];
		    }

		    other_popup[++n].type = tTail;

		    if(n > 0)
		      mswin_popup(other_popup);
#endif
		}
	    }
	    break;
#endif

	  case MC_PRINTTXT:			/* print screen */
	    print_option_screen(screen, pdesc ? pdesc : "");
	    break;

	  case MC_WHEREIS:			/* whereis */
	    /*--- get string  ---*/
	    {int   rc, found = 0;
#define FOUND_IT      1
#define FOUND_CURRENT 2
#define FOUND_WRAPPED 4
#define FOUND_ABOVE   8
	     char *result = NULL, buf[64];
	     static char last[64];
	     HelpType help;
	     static ESCKEY_S ekey[] = {
		{0, 0, "", ""},
		{ctrl('Y'), 10, "^Y", "Top"},
		{ctrl('V'), 11, "^V", "Bottom"},
		{-1, 0, NULL, NULL}};

	     ps->mangled_footer = 1;
	     buf[0] = '\0';
	     sprintf(tmp, "Word to find %s%s%s: ",
		     (last[0]) ? "[" : "",
		     (last[0]) ? last : "",
		     (last[0]) ? "]" : "");
	     help = NO_HELP;
	     while(1){
		 int flags = OE_APPEND_CURRENT;

		 rc = optionally_enter(buf,-FOOTER_ROWS(ps),0,63,
					 tmp,ekey,help,&flags);
		 if(rc == 3)
		   help = help == NO_HELP ? h_config_whereis : NO_HELP;
		 else if(rc == 0 || rc == 1 || rc == 10 || rc == 11 || !buf[0]){
		     if(rc == 0 && !buf[0] && last[0])
		       strcpy(buf, last);

		     break;
		 }
	     }

	     screen->current->flags &= ~CF_VAR2;
	     if(rc == 0 && buf[0]){
		 CONF_S *started_here;

		 ch   = KEY_DOWN;
		 ctmpa = screen->current;
		 /*
		  * Skip over the unselectable lines of this "item"
		  * before starting search so that we don't find the
		  * same one again.
		  */
		 while((ctmpb = next_confline(ctmpa)) &&
		       (ctmpb->flags & CF_NOSELECT) &&
		       !(ctmpb->flags & CF_STARTITEM))
		   ctmpa = ctmpb;

		 started_here = next_confline(ctmpa);
		 while(ctmpa = next_confline(ctmpa))
		   if(srchstr(ctmpa->varname, buf)
		      || srchstr(ctmpa->value, buf)){

		       found = FOUND_IT;
		       /*
			* If this line is not selectable, back up to the
			* previous selectable line, but not past the
			* start of this "entry".
			*/
		       while((ctmpa->flags & CF_NOSELECT) &&
			     !(ctmpa->flags & CF_STARTITEM) &&
			     (ctmpb = prev_confline(ctmpa)))
			 ctmpa = ctmpb;
		       
		       /*
			* If that isn't selectable, better search forward
			* for something that is.
			*/
		       while((ctmpa->flags & CF_NOSELECT) &&
			     (ctmpb = next_confline(ctmpa))){
			   ctmpa = ctmpb;
			   found |= FOUND_ABOVE;
		       }

		       break;
		   }

		 if(!found){
		     found = FOUND_WRAPPED;
		     ctmpa = first_confline(screen->current);

		     while(ctmpa != started_here)
		       if(srchstr(ctmpa->varname, buf)
			  || srchstr(ctmpa->value, buf)){

			   found |= FOUND_IT;
			   while((ctmpa->flags & CF_NOSELECT) &&
				 !(ctmpa->flags & CF_STARTITEM) &&
				 (ctmpb = prev_confline(ctmpa)))
			     ctmpa = ctmpb;

			   while((ctmpa->flags & CF_NOSELECT) &&
				 (ctmpb = next_confline(ctmpa))){
			       ctmpa = ctmpb;
			       found |= FOUND_ABOVE;
			   }

			   if(ctmpa == screen->current)
			     found |= FOUND_CURRENT;

			   break;
		       }
		       else
			 ctmpa = next_confline(ctmpa);
		 }
	     }
	     else if(rc == 10){
		 screen->current = first_confline(screen->current);
		 if(screen->current && screen->current->flags & CF_NOSELECT){
		    for(ctmpa = next_confline(screen->current);
			ctmpa && (ctmpa->flags & CF_NOSELECT);
			ctmpa = next_confline(ctmpa))
		      ;
		    
		    if(ctmpa)
		      screen->current = ctmpa;
		 }

		 result = "Searched to top";
	     }
	     else if(rc == 11){
		 screen->current = last_confline(screen->current);
		 if(screen->current && screen->current->flags & CF_NOSELECT){
		    for(ctmpa = prev_confline(screen->current);
			ctmpa && (ctmpa->flags & CF_NOSELECT);
			ctmpa = prev_confline(ctmpa))
		      ;
		    
		    if(ctmpa)
		      screen->current = ctmpa;
		 }
		 
		 result = "Searched to bottom";
	     }
	     else
	       result = "WhereIs cancelled";

	     if((found & FOUND_IT) && ctmpa){
		 strcpy(last, buf);
		 result  = (found & FOUND_CURRENT && found & FOUND_WRAPPED &&
			    found & FOUND_ABOVE)
			    ? "Current item contains the only match"
			    : (found & FOUND_CURRENT && found & FOUND_WRAPPED)
			      ? "Current line contains the only match"
			      : (found & FOUND_ABOVE && found & FOUND_WRAPPED)
			        ? "Search wrapped: word found in text above current line"
			        : (found & FOUND_WRAPPED)
			          ? "Search wrapped to beginning: word found"
			          : (found & FOUND_ABOVE)
			            ? "Word found in text above current line"
			            : "Word found";
		 screen->current = ctmpa;
	     }

	     q_status_message(SM_ORDER,0,3,result ? result : "Word not found");
	    }

	    break;

	  case MC_REPAINT:			/* redraw the display */
	  case MC_RESIZE:
	    ClearScreen();
	    ps->mangled_screen = 1;
	    break;

	  default:
	    if(edit_config
	       && (ps_global->restricted || ps_global->readonly_pinerc)){
		q_status_message1(SM_ORDER, 0, 3,
		     "%s can't change options or settings",
		     ps_global->restricted ? "Pine demo"
					   : "Config file not changeable,");
		if(cmd == MC_EXIT){
		    retval = 0;
		    done++;
		}
	    }
	    else if(screen->current->tool){
		unsigned flags;

		flags  = screen->current->flags;
		flags |= (changes ? CF_CHANGES : 0);

		switch(i=(*screen->current->tool)(ps, cmd,
		    &screen->current, flags)){
		  case -1:
		    q_status_message2(SM_ORDER, 0, 2,
		      "Command \"%s\" not defined here.%s",
		      pretty_command(ch),
		      F_ON(F_BLANK_KEYMENU,ps) ? "" : "  See key menu below.");
		    break;

		  case 0:
		    break;

		  case 1:
		    changes = 1;
		    break;

		  case 2:
		    retval = changes;
		    done++;
		    break;

		  case 3:
		    retval = 1;
		    done++;
		    break;

		  default:
		    retval = i;
		    done++;
		    break;
		}
	    }

	    break;

	  case MC_NONE:				/* simple timeout */
	    break;
	}
    }

    screen->current = first_confline(screen->current);
    free_conflines(&screen->current);
    return(retval);
}


/*
 *
 */
void
config_scroll_up(n)
    long n;
{
    CONF_S *ctmp = opt_screen->top_line;
    int     cur_found = 0, rv = 1;

    if(n < 0)
      config_scroll_down(-n);
    else if(n){
      for(; n>0 && ctmp->next; n--){
	ctmp = next_confline(ctmp);
	if(prev_confline(ctmp) == opt_screen->current)
	  cur_found++;
      }

      opt_screen->top_line = ctmp;
      rv = ps_global->mangled_body = 1;
      if(cur_found){
	for(ctmp = opt_screen->top_line;
	    ctmp && (ctmp->flags & CF_NOSELECT);
	    ctmp = next_confline(ctmp))
	  ;

	if(ctmp)
	  opt_screen->current = opt_screen->prev = ctmp;
	else {
	  while(opt_screen->top_line->flags & CF_NOSELECT)
	    opt_screen->top_line = prev_confline(opt_screen->top_line);
	  opt_screen->current = opt_screen->prev = opt_screen->top_line;
	}
      }
    }
}


/*
 * config_scroll_down -
 */
void
config_scroll_down(n)
    long n;
{
    CONF_S *ctmp = opt_screen->top_line, *last_sel = NULL;
    int     i, rv = 1;

    if(n < 0)
      config_scroll_up(-n);
    else if(n){
	for(; n>0 && ctmp->prev; n--)
	  ctmp = prev_confline(ctmp);

	opt_screen->top_line = ctmp;
	rv = ps_global->mangled_body = 1;
	for(ctmp = opt_screen->top_line, i = BODY_LINES(ps_global);
	    i > 0 && ctmp && ctmp != opt_screen->current;
	    ctmp = next_confline(ctmp), i--)
	  if(!(ctmp->flags & CF_NOSELECT))
	    last_sel = ctmp;

	if(!i && last_sel)
	  opt_screen->current = opt_screen->prev = last_sel;
    }
}


/*
 * config_scroll_to_pos -
 */
void
config_scroll_to_pos(n)
    long n;
{
    CONF_S *ctmp;

    for(ctmp = first_confline(opt_screen->current);
	n && ctmp && ctmp != opt_screen->top_line;
	ctmp = next_confline(ctmp), n--)
      ;

    if(n == 0)
      while(ctmp && ctmp != opt_screen->top_line)
	if(ctmp = next_confline(ctmp))
	  n--;

    config_scroll_up(n);
}


/*
 * config_top_scroll - return pointer to the 
 */
CONF_S *
config_top_scroll(ps, topline)
    struct pine *ps;
    CONF_S *topline;
{
    int     i;
    CONF_S *ctmp;

    for(ctmp = topline, i = HS_MARGIN(ps);
	ctmp && i;
	ctmp = next_confline(ctmp), i--)
      ;

    return(ctmp ? ctmp : topline);
}


/*
 *
 */
HelpType
config_help(var, feature)
    int var, feature;
{
    switch(var){
      case V_FEATURE_LIST :
	return(feature_list_help(feature));
	break;

      case V_PERSONAL_NAME :
	return(h_config_pers_name);
      case V_USER_ID :
	return(h_config_user_id);
      case V_USER_DOMAIN :
	return(h_config_user_dom);
      case V_SMTP_SERVER :
	return(h_config_smtp_server);
      case V_NNTP_SERVER :
	return(h_config_nntp_server);
      case V_INBOX_PATH :
	return(h_config_inbox_path);
      case V_PRUNED_FOLDERS :
	return(h_config_pruned_folders);
      case V_DEFAULT_FCC :
	return(h_config_default_fcc);
      case V_DEFAULT_SAVE_FOLDER :
	return(h_config_def_save_folder);
      case V_POSTPONED_FOLDER :
	return(h_config_postponed_folder);
      case V_READ_MESSAGE_FOLDER :
	return(h_config_read_message_folder);
      case V_FORM_FOLDER :
	return(h_config_form_folder);
      case V_ARCHIVED_FOLDERS :
	return(h_config_archived_folders);
      case V_SIGNATURE_FILE :
	return(h_config_signature_file);
      case V_INIT_CMD_LIST :
	return(h_config_init_cmd_list);
      case V_COMP_HDRS :
	return(h_config_comp_hdrs);
      case V_CUSTOM_HDRS :
	return(h_config_custom_hdrs);
      case V_VIEW_HEADERS :
	return(h_config_viewer_headers);
      case V_SAVED_MSG_NAME_RULE :
	return(h_config_saved_msg_name_rule);
      case V_FCC_RULE :
	return(h_config_fcc_rule);
      case V_SORT_KEY :
	return(h_config_sort_key);
      case V_AB_SORT_RULE :
	return(h_config_ab_sort_rule);
      case V_FLD_SORT_RULE :
	return(h_config_fld_sort_rule);
      case V_CHAR_SET :
	return(h_config_char_set);
      case V_EDITOR :
	return(h_config_editor);
      case V_SPELLER :
	return(h_config_speller);
      case V_DISPLAY_FILTERS :
	return(h_config_display_filters);
      case V_SEND_FILTER :
	return(h_config_sending_filter);
      case V_ALT_ADDRS :
	return(h_config_alt_addresses);
      case V_ABOOK_FORMATS :
	return(h_config_abook_formats);
      case V_INDEX_FORMAT :
	return(h_config_index_format);
      case V_OVERLAP :
	return(h_config_viewer_overlap);
      case V_MARGIN :
	return(h_config_scroll_margin);
      case V_FILLCOL :
	return(h_config_composer_wrap_column);
      case V_REPLY_STRING :
	return(h_config_reply_indent_string);
      case V_REPLY_INTRO :
	return(h_config_reply_intro);
      case V_EMPTY_HDR_MSG :
	return(h_config_empty_hdr_msg);
      case V_STATUS_MSG_DELAY :
	return(h_config_status_msg_delay);
      case V_MAILCHECK :
	return(h_config_mailcheck);
      case V_NEWS_ACTIVE_PATH :
	return(h_config_news_active);
      case V_NEWS_SPOOL_DIR :
	return(h_config_news_spool);
      case V_IMAGE_VIEWER :
	return(h_config_image_viewer);
      case V_USE_ONLY_DOMAIN_NAME :
	return(h_config_domain_name);
      case V_LAST_TIME_PRUNE_QUESTION :
	return(h_config_prune_date);
      case V_UPLOAD_CMD:
	return(h_config_upload_cmd);
      case V_UPLOAD_CMD_PREFIX:
	return(h_config_upload_prefix);
      case V_DOWNLOAD_CMD:
	return(h_config_download_cmd);
      case V_DOWNLOAD_CMD_PREFIX:
	return(h_config_download_prefix);
      case V_GOTO_DEFAULT_RULE:
	return(h_config_goto_default);
      case V_INCOMING_STARTUP:
	return(h_config_inc_startup);
      case V_MAILCAP_PATH :
	return(h_config_mailcap_path);
      case V_MIMETYPE_PATH :
	return(h_config_mimetype_path);
      case V_NEWSRC_PATH :
	return(h_config_newsrc_path);
      case V_BROWSER :
	return(h_config_browser);
#if defined(DOS) || defined(OS2)
      case V_FILE_DIR :
	return(h_config_file_dir);
#endif
#ifndef	_WINDOWS
      case V_COLOR_STYLE :
	return(h_config_color_style);
#endif
      case V_NORM_FORE_COLOR :
	return(h_config_normal_color);
      case V_REV_FORE_COLOR :
	return(h_config_reverse_color);
      case V_TITLE_FORE_COLOR :
	return(h_config_title_color);
      case V_STATUS_FORE_COLOR :
	return(h_config_status_color);
      case V_SLCTBL_FORE_COLOR :
	return(h_config_slctbl_color);
      case V_QUOTE1_FORE_COLOR :
      case V_QUOTE2_FORE_COLOR :
      case V_QUOTE3_FORE_COLOR :
	return(h_config_quote_color);
      case V_PROMPT_FORE_COLOR :
	return(h_config_prompt_color);
      case V_IND_PLUS_FORE_COLOR :
      case V_IND_IMP_FORE_COLOR :
      case V_IND_DEL_FORE_COLOR :
      case V_IND_ANS_FORE_COLOR :
      case V_IND_NEW_FORE_COLOR :
      case V_IND_UNS_FORE_COLOR :
      case V_IND_REC_FORE_COLOR :
	return(h_config_index_color);
      case V_KEYLABEL_FORE_COLOR :
	return(h_config_keylabel_color);
      case V_KEYNAME_FORE_COLOR :
	return(h_config_keyname_color);
      case V_VIEW_HDR_COLORS :
	return(h_config_customhdr_color);
      default :
	return(NO_HELP);
    }
}


/*
 * simple text variable handler
 *
 * note, things get a little involved due to the
 *	 screen struct <--> variable mapping. (but, once its
 *       running it shouldn't need changing ;).
 * 
 * returns:  -1 on unrecognized cmd, 0 if no change, 1 if change
 *           returns what conf_exit_cmd returns for exit command.
 */
int
text_tool(ps, cmd, cl, flags)
    struct pine  *ps;
    int		  cmd;
    CONF_S      **cl;
    unsigned      flags;
{
    char	     prompt[81], *sval, *tmp, **newval = NULL;
    int		     rv = 0, skip_to_next = 0, after = 0, i = 4, j, k;
    int		     lowrange, hirange, incr, oeflags, oebufsize;
    int		     numval, repeat_key = 0;
    HelpType         help;
    ESCKEY_S         ekey[6];

    oebufsize = MAXPATH;
    sval = (char *)fs_get(oebufsize*sizeof(char));
    sval[0] = '\0';

    if(flags&CF_NUMBER){ /* only happens if !is_list */
	incr = 1;
	if((*cl)->var == &ps->vars[V_FILLCOL]){
	    lowrange = 1;
	    hirange  = MAX_FILLCOL;
	}
	else if((*cl)->var == &ps->vars[V_OVERLAP]
		|| (*cl)->var == &ps->vars[V_MARGIN]){
	    lowrange = 0;
	    hirange  = 20;
	}
	else if((*cl)->var == &ps->vars[V_STATUS_MSG_DELAY]){
	    lowrange = 0;
	    hirange  = 30;
	}
	else if((*cl)->var == &ps->vars[V_MAILCHECK]){
	    lowrange = 0;
	    hirange  = 25000;
	    incr     = 15;
	}
	else if((*cl)->var == score_act_global_ptr){
	    lowrange = -100;
	    hirange  = 100;
	    incr     = 1;
	}
	else{
	    lowrange = 0;
	    hirange  = 25000;
	}

	ekey[0].ch    = -2;
	ekey[0].rval  = 'x';
	ekey[0].name  = "";
	ekey[0].label = "";
	ekey[1].ch    = ctrl('P');
	ekey[1].rval  = ctrl('P');
	ekey[1].name  = "^P";
	ekey[1].label = "Decrease";
	ekey[2].ch    = ctrl('N');
	ekey[2].rval  = ctrl('N');
	ekey[2].name  = "^N";
	ekey[2].label = "Increase";
	ekey[3].ch    = KEY_DOWN;
	ekey[3].rval  = ctrl('P');
	ekey[3].name  = "";
	ekey[3].label = "";
	ekey[4].ch    = KEY_UP;
	ekey[4].rval  = ctrl('N');
	ekey[4].name  = "";
	ekey[4].label = "";
	ekey[5].ch    = -1;
    }

    switch(cmd){
      case MC_ADD:				/* add to list */
	if(fixed_var((*cl)->var, "add to", NULL)){
	    break;
	}
	else if(!(*cl)->var->is_list && (*cl)->var->user_val.p){
	    q_status_message(SM_ORDER, 3, 3,
			    "Only single value allowed.  Use \"Change\".");
	}
	else{
	    int maxwidth =min(80,ps->ttyo->screen_cols) - 15;
	    char *p;

	    if((*cl)->var->is_list
	       && (*cl)->var->user_val.l
	       && (*cl)->var->user_val.l[0]
	       && (*cl)->var->user_val.l[0][0]
	       && (*cl)->value){
		char tmpval[101];
		/* regular add to an existing list */

		strncpy(tmpval, (*cl)->value, 100);
		removing_trailing_white_space(tmpval);
		/* 33 is the number of chars other than the value */
		k = min(18, max(maxwidth-33,0));
		if(strlen(tmpval) > k && k >= 3){
		    tmpval[k-1] = tmpval[k-2] = tmpval[k-3] = '.';
		    tmpval[k] = '\0';
		}

		sprintf(prompt,
		    "Enter text to insert before \"%.*s\": ",k,tmpval);
	    }
	    else if((*cl)->var->is_list
		    && !(*cl)->var->user_val.l
		    && (*cl)->var->current_val.l){
		/* Add to list which doesn't exist, but default does exist */
		ekey[0].ch    = 'r';
		ekey[0].rval  = 'r';
		ekey[0].name  = "R";
		ekey[0].label = "Replace";
		ekey[1].ch    = 'a';
		ekey[1].rval  = 'a';
		ekey[1].name  = "A";
		ekey[1].label = "Add To";
		ekey[2].ch    = -1;
		strcpy(prompt, "Replace or Add To default value ? ");
		switch(radio_buttons(prompt, -FOOTER_ROWS(ps), ekey, 'a', 'x',
				     h_config_replace_add, RB_NORM)){
		  case 'a':
		    p = sval;
		    for(j = 0; (*cl)->var->current_val.l[j]; j++){
			strcpy(p, (*cl)->var->current_val.l[j]);
			p += strlen(p);
			*p++ = ',';
			*p++ = ' ';
			*p = '\0';
		    }

add_text:
		    sprintf(prompt, "Enter the %stext to be added : ",
			flags&CF_NUMBER ? "numeric " : "");
		    break;
		    
		  case 'r':
replace_text:
		    sprintf(prompt, "Enter the %sreplacement text : ",
			flags&CF_NUMBER ? "numeric " : "");
		    break;
		    
		  case 'x':
		    i = 1;
		    cmd_cancelled("Add");
		    break;
		}
	    }
	    else
	      sprintf(prompt, "Enter the %stext to be added : ",
		    flags&CF_NUMBER ? "numeric " : "");

	    ps->mangled_footer = 1;

	    if(i == 1)
	      break;

	    help = NO_HELP;
	    while(1){
		if((*cl)->var->is_list
		    && (*cl)->var->user_val.l
		    && (*cl)->var->user_val.l[0]
		    && (*cl)->var->user_val.l[0][0]
		    && (*cl)->value){
		    ekey[0].ch    = ctrl('W');
		    ekey[0].rval  = 5;
		    ekey[0].name  = "^W";
		    ekey[0].label = after ? "InsertBefore" : "InsertAfter";
		    ekey[1].ch    = -1;
		}
		else if(!(flags&CF_NUMBER))
		  ekey[0].ch    = -1;

		oeflags = OE_APPEND_CURRENT;
		i = optionally_enter(sval, -FOOTER_ROWS(ps), 0, oebufsize,
				     prompt,
				     (ekey[0].ch != -1) ? ekey : NULL,
				     help, &oeflags);
		if(i == 0){
		    rv = ps->mangled_body = 1;
		    removing_leading_and_trailing_white_space(sval);
		    /*
		     * Coerce "" and <Empty Value> to empty string input.
		     * Catch <No Value Set> as a substitute for deleting.
		     */
		    if((*sval == '\"' && *(sval+1) == '\"' && *(sval+2) == '\0')
		        || !struncmp(sval, empty_val, EMPTY_VAL_LEN) 
			|| (*sval == '<'
			    && !struncmp(sval+1, empty_val, EMPTY_VAL_LEN)))
		      *sval = '\0';
		    else if(!struncmp(sval, no_val, NO_VAL_LEN)
		        || (*sval == '<'
			    && !struncmp(sval+1, no_val, NO_VAL_LEN)))
		      goto delete;

		    if((*cl)->var->is_list){
			if(*sval || !(*cl)->var->user_val.l){
			    char **ltmp;
			    int    i;

			    i = 0;
			    for(tmp = sval; *tmp; tmp++)
			      if(*tmp == ',')
				i++;	/* conservative count of ,'s */

			    if(!i){
				ltmp    = (char **)fs_get(2 * sizeof(char *));
				ltmp[0] = cpystr(sval);
				ltmp[1] = NULL;
			    }
			    else
			      ltmp = parse_list(sval, i + 1, NULL);

			    if(ltmp[0]){
				config_add_list(ps, cl, ltmp, &newval, after);
				if(after)
				  skip_to_next = 1;
			    }
			    else{
				q_status_message1(SM_ORDER, 0, 3,
					 "Can't add %s to list", empty_val);
				rv = ps->mangled_body = 0;
			    }

			    fs_give((void **)&ltmp);
			}
			else{
			    q_status_message1(SM_ORDER, 0, 3,
					 "Can't add %s to list", empty_val);
			}
		    }
		    else{
			if(flags&CF_NUMBER && sval[0]
			  && !(isdigit((unsigned char)sval[0])
			       || sval[0] == '-' || sval[0] == '+')){
			    q_status_message(SM_ORDER,3,3,
				  "Entry must be numeric");
			    i = 3; /* to keep loop going */
			    continue;
			}

			if((*cl)->var->user_val.p)
			  fs_give((void **)&(*cl)->var->user_val.p);

			(*cl)->var->user_val.p = cpystr(sval);
			newval = &(*cl)->value;
		    }
		}
		else if(i == 1){
		    cmd_cancelled("Add");
		}
		else if(i == 3){
		    help = help == NO_HELP ? h_config_add : NO_HELP;
		    continue;
		}
		else if(i == 4){		/* no redraw, yet */
		    continue;
		}
		else if(i == 5){ /* change from/to prepend to/from append */
		    char tmpval[101];

		    after = after ? 0 : 1;
		    strncpy(tmpval, (*cl)->value, 100);
		    removing_trailing_white_space(tmpval);
		    /* 33 is the number of chars other than the value */
		    k = min(18, max(maxwidth-33,0));
		    if(strlen(tmpval) > k && k >= 3){
			tmpval[k-1] = tmpval[k-2] = tmpval[k-3] = '.';
			tmpval[k] = '\0';
		    }

		    sprintf(prompt,
			"Enter text to insert %s \"%.*s\": ",
			after ? "after" : "before", k, tmpval);
		    continue;
		}
		else if(i == ctrl('P')){
		    if(sval[0])
		      numval = atoi(sval);
		    else{
		      if((*cl)->var->current_val.p)
			numval = atoi((*cl)->var->current_val.p);
		      else
			numval = lowrange + 1;
		    }

		    if(numval == lowrange){
			/*
			 * Protect user from repeating arrow key that
			 * causes message to appear over and over.
			 */
			if(++repeat_key > 0){
			    q_status_message1(SM_ORDER,3,3,
				"Minimum value is %s", comatose(lowrange));
			    repeat_key = -5;
			}
		    }
		    else
		      repeat_key = 0;

		    numval = max(numval - incr, lowrange);
		    sprintf(sval, "%d", numval);
		    continue;
		}
		else if(i == ctrl('N')){
		    if(sval[0])
		      numval = atoi(sval);
		    else{
		      if((*cl)->var->current_val.p)
			numval = atoi((*cl)->var->current_val.p);
		      else
			numval = lowrange + 1;
		    }

		    if(numval == hirange){
			if(++repeat_key > 0){
			    q_status_message1(SM_ORDER,3,3,
				"Maximum value is %s", comatose(hirange));
			    repeat_key = -5;
			}
		    }
		    else
		      repeat_key = 0;

		    numval = min(numval + incr, hirange);
		    sprintf(sval, "%d", numval);
		    continue;
		}

		break;
	    }
	}

	break;

      case MC_DELETE:				/* delete */
delete:
	if(!(*cl)->var->is_list
	    && !(*cl)->var->user_val.p
	    && (*cl)->var->current_val.p){
	    char pmt[40];

	    sprintf(pmt, "Override default with %s", empty_val2);
	    if(want_to(pmt, 'n', 'n', NO_HELP, WT_FLUSH_IN) == 'y'){
		sval[0] = '\0';
		(*cl)->var->user_val.p = cpystr(sval);
		newval = &(*cl)->value;
		rv = ps->mangled_body = 1;
	    }
	}
	else if((*cl)->var->is_list
		&& !(*cl)->var->user_val.l
		&& (*cl)->var->current_val.l){
	    char pmt[40];

	    sprintf(pmt, "Override default with %s", empty_val2);
	    if(want_to(pmt, 'n', 'n', NO_HELP, WT_FLUSH_IN) == 'y'){
		char **ltmp;

		sval[0] = '\0';
		ltmp    = (char **)fs_get(2 * sizeof(char *));
		ltmp[0] = cpystr(sval);
		ltmp[1] = NULL;
		config_add_list(ps, cl, ltmp, &newval, 0);
		fs_give((void **)&ltmp);
		rv = ps->mangled_body = 1;
	    }
	}
	else if(((*cl)->var->is_list && !(*cl)->var->user_val.l)
		|| (!(*cl)->var->is_list && !(*cl)->var->user_val.p)){
	    q_status_message(SM_ORDER, 0, 3, "No set value to delete");
	}
	else{
	    if((*cl)->var->is_fixed)
	        sprintf(prompt, "Delete (unused) %.30s from %.20s ",
		    (*cl)->var->is_list
		      ? (!*(*cl)->var->user_val.l[(*cl)->varmem])
			  ? empty_val2
			  : (*cl)->var->user_val.l[(*cl)->varmem]
		      : ((*cl)->var->user_val.p)
			  ? (!*(*cl)->var->user_val.p)
			      ? empty_val2
			      : (*cl)->var->user_val.p
		 	  : "<NULL VALUE>",
		    (*cl)->var->name);
	    else
	        sprintf(prompt, "Really delete %s%.20s from %.30s ",
		    (*cl)->var->is_list ? "item " : "", 
		    (*cl)->var->is_list
		      ? int2string((*cl)->varmem + 1)
		      : ((*cl)->var->user_val.p)
			  ? (!*(*cl)->var->user_val.p)
			      ? empty_val2
			      : (*cl)->var->user_val.p
		 	  : "<NULL VALUE>",
		    (*cl)->var->name);

	    ps->mangled_footer = 1;
	    if(want_to(prompt, 'n', 'n', NO_HELP, WT_FLUSH_IN) == 'y'){
		rv = ps->mangled_body = 1;
		if((*cl)->var->is_list){
		    fs_give((void **)&(*cl)->var->user_val.l[(*cl)->varmem]);
		    config_del_list_item(cl, &newval);
		}
		else{
		    fs_give((void **)&(*cl)->var->user_val.p);
		    newval = &(*cl)->value;
		}
	    }
	    else
	      q_status_message(SM_ORDER, 0, 3, "Value not deleted");
	}

	break;

      case MC_EDIT:				/* edit/change list option */
	if(fixed_var((*cl)->var, NULL, NULL)){
	    break;
	}
	else if(((*cl)->var->is_list
		    && !(*cl)->var->user_val.l
		    && (*cl)->var->current_val.l)
		||
		(!(*cl)->var->is_list
		    && !(*cl)->var->user_val.p
		    && (*cl)->var->current_val.p)){
	    goto replace_text;
	}
	else if(((*cl)->var->is_list
		    && !(*cl)->var->user_val.l
		    && !(*cl)->var->current_val.l)
		||
		(!(*cl)->var->is_list
		    && !(*cl)->var->user_val.p
		    && !(*cl)->var->current_val.p)){
	    goto add_text;
	}
	else{
	    HelpType help;
	    char *clptr;

	    if(sval)
	      fs_give((void **)&sval);
	    if((*cl)->var->is_list){
		sprintf(prompt, "Change field %.30s list entry : ",
			(*cl)->var->name);
 		clptr = (*cl)->var->user_val.l[(*cl)->varmem]
		      ? (*cl)->var->user_val.l[(*cl)->varmem] : NULL;
	    }
	    else{
		sprintf(prompt, "Change %sfield %.35s value : ",
			flags&CF_NUMBER ? "numeric " : "",
			(*cl)->var->name);
 		clptr = (*cl)->var->user_val.p ? (*cl)->var->user_val.p : NULL;
	    }

 	    oebufsize = clptr ? (int)max(MAXPATH, 50+strlen(clptr)) : MAXPATH;
 	    sval = (char *)fs_get(oebufsize * sizeof(char));
 	    sprintf(sval, "%s", clptr ? clptr : "");

	    ps->mangled_footer = 1;
	    help = NO_HELP;
	    while(1){
		if(!(flags&CF_NUMBER))
		  ekey[0].ch = -1;

		oeflags = OE_APPEND_CURRENT;
		i = optionally_enter(sval, -FOOTER_ROWS(ps), 0, oebufsize,
				     prompt,
				     (ekey[0].ch != -1) ? ekey : NULL,
				     help, &oeflags);
		if(i == 0){
		    removing_leading_and_trailing_white_space(sval);
		    /*
		     * Coerce "" and <Empty Value> to empty string input.
		     * Catch <No Value Set> as a substitute for deleting.
		     */
		    if((*sval == '\"' && *(sval+1) == '\"' && *(sval+2) == '\0')
		        || !struncmp(sval, empty_val, EMPTY_VAL_LEN) 
			|| (*sval == '<'
			    && !struncmp(sval+1, empty_val, EMPTY_VAL_LEN)))
		      *sval = '\0';
		    else if(!struncmp(sval, no_val, NO_VAL_LEN)
			|| (*sval == '<'
			    && !struncmp(sval+1, no_val, NO_VAL_LEN)))
		      goto delete;

		    rv = ps->mangled_body = 1;
		    if((*cl)->var->is_list){
			char **ltmp = NULL;
			int    i;

			if((*cl)->var->user_val.l[(*cl)->varmem])
			  fs_give((void **)&(*cl)->var->user_val.l[
							       (*cl)->varmem]);

			i = 0;
			for(tmp = sval; *tmp; tmp++)
			  if(*tmp == ',')
			    i++;	/* conservative count of ,'s */

			if(i)
			  ltmp = parse_list(sval, i + 1, NULL);

			if(ltmp && !ltmp[0])		/* only commas */
			  goto delete;
			else if(!i || (ltmp && !ltmp[1])){  /* only one item */
			    (*cl)->var->user_val.l[(*cl)->varmem] =
								  cpystr(sval);
			    newval = &(*cl)->value;

			    if(ltmp && ltmp[0])
			      fs_give((void **)&ltmp[0]);
			}
			else if(ltmp){
			    /*
			     * Looks like the value was changed to a 
			     * list, so delete old value, and insert
			     * new list...
			     *
			     * If more than one item in existing list and
			     * current is end of existing list, then we
			     * have to delete and append instead of
			     * deleting and prepending.
			     */
			    if(((*cl)->varmem > 0 || (*cl)->var->user_val.l[1])
			       && !((*cl)->var->user_val.l[(*cl)->varmem+1])){
				after = 1;
				skip_to_next = 1;
			    }

			    config_del_list_item(cl, &newval);
			    config_add_list(ps, cl, ltmp, &newval, after);
			}

			if(ltmp)
			  fs_give((void **)&ltmp);
		    }
		    else{
			if(flags&CF_NUMBER && sval[0]
			  && !(isdigit((unsigned char)sval[0])
			       || sval[0] == '-' || sval[0] == '+')){
			    q_status_message(SM_ORDER,3,3,
				  "Entry must be numeric");
			    continue;
			}

			if((*cl)->var->user_val.p)
			  fs_give((void **)&(*cl)->var->user_val.p);

			if(sval[0])
			  (*cl)->var->user_val.p = cpystr(sval);

			newval = &(*cl)->value;
		    }
		}
		else if(i == 1){
		    cmd_cancelled("Change");
		}
		else if(i == 3){
		    help = help == NO_HELP ? h_config_change : NO_HELP;
		    continue;
		}
		else if(i == 4){		/* no redraw, yet */
		    continue;
		}
		else if(i == ctrl('P')){
		    numval = atoi(sval);
		    if(numval == lowrange){
			/*
			 * Protect user from repeating arrow key that
			 * causes message to appear over and over.
			 */
			if(++repeat_key > 0){
			    q_status_message1(SM_ORDER,3,3,
				"Minimum value is %s", comatose(lowrange));
			    repeat_key = -5;
			}
		    }
		    else
		      repeat_key = 0;

		    numval = max(numval - incr, lowrange);
		    sprintf(sval, "%d", numval);
		    continue;
		}
		else if(i == ctrl('N')){
		    numval = atoi(sval);
		    if(numval == hirange){
			if(++repeat_key > 0){
			    q_status_message1(SM_ORDER,3,3,
				"Maximum value is %s", comatose(hirange));
			    repeat_key = -5;
			}
		    }
		    else
		      repeat_key = 0;

		    numval = min(numval + incr, hirange);
		    sprintf(sval, "%d", numval);
		    continue;
		}

		break;
	    }
	}

	break;

      case MC_EXIT:				/* exit */
	rv = config_exit_cmd(flags);
	break;

      default:
	rv = -1;
	break;
    }

    if(skip_to_next)
      *cl = next_confline(*cl);

    /*
     * At this point, if changes occurred, var->user_val.X is set.
     * So, fix the current_val, and handle special cases...
     *
     * NOTE: we don't worry about the "fixed variable" case here, because
     *       editing such vars should have been prevented above...
     */
    if(rv == 1){
	/*
	 * Now go and set the current_val based on user_val changes
	 * above.  Turn off command line settings...
	 */
	set_current_val((*cl)->var, TRUE, FALSE);
	fix_side_effects(ps, (*cl)->var, 0);

	/*
	 * Delay setting the displayed value until "var.current_val" is set
	 * in case current val get's changed due to a special case above.
	 */
	if(newval){
	    if(*newval)
	      fs_give((void **)newval);

	    *newval = pretty_value(ps, *cl);
	}
    }

    if(sval)
      fs_give((void **)&sval);
 
    return(rv);
}


int
config_exit_cmd(flags)
    unsigned flags;
{
    return(screen_exit_cmd(flags, "Configuration"));
}


simple_exit_cmd(flags)
    unsigned flags;
{
    return(2);
}


/*
 * screen_exit_cmd - basic config/flag screen exit logic
 */
int
screen_exit_cmd(flags, cmd)
    unsigned  flags;
    char     *cmd;
{
    if(flags & CF_CHANGES){
      switch(want_to(EXIT_PMT, 'y', 'x', h_config_undo, WT_FLUSH_IN)){
	case 'y':
	  q_status_message1(SM_ORDER,0,3,"%s changes saved", cmd);
	  return(2);

	case 'n':
	  q_status_message1(SM_ORDER,3,5,"No %s changes saved", cmd);
	  return(10);

	case 'x':  /* ^C */
	default :
	  q_status_message(SM_ORDER,3,5,"Changes not yet saved");
	  return(0);
      }
    }
    else
      return(2);
}


/*
 *
 */
void
config_add_list(ps, cl, ltmp, newval, after)
    struct pine *ps;
    CONF_S     **cl;
    char       **ltmp, ***newval;
    int		 after;
{
    int	    items, i;
    char   *tmp;
    CONF_S *ctmp;

    for(items = 0, i = 0; ltmp[i]; i++)		/* count list items */
      items++;

    if((*cl)->var->user_val.l){
	if((*cl)->var->user_val.l[0]
	   && (*cl)->var->user_val.l[0][0]){
	    /*
	     * Since we were already a list, make room
	     * for the new member[s] and fall thru to
	     * actually fill them in below...
	     */
	    for(i = 0; (*cl)->var->user_val.l[i]; i++)
	      ;

	    fs_resize((void **)&(*cl)->var->user_val.l,
		      (i + items + 1) * sizeof(char *));
	    /*
	     * move the ones that will be bumped down to the bottom of the list
	     */
	    for(; i >= (*cl)->varmem + (after?1:0); i--)
	      (*cl)->var->user_val.l[i+items] =
		(*cl)->var->user_val.l[i];

	    i = 0;
	}
	else{
	    (*cl)->varmem = 0;
	    free_list_array(&((*cl)->var->user_val.l));
	    (*cl)->var->user_val.l = (char **)fs_get((items+1)*sizeof(char *));
	    memset((void *)(*cl)->var->user_val.l, 0, (items+1)*sizeof(char *));
	    (*cl)->var->user_val.l[0] = ltmp[0];
	    *newval = &(*cl)->value;
	    if((*cl)->value)
	      fs_give((void **)&(*cl)->value);

	    i = 1;
	}
    }
    else{
	/*
	 * since we were previously empty, we want
	 * to replace the first CONF_S's value with
	 * the first new value, and fill the other
	 * in below if there's a list...
	 *
	 * first, make sure we're at the beginning of this config
	 * section and dump the config lines for the default list,
	 * except for the first one, which we will over-write.
	 */
	*cl = (*cl)->varnamep; 
	while((*cl)->next && (*cl)->next->varnamep == (*cl)->varnamep)
	  snip_confline(&(*cl)->next);

	/*
	 * now allocate the new user_val array and fill in the first entry.
	 */
	(*cl)->var->user_val.l = (char **)fs_get((items+1)*sizeof(char *));
	memset((void *)(*cl)->var->user_val.l, 0, (items+1) * sizeof(char *));
	(*cl)->var->user_val.l[(*cl)->varmem=0] = ltmp[0];
	*newval = &(*cl)->value;
	if((*cl)->value)
	  fs_give((void **)&(*cl)->value);

	i = 1;
    }

    /*
     * Make new cl's to fit in the new space.  Move the value from the current
     * line if inserting before it, else leave it where it is.
     */
    for(; i < items ; i++){
	(*cl)->var->user_val.l[i+(*cl)->varmem + (after?1:0)] = ltmp[i];
	tmp = (*cl)->value;
	new_confline(cl);
	if(after)
	  (*cl)->value   = NULL;
	else
	  (*cl)->value   = tmp;

	(*cl)->var       = (*cl)->prev->var;
	(*cl)->valoffset = (*cl)->prev->valoffset;
	(*cl)->varoffset = (*cl)->prev->varoffset;
	(*cl)->headingp  = (*cl)->prev->headingp;
	(*cl)->keymenu   = (*cl)->prev->keymenu;
	(*cl)->help      = (*cl)->prev->help;
	(*cl)->tool      = (*cl)->prev->tool;
	(*cl)->varnamep  = (*cl)->prev->varnamep;
	*cl		 = (*cl)->prev;
	if(!after)
	  (*cl)->value   = NULL;

	if(after)
	  *newval	 = &(*cl)->next->value;
	else
	  *newval	 = &(*cl)->value;
    }

    /*
     * now fix up varmem values and fill in new values that have been
     * left NULL
     */
    for(ctmp = (*cl)->varnamep, i = 0;
	(*cl)->var->user_val.l[i];
	ctmp = ctmp->next, i++){
	ctmp->varmem = i;
	if(!ctmp->value){
	    /* BUG:  We should be able to do this without the temp
	     * copy...  
	     */
	    char *ptmp = pretty_value(ps, ctmp);
	    ctmp->value = (ctmp->varnamep->flags & CF_PRINTER) ? printer_name(ptmp) : cpystr(ptmp);
	    fs_give((void **)&ptmp);
	}
    }
}


/*
 *
 */
void
config_del_list_item(cl, newval)
    CONF_S  **cl;
    char   ***newval;
{
    char   **bufp;
    int	     i;
    CONF_S  *ctmp;

    if((*cl)->var->user_val.l[(*cl)->varmem + 1]){
	for(bufp = &(*cl)->var->user_val.l[(*cl)->varmem];
	    *bufp = *(bufp+1); bufp++)
	  ;

	if(*cl == (*cl)->varnamep){		/* leading value */
	    if((*cl)->value)
	      fs_give((void **)&(*cl)->value);

	    ctmp = (*cl)->next;
	    (*cl)->value = ctmp->value;
	    ctmp->value  = NULL;
	}
	else{
	    ctmp = *cl;			/* blast the confline */
	    *cl = (*cl)->next;
	    if(ctmp == opt_screen->top_line)
	      opt_screen->top_line = *cl;
	}

	snip_confline(&ctmp);

	for(ctmp = (*cl)->varnamep, i = 0;	/* now fix up varmem values */
	    (*cl)->var->user_val.l[i];
	    ctmp = ctmp->next, i++)
	  ctmp->varmem = i;
    }
    else if((*cl)->varmem){			/* blasted last in list */
	ctmp = *cl;
	*cl = (*cl)->prev;
	if(ctmp == opt_screen->top_line)
	  opt_screen->top_line = *cl;

	snip_confline(&ctmp);
    }
    else{					/* blasted last remaining */
	fs_give((void **)&(*cl)->var->user_val.l);
	*newval = &(*cl)->value;
    }
}


/*
 * feature list manipulation tool
 * 
 * 
 * returns:  -1 on unrecognized cmd, 0 if no change, 1 if change
 */
int
checkbox_tool(ps, cmd, cl, flags)
    struct pine  *ps;
    int		  cmd;
    CONF_S	**cl;
    unsigned      flags;
{
    int  rv = 0;

    switch(cmd){
      case MC_TOGGLE:				/* mark/unmark feature */
	if((*cl)->var == &ps->vars[V_FEATURE_LIST]){
	    rv = 1;
	    toggle_feature_bit(ps, (*cl)->varmem, (*cl)->var, (*cl)->value);
	}
	else
	  q_status_message(SM_ORDER | SM_DING, 3, 6,
			   "Programmer botch!  Unknown checkbox type.");

	break;

      case MC_EXIT:				 /* exit */
	rv = config_exit_cmd(flags);
	break;

      default :
	rv = -1;
	break;
    }

    return(rv);
}


/*
 * Message flag manipulation tool
 * 
 * 
 * returns:  -1 on unrecognized cmd, 0 if no change, 1 if change
 */
int
flag_checkbox_tool(ps, cmd, cl, flags)
    struct pine  *ps;
    int		  cmd;
    CONF_S	**cl;
    unsigned      flags;
{
    int  rv = 0, state;

    switch(cmd){
      case MC_TOGGLE:				/* mark/unmark feature */
	state = (*cl)->d.fp->set;
	state = (state == 1) ? 0 : (!state && ((*cl)->d.fp->ukn)) ? 2 : 1;
	(*cl)->value[1] = (state == 0) ? ' ' : ((state == 1) ? 'X': '?');
	(*cl)->d.fp->set = state;
	rv = 1;
	break;

      case MC_EXIT:				/* exit */
	rv = simple_exit_cmd(flags);
	break;

      default :
	rv = -1;
	break;
    }

    return(rv);
}


/*
 * simple radio-button style variable handler
 */
int
radiobutton_tool(ps, cmd, cl, flags)
    struct pine  *ps;
    int	          cmd;
    CONF_S      **cl;
    unsigned      flags;
{
    int	       rv = 0;
    CONF_S    *ctmp;

    switch(cmd){
      case MC_CHOICE :				/* set/unset feature */

	if(fixed_var((*cl)->var, NULL, NULL)){
	    if((*cl)->var->user_val.p
	       && want_to("Delete old unused personal option setting",
			  'y', 'n', NO_HELP, WT_FLUSH_IN) == 'y'){
		fs_give((void **)&(*cl)->var->user_val.p);
		q_status_message(SM_ORDER, 0, 3, "Deleted");
		rv = 1;
	    }

	    return(rv);
	}

	/* hunt backwards, turning off old values */
	for(ctmp = *cl; ctmp && !(ctmp->flags & CF_NOSELECT) && !ctmp->varname;
	    ctmp = prev_confline(ctmp))
	  ctmp->value[1] = ' ';

	/* hunt forwards, turning off old values */
	for(ctmp = *cl;
	    ctmp && !ctmp->varname && !(ctmp->flags & CF_NOSELECT);
	    ctmp = next_confline(ctmp))
	  ctmp->value[1] = ' ';

	/* turn on current value */
	(*cl)->value[1] = R_SELD;

	if((*cl)->var == &ps->vars[V_SAVED_MSG_NAME_RULE]
	   || (*cl)->var == &ps->vars[V_FCC_RULE]
	   || (*cl)->var == &ps->vars[V_GOTO_DEFAULT_RULE]
	   || (*cl)->var == &ps->vars[V_INCOMING_STARTUP]
	   || (*cl)->var == &ps->vars[V_AB_SORT_RULE]
#ifndef	_WINDOWS
	   || (*cl)->var == &ps->vars[V_COLOR_STYLE]
#endif
	   || (*cl)->var == &ps->vars[V_FLD_SORT_RULE]){
	    NAMEVAL_S *rule;

	    if((*cl)->var == &ps->vars[V_SAVED_MSG_NAME_RULE]){
		rule		  = save_msg_rules((*cl)->varmem);
		ps->save_msg_rule = rule->value;
	    }
	    else if((*cl)->var == &ps->vars[V_FCC_RULE]){
		rule	     = fcc_rules((*cl)->varmem);
		ps->fcc_rule = rule->value;
	    }
	    else if((*cl)->var == &ps->vars[V_GOTO_DEFAULT_RULE]){
		rule		      = goto_rules((*cl)->varmem);
		ps->goto_default_rule = rule->value;
	    }
	    else if((*cl)->var == &ps->vars[V_INCOMING_STARTUP]){
		rule		     = incoming_startup_rules((*cl)->varmem);
		ps->inc_startup_rule = rule->value;
	    }
#ifndef	_WINDOWS
	    else if((*cl)->var == &ps->vars[V_COLOR_STYLE]){
		int old_uc;

		old_uc            = pico_usingcolor();
		rule	          = col_style((*cl)->varmem);
		ps->color_style   = rule->value;
		pico_toggle_color(0);
		switch(ps->color_style){
		  case COL_NONE:
		  case COL_TERMDEF:
		    pico_set_color_options(0);
		    break;
		  case COL_ANSI8:
		    pico_set_color_options(COLOR_ANSI8_OPT);
		    break;
		  case COL_ANSI16:
		    pico_set_color_options(COLOR_ANSI16_OPT);
		    break;
		}

		if(ps->color_style != COL_NONE)
		  pico_toggle_color(1);
	    
		if(pico_usingcolor())
		  pico_set_normal_color();

		if(!old_uc && pico_usingcolor()){

		    /* remove the two explanatory warning lines */
		    if(ctmp && ctmp->flags & CF_NOSELECT &&
		       ctmp->prev && !(ctmp->prev->flags & CF_NOSELECT) &&
		       ctmp->next && ctmp->next->flags & CF_NOSELECT &&
		       ctmp->next->next &&
		       ctmp->next->next->flags & CF_NOSELECT){
			ctmp->prev->next = ctmp->next->next;
			ctmp->next->next->prev = ctmp->prev;
			ctmp->next->next = NULL;
			free_conflines(&ctmp);
		    }

		    /* make colors selectable */
		    for(ctmp = *cl; ctmp; ctmp = next_confline(ctmp))
		      if(ctmp->flags & CF_POT_SLCTBL)
		        ctmp->flags &= ~CF_NOSELECT;
		}
		else if(old_uc && !pico_usingcolor()){
		    new_confline(&ctmp);
		    ctmp->help   = NO_HELP;
		    ctmp->flags |= CF_NOSELECT;
		    ctmp->value  = cpystr(COLORNOSET);

		    new_confline(&ctmp);
		    /* Blank line */
		    ctmp->flags |= (CF_NOSELECT | CF_B_LINE);

		    /* make colors non-selectable */
		    for(ctmp = *cl; ctmp; ctmp = next_confline(ctmp))
		      if(ctmp->flags & CF_POT_SLCTBL)
		        ctmp->flags |= CF_NOSELECT;
		}

		clear_index_cache();
		ClearScreen();
		ps->mangled_screen = 1;
	    }
#endif
	    else if((*cl)->var == &ps->vars[V_FLD_SORT_RULE]){
		rule	          = fld_sort_rules((*cl)->varmem);
		ps->fld_sort_rule = rule->value;
	    }
	    else{
		rule	         = ab_sort_rules((*cl)->varmem);
		ps->ab_sort_rule = rule->value;
		addrbook_reset();
	    }

	    if((*cl)->var->user_val.p)
	      fs_give((void **)&(*cl)->var->user_val.p);

	    (*cl)->var->user_val.p = cpystr(rule->name);

	    ps->mangled_body = 1;	/* BUG: redraw it all for now? */
	    rv = 1;
	}
	else if((*cl)->var == &ps->vars[V_SORT_KEY]){
	    ps->def_sort_rev  = (*cl)->varmem >= (short) EndofList;
	    ps->def_sort      = (SortOrder) ((*cl)->varmem - (ps->def_sort_rev
								 * EndofList));
	    if((*cl)->var->user_val.p)
	      fs_give((void **)&(*cl)->var->user_val.p);

	    sprintf(tmp_20k_buf, "%s%s%s", sort_name(ps->def_sort),
		    (ps->def_sort_rev) ? "/" : "",
		    (ps->def_sort_rev) ? "Reverse" : "");

	    (*cl)->var->user_val.p = cpystr(tmp_20k_buf);

	    ps->mangled_body = 1;	/* BUG: redraw it all for now? */
	    rv = 1;
	}
	else
	  q_status_message(SM_ORDER | SM_DING, 3, 6,
			   "Programmer botch!  Unknown radiobutton type.");

	break;

      case MC_EXIT:				/* exit */
	rv = config_exit_cmd(flags);
	break;

      default :
	rv = -1;
	break;
    }

    return(rv);
}



/*
 * simple yes/no style variable handler
 */
int
yesno_tool(ps, cmd, cl, flags)
    struct pine  *ps;
    int	          cmd;
    CONF_S      **cl;
    unsigned      flags;
{
    int  rv = 0;

    switch(cmd){
      case MC_TOGGLE:				/* toggle yes to no and back */
	if(fixed_var((*cl)->var, NULL, NULL)){
	    if((*cl)->var->user_val.p
	       && want_to("Delete old unused personal option setting",
			  'y', 'n', NO_HELP, WT_FLUSH_IN) == 'y'){
		fs_give((void **)&(*cl)->var->user_val.p);
		q_status_message(SM_ORDER, 0, 3, "Deleted");
		rv = 1;
	    }

	    return(rv);
	}

	rv = 1;
	fs_give((void **)&(*cl)->value);
	if((*cl)->var->user_val.p)
	  fs_give((void **)&(*cl)->var->user_val.p);

	if((*cl)->var->user_val.p && !strucmp((*cl)->var->user_val.p, "yes")
	   || (!(*cl)->var->user_val.p && (*cl)->var->current_val.p
	       && !strucmp((*cl)->var->current_val.p, "yes")))
	  (*cl)->var->user_val.p = cpystr("No");
	else
	  (*cl)->var->user_val.p = cpystr("Yes");

	sprintf(tmp_20k_buf, "%-*s", ps->ttyo->screen_cols - (*cl)->valoffset,
		(*cl)->var->user_val.p);

	(*cl)->value = cpystr(tmp_20k_buf);

	if((*cl)->var == &ps->vars[V_USE_ONLY_DOMAIN_NAME]){
	    set_current_val((*cl)->var, FALSE, FALSE);
	    init_hostname(ps);
	}

	break;

      case MC_EXIT:				/* exit */
	rv = config_exit_cmd(flags);
	break;

      default :
	rv = -1;
	break;
    }

    return(rv);
}


int
print_select_tool(ps, cmd, cl, flags)
    struct pine *ps;
    int          cmd;
    CONF_S     **cl;
    unsigned     flags;
{
    int rc, i, retval;
    char *p;
    struct variable *vtmp;

    switch(cmd){
      case MC_EXIT:
        retval = config_exit_cmd(flags);
	break;

      case MC_CHOICE :
	if(cl && *cl){
	    if((*cl)->var){
		vtmp = (*cl)->var;
		i = vtmp->current_val.l
		    && vtmp->current_val.l[(*cl)->varmem]
		    && vtmp->current_val.l[(*cl)->varmem][0];
		rc = set_variable(V_PRINTER,
			vtmp->current_val.l
			  ? vtmp->current_val.l[(*cl)->varmem] : NULL, 1);
		if(rc == 0){
		    if(vtmp == &ps->vars[V_STANDARD_PRINTER])
		      ps->printer_category = 2;
		    else if(vtmp == &ps->vars[V_PERSONAL_PRINT_COMMAND])
		      ps->printer_category = 3;

		    set_variable(V_PERSONAL_PRINT_CATEGORY, 
			comatose(ps->printer_category), 0);

		    p = NULL;
		    if(i){
			char *nick, *q;

			parse_printer(vtmp->current_val.l[(*cl)->varmem],
			    &nick, &q, NULL, NULL, NULL, NULL);
			p = cpystr(*nick ? nick : q);
			fs_give((void **)&nick);
			fs_give((void **)&q);
		    }

		    q_status_message3(SM_ORDER,0,3, "Default printer %s%s%s",
			p ? "set to \"" : "unset", p ? p : "", p ? "\"" : ""); 

		    if(p)
		      fs_give((void **)&p);
		}
		else
		  q_status_message(SM_ORDER,3,5,
			"Trouble setting default printer");

		retval = 1;
	    }
	    else if(!strcmp((*cl)->value,ANSI_PRINTER)){
		rc = set_variable(V_PRINTER, ANSI_PRINTER, 1);
		if(rc == 0){
		    ps->printer_category = 1;
		    set_variable(V_PERSONAL_PRINT_CATEGORY, 
			comatose(ps->printer_category), 0);
		    q_status_message1(SM_ORDER,0,3,
			"Default printer set to \"%s\"", ANSI_PRINTER);
		}
		else
		  q_status_message(SM_ORDER,3,5,
			"Trouble setting default printer");

		retval = 1;
	    }
	    else{
		char aname[100];

		strcat(strcpy(aname, ANSI_PRINTER), no_ff);
		if(!strcmp((*cl)->value,aname)){
		    rc = set_variable(V_PRINTER, aname, 1);
		    if(rc == 0){
			ps->printer_category = 1;
			set_variable(V_PERSONAL_PRINT_CATEGORY, 
			    comatose(ps->printer_category), 0);
			q_status_message1(SM_ORDER,0,3,
			    "Default printer set to \"%s\"", aname);
		    }
		    else
		      q_status_message(SM_ORDER,3,5,
			    "Trouble setting default printer");

		    retval = 1;
		}
		else
		  retval = 0;
	    }
	}
	else
	  retval = 0;

	if(retval){
	    ps->mangled_body = 1;	/* BUG: redraw it all for now? */
	    set_def_printer_value(ps->VAR_PRINTER);
	}

	break;

      default:
	retval = -1;
	break;
    }

    return(retval);
}


int
print_edit_tool(ps, cmd, cl, flags)
    struct pine *ps;
    int          cmd;
    CONF_S     **cl;
    unsigned     flags;
{
    char	     prompt[81], sval[MAXPATH+1], name[MAXPATH+1];
    char            *nick, *p, *tmp, **newval = NULL, **ltmp = NULL;
    int		     rv = 0, skip_to_next = 0, after = 0, i = 4, j, k = 0;
    int		     oeflags, changing_selected = 0;
    HelpType         help;
    ESCKEY_S         ekey[6];

    if(cmd == MC_CHOICE)
      return(print_select_tool(ps, cmd, cl, flags));

    if(!(cl && *cl && (*cl)->var))
      return(0);

    switch(cmd){
      case MC_ADD:				/* add to list */
	sval[0] = '\0';
	if(!fixed_var((*cl)->var, "add to", NULL)){

	    if((*cl)->var->user_val.l && (*cl)->value){
		strcpy(prompt, "Enter printer name : ");
	    }
	    else if(!(*cl)->var->user_val.l && (*cl)->var->current_val.l){
		/* Add to list which doesn't exist, but default does exist */
		ekey[0].ch    = 'r';
		ekey[0].rval  = 'r';
		ekey[0].name  = "R";
		ekey[0].label = "Replace";
		ekey[1].ch    = 'a';
		ekey[1].rval  = 'a';
		ekey[1].name  = "A";
		ekey[1].label = "Add To";
		ekey[2].ch    = -1;
		strcpy(prompt, "Replace or Add To default value ? ");
		switch(i = radio_buttons(prompt, -FOOTER_ROWS(ps), ekey, 'a',
					 'x', h_config_replace_add, RB_NORM)){
		  case 'a':
		    /* Make a list of the default commands, leaving room for 
		       the command we are about to add below. */
		    for(k = 0; (*cl)->var->current_val.l[k]; k++)
		      ;
		    ltmp = (char **)fs_get((k+2) * sizeof(char *));
		    
		    for(j = 0; j < k; j++){
		      ltmp[j] = cpystr((*cl)->var->current_val.l[j]);
		    }
		    ltmp[k + 1] = ltmp[k] = NULL;

add_text:
		    strcpy(prompt, "Enter name of printer to be added : ");
		    break;
		    
		  case 'r':
replace_text:
		    strcpy(prompt,
			"Enter the name for replacement printer : ");
		    break;
		    
		  case 'x':
		    cmd_cancelled("Add");
		    break;
		}

		if(i == 'x')
		  break;
	    }
	    else
	      strcpy(prompt, "Enter name of printer to be added : ");

	    ps->mangled_footer = 1;
	    help = NO_HELP;

	    name[0] = '\0';
	    i = 2;
	    while(i != 0 && i != 1){
		if((*cl)->var->user_val.l && (*cl)->value){
		    ekey[0].ch    = ctrl('W');
		    ekey[0].rval  = 5;
		    ekey[0].name  = "^W";
		    ekey[0].label = after ? "InsertBefore" : "InsertAfter";
		    ekey[1].ch    = -1;
		}
		else
		  ekey[0].ch    = -1;

		oeflags = OE_APPEND_CURRENT;
		i = optionally_enter(name, -FOOTER_ROWS(ps), 0, MAXPATH,
				     prompt,
				     (ekey[0].ch != -1) ? ekey : NULL,
				     help, &oeflags);
		if(i == 0){
		    rv = ps->mangled_body = 1;
		    removing_leading_and_trailing_white_space(name);
		}
		else if(i == 1){
		    cmd_cancelled("Add");
		}
		else if(i == 3){
		    help = (help == NO_HELP) ? h_config_insert_after : NO_HELP;
		}
		else if(i == 4){		/* no redraw, yet */
		}
		else if(i == 5){ /* change from/to prepend to/from append */
		    after = after ? 0 : 1;
		}
	    }

	    if(i == 0)
	      i = 2;

#ifdef OS2
	    strcpy(prompt, "Enter port or |command : ");
#else
	    strcpy(prompt, "Enter command for printer : ");
#endif
	    while(i != 0 && i != 1){
		oeflags = OE_APPEND_CURRENT;
		i = optionally_enter(sval, -FOOTER_ROWS(ps), 0, MAXPATH,
				     prompt,
				     (ekey[0].ch != -1) ? ekey : NULL,
				     help, &oeflags);
		if(i == 0){
		    rv = ps->mangled_body = 1;
		    removing_leading_and_trailing_white_space(sval);
		    if(*sval || !(*cl)->var->user_val.l){

			for(tmp = sval; *tmp; tmp++)
			  if(*tmp == ',')
			  i++;	/* conservative count of ,'s */

			if(!i){	/* only one item */
			  if (!ltmp){
			    ltmp = (char **)fs_get(2 * sizeof(char *));
			    ltmp[1] = NULL;
			    k = 0;
			  }
			  if(*name){
			    ltmp[k] = (char *)fs_get(strlen(name) + 4 + strlen(sval) + 1);
			    sprintf(ltmp[k], "%s [] %s", name, sval);
			  }
			  else
			    ltmp[k] = cpystr(sval);
			}
			else{
			    /*
			     * Don't allow input of multiple entries at once.
			     */
			    q_status_message(SM_ORDER,3,5,
				"No commas allowed in command");
			    i = 2;
			    continue;
			}

			config_add_list(ps, cl, ltmp, &newval, after);

			if(after)
			  skip_to_next = 1;

			fs_give((void **)&ltmp);
			k = 0;
		    }
		    else
		      q_status_message1(SM_ORDER, 0, 3,
					 "Can't add %s to list", empty_val);
		}
		else if(i == 1){
		    cmd_cancelled("Add");
		}
		else if(i == 3){
		    help = help == NO_HELP ? h_config_print_cmd : NO_HELP;
		}
		else if(i == 4){		/* no redraw, yet */
		}
		else if(i == 5){ /* change from/to prepend to/from append */
		    after = after ? 0 : 1;
		}
	    }
	}

	break;

      case MC_DELETE:					/* delete */
	if((*cl)->var->current_val.l
	  && (*cl)->var->current_val.l[(*cl)->varmem]
	  && !strucmp(ps->VAR_PRINTER,(*cl)->var->current_val.l[(*cl)->varmem]))
	    changing_selected = 1;

	if(!(*cl)->var->user_val.l && (*cl)->var->current_val.l){
	    char pmt[40];

	    sprintf(pmt, "Override default with %s", empty_val2);
	    if(want_to(pmt, 'n', 'n', NO_HELP, WT_FLUSH_IN) == 'y'){
		char **ltmp;

		sval[0] = '\0';
		ltmp    = (char **)fs_get(2 * sizeof(char *));
		ltmp[0] = cpystr(sval);
		ltmp[1] = NULL;
		config_add_list(ps, cl, ltmp, &newval, 0);
		fs_give((void **)&ltmp);
		rv = ps->mangled_body = 1;
	    }
	}
	else if(!(*cl)->var->user_val.l){
	    q_status_message(SM_ORDER, 0, 3, "No set value to delete");
	}
	else{
	    if((*cl)->var->is_fixed){
		parse_printer((*cl)->var->user_val.l[(*cl)->varmem],
		    &nick, &p, NULL, NULL, NULL, NULL);
	        sprintf(prompt, "Delete (unused) printer %.30s ",
		    *nick ? nick : (!*p) ? empty_val2 : p);
		fs_give((void **)&nick);
		fs_give((void **)&p);
	    }
	    else
	      sprintf(prompt, "Really delete item %.20s from printer list ",
		    int2string((*cl)->varmem + 1));

	    ps->mangled_footer = 1;
	    if(want_to(prompt,'n','n',h_config_print_del, WT_FLUSH_IN) == 'y'){
		rv = ps->mangled_body = 1;
		fs_give((void **)&(*cl)->var->user_val.l[(*cl)->varmem]);
		config_del_list_item(cl, &newval);
	    }
	    else
	      q_status_message(SM_ORDER, 0, 3, "Printer not deleted");
	}

	break;

      case MC_EDIT:				/* edit/change list option */
	if((*cl)->var->current_val.l
	  && (*cl)->var->current_val.l[(*cl)->varmem]
	  && !strucmp(ps->VAR_PRINTER,(*cl)->var->current_val.l[(*cl)->varmem]))
	    changing_selected = 1;

	if(fixed_var((*cl)->var, NULL, "printer"))
	  break;
	else if(!(*cl)->var->user_val.l && (*cl)->var->current_val.l)
	  goto replace_text;
	else if(!(*cl)->var->user_val.l && !(*cl)->var->current_val.l)
	  goto add_text;
	else{
	    HelpType help;

	    ekey[0].ch    = 'n';
	    ekey[0].rval  = 'n';
	    ekey[0].name  = "N";
	    ekey[0].label = "Name";
	    ekey[1].ch    = 'c';
	    ekey[1].rval  = 'c';
	    ekey[1].name  = "C";
	    ekey[1].label = "Command";
	    ekey[2].ch    = 'o';
	    ekey[2].rval  = 'o';
	    ekey[2].name  = "O";
	    ekey[2].label = "Options";
	    ekey[3].ch    = -1;
	    strcpy(prompt, "Change Name or Command or Options ? ");
	    i = radio_buttons(prompt, -FOOTER_ROWS(ps), ekey, 'c', 'x',
			      h_config_print_name_cmd, RB_NORM);

	    if(i == 'x'){
		cmd_cancelled("Change");
		break;
	    } 
	    else if(i == 'c'){
		char *all_but_cmd;

		parse_printer((*cl)->var->user_val.l[(*cl)->varmem],
		    NULL, &p, NULL, NULL, NULL, &all_but_cmd);
		
		strcpy(prompt, "Change command : ");
		strcpy(sval, p ? p : "");
		fs_give((void **)&p);

		ps->mangled_footer = 1;
		help = NO_HELP;
		while(1){
		    oeflags = OE_APPEND_CURRENT;
		    i = optionally_enter(sval, -FOOTER_ROWS(ps), 0, MAXPATH,
					 prompt, NULL, help, &oeflags);
		    if(i == 0){
			removing_leading_and_trailing_white_space(sval);
			rv = ps->mangled_body = 1;
			if((*cl)->var->user_val.l[(*cl)->varmem])
			  fs_give((void **)&(*cl)->var->user_val.l[
							       (*cl)->varmem]);

			i = 0;
			for(tmp = sval; *tmp; tmp++)
			  if(*tmp == ',')
			    i++;	/* count of ,'s */

			if(!i){	/* only one item */
			    (*cl)->var->user_val.l[(*cl)->varmem]
			      = (char *)fs_get(strlen(all_but_cmd) +
						strlen(sval) + 1);
			    strcpy((*cl)->var->user_val.l[(*cl)->varmem],
				    all_but_cmd);
			    strcat((*cl)->var->user_val.l[(*cl)->varmem],
				    sval);

			    newval = &(*cl)->value;
			}
			else{
			    /*
			     * Don't allow input of multiple entries at once.
			     */
			    q_status_message(SM_ORDER,3,5,
				"No commas allowed in command");
			    continue;
			}
		    }
		    else if(i == 1){
			cmd_cancelled("Change");
		    }
		    else if(i == 3){
			help = help == NO_HELP ? h_config_change : NO_HELP;
			continue;
		    }
		    else if(i == 4){		/* no redraw, yet */
			continue;
		    }

		    break;
		}
	    }
	    else if(i == 'n'){
		char *all_but_nick;

		parse_printer((*cl)->var->user_val.l[(*cl)->varmem],
		    &p, NULL, NULL, NULL, &all_but_nick, NULL);
		
		strcpy(prompt, "Change name : ");
		strcpy(name, p ? p : "");
		fs_give((void **)&p);

		ps->mangled_footer = 1;
		help = NO_HELP;
		while(1){
		    oeflags = OE_APPEND_CURRENT;
		    i = optionally_enter(name, -FOOTER_ROWS(ps), 0, MAXPATH,
					 prompt, NULL, help, &oeflags);
		    if(i == 0){
			rv = ps->mangled_body = 1;
			removing_leading_and_trailing_white_space(name);
			if((*cl)->var->user_val.l[(*cl)->varmem])
			  fs_give((void **)&(*cl)->var->user_val.l[
							       (*cl)->varmem]);

			(*cl)->var->user_val.l[(*cl)->varmem]
			  = (char *)fs_get(strlen(name) + 1
					+ ((*all_but_nick == '[') ? 0 : 3)
					+ strlen(all_but_nick) + 1);
			sprintf((*cl)->var->user_val.l[(*cl)->varmem],
			    "%s %s%s", name,
			    (*all_but_nick == '[') ? "" : "[] ",
			    all_but_nick);
			
			newval = &(*cl)->value;
		    }
		    else if(i == 1){
			cmd_cancelled("Change");
		    }
		    else if(i == 3){
			help = help == NO_HELP ? h_config_change : NO_HELP;
			continue;
		    }
		    else if(i == 4){		/* no redraw, yet */
			continue;
		    }

		    break;
		}
		
		fs_give((void **)&all_but_nick);
	    }
	    else if(i == 'o'){
		HelpType help;

		ekey[0].ch    = 'i';
		ekey[0].rval  = 'i';
		ekey[0].name  = "I";
		ekey[0].label = "Init";
		ekey[1].ch    = 't';
		ekey[1].rval  = 't';
		ekey[1].name  = "T";
		ekey[1].label = "Trailer";
		ekey[2].ch    = -1;
		strcpy(prompt, "Change Init string or Trailer string ? ");
		j = radio_buttons(prompt, -FOOTER_ROWS(ps), ekey, 'i', 'x',
				  h_config_print_opt_choice, RB_NORM);

		if(j == 'x'){
		    cmd_cancelled("Change");
		    break;
		} 
		else{
		    char *init, *trailer;

		    parse_printer((*cl)->var->user_val.l[(*cl)->varmem],
			&nick, &p, &init, &trailer, NULL, NULL);
		    
		    sprintf(prompt, "Change %s string : ",
			(j == 'i') ? "INIT" : "TRAILER");
		    strcpy(sval, (j == 'i') ? init : trailer);

		    tmp = string_to_cstring(sval);
		    strcpy(sval, tmp);
		    fs_give((void **)&tmp);
		    
		    ps->mangled_footer = 1;
		    help = NO_HELP;
		    while(1){
			oeflags = OE_APPEND_CURRENT;
			i = optionally_enter(sval, -FOOTER_ROWS(ps), 0,
			    MAXPATH, prompt, NULL, help, &oeflags);
			if(i == 0){
			    removing_leading_and_trailing_white_space(sval);
			    rv = 1;
			    if((*cl)->var->user_val.l[(*cl)->varmem])
			      fs_give((void **)&(*cl)->var->user_val.l[
							       (*cl)->varmem]);
			    if(j == 'i'){
				init = cstring_to_hexstring(sval);
				tmp = cstring_to_hexstring(trailer);
				fs_give((void **)&trailer);
				trailer = tmp;
			    }
			    else{
				trailer = cstring_to_hexstring(sval);
				tmp = cstring_to_hexstring(init);
				fs_give((void **)&init);
				init = tmp;
			    }

			    (*cl)->var->user_val.l[(*cl)->varmem]
			      = (char *)fs_get(strlen(nick) + 1
				  + 2 + strlen("INIT=") + strlen(init)
				  + 1 + strlen("TRAILER=") + strlen(trailer)
				  + 1 + strlen(p) + 1);
			    sprintf((*cl)->var->user_val.l[(*cl)->varmem],
				"%s%s%s%s%s%s%s%s%s%s%s",
	    /* nick */	    nick,
	    /* space */	    *nick ? " " : "",
	    /* [ */		    (*nick || *init || *trailer) ? "[" : "",
	    /* INIT= */	    *init ? "INIT=" : "",
	    /* init */	    init,
	    /* space */	    (*init && *trailer) ? " " : "",
	    /* TRAILER= */	    *trailer ? "TRAILER=" : "",
	    /* trailer */	    trailer,
	    /* ] */		    (*nick || *init || *trailer) ? "]" : "",
	    /* space */	    (*nick || *init || *trailer) ? " " : "",
	    /* command */	    p);
	    
			    newval = &(*cl)->value;
			}
			else if(i == 1){
			    cmd_cancelled("Change");
			}
			else if(i == 3){
			    help=(help == NO_HELP)?h_config_print_init:NO_HELP;
			    continue;
			}
			else if(i == 4){		/* no redraw, yet */
			    continue;
			}

			break;
		    }

		    fs_give((void **)&nick);
		    fs_give((void **)&p);
		    fs_give((void **)&init);
		    fs_give((void **)&trailer);
		}
	    }
	}

	break;

      case MC_EXIT:				/* exit */
	rv = config_exit_cmd(flags);
	break;

      default:
	rv = -1;
	break;
    }

    if(skip_to_next)
      *cl = next_confline(*cl);

    /*
     * At this point, if changes occurred, var->user_val.X is set.
     * So, fix the current_val, and handle special cases...
     */
    if(rv == 1){
	set_current_val((*cl)->var, TRUE, FALSE);
	fix_side_effects(ps, (*cl)->var, 0);

	if(newval){
	    if(*newval)
	      fs_give((void **)newval);
	    
	    if((*cl)->var->current_val.l)
	      *newval = printer_name((*cl)->var->current_val.l[(*cl)->varmem]);
	    else
	      *newval = cpystr("");
	}

	if(changing_selected)
	  print_select_tool(ps, 's', cl, flags);
    }

    return(rv);
}


int
context_select_tool(ps, cmd, cl, flags)
    struct pine *ps;
    int          cmd;
    CONF_S     **cl;
    unsigned     flags;
{
    int retval = 0;

    switch(cmd){
      case MC_CHOICE :
	(*cl)->d.c.cs->selected = (*cl)->d.c.ct;
	retval = simple_exit_cmd(flags);
	break;

      case MC_DELETE :
	if((*cl)->d.c.ct->use & CNTXT_INCMNG)
	  q_status_message1(SM_ORDER, 0, 3, "Sorry, Can't delete %s",
			    (*cl)->d.c.ct->nickname);
	else if(!fixed_var((*cl)->var, "delete", "collection"))
	  context_select_delete(ps, cl);

	break;

      case MC_EDIT :
	if((*cl)->d.c.ct->use & CNTXT_INCMNG)
	  q_status_message1(SM_ORDER, 0, 3, "Sorry, Can't rename %s",
			    (*cl)->d.c.ct->nickname);
	else if(!fixed_var((*cl)->var, "add to", "collection")){
	    context_select_edit(ps, cl);
	    ps->mangled_screen = 1;
	}

	break;

      case MC_ADD :
	if(!fixed_var((*cl)->var, "add to", "collection")){
	    context_select_add(ps, cl);
	    ps->mangled_screen = 1;
	}

	break;

      case MC_SHUFFLE :
	if((*cl)->d.c.ct->use & CNTXT_INCMNG)
	  q_status_message1(SM_ORDER, 0, 3, "Sorry, Can't Shuffle %s",
			    (*cl)->d.c.ct->nickname);
	else if(!fixed_var((*cl)->var, "Shuffle", "collection"))
	  context_select_shuffle(ps, cl);

	break;

      case MC_EXIT :
        retval = simple_exit_cmd(flags);
	break;

      case MC_MAIN :
        retval = simple_exit_cmd(flags);
	ps_global->next_screen = main_menu_screen;
	break;

      case MC_INDEX :
	retval = simple_exit_cmd(flags);
	ps_global->next_screen = mail_index_screen;
	break;

      case MC_COMPOSE :
	retval = simple_exit_cmd(flags);
	ps_global->next_screen = compose_screen;
	break;

      case MC_ROLE :
	retval = simple_exit_cmd(flags);
	ps_global->next_screen = alt_compose_screen;
	break;

      case MC_GOTO :
        {
	    CONTEXT_S *c = (*cl)->d.c.ct;
	    char *new_fold = broach_folder(-FOOTER_ROWS(ps), 0, &c);

	    if(new_fold && do_broach_folder(new_fold, c) > 0){
		ps_global->next_screen = mail_index_screen;
		retval = simple_exit_cmd(flags);
	    }
	    else
	      ps->mangled_footer = 1;
        }

	break;

      case MC_QUIT :
	retval = simple_exit_cmd(flags);
	ps_global->next_screen = quit_screen;
	break;

      default:
	retval = -1;
	break;
    }

    if(retval > 0)
      ps->mangled_body = 1;

    return(retval);
}


void
context_select_add(ps, cl)
    struct pine  *ps;
    CONF_S	**cl;
{
    char	    *raw_ctxt;
    CONTEXT_S	    *new_ctxt;
    CONF_S	    *orig_cl, *new_cl, *ctmp;
    struct key_menu *km;
    CONT_SCR_S	    *cs;

    if(raw_ctxt = context_edit_screen(ps, "ADD", NULL, NULL, NULL, NULL)){

	/* create a corresponding new CONF_S */
	new_ctxt = new_context(raw_ctxt, NULL);

	/* find last "cl" and append new context to it */
	for(; (*cl)->headingp->next; *cl = (*cl)->headingp->next)
	  ;

	new_ctxt->var.v = (*cl)->d.c.ct->var.v;
	new_ctxt->var.i = (*cl)->d.c.ct->var.i + 1;
	orig_cl = *cl;

	km   = (*cl)->keymenu;
	cs   = (*cl)->d.c.cs;
	ctmp = NULL;

	new_confline(&ctmp);
	*cl		= new_cl = ctmp;
	ctmp->value	= cpystr(new_ctxt->nickname
				   ? new_ctxt->nickname
				   : new_ctxt->context);
	ctmp->var	= new_ctxt->var.v;
	ctmp->keymenu   = km;
	ctmp->help      = h_collection_maint;
	ctmp->tool      = context_select_tool;
	ctmp->valoffset = 4;
	ctmp->d.c.ct    = new_ctxt;
	ctmp->d.c.cs	= cs;

	/* Explanation */
	new_confline(&ctmp);
	ctmp->value     = cpystr(new_ctxt->label ? new_ctxt->label : "* * *");
	ctmp->keymenu   = km;
	ctmp->help      = h_collection_maint;
	ctmp->tool      = context_select_tool;
	ctmp->flags    |= CF_NOSELECT;
	ctmp->valoffset = 8;

	/* blank line */
	new_confline(&ctmp);
	new_cl->headingp  = ctmp;
	ctmp->keymenu	  = km;
	ctmp->help	  = h_collection_maint;
	ctmp->tool	  = context_select_tool;
	ctmp->flags	 |= CF_NOSELECT | CF_B_LINE;
	ctmp->valoffset	  = 0;

	/* integrate new context into context list */
	new_ctxt->prev = orig_cl->d.c.ct;
	orig_cl->d.c.ct->next = new_ctxt;

	/* Wire in new conf lines */
	orig_cl->headingp->next = new_cl;
	new_cl->prev = orig_cl->headingp;

	if(!css_var_insert(ps, new_ctxt, raw_ctxt)){
	    q_status_message(SM_ORDER|SM_DING, 3, 3,
			     "Error saving reordered context");
	    return;
	}

	/* Tell the user it was a huge success... */
	q_status_message(SM_ORDER, 0, 3,
		      "New collection added.  Use \"$\" to adjust order.");
    }
}



void
context_select_delete(ps, cl)
    struct pine  *ps;
    CONF_S      **cl;
{
    int	       new_current = 0, new_list = 0;
    char       tmp[MAILTMPLEN];
    CONF_S    *old_cl = *cl;
    FOLDER_S  *f;
    CONTEXT_S *ctxt;

    if(!((*cl)->var->user_val.l && (*cl)->var->user_val.l[0])){
	q_status_message(SM_ORDER | SM_DING, 3, 3,
			 "Can't delete default value.  Try rename.");
	return;
    }

    sprintf(tmp, "Delete the collection definition for \"%.40s\"",
	    old_cl->value);
    if(want_to(tmp, 'n', 'n', NO_HELP, WT_FLUSH_IN) == 'y'){
	/* Remove from var list */
	if(!css_var_delete(ps, old_cl->d.c.ct, NULL)){
	    q_status_message(SM_ORDER|SM_DING, 3, 3,
			     "Error deleting renamed context");
	    return;
	}
	  
	if(old_cl->d.c.ct == ps->context_list)
	  new_list = 1;

	if(old_cl->d.c.ct == ps->context_current){
	    strcpy(ps->cur_folder, ps->mail_stream->mailbox);
	    new_current = 1;
	}

	if(old_cl->d.c.ct == ps->context_last)
	  ps->context_last = NULL;

	/* Deleting last context?  Make generic default */
	if((old_cl->d.c.ct->next
	    && old_cl->d.c.ct->next->var.v == old_cl->d.c.ct->var.v)
	   || (old_cl->d.c.ct->prev
	       && old_cl->d.c.ct->prev->var.v == old_cl->d.c.ct->var.v)
	   || !old_cl->var->current_val.l){
	    /* Pick the resulting config line */
	    if(old_cl->headingp && old_cl->headingp->next
	       && old_cl->var == old_cl->headingp->next->var)
	      *cl = old_cl->headingp->next;
	    else if(!(*cl = context_select_prev(old_cl)))
	      panic("Internal error: null context list");

	    /* snip old context from list */
	    if(old_cl->d.c.ct->next)
	      old_cl->d.c.ct->next->prev = old_cl->d.c.ct->prev;

	    if(old_cl->d.c.ct->prev)
	      old_cl->d.c.ct->prev->next = old_cl->d.c.ct->next;

	    old_cl->d.c.ct->next = old_cl->d.c.ct->prev = NULL;

	    /* carry the save-default flag if necessary */
	    if(old_cl->d.c.ct->use & CNTXT_SAVEDFLT)
	      (*cl)->d.c.ct->use |= CNTXT_SAVEDFLT;

	    if((f = folder_entry(0, FOLDERS(old_cl->d.c.ct)))
	       && f->nickname && !strcmp(f->nickname, ps_global->inbox_name)){
		f = new_folder(ps->VAR_INBOX_PATH);
		f->nickname = cpystr(ps_global->inbox_name);
		f->name_len = strlen(f->nickname);
		folder_insert(0, f, FOLDERS((*cl)->d.c.ct));
	    }

	    /* snip old config line group from list */
	    if(old_cl->prev)
	      old_cl->prev->next = old_cl->headingp->next;

	    if(old_cl->headingp->next)
	      old_cl->headingp->next->prev = old_cl->prev;

	    old_cl->prev = old_cl->headingp->next = NULL;

	    free_context(&old_cl->d.c.ct);
	    free_conflines(&old_cl);
	}
	else{
	    ctxt = new_context((*cl)->var->current_val.l[0], NULL);
	    if((ctxt->var.v = (*cl)->var) == &ps->vars[V_FOLDER_SPEC]){
		ctxt->use |= CNTXT_SAVEDFLT;
		if(!(ps->VAR_INCOMING_FOLDERS && ps->VAR_INCOMING_FOLDERS[0])){
		    f = new_folder(ps->VAR_INBOX_PATH);
		    f->nickname = cpystr(ps_global->inbox_name);
		    f->name_len = strlen(f->nickname);
		    folder_insert(0, f, FOLDERS(ctxt));
		}
	    }

	    /* Just tweak conf line data */
	    if(old_cl->value)
	      fs_give((void **) &old_cl->value);

	    old_cl->value = cpystr(ctxt->nickname
				   ? ctxt->nickname : ctxt->context);

	    if(old_cl->next->value)
	      fs_give((void **) &old_cl->next->value);

	    old_cl->next->value = cpystr(ctxt->label ? ctxt->label : "* * *");

	    if(ctxt->next = old_cl->d.c.ct->next)
	      old_cl->d.c.ct->next->prev = ctxt;

	    if(ctxt->prev = old_cl->d.c.ct->prev)
	      old_cl->d.c.ct->prev->next = ctxt;

	    free_context(&old_cl->d.c.ct);
	    old_cl->d.c.ct = ctxt;
	}

	if(new_list)
	  ps->context_list = (*cl)->d.c.ct;

	if(new_current){
	    ps->context_current = (*cl)->d.c.ct;
	    ps->mangled_header = 1;
	}

	ps->mangled_body = 1;
	q_status_message(SM_ORDER, 0, 3,
			 (old_cl == *cl)
			   ? "Last collection deleted.  Using default."
			   : "Collection deleted");

    }
    else
      q_status_message(SM_ORDER, 0, 3, "No collections deleted");
}


void
context_select_edit(ps, cl)
    struct pine  *ps;
    CONF_S      **cl;
{
    char      *raw_ctxt, tpath[MAILTMPLEN], *p, **newl;
    int	       n;
    CONTEXT_S *new_ctxt, *orig_ctxt;

    /* Undigest the context */
    strcpy(tpath, ((*cl)->d.c.ct->context[0] == '{'
		   && (p = strchr((*cl)->d.c.ct->context, '}')))
		      ? ++p
		      : (*cl)->d.c.ct->context);

    if(p = strstr(tpath, "%s"))
      *p = '\0';

    if(raw_ctxt = context_edit_screen(ps, "EDIT", (*cl)->d.c.ct->nickname,
				      (*cl)->d.c.ct->server, tpath,
				      (*cl)->d.c.ct->dir->view.user)){

	/* Generate corresponding context */
	new_ctxt = new_context(raw_ctxt, NULL);

	/* Replace relevant confing line strings/pointers */
	orig_ctxt     = (*cl)->d.c.ct;
	(*cl)->d.c.ct = new_ctxt;
	fs_give((void **) &(*cl)->value);
	(*cl)->value = cpystr(new_ctxt->nickname
				? new_ctxt->nickname
				: new_ctxt->context);
	fs_give((void **) &(*cl)->next->value);
	(*cl)->next->value = cpystr(new_ctxt->label
				      ? new_ctxt->label
				      : "* * *");

	/* Update new context's various bits of data */
	new_ctxt->var  = orig_ctxt->var;
	if(orig_ctxt->use & CNTXT_SAVEDFLT)
	  new_ctxt->use |= CNTXT_SAVEDFLT;

	/* Wire it into the linked context list */
	if(new_ctxt->next = orig_ctxt->next)
	  orig_ctxt->next->prev = new_ctxt;

	if(new_ctxt->prev = orig_ctxt->prev)
	  orig_ctxt->prev->next = new_ctxt;

	/* Any other pointers to old context? */
	if(orig_ctxt == ps->context_current){
	    ps->context_current = new_ctxt;
	    ps->mangled_header = 1;
	}

	if(orig_ctxt == ps->context_list)
	  init_inbox_mapping(ps->VAR_INBOX_PATH, ps->context_list = new_ctxt);

	if(orig_ctxt == ps->context_last)
	  ps->context_last = NULL;

	/* And replace old with new context */
	orig_ctxt->next = orig_ctxt->prev = NULL;
	free_context(&orig_ctxt);

	/* Update appropriate variable, start with copying */
	for(n = 0; (*cl)->var->current_val.l[n]; n++)
	  ;				/* sum the list */

	newl = (char **) fs_get((n + 1) * sizeof(char *));
	newl[n] = NULL;
	for(n = 0; (*cl)->var->current_val.l[n]; n++)
	  newl[n] = (n == new_ctxt->var.i)
		      ? raw_ctxt
		      : cpystr((*cl)->var->current_val.l[n]);

	/* and write it */
	n = set_variable_list((*cl)->var - ps->vars, newl, TRUE);
	free_list_array(&newl);
	if(n){
	    set_current_val((*cl)->var, TRUE, FALSE);
	    q_status_message(SM_ORDER|SM_DING, 3, 3,
			     "Error saving renamed context");
	    return;
	}

	set_current_val((*cl)->var, TRUE, FALSE);

	q_status_message(SM_ORDER, 0, 3, "Collection list entry updated");
    }
}


void
context_select_shuffle(ps, cl)
    struct pine  *ps;
    CONF_S	**cl;
{
    char      prompt[256], **newl, *p, *entry, *errstr = NULL;
    int	      n = 0, cmd;
    ESCKEY_S  ekey[3];
    CONF_S   *ctmp;

    if((*cl)->d.c.ct->prev
       && !((*cl)->d.c.ct->use & CNTXT_INCMNG)
       && !((*cl)->d.c.ct->prev->use & CNTXT_INCMNG)){
	/* enable UP */
	ekey[n].ch      = 'u';
	ekey[n].rval    = 'u';
	ekey[n].name    = "U";
	ekey[n++].label = "Up";
    }

    if((*cl)->d.c.ct->next && !((*cl)->d.c.ct->use & CNTXT_INCMNG)){
	ekey[n].ch      = 'd';
	ekey[n].rval    = 'd';
	ekey[n].name    = "D";
	ekey[n++].label = "Down";
    }

    if(n){
	sprintf(prompt, "Shuffle selected context %s%s%s? ",
		(ekey[0].ch == 'u') ?  "UP" : "",
		(n > 1) ? " or " : "",
		(ekey[0].ch == 'd' || n > 1) ? "DOWN" : "");
	ekey[n].ch = -1;

	if((cmd = radio_buttons(prompt, -FOOTER_ROWS(ps), ekey,
				(n == 1) ? 'd' : 0, 'x',
				NO_HELP, RB_NORM)) == 'x'){
	    cmd_cancelled("Shuffle");
	}
	else if((cmd == 'u' && (ctmp = context_select_prev(*cl)))
		|| (cmd == 'd' && (ctmp = context_select_next(*cl)))){
	    /* Prepare by counting current var's entries */
	    if((*cl)->var == ctmp->var){
		/*
		 * Swap within same variable.
		 * First, Update pinerc with new collection order.
		 */
		for(n = 0; ctmp->var->current_val.l[n]; n++)
		  ;				/* sum the list */

		newl = (char **) fs_get((n + 1) * sizeof(char *));
		newl[n--] = NULL;
		do
		  newl[n] = cpystr(ctmp->var->current_val.l[n]);
		while(n--);

		/* Update order */
		p = newl[(*cl)->d.c.ct->var.i];
		newl[(*cl)->d.c.ct->var.i] = newl[ctmp->d.c.ct->var.i];
		newl[ctmp->d.c.ct->var.i] = p;

		/* and write it */
		n = set_variable_list(ctmp->var - ps->vars, newl, TRUE);
		free_list_array(&newl);
		if(n){
		    set_current_val(ctmp->var, TRUE, FALSE);
		    errstr = "Error saving reordered context";
		}
		else{
		    set_current_val(ctmp->var, TRUE, FALSE);
		    /* Fix up indexes */
		    if(cmd == 'u'){
			ctmp->d.c.ct->var.i++;
			(*cl)->d.c.ct->var.i--;
		    }
		    else{
			(*cl)->d.c.ct->var.i++;
			ctmp->d.c.ct->var.i--;
		    }
		}
	    }
	    else if(cmd == 'd'){
		if(NEWS_TEST((*cl)->d.c.ct)){
		    /* Remove (*cl)'s var entry  */
		    /* Insert it after ctmp's var entry */
		    if(css_var_delete(ps, (*cl)->d.c.ct, &entry)){
			(*cl)->d.c.ct->var.v = (*cl)->var
							 = ctmp->d.c.ct->var.v;
			(*cl)->d.c.ct->var.i = ctmp->d.c.ct->var.i + 1;
			if(!css_var_insert(ps, (*cl)->d.c.ct, entry))
			  errstr = "Error saving reordered context";
		    }
		    else
		      errstr = "Error saving reordered context";
		}
		else{
		    /* Remove ctmp's var entry  */
		    /* Insert it before (*cl)'s var entry */
		    if(css_var_delete(ps, ctmp->d.c.ct, &entry)){
			ctmp->d.c.ct->var.v = ctmp->var = (*cl)->d.c.ct->var.v;
			ctmp->d.c.ct->var.i = (*cl)->d.c.ct->var.i;
			if(!css_var_insert(ps, ctmp->d.c.ct, entry))
			  errstr = "Error saving reordered context";
		    }
		    else
		      errstr = "Error saving reordered context";
		}
	    }
	    else{
		/* Remove (*cl)'s var entry */
		/* Insert it before ctmp's entry */
		if(css_var_delete(ps, (*cl)->d.c.ct, &entry)){
		    (*cl)->d.c.ct->var.v = (*cl)->var = ctmp->d.c.ct->var.v;
		    (*cl)->d.c.ct->var.i = ctmp->d.c.ct->var.i;
		    if(!css_var_insert(ps, (*cl)->d.c.ct, entry))
		      errstr = "Error saving reordered context";
		}
		else
		  errstr = "Error saving reordered context";
	    }

	    if(errstr){
		q_status_message(SM_ORDER|SM_DING, 3, 3, errstr);
		return;
	    }

	    if(cmd == 'u')
	      context_select_swap(ctmp, *cl);
	    else
	      context_select_swap(*cl, ctmp);

	    /* and lastly make sure the user see it */
	    ps->mangled_body = 1;
	}
    }
    else
      q_status_message(SM_ORDER, 0, 3, "Sorry, nothing to Shuffle");
}


/* swap a with b */
void
context_select_swap(a, b)
    CONF_S *a, *b;
{
    /* first, swap collection list members */
    if(b->prev = a->prev)
      a->prev->next = b;

    a->prev = b->headingp;
    if(a->headingp->next = b->headingp->next)
      b->headingp->next->prev = a->headingp;
    
    b->headingp->next = a;

    /* and make sure it's reflected in the associated context list */
    if(b->d.c.ct->prev = a->d.c.ct->prev)
      a->d.c.ct->prev->next = b->d.c.ct;

    a->d.c.ct->prev = b->d.c.ct;
    if(a->d.c.ct->next = b->d.c.ct->next)
      b->d.c.ct->next->prev = a->d.c.ct;

    b->d.c.ct->next = a->d.c.ct;

    if(ps_global->context_list == a->d.c.ct)
      ps_global->context_list = b->d.c.ct;

    if((a->d.c.ct->use & CNTXT_SAVEDFLT) && !NEWS_TEST(b->d.c.ct))
      context_select_swap_special(a->d.c.ct, b->d.c.ct);
}


int
css_var_delete(ps, ctxt, entry)
    struct pine	 *ps;
    CONTEXT_S	 *ctxt;
    char	**entry;
{
    int		count, n, offset;
    char      **newl;
    CONTEXT_S  *c;

    if(entry)				/* remember entry if requested */
      *entry = cpystr(ctxt->var.v->current_val.l[ctxt->var.i]);

    for(count = 0; ctxt->var.v->current_val.l[count]; count++)
      ;				/* sum the list */

    if(count > 1){
	newl = (char **) fs_get(count * sizeof(char *));
	newl[count - 1] = NULL;
	for(n = offset = 0; ctxt->var.v->current_val.l[n]; n++)
	  if(n == ctxt->var.i)
	    offset = 1;
	  else
	    newl[n - offset] = cpystr(ctxt->var.v->current_val.l[n]);

	n = set_variable_list(ctxt->var.v - ps->vars, newl, TRUE);
	free_list_array(&newl);
    }
    else
      n = set_variable_list(ctxt->var.v - ps->vars, NULL, TRUE);

    if(n){
	set_current_val(ctxt->var.v, TRUE, FALSE);
	return(0);
    }

    set_current_val(ctxt->var.v, TRUE, FALSE);

    /* straighten out var.i's */
    for(c = ctxt->next; c && c->var.v == ctxt->var.v; c = c->next)
      c->var.i--;

    return(1);
}


void
context_select_swap_special(a, b)
    CONTEXT_S *a, *b;
{
    FOLDER_S *old_f, *new_f;

    a->use &= ~CNTXT_SAVEDFLT;
    b->use |= CNTXT_SAVEDFLT;

    if((old_f = folder_entry(0, FOLDERS(a)))
       && old_f->nickname && !strcmp(old_f->nickname, ps_global->inbox_name)){

	new_f = new_folder(old_f->name);
	new_f->nickname = cpystr(old_f->nickname);
	new_f->name_len = old_f->name_len;

	folder_delete(0, FOLDERS(a));
	folder_insert(0, new_f, FOLDERS(b));
    }
}



int
css_var_insert(ps, ctxt, entry)
    struct pine	*ps;
    CONTEXT_S	*ctxt;
    char	*entry;
{
    int		count, n, offset;
    char      **newl;
    CONTEXT_S  *c;

    for(count = 0; ctxt->var.v->current_val.l[count]; count++)
      ;				/* sum the list */

    newl = (char **) fs_get((count + 2)* sizeof(char *));
    newl[ctxt->var.i] = entry;
    newl[count + 1]   = NULL;
    for(n = offset = 0; ctxt->var.v->current_val.l[n]; n++){
	if(n == ctxt->var.i)
	  offset = 1;

	newl[n + offset] = cpystr(ctxt->var.v->current_val.l[n]);
    }

    n = set_variable_list(ctxt->var.v - ps->vars, newl, TRUE);
    free_list_array(&newl);
    if(n){
	set_current_val(ctxt->var.v, TRUE, FALSE);
	return(0);
    }

    set_current_val(ctxt->var.v, TRUE, FALSE);

    /* straighten out var.i's */
    for(c = ps->context_list; c; c = c->next)
      if(c != ctxt && c->var.v == ctxt->var.v && c->var.i >= ctxt->var.i)
	c->var.i++;

    return(1);
}


CONF_S *
context_select_prev(c)
  CONF_S *c;
{
    for(c = c->prev; c; c = c->prev)
      if(c->d.c.ct)		/* valid context pointer! */
	return(c);

    return(NULL);
}


/* return start of next context */
CONF_S *
context_select_next(c)
  CONF_S *c;
{
    for(c = c->next; c; c = c->next)
      if(c->d.c.ct)
	return(c);			/* start of next collection */

    return(NULL);
}



/*
 * Manage display of the config/options menu body.
 */
void
update_option_screen(ps, screen, cursor_pos)
    struct pine  *ps;
    OPT_SCREEN_S *screen;
    Pos          *cursor_pos;
{
    int		   dline, last_selectable, save;
    CONF_S	  *top_line, *ctmp;

#ifdef _WINDOWS
    mswin_beginupdate();
#endif
    if(cursor_pos){
	cursor_pos->col = 0;
	cursor_pos->row = -1;		/* to tell us if we've set it yet */
    }

    /*
     * calculate top line of display for reframing if the current field
     * is off the display defined by screen->top_line...
     */
    if(ctmp = screen->top_line)
      for(dline = BODY_LINES(ps);
	  dline && ctmp && ctmp != screen->current;
	  ctmp = next_confline(ctmp), dline--)
	;

    if(!ctmp || !dline){		/* force reframing */
	dline = 0;
	ctmp = top_line = first_confline(screen->current);
	do
	  if(((dline++)%BODY_LINES(ps)) == 0)
	    top_line = ctmp;
	while(ctmp != screen->current && (ctmp = next_confline(ctmp)));
    }
    else
      top_line = screen->top_line;

#ifdef _WINDOWS
    /*
     * Figure out how far down the top line is from the top and how many
     * total lines there are.  Dumb to loop every time thru, but
     * there aren't that many lines, and it's cheaper than rewriting things
     * to maintain a line count in each structure...
     */
    for(dline = 0, ctmp = prev_confline(top_line); ctmp; ctmp = prev_confline(ctmp))
      dline++;

    scroll_setpos(dline);
    last_selectable = dline;
    for(ctmp = next_confline(top_line); ctmp ; ctmp = next_confline(ctmp)){
      dline++;
      if (!(ctmp->flags & CF_NOSELECT))
	last_selectable = dline;
    }
    dline = last_selectable;
    scroll_setrange(BODY_LINES(ps), dline);
#endif

    /* mangled body or new page, force redraw */
    if(ps->mangled_body || screen->top_line != top_line)
      screen->prev = NULL;

    /* loop thru painting what's needed */
    for(dline = 0, ctmp = top_line;
	dline < BODY_LINES(ps);
	dline++, ctmp = next_confline(ctmp)){

	/*
	 * only fall thru painting if something needs painting...
	 */
	if(!(!screen->prev || ctmp == screen->prev || ctmp == screen->current
	     || ctmp == screen->prev->varnamep
	     || ctmp == screen->current->varnamep
	     || ctmp == screen->prev->headingp
	     || ctmp == screen->current->headingp))
	  continue;

	ClearLine(dline + HEADER_ROWS(ps));

	if(ctmp){
	    if(ctmp->flags & CF_B_LINE)
	      continue;

	    if(ctmp->varname && !(ctmp->flags & CF_INVISIBLEVAR)){
		if(ctmp == screen->current && cursor_pos)
		  cursor_pos->row  = dline + HEADER_ROWS(ps);

		if((ctmp == screen->current
		    || ctmp == screen->current->varnamep
		    || ctmp == screen->current->headingp)
		   && !(ctmp->flags & CF_NOHILITE))
		  StartInverse();

		if(ctmp->flags & CF_H_LINE){
		    MoveCursor(dline + HEADER_ROWS(ps), 0);
		    Write_to_screen(repeat_char(ps->ttyo->screen_cols, '-'));
		}

		if(ctmp->flags & CF_CENTERED){
		    int offset = ps->ttyo->screen_cols/2
				  - (strlen(ctmp->varname)/2);
		    MoveCursor(dline + HEADER_ROWS(ps),
			       (offset > 0) ? offset : 0);
		}
		else if(ctmp->varoffset)
		  MoveCursor(dline+HEADER_ROWS(ps), ctmp->varoffset);

		Write_to_screen(ctmp->varname);
		if((ctmp == screen->current
		    || ctmp == screen->current->varnamep
		    || ctmp == screen->current->headingp)
		   && !(ctmp->flags & CF_NOHILITE))
		  EndInverse();
	    }

	    if(ctmp->value){
		char *p;
		int   i, j;

		memset(tmp_20k_buf, '\0',
		       (ps->ttyo->screen_cols + 1) * sizeof(char));
		if(ctmp == screen->current){
		    if(!(ctmp->flags & CF_DOUBLEVAR && ctmp->flags & CF_VAR2))
		      StartInverse();

		    if(cursor_pos)
		      cursor_pos->row  = dline + HEADER_ROWS(ps);
		}

		if(ctmp->flags & CF_H_LINE)
		  memset(tmp_20k_buf, '-',
			 ps->ttyo->screen_cols * sizeof(char));

		if(ctmp->flags & CF_CENTERED){
		    int offset = ps->ttyo->screen_cols/2
				  - (strlen(ctmp->value)/2);
		    /* BUG: tabs screw us figuring length above */
		    if(offset > 0){
			char *q;

			p = tmp_20k_buf + offset;
			if(!*(q = tmp_20k_buf))
			  while(q < p)
			    *q++ = ' ';
		    }
		}
		else
		  p = tmp_20k_buf;

		/*
		 * Copy the value to a temp buffer expanding tabs, and
		 * making sure not to write beyond screen right...
		 */
		for(i = 0, j = ctmp->valoffset;
		    ctmp->value[i] && j < ps->ttyo->screen_cols;
		    i++){
		    if(ctmp->value[i] == ctrl('I')){
			do
			  *p++ = ' ';
			while(j < ps_global->ttyo->screen_cols
			      && ((++j) & 0x07));
		    }
		    else{
			*p++ = ctmp->value[i];
			j++;
		    }
		}

		if(ctmp == screen->current && cursor_pos){
		    if(ctmp->flags & CF_DOUBLEVAR && ctmp->flags & CF_VAR2)
		      cursor_pos->col = ctmp->val2offset;
		    else
		      cursor_pos->col = ctmp->valoffset;

		    if(ctmp->tool == radiobutton_tool
#ifdef	ENABLE_LDAP
		       || ctmp->tool==ldap_radiobutton_tool
#endif
		       || ctmp->tool==role_radiobutton_tool
		       || ctmp->tool==color_setting_tool
		       || ctmp->tool==checkbox_tool)
		      cursor_pos->col++;
		}

		if(ctmp->flags & CF_DOUBLEVAR){
		    long l;

		    p = tmp_20k_buf;
		    if((l=strlen(p)) > ctmp->val2offset - ctmp->valoffset
				       - SPACE_BETWEEN_DOUBLEVARS &&
		       ctmp->val2offset - ctmp->valoffset
			- SPACE_BETWEEN_DOUBLEVARS >= 0){
			save = p[ctmp->val2offset - ctmp->valoffset
			         - SPACE_BETWEEN_DOUBLEVARS];
			p[ctmp->val2offset - ctmp->valoffset
			  - SPACE_BETWEEN_DOUBLEVARS] = '\0';
		    }
		    else
		      save = '\0';

		    /*
		     * If this is a COLOR_BLOB line we do special coloring.
		     * The current object inverse hilite is only on the
		     * checkbox part, the exact format comes from the
		     * new_color_line function. If we change that we'll have
		     * to change this to get the coloring right.
		     */
		    if(p[0] == '(' && p[2] == ')' &&
		       p[3] == ' ' && p[4] == ' ' &&
		       !strncmp(p+5, COLOR_BLOB, COLOR_BLOB_LEN)){
			COLOR_PAIR  *lastc = NULL, *newc = NULL;

			MoveCursor(dline+HEADER_ROWS(ps), ctmp->valoffset);
			Write_to_screen_n(p, 3);
			if(!(ctmp->flags & CF_VAR2) && ctmp == screen->current)
			  EndInverse();

			Write_to_screen_n(p+3, 3);
			newc = new_color_pair(colorx(CFC_ICOLOR(ctmp)),
					      colorx(CFC_ICOLOR(ctmp)));
			if(newc){
			    lastc = pico_get_cur_color();
			    (void)pico_set_colorp(newc, PSC_NONE);
			    free_color_pair(&newc);
			}

			Write_to_screen_n(p+6, COLOR_BLOB_LEN-2);

			if(lastc){
			    (void)pico_set_colorp(lastc, PSC_NONE);
			    free_color_pair(&lastc);
			}

			Write_to_screen(p+6+COLOR_BLOB_LEN-2);
		    }
		    else{
			PutLine0(dline+HEADER_ROWS(ps), ctmp->valoffset, p);
			if(!(ctmp->flags & CF_VAR2) && ctmp == screen->current)
			  EndInverse();
		    }

		    if(save)
		      p[ctmp->val2offset - ctmp->valoffset
			- SPACE_BETWEEN_DOUBLEVARS] = save;

		    PutLine0(dline+HEADER_ROWS(ps),
			     ctmp->val2offset - SPACE_BETWEEN_DOUBLEVARS,
			     repeat_char(SPACE_BETWEEN_DOUBLEVARS, SPACE));

		    if(l > ctmp->val2offset - ctmp->valoffset &&
		           ctmp->val2offset - ctmp->valoffset >= 0)
		      p += (ctmp->val2offset - ctmp->valoffset);

		    if(p > tmp_20k_buf){
			if(ctmp->flags & CF_VAR2 && ctmp == screen->current)
			  StartInverse();

			if(p[0] == '(' && p[2] == ')' &&
			   p[3] == ' ' && p[4] == ' ' &&
			   !strncmp(p+5, COLOR_BLOB, COLOR_BLOB_LEN)){
			    COLOR_PAIR  *lastc = NULL, *newc = NULL;

			    MoveCursor(dline+HEADER_ROWS(ps), ctmp->val2offset);
			    Write_to_screen_n(p, 3);
			    if(ctmp->flags & CF_VAR2 && ctmp == screen->current)
			      EndInverse();

			    Write_to_screen_n(p+3, 3);
			    newc = new_color_pair(colorx(CFC_ICOLOR(ctmp)),
						  colorx(CFC_ICOLOR(ctmp)));
			    if(newc){
				lastc = pico_get_cur_color();
				(void)pico_set_colorp(newc, PSC_NONE);
				free_color_pair(&newc);
			    }

			    Write_to_screen_n(p+6, COLOR_BLOB_LEN-2);

			    if(lastc){
				(void)pico_set_colorp(lastc, PSC_NONE);
				free_color_pair(&lastc);
			    }

			    Write_to_screen(p+6+COLOR_BLOB_LEN-2);
			}
			else{
			    PutLine0(dline+HEADER_ROWS(ps),ctmp->val2offset,p);
			    if(ctmp->flags & CF_VAR2 && ctmp == screen->current)
			      EndInverse();
			}
		    }
		}
		else{
		    char *q, *first_space, *sample;

		    if(ctmp->flags & CF_COLORSAMPLE &&
		       pico_usingcolor() &&
		       ((q = strstr(tmp_20k_buf, SAMPLE_LEADER)) ||
			(q = strstr(tmp_20k_buf, "Color"))) &&
		       (first_space = strindex(q, SPACE)) &&
		       strstr(ctmp->value, SAMPLE)){
			/* write out first part */
			*first_space = '\0';
			PutLine0(dline+HEADER_ROWS(ps), ctmp->valoffset,
				 tmp_20k_buf);
			*first_space = SPACE;

			/*
			 * Then the spaces between there and sample.
			 * Have to take it out of inverse mode for spaces.
			 */
			if(ctmp == screen->current)
			  EndInverse();

			sample = skip_white_space(first_space);
			/* if there's enough room to put some sample up */
			if(ctmp->valoffset + sample - tmp_20k_buf <=
							ps->ttyo->screen_cols){
			    save = *sample;
			    *sample = '\0';
			    PutLine0(dline+HEADER_ROWS(ps),
				     ctmp->valoffset+first_space-tmp_20k_buf,
				     first_space);
			    *sample = save;

			    /* then the color sample */
			    if(ctmp->var == &ps->vars[V_VIEW_HDR_COLORS]){
				COLOR_PAIR  *lastc = NULL, *newc = NULL;
				HDR_COLOR_S *hc;

				/*
				 * look for the header in the varname field
				 * of the title for this color.
				 */
				for(hc = ps->hdr_colors, i=0; hc;
				    hc = hc->next, i++)
				  if(CFC_ICUST(ctmp) == i)
				    break;

				if(hc && hc->fg && hc->fg[0] && hc->bg &&
				   hc->bg[0])
				  newc = new_color_pair(hc->fg, hc->bg);

				if(newc){
				    lastc = pico_get_cur_color();
				    (void)pico_set_colorp(newc, PSC_NONE);
				    free_color_pair(&newc);
				}

				save = tmp_20k_buf[ps->ttyo->screen_cols -
						   ctmp->valoffset];
				tmp_20k_buf[ps->ttyo->screen_cols -
					    ctmp->valoffset] = '\0';
				PutLine0(dline+HEADER_ROWS(ps),
					 ctmp->valoffset + sample - tmp_20k_buf,
					 sample);
				tmp_20k_buf[ps->ttyo->screen_cols -
					    ctmp->valoffset] = save;
				if(lastc){
				    (void)pico_set_colorp(lastc, PSC_NONE);
				    free_color_pair(&lastc);
				}
			    }
			    else{
				COLOR_PAIR *lastc = NULL, *newc = NULL;
				int         invert = 0;

				if(ctmp->var->current_val.p &&
				   (ctmp->var+1)->current_val.p &&
				   (lastc = pico_get_cur_color())){
				    newc =
				       new_color_pair(ctmp->var->current_val.p,
						  (ctmp->var+1)->current_val.p);
				    (void)pico_set_colorp(newc, PSC_NONE);
				    free_color_pair(&newc);
				}
				else if(var_defaults_to_rev(ctmp->var)){
				    if(newc = pico_get_rev_color()){
					/*
					 * Note, don't have to free newc.
					 */
					if(lastc = pico_get_cur_color())
					  (void)pico_set_colorp(newc, PSC_NONE);
				    }
				    else{
					StartInverse();
					invert = 1;
				    }
				}

				if(ctmp->var==&ps->vars[V_SLCTBL_FORE_COLOR] &&

				   (F_OFF(F_SLCTBL_ITEM_NOBOLD, ps) ||
				    !(ctmp->var->current_val.p &&
				      (ctmp->var+1)->current_val.p)))
				  StartBold();

				save = tmp_20k_buf[ps->ttyo->screen_cols -
						   ctmp->valoffset];
				tmp_20k_buf[ps->ttyo->screen_cols -
					    ctmp->valoffset] = '\0';
				PutLine0(dline+HEADER_ROWS(ps),
					 ctmp->valoffset + sample - tmp_20k_buf,
					 sample);
				tmp_20k_buf[ps->ttyo->screen_cols -
					    ctmp->valoffset] = save;

				if(ctmp->var==&ps->vars[V_SLCTBL_FORE_COLOR] &&

				   (F_OFF(F_SLCTBL_ITEM_NOBOLD, ps) ||
				    !(ctmp->var->current_val.p &&
				      (ctmp->var+1)->current_val.p)))
				  EndBold();

				if(lastc){
				    (void)pico_set_colorp(lastc, PSC_NONE);
				    free_color_pair(&lastc);
				}
				else if(invert)
				  EndInverse();
			    }
			}
		    }
		    else{
			PutLine0(dline+HEADER_ROWS(ps), ctmp->valoffset,
				 tmp_20k_buf);
			if(ctmp == screen->current)
			  EndInverse();
		    }
		}
	    }
	}
    }

    ps->mangled_body = 0;
    screen->top_line = top_line;
    screen->prev     = screen->current;
#ifdef _WINDOWS
    mswin_endupdate();
#endif
}



/*
 * 
 */
void
print_option_screen(screen, prompt)
    OPT_SCREEN_S *screen;
    char *prompt;
{
    CONF_S *ctmp;
    int     so_far;
    char    line[500];

    if(open_printer(prompt) == 0){
	for(ctmp = first_confline(screen->current);
	    ctmp;
	    ctmp = next_confline(ctmp)){

	    so_far = 0;
	    if(ctmp->varname && !(ctmp->flags & CF_INVISIBLEVAR)){

		sprintf(line, "%*s%s", ctmp->varoffset, "", ctmp->varname);
		print_text(line);
		so_far = ctmp->varoffset + strlen(ctmp->varname);
	    }

	    if(ctmp && ctmp->value){
		char *p = tmp_20k_buf;
		int   i, j, spaces;

		/* Copy the value to a temp buffer expanding tabs. */
		for(i = 0, j = ctmp->valoffset; ctmp->value[i]; i++){
		    if(ctmp->value[i] == ctrl('I')){
			do
			  *p++ = ' ';
			while((++j) & 0x07);
			      
		    }
		    else{
			*p++ = ctmp->value[i];
			j++;
		    }
		}

		*p = '\0';
		removing_trailing_white_space(tmp_20k_buf);

		spaces = max(ctmp->valoffset - so_far, 0);
		sprintf(line, "%*s%s\n", spaces, "", tmp_20k_buf);
		print_text(line);
	    }
	}

	close_printer();
    }
}



/*
 *
 */
void
option_screen_redrawer()
{
    ps_global->mangled_body = 1;
    update_option_screen(ps_global, opt_screen, (Pos *)NULL);
}



/*
 * pretty_value - given the variable and, if list, member, return an
 *                alloc'd string containing var's value...
 */
char *
pretty_value(ps, cl)
    struct pine *ps;
    CONF_S      *cl;
{
    char tmp[MAXPATH];
    int  user_val, safe_width;

    if(cl->var->is_list){
	if(!cl->var->is_fixed && cl->var->user_val.l){
	    sprintf(tmp, "%-*s", ps->ttyo->screen_cols - cl->valoffset,
		    (cl->var->user_val.l[cl->varmem] &&
		     *cl->var->user_val.l[cl->varmem])
		       ? cl->var->user_val.l[cl->varmem]
		       : empty_val2);
	}
	else{
	    char *p = tmp;
	    *p++ = '<';
	    *p   = '\0';
	    sstrcpy(&p, cl->var->is_fixed ? fixed_val : no_val);
	    if(cl->var->current_val.l){
		int i, l, l2;

		sstrcpy(&p, ": using \"");
		for(i = 0; cl->var->current_val.l[i]; i++){
		    if(i)
		      *p++ = ',';

		    if((l=ps->ttyo->screen_cols-cl->valoffset-(p-tmp)-2) > 0){
			strncpy(p, cl->var->current_val.l[i], l);
			if(l < (l2 = strlen(cl->var->current_val.l[i]))){
			    strncpy(p += (l - 4), " ...", 4);
			    p += 4;
			    break;
			}
			else
			  p += l2;
		    }
		    else
		      break;
		}

		*p++ = '\"';
	    }

	    sprintf(p, ">%*s", max(0, ps->ttyo->screen_cols - cl->valoffset
								 - (p - tmp)),
		    "");
	}

    }
    else if(cl->var->is_fixed || !cl->var->user_val.p){
	sprintf(tmp, cl->var->is_fixed
			? "<%s%s%s%s>%*s" : "<%s%s%s%s>%*s", 
		cl->var->is_fixed ? fixed_val : no_val,
		(cl->var->current_val.p) ? ": using \"" : "",
		(cl->var->current_val.p) ? cl->var->current_val.p : "",
		(cl->var->current_val.p) ? "\"" : "",
		max(0, ps->ttyo->screen_cols - cl->valoffset - 13
				  - ((cl->var->current_val.p) ? 9 : 0)
				  - ((cl->var->current_val.p)
				       ? strlen(cl->var->current_val.p) : 0)
				  - ((cl->var->current_val.p) ? 1 : 0)),
		"");
    }
    else{
      int right_margin = 6;

      if(cl->var->user_val.p && *cl->var->user_val.p)
 	user_val = strlen(cl->var->user_val.p);
      else
 	user_val = 0;
      safe_width = ps->ttyo->screen_cols - cl->valoffset - right_margin;
      sprintf(tmp, "%-*.*s%-*s", safe_width, safe_width,
 	      user_val ? cl->var->user_val.p : empty_val2, right_margin,
 	      (user_val > ps->ttyo->screen_cols - cl->valoffset - 1) 
	      ? "..." 
	      :  (user_val > safe_width) ? cl->var->user_val.p + safe_width
	                                 : "");
    }
    return(cpystr(tmp));
}


/*
 * test_feature - runs thru a feature list, and returns:
 *                 1 if feature explicitly set and matches 'v'
 *                 0 if feature not explicitly set *or* doesn't match 'v'
 */
int
test_feature(l, f, g, v)
    char **l;
    char  *f;
    int    g, v;
{
    char *p;
    int   rv = 0, forced_off;

    for(; l && *l; l++){
	p = (forced_off = !struncmp(*l, "no-", 3)) ? *l + 3 : *l;
	if(!strucmp(p, f))
	  rv = (v == !forced_off);
	else if(g && !strucmp(p, "old-growth"))
	  rv = (v == forced_off);
    }

    return(rv);
}


void
clear_feature(l, f)
    char ***l;
    char   *f;
{
    char **list = l ? *l : NULL;
    int    count = 0;

    for(; list && *list; list++, count++){
	if(f && !strucmp(((!struncmp(*list,"no-",3)) ? *list + 3 : *list), f)){
	    fs_give((void **)list);
	    f = NULL;
	}

	if(!f)					/* shift  */
	  *list = *(list + 1);
    }

    /*
     * this is helpful to keep the array from growing if a feature
     * get's set and unset repeatedly
     */
    if(!f)
      fs_resize((void **)l, count * sizeof(char *));
}


void
set_feature(l, f, v)
    char ***l;
    char   *f;
    int     v;
{
    char **list = l ? *l : NULL, newval[256];
    int    count = 0;

    sprintf(newval, "%s%s", v ? "" : "no-", f);
    for(; list && *list; list++, count++)
      if((**list == '\0')                       /* anything can replace an empty value */
	 || !strucmp(((!struncmp(*list, "no-", 3)) ? *list + 3 : *list), f)){
	  fs_give((void **)list);		/* replace with new value */
	  *list = cpystr(newval);
	  return;
      }

    /*
     * if we got here, we didn't find it in the list, so grow the list
     * and add it..
     */
    if(!*l)
      *l = (char **)fs_get((count + 2) * sizeof(char *));
    else
      fs_resize((void **)l, (count + 2) * sizeof(char *));

    (*l)[count]     = cpystr(newval);
    (*l)[count + 1] = NULL;
}


/*
 * feature_replaces_obsolete - Check to see if this feature replaces
 *			       an obsolete variable.
 */
int
feature_replaces_obsolete(v)
    int v;
{
    return((v == F_INCLUDE_HEADER) || (v == F_SIG_AT_BOTTOM));
}


/*
 *
 */
void
toggle_feature_bit(ps, index, var, value)
    struct pine     *ps;
    int		     index;
    struct variable *var;
    char            *value;
{
    FEATURE_S  *f;
    char      **vp, *p;
    int		og;

    f  = feature_list(index);
    og = test_old_growth_bits(ps, f->id);

    /*
     * if this feature is in the fixed set, or old-growth is in the fixed
     * set and this feature is in the old-growth set, don't alter it...
     */
    for(vp = var->fixed_val.l; vp && *vp; vp++){
	p = (struncmp(*vp, "no-", 3)) ? *vp : *vp + 3;
	if(!strucmp(p, f->name) || (og && !strucmp(p, "old-growth"))){
	    q_status_message(SM_ORDER, 3, 3,
			     "Can't change value fixed by sys-admin.");
	    return;
	}
    }

    F_SET(f->id, ps, !F_ON(f->id, ps));		/* flip the bit */
    if(value)
      value[1] = F_ON(f->id, ps) ? 'X' : ' ';

    /*
     * fix up the user's feature list based on global and current
     * settings..
     *
     * Note, we only care if "old-growth" is set or not in as much as
     * we don't want to add redundant feature entries.  we won't add or 
     * remove "old-growth" in that the set it defines may change in the
     * future...
     */
    if((test_feature(var->global_val.l, f->name, og, F_ON(f->id, ps))
	|| test_feature(var->user_val.l, f->name, og, !F_ON(f->id, ps)))
       && !feature_replaces_obsolete(f->id))
      clear_feature(&var->user_val.l, f->name);
    else
      set_feature(&var->user_val.l, f->name, F_ON(f->id, ps));

    /*
     * Handle any features that need special attention here...
     */
    switch(f->id){
      case F_QUOTE_ALL_FROMS :
	mail_parameters(NULL,SET_FROMWIDGET,(void *)(F_ON(f->id ,ps) ? 1 : 0));
	break;

      case F_CMBND_ABOOK_DISP :
	addrbook_reset();
	break;

      case F_QUELL_LOCK_FAILURE_MSGS :
	mail_parameters(NULL, SET_LOCKEACCESERROR,
			(void *)(F_ON(f->id, ps) ? 1 : 0));
	break;

      case F_QUELL_INTERNAL_MSG :
	mail_parameters(NULL,SET_USERHASNOLIFE,
			(void *)(F_ON(f->id, ps) ? 1 : 0));
	break;

      case F_ENABLE_INCOMING :
	  q_status_message(SM_ORDER | SM_DING, 3, 4,
	       "Folder List changes will take effect your next pine session.");

	break;

      case F_PRESERVE_START_STOP :
	/* toggle raw mode settings to make tty driver aware of new setting */
	PineRaw(0);
	PineRaw(1);
	break;

      case F_USE_FK :
	ps->orig_use_fkeys = F_ON(F_USE_FK, ps);
	ps->mangled_footer = 1;
	mark_keymenu_dirty();
	break;

      case F_BLANK_KEYMENU :
	clearfooter(ps);
	if(F_ON(f->id, ps)){
	    FOOTER_ROWS(ps) = 1;
	    ps->mangled_body = 1;
	}
	else{
	    FOOTER_ROWS(ps) = 3;
	    ps->mangled_footer = 1;
	}

	break;

#ifdef	_WINDOWS
      case F_SHOW_CURSOR :
	mswin_showcaret(F_ON(f->id,ps));
	break;

      case F_ENABLE_TRAYICON :
	mswin_trayicon(F_ON(f->id,ps));
	break;

#endif
#if !defined(DOS) && !defined(OS2)
      case F_ALLOW_TALK :
	if(F_ON(f->id, ps))
	  allow_talk(ps);
	else
	  disallow_talk(ps);

	break;
#endif
#ifdef	MOUSE
      case F_ENABLE_MOUSE :
	if(F_ON(f->id, ps))
	  init_mouse();
	else
	  end_mouse();

	break;
#endif

      default :
	break;
    }
}


/*
 * new_confline - create new CONF_S zero it out, and insert it after current.
 *                NOTE current gets set to the new CONF_S too!
 */
CONF_S *
new_confline(current)
    CONF_S **current;
{
    CONF_S *p;

    p = (CONF_S *)fs_get(sizeof(CONF_S));
    memset((void *)p, 0, sizeof(CONF_S));
    if(current){
	if(*current){
	    p->next	     = (*current)->next;
	    (*current)->next = p;
	    p->prev	     = *current;
	    if(p->next)
	      p->next->prev = p;
	}

	*current = p;
    }

    return(p);
}


/*
 *
 */
void
snip_confline(p)
    CONF_S **p;
{
    if(*p){
	/* Yank it from the linked list */
	if((*p)->prev)
	  (*p)->prev->next = (*p)->next;

	if((*p)->next)
	  (*p)->next->prev = (*p)->prev;

	/* Then free up it's memory */
	(*p)->prev = (*p)->next = NULL;
	free_conflines(p);
    }
}


/*
 *
 */
void
free_conflines(p)
    CONF_S **p;
{
    if(*p){
	free_conflines(&(*p)->next);

	if((*p)->varname)
	  fs_give((void **) &(*p)->varname);

	if((*p)->value)
	  fs_give((void **) &(*p)->value);

	fs_give((void **) p);
    }
}


/*
 *
 */
CONF_S *
first_confline(p)
    CONF_S *p;
{
    while(p && p->prev)
      p = p->prev;

    return(p);
}


/*
 * First selectable confline.
 */
CONF_S *
first_sel_confline(p)
    CONF_S *p;
{
    for(p = first_confline(p); p && (p->flags&CF_NOSELECT); p=next_confline(p))
      ;/* do nothing */

    return(p);
}


/*
 *
 */
CONF_S *
last_confline(p)
    CONF_S *p;
{
    while(p && p->next)
      p = p->next;

    return(p);
}


/*
 *
 */
fixed_var(v, action, name)
    struct variable *v;
    char	    *action, *name;
{
    if(v && v->is_fixed){
	q_status_message2(SM_ORDER, 3, 3,
			  "Can't %s sys-admin defined %s.",
			  action ? action : "change", name ? name : "value");
	return(1);
    }

    return(0);
}


int
offer_to_fix_pinerc(ps)
    struct pine *ps;
{
    struct variable *v;
    char             prompt[300];
    char            *p, *q;
    char           **list;
    char           **list_fixed;
    int              rv = 0;
    int              i, k, need;
    char            *clear = ": delete it";

    ps->fix_fixed_warning = 0;  /* so we only ask first time */

    if(ps->readonly_pinerc)
      return(rv);

    set_titlebar("FIXING PINERC", ps->mail_stream,
		 ps->context_current,
		 ps->cur_folder, ps->msgmap, 1, FolderName, 0, 0);

    if(want_to("Some of your options conflict with site policy.  Investigate",
	'y', 'n', NO_HELP, WT_FLUSH_IN) != 'y')
      return(rv);
    
/* space want_to requires in addition to the string you pass in */
#define WANTTO_SPACE 6
    need = WANTTO_SPACE + strlen(clear);

    for(v = ps->vars; v->name; v++){
	if(!v->is_fixed ||
	   !v->is_user ||
	    v->is_obsolete ||
	    v == &ps->vars[V_FEATURE_LIST]) /* handle feature-list below */
	  continue;
	
	prompt[0] = '\0';
	
	if(v->is_list && v->user_val.l){
	    if(*v->user_val.l){
		sprintf(prompt, "Your setting for %s is ", v->name);
		p = prompt + strlen(prompt);
		for(i = 0; v->user_val.l[i]; i++){
		    if(p - prompt > ps->ttyo->screen_cols - need)
		      break;
		    if(i)
		      *p++ = ',';
		    sstrcpy(&p, v->user_val.l[i]);
		}
		*p = '\0';
	    }
	    else
	      sprintf(prompt, "Your setting for %s is %s", v->name, empty_val2);
	}
	else{
	    if(v->user_val.p){
		if(*v->user_val.p){
		    sprintf(prompt, "Your setting for %s is %s",
			v->name, v->user_val.p);
		}
		else{
		    sprintf(prompt, "Your setting for %s is %s",
			v->name, empty_val2);
		}
	    }
	}
	if(*prompt){
	    if(strlen(prompt) > ps->ttyo->screen_cols - need)
	      (void)strcpy(prompt + max(ps->ttyo->screen_cols - need - 3, 0),
			  "...");

	    (void)strcat(prompt, clear);
	    if(want_to(prompt, 'y', 'n', NO_HELP, WT_NORM) == 'y'){
		if(v->is_list){
		    if(v->user_val.l){
			rv++;
			for(i = 0; v->user_val.l[i]; i++)
			  fs_give((void **)&v->user_val.l[i]);

			fs_give((void **)&v->user_val.l);
		    }
		}
		else if(v->user_val.p){
		    rv++;
		    fs_give((void **)&v->user_val.p);
		}
	    }
	}
    }

    /*
     * As always, feature-list has to be handled separately.
     */
    v = &ps->vars[V_FEATURE_LIST];
    list = v->user_val.l;
    list_fixed = v->fixed_val.l;
    if(list){
      for(i = 0; list[i]; i++){
	p = list[i];
	if(!struncmp(p, "no-", 3))
	  p += 3;
	for(k = 0; list_fixed && list_fixed[k]; k++){
	  q = list_fixed[k];
	  if(!struncmp(q, "no-", 3))
	    q += 3;
	  if(!strucmp(q, p) && strucmp(list[i], list_fixed[k])){
	    sprintf(prompt, "Your %s is %s, fixed value is %s",
		p, p == list[i] ? "ON" : "OFF",
		q == list_fixed[k] ? "ON" : "OFF");

	    if(strlen(prompt) > ps->ttyo->screen_cols - need)
	      (void)strcpy(prompt + max(ps->ttyo->screen_cols - need - 3, 0),
			  "...");

	    (void)strcat(prompt, clear);
	    if(want_to(prompt, 'y', 'n', NO_HELP, WT_NORM) == 'y'){
		rv++;
		/*
		 * Clear the feature from the user's pinerc
		 * so that we'll stop bothering them when they
		 * start up Pine.
		 */
		clear_feature(&v->user_val.l, p);

		/*
		 * clear_feature scoots the list up, so if list[i] was
		 * the last one going in, now it is the end marker.  We
		 * just decrement i so that it will get incremented and
		 * then test == 0 in the for loop.  We could just goto
		 * outta_here to accomplish the same thing.
		 */
		if(!list[i])
		  i--;
	    }
	  }
	}
      }
    }

    return(rv);
}


/*
 * Compare saved user_val with current user_val to see if it changed.
 * If any have changed, change it back and take the appropriate action.
 */
void
revert_to_saved_config(ps, vsave)
struct pine *ps;
SAVED_CONFIG_S *vsave;
{
    struct variable *vreal;
    SAVED_CONFIG_S  *v;
    int i, n;

    v = vsave;
    for(vreal = ps->vars; vreal->name; vreal++,v++){
	if(!save_include(ps, vreal))
	  continue;
	
	if(vreal == &ps->vars[V_FEATURE_LIST]){
	    /* handle feature changes */
	    if(memcmp(v->user_val.features, ps->feature_list, BM_SIZE)){
		FEATURE_S *f;

		/*
		 * At least one feature was changed.  Go through the
		 * list of eligible features and toggle them back to what
		 * they were.
		 */

		for(i = 0; f = feature_list(i); i++){
		    if(feature_list_section(f)
			&& ((F_ON(f->id, ps)
			      && !bitnset(f->id, v->user_val.features))
			   ||
			   (F_OFF(f->id, ps)
			      && bitnset(f->id, v->user_val.features)))){

			toggle_feature_bit(ps, i, vreal, NULL);
		    }
		}
	    }
	}
	else{
	    int changed = 0;

	    if(vreal->is_list){
		if((v->user_val.l && !vreal->user_val.l)
		   || (!v->user_val.l && vreal->user_val.l))
		  changed++;
		else if(!v->user_val.l && !vreal->user_val.l)
		  ;/* no change, nothing to do */
		else
		  for(i = 0; v->user_val.l[i] || vreal->user_val.l[i]; i++)
		    if((v->user_val.l[i]
			  && (!vreal->user_val.l[i]
			     || strcmp(v->user_val.l[i], vreal->user_val.l[i])))
		       ||
			 (!v->user_val.l[i] && vreal->user_val.l[i])){
			changed++;
			break;
		    }
		
		if(changed){
		    char **list;

		    /* free the changed value */
		    if(vreal->user_val.l){
			for(i = 0; vreal->user_val.l[i]; i++)
			  fs_give((void **)&vreal->user_val.l[i]);
			
			fs_give((void **)&vreal->user_val.l);
		    }

		    /* copy back the original one */
		    if(v->user_val.l){
			list = v->user_val.l;
			n = 0;
			/* count how many */
			while(list[n])
			  n++;

			vreal->user_val.l
				= (char **)fs_get((n+1) * sizeof(char *));
			for(i = 0; i < n; i++)
			  vreal->user_val.l[i] = cpystr(v->user_val.l[i]);

			vreal->user_val.l[n] = NULL;
		    }
		}
	    }
	    else{
		if((v->user_val.p
		      && (!vreal->user_val.p
			  || strcmp(v->user_val.p, vreal->user_val.p)))
		   ||
		     (!v->user_val.p && vreal->user_val.p)){
		    /* It changed, fix it */
		    changed++;
		    /* free the changed value */
		    if(vreal->user_val.p)
		      fs_give((void **)&vreal->user_val.p);
		    
		    /* copy back the original one */
		    vreal->user_val.p = cpystr(v->user_val.p);
		}
	    }

	    if(changed){
		set_current_val(vreal, TRUE, FALSE);
		fix_side_effects(ps, vreal, 1);
	    }
	}
    }
}


/*
 * Compare saved user_val with current user_val to see if it changed.
 * If any have changed, change it back and take the appropriate action.
 */
void
revert_to_saved_color_config(ps, vsave)
struct pine *ps;
SAVED_CONFIG_S *vsave;
{
    struct variable *vreal;
    SAVED_CONFIG_S  *v;
    int i, n;
    int changed = 0;

    v = vsave;
    for(vreal = ps->vars; vreal->name; vreal++,v++){
	if(!(color_related_var(ps, vreal) || vreal==&ps->vars[V_FEATURE_LIST]))
	  continue;
	
	if(vreal == &ps->vars[V_FEATURE_LIST]){
	    /* handle feature changes */
	    if(memcmp(v->user_val.features, ps->feature_list, BM_SIZE)){
		FEATURE_S *f;

		/*
		 * At least one feature was changed.  Go through the
		 * list of eligible features and toggle them back to what
		 * they were.
		 */

		for(i = 0; f = feature_list(i); i++){
		    if(feature_list_section(f)
			&& ((F_ON(f->id, ps)
			      && !bitnset(f->id, v->user_val.features))
			   ||
			   (F_OFF(f->id, ps)
			      && bitnset(f->id, v->user_val.features)))){

			toggle_feature_bit(ps, i, vreal, NULL);
		    }
		}
	    }
	}
	else{
	    if(vreal->is_list){
		if((v->user_val.l && !vreal->user_val.l)
		   || (!v->user_val.l && vreal->user_val.l))
		  changed++;
		else if(!v->user_val.l && !vreal->user_val.l)
		  ;/* no change, nothing to do */
		else
		  for(i = 0; v->user_val.l[i] || vreal->user_val.l[i]; i++)
		    if((v->user_val.l[i]
			  && (!vreal->user_val.l[i]
			     || strcmp(v->user_val.l[i], vreal->user_val.l[i])))
		       ||
			 (!v->user_val.l[i] && vreal->user_val.l[i])){
			changed++;
			break;
		    }
		
		if(changed){
		    char **list;

		    /* free the changed value */
		    if(vreal->user_val.l){
			for(i = 0; vreal->user_val.l[i]; i++)
			  fs_give((void **)&vreal->user_val.l[i]);
			
			fs_give((void **)&vreal->user_val.l);
		    }

		    /* copy back the original one */
		    if(v->user_val.l){
			list = v->user_val.l;
			n = 0;
			/* count how many */
			while(list[n])
			  n++;

			vreal->user_val.l
				= (char **)fs_get((n+1) * sizeof(char *));
			for(i = 0; i < n; i++)
			  vreal->user_val.l[i] = cpystr(v->user_val.l[i]);

			vreal->user_val.l[n] = NULL;
		    }
		}
	    }
	    else{
		if((v->user_val.p
		      && (!vreal->user_val.p
			  || strcmp(v->user_val.p, vreal->user_val.p)))
		   ||
		     (!v->user_val.p && vreal->user_val.p)){
		    /* It changed, fix it */
		    changed++;
		    /* free the changed value */
		    if(vreal->user_val.p)
		      fs_give((void **)&vreal->user_val.p);
		    
		    /* copy back the original one */
		    vreal->user_val.p = cpystr(v->user_val.p);
		}
	    }

	    if(changed)
	      fix_side_effects(ps, vreal, 1);
	}
    }

    if(changed){
	set_current_color_vals(ps);
	ClearScreen();
	ps->mangled_screen = 1;
    }
}


/*
 * Adjust side effects that happen because variable changes values.
 *
 * Var->user_val should be set to the new value before calling this.
 */
void
fix_side_effects(ps, var, revert)
struct pine     *ps;
struct variable *var;
int              revert;
{
    int    i;
    char **v, *q;
    struct variable *vars = ps->vars;

    /* move this up here so we get the Using default message */
    if(var == &ps->vars[V_PERSONAL_NAME]){
	if(!var->user_val.p && ps->ui.fullname){
	    if(var->current_val.p)
	      fs_give((void **)&var->current_val.p);

	    var->current_val.p = cpystr(ps->ui.fullname);
	}
    }

    if(!revert
      && ((!var->is_fixed
	    && !var->is_list
	    && !var->user_val.p
	    && var->current_val.p)
	 ||
	 (!var->is_fixed
	    && var->is_list
	    && !var->user_val.l
	    && var->current_val.l)))
      q_status_message(SM_ORDER,0,3,"Using default value");

    if(var == &ps->vars[V_USER_DOMAIN]){
	char *p, *q;

	if(ps->VAR_USER_DOMAIN
	   && ps->VAR_USER_DOMAIN[0]
	   && (p = strrindex(ps->VAR_USER_DOMAIN, '@'))){
	    if(*(++p)){
		if(!revert)
		  q_status_message2(SM_ORDER, 3, 5,
		    "User-domain (%s) cannot contain \"@\"; using %s",
		    ps->VAR_USER_DOMAIN, p);
		q = ps->VAR_USER_DOMAIN;
		while((*q++ = *p++) != '\0')
		  ;/* do nothing */
	    }
	    else{
		if(!revert)
		  q_status_message1(SM_ORDER, 3, 5,
		    "User-domain (%s) cannot contain \"@\"; deleting",
		    ps->VAR_USER_DOMAIN);
		fs_give((void **)&ps->USR_USER_DOMAIN);
		set_current_val(&ps->vars[V_USER_DOMAIN], TRUE, TRUE);
	    }
	}

	/*
	 * Reset various pointers pertaining to domain name and such...
	 */
	init_hostname(ps);
    }
    else if(var == &ps->vars[V_INBOX_PATH]){
	/*
	 * fixup the inbox path based on global/default values...
	 */
	init_inbox_mapping(ps->VAR_INBOX_PATH, ps->context_list);

	if(!strucmp(ps->cur_folder, ps->inbox_name) && ps->mail_stream
	   && strcmp(ps->VAR_INBOX_PATH, ps->mail_stream->mailbox)){
	    /*
	     * If we currently have "inbox" open and the mailbox name
	     * doesn't match, reset the current folder's name...
	     */
	    strcpy(ps->cur_folder, ps->mail_stream->mailbox);
	    ps->inbox_stream   = NULL;
	    ps->mangled_header = 1;
	}
	else if(ps->inbox_stream
		&& strcmp(ps->VAR_INBOX_PATH, ps->inbox_stream->mailbox)){
	    /*
	     * if we don't have inbox directly open, but have it
	     * open for new mail notification, close the stream like
	     * any other ordinary folder, and clean up...
	     */
	    MAILSTREAM *s = ps->inbox_stream;
	    ps->inbox_stream = NULL;
	    mn_give(&ps->inbox_msgmap);
	    expunge_and_close(s, NULL, s->mailbox, NULL);
	}
    }
    else if(var == &ps->vars[V_ADDRESSBOOK] ||
	    var == &ps->vars[V_GLOB_ADDRBOOK] ||
#ifdef	ENABLE_LDAP
	    var == &ps->vars[V_LDAP_SERVERS] ||
#endif
	    var == &ps->vars[V_ABOOK_FORMATS]){
	addrbook_reset();
    }
    else if(var == &ps->vars[V_INDEX_FORMAT]){
	init_index_format(ps->VAR_INDEX_FORMAT, &ps->index_disp_format);
	clear_index_cache();
    }
    else if(var == &ps->vars[V_DEFAULT_FCC] ||
	    var == &ps->vars[V_DEFAULT_SAVE_FOLDER]){
	init_save_defaults();
    }
    else if(var == &ps->vars[V_INIT_CMD_LIST]){
	if(!revert)
	  q_status_message(SM_ASYNC, 0, 3,
	    "Initial command changes will affect your next pine session.");
    }
    else if(var == &ps->vars[V_VIEW_HEADERS]){
	ps->view_all_except = 0;
	if(ps->VAR_VIEW_HEADERS)
	  for(v = ps->VAR_VIEW_HEADERS; (q = *v) != NULL; v++)
	    if(q[0]){
		char *p;

		removing_leading_white_space(q);
		/* look for colon or space or end */
		for(p = q; *p && !isspace((unsigned char)*p) && *p != ':'; p++)
		  ;/* do nothing */
		
		*p = '\0';
		if(strucmp(q, ALL_EXCEPT) == 0)
		  ps->view_all_except = 1;
	    }
    }
    else if(var == &ps->vars[V_OVERLAP]){
	int old_value = ps->viewer_overlap;

	if(SVAR_OVERLAP(ps, old_value, tmp_20k_buf)){
	    if(!revert)
	      q_status_message(SM_ORDER, 3, 5, tmp_20k_buf);
	}
	else
	  ps->viewer_overlap = old_value;
    }
    else if(var == &ps->vars[V_MARGIN]){
	int old_value = ps->scroll_margin;

	if(SVAR_MARGIN(ps, old_value, tmp_20k_buf)){
	    if(!revert)
	      q_status_message(SM_ORDER, 3, 5, tmp_20k_buf);
	}
	else
	  ps->scroll_margin = old_value;
    }
    else if(var == &ps->vars[V_FILLCOL]){
	if(SVAR_FILLCOL(ps, ps->composer_fillcol, tmp_20k_buf)){
	    if(!revert)
	      q_status_message(SM_ORDER, 3, 5, tmp_20k_buf);
	}
    }
    else if(var == &ps->vars[V_STATUS_MSG_DELAY]){
	if(SVAR_MSGDLAY(ps, ps->status_msg_delay, tmp_20k_buf)){
	    if(!revert)
	      q_status_message(SM_ORDER, 3, 5, tmp_20k_buf);
	}
    }
    else if(var == &ps->vars[V_SIGNATURE_FILE]){
	if(ps->VAR_OPER_DIR && ps->VAR_SIGNATURE_FILE &&
	   is_absolute_path(ps->VAR_SIGNATURE_FILE) &&
	   !in_dir(ps->VAR_OPER_DIR, ps->VAR_SIGNATURE_FILE)){
	    char *e;

	    e = (char *)fs_get((strlen(ps->VAR_OPER_DIR) + 100) * sizeof(char));
	    sprintf(e, "Warning: Sig file can't be outside of %s",
		    ps->VAR_OPER_DIR);
	    q_status_message(SM_ORDER, 3, 6, e);
	    fs_give((void **)&e);
	}
    }
    else if(var == &ps->vars[V_MAILCHECK]){
	timeo = 15;
	if(SVAR_MAILCHK(ps, timeo, tmp_20k_buf)){
	    if(!revert)
	      q_status_message(SM_ORDER, 3, 5, tmp_20k_buf);
	}
	else if(timeo == 0L && !revert){
	    q_status_message(SM_ORDER, 4, 6,
"Warning: automatic new mail checking and mailbox checkpointing is disabled");
	    if(ps->VAR_INBOX_PATH && ps->VAR_INBOX_PATH[0] == '{')
	      q_status_message(SM_ASYNC, 3, 6,
"Warning: mail-check-interval=0 may cause IMAP server connection to time out");
	}
    }
#if defined(DOS) || defined(OS2)
    else if(var == &ps->vars[V_FOLDER_EXTENSION]){
	mail_parameters(NULL, SET_EXTENSION,
			(void *)var->current_val.p);
    }
    else if(var == &ps->vars[V_NEWSRC_PATH]){
	if(var->current_val.p && var->current_val.p[0])
	  mail_parameters(NULL, SET_NEWSRC,
			  (void *)var->current_val.p);
    }
#endif
    else if(revert
	  && (var == &ps->vars[V_SAVED_MSG_NAME_RULE]
              || var == &ps->vars[V_FCC_RULE]
              || var == &ps->vars[V_GOTO_DEFAULT_RULE]
              || var == &ps->vars[V_INCOMING_STARTUP]
              || var == &ps->vars[V_AB_SORT_RULE]
#ifndef	_WINDOWS
              || var == &ps->vars[V_COLOR_STYLE]
#endif
              || var == &ps->vars[V_FLD_SORT_RULE])){
	NAMEVAL_S *rule;

	if(var == &ps->vars[V_SAVED_MSG_NAME_RULE]){
	    for(i = 0; rule = save_msg_rules(i); i++)
	      if(!strucmp(var->user_val.p, rule->name)){
		  ps->save_msg_rule = rule->value;
		  break;
	      }
	}
	else if(var == &ps->vars[V_FCC_RULE]){
	    for(i = 0; rule = fcc_rules(i); i++)
	      if(!strucmp(var->user_val.p, rule->name)){
		  ps->fcc_rule = rule->value;
		  break;
	      }
	}
	else if(var == &ps->vars[V_GOTO_DEFAULT_RULE]){
	    for(i = 0; rule = goto_rules(i); i++)
	      if(!strucmp(var->user_val.p, rule->name)){
		  ps->goto_default_rule = rule->value;
		  break;
	      }
	}
	else if(var == &ps->vars[V_INCOMING_STARTUP]){
	    for(i = 0; rule = incoming_startup_rules(i); i++)
	      if(!strucmp(var->user_val.p, rule->name)){
		  ps->inc_startup_rule = rule->value;
		  break;
	      }
	}
#ifndef	_WINDOWS
	else if(var == &ps->vars[V_COLOR_STYLE]){
	    for(i = 0; rule = col_style(i); i++)
	      if(!strucmp(var->user_val.p, rule->name)){
		  ps->color_style = rule->value;
		  break;
	      }
	    
	    pico_toggle_color(0);
	    switch(ps->color_style){
	      case COL_NONE:
	      case COL_TERMDEF:
		pico_set_color_options(0);
		break;
	      case COL_ANSI8:
		pico_set_color_options(COLOR_ANSI8_OPT);
		break;
	      case COL_ANSI16:
		pico_set_color_options(COLOR_ANSI16_OPT);
		break;
	    }

	    if(ps->color_style != COL_NONE)
	      pico_toggle_color(1);
	
	    if(pico_usingcolor())
	      pico_set_normal_color();

	    clear_index_cache();
	    ClearScreen();
	    ps->mangled_screen = 1;
	}
#endif
	else if(var == &ps->vars[V_FLD_SORT_RULE]){
	    for(i = 0; rule = fld_sort_rules(i); i++)
	      if(!strucmp(var->user_val.p, rule->name)){
		  ps->fld_sort_rule = rule->value;
		  break;
	      }
	}
	else{
	    for(i = 0; rule = ab_sort_rules(i); i++)
	      if(!strucmp(var->user_val.p, rule->name)){
		  ps->ab_sort_rule = rule->value;
		  break;
	      }

	    addrbook_reset();
	}
    }
    else if(revert && var == &ps->vars[V_SORT_KEY]){
	decode_sort(ps, VAR_SORT_KEY);
    }
    else if(var == &ps->vars[V_USE_ONLY_DOMAIN_NAME]){
	init_hostname(ps);
    }
    else if(var == &ps->vars[V_IND_PLUS_FORE_COLOR] ||
	    var == &ps->vars[V_IND_IMP_FORE_COLOR]  ||
            var == &ps->vars[V_IND_DEL_FORE_COLOR]  ||
            var == &ps->vars[V_IND_ANS_FORE_COLOR]  ||
            var == &ps->vars[V_IND_NEW_FORE_COLOR]  ||
            var == &ps->vars[V_IND_UNS_FORE_COLOR]  ||
            var == &ps->vars[V_IND_REC_FORE_COLOR]  ||
            var == &ps->vars[V_IND_PLUS_BACK_COLOR] ||
            var == &ps->vars[V_IND_IMP_BACK_COLOR]  ||
            var == &ps->vars[V_IND_DEL_BACK_COLOR]  ||
            var == &ps->vars[V_IND_ANS_BACK_COLOR]  ||
            var == &ps->vars[V_IND_NEW_BACK_COLOR]  ||
            var == &ps->vars[V_IND_UNS_BACK_COLOR]  ||
            var == &ps->vars[V_IND_REC_BACK_COLOR]){
	clear_index_cache();
    }
    else if(var == score_act_global_ptr){
	int score;

	score = atoi(var->current_val.p);
	if(score < SCORE_MIN || score > SCORE_MAX){
	    q_status_message2(SM_ORDER, 3, 5,
			      "Score Value must be in range %s to %s",
			      comatose(SCORE_MIN), comatose(SCORE_MAX));
	    if(var->user_val.p)
	      fs_give((void **)&var->user_val.p);
	    
	    set_current_val(var, FALSE, FALSE);
	}
    }
    else if(var == scorei_pat_global_ptr){
	if(var->user_val.p){
	    int left, right;

	    if(parse_score_interval(var->user_val.p, &left, &right)){
		fs_give((void **)&var->user_val.p);
		var->user_val.p = stringform_of_score_interval(left, right);
	    }
	    else
	      fs_give((void **)&var->user_val.p);
	}

	set_current_val(var, FALSE, FALSE);
    }
}


SAVED_CONFIG_S *
save_config_vars(ps)
struct pine *ps;
{
    struct variable *vreal;
    SAVED_CONFIG_S *vsave, *v;

    vsave = (SAVED_CONFIG_S *)fs_get((V_LAST_VAR+1)*sizeof(SAVED_CONFIG_S));
    memset((void *)vsave, 0, (V_LAST_VAR+1)*sizeof(SAVED_CONFIG_S));
    v = vsave;
    for(vreal = ps->vars; vreal->name; vreal++,v++){
	if(!save_include(ps, vreal))
	  continue;
	
	if(vreal == &ps->vars[V_FEATURE_LIST]){
	    /* save copy of feature bitmap */
	    memcpy(v->user_val.features, ps->feature_list, BM_SIZE);
	}
	else if(vreal->is_list){  /* save user_val.l */
	    int n, i;
	    char **list;

	    if(vreal->user_val.l){
		/* count how many */
		n = 0;
		list = vreal->user_val.l;
		while(list[n])
		  n++;

		v->user_val.l = (char **)fs_get((n+1) * sizeof(char *));
		memset((void *)v->user_val.l, 0, (n+1)*sizeof(char *));
		for(i = 0; i < n; i++)
		  v->user_val.l[i] = cpystr(vreal->user_val.l[i]);

		v->user_val.l[n] = NULL;
	    }
	}
	else{  /* save user_val.p */
	    if(vreal->user_val.p)
	      v->user_val.p = cpystr(vreal->user_val.p);
	}
    }

    return(vsave);
}


SAVED_CONFIG_S *
save_color_config_vars(ps)
struct pine *ps;
{
    struct variable *vreal;
    SAVED_CONFIG_S *vsave, *v;

    vsave = (SAVED_CONFIG_S *)fs_get((V_LAST_VAR+1)*sizeof(SAVED_CONFIG_S));
    memset((void *)vsave, 0, (V_LAST_VAR+1)*sizeof(SAVED_CONFIG_S));
    v = vsave;
    for(vreal = ps->vars; vreal->name; vreal++,v++){
	if(!(color_related_var(ps, vreal) || vreal==&ps->vars[V_FEATURE_LIST]))
	  continue;
	
	if(vreal == &ps->vars[V_FEATURE_LIST]){
	    /* save copy of feature bitmap */
	    memcpy(v->user_val.features, ps->feature_list, BM_SIZE);
	}
	else if(vreal->is_list){  /* save user_val.l */
	    int n, i;
	    char **list;

	    if(vreal->user_val.l){
		/* count how many */
		n = 0;
		list = vreal->user_val.l;
		while(list[n])
		  n++;

		v->user_val.l = (char **)fs_get((n+1) * sizeof(char *));
		memset((void *)v->user_val.l, 0, (n+1)*sizeof(char *));
		for(i = 0; i < n; i++)
		  v->user_val.l[i] = cpystr(vreal->user_val.l[i]);

		v->user_val.l[n] = NULL;
	    }
	}
	else{  /* save user_val.p */
	    if(vreal->user_val.p)
	      v->user_val.p = cpystr(vreal->user_val.p);
	}
    }

    return(vsave);
}


void
free_saved_config(ps, vsavep)
struct pine *ps;
SAVED_CONFIG_S **vsavep;
{
    struct variable *vreal;
    SAVED_CONFIG_S *v;

    v = *vsavep;
    for(vreal = ps->vars; vreal->name; vreal++,v++){
	if(!save_include(ps, vreal))
	  continue;
	
	if(vreal == &ps->vars[V_FEATURE_LIST]) /* nothing to free */
	  continue;

	if(vreal->is_list){  /* free user_val.l */
	    int i;

	    if(v->user_val.l){
		for(i = 0; v->user_val.l[i]; i++)
	          fs_give((void **)&v->user_val.l[i]);

	        fs_give((void **)&v->user_val.l);
	    }
	}
	else if(v->user_val.p)
	  fs_give((void **)&v->user_val.p);
    }

    fs_give((void **)vsavep);
}


void
free_saved_color_config(ps, vsavep)
struct pine *ps;
SAVED_CONFIG_S **vsavep;
{
    struct variable *vreal;
    SAVED_CONFIG_S *v;

    v = *vsavep;
    for(vreal = ps->vars; vreal->name; vreal++,v++){
	if(!(color_related_var(ps, vreal) || vreal==&ps->vars[V_FEATURE_LIST]))
	  continue;
	
	if(vreal == &ps->vars[V_FEATURE_LIST]) /* nothing to free */
	  continue;

	if(vreal->is_list){  /* free user_val.l */
	    int i;

	    if(v->user_val.l){
		for(i = 0; v->user_val.l[i]; i++)
	          fs_give((void **)&v->user_val.l[i]);

	        fs_give((void **)&v->user_val.l);
	    }
	}
	else if(v->user_val.p)
	  fs_give((void **)&v->user_val.p);
    }

    fs_give((void **)vsavep);
}


/*
 * Given a single printer string from the config file, returns pointers
 * to alloc'd strings containing the printer nickname, the command,
 * the init string, the trailer string, everything but the nickname string,
 * and everything but the command string.  All_but_cmd includes the trailing
 * space at the end (the one before the command) but all_but_nick does not
 * include the leading space (the one before the [).
 * If you pass in a pointer it is guaranteed to come back pointing to an
 * allocated string, even if it is just an empty string.  It is ok to pass
 * NULL for any of the six return strings.
 */
void
parse_printer(input, nick, cmd, init, trailer, all_but_nick, all_but_cmd)
    char  *input;
    char **nick,
	 **cmd,
	 **init,
	 **trailer,
	 **all_but_nick,
	 **all_but_cmd;
{
    char *p, *q, *start, *saved_options = NULL;
    int tmpsave, cnt;

    if(!input)
      input = "";

    if(nick || all_but_nick){
	if(p = srchstr(input, " [")){
	    if(all_but_nick)
	      *all_but_nick = cpystr(p+1);

	    if(nick){
		while(p-1 > input && isspace((unsigned char)*(p-1)))
		  p--;

		tmpsave = *p;
		*p = '\0';
		*nick = cpystr(input);
		*p = tmpsave;
	    }
	}
	else{
	    if(nick)
	      *nick = cpystr("");

	    if(all_but_nick)
	      *all_but_nick = cpystr(input);
	}
    }

    if(p = srchstr(input, "] ")){
	do{
	    ++p;
	}while(isspace((unsigned char)*p));

	tmpsave = *p;
	*p = '\0';
	saved_options = cpystr(input);
	*p = tmpsave;
    }
    else
      p = input;
    
    if(cmd)
      *cmd = cpystr(p);

    if(init){
	if(saved_options && (p = srchstr(saved_options, "INIT="))){
	    start = p + strlen("INIT=");
	    for(cnt=0, p = start; *p && *(p+1) && isxpair(p); p += 2)
	      cnt++;
	    
	    q = *init = (char *)fs_get((cnt + 1) * sizeof(char));
	    for(p = start; *p && *(p+1) && isxpair(p); p += 2)
	      *q++ = read_hex(p);
	    
	    *q = '\0';
	}
	else
	  *init = cpystr("");
    }

    if(trailer){
	if(saved_options && (p = srchstr(saved_options, "TRAILER="))){
	    start = p + strlen("TRAILER=");
	    for(cnt=0, p = start; *p && *(p+1) && isxpair(p); p += 2)
	      cnt++;
	    
	    q = *trailer = (char *)fs_get((cnt + 1) * sizeof(char));
	    for(p = start; *p && *(p+1) && isxpair(p); p += 2)
	      *q++ = read_hex(p);
	    
	    *q = '\0';
	}
	else
	  *trailer = cpystr("");
    }

    if(all_but_cmd){
	if(saved_options)
	  *all_but_cmd = saved_options;
	else
	  *all_but_cmd = cpystr("");
    }
    else if(saved_options)
      fs_give((void **)&saved_options);
}


/*
 * Given a single printer string from the config file, returns an allocated
 * copy of the friendly printer name, which is
 *      "Nickname"  command
 */
char *
printer_name(input)
    char *input;
{
    char *nick, *cmd;
    char *ret;

    parse_printer(input, &nick, &cmd, NULL, NULL, NULL, NULL);
    ret = (char *)fs_get((2+22+1+strlen(cmd)) * sizeof(char));
    sprintf(ret, "\"%.21s\"%*s%s",
	*nick ? nick : "",
	22 - min(strlen(nick), 21),
	"",
	cmd);
    fs_give((void **)&nick);
    fs_give((void **)&cmd);

    return(ret);
}


static struct key role_select_keys[] = 
       {HELP_MENU,
	NULL_MENU,
        {"E", "Exit", {MC_EXIT,1,{'e'}}, KS_EXITMODE},
	NULL_MENU,
	{"P", "PrevRole", {MC_PREVITEM, 1, {'p'}}, KS_NONE},
	{"N", "NextRole", {MC_NEXTITEM, 2, {'n', TAB}}, KS_NONE},
	PREVPAGE_MENU,
	NEXTPAGE_MENU,
	NULL_MENU,
	NULL_MENU,
	NULL_MENU,
	WHEREIS_MENU};
INST_KEY_MENU(role_select_km, role_select_keys);
#define DEFAULT_KEY 3

int
role_select_screen(ps, role, alt_compose)
    struct pine    *ps;
    ACTION_S      **role;
    int             alt_compose;
{
    CONF_S        *ctmp = NULL, *first_line = NULL;
    OPT_SCREEN_S   screen;
    PAT_S         *pat, *sel_pat = NULL;
    int            ret = -1;
    char          *helptitle;
    HelpType       help;
    PAT_STATE      pstate;

    if(!role)
      return(ret);

    *role = NULL;

    if(!(nonempty_patterns() && first_pattern(ROLE_DO_ROLES, &pstate))){
	q_status_message(SM_ORDER, 0, 3,
			 "No roles available. Use Setup/Rules to add roles.");
	return(ret);
    }


    if(alt_compose){
	menu_init_binding(&role_select_km, 'C', MC_CHOICE, "C", "[ComposeAs]",
			  DEFAULT_KEY);
	menu_add_binding(&role_select_km, ctrl('J'), MC_CHOICE);
	menu_add_binding(&role_select_km, ctrl('M'), MC_CHOICE);
    }
    else{
	menu_init_binding(&role_select_km, 'S', MC_CHOICE, "S", "[Select]",
			  DEFAULT_KEY);
	menu_add_binding(&role_select_km, ctrl('J'), MC_CHOICE);
	menu_add_binding(&role_select_km, ctrl('M'), MC_CHOICE);
    }

    if(alt_compose){
	helptitle = "HELP FOR SELECTING A ROLE TO COMPOSE AS";
	help      = h_role_select;
    }
    else{
	helptitle = "HELP FOR SELECTING A ROLE NICKNAME";
	help      = h_role_nick_select;
    }

    for(pat = first_pattern(ROLE_DO_ROLES, &pstate);
	pat;
	pat = next_pattern(ROLE_DO_ROLES, &pstate)){
	new_confline(&ctmp);
	if(!first_line)
	  first_line = ctmp;

	ctmp->value        = cpystr((pat->patgrp && pat->patgrp->nick)
					? pat->patgrp->nick : "?");
	ctmp->d.r.selected = &sel_pat;
	ctmp->d.r.pat      = pat;
	ctmp->keymenu      = &role_select_km;
	ctmp->help         = help;
	ctmp->help_title   = helptitle;
	ctmp->tool         = role_select_tool;
	ctmp->flags        = CF_STARTITEM;
	ctmp->valoffset    = 4;
    }

    (void)conf_scroll_screen(ps, &screen, first_line, "SELECT ROLE",
			     "roles ", 0, 0);

    if(sel_pat){
	*role = sel_pat->action;
	ret = 0;
    }

    ps->mangled_screen = 1;
    return(ret);
}


int
role_select_tool(ps, cmd, cl, flags)
    struct pine *ps;
    int          cmd;
    CONF_S     **cl;
    unsigned     flags;
{
    int retval;

    switch(cmd){
      case MC_CHOICE :
	*((*cl)->d.r.selected) = (*cl)->d.r.pat;
	retval = simple_exit_cmd(flags);
	break;

      case MC_EXIT :
        retval = simple_exit_cmd(flags);
	break;

      default:
	retval = -1;
	break;
    }

    if(retval > 0)
      ps->mangled_body = 1;

    return(retval);
}


void
role_config_screen(ps, rflags)
    struct pine *ps;
    int          rflags;
{
    CONF_S      *ctmp, *first_line;
    OPT_SCREEN_S screen;
    char         title[60];

    dprint(4,(debugfile, "role_config_screen()\n"));

    if(ps->fix_fixed_warning && offer_to_fix_pinerc(ps))
      write_pinerc(ps);

    if(!all_patterns())
      return;

uh_oh:
    ctmp = NULL;
    first_line = NULL;

    role_type_print(title, "SETUP %sRULES", rflags);
    role_global_flags = rflags;
    role_config_init_disp(ps, &ctmp, &first_line, rflags);
    switch(conf_scroll_screen(ps, &screen, first_line, title, "rules ", 0, 0)){
	case 0:
	  break;

	case 10:
	  close_patterns();	/* flushes data changes and re-reads original */
	  break;
	
	case 1:
	  if(write_patterns())
	    goto uh_oh;

	  /* scores may have changed */
	  if(rflags & ROLE_DO_SCORES){
	      clear_index_cache();
	      if(ps_global->mail_stream &&
	         ps_global->mail_stream != ps_global->inbox_stream)
	        clear_msg_scores(ps_global->mail_stream);
	      if(ps_global->inbox_stream)
	        clear_msg_scores(ps_global->inbox_stream);
	      
	      if(mn_get_sort(ps_global->msgmap) == SortScore)
	        sort_folder(ps_global->msgmap, mn_get_sort(ps_global->msgmap),
			    mn_get_revsort(ps_global->msgmap), TRUE);
	  }

	  /* recalculate need for scores */
	  scores_are_used(SCOREUSE_INVALID);

	  /* we may want to fetch more or fewer headers each fetch */ 
	  calc_extra_hdrs();
	  if(get_extra_hdrs())
	    (void) mail_parameters(NULL, SET_IMAPEXTRAHEADERS,
				   (void *) get_extra_hdrs());

	  if(rflags & ROLE_DO_INCOLS && pico_usingcolor())
	    clear_index_cache();

	  if(rflags & ROLE_DO_FILTER)
	    role_process_filters();

	  break;
	
	default:
	  q_status_message(SM_ORDER,7,10, "conf_scroll_screen unexpected ret");
	  break;
    }

    role_global_flags = 0;
    ps->mangled_screen = 1;
}


/*
 * This is called from process_cmd to add a new pattern to the end of the
 * list of patterns. The pattern is seeded with values from the current
 * message.
 */
void
role_take(ps, msgmap, rtype)
    struct pine *ps;
    MSGNO_S     *msgmap;
    int          rtype;
{
    PAT_S       *defpat, *newpat = NULL;
    PAT_LINE_S  *new_patline, *patline;
    ENVELOPE    *env;
    int          rflags;
    char        *s, title[60], specific_fldr[MAXPATH+1];
    PAT_STATE    pstate;

    dprint(4,(debugfile, "role_take()\n"));

    env = mail_fetchstructure(ps->mail_stream,
			      mn_m2raw(msgmap, mn_get_cur(msgmap)), NULL);
    
    if(!env){
	q_status_message(SM_ORDER, 3, 7,
			 "problem getting addresses from message");
	return;
    }

    if(!all_patterns()){
	q_status_message(SM_ORDER, 3, 7, "problem accessing rules");
	return;
    }

    switch(rtype){
      case 'r':
      case 's':
      case 'i':
      case 'f':
	rflags = (rtype == 'r') ? ROLE_DO_ROLES :
		  (rtype == 's') ? ROLE_DO_SCORES :
		   (rtype == 'f') ? ROLE_DO_FILTER :
				     ROLE_DO_INCOLS;
	break;

      default:
	cmd_cancelled(NULL);
	return;
    }

    /* set this so that even if we don't edit at all, we'll be asked */
    rflags |= ROLE_CHANGES;


    /*
     * Make a pattern out of the information in the envelope and
     * use that as the default pattern we give to the role editor.
     * It will have a pattern but no actions set.
     */
    defpat = (PAT_S *)fs_get(sizeof(*defpat));
    memset((void *)defpat, 0, sizeof(*defpat));

    defpat->patgrp = (PATGRP_S *)fs_get(sizeof(*defpat->patgrp));
    memset((void *)defpat->patgrp, 0, sizeof(*defpat->patgrp));

    if(env->to)
      defpat->patgrp->to = addrlst_to_pattern(env->to);

    if(env->from)
      defpat->patgrp->from = addrlst_to_pattern(env->from);

    if(env->cc)
      defpat->patgrp->cc = addrlst_to_pattern(env->cc);

    if(env->sender && (!env->from || !address_is_same(env->sender, env->from)))
      defpat->patgrp->sender = addrlst_to_pattern(env->sender);

    /*
     * Env->newsgroups is already comma-separated and there shouldn't be
     * any commas or backslashes in newsgroup names, so we don't add the
     * roletake escapes.
     */
    if(env->newsgroups)
      defpat->patgrp->news = string_to_pattern(env->newsgroups);

    /*
     * Subject may have commas or backslashes, so we add escapes.
     * We lose track of the charset when we go into the pattern editor.
     */
    if(env->subject){
	char *p, *q, *t = NULL, *charset;

	p = (char *)rfc1522_decode((unsigned char *)tmp_20k_buf,
				   env->subject, &charset);

	/* toss the charset for now */
	if(charset)
	  fs_give((void **)&charset);

	/*
	 * We do the mime decode above before passing it to strip_subject
	 * because otherwise strip_subject wants to give us utf8 back, and
	 * we can't handle it.
	 */
	if((q = mail_strip_subject(p)) != NULL){
	    t = add_roletake_escapes(q);
	    fs_give((void **)&q);
	}

	if(t){
	    defpat->patgrp->subj = string_to_pattern(t);
	    fs_give((void **)&t);
	}
    }
    
    if(IS_NEWS(ps->mail_stream))
      defpat->patgrp->fldr_type = FLDR_NEWS;
    else
      defpat->patgrp->fldr_type = FLDR_EMAIL;

    specific_fldr[0] = specific_fldr[MAXPATH] = '\0';
    if(ps->mail_stream == ps->inbox_stream)
      strcpy(specific_fldr, ps_global->inbox_name);
    else if(ps->context_current
	    && ps->context_current->use & CNTXT_INCMNG &&
	    folder_is_nick(ps->cur_folder, FOLDERS(ps->context_current)))
      strncpy(specific_fldr, ps->cur_folder, MAXPATH);
    else
      context_apply(specific_fldr, ps->context_current, ps->cur_folder);
    
    if(specific_fldr[0]){
	s = add_folder_escapes(specific_fldr);
	if(s){
	    if(rtype == 'f')
	      defpat->patgrp->fldr_type = FLDR_SPECIFIC;

	    defpat->patgrp->folder = string_to_pattern(s);
	    fs_give((void **)&s);
	}
    }

    role_type_print(title, "ADD NEW %sRULE", rflags);

    if(role_config_edit_screen(ps, defpat, title, rflags,
			       &newpat) == 1 && newpat){

	if(ps->never_allow_changing_from && newpat->action &&
	   newpat->action->from)
	    q_status_message(SM_ORDER|SM_DING, 3, 7,
      "Site policy doesn't allow changing From address so From is ignored");

	if(rflags & ROLE_DO_ROLES && newpat->patgrp && newpat->patgrp->nick){
	    PAT_S *pat;
	    
	    for(pat = first_pattern(ROLE_DO_ROLES, &pstate);
		pat;
		pat = next_pattern(ROLE_DO_ROLES, &pstate)){
		if(pat->patgrp && pat->patgrp->nick &&
		   !strucmp(pat->patgrp->nick, newpat->patgrp->nick)){
		    q_status_message(SM_ORDER|SM_DING, 3, 7, "Warning: The nickname of the new role is already in use.");
		    break;
		}
	    }
	}


	/* need a new patline */
	new_patline = (PAT_LINE_S *)fs_get(sizeof(*new_patline));
	memset((void *)new_patline, 0, sizeof(*new_patline));
	new_patline->type = Literal;
	pattern_h->dirtypinerc = 1;

	/* tie together with new pattern */
	new_patline->first = new_patline->last = newpat;
	newpat->patline = new_patline;

	/* find last current patline */
	for(patline = pattern_h->patlinehead;
	    patline && patline->next;
	    patline = patline->next)
	  ;
	
	/* add new patline to end of list */
	if(patline){
	    patline->next = new_patline;
	    new_patline->prev = patline;
	}
	else
	  pattern_h->patlinehead = new_patline;

	if(write_patterns() == 0){
	    char msg[60];

	    role_type_print(msg, "New %srule saved", rflags);
	    q_status_message(SM_ORDER, 0, 3, msg);

	    /* scores may have changed */
	    if(rflags & ROLE_DO_SCORES){
		clear_index_cache();
	        if(ps_global->mail_stream &&
	           ps_global->mail_stream != ps_global->inbox_stream)
	          clear_msg_scores(ps_global->mail_stream);
	        if(ps_global->inbox_stream)
	          clear_msg_scores(ps_global->inbox_stream);
	      
		if(mn_get_sort(msgmap) == SortScore)
		  sort_folder(msgmap, mn_get_sort(msgmap),
			      mn_get_revsort(msgmap), TRUE);
	    }

	    if(rflags & ROLE_DO_FILTER)
	      role_process_filters();

	    /* recalculate need for scores */
	    scores_are_used(SCOREUSE_INVALID);

	    /* we may want to fetch more or fewer headers each fetch */ 
	    calc_extra_hdrs();
	    if(get_extra_hdrs())
	      (void) mail_parameters(NULL, SET_IMAPEXTRAHEADERS,
				     (void *) get_extra_hdrs());

	    if(rflags & ROLE_DO_INCOLS && pico_usingcolor())
	      clear_index_cache();
	}
    }
    else
      cmd_cancelled(NULL);

    free_pat(&defpat);
    ps->mangled_screen = 1;
}


PATTERN_S *
addrlst_to_pattern(addr)
    ADDRESS *addr;
{
    char      *s, *t, *u, *v;
    PATTERN_S *p = NULL;

    if(addr){
	t = s = (char *)fs_get((est_size(addr) + 1) * sizeof(char));
	s[0] = '\0';
	while(addr){
	    u = simple_addr_string(addr, tmp_20k_buf);
	    v = add_roletake_escapes(u);
	    if(v){
		if(*v && t != s)
		  sstrcpy(&t, ",");

		sstrcpy(&t, v);
		fs_give((void **)&v);
	    }

	    addr = addr->next;
	}

	if(*s)
	  p = string_to_pattern(s);
	
	fs_give((void **)&s);
    }

    return(p);
}


static struct key role_config_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},
	{"P", "PrevRule", {MC_PREVITEM, 1, {'p'}}, KS_NONE},
	{"N", "NextRule", {MC_NEXTITEM, 2, {'n', TAB}}, KS_NONE},
	PREVPAGE_MENU,
	NEXTPAGE_MENU,
	{"A", "Add", {MC_ADD,1,{'a'}}, KS_NONE},
	{"D", "Delete", {MC_DELETE,1,{'d'}}, KS_NONE},
	{"$", "Shuffle", {MC_SHUFFLE,1,{'$'}}, KS_NONE},
	WHEREIS_MENU,

        HELP_MENU,
	OTHER_MENU,
        NULL_MENU,
        NULL_MENU,
	{"I", "IncludeFile", {MC_ADDFILE,1,{'i'}}, KS_NONE},
	{"X", "eXcludeFile", {MC_DELFILE,1,{'x'}}, KS_NONE},
        NULL_MENU,
        NULL_MENU,
        NULL_MENU,
        NULL_MENU,
        NULL_MENU,
	NULL_MENU};
INST_KEY_MENU(role_conf_km, role_config_keys);


void
role_config_init_disp(ps, ctmp, first_line, rflags)
    struct pine     *ps;
    CONF_S         **ctmp;
    CONF_S         **first_line;
    int              rflags;
{
    PAT_LINE_S    *patline;
    PAT_STATE      pstate;

    if(first_line)
      *first_line = NULL;

    if(!first_pattern(rflags, &pstate)){
	add_fake_first_role(ctmp, 0, rflags);
	if(first_line && !*first_line)
	  (*first_line) = (*ctmp);
    }

    for(patline = pattern_h ? pattern_h->patlinehead : NULL;
	patline;
	patline = patline->next)
      add_patline_to_display(ps, ctmp, 0, first_line, NULL, patline, rflags);
}


void
add_patline_to_display(ps, ctmp, before, first_line, top_line, patline, rflags)
    struct pine *ps;
    CONF_S     **ctmp;
    int          before;
    CONF_S     **first_line;
    CONF_S     **top_line;
    PAT_LINE_S  *patline;
    int          rflags;
{
    PAT_S *pat;
    int    len, firstitem;
    char  *q;

    /* put dashed line around file contents */
    if(patline->type == File){
	new_confline(ctmp);
	if(before){
	    /*
	     * New_confline appends ctmp after old current instead of inserting
	     * it, so we have to adjust. We have
	     *  <- a <-> b <-> p <-> c -> and want <- a <-> p <-> b <-> c ->
	     */

	    CONF_S *a, *b, *c, *p;

	    p = *ctmp;
	    b = (*ctmp)->prev;
	    c = (*ctmp)->next;
	    a = b ? b->prev : NULL;
	    if(a)
	      a->next = p;

	    if(b){
		b->prev = p;
		b->next = c;
	    }

	    if(c)
	      c->prev = b;

	    p->prev = a;
	    p->next = b;
	    before = 0;
	}

	if(top_line && *top_line == NULL)
	  *top_line = (*ctmp);

	(*ctmp)->value = cpystr(repeat_char(ps->ttyo->screen_cols, '-'));
	len = strlen(patline->filename);
	q = (char *)fs_get((len + 100) * sizeof(char));
	sprintf(q, "From file %s%s", patline->filename,
		patline->readonly ? " (ReadOnly)" : ""); 
	len = min(strlen(q), ps->ttyo->screen_cols -2);
	strncpy((*ctmp)->value + 2, q, len);
	fs_give((void **)&q);
	(*ctmp)->flags     |= (CF_NOSELECT | CF_STARTITEM);
	(*ctmp)->d.r.patline = patline;
	firstitem = 0;
    }
    else
      firstitem = 1;

    for(pat = patline->first; pat; pat = pat->next){
	
	/* Check that pattern has a role and is of right type */
	if(pat->action &&
	   ((rflags & ROLE_DO_ROLES) && pat->action->is_a_role ||
	    (rflags & ROLE_DO_INCOLS) && pat->action->is_a_incol ||
	    (rflags & ROLE_DO_SCORES) && pat->action->is_a_score ||
	    (rflags & ROLE_DO_FILTER) && pat->action->is_a_filter)){
	    add_role_to_display(ctmp, patline, pat, 0,
				(first_line && *first_line == NULL)
				  ? first_line :
				    (top_line && *top_line == NULL)
				      ? top_line : NULL,
				firstitem, rflags);
	    firstitem = 1;
	    if(top_line && *top_line == NULL && first_line)
	      *top_line = *first_line;
	}

    }

    if(patline->type == File){
	new_confline(ctmp);
	(*ctmp)->value = cpystr(repeat_char(ps->ttyo->screen_cols, '-'));
	len = strlen(patline->filename);
	q = (char *)fs_get((len + 100) * sizeof(char));
	sprintf(q, "End of rules from %s", patline->filename);
	len = min(strlen(q), ps->ttyo->screen_cols -2);
	strncpy((*ctmp)->value + 2, q, len);
	fs_give((void **)&q);
	(*ctmp)->flags     |= CF_NOSELECT;
	(*ctmp)->d.r.patline = patline;
    }
}


void
add_role_to_display(ctmp, patline, pat, before, first_line, firstitem, rflags)
    CONF_S     **ctmp;
    PAT_LINE_S  *patline;
    PAT_S       *pat;
    int          before;
    CONF_S     **first_line;
    int          firstitem;
    int          rflags;
{
    ACTION_S *role;
    char      title[60];

    if(!(pat && pat->action))
      return;

    role = pat->action;

    new_confline(ctmp);
    if(first_line)
      *first_line = *ctmp;

    if(before){
	/*
	 * New_confline appends ctmp after old current instead of inserting
	 * it, so we have to adjust. We have
	 *  <- a <-> b <-> p <-> c -> and want <- a <-> p <-> b <-> c ->
	 */

	CONF_S *a, *b, *c, *p;

	p = *ctmp;
	b = (*ctmp)->prev;
	c = (*ctmp)->next;
	a = b ? b->prev : NULL;
	if(a)
	  a->next = p;

	if(b){
	    b->prev = p;
	    b->next = c;
	}

	if(c)
	  c->prev = b;

	p->prev = a;
	p->next = b;
    }

    role_type_print(title, "HELP FOR %sRULE CONFIGURATION", rflags);

    (*ctmp)->value       = cpystr((pat && pat->patgrp && pat->patgrp->nick)
				    ? pat->patgrp->nick : "?");
    (*ctmp)->d.r.patline = patline;
    (*ctmp)->d.r.pat     = pat;
    (*ctmp)->keymenu     = &role_conf_km;
    (*ctmp)->help        = (rflags & ROLE_DO_INCOLS) ? h_rules_incols :
			    (rflags & ROLE_DO_FILTER) ? h_rules_filter :
			     (rflags & ROLE_DO_SCORES) ? h_rules_score :
			      (rflags & ROLE_DO_ROLES)  ? h_rules_roles :
			       NO_HELP;
    (*ctmp)->help_title  = title;
    (*ctmp)->tool        = role_config_tool;
    (*ctmp)->flags      |= (firstitem ? CF_STARTITEM : 0);
    (*ctmp)->valoffset   = 4;
}


void
add_fake_first_role(ctmp, before, rflags)
    CONF_S **ctmp;
    int      before;
    int      rflags;
{
    char title[60];
    char add[60];

    new_confline(ctmp);

    if(before){
	/*
	 * New_confline appends ctmp after old current instead of inserting
	 * it, so we have to adjust. We have
	 *  <- a <-> b <-> p <-> c -> and want <- a <-> p <-> b <-> c ->
	 */

	CONF_S *a, *b, *c, *p;

	p = *ctmp;
	b = (*ctmp)->prev;
	c = (*ctmp)->next;
	a = b ? b->prev : NULL;
	if(a)
	  a->next = p;

	if(b){
	    b->prev = p;
	    b->next = c;
	}

	if(c)
	  c->prev = b;

	p->prev = a;
	p->next = b;
    }

    role_type_print(title, "HELP FOR %sRULE CONFIGURATION", rflags);
    role_type_print(add, "Use Add to add a %sRule", rflags);

    (*ctmp)->value      = cpystr(add);
    (*ctmp)->keymenu    = &role_conf_km;
    (*ctmp)->help        = (rflags & ROLE_DO_INCOLS) ? h_rules_incols :
			    (rflags & ROLE_DO_FILTER) ? h_rules_filter :
			     (rflags & ROLE_DO_SCORES) ? h_rules_score :
			      (rflags & ROLE_DO_ROLES)  ? h_rules_roles :
			       NO_HELP;
    (*ctmp)->help_title = title;
    (*ctmp)->tool       = role_config_tool;
    (*ctmp)->flags     |= CF_STARTITEM;
    (*ctmp)->valoffset  = 4;
}


int
role_config_tool(ps, cmd, cl, flags)
    struct pine *ps;
    int          cmd;
    CONF_S     **cl;
    unsigned     flags;
{
    int       first_one, rv = 0;
    PAT_STATE pstate;
    char      exitpmt[60];

    first_one = !first_pattern(role_global_flags, &pstate);
    switch(cmd){
      case MC_DELETE :
	if(first_one)
	  q_status_message(SM_ORDER|SM_DING, 0, 3,
			   "Nothing to Delete, use Add");
	else
	  rv = role_config_del(ps, cl, role_global_flags);

	break;

      case MC_ADD :
	rv = role_config_add(ps, cl, role_global_flags);
	break;

      case MC_EDIT :
	if(first_one)
	  rv = role_config_add(ps, cl, role_global_flags);
	else
	  rv = role_config_edit(ps, cl, role_global_flags);

	break;

      case MC_SHUFFLE :
	if(first_one)
	  q_status_message(SM_ORDER|SM_DING, 0, 3,
			   "Nothing to Shuffle, use Add");
	else
	  rv = role_config_shuffle(ps, cl);

	break;

      case MC_EXIT :
	role_type_print(exitpmt, "%sRule Setup", role_global_flags);
	rv = screen_exit_cmd(flags, exitpmt);
	break;

      case MC_ADDFILE :
	rv = role_config_addfile(ps, cl, role_global_flags);
	break;

      case MC_DELFILE :
	rv = role_config_delfile(ps, cl, role_global_flags);
	break;

      default:
	rv = -1;
	break;
    }

    return(rv);
}


/*
 * Add a new role.
 *
 * Returns  1 -- There were changes
 *          0 -- No changes
 */
int
role_config_add(ps, cl, rflags)
    struct pine  *ps;
    CONF_S      **cl;
    int           rflags;
{
    int         rv = 0, first_pat = 0;
    PAT_S      *new_pat = NULL, *cur_pat;
    PAT_LINE_S *new_patline, *cur_patline;
    PAT_STATE   pstate;
    char        title[60];

    if((*cl)->d.r.patline &&
       (*cl)->d.r.patline->readonly
       && (*cl)->d.r.patline->type == File){
	q_status_message(SM_ORDER, 0, 3, "Can't add rule to ReadOnly file");
	return(rv);
    }

    role_type_print(title, "ADD A %sRULE", rflags);

    if(role_config_edit_screen(ps, NULL, title, rflags,
			       &new_pat) == 1 && new_pat){
	if(ps->never_allow_changing_from &&
	   new_pat->action &&
	   new_pat->action->from)
	  q_status_message(SM_ORDER|SM_DING, 0, 3,
      "Site policy doesn't allow changing From address so From is ignored");

	if(rflags & ROLE_DO_ROLES && new_pat->patgrp && new_pat->patgrp->nick){
	    PAT_S *pat;
	    
	    for(pat = first_pattern(ROLE_DO_ROLES, &pstate);
		pat;
		pat = next_pattern(ROLE_DO_ROLES, &pstate)){
		if(pat->patgrp && pat->patgrp->nick &&
		   !strucmp(pat->patgrp->nick, new_pat->patgrp->nick)){
		    q_status_message(SM_ORDER|SM_DING, 3, 7, "Warning: The nickname of the new role is already in use.");
		    break;
		}
	    }
	}
	
	rv = 1;
	cur_pat = (*cl)->d.r.pat;
	if(!cur_pat)
	  first_pat++;

	cur_patline = first_pat ? pattern_h->patlinehead : cur_pat->patline;

	/* need a new pat_line */
	if(first_pat || cur_patline->type == Literal){
	    new_patline = (PAT_LINE_S *)fs_get(sizeof(*new_patline));
	    memset((void *)new_patline, 0, sizeof(*new_patline));
	    new_patline->type = Literal;
	    pattern_h->dirtypinerc = 1;
	}

	if(cur_patline){
	    if(first_pat || cur_patline->type == Literal){
		new_patline->prev = cur_patline->prev;
		new_patline->next = cur_patline;
		if(cur_patline->prev)
		  cur_patline->prev->next = new_patline;
		else
		  pattern_h->patlinehead = new_patline;

		cur_patline->prev = new_patline;

		/* tie new patline and new pat together */
		new_pat->patline   = new_patline;
		new_patline->first = new_patline->last = new_pat;
	    }
	    else if(cur_patline->type == File){ /* don't need a new pat_line */
		/* tie together */
		new_pat->patline = cur_patline;
		cur_patline->dirty = 1;

		/* Splice new_pat before cur_pat */
		new_pat->prev = cur_pat->prev;
		new_pat->next = cur_pat;
		if(cur_pat->prev)
		  cur_pat->prev->next = new_pat;
		else
		  cur_patline->first = new_pat;

		cur_pat->prev = new_pat;
	    }
	}
	else{
	    /* tie new first patline and pat together */
	    new_pat->patline   = new_patline;
	    new_patline->first = new_patline->last = new_pat;

	    /* set head of list */
	    pattern_h->patlinehead = new_patline;
	}

	/*
	 * If this is the first role, we replace the "Use Add" fake role
	 * with this real one.
	 */
	if(first_pat){
	    /* Adjust conf_scroll_screen variables */
	    (*cl)->d.r.pat = new_pat;
	    (*cl)->d.r.patline = new_pat->patline;
	    if((*cl)->value)
	      fs_give((void **)&(*cl)->value);

	    (*cl)->value = cpystr((new_pat && new_pat->patgrp &&
				   new_pat->patgrp->nick)
					 ? new_pat->patgrp->nick : "?");
	}
	/* Else we are inserting a new role before the cur role */
	else
	  add_role_to_display(cl, new_pat->patline, new_pat, 1, NULL,
			      1, rflags);
    }

    return(rv);
}


/* 
 * Change the current role.
 *
 * Returns  1 -- There were changes
 *          0 -- No changes
 */
int
role_config_edit(ps, cl, rflags)
    struct pine  *ps;
    CONF_S      **cl;
    int           rflags;
{
    int         rv = 0;
    PAT_S      *new_pat = NULL, *cur_pat, *prev_pat;
    PAT_LINE_S *new_patline, *cur_patline, *prev_patline;
    PAT_STATE   pstate;
    char        title[60];

    if((*cl)->d.r.patline->readonly){
	q_status_message(SM_ORDER, 0, 3, "Can't change ReadOnly rule");
	return(rv);
    }

    cur_pat = (*cl)->d.r.pat;

    role_type_print(title, "CHANGE THIS %sRULE", rflags);

    if(role_config_edit_screen(ps, cur_pat, title,
			       rflags, &new_pat) == 1 && new_pat){

	if(ps->never_allow_changing_from &&
	   new_pat->action &&
	   new_pat->action->from)
	  q_status_message(SM_ORDER|SM_DING, 0, 3,
      "Site policy doesn't allow changing From address so From is ignored");

	if(rflags & ROLE_DO_ROLES && new_pat->patgrp &&
	   new_pat->patgrp->nick){
	    PAT_S *pat;
	    
	    for(pat = first_pattern(ROLE_DO_ROLES, &pstate);
		pat;
		pat = next_pattern(ROLE_DO_ROLES, &pstate)){
		if(pat->patgrp && pat->patgrp->nick && pat != cur_pat &&
		   !strucmp(pat->patgrp->nick, new_pat->patgrp->nick)){
		    q_status_message(SM_ORDER|SM_DING, 3, 7, "Warning: The nickname of this role is also used for another role.");
		    break;
		}
	    }
	}
	
	rv = 1;

	/*
	 * Splice in new_pat in place of cur_pat
	 */

	if(cur_pat->prev)
	  cur_pat->prev->next = new_pat;

	if(cur_pat->next)
	  cur_pat->next->prev = new_pat;

	new_pat->prev = cur_pat->prev;
	new_pat->next = cur_pat->next;

	/* tie together patline and pat (new_pat gets patline in editor) */
	if(new_pat->patline->first == cur_pat)
	  new_pat->patline->first = new_pat;

	if(new_pat->patline->last == cur_pat)
	  new_pat->patline->last = new_pat;
	
	if(new_pat->patline->type == Literal)
	  pattern_h->dirtypinerc = 1;
	else
	  new_pat->patline->dirty = 1;

	cur_pat->next = NULL;
	free_pat(&cur_pat);

	/* Adjust conf_scroll_screen variables */
	(*cl)->d.r.pat = new_pat;
	(*cl)->d.r.patline = new_pat->patline;
	if((*cl)->value)
	  fs_give((void **)&(*cl)->value);

	(*cl)->value = cpystr((new_pat->patgrp && new_pat->patgrp->nick)
				? new_pat->patgrp->nick : "?");
    }

    return(rv);
}


/*
 * Delete a role.
 *
 * Returns  1 -- There were changes
 *          0 -- No changes
 */
int
role_config_del(ps, cl, rflags)
    struct pine  *ps;
    CONF_S      **cl;
    int           rflags;
{
    int  rv = 0;
    char msg[60];
    char prompt[100];

    if((*cl)->d.r.patline->readonly){
	q_status_message(SM_ORDER, 0, 3, "Can't delete ReadOnly rule");
	return(rv);
    }

    role_type_print(msg, "Really delete %srule", rflags);
    sprintf(prompt, "%s \"%.20s\" ", msg, (*cl)->value);

    ps->mangled_footer = 1;
    if(want_to(prompt,'n','n',h_config_role_del, WT_FLUSH_IN) == 'y'){
	rv = ps->mangled_body = 1;
	delete_a_role(cl, rflags);
    }
    else
      q_status_message(SM_ORDER, 0, 3, "Rule not deleted");
    
    return(rv);
}


void
delete_a_role(cl, rflags)
    CONF_S **cl;
    int      rflags;
{
    PAT_S      *cur_pat;
    CONF_S     *cp, *cq;
    PAT_LINE_S *cur_patline;
    PAT_STATE   pstate;

    cur_pat     = (*cl)->d.r.pat;
    cur_patline = (*cl)->d.r.patline;

    if(cur_patline->type == Literal){	/* delete patline */
	if(cur_patline->prev)
	  cur_patline->prev->next = cur_patline->next;
	else
	  pattern_h->patlinehead = cur_patline->next;

	if(cur_patline->next)
	  cur_patline->next->prev = cur_patline->prev;
	
	pattern_h->dirtypinerc = 1;

	cur_patline->next = NULL;
	free_patline(&cur_patline);
    }
    else if(cur_patline->type == File){	/* or delete pat */
	if(cur_pat->prev)
	  cur_pat->prev->next = cur_pat->next;
	else
	  cur_patline->first = cur_pat->next;

	if(cur_pat->next)
	  cur_pat->next->prev = cur_pat->prev;
	else
	  cur_patline->last = cur_pat->prev;

	cur_patline->dirty = 1;

	cur_pat->next = NULL;
	free_pat(&cur_pat);
    }

    /* delete the conf lines */

    if(!first_pattern(rflags, &pstate)){	/* deleting last real rule */
	if(!((*cl)->prev || (*cl)->next)){	/* deleting only line */
	    free_conflines(cl);
	    add_fake_first_role(cl, 0, rflags);
	    opt_screen->top_line = (*cl);
	}
	else{
	    cp = (*cl);
	    if((*cl)->prev)
	      *cl = (*cl)->prev;
	    else
	      *cl = (*cl)->next;
	    
	    snip_confline(&cp);	/* snip deleted line       */
	    while((*cl)->prev)
	      *cl = (*cl)->prev;

	    add_fake_first_role(cl, 1, rflags);
	    opt_screen->top_line = (*cl);
	}
    }
    else{
	/* find next selectable line */
	for(cp = (*cl)->next;
	    cp && (cp->flags & CF_NOSELECT);
	    cp = cp->next)
	  ;
	
	if(!cp){	/* no next selectable, find previous selectable */
	    if(*cl == opt_screen->top_line)
	      opt_screen->top_line = (*cl)->prev;

	    for(cp = (*cl)->prev;
		cp && (cp->flags & CF_NOSELECT);
		cp = cp->prev)
	      ;
	}
	else if(*cl == opt_screen->top_line)
	  opt_screen->top_line = (*cl)->next;

	cq = *cl;
	*cl = cp;
	snip_confline(&cq);
    }
}


/*
 * Shuffle the current role up or down.
 *
 * Returns  1 -- There were changes
 *          0 -- No changes
 */
int
role_config_shuffle(ps, cl)
    struct pine  *ps;
    CONF_S      **cl;
{
    int      rv = 0, deefault, i;
    int      readonlyabove = 0, readonlybelow = 0;
    ESCKEY_S opts[5];
    HelpType help;
    char     tmp[200];
    CONF_S  *a, *b;
    PAT_TYPE curtype, prevtype, nexttype;

    if(!((*cl)->prev || (*cl)->next)){
	q_status_message(SM_ORDER, 0, 3,
	   "Shuffle only makes sense when there is more than one rule defined");
	return(rv);
    }

    /* Move it up or down? */
    i = 0;
    opts[i].ch      = 'u';
    opts[i].rval    = 'u';
    opts[i].name    = "U";
    opts[i++].label = "Up";

    opts[i].ch      = 'd';
    opts[i].rval    = 'd';
    opts[i].name    = "D";
    opts[i++].label = "Down";

    opts[i].ch      = 'b';
    opts[i].rval    = 'b';
    opts[i].name    = "B";
    opts[i++].label = "Before File";

    opts[i].ch      = 'a';
    opts[i].rval    = 'a';
    opts[i].name    = "A";
    opts[i++].label = "After File";

    opts[i].ch = -1;
    deefault = 'u';

    curtype = ((*cl)->d.r.patline) ? (*cl)->d.r.patline->type : TypeNotSet;

    prevtype = ((*cl)->prev && (*cl)->prev->d.r.patline)
		? (*cl)->prev->d.r.patline->type : TypeNotSet;
    if(curtype == File && prevtype == File && (*cl)->prev->d.r.pat == NULL)
      prevtype = TypeNotSet;

    nexttype = ((*cl)->next && (*cl)->next->d.r.patline)
		? (*cl)->next->d.r.patline->type : TypeNotSet;
    if(curtype == File && nexttype == File && (*cl)->next->d.r.pat == NULL)
      nexttype = TypeNotSet;


    if(curtype == Literal){
	if(prevtype == TypeNotSet){	/* no up, at top	*/
	    opts[0].ch = -2;
	    opts[2].ch = -2;
	    deefault = 'd';
	}
	else if(prevtype == Literal){	/* regular up		*/
	    opts[2].ch = -2;
	}
	else if(prevtype == File){	/* file above us	*/
	    if((*cl)->prev->d.r.patline->readonly)
	      readonlyabove++;
	}

	if(nexttype == TypeNotSet){	/* no down, at bottom	*/
	    opts[1].ch = -2;
	    opts[3].ch = -2;
	}
	else if(nexttype == Literal){	/* regular down		*/
	    opts[3].ch = -2;
	}
	else if(nexttype == File){	/* file below us	*/
	    if((*cl)->next->d.r.patline->readonly)
	      readonlybelow++;
	}
    }
    else if(curtype == File){
	if((*cl)->d.r.patline && (*cl)->d.r.patline->readonly){
	    q_status_message(SM_ORDER, 0, 3, "Can't change ReadOnly file");
	    return(0);
	}

	opts[2].ch = -2;
	opts[3].ch = -2;
    }
    else{
	q_status_message(SM_ORDER, 0, 3,
	"Programming Error: unknown line type in role_shuffle");
	return(rv);
    }

    sprintf(tmp, "Shuffle \"%s\" %s%s%s%s%s%s%s ? ",
	    (*cl)->value,
	    (opts[0].ch != -2) ? "UP" : "",
	    (opts[0].ch != -2  && opts[1].ch != -2) ? " or " : "",
	    (opts[1].ch != -2) ? "DOWN" : "",
	    ((opts[0].ch != -2 ||
	      opts[1].ch != -2) && opts[2].ch != -2) ? " or " : "",
	    (opts[2].ch != -2) ? "BEFORE" : "",
	    ((opts[0].ch != -2 ||
	      opts[1].ch != -2 ||
	      opts[2].ch != -2) && opts[3].ch != -2) ? " or " : "",
	    (opts[3].ch != -2) ? "AFTER" : "");

    help = (opts[0].ch == -2) ? h_role_shuf_down
			      : (opts[1].ch == -2) ? h_role_shuf_up
						   : h_role_shuf;

    rv = radio_buttons(tmp, -FOOTER_ROWS(ps), opts, deefault, 'x',
		       help, RB_NORM);

    if(rv == 'x'){
	cmd_cancelled("Shuffle");
	return(0);
    }

    if((readonlyabove && rv == 'u' && curtype != prevtype) ||
       (readonlybelow && rv == 'd' && curtype != nexttype)){
	q_status_message(SM_ORDER, 0, 3, "Can't shuffle into ReadOnly file");
	return(0);
    }

    if(rv == 'u' && curtype == Literal && prevtype == Literal){
	rv = 1;
	a = (*cl)->prev;
	b = (*cl);
	if(a == opt_screen->top_line)
	  opt_screen->top_line = b;

	swap_literal_roles(a, b);
	ps->mangled_body = 1;
    }
    else if(rv == 'd' && curtype == Literal && nexttype == Literal){
	rv = 1;
	a = (*cl);
	b = (*cl)->next;
	if(a == opt_screen->top_line)
	  opt_screen->top_line = b;

	swap_literal_roles(a, b);
	ps->mangled_body = 1;
    }
    else if(rv == 'u' && curtype == File && prevtype == File){
	rv = 1;
	a = (*cl)->prev;
	b = (*cl);
	if(a == opt_screen->top_line)
	  opt_screen->top_line = b;

	swap_file_roles(a, b);
	ps->mangled_body = 1;
    }
    else if(rv == 'u' && curtype == File){
	rv = 1;
	move_role_outof_file(cl, 1);
	ps->mangled_body = 1;
    }
    else if(rv == 'd' && curtype == File && nexttype == File){
	rv = 1;
	a = (*cl);
	b = (*cl)->next;
	if(a == opt_screen->top_line)
	  opt_screen->top_line = b;

	swap_file_roles(a, b);
	ps->mangled_body = 1;
    }
    else if(rv == 'd' && curtype == File){
	rv = 1;
	if(*cl == opt_screen->top_line)
	  opt_screen->top_line = (*cl)->next;

	move_role_outof_file(cl, 0);
	ps->mangled_body = 1;
    }
    else if(rv == 'u' && curtype == Literal && prevtype == File){
	rv = 1;
	move_role_into_file(cl, 1);
	ps->mangled_body = 1;
    }
    else if(rv == 'd' && curtype == Literal && nexttype == File){
	rv = 1;
	if(*cl == opt_screen->top_line)
	  opt_screen->top_line = (*cl)->next;

	move_role_into_file(cl, 0);
	ps->mangled_body = 1;
    }
    else if(rv == 'b'){
	rv = 1;
	move_role_around_file(cl, 1);
	ps->mangled_body = 1;
    }
    else if(rv == 'a'){
	rv = 1;
	if(*cl == opt_screen->top_line)
	  opt_screen->top_line = (*cl)->next;

	move_role_around_file(cl, 0);
	ps->mangled_body = 1;
    }

    return(rv);
}


int
role_config_addfile(ps, cl, rflags)
    struct pine  *ps;
    CONF_S      **cl;
    int           rflags;
{
    char        filename[MAXPATH+1], full_filename[MAXPATH+1];
    char        dir2[MAXPATH+1], pdir[MAXPATH+1];
    char       *lc, *newfile = NULL;
    PAT_LINE_S *file_patline;
    int         rv = 0, len;
    int         r = 1, flags;
    HelpType    help = NO_HELP;
    PAT_TYPE    curtype;
    struct variable *vars = ps->vars;

    if(ps->restricted){
	q_status_message(SM_ORDER, 0, 3, "Pine demo can't read files");
	return(rv);
    }

    curtype = ((*cl)->d.r.patline && (*cl)->d.r.patline)
	        ? (*cl)->d.r.patline->type : TypeNotSet;

    if(curtype == File){
	q_status_message(SM_ORDER, 0, 3, "Current rule is already part of a file. Move outside any files first.");
	return(rv);
    }

    /*
     * Parse_pattern_file uses signature_path to figure out where to look
     * for the file. In signature_path we read signature files relative
     * to the pinerc dir, so if user selects one that is in there we'll
     * use relative instead of absolute, so it looks nicer.
     */
    pdir[0] = '\0';
    if(VAR_OPER_DIR){
	strncpy(pdir, VAR_OPER_DIR, MAXPATH);
	pdir[MAXPATH] = '\0';
	len = strlen(pdir) + 1;
    }
    else if((lc = last_cmpnt(ps->pinerc)) != NULL){
	strncpy(pdir, ps->pinerc, min(MAXPATH,lc-ps->pinerc));
	pdir[min(MAXPATH, lc-ps->pinerc)] = '\0';
	len = strlen(pdir);
    }

    strcpy(dir2, pdir);
    filename[0] = '\0';
    full_filename[0] = '\0';
    ps->mangled_footer = 1;

    while(1){
	flags = OE_APPEND_CURRENT;
	r = optionally_enter(filename, -FOOTER_ROWS(ps), 0, MAXPATH,
			     "Name of file to be added to rules: ",
			     NULL, help, &flags);
	
	if(r == 3){
	    help = (help == NO_HELP) ? h_config_role_addfile : NO_HELP;
	    continue;
	}
	else if(r == 10 || r == 11){    /* Browser or File Completion */
	    continue;
	}
	else if(r == 1 || (r == 0 && filename[0] == '\0')){
	    cmd_cancelled("IncludeFile");
	    return(rv);
	}
	else if(r == 4){
	    continue;
	}
	else if(r != 0){
	    Writechar(BELL, 0);
	    continue;
	}

	removing_leading_and_trailing_white_space(filename);
	if(is_absolute_path(filename))
	  newfile = cpystr(filename);
	else{
	    build_path(full_filename, dir2, filename);
	    removing_leading_and_trailing_white_space(full_filename);
	    if(!strncmp(full_filename, pdir, strlen(pdir)))
	      newfile = cpystr(full_filename + len); 
	    else
	      newfile = cpystr(full_filename);
	}
	
	if(newfile && *newfile)
	  break;
    }

    if(!newfile)
      return(rv);

    if((file_patline = parse_pat_file(newfile)) != NULL){
	/*
	 * Insert the file before the current line.
	 */
	PAT_LINE_S *cur_patline;
	int         first_pat = 0;

	rv = ps->mangled_screen = 1;
	cur_patline = (*cl)->d.r.patline ? (*cl)->d.r.patline
					 : pattern_h->patlinehead;
	first_pat = !(*cl)->d.r.pat;

	pattern_h->dirtypinerc = 1;
	file_patline->dirty = 1;

	if(cur_patline){
	    file_patline->prev = cur_patline->prev;
	    file_patline->next = cur_patline;
	    if(cur_patline->prev)
	      cur_patline->prev->next = file_patline;
	    else
	      pattern_h->patlinehead = file_patline;

	    cur_patline->prev = file_patline;
	}
	else{
	    /* set head of list */
	    pattern_h->patlinehead = file_patline;
	}

	if(first_pat){
	    if(file_patline->first){
		CONF_S *first_line = NULL, *add_line;

		/* get rid of Fake Add line */
		add_line = *cl;
		opt_screen->top_line = NULL;
		add_patline_to_display(ps, cl, 0, &first_line,
				       &opt_screen->top_line, file_patline,
				       rflags);
		opt_screen->current = first_line;
		snip_confline(&add_line);
	    }
	    else{
		CONF_S *save_current;

		/* we're _appending_ to the Fake Add line */
		save_current = opt_screen->current;
		add_patline_to_display(ps, cl, 0, NULL, NULL, file_patline,
				       rflags);
		opt_screen->current = save_current;
	    }
	}
	else{
	    opt_screen->top_line = NULL;
	    add_patline_to_display(ps, cl, 1, NULL, &opt_screen->top_line,
				   file_patline, rflags);
	    /*
	     * The "before" in add_patline swaps screen->current on us.
	     */
	    opt_screen->current = (*cl)->next;
	}
    }

    if(newfile)
      fs_give((void **)&newfile);
    
    return(rv);
}


int
role_config_delfile(ps, cl, rflags)
    struct pine  *ps;
    CONF_S      **cl;
    int           rflags;
{
    int         rv = 0;
    PAT_LINE_S *cur_patline;
    char        prompt[100];
    PAT_STATE   pstate;

    if(!(cur_patline = (*cl)->d.r.patline)){
	q_status_message(SM_ORDER, 0, 3,
			 "Unknown problem in role_config_delfile");
	return(rv);
    }

    if(cur_patline->type != File){
	q_status_message(SM_ORDER, 0, 3, "Current rule is not part of a file. Use Delete to remove current rule");
	return(rv);
    }

    sprintf(prompt, "Really remove rule file \"%.20s\" from rules config ",
	    cur_patline->filename);

    ps->mangled_footer = 1;
    if(want_to(prompt,'n','n',h_config_role_delfile, WT_FLUSH_IN) == 'y'){
	CONF_S *ctmp, *cp;

	rv = ps->mangled_screen = 1;
	pattern_h->dirtypinerc = 1;

	if(cur_patline->prev)
	  cur_patline->prev->next = cur_patline->next;
	else
	  pattern_h->patlinehead = cur_patline->next;

	if(cur_patline->next)
	  cur_patline->next->prev = cur_patline->prev;
	
	/* delete the conf lines */

	/* find the first one associated with this file */
	for(ctmp = *cl;
	    ctmp && ctmp->prev && ctmp->prev->d.r.patline == cur_patline;
	    ctmp = ctmp->prev)
	  ;
	
	if(ctmp->prev)	/* this file wasn't the first thing in config */
	  *cl = ctmp->prev;
	else{		/* this file was first in config */
	    for(cp = ctmp; cp && cp->next; cp = cp->next)
	      ;

	    if(cp->d.r.patline == cur_patline)
	      *cl = NULL;
	    else
	      *cl = cp;
	}
	
	/* delete lines from the file */
	while(ctmp && ctmp->d.r.patline == cur_patline){
	    cp = ctmp;
	    ctmp = ctmp->next;
	    snip_confline(&cp);
	}

	if(!first_pattern(rflags, &pstate)){
	    /*
	     * Find the start and prepend the fake first role
	     * in there.
	     */
	     while(*cl && (*cl)->prev)
	       *cl = (*cl)->prev;

	    add_fake_first_role(cl, 1, rflags);
	}

	opt_screen->top_line = first_confline(*cl);
	opt_screen->current  = first_sel_confline(opt_screen->top_line);

	cur_patline->next = NULL;
	free_patline(&cur_patline);
    }
    else
      q_status_message(SM_ORDER, 0, 3, "Rule file not removed");
    
    return(rv);
}


/*
 * Swap from a, b to b, a.
 */
void
swap_literal_roles(a, b)
    CONF_S *a, *b;
{
    PAT_LINE_S *patline_a, *patline_b;

    patline_a = a->d.r.patline;
    patline_b = b->d.r.patline;

    pattern_h->dirtypinerc = 1;

    /* first swap the patlines */
    if(patline_a->next == patline_b){
	patline_b->prev = patline_a->prev;
	if(patline_a->prev)
	  patline_a->prev->next = patline_b;

	patline_a->next = patline_b->next;
	if(patline_b->next)
	  patline_b->next->prev = patline_a;

	patline_b->next = patline_a;
	patline_a->prev = patline_b;
    }
    else{
	PAT_LINE_S *new_a_prev, *new_a_next;

	new_a_prev = patline_b->prev;
	new_a_next = patline_b->next;

	patline_b->prev = patline_a->prev;
	patline_b->next = patline_a->next;
	if(patline_b->prev)
	  patline_b->prev->next = patline_b;
	if(patline_b->next)
	  patline_b->next->prev = patline_b;

	patline_a->prev = new_a_prev;
	patline_a->next = new_a_next;
	if(patline_a->prev)
	  patline_a->prev->next = patline_a;
	if(patline_a->next)
	  patline_a->next->prev = patline_a;
    }

    /*
     * If patline_b is now the first one in the list, we need to fix the
     * head of the list to point to this new role.
     */
    if(patline_b->prev == NULL)
      pattern_h->patlinehead = patline_b;


    /* and then swap the conf lines */

    b->prev = a->prev;
    if(a->prev)
      a->prev->next = b;
    
    a->next = b->next;
    if(b->next)
      b->next->prev = a;
    
    b->next = a;
    a->prev = b;
}


/*
 * Swap from a, b to b, a.
 */
void
swap_file_roles(a, b)
    CONF_S *a, *b;
{
    PAT_S      *pat_a, *pat_b;
    PAT_LINE_S *patline;

    pat_a = a->d.r.pat;
    pat_b = b->d.r.pat;
    patline = pat_a->patline;

    patline->dirty = 1;

    /* first swap the pats */
    if(pat_a->next == pat_b){
	pat_b->prev = pat_a->prev;
	if(pat_a->prev)
	  pat_a->prev->next = pat_b;
	
	pat_a->next = pat_b->next;
	if(pat_b->next)
	  pat_b->next->prev = pat_a;
	
	pat_b->next = pat_a;
	pat_a->prev = pat_b;
    }
    else{
	PAT_S *new_a_prev, *new_a_next;

	new_a_prev = pat_b->prev;
	new_a_next = pat_b->next;

	pat_b->prev = pat_a->prev;
	pat_b->next = pat_a->next;
	if(pat_b->prev)
	  pat_b->prev->next = pat_b;
	if(pat_b->next)
	  pat_b->next->prev = pat_b;

	pat_a->prev = new_a_prev;
	pat_a->next = new_a_next;
	if(pat_a->prev)
	  pat_a->prev->next = pat_a;
	if(pat_a->next)
	  pat_a->next->prev = pat_a;
    }

    /*
     * Fix the first and last pointers.
     */
    if(patline->first == pat_a)
      patline->first = pat_b;
    if(patline->last == pat_b)
      patline->last = pat_a;

    /* and then swap the conf lines */

    b->prev = a->prev;
    if(a->prev)
      a->prev->next = b;
    
    a->next = b->next;
    if(b->next)
      b->next->prev = a;
    
    b->next = a;
    a->prev = b;
}


/*
 */
void
move_role_into_file(cl, up)
    CONF_S **cl;
    int      up;
{
    PAT_LINE_S *cur_patline, *file_patline;
    PAT_S      *pat;
    CONF_S     *a, *b;

    cur_patline = (*cl)->d.r.patline;

    if(up){
	file_patline = (*cl)->prev->d.r.patline;
	a = (*cl)->prev;
	b = (*cl);
	b->d.r.patline = file_patline;
    }
    else{
	file_patline = (*cl)->next->d.r.patline;
	a = (*cl);
	b = (*cl)->next;
	a->d.r.patline = file_patline;
    }

    pattern_h->dirtypinerc = 1;
    file_patline->dirty = 1;

    pat = cur_patline->first;

    if(!up && cur_patline == pattern_h->patlinehead)
      pattern_h->patlinehead = pattern_h->patlinehead->next;

    if(file_patline->first){
	if(up){
	    file_patline->last->next = pat;
	    pat->prev = file_patline->last;
	    file_patline->last = pat;
	}
	else{
	    file_patline->first->prev = pat;
	    pat->next = file_patline->first;
	    file_patline->first = pat;
	}
    }
    else		/* will be only role in file */
      file_patline->first = file_patline->last = pat;

    pat->patline = file_patline;

    /* delete the now unused cur_patline */
    cur_patline->first = cur_patline->last = NULL;
    if(cur_patline->prev)
      cur_patline->prev->next = cur_patline->next;
    if(cur_patline->next)
      cur_patline->next->prev = cur_patline->prev;
    
    cur_patline->next = NULL;
    free_patline(&cur_patline);

    /* and then swap the conf lines */

    b->prev = a->prev;
    if(a->prev)
      a->prev->next = b;
    
    a->next = b->next;
    if(b->next)
      b->next->prev = a;
    
    b->next = a;
    a->prev = b;
}


/*
 */
void
move_role_outof_file(cl, up)
    CONF_S **cl;
    int      up;
{
    PAT_LINE_S *file_patline, *new_patline;
    PAT_S      *pat;
    CONF_S     *a, *b;

    new_patline = (PAT_LINE_S *)fs_get(sizeof(*new_patline));
    memset((void *)new_patline, 0, sizeof(*new_patline));
    new_patline->type = Literal;

    file_patline = (*cl)->d.r.patline;
    pat = (*cl)->d.r.pat;

    if(up){
	a = (*cl)->prev;
	b = (*cl);

	if(pat->prev)
	  pat->prev->next = pat->next;
	else
	  file_patline->first = pat->next;

	if(pat->next)
	  pat->next->prev = pat->prev;
	else
	  file_patline->last = pat->prev;

	if(file_patline->first)
	  file_patline->first->prev = NULL;

	if(file_patline->last)
	  file_patline->last->next = NULL;
	
	if(file_patline->prev)
	  file_patline->prev->next = new_patline;
	
	new_patline->prev = file_patline->prev;
	new_patline->next = file_patline;
	file_patline->prev = new_patline;
	b->d.r.patline = new_patline;
    }
    else{
	a = (*cl);
	b = (*cl)->next;

	if(pat->prev)
	  pat->prev->next = pat->next;
	else
	  file_patline->first = pat->next;

	if(pat->next)
	  pat->next->prev = pat->prev;
	else
	  file_patline->last = pat->prev;

	if(file_patline->first)
	  file_patline->first->prev = NULL;

	if(file_patline->last)
	  file_patline->last->next = NULL;

	if(file_patline->next)
	  file_patline->next->prev = new_patline;
	
	new_patline->next = file_patline->next;
	new_patline->prev = file_patline;
	file_patline->next = new_patline;
	a->d.r.patline = new_patline;
    }

    pattern_h->dirtypinerc = 1;
    file_patline->dirty = 1;

    new_patline->first = new_patline->last = pat;
    pat->patline = new_patline;
    pat->prev = pat->next = NULL;

    if(up && file_patline == pattern_h->patlinehead)
      pattern_h->patlinehead = new_patline;

    /* and then swap the conf lines */

    b->prev = a->prev;
    if(a->prev)
      a->prev->next = b;
    
    a->next = b->next;
    if(b->next)
      b->next->prev = a;
    
    b->next = a;
    a->prev = b;
}


/*
 * This is a move of a literal role from before a file to after a file,
 * or vice versa.
 */
void
move_role_around_file(cl, up)
    CONF_S **cl;
    int      up;
{
    PAT_LINE_S *file_patline, *lit_patline;
    CONF_S     *cp;

    lit_patline = (*cl)->d.r.patline;
    if(up)
      file_patline = (*cl)->prev->d.r.patline;
    else{
	if(lit_patline == pattern_h->patlinehead)
	  pattern_h->patlinehead = pattern_h->patlinehead->next;

	file_patline = (*cl)->next->d.r.patline;
    }

    pattern_h->dirtypinerc = 1;

    /* remove the lit_patline from the list */
    if(lit_patline->prev)
      lit_patline->prev->next = lit_patline->next;
    if(lit_patline->next)
      lit_patline->next->prev = lit_patline->prev;

    /* and reinsert it on the other side of the file */
    if(up){
	if(file_patline == pattern_h->patlinehead)
	  pattern_h->patlinehead = lit_patline;

	lit_patline->prev = file_patline->prev;
	lit_patline->next = file_patline;

	if(file_patline->prev)
	  file_patline->prev->next = lit_patline;
	
	file_patline->prev = lit_patline;
    }
    else{
	lit_patline->next = file_patline->next;
	lit_patline->prev = file_patline;

	if(file_patline->next)
	  file_patline->next->prev = lit_patline;
	
	file_patline->next = lit_patline;
    }

    /*
     * And then move the conf line around the file conf lines.
     */

    /* find it's new home */
    if(up)
      for(cp = (*cl);
	  cp && cp->prev && cp->prev->d.r.patline == file_patline;
	  cp = prev_confline(cp))
	;
    else
      for(cp = (*cl);
	  cp && cp->next && cp->next->d.r.patline == file_patline;
	  cp = next_confline(cp))
	;

    /* remove it from where it is */
    if((*cl)->prev)
      (*cl)->prev->next = (*cl)->next;
    if((*cl)->next)
      (*cl)->next->prev = (*cl)->prev;
    
    /* cp points to top or bottom of the file lines */
    if(up){
	(*cl)->prev = cp->prev;
	if(cp->prev)
	  cp->prev->next = (*cl);
	
	cp->prev = (*cl);
	(*cl)->next = cp;
    }
    else{
	(*cl)->next = cp->next;
	if(cp->next)
	  cp->next->prev = (*cl);
	
	cp->next = (*cl);
	(*cl)->prev = cp;
    }
}


struct variable *role_forw_ptr, *role_repl_ptr, *role_fldr_ptr, *role_filt_ptr;

#define INICK_INICK_CONF    0
#define INICK_FROM_CONF     1
#define INICK_REPLYTO_CONF  2
#define INICK_FCC_CONF      3
#define INICK_SIG_CONF      4
#define INICK_TEMPL_CONF    5
CONF_S *inick_confs[INICK_TEMPL_CONF+1];


/*
 * Screen for editing configuration of a role.
 *
 * Args     ps -- pine struct
 *         def -- default role values to start with
 *       title -- part of title at top of screen
 *      rflags -- which parts of role to edit
 *      result -- This is the returned PAT_S, freed by caller.
 *
 * Returns:  0 if no change
 *           1 if user requested a change
 *               (change is stored in raw_server and hasn't been acted upon yet)
 *          10 user says abort
 */
int
role_config_edit_screen(ps, def, title, rflags, result)
    struct pine *ps;
    PAT_S       *def;
    char        *title;
    int          rflags;
    PAT_S      **result;
{
    OPT_SCREEN_S     screen, *saved_screen;
    CONF_S          *ctmp = NULL, *ctmpb, *first_line = NULL;
    struct variable  nick_var, to_pat_var, from_pat_var,
		     sender_pat_var, cc_pat_var, recip_pat_var, news_pat_var,
		     subj_pat_var, inick_var, fldr_type_var, folder_pat_var,
		     alltext_pat_var, scorei_pat_var, partic_pat_var,
		     from_act_var,
		     replyto_act_var, fcc_act_var, sig_act_var, templ_act_var,
		     repl_type_var, forw_type_var, comp_type_var, score_act_var,
		     rolecolor_vars[2], filter_type_var, folder_act_var;
    struct variable *v, *varlist[32], *arb_pat_vars = NULL;
    char            *nick = NULL, *to_pat = NULL, *from_pat = NULL,
		    *sender_pat = NULL, *cc_pat = NULL, *news_pat = NULL,
		    *recip_pat = NULL, *partic_pat = NULL,
		    *subj_pat = NULL, *inick = NULL, *fldr_type_pat = NULL,
		    *alltext_pat = NULL,
		    *folder_pat = NULL, *scorei_pat = NULL,
		    *from_act = NULL,
		    *replyto_act = NULL, *fcc_act = NULL, *sig_act = NULL,
		    *templ_act = NULL, *repl_type = NULL, *forw_type = NULL,
		    *comp_type = NULL, *rc_fg = NULL, *rc_bg = NULL,
		    *score_act = NULL, *folder_act = NULL, *filter_type = NULL,
		    *old_fg = NULL, *old_bg = NULL, *spat;
    char             tmp[MAXPATH+1];
    ARBHDR_S        *aa, *a;
    EARB_S          *earb = NULL, *ea;
    int              rv, i, j, lv, indent = 18, pindent, nahdrs = 0,
		     scoreval = 0, edit_all, edit_role,
		     edit_incol, edit_score, edit_filter;
    int	        (*radio_tool) PROTO((struct pine *, int, CONF_S **, unsigned));
    int	        (*t_tool) PROTO((struct pine *, int, CONF_S **, unsigned));
    ROLE_NAMEVAL_S  *f;

    dprint(4,(debugfile, "role_config_edit_screen()\n"));
    edit_all	= rflags & ROLE_ANY;
    edit_role	= rflags & (ROLE_ANY | ROLE_DO_ROLES);
    edit_incol	= rflags & (ROLE_ANY | ROLE_DO_INCOLS);
    edit_score	= rflags & (ROLE_ANY | ROLE_DO_SCORES);
    edit_filter	= rflags & (ROLE_ANY | ROLE_DO_FILTER);

    radio_tool = (edit_filter && !edit_all) ? role_filt_radiobutton_tool
					    : role_radiobutton_tool;
    t_tool = (edit_filter && !edit_all) ? role_filt_text_tool
					    : role_text_tool;

    /*
     * We edit by making a nested call to conf_scroll_screen.
     * We use some fake struct variables to get back the results in, and
     * so we can use the existing tools from the config screen.
     */
    varlist[j = 0] = &nick_var;
    varlist[++j] = &to_pat_var;
    varlist[++j] = &from_pat_var;
    varlist[++j] = &sender_pat_var;
    varlist[++j] = &cc_pat_var;
    varlist[++j] = &recip_pat_var;
    varlist[++j] = &partic_pat_var;
    varlist[++j] = &news_pat_var;
    varlist[++j] = &subj_pat_var;
    varlist[++j] = &alltext_pat_var;
    varlist[++j] = &scorei_pat_var;
    varlist[++j] = &inick_var;
    varlist[++j] = &fldr_type_var;
    varlist[++j] = &folder_pat_var;
    varlist[++j] = &from_act_var;
    varlist[++j] = &replyto_act_var;
    varlist[++j] = &fcc_act_var;
    varlist[++j] = &sig_act_var;
    varlist[++j] = &templ_act_var;
    varlist[++j] = &score_act_var;
    varlist[++j] = &repl_type_var;
    varlist[++j] = &forw_type_var;
    varlist[++j] = &comp_type_var;
    varlist[++j] = &rolecolor_vars[0];
    varlist[++j] = &rolecolor_vars[1];
    varlist[++j] = &filter_type_var;
    varlist[++j] = &folder_act_var;
    varlist[++j] = NULL;
    for(j = 0; varlist[j]; j++)
      memset(varlist[j], 0, sizeof(struct variable));

    role_forw_ptr = role_repl_ptr = role_fldr_ptr = role_filt_ptr = NULL;

    nick_var.name       = cpystr("Nickname");
    nick_var.is_used    = 1;
    nick_var.is_user    = 1;
    nick_var.user_val.p = (def && def->patgrp && def->patgrp->nick)
				? cpystr(def->patgrp->nick) : NULL;

    nick_var.global_val.p = cpystr(edit_role
				    ? "Alternate Role"
				    : (edit_incol
					? "Index Color Rule"
					: (edit_score
					    ? "Score Rule"
					    : "Filter Rule")));
    set_current_val(&nick_var, FALSE, FALSE);

    to_pat_var.name       = cpystr("To pattern");
    to_pat_var.is_used    = 1;
    to_pat_var.is_user    = 1;
    to_pat_var.user_val.p = (def && def->patgrp)
				? pattern_to_string(def->patgrp->to) : NULL;
    set_current_val(&to_pat_var, FALSE, FALSE);

    from_pat_var.name       = cpystr("From pattern");
    from_pat_var.is_used    = 1;
    from_pat_var.is_user    = 1;
    from_pat_var.user_val.p = (def && def->patgrp)
				? pattern_to_string(def->patgrp->from)
				: NULL;
    set_current_val(&from_pat_var, FALSE, FALSE);

    sender_pat_var.name       = cpystr("Sender pattern");
    sender_pat_var.is_used    = 1;
    sender_pat_var.is_user    = 1;
    sender_pat_var.user_val.p = (def && def->patgrp)
				  ? pattern_to_string(def->patgrp->sender)
				  : NULL;
    set_current_val(&sender_pat_var, FALSE, FALSE);

    cc_pat_var.name       = cpystr("Cc pattern");
    cc_pat_var.is_used    = 1;
    cc_pat_var.is_user    = 1;
    cc_pat_var.user_val.p = (def && def->patgrp)
				? pattern_to_string(def->patgrp->cc) : NULL;
    set_current_val(&cc_pat_var, FALSE, FALSE);

    recip_pat_var.name       = cpystr("Recip pattern");
    recip_pat_var.is_used    = 1;
    recip_pat_var.is_user    = 1;
    recip_pat_var.user_val.p = (def && def->patgrp)
				? pattern_to_string(def->patgrp->recip) : NULL;
    set_current_val(&recip_pat_var, FALSE, FALSE);

    partic_pat_var.name       = cpystr("Partic pattern");
    partic_pat_var.is_used    = 1;
    partic_pat_var.is_user    = 1;
    partic_pat_var.user_val.p = (def && def->patgrp)
				? pattern_to_string(def->patgrp->partic) : NULL;
    set_current_val(&partic_pat_var, FALSE, FALSE);

    news_pat_var.name       = cpystr("News pattern");
    news_pat_var.is_used    = 1;
    news_pat_var.is_user    = 1;
    news_pat_var.user_val.p = (def && def->patgrp)
				? pattern_to_string(def->patgrp->news)
				: NULL;
    set_current_val(&news_pat_var, FALSE, FALSE);

    subj_pat_var.name       = cpystr("Subject pattern");
    subj_pat_var.is_used    = 1;
    subj_pat_var.is_user    = 1;
    subj_pat_var.user_val.p = (def && def->patgrp)
				? pattern_to_string(def->patgrp->subj)
				: NULL;
    set_current_val(&subj_pat_var, FALSE, FALSE);

    alltext_pat_var.name       = cpystr("AllText pattern");
    alltext_pat_var.is_used    = 1;
    alltext_pat_var.is_user    = 1;
    alltext_pat_var.user_val.p = (def && def->patgrp)
				? pattern_to_string(def->patgrp->alltext)
				: NULL;
    set_current_val(&alltext_pat_var, FALSE, FALSE);

    scorei_pat_global_ptr     = &scorei_pat_var;
    scorei_pat_var.name       = cpystr("Score interval");
    scorei_pat_var.is_used    = 1;
    scorei_pat_var.is_user    = 1;
    if(def && def->patgrp && def->patgrp->do_score)
      scorei_pat_var.user_val.p =
			  stringform_of_score_interval(def->patgrp->score_min,
						       def->patgrp->score_max);

    set_current_val(&scorei_pat_var, FALSE, FALSE);

    pindent = strlen(subj_pat_var.name);	/* the longest one */
    for(a = (def && def->patgrp) ? def->patgrp->arbhdr : NULL; a; a = a->next)
      if((lv=strlen(a->field ? a->field : "")+9) > pindent)
	pindent = lv;
    
    pindent += 3;

    inick_var.name       = cpystr("Initialize settings using role");
    inick_var.is_used    = 1;
    inick_var.is_user    = 1;
    inick_var.user_val.p = (def && def->action &&
			    def->action->inherit_nick &&
			    def->action->inherit_nick[0])
			       ? cpystr(def->action->inherit_nick) : NULL;

    role_fldr_ptr = &fldr_type_var;		/* so radiobuttons can tell */
    fldr_type_var.name       = cpystr("Current Folder Type");
    fldr_type_var.is_used    = 1;
    fldr_type_var.is_user    = 1;
    fldr_type_var.user_val.p = (f=pat_fldr_types((def && def->patgrp) ? def->patgrp->fldr_type : (!def && edit_filter) ? FLDR_SPECIFIC : -1)) ? cpystr(f->name) : NULL;
    set_current_val(&fldr_type_var, FALSE, FALSE);

    folder_pat_var.name       = cpystr("Folder List");
    folder_pat_var.is_used    = 1;
    folder_pat_var.is_user    = 1;
    folder_pat_var.user_val.p = (def && def->patgrp)
				? pattern_to_string(def->patgrp->folder)
				: (!def && edit_filter)
				  ? cpystr(ps_global->inbox_name) : NULL;
    set_current_val(&folder_pat_var, FALSE, FALSE);

    from_act_var.name       = cpystr("Set From");
    from_act_var.is_used    = 1;
    from_act_var.is_user    = 1;
    if(def && def->action && def->action->from){
	char *bufp;

	bufp = (char *)fs_get((size_t)est_size(def->action->from));
	from_act_var.user_val.p = addr_string(def->action->from, bufp);
    }
    else
      from_act_var.user_val.p = NULL;

    replyto_act_var.name       = cpystr("Set Reply-To");
    replyto_act_var.is_used    = 1;
    replyto_act_var.is_user    = 1;
    if(def && def->action && def->action->replyto){
	char *bufp;

	bufp = (char *)fs_get((size_t)est_size(def->action->replyto));
	replyto_act_var.user_val.p = addr_string(def->action->replyto,
						 bufp);
    }
    else
      replyto_act_var.user_val.p = NULL;

    fcc_act_var.name       = cpystr("Set Fcc");
    fcc_act_var.is_used    = 1;
    fcc_act_var.is_user    = 1;
    fcc_act_var.user_val.p = (def && def->action && def->action->fcc)
			       ? cpystr(def->action->fcc) : NULL;

    sig_act_var.name       = cpystr("Set Signature");
    sig_act_var.is_used    = 1;
    sig_act_var.is_user    = 1;
    sig_act_var.user_val.p = (def && def->action && def->action->sig)
			       ? cpystr(def->action->sig) : NULL;

    templ_act_var.name       = cpystr("Set Template");
    templ_act_var.is_used    = 1;
    templ_act_var.is_user    = 1;
    templ_act_var.user_val.p = (def && def->action && def->action->template)
			         ? cpystr(def->action->template) : NULL;

    score_act_global_ptr     = &score_act_var;
    score_act_var.name       = cpystr("Score Value");
    score_act_var.is_used    = 1;
    score_act_var.is_user    = 1;
    if(def && def->action && def->action->scoreval >= SCORE_MIN &&
       def->action->scoreval <= SCORE_MAX)
      scoreval = def->action->scoreval;

    score_act_var.global_val.p = cpystr("0");
    if(scoreval != 0){
	score_act_var.user_val.p = (char *)fs_get(5 * sizeof(char));
	sprintf(score_act_var.user_val.p, "%d", scoreval);
    }

    set_current_val(&score_act_var, FALSE, FALSE);

    role_repl_ptr = &repl_type_var;		/* so radiobuttons can tell */
    repl_type_var.name       = cpystr("Reply Use");
    repl_type_var.is_used    = 1;
    repl_type_var.is_user    = 1;
    repl_type_var.user_val.p = (f=role_repl_types((def && def->action) ? def->action->repl_type : -1)) ? cpystr(f->name) : NULL;
    set_current_val(&repl_type_var, FALSE, FALSE);

    role_forw_ptr = &forw_type_var;		/* so radiobuttons can tell */
    forw_type_var.name       = cpystr("Forward Use");
    forw_type_var.is_used    = 1;
    forw_type_var.is_user    = 1;
    forw_type_var.user_val.p = (f=role_forw_types((def && def->action) ? def->action->forw_type : -1)) ? cpystr(f->name) : NULL;
    set_current_val(&forw_type_var, FALSE, FALSE);

    comp_type_var.name       = cpystr("Compose Use");
    comp_type_var.is_used    = 1;
    comp_type_var.is_user    = 1;
    comp_type_var.user_val.p = (f=role_comp_types((def && def->action) ? def->action->comp_type : -1)) ? cpystr(f->name) : NULL;
    set_current_val(&comp_type_var, FALSE, FALSE);

    rolecolor_vars[0].is_used    = 1;
    rolecolor_vars[0].is_user    = 1;
    rolecolor_vars[0].user_val.p = (def && def->action &&
			      def->action->incol && def->action->incol->fg[0])
			       ? cpystr(def->action->incol->fg) : NULL;
    rolecolor_vars[1].is_used    = 1;
    rolecolor_vars[1].is_user    = 1;
    rolecolor_vars[1].user_val.p = (def && def->action &&
			      def->action->incol && def->action->incol->bg[0])
			       ? cpystr(def->action->incol->bg) : NULL;
    set_current_val(&rolecolor_vars[0], FALSE, FALSE);
    set_current_val(&rolecolor_vars[1], FALSE, FALSE);
    old_fg = rolecolor_vars[0].user_val.p ? cpystr(rolecolor_vars[0].user_val.p)
					  : NULL;
    old_bg = rolecolor_vars[1].user_val.p ? cpystr(rolecolor_vars[1].user_val.p)
					  : NULL;


    /* save the old opt_screen before calling scroll screen again */
    saved_screen = opt_screen;

    /* Nickname */
    new_confline(&ctmp);
    ctmp->help_title= "HELP FOR NICKNAME";
    ctmp->var       = &nick_var;
    ctmp->valoffset = pindent;
    ctmp->keymenu   = &config_role_keymenu;
    ctmp->help      = edit_role ? h_config_role_nick :
		       edit_incol ? h_config_incol_nick :
			edit_score ? h_config_score_nick : h_config_filt_nick;
    ctmp->tool      = t_tool;
    sprintf(tmp, "%-*s =", pindent-3, nick_var.name);
    ctmp->varname   = cpystr(tmp);
    ctmp->varnamep  = ctmp;
    ctmp->value     = pretty_value(ps, ctmp);
    ctmp->d.earb    = &earb;
    ctmp->varmem    = -1;

    first_line = ctmp;
    if(rflags & ROLE_CHANGES)
      first_line->flags |= CF_CHANGES;

    /* To Pattern */
    new_confline(&ctmp);
    ctmp->help_title= "HELP FOR TO PATTERN";
    ctmp->var       = &to_pat_var;
    ctmp->valoffset = pindent;
    ctmp->keymenu   = &config_role_addr_pat_keymenu;
    ctmp->help      = edit_role ? h_config_role_topat :
		       edit_incol ? h_config_incol_topat :
			edit_score ? h_config_score_topat : h_config_filt_topat;
    ctmp->tool      = t_tool;
    sprintf(tmp, "%-*s =", pindent-3, to_pat_var.name);
    ctmp->varname   = cpystr(tmp);
    ctmp->varnamep  = ctmp;
    ctmp->value     = pretty_value(ps, ctmp);
    ctmp->d.earb    = &earb;
    ctmp->varmem    = -1;

    /* From Pattern */
    new_confline(&ctmp);
    ctmp->help_title= "HELP FOR FROM PATTERN";
    ctmp->var       = &from_pat_var;
    ctmp->valoffset = pindent;
    ctmp->keymenu   = &config_role_addr_pat_keymenu;
    ctmp->help      = h_config_role_frompat;
    ctmp->tool      = t_tool;
    sprintf(tmp, "%-*s =", pindent-3, from_pat_var.name);
    ctmp->varname   = cpystr(tmp);
    ctmp->varnamep  = ctmp;
    ctmp->value     = pretty_value(ps, ctmp);
    ctmp->d.earb    = &earb;
    ctmp->varmem    = -1;

    /* Sender Pattern */
    new_confline(&ctmp);
    ctmp->help_title= "HELP FOR SENDER PATTERN";
    ctmp->var       = &sender_pat_var;
    ctmp->valoffset = pindent;
    ctmp->keymenu   = &config_role_addr_pat_keymenu;
    ctmp->help      = h_config_role_senderpat;
    ctmp->tool      = t_tool;
    sprintf(tmp, "%-*s =", pindent-3, sender_pat_var.name);
    ctmp->varname   = cpystr(tmp);
    ctmp->varnamep  = ctmp;
    ctmp->value     = pretty_value(ps, ctmp);
    ctmp->d.earb    = &earb;
    ctmp->varmem    = -1;

    /* Cc Pattern */
    new_confline(&ctmp);
    ctmp->help_title= "HELP FOR CC PATTERN";
    ctmp->var       = &cc_pat_var;
    ctmp->valoffset = pindent;
    ctmp->keymenu   = &config_role_addr_pat_keymenu;
    ctmp->help      = h_config_role_ccpat;
    ctmp->tool      = t_tool;
    sprintf(tmp, "%-*s =", pindent-3, cc_pat_var.name);
    ctmp->varname   = cpystr(tmp);
    ctmp->varnamep  = ctmp;
    ctmp->value     = pretty_value(ps, ctmp);
    ctmp->d.earb    = &earb;
    ctmp->varmem    = -1;

    /* News Pattern */
    new_confline(&ctmp);
    ctmp->help_title= "HELP FOR NEWS PATTERN";
    ctmp->var       = &news_pat_var;
    ctmp->valoffset = pindent;
    ctmp->keymenu   = &config_role_keymenu;
    ctmp->help      = h_config_role_newspat;
    ctmp->tool      = t_tool;
    sprintf(tmp, "%-*s =", pindent-3, news_pat_var.name);
    ctmp->varname   = cpystr(tmp);
    ctmp->varnamep  = ctmp;
    ctmp->value     = pretty_value(ps, ctmp);
    ctmp->d.earb    = &earb;
    ctmp->varmem    = -1;

    /* Subject Pattern */
    new_confline(&ctmp);
    ctmp->help_title= "HELP FOR SUBJECT PATTERN";
    ctmp->var       = &subj_pat_var;
    ctmp->valoffset = pindent;
    ctmp->keymenu   = &config_role_keymenu;
    ctmp->help      = h_config_role_subjpat;
    ctmp->tool      = t_tool;
    sprintf(tmp, "%-*s =", pindent-3, subj_pat_var.name);
    ctmp->varname   = cpystr(tmp);
    ctmp->varnamep  = ctmp;
    ctmp->value     = pretty_value(ps, ctmp);
    ctmp->d.earb    = &earb;
    ctmp->varmem    = -1;

    /* Recip Pattern */
    new_confline(&ctmp);
    ctmp->help_title= "HELP FOR RECIPIENT PATTERN";
    ctmp->var       = &recip_pat_var;
    ctmp->valoffset = pindent;
    ctmp->keymenu   = &config_role_addr_pat_keymenu;
    ctmp->help      = h_config_role_recippat;
    ctmp->tool      = t_tool;
    sprintf(tmp, "%-*s =", pindent-3, recip_pat_var.name);
    ctmp->varname   = cpystr(tmp);
    ctmp->varnamep  = ctmp;
    ctmp->value     = pretty_value(ps, ctmp);
    ctmp->d.earb    = &earb;
    ctmp->varmem    = -1;

    /* Participant Pattern */
    new_confline(&ctmp);
    ctmp->help_title= "HELP FOR PARTICIPANT PATTERN";
    ctmp->var       = &partic_pat_var;
    ctmp->valoffset = pindent;
    ctmp->keymenu   = &config_role_addr_pat_keymenu;
    ctmp->help      = h_config_role_particpat;
    ctmp->tool      = t_tool;
    sprintf(tmp, "%-*s =", pindent-3, partic_pat_var.name);
    ctmp->varname   = cpystr(tmp);
    ctmp->varnamep  = ctmp;
    ctmp->value     = pretty_value(ps, ctmp);
    ctmp->d.earb    = &earb;
    ctmp->varmem    = -1;

    /* Arbitrary Patterns */
    ea = NULL;
    for(j = 0, a = (def && def->patgrp) ? def->patgrp->arbhdr : NULL;
	a;
	j++, a = a->next){
	char *fn = (a->field) ? a->field : "";

	if(ea){
	    ea->next = (EARB_S *)fs_get(sizeof(*ea));
	    ea = ea->next;
	}
	else{
	    earb = (EARB_S *)fs_get(sizeof(*ea));
	    ea = earb;
	}

	memset((void *)ea, 0, sizeof(*ea));
	ea->v = (struct variable *)fs_get(sizeof(struct variable));
	memset((void *)ea->v, 0, sizeof(struct variable));
	ea->a = (ARBHDR_S *)fs_get(sizeof(ARBHDR_S));
	memset((void *)ea->a, 0, sizeof(ARBHDR_S));

	ea->a->field = cpystr(fn);

	new_confline(&ctmp);
	ea->v->name = (char *)fs_get(strlen(fn) + 9);
	sprintf(ea->v->name, "%s pattern", fn);
	ea->v->is_used    = 1;
	ea->v->is_user    = 1;
	ea->v->user_val.p = pattern_to_string(a->p);
	set_current_val(ea->v, FALSE, FALSE);

	ctmp->help_title= ARB_HELP;
	ctmp->var       = ea->v;
	ctmp->valoffset = pindent;
	ctmp->keymenu   = &config_role_xtrahdr_keymenu;
	ctmp->help      = h_config_role_arbpat;
	ctmp->tool      = t_tool;
	sprintf(tmp, "%-*s =", pindent-3, ea->v->name);
	ctmp->varname   = cpystr(tmp);
	ctmp->varnamep  = ctmp;
	ctmp->value     = pretty_value(ps, ctmp);
	ctmp->d.earb    = &earb;
	ctmp->varmem    = j;
    }

    /* AllText Pattern */
    new_confline(&ctmp);
    ctmp->help_title= "HELP FOR ALL TEXT PATTERN";
    ctmp->var       = &alltext_pat_var;
    ctmp->valoffset = pindent;
    ctmp->keymenu   = &config_role_keymenu;
    ctmp->help      = h_config_role_alltextpat;
    ctmp->tool      = t_tool;
    sprintf(tmp, "%-*s =", pindent-3, alltext_pat_var.name);
    ctmp->varname   = cpystr(tmp);
    ctmp->varnamep  = ctmp;
    ctmp->value     = pretty_value(ps, ctmp);
    ctmp->varmem    = -1;

    if(!(edit_score && !edit_all)){
	/* Score Interval */
	new_confline(&ctmp);
	ctmp->help_title= "HELP FOR SCORE INTERVAL";
	ctmp->var       = &scorei_pat_var;
	ctmp->valoffset = pindent;
	ctmp->keymenu   = &config_text_keymenu;
	ctmp->help      = h_config_role_scorei;
	ctmp->tool      = t_tool;
	sprintf(tmp, "%-*s =", pindent-3, scorei_pat_var.name);
	ctmp->varname   = cpystr(tmp);
	ctmp->varnamep  = ctmp;
	ctmp->value     = pretty_value(ps, ctmp);
    }

    /* Blank line */
    new_confline(&ctmp);
    ctmp->flags    |= CF_NOSELECT | CF_B_LINE;

    /* Folder Type */
    new_confline(&ctmp);
    ctmp->var       = &fldr_type_var;
    ctmp->valoffset = indent;
    ctmp->keymenu   = &config_radiobutton_keymenu;
    ctmp->help      = NO_HELP;
    ctmp->tool      = NULL;
    sprintf(tmp, "%-*s =", indent-3, fldr_type_var.name);
    ctmp->varname   = cpystr(tmp);
    ctmp->varnamep  = ctmpb = ctmp;
    ctmp->flags    |= (CF_NOSELECT | CF_STARTITEM);

    new_confline(&ctmp);
    ctmp->var       = NULL;
    ctmp->valoffset = 12;
    ctmp->keymenu   = &config_radiobutton_keymenu;
    ctmp->help      = NO_HELP;
    ctmp->tool      = NULL;
    ctmp->varnamep  = ctmpb;
    ctmp->flags    |= CF_NOSELECT;
    ctmp->value     = cpystr("Set    Choose One");

    new_confline(&ctmp);
    ctmp->var       = NULL;
    ctmp->valoffset = 12;
    ctmp->keymenu   = &config_radiobutton_keymenu;
    ctmp->help      = NO_HELP;
    ctmp->tool      = radio_tool;
    ctmp->varnamep  = ctmpb;
    ctmp->flags    |= CF_NOSELECT;
    ctmp->value     = cpystr("---  --------------------");

    /* find longest value's name */
    for(lv = 0, i = 0; f = pat_fldr_types(i); i++)
      if(lv < (j = strlen(f->name)))
	lv = j;
    
    for(i = 0; f = pat_fldr_types(i); i++){
	new_confline(&ctmp);
	ctmp->help_title= "HELP FOR ROLE FOLDER TYPE";
	ctmp->var       = &fldr_type_var;
	ctmp->valoffset = 12;
	ctmp->keymenu   = &config_radiobutton_keymenu;
	ctmp->help      = edit_role ? h_config_role_fldr_type :
			   edit_incol ? h_config_incol_fldr_type :
			    edit_score ? h_config_score_fldr_type
				       : h_config_filt_fldr_type;
	ctmp->varmem    = i;
	ctmp->tool      = radio_tool;
	ctmp->varnamep  = ctmpb;
	sprintf(tmp, "(%c)  %-*.*s",
		((fldr_type_var.user_val.p &&
		  !strucmp(fldr_type_var.user_val.p, f->name)) ||
		 (!fldr_type_var.user_val.p && f->value == FLDR_DEFL))
		  ? R_SELD : ' ',
		lv, lv, f->name);
	ctmp->value     = cpystr(tmp);
    }

    /* Folder */
    new_confline(&ctmp);
    ctmp->help_title= "HELP FOR FOLDER";
    ctmp->var       = &folder_pat_var;
    ctmp->varoffset = 12+5;
    ctmp->valoffset = 12+5+strlen(folder_pat_var.name)+3;
    ctmp->keymenu   = &config_role_patfolder_keymenu;
    ctmp->help      = edit_role ? h_config_role_fldr_type :
		       edit_incol ? h_config_incol_fldr_type :
			edit_score ? h_config_score_fldr_type
				   : h_config_filt_fldr_type;
    ctmp->tool      = t_tool;
    ctmp->varnamep  = ctmpb;
    sprintf(tmp, "%s =", folder_pat_var.name);
    ctmp->varname   = cpystr(tmp);
    ctmp->value     = pretty_value(ps, ctmp);
    if(def && def->patgrp && def->patgrp->fldr_type != FLDR_SPECIFIC)
      ctmp->flags |= CF_NOSELECT;

    if(edit_role){
	/* Blank line */
	new_confline(&ctmp);
	ctmp->flags    |= CF_NOSELECT | CF_B_LINE;

	/* Inherit Nickname */
	new_confline(&ctmp);
	inick_confs[INICK_INICK_CONF] = ctmp;
	ctmp->help_title= "HELP FOR INITIAL SET NICKNAME";
	ctmp->var       = &inick_var;
	ctmp->valoffset = 33;
	ctmp->keymenu   = &config_role_inick_keymenu;
	ctmp->help      = h_config_role_inick;
	ctmp->tool      = role_text_tool_inick;
	sprintf(tmp, "%-30s :", inick_var.name);
	ctmp->varname   = cpystr(tmp);
	ctmp->varnamep  = ctmp;

	/* From Action */
	new_confline(&ctmp);
	inick_confs[INICK_FROM_CONF] = ctmp;
	ctmp->help_title= "HELP FOR SET FROM ACTION";
	ctmp->var       = &from_act_var;
	ctmp->valoffset = indent;
	ctmp->keymenu   = &config_role_addr_act_keymenu;
	ctmp->help      = h_config_role_setfrom;
	ctmp->tool      = role_text_tool;
	sprintf(tmp, "%-*s =", indent-3, from_act_var.name);
	ctmp->varname   = cpystr(tmp);
	ctmp->varnamep  = ctmp;

	/* Reply-To Action */
	new_confline(&ctmp);
	inick_confs[INICK_REPLYTO_CONF] = ctmp;
	ctmp->help_title= "HELP FOR SET REPLY-TO ACTION";
	ctmp->var       = &replyto_act_var;
	ctmp->valoffset = indent;
	ctmp->keymenu   = &config_role_addr_act_keymenu;
	ctmp->help      = h_config_role_setreplyto;
	ctmp->tool      = role_text_tool;
	sprintf(tmp, "%-*s =", indent-3, replyto_act_var.name);
	ctmp->varname   = cpystr(tmp);
	ctmp->varnamep  = ctmp;

	/* Fcc Action */
	new_confline(&ctmp);
	inick_confs[INICK_FCC_CONF] = ctmp;
	ctmp->help_title= "HELP FOR SET FCC ACTION";
	ctmp->var       = &fcc_act_var;
	ctmp->valoffset = indent;
	ctmp->keymenu   = &config_role_actionfolder_keymenu;
	ctmp->help      = h_config_role_setfcc;
	ctmp->tool      = role_text_tool;
	sprintf(tmp, "%-*s =", indent-3, fcc_act_var.name);
	ctmp->varname   = cpystr(tmp);
	ctmp->varnamep  = ctmp;

	/* Sig Action */
	new_confline(&ctmp);
	inick_confs[INICK_SIG_CONF] = ctmp;
	ctmp->help_title= "HELP FOR SET SIGNATURE ACTION";
	ctmp->var       = &sig_act_var;
	ctmp->valoffset = indent;
	if(F_ON(F_DISABLE_ROLES_SIGEDIT, ps_global))
	  ctmp->keymenu   = &config_role_file_res_keymenu;
	else
	  ctmp->keymenu   = &config_role_file_keymenu;

	ctmp->help      = h_config_role_setsig;
	ctmp->tool      = role_text_tool;
	sprintf(tmp, "%-*s =", indent-3, sig_act_var.name);
	ctmp->varname   = cpystr(tmp);
	ctmp->varnamep  = ctmp;

	/* Template Action */
	new_confline(&ctmp);
	inick_confs[INICK_TEMPL_CONF] = ctmp;
	ctmp->help_title= "HELP FOR SET TEMPLATE ACTION";
	ctmp->var       = &templ_act_var;
	ctmp->valoffset = indent;
	if(F_ON(F_DISABLE_ROLES_TEMPLEDIT, ps_global))
	  ctmp->keymenu   = &config_role_file_res_keymenu;
	else
	  ctmp->keymenu   = &config_role_file_keymenu;

	ctmp->help      = h_config_role_settempl;
	ctmp->tool      = role_text_tool;
	sprintf(tmp, "%-*s =", indent-3, templ_act_var.name);
	ctmp->varname   = cpystr(tmp);
	ctmp->varnamep  = ctmp;

	calculate_inick_stuff(ps);
    }

    if(edit_score){
	/* Blank line */
	new_confline(&ctmp);
	ctmp->flags    |= CF_NOSELECT | CF_B_LINE;

	/* Score Value -- This doesn't inherit from inick */
	new_confline(&ctmp);
	ctmp->help_title= "HELP FOR SCORE VALUE ACTION";
	ctmp->var       = &score_act_var;
	ctmp->valoffset = indent;
	ctmp->keymenu   = &config_text_keymenu;
	ctmp->help      = h_config_role_scoreval;
	ctmp->tool      = text_tool;
	ctmp->flags    |= CF_NUMBER;
	sprintf(tmp, "%-*s =", indent-3, score_act_var.name);
	ctmp->varname   = cpystr(tmp);
	ctmp->varnamep  = ctmp;
	ctmp->value     = pretty_value(ps, ctmp);
    }

    if(edit_role){
	/* Blank line */
	new_confline(&ctmp);
	ctmp->flags    |= CF_NOSELECT | CF_B_LINE;

	/* Reply Type */
	new_confline(&ctmp);
	ctmp->var       = &repl_type_var;
	ctmp->valoffset = indent;
	ctmp->keymenu   = &config_radiobutton_keymenu;
	ctmp->help      = NO_HELP;
	ctmp->tool      = NULL;
	sprintf(tmp, "%-*s =", indent-3, repl_type_var.name);
	ctmp->varname   = cpystr(tmp);
	ctmp->varnamep  = ctmpb = ctmp;
	ctmp->flags    |= (CF_NOSELECT | CF_STARTITEM);

	new_confline(&ctmp);
	ctmp->var       = NULL;
	ctmp->valoffset = 12;
	ctmp->keymenu   = &config_radiobutton_keymenu;
	ctmp->help      = NO_HELP;
	ctmp->tool      = NULL;
	ctmp->varnamep  = ctmpb;
	ctmp->flags    |= CF_NOSELECT;
	ctmp->value     = cpystr("Set    Choose One");

	new_confline(&ctmp);
	ctmp->var       = NULL;
	ctmp->valoffset = 12;
	ctmp->keymenu   = &config_radiobutton_keymenu;
	ctmp->help      = NO_HELP;
	ctmp->tool      = radio_tool;
	ctmp->varnamep  = ctmpb;
	ctmp->flags    |= CF_NOSELECT;
	ctmp->value     = cpystr("---  --------------------");

	/* find longest value's name */
	for(lv = 0, i = 0; f = role_repl_types(i); i++)
	  if(lv < (j = strlen(f->name)))
	    lv = j;
	
	for(i = 0; f = role_repl_types(i); i++){
	    new_confline(&ctmp);
	    ctmp->help_title= "HELP FOR ROLE REPLY USE";
	    ctmp->var       = &repl_type_var;
	    ctmp->valoffset = 12;
	    ctmp->keymenu   = &config_radiobutton_keymenu;
	    ctmp->help      = h_config_role_replyuse;
	    ctmp->varmem    = i;
	    ctmp->tool      = radio_tool;
	    ctmp->varnamep  = ctmpb;
	    sprintf(tmp, "(%c)  %-*.*s", (((!(def && def->action) ||
					     def->action->repl_type == -1) &&
					    f->value == ROLE_REPL_DEFL) ||
					  (def && def->action &&
					  f->value == def->action->repl_type))
					     ? R_SELD : ' ',
		    lv, lv, f->name);
	    ctmp->value     = cpystr(tmp);
	}

	/* Blank line */
	new_confline(&ctmp);
	ctmp->flags    |= CF_NOSELECT | CF_B_LINE;

	/* Forward Type */
	new_confline(&ctmp);
	ctmp->var       = &forw_type_var;
	ctmp->valoffset = indent;
	ctmp->keymenu   = &config_radiobutton_keymenu;
	ctmp->help      = NO_HELP;
	ctmp->tool      = NULL;
	sprintf(tmp, "%-*s =", indent-3, forw_type_var.name);
	ctmp->varname   = cpystr(tmp);
	ctmp->varnamep  = ctmpb = ctmp;
	ctmp->flags    |= (CF_NOSELECT | CF_STARTITEM);

	new_confline(&ctmp);
	ctmp->var       = NULL;
	ctmp->valoffset = 12;
	ctmp->keymenu   = &config_radiobutton_keymenu;
	ctmp->help      = NO_HELP;
	ctmp->tool      = NULL;
	ctmp->varnamep  = ctmpb;
	ctmp->flags    |= CF_NOSELECT;
	ctmp->value     = cpystr("Set    Choose One");

	new_confline(&ctmp);
	ctmp->var       = NULL;
	ctmp->valoffset = 12;
	ctmp->keymenu   = &config_radiobutton_keymenu;
	ctmp->help      = NO_HELP;
	ctmp->tool      = radio_tool;
	ctmp->varnamep  = ctmpb;
	ctmp->flags    |= CF_NOSELECT;
	ctmp->value     = cpystr("---  --------------------");

	/* find longest value's name */
	for(lv = 0, i = 0; f = role_forw_types(i); i++)
	  if(lv < (j = strlen(f->name)))
	    lv = j;
	
	for(i = 0; f = role_forw_types(i); i++){
	    new_confline(&ctmp);
	    ctmp->help_title= "HELP FOR ROLE FORWARD USE";
	    ctmp->var       = &forw_type_var;
	    ctmp->valoffset = 12;
	    ctmp->keymenu   = &config_radiobutton_keymenu;
	    ctmp->help      = h_config_role_forwarduse;
	    ctmp->varmem    = i;
	    ctmp->tool      = radio_tool;
	    ctmp->varnamep  = ctmpb;
	    sprintf(tmp, "(%c)  %-*.*s", (((!(def && def->action) ||
					     def->action->forw_type == -1) &&
					    f->value == ROLE_FORW_DEFL) ||
					  (def && def->action &&
					  f->value == def->action->forw_type))
					     ? R_SELD : ' ',
		    lv, lv, f->name);
	    ctmp->value     = cpystr(tmp);
	}

	/* Blank line */
	new_confline(&ctmp);
	ctmp->flags    |= CF_NOSELECT | CF_B_LINE;

	/* Compose Type */
	new_confline(&ctmp);
	ctmp->var       = &comp_type_var;
	ctmp->valoffset = indent;
	ctmp->keymenu   = &config_radiobutton_keymenu;
	ctmp->help      = NO_HELP;
	ctmp->tool      = NULL;
	sprintf(tmp, "%-*s =", indent-3, comp_type_var.name);
	ctmp->varname   = cpystr(tmp);
	ctmp->varnamep  = ctmpb = ctmp;
	ctmp->flags    |= (CF_NOSELECT | CF_STARTITEM);

	new_confline(&ctmp);
	ctmp->var       = NULL;
	ctmp->valoffset = 12;
	ctmp->keymenu   = &config_radiobutton_keymenu;
	ctmp->help      = NO_HELP;
	ctmp->tool      = NULL;
	ctmp->varnamep  = ctmpb;
	ctmp->flags    |= CF_NOSELECT;
	ctmp->value     = cpystr("Set    Choose One");

	new_confline(&ctmp);
	ctmp->var       = NULL;
	ctmp->valoffset = 12;
	ctmp->keymenu   = &config_radiobutton_keymenu;
	ctmp->help      = NO_HELP;
	ctmp->tool      = radio_tool;
	ctmp->varnamep  = ctmpb;
	ctmp->flags    |= CF_NOSELECT;
	ctmp->value     = cpystr("---  --------------------");

	/* find longest value's name */
	for(lv = 0, i = 0; f = role_comp_types(i); i++)
	  if(lv < (j = strlen(f->name)))
	    lv = j;
	
	for(i = 0; f = role_comp_types(i); i++){
	    new_confline(&ctmp);
	    ctmp->help_title= "HELP FOR ROLE COMPOSE USE";
	    ctmp->var       = &comp_type_var;
	    ctmp->valoffset = 12;
	    ctmp->keymenu   = &config_radiobutton_keymenu;
	    ctmp->help      = h_config_role_composeuse;
	    ctmp->varmem    = i;
	    ctmp->tool      = radio_tool;
	    ctmp->varnamep  = ctmpb;
	    sprintf(tmp, "(%c)  %-*.*s", (((!(def && def->action) ||
					     def->action->comp_type == -1) &&
					    f->value == ROLE_COMP_DEFL) ||
					  (def && def->action &&
					  f->value == def->action->comp_type))
					     ? R_SELD : ' ',
		    lv, lv, f->name);
	    ctmp->value     = cpystr(tmp);
	}
    }

    if(edit_filter){
	role_filt_ptr = &filter_type_var;	/* so radiobuttons can tell */
	filter_type_var.name       = cpystr("Filter Action");
	filter_type_var.is_used    = 1;
	filter_type_var.is_user    = 1;
	filter_type_var.user_val.p = (f=filter_types((def && def->action && !def->action->folder) ? FILTER_KILL : FILTER_FOLDER)) ? cpystr(f->name) : NULL;
	set_current_val(&filter_type_var, FALSE, FALSE);

	/* Blank line */
	new_confline(&ctmp);
	ctmp->flags    |= CF_NOSELECT | CF_B_LINE;

	/* Filter Type */
	new_confline(&ctmp);
	ctmp->var       = &filter_type_var;
	ctmp->valoffset = indent;
	ctmp->keymenu   = &config_radiobutton_keymenu;
	ctmp->help      = NO_HELP;
	ctmp->tool      = NULL;
	sprintf(tmp, "%-*s =", indent-3, filter_type_var.name);
	ctmp->varname   = cpystr(tmp);
	ctmp->varnamep  = ctmpb = ctmp;
	ctmp->flags    |= (CF_NOSELECT | CF_STARTITEM);

	new_confline(&ctmp);
	ctmp->var       = NULL;
	ctmp->valoffset = 12;
	ctmp->keymenu   = &config_radiobutton_keymenu;
	ctmp->help      = NO_HELP;
	ctmp->tool      = NULL;
	ctmp->varnamep  = ctmpb;
	ctmp->flags    |= CF_NOSELECT;
	ctmp->value     = cpystr("Set    Choose One");

	new_confline(&ctmp);
	ctmp->var       = NULL;
	ctmp->valoffset = 12;
	ctmp->keymenu   = &config_radiobutton_keymenu;
	ctmp->help      = NO_HELP;
	ctmp->tool      = radio_tool;
	ctmp->varnamep  = ctmpb;
	ctmp->flags    |= CF_NOSELECT;
	ctmp->value     = cpystr("---  --------------------");

	/* find longest value's name */
	for(lv = 0, i = 0; f = filter_types(i); i++)
	  if(lv < (j = strlen(f->name)))
	    lv = j;
	
	for(i = 0; f = filter_types(i); i++){
	    new_confline(&ctmp);
	    ctmp->help_title= "HELP FOR FILTER ACTION";
	    ctmp->var       = &filter_type_var;
	    ctmp->valoffset = 12;
	    ctmp->keymenu   = &config_radiobutton_keymenu;
	    ctmp->help      = h_config_filt_rule_type;
	    ctmp->varmem    = i;
	    ctmp->tool      = radio_tool;
	    ctmp->varnamep  = ctmpb;
	    sprintf(tmp, "(%c)  %-*.*s", ((f->value == FILTER_KILL && 
					   (def && def->action &&
					    !def->action->folder)) ||
					  (f->value == FILTER_FOLDER &&
					   !(def && def->action &&
					     !def->action->folder)))
					     ? R_SELD : ' ',
		    lv, lv, f->name);
	    ctmp->value     = cpystr(tmp);
	}

	folder_act_var.name       = cpystr("to Folder");
	folder_act_var.is_used    = 1;
	folder_act_var.is_user    = 1;
	folder_act_var.user_val.p = (def && def->action)
				      ? pattern_to_string(def->action->folder)
				      : NULL;
	set_current_val(&folder_act_var, FALSE, FALSE);

	/* Folder */
	new_confline(&ctmp);
	ctmp->help_title= "HELP FOR FILTER FOLDER NAME";
	ctmp->var       = &folder_act_var;
	ctmp->varoffset = 12+5;
	ctmp->valoffset = 12+5+strlen(folder_act_var.name)+3;
	ctmp->keymenu   = &config_role_actionfolder_keymenu;
	ctmp->help      = h_config_filter_folder;
	ctmp->tool      = t_tool;
	ctmp->varnamep  = ctmpb;
	sprintf(tmp, "%s =", folder_act_var.name);
	ctmp->varname   = cpystr(tmp);
	ctmp->varnamep  = ctmp;
	ctmp->value     = pretty_value(ps, ctmp);
	if(def && def->action && !def->action->folder)
	  ctmp->flags |= CF_NOSELECT;
    }

    if(edit_incol && pico_usingcolor()){
	int def;

	indent = 12;

	/* Blank line */
	new_confline(&ctmp);
	ctmp->flags    |= CF_NOSELECT | CF_B_LINE;

	new_confline(&ctmp);
	ctmp->var		 = &rolecolor_vars[0];	/* foreground */
	ctmp->varname		 = cpystr("Index Line Color    =");
	ctmp->varnamep		 = ctmpb = ctmp;
	ctmp->flags		|= (CF_STARTITEM | CF_NOSELECT);
	ctmp->keymenu		 = &role_color_setting_keymenu;

	def = !(rolecolor_vars[0].user_val.p &&
		rolecolor_vars[0].user_val.p[0] &&
		rolecolor_vars[1].user_val.p &&
		rolecolor_vars[1].user_val.p[0]);

	add_color_setting_disp(&ctmp, &rolecolor_vars[0], ctmpb,
			       &role_color_setting_keymenu,
			       &config_checkbox_keymenu,
			       h_config_incol,
			       indent, 0,
			       def ? ps->VAR_NORM_FORE_COLOR
				   : rolecolor_vars[0].current_val.p,
			       def ? ps->VAR_NORM_BACK_COLOR
			           : rolecolor_vars[1].current_val.p,
			       def);
    }

    rv = conf_scroll_screen(ps, &screen, first_line, title, "roles ", 1, 1);

    /*
     * Now look at the fake variables and extract the information we
     * want from them.
     */

    if(rv == 1 && result){
	nick = nick_var.user_val.p;
	nick_var.user_val.p = NULL;
	removing_leading_and_trailing_white_space(nick);
	to_pat = to_pat_var.user_val.p;
	to_pat_var.user_val.p = NULL;
	removing_leading_and_trailing_white_space(to_pat);
	from_pat = from_pat_var.user_val.p;
	from_pat_var.user_val.p = NULL;
	removing_leading_and_trailing_white_space(from_pat);
	sender_pat = sender_pat_var.user_val.p;
	sender_pat_var.user_val.p = NULL;
	removing_leading_and_trailing_white_space(sender_pat);
	cc_pat = cc_pat_var.user_val.p;
	cc_pat_var.user_val.p = NULL;
	removing_leading_and_trailing_white_space(cc_pat);
	recip_pat = recip_pat_var.user_val.p;
	recip_pat_var.user_val.p = NULL;
	removing_leading_and_trailing_white_space(recip_pat);
	partic_pat = partic_pat_var.user_val.p;
	partic_pat_var.user_val.p = NULL;
	removing_leading_and_trailing_white_space(partic_pat);
	news_pat = news_pat_var.user_val.p;
	news_pat_var.user_val.p = NULL;
	removing_leading_and_trailing_white_space(news_pat);
	subj_pat = subj_pat_var.user_val.p;
	subj_pat_var.user_val.p = NULL;
	removing_leading_and_trailing_white_space(subj_pat);
	alltext_pat = alltext_pat_var.user_val.p;
	alltext_pat_var.user_val.p = NULL;
	removing_leading_and_trailing_white_space(alltext_pat);
	scorei_pat = scorei_pat_var.user_val.p;
	scorei_pat_var.user_val.p = NULL;
	removing_leading_and_trailing_white_space(scorei_pat);
	fldr_type_pat = fldr_type_var.user_val.p;
	fldr_type_var.user_val.p = NULL;
	removing_leading_and_trailing_white_space(fldr_type_pat);
	folder_pat = folder_pat_var.user_val.p;
	folder_pat_var.user_val.p = NULL;
	removing_leading_and_trailing_white_space(folder_pat);
	inick = inick_var.user_val.p;
	inick_var.user_val.p = NULL;
	removing_leading_and_trailing_white_space(inick);
	from_act = from_act_var.user_val.p;
	from_act_var.user_val.p = NULL;
	removing_leading_and_trailing_white_space(from_act);
	replyto_act = replyto_act_var.user_val.p;
	replyto_act_var.user_val.p = NULL;
	removing_leading_and_trailing_white_space(replyto_act);
	fcc_act = fcc_act_var.user_val.p;
	fcc_act_var.user_val.p = NULL;
	removing_leading_and_trailing_white_space(fcc_act);
	sig_act = sig_act_var.user_val.p;
	sig_act_var.user_val.p = NULL;
	removing_leading_and_trailing_white_space(sig_act);
	templ_act = templ_act_var.user_val.p;
	templ_act_var.user_val.p = NULL;
	removing_leading_and_trailing_white_space(templ_act);
	score_act = score_act_var.user_val.p;
	score_act_var.user_val.p = NULL;
	removing_leading_and_trailing_white_space(score_act);
	repl_type = repl_type_var.user_val.p;
	repl_type_var.user_val.p = NULL;
	removing_leading_and_trailing_white_space(repl_type);
	forw_type = forw_type_var.user_val.p;
	forw_type_var.user_val.p = NULL;
	removing_leading_and_trailing_white_space(forw_type);
	comp_type = comp_type_var.user_val.p;
	comp_type_var.user_val.p = NULL;
	removing_leading_and_trailing_white_space(comp_type);
	rc_fg = rolecolor_vars[0].user_val.p;
	rolecolor_vars[0].user_val.p = NULL;
	removing_leading_and_trailing_white_space(rc_fg);
	rc_bg = rolecolor_vars[1].user_val.p;
	rolecolor_vars[1].user_val.p = NULL;
	removing_leading_and_trailing_white_space(rc_bg);
	filter_type = filter_type_var.user_val.p;
	filter_type_var.user_val.p = NULL;
	removing_leading_and_trailing_white_space(filter_type);
	folder_act = folder_act_var.user_val.p;
	folder_act_var.user_val.p = NULL;
	removing_leading_and_trailing_white_space(folder_act);

	if(ps->VAR_OPER_DIR && sig_act &&
	   is_absolute_path(sig_act) &&
	   !in_dir(ps->VAR_OPER_DIR, sig_act)){
	    q_status_message1(SM_ORDER | SM_DING, 3, 4,
			      "Warning: Sig file can't be outside of %s",
			      ps->VAR_OPER_DIR);
	}

	if(ps->VAR_OPER_DIR && templ_act &&
	   is_absolute_path(templ_act) &&
	   !in_dir(ps->VAR_OPER_DIR, templ_act)){
	    q_status_message1(SM_ORDER | SM_DING, 3, 4,
			    "Warning: Template file can't be outside of %s",
			    ps->VAR_OPER_DIR);
	}

	if(ps->VAR_OPER_DIR && folder_act &&
	   is_absolute_path(folder_act) &&
	   !in_dir(ps->VAR_OPER_DIR, folder_act)){
	    q_status_message1(SM_ORDER | SM_DING, 3, 4,
			      "Warning: Folder can't be outside of %s",
			      ps->VAR_OPER_DIR);
	}

	*result = (PAT_S *)fs_get(sizeof(**result));
	memset((void *)(*result), 0, sizeof(**result));

	(*result)->patgrp = (PATGRP_S *)fs_get(sizeof(*(*result)->patgrp));
	memset((void *)(*result)->patgrp, 0, sizeof(*(*result)->patgrp));

	(*result)->action = (ACTION_S *)fs_get(sizeof(*(*result)->action));
	memset((void *)(*result)->action, 0, sizeof(*(*result)->action));

	(*result)->patline = def ? def->patline : NULL;
	
	if(nick && *nick){
	    (*result)->patgrp->nick = nick;
	    nick = NULL;
	}
	else
	  (*result)->patgrp->nick = cpystr(nick_var.global_val.p);

	(*result)->action->nick = cpystr((*result)->patgrp->nick);

	(*result)->action->is_a_role  = edit_role  ? 1 : 0;
	(*result)->action->is_a_incol = edit_incol ? 1 : 0;
	(*result)->action->is_a_score = edit_score ? 1 : 0;
	(*result)->action->is_a_filter = edit_filter ? 1 : 0;

	(*result)->patgrp->to      = string_to_pattern(to_pat);
	(*result)->patgrp->from    = string_to_pattern(from_pat);
	(*result)->patgrp->sender  = string_to_pattern(sender_pat);
	(*result)->patgrp->cc      = string_to_pattern(cc_pat);
	(*result)->patgrp->recip   = string_to_pattern(recip_pat);
	(*result)->patgrp->partic  = string_to_pattern(partic_pat);
	(*result)->patgrp->news    = string_to_pattern(news_pat);
	(*result)->patgrp->subj    = string_to_pattern(subj_pat);
	(*result)->patgrp->alltext = string_to_pattern(alltext_pat);

	if(scorei_pat){
	    int left, right;

	    if(parse_score_interval(scorei_pat, &left, &right)){
		(*result)->patgrp->do_score  = 1;
		(*result)->patgrp->score_min = left;
		(*result)->patgrp->score_max = right;
	    }
	}

	aa = NULL;
	for(ea = earb; ea; ea = ea->next){
	    char *xyz;

	    if(aa){
		aa->next = (ARBHDR_S *)fs_get(sizeof(*aa));
		aa = aa->next;
	    }
	    else{
		(*result)->patgrp->arbhdr =
				      (ARBHDR_S *)fs_get(sizeof(ARBHDR_S));
		aa = (*result)->patgrp->arbhdr;
	    }

	    memset(aa, 0, sizeof(*aa));

	    aa->field = cpystr((ea->a && ea->a->field) ? ea->a->field : "");

	    spat = ea->v->user_val.p;
	    removing_leading_and_trailing_white_space(spat);
	    removing_double_quotes(spat);
	    aa->p = string_to_pattern(spat);
	    if((xyz = pattern_to_string(aa->p)) != NULL){
		if(!*xyz)
		  aa->isemptyval = 1;
		
		fs_give((void **)&xyz);
	    }
	}

	if(fldr_type_pat && *fldr_type_pat){
	    for(j = 0; f = pat_fldr_types(j); j++)
	      if(!strucmp(fldr_type_pat, f->name)){
		  (*result)->patgrp->fldr_type = f->value;
		  break;
	      }
	}
	else{
	    f = pat_fldr_types(FLDR_DEFL);
	    if(f)
	      (*result)->patgrp->fldr_type = f->value;
	}

	(*result)->patgrp->folder = string_to_pattern(folder_pat);

	(*result)->action->inherit_nick = inick;
	inick = NULL;
	(*result)->action->fcc = fcc_act;
	fcc_act = NULL;
	(*result)->action->sig = sig_act;
	sig_act = NULL;
	(*result)->action->template = templ_act;
	templ_act = NULL;

	if(filter_type && *filter_type && folder_act && *folder_act){
	    for(j = 0; f = filter_types(j); j++)
	      if(!strucmp(filter_type, f->name)){
		  if(f->value == FILTER_FOLDER)
		    (*result)->action->folder = string_to_pattern(folder_act);

		  break;
	      }
	}

	if(from_act && *from_act)
	  rfc822_parse_adrlist(&(*result)->action->from, from_act,
			       ps->maildomain);

	if(replyto_act && *replyto_act)
	  rfc822_parse_adrlist(&(*result)->action->replyto, replyto_act,
			       ps->maildomain);

	if(score_act && (j = atoi(score_act)) >= SCORE_MIN && j <= SCORE_MAX)
	  (*result)->action->scoreval = j;

	if(repl_type && *repl_type){
	    for(j = 0; f = role_repl_types(j); j++)
	      if(!strucmp(repl_type, f->name)){
		  (*result)->action->repl_type = f->value;
		  break;
	      }
	}
	else{
	    f = role_repl_types(ROLE_REPL_DEFL);
	    if(f)
	      (*result)->action->repl_type = f->value;
	}

	if(forw_type && *forw_type){
	    for(j = 0; f = role_forw_types(j); j++)
	      if(!strucmp(forw_type, f->name)){
		  (*result)->action->forw_type = f->value;
		  break;
	      }
	}
	else{
	    f = role_forw_types(ROLE_FORW_DEFL);
	    if(f)
	      (*result)->action->forw_type = f->value;
	}

	if(comp_type && *comp_type){
	    for(j = 0; f = role_comp_types(j); j++)
	      if(!strucmp(comp_type, f->name)){
		  (*result)->action->comp_type = f->value;
		  break;
	      }
	}
	else{
	    f = role_comp_types(ROLE_COMP_DEFL);
	    if(f)
	      (*result)->action->comp_type = f->value;
	}

	if(rc_fg && *rc_fg && rc_bg && *rc_bg){
	    if(!old_fg || !old_bg || strucmp(old_fg, rc_fg) ||
	       strucmp(old_bg, rc_bg))
	      clear_index_cache();

	    /*
	     * If same as normal color, don't set it. This may or may
	     * not surprise the user when they change the normal color.
	     * This color will track the normal color instead of staying
	     * the same as the old normal color, which is probably
	     * what they want.
	     */
	    if(!ps_global->VAR_NORM_FORE_COLOR ||
	       !ps_global->VAR_NORM_BACK_COLOR ||
	       strucmp(ps_global->VAR_NORM_FORE_COLOR, rc_fg) ||
	       strucmp(ps_global->VAR_NORM_BACK_COLOR, rc_bg))
	      (*result)->action->incol = new_color_pair(rc_fg, rc_bg);
	}
    }

    for(j = 0; varlist[j]; j++){
	v = varlist[j];
	if(v->current_val.p)
	  fs_give((void **)&v->current_val.p);
	if(v->global_val.p)
	  fs_give((void **)&v->global_val.p);
	if(v->user_val.p)
	  fs_give((void **)&v->user_val.p);
	if(v->name)
	  fs_give((void **)&v->name);
    }

    if(earb)
      free_earb(&earb);
    if(nick)
      fs_give((void **)&nick);
    if(to_pat)
      fs_give((void **)&to_pat);
    if(from_pat)
      fs_give((void **)&from_pat);
    if(sender_pat)
      fs_give((void **)&sender_pat);
    if(cc_pat)
      fs_give((void **)&cc_pat);
    if(recip_pat)
      fs_give((void **)&recip_pat);
    if(partic_pat)
      fs_give((void **)&partic_pat);
    if(news_pat)
      fs_give((void **)&news_pat);
    if(subj_pat)
      fs_give((void **)&subj_pat);
    if(alltext_pat)
      fs_give((void **)&alltext_pat);
    if(scorei_pat)
      fs_give((void **)&scorei_pat);
    if(fldr_type_pat)
      fs_give((void **)&fldr_type_pat);
    if(folder_pat)
      fs_give((void **)&folder_pat);
    if(inick)
      fs_give((void **)&inick);
    if(from_act)
      fs_give((void **)&from_act);
    if(replyto_act)
      fs_give((void **)&replyto_act);
    if(fcc_act)
      fs_give((void **)&fcc_act);
    if(sig_act)
      fs_give((void **)&sig_act);
    if(templ_act)
      fs_give((void **)&templ_act);
    if(score_act)
      fs_give((void **)&score_act);
    if(repl_type)
      fs_give((void **)&repl_type);
    if(forw_type)
      fs_give((void **)&forw_type);
    if(comp_type)
      fs_give((void **)&comp_type);
    if(rc_fg)
      fs_give((void **)&rc_fg);
    if(rc_bg)
      fs_give((void **)&rc_bg);
    if(old_fg)
      fs_give((void **)&old_fg);
    if(old_bg)
      fs_give((void **)&old_bg);
    if(folder_act)
      fs_give((void **)&folder_act);

    opt_screen = saved_screen;
    ps->mangled_screen = 1;
    return(rv);
}


void
free_earb(ea)
    EARB_S **ea;
{
    if(ea && *ea){
	free_earb(&(*ea)->next);
	if((*ea)->v){
	    if((*ea)->v->name)
	      fs_give((void **)&(*ea)->v->name);
	    if((*ea)->v->current_val.p)
	      fs_give((void **)&(*ea)->v->current_val.p);
	    if((*ea)->v->user_val.p)
	      fs_give((void **)&(*ea)->v->user_val.p);
	    if((*ea)->v->global_val.p)
	      fs_give((void **)&(*ea)->v->global_val.p);

	    fs_give((void **)&(*ea)->v);;
	}

	free_arbhdr(&(*ea)->a);
	fs_give((void **)ea);
    }
}


void
calculate_inick_stuff(ps)
    struct pine *ps;
{
    ACTION_S        *role, *irole;
    CONF_S          *ctmp;
    struct variable *v;
    int              i;
    char            *nick;

    for(i = INICK_FROM_CONF; i <= INICK_TEMPL_CONF; i++){
	v = inick_confs[i] ? inick_confs[i]->var : NULL;
	if(v && v->global_val.p)
	  fs_give((void **)&v->global_val.p);
    }

    nick = inick_confs[INICK_INICK_CONF]->var->user_val.p;

    if(nick){
	/*
	 * Use an empty role with inherit_nick set to nick and then use the
	 * combine function to find the action values.
	 */
	role = (ACTION_S *)fs_get(sizeof(*role));
	memset((void *)role, 0, sizeof(*role));
	role->is_a_role = 1;
	role->inherit_nick = cpystr(nick);
	irole = combine_inherited_role(role);

	ctmp = inick_confs[INICK_FROM_CONF];
	v = ctmp ? ctmp->var : NULL;

	if(irole && irole->from){
	    char *bufp;

	    bufp = (char *)fs_get((size_t)est_size(irole->from));
	    v->global_val.p = addr_string(irole->from, bufp);
	}

	ctmp = inick_confs[INICK_REPLYTO_CONF];
	v = ctmp ? ctmp->var : NULL;

	if(irole && irole->replyto){
	    char *bufp;

	    bufp = (char *)fs_get((size_t)est_size(irole->replyto));
	    v->global_val.p = addr_string(irole->replyto, bufp);
	}

	ctmp = inick_confs[INICK_FCC_CONF];
	v = ctmp ? ctmp->var : NULL;
	v->global_val.p = (irole && irole->fcc) ? cpystr(irole->fcc) : NULL;
	
	ctmp = inick_confs[INICK_SIG_CONF];
	v = ctmp ? ctmp->var : NULL;
	v->global_val.p = (irole && irole->sig) ? cpystr(irole->sig) : NULL;

	ctmp = inick_confs[INICK_TEMPL_CONF];
	v = ctmp ? ctmp->var : NULL;
	v->global_val.p = (irole && irole->template)
					? cpystr(irole->template) : NULL;

	free_action(&role);
	free_action(&irole);
    }

    for(i = INICK_INICK_CONF; i <= INICK_TEMPL_CONF; i++){
	ctmp = inick_confs[i];
	v = ctmp ? ctmp->var : NULL;
	if(v && !v->global_val.p){
	    char    *str, *astr, *lc, pdir[MAXPATH+1];
	    ADDRESS *addr;
	    int      len;

	    switch(i){
	      case INICK_FROM_CONF:
		addr = generate_from();
		astr = addr_list_string(addr, NULL, 0, 1);
		str = (astr && astr[0]) ? astr : "?";
		v->global_val.p = (char *)fs_get((strlen(str) + 20) *
							    sizeof(char));
		sprintf(v->global_val.p, "default (%s)", str);
		if(astr)
		  fs_give((void **)&astr);

		if(addr)
		  mail_free_address(&addr);

		break;

	      case INICK_FCC_CONF:
		v->global_val.p = cpystr("value from fcc-name-rule");
		break;

	      case INICK_SIG_CONF:
		pdir[0] = '\0';
		if(ps_global->VAR_OPER_DIR){
		    strncpy(pdir, ps_global->VAR_OPER_DIR, MAXPATH);
		    pdir[MAXPATH] = '\0';
		    len = strlen(pdir) + 1;
		}
		else if((lc = last_cmpnt(ps_global->pinerc)) != NULL){
		    strncpy(pdir, ps_global->pinerc,
			    min(MAXPATH,lc-ps_global->pinerc));
		    pdir[min(MAXPATH, lc-ps_global->pinerc)] = '\0';
		    len = strlen(pdir);
		}

		if(pdir[0] && ps->VAR_SIGNATURE_FILE &&
		   ps->VAR_SIGNATURE_FILE[0] &&
		   is_absolute_path(ps->VAR_SIGNATURE_FILE) &&
		   !strncmp(ps->VAR_SIGNATURE_FILE, pdir, len)){
		    str = ps->VAR_SIGNATURE_FILE + len;
		}
		else
		  str = (ps->VAR_SIGNATURE_FILE && ps->VAR_SIGNATURE_FILE[0])
			  ? ps->VAR_SIGNATURE_FILE : NULL;
		if(str){
		    v->global_val.p = (char *)fs_get((strlen(str) + 20) *
								sizeof(char));
		    sprintf(v->global_val.p, "default (%s)", str);
		}
		break;

	      case INICK_INICK_CONF:
	      case INICK_REPLYTO_CONF:
	      case INICK_TEMPL_CONF:
		break;
	    }
	}

	if(v)
	  set_current_val(v, FALSE, FALSE);

	if(ctmp){
	    if(ctmp->value)
	      fs_give((void **)&ctmp->value);

	    ctmp->value = pretty_value(ps, inick_confs[i]);
	}
    }
}


ROLE_NAMEVAL_S *
pat_fldr_types(index)
    int index;
{
    static ROLE_NAMEVAL_S pat_fldr_list[] = {
	{"Any",			"ANY",		FLDR_ANY},
	{"News",		"NEWS",		FLDR_NEWS},
	{"Email",		"EMAIL",	FLDR_EMAIL},
	{"Specific",		"SPEC",		FLDR_SPECIFIC}
    };

    return((index >= 0 &&
	    index < (sizeof(pat_fldr_list)/sizeof(pat_fldr_list[0])))
		   ? &pat_fldr_list[index] : NULL);
}


ROLE_NAMEVAL_S *
filter_types(index)
    int index;
{
    static ROLE_NAMEVAL_S filter_type_list[] = {
	{"Delete",		"DEL",		FILTER_KILL},
	{"Move",		"FLDR",		FILTER_FOLDER}
    };

    return((index >= 0 &&
	    index < (sizeof(filter_type_list)/sizeof(filter_type_list[0])))
		   ? &filter_type_list[index] : NULL);
}


ROLE_NAMEVAL_S *
role_repl_types(index)
    int index;
{
    static ROLE_NAMEVAL_S role_repl_list[] = {
	{"Never",			"NO",	ROLE_REPL_NO},
	{"With confirmation",		"YES",	ROLE_REPL_YES},
	{"Without confirmation",	"NC",	ROLE_REPL_NOCONF}
    };

    return((index >= 0 &&
	    index < (sizeof(role_repl_list)/sizeof(role_repl_list[0])))
		   ? &role_repl_list[index] : NULL);
}


ROLE_NAMEVAL_S *
role_forw_types(index)
    int index;
{
    static ROLE_NAMEVAL_S role_forw_list[] = {
	{"Never",		  	"NO",	ROLE_FORW_NO},
	{"With confirmation",		"YES",	ROLE_FORW_YES},
	{"Without confirmation",	"NC",	ROLE_FORW_NOCONF}
    };

    return((index >= 0 &&
	    index < (sizeof(role_forw_list)/sizeof(role_forw_list[0])))
		   ? &role_forw_list[index] : NULL);
}


ROLE_NAMEVAL_S *
role_comp_types(index)
    int index;
{
    static ROLE_NAMEVAL_S role_comp_list[] = {
	{"Never",		  	"NO",	ROLE_COMP_NO},
	{"With confirmation",		"YES",	ROLE_COMP_YES},
	{"Without confirmation",	"NC",	ROLE_COMP_NOCONF}
    };

    return((index >= 0 &&
	    index < (sizeof(role_comp_list)/sizeof(role_comp_list[0])))
		   ? &role_comp_list[index] : NULL);
}

/* Arguments:
 *      str:  a list of folders, separated by commas (commas in folder names
 *   will have been backslash escaped.
 *      action: a 1 or 0 value which basically says that str is associated with
 *   the filter action if true or the Current Folder type if false.
 * Return:
 *   Returns 2 on success (user wants to exit) and 0 on failure.
 *
 * This function cycles through a list of folders and checks whether or not each
 * folder exists.  If they exist, return 2, if they don't exist, notify the user
 * or offer to create depending on whether or not it is a filter action or not. 
 * With each of these prompts, the user can abort their desire to exit.
 */
int
check_role_folders(str, action)
     char      *str;
     unsigned   action;
{
    char       *cur_fn, wt_res, prompt[MAXPATH];
    int         i, j, slen, rv = 2;
    CONTEXT_S  *cntxt = NULL;

    if(!str){
      if(action)
	q_status_message(SM_ORDER,3,5,"Set a valid Filter Action before Exiting");
      else
	q_status_message(SM_ORDER,3,5,"Set a valid Specific Folder before Exiting");
      rv = 0;
      return rv;
    }

    slen = strlen(str);
    cur_fn = (char *)fs_get((slen + 1)*sizeof(char));

    for(i = 0, j = 0; i <= slen; i++, j++){
      if(str[i] == ',' || str[i] == '\0'){
	cur_fn[j] = '\0';
	removing_leading_and_trailing_white_space(cur_fn);
	if(*cur_fn != '\0'){
	  if(!is_absolute_path(cur_fn))
	    cntxt = default_save_context(ps_global->context_list);
	  else
	    cntxt = NULL;
	  if(!folder_exists(cntxt, cur_fn) 
	     && (action || !folder_is_nick(cur_fn,FOLDERS(ps_global->context_current)))){
	    if(cntxt && (action == 1))
	      sprintf(prompt,
		      "Folder \"%.15s%s\" in <%.15s%s> doesn't exist. Create",
		      cur_fn, (strlen(cur_fn) > 15) ? "..." : "",
		      cntxt->nickname,
		      (strlen(cntxt->nickname) > 15) ? "..." : "");
	    else if(cntxt && (action == 0))
	      sprintf(prompt,
		      "Folder \"%.15s%s\" in <%.15s%s> doesn't exist. Exit and save anyway",
		      cur_fn, (strlen(cur_fn) > 15) ? "..." : "",
		      cntxt->nickname,
		      (strlen(cntxt->nickname) > 15) ? "..." : "");
	    else if(!cntxt && (action == 1))
	      sprintf(prompt,
		      "Folder \"%.15s%s\" doesn't exist. Create",
		      cur_fn, (strlen(cur_fn) > 15) ? "..." : "");
	    else /*!cntxt && (action == 0) */
	      sprintf(prompt,
		      "Folder \"%.15s%s\" doesn't exist. Exit and save anyway",
		      cur_fn, (strlen(cur_fn) > 15) ? "..." : "");
	    wt_res = want_to(prompt, 'n', 'x', NO_HELP, WT_NORM);
	    if(wt_res == 'y'){
	      if(action){
		if(context_create(cntxt, NULL, cur_fn))
		  q_status_message(SM_ORDER,3,5,"Folder created");
	      }
	      /* No message to notify of changes being saved, we can't */
	      /* assume that the role screen isn't exited yet          */
	      rv = 2;
	    }
	    else if(wt_res == 'n' && action){
	      rv = 2;
	      q_status_message(SM_ORDER,3,5,"Folder not created");
	    }
	    else{
	      q_status_message(SM_ORDER,3,5,"Exit cancelled");
	      rv = 0;
	      break;
	    }
	      
	  }
	  else
	    rv = 2;
	}
	else{ /* blank item in list of folders */
	  if(action && str[i] == ',')
	    q_status_message(SM_ORDER,3,5,"Item in list of filter actions cannot be empty");
	  else if(action && str[i] == '\0')
	    q_status_message(SM_ORDER,3,5,"Set a valid Filter Action before Exiting");
	  else if(!action && str[i] == ',')
	    q_status_message(SM_ORDER,3,5,"Item in list of specific folders cannot be empty");
	  else /* !action && str[i] == '\0' */
	    q_status_message(SM_ORDER,3,5,"Set a valid Specific Folder before Exiting");
	  rv = 0;
	  break;
	}
	j = -1; /* restart for next folder check */
	*cur_fn = '\0';
      }
      else if(str[i] == '\\' && str[i+1] == ','){
	cur_fn[j] = ',';
	i++;
      }
      else
	cur_fn[j] = str[i];
    }
    fs_give((void **)&cur_fn);
    return(rv);
}

int
role_filt_exitcheck(cl, flags)
    CONF_S      **cl;
    unsigned      flags;
{
    int             rv, j, action;
    char           *val = NULL, *spec_fldr = NULL;
    CONF_S         *ctmp;
    ROLE_NAMEVAL_S *f;
#define ACT_UNKNOWN		0
#define ACT_KILL		1
#define ACT_MOVE		2
#define ACT_MOVE_NOFOLDER	3

    /*
     * We have to locate the lines which define the Filter Action and
     * then check to see that it is set to something before allowing
     * user to Exit.
     */
    action = ACT_UNKNOWN;
    if(flags & CF_CHANGES && role_filt_ptr && role_filt_ptr->user_val.p){
	for(j = 0; f = filter_types(j); j++)
	  if(!strucmp(role_filt_ptr->user_val.p, f->name))
	    break;
	
	switch(f ? f->value : -1){
	  case FILTER_KILL:
	    action = ACT_KILL;
	    break;

	  case FILTER_FOLDER:
	    /*
	     * Check that the folder is set to something.
	     */

	    action = ACT_MOVE_NOFOLDER;
	    /* go to end of screen */
	    for(ctmp = (*cl);
		ctmp && ctmp->next;
		ctmp = next_confline(ctmp))
	      ;

	    /* back up to start of Filter Action */
	    for(;
		ctmp &&
		!(ctmp->flags & CF_STARTITEM && ctmp->var == role_filt_ptr);
		ctmp = prev_confline(ctmp))
	      ;
	    
	    /* skip back past NOSELECTs */
	    for(;
		ctmp && (ctmp->flags & CF_NOSELECT);
		ctmp = next_confline(ctmp))
	      ;

	    /* find line with new var (the Folder line) */
	    for(;
		ctmp && (ctmp->var == role_filt_ptr);
		ctmp = next_confline(ctmp))
	      ;
	    
	    /* ok, we're finally at the Folder line */
	    if(ctmp && ctmp->var && ctmp->var->user_val.p){
		val = cpystr(ctmp->var->user_val.p);
		removing_leading_and_trailing_white_space(val);
		if(*val)
		  action = ACT_MOVE;
	    }

	    break;

	  default:
	    dprint(1,(debugfile,
		    "Can't happen, role_filt_ptr set to %s\n",
		    role_filt_ptr->user_val.p));
	    break;
	}
    }

    if(flags & CF_CHANGES){
	switch(want_to((action == ACT_KILL)
	   ? "Commit changes (\"Yes\" means matching messages will be deleted)"
	   : EXIT_PMT, 'y', 'x', h_config_undo, WT_FLUSH_IN)){
	  case 'y':
	    switch(action){
	      case ACT_KILL:
		if(spec_fldr = get_role_specific_folder(cl)){
		  rv = check_role_folders(spec_fldr ,0);
		  fs_give((void **)&spec_fldr);
		  if(rv == 2)
		    q_status_message(SM_ORDER,0,3,"Ok, messages matching that Pattern will be deleted");
		}
		else{
		  q_status_message(SM_ORDER, 0, 3,
				   "Ok, messages matching that Pattern will be deleted");
		  rv = 2;
		}
		break;

	      case ACT_MOVE:
		if(spec_fldr = get_role_specific_folder(cl)){
		  rv = check_role_folders(spec_fldr ,0);
		  fs_give((void **)&spec_fldr);
		  if(val && rv == 2)
		    rv = check_role_folders(val, 1);  
		}
		else
		  rv = 2;
		break;

	      case ACT_MOVE_NOFOLDER:
		rv = 0;
		q_status_message(SM_ORDER, 3, 5,
				 "Set a valid Filter Action before Exiting");
		break;

	      default:
		rv = 2;
		dprint(1,(debugfile,
			"This can't happen, role_filt_ptr or val not set\n"));
		break;
	    }

	    break;

	  case 'n':
	    q_status_message(SM_ORDER,3,5,"No filter changes saved");
	    rv = 10;
	    break;

	  case 'x':  /* ^C */
	  default :
	    q_status_message(SM_ORDER,3,5,"Changes not yet saved");
	    rv = 0;
	    break;
	}
    }
    else
      rv = 2;

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

    return(rv);
}


/*
 * Don't allow exit unless user has set the action to something.
 */
int
role_filt_text_tool(ps, cmd, cl, flags)
    struct pine  *ps;
    int	          cmd;
    CONF_S      **cl;
    unsigned      flags;
{
    int             rv, j;
    CONF_S         *ctmp;
    ROLE_NAMEVAL_S *f;

    switch(cmd){
      case MC_EXIT:
	rv = role_filt_exitcheck(cl, flags);
	break;

      default:
	rv = role_text_tool(ps, cmd, cl, flags);
	break;
    }

    return(rv);
}


/*
 * Don't allow exit unless user has set the action to something.
 */
int
role_filt_radiobutton_tool(ps, cmd, cl, flags)
    struct pine  *ps;
    int	          cmd;
    CONF_S      **cl;
    unsigned      flags;
{
    int             rv, j;
    CONF_S         *ctmp;
    ROLE_NAMEVAL_S *f;

    switch(cmd){
      case MC_EXIT:
	rv = role_filt_exitcheck(cl, flags);
	break;

      default:
	rv = role_radiobutton_tool(ps, cmd, cl, flags);
	break;
    }

    return(rv);
}


/*
 * simple radio-button style variable handler
 */
int
role_radiobutton_tool(ps, cmd, cl, flags)
    struct pine  *ps;
    int	          cmd;
    CONF_S      **cl;
    unsigned      flags;
{
    int	       rv = 0, varmem_was = -1, i;
    CONF_S    *ctmp, *folder_ctmp = NULL;
    ROLE_NAMEVAL_S *rule;

    switch(cmd){
      case MC_CHOICE :				/* set/unset feature */

	/* back up to first line */
	for(ctmp = (*cl);
	    ctmp && ctmp->prev && !(ctmp->prev->flags & CF_NOSELECT);
	    ctmp = prev_confline(ctmp))
	  ;
	
	for(i = 0; ctmp && (!(ctmp->flags & CF_NOSELECT) ||
			    ((*cl)->var == role_fldr_ptr ||
			     (*cl)->var == role_filt_ptr));
	    ctmp = next_confline(ctmp), i++){
	    if(((*cl)->var == role_fldr_ptr) ||
	       ((*cl)->var == role_filt_ptr)){
		if((((*cl)->var == role_fldr_ptr) && !pat_fldr_types(i)) ||
		   (((*cl)->var == role_filt_ptr) && !filter_types(i))){
		    folder_ctmp = ctmp;
		    break;
		}
		
		if(ctmp->value[1] == R_SELD)
		  varmem_was = i;
	    }

	    ctmp->value[1] = ' ';
	}

	/* turn on current value */
	(*cl)->value[1] = R_SELD;

	if((*cl)->var == role_fldr_ptr){
	    if(folder_ctmp){
		if((*cl)->varmem == FLDR_SPECIFIC)
		  folder_ctmp->flags &= ~CF_NOSELECT;
		else if(varmem_was == FLDR_SPECIFIC)
		  folder_ctmp->flags |= CF_NOSELECT;
	    }

	    rule = pat_fldr_types((*cl)->varmem);
	}
	else if((*cl)->var == role_filt_ptr){
	    if(folder_ctmp){
		if((*cl)->varmem == FILTER_FOLDER)
		  folder_ctmp->flags &= ~CF_NOSELECT;
		else if(varmem_was == FILTER_FOLDER)
		  folder_ctmp->flags |= CF_NOSELECT;
	    }

	    rule = filter_types((*cl)->varmem);
	}
	else if((*cl)->var == role_forw_ptr)
	  rule = role_forw_types((*cl)->varmem);
	else if((*cl)->var == role_repl_ptr)
	  rule = role_repl_types((*cl)->varmem);
	else
	  rule = role_comp_types((*cl)->varmem);

	if((*cl)->var->user_val.p)
	  fs_give((void **)&(*cl)->var->user_val.p);

	(*cl)->var->user_val.p = cpystr(rule->name);

	ps->mangled_body = 1;	/* BUG: redraw it all for now? */
	rv = 1;

	break;

      case MC_EXIT:				/* exit */
	rv = role_text_tool(ps, cmd, cl, flags);
	break;

      default :
	rv = -1;
	break;
    }

    return(rv);
}

/*
 * Return an allocated string of the Specific Folder list for 
 * roles, or NULL if Current Folder type is not set to 
 * to Specific Folder
 *
 * WARNING, the method used in obtaining the specific folder is
 * VERY dependent on the order in which it is presented on the 
 * screen.  If the Current Folder radio buttons were changed,
 * this function would probably need to be fixed accordingly
 * (especially if folders were to be presented line by line)
 */
char *
get_role_specific_folder(cl)
     CONF_S     **cl;
{
  CONF_S   *ctmp;

  /* go to the first line */
  for(ctmp = *cl;
      ctmp && ctmp->prev;
      ctmp = prev_confline(ctmp))
    ;
  
  /* go to the current folder radio button list */
  while(ctmp && ctmp->var != role_fldr_ptr)
    ctmp = next_confline(ctmp);

  /* go to the specific folder button (caution) */
  while(ctmp && ctmp->varmem != FLDR_SPECIFIC)
    ctmp = next_confline(ctmp);

  /* check if selected (assumption of format "(*)" */
  if(ctmp && ctmp->value[1] == R_SELD){
    ctmp = next_confline(ctmp);
    if(ctmp->var->user_val.p)
      return cpystr(ctmp->var->user_val.p);
    else  
      /* need to allocate empty string so as not to confuse it */
      /* with the possibility that Specific Folder is not selected */
      return cpystr("");
  }
  else
    return NULL;
}

/*
 */
int
role_text_tool(ps, cmd, cl, flags)
    struct pine  *ps;
    int		  cmd;
    CONF_S      **cl;
    unsigned      flags;
{
    OPT_SCREEN_S *saved_screen;
    int   rv = -1, oeflags, len, sig, r, i, j, at, further, cancel = 0;
    char *file, *err, title[20], *newfile, *lc, *addr, *fldr, *tmpfldr;
    char  dir[MAXPATH+1], dir2[MAXPATH+1], pdir[MAXPATH+1];
    char  full_filename[MAXPATH+1], filename[MAXPATH+1], filename2[MAXPATH+1];
    char  tmp[MAXPATH+1], *spec_fldr;
    EARB_S *earb, *ea, *eaprev;
    CONF_S *ctmp, *newcp;
    HelpType help;

    switch(cmd){
      case MC_EXIT :
	if(flags & CF_CHANGES){
	  switch(want_to(EXIT_PMT, 'y', 'x', h_config_role_undo, WT_FLUSH_IN)){
	    case 'y':
	      if(spec_fldr = get_role_specific_folder(cl)){
		rv = check_role_folders(spec_fldr ,0);
		fs_give((void **)&spec_fldr);
	      }
	      else 
		rv = 2;
	      break;

	    case 'n':
	      q_status_message(SM_ORDER,3,5,"No changes saved");
	      rv = 10;
	      break;

	    case 'x':  /* ^C */
	      q_status_message(SM_ORDER,3,5,"Changes not yet saved");
	      rv = 0;
	      break;
	  }
	}
	else
	  rv = 2;

	break;

      case MC_CHOICE :				/* Choose a file */
	/*
	 * In signature_path we read signature files relative to the pinerc
	 * dir, so if user selects one that is in there we'll make it
	 * relative instead of absolute, so it looks nicer.
	 */
	pdir[0] = '\0';
	if(ps_global->VAR_OPER_DIR){
	    strncpy(pdir, ps_global->VAR_OPER_DIR, MAXPATH);
	    pdir[MAXPATH] = '\0';
	    len = strlen(pdir) + 1;
	}
	else if((lc = last_cmpnt(ps_global->pinerc)) != NULL){
	    strncpy(pdir, ps_global->pinerc, min(MAXPATH,lc-ps_global->pinerc));
	    pdir[min(MAXPATH, lc-ps_global->pinerc)] = '\0';
	    len = strlen(pdir);
	}

	strcpy(title, "SIGNATURE");
	strcpy(dir2, pdir);

	filename2[0] = '\0';
	build_path(full_filename, dir2, filename2);

	r = file_lister(title, dir2, MAXPATH+1,
			filename2, MAXPATH+1, 
			TRUE, FB_READ);
	ps->mangled_screen = 1;

	if(r == 1){
	    build_path(full_filename, dir2, filename2);
	    removing_leading_and_trailing_white_space(full_filename);
	    if(!strncmp(full_filename, pdir, strlen(pdir)))
	      newfile = cpystr(full_filename + len); 
	    else
	      newfile = cpystr(full_filename);

	    if((*cl)->var->user_val.p)
	      fs_give((void **)&((*cl)->var->user_val.p));
	    
	    (*cl)->var->user_val.p = newfile;
	    if((*cl)->value)
	      fs_give((void **)&((*cl)->value));

	    (*cl)->value = pretty_value(ps, *cl);
	    rv = 1;
	}
	else
	  rv = 0;

	break;

      case MC_CHOICEB :		/* Choose an Address, no full name */
      case MC_CHOICEC :		/* Choose an Address from address book */
	if(cmd == MC_CHOICEB)
	  addr = addr_book_oneaddr_nf();
	else
	  addr = addr_book_oneaddr();

	ps->mangled_screen = 1;
	if(addr){
	    if((*cl)->var->user_val.p)
	      fs_give((void **)&((*cl)->var->user_val.p));
	    
	    (*cl)->var->user_val.p = addr;
	    if((*cl)->value)
	      fs_give((void **)&((*cl)->value));

	    (*cl)->value = pretty_value(ps, *cl);
	    rv = 1;
	}
	else
	  rv = 0;

	break;

      case MC_CHOICED :				/* Choose a Folder */
      case MC_CHOICEE :
	saved_screen = opt_screen;
	if(cmd == MC_CHOICED)
	  tmpfldr = folders_for_roles(1);
	else
	  tmpfldr = folders_for_roles(0);
	
	fldr = add_folder_escapes(tmpfldr);
	fs_give((void**)&tmpfldr);

	opt_screen = saved_screen;

	ps->mangled_screen = 1;
	if(fldr){
	    if((*cl)->var->user_val.p)
	      fs_give((void **)&((*cl)->var->user_val.p));
	    
	    (*cl)->var->user_val.p = fldr;
	    if((*cl)->value)
	      fs_give((void **)&((*cl)->value));

	    (*cl)->value = pretty_value(ps, *cl);
	    rv = 1;
	}
	else
	  rv = 0;

	break;

      case MC_EDITFILE :
	file = ((*cl)->var && (*cl)->var->user_val.p && (*cl)->value)
		    ? cpystr((*cl)->value) : NULL;
	if(file)
	  removing_leading_and_trailing_white_space(file);

	sig = (srchstr((*cl)->varname, "signature") != NULL);
	if(!file || !*file){
	    err = (char *)fs_get(100);
	    sprintf(err, "No %s file defined. First define a file name.",
		    sig ? "signature" : "template");
	}
	else{
	    if(file[len=(strlen(file)-1)] == '|')
	      file[len] = '\0';

	    sprintf(title, "%s EDITOR", sig ? "SIGNATURE" : "TEMPLATE");
	    err = signature_edit(file, title);
	}

	fs_give((void **)&file);
	if(err){
	    q_status_message1(SM_ORDER, 3, 5, "%s", err);
	    fs_give((void **)&err);
	}

	rv = 0;
	ps->mangled_screen = 1;
	break;

      /* Add an arbitrary header to this role */
      case MC_ADDHDR :
	rv = 0;
	/* make earb point to last one */
	for(earb = *(*cl)->d.earb; earb && earb->next; earb = earb->next)
	  ;

	/* Add new one to end of list */
	ea = (EARB_S *)fs_get(sizeof(*ea));
	memset((void *)ea, 0, sizeof(*ea));
	ea->v = (struct variable *)fs_get(sizeof(struct variable));
	memset((void *)ea->v, 0, sizeof(struct variable));
	ea->a = (ARBHDR_S *)fs_get(sizeof(ARBHDR_S));
	memset((void *)ea->a, 0, sizeof(ARBHDR_S));

	/* get new header field name */
	help = NO_HELP;
	while(1){
	    i = optionally_enter(tmp, -FOOTER_ROWS(ps), 0, MAXPATH,
			     "Enter the name of the header field to be added: ",
				 NULL, help, NULL);
	    if(i == 0)
	      break;
	    else if(i == 1){
		cmd_cancelled("eXtraHdr");
		cancel = 1;
		break;
	    }
	    else if(i == 3){
		help = help == NO_HELP ? h_config_add_pat_hdr : NO_HELP;
		continue;
	    }
	    else
	      break;
	}

	ps->mangled_footer = 1;

	removing_leading_and_trailing_white_space(tmp);
	if(tmp[strlen(tmp)-1] == ':')  /* remove trailing colon */
	  tmp[strlen(tmp)-1] = '\0';

	removing_trailing_white_space(tmp);

	if(cancel || !tmp[0])
	  break;

	tmp[0] = islower((unsigned char)tmp[0]) ? toupper((unsigned char)tmp[0])
						: tmp[0];
	ea->a->field = cpystr(tmp);

	if(earb)
	  earb->next = ea;
	else
	  *((*cl)->d.earb) = ea;

	/* make ctmp point to last pattern line */
	for(ctmp = *cl;
	    ctmp && ctmp->next && ctmp->next->d.earb;
	    ctmp = next_confline(ctmp))
	  ;
	
	/* add new ctmp line after last pattern line */
	new_confline(&ctmp);

	ea->v->name = (char *)fs_get(strlen(tmp) + 9);
	sprintf(ea->v->name, "%s pattern", tmp);
	ea->v->is_used    = 1;
	ea->v->is_user    = 1;
	set_current_val(ea->v, FALSE, FALSE);

	ctmp->help_title= ARB_HELP;
	ctmp->var       = ea->v;
	ctmp->valoffset = ctmp->prev->valoffset;
	ctmp->keymenu   = &config_role_xtrahdr_keymenu;
	ctmp->help      = h_config_role_arbpat;
	ctmp->tool      = ctmp->prev->tool;
	i = max(strlen(ctmp->prev->varname) - 2, 3);
	sprintf(tmp, "%-*.*s =", i, i, ea->v->name);
	ctmp->varname   = cpystr(tmp);
	ctmp->varnamep  = ctmp;
	ctmp->value     = pretty_value(ps, ctmp);
	ctmp->d.earb    = ctmp->prev->d.earb;
	ctmp->varmem    = ctmp->prev->varmem + 1;

	/*
	 * move current line to new line
	 */

	newcp = ctmp;

	/* how far are we from top? */
	for(at = 0, ctmp = opt_screen->top_line;
	    ctmp != *cl;
	    at++, ctmp = next_confline(ctmp))
	  ;

	/* how far down is it to new line? */
	for(further = 0, ctmp = *cl;
	    ctmp != newcp;
	    further++, ctmp = next_confline(ctmp))
	  ;
	
	if(at + further >= BODY_LINES(ps)){	/* new line is off screen */
	    /* move top line down this far */
	    i = (at + further) + 1 - BODY_LINES(ps);
	    for(ctmp = opt_screen->top_line;
		i > 0;
		i--, ctmp = next_confline(ctmp))
	      ;

	    opt_screen->top_line = ctmp;
	}

	*cl = newcp;

	ps->mangled_screen = 1;
	rv = 1;
	break;

      /* Delete an arbitrary header from this role */
      case MC_DELHDR :
	/* find this one in earb list, using varmem */
	rv = 0;
	eaprev = NULL;
	for(i = 0, ea = *(*cl)->d.earb; ea; i++, ea = ea->next){
	    if(i == (*cl)->varmem)
	      break;

	    eaprev = ea;
	}
	
	sprintf(tmp, "Really remove \"%s\" pattern from this rule",
		(ea && ea->a && ea->a->field) ? ea->a->field : "this");
	if(want_to(tmp, 'y', 'n', NO_HELP, WT_NORM) != 'y'){
	    cmd_cancelled("RemoveHdr");
	    return(rv);
	}

	/* delete the earb element from the list */
	if(ea){
	    if(eaprev)
	      eaprev->next = ea->next;
	    else
	      *(*cl)->d.earb = NULL;
	    
	    if(ea->v){
		if(ea->v->current_val.p)
		  fs_give((void **)&ea->v->current_val.p);
		if(ea->v->global_val.p)
		  fs_give((void **)&ea->v->global_val.p);
		if(ea->v->user_val.p)
		  fs_give((void **)&ea->v->user_val.p);
		if(ea->v->name)
		  fs_give((void **)&ea->v->name);
		
		fs_give((void **)&ea->v);
	    }

	    if(ea->a){
		if(ea->a->field)
		  fs_give((void **)&ea->a->field);
		
		fs_give((void **)&ea->a);
	    }

	    fs_give((void **)&ea);
	}

	/* adjust varmems in following lines */
	for(ctmp = *cl; ctmp && ctmp->d.earb; ctmp = next_confline(ctmp))
	  ctmp->varmem--;
	
	/* if not the last extra pattern */
	if((*cl)->next->d.earb){
	    if(*cl == opt_screen->top_line)
	      opt_screen->top_line = (*cl)->next;

	    /* move to next line */
	    ctmp = *cl;
	    *cl = (*cl)->next;
	}
	else{		/* the last pattern */
	    if(*cl == opt_screen->top_line)
	      opt_screen->top_line = (*cl)->prev;

	    /* move to previous line */
	    ctmp = *cl;
	    *cl = (*cl)->prev;
	}

	/* remove current line */
	snip_confline(&ctmp);
	ps->mangled_body = 1;
	rv = 1;
	break;

      default :
	if((*cl)->var == scorei_pat_global_ptr &&
	   (cmd == MC_EDIT || (cmd == MC_ADD && !(*cl)->var->user_val.p))){
	    char prompt[60];

	    rv = 0;
	    sprintf(prompt, "%s the interval : ",
		    (*cl)->var->user_val.p ? "Change" : "Enter");

	    ps->mangled_footer = 1;
	    help = NO_HELP;
	    tmp[0] = '\0';
	    sprintf(tmp,
		    "%s", (*cl)->var->user_val.p ? (*cl)->var->user_val.p : "");
	    while(1){
		oeflags = OE_APPEND_CURRENT;
		i = optionally_enter(tmp, -FOOTER_ROWS(ps), 0, MAXPATH,
				     prompt, NULL, help, &oeflags);
		if(i == 0){
		    rv = ps->mangled_body = 1;
		    if((*cl)->var->user_val.p)
		      fs_give((void **)&(*cl)->var->user_val.p);
		    
		    if(tmp[0])
		      (*cl)->var->user_val.p = cpystr(tmp);

		    fix_side_effects(ps, (*cl)->var, 0);
		    if((*cl)->value)
		      fs_give((void **)&(*cl)->value);
		    
		    (*cl)->value = pretty_value(ps, *cl);
		}
		else if(i == 1)
		  cmd_cancelled(cmd == MC_ADD ? "Add" : "Change");
		else if(i == 3){
		    help = help == NO_HELP ? h_config_edit_scorei : NO_HELP;
		    continue;
		}
		else if(i == 4)
		  continue;

		break;
	    }
	}
	else{
	    if(cmd == MC_ADD)
	      cmd = MC_EDIT;

	    rv = text_tool(ps, cmd, cl, flags);
	}

	break;
    }

    return(rv);
}


/*
 */
int
role_text_tool_inick(ps, cmd, cl, flags)
    struct pine  *ps;
    int		  cmd;
    CONF_S      **cl;
    unsigned      flags;
{
    int   rv = -1;

    switch(cmd){
      case MC_EXIT :
	if(flags & CF_CHANGES){
	  switch(want_to(EXIT_PMT, 'y', 'x', h_config_role_undo, WT_FLUSH_IN)){
	    case 'y':
	      rv = 2;
	      break;

	    case 'n':
	      q_status_message(SM_ORDER,3,5,"No changes saved");
	      rv = 10;
	      break;

	    case 'x':  /* ^C */
	      q_status_message(SM_ORDER,3,5,"Changes not yet saved");
	      rv = 0;
	      break;
	  }
	}
	else
	  rv = 2;

	break;

      case MC_CHOICE :		/* Choose a role nickname */
	{void (*prev_screen)() = ps->prev_screen,
	      (*redraw)() = ps->redrawer;
	 OPT_SCREEN_S *saved_screen;
	 ACTION_S     *role;

	ps->redrawer = NULL;
	ps->next_screen = SCREEN_FUN_NULL;

	saved_screen = opt_screen;
	if(role_select_screen(ps, &role, 0) == 0){
	    if((*cl)->var->user_val.p)
	      fs_give((void **)&((*cl)->var->user_val.p));
	    
	    (*cl)->var->user_val.p = (role && role->nick) ? cpystr(role->nick)
							  : NULL;
	    if((*cl)->value)
	      fs_give((void **)&((*cl)->value));

	    (*cl)->value = pretty_value(ps, *cl);
	    rv = 1;
	}
	else{
	    ps->next_screen = prev_screen;
	    ps->redrawer = redraw;
	    rv = 0;
	}

	opt_screen = saved_screen;
	}
	/* fall through */

      case MC_EDIT :
      case MC_ADD :
      case MC_DELETE :
	if(cmd != MC_CHOICE)
	  rv = text_tool(ps, cmd, cl, flags);

	/*
	 * If the inherit nickname changed, we have to re-calculate the
	 * global_vals and values for the action variables.
	 */
	if(rv)
	  calculate_inick_stuff(ps);

	ps->mangled_screen = 1;
	break;

      default :
	rv = text_tool(ps, cmd, cl, flags);
	break;
    }

    return(rv);
}


/*
 * Args fmt -- a printf style fmt string with a single %s
 *      buf -- place to put result, assumed large enough (strlen(fmt)+11)
 *   rflags -- controls what goes in buf
 *
 * Returns -- pointer to buf
 */
char *
role_type_print(buf, fmt, rflags)
    char *buf;
    char *fmt;
    int   rflags;
{
#define CASE_MIXED	1
#define CASE_UPPER	2
#define CASE_LOWER	3
    int   cas = CASE_UPPER;
    int   prev_word_is_a = 0;
    char *q, *p;

    /* find %sRule to see what case */
    if((p = srchstr(fmt, "%srule")) != NULL){
	if(p[2] == 'R'){
	    if(p[3] == 'U')
	      cas = CASE_UPPER;
	    else
	      cas = CASE_MIXED;
	}
	else
	  cas = CASE_LOWER;
	
	if(p-3 >= fmt &&
	   p[-1] == SPACE &&
	   (p[-2] == 'a' || p[-2] == 'A')
	   && p[-3] == SPACE)
	  prev_word_is_a++;
    }

    if(cas == CASE_UPPER)
      q = (rflags & ROLE_DO_INCOLS) ? "INDEX COLOR " :
	   (rflags & ROLE_DO_FILTER) ? "FILTERING " :
	    (rflags & ROLE_DO_SCORES) ? "SCORING " :
	     (rflags & ROLE_DO_ROLES)  ? "ROLE " : "";
    else if(cas == CASE_LOWER)
      q = (rflags & ROLE_DO_INCOLS) ? "index color " :
	   (rflags & ROLE_DO_FILTER) ? "filtering " :
	    (rflags & ROLE_DO_SCORES) ? "scoring " :
	     (rflags & ROLE_DO_ROLES)  ? "role " : "";
    else
      q = (rflags & ROLE_DO_INCOLS) ? "Index Color " :
	   (rflags & ROLE_DO_FILTER) ? "Filtering " :
	    (rflags & ROLE_DO_SCORES) ? "Scoring " :
	     (rflags & ROLE_DO_ROLES)  ? "Role " : "";
    
    /* it ain't right to say "a index" */
    if(prev_word_is_a && !struncmp(q, "index", 5))
      q += 6;
      
    sprintf(buf, fmt, q);
    return(buf);
}



void
role_process_filters()
{
    if(ps_global->mail_stream){
	msgno_include(ps_global->mail_stream, ps_global->msgmap, TRUE);
	clear_index_cache();
	refresh_sort(ps_global->msgmap, FALSE);
	ps_global->mangled_header = 1;

	process_filter_patterns(ps_global->mail_stream, ps_global->msgmap, 0L);

	if(ps_global->inbox_stream
	   && ps_global->inbox_stream != ps_global->mail_stream){
	    msgno_include(ps_global->inbox_stream,
			  ps_global->inbox_msgmap, TRUE);
	    process_filter_patterns(ps_global->inbox_stream,
				    ps_global->inbox_msgmap, 0L);
	}
    }
}


void
color_config_screen(ps)
    struct pine *ps;
{
    CONF_S	   *ctmp = NULL, *first_line = NULL;
    SAVED_CONFIG_S *vsave;
    OPT_SCREEN_S    screen;

    ps->next_screen = SCREEN_FUN_NULL;

    mailcap_free(); /* free resources we won't be using for a while */

    if(ps->fix_fixed_warning && offer_to_fix_pinerc(ps))
      write_pinerc(ps);

    color_config_init_display(ps, &ctmp, &first_line);

    vsave = save_color_config_vars(ps);

    switch(conf_scroll_screen(ps, &screen, first_line,
			      "SETUP COLOR", "configuration ", 1, 1)){
      case 0:
	break;

      case 1:
	write_pinerc(ps);
	break;
    
      case 10:
	revert_to_saved_color_config(ps, vsave);
	break;
      
      default:
	q_status_message(SM_ORDER, 7, 10,
			 "conf_scroll_screen bad ret in color_config");
	break;
    }

    free_saved_color_config(ps, &vsave);

#ifdef _WINDOWS
    mswin_set_quit_confirm (F_OFF(F_QUIT_WO_CONFIRM, ps_global));
#endif
}


void
color_config_init_display(ps, ctmp, first_line)
    struct pine *ps;
    CONF_S     **ctmp,
	       **first_line;
{
    char	    tmp[100+1], *p;
    int		    i, lv, j, l, saw_first_index = 0;
    struct	    variable  *vtmp;
    CONF_S	   *ctmpb;
    HDR_COLOR_S    *hc;
    char           *title   = "HELP FOR SETTING UP COLOR";
    NAMEVAL_S      *f;

#ifndef	_WINDOWS
    vtmp = &ps->vars[V_COLOR_STYLE];

    new_confline(ctmp);
    (*ctmp)->flags       |= (CF_NOSELECT | CF_STARTITEM);
    (*ctmp)->keymenu      = &config_radiobutton_keymenu;
    (*ctmp)->tool	  = NULL;
    (*ctmp)->varname	  = cpystr("Color Style");
    (*ctmp)->varnamep	  = ctmpb = *ctmp;

    /* put a nice delimiter before list */
    new_confline(ctmp);
    (*ctmp)->var	      = NULL;
    (*ctmp)->varnamep	      = ctmpb;
    (*ctmp)->keymenu	      = &config_radiobutton_keymenu;
    (*ctmp)->help	      = NO_HELP;
    (*ctmp)->tool	      = radiobutton_tool;
    (*ctmp)->valoffset	      = 12;
    (*ctmp)->flags           |= CF_NOSELECT;
    (*ctmp)->value = cpystr("Set    Rule Values");

    new_confline(ctmp);
    (*ctmp)->var	      = NULL;
    (*ctmp)->varnamep	      = ctmpb;
    (*ctmp)->keymenu	      = &config_radiobutton_keymenu;
    (*ctmp)->help	      = NO_HELP;
    (*ctmp)->tool	      = radiobutton_tool;
    (*ctmp)->valoffset	      = 12;
    (*ctmp)->flags           |= CF_NOSELECT;
    (*ctmp)->value = cpystr("---  ----------------------");

    /* find longest value's name */
    for(lv = 0, i = 0; f = col_style(i); i++)
      if(lv < (j = strlen(f->name)))
	lv = j;
    
    for(i = 0; f = col_style(i); i++){
	new_confline(ctmp);
	if(first_line && !pico_usingcolor() && !*first_line)
	  *first_line = *ctmp;

	(*ctmp)->var		  = vtmp;
	(*ctmp)->varnamep	  = ctmpb;
	(*ctmp)->keymenu	  = &config_radiobutton_keymenu;
	(*ctmp)->help		  = config_help(vtmp - ps->vars, 0);
	(*ctmp)->tool		  = radiobutton_tool;
	(*ctmp)->valoffset	  = 12;
	(*ctmp)->varmem		  = i;
	sprintf(tmp, "(%c)  %-*.*s",
		(ps->color_style == f->value) ? R_SELD : ' ',
		lv, lv, f->name);
	(*ctmp)->value = cpystr(tmp);
    }

    new_confline(ctmp);
    /* Blank line */
    (*ctmp)->flags |= (CF_NOSELECT | CF_B_LINE);

    if(!pico_usingcolor()){
	new_confline(ctmp);
	(*ctmp)->help			 = NO_HELP;
	(*ctmp)->flags			|= CF_NOSELECT;
	(*ctmp)->value = cpystr(COLORNOSET);

	new_confline(ctmp);
	/* Blank line */
	(*ctmp)->flags |= (CF_NOSELECT | CF_B_LINE);
    }

#endif

    new_confline(ctmp);
    /* title before general colors */
    (*ctmp)->help			 = NO_HELP;
    (*ctmp)->flags			|= CF_NOSELECT;
    (*ctmp)->value = cpystr("----------------");
    new_confline(ctmp);
    (*ctmp)->help			 = NO_HELP;
    (*ctmp)->flags			|= CF_NOSELECT;
    (*ctmp)->value = cpystr("GENERAL COLORS");
    new_confline(ctmp);
    (*ctmp)->help			 = NO_HELP;
    (*ctmp)->flags			|= CF_NOSELECT;
    (*ctmp)->value = cpystr("----------------");

    for(vtmp = ps->vars; vtmp->name; vtmp++){
	if(!color_holding_var(ps, vtmp))
	  continue;

	/* If not foreground, skip it */
	if(!(p=strstr(vtmp->name, "-foreground-color")))
	  continue;

	if(!saw_first_index && !struncmp(vtmp->name, "index-", 6)){
	    saw_first_index++;
	    new_confline(ctmp);		/* Blank line */
	    (*ctmp)->flags |= (CF_NOSELECT | CF_B_LINE);
	    new_confline(ctmp);
	    (*ctmp)->help			 = NO_HELP;
	    (*ctmp)->flags			|= CF_NOSELECT;
	    (*ctmp)->value = cpystr("--------------");
	    new_confline(ctmp);
	    (*ctmp)->help			 = NO_HELP;
	    (*ctmp)->flags			|= CF_NOSELECT;
	    (*ctmp)->value = cpystr("INDEX COLORS");
	    new_confline(ctmp);
	    (*ctmp)->help			 = NO_HELP;
	    (*ctmp)->flags			|= CF_NOSELECT;
	    (*ctmp)->value = cpystr("--------------");
	}

	new_confline(ctmp);
	/* Blank line */
	(*ctmp)->flags |= CF_NOSELECT | CF_B_LINE;

	new_confline(ctmp)->var = vtmp;
	if(first_line && !*first_line)
	  *first_line = *ctmp;

	(*ctmp)->varnamep		 = ctmpb = *ctmp;
	(*ctmp)->keymenu		 = &color_setting_keymenu;
	(*ctmp)->help		 	 = config_help(vtmp - ps->vars, 0);
	(*ctmp)->tool			 = color_setting_tool;
	(*ctmp)->flags |= (CF_STARTITEM | CF_COLORSAMPLE | CF_POT_SLCTBL);
	if(!pico_usingcolor())
	  (*ctmp)->flags |= CF_NOSELECT;

	sprintf(tmp, "%c%.*s %sColor%*s %s",
		islower((unsigned char)vtmp->name[0])
					? toupper((unsigned char)vtmp->name[0])
					: vtmp->name[0],
		min(p-vtmp->name-1,30),
		vtmp->name+1,
		saw_first_index ? "Symbol " : "",
		max(EQ_COL - COLOR_INDENT -1 - min(p-vtmp->name-1,30)
			    - 6 - (saw_first_index ? 7 : 0),0), "",
		SAMPLE);
	(*ctmp)->value			 = cpystr(tmp);
	(*ctmp)->valoffset		 = COLOR_INDENT;
    }

    /*
     * custom header colors
     */
    new_confline(ctmp);		/* Blank line */
    (*ctmp)->flags |= CF_NOSELECT | CF_B_LINE;
    new_confline(ctmp);
    (*ctmp)->help			 = NO_HELP;
    (*ctmp)->flags			|= CF_NOSELECT;
    (*ctmp)->value = cpystr("----------------");
    new_confline(ctmp);
    (*ctmp)->help			 = NO_HELP;
    (*ctmp)->flags			|= CF_NOSELECT;
    (*ctmp)->value = cpystr("HEADER COLORS");
    new_confline(ctmp);
    (*ctmp)->help			 = NO_HELP;
    (*ctmp)->flags			|= CF_NOSELECT;
    (*ctmp)->value = cpystr("----------------");

    vtmp = &ps->vars[V_VIEW_HDR_COLORS];
    l = strlen(HEADER_WORD);

    if(ps->hdr_colors){
	for(hc = ps->hdr_colors, i = 0; hc; hc = hc->next, i++)
	  add_header_color_lines(ps, ctmp, hc, i);
    }
    else{
	new_confline(ctmp);		/* Blank line */
	(*ctmp)->flags |= CF_NOSELECT | CF_B_LINE;
	new_confline(ctmp);
	(*ctmp)->help			 = NO_HELP;
	(*ctmp)->flags			|= CF_NOSELECT;
	(*ctmp)->value = cpystr("[ Use the AddHeader command to add colored headers in MESSAGE VIEW ]");
	(*ctmp)->valoffset		 = COLOR_INDENT;
    }
}


void
add_header_color_lines(ps, ctmp, hc, which)
    struct pine *ps;
    CONF_S     **ctmp;
    HDR_COLOR_S *hc;
    int          which;
{
    struct variable *vtmp;
    char	     tmp[100+1];
    int              l;

    vtmp = &ps->vars[V_VIEW_HDR_COLORS];
    l = strlen(HEADER_WORD);

    /* Blank line */
    new_confline(ctmp);
    (*ctmp)->flags		|= CF_NOSELECT | CF_B_LINE;

    new_confline(ctmp)->var	 = vtmp;
    (*ctmp)->varnamep		 = *ctmp;
    (*ctmp)->keymenu		 = &custom_color_setting_keymenu;
    (*ctmp)->help		 = config_help(vtmp - ps->vars, 0);
    (*ctmp)->tool		 = color_setting_tool;
    (*ctmp)->flags |= (CF_STARTITEM | CF_COLORSAMPLE | CF_POT_SLCTBL);
    if(!pico_usingcolor())
      (*ctmp)->flags |= CF_NOSELECT;

    (*ctmp)->varmem		 = CFC_SET_COLOR(which, 0);
    sprintf(tmp, "%s%c%.*s Color%*s %s",
	    HEADER_WORD,
	    islower((unsigned char)hc->hdr[0])
				? toupper((unsigned char)hc->hdr[0])
				: hc->hdr[0],
	    min(strlen(hc->hdr+1),30-l),
	    hc->hdr+1,
	    max(EQ_COL - COLOR_INDENT -1 - min(strlen(hc->hdr+1),30-l)
			- l - 6, 0), "",
	    SAMPLE);
    (*ctmp)->value		 = cpystr(tmp);
    (*ctmp)->valoffset		 = COLOR_INDENT;
}


/*
 * Set up the standard color setting display for one color.
 *
 * Args   fg -- the current foreground color
 *        bg -- the current background color
 *       def -- default box should be checked
 */
void
add_color_setting_disp(ctmp, var, varnamep, km, cb_km, help, indent, which,
		       fg, bg, def)
    CONF_S         **ctmp;
    struct variable *var;
    CONF_S	    *varnamep;
    struct key_menu *km;
    struct key_menu *cb_km;
    HelpType         help;
    int              indent;
    int              which;
    char            *fg;
    char            *bg;
    int              def;
{
    int             i, j, lv, count;
    char	    tmp[100+1];
    char           *title   = "HELP FOR SETTING UP COLOR";

    /* find longest value's name */
    count = pico_count_in_color_table();
    lv = COLOR_BLOB_LEN;

    /* put a title before list */
    new_confline(ctmp);
    (*ctmp)->varnamep		 = varnamep;
    (*ctmp)->keymenu		 = km;
    (*ctmp)->help		 = NO_HELP;
    (*ctmp)->tool		 = color_setting_tool;
    (*ctmp)->valoffset		 = indent;
    (*ctmp)->flags		|= CF_NOSELECT;
    (*ctmp)->varmem		 = 0;
    (*ctmp)->value = cpystr("Foreground     Background");

    new_confline(ctmp)->var	 = var;
    (*ctmp)->varnamep		 = varnamep;
    (*ctmp)->keymenu		 = km;
    (*ctmp)->help		 = NO_HELP;
    (*ctmp)->tool		 = color_setting_tool;
    (*ctmp)->valoffset		 = indent;
    (*ctmp)->flags		|= (CF_COLORSAMPLE | CF_NOSELECT);
    (*ctmp)->varmem		 = CFC_SET_COLOR(which, 0);
    sprintf(tmp, "%s     %s", SAMPLE_LEADER, SAMPLE);
    (*ctmp)->value		 = cpystr(tmp);

    for(i = 0; i < count; i++){
	new_confline(ctmp)->var	 = var;
	(*ctmp)->varnamep	 = varnamep;
	(*ctmp)->keymenu	 = km;
	(*ctmp)->help		 = help;
	(*ctmp)->help_title	 = title;
	(*ctmp)->tool		 = color_setting_tool;
	(*ctmp)->valoffset	 = indent;
	/* 5 is length of "( )  " */
	(*ctmp)->val2offset	 = indent + lv + 5 + SPACE_BETWEEN_DOUBLEVARS;
	(*ctmp)->flags		|= CF_DOUBLEVAR;
	(*ctmp)->varmem		 = CFC_SET_COLOR(which, i);
	/*
	 * Special case: The 2nd and 3rd arguments here have the count == 8
	 * special case in them. See pico/osdep/unix init_color_table().
	 */
	(*ctmp)->value		 = new_color_line(COLOR_BLOB,
						  fg &&
						   (!strucmp(fg, colorx(i)) ||
						   (count == 8 &&
						    !strucmp(fg, colorx(i+8)))),
						  bg &&
						   (!strucmp(bg, colorx(i)) ||
						   (count == 8 &&
						    !strucmp(bg, colorx(i+8)))),
						  lv);
    }

#ifdef	_WINDOWS
    new_confline(ctmp)->var  = var;
    (*ctmp)->varnamep	     = varnamep;
    (*ctmp)->keymenu	     = (km == &custom_color_changing_keymenu)
				 ? &custom_rgb_keymenu
				 : &color_rgb_keymenu;
    (*ctmp)->help	     = help;
    (*ctmp)->help_title	     = title;
    (*ctmp)->tool	     = color_setting_tool;
    (*ctmp)->valoffset	     = indent;
    /* 5 is length of "( )  " */
    (*ctmp)->val2offset	     = indent + lv + 5 + SPACE_BETWEEN_DOUBLEVARS;
    (*ctmp)->flags	    |= CF_DOUBLEVAR;
    (*ctmp)->varmem	     = CFC_SET_COLOR(which, i);
    (*ctmp)->value	     = new_color_line("Custom",
					      (fg && is_rgb_color(fg)),
					      (bg && is_rgb_color(bg)),
					      lv);
#endif

    new_confline(ctmp)->var	= var;
    (*ctmp)->varnamep		= varnamep;
    (*ctmp)->keymenu		= cb_km;
    (*ctmp)->help		= h_config_dflt_color;
    (*ctmp)->help_title		= title;
    (*ctmp)->tool		= color_setting_tool;
    (*ctmp)->valoffset		= indent;
#ifdef	_WINDOWS
    sprintf(tmp, "[%c]  %s", def ? 'X' : ' ', "Default");
#else
    if(var == &ps_global->vars[V_REV_FORE_COLOR])
      sprintf(tmp, "[%c]  %s", def ? 'X' : ' ',
	  "Default (terminal's standout mode, usually reverse video)");
    else if(var == &ps_global->vars[V_SLCTBL_FORE_COLOR])
      sprintf(tmp, "[%c]  %s", def ? 'X' : ' ',
	  "Default (Bold Normal Color)");
    else if(var == &ps_global->vars[V_NORM_FORE_COLOR])
      sprintf(tmp, "[%c]  %s", def ? 'X' : ' ', "Default");
    else if(var_defaults_to_rev(var))
      sprintf(tmp, "[%c]  %s", def ? 'X' : ' ',
	  "Default (same as Reverse Color)");
    else
      sprintf(tmp, "[%c]  %s", def ? 'X' : ' ',
	  "Default (same as Normal Color)");
#endif
    (*ctmp)->value		= cpystr(tmp);

    /*
     * Add a checkbox to turn bold on or off for selectable-item color.
     */
    if(var == &ps_global->vars[V_SLCTBL_FORE_COLOR]){
	new_confline(ctmp)->var	= var;
	(*ctmp)->varnamep	= varnamep;
	(*ctmp)->keymenu	= &selectable_bold_checkbox_keymenu;
	(*ctmp)->help		= h_config_bold_slctbl;
	(*ctmp)->help_title	= title;
	(*ctmp)->tool		= color_setting_tool;
	(*ctmp)->valoffset	= indent;
	sprintf(tmp, "[%c]  %s",
		F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global) ? 'X' : ' ', "Bold");
	(*ctmp)->value		= cpystr(tmp);
    }
}


int
is_rgb_color(color)
    char *color;
{
    int i, j;

    for(i = 0; i < 3; i++){
	if(i && *color++ != ',')
	  return(FALSE);

	for(j = 0; j < 3; j++, color++)
	  if(!isdigit((unsigned char) *color))
	    return(FALSE);
    }

    return(TRUE);
}


char *
new_color_line(color, fg, bg, len)
    char *color;
    int	  fg, bg, len;
{
    char tmp[256];

    sprintf(tmp, "(%c)  %-*.*s%*s(%c)  %-*.*s",
	    fg ? R_SELD : ' ', len, len, color, SPACE_BETWEEN_DOUBLEVARS, "",
	    bg ? R_SELD : ' ', len, len, color);
    return(cpystr(tmp));
}


int
color_text_tool(ps, cmd, cl, flags)
    struct pine  *ps;
    int		  cmd;
    CONF_S      **cl;
    unsigned      flags;
{
    int             rv = -1, i;
    struct variable v;
    HDR_COLOR_S    *hc;
    char           *starting_val, *val, tmp[100];

    if(cmd == MC_EXIT)
      return(simple_exit_cmd(flags));

    for(hc = ps->hdr_colors, i=0; hc; hc = hc->next, i++)
      if(CFC_ICUST(*cl) == i)
	break;

    starting_val = (hc && hc->val) ? pattern_to_string(hc->val) : NULL;

    memset(&v, 0, sizeof(v));
    v.is_used    = 1;
    v.is_user    = 1;
    sprintf(tmp, "\"%c%.30s Pattern\"",
	    islower((unsigned char)hc->hdr[0])
					? toupper((unsigned char)hc->hdr[0])
					: hc->hdr[0],
	    hc->hdr[1] ? hc->hdr + 1 : "");
    v.name       = tmp;
    v.user_val.p = starting_val ? cpystr(starting_val) : NULL;
    set_current_val(&v, FALSE, FALSE);

    (*cl)->var = &v;
    rv = text_tool(ps, cmd, cl, flags);

    if(rv == 1){
	val = v.user_val.p;
	v.user_val.p = NULL;
	if(val)
	  removing_leading_and_trailing_white_space(val);
	
	if(hc->val)
	  fs_give((void **)&hc->val);
	
	hc->val = string_to_pattern(val);
	write_custom_hdr_colors(ps);
	ps->mangled_screen = 1;
    }
    
    if(v.user_val.p)
      fs_give((void **)&v.user_val.p);
    if(v.current_val.p)
      fs_give((void **)&v.current_val.p);
    if(starting_val)
      fs_give((void **)&starting_val);

    return(rv);
}


/*
 * Test whether or not a var is one of the vars which might have a
 * color value stored in it.
 *
 * returns:  1 if it is a color var, 0 otherwise
 */
int
color_holding_var(ps, var)
    struct variable *var;
    struct pine     *ps;
{
    return(var && var->name &&
	   (strstr(var->name, "-foreground-color") ||
	    strstr(var->name, "-background-color") ||
	    var == &ps->vars[V_VIEW_HDR_COLORS]));
}


#ifndef	_WINDOWS
/*
 * test whether or not a var is one of the vars having to do with color
 *
 * returns:  1 if it is a color var, 0 otherwise
 */
int
color_related_var(ps, var)
    struct variable *var;
    struct pine     *ps;
{
    return(var == &ps->vars[V_COLOR_STYLE] || color_holding_var(ps, var));
}
#endif


int
color_setting_tool(ps, cmd, cl, flags)
    struct pine  *ps;
    int	          cmd;
    CONF_S      **cl;
    unsigned      flags;
{
    int	             rv = 0, i, l, cancel = 0, deefault;
    CONF_S          *ctmp, *first_line, *beg = NULL, *end = NULL,
		    *cur_beg, *cur_end, *prev_beg, *prev_end,
		    *next_beg, *next_end;
    struct variable *v, *fgv, *bgv, *setv = NULL, *otherv;
    HDR_COLOR_S     *hc = NULL, *hc_prev, *hc_next, *hc_prevprev, *new_hcolor;
    char            *fgcolor, *bgcolor, *old_val;
    char             header[50+1];
    char             prompt[100], sval[MAXPATH+1];
    HelpType         help;
    ESCKEY_S         opts[3];

    switch(cmd){
      case MC_CHOICE :				/* set a color */

	if(((*cl)->flags & CF_VAR2 && fixed_var((*cl)->var+1, NULL, NULL)) ||
	   (!((*cl)->flags & CF_VAR2) && fixed_var((*cl)->var, NULL, NULL))){
	    if(((*cl)->var->user_val.p || ((*cl)->var+1)->user_val.p)
	       && want_to("Delete old unused personal option setting",
			  'y', 'n', NO_HELP, WT_FLUSH_IN) == 'y'){
		if((*cl)->var->user_val.p)
		  fs_give((void **)&(*cl)->var->user_val.p);

		if(((*cl)->var+1)->user_val.p)
		  fs_give((void **)&((*cl)->var+1)->user_val.p);

		q_status_message(SM_ORDER, 0, 3, "Deleted");
		rv = 1;
	    }

	    return(rv);
	}

	fgv = (*cl)->var;				/* foreground color */
	bgv = (*cl)->var+1;				/* background color */
	v = ((*cl)->flags & CF_VAR2) ? bgv : fgv;	/* var being changed */

	old_val = v->user_val.p;

	if(CFC_ICOLOR(*cl) < pico_count_in_color_table())
	  v->user_val.p = cpystr(colorx(CFC_ICOLOR(*cl)));
	else if(old_val)
	  v->user_val.p = cpystr(is_rgb_color(old_val)
				   ? old_val
				   : color_to_asciirgb(old_val));
	else if(v->current_val.p)
	  v->user_val.p = cpystr(is_rgb_color(v->current_val.p)
				   ? v->current_val.p
				   : color_to_asciirgb(v->current_val.p));
	else if(v == fgv)
	  v->user_val.p = cpystr(color_to_asciirgb(colorx(COL_BLACK)));
	else
	  v->user_val.p = cpystr(color_to_asciirgb(colorx(COL_WHITE)));

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

	set_current_val(v, TRUE, FALSE);

	/*
	 * If the user sets one of foreground/background and the
	 * other is not yet set, set the other.
	 */
	if(v->current_val.p){
	    if(v == fgv && !bgv->user_val.p){
		setv   = bgv;
		otherv = fgv;
	    }
	    else if(v == bgv && !fgv->user_val.p){
		setv   = fgv;
		otherv = bgv;
	    }
	}
	
	if(setv){
	    if(setv->current_val.p)
	      setv->user_val.p = cpystr(setv->current_val.p);
	    else if (setv == fgv && ps_global->VAR_NORM_FORE_COLOR)
	      setv->user_val.p = cpystr(ps_global->VAR_NORM_FORE_COLOR);
	    else if (setv == bgv && ps_global->VAR_NORM_BACK_COLOR)
	      setv->user_val.p = cpystr(ps_global->VAR_NORM_BACK_COLOR);
	    else if(!strucmp(otherv->current_val.p, colorx(COL_WHITE)))
	      setv->user_val.p = cpystr(colorx(COL_BLACK));
	    else
	      setv->user_val.p = cpystr(colorx(COL_WHITE));

	    set_current_val(setv, TRUE, FALSE);
	}

	fix_side_effects(ps, v, 0);
	set_current_color_vals(ps);

	/*
	 * Turn on selected *'s for default selections, if any, and
	 * for ones we forced on.
	 */
	color_update_selected(*cl, fgv->current_val.p, bgv->current_val.p, 1);

	ClearScreen();
	rv = ps->mangled_screen = 1;
	break;

      case MC_CHOICEB :				/* set a custom hdr color */
	/*
	 * Find the HDR_COLOR_S for header.
	 */
	for(hc = ps->hdr_colors, i=0; hc; hc = hc->next, i++)
	  if(CFC_ICUST(*cl) == i)
	    break;

	if(hc){
	    if((*cl)->flags & CF_VAR2){
		old_val = hc->bg;
		if(CFC_ICOLOR(*cl) < pico_count_in_color_table())
		  hc->bg = cpystr(colorx(CFC_ICOLOR(*cl)));
		else if(old_val)
		  hc->bg = cpystr(is_rgb_color(old_val)
				    ? old_val
				    : color_to_asciirgb(old_val));
		else
		  hc->bg = cpystr(color_to_asciirgb(colorx(COL_WHITE)));

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

		/*
		 * If the user sets one of foreground/background and the
		 * other is not yet set, set it.
		 */
		if(!(hc->fg && hc->fg[0])){
		    if(hc->fg)
		      fs_give((void **)&hc->fg);

		    hc->fg = cpystr(ps->VAR_NORM_FORE_COLOR);
		}
	    }
	    else{
		old_val = hc->fg;

		if(CFC_ICOLOR(*cl) < pico_count_in_color_table())
		  hc->fg = cpystr(colorx(CFC_ICOLOR(*cl)));
		else if(old_val)
		  hc->fg = cpystr(is_rgb_color(old_val)
				    ? old_val
				    : color_to_asciirgb(old_val));
		else
		  hc->fg = cpystr(color_to_asciirgb(colorx(COL_BLACK)));

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

		if(!(hc->bg && hc->bg[0])){
		    if(hc->bg)
		      fs_give((void **)&hc->bg);

		    hc->bg = cpystr(ps->VAR_NORM_BACK_COLOR);
		}
	    }

	}

	/*
	 * Turn on selected *'s for default selections, if any, and
	 * for ones we forced on.
	 */
	color_update_selected(*cl, 
			      (hc && hc->fg && hc->fg[0]
			       && hc->bg && hc->bg[0])
				  ? hc->fg : ps->VAR_NORM_FORE_COLOR,
			      (hc && hc->fg && hc->fg[0]
			       && hc->bg && hc->bg[0])
				  ? hc->bg : ps->VAR_NORM_BACK_COLOR,
			      TRUE);

	write_custom_hdr_colors(ps);
	pico_set_normal_color();
	ClearScreen();
	rv = ps->mangled_screen = 1;
	break;

      case MC_TOGGLE :		/* toggle default on or off */
	fgv = (*cl)->var;				/* foreground color */
	bgv = (*cl)->var+1;				/* background color */

	if((*cl)->value[1] == 'X'){		/* turning default off */
	    (*cl)->value[1] = ' ';
	    /*
	     * Take whatever color is the current_val and suck it
	     * into the user_val. Same colors remain checked.
	     */
	    if(fgv->user_val.p)
	      fs_give((void **)&fgv->user_val.p);
	    if(bgv->user_val.p)
	      fs_give((void **)&bgv->user_val.p);
	    if(fgv->current_val.p)
	      fgv->user_val.p = cpystr(fgv->current_val.p);
	    if(bgv->current_val.p)
	      bgv->user_val.p = cpystr(bgv->current_val.p);
	}
	else{					/* turning default on */
	    (*cl)->value[1] = 'X';
	    if(fgv->user_val.p)
	      fs_give((void **)&fgv->user_val.p);

	    if(fgv->cmdline_val.p)
	      fs_give((void **)&fgv->cmdline_val.p);

	    if(bgv->user_val.p)
	      fs_give((void **)&bgv->user_val.p);

	    if(bgv->cmdline_val.p)
	      fs_give((void **)&bgv->cmdline_val.p);

	    set_current_val(fgv, TRUE, FALSE);
	    set_current_val(bgv, TRUE, FALSE);
	    set_current_color_vals(ps);

	    if(fgv == &ps->vars[V_SLCTBL_FORE_COLOR]){
		F_TURN_OFF(F_SLCTBL_ITEM_NOBOLD, ps);
		(*cl)->next->value[1] = 'X';
	    }

	    /*
	     * Turn on selected *'s for default selections.
	     */
	    color_update_selected(prev_confline(*cl),
				  fgv->current_val.p,
				  bgv->current_val.p,
				  FALSE);

	    ps->mangled_body = 1;
	}

	fix_side_effects(ps, fgv, 0);
	rv = 1;
	break;

      case MC_TOGGLEB :		/* toggle default on or off, hdr color */
	/*
	 * Find the HDR_COLOR_S for header.
	 */
	rv = 1;
	for(hc = ps->hdr_colors, i=0; hc; hc = hc->next, i++)
	  if(CFC_ICUST(*cl) == i)
	    break;

	if((*cl)->value[1] == 'X'){		/* turning default off */
	    (*cl)->value[1] = ' ';
	    /*
	     * Take whatever color is the default value and suck it
	     * into the hc structure.
	     */
	    if(hc){
		if(hc->bg)
		  fs_give((void **)&hc->bg);
		if(hc->fg)
		  fs_give((void **)&hc->fg);
		
		if(ps->VAR_NORM_FORE_COLOR &&
		   ps->VAR_NORM_FORE_COLOR[0] &&
		   ps->VAR_NORM_BACK_COLOR &&
		   ps->VAR_NORM_BACK_COLOR[0]){
		    hc->fg = cpystr(ps->VAR_NORM_FORE_COLOR);
		    hc->bg = cpystr(ps->VAR_NORM_BACK_COLOR);
		}
	    }
	}
	else{					/* turning default on */
	    (*cl)->value[1] = 'X';
	    /* Remove current colors, leaving val */
	    if(hc){
		if(hc->bg)
		  fs_give((void **)&hc->bg);
		if(hc->fg)
		  fs_give((void **)&hc->fg);
	    }

	    write_custom_hdr_colors(ps);
	    pico_set_normal_color();
	    ClearScreen();
	    ps->mangled_body = 1;

	    /*
	     * Turn on selected *'s for default selections.
	     */
	    color_update_selected(prev_confline(*cl),
				  ps->VAR_NORM_FORE_COLOR,
				  ps->VAR_NORM_BACK_COLOR,
				  FALSE);
	}

	break;

      case MC_TOGGLEC :		/* toggle selectable item bold on or off */
	toggle_feature_bit(ps, feature_list_index(F_SLCTBL_ITEM_NOBOLD),
			   &ps->vars[V_FEATURE_LIST], (*cl)->value);
	/* set this here because it's backwards from usual */
	(*cl)->value[1] = F_OFF(F_SLCTBL_ITEM_NOBOLD, ps) ? 'X' : ' ';

	ps->mangled_body = 1;		/* to fix Sample Text */
	rv = 1;
	break;

      case MC_DEFAULT :				/* restore default values */

	/* First, confirm that user wants to restore all default colors */
	if(want_to("Really restore all colors to default values",
		   'y', 'n', NO_HELP, WT_NORM) != 'y'){
	    cmd_cancelled("RestoreDefs");
	    return(rv);
	}

	/* get rid of all user set colors */
	for(v = ps->vars; v->name; v++){
	    if(!color_holding_var(ps, v))
	      continue;

	    if(v->user_val.p)
	      fs_give((void **)&v->user_val.p);

	    if(v->cmdline_val.p)
	      fs_give((void **)&v->cmdline_val.p);
	}

	/*
	 * For custom header colors, we want to remove the color values
	 * but leave the hdr value so that it is easy to reset.
	 */
	for(hc = ps->hdr_colors; hc; hc = hc->next){
	    if(hc->fg)
	      fs_give((void **)&hc->fg);
	    if(hc->bg)
	      fs_give((void **)&hc->bg);
	}

	/* set bold for selectable items */
	F_TURN_OFF(F_SLCTBL_ITEM_NOBOLD, ps);

	write_custom_hdr_colors(ps);
	set_current_color_vals(ps);
	clear_index_cache();

	/* redo config display */
	*cl = first_confline(*cl);
	free_conflines(cl);
	opt_screen->top_line = NULL;
	first_line = NULL;
	color_config_init_display(ps, cl, &first_line);
	*cl = first_line;
	ClearScreen();
	ps->mangled_screen = 1;
	rv = 1;
	break;

      case MC_ADD :				/* add custom header color */
	/* get header field name */
	help = NO_HELP;
	while(1){
	    i = optionally_enter(sval, -FOOTER_ROWS(ps), 0, MAXPATH,
			     "Enter the name of the header field to be added: ",
				 NULL, help, NULL);
	    if(i == 0)
	      break;
	    else if(i == 1){
		cmd_cancelled("Add");
		cancel = 1;
		break;
	    }
	    else if(i == 3){
		help = help == NO_HELP ? h_config_add_custom_color : NO_HELP;
		continue;
	    }
	    else
	      break;
	}

	ps->mangled_footer = 1;

	removing_leading_and_trailing_white_space(sval);
	if(sval[strlen(sval)-1] == ':')  /* remove trailing colon */
	  sval[strlen(sval)-1] = '\0';

	removing_trailing_white_space(sval);

	if(cancel || !sval[0])
	  break;

	new_hcolor = (HDR_COLOR_S *)fs_get(sizeof(*new_hcolor));
	memset((void *)new_hcolor, 0, sizeof(*new_hcolor));
	new_hcolor->hdr = cpystr(sval);

	/* add it to end of hdr_colors list */
	for(hc = ps->hdr_colors, i = 0; hc && hc->next; hc = hc->next, i++)
	  ;

	if(hc){
	    i++;
	    hc->next = new_hcolor;
	}
	else
	  ps->hdr_colors = new_hcolor;
	
	/* and to pinerc variable */
	write_custom_hdr_colors(ps);

	/*
	 * add it to end of display, but first remove the comment line
	 * if it's there.
	 */
	for(ctmp = *cl; ctmp && ctmp->next; ctmp = next_confline(ctmp))
	  ;
	
	if(ctmp->flags & CF_NOSELECT && ctmp->prev->flags & CF_NOSELECT){
	    ctmp = ctmp->prev->prev;
	    free_conflines(&ctmp->next);
	}
	
	*cl = ctmp;
	add_header_color_lines(ps, &ctmp, new_hcolor, i);
	*cl = next_confline(*cl);

	/* put current on selectable line */
	for(; *cl && ((*cl)->flags & CF_NOSELECT); *cl = next_confline(*cl))
	  ;

	opt_screen->top_line = *cl;
	rv = ps->mangled_body = 1;
	break;

      case MC_DELETE :				/* delete custom header color */
	if((*cl)->var != &ps->vars[V_VIEW_HDR_COLORS]){
	    q_status_message(SM_ORDER, 0, 2,
			     "Can't delete this color setting");
	    break;
	}

	/* find the HDR_COLOR_S for header */
	hc_prev = NULL;
	for(hc = ps->hdr_colors, i=0; hc; hc = hc->next, i++){
	    if(CFC_ICUST(*cl) == i)
	      break;
	    
	    hc_prev = hc;
	}

	strncpy(header, (hc && hc->hdr && hc->hdr[0]) ? hc->hdr : "header", 50);
	header[30] = '\0';
	header[0] = islower((unsigned char)header[0])
				? toupper((unsigned char)header[0])
				: header[0];

	sprintf(prompt, "Really delete \"%s\" color from config", header);
	if(want_to(prompt, 'y', 'n', NO_HELP, WT_NORM) != 'y'){
	    cmd_cancelled("Delete");
	    return(rv);
	}

	/* remove it from the list */
	if(hc){
	    if(hc_prev)
	      hc_prev->next = hc->next;
	    else
	      ps->hdr_colors = hc->next;

	    hc->next = NULL;
	    free_hdr_colors(&hc);
	    write_custom_hdr_colors(ps);
	}

	/*
	 * Remove the conf lines that go with this header. That's the
	 * blank line before and the current line.
	 */

	beg = (*cl)->prev;
	end = *cl;
	/* handle deleting of top_line on screen */
	if(beg == opt_screen->top_line || end == opt_screen->top_line)
	  opt_screen->top_line = NULL;

	/* reset current line */
	if((*cl)->next && (*cl)->next->next)
	  *cl = (*cl)->next->next;
	else{
	    for(ctmp = (*cl)->prev;
		ctmp && ctmp->flags & CF_NOSELECT && ctmp->prev;
		ctmp = prev_confline(ctmp))
	      if(ctmp == opt_screen->top_line)
		opt_screen->top_line = NULL;

	    *cl = ctmp;
	}

	/* put current on selectable line */
	for(; *cl && ((*cl)->flags & CF_NOSELECT); *cl = next_confline(*cl))
	  ;
	for(; *cl && ((*cl)->flags & CF_NOSELECT); *cl = prev_confline(*cl))
	  ;

	/* top line is deleted, reset */
	if(opt_screen->top_line == NULL)
	  opt_screen->top_line = *cl;

	/* adjust HDR_COLOR_S index (varmem) values */
	for(ctmp = end; ctmp; ctmp = next_confline(ctmp))
	  ctmp->varmem = CFC_ICUST_DEC(ctmp);
	
	/* delete conf lines */
	if(beg && beg->prev)			/* this will always be true */
	  beg->prev->next = end ? end->next : NULL;
	
	if(end && end->next)
	  end->next->prev = beg ? beg->prev : NULL;
	
	if(end)
	  end->next = NULL;
	
	free_conflines(&beg);
	rv = ps->mangled_body = 1;
	q_status_message1(SM_ORDER, 0, 3, "%s color deleted", header);
	break;

      case MC_SHUFFLE :  /* shuffle order of custom headers */
	if((*cl)->var != &ps->vars[V_VIEW_HDR_COLORS]){
	    q_status_message(SM_ORDER, 0, 2,
			     "Can't shuffle this color setting");
	    break;
	}

	/* find the HDR_COLOR_S for header */
	hc_prevprev = hc_prev = NULL;
	for(hc = ps->hdr_colors, i=0; hc; hc = hc->next, i++){
	    if(CFC_ICUST(*cl) == i)
	      break;
	    
	    hc_prevprev = hc_prev;
	    hc_prev = hc;
	}

	if(!hc)		/* can't happen */
	  return(rv);

	hc_next = hc->next;

	if(!(hc_prev || hc_next)){
	    q_status_message(SM_ORDER, 0, 3,
   "Shuffle only makes sense when there is more than one Header Color defined");
	    return(rv);
	}

	/* Move it up or down? */
	i = 0;
	opts[i].ch      = 'u';
	opts[i].rval    = 'u';
	opts[i].name    = "U";
	opts[i++].label = "Up";

	opts[i].ch      = 'd';
	opts[i].rval    = 'd';
	opts[i].name    = "D";
	opts[i++].label = "Down";

	opts[i].ch = -1;
	deefault = 'u';

	if(!hc_prev){		/* no up */
	    opts[0].ch = -2;
	    deefault = 'd';
	}
	else if(!hc_next)
	  opts[1].ch = -2;	/* no down */

	sprintf(prompt, "Shuffle %s%s%s ? ",
		(opts[0].ch != -2) ? "UP" : "",
		(opts[0].ch != -2 && opts[1].ch != -2) ? " or " : "",
		(opts[1].ch != -2) ? "DOWN" : "");
	help = (opts[0].ch == -2) ? h_hdrcolor_shuf_down
				  : (opts[1].ch == -2) ? h_hdrcolor_shuf_up
						       : h_hdrcolor_shuf;

	i = radio_buttons(prompt, -FOOTER_ROWS(ps), opts, deefault, 'x',
			   help, RB_NORM);

	switch(i){
	  case 'x':
	    cmd_cancelled("Shuffle");
	    return(rv);

	  case 'u':
	  case 'd':
	    break;
	}
		
	/* swap order in hc */
	if(i == 'd'){
	    hc->next = hc_next->next;
	    hc_next->next = hc;
	    if(hc_prev)
	      hc_prev->next = hc_next;
	    if(hc == ps->hdr_colors)
	      ps->hdr_colors = hc_next;
	}
	else if(i == 'u'){
	    hc_prev->next = hc_next;
	    hc->next = hc_prev;
	    if(hc_prevprev)
	      hc_prevprev->next = hc;
	    if(hc_prev == ps->hdr_colors)
	      ps->hdr_colors = hc;
	}
	else		/* can't happen */
	  return(rv);

	write_custom_hdr_colors(ps);

	/*
	 * Swap the conf lines.
	 */

	cur_beg = (*cl)->prev;
	cur_end = *cl;

	if(i == 'd'){
	    next_beg = cur_end->next;
	    next_end = next_beg ? next_beg->next : NULL;

	    if(next_end->next)
	      next_end->next->prev = cur_end;
	    cur_end->next = next_end->next;
	    next_end->next = cur_beg;
	    if(cur_beg->prev)
	      cur_beg->prev->next = next_beg;
	    next_beg->prev = cur_beg->prev;
	    cur_beg->prev = next_end;

	    /* adjust HDR_COLOR_S index values */
	    cur_beg->varmem	  = CFC_ICUST_INC(cur_beg);
	    cur_beg->next->varmem = CFC_ICUST_INC(cur_beg->next);

	    next_beg->varmem	   = CFC_ICUST_DEC(next_beg);
	    next_beg->next->varmem = CFC_ICUST_DEC(next_beg->next);

	    if(opt_screen->top_line == cur_end)
	      opt_screen->top_line = next_end;
	    else if(opt_screen->top_line == cur_beg)
	      opt_screen->top_line = next_beg;
	}
	else{
	    prev_end = cur_beg->prev;
	    prev_beg = prev_end ? prev_end->prev : NULL;

	    if(prev_beg && prev_beg->prev)
	      prev_beg->prev->next = cur_beg;
	    cur_beg->prev = prev_beg->prev;
	    prev_beg->prev = cur_end;
	    if(cur_end->next)
	      cur_end->next->prev = prev_end;
	    prev_end->next = cur_end->next;
	    cur_end->next = prev_beg;

	    /* adjust HDR_COLOR_S index values */
	    cur_beg->varmem	  = CFC_ICUST_DEC(cur_beg);
	    cur_beg->next->varmem = CFC_ICUST_DEC(cur_beg->next);

	    prev_beg->varmem	   = CFC_ICUST_INC(prev_beg);
	    prev_beg->next->varmem = CFC_ICUST_INC(prev_beg->next);

	    if(opt_screen->top_line == prev_end)
	      opt_screen->top_line = cur_end;
	    else if(opt_screen->top_line == prev_beg)
	      opt_screen->top_line = cur_beg;
	}


	rv = ps->mangled_body = 1;
	q_status_message(SM_ORDER, 0, 3, "Header Colors shuffled");
	break;

      case MC_EDIT:
	rv = color_edit_screen(ps, cl);
	break;

      case MC_EXIT:				/* exit */
	if((*cl)->keymenu == &color_changing_keymenu ||
	   (*cl)->keymenu == &custom_color_changing_keymenu ||
	   ((*cl)->prev &&
	    ((*cl)->prev->keymenu == &color_changing_keymenu ||
	     (*cl)->prev->keymenu == &custom_color_changing_keymenu)) ||
	   ((*cl)->prev->prev &&
	    ((*cl)->prev->prev->keymenu == &color_changing_keymenu ||
	     (*cl)->prev->prev->keymenu == &custom_color_changing_keymenu)))
	  rv = simple_exit_cmd(flags);
	else
	  rv = config_exit_cmd(flags);

	break;

#ifdef	_WINDOWS
      case MC_RGB1 :
	v = (*cl)->var;
	if((*cl)->flags & CF_VAR2)
	  v += 1;

	if(old_val = mswin_rgbchoice(v->user_val.p
				       ? v->user_val.p : v->current_val.p)){
	    if(v->user_val.p)
	      fs_give((void **) &v->user_val.p);

	    v->user_val.p = old_val;
	    set_current_val(v, TRUE, FALSE);
	    fix_side_effects(ps, v, 0);
	    set_current_color_vals(ps);
	    rv = ps->mangled_screen = 1;
	}

	break;

      case MC_RGB2 :
	/*
	 * Find the HDR_COLOR_S for header.
	 */
	for(hc = ps->hdr_colors, i = 0; hc; hc = hc->next, i++)
	  if(CFC_ICUST(*cl) == i){
	      char **pc = ((*cl)->flags & CF_VAR2) ? &hc->bg : &hc->fg;

	      if(old_val = mswin_rgbchoice(*pc)){
		  fs_give((void **) pc);
		  *pc = old_val;
		  color_update_selected(*cl,
					(hc->fg && hc->fg[0]
					 && hc->bg && hc->bg[0])
					  ? hc->fg : ps->VAR_NORM_FORE_COLOR,
					(hc->fg && hc->fg[0]
					 && hc->bg && hc->bg[0])
					  ? hc->bg : ps->VAR_NORM_BACK_COLOR,
					TRUE);

		  write_custom_hdr_colors(ps);
		  pico_set_normal_color();
		  ClearScreen();
		  rv = ps->mangled_screen = 1;
	      }

	      break;
	  }

	break;
#endif

      default :
	rv = -1;
	break;
    }


    return(rv);
}


void
color_update_selected(cl, fg, bg, cleardef)
    CONF_S *cl;
    char   *fg, *bg;
    int	    cleardef;
{
    int i;

    /* back up to header line */
    for(; cl && (cl->flags & CF_DOUBLEVAR); cl = prev_confline(cl))
      ;

    for(i = 0, cl = next_confline(cl);
	i < pico_count_in_color_table() && cl;
	i++, cl = next_confline(cl)){
	if(fg && !strucmp(fg, colorx(i)))
	  cl->value[1] = R_SELD;
	else
	  cl->value[1] = ' ';

	if(bg && !strucmp(bg, colorx(i)))
	  cl->value[cl->val2offset - cl->valoffset + 1] = R_SELD;
	else
	  cl->value[cl->val2offset - cl->valoffset + 1] = ' ';
    }

#ifdef	_WINDOWS
    /* check for rgb color indicating a custom setting */
    cl->value[1] = (fg && is_rgb_color(fg)) ? R_SELD : ' ';
    cl->value[cl->val2offset - cl->valoffset + 1] =  (bg && is_rgb_color(bg))
							? R_SELD : ' ';
    cl = next_confline(cl); /* advance to Default checkbox */
#endif

    /* Turn off Default X */
    if(cleardef)
      cl->value[1] = ' ';
}


int
color_edit_screen(ps, cl)
    struct pine *ps;
    CONF_S     **cl;
{
    OPT_SCREEN_S     screen, *saved_screen;
    CONF_S          *ctmp = NULL, *first_line = NULL, *ctmpb;
    int              rv, is_index = 0, is_custom = 0, indent = 12;
    int              is_normal = 0;
    char             tmp[100+1], name[100], *p;
    struct variable *vtmp, v;
    int              i, def;
    HDR_COLOR_S     *hc = NULL;

    vtmp = (*cl)->var;
    if(vtmp == &ps->vars[V_VIEW_HDR_COLORS])
      is_custom++;
    else if(color_holding_var(ps, vtmp)){
	if(!struncmp(vtmp->name, "index-", 6))
	  is_index++;
	else
	  is_normal++;
    }

    new_confline(&ctmp);
    /* Blank line */
    ctmp->flags |= CF_NOSELECT | CF_B_LINE;

    first_line = ctmp;

    new_confline(&ctmp)->var = vtmp;

    if(is_normal){
	p = strstr(vtmp->name, "-foreground-color");
	sprintf(name, "%.*s", p ? min(p - vtmp->name, 30) : 30, vtmp->name);
	if(islower((unsigned char)name[0]))
	  name[0] = toupper((unsigned char)name[0]);
    }
    else if(is_index){
	p = strstr(vtmp->name, "-foreground-color");
	sprintf(name, "%.*s Symbol",
		p ? min(p - vtmp->name, 30) : 30, vtmp->name);
	if(islower((unsigned char)name[0]))
	  name[0] = toupper((unsigned char)name[0]);
    }
    else if(is_custom){
	for(hc = ps->hdr_colors, i = 0; hc; hc = hc->next, i++)
	  if(CFC_ICUST(*cl) == i)
	    break;
	
	if(hc){
	    sprintf(name, "%s%.*s", HEADER_WORD, 30, hc->hdr);
	    i = sizeof(HEADER_WORD) - 1;
	    if(islower((unsigned char) name[i]))
	      name[i] = toupper((unsigned char) name[i]);
	}
    }
    else{
	name[0] = '\0';
    }

    sprintf(tmp, "%.*s Color =", 40, name);
    ctmp->varname		 = cpystr(tmp);
    ctmp->varnamep		 = ctmpb = ctmp;
    ctmp->flags			|= (CF_STARTITEM | CF_NOSELECT);
    ctmp->keymenu		 = &color_changing_keymenu;

    if(is_custom){
	def = !(hc && hc->fg && hc->fg[0] && hc->bg && hc->bg[0]);
	
	add_color_setting_disp(&ctmp, vtmp, ctmpb,
			       &custom_color_changing_keymenu,
			       &hdr_color_checkbox_keymenu,
			       config_help(vtmp - ps->vars, 0),
			       indent, CFC_ICUST(*cl),
			       def ? ps_global->VAR_NORM_FORE_COLOR
				   : hc->fg,
			       def ? ps_global->VAR_NORM_BACK_COLOR
				   : hc->bg,
			       def);

	/* optional string to match in header value */
	new_confline(&ctmp);
	ctmp->varnamep		 = ctmpb;
	ctmp->keymenu		 = &color_pattern_keymenu;
	ctmp->help		 = h_config_customhdr_pattern;
	ctmp->tool		 = color_text_tool;
	ctmp->varoffset		 = indent-5;
	ctmp->varname		 = cpystr("Pattern to match =");
	ctmp->valoffset		 = indent-5 + strlen(ctmp->varname) + 1;
	ctmp->varmem		 = (*cl)->varmem;

	/*
	 * This is really ugly. This is just to get the value correct.
	 */
	memset(&v, 0, sizeof(v));
	v.is_used    = 1;
	v.is_user    = 1;
	if(hc && hc->val)
	  v.user_val.p = pattern_to_string(hc->val);

	set_current_val(&v, FALSE, FALSE);
	ctmp->var = &v;
	ctmp->value = pretty_value(ps, ctmp);
	ctmp->var = NULL;
	if(v.user_val.p)
	  fs_give((void **)&v.user_val.p);
	if(v.current_val.p)
	  fs_give((void **)&v.current_val.p);
    }
    else{
	add_color_setting_disp(&ctmp, vtmp, ctmpb,
			       &color_changing_keymenu,
			       &config_checkbox_keymenu,
			       config_help(vtmp - ps->vars, 0),
			       indent, 0,
			       ps->vars[vtmp - ps->vars].current_val.p,
			       ps->vars[vtmp - ps->vars + 1].current_val.p,
			       !(ps->vars[vtmp - ps->vars].user_val.p &&
				 ps->vars[vtmp - ps->vars].user_val.p[0] &&
				 ps->vars[vtmp - ps->vars + 1].user_val.p &&
				 ps->vars[vtmp - ps->vars + 1].user_val.p[0]));
    }

    first_line = first_sel_confline(first_line);

    saved_screen = opt_screen;
    rv = conf_scroll_screen(ps, &screen, first_line, "SETUP COLOR",
			    "configuration ", 1, 1);

    opt_screen = saved_screen;
    ps->mangled_screen = 1;
    return(rv);
}


void
set_current_color_vals(ps)
    struct pine *ps;
{
    struct variable *vars = ps->vars;

    set_current_val(&vars[V_NORM_FORE_COLOR], TRUE, TRUE);
    set_current_val(&vars[V_NORM_BACK_COLOR], TRUE, TRUE);
    pico_nfcolor(VAR_NORM_FORE_COLOR);
    pico_nbcolor(VAR_NORM_BACK_COLOR);

    set_current_val(&vars[V_REV_FORE_COLOR], TRUE, TRUE);
    set_current_val(&vars[V_REV_BACK_COLOR], TRUE, TRUE);
    pico_rfcolor(VAR_REV_FORE_COLOR);
    pico_rbcolor(VAR_REV_BACK_COLOR);

    set_color_val(&vars[V_TITLE_FORE_COLOR]);
    set_color_val(&vars[V_STATUS_FORE_COLOR]);
    set_color_val(&vars[V_KEYLABEL_FORE_COLOR]);
    set_color_val(&vars[V_KEYNAME_FORE_COLOR]);
    set_color_val(&vars[V_SLCTBL_FORE_COLOR]);
    set_color_val(&vars[V_QUOTE1_FORE_COLOR]);
    set_color_val(&vars[V_QUOTE2_FORE_COLOR]);
    set_color_val(&vars[V_QUOTE3_FORE_COLOR]);
    set_color_val(&vars[V_PROMPT_FORE_COLOR]);
    set_color_val(&vars[V_IND_PLUS_FORE_COLOR]);
    set_color_val(&vars[V_IND_IMP_FORE_COLOR]);
    set_color_val(&vars[V_IND_DEL_FORE_COLOR]);
    set_color_val(&vars[V_IND_ANS_FORE_COLOR]);
    set_color_val(&vars[V_IND_NEW_FORE_COLOR]);
    set_color_val(&vars[V_IND_REC_FORE_COLOR]);
    set_color_val(&vars[V_IND_UNS_FORE_COLOR]);

    set_current_val(&ps->vars[V_VIEW_HDR_COLORS], TRUE, TRUE);
    set_custom_hdr_colors(ps);
    pico_set_normal_color();
}


/*
 * Set current_val for the foreground and background color vars, which
 * are assumed to be in order. If a set_current_val on them doesn't
 * produce current_vals, then use the colors from defvar to set those
 * current_vals.
 */
void
set_color_val(v)
    struct variable *v;
{
    set_current_val(v, TRUE, TRUE);
    set_current_val(v+1, TRUE, TRUE);

    if(!(v->current_val.p && v->current_val.p[0] &&
         (v+1)->current_val.p && (v+1)->current_val.p[0])){
	struct variable *defvar;

	if(v->current_val.p)
	  fs_give((void **)&v->current_val.p);
	if((v+1)->current_val.p)
	  fs_give((void **)&(v+1)->current_val.p);

	if(var_defaults_to_rev(v))
	  defvar = &ps_global->vars[V_REV_FORE_COLOR];
	else
	  defvar = &ps_global->vars[V_NORM_FORE_COLOR];

	/* use default vars values instead */
	if(defvar && defvar->current_val.p && defvar->current_val.p[0] &&
           (defvar+1)->current_val.p && (defvar+1)->current_val.p[0]){
	    v->current_val.p = cpystr(defvar->current_val.p);
	    (v+1)->current_val.p = cpystr((defvar+1)->current_val.p);
	}
    }
}


int
var_defaults_to_rev(v)
    struct variable *v;
{
    return(v == &ps_global->vars[V_REV_FORE_COLOR] ||
	   v == &ps_global->vars[V_TITLE_FORE_COLOR] ||
	   v == &ps_global->vars[V_STATUS_FORE_COLOR] ||
	   v == &ps_global->vars[V_KEYNAME_FORE_COLOR] ||
	   v == &ps_global->vars[V_PROMPT_FORE_COLOR]);
}


/*
 * Each item in the list looks like:
 *
 *  /HDR=<header>/FG=<foreground color>/BG=<background color>
 *
 * We separate the three pieces into an array of structures to make
 * it easier to deal with later.
 */
void
set_custom_hdr_colors(ps)
    struct pine *ps;
{
    char        *p, *q, *hdr, *fg, *bg, **s;
    PATTERN_S   *val;
    HDR_COLOR_S *new_hcolor, *hc, **nexthc;

    if(ps->hdr_colors)
      free_hdr_colors(&ps->hdr_colors);

    nexthc = &ps->hdr_colors;

    if(ps->VAR_VIEW_HDR_COLORS){
	for(s = ps->VAR_VIEW_HDR_COLORS; (q = *s) != NULL; s++){
	    if(q[0]){
		hdr = fg = bg = NULL;
		val = NULL;
		if((p = srchstr(q, "/HDR=")) != NULL)
		  hdr = remove_backslash_escapes(p+5);
		if((p = srchstr(q, "/FG=")) != NULL)
		  fg = remove_backslash_escapes(p+4);
		if((p = srchstr(q, "/BG=")) != NULL)
		  bg = remove_backslash_escapes(p+4);
		val = parse_pattern("/VAL=", q, 0);
		
		if(hdr && *hdr){
		    /* remove colons */
		    if((p = strindex(hdr, ':')) != NULL)
		      *p = '\0';

		    new_hcolor = (HDR_COLOR_S *)fs_get(sizeof(*new_hcolor));
		    memset((void *)new_hcolor, 0, sizeof(*new_hcolor));
		    new_hcolor->hdr = hdr;
		    new_hcolor->fg  = fg;
		    new_hcolor->bg  = bg;
		    new_hcolor->val = val;

		    *nexthc = new_hcolor;
		    nexthc = &new_hcolor->next;
		}
		else{
		    if(hdr)
		      fs_give((void **)&hdr);
		    if(fg)
		      fs_give((void **)&fg);
		    if(bg)
		      fs_give((void **)&bg);
		    if(val)
		      free_pattern(&val);
		}
	    }
	}
    }
}


/*
 * Each item in the list looks like:
 *
 *  /HDR=<header>/FG=<foreground color>/BG=<background color>
 *
 * We separate the three pieces into an array of structures to make
 * it easier to deal with later.
 */
void
write_custom_hdr_colors(ps)
    struct pine *ps;
{
    int          lineno = 0;
    char       **lvalue, conf_line[300], *p;
    HDR_COLOR_S *hc;

    if(!ps->hdr_colors)
      return;

    /* Count how many lines will be in patterns variable */
    for(hc = ps->hdr_colors; hc; hc = hc->next)
      lineno++;

    lvalue = (char **)fs_get((lineno+1)*sizeof(char *));
    memset(lvalue, 0, (lineno+1) * sizeof(char *));

    for(hc = ps->hdr_colors, lineno = 0; hc; hc = hc->next, lineno++){
	char *hdr = NULL, *fg = NULL, *bg = NULL, *val = NULL;

	if(hc->hdr)
	  hdr = add_viewerhdr_escapes(hc->hdr);
	if(hc->fg)
	  fg = add_viewerhdr_escapes(hc->fg);
	if(hc->bg)
	  bg = add_viewerhdr_escapes(hc->bg);
	if(hc->val){
	    p = pattern_to_string(hc->val);
	    if(p){
		val = add_viewerhdr_escapes(p);
		fs_give((void **)&p);
	    }
	}

	sprintf(conf_line, "/HDR=%.50s/FG=%.50s/BG=%.50s%s%.100s",
		hdr ? hdr : "", fg ? fg : "", bg ? bg : "",
		val ? "/VAL=" : "", val ? val : "");

	if(hdr)
	  fs_give((void **)&hdr);
	if(fg)
	  fs_give((void **)&fg);
	if(bg)
	  fs_give((void **)&bg);
	if(val)
	  fs_give((void **)&val);

	lvalue[lineno] = cpystr(conf_line);
    }

    free_list_array(&ps->vars[V_VIEW_HDR_COLORS].user_val.l);
    ps->vars[V_VIEW_HDR_COLORS].user_val.l = lvalue;
    set_current_val(&ps->vars[V_VIEW_HDR_COLORS], TRUE, TRUE);
}


#ifdef _WINDOWS
/*----------------------------------------------------------------------
     MSWin scroll callback.  Called during scroll message processing.
	     


  Args: cmd - what type of scroll operation.
	scroll_pos - paramter for operation.  
			used as position for SCROLL_TO operation.

  Returns: TRUE - did the scroll operation.
	   FALSE - was not able to do the scroll operation.
 ----*/
int
config_scroll_callback (cmd, scroll_pos)
int	cmd;
long	scroll_pos;
{   
    switch (cmd) {
      case MSWIN_KEY_SCROLLUPLINE:
	config_scroll_down (scroll_pos);
	break;

      case MSWIN_KEY_SCROLLDOWNLINE:
	config_scroll_up (scroll_pos);
	break;

      case MSWIN_KEY_SCROLLUPPAGE:
	config_scroll_down (BODY_LINES(ps_global));
	break;

      case MSWIN_KEY_SCROLLDOWNPAGE:
	config_scroll_up (BODY_LINES(ps_global));
	break;

      case MSWIN_KEY_SCROLLTO:
	config_scroll_to_pos (scroll_pos);
	break;
    }

    option_screen_redrawer();
    fflush(stdout);

    return(TRUE);
}
#endif	/* _WINDOWS */
