#if !defined(lint) && !defined(DOS)
static char rcsid[] = "$Id: other.c,v 4.85 1994/10/08 00:28:43 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"

   Copyright 1989-1994  University of Washington

    Permission to use, copy, modify, and distribute this software and its
   documentation for any purpose and without fee to the University of
   Washington is hereby granted, provided that the above copyright notice
   appears in all copies and that both the above copyright notice and this
   permission notice appear in supporting documentation, and that the name
   of the University of Washington not be used in advertising or publicity
   pertaining to distribution of the software without specific, written
   prior permission.  This software is made available "as is", and
   THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
   WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
   NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
   INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
   LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
   (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
   WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  
   Pine and Pico are 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 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 HEADER_LINES	2
#define BOTTOM_LINES	3
#define	FIRST_DLINE	2		/* first screen line for display */

#define	CONFIG_SCREEN_TITLE		"SETUP CONFIGURATION"
#define	CONFIG_SCREEN_HELP_TITLE	"HELP FOR SETUP CONFIGURATION"
#define	R_SELD				'*'


/*
 * Information used to paint and maintain a line on the configuration screen
 */
typedef struct conf_line {
    char	     *varname,			/* alloc'd var name string   */
		     *value;			/* alloc'd var value string  */
    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      */
    short	      unselectable;		/* not user selectable line  */
    struct conf_line *varnamep;			/* pointer to varname        */
    struct conf_line *next, *prev;
} CONF_S;


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


#define	next_confline(p)	((p) ? (p)->next : NULL)
#define	prev_confline(p)	((p) ? (p)->prev : NULL)
static OPT_SCREEN_S *opt_screen;
#ifdef	DOS
static char *config_colors[] = {"black", "blue", "green", "cyan", "red",
				"magenta", "yellow", "white", NULL};
#endif


#ifdef ANSI
void	 draw_klocked_body(char *, char *);
int	 update_option_screen(struct pine *, CONF_S *, struct conf_screen *);
void	 option_screen_redrawer();
HelpType config_help(int, int);
int	 text_tool(struct pine *, int, CONF_S **, int);
int	 checkbox_tool(struct pine *, int, CONF_S **, int);
int	 radiobutton_tool(struct pine *, int, CONF_S **, int);
int	 yesno_tool(struct pine *, int, CONF_S **, int);
char	*pretty_value(struct pine *, CONF_S *);
void	 toggle_feature_bit(struct pine *, int, CONF_S *);
CONF_S	*new_confline(CONF_S **);
void	 free_confline(CONF_S **);
CONF_S	*first_confline(CONF_S *);
void	 config_add_list(struct pine *, CONF_S **, char **, char ***);
void	 config_del_list_item(CONF_S **, char ***);
int      offer_to_fix_pinerc(struct pine *);

#else
void	 draw_klocked_body();
int	 update_option_screen();
void	 option_screen_redrawer();
HelpType config_help();
int	 text_tool();
int	 checkbox_tool();
int	 radiobutton_tool();
int	 yesno_tool();
char	*pretty_value();
void	 toggle_feature_bit();
CONF_S	*new_confline();
void	 free_confline();
CONF_S	*first_confline();
void	 config_add_list();
void	 config_del_list_item();
int      offer_to_fix_pinerc();
#endif

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];
    HelpType help = NO_HELP;
    SigType (*hold_quit)();

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

    inpasswd[0] = '\0';
    while(1){			/* input pasword to use for locking */
        int rc;
        rc =  optionally_enter(inpasswd, -3, 0, 30, 0, 1,
			       "Enter password to LOCK keyboard : ", NULL,
                               help, 0);

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

    if(want_to("Really lock keyboard with entered password", 'y', 'n',
	       NO_HELP, 0, 0) != 'y'){
	q_status_message(0, 0, 2, "Lock keyboard 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;
    while(strcmp(inpasswd, passwd)){
	if(passwd[0]){
	    q_status_message(1,2,4,
		"\007Password to UNLOCK doesn't match password used to LOCK");
	    display_message('x');
	    sleep(3);
	}
        
        help = NO_HELP;
        while(1){
	    int rc;
	    rc =  optionally_enter(passwd,  -3, 0, 30, 0, 1, 
				   "Enter password to UNLOCK keyboard : ",NULL,
				   help, 1);
	    if(rc == 3) {
		help = help == NO_HELP ? h_oe_keylock : NO_HELP;
		continue;
	    }

	    if(rc != 4)
	      break;
        }
    }

    q_status_message(0, 1,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 */



static struct key config_text_keys[] = 
       {{"?","Help",0},	        {NULL,NULL, 0},       {"E","Exit Config",0},
	{"C","[Change Val]",0}, {"P","Prev", 0},      {"N","Next", 0},
        {"-","PrevPage",0},     {"Spc","NextPage",0}, {"A","Add Value",0},
	{"D","Delete Val",0} ,  {NULL,NULL,0},        {"W","WhereIs",0}};
static struct key_menu config_text_keymenu = {sizeof(config_text_keys)/(sizeof(config_text_keys[0])*12), 0, 0, 0, 0, 0, config_text_keys};

static struct key config_checkbox_keys[] = 
       {{"?","Help",0},	       {NULL,NULL, 0},       {"E","Exit Config",0},
	{"X","[Set/Unset]",0}, {"P","Prev", 0},      {"N","Next", 0},
        {"-","PrevPage",0},    {"Spc","NextPage",0}, {NULL,NULL,0},
	{NULL,NULL,0},         {NULL,NULL,0},        {"W","WhereIs",0}};
static struct key_menu config_checkbox_keymenu = {sizeof(config_checkbox_keys)/(sizeof(config_checkbox_keys[0])*12), 0, 0, 0, 0, 0, config_checkbox_keys};

static struct key config_radiobutton_keys[] = 
       {{"?","Help",0},	       {NULL,NULL, 0},       {"E","Exit Config",0},
	{"S","[Select]",0},    {"P","Prev", 0},      {"N","Next", 0},
        {"-","PrevPage",0},    {"Spc","NextPage",0}, {NULL,NULL,0},
	{NULL,NULL,0},         {NULL,NULL,0},        {"W","WhereIs",0}};
static struct key_menu config_radiobutton_keymenu = {sizeof(config_radiobutton_keys)/(sizeof(config_radiobutton_keys[0])*12), 0, 0, 0, 0, 0, config_radiobutton_keys};

static struct key config_yesno_keys[] = 
       {{"?","Help",0},	       {NULL,NULL, 0},       {"E","Exit Config",0},
	{"C","[Change]",0},    {"P","Prev", 0},      {"N","Next", 0},
        {"-","PrevPage",0},    {"Spc","NextPage",0}, {NULL,NULL,0},
	{NULL,NULL,0},         {NULL,NULL,0},        {"W","WhereIs",0}};
static struct key_menu config_yesno_keymenu = {sizeof(config_yesno_keys)/(sizeof(config_yesno_keys[0])*12), 0, 0, 0, 0, 0, config_yesno_keys};


/*
 * test that variables must pass to make it into the menu...
 */
#ifdef	_WINDOWS
#define	V_INCLUDE(P,V)	((V)->is_user && (V)->is_used && !(V)->is_obsolete \
			  && (V) != &(P)->vars[V_MAIL_DIRECTORY]	    \
			  && (V) != &(P)->vars[V_INCOMING_FOLDERS]	    \
			  && (V) != &(P)->vars[V_PRINTER]		    \
			  && (V) != &(P)->vars[V_PERSONAL_PRINT_COMMAND]    \
			  && (V) != &(P)->vars[V_STANDARD_PRINTER]	    \
			  && (V) != &(P)->vars[V_LAST_TIME_PRUNE_QUESTION]  \
			  && (V) != &(P)->vars[V_FONT_NAME]		    \
			  && (V) != &(P)->vars[V_FONT_SIZE]		    \
			  && (V) != &(P)->vars[V_FONT_STYLE]		    \
			  && (V) != &(P)->vars[V_WINDOW_POSITION]	    \
			  && (V) != &(P)->vars[V_LAST_VERS_USED])
#else
#define	V_INCLUDE(P,V)	((V)->is_user && (V)->is_used && !(V)->is_obsolete \
			  && (V) != &(P)->vars[V_MAIL_DIRECTORY]	    \
			  && (V) != &(P)->vars[V_INCOMING_FOLDERS]	    \
			  && (V) != &(P)->vars[V_PRINTER]		    \
			  && (V) != &(P)->vars[V_PERSONAL_PRINT_COMMAND]    \
			  && (V) != &(P)->vars[V_STANDARD_PRINTER]	    \
			  && (V) != &(P)->vars[V_LAST_TIME_PRUNE_QUESTION]  \
			  && (V) != &(P)->vars[V_LAST_VERS_USED])
#endif


/*
 * Test to return if the given feature should be included
 * in the config screen.
 */
#ifdef	DOS
/*
 * Only exclude old-growth as it simplifies the interface (not to 
 * mention code that supports it!).
 */
#define	F_INCLUDE(F)	((F) != F_OLD_GROWTH			\
			 && (F) != F_DISABLE_CONFIG_SCREEN	\
			 && (F) != F_DISABLE_PASSWORD_CMD	\
			 && (F) != F_DISABLE_KBLOCK_CMD		\
			 && (F) != F_DISABLE_UPDATE_CMD)
#else
/*
 * Otherwise, we also exclude function key mode, as it usually requires
 * some shenanigans during terminal initialization to enable.  This
 * isn't necessary under DOS/Windows.
 */
#define	F_INCLUDE(F)	((F) != F_OLD_GROWTH && (F) != F_USE_FK		\
			 && (F) != F_DISABLE_CONFIG_SCREEN		\
			 && (F) != F_DISABLE_PASSWORD_CMD		\
			 && (F) != F_DISABLE_KBLOCK_CMD			\
			 && (F) != F_DISABLE_UPDATE_CMD)
