xref: /freebsd/contrib/less/opttbl.c (revision 4523eebc6c1828d4fd41d7f1ab943cf079043bc8)
1 /*
2  * Copyright (C) 1984-2026  Mark Nudelman
3  *
4  * You may distribute under the terms of either the GNU General Public
5  * License or the Less License, as specified in the README file.
6  *
7  * For more information, see the README file.
8  */
9 
10 
11 /*
12  * The option table.
13  */
14 
15 #include "less.h"
16 #include "option.h"
17 
18 /*
19  * Variables controlled by command line options.
20  */
21 public int quiet;               /* Should we suppress the audible bell? */
22 public int no_vbell;            /* Should we suppress the visual bell? */
23 public int how_search;          /* Where should forward searches start? */
24 public int top_scroll;          /* Repaint screen from top?
25                                    (alternative is scroll from bottom) */
26 public int pr_type;             /* Type of prompt (short, medium, long) */
27 public int bs_mode;             /* How to process backspaces */
28 public int know_dumb;           /* Don't complain about dumb terminals */
29 public int quit_at_eof;         /* Quit after hitting end of file twice */
30 public int quit_if_one_screen;  /* Quit if EOF on first screen */
31 public int squeeze;             /* Squeeze multiple blank lines into one */
32 public int tabstop;             /* Tab settings */
33 public int back_scroll;         /* Repaint screen on backwards movement */
34 public int forw_scroll;         /* Repaint screen on forward movement */
35 public int caseless;            /* Do "caseless" searches */
36 public int linenums;            /* Use line numbers */
37 public int autobuf;             /* Automatically allocate buffers as needed */
38 public int bufspace;            /* Max buffer space per file (K) */
39 public int ctldisp;             /* Send control chars to screen untranslated */
40 public int force_open;          /* Open the file even if not regular file */
41 public int swindow;             /* Size of scrolling window */
42 public int jump_sline;          /* Screen line of "jump target" */
43 public int jump_sline_arg;
44 public long jump_sline_fraction = -1;
45 public int shift_count;         /* Number of positions to shift horizontally */
46 public long shift_count_fraction = -1;
47 public int chopline;            /* Truncate displayed lines at screen width */
48 public int wordwrap;            /* Wrap lines at space */
49 public int no_init;             /* Disable sending ti/te termcap strings */
50 public int no_keypad;           /* Disable sending ks/ke termcap strings */
51 public int twiddle;             /* Show tildes after EOF */
52 public int show_attn;           /* Hilite first unread line */
53 public int hilite_target;       /* Hilite target line */
54 public int status_col;          /* Display a status column */
55 public int use_lessopen;        /* Use the LESSOPEN filter */
56 public int quit_on_intr;        /* Quit on interrupt */
57 public int follow_mode;         /* F cmd Follows file desc or file name? */
58 public int oldbot;              /* Old bottom of screen behavior {{REMOVE}} */
59 public int opt_use_backslash;   /* Use backslash escaping in option parsing */
60 public LWCHAR rscroll_char;     /* Char which marks chopped lines with -S */
61 public int rscroll_attr;        /* Attribute of rscroll_char */
62 public int no_hist_dups;        /* Remove dups from history list */
63 public int emouse;              /* Enabled mouse features */
64 public int mouse_reverse;       /* Reverse mouse wheel scrolling direction */
65 public int xmouse;              /* Old --mouse option, replaced by --emouse */
66 public int wheel_lines;         /* Number of lines to scroll on mouse wheel scroll */
67 public int perma_marks;         /* Save marks in history file */
68 public int linenum_width;       /* Width of line numbers */
69 public int status_col_width;    /* Width of status column */
70 public int incr_search;         /* Incremental search */
71 public int use_color;           /* Use UI color */
72 public int want_filesize;       /* Scan to EOF if necessary to get file size */
73 public int status_line;         /* Highlight entire marked lines */
74 public int header_lines;        /* Freeze header lines at top of screen */
75 public int header_cols;         /* Freeze header columns at left of screen */
76 public int nonum_headers;       /* Don't give headers line numbers */
77 public int nosearch_header_lines = 0; /* Don't search in header lines */
78 public int nosearch_header_cols = 0; /* Don't search in header columns */
79 public int redraw_on_quit;      /* Redraw last screen after term deinit */
80 public int def_search_type;     /* */
81 public int exit_F_on_close;     /* Exit F command when input closes */
82 public int modelines;           /* Lines to read looking for modelines */
83 public int show_preproc_error;  /* Display msg when preproc exits with error */
84 public int proc_backspace;      /* Special handling of backspace */
85 public int proc_tab;            /* Special handling of tab */
86 public int proc_return;         /* Special handling of carriage return */
87 public int match_shift;         /* Extra horizontal shift on search match */
88 public int no_paste;            /* Don't accept pasted input */
89 public int no_edit_warn;        /* Don't warn when editing a LESSOPENed file */
90 public int stop_on_form_feed;   /* Stop scrolling on a line starting with form feed */
91 public int past_eof;            /* Continue scrolling past EOF */
92 public long match_shift_fraction = NUM_FRAC_DENOM/2; /* 1/2 of screen width */
93 public char intr_char = CONTROL('X'); /* Char to interrupt reads */
94 public char *first_cmd_at_prompt = NULL; /* Command to exec before first prompt */
95 public char *autosave;          /* Actions which do autosave of history file */
96 public char *end_prompt;        /* Printed after clearing the prompt */
97 #if HILITE_SEARCH
98 public int hilite_search;       /* Highlight matched search patterns? */
99 #endif
100 
101 public lbool less_is_more = FALSE; /* Make compatible with POSIX more */
102 
103 /*
104  * Long option names.
105  */
106 static struct optname a_optname      = { "search-skip-screen",   NULL };
107 static struct optname b_optname      = { "buffers",              NULL };
108 static struct optname B__optname     = { "auto-buffers",         NULL };
109 static struct optname c_optname      = { "clear-screen",         NULL };
110 static struct optname d_optname      = { "dumb",                 NULL };
111 static struct optname D__optname     = { "color",                NULL };
112 static struct optname e_optname      = { "quit-at-eof",          NULL };
113 static struct optname f_optname      = { "force",                NULL };
114 static struct optname F__optname     = { "quit-if-one-screen",   NULL };
115 #if HILITE_SEARCH
116 static struct optname g_optname      = { "hilite-search",        NULL };
117 #endif
118 static struct optname h_optname      = { "max-back-scroll",      NULL };
119 static struct optname i_optname      = { "ignore-case",          NULL };
120 static struct optname j_optname      = { "jump-target",          NULL };
121 static struct optname J__optname     = { "status-column",        NULL };
122 #if USERFILE
123 static struct optname k_optname      = { "lesskey-file",         NULL };
124 #if HAVE_LESSKEYSRC
125 static struct optname ks_optname     = { "lesskey-src",          NULL };
126 static struct optname kc_optname     = { "lesskey-content",      NULL };
127 #endif /* HAVE_LESSKEYSRC */
128 #endif
129 static struct optname K__optname     = { "quit-on-intr",         NULL };
130 static struct optname L__optname     = { "no-lessopen",          NULL };
131 static struct optname m_optname      = { "long-prompt",          NULL };
132 static struct optname n_optname      = { "line-numbers",         NULL };
133 #if LOGFILE
134 static struct optname o_optname      = { "log-file",             NULL };
135 static struct optname O__optname     = { "LOG-FILE",             NULL };
136 #endif
137 static struct optname p_optname      = { "pattern",              NULL };
138 static struct optname P__optname     = { "prompt",               NULL };
139 static struct optname q2_optname     = { "silent",               NULL };
140 static struct optname q_optname      = { "quiet",                &q2_optname };
141 static struct optname r_optname      = { "raw-control-chars",    NULL };
142 static struct optname s_optname      = { "squeeze-blank-lines",  NULL };
143 static struct optname S__optname     = { "chop-long-lines",      NULL };
144 #if TAGS
145 static struct optname t_optname      = { "tag",                  NULL };
146 static struct optname T__optname     = { "tag-file",             NULL };
147 #endif
148 static struct optname u_optname      = { "underline-special",    NULL };
149 static struct optname V__optname     = { "version",              NULL };
150 static struct optname w_optname      = { "hilite-unread",        NULL };
151 static struct optname x_optname      = { "tabs",                 NULL };
152 static struct optname X__optname     = { "no-init",              NULL };
153 static struct optname y_optname      = { "max-forw-scroll",      NULL };
154 static struct optname z_optname      = { "window",               NULL };
155 static struct optname quote_optname  = { "quotes",               NULL };
156 static struct optname tilde_optname  = { "tilde",                NULL };
157 static struct optname query_optname  = { "help",                 NULL };
158 static struct optname pound_optname  = { "shift",                NULL };
159 static struct optname keypad_optname = { "no-keypad",            NULL };
160 static struct optname oldbot_optname = { "old-bot",              NULL };
161 static struct optname follow_optname = { "follow-name",          NULL };
162 static struct optname use_backslash_optname = { "use-backslash", NULL };
163 static struct optname rscroll_optname = { "rscroll", NULL };
164 static struct optname nohistdups_optname = { "no-histdups",      NULL };
165 static struct optname mouse_optname = { "mouse",                 NULL };
166 static struct optname emouse_optname = { "emouse",               NULL };
167 static struct optname mouse_reverse_optname = { "rmouse",        NULL };
168 static struct optname wheel_lines_optname = { "wheel-lines",     NULL };
169 static struct optname perma_marks_optname = { "save-marks",      NULL };
170 static struct optname linenum_width_optname = { "line-num-width", NULL };
171 static struct optname status_col_width_optname = { "status-col-width", NULL };
172 static struct optname incr_search_optname = { "incsearch",       NULL };
173 static struct optname use_color_optname = { "use-color",         NULL };
174 static struct optname want_filesize_optname = { "file-size",     NULL };
175 static struct optname status_line_optname = { "status-line",     NULL };
176 static struct optname header_optname = { "header",               NULL };
177 static struct optname no_paste_optname = { "no-paste",           NULL };
178 static struct optname form_feed_optname = { "form-feed",         NULL };
179 static struct optname past_eof_optname = { "past-eof",           NULL };
180 static struct optname no_edit_warn_optname2 = { "no-warn-edit",   NULL };
181 static struct optname no_edit_warn_optname = { "no-edit-warn",   &no_edit_warn_optname2 };
182 static struct optname nonum_headers_optname = { "no-number-headers", NULL };
183 static struct optname nosearch_headers_optname = { "no-search-headers", NULL };
184 static struct optname nosearch_header_lines_optname = { "no-search-header-lines", NULL };
185 static struct optname nosearch_header_cols_optname = { "no-search-header-columns", NULL };
186 static struct optname redraw_on_quit_optname = { "redraw-on-quit", NULL };
187 static struct optname search_type_optname = { "search-options", NULL };
188 static struct optname exit_F_on_close_optname = { "exit-follow-on-close", NULL };
189 static struct optname modelines_optname = { "modelines", NULL };
190 static struct optname no_vbell_optname = { "no-vbell", NULL };
191 static struct optname intr_optname = { "intr", NULL };
192 static struct optname wordwrap_optname = { "wordwrap", NULL };
193 static struct optname show_preproc_error_optname = { "show-preproc-errors", NULL };
194 static struct optname proc_backspace_optname = { "proc-backspace", NULL };
195 static struct optname proc_tab_optname = { "proc-tab", NULL };
196 static struct optname proc_return_optname = { "proc-return", NULL };
197 static struct optname match_shift_optname = { "match-shift", NULL };
198 static struct optname first_cmd_at_prompt_optname = { "cmd", NULL };
199 static struct optname autosave_optname = { "autosave", NULL };
200 static struct optname hilite_target_optname = { "hilite-target", NULL };
201 static struct optname end_prompt_optname = { "end-prompt", NULL };
202 #if LESSTEST
203 static struct optname ttyin_name_optname = { "tty",              NULL };
204 #endif /*LESSTEST*/
205 
206 
207 /*
208  * Table of all options and their semantics.
209  *
210  * For BOOL and TRIPLE options, odesc[0], odesc[1], odesc[2] are
211  * the description of the option when set to 0, 1 or 2, respectively.
212  * For NUMBER options, odesc[0] is the prompt to use when entering
213  * a new value, and odesc[1] is the description, which should contain
214  * one %d which is replaced by the value of the number.
215  * For STRING options, odesc[0] is the prompt to use when entering
216  * a new value, and odesc[1], if not NULL, is a description of the
217  * characters that are valid in the string (see optstring()).
218  */
219 static struct loption option[] =
220 {
221 	{ 'a', &a_optname,
222 		O_TRIPLE, OPT_ONPLUS, &how_search, NULL,
223 		{
224 			LM_Search_includes_displayed_screen,
225 			LM_Search_skips_displayed_screen,
226 			LM_Search_includes_all_of_displayed_screen
227 		},
228 		{ NULL, NULL, NULL }
229 	},
230 
231 	{ 'b', &b_optname,
232 		O_NUMBER|O_INIT_HANDLER, 64, &bufspace, opt_b,
233 		{
234 			LM_Max_buffer_space_per_file,
235 			LM_Max_buffer_space_per_file_X,
236 			LM_NULL
237 		},
238 		{ NULL, NULL, NULL }
239 	},
240 	{ 'B', &B__optname,
241 		O_BOOL, OPT_ON, &autobuf, NULL,
242 		{
243 			LM_Dont_automatically_allocate_buffers,
244 			LM_Automatically_allocate_buffers_when_needed,
245 			LM_NULL
246 		},
247 		{ NULL, NULL, NULL }
248 	},
249 	{ 'c', &c_optname,
250 		O_TRIPLE, OPT_OFF, &top_scroll, NULL,
251 		{
252 			LM_Repaint_by_scrolling_from_bottom_of_screen,
253 			LM_Repaint_by_painting_from_top_of_screen,
254 			LM_Repaint_by_painting_from_top_of_screen,
255 		},
256 		{ NULL, NULL, NULL }
257 	},
258 	{ 'd', &d_optname,
259 		O_BOOL|O_NO_TOGGLE, OPT_OFF, &know_dumb, NULL,
260 		{
261 			LM_Assume_intelligent_terminal,
262 			LM_Assume_dumb_terminal,
263 			LM_NULL
264 		},
265 		{ NULL, NULL, NULL }
266 	},
267 	{ 'D', &D__optname,
268 		O_STRING|O_REPAINT|O_NO_QUERY, 0, NULL, opt_D,
269 		{ LM_color_desc, LM_s, LM_NULL },
270 		{ NULL, NULL, NULL }
271 	},
272 	{ 'e', &e_optname,
273 		O_TRIPLE, OPT_OFF, &quit_at_eof, NULL,
274 		{
275 			LM_Dont_quit_at_end_of_file,
276 			LM_Quit_at_end_of_file,
277 			LM_Quit_immediately_at_end_of_file
278 		},
279 		{ NULL, NULL, NULL }
280 	},
281 	{ 'f', &f_optname,
282 		O_BOOL, OPT_OFF, &force_open, NULL,
283 		{
284 			LM_Open_only_regular_files,
285 			LM_Open_even_non_regular_files,
286 			LM_NULL
287 		},
288 		{ NULL, NULL, NULL }
289 	},
290 	{ 'F', &F__optname,
291 		O_BOOL, OPT_OFF, &quit_if_one_screen, NULL,
292 		{
293 			LM_Dont_quit_if_end_of_file_on_first_screen,
294 			LM_Quit_if_end_of_file_on_first_screen,
295 			LM_NULL
296 		},
297 		{ NULL, NULL, NULL }
298 	},
299 #if HILITE_SEARCH
300 	{ 'g', &g_optname,
301 		O_TRIPLE|O_HL_REPAINT, OPT_ONPLUS, &hilite_search, NULL,
302 		{
303 			LM_Dont_highlight_search_matches,
304 			LM_Highlight_matches_for_previous_search_only,
305 			LM_Highlight_all_matches_for_previous_search_pattern,
306 		},
307 		{ NULL, NULL, NULL }
308 	},
309 #endif
310 	{ 'h', &h_optname,
311 		O_NUMBER, -1, &back_scroll, NULL,
312 		{
313 			LM_Backwards_scroll_limit,
314 			LM_Backwards_scroll_limit_is_X_lines,
315 			LM_NULL
316 		},
317 		{ NULL, NULL, NULL }
318 	},
319 	{ 'i', &i_optname,
320 		O_TRIPLE|O_HL_REPAINT, OPT_OFF, &caseless, opt_i,
321 		{
322 			LM_Case_is_significant_in_searches,
323 			LM_Ignore_case_in_searches,
324 			LM_Ignore_case_in_searches_and_in_patterns,
325 		},
326 		{ NULL, NULL, NULL }
327 	},
328 	{ 'j', &j_optname,
329 		O_STRING, 0, NULL, opt_j,
330 		{
331 			LM_Target_line,
332 			LM_neg_dot_d,
333 			LM_NULL
334 		},
335 		{ NULL, NULL, NULL }
336 	},
337 	{ 'J', &J__optname,
338 		O_BOOL|O_REPAINT, OPT_OFF, &status_col, NULL,
339 		{
340 			LM_Dont_display_a_status_column,
341 			LM_Display_a_status_column,
342 			LM_NULL
343 		},
344 		{ NULL, NULL, NULL }
345 	},
346 #if USERFILE
347 	{ 'k', &k_optname,
348 		O_STRING|O_NO_TOGGLE|O_NO_QUERY, 0, NULL, opt_k,
349 		{ LM_NULL, LM_NULL, LM_NULL },
350 		{ NULL, NULL, NULL }
351 	},
352 #if HAVE_LESSKEYSRC
353 	{ OLETTER_NONE, &kc_optname,
354 		O_STRING|O_NO_TOGGLE|O_NO_QUERY, 0, NULL, opt_kc,
355 		{ LM_NULL, LM_NULL, LM_NULL },
356 		{ NULL, NULL, NULL }
357 	},
358 	{ OLETTER_NONE, &ks_optname,
359 		O_STRING|O_NO_TOGGLE|O_NO_QUERY, 0, NULL, opt_ks,
360 		{ LM_NULL, LM_NULL, LM_NULL },
361 		{ NULL, NULL, NULL }
362 	},
363 #endif /* HAVE_LESSKEYSRC */
364 #endif
365 	{ 'K', &K__optname,
366 		O_BOOL, OPT_OFF, &quit_on_intr, NULL,
367 		{
368 			LM_Dont_quit_if_end_of_file_on_first_screen,
369 			LM_Interrupt_exits_less,
370 			LM_NULL
371 		},
372 		{ NULL, NULL, NULL }
373 	},
374 	{ 'L', &L__optname,
375 		O_BOOL, OPT_ON, &use_lessopen, NULL,
376 		{
377 			LM_Dont_use_the_LESSOPEN_filter,
378 			LM_Use_the_LESSOPEN_filter,
379 			LM_NULL
380 		},
381 		{ NULL, NULL, NULL }
382 	},
383 	{ 'm', &m_optname,
384 		O_TRIPLE, OPT_OFF, &pr_type, NULL,
385 		{
386 			LM_Short_prompt,
387 			LM_Medium_prompt,
388 			LM_Long_prompt,
389 		},
390 		{ NULL, NULL, NULL }
391 	},
392 	{ 'n', &n_optname,
393 		O_TRIPLE|O_REPAINT, OPT_ON, &linenums, NULL,
394 		{
395 			LM_Dont_use_line_numbers,
396 			LM_Use_line_numbers,
397 			LM_Constantly_display_line_numbers,
398 		},
399 		{ NULL, NULL, NULL }
400 	},
401 #if LOGFILE
402 	{ 'o', &o_optname,
403 		O_STRING, 0, NULL, opt_o,
404 		{ LM_log_file, LM_NULL, LM_NULL },
405 		{ NULL, NULL, NULL }
406 	},
407 	{ 'O', &O__optname,
408 		O_STRING, 0, NULL, opt__O,
409 		{ LM_Log_file, LM_NULL, LM_NULL },
410 		{ NULL, NULL, NULL }
411 	},
412 #endif
413 	{ 'p', &p_optname,
414 		O_STRING|O_NO_TOGGLE|O_NO_QUERY, 0, NULL, opt_p,
415 		{ LM_NULL, LM_NULL, LM_NULL },
416 		{ NULL, NULL, NULL }
417 	},
418 	{ 'P', &P__optname,
419 		O_STRING, 0, NULL, opt__P,
420 		{ LM_prompt, LM_NULL, LM_NULL },
421 		{ NULL, NULL, NULL }
422 	},
423 	{ 'q', &q_optname,
424 		O_TRIPLE, OPT_OFF, &quiet, NULL,
425 		{
426 			LM_Ring_the_bell_for_errors_AND_at_eof,
427 			LM_Ring_the_bell_for_errors_but_not_at_eof,
428 			LM_Never_ring_the_bell,
429 		},
430 		{ NULL, NULL, NULL }
431 	},
432 	{ 'r', &r_optname,
433 		O_TRIPLE|O_REPAINT, OPT_OFF, &ctldisp, NULL,
434 		{
435 			LM_Display_control_characters_as_caret,
436 			LM_Display_control_characters_directly,
437 			LM_Display_ANSI_sequences_directly,
438 		},
439 		{ NULL, NULL, NULL }
440 	},
441 	{ 's', &s_optname,
442 		O_BOOL|O_REPAINT, OPT_OFF, &squeeze, NULL,
443 		{
444 			LM_Display_all_blank_lines,
445 			LM_Squeeze_multiple_blank_lines,
446 			LM_NULL
447 		},
448 		{ NULL, NULL, NULL }
449 	},
450 	{ 'S', &S__optname,
451 		O_BOOL|O_REPAINT, OPT_OFF, &chopline, opt__S,
452 		{
453 			LM_Fold_long_lines,
454 			LM_Chop_long_lines,
455 			LM_NULL
456 		},
457 		{ NULL, NULL, NULL }
458 	},
459 #if TAGS
460 	{ 't', &t_optname,
461 		O_STRING|O_NO_QUERY, 0, NULL, opt_t,
462 		{ LM_tag, LM_NULL, LM_NULL },
463 		{ NULL, NULL, NULL }
464 	},
465 	{ 'T', &T__optname,
466 		O_STRING, 0, NULL, opt__T,
467 		{ LM_tags_file, LM_NULL, LM_NULL },
468 		{ NULL, NULL, NULL }
469 	},
470 #endif
471 	{ 'u', &u_optname,
472 		O_TRIPLE|O_REPAINT|O_HL_REPAINT, OPT_OFF, &bs_mode, NULL,
473 		{
474 			LM_Display_underlined_text_in_underline_mode,
475 			LM_Backspaces_cause_overstrike,
476 			LM_Print_backspace_as_caret,
477 		},
478 		{ NULL, NULL, NULL }
479 	},
480 	{ 'V', &V__optname,
481 		O_NOVAR, 0, NULL, opt__V,
482 		{ LM_NULL, LM_NULL, LM_NULL },
483 		{ NULL, NULL, NULL }
484 	},
485 	{ OLETTER_NONE, &hilite_target_optname,
486 		O_BOOL, OPT_OFF, &hilite_target, opt_hilite_target,
487 		{
488 			LM_Dont_highlight_target_line,
489 			LM_Highlight_target_line,
490 			LM_NULL
491 		},
492 		{ NULL, NULL, NULL }
493 	},
494 	{ 'w', &w_optname,
495 		O_TRIPLE|O_REPAINT, OPT_OFF, &show_attn, NULL,
496 		{
497 			LM_Dont_highlight_first_unread_line,
498 			LM_Highlight_first_unread_line_after_forward_screen,
499 			LM_Highlight_first_unread_line_after_any_forward_movement,
500 		},
501 		{ NULL, NULL, NULL }
502 	},
503 	{ 'x', &x_optname,
504 		O_STRING|O_REPAINT, 0, NULL, opt_x,
505 		{ LM_Tab_stops_, LM_d_comma, LM_NULL },
506 		{ NULL, NULL, NULL }
507 	},
508 	{ 'X', &X__optname,
509 		O_BOOL|O_NO_TOGGLE, OPT_OFF, &no_init, NULL,
510 		{
511 			LM_Send_init_deinit_strings_to_terminal,
512 			LM_Dont_use_init_deinit_strings,
513 			LM_NULL
514 		},
515 		{ NULL, NULL, NULL }
516 	},
517 	{ 'y', &y_optname,
518 		O_NUMBER, -1, &forw_scroll, NULL,
519 		{
520 			LM_Forward_scroll_limit,
521 			LM_Forward_scroll_limit_is_X_lines,
522 			LM_NULL
523 		},
524 		{ NULL, NULL, NULL }
525 	},
526 	{ 'z', &z_optname,
527 		O_NUMBER|O_NEGOK, -1, &swindow, NULL,
528 		{
529 			LM_Scroll_window_size,
530 			LM_Scroll_window_size_is_X_lines,
531 			LM_NULL
532 		},
533 		{ NULL, NULL, NULL }
534 	},
535 	{ '"', &quote_optname,
536 		O_STRING, 0, NULL, opt_quote,
537 		{ LM_quotes, LM_s, LM_NULL },
538 		{ NULL, NULL, NULL }
539 	},
540 	{ '~', &tilde_optname,
541 		O_BOOL|O_REPAINT, OPT_ON, &twiddle, NULL,
542 		{
543 			LM_Dont_show_tildes_after_end_of_file,
544 			LM_Show_tildes_after_end_of_file,
545 			LM_NULL
546 		},
547 		{ NULL, NULL, NULL }
548 	},
549 	{ '?', &query_optname,
550 		O_NOVAR, 0, NULL, opt_query,
551 		{ LM_NULL, LM_NULL, LM_NULL },
552 		{ NULL, NULL, NULL }
553 	},
554 	{ '#', &pound_optname,
555 		O_STRING, 0, NULL, opt_shift,
556 		{ LM_Horizontal_shift, LM_dot_d, LM_NULL },
557 		{ NULL, NULL, NULL }
558 	},
559 	{ OLETTER_NONE, &keypad_optname,
560 		O_BOOL|O_NO_TOGGLE, OPT_OFF, &no_keypad, NULL,
561 		{
562 			LM_Use_keypad_mode,
563 			LM_Dont_use_keypad_mode,
564 			LM_NULL
565 		},
566 		{ NULL, NULL, NULL }
567 	},
568 	{ OLETTER_NONE, &oldbot_optname,
569 		O_BOOL, OPT_OFF, &oldbot, NULL,
570 		{
571 			LM_Use_new_bottom_of_screen_behavior,
572 			LM_Use_old_bottom_of_screen_behavior,
573 			LM_NULL
574 		},
575 		{ NULL, NULL, NULL }
576 	},
577 	{ OLETTER_NONE, &follow_optname,
578 		O_BOOL, FOLLOW_DESC, &follow_mode, NULL,
579 		{
580 			LM_F_command_follows_file_descriptor,
581 			LM_F_command_follows_file_name,
582 			LM_NULL
583 		},
584 		{ NULL, NULL, NULL }
585 	},
586 	{ OLETTER_NONE, &use_backslash_optname,
587 		O_BOOL, OPT_OFF, &opt_use_backslash, NULL,
588 		{
589 			LM_Dont_use_backslash_escaping_in_command_line_parameters,
590 			LM_Use_backslash_escaping_in_command_line_parameters,
591 			LM_NULL
592 		},
593 		{ NULL, NULL, NULL }
594 	},
595 	{ OLETTER_NONE, &rscroll_optname,
596 		O_STRING|O_REPAINT|O_INIT_HANDLER, 0, NULL, opt_rscroll,
597 		{ LM_rscroll_character, LM_s, LM_NULL },
598 		{ NULL, NULL, NULL }
599 	},
600 	{ OLETTER_NONE, &nohistdups_optname,
601 		O_BOOL, OPT_OFF, &no_hist_dups, NULL,
602 		{
603 			LM_Allow_duplicates_in_history_list,
604 			LM_Remove_duplicates_from_history_list,
605 			LM_NULL
606 		},
607 		{ NULL, NULL, NULL }
608 	},
609 	{ OLETTER_NONE, &mouse_optname,
610 		O_TRIPLE, OPT_OFF, &xmouse, opt_mouse,
611 		{ LM_NULL, LM_NULL, LM_NULL },
612 		{ NULL, NULL, NULL }
613 	},
614 	{ OLETTER_NONE, &emouse_optname,
615 		O_STRING, 0, NULL, opt_emouse,
616 		{ LM_Mouse_features, LM_s, LM_NULL },
617 		{ NULL, NULL, NULL }
618 	},
619 	{ OLETTER_NONE, &mouse_reverse_optname,
620 		O_BOOL, OPT_OFF, &mouse_reverse, NULL,
621 		{
622 			LM_Normal_mouse_scroll_direction,
623 			LM_Reverse_mouse_scroll_direction,
624 			LM_NULL
625 		},
626 		{ NULL, NULL, NULL }
627 	},
628 	{ OLETTER_NONE, &wheel_lines_optname,
629 		O_NUMBER|O_INIT_HANDLER, 0, &wheel_lines, opt_wheel_lines,
630 		{
631 			LM_Lines_to_scroll_on_mouse_wheel,
632 			LM_Scroll_X_lines_on_mouse_wheel,
633 			LM_NULL
634 		},
635 		{ NULL, NULL, NULL }
636 	},
637 	{ OLETTER_NONE, &perma_marks_optname,
638 		O_BOOL, OPT_OFF, &perma_marks, NULL,
639 		{
640 			LM_Dont_save_marks_in_history_file,
641 			LM_Save_marks_in_history_file,
642 			LM_NULL
643 		},
644 		{ NULL, NULL, NULL }
645 	},
646 	{ OLETTER_NONE, &linenum_width_optname,
647 		O_NUMBER|O_REPAINT, MIN_LINENUM_WIDTH, &linenum_width, opt_linenum_width,
648 		{
649 			LM_Line_number_width,
650 			LM_Line_number_width_is_X_chars,
651 			LM_NULL
652 		},
653 		{ NULL, NULL, NULL }
654 	},
655 	{ OLETTER_NONE, &status_col_width_optname,
656 		O_NUMBER|O_REPAINT, 2, &status_col_width, opt_status_col_width,
657 		{
658 			LM_Status_column_width,
659 			LM_Status_column_width_is_X_chars,
660 			LM_NULL
661 		},
662 		{ NULL, NULL, NULL }
663 	},
664 	{ OLETTER_NONE, &incr_search_optname,
665 		O_BOOL, OPT_OFF, &incr_search, NULL,
666 		{
667 			LM_Incremental_search_is_off,
668 			LM_Incremental_search_is_on,
669 			LM_NULL
670 		},
671 		{ NULL, NULL, NULL }
672 	},
673 	{ OLETTER_NONE, &use_color_optname,
674 		O_BOOL|O_REPAINT, OPT_OFF, &use_color, NULL,
675 		{
676 			LM_Dont_use_color,
677 			LM_Use_color,
678 			LM_NULL
679 		},
680 		{ NULL, NULL, NULL }
681 	},
682 	{ OLETTER_NONE, &want_filesize_optname,
683 		O_BOOL|O_REPAINT, OPT_OFF, &want_filesize, opt_filesize,
684 		{
685 			LM_Dont_get_size_of_each_file,
686 			LM_Get_size_of_each_file,
687 			LM_NULL
688 		},
689 		{ NULL, NULL, NULL }
690 	},
691 	{ OLETTER_NONE, &status_line_optname,
692 		O_BOOL|O_REPAINT, OPT_OFF, &status_line, NULL,
693 		{
694 			LM_Line_highlight_applies_to_text_only,
695 			LM_Line_highlight_applies_to_entire_width_of_screen,
696 			LM_NULL
697 		},
698 		{ NULL, NULL, NULL }
699 	},
700 	{ OLETTER_NONE, &header_optname,
701 		O_STRING|O_REPAINT, 0, NULL, opt_header,
702 		{ LM_Header_lines, LM_d_comma, LM_NULL },
703 		{ NULL, NULL, NULL }
704 	},
705 	{ OLETTER_NONE, &no_paste_optname,
706 		O_BOOL, OPT_OFF, &no_paste, opt_no_paste,
707 		{
708 			LM_Accept_pasted_input,
709 			LM_Ignore_pasted_input,
710 			LM_NULL
711 		},
712 		{ NULL, NULL, NULL }
713 	},
714 	{ OLETTER_NONE, &form_feed_optname,
715 		O_BOOL, OPT_OFF, &stop_on_form_feed, NULL,
716 		{
717 			LM_Dont_stop_on_form_feed,
718 			LM_Stop_on_form_feed,
719 			LM_NULL
720 		},
721 		{ NULL, NULL, NULL }
722 	},
723 	{ OLETTER_NONE, &past_eof_optname,
724 		O_BOOL, OPT_OFF, &past_eof, NULL,
725 		{
726 			LM_Stop_scrolling_at_end_of_file,
727 			LM_Dont_stop_scrolling_at_end_of_file,
728 			LM_NULL
729 		},
730 		{ NULL, NULL, NULL }
731 	},
732 	{ OLETTER_NONE, &no_edit_warn_optname,
733 		O_BOOL, OPT_OFF, &no_edit_warn, NULL,
734 		{
735 			LM_Warn_when_editing_a_file_opened_via_LESSOPEN,
736 			LM_Dont_warn_when_editing_a_file_opened_via_LESSOPEN,
737 			LM_NULL
738 		},
739 		{ NULL, NULL, NULL }
740 	},
741 	{ OLETTER_NONE, &nonum_headers_optname,
742 		O_BOOL|O_REPAINT, 0, &nonum_headers, NULL,
743 		{
744 			LM_Number_header_lines,
745 			LM_Dont_number_header_lines,
746 			LM_NULL
747 		},
748 		{ NULL, NULL, NULL }
749 	},
750 	{ OLETTER_NONE, &nosearch_headers_optname,
751 		O_BOOL|O_HL_REPAINT, 0, NULL, opt_nosearch_headers,
752 		{ LM_NULL, LM_NULL, LM_NULL },
753 		{ NULL, NULL, NULL }
754 	},
755 	{ OLETTER_NONE, &nosearch_header_lines_optname,
756 		O_BOOL|O_HL_REPAINT, 0, NULL, opt_nosearch_header_lines,
757 		{ LM_NULL, LM_NULL, LM_NULL },
758 		{ NULL, NULL, NULL }
759 	},
760 	{ OLETTER_NONE, &nosearch_header_cols_optname,
761 		O_BOOL|O_HL_REPAINT, 0, NULL, opt_nosearch_header_cols,
762 		{ LM_NULL, LM_NULL, LM_NULL },
763 		{ NULL, NULL, NULL }
764 	},
765 	{ OLETTER_NONE, &redraw_on_quit_optname,
766 		O_BOOL, OPT_OFF, &redraw_on_quit, NULL,
767 		{
768 			LM_Dont_redraw_screen_when_quitting,
769 			LM_Redraw_last_screen_when_quitting,
770 			LM_NULL
771 		},
772 		{ NULL, NULL, NULL }
773 	},
774 	{ OLETTER_NONE, &search_type_optname,
775 		O_STRING, 0, NULL, opt_search_type,
776 		{ LM_Search_options, LM_s, LM_NULL },
777 		{ NULL, NULL, NULL }
778 	},
779 	{ OLETTER_NONE, &exit_F_on_close_optname,
780 		O_BOOL, OPT_OFF, &exit_F_on_close, NULL,
781 		{
782 			LM_Dont_exit_F_command_when_input_closes,
783 			LM_Exit_F_command_when_input_closes,
784 			LM_NULL
785 		},
786 		{ NULL, NULL, NULL }
787 	},
788 	{ OLETTER_NONE, &no_vbell_optname,
789 		O_BOOL, OPT_OFF, &no_vbell, NULL,
790 		{
791 			LM_Display_visual_bell,
792 			LM_Dont_display_visual_bell,
793 			LM_NULL
794 		},
795 		{ NULL, NULL, NULL }
796 	},
797 	{ OLETTER_NONE, &modelines_optname,
798 		O_NUMBER, 0, &modelines, NULL,
799 		{
800 			LM_Lines_to_read_looking_for_modelines,
801 			LM_Read_X_lines_looking_for_modelines,
802 			LM_NULL
803 		},
804 		{ NULL, NULL, NULL }
805 	},
806 	{ OLETTER_NONE, &intr_optname,
807 		O_STRING, 0, NULL, opt_intr,
808 		{ LM_interrupt_character, LM_s, LM_NULL },
809 		{ NULL, NULL, NULL }
810 	},
811 	{ OLETTER_NONE, &wordwrap_optname,
812 		O_BOOL|O_REPAINT, OPT_OFF, &wordwrap, NULL,
813 		{
814 			LM_Wrap_lines_at_any_character,
815 			LM_Wrap_lines_at_spaces,
816 			LM_NULL
817 		},
818 		{ NULL, NULL, NULL }
819 	},
820 	{ OLETTER_NONE, &show_preproc_error_optname,
821 		O_BOOL, OPT_OFF, &show_preproc_error, NULL,
822 		{
823 			LM_Dont_show_error_message_if_preprocessor_fails,
824 			LM_Show_error_message_if_preprocessor_fails,
825 			LM_NULL
826 		},
827 		{ NULL, NULL, NULL }
828 	},
829 	{ OLETTER_NONE, &proc_backspace_optname,
830 		O_TRIPLE|O_REPAINT|O_HL_REPAINT, OPT_OFF, &proc_backspace, NULL,
831 		{
832 			LM_Backspace_handling_is_specified_by_the_U_option,
833 			LM_Display_underline_text_in_underline_mode,
834 			LM_Print_backspaces_as_caret,
835 		},
836 		{ NULL, NULL, NULL }
837 	},
838 	{ OLETTER_NONE, &proc_tab_optname,
839 		O_TRIPLE|O_REPAINT|O_HL_REPAINT, OPT_OFF, &proc_tab, NULL,
840 		{
841 			LM_Tab_handling_is_specified_by_the_U_option,
842 			LM_Expand_tabs_to_spaces,
843 			LM_Print_tabs_as_caret,
844 		},
845 		{ NULL, NULL, NULL }
846 	},
847 	{ OLETTER_NONE, &proc_return_optname,
848 		O_TRIPLE|O_REPAINT|O_HL_REPAINT, OPT_OFF, &proc_return, NULL,
849 		{
850 			LM_Carriage_return_handling_is_specified_by_the_U_option,
851 			LM_Delete_carriage_return_before_newline,
852 			LM_Print_carriage_return_as_caret,
853 		},
854 		{ NULL, NULL, NULL }
855 	},
856 	{ OLETTER_NONE, &first_cmd_at_prompt_optname,
857 		O_STRING|O_NO_TOGGLE|O_NO_QUERY, 0, NULL, opt_first_cmd_at_prompt,
858 		{ LM_NULL, LM_NULL, LM_NULL },
859 		{ NULL, NULL, NULL }
860 	},
861 	{ OLETTER_NONE, &match_shift_optname,
862 		O_STRING|O_INIT_HANDLER, 0, NULL, opt_match_shift,
863 		{ LM_Search_match_shift, LM_dot_d, LM_NULL },
864 		{ NULL, NULL, NULL }
865 	},
866 	{ OLETTER_NONE, &autosave_optname,
867 		O_STRING|O_INIT_HANDLER, 0, NULL, opt_autosave,
868 		{ LM_Autosave_actions, LM_s, LM_NULL },
869 		{ NULL, NULL, NULL }
870 	},
871 	{ OLETTER_NONE, &end_prompt_optname,
872 		O_STRING, 0, NULL, opt_end_prompt,
873 		{ LM_Print_after_prompt, LM_s, LM_NULL },
874 		{ NULL, NULL, NULL }
875 	},
876 #if LESSTEST
877 	{ OLETTER_NONE, &ttyin_name_optname,
878 		O_STRING|O_NO_TOGGLE, 0, NULL, opt_ttyin_name,
879 		{ LM_NULL, LM_NULL, LM_NULL },
880 		{ NULL, NULL, NULL }
881 	},
882 #endif /*LESSTEST*/
883 	{ '\0', NULL, O_NOVAR, 0, NULL, NULL,
884 		{ LM_NULL, LM_NULL, LM_NULL },
885 		{ NULL, NULL, NULL }
886 	}
887 };
888 
889 
890 /*
891  * Initialize each option to its default value.
892  */
893 public void init_option(void)
894 {
895 	struct loption *o;
896 	constant char *p;
897 
898 	p = lgetenv("LESS_IS_MORE");
899 	if (!isnullenv(p) && !(p[0] == '0' && p[1] == '\0'))
900 		less_is_more = TRUE;
901 
902 	for (o = option;  o->oletter != '\0';  o++)
903 	{
904 		int i;
905 		for (i = 0;  i < 3;  i++)
906 			o->odesc[i] = (o->odescid[i] == LM_NULL) ? NULL : lmsg(o->odescid[i]);
907 		if (o->ovar != NULL)
908 			*(o->ovar) = o->odefault;
909 		if (o->otype & O_INIT_HANDLER)
910 			(*(o->ofunc))(INIT, (char *) NULL);
911 	}
912 }
913 
914 /*
915  * Find an option in the option table, given its option letter.
916  */
917 public struct loption * findopt(int c)
918 {
919 	struct loption *o;
920 
921 	for (o = option;  o->oletter != '\0';  o++)
922 	{
923 		if (o->oletter == c)
924 			return (o);
925 		if ((o->otype & O_TRIPLE) && ASCII_TO_UPPER(o->oletter) == c)
926 			return (o);
927 	}
928 	return (NULL);
929 }
930 
931 /*
932  *
933  */
934 static lbool is_optchar(char c)
935 {
936 	if (ASCII_IS_UPPER(c))
937 		return TRUE;
938 	if (ASCII_IS_LOWER(c))
939 		return TRUE;
940 	if (c == '-')
941 		return TRUE;
942 	return FALSE;
943 }
944 
945 /*
946  * Find an option in the option table, given its option name.
947  * p_optname is the (possibly partial) name to look for, and
948  * is updated to point after the matched name.
949  * p_oname if non-NULL is set to point to the full option name.
950  */
951 public struct loption * findopt_name(constant char **p_optname, constant char **p_oname, lbool *p_ambig)
952 {
953 	constant char *optname = *p_optname;
954 	struct loption *o;
955 	struct optname *oname;
956 	size_t len;
957 	int uppercase;
958 	struct loption *maxo = NULL;
959 	struct optname *maxoname = NULL;
960 	size_t maxlen = 0;
961 	lbool ambig = FALSE;
962 	lbool exact = FALSE;
963 
964 	/*
965 	 * Check all options.
966 	 */
967 	for (o = option;  o->oletter != '\0';  o++)
968 	{
969 		/*
970 		 * Check all names for this option.
971 		 */
972 		for (oname = o->onames;  oname != NULL;  oname = oname->onext)
973 		{
974 			/*
975 			 * Try normal match first (uppercase == 0),
976 			 * then, then if it's a TRIPLE option,
977 			 * try uppercase match (uppercase == 1).
978 			 */
979 			for (uppercase = 0;  uppercase <= 1;  uppercase++)
980 			{
981 				len = sprefix(optname, oname->oname, uppercase);
982 				if (len == 0 || is_optchar(optname[len]))
983 				{
984 					/*
985 					 * We didn't use all of the option name.
986 					 */
987 					continue;
988 				}
989 				if (!exact && len == maxlen)
990 					/*
991 					 * Already had a partial match,
992 					 * and now there's another one that
993 					 * matches the same length.
994 					 */
995 					ambig = TRUE;
996 				else if (len > maxlen)
997 				{
998 					/*
999 					 * Found a better match than
1000 					 * the one we had.
1001 					 */
1002 					maxo = o;
1003 					maxoname = oname;
1004 					maxlen = len;
1005 					ambig = FALSE;
1006 					exact = (len == strlen(oname->oname));
1007 				}
1008 				if (!(o->otype & O_TRIPLE))
1009 					break;
1010 			}
1011 		}
1012 	}
1013 	if (p_ambig != NULL)
1014 		*p_ambig = ambig;
1015 	if (ambig)
1016 	{
1017 		/*
1018 		 * Name matched more than one option.
1019 		 */
1020 		return (NULL);
1021 	}
1022 	*p_optname = optname + maxlen;
1023 	if (p_oname != NULL)
1024 		*p_oname = maxoname == NULL ? NULL : maxoname->oname;
1025 	return (maxo);
1026 }
1027 
1028 /*
1029  * Find all toggleable options whose names begin with a specified string.
1030  * Return them in a space-separated string.
1031  */
1032 public char * findopts_name(constant char *pfx)
1033 {
1034 	constant struct loption *o;
1035 	constant struct optname *oname;
1036 	struct xbuffer xbuf;
1037 	int uppercase;
1038 
1039 	xbuf_init(&xbuf);
1040 	for (o = option;  o->oletter != '\0';  o++)
1041 	{
1042 		if (o->otype & O_NO_TOGGLE)
1043 			continue;
1044 		for (oname = o->onames;  oname != NULL;  oname = oname->onext)
1045 		{
1046 			for (uppercase = 0;  uppercase <= 1;  uppercase++)
1047 			{
1048 				size_t len = sprefix(pfx, oname->oname, uppercase);
1049 				if (len >= strlen(pfx))
1050 				{
1051 					constant char *np;
1052 					for (np = oname->oname;  *np != '\0';  np++)
1053 						xbuf_add_char(&xbuf, uppercase && ASCII_IS_LOWER(*np) ? ASCII_TO_UPPER(*np) : *np);
1054 					xbuf_add_char(&xbuf, ' ');
1055 				}
1056 				if (!(o->otype & O_TRIPLE))
1057 					break;
1058 			}
1059 		}
1060 	}
1061 	xbuf_pop(&xbuf); /* remove final space */
1062 	xbuf_add_char(&xbuf, '\0');
1063 	return (char *) xbuf.data;
1064 }
1065