#if !defined(lint) && !defined(DOS)
static char rcsid[] = "$Id: other.c,v 4.434 1999/02/01 23:31:42 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"

#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 ADD_FIRST_ROLE "Use Add to add a role"

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   */
    struct variable  *var;			/* pointer to pinerc var     */
    short	      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_HANDLE *handle;
	    PAT_LINE_S *patline;
	    PAT_S      *pat;
	    PAT_S     **selected;
	} r;
#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     */

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;
#if defined(DOS) || defined(OS2)
static char *config_colors[] = {"black", "blue", "green", "cyan", "red",
				"magenta", "yellow", "white", NULL};
#endif
static char no_ff[] = "-no-formfeed";

#define next_confline(p)  ((p) ? (p)->next : NULL)
#define prev_confline(p)  ((p) ? (p)->prev : NULL)



/*
 * 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));
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	 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 *));
void            revert_to_saved_config PROTO((struct pine *, SAVED_CONFIG_S *));
void            free_saved_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 *));
int      save_include PROTO((struct pine *, struct variable *));
void     role_config_init_disp PROTO((struct pine *, PAT_HANDLE *,
				      CONF_S **,CONF_S **));
void     add_patline_to_display PROTO((struct pine *, CONF_S **, int, CONF_S **,
				       CONF_S **, PAT_HANDLE *, PAT_LINE_S *));
void     add_role_to_display PROTO((CONF_S **, PAT_LINE_S *, PAT_S *, int,
				    CONF_S **, int, PAT_HANDLE *));
void     add_fake_first_role PROTO((CONF_S **, int, PAT_HANDLE *));
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      role_config_edit PROTO((struct pine *, CONF_S **));
int      role_config_shuffle PROTO((struct pine *, CONF_S **));
int      role_config_del PROTO((struct pine *, CONF_S **));
int      role_config_addfile PROTO((struct pine *, CONF_S **));
int      role_config_delfile PROTO((struct pine *, CONF_S **));
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      role_config_edit_screen PROTO((struct pine *, PAT_S *, PAT_HANDLE *,
					char *, PAT_S **));
int      role_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 *, PAT_HANDLE *));
int	 role_radiobutton_tool PROTO((struct pine *, int, CONF_S **, unsigned));


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.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));
	    }
	}
    }

    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}

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 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_addr_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},
	NULL_MENU,
	NULL_MENU,
	NULL_MENU,
	NULL_MENU,
	NULL_MENU,
	NULL_MENU,
	NULL_MENU};
INST_KEY_MENU(config_role_addr_keymenu, config_role_addr_keys);

static struct key config_role_fcc_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_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_fcc_keymenu, config_role_fcc_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 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);






/*----------------------------------------------------------------------
    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);
	    }
	}
#if defined(DOS) || defined(OS2)
	else if(vtmp == &ps->vars[V_NORM_FORE_COLOR] /* radio case */
		|| vtmp == &ps->vars[V_NORM_BACK_COLOR]
		|| vtmp == &ps->vars[V_REV_FORE_COLOR]
		|| vtmp == &ps->vars[V_REV_BACK_COLOR]){
	    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    Color 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; config_colors[i]; i++)
	      if(lv < (j = strlen(config_colors[i])))
		lv = j;
	    
	    for(i = 0; config_colors[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;

		/*
		 * varmem == sort_type index (reverse doubles index)
		 */
		ctmpa->varmem = i;
		sprintf(tmp, "(%c)  %-*.*s",
			!strucmp(ps->vars[vtmp - ps->vars].current_val.p,
				 config_colors[i]) ? R_SELD : ' ',
			lv, lv, config_colors[i]);
		ctmpa->value = cpystr(tmp);
	    }
	}
#endif
	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)){
      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_OPER_DIR :
      case V_USERINPUTTIMEO :
      case V_TCPOPENTIMEO :
      case V_RSHCMD :
      case V_RSHPATH :
      case V_RSHOPENTIMEO :
      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 :
#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));
}


