git: 054ae5e7b465 - stable/14 - MFV: less v685.

From: Xin LI <delphij_at_FreeBSD.org>
Date: Thu, 13 Nov 2025 04:50:20 UTC
The branch stable/14 has been updated by delphij:

URL: https://cgit.FreeBSD.org/src/commit/?id=054ae5e7b465a32079a5ec8a9b943150b8da3212

commit 054ae5e7b465a32079a5ec8a9b943150b8da3212
Author:     Xin LI <delphij@FreeBSD.org>
AuthorDate: 2025-11-10 08:55:06 +0000
Commit:     Xin LI <delphij@FreeBSD.org>
CommitDate: 2025-11-13 04:49:52 +0000

    MFV: less v685.
    
    (cherry picked from commit d5cb458b4b58b0f0b3c058a32439f232fd5455ca)
---
 contrib/less/NEWS         |  39 ++++++++++++++++
 contrib/less/ch.c         |   4 --
 contrib/less/charset.c    |  48 +++++++++++++++++---
 contrib/less/command.c    |  40 +++++++++++++++++
 contrib/less/compose.uni  |   4 +-
 contrib/less/decode.c     |  11 +++--
 contrib/less/edit.c       |  10 +++--
 contrib/less/fmt.uni      |   6 +--
 contrib/less/forwback.c   |   2 +-
 contrib/less/funcs.h      |  12 ++++-
 contrib/less/help.c       |   2 +-
 contrib/less/input.c      |  72 +++++++++++++++++-------------
 contrib/less/less.h       |   5 ++-
 contrib/less/less.nro     | 110 ++++++++++++++++++++++++++++++++++++----------
 contrib/less/lessecho.nro |   6 +--
 contrib/less/lesskey.nro  |  18 +++++---
 contrib/less/lglob.h      |   2 +-
 contrib/less/line.c       | 106 +++++++++++++++++++++++++++++++++++++++-----
 contrib/less/lsystem.c    |   2 +-
 contrib/less/main.c       |  41 ++++++++++++-----
 contrib/less/mkutable     |  21 ++++++++-
 contrib/less/omit.uni     |   7 +++
 contrib/less/optfunc.c    |  18 ++++++++
 contrib/less/opttbl.c     |   6 +++
 contrib/less/os.c         |  51 ++++++++++++++++++---
 contrib/less/pattern.c    |   7 ++-
 contrib/less/screen.c     |  30 +++++++++++++
 contrib/less/search.c     |  12 +++--
 contrib/less/ubin.uni     |   2 +-
 contrib/less/version.c    |  14 +++++-
 contrib/less/wide.uni     |   8 ++--
 contrib/less/xbuf.c       |   2 +-
 usr.bin/less/defines.h    |  18 ++++++--
 33 files changed, 596 insertions(+), 140 deletions(-)

diff --git a/contrib/less/NEWS b/contrib/less/NEWS
index cdc8196a5f16..442fe21e406a 100644
--- a/contrib/less/NEWS
+++ b/contrib/less/NEWS
@@ -9,6 +9,45 @@
   Report bugs, suggestions or comments at 
   https://github.com/gwsw/less/issues.
 
