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