/*
 * 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)){
      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);
    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);
    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,
	NULL_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,
	NULL_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];

    dprint(4,(debugfile, "ldap_addr_select()\n"));

    need_mail = (style == AlwaysDisplay || style == DisplayForURL) ? 0 : 1;
    if(style == AlwaysDisplay)
      km = &addr_s_km_with_view;
    else if(style == AlwaysDisplayAndMailRequired)
      km = &addr_s_km_with_goback;
    else if(style == DisplayForURL)
      km = &addr_s_km_for_url;
    else
      km = &addr_s_km;

    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       = h_address_selection;
		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       = h_address_selection;
		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       = h_address_selection;
		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       = h_address_selection;
		    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       = h_address_selection;
		    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       = h_address_selection;
		    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       = h_address_selection;
		    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       = h_address_selection;
		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       = h_address_selection;
	    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       = h_address_selection;
	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       = h_address_selection;
	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)){
	  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 :
	{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);
	    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));
	    sprintf(subtitle, "%s%s%s%s",
		    dserv,
		    (info && info->serv && *info->serv) ? info->serv : "<?>",
		    (info && info->port >= 0) ? ":" : "",
		    (info && info->port >= 0) ? comatose(info->port) : "");

	    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);
    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));
	    sprintf(subtitle, "%s%s%s%s",
		    dserv,
		    (info && info->serv && *info->serv) ? info->serv : "<?>",
		    (info && info->port >= 0) ? ":" : "",
		    (info && info->port >= 0) ? comatose(info->port) : "");

	    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':
	q_status_message(SM_ORDER, 0, 3, "Shuffle cancelled");
	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));
	    sprintf(subtitle, "%s%s%s%s",
		    dserv,
		    (info && info->serv && *info->serv) ? info->serv : "<?>",
		    (info && info->port >= 0) ? ":" : "",
		    (info && info->port >= 0) ? comatose(info->port) : "");
	    (*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);

    /*
     * 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)
    struct pine  *ps;
    OPT_SCREEN_S *screen;
    CONF_S       *start_line;
    char         *title;
    char	 *pdesc;
    int		  edit_config;
{
    char	  tmp[MAXPATH+1];
    int		  cmd, i, j, ch = 'x', done = 0, changes = 0;
    int		  retval = 0;
    int		  km_popped = 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;

	    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:
	    for(ctmpa = next_confline(screen->current), i = 1;
		ctmpa && (ctmpa->flags & CF_NOSELECT);
		ctmpa = next_confline(ctmpa), i++)
	      ;

	    if(ctmpa){
		screen->current = ctmpa;
		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:			/* next list element */
	  case MC_CHARUP:
	    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((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 */
	    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;
	    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)){
		    screen->current = ctmpa;

		    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;

			if((cmd = menu_command(ctrl('M'), km)) != MC_UNKNOWN){
			    i = menu_binding_index(km, cmd);
			    other_popup[++n].type	= tQueue;
			    other_popup[n].label.style  = lNormal;
			    other_popup[n].label.string = km->keys[i].label;
			    other_popup[n].data.val   = ctrl('M');
			}
			else if((cmd = menu_command('>', km)) != MC_UNKNOWN){
			    i = menu_binding_index(km, cmd);
			    other_popup[++n].type	= tQueue;
			    other_popup[n].label.style  = lNormal;
			    other_popup[n].label.string = km->keys[i].label;
			    other_popup[n].data.val	= '>';
			}

			if((cmd = menu_command('<', km)) != MC_UNKNOWN){
			    i = menu_binding_index(km, cmd);
			    other_popup[++n].type	= tQueue;
			    other_popup[n].label.style  = lNormal;
			    other_popup[n].label.string = km->keys[i].label;
			    other_popup[n].data.val	= '<';
			}
			else if((i = menu_binding_index(km, MC_EXIT)) >= 0){
			    other_popup[++n].type	= tQueue;
			    other_popup[n].label.style  = lNormal;
			    other_popup[n].label.string = km->keys[i].label;
			    other_popup[n].data.val	=
							km->keys[i].bind.ch[0];
			}

			if((i = menu_binding_index(km, 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 = km->keys[i].label;
			    other_popup[n].data.val = km->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;

		    if((cmd = menu_command('<', km)) != MC_UNKNOWN){
			i = menu_binding_index(km, cmd);
			other_popup[++n].type	    = tQueue;
			other_popup[n].label.style  = lNormal;
			other_popup[n].label.string = km->keys[i].label;
			other_popup[n].data.val	    = '<';
		    }
		    else if((i = menu_binding_index(km, MC_EXIT)) >= 0){
			other_popup[++n].type	    = tQueue;
			other_popup[n].label.style  = lNormal;
			other_popup[n].label.string = km->keys[i].label;
			other_popup[n].data.val	    = km->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;
		 }
	     }

	     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);
      case V_FOLDER_EXTENSION :
	return(h_config_folder_extension);
      case V_NORM_FORE_COLOR :
	return(h_config_normal_fg);
      case V_NORM_BACK_COLOR :
	return(h_config_normal_bg);
      case V_REV_FORE_COLOR :
	return(h_config_reverse_fg);
      case V_REV_BACK_COLOR :
	return(h_config_reverse_bg);
#endif
      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[MAXPATH+1], *tmp, **newval = NULL;
    int		     rv = 0, skip_to_next = 0, after = 0, i = 4, j, k;
    int		     lowrange, hirange, incr, oeflags;
    int		     numval, repeat_key = 0;
    HelpType         help;
    ESCKEY_S         ekey[6];

    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{
	    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;
    }

    sval[0] = '\0';
    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;
		    q_status_message(SM_ORDER,0,3,"Add cancelled");
		    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, 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);
		    /*
		     * 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){
		    q_status_message(SM_ORDER,0,3,"Add cancelled");
		}
		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;

	    if((*cl)->var->is_list){
		sprintf(prompt, "Change field %.30s list entry : ",
			(*cl)->var->name);
		sprintf(sval, "%s",
			(*cl)->var->user_val.l[(*cl)->varmem]
			  ? (*cl)->var->user_val.l[(*cl)->varmem] : "");
	    }
	    else{
		sprintf(prompt, "Change %sfield %.35s value : ",
			flags&CF_NUMBER ? "numeric " : "",
			(*cl)->var->name);
		sprintf(sval, "%s", (*cl)->var->user_val.p
				     ? (*cl)->var->user_val.p : "");
	    }

	    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, MAXPATH,
				     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){
		    q_status_message(SM_ORDER,0,3,"Change cancelled");
		}
		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);
	}
    }

    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 */
	  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 = 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]
	   || (*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;
	    }
	    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;
	}
#if defined(DOS) || defined(OS2)
	else if((*cl)->var == &ps->vars[V_NORM_FORE_COLOR]
		|| (*cl)->var == &ps->vars[V_NORM_BACK_COLOR]
		|| (*cl)->var == &ps->vars[V_REV_FORE_COLOR]
		|| (*cl)->var == &ps->vars[V_REV_BACK_COLOR]){
	    if((*cl)->var->user_val.p)
	      fs_give((void **)&(*cl)->var->user_val.p);

	    (*cl)->var->user_val.p = cpystr(config_colors[(*cl)->varmem]);

	    if((*cl)->var == &ps->vars[V_NORM_FORE_COLOR])
	      pico_nfcolor((*cl)->var->user_val.p);
	    else if((*cl)->var == &ps->vars[V_NORM_BACK_COLOR])
	      pico_nbcolor((*cl)->var->user_val.p);
	    else if((*cl)->var == &ps->vars[V_REV_FORE_COLOR])
	      pico_rfcolor((*cl)->var->user_val.p);
	    else
	      pico_rbcolor((*cl)->var->user_val.p);
	    
	    ps->mangled_screen = 1;
	    rv = 1;
	}
