diff options
Diffstat (limited to 'contrib/less')
32 files changed, 581 insertions, 137 deletions
diff --git a/contrib/less/NEWS b/contrib/less/NEWS index cdc8196a5f16..442fe21e406a 100644 --- a/contrib/less/NEWS +++ b/contrib/less/NEWS @@ -11,6 +11,45 @@ ====================================================================== + 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 * Fix bad parsing of lesskey file an env var is a prefix of another 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}, @@ -857,6 +868,17 @@ static lbool is_in_table(LWCHAR ch, struct wchar_range_table *table) } /* + * 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. Mark Nudelman .br Report bugs at -.UR https://github.com/gwsw/less/issues -.UE . +.nh +https://github.com/gwsw/less/issues. +.hy .br For more information, see the less homepage at .br -.UR https://greenwoodsoftware.com/less -.UE . +.nh +https://greenwoodsoftware.com/less. +.hy diff --git a/contrib/less/lessecho.nro b/contrib/less/lessecho.nro index f0cccc4de6da..71e029fe81c4 100644 --- a/contrib/less/lessecho.nro +++ b/contrib/less/lessecho.nro @@ -1,4 +1,4 @@ -.TH LESSECHO 1 "Version 679: 28 May 2025" +.TH LESSECHO 1 "Version 685: 04 Oct 2025" .SH NAME lessecho \- expand metacharacters .SH SYNOPSIS @@ -53,6 +53,4 @@ The default is that only arguments containing metacharacters are quoted. This manual page was written by Thomas Schoepf <schoepf@debian.org>, for the Debian GNU/Linux system (but may be used by others). .PP -Report bugs at -.UR https://github.com/gwsw/less/issues -.UE . +Report bugs at https://github.com/gwsw/less/issues. diff --git a/contrib/less/lesskey.nro b/contrib/less/lesskey.nro index 0a17c9deff71..b829c062cdb4 100644 --- a/contrib/less/lesskey.nro +++ b/contrib/less/lesskey.nro @@ -1,5 +1,5 @@ '\" t -.TH LESSKEY 1 "Version 679: 28 May 2025" +.TH LESSKEY 1 "Version 685: 04 Oct 2025" .SH NAME lesskey \- customize key bindings for less .SH "SYNOPSIS (deprecated)" @@ -83,8 +83,10 @@ l l l. \er RETURN (0x0D) \et TAB (0x09) .TE +.RE .sp \ek followed by a single character represents the char(s) produced when one of these keys is pressed: +.RS 5m .TS l l. \ekb BACKSPACE (the BACKSPACE key) @@ -105,7 +107,7 @@ l l. \ekX ctrl-DELETE \ek1 F1 .TE - +.RE .PP A backslash followed by any other character indicates that character is to be taken literally. @@ -121,6 +123,14 @@ string is parsed, just as if it were typed in to This feature can be used in certain cases to extend the functionality of a command. For example, see the "{" and ":t" commands in the example below. +It can also be used to execute more than one command when a key is pressed. +For example, if this line were in a lesskey file, pressing the "B" key +would first set the "b" mark and then search for the string "next": +.sp +.nf + B set-mark b/next\en +.fi +.sp The extra string has a special meaning for the "quit" action: when .B less @@ -486,6 +496,4 @@ See the GNU General Public License for more details. . Mark Nudelman .br -Report bugs at -.UR https://github.com/gwsw/less/issues -.UE . +Report bugs at https://github.com/gwsw/less/issues. diff --git a/contrib/less/lglob.h b/contrib/less/lglob.h index d54315aef39d..af261fb2cfdd 100644 --- a/contrib/less/lglob.h +++ b/contrib/less/lglob.h @@ -56,7 +56,7 @@ char ext[_MAX_EXT]; \ int handle; #else -#if MSDOS_COMPILER==WIN32C && (defined(_MSC_VER) || defined(MINGW)) +#if MSDOS_COMPILER==WIN32C && (defined(_MSC_VER) || defined(__MINGW32__)) #define GLOB_FIRST_NAME(filename,fndp,h) h = _findfirst(filename, fndp) #define GLOB_FIRST_FAILED(handle) ((handle) == -1) diff --git a/contrib/less/line.c b/contrib/less/line.c index 8cc6397b804b..fc5d18abf4d2 100644 --- a/contrib/less/line.c +++ b/contrib/less/line.c @@ -28,6 +28,7 @@ static struct { int *attr; /* Parallel to buf, to hold attributes */ size_t print; /* Index in buf of first printable char */ size_t end; /* Number of chars in buf */ + size_t prev_end; /* Number of chars in buf for previous line */ char pfx[MAX_PFX_WIDTH]; /* Holds status column and line number */ int pfx_attr[MAX_PFX_WIDTH]; size_t pfx_end; /* Number of chars in pfx */ @@ -63,6 +64,7 @@ public int ntabstops = 1; /* Number of tabstops */ public int tabdefault = 8; /* Default repeated tabstops */ public POSITION highest_hilite; /* Pos of last hilite in file found so far */ static POSITION line_pos; +static POSITION line_contig_pos = NULL_POSITION; /* One after last byte processed */ static int end_column; /* Printable length, accounting for backspaces, etc. */ static int right_curr; @@ -255,12 +257,42 @@ public POSITION line_position(void) } /* + * Is this byte the next one after the previous byte processed? + */ +public lbool is_line_contig_pos(POSITION pos) +{ + return pos == line_contig_pos; +} + +/* + * Set the position of the next byte to be processed. + */ +public void set_line_contig_pos(POSITION pos) +{ + line_contig_pos = pos; +} + +/* + * Copy any ANSI sequences from line buffer to shifted_ansi. + */ +static void pshift(size_t end) +{ + size_t i; + for (i = linebuf.print; i < end; i++) + if (linebuf.attr[i] == AT_ANSI) + xbuf_add_char(&shifted_ansi, linebuf.buf[i]); +} + +/* * Rewind the line buffer. */ -public void prewind(void) +public void prewind(lbool contig) { int ax; + xbuf_reset(&shifted_ansi); + if (contig && linebuf.prev_end != 0) + pshift(linebuf.prev_end); linebuf.print = 6; /* big enough for longest UTF-8 sequence */ linebuf.pfx_end = 0; for (linebuf.end = 0; linebuf.end < linebuf.print; linebuf.end++) @@ -285,7 +317,6 @@ public void prewind(void) clear_after_line = FALSE; line_mark_attr = 0; line_pos = NULL_POSITION; - xbuf_reset(&shifted_ansi); xbuf_reset(&last_ansi); for (ax = 0; ax < NUM_LAST_ANSIS; ax++) xbuf_reset(&last_ansis[ax]); @@ -431,10 +462,7 @@ public int line_pfx_width(void) */ public void pshift_all(void) { - size_t i; - for (i = linebuf.print; i < linebuf.end; i++) - if (linebuf.attr[i] == AT_ANSI) - xbuf_add_char(&shifted_ansi, linebuf.buf[i]); + pshift(linebuf.end); linebuf.end = linebuf.print; end_column = (int) linebuf.pfx_end; /*{{type-issue}}*/ line_pos = NULL_POSITION; @@ -517,6 +545,12 @@ public int pwidth(LWCHAR ch, int a, LWCHAR prev_ch, int prev_a) } } else { + if (ch == VARSEL_15) + /* If prev char was double width, make it single width. */ + return (prev_ch != 0 && pwidth(prev_ch, a, 0, 0) == 2) ? -1 : 0; + if (ch == VARSEL_16) + /* If prev char was single width, make it double width. */ + return (prev_ch != 0 && pwidth(prev_ch, a, 0, 0) == 1) ? +1 : 0; if (is_composing_char(ch) || is_combining_char(prev_ch, ch)) { /* @@ -814,6 +848,7 @@ static int store_char(LWCHAR ch, int a, constant char *rep, POSITION pos) size_t replen; char cs; int ov; + lbool need_shift; ov = (a & (AT_UNDERLINE|AT_BOLD)); if (ov != AT_NORMAL) @@ -899,14 +934,41 @@ static int store_char(LWCHAR ch, int a, constant char *rep, POSITION pos) add_linebuf((char) shifted_ansi.data[i], AT_ANSI, 0); xbuf_reset(&shifted_ansi); } + if (linebuf.end == linebuf.print+1) + { + /* If first char is a placeholder, the one before it is double-width. + * VS15 changes the double-width char to single-width, so replace the + * placeholder with this VS15. */ + if (ch == VARSEL_15 && (linebuf.attr[linebuf.end-1] & AT_PLACEHOLDER)) + { + linebuf.end--; + inc_end_column(-1); + } + } else if (linebuf.end == linebuf.print) + { + /* VS16 changes the previous single-width char to double-width. + * Add a placeholder to represent the second half of the + * double-width char. */ + if (ch == VARSEL_16) + { + char *p = &linebuf.buf[linebuf.end]; + LWCHAR prev_ch = (linebuf.end > 0) ? step_char(&p, -1, linebuf.buf) : 0; + if (prev_ch != 0 && pwidth(prev_ch, a, 0, 0) == 1) + add_linebuf(' ', rscroll_attr|AT_PLACEHOLDER, 0); + } + } } /* Add the char to the buf, even if we will left-shift it next. */ + need_shift = (cshift < hshift); + if (!need_shift && w <= 0 && linebuf.end <= linebuf.print+1 && is_composing_char(ch) && + (linebuf.end == linebuf.print || (linebuf.end == linebuf.print+1 && (linebuf.attr[linebuf.end-1] & AT_PLACEHOLDER)))) + need_shift = TRUE; inc_end_column(w); for (i = 0; i < replen; i++) add_linebuf(*rep++, a, 0); - if (cshift < hshift) + if (need_shift) { /* We haven't left-shifted enough yet. */ if (a == AT_ANSI) @@ -929,7 +991,7 @@ static int store_char(LWCHAR ch, int a, constant char *rep, POSITION pos) */ while (cshift > hshift) { - add_linebuf(' ', rscroll_attr, 0); + add_linebuf(' ', rscroll_attr|AT_PLACEHOLDER, 0); cshift--; } } @@ -1267,6 +1329,17 @@ static int do_append(LWCHAR ch, constant char *rep, POSITION pos) overstrike = 0; } + if (is_omit_char(ch)) + { + if (bs_mode == BS_CONTROL) + { + if (utf_mode) + STORE_STRING(prutfchar(ch), AT_BINARY, pos); + else + STORE_PRCHAR(ch, pos); + } + return (0); /* omit the character. */ + } if (ch == '\t') { /* @@ -1340,6 +1413,7 @@ static void add_attr_normal(void) public void pdone(lbool endline, lbool chopped, lbool forw) { (void) pflushmbc(); + linebuf.prev_end = (!endline && !chopped) ? linebuf.end : 0; if (pendc && (pendc != '\r' || !endline)) /* @@ -1509,7 +1583,11 @@ static void col_vs_pos(POSITION linepos, mutable struct col_pos *cp, POSITION sa LWCHAR wch = get_wchar(utf8_buf); int attr = 0; /* {{ ignoring attribute is not correct for magic cookie terminals }} */ utf8_len = 0; - if (utf_mode && ctldisp != OPT_ON && is_ubin_char(wch)) + if (is_omit_char(wch)) + { + if (bs_mode == BS_CONTROL) + cw = strlen(utf_mode ? prutfchar(wch) : prchar(wch)); + } else if (utf_mode && ctldisp != OPT_ON && is_ubin_char(wch)) cw = (int) strlen(prutfchar(wch)); else cw = pwidth(wch, attr, prev_ch, attr); @@ -1638,6 +1716,7 @@ public POSITION forw_raw_line_len(POSITION curr_pos, size_t read_len, constant c (c = ch_forw_get()) == EOI) return (NULL_POSITION); + set_line_contig_pos(NULL_POSITION); n = 0; for (;;) { @@ -1693,6 +1772,7 @@ public POSITION back_raw_line(POSITION curr_pos, constant char **linep, size_t * ch_seek(curr_pos-1)) return (NULL_POSITION); + set_line_contig_pos(NULL_POSITION); n = size_linebuf; linebuf.buf[--n] = '\0'; for (;;) @@ -1804,11 +1884,14 @@ static int pappstr(constant char *str) public void load_line(constant char *str) { int save_hshift = hshift; - hshift = 0; + + /* We're overwriting the line buffer, so what's in it will no longer be contiguous. */ + set_line_contig_pos(NULL_POSITION); + for (;;) { - prewind(); + prewind(FALSE); if (pappstr(str) == 0) break; /* @@ -1819,6 +1902,7 @@ public void load_line(constant char *str) hshift += 1; } set_linebuf(linebuf.end, '\0', AT_NORMAL); + linebuf.prev_end = 0; /* Color the prompt unless it has ansi sequences in it. */ if (!ansi_in_line) diff --git a/contrib/less/lsystem.c b/contrib/less/lsystem.c index c32d5d59d4c2..a56f8238e480 100644 --- a/contrib/less/lsystem.c +++ b/contrib/less/lsystem.c @@ -19,7 +19,7 @@ #if MSDOS_COMPILER #include <dos.h> -#if MSDOS_COMPILER==WIN32C && defined(MINGW) +#if MSDOS_COMPILER==WIN32C && defined(__MINGW32__) #include <direct.h> #define setdisk(n) _chdrive((n)+1) #else diff --git a/contrib/less/main.c b/contrib/less/main.c index 280322e01d4b..e3a546d7084d 100644 --- a/contrib/less/main.c +++ b/contrib/less/main.c @@ -17,7 +17,7 @@ #define WIN32_LEAN_AND_MEAN #include <windows.h> -#if defined(MINGW) || defined(_MSC_VER) +#if defined(__MINGW32__) || defined(_MSC_VER) #include <locale.h> #include <shellapi.h> #endif @@ -78,7 +78,7 @@ extern int redraw_on_quit; extern int term_init_done; extern lbool first_time; -#if MSDOS_COMPILER==WIN32C && (defined(MINGW) || defined(_MSC_VER)) +#if MSDOS_COMPILER==WIN32C && (defined(__MINGW32__) || defined(_MSC_VER)) /* malloc'ed 0-terminated utf8 of 0-terminated wide ws, or null on errors */ static char *utf8_from_wide(constant wchar_t *ws) { @@ -247,8 +247,14 @@ int main(int argc, constant char *argv[]) { IFILE ifile; constant char *s; + int i; + struct xbuffer xfiles; + constant int *files; + size_t num_files; + lbool end_opts = FALSE; + lbool posixly_correct = FALSE; -#if MSDOS_COMPILER==WIN32C && (defined(MINGW) || defined(_MSC_VER)) +#if MSDOS_COMPILER==WIN32C && (defined(__MINGW32__) || defined(_MSC_VER)) if (GetACP() != CP_UTF8) /* not using a UTF-8 manifest */ try_utf8_locale(&argc, &argv); #endif @@ -318,13 +324,20 @@ int main(int argc, constant char *argv[]) #define isoptstring(s) less_is_more ? (((s)[0] == '-') && (s)[1] != '\0') : \ (((s)[0] == '-' || (s)[0] == '+') && (s)[1] != '\0') - while (argc > 0 && (isoptstring(*argv) || isoptpending())) + xbuf_init(&xfiles); + posixly_correct = (getenv("POSIXLY_CORRECT") != NULL); + for (i = 0; i < argc; i++) { - s = *argv++; - argc--; - if (strcmp(s, "--") == 0) - break; - scan_option(s, FALSE); + if (strcmp(argv[i], "--") == 0) + end_opts = TRUE; + else if (!end_opts && (isoptstring(argv[i]) || isoptpending())) + scan_option(argv[i], FALSE); + else + { + if (posixly_correct) + end_opts = TRUE; + xbuf_add_data(&xfiles, (constant unsigned char *) &i, sizeof(i)); + } } #undef isoptstring @@ -364,7 +377,9 @@ int main(int argc, constant char *argv[]) ifile = NULL_IFILE; if (dohelp) ifile = get_ifile(FAKE_HELPFILE, ifile); - while (argc-- > 0) + files = (constant int *) xfiles.data; + num_files = xfiles.end / sizeof(int); + for (i = 0; i < num_files; i++) { #if (MSDOS_COMPILER && MSDOS_COMPILER != DJGPPC) /* @@ -378,7 +393,7 @@ int main(int argc, constant char *argv[]) char *gfilename; char *qfilename; - gfilename = lglob(*argv++); + gfilename = lglob(argv[files[i]]); init_textlist(&tlist, gfilename); filename = NULL; while ((filename = forw_textlist(&tlist, filename)) != NULL) @@ -390,10 +405,12 @@ int main(int argc, constant char *argv[]) } free(gfilename); #else - (void) get_ifile(*argv++, ifile); + (void) get_ifile(argv[files[i]], ifile); ifile = prev_ifile(NULL_IFILE); #endif } + xbuf_deinit(&xfiles); + /* * Set up terminal, etc. */ diff --git a/contrib/less/mkutable b/contrib/less/mkutable index 21b84c3d55de..51dbab71d161 100755 --- a/contrib/less/mkutable +++ b/contrib/less/mkutable @@ -22,6 +22,17 @@ my %force_space = ( 0x0d => 1, # carriage return ); +# Override Unicode tables for certain modifier chars which act differently +# on different terminals. Treat them as omittable. +my @force_omit = ( + [0xad, 0xad], # SOFT HYPHEN + [0x200d, 0x200d], # ZERO WIDTH JOINER + [0x1f3fb, 0x1f3ff], # EMOJI MODIFIER FITZPATRICK TYPE-[1-6] + [0x1f9b0, 0x1f9b3], # EMOJI COMPONENT [RED,CURLY,BALD,WHITE] HAIR + [0xfe00, 0xfe0f], # VARIATION SELECTOR-[1-16] + [0xe0100, 0xe01ef], # VARIATION SELECTOR-[17-256] +); + # Hangul Jamo medial vowels and final consonants should be zero width. my @force_compose = ( [0x1160, 0x11ff], @@ -51,6 +62,13 @@ sub main { $force_compose{$ch} = 1; } } + my %force_omit; + foreach my $comp (@force_omit) { + my ($lo,$hi) = @$comp; + for (my $ch = $lo; $ch <= $hi; ++$ch) { + $force_omit{$ch} = 1; + } + } my ($sec,$min,$hour,$mday,$mon,$year) = gmtime($ENV{SOURCE_DATE_EPOCH} // time()); my @month = ( "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec" ); @@ -88,7 +106,8 @@ sub main { $type =~ s/\s//g; for ($last_code = $lo_code; $last_code <= $hi_code; ++$last_code) { output(\%out, $last_code, - $force_space{$last_code} ? 'Zs' : $force_compose{$last_code} ? 'Mn' : $type); + $force_space{$last_code} ? 'Zs' : $force_compose{$last_code} ? 'Mn' : + $force_omit{$last_code} ? 'Xx' : $type); } } output(\%out, $last_code); diff --git a/contrib/less/omit.uni b/contrib/less/omit.uni new file mode 100644 index 000000000000..9977ec403f3d --- /dev/null +++ b/contrib/less/omit.uni @@ -0,0 +1,7 @@ +/* Generated by "./mkutable -f2 Xx -- unicode/UnicodeData.txt" on Aug 19 1:16:32 GMT 2025 */ + { 0x00ad, 0x00ad }, /* Xx */ + { 0x200d, 0x200d }, /* Xx */ + { 0xfe00, 0xfe0f }, /* Xx */ + { 0x1f3fb, 0x1f3ff }, /* Xx */ + { 0x1f9b0, 0x1f9b3 }, /* Xx */ + { 0xe0100, 0xe01ef }, /* Xx */ diff --git a/contrib/less/optfunc.c b/contrib/less/optfunc.c index fda46b588f21..9c0117cc84ff 100644 --- a/contrib/less/optfunc.c +++ b/contrib/less/optfunc.c @@ -72,6 +72,7 @@ extern int nosearch_header_lines; extern int nosearch_header_cols; extern POSITION header_start_pos; extern char *init_header; +extern char *first_cmd_at_prompt; #if LOGFILE extern char *namelogfile; extern lbool force_logfile; @@ -974,6 +975,23 @@ public void opt_filesize(int type, constant char *s) } /* + * Handler for the --cmd option. + */ + /*ARGSUSED*/ +public void opt_first_cmd_at_prompt(int type, constant char *s) +{ + switch (type) + { + case INIT: + case TOGGLE: + first_cmd_at_prompt = save(s); + break; + case QUERY: + break; + } +} + +/* * Handler for the --intr option. */ /*ARGSUSED*/ diff --git a/contrib/less/opttbl.c b/contrib/less/opttbl.c index be89403d6ce9..292a8be15d1e 100644 --- a/contrib/less/opttbl.c +++ b/contrib/less/opttbl.c @@ -86,6 +86,7 @@ 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 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 */ #if HILITE_SEARCH public int hilite_search; /* Highlight matched search patterns? */ #endif @@ -184,6 +185,7 @@ static struct optname proc_backspace_optname = { "proc-backspace", NULL }; static struct optname proc_tab_optname = { "proc-tab", NULL }; 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 }; #if LESSTEST static struct optname ttyin_name_optname = { "tty", NULL }; #endif /*LESSTEST*/ @@ -748,6 +750,10 @@ static struct loption option[] = "Print carriage return as ^M" } }, + { OLETTER_NONE, &first_cmd_at_prompt_optname, + O_STRING|O_NO_TOGGLE|O_NO_QUERY, 0, NULL, opt_first_cmd_at_prompt, + { NULL, NULL, NULL } + }, { OLETTER_NONE, &match_shift_optname, O_STRING|O_INIT_HANDLER, 0, NULL, opt_match_shift, { diff --git a/contrib/less/os.c b/contrib/less/os.c index 357cbb356a16..3bac7a61a7be 100644 --- a/contrib/less/os.c +++ b/contrib/less/os.c @@ -43,7 +43,7 @@ extern int errno; #include <sys/utsname.h> #endif -#if HAVE_POLL && !MSDOS_COMPILER +#if HAVE_POLL && !MSDOS_COMPILER && !defined(__MVS__) #define USE_POLL 1 static lbool use_poll = TRUE; #else @@ -82,6 +82,7 @@ static lbool opening; public lbool waiting_for_data; public int consecutive_nulls = 0; public lbool getting_one_screen = FALSE; +public lbool no_poll = FALSE; /* Milliseconds to wait for data before displaying "waiting for data" message. */ static int waiting_for_data_delay = 4000; @@ -163,7 +164,8 @@ static int check_poll(int fd, int tty) /* Break out of "waiting for data". */ return (READ_INTR); ungetcc_back((char) ch); - return (READ_INTR); + if (!no_poll) + return (READ_INTR); } } if (ignore_eoi && exit_F_on_close && (poller[0].revents & (POLLHUP|POLLIN)) == POLLHUP) @@ -177,6 +179,36 @@ static int check_poll(int fd, int tty) } #endif /* USE_POLL */ +/* + * Is a character available to be read from the tty? + */ +public lbool ttyin_ready(void) +{ +#if MSDOS_COMPILER==WIN32C + return win32_kbhit(); +#else +#if MSDOS_COMPILER + return kbhit(); +#else +#if USE_POLL +#if LESSTEST + if (is_lesstest()) + return FALSE; +#endif /*LESSTEST*/ + if (!use_poll) + return FALSE; + { + struct pollfd poller[1] = { { tty, POLLIN, 0 } }; + poll(poller, 1, 0); + return ((poller[0].revents & POLLIN) != 0); + } +#else + return FALSE; +#endif +#endif +#endif +} + public int supports_ctrl_x(void) { #if MSDOS_COMPILER==WIN32C @@ -282,16 +314,21 @@ start: } #else #if MSDOS_COMPILER==WIN32C - if (win32_kbhit2(TRUE)) + if (!(quit_if_one_screen && one_screen) && win32_kbhit2(TRUE)) { int c; + lbool intr; c = WIN32getch(); - sigs |= S_SWINTERRUPT; - reading = FALSE; - if (c != CONTROL('C') && c != intr_char) + intr = (c == CONTROL('C') || c == intr_char); + if (!intr) WIN32ungetch((char) c); - return (READ_INTR); + if (intr || !no_poll) + { + sigs |= S_SWINTERRUPT; + reading = FALSE; + return (READ_INTR); + } } #endif #endif diff --git a/contrib/less/pattern.c b/contrib/less/pattern.c index f2ec7cbcb6c8..0c72b98d2cd1 100644 --- a/contrib/less/pattern.c +++ b/contrib/less/pattern.c @@ -469,7 +469,12 @@ public lbool match_pattern(PATTERN_TYPE pattern, constant char *tpattern, consta lbool matched = match_pattern1(pattern, tpattern, line, line_len, line_off, sp, ep, nsp, notbol, search_type); if (!matched || subsearch_ok(sp, ep, search_type)) return matched; - mlen = ep[0] - line; + /* We have a match, but it does not satisfy all SUBSEARCH conditions. + * Continue searching after this match. */ + mlen = ptr_diff(ep[0], line); + if (mlen == 0) + /* If the match is empty, we can't progress. */ + return FALSE; line += mlen; line_len -= mlen; notbol = 1; diff --git a/contrib/less/screen.c b/contrib/less/screen.c index 3a997b6f1a70..7b91cbb63bbe 100644 --- a/contrib/less/screen.c +++ b/contrib/less/screen.c @@ -221,6 +221,8 @@ static constant char *sc_e_mousecap, /* End mouse capture mode */ *sc_s_bracketed_paste, /* Start bracketed paste mode */ *sc_e_bracketed_paste, /* End bracketed paste mode */ + *sc_suspend, /* Suspend screen updates */ + *sc_resume, /* Resume screen updates */ *sc_init, /* Startup terminal initialization */ *sc_deinit; /* Exit terminal de-initialization */ @@ -1330,6 +1332,13 @@ public void get_term(void) if (sc_e_bracketed_paste == NULL) sc_e_bracketed_paste = ESCS"[?2004l"; + sc_suspend = ltgetstr("SUSPEND", &sp); + if (sc_suspend == NULL) + sc_suspend = ""; + sc_resume = ltgetstr("RESUME", &sp); + if (sc_resume == NULL) + sc_resume = ""; + sc_init = ltgetstr("ti", &sp); if (sc_init == NULL) sc_init = ""; @@ -1756,6 +1765,26 @@ public void deinit_mouse(void) } /* + * Suspend screen updates. + */ +public void suspend_screen(void) +{ +#if !MSDOS_COMPILER + ltputs(sc_suspend, 1, putchr); +#endif +} + +/* + * Resume screen updates. + */ +public void resume_screen(void) +{ +#if !MSDOS_COMPILER + ltputs(sc_resume, 1, putchr); +#endif +} + +/* * Initialize terminal */ public void init(void) @@ -2341,6 +2370,7 @@ public void bell(void) public void clear(void) { assert_interactive(); + suspend_screen(); #if !MSDOS_COMPILER ltputs(sc_clear, sc_height, putchr); #else diff --git a/contrib/less/search.c b/contrib/less/search.c index 78bcc7085e2d..75f7efee0535 100644 --- a/contrib/less/search.c +++ b/contrib/less/search.c @@ -52,6 +52,7 @@ static POSITION prep_endpos; public POSITION header_start_pos = NULL_POSITION; static POSITION header_end_pos; public lbool search_wrapped = FALSE; +public POSITION search_incr_start = NULL_POSITION; #if OSC8_LINK public POSITION osc8_linepos = NULL_POSITION; public POSITION osc8_match_start = NULL_POSITION; @@ -1083,7 +1084,7 @@ public void chg_hilite(void) /* * Figure out where to start a search. */ -static POSITION search_pos(int search_type) +public POSITION search_pos(int search_type) { POSITION pos; int sindex; @@ -1189,6 +1190,7 @@ static lbool matches_filters(POSITION pos, char *cline, size_t line_len, int *ch struct hilite hl; hl.hl_startpos = linepos; hl.hl_endpos = pos; + hl.hl_attr = 0; add_hilite(&filter_anchor, &hl); free(cline); free(chpos); @@ -2118,7 +2120,8 @@ public int search(int search_type, constant char *pattern, int n) /* * Figure out where to start the search. */ - pos = search_pos(search_type); + pos = ((search_type & SRCH_INCR) && search_incr_start != NULL_POSITION) ? + search_incr_start : search_pos(search_type); opos = position(sindex_from_sline(jump_sline)); if (pos == NULL_POSITION) { @@ -2241,7 +2244,7 @@ public void prep_hilite(POSITION spos, POSITION epos, int maxlines) */ clr_hilite(); clr_filter(); - nprep_startpos = spos; + nprep_startpos = nprep_endpos = spos; } else { /* @@ -2282,7 +2285,7 @@ public void prep_hilite(POSITION spos, POSITION epos, int maxlines) result = search_range(spos, epos, search_type, 0, maxlines, (POSITION*)NULL, &new_epos, (POSITION*)NULL); if (result < 0) return; - if (prep_endpos == NULL_POSITION || new_epos > prep_endpos) + if (nprep_endpos == NULL_POSITION || new_epos > nprep_endpos) nprep_endpos = new_epos; /* @@ -2300,6 +2303,7 @@ public void prep_hilite(POSITION spos, POSITION epos, int maxlines) if (epos == NULL_POSITION) break; maxlines = 1; + nprep_endpos = epos; continue; } } diff --git a/contrib/less/ubin.uni b/contrib/less/ubin.uni index e422427e1976..5d6f97c237ad 100644 --- a/contrib/less/ubin.uni +++ b/contrib/less/ubin.uni @@ -1,4 +1,4 @@ -/* Generated by "./mkutable -f2 Cc Cs Co Zl Zp -- unicode/UnicodeData.txt" on Oct 1 18:10:07 GMT 2024 */ +/* Generated by "./mkutable -f2 Cc Cs Co Zl Zp -- unicode/UnicodeData.txt" on Aug 19 1:16:32 GMT 2025 */ { 0x0000, 0x0007 }, /* Cc */ { 0x000b, 0x000b }, /* Cc */ { 0x000e, 0x001f }, /* Cc */ diff --git a/contrib/less/version.c b/contrib/less/version.c index 68a42a6272fa..a5925c335a8f 100644 --- a/contrib/less/version.c +++ b/contrib/less/version.c @@ -1049,6 +1049,18 @@ v677 4/27/25 Fix & filtering bug. v678 5/1/25 Don't change stty tab setting. v679 5/28/25 Fix lesskey parsing bug when env var is prefix of another; fix unexpected exit when using -K. +v680 6/25/25 Fix hang if a search using ^S modifier matches empty string. +v681 8/2/25 Fix bug using -g with -J; fix bug when pasting input + with --incsearch; improve performance with long lines; + fix performance with & filtering; change search position when + using --incsearch; treat some composing chars as binary. +v682 8/24/25 Add --cmd; fix lesskey bug using #stop; fix lesskey bug + using "invalid"; fix some emoji bugs. +v683 9/4/25 Fix bug if cmd char received during file read. +v684 9/18/25 Allow mixing of options and filenames on command line; + add LESS_TERMCAP_SUSPEND & LESS_TERMCAP_RESUME. +v685 10/4/25 Make --incsearch return to same column as well as same line; + fix some problems reported by valgrind. */ -char version[] = "679"; +char version[] = "685"; diff --git a/contrib/less/wide.uni b/contrib/less/wide.uni index 6b4688c3b1f1..7ac89653987b 100644 --- a/contrib/less/wide.uni +++ b/contrib/less/wide.uni @@ -1,4 +1,4 @@ -/* Generated by "./mkutable -f1 W F -- unicode/EastAsianWidth.txt" on Sep 23 19:46:51 GMT 2024 */ +/* Generated by "./mkutable -f1 W F -- unicode/EastAsianWidth.txt" on Aug 10 20:15:03 GMT 2025 */ { 0x1100, 0x115f }, /* W */ { 0x231a, 0x231b }, /* W */ { 0x2329, 0x232a }, /* W */ @@ -93,7 +93,8 @@ { 0x1f3cf, 0x1f3d3 }, /* W */ { 0x1f3e0, 0x1f3f0 }, /* W */ { 0x1f3f4, 0x1f3f4 }, /* W */ - { 0x1f3f8, 0x1f43e }, /* W */ + { 0x1f3f8, 0x1f3fa }, /* W */ + { 0x1f400, 0x1f43e }, /* W */ { 0x1f440, 0x1f440 }, /* W */ { 0x1f442, 0x1f4fc }, /* W */ { 0x1f4ff, 0x1f53d }, /* W */ @@ -114,7 +115,8 @@ { 0x1f7f0, 0x1f7f0 }, /* W */ { 0x1f90c, 0x1f93a }, /* W */ { 0x1f93c, 0x1f945 }, /* W */ - { 0x1f947, 0x1f9ff }, /* W */ + { 0x1f947, 0x1f9af }, /* W */ + { 0x1f9b4, 0x1f9ff }, /* W */ { 0x1fa70, 0x1fa7c }, /* W */ { 0x1fa80, 0x1fa89 }, /* W */ { 0x1fa8f, 0x1fac6 }, /* W */ diff --git a/contrib/less/xbuf.c b/contrib/less/xbuf.c index 443e21da907b..8c940c41c8a9 100644 --- a/contrib/less/xbuf.c +++ b/contrib/less/xbuf.c @@ -86,7 +86,7 @@ public int xbuf_pop(struct xbuffer *buf) /* * Set an xbuf to the contents of another xbuf. */ -public void xbuf_set(struct xbuffer *dst, struct xbuffer *src) +public void xbuf_set(struct xbuffer *dst, constant struct xbuffer *src) { xbuf_reset(dst); xbuf_add_data(dst, src->data, src->end); |
