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