#endif




/*----------------------------------------------------------------------
    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, ch, orig_ch, done = 0, changes = 0, dline, ln = 0, lv;
    struct	  key_menu  *km = NULL;
    struct	  variable  *vtmp;
    CONF_S	 *current = NULL, *ctmp = NULL;
    OPT_SCREEN_S  screen;
    NAMEVAL_S	 *f;

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

    if(ps->fix_fixed_warning){
	set_titlebar(CONFIG_SCREEN_TITLE, ps->mail_stream,
		     ps->context_current,
		     ps->cur_folder, ps->msgmap, 1, FolderName, 0, 0);
        if(offer_to_fix_pinerc(ps))
          write_pinerc(ps);
    }

    screen.current = screen.top_line = NULL;

    /*
     * First, find longest variable name
     */
    for(vtmp = ps->vars; vtmp->name; vtmp++){
	if(!V_INCLUDE(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(!V_INCLUDE(ps, vtmp))
	  continue;

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

	sprintf(tmp, "%-*s =", ln, vtmp->name);
	current->varname  = cpystr(tmp);
	current->varnamep = ctmp = current;
	if(vtmp == &ps->vars[V_FEATURE_LIST]){	/* special checkbox case */
	    current->unselectable = 1;
	    current->keymenu      = &config_checkbox_keymenu;
	    current->help	  = config_help(vtmp - ps->vars, 0);
	    current->tool	  = NULL;

	    /* put a nice delimiter before list */
	    new_confline(&current)->var = NULL;
	    current->varnamep		= ctmp;
	    current->keymenu	        = &config_checkbox_keymenu;
	    current->help	        = NO_HELP;
	    current->tool	        = checkbox_tool;
	    current->valoffset	        = 12;
	    current->unselectable       = 1;
	    current->value = cpystr("Set        Feature Name");

	    new_confline(&current)->var = NULL;
	    current->varnamep		= ctmp;
	    current->keymenu	        = &config_checkbox_keymenu;
	    current->help	        = NO_HELP;
	    current->tool	        = checkbox_tool;
	    current->valoffset	        = 12;
	    current->unselectable       = 1;
	    current->value = cpystr("---   ----------------------");

	    /* find longest value's name */
	    for(lv = 0, i = 0; f = feature_list(i); i++)
	      if(F_INCLUDE(f->value))
		if(lv < (j = strlen(f->name)))
		  lv = j;
	    
	    for(i = 0; f = feature_list(i); i++){
		if(F_INCLUDE(f->value)){
		    new_confline(&current)->var = vtmp;
		    current->varnamep		= ctmp;
		    current->keymenu	        = &config_checkbox_keymenu;
		    current->help	        = config_help(vtmp-ps->vars,
							      f->value);
		    current->tool	        = checkbox_tool;
		    current->valoffset	        = 12;
		    current->varmem	        = i;
		    sprintf(tmp, "[%c]  %-*.*s",
			    F_ON(f->value, ps) ? 'X' : ' ', lv, lv, f->name);
		    current->value = cpystr(tmp);
		}
	    }
	}
	else if(vtmp == &ps->vars[V_SAVED_MSG_NAME_RULE]){ /* radio case */
	    current->unselectable = 1;
	    current->keymenu      = &config_radiobutton_keymenu;
	    current->help	  = config_help(vtmp - ps->vars, 0);
	    current->tool	  = NULL;

	    /* put a nice delimiter before list */
	    new_confline(&current)->var = NULL;
	    current->varnamep		= ctmp;
	    current->keymenu	        = &config_radiobutton_keymenu;
	    current->help	        = NO_HELP;
	    current->tool	        = radiobutton_tool;
	    current->valoffset	        = 12;
	    current->unselectable       = 1;
	    current->value = cpystr("Set       Rule Values");

	    new_confline(&current)->var = NULL;
	    current->varnamep		= ctmp;
	    current->keymenu	        = &config_radiobutton_keymenu;
	    current->help	        = NO_HELP;
	    current->tool	        = radiobutton_tool;
	    current->valoffset	        = 12;
	    current->unselectable       = 1;
	    current->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(&current)->var = vtmp;
		current->varnamep	    = ctmp;
		current->keymenu	    = &config_radiobutton_keymenu;
		current->help		    = config_help(vtmp - ps->vars, 0);
		current->tool		    = radiobutton_tool;
		current->valoffset	    = 12;
		current->varmem		    = i;
		sprintf(tmp, "(%c)  %-*.*s",
			(ps->save_msg_rule == f->value) ? R_SELD : ' ',
			lv, lv, f->name);
		current->value = cpystr(tmp);
	    }
	}
	else if(vtmp == &ps->vars[V_FCC_RULE]){		/* radio case */
	    current->unselectable = 1;
	    current->keymenu      = &config_radiobutton_keymenu;
	    current->help	  = config_help(vtmp - ps->vars, 0);
	    current->tool	  = NULL;

	    /* put a nice delimiter before list */
	    new_confline(&current)->var = NULL;
	    current->varnamep		= ctmp;
	    current->keymenu	        = &config_radiobutton_keymenu;
	    current->help	        = NO_HELP;
	    current->tool	        = radiobutton_tool;
	    current->valoffset	        = 12;
	    current->unselectable       = 1;
	    current->value = cpystr("Set       Rule Values");

	    new_confline(&current)->var = NULL;
	    current->varnamep		= ctmp;
	    current->keymenu	        = &config_radiobutton_keymenu;
	    current->help	        = NO_HELP;
	    current->tool	        = radiobutton_tool;
	    current->valoffset	        = 12;
	    current->unselectable       = 1;
	    current->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(&current)->var = vtmp;
		current->varnamep	    = ctmp;
		current->keymenu	    = &config_radiobutton_keymenu;
		current->help		    = config_help(vtmp - ps->vars, 0);
		current->tool		    = radiobutton_tool;
		current->valoffset	    = 12;
		current->varmem		    = i;
		sprintf(tmp, "(%c)  %-*.*s",
			(ps->fcc_rule == f->value) ? R_SELD : ' ',
			lv, lv, f->name);
		current->value = cpystr(tmp);
	    }
	}
	else if(vtmp == &ps->vars[V_SORT_KEY]){ /* radio case */
	    current->unselectable = 1;
	    current->keymenu      = &config_radiobutton_keymenu;
	    current->help	  = config_help(vtmp - ps->vars, 0);
	    current->tool	  = NULL;

	    /* put a nice delimiter before list */
	    new_confline(&current)->var = NULL;
	    current->varnamep		= ctmp;
	    current->keymenu	        = &config_radiobutton_keymenu;
	    current->help	        = NO_HELP;
	    current->tool	        = radiobutton_tool;
	    current->valoffset	        = 12;
	    current->unselectable       = 1;
	    current->value = cpystr("Set       Sort Options");

	    new_confline(&current)->var = NULL;
	    current->varnamep		= ctmp;
	    current->keymenu	        = &config_radiobutton_keymenu;
	    current->help	        = NO_HELP;
	    current->tool	        = radiobutton_tool;
	    current->valoffset	        = 12;
	    current->unselectable       = 1;
	    current->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++){
		    if(strucmp(sort_name(i), "to") /* if implemented... */
		       && strucmp(sort_name(i), "cc")){
			new_confline(&current)->var = vtmp;
			current->varnamep  = ctmp;
			current->keymenu   = &config_radiobutton_keymenu;
			current->help	   = config_help(vtmp - ps->vars, 0);
			current->tool	   = radiobutton_tool;
			current->valoffset = 12;

			/*
			 * varmem == sort_type index (reverse doubles index)
			 */
			current->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, "");
			current->value = cpystr(tmp);
		    }
		}
	    }
	}
	else if(vtmp == &ps->vars[V_AB_SORT_RULE]){	/* radio case */
	    current->unselectable = 1;
	    current->keymenu      = &config_radiobutton_keymenu;
	    current->help	  = config_help(vtmp - ps->vars, 0);
	    current->tool	  = NULL;

	    /* put a nice delimiter before list */
	    new_confline(&current)->var = NULL;
	    current->varnamep		= ctmp;
	    current->keymenu	        = &config_radiobutton_keymenu;
	    current->help	        = NO_HELP;
	    current->tool	        = radiobutton_tool;
	    current->valoffset	        = 12;
	    current->unselectable       = 1;
	    current->value = cpystr("Set       Rule Values");

	    new_confline(&current)->var = NULL;
	    current->varnamep		= ctmp;
	    current->keymenu	        = &config_radiobutton_keymenu;
	    current->help	        = NO_HELP;
	    current->tool	        = radiobutton_tool;
	    current->valoffset	        = 12;
	    current->unselectable       = 1;
	    current->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(&current)->var = vtmp;
		current->varnamep	    = ctmp;
		current->keymenu	    = &config_radiobutton_keymenu;
		current->help		    = config_help(vtmp - ps->vars, 0);
		current->tool		    = radiobutton_tool;
		current->valoffset	    = 12;
		current->varmem		    = i;
		sprintf(tmp, "(%c)  %-*.*s",
			(ps->ab_sort_rule == f->value) ? R_SELD : ' ',
			lv, lv, f->name);
		current->value = cpystr(tmp);
	    }
	}