+======================================================================
+
+	Major changes between "less" versions 679 and 685
+
+* Add --cmd option (github #624).
+
+* Add LESS_TERMCAP_SUSPEND and LESS_TERMCAP_RESUME (github #654).
+
+* Change --incsearch so that after typing each character of the pattern,
+  the search begins at the position where the search command was invoked,
+  not the current position (github #640).
+
+* Allow mixing of option arguments and filename arguments on the
+  command line unless POSIXLY_CORRECT is set (github #653).
+
+* Don't output U+00AD and U+200D, and fix some bugs handling emoji
+  modifier characters (github #637).
+
+* Fix hang if a search using ^S modifier matches empty string (github #634).
+
+* Fix bug using -g and -J (github #636).
+
+* Fix bug when pasting a search pattern while --incsearch is active
+  (github #635).
+
+* Fix bug in Windows build when autorepeating a search pattern (github #639).
+
+* Fix lesskey bug using #stop directive.
+
+* Fix lesskey bug using "invalid" action (github #643).
+
+* Fix bug causing file to appear to end prematurely if an input command
+  was received during a file read (github #649).
+
+* Fix performance issue in & filtering (github #638). Problem was introduced
+  in da2a9ecdf16beb642d0c030e35f0351c5f2e5a12 and released in less-673.
+
+* Fix some problems reported by valgrind (github #659, github #660, github #661).
+
 ======================================================================
 
 	Major changes between "less" versions 678 and 679
diff --git a/contrib/less/ch.c b/contrib/less/ch.c
index 870028c73a23..fb7572e7f88a 100644
--- a/contrib/less/ch.c
+++ b/contrib/less/ch.c
@@ -283,11 +283,7 @@ static int ch_get(void)
 
 		read_again = FALSE;
 		if (n == READ_INTR)
-		{
-			if (ch_flags & CH_CANSEEK)
-				ch_fsize = pos;
 			return (EOI);
-		}
 		if (n == READ_AGAIN)
 		{
 			read_again = TRUE;
diff --git a/contrib/less/charset.c b/contrib/less/charset.c
index 5e5df2a4e60f..0f62739bc88d 100644
--- a/contrib/less/charset.c
+++ b/contrib/less/charset.c
@@ -128,10 +128,12 @@ static struct xbuffer user_wide_array;
 static struct xbuffer user_ubin_array;
 static struct xbuffer user_compose_array;
 static struct xbuffer user_prt_array;
+static struct xbuffer user_omit_array;
 static struct wchar_range_table user_wide_table;
 static struct wchar_range_table user_ubin_table;
 static struct wchar_range_table user_compose_table;
 static struct wchar_range_table user_prt_table;
+static struct wchar_range_table user_omit_table;
 
 /*
  * Set a wchar_range_table to the table in an xbuffer.
@@ -179,6 +181,7 @@ static void ichardef_utf(constant char *s)
 	xbuf_init(&user_ubin_array);
 	xbuf_init(&user_compose_array);
 	xbuf_init(&user_prt_array);
+	xbuf_init(&user_omit_array);
 
 	if (s != NULL)
 	{
@@ -204,6 +207,9 @@ static void ichardef_utf(constant char *s)
 			case 'c':
 				xbuf_add_data(&user_compose_array, (unsigned char *) &range, sizeof(range));
 				break;
+			case 'd':
+				xbuf_add_data(&user_omit_array, (unsigned char *) &range, sizeof(range));
+				break;
 			case 'w':
 				xbuf_add_data(&user_wide_array, (unsigned char *) &range, sizeof(range));
 				xbuf_add_data(&user_prt_array, (unsigned char *) &range, sizeof(range));
@@ -225,6 +231,7 @@ static void ichardef_utf(constant char *s)
 	wchar_range_table_set(&user_ubin_table, &user_ubin_array);
 	wchar_range_table_set(&user_compose_table, &user_compose_array);
 	wchar_range_table_set(&user_prt_table, &user_prt_array);
+	wchar_range_table_set(&user_omit_table, &user_omit_array);
 }
 
 /*
@@ -554,7 +561,7 @@ public constant char * prutfchar(LWCHAR ch)
 			SNPRINTF1(buf, sizeof(buf), "^%c", ((char) ch) ^ 0100);
 		else
 			SNPRINTF1(buf, sizeof(buf), binfmt, (char) ch);
-	} else if (is_ubin_char(ch))
+	} else if (is_ubin_char(ch) || is_omit_char(ch))
 	{
 		SNPRINTF1(buf, sizeof(buf), utfbinfmt, ch);
 	} else
@@ -827,6 +834,10 @@ DECLARE_RANGE_TABLE_START(fmt)
 #include "fmt.uni"
 DECLARE_RANGE_TABLE_END(fmt)
 
+DECLARE_RANGE_TABLE_START(omit)
+#include "omit.uni"
+DECLARE_RANGE_TABLE_END(omit)
+
 /* comb_table is special pairs, not ranges. */
 static struct wchar_range comb_table[] = {
 	{0x0644,0x0622}, {0x0644,0x0623}, {0x0644,0x0625}, {0x0644,0x0627},
@@ -856,6 +867,17 @@ static lbool is_in_table(LWCHAR ch, struct wchar_range_table *table)
 	return FALSE;
 }
 
+/*
+ * Is a character in none of a set of specified user tables?
+ */
+static lbool not_user_defined(LWCHAR ch, struct wchar_range_table *tbl1, struct wchar_range_table *tbl2, struct wchar_range_table *tbl3)
+{
+	if (is_in_table(ch, tbl1)) return FALSE;
+	if (is_in_table(ch, tbl2)) return FALSE;
+	if (is_in_table(ch, tbl3)) return FALSE;
+	return TRUE;
+}
+
 /*
  * Is a character a UTF-8 composing character?
  * If a composing character follows any char, the two combine into one glyph.
@@ -864,8 +886,9 @@ public lbool is_composing_char(LWCHAR ch)
 {
 	if (is_in_table(ch, &user_prt_table)) return FALSE;
 	return is_in_table(ch, &user_compose_table) ||
-	       is_in_table(ch, &compose_table) ||
-	       (bs_mode != BS_CONTROL && is_in_table(ch, &fmt_table));
+	       (is_in_table(ch, &compose_table) ||
+	       (bs_mode != BS_CONTROL && is_in_table(ch, &fmt_table) &&
+            not_user_defined(ch, &user_prt_table, &user_ubin_table, &user_omit_table)));
 }
 
 /*
@@ -875,8 +898,9 @@ public lbool is_ubin_char(LWCHAR ch)
 {
 	if (is_in_table(ch, &user_prt_table)) return FALSE;
 	return is_in_table(ch, &user_ubin_table) ||
-	       is_in_table(ch, &ubin_table) ||
-	       (bs_mode == BS_CONTROL && is_in_table(ch, &fmt_table));
+	       (is_in_table(ch, &ubin_table) ||
+	       (bs_mode == BS_CONTROL && is_in_table(ch, &fmt_table) &&
+            not_user_defined(ch, &user_prt_table, &user_compose_table, &user_omit_table)));
 }
 
 /*
@@ -885,7 +909,18 @@ public lbool is_ubin_char(LWCHAR ch)
 public lbool is_wide_char(LWCHAR ch)
 {
 	return is_in_table(ch, &user_wide_table) ||
-	       is_in_table(ch, &wide_table);
+	       (is_in_table(ch, &wide_table) &&
+            not_user_defined(ch, &user_compose_table, &user_ubin_table, &user_omit_table));
+}
+
+/*
+ * Is this an omittable character?
+ */
+public lbool is_omit_char(LWCHAR ch)
+{
+	return is_in_table(ch, &user_omit_table) ||
+	       (is_in_table(ch, &omit_table) &&
+            not_user_defined(ch, &user_prt_table, &user_compose_table, &user_ubin_table));
 }
 
 /*
@@ -905,4 +940,3 @@ public lbool is_combining_char(LWCHAR ch1, LWCHAR ch2)
 	}
 	return FALSE;
 }
-
diff --git a/contrib/less/command.c b/contrib/less/command.c
index 3ec1f9b48358..390385547385 100644
--- a/contrib/less/command.c
+++ b/contrib/less/command.c
@@ -49,10 +49,13 @@ extern void *ml_examine;
 extern int wheel_lines;
 extern int def_search_type;
 extern lbool search_wrapped;
+extern lbool no_poll;
 extern int no_paste;
 extern lbool pasting;
 extern int no_edit_warn;
 extern POSITION soft_eof;
+extern POSITION search_incr_start;
+extern char *first_cmd_at_prompt;
 #if SHELL_ESCAPE || PIPEC
 extern void *ml_shell;
 #endif
@@ -90,6 +93,8 @@ static int save_proc_backspace;
 static int screen_trashed_value = 0;
 static lbool literal_char = FALSE;
 static lbool ignoring_input = FALSE;
+static struct scrpos search_incr_pos = { NULL_POSITION, 0 };
+static int search_incr_hshift;
 #if HAVE_TIME
 static time_type ignoring_input_time;
 #endif
@@ -209,6 +214,13 @@ static void mca_search1(void)
 
 static void mca_search(void)
 {
+	if (incr_search)
+	{
+		/* Remember where the incremental search started. */
+		get_scrpos(&search_incr_pos, TOP);
+		search_incr_start = search_pos(search_type);
+		search_incr_hshift = hshift;
+	}
 	mca_search1();
 	set_mlist(ml_search, 0);
 }
@@ -747,6 +759,9 @@ static int mca_char(char c)
 			constant char *pattern = get_cmdbuf();
 			if (pattern == NULL)
 				return (MCA_MORE);
+			/* Defer searching if more chars of the pattern are available. */
+			if (ttyin_ready())
+				return (MCA_MORE);
 			/*
 			 * Must save updown_match because mca_search
 			 * reinits it. That breaks history scrolling.
@@ -758,11 +773,24 @@ static int mca_char(char c)
 			{
 				/* User has backspaced to an empty pattern. */
 				undo_search(1);
+				hshift = search_incr_hshift;
+				jump_loc(search_incr_pos.pos, search_incr_pos.ln);
 			} else
 			{
+				/*
+				 * Suppress tty polling while searching.
+				 * This avoids a problem where tty input
+				 * can cause the search to be interrupted.
+				 */
+				no_poll = TRUE;
 				if (search(st | SRCH_INCR, pattern, 1) != 0)
+				{
 					/* No match, invalid pattern, etc. */
 					undo_search(1);
+					hshift = search_incr_hshift;
+					jump_loc(search_incr_pos.pos, search_incr_pos.ln);
+				}
+				no_poll = FALSE;
 			}
 			/* Redraw the search prompt and search string. */
 			if (is_screen_trashed() || !full_screen)
@@ -795,6 +823,7 @@ static void clear_buffers(void)
 #if HILITE_SEARCH
 	clr_hilite();
 #endif
+	set_line_contig_pos(NULL_POSITION);
 }
 
 public void screen_trashed_num(int trashed)
@@ -889,6 +918,12 @@ static void prompt(void)
 	    next_ifile(curr_ifile) == NULL_IFILE)
 		quit(QUIT_OK);
 	quit_if_one_screen = FALSE; /* only get one chance at this */
+	if (first_cmd_at_prompt != NULL)
+	{
+		ungetsc(first_cmd_at_prompt);
+		first_cmd_at_prompt = NULL;
+		return;
+	}
 
 #if MSDOS_COMPILER==WIN32C
 	/* 
@@ -962,6 +997,7 @@ static void prompt(void)
 		put_line(FALSE);
 	}
 	clear_eol();
+	resume_screen();
 }
 
 /*
@@ -2261,6 +2297,7 @@ public void commands(void)
 			pos_rehead();
 			hshift -= (int) number;
 			screen_trashed();
+			cmd_exec();
 			break;
 
 		case A_RSHIFT:
@@ -2274,6 +2311,7 @@ public void commands(void)
 			pos_rehead();
 			hshift += (int) number;
 			screen_trashed();
+			cmd_exec();
 			break;
 
 		case A_LLSHIFT:
@@ -2283,6 +2321,7 @@ public void commands(void)
 			pos_rehead();
 			hshift = 0;
 			screen_trashed();
+			cmd_exec();
 			break;
 
 		case A_RRSHIFT:
@@ -2292,6 +2331,7 @@ public void commands(void)
 			pos_rehead();
 			hshift = rrshift();
 			screen_trashed();
+			cmd_exec();
 			break;
 
 		case A_PREFIX:
diff --git a/contrib/less/compose.uni b/contrib/less/compose.uni
index 0875a8dceec1..6b4458efc4b4 100644
--- a/contrib/less/compose.uni
+++ b/contrib/less/compose.uni
@@ -1,4 +1,4 @@
-/* Generated by "./mkutable -f2 Mn Me -- unicode/UnicodeData.txt" on Oct  1 18:10:07 GMT 2024 */
+/* Generated by "./mkutable -f2 Mn Me -- unicode/UnicodeData.txt" on Aug 11  0:27:25 GMT 2025 */
 	{ 0x0300, 0x036f }, /* Mn */
 	{ 0x0483, 0x0487 }, /* Mn */
 	{ 0x0488, 0x0489 }, /* Me */
@@ -217,7 +217,6 @@
 	{ 0xd7b0, 0xd7c6 }, /* Mn */
 	{ 0xd7cb, 0xd7fb }, /* Mn */
 	{ 0xfb1e, 0xfb1e }, /* Mn */
-	{ 0xfe00, 0xfe0f }, /* Mn */
 	{ 0xfe20, 0xfe2f }, /* Mn */
 	{ 0x101fd, 0x101fd }, /* Mn */
 	{ 0x102e0, 0x102e0 }, /* Mn */
@@ -363,4 +362,3 @@
 	{ 0x1e5ee, 0x1e5ef }, /* Mn */
 	{ 0x1e8d0, 0x1e8d6 }, /* Mn */
 	{ 0x1e944, 0x1e94a }, /* Mn */
-	{ 0xe0100, 0xe01ef }, /* Mn */
diff --git a/contrib/less/decode.c b/contrib/less/decode.c
index 8e451d1810c9..1d80d126c207 100644
--- a/contrib/less/decode.c
+++ b/contrib/less/decode.c
@@ -483,12 +483,12 @@ public void add_ecmd_table(unsigned char *buf, size_t len)
 /*
  * Add an environment variable table.
  */
-static void add_var_table(struct tablelist **tlist, unsigned char *buf, size_t len)
+static void add_var_table(struct tablelist **tlist, mutable unsigned char *buf, size_t len)
 {
 	struct xbuffer xbuf;
 
 	xbuf_init(&xbuf);
-	expand_evars((char*)buf, len, &xbuf); /*{{unsigned-issue}}*/
+	expand_evars((mutable char*)buf, len, &xbuf); /*{{unsigned-issue}}*/
 	/* {{ We leak the table in buf. expand_evars scribbled in it so it's useless anyway. }} */
 	if (add_cmd_table(tlist, xbuf.data, xbuf.end) < 0)
 		error("Warning: environment variables from lesskey file unavailable", NULL_PARG);
@@ -749,7 +749,8 @@ static int cmd_search(constant char *cmd, constant unsigned char *table, constan
 			if (match == cmdlen) /* (last chars of) cmd matches this table entry */
 			{
 				action = taction;
-				*extra = textra;
+				if (extra != NULL)
+					*extra = textra;
 			} else if (match > 0 && action == A_INVALID) /* cmd is a prefix of this table entry */
 			{
 				action = A_PREFIX;
@@ -780,13 +781,11 @@ static int cmd_decode(struct tablelist *tlist, constant char *cmd, constant char
 	for (t = tlist;  t != NULL;  t = t->t_next)
 	{
 		constant unsigned char *tsp;
-		size_t mlen;
+		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_UINVALID)
-				taction = A_INVALID;
 			if (taction != A_INVALID)
 			{
 				*sp = (constant char *) tsp;
diff --git a/contrib/less/edit.c b/contrib/less/edit.c
index 0254584bf211..1816e6f9f9bc 100644
--- a/contrib/less/edit.c
+++ b/contrib/less/edit.c
@@ -113,9 +113,7 @@ public constant char * forw_textlist(struct textlist *tlist, constant char *prev
 		s = tlist->string;
 	else
 		s = prev + strlen(prev);
-	if (s >= tlist->endstring)
-		return (NULL);
-	while (*s == '\0')
+	while (s < tlist->endstring && *s == '\0')
 		s++;
 	if (s >= tlist->endstring)
 		return (NULL);
@@ -306,7 +304,11 @@ static void close_pipe(FILE *pipefd)
 	if (WIFSIGNALED(status))
 	{
 		int sig = WTERMSIG(status);
-		if (sig != SIGPIPE || ch_length() != NULL_POSITION)
+		if (
+#ifdef SIGPIPE
+			sig != SIGPIPE || 
+#endif
+			ch_length() != NULL_POSITION)
 		{
 			parg.p_string = signal_message(sig);
 			error("Input preprocessor terminated: %s", &parg);
diff --git a/contrib/less/fmt.uni b/contrib/less/fmt.uni
index 91cfc3e91e61..c861e1908360 100644
--- a/contrib/less/fmt.uni
+++ b/contrib/less/fmt.uni
@@ -1,5 +1,4 @@
-/* Generated by "./mkutable -f2 Cf -- unicode/UnicodeData.txt" on Oct  1 18:10:07 GMT 2024 */
-	{ 0x00ad, 0x00ad }, /* Cf */
+/* Generated by "./mkutable -f2 Cf -- unicode/UnicodeData.txt" on Jul 27 19:38:50 GMT 2025 */
 	{ 0x0600, 0x0605 }, /* Cf */
 	{ 0x061c, 0x061c }, /* Cf */
 	{ 0x06dd, 0x06dd }, /* Cf */
@@ -7,7 +6,8 @@
 	{ 0x0890, 0x0891 }, /* Cf */
 	{ 0x08e2, 0x08e2 }, /* Cf */
 	{ 0x180e, 0x180e }, /* Cf */
-	{ 0x200b, 0x200f }, /* Cf */
+	{ 0x200b, 0x200c }, /* Cf */
+	{ 0x200e, 0x200f }, /* Cf */
 	{ 0x202a, 0x202e }, /* Cf */
 	{ 0x2060, 0x2064 }, /* Cf */
 	{ 0x2066, 0x206f }, /* Cf */
diff --git a/contrib/less/forwback.c b/contrib/less/forwback.c
index 300e669f9371..e77c0d4ce198 100644
--- a/contrib/less/forwback.c
+++ b/contrib/less/forwback.c
@@ -359,7 +359,7 @@ public void forw(int n, POSITION pos, lbool force, lbool only_last, lbool to_new
 	}
 	if (!first_line)
 		add_forw_pos(pos, FALSE);
-	if (nlines == 0 && !ignore_eoi)
+	if (nlines == 0 && !ignore_eoi && !ABORT_SIGS())
 		eof_bell();
 	else if (do_repaint)
 		repaint();
diff --git a/contrib/less/funcs.h b/contrib/less/funcs.h
index b001a5c31902..11605acc8b3b 100644
--- a/contrib/less/funcs.h
+++ b/contrib/less/funcs.h
@@ -14,6 +14,8 @@ public void init_win_colors(void);
 public void get_term(void);
 public void init_mouse(void);
 public void deinit_mouse(void);
+public void suspend_screen(void);
+public void resume_screen(void);
 public void init(void);
 public void deinit(void);
 public int interactive(void);
@@ -83,6 +85,7 @@ public LWCHAR step_char(char **pp, signed int dir, constant char *limit);
 public lbool is_composing_char(LWCHAR ch);
 public lbool is_ubin_char(LWCHAR ch);
 public lbool is_wide_char(LWCHAR ch);
+public lbool is_omit_char(LWCHAR ch);
 public lbool is_combining_char(LWCHAR ch1, LWCHAR ch2);
 public void cmd_reset(void);
 public void clear_cmd(void);
@@ -228,7 +231,9 @@ public void jump_loc(POSITION pos, int sline);
 public void init_line(void);
 public lbool is_ascii_char(LWCHAR ch);
 public POSITION line_position(void);
-public void prewind(void);
+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 void pshift_all(void);
@@ -314,6 +319,7 @@ 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);
 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 int next_cnum(constant char **sp, constant char *printopt, constant char *errmsg, lbool *errp);
 public void opt_header(int type, constant char *s);
@@ -343,6 +349,7 @@ public struct loption * findopt(int c);
 public struct loption * findopt_name(constant char **p_optname, constant char **p_oname, lbool *p_ambig);
 public char * findopts_name(constant char *pfx);
 public void init_poll(void);
+public lbool ttyin_ready(void);
 public int supports_ctrl_x(void);
 public ssize_t iread(int fd, unsigned char *buf, size_t len);
 public int iopen(constant char *filename, int flags);
@@ -403,6 +410,7 @@ public lbool is_filtered(POSITION pos);
 public POSITION next_unfiltered(POSITION pos);
 public int is_hilited_attr(POSITION pos, POSITION epos, int nohide, int *p_matches);
 public void chg_hilite(void);
+public POSITION search_pos(int search_type);
 public void osc8_search(int search_type, constant char *param, int matches);
 public lbool osc8_click(int sindex, int col);
 public void osc8_open(void);
@@ -439,7 +447,7 @@ public void xbuf_add_byte(struct xbuffer *xbuf, unsigned char b);
 public void xbuf_add_char(struct xbuffer *xbuf, char c);
 public void xbuf_add_data(struct xbuffer *xbuf, constant unsigned char *data, size_t len);
 public int xbuf_pop(struct xbuffer *buf);
-public void xbuf_set(struct xbuffer *dst, struct xbuffer *src);
+public void xbuf_set(struct xbuffer *dst, constant struct xbuffer *src);
 public constant char * xbuf_char_data(constant struct xbuffer *xbuf);
 public lbool help_ckd_add(void *r, uintmax a, uintmax b, int rsize, int rsigned);
 public lbool help_ckd_mul(void *r, uintmax a, uintmax b, int rsize, int rsigned);
diff --git a/contrib/less/help.c b/contrib/less/help.c
index 5d8ba9a1b0fe..ed9465ad9560 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 19:46 on 2025/5/28 */
+/* This file was generated by mkhelp.pl from less.hlp at 18:02 on 2025/10/4 */
 #include "less.h"
 constant char helpdata[] = {
 '\n',
diff --git a/contrib/less/input.c b/contrib/less/input.c
index c2f7a28c2c58..dda039b21a27 100644
--- a/contrib/less/input.c
+++ b/contrib/less/input.c
@@ -96,6 +96,8 @@ public POSITION forw_line_seg(POSITION curr_pos, lbool skipeol, lbool rscroll, l
 
 	if (p_linepos != NULL)
 		*p_linepos = NULL_POSITION;
+	if (p_newline != NULL)
+		*p_newline = TRUE;
 
 get_forw_line:
 	if (curr_pos == NULL_POSITION)
@@ -104,7 +106,7 @@ get_forw_line:
 		return (NULL_POSITION);
 	}
 #if HILITE_SEARCH
-	if (hilite_search == OPT_ONPLUS || is_filtering() || status_col)
+	if (hilite_search == OPT_ONPLUS || is_filtering() || (status_col && hilite_search != OPT_ON))
 	{
 		/*
 		 * If we are ignoring EOI (command F), only prepare
@@ -142,39 +144,48 @@ get_forw_line:
 	/*
 	 * Read forward again to the position we should start at.
 	 */
-	prewind();
-	plinestart(base_pos);
-	(void) ch_seek(base_pos);
-	new_pos = base_pos;
-	while (new_pos < curr_pos)
+	if (is_line_contig_pos(curr_pos))
 	{
-		c = ch_forw_get();
-		if (c == EOI)
-		{
-			null_line();
-			return (NULL_POSITION);
-		}
-		backchars = pappend((char) c, new_pos);
-		new_pos++;
-		if (backchars > 0)
+		prewind(TRUE);
+		plinestart(base_pos);
+		ch_seek(curr_pos);
+		new_pos = curr_pos;
+	} else
+	{
+		prewind(FALSE);
+		plinestart(base_pos);
+		ch_seek(base_pos);
+		new_pos = base_pos;
+		while (new_pos < curr_pos)
 		{
-			pshift_all();
-			if (wordwrap && (c == ' ' || c == '\t'))
+			c = ch_forw_get();
+			if (c == EOI)
 			{
-				do
+				null_line();
+				return (NULL_POSITION);
+			}
+			backchars = pappend((char) c, new_pos);
+			new_pos++;
+			if (backchars > 0)
+			{
+				pshift_all();
+				if (wordwrap && (c == ' ' || c == '\t'))
 				{
-					new_pos++;
-					c = ch_forw_get(); /* {{ what if c == EOI? }} */
-				} while (c == ' ' || c == '\t');
-				backchars = 1;
+					do
+					{
+						new_pos++;
+						c = ch_forw_get(); /* {{ what if c == EOI? }} */
+					} while (c == ' ' || c == '\t');
+					backchars = 1;
+				}
+				new_pos -= backchars;
+				while (--backchars >= 0)
+					(void) ch_back_get();
 			}
-			new_pos -= backchars;
-			while (--backchars >= 0)
-				(void) ch_back_get();
 		}
+		pshift_all();
 	}
 	(void) pflushmbc();
-	pshift_all();
 
 	/*
 	 * Read the first character to display.
@@ -329,6 +340,7 @@ get_forw_line:
 		*p_linepos = curr_pos;
 	if (p_newline != NULL)
 		*p_newline = endline;
+	set_line_contig_pos(endline ? NULL_POSITION : new_pos);
 	return (new_pos);
 }
 
@@ -358,6 +370,8 @@ public POSITION back_line(POSITION curr_pos, lbool *p_newline)
 	lbool skipped_leading;
 
 get_back_line:
+	if (p_newline != NULL)
+		*p_newline = TRUE;
 	if (curr_pos == NULL_POSITION || curr_pos <= ch_zero())
 	{
 		null_line();
@@ -426,7 +440,7 @@ get_back_line:
 	}
 
 #if HILITE_SEARCH
-	if (hilite_search == OPT_ONPLUS || is_filtering() || status_col)
+	if (hilite_search == OPT_ONPLUS || is_filtering() || (status_col && hilite_search != OPT_ON))
 		prep_hilite(base_pos, NULL_POSITION, 1);
 #endif
 
@@ -446,10 +460,8 @@ get_back_line:
 		return (NULL_POSITION);
 	}
 	endline = FALSE;
-	prewind();
+	prewind(FALSE);
 	plinestart(new_pos);
-	if (p_newline != NULL)
-		*p_newline = TRUE;
     loop:
 	wrap_pos = NULL_POSITION;
 	skipped_leading = FALSE;
diff --git a/contrib/less/less.h b/contrib/less/less.h
index 7b2d2c25bfc6..a30693a35a7a 100644
--- a/contrib/less/less.h
+++ b/contrib/less/less.h
@@ -216,7 +216,7 @@ void free();
  * Special types and constants.
  */
 typedef unsigned long LWCHAR;
-#if defined(MINGW) || (defined(_MSC_VER) && _MSC_VER >= 1500)
+#if defined(__MINGW32__) || (defined(_MSC_VER) && _MSC_VER >= 1500)
 typedef long long less_off_t;  /* __int64 */
 typedef struct _stat64 less_stat_t;
 #define less_fstat _fstat64
@@ -435,6 +435,7 @@ typedef enum osc8_state {
 #define AT_ANSI         (1 << 4)  /* Content-supplied "ANSI" escape sequence */
 #define AT_BINARY       (1 << 5)  /* LESS*BINFMT representation */
 #define AT_HILITE       (1 << 6)  /* Internal highlights (e.g., for search) */
+#define AT_PLACEHOLDER  (1 << 7)  /* Placeholder for half of double-wide char */
 
 #define AT_COLOR_SHIFT    8
 #define AT_NUM_COLORS     16
@@ -554,6 +555,8 @@ typedef enum {
 #define ESC             CONTROL('[')
 #define ESCS            "\33"
 #define CSI             ((unsigned char)'\233')
+#define VARSEL_15       ((LWCHAR)0xFE0E)  /* VARIATION SELECTOR 15 */
+#define VARSEL_16       ((LWCHAR)0xFE0F)  /* VARIATION SELECTOR 16 */
 
 #if _OSK_MWC32
 #define LSIGNAL(sig,func)       os9_signal(sig,func)
diff --git a/contrib/less/less.nro b/contrib/less/less.nro
index 25a9869a9c59..ae43e8851d0e 100644
--- a/contrib/less/less.nro
+++ b/contrib/less/less.nro
@@ -1,5 +1,5 @@
 '\" t
-.TH LESS 1 "Version 679: 28 May 2025"
+.TH LESS 1 "Version 685: 04 Oct 2025"
 .SH NAME
 less \- display the contents of a file in a terminal
 .SH SYNOPSIS
@@ -203,8 +203,11 @@ Followed by another single quote, returns to the position at
 which the last "large" movement command was executed.
 Followed by a \(ha or $, jumps to the beginning or end of the
 file respectively.
-Marks are preserved when a new file is examined,
+Marks are preserved when a new file is examined within a single invocation of
+.BR less ,
 so the \(aq command can be used to switch between input files.
+The \-\-save-marks option causes marks to be preserved across different invocations of
+.BR less .
 .IP "\(haX\(haX"
 Same as single quote.
 .IP "ESC-m"
@@ -800,8 +803,9 @@ where the first integer specifies the foreground color and
 the second specifies the background color.
 Each integer is a value between 0 and 255 inclusive which selects
 a "CSI 38;5" color value (see
-.UR https://en.wikipedia.org/wiki/ANSI_escape_code#SGR
-.UE ).
+.nh
+https://en.wikipedia.org/wiki/ANSI_escape_code#SGR).
+.hy
 If either integer is a "-" or is omitted,
 the corresponding color is set to that of normal text.
 .PP
@@ -835,8 +839,9 @@ CHAR_INFO.Attributes
 .hy
 value, between 0 and 15 inclusive
 (see 
-.UR https://learn.microsoft.com/en-us/windows/console/char-info-str
-.UE ).
+.nh
+https://learn.microsoft.com/en-us/windows/console/char-info-str).
+.hy
 
 To avoid confusion, it is recommended that the equivalent letters rather than numbers
 be used after a lowercase color selector on MS-DOS/Windows.
@@ -1213,8 +1218,9 @@ the name of a command compatible with
 .BR global (1),
 and that command is executed to find the tag.
 (See 
-.UR http://www.gnu.org/software/global/global.html
-.UE ).
+.nh
+http://www.gnu.org/software/global/global.html).
+.hy
 The \-t option may also be specified from within
 .B less
 (using the \- command) as a way of examining a new file.
@@ -1328,6 +1334,16 @@ of the screen, starting with a decimal point: \&.5 is half of the
 screen width, \&.3 is three tenths of the screen width, and so on.
 If the number is specified as a fraction, the actual number of
 scroll positions is recalculated if the terminal window is resized.
+.IP "\-\-cmd=\fIcommands\fP
+The specified string is taken to be an initial command to
+.BR less .
+This is similar to specifying "+\fIcommands\fP", except that
+commands specified by \-\-cmd are not executed if
+.B less
+exits immediately due to the use of the \-E or \-F option, while
+commands specified by the \fB+\fP option are executed even if
+.B less
+exits immediately.
 .IP "\-\-exit-follow-on-close"
 When using the "F" command on a pipe,
 .B less
@@ -1583,8 +1599,9 @@ Enables colored text in various places.
 The \-D option can be used to change the colors.
 Colored text works only if the terminal supports 
 ANSI color escape sequences (as defined in 
-.UR https://www.ecma-international.org/publications-and-standards/standards/ecma-48
-.UE ).
+.nh
+https://www.ecma-international.org/publications-and-standards/standards/ecma-48).
+.hy
 .IP "\-\-wheel-lines=\fIn\fP"
 Set the number of lines to scroll when the mouse wheel is scrolled
 and the \-\-mouse or \-\-MOUSE option is in effect.
@@ -1598,6 +1615,9 @@ The default is to wrap at any character.
 A command line argument of "\-\-" marks the end of option arguments.
 Any arguments following this are interpreted as filenames.
 This can be useful when viewing a file whose name begins with a "\-" or "+".
+Otherwise, option arguments and filename arguments can be intermixed;
+that is, option arguments do not need to appear before filename arguments,
+unless the environment variable POSIXLY_CORRECT is set.
 .IP +
 If a command line option begins with \fB+\fP,
 the remainder of that option is taken to be an initial command to
@@ -1613,6 +1633,7 @@ If the option starts with ++, the initial command applies to
 every file being viewed, not just the first one.
 The + command described previously
 may also be used to set (or change) an initial command for every file.
+Also see the \-\-cmd option.
 .
 .SH "LINE EDITING"
 When entering a command line at the bottom of the screen
@@ -1913,7 +1934,7 @@ Again, in this case the dash is not considered to be part of
 the input pipe command.
 .
 .SH "NATIONAL CHARACTER SETS"
-There are three types of characters in the input file:
+There are five types of characters in the input file:
 .IP "normal characters"
 can be displayed directly to the screen.
 .IP "control characters"
@@ -1922,6 +1943,12 @@ in ordinary text files (such as backspace and tab).
 .IP "binary characters"
 should not be displayed directly and are not expected to be found
 in text files.
+.IP "composing characters"
+are not displayed separately, but modify the display of the
+preceding character. (Only when LESSCHARSET is "utf8".)
+.IP "deleted characters"
+are simply deleted from the input and not displayed.
+(Only when LESSCHARSET is "utf8".)
 .PP
 A "character set" is simply a description of which characters are to
 be considered normal, control, and binary.
@@ -2049,7 +2076,7 @@ of how the UTF-8 file is ill-formed.
 .PP
 When the character set is utf-8, in rare cases it may be desirable to
 override the Unicode definition of the type of certain characters.
-For example, characters in a Private Use Area are normally treated as control
+For example, characters in a Private Use Area are normally treated as binary
 characters, but if you are using a custom font with printable characters
 in that range, it may be desirable to tell
 .B less
@@ -2076,6 +2103,8 @@ A wide (2-space) printable character.
 A binary (non-printable) character.
 .IP "c"
 A composing (zero width) character.
+.IP "d"
+A deleted character (deleted from the input and not displayed).
 .RE
 .PP
 For example, setting LESSUTFCHARDEF to
@@ -2085,6 +2114,18 @@ For example, setting LESSUTFCHARDEF to
 .sp
 .fi
 would make all Private Use Area characters be treated as printable.
+.PP
+By default, emoji modifiers, components and variation selectors
+are deleted because many terminals do not display them correctly.
+If you use a terminal which does display some or all of them correctly,
+you can cause to be displayed by setting LESSUTFCHARDEF
+to treat them as composing characters.
+For example, this sets them all to composing characters:
+.nf
+.sp
+	FE00-FE0F:c,1F3FB-1F3FF:c,1F9B0-1F9B3:c,E0100-E01EF:c
+.sp
+.fi
 .SH "PROMPTS"
 The \-P option allows you to tailor the prompt to your preference.
 The string given to the \-P option replaces the specified prompt string.
@@ -2407,8 +2448,8 @@ end character in an ANSI color escape sequence
 (default "0123456789:;[?!"\(aq#%()*+\ ").
 .IP LESSANSIOSCALLOW
 A comma-separated list of OSC types which are output directly to the
-terminal when \-R is in effect.
-By default, only OSC 8 sequences are output directly.
+terminal when \-R is in effect
+(default "8"; that is, only OSC 8 sequences are output directly).
 .IP LESSANSIOSCCHARS
 Characters which may follow an ESC character to mark the start
 of an "OS Command" sequence. 
@@ -2480,9 +2521,7 @@ file. (Not used if "$LESSKEYIN_SYSTEM" exists.)
 List of characters which are considered "metacharacters" by the shell.
 .IP LESSMETAESCAPE
 Prefix which less will add before each metacharacter in a
-command sent to the shell.
-If LESSMETAESCAPE is an empty string, commands containing
-metacharacters will not be passed to the shell.
+command sent to the shell (default "\\").
 .IP LESSOPEN
 Command line to invoke the (optional) input-preprocessor.
 .IP LESSSECURE
@@ -2492,7 +2531,8 @@ See discussion under SECURITY.
 Enables individual features which are normally disabled by LESSSECURE.
 See discussion under SECURITY.
 .IP LESSSEPARATOR
-String to be appended to a directory name in filename completion.
+String to be appended to a directory name in filename completion
+(default "\\" on MS-DOS, Windows, and OS/2; otherwise "/").
 .IP LESSUTFBINFMT
 Format for displaying non-printable Unicode code points.
 .IP LESSUTFCHARDEF 
@@ -2550,6 +2590,24 @@ receives a SIGUSR1 signal.
 .IP LESS_TERMCAP_xx
 Where "xx" is any two characters, overrides the definition 
 of the termcap "xx" capability for the terminal.
+.IP LESS_TERMCAP_BRACKETED_PASTE_START
+Overrides the standard ANSI escape sequence to enable bracketed paste.
+This is used when the \-\-no-paste option is in effect.
+.IP LESS_TERMCAP_BRACKETED_PASTE_END
+Overrides the standard ANSI escape sequence to disable bracketed paste.
+.IP LESS_TERMCAP_MOUSE_START
+Overrides the standard ANSI escape sequence to enable mouse reporting.
+This is used when the \-\-mouse option is in effect.
+.IP LESS_TERMCAP_MOUSE_END
+Overrides the standard ANSI escape sequence to disable mouse reporting.
+.IP LESS_TERMCAP_SUSPEND
+Defines an escape sequence to temporarily suspend screen updates.
+This is sent to the terminal before clearing the screen.
+This can be used to avoid screen tearing when the screen is redrawn
+on certain terminals.
+.IP LESS_TERMCAP_RESUME
+Defines an escape sequence to resume screen updates.
+This is sent to the terminal after displaying the prompt.
 .IP LESS_UNSUPPORT
 A space-separated list of command line options.
 These options will be ignored (with no error message) if they appear 
@@ -2571,6 +2629,12 @@ automatically when running in
 .IP PATH
 User's search path (used to find a lesskey file
 on MS-DOS, Windows, and OS/2 systems).
+.IP POSIXLY_CORRECT
+If set to any value, all option arguments on the command line
+are expected to appear before any filename arguments.
+This must be set as an actual environment variable, not in a
+.B lesskey
+file.
 .IP SHELL
 The shell used to execute the !\& command, as well as to expand filenames.
 .IP TERM
@@ -2619,10 +2683,12 @@ See the GNU General Public License for more details.
*** 938 LINES SKIPPED ***