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
30extern int nbufs;
31extern int bufspace;
32extern int pr_type;
33extern int plusoption;
34extern int swindow;
35extern int sc_width;
36extern int sc_height;
37extern int secure;
38extern int dohelp;
39extern int is_tty;
40extern char openquote;
41extern char closequote;
42extern char *prproto[];
43extern char *eqproto;
44extern char *hproto;
45extern char *wproto;
46extern char *every_first_cmd;
47extern IFILE curr_ifile;
48extern char version[];
49extern int jump_sline;
50extern long jump_sline_fraction;
51extern int shift_count;
52extern long shift_count_fraction;
53extern char rscroll_char;
54extern int rscroll_attr;
55extern int mousecap;
56extern int wheel_lines;
57extern int less_is_more;
58extern int linenum_width;
59extern int status_col_width;
60extern int use_color;
61extern int want_filesize;
62extern int header_lines;
63extern int header_cols;
64extern int def_search_type;
65extern int chopline;
66extern int tabstops[];
67extern int ntabstops;
68extern int tabdefault;
69extern char intr_char;
70#if LOGFILE
71extern char *namelogfile;
72extern int force_logfile;
73extern int logfile;
74#endif
75#if TAGS
76public char *tagoption = NULL;
77extern char *tags;
78extern char ztags[];
79#endif
80#if LESSTEST
81extern char *ttyin_name;
82#endif /*LESSTEST*/
83#if MSDOS_COMPILER
84extern int nm_fg_color, nm_bg_color;
85extern int bo_fg_color, bo_bg_color;
86extern int ul_fg_color, ul_bg_color;
87extern int so_fg_color, so_bg_color;
88extern int bl_fg_color, bl_bg_color;
89extern 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 */
102public 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 */
152public 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 */
162public 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
212public 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 */
222public 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
272public 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
280public 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
297public 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 */
319public 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 */
357public 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 */
386public 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 */
419public 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*/
455public 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*/
475public 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*/
492public 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 */
534static 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
572static 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*/
602public 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 */
677public 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 */
709public 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 */
745public 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*/
784public 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*/
816public 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*/
833public 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*/
853public 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*/
871public 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*/
895public 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*/
919public 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*/
937public 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*/
960public 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*/
1007public 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*/
1070public 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
1082public int chop_line(void)
1083{
1084	return (chopline || header_cols > 0 || header_lines > 0);
1085}
1086
1087/*
1088 * Get the "screen window" size.
1089 */
1090public int get_swindow(void)
1091{
1092	if (swindow > 0)
1093		return (swindow);
1094	return (sc_height - header_lines + swindow);
1095}
1096
1097