#ifdef	DOS
	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]){
	    current->unselectable = 1;
	    current->keymenu      = &config_radiobutton_keymenu;
	    current->help	  = config_help(vtmp - ps->vars, 0);
	    current->tool	  = NULL;

	    /* put a nice delimiter before list */
	    new_confline(&current)->var = NULL;
	    current->varnamep		= ctmp;
	    current->keymenu	        = &config_radiobutton_keymenu;
	    current->help	        = NO_HELP;
	    current->tool	        = radiobutton_tool;
	    current->valoffset	        = 12;
	    current->unselectable       = 1;
	    current->value = cpystr("Set       Color Options");

	    new_confline(&current)->var = NULL;
	    current->varnamep		= ctmp;
	    current->keymenu	        = &config_radiobutton_keymenu;
	    current->help	        = NO_HELP;
	    current->tool	        = radiobutton_tool;
	    current->valoffset	        = 12;
	    current->unselectable       = 1;
	    current->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(&current)->var = vtmp;
		current->varnamep  = ctmp;
		current->keymenu   = &config_radiobutton_keymenu;
		current->help      = config_help(vtmp - ps->vars, 0);
		current->tool      = radiobutton_tool;
		current->valoffset = 12;

		/*
		 * varmem == sort_type index (reverse doubles index)
		 */
		current->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]);
		current->value = cpystr(tmp);
	    }
	}
