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