optfunc.c revision 221715
1238384Sjkim/*
2238384Sjkim * Copyright (C) 1984-2011  Mark Nudelman
3238384Sjkim *
4238384Sjkim * You may distribute under the terms of either the GNU General Public
5238384Sjkim * License or the Less License, as specified in the README file.
6238384Sjkim *
7238384Sjkim * For more information about less, or for information on how to
8238384Sjkim * contact the author, see the README file.
9238384Sjkim */
10238384Sjkim
11238384Sjkim
12238384Sjkim/*
13238384Sjkim * Handling functions for command line options.
14238384Sjkim *
15238384Sjkim * Most options are handled by the generic code in option.c.
16238384Sjkim * But all string options, and a few non-string options, require
17238384Sjkim * special handling specific to the particular option.
18238384Sjkim * This special processing is done by the "handling functions" in this file.
19238384Sjkim *
20238384Sjkim * Each handling function is passed a "type" and, if it is a string
21238384Sjkim * option, the string which should be "assigned" to the option.
22238384Sjkim * The type may be one of:
23238384Sjkim *	INIT	The option is being initialized from the command line.
24238384Sjkim *	TOGGLE	The option is being changed from within the program.
25238384Sjkim *	QUERY	The setting of the option is merely being queried.
26238384Sjkim */
27238384Sjkim
28238384Sjkim#include "less.h"
29238384Sjkim#include "option.h"
30238384Sjkim
31238384Sjkimextern int nbufs;
32238384Sjkimextern int bufspace;
33238384Sjkimextern int pr_type;
34238384Sjkimextern int plusoption;
35238384Sjkimextern int swindow;
36238384Sjkimextern int sc_width;
37238384Sjkimextern int sc_height;
38238384Sjkimextern int secure;
39238384Sjkimextern int dohelp;
40238384Sjkimextern int any_display;
41238384Sjkimextern char openquote;
42238384Sjkimextern char closequote;
43238384Sjkimextern char *prproto[];
44238384Sjkimextern char *eqproto;
45238384Sjkimextern char *hproto;
46238384Sjkimextern char *wproto;
47238384Sjkimextern IFILE curr_ifile;
48238384Sjkimextern char version[];
49238384Sjkimextern int jump_sline;
50238384Sjkimextern int jump_sline_fraction;
51238384Sjkimextern int shift_count;
52238384Sjkimextern int shift_count_fraction;
53238384Sjkimextern int less_is_more;
54238384Sjkim#if LOGFILE
55238384Sjkimextern char *namelogfile;
56238384Sjkimextern int force_logfile;
57238384Sjkimextern int logfile;
58238384Sjkim#endif
59238384Sjkim#if TAGS
60238384Sjkimpublic char *tagoption = NULL;
61238384Sjkimextern char *tags;
62238384Sjkim#endif
63238384Sjkim#if MSDOS_COMPILER
64238384Sjkimextern int nm_fg_color, nm_bg_color;
65238384Sjkimextern int bo_fg_color, bo_bg_color;
66238384Sjkimextern int ul_fg_color, ul_bg_color;
67238384Sjkimextern int so_fg_color, so_bg_color;
68238384Sjkimextern int bl_fg_color, bl_bg_color;
69238384Sjkim#endif
70238384Sjkim
71238384Sjkim
72238384Sjkim#if LOGFILE
73238384Sjkim/*
74238384Sjkim * Handler for -o option.
75238384Sjkim */
76238384Sjkim	public void
77238384Sjkimopt_o(type, s)
78238384Sjkim	int type;
79238384Sjkim	char *s;
80238384Sjkim{
81238384Sjkim	PARG parg;
82238384Sjkim
83238384Sjkim	if (secure)
84238384Sjkim	{
85238384Sjkim		error("log file support is not available", NULL_PARG);
86238384Sjkim		return;
87238384Sjkim	}
88238384Sjkim	switch (type)
89238384Sjkim	{
90238384Sjkim	case INIT:
91238384Sjkim		namelogfile = s;
92238384Sjkim		break;
93238384Sjkim	case TOGGLE:
94238384Sjkim		if (ch_getflags() & CH_CANSEEK)
95238384Sjkim		{
96238384Sjkim			error("Input is not a pipe", NULL_PARG);
97238384Sjkim			return;
98238384Sjkim		}
99238384Sjkim		if (logfile >= 0)
100238384Sjkim		{
101238384Sjkim			error("Log file is already in use", NULL_PARG);
102238384Sjkim			return;
103238384Sjkim		}
104238384Sjkim		s = skipsp(s);
105238384Sjkim		namelogfile = lglob(s);
106238384Sjkim		use_logfile(namelogfile);
107238384Sjkim		sync_logfile();
108238384Sjkim		break;
109238384Sjkim	case QUERY:
110238384Sjkim		if (logfile < 0)
111238384Sjkim			error("No log file", NULL_PARG);
112238384Sjkim		else
113238384Sjkim		{
114238384Sjkim			parg.p_string = namelogfile;
115238384Sjkim			error("Log file \"%s\"", &parg);
116238384Sjkim		}
117238384Sjkim		break;
118238384Sjkim	}
119238384Sjkim}
120238384Sjkim
121238384Sjkim/*
122238384Sjkim * Handler for -O option.
123238384Sjkim */
124238384Sjkim	public void
125238384Sjkimopt__O(type, s)
126238384Sjkim	int type;
127238384Sjkim	char *s;
128238384Sjkim{
129238384Sjkim	force_logfile = TRUE;
130238384Sjkim	opt_o(type, s);
131238384Sjkim}
132238384Sjkim#endif
133238384Sjkim
134238384Sjkim/*
135238384Sjkim * Handlers for -l option.
136238384Sjkim */
137238384Sjkim	public void
138238384Sjkimopt_l(type, s)
139238384Sjkim	int type;
140238384Sjkim	char *s;
141238384Sjkim{
142238384Sjkim	int err;
143238384Sjkim	int n;
144238384Sjkim	char *t;
145238384Sjkim
146238384Sjkim	switch (type)
147238384Sjkim	{
148238384Sjkim	case INIT:
149238384Sjkim		t = s;
150238384Sjkim		n = getnum(&t, "l", &err);
151238384Sjkim		if (err || n <= 0)
152238384Sjkim		{
153238384Sjkim			error("Line number is required after -l", NULL_PARG);
154238384Sjkim			return;
155238384Sjkim		}
156238384Sjkim		plusoption = TRUE;
157238384Sjkim		ungetsc(s);
158238384Sjkim		break;
159238384Sjkim	}
160238384Sjkim}
161238384Sjkim
162238384Sjkim/*
163238384Sjkim * Handlers for -j option.
164238384Sjkim */
165238384Sjkim	public void
166238384Sjkimopt_j(type, s)
167238384Sjkim	int type;
168238384Sjkim	char *s;
169238384Sjkim{
170238384Sjkim	PARG parg;
171238384Sjkim	char buf[16];
172238384Sjkim	int len;
173238384Sjkim	int err;
174238384Sjkim
175238384Sjkim	switch (type)
176238384Sjkim	{
177238384Sjkim	case INIT:
178238384Sjkim	case TOGGLE:
179238384Sjkim		if (*s == '.')
180238384Sjkim		{
181238384Sjkim			s++;
182238384Sjkim			jump_sline_fraction = getfraction(&s, "j", &err);
183238384Sjkim			if (err)
184238384Sjkim				error("Invalid line fraction", NULL_PARG);
185238384Sjkim			else
186238384Sjkim				calc_jump_sline();
187238384Sjkim		} else
188238384Sjkim		{
189238384Sjkim			int sline = getnum(&s, "j", &err);
190238384Sjkim			if (err)
191238384Sjkim				error("Invalid line number", NULL_PARG);
192238384Sjkim			else
193238384Sjkim			{
194238384Sjkim				jump_sline = sline;
195238384Sjkim				jump_sline_fraction = -1;
196238384Sjkim			}
197238384Sjkim		}
198238384Sjkim		break;
199238384Sjkim	case QUERY:
200238384Sjkim		if (jump_sline_fraction < 0)
201238384Sjkim		{
202238384Sjkim			parg.p_int =  jump_sline;
203238384Sjkim			error("Position target at screen line %d", &parg);
204238384Sjkim		} else
205238384Sjkim		{
206238384Sjkim
207238384Sjkim			sprintf(buf, ".%06d", jump_sline_fraction);
208238384Sjkim			len = strlen(buf);
209238384Sjkim			while (len > 2 && buf[len-1] == '0')
210238384Sjkim				len--;
211238384Sjkim			buf[len] = '\0';
212238384Sjkim			parg.p_string = buf;
213238384Sjkim			error("Position target at screen position %s", &parg);
214238384Sjkim		}
215238384Sjkim		break;
216238384Sjkim	}
217238384Sjkim}
218238384Sjkim
219238384Sjkim	public void
220238384Sjkimcalc_jump_sline()
221238384Sjkim{
222238384Sjkim	if (jump_sline_fraction < 0)
223238384Sjkim		return;
224238384Sjkim	jump_sline = sc_height * jump_sline_fraction / NUM_FRAC_DENOM;
225238384Sjkim}
226238384Sjkim
227238384Sjkim/*
228238384Sjkim * Handlers for -# option.
229238384Sjkim */
230238384Sjkim	public void
231238384Sjkimopt_shift(type, s)
232238384Sjkim	int type;
233238384Sjkim	char *s;
234238384Sjkim{
235238384Sjkim	PARG parg;
236238384Sjkim	char buf[16];
237238384Sjkim	int len;
238238384Sjkim	int err;
239238384Sjkim
240238384Sjkim	switch (type)
241238384Sjkim	{
242238384Sjkim	case INIT:
243238384Sjkim	case TOGGLE:
244238384Sjkim		if (*s == '.')
245238384Sjkim		{
246238384Sjkim			s++;
247238384Sjkim			shift_count_fraction = getfraction(&s, "#", &err);
248238384Sjkim			if (err)
249238384Sjkim				error("Invalid column fraction", NULL_PARG);
250238384Sjkim			else
251238384Sjkim				calc_shift_count();
252238384Sjkim		} else
253238384Sjkim		{
254238384Sjkim			int hs = getnum(&s, "#", &err);
255238384Sjkim			if (err)
256238384Sjkim				error("Invalid column number", NULL_PARG);
257238384Sjkim			else
258238384Sjkim			{
259238384Sjkim				shift_count = hs;
260238384Sjkim				shift_count_fraction = -1;
261279264Sdelphij			}
262279264Sdelphij		}
263279264Sdelphij		break;
264279264Sdelphij	case QUERY:
265279264Sdelphij		if (shift_count_fraction < 0)
266279264Sdelphij		{
267238384Sjkim			parg.p_int = shift_count;
268238384Sjkim			error("Horizontal shift %d columns", &parg);
269238384Sjkim		} else
270238384Sjkim		{
271238384Sjkim
272238384Sjkim			sprintf(buf, ".%06d", shift_count_fraction);
273238384Sjkim			len = strlen(buf);
274238384Sjkim			while (len > 2 && buf[len-1] == '0')
275238384Sjkim				len--;
276238384Sjkim			buf[len] = '\0';
277238384Sjkim			parg.p_string = buf;
278238384Sjkim			error("Horizontal shift %s of screen width", &parg);
279238384Sjkim		}
280238384Sjkim		break;
281238384Sjkim	}
282238384Sjkim}
283238384Sjkim	public void
284238384Sjkimcalc_shift_count()
285238384Sjkim{
286238384Sjkim	if (shift_count_fraction < 0)
287238384Sjkim		return;
288238384Sjkim	shift_count = sc_width * shift_count_fraction / NUM_FRAC_DENOM;
289238384Sjkim}
290238384Sjkim
291238384Sjkim#if USERFILE
292238384Sjkim	public void
293238384Sjkimopt_k(type, s)
294238384Sjkim	int type;
295238384Sjkim	char *s;
296238384Sjkim{
297238384Sjkim	PARG parg;
298238384Sjkim
299238384Sjkim	switch (type)
300238384Sjkim	{
301238384Sjkim	case INIT:
302238384Sjkim		if (lesskey(s, 0))
303238384Sjkim		{
304238384Sjkim			parg.p_string = s;
305238384Sjkim			error("Cannot use lesskey file \"%s\"", &parg);
306238384Sjkim		}
307238384Sjkim		break;
308238384Sjkim	}
309238384Sjkim}
310238384Sjkim#endif
311238384Sjkim
312238384Sjkim#if TAGS
313238384Sjkim/*
314238384Sjkim * Handler for -t option.
315238384Sjkim */
316238384Sjkim	public void
317238384Sjkimopt_t(type, s)
318238384Sjkim	int type;
319238384Sjkim	char *s;
320238384Sjkim{
321238384Sjkim	IFILE save_ifile;
322238384Sjkim	POSITION pos;
323238384Sjkim
324238384Sjkim	switch (type)
325238384Sjkim	{
326238384Sjkim	case INIT:
327238384Sjkim		tagoption = s;
328238384Sjkim		/* Do the rest in main() */
329238384Sjkim		break;
330238384Sjkim	case TOGGLE:
331238384Sjkim		if (secure)
332238384Sjkim		{
333238384Sjkim			error("tags support is not available", NULL_PARG);
334238384Sjkim			break;
335238384Sjkim		}
336238384Sjkim		findtag(skipsp(s));
337238384Sjkim		save_ifile = save_curr_ifile();
338238384Sjkim		/*
339238384Sjkim		 * Try to open the file containing the tag
340238384Sjkim		 * and search for the tag in that file.
341238384Sjkim		 */
342238384Sjkim		if (edit_tagfile() || (pos = tagsearch()) == NULL_POSITION)
343238384Sjkim		{
344238384Sjkim			/* Failed: reopen the old file. */
345238384Sjkim			reedit_ifile(save_ifile);
346238384Sjkim			break;
347238384Sjkim		}
348238384Sjkim		unsave_ifile(save_ifile);
349238384Sjkim		jump_loc(pos, jump_sline);
350238384Sjkim		break;
351238384Sjkim	}
352238384Sjkim}
353238384Sjkim
354238384Sjkim/*
355238384Sjkim * Handler for -T option.
356238384Sjkim */
357238384Sjkim	public void
358238384Sjkimopt__T(type, s)
359238384Sjkim	int type;
360238384Sjkim	char *s;
361238384Sjkim{
362238384Sjkim	PARG parg;
363238384Sjkim
364238384Sjkim	switch (type)
365238384Sjkim	{
366238384Sjkim	case INIT:
367238384Sjkim		tags = s;
368238384Sjkim		break;
369238384Sjkim	case TOGGLE:
370238384Sjkim		s = skipsp(s);
371238384Sjkim		tags = lglob(s);
372238384Sjkim		break;
373238384Sjkim	case QUERY:
374238384Sjkim		parg.p_string = tags;
375238384Sjkim		error("Tags file \"%s\"", &parg);
376238384Sjkim		break;
377238384Sjkim	}
378238384Sjkim}
379238384Sjkim#endif
380238384Sjkim
381238384Sjkim/*
382238384Sjkim * Handler for -p option.
383238384Sjkim */
384238384Sjkim	public void
385238384Sjkimopt_p(type, s)
386238384Sjkim	int type;
387238384Sjkim	register char *s;
388238384Sjkim{
389238384Sjkim	switch (type)
390238384Sjkim	{
391238384Sjkim	case INIT:
392238384Sjkim		/*
393238384Sjkim		 * Unget a search command for the specified string.
394238384Sjkim		 * {{ This won't work if the "/" command is
395238384Sjkim		 *    changed or invalidated by a .lesskey file. }}
396238384Sjkim		 */
397238384Sjkim		plusoption = TRUE;
398238384Sjkim		ungetsc(s);
399238384Sjkim		/*
400238384Sjkim		 * In "more" mode, the -p argument is a command,
401238384Sjkim		 * not a search string, so we don't need a slash.
402238384Sjkim		 */
403238384Sjkim		if (!less_is_more)
404238384Sjkim			ungetsc("/");
405238384Sjkim		break;
406238384Sjkim	}
407238384Sjkim}
408238384Sjkim
409238384Sjkim/*
410238384Sjkim * Handler for -P option.
411238384Sjkim */
412238384Sjkim	public void
413238384Sjkimopt__P(type, s)
414238384Sjkim	int type;
415238384Sjkim	register char *s;
416238384Sjkim{
417238384Sjkim	register char **proto;
418238384Sjkim	PARG parg;
419238384Sjkim
420238384Sjkim	switch (type)
421238384Sjkim	{
422238384Sjkim	case INIT:
423238384Sjkim	case TOGGLE:
424238384Sjkim		/*
425238384Sjkim		 * Figure out which prototype string should be changed.
426238384Sjkim		 */
427238384Sjkim		switch (*s)
428238384Sjkim		{
429238384Sjkim		case 's':  proto = &prproto[PR_SHORT];	s++;	break;
430238384Sjkim		case 'm':  proto = &prproto[PR_MEDIUM];	s++;	break;
431238384Sjkim		case 'M':  proto = &prproto[PR_LONG];	s++;	break;
432238384Sjkim		case '=':  proto = &eqproto;		s++;	break;
433238384Sjkim		case 'h':  proto = &hproto;		s++;	break;
434238384Sjkim		case 'w':  proto = &wproto;		s++;	break;
435238384Sjkim		default:   proto = &prproto[PR_SHORT];		break;
436238384Sjkim		}
437238384Sjkim		free(*proto);
438238384Sjkim		*proto = save(s);
439238384Sjkim		break;
440238384Sjkim	case QUERY:
441238384Sjkim		parg.p_string = prproto[pr_type];
442238384Sjkim		error("%s", &parg);
443238384Sjkim		break;
444238384Sjkim	}
445238384Sjkim}
446238384Sjkim
447238384Sjkim/*
448238384Sjkim * Handler for the -b option.
449238384Sjkim */
450238384Sjkim	/*ARGSUSED*/
451238384Sjkim	public void
452238384Sjkimopt_b(type, s)
453238384Sjkim	int type;
454238384Sjkim	char *s;
455238384Sjkim{
456238384Sjkim	switch (type)
457238384Sjkim	{
458238384Sjkim	case INIT:
459238384Sjkim	case TOGGLE:
460238384Sjkim		/*
461238384Sjkim		 * Set the new number of buffers.
462238384Sjkim		 */
463238384Sjkim		ch_setbufspace(bufspace);
464238384Sjkim		break;
465238384Sjkim	case QUERY:
466		break;
467	}
468}
469
470/*
471 * Handler for the -i option.
472 */
473	/*ARGSUSED*/
474	public void
475opt_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
495opt__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
527colordesc(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
564opt_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
609opt_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
667opt_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
710opt_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
729get_swindow()
730{
731	if (swindow > 0)
732		return (swindow);
733	return (sc_height + swindow);
734}
735
736