#endif
	else if(vtmp == &ps->vars[V_USE_ONLY_DOMAIN_NAME]){ /* yesno case */
	    current->keymenu      = &config_yesno_keymenu;
	    current->help	  = config_help(vtmp - ps->vars, 0);
	    current->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, "");

	    current->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(&current);

		    current->var       = vtmp;
		    current->varmem    = i;
		    current->valoffset = ln + 3;
		    current->value     = pretty_value(ps, current);
		    current->keymenu   = &config_text_keymenu;
		    current->help      = config_help(vtmp - ps->vars, 0);
		    current->tool      = text_tool;
		    current->varnamep  = ctmp;
		}
	    }
	    else{
		current->varmem = 0;
		current->value  = pretty_value(ps, current);
	    }
	}
	else
	  current->value = pretty_value(ps, current);
    }

    current	       = first_confline(current);
    ps->mangled_screen = 1;
    ps->redrawer       = option_screen_redrawer;
    opt_screen	       = &screen;

    while(!done){
	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(NULL, 0,ch==NO_OP_IDLE ? 0 : ch==NO_OP_COMMAND ?1 :2) >= 0)
          ps->mangled_header = 1;

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

	/*
	 * Move past unselectable items...
	 */
	while(current->unselectable){	/* BUG: if first or last line...    */
	    if(ch == KEY_UP)		/*      unselectable, we're screwed */
	      current = prev_confline(current);
	    else if(ch == KEY_DOWN)
	      current = next_confline(current);
	}

	dline = update_option_screen(ps, current, &screen);

	/*---- This displays new mail notification, or errors ---*/
        display_message(ch);

        /*------ Read the command from the keyboard ----*/
	if(ps->mangled_footer || km != current->keymenu){
	    bitmap_t	 bitmap;

	    setbitmap(bitmap);
	    ps->mangled_footer = 0;
	    km                 = current->keymenu;
	    draw_keymenu(km, bitmap, ps->ttyo->screen_cols,-2, 0, FirstMenu,0);
	}

	MoveCursor(max(0, ps->ttyo->screen_rows - 3), 0);
	ch = orig_ch = read_command();

        if(ch <= 0xff && isupper(ch))
          ch = tolower(ch);

	switch(ch){
	  case '?' :				/* help! */
	  case ctrl('G'):
	  case PF1 :
	    if(current->help != NO_HELP){
		helper(current->help, CONFIG_SCREEN_HELP_TITLE, 0);
		ps->mangled_screen = 1;
	    }
	    else
	      q_status_message(0,0,3,"\007No help yet!");

	    break;

	  case 'e' :				/* exit options screen */
	  case PF3 :
	    done++;
	    break;

	  case 'n' :				/* next list element */
	  case '\t' :
	  case ctrl('F') :
	  case KEY_RIGHT :
	  case ctrl('N'):			/* down arrow */
	  case KEY_DOWN :
	  case PF6 :
	    ch = KEY_DOWN;
	    if(ctmp = next_confline(current))
	      current = ctmp;

	    break;

	  case 'p' :				/* previous list element */
	  case ctrl('B') :
	  case KEY_LEFT :
	  case ctrl('P') :			/* up arrow */
	  case KEY_UP :
	  case PF5 :
	    ch = KEY_UP;
	    if(ctmp = prev_confline(current))
	      current = ctmp;

	    break;

	  case '+' :				/* page forward */
	  case ' ' :
	  case ctrl('V') :
	  case PF8 :
	    ch = KEY_DOWN;
	    while(dline++ < ps->ttyo->screen_rows - BOTTOM_LINES)
	      if(ctmp = next_confline(current))
		current = ctmp;
	      else
		break;

	    break;

	  case '-' :				/* page backward */
	  case ctrl('Y') :
	  case PF7 :
	    ch = KEY_UP;
	    while(dline-- > FIRST_DLINE)
	      if(ctmp = prev_confline(current))
		current = ctmp;
	      else
		break;

	    while(++dline < ps->ttyo->screen_rows - BOTTOM_LINES)
	      if(ctmp = prev_confline(current))
		current = ctmp;
	      else
		break;

	    break;

	  case 'w' :				/* whereis */
	  case ctrl('W') :
	  case PF12 :
	    /*--- get string  ---*/
	    {int   rc, found = 0;
	     char *result = NULL, buf[64];
	     static char last[64];
	     HelpType help;

	     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) {
		 rc = optionally_enter(buf,-3,0,63,1,0,tmp,NULL,help,0);
		 if(rc == 3)
		   help = help == NO_HELP ? h_config_whereis : NO_HELP;
		 else if(rc == 0 || rc == 1 || !buf[0]){
		     if(rc == 0 && !buf[0] && last[0])
		       strcpy(buf, last);

		     break;
		 }
	     }

	     if(rc == 0 && buf[0]){
		 ch   = KEY_DOWN;
		 ctmp = current;
		 while(ctmp = next_confline(ctmp))
		   if(srchstr(ctmp->varname, buf)
		      || srchstr(ctmp->value, buf)){
		       found++;
		       break;
		   }

		 if(!found){
		     ctmp = first_confline(current);

		     while(ctmp != current)
		       if(srchstr(ctmp->varname, buf)
			  || srchstr(ctmp->value, buf)){
			   found++;
			   break;
		       }
		       else
			 ctmp = next_confline(ctmp);
		 }
	     }
	     else
	       result = "WhereIs cancelled";

	     if(found && ctmp){
		 strcpy(last, buf);
		 result  = "Word found";
		 current = ctmp;
	     }

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

	    break;

          case ctrl('Z'):			/* suspend pine! */
            if(!have_job_control()){
		bogus_command(orig_ch, F_ON(F_USE_FK, ps) ? "F1" : "?");
		break;
	    }

	    if(F_OFF(F_CAN_SUSPEND, ps)){
		q_status_message(1, 1, 3,
			    "\007Pine suspension not enabled - see help text");
		break;
	    }
	    else
	      do_suspend(ps);			/* fall thru to redraw */

	  case ctrl('L'):			/* redraw the display */
          case KEY_RESIZE:
	    ClearScreen();
	    ps->mangled_screen = 1;
	    break;

	  default:
	    if(ps_global->restricted){
		q_status_message(1, 1, 3,
				 "Pine demo can't change options or settings");
	    }
	    else if(current->tool){
		if((i = (*current->tool)(ps, ch, &current, dline)) == 1){
		    changes = 1;
		    break;
		}
		else if(i == 0)
		  break;
	    }

	    q_status_message1(0, 0, 2,
		  "\007Command \"%s\" not defined here.  See key menu below.",
		  pretty_command(orig_ch));

	  case NO_OP_IDLE:			/* simple timeout */
	  case NO_OP_COMMAND:
	    break;
	}
    }

    if(changes)
      write_pinerc(ps);				/* save any changes */

    for(current = first_confline(current); current;){	/* clean up */
	ctmp = current->next;
	free_confline(&current);
	current = ctmp;
    }

    ps->mangled_screen = 1;
    ClearScreen();
}