#endif
	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':
		    q_status_message(SM_ORDER,0,3,"Add cancelled");
		    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){
		    q_status_message(SM_ORDER,0,3,"Add cancelled");
		}
		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){
		    q_status_message(SM_ORDER,0,3,"Add cancelled");
		}
		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'){
		q_status_message(SM_ORDER,0,3,"Change cancelled");
		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){
			q_status_message(SM_ORDER,0,3,"Change cancelled");
		    }
		    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){
			q_status_message(SM_ORDER,0,3,"Change cancelled");
		    }
		    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'){
		    q_status_message(SM_ORDER,0,3,"Change cancelled");
		    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){
			    q_status_message(SM_ORDER,0,3,"Change cancelled");
			}
			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_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)
	  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;
    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){
		    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){
		    cursor_pos->col = ctmp->valoffset;
		    if(ctmp->tool == radiobutton_tool
		       || ctmp->tool==checkbox_tool)
		      cursor_pos->col++;
		}

		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];

    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
      sprintf(tmp, "%-*s", ps->ttyo->screen_cols - cl->valoffset,
	      (cl->var->user_val.p && *cl->var->user_val.p)
				        ? cl->var->user_val.p
					: empty_val2);

    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_showcursor(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)){
	    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);
	    }
	}
    }
}


