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