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 **) ¯o_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