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