1 /*
2  *	(c) Copyright 1990, Kim Fabricius Storm.  All rights reserved.
3  *      Copyright (c) 1996-2005 Michael T Pins.  All rights reserved.
4  *
5  *	Variable setting and display
6  */
7 
8 #include <stdlib.h>
9 #include <string.h>
10 #include <ctype.h>
11 #include "config.h"
12 #include "global.h"
13 #include "chset.h"
14 #include "folder.h"
15 #include "init.h"
16 #include "keymap.h"
17 #include "regexp.h"
18 #include "sort.h"
19 #include "nn_term.h"
20 
21 /* variable.c */
22 
23 static struct variable_defs *lookup_variable(char *variable);
24 static char     escaped_char(char c);
25 static void     adjust(register char *str);
26 static char    *var_value(register struct variable_defs * var, char *tag);
27 static int      var_on_stack(register struct variable_defs * var);
28 
29 extern int      prompt_length, prompt_line;
30 extern regexp  *pg_regexp;
31 extern int      pg_new_regexp;
32 
33 
34 /*
35  * Variable types and variants
36  *
37  * Boolean: type 'int'.
38  *
39  *	BOOL 0		Ordinary boolean variable.
40  *	BOOL 1		As 0 + redraw screen on return.
41  *	BOOL 2		Special: reorder menu according to new value.
42  *	BOOL 4		Inverse boolean varible ("set" clears internal var).
43  *
44  * Numeric: type 'int'.
45  *
46  *	INT 0		Ordinary numeric variable ("unset" set var to 0).
47  *	INT 1		As 0 + redraw screen on return.
48  *	INT 2		As 0, but "unset" set var to -1.
49  *	INT 3		As 2 + redraw screen on return.
50  *
51  * Strings: type 'char *'
52  *
53  *	STR 0		Ordinary string ("unset" set var to NULL).
54  *	STR 2		(home relative) file name or full path.
55  *			Automatically expanded.  ("unset" set var to NULL).
56  *	STR 3		Ordinary string, but cannot be "unset".
57  *	STR 4		(Expanded) file name - cannot be unset.
58  *	STR 5		Ordinary string ("unset" set var to "").
59  *
60  *	CODES n		String initialized by list of key names.
61  *			A maximum of 16 CODES variables (n = 0 - 15) exist.
62  *
63  * Strings: type 'char []'
64  *
65  *	STR 1		Ordinary string saved in static array.
66  *			"unset" set array to empty string.
67  *			Warning: no bounds checking!
68  *			Notice: Variable address is "(char **)var"  (no &).
69  *
70  * Keys: type 'key_type'
71  *
72  *	KEY 0		Ordinary key.
73  *
74  * Pseudo variables:
75  *
76  *	SPEC n		Treated by V_SPECIAL 'case n'.
77  *
78  * Modifiers:
79  *
80  *	INIT		Can only be set in init file.
81  *	SAFE		Cannot be changed if "shell-restrictions" is set.
82  */
83 
84 extern int      in_init;
85 
86 extern char			/* string variables */
87                *backup_folder_path, *bak_suffix, *bug_address, *counter_delim_left,
88                *counter_delim_right, *decode_header_file, *default_distribution,
89                *default_save_file, *distribution_follow, *distribution_post,
90                *editor_program, *extra_mail_headers, *extra_news_headers,
91                *folder_save_file, *header_lines, *folder_directory, included_mark[],
92                *inews_program, *initial_newsrc_path, *mail_alias_expander,
93                *mail_box, *mail_record, *mail_script, *mailer_program,
94                 attributes[], *newsrc_file, *news_record, *news_script,
95                *pager, patch_command[], printer[], *print_header_lines,
96                *response_dflt_answer, *save_counter_format, *save_header_lines,
97                *saved_header_escape, *shade_on_attr, *shade_off_attr, *spell_checker,
98                *sign_type, *trusted_escapes, unshar_command[], *unshar_header_file,
99                *user_shell;
100 
101 extern int			/* boolean variables */
102                 also_cross_postings, also_full_digest, also_subgroups,
103                 append_sig_mail, append_sig_post, auto_junk_seen, auto_select_rw,
104                 auto_select_subject, auto_preview_mode, body_search_header,
105                 case_fold_search, check_group_access, compress_mode, conf_append,
106                 conf_auto_quit, conf_create, conf_dont_sleep, conf_group_entry,
107                 conf_junk_seen, consolidated_manual, consolidated_menu,
108                 counter_padding, decode_keep, delay_redraw, dflt_kill_select,
109                 do_kill_handling, dont_sort_articles, dont_sort_folders,
110                 dont_split_digests, echo_prefix_key, edit_patch_command,
111                 edit_print_command, edit_unshar_command, empty_answer_check,
112                 enter_last_read_mode, flow_control, flush_typeahead, fmt_rptsubj,
113                 folder_format_check, folder_rewrite_trace, guard_double_slash,
114                 ignore_formfeed, ignore_xon_xoff, ignore_re, include_art_id,
115                 include_full_header, include_mark_blanks, inews_pipe_input,
116                 keep_backup_folder, keep_rc_backup, keep_unsubscribed, keep_unsub_long,
117                 long_menu, macro_debug, mailer_pipe_input, mark_overlap,
118                 mark_overlap_shading, match_parts_equal, match_parts_begin,
119                 monitor_mode, new_read_prompt, novice, old_packname, prev_also_read,
120                 preview_mark_read, query_signature, quick_save, quick_unread_count,
121                 read_ret_next_page, repeat_group_query, report_cost_on_exit, retain_seen_status,
122                 save_report, scroll_clear_page, select_leave_next, select_on_sender,
123                 seq_cross_filtering, shell_restrictions, show_article_date,
124                 show_current_time, show_motd_on_entry, silent, slow_mode,
125                 suggest_save_file, tidy_newsrc, use_ed_line, use_mail_folders,
126                 use_mmdf_folders, use_path_in_from, use_selections, use_visible_bell;
127 
128 extern int			/* integer variables */
129                 also_read_articles, article_limit, auto_read_limit, auto_select_closed,
130                 check_db_update, conf_entry_limit, collapse_subject, Columns,
131                 cross_post_limit, data_bits, Debug, decode_skip_prefix,
132                 entry_message_limit, expired_msg_delay, first_page_lines,
133                 fmt_linenum, kill_debug, kill_ref_count, Lines, match_skip_prefix,
134                 mark_next_group, mark_read_return, mark_read_skip, menu_spacing,
135                 merge_report_rate, message_history, min_pv_window, mouse_usage,
136                 multi_key_guard_time, new_group_action, newsrc_update_freq,
137                 orig_to_include_mask, overlap, preview_continuation, preview_window,
138                 print_header_type, re_layout, re_layout_more, response_check_pause,
139                 retry_on_error, save_closed_mode, save_counter_offset,
140                 scroll_last_lines, show_purpose_mode, slow_speed, strict_from_parse,
141                 sort_mode, subject_match_limit, wrap_headers;
142 
143 #ifdef NNTP
144 extern char    *nn_directory;
145 extern char    *nntp_cache_dir, *nntp_server;
146 extern char    *nntp_user, *nntp_password;
147 extern int      nntp_cache_size, nntp_debug;
148 #endif
149 
150 extern          key_type	/* key strokes */
151                 comp1_key, comp2_key, help_key, erase_key, delword_key,
152                 kill_key;
153 
154 #undef STR
155 #undef BOOL
156 #undef INT
157 #undef KEY
158 #undef SPEC
159 #undef SAFE
160 #undef INIT
161 #undef CODES
162 
163 
164 #define	V_STRING	0x1000
165 #define V_BOOLEAN	0x2000
166 #define	V_INTEGER	0x3000
167 #define V_KEY		0x4000
168 #define	V_SPECIAL	0x5000
169 #define V_CODES		0x6000
170 
171 #define V_SAFE		0x0100
172 #define V_INIT		0x0200
173 
174 #define V_LOCKED	0x0800
175 #define V_MODIFIED	0x8000
176 
177 #define	STR		V_STRING |
178 #define BOOL		V_BOOLEAN |
179 #define	INT		V_INTEGER |
180 #define KEY		V_KEY |
181 #define	SPEC		V_SPECIAL |
182 #define	CODES		V_CODES |
183 
184 #define SAFE		V_SAFE |
185 #define INIT		V_INIT |
186 
187 static char    *code_strings[16] = {
188     NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
189     NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
190 };
191 
192 static int      var_on_stack();
193 
194 static struct variable_defs {
195     char           *var_name;
196     int             var_flags;
197     char          **var_addr;
198 }               variables[] = {
199 
200     "also-full-digest", BOOL 0, (char **) &also_full_digest,
201     "also-subgroups", BOOL INIT 0, (char **) &also_subgroups,
202     "append-signature-mail", BOOL 0, (char **) &append_sig_mail,
203     "append-signature-post", BOOL 0, (char **) &append_sig_post,
204     "attributes", STR 1, (char **) attributes,
205     "auto-junk-seen", BOOL 0, (char **) &auto_junk_seen,
206     "auto-preview-mode", BOOL 0, (char **) &auto_preview_mode,
207     "auto-read-mode-limit", INT 0, (char **) &auto_read_limit,
208     "auto-select-closed", INT 0, (char **) &auto_select_closed,
209     "auto-select-rw", BOOL 0, (char **) &auto_select_rw,
210     "auto-select-subject", BOOL 0, (char **) &auto_select_subject,
211     "backup", BOOL 0, (char **) &keep_rc_backup,
212     "backup-folder-path", STR 4, (char **) &backup_folder_path,
213     "backup-suffix", STR 0, (char **) &bak_suffix,
214     "body-search-header", BOOL 0, (char **) &body_search_header,
215     "bug-report-address", STR 0, (char **) &bug_address,
216     "case-fold-search", BOOL 0, (char **) &case_fold_search,
217     "charset", SPEC 3, (char **) NULL,
218     "check-db-update-time", INT 0, (char **) &check_db_update,
219     "check-group-access", BOOL 0, (char **) &check_group_access,
220     "collapse-subject", INT 3, (char **) &collapse_subject,
221     "columns", INT 1, (char **) &Columns,
222     "comp1-key", KEY 0, (char **) &comp1_key,
223     "comp2-key", KEY 0, (char **) &comp2_key,
224     "compress", BOOL 0, (char **) &compress_mode,
225     "confirm-append", BOOL 0, (char **) &conf_append,
226     "confirm-auto-quit", BOOL 0, (char **) &conf_auto_quit,
227     "confirm-create", BOOL 0, (char **) &conf_create,
228     "confirm-entry", BOOL 0, (char **) &conf_group_entry,
229     "confirm-entry-limit", INT 0, (char **) &conf_entry_limit,
230     "confirm-junk-seen", BOOL 0, (char **) &conf_junk_seen,
231     "confirm-messages", BOOL 0, (char **) &conf_dont_sleep,
232     "consolidated-manual", BOOL 0, (char **) &consolidated_manual,
233     "consolidated-menu", BOOL 1, (char **) &consolidated_menu,
234     "counter-delim-left", STR 5, (char **) &counter_delim_left,
235     "counter-delim-right", STR 5, (char **) &counter_delim_right,
236     "counter-padding", INT 1, (char **) &counter_padding,
237     "cross-filter-seq", BOOL 0, (char **) &seq_cross_filtering,
238     "cross-post", BOOL 0, (char **) &also_cross_postings,
239     "cross-post-limit", INT 0, (char **) &cross_post_limit,
240     "data-bits", INT 0, (char **) &data_bits,
241     "date", BOOL 0, (char **) &show_article_date,
242     "debug", INT 0, (char **) &Debug,
243     "decode-header-file", STR 0, (char **) &decode_header_file,
244     "decode-keep", INT 0, (char **) &decode_keep,
245     "decode-skip-prefix", INT 0, (char **) &decode_skip_prefix,
246     "default-distribution", STR 0, (char **) &default_distribution,
247     "default-kill-select", INT 0, (char **) &dflt_kill_select,
248     "default-save-file", STR 3, (char **) &default_save_file,
249     "delay-redraw", BOOL 0, (char **) &delay_redraw,
250     "echo-prefix-key", BOOL 0, (char **) &echo_prefix_key,
251     "edit-patch-command", BOOL 0, (char **) &edit_patch_command,
252     "edit-print-command", BOOL 0, (char **) &edit_print_command,
253     "edit-response-check", BOOL 0, (char **) &empty_answer_check,
254     "edit-unshar-command", BOOL 0, (char **) &edit_unshar_command,
255     "editor", STR 0, (char **) &editor_program,
256     "embedded-header-escape", STR 0, (char **) &saved_header_escape,
257     "enter-last-read-mode", INT 0, (char **) &enter_last_read_mode,
258     "entry-report-limit", INT 0, (char **) &entry_message_limit,
259     "erase-key", KEY 0, (char **) &erase_key,
260     "expert", BOOL 4, (char **) &novice,
261     "expired-message-delay", INT 0, (char **) &expired_msg_delay,
262     "flow-control", BOOL 0, (char **) &flow_control,
263     "flush-typeahead", BOOL 0, (char **) &flush_typeahead,
264     "folder", STR 2, (char **) &folder_directory,
265     "folder-format-check", BOOL 0, (char **) &folder_format_check,
266     "folder-save-file", STR 3, (char **) &folder_save_file,
267     "follow-distribution", STR 0, (char **) &distribution_follow,
268     "from-line-parsing", INT 0, (char **) &strict_from_parse,
269     "fsort", BOOL 2, (char **) &dont_sort_folders,
270     "guard-double-slash", BOOL 0, (char **) &guard_double_slash,
271     "header-lines", STR 0, (char **) &header_lines,
272     "help-key", KEY 0, (char **) &help_key,
273     "ignore-formfeed", BOOL 0, (char **) &ignore_formfeed,
274     "ignore-re", BOOL 0, (char **) &ignore_re,
275     "ignore-xon-xoff", BOOL 0, (char **) &ignore_xon_xoff,
276     "include-art-id", BOOL 0, (char **) &include_art_id,
277     "include-full-header", BOOL 0, (char **) &include_full_header,
278     "include-mark-blank-lines", BOOL 0, (char **) &include_mark_blanks,
279     "included-mark", STR 1, (char **) included_mark,
280     "inews", STR 0, (char **) &inews_program,
281     "inews-pipe-input", BOOL 0, (char **) &inews_pipe_input,
282     "initial-newsrc-file", STR 0, (char **) &initial_newsrc_path,
283     "keep-backup-folder", BOOL 0, (char **) &keep_backup_folder,
284     "keep-unsubscribed", BOOL 0, (char **) &keep_unsubscribed,
285     "kill", BOOL 0, (char **) &do_kill_handling,
286     "kill-debug", BOOL 0, (char **) &kill_debug,
287     "kill-key", KEY 0, (char **) &kill_key,
288     "kill-reference-count", INT 0, (char **) &kill_ref_count,
289     "layout", INT 1, (char **) &fmt_linenum,
290     "limit", INT 2, (char **) &article_limit,
291     "lines", INT 1, (char **) &Lines,
292     "long-menu", BOOL 1, (char **) &long_menu,
293     "macro-debug", BOOL 0, (char **) &macro_debug,
294     "mail", STR 2, (char **) &mail_box,
295     "mail-alias-expander", STR 0, (char **) &mail_alias_expander,
296     "mail-format", BOOL 0, (char **) &use_mail_folders,
297     "mail-header", STR 0, (char **) &extra_mail_headers,
298     "mail-record", STR 2, (char **) &mail_record,
299     "mail-script", STR SAFE 2, (char **) &mail_script,
300     "mailer", STR 0, (char **) &mailer_program,
301     "mailer-pipe-input", BOOL 0, (char **) &mailer_pipe_input,
302     "mark-overlap", BOOL 0, (char **) &mark_overlap,
303     "mark-overlap-shading", BOOL 0, (char **) &mark_overlap_shading,
304     "marked-by-next-group", INT 0, (char **) &mark_next_group,
305     "marked-by-read-return", INT 0, (char **) &mark_read_return,
306     "marked-by-read-skip", INT 0, (char **) &mark_read_skip,
307     "menu-spacing", INT 1, (char **) &menu_spacing,
308     "merge-report-rate", INT 0, (char **) &merge_report_rate,
309     "message-history", INT 0, (char **) &message_history,
310     "min-window", INT 1, (char **) &min_pv_window,
311     "mmdf-format", BOOL 0, (char **) &use_mmdf_folders,
312     "monitor", BOOL 0, (char **) &monitor_mode,
313     "motd", BOOL 0, (char **) &show_motd_on_entry,
314     "mouse-usage", INT 0, (char **) &mouse_usage,
315     "multi-key-guard-time", INT 0, (char **) &multi_key_guard_time,
316     "new-group-action", INT 0, (char **) &new_group_action,
317     "new-style-read-prompt", BOOL 0, (char **) &new_read_prompt,
318     "news-header", STR 0, (char **) &extra_news_headers,
319     "news-record", STR 2, (char **) &news_record,
320     "news-script", STR SAFE 2, (char **) &news_script,
321     "newsrc", STR 2, (char **) &newsrc_file,
322     "nn-directory", STR 2, (char **) &nn_directory,
323 
324 #ifdef NNTP
325     "nntp-cache-dir", STR INIT 0, (char **) &nntp_cache_dir,
326     "nntp-cache-size", INT INIT 0, (char **) &nntp_cache_size,
327     "nntp-debug", BOOL 0, (char **) &nntp_debug,
328     "nntp-password", STR 3, (char **) &nntp_password,
329     "nntp-server", STR 3, (char **) &nntp_server,
330     "nntp-user", STR 3, (char **) &nntp_user,
331 #endif
332 
333 /*  "no....."  -- cannot have variable names starting with "no" */
334     "old", SPEC 2, (char **) NULL,
335     "old-packname", BOOL 0, (char **) &old_packname,
336     "orig-to-include-mask", INT 0, (char **) &orig_to_include_mask,
337     "overlap", INT 0, (char **) &overlap,
338     "pager", STR SAFE 3, (char **) &pager,
339     "patch-command", STR SAFE 1, (char **) patch_command,
340     "post-distribution", STR 0, (char **) &distribution_post,
341     "preview-continuation", INT 0, (char **) &preview_continuation,
342     "preview-mark-read", BOOL 0, (char **) &preview_mark_read,
343     "previous-also-read", BOOL 0, (char **) &prev_also_read,
344     "print-header-lines", STR 0, (char **) &print_header_lines,
345     "print-header-type", INT 0, (char **) &print_header_type,
346     "printer", STR SAFE 1, (char **) printer,
347     "query-signature", BOOL 0, (char **) &query_signature,
348     "quick-count", BOOL 0, (char **) &quick_unread_count,
349     "quick-save", BOOL 0, (char **) &quick_save,
350     "re-layout", INT 0, (char **) &re_layout,
351     "re-layout-read", INT 0, (char **) &re_layout_more,
352     "read-return-next-page", BOOL 0, (char **) &read_ret_next_page,
353     "record", SPEC 1, (char **) NULL,
354     "repeat", BOOL 0, (char **) &fmt_rptsubj,
355     "repeat-group-query", BOOL 0, (char **) &repeat_group_query,
356     "report-cost", BOOL 0, (char **) &report_cost_on_exit,
357     "response-check-pause", INT 0, (char **) &response_check_pause,
358     "response-default-answer", STR 0, (char **) &response_dflt_answer,
359     "retain-seen-status", BOOL 0, (char **) &retain_seen_status,
360     "retry-on-error", INT 0, (char **) &retry_on_error,
361     "save-closed-mode", INT 0, (char **) &save_closed_mode,
362     "save-counter", STR 3, (char **) &save_counter_format,
363     "save-counter-offset", INT 0, (char **) &save_counter_offset,
364     "save-header-lines", STR 0, (char **) &save_header_lines,
365     "save-report", BOOL 0, (char **) &save_report,
366     "scroll-clear-page", BOOL 0, (char **) &scroll_clear_page,
367     "scroll-last-lines", INT 0, (char **) &scroll_last_lines,
368     "select-leave-next", BOOL 0, (char **) &select_leave_next,
369     "select-on-sender", BOOL 0, (char **) &select_on_sender,
370     "shading-off", CODES 0, (char **) &shade_off_attr,
371     "shading-on", CODES 1, (char **) &shade_on_attr,
372     "shell", STR SAFE 0, (char **) &user_shell,
373     "shell-restrictions", BOOL INIT 0, (char **) &shell_restrictions,
374     "show-purpose-mode", INT 0, (char **) &show_purpose_mode,
375     "sign-type", STR 0, (char **) &sign_type,
376     "silent", BOOL 0, (char **) &silent,
377     "slow-mode", BOOL 0, (char **) &slow_mode,
378     "slow-speed", INT 0, (char **) &slow_speed,
379     "sort", BOOL 2, (char **) &dont_sort_articles,
380     "sort-mode", INT 0, (char **) &sort_mode,
381     "spell-checker", STR 0, (char **) &spell_checker,
382     "split", BOOL 4, (char **) &dont_split_digests,
383     "stop", INT 0, (char **) &first_page_lines,
384     "subject-match-limit", INT 0, (char **) &subject_match_limit,
385     "subject-match-minimum", INT 0, (char **) &match_parts_begin,
386     "subject-match-offset", INT 0, (char **) &match_skip_prefix,
387     "subject-match-parts", BOOL 0, (char **) &match_parts_equal,
388     "suggest-default-save", BOOL 0, (char **) &suggest_save_file,
389     "tidy-newsrc", BOOL 0, (char **) &tidy_newsrc,
390     "time", BOOL 0, (char **) &show_current_time,
391     "trace-folder-packing", BOOL 0, (char **) &folder_rewrite_trace,
392     "trusted-escape-codes", STR 0, (char **) &trusted_escapes,
393     "unshar-command", STR SAFE 1, (char **) unshar_command,
394     "unshar-header-file", STR 0, (char **) &unshar_header_file,
395     "unsubscribe-mark-read", BOOL 4, (char **) &keep_unsub_long,
396     "update-frequency", INT 0, (char **) &newsrc_update_freq,
397     "use-editor-line", BOOL 0, (char **) &use_ed_line,
398     "use-path-in-from", BOOL 0, (char **) &use_path_in_from,
399     "use-selections", BOOL 0, (char **) &use_selections,
400     "visible-bell", BOOL 0, (char **) &use_visible_bell,
401     "window", INT 1, (char **) &preview_window,
402     "word-key", KEY 0, (char **) &delword_key,
403     "wrap-header-margin", INT 2, (char **) &wrap_headers
404 };
405 
406 #define TABLE_SIZE	(sizeof(variables)/sizeof(struct variable_defs))
407 
408 #define INT_VAR		(*((int *)(var->var_addr)))
409 #define BOOL_VAR	(*((int *)(var->var_addr)))
410 #define STR_VAR		(*(var->var_addr))
411 #define CBUF_VAR	((char *)(var->var_addr))
412 #define KEY_VAR		(*((key_type *)(var->var_addr)))
413 
414 #define VAR_TYPE	(var->var_flags & 0x7000)
415 #define VAR_OP		(var->var_flags & 0x000f)
416 
417 static struct variable_defs *
lookup_variable(char * variable)418 lookup_variable(char *variable)
419 {
420     register struct variable_defs *var;
421     register int    i, j, k, t;
422 
423     i = 0;
424     j = TABLE_SIZE - 1;
425 
426     while (i <= j) {
427 	k = (i + j) / 2;
428 	var = &variables[k];
429 
430 	if ((t = strcmp(variable, var->var_name)) > 0)
431 	    i = k + 1;
432 	else if (t < 0)
433 	    j = k - 1;
434 	else
435 	    return var;
436     }
437 
438     init_message("unknown variable: %s", variable);
439     return NULL;
440 }
441 
442 
443 static char
escaped_char(char c)444 escaped_char(char c)
445 {
446     switch (c) {
447 	    case 'a':
448 	    return 007;
449 	case 'b':
450 	    return BS;
451 	case 'e':
452 	    return 033;
453 	case 'f':
454 	    return '\f';
455 	case 'n':
456 	    return NL;
457 	case 'r':
458 	    return CR;
459 	case 't':
460 	    return TAB;
461     }
462     return c;
463 }
464 
465 static void
adjust(register char * str)466 adjust(register char *str)
467 {
468     register char  *s, *t;
469 
470     if ((s = t = str) == NULL)
471 	return;
472     while (*s && *s != '#') {
473 	if (*s == '\\' && s[1] != NUL) {
474 	    s++;
475 	    *str++ = escaped_char(*s++);
476 	} else if (str == s) {
477 	    str++;
478 	    if (isspace(*s++))
479 		continue;
480 	} else if (isspace(*str++ = *s++))
481 	    continue;
482 	t = str;
483     }
484     *t = NUL;
485 }
486 
487 int
set_variable(char * variable,int on,char * val_string)488 set_variable(char *variable, int on, char *val_string)
489 {
490     int             value;
491     register struct variable_defs *var;
492 
493     if (strncmp(variable, "no", 2) == 0) {
494 	on = !on;
495 	variable += 2;
496 	if (variable[0] == '-')
497 	    variable++;
498     }
499     if ((var = lookup_variable(variable)) == NULL)
500 	return 0;
501 
502     if (!in_init && (var->var_flags & (V_INIT | V_SAFE))) {
503 	if (var->var_flags & V_INIT) {
504 	    msg("'%s' can only be set in the init file", variable);
505 	    return 0;
506 	}
507 	if (shell_restrictions) {
508 	    msg("Restricted operation - cannot change");
509 	    return 0;
510 	}
511     }
512     if (var->var_flags & V_LOCKED) {
513 	msg("Variable '%s' is locked", variable);
514 	return 0;
515     }
516     if (!on || val_string == NULL)
517 	value = 0;
518     else
519 	value = atoi(val_string);
520 
521     var->var_flags |= V_MODIFIED;
522 
523     switch (VAR_TYPE) {
524 
525 	case V_STRING:
526 
527 	    if (on)
528 		adjust(val_string);
529 
530 	    switch (VAR_OP) {
531 		case 0:
532 		    STR_VAR = (on && val_string) ? copy_str(val_string) : (char *) NULL;
533 		    break;
534 
535 		case 1:
536 		    strcpy(CBUF_VAR, (on && val_string) ? val_string : "");
537 		    break;
538 
539 		case 2:
540 		    if (on) {
541 			char            exp_buf[FILENAME];
542 
543 			if (val_string) {
544 			    if (expand_file_name(exp_buf, val_string, 1))
545 				STR_VAR = home_relative(exp_buf);
546 			}
547 		    } else
548 			STR_VAR = (char *) NULL;
549 		    break;
550 
551 		case 3:
552 		case 4:
553 		    if (!on || val_string == NULL) {
554 			msg("Cannot unset string `%s'", variable);
555 			break;
556 		    }
557 		    if (VAR_OP == 4) {
558 			char            exp_buf[FILENAME];
559 			if (expand_file_name(exp_buf, val_string, 1)) {
560 			    STR_VAR = copy_str(exp_buf);
561 			    break;
562 			}
563 		    }
564 		    STR_VAR = copy_str(val_string);
565 		    break;
566 		case 5:
567 		    STR_VAR = (on && val_string) ? copy_str(val_string) : "";
568 		    break;
569 
570 	    }
571 	    break;
572 
573 	case V_BOOLEAN:
574 
575 	    adjust(val_string);
576 	    if (val_string && *val_string != NUL) {
577 		if (val_string[0] == 'o')
578 		    on = val_string[1] == 'n';	/* on */
579 		else
580 		    on = val_string[0] == 't';	/* true */
581 	    }
582 	    switch (VAR_OP) {
583 		case 0:
584 		    BOOL_VAR = on;
585 		    break;
586 
587 		case 1:
588 		    BOOL_VAR = on;
589 		    return 1;
590 
591 		case 2:
592 		    if (BOOL_VAR) {	/* don't change if already ok */
593 			if (!on)
594 			    break;
595 		    } else if (on)
596 			break;
597 
598 		    BOOL_VAR = !on;
599 		    if (!in_init) {
600 			sort_articles(BOOL_VAR ? 0 : -1);
601 			return 1;
602 		    }
603 		    break;
604 
605 		case 4:
606 		    BOOL_VAR = !on;
607 		    break;
608 	    }
609 	    break;
610 
611 	case V_INTEGER:
612 
613 	    switch (VAR_OP) {
614 		case 0:
615 		case 1:
616 		    INT_VAR = value;
617 		    break;
618 
619 		case 2:
620 		case 3:
621 		    if (!on)
622 			value = -1;
623 		    INT_VAR = value;
624 		    break;
625 	    }
626 	    return (VAR_OP & 1);
627 
628 	case V_KEY:
629 	    switch (VAR_OP) {
630 		case 0:
631 		    if (val_string) {
632 			if (*val_string)
633 			    adjust(val_string + 1);	/* #N is valid */
634 			KEY_VAR = parse_key(val_string);
635 		    }
636 		    break;
637 	    }
638 	    break;
639 
640 	case V_SPECIAL:
641 
642 	    switch (VAR_OP) {
643 		case 1:
644 		    if (val_string) {
645 			adjust(val_string);
646 			news_record = home_relative(val_string);
647 			mail_record = news_record;
648 			var->var_flags &= ~V_MODIFIED;
649 			lookup_variable("mail-record")->var_flags |= V_MODIFIED;
650 			lookup_variable("news-record")->var_flags |= V_MODIFIED;
651 		    }
652 		    break;
653 
654 		case 2:
655 		    also_read_articles = on;
656 		    article_limit = (on && value > 0) ? value : -1;
657 		    break;
658 
659 		case 3:
660 		    {
661 			struct chset   *csp;
662 			struct variable_defs *dbvar;
663 
664 			dbvar = lookup_variable("data-bits");
665 
666 			if (on && val_string) {
667 			    if ((csp = getchset(val_string)) == NULL)
668 				msg("Illegal value for `%s' variable", variable);
669 			    else {
670 				curchset = csp;
671 				data_bits = csp->cs_width ? csp->cs_width : 7;
672 				dbvar->var_flags &= ~V_MODIFIED;
673 			    }
674 			} else
675 			    msg("Cannot unset special `%s' variable", variable);
676 		    }
677 		    break;
678 	    }
679 	    break;
680 
681 	case V_CODES:
682 	    {
683 		char            codes[80], code[16], *sp, *cp, *vs;
684 
685 		if (val_string == NULL)
686 		    on = 0;
687 		if (on) {
688 		    adjust(val_string);
689 		    if (val_string[0] == NUL)
690 			on = 0;
691 		}
692 		if (on) {
693 		    sp = codes;
694 		    vs = val_string;
695 		    while (*vs) {
696 			while (*vs && (!isascii(*vs) || isspace(*vs)))
697 			    vs++;
698 			if (*vs == NUL)
699 			    break;
700 			cp = code;
701 			while (*vs && isascii(*vs) && !isspace(*vs))
702 			    *cp++ = *vs++;
703 			*cp = NUL;
704 			*sp++ = parse_key(code);
705 		    }
706 		    *sp = NUL;
707 		    if (codes[0] == NUL)
708 			on = 0;
709 		}
710 		freeobj(code_strings[VAR_OP]);
711 		code_strings[VAR_OP] = on ? copy_str(val_string) : NULL;
712 		STR_VAR = on ? copy_str(codes) : (char *) NULL;
713 		break;
714 	    }
715     }
716     return 0;
717 }
718 
719 void
toggle_variable(char * variable)720 toggle_variable(char *variable)
721 {
722     register struct variable_defs *var;
723 
724     if ((var = lookup_variable(variable)) == NULL)
725 	return;
726     if (VAR_TYPE != V_BOOLEAN) {
727 	init_message("variable %s is not boolean", variable);
728 	return;
729     }
730     BOOL_VAR = !BOOL_VAR;
731 }
732 
733 void
lock_variable(char * variable)734 lock_variable(char *variable)
735 {
736     register struct variable_defs *var;
737 
738     if ((var = lookup_variable(variable)) != NULL)
739 	var->var_flags |= V_LOCKED;
740 }
741 
742 
743 static char    *
var_value(register struct variable_defs * var,char * tag)744 var_value(register struct variable_defs * var, char *tag)
745 {
746     static char     ival[16];
747     register char  *str = NULL;
748     register int    b;
749 
750     if (tag != NULL)
751 	*tag = var_on_stack(var) ? '>' :
752 	    (var->var_flags & V_LOCKED) ? '!' :
753 	    (var->var_flags & V_MODIFIED) ? '*' : ' ';
754 
755     switch (VAR_TYPE) {
756 	case V_STRING:
757 	    str = (VAR_OP == 1) ? CBUF_VAR : STR_VAR;
758 	    break;
759 
760 	case V_BOOLEAN:
761 	    b = BOOL_VAR;
762 	    if (VAR_OP == 2 || VAR_OP == 4)
763 		b = !b;
764 	    str = b ? "on" : "off";
765 	    break;
766 
767 	case V_INTEGER:
768 	    sprintf(ival, "%d", INT_VAR);
769 	    str = ival;
770 	    break;
771 
772 	case V_KEY:
773 	    str = key_name(KEY_VAR);
774 	    break;
775 
776 	case V_SPECIAL:
777 	    str = "UNDEF";
778 	    switch (VAR_OP) {
779 		case 2:
780 		    if (!also_read_articles)
781 			break;
782 		    sprintf(ival, "%d", article_limit);
783 		    str = ival;
784 		    break;
785 		case 3:
786 		    str = curchset->cs_name;
787 		    break;
788 	    }
789 	    break;
790 
791 	case V_CODES:
792 	    str = code_strings[VAR_OP];
793 	    break;
794     }
795     if (str == NULL)
796 	str = "NULL";
797     return str;
798 }
799 
800 int
test_variable(char * expr)801 test_variable(char *expr)
802 {
803     char           *variable;
804     register struct variable_defs *var;
805     int             res = -1;
806 
807     variable = expr;
808     if ((expr = strchr(variable, '=')) == NULL)
809 	goto err;
810 
811     *expr++ = NUL;
812 
813     if ((var = lookup_variable(variable)) == NULL) {
814 	msg("testing unknown variable %s=%s", variable, expr);
815 	goto out;
816     }
817     switch (VAR_TYPE) {
818 
819 	case V_BOOLEAN:
820 	    res = BOOL_VAR;
821 
822 	    if (strcmp(expr, "on") == 0 || strcmp(expr, "true") == 0)
823 		break;
824 	    if (strcmp(expr, "off") == 0 || strcmp(expr, "false") == 0) {
825 		res = !res;
826 		break;
827 	    }
828 	    msg("boolean variables must be tested =on or =off");
829 	    break;
830 
831 	case V_INTEGER:
832 	    res = (INT_VAR == atoi(expr)) ? 1 : 0;
833 	    break;
834 
835 	default:
836 	    msg("%s: cannot only test boolean and integer variables", variable);
837 	    break;
838     }
839 out:
840     *--expr = '=';
841 err:
842     return res;
843 }
844 
845 static int      vc_column;
846 
847 void
var_compl_opts(int col)848 var_compl_opts(int col)
849 {
850     vc_column = col;
851 }
852 
853 int
var_completion(char * path,int index)854 var_completion(char *path, int index)
855 {
856     static char    *head, *tail = NULL;
857     static int      len;
858     static struct variable_defs *var, *help_var;
859 
860     if (index < 0) {
861 	clrmsg(-(index + 1));
862 	return 1;
863     }
864     if (path) {
865 	head = path;
866 	tail = path + index;
867 	while (*head && isspace(*head))
868 	    head++;
869 	if (strncmp(head, "no", 2) == 0) {
870 	    head += 2;
871 	    if (*head == '-')
872 		head++;
873 	}
874 	help_var = var = variables;
875 	len = tail - head;
876 
877 	return 1;
878     }
879     if (index) {
880 	list_completion((char *) NULL);
881 
882 	for (;; help_var++) {
883 	    if (help_var >= &variables[TABLE_SIZE]) {
884 		help_var = variables;
885 		break;
886 	    }
887 	    index = strncmp(help_var->var_name, head, len);
888 	    if (index < 0)
889 		continue;
890 	    if (index > 0) {
891 		help_var = variables;
892 		break;
893 	    }
894 	    if (list_completion(help_var->var_name) == 0)
895 		break;
896 	}
897 	fl;
898 	return 1;
899     }
900     for (; var < &variables[TABLE_SIZE]; var++) {
901 	if (len == 0)
902 	    index = 0;
903 	else
904 	    index = strncmp(var->var_name, head, len);
905 	if (index < 0)
906 	    continue;
907 	if (index > 0)
908 	    break;
909 	sprintf(tail, "%s ", var->var_name + len);
910 	msg("%.70s", var_value(var, (char *) NULL));
911 	gotoxy(prompt_length + vc_column, prompt_line);
912 	var++;
913 	return 1;
914     }
915     clrmsg(vc_column);
916     return 0;
917 }
918 
919 static struct var_stack {
920     struct var_stack *next;
921     struct variable_defs *v;
922     int             mod_flag;
923     union {
924 	int             ivar;
925 	int             bool;
926 	char            key;
927 	char           *str;
928     }               value;
929 }              *var_stack = NULL, *vs_pool = NULL;
930 
931 void
mark_var_stack(void)932 mark_var_stack(void)
933 {
934     register struct var_stack *vs;
935 
936     if (vs_pool) {
937 	vs = vs_pool;
938 	vs_pool = vs->next;
939     } else
940 	vs = newobj(struct var_stack, 1);
941 
942     vs->next = var_stack;
943     var_stack = vs;
944     vs->v = NULL;
945 }
946 
947 int
push_variable(char * variable)948 push_variable(char *variable)
949 {
950     register struct variable_defs *var;
951     register struct var_stack *vs;
952 
953     if (strncmp(variable, "no", 2) == 0) {
954 	variable += 2;
955 	if (variable[0] == '-')
956 	    variable++;
957     }
958     if ((var = lookup_variable(variable)) == NULL) {
959 	msg("pushing unknown variable %s", variable);
960 	return 0;
961     }
962     mark_var_stack();
963     vs = var_stack;
964     vs->v = var;
965     vs->mod_flag = var->var_flags & V_MODIFIED;
966 
967     switch (VAR_TYPE) {
968 
969 	case V_STRING:
970 
971 	    switch (VAR_OP) {
972 		case 0:	/* if we update one of these variables,	 */
973 		case 2:	/* new storage will be allocated for it */
974 		case 3:	/* so it is ok just to save the pointer */
975 		case 4:
976 		    vs->value.str = STR_VAR;
977 		    break;
978 
979 		case 1:	/* we free this memory when restored	 */
980 		    vs->value.str = copy_str(CBUF_VAR);
981 		    break;
982 	    }
983 	    break;
984 
985 	case V_BOOLEAN:
986 	    vs->value.bool = BOOL_VAR;
987 	    break;
988 
989 	case V_INTEGER:
990 	    vs->value.ivar = INT_VAR;
991 	    break;
992 
993 	case V_KEY:
994 	    vs->value.key = KEY_VAR;
995 	    break;
996 
997 	case V_SPECIAL:
998 	    msg("Cannot push pseudo variable %s", var->var_name);
999 	    break;
1000 
1001 	case V_CODES:
1002 	    msg("Cannot push code string variable %s", var->var_name);
1003 	    break;
1004     }
1005 
1006     return 1;
1007 }
1008 
1009 void
restore_variables(void)1010 restore_variables(void)
1011 {
1012     register struct variable_defs *var;
1013     register struct var_stack *vs, *vs1;
1014 
1015     vs = var_stack;
1016 
1017     while (vs != NULL) {
1018 	if ((var = vs->v) == NULL) {
1019 	    var_stack = vs->next;
1020 	    vs->next = vs_pool;
1021 	    vs_pool = vs;
1022 	    return;
1023 	}
1024 	var->var_flags &= ~V_MODIFIED;
1025 	var->var_flags |= vs->mod_flag;
1026 
1027 	switch (VAR_TYPE) {
1028 
1029 	    case V_STRING:
1030 		switch (VAR_OP) {
1031 		    case 0:	/* only restore the string if changed; then
1032 				 * we	 */
1033 		    case 2:	/* can also free the memory occupied by the	 */
1034 		    case 3:	/* 'new' value (if not NULL)			 */
1035 		    case 4:
1036 			if (STR_VAR != vs->value.str) {
1037 			    if (STR_VAR != NULL)
1038 				freeobj(STR_VAR);
1039 			    STR_VAR = vs->value.str;
1040 			}
1041 			break;
1042 
1043 		    case 1:	/* it fitted before, so it will fit againg */
1044 			strcpy(CBUF_VAR, vs->value.str);
1045 			freeobj(vs->value.str);
1046 			break;
1047 		}
1048 		break;
1049 
1050 	    case V_BOOLEAN:
1051 		BOOL_VAR = vs->value.bool;
1052 		break;
1053 
1054 	    case V_INTEGER:
1055 		INT_VAR = vs->value.ivar;
1056 		break;
1057 
1058 	    case V_KEY:
1059 		KEY_VAR = vs->value.key;
1060 		break;
1061 
1062 	    case V_SPECIAL:	/* these are not saved, so... */
1063 		break;
1064 
1065 	    case V_CODES:
1066 		break;
1067 	}
1068 
1069 	vs1 = vs->next;
1070 	vs->next = vs_pool;
1071 	vs_pool = vs;
1072 	vs = vs1;
1073     }
1074     var_stack = NULL;
1075 }
1076 
1077 static int
var_on_stack(register struct variable_defs * var)1078 var_on_stack(register struct variable_defs * var)
1079 {
1080     register struct var_stack *vs;
1081 
1082     for (vs = var_stack; vs; vs = vs->next)
1083 	if (vs->v == var)
1084 	    return 1;
1085     return 0;
1086 }
1087 
1088 void
disp_variables(int all,char * rexp)1089 disp_variables(int all, char *rexp)
1090 {
1091     char           *str, pushed;
1092     register struct variable_defs *var;
1093 
1094     if (in_init)
1095 	return;
1096 
1097     clrdisp();
1098     if (novice && !all && rexp == NULL) {
1099 	msg("Use `:set all' to see all variable settings");
1100 	home();
1101     }
1102     so_printxy(0, 0, "Variable settings");
1103     pg_init(1, 1);
1104     if (rexp) {
1105 	pg_regexp = regcomp(rexp + 1);
1106 	all = 1;
1107     }
1108     for (var = variables; var < &variables[TABLE_SIZE]; var++) {
1109 	if (pg_regexp != NULL && regexec(pg_regexp, var->var_name) == 0)
1110 	    continue;
1111 	str = var_value(var, &pushed);
1112 	if (!all && pushed == ' ')
1113 	    continue;
1114 	if (pg_next() < 0)
1115 	    break;
1116 	if (pg_new_regexp) {
1117 	    pg_new_regexp = 0;
1118 	    var = variables;
1119 	    var--;
1120 	    continue;
1121 	}
1122 	if (VAR_TYPE == V_STRING)
1123 	    tprintf("%c %-25.25s = \"%s\"\n", pushed, var->var_name, str);
1124 	else
1125 	    tprintf("%c %-25.25s = %s\n", pushed, var->var_name, str);
1126     }
1127 
1128     pg_end();
1129 }
1130 
1131 void
print_variable_config(FILE * f,int all)1132 print_variable_config(FILE * f, int all)
1133 {
1134     register struct variable_defs *var;
1135     char           *str, tag[2];
1136 
1137     tag[1] = NUL;
1138     for (var = variables; var < &variables[TABLE_SIZE]; var++) {
1139 	if (!all && (var->var_flags & V_MODIFIED) == 0)
1140 	    continue;
1141 	str = var_value(var, tag);
1142 	if (var->var_name == "nntp-password" || var->var_name == "nntp-user")
1143 	    str = "XXXXXXXX";
1144 	fprintf(f, "%s%s='%s'\n", all ? tag : "", var->var_name, str);
1145     }
1146 }
1147 
1148 /*
1149  *	var_options(string_var *, options, result *)
1150  *
1151  *	test whether "string_var" contains any of the options
1152  *	listed in "options" (NUL-separated list of names ended with
1153  *	double NUL).  On return, string_var is advanced over all
1154  *	recognized options and the result will have the bits corresponding
1155  *	to the recognized options set.
1156  */
1157 
1158 void
var_options(char ** str,register char * options,flag_type * res)1159 var_options(char **str, register char *options, flag_type * res)
1160 {
1161     char            word[128];
1162     char           *optab[32];
1163     register char **op, *wp, c;
1164     register int    n;
1165 
1166     for (op = optab; *options != NUL; op++, options++) {
1167 	*op = options;
1168 	while (*options)
1169 	    options++;
1170     }
1171     *op = NULL;
1172 
1173     *res = 0;
1174 
1175     options = *str;
1176     while (*options) {
1177 	for (wp = word; (c = *options) != NUL; *wp++ = c, options++)
1178 	    if (isascii(c) && isspace(c))
1179 		break;
1180 	while (((c = *options)) && isascii(c) && isspace(c))
1181 	    options++;
1182 	*wp = NUL;
1183 
1184 	for (op = optab, n = 1; *op != NULL; op++, n++) {
1185 	    if (strcmp(word, *op))
1186 		continue;
1187 	    *res |= FLAG(n);
1188 	    *str = options;
1189 	    break;
1190 	}
1191 	if (*op == NULL)
1192 	    break;
1193     }
1194 }
1195