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