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