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