git: 7cc6a5d46484 - releng/14.5 - MFV: less v704.

From: Colin Percival <cperciva_at_FreeBSD.org>
Date: Tue, 18 Aug 2026 20:18:10 UTC
The branch releng/14.5 has been updated by cperciva:

URL: https://cgit.FreeBSD.org/src/commit/?id=7cc6a5d46484ef5e8f5d70c7e6170dcd90981b96

commit 7cc6a5d46484ef5e8f5d70c7e6170dcd90981b96
Author:     Xin LI <delphij@FreeBSD.org>
AuthorDate: 2026-08-15 05:55:38 +0000
Commit:     Colin Percival <cperciva@FreeBSD.org>
CommitDate: 2026-08-18 20:17:56 +0000

    MFV: less v704.
    
    (cherry picked from commit fa0dc4f0f96a1b77d4be7bcdbf965897cda14521)
    (cherry picked from commit 86aea5b4db379327f02ca547575105788bfbe807)
    
    Approved by:    re (cperciva)
---
 contrib/less/NEWS              | 126 ++++++++++++++++
 contrib/less/README            |   8 -
 contrib/less/ch.c              |  10 +-
 contrib/less/charset.c         |  25 +--
 contrib/less/charset.h         |   2 +
 contrib/less/cmd.h             |   3 +
 contrib/less/cmdbuf.c          |   6 +-
 contrib/less/command.c         | 186 ++++++++++++++++-------
 contrib/less/decode.c          | 334 ++++++++++++++++++++++++++++++-----------
 contrib/less/edit.c            |  42 ++++--
 contrib/less/filename.c        |  20 +--
 contrib/less/forwback.c        |  53 +++++--
 contrib/less/funcs.h           |  41 +++--
 contrib/less/help.c            |  28 ++--
 contrib/less/input.c           |  12 +-
 contrib/less/jump.c            |  31 ++--
 contrib/less/less-osc8-open.sh |  64 ++++++++
 contrib/less/less.h            |  44 ++++--
 contrib/less/less.hlp          |  26 ++--
 contrib/less/less.nro          | 331 ++++++++++++++++++++++++++--------------
 contrib/less/lessecho.nro      |   2 +-
 contrib/less/lesskey.nro       |   3 +-
 contrib/less/lesskey_parse.c   |   2 +
 contrib/less/line.c            |  62 ++++----
 contrib/less/lsystem.c         |   2 +-
 contrib/less/main.c            |  73 ++-------
 contrib/less/mark.c            |  50 ++++--
 contrib/less/optfunc.c         | 200 ++++++++++++++++++++----
 contrib/less/opttbl.c          |  60 ++++++--
 contrib/less/os.c              |  45 +++---
 contrib/less/output.c          |  68 +++++----
 contrib/less/pattern.c         |   8 +-
 contrib/less/pattern.h         |  10 +-
 contrib/less/position.c        |  82 ++++++----
 contrib/less/prompt.c          |  33 ++--
 contrib/less/regexp.c          | 206 +++++++++++++++++--------
 contrib/less/regexp.h          |   5 +-
 contrib/less/screen.c          | 105 +++++++++----
 contrib/less/search.c          | 140 ++++++++++-------
 contrib/less/tags.c            |   8 +-
 contrib/less/ttyin.c           |   2 +-
 contrib/less/version.c         |  37 ++++-
 usr.bin/less/Makefile          |   2 +-
 usr.bin/less/defines.h         |  12 ++
 44 files changed, 1845 insertions(+), 764 deletions(-)

diff --git a/contrib/less/NEWS b/contrib/less/NEWS
index 0f767aaeb059..a031353acf46 100644
--- a/contrib/less/NEWS
+++ b/contrib/less/NEWS
@@ -9,6 +9,132 @@
   Report bugs, suggestions or comments at 
   https://github.com/gwsw/less/issues.
 
+======================================================================
+
+	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
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);
 }
 
@@ -850,12 +855,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.
 	 */
@@ -884,12 +883,47 @@ 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;
*** 4363 LINES SKIPPED ***