/*
 * 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]
              || 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;
	      }
	}
	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->inc_startup_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);
    }
#if defined(DOS) || defined(OS2)
    else if(revert
	   && (var == &ps->vars[V_NORM_FORE_COLOR]
	    || var == &ps->vars[V_NORM_BACK_COLOR]
	    || var == &ps->vars[V_REV_FORE_COLOR]
	    || var == &ps->vars[V_REV_BACK_COLOR])){
	if(var == &ps->vars[V_NORM_FORE_COLOR])
	  pico_nfcolor(var->user_val.p);
	else if(var == &ps->vars[V_NORM_BACK_COLOR])
	  pico_nbcolor(var->user_val.p);
	else if(var == &ps->vars[V_REV_FORE_COLOR])
	  pico_rfcolor(var->user_val.p);
	else
	  pico_rbcolor(var->user_val.p);
    }
#endif
    else if(var == &ps->vars[V_USE_ONLY_DOMAIN_NAME]){
	init_hostname(ps);
    }
}


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);
}


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);
}


/*
 * 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;
    ROLE_ACTION_S **role;
    int             alt_compose;
{
    PAT_HANDLE    *pattern_h = NULL;
    CONF_S        *ctmp = NULL, *first_line = NULL;
    OPT_SCREEN_S   screen;
    PAT_S         *pat, local_pat, *sel_pat = NULL;
    int            ret = -1;
    char          *title   = "HELP FOR SELECTING A ROLE TO COMPOSE AS";

    if(!role)
      return(ret);

    *role = NULL;

    if((pattern_h = open_patterns()) == NULL ||
       !first_pattern(pattern_h, ROLE_ANY)){
	if(pattern_h)
	  close_patterns(&pattern_h);

	q_status_message(SM_ORDER, 0, 3,
			 "No roles available. Use Setup to add roles.");
	return(ret);
    }

    new_confline(&ctmp);
    first_line = ctmp;

    ctmp->value        = cpystr("Default Role");
    ctmp->d.r.selected = &sel_pat;
    ctmp->d.r.pat      = &local_pat;
    ctmp->d.r.handle   = pattern_h;
    ctmp->keymenu      = &role_select_km;
    ctmp->help         = h_role_select;
    ctmp->help_title   = title;
    ctmp->tool         = role_select_tool;
    ctmp->flags        = CF_STARTITEM;
    ctmp->valoffset    = 4;

    if(alt_compose){
	menu_init_binding(ctmp->keymenu, 'C', MC_CHOICE, "C", "[ComposeAs]",
			  DEFAULT_KEY);
	menu_add_binding(ctmp->keymenu, ctrl('J'), MC_CHOICE);
	menu_add_binding(ctmp->keymenu, ctrl('M'), MC_CHOICE);
    }
    else{
	menu_init_binding(ctmp->keymenu, 'S', MC_CHOICE, "S", "[Select]",
			  DEFAULT_KEY);
	menu_add_binding(ctmp->keymenu, ctrl('J'), MC_CHOICE);
	menu_add_binding(ctmp->keymenu, ctrl('M'), MC_CHOICE);
    }

    for(pat = first_pattern(pattern_h, ROLE_ANY);
	pat;
	pat = next_pattern(pattern_h, ROLE_ANY)){
	new_confline(&ctmp);
	ctmp->value        = cpystr((pat->patgrp && pat->patgrp->nick)
					? pat->patgrp->nick : "?");
	ctmp->d.r.selected = &sel_pat;
	ctmp->d.r.pat      = pat;
	ctmp->d.r.handle   = pattern_h;
	ctmp->keymenu      = &role_select_km;
	ctmp->help         = h_role_select;
	ctmp->help_title   = title;
	ctmp->tool         = role_select_tool;
	ctmp->flags        = CF_STARTITEM;
	ctmp->valoffset    = 4;
    }

    (void)conf_scroll_screen(ps, &screen, first_line, "SELECT ROLE",
			     "roles ", 0);

    if(sel_pat){
	if(sel_pat != &local_pat)
	  *role = copy_role(sel_pat->action->role);

	ret = 0;
    }

    if(pattern_h)
      close_patterns(&pattern_h);

    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)
    struct pine *ps;
{
    char         path[MAXPATH+1], dir[MAXPATH+1];
    char        *p;
    CONF_S      *ctmp, *first_line;
    OPT_SCREEN_S screen;
    FILE        *fp;
    PAT_HANDLE  *pattern_h = NULL;

    dprint(4,(debugfile, "role_config_screen()\n"));

    if(ps->fix_fixed_warning && offer_to_fix_pinerc(ps))
      write_pinerc(ps);

    if((pattern_h = open_patterns()) == NULL)
      return;

uh_oh:
    ctmp = NULL;
    first_line = NULL;

    role_config_init_disp(ps, pattern_h, &ctmp, &first_line);
    switch(conf_scroll_screen(ps, &screen, first_line, "SETUP ROLES",
			      "roles ", 0)){
	case 0:
	case 10:
	  break;
	
	case 1:
	  if(write_patterns(pattern_h))
	    goto uh_oh;

	  break;
	
	default:
	  q_status_message(SM_ORDER,7,10, "conf_scroll_screen unexpected ret");
	  break;
    }

    close_patterns(&pattern_h);

    ps->mangled_screen = 1;
}


static char *role_help_title = "HELP FOR ROLE CONFIGURATION";

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", "PrevRole", {MC_PREVITEM, 1, {'p'}}, KS_NONE},
	{"N", "NextRole", {MC_NEXTITEM, 2, {'n', TAB}}, KS_NONE},
	PREVPAGE_MENU,
	NEXTPAGE_MENU,
	{"A", "Add Role", {MC_ADD,1,{'a'}}, KS_NONE},
	{"D", "Del Role", {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, pattern_h, ctmp, first_line)
    struct pine     *ps;
    PAT_HANDLE      *pattern_h;
    CONF_S         **ctmp;
    CONF_S         **first_line;
{
    PAT_LINE_S    *patline;

    if(first_line)
      *first_line = NULL;

    if(!first_pattern(pattern_h, ROLE_ANY)){
	add_fake_first_role(ctmp, 0, pattern_h);
	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, pattern_h, patline);
}


void
add_patline_to_display(ps, ctmp, before, first_line, top_line, handle, patline)
    struct pine *ps;
    CONF_S     **ctmp;
    int          before;
    CONF_S     **first_line;
    CONF_S     **top_line;
    PAT_HANDLE  *handle;
    PAT_LINE_S  *patline;
{
    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, "Roles 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 */
	if(pat->action && pat->action->role){
	    add_role_to_display(ctmp, patline, pat, 0,
				(first_line && *first_line == NULL)
				  ? first_line :
				    (top_line && *top_line == NULL)
				      ? top_line : NULL,
				firstitem, handle);
	    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 roles 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, handle)
    CONF_S     **ctmp;
    PAT_LINE_S  *patline;
    PAT_S       *pat;
    int          before;
    CONF_S     **first_line;
    int          firstitem;
    PAT_HANDLE  *handle;
{
    ROLE_ACTION_S *role;

    if(!(pat && pat->action && pat->action->role))
      return;

    role = pat->action->role;

    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)->value       = cpystr((pat && pat->patgrp && pat->patgrp->nick)
				    ? pat->patgrp->nick : "?");
    (*ctmp)->d.r.patline = patline;
    (*ctmp)->d.r.pat     = pat;
    (*ctmp)->d.r.handle  = handle;
    (*ctmp)->keymenu     = &role_conf_km;
    (*ctmp)->help        = h_role_config;
    (*ctmp)->help_title  = role_help_title;
    (*ctmp)->tool        = role_config_tool;
    (*ctmp)->flags      |= (firstitem ? CF_STARTITEM : 0);
    (*ctmp)->valoffset   = 4;
}


void
add_fake_first_role(ctmp, before, handle)
    CONF_S        **ctmp;
    int             before;
    PAT_HANDLE *handle;
{
    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;
    }

    (*ctmp)->value      = cpystr(ADD_FIRST_ROLE);
    (*ctmp)->d.r.handle = handle;
    (*ctmp)->keymenu    = &role_conf_km;
    (*ctmp)->help       = h_role_config;
    (*ctmp)->help_title = role_help_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;

    first_one = !first_pattern((*cl)->d.r.handle, ROLE_ANY);
    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);

	break;

      case MC_ADD :
	rv = role_config_add(ps, cl);
	break;

      case MC_EDIT :
	if(first_one)
	  rv = role_config_add(ps, cl);
	else
	  rv = role_config_edit(ps, cl);

	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 :
	rv = screen_exit_cmd(flags, "Role Configuration");
	break;

      case MC_ADDFILE :
	rv = role_config_addfile(ps, cl);
	break;

      case MC_DELFILE :
	rv = role_config_delfile(ps, cl);
	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)
    struct pine  *ps;
    CONF_S      **cl;
{
    int         rv = 0, first_pat = 0;
    PAT_S      *new_pat = NULL, *cur_pat;
    PAT_LINE_S *new_patline, *cur_patline;
    PAT_HANDLE *ph;

    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 role to ReadOnly file");
	return(rv);
    }

    ph = (*cl)->d.r.handle;

    if(role_config_edit_screen(ps, NULL, ph, "ADD A ROLE",
			       &new_pat) == 1 && new_pat){
	if(ps->never_allow_changing_from &&
	   new_pat->action &&
	   new_pat->action->role &&
	   new_pat->action->role->from)
	  q_status_message(SM_ORDER|SM_DING, 0, 3,
      "Site policy doesn't allow changing From address so From is ignored");

	rv = 1;
	ph = (*cl)->d.r.handle;
	cur_pat = (*cl)->d.r.pat;
	if(!cur_pat)
	  first_pat++;

	cur_patline = first_pat ? ph->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;
	    ph->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
		  ph->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 */
	    ph->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,
			      (*cl)->d.r.handle);
    }

    return(rv);
}


