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