- Accommodate macOS dynamic pipe sizing, avoiding hangs.
- Modify the readline redisplay code to use a buffer offset instead of
the physical prompt length in a multibyte locale to determine whether
to reprint the prompt from column 0 because the cursor is before the
last invisible character in the prompt string.
- Fix readline crash with a prompt that exceeds 256 wrapped lines.
Fix readline redisplay code to print the entire prompt if the first
few characters are identical but part of a terminal escape sequence
and the prompt needs to be redrawn.
- Work around systems including bytes between 128 and 255 in the
isalpha(3) set, which causes bash to potentially include them in
identifier names.
- If readline handles a SIGWINCH and updates its idea of the screen
dimensions, it needs to recompute the columns where the prompt wraps
lines every time, not just when the screen width decreases.
Signed-off-by: Wei-Ting Yang <redacted>
PKG_NAME:=bash
PKG_VERSION:=5.3
-PKG_RELEASE:=4
+PKG_RELEASE:=5
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=@GNU/bash
--- /dev/null
+From a5f4367d2aaabfcad67eb7382bf83ee0aead1f35 Mon Sep 17 00:00:00 2001
+From: Chet Ramey <chet.ramey@case.edu>
+Date: Tue, 15 Sep 2026 11:12:23 -0400
+Subject: Bash-5.3 patch 16: accommodate macOS dynamic pipe sizing, avoiding
+ hangs
+
+--- a/configure
++++ b/configure
+@@ -23133,7 +23133,7 @@ hpux*) LOCAL_CFLAGS="-DHPUX -DTGETENT_B
+ dgux*) LOCAL_CFLAGS=-D_DGUX_SOURCE; LOCAL_LIBS=-ldgc ;;
+ isc*) LOCAL_CFLAGS=-Disc386 ;;
+ rhapsody*) LOCAL_CFLAGS=-DRHAPSODY ;;
+-darwin*) LOCAL_CFLAGS=-DMACOSX ;;
++darwin*) LOCAL_CFLAGS="-DMACOSX -DPIPESIZE_DYNAMIC" ;;
+ sco3.2v5*) LOCAL_CFLAGS="-b elf -DWAITPID_BROKEN -DPATH_MAX=1024" ;;
+ sco3.2v4*) LOCAL_CFLAGS="-DMUST_UNBLOCK_CHLD -DPATH_MAX=1024" ;;
+ sco3.2*) LOCAL_CFLAGS=-DMUST_UNBLOCK_CHLD ;;
+--- a/configure.ac
++++ b/configure.ac
+@@ -1209,7 +1209,7 @@ hpux*) LOCAL_CFLAGS="-DHPUX -DTGETENT_B
+ dgux*) LOCAL_CFLAGS=-D_DGUX_SOURCE; LOCAL_LIBS=-ldgc ;;
+ isc*) LOCAL_CFLAGS=-Disc386 ;;
+ rhapsody*) LOCAL_CFLAGS=-DRHAPSODY ;;
+-darwin*) LOCAL_CFLAGS=-DMACOSX ;;
++darwin*) LOCAL_CFLAGS="-DMACOSX -DPIPESIZE_DYNAMIC" ;;
+ sco3.2v5*) LOCAL_CFLAGS="-b elf -DWAITPID_BROKEN -DPATH_MAX=1024" ;;
+ sco3.2v4*) LOCAL_CFLAGS="-DMUST_UNBLOCK_CHLD -DPATH_MAX=1024" ;;
+ sco3.2*) LOCAL_CFLAGS=-DMUST_UNBLOCK_CHLD ;;
+--- a/general.c
++++ b/general.c
+@@ -595,6 +595,27 @@ sh_unset_nodelay_mode (int fd)
+ return 0;
+ }
+
++int
++sh_setnodelay (int fd)
++{
++ int flags;
++
++ if ((flags = fcntl (fd, F_GETFL, 0)) < 0)
++ return -1;
++
++ /* This is defined to O_NDELAY in filecntl.h if O_NONBLOCK is not present
++ and O_NDELAY is defined. */
++#ifdef O_NONBLOCK
++ flags |= O_NONBLOCK;
++#endif
++
++#ifdef O_NDELAY
++ flags |= O_NDELAY;
++#endif
++
++ return (fcntl (fd, F_SETFL, flags));
++}
++
+ /* Just a wrapper for the define in include/filecntl.h */
+ int
+ sh_setclexec (int fd)
+--- a/general.h
++++ b/general.h
+@@ -317,6 +317,7 @@ extern int line_isblank (const char *);
+ extern int assignment (const char *, int);
+
+ extern int sh_unset_nodelay_mode (int);
++extern int sh_setnodelay (int);
+ extern int sh_setclexec (int);
+ extern int sh_validfd (int);
+ extern int fd_ispipe (int);
+--- a/patchlevel.h
++++ b/patchlevel.h
+@@ -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 15
++#define PATCHLEVEL 16
+
+ #endif /* _PATCHLEVEL_H_ */
+--- a/redir.c
++++ b/redir.c
+@@ -472,7 +472,30 @@ here_document_to_fd (WORD_DESC *redirect
+ }
+ #endif
+
++#if defined (PIPESIZE_DYNAMIC)
++ /* If we can't count on the pipe size determined at compile time to be
++ constant across systems, define PIPESIZE_DYNAMIC in configure.ac.
++ We set the pipe's write end to be non-blocking, try to write, and
++ fall back to a temp file on error. */
++ if (sh_setnodelay (herepipe[1]) < 0)
++ {
++ close (herepipe[0]);
++ close (herepipe[1]);
++ goto use_tempfile;
++ }
++#endif
++
+ r = heredoc_write (herepipe[1], document, document_len);
++
++#if defined (PIPESIZE_DYNAMIC)
++ if (r == ENOSPC || r == EAGAIN)
++ {
++ close (herepipe[0]);
++ close (herepipe[1]);
++ goto use_tempfile;
++ }
++#endif
++
+ if (document != redirectee->word)
+ free (document);
+ close (herepipe[1]);
--- /dev/null
+From ee9a3cee01983dca05d47591ea7b8b7b15084cb1 Mon Sep 17 00:00:00 2001
+From: Chet Ramey <chet.ramey@case.edu>
+Date: Tue, 15 Sep 2026 11:14:33 -0400
+Subject: Bash-5.3 patch 17: Modify the readline redisplay code to use a buffer
+ offset instead of the physical prompt length in a multibyte locale to
+ determine whether to reprint the prompt from column 0 because the cursor is
+ before the last invisible character in the prompt string.
+
+--- a/lib/readline/display.c
++++ b/lib/readline/display.c
+@@ -1497,7 +1497,6 @@ rl_redisplay (void)
+ the characters from the current cursor position. But we
+ only need to reprint it if the cursor is before the last
+ invisible character in the prompt string. */
+- /* XXX - why not use local_prompt_len? */
+ nleft = prompt_visible_length + wrap_offset;
+ if (cursor_linenum == prompt_last_screen_line)
+ {
+@@ -1509,11 +1508,7 @@ rl_redisplay (void)
+ on the current screen line begins in the buffer. It is a
+ buffer position, an index into curline
+ (local_prompt + pmt_offset) */
+- cursor_bufpos = pmt_offset;
+- if (mb_cur_max == 1 || rl_byte_oriented)
+- cursor_bufpos += _rl_last_c_pos;
+- else
+- cursor_bufpos += _rl_last_c_pos + curline_invchars;
++ cursor_bufpos = pmt_offset + local_prompt_len;
+
+ if (local_prompt && local_prompt_invis_chars[cursor_linenum] &&
+ _rl_last_c_pos > 0 &&
+--- a/patchlevel.h
++++ b/patchlevel.h
+@@ -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 16
++#define PATCHLEVEL 17
+
+ #endif /* _PATCHLEVEL_H_ */
--- /dev/null
+From 49502f0cdd1331d73e8747552d791aea31706784 Mon Sep 17 00:00:00 2001
+From: Chet Ramey <chet.ramey@case.edu>
+Date: Tue, 15 Sep 2026 11:15:57 -0400
+Subject: Bash-5.3 patch 18: fix readline crash with a prompt that exceeds 256
+ wrapped lines; fix readline redisplay code to print the entire prompt if the
+ first few characters are identical but part of a terminal escape sequence and
+ the prompt needs to be redrawn.
+
+--- a/lib/readline/display.c
++++ b/lib/readline/display.c
+@@ -1024,6 +1024,7 @@ rl_redisplay (void)
+ while (local_prompt_newlines[newlines+1] != -1)
+ {
+ temp = local_prompt_newlines[newlines+1];
++ CHECK_INV_LBREAKS ();
+ inv_lbreaks[++newlines] = temp;
+ }
+
+@@ -2261,11 +2262,12 @@ update_line (char *old, char *old_face,
+ od = ofd - old; /* index of first difference in visible line */
+ nd = nfd - new; /* nd, od are buffer indexes */
+ if (current_line == 0 && !_rl_horizontal_scroll_mode &&
+- _rl_term_cr && lendiff > prompt_visible_length && _rl_last_c_pos > 0 &&
++ _rl_term_cr && lendiff > prompt_visible_length && _rl_last_c_pos >= 0 &&
+ (((od > 0 || nd > 0) && (od <= prompt_last_invisible || nd <= prompt_last_invisible)) ||
+ ((od >= lendiff) && _rl_last_c_pos < PROMPT_ENDING_INDEX)))
+ {
+- _rl_cr ();
++ if (_rl_last_c_pos > 0)
++ _rl_cr ();
+ if (modmark)
+ _rl_output_some_chars ("*", 1);
+ _rl_output_some_chars (local_prompt, lendiff);
+--- a/patchlevel.h
++++ b/patchlevel.h
+@@ -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 17
++#define PATCHLEVEL 18
+
+ #endif /* _PATCHLEVEL_H_ */
--- /dev/null
+From 549639c902ff3fa07e49fb54916600408c2fde69 Mon Sep 17 00:00:00 2001
+From: Chet Ramey <chet.ramey@case.edu>
+Date: Tue, 15 Sep 2026 11:18:03 -0400
+Subject: Bash-5.3 patch 19: work around systems including bytes between 128
+ and 255 in the isalpha(3) set, which causes bash to potentially include them
+ in identifier names
+
+--- a/general.h
++++ b/general.h
+@@ -110,8 +110,13 @@ extern char *strcpy (char *, const char
+ #define INT_BUFSIZE_BOUND(t) (INT_STRLEN_BOUND (t) + 1)
+
+ /* Define exactly what a legal shell identifier consists of. */
+-#define legal_variable_starter(c) (ISALPHA(c) || (c == '_'))
+-#define legal_variable_char(c) (ISALNUM(c) || c == '_')
++#if 0
++#define legal_variable_starter(c) (c < 128 && (ISALPHA(c) || c == '_'))
++#define legal_variable_char(c) (c < 128 && (ISALNUM(c) || c == '_'))
++#else
++#define legal_variable_starter(c) (sh_syntaxtab[c] & CNAMESTART)
++#define legal_variable_char(c) (sh_syntaxtab[c] & CNAME)
++#endif
+
+ /* Definitions used in subst.c and by the `read' builtin for field
+ splitting. */
+--- a/mksyntax.c
++++ b/mksyntax.c
+@@ -60,6 +60,8 @@ struct wordflag {
+ { CSPECVAR, "CSPECVAR" },
+ { CSUBSTOP, "CSUBSTOP" },
+ { CBLANK, "CBLANK" },
++ { CNAME, "CNAME" },
++ { CNAMESTART, "CNAMESTART" }
+ };
+
+ #define N_WFLAGS (sizeof (wordflags) / sizeof (wordflags[0]))
+@@ -198,6 +200,45 @@ addblanks (void)
+ }
+ }
+
++static void
++setnamechars(void)
++{
++ lsyntax['_'] |= CNAME|CNAMESTART;
++
++ lsyntax['0'] |= CNAME; lsyntax['1'] |= CNAME; lsyntax['2'] |= CNAME;
++ lsyntax['3'] |= CNAME; lsyntax['4'] |= CNAME; lsyntax['5'] |= CNAME;
++ lsyntax['6'] |= CNAME; lsyntax['7'] |= CNAME; lsyntax['8'] |= CNAME;
++ lsyntax['9'] |= CNAME;
++
++ lsyntax['a'] |= CNAME|CNAMESTART; lsyntax['b'] |= CNAME|CNAMESTART;
++ lsyntax['c'] |= CNAME|CNAMESTART; lsyntax['d'] |= CNAME|CNAMESTART;
++ lsyntax['e'] |= CNAME|CNAMESTART; lsyntax['f'] |= CNAME|CNAMESTART;
++ lsyntax['g'] |= CNAME|CNAMESTART; lsyntax['h'] |= CNAME|CNAMESTART;
++ lsyntax['i'] |= CNAME|CNAMESTART; lsyntax['j'] |= CNAME|CNAMESTART;
++ lsyntax['k'] |= CNAME|CNAMESTART; lsyntax['l'] |= CNAME|CNAMESTART;
++ lsyntax['m'] |= CNAME|CNAMESTART; lsyntax['n'] |= CNAME|CNAMESTART;
++ lsyntax['o'] |= CNAME|CNAMESTART; lsyntax['p'] |= CNAME|CNAMESTART;
++ lsyntax['q'] |= CNAME|CNAMESTART; lsyntax['r'] |= CNAME|CNAMESTART;
++ lsyntax['s'] |= CNAME|CNAMESTART; lsyntax['t'] |= CNAME|CNAMESTART;
++ lsyntax['u'] |= CNAME|CNAMESTART; lsyntax['v'] |= CNAME|CNAMESTART;
++ lsyntax['w'] |= CNAME|CNAMESTART; lsyntax['x'] |= CNAME|CNAMESTART;
++ lsyntax['y'] |= CNAME|CNAMESTART; lsyntax['z'] |= CNAME|CNAMESTART;
++
++ lsyntax['A'] |= CNAME|CNAMESTART; lsyntax['B'] |= CNAME|CNAMESTART;
++ lsyntax['C'] |= CNAME|CNAMESTART; lsyntax['D'] |= CNAME|CNAMESTART;
++ lsyntax['E'] |= CNAME|CNAMESTART; lsyntax['F'] |= CNAME|CNAMESTART;
++ lsyntax['G'] |= CNAME|CNAMESTART; lsyntax['H'] |= CNAME|CNAMESTART;
++ lsyntax['I'] |= CNAME|CNAMESTART; lsyntax['J'] |= CNAME|CNAMESTART;
++ lsyntax['K'] |= CNAME|CNAMESTART; lsyntax['L'] |= CNAME|CNAMESTART;
++ lsyntax['M'] |= CNAME|CNAMESTART; lsyntax['N'] |= CNAME|CNAMESTART;
++ lsyntax['O'] |= CNAME|CNAMESTART; lsyntax['P'] |= CNAME|CNAMESTART;
++ lsyntax['Q'] |= CNAME|CNAMESTART; lsyntax['R'] |= CNAME|CNAMESTART;
++ lsyntax['S'] |= CNAME|CNAMESTART; lsyntax['T'] |= CNAME|CNAMESTART;
++ lsyntax['U'] |= CNAME|CNAMESTART; lsyntax['V'] |= CNAME|CNAMESTART;
++ lsyntax['W'] |= CNAME|CNAMESTART; lsyntax['X'] |= CNAME|CNAMESTART;
++ lsyntax['Y'] |= CNAME|CNAMESTART; lsyntax['Z'] |= CNAME|CNAMESTART;
++}
++
+ /* load up the correct flag values in lsyntax */
+ static void
+ load_lsyntax (void)
+@@ -234,6 +275,8 @@ load_lsyntax (void)
+ addcstr ("-=?+", CSUBSTOP); /* OP in ${paramOPword} */
+
+ addblanks ();
++
++ setnamechars ();
+ }
+
+ static void
+--- a/patchlevel.h
++++ b/patchlevel.h
+@@ -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 18
++#define PATCHLEVEL 19
+
+ #endif /* _PATCHLEVEL_H_ */
+--- a/syntax.h
++++ b/syntax.h
+@@ -63,6 +63,8 @@
+ #define CSPECVAR 0x0800 /* single-character shell variable name */
+ #define CSUBSTOP 0x1000 /* values of OP for ${word[:]OPstuff} */
+ #define CBLANK 0x2000 /* whitespace (blank) character */
++#define CNAME 0x4000 /* POSIX name character ([_0-9a-zA-Z]) */
++#define CNAMESTART 0x8000 /* POSIX name start character ([_a-zA-Z]) */
+
+ /* Defines for use by the rest of the shell. */
+ extern int sh_syntaxtab[];
--- /dev/null
+From 9c465866b7d849378369ef700cbe2965aa9691e3 Mon Sep 17 00:00:00 2001
+From: Chet Ramey <chet.ramey@case.edu>
+Date: Tue, 15 Sep 2026 11:19:22 -0400
+Subject: Bash-5.3 patch 20: If readline handles a SIGWINCH and updates its
+ idea of the screen dimensions, it needs to recompute the columns where the
+ prompt wraps lines every time, not just when the screen width decreases.
+
+--- a/lib/readline/display.c
++++ b/lib/readline/display.c
+@@ -3522,8 +3522,9 @@ _rl_redisplay_after_sigwinch (void)
+ else
+ rl_crlf ();
+
+- if (_rl_screenwidth < prompt_visible_length)
+- _rl_reset_prompt (); /* update local_prompt_newlines array */
++ /* Let expand_prompt() update local_prompt_newlines and local_prompt_invis_chars
++ arrays */
++ _rl_reset_prompt ();
+
+ /* Redraw only the last line of a multi-line prompt. */
+ t = strrchr (rl_display_prompt, '\n');
+--- a/patchlevel.h
++++ b/patchlevel.h
+@@ -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 19
++#define PATCHLEVEL 20
+
+ #endif /* _PATCHLEVEL_H_ */