aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorXin LI <delphij@FreeBSD.org>2026-08-15 05:55:38 +0000
committerColin Percival <cperciva@FreeBSD.org>2026-08-18 20:17:56 +0000
commit7cc6a5d46484ef5e8f5d70c7e6170dcd90981b96 (patch)
tree2ca28bedb0ede57fd84d14912915433504ca1282
parent3a1396af294dc0f97655f9a8726c845ec04283a1 (diff)
MFV: less v704.
(cherry picked from commit fa0dc4f0f96a1b77d4be7bcdbf965897cda14521) (cherry picked from commit 86aea5b4db379327f02ca547575105788bfbe807) Approved by: re (cperciva)
-rw-r--r--contrib/less/NEWS126
-rw-r--r--contrib/less/README8
-rw-r--r--contrib/less/ch.c10
-rw-r--r--contrib/less/charset.c25
-rw-r--r--contrib/less/charset.h2
-rw-r--r--contrib/less/cmd.h3
-rw-r--r--contrib/less/cmdbuf.c6
-rw-r--r--contrib/less/command.c186
-rw-r--r--contrib/less/decode.c334
-rw-r--r--contrib/less/edit.c42
-rw-r--r--contrib/less/filename.c20
-rw-r--r--contrib/less/forwback.c53
-rw-r--r--contrib/less/funcs.h41
-rw-r--r--contrib/less/help.c28
-rw-r--r--contrib/less/input.c12
-rw-r--r--contrib/less/jump.c31
-rwxr-xr-xcontrib/less/less-osc8-open.sh64
-rw-r--r--contrib/less/less.h44
-rw-r--r--contrib/less/less.hlp26
-rw-r--r--contrib/less/less.nro327
-rw-r--r--contrib/less/lessecho.nro2
-rw-r--r--contrib/less/lesskey.nro3
-rw-r--r--contrib/less/lesskey_parse.c2
-rw-r--r--contrib/less/line.c62
-rw-r--r--contrib/less/lsystem.c2
-rw-r--r--contrib/less/main.c73
-rw-r--r--contrib/less/mark.c50
-rw-r--r--contrib/less/optfunc.c200
-rw-r--r--contrib/less/opttbl.c60
-rw-r--r--contrib/less/os.c45
-rw-r--r--contrib/less/output.c68
-rw-r--r--contrib/less/pattern.c8
-rw-r--r--contrib/less/pattern.h10
-rw-r--r--contrib/less/position.c82
-rw-r--r--contrib/less/prompt.c33
-rw-r--r--contrib/less/regexp.c206
-rw-r--r--contrib/less/regexp.h5
-rw-r--r--contrib/less/screen.c105
-rw-r--r--contrib/less/search.c138
-rw-r--r--contrib/less/tags.c8
-rw-r--r--contrib/less/ttyin.c2
-rw-r--r--contrib/less/version.c37
-rw-r--r--usr.bin/less/Makefile2
-rw-r--r--usr.bin/less/defines.h12
44 files changed, 1842 insertions, 761 deletions
diff --git a/contrib/less/NEWS b/contrib/less/NEWS
index 0f767aaeb059..a031353acf46 100644
--- a/contrib/less/NEWS
+++ b/contrib/less/NEWS
@@ -11,6 +11,132 @@
======================================================================
+ Major changes between "less" versions 702 and 704
+
+* Fix possibly passing unsafe options to man when opening an
+ OSC 8 link (github #779).
+
+* Fix possibly sending unsafe OSC sequence to terminal when file
+ contains an unterminated OSC sequence (github #781).
+
+* In Examine and Shell commands, expand % and # to shell-escaped
+ filenames (github #784).
+
+======================================================================
+
+ Major changes between "less" versions 692 and 702
+
+* Add --hilite-target option and -DJ to color target line (github #745).
+
+* Add --past-eof option.
+
+* Add --end-prompt option.
+
+* Add --emouse and --rmouse options, and horizontal mouse scrolling
+ and dragging (github #744).
+
+* Add -DT option to format tilde lines (github #725).
+
+* Change OSC 8 link handling: replace LESS_OSC8_xxx with LESS_OSC8_OPEN_xxx.
+ Remove %O from prompt expansion as no longer needed.
+ Any use of environment variables LESS_OSC8_xxx need to be manually
+ changed to use LESS_OSC8_OPEN_xxx.
+
+* Add ?o to prompt strings, to detect whether an OSC 8 link is selected.
+
+* When scrolling past end-of-file or before beginning-of-file,
+ stop when exactly one line is left on screen.
+
+* Make -w/-W highlight lines when moving backward as well as forward
+ (github #729).
+
+* Display pattern in "Pattern not found" message (github #731).
+
+* Allow m and M commands to take a numeric argument to specify the
+ line to be marked (github #736).
+
+* Allow ' command to take a numeric argument to specify the screen
+ position on which to place the marked line.
+
+* Allow lesskey to map keypad ENTER with \kpe (github #761).
+
+* Add "noaction" as a possible action in #line-edit section
+ in a lesskey file (github #761).
+
+* Support POSIX character classes with the built-in V8 regex library
+ (github #732).
+
+* Change | command to pipe just one line if the marked line is at the
+ top of the screen (github #733).
+
+* If OSC8 handler command begins with "-", suppress command echo,
+ and if it begins with ctrl-P, suppress "done" message (github #747).
+
+* Don't ask for confirmation when input is a binary file and stdout is
+ redirected. Fixes infinite loop in that situation (github #719).
+
+* Make early error messages go to stderr if stdout is redirected
+ (github #719).
+
+* Don't retry read after read error; fixes hang when attempting to read
+ a directory or other unreadable file (github #741).
+
+* Fix incorrect restoration of saved mark if not at top of screen.
+
+* With --save-marks, don't save a mark that was cleared with ESC-m.
+
+* Fix buffer overflow when using malformed lesskey file (github #721).
+
+* Fix unexpected scrolling past end of file (github #720).
+
+* Fix bug when env var in LESSKEY_CONTENT partially matches env var
+ defined in lesskey file (github #727).
+
+* Fix bug when env var in lesskey file matches tail of env var used
+ by less (github #728).
+
+* Fix command parsing bug when one command is a substring of another.
+ Also fixes --no-paste option (github #724).
+
+* Fix incorrect display using --color to set character attributes
+ without color, such as -DS-u (github #730).
+
+* Fix crash when tags file contains invalid line number 0 (github #743).
+
+* Fix build when tparm() doesn't use varargs (github #748).
+
+* Fix prompt overflow when filtering with long prompt (github #749).
+
+* Fix incorrect highlighting when change -i while filtering (github #750).
+
+* Fix erroneous error mesage using --show-preproc-error with
+ some shells (github #753).
+
+* Fix erroneous highlighting when using a search pattern containing more
+ than 5 pairs of parentheses with PCRE2 (github #754).
+
+* When ^X interrupts F mode, discard pending keys as is done when
+ ^C interrupts it (github #757).
+
+* Fix bug in Windows where pressing any key during "waiting for data" would
+ prevent a subsequent ^X from working.
+
+* Fix erroneous display in some situations when using LESS_LINES (github #759).
+
+* Fix erroneous display after certain messages are displayed in a
+ very narrow terminal (github #760).
+
+* Don't init terminal if stdout is not a tty (github #768).
+
+* Fix bug clicking OSC 8 link that crosses a screen line boundary
+ (github #775).
+
+* Fix beeps when resizing window on Windows (github #771).
+
+* Fix beeps when paging continuously on Windows (github #772).
+
+======================================================================
+
Major changes between "less" versions 691 and 692
* Revert HOME key to scroll to beginning of file and END key
diff --git a/contrib/less/README b/contrib/less/README
index a6954eb67c1e..384d99155616 100644
--- a/contrib/less/README
+++ b/contrib/less/README
@@ -108,14 +108,6 @@ INSTALLATION (Unix & Linux systems only):
bindir and/or mandir to the appropriate directories.
-Note to hackers: comments noting possible improvements are enclosed
-in double curly brackets {{ like this }}.
-
-(Note that the above note was originally written at a time when
-"hackers" most commonly meant "enthusiastic and dedicated computer
-programmers", not "persons who attempt to circumvent computer security".)
-
-
=======================================================================
INSTALLATION (Windows-95, Windows-98 and Windows-NT systems only,
diff --git a/contrib/less/ch.c b/contrib/less/ch.c
index 47d31ecffadd..dfe1e7169f72 100644
--- a/contrib/less/ch.c
+++ b/contrib/less/ch.c
@@ -23,6 +23,7 @@
typedef POSITION BLOCKNUM;
public lbool ignore_eoi = FALSE;
+public lbool read_error = FALSE;
/*
* Pool of buffers holding the most recently used blocks of the input file.
@@ -292,13 +293,14 @@ static int ch_get(void)
if (n < 0)
{
#if MSDOS_COMPILER==WIN32C
- if (errno != EPIPE)
+ if (errno == EPIPE)
+ n = 0;
+ else
#endif
{
- error("read error", NULL_PARG);
- clear_eol();
+ read_error = TRUE;
+ return (EOI);
}
- n = 0;
}
#if LOGFILE
diff --git a/contrib/less/charset.c b/contrib/less/charset.c
index 74159404148b..f2020e4ef5c5 100644
--- a/contrib/less/charset.c
+++ b/contrib/less/charset.c
@@ -585,20 +585,21 @@ public constant char * prutfchar(LWCHAR ch)
/*
* Get the length of a UTF-8 character in bytes.
*/
-public int utf_len(char ch)
+public int utf_len(char ach)
{
- if ((ch & 0x80) == 0)
+ unsigned char ch = (unsigned char) ach;
+ if (IS_ASCII_OCTET(ch))
return 1;
- if ((ch & 0xE0) == 0xC0)
+ if (IS_UTF8_LEAD2(ch))
return 2;
- if ((ch & 0xF0) == 0xE0)
+ if (IS_UTF8_LEAD3(ch))
return 3;
- if ((ch & 0xF8) == 0xF0)
+ if (IS_UTF8_LEAD4(ch))
return 4;
#if 0
- if ((ch & 0xFC) == 0xF8)
+ if (IS_UTF8_LEAD5(ch))
return 5;
- if ((ch & 0xFE) == 0xFC)
+ if (IS_UTF8_LEAD6(ch))
return 6;
#endif
/* Invalid UTF-8 encoding. */
@@ -608,16 +609,17 @@ public int utf_len(char ch)
/*
* Does the parameter point to the lead byte of a well-formed UTF-8 character?
*/
-public lbool is_utf8_well_formed(constant char *ss, int slen)
+public lbool is_utf8_well_formed(constant char *ssa, int slen)
{
int i;
int len;
- unsigned char s0 = (unsigned char) ss[0];
+ constant unsigned char *ss = (constant unsigned char *) ssa;
+ unsigned char s0 = ss[0];
- if (IS_UTF8_INVALID(s0))
+ if (IS_UTF8_LEAD5(s0) || IS_UTF8_LEAD6(s0) || IS_UTF8_INVALID(s0))
return (FALSE);
- len = utf_len(ss[0]);
+ len = utf_len(s0);
if (len > slen)
return (FALSE);
if (len == 1)
@@ -904,6 +906,7 @@ public lbool is_composing_char(LWCHAR ch)
public lbool is_ubin_char(LWCHAR ch)
{
if (is_in_table(ch, &user_prt_table)) return FALSE;
+ if (ch > MAX_UNICODE) return TRUE;
return is_in_table(ch, &user_ubin_table) ||
(is_in_table(ch, &ubin_table) ||
(bs_mode == BS_CONTROL && is_in_table(ch, &fmt_table) &&
diff --git a/contrib/less/charset.h b/contrib/less/charset.h
index cf2e02547cb5..dab7474acc73 100644
--- a/contrib/less/charset.h
+++ b/contrib/less/charset.h
@@ -16,3 +16,5 @@
#define IS_UTF8_LEAD6(c) (((c) & 0xFE) == 0xFC)
#define IS_UTF8_INVALID(c) (((c) & 0xFE) == 0xFE)
#define IS_UTF8_LEAD(c) (((c) & 0xC0) == 0xC0 && !IS_UTF8_INVALID(c))
+
+#define MAX_UNICODE 0x10FFFF
diff --git a/contrib/less/cmd.h b/contrib/less/cmd.h
index 945f99719d0c..fb66a6adb99d 100644
--- a/contrib/less/cmd.h
+++ b/contrib/less/cmd.h
@@ -87,6 +87,8 @@
#define A_START_PASTE 75 /* must not overlap EC_* */
#define A_END_PASTE 76 /* must not overlap EC_* */
#define A_F_FOREVER_BELL 77
+#define A_L_MOUSE 78
+#define A_R_MOUSE 79
/* These values must not conflict with any A_* or EC_* value. */
#define A_INVALID 100
@@ -178,5 +180,6 @@
#define SK_CTL_END 37
#define SK_SHIFT_LEFT_ARROW 38
#define SK_SHIFT_RIGHT_ARROW 39
+#define SK_PAD_ENTER 41
#define SK_CONTROL_K 40
diff --git a/contrib/less/cmdbuf.c b/contrib/less/cmdbuf.c
index 1c3b3d461ad9..9f13473eba03 100644
--- a/contrib/less/cmdbuf.c
+++ b/contrib/less/cmdbuf.c
@@ -113,8 +113,8 @@ static struct mlist *curr_mlist = NULL;
static int curr_cmdflags;
static char cmd_mbc_buf[MAX_UTF_CHAR_LEN];
-static int cmd_mbc_buf_len;
-static int cmd_mbc_buf_index;
+static size_t cmd_mbc_buf_len;
+static size_t cmd_mbc_buf_index;
/*
@@ -1330,7 +1330,7 @@ static int cmd_uchar(char c, size_t *plen)
goto retry;
}
- *plen = (size_t) cmd_mbc_buf_len; /*{{type-issue}}*/
+ *plen = cmd_mbc_buf_len;
cmd_mbc_buf_len = 0;
}
return (CC_PASS);
diff --git a/contrib/less/command.c b/contrib/less/command.c
index 1bb8d006f8ec..09eb1614c76d 100644
--- a/contrib/less/command.c
+++ b/contrib/less/command.c
@@ -23,10 +23,11 @@
extern int erase_char, erase2_char, kill_char;
extern int sigs;
extern int quit_if_one_screen;
-extern int one_screen;
+extern lbool one_screen;
extern int sc_width;
extern int sc_height;
extern char *kent;
+extern lbool kent_mapped;
extern int swindow;
extern int jump_sline;
extern lbool quitting;
@@ -48,13 +49,16 @@ extern void *ml_search;
extern void *ml_examine;
extern int wheel_lines;
extern int def_search_type;
+extern int hilite_target;
extern lbool search_wrapped;
extern int no_paste;
extern lbool pasting;
extern int no_edit_warn;
+extern lbool read_error;
extern POSITION soft_eof;
extern POSITION search_incr_start;
extern char *first_cmd_at_prompt;
+extern lbool prompting;
#if SHELL_ESCAPE || PIPEC
extern void *ml_shell;
#endif
@@ -65,9 +69,9 @@ extern constant char *editproto;
extern char *osc8_uri;
#endif
extern int shift_count;
-extern int forw_prompt;
+extern lbool forw_prompt;
extern int incr_search;
-extern int full_screen;
+extern lbool full_screen;
#if MSDOS_COMPILER==WIN32C
extern int utf_mode;
extern unsigned less_acp;
@@ -85,6 +89,7 @@ static struct loption *curropt;
static lbool opt_lower;
static int optflag;
static lbool optgetname;
+static POSITION toppos;
static POSITION bottompos;
static int save_hshift;
static int save_bs_mode;
@@ -208,7 +213,7 @@ static void mca_search1(void)
cmd_putstr("/");
else
cmd_putstr("?");
- forw_prompt = 0;
+ forw_prompt = FALSE;
}
static void mca_search(void)
@@ -248,7 +253,7 @@ static void mca_opt_toggle(void)
cmd_putstr("!");
break;
}
- forw_prompt = 0;
+ forw_prompt = FALSE;
set_mlist(NULL, CF_OPTION);
}
@@ -851,12 +856,6 @@ public int is_screen_trashed(void)
static void make_display(void)
{
/*
- * If not full_screen, we can't rely on scrolling to fill the screen.
- * We need to clear and repaint screen before any change.
- */
- if (!full_screen && !(quit_if_one_screen && one_screen))
- lclear();
- /*
* If nothing is displayed yet, display starting from initial_scrpos.
*/
if (empty_screen())
@@ -885,11 +884,46 @@ static void make_display(void)
}
/*
+ * Display any message that needs to be displayed before the prompt.
+ */
+static void prompt_message(void)
+{
+ if (read_error)
+ {
+ error("read error", NULL_PARG);
+ read_error = FALSE;
+ }
+ if (search_wrapped)
+ {
+ if (search_type & SRCH_BACK)
+ error("Search hit top; continuing at bottom", NULL_PARG);
+ else
+ error("Search hit bottom; continuing at top", NULL_PARG);
+ search_wrapped = FALSE;
+ }
+#if OSC8_LINK
+ if (osc8_uri != NULL)
+ {
+ PARG parg;
+ parg.p_string = osc8_uri;
+ error("Link: %s", &parg);
+ free(osc8_uri);
+ osc8_uri = NULL;
+ }
+#endif
+}
+
+/*
* Display the appropriate prompt.
*/
static void prompt(void)
{
constant char *p;
+ int attr;
+#if MSDOS_COMPILER==WIN32C
+ WCHAR w[MAX_PATH*2];
+ char a[MAX_PATH*2];
+#endif
if (ungot != NULL && !ungot->ug_end_command)
{
@@ -904,6 +938,10 @@ static void prompt(void)
* Make sure the screen is displayed.
*/
make_display();
+
+ if (hilite_target)
+ draw_target_attn(TRUE); /* Redraw target line for --hilite-target. */
+ toppos = position(TOP);
bottompos = position(BOTTOM_PLUS_ONE);
/*
@@ -921,7 +959,7 @@ static void prompt(void)
entire_file_displayed() && !(ch_getflags() & CH_HELPFILE) &&
next_ifile(curr_ifile) == NULL_IFILE)
quit(QUIT_OK);
- quit_if_one_screen = FALSE; /* only get one chance at this */
+ quit_if_one_screen = 0; /* only get one chance at this */
if (first_cmd_at_prompt != NULL)
{
ungetsc(first_cmd_at_prompt);
@@ -932,6 +970,7 @@ static void prompt(void)
#if MSDOS_COMPILER==WIN32C
/*
* In Win32, display the file name in the window title.
+ * {{ Seems like this should be done in edit_ifile, not on every prompt. }}
*/
if (!(ch_getflags() & CH_HELPFILE))
{
@@ -958,50 +997,43 @@ static void prompt(void)
if (!forw_prompt)
clear_bot();
clear_cmd();
- forw_prompt = 0;
+ forw_prompt = FALSE;
+ prompt_message();
+ /* We called make_display above, but if prompt_message displayed
+ * a message longer than the screen width, we may have trashed
+ * the screen and need to call make_display again. */
+ if (is_screen_trashed())
+ make_display();
p = pr_string();
-#if HILITE_SEARCH
- if (is_filtering())
- putstr("& ");
-#endif
- if (search_wrapped)
- {
- if (search_type & SRCH_BACK)
- error("Search hit top; continuing at bottom", NULL_PARG);
- else
- error("Search hit bottom; continuing at top", NULL_PARG);
- search_wrapped = FALSE;
- }
-#if OSC8_LINK
- if (osc8_uri != NULL)
- {
- PARG parg;
- parg.p_string = osc8_uri;
- error("Link: %s", &parg);
- free(osc8_uri);
- osc8_uri = NULL;
- }
-#endif
if (p == NULL || *p == '\0')
{
- at_enter(AT_NORMAL|AT_COLOR_PROMPT);
- putchr(':');
- at_exit();
+ p = ":";
+ attr = AT_NORMAL|AT_COLOR_PROMPT;
} else
{
+ attr = AT_STANDOUT|AT_COLOR_PROMPT;
#if MSDOS_COMPILER==WIN32C
- WCHAR w[MAX_PATH*2];
- char a[MAX_PATH*2];
MultiByteToWideChar(less_acp, 0, p, -1, w, countof(w));
WideCharToMultiByte(utf_mode ? CP_UTF8 : GetConsoleOutputCP(),
0, w, -1, a, sizeof(a), NULL, NULL);
p = a;
#endif
- load_line(p);
- put_line(FALSE);
}
+#if HILITE_SEARCH
+ if (is_filtering())
+ {
+ constant char *amp = "& ";
+ load_line(p, attr, strlen(amp)+1);
+ putstr(amp);
+ } else
+#endif
+ {
+ load_line(p, attr, 1);
+ }
+ put_line(FALSE);
clear_eol();
resume_screen();
+ prompting = TRUE;
}
/*
@@ -1139,8 +1171,9 @@ static char getcc_repl(char constant *orig, char constant *repl, char (*gr_getc)
*/
public char getcc(void)
{
- /* Replace kent (keypad Enter) with a newline. */
- return getcc_repl(kent, "\n", getccu, ungetcc);
+ /* Replace kent (keypad Enter) with a newline.
+ * However don't do this if kent is mapped to a command via lesskey. */
+ return getcc_repl(kent_mapped ? NULL : kent, "\n", getccu, ungetcc);
}
/*
@@ -1291,7 +1324,14 @@ static void multi_search(constant char *pattern, int n, int silent)
* Print an error message if we haven't already.
*/
if (n > 0 && !silent)
- error("Pattern not found", NULL_PARG);
+ {
+ PARG parg;
+ parg.p_string = prev_pattern_text();
+ if (parg.p_string == NULL) /* {{ can this happen? }} */
+ error("Pattern not found", NULL_PARG);
+ else
+ error("Pattern not found: %s", &parg);
+ }
if (changed_file)
{
@@ -1348,7 +1388,7 @@ static int forw_loop(int action)
/*
* Ignore subsequent (pasted) input chars.
*/
-public void start_ignoring_input()
+public void start_ignoring_input(void)
{
ignoring_input = TRUE;
#if HAVE_TIME
@@ -1359,7 +1399,7 @@ public void start_ignoring_input()
/*
* Stop ignoring input chars.
*/
-public void stop_ignoring_input()
+public void stop_ignoring_input(void)
{
ignoring_input = FALSE;
pasting = FALSE;
@@ -1443,6 +1483,8 @@ public void commands(void)
c = getcc();
again:
+ if (c == READ_AGAIN)
+ continue;
if (sigs)
continue;
@@ -1582,12 +1624,13 @@ public void commands(void)
if (number <= 0)
number = get_swindow();
cmd_exec();
+ if (show_attn && toppos != NULL_POSITION && toppos > ch_zero())
+ set_attnpos(toppos-1);
backward((int) number, FALSE, TRUE, FALSE);
break;
case A_F_LINE:
case A_F_NEWLINE:
-
/*
* Forward N (default 1) line.
*/
@@ -1607,6 +1650,8 @@ public void commands(void)
if (number <= 0)
number = 1;
cmd_exec();
+ if (show_attn == OPT_ONPLUS && number > 1 && toppos != NULL_POSITION && toppos > ch_zero())
+ set_attnpos(toppos-1);
backward((int) number, FALSE, FALSE, action == A_B_NEWLINE && !chopline);
break;
@@ -1626,6 +1671,29 @@ public void commands(void)
backward(wheel_lines, FALSE, FALSE, FALSE);
break;
+ case A_L_MOUSE:
+ /*
+ * Left wheel_lines columns.
+ */
+ cmd_exec();
+ pos_rehead(FALSE);
+ if (wheel_lines > hshift)
+ hshift = 0;
+ else
+ hshift -= wheel_lines;
+ screen_trashed();
+ break;
+
+ case A_R_MOUSE:
+ /*
+ * Right wheel_lines columns.
+ */
+ cmd_exec();
+ pos_rehead(FALSE);
+ hshift += wheel_lines;
+ screen_trashed();
+ break;
+
case A_FF_LINE:
/*
* Force forward N (default 1) line.
@@ -1645,6 +1713,8 @@ public void commands(void)
if (number <= 0)
number = 1;
cmd_exec();
+ if (show_attn == OPT_ONPLUS && number > 1 && toppos != NULL_POSITION && toppos > ch_zero())
+ set_attnpos(toppos-1);
backward((int) number, TRUE, FALSE, FALSE);
break;
@@ -1667,6 +1737,8 @@ public void commands(void)
if (number <= 0)
number = get_swindow();
cmd_exec();
+ if (show_attn == OPT_ONPLUS && toppos != NULL_POSITION && toppos > ch_zero())
+ set_attnpos(toppos-1);
backward((int) number, TRUE, FALSE, FALSE);
break;
@@ -1704,6 +1776,8 @@ public void commands(void)
if (number > 0)
wscroll = (int) number;
cmd_exec();
+ if (show_attn == OPT_ONPLUS && toppos != NULL_POSITION && toppos > ch_zero())
+ set_attnpos(toppos-1);
backward(wscroll, FALSE, FALSE, FALSE);
break;
@@ -1963,6 +2037,7 @@ public void commands(void)
/*
* Clear search string highlighting.
*/
+ cmd_exec();
undo_search(action == A_CLR_SEARCH);
break;
@@ -2229,9 +2304,11 @@ public void commands(void)
}
start_mca(A_SETMARK, "set mark: ", NULL, 0);
c = getcc();
+ make_display();
+ cmd_exec();
if (is_erase_char(c) || is_newline_char(c))
break;
- setmark(c, action == A_SETMARKBOT ? BOTTOM : TOP);
+ setmark(c, action == A_SETMARKBOT ? BOTTOM : TOP, number);
repaint();
break;
@@ -2241,6 +2318,7 @@ public void commands(void)
*/
start_mca(A_CLRMARK, "clear mark: ", NULL, 0);
c = getcc();
+ cmd_exec();
if (is_erase_char(c) || is_newline_char(c))
break;
clrmark(c);
@@ -2256,7 +2334,7 @@ public void commands(void)
if (is_erase_char(c) || is_newline_char(c))
break;
cmd_exec();
- gomark(c);
+ gomark(c, number);
break;
case A_PIPE:
@@ -2299,7 +2377,7 @@ public void commands(void)
number = (shift_count > 0) ? shift_count : sc_width / 2;
if (number > hshift)
number = hshift;
- pos_rehead();
+ pos_rehead(FALSE);
hshift -= (int) number;
screen_trashed();
cmd_exec();
@@ -2313,7 +2391,7 @@ public void commands(void)
shift_count = (int) number;
else
number = (shift_count > 0) ? shift_count : sc_width / 2;
- pos_rehead();
+ pos_rehead(FALSE);
hshift += (int) number;
screen_trashed();
cmd_exec();
@@ -2323,7 +2401,7 @@ public void commands(void)
/*
* Shift view left to margin.
*/
- pos_rehead();
+ pos_rehead(FALSE);
hshift = 0;
screen_trashed();
cmd_exec();
@@ -2333,7 +2411,7 @@ public void commands(void)
/*
* Shift view right to view rightmost char on screen.
*/
- pos_rehead();
+ pos_rehead(FALSE);
hshift = rrshift();
screen_trashed();
cmd_exec();
diff --git a/contrib/less/decode.c b/contrib/less/decode.c
index 3615f5b905d7..393082c84097 100644
--- a/contrib/less/decode.c
+++ b/contrib/less/decode.c
@@ -34,12 +34,12 @@
#include "lesskey.h"
extern int erase_char, erase2_char, kill_char;
-extern int mousecap;
+extern int emouse;
+extern int mouse_reverse;
+extern int hshift;
extern int sc_height;
extern char *no_config;
-static constant lbool allow_drag = TRUE;
-
#if USERFILE
/* "content" is lesskey source, never binary. */
static void add_content_table(int (*call_lesskey)(constant char *, lbool), constant char *envname, lbool sysvar);
@@ -286,17 +286,19 @@ static unsigned char edittable[] =
ESC,'[','2','0','1','~',0, A_END_PASTE, /* close paste bracket */
};
-static unsigned char dflt_vartable[] =
+/*
+ * Default environment variables.
+ * Note: "\201" is (A_EXTRA|EV_OK).
+ */
+static char dflt_vartable[] =
{
- 'L','E','S','S','_','O','S','C','8','_','m','a','n', 0, EV_OK|A_EXTRA,
- /* echo '%o' | sed -e "s,^man\:\\([^(]*\\)( *\\([^)]*\\)\.*,-man '\\2' '\\1'," -e"t X" -e"s,\.*,-echo Invalid man link," -e"\: X" */
- 'e','c','h','o',' ','\'','%','o','\'',' ','|',' ','s','e','d',' ','-','e',' ','"','s',',','^','m','a','n','\\',':','\\','\\','(','[','^','(',']','*','\\','\\',')','(',' ','*','\\','\\','(','[','^',')',']','*','\\','\\',')','\\','.','*',',','-','m','a','n',' ','\'','\\','\\','2','\'',' ','\'','\\','\\','1','\'',',','"',' ','-','e','"','t',' ','X','"',' ','-','e','"','s',',','\\','.','*',',','-','e','c','h','o',' ','I','n','v','a','l','i','d',' ','m','a','n',' ','l','i','n','k',',','"',' ','-','e','"','\\',':',' ','X','"',
- 0,
-
- 'L','E','S','S','_','O','S','C','8','_','f','i','l','e', 0, EV_OK|A_EXTRA,
- /* eval `echo '%o' | sed -e "s,^file://\\([^/]*\\)\\(.*\\),_H=\\1;_P=\\2;_E=0," -e"t X" -e"s,.*,_E=1," -e": X"`; if [ "$_E" = 1 ]; then echo -echo Invalid file link; elif [ -z "$_H" -o "$_H" = localhost -o "$_H" = $HOSTNAME ]; then echo ":e $_P"; else echo -echo Cannot open remote file on "$_H"; fi */
- 'e','v','a','l',' ','`','e','c','h','o',' ','\'','%','o','\'',' ','|',' ','s','e','d',' ','-','e',' ','"','s',',','^','f','i','l','e','\\',':','/','/','\\','\\','(','[','^','/',']','*','\\','\\',')','\\','\\','(','\\','.','*','\\','\\',')',',','_','H','=','\\','\\','1',';','_','P','=','\\','\\','2',';','_','E','=','0',',','"',' ','-','e','"','t',' ','X','"',' ','-','e','"','s',',','\\','.','*',',','_','E','=','1',',','"',' ','-','e','"','\\',':',' ','X','"','`',';',' ','i','f',' ','[',' ','"','$','_','E','"',' ','=',' ','1',' ',']',';',' ','t','h','e','n',' ','e','c','h','o',' ','-','e','c','h','o',' ','I','n','v','a','l','i','d',' ','f','i','l','e',' ','l','i','n','k',';',' ','e','l','i','f',' ','[',' ','-','z',' ','"','$','_','H','"',' ','-','o',' ','"','$','_','H','"',' ','=',' ','l','o','c','a','l','h','o','s','t',' ','-','o',' ','"','$','_','H','"',' ','=',' ','$','H','O','S','T','N','A','M','E',' ',']',';',' ','t','h','e','n',' ','e','c','h','o',' ','"','\\',':','e',' ','$','_','P','"',';',' ','e','l','s','e',' ','e','c','h','o',' ','-','e','c','h','o',' ','C','a','n','n','o','t',' ','o','p','e','n',' ','r','e','m','o','t','e',' ','f','i','l','e',' ','o','n',' ','"','$','_','H','"',';',' ','f','i',
- 0,
+ "LESS_OSC8_OPEN_ANY\0\201"
+#ifdef LIBEXECDIR
+ "-" LIBEXECDIR "/less-osc8-open"
+#else
+ "-less-osc8-open"
+#endif
+ "\0"
};
/*
@@ -317,6 +319,9 @@ static struct tablelist *list_ecmd_tables = NULL;
static struct tablelist *list_var_tables = NULL;
static struct tablelist *list_sysvar_tables = NULL;
+#if USERFILE
+static struct tablelist * find_stop_table(struct tablelist *t);
+#endif
/*
* Expand special key abbreviations in a command table.
@@ -335,7 +340,7 @@ static void expand_special_keys(unsigned char *table, size_t len)
* Rewrite each command in the table with any
* special key abbreviations expanded.
*/
- for (to = fm; *fm != '\0'; )
+ for (to = fm; fm < table + len && *fm != '\0'; )
{
if (*fm != SK_SPECIAL_KEY)
{
@@ -352,7 +357,7 @@ static void expand_special_keys(unsigned char *table, size_t len)
* output by the special key on this terminal.
*/
repl = special_key_str(fm[1]);
- klen = fm[2] & 0377;
+ klen = fm[2];
fm += klen;
if (repl == NULL || strlen(repl) > klen)
repl = "\377";
@@ -367,10 +372,10 @@ static void expand_special_keys(unsigned char *table, size_t len)
while (to <= fm)
*to++ = A_SKIP;
fm++;
- a = *fm++ & 0377;
+ a = *fm++;
if (a & A_EXTRA)
{
- while (*fm++ != '\0')
+ while (fm < table + len && *fm++ != '\0')
continue;
}
}
@@ -404,12 +409,15 @@ public void expand_cmd_tables(void)
*/
public void init_cmds(void)
{
+ struct tablelist *t;
+ unsigned char *udflt_vartable = (unsigned char *) dflt_vartable;
+
/*
* Add the default command tables.
*/
add_fcmd_table(cmdtable, sizeof(cmdtable));
add_ecmd_table(edittable, sizeof(edittable));
- add_sysvar_table(dflt_vartable, sizeof(dflt_vartable));
+ add_sysvar_table(udflt_vartable, sizeof(dflt_vartable));
#if USERFILE
#ifdef BINDIR /* For backwards compatibility */
/* Try to add tables in the OLD system lesskey file. */
@@ -449,6 +457,16 @@ public void init_cmds(void)
add_content_table(lesskey_content, "LESSKEY_CONTENT_SYSTEM", TRUE);
add_content_table(lesskey_content, "LESSKEY_CONTENT", FALSE);
+
+ /*
+ * If any command table contains a #stop directive, discard all other tables.
+ */
+ t = find_stop_table(list_fcmd_tables);
+ if (t != NULL)
+ {
+ t->t_next = NULL;
+ list_fcmd_tables = t;
+ }
#endif /* USERFILE */
}
@@ -555,7 +573,9 @@ public void add_sysvar_table(unsigned char *buf, size_t len)
*/
static int mouse_wheel_down(void)
{
- return ((mousecap == OPT_ONPLUS) ? A_B_MOUSE : A_F_MOUSE);
+ if (!(emouse & EMOUSE_VSCROLL))
+ return A_NOACTION;
+ return mouse_reverse ? A_B_MOUSE : A_F_MOUSE;
}
/*
@@ -563,7 +583,29 @@ static int mouse_wheel_down(void)
*/
static int mouse_wheel_up(void)
{
- return ((mousecap == OPT_ONPLUS) ? A_F_MOUSE : A_B_MOUSE);
+ if (!(emouse & EMOUSE_VSCROLL))
+ return A_NOACTION;
+ return mouse_reverse ? A_F_MOUSE : A_B_MOUSE;
+}
+
+/*
+ * Return action for a mouse wheel left event.
+ */
+static int mouse_wheel_left(void)
+{
+ if (!(emouse & EMOUSE_HSCROLL))
+ return A_NOACTION;
+ return mouse_reverse ? A_R_MOUSE : A_L_MOUSE;
+}
+
+/*
+ * Return action for a mouse wheel right event.
+ */
+static int mouse_wheel_right(void)
+{
+ if (!(emouse & EMOUSE_HSCROLL))
+ return A_NOACTION;
+ return mouse_reverse ? A_L_MOUSE : A_R_MOUSE;
}
/*
@@ -571,32 +613,48 @@ static int mouse_wheel_up(void)
*/
static int mouse_button_left(int x, int y, lbool down, lbool drag)
{
+ static int last_drag_x = -1;
static int last_drag_y = -1;
static int last_click_y = -1;
if (down && !drag)
{
+ last_drag_x = x;
last_drag_y = last_click_y = y;
}
- if (allow_drag && drag && last_drag_y >= 0)
+ if (drag)
{
- /* Drag text up/down */
- if (y > last_drag_y)
- {
- cmd_exec();
- backward(y - last_drag_y, FALSE, FALSE, FALSE);
- last_drag_y = y;
- } else if (y < last_drag_y)
- {
+ if ((emouse & EMOUSE_HDRAG) && last_drag_x >= 0 && x != last_drag_x) {
+ /* Drag text left/right */
+ pos_rehead(FALSE);
+ if (hshift < x - last_drag_x)
+ hshift = 0;
+ else
+ hshift -= x - last_drag_x;
+ screen_trashed();
cmd_exec();
- forward(last_drag_y - y, FALSE, FALSE, FALSE);
- last_drag_y = y;
+ last_drag_x = x;
}
- } else if (!down)
+ if ((emouse & EMOUSE_VDRAG) && last_drag_y >= 0) {
+ /* Drag text up/down */
+ if (y > last_drag_y)
+ {
+ cmd_exec();
+ backward(y - last_drag_y, FALSE, FALSE, FALSE);
+ last_drag_y = y;
+ } else if (y < last_drag_y)
+ {
+ cmd_exec();
+ forward(last_drag_y - y, FALSE, FALSE, FALSE);
+ last_drag_y = y;
+ }
+ }
+ } else if ((emouse & EMOUSE_LCLICK) && !down)
{
#if OSC8_LINK
if (secure_allow(SF_OSC8_OPEN))
{
+ cmd_exec();
if (osc8_click(y, x))
return (A_NOACTION);
}
@@ -605,7 +663,8 @@ static int mouse_button_left(int x, int y, lbool down, lbool drag)
#endif /* OSC8_LINK */
if (y < sc_height-1 && y == last_click_y)
{
- setmark('#', y);
+ cmd_exec();
+ setmark('#', y, 0);
screen_trashed();
}
}
@@ -622,9 +681,11 @@ static int mouse_button_right(int x, int y, lbool down, lbool drag)
* {{ unlike mouse_button_left, we could return an action,
* but keep it near mouse_button_left for readability. }}
*/
+ if (!(emouse & EMOUSE_RCLICK))
+ return (A_NOACTION);
if (!down && y < sc_height-1)
{
- gomark('#');
+ gomark('#', 0);
screen_trashed();
}
return (A_NOACTION);
@@ -686,6 +747,10 @@ static int x11mouse_action(lbool skip)
return mouse_wheel_down();
case X11MOUSE_WHEEL_UP:
return mouse_wheel_up();
+ case X11MOUSE_WHEEL_LEFT:
+ return mouse_wheel_left();
+ case X11MOUSE_WHEEL_RIGHT:
+ return mouse_wheel_right();
case X11MOUSE_BUTTON1:
case X11MOUSE_BUTTON2:
case X11MOUSE_BUTTON3:
@@ -720,6 +785,10 @@ static int x116mouse_action(lbool skip)
return mouse_wheel_down();
case X11MOUSE_WHEEL_UP:
return mouse_wheel_up();
+ case X11MOUSE_WHEEL_LEFT:
+ return mouse_wheel_left();
+ case X11MOUSE_WHEEL_RIGHT:
+ return mouse_wheel_right();
case X11MOUSE_BUTTON1:
case X11MOUSE_BUTTON2:
case X11MOUSE_BUTTON3: {
@@ -776,82 +845,95 @@ static constant unsigned char * cmd_next_entry(constant unsigned char *entry, mu
}
/*
+ * Does a command table contain a #stop directive?
+ */
+static lbool table_has_stop(struct tablelist *t)
+{
+ constant unsigned char *entry = t->t_start;
+
+ while (entry < t->t_end)
+ {
+ int action;
+ entry = cmd_next_entry(entry, &action, NULL, NULL);
+ if (action == A_END_LIST)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/*
+ * Find the first command table with a #stop directive, if any.
+ */
+static struct tablelist * find_stop_table(struct tablelist *t)
+{
+ for (; t != NULL; t = t->t_next)
+ {
+ if (table_has_stop(t))
+ return t;
+ }
+ return NULL;
+}
+
+/*
* Search a single command table for the command string in cmd.
*/
-static int cmd_search(constant char *cmd, constant unsigned char *table, constant unsigned char *endtable, constant unsigned char **extra, size_t *mlen)
+static int cmd_decode(struct tablelist *tlist, constant char *cmd, lbool anchored, constant char **extra)
{
int action = A_INVALID;
size_t match_len = 0;
+ constant unsigned char *table = NULL;
+ constant unsigned char *endtable;
+
if (extra != NULL)
*extra = NULL;
- while (table < endtable)
+ while (tlist != NULL)
{
int taction;
constant unsigned char *textra;
- size_t cmdlen;
- size_t match = cmd_match((constant char *) table, cmd);
- table = cmd_next_entry(table, &taction, &textra, &cmdlen);
+ size_t tcmdlen;
+ size_t tmatch;
+ if (table == NULL)
+ {
+ /* Beginning of table: set start/end pointers. */
+ table = tlist->t_start;
+ endtable = tlist->t_end;
+ }
+ if (anchored)
+ tmatch = (strcmp((constant char *) table, cmd) == 0) ? strlen(cmd) : 0;
+ else
+ tmatch = cmd_match((constant char *) table, cmd);
+ table = cmd_next_entry(table, &taction, &textra, &tcmdlen);
+ if (table >= endtable)
+ {
+ /* End of table; move to next table. */
+ tlist = tlist->t_next;
+ table = NULL;
+ }
if (taction == A_END_LIST)
- return (-action);
- if (match >= match_len)
+ break;
+ if (tmatch >= match_len)
{
- if (match == cmdlen) /* (last chars of) cmd matches this table entry */
+ if (tmatch == tcmdlen)
{
+ /* (Last chars of) cmd matches this table entry. */
action = taction;
if (extra != NULL)
- *extra = textra;
- } else if (match > 0 && action == A_INVALID) /* cmd is a prefix of this table entry */
+ *extra = (constant char *) textra;
+ } else if (tmatch > 0 && (tmatch > match_len || action == A_INVALID))
{
+ /* cmd is a prefix of this table entry */
action = A_PREFIX;
+ if (extra != NULL)
+ *extra = NULL;
}
- match_len = match;
- }
- }
- if (mlen != NULL)
- *mlen = match_len;
- return (action);
-}
-
-/*
- * Decode a command character and return the associated action.
- * The "extra" string, if any, is returned in sp.
- */
-static int cmd_decode(struct tablelist *tlist, constant char *cmd, constant char **sp)
-{
- struct tablelist *t;
- int action = A_INVALID;
- size_t match_len = 0;
-
- /*
- * Search for the cmd thru all the command tables.
- * If we find it more than once, take the last one.
- */
- *sp = NULL;
- for (t = tlist; t != NULL; t = t->t_next)
- {
- constant unsigned char *tsp;
- size_t mlen = match_len;
- int taction = cmd_search(cmd, t->t_start, t->t_end, &tsp, &mlen);
- if (mlen >= match_len)
- {
- match_len = mlen;
- if (taction != A_INVALID)
- {
- *sp = (constant char *) tsp;
- if (taction < 0)
- {
- action = -taction;
- break;
- }
- action = taction;
- }
+ match_len = tmatch;
}
}
if (action == A_X11MOUSE_IN)
action = x11mouse_action(FALSE);
else if (action == A_X116MOUSE_IN)
action = x116mouse_action(FALSE);
- return (action);
+ return action;
}
/*
@@ -859,7 +941,7 @@ static int cmd_decode(struct tablelist *tlist, constant char *cmd, constant char
*/
public int fcmd_decode(constant char *cmd, constant char **sp)
{
- return (cmd_decode(list_fcmd_tables, cmd, sp));
+ return (cmd_decode(list_fcmd_tables, cmd, FALSE, sp));
}
/*
@@ -867,7 +949,7 @@ public int fcmd_decode(constant char *cmd, constant char **sp)
*/
public int ecmd_decode(constant char *cmd, constant char **sp)
{
- return (cmd_decode(list_ecmd_tables, cmd, sp));
+ return (cmd_decode(list_ecmd_tables, cmd, FALSE, sp));
}
/*
@@ -894,6 +976,78 @@ public lbool parse_csl(lbool (*func)(constant char *word, size_t wlen, void *arg
}
/*
+ * Display error message for parse_csl_bitmap.
+ */
+static int csl_bitmap_error(constant char *pfx, constant char *type, size_t len, constant char *name)
+{
+ PARG parg;
+ size_t msglen = len + strlen(pfx) + strlen(type) + 32;
+ char *msg = ecalloc(msglen, sizeof(char));
+ SNPRINTF4(msg, msglen, "%s: %s name \"%.*s\"", pfx, type, (int) len, name);
+ parg.p_string = msg;
+ error("%s", &parg);
+ free(msg);
+ return 0;
+}
+
+/*
+ * Return the bit value of a csl_bitmap name.
+ */
+public int csl_bitmap_bit(constant char *name, size_t len, struct csl_bitmap_def *defs, int num_defs, constant char *pfx)
+{
+ int i;
+ int match = -1;
+
+ for (i = 0; i < num_defs; i++)
+ {
+ if (strncmp(defs[i].bit_name, name, len) == 0)
+ {
+ if (match >= 0) /* name is ambiguous */
+ return csl_bitmap_error(pfx, "ambiguous", len, name);
+ match = i;
+ }
+ }
+ if (match < 0)
+ return csl_bitmap_error(pfx, "invalid", len, name);
+ return defs[match].bit_value;
+}
+
+/* State for parse_csl_bitmap. */
+struct csl_bitmap_info
+{
+ int bitmap; /* bitmap being constructed */
+ struct csl_bitmap_def *def; /* array of name/value pairs */
+ int num_defs; /* length of def array */
+ constant char *pfx; /* prefix for error messages */
+};
+
+/*
+ * Set a bit in a csl_bitmap_info based on a csl_bitmap name.
+ */
+static lbool csl_bitmap_set(constant char *word, size_t wlen, void *arg)
+{
+ struct csl_bitmap_info *info = (struct csl_bitmap_info *) arg;
+ info->bitmap |= csl_bitmap_bit(word, wlen, info->def, info->num_defs, info->pfx);
+ return TRUE;
+}
+
+/*
+ * Parse a comma-separated list of csl_bitmap names and return
+ * the combined bitmap value.
+ */
+public int parse_csl_bitmap(constant char *str, struct csl_bitmap_def *defs, int num_defs, constant char *pfx)
+{
+ struct csl_bitmap_info info;
+ info.bitmap = 0;
+ info.def = defs;
+ info.num_defs = num_defs;
+ info.pfx = pfx;
+ if (!parse_csl(csl_bitmap_set, str, &info))
+ return -1;
+ return info.bitmap;
+}
+
+/*
* Should we ignore the setting of an environment variable?
*/
static lbool word_no_match(constant char *word, size_t wlen, void *arg)
@@ -920,13 +1074,13 @@ public constant char * lgetenv(constant char *var)
if (ignore_env(var))
return (NULL);
- a = cmd_decode(list_var_tables, var, &s);
+ a = cmd_decode(list_var_tables, var, TRUE, &s);
if (a == EV_OK)
return (s);
s = getenv(var);
if (s != NULL && *s != '\0')
return (s);
- a = cmd_decode(list_sysvar_tables, var, &s);
+ a = cmd_decode(list_sysvar_tables, var, TRUE, &s);
if (a == EV_OK)
return (s);
return (NULL);
diff --git a/contrib/less/edit.c b/contrib/less/edit.c
index 7171b82d15f7..9873ce257315 100644
--- a/contrib/less/edit.c
+++ b/contrib/less/edit.c
@@ -25,13 +25,13 @@ public int fd0 = 0;
extern lbool new_file;
extern char *every_first_cmd;
extern int force_open;
-extern int is_tty;
+extern lbool is_tty;
extern int sigs;
extern int hshift;
extern int want_filesize;
-extern int consecutive_nulls;
extern int modelines;
extern int show_preproc_error;
+extern lbool read_error;
extern IFILE curr_ifile;
extern IFILE old_ifile;
extern struct scrpos initial_scrpos;
@@ -264,6 +264,7 @@ static void close_pipe(FILE *pipefd)
int status;
char *p;
PARG parg;
+ int sig = 0;
if (pipefd == NULL)
return;
@@ -290,18 +291,33 @@ static void close_pipe(FILE *pipefd)
if (WIFEXITED(status))
{
int s = WEXITSTATUS(status);
- if (s != 0)
+ if (s == 0)
+ return;
+ if (s <= 128)
{
parg.p_int = s;
error("Input preprocessor failed (status %d)", &parg);
+ return;
}
- return;
+ /*
+ * popen invoked the shell, which likely last ran a
+ * program that terminated due to a signal.
+ * Assume the longstanding tradition (allowed but not
+ * required by POSIX) of adding 128 to the signal.
+ * Many shells use last command optimization, i.e.,
+ * they exec the last command instead of forking and
+ * waiting for it, and in that case the more-reliable
+ * WIFSIGNALED code below will be used.
+ */
+ sig = s - 128;
}
#endif
#if defined WIFSIGNALED && defined WTERMSIG
if (WIFSIGNALED(status))
+ sig = WTERMSIG(status);
+#endif
+ if (sig != 0)
{
- int sig = WTERMSIG(status);
if (
#ifdef SIGPIPE
sig != SIGPIPE ||
@@ -313,7 +329,6 @@ static void close_pipe(FILE *pipefd)
}
return;
}
-#endif
if (status != 0)
{
parg.p_int = status;
@@ -361,11 +376,14 @@ static void close_file(void)
* Save the current position so that we can return to
* the same position if we edit this file again.
*/
- get_scrpos(&scrpos, TOP);
- if (scrpos.pos != NULL_POSITION)
+ if (is_tty)
{
- store_pos(curr_ifile, &scrpos);
- lastmark();
+ get_scrpos(&scrpos, TOP);
+ if (scrpos.pos != NULL_POSITION)
+ {
+ store_pos(curr_ifile, &scrpos);
+ lastmark();
+ }
}
/*
* Close the file descriptor, unless it is a pipe.
@@ -545,7 +563,7 @@ public int edit_ifile(IFILE ifile)
} else
{
chflags |= CH_CANSEEK;
- if (bin_file(f, &nread) && !force_open && !opened(ifile))
+ if (bin_file(f, &nread) && is_tty && !force_open && !opened(ifile))
{
/*
* Looks like a binary file.
@@ -611,7 +629,6 @@ public int edit_ifile(IFILE ifile)
set_open(curr_ifile); /* File has been opened */
get_pos(curr_ifile, &initial_scrpos);
ch_init(f, chflags, nread);
- consecutive_nulls = 0;
check_modelines();
if (!(chflags & CH_HELPFILE))
@@ -660,6 +677,7 @@ public int edit_ifile(IFILE ifile)
#endif
undo_osc8();
hshift = 0;
+ read_error = FALSE;
if (strcmp(filename, FAKE_HELPFILE) && strcmp(filename, FAKE_EMPTYFILE))
{
char *qfilename = shell_quote(filename);
diff --git a/contrib/less/filename.c b/contrib/less/filename.c
index ad1b4b3cf4a9..0c7f32f54f09 100644
--- a/contrib/less/filename.c
+++ b/contrib/less/filename.c
@@ -319,15 +319,13 @@ static void xcpy_char(xcpy *xp, char ch)
static void xcpy_filename(xcpy *xp, constant char *str)
{
- /* If filename contains spaces, quote it
- * to prevent edit_list from splitting it. */
- lbool quote = (strchr(str, ' ') != NULL);
- if (quote)
- xcpy_char(xp, openquote);
- for (; *str != '\0'; str++)
- xcpy_char(xp, *str);
- if (quote)
- xcpy_char(xp, closequote);
+ char *qstr = shell_quote(str);
+ char *s;
+ if (qstr == NULL)
+ return;
+ for (s = qstr; *s != '\0'; s++)
+ xcpy_char(xp, *s);
+ free(qstr);
}
static size_t fexpand_copy(constant char *fr, char *to)
@@ -733,7 +731,11 @@ public char * lglob(constant char *afilename)
}
lessecho = lgetenv("LESSECHO");
if (isnullenv(lessecho))
+#ifdef LIBEXECDIR
+ lessecho = LIBEXECDIR "/lessecho";
+#else
lessecho = "lessecho";
+#endif
/*
* Invoke lessecho, and read its output (a globbed list of filenames).
*/
diff --git a/contrib/less/forwback.c b/contrib/less/forwback.c
index 3f21bbffd89f..76a7fb5849cc 100644
--- a/contrib/less/forwback.c
+++ b/contrib/less/forwback.c
@@ -19,8 +19,8 @@
extern int less_is_more;
public lbool squished;
-public int no_back_scroll = 0;
-public int forw_prompt;
+public lbool no_back_scroll = FALSE;
+public lbool forw_prompt;
public lbool first_time = TRUE; /* We're printing the first screen of output */
public int shell_lines = 1;
/* soft_eof is set as end-of-file when a read attempt returns EOF. This can
@@ -39,8 +39,9 @@ extern int back_scroll;
extern lbool ignore_eoi;
extern int header_lines;
extern int header_cols;
-extern int full_screen;
+extern lbool full_screen;
extern int stop_on_form_feed;
+extern int past_eof;
extern POSITION header_start_pos;
extern lbool getting_one_screen;
#if HILITE_SEARCH
@@ -140,7 +141,7 @@ static POSITION forw_line_pfx(POSITION pos, int pfx, int skipeol)
sc_width = pfx + line_pfx_width();
auto_wrap = 0;
hshift = 0;
- pos = forw_line_seg(pos, skipeol, FALSE, FALSE, NULL, NULL);
+ pos = forw_line_seg(pos, skipeol, FALSE, FALSE, FALSE, TRUE, NULL, NULL);
sc_width = save_sc_width;
auto_wrap = save_auto_wrap;
hshift = save_hshift;
@@ -228,6 +229,8 @@ public void forw(int n, POSITION pos, lbool force, lbool only_last, lbool to_new
if (pos != NULL_POSITION)
pos = after_header_pos(pos);
+ if (past_eof)
+ force = TRUE;
squish_check();
/*
@@ -243,7 +246,7 @@ public void forw(int n, POSITION pos, lbool force, lbool only_last, lbool to_new
(forw_scroll >= 0 && n > forw_scroll && n != sc_height-1);
if (!do_repaint)
{
- if (top_scroll && n >= sc_height - 1 && pos != ch_length())
+ if (top_scroll && n >= sc_height - 1 && pos != NULL_POSITION && pos != ch_length())
{
/*
* Start a new screen.
@@ -305,18 +308,25 @@ public void forw(int n, POSITION pos, lbool force, lbool only_last, lbool to_new
/*
* End of file: stop here unless the top line
* is still empty, or "force" is true.
- * Even if force is true, stop when the last
- * line in the file reaches the top of screen.
*/
soft_eof = opos;
- linepos = opos;
- if (ABORT_SIGS() ||
- (!force && position(TOP) != NULL_POSITION) ||
- (!empty_lines(0, 0) && !empty_lines(1, 1) && empty_lines(2, sc_height-1)))
+ linepos = (opos == ch_zero()) ? NULL_POSITION : opos;
+ if (ABORT_SIGS() || !force)
{
pos = opos;
break;
}
+ /*
+ * Even if force is true, stop when the last
+ * line in the file reaches the top of screen.
+ * Check for 3 non-empty lines at top of screen.
+ * The first will be shifted away by the add_forw_pos()
+ * after this loop; the other two are the start and end of
+ * the single line that we want to retain at top of screen.
+ */
+ if (!first_line && sc_height >= 3 && empty_lines(3, sc_height-1) &&
+ !empty_lines(0, 0) && !empty_lines(1, 1) && !empty_lines(2, 2))
+ break;
}
}
/*
@@ -358,7 +368,7 @@ public void forw(int n, POSITION pos, lbool force, lbool only_last, lbool to_new
put_line(TRUE);
if (do_stop_on_form_feed && !do_repaint && line_is_ff() && position(TOP) != NULL_POSITION)
break;
- forw_prompt = 1;
+ forw_prompt = TRUE;
}
if (!first_line)
add_forw_pos(pos, FALSE);
@@ -385,6 +395,8 @@ public void back(int n, POSITION pos, lbool force, lbool only_last, lbool to_new
lbool newline;
squish_check();
+ if (past_eof)
+ force = TRUE;
do_repaint = (n > get_back_scroll() || (only_last && n > sc_height-1) || header_lines > 0);
while (--n >= 0)
@@ -400,7 +412,18 @@ public void back(int n, POSITION pos, lbool force, lbool only_last, lbool to_new
/*
* Beginning of file: stop here unless "force" is true.
*/
- if (!force)
+ if (ABORT_SIGS() || !force)
+ break;
+ /*
+ * Even if force is true, stop when the first
+ * line in the file reaches the bottom of the screen.
+ * Check for 2 non-empty lines at bottom of screen.
+ * These are the start and end of the single line that
+ * we want to retain at bottom of screen.
+ */
+ if (sc_height >= 3 && empty_lines(0, sc_height-3) &&
+ !empty_lines(sc_height-1, sc_height-1) &&
+ !empty_lines(sc_height-2, sc_height-2))
break;
}
if (pos != after_header_pos(pos))
@@ -456,6 +479,8 @@ public void forward(int n, lbool force, lbool only_last, lbool to_newline)
return;
}
+ if (past_eof)
+ force = TRUE;
pos = position(BOTTOM_PLUS_ONE);
if (pos == NULL_POSITION && (!force || empty_lines(2, sc_height-1)))
{
@@ -493,6 +518,8 @@ public void backward(int n, lbool force, lbool only_last, lbool to_newline)
{
POSITION pos;
+ if (past_eof)
+ force = TRUE;
pos = position(TOP);
if (pos == NULL_POSITION && (!force || position(BOTTOM) == 0))
{
diff --git a/contrib/less/funcs.h b/contrib/less/funcs.h
index 903087785b0b..05e60e2f7b9f 100644
--- a/contrib/less/funcs.h
+++ b/contrib/less/funcs.h
@@ -12,7 +12,7 @@ public void scrsize(void);
public void screen_size_changed(void);
public constant char * special_key_str(int key);
public void init_win_colors(void);
-public void get_term();
+public void get_term(void);
public void init_mouse(void);
public void deinit_mouse(void);
public void suspend_screen(void);
@@ -43,8 +43,10 @@ public void at_switch(int attr);
public lbool is_at_equiv(int attr1, int attr2);
public int apply_at_specials(int attr);
public void putbs(void);
+public void win32_enqueue(int ch);
public void WIN32ungetch(int ch);
-public lbool win32_kbhit2(lbool no_queued);
+public void win32_clear_queue(void);
+public lbool win32_kbhit2(char *pch);
public lbool win32_kbhit(void);
public int WIN32getch(void);
public void win32_getch_clear(void);
@@ -76,8 +78,8 @@ public lbool binary_char(LWCHAR c);
public lbool control_char(LWCHAR c);
public constant char * prchar(LWCHAR c);
public constant char * prutfchar(LWCHAR ch);
-public int utf_len(char ch);
-public lbool is_utf8_well_formed(constant char *ss, int slen);
+public int utf_len(char ach);
+public lbool is_utf8_well_formed(constant char *ssa, int slen);
public void utf_skip_to_lead(constant char **pp, constant char *limit);
public LWCHAR get_wchar(constant char *sp);
public void put_wchar(mutable char **pp, LWCHAR ch);
@@ -120,8 +122,8 @@ public void ungetcc_back(char c);
public void ungetcc_end_command(void);
public void ungetsc(constant char *s);
public char peekcc(void);
-public void start_ignoring_input();
-public void stop_ignoring_input();
+public void start_ignoring_input(void);
+public void stop_ignoring_input(void);
public lbool is_ignoring_input(int action);
public void commands(void);
public size_t cvt_length(size_t len, int ops);
@@ -136,6 +138,8 @@ public void add_sysvar_table(unsigned char *buf, size_t len);
public int fcmd_decode(constant char *cmd, constant char **sp);
public int ecmd_decode(constant char *cmd, constant char **sp);
public lbool parse_csl(lbool (*func)(constant char *word, size_t wlen, void *arg), constant char *str, void *arg);
+public int csl_bitmap_bit(constant char *name, size_t len, struct csl_bitmap_def *defs, int num_defs, constant char *pfx);
+public int parse_csl_bitmap(constant char *str, struct csl_bitmap_def *defs, int num_defs, constant char *pfx);
public constant char * lgetenv(constant char *var);
public constant char * lgetenv_ext(constant char *var, unsigned char *env_buf, size_t env_buf_len);
public lbool isnullenv(constant char *s);
@@ -218,7 +222,7 @@ public void *get_altpipe(IFILE ifile);
public void set_altfilename(IFILE ifile, char *altfilename);
public char * get_altfilename(IFILE ifile);
public void if_dump(void);
-public POSITION forw_line_seg(POSITION curr_pos, lbool skipeol, lbool rscroll, lbool nochop, POSITION *p_linepos, lbool *p_newline);
+public POSITION forw_line_seg(POSITION curr_pos, lbool skipeol, lbool rscroll, lbool nochop, lbool full_pad, lbool rforw, POSITION *p_linepos, lbool *p_newline);
public POSITION forw_line(POSITION curr_pos, POSITION *p_linepos, lbool *p_newline);
public POSITION back_line(POSITION curr_pos, lbool *p_newline);
public void set_attnpos(POSITION pos);
@@ -237,7 +241,7 @@ public lbool is_line_contig_pos(POSITION pos);
public void set_line_contig_pos(POSITION pos);
public void prewind(lbool contig);
public void plinestart(POSITION pos);
-public int line_pfx_width(void);
+public unsigned line_pfx_width(void);
public void pshift_all(void);
public int pwidth(LWCHAR ch, int a, LWCHAR prev_ch, int prev_a);
public void savec(void);
@@ -253,7 +257,7 @@ public int pappend_b(char c, POSITION pos, lbool before_pendc);
public int pappend(char c, POSITION pos);
public lbool line_is_ff(void);
public int pflushmbc(void);
-public void pdone(lbool endline, lbool chopped, lbool forw);
+public void pdone(lbool endline, lbool chopped, lbool forw, lbool full_pad);
public int col_from_pos(POSITION linepos, POSITION spos, POSITION saved_pos, int saved_col);
public POSITION pos_from_col(POSITION linepos, int col, POSITION saved_pos, int saved_col);
public void set_attr_line(int a);
@@ -265,7 +269,7 @@ public POSITION forw_raw_line_len(POSITION curr_pos, size_t read_len, constant c
public POSITION forw_raw_line(POSITION curr_pos, constant char **linep, size_t *line_lenp);
public POSITION back_raw_line(POSITION curr_pos, constant char **linep, size_t *line_lenp);
public int skip_columns(int cols, constant char **linep, size_t *line_lenp);
-public void load_line(constant char *str);
+public void load_line(constant char *str, int attr, int reserve);
public int longest_line_width(void);
public int rrshift(void);
public int set_color_map(int attr, constant char *colorstr);
@@ -282,10 +286,10 @@ public int pipe_mark(char c, constant char *cmd);
public int pipe_data(constant char *cmd, POSITION spos, POSITION epos);
public void init_mark(void);
public lbool badmark(char c);
-public void setmark(char c, int where);
+public void setmark(char c, int where, LINENUM linenum);
public void clrmark(char c);
public void lastmark(void);
-public void gomark(char c);
+public void gomark(char c, int sline);
public POSITION markpos(char c);
public char posmark(POSITION pos);
public void unmark(IFILE ifile);
@@ -306,6 +310,7 @@ public void opt_t(int type, constant char *s);
public void opt__T(int type, constant char *s);
public void opt_p(int type, constant char *s);
public void opt__P(int type, constant char *s);
+public void opt_end_prompt(int type, constant char *s);
public void opt_autosave(int type, constant char *s);
public void opt_b(int type, constant char *s);
public void opt_i(int type, constant char *s);
@@ -318,7 +323,8 @@ public void opt_rscroll(int type, constant char *s);
public void opt_query(int type, constant char *s);
public void opt_match_shift(int type, constant char *s);
public void calc_match_shift(void);
-public void opt_mousecap(int type, constant char *s);
+public void opt_emouse(int type, constant char *s);
+public void opt_mouse(int type, constant char *s);
public void opt_wheel_lines(int type, constant char *s);
public void opt_linenum_width(int type, constant char *s);
public void opt_status_col_width(int type, constant char *s);
@@ -326,6 +332,7 @@ public void opt_filesize(int type, constant char *s);
public void opt_first_cmd_at_prompt(int type, constant char *s);
public void opt_intr(int type, constant char *s);
public void opt_header(int type, constant char *s);
+public void opt_hilite_target(int type, constant char *s);
public void opt_search_type(int type, constant char *s);
public void opt_nosearch_headers(int type, constant char *s);
public void opt_nosearch_header_lines(int type, constant char *s);
@@ -367,6 +374,7 @@ public int percentage(POSITION num, POSITION den);
public POSITION percent_pos(POSITION pos, int percent, long fraction);
public int os9_signal(int type, RETSIGTYPE (*handler)());
public void sleep_ms(int ms);
+public void put_line_hilite(lbool forw_scroll, lbool target);
public void put_line(lbool forw_scroll);
public void flush(void);
public void set_output(int fd, lbool no_term_init);
@@ -393,17 +401,22 @@ public void pos_init(void);
public int onscreen(POSITION pos);
public lbool empty_screen(void);
public lbool empty_lines(int s, int e);
+public void get_scrpos_pos(struct scrpos *scrpos, int where, POSITION pos);
public void get_scrpos(struct scrpos *scrpos, int where);
public int sindex_from_sline(int sline);
-public void pos_rehead(void);
+public POSITION beginning_of_line(POSITION tpos);
+public void pos_rehead(lbool adj_hshift);
public void init_prompt(void);
public constant char * pr_expand(constant char *proto);
public constant char * eq_message(void);
public constant char * pr_string(void);
+public constant char * end_pr_string(void);
public constant char * wait_message(void);
public void init_search(void);
public int get_cvt_ops(int search_type);
+public constant char* prev_pattern_text(void);
public void repaint_hilite(lbool on);
+public void draw_target_attn(lbool hilite);
public void clear_attn(void);
public void undo_search(lbool clear);
public lbool undo_osc8(void);
diff --git a/contrib/less/help.c b/contrib/less/help.c
index 52f7a9dc291a..d09bc0d5e5ae 100644
--- a/contrib/less/help.c
+++ b/contrib/less/help.c
@@ -1,4 +1,4 @@
-/* This file was generated by mkhelp.pl from less.hlp at 17:46 on 2026/1/29 */
+/* This file was generated by mkhelp.pl from less.hlp at 23:54 on 2026/5/30 */
#include "less.h"
constant char helpdata[] = {
'\n',
@@ -55,7 +55,7 @@ constant char helpdata[] = {
' ',' ','E','S','C','-','U',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','C','l','e','a','r',' ','s','e','a','r','c','h',' ','h','i','g','h','l','i','g','h','t','i','n','g','.','\n',
' ',' ','&','_','\b','p','_','\b','a','_','\b','t','_','\b','t','_','\b','e','_','\b','r','_','\b','n',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','*',' ',' ','D','i','s','p','l','a','y',' ','o','n','l','y',' ','m','a','t','c','h','i','n','g',' ','l','i','n','e','s','.','\n',
' ',' ',' ',' ',' ',' ',' ',' ','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\n',
-'\t','\t','S','e','a','r','c','h',' ','i','s',' ','c','a','s','e','-','s','e','n','s','i','t','i','v','e',' ','u','n','l','e','s','s',' ','c','h','a','n','g','e','d',' ','w','i','t','h',' ','-','i',' ','o','r',' ','-','I','.','\n',
+' ',' ',' ',' ',' ',' ',' ',' ','S','e','a','r','c','h',' ','i','s',' ','c','a','s','e','-','s','e','n','s','i','t','i','v','e',' ','u','n','l','e','s','s',' ','c','h','a','n','g','e','d',' ','w','i','t','h',' ','-','i',' ','o','r',' ','-','I','.','\n',
' ',' ',' ',' ',' ',' ',' ',' ','A',' ','s','e','a','r','c','h',' ','p','a','t','t','e','r','n',' ','m','a','y',' ','b','e','g','i','n',' ','w','i','t','h',' ','o','n','e',' ','o','r',' ','m','o','r','e',' ','o','f',':','\n',
' ',' ',' ',' ',' ',' ',' ',' ','^','N',' ','o','r',' ','!',' ',' ','S','e','a','r','c','h',' ','f','o','r',' ','N','O','N','-','m','a','t','c','h','i','n','g',' ','l','i','n','e','s','.','\n',
' ',' ',' ',' ',' ',' ',' ',' ','^','E',' ','o','r',' ','*',' ',' ','S','e','a','r','c','h',' ','m','u','l','t','i','p','l','e',' ','f','i','l','e','s',' ','(','p','a','s','s',' ','t','h','r','u',' ','E','N','D',' ','O','F',' ','F','I','L','E',')','.','\n',
@@ -84,11 +84,11 @@ constant char helpdata[] = {
' ',' ',' ',' ',' ',' ',' ',' ','E','a','c','h',' ','"','f','i','n','d',' ','o','p','e','n',' ','b','r','a','c','k','e','t','"',' ','c','o','m','m','a','n','d',' ','g','o','e','s',' ','b','a','c','k','w','a','r','d',' ','t','o',' ','t','h','e',' ','o','p','e','n',' ','b','r','a','c','k','e','t',' ','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','m','a','t','c','h','i','n','g',' ','t','h','e',' ','(','_','\b','N','-','t','h',')',' ','c','l','o','s','e',' ','b','r','a','c','k','e','t',' ','i','n',' ','t','h','e',' ','b','o','t','t','o','m',' ','l','i','n','e','.','\n',
'\n',
-' ',' ','m','_','\b','<','_','\b','l','_','\b','e','_','\b','t','_','\b','t','_','\b','e','_','\b','r','_','\b','>',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','M','a','r','k',' ','t','h','e',' ','c','u','r','r','e','n','t',' ','t','o','p',' ','l','i','n','e',' ','w','i','t','h',' ','<','l','e','t','t','e','r','>','.','\n',
-' ',' ','M','_','\b','<','_','\b','l','_','\b','e','_','\b','t','_','\b','t','_','\b','e','_','\b','r','_','\b','>',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','M','a','r','k',' ','t','h','e',' ','c','u','r','r','e','n','t',' ','b','o','t','t','o','m',' ','l','i','n','e',' ','w','i','t','h',' ','<','l','e','t','t','e','r','>','.','\n',
-' ',' ','\'','_','\b','<','_','\b','l','_','\b','e','_','\b','t','_','\b','t','_','\b','e','_','\b','r','_','\b','>',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','G','o',' ','t','o',' ','a',' ','p','r','e','v','i','o','u','s','l','y',' ','m','a','r','k','e','d',' ','p','o','s','i','t','i','o','n','.','\n',
-' ',' ','\'','\'',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','G','o',' ','t','o',' ','t','h','e',' ','p','r','e','v','i','o','u','s',' ','p','o','s','i','t','i','o','n','.','\n',
-' ',' ','^','X','^','X',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','S','a','m','e',' ','a','s',' ','\'','.','\n',
+' ',' ','m','_','\b','<','_','\b','l','_','\b','e','_','\b','t','_','\b','t','_','\b','e','_','\b','r','_','\b','>',' ',' ',' ',' ',' ',' ',' ',' ',' ','*',' ',' ','M','a','r','k',' ','t','h','e',' ','t','o','p',' ','l','i','n','e',' ','(','o','r',' ','l','i','n','e',' ','_','\b','N',')',' ','w','i','t','h',' ','<','l','e','t','t','e','r','>','.','\n',
+' ',' ','M','_','\b','<','_','\b','l','_','\b','e','_','\b','t','_','\b','t','_','\b','e','_','\b','r','_','\b','>',' ',' ',' ',' ',' ',' ',' ',' ',' ','*',' ',' ','M','a','r','k',' ','t','h','e',' ','b','o','t','t','o','m',' ','l','i','n','e',' ','(','o','r',' ','l','i','n','e',' ','_','\b','N',')',' ','w','i','t','h',' ','<','l','e','t','t','e','r','>','.','\n',
+' ',' ','\'','_','\b','<','_','\b','l','_','\b','e','_','\b','t','_','\b','t','_','\b','e','_','\b','r','_','\b','>',' ',' ',' ',' ',' ',' ',' ',' ',' ','*',' ',' ','G','o',' ','t','o',' ','a',' ','p','r','e','v','i','o','u','s','l','y',' ','m','a','r','k','e','d',' ','p','o','s','i','t','i','o','n',' ','(','p','u','t',' ','o','n',' ','p','o','s','i','t','i','o','n',' ','_','\b','N',')','.','\n',
+' ',' ','\'','\'',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','*',' ',' ','G','o',' ','t','o',' ','t','h','e',' ','p','r','e','v','i','o','u','s',' ','p','o','s','i','t','i','o','n','.','\n',
+' ',' ','^','X','^','X',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','*',' ',' ','S','a','m','e',' ','a','s',' ','\'','.','\n',
' ',' ','E','S','C','-','m','_','\b','<','_','\b','l','_','\b','e','_','\b','t','_','\b','t','_','\b','e','_','\b','r','_','\b','>',' ',' ',' ',' ',' ',' ',' ',' ','C','l','e','a','r',' ','a',' ','m','a','r','k','.','\n',
' ',' ',' ',' ',' ',' ',' ',' ','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\n',
' ',' ',' ',' ',' ',' ',' ',' ','A',' ','m','a','r','k',' ','i','s',' ','a','n','y',' ','u','p','p','e','r','-','c','a','s','e',' ','o','r',' ','l','o','w','e','r','-','c','a','s','e',' ','l','e','t','t','e','r','.','\n',
@@ -205,9 +205,9 @@ constant char helpdata[] = {
' ',' ','-','V',' ',' ','.','.','.','.','.','.','.','.',' ',' ','-','-','v','e','r','s','i','o','n','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','D','i','s','p','l','a','y',' ','t','h','e',' ','v','e','r','s','i','o','n',' ','n','u','m','b','e','r',' ','o','f',' ','"','l','e','s','s','"','.','\n',
' ',' ','-','w',' ',' ','.','.','.','.','.','.','.','.',' ',' ','-','-','h','i','l','i','t','e','-','u','n','r','e','a','d','\n',
-' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','H','i','g','h','l','i','g','h','t',' ','f','i','r','s','t',' ','n','e','w',' ','l','i','n','e',' ','a','f','t','e','r',' ','f','o','r','w','a','r','d','-','s','c','r','e','e','n','.','\n',
+' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','H','i','g','h','l','i','g','h','t',' ','f','i','r','s','t',' ','n','e','w',' ','l','i','n','e',' ','a','f','t','e','r',' ','f','u','l','l',' ','s','c','r','e','e','n',' ','m','o','v','e','m','e','n','t','.','\n',
' ',' ','-','W',' ',' ','.','.','.','.','.','.','.','.',' ',' ','-','-','H','I','L','I','T','E','-','U','N','R','E','A','D','\n',
-' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','H','i','g','h','l','i','g','h','t',' ','f','i','r','s','t',' ','n','e','w',' ','l','i','n','e',' ','a','f','t','e','r',' ','a','n','y',' ','f','o','r','w','a','r','d',' ','m','o','v','e','m','e','n','t','.','\n',
+' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','H','i','g','h','l','i','g','h','t',' ','f','i','r','s','t',' ','n','e','w',' ','l','i','n','e',' ','a','f','t','e','r',' ','a','n','y',' ','m','o','v','e','m','e','n','t','.','\n',
' ',' ','-','x',' ','[','_','\b','N','[',',','.','.','.',']',']',' ',' ','-','-','t','a','b','s','=','[','_','\b','N','[',',','.','.','.',']',']','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','S','e','t',' ','t','a','b',' ','s','t','o','p','s','.','\n',
' ',' ','-','X',' ',' ','.','.','.','.','.','.','.','.',' ',' ','-','-','n','o','-','i','n','i','t','\n',
@@ -225,6 +225,10 @@ constant char helpdata[] = {
'\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','-','-','a','u','t','o','s','a','v','e','=','[','_','\b','m','_','\b','/','_','\b','!','_','\b','*',']','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','A','c','t','i','o','n','s',' ','w','h','i','c','h',' ','c','a','u','s','e',' ','t','h','e',' ','h','i','s','t','o','r','y',' ','f','i','l','e',' ','t','o',' ','b','e',' ','s','a','v','e','d','.','\n',
+' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','-','-','e','m','o','u','s','e','=','[','_','\b','v','_','\b','s','_','\b','c','_','\b','r','_','\b','o','_','\b','l','_','\b','l',',','_','\b','v','_','\b','d','_','\b','r','_','\b','a','_','\b','g',',','_','\b','h','_','\b','s','_','\b','c','_','\b','r','_','\b','o','_','\b','l','_','\b','l',',','_','\b','h','_','\b','d','_','\b','r','_','\b','a','_','\b','g',',','_','\b','l','_','\b','c','_','\b','l','_','\b','i','_','\b','c','_','\b','k',',','_','\b','r','_','\b','c','_','\b','l','_','\b','i','_','\b','c','_','\b','k',']','\n',
+' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','E','n','a','b','l','e',' ','m','o','u','s','e',' ','f','e','a','t','u','r','e','s','.','\n',
+' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','-','-','e','n','d','-','p','r','o','m','p','t','=','[','_','\b','s','_','\b','t','_','\b','r','_','\b','i','_','\b','n','_','\b','g',']','\n',
+' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','S','t','r','i','n','g',' ','t','o',' ','b','e',' ','p','r','i','n','t','e','d',' ','a','f','t','e','r',' ','e','r','a','s','i','n','g',' ','t','h','e',' ','p','r','o','m','p','t','.','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','-','-','e','x','i','t','-','f','o','l','l','o','w','-','o','n','-','c','l','o','s','e','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','E','x','i','t',' ','F',' ','c','o','m','m','a','n','d',' ','o','n',' ','a',' ','p','i','p','e',' ','w','h','e','n',' ','w','r','i','t','e','r',' ','c','l','o','s','e','s',' ','p','i','p','e','.','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','-','-','f','i','l','e','-','s','i','z','e','\n',
@@ -235,6 +239,8 @@ constant char helpdata[] = {
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','S','t','o','p',' ','s','c','r','o','l','l','i','n','g',' ','w','h','e','n',' ','a',' ','f','o','r','m',' ','f','e','e','d',' ','c','h','a','r','a','c','t','e','r',' ','i','s',' ','r','e','a','c','h','e','d','.','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','-','-','h','e','a','d','e','r','=','[','_','\b','L','[',',','_','\b','C','[',',','_','\b','N',']',']',']','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','U','s','e',' ','_','\b','L',' ','l','i','n','e','s',' ','(','s','t','a','r','t','i','n','g',' ','a','t',' ','l','i','n','e',' ','_','\b','N',')',' ','a','n','d',' ','_','\b','C',' ','c','o','l','u','m','n','s',' ','a','s',' ','h','e','a','d','e','r','s','.','\n',
+' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','-','-','h','i','l','i','t','e','-','t','a','r','g','e','t','\n',
+' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','H','i','g','h','l','i','g','h','t',' ','t','h','e',' ','t','a','r','g','e','t',' ','l','i','n','e','.','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','-','-','i','n','c','s','e','a','r','c','h','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','S','e','a','r','c','h',' ','f','i','l','e',' ','a','s',' ','e','a','c','h',' ','p','a','t','t','e','r','n',' ','c','h','a','r','a','c','t','e','r',' ','i','s',' ','t','y','p','e','d',' ','i','n','.','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','-','-','i','n','t','r','=','[','_','\b','C',']','\n',
@@ -250,7 +256,7 @@ constant char helpdata[] = {
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','-','-','m','o','d','e','l','i','n','e','s','=','[','_','\b','N',']','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','R','e','a','d',' ','_','\b','N',' ','l','i','n','e','s',' ','f','r','o','m',' ','t','h','e',' ','i','n','p','u','t',' ','f','i','l','e',' ','a','n','d',' ','l','o','o','k',' ','f','o','r',' ','v','i','m',' ','m','o','d','e','l','i','n','e','s','.','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','-','-','m','o','u','s','e','\n',
-' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','E','n','a','b','l','e',' ','m','o','u','s','e',' ','i','n','p','u','t','.','\n',
+' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','E','n','a','b','l','e',' ','m','o','u','s','e',' ','c','l','i','c','k','i','n','g',' ','a','n','d',' ','v','e','r','t','i','c','a','l',' ','s','c','r','o','l','l','i','n','g','.','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','-','-','n','o','-','e','d','i','t','-','w','a','r','n','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','D','o','n','\'','t',' ','w','a','r','n',' ','w','h','e','n',' ','u','s','i','n','g',' ','v',' ','c','o','m','m','a','n','d',' ','o','n',' ','a',' ','f','i','l','e',' ','o','p','e','n','e','d',' ','v','i','a',' ','L','E','S','S','O','P','E','N','.','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','-','-','n','o','-','k','e','y','p','a','d','\n',
@@ -279,6 +285,8 @@ constant char helpdata[] = {
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','S','e','t',' ','d','e','f','a','u','l','t',' ','o','p','t','i','o','n','s',' ','f','o','r',' ','e','v','e','r','y',' ','s','e','a','r','c','h','.','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','-','-','s','h','o','w','-','p','r','e','p','r','o','c','-','e','r','r','o','r','s','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','D','i','s','p','l','a','y',' ','a',' ','m','e','s','s','a','g','e',' ','i','f',' ','p','r','e','p','r','o','c','e','s','s','o','r',' ','e','x','i','t','s',' ','w','i','t','h',' ','a','n',' ','e','r','r','o','r',' ','s','t','a','t','u','s','.','\n',
+' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','-','-','p','a','s','t','-','e','o','f','\n',
+' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','S','c','r','o','l','l','i','n','g',' ','c','o','m','m','a','n','d','s',' ','c','o','n','t','i','n','u','e',' ','p','a','s','t',' ','e','n','d',' ','o','f',' ','f','i','l','e','.','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','-','-','p','r','o','c','-','b','a','c','k','s','p','a','c','e','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','P','r','o','c','e','s','s',' ','b','a','c','k','s','p','a','c','e','s',' ','f','o','r',' ','b','o','l','d','/','u','n','d','e','r','l','i','n','e','.','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','-','-','P','R','O','C','-','B','A','C','K','S','P','A','C','E','\n',
diff --git a/contrib/less/input.c b/contrib/less/input.c
index a3daa0d83827..5b6ed6ece6e4 100644
--- a/contrib/less/input.c
+++ b/contrib/less/input.c
@@ -80,7 +80,7 @@ static void init_status_col(POSITION base_pos, POSITION disp_pos, POSITION edisp
* a line. The new position is the position of the first character
* of the NEXT line. The line obtained is the line starting at curr_pos.
*/
-public POSITION forw_line_seg(POSITION curr_pos, lbool skipeol, lbool rscroll, lbool nochop, POSITION *p_linepos, lbool *p_newline)
+public POSITION forw_line_seg(POSITION curr_pos, lbool skipeol, lbool rscroll, lbool nochop, lbool full_pad, lbool rforw, POSITION *p_linepos, lbool *p_newline)
{
POSITION base_pos;
POSITION new_pos;
@@ -245,7 +245,7 @@ get_forw_line:
} while (c != '\n' && c != EOI);
new_pos = ch_tell();
endline = TRUE;
- quit_if_one_screen = FALSE;
+ quit_if_one_screen = 0;
chopped = TRUE;
} else
{
@@ -306,7 +306,7 @@ get_forw_line:
pappend_b(' ', ch_tell()-1, TRUE);
}
#endif
- pdone(endline, rscroll && chopped, TRUE);
+ pdone(endline, rscroll && chopped, rforw, full_pad);
#if HILITE_SEARCH
if (is_filtered(base_pos))
@@ -345,7 +345,7 @@ get_forw_line:
public POSITION forw_line(POSITION curr_pos, POSITION *p_linepos, lbool *p_newline)
{
- return forw_line_seg(curr_pos, (chop_line() || hshift > 0), TRUE, FALSE, p_linepos, p_newline);
+ return forw_line_seg(curr_pos, (chop_line() || hshift > 0), TRUE, FALSE, FALSE, TRUE, p_linepos, p_newline);
}
/*
@@ -501,7 +501,7 @@ get_back_line:
{
endline = TRUE;
chopped = TRUE;
- quit_if_one_screen = FALSE;
+ quit_if_one_screen = 0;
edisp_pos = new_pos;
break;
}
@@ -568,7 +568,7 @@ get_back_line:
}
}
- pdone(endline, chopped, FALSE);
+ pdone(endline, chopped, FALSE, FALSE);
#if HILITE_SEARCH
if (is_filtered(base_pos))
diff --git a/contrib/less/jump.c b/contrib/less/jump.c
index 095f652ef35d..b5e09d1000cf 100644
--- a/contrib/less/jump.c
+++ b/contrib/less/jump.c
@@ -20,6 +20,9 @@ extern lbool squished;
extern int sc_width, sc_height;
extern int show_attn;
extern int top_scroll;
+extern int quit_if_one_screen;
+extern lbool one_screen;
+extern lbool full_screen;
extern POSITION header_start_pos;
/*
@@ -188,15 +191,15 @@ static void after_header_message(void)
{
#if HAVE_TIME
#define MSG_FREQ 1 /* seconds */
- static time_type last_msg = (time_type) 0;
- time_type now = get_time();
- if (now < last_msg + MSG_FREQ)
- return;
- last_msg = now;
+ static time_type last_msg = (time_type) 0;
+ time_type now = get_time();
+ if (now < last_msg + MSG_FREQ)
+ return;
+ last_msg = now;
#endif
- lbell();
- /* {{ This message displays before the file text is updated, which is not a good UX. }} */
- /** error("Cannot display text before header; use --header=- to disable header", NULL_PARG); */
+ lbell();
+ /* {{ This message displays before the file text is updated, which is not a good UX. }} */
+ /** error("Cannot display text before header; use --header=- to disable header", NULL_PARG); */
}
/*
@@ -210,8 +213,8 @@ public POSITION after_header_pos(POSITION pos)
{
if (header_start_pos != NULL_POSITION && pos < header_start_pos)
{
- after_header_message();
- pos = header_start_pos;
+ after_header_message();
+ pos = header_start_pos;
}
return pos;
}
@@ -235,6 +238,14 @@ public void jump_loc(POSITION pos, int sline)
pos = next_unfiltered(pos);
sindex = sindex_from_sline(sline);
+ if (!full_screen && !(quit_if_one_screen && one_screen))
+ {
+ /* If not full screen, can't rely on scrolling logic below, since
+ * "scrolling" may just print lines in the unused part of the screen. */
+ pos_clear();
+ lclear();
+ }
+
if ((nline = onscreen(pos)) >= 0)
{
/*
diff --git a/contrib/less/less-osc8-open.sh b/contrib/less/less-osc8-open.sh
new file mode 100755
index 000000000000..fbcde90a9824
--- /dev/null
+++ b/contrib/less/less-osc8-open.sh
@@ -0,0 +1,64 @@
+#!/bin/sh
+
+# Less OSC 8 handler.
+
+echo() { printf %s\\n "$*"; }
+
+# Open a link of the form "file://HOST/PATH" or "file:///PATH"
+open_file() {
+ case $1 in
+ file:///* | file://localhost/*)
+ less -- "/${1#file://*/}"
+ ;;
+
+ file://*/*)
+ linkhost=${1#file://}; linkhost=${linkhost%%/*}
+ localhost=${HOSTNAME:-$(bash -c 'printf %s "$HOSTNAME"' || uname -n)} 2>/dev/null
+
+ case $localhost in
+ */* | '')
+ echo "unable to detect local hostname: $localhost"
+ exit 1
+ ;;
+ "$linkhost")
+ less -- "/${1#file://*/}"
+ ;;
+ *)
+ echo "cannot open remote file: $1"
+ exit 1
+ ;;
+ esac
+ ;;
+
+ *)
+ echo "invalid file link: $1"
+ exit 1
+ ;;
+ esac
+}
+
+# Open a link of the form "man:NAME" or "man:NAME(SECTION)".
+open_man() {
+ case $1 in
+ man:?*\(*\) )
+ sect=${1#*\(}; sect=${sect%?}
+ name=${1#man:}; name=${name%%\(*}
+ man -- ${sect:+"$sect"} "$name"
+ ;;
+
+ man:?*)
+ man -- "${1#man:}"
+ ;;
+
+ *)
+ echo "invalid man link: $1"
+ exit 1
+ ;;
+ esac
+}
+
+case $1 in
+file:*) open_file "$1" ;;
+man:*) open_man "$1" ;;
+*) echo "unsupported link type: $1"; exit 1 ;;
+esac
diff --git a/contrib/less/less.h b/contrib/less/less.h
index 264663e59e51..1624eb2f1d09 100644
--- a/contrib/less/less.h
+++ b/contrib/less/less.h
@@ -338,6 +338,12 @@ struct wchar_range_table
unsigned int count;
};
+struct csl_bitmap_def
+{
+ constant char *bit_name;
+ int bit_value;
+};
+
#if HAVE_POLL
typedef short POLL_EVENTS;
#endif
@@ -440,8 +446,7 @@ typedef enum osc8_state {
#define AT_PLACEHOLDER (1 << 7) /* Placeholder for half of double-wide char */
#define AT_COLOR_SHIFT 8
-#define AT_NUM_COLORS 16
-#define AT_COLOR ((AT_NUM_COLORS-1) << AT_COLOR_SHIFT)
+#define AT_COLOR ((~(unsigned)0) << AT_COLOR_SHIFT)
#define AT_COLOR_ATTN (1 << AT_COLOR_SHIFT)
#define AT_COLOR_BIN (2 << AT_COLOR_SHIFT)
#define AT_COLOR_CTRL (3 << AT_COLOR_SHIFT)
@@ -452,8 +457,12 @@ typedef enum osc8_state {
#define AT_COLOR_RSCROLL (8 << AT_COLOR_SHIFT)
#define AT_COLOR_HEADER (9 << AT_COLOR_SHIFT)
#define AT_COLOR_SEARCH (10 << AT_COLOR_SHIFT)
-#define AT_COLOR_SUBSEARCH(i) ((10+(i)) << AT_COLOR_SHIFT)
-#define NUM_SEARCH_COLORS (AT_NUM_COLORS-10-1)
+#define AT_COLOR_TILDE (11 << AT_COLOR_SHIFT)
+#define AT_COLOR_TARGET (12 << AT_COLOR_SHIFT)
+#define AT_COLOR_SS_OFFSET 13 /* largest AT_COLOR_* value + 1 */
+#define NUM_SEARCH_COLORS 5
+#define AT_NUM_COLORS (AT_COLOR_SS_OFFSET + NUM_SEARCH_COLORS)
+#define AT_COLOR_SUBSEARCH(i) ((AT_COLOR_SS_OFFSET+(i)-1) << AT_COLOR_SHIFT)
typedef enum { CT_NULL, CT_4BIT, CT_6BIT } COLOR_TYPE;
@@ -629,14 +638,25 @@ typedef enum {
#endif
/* X11 mouse reporting definitions */
-#define X11MOUSE_BUTTON1 0 /* Left button press */
-#define X11MOUSE_BUTTON2 1 /* Middle button press */
-#define X11MOUSE_BUTTON3 2 /* Right button press */
-#define X11MOUSE_BUTTON_REL 3 /* Button release */
-#define X11MOUSE_DRAG 0x20 /* Drag with button down */
-#define X11MOUSE_WHEEL_UP 0x40 /* Wheel scroll up */
-#define X11MOUSE_WHEEL_DOWN 0x41 /* Wheel scroll down */
-#define X11MOUSE_OFFSET 0x20 /* Added to button & pos bytes to create a char */
+#define X11MOUSE_BUTTON1 0 /* Left button press */
+#define X11MOUSE_BUTTON2 1 /* Middle button press */
+#define X11MOUSE_BUTTON3 2 /* Right button press */
+#define X11MOUSE_BUTTON_REL 3 /* Button release */
+#define X11MOUSE_DRAG 0x20 /* Drag with button down */
+#define X11MOUSE_WHEEL_UP 0x40 /* Wheel scroll up */
+#define X11MOUSE_WHEEL_DOWN 0x41 /* Wheel scroll down */
+#define X11MOUSE_WHEEL_LEFT 0x42 /* Wheel scroll left */
+#define X11MOUSE_WHEEL_RIGHT 0x43 /* Wheel scroll right */
+#define X11MOUSE_OFFSET 0x20 /* Added to button & pos bytes to create a char */
+
+/* Mouse features */
+#define EMOUSE_HSCROLL (1<<0) /* Horizontal scroll */
+#define EMOUSE_VSCROLL (1<<1) /* Vertical scroll */
+#define EMOUSE_HDRAG (1<<2) /* Horizontal drag */
+#define EMOUSE_VDRAG (1<<3) /* Vertical drag */
+#define EMOUSE_LCLICK (1<<4) /* Left click */
+#define EMOUSE_RCLICK (1<<5) /* Right click */
+#define EMOUSE_COUNT 6
/* Security features. */
#define SF_EDIT (1<<1) /* Edit file (v) */
diff --git a/contrib/less/less.hlp b/contrib/less/less.hlp
index 3bac49d80ff0..7ac7bed493b5 100644
--- a/contrib/less/less.hlp
+++ b/contrib/less/less.hlp
@@ -52,7 +52,7 @@
ESC-U Clear search highlighting.
&_p_a_t_t_e_r_n * Display only matching lines.
---------------------------------------------------
- Search is case-sensitive unless changed with -i or -I.
+ Search is case-sensitive unless changed with -i or -I.
A search pattern may begin with one or more of:
^N or ! Search for NON-matching lines.
^E or * Search multiple files (pass thru END OF FILE).
@@ -81,11 +81,11 @@
Each "find open bracket" command goes backward to the open bracket
matching the (_N-th) close bracket in the bottom line.
- m_<_l_e_t_t_e_r_> Mark the current top line with <letter>.
- M_<_l_e_t_t_e_r_> Mark the current bottom line with <letter>.
- '_<_l_e_t_t_e_r_> Go to a previously marked position.
- '' Go to the previous position.
- ^X^X Same as '.
+ m_<_l_e_t_t_e_r_> * Mark the top line (or line _N) with <letter>.
+ M_<_l_e_t_t_e_r_> * Mark the bottom line (or line _N) with <letter>.
+ '_<_l_e_t_t_e_r_> * Go to a previously marked position (put on position _N).
+ '' * Go to the previous position.
+ ^X^X * Same as '.
ESC-m_<_l_e_t_t_e_r_> Clear a mark.
---------------------------------------------------
A mark is any upper-case or lower-case letter.
@@ -202,9 +202,9 @@
-V ........ --version
Display the version number of "less".
-w ........ --hilite-unread
- Highlight first new line after forward-screen.
+ Highlight first new line after full screen movement.
-W ........ --HILITE-UNREAD
- Highlight first new line after any forward movement.
+ Highlight first new line after any movement.
-x [_N[,...]] --tabs=[_N[,...]]
Set tab stops.
-X ........ --no-init
@@ -222,6 +222,10 @@
--autosave=[_m_/_!_*]
Actions which cause the history file to be saved.
+ --emouse=[_v_s_c_r_o_l_l,_v_d_r_a_g,_h_s_c_r_o_l_l,_h_d_r_a_g,_l_c_l_i_c_k,_r_c_l_i_c_k]
+ Enable mouse features.
+ --end-prompt=[_s_t_r_i_n_g]
+ String to be printed after erasing the prompt.
--exit-follow-on-close
Exit F command on a pipe when writer closes pipe.
--file-size
@@ -232,6 +236,8 @@
Stop scrolling when a form feed character is reached.
--header=[_L[,_C[,_N]]]
Use _L lines (starting at line _N) and _C columns as headers.
+ --hilite-target
+ Highlight the target line.
--incsearch
Search file as each pattern character is typed in.
--intr=[_C]
@@ -247,7 +253,7 @@
--modelines=[_N]
Read _N lines from the input file and look for vim modelines.
--mouse
- Enable mouse input.
+ Enable mouse clicking and vertical scrolling.
--no-edit-warn
Don't warn when using v command on a file opened via LESSOPEN.
--no-keypad
@@ -276,6 +282,8 @@
Set default options for every search.
--show-preproc-errors
Display a message if preprocessor exits with an error status.
+ --past-eof
+ Scrolling commands continue past end of file.
--proc-backspace
Process backspaces for bold/underline.
--PROC-BACKSPACE
diff --git a/contrib/less/less.nro b/contrib/less/less.nro
index 093d9f121e4b..6b72acdf504c 100644
--- a/contrib/less/less.nro
+++ b/contrib/less/less.nro
@@ -1,5 +1,5 @@
'\" t
-.TH LESS 1 "29 Jan 2026" "less 692"
+.TH LESS 1 "30 May 2026" "less 704"
.\" Define a string for the caret ("hat") as portably as possible.
.ie \n(.g .ds ^ \(ha
.el .ds ^ ^
@@ -268,6 +268,7 @@ go backward to the < which matches the > in the bottom displayed line.
.IP m
Followed by any lowercase or uppercase letter,
marks the first displayed line with that letter.
+If a number N is given, line number N is marked instead.
If the status column is enabled via the
.B "\-J"
option,
@@ -275,6 +276,7 @@ the status column shows the marked line.
.IP M
Acts like m, except the last displayed line is marked
rather than the first displayed line.
+If a number N is given, line number N is marked instead.
.IP "\*'"
(Single quote.)
Followed by any lowercase or uppercase letter, returns to the position which
@@ -283,6 +285,14 @@ Followed by another single quote, returns to the position at
which the last "large" movement command was executed.
Followed by a \*^ or $, jumps to the beginning or end of the
file respectively.
+If a number N is given, the marked line is placed on the N-th line
+on the screen.
+Otherwise, it is placed on the same line that it was on when it was marked
+(the first line on the screen if marked via the
+.B m
+command, the last line on the screen if marked via the
+.B M
+command, or the line that was clicked if marked via the mouse).
Marks are preserved when a new file is examined within a single invocation of
.BR less ,
so the \*' command can be used to switch between input files.
@@ -295,7 +305,7 @@ Same as single quote.
.IP "ESC-m"
Followed by any lowercase or uppercase letter,
clears the mark identified by that letter.
-.IP /pattern
+.IP "/\fIpattern\fP"
Search forward in the file for the N-th line containing the pattern.
N defaults to 1.
The pattern is a regular expression, as recognized by
@@ -360,7 +370,7 @@ If the \*^W modifier is set, the \*^E modifier is ignored.
The next character is taken literally; that is, it becomes part of the pattern
even if it is one of the above search modifier characters.
.RE
-.IP ?pattern
+.IP "?\fIpattern\fP"
Search backward in the file for the N-th line containing the pattern.
The search starts at the last line displayed
(but see the
@@ -401,9 +411,9 @@ current file up to the line where it started.
.IP "\*^L"
As in forward searches.
.RE
-.IP "ESC-/pattern"
+.IP "ESC-/\fIpattern\fP"
Same as "/*".
-.IP "ESC-?pattern"
+.IP "ESC-?\fIpattern\fP"
Same as "?*".
.IP n
Repeat previous search, for N-th line containing the last pattern.
@@ -430,6 +440,7 @@ Turn off highlighting of strings matching the current search pattern.
If highlighting is already off because of a previous ESC-u command,
turn highlighting back on.
Any search command will also turn highlighting back on.
+Also unselects any OSC 8 hyperlink that is currently selected.
(Highlighting can also be disabled by toggling the
.B "\-G"
option;
@@ -440,9 +451,9 @@ If the status column is enabled via the
.B "\-J"
option,
this clears all search matches marked in the status column.
-.IP "&pattern"
+.IP "&\fIpattern\fP"
Display only lines which match the pattern;
-lines which do not match the pattern are not displayed.
+lines which do not match the pattern are hidden.
If pattern is empty (if you type & immediately followed by ENTER),
any filtering is turned off, and all lines are displayed.
While filtering is in effect, an ampersand is displayed at the
@@ -459,13 +470,13 @@ Display only lines which do NOT match the pattern.
Don't interpret regular expression metacharacters;
that is, do a simple textual comparison.
.RE
-.IP ":e [filename]"
+.IP ":e [\fIfilename\fP]"
Examine a new file.
If the filename is missing, the "current" file (see the :n and :p commands
below) from the list of files in the command line is re-examined.
-A percent sign (%) in the filename is replaced by the name of the
-current file.
-A pound sign (#) is replaced by the name of the previously examined file.
+A percent sign (%) in the filename is replaced by the
+shell-escaped name of the current file.
+A pound sign (#) is replaced by the shell-escaped name of the previously examined file.
However, two consecutive percent signs are simply
replaced with a single percent sign.
This allows you to enter a filename that contains a percent sign
@@ -570,7 +581,7 @@ The setting of the option is not changed.
Like the _ (underscore) command, but takes a long option name
rather than a single option letter.
You must press ENTER or RETURN after typing the option name.
-.IP +cmd
+.IP +\fIcmd\fP
Causes the specified cmd to be executed each time a new file is examined.
For example, +G causes
.B less
@@ -594,11 +605,11 @@ The editor is taken from the environment variable VISUAL if defined,
or EDITOR if VISUAL is not defined,
or defaults to "vi" if neither VISUAL nor EDITOR is defined.
See also the discussion of LESSEDIT under the section on PROMPTS below.
-.IP "! shell-command"
+.IP "! \fIshell-command\fP"
Invokes a shell to run the shell-command given.
-A percent sign (%) in the command is replaced by the name of the
-current file.
-A pound sign (#) is replaced by the name of the previously examined file.
+A percent sign (%) in the filename is replaced by the
+shell-escaped name of the current file.
+A pound sign (#) is replaced by the shell-escaped name of the previously examined file.
"!!" repeats the last shell command.
"!" with no shell command invokes an interactive shell.
If a \*^P (CONTROL-P) is entered immediately after the !,
@@ -606,88 +617,85 @@ no "done" message is printed after the shell command is executed.
On Unix systems, the shell is taken from the environment variable SHELL,
or defaults to "sh".
On MS-DOS, Windows, and OS/2 systems, the shell is the normal command processor.
-.IP "# shell-command"
+.IP "# \fIshell-command\fP"
Similar to the "!" command,
except that the command is expanded in the same way as prompt strings.
For example, the name of the current file would be given as "%f".
-.IP "| <m> shell-command"
+.IP "| <\fIm\fP> \fIshell-command\fP"
<m> represents any mark letter.
Pipes a section of the input file to the given shell command.
-The section of the file to be piped is between the position marked by
-the letter and the current screen.
-The entire current screen is included, regardless of whether the
-marked position is before or after the current screen.
+If the marked line is before the top line of the current screen,
+the section to be piped is between the marked line and the bottom line
+of the current screen, inclusive.
+Otherwise (if the marked line is the top line of the current screen
+or after it) the section of the file to be piped is between the top line
+of the current screen and the marked line, inclusive.
<m> may also be \*^ or $ to indicate beginning or end of file respectively.
If <m> is \&.\& or newline, the current screen is piped.
If a \*^P (CONTROL-P) is entered immediately after the mark letter,
no "done" message is printed after the shell command is executed.
-.IP "s filename"
+.IP "s \fIfilename\fP"
Save the input to a file.
This works only if the input is a pipe, not an ordinary file.
.IP "\*^O\*^O"
.RS
Run a shell command to open the URI in the current OSC 8 hyperlink,
selected by a previous \*^O\*^N or \*^O\*^P command.
-To find the shell command,
-the environment variable named "LESS_OSC8_xxx" is read,
+The shell command is given by the environment variable
+named "LESS_OSC8_OPEN_xxx"
where "xxx" is the scheme from the URI (the part before the first colon),
-or is empty if there is no colon in the URI.
-The value of the environment variable is then expanded in the same way as
-prompt strings (in particular, any instance of "%o" is replaced with the URI)
-to produce an OSC 8 "handler" shell command.
-The standard output from the handler is an "opener" shell command
-which is then executed to open the URI.
-.PP
-There are two special cases:
-.RS
-.IP 1.
-If the URI begins with "#", the remainder of the URI is taken to be
-the value of the id parameter in another OSC 8 link in the same file,
-and \*^O\*^O will simply jump to that link.
-.IP 2.
-If the opener begins with the characters ":e" followed by
-whitespace and a filename,
-then instead of running the opener as a shell command,
-the specified filename is opened in the current instance of
-.BR less .
-.RE
+converted to lowercase.
+If there is no colon in the URI, the environment variable
+LESS_OSC8_OPEN_NONE is used.
+The URI from the hyperlink is then shell-escaped and appended to the
+value of the environment variable after a space,
+and the resulting string is executed as a shell command.
.PP
-In a simple case where the opener accepts the complete URI
-as a command line parameter, the handler may be as simple as
+For example, if you wish to open https links by invoking a program
+called "mybrowser" with a "\-\-new\-window" option, you could set
.PP
.EX
.nf
-echo mybrowser \*'%o\*'
+LESS_OSC8_OPEN_https="mybrowser \-\-new\-window"
.fi
.EE
.PP
-In other cases, the URI may need to be modified, so the handler
-may have to do some manipulation of the %o value.
+If the handler command begins with "\-",
+the command is not displayed before it is executed.
+If the handler command begins with \*^P (CONTROL-P),
+the "link done" message is not displayed after it is executed.
+If the handler command begins with \*^P followed by "\-",
+neither the command nor the "link done" message is displayed.
.PP
-If the LESS_OSC8_xxx variable is not set, the variable LESS_OSC8_ANY is tried.
-If neither LESS_OSC8_xxx nor LESS_OSC8_ANY is set,
-links using the "xxx" scheme cannot be opened.
-However, there are default handlers for the
-schemes "man" (used when LESS_OSC8_man is not set)
-and "file" (used when LESS_OSC8_file is not set),
-which should work on systems which provide the
-.BR sed (1)
-command and a shell with syntax compatible with the Bourne shell
-.BR sh (1).
-If you use LESS_OSC8_ANY to override LESS_OSC8_file, you must
-set LESS_OSC8_file to "\-" to indicate that the default value
-should not be used, and likewise for LESS_OSC8_man.
+If you attempt to open a link with the "xxx" scheme and the
+LESS_OSC8_OPEN_xxx variable is not set,
+the variable LESS_OSC8_OPEN_ANY is tried.
+If neither LESS_OSC8_OPEN_xxx nor LESS_OSC8_OPEN_ANY is set,
+links using the "xxx" scheme cannot be opened.
+However, there is a default value for LESS_OSC8_OPEN_ANY
+which invokes the program
+.BR less\-osc8\-open (1).
+This program will open links with the
+schemes "man" (by running
+.BR man (1))
+or "file" (by running another instance of
+.BR less (1)
+if the file is local).
+.B less\-osc8\-open
+should work on systems which provide a POSIX-compatible shell.
.PP
-The URI passed to an OSC8 handler via %o is guaranteed not to contain any single quote
-or double quote characters, but it may contain any other shell metacharacters
-such as semicolons, dollar signs, ampersands, etc.
-The handler should take care to appropriately quote parameters in the opener command,
-to prevent execution of unintended shell commands in the case of opening
+Any shell metacharacters such as semicolons, dollar signs, ampersands, etc.
+in the URI will be shell-escaped before the URI is appended to the handler command.
+If the handler passes the URI or any substring of it to a shell,
+it should take care to appropriately quote such strings,
+to prevent execution of unintended shell commands when opening
a URI which contains shell metacharacters.
-Also, since the handler command is expanded like a command prompt,
-any metacharacters interpreted by prompt expansion
-(such as percent, dot, colon, backslash, etc.) must be escaped with a backslash
-(see the PROMPTS section for details).
+.PP
+As a special case, if the URI begins with "#",
+the remainder of the URI is taken to be
+the value of the id parameter in another OSC 8 link in the same file,
+and \*^O\*^O will simply jump to that link rather than running
+a handler program.
.RE
.IP "\*^X"
When the "Waiting for data" message is displayed,
@@ -816,7 +824,7 @@ the bottom of the screen
and backward searches to start at the top of the screen,
thus skipping all lines displayed on the screen.
.IP "\fB\-A\fP or \fB\-\-SEARCH\-SKIP\-SCREEN\fP"
-Causes all forward searches (not just non-repeated searches)
+auses all forward searches (not just non-repeated searches)
to start just after the target line, and all backward searches
to start just before the target line.
Thus, forward searches will skip part of the displayed screen
@@ -893,6 +901,8 @@ Errors and informational messages.
Header lines and columns, set via the
.B "\-\-header"
option.
+.IP "J"
+Target line when highlighted via \-\-hilite\-target.
.IP "M"
Mark letters in the status column.
.IP "N"
@@ -905,6 +915,9 @@ Prompts.
The rscroll character.
.IP "S"
Search results.
+.IP "T"
+The tilde lines displayed after the end of the file or
+before the beginning of the file.
.IP "W"
The highlight enabled via the
.B "\-w"
@@ -1001,7 +1014,7 @@ the corresponding color is set to that of normal text.
.PP
A 4-bit or 8-bit color string may be followed by one or more of the
following characters to set text attributes in addition to the color.
-.IP "s or ~"
+.IP "s or \*~"
Standout (reverse video)
.IP "u or _"
Underline
@@ -1247,7 +1260,7 @@ Warning: certain environment variables such as
LESS, LESSSECURE, LESSCHARSET and others,
which are used early in startup,
cannot be set in a file specified by a command line option
-.RB "(" "\-\-lesskey" "," "\-\-lesskey\-src" " or " "\-\-lesskey\-content" ")."
+.RB "(" "\-\-lesskey" ", " "\-\-lesskey\-src" " or " "\-\-lesskey\-content" ")."
When using a
.B lesskey
file to set environment variables, it is safer to use the
@@ -1560,7 +1573,7 @@ options.
Displays the version number of
.BR less .
.IP "\fB\-w\fP or \fB\-\-hilite\-unread\fP"
-Temporarily highlights the first "new" line after a forward movement
+Temporarily highlights the first "new" line after a movement
of a full page.
The first "new" line is the line immediately following the line previously
at the bottom of the screen.
@@ -1579,16 +1592,22 @@ in which case only the status column is highlighted.
Like
.BR "\-w" ","
but temporarily highlights the first new line after any
-forward movement command larger than one line.
+movement command larger than one line.
.IP "\fB\-x\fP\fIn\fP,...\& or \fB\-\-tabs=\fP\fIn\fP,..."
Sets tab stops.
If only one \fIn\fP is specified, tab stops are set at multiples of \fIn\fP.
If multiple values separated by commas are specified, tab stops
are set at those positions, and then continue with the same spacing as the
last two.
+Position numbers are zero-based: the leftmost position is 0,
+the next is 1, etc.
For example,
-.RB "\[dq]" "\-x9,17" "\[dq]"
-will set tabs at positions 9, 17, 25, 33, etc.
+.RB "\[dq]" "\-x8,16" "\[dq]"
+is the same as
+.RB "\[dq]" "\-x8" "\[dq]"
+and sets tabs at position 8, 16, 24, 32, etc.
+.RB "\[dq]" "\-x10,14,20" "\[dq]"
+will set tabs at position 10, 14, 20, 26, 32, etc.
The default for \fIn\fP is 8.
.IP "\fB\-X\fP or \fB\-\-no\-init\fP"
Disables sending the termcap initialization and deinitialization strings
@@ -1693,6 +1712,76 @@ option, while
commands specified by the \fB+\fP option are executed even if
.B less
exits immediately.
+.IP "\fB\-\-emouse=\fP\fIfeatures\fP
+\fIfeatures\fP is a comma-separated list of mouse features to be enabled,
+selected from this list:
+.RS
+.IP "vscroll"
+Scrolling the mouse wheel down or up moves forward or backward in
+the file.
+The number of lines to scroll when the wheel is moved can be set by the
+.B "\-\-wheel\-lines"
+option.
+The
+.B "\-\-rmouse"
+option reverses the direction of movement.
+.IP "vdrag"
+Holding down the left button and dragging up or down moves
+forward or backward in the file.
+.IP "hscroll"
+Scrolling the side scroll wheel left or right moves left or right
+in the file.
+The number of columns to scroll when the wheel is moved can be set by the
+.B "\-\-wheel\-lines"
+option.
+The
+.B "\-\-rmouse"
+option reverses the direction of movement.
+.IP "hdrag"
+Holding down the left button and dragging left or right moves
+left or right in the file.
+.IP "lclick"
+Left-clicking sets the "#" mark to the line where the mouse is clicked.
+If a left-click is performed with the mouse cursor on an OSC 8 hyperlink,
+the hyperlink is selected as if by the \*^O\*^N command.
+If a left-click is performed with the mouse cursor on an OSC 8 hyperlink
+which is already selected, the hyperlink is opened as if by the \*^O\*^O command.
+.IP "rclick"
+Right-clicking (or clicking any button other than the left one)
+returns to the "#" mark position.
+.IP "scroll"
+Same as "vscroll,hscroll".
+.IP "drag"
+Same as "vdrag,hdrag".
+.IP "hmove"
+Same as "hscroll,hdrag".
+.IP "vmove"
+Same as "vscroll,vdrag".
+.IP "move"
+Same as "vmove,hmove".
+.IP "click"
+Same as "lclick,rclick".
+.IP "all"
+Same as "move,click".
+.RE
+.IP
+Each feature name may be abbreviated as long as the abbreviation
+is unambiguous.
+If \fIfeatures\fP is set to "-", all mouse features are disabled.
+Mouse input works only on terminals which support X11 mouse reporting,
+and on Windows.
+.IP "\fB\-\-end\-prompt=\fP\fIstring\fP"
+Specifies a string which is printed immediately after the prompt is erased.
+As with the \fB\-\-prompt\fP option, the first character of the string
+determines which prompt the string will be printed after
+("\fBs\fP" for the short prompt, "\fBm\fP" for the medium prompt,
+or "\fBM\fP" for the long prompt).
+Special characters in the string are processed as described in the PROMPTS
+section, in the same way as prompts specified by the \fB\-\-prompt\fP option.
+If set to a dash (\fB\-\fP), nothing is printed after the prompt is erased.
+This option can be useful if the prompt string has been modified to contain
+an escape sequence that puts the terminal in a mode which should be cleared
+when the prompt is erased.
.IP "\fB\-\-exit\-follow\-on\-close\fP"
When using the "F" command on a pipe,
.B less
@@ -1705,7 +1794,8 @@ is specified,
.B less
will determine the size of the file
immediately after opening the file.
-Then the "=" command will display the number of lines in the file.
+With this option enabled, the = command and the long prompt will display
+the number of lines in the file.
Normally this is not done, because it can be slow if the input file
is non-seekable (such as a pipe) and is large.
.IP "\fB\-\-follow\-name\fP"
@@ -1768,6 +1858,17 @@ and the
.B "\-S"
option is ignored.
.RE
+.IP "\fB\-\-hilite\-target\fP"
+Highlights the "target" line, as specified by the \-j option.
+If the
+.B "\-\-status\-line"
+option is in effect, the entire line
+(the width of the screen) is highlighted.
+Otherwise, only the text in the line is highlighted,
+unless the
+.B "\-J"
+option is in effect,
+in which case only the status column is highlighted.
.IP "\fB\-\-incsearch\fP"
Subsequent search commands will be "incremental"; that is,
.B less
@@ -1833,27 +1934,13 @@ See the
description for acceptable values of \fIn\fP.
.RE
.IP "\fB\-\-mouse\fP"
-Enables mouse input:
-scrolling the mouse wheel down moves forward in the file,
-scrolling the mouse wheel up moves backwards in the file,
-left-click sets the "#" mark to the line where the mouse is clicked,
-and right-click (or any other) returns to the "#" mark position.
-Holding down the left button and dragging also moves in the file.
-If a left-click is performed with the mouse cursor on an OSC 8 hyperlink,
-the hyperlink is selected as if by the \*^O\*^N command.
-If a left-click is performed with the mouse cursor on an OSC 8 hyperlink
-which is already selected, the hyperlink is opened as if by the \*^O\*^O command.
-The number of lines to scroll when the wheel is moved
-can be set by the
-.B "\-\-wheel\-lines"
-option.
-Mouse input works only on terminals which support X11 mouse reporting,
-and on Windows.
+Enabling \fB\-\-mouse\fP is the same as \fB\-\-emouse=vmove,click\fP.
+Disabling \fB\-\-mouse\fP is the same as \fB\-\-emouse=-\fP.
.IP "\fB\-\-MOUSE\fP"
Like
-.BR "\-\-mouse" ","
-except the direction scrolled
-on mouse wheel movement is reversed.
+.B "\-\-mouse"
+with
+.BR "\-\-rmouse" "."
.IP "\fB\-\-no\-edit\-warn\fP"
Don't print a warning message when using the v command
on a file which was opened using a LESSOPEN preprocessor
@@ -1891,6 +1978,12 @@ Searches do not include header columns, but still include header lines.
Searches do not include header lines or header columns.
.IP "\fB\-\-no\-vbell\fP"
Disables the terminal's visual bell.
+.IP "\fB\-\-past\-eof\fP"
+Normally scrolling commands (other than J, K, ESC-SPACE and ESC-b)
+stop scrolling when end-of-file is reached scrolling forward, or
+beginning-of-file is reached scrolling backward.
+This option makes all scrolling commands continue past end-of-file
+or beginning-of-file.
.IP "\fB\-\-proc\-backspace\fP"
If set, backspaces are handled as if neither the
.B "\-u"
@@ -1966,6 +2059,9 @@ terminal to switch from an alternate screen,
this makes the last screenful of the current file remain visible after
.B less
has quit.
+.IP "\fB\-\-rmouse\fP"
+Reverses the normal direction of scrolling when mouse scrolling is
+enabled via \fB\-\-emouse=vscroll\fP or \fB\-\-emouse=hscroll\fP.
.IP "\fB\-\-rscroll=\fP\fIc\fP"
This option changes the character used to mark truncated lines.
It may begin with a two-character attribute indicator like LESSBINFMT does.
@@ -2004,6 +2100,8 @@ If a line is marked, the entire line (rather than just the status column)
is highlighted.
Also lines highlighted due to the
.B "\-w"
+or
+.B "\-\-hilite\-target"
option will have
the entire line highlighted.
If
@@ -2027,13 +2125,13 @@ ANSI color escape sequences (as defined in
https://\*:www\*:.ecma\-international\*:.org/\*:publications\-and\-standards/\*:standards/\*:ecma\-48).
.hy
.IP "\fB\-\-wheel\-lines=\fP\fIn\fP"
-Set the number of lines to scroll when the mouse wheel is scrolled
+Set the number of lines or columns to scroll when the mouse wheel is scrolled
and the
.B "\-\-mouse"
or
.B "\-\-MOUSE"
option is in effect.
-The default is 1 line.
+The default is 1 line or column.
.IP "\fB\-\-wordwrap\fP"
When the
.B "\-S"
@@ -2098,7 +2196,7 @@ Move the cursor to the end of the line.
.IP "BACKSPACE"
Delete the character to the left of the cursor,
or cancel the command if the command line is empty.
-.IP "DELETE or [ ESC-x ]"
+.IP "DELETE [ ESC-x ]"
Delete the character under the cursor.
.IP "\*^BACKSPACE [ ESC-BACKSPACE ]"
(That is, CONTROL and BACKSPACE simultaneously.)
@@ -2118,13 +2216,13 @@ it will retrieve the next command which begins with that text.
Complete the partial filename to the left of the cursor.
If it matches more than one filename, the first match
is entered into the command line.
-Repeated TABs will cycle thru the other matching filenames.
+Repeated TABs will cycle through the other matching filenames.
If the completed filename is a directory, a "/" is appended to the filename.
(On MS-DOS and Windows systems, a "\e" is appended.)
The environment variable LESSSEPARATOR can be used to specify a
different character to append to a directory name.
.IP "BACKTAB (SHIFT-TAB) [ ESC-TAB ]"
-Like TAB, but cycles in the reverse direction thru the matching filenames.
+Like TAB, but cycles in the reverse direction through the matching filenames.
.IP "\*^L"
Complete the partial filename to the left of the cursor.
If it matches more than one filename, all matches are entered into
@@ -2355,14 +2453,14 @@ For compatibility with previous versions of
the input preprocessor or pipe is not used if
.B less
is viewing standard input.
-However, if the first character of LESSOPEN is a dash (\-),
+However, if the first character of LESSOPEN is a dash (\fB\-\fP),
the input preprocessor is used on standard input as well as other files.
In this case, the dash is not considered to be part of
the preprocessor command.
If standard input is being viewed, the input preprocessor is passed
a file name consisting of a single dash.
Similarly, if the first two characters of LESSOPEN are vertical bar and dash
-(|\-) or two vertical bars and a dash (||\-),
+(\fB|\-\fP) or two vertical bars and a dash (\fB||\-\fP),
the input pipe is used on standard input as well as other files.
Again, in this case the dash is not considered to be part of
the input pipe command.
@@ -2627,10 +2725,6 @@ The line to be used is determined by the \fIX\fP, as with the %b option.
Replaced by the line number of the last line in the input file.
.IP "%m"
Replaced by the total number of input files.
-.IP "%o"
-Replaced by the URI of the currently selected OSC 8 hyperlink,
-or a question mark if no hyperlink is selected.
-This is used by OSC 8 handlers as explained in the \*^O\*^O command description.
.IP "%p\fIX\fP"
Replaced by the percent into the current input file, based on byte offsets.
The line used is determined by the \fIX\fP as with the %b option.
@@ -2681,7 +2775,7 @@ True if the byte offset of the specified line is known.
.IP "?B"
True if the size of current input file is known.
.IP "?c"
-True if the text is horizontally shifted (%c is not zero).
+True if the text is horizontally shifted (%c is greater than 1).
.IP "?d\fIX\fP"
True if the page number of the specified line is known.
.IP "?e"
@@ -2697,6 +2791,8 @@ True if the line number of the last line in the file is known.
True if there is more than one input file.
.IP "?n"
True if this is the first prompt in a new input file.
+.IP "?O"
+True if an OSC 8 hyperlink is selected.
.IP "?p\fIX\fP"
True if the percent into the current input file, based on byte offsets,
of the specified line is known.
@@ -3085,12 +3181,15 @@ The default is 4000 (4 seconds).
Emulate the
.BR more (1)
command.
-.IP LESS_OSC8_xxx
+.IP LESS_OSC8_OPEN_xxx
Where "xxx" is a URI scheme such as "http" or "file",
sets an OSC 8 handler for opening OSC 8 links containing a URI with that scheme.
-.IP LESS_OSC8_ANY
+.IP LESS_OSC8_OPEN_ANY
Sets an OSC 8 handler for opening OSC 8 links for which there is
-no specific LESS_OSC8_xxx handler set for the "xxx" scheme.
+no specific LESS_OSC8_OPEN_xxx handler set for the "xxx" scheme.
+.IP LESS_OSC8_OPEN_NONE
+Sets an OSC 8 handler for opening OSC 8 links which contain no scheme
+(that is, there is no colon in the URI).
.IP LESS_SHELL_LINES
When the
.B "\-F"
diff --git a/contrib/less/lessecho.nro b/contrib/less/lessecho.nro
index c860dd847405..d7bb81a300ed 100644
--- a/contrib/less/lessecho.nro
+++ b/contrib/less/lessecho.nro
@@ -1,4 +1,4 @@
-.TH LESSECHO 1 "29 Jan 2026" "less 692"
+.TH LESSECHO 1 "30 May 2026" "less 704"
.\" Define a string to interpolate the hyphenless break point escape
.\" sequence (for URLs and similar) if the formatter claims groff
.\" compatibility, else interpolate nothing.
diff --git a/contrib/less/lesskey.nro b/contrib/less/lesskey.nro
index 11f5a306aea8..e472a059ce87 100644
--- a/contrib/less/lesskey.nro
+++ b/contrib/less/lesskey.nro
@@ -1,5 +1,5 @@
'\" t
-.TH LESSKEY 1 "29 Jan 2026" "less 692"
+.TH LESSKEY 1 "30 May 2026" "less 704"
.\" Define a string for the caret ("hat") as portably as possible.
.ie \n(.g .ds ^ \(ha
.el .ds ^ ^
@@ -148,6 +148,7 @@ l l.
\ekp+ + on the numeric keypad
\ekp. . on the numeric keypad
\ekp, , on the numeric keypad
+\ekpe ENTER on the numeric keypad
.TE
.RE
.PP
diff --git a/contrib/less/lesskey_parse.c b/contrib/less/lesskey_parse.c
index d19ae26dffdf..d69b0cd7d0fd 100644
--- a/contrib/less/lesskey_parse.c
+++ b/contrib/less/lesskey_parse.c
@@ -128,6 +128,7 @@ static constant struct lesskey_cmdname editnames[] =
{ "literal", EC_LITERAL },
{ "mouse", EC_X11MOUSE },
{ "mouse6", EC_X116MOUSE },
+ { "noaction", A_NOACTION },
{ "right", EC_RIGHT },
{ "up", EC_UP },
{ "word-backspace", EC_W_BACKSPACE },
@@ -295,6 +296,7 @@ static constant char * tstr(char **pp, int xlate)
case '+': ch = SK_PAD_PLUS; break;
case '.': ch = SK_PAD_DOT; break;
case ',': ch = SK_PAD_COMMA; break;
+ case 'e': ch = SK_PAD_ENTER; break;
}
break;
}
diff --git a/contrib/less/line.c b/contrib/less/line.c
index 25ff641774b9..00535a2347a5 100644
--- a/contrib/less/line.c
+++ b/contrib/less/line.c
@@ -135,6 +135,8 @@ static struct color_map color_map[] = {
{ AT_COLOR_RSCROLL, "kc" },
{ AT_COLOR_HEADER, "" },
{ AT_COLOR_SEARCH, "kG" },
+ { AT_COLOR_TILDE, "-d" },
+ { AT_COLOR_TARGET, "-u" },
{ AT_COLOR_SUBSEARCH(1), "ky" },
{ AT_COLOR_SUBSEARCH(2), "wb" },
{ AT_COLOR_SUBSEARCH(3), "YM" },
@@ -446,7 +448,7 @@ public void plinestart(POSITION pos)
* Return the width of the line prefix (status column and line number).
* {{ Actual line number can be wider than linenum_width. }}
*/
-public int line_pfx_width(void)
+public unsigned line_pfx_width(void)
{
int width = 0;
if (status_col)
@@ -1077,7 +1079,7 @@ static int flush_mbc_buf(POSITION pos)
*/
public int pappend_b(char c, POSITION pos, lbool before_pendc)
{
- LWCHAR ch = c & 0377;
+ LWCHAR ch = (unsigned char) c;
int r;
if (pendc && !before_pendc)
@@ -1192,6 +1194,22 @@ static int store_control_char(LWCHAR ch, constant char *rep, POSITION pos)
return (0);
}
+/*
+ * Remove invalid ANSI sequence.
+ */
+static void remove_ansi(void)
+{
+ constant char *start = (cshift < hshift) ? xbuf_char_data(&shifted_ansi): linebuf.buf;
+ size_t *end = (cshift < hshift) ? &shifted_ansi.end : &linebuf.end;
+ constant char *p = start + *end;
+ LWCHAR bch;
+ do {
+ bch = step_charc(&p, -1, start);
+ } while (p > start && (!IS_CSI_START(bch) || line_ansi->escs_in_seq-- > 0));
+ *end = ptr_diff(p, start);
+ xbuf_reset(&last_ansi);
+}
+
static int store_ansi(LWCHAR ch, constant char *rep, POSITION pos)
{
switch (ansi_step2(line_ansi, ch, pos != NULL_POSITION))
@@ -1215,18 +1233,7 @@ static int store_ansi(LWCHAR ch, constant char *rep, POSITION pos)
curr_last_ansi = (curr_last_ansi + 1) % NUM_LAST_ANSIS;
break;
case ANSI_ERR:
- {
- /* Remove whole unrecognized sequence. */
- constant char *start = (cshift < hshift) ? xbuf_char_data(&shifted_ansi): linebuf.buf;
- size_t *end = (cshift < hshift) ? &shifted_ansi.end : &linebuf.end;
- constant char *p = start + *end;
- LWCHAR bch;
- do {
- bch = step_charc(&p, -1, start);
- } while (p > start && (!IS_CSI_START(bch) || line_ansi->escs_in_seq-- > 0));
- *end = ptr_diff(p, start);
- }
- xbuf_reset(&last_ansi);
+ remove_ansi();
ansi_done(line_ansi);
line_ansi = NULL;
break;
@@ -1388,13 +1395,12 @@ static void add_attr_normal(void)
{
switch (line_ansi->ostate)
{
- case OSC_TYPENUM:
- case OSC8_PARAMS:
- case OSC8_URI:
- case OSC_STRING:
- addstr_linebuf("\033\\", AT_ANSI, 0);
+ case OSC_START:
+ case OSC_END:
break;
default:
+ /* We're in an unterminated OSC sequence; remove it. */
+ remove_ansi();
break;
}
ansi_done(line_ansi);
@@ -1410,7 +1416,7 @@ static void add_attr_normal(void)
/*
* Terminate the line in the line buffer.
*/
-public void pdone(lbool endline, lbool chopped, lbool forw)
+public void pdone(lbool endline, lbool chopped, lbool forw, lbool full_pad)
{
(void) pflushmbc();
linebuf.prev_end = (!endline && !chopped) ? linebuf.end : 0;
@@ -1462,8 +1468,8 @@ public void pdone(lbool endline, lbool chopped, lbool forw)
/*
* If we're coloring a status line, fill out the line with spaces.
*/
- if (status_line && line_mark_attr != 0) {
- while (end_column +1 < sc_width + cshift)
+ if (status_line && (line_mark_attr != 0 || full_pad)) {
+ while (end_column < sc_width + cshift)
add_linebuf(' ', line_mark_attr, 1);
}
@@ -1664,7 +1670,7 @@ public int gline(size_t i, int *ap)
{
if (i == 0)
{
- *ap = AT_BOLD;
+ *ap = use_color ? AT_COLOR_TILDE : AT_BOLD;
return '~';
}
--i;
@@ -1681,7 +1687,7 @@ public int gline(size_t i, int *ap)
}
i += linebuf.print - linebuf.pfx_end;
*ap = linebuf.attr[i];
- return (linebuf.buf[i] & 0xFF);
+ return (unsigned char) linebuf.buf[i];
}
/*
@@ -1878,10 +1884,10 @@ static int pappstr(constant char *str)
/*
* Load a string into the line buffer.
- * If the string is too long to fit on the screen,
+ * If the string is too long to fit on the screen (minus reserve chars),
* truncate the beginning of the string to fit.
*/
-public void load_line(constant char *str)
+public void load_line(constant char *str, int attr, int reserve)
{
int save_hshift = hshift;
hshift = 0;
@@ -1889,6 +1895,7 @@ public void load_line(constant char *str)
/* We're overwriting the line buffer, so what's in it will no longer be contiguous. */
set_line_contig_pos(NULL_POSITION);
+ sc_width -= reserve;
for (;;)
{
prewind(FALSE);
@@ -1903,13 +1910,14 @@ public void load_line(constant char *str)
}
set_linebuf(linebuf.end, '\0', AT_NORMAL);
linebuf.prev_end = 0;
+ sc_width += reserve;
/* Color the prompt unless it has ansi sequences in it. */
if (!ansi_in_line)
{
size_t i;
for (i = linebuf.print; i < linebuf.end; i++)
- set_linebuf(i, linebuf.buf[i], AT_STANDOUT|AT_COLOR_PROMPT);
+ set_linebuf(i, linebuf.buf[i], attr);
}
hshift = save_hshift;
}
diff --git a/contrib/less/lsystem.c b/contrib/less/lsystem.c
index 63c0cf074524..40bff05954e2 100644
--- a/contrib/less/lsystem.c
+++ b/contrib/less/lsystem.c
@@ -266,7 +266,7 @@ public int pipe_mark(char c, constant char *cmd)
if (c == '.')
return (pipe_data(cmd, tpos, bpos));
- else if (mpos <= tpos)
+ else if (mpos < tpos)
return (pipe_data(cmd, mpos, bpos));
else if (bpos == NULL_POSITION)
return (pipe_data(cmd, tpos, bpos));
diff --git a/contrib/less/main.c b/contrib/less/main.c
index 3adc06dc1cd1..defd65db07ef 100644
--- a/contrib/less/main.c
+++ b/contrib/less/main.c
@@ -29,7 +29,7 @@ public unsigned less_acp = CP_ACP;
public char * every_first_cmd = NULL;
public lbool new_file;
-public int is_tty;
+public lbool is_tty;
public IFILE curr_ifile = NULL_IFILE;
public IFILE old_ifile = NULL_IFILE;
public struct scrpos initial_scrpos;
@@ -38,7 +38,7 @@ public POSITION end_attnpos = NULL_POSITION;
public int wscroll;
public constant char *progname;
public lbool quitting = FALSE;
-public int dohelp;
+public lbool dohelp = FALSE;
public char * init_header = NULL;
public char * no_config = NULL;
static int secure_allow_features;
@@ -68,7 +68,7 @@ public time_type less_start_time;
static wchar_t consoleTitle[256];
#endif
-public int one_screen;
+public lbool one_screen;
extern int less_is_more;
extern lbool missing_cap;
extern int know_dumb;
@@ -158,26 +158,16 @@ cleanup:
}
#endif
-#if !SECURE
-static int security_feature_error(constant char *type, size_t len, constant char *name)
-{
- PARG parg;
- size_t msglen = len + strlen(type) + 64;
- char *msg = ecalloc(msglen, sizeof(char));
- SNPRINTF3(msg, msglen, "LESSSECURE_ALLOW: %s feature name \"%.*s\"", type, (int) len, name);
- parg.p_string = msg;
- error("%s", &parg);
- free(msg);
- return 0;
-}
-
/*
- * Return the SF_xxx value of a secure feature given the name of the feature.
+ * Set the secure_allow_features bitmask, which controls
+ * whether certain secure features are allowed.
*/
-static int security_feature(constant char *name, size_t len)
+static void init_secure(void)
{
- struct secure_feature { constant char *name; int sf_value; };
- static struct secure_feature features[] = {
+#if SECURE
+ secure_allow_features = 0;
+#else
+ static struct csl_bitmap_def security_features[] = {
{ "edit", SF_EDIT },
{ "examine", SF_EXAMINE },
{ "glob", SF_GLOB },
@@ -191,39 +181,6 @@ static int security_feature(constant char *name, size_t len)
{ "stop", SF_STOP },
{ "tags", SF_TAGS },
};
- int i;
- int match = -1;
-
- for (i = 0; i < countof(features); i++)
- {
- if (strncmp(features[i].name, name, len) == 0)
- {
- if (match >= 0) /* name is ambiguous */
- return security_feature_error("ambiguous", len, name);
- match = i;
- }
- }
- if (match < 0)
- return security_feature_error("invalid", len, name);
- return features[match].sf_value;
-}
-
-static lbool set_security_feature(constant char *word, size_t wlen, void *arg)
-{
- secure_allow_features |= security_feature(word, wlen);
- return TRUE;
-}
-#endif /* !SECURE */
-
-/*
- * Set the secure_allow_features bitmask, which controls
- * whether certain secure features are allowed.
- */
-static void init_secure(void)
-{
-#if SECURE
- secure_allow_features = 0;
-#else
constant char *str = lgetenv("LESSSECURE");
if (isnullenv(str))
secure_allow_features = ~0; /* allow everything */
@@ -232,7 +189,8 @@ static void init_secure(void)
str = lgetenv("LESSSECURE_ALLOW");
if (!isnullenv(str))
- parse_csl(set_security_feature, str, NULL);
+ secure_allow_features = parse_csl_bitmap(str,
+ security_features, countof(security_features), "LESSSECURE_ALLOW");
#endif
}
@@ -294,7 +252,7 @@ int main(int argc, constant char *argv[])
* Process command line arguments and LESS environment arguments.
* Command line arguments override environment arguments.
*/
- is_tty = isatty(1);
+ is_tty = (isatty(1) != 0);
init_mark();
init_cmds();
init_poll();
@@ -353,7 +311,8 @@ int main(int argc, constant char *argv[])
if (less_is_more)
no_init = TRUE;
- get_term();
+ if (is_tty)
+ get_term();
expand_cmd_tables();
#if EDITOR
@@ -419,10 +378,10 @@ int main(int argc, constant char *argv[])
* Output is not a tty.
* Just copy the input file(s) to output.
*/
- set_output(1, TRUE); /* write to stdout */
SET_BINARY(1);
if (edit_first() == 0)
{
+ set_output(1, TRUE); /* write to stdout */
do {
cat_file();
} while (edit_next(1) == 0);
diff --git a/contrib/less/mark.c b/contrib/less/mark.c
index 7813429ce58a..070bbd54b58f 100644
--- a/contrib/less/mark.c
+++ b/contrib/less/mark.c
@@ -161,6 +161,7 @@ static struct mark * getmark(char c)
{
struct mark *m;
static struct mark sm;
+ POSITION pos;
switch (c)
{
@@ -181,7 +182,13 @@ static struct mark * getmark(char c)
return (NULL);
}
m = &sm;
- cmark(m, curr_ifile, ch_tell(), sc_height);
+ pos = ch_tell();
+ if (pos == NULL_POSITION)
+ return (NULL);
+ pos = back_line(pos, NULL);
+ if (pos == NULL_POSITION)
+ return (NULL);
+ cmark(m, curr_ifile, pos, sc_height-1);
break;
case '.':
/*
@@ -225,19 +232,35 @@ public lbool badmark(char c)
/*
* Set a user-defined mark.
*/
-public void setmark(char c, int where)
+public void setmark(char c, int where, LINENUM linenum)
{
struct mark *m;
struct scrpos scrpos;
+ POSITION pos;
m = getumark(c);
if (m == NULL)
return;
- get_scrpos(&scrpos, where);
- if (scrpos.pos == NULL_POSITION)
+
+ if (linenum == 0)
{
- lbell();
- return;
+ get_scrpos(&scrpos, where);
+ if (scrpos.pos == NULL_POSITION)
+ {
+ lbell();
+ return;
+ }
+ } else
+ {
+ pos = find_pos(linenum);
+ if (pos == NULL_POSITION)
+ {
+ PARG parg;
+ parg.p_linenum = linenum;
+ error("Cannot find line number %n", &parg);
+ return;
+ }
+ get_scrpos_pos(&scrpos, where, pos);
}
cmark(m, curr_ifile, scrpos.pos, scrpos.ln);
marks_modified = TRUE;
@@ -260,7 +283,12 @@ public void clrmark(char c)
lbell();
return;
}
- m->m_scrpos.pos = NULL_POSITION;
+ mark_clear(m);
+#if CMD_HISTORY
+ /* Also clear in file_marks, so save_marks doesn't save it to history file. */
+ m = &file_marks[mark_index(c)];
+ mark_clear(m);
+#endif
marks_modified = TRUE;
if (perma_marks && autosave_action('m'))
save_cmdhist();
@@ -285,7 +313,7 @@ public void lastmark(void)
/*
* Go to a mark.
*/
-public void gomark(char c)
+public void gomark(char c, int sline)
{
struct mark *m;
struct scrpos scrpos;
@@ -293,6 +321,8 @@ public void gomark(char c)
m = getmark(c);
if (m == NULL)
return;
+ if (sline != 0)
+ m->m_scrpos.ln = sline;
/*
* If we're trying to go to the lastmark and
@@ -466,10 +496,6 @@ public void restore_mark(constant char *line)
ln = lstrtoic(line, &line, 10);
if (ln < 0)
return;
- if (ln < 1)
- ln = 1;
- if (ln > sc_height)
- ln = sc_height;
skip_whitespace;
pos = lstrtoposc(line, &line, 10);
if (pos < 0)
diff --git a/contrib/less/optfunc.c b/contrib/less/optfunc.c
index 2370103349a0..a39e93181be7 100644
--- a/contrib/less/optfunc.c
+++ b/contrib/less/optfunc.c
@@ -34,10 +34,11 @@ extern lbool plusoption;
extern int swindow;
extern int sc_width;
extern int sc_height;
-extern int dohelp;
+extern lbool dohelp;
extern char openquote;
extern char closequote;
extern char *prproto[];
+extern char *eprproto[];
extern char *eqproto;
extern char *hproto;
extern char *wproto;
@@ -53,7 +54,9 @@ extern int match_shift;
extern long match_shift_fraction;
extern LWCHAR rscroll_char;
extern int rscroll_attr;
-extern int mousecap;
+extern int emouse;
+extern int mouse_reverse;
+extern int xmouse;
extern int wheel_lines;
extern int less_is_more;
extern int linenum_width;
@@ -68,6 +71,7 @@ extern int tabstops[];
extern int ntabstops;
extern int tabdefault;
extern int no_paste;
+extern int hilite_target;
extern char intr_char;
extern int nosearch_header_lines;
extern int nosearch_header_cols;
@@ -87,7 +91,7 @@ extern char ztags[];
#endif
#if LESSTEST
extern constant char *ttyin_name;
-extern int is_tty;
+extern lbool is_tty;
#endif /*LESSTEST*/
#if MSDOS_COMPILER
extern int nm_fg_color, nm_bg_color, nm_attr;
@@ -333,7 +337,7 @@ public void opt__S(int type, constant char *s)
switch (type)
{
case TOGGLE:
- pos_rehead();
+ pos_rehead(TRUE);
break;
}
}
@@ -469,7 +473,49 @@ public void opt__P(int type, constant char *s)
break;
case QUERY:
parg.p_string = prproto[pr_type];
- error("%s", &parg);
+ switch (pr_type)
+ {
+ case PR_MEDIUM: error("Prompt (medium): %s", &parg); break;
+ case PR_LONG: error("Prompt (long): %s", &parg); break;
+ default: error("Prompt (short): %s", &parg); break;
+ }
+ break;
+ }
+}
+
+/*
+ * Handler for --end-prompt option.
+ */
+public void opt_end_prompt(int type, constant char *s)
+{
+ char **pend;
+ PARG parg;
+
+ switch (type)
+ {
+ case INIT:
+ case TOGGLE:
+ switch (*s)
+ {
+ case 's': pend = &eprproto[PR_SHORT]; s++; break;
+ case 'm': pend = &eprproto[PR_MEDIUM]; s++; break;
+ case 'M': pend = &eprproto[PR_LONG]; s++; break;
+ default: pend = &eprproto[PR_SHORT]; break;
+ }
+ if (*pend != NULL)
+ free(*pend);
+ *pend = (strcmp(s, "-") == 0) ? NULL : save(s);
+ break;
+ case QUERY:
+ parg.p_string = eprproto[pr_type];
+ if (parg.p_string == NULL)
+ parg.p_string = "(nothing)";
+ switch (pr_type)
+ {
+ case PR_MEDIUM: error("Print after medium prompt: %s", &parg); break;
+ case PR_LONG: error("Print after long prompt: %s", &parg); break;
+ default: error("Print after short prompt: %s", &parg); break;
+ }
break;
}
}
@@ -613,11 +659,13 @@ static int color_from_namechar(char namechar)
case 'C': return AT_COLOR_CTRL;
case 'E': return AT_COLOR_ERROR;
case 'H': return AT_COLOR_HEADER;
+ case 'J': return AT_COLOR_TARGET;
case 'M': return AT_COLOR_MARK;
case 'N': return AT_COLOR_LINENUM;
case 'P': return AT_COLOR_PROMPT;
case 'R': return AT_COLOR_RSCROLL;
case 'S': return AT_COLOR_SEARCH;
+ case 'T': return AT_COLOR_TILDE;
case 'W': case 'A': return AT_COLOR_ATTN;
case 'n': return AT_NORMAL;
case 's': return AT_STANDOUT;
@@ -648,6 +696,11 @@ public void opt_D(int type, constant char *s)
if (*s == 'a')
{
sgr_mode = !sgr_mode;
+ if (type == TOGGLE)
+ {
+ p.p_string = (sgr_mode) ? "on" : "off";
+ error("SGR mode is %s", &p);
+ }
break;
}
#endif
@@ -700,12 +753,6 @@ public void opt_D(int type, constant char *s)
return;
}
break;
-#if MSDOS_COMPILER
- case QUERY:
- p.p_string = (sgr_mode) ? "on" : "off";
- error("SGR mode is %s", &p);
- break;
-#endif
}
}
@@ -868,7 +915,7 @@ public void opt_query(int type, constant char *s)
error("Use \"h\" for help", NULL_PARG);
break;
case INIT:
- dohelp = 1;
+ dohelp = TRUE;
}
}
@@ -897,20 +944,99 @@ public void calc_match_shift(void)
}
/*
+ * Handler for the --emouse option.
+ */
+ /*ARGSUSED*/
+public void opt_emouse(int type, constant char *s)
+{
+ /* Order of entries matters for QUERY.
+ * Combinations must come after their components. */
+ static struct csl_bitmap_def emouse_defs[] = {
+ { "hscroll", EMOUSE_HSCROLL },
+ { "vscroll", EMOUSE_VSCROLL },
+ { "hdrag", EMOUSE_HDRAG },
+ { "vdrag", EMOUSE_VDRAG },
+ { "lclick", EMOUSE_LCLICK },
+ { "rclick", EMOUSE_RCLICK },
+ { "scroll", EMOUSE_HSCROLL|EMOUSE_VSCROLL },
+ { "drag", EMOUSE_HDRAG|EMOUSE_VDRAG },
+ { "hmove", EMOUSE_HSCROLL|EMOUSE_HDRAG },
+ { "vmove", EMOUSE_VSCROLL|EMOUSE_VDRAG },
+ { "move", EMOUSE_VSCROLL|EMOUSE_VDRAG|EMOUSE_HSCROLL|EMOUSE_HDRAG },
+ { "click", EMOUSE_LCLICK|EMOUSE_RCLICK },
+ { "all", EMOUSE_VSCROLL|EMOUSE_VDRAG|EMOUSE_HSCROLL|EMOUSE_HDRAG|EMOUSE_LCLICK|EMOUSE_RCLICK },
+ };
+
+ switch (type)
+ {
+ case INIT:
+ case TOGGLE: {
+ lbool was_mouse_enabled = (emouse != 0);
+ if (s == NULL || strcmp(s, "-") == 0)
+ emouse = 0;
+ else
+ emouse = parse_csl_bitmap(s,
+ emouse_defs, countof(emouse_defs), "--emouse");
+ if (type == TOGGLE && was_mouse_enabled != (emouse != 0))
+ {
+ if (emouse == 0)
+ deinit_mouse();
+ else
+ init_mouse();
+ }
+ break; }
+ case QUERY: {
+ char buf[128];
+ char *bp = buf;
+ char *ebuf = buf + sizeof(buf);
+ int qmouse = emouse;
+ PARG parg;
+ int i;
+
+ for (i = countof(emouse_defs)-1; i >= 0; i--)
+ {
+ int bit = emouse_defs[i].bit_value;
+ if ((qmouse & bit) == bit)
+ {
+ if (bp > buf && bp+1 < ebuf)
+ *bp++ = ',';
+ strncpy(bp, emouse_defs[i].bit_name, ptr_diff(ebuf, bp));
+ bp += strlen(bp);
+ qmouse &= ~bit;
+ }
+ }
+ *bp = '\0';
+ parg.p_string = buf;
+ if (buf[0] == '\0')
+ error("Ignore mouse input", NULL_PARG);
+ else
+ error("Mouse features enabled: %s", &parg);
+ break; }
+ }
+}
+
+/*
* Handler for the --mouse option.
*/
/*ARGSUSED*/
-public void opt_mousecap(int type, constant char *s)
+public void opt_mouse(int type, constant char *s)
{
switch (type)
{
+ case INIT:
case TOGGLE:
- if (mousecap == OPT_OFF)
- deinit_mouse();
- else
- init_mouse();
+ switch (xmouse)
+ {
+ case OPT_OFF:
+ opt_emouse(type, "-");
+ break;
+ case OPT_ON:
+ case OPT_ONPLUS:
+ opt_emouse(type, "vmove,click");
+ mouse_reverse = (xmouse == OPT_ONPLUS);
+ break;
+ }
break;
- case INIT:
case QUERY:
break;
}
@@ -1116,13 +1242,31 @@ public void opt_header(int type, constant char *s)
set_header(start_pos);
calc_jump_sline();
break; }
- case QUERY: {
- char buf[3*INT_STRLEN_BOUND(long)+3];
- PARG parg;
- SNPRINTF3(buf, sizeof(buf), "%ld,%ld,%ld", (long) header_lines, (long) header_cols, (long) find_linenum(header_start_pos));
- parg.p_string = buf;
- error("Header (lines,columns,line-number) is %s", &parg);
- break; }
+ case QUERY: {
+ char buf[3*INT_STRLEN_BOUND(long)+3];
+ PARG parg;
+ SNPRINTF3(buf, sizeof(buf), "%ld,%ld,%ld", (long) header_lines, (long) header_cols, (long) find_linenum(header_start_pos));
+ parg.p_string = buf;
+ error("Header (lines,columns,line-number) is %s", &parg);
+ break; }
+ }
+}
+
+/*
+ * Handler for the --hilite-target option.
+ */
+ /*ARGSUSED*/
+public void opt_hilite_target(int type, constant char *s)
+{
+ switch (type)
+ {
+ case INIT:
+ break;
+ case TOGGLE:
+ draw_target_attn(hilite_target != OPT_OFF);
+ break;
+ case QUERY:
+ break;
}
}
@@ -1242,11 +1386,11 @@ public void opt_no_paste(int type, constant char *s)
init_bracketed_paste();
else
deinit_bracketed_paste();
- break;
+ break;
case INIT:
case QUERY:
break;
- }
+ }
}
#if LESSTEST
@@ -1260,7 +1404,7 @@ public void opt_ttyin_name(int type, constant char *s)
{
case INIT:
ttyin_name = s;
- is_tty = 1;
+ is_tty = TRUE;
break;
}
}
diff --git a/contrib/less/opttbl.c b/contrib/less/opttbl.c
index 5f9b102d934b..a06d10c8eba3 100644
--- a/contrib/less/opttbl.c
+++ b/contrib/less/opttbl.c
@@ -50,6 +50,7 @@ public int no_init; /* Disable sending ti/te termcap strings */
public int no_keypad; /* Disable sending ks/ke termcap strings */
public int twiddle; /* Show tildes after EOF */
public int show_attn; /* Hilite first unread line */
+public int hilite_target; /* Hilite target line */
public int status_col; /* Display a status column */
public int use_lessopen; /* Use the LESSOPEN filter */
public int quit_on_intr; /* Quit on interrupt */
@@ -59,7 +60,9 @@ public int opt_use_backslash; /* Use backslash escaping in option parsing */
public LWCHAR rscroll_char; /* Char which marks chopped lines with -S */
public int rscroll_attr; /* Attribute of rscroll_char */
public int no_hist_dups; /* Remove dups from history list */
-public int mousecap; /* Allow mouse for scrolling */
+public int emouse; /* Enabled mouse features */
+public int mouse_reverse; /* Reverse mouse wheel scrolling direction */
+public int xmouse; /* Old --mouse option, replaced by --emouse */
public int wheel_lines; /* Number of lines to scroll on mouse wheel scroll */
public int perma_marks; /* Save marks in history file */
public int linenum_width; /* Width of line numbers */
@@ -85,10 +88,12 @@ public int match_shift; /* Extra horizontal shift on search match */
public int no_paste; /* Don't accept pasted input */
public int no_edit_warn; /* Don't warn when editing a LESSOPENed file */
public int stop_on_form_feed; /* Stop scrolling on a line starting with form feed */
+public int past_eof; /* Continue scrolling past EOF */
public long match_shift_fraction = NUM_FRAC_DENOM/2; /* 1/2 of screen width */
public char intr_char = CONTROL('X'); /* Char to interrupt reads */
public char *first_cmd_at_prompt = NULL; /* Command to exec before first prompt */
public char *autosave; /* Actions which do autosave of history file */
+public char *end_prompt; /* Printed after clearing the prompt */
#if HILITE_SEARCH
public int hilite_search; /* Highlight matched search patterns? */
#endif
@@ -157,7 +162,9 @@ static struct optname follow_optname = { "follow-name", NULL };
static struct optname use_backslash_optname = { "use-backslash", NULL };
static struct optname rscroll_optname = { "rscroll", NULL };
static struct optname nohistdups_optname = { "no-histdups", NULL };
-static struct optname mousecap_optname = { "mouse", NULL };
+static struct optname mouse_optname = { "mouse", NULL };
+static struct optname emouse_optname = { "emouse", NULL };
+static struct optname mouse_reverse_optname = { "rmouse", NULL };
static struct optname wheel_lines_optname = { "wheel-lines", NULL };
static struct optname perma_marks_optname = { "save-marks", NULL };
static struct optname linenum_width_optname = { "line-num-width", NULL };
@@ -169,6 +176,7 @@ static struct optname status_line_optname = { "status-line", NULL };
static struct optname header_optname = { "header", NULL };
static struct optname no_paste_optname = { "no-paste", NULL };
static struct optname form_feed_optname = { "form-feed", NULL };
+static struct optname past_eof_optname = { "past-eof", NULL };
static struct optname no_edit_warn_optname2 = { "no-warn-edit", NULL };
static struct optname no_edit_warn_optname = { "no-edit-warn", &no_edit_warn_optname2 };
static struct optname nonum_headers_optname = { "no-number-headers", NULL };
@@ -189,6 +197,8 @@ static struct optname proc_return_optname = { "proc-return", NULL };
static struct optname match_shift_optname = { "match-shift", NULL };
static struct optname first_cmd_at_prompt_optname = { "cmd", NULL };
static struct optname autosave_optname = { "autosave", NULL };
+static struct optname hilite_target_optname = { "hilite-target", NULL };
+static struct optname end_prompt_optname = { "end-prompt", NULL };
#if LESSTEST
static struct optname ttyin_name_optname = { "tty", NULL };
#endif /*LESSTEST*/
@@ -439,6 +449,14 @@ static struct loption option[] =
O_NOVAR, 0, NULL, opt__V,
{ NULL, NULL, NULL }
},
+ { OLETTER_NONE, &hilite_target_optname,
+ O_BOOL, OPT_OFF, &hilite_target, opt_hilite_target,
+ {
+ "Don't highlight target line",
+ "Highlight target line",
+ NULL
+ }
+ },
{ 'w', &w_optname,
O_TRIPLE|O_REPAINT, OPT_OFF, &show_attn, NULL,
{
@@ -530,8 +548,8 @@ static struct loption option[] =
{ OLETTER_NONE, &use_backslash_optname,
O_BOOL, OPT_OFF, &opt_use_backslash, NULL,
{
- "Use backslash escaping in command line parameters",
"Don't use backslash escaping in command line parameters",
+ "Use backslash escaping in command line parameters",
NULL
}
},
@@ -547,12 +565,24 @@ static struct loption option[] =
NULL
}
},
- { OLETTER_NONE, &mousecap_optname,
- O_TRIPLE, OPT_OFF, &mousecap, opt_mousecap,
+ { OLETTER_NONE, &mouse_optname,
+ O_TRIPLE, OPT_OFF, &xmouse, opt_mouse,
{
"Ignore mouse input",
- "Use the mouse for scrolling",
- "Use the mouse for scrolling (reverse)"
+ "Use the mouse for scrolling vertically",
+ "Use the mouse for scrolling vertically (reverse)"
+ }
+ },
+ { OLETTER_NONE, &emouse_optname,
+ O_STRING, 0, NULL, opt_emouse,
+ { "Mouse features: ", "s", NULL }
+ },
+ { OLETTER_NONE, &mouse_reverse_optname,
+ O_BOOL, OPT_OFF, &mouse_reverse, NULL,
+ {
+ "Normal mouse scroll direction",
+ "Reverse mouse scroll direction",
+ NULL
}
},
{ OLETTER_NONE, &wheel_lines_optname,
@@ -614,8 +644,8 @@ static struct loption option[] =
{ OLETTER_NONE, &status_line_optname,
O_BOOL|O_REPAINT, OPT_OFF, &status_line, NULL,
{
- "Don't color each line with its status column color",
- "Color each line with its status column color",
+ "Line highlight applies to text only",
+ "Line highlight applies to entire width of screen",
NULL
}
},
@@ -639,6 +669,14 @@ static struct loption option[] =
NULL
}
},
+ { OLETTER_NONE, &past_eof_optname,
+ O_BOOL, OPT_OFF, &past_eof, NULL,
+ {
+ "Stop scrolling at end of file",
+ "Don't stop scrolling at end of file",
+ NULL
+ }
+ },
{ OLETTER_NONE, &no_edit_warn_optname,
O_BOOL, OPT_OFF, &no_edit_warn, NULL,
{
@@ -769,6 +807,10 @@ static struct loption option[] =
O_STRING|O_INIT_HANDLER, 0, NULL, opt_autosave,
{ "Autosave actions: ", "s", NULL }
},
+ { OLETTER_NONE, &end_prompt_optname,
+ O_STRING, 0, NULL, opt_end_prompt,
+ { "Print after prompt: ", "s", NULL }
+ },
#if LESSTEST
{ OLETTER_NONE, &ttyin_name_optname,
O_STRING|O_NO_TOGGLE, 0, NULL, opt_ttyin_name,
diff --git a/contrib/less/os.c b/contrib/less/os.c
index 06ebce12eb96..1b8a92be03e9 100644
--- a/contrib/less/os.c
+++ b/contrib/less/os.c
@@ -80,7 +80,6 @@ static lbool any_data = FALSE;
static lbool reading;
static lbool opening;
public lbool waiting_for_data;
-public int consecutive_nulls = 0;
public lbool getting_one_screen = FALSE;
/* Milliseconds to wait for data before displaying "waiting for data" message. */
@@ -97,9 +96,9 @@ extern int exit_F_on_close;
extern int follow_mode;
extern int scanning_eof;
extern char intr_char;
-extern int is_tty;
+extern lbool is_tty;
extern int quit_if_one_screen;
-extern int one_screen;
+extern lbool one_screen;
#if HAVE_TIME
extern time_type less_start_time;
#endif
@@ -305,46 +304,36 @@ start:
if (ret != 0)
{
if (ret == READ_INTR)
+ {
sigs |= S_SWINTERRUPT;
+ getcc_clear();
+ }
reading = FALSE;
return (ret);
}
}
#else
#if MSDOS_COMPILER==WIN32C
- if (!(quit_if_one_screen && one_screen) && win32_kbhit2(TRUE))
+ if (!(quit_if_one_screen && one_screen))
{
- int c = WIN32getch();
- if (c == CONTROL('C') || c == intr_char)
+ char c;
+ if (win32_kbhit2(&c))
{
- sigs |= S_SWINTERRUPT;
- reading = FALSE;
- return (READ_INTR);
+ if (c == CONTROL('C') || c == intr_char)
+ {
+ sigs |= S_SWINTERRUPT;
+ reading = FALSE;
+ win32_clear_queue();
+ return (READ_INTR);
+ }
+ win32_enqueue((int) c);
}
- WIN32ungetch(c);
}
#endif
#endif
n = read(fd, buf, len);
reading = FALSE;
-#if 0
- /*
- * This is a kludge to workaround a problem on some systems
- * where terminating a remote tty connection causes read() to
- * start returning 0 forever, instead of -1.
- */
- {
- if (!ignore_eoi)
- {
- if (n == 0)
- consecutive_nulls++;
- else
- consecutive_nulls = 0;
- if (consecutive_nulls > 20)
- quit(QUIT_ERROR);
- }
- }
-#endif
+
if (n < 0)
{
#if HAVE_ERRNO
diff --git a/contrib/less/output.c b/contrib/less/output.c
index 14627f65c900..bc50b0caf716 100644
--- a/contrib/less/output.c
+++ b/contrib/less/output.c
@@ -21,17 +21,22 @@
#endif
public int errmsgs; /* Count of messages displayed by error() */
-public int need_clr;
-public int final_attr;
+public lbool prompting = FALSE;
+static lbool need_clr = FALSE;
extern int sigs;
extern int sc_width;
extern int so_s_width, so_e_width;
-extern int is_tty;
+extern lbool is_tty;
extern int oldbot;
extern int utf_mode;
+extern int status_col;
+extern int status_line;
+extern int hilite_target;
+extern int use_color;
extern char intr_char;
extern lbool term_init_ever;
+extern int pr_type;
#if MSDOS_COMPILER==WIN32C || MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
extern int ctldisp;
@@ -49,11 +54,13 @@ extern int vt_enabled;
/*
* Display the line which is in the line buffer.
*/
-public void put_line(lbool forw_scroll)
+public void put_line_hilite(lbool forw_scroll, lbool target)
{
int c;
size_t i;
int a;
+ lbool empty_line = TRUE;
+ constant int target_attr = use_color ? AT_COLOR_TARGET : AT_UNDERLINE;
if (ABORT_SIGS())
{
@@ -64,12 +71,27 @@ public void put_line(lbool forw_scroll)
return;
}
- final_attr = AT_NORMAL;
-
for (i = 0; (c = gline(i, &a)) != '\0'; i++)
{
+ if (target && a == AT_NORMAL)
+ {
+ /* We're highlighting this line as the target line. Highlight
+ * this char if it's not already highlighted, and either we're
+ * highlighting the whole line or we're highlighting only the
+ * status column and this is the status column. */
+ if ((status_col && i == 0) ||
+ (i >= line_pfx_width() && (status_line || !status_col)))
+ a = target_attr;
+ }
+ if (target && (c == '\n' || c == '\r') && empty_line)
+ {
+ /* Line is empty; add a space to carry the target hilite. */
+ at_switch(target_attr);
+ putchr(' ');
+ }
+ if (!(a & AT_ANSI))
+ empty_line = FALSE;
at_switch(a);
- final_attr = a;
if (c == '\b')
putbs();
else
@@ -81,6 +103,11 @@ public void put_line(lbool forw_scroll)
clear_eol();
}
+public void put_line(lbool forw_scroll)
+{
+ put_line_hilite(forw_scroll, FALSE);
+}
+
/*
* win_flush has at least one non-critical issue when an escape sequence
* begins at the last char of the buffer, and possibly more issues.
@@ -466,25 +493,14 @@ public int putchr(int ch)
*/
if (!term_init_ever && outfd == 1)
term_init();
-
-#if 0 /* fake UTF-8 output for testing */
- if (utf_mode)
+ if (prompting)
{
- static char ubuf[MAX_UTF_CHAR_LEN];
- static int ubuf_len = 0;
- static int ubuf_index = 0;
- if (ubuf_len == 0)
- {
- ubuf_len = utf_len(c);
- ubuf_index = 0;
- }
- ubuf[ubuf_index++] = c;
- if (ubuf_index < ubuf_len)
- return c;
- c = get_wchar(ubuf) & 0xFF;
- ubuf_len = 0;
+ constant char *epr = end_pr_string();
+ prompting = FALSE;
+ if (epr != NULL)
+ putstr(epr);
}
-#endif
+
clear_bot_if_needed();
#if MSDOS_COMPILER
if (c == '\n' && is_tty)
@@ -512,7 +528,7 @@ public void clear_bot_if_needed(void)
{
if (!need_clr)
return;
- need_clr = 0;
+ need_clr = FALSE;
clear_bot();
}
@@ -743,7 +759,7 @@ static void ierror_suffix(constant char *fmt, constant PARG *parg, constant char
putstr(suffix3);
at_exit();
flush();
- need_clr = 1;
+ need_clr = TRUE;
}
public void ierror(constant char *fmt, constant PARG *parg)
diff --git a/contrib/less/pattern.c b/contrib/less/pattern.c
index 228c89b49fcc..47b449e1c7ca 100644
--- a/contrib/less/pattern.c
+++ b/contrib/less/pattern.c
@@ -147,7 +147,11 @@ public int compile_pattern(constant char *pattern, int search_type, int show_err
{
int result;
- if (caseless != OPT_ONPLUS || (re_handles_caseless && !(search_type & SRCH_NO_REGEX)))
+#if RE_HANDLES_CASELESS
+ if (caseless != OPT_ONPLUS || (!(search_type & SRCH_NO_REGEX)))
+#else
+ if (caseless != OPT_ONPLUS)
+#endif
{
result = compile_pattern2(pattern, search_type, comp_pattern, show_error);
} else
@@ -391,7 +395,7 @@ static lbool match_pattern1(PATTERN_TYPE pattern, constant char *tpattern, const
#if HAVE_PCRE2
{
int flags = (notbol) ? PCRE2_NOTBOL : 0;
- pcre2_match_data *md = pcre2_match_data_create(nsp-1, NULL);
+ pcre2_match_data *md = pcre2_match_data_create_from_pattern(pattern, NULL);
int mcount = pcre2_match(pattern, (PCRE2_SPTR)line, line_len,
line_off, flags, md, NULL);
matched = (mcount > 0);
diff --git a/contrib/less/pattern.h b/contrib/less/pattern.h
index 3f7caf3d4b9e..07d879287a96 100644
--- a/contrib/less/pattern.h
+++ b/contrib/less/pattern.h
@@ -25,7 +25,7 @@ extern int less_is_more;
#endif
#define PATTERN_TYPE regex_t *
#define SET_NULL_PATTERN(name) name = NULL
-#define re_handles_caseless TRUE
+#define RE_HANDLES_CASELESS 1
#endif
/* ---- PCRE ---- */
@@ -33,7 +33,7 @@ extern int less_is_more;
#include <pcre.h>
#define PATTERN_TYPE pcre *
#define SET_NULL_PATTERN(name) name = NULL
-#define re_handles_caseless TRUE
+#define RE_HANDLES_CASELESS 1
#endif
/* ---- PCRE2 ---- */
@@ -42,7 +42,7 @@ extern int less_is_more;
#include <pcre2.h>
#define PATTERN_TYPE pcre2_code *
#define SET_NULL_PATTERN(name) name = NULL
-#define re_handles_caseless TRUE
+#define RE_HANDLES_CASELESS 1
#endif
/* ---- RE_COMP ---- */
@@ -76,6 +76,6 @@ extern int reg_show_error;
#define SET_NULL_PATTERN(name)
#endif
-#ifndef re_handles_caseless
-#define re_handles_caseless FALSE
+#ifndef RE_HANDLES_CASELESS
+#define RE_HANDLES_CASELESS 0
#endif
diff --git a/contrib/less/position.c b/contrib/less/position.c
index 442e6c929f6f..2f67925a62cb 100644
--- a/contrib/less/position.c
+++ b/contrib/less/position.c
@@ -157,7 +157,7 @@ public lbool empty_lines(int s, int e)
int i;
for (i = s; i <= e; i++)
- if (table[i] != NULL_POSITION && table[i] != 0)
+ if (table[i] != NULL_POSITION)
return FALSE;
return TRUE;
}
@@ -170,7 +170,7 @@ public lbool empty_lines(int s, int e)
* such that the top few lines are empty, we may have to set
* the screen line to a number > 0.
*/
-public void get_scrpos(struct scrpos *scrpos, int where)
+public void get_scrpos_pos(struct scrpos *scrpos, int where, POSITION pos)
{
int i;
int dir;
@@ -194,24 +194,52 @@ public void get_scrpos(struct scrpos *scrpos, int where)
break;
}
- /*
- * Find the first line on the screen which has something on it,
- * and return the screen line number and the file position.
- */
- for (;; i += dir)
+ if (pos != NULL_POSITION)
{
- if (table[i] != NULL_POSITION)
+ scrpos->ln = i+1;
+ scrpos->pos = pos;
+ } else
+ {
+ /*
+ * Find the first line on the screen which has something on it,
+ * and return the screen line number and the file position.
+ */
+ for (;; i += dir)
{
- scrpos->ln = i+1;
- scrpos->pos = table[i];
- return;
+ if (table[i] != NULL_POSITION)
+ {
+ scrpos->ln = i+1;
+ scrpos->pos = table[i];
+ return;
+ }
+ if (i == last) break;
}
- if (i == last) break;
+ /* The screen is empty. */
+ scrpos->pos = NULL_POSITION;
}
+}
+
+public void get_scrpos(struct scrpos *scrpos, int where)
+{
+ get_scrpos_pos(scrpos, where, NULL_POSITION);
+}
+
+/*
+ * Convert sline to sindex and clip to screen size.
+ */
+static int sindex_from_sline_clipped(int sline)
+{
/*
- * The screen is empty.
+ * Can't be less than 1 or greater than sc_height-1.
*/
- scrpos->pos = NULL_POSITION;
+ if (sline <= 0)
+ sline = 1;
+ if (sline >= sc_height)
+ sline = sc_height-1;
+ /*
+ * Return zero-based line number, not one-based.
+ */
+ return (sline-1);
}
/*
@@ -231,17 +259,7 @@ public int sindex_from_sline(int sline)
*/
if (sline < 0)
sline += sc_height;
- /*
- * Can't be less than 1 or greater than sc_height-1.
- */
- if (sline <= 0)
- sline = 1;
- if (sline >= sc_height)
- sline = sc_height-1;
- /*
- * Return zero-based line number, not one-based.
- */
- return (sline-1);
+ return sindex_from_sline_clipped(sline);
}
/*
@@ -273,12 +291,14 @@ static int pos_shift(POSITION linepos, size_t choff)
* Return the position of the first char of the line containing tpos.
* Thus if tpos is the first char of its line, just return tpos.
*/
-static POSITION beginning_of_line(POSITION tpos)
+public POSITION beginning_of_line(POSITION tpos)
{
ch_seek(tpos);
while (ch_tell() != ch_zero())
{
int ch = ch_back_get();
+ if (ch == EOI)
+ break; /* {{ probably unnecessary due to ch_zero() check }} */
if (ch == '\n')
{
(void) ch_forw_get();
@@ -291,10 +311,11 @@ static POSITION beginning_of_line(POSITION tpos)
/*
* When viewing long lines, it may be that the first char in the top screen
* line is not the first char in its (file) line (the table is "beheaded").
- * This function sets that entry to the position of the first char in the line,
- * and sets hshift so that the first char in the first line is unchanged.
+ * This function sets that entry to the position of the first char in the
+ * line, and optionally sets hshift so that the first char in the first line
+ * is unchanged.
*/
-public void pos_rehead(void)
+public void pos_rehead(lbool adj_hshift)
{
POSITION linepos;
POSITION tpos = table[TOP];
@@ -304,6 +325,7 @@ public void pos_rehead(void)
if (linepos == tpos)
return;
table[TOP] = linepos;
- hshift = pos_shift(linepos, (size_t) (tpos - linepos));
+ if (adj_hshift)
+ hshift = pos_shift(linepos, (size_t) (tpos - linepos));
screen_trashed();
}
diff --git a/contrib/less/prompt.c b/contrib/less/prompt.c
index 3ab8a593877c..8055de8228f4 100644
--- a/contrib/less/prompt.c
+++ b/contrib/less/prompt.c
@@ -32,7 +32,7 @@ extern int header_lines;
extern int utf_mode;
extern IFILE curr_ifile;
#if OSC8_LINK
-extern char *osc8_path;
+extern POSITION osc8_linepos;
#endif
#if EDITOR
extern constant char *editor;
@@ -59,6 +59,7 @@ static constant char more_proto[] =
"--More--(?eEND ?x- Next\\: %x.:?pB%pB\\%:byte %bB?s/%s...%t)";
public char *prproto[3];
+public char *eprproto[3];
public char constant *eqproto = e_proto;
public char constant *hproto = h_proto;
public char constant *wproto = w_proto;
@@ -75,6 +76,7 @@ public void init_prompt(void)
prproto[0] = save(s_proto);
prproto[1] = save(less_is_more ? more_proto : m_proto);
prproto[2] = save(M_proto);
+ eprproto[0] = eprproto[1] = eprproto[2] = NULL;
eqproto = save(e_proto);
hproto = save(h_proto);
wproto = save(w_proto);
@@ -241,6 +243,12 @@ static lbool cond(char c, int where)
#else
return (new_file ? TRUE : FALSE);
#endif
+ case 'O': /* OSC 8 link selected? */
+#if OSC8_LINK
+ return (osc8_linepos != NULL_POSITION);
+#else
+ return FALSE;
+#endif
case 'p': /* Percent into file (bytes) known? */
return (curr_byte(where) != NULL_POSITION && ch_length() > 0);
case 'P': /* Percent into file (lines) known? */
@@ -368,14 +376,6 @@ static void protochar(char c, int where)
#endif
ap_int(nifile());
break; }
- case 'o': /* path (URI without protocol) of selected OSC8 link */
-#if OSC8_LINK
- if (osc8_path != NULL)
- ap_str(osc8_path);
- else
-#endif
- ap_quest();
- break;
case 'p': /* Percent into file (bytes) */
pos = curr_byte(where);
len = ch_length();
@@ -599,6 +599,21 @@ public constant char * pr_string(void)
}
/*
+ * Return the end prompt string.
+ */
+public constant char * end_pr_string(void)
+{
+ int type;
+
+ if (ch_getflags() & CH_HELPFILE)
+ return NULL;
+ type = (!less_is_more) ? pr_type : pr_type ? 0 : 1;
+ if (eprproto[type] == NULL)
+ return NULL;
+ return pr_expand(eprproto[type]);
+}
+
+/*
* Return a message suitable for printing while waiting in the F command.
*/
public constant char * wait_message(void)
diff --git a/contrib/less/regexp.c b/contrib/less/regexp.c
index 5506385cb34c..d2bf631da3e7 100644
--- a/contrib/less/regexp.c
+++ b/contrib/less/regexp.c
@@ -25,6 +25,8 @@
* *** NOTE: this code has been altered slightly for use in Tcl. ***
* Slightly modified by David MacKenzie to undo most of the changes for TCL.
* Added regexec2 with notbol parameter. -- 4/19/99 Mark Nudelman
+ * Change functions style from K&R to C89 -- 2023-09 Avi Halachmi
+ * Add support for character classes [:NAME:] -- 2026-02 Avi Halachmi
*/
#include "less.h"
@@ -47,16 +49,13 @@
* regstart char that must begin a match; '\0' if none obvious
* reganch is the match anchored (at beginning-of-line only)?
* regmust string (pointer into program) that match must include, or NULL
- * regmlen length of regmust string
*
* Regstart and reganch permit very fast decisions on suitable starting points
* for a match, cutting down the work a lot. Regmust permits fast rejection
* of lines that cannot possibly match. The regmust tests are costly enough
* that regcomp() supplies a regmust only if the r.e. contains something
* potentially expensive (at present, the only such thing detected is * or +
- * at the start of the r.e., which can involve a lot of backup). Regmlen is
- * supplied because the test in regexec() needs it and regcomp() is
- * computing it anyway.
+ * at the start of the r.e., which can involve a lot of backup).
*/
/*
@@ -251,7 +250,6 @@ regcomp(constant char *exp)
r->regstart = '\0'; /* Worst-case defaults. */
r->reganch = 0;
r->regmust = NULL;
- r->regmlen = 0;
scan = r->program+1; /* First BRANCH. */
if (OP(regnext(scan)) == END) { /* Only one top-level choice. */
scan = OPERAND(scan);
@@ -276,10 +274,10 @@ regcomp(constant char *exp)
for (; scan != NULL; scan = regnext(scan))
if (OP(scan) == EXACTLY && ((int) strlen(OPERAND(scan))) >= len) {
longest = OPERAND(scan);
+ /* redundant strlen, not critical in regcomp */
len = (int) strlen(OPERAND(scan));
}
r->regmust = longest;
- r->regmlen = len;
}
}
@@ -459,6 +457,126 @@ regpiece(int *flagp)
}
/*
+ * Handling of character classes [:NAME:]
+ *
+ * - We only cover C/POSIX locale (ASCII).
+ * - We identify the 12 POSIX classes.
+ * - We error on unknown [:THING:].
+ * - [=EQUIV=] or [.COLLATE.] are NOT detected, and considered non-special.
+ * - [:NAME:] errors on either side of '-' range (but not if the '-' is
+ * literal - at the beginning or end of the [...]).
+ * - The logic is confined to regbracket(), and only used during regcomp.
+ *
+ * POSIX regex, classes, collation, etc:
+ * https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/V1_chap09.html
+ * https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/V1_chap07.html
+ */
+
+typedef struct cclass {
+ constant size_t len; /* of id */
+ constant char *id;
+ constant char *chars;
+} cclass;
+
+static constant cclass cclasses[] = {
+ /* first two chars of id are ignored, and expected to be "[:" */
+ {9, "[:alnum:]", "0-9A-Za-z"},
+ {9, "[:alpha:]", "A-Za-z"},
+ {9, "[:blank:]", "\t "},
+ {9, "[:cntrl:]", "\x01-\x1f\x7f"},
+ {9, "[:digit:]", "0-9"},
+ {9, "[:graph:]", "\x21-\x7e"},
+ {9, "[:lower:]", "a-z"},
+ {9, "[:print:]", "\x20-\x7e"},
+ {9, "[:punct:]", "!-/:-@[-`{|}~"},
+ {9, "[:space:]", "\t\n\v\f\r "},
+ {9, "[:upper:]", "A-Z"},
+ {10, "[:xdigit:]", "0-9A-Fa-f"},
+};
+
+/* returns index>=0 in cclasses if s starts with known [:NAME:],
+ * else -2 on error (s starts with unknown [:THING:]),
+ * else -1 (not-special).
+ *
+ * we could create a wrapper macro which tests '[' and ':' before
+ * calling cclass_idx(s) - to avoid a function call on early abort,
+ * and/or replace the string search in cclass_idx with binary search
+ * or something else, but this is not hot code, so keep it simple.
+ */
+static int
+cclass_idx(constant char *s)
+{
+ int i;
+ constant char *p;
+
+ if (*s++ != '[' || *s++ != ':')
+ return -1;
+
+ /* s is past the "[:" prefix */
+ for (i = 0; i < countof(cclasses); ++i)
+ if (strncmp(s, cclasses[i].id + 2, cclasses[i].len - 2) == 0)
+ return i; /* found */
+
+ p = strchr(s, ']');
+ if (p && p != s && p[-1] == ':')
+ return -2; /* unrecognized [:THING:] */
+
+ return -1; /* not special as far as we can tell */
+}
+
+/* Calls regc(c) for each char which a bracket expression matches,
+ * without touching the global regparse.
+ *
+ * The caller should ensure that ANYOF or ANYBUT node was already added
+ * (depending on whether it starts with "[" or "[^"), and "in" should
+ * point to the first data char after this prefix.
+ *
+ * calls FAIL(...) and returns NULL on failure,
+ * or returns a pointer to the first unprocessed char (']' or '\0').
+ * the caller may then update the global regparse, and add final regc(0).
+ */
+static constant char *
+regbracket(constant char *in)
+{
+ register int clss;
+ register int classend;
+ int idx;
+
+ if (*in == ']' || *in == '-')
+ regc(*in++);
+ while (*in != '\0' && *in != ']') {
+ if (*in == '-') {
+ in++;
+ if (cclass_idx(in) != -1)
+ FAIL("invalid [] range end"); /* X-[:NAME:] */
+ if (*in == ']' || *in == '\0')
+ regc('-');
+ else {
+ clss = UCHARAT(in-2)+1;
+ classend = UCHARAT(in);
+ if (clss > classend+1)
+ FAIL("invalid [] range");
+ for (; clss <= classend; clss++)
+ regc(clss);
+ in++;
+ }
+ } else if ((idx = cclass_idx(in)) == -1) {
+ regc(*in++);
+ } else if (idx == -2) {
+ FAIL("unknown [:CLASS:] name");
+ } else { /* idx is a valid index, add class chars */
+ if (regbracket(cclasses[idx].chars) == NULL)
+ return(NULL); /* should be unreachable */
+ in += cclasses[idx].len;
+
+ if (*in == '-' && in[1] != '\0' && in[1] != ']')
+ FAIL("invalid [] range start"); /* [:NAME:]-X */
+ }
+ }
+ return in;
+}
+
+/*
- regatom - the lowest level
*
* Optimization: gobbles an entire sequence of ordinary characters so that
@@ -485,40 +603,22 @@ regatom(int *flagp)
ret = regnode(ANY);
*flagp |= HASWIDTH|SIMPLE;
break;
- case '[': {
- register int clss;
- register int classend;
-
- if (*regparse == '^') { /* Complement of range. */
- ret = regnode(ANYBUT);
- regparse++;
- } else
- ret = regnode(ANYOF);
- if (*regparse == ']' || *regparse == '-')
- regc(*regparse++);
- while (*regparse != '\0' && *regparse != ']') {
- if (*regparse == '-') {
- regparse++;
- if (*regparse == ']' || *regparse == '\0')
- regc('-');
- else {
- clss = UCHARAT(regparse-2)+1;
- classend = UCHARAT(regparse);
- if (clss > classend+1)
- FAIL("invalid [] range");
- for (; clss <= classend; clss++)
- regc(clss);
- regparse++;
- }
- } else
- regc(*regparse++);
- }
- regc('\0');
- if (*regparse != ']')
- FAIL("unmatched []");
+ case '[':
+ if (*regparse == '^') { /* Complement of range. */
+ ret = regnode(ANYBUT);
regparse++;
- *flagp |= HASWIDTH|SIMPLE;
- }
+ } else
+ ret = regnode(ANYOF);
+
+ regparse = regbracket(regparse);
+ if (regparse == NULL)
+ return(NULL);
+
+ regc('\0');
+ if (*regparse != ']')
+ FAIL("unmatched []");
+ regparse++;
+ *flagp |= HASWIDTH|SIMPLE;
break;
case '(':
ret = reg(1, &flags);
@@ -701,8 +801,7 @@ STATIC int regrepeat(char *);
#ifdef DEBUG
int regnarrate = 0;
-void regdump();
-STATIC char *regprop();
+STATIC constant char *regprop(constant char *);
#endif
/*
@@ -726,16 +825,8 @@ regexec2(register regexp *prog, register constant char *string, int notbol)
}
/* If there is a "must appear" string, look for it. */
- if (prog->regmust != NULL) {
- s = string;
- while ((s = strchr(s, prog->regmust[0])) != NULL) {
- if (strncmp(s, prog->regmust, prog->regmlen) == 0)
- break; /* Found it. */
- s++;
- }
- if (s == NULL) /* Not present. */
- return(0);
- }
+ if (prog->regmust != NULL && strstr(string, prog->regmust) == NULL)
+ return(0); /* Not present. */
/* Mark beginning of line for ^ . */
if (notbol)
@@ -1068,14 +1159,11 @@ regnext(register char *p)
#ifdef DEBUG
-STATIC char *regprop();
-
/*
- regdump - dump a regexp onto stdout in vaguely comprehensible form
*/
void
-regdump(r)
-regexp *r;
+regdump(regexp *r)
{
register char *s;
register char op = EXACTLY; /* Arbitrary non-END op. */
@@ -1085,12 +1173,13 @@ regexp *r;
s = r->program + 1;
while (op != END) { /* While that wasn't END last time... */
op = OP(s);
- printf("%2d%s", s-r->program, regprop(s)); /* Where, what. */
+ /* prints ADDRESS:... where the ":..." is from regprop(s) */
+ printf("%2ld%s", (long)(s - r->program), regprop(s));
next = regnext(s);
if (next == NULL) /* Next ptr. */
printf("(0)");
else
- printf("(%d)", (s-r->program)+(next-s));
+ printf("(%ld)", (long)((s - r->program)+(next-s)));
s += 3;
if (op == ANYOF || op == ANYBUT || op == EXACTLY) {
/* Literal string, where present. */
@@ -1117,8 +1206,7 @@ regexp *r;
- regprop - printable representation of opcode
*/
static constant char *
-regprop(op)
-constant char *op;
+regprop(constant char *op)
{
register char *p;
static char buf[50];
diff --git a/contrib/less/regexp.h b/contrib/less/regexp.h
index 12f3309ae46c..66a9bc8bc874 100644
--- a/contrib/less/regexp.h
+++ b/contrib/less/regexp.h
@@ -15,7 +15,6 @@ typedef struct regexp {
char regstart; /* Internal use only. */
char reganch; /* Internal use only. */
char *regmust; /* Internal use only. */
- int regmlen; /* Internal use only. */
char program[1]; /* Unwarranted chumminess with compiler. */
} regexp;
@@ -31,4 +30,8 @@ extern int regexec2 _ANSI_ARGS_((regexp *prog, constant char *string, int notbol
extern void regsub _ANSI_ARGS_((regexp *prog, constant char *source, char *dest));
extern void regerror _ANSI_ARGS_((constant char *msg));
+#ifdef DEBUG
+ extern void regdump _ANSI_ARGS_((regexp *r));
+#endif
+
#endif /* REGEXP */
diff --git a/contrib/less/screen.c b/contrib/less/screen.c
index 4c95f985daf5..c7872d767672 100644
--- a/contrib/less/screen.c
+++ b/contrib/less/screen.c
@@ -75,6 +75,18 @@ extern int fd0;
#if USE_TERMINFO
#include <curses.h>
#include <term.h>
+#if HAVE_TPARM2
+#define LTPARM2(s,p1,p2) tparm(s, p1, p2)
+#else
+#if HAVE_TPARM8
+#define LTPARM2(s,p1,p2) tparm(s, p1, p2, 0, 0, 0, 0, 0, 0)
+#else
+#if HAVE_TPARM9
+#define LTPARM2(s,p1,p2) tparm(s, p1, p2, 0, 0, 0, 0, 0, 0, 0)
+#else
+#endif
+#endif
+#endif
#else
#if HAVE_NCURSESW_TERMCAP_H
#include <ncursesw/termcap.h>
@@ -254,6 +266,11 @@ static constant char
*sc_init, /* Startup terminal initialization */
*sc_deinit; /* Exit terminal de-initialization */
+/* attrbits is the current attribute mode of the terminal
+ * (AT_UNDERLINE, AT_BOLD, AT_STANDOUT, AT_BLINK).
+ * attrmode is the user-defined mode, which may include AT_COLOR values,
+ * and the mapping of the AT_COLOR value may include the 4 attribute bits. */
+static int attrbits = 0;
static int attrcolor = -1;
#endif
@@ -277,14 +294,15 @@ public int can_goto_line; /* Can move cursor to any line */
public int clear_bg; /* Clear fills with background color */
public lbool missing_cap = FALSE; /* Some capability is missing */
public constant char *kent = NULL; /* Keypad ENTER sequence */
+public lbool kent_mapped = FALSE; /* Keypad ENTER is mapped to a command */
public lbool term_addrs = FALSE; /* "ti" has been sent to terminal */
public lbool full_screen = TRUE; /* We're using all lines of terminal */
-static int attrmode = AT_NORMAL;
+static int attrmode = AT_NORMAL; /* current attributes (AT_* bits) */
static int termcap_debug = -1;
static int no_alt_screen; /* sc_init does not switch to alt screen */
extern int binattr;
-extern int one_screen;
+extern lbool one_screen;
extern int shell_lines;
#if !MSDOS_COMPILER
@@ -295,15 +313,15 @@ static void tmodes(constant char *inti, constant char *outti, constant char *int
extern int quiet; /* If VERY_QUIET, use visual bell for bell */
extern int no_vbell;
-extern int no_back_scroll;
+extern lbool no_back_scroll;
extern int no_init;
extern int no_keypad;
extern int sigs;
extern int top_scroll;
extern int quit_if_one_screen;
extern int oldbot;
-extern int mousecap;
-extern int is_tty;
+extern int emouse;
+extern lbool is_tty;
extern int use_color;
extern int no_paste;
extern int wscroll;
@@ -1285,6 +1303,10 @@ public constant char * special_key_str(int key)
case SK_PAD_ZERO:
s = ltgetstr("kpZRO", NULL, &sp);
break;
+ case SK_PAD_ENTER:
+ s = kent;
+ kent_mapped = TRUE;
+ break;
case SK_DELETE:
s = ltgetstr("kdch1", "kD", &sp);
if (s == NULL)
@@ -1321,7 +1343,7 @@ public constant char * special_key_str(int key)
static constant char *ltgoto(constant char *cap, int col, int line)
{
#if USE_TERMINFO
- return tparm(cap, line, col);
+ return LTPARM2(cap, line, col);
#else
return tgoto(cap, col, line);
#endif
@@ -1352,7 +1374,7 @@ public void init_win_colors(void)
/*
* Get terminal capabilities via termcap.
*/
-public void get_term()
+public void get_term(void)
{
termcap_debug = !isnullenv(lgetenv("LESS_TERMCAP_DEBUG"));
#if MSDOS_COMPILER
@@ -1655,7 +1677,7 @@ public void get_term()
/*
* Force repaint on any backward movement.
*/
- no_back_scroll = 1;
+ no_back_scroll = TRUE;
}
}
#endif /* MSDOS_COMPILER */
@@ -1998,7 +2020,7 @@ public void term_init(void)
}
if (!no_keypad)
ltputs(sc_s_keypad, sc_height, putchr);
- if (mousecap)
+ if (emouse != 0)
init_mouse();
if (no_paste)
init_bracketed_paste();
@@ -2026,7 +2048,7 @@ public void term_init(void)
win32_init_term();
term_addrs = TRUE;
}
- if (mousecap)
+ if (emouse != 0)
init_mouse();
}
@@ -2047,7 +2069,7 @@ public void term_deinit(void)
#if !MSDOS_COMPILER
if (!(quit_if_one_screen && one_screen))
{
- if (mousecap)
+ if (emouse != 0)
deinit_mouse();
if (no_paste)
deinit_bracketed_paste();
@@ -2063,7 +2085,7 @@ public void term_deinit(void)
win32_deinit_vt_term();
if (!(quit_if_one_screen && one_screen))
{
- if (mousecap)
+ if (emouse != 0)
deinit_mouse();
if (!no_init)
win32_deinit_term();
@@ -2858,13 +2880,25 @@ static void tput_fmt(constant char *fmt, int color, int (*f_putc)(int))
static void tput_char_cattr(CHAR_ATTR cattr, int (*f_putc)(int))
{
if (cattr & CATTR_UNDERLINE)
+ {
ltputs(sc_u_in, 1, f_putc);
+ attrbits |= AT_UNDERLINE;
+ }
if (cattr & CATTR_BOLD)
+ {
ltputs(sc_b_in, 1, f_putc);
+ attrbits |= AT_BOLD;
+ }
if (cattr & CATTR_BLINK)
+ {
ltputs(sc_bl_in, 1, f_putc);
+ attrbits |= AT_BLINK;
+ }
if (cattr & CATTR_STANDOUT)
+ {
ltputs(sc_s_in, 1, f_putc);
+ attrbits |= AT_STANDOUT;
+ }
}
static void tput_color(constant char *str, int (*f_putc)(int))
@@ -2909,6 +2943,7 @@ static void tput_inmode(constant char *mode_str, int attr, int attr_bit, int (*f
if (color_str == NULL || *color_str == '\0' || *color_str == '+')
{
ltputs(mode_str, 1, f_putc);
+ attrbits |= attr_bit;
if (color_str == NULL || *color_str++ != '+')
return;
}
@@ -2918,9 +2953,10 @@ static void tput_inmode(constant char *mode_str, int attr, int attr_bit, int (*f
static void tput_outmode(constant char *mode_str, int attr_bit, int (*f_putc)(int))
{
- if ((attrmode & attr_bit) == 0)
+ if ((attrbits & attr_bit) == 0)
return;
ltputs(mode_str, 1, f_putc);
+ attrbits &= ~attr_bit;
}
#else /* MSDOS_COMPILER */
@@ -3154,7 +3190,7 @@ static lbool win32_queued_char(void)
/*
* Push a char onto the back of the win32_queue.
*/
-static void win32_enqueue(int ch)
+public void win32_enqueue(int ch)
{
WIN32_CHAR *wch = (WIN32_CHAR *) ecalloc(1, sizeof(WIN32_CHAR));
wch->wc_ch = ch;
@@ -3194,6 +3230,12 @@ static int win32_get_queue(void)
return ch;
}
+public void win32_clear_queue(void)
+{
+ while (win32_queued_char())
+ (void) win32_get_queue();
+}
+
/*
* Handle a mouse input event.
*/
@@ -3201,7 +3243,7 @@ static lbool win32_mouse_event(XINPUT_RECORD *xip)
{
char b;
- if (!mousecap || xip->ir.EventType != MOUSE_EVENT)
+ if (emouse == 0 || xip->ir.EventType != MOUSE_EVENT)
return (FALSE);
/* Generate an X11 mouse sequence from the mouse event. */
@@ -3358,7 +3400,7 @@ static lbool win32_scan_code(XINPUT_RECORD *xip)
/*
* Handle a key input event.
*/
-static lbool win32_key_event(XINPUT_RECORD *xip)
+static lbool win32_key_event(XINPUT_RECORD *xip, char *pch)
{
int repeat;
char utf8[UTF8_MAX_LENGTH];
@@ -3379,9 +3421,19 @@ static lbool win32_key_event(XINPUT_RECORD *xip)
return (FALSE);
if (!xip->ir.Event.KeyEvent.bKeyDown)
return (FALSE);
-
- if (win32_scan_code(xip))
+
+ /*
+ * If caller wants to see an immediate (unqueued) ASCII char from the
+ * keyboard, return it now. Otherwise queue the char for WIN32getch to read.
+ */
+ if (pch != NULL && xip->ichar != '\0' && is_ascii_char(xip->ichar))
+ {
+ *pch = (char) xip->ichar;
return (TRUE);
+ }
+
+ if (win32_scan_code(xip))
+ return (pch == NULL);
repeat = xip->ir.Event.KeyEvent.wRepeatCount;
if (repeat > WIN32_MAX_REPEAT)
@@ -3392,9 +3444,9 @@ static lbool win32_key_event(XINPUT_RECORD *xip)
{
constant char *p;
for (p = utf8; p < up; ++p)
- win32_enqueue(*p & 0xFF);
+ win32_enqueue((unsigned char) *p);
}
- return (TRUE);
+ return (pch == NULL);
}
/*
@@ -3411,11 +3463,11 @@ static lbool win32_window_event(XINPUT_RECORD *xip)
/*
* Determine whether an input character is waiting to be read.
*/
-public lbool win32_kbhit2(lbool no_queued)
+public lbool win32_kbhit2(char *pch)
{
XINPUT_RECORD xip;
- if (!no_queued && win32_queued_char())
+ if (pch == NULL && win32_queued_char())
return (TRUE);
for (;;)
@@ -3434,15 +3486,16 @@ public lbool win32_kbhit2(lbool no_queued)
ReadConsoleInputW(tty, &xip.ir, 1, &nread);
if (nread == 0)
return (FALSE);
- if (win32_mouse_event(&xip) || win32_key_event(&xip) || win32_window_event(&xip))
- break;
+ if (win32_key_event(&xip, pch))
+ return (TRUE);
+ if (win32_mouse_event(&xip) || win32_window_event(&xip))
+ return (pch == NULL);
}
- return (TRUE);
}
public lbool win32_kbhit(void)
{
- return win32_kbhit2(FALSE);
+ return win32_kbhit2(NULL);
}
/*
diff --git a/contrib/less/search.c b/contrib/less/search.c
index 0b949f2234e9..be0fd64c93ed 100644
--- a/contrib/less/search.c
+++ b/contrib/less/search.c
@@ -29,6 +29,8 @@ extern int proc_backspace;
extern int proc_return;
extern int ctldisp;
extern int status_col;
+extern int status_line;
+extern int hilite_target;
extern void *ml_search;
extern POSITION start_attnpos;
extern POSITION end_attnpos;
@@ -63,7 +65,6 @@ public POSITION osc8_uri_start = NULL_POSITION;
public POSITION osc8_uri_end = NULL_POSITION;
public POSITION osc8_text_start = NULL_POSITION;
public POSITION osc8_text_end = NULL_POSITION;
-char *osc8_path = NULL;
char *osc8_uri = NULL;
constant char *osc8_search_param = NULL;
#endif
@@ -230,8 +231,13 @@ public int get_cvt_ops(int search_type)
{
int ops = 0;
- if (is_caseless && (!re_handles_caseless || (search_type & SRCH_NO_REGEX)))
+#if RE_HANDLES_CASELESS
+ if (is_caseless && (search_type & SRCH_NO_REGEX))
ops |= CVT_TO_LC;
+#else
+ if (is_caseless)
+ ops |= CVT_TO_LC;
+#endif
if (proc_backspace == OPT_ON || (bs_mode == BS_SPECIAL && proc_backspace == OPT_OFF))
ops |= CVT_BS;
if (proc_return == OPT_ON || (bs_mode != BS_CONTROL && proc_backspace == OPT_OFF))
@@ -253,6 +259,14 @@ static lbool prev_pattern(struct pattern_info *info)
return (info->text != NULL);
}
+/*
+ * Return text of previous search pattern.
+ */
+public constant char* prev_pattern_text(void)
+{
+ return search_info.text;
+}
+
#if HILITE_SEARCH
/*
* Repaint the hilites currently displayed on the screen.
@@ -300,6 +314,27 @@ public void repaint_hilite(lbool on)
#endif
/*
+ * Redraw the jump target line with the attn hilite on or off.
+ */
+public void draw_target_attn(lbool hilite)
+{
+ int sindex;
+ POSITION pos;
+
+ if (!can_goto_line) /* {{ Are there any such terminals any more? }} */
+ return;
+ if (squished)
+ return;
+ sindex = sindex_from_sline(jump_sline);
+ pos = position(sindex);
+ forw_line_seg(pos, chop_line() || hshift > 0, TRUE, FALSE, hilite && status_line, FALSE, NULL, NULL);
+ goto_line(sindex);
+ clear_eol();
+ put_line_hilite(TRUE, hilite);
+ lower_left();
+}
+
+/*
* Clear the attn hilite.
*/
public void clear_attn(void)
@@ -312,6 +347,9 @@ public void clear_attn(void)
POSITION epos;
int moved = 0;
+ if (hilite_target)
+ draw_target_attn(FALSE);
+
if (start_attnpos == NULL_POSITION)
return;
old_start_attnpos = start_attnpos;
@@ -962,7 +1000,7 @@ static void add_hilite(struct hilite_tree *anchor, struct hilite *hl)
*/
static void create_hilites(POSITION linepos, constant char *line, constant char *sp, constant char *ep, int attr, int *chpos)
{
- size_t start_index = ptr_diff(sp, line); /*{{type-issue}}*/
+ size_t start_index = ptr_diff(sp, line);
size_t end_index = ptr_diff(ep, line);
struct hilite hl;
size_t i;
@@ -1818,6 +1856,7 @@ public lbool osc8_click(int sindex, int col)
clickpos = pos_from_col(linepos, col, NULL_POSITION, -1);
if (clickpos == NULL_POSITION)
return FALSE;
+ linepos = beginning_of_line(linepos);
if (forw_raw_line(linepos, &line, &line_len) == NULL_POSITION)
return FALSE;
r = osc8_search_line(SRCH_FORW|SRCH_OSC8, linepos, line, line_len, NULL, clickpos, &matches);
@@ -1849,18 +1888,6 @@ static size_t scheme_length(constant char *uri, size_t uri_len)
}
/*
- * Does a URI contain any dangerous characters?
- */
-static lbool bad_uri(constant char *uri, size_t uri_len)
-{
- size_t i;
- for (i = 0; i < uri_len; i++)
- if (strchr("'\"", uri[i]) != NULL)
- return TRUE;
- return FALSE;
-}
-
-/*
* Re-read the line containing the selected OSC8 link.
*/
static lbool osc8_read_selected(struct osc8_parse_info *op)
@@ -1890,10 +1917,11 @@ public void osc8_open(void)
char env_name[64];
size_t scheme_len;
constant char *handler;
- char *open_cmd;
+ char *cmd;
+ char *uri_q;
size_t uri_len;
- FILE *hf;
- static constant char *env_name_pfx = "LESS_OSC8_";
+ char *p;
+ static constant char *env_name_pfx = "LESS_OSC8_OPEN_";
if (osc8_linepos == NULL_POSITION)
{
@@ -1906,9 +1934,8 @@ public void osc8_open(void)
return;
}
/*
- * Read a "handler" shell cmd from environment variable "LESS_OSC8_scheme".
- * pr_expand the handler cmd (to expand %o -> osc8_path) and execute it.
- * Handler's stdout is an "opener" shell cmd; execute opener to open the link.
+ * Read a "handler" shell cmd from environment variable "LESS_OSC8_OPEN_scheme".
+ * Append the URI to the handler as an argument, and execute it.
*/
uri_len = ptr_diff(op.uri_end, op.uri_start);
scheme_len = scheme_length(op.uri_start, uri_len);
@@ -1923,16 +1950,19 @@ public void osc8_open(void)
free(param);
return;
}
-#if HAVE_POPEN
- if (bad_uri(op.uri_start, uri_len))
+ if (scheme_len == 0)
{
- error("Cannot open link containing quote characters", NULL_PARG);
- return;
+ SNPRINTF1(env_name, sizeof(env_name), "%sNONE", env_name_pfx);
+ } else
+ {
+ SNPRINTF3(env_name, sizeof(env_name), "%s%.*s", env_name_pfx, (int) scheme_len, op.uri_start);
+ for (p = &env_name[strlen(env_name_pfx)]; *p != '\0'; p++)
+ if (ASCII_IS_UPPER(*p))
+ *p = ASCII_TO_LOWER(*p);
}
- SNPRINTF3(env_name, sizeof(env_name), "%s%.*s", env_name_pfx, (int) scheme_len, op.uri_start);
handler = lgetenv(env_name);
if (isnullenv(handler) || strcmp(handler, "-") == 0)
- handler = lgetenv("LESS_OSC8_ANY");
+ handler = lgetenv("LESS_OSC8_OPEN_ANY");
if (isnullenv(handler))
{
PARG parg;
@@ -1940,31 +1970,25 @@ public void osc8_open(void)
error("No handler for \"%s\" link type", &parg);
return;
}
- /* {{ ugly global osc8_path }} */
- osc8_path = saven(op.uri_start, uri_len);
- hf = popen(pr_expand(handler), "r");
- free(osc8_path);
- osc8_path = NULL;
- if (hf == NULL)
- {
- PARG parg;
- parg.p_string = env_name;
- error("Cannot execute protocol handler in %s", &parg);
- return;
- }
- open_cmd = readfd(hf);
- pclose(hf);
- if (strncmp(open_cmd, ":e", 2) == 0)
+ uri_q = shell_quoten(op.uri_start, uri_len);
+ cmd = ecalloc(strlen(handler) + strlen(uri_q) + 2, sizeof(char));
+ sprintf(cmd, "%s %s", handler, uri_q);
+ free(uri_q);
{
- edit(skipsp(&open_cmd[2]));
- } else
- {
- lsystem(open_cmd, "link done");
+ constant char *exec_cmd = cmd;
+ constant char *done_msg = "link done";
+ POSITION save_osc8_linepos = osc8_linepos;
+ if (*exec_cmd == CONTROL('P'))
+ {
+ done_msg = NULL;
+ exec_cmd++;
+ }
+ lsystem(exec_cmd, done_msg);
+ /* lsystem reedits the input file which clears the selected
+ * OSC8 link, so restore it. */
+ osc8_linepos = save_osc8_linepos;
}
- free(open_cmd);
-#else
- error("Cannot open link because your system does not support popen", NULL_PARG);
-#endif /* HAVE_POPEN */
+ free(cmd);
}
/*
@@ -2026,15 +2050,19 @@ public void chg_caseless(void)
* If regex handles caseless, we need to discard
* the pattern which was compiled with the old caseless.
*/
- if (!re_handles_caseless)
- /* We handle caseless, so the pattern doesn't change. */
- return;
+#if !RE_HANDLES_CASELESS
+ /* Less handles caseless, so the pattern doesn't change. */
+ return;
+#endif
}
/*
* Regenerate the pattern using the new state.
*/
- clear_pattern(&search_info);
- (void) hist_pattern(search_info.search_type);
+ if (prev_pattern(&search_info))
+ {
+ clear_pattern(&search_info);
+ (void) hist_pattern(search_info.search_type);
+ }
}
/*
diff --git a/contrib/less/tags.c b/contrib/less/tags.c
index 7f5d1b2976cd..e47d7a15265c 100644
--- a/contrib/less/tags.c
+++ b/contrib/less/tags.c
@@ -321,8 +321,14 @@ static enum tag_result findctag(constant char *tag)
* First see if it is a line number.
*/
tagendline = FALSE;
- if (getnum(&p, NULL, FALSE, &n))
+ if (getnum(&p, NULL, FALSE, &n)) {
+ /*
+ * Line numbers start from 1.
+ */
+ if (n == 0)
+ continue;
taglinenum = n;
+ }
else
{
/*
diff --git a/contrib/less/ttyin.c b/contrib/less/ttyin.c
index 678324a5090c..9474ca9cc477 100644
--- a/contrib/less/ttyin.c
+++ b/contrib/less/ttyin.c
@@ -274,5 +274,5 @@ public int getchr(void)
c = '\340';
} while (result != 1);
- return (c & 0xFF);
+ return (unsigned char) c;
}
diff --git a/contrib/less/version.c b/contrib/less/version.c
index b3d83c1ca2fc..5d03b1350218 100644
--- a/contrib/less/version.c
+++ b/contrib/less/version.c
@@ -1077,7 +1077,40 @@ v691 1/10/26 Fix mingw build.
v692 1/29/26 Fix memory leak in setupterm; clear termio ICRNL|INLCR;
fix bug with negative value in -z; fix bug with empty
terminfo caps; make lesstest ignore nl_langinfo;
- revert HOME/END to g/G; add keys & cmds to lesskey parser.
+ revert HOME/END to g/G; add keys & cmds to lesskey parser.
+v693 2/14/26 Fix binary file with redirected stdout; fix error messages
+ with redirected stdout; fix buffer overrun with malformed
+ lesskey file; fix bad scrolling past EOF; fix command
+ parser bugs; fix -D bug when no color specified; add -DT;
+ make -w/-W hilite on backward movement; show pattern in
+ "not found" message.
+v694 3/24/26 | cmd pipes 1 line if mark is top line; allow numeric arg
+ on m/M cmds; support POSIX char classes with built-in regex;
+ don't retry read after error; fix crash with bad tags file;
+ add --past-eof; add --emouse; fix restore mark if not at top;
+ don't save cleared marks.
+v695 4/3/26 Fix build if tparm doesn't use varargs; fix prompt overflow
+ with & filtering; fix hilite when change -i with & filtering;
+ add --end-prompt; install lessecho in libexecdir; change mark
+ cmds to take file line number not screen line; add numeric
+ arg to ' cmd; add %O and ?o to prompts; fix
+ --show-preproc-error with some shells.
+v696 4/3/26 Doc.
+v697 4/19/26 Fix bug using PCRE2 with more than 5 parentheses; discard
+ pending keys when interrupt with ^X; fix bad display with
+ LESS_LINES.
+v698 4/23/26 Fix bad display after message in narrow terminal; replace
+ LESS_OSC8_xxx with LESS_OSC8_OPEN_xxx; add \kpe to lesskey.
+v699 4/23/26 Add less-osc8-*.bash files to distribution.
+v700 4/30/26 Add \kpe to lesskey; improve less-osc8-* scripts.
+v701 5/13/26 Replace less-osc-*.bash with less-osc8-open.sh; don't init
+ terminal if stdout is not tty; fix bug clicking OSC 8 link
+ in a long line; fix misread chars on Windows; remove %O.
+v702 5/22/26 Doc.
+v703 5/30/26 Fix handling unsafe man options in OSC 8 link; fix unsafe
+ handling of unterminated OSC seq; shell-escape expansion
+ of % and #.
+v704 5/30/26 Fix uninstall target in Makefile.
*/
-char version[] = "692";
+char version[] = "704";
diff --git a/usr.bin/less/Makefile b/usr.bin/less/Makefile
index bf0651eec728..d54dca845a14 100644
--- a/usr.bin/less/Makefile
+++ b/usr.bin/less/Makefile
@@ -7,7 +7,7 @@ SRCS= main.c screen.c brac.c ch.c charset.c cmdbuf.c command.c cvt.c \
line.c linenum.c lsystem.c mark.c optfunc.c option.c \
opttbl.c os.c output.c pattern.c position.c prompt.c search.c \
signal.c tags.c ttyin.c version.c xbuf.c
-SCRIPTS=lesspipe.sh zless.sh
+SCRIPTS=lesspipe.sh zless.sh less-osc8-open.sh
SCRIPTSNAME_lesspipe.sh=lesspipe.sh
LIBADD= tinfow
LINKS= ${BINDIR}/less ${BINDIR}/more \
diff --git a/usr.bin/less/defines.h b/usr.bin/less/defines.h
index a051790d0f49..06a6d03a077d 100644
--- a/usr.bin/less/defines.h
+++ b/usr.bin/less/defines.h
@@ -414,6 +414,18 @@
/* Define HAVE_TIME_T if your system supports the "time_t" type. */
#define HAVE_TIME_T 1
+/* Define HAVE_TPARM2 if your terminfo library supports tparm with 2
+ parameters. */
+#define HAVE_TPARM2 1
+
+/* Define HAVE_TPARM8 if your terminfo library supports tparm with 8
+ parameters. */
+#define HAVE_TPARM8 1
+
+/* Define HAVE_TPARM9 if your terminfo library supports tparm with 9
+ parameters. */
+#define HAVE_TPARM9 1
+
/* Define to 1 if you have the 'ttyname' function. */
#define HAVE_TTYNAME 1