/* 
 * Change the current role.
 *
 * Returns  1 -- There were changes
 *          0 -- No changes
 */
int
role_config_edit(ps, cl)
    struct pine  *ps;
    CONF_S      **cl;
{
    int         rv = 0;
    PAT_S      *new_pat = NULL, *cur_pat, *prev_pat;
    PAT_LINE_S *new_patline, *cur_patline, *prev_patline;
    PAT_HANDLE *ph;

    if((*cl)->d.r.patline->readonly){
	q_status_message(SM_ORDER, 0, 3, "Can't change ReadOnly role");
	return(rv);
    }

    cur_pat = (*cl)->d.r.pat;
    ph = (*cl)->d.r.handle;

    if(role_config_edit_screen(ps, cur_pat, ph, "CHANGE THIS ROLE",
			       &new_pat) == 1 && new_pat){

	if(ps->never_allow_changing_from &&
	   new_pat->action &&
	   new_pat->action->role &&
	   new_pat->action->role->from)
	  q_status_message(SM_ORDER|SM_DING, 0, 3,
      "Site policy doesn't allow changing From address so From is ignored");

	rv = 1;
	ph = (*cl)->d.r.handle;

	/*
	 * 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)
	  ph->dirtypinerc = 1;
	else
	  new_pat->patline->dirty = 1;

	cur_pat->prev = 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)
    struct pine  *ps;
    CONF_S      **cl;
{
    int  rv = 0;
    char prompt[100];

    if((*cl)->d.r.patline->readonly){
	q_status_message(SM_ORDER, 0, 3, "Can't delete ReadOnly role");
	return(rv);
    }

    sprintf(prompt, "Really delete role \"%.20s\" ", (*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);
    }
    else
      q_status_message(SM_ORDER, 0, 3, "Role not deleted");
    
    return(rv);
}


void
delete_a_role(cl)
    CONF_S **cl;
{
    PAT_S      *cur_pat;
    CONF_S     *cp, *cq;
    PAT_HANDLE *ph;
    PAT_LINE_S *cur_patline;

    cur_pat = (*cl)->d.r.pat;
    ph  = (*cl)->d.r.handle;

    cur_patline = cur_pat->patline;

    if(cur_patline->type == Literal){	/* delete patline */
	if(cur_patline->prev)
	  cur_patline->prev->next = cur_patline->next;
	else
	  ph->patlinehead = cur_patline->next;

	if(cur_patline->next)
	  cur_patline->next->prev = cur_patline->prev;

	ph->dirtypinerc = 1;
	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;
	free_pat(&cur_pat);
    }

    /* delete the conf lines */

    if(!first_pattern(ph, ROLE_ANY)){		/* deleting last real role */
	if(!((*cl)->prev || (*cl)->next)){	/* deleting only line */
	    free_conflines(cl);
	    add_fake_first_role(cl, 0, ph);
	    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, ph);
	    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 role 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.pat && (*cl)->d.r.pat->patline)
	        ? (*cl)->d.r.pat->patline->type : TypeNotSet;
	    
    if(curtype == Literal){
	prevtype = ((*cl)->d.r.pat && (*cl)->d.r.pat->patline &&
		    (*cl)->d.r.pat->patline->prev)
		     ? (*cl)->d.r.pat->patline->prev->type : TypeNotSet;
	nexttype = ((*cl)->d.r.pat && (*cl)->d.r.pat->patline &&
		    (*cl)->d.r.pat->patline->next)
		     ? (*cl)->d.r.pat->patline->next->type : TypeNotSet;
	
	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)->d.r.pat->patline->prev->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)->d.r.pat->patline->next->readonly)
	      readonlybelow++;
	}
    }
    else if(curtype == File){
	prevtype = ((*cl)->d.r.pat && (*cl)->d.r.pat->prev)
		     ? File : TypeNotSet;
	nexttype = ((*cl)->d.r.pat && (*cl)->d.r.pat->next)
		     ? File : TypeNotSet;

	if((*cl)->d.r.pat && (*cl)->d.r.pat->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'){
	q_status_message(SM_ORDER, 0, 3, "Shuffle cancelled");
	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)
    struct pine  *ps;
    CONF_S      **cl;
{
    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.pat && (*cl)->d.r.pat->patline)
	        ? (*cl)->d.r.pat->patline->type : TypeNotSet;

    if(curtype == File){
	q_status_message(SM_ORDER, 0, 3, "Current role 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
     * 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 roles: ",
			     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')){
	    q_status_message(SM_ORDER, 0, 3, "IncludeFile cancelled");
	    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_HANDLE *ph;
	PAT_LINE_S *cur_patline;
	int         first_pat = 0;

	rv = ps->mangled_screen = 1;
	ph = (*cl)->d.r.handle;
	cur_patline = (*cl)->d.r.patline ? (*cl)->d.r.patline : ph->patlinehead;
	first_pat = !(*cl)->d.r.pat;

	ph->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
	      ph->patlinehead = file_patline;

	    cur_patline->prev = file_patline;
	}
	else{
	    /* set head of list */
	    ph->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, ph, file_patline);
		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, ph, file_patline);
		opt_screen->current = save_current;
	    }
	}
	else{
	    opt_screen->top_line = NULL;
	    add_patline_to_display(ps, cl, 1, NULL, &opt_screen->top_line, ph,
				   file_patline);
	    /*
	     * 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)
    struct pine  *ps;
    CONF_S      **cl;
{
    int         rv = 0;
    PAT_LINE_S *cur_patline;
    PAT_HANDLE *ph;
    char        prompt[100];

    if(!((*cl)->d.r.pat && (cur_patline = (*cl)->d.r.pat->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 role is not part of a file. Use Delete to remove current role");
	return(rv);
    }

    sprintf(prompt, "Really remove role file \"%.20s\" from roles 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;
	ph = (*cl)->d.r.handle;
	ph->dirtypinerc = 1;

	if(cur_patline->prev)
	  cur_patline->prev->next = cur_patline->next;
	else
	  ph->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(ph, ROLE_ANY)){
	    /*
	     * Find the start and prepend the fake first role
	     * in there.
	     */
	     while(*cl && (*cl)->prev)
	       *cl = (*cl)->prev;

	    add_fake_first_role(cl, 1, ph);
	}

	opt_screen->top_line = first_confline(*cl);
	opt_screen->current  = first_sel_confline(opt_screen->top_line);

	free_patline(&cur_patline);
    }
    else
      q_status_message(SM_ORDER, 0, 3, "Role 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;
    PAT_HANDLE *ph;

    ph    = a->d.r.handle;
    patline_a = a->d.r.pat->patline;
    patline_b = b->d.r.pat->patline;

    ph->dirtypinerc = 1;

    /* swap the patlines */
    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;

    /*
     * 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)
      ph->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_HANDLE *ph;

    ph    = a->d.r.handle;
    pat_a = a->d.r.pat;
    pat_b = b->d.r.pat;
    patline = pat_a->patline;

    patline->dirty = 1;

    /* first swap the pats */
    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;

    /*
     * 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;
    PAT_HANDLE *ph;
    CONF_S     *a, *b;

    ph    = (*cl)->d.r.handle;

    if(up){
	file_patline = (*cl)->prev->d.r.patline;
	cur_patline = (*cl)->d.r.patline;
	a = (*cl)->prev;
	b = (*cl);
	b->d.r.patline = file_patline;
    }
    else{
	cur_patline = (*cl)->d.r.patline;
	file_patline = (*cl)->next->d.r.patline;
	a = (*cl);
	b = (*cl)->next;
	a->d.r.patline = file_patline;
    }

    ph->dirtypinerc = 1;
    file_patline->dirty = 1;

    pat = cur_patline->first;

    if(!up && cur_patline == ph->patlinehead)
      ph->patlinehead = file_patline;

    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;
    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;
    PAT_HANDLE *ph;
    CONF_S     *a, *b;

    ph    = (*cl)->d.r.handle;

    new_patline = (PAT_LINE_S *)fs_get(sizeof(*new_patline));
    memset((void *)new_patline, 0, sizeof(*new_patline));
    new_patline->type = Literal;

    if(up){
	file_patline = (*cl)->d.r.patline;
	a = (*cl)->prev;
	b = (*cl);
	pat = file_patline->first;
	file_patline->first = pat->next;
	if(file_patline->first)
	  file_patline->first->prev = NULL;

	if(file_patline->last == pat)
	  file_patline->last = 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{
	file_patline = (*cl)->d.r.patline;
	a = (*cl);
	b = (*cl)->next;
	pat = file_patline->last;
	file_patline->last = pat->prev;
	if(file_patline->last)
	  file_patline->last->next = NULL;

	if(file_patline->first == pat)
	  file_patline->first = 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;
    }

    ph->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 == ph->patlinehead)
      ph->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 *patline_a, *patline_b, *file_patline;
    PAT_HANDLE *ph;
    CONF_S     *cp;

    ph    = (*cl)->d.r.handle;
    if(up){
	patline_a = (*cl)->d.r.pat->patline->prev;
	patline_b = (*cl)->d.r.pat->patline;
	file_patline = patline_a;
    }
    else{
	patline_a = (*cl)->d.r.pat->patline;
	patline_b = (*cl)->d.r.pat->patline->next;
	file_patline = patline_b;
    }

    ph->dirtypinerc = 1;

    /* swap the patlines */
    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;

    /*
     * 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)
      ph->patlinehead = patline_b;


    /*
     * 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_rule_ptr;
#define	 ALT_ROLE     "Alternate Role"

CONF_S *inick_confs[5];
#define INICK_INICK_CONF 0
#define INICK_FROM_CONF  1
#define INICK_FCC_CONF   2
#define INICK_SIG_CONF   3
#define INICK_TEMPL_CONF 4


/*
 * 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
 *      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, pattern_h, title, result)
    struct pine *ps;
    PAT_S       *def;
    PAT_HANDLE  *pattern_h;
    char        *title;
    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, news_pat_var,
		    subj_pat_var, inick_var, from_act_var,
		    fcc_act_var, sig_act_var, templ_act_var,
		    repl_type_var, forw_type_var, *v, *varlist[20];
    char           *nick = NULL, *to_pat = NULL, *from_pat = NULL,
		   *sender_pat = NULL, *cc_pat = NULL, *news_pat = NULL,
		   *subj_pat = NULL, *inick = NULL, *from_act = NULL,
		   *fcc_act = NULL, *sig_act = NULL, *templ_act = NULL,
		   *repl_type = NULL, *forw_type = NULL;
    char            tmp[MAXPATH+1];
    int             rv, i, j, lv, indent = 18;
    ROLE_NAMEVAL_S *f;

    /*
     * 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] = &news_pat_var;
    varlist[++j] = &subj_pat_var;
    varlist[++j] = &inick_var;
    varlist[++j] = &from_act_var;
    varlist[++j] = &fcc_act_var;
    varlist[++j] = &sig_act_var;
    varlist[++j] = &templ_act_var;
    varlist[++j] = &repl_type_var;
    varlist[++j] = &forw_type_var;
    varlist[++j] = NULL;
    for(j = 0; varlist[j]; j++)
      memset(varlist[j], 0, sizeof(struct variable));

    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(ALT_ROLE);
    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);

    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);

    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->role &&
			    def->action->role->inherit_nick &&
			    def->action->role->inherit_nick[0])
			       ? cpystr(def->action->role->inherit_nick) : NULL;
    inick_var.global_val.p = cpystr("Default Role");

    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->role && def->action->role->from){
	char *bufp;

	bufp = (char *)fs_get((size_t)est_size(def->action->role->from));
	from_act_var.user_val.p = addr_string(def->action->role->from, bufp);
    }
    else
      from_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->role &&
			      def->action->role->fcc)
			       ? cpystr(def->action->role->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->role &&
			      def->action->role->sig)
			       ? cpystr(def->action->role->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->role &&
			        def->action->role->template)
			         ? cpystr(def->action->role->template) : NULL;

    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->role) ? def->action->role->repl_type : -1)) ? cpystr(f->name) : NULL;
    set_current_val(&repl_type_var, FALSE, FALSE);

    role_rule_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->role) ? def->action->role->forw_type : -1)) ? cpystr(f->name) : NULL;
    set_current_val(&forw_type_var, FALSE, FALSE);


    /* 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 = indent;
    ctmp->keymenu   = &config_text_keymenu;
    ctmp->help      = h_config_role_nick;
    ctmp->tool      = role_text_tool;
    sprintf(tmp, "%-*s =", indent-3, nick_var.name);
    ctmp->varname   = cpystr(tmp);
    ctmp->varnamep  = ctmp;
    ctmp->value     = pretty_value(ps, ctmp);

    first_line = ctmp;

    /* To Pattern */
    new_confline(&ctmp);
    ctmp->help_title= "HELP FOR TO PATTERN";
    ctmp->var       = &to_pat_var;
    ctmp->valoffset = indent;
    ctmp->keymenu   = &config_role_addr_keymenu;
    ctmp->help      = h_config_role_topat;
    ctmp->tool      = role_text_tool;
    sprintf(tmp, "%-*s =", indent-3, to_pat_var.name);
    ctmp->varname   = cpystr(tmp);
    ctmp->varnamep  = ctmp;
    ctmp->value     = pretty_value(ps, ctmp);

    /* From Pattern */
    new_confline(&ctmp);
    ctmp->help_title= "HELP FOR FROM PATTERN";
    ctmp->var       = &from_pat_var;
    ctmp->valoffset = indent;
    ctmp->keymenu   = &config_role_addr_keymenu;
    ctmp->help      = h_config_role_frompat;
    ctmp->tool      = role_text_tool;
    sprintf(tmp, "%-*s =", indent-3, from_pat_var.name);
    ctmp->varname   = cpystr(tmp);
    ctmp->varnamep  = ctmp;
    ctmp->value     = pretty_value(ps, ctmp);

    /* Sender Pattern */
    new_confline(&ctmp);
    ctmp->help_title= "HELP FOR SENDER PATTERN";
    ctmp->var       = &sender_pat_var;
    ctmp->valoffset = indent;
    ctmp->keymenu   = &config_role_addr_keymenu;
    ctmp->help      = h_config_role_senderpat;
    ctmp->tool      = role_text_tool;
    sprintf(tmp, "%-*s =", indent-3, sender_pat_var.name);
    ctmp->varname   = cpystr(tmp);
    ctmp->varnamep  = ctmp;
    ctmp->value     = pretty_value(ps, ctmp);

    /* Cc Pattern */
    new_confline(&ctmp);
    ctmp->help_title= "HELP FOR CC PATTERN";
    ctmp->var       = &cc_pat_var;
    ctmp->valoffset = indent;
    ctmp->keymenu   = &config_role_addr_keymenu;
    ctmp->help      = h_config_role_ccpat;
    ctmp->tool      = role_text_tool;
    sprintf(tmp, "%-*s =", indent-3, cc_pat_var.name);
    ctmp->varname   = cpystr(tmp);
    ctmp->varnamep  = ctmp;
    ctmp->value     = pretty_value(ps, ctmp);

    /* News Pattern */
    new_confline(&ctmp);
    ctmp->help_title= "HELP FOR NEWS PATTERN";
    ctmp->var       = &news_pat_var;
    ctmp->valoffset = indent;
    ctmp->keymenu   = &config_text_keymenu;
    ctmp->help      = h_config_role_newspat;
    ctmp->tool      = role_text_tool;
    sprintf(tmp, "%-*s =", indent-3, news_pat_var.name);
    ctmp->varname   = cpystr(tmp);
    ctmp->varnamep  = ctmp;
    ctmp->value     = pretty_value(ps, ctmp);

    /* Subject Pattern */
    new_confline(&ctmp);
    ctmp->help_title= "HELP FOR SUBJECT PATTERN";
    ctmp->var       = &subj_pat_var;
    ctmp->valoffset = indent;
    ctmp->keymenu   = &config_text_keymenu;
    ctmp->help      = h_config_role_subjpat;
    ctmp->tool      = role_text_tool;
    sprintf(tmp, "%-*s =", indent-3, subj_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;

    /* Inherit Nickname */
    new_confline(&ctmp);
    inick_confs[INICK_INICK_CONF] = ctmp;
    ctmp->d.r.handle = pattern_h;
    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_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;

    /* 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_fcc_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;
    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;
    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, pattern_h);

    /* 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      = role_radiobutton_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      = role_radiobutton_tool;
	ctmp->varnamep  = ctmpb;
	sprintf(tmp, "(%c)  %-*.*s", (((!(def && def->action &&
					  def->action->role) ||
					 def->action->role->repl_type == -1) &&
				        f->value == ROLE_REPL_DEFL) ||
				      (def && def->action &&
				       def->action->role &&
				      f->value == def->action->role->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      = role_radiobutton_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      = role_radiobutton_tool;
	ctmp->varnamep  = ctmpb;
	sprintf(tmp, "(%c)  %-*.*s", (((!(def && def->action &&
					  def->action->role) ||
					 def->action->role->forw_type == -1) &&
				        f->value == ROLE_FORW_DEFL) ||
				      (def && def->action &&
				       def->action->role &&
				      f->value == def->action->role->forw_type))
				         ? R_SELD : ' ',
		lv, lv, f->name);
	ctmp->value     = cpystr(tmp);
    }

    rv = conf_scroll_screen(ps, &screen, first_line, title, "roles ", 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);
	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);
	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);
	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);
	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);

	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);
	}

	*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)->action->role =
		(ROLE_ACTION_S *)fs_get(sizeof(*(*result)->action->role));
	memset((void *)(*result)->action->role, 0,
	       sizeof(*(*result)->action->role));

	(*result)->patline = def ? def->patline : NULL;
	
	if(nick && *nick){
	    (*result)->patgrp->nick = nick;
	    nick = NULL;
	}
	else
	  (*result)->patgrp->nick = cpystr(ALT_ROLE);

	(*result)->action->role->nick = cpystr((*result)->patgrp->nick);

	(*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->news   = string_to_pattern(news_pat);
	(*result)->patgrp->subj   = string_to_pattern(subj_pat);
	(*result)->action->role->inherit_nick = inick;
	inick = NULL;
	(*result)->action->role->fcc = fcc_act;
	fcc_act = NULL;
	(*result)->action->role->sig = sig_act;
	sig_act = NULL;
	(*result)->action->role->template = templ_act;
	templ_act = NULL;
	if(from_act && *from_act)
	  rfc822_parse_adrlist(&(*result)->action->role->from, from_act,
			       ps->maildomain);

	if(repl_type && *repl_type){
	    for(j = 0; f = role_repl_types(j); j++)
	      if(!strucmp(repl_type, f->name)){
		  (*result)->action->role->repl_type = f->value;
		  break;
	      }
	}
	else{
	    f = role_repl_types(ROLE_REPL_DEFL);
	    if(f)
	      (*result)->action->role->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->role->forw_type = f->value;
		  break;
	      }
	}
	else{
	    f = role_forw_types(ROLE_FORW_DEFL);
	    if(f)
	      (*result)->action->role->forw_type = f->value;
	}

	if((*result)->action->role->repl_type == ROLE_REPL_NO &&
	   (*result)->action->role->forw_type == ROLE_FORW_NO)
	  q_status_message1(SM_ORDER, 0, 3,
  "Warning: Both \"Reply Use\" and \"Forward Use\" are set to Never for \"%s\"",
			    (*result)->action->role->nick);
    }

    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(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(news_pat)
      fs_give((void **)&news_pat);
    if(subj_pat)
      fs_give((void **)&subj_pat);
    if(inick)
      fs_give((void **)&inick);
    if(from_act)
      fs_give((void **)&from_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(repl_type)
      fs_give((void **)&repl_type);
    if(forw_type)
      fs_give((void **)&forw_type);

    opt_screen = saved_screen;
    ps->mangled_screen = 1;
    return(rv);
}


void
calculate_inick_stuff(ps, pattern_h)
    struct pine *ps;
    PAT_HANDLE *pattern_h;
{
    ROLE_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 that use the
	 * combine function to find the action values.
	 */
	role = (ROLE_ACTION_S *)fs_get(sizeof(*role));
	memset((void *)role, 0, sizeof(*role));
	role->inherit_nick = cpystr(nick);
	irole = combine_inherited_role(role, pattern_h);

	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_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_role(&role);
	free_role(&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_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 *
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);
}


/*
 * 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;
    CONF_S    *ctmp;
    ROLE_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 == role_rule_ptr)
	  rule = role_forw_types((*cl)->varmem);
	else
	  rule = role_repl_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);
}


/*
 */
int
role_text_tool(ps, cmd, cl, flags)
    struct pine  *ps;
    int		  cmd;
    CONF_S      **cl;
    unsigned      flags;
{
    int   rv = -1, len, sig, r;
    char *file, *err, title[100], *newfile, *lc, *addr, *fldr;
    char  dir[MAXPATH+1], dir2[MAXPATH+1], pdir[MAXPATH+1];
    char  full_filename[MAXPATH+1], filename[MAXPATH+1], filename2[MAXPATH+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 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(dir2, pdir);

	filename2[0] = '\0';
	build_path(full_filename, dir2, filename2);

	r = file_lister(title, dir2, MAXPATH+1,
			filename2, MAXPATH+1, 
			TRUE, FB_SAVE);
	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 from address book */
	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_CHOICEC :				/* Choose a Folder */
	fldr = folders_for_fcc(NULL);
	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;

      default :
	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;
	 ROLE_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);
	    free_role(&role);
	    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, (*cl)->d.r.handle);

	ps->mangled_screen = 1;
	break;

      default :
	rv = text_tool(ps, cmd, cl, flags);
	break;
    }

    return(rv);
}


#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 */
