xref: /openbsd-src/usr.bin/less/optfunc.c (revision ae3cb403620ab940fbaabb3055fac045a63d56b7)
1 /*
2  * Copyright (C) 1984-2012  Mark Nudelman
3  * Modified for use with illumos by Garrett D'Amore.
4  * Copyright 2014 Garrett D'Amore <garrett@damore.org>
5  *
6  * You may distribute under the terms of either the GNU General Public
7  * License or the Less License, as specified in the README file.
8  *
9  * For more information, see the README file.
10  */
11 
12 /*
13  * Handling functions for command line options.
14  *
15  * Most options are handled by the generic code in option.c.
16  * But all string options, and a few non-string options, require
17  * special handling specific to the particular option.
18  * This special processing is done by the "handling functions" in this file.
19  *
20  * Each handling function is passed a "type" and, if it is a string
21  * option, the string which should be "assigned" to the option.
22  * The type may be one of:
23  *	INIT	The option is being initialized from the command line.
24  *	TOGGLE	The option is being changed from within the program.
25  *	QUERY	The setting of the option is merely being queried.
26  */
27 
28 #include "less.h"
29 #include "option.h"
30 
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 less_is_more;
51 extern char *namelogfile;
52 extern int force_logfile;
53 extern int logfile;
54 char *tagoption = NULL;
55 extern char *tags;
56 
57 int shift_count;	/* Number of positions to shift horizontally */
58 static int shift_count_fraction = -1;
59 
60 /*
61  * Handler for -o option.
62  */
63 void
64 opt_o(int type, char *s)
65 {
66 	PARG parg;
67 
68 	if (secure) {
69 		error("log file support is not available", NULL);
70 		return;
71 	}
72 	switch (type) {
73 	case INIT:
74 		namelogfile = s;
75 		break;
76 	case TOGGLE:
77 		if (ch_getflags() & CH_CANSEEK) {
78 			error("Input is not a pipe", NULL);
79 			return;
80 		}
81 		if (logfile >= 0) {
82 			error("Log file is already in use", NULL);
83 			return;
84 		}
85 		s = skipsp(s);
86 		namelogfile = lglob(s);
87 		use_logfile(namelogfile);
88 		sync_logfile();
89 		break;
90 	case QUERY:
91 		if (logfile < 0) {
92 			error("No log file", NULL);
93 		} else {
94 			parg.p_string = namelogfile;
95 			error("Log file \"%s\"", &parg);
96 		}
97 		break;
98 	}
99 }
100 
101 /*
102  * Handler for -O option.
103  */
104 void
105 opt__O(int type, char *s)
106 {
107 	force_logfile = TRUE;
108 	opt_o(type, s);
109 }
110 
111 /*
112  * Handlers for -j option.
113  */
114 void
115 opt_j(int type, char *s)
116 {
117 	PARG parg;
118 	char buf[16];
119 	int len;
120 	int err;
121 
122 	switch (type) {
123 	case INIT:
124 	case TOGGLE:
125 		if (*s == '.') {
126 			s++;
127 			jump_sline_fraction = getfraction(&s, "j", &err);
128 			if (err)
129 				error("Invalid line fraction", NULL);
130 			else
131 				calc_jump_sline();
132 		} else {
133 			int sline = getnum(&s, "j", &err);
134 			if (err) {
135 				error("Invalid line number", NULL);
136 			} else {
137 				jump_sline = sline;
138 				jump_sline_fraction = -1;
139 			}
140 		}
141 		break;
142 	case QUERY:
143 		if (jump_sline_fraction < 0) {
144 			parg.p_int =  jump_sline;
145 			error("Position target at screen line %d", &parg);
146 		} else {
147 			(void) snprintf(buf, sizeof (buf), ".%06d",
148 			    jump_sline_fraction);
149 			len = strlen(buf);
150 			while (len > 2 && buf[len-1] == '0')
151 				len--;
152 			buf[len] = '\0';
153 			parg.p_string = buf;
154 			error("Position target at screen position %s", &parg);
155 		}
156 		break;
157 	}
158 }
159 
160 void
161 calc_jump_sline(void)
162 {
163 	if (jump_sline_fraction < 0)
164 		return;
165 	jump_sline = sc_height * jump_sline_fraction / NUM_FRAC_DENOM;
166 }
167 
168 /*
169  * Handlers for -# option.
170  */
171 void
172 opt_shift(int type, char *s)
173 {
174 	PARG parg;
175 	char buf[16];
176 	int len;
177 	int err;
178 
179 	switch (type) {
180 	case INIT:
181 	case TOGGLE:
182 		if (*s == '.') {
183 			s++;
184 			shift_count_fraction = getfraction(&s, "#", &err);
185 			if (err)
186 				error("Invalid column fraction", NULL);
187 			else
188 				calc_shift_count();
189 		} else {
190 			int hs = getnum(&s, "#", &err);
191 			if (err) {
192 				error("Invalid column number", NULL);
193 			} else {
194 				shift_count = hs;
195 				shift_count_fraction = -1;
196 			}
197 		}
198 		break;
199 	case QUERY:
200 		if (shift_count_fraction < 0) {
201 			parg.p_int = shift_count;
202 			error("Horizontal shift %d columns", &parg);
203 		} else {
204 
205 			(void) snprintf(buf, sizeof (buf), ".%06d",
206 			    shift_count_fraction);
207 			len = strlen(buf);
208 			while (len > 2 && buf[len-1] == '0')
209 				len--;
210 			buf[len] = '\0';
211 			parg.p_string = buf;
212 			error("Horizontal shift %s of screen width", &parg);
213 		}
214 		break;
215 	}
216 }
217 
218 void
219 calc_shift_count(void)
220 {
221 	if (shift_count_fraction < 0)
222 		return;
223 	shift_count = sc_width * shift_count_fraction / NUM_FRAC_DENOM;
224 }
225 
226 void
227 opt_k(int type, char *s)
228 {
229 	PARG parg;
230 
231 	switch (type) {
232 	case INIT:
233 		if (lesskey(s, 0)) {
234 			parg.p_string = s;
235 			error("Cannot use lesskey file \"%s\"", &parg);
236 		}
237 		break;
238 	}
239 }
240 
241 /*
242  * Handler for -t option.
243  */
244 void
245 opt_t(int type, char *s)
246 {
247 	IFILE save_ifile;
248 	off_t pos;
249 
250 	switch (type) {
251 	case INIT:
252 		tagoption = s;
253 		/* Do the rest in main() */
254 		break;
255 	case TOGGLE:
256 		if (secure) {
257 			error("tags support is not available", NULL);
258 			break;
259 		}
260 		findtag(skipsp(s));
261 		save_ifile = save_curr_ifile();
262 		/*
263 		 * Try to open the file containing the tag
264 		 * and search for the tag in that file.
265 		 */
266 		if (edit_tagfile() || (pos = tagsearch()) == -1) {
267 			/* Failed: reopen the old file. */
268 			reedit_ifile(save_ifile);
269 			break;
270 		}
271 		unsave_ifile(save_ifile);
272 		jump_loc(pos, jump_sline);
273 		break;
274 	}
275 }
276 
277 /*
278  * Handler for -T option.
279  */
280 void
281 opt__T(int type, char *s)
282 {
283 	PARG parg;
284 
285 	switch (type) {
286 	case INIT:
287 		tags = s;
288 		break;
289 	case TOGGLE:
290 		s = skipsp(s);
291 		tags = lglob(s);
292 		break;
293 	case QUERY:
294 		parg.p_string = tags;
295 		error("Tags file \"%s\"", &parg);
296 		break;
297 	}
298 }
299 
300 /*
301  * Handler for -p option.
302  */
303 void
304 opt_p(int type, char *s)
305 {
306 	switch (type) {
307 	case INIT:
308 		/*
309 		 * Unget a search command for the specified string.
310 		 * {{ This won't work if the "/" command is
311 		 *    changed or invalidated by a .lesskey file. }}
312 		 */
313 		plusoption = TRUE;
314 		ungetsc(s);
315 		/*
316 		 * In "more" mode, the -p argument is a command,
317 		 * not a search string, so we don't need a slash.
318 		 */
319 		if (!less_is_more)
320 			ungetsc("/");
321 		break;
322 	}
323 }
324 
325 /*
326  * Handler for -P option.
327  */
328 void
329 opt__P(int type, char *s)
330 {
331 	char **proto;
332 	PARG parg;
333 
334 	switch (type) {
335 	case INIT:
336 	case TOGGLE:
337 		/*
338 		 * Figure out which prototype string should be changed.
339 		 */
340 		switch (*s) {
341 		case 's':  proto = &prproto[PR_SHORT];	s++;	break;
342 		case 'm':  proto = &prproto[PR_MEDIUM];	s++;	break;
343 		case 'M':  proto = &prproto[PR_LONG];	s++;	break;
344 		case '=':  proto = &eqproto;		s++;	break;
345 		case 'h':  proto = &hproto;		s++;	break;
346 		case 'w':  proto = &wproto;		s++;	break;
347 		default:   proto = &prproto[PR_SHORT];		break;
348 		}
349 		free(*proto);
350 		*proto = estrdup(s);
351 		break;
352 	case QUERY:
353 		parg.p_string = prproto[pr_type];
354 		error("%s", &parg);
355 		break;
356 	}
357 }
358 
359 /*
360  * Handler for the -b option.
361  */
362 void
363 opt_b(int type, char *s)
364 {
365 	switch (type) {
366 	case INIT:
367 	case TOGGLE:
368 		/*
369 		 * Set the new number of buffers.
370 		 */
371 		ch_setbufspace(bufspace);
372 		break;
373 	case QUERY:
374 		break;
375 	}
376 }
377 
378 /*
379  * Handler for the -i option.
380  */
381 void
382 opt_i(int type, char *s)
383 {
384 	switch (type) {
385 	case TOGGLE:
386 		chg_caseless();
387 		break;
388 	case QUERY:
389 	case INIT:
390 		break;
391 	}
392 }
393 
394 /*
395  * Handler for the -V option.
396  */
397 void
398 opt__V(int type, char *s)
399 {
400 	switch (type) {
401 	case TOGGLE:
402 	case QUERY:
403 		dispversion();
404 		break;
405 	case INIT:
406 		/*
407 		 * Force output to stdout per GNU standard for --version output.
408 		 */
409 		any_display = 1;
410 		putstr("less ");
411 		putstr(version);
412 		putstr(" (");
413 		putstr("POSIX ");
414 		putstr("regular expressions)\n");
415 		putstr("Copyright (C) 1984-2012 Mark Nudelman\n");
416 		putstr("Modified for use with illumos by Garrett D'Amore.\n");
417 		putstr("Copyright 2014 Garrett D'Amore\n\n");
418 		putstr("less comes with NO WARRANTY, ");
419 		putstr("to the extent permitted by law.\n");
420 		putstr("For information about the terms of redistribution,\n");
421 		putstr("see the file named README in the less distribution.\n");
422 		putstr("Homepage: http://www.greenwoodsoftware.com/less\n");
423 		putstr("\n");
424 		quit(QUIT_OK);
425 		break;
426 	}
427 }
428 
429 /*
430  * Handler for the -x option.
431  */
432 void
433 opt_x(int type, char *s)
434 {
435 	extern int tabstops[];
436 	extern int ntabstops;
437 	extern int tabdefault;
438 	char tabs[60+(4*TABSTOP_MAX)];
439 	int i;
440 	PARG p;
441 
442 	switch (type) {
443 	case INIT:
444 	case TOGGLE:
445 		/* Start at 1 because tabstops[0] is always zero. */
446 		for (i = 1; i < TABSTOP_MAX; ) {
447 			int n = 0;
448 			s = skipsp(s);
449 			while (*s >= '0' && *s <= '9')
450 				n = (10 * n) + (*s++ - '0');
451 			if (n > tabstops[i-1])
452 				tabstops[i++] = n;
453 			s = skipsp(s);
454 			if (*s++ != ',')
455 				break;
456 		}
457 		if (i < 2)
458 			return;
459 		ntabstops = i;
460 		tabdefault = tabstops[ntabstops-1] - tabstops[ntabstops-2];
461 		break;
462 	case QUERY:
463 		(void) strlcpy(tabs, "Tab stops ", sizeof(tabs));
464 		if (ntabstops > 2) {
465 			for (i = 1; i < ntabstops; i++) {
466 				if (i > 1)
467 					strlcat(tabs, ",", sizeof(tabs));
468 				(void) snprintf(tabs+strlen(tabs),
469 				    sizeof(tabs)-strlen(tabs),
470 				    "%d", tabstops[i]);
471 			}
472 			(void) snprintf(tabs+strlen(tabs),
473 			    sizeof(tabs)-strlen(tabs), " and then ");
474 		}
475 		(void) snprintf(tabs+strlen(tabs), sizeof(tabs)-strlen(tabs),
476 		    "every %d spaces", tabdefault);
477 		p.p_string = tabs;
478 		error("%s", &p);
479 		break;
480 	}
481 }
482 
483 
484 /*
485  * Handler for the -" option.
486  */
487 void
488 opt_quote(int type, char *s)
489 {
490 	char buf[3];
491 	PARG parg;
492 
493 	switch (type) {
494 	case INIT:
495 	case TOGGLE:
496 		if (s[0] == '\0') {
497 			openquote = closequote = '\0';
498 			break;
499 		}
500 		if (s[1] != '\0' && s[2] != '\0') {
501 			error("-\" must be followed by 1 or 2 chars",
502 			    NULL);
503 			return;
504 		}
505 		openquote = s[0];
506 		if (s[1] == '\0')
507 			closequote = openquote;
508 		else
509 			closequote = s[1];
510 		break;
511 	case QUERY:
512 		buf[0] = openquote;
513 		buf[1] = closequote;
514 		buf[2] = '\0';
515 		parg.p_string = buf;
516 		error("quotes %s", &parg);
517 		break;
518 	}
519 }
520 
521 /*
522  * "-?" means display a help message.
523  * If from the command line, exit immediately.
524  */
525 void
526 opt_query(int type, char *s)
527 {
528 	switch (type) {
529 	case QUERY:
530 	case TOGGLE:
531 		error("Use \"h\" for help", NULL);
532 		break;
533 	case INIT:
534 		dohelp = 1;
535 	}
536 }
537 
538 /*
539  * Get the "screen window" size.
540  */
541 int
542 get_swindow(void)
543 {
544 	if (swindow > 0)
545 		return (swindow);
546 	return (sc_height + swindow);
547 }
548