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