🔒 Repository is read-only – file editing is disabled.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451
diff -Naur a/builtins/evalfile.c b/builtins/evalfile.c
--- a/builtins/evalfile.c 2024-09-06 21:42:40.000000000 +0200
+++ b/builtins/evalfile.c 2026-01-18 19:48:34.497910257 +0100
@@ -160,8 +160,10 @@
nr = read (fd, string, file_size);
if (nr >= 0)
string[nr] = '\0';
+#if 0
if (nr != file_size)
nr = -1; /* XXX - didn't get the whole file */
+#endif
}
else
nr = zmapfd (fd, &string, 0);
diff -Naur a/jobs.c b/jobs.c
--- a/jobs.c 2025-03-08 00:48:44.000000000 +0100
+++ b/jobs.c 2026-01-18 19:47:35.826051535 +0100
@@ -3538,7 +3538,7 @@
/* There aren't any dead jobs in the jobs table, but let's see if there's
one in bgpids. We can do this in posix mode because we'll remove any
one we find from the table, preserving existing semantics. */
- if (posixly_correct && (t = bgp_findone ()))
+ if (posixly_correct && (flags & JWAIT_WAITING) == 0 && (t = bgp_findone ()))
{
pid = t->pid;
r = t->status;
diff -Naur a/lib/glob/smatch.c b/lib/glob/smatch.c
--- a/lib/glob/smatch.c 2023-05-29 20:15:17.000000000 +0200
+++ b/lib/glob/smatch.c 2026-01-18 19:48:35.061877358 +0100
@@ -390,7 +390,7 @@
if (c1 == c2)
return 0;
- if (forcecoll == 0 && glob_asciirange && c1 <= UCHAR_MAX && c2 <= UCHAR_MAX)
+ if (forcecoll == 0 && glob_asciirange)
return ((int)(c1 - c2));
s1[0] = c1;
diff -Naur a/lib/readline/input.c b/lib/readline/input.c
--- a/lib/readline/input.c 2025-05-02 15:29:05.000000000 +0200
+++ b/lib/readline/input.c 2026-01-18 19:48:35.915941545 +0100
@@ -971,11 +971,11 @@
call the application's signal event hook. */
if (rl_signal_event_hook)
(*rl_signal_event_hook) ();
-#if defined (READLINE_CALLBACKS)
- else if (osig == SIGINT && (ostate & RL_STATE_CALLBACK) && (ostate & (RL_STATE_ISEARCH|RL_STATE_NSEARCH|RL_STATE_NUMERICARG)))
+ /* If the application's SIGINT handler returns, make sure we abort out of
+ searches and numeric arguments because we've freed necessary state. */
+ if (osig == SIGINT && (ostate & (RL_STATE_ISEARCH|RL_STATE_NSEARCH|RL_STATE_NUMERICARG)))
/* just these cases for now */
_rl_abort_internal ();
-#endif
}
}
diff -Naur a/lib/readline/input.c.orig b/lib/readline/input.c.orig
--- a/lib/readline/input.c.orig 1970-01-01 01:00:00.000000000 +0100
+++ b/lib/readline/input.c.orig 2025-05-02 15:29:05.000000000 +0200
@@ -0,0 +1,1056 @@
+/* input.c -- character input functions for readline. */
+
+/* Copyright (C) 1994-2025 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.
+
+ Readline 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.
+
+ Readline 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 Readline. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#define READLINE_LIBRARY
+
+#if defined (__TANDEM)
+# define _XOPEN_SOURCE_EXTENDED 1
+# define _TANDEM_SOURCE 1
+# include <floss.h>
+#endif
+
+/* These are needed to get the declaration of 'alarm' when including
+ <unistd.h>. I'm not sure it's needed, but the compiler might require it. */
+#if defined (__MINGW32__)
+# define __USE_MINGW_ALARM
+# define _POSIX
+#endif
+
+#if defined (HAVE_CONFIG_H)
+# include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <fcntl.h>
+#if defined (HAVE_SYS_FILE_H)
+# include <sys/file.h>
+#endif /* HAVE_SYS_FILE_H */
+
+#if defined (HAVE_UNISTD_H)
+# include <unistd.h>
+#endif /* HAVE_UNISTD_H */
+
+#if defined (HAVE_STDLIB_H)
+# include <stdlib.h>
+#else
+# include "ansi_stdlib.h"
+#endif /* HAVE_STDLIB_H */
+
+#include <signal.h>
+
+#include "posixselect.h"
+#include "posixtime.h"
+
+#if defined (FIONREAD_IN_SYS_IOCTL)
+# include <sys/ioctl.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+
+#if !defined (errno)
+extern int errno;
+#endif /* !errno */
+
+/* System-specific feature definitions and include files. */
+#include "rldefs.h"
+#include "rlmbutil.h"
+
+/* Some standard library routines. */
+#include "readline.h"
+
+#include "rlprivate.h"
+#include "rlshell.h"
+#include "xmalloc.h"
+
+/* What kind of non-blocking I/O do we have? */
+#if !defined (O_NDELAY) && defined (O_NONBLOCK)
+# define O_NDELAY O_NONBLOCK /* Posix style */
+#endif
+
+#if defined (HAVE_PSELECT) || defined (HAVE_SELECT)
+extern sigset_t _rl_orig_sigset;
+#endif
+
+/* Non-null means it is a pointer to a function to run while waiting for
+ character input. */
+rl_hook_func_t *rl_event_hook = (rl_hook_func_t *)NULL;
+
+/* A function to call if a read(2) is interrupted by a signal. */
+rl_hook_func_t *rl_signal_event_hook = (rl_hook_func_t *)NULL;
+
+/* A function to call when readline times out after a time is specified. */
+rl_hook_func_t *rl_timeout_event_hook = (rl_hook_func_t *)NULL;
+
+/* A function to replace _rl_input_available for applications using the
+ callback interface. */
+rl_hook_func_t *rl_input_available_hook = (rl_hook_func_t *)NULL;
+
+rl_getc_func_t *rl_getc_function = rl_getc;
+
+static int _keyboard_input_timeout = 100000; /* 0.1 seconds; it's in usec */
+
+static int ibuffer_space (void);
+static int rl_get_char (int *);
+static int rl_gather_tyi (void);
+
+/* Windows isatty returns true for every character device, including the null
+ device, so we need to perform additional checks. */
+#if defined (_WIN32) && !defined (__CYGWIN__)
+#include <io.h>
+#include <conio.h>
+#define WIN32_LEAN_AND_MEAN 1
+#include <windows.h>
+
+int
+win32_isatty (int fd)
+{
+ if (_isatty(fd))
+ {
+ HANDLE h;
+ DWORD ignored;
+
+ if ((h = (HANDLE) _get_osfhandle (fd)) == INVALID_HANDLE_VALUE)
+ {
+ errno = EBADF;
+ return 0;
+ }
+ if (GetConsoleMode (h, &ignored) != 0)
+ return 1;
+ }
+ errno = ENOTTY;
+ return 0;
+}
+
+#define isatty(x) win32_isatty(x)
+#endif
+
+/* Readline timeouts */
+
+/* We now define RL_TIMEOUT_USE_SELECT or RL_TIMEOUT_USE_SIGALRM in rlprivate.h */
+
+int rl_set_timeout (unsigned int, unsigned int);
+int rl_timeout_remaining (unsigned int *, unsigned int *);
+
+int _rl_timeout_init (void);
+int _rl_timeout_handle_sigalrm (void);
+#if defined (RL_TIMEOUT_USE_SELECT)
+int _rl_timeout_select (int, fd_set *, fd_set *, fd_set *, const struct timeval *, const sigset_t *);
+#endif
+
+static void _rl_timeout_handle (void);
+#if defined (RL_TIMEOUT_USE_SIGALRM)
+static int set_alarm (unsigned int *, unsigned int *);
+static void reset_alarm (void);
+#endif
+
+/* We implement timeouts as a future time using a supplied interval
+ (timeout_duration) from when the timeout is set (timeout_point).
+ That allows us to easily determine whether the timeout has occurred
+ and compute the time remaining until it does. */
+static struct timeval timeout_point;
+static struct timeval timeout_duration;
+
+/* **************************************************************** */
+/* */
+/* Character Input Buffering */
+/* */
+/* **************************************************************** */
+
+static int pop_index, push_index;
+static unsigned char ibuffer[512];
+static int ibuffer_len = sizeof (ibuffer) - 1;
+
+#define any_typein (push_index != pop_index)
+
+int
+_rl_any_typein (void)
+{
+ return any_typein;
+}
+
+int
+_rl_pushed_input_available (void)
+{
+ return (push_index != pop_index);
+}
+
+/* Return the amount of space available in the buffer for stuffing
+ characters. */
+static int
+ibuffer_space (void)
+{
+ if (pop_index > push_index)
+ return (pop_index - push_index - 1);
+ else
+ return (ibuffer_len - (push_index - pop_index));
+}
+
+/* Get a key from the buffer of characters to be read.
+ Return the key in KEY.
+ Result is non-zero if there was a key, or 0 if there wasn't. */
+static int
+rl_get_char (int *key)
+{
+ if (push_index == pop_index)
+ return (0);
+
+ *key = ibuffer[pop_index++];
+#if 0
+ if (pop_index >= ibuffer_len)
+#else
+ if (pop_index > ibuffer_len)
+#endif
+ pop_index = 0;
+
+ return (1);
+}
+
+/* Stuff KEY into the *front* of the input buffer.
+ Returns non-zero if successful, zero if there is
+ no space left in the buffer. */
+int
+_rl_unget_char (int key)
+{
+ if (ibuffer_space ())
+ {
+ pop_index--;
+ if (pop_index < 0)
+ pop_index = ibuffer_len;
+ ibuffer[pop_index] = key;
+ return (1);
+ }
+ return (0);
+}
+
+/* If a character is available to be read, then read it and stuff it into
+ IBUFFER. Otherwise, just return. Returns number of characters read
+ (0 if none available) and -1 on error (EIO). */
+static int
+rl_gather_tyi (void)
+{
+ int tty;
+ register int tem, result;
+ int chars_avail, k;
+ char input;
+#if defined (HAVE_PSELECT) || defined (HAVE_SELECT)
+ fd_set readfds, exceptfds;
+ struct timeval timeout;
+#endif
+
+ result = -1;
+ chars_avail = 0;
+ input = 0;
+ tty = fileno (rl_instream);
+
+ /* Move this up here to give it first shot, but it can't set chars_avail */
+ /* XXX - need rl_chars_available_hook? */
+ if (rl_input_available_hook)
+ {
+ result = (*rl_input_available_hook) ();
+ if (result == 0)
+ result = -1;
+ }
+
+#if defined (HAVE_PSELECT) || defined (HAVE_SELECT)
+ if (result == -1)
+ {
+ FD_ZERO (&readfds);
+ FD_ZERO (&exceptfds);
+ FD_SET (tty, &readfds);
+ FD_SET (tty, &exceptfds);
+ USEC_TO_TIMEVAL (_keyboard_input_timeout, timeout);
+#if defined (RL_TIMEOUT_USE_SELECT)
+ result = _rl_timeout_select (tty + 1, &readfds, (fd_set *)NULL, &exceptfds, &timeout, NULL);
+#else
+ result = select (tty + 1, &readfds, (fd_set *)NULL, &exceptfds, &timeout);
+#endif
+ if (result <= 0)
+ return 0; /* Nothing to read. */
+ }
+#endif
+
+#if defined (FIONREAD)
+ if (result == -1)
+ {
+ errno = 0;
+ result = ioctl (tty, FIONREAD, &chars_avail);
+ if (result == -1 && errno == EIO)
+ return -1;
+ if (result == -1)
+ chars_avail = 0;
+ }
+#endif
+
+#if defined (O_NDELAY)
+ if (result == -1)
+ {
+ tem = fcntl (tty, F_GETFL, 0);
+
+ fcntl (tty, F_SETFL, (tem | O_NDELAY));
+ chars_avail = read (tty, &input, 1);
+
+ fcntl (tty, F_SETFL, tem);
+ if (chars_avail == -1 && errno == EAGAIN)
+ return 0;
+ if (chars_avail == -1 && errno == EIO)
+ return -1;
+ if (chars_avail == 0) /* EOF */
+ {
+ rl_stuff_char (EOF);
+ return (0);
+ }
+ }
+#endif /* O_NDELAY */
+
+#if defined (__MINGW32__)
+ /* Use getch/_kbhit to check for available console input, in the same way
+ that we read it normally. */
+ if (result == -1)
+ {
+ chars_avail = isatty (tty) ? _kbhit () : 0;
+ result = 0;
+ }
+#endif
+
+ /* If there's nothing available, don't waste time trying to read
+ something. */
+ if (chars_avail <= 0)
+ return 0;
+
+ tem = ibuffer_space ();
+
+ if (chars_avail > tem)
+ chars_avail = tem;
+
+ /* One cannot read all of the available input. I can only read a single
+ character at a time, or else programs which require input can be
+ thwarted. If the buffer is larger than one character, I lose.
+ Damn! */
+ if (tem < ibuffer_len)
+ chars_avail = 0;
+
+ if (result != -1)
+ {
+ while (chars_avail--)
+ {
+ RL_CHECK_SIGNALS ();
+ k = (*rl_getc_function) (rl_instream);
+ if (rl_stuff_char (k) == 0)
+ break; /* some problem; no more room */
+ if (k == NEWLINE || k == RETURN)
+ break;
+ }
+ }
+ else
+ {
+ if (chars_avail)
+ rl_stuff_char (input);
+ }
+
+ return 1;
+}
+
+int
+rl_set_keyboard_input_timeout (int u)
+{
+ int o;
+
+ o = _keyboard_input_timeout;
+ if (u >= 0)
+ _keyboard_input_timeout = u;
+ return (o);
+}
+
+/* Is there input available to be read on the readline input file
+ descriptor? Only works if the system has select(2) or FIONREAD.
+ Uses the value of _keyboard_input_timeout as the timeout; if another
+ readline function wants to specify a timeout and not leave it up to
+ the user, it should use _rl_input_queued(timeout_value_in_microseconds)
+ instead. */
+int
+_rl_input_available (void)
+{
+#if defined (HAVE_PSELECT) || defined (HAVE_SELECT)
+ fd_set readfds, exceptfds;
+ struct timeval timeout;
+#endif
+#if !defined (HAVE_SELECT) && defined (FIONREAD)
+ int chars_avail;
+#endif
+ int tty;
+
+ if (rl_input_available_hook)
+ return (*rl_input_available_hook) ();
+
+ tty = fileno (rl_instream);
+
+#if defined (HAVE_PSELECT) || defined (HAVE_SELECT)
+ FD_ZERO (&readfds);
+ FD_ZERO (&exceptfds);
+ FD_SET (tty, &readfds);
+ FD_SET (tty, &exceptfds);
+ USEC_TO_TIMEVAL (_keyboard_input_timeout, timeout);
+# if defined (RL_TIMEOUT_USE_SELECT)
+ return (_rl_timeout_select (tty + 1, &readfds, (fd_set *)NULL, &exceptfds, &timeout, NULL) > 0);
+# else
+ return (select (tty + 1, &readfds, (fd_set *)NULL, &exceptfds, &timeout) > 0);
+# endif
+#else
+
+#if defined (FIONREAD)
+ if (ioctl (tty, FIONREAD, &chars_avail) == 0)
+ return (chars_avail);
+#endif
+
+#endif
+
+#if defined (__MINGW32__)
+ if (isatty (tty))
+ return (_kbhit ());
+#endif
+
+ return 0;
+}
+
+int
+_rl_nchars_available ()
+{
+ int chars_avail, fd, result;
+
+ chars_avail = 0;
+
+#if defined (FIONREAD)
+ fd = fileno (rl_instream);
+ errno = 0;
+ result = ioctl (fd, FIONREAD, &chars_avail);
+ if (result == -1 && errno == EIO)
+ return -1;
+#endif
+
+ return chars_avail;
+}
+
+int
+_rl_input_queued (int t)
+{
+ int old_timeout, r;
+
+ old_timeout = rl_set_keyboard_input_timeout (t);
+ r = _rl_input_available ();
+ rl_set_keyboard_input_timeout (old_timeout);
+ return r;
+}
+
+void
+_rl_insert_typein (int c)
+{
+ int key, t, i;
+ char *string;
+
+ i = key = 0;
+ string = (char *)xmalloc (ibuffer_len + 1);
+ string[i++] = (char) c;
+
+ while ((t = rl_get_char (&key)) &&
+ _rl_keymap[key].type == ISFUNC &&
+ _rl_keymap[key].function == rl_insert)
+ string[i++] = key;
+
+ if (t)
+ _rl_unget_char (key);
+
+ string[i] = '\0';
+ rl_insert_text (string);
+ xfree (string);
+}
+
+/* Add KEY to the buffer of characters to be read. Returns 1 if the
+ character was stuffed correctly; 0 otherwise. */
+int
+rl_stuff_char (int key)
+{
+ if (ibuffer_space () == 0)
+ return 0;
+
+ if (key == EOF)
+ {
+ key = NEWLINE;
+ rl_pending_input = EOF;
+ RL_SETSTATE (RL_STATE_INPUTPENDING);
+ }
+ ibuffer[push_index++] = key;
+#if 0
+ if (push_index >= ibuffer_len)
+#else
+ if (push_index > ibuffer_len)
+#endif
+ push_index = 0;
+
+ return 1;
+}
+
+/* Make C be the next command to be executed. */
+int
+rl_execute_next (int c)
+{
+ rl_pending_input = c;
+ RL_SETSTATE (RL_STATE_INPUTPENDING);
+ return 0;
+}
+
+/* Clear any pending input pushed with rl_execute_next() */
+int
+rl_clear_pending_input (void)
+{
+ rl_pending_input = 0;
+ RL_UNSETSTATE (RL_STATE_INPUTPENDING);
+ return 0;
+}
+
+/* **************************************************************** */
+/* */
+/* Timeout utility */
+/* */
+/* **************************************************************** */
+
+#if defined (RL_TIMEOUT_USE_SIGALRM)
+# if defined (HAVE_SETITIMER)
+
+static int
+set_alarm (unsigned int *secs, unsigned int *usecs)
+{
+ struct itimerval it;
+
+ timerclear (&it.it_interval);
+ timerset (&it.it_value, *secs, *usecs);
+ return setitimer (ITIMER_REAL, &it, NULL);
+}
+
+static void
+reset_alarm ()
+{
+ struct itimerval it;
+
+ timerclear (&it.it_interval);
+ timerclear (&it.it_value);
+ setitimer (ITIMER_REAL, &it, NULL);
+}
+# else /* !HAVE_SETITIMER */
+# if defined (__MINGW32_MAJOR_VERSION)
+/* mingw.org's MinGW doesn't have alarm(3). */
+unsigned int
+alarm (unsigned int seconds)
+{
+ return 0;
+}
+# endif /* __MINGW32_MAJOR_VERSION */
+
+static int
+set_alarm (unsigned int *secs, unsigned int *usecs)
+{
+ if (*secs == 0 || *usecs >= USEC_PER_SEC / 2)
+ (*secs)++;
+ *usecs = 0;
+
+ return alarm (*secs);
+}
+static void
+reset_alarm ()
+{
+ alarm (0);
+}
+# endif /* !HAVE_SETITIMER */
+#endif /* RL_TIMEOUT_USE_SIGALRM */
+
+/* Set a timeout which will be used for the next call of `readline
+ ()'. When (0, 0) are specified the timeout is cleared. */
+int
+rl_set_timeout (unsigned int secs, unsigned int usecs)
+{
+ timeout_duration.tv_sec = secs + usecs / USEC_PER_SEC;
+ timeout_duration.tv_usec = usecs % USEC_PER_SEC;
+
+ return 0;
+}
+
+/* Start measuring the time. Returns 0 on success. Returns -1 on
+ error. */
+int
+_rl_timeout_init (void)
+{
+ unsigned int secs, usecs;
+
+ /* Clear the timeout state of the previous edit */
+ RL_UNSETSTATE(RL_STATE_TIMEOUT);
+ timerclear (&timeout_point);
+
+ /* Return 0 when timeout is unset. */
+ if (timerisunset (&timeout_duration))
+ return 0;
+
+ /* Return -1 on gettimeofday error. */
+ if (gettimeofday(&timeout_point, 0) != 0)
+ {
+ timerclear (&timeout_point);
+ return -1;
+ }
+
+ secs = timeout_duration.tv_sec;
+ usecs = timeout_duration.tv_usec;
+
+#if defined (RL_TIMEOUT_USE_SIGALRM)
+ /* If select(2)/pselect(2) is unavailable, use SIGALRM. */
+ if (set_alarm (&secs, &usecs) < 0)
+ return -1;
+#endif
+
+ timeout_point.tv_sec += secs;
+ timeout_point.tv_usec += usecs;
+ if (timeout_point.tv_usec >= USEC_PER_SEC)
+ {
+ timeout_point.tv_sec++;
+ timeout_point.tv_usec -= USEC_PER_SEC;
+ }
+
+ return 0;
+}
+
+/* Get the remaining time until the scheduled timeout. Returns -1 on
+ error or no timeout set with secs and usecs unchanged. Returns 0
+ on an expired timeout with secs and usecs unchanged. Returns 1
+ when the timeout has not yet expired. The remaining time is stored
+ in secs and usecs. When NULL is specified to either of the
+ arguments, just the expiration is tested. */
+int
+rl_timeout_remaining (unsigned int *secs, unsigned int *usecs)
+{
+ struct timeval current_time;
+
+ /* Return -1 when timeout is unset. */
+ if (timerisunset (&timeout_point))
+ {
+ errno = 0;
+ return -1;
+ }
+
+ /* Return -1 on error. errno is set by gettimeofday. */
+ if (gettimeofday(¤t_time, 0) != 0)
+ return -1;
+
+ /* Return 0 when timeout has already expired. */
+ /* could use timercmp (&timeout_point, ¤t_time, <) here */
+ if (current_time.tv_sec > timeout_point.tv_sec ||
+ (current_time.tv_sec == timeout_point.tv_sec &&
+ current_time.tv_usec >= timeout_point.tv_usec))
+ return 0;
+
+ if (secs && usecs)
+ {
+ *secs = timeout_point.tv_sec - current_time.tv_sec;
+ *usecs = timeout_point.tv_usec - current_time.tv_usec;
+ if (timeout_point.tv_usec < current_time.tv_usec)
+ {
+ (*secs)--;
+ *usecs += USEC_PER_SEC;
+ }
+ }
+
+ return 1;
+}
+
+/* This should only be called if RL_TIMEOUT_USE_SELECT is defined. */
+
+#if defined (RL_TIMEOUT_USE_SELECT)
+int
+_rl_timeout_select (int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const struct timeval *timeout, const sigset_t *sigmask)
+{
+ int result;
+#if defined (HAVE_PSELECT)
+ struct timespec ts;
+#else
+ sigset_t origmask;
+ struct timeval tv;
+#endif
+ int tmout_status;
+ struct timeval tmout;
+ unsigned int sec, usec;
+
+ /* When the remaining time for rl_timeout is shorter than the
+ keyboard input timeout, replace `timeout' with the remaining time
+ for `rl_timeout' and set `tmout_status = 1'. */
+ tmout_status = rl_timeout_remaining (&sec, &usec);
+ tmout.tv_sec = sec;
+ tmout.tv_usec = usec;
+
+ if (tmout_status == 0)
+ _rl_timeout_handle ();
+ else if (tmout_status == 1)
+ {
+ if (timeout == NULL || timercmp (&tmout, timeout, <))
+ timeout = &tmout;
+ else
+ tmout_status = -1;
+ }
+
+#if defined (HAVE_PSELECT)
+ if (timeout)
+ {
+ TIMEVAL_TO_TIMESPEC (timeout, &ts);
+ result = pselect (nfds, readfds, writefds, exceptfds, &ts, sigmask);
+ }
+ else
+ result = pselect (nfds, readfds, writefds, exceptfds, NULL, sigmask);
+#else
+ if (sigmask)
+ sigprocmask (SIG_SETMASK, sigmask, &origmask);
+
+ if (timeout)
+ {
+ tv.tv_sec = timeout->tv_sec;
+ tv.tv_usec = timeout->tv_usec;
+ result = select (nfds, readfds, writefds, exceptfds, &tv);
+ }
+ else
+ result = select (nfds, readfds, writefds, exceptfds, NULL);
+
+ if (sigmask)
+ sigprocmask (SIG_SETMASK, &origmask, NULL);
+#endif
+
+ if (tmout_status == 1 && result == 0)
+ _rl_timeout_handle ();
+
+ return result;
+}
+#endif
+
+static void
+_rl_timeout_handle ()
+{
+ if (rl_timeout_event_hook)
+ (*rl_timeout_event_hook) ();
+
+ RL_SETSTATE(RL_STATE_TIMEOUT);
+ _rl_abort_internal ();
+}
+
+int
+_rl_timeout_handle_sigalrm ()
+{
+#if defined (RL_TIMEOUT_USE_SIGALRM)
+ if (timerisunset (&timeout_point))
+ return -1;
+
+ /* Reset `timeout_point' to the current time to ensure that later
+ calls of `rl_timeout_pending ()' return 0 (timeout expired). */
+ if (gettimeofday(&timeout_point, 0) != 0)
+ timerclear (&timeout_point);
+
+ reset_alarm ();
+
+ _rl_timeout_handle ();
+#endif
+ return -1;
+}
+/* **************************************************************** */
+/* */
+/* Character Input */
+/* */
+/* **************************************************************** */
+
+/* Read a key, including pending input. */
+int
+rl_read_key (void)
+{
+ int c, r;
+
+ if (rl_pending_input)
+ {
+ c = rl_pending_input; /* XXX - cast to unsigned char if > 0? */
+ rl_clear_pending_input ();
+ }
+ else
+ {
+ /* If input is coming from a macro, then use that. */
+ if (c = _rl_next_macro_key ())
+ return ((unsigned char)c);
+
+ /* If the user has an event function, then call it periodically. */
+ if (rl_event_hook)
+ {
+ while (rl_event_hook)
+ {
+ if (rl_get_char (&c) != 0)
+ break;
+
+ if ((r = rl_gather_tyi ()) < 0) /* XXX - EIO */
+ {
+ rl_done = 1;
+ RL_SETSTATE (RL_STATE_DONE);
+ return (errno == EIO ? (RL_ISSTATE (RL_STATE_READCMD) ? READERR : EOF) : '\n');
+ }
+ else if (r > 0) /* read something */
+ continue;
+
+ RL_CHECK_SIGNALS ();
+ if (rl_done) /* XXX - experimental */
+ return ('\n');
+ (*rl_event_hook) ();
+ }
+ }
+ else
+ {
+ if (rl_get_char (&c) == 0)
+ c = (*rl_getc_function) (rl_instream);
+/* fprintf(stderr, "rl_read_key: calling RL_CHECK_SIGNALS: _rl_caught_signal = %d\r\n", _rl_caught_signal); */
+ RL_CHECK_SIGNALS ();
+ }
+ }
+
+ return (c);
+}
+
+int
+rl_getc (FILE *stream)
+{
+ int result, ostate, osig;
+ unsigned char c;
+ int fd;
+#if defined (HAVE_PSELECT) || defined (HAVE_SELECT)
+ sigset_t empty_set;
+ fd_set readfds;
+#endif
+
+ fd = fileno (stream);
+ while (1)
+ {
+ osig = _rl_caught_signal;
+ ostate = rl_readline_state;
+
+ RL_CHECK_SIGNALS ();
+
+#if defined (READLINE_CALLBACKS)
+ /* Do signal handling post-processing here, but just in callback mode
+ for right now because the signal cleanup can change some of the
+ callback state, and we need to either let the application have a
+ chance to react or abort some current operation that gets cleaned
+ up by rl_callback_sigcleanup(). If not, we'll just run through the
+ loop again. */
+ if (osig != 0 && (ostate & RL_STATE_CALLBACK))
+ goto postproc_signal;
+#endif
+
+ /* We know at this point that _rl_caught_signal == 0 */
+
+#if defined (__MINGW32__)
+ if (isatty (fd))
+ return (_getch ()); /* "There is no error return." */
+#endif
+ result = 0;
+#if defined (HAVE_PSELECT) || defined (HAVE_SELECT)
+ /* At this point, if we have pselect, we're using select/pselect for the
+ timeouts. We handled MinGW above. */
+ FD_ZERO (&readfds);
+ FD_SET (fd, &readfds);
+# if defined (HANDLE_SIGNALS)
+ result = _rl_timeout_select (fd + 1, &readfds, NULL, NULL, NULL, &_rl_orig_sigset);
+# else
+ sigemptyset (&empty_set);
+ sigprocmask (SIG_BLOCK, (sigset_t *)NULL, &empty_set);
+ result = _rl_timeout_select (fd + 1, &readfds, NULL, NULL, NULL, &empty_set);
+# endif /* HANDLE_SIGNALS */
+ if (result == 0)
+ _rl_timeout_handle (); /* check the timeout */
+#endif
+ if (result >= 0)
+ result = read (fd, &c, sizeof (unsigned char));
+
+ if (result == sizeof (unsigned char))
+ return (c);
+
+ /* If zero characters are returned, then the file that we are
+ reading from is empty! Return EOF in that case. */
+ if (result == 0)
+ return (EOF);
+
+#if defined (__BEOS__)
+ if (errno == EINTR)
+ continue;
+#endif
+
+#if defined (EWOULDBLOCK)
+# define X_EWOULDBLOCK EWOULDBLOCK
+#else
+# define X_EWOULDBLOCK -99
+#endif
+
+#if defined (EAGAIN)
+# define X_EAGAIN EAGAIN
+#else
+# define X_EAGAIN -99
+#endif
+
+ if (errno == X_EWOULDBLOCK || errno == X_EAGAIN)
+ {
+ if (sh_unset_nodelay_mode (fd) < 0)
+ return (EOF);
+ continue;
+ }
+
+#undef X_EWOULDBLOCK
+#undef X_EAGAIN
+
+/* fprintf(stderr, "rl_getc: result = %d errno = %d\n", result, errno); */
+
+ /* Handle errors here. */
+ osig = _rl_caught_signal;
+ ostate = rl_readline_state;
+
+ /* If the error that we received was EINTR, then try again,
+ this is simply an interrupted system call to read (). We allow
+ the read to be interrupted if we caught SIGHUP, SIGTERM, or any
+ of the other signals readline treats specially. If the
+ application sets an event hook, call it for other signals.
+ Otherwise (not EINTR), some error occurred, also signifying EOF. */
+ if (errno != EINTR)
+ return (RL_ISSTATE (RL_STATE_READCMD) ? READERR : EOF);
+ /* fatal signals of interest */
+#if defined (SIGHUP)
+ else if (_rl_caught_signal == SIGHUP || _rl_caught_signal == SIGTERM)
+#else
+ else if (_rl_caught_signal == SIGTERM)
+#endif
+ return (RL_ISSTATE (RL_STATE_READCMD) ? READERR : EOF);
+ /* keyboard-generated signals of interest */
+#if defined (SIGQUIT)
+ else if (_rl_caught_signal == SIGINT || _rl_caught_signal == SIGQUIT)
+#else
+ else if (_rl_caught_signal == SIGINT)
+#endif
+ RL_CHECK_SIGNALS ();
+#if defined (SIGTSTP)
+ else if (_rl_caught_signal == SIGTSTP)
+ RL_CHECK_SIGNALS ();
+#endif
+ /* non-keyboard-generated signals of interest */
+#if defined (SIGWINCH)
+ else if (_rl_caught_signal == SIGWINCH)
+ RL_CHECK_SIGNALS ();
+#endif /* SIGWINCH */
+#if defined (SIGALRM)
+ else if (_rl_caught_signal == SIGALRM
+# if defined (SIGVTALRM)
+ || _rl_caught_signal == SIGVTALRM
+# endif
+ )
+ RL_CHECK_SIGNALS ();
+#endif /* SIGALRM */
+
+postproc_signal:
+ /* POSIX says read(2)/pselect(2)/select(2) don't return EINTR for any
+ reason other than being interrupted by a signal, so we can safely
+ call the application's signal event hook. */
+ if (rl_signal_event_hook)
+ (*rl_signal_event_hook) ();
+#if defined (READLINE_CALLBACKS)
+ else if (osig == SIGINT && (ostate & RL_STATE_CALLBACK) && (ostate & (RL_STATE_ISEARCH|RL_STATE_NSEARCH|RL_STATE_NUMERICARG)))
+ /* just these cases for now */
+ _rl_abort_internal ();
+#endif
+ }
+}
+
+#if defined (HANDLE_MULTIBYTE)
+/* read multibyte char */
+int
+_rl_read_mbchar (char *mbchar, int size)
+{
+ int mb_len, c;
+ size_t mbchar_bytes_length;
+ WCHAR_T wc;
+ mbstate_t ps, ps_back;
+
+ memset(&ps, 0, sizeof (mbstate_t));
+ memset(&ps_back, 0, sizeof (mbstate_t));
+
+ mb_len = 0;
+ while (mb_len < size)
+ {
+ c = (mb_len == 0) ? _rl_bracketed_read_key () : rl_read_key ();
+
+ if (c < 0)
+ break;
+
+ mbchar[mb_len++] = c;
+
+ mbchar_bytes_length = MBRTOWC (&wc, mbchar, mb_len, &ps);
+ if (mbchar_bytes_length == (size_t)(-1))
+ break; /* invalid byte sequence for the current locale */
+ else if (mbchar_bytes_length == (size_t)(-2))
+ {
+ /* shorted bytes */
+ ps = ps_back;
+ continue;
+ }
+ else if (mbchar_bytes_length == 0)
+ {
+ mbchar[0] = '\0'; /* null wide character */
+ mb_len = 1;
+ break;
+ }
+ else if (mbchar_bytes_length > (size_t)(0))
+ break;
+ }
+
+ return mb_len;
+}
+
+/* Read a multibyte-character string whose first character is FIRST into
+ the buffer MB of length MLEN. Returns the last character read, which
+ may be FIRST. Used by the search functions, among others. Very similar
+ to _rl_read_mbchar. */
+int
+_rl_read_mbstring (int first, char *mb, int mlen)
+{
+ int i, c, n;
+ mbstate_t ps;
+
+ c = first;
+ memset (mb, 0, mlen);
+ for (i = 0; c >= 0 && i < mlen; i++)
+ {
+ mb[i] = (char)c;
+ memset (&ps, 0, sizeof (mbstate_t));
+ n = _rl_get_char_len (mb, &ps);
+ if (n == -2)
+ {
+ /* Read more for multibyte character */
+ RL_SETSTATE (RL_STATE_MOREINPUT);
+ c = rl_read_key ();
+ RL_UNSETSTATE (RL_STATE_MOREINPUT);
+ }
+ else
+ break;
+ }
+ return c;
+}
+#endif /* HANDLE_MULTIBYTE */
diff -Naur a/lib/readline/isearch.c b/lib/readline/isearch.c
--- a/lib/readline/isearch.c 2025-06-05 15:22:11.000000000 +0200
+++ b/lib/readline/isearch.c 2026-01-18 19:48:35.916561331 +0100
@@ -889,12 +889,14 @@
int
_rl_isearch_cleanup (_rl_search_cxt *cxt, int r)
{
+ RL_UNSETSTATE(RL_STATE_ISEARCH);
+ if (cxt == 0)
+ return (r != 0);
+
+ _rl_iscxt = 0;
if (r >= 0)
_rl_isearch_fini (cxt);
_rl_scxt_dispose (cxt, 0);
- _rl_iscxt = 0;
-
- RL_UNSETSTATE(RL_STATE_ISEARCH);
return (r != 0);
}
diff -Naur a/lib/sh/anonfile.c b/lib/sh/anonfile.c
--- a/lib/sh/anonfile.c 2024-07-09 17:41:57.000000000 +0200
+++ b/lib/sh/anonfile.c 2026-01-18 19:48:33.934715578 +0100
@@ -25,7 +25,7 @@
#endif
#include <bashtypes.h>
-#if defined (HAVE_MEMFD_CREATE) || defined (HAVE_SHM_OPEN) || defined (HAVE_SHM_MKSTEMP)
+#if defined (HAVE_MEMFD_CREATE) || defined (HAVE_SHM_MKSTEMP)
# include <sys/mman.h>
#endif
#include <filecntl.h>
@@ -41,17 +41,7 @@
# define MFD_NOEXEC_SEAL 0
#endif
-#if defined (HAVE_SHM_OPEN)
-#ifndef O_NOFOLLOW
-# define O_NOFOLLOW 0
-#endif
-
-static int
-anonshmunlink (const char *fn)
-{
- return (shm_unlink (fn));
-}
-
+#if defined (HAVE_SHM_MKSTEMP)
static int
anonshmopen (const char *name, int flags, char **fn)
{
@@ -62,35 +52,14 @@
if (fn)
*fn = 0;
-#if defined (HAVE_SHM_MKSTEMP)
fname = savestring ("/shm-XXXXXXXXXX");
fd = shm_mkstemp (fname);
if (fd < 0)
- free (fname);
-#endif
-
- if (fd < 0)
- {
- fname = sh_mktmpname (name, flags);
- fd = shm_open (fname, O_RDWR|O_CREAT|O_EXCL|O_NOFOLLOW, 0600);
- }
-
- if (fd < 0)
{
free (fname);
return fd;
}
- if (shm_unlink (fname) < 0)
- {
- int o;
- o = errno;
- free (fname);
- close (fd);
- errno = o;
- return -1;
- }
-
if (fn)
*fn = fname;
else
@@ -122,7 +91,7 @@
/* Heuristic */
flag = (name && *name == '/') ? MT_TEMPLATE : MT_USETMPDIR;
-#if defined (HAVE_SHM_OPEN)
+#if defined (HAVE_SHM_MKSTEMP)
fd = anonshmopen (name, flag, fn);
if (fd >= 0)
return fd; /* anonshmopen sets *FN appropriately */
diff -Naur a/lib/sh/strtrans.c b/lib/sh/strtrans.c
--- a/lib/sh/strtrans.c 2023-10-13 17:57:46.000000000 +0200
+++ b/lib/sh/strtrans.c 2026-01-18 19:48:35.631633511 +0100
@@ -55,7 +55,7 @@
const char *s;
unsigned long v;
size_t clen;
- int mb_cur_max;
+ size_t mb_cur_max;
#if defined (HANDLE_MULTIBYTE)
wchar_t wc;
#endif
@@ -63,7 +63,7 @@
if (string == 0 || *string == '\0')
return ((char *)0);
- mb_cur_max = MB_CUR_MAX;
+ mb_cur_max = locale_mb_cur_max;
#if defined (HANDLE_MULTIBYTE)
temp = 4*len + 4;
if (temp < 12)
@@ -79,10 +79,14 @@
{
clen = 1;
#if defined (HANDLE_MULTIBYTE)
- if ((locale_utf8locale && (c & 0x80)) ||
- (locale_utf8locale == 0 && mb_cur_max > 0 && is_basic (c) == 0))
+ /* We read an entire multibyte character at a time if we are in a
+ locale where a backslash can possibly appear as part of a
+ multibyte character. UTF-8 encodings prohibit this. */
+ if (locale_utf8locale == 0 && mb_cur_max > 1 && is_basic (c) == 0)
{
clen = mbrtowc (&wc, s - 1, mb_cur_max, 0);
+ if (MB_NULLWCH (clen))
+ break; /* it apparently can happen */
if (MB_INVALIDCH (clen))
clen = 1;
}
@@ -227,30 +231,24 @@
{
char *r, *ret;
const char *s;
- size_t l, rsize;
unsigned char c;
+#if defined (HANDLE_MULTIBYTE)
size_t clen;
int b;
-#if defined (HANDLE_MULTIBYTE)
wchar_t wc;
+ DECLARE_MBSTATE;
#endif
if (str == 0 || *str == 0)
return ((char *)0);
- l = strlen (str);
- rsize = 4 * l + 4;
- r = ret = (char *)xmalloc (rsize);
+ r = ret = (char *)xmalloc (4 * strlen (str) + 4);
*r++ = '$';
*r++ = '\'';
for (s = str; c = *s; s++)
{
- b = 1; /* 1 == add backslash; 0 == no backslash */
- l = 1;
- clen = 1;
-
switch (c)
{
case ESC: c = 'E'; break;
@@ -266,39 +264,42 @@
break;
default:
#if defined (HANDLE_MULTIBYTE)
- b = is_basic (c);
- /* XXX - clen comparison to 0 is dicey */
- if ((b == 0 && ((clen = mbrtowc (&wc, s, MB_CUR_MAX, 0)) < 0 || MB_INVALIDCH (clen) || iswprint (wc) == 0)) ||
- (b == 1 && ISPRINT (c) == 0))
-#else
- if (ISPRINT (c) == 0)
-#endif
+ if ((locale_utf8locale && (c & 0x80)) ||
+ (locale_utf8locale == 0 && locale_mb_cur_max > 1 && is_basic (c) == 0))
{
- *r++ = '\\';
- *r++ = TOCHAR ((c >> 6) & 07);
- *r++ = TOCHAR ((c >> 3) & 07);
- *r++ = TOCHAR (c & 07);
- continue;
+ clen = mbrtowc (&wc, s, locale_mb_cur_max, &state);
+ if (MB_NULLWCH (clen))
+ goto quote_end;
+ if (MB_INVALIDCH (clen))
+ INITIALIZE_MBSTATE;
+ else if (iswprint (wc))
+ {
+ for (b = 0; b < (int)clen; b++)
+ *r++ = (unsigned char)s[b];
+ s += clen - 1; /* -1 because of the increment above */
+ continue;
+ }
}
- l = 0;
- break;
+ else
+#endif
+ if (ISPRINT (c))
+ {
+ *r++ = c;
+ continue;
+ }
+
+ *r++ = '\\';
+ *r++ = TOCHAR ((c >> 6) & 07);
+ *r++ = TOCHAR ((c >> 3) & 07);
+ *r++ = TOCHAR (c & 07);
+ continue;
}
- if (b == 0 && clen == 0)
- break;
- if (l)
- *r++ = '\\';
-
- if (clen == 1)
- *r++ = c;
- else
- {
- for (b = 0; b < (int)clen; b++)
- *r++ = (unsigned char)s[b];
- s += clen - 1; /* -1 because of the increment above */
- }
+ *r++ = '\\';
+ *r++ = c;
}
+quote_end:
*r++ = '\'';
*r = '\0';
if (rlen)
@@ -348,7 +349,8 @@
for (s = string; c = *s; s++)
{
#if defined (HANDLE_MULTIBYTE)
- if (is_basic (c) == 0)
+ if ((locale_utf8locale && (c & 0x80)) ||
+ (locale_utf8locale == 0 && locale_mb_cur_max > 1 && is_basic (c) == 0))
return (ansic_wshouldquote (s));
#endif
if (ISPRINT (c) == 0)
diff -Naur a/patchlevel.h b/patchlevel.h
--- a/patchlevel.h 2025-03-17 16:47:32.000000000 +0100
+++ b/patchlevel.h 2026-01-18 19:48:35.917001398 +0100
@@ -25,6 +25,6 @@
regexp `^#define[ ]*PATCHLEVEL', since that's what support/mkversion.sh
looks for to find the patch level (for the sccs version string). */
-#define PATCHLEVEL 0
+#define PATCHLEVEL 9
#endif /* _PATCHLEVEL_H_ */
diff -Naur a/subst.c b/subst.c
--- a/subst.c 2025-05-18 21:00:13.000000000 +0200
+++ b/subst.c 2026-01-18 19:48:35.346475201 +0100
@@ -206,6 +206,8 @@
extern int singlequote_translations;
extern int extended_quote;
+extern REDIRECT *exec_redirection_undo_list, *redirection_undo_list;
+
#if !defined (HAVE_WCSDUP) && defined (HANDLE_MULTIBYTE)
extern wchar_t *wcsdup (const wchar_t *);
#endif
@@ -3795,9 +3797,9 @@
#define EXP_CHAR(s) (s == '$' || s == '`' || s == CTLESC || s == '~')
#endif
-/* We don't perform process substitution in arithmetic expressions, so don't
- bother checking for it. */
-#define ARITH_EXP_CHAR(s) (s == '$' || s == '`' || s == CTLESC || s == '~')
+/* We don't perform process substitution or tilde expansion in arithmetic
+ expressions, so don't bother checking for them. */
+#define ARITH_EXP_CHAR(s) (s == '$' || s == '`' || s == CTLESC)
/* If there are any characters in STRING that require full expansion,
then call FUNC to expand STRING; otherwise just perform quote
@@ -7000,6 +7002,11 @@
add_unwind_protect (uw_restore_pipestatus_array, psa);
}
#endif
+
+ unwind_protect_pointer (redirection_undo_list);
+ redirection_undo_list = NULL;
+ unwind_protect_pointer (exec_redirection_undo_list);
+ exec_redirection_undo_list = NULL;
subst_assign_varlist = 0;
@@ -12215,6 +12222,14 @@
*r++ = '\\';
break;
}
+#if defined (ARRAY_VARS)
+ /* The only special characters that matter here are []~, since those
+ are backslash-quoted in expand_array_subscript but not dequoted
+ by the statement following this one. */
+ if ((quoted & Q_ARITH) && (c == LBRACK || c == RBRACK || c == '~'))
+ ; /* placeholder here */
+ else
+#endif
if (((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) || dquote) && (sh_syntaxtab[c] & CBSDQUOTE) == 0)
*r++ = '\\';
/* FALLTHROUGH */
diff -Naur a/tests/quotearray.right b/tests/quotearray.right
--- a/tests/quotearray.right 2022-11-10 16:39:56.000000000 +0100
+++ b/tests/quotearray.right 2026-01-18 19:48:34.215387874 +0100
@@ -44,7 +44,7 @@
foo
0
0
-./quotearray1.sub: line 68: 0\],b\[1: arithmetic syntax error: invalid arithmetic operator (error token is "\],b\[1")
+./quotearray1.sub: line 68: 0],b[1: arithmetic syntax error: invalid arithmetic operator (error token is "],b[1")
declare -a array
0
0
diff -Naur a/trap.c b/trap.c
--- a/trap.c 2024-12-19 17:35:49.000000000 +0100
+++ b/trap.c 2026-01-18 19:48:34.778403175 +0100
@@ -964,6 +964,7 @@
original_signals[sig] = SIG_DFL; /* XXX */
set_signal_handler (sig, SIG_DFL);
change_signal (sig, (char *)DEFAULT_SIG);
+ sigmodes[sig] &= ~SIG_TRAPPED; /* no longer trapped */
return;
}