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 * Handling functions for command line options.
13 *
14 * Most options are handled by the generic code in option.c.
15 * But all string options, and a few non-string options, require
16 * special handling specific to the particular option.
17 * This special processing is done by the "handling functions" in this file.
18 *
19 * Each handling function is passed a "type" and, if it is a string
20 * option, the string which should be "assigned" to the option.
21 * The type may be one of:
22 * INIT The option is being initialized from the command line.
23 * TOGGLE The option is being changed from within the program.
24 * QUERY The setting of the option is merely being queried.
25 */
26
27 #include "less.h"
28 #include "option.h"
29 #include "position.h"
30
31 extern int bufspace;
32 extern int pr_type;
33 extern lbool plusoption;
34 extern int swindow;
35 extern int sc_width;
36 extern int sc_height;
37 extern lbool dohelp;
38 extern char openquote;
39 extern char closequote;
40 extern char *prproto[];
41 extern char *eprproto[];
42 extern char *eqproto;
43 extern char *hproto;
44 extern char *wproto;
45 extern char *every_first_cmd;
46 extern IFILE curr_ifile;
47 extern char version[];
48 extern int jump_sline_arg;
49 extern int jump_sline;
50 extern long jump_sline_fraction;
51 extern int shift_count;
52 extern long shift_count_fraction;
53 extern int match_shift;
54 extern long match_shift_fraction;
55 extern LWCHAR rscroll_char;
56 extern int rscroll_attr;
57 extern int emouse;
58 extern int mouse_reverse;
59 extern int xmouse;
60 extern int wheel_lines;
61 extern lbool less_is_more;
62 extern int linenum_width;
63 extern int status_col_width;
64 extern int use_color;
65 extern int want_filesize;
66 extern int header_lines;
67 extern int header_cols;
68 extern int def_search_type;
69 extern int chopline;
70 extern int tabstops[];
71 extern int ntabstops;
72 extern int tabdefault;
73 extern int no_paste;
74 extern int hilite_target;
75 extern char intr_char;
76 extern int utf_mode;
77 extern int nosearch_header_lines;
78 extern int nosearch_header_cols;
79 extern POSITION header_start_pos;
80 extern char *init_header;
81 extern char *first_cmd_at_prompt;
82 extern char *autosave;
83 #if LOGFILE
84 extern char *namelogfile;
85 extern lbool force_logfile;
86 extern int logfile;
87 #endif
88 #if TAGS
89 public char *tagoption = NULL;
90 extern char *tags;
91 extern char ztags[];
92 #endif
93 #if LESSTEST
94 extern constant char *ttyin_name;
95 extern lbool is_tty;
96 #endif /*LESSTEST*/
97 #if MSDOS_COMPILER
98 extern int nm_fg_color, nm_bg_color, nm_attr;
99 extern int bo_fg_color, bo_bg_color, bo_attr;
100 extern int ul_fg_color, ul_bg_color, ul_attr;
101 extern int so_fg_color, so_bg_color, so_attr;
102 extern int bl_fg_color, bl_bg_color, bl_attr;
103 extern int sgr_mode;
104 #if MSDOS_COMPILER==WIN32C
105 #ifndef COMMON_LVB_UNDERSCORE
106 #define COMMON_LVB_UNDERSCORE 0x8000
107 #endif
108 #ifndef COMMON_LVB_REVERSE_VIDEO
109 #define COMMON_LVB_REVERSE_VIDEO 0x4000
110 #endif
111 #endif
112 #endif
113
114
115 #if LOGFILE
116 /*
117 * Handler for -o option.
118 */
opt_o(int type,constant char * s)119 public void opt_o(int type, constant char *s)
120 {
121 PARG parg;
122 char *filename;
123
124 if (!secure_allow(SF_LOGFILE))
125 {
126 error(LM(log_file_support_is_not_available), NULL_PARG);
127 return;
128 }
129 switch (type)
130 {
131 case INIT:
132 namelogfile = save(s);
133 break;
134 case TOGGLE:
135 if (ch_getflags() & CH_CANSEEK)
136 {
137 error(LM(Input_is_not_a_pipe), NULL_PARG);
138 return;
139 }
140 if (logfile >= 0)
141 {
142 error(LM(Log_file_is_already_in_use), NULL_PARG);
143 return;
144 }
145 s = skipspc(s);
146 if (namelogfile != NULL)
147 free(namelogfile);
148 filename = lglob(s);
149 namelogfile = shell_unquote(filename);
150 free(filename);
151 use_logfile(namelogfile);
152 sync_logfile();
153 break;
154 case QUERY:
155 if (logfile < 0)
156 error(LM(No_log_file), NULL_PARG);
157 else
158 {
159 parg.p_string = namelogfile;
160 error(LM(Log_file_X), &parg);
161 }
162 break;
163 }
164 }
165
166 /*
167 * Handler for -O option.
168 */
opt__O(int type,constant char * s)169 public void opt__O(int type, constant char *s)
170 {
171 force_logfile = TRUE;
172 opt_o(type, s);
173 }
174 #endif
175
toggle_fraction(int * num,long * frac,constant char * s,constant char * printopt,lbool neg_ok,void (* calc)(void))176 static void toggle_fraction(int *num, long *frac, constant char *s, constant char *printopt, lbool neg_ok, void (*calc)(void))
177 {
178 if (s == NULL)
179 {
180 } else if (*s == '.')
181 {
182 s++;
183 if (!getfraction(&s, frac))
184 {
185 PARG parg;
186 parg.p_string = printopt;
187 error(LM(Invalid_fraction_in_X), &parg);
188 return;
189 }
190 } else
191 {
192 if (!getnumc(&s, printopt, neg_ok, num))
193 return;
194 *frac = -1;
195 }
196 (*calc)();
197 }
198
query_fraction(int value,long fraction,constant char * int_msg,constant char * frac_msg)199 static void query_fraction(int value, long fraction, constant char *int_msg, constant char *frac_msg)
200 {
201 PARG parg;
202
203 if (fraction < 0)
204 {
205 parg.p_int = value;
206 error(int_msg, &parg);
207 } else
208 {
209 char buf[INT_STRLEN_BOUND(long)+2];
210 size_t len;
211 SNPRINTF1(buf, sizeof(buf), ".%06ld", fraction);
212 len = strlen(buf);
213 while (len > 2 && buf[len-1] == '0')
214 len--;
215 buf[len] = '\0';
216 parg.p_string = buf;
217 error(frac_msg, &parg);
218 }
219 }
220
221 /*
222 * Handlers for -j option.
223 */
opt_j(int type,constant char * s)224 public void opt_j(int type, constant char *s)
225 {
226 switch (type)
227 {
228 case INIT:
229 case TOGGLE:
230 toggle_fraction(&jump_sline_arg, &jump_sline_fraction,
231 s, "-j", TRUE, calc_jump_sline);
232 break;
233 case QUERY:
234 query_fraction(jump_sline_arg, jump_sline_fraction,
235 LM(Position_target_at_screen_line_X), LM(Position_target_at_screen_position_X));
236 break;
237 }
238 }
239
calc_jump_sline(void)240 public void calc_jump_sline(void)
241 {
242 jump_sline = jump_sline_arg;
243 /* If jump_sline_fraction is set, calculate jump_sline from it. */
244 if (jump_sline_fraction >= 0)
245 jump_sline = (int) muldiv(sc_height, jump_sline_fraction, NUM_FRAC_DENOM);
246 /* Negative jump_sline means relative to bottom of screen. */
247 if (jump_sline < 0)
248 jump_sline += sc_height;
249 /* If jump_sline is obscured by header, move it after the header. */
250 if (jump_sline <= header_lines)
251 jump_sline = header_lines + 1;
252 /* If jump_sline is past bottom of screen, move it to the bottom. */
253 if (jump_sline >= sc_height)
254 jump_sline = sc_height - 1;
255 }
256
257 /*
258 * Handlers for -# option.
259 */
opt_shift(int type,constant char * s)260 public void opt_shift(int type, constant char *s)
261 {
262 switch (type)
263 {
264 case INIT:
265 case TOGGLE:
266 toggle_fraction(&shift_count, &shift_count_fraction,
267 s, "-#", FALSE, calc_shift_count);
268 break;
269 case QUERY:
270 query_fraction(shift_count, shift_count_fraction,
271 LM(Horizontal_shift_X_columns), LM(Horizontal_shift_X_of_screen_width));
272 break;
273 }
274 }
275
calc_shift_count(void)276 public void calc_shift_count(void)
277 {
278 if (shift_count_fraction < 0)
279 return;
280 shift_count = (int) muldiv(sc_width, shift_count_fraction, NUM_FRAC_DENOM);
281 }
282
283 #if USERFILE
opt_k(int type,constant char * s)284 public void opt_k(int type, constant char *s)
285 {
286 PARG parg;
287
288 switch (type)
289 {
290 case INIT:
291 if (lesskey(s, FALSE))
292 {
293 parg.p_string = s;
294 error(LM(Cannot_use_lesskey_file_X), &parg);
295 }
296 break;
297 }
298 }
299
300 #if HAVE_LESSKEYSRC
opt_ks(int type,constant char * s)301 public void opt_ks(int type, constant char *s)
302 {
303 PARG parg;
304
305 switch (type)
306 {
307 case INIT:
308 if (lesskey_src(s, FALSE))
309 {
310 parg.p_string = s;
311 error(LM(Cannot_use_lesskey_source_file_X), &parg);
312 }
313 break;
314 }
315 }
316
opt_kc(int type,constant char * s)317 public void opt_kc(int type, constant char *s)
318 {
319 switch (type)
320 {
321 case INIT:
322 if (lesskey_content(s, FALSE))
323 {
324 error(LM(Error_in_lesskey_content), NULL_PARG);
325 }
326 break;
327 }
328 }
329
330 #endif /* HAVE_LESSKEYSRC */
331 #endif /* USERFILE */
332
333 /*
334 * Handler for -S option.
335 */
opt__S(int type,constant char * s)336 public void opt__S(int type, constant char *s)
337 {
338 (void)s;
339 switch (type)
340 {
341 case TOGGLE:
342 pos_rehead(TRUE);
343 break;
344 }
345 }
346
347 #if TAGS
348 /*
349 * Handler for -t option.
350 */
opt_t(int type,constant char * s)351 public void opt_t(int type, constant char *s)
352 {
353 IFILE save_ifile;
354 POSITION pos;
355
356 switch (type)
357 {
358 case INIT:
359 tagoption = save(s);
360 /* Do the rest in main() */
361 break;
362 case TOGGLE:
363 if (!secure_allow(SF_TAGS))
364 {
365 error(LM(tags_support_is_not_available), NULL_PARG);
366 break;
367 }
368 findtag(skipspc(s));
369 save_ifile = save_curr_ifile();
370 /*
371 * Try to open the file containing the tag
372 * and search for the tag in that file.
373 */
374 if (edit_tagfile() || (pos = tagsearch()) == NULL_POSITION)
375 {
376 /* Failed: reopen the old file. */
377 reedit_ifile(save_ifile);
378 break;
379 }
380 unsave_ifile(save_ifile);
381 jump_loc(pos, jump_sline);
382 break;
383 }
384 }
385
386 /*
387 * Handler for -T option.
388 */
opt__T(int type,constant char * s)389 public void opt__T(int type, constant char *s)
390 {
391 PARG parg;
392 char *filename;
393
394 switch (type)
395 {
396 case INIT:
397 tags = save(s);
398 break;
399 case TOGGLE:
400 s = skipspc(s);
401 if (tags != NULL && tags != ztags)
402 free(tags);
403 filename = lglob(s);
404 tags = shell_unquote(filename);
405 free(filename);
406 break;
407 case QUERY:
408 parg.p_string = tags;
409 error(LM(Tags_file_X), &parg);
410 break;
411 }
412 }
413 #endif
414
415 /*
416 * Handler for -p option.
417 */
opt_p(int type,constant char * s)418 public void opt_p(int type, constant char *s)
419 {
420 switch (type)
421 {
422 case INIT:
423 /*
424 * Unget a command for the specified string.
425 */
426 if (less_is_more)
427 {
428 /*
429 * In "more" mode, the -p argument is a command,
430 * not a search string, so we don't need a slash.
431 */
432 every_first_cmd = save(s);
433 } else
434 {
435 plusoption = TRUE;
436 /*
437 * {{ This won't work if the "/" command is
438 * changed or invalidated by a .lesskey file. }}
439 */
440 ungetsc("/");
441 ungetsc(s);
442 ungetcc_end_command();
443 }
444 break;
445 }
446 }
447
448 /*
449 * Handler for -P option.
450 */
opt__P(int type,constant char * s)451 public void opt__P(int type, constant char *s)
452 {
453 char **proto;
454 PARG parg;
455
456 switch (type)
457 {
458 case INIT:
459 case TOGGLE:
460 /*
461 * Figure out which prototype string should be changed.
462 */
463 switch (*s)
464 {
465 case 's': proto = &prproto[PR_SHORT]; s++; break;
466 case 'm': proto = &prproto[PR_MEDIUM]; s++; break;
467 case 'M': proto = &prproto[PR_LONG]; s++; break;
468 case '=': proto = &eqproto; s++; break;
469 case 'h': proto = &hproto; s++; break;
470 case 'w': proto = &wproto; s++; break;
471 default: proto = &prproto[PR_SHORT]; break;
472 }
473 free(*proto);
474 *proto = save(s);
475 break;
476 case QUERY:
477 parg.p_string = prproto[pr_type];
478 switch (pr_type)
479 {
480 case PR_MEDIUM: error(LM(Prompt_medium_X), &parg); break;
481 case PR_LONG: error(LM(Prompt_long_X), &parg); break;
482 default: error(LM(Prompt_short_X), &parg); break;
483 }
484 break;
485 }
486 }
487
488 /*
489 * Handler for --end-prompt option.
490 */
opt_end_prompt(int type,constant char * s)491 public void opt_end_prompt(int type, constant char *s)
492 {
493 char **pend;
494 PARG parg;
495
496 switch (type)
497 {
498 case INIT:
499 case TOGGLE:
500 switch (*s)
501 {
502 case 's': pend = &eprproto[PR_SHORT]; s++; break;
503 case 'm': pend = &eprproto[PR_MEDIUM]; s++; break;
504 case 'M': pend = &eprproto[PR_LONG]; s++; break;
505 default: pend = &eprproto[PR_SHORT]; break;
506 }
507 if (*pend != NULL)
508 free(*pend);
509 *pend = (strcmp(s, "-") == 0) ? NULL : save(s);
510 break;
511 case QUERY:
512 parg.p_string = eprproto[pr_type];
513 if (parg.p_string == NULL)
514 parg.p_string = LM(nothing);
515 switch (pr_type)
516 {
517 case PR_MEDIUM: error(LM(Print_after_medium_prompt_X), &parg); break;
518 case PR_LONG: error(LM(Print_after_long_prompt_X), &parg); break;
519 default: error(LM(Print_after_short_prompt_X), &parg); break;
520 }
521 break;
522 }
523 }
524
525 /*
526 * Handler for --autosave option.
527 */
opt_autosave(int type,constant char * s)528 public void opt_autosave(int type, constant char *s)
529 {
530 PARG parg;
531
532 switch (type)
533 {
534 case INIT:
535 case TOGGLE:
536 if (s == NULL)
537 s = "-";
538 if (autosave != NULL)
539 free(autosave);
540 autosave = save(s);
541 break;
542 case QUERY:
543 parg.p_string = (autosave != NULL) ? autosave : "-";
544 error(LM(Autosave_actions_X), &parg);
545 break;
546 }
547 }
548
549 /*
550 * Handler for the -b option.
551 */
552 /*ARGSUSED*/
opt_b(int type,constant char * s)553 public void opt_b(int type, constant char *s)
554 {
555 (void)s;
556 switch (type)
557 {
558 case INIT:
559 case TOGGLE:
560 /*
561 * Set the new number of buffers.
562 */
563 ch_setbufspace((ssize_t) bufspace);
564 break;
565 case QUERY:
566 break;
567 }
568 }
569
570 /*
571 * Handler for the -i option.
572 */
573 /*ARGSUSED*/
opt_i(int type,constant char * s)574 public void opt_i(int type, constant char *s)
575 {
576 (void)s;
577 switch (type)
578 {
579 case TOGGLE:
580 chg_caseless();
581 break;
582 case QUERY:
583 case INIT:
584 break;
585 }
586 }
587
588 /*
589 * Handler for the -V option.
590 */
591 /*ARGSUSED*/
opt__V(int type,constant char * s)592 public void opt__V(int type, constant char *s)
593 {
594 (void)s;
595 switch (type)
596 {
597 case TOGGLE:
598 case QUERY:
599 dispversion();
600 break;
601 case INIT:
602 set_output(1, TRUE); /* Force output to stdout per GNU standard for --version output. */
603 putstr("less ");
604 putstr(version);
605 putstr(" (");
606 putstr(pattern_lib_name());
607 putstr(" regular expressions)\n");
608 {
609 char constant *copyright =
610 "Copyright (C) 1984-2026 Mark Nudelman\n\n";
611 putstr(copyright);
612 }
613 if (version[strlen(version)-1] == 'x')
614 {
615 putstr("** This is an EXPERIMENTAL build of the 'less' software,\n");
616 putstr("** and may not function correctly.\n");
617 putstr("** Obtain release builds from the web page below.\n\n");
618 }
619 #if LESSTEST
620 putstr("This build supports LESSTEST.\n");
621 #endif /*LESSTEST*/
622 putstr("less comes with NO WARRANTY, to the extent permitted by law.\n");
623 putstr("For information about the terms of redistribution,\n");
624 putstr("see the file named README in the less distribution.\n");
625 putstr("Home page: https://greenwoodsoftware.com/less\n");
626 quit(QUIT_OK);
627 break;
628 }
629 }
630
631 #if MSDOS_COMPILER
632 /*
633 * Parse an MSDOS color descriptor.
634 */
colordesc(constant char * s,int * fg_color,int * bg_color,int * dattr)635 static void colordesc(constant char *s, int *fg_color, int *bg_color, int *dattr)
636 {
637 int fg, bg;
638 CHAR_ATTR attr;
639 if (parse_color(s, &fg, &bg, &attr) == CT_NULL)
640 {
641 PARG p;
642 p.p_string = s;
643 error(LM(Invalid_color_string_X), &p);
644 } else
645 {
646 *fg_color = fg;
647 *bg_color = bg;
648 *dattr = 0;
649 #if MSDOS_COMPILER==WIN32C
650 if (attr & CATTR_UNDERLINE)
651 *dattr |= COMMON_LVB_UNDERSCORE;
652 if (attr & CATTR_STANDOUT)
653 *dattr |= COMMON_LVB_REVERSE_VIDEO;
654 #endif
655 }
656 }
657 #endif
658
color_from_namechar(char namechar)659 static int color_from_namechar(char namechar)
660 {
661 switch (namechar)
662 {
663 case 'B': return AT_COLOR_BIN;
664 case 'C': return AT_COLOR_CTRL;
665 case 'E': return AT_COLOR_ERROR;
666 case 'H': return AT_COLOR_HEADER;
667 case 'J': return AT_COLOR_TARGET;
668 case 'M': return AT_COLOR_MARK;
669 case 'N': return AT_COLOR_LINENUM;
670 case 'O': return AT_COLOR_OSC8;
671 case 'P': return AT_COLOR_PROMPT;
672 case 'R': return AT_COLOR_RSCROLL;
673 case 'S': return AT_COLOR_SEARCH;
674 case 'T': return AT_COLOR_TILDE;
675 case 'W': case 'A': return AT_COLOR_ATTN;
676 case 'n': return AT_NORMAL;
677 case 's': return AT_STANDOUT;
678 case 'd': return AT_BOLD;
679 case 'u': return AT_UNDERLINE;
680 case 'k': return AT_BLINK;
681 default:
682 if (namechar >= '1' && namechar <= '0'+NUM_SEARCH_COLORS)
683 return AT_COLOR_SUBSEARCH(namechar-'0');
684 return -1;
685 }
686 }
687
688 /*
689 * Handler for the -D option.
690 */
691 /*ARGSUSED*/
opt_D(int type,constant char * s)692 public void opt_D(int type, constant char *s)
693 {
694 PARG p;
695 int attr;
696
697 switch (type)
698 {
699 case INIT:
700 case TOGGLE:
701 #if MSDOS_COMPILER
702 if (*s == 'a')
703 {
704 sgr_mode = !sgr_mode;
705 if (type == TOGGLE)
706 {
707 p.p_string = (sgr_mode) ? LM(on) : LM(off);
708 error(LM(SGR_mode_is_X), &p);
709 }
710 break;
711 }
712 #endif
713 attr = color_from_namechar(s[0]);
714 if (attr < 0)
715 {
716 p.p_char = s[0];
717 error(LM(Invalid_color_specifier_X), &p);
718 return;
719 }
720 if (!use_color && (attr & AT_COLOR))
721 {
722 error(LM(Set__use_color_before_changing_colors), NULL_PARG);
723 return;
724 }
725 s++;
726 #if MSDOS_COMPILER
727 if (!(attr & AT_COLOR))
728 {
729 switch (attr)
730 {
731 case AT_NORMAL:
732 colordesc(s, &nm_fg_color, &nm_bg_color, &nm_attr);
733 break;
734 case AT_BOLD:
735 colordesc(s, &bo_fg_color, &bo_bg_color, &bo_attr);
736 break;
737 case AT_UNDERLINE:
738 colordesc(s, &ul_fg_color, &ul_bg_color, &ul_attr);
739 break;
740 case AT_BLINK:
741 colordesc(s, &bl_fg_color, &bl_bg_color, &bl_attr);
742 break;
743 case AT_STANDOUT:
744 colordesc(s, &so_fg_color, &so_bg_color, &so_attr);
745 break;
746 }
747 if (type == TOGGLE)
748 {
749 init_win_colors();
750 at_enter(AT_STANDOUT);
751 at_exit();
752 }
753 } else
754 #endif
755 if (set_color_map(attr, s) < 0)
756 {
757 p.p_string = s;
758 error(LM(Invalid_color_string_X), &p);
759 return;
760 }
761 break;
762 }
763 }
764
765 /*
766 */
set_tabs(constant char * s,size_t len)767 public void set_tabs(constant char *s, size_t len)
768 {
769 int i;
770 constant char *es = s + len;
771 /* Start at 1 because tabstops[0] is always zero. */
772 for (i = 1; i < TABSTOP_MAX; )
773 {
774 int n = 0;
775 lbool v = FALSE;
776 while (s < es && *s == ' ')
777 s++;
778 for (; s < es && *s >= '0' && *s <= '9'; s++)
779 {
780 v = v || ckd_mul(&n, n, 10);
781 v = v || ckd_add(&n, n, *s - '0');
782 }
783 if (!v && n > tabstops[i-1])
784 tabstops[i++] = n;
785 while (s < es && *s == ' ')
786 s++;
787 if (s == es || *s++ != ',')
788 break;
789 }
790 if (i < 2)
791 return;
792 ntabstops = i;
793 tabdefault = tabstops[ntabstops-1] - tabstops[ntabstops-2];
794 }
795
796 /*
797 * Handler for the -x option.
798 */
opt_x(int type,constant char * s)799 public void opt_x(int type, constant char *s)
800 {
801 size_t msglen;
802 char *msg;
803 int i;
804 PARG p;
805
806 switch (type)
807 {
808 case INIT:
809 case TOGGLE:
810 set_tabs(s, strlen(s));
811 break;
812 case QUERY:
813 msglen = strlen(LM(Tab_stops)) + strlen(LM(and_then)) + strlen(LM(every_X_spaces)) + ((INT_STRLEN_BOUND(int)+1)*ntabstops) + 2;
814 msg = ecalloc(msglen, sizeof(char));
815 strcpy(msg, LM(Tab_stops));
816 if (ntabstops > 2)
817 {
818 for (i = 1; i < ntabstops; i++)
819 {
820 if (i > 1)
821 strcat(msg, ",");
822 sprintf(msg+strlen(msg), "%d", tabstops[i]);
823 }
824 strcat(msg, " ");
825 strcat(msg, LM(and_then));
826 }
827 sprintf(msg+strlen(msg), LM(every_X_spaces), tabdefault);
828 p.p_string = msg;
829 error("%s", &p);
830 free(msg);
831 break;
832 }
833 }
834
835
836 /*
837 * Handler for the -" option.
838 */
opt_quote(int type,constant char * s)839 public void opt_quote(int type, constant char *s)
840 {
841 char buf[3];
842 PARG parg;
843
844 switch (type)
845 {
846 case INIT:
847 case TOGGLE:
848 if (s[0] == '\0')
849 {
850 openquote = closequote = '\0';
851 break;
852 }
853 if (s[1] != '\0' && s[2] != '\0')
854 {
855 error(LM(dq_must_be_followed_by_1_or_2_chars), NULL_PARG);
856 return;
857 }
858 openquote = s[0];
859 if (s[1] == '\0')
860 closequote = openquote;
861 else
862 closequote = s[1];
863 break;
864 case QUERY:
865 buf[0] = openquote;
866 buf[1] = closequote;
867 buf[2] = '\0';
868 parg.p_string = buf;
869 error(LM(quotes_X), &parg);
870 break;
871 }
872 }
873
874 /*
875 * Handler for the --rscroll option.
876 */
877 /*ARGSUSED*/
opt_rscroll(int type,constant char * s)878 public void opt_rscroll(int type, constant char *s)
879 {
880 PARG p;
881
882 switch (type)
883 {
884 case INIT:
885 case TOGGLE: {
886 constant char *fmt;
887 int attr = AT_STANDOUT;
888 setfmt(s, &fmt, &attr, "*s>", FALSE);
889 if (strcmp(fmt, "-") == 0)
890 {
891 rscroll_char = 0;
892 } else
893 {
894 rscroll_attr = attr|AT_COLOR_RSCROLL;
895 if (*fmt == '\0')
896 rscroll_char = '>';
897 else
898 {
899 LWCHAR ch = step_charc(&fmt, +1, fmt+strlen(fmt));
900 if (pwidth(ch, rscroll_attr, 0, 0) > 1)
901 error(LM(cannot_set_rscroll_to_a_wide_character), NULL_PARG);
902 else
903 rscroll_char = ch;
904 }
905 }
906 break; }
907 case QUERY: {
908 p.p_string = rscroll_char == 0 ? "-" : utf_mode ? prutfchar(rscroll_char) : prchar(rscroll_char);
909 error(LM(rscroll_character_is_X), &p);
910 break; }
911 }
912 }
913
914 /*
915 * "-?" means display a help message.
916 * If from the command line, exit immediately.
917 */
918 /*ARGSUSED*/
opt_query(int type,constant char * s)919 public void opt_query(int type, constant char *s)
920 {
921 (void)s;
922 switch (type)
923 {
924 case QUERY:
925 case TOGGLE:
926 error(LM(Use_h_for_help), NULL_PARG);
927 break;
928 case INIT:
929 dohelp = TRUE;
930 }
931 }
932
933 /*ARGSUSED*/
opt_match_shift(int type,constant char * s)934 public void opt_match_shift(int type, constant char *s)
935 {
936 switch (type)
937 {
938 case INIT:
939 case TOGGLE:
940 toggle_fraction(&match_shift, &match_shift_fraction,
941 s, "--match-shift", FALSE, calc_match_shift);
942 break;
943 case QUERY:
944 query_fraction(match_shift, match_shift_fraction,
945 LM(Search_match_shift_is_X), LM(Search_match_shift_is_X_of_screen_width));
946 break;
947 }
948 }
949
calc_match_shift(void)950 public void calc_match_shift(void)
951 {
952 if (match_shift_fraction < 0)
953 return;
954 match_shift = (int) muldiv(sc_width, match_shift_fraction, NUM_FRAC_DENOM);
955 }
956
957 /*
958 * Handler for the --emouse option.
959 */
960 /*ARGSUSED*/
opt_emouse(int type,constant char * s)961 public void opt_emouse(int type, constant char *s)
962 {
963 /* Order of entries matters for QUERY.
964 * Combinations must come after their components. */
965 static struct csl_bitmap_def emouse_defs[] = {
966 { "hscroll", EMOUSE_HSCROLL },
967 { "vscroll", EMOUSE_VSCROLL },
968 { "hdrag", EMOUSE_HDRAG },
969 { "vdrag", EMOUSE_VDRAG },
970 { "lclick", EMOUSE_LCLICK },
971 { "rclick", EMOUSE_RCLICK },
972 { "scroll", EMOUSE_HSCROLL|EMOUSE_VSCROLL },
973 { "drag", EMOUSE_HDRAG|EMOUSE_VDRAG },
974 { "hmove", EMOUSE_HSCROLL|EMOUSE_HDRAG },
975 { "vmove", EMOUSE_VSCROLL|EMOUSE_VDRAG },
976 { "move", EMOUSE_VSCROLL|EMOUSE_VDRAG|EMOUSE_HSCROLL|EMOUSE_HDRAG },
977 { "click", EMOUSE_LCLICK|EMOUSE_RCLICK },
978 { "all", EMOUSE_VSCROLL|EMOUSE_VDRAG|EMOUSE_HSCROLL|EMOUSE_HDRAG|EMOUSE_LCLICK|EMOUSE_RCLICK },
979 };
980
981 switch (type)
982 {
983 case INIT:
984 case TOGGLE: {
985 lbool was_mouse_enabled = (emouse != 0);
986 if (s == NULL || strcmp(s, "-") == 0)
987 emouse = 0;
988 else
989 emouse = parse_csl_bitmap(s,
990 emouse_defs, countof(emouse_defs), "--emouse");
991 if (type == TOGGLE && was_mouse_enabled != (emouse != 0))
992 {
993 if (emouse == 0)
994 deinit_mouse();
995 else
996 init_mouse();
997 }
998 break; }
999 case QUERY: {
1000 char buf[128];
1001 char *bp = buf;
1002 char *ebuf = buf + sizeof(buf);
1003 int qmouse = emouse;
1004 PARG parg;
1005 int i;
1006
1007 for (i = countof(emouse_defs)-1; i >= 0; i--)
1008 {
1009 int bit = emouse_defs[i].bit_value;
1010 if ((qmouse & bit) == bit)
1011 {
1012 if (bp > buf && bp+1 < ebuf)
1013 *bp++ = ',';
1014 strncpy(bp, emouse_defs[i].bit_name, ptr_diff(ebuf, bp));
1015 bp += strlen(bp);
1016 qmouse &= ~bit;
1017 }
1018 }
1019 *bp = '\0';
1020 parg.p_string = buf;
1021 if (buf[0] == '\0')
1022 error(LM(Ignore_mouse_input), NULL_PARG);
1023 else
1024 error(LM(Mouse_features_enabled_X), &parg);
1025 break; }
1026 }
1027 }
1028
1029 /*
1030 * Handler for the --mouse option.
1031 */
1032 /*ARGSUSED*/
opt_mouse(int type,constant char * s)1033 public void opt_mouse(int type, constant char *s)
1034 {
1035 (void)s;
1036 switch (type)
1037 {
1038 case INIT:
1039 case TOGGLE:
1040 if (emouse == 0)
1041 {
1042 opt_emouse(type, "vmove,click");
1043 mouse_reverse = (xmouse == OPT_ONPLUS);
1044 } else
1045 {
1046 opt_emouse(type, "-");
1047 xmouse = 0;
1048 }
1049 if (type == INIT)
1050 break;
1051 /*FALLTHRU*/
1052 case QUERY: /* odesc[] entries are NULL so we can do the QUERY here */
1053 if (emouse == (EMOUSE_VSCROLL|EMOUSE_VDRAG|EMOUSE_LCLICK|EMOUSE_RCLICK))
1054 {
1055 if (mouse_reverse)
1056 error(LM(Use_the_mouse_for_scrolling_vertically_reverse), NULL_PARG);
1057 else
1058 error(LM(Use_the_mouse_for_scrolling_vertically), NULL_PARG);
1059 } else
1060 opt_emouse(QUERY, NULL);
1061 break;
1062 }
1063 }
1064
1065 /*
1066 * Handler for the --wheel-lines option.
1067 */
1068 /*ARGSUSED*/
opt_wheel_lines(int type,constant char * s)1069 public void opt_wheel_lines(int type, constant char *s)
1070 {
1071 (void)s;
1072 switch (type)
1073 {
1074 case INIT:
1075 case TOGGLE:
1076 if (wheel_lines <= 0)
1077 wheel_lines = default_wheel_lines();
1078 break;
1079 case QUERY:
1080 break;
1081 }
1082 }
1083
1084 /*
1085 * Handler for the --line-number-width option.
1086 */
1087 /*ARGSUSED*/
opt_linenum_width(int type,constant char * s)1088 public void opt_linenum_width(int type, constant char *s)
1089 {
1090 PARG parg;
1091
1092 (void)s;
1093 switch (type)
1094 {
1095 case INIT:
1096 case TOGGLE:
1097 if (linenum_width > MAX_LINENUM_WIDTH)
1098 {
1099 parg.p_int = MAX_LINENUM_WIDTH;
1100 error(LM(Line_number_width_must_not_be_larger_than_X), &parg);
1101 linenum_width = MIN_LINENUM_WIDTH;
1102 }
1103 break;
1104 case QUERY:
1105 break;
1106 }
1107 }
1108
1109 /*
1110 * Handler for the --status-column-width option.
1111 */
1112 /*ARGSUSED*/
opt_status_col_width(int type,constant char * s)1113 public void opt_status_col_width(int type, constant char *s)
1114 {
1115 PARG parg;
1116
1117 (void)s;
1118 switch (type)
1119 {
1120 case INIT:
1121 case TOGGLE:
1122 if (status_col_width > MAX_STATUSCOL_WIDTH)
1123 {
1124 parg.p_int = MAX_STATUSCOL_WIDTH;
1125 error(LM(Status_column_width_must_not_be_larger_than_X), &parg);
1126 status_col_width = 2;
1127 }
1128 break;
1129 case QUERY:
1130 break;
1131 }
1132 }
1133
1134 /*
1135 * Handler for the --file-size option.
1136 */
1137 /*ARGSUSED*/
opt_filesize(int type,constant char * s)1138 public void opt_filesize(int type, constant char *s)
1139 {
1140 (void)s;
1141 switch (type)
1142 {
1143 case INIT:
1144 case TOGGLE:
1145 if (want_filesize && curr_ifile != NULL && ch_length() == NULL_POSITION)
1146 scan_eof();
1147 break;
1148 case QUERY:
1149 break;
1150 }
1151 }
1152
1153 /*
1154 * Handler for the --cmd option.
1155 */
1156 /*ARGSUSED*/
opt_first_cmd_at_prompt(int type,constant char * s)1157 public void opt_first_cmd_at_prompt(int type, constant char *s)
1158 {
1159 switch (type)
1160 {
1161 case INIT:
1162 case TOGGLE:
1163 first_cmd_at_prompt = save(s);
1164 break;
1165 case QUERY:
1166 break;
1167 }
1168 }
1169
1170 /*
1171 * Handler for the --intr option.
1172 */
1173 /*ARGSUSED*/
opt_intr(int type,constant char * s)1174 public void opt_intr(int type, constant char *s)
1175 {
1176 PARG p;
1177
1178 switch (type)
1179 {
1180 case INIT:
1181 case TOGGLE:
1182 intr_char = *s;
1183 if (intr_char == '^' && s[1] != '\0')
1184 intr_char = CONTROL(s[1]);
1185 break;
1186 case QUERY: {
1187 p.p_string = prchar((LWCHAR) intr_char);
1188 error(LM(interrupt_character_is_X), &p);
1189 break; }
1190 }
1191 }
1192
1193 /*
1194 * Return the next number from a comma-separated list.
1195 * Return -1 if the list entry is missing or empty.
1196 * Updates *sp to point to the first char of the next number in the list.
1197 */
next_cnum(constant char ** sp,constant char * printopt,mutable int * p_num)1198 static lbool next_cnum(constant char **sp, constant char *printopt, mutable int *p_num)
1199 {
1200 if (**sp == '\0') /* at end of line */
1201 {
1202 *p_num = -1;
1203 return TRUE;
1204 }
1205 if (**sp == ',') /* that's the next comma; we have an empty string */
1206 {
1207 ++(*sp);
1208 *p_num = -1;
1209 return TRUE;
1210 }
1211 if (!getnumc(sp, printopt, FALSE, p_num))
1212 return FALSE;
1213 if (**sp == ',')
1214 ++(*sp);
1215 return TRUE;
1216 }
1217
1218 /*
1219 * Parse a parameter to the --header option.
1220 * Value is "L,C,N", where each field is a decimal number or empty.
1221 */
parse_header(constant char * s,int * lines,int * cols,POSITION * start_pos)1222 static lbool parse_header(constant char *s, int *lines, int *cols, POSITION *start_pos)
1223 {
1224 int n;
1225
1226 if (*s == '-')
1227 s = "0,0";
1228 if (!next_cnum(&s, "--header", &n))
1229 return FALSE;
1230 if (n >= 0) *lines = n;
1231 if (!next_cnum(&s, "--header", &n))
1232 return FALSE;
1233 if (n >= 0) *cols = n;
1234 if (!next_cnum(&s, "--header", &n))
1235 return FALSE;
1236 if (n > 0)
1237 {
1238 if (n < 1) n = 1;
1239 *start_pos = find_pos((LINENUM)n);
1240 }
1241 return TRUE;
1242 }
1243
1244 /*
1245 * Handler for the --header option.
1246 */
1247 /*ARGSUSED*/
opt_header(int type,constant char * s)1248 public void opt_header(int type, constant char *s)
1249 {
1250 switch (type)
1251 {
1252 case INIT:
1253 /* Can't call parse_header now because input file is not yet opened,
1254 * so find_pos won't work. */
1255 init_header = save(s);
1256 break;
1257 case TOGGLE: {
1258 int lines = header_lines;
1259 int cols = header_cols;
1260 POSITION start_pos = (type == INIT) ? ch_zero() : position(TOP);
1261 if (start_pos == NULL_POSITION) start_pos = ch_zero();
1262 if (!parse_header(s, &lines, &cols, &start_pos))
1263 break;
1264 header_lines = lines;
1265 header_cols = cols;
1266 set_header(start_pos);
1267 calc_jump_sline();
1268 break; }
1269 case QUERY: {
1270 char buf[3*INT_STRLEN_BOUND(long)+3];
1271 PARG parg;
1272 SNPRINTF3(buf, sizeof(buf), "%ld,%ld,%ld", (long) header_lines, (long) header_cols, (long) find_linenum(header_start_pos));
1273 parg.p_string = buf;
1274 error(LM(Header_is_X), &parg);
1275 break; }
1276 }
1277 }
1278
1279 /*
1280 * Handler for the --hilite-target option.
1281 */
1282 /*ARGSUSED*/
opt_hilite_target(int type,constant char * s)1283 public void opt_hilite_target(int type, constant char *s)
1284 {
1285 (void)s;
1286 switch (type)
1287 {
1288 case INIT:
1289 break;
1290 case TOGGLE:
1291 draw_target_attn(hilite_target != OPT_OFF);
1292 break;
1293 case QUERY:
1294 break;
1295 }
1296 }
1297
1298 /*
1299 * Handler for the --search-options option.
1300 */
1301 /*ARGSUSED*/
opt_search_type(int type,constant char * s)1302 public void opt_search_type(int type, constant char *s)
1303 {
1304 int st;
1305 PARG parg;
1306 char buf[16];
1307 char *bp;
1308 int i;
1309
1310 switch (type)
1311 {
1312 case INIT:
1313 case TOGGLE:
1314 st = 0;
1315 for (; *s != '\0'; s++)
1316 {
1317 switch (*s)
1318 {
1319 case 'E': case 'e': case CONTROL('E'): st |= SRCH_PAST_EOF; break;
1320 case 'F': case 'f': case CONTROL('F'): st |= SRCH_FIRST_FILE; break;
1321 case 'K': case 'k': case CONTROL('K'): st |= SRCH_NO_MOVE; break;
1322 case 'N': case 'n': case CONTROL('N'): st |= SRCH_NO_MATCH; break;
1323 case 'R': case 'r': case CONTROL('R'): st |= SRCH_NO_REGEX; break;
1324 case 'W': case 'w': case CONTROL('W'): st |= SRCH_WRAP; break;
1325 case '-': st = 0; break;
1326 case '^': break;
1327 default:
1328 if (*s >= '1' && *s <= '0'+NUM_SEARCH_COLORS)
1329 {
1330 st |= SRCH_SUBSEARCH(*s-'0');
1331 break;
1332 }
1333 parg.p_char = *s;
1334 error(LM(invalid_search_option_X), &parg);
1335 return;
1336 }
1337 }
1338 def_search_type = norm_search_type(st);
1339 break;
1340 case QUERY:
1341 bp = buf;
1342 if (def_search_type & SRCH_PAST_EOF) *bp++ = 'E';
1343 if (def_search_type & SRCH_FIRST_FILE) *bp++ = 'F';
1344 if (def_search_type & SRCH_NO_MOVE) *bp++ = 'K';
1345 if (def_search_type & SRCH_NO_MATCH) *bp++ = 'N';
1346 if (def_search_type & SRCH_NO_REGEX) *bp++ = 'R';
1347 if (def_search_type & SRCH_WRAP) *bp++ = 'W';
1348 for (i = 1; i <= NUM_SEARCH_COLORS; i++)
1349 if (def_search_type & SRCH_SUBSEARCH(i))
1350 *bp++ = (char) ('0'+i);
1351 if (bp == buf)
1352 *bp++ = '-';
1353 *bp = '\0';
1354 parg.p_string = buf;
1355 error(LM(search_options_X), &parg);
1356 break;
1357 }
1358 }
1359
1360 /*
1361 * Handler for the --no-search-headers, --no-search-header-lines
1362 * and --no-search-header-cols options.
1363 */
do_nosearch_headers(int type,int no_header_lines,int no_header_cols)1364 static void do_nosearch_headers(int type, int no_header_lines, int no_header_cols)
1365 {
1366 switch (type)
1367 {
1368 case INIT:
1369 case TOGGLE:
1370 nosearch_header_lines = no_header_lines;
1371 nosearch_header_cols = no_header_cols;
1372 if (type != TOGGLE) break;
1373 /*FALLTHRU*/
1374 case QUERY:
1375 if (nosearch_header_lines && nosearch_header_cols)
1376 error(LM(Search_does_not_include_header_lines_or_columns), NULL_PARG);
1377 else if (nosearch_header_lines)
1378 error(LM(Search_includes_header_columns_but_not_header_lines), NULL_PARG);
1379 else if (nosearch_header_cols)
1380 error(LM(Search_includes_header_lines_but_not_header_columns), NULL_PARG);
1381 else
1382 error(LM(Search_includes_header_lines_and_columns), NULL_PARG);
1383 }
1384 }
1385
1386 /*ARGSUSED*/
opt_nosearch_headers(int type,constant char * s)1387 public void opt_nosearch_headers(int type, constant char *s)
1388 {
1389 (void)s;
1390 do_nosearch_headers(type, 1, 1);
1391 }
1392
1393 /*ARGSUSED*/
opt_nosearch_header_lines(int type,constant char * s)1394 public void opt_nosearch_header_lines(int type, constant char *s)
1395 {
1396 (void)s;
1397 do_nosearch_headers(type, 1, 0);
1398 }
1399
1400 /*ARGSUSED*/
opt_nosearch_header_cols(int type,constant char * s)1401 public void opt_nosearch_header_cols(int type, constant char *s)
1402 {
1403 (void)s;
1404 do_nosearch_headers(type, 0, 1);
1405 }
1406
1407 /*ARGSUSED*/
opt_no_paste(int type,constant char * s)1408 public void opt_no_paste(int type, constant char *s)
1409 {
1410 (void)s;
1411 switch (type)
1412 {
1413 case TOGGLE:
1414 if (no_paste)
1415 init_bracketed_paste();
1416 else
1417 deinit_bracketed_paste();
1418 break;
1419 case INIT:
1420 case QUERY:
1421 break;
1422 }
1423 }
1424
1425 #if LESSTEST
1426 /*
1427 * Handler for the --tty option.
1428 */
1429 /*ARGSUSED*/
opt_ttyin_name(int type,constant char * s)1430 public void opt_ttyin_name(int type, constant char *s)
1431 {
1432 switch (type)
1433 {
1434 case INIT:
1435 ttyin_name = s;
1436 is_tty = TRUE;
1437 break;
1438 }
1439 }
1440 #endif /*LESSTEST*/
1441
chop_line(void)1442 public int chop_line(void)
1443 {
1444 return (chopline || header_cols > 0 || header_lines > 0);
1445 }
1446
1447 /*
1448 * Get the "screen window" size.
1449 */
get_swindow(void)1450 public int get_swindow(void)
1451 {
1452 if (swindow > 0)
1453 return (swindow);
1454 return (sc_height - header_lines + swindow);
1455 }
1456
1457 /*
1458 * Should an action initiate an autosave?
1459 */
autosave_action(char act)1460 public lbool autosave_action(char act)
1461 {
1462 return strchr(autosave, act) != NULL || strchr(autosave, '*') != NULL;
1463 }
1464