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