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