xref: /openbsd-src/usr.bin/less/optfunc.c (revision 50b7afb2c2c0993b0894d4e34bf857cb13ed9c80)
1 /*
2  * Copyright (C) 1984-2012  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 IFILE curr_ifile;
47 extern char version[];
48 extern int jump_sline;
49 extern int jump_sline_fraction;
50 extern int shift_count;
51 extern int shift_count_fraction;
52 extern int less_is_more;
53 #if LOGFILE
54 extern char *namelogfile;
55 extern int force_logfile;
56 extern int logfile;
57 #endif
58 #if TAGS
59 public char *tagoption = NULL;
60 extern char *tags;
61 #endif
62 #if MSDOS_COMPILER
63 extern int nm_fg_color, nm_bg_color;
64 extern int bo_fg_color, bo_bg_color;
65 extern int ul_fg_color, ul_bg_color;
66 extern int so_fg_color, so_bg_color;
67 extern int bl_fg_color, bl_bg_color;
68 #endif
69 extern char *every_first_cmd;
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 -j option.
136  */
137 	public void
138 opt_j(type, s)
139 	int type;
140 	char *s;
141 {
142 	PARG parg;
143 	char buf[16];
144 	int len;
145 	int err;
146 
147 	switch (type)
148 	{
149 	case INIT:
150 	case TOGGLE:
151 		if (*s == '.')
152 		{
153 			s++;
154 			jump_sline_fraction = getfraction(&s, "j", &err);
155 			if (err)
156 				error("Invalid line fraction", NULL_PARG);
157 			else
158 				calc_jump_sline();
159 		} else
160 		{
161 			int sline = getnum(&s, "j", &err);
162 			if (err)
163 				error("Invalid line number", NULL_PARG);
164 			else
165 			{
166 				jump_sline = sline;
167 				jump_sline_fraction = -1;
168 			}
169 		}
170 		break;
171 	case QUERY:
172 		if (jump_sline_fraction < 0)
173 		{
174 			parg.p_int =  jump_sline;
175 			error("Position target at screen line %d", &parg);
176 		} else
177 		{
178 
179 			snprintf(buf, sizeof(buf), ".%06d", jump_sline_fraction);
180 			len = strlen(buf);
181 			while (len > 2 && buf[len-1] == '0')
182 				len--;
183 			buf[len] = '\0';
184 			parg.p_string = buf;
185 			error("Position target at screen position %s", &parg);
186 		}
187 		break;
188 	}
189 }
190 
191 	public void
192 calc_jump_sline()
193 {
194 	if (jump_sline_fraction < 0)
195 		return;
196 	jump_sline = sc_height * jump_sline_fraction / NUM_FRAC_DENOM;
197 }
198 
199 /*
200  * Handlers for -# option.
201  */
202 	public void
203 opt_shift(type, s)
204 	int type;
205 	char *s;
206 {
207 	PARG parg;
208 	char buf[16];
209 	int len;
210 	int err;
211 
212 	switch (type)
213 	{
214 	case INIT:
215 	case TOGGLE:
216 		if (*s == '.')
217 		{
218 			s++;
219 			shift_count_fraction = getfraction(&s, "#", &err);
220 			if (err)
221 				error("Invalid column fraction", NULL_PARG);
222 			else
223 				calc_shift_count();
224 		} else
225 		{
226 			int hs = getnum(&s, "#", &err);
227 			if (err)
228 				error("Invalid column number", NULL_PARG);
229 			else
230 			{
231 				shift_count = hs;
232 				shift_count_fraction = -1;
233 			}
234 		}
235 		break;
236 	case QUERY:
237 		if (shift_count_fraction < 0)
238 		{
239 			parg.p_int = shift_count;
240 			error("Horizontal shift %d columns", &parg);
241 		} else
242 		{
243 
244 			snprintf(buf, sizeof(buf), ".%06d", shift_count_fraction);
245 			len = strlen(buf);
246 			while (len > 2 && buf[len-1] == '0')
247 				len--;
248 			buf[len] = '\0';
249 			parg.p_string = buf;
250 			error("Horizontal shift %s of screen width", &parg);
251 		}
252 		break;
253 	}
254 }
255 	public void
256 calc_shift_count()
257 {
258 	if (shift_count_fraction < 0)
259 		return;
260 	shift_count = sc_width * shift_count_fraction / NUM_FRAC_DENOM;
261 }
262 
263 #if USERFILE
264 	public void
265 opt_k(type, s)
266 	int type;
267 	char *s;
268 {
269 	PARG parg;
270 
271 	switch (type)
272 	{
273 	case INIT:
274 		if (lesskey(s, 0))
275 		{
276 			parg.p_string = s;
277 			error("Cannot use lesskey file \"%s\"", &parg);
278 		}
279 		break;
280 	}
281 }
282 #endif
283 
284 #if TAGS
285 /*
286  * Handler for -t option.
287  */
288 	public void
289 opt_t(type, s)
290 	int type;
291 	char *s;
292 {
293 	IFILE save_ifile;
294 	POSITION pos;
295 
296 	switch (type)
297 	{
298 	case INIT:
299 		tagoption = s;
300 		/* Do the rest in main() */
301 		break;
302 	case TOGGLE:
303 		if (secure)
304 		{
305 			error("tags support is not available", NULL_PARG);
306 			break;
307 		}
308 		findtag(skipsp(s));
309 		save_ifile = save_curr_ifile();
310 		/*
311 		 * Try to open the file containing the tag
312 		 * and search for the tag in that file.
313 		 */
314 		if (edit_tagfile() || (pos = tagsearch()) == NULL_POSITION)
315 		{
316 			/* Failed: reopen the old file. */
317 			reedit_ifile(save_ifile);
318 			break;
319 		}
320 		unsave_ifile(save_ifile);
321 		jump_loc(pos, jump_sline);
322 		break;
323 	}
324 }
325 
326 /*
327  * Handler for -T option.
328  */
329 	public void
330 opt__T(type, s)
331 	int type;
332 	char *s;
333 {
334 	PARG parg;
335 
336 	switch (type)
337 	{
338 	case INIT:
339 		tags = s;
340 		break;
341 	case TOGGLE:
342 		s = skipsp(s);
343 		tags = lglob(s);
344 		break;
345 	case QUERY:
346 		parg.p_string = tags;
347 		error("Tags file \"%s\"", &parg);
348 		break;
349 	}
350 }
351 #endif
352 
353 /*
354  * Handler for -p option.
355  */
356 	public void
357 opt_p(type, s)
358 	int type;
359 	register char *s;
360 {
361 	switch (type)
362 	{
363 	case INIT:
364 		/*
365 		 * Unget a search command for the specified string.
366 		 * {{ This won't work if the "/" command is
367 		 *    changed or invalidated by a .lesskey file. }}
368 		 */
369 		if (less_is_more) {
370 			/*
371 			 * In "more" mode, the -p argument is a command,
372 			 * not a search string, run for each file.
373 			 */
374 			every_first_cmd = save(s);
375 		} else {
376 			plusoption = TRUE;
377 			ungetsc(s);
378 			ungetsc("/");
379 		}
380 		break;
381 	}
382 }
383 
384 /*
385  * Handler for -P option.
386  */
387 	public void
388 opt__P(type, s)
389 	int type;
390 	register char *s;
391 {
392 	register char **proto;
393 	PARG parg;
394 
395 	switch (type)
396 	{
397 	case INIT:
398 	case TOGGLE:
399 		/*
400 		 * Figure out which prototype string should be changed.
401 		 */
402 		switch (*s)
403 		{
404 		case 's':  proto = &prproto[PR_SHORT];	s++;	break;
405 		case 'm':  proto = &prproto[PR_MEDIUM];	s++;	break;
406 		case 'M':  proto = &prproto[PR_LONG];	s++;	break;
407 		case '=':  proto = &eqproto;		s++;	break;
408 		case 'h':  proto = &hproto;		s++;	break;
409 		case 'w':  proto = &wproto;		s++;	break;
410 		default:   proto = &prproto[PR_SHORT];		break;
411 		}
412 		free(*proto);
413 		*proto = save(s);
414 		break;
415 	case QUERY:
416 		parg.p_string = prproto[pr_type];
417 		error("%s", &parg);
418 		break;
419 	}
420 }
421 
422 /*
423  * Handler for the -b option.
424  */
425 	/*ARGSUSED*/
426 	public void
427 opt_b(type, s)
428 	int type;
429 	char *s;
430 {
431 	switch (type)
432 	{
433 	case INIT:
434 	case TOGGLE:
435 		/*
436 		 * Set the new number of buffers.
437 		 */
438 		ch_setbufspace(bufspace);
439 		break;
440 	case QUERY:
441 		break;
442 	}
443 }
444 
445 /*
446  * Handler for the -i option.
447  */
448 	/*ARGSUSED*/
449 	public void
450 opt_i(type, s)
451 	int type;
452 	char *s;
453 {
454 	switch (type)
455 	{
456 	case TOGGLE:
457 		chg_caseless();
458 		break;
459 	case QUERY:
460 	case INIT:
461 		break;
462 	}
463 }
464 
465 /*
466  * Handler for the -V option.
467  */
468 	/*ARGSUSED*/
469 	public void
470 opt__V(type, s)
471 	int type;
472 	char *s;
473 {
474 	switch (type)
475 	{
476 	case TOGGLE:
477 	case QUERY:
478 		dispversion();
479 		break;
480 	case INIT:
481 		/*
482 		 * Force output to stdout per GNU standard for --version output.
483 		 */
484 		any_display = 1;
485 		putstr("less ");
486 		putstr(version);
487 		putstr(" (");
488 #if HAVE_GNU_REGEX
489 		putstr("GNU ");
490 #endif
491 #if HAVE_POSIX_REGCOMP
492 		putstr("POSIX ");
493 #endif
494 #if HAVE_PCRE
495 		putstr("PCRE ");
496 #endif
497 #if HAVE_RE_COMP
498 		putstr("BSD ");
499 #endif
500 #if HAVE_REGCMP
501 		putstr("V8 ");
502 #endif
503 #if HAVE_V8_REGCOMP
504 		putstr("Spencer V8 ");
505 #endif
506 #if !HAVE_GNU_REGEX && !HAVE_POSIX_REGCOMP && !HAVE_PCRE && !HAVE_RE_COMP && !HAVE_REGCMP && !HAVE_V8_REGCOMP
507 		putstr("no ");
508 #endif
509 		putstr("regular expressions)\n");
510 		putstr("Copyright (C) 1984-2012 Mark Nudelman\n\n");
511 		putstr("less comes with NO WARRANTY, to the extent permitted by law.\n");
512 		putstr("For information about the terms of redistribution,\n");
513 		putstr("see the file named README in the less distribution.\n");
514 		putstr("Homepage: http://www.greenwoodsoftware.com/less\n");
515 		quit(QUIT_OK);
516 		break;
517 	}
518 }
519 
520 #if MSDOS_COMPILER
521 /*
522  * Parse an MSDOS color descriptor.
523  */
524    	static void
525 colordesc(s, fg_color, bg_color)
526 	char *s;
527 	int *fg_color;
528 	int *bg_color;
529 {
530 	int fg, bg;
531 	int err;
532 
533 	fg = getnum(&s, "D", &err);
534 	if (err)
535 	{
536 		error("Missing fg color in -D", NULL_PARG);
537 		return;
538 	}
539 	if (*s != '.')
540 		bg = nm_bg_color;
541 	else
542 	{
543 		s++;
544 		bg = getnum(&s, "D", &err);
545 		if (err)
546 		{
547 			error("Missing bg color in -D", NULL_PARG);
548 			return;
549 		}
550 	}
551 	if (*s != '\0')
552 		error("Extra characters at end of -D option", NULL_PARG);
553 	*fg_color = fg;
554 	*bg_color = bg;
555 }
556 
557 /*
558  * Handler for the -D option.
559  */
560 	/*ARGSUSED*/
561 	public void
562 opt_D(type, s)
563 	int type;
564 	char *s;
565 {
566 	switch (type)
567 	{
568 	case INIT:
569 	case TOGGLE:
570 		switch (*s++)
571 		{
572 		case 'n':
573 			colordesc(s, &nm_fg_color, &nm_bg_color);
574 			break;
575 		case 'd':
576 			colordesc(s, &bo_fg_color, &bo_bg_color);
577 			break;
578 		case 'u':
579 			colordesc(s, &ul_fg_color, &ul_bg_color);
580 			break;
581 		case 'k':
582 			colordesc(s, &bl_fg_color, &bl_bg_color);
583 			break;
584 		case 's':
585 			colordesc(s, &so_fg_color, &so_bg_color);
586 			break;
587 		default:
588 			error("-D must be followed by n, d, u, k or s", NULL_PARG);
589 			break;
590 		}
591 		if (type == TOGGLE)
592 		{
593 			at_enter(AT_STANDOUT);
594 			at_exit();
595 		}
596 		break;
597 	case QUERY:
598 		break;
599 	}
600 }
601 #endif
602 
603 /*
604  * Handler for the -x option.
605  */
606 	public void
607 opt_x(type, s)
608 	int type;
609 	register char *s;
610 {
611 	extern int tabstops[];
612 	extern int ntabstops;
613 	extern int tabdefault;
614 	char msg[60+(4*TABSTOP_MAX)];
615 	int i;
616 	PARG p;
617 
618 	switch (type)
619 	{
620 	case INIT:
621 	case TOGGLE:
622 		/* Start at 1 because tabstops[0] is always zero. */
623 		for (i = 1;  i < TABSTOP_MAX;  )
624 		{
625 			int n = 0;
626 			s = skipsp(s);
627 			while (*s >= '0' && *s <= '9')
628 				n = (10 * n) + (*s++ - '0');
629 			if (n > tabstops[i-1])
630 				tabstops[i++] = n;
631 			s = skipsp(s);
632 			if (*s++ != ',')
633 				break;
634 		}
635 		if (i < 2)
636 			return;
637 		ntabstops = i;
638 		tabdefault = tabstops[ntabstops-1] - tabstops[ntabstops-2];
639 		break;
640 	case QUERY:
641 		strlcpy(msg, "Tab stops ", sizeof(msg));
642 		if (ntabstops > 2)
643 		{
644 			for (i = 1;  i < ntabstops;  i++)
645 			{
646 				if (i > 1)
647 					strlcat(msg, ",", sizeof(msg));
648 				snprintf(msg+strlen(msg),
649 				    sizeof(msg)-strlen(msg), "%d", tabstops[i]);
650 			}
651 			snprintf(msg+strlen(msg), sizeof(msg)-strlen(msg),
652 			    " and then ");
653 		}
654 		snprintf(msg+strlen(msg), sizeof(msg)-strlen(msg),
655 		    "every %d spaces", 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