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