/*
 *
 */
HelpType
config_help(var, feature)
    int var, feature;
{
    switch(var){
      case V_FEATURE_LIST :
	switch(feature){
	  case F_ENABLE_FULL_HDR :
	    return(h_config_enable_full_hdr);
	  case F_ENABLE_PIPE :
	    return(h_config_enable_pipe);
	  case F_ENABLE_TAB_COMPLETE :
	    return(h_config_enable_tab_complete);
	  case F_QUIT_WO_CONFIRM :
	    return(h_config_quit_wo_confirm);
	  case F_ENABLE_JUMP :
	    return(h_config_enable_jump);
	  case F_ENABLE_ALT_ED :
	    return(h_config_enable_alt_ed);
	  case F_ENABLE_BOUNCE :
	    return(h_config_enable_bounce);
	  case F_ENABLE_AGG_OPS :
	    return(h_config_enable_agg_ops);
	  case F_ENABLE_FLAG :
	    return(h_config_enable_flag);
	  case F_CAN_SUSPEND :
	    return(h_config_can_suspend);
	  case F_EXPANDED_ADDRBOOKS :
	    return(h_config_expanded_addrbooks);
	  case F_EXPANDED_FOLDERS :
	    return(h_config_expanded_folders);
	  case F_USE_FK :
	    return(h_config_use_fk);
	  case F_INCLUDE_HEADER :
	    return(h_config_include_header);
	  case F_SIG_AT_BOTTOM :
	    return(h_config_sig_at_bottom);
	  case F_DEL_SKIPS_DEL :
	    return(h_config_del_skips_del);
	  case F_AUTO_EXPUNGE :
	    return(h_config_auto_expunge);
	  case F_AUTO_READ_MSGS :
	    return(h_config_auto_read_msgs);
	  case F_READ_IN_NEWSRC_ORDER :
	    return(h_config_read_in_newsrc_order);
	  case F_SELECT_WO_CONFIRM :
	    return(h_config_select_wo_confirm);
	  case F_COMPOSE_TO_NEWSGRP :
	    return(h_config_compose_news_wo_conf);
	  case F_USE_CURRENT_DIR :
	    return(h_config_use_current_dir);
	  case F_SAVE_WONT_DELETE :
	    return(h_config_save_wont_delete);
	  case F_SAVE_ADVANCES :
	    return(h_config_save_advances);
	  case F_FORCE_LOW_SPEED :
	    return(h_config_force_low_speed);
	  case F_ALT_ED_NOW :
	    return(h_config_alt_ed_now);
	  case F_SHOW_DELAY_CUE :
	    return(h_config_show_delay_cue);
	  case F_DISABLE_CONFIG_SCREEN :
	    return(h_config_disable_config_screen);
	  case F_DISABLE_PASSWORD_CMD :
	    return(h_config_disable_password_cmd);
	  case F_DISABLE_UPDATE_CMD :
	    return(h_config_disable_update_cmd);
	  case F_DISABLE_KBLOCK_CMD :
	    return(h_config_disable_kblock_cmd);
	  case F_QUOTE_ALL_FROMS :
	    return(h_config_quote_all_froms);
	  case F_AUTO_OPEN_NEXT_UNREAD :
	    return(h_config_auto_open_unread);
	  case F_AUTO_INCLUDE_IN_REPLY :
	    return(h_config_auto_include_reply);
	  case F_SELECTED_SHOWN_BOLD :
	    return(h_config_select_in_bold);
	  case F_NO_NEWS_VALIDATION :
	    return(h_config_post_wo_validation);
	  case F_ENABLE_INCOMING :
	    return(h_config_enable_incoming);
	  case F_ATTACHMENTS_IN_REPLY :
	    return(h_config_attach_in_reply);
	  case F_QUELL_LOCAL_LOOKUP :
	    return(h_config_quell_local_lookup);
	  case F_PRESERVE_START_STOP :
	    return(h_config_preserve_start_stop);
	  case F_COMPOSE_REJECTS_UNQUAL:
	    return(h_config_compose_rejects_unqual);
	  case F_FAKE_NEW_IN_NEWS:
	    return(h_config_news_uses_recent);
	  default :
	    return(NO_HELP);
        }

	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_FOLDER_SPEC :
	return(h_config_folder_spec);
      case V_NEWS_SPEC :
	return(h_config_news_spec);
      case V_DEFAULT_FCC :
	return(h_config_default_fcc);
      case V_POSTPONED_FOLDER :
	return(h_config_postponed_folder);
      case V_READ_MESSAGE_FOLDER :
	return(h_config_read_message_folder);
      case V_SIGNATURE_FILE :
	return(h_config_signature_file);
      case V_GLOB_ADDRBOOK :
	return(h_config_global_addrbook);
      case V_ADDRESSBOOK :
	return(h_config_addressbook);
      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_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_CHAR_SET :
	return(h_config_char_set);
      case V_EDITOR :
	return(h_config_editor);
      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);
#ifdef	DOS
      case V_NEWSRC_PATH :
	return(h_config_newsrc_path);
      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 envolved 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
 */
int
text_tool(ps, cmd, cl, line)
    struct pine  *ps;
    int		  cmd;
    CONF_S      **cl;
    int		  line;
{
    char	     prompt[80], sval[MAXPATH+1], *tmp, **newval = NULL;
    int		     rv = 0, i;
    struct variable *vtmp;
    CONF_S	    *ctmp;
    HelpType         help;

    switch(cmd){
      case 'a' :				/* add to list */
      case PF9 :
	if((*cl)->var->is_fixed){
	    q_status_message(0, 0, 3,
			     "\007Can't add to sys-admin defined value.");
	}
	else if(!(*cl)->var->is_list && (*cl)->var->user_val.p){
	    q_status_message(0, 0, 3,
			    "\007Only single value allowed.  Use \"Change\".");
	}
	else{
	    strcpy(prompt, "Enter the value to be added : ");
	    sval[0] = '\0';
	    ps->mangled_footer = 1;
	    help = NO_HELP;
	    while(1){
		i = optionally_enter(sval, -3, 0, MAXPATH, 1, 0, prompt,
				     NULL, help, 0);
		if(i == 0){
		    rv = ps->mangled_body = 1;
		    removing_leading_white_space(sval);
		    removing_trailing_white_space(sval);
		    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);

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

			    fs_give((void **)&ltmp);
			}
			else{
			    q_status_message(0, 0, 3,
					     "Can't add Empty value to list");
			}
		    }
		    else{
			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(0,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;
		}
		
		break;
	    }
	}

	break;

      case 'd' :				/* delete from list */
      case PF10 :
	if(((*cl)->var->is_list && !(*cl)->var->user_val.l)
		|| (!(*cl)->var->is_list && !(*cl)->var->user_val.p)){
	    q_status_message(0, 0, 3, "\007No 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 Value>"
			  : (*cl)->var->user_val.l[(*cl)->varmem]
		      : ((*cl)->var->user_val.p)
			  ? (!*(*cl)->var->user_val.p)
			      ? "<Empty Value>"
			      : (*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 Value>"
			      : (*cl)->var->user_val.p
		 	  : "<NULL VALUE>",
		    (*cl)->var->name);

	    ps->mangled_footer = 1;
	    if(want_to(prompt, 'n', 'n', NO_HELP, 0, 1) == '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(0, 1, 3, "\007Value not deleted");
	}

	break;

      case ctrl('M') :
      case ctrl('J') :
      case 'c' :				/* edit/change list option */
      case PF4 :
	if((*cl)->var->is_fixed){
	    q_status_message(0, 0, 3,
			     "\007Can't change sys-admin defined value.");
	}
	else if(((*cl)->var->is_list && !(*cl)->var->user_val.l)
		|| (!(*cl)->var->is_list && !(*cl)->var->user_val.p)){
	    q_status_message(0, 0, 3,
			     "\007No current value set.  Use \"Add\".");
	}
	else{
	    HelpType help;

	    ps->mangled_footer = 1;
	    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 field %.35s value : ",
			(*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){
		i = optionally_enter(sval, -3, 0, MAXPATH, 1, 0, prompt,
				      NULL, help, 0);
		if(i == 0){
		    removing_leading_white_space(sval);
		    removing_trailing_white_space(sval);
		    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(!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...
			     */
			    config_del_list_item(cl, &newval);
			    config_add_list(ps, cl, ltmp, &newval);
			}

			if(ltmp)
			  fs_give((void **)&ltmp);
		    }
		    else{
			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(0,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;
	    }
	}

	break;

      default :
	rv = -1;
	break;
    }

    /*
     * 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){
	/*
	 * Addressbook is the only list with a default, so that's why
	 * the special code here.
	 */
	if((*cl)->var == &ps->vars[V_ADDRESSBOOK]){
	    if(!(*cl)->var->user_val.l && !(*cl)->var->command_line_val.l
		    && !(*cl)->var->fixed_val.l && !(*cl)->var->global_val.l)
	      (*cl)->var->global_val.l = parse_list(DF_ADDRESSBOOK, 1, NULL);
	}

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

	/*
	 * Handle any special case variable or config setting here
	 * (resetting current_val where needed).
	 *
	 * "Special cases" are variables that are not used by pine
	 * on-the-fly, but are used, in turn, to set other config
	 * data or internal structures *or* are variables that when
	 * NULL are given internally defined defaults (in which case
	 * we've got to fix up var->current.val)...
	 */
	if((*cl)->var == &ps->vars[V_PERSONAL_NAME]){
	    if(!(*cl)->var->user_val.p && ps->ui.fullname){
		if((*cl)->var->current_val.p)
		  fs_give((void **)&(*cl)->var->current_val.p);

		(*cl)->var->current_val.p = cpystr(ps->ui.fullname);
	    }
	}
	else if((*cl)->var == &ps->vars[V_USER_DOMAIN]){
	    /*
	     * Reset various pointers pertaining to domain name and such...
	     */
	    init_hostname(ps);
	}
	else if((*cl)->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, s->mailbox);
	    }
	}
	else if((*cl)->var == &ps->vars[V_FOLDER_SPEC]
	   || (*cl)->var == &ps->vars[V_NEWS_SPEC]){
	    q_status_message(0, 0, 3,
	   "\007Folder List changes will take affect your next pine session.");
	}
	else if((*cl)->var == &ps->vars[V_ADDRESSBOOK] ||
	        (*cl)->var == &ps->vars[V_GLOB_ADDRBOOK]){
	    addrbook_reset();
	}
	else if((*cl)->var == &ps->vars[V_DEFAULT_FCC]){
	    init_save_defaults();
	}
	else if((*cl)->var == &ps->vars[V_INIT_CMD_LIST]){
	    q_status_message(0, 0, 3,
	    "\007Initial command changes will affect your next pine session.");
	}
#ifdef	DOS
	else if((*cl)->var == &ps->vars[V_FOLDER_EXTENSION]){
	    mail_parameters(NULL, SET_EXTENSION,
			    (void *)(*cl)->var->current_val.p);
	}
	else if((*cl)->var == &ps->vars[V_NEWSRC_PATH]){
	    if((*cl)->var->current_val.p && (*cl)->var->current_val.p[0])
	      mail_parameters(NULL, SET_NEWSRC,
			      (void *)(*cl)->var->current_val.p);
	}
#endif

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


/*
 *
 */
void
config_add_list(ps, cl, ltmp, newval)
    struct pine *ps;
    CONF_S     **cl;
    char       **ltmp, ***newval;
{
    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 *));
	    for(; i >= (*cl)->varmem; i--)
	      (*cl)->var->user_val.l[i+items] =
		(*cl)->var->user_val.l[i];

	    i = 0;
	}
	else{
	    (*cl)->varmem = 0;
	    if((*cl)->var->user_val.l[0])
	      fs_give((void **)&(*cl)->var->user_val.l[0]);

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

    for(; i < items ; i++){
	(*cl)->var->user_val.l[i+(*cl)->varmem] = ltmp[i];
	tmp = (*cl)->value;
	new_confline(cl);
	(*cl)->value     = tmp;
	(*cl)->var       = (*cl)->prev->var;
	(*cl)->valoffset = (*cl)->prev->valoffset;
	(*cl)->keymenu   = (*cl)->prev->keymenu;
	(*cl)->help      = (*cl)->prev->help;
	(*cl)->tool      = (*cl)->prev->tool;
	(*cl)->varnamep  = (*cl)->prev->varnamep;
	*cl		 = (*cl)->prev;
	(*cl)->value     = NULL;
	*newval		 = &(*cl)->value;
    }

    /* now fix up varmem values */
    for(ctmp = (*cl)->varnamep, i = 0;
	(*cl)->var->user_val.l[i];
	ctmp = ctmp->next, i++){
	ctmp->varmem = i;
	if(!ctmp->value)
	  ctmp->value = pretty_value(ps, ctmp);
    }
}


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

	free_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;
	free_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, line)
    struct pine  *ps;
    int		  cmd;
    CONF_S	**cl;
    int		  line;
{
    int  rv = 0;

    switch(cmd){
      case ctrl('M') :
      case ctrl('J') :
      case 'x' :				/* mark/unmark feature */
      case PF4 :
	if((*cl)->var == &ps->vars[V_FEATURE_LIST]){
	    rv = 1;
	    toggle_feature_bit(ps, (*cl)->varmem, (*cl));
	}
	else
	  q_status_message(0, 0, 3,
			   "\007Programmer botch!  Unknown checkbox type.");

	break;

      default :
	rv = -1;
	break;
    }

    return(rv);
}


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

    if((*cl)->var->is_fixed){
	q_status_message(0, 0, 3,
			 "\007Can't change sys-admin defined value.");
	if((*cl)->var->user_val.p){
	    flush_status_messages();
	    sleep(1);
	    if(want_to("Delete old unused personal option setting", 'y', 'n',
		        NO_HELP, 0, 1) == 'y'){
		fs_give((void **)&(*cl)->var->user_val.p);
		q_status_message(0, 0, 3, "Deleted");
		rv = 1;
	    }
	}
	return(rv);
    }

    switch(cmd){
      case ctrl('M') :
      case ctrl('J') :
      case 's' :				/* set/unset feature */
      case PF4 :
	/* hunt backwards, turning off old values */
	for(ctmp = *cl; ctmp && !ctmp->unselectable && !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_AB_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{
		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;
	}
#ifdef	DOS
	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(0, 0, 3,
			   "\007Programmer botch!  Unknown radiobutton type.");

	break;

      default :
	rv = -1;
	break;
    }

    return(rv);
}



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

    if((*cl)->var->is_fixed){
	q_status_message(0, 0, 3,
			 "\007Can't change sys-admin defined value.");
	if((*cl)->var->user_val.p){
	    flush_status_messages();
	    sleep(1);
	    if(want_to("Delete old unused personal option setting", 'y', 'n',
		        NO_HELP, 0, 1) == 'y'){
		fs_give((void **)&(*cl)->var->user_val.p);
		q_status_message(0, 0, 3, "Deleted");
		rv = 1;
	    }
	}
	return(rv);
    }

    switch(cmd){
      case ctrl('M') :
      case ctrl('J') :
      case 'c' :				/* toggle yes to no and back */
      case PF4 :
	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;

      default :
	rv = -1;
	break;
    }

    return(rv);
}



