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