1 /*
2 * Copyright (C) 1984-2024 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 #include "position.h"
30
31 extern int bufspace;
32 extern int pr_type;
33 extern lbool plusoption;
34 extern int swindow;
35 extern int sc_width;
36 extern int sc_height;
37 extern int dohelp;
38 extern char openquote;
39 extern char closequote;
40 extern char *prproto[];
41 extern char *eqproto;
42 extern char *hproto;
43 extern char *wproto;
44 extern char *every_first_cmd;
45 extern IFILE curr_ifile;
46 extern char version[];
47 extern int jump_sline;
48 extern long jump_sline_fraction;
49 extern int shift_count;
50 extern long shift_count_fraction;
51 extern int match_shift;
52 extern long match_shift_fraction;
53 extern LWCHAR rscroll_char;
54 extern int rscroll_attr;
55 extern int mousecap;
56 extern int wheel_lines;
57 extern int less_is_more;
58 extern int linenum_width;
59 extern int status_col_width;
60 extern int use_color;
61 extern int want_filesize;
62 extern int header_lines;
63 extern int header_cols;
64 extern int def_search_type;
65 extern int chopline;
66 extern int tabstops[];
67 extern int ntabstops;
68 extern int tabdefault;
69 extern char intr_char;
70 extern int nosearch_header_lines;
71 extern int nosearch_header_cols;
72 extern POSITION header_start_pos;
73 #if LOGFILE
74 extern char *namelogfile;
75 extern lbool force_logfile;
76 extern int logfile;
77 #endif
78 #if TAGS
79 public char *tagoption = NULL;
80 extern char *tags;
81 extern char ztags[];
82 #endif
83 #if LESSTEST
84 extern constant char *ttyin_name;
85 extern int is_tty;
86 #endif /*LESSTEST*/
87 #if MSDOS_COMPILER
88 extern int nm_fg_color, nm_bg_color, nm_attr;
89 extern int bo_fg_color, bo_bg_color, bo_attr;
90 extern int ul_fg_color, ul_bg_color, ul_attr;
91 extern int so_fg_color, so_bg_color, so_attr;
92 extern int bl_fg_color, bl_bg_color, bl_attr;
93 extern int sgr_mode;
94 #if MSDOS_COMPILER==WIN32C
95 #ifndef COMMON_LVB_UNDERSCORE
96 #define COMMON_LVB_UNDERSCORE 0x8000
97 #endif
98 #ifndef COMMON_LVB_REVERSE_VIDEO
99 #define COMMON_LVB_REVERSE_VIDEO 0x4000
100 #endif
101 #endif
102 #endif
103
104
105 #if LOGFILE
106 /*
107 * Handler for -o option.
108 */
opt_o(int type,constant char * s)109 public void opt_o(int type, constant char *s)
110 {
111 PARG parg;
112 char *filename;
113
114 if (!secure_allow(SF_LOGFILE))
115 {
116 error("log file support is not available", NULL_PARG);
117 return;
118 }
119 switch (type)
120 {
121 case INIT:
122 namelogfile = save(s);
123 break;
124 case TOGGLE:
125 if (ch_getflags() & CH_CANSEEK)
126 {
127 error("Input is not a pipe", NULL_PARG);
128 return;
129 }
130 if (logfile >= 0)
131 {
132 error("Log file is already in use", NULL_PARG);
133 return;
134 }
135 s = skipspc(s);
136 if (namelogfile != NULL)
137 free(namelogfile);
138 filename = lglob(s);
139 namelogfile = shell_unquote(filename);
140 free(filename);
141 use_logfile(namelogfile);
142 sync_logfile();
143 break;
144 case QUERY:
145 if (logfile < 0)
146 error("No log file", NULL_PARG);
147 else
148 {
149 parg.p_string = namelogfile;
150 error("Log file \"%s\"", &parg);
151 }
152 break;
153 }
154 }
155
156 /*
157 * Handler for -O option.
158 */
opt__O(int type,constant char * s)159 public void opt__O(int type, constant char *s)
160 {
161 force_logfile = TRUE;
162 opt_o(type, s);
163 }
164 #endif
165
toggle_fraction(int * num,long * frac,constant char * s,constant char * printopt,void (* calc)(void))166 static int toggle_fraction(int *num, long *frac, constant char *s, constant char *printopt, void (*calc)(void))
167 {
168 lbool err;
169 if (s == NULL)
170 {
171 (*calc)();
172 } else if (*s == '.')
173 {
174 long tfrac;
175 s++;
176 tfrac = getfraction(&s, printopt, &err);
177 if (err)
178 {
179 error("Invalid fraction", NULL_PARG);
180 return -1;
181 }
182 *frac = tfrac;
183 (*calc)();
184 } else
185 {
186 int tnum = getnumc(&s, printopt, &err);
187 if (err)
188 {
189 error("Invalid number", NULL_PARG);
190 return -1;
191 }
192 *frac = -1;
193 *num = tnum;
194 }
195 return 0;
196 }
197
query_fraction(int value,long fraction,constant char * int_msg,constant char * frac_msg)198 static void query_fraction(int value, long fraction, constant char *int_msg, constant char *frac_msg)
199 {
200 PARG parg;
201
202 if (fraction < 0)
203 {
204 parg.p_int = value;
205 error(int_msg, &parg);
206 } else
207 {
208 char buf[INT_STRLEN_BOUND(long)+2];
209 size_t len;
210 SNPRINTF1(buf, sizeof(buf), ".%06ld", fraction);
211 len = strlen(buf);
212 while (len > 2 && buf[len-1] == '0')
213 len--;
214 buf[len] = '\0';
215 parg.p_string = buf;
216 error(frac_msg, &parg);
217 }
218 }
219
220 /*
221 * Handlers for -j option.
222 */
opt_j(int type,constant char * s)223 public void opt_j(int type, constant char *s)
224 {
225 switch (type)
226 {
227 case INIT:
228 case TOGGLE:
229 toggle_fraction(&jump_sline, &jump_sline_fraction,
230 s, "j", calc_jump_sline);
231 break;
232 case QUERY:
233 query_fraction(jump_sline, jump_sline_fraction,
234 "Position target at screen line %d", "Position target at screen position %s");
235 break;
236 }
237 }
238
calc_jump_sline(void)239 public void calc_jump_sline(void)
240 {
241 if (jump_sline_fraction >= 0)
242 jump_sline = (int) muldiv(sc_height, jump_sline_fraction, NUM_FRAC_DENOM);
243 if (jump_sline <= header_lines)
244 jump_sline = header_lines + 1;
245 }
246
247 /*
248 * Handlers for -# option.
249 */
opt_shift(int type,constant char * s)250 public void opt_shift(int type, constant char *s)
251 {
252 switch (type)
253 {
254 case INIT:
255 case TOGGLE:
256 toggle_fraction(&shift_count, &shift_count_fraction,
257 s, "#", calc_shift_count);
258 break;
259 case QUERY:
260 query_fraction(shift_count, shift_count_fraction,
261 "Horizontal shift %d columns", "Horizontal shift %s of screen width");
262 break;
263 }
264 }
265
calc_shift_count(void)266 public void calc_shift_count(void)
267 {
268 if (shift_count_fraction < 0)
269 return;
270 shift_count = (int) muldiv(sc_width, shift_count_fraction, NUM_FRAC_DENOM);
271 }
272
273 #if USERFILE
opt_k(int type,constant char * s)274 public void opt_k(int type, constant char *s)
275 {
276 PARG parg;
277
278 switch (type)
279 {
280 case INIT:
281 if (lesskey(s, 0))
282 {
283 parg.p_string = s;
284 error("Cannot use lesskey file \"%s\"", &parg);
285 }
286 break;
287 }
288 }
289
290 #if HAVE_LESSKEYSRC
opt_ks(int type,constant char * s)291 public void opt_ks(int type, constant char *s)
292 {
293 PARG parg;
294
295 switch (type)
296 {
297 case INIT:
298 if (lesskey_src(s, 0))
299 {
300 parg.p_string = s;
301 error("Cannot use lesskey source file \"%s\"", &parg);
302 }
303 break;
304 }
305 }
306
opt_kc(int type,constant char * s)307 public void opt_kc(int type, constant char *s)
308 {
309 switch (type)
310 {
311 case INIT:
312 if (lesskey_content(s, 0))
313 {
314 error("Error in lesskey content", NULL_PARG);
315 }
316 break;
317 }
318 }
319
320 #endif /* HAVE_LESSKEYSRC */
321 #endif /* USERFILE */
322
323 /*
324 * Handler for -S option.
325 */
opt__S(int type,constant char * s)326 public void opt__S(int type, constant char *s)
327 {
328 switch (type)
329 {
330 case TOGGLE:
331 pos_rehead();
332 break;
333 }
334 }
335
336 #if TAGS
337 /*
338 * Handler for -t option.
339 */
opt_t(int type,constant char * s)340 public void opt_t(int type, constant char *s)
341 {
342 IFILE save_ifile;
343 POSITION pos;
344
345 switch (type)
346 {
347 case INIT:
348 tagoption = save(s);
349 /* Do the rest in main() */
350 break;
351 case TOGGLE:
352 if (!secure_allow(SF_TAGS))
353 {
354 error("tags support is not available", NULL_PARG);
355 break;
356 }
357 findtag(skipspc(s));
358 save_ifile = save_curr_ifile();
359 /*
360 * Try to open the file containing the tag
361 * and search for the tag in that file.
362 */
363 if (edit_tagfile() || (pos = tagsearch()) == NULL_POSITION)
364 {
365 /* Failed: reopen the old file. */
366 reedit_ifile(save_ifile);
367 break;
368 }
369 unsave_ifile(save_ifile);
370 jump_loc(pos, jump_sline);
371 break;
372 }
373 }
374
375 /*
376 * Handler for -T option.
377 */
opt__T(int type,constant char * s)378 public void opt__T(int type, constant char *s)
379 {
380 PARG parg;
381 char *filename;
382
383 switch (type)
384 {
385 case INIT:
386 tags = save(s);
387 break;
388 case TOGGLE:
389 s = skipspc(s);
390 if (tags != NULL && tags != ztags)
391 free(tags);
392 filename = lglob(s);
393 tags = shell_unquote(filename);
394 free(filename);
395 break;
396 case QUERY:
397 parg.p_string = tags;
398 error("Tags file \"%s\"", &parg);
399 break;
400 }
401 }
402 #endif
403
404 /*
405 * Handler for -p option.
406 */
opt_p(int type,constant char * s)407 public void opt_p(int type, constant char *s)
408 {
409 switch (type)
410 {
411 case INIT:
412 /*
413 * Unget a command for the specified string.
414 */
415 if (less_is_more)
416 {
417 /*
418 * In "more" mode, the -p argument is a command,
419 * not a search string, so we don't need a slash.
420 */
421 every_first_cmd = save(s);
422 } else
423 {
424 plusoption = TRUE;
425 /*
426 * {{ This won't work if the "/" command is
427 * changed or invalidated by a .lesskey file. }}
428 */
429 ungetsc("/");
430 ungetsc(s);
431 ungetcc_end_command();
432 }
433 break;
434 }
435 }
436
437 /*
438 * Handler for -P option.
439 */
opt__P(int type,constant char * s)440 public void opt__P(int type, constant char *s)
441 {
442 char **proto;
443 PARG parg;
444
445 switch (type)
446 {
447 case INIT:
448 case TOGGLE:
449 /*
450 * Figure out which prototype string should be changed.
451 */
452 switch (*s)
453 {
454 case 's': proto = &prproto[PR_SHORT]; s++; break;
455 case 'm': proto = &prproto[PR_MEDIUM]; s++; break;
456 case 'M': proto = &prproto[PR_LONG]; s++; break;
457 case '=': proto = &eqproto; s++; break;
458 case 'h': proto = &hproto; s++; break;
459 case 'w': proto = &wproto; s++; break;
460 default: proto = &prproto[PR_SHORT]; break;
461 }
462 free(*proto);
463 *proto = save(s);
464 break;
465 case QUERY:
466 parg.p_string = prproto[pr_type];
467 error("%s", &parg);
468 break;
469 }
470 }
471
472 /*
473 * Handler for the -b option.
474 */
475 /*ARGSUSED*/
opt_b(int type,constant char * s)476 public void opt_b(int type, constant char *s)
477 {
478 switch (type)
479 {
480 case INIT:
481 case TOGGLE:
482 /*
483 * Set the new number of buffers.
484 */
485 ch_setbufspace((ssize_t) bufspace);
486 break;
487 case QUERY:
488 break;
489 }
490 }
491
492 /*
493 * Handler for the -i option.
494 */
495 /*ARGSUSED*/
opt_i(int type,constant char * s)496 public void opt_i(int type, constant char *s)
497 {
498 switch (type)
499 {
500 case TOGGLE:
501 chg_caseless();
502 break;
503 case QUERY:
504 case INIT:
505 break;
506 }
507 }
508
509 /*
510 * Handler for the -V option.
511 */
512 /*ARGSUSED*/
opt__V(int type,constant char * s)513 public void opt__V(int type, constant char *s)
514 {
515 switch (type)
516 {
517 case TOGGLE:
518 case QUERY:
519 dispversion();
520 break;
521 case INIT:
522 set_output(1); /* Force output to stdout per GNU standard for --version output. */
523 putstr("less ");
524 putstr(version);
525 putstr(" (");
526 putstr(pattern_lib_name());
527 putstr(" regular expressions)\n");
528 {
529 char constant *copyright =
530 "Copyright (C) 1984-2024 Mark Nudelman\n\n";
531 putstr(copyright);
532 }
533 if (version[strlen(version)-1] == 'x')
534 {
535 putstr("** This is an EXPERIMENTAL build of the 'less' software,\n");
536 putstr("** and may not function correctly.\n");
537 putstr("** Obtain release builds from the web page below.\n\n");
538 }
539 #if LESSTEST
540 putstr("This build supports LESSTEST.\n");
541 #endif /*LESSTEST*/
542 putstr("less comes with NO WARRANTY, to the extent permitted by law.\n");
543 putstr("For information about the terms of redistribution,\n");
544 putstr("see the file named README in the less distribution.\n");
545 putstr("Home page: https://greenwoodsoftware.com/less\n");
546 quit(QUIT_OK);
547 break;
548 }
549 }
550
551 #if MSDOS_COMPILER
552 /*
553 * Parse an MSDOS color descriptor.
554 */
colordesc(constant char * s,int * fg_color,int * bg_color,int * dattr)555 static void colordesc(constant char *s, int *fg_color, int *bg_color, int *dattr)
556 {
557 int fg, bg;
558 CHAR_ATTR attr;
559 if (parse_color(s, &fg, &bg, &attr) == CT_NULL)
560 {
561 PARG p;
562 p.p_string = s;
563 error("Invalid color string \"%s\"", &p);
564 } else
565 {
566 *fg_color = fg;
567 *bg_color = bg;
568 *dattr = 0;
569 #if MSDOS_COMPILER==WIN32C
570 if (attr & CATTR_UNDERLINE)
571 *dattr |= COMMON_LVB_UNDERSCORE;
572 if (attr & CATTR_STANDOUT)
573 *dattr |= COMMON_LVB_REVERSE_VIDEO;
574 #endif
575 }
576 }
577 #endif
578
color_from_namechar(char namechar)579 static int color_from_namechar(char namechar)
580 {
581 switch (namechar)
582 {
583 case 'B': return AT_COLOR_BIN;
584 case 'C': return AT_COLOR_CTRL;
585 case 'E': return AT_COLOR_ERROR;
586 case 'H': return AT_COLOR_HEADER;
587 case 'M': return AT_COLOR_MARK;
588 case 'N': return AT_COLOR_LINENUM;
589 case 'P': return AT_COLOR_PROMPT;
590 case 'R': return AT_COLOR_RSCROLL;
591 case 'S': return AT_COLOR_SEARCH;
592 case 'W': case 'A': return AT_COLOR_ATTN;
593 case 'n': return AT_NORMAL;
594 case 's': return AT_STANDOUT;
595 case 'd': return AT_BOLD;
596 case 'u': return AT_UNDERLINE;
597 case 'k': return AT_BLINK;
598 default:
599 if (namechar >= '1' && namechar <= '0'+NUM_SEARCH_COLORS)
600 return AT_COLOR_SUBSEARCH(namechar-'0');
601 return -1;
602 }
603 }
604
605 /*
606 * Handler for the -D option.
607 */
608 /*ARGSUSED*/
opt_D(int type,constant char * s)609 public void opt_D(int type, constant char *s)
610 {
611 PARG p;
612 int attr;
613
614 switch (type)
615 {
616 case INIT:
617 case TOGGLE:
618 #if MSDOS_COMPILER
619 if (*s == 'a')
620 {
621 sgr_mode = !sgr_mode;
622 break;
623 }
624 #endif
625 attr = color_from_namechar(s[0]);
626 if (attr < 0)
627 {
628 p.p_char = s[0];
629 error("Invalid color specifier '%c'", &p);
630 return;
631 }
632 if (!use_color && (attr & AT_COLOR))
633 {
634 error("Set --use-color before changing colors", NULL_PARG);
635 return;
636 }
637 s++;
638 #if MSDOS_COMPILER
639 if (!(attr & AT_COLOR))
640 {
641 switch (attr)
642 {
643 case AT_NORMAL:
644 colordesc(s, &nm_fg_color, &nm_bg_color, &nm_attr);
645 break;
646 case AT_BOLD:
647 colordesc(s, &bo_fg_color, &bo_bg_color, &bo_attr);
648 break;
649 case AT_UNDERLINE:
650 colordesc(s, &ul_fg_color, &ul_bg_color, &ul_attr);
651 break;
652 case AT_BLINK:
653 colordesc(s, &bl_fg_color, &bl_bg_color, &bl_attr);
654 break;
655 case AT_STANDOUT:
656 colordesc(s, &so_fg_color, &so_bg_color, &so_attr);
657 break;
658 }
659 if (type == TOGGLE)
660 {
661 init_win_colors();
662 at_enter(AT_STANDOUT);
663 at_exit();
664 }
665 } else
666 #endif
667 if (set_color_map(attr, s) < 0)
668 {
669 p.p_string = s;
670 error("Invalid color string \"%s\"", &p);
671 return;
672 }
673 break;
674 #if MSDOS_COMPILER
675 case QUERY:
676 p.p_string = (sgr_mode) ? "on" : "off";
677 error("SGR mode is %s", &p);
678 break;
679 #endif
680 }
681 }
682
683 /*
684 */
set_tabs(constant char * s,size_t len)685 public void set_tabs(constant char *s, size_t len)
686 {
687 int i;
688 constant char *es = s + len;
689 /* Start at 1 because tabstops[0] is always zero. */
690 for (i = 1; i < TABSTOP_MAX; )
691 {
692 int n = 0;
693 lbool v = FALSE;
694 while (s < es && *s == ' ')
695 s++;
696 for (; s < es && *s >= '0' && *s <= '9'; s++)
697 {
698 v = v || ckd_mul(&n, n, 10);
699 v = v || ckd_add(&n, n, *s - '0');
700 }
701 if (!v && n > tabstops[i-1])
702 tabstops[i++] = n;
703 while (s < es && *s == ' ')
704 s++;
705 if (s == es || *s++ != ',')
706 break;
707 }
708 if (i < 2)
709 return;
710 ntabstops = i;
711 tabdefault = tabstops[ntabstops-1] - tabstops[ntabstops-2];
712 }
713
714 /*
715 * Handler for the -x option.
716 */
opt_x(int type,constant char * s)717 public void opt_x(int type, constant char *s)
718 {
719 char msg[60+((INT_STRLEN_BOUND(int)+1)*TABSTOP_MAX)];
720 int i;
721 PARG p;
722
723 switch (type)
724 {
725 case INIT:
726 case TOGGLE:
727 set_tabs(s, strlen(s));
728 break;
729 case QUERY:
730 strcpy(msg, "Tab stops ");
731 if (ntabstops > 2)
732 {
733 for (i = 1; i < ntabstops; i++)
734 {
735 if (i > 1)
736 strcat(msg, ",");
737 sprintf(msg+strlen(msg), "%d", tabstops[i]);
738 }
739 sprintf(msg+strlen(msg), " and then ");
740 }
741 sprintf(msg+strlen(msg), "every %d spaces",
742 tabdefault);
743 p.p_string = msg;
744 error("%s", &p);
745 break;
746 }
747 }
748
749
750 /*
751 * Handler for the -" option.
752 */
opt_quote(int type,constant char * s)753 public void opt_quote(int type, constant char *s)
754 {
755 char buf[3];
756 PARG parg;
757
758 switch (type)
759 {
760 case INIT:
761 case TOGGLE:
762 if (s[0] == '\0')
763 {
764 openquote = closequote = '\0';
765 break;
766 }
767 if (s[1] != '\0' && s[2] != '\0')
768 {
769 error("-\" must be followed by 1 or 2 chars", NULL_PARG);
770 return;
771 }
772 openquote = s[0];
773 if (s[1] == '\0')
774 closequote = openquote;
775 else
776 closequote = s[1];
777 break;
778 case QUERY:
779 buf[0] = openquote;
780 buf[1] = closequote;
781 buf[2] = '\0';
782 parg.p_string = buf;
783 error("quotes %s", &parg);
784 break;
785 }
786 }
787
788 /*
789 * Handler for the --rscroll option.
790 */
791 /*ARGSUSED*/
opt_rscroll(int type,constant char * s)792 public void opt_rscroll(int type, constant char *s)
793 {
794 PARG p;
795
796 switch (type)
797 {
798 case INIT:
799 case TOGGLE: {
800 constant char *fmt;
801 int attr = AT_STANDOUT;
802 setfmt(s, &fmt, &attr, "*s>", FALSE);
803 if (strcmp(fmt, "-") == 0)
804 {
805 rscroll_char = 0;
806 } else
807 {
808 rscroll_attr = attr|AT_COLOR_RSCROLL;
809 if (*fmt == '\0')
810 rscroll_char = '>';
811 else
812 {
813 LWCHAR ch = step_charc(&fmt, +1, fmt+strlen(fmt));
814 if (pwidth(ch, rscroll_attr, 0, 0) > 1)
815 error("cannot set rscroll to a wide character", NULL_PARG);
816 else
817 rscroll_char = ch;
818 }
819 }
820 break; }
821 case QUERY: {
822 p.p_string = rscroll_char ? prchar((LWCHAR) rscroll_char) : "-";
823 error("rscroll character is %s", &p);
824 break; }
825 }
826 }
827
828 /*
829 * "-?" means display a help message.
830 * If from the command line, exit immediately.
831 */
832 /*ARGSUSED*/
opt_query(int type,constant char * s)833 public void opt_query(int type, constant char *s)
834 {
835 switch (type)
836 {
837 case QUERY:
838 case TOGGLE:
839 error("Use \"h\" for help", NULL_PARG);
840 break;
841 case INIT:
842 dohelp = 1;
843 }
844 }
845
846 /*ARGSUSED*/
opt_match_shift(int type,constant char * s)847 public void opt_match_shift(int type, constant char *s)
848 {
849 switch (type)
850 {
851 case INIT:
852 case TOGGLE:
853 toggle_fraction(&match_shift, &match_shift_fraction,
854 s, "--match-shift", calc_match_shift);
855 break;
856 case QUERY:
857 query_fraction(match_shift, match_shift_fraction,
858 "Search match shift is %d", "Search match shift is %s of screen width");
859 break;
860 }
861 }
862
calc_match_shift(void)863 public void calc_match_shift(void)
864 {
865 if (match_shift_fraction < 0)
866 return;
867 match_shift = (int) muldiv(sc_width, match_shift_fraction, NUM_FRAC_DENOM);
868 }
869
870 /*
871 * Handler for the --mouse option.
872 */
873 /*ARGSUSED*/
opt_mousecap(int type,constant char * s)874 public void opt_mousecap(int type, constant char *s)
875 {
876 switch (type)
877 {
878 case TOGGLE:
879 if (mousecap == OPT_OFF)
880 deinit_mouse();
881 else
882 init_mouse();
883 break;
884 case INIT:
885 case QUERY:
886 break;
887 }
888 }
889
890 /*
891 * Handler for the --wheel-lines option.
892 */
893 /*ARGSUSED*/
opt_wheel_lines(int type,constant char * s)894 public void opt_wheel_lines(int type, constant char *s)
895 {
896 switch (type)
897 {
898 case INIT:
899 case TOGGLE:
900 if (wheel_lines <= 0)
901 wheel_lines = default_wheel_lines();
902 break;
903 case QUERY:
904 break;
905 }
906 }
907
908 /*
909 * Handler for the --line-number-width option.
910 */
911 /*ARGSUSED*/
opt_linenum_width(int type,constant char * s)912 public void opt_linenum_width(int type, constant char *s)
913 {
914 PARG parg;
915
916 switch (type)
917 {
918 case INIT:
919 case TOGGLE:
920 if (linenum_width > MAX_LINENUM_WIDTH)
921 {
922 parg.p_int = MAX_LINENUM_WIDTH;
923 error("Line number width must not be larger than %d", &parg);
924 linenum_width = MIN_LINENUM_WIDTH;
925 }
926 break;
927 case QUERY:
928 break;
929 }
930 }
931
932 /*
933 * Handler for the --status-column-width option.
934 */
935 /*ARGSUSED*/
opt_status_col_width(int type,constant char * s)936 public void opt_status_col_width(int type, constant char *s)
937 {
938 PARG parg;
939
940 switch (type)
941 {
942 case INIT:
943 case TOGGLE:
944 if (status_col_width > MAX_STATUSCOL_WIDTH)
945 {
946 parg.p_int = MAX_STATUSCOL_WIDTH;
947 error("Status column width must not be larger than %d", &parg);
948 status_col_width = 2;
949 }
950 break;
951 case QUERY:
952 break;
953 }
954 }
955
956 /*
957 * Handler for the --file-size option.
958 */
959 /*ARGSUSED*/
opt_filesize(int type,constant char * s)960 public void opt_filesize(int type, constant char *s)
961 {
962 switch (type)
963 {
964 case INIT:
965 case TOGGLE:
966 if (want_filesize && curr_ifile != NULL && ch_length() == NULL_POSITION)
967 scan_eof();
968 break;
969 case QUERY:
970 break;
971 }
972 }
973
974 /*
975 * Handler for the --intr option.
976 */
977 /*ARGSUSED*/
opt_intr(int type,constant char * s)978 public void opt_intr(int type, constant char *s)
979 {
980 PARG p;
981
982 switch (type)
983 {
984 case INIT:
985 case TOGGLE:
986 intr_char = *s;
987 if (intr_char == '^' && s[1] != '\0')
988 intr_char = CONTROL(s[1]);
989 break;
990 case QUERY: {
991 p.p_string = prchar((LWCHAR) intr_char);
992 error("interrupt character is %s", &p);
993 break; }
994 }
995 }
996
997 /*
998 * Return the next number from a comma-separated list.
999 * Return -1 if the list entry is missing or empty.
1000 * Updates *sp to point to the first char of the next number in the list.
1001 */
next_cnum(constant char ** sp,constant char * printopt,constant char * errmsg,lbool * errp)1002 public int next_cnum(constant char **sp, constant char *printopt, constant char *errmsg, lbool *errp)
1003 {
1004 int n;
1005 *errp = FALSE;
1006 if (**sp == '\0') /* at end of line */
1007 return -1;
1008 if (**sp == ',') /* that's the next comma; we have an empty string */
1009 {
1010 ++(*sp);
1011 return -1;
1012 }
1013 n = getnumc(sp, printopt, errp);
1014 if (*errp)
1015 {
1016 PARG parg;
1017 parg.p_string = errmsg;
1018 error("invalid %s", &parg);
1019 return -1;
1020 }
1021 if (**sp == ',')
1022 ++(*sp);
1023 return n;
1024 }
1025
1026 /*
1027 * Parse a parameter to the --header option.
1028 * Value is "L,C,N", where each field is a decimal number or empty.
1029 */
parse_header(constant char * s,int * lines,int * cols,POSITION * start_pos)1030 static lbool parse_header(constant char *s, int *lines, int *cols, POSITION *start_pos)
1031 {
1032 int n;
1033 lbool err;
1034
1035 if (*s == '-')
1036 s = "0,0";
1037
1038 n = next_cnum(&s, "header", "number of lines", &err);
1039 if (err) return FALSE;
1040 if (n >= 0) *lines = n;
1041
1042 n = next_cnum(&s, "header", "number of columns", &err);
1043 if (err) return FALSE;
1044 if (n >= 0) *cols = n;
1045
1046 n = next_cnum(&s, "header", "line number", &err);
1047 if (err) return FALSE;
1048 if (n > 0)
1049 {
1050 LINENUM lnum = (LINENUM) n;
1051 if (lnum < 1) lnum = 1;
1052 *start_pos = find_pos(lnum);
1053 }
1054 return TRUE;
1055 }
1056
1057 /*
1058 * Handler for the --header option.
1059 */
1060 /*ARGSUSED*/
opt_header(int type,constant char * s)1061 public void opt_header(int type, constant char *s)
1062 {
1063 switch (type)
1064 {
1065 case INIT:
1066 case TOGGLE: {
1067 int lines = header_lines;
1068 int cols = header_cols;
1069 POSITION start_pos = (type == INIT) ? ch_zero() : position(TOP);
1070 if (start_pos == NULL_POSITION) start_pos = ch_zero();
1071 if (!parse_header(s, &lines, &cols, &start_pos))
1072 break;
1073 header_lines = lines;
1074 header_cols = cols;
1075 set_header(start_pos);
1076 calc_jump_sline();
1077 break; }
1078 case QUERY: {
1079 char buf[3*INT_STRLEN_BOUND(long)+3];
1080 PARG parg;
1081 SNPRINTF3(buf, sizeof(buf), "%ld,%ld,%ld", (long) header_lines, (long) header_cols, (long) find_linenum(header_start_pos));
1082 parg.p_string = buf;
1083 error("Header (lines,columns,line-number) is %s", &parg);
1084 break; }
1085 }
1086 }
1087
1088 /*
1089 * Handler for the --search-options option.
1090 */
1091 /*ARGSUSED*/
opt_search_type(int type,constant char * s)1092 public void opt_search_type(int type, constant char *s)
1093 {
1094 int st;
1095 PARG parg;
1096 char buf[16];
1097 char *bp;
1098 int i;
1099
1100 switch (type)
1101 {
1102 case INIT:
1103 case TOGGLE:
1104 st = 0;
1105 for (; *s != '\0'; s++)
1106 {
1107 switch (*s)
1108 {
1109 case 'E': case 'e': case CONTROL('E'): st |= SRCH_PAST_EOF; break;
1110 case 'F': case 'f': case CONTROL('F'): st |= SRCH_FIRST_FILE; break;
1111 case 'K': case 'k': case CONTROL('K'): st |= SRCH_NO_MOVE; break;
1112 case 'N': case 'n': case CONTROL('N'): st |= SRCH_NO_MATCH; break;
1113 case 'R': case 'r': case CONTROL('R'): st |= SRCH_NO_REGEX; break;
1114 case 'W': case 'w': case CONTROL('W'): st |= SRCH_WRAP; break;
1115 case '-': st = 0; break;
1116 case '^': break;
1117 default:
1118 if (*s >= '1' && *s <= '0'+NUM_SEARCH_COLORS)
1119 {
1120 st |= SRCH_SUBSEARCH(*s-'0');
1121 break;
1122 }
1123 parg.p_char = *s;
1124 error("invalid search option '%c'", &parg);
1125 return;
1126 }
1127 }
1128 def_search_type = norm_search_type(st);
1129 break;
1130 case QUERY:
1131 bp = buf;
1132 if (def_search_type & SRCH_PAST_EOF) *bp++ = 'E';
1133 if (def_search_type & SRCH_FIRST_FILE) *bp++ = 'F';
1134 if (def_search_type & SRCH_NO_MOVE) *bp++ = 'K';
1135 if (def_search_type & SRCH_NO_MATCH) *bp++ = 'N';
1136 if (def_search_type & SRCH_NO_REGEX) *bp++ = 'R';
1137 if (def_search_type & SRCH_WRAP) *bp++ = 'W';
1138 for (i = 1; i <= NUM_SEARCH_COLORS; i++)
1139 if (def_search_type & SRCH_SUBSEARCH(i))
1140 *bp++ = (char) ('0'+i);
1141 if (bp == buf)
1142 *bp++ = '-';
1143 *bp = '\0';
1144 parg.p_string = buf;
1145 error("search options: %s", &parg);
1146 break;
1147 }
1148 }
1149
1150 /*
1151 * Handler for the --no-search-headers, --no-search-header-lines
1152 * and --no-search-header-cols options.
1153 */
do_nosearch_headers(int type,int no_header_lines,int no_header_cols)1154 static void do_nosearch_headers(int type, int no_header_lines, int no_header_cols)
1155 {
1156 switch (type)
1157 {
1158 case INIT:
1159 case TOGGLE:
1160 nosearch_header_lines = no_header_lines;
1161 nosearch_header_cols = no_header_cols;
1162 break;
1163 case QUERY:
1164 if (nosearch_header_lines && nosearch_header_cols)
1165 error("Search does not include header lines or columns", NULL_PARG);
1166 else if (nosearch_header_lines)
1167 error("Search includes header columns but not header lines", NULL_PARG);
1168 else if (nosearch_header_cols)
1169 error("Search includes header lines but not header columns", NULL_PARG);
1170 else
1171 error("Search includes header lines and columns", NULL_PARG);
1172 }
1173 }
1174
1175 /*ARGSUSED*/
opt_nosearch_headers(int type,constant char * s)1176 public void opt_nosearch_headers(int type, constant char *s)
1177 {
1178 do_nosearch_headers(type, 1, 1);
1179 }
1180
1181 /*ARGSUSED*/
opt_nosearch_header_lines(int type,constant char * s)1182 public void opt_nosearch_header_lines(int type, constant char *s)
1183 {
1184 do_nosearch_headers(type, 1, 0);
1185 }
1186
1187 /*ARGSUSED*/
opt_nosearch_header_cols(int type,constant char * s)1188 public void opt_nosearch_header_cols(int type, constant char *s)
1189 {
1190 do_nosearch_headers(type, 0, 1);
1191 }
1192
1193 #if LESSTEST
1194 /*
1195 * Handler for the --tty option.
1196 */
1197 /*ARGSUSED*/
opt_ttyin_name(int type,constant char * s)1198 public void opt_ttyin_name(int type, constant char *s)
1199 {
1200 switch (type)
1201 {
1202 case INIT:
1203 ttyin_name = s;
1204 is_tty = 1;
1205 break;
1206 }
1207 }
1208 #endif /*LESSTEST*/
1209
chop_line(void)1210 public int chop_line(void)
1211 {
1212 return (chopline || header_cols > 0 || header_lines > 0);
1213 }
1214
1215 /*
1216 * Get the "screen window" size.
1217 */
get_swindow(void)1218 public int get_swindow(void)
1219 {
1220 if (swindow > 0)
1221 return (swindow);
1222 return (sc_height - header_lines + swindow);
1223 }
1224
1225