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