xref: /freebsd/contrib/less/optfunc.c (revision d5cb458b4b58b0f0b3c058a32439f232fd5455ca)
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