From 094ca96844842928810f14844413109fc6cdd890 Mon Sep 17 00:00:00 2001 From: Eduardo Chappa Date: Sun, 3 Feb 2013 00:59:38 -0700 Subject: Initial Alpine Version --- alpine/confscroll.c | 5968 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 5968 insertions(+) create mode 100644 alpine/confscroll.c (limited to 'alpine/confscroll.c') diff --git a/alpine/confscroll.c b/alpine/confscroll.c new file mode 100644 index 00000000..0ada8a79 --- /dev/null +++ b/alpine/confscroll.c @@ -0,0 +1,5968 @@ +#if !defined(lint) && !defined(DOS) +static char rcsid[] = "$Id: confscroll.c 1169 2008-08-27 06:42:06Z hubert@u.washington.edu $"; +#endif + +/* + * ======================================================================== + * Copyright 2006-2008 University of Washington + * Copyright 2013 Eduardo Chappa + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * ======================================================================== + */ + +#include "headers.h" +#include "confscroll.h" +#include "keymenu.h" +#include "status.h" +#include "titlebar.h" +#include "help.h" +#include "radio.h" +#include "print.h" +#include "ldapconf.h" +#include "roleconf.h" +#include "colorconf.h" +#include "mailview.h" +#include "mailcmd.h" +#include "mailindx.h" +#include "talk.h" +#include "setup.h" +#include "smime.h" +#include "../pith/state.h" +#include "../pith/flag.h" +#include "../pith/list.h" +#include "../pith/conf.h" +#include "../pith/util.h" +#include "../pith/newmail.h" +#include "../pith/sort.h" +#include "../pith/thread.h" +#include "../pith/color.h" +#include "../pith/hist.h" +#include "../pith/icache.h" +#include "../pith/conf.h" +#include "../pith/init.h" +#include "../pith/folder.h" +#include "../pith/busy.h" +#include "../pith/tempfile.h" +#include "../pith/pattern.h" +#include "../pith/charconv/utf8.h" + + +#define CONFIG_SCREEN_HELP_TITLE _("HELP FOR SETUP CONFIGURATION") + +/* TRANSLATORS: Empty Value is what is shown in the configuration + screen when the user not only does not set an option but also + wants to explicitly not use the default value. Empty value means + an option with no value. */ +char *empty_val = N_("Empty Value"); +char *empty_val2 = N_(""); +/* TRANSLATORS: No Value set is similar to Empty Value, but the + user has not explicitly decided to not use the default. It is + just an option which the user has left at the default value. */ +char *no_val = N_("No Value Set"); +/* TRANSLATORS: Value is Fixed is what is displayed in the config + screen when the system managers have set an option to a specific + value and they don't allow the user to change it. The value + is fixed to a certain value. This isn't the same word as + Repaired, it means Unchanging. */ +char *fixed_val = N_("Value is Fixed"); +char yesstr[] = "Yes"; +char nostr[] = "No"; + + +EditWhich ew = Main; + + +OPT_SCREEN_S *opt_screen; + + +/* + * This is pretty ugly. Some of the routines operate differently depending + * on which variable they are operating on. Sometimes those variables are + * global (real alpine.h V_ style variables) and sometimes they are just + * local variables (as in role_config_edit_screen). These pointers are here + * so that the routines can figure out which variable they are operating + * on and do the right thing. + */ +struct variable *score_act_global_ptr, + *scorei_pat_global_ptr, + *age_pat_global_ptr, + *size_pat_global_ptr, + *cati_global_ptr, + *cat_cmd_global_ptr, + *cat_lim_global_ptr, + *startup_ptr, + *role_comment_ptr, + *role_forw_ptr, + *role_repl_ptr, + *role_fldr_ptr, + *role_afrom_ptr, + *role_filt_ptr, + *role_status1_ptr, + *role_status2_ptr, + *role_status3_ptr, + *role_status4_ptr, + *role_status5_ptr, + *role_status6_ptr, + *role_status7_ptr, + *role_status8_ptr, + *msg_state1_ptr, + *msg_state2_ptr, + *msg_state3_ptr, + *msg_state4_ptr; + + +typedef NAMEVAL_S *(*PTR_TO_RULEFUNC)(int); + + +/* + * Internal prototypes + */ +PTR_TO_RULEFUNC rulefunc_from_var(struct pine *, struct variable *); +void set_radio_pretty_vals(struct pine *, CONF_S **); +int save_include(struct pine *, struct variable *, int); +void config_scroll_up(long); +void config_scroll_down(long); +void config_scroll_to_pos(long); +CONF_S *config_top_scroll(struct pine *, CONF_S *); +void update_option_screen(struct pine *, OPT_SCREEN_S *, Pos *); +void print_option_screen(OPT_SCREEN_S *, char *); +void option_screen_redrawer(void); +char *text_pretty_value(struct pine *, CONF_S *); +char *checkbox_pretty_value(struct pine *, CONF_S *); +char *yesno_pretty_value(struct pine *, CONF_S *); +char *radio_pretty_value(struct pine *, CONF_S *); +char *sigfile_pretty_value(struct pine *, CONF_S *); +char *color_pretty_value(struct pine *, CONF_S *); +char *sort_pretty_value(struct pine *, CONF_S *); +int longest_feature_name(void); +COLOR_PAIR *sample_color(struct pine *, struct variable *); +COLOR_PAIR *sampleexc_color(struct pine *, struct variable *); +void clear_feature(char ***, char *); +CONF_S *last_confline(CONF_S *); +#ifdef _WINDOWS +int config_scroll_callback(int, long); +#endif + + +/* + * We test for this same set of vars in a few places. + */ +int +standard_radio_var(struct pine *ps, struct variable *v) +{ + return(v == &ps->vars[V_SAVED_MSG_NAME_RULE] || + v == &ps->vars[V_FCC_RULE] || + v == &ps->vars[V_GOTO_DEFAULT_RULE] || + v == &ps->vars[V_INCOMING_STARTUP] || + v == &ps->vars[V_PRUNING_RULE] || + v == &ps->vars[V_REOPEN_RULE] || + v == &ps->vars[V_THREAD_DISP_STYLE] || + v == &ps->vars[V_THREAD_INDEX_STYLE] || + v == &ps->vars[V_FLD_SORT_RULE] || +#ifndef _WINDOWS + v == &ps->vars[V_COLOR_STYLE] || +#endif + v == &ps->vars[V_INDEX_COLOR_STYLE] || + v == &ps->vars[V_TITLEBAR_COLOR_STYLE] || + v == &ps->vars[V_AB_SORT_RULE]); +} + + +PTR_TO_RULEFUNC +rulefunc_from_var(struct pine *ps, struct variable *v) +{ + PTR_TO_RULEFUNC rulefunc = NULL; + + if(v == &ps->vars[V_SAVED_MSG_NAME_RULE]) + rulefunc = save_msg_rules; + else if(v == &ps->vars[V_FCC_RULE]) + rulefunc = fcc_rules; + else if(v == &ps->vars[V_GOTO_DEFAULT_RULE]) + rulefunc = goto_rules; + else if(v == &ps->vars[V_INCOMING_STARTUP]) + rulefunc = incoming_startup_rules; + else if(v == startup_ptr) + rulefunc = startup_rules; + else if(v == &ps->vars[V_PRUNING_RULE]) + rulefunc = pruning_rules; + else if(v == &ps->vars[V_REOPEN_RULE]) + rulefunc = reopen_rules; + else if(v == &ps->vars[V_THREAD_DISP_STYLE]) + rulefunc = thread_disp_styles; + else if(v == &ps->vars[V_THREAD_INDEX_STYLE]) + rulefunc = thread_index_styles; + else if(v == &ps->vars[V_FLD_SORT_RULE]) + rulefunc = fld_sort_rules; + else if(v == &ps->vars[V_AB_SORT_RULE]) + rulefunc = ab_sort_rules; + else if(v == &ps->vars[V_INDEX_COLOR_STYLE]) + rulefunc = index_col_style; + else if(v == &ps->vars[V_TITLEBAR_COLOR_STYLE]) + rulefunc = titlebar_col_style; +#ifndef _WINDOWS + else if(v == &ps->vars[V_COLOR_STYLE]) + rulefunc = col_style; +#endif + + return(rulefunc); +} + + +void +standard_radio_setup(struct pine *ps, CONF_S **cl, struct variable *v, CONF_S **first_line) +{ + int i, rindent = 12; + CONF_S *ctmpb; + PTR_TO_RULEFUNC rulefunc; + NAMEVAL_S *f; + char b[100]; + + if(!(cl && *cl)) + return; + + rulefunc = rulefunc_from_var(ps, v); + ctmpb = (*cl); + + (*cl)->flags |= CF_NOSELECT; + (*cl)->keymenu = &config_radiobutton_keymenu; + (*cl)->tool = NULL; + + /* put a nice delimiter before list */ + new_confline(cl)->var = NULL; + (*cl)->varnamep = ctmpb; + (*cl)->keymenu = &config_radiobutton_keymenu; + (*cl)->help = NO_HELP; + (*cl)->tool = radiobutton_tool; + (*cl)->valoffset = rindent; + (*cl)->flags |= CF_NOSELECT; + /* TRANSLATORS: Set and Rule Values are the headings for an option + that can take one of several values. Underneath the Set heading + will be a column where one possibility is turned on (is Set). + The other column will be very short descriptions of what + the possibilities are (the Rule Values). */ + utf8_snprintf(b, sizeof(b), "%-5.5w %s", _("Set"), _("Rule Values")); + (*cl)->value = cpystr(b); + + new_confline(cl)->var = NULL; + (*cl)->varnamep = ctmpb; + (*cl)->keymenu = &config_radiobutton_keymenu; + (*cl)->help = NO_HELP; + (*cl)->tool = radiobutton_tool; + (*cl)->valoffset = rindent; + (*cl)->flags |= CF_NOSELECT; + (*cl)->value = cpystr("--- ----------------------"); + + if(rulefunc) + for(i = 0; (f = (*rulefunc)(i)); i++){ + new_confline(cl)->var = v; + if(first_line && !*first_line && !pico_usingcolor()) + *first_line = (*cl); + + (*cl)->varnamep = ctmpb; + (*cl)->keymenu = &config_radiobutton_keymenu; + (*cl)->help = (v == startup_ptr) + ? h_config_other_startup + : config_help(v - ps->vars,0); + (*cl)->tool = radiobutton_tool; + (*cl)->valoffset = rindent; + (*cl)->varmem = i; + (*cl)->value = pretty_value(ps, *cl); + } +} + + +/* + * Reset the displayed values for all of the lines for this + * variable because others besides this line may change. + */ +void +set_radio_pretty_vals(struct pine *ps, CONF_S **cl) +{ + CONF_S *ctmp; + + if(!(cl && *cl && + ((*cl)->var == &ps->vars[V_SORT_KEY] || + standard_radio_var(ps, (*cl)->var) || + (*cl)->var == startup_ptr))) + return; + + /* hunt backwards */ + for(ctmp = *cl; + ctmp && !(ctmp->flags & CF_NOSELECT) && !ctmp->varname; + ctmp = prev_confline(ctmp)){ + if(ctmp->value) + fs_give((void **)&ctmp->value); + + ctmp->value = pretty_value(ps, ctmp); + } + + /* hunt forwards */ + for(ctmp = *cl; + ctmp && !ctmp->varname && !(ctmp->flags & CF_NOSELECT); + ctmp = next_confline(ctmp)){ + if(ctmp->value) + fs_give((void **)&ctmp->value); + + ctmp->value = pretty_value(ps, ctmp); + } +} + + +/* + * test whether or not a var is + * + * returns: 1 if it should be excluded, 0 otw + */ +int +exclude_config_var(struct pine *ps, struct variable *var, int allow_hard_to_config_remotely) +{ + if((ew != Main && (var->is_onlymain)) || + (ew != ps_global->ew_for_except_vars && var->is_outermost)) + return(1); + + if(allow_hard_to_config_remotely) + return(!(var->is_user && var->is_used && !var->is_obsolete)); + + switch(var - ps->vars){ + case V_MAIL_DIRECTORY : + case V_INCOMING_FOLDERS : + case V_FOLDER_SPEC : + case V_NEWS_SPEC : + 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_DISABLE_AUTHS : + case V_REMOTE_ABOOK_METADATA : + case V_REMOTE_ABOOK_HISTORY : + case V_REMOTE_ABOOK_VALIDITY : + case V_OPER_DIR : + case V_USERINPUTTIMEO : + case V_TCPOPENTIMEO : + case V_TCPREADWARNTIMEO : + case V_TCPWRITEWARNTIMEO : + case V_TCPQUERYTIMEO : + case V_RSHCMD : + case V_RSHPATH : + case V_RSHOPENTIMEO : + case V_SSHCMD : + case V_SSHPATH : + case V_SSHOPENTIMEO : + case V_SENDMAIL_PATH : + case V_NEW_VER_QUELL : + case V_PATTERNS : + case V_PAT_ROLES : + case V_PAT_FILTS : + case V_PAT_SCORES : + case V_PAT_INCOLS : + case V_PAT_OTHER : + case V_PAT_SRCH : + case V_PRINTER : + case V_PERSONAL_PRINT_COMMAND : + case V_PERSONAL_PRINT_CATEGORY : + case V_RSS_NEWS : + case V_RSS_WEATHER : + case V_WP_INDEXHEIGHT : + case V_WP_INDEXLINES : + case V_WP_AGGSTATE : + case V_WP_STATE : + case V_WP_COLUMNS : +#ifndef _WINDOWS + case V_OLD_CHAR_SET : +#endif /* ! _WINDOWS */ +#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_FONT_CHAR_SET : + case V_PRINT_FONT_NAME : + case V_PRINT_FONT_SIZE : + case V_PRINT_FONT_STYLE : + case V_PRINT_FONT_CHAR_SET : + case V_WINDOW_POSITION : + case V_CURSOR_STYLE : +#endif /* _WINDOWS */ +#endif /* DOS */ +#ifdef ENABLE_LDAP + case V_LDAP_SERVERS : +#endif /* ENABLE_LDAP */ + return(1); + + default: + break; + } + + return(!(var->is_user && var->is_used && !var->is_obsolete && +#ifdef SMIME + !smime_related_var(ps, var) && +#endif /* SMIME */ + !color_related_var(ps, var))); +} + + +/* + * Test to indicate what should be saved in case user wants to abandon + * changes. + */ +int +save_include(struct pine *ps, struct variable *v, int allow_hard_to_config_remotely) +{ + return(!exclude_config_var(ps, v, allow_hard_to_config_remotely) + || (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 + ))); +} + + +/* + * 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, 2, or 3. 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(struct pine *ps, OPT_SCREEN_S *screen, CONF_S *start_line, char *title, char *pdesc, int multicol) +{ + char tmp[MAXPATH+1]; + char *utf8str; + UCS ch = 'x'; + int cmd, i, j, done = 0, changes = 0; + int retval = 0; + int km_popped = 0, stay_in_col = 0; + struct key_menu *km = NULL; + CONF_S *ctmpa = NULL, *ctmpb = NULL; + Pos cursor_pos; + OtherMenu what_keymenu = FirstMenu; + void (*prev_redrawer)(void); + + dprint((7, "conf_scroll_screen()\n")); + + if(BODY_LINES(ps) < 1){ + q_status_message(SM_ORDER | SM_DING, 3, 3, _("Screen too small")); + return(0); + } + + if(screen && screen->ro_warning) + q_status_message1(SM_ORDER, 1, 3, + /* TRANSLATORS: "Config file not changeable," is what replaces the %s */ + _("%s can't change options or settings"), + ps_global->restricted ? "Alpine demo" + : _("Config file not changeable,")); + + 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){ + ps->user_says_cancel = 0; + 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, NULL); + ps->mangled_header = 0; + } + + update_option_screen(ps, screen, &cursor_pos); + + if(F_OFF(F_SHOW_CURSOR, ps)){ + cursor_pos.row = ps->ttyo->screen_rows - FOOTER_ROWS(ps); + cursor_pos.col = 0; + } + + /*---- This displays new mail notification, or errors ---*/ + if(km_popped){ + FOOTER_ROWS(ps) = 3; + mark_status_unknown(); + } + + display_message(ch); + if(km_popped){ + FOOTER_ROWS(ps) = 1; + mark_status_unknown(); + } + + if(ps->mangled_footer || km != screen->current->keymenu){ + bitmap_t bitmap; + + setbitmap(bitmap); + + ps->mangled_footer = 0; + km = screen->current->keymenu; + + if(multicol && + (F_OFF(F_ARROW_NAV, ps_global) || + F_ON(F_RELAXED_ARROW_NAV, ps_global))){ + menu_clear_binding(km, KEY_LEFT); + menu_clear_binding(km, KEY_RIGHT); + menu_clear_binding(km, KEY_UP); + menu_clear_binding(km, KEY_DOWN); + menu_add_binding(km, KEY_UP, MC_CHARUP); + menu_add_binding(km, KEY_DOWN, MC_CHARDOWN); + menu_add_binding(km, KEY_LEFT, MC_PREVITEM); + menu_add_binding(km, ctrl('B'), MC_PREVITEM); + menu_add_binding(km, KEY_RIGHT, MC_NEXTITEM); + menu_add_binding(km, ctrl('F'), MC_NEXTITEM); + } + else{ + menu_clear_binding(km, KEY_LEFT); + menu_clear_binding(km, KEY_RIGHT); + menu_clear_binding(km, KEY_UP); + menu_clear_binding(km, KEY_DOWN); + + /* + * Fix up arrow nav mode if necessary... + */ + if(F_ON(F_ARROW_NAV, ps_global)){ + int cmd; + + if((cmd = menu_clear_binding(km, '<')) != MC_UNKNOWN){ + menu_add_binding(km, '<', cmd); + menu_add_binding(km, KEY_LEFT, cmd); + } + + if((cmd = menu_clear_binding(km, '>')) != MC_UNKNOWN){ + menu_add_binding(km, '>', cmd); + menu_add_binding(km, KEY_RIGHT, cmd); + } + + if((cmd = menu_clear_binding(km, 'p')) != MC_UNKNOWN){ + menu_add_binding(km, 'p', cmd); + menu_add_binding(km, KEY_UP, cmd); + } + + if((cmd = menu_clear_binding(km, 'n')) != MC_UNKNOWN){ + menu_add_binding(km, 'n', cmd); + menu_add_binding(km, KEY_DOWN, cmd); + } + } + } + + if(km_popped){ + FOOTER_ROWS(ps) = 3; + clearfooter(ps); + } + + draw_keymenu(km, bitmap, ps->ttyo->screen_cols, + 1-FOOTER_ROWS(ps), 0, what_keymenu); + what_keymenu = SameMenu; + + if(km_popped){ + FOOTER_ROWS(ps) = 1; + mark_keymenu_dirty(); + } + } + + MoveCursor(cursor_pos.row, cursor_pos.col); +#ifdef MOUSE + mouse_in_content(KEY_MOUSE, -1, -1, 0, 0); /* prime the handler */ + register_mfunc(mouse_in_content, HEADER_ROWS(ps_global), 0, + ps_global->ttyo->screen_rows -(FOOTER_ROWS(ps_global)+1), + ps_global->ttyo->screen_cols); +#endif +#ifdef _WINDOWS + mswin_setscrollcallback(config_scroll_callback); +#endif + /*------ Read the command from the keyboard ----*/ + ch = READ_COMMAND(&utf8str); +#ifdef MOUSE + clear_mfunc(mouse_in_content); +#endif +#ifdef _WINDOWS + mswin_setscrollcallback(NULL); +#endif + + cmd = menu_command(ch, km); + + if(km_popped) + switch(cmd){ + case MC_NONE: + case MC_OTHER: + case MC_RESIZE: + case MC_REPAINT: + km_popped++; + break; + + default: + clearfooter(ps); + break; + } + + switch(cmd){ + case MC_OTHER : + what_keymenu = NextMenu; + ps->mangled_footer = 1; + break; + + case MC_HELP: /* help! */ + if(FOOTER_ROWS(ps) == 1 && km_popped == 0){ + km_popped = 2; + ps->mangled_footer = 1; + break; + } + + if(screen->current->help != NO_HELP){ + prev_redrawer = ps_global->redrawer; + helper(screen->current->help, + (screen->current->help_title) + ? screen->current->help_title + : CONFIG_SCREEN_HELP_TITLE, + HLPD_SIMPLE); + ps_global->redrawer = prev_redrawer; + ps->mangled_screen = 1; + } + else + q_status_message(SM_ORDER,0,3,_("No help yet.")); + + break; + + + case MC_NEXTITEM: /* next list element */ + case MC_CHARDOWN: + stay_in_col = 0; + if(screen->current->flags & CF_DOUBLEVAR){ + /* if going from col1 to col2, it's simple */ + if(!(screen->current->flags & CF_VAR2) && cmd == MC_NEXTITEM){ + screen->current->flags |= CF_VAR2; + break; + } + + /* otherwise we fall through to normal next */ + stay_in_col = (screen->current->flags & CF_VAR2 && + cmd == MC_CHARDOWN); + screen->current->flags &= ~CF_VAR2; + } + + for(ctmpa = next_confline(screen->current), i = 1; + ctmpa && (ctmpa->flags & CF_NOSELECT); + ctmpa = next_confline(ctmpa), i++) + ; + + if(ctmpa){ + screen->current = ctmpa; + if(screen->current->flags & CF_DOUBLEVAR && stay_in_col) + screen->current->flags |= CF_VAR2; + + if(cmd == MC_CHARDOWN){ + for(ctmpa = screen->top_line, + j = BODY_LINES(ps) - 1 - HS_MARGIN(ps); + j > 0 && ctmpa && ctmpa != screen->current; + ctmpa = next_confline(ctmpa), j--) + ; + + if(!j && ctmpa){ + for(i = 0; + ctmpa && ctmpa != screen->current; + ctmpa = next_confline(ctmpa), i++) + ; + + if(i) + config_scroll_up(i); + } + } + } + else{ + /* + * Scroll screen a bit so we show the non-selectable + * lines at the bottom. + */ + + /* set ctmpa to the bottom line on the screen */ + for(ctmpa = screen->top_line, j = BODY_LINES(ps) - 1; + j > 0 && ctmpa; + ctmpa = next_confline(ctmpa), j--) + ; + + i = 0; + if(ctmpa){ + for(ctmpa = next_confline(ctmpa); + ctmpa && + (ctmpa->flags & (CF_NOSELECT | CF_B_LINE)) == + CF_NOSELECT; + ctmpa = next_confline(ctmpa), i++) + ; + } + + if(i) + config_scroll_up(i); + else + q_status_message(SM_ORDER,0,1, _("Already at end of screen")); + } + + break; + + case MC_PREVITEM: /* prev list element */ + case MC_CHARUP: + stay_in_col = 0; + if(screen->current->flags & CF_DOUBLEVAR){ + if(screen->current->flags & CF_VAR2 && cmd == MC_PREVITEM){ + screen->current->flags &= ~CF_VAR2; + break; + } + + /* otherwise we fall through to normal prev */ + stay_in_col = (!(screen->current->flags & CF_VAR2) && + cmd == MC_CHARUP); + screen->current->flags &= ~CF_VAR2; + } + else if(cmd == MC_CHARUP) + stay_in_col = 1; + + ctmpa = screen->current; + i = 0; + do + if(ctmpa == config_top_scroll(ps, screen->top_line)) + i = 1; + else if(i) + i++; + while((ctmpa = prev_confline(ctmpa)) + && (ctmpa->flags&CF_NOSELECT)); + + if(ctmpa){ + screen->current = ctmpa; + if(screen->current->flags & CF_DOUBLEVAR && !stay_in_col) + screen->current->flags |= CF_VAR2; + + if((cmd == MC_CHARUP) && i) + config_scroll_down(i); + } + else + q_status_message(SM_ORDER, 0, 1, + _("Already at start of screen")); + + break; + + case MC_PAGEDN: /* page forward */ + screen->current->flags &= ~CF_VAR2; + for(ctmpa = screen->top_line, i = BODY_LINES(ps); + i > 0 && ctmpa; + ctmpb = ctmpa, ctmpa = next_confline(ctmpa), i--) + ; + + if(ctmpa){ /* first line off bottom of screen */ + ctmpb = ctmpa; + ps->mangled_body = 1; + /* find first selectable line on next page */ + for(screen->top_line = ctmpa; + ctmpa && (ctmpa->flags & CF_NOSELECT); + ctmpa = next_confline(ctmpa)) + ; + + /* + * No selectable lines on next page. Slide up to first + * selectable. + */ + if(!ctmpa){ + for(ctmpa = prev_confline(ctmpb); + ctmpa && (ctmpa->flags & CF_NOSELECT); + ctmpa = prev_confline(ctmpa)) + ; + + if(ctmpa) + screen->top_line = ctmpa; + } + } + else{ /* on last screen */ + /* just move current down to last entry on screen */ + if(ctmpb){ /* last line of data */ + for(ctmpa = ctmpb, i = BODY_LINES(ps); + i > 0 && ctmpa && (ctmpa->flags & CF_NOSELECT); + ctmpa = prev_confline(ctmpa), i--) + ; + + if(ctmpa == screen->current){ + q_status_message(SM_ORDER,0,1, + _("Already at end of screen")); + goto no_down; + } + + ps->mangled_body = 1; + } + } + + if(ctmpa) + screen->current = ctmpa; +no_down: + break; + + case MC_PAGEUP: /* page backward */ + ps->mangled_body = 1; + screen->current->flags &= ~CF_VAR2; + if(!(ctmpa=prev_confline(screen->top_line))) + ctmpa = screen->current; + + for(i = BODY_LINES(ps) - 1; + i > 0 && prev_confline(ctmpa); + i--, ctmpa = prev_confline(ctmpa)) + ; + + for(screen->top_line = ctmpa; + ctmpa && (ctmpa->flags & CF_NOSELECT); + ctmpa = next_confline(ctmpa)) + ; + + if(ctmpa){ + if(ctmpa == screen->current){ + /* + * We get to here if there was nothing selectable on + * the previous page. There still may be something + * selectable further back than the previous page, + * so look for that. + */ + for(ctmpa = prev_confline(screen->top_line); + ctmpa && (ctmpa->flags & CF_NOSELECT); + ctmpa = prev_confline(ctmpa)) + ; + + if(!ctmpa){ + ctmpa = screen->current; + q_status_message(SM_ORDER, 0, 1, + _("Already at start of screen")); + } + } + + screen->current = ctmpa; + } + + break; + +#ifdef MOUSE + case MC_MOUSE: + { + MOUSEPRESS mp; + + mouse_get_last (NULL, &mp); + mp.row -= HEADER_ROWS(ps); + ctmpa = screen->top_line; + + while (mp.row && ctmpa != NULL) { + --mp.row; + ctmpa = ctmpa->next; + } + + if (ctmpa != NULL && !(ctmpa->flags & CF_NOSELECT)){ + if(screen->current->flags & CF_DOUBLEVAR) + screen->current->flags &= ~CF_VAR2; + + screen->current = ctmpa; + + if(screen->current->flags & CF_DOUBLEVAR && + mp.col >= screen->current->val2offset) + screen->current->flags |= CF_VAR2; + + update_option_screen(ps, screen, &cursor_pos); + + if(mp.button == M_BUTTON_LEFT && mp.doubleclick){ + + if(screen->current->tool){ + unsigned flags; + int default_cmd; + + flags = screen->current->flags; + flags |= (changes ? CF_CHANGES : 0); + + default_cmd = menu_command(ctrl('M'), km); + switch(i=(*screen->current->tool)(ps, default_cmd, + &screen->current, flags)){ + case -1: + case 0: + break; + + case 1: + changes = 1; + break; + + case 2: + retval = changes; + done++; + break; + + case 3: + retval = 1; + done++; + break; + + default: + retval = i; + done++; + break; + } + } + } +#ifdef _WINDOWS + else if(mp.button == M_BUTTON_RIGHT) { + MPopup other_popup[20]; + int n = -1, cmd, i; + struct key_menu *sckm = screen->current->keymenu; /* only for popup */ + + if((cmd = menu_command(ctrl('M'), sckm)) != MC_UNKNOWN){ + i = menu_binding_index(sckm, cmd); + other_popup[++n].type = tQueue; + other_popup[n].label.style = lNormal; + other_popup[n].label.string = sckm->keys[i].label; + other_popup[n].data.val = ctrl('M'); + } + else if((cmd = menu_command('>', sckm)) != MC_UNKNOWN){ + i = menu_binding_index(sckm, cmd); + other_popup[++n].type = tQueue; + other_popup[n].label.style = lNormal; + other_popup[n].label.string = sckm->keys[i].label; + other_popup[n].data.val = '>'; + } + + if(((i = menu_binding_index(sckm, MC_RGB1)) >= 0) || + ((i = menu_binding_index(sckm, MC_RGB2)) >= 0)){ + other_popup[++n].type = tQueue; + other_popup[n].label.style = lNormal; + other_popup[n].label.string = sckm->keys[i].label; + other_popup[n].data.val = + sckm->keys[i].bind.ch[0]; + } + + if((cmd = menu_command('<', sckm)) != MC_UNKNOWN){ + i = menu_binding_index(sckm, cmd); + other_popup[++n].type = tQueue; + other_popup[n].label.style = lNormal; + other_popup[n].label.string = sckm->keys[i].label; + other_popup[n].data.val = '<'; + } + else if((i = menu_binding_index(sckm, MC_EXIT)) >= 0){ + other_popup[++n].type = tQueue; + other_popup[n].label.style = lNormal; + other_popup[n].label.string = sckm->keys[i].label; + other_popup[n].data.val = + sckm->keys[i].bind.ch[0]; + } + + if((i = menu_binding_index(sckm, MC_HELP)) >= 0){ + if(n > 0) + other_popup[++n].type = tSeparator; + + other_popup[++n].type = tQueue; + other_popup[n].label.style = lNormal; + other_popup[n].label.string = sckm->keys[i].label; + other_popup[n].data.val = sckm->keys[i].bind.ch[0]; + } + + if(n > 0){ + other_popup[++n].type = tTail; + mswin_popup(other_popup); + } + } + } + else if(mp.button == M_BUTTON_RIGHT) { + MPopup other_popup[20]; + int n = -1, cmd, i; + struct key_menu *sckm = screen->current->keymenu; /* only for popup */ + + if((cmd = menu_command('<', sckm)) != MC_UNKNOWN){ + i = menu_binding_index(sckm, cmd); + other_popup[++n].type = tQueue; + other_popup[n].label.style = lNormal; + other_popup[n].label.string = sckm->keys[i].label; + other_popup[n].data.val = '<'; + } + else if((i = menu_binding_index(sckm, MC_EXIT)) >= 0){ + other_popup[++n].type = tQueue; + other_popup[n].label.style = lNormal; + other_popup[n].label.string = sckm->keys[i].label; + other_popup[n].data.val = sckm->keys[i].bind.ch[0]; + } + + other_popup[++n].type = tTail; + + if(n > 0) + mswin_popup(other_popup); +#endif + } + } + break; +#endif + + case MC_PRINTTXT: /* print screen */ + print_option_screen(screen, pdesc ? pdesc : ""); + break; + + case MC_WHEREIS: /* whereis */ + /*--- get string ---*/ + {int rc, found = 0; +#define FOUND_IT 0x01 +#define FOUND_CURRENT 0x02 +#define FOUND_WRAPPED 0x04 +#define FOUND_NOSELECT 0x08 +#define FOUND_ABOVE 0x10 + char *result = NULL, buf[64]; + char *p, last[64]; + static HISTORY_S *history = NULL; + HelpType help; + static ESCKEY_S ekey[] = { + {0, 0, "", ""}, + /* TRANSLATORS: go to Top of screen */ + {ctrl('Y'), 10, "^Y", N_("Top")}, + {ctrl('V'), 11, "^V", N_("Bottom")}, + {KEY_UP, 30, "", ""}, + {KEY_DOWN, 31, "", ""}, + {-1, 0, NULL, NULL}}; +#define KU_WI (3) /* index of KEY_UP */ + + init_hist(&history, HISTSIZE); + last[0] = '\0'; + if((p = get_prev_hist(history, "", 0, NULL)) != NULL){ + strncpy(last, p, sizeof(last)); + last[sizeof(last)-1] = '\0'; + } + + ps->mangled_footer = 1; + buf[0] = '\0'; + snprintf(tmp, sizeof(tmp), "Word to find %s%s%s: ", + (last[0]) ? "[" : "", + (last[0]) ? last : "", + (last[0]) ? "]" : ""); + tmp[sizeof(tmp)-1] = '\0'; + help = NO_HELP; + while(1){ + int flags = OE_APPEND_CURRENT; + + /* + * 2 is really 1 because there will be one real entry and + * one entry of "" because of the get_prev_hist above. + */ + if(items_in_hist(history) > 2){ + ekey[KU_WI].name = HISTORY_UP_KEYNAME; + ekey[KU_WI].label = HISTORY_KEYLABEL; + ekey[KU_WI+1].name = HISTORY_DOWN_KEYNAME; + ekey[KU_WI+1].label = HISTORY_KEYLABEL; + } + else{ + ekey[KU_WI].name = ""; + ekey[KU_WI].label = ""; + ekey[KU_WI+1].name = ""; + ekey[KU_WI+1].label = ""; + } + + rc = optionally_enter(buf,-FOOTER_ROWS(ps),0,sizeof(buf), + tmp,ekey,help,&flags); + if(rc == 3) + help = help == NO_HELP ? h_config_whereis : NO_HELP; + else if(rc == 30){ + if((p = get_prev_hist(history, buf, 0, NULL)) != NULL){ + strncpy(buf, p, sizeof(buf)); + buf[sizeof(buf)-1] = '\0'; + } + else + Writechar(BELL, 0); + + continue; + } + else if(rc == 31){ + if((p = get_next_hist(history, buf, 0, NULL)) != NULL){ + strncpy(buf, p, sizeof(buf)); + buf[sizeof(buf)-1] = '\0'; + } + else + Writechar(BELL, 0); + + continue; + } + else if(rc == 0 || rc == 1 || rc == 10 || rc == 11 || !buf[0]){ + if(rc == 0 && !buf[0] && last[0]) + strncpy(buf, last, 64); + + break; + } + } + + screen->current->flags &= ~CF_VAR2; + if(rc == 0 && buf[0]){ + CONF_S *started_here; + + save_hist(history, buf, 0, NULL); + + 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)) != NULL) + 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". + */ + if(ctmpa->flags & CF_NOSELECT) + found |= FOUND_NOSELECT; + + 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; + } + + /* + * If that still isn't selectable, better search + * backwards for something that is. + */ + while((ctmpa->flags & CF_NOSELECT) && + (ctmpb = prev_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; + if(ctmpa->flags & CF_NOSELECT) + found |= FOUND_NOSELECT; + + 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; + } + + /* TRANSLATORS: Searched to ... is the result of the search, searched + to top means the search went past the bottom of the screen and + wrapped back around to the top. */ + 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){ + strncpy(last, buf, 64); + result = + (found & FOUND_CURRENT && found & FOUND_WRAPPED && found & FOUND_NOSELECT) + ? _("Current item contains the only match") + : (found & FOUND_CURRENT && found & FOUND_WRAPPED) + ? _("Current line contains the only match") + : (found & FOUND_NOSELECT && found & FOUND_WRAPPED) + ? ((found & FOUND_ABOVE) + ? _("Search wrapped: word found in text above current line") + : _("Search wrapped: word found in text below current line")) + : (found & FOUND_WRAPPED) + ? _("Search wrapped to beginning: word found") + : (found & FOUND_NOSELECT) + ? ((found & FOUND_ABOVE) + ? _("Word found in text above current line") + : _("Word found in text below current line")) + : _("Word found"); + screen->current = ctmpa; + } + + q_status_message(SM_ORDER,0,3,result ? result : _("Word not found")); + } + + break; + + case MC_HOMEKEY: + 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; + } + + q_status_message(SM_ORDER,0,3, _("Moved to top")); + break; + + case MC_ENDKEY: + 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; + } + + q_status_message(SM_ORDER,0,3, _("Moved to bottom")); + break; + + case MC_REPAINT: /* redraw the display */ + case MC_RESIZE: + ClearScreen(); + ps->mangled_screen = 1; + break; + + default: + if(screen && screen->ro_warning){ + if(cmd == MC_EXIT){ + retval = 0; + done++; + } + else + q_status_message1(SM_ORDER|SM_DING, 1, 3, + _("%s can't change options or settings"), + ps_global->restricted ? "Alpine demo" + : _("Config file not changeable,")); + } + 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, + /* TRANSLATORS: Command not defined here. + Leave the trailing %s which might be a parenthetical + remark. */ + _("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_UTF8: + bogus_utf8_command(utf8str, "?"); + break; + + case MC_NONE: /* simple timeout */ + break; + } + } + + screen->current = first_confline(screen->current); + free_conflines(&screen->current); + return(retval); +} + + +/* + * + */ +void +config_scroll_up(long int n) +{ + CONF_S *ctmp = opt_screen->top_line; + int cur_found = 0; + + 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; + 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(long int n) +{ + CONF_S *ctmp = opt_screen->top_line, *last_sel = NULL; + int i; + + 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; + 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(long int 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)) != NULL) + n--; + + config_scroll_up(n); +} + + +/* + * config_top_scroll - return pointer to the + */ +CONF_S * +config_top_scroll(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); +} + + +int +text_tool(struct pine *ps, int cmd, CONF_S **cl, unsigned int flags) +{ + return(text_toolit(ps, cmd, cl, flags, 0)); +} + + +/* + * 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 ;). + * + * look_for_backslash == 1 means that backslash is an escape character. + * In particular, \, can be used to put a literal comma + * into a value. The value will still have the backslash + * in it, but the comma after the backslash won't be treated + * as an item separator. + * + * returns: -1 on unrecognized cmd, 0 if no change, 1 if change + * returns what conf_exit_cmd returns for exit command. + */ +int +text_toolit(struct pine *ps, int cmd, CONF_S **cl, unsigned int flags, int look_for_backslash) +{ + char prompt[81], *sval, *tmp, *swap_val, **newval = NULL; + char *pval, **apval, **lval, ***alval; + char *olddefval = NULL; + int rv = 0, skip_to_next = 0, after = 0, i = 4, j, k; + int lowrange, hirange, incr, oeflags, oebufsize; + int numval, repeat_key = 0; + int curindex, previndex, nextindex, deefault; + HelpType help; + ESCKEY_S ekey[6]; + + if((*cl)->var->is_list){ + lval = LVAL((*cl)->var, ew); + alval = ALVAL((*cl)->var, ew); + } + else{ + pval = PVAL((*cl)->var, ew); + apval = APVAL((*cl)->var, ew); + } + + oebufsize = 6*MAXPATH; + sval = (char *) fs_get(oebufsize*sizeof(char)); + sval[0] = '\0'; + + if(flags&CF_NUMBER){ /* only happens if !is_list */ + incr = 1; + if((*cl)->var == &ps->vars[V_FILLCOL]){ + lowrange = 1; + hirange = MAX_FILLCOL; + } + else if((*cl)->var == &ps->vars[V_OVERLAP] + || (*cl)->var == &ps->vars[V_MARGIN]){ + lowrange = 0; + hirange = 20; + } + else if((*cl)->var == &ps->vars[V_QUOTE_SUPPRESSION]){ + lowrange = -(Q_SUPP_LIMIT-1); + hirange = 1000; + } + else if((*cl)->var == &ps->vars[V_MAXREMSTREAM]){ + lowrange = 0; + hirange = 15; + } + else if((*cl)->var == &ps->vars[V_STATUS_MSG_DELAY]){ + lowrange = -10; + hirange = 30; + } + else if((*cl)->var == &ps->vars[V_ACTIVE_MSG_INTERVAL]){ + lowrange = 0; + hirange = 20; + } + else if((*cl)->var == &ps->vars[V_MAILCHECK] || + (*cl)->var == &ps->vars[V_INCCHECKINTERVAL] || + (*cl)->var == &ps->vars[V_INC2NDCHECKINTERVAL] || + (*cl)->var == &ps->vars[V_MAILCHECKNONCURR]){ + lowrange = 0; + hirange = 25000; + incr = 15; + } + else if((*cl)->var == &ps->vars[V_DEADLETS]){ + lowrange = 0; + hirange = 9; + } + else if((*cl)->var == &ps->vars[V_NMW_WIDTH]){ + lowrange = 20; + hirange = MAX_SCREEN_COLS; + } + else if((*cl)->var == score_act_global_ptr){ + lowrange = -100; + hirange = 100; + } + else if((*cl)->var == &ps->vars[V_TCPOPENTIMEO] || + (*cl)->var == &ps->vars[V_TCPREADWARNTIMEO] || + (*cl)->var == &ps->vars[V_TCPQUERYTIMEO]){ + lowrange = 5; + hirange = 1000; + } + else if((*cl)->var == &ps->vars[V_TCPWRITEWARNTIMEO] || + (*cl)->var == &ps->vars[V_RSHOPENTIMEO] || + (*cl)->var == &ps->vars[V_SSHOPENTIMEO] || + (*cl)->var == &ps->vars[V_USERINPUTTIMEO]){ + lowrange = 0; + hirange = 1000; + } + else if((*cl)->var == &ps->vars[V_INCCHECKTIMEO]){ + lowrange = 1; + hirange = 1000; + } + else if((*cl)->var == &ps->vars[V_MAILDROPCHECK]){ + lowrange = 0; + hirange = 1000000; + incr = 60; + } + else if((*cl)->var == &ps->vars[V_NNTPRANGE]){ + lowrange = 0; + hirange = 1000000; + incr = 100; + } + else if((*cl)->var == &ps->vars[V_REMOTE_ABOOK_VALIDITY]){ + lowrange = -1; + hirange = 25000; + } + else if((*cl)->var == &ps->vars[V_REMOTE_ABOOK_HISTORY]){ + lowrange = 0; + hirange = 100; + } + else if((*cl)->var == cat_lim_global_ptr){ + lowrange = -1; + hirange = 10000000; + } + 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 = N_("Decrease"); + ekey[2].ch = ctrl('N'); + ekey[2].rval = ctrl('N'); + ekey[2].name = "^N"; + ekey[2].label = N_("Increase"); + ekey[3].ch = KEY_DOWN; + ekey[3].rval = ctrl('P'); + ekey[3].name = ""; + ekey[3].label = ""; + ekey[4].ch = KEY_UP; + ekey[4].rval = ctrl('N'); + ekey[4].name = ""; + ekey[4].label = ""; + ekey[5].ch = -1; + } + + switch(cmd){ + case MC_ADD: /* add to list */ + if(fixed_var((*cl)->var, "add to", NULL)){ + break; + } + else if(!(*cl)->var->is_list && pval){ + q_status_message(SM_ORDER, 3, 3, + _("Only single value allowed. Use \"Change\".")); + } + else{ + int maxwidth; + char *p; + + if((*cl)->var->is_list + && lval && lval[0] && lval[0][0] + && (*cl)->value){ + char tmpval[101]; + /* regular add to an existing list */ + + strncpy(tmpval, (*cl)->value, sizeof(tmpval)); + tmpval[sizeof(tmpval)-1] = '\0'; + removing_trailing_white_space(tmpval); + + /* 33 is the number of chars other than the value */ + maxwidth = MIN(80, ps->ttyo->screen_cols) - 15; + k = MIN(18, MAX(maxwidth-33,0)); + if(utf8_width(tmpval) > k && k >= 3){ + (void) utf8_truncate(tmpval, k-3); + strncat(tmpval, "...", sizeof(tmpval)-strlen(tmpval)-1); + tmpval[sizeof(tmpval)-1] = '\0'; + } + + utf8_snprintf(prompt, sizeof(prompt), + _("Enter text to insert before \"%.*w\": "), k, tmpval); + prompt[sizeof(prompt)-1] = '\0'; + } + else if((*cl)->var->is_list + && !lval + && (*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 = N_("Replace"); + ekey[1].ch = 'a'; + ekey[1].rval = 'a'; + ekey[1].name = "A"; + ekey[1].label = N_("Add To"); + ekey[2].ch = -1; + strncpy(prompt, _("Replace or Add To default value ? "), sizeof(prompt)); + prompt[sizeof(prompt)-1] = '\0'; + 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++){ + sstrncpy(&p, (*cl)->var->current_val.l[j], oebufsize-(p-sval)); + if(oebufsize-(p-sval) > 2){ + *p++ = ','; + *p++ = ' '; + } + + if(oebufsize-(p-sval) > 0) + *p = '\0'; + } + + sval[oebufsize-1] = '\0'; + +add_text: + if(flags & CF_NUMBER) + snprintf(prompt, sizeof(prompt), _("Enter the numeric text to be added : ")); + else + snprintf(prompt, sizeof(prompt), _("Enter the text to be added : ")); + + break; + + case 'r': +replace_text: + if(olddefval){ + strncpy(sval, olddefval, oebufsize); + sval[oebufsize-1] = '\0'; + } + + if(flags & CF_NUMBER) + snprintf(prompt, sizeof(prompt), _("Enter the numeric replacement text : ")); + else + snprintf(prompt, sizeof(prompt), _("Enter the replacement text : ")); + + break; + + case 'x': + i = 1; + cmd_cancelled("Add"); + break; + } + } + else{ + if(flags & CF_NUMBER) + snprintf(prompt, sizeof(prompt), _("Enter the numeric text to be added : ")); + else + snprintf(prompt, sizeof(prompt), _("Enter the text to be added : ")); + } + + prompt[sizeof(prompt)-1] = '\0'; + + ps->mangled_footer = 1; + + if(i == 1) + break; + + help = NO_HELP; + while(1){ + if((*cl)->var->is_list + && lval && lval[0] && lval[0][0] + && (*cl)->value){ + ekey[0].ch = ctrl('W'); + ekey[0].rval = 5; + ekey[0].name = "^W"; + /* TRANSLATORS: Insert new item before current item */ + ekey[0].label = after ? N_("InsertBefore") : N_("InsertAfter"); + ekey[1].ch = -1; + } + else if(!(flags&CF_NUMBER)) + ekey[0].ch = -1; + + oeflags = OE_APPEND_CURRENT; + i = optionally_enter(sval, -FOOTER_ROWS(ps), 0, oebufsize, + prompt, + (ekey[0].ch != -1) ? ekey : NULL, + help, &oeflags); + if(i == 0){ + rv = 1; + if((*cl)->var->is_list) + ps->mangled_body = 1; + else + ps->mangled_footer = 1; + + removing_leading_and_trailing_white_space(sval); + /* + * Coerce "" and to empty string input. + * Catch as a substitute for deleting. + */ + if((*sval == '\"' && *(sval+1) == '\"' && *(sval+2) == '\0') + || !struncmp(sval, _(empty_val), strlen(_(empty_val))) + || (*sval == '<' + && !struncmp(sval+1, _(empty_val), strlen(_(empty_val))))) + *sval = '\0'; + else if(!struncmp(sval, _(no_val), strlen(_(no_val))) + || (*sval == '<' + && !struncmp(sval+1, _(no_val), strlen(_(no_val))))) + goto delete; + + if((*cl)->var->is_list){ + if(*sval || !lval){ + 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, + look_for_backslash + ? PL_COMMAQUOTE : 0, + 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 **)<mp); + } + 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(apval && *apval) + fs_give((void **)apval); + + if(!(olddefval && !strcmp(sval, olddefval)) + || ((*cl)->var == &ps->vars[V_POST_CHAR_SET]) + || want_to(_("Leave unset and use default "), + 'y', 'y', NO_HELP, WT_FLUSH_IN) == 'n') + *apval = cpystr(sval); + + newval = &(*cl)->value; + } + } + else if(i == 1){ + cmd_cancelled("Add"); + } + else if(i == 3){ + help = help == NO_HELP ? h_config_add : NO_HELP; + continue; + } + else if(i == 4){ /* no redraw, yet */ + continue; + } + else if(i == 5){ /* change from/to prepend to/from append */ + char tmpval[101]; + + after = after ? 0 : 1; + strncpy(tmpval, (*cl)->value, sizeof(tmpval)); + tmpval[sizeof(tmpval)-1] = '\0'; + removing_trailing_white_space(tmpval); + /* 33 is the number of chars other than the value */ + maxwidth = MIN(80, ps->ttyo->screen_cols) - 15; + k = MIN(18, MAX(maxwidth-33,0)); + if(utf8_width(tmpval) > k && k >= 3){ + (void) utf8_truncate(tmpval, k-3); + strncat(tmpval, "...", sizeof(tmpval)-strlen(tmpval)-1); + tmpval[sizeof(tmpval)-1] = '\0'; + } + + if(after) + snprintf(prompt, sizeof(prompt), _("Enter text to insert after \"%.*s\": "), k, tmpval); + else + snprintf(prompt, sizeof(prompt), _("Enter text to insert before \"%.*s\": "), k, tmpval); + + continue; + } + else if(i == ctrl('P')){ + if(sval[0]) + numval = atoi(sval); + else{ + if(pval) + numval = atoi(pval); + 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); + snprintf(sval, oebufsize, "%d", numval); + sval[oebufsize-1] = '\0'; + continue; + } + else if(i == ctrl('N')){ + if(sval[0]) + numval = atoi(sval); + else{ + if(pval) + numval = atoi(pval); + 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); + snprintf(sval, oebufsize, "%d", numval); + sval[oebufsize-1] = '\0'; + continue; + } + + break; + } + } + + break; + + case MC_DELETE: /* delete */ +delete: + if(!(*cl)->var->is_list + && apval && !*apval + && (*cl)->var->current_val.p){ + char pmt[80]; + + snprintf(pmt, sizeof(pmt), _("Override default with %s"), _(empty_val2)); + pmt[sizeof(pmt)-1] = '\0'; + if(want_to(pmt, 'n', 'n', NO_HELP, WT_FLUSH_IN) == 'y'){ + sval[0] = '\0'; + *apval = cpystr(sval); + newval = &(*cl)->value; + rv = ps->mangled_footer = 1; + } + } + else if((*cl)->var->is_list + && alval && !lval + && (*cl)->var->current_val.l){ + char pmt[80]; + + snprintf(pmt, sizeof(pmt), _("Override default with %s"), _(empty_val2)); + pmt[sizeof(pmt)-1] = '\0'; + 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 **)<mp); + rv = ps->mangled_body = 1; + } + } + else if(((*cl)->var->is_list && !lval) + || (!(*cl)->var->is_list && !pval)){ + q_status_message(SM_ORDER, 0, 3, _("No set value to delete")); + } + else{ + if((*cl)->var->is_fixed) + snprintf(prompt, sizeof(prompt), _("Delete (unused) %s from %s "), + (*cl)->var->is_list + ? (!*lval[(*cl)->varmem]) + ? _(empty_val2) + : lval[(*cl)->varmem] + : (pval) + ? (!*pval) + ? _(empty_val2) + : pval + : "", + (*cl)->var->name); + else + snprintf(prompt, sizeof(prompt), _("Really delete %s%s from %s "), + (*cl)->var->is_list ? "item " : "", + (*cl)->var->is_list + ? int2string((*cl)->varmem + 1) + : (pval) + ? (!*pval) + ? _(empty_val2) + : pval + : "", + (*cl)->var->name); + + prompt[sizeof(prompt)-1] = '\0'; + + + ps->mangled_footer = 1; + if(want_to(prompt, 'n', 'n', NO_HELP, WT_FLUSH_IN) == 'y'){ + rv = 1; + if((*cl)->var->is_list) + ps->mangled_body = 1; + else + ps->mangled_footer = 1; + + if((*cl)->var->is_list){ + if(lval[(*cl)->varmem]) + fs_give((void **)&lval[(*cl)->varmem]); + + config_del_list_item(cl, &newval); + } + else{ + if(apval && *apval) + fs_give((void **)apval); + + 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 + && !lval + && (*cl)->var->current_val.l) + || + (!(*cl)->var->is_list + && !pval + && (*cl)->var->current_val.p)){ + + /* + * In non-list case, offer default value for editing. + */ + if(!(*cl)->var->is_list + && (*cl)->var != &ps->vars[V_REPLY_INTRO] + && (*cl)->var->current_val.p[0] + && strcmp(VSTRING,(*cl)->var->current_val.p)){ + int quote_it; + size_t len; + + olddefval = (char *) fs_get(strlen((*cl)->var->current_val.p)+3); + + if(!strncmp((*cl)->var->current_val.p, + DSTRING, + (len=strlen(DSTRING)))){ + /* strip DSTRING and trailing paren */ + strncpy(olddefval, (*cl)->var->current_val.p+len, + strlen((*cl)->var->current_val.p)-len-1); + olddefval[strlen((*cl)->var->current_val.p)-len-1] = '\0'; + } + else{ + /* quote it if there are trailing spaces */ + quote_it = ((*cl)->var->current_val.p[strlen((*cl)->var->current_val.p)-1] == SPACE); + snprintf(olddefval, strlen((*cl)->var->current_val.p)+3, "%s%s%s", quote_it ? "\"" : "", (*cl)->var->current_val.p, quote_it ? "\"" : ""); + } + + olddefval[strlen((*cl)->var->current_val.p)+3-1] = '\0'; + } + + goto replace_text; + } + else if(((*cl)->var->is_list + && !lval + && !(*cl)->var->current_val.l) + || + (!(*cl)->var->is_list + && !pval + && !(*cl)->var->current_val.p)){ + goto add_text; + } + else{ + HelpType help; + char *clptr; + + if(sval) + fs_give((void **)&sval); + if((*cl)->var->is_list){ + snprintf(prompt, sizeof(prompt), _("Change field %s list entry : "), + (*cl)->var->name); + prompt[sizeof(prompt)-1] = '\0'; + clptr = lval[(*cl)->varmem] ? lval[(*cl)->varmem] : NULL; + } + else{ + if(flags & CF_NUMBER) + snprintf(prompt, sizeof(prompt), _("Change numeric field %s value : "), (*cl)->var->name); + else + snprintf(prompt, sizeof(prompt), _("Change field %s value : "), (*cl)->var->name); + + clptr = pval ? pval : NULL; + } + + oebufsize = clptr ? (int) MAX(MAXPATH, 50+strlen(clptr)) : MAXPATH; + sval = (char *) fs_get(oebufsize * sizeof(char)); + snprintf(sval, oebufsize, "%s", clptr ? clptr : ""); + sval[oebufsize-1] = '\0'; + + ps->mangled_footer = 1; + help = NO_HELP; + while(1){ + if(!(flags&CF_NUMBER)) + ekey[0].ch = -1; + + oeflags = OE_APPEND_CURRENT; + i = optionally_enter(sval, -FOOTER_ROWS(ps), 0, oebufsize, + prompt, + (ekey[0].ch != -1) ? ekey : NULL, + help, &oeflags); + if(i == 0){ + removing_leading_and_trailing_white_space(sval); + /* + * Coerce "" and to empty string input. + * Catch as a substitute for deleting. + */ + if((*sval == '\"' && *(sval+1) == '\"' && *(sval+2) == '\0') + || !struncmp(sval, _(empty_val), strlen(_(empty_val))) + || (*sval == '<' + && !struncmp(sval+1, _(empty_val), strlen(_(empty_val))))) + *sval = '\0'; + else if(!struncmp(sval, _(no_val), strlen(_(no_val))) + || (*sval == '<' + && !struncmp(sval+1, _(no_val), strlen(_(no_val))))) + goto delete; + + rv = 1; + if((*cl)->var->is_list) + ps->mangled_body = 1; + else + ps->mangled_footer = 1; + + if((*cl)->var->is_list){ + char **ltmp = NULL; + int i; + + if(lval[(*cl)->varmem]) + fs_give((void **)&lval[(*cl)->varmem]); + + i = 0; + for(tmp = sval; *tmp; tmp++) + if(*tmp == ',') + i++; /* conservative count of ,'s */ + + if(i) + ltmp = parse_list(sval, i + 1, + look_for_backslash + ? PL_COMMAQUOTE : 0, + NULL); + + if(ltmp && !ltmp[0]) /* only commas */ + goto delete; + else if(!i || (ltmp && !ltmp[1])){ /* only one item */ + lval[(*cl)->varmem] = cpystr(sval); + newval = &(*cl)->value; + + if(ltmp && ltmp[0]) + fs_give((void **)<mp[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 || lval[1]) + && !(lval[(*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 **)<mp); + } + 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(apval && *apval) + fs_give((void **)apval); + + if(sval[0] && apval) + *apval = cpystr(sval); + + newval = &(*cl)->value; + } + } + else if(i == 1){ + cmd_cancelled("Change"); + } + else if(i == 3){ + help = help == NO_HELP ? h_config_change : NO_HELP; + continue; + } + else if(i == 4){ /* no redraw, yet */ + continue; + } + else if(i == ctrl('P')){ + numval = atoi(sval); + if(numval == lowrange){ + /* + * Protect user from repeating arrow key that + * causes message to appear over and over. + */ + if(++repeat_key > 0){ + q_status_message1(SM_ORDER,3,3, + _("Minimum value is %s"), comatose(lowrange)); + repeat_key = -5; + } + } + else + repeat_key = 0; + + numval = MAX(numval - incr, lowrange); + snprintf(sval, oebufsize, "%d", numval); + sval[oebufsize-1] = '\0'; + 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); + snprintf(sval, oebufsize, "%d", numval); + sval[oebufsize-1] = '\0'; + continue; + } + + break; + } + } + + break; + + case MC_SHUFFLE: + if(!((*cl)->var && (*cl)->var->is_list)){ + q_status_message(SM_ORDER, 0, 2, + _("Can't shuffle single-valued setting")); + break; + } + + if(!alval) + break; + + curindex = (*cl)->varmem; + previndex = curindex-1; + nextindex = curindex+1; + if(!*alval || !(*alval)[nextindex]) + nextindex = -1; + + if((previndex < 0 && nextindex < 0) || !*alval){ + q_status_message(SM_ORDER, 0, 3, + _("Shuffle only makes sense when there is more than one value defined")); + break; + } + + /* Move it up or down? */ + i = 0; + ekey[i].ch = 'u'; + ekey[i].rval = 'u'; + ekey[i].name = "U"; + ekey[i++].label = N_("Up"); + + ekey[i].ch = 'd'; + ekey[i].rval = 'd'; + ekey[i].name = "D"; + ekey[i++].label = N_("Down"); + + ekey[i].ch = -1; + deefault = 'u'; + + if(previndex < 0){ /* no up */ + ekey[0].ch = -2; + deefault = 'd'; + } + else if(nextindex < 0) + ekey[1].ch = -2; /* no down */ + + snprintf(prompt, sizeof(prompt), "Shuffle %s%s%s ? ", + (ekey[0].ch != -2) ? "UP" : "", + (ekey[0].ch != -2 && ekey[1].ch != -2) ? " or " : "", + (ekey[1].ch != -2) ? "DOWN" : ""); + help = (ekey[0].ch == -2) ? h_hdrcolor_shuf_down + : (ekey[1].ch == -2) ? h_hdrcolor_shuf_up + : h_hdrcolor_shuf; + prompt[sizeof(prompt)-1] = '\0'; + + i = radio_buttons(prompt, -FOOTER_ROWS(ps), ekey, deefault, 'x', + help, RB_NORM); + + switch(i){ + case 'x': + cmd_cancelled("Shuffle"); + return(rv); + + case 'u': + case 'd': + break; + } + + /* swap order */ + if(i == 'd'){ + swap_val = (*alval)[curindex]; + (*alval)[curindex] = (*alval)[nextindex]; + (*alval)[nextindex] = swap_val; + } + else if(i == 'u'){ + swap_val = (*alval)[curindex]; + (*alval)[curindex] = (*alval)[previndex]; + (*alval)[previndex] = swap_val; + } + else /* can't happen */ + break; + + /* + * Fix the conf line values. + */ + + if((*cl)->value) + fs_give((void **)&(*cl)->value); + + (*cl)->value = pretty_value(ps, *cl); + + if(i == 'd'){ + if((*cl)->next->value) + fs_give((void **)&(*cl)->next->value); + + (*cl)->next->value = pretty_value(ps, (*cl)->next); + *cl = next_confline(*cl); + } + else{ + if((*cl)->prev->value) + fs_give((void **)&(*cl)->prev->value); + + (*cl)->prev->value = pretty_value(ps, (*cl)->prev); + *cl = prev_confline(*cl); + } + + rv = ps->mangled_body = 1; + 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); + } + + exception_override_warning((*cl)->var); + } + + if(sval) + fs_give((void **) &sval); + + if(olddefval) + fs_give((void **) &olddefval); + + return(rv); +} + + +int +config_exit_cmd(unsigned int flags) +{ + return(screen_exit_cmd(flags, "Configuration")); +} + + +int +simple_exit_cmd(unsigned int flags) +{ + return(2); +} + + +/* + * screen_exit_cmd - basic config/flag screen exit logic + */ +int +screen_exit_cmd(unsigned int flags, char *cmd) +{ + if(flags & CF_CHANGES){ + switch(want_to(EXIT_PMT, 'y', 'x', h_config_undo, WT_FLUSH_IN)){ + case 'y': + q_status_message1(SM_ORDER,0,3,"%s changes saved", cmd); + return(2); + + case 'n': + q_status_message1(SM_ORDER,3,5,"No %s changes saved", cmd); + return(10); + + case 'x': /* ^C */ + default : + q_status_message(SM_ORDER,3,5,"Changes not yet saved"); + return(0); + } + } + else + return(2); +} + + +/* + * + */ +void +config_add_list(struct pine *ps, CONF_S **cl, char **ltmp, char ***newval, int after) +{ + int items, i; + char *tmp, ***alval; + CONF_S *ctmp; + + for(items = 0, i = 0; ltmp[i]; i++) /* count list items */ + items++; + + alval = ALVAL((*cl)->var, ew); + + if(alval && (*alval)){ + if((*alval)[0] && (*alval)[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; (*alval)[i]; i++) + ; + + fs_resize((void **)alval, (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--) + (*alval)[i+items] = (*alval)[i]; + + i = 0; + } + else if(alval){ + (*cl)->varmem = 0; + if(*alval) + free_list_array(alval); + + *alval = (char **)fs_get((items+1)*sizeof(char *)); + memset((void *)(*alval), 0, (items+1)*sizeof(char *)); + (*alval)[0] = ltmp[0]; + if(newval) + *newval = &(*cl)->value; + + if((*cl)->value) + fs_give((void **)&(*cl)->value); + + i = 1; + } + } + else if(alval){ + /* + * 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. + */ + *alval = (char **)fs_get((items+1)*sizeof(char *)); + memset((void *)(*alval), 0, (items+1) * sizeof(char *)); + (*alval)[(*cl)->varmem=0] = ltmp[0]; + if(newval) + *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++){ + (*alval)[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(newval){ + 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; + (*alval)[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(CONF_S **cl, char ***newval) +{ + char **bufp, ***alval; + int i; + CONF_S *ctmp; + + alval = ALVAL((*cl)->var, ew); + + if((*alval)[(*cl)->varmem + 1]){ + for(bufp = &(*alval)[(*cl)->varmem]; + (*bufp = *(bufp+1)) != NULL; 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 */ + (*alval)[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 */ + if(alval && *alval) + fs_give((void **)alval); + + *newval = &(*cl)->value; + } +} + + +/* + * feature list manipulation tool + * + * + * returns: -1 on unrecognized cmd, 0 if no change, 1 if change + */ +int +checkbox_tool(struct pine *ps, int cmd, CONF_S **cl, unsigned int 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, 0); + } + 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); +} + + +/* + * simple radio-button style variable handler + */ +int +radiobutton_tool(struct pine *ps, int cmd, CONF_S **cl, unsigned int flags) +{ + char **apval; + int rv = 0; + NAMEVAL_S *rule = NULL; +#ifndef _WINDOWS + int old_uc, old_cs; + CONF_S *ctmp; +#endif + + apval = APVAL((*cl)->var, ew); + + switch(cmd){ + case MC_CHOICE : /* set/unset feature */ + + if(fixed_var((*cl)->var, NULL, NULL)){ + if(((*cl)->var->post_user_val.p || (*cl)->var->main_user_val.p) + && want_to(_("Delete old unused personal option setting"), + 'y', 'n', NO_HELP, WT_FLUSH_IN) == 'y'){ + delete_user_vals((*cl)->var); + q_status_message(SM_ORDER, 0, 3, _("Deleted")); + rv = 1; + } + + return(rv); + } + + if(standard_radio_var(ps, (*cl)->var) || (*cl)->var == startup_ptr){ + PTR_TO_RULEFUNC rulefunc; + +#ifndef _WINDOWS + if((*cl)->var == &ps->vars[V_COLOR_STYLE]){ + old_uc = pico_usingcolor(); + old_cs = ps->color_style; + } +#endif + + if((*cl)->var->cmdline_val.p) + fs_give((void **)&(*cl)->var->cmdline_val.p); + + if(apval && *apval) + fs_give((void **)apval); + + rulefunc = rulefunc_from_var(ps, (*cl)->var); + if(rulefunc) + rule = (*rulefunc)((*cl)->varmem); + + if(apval && rule) + *apval = cpystr(S_OR_L(rule)); + + cur_rule_value((*cl)->var, TRUE, TRUE); + set_radio_pretty_vals(ps, cl); + + if((*cl)->var == &ps->vars[V_AB_SORT_RULE]) + addrbook_redo_sorts(); + else if((*cl)->var == &ps->vars[V_THREAD_DISP_STYLE]){ + clear_index_cache(ps->mail_stream, 0); + } + else if((*cl)->var == &ps->vars[V_THREAD_INDEX_STYLE]){ + MAILSTREAM *m; + int i; + + clear_index_cache(ps->mail_stream, 0); + /* clear all hidden and collapsed flags */ + set_lflags(ps->mail_stream, ps->msgmap, MN_COLL | MN_CHID, 0); + + if(SEP_THRDINDX() + && SORT_IS_THREADED(ps->msgmap) + && unview_thread(ps, ps->mail_stream, ps->msgmap)){ + ps->next_screen = mail_index_screen; + ps->view_skipped_index = 0; + ps->mangled_screen = 1; + } + + if(SORT_IS_THREADED(ps->msgmap) + && (SEP_THRDINDX() || COLL_THRDS())) + collapse_threads(ps->mail_stream, ps->msgmap, NULL); + + for(i = 0; i < ps_global->s_pool.nstream; i++){ + m = ps_global->s_pool.streams[i]; + if(m) + sp_set_viewing_a_thread(m, 0); + } + + adjust_cur_to_visible(ps->mail_stream, ps->msgmap); + } +#ifndef _WINDOWS + else if((*cl)->var == &ps->vars[V_COLOR_STYLE]){ + if(old_cs != ps->color_style){ + pico_toggle_color(0); + switch(ps->color_style){ + case COL_NONE: + case COL_TERMDEF: + pico_set_color_options(pico_trans_color() ? COLOR_TRANS_OPT : 0); + break; + case COL_ANSI8: + pico_set_color_options(COLOR_ANSI8_OPT|COLOR_TRANS_OPT); + break; + case COL_ANSI16: + pico_set_color_options(COLOR_ANSI16_OPT|COLOR_TRANS_OPT); + break; + case COL_ANSI256: + pico_set_color_options(COLOR_ANSI256_OPT|COLOR_TRANS_OPT); + break; + } + + if(ps->color_style != COL_NONE) + pico_toggle_color(1); + } + + if(pico_usingcolor()) + pico_set_normal_color(); + + if(!old_uc && pico_usingcolor()){ + + /* + * remove the explanatory warning line and a blank line + */ + + /* first find the first blank line */ + for(ctmp = *cl; ctmp; ctmp = next_confline(ctmp)) + if(ctmp->flags & CF_NOSELECT) + break; + + if(ctmp && ctmp->flags & CF_NOSELECT && + ctmp->prev && !(ctmp->prev->flags & CF_NOSELECT) && + ctmp->next && ctmp->next->flags & CF_NOSELECT && + ctmp->next->next && + ctmp->next->next->flags & CF_NOSELECT){ + ctmp->prev->next = ctmp->next->next; + ctmp->next->next->prev = ctmp->prev; + ctmp->next->next = NULL; + free_conflines(&ctmp); + } + + /* make all the colors selectable */ + for(ctmp = *cl; ctmp; ctmp = next_confline(ctmp)) + if(ctmp->flags & CF_POT_SLCTBL) + ctmp->flags &= ~CF_NOSELECT; + } + else if(old_uc && !pico_usingcolor()){ + + /* + * add the explanatory warning line and a blank line + */ + + /* first find the existing blank line */ + for(ctmp = *cl; ctmp; ctmp = next_confline(ctmp)) + if(ctmp->flags & CF_NOSELECT) + break; + + /* add the explanatory warning line */ + new_confline(&ctmp); + ctmp->help = NO_HELP; + ctmp->flags |= CF_NOSELECT; + ctmp->value = cpystr(COLORNOSET); + + /* and add another blank line */ + new_confline(&ctmp); + ctmp->flags |= (CF_NOSELECT | CF_B_LINE); + + /* make all the colors non-selectable */ + for(ctmp = *cl; ctmp; ctmp = next_confline(ctmp)) + if(ctmp->flags & CF_POT_SLCTBL) + ctmp->flags |= CF_NOSELECT; + } + + clear_index_cache(ps->mail_stream, 0); + ClearScreen(); + ps->mangled_screen = 1; + } +#endif + + ps->mangled_body = 1; /* BUG: redraw it all for now? */ + rv = 1; + } + else if((*cl)->var == &ps->vars[V_SORT_KEY]){ + SortOrder def_sort; + int def_sort_rev; + + def_sort_rev = (*cl)->varmem >= (short) EndofList; + def_sort = (SortOrder) ((*cl)->varmem - (def_sort_rev + * EndofList)); + snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s", sort_name(def_sort), + (def_sort_rev) ? "/Reverse" : ""); + + if((*cl)->var->cmdline_val.p) + fs_give((void **)&(*cl)->var->cmdline_val.p); + + if(apval){ + if(*apval) + fs_give((void **)apval); + + *apval = cpystr(tmp_20k_buf); + } + + set_current_val((*cl)->var, TRUE, TRUE); + if(decode_sort(ps->VAR_SORT_KEY, &def_sort, &def_sort_rev) != -1){ + ps->def_sort = def_sort; + ps->def_sort_rev = def_sort_rev; + } + + set_radio_pretty_vals(ps, cl); + ps->mangled_body = 1; /* BUG: redraw it all for now? */ + rv = 1; + } + else + q_status_message(SM_ORDER | SM_DING, 3, 6, + "Programmer botch! Unknown radiobutton type."); + + break; + + case MC_EXIT: /* exit */ + rv = config_exit_cmd(flags); + break; + + default : + rv = -1; + break; + } + + if(rv == 1) + exception_override_warning((*cl)->var); + + return(rv); +} + + +/* + * simple yes/no style variable handler + */ +int +yesno_tool(struct pine *ps, int cmd, CONF_S **cl, unsigned int flags) +{ + int rv = 0, yes = 0; + char *pval, **apval; + + pval = PVAL((*cl)->var, ew); + apval = APVAL((*cl)->var, ew); + + switch(cmd){ + case MC_TOGGLE: /* toggle yes to no and back */ + if(fixed_var((*cl)->var, NULL, NULL)){ + if(((*cl)->var->post_user_val.p || (*cl)->var->main_user_val.p) + && want_to(_("Delete old unused personal option setting"), + 'y', 'n', NO_HELP, WT_FLUSH_IN) == 'y'){ + delete_user_vals((*cl)->var); + q_status_message(SM_ORDER, 0, 3, _("Deleted")); + rv = 1; + } + + return(rv); + } + + rv = 1; + yes = ((pval && !strucmp(pval, yesstr)) || + (!pval && (*cl)->var->current_val.p && + !strucmp((*cl)->var->current_val.p, yesstr))); + fs_give((void **)&(*cl)->value); + + if(apval){ + if(*apval) + fs_give((void **)apval); + + if(yes) + *apval = cpystr(nostr); + else + *apval = cpystr(yesstr); + } + + set_current_val((*cl)->var, FALSE, FALSE); + if((*cl)->value) + fs_give((void **)&(*cl)->value); + + (*cl)->value = pretty_value(ps, *cl); + fix_side_effects(ps, (*cl)->var, 0); + + break; + + case MC_EXIT: /* exit */ + rv = config_exit_cmd(flags); + break; + + default : + rv = -1; + break; + } + + return(rv); +} + + +/* + * Manage display of the config/options menu body. + */ +void +update_option_screen(struct pine *ps, OPT_SCREEN_S *screen, Pos *cursor_pos) +{ + int dline, w, save = '\0'; + CONF_S *top_line, *ctmp; + char *value; + unsigned got_width; + int want_width, first_width; + char *saveptr = NULL; + +#ifdef _WINDOWS + int last_selectable; + mswin_beginupdate(); +#endif + if(screen == NULL) + return; + + 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) != NULL) + 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 + - (utf8_width(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(); + } + + value = (ctmp->flags & CF_INHERIT) ? INHERIT : ctmp->value; + if(value){ + char *p; + int i, j; + + memset(tmp_20k_buf, '\0', + (6*ps->ttyo->screen_cols + 1) * sizeof(char)); + if(ctmp == screen->current){ + if(!(ctmp->flags & CF_DOUBLEVAR && ctmp->flags & CF_VAR2)) + StartInverse(); + + if(cursor_pos) + cursor_pos->row = dline + HEADER_ROWS(ps); + } + + if(ctmp->flags & CF_H_LINE) + memset(tmp_20k_buf, '-', + ps->ttyo->screen_cols * sizeof(char)); + + if(ctmp->flags & CF_CENTERED){ + int offset = ps->ttyo->screen_cols/2 + - (utf8_width(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; value[i]; i++){ + if(value[i] == ctrl('I')){ + do + *p++ = ' '; + while((++j) & 0x07); + } + else{ + *p++ = value[i]; + j++; + } + } + + if(ctmp == screen->current && cursor_pos){ + if(ctmp->flags & CF_DOUBLEVAR && ctmp->flags & CF_VAR2) + cursor_pos->col = ctmp->val2offset; + else + cursor_pos->col = ctmp->valoffset; + + if(ctmp->tool == radiobutton_tool +#ifdef ENABLE_LDAP + || ctmp->tool==ldap_radiobutton_tool +#endif + || ctmp->tool==role_radiobutton_tool + || ctmp->tool==checkbox_tool + || (ctmp->tool==color_setting_tool && + ctmp->valoffset != COLOR_INDENT)) + cursor_pos->col++; + } + + if(ctmp->flags & CF_DOUBLEVAR){ + long l; + + p = tmp_20k_buf; + first_width = ctmp->val2offset - ctmp->valoffset - SPACE_BETWEEN_DOUBLEVARS; + if((l=utf8_width(p)) > first_width && first_width >= 0){ + saveptr = utf8_count_forw_width(p, first_width, &got_width); + /* + * got_width != first_width indicates there's a problem + * that should not happen. Ignore it. + */ + if(saveptr){ + save = *saveptr; + *saveptr = '\0'; + } + } + else + save = '\0'; + + /* + * If this is a COLOR_BLOB line we do special coloring. + * The current object inverse hilite is only on the + * checkbox part, the exact format comes from the + * new_color_line function. If we change that we'll have + * to change this to get the coloring right. + */ + if(p[0] == '(' && p[2] == ')' && + p[3] == ' ' && p[4] == ' ' && + (!strncmp(p+5, COLOR_BLOB, COLOR_BLOB_LEN) + || !strncmp(p+5, COLOR_BLOB_TRAN, COLOR_BLOB_LEN) + || !strncmp(p+5, COLOR_BLOB_NORM, COLOR_BLOB_LEN))){ + COLOR_PAIR *lastc = NULL, *newc = NULL; + + MoveCursor(dline+HEADER_ROWS(ps), ctmp->valoffset); + Write_to_screen_n(p, 3); + if(!(ctmp->flags & CF_VAR2) && ctmp == screen->current) + EndInverse(); + + Write_to_screen_n(p+3, 3); + newc = new_color_pair(colorx(CFC_ICOLOR(ctmp)), + colorx(CFC_ICOLOR(ctmp))); + if(newc){ + lastc = pico_get_cur_color(); + (void)pico_set_colorp(newc, PSC_NONE); + free_color_pair(&newc); + } + + Write_to_screen_n(p+6, COLOR_BLOB_LEN-2); + + if(lastc){ + (void)pico_set_colorp(lastc, PSC_NONE); + free_color_pair(&lastc); + } + + Write_to_screen(p+6+COLOR_BLOB_LEN-2); + } + else{ + PutLine0(dline+HEADER_ROWS(ps), ctmp->valoffset, p); + if(!(ctmp->flags & CF_VAR2) && ctmp == screen->current) + EndInverse(); + } + + if(saveptr) + *saveptr = save; + + PutLine0(dline+HEADER_ROWS(ps), + ctmp->val2offset - SPACE_BETWEEN_DOUBLEVARS, + repeat_char(SPACE_BETWEEN_DOUBLEVARS, SPACE)); + + if(l > ctmp->val2offset - ctmp->valoffset && ctmp->val2offset - ctmp->valoffset >= 0) + p = saveptr + SPACE_BETWEEN_DOUBLEVARS; + + if(p > tmp_20k_buf){ + if(ctmp->flags & CF_VAR2 && ctmp == screen->current) + StartInverse(); + + if(p[0] == '(' && p[2] == ')' && + p[3] == ' ' && p[4] == ' ' && + (!strncmp(p+5, COLOR_BLOB, COLOR_BLOB_LEN) + || !strncmp(p+5, COLOR_BLOB_TRAN, COLOR_BLOB_LEN) + || !strncmp(p+5, COLOR_BLOB_NORM, COLOR_BLOB_LEN))){ + COLOR_PAIR *lastc = NULL, *newc = NULL; + + MoveCursor(dline+HEADER_ROWS(ps), ctmp->val2offset); + Write_to_screen_n(p, 3); + if(ctmp->flags & CF_VAR2 && ctmp == screen->current) + EndInverse(); + + Write_to_screen_n(p+3, 3); + newc = new_color_pair(colorx(CFC_ICOLOR(ctmp)), + colorx(CFC_ICOLOR(ctmp))); + if(newc){ + lastc = pico_get_cur_color(); + (void)pico_set_colorp(newc, PSC_NONE); + free_color_pair(&newc); + } + + Write_to_screen_n(p+6, COLOR_BLOB_LEN-2); + + if(lastc){ + (void)pico_set_colorp(lastc, PSC_NONE); + free_color_pair(&lastc); + } + + Write_to_screen(p+6+COLOR_BLOB_LEN-2); + } + else{ + PutLine0(dline+HEADER_ROWS(ps),ctmp->val2offset,p); + if(ctmp->flags & CF_VAR2 && ctmp == screen->current) + EndInverse(); + } + } + } + else{ + char *q, *first_space, *sample, *ptr; + COLOR_PAIR *lastc, *newc; + int invert; + + + if(ctmp->flags & CF_COLORSAMPLE && + pico_usingcolor() && + ((q = strstr(tmp_20k_buf, SAMPLE_LEADER)) || + (q = strstr(tmp_20k_buf, "Color"))) && + (first_space = strindex(q, SPACE)) && + (strstr(value, SAMP1) || + strstr(value, SAMP2))){ + + ptr = tmp_20k_buf; + + /* write out first part */ + *first_space = '\0'; + PutLine0(dline+HEADER_ROWS(ps), ctmp->valoffset, + ptr); + *first_space = SPACE; + ptr = first_space; + + if(ctmp == screen->current) + EndInverse(); + + sample = skip_white_space(ptr); + /* if there's enough room to put some sample up */ + save = *sample; + *sample = '\0'; + w = utf8_width(tmp_20k_buf); + *sample = save; + if(ctmp->valoffset + w < ps->ttyo->screen_cols){ + + sample++; /* for `[' at edge of sample */ + + save = *ptr; + *ptr = '\0'; + w = utf8_width(tmp_20k_buf); + *ptr = save; + + save = *sample; + *sample = '\0'; + /* spaces and bracket before sample1 */ + PutLine0(dline+HEADER_ROWS(ps), ctmp->valoffset+w, ptr); + *sample = save; + + ptr = sample; + + /* then the color sample */ + if(ctmp->var == &ps->vars[V_VIEW_HDR_COLORS]){ + SPEC_COLOR_S *hc, *hcolors; + + lastc = newc = NULL; + + hcolors = + spec_colors_from_varlist(LVAL(ctmp->var, ew), + 0); + for(hc = hcolors, i=0; hc; hc = hc->next, i++) + if(CFC_ICUST(ctmp) == i) + break; + + if(hc && hc->fg && hc->fg[0] && hc->bg && + hc->bg[0]) + newc = new_color_pair(hc->fg, hc->bg); + + if(newc){ + lastc = pico_get_cur_color(); + (void)pico_set_colorp(newc, PSC_NONE); + free_color_pair(&newc); + } + + if(hcolors) + free_spec_colors(&hcolors); + + + /* print out sample1 */ + + save = *ptr; + *ptr = '\0'; + w = utf8_width(tmp_20k_buf); + *ptr = save; + + want_width = MIN(utf8_width(SAMP1)-2, ps->ttyo->screen_cols - w - ctmp->valoffset); + saveptr = utf8_count_forw_width(ptr, want_width, &got_width); + if(saveptr){ + save = *saveptr; + *saveptr = '\0'; + } + + PutLine0(dline+HEADER_ROWS(ps), ctmp->valoffset + w, ptr); + if(saveptr) + *saveptr = save; + + ptr = strindex(ptr, ']'); + + if(lastc){ + (void)pico_set_colorp(lastc, PSC_NONE); + free_color_pair(&lastc); + } + } + else if(ctmp->var == &ps->vars[V_KW_COLORS]){ + KEYWORD_S *kw; + SPEC_COLOR_S *kw_col = NULL; + + lastc = newc = NULL; + + /* find keyword associated with this line */ + for(kw=ps->keywords, i=0; kw; kw=kw->next, i++) + if(CFC_ICUST(ctmp) == i) + break; + + if(kw) + kw_col = + spec_colors_from_varlist(LVAL(ctmp->var,ew), + 0); + + /* color for this keyword */ + if(kw && kw_col + && ((kw->nick && kw->nick[0] + && (newc=hdr_color(kw->nick, NULL, + kw_col))) + || + (kw->kw && kw->kw[0] + && (newc=hdr_color(kw->kw, NULL, + kw_col))))){ + lastc = pico_get_cur_color(); + (void)pico_set_colorp(newc, PSC_NONE); + free_color_pair(&newc); + } + + if(kw_col) + free_spec_colors(&kw_col); + + /* print out sample1 */ + + save = *ptr; + *ptr = '\0'; + w = utf8_width(tmp_20k_buf); + *ptr = save; + + want_width = MIN(utf8_width(SAMP1)-2, ps->ttyo->screen_cols - w - ctmp->valoffset); + saveptr = utf8_count_forw_width(ptr, want_width, &got_width); + if(saveptr){ + save = *saveptr; + *saveptr = '\0'; + } + + PutLine0(dline+HEADER_ROWS(ps), ctmp->valoffset + w, ptr); + if(saveptr) + *saveptr = save; + + ptr = strindex(ptr, ']'); + + if(lastc){ + (void)pico_set_colorp(lastc, PSC_NONE); + free_color_pair(&lastc); + } + } + else{ + lastc = NULL; + invert = 0; + newc = sample_color(ps, ctmp->var); + if(newc){ + if((lastc = pico_get_cur_color()) != NULL) + (void)pico_set_colorp(newc, PSC_NONE); + + free_color_pair(&newc); + } + else if(var_defaults_to_rev(ctmp->var)){ + if((newc = pico_get_rev_color()) != NULL){ + /* + * Note, don't have to free newc. + */ + if((lastc = pico_get_cur_color()) != NULL) + (void)pico_set_colorp(newc, PSC_NONE); + } + else{ + StartInverse(); + invert = 1; + } + } + + if(ctmp->var==&ps->vars[V_SLCTBL_FORE_COLOR] && + + (F_OFF(F_SLCTBL_ITEM_NOBOLD, ps) || + !(PVAL(ctmp->var,ew) && + PVAL(ctmp->var+1,ew)))) + StartBold(); + + /* print out sample1 */ + + save = *ptr; + *ptr = '\0'; + w = utf8_width(tmp_20k_buf); + *ptr = save; + + want_width = MIN(utf8_width(SAMP1)-2, ps->ttyo->screen_cols - w - ctmp->valoffset); + saveptr = utf8_count_forw_width(ptr, want_width, &got_width); + if(saveptr){ + save = *saveptr; + *saveptr = '\0'; + } + + PutLine0(dline+HEADER_ROWS(ps), ctmp->valoffset + w, ptr); + if(saveptr) + *saveptr = save; + + ptr = strindex(ptr, ']'); + + if(ctmp->var==&ps->vars[V_SLCTBL_FORE_COLOR] && + + (F_OFF(F_SLCTBL_ITEM_NOBOLD, ps) || + !(PVAL(ctmp->var,ew) && + PVAL(ctmp->var+1,ew)))) + EndBold(); + + if(lastc){ + (void)pico_set_colorp(lastc, PSC_NONE); + free_color_pair(&lastc); + } + else if(invert) + EndInverse(); + } + + /* + * Finish sample1 with the right bracket. + */ + save = *ptr; + *ptr = '\0'; + w = utf8_width(tmp_20k_buf); + *ptr = save; + if(ctmp->valoffset + w < ps->ttyo->screen_cols){ + save = *(ptr+1); + *(ptr+1) = '\0'; + PutLine0(dline+HEADER_ROWS(ps), ctmp->valoffset + w, ptr); + *(ptr+1) = save; + ptr++; + w++; + } + + /* + * Now check for an exception sample and paint it. + */ + if(ctmp->valoffset + w + SBS + 1 < ps->ttyo->screen_cols && (q = strstr(ptr, SAMPEXC))){ + /* spaces + `[' */ + save = ptr[SBS+1]; + ptr[SBS+1] = '\0'; + PutLine0(dline+HEADER_ROWS(ps), ctmp->valoffset + w, ptr); + ptr[SBS+1] = save; + ptr += (SBS+1); + + /* + * Figure out what color to paint it. + * This only happens with normal variables, + * not with V_VIEW_HDR_COLORS. + */ + lastc = NULL; + invert = 0; + newc = sampleexc_color(ps, ctmp->var); + if(newc){ + if((lastc = pico_get_cur_color()) != NULL) + (void)pico_set_colorp(newc, PSC_NONE); + + free_color_pair(&newc); + } + else if(var_defaults_to_rev(ctmp->var)){ + if((newc = pico_get_rev_color()) != NULL){ + /* + * Note, don't have to free newc. + */ + if((lastc = pico_get_cur_color()) != NULL) + (void)pico_set_colorp(newc, PSC_NONE); + } + else{ + StartInverse(); + invert = 1; + } + } + + if(ctmp->var==&ps->vars[V_SLCTBL_FORE_COLOR] && + + (F_OFF(F_SLCTBL_ITEM_NOBOLD, ps) || + !(PVAL(ctmp->var,Post) && + PVAL(ctmp->var+1,Post)))) + StartBold(); + + /* sample2 */ + save = *ptr; + *ptr = '\0'; + w = utf8_width(tmp_20k_buf); + *ptr = save; + + want_width = MIN(utf8_width(SAMPEXC)-2, ps->ttyo->screen_cols - w - ctmp->valoffset); + saveptr = utf8_count_forw_width(ptr, want_width, &got_width); + if(saveptr){ + save = *saveptr; + *saveptr = '\0'; + } + + PutLine0(dline+HEADER_ROWS(ps), ctmp->valoffset + w, ptr); + if(saveptr) + *saveptr = save; + + ptr = strindex(ptr, ']'); + + /* turn off bold and color */ + if(ctmp->var==&ps->vars[V_SLCTBL_FORE_COLOR] && + + (F_OFF(F_SLCTBL_ITEM_NOBOLD, ps) || + !(PVAL(ctmp->var,Post) && + PVAL(ctmp->var+1,Post)))) + EndBold(); + + if(lastc){ + (void)pico_set_colorp(lastc, PSC_NONE); + free_color_pair(&lastc); + } + else if(invert) + EndInverse(); + + /* + * Finish sample2 with the right bracket. + */ + save = *ptr; + *ptr = '\0'; + w = utf8_width(tmp_20k_buf); + *ptr = save; + if(ctmp->valoffset + w < ps->ttyo->screen_cols){ + save = *(ptr+1); + *(ptr+1) = '\0'; + PutLine0(dline+HEADER_ROWS(ps), ctmp->valoffset + w, ptr); + *(ptr+1) = save; + ptr++; + w++; + } + } + + /* paint rest of the line if there is any left */ + if(ctmp->valoffset + w < ps->ttyo->screen_cols && *ptr){ + want_width = ps->ttyo->screen_cols - w - ctmp->valoffset; + saveptr = utf8_count_forw_width(ptr, want_width, &got_width); + if(saveptr){ + save = *saveptr; + *saveptr = '\0'; + } + + PutLine0(dline+HEADER_ROWS(ps), ctmp->valoffset + w, ptr); + if(saveptr) + *saveptr = save; + } + } + } + else{ + w = utf8_width(tmp_20k_buf); + want_width = ps->ttyo->screen_cols - ctmp->valoffset; + if(w > want_width){ + saveptr = utf8_count_forw_width(tmp_20k_buf, want_width, &got_width); + if(saveptr) + *saveptr = '\0'; + } + + 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(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)){ + + snprintf(line, sizeof(line), "%*s%s", ctmp->varoffset, "", + ctmp->varname); + line[sizeof(line)-1] = '\0'; + print_text(line); + so_far = ctmp->varoffset + utf8_width(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); + snprintf(line, sizeof(line), "%*s%s\n", spaces, "", tmp_20k_buf); + line[sizeof(line)-1] = '\0'; + print_text(line); + } + } + + close_printer(); + } +} + + + +/* + * + */ +void +option_screen_redrawer(void) +{ + ps_global->mangled_body = 1; + update_option_screen(ps_global, opt_screen, (Pos *)NULL); +} + + + +/* + * pretty_value - given the line, return an + * alloc'd string for line's value... + */ +char * +pretty_value(struct pine *ps, CONF_S *cl) +{ + struct variable *v; + + v = cl->var; + + if(v == &ps->vars[V_FEATURE_LIST]) + return(checkbox_pretty_value(ps, cl)); + else if(standard_radio_var(ps, v) || v == startup_ptr) + return(radio_pretty_value(ps, cl)); + else if(v == &ps->vars[V_SORT_KEY]) + return(sort_pretty_value(ps, cl)); + else if(v == &ps->vars[V_SIGNATURE_FILE]) + return(sigfile_pretty_value(ps, cl)); + else if(v == &ps->vars[V_USE_ONLY_DOMAIN_NAME]) + return(yesno_pretty_value(ps, cl)); + else if(color_holding_var(ps, v)) + return(color_pretty_value(ps, cl)); + else + return(text_pretty_value(ps, cl)); +} + + +char * +text_pretty_value(struct pine *ps, CONF_S *cl) +{ + char tmp[6*MAX_SCREEN_COLS+20], *pvalnorm, **lvalnorm, *pvalexc, **lvalexc; + char *p, *pval, **lval, lastchar = '\0'; + int editing_except, fixed, uvalset, uvalposlen; + unsigned got_width; + int comments, except_set, avail_width; + int norm_with_except = 0, norm_with_except_inherit = 0; + int inherit_line = 0; + + editing_except = (ew == ps_global->ew_for_except_vars); + fixed = cl->var->is_fixed; + if((ps_global->ew_for_except_vars != Main) && (ew == Main)) + norm_with_except++; /* editing normal and except config exists */ + + if(cl->var->is_list){ + lvalnorm = LVAL(cl->var, Main); + lvalexc = LVAL(cl->var, ps_global->ew_for_except_vars); + if(editing_except){ + uvalset = lvalexc != NULL; + uvalposlen = uvalset && lvalexc[0] && lvalexc[0][0]; + lval = lvalexc; + } + else{ + uvalset = lvalnorm != NULL; + uvalposlen = uvalset && lvalnorm[0] && lvalnorm[0][0]; + lval = lvalnorm; + } + + except_set = lvalexc != NULL; + comments = cl->var->current_val.l != NULL; + if(norm_with_except && except_set && lvalexc[0] && + !strcmp(lvalexc[0],INHERIT)) + norm_with_except_inherit++; + + if(uvalset && !strcmp(lval[0], INHERIT)){ + if(cl->varmem == 0){ + inherit_line++; + comments = 0; + } + } + + /* only add extra comments on last member of list */ + if(uvalset && !inherit_line && lval && lval[cl->varmem] && + lval[cl->varmem + 1]) + comments = 0; + } + else{ + pvalnorm = PVAL(cl->var, Main); + pvalexc = PVAL(cl->var, ps_global->ew_for_except_vars); + if(editing_except){ + uvalset = pvalexc != NULL; + uvalposlen = uvalset && *pvalexc; + pval = pvalexc; + } + else{ + uvalset = pvalnorm != NULL; + uvalposlen = uvalset && *pvalnorm; + pval = pvalnorm; + } + + except_set = pvalexc != NULL; + comments = cl->var->current_val.p != NULL; + } + + memset(tmp, 0, sizeof(tmp)); + p = tmp; + *p = '\0'; + + avail_width = ps->ttyo->screen_cols - cl->valoffset; + + if(fixed || !uvalset || !uvalposlen){ + p += utf8_to_width(p, "<", sizeof(tmp)-(p-tmp), avail_width, &got_width); + avail_width -= got_width; + } + + if(fixed){ + p += utf8_to_width(p, _(fixed_val), sizeof(tmp)-(p-tmp), avail_width, &got_width); + avail_width -= got_width; + } + else if(!uvalset){ + p += utf8_to_width(p, _(no_val), sizeof(tmp)-(p-tmp), avail_width, &got_width); + avail_width -= got_width; + } + else if(!uvalposlen){ + p += utf8_to_width(p, _(empty_val), sizeof(tmp)-(p-tmp), avail_width, &got_width); + avail_width -= got_width; + } + else if(inherit_line){ + p += utf8_to_width(p, INHERIT, sizeof(tmp)-(p-tmp), avail_width, &got_width); + avail_width -= got_width; + } + else{ + if(cl->var->is_list){ + p += utf8_to_width(p, lval[cl->varmem], sizeof(tmp)-(p-tmp), avail_width, &got_width); + avail_width -= got_width; + } + else{ + p += utf8_to_width(p, pval, sizeof(tmp)-(p-tmp), avail_width, &got_width); + avail_width -= got_width; + } + } + + if(comments && (fixed || !uvalset || (norm_with_except && except_set))){ + if(fixed || !uvalset){ + p += utf8_to_width(p, ": using ", sizeof(tmp)-(p-tmp), avail_width, &got_width); + avail_width -= got_width; + } + + if(norm_with_except && except_set){ + if(!uvalset){ + p += utf8_to_width(p, "exception ", sizeof(tmp)-(p-tmp), avail_width, &got_width); + avail_width -= got_width; + } + else if(!fixed){ + if(!uvalposlen){ + p += utf8_to_width(p, ": ", sizeof(tmp)-(p-tmp), avail_width, &got_width); + avail_width -= got_width; + } + else{ + p += utf8_to_width(p, " (", sizeof(tmp)-(p-tmp), avail_width, &got_width); + avail_width -= got_width; + } + + if(norm_with_except_inherit){ + p += utf8_to_width(p, "added to by exception ", sizeof(tmp)-(p-tmp), avail_width, &got_width); + avail_width -= got_width; + } + else{ + p += utf8_to_width(p, "overridden by exception ", sizeof(tmp)-(p-tmp), avail_width, &got_width); + avail_width -= got_width; + } + } + } + + if(avail_width >= 7){ + if(cl->var == &ps_global->vars[V_POST_CHAR_SET]){ + p += utf8_to_width(p, "most specific (see help)", sizeof(tmp)-(p-tmp), avail_width, &got_width); + avail_width -= got_width; + } + else{ + sstrncpy(&p, "\"", sizeof(tmp)-(p-tmp)); + avail_width--; + if(cl->var->is_list){ + char **the_list; + + the_list = cl->var->current_val.l; + + if(norm_with_except && except_set) + the_list = lvalexc; + + if(the_list && the_list[0] && !strcmp(the_list[0], INHERIT)) + the_list++; + + for(lval = the_list; avail_width-(p-tmp) > 0 && *lval; lval++){ + if(lval != the_list){ + p += utf8_to_width(p, ",", sizeof(tmp)-(p-tmp), avail_width, &got_width); + avail_width -= got_width; + } + + p += utf8_to_width(p, *lval, sizeof(tmp)-(p-tmp), avail_width, &got_width); + avail_width -= got_width; + } + } + else{ + p += utf8_to_width(p, cl->var->current_val.p, sizeof(tmp)-(p-tmp), avail_width, &got_width); + avail_width -= got_width; + } + + if(p-tmp+2 < sizeof(tmp)){ + *p++ = '\"'; + *p = '\0'; + } + } + } + else if(*(p-1) == SPACE) + *--p = '\0'; + } + + tmp[sizeof(tmp)-1] = '\0'; + + if(fixed || !uvalset || !uvalposlen) + lastchar = '>'; + else if(comments && norm_with_except && except_set) + lastchar = ')'; + + if(lastchar){ + if(p-tmp+2 < sizeof(tmp)){ + *p++ = lastchar; + *p = '\0'; + } + } + + tmp[sizeof(tmp)-1] = '\0'; + avail_width = ps->ttyo->screen_cols - cl->valoffset; + + if(utf8_width(tmp) < avail_width) + snprintf(tmp+strlen(tmp), sizeof(tmp)-strlen(tmp), "%*s", avail_width-utf8_width(tmp), ""); + + tmp[sizeof(tmp)-1] = '\0'; + + return(cpystr(tmp)); +} + + +char * +checkbox_pretty_value(struct pine *ps, CONF_S *cl) +{ + char tmp[6*MAXPATH]; + char *comment = NULL; + int indent, x, screen_width, need; + int longest_featname, longest_comment; + int nwidcomm; /* name width with comment */ + int nwidnocomm; /* and without comment */ + FEATURE_S *feature; + + screen_width = (ps && ps->ttyo) ? ps->ttyo->screen_cols : 80; + tmp[0] = '\0'; + + longest_featname = longest_feature_name(); + longest_comment = longest_feature_comment(ps, ew); + indent = feature_indent(); + + nwidcomm = longest_featname; + nwidnocomm = longest_featname + 2 + longest_comment; + + if((need = (indent + 5 + longest_featname + 2 + longest_comment) - screen_width) > 0){ + if(need < 10){ + nwidcomm -= need; + nwidnocomm -= need; + } + else{ + longest_comment = 0; + nwidnocomm = longest_featname; + } + } + + feature = feature_list(cl->varmem); + + x = feature_gets_an_x(ps, cl->var, feature, &comment, ew); + + if(longest_comment && comment && *comment){ + utf8_snprintf(tmp, sizeof(tmp), "[%c] %-*.*w %-*.*w", x ? 'X' : ' ', + nwidcomm, nwidcomm, + pretty_feature_name(feature->name, nwidcomm), + longest_comment, longest_comment, comment ? comment : ""); + } + else{ + utf8_snprintf(tmp, sizeof(tmp), "[%c] %-*.*w", x ? 'X' : ' ', + nwidnocomm, nwidnocomm, + pretty_feature_name(feature->name, nwidnocomm)); + } + + return(cpystr(tmp)); +} + + +int +longest_feature_name(void) +{ + static int lv = -1; + int i, j; + FEATURE_S *feature; + + if(lv < 0){ + for(lv = 0, i = 0; (feature = feature_list(i)); i++) + if(feature_list_section(feature) + && lv < (j = utf8_width(pretty_feature_name(feature->name, -1)))) + lv = j; + + lv = MIN(lv, 100); + } + + return(lv); +} + + +int +feature_indent(void) +{ + return(6); +} + + +char * +yesno_pretty_value(struct pine *ps, CONF_S *cl) +{ + char tmp[6*MAXPATH], *pvalnorm, *pvalexc; + char *p, *pval, lastchar = '\0'; + int editing_except, fixed, norm_with_except, uvalset; + int curval, except_set; + + editing_except = (ew == ps_global->ew_for_except_vars); + fixed = cl->var->is_fixed; + if((ps_global->ew_for_except_vars == Main) || + (ew == ps_global->ew_for_except_vars)) + norm_with_except = 0; + else + norm_with_except = 1; /* editing normal and except config exists */ + + pvalnorm = PVAL(cl->var, Main); + pvalexc = PVAL(cl->var, ps_global->ew_for_except_vars); + if(editing_except){ + uvalset = (pvalexc != NULL && + (!strucmp(pvalexc,yesstr) || !strucmp(pvalexc,nostr))); + pval = pvalexc; + } + else{ + uvalset = (pvalnorm != NULL && + (!strucmp(pvalnorm,yesstr) || !strucmp(pvalnorm,nostr))); + pval = pvalnorm; + } + + except_set = (pvalexc != NULL && + (!strucmp(pvalexc,yesstr) || !strucmp(pvalexc,nostr))); + curval = (cl->var->current_val.p != NULL && + (!strucmp(cl->var->current_val.p,yesstr) || + !strucmp(cl->var->current_val.p,nostr))); + + p = tmp; + *p = '\0'; + + if(fixed || !uvalset) + sstrncpy(&p, "<", sizeof(tmp)-(p-tmp)); + + if(fixed) + sstrncpy(&p, _(fixed_val), sizeof(tmp)-(p-tmp)); + else if(!uvalset) + sstrncpy(&p, _(no_val), sizeof(tmp)-(p-tmp)); + else if(!strucmp(pval, yesstr)) + sstrncpy(&p, yesstr, sizeof(tmp)-(p-tmp)); + else + sstrncpy(&p, nostr, sizeof(tmp)-(p-tmp)); + + if(curval && (fixed || !uvalset || (norm_with_except && except_set))){ + if(fixed || !uvalset) + sstrncpy(&p, ": using ", sizeof(tmp)-(p-tmp)); + + if(norm_with_except && except_set){ + if(!uvalset) + sstrncpy(&p, "exception ", sizeof(tmp)-(p-tmp)); + else if(!fixed){ + sstrncpy(&p, " (", sizeof(tmp)-(p-tmp)); + sstrncpy(&p, "overridden by exception ", sizeof(tmp)-(p-tmp)); + } + } + + sstrncpy(&p, "\"", sizeof(tmp)-(p-tmp)); + sstrncpy(&p, !strucmp(cl->var->current_val.p,yesstr) ? yesstr : nostr, sizeof(tmp)-(p-tmp)); + sstrncpy(&p, "\"", sizeof(tmp)-(p-tmp)); + } + + if(fixed || !uvalset) + lastchar = '>'; + else if(curval && norm_with_except && except_set) + lastchar = ')'; + + if(lastchar && sizeof(tmp)-(p-tmp) > 1){ + *p++ = lastchar; + *p = '\0'; + } + + tmp[sizeof(tmp)-1] = '\0'; + + if(utf8_width(tmp) < ps->ttyo->screen_cols - cl->valoffset) + snprintf(tmp+strlen(tmp), sizeof(tmp)-strlen(tmp), + "%*s", ps->ttyo->screen_cols - cl->valoffset - utf8_width(tmp), ""); + + tmp[sizeof(tmp)-1] = '\0'; + + return(cpystr(tmp)); +} + + +char * +radio_pretty_value(struct pine *ps, CONF_S *cl) +{ + char tmp[6*MAXPATH]; + char *pvalnorm, *pvalexc, *pval; + int editing_except_which_isnt_normal, editing_normal_which_isnt_except; + int fixed, is_set_for_this_level = 0, is_the_one, the_exc_one; + int i, j, lv = 0; + NAMEVAL_S *rule = NULL, *f; + PTR_TO_RULEFUNC rulefunc; + struct variable *v; + + tmp[0] = '\0'; + v = cl->var; + + editing_except_which_isnt_normal = (ew == ps_global->ew_for_except_vars && + ew != Main); + editing_normal_which_isnt_except = (ew == Main && + ew != ps_global->ew_for_except_vars); + fixed = cl->var->is_fixed; + pvalnorm = PVAL(v, Main); + pvalexc = PVAL(v, ps_global->ew_for_except_vars); + + rulefunc = rulefunc_from_var(ps, v); + rule = rulefunc ? (*rulefunc)(cl->varmem) : NULL; + + /* find longest name */ + if(rulefunc) + for(lv = 0, i = 0; (f = (*rulefunc)(i)); i++) + if(lv < (j = utf8_width(f->name))) + lv = j; + + lv = MIN(lv, 100); + + if(editing_except_which_isnt_normal) + pval = pvalexc; + else + pval = pvalnorm; + + if(pval) + is_set_for_this_level++; + + if(fixed){ + pval = v->fixed_val.p; + is_the_one = (pval && !strucmp(pval, S_OR_L(rule))); + + utf8_snprintf(tmp, sizeof(tmp), "(%c) %-*.*w%s", + is_the_one ? R_SELD : ' ', + lv, lv, rule->name, is_the_one ? " (value is fixed)" : ""); + } + else if(is_set_for_this_level){ + is_the_one = (pval && !strucmp(pval, S_OR_L(rule))); + the_exc_one = (editing_normal_which_isnt_except && pvalexc && + !strucmp(pvalexc, S_OR_L(rule))); + utf8_snprintf(tmp, sizeof(tmp), "(%c) %-*.*w%s", + is_the_one ? R_SELD : ' ', + lv, lv, rule->name, + (!is_the_one && the_exc_one) ? " (value set in exceptions)" : + (is_the_one && the_exc_one) ? " (also set in exceptions)" : + (is_the_one && + editing_normal_which_isnt_except && + pvalexc && + !the_exc_one) ? " (overridden by exceptions)" : + ""); + } + else{ + if(pvalexc){ + is_the_one = !strucmp(pvalexc, S_OR_L(rule)); + utf8_snprintf(tmp, sizeof(tmp), "(%c) %-*.*w%s", + is_the_one ? R_SELD : ' ', + lv, lv, rule->name, + is_the_one ? " (value set in exceptions)" : ""); + } + else{ + pval = v->current_val.p; + is_the_one = (pval && !strucmp(pval, S_OR_L(rule))); + utf8_snprintf(tmp, sizeof(tmp), "(%c) %-*.*w%s", + is_the_one ? R_SELD : ' ', + lv, lv, rule->name, + is_the_one ? ((editing_except_which_isnt_normal && pvalnorm) ? " (default from regular config)" : " (default)") : ""); + } + } + + tmp[sizeof(tmp)-1] = '\0'; + + return(cpystr(tmp)); +} + + +char * +sigfile_pretty_value(struct pine *ps, CONF_S *cl) +{ + if(cl && cl->var == &ps->vars[V_SIGNATURE_FILE] && + cl->prev && cl->prev->var == &ps->vars[V_LITERAL_SIG]){ + if(cl->prev->var->current_val.p){ + cl->flags |= CF_NOSELECT; /* side effect */ + return(cpystr(_(""))); + } + else{ + cl->flags &= ~CF_NOSELECT; + return(text_pretty_value(ps, cl)); + } + } + else + return(cpystr("")); +} + + +char * +color_pretty_value(struct pine *ps, CONF_S *cl) +{ + char tmp[6*MAXPATH]; + char *p, *q; + struct variable *v; + int is_index; + + tmp[0] = '\0'; + v = cl->var; + + if(v && color_holding_var(ps, v) && + (p=srchstr(v->name, "-foreground-color"))){ + + is_index = !struncmp(v->name, "index-", 6); + + q = sampleexc_text(ps, v); + utf8_snprintf(tmp, sizeof(tmp), "%c%.*s %sColor%*.50s %.20w%*s%.20w%.20w", + islower((unsigned char)v->name[0]) + ? toupper((unsigned char)v->name[0]) + : v->name[0], + MIN(p-v->name-1,30), v->name+1, + is_index ? "Symbol " : "", + MAX(EQ_COL - COLOR_INDENT -1 - MIN(p-v->name-1,30) + - 6 - (is_index ? 7 : 0) - 1,0), "", + sample_text(ps,v), *q ? SBS : 0, "", q, + color_parenthetical(v)); + } + + tmp[sizeof(tmp)-1] = '\0'; + + return(cpystr(tmp)); +} + + +char * +sort_pretty_value(struct pine *ps, CONF_S *cl) +{ + return(generalized_sort_pretty_value(ps, cl, 1)); +} + + +char * +generalized_sort_pretty_value(struct pine *ps, CONF_S *cl, int default_ok) +{ + char tmp[6*MAXPATH]; + char *pvalnorm, *pvalexc, *pval; + int editing_except_which_isnt_normal, editing_normal_which_isnt_except; + int fixed, is_set_for_this_level = 0, is_the_one, the_exc_one; + int i, j, lv = 0; + struct variable *v; + SortOrder line_sort, var_sort, exc_sort; + int line_sort_rev, var_sort_rev, exc_sort_rev; + + tmp[0] = '\0'; + v = cl->var; + + editing_except_which_isnt_normal = (ew == ps_global->ew_for_except_vars && + ew != Main); + editing_normal_which_isnt_except = (ew == Main && + ew != ps_global->ew_for_except_vars); + fixed = cl->var->is_fixed; + pvalnorm = PVAL(v, Main); + pvalexc = PVAL(v, ps_global->ew_for_except_vars); + + /* find longest value's name */ + for(lv = 0, i = 0; ps->sort_types[i] != EndofList; i++) + if(lv < (j = utf8_width(sort_name(ps->sort_types[i])))) + lv = j; + + lv = MIN(lv, 100); + + if(editing_except_which_isnt_normal) + pval = pvalexc; + else + pval = pvalnorm; + + if(pval) + is_set_for_this_level++; + + /* the config line we're talking about */ + if(cl->varmem >= 0){ + line_sort_rev = cl->varmem >= (short)EndofList; + line_sort = (SortOrder)(cl->varmem - (line_sort_rev * EndofList)); + } + + if(cl->varmem < 0){ + utf8_snprintf(tmp, sizeof(tmp), "(%c) %-*w", + (pval == NULL) ? R_SELD : ' ', + lv, "Default"); + } + else if(fixed){ + pval = v->fixed_val.p; + decode_sort(pval, &var_sort, &var_sort_rev); + is_the_one = (var_sort_rev == line_sort_rev && var_sort == line_sort); + + utf8_snprintf(tmp, sizeof(tmp), "(%c) %s%-*w%*s%s", + is_the_one ? R_SELD : ' ', + line_sort_rev ? "Reverse " : "", + lv, sort_name(line_sort), + line_sort_rev ? 0 : 8, "", + is_the_one ? " (value is fixed)" : ""); + } + else if(is_set_for_this_level){ + decode_sort(pval, &var_sort, &var_sort_rev); + is_the_one = (var_sort_rev == line_sort_rev && var_sort == line_sort); + decode_sort(pvalexc, &exc_sort, &exc_sort_rev); + the_exc_one = (editing_normal_which_isnt_except && pvalexc && + exc_sort_rev == line_sort_rev && exc_sort == line_sort); + utf8_snprintf(tmp, sizeof(tmp), "(%c) %s%-*w%*s%s", + is_the_one ? R_SELD : ' ', + line_sort_rev ? "Reverse " : "", + lv, sort_name(line_sort), + line_sort_rev ? 0 : 8, "", + (!is_the_one && the_exc_one) ? " (value set in exceptions)" : + (is_the_one && the_exc_one) ? " (also set in exceptions)" : + (is_the_one && + editing_normal_which_isnt_except && + pvalexc && + !the_exc_one) ? " (overridden by exceptions)" : + ""); + } + else{ + if(pvalexc){ + decode_sort(pvalexc, &exc_sort, &exc_sort_rev); + is_the_one = (exc_sort_rev == line_sort_rev && + exc_sort == line_sort); + utf8_snprintf(tmp, sizeof(tmp), "( ) %s%-*w%*s%s", + line_sort_rev ? "Reverse " : "", + lv, sort_name(line_sort), + line_sort_rev ? 0 : 8, "", + is_the_one ? " (value set in exceptions)" : ""); + } + else{ + pval = v->current_val.p; + decode_sort(pval, &var_sort, &var_sort_rev); + is_the_one = ((pval || default_ok) && + var_sort_rev == line_sort_rev && + var_sort == line_sort); + utf8_snprintf(tmp, sizeof(tmp), "(%c) %s%-*w%*s%s", + is_the_one ? R_SELD : ' ', + line_sort_rev ? "Reverse " : "", + lv, sort_name(line_sort), + line_sort_rev ? 0 : 8, "", + is_the_one ? ((editing_except_which_isnt_normal && pvalnorm) ? " (default from regular config)" : " (default)") : ""); + } + } + + return(cpystr(tmp)); +} + + +COLOR_PAIR * +sample_color(struct pine *ps, struct variable *v) +{ + COLOR_PAIR *cp = NULL; + char *pvalefg, *pvalebg; + char *pvalmfg, *pvalmbg; + + pvalefg = PVAL(v, ew); + pvalebg = PVAL(v+1, ew); + pvalmfg = PVAL(v, Main); + pvalmbg = PVAL(v+1, Main); + if(v && color_holding_var(ps, v) && + srchstr(v->name, "-foreground-color")){ + if(pvalefg && pvalefg[0] && pvalebg && pvalebg[0]) + cp = new_color_pair(pvalefg, pvalebg); + else if(ew == Post && pvalmfg && pvalmfg[0] && pvalmbg && pvalmbg[0]) + cp = new_color_pair(pvalmfg, pvalmbg); + else if(v->global_val.p && v->global_val.p[0] && + (v+1)->global_val.p && (v+1)->global_val.p[0]) + cp = new_color_pair(v->global_val.p, (v+1)->global_val.p); + } + + return(cp); +} + + +COLOR_PAIR * +sampleexc_color(struct pine *ps, struct variable *v) +{ + COLOR_PAIR *cp = NULL; + char *pvalfg, *pvalbg; + + pvalfg = PVAL(v, Post); + pvalbg = PVAL(v+1, Post); + if(v && color_holding_var(ps, v) && + srchstr(v->name, "-foreground-color") && + pvalfg && pvalfg[0] && pvalbg && pvalbg[0]) + cp = new_color_pair(pvalfg, pvalbg); + + return(cp); +} + + +void +clear_feature(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 +toggle_feature_bit(struct pine *ps, int index, struct variable *var, CONF_S *cl, int just_flip_value) +{ + FEATURE_S *f; + int og, on_before; + char *p, **vp; + + 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; + } + } + + on_before = F_ON(f->id, ps); + + toggle_feature(ps, var, f, just_flip_value, ew); + + /* + * Handle any alpine-specific features that need attention here. Features + * that aren't alpine-specific should be handled in toggle_feature instead. + */ + if(on_before != F_ON(f->id, ps)) + switch(f->id){ + case F_CMBND_ABOOK_DISP : + addrbook_reset(); + 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_SHOW_SORT : + ps->mangled_header = 1; + break; + + case F_BLANK_KEYMENU : + if(F_ON(f->id, ps)){ + FOOTER_ROWS(ps) = 1; + ps->mangled_body = 1; + } + else{ + FOOTER_ROWS(ps) = 3; + ps->mangled_footer = 1; + } + + clearfooter(ps); + break; + + case F_ENABLE_INCOMING : + q_status_message(SM_ORDER | SM_DING, 3, 4, + "Folder List changes will take effect your next Alpine session."); + break; + +#ifdef _WINDOWS + case F_SHOW_CURSOR : + mswin_showcaret(F_ON(f->id,ps)); + break; + + case F_ENABLE_TRAYICON : + mswin_trayicon(F_ON(f->id,ps)); + break; +#endif + +#if !defined(DOS) && !defined(OS2) + case F_ALLOW_TALK : + if(F_ON(f->id, ps)) + allow_talk(ps); + else + disallow_talk(ps); + + break; +#endif + + case F_PASS_CONTROL_CHARS : + ps->pass_ctrl_chars = F_ON(F_PASS_CONTROL_CHARS,ps_global) ? 1 : 0; + break; + + case F_PASS_C1_CONTROL_CHARS : + ps->pass_c1_ctrl_chars = F_ON(F_PASS_C1_CONTROL_CHARS,ps_global) ? 1 : 0; + break; + +#ifdef MOUSE + case F_ENABLE_MOUSE : + if(F_ON(f->id, ps)){ + init_mouse(); + if(!mouseexist()) + q_status_message(SM_ORDER | SM_DING, 3, 4, + "Mouse tracking still off ($DISPLAY variable set?)"); + } + else + end_mouse(); + + break; +#endif + } + + if(just_flip_value){ + if(cl->value && cl->value[0]) + cl->value[1] = (cl->value[1] == ' ') ? 'X' : ' '; + } + else{ + /* + * This fork is only called from the checkbox_tool, which has + * varmem set to index correctly and cl->var set correctly. + */ + if(cl->value) + fs_give((void **)&cl->value); + + cl->value = pretty_value(ps, cl); + } +} + + +/* + * 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(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(CONF_S **p) +{ + CONF_S *q; + + /* + * Be careful. We need this line because the + * q->prev->next = ... + * may change q itself if &q == &q->prev->next. + * Then the use of q in the next line is wrong. + * That's what happens if we pass in the address of + * some ->next and use *p directly instead of q. + */ + q = *p; + + if(q){ + /* Yank it from the linked list */ + if(q->prev) + q->prev->next = q->next; + + if(q->next) + q->next->prev = q->prev; + + /* Then free up it's memory */ + q->prev = q->next = NULL; + free_conflines(&q); + } +} + + +/* + * + */ +void +free_conflines(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(CONF_S *p) +{ + while(p && p->prev) + p = p->prev; + + return(p); +} + + +/* + * First selectable confline. + */ +CONF_S * +first_sel_confline(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(CONF_S *p) +{ + while(p && p->next) + p = p->next; + + return(p); +} + + +/* + * + */ +int +fixed_var(struct variable *v, char *action, char *name) +{ + char **lval; + + if(v && v->is_fixed + && (!v->is_list + || ((lval=v->fixed_val.l) && lval[0] + && strcmp(INHERIT, lval[0]) != 0))){ + q_status_message2(SM_ORDER, 3, 3, + "Can't %s sys-admin defined %s.", + action ? action : "change", name ? name : "value"); + return(1); + } + + return(0); +} + + +void +exception_override_warning(struct variable *v) +{ + char **lval; + + /* if exceptions config file exists and we're not editing it */ + if(v && (ps_global->ew_for_except_vars != Main) && (ew == Main)){ + if((!v->is_list && PVAL(v, ps_global->ew_for_except_vars)) || + (v->is_list && (lval=LVAL(v, ps_global->ew_for_except_vars)) && + lval[0] && strcmp(INHERIT, lval[0]) != 0)) + q_status_message1(SM_ORDER, 3, 3, + _("Warning: \"%s\" is overridden in your exceptions configuration"), + v->name); + } +} + + +void +offer_to_fix_pinerc(struct pine *ps) +{ + struct variable *v; + char prompt[300]; + char *p, *q; + char **list; + char **list_fixed; + int rv = 0, write_main = 0, write_post = 0; + int i, k, j, need, exc; + char *clear = ": delete it"; + char ***plist; + + dprint((4, "offer_to_fix_pinerc()\n")); + + ps->fix_fixed_warning = 0; /* so we only ask first time */ + + if(ps->readonly_pinerc) + return; + + set_titlebar(_("FIXING PINERC"), ps->mail_stream, + ps->context_current, + ps->cur_folder, ps->msgmap, 1, FolderName, 0, 0, NULL); + + if(want_to(_("Some of your options conflict with site policy. Investigate"), + 'y', 'n', NO_HELP, WT_FLUSH_IN) != 'y') + return; + +/* space want_to requires in addition to the string you pass in */ +#define WANTTO_SPACE 6 + need = WANTTO_SPACE + utf8_width(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->post_user_val.l || v->main_user_val.l)){ + char **active_list; + + active_list = v->post_user_val.l ? v->post_user_val.l + : v->main_user_val.l; + if(*active_list){ + snprintf(prompt, sizeof(prompt), _("Your setting for %s is "), v->name); + prompt[sizeof(prompt)-1] = '\0'; + p = prompt + strlen(prompt); + for(i = 0; active_list[i]; i++){ + if(utf8_width(prompt) > ps->ttyo->screen_cols - need) + break; + if(i && sizeof(prompt)-(p-prompt) > 0) + *p++ = ','; + + sstrncpy(&p, active_list[i], sizeof(prompt)-(p-prompt)); + if(sizeof(prompt)-(p-prompt) > 0) + *p = '\0'; + + prompt[sizeof(prompt)-1] = '\0'; + } + + if(sizeof(prompt)-(p-prompt) > 0) + *p = '\0'; + } + else + snprintf(prompt, sizeof(prompt), _("Your setting for %s is %s"), v->name, _(empty_val2)); + } + else{ + if(v->post_user_val.p || v->main_user_val.p){ + char *active_var; + + active_var = v->post_user_val.p ? v->post_user_val.p + : v->main_user_val.p; + if(*active_var){ + snprintf(prompt, sizeof(prompt), _("Your setting for %s is %s"), + v->name, active_var); + } + else{ + snprintf(prompt, sizeof(prompt), _("Your setting for %s is %s"), + v->name, _(empty_val2)); + } + } + } + + prompt[sizeof(prompt)-1] = '\0'; + + if(*prompt){ + if(utf8_width(prompt) > ps->ttyo->screen_cols - need) + (void) utf8_truncate(prompt, ps->ttyo->screen_cols - need); + + (void) strncat(prompt, clear, sizeof(prompt)-strlen(prompt)-1); + prompt[sizeof(prompt)-1] = '\0'; + if(want_to(prompt, 'y', 'n', NO_HELP, WT_NORM) == 'y'){ + if(v->is_list){ + if(v->main_user_val.l) + write_main++; + if(v->post_user_val.l) + write_post++; + } + else{ + if(v->main_user_val.p) + write_main++; + if(v->post_user_val.p) + write_post++; + } + + if(delete_user_vals(v)) + rv++; + } + } + } + + + /* + * As always, feature-list has to be handled separately. + */ + exc = (ps->ew_for_except_vars != Main); + v = &ps->vars[V_FEATURE_LIST]; + list_fixed = v->fixed_val.l; + + for(j = 0; j < 2; j++){ + plist = (j==0) ? &v->main_user_val.l : &v->post_user_val.l; + list = *plist; + if(list){ + for(i = 0; list[i]; i++){ + p = list[i]; + if(!struncmp(p, "no-", 3)) + p += 3; + for(k = 0; list_fixed && list_fixed[k]; k++){ + q = list_fixed[k]; + if(!struncmp(q, "no-", 3)) + q += 3; + if(!strucmp(q, p) && strucmp(list[i], list_fixed[k])){ + snprintf(prompt, sizeof(prompt), "Your %s is %s%s, fixed value is %s", + p, p == list[i] ? _("ON") : _("OFF"), + exc ? ((plist == &v->main_user_val.l) ? "" + : " in postload-config") + : "", + q == list_fixed[k] ? _("ON") : _("OFF")); + + prompt[sizeof(prompt)-1] = '\0'; + if(utf8_width(prompt) > ps->ttyo->screen_cols - need) + (void) utf8_truncate(prompt, ps->ttyo->screen_cols - need); + + (void) strncat(prompt, clear, sizeof(prompt)-strlen(prompt)-1); + prompt[sizeof(prompt)-1] = '\0'; + if(want_to(prompt, 'y', 'n', NO_HELP, WT_NORM) == 'y'){ + rv++; + + if(plist == &v->main_user_val.l) + write_main++; + else + write_post++; + + /* + * Clear the feature from the user's pinerc + * so that we'll stop bothering them when they + * start up Pine. + */ + clear_feature(plist, 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--; + } + } + } + } + } + } + + + if(write_main) + write_pinerc(ps, Main, WRP_NONE); + if(write_post) + write_pinerc(ps, Post, WRP_NONE); + + return; +} + + +/* + * 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(struct pine *ps, struct variable *var, int revert) +{ + int val = 0; + char **v, *q, **apval; + 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->main_user_val.p || + var->post_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->main_user_val.p || + var->post_user_val.p) + && var->current_val.p) + || + (!var->is_fixed + && var->is_list + && !(var->main_user_val.l || + var->post_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); + + if(ps->vars[V_USER_DOMAIN].post_user_val.p){ + fs_give((void **)&ps->vars[V_USER_DOMAIN].post_user_val.p); + set_current_val(&vars[V_USER_DOMAIN], TRUE, TRUE); + } + + if(ps->VAR_USER_DOMAIN + && ps->VAR_USER_DOMAIN[0] + && (p = strrindex(ps->VAR_USER_DOMAIN, '@'))){ + if(ps->vars[V_USER_DOMAIN].main_user_val.p){ + fs_give((void **)&ps->vars[V_USER_DOMAIN].main_user_val.p); + set_current_val(&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 and + * remove the SP_INBOX flag. + */ + strncpy(ps->cur_folder, ps->mail_stream->mailbox, + sizeof(ps->cur_folder)-1); + ps->cur_folder[sizeof(ps->cur_folder)-1] = '\0'; + sp_set_fldr(ps->mail_stream, ps->cur_folder); + sp_unflag(ps->mail_stream, SP_INBOX); + ps->mangled_header = 1; + } + else if(sp_inbox_stream() + && strcmp(ps->VAR_INBOX_PATH, sp_inbox_stream()->original_mailbox)){ + MAILSTREAM *m = sp_inbox_stream(); + + /* + * 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... + */ + if(m){ + sp_unflag(m, SP_PERMLOCKED | SP_INBOX); + sp_set_fldr(m, m->mailbox); + expunge_and_close(m, NULL, EC_NONE); + } + } + } + else if(var == &ps->vars[V_INCCHECKTIMEO]){ + int old_value = ps->inc_check_timeout; + + if(SVAR_INC_CHECK_TIMEO(ps, old_value, tmp_20k_buf, SIZEOF_20KBUF)){ + if(!revert) + q_status_message(SM_ORDER, 3, 5, tmp_20k_buf); + } + else + ps->inc_check_timeout = old_value; + + if(!revert && (F_OFF(F_ENABLE_INCOMING, ps) || F_OFF(F_ENABLE_INCOMING_CHECKING, ps))) + q_status_message(SM_ORDER, 0, 3, _("This option has no effect without Enable-Incoming-Folders-Checking")); + } + else if(var == &ps->vars[V_INCCHECKINTERVAL]){ + int old_value = ps->inc_check_interval; + + if(SVAR_INC_CHECK_INTERV(ps, old_value, tmp_20k_buf, SIZEOF_20KBUF)){ + if(!revert) + q_status_message(SM_ORDER, 3, 5, tmp_20k_buf); + } + else + ps->inc_check_interval = old_value; + + if(!revert && (F_OFF(F_ENABLE_INCOMING, ps) || F_OFF(F_ENABLE_INCOMING_CHECKING, ps))) + q_status_message(SM_ORDER, 0, 3, _("This option has no effect without Enable-Incoming-Folders-Checking")); + } + else if(var == &ps->vars[V_INC2NDCHECKINTERVAL]){ + int old_value = ps->inc_second_check_interval; + + if(SVAR_INC_2NDCHECK_INTERV(ps, old_value, tmp_20k_buf, SIZEOF_20KBUF)){ + if(!revert) + q_status_message(SM_ORDER, 3, 5, tmp_20k_buf); + } + else + ps->inc_second_check_interval = old_value; + + if(!revert && (F_OFF(F_ENABLE_INCOMING, ps) || F_OFF(F_ENABLE_INCOMING_CHECKING, ps))) + q_status_message(SM_ORDER, 0, 3, _("This option has no effect without Enable-Incoming-Folders-Checking")); + } + else if(var == &ps->vars[V_INCCHECKLIST]){ + if(ps->context_list && ps->context_list->use & CNTXT_INCMNG) + reinit_incoming_folder_list(ps, ps->context_list); + + if(!revert && (F_OFF(F_ENABLE_INCOMING, ps) || F_OFF(F_ENABLE_INCOMING_CHECKING, ps))) + q_status_message(SM_ORDER, 0, 3, _("This option has no effect without Enable-Incoming-Folders-Checking")); + } + 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]){ + reset_index_format(); + clear_index_cache(ps->mail_stream, 0); + } + else if(var == &ps->vars[V_DEFAULT_FCC] || + var == &ps->vars[V_DEFAULT_SAVE_FOLDER]){ + init_save_defaults(); + } + else if(var == &ps->vars[V_KW_BRACES] || + var == &ps->vars[V_OPENING_SEP] || + var == &ps->vars[V_ALT_ADDRS]){ + clear_index_cache(ps->mail_stream, 0); + } + else if(var == &ps->vars[V_KEYWORDS]){ + if(ps_global->keywords) + free_keyword_list(&ps_global->keywords); + + if(var->current_val.l && var->current_val.l[0]) + ps_global->keywords = init_keyword_list(var->current_val.l); + + clear_index_cache(ps->mail_stream, 0); + } + 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 Alpine 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, SIZEOF_20KBUF)){ + if(!revert) + q_status_message(SM_ORDER, 3, 5, tmp_20k_buf); + } + else + ps->viewer_overlap = old_value; + } +#ifdef SMIME + else if(smime_related_var(ps, var)){ + smime_deinit(); + } +#endif /* SMIME */ + else if(var == &ps->vars[V_MAXREMSTREAM]){ + int old_value = ps->s_pool.max_remstream; + + if(SVAR_MAXREMSTREAM(ps, old_value, tmp_20k_buf, SIZEOF_20KBUF)){ + if(!revert ) + q_status_message(SM_ORDER, 3, 5, tmp_20k_buf); + } + else + ps->s_pool.max_remstream = old_value; + + dprint((9, "max_remstream goes to %d\n", + ps->s_pool.max_remstream)); + } +#ifndef _WINDOWS + else if(var == &ps->vars[V_CHAR_SET]){ + char *err = NULL; + + if(F_ON(F_USE_SYSTEM_TRANS, ps)){ + if(!revert) + q_status_message(SM_ORDER, 5, 5, _("This change has no effect because feature Use-System-Translation is on")); + } + else{ + if(reset_character_set_stuff(&err) == -1) + panic(err ? err : "trouble with Character-Set"); + else if(err){ + q_status_message(SM_ORDER | SM_DING, 3, 5, err); + fs_give((void **) &err); + } + } + } + else if(var == &ps->vars[V_KEY_CHAR_SET]){ + char *err = NULL; + + if(F_ON(F_USE_SYSTEM_TRANS, ps)){ + if(!revert) + q_status_message(SM_ORDER, 5, 5, _("This change has no effect because feature Use-System-Translation is on")); + } + else{ + if(reset_character_set_stuff(&err) == -1) + panic(err ? err : "trouble with Character-Set"); + else if(err){ + q_status_message(SM_ORDER | SM_DING, 3, 5, err); + fs_give((void **) &err); + } + } + } +#endif /* ! _WINDOWS */ + else if(var == &ps->vars[V_POST_CHAR_SET]){ + update_posting_charset(ps, revert); + } + else if(var == &ps->vars[V_MARGIN]){ + int old_value = ps->scroll_margin; + + if(SVAR_MARGIN(ps, old_value, tmp_20k_buf, SIZEOF_20KBUF)){ + if(!revert) + q_status_message(SM_ORDER, 3, 5, tmp_20k_buf); + } + else + ps->scroll_margin = old_value; + } + else if(var == &ps->vars[V_DEADLETS]){ + int old_value = ps->deadlets; + + if(SVAR_DEADLETS(ps, old_value, tmp_20k_buf, SIZEOF_20KBUF)){ + if(!revert) + q_status_message(SM_ORDER, 3, 5, tmp_20k_buf); + } + else + ps->deadlets = old_value; + } + else if(var == &ps->vars[V_FILLCOL]){ + if(SVAR_FILLCOL(ps, ps->composer_fillcol, tmp_20k_buf, SIZEOF_20KBUF)){ + if(!revert) + q_status_message(SM_ORDER, 3, 5, tmp_20k_buf); + } + } + else if(var == &ps->vars[V_QUOTE_SUPPRESSION]){ + val = ps->quote_suppression_threshold; + if(val < Q_SUPP_LIMIT && val > 0) + val = -val; + + if(ps->VAR_QUOTE_SUPPRESSION + && SVAR_QUOTE_SUPPRESSION(ps, val, tmp_20k_buf, SIZEOF_20KBUF)){ + if(!revert) + q_status_message(SM_ORDER, 3, 5, tmp_20k_buf); + } + else{ + if(val > 0 && val < Q_SUPP_LIMIT){ + if(!revert){ + snprintf(tmp_20k_buf, SIZEOF_20KBUF, _("Ignoring Quote-Suppression-Threshold value of %s, see help"), ps->VAR_QUOTE_SUPPRESSION); + tmp_20k_buf[SIZEOF_20KBUF-1] = '\0'; + q_status_message(SM_ORDER, 3, 5, tmp_20k_buf); + } + } + else{ + if(val < 0 && val != Q_DEL_ALL) + ps->quote_suppression_threshold = -val; + else + ps->quote_suppression_threshold = val; + } + } + } + else if(var == &ps->vars[V_STATUS_MSG_DELAY]){ + if(SVAR_MSGDLAY(ps, ps->status_msg_delay, tmp_20k_buf, SIZEOF_20KBUF)){ + if(!revert) + q_status_message(SM_ORDER, 3, 5, tmp_20k_buf); + } + } + else if(var == &ps->vars[V_ACTIVE_MSG_INTERVAL]){ + if(SVAR_ACTIVEINTERVAL(ps, ps->active_status_interval, tmp_20k_buf, SIZEOF_20KBUF)){ + if(!revert) + q_status_message(SM_ORDER, 3, 5, tmp_20k_buf); + } + else{ + busy_cue(_("Active Example"), NULL, 0); + sleep(5); + cancel_busy_cue(-1); + } + } +#if !defined(DOS) && !defined(OS2) && !defined(LEAVEOUTFIFO) + else if(var == &ps->vars[V_FIFOPATH]){ + init_newmailfifo(ps->VAR_FIFOPATH); + } +#endif + else if(var == &ps->vars[V_NMW_WIDTH]){ + int old_value = ps->nmw_width; + + if(SVAR_NMW_WIDTH(ps, old_value, tmp_20k_buf, SIZEOF_20KBUF)){ + if(!revert ) + q_status_message(SM_ORDER, 3, 5, tmp_20k_buf); + } + else{ +#ifdef _WINDOWS + if(old_value != ps->nmw_width) + mswin_setnewmailwidth(old_value); /* actually the new value */ +#endif + ps->nmw_width = old_value; + } + } + else if(var == &ps->vars[V_TCPOPENTIMEO]){ + val = 30; + if(!revert) + if(ps->VAR_TCPOPENTIMEO && SVAR_TCP_OPEN(ps, val, tmp_20k_buf, SIZEOF_20KBUF)) + q_status_message(SM_ORDER, 3, 5, tmp_20k_buf); + } + else if(var == &ps->vars[V_TCPREADWARNTIMEO]){ + val = 15; + if(!revert) + if(ps->VAR_TCPREADWARNTIMEO && SVAR_TCP_READWARN(ps,val,tmp_20k_buf, SIZEOF_20KBUF)) + q_status_message(SM_ORDER, 3, 5, tmp_20k_buf); + } + else if(var == &ps->vars[V_TCPWRITEWARNTIMEO]){ + val = 0; + if(!revert) + if(ps->VAR_TCPWRITEWARNTIMEO && SVAR_TCP_WRITEWARN(ps,val,tmp_20k_buf, SIZEOF_20KBUF)) + q_status_message(SM_ORDER, 3, 5, tmp_20k_buf); + } + else if(var == &ps->vars[V_TCPQUERYTIMEO]){ + val = 60; + if(!revert) + if(ps->VAR_TCPQUERYTIMEO && SVAR_TCP_QUERY(ps, val, tmp_20k_buf, SIZEOF_20KBUF)) + q_status_message(SM_ORDER, 3, 5, tmp_20k_buf); + } + else if(var == &ps->vars[V_RSHOPENTIMEO]){ + val = 15; + if(!revert) + if(ps->VAR_RSHOPENTIMEO && SVAR_RSH_OPEN(ps, val, tmp_20k_buf, SIZEOF_20KBUF)) + q_status_message(SM_ORDER, 3, 5, tmp_20k_buf); + } + else if(var == &ps->vars[V_SSHOPENTIMEO]){ + val = 15; + if(!revert) + if(ps->VAR_SSHOPENTIMEO && SVAR_SSH_OPEN(ps, val, tmp_20k_buf, SIZEOF_20KBUF)) + 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; + size_t l; + + l = strlen(ps->VAR_OPER_DIR) + 100; + e = (char *) fs_get((l+1) * sizeof(char)); + snprintf(e, l+1, _("Warning: Sig file can't be outside of %s"), + ps->VAR_OPER_DIR); + e[l] = '\0'; + q_status_message(SM_ORDER, 3, 6, e); + fs_give((void **)&e); + } + } + else if(var == &ps->vars[V_OPER_DIR]){ + if(ps->VAR_OPER_DIR && !ps->VAR_OPER_DIR[0]){ + q_status_message(SM_ORDER, 3, 5, "Operating-dir is turned off."); + fs_give((void **)&ps->vars[V_OPER_DIR].current_val.p); + if(ps->vars[V_OPER_DIR].fixed_val.p) + fs_give((void **)&ps->vars[V_OPER_DIR].fixed_val.p); + if(ps->vars[V_OPER_DIR].global_val.p) + fs_give((void **)&ps->vars[V_OPER_DIR].global_val.p); + if(ps->vars[V_OPER_DIR].cmdline_val.p) + fs_give((void **)&ps->vars[V_OPER_DIR].cmdline_val.p); + if(ps->vars[V_OPER_DIR].post_user_val.p) + fs_give((void **)&ps->vars[V_OPER_DIR].post_user_val.p); + if(ps->vars[V_OPER_DIR].main_user_val.p) + fs_give((void **)&ps->vars[V_OPER_DIR].main_user_val.p); + } + } + else if(var == &ps->vars[V_MAILCHECK]){ + int timeo = 15; + if(SVAR_MAILCHK(ps, timeo, tmp_20k_buf, SIZEOF_20KBUF)){ + set_input_timeout(15); + if(!revert) + q_status_message(SM_ORDER, 3, 5, tmp_20k_buf); + } + else{ + set_input_timeout(timeo); + if(get_input_timeout() == 0 && !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")); + } + } + } + else if(var == &ps->vars[V_MAILCHECKNONCURR]){ + val = (int) ps->check_interval_for_noncurr; + if(ps->VAR_MAILCHECKNONCURR + && SVAR_MAILCHKNONCURR(ps, val, tmp_20k_buf, SIZEOF_20KBUF)){ + if(!revert) + q_status_message(SM_ORDER, 3, 5, tmp_20k_buf); + } + else + ps->check_interval_for_noncurr = (time_t) val; + } + else if(var == &ps->vars[V_MAILDROPCHECK]){ + long rvl; + + rvl = 60L; + if(ps->VAR_MAILDROPCHECK && SVAR_MAILDCHK(ps, rvl, tmp_20k_buf, SIZEOF_20KBUF)) + q_status_message(SM_ORDER, 3, 5, tmp_20k_buf); + else{ + if(rvl == 0L) + rvl = (60L * 60L * 24L * 100L); /* 100 days */ + + if(rvl >= 60L) + mail_parameters(NULL, SET_SNARFINTERVAL, (void *) rvl); + } + } + else if(var == &ps->vars[V_NNTPRANGE]){ + long rvl; + + rvl = 0L; + if(ps->VAR_NNTPRANGE && SVAR_NNTPRANGE(ps, rvl, tmp_20k_buf, SIZEOF_20KBUF)) + q_status_message(SM_ORDER, 3, 5, tmp_20k_buf); + else{ + if(rvl >= 0L) + mail_parameters(NULL, SET_NNTPRANGE, (void *) rvl); + } + } + else if(var == &ps->vars[V_CUSTOM_HDRS] || var == &ps->vars[V_COMP_HDRS]){ + /* this will give warnings about headers that can't be changed */ + if(!revert && var->current_val.l && var->current_val.l[0]) + customized_hdr_setup(NULL, var->current_val.l, UseAsDef); + } +#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 && standard_radio_var(ps, var)){ + + cur_rule_value(var, TRUE, FALSE); + if(var == &ps_global->vars[V_AB_SORT_RULE]) + addrbook_redo_sorts(); + else if(var == &ps_global->vars[V_THREAD_INDEX_STYLE]){ + clear_index_cache(ps_global->mail_stream, 0); + set_lflags(ps_global->mail_stream, ps_global->msgmap, + MN_COLL | MN_CHID, 0); + if(SORT_IS_THREADED(ps_global->msgmap) + && (SEP_THRDINDX() || COLL_THRDS())) + collapse_threads(ps_global->mail_stream, ps_global->msgmap, NULL); + + adjust_cur_to_visible(ps_global->mail_stream, ps_global->msgmap); + } +#ifndef _WINDOWS + else if(var == &ps->vars[V_COLOR_STYLE]){ + pico_toggle_color(0); + switch(ps->color_style){ + case COL_NONE: + case COL_TERMDEF: + pico_set_color_options(pico_trans_color() ? COLOR_TRANS_OPT : 0); + break; + case COL_ANSI8: + pico_set_color_options(COLOR_ANSI8_OPT|COLOR_TRANS_OPT); + break; + case COL_ANSI16: + pico_set_color_options(COLOR_ANSI16_OPT|COLOR_TRANS_OPT); + break; + case COL_ANSI256: + pico_set_color_options(COLOR_ANSI256_OPT|COLOR_TRANS_OPT); + break; + } + + if(ps->color_style != COL_NONE) + pico_toggle_color(1); + + if(pico_usingcolor()) + pico_set_normal_color(); + + clear_index_cache(ps_global->mail_stream, 0); + ClearScreen(); + ps->mangled_screen = 1; + } +#endif + } + else if(revert && var == &ps->vars[V_SORT_KEY]){ + int def_sort_rev; + + decode_sort(VAR_SORT_KEY, &ps->def_sort, &def_sort_rev); + ps->def_sort_rev = def_sort_rev; + } + else if(var == &ps->vars[V_THREAD_MORE_CHAR] || + var == &ps->vars[V_THREAD_EXP_CHAR] || + var == &ps->vars[V_THREAD_LASTREPLY_CHAR]){ + + if(var == &ps->vars[V_THREAD_LASTREPLY_CHAR] && + !(var->current_val.p && var->current_val.p[0])){ + if(var->current_val.p) + fs_give((void **) &var->current_val.p); + + q_status_message1(SM_ORDER, 3, 5, + _("\"%s\" can't be Empty, using default"), var->name); + + apval = APVAL(var, ew); + if(*apval) + fs_give((void **)apval); + + set_current_val(var, FALSE, FALSE); + + if(!(var->current_val.p && var->current_val.p[0] + && !var->current_val.p[1])){ + if(var->current_val.p) + fs_give((void **) &var->current_val.p); + + var->current_val.p = cpystr(DF_THREAD_LASTREPLY_CHAR); + } + } + + if(var == &ps->vars[V_THREAD_MORE_CHAR] || + var == &ps->vars[V_THREAD_EXP_CHAR] || + var == &ps->vars[V_THREAD_LASTREPLY_CHAR]){ + if(var->current_val.p && var->current_val.p[0] && + var->current_val.p[1]){ + q_status_message1(SM_ORDER, 3, 5, + "Only first character of \"%s\" is used", + var->name); + var->current_val.p[1] = '\0'; + } + + if(var->main_user_val.p && var->main_user_val.p[0] && + var->main_user_val.p[1]) + var->main_user_val.p[1] = '\0'; + + if(var->post_user_val.p && var->post_user_val.p[0] && + var->post_user_val.p[1]) + var->post_user_val.p[1] = '\0'; + } + + clear_index_cache(ps_global->mail_stream, 0); + set_need_format_setup(ps_global->mail_stream); + } + else if(var == &ps->vars[V_NNTP_SERVER]){ + free_contexts(&ps_global->context_list); + init_folders(ps_global); + } + else if(var == &ps->vars[V_USE_ONLY_DOMAIN_NAME]){ + init_hostname(ps); + } + else if(var == &ps->vars[V_PRINTER]){ + if(!revert && ps->vars[V_PERSONAL_PRINT_COMMAND].is_fixed){ + if(printer_value_check_and_adjust()) + q_status_message1(SM_ORDER, 3, 5, + _("Can't set \"%s\" to that value, see Setup/Printer"), + pretty_var_name(var->name)); + } + } + else if(var == &ps->vars[V_KW_COLORS] || + var == &ps->vars[V_IND_PLUS_FORE_COLOR] || + var == &ps->vars[V_IND_IMP_FORE_COLOR] || + var == &ps->vars[V_IND_DEL_FORE_COLOR] || + var == &ps->vars[V_IND_ANS_FORE_COLOR] || + var == &ps->vars[V_IND_NEW_FORE_COLOR] || + var == &ps->vars[V_IND_UNS_FORE_COLOR] || + var == &ps->vars[V_IND_HIPRI_FORE_COLOR]|| + var == &ps->vars[V_IND_LOPRI_FORE_COLOR]|| + var == &ps->vars[V_IND_ARR_FORE_COLOR] || + var == &ps->vars[V_IND_REC_FORE_COLOR] || + var == &ps->vars[V_IND_FWD_FORE_COLOR] || + var == &ps->vars[V_IND_OP_FORE_COLOR] || + var == &ps->vars[V_IND_FROM_FORE_COLOR] || + var == &ps->vars[V_IND_SUBJ_FORE_COLOR] || + var == &ps->vars[V_IND_PLUS_BACK_COLOR] || + var == &ps->vars[V_IND_IMP_BACK_COLOR] || + var == &ps->vars[V_IND_DEL_BACK_COLOR] || + var == &ps->vars[V_IND_ANS_BACK_COLOR] || + var == &ps->vars[V_IND_NEW_BACK_COLOR] || + var == &ps->vars[V_IND_UNS_BACK_COLOR] || + var == &ps->vars[V_IND_ARR_BACK_COLOR] || + var == &ps->vars[V_IND_REC_BACK_COLOR] || + var == &ps->vars[V_IND_FWD_BACK_COLOR] || + var == &ps->vars[V_IND_OP_BACK_COLOR] || + var == &ps->vars[V_IND_FROM_BACK_COLOR] || + var == &ps->vars[V_IND_SUBJ_BACK_COLOR]){ + clear_index_cache(ps_global->mail_stream, 0); + } + else if(var == score_act_global_ptr){ + int score; + + score = atoi(var->current_val.p); + if(score < SCORE_MIN || score > SCORE_MAX){ + q_status_message2(SM_ORDER, 3, 5, + _("Score Value must be in range %s to %s"), + comatose(SCORE_MIN), comatose(SCORE_MAX)); + apval = APVAL(var, ew); + if(*apval) + fs_give((void **)apval); + + set_current_val(var, FALSE, FALSE); + } + } + else if(var == scorei_pat_global_ptr || var == age_pat_global_ptr + || var == size_pat_global_ptr || var == cati_global_ptr){ + apval = APVAL(var, ew); + if(*apval){ + INTVL_S *iv; + iv = parse_intvl(*apval); + if(iv){ + fs_give((void **) apval); + *apval = stringform_of_intvl(iv); + free_intvl(&iv); + } + else + fs_give((void **) apval); + } + + set_current_val(var, FALSE, FALSE); + } + else if(var == &ps->vars[V_FEATURE_LIST]){ + process_feature_list(ps, var->current_val.l, 0, 0, 0); + } + else if(!revert && (var == &ps->vars[V_LAST_TIME_PRUNE_QUESTION] || + var == &ps->vars[V_REMOTE_ABOOK_HISTORY] || + var == &ps->vars[V_REMOTE_ABOOK_VALIDITY] || + var == &ps->vars[V_USERINPUTTIMEO] || + var == &ps->vars[V_NEWS_ACTIVE_PATH] || + var == &ps->vars[V_NEWS_SPOOL_DIR] || + var == &ps->vars[V_INCOMING_FOLDERS] || + var == &ps->vars[V_FOLDER_SPEC] || + var == &ps->vars[V_NEWS_SPEC] || + var == &ps->vars[V_DISABLE_DRIVERS] || + var == &ps->vars[V_DISABLE_AUTHS] || + var == &ps->vars[V_RSHPATH] || + var == &ps->vars[V_RSHCMD] || + var == &ps->vars[V_SSHCMD] || + var == &ps->vars[V_SSHPATH])){ + q_status_message2(SM_ASYNC, 0, 3, + _("Changes%s%s will affect your next Alpine session."), + var->name ? " to " : "", var->name ? var->name : ""); + } + + if(!revert && (var == &ps->vars[V_TCPOPENTIMEO] || + var == &ps->vars[V_TCPREADWARNTIMEO] || + var == &ps->vars[V_TCPWRITEWARNTIMEO] || + var == &ps->vars[V_TCPQUERYTIMEO] || + var == &ps->vars[V_RSHOPENTIMEO] || + var == &ps->vars[V_SSHOPENTIMEO])) + q_status_message(SM_ASYNC, 0, 3, + _("Timeout changes will affect your next Alpine session.")); +} + + +/* + * 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(struct pine *ps, SAVED_CONFIG_S *vsave, int allow_hard_to_config_remotely) +{ + struct variable *vreal; + SAVED_CONFIG_S *v; + int i, n; + int changed = 0; + char *pval, **apval, **lval, ***alval; + + v = vsave; + for(vreal = ps->vars; vreal->name; vreal++,v++){ + if(!save_include(ps, vreal, allow_hard_to_config_remotely)) + continue; + + changed = 0; + if(vreal->is_list){ + lval = LVAL(vreal, ew); + alval = ALVAL(vreal, ew); + + if((v->saved_user_val.l && !lval) + || (!v->saved_user_val.l && lval)) + changed++; + else if(!v->saved_user_val.l && !lval) + ;/* no change, nothing to do */ + else + for(i = 0; v->saved_user_val.l[i] || lval[i]; i++) + if((v->saved_user_val.l[i] + && (!lval[i] + || strcmp(v->saved_user_val.l[i], lval[i]))) + || + (!v->saved_user_val.l[i] && lval[i])){ + changed++; + break; + } + + if(changed){ + char **list; + + if(alval){ + if(*alval) + free_list_array(alval); + + /* copy back the original one */ + if(v->saved_user_val.l){ + list = v->saved_user_val.l; + n = 0; + /* count how many */ + while(list[n]) + n++; + + *alval = (char **)fs_get((n+1) * sizeof(char *)); + + for(i = 0; i < n; i++) + (*alval)[i] = cpystr(v->saved_user_val.l[i]); + + (*alval)[n] = NULL; + } + } + } + } + else{ + pval = PVAL(vreal, ew); + apval = APVAL(vreal, ew); + + if((v->saved_user_val.p && + (!pval || strcmp(v->saved_user_val.p, pval))) || + (!v->saved_user_val.p && pval)){ + /* It changed, fix it */ + changed++; + if(apval){ + /* free the changed value */ + if(*apval) + fs_give((void **)apval); + + if(v->saved_user_val.p) + *apval = cpystr(v->saved_user_val.p); + } + } + } + + if(changed){ + if(vreal == &ps->vars[V_FEATURE_LIST]) + set_feature_list_current_val(vreal); + else + set_current_val(vreal, TRUE, FALSE); + + fix_side_effects(ps, vreal, 1); + } + } +} + + +SAVED_CONFIG_S * +save_config_vars(struct pine *ps, int allow_hard_to_config_remotely) +{ + 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, allow_hard_to_config_remotely)) + continue; + + if(vreal->is_list){ + int n, i; + char **list; + + if(LVAL(vreal, ew)){ + /* count how many */ + n = 0; + list = LVAL(vreal, ew); + while(list[n]) + n++; + + v->saved_user_val.l = (char **)fs_get((n+1) * sizeof(char *)); + memset((void *)v->saved_user_val.l, 0, (n+1)*sizeof(char *)); + for(i = 0; i < n; i++) + v->saved_user_val.l[i] = cpystr(list[i]); + + v->saved_user_val.l[n] = NULL; + } + } + else{ + if(PVAL(vreal, ew)) + v->saved_user_val.p = cpystr(PVAL(vreal, ew)); + } + } + + return(vsave); +} + + +void +free_saved_config(struct pine *ps, SAVED_CONFIG_S **vsavep, int allow_hard_to_config_remotely) +{ + struct variable *vreal; + SAVED_CONFIG_S *v; + + if(vsavep && *vsavep){ + for(v = *vsavep, vreal = ps->vars; vreal->name; vreal++,v++){ + if(!save_include(ps, vreal, allow_hard_to_config_remotely)) + continue; + + if(vreal->is_list){ /* free saved_user_val.l */ + if(v && v->saved_user_val.l) + free_list_array(&v->saved_user_val.l); + } + else if(v && v->saved_user_val.p) + fs_give((void **)&v->saved_user_val.p); + } + + fs_give((void **)vsavep); + } +} + + +/* + * Returns positive if any thing was actually deleted. + */ +int +delete_user_vals(struct variable *v) +{ + int rv = 0; + + if(v){ + if(v->is_list){ + if(v->post_user_val.l){ + rv++; + free_list_array(&v->post_user_val.l); + } + if(v->main_user_val.l){ + rv++; + free_list_array(&v->main_user_val.l); + } + } + else{ + if(v->post_user_val.p){ + rv++; + fs_give((void **)&v->post_user_val.p); + } + if(v->main_user_val.p){ + rv++; + fs_give((void **)&v->main_user_val.p); + } + } + } + + return(rv); +} + + +/* + * ../pith/conf.c required function + */ +int +unexpected_pinerc_change(void) +{ + Writechar(BELL, 0); + if(want_to("Unexpected pinerc change! Overwrite with current config", + 'n', 0, NO_HELP, WT_FLUSH_IN) == 'n'){ + return(-1); /* abort pinerc write */ + } + + return(0); /* overwrite */ +} + + +#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 */ -- cgit v1.2.3-54-g00ecf