xref: /freebsd/contrib/less/optfunc.c (revision 7029da5c36f2d3cf6bb6c81bf551229f416399e8)
1 /*
2  * Copyright (C) 1984-2019  Mark Nudelman
3  *
4  * You may distribute under the terms of either the GNU General Public
5  * License or the Less License, as specified in the README file.
6  *
7  * For more information, see the README file.
8  */
9 
10 
11 /*
12  * Handling functions for command line options.
13  *
14  * Most options are handled by the generic code in option.c.
15  * But all string options, and a few non-string options, require
16  * special handling specific to the particular option.
17  * This special processing is done by the "handling functions" in this file.
18  *
19  * Each handling function is passed a "type" and, if it is a string
20  * option, the string which should be "assigned" to the option.
21  * The type may be one of:
22  *	INIT	The option is being initialized from the command line.
23  *	TOGGLE	The option is being changed from within the program.
24  *	QUERY	The setting of the option is merely being queried.
25  */
26 
27 #include "less.h"
28 #include "option.h"
29 
30 extern int nbufs;
31 extern int bufspace;
32 extern int pr_type;
33 extern int plusoption;
34 extern int swindow;
35 extern int sc_width;
36 extern int sc_height;
37 extern int secure;
38 extern int dohelp;
39 extern int any_display;
40 extern char openquote;
41 extern char closequote;
42 extern char *prproto[];
43 extern char *eqproto;
44 extern char *hproto;
45 extern char *wproto;
46 extern char *every_first_cmd;
47 extern IFILE curr_ifile;
48 extern char version[];
49 extern int jump_sline;
50 extern long jump_sline_fraction;
51 extern int shift_count;
52 extern long shift_count_fraction;
53 extern char rscroll_char;
54 extern int rscroll_attr;
55 extern int mousecap;
56 extern int wheel_lines;
57 extern int less_is_more;
58 #if LOGFILE
59 extern char *namelogfile;
60 extern int force_logfile;
61 extern int logfile;
62 #endif
63 #if TAGS
64 public char *tagoption = NULL;
65 extern char *tags;
66 extern char ztags[];
67 #endif
68 #if MSDOS_COMPILER
69 extern int nm_fg_color, nm_bg_color;
70 extern int bo_fg_color, bo_bg_color;
71 extern int ul_fg_color, ul_bg_color;
72 extern int so_fg_color, so_bg_color;
73 extern int bl_fg_color, bl_bg_color;
74 extern int sgr_mode;
75 #if MSDOS_COMPILER==WIN32C
76 #ifndef COMMON_LVB_UNDERSCORE
77 #define COMMON_LVB_UNDERSCORE 0x8000
78 #endif
79 #endif
80 #endif
81 
82 
83 #if LOGFILE
84 /*
85  * Handler for -o option.
86  */
87 	public void
88 opt_o(type, s)
89 	int type;
90 	char *s;
91 {
92 	PARG parg;
93 	char *filename;
94 
95 	if (secure)
96 	{
97 		error("log file support is not available", NULL_PARG);
98 		return;
99 	}
100 	switch (type)
101 	{
102 	case INIT:
103 		namelogfile = save(s);
104 		break;
105 	case TOGGLE:
106 		if (ch_getflags() & CH_CANSEEK)
107 		{
108 			error("Input is not a pipe", NULL_PARG);
109 			return;
110 		}
111 		if (logfile >= 0)
112 		{
113 			error("Log file is already in use", NULL_PARG);
114 			return;
115 		}
116 		s = skipsp(s);
117 		if (namelogfile != NULL)
118 			free(namelogfile);
119 		filename = lglob(s);
120 		namelogfile = shell_unquote(filename);
121 		free(filename);
122 		use_logfile(namelogfile);
123 		sync_logfile();
124 		break;
125 	case QUERY:
126 		if (logfile < 0)
127 			error("No log file", NULL_PARG);
128 		else
129 		{
130 			parg.p_string = namelogfile;
131 			error("Log file \"%s\"", &parg);
132 		}
133 		break;
134 	}
135 }
136 
137 /*
138  * Handler for -O option.
139  */
140 	public void
141 opt__O(type, s)
142 	int type;
143 	char *s;
144 {
145 	force_logfile = TRUE;
146 	opt_o(type, s);
147 }
148 #endif
149 
150 /*
151  * Handlers for -j option.
152  */
153 	public void
154 opt_j(type, s)
155 	int type;
156 	char *s;
157 {
158 	PARG parg;
159 	char buf[16];
160 	int len;
161 	int err;
162 
163 	switch (type)
164 	{
165 	case INIT:
166 	case TOGGLE:
167 		if (*s == '.')
168 		{
169 			s++;
170 			jump_sline_fraction = getfraction(&s, "j", &err);
171 			if (err)
172 				error("Invalid line fraction", NULL_PARG);
173 			else
174 				calc_jump_sline();
175 		} else
176 		{
177 			int sline = getnum(&s, "j", &err);
178 			if (err)
179 				error("Invalid line number", NULL_PARG);
180 			else
181 			{
182 				jump_sline = sline;
183 				jump_sline_fraction = -1;
184 			}
185 		}
186 		break;
187 	case QUERY:
188 		if (jump_sline_fraction < 0)
189 		{
190 			parg.p_int =  jump_sline;
191 			error("Position target at screen line %d", &parg);
192 		} else
193 		{
194 
195 			sprintf(buf, ".%06ld", jump_sline_fraction);
196 			len = (int) strlen(buf);
197 			while (len > 2 && buf[len-1] == '0')
198 				len--;
199 			buf[len] = '\0';
200 			parg.p_string = buf;
201 			error("Position target at screen position %s", &parg);
202 		}
203 		break;
204 	}
205 }
206 
207 	public void
208 calc_jump_sline(VOID_PARAM)
209 {
210 	if (jump_sline_fraction < 0)
211 		return;
212 	jump_sline = sc_height * jump_sline_fraction / NUM_FRAC_DENOM;
213 }
214 
215 /*
216  * Handlers for -# option.
217  */
218 	public void
219 opt_shift(type, s)
220 	int type;
221 	char *s;
222 {
223 	PARG parg;
224 	char buf[16];
225 	int len;
226 	int err;
227 
228 	switch (type)
229 	{
230 	case INIT:
231 	case TOGGLE:
232 		if (*s == '.')
233 		{
234 			s++;
235 			shift_count_fraction = getfraction(&s, "#", &err);
236 			if (err)
237 				error("Invalid column fraction", NULL_PARG);
238 			else
239 				calc_shift_count();
240 		} else
241 		{
242 			int hs = getnum(&s, "#", &err);
243 			if (err)
244 				error("Invalid column number", NULL_PARG);
245 			else
246 			{
247 				shift_count = hs;
248 				shift_count_fraction = -1;
249 			}
250 		}
251 		break;
252 	case QUERY:
253 		if (shift_count_fraction < 0)
254 		{
255 			parg.p_int = shift_count;
256 			error("Horizontal shift %d columns", &parg);
257 		} else
258 		{
259 
260 			sprintf(buf, ".%06ld", shift_count_fraction);
261 			len = (int) strlen(buf);
262 			while (len > 2 && buf[len-1] == '0')
263 				len--;
264 			buf[len] = '\0';
265 			parg.p_string = buf;
266 			error("Horizontal shift %s of screen width", &parg);
267 		}
268 		break;
269 	}
270 }
271 	public void
272 calc_shift_count(VOID_PARAM)
273 {
274 	if (shift_count_fraction < 0)
275 		return;
276 	shift_count = sc_width * shift_count_fraction / NUM_FRAC_DENOM;
277 }
278 
279 #if USERFILE
280 	public void
281 opt_k(type, s)
282 	int type;
283 	char *s;
284 {
285 	PARG parg;
286 
287 	switch (type)
288 	{
289 	case INIT:
290 		if (lesskey(s, 0))
291 		{
292 			parg.p_string = s;
293 			error("Cannot use lesskey file \"%s\"", &parg);
294 		}
295 		break;
296 	}
297 }
298 #endif
299 
300 #if TAGS
301 /*
302  * Handler for -t option.
303  */
304 	public void
305 opt_t(type, s)
306 	int type;
307 	char *s;
308 {
309 	IFILE save_ifile;
310 	POSITION pos;
311 
312 	switch (type)
313 	{
314 	case INIT:
315 		tagoption = save(s);
316 		/* Do the rest in main() */
317 		break;
318 	case TOGGLE:
319 		if (secure)
320 		{
321 			error("tags support is not available", NULL_PARG);
322 			break;
323 		}
324 		findtag(skipsp(s));
325 		save_ifile = save_curr_ifile();
326 		/*
327 		 * Try to open the file containing the tag
328 		 * and search for the tag in that file.
329 		 */
330 		if (edit_tagfile() || (pos = tagsearch()) == NULL_POSITION)
331 		{
332 			/* Failed: reopen the old file. */
333 			reedit_ifile(save_ifile);
334 			break;
335 		}
336 		unsave_ifile(save_ifile);
337 		jump_loc(pos, jump_sline);
338 		break;
339 	}
340 }
341 
342 /*
343  * Handler for -T option.
344  */
345 	public void
346 opt__T(type, s)
347 	int type;
348 	char *s;
349 {
350 	PARG parg;
351 	char *filename;
352 
353 	switch (type)
354 	{
355 	case INIT:
356 		tags = save(s);
357 		break;
358 	case TOGGLE:
359 		s = skipsp(s);
360 		if (tags != NULL && tags != ztags)
361 			free(tags);
362 		filename = lglob(s);
363 		tags = shell_unquote(filename);
364 		free(filename);
365 		break;
366 	case QUERY:
367 		parg.p_string = tags;
368 		error("Tags file \"%s\"", &parg);
369 		break;
370 	}
371 }
372 #endif
373 
374 /*
375  * Handler for -p option.
376  */
377 	public void
378 opt_p(type, s)
379 	int type;
380 	char *s;
381 {
382 	switch (type)
383 	{
384 	case INIT:
385 		/*
386 		 * Unget a command for the specified string.
387 		 */
388 		if (less_is_more)
389 		{
390 			/*
391 			 * In "more" mode, the -p argument is a command,
392 			 * not a search string, so we don't need a slash.
393 			 */
394 			every_first_cmd = save(s);
395 		} else
396 		{
397 			plusoption = TRUE;
398 			ungetcc(CHAR_END_COMMAND);
399 			ungetsc(s);
400 			 /*
401 			  * {{ This won't work if the "/" command is
402 			  *    changed or invalidated by a .lesskey file. }}
403 			  */
404 			ungetsc("/");
405 		}
406 		break;
407 	}
408 }
409 
410 /*
411  * Handler for -P option.
412  */
413 	public void
414 opt__P(type, s)
415 	int type;
416 	char *s;
417 {
418 	char **proto;
419 	PARG parg;
420 
421 	switch (type)
422 	{
423 	case INIT:
424 	case TOGGLE:
425 		/*
426 		 * Figure out which prototype string should be changed.
427 		 */
428 		switch (*s)
429 		{
430 		case 's':  proto = &prproto[PR_SHORT];	s++;	break;
431 		case 'm':  proto = &prproto[PR_MEDIUM];	s++;	break;
432 		case 'M':  proto = &prproto[PR_LONG];	s++;	break;
433 		case '=':  proto = &eqproto;		s++;	break;
434 		case 'h':  proto = &hproto;		s++;	break;
435 		case 'w':  proto = &wproto;		s++;	break;
436 		default:   proto = &prproto[PR_SHORT];		break;
437 		}
438 		free(*proto);
439 		*proto = save(s);
440 		break;
441 	case QUERY:
442 		parg.p_string = prproto[pr_type];
443 		error("%s", &parg);
444 		break;
445 	}
446 }
447 
448 /*
449  * Handler for the -b option.
450  */
451 	/*ARGSUSED*/
452 	public void
453 opt_b(type, s)
454 	int type;
455 	char *s;
456 {
457 	switch (type)
458 	{
459 	case INIT:
460 	case TOGGLE:
461 		/*
462 		 * Set the new number of buffers.
463 		 */
464 		ch_setbufspace(bufspace);
465 		break;
466 	case QUERY:
467 		break;
468 	}
469 }
470 
471 /*
472  * Handler for the -i option.
473  */
474 	/*ARGSUSED*/
475 	public void
476 opt_i(type, s)
477 	int type;
478 	char *s;
479 {
480 	switch (type)
481 	{
482 	case TOGGLE:
483 		chg_caseless();
484 		break;
485 	case QUERY:
486 	case INIT:
487 		break;
488 	}
489 }
490 
491 /*
492  * Handler for the -V option.
493  */
494 	/*ARGSUSED*/
495 	public void
496 opt__V(type, s)
497 	int type;
498 	char *s;
499 {
500 	switch (type)
501 	{
502 	case TOGGLE:
503 	case QUERY:
504 		dispversion();
505 		break;
506 	case INIT:
507 		/*
508 		 * Force output to stdout per GNU standard for --version output.
509 		 */
510 		any_display = 1;
511 		putstr("less ");
512 		putstr(version);
513 		putstr(" (");
514 		putstr(pattern_lib_name());
515 		putstr(" regular expressions)\n");
516 		putstr("Copyright (C) 1984-2019  Mark Nudelman\n\n");
517 		putstr("less comes with NO WARRANTY, to the extent permitted by law.\n");
518 		putstr("For information about the terms of redistribution,\n");
519 		putstr("see the file named README in the less distribution.\n");
520 		putstr("Home page: http://www.greenwoodsoftware.com/less\n");
521 		quit(QUIT_OK);
522 		break;
523 	}
524 }
525 
526 #if MSDOS_COMPILER
527 /*
528  * Parse an MSDOS color descriptor.
529  */
530    	static void
531 colordesc(s, fg_color, bg_color)
532 	char *s;
533 	int *fg_color;
534 	int *bg_color;
535 {
536 	int fg, bg;
537 	int err;
538 #if MSDOS_COMPILER==WIN32C
539 	int ul = 0;
540 
541 	if (*s == 'u')
542 	{
543 		ul = COMMON_LVB_UNDERSCORE;
544 		++s;
545 	}
546 #endif
547 	fg = getnum(&s, "D", &err);
548 	if (err)
549 	{
550 #if MSDOS_COMPILER==WIN32C
551 		if (ul)
552 			fg = nm_fg_color;
553 		else
554 #endif
555 		{
556 			error("Missing fg color in -D", NULL_PARG);
557 			return;
558 		}
559 	}
560 	if (*s != '.')
561 		bg = nm_bg_color;
562 	else
563 	{
564 		s++;
565 		bg = getnum(&s, "D", &err);
566 		if (err)
567 		{
568 			error("Missing bg color in -D", NULL_PARG);
569 			return;
570 		}
571 	}
572 #if MSDOS_COMPILER==WIN32C
573 	if (*s == 'u')
574 	{
575 		ul = COMMON_LVB_UNDERSCORE;
576 		++s;
577 	}
578 	fg |= ul;
579 #endif
580 	if (*s != '\0')
581 		error("Extra characters at end of -D option", NULL_PARG);
582 	*fg_color = fg;
583 	*bg_color = bg;
584 }
585 
586 /*
587  * Handler for the -D option.
588  */
589 	/*ARGSUSED*/
590 	public void
591 opt_D(type, s)
592 	int type;
593 	char *s;
594 {
595 	PARG p;
596 
597 	switch (type)
598 	{
599 	case INIT:
600 	case TOGGLE:
601 		switch (*s++)
602 		{
603 		case 'n':
604 			colordesc(s, &nm_fg_color, &nm_bg_color);
605 			break;
606 		case 'd':
607 			colordesc(s, &bo_fg_color, &bo_bg_color);
608 			break;
609 		case 'u':
610 			colordesc(s, &ul_fg_color, &ul_bg_color);
611 			break;
612 		case 'k':
613 			colordesc(s, &bl_fg_color, &bl_bg_color);
614 			break;
615 		case 's':
616 			colordesc(s, &so_fg_color, &so_bg_color);
617 			break;
618 		case 'a':
619 			sgr_mode = !sgr_mode;
620 			break;
621 		default:
622 			error("-D must be followed by n, d, u, k, s or a", NULL_PARG);
623 			break;
624 		}
625 		if (type == TOGGLE)
626 		{
627 			at_enter(AT_STANDOUT);
628 			at_exit();
629 		}
630 		break;
631 	case QUERY:
632 		p.p_string = (sgr_mode) ? "on" : "off";
633 		error("SGR mode is %s", &p);
634 		break;
635 	}
636 }
637 #endif
638 
639 /*
640  * Handler for the -x option.
641  */
642 	public void
643 opt_x(type, s)
644 	int type;
645 	char *s;
646 {
647 	extern int tabstops[];
648 	extern int ntabstops;
649 	extern int tabdefault;
650 	char msg[60+(4*TABSTOP_MAX)];
651 	int i;
652 	PARG p;
653 
654 	switch (type)
655 	{
656 	case INIT:
657 	case TOGGLE:
658 		/* Start at 1 because tabstops[0] is always zero. */
659 		for (i = 1;  i < TABSTOP_MAX;  )
660 		{
661 			int n = 0;
662 			s = skipsp(s);
663 			while (*s >= '0' && *s <= '9')
664 				n = (10 * n) + (*s++ - '0');
665 			if (n > tabstops[i-1])
666 				tabstops[i++] = n;
667 			s = skipsp(s);
668 			if (*s++ != ',')
669 				break;
670 		}
671 		if (i < 2)
672 			return;
673 		ntabstops = i;
674 		tabdefault = tabstops[ntabstops-1] - tabstops[ntabstops-2];
675 		break;
676 	case QUERY:
677 		strcpy(msg, "Tab stops ");
678 		if (ntabstops > 2)
679 		{
680 			for (i = 1;  i < ntabstops;  i++)
681 			{
682 				if (i > 1)
683 					strcat(msg, ",");
684 				sprintf(msg+strlen(msg), "%d", tabstops[i]);
685 			}
686 			sprintf(msg+strlen(msg), " and then ");
687 		}
688 		sprintf(msg+strlen(msg), "every %d spaces",
689 			tabdefault);
690 		p.p_string = msg;
691 		error("%s", &p);
692 		break;
693 	}
694 }
695 
696 
697 /*
698  * Handler for the -" option.
699  */
700 	public void
701 opt_quote(type, s)
702 	int type;
703 	char *s;
704 {
705 	char buf[3];
706 	PARG parg;
707 
708 	switch (type)
709 	{
710 	case INIT:
711 	case TOGGLE:
712 		if (s[0] == '\0')
713 		{
714 			openquote = closequote = '\0';
715 			break;
716 		}
717 		if (s[1] != '\0' && s[2] != '\0')
718 		{
719 			error("-\" must be followed by 1 or 2 chars", NULL_PARG);
720 			return;
721 		}
722 		openquote = s[0];
723 		if (s[1] == '\0')
724 			closequote = openquote;
725 		else
726 			closequote = s[1];
727 		break;
728 	case QUERY:
729 		buf[0] = openquote;
730 		buf[1] = closequote;
731 		buf[2] = '\0';
732 		parg.p_string = buf;
733 		error("quotes %s", &parg);
734 		break;
735 	}
736 }
737 
738 /*
739  * Handler for the --rscroll option.
740  */
741 	/*ARGSUSED*/
742 	public void
743 opt_rscroll(type, s)
744 	int type;
745 	char *s;
746 {
747 	PARG p;
748 
749 	switch (type)
750 	{
751 	case INIT:
752 	case TOGGLE: {
753 		char *fmt;
754 		int attr = AT_STANDOUT;
755 		setfmt(s, &fmt, &attr, "*s>");
756 		if (strcmp(fmt, "-") == 0)
757 		{
758 			rscroll_char = 0;
759 		} else
760 		{
761 			rscroll_char = *fmt ? *fmt : '>';
762 			rscroll_attr = attr;
763 		}
764 		break; }
765 	case QUERY: {
766 		p.p_string = rscroll_char ? prchar(rscroll_char) : "-";
767 		error("rscroll char is %s", &p);
768 		break; }
769 	}
770 }
771 
772 /*
773  * "-?" means display a help message.
774  * If from the command line, exit immediately.
775  */
776 	/*ARGSUSED*/
777 	public void
778 opt_query(type, s)
779 	int type;
780 	char *s;
781 {
782 	switch (type)
783 	{
784 	case QUERY:
785 	case TOGGLE:
786 		error("Use \"h\" for help", NULL_PARG);
787 		break;
788 	case INIT:
789 		dohelp = 1;
790 	}
791 }
792 
793 /*
794  * Handler for the --mouse option.
795  */
796 	/*ARGSUSED*/
797 	public void
798 opt_mousecap(type, s)
799 	int type;
800 	char *s;
801 {
802 	switch (type)
803 	{
804 	case TOGGLE:
805 		if (mousecap == OPT_OFF)
806 			deinit_mouse();
807 		else
808 			init_mouse();
809 		break;
810 	case INIT:
811 	case QUERY:
812 		break;
813 	}
814 }
815 
816 /*
817  * Handler for the --wheel-lines option.
818  */
819 	/*ARGSUSED*/
820 	public void
821 opt_wheel_lines(type, s)
822 	int type;
823 	char *s;
824 {
825 	switch (type)
826 	{
827 	case INIT:
828 	case TOGGLE:
829 		if (wheel_lines <= 0)
830 			wheel_lines = default_wheel_lines();
831 		break;
832 	case QUERY:
833 		break;
834 	}
835 }
836 
837 /*
838  * Get the "screen window" size.
839  */
840 	public int
841 get_swindow(VOID_PARAM)
842 {
843 	if (swindow > 0)
844 		return (swindow);
845 	return (sc_height + swindow);
846 }
847 
848