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