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