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