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