/*
 * Manage display of the config/options menu body.
 */
int
update_option_screen(ps, current, screen)
    struct pine  *ps;
    CONF_S	 *current;
    OPT_SCREEN_S *screen;
{
    int		   dline, return_line = FIRST_DLINE;
    CONF_S	  *top_line, *ctmp;

    /* calculate top line of display */
    dline = 0;
    ctmp = top_line = first_confline(current);
    do
      if(((dline++)%(ps->ttyo->screen_rows-HEADER_LINES-BOTTOM_LINES)) == 0)
	top_line = ctmp;
    while(ctmp != current && (ctmp = next_confline(ctmp)));

    /* mangled body or new page, force redraw */
    if(ps->mangled_body || screen->top_line != top_line)
      screen->current = NULL;

    /* loop thru painting what's needed */
    for(dline = 0, ctmp = top_line;
	dline < ps->ttyo->screen_rows - BOTTOM_LINES - HEADER_LINES;
	dline++, ctmp = next_confline(ctmp)){

	/*
	 * only fall thru painting if something needs painting...
	 */
	if(!(!screen->current || ctmp == screen->current || ctmp == current
	     || ctmp == screen->current->varnamep
	     || ctmp == current->varnamep))
	  continue;

	MoveCursor(dline + FIRST_DLINE, 0);
	CleartoEOLN();

	if(ctmp && ctmp->varname){
	    if(ctmp == current || ctmp == current->varnamep){
		StartInverse();
		return_line = dline + FIRST_DLINE;
	    }

	    Write_to_screen(ctmp->varname);
	    if(ctmp == current || ctmp == current->varnamep)
	      EndInverse();
	}

	if(ctmp && ctmp->value){
	    char *p = tmp_20k_buf;
	    int   i, j;
	    if(ctmp == current){
		return_line = dline + FIRST_DLINE;
		StartInverse();
	    }

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

	    *p = '\0';
	    PutLine0(dline+FIRST_DLINE, ctmp->valoffset, tmp_20k_buf);

	    if(ctmp == current)
	      EndInverse();
	}
    }

    ps->mangled_body = 0;
    screen->top_line = top_line;
    screen->current  = current;
    return(return_line);
}



/*
 *
 */
void
option_screen_redrawer()
{
    bitmap_t	 bitmap;

    ClearScreen();

    set_titlebar(CONFIG_SCREEN_TITLE, ps_global->mail_stream,
		 ps_global->context_current, ps_global->cur_folder,
		 ps_global->msgmap, 1, FolderName,0,0);

    ps_global->mangled_body = 1;
    (void)update_option_screen(ps_global, opt_screen->current, opt_screen);

    setbitmap(bitmap);
    draw_keymenu(opt_screen->current->keymenu, bitmap,
		 ps_global->ttyo->screen_cols, -2, 0, FirstMenu, 0);
}



/*
 * 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]
		       : "<Empty Value>");
	}
	else{
	    char *p = tmp;
	    sstrcpy(&p, cl->var->is_fixed?"<Value is Fixed":"<No Value Set");
	    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]))){
			    if(l > 6){
				p += l - 3;
				sstrcpy(&p, "...");
			    }
			    else
			      p += l;

			    break;
			}
			else
			  p += l2;
		    }
		    else
		      break;
		}
	    }

	    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
			? "<Value is Fixed%s%s%s>%*s" :
			"<No Value Set%s%s%s>%*s", 
		(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
					: "<Empty Value>");

    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, newval[256];
    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(!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;
}


/*
 *
 */
void
toggle_feature_bit(ps, index, current)
    struct pine *ps;
    int		 index;
    CONF_S      *current;
{
    NAMEVAL_S  *f;
    char      **vp, *p;
    int		i, og;

    f  = feature_list(index);
    og = test_old_growth_bits(ps, f->value);

    /*
     * 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 = current->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(0, 0, 3,
			     "\007Can't change value fixed by sys-admin.");
	    return;
	}
    }

    F_SET(f->value, ps, !F_ON(f->value, ps));	/* flip the bit */
    current->value[1] = F_ON(f->value, 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(current->var->global_val.l,f->name,og,F_ON(f->value,ps))
       || test_feature(current->var->user_val.l,f->name,og,!F_ON(f->value,ps)))
      clear_feature(&current->var->user_val.l, f->name);
    else
      set_feature(&current->var->user_val.l, f->name, F_ON(f->value, ps));

    /*
     * Handle any features that need special attention here...
     */
    if(f->value == F_QUOTE_ALL_FROMS)
      mail_parameters(NULL,SET_FROMWIDGET,(void *)(F_ON(f->value,ps) ? 1 : 0));
    else if(f->value == F_PRESERVE_START_STOP){
	/* toggle raw mode settings to make tty driver aware of new setting */
	Raw(0);
	Raw(1);
    }
}


/*
 * 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
free_confline(p)
    CONF_S **p;
{
    if(p){
	if((*p)->varname)
	  fs_give((void **)&(*p)->varname);

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

	if((*p)->prev)
	  (*p)->prev->next = (*p)->next;

	if((*p)->next)
	  (*p)->next->prev = (*p)->prev;

	fs_give((void **)p);
    }
}


/*
 *
 */
CONF_S *
first_confline(p)
    CONF_S *p;
{
    while(p && p->prev)
      p = p->prev;

    return(p);
}


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

    ps->fix_fixed_warning = 0;  /* so we only ask first time */

    if(want_to("Some of your options conflict with site policy.  Investigate",
	'y', 'n', NO_HELP, 0, 1) != '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 <Empty Value>", v->name);
	}
	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 <Empty Value>",
			v->name);
		}
	    }
	}
	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, 0, 0) == '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, 0, 0) == '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);
}
