diff --git a/CWRU/CWRU.chlog b/CWRU/CWRU.chlog index ca99ee98..dd77dacd 100644 --- a/CWRU/CWRU.chlog +++ b/CWRU/CWRU.chlog @@ -5327,3 +5327,69 @@ parse.y bashline.c - set_up_new_line: add some more heuristics to try and usefully position rl_point + + 2/16 + ---- +lib/readline/complete.c + - complete_fncmp: fix for case mapping `-' and `_' if the bytes do not + form a valid multibyte character + +lib/readline/util.c + - new functions: _rl_strcaseeqn, like strcasencmp but with a flag to + handle character mapping (-_) like completion-map-case wants, and + _rl_charcasecmp, to do the same thing for two characters. + Inspired by a patch from Brennan Vincent + +lib/readline/mbutil.c + - new functions: _rl_mb_strcaseeqn and _rl_mb_charcasecmp, multibyte + versions of the above + Inspired by a patch from Brennan Vincent + +lib/readline/{rlprivate.h,rlmbutil.h} + - extern declarations for the above functions + +lib/readline/complete.c + - complete_fncmp: use _rl_mb_strcaseeqn and _rl_strcaseeqn as + appropriate, remove duplicated inline code + Inspired by a patch from Brennan Vincent + +lib/readline/{isearch.c,rlprivate.h} + - _rl_search_case_fold: new variable, will be used to indicate case- + insensitive incremental and non-incremental searches + +lib/readline/bind.c + - search-ignore-case: new bindable variable, tied to _rl_search_case_fold + Inspired by a patch from Brennan Vincent + +lib/sh/mbsncmp.c + - mbsncmp(3) replacement + +lib/readline/isearch.c + - _rl_isearch_dispatch: honor `search-ignore-case' variable and perform + case-insensitive incremental searches if it's set + Inspired by a patch from Brennan Vincent + +lib/readline/histlib.h + - CASEFOLD_SEARCH: new flag for history searching, means to search + strings case-insensitively + +lib/readline/histsearch.c + - history_search_internal: anchored searches honor CASEFOLD_SEARCH + + 2/17 + ---- +lib/readline/histsearch.c + - history_search_internal: searches now honor CASEFOLD_SEARCH + - _hs_history_search: new function, just external interface to + history_search_internal, allows the rest of the library to specify + search flags + +lib/readline/histlib.h + - _hs_history_search: extern declaration + +lib/readline/search.c + - noninc_search_from_pos: use _hs_history_search, add CASEFOLD_SEARCH + to flags if _rl_search_case_fold is non-zero + +doc/bash.1,lib/readline/doc/{readline.3,rluser.texi} + - search-ignore-case: document new bindable variable diff --git a/MANIFEST b/MANIFEST index f9dc0e90..1e9685d3 100644 --- a/MANIFEST +++ b/MANIFEST @@ -419,6 +419,7 @@ lib/sh/makepath.c f lib/sh/mbscasecmp.c f lib/sh/mbschr.c f lib/sh/mbscmp.c f +lib/sh/mbsncmp.c f lib/sh/memset.c f lib/sh/mktime.c f lib/sh/netconn.c f diff --git a/Makefile.in b/Makefile.in index a51f3359..ab45e1a5 100644 --- a/Makefile.in +++ b/Makefile.in @@ -236,6 +236,7 @@ SHLIB_SOURCE = ${SH_LIBSRC}/clktck.c ${SH_LIBSRC}/getcwd.c \ ${SH_LIBSRC}/eaccess.c ${SH_LIBSRC}/wcsdup.c \ ${SH_LIBSRC}/zmapfd.c ${SH_LIBSRC}/fpurge.c \ ${SH_LIBSRC}/zgetline.c ${SH_LIBSRC}/mbscmp.c \ + ${SH_LIBSRC}/mbsncmp.c \ ${SH_LIBSRC}/casemod.c ${SH_LIBSRC}/uconvert.c \ ${SH_LIBSRC}/ufuncs.c ${SH_LIBSRC}/dprintf.c \ ${SH_LIBSRC}/input_avail.c ${SH_LIBSRC}/mbscasecmp.c \ diff --git a/aclocal.m4 b/aclocal.m4 index c5e8ab15..37546c6c 100644 --- a/aclocal.m4 +++ b/aclocal.m4 @@ -1758,8 +1758,9 @@ AC_CHECK_HEADERS(langinfo.h) AC_CHECK_HEADERS(mbstr.h) AC_CHECK_FUNC(mbrlen, AC_DEFINE(HAVE_MBRLEN)) -AC_CHECK_FUNC(mbscasecmp, AC_DEFINE(HAVE_MBSCMP)) +AC_CHECK_FUNC(mbscasecmp, AC_DEFINE(HAVE_MBSCASECMP)) AC_CHECK_FUNC(mbscmp, AC_DEFINE(HAVE_MBSCMP)) +AC_CHECK_FUNC(mbsncmp, AC_DEFINE(HAVE_MBSNCMP)) AC_CHECK_FUNC(mbsnrtowcs, AC_DEFINE(HAVE_MBSNRTOWCS)) AC_CHECK_FUNC(mbsrtowcs, AC_DEFINE(HAVE_MBSRTOWCS)) diff --git a/config.h.in b/config.h.in index 5a221ba2..ba2b9fc8 100644 --- a/config.h.in +++ b/config.h.in @@ -738,6 +738,9 @@ /* Define if you have the mbscmp function. */ #undef HAVE_MBSCMP +/* Define if you have the mbsncmp function. */ +#undef HAVE_MBSNCMP + /* Define if you have the mbsnrtowcs function. */ #undef HAVE_MBSNRTOWCS diff --git a/configure b/configure index 03008d03..78e55ecf 100755 --- a/configure +++ b/configure @@ -16289,7 +16289,7 @@ fi ac_fn_c_check_func "$LINENO" "mbscasecmp" "ac_cv_func_mbscasecmp" if test "x$ac_cv_func_mbscasecmp" = xyes then : - printf "%s\n" "#define HAVE_MBSCMP 1" >>confdefs.h + printf "%s\n" "#define HAVE_MBSCASECMP 1" >>confdefs.h fi @@ -16300,6 +16300,13 @@ then : fi +ac_fn_c_check_func "$LINENO" "mbsncmp" "ac_cv_func_mbsncmp" +if test "x$ac_cv_func_mbsncmp" = xyes +then : + printf "%s\n" "#define HAVE_MBSNCMP 1" >>confdefs.h + +fi + ac_fn_c_check_func "$LINENO" "mbsnrtowcs" "ac_cv_func_mbsnrtowcs" if test "x$ac_cv_func_mbsnrtowcs" = xyes then : diff --git a/doc/bash.1 b/doc/bash.1 index 376e347e..3a2670e0 100644 --- a/doc/bash.1 +++ b/doc/bash.1 @@ -6241,6 +6241,10 @@ before returning when \fBaccept\-line\fP is executed. By default, history lines may be modified and retain individual undo lists across calls to \fBreadline\fP. .TP +.B search\-ignore\-case (Off) +If set to \fBOn\fP, readline performs incremental and non-incremental +history list searches in a case\-insensitive fashion. +.TP .B show\-all\-if\-ambiguous (Off) This alters the default behavior of the completion functions. If set to diff --git a/lib/readline/bind.c b/lib/readline/bind.c index 96778938..54ee5cde 100644 --- a/lib/readline/bind.c +++ b/lib/readline/bind.c @@ -1916,6 +1916,7 @@ static const struct { { "prefer-visible-bell", &_rl_prefer_visible_bell, V_SPECIAL }, { "print-completions-horizontally", &_rl_print_completions_horizontally, 0 }, { "revert-all-at-newline", &_rl_revert_all_at_newline, 0 }, + { "search-ignore-case", &_rl_search_case_fold, 0 }, { "show-all-if-ambiguous", &_rl_complete_show_all, 0 }, { "show-all-if-unmodified", &_rl_complete_show_unmodified, 0 }, { "show-mode-in-prompt", &_rl_show_mode_in_prompt, 0 }, diff --git a/lib/readline/complete.c b/lib/readline/complete.c index fe0b8798..2016d393 100644 --- a/lib/readline/complete.c +++ b/lib/readline/complete.c @@ -1,6 +1,6 @@ /* complete.c -- filename completion for readline. */ -/* Copyright (C) 1987-2022 Free Software Foundation, Inc. +/* Copyright (C) 1987-2022,2023 Free Software Foundation, Inc. This file is part of the GNU Readline Library (Readline), a library for reading lines of text with interactive input and history editing. @@ -2349,18 +2349,7 @@ rl_username_completion_function (const char *text, int state) static int complete_fncmp (const char *convfn, int convlen, const char *filename, int filename_len) { - register char *s1, *s2; - int d, len; -#if defined (HANDLE_MULTIBYTE) - size_t v1, v2; - mbstate_t ps1, ps2; - WCHAR_T wc1, wc2; -#endif - -#if defined (HANDLE_MULTIBYTE) - memset (&ps1, 0, sizeof (mbstate_t)); - memset (&ps2, 0, sizeof (mbstate_t)); -#endif + size_t len; if (filename_len == 0) return 1; @@ -2368,100 +2357,26 @@ complete_fncmp (const char *convfn, int convlen, const char *filename, int filen return 0; len = filename_len; - s1 = (char *)convfn; - s2 = (char *)filename; /* Otherwise, if these match up to the length of filename, then it is a match. */ - if (_rl_completion_case_fold && _rl_completion_case_map) + if (_rl_completion_case_fold) { - /* Case-insensitive comparison treating _ and - as equivalent */ + /* Case-insensitive comparison treating _ and - as equivalent if + _rl_completion_case_map is non-zero */ #if defined (HANDLE_MULTIBYTE) if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) - { - do - { - v1 = MBRTOWC (&wc1, s1, convlen, &ps1); - v2 = MBRTOWC (&wc2, s2, filename_len, &ps2); - if (v1 == 0 && v2 == 0) - return 1; - else if (MB_INVALIDCH (v1) || MB_INVALIDCH (v2)) - { - if (*s1 != *s2) /* do byte comparison */ - return 0; - else if ((*s1 == '-' || *s1 == '_') && (*s2 == '-' || *s2 == '_')) - return 0; - s1++; s2++; len--; - continue; - } - wc1 = towlower (wc1); - wc2 = towlower (wc2); - s1 += v1; - s2 += v1; - len -= v1; - if ((wc1 == L'-' || wc1 == L'_') && (wc2 == L'-' || wc2 == L'_')) - continue; - if (wc1 != wc2) - return 0; - } - while (len != 0); - } - else -#endif - { - do - { - d = _rl_to_lower (*s1) - _rl_to_lower (*s2); - /* *s1 == [-_] && *s2 == [-_] */ - if ((*s1 == '-' || *s1 == '_') && (*s2 == '-' || *s2 == '_')) - d = 0; - if (d != 0) - return 0; - s1++; s2++; /* already checked convlen >= filename_len */ - } - while (--len != 0); - } - - return 1; - } - else if (_rl_completion_case_fold) - { -#if defined (HANDLE_MULTIBYTE) - if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) - { - do - { - v1 = MBRTOWC (&wc1, s1, convlen, &ps1); - v2 = MBRTOWC (&wc2, s2, filename_len, &ps2); - if (v1 == 0 && v2 == 0) - return 1; - else if (MB_INVALIDCH (v1) || MB_INVALIDCH (v2)) - { - if (*s1 != *s2) /* do byte comparison */ - return 0; - s1++; s2++; len--; - continue; - } - wc1 = towlower (wc1); - wc2 = towlower (wc2); - if (wc1 != wc2) - return 0; - s1 += v1; - s2 += v1; - len -= v1; - } - while (len != 0); - return 1; - } + return (_rl_mb_strcaseeqn (convfn, convlen, filename, filename_len, len, _rl_completion_case_map)); else #endif if ((_rl_to_lower (convfn[0]) == _rl_to_lower (filename[0])) && - (convlen >= filename_len) && - (_rl_strnicmp (filename, convfn, filename_len) == 0)) - return 1; + (convlen >= filename_len)) + return (_rl_strcaseeqn (convfn, filename, len, _rl_completion_case_map)); } else { + /* XXX - add new _rl_mb_streqn function (like mbsncmp) instead of + relying on byte equivalence? */ if ((convfn[0] == filename[0]) && (convlen >= filename_len) && (strncmp (filename, convfn, filename_len) == 0)) diff --git a/lib/readline/doc/readline.3 b/lib/readline/doc/readline.3 index 77ef02a6..9a63e09e 100644 --- a/lib/readline/doc/readline.3 +++ b/lib/readline/doc/readline.3 @@ -6,9 +6,9 @@ .\" Case Western Reserve University .\" chet.ramey@case.edu .\" -.\" Last Change: Mon Sep 19 11:11:22 EDT 2022 +.\" Last Change: Fri Feb 17 10:59:58 EST 2023 .\" -.TH READLINE 3 "2022 September 19" "GNU Readline 8.2" +.TH READLINE 3 "2023 February 17" "GNU Readline 8.2" .\" .\" File Name macro. This used to be `.PN', for Path Name, .\" but Sun doesn't seem to like that very much. @@ -34,8 +34,8 @@ readline \- get a line from a user with editing \fBreadline\fP (\fIconst char *prompt\fP); .fi .SH COPYRIGHT -.if n Readline is Copyright (C) 1989\-2020 Free Software Foundation, Inc. -.if t Readline is Copyright \(co 1989\-2020 Free Software Foundation, Inc. +.if n Readline is Copyright (C) 1989\-2023 Free Software Foundation, Inc. +.if t Readline is Copyright \(co 1989\-2023 Free Software Foundation, Inc. .SH DESCRIPTION .LP .B readline @@ -622,6 +622,10 @@ before returning when \fBaccept\-line\fP is executed. By default, history lines may be modified and retain individual undo lists across calls to \fBreadline\fP. .TP +.B search\-ignore\-case (Off) +If set to \fBOn\fP, readline performs incremental and non-incremental +history list searches in a case\-insensitive fashion. +.TP .B show\-all\-if\-ambiguous (Off) This alters the default behavior of the completion functions. If set to diff --git a/lib/readline/doc/rluser.texi b/lib/readline/doc/rluser.texi index cbcbb45c..f68a4d70 100644 --- a/lib/readline/doc/rluser.texi +++ b/lib/readline/doc/rluser.texi @@ -9,7 +9,7 @@ use these features. There is a document entitled "readline.texinfo" which contains both end-user and programmer documentation for the GNU Readline Library. -Copyright (C) 1988--2022 Free Software Foundation, Inc. +Copyright (C) 1988--2023 Free Software Foundation, Inc. Authored by Brian Fox and Chet Ramey. @@ -757,6 +757,12 @@ before returning when @code{accept-line} is executed. By default, history lines may be modified and retain individual undo lists across calls to @code{readline()}. The default is @samp{off}. +@item search-ignore-case +@vindex search-ignore-case +If set to @samp{on}, Readline performs incremental and non-incremental +history list searches in a case-insensitive fashion. +The default value is @samp{off}. + @item show-all-if-ambiguous @vindex show-all-if-ambiguous This alters the default behavior of the completion functions. If diff --git a/lib/readline/doc/version.texi b/lib/readline/doc/version.texi index 2b80d343..a72ee264 100644 --- a/lib/readline/doc/version.texi +++ b/lib/readline/doc/version.texi @@ -5,7 +5,7 @@ Copyright (C) 1988-2023 Free Software Foundation, Inc. @set EDITION 8.2 @set VERSION 8.2 -@set UPDATED 21 January 2023 -@set UPDATED-MONTH January 2023 +@set UPDATED 17 February 2023 +@set UPDATED-MONTH February 2023 -@set LASTCHANGE Sat Jan 21 17:10:44 EST 2023 +@set LASTCHANGE Fri Feb 17 11:03:17 EST 2023 diff --git a/lib/readline/histlib.h b/lib/readline/histlib.h index 29fc4d2e..ca698aca 100644 --- a/lib/readline/histlib.h +++ b/lib/readline/histlib.h @@ -34,6 +34,11 @@ : ((a)[0] == (b)[0]) && (strncmp ((a), (b), (n)) == 0)) #endif +#if !defined (HAVE_STRCASECMP) +#define strcasecmp(a,b) strcmp ((a), (b)) +#define strncasecmp(a, b, n) strncmp ((a), (b), (n)) +#endif + #ifndef savestring #define savestring(x) strcpy (xmalloc (1 + strlen (x)), (x)) #endif @@ -72,6 +77,7 @@ extern char *strchr (); #define NON_ANCHORED_SEARCH 0 #define ANCHORED_SEARCH 0x01 #define PATTERN_SEARCH 0x02 +#define CASEFOLD_SEARCH 0x04 /* Possible definitions for what style of writing the history file we want. */ #define HISTORY_APPEND 0 @@ -81,6 +87,7 @@ extern char *strchr (); /* histsearch.c */ extern int _hs_history_patsearch (const char *, int, int); +extern int _hs_history_search (const char *, int, int); /* history.c */ extern void _hs_replace_history_data (int, histdata_t *, histdata_t *); diff --git a/lib/readline/histsearch.c b/lib/readline/histsearch.c index 7e5b2501..b43ead1f 100644 --- a/lib/readline/histsearch.c +++ b/lib/readline/histsearch.c @@ -1,6 +1,6 @@ /* histsearch.c -- searching the history list. */ -/* Copyright (C) 1989, 1992-2009,2017,2021 Free Software Foundation, Inc. +/* Copyright (C) 1989, 1992-2009,2017,2021,2023 Free Software Foundation, Inc. This file contains the GNU History Library (History), a set of routines for managing the text of previously typed lines. @@ -47,6 +47,8 @@ #include "histlib.h" #include "xmalloc.h" +#include "rlmbutil.h" + /* The list of alternate characters that can delimit a history search string. */ char *history_search_delimiter_chars = (char *)NULL; @@ -57,7 +59,10 @@ static int history_search_internal (const char *, int, int); If DIRECTION < 0, then the search is through previous entries, else through subsequent. If ANCHORED is non-zero, the string must appear at the beginning of a history line, otherwise, the string - may appear anywhere in the line. If the string is found, then + may appear anywhere in the line. If PATSEARCH is non-zero, and fnmatch(3) + is available, fnmatch is used to match the string instead of a simple + string comparison. If IGNORECASE is set, the string comparison is + performed case-insensitively. If the string is found, then current_history () is the history entry, and the value of this function is the offset in the line of that history entry that the string was found in. Otherwise, nothing is changed, and a -1 is @@ -68,9 +73,10 @@ history_search_internal (const char *string, int direction, int flags) { int i, reverse; char *line; - int line_index; - size_t string_len; - int anchored, patsearch; + size_t string_len, line_len; + int line_index; /* can't be unsigned */ + int anchored, patsearch, igncase; + int found, mb_cur_max; HIST_ENTRY **the_history; /* local */ i = history_offset; @@ -81,6 +87,7 @@ history_search_internal (const char *string, int direction, int flags) #else patsearch = 0; #endif + igncase = (flags & CASEFOLD_SEARCH); /* Take care of trivial cases first. */ if (string == 0 || *string == '\0') @@ -92,6 +99,8 @@ history_search_internal (const char *string, int direction, int flags) if (reverse && (i >= history_length)) i = history_length - 1; + mb_cur_max = MB_CUR_MAX; + #define NEXT_LINE() do { if (reverse) i--; else i++; } while (0) the_history = history_list (); @@ -105,7 +114,7 @@ history_search_internal (const char *string, int direction, int flags) return (-1); line = the_history[i]->line; - line_index = strlen (line); + line_len = line_index = strlen (line); /* If STRING is longer than line, no match. */ if (patsearch == 0 && (string_len > line_index)) @@ -117,18 +126,27 @@ history_search_internal (const char *string, int direction, int flags) /* Handle anchored searches first. */ if (anchored == ANCHORED_SEARCH) { + found = 0; #if defined (HAVE_FNMATCH) if (patsearch) - { - if (fnmatch (string, line, 0) == 0) - { - history_offset = i; - return (0); - } - } + found = fnmatch (string, line, 0) == 0; else #endif - if (STREQN (string, line, string_len)) + if (igncase) + { +#if defined (HANDLE_MULTIBYTE) + if (mb_cur_max > 1) /* no rl_byte_oriented equivalent */ + found = _rl_mb_strcaseeqn (string, string_len, + line, line_len, + string_len, 0); + else +#endif + found = strncasecmp (string, line, string_len) == 0; + } + else + found = STREQN (string, line, string_len); + + if (found) { history_offset = i; return (0); @@ -141,55 +159,80 @@ history_search_internal (const char *string, int direction, int flags) /* Do substring search. */ if (reverse) { - line_index -= (patsearch == 0) ? string_len : 1; + size_t ll; + + ll = (patsearch == 0) ? string_len : 1; + line_index -= ll; + found = 0; while (line_index >= 0) { #if defined (HAVE_FNMATCH) if (patsearch) - { - if (fnmatch (string, line + line_index, 0) == 0) - { - history_offset = i; - return (line_index); - } - } + found = fnmatch (string, line + line_index, 0) == 0; else #endif - if (STREQN (string, line + line_index, string_len)) + if (igncase) + { +#if defined (HANDLE_MULTIBYTE) + if (mb_cur_max > 1) /* no rl_byte_oriented equivalent */ + found = _rl_mb_strcaseeqn (string, string_len, + line + line_index, ll, + string_len, 0); + else +#endif + found = strncasecmp (string, line + line_index, string_len) == 0; + } + else + found = STREQN (string, line + line_index, string_len); + + if (found) { history_offset = i; return (line_index); } line_index--; + ll++; } } else { register int limit; + size_t ll; + ll = line_len; limit = line_index - string_len + 1; line_index = 0; + found = 0; while (line_index < limit) { #if defined (HAVE_FNMATCH) if (patsearch) - { - if (fnmatch (string, line + line_index, 0) == 0) - { - history_offset = i; - return (line_index); - } - } + found = fnmatch (string, line + line_index, 0) == 0; else #endif - if (STREQN (string, line + line_index, string_len)) + if (igncase) + { +#if defined (HANDLE_MULTIBYTE) + if (mb_cur_max > 1) /* no rl_byte_oriented equivalent */ + found = _rl_mb_strcaseeqn (string, string_len, + line + line_index, ll, + string_len, 0); + else +#endif + found = strncasecmp (string, line + line_index, string_len) == 0; + } + else + found = STREQN (string, line + line_index, string_len); + + if (found) { history_offset = i; return (line_index); } line_index++; + ll--; } } NEXT_LINE (); @@ -267,6 +310,13 @@ history_search_prefix (const char *string, int direction) return (history_search_internal (string, direction, ANCHORED_SEARCH)); } +/* At some point, make this public for users of the history library. */ +int +_hs_history_search (const char *string, int direction, int flags) +{ + return (history_search_internal (string, direction, flags)); +} + /* Search for STRING in the history list. DIR is < 0 for searching backwards. POS is an absolute index into the history list at which point to begin searching. */ diff --git a/lib/readline/isearch.c b/lib/readline/isearch.c index 37561817..89c93386 100644 --- a/lib/readline/isearch.c +++ b/lib/readline/isearch.c @@ -59,6 +59,8 @@ char *_rl_isearch_terminators = (char *)NULL; _rl_search_cxt *_rl_iscxt = 0; +int _rl_search_case_fold = 0; + static int rl_search_history (int, int); static _rl_search_cxt *_rl_isearch_init (int); @@ -753,7 +755,27 @@ opcode_dispatch: /* Search the current line. */ while ((cxt->sflags & SF_REVERSE) ? (cxt->sline_index >= 0) : (cxt->sline_index < limit)) { - if (STREQN (cxt->search_string, cxt->sline + cxt->sline_index, cxt->search_string_index)) + int found; + + if (_rl_search_case_fold) + { +#if defined (HANDLE_MULTIBYTE) + if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + found = _rl_mb_strcaseeqn (cxt->search_string, + cxt->search_string_index, + cxt->sline + cxt->sline_index, + limit, + cxt->search_string_index, 0); + else + found = _rl_strnicmp (cxt->search_string, + cxt->sline + cxt->sline_index, + cxt->search_string_index) == 0; +#endif + } + else + found = STREQN (cxt->search_string, cxt->sline + cxt->sline_index, cxt->search_string_index); + + if (found) { cxt->sflags |= SF_FOUND; break; diff --git a/lib/readline/mbutil.c b/lib/readline/mbutil.c index 11cf2293..0844e286 100644 --- a/lib/readline/mbutil.c +++ b/lib/readline/mbutil.c @@ -521,3 +521,78 @@ _rl_find_prev_mbchar (const char *string, int seed, int flags) return ((seed == 0) ? seed : seed - 1); #endif } + +/* Compare the first N characters of S1 and S2 without regard to case. If + FLAGS&1, apply the mapping specified by completion-map-case and make + `-' and `_' equivalent. Returns 1 if the strings are equal. */ +int +_rl_mb_strcaseeqn (const char *s1, size_t l1, const char *s2, size_t l2, size_t n, int flags) +{ + size_t v1, v2; + mbstate_t ps1, ps2; + WCHAR_T wc1, wc2; + + memset (&ps1, 0, sizeof (mbstate_t)); + memset (&ps2, 0, sizeof (mbstate_t)); + + do + { + v1 = MBRTOWC(&wc1, s1, l1, &ps1); + v2 = MBRTOWC(&wc2, s2, l2, &ps2); + if (v1 == 0 && v2 == 0) + return 1; + else if (MB_INVALIDCH(v1) || MB_INVALIDCH(v2)) + { + int d; + d = _rl_to_lower (*s1) - _rl_to_lower (*s2); /* do byte comparison */ + if ((flags & 1) && (*s1 == '-' || *s1 == '_') && (*s2 == '-' || *s2 == '_')) + d = 0; /* case insensitive character mapping */ + if (d != 0) + return 0; + s1++; + s2++; + n--; + continue; + } + wc1 = towlower(wc1); + wc2 = towlower(wc2); + s1 += v1; + s2 += v1; + n -= v1; + if ((flags & 1) && (wc1 == L'-' || wc1 == L'_') && (wc2 == L'-' || wc2 == L'_')) + continue; + if (wc1 != wc2) + return 0; + } + while (n != 0); + + return 1; +} + +/* Return 1 if the multibyte characters pointed to by S1 and S2 are equal + without regard to case. If FLAGS&1, apply the mapping specified by + completion-map-case and make `-' and `_' equivalent. */ +int +_rl_mb_charcasecmp (const char *s1, mbstate_t *ps1, const char *s2, mbstate_t *ps2, int flags) +{ + int d; + size_t v1, v2; + wchar_t wc1, wc2; + + d = MB_CUR_MAX; + v1 = MBRTOWC(&wc1, s1, d, ps1); + v2 = MBRTOWC(&wc2, s2, d, ps2); + if (v1 == 0 && v2 == 0) + return 1; + else if (MB_INVALIDCH(v1) || MB_INVALIDCH(v2)) + { + if ((flags & 1) && (*s1 == '-' || *s1 == '_') && (*s2 == '-' || *s2 == '_')) + return 1; + return (_rl_to_lower (*s1) == _rl_to_lower (*s2)); + } + wc1 = towlower(wc1); + wc2 = towlower(wc2); + if ((flags & 1) && (wc1 == L'-' || wc1 == L'_') && (wc2 == L'-' || wc2 == L'_')) + return 1; + return (wc1 == wc2); +} diff --git a/lib/readline/rlmbutil.h b/lib/readline/rlmbutil.h index 42d47c3d..92ce05bd 100644 --- a/lib/readline/rlmbutil.h +++ b/lib/readline/rlmbutil.h @@ -1,6 +1,6 @@ /* rlmbutil.h -- utility functions for multibyte characters. */ -/* Copyright (C) 2001-2021 Free Software Foundation, Inc. +/* Copyright (C) 2001-2021,2023 Free Software Foundation, Inc. This file is part of the GNU Readline Library (Readline), a library for reading lines of text with interactive input and history editing. @@ -121,6 +121,9 @@ extern int _rl_is_mbchar_matched (const char *, int, int, char *, int); extern WCHAR_T _rl_char_value (const char *, int); extern int _rl_walphabetic (WCHAR_T); +extern int _rl_mb_strcaseeqn (const char *, size_t, const char *, size_t, size_t, int); +extern int _rl_mb_charcasecmp (const char *, mbstate_t *, const char *, mbstate_t *, int); + #define _rl_to_wupper(wc) (iswlower (wc) ? towupper (wc) : (wc)) #define _rl_to_wlower(wc) (iswupper (wc) ? towlower (wc) : (wc)) diff --git a/lib/readline/rlprivate.h b/lib/readline/rlprivate.h index 77a4d8c6..e97ea90e 100644 --- a/lib/readline/rlprivate.h +++ b/lib/readline/rlprivate.h @@ -475,6 +475,8 @@ extern int _rl_tropen (void); extern int _rl_abort_internal (void); extern int _rl_null_function (int, int); extern char *_rl_strindex (const char *, const char *); +extern int _rl_strcaseeqn (const char *, const char *, size_t, int); +extern int _rl_charcasecmp (int, int, int); extern int _rl_qsort_string_compare (char **, char **); extern int (_rl_uppercase_p) (int); extern int (_rl_lowercase_p) (int); @@ -544,6 +546,7 @@ extern int _rl_vi_cmd_modestr_len; extern char *_rl_isearch_terminators; extern _rl_search_cxt *_rl_iscxt; +extern int _rl_search_case_fold; /* macro.c */ extern char *_rl_executing_macro; diff --git a/lib/readline/search.c b/lib/readline/search.c index 43c66b1b..b7be876f 100644 --- a/lib/readline/search.c +++ b/lib/readline/search.c @@ -135,21 +135,23 @@ noninc_search_from_pos (char *string, int pos, int dir, int flags, int *ncp) RL_SETSTATE(RL_STATE_SEARCH); /* These functions return the match offset in the line; history_offset gives the matching line in the history list */ - if (flags & SF_PATTERN) + + sflags = 0; /* Non-anchored search */ + s = string; + if (*s == '^') { - s = string; - sflags = 0; /* Non-anchored search */ - if (*s == '^') - { - sflags |= ANCHORED_SEARCH; - s++; - } - ret = _hs_history_patsearch (s, dir, sflags); + sflags |= ANCHORED_SEARCH; + s++; } - else if (*string == '^') - ret = history_search_prefix (string + 1, dir); + + if (flags & SF_PATTERN) + ret = _hs_history_patsearch (s, dir, sflags); else - ret = history_search (string, dir); + { + if (_rl_search_case_fold) + sflags |= CASEFOLD_SEARCH; + ret = _hs_history_search (s, dir, sflags); + } RL_UNSETSTATE(RL_STATE_SEARCH); if (ncp) @@ -201,7 +203,7 @@ noninc_dosearch (char *string, int dir, int flags) make_history_line_current (entry); - if (_rl_enable_active_region && ((flags & SF_PATTERN) == 0) && ind > 0 && ind < rl_end) + if (_rl_enable_active_region && ((flags & SF_PATTERN) == 0) && ind >= 0 && ind < rl_end) { rl_point = ind; rl_mark = ind + strlen (string); diff --git a/lib/readline/util.c b/lib/readline/util.c index d481b856..b68abdc6 100644 --- a/lib/readline/util.c +++ b/lib/readline/util.c @@ -418,6 +418,48 @@ _rl_stricmp (const char *string1, const char *string2) } #endif /* !HAVE_STRCASECMP */ +/* Compare the first N characters of S1 and S2 without regard to case. If + FLAGS&1, apply the mapping specified by completion-map-case and make + `-' and `_' equivalent. Returns 1 if the strings are equal. */ +int +_rl_strcaseeqn(const char *s1, const char *s2, size_t n, int flags) +{ + int c1, c2; + int d; + + if ((flags & 1) == 0) + return (_rl_strnicmp (s1, s2, n) == 0); + + do + { + c1 = _rl_to_lower (*s1); + c2 = _rl_to_lower (*s2); + + d = c1 - c2; + if ((*s1 == '-' || *s1 == '_') && (*s2 == '-' || *s2 == '_')) + d = 0; /* case insensitive character mapping */ + if (d != 0) + return 0; + s1++; + s2++; + n--; + } + while (n != 0); + + return 1; +} + +/* Return 1 if the characters C1 and C2 are equal without regard to case. + If FLAGS&1, apply the mapping specified by completion-map-case and make + `-' and `_' equivalent. */ +int +_rl_charcasecmp (int c1, int c2, int flags) +{ + if ((flags & 1) && (c1 == '-' || c1 == '_') && (c2 == '-' || c2 == '_')) + return 1; + return ( _rl_to_lower (c1) == _rl_to_lower (c2)); +} + /* Stupid comparison routine for qsort () ing strings. */ int _rl_qsort_string_compare (char **s1, char **s2) diff --git a/lib/sh/Makefile.in b/lib/sh/Makefile.in index 9f0a7fa6..b343dd66 100644 --- a/lib/sh/Makefile.in +++ b/lib/sh/Makefile.in @@ -91,8 +91,8 @@ CSOURCES = clktck.c clock.c getcwd.c getenv.c oslib.c setlinebuf.c \ fmtulong.c fmtullong.c fmtumax.c shmatch.c strnlen.c \ strtoll.c strtoull.c strtoimax.c strtoumax.c memset.c strstr.c \ mktime.c strftime.c mbschr.c zcatfd.c zmapfd.c winsize.c eaccess.c \ - wcsdup.c fpurge.c zgetline.c mbscmp.c uconvert.c ufuncs.c \ - casemod.c dprintf.c input_avail.c mbscasecmp.c fnxform.c \ + wcsdup.c fpurge.c zgetline.c mbscmp.c mbsncmp.c uconvert.c \ + ufuncs.c casemod.c dprintf.c input_avail.c mbscasecmp.c fnxform.c \ strchrnul.c unicode.c wcswidth.c wcsnwidth.c shmbchar.c strdup.c \ strvis.c utf8.c random.c gettimeofday.c timers.c @@ -107,7 +107,7 @@ OBJECTS = clktck.o clock.o getenv.o oslib.o setlinebuf.o strnlen.o \ pathphys.o tmpfile.o stringlist.o stringvec.o spell.o shquote.o \ strtrans.o snprintf.o mailstat.o fmtulong.o \ fmtullong.o fmtumax.o zcatfd.o zmapfd.o winsize.o wcsdup.o \ - fpurge.o zgetline.o mbscmp.o uconvert.o ufuncs.o casemod.o \ + fpurge.o zgetline.o mbscmp.o mbsncmp.o uconvert.o ufuncs.o casemod.o \ input_avail.o mbscasecmp.o fnxform.o unicode.o shmbchar.o strvis.o \ utf8.o random.o gettimeofday.o timers.o wcsnwidth.o ${LIBOBJS} @@ -165,6 +165,7 @@ makepath.o: makepath.c mbscasecmp.o: mbscasecmp.c mbschr.o: mbschr.c mbscmp.o: mbscmp.c +mbsncmp.o: mbsncmp.c memset.o: memset.c mktime.o: mktime.c netconn.o: netconn.c @@ -247,6 +248,7 @@ makepath.o: ${BUILD_DIR}/config.h mbscasecmp.o: ${BUILD_DIR}/config.h mbschr.o: ${BUILD_DIR}/config.h mbscmp.o: ${BUILD_DIR}/config.h +mbsncmp.o: ${BUILD_DIR}/config.h memset.o: ${BUILD_DIR}/config.h mktime.o: ${BUILD_DIR}/config.h netconn.o: ${BUILD_DIR}/config.h @@ -607,6 +609,10 @@ mbscmp.o: ${topdir}/bashansi.h ${BASHINCDIR}/ansi_stdlib.h mbscmp.o: ${BASHINCDIR}/stdc.h mbscmp.o: ${topdir}/xmalloc.h +mbsncmp.o: ${topdir}/bashansi.h ${BASHINCDIR}/ansi_stdlib.h +mbsncmp.o: ${BASHINCDIR}/stdc.h +mbsncmp.o: ${topdir}/xmalloc.h + casemod.o: ${topdir}/bashansi.h ${BASHINCDIR}/ansi_stdlib.h casemod.o: ${BASHINCDIR}/stdc.h casemod.o: ${topdir}/xmalloc.h diff --git a/lib/sh/mbscmp.c b/lib/sh/mbscmp.c index a8d4bfd1..d86dbf17 100644 --- a/lib/sh/mbscmp.c +++ b/lib/sh/mbscmp.c @@ -26,6 +26,8 @@ #include #include +#include + extern int locale_utf8locale; extern int utf8_mbscmp (const char *, const char *); diff --git a/lib/sh/mbsncmp.c b/lib/sh/mbsncmp.c new file mode 100644 index 00000000..3b230aa1 --- /dev/null +++ b/lib/sh/mbsncmp.c @@ -0,0 +1,80 @@ +/* mbsncmp - multibyte string comparison. */ + +/* Copyright (C) 1995-2023 Free Software Foundation, Inc. + + This file is part of GNU Bash, the Bourne Again SHell. + + Bash is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Bash is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Bash. If not, see . +*/ + +#include + +#if !defined (HAVE_MBSNCMP) && defined (HANDLE_MULTIBYTE) + +#include +#include +#include + +#include + +extern int locale_utf8locale; + +/* Compare MBS1 and MBS2 up to N multibyte characters. */ +int +mbsncmp (const char *mbs1, const char *mbs2, size_t n) +{ + int len1, len2, mb_cur_max; + wchar_t c1, c2; + mbstate_t state1 = { 0 }, state2 = { 0 }; + + len1 = len2 = 0; + mb_cur_max = MB_CUR_MAX; + + if (n == 0) + return 0; + + do + { + len1 = mbrtowc (&c1, mbs1, mb_cur_max, &state1); + len2 = mbrtowc (&c2, mbs2, mb_cur_max, &state2); + + if (len1 == 0) + return len2 == 0 ? 0 : -1; + else if (len2 == 0) + return 1; + else if (len1 > 0 && len2 < 0) + return -1; + else if (len1 < 0 && len2 > 0) + return 1; + else if (len1 < 0 && len2 < 0) + { + len1 = strlen (mbs1); + len2 = strlen (mbs2); + return (len1 == len2 ? memcmp (mbs1, mbs2, len1) + : ((len1 < len2) ? (memcmp (mbs1, mbs2, len1) > 0 ? 1 : -1) + : (memcmp (mbs1, mbs2, len2) >= 0 ? 1 : -1))); + } + + mbs1 += len1; + mbs2 += len2; + n--; + if (c1 != c2) + break; + } + while (n > 0); + + return c1 - c2; +} + +#endif