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