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