an interactive shell reported by Grisha Levit
<grishalevit@gmail.com>
+ 2/19
+ ----
+
+jobs.c
+ - end_job_control: if the setpgid(0, ...) succeeds, reset the value of
+ shell_pgrp. Fixes pgrp mismatch after failed exec in an interactive
+ login shell reported by Grisha Levit <grishalevit@gmail.com>
+
+ 2/20
+ ----
+bashhist.c
+ - pre_process_line: save history_length before calling history_expand,
+ and after possibly decrementing history_length, just restore the old
+ value instead of incrementing it. Fixes bug reported by
+ Michael Albinus <michael.albinus@gmx.de>
+ - bash_add_history: don't bother calling really_add_history if the
+ history is stifled and the max number of entries is 0
+
+aclocal.m4,configure.ac,m4/*.m4
+ - replace the old set of gettext m4 macros with the latest m4 files
+ from the gnulib and gettext distributions
+
+ 2/21
+ ----
+builtins/cd.def
+ - bindpwd: initialize canon_failed to 0 to prevent `cd -e' from always
+ failing
+
+command.h
+ - W_EXPANDRHS: new flag, set when expanding WORD in ${paramOPword}
+
+subst.c
+ - expand_string_for_rhs: set W_EXPANDRHS in word to be expanded
+
+subst.c
+ - expand_word_internal: when encountering a single or double quoted
+ string that expands to nothing, add a CTLNUL if the W_EXPANDRHS
+ flag is set and the word isn't quoted, indicating that the word
+ will eventually be split and we need to preserve the null to
+ produce an empty word. From a discussion on bug-bash started by
+ sunnycemetery@gmail.com
m4/po.m4 f
m4/printf-posix.m4 f
m4/progtest.m4 f
+m4/pthread_rwlock_rdlock.m4 f
m4/size_max.m4 f
m4/stdint_h.m4 f
m4/threadlib.m4 f
add that line to the history if ADDIT is non-zero. */
if (!history_expansion_inhibited && history_expansion && history_expansion_p (line))
{
+ int old_len;
+
/* If we are expanding the second or later line of a multi-line
command, decrease history_length so references to history expansions
in these lines refer to the previous history entry and not the
current command. */
+ old_len = history_length;
if (history_length > 0 && command_oriented_history && current_command_first_line_saved && current_command_line_count > 1)
history_length--;
expanded = history_expand (line, &history_value);
if (history_length >= 0 && command_oriented_history && current_command_first_line_saved && current_command_line_count > 1)
- history_length++;
+ history_length = old_len;
if (expanded)
{
}
}
+ if (add_it && history_is_stifled() && history_length == 0 && history_length == history_max_entries)
+ add_it = 0;
+
if (add_it)
really_add_history (line);
#undef tcwd
/* If canonicalization fails, reset dirname to the_current_working_directory */
+ canon_failed = 0;
if (dirname == 0)
{
canon_failed = 1;
#define W_ASSIGNRHS 0x000800 /* Word is rhs of an assignment statement */
#define W_NOTILDE 0x001000 /* Don't perform tilde expansion on this word */
#define W_ITILDE 0x002000 /* Internal flag for word expansion */
-#define W_NOEXPAND 0x004000 /* Don't expand at all -- do quote removal */
+#define W_EXPANDRHS 0x004000 /* Expanding word in ${paramOPword} */
#define W_COMPASSIGN 0x008000 /* Compound assignment */
#define W_ASSNBLTIN 0x010000 /* word is a builtin command that takes assignments */
#define W_ASSIGNARG 0x020000 /* word is assignment argument to command */
#define PF_NOSPLIT2 0x04 /* same as W_NOSPLIT2 */
#define PF_ASSIGNRHS 0x08 /* same as W_ASSIGNRHS */
#define PF_COMPLETE 0x10 /* same as W_COMPLETE, sets SX_COMPLETE */
+#define PF_EXPANDRHS 0x20 /* same as W_EXPANDRHS */
/* Possible values for subshell_environment */
#define SUBSHELL_ASYNC 0x01 /* subshell caused by `command &' */
#! /bin/sh
-# From configure.ac for Bash 5.0, version 5.007.
+# From configure.ac for Bash 5.0, version 5.008.
# Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.69 for bash 5.0-maint.
#
+# pthread_rwlock_rdlock.m4 serial 2
+
+
+
+
# size_max.m4 serial 11
fi
if $has_rwlock; then
- gl_PTHREAD_RWLOCK_RDLOCK_PREFER_WRITER
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether pthread_rwlock_rdlock prefers a writer to a reader" >&5
+$as_echo_n "checking whether pthread_rwlock_rdlock prefers a writer to a reader... " >&6; }
+if ${gl_cv_pthread_rwlock_rdlock_prefer_writer+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ save_LIBS="$LIBS"
+ LIBS="$LIBS $LIBMULTITHREAD"
+ if test "$cross_compiling" = yes; then :
+ gl_cv_pthread_rwlock_rdlock_prefer_writer="guessing yes"
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+#include <errno.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#define SUCCEED() exit (0)
+#define FAILURE() exit (1)
+#define UNEXPECTED(n) (exit (10 + (n)))
+
+/* The main thread creates the waiting writer and the requesting reader threads
+ in the default way; this guarantees that they have the same priority.
+ We can reuse the main thread as first reader thread. */
+
+static pthread_rwlock_t lock;
+static pthread_t reader1;
+static pthread_t writer;
+static pthread_t reader2;
+static pthread_t timer;
+/* Used to pass control from writer to reader2 and from reader2 to timer,
+ as in a relay race.
+ Passing control from one running thread to another running thread
+ is most likely faster than to create the second thread. */
+static pthread_mutex_t baton;
+
+static void *
+timer_func (void *ignored)
+{
+ /* Step 13 (can be before or after step 12):
+ The timer thread takes the baton, then waits a moment to make sure
+ it can tell whether the second reader thread is blocked at step 12. */
+ if (pthread_mutex_lock (&baton))
+ UNEXPECTED (13);
+ usleep (100000);
+ /* By the time we get here, it's clear that the second reader thread is
+ blocked at step 12. This is the desired behaviour. */
+ SUCCEED ();
+}
+
+static void *
+reader2_func (void *ignored)
+{
+ int err;
+
+ /* Step 8 (can be before or after step 7):
+ The second reader thread takes the baton, then waits a moment to make sure
+ the writer thread has reached step 7. */
+ if (pthread_mutex_lock (&baton))
+ UNEXPECTED (8);
+ usleep (100000);
+ /* Step 9: The second reader thread requests the lock. */
+ err = pthread_rwlock_tryrdlock (&lock);
+ if (err == 0)
+ FAILURE ();
+ else if (err != EBUSY)
+ UNEXPECTED (9);
+ /* Step 10: Launch a timer, to test whether the next call blocks. */
+ if (pthread_create (&timer, NULL, timer_func, NULL))
+ UNEXPECTED (10);
+ /* Step 11: Release the baton. */
+ if (pthread_mutex_unlock (&baton))
+ UNEXPECTED (11);
+ /* Step 12: The second reader thread requests the lock. */
+ err = pthread_rwlock_rdlock (&lock);
+ if (err == 0)
+ FAILURE ();
+ else
+ UNEXPECTED (12);
+}
+
+static void *
+writer_func (void *ignored)
+{
+ /* Step 4: Take the baton, so that the second reader thread does not go ahead
+ too early. */
+ if (pthread_mutex_lock (&baton))
+ UNEXPECTED (4);
+ /* Step 5: Create the second reader thread. */
+ if (pthread_create (&reader2, NULL, reader2_func, NULL))
+ UNEXPECTED (5);
+ /* Step 6: Release the baton. */
+ if (pthread_mutex_unlock (&baton))
+ UNEXPECTED (6);
+ /* Step 7: The writer thread requests the lock. */
+ if (pthread_rwlock_wrlock (&lock))
+ UNEXPECTED (7);
+ return NULL;
+}
+
+int
+main ()
+{
+ reader1 = pthread_self ();
+
+ /* Step 1: The main thread initializes the lock and the baton. */
+ if (pthread_rwlock_init (&lock, NULL))
+ UNEXPECTED (1);
+ if (pthread_mutex_init (&baton, NULL))
+ UNEXPECTED (1);
+ /* Step 2: The main thread acquires the lock as a reader. */
+ if (pthread_rwlock_rdlock (&lock))
+ UNEXPECTED (2);
+ /* Step 3: Create the writer thread. */
+ if (pthread_create (&writer, NULL, writer_func, NULL))
+ UNEXPECTED (3);
+ /* Job done. Go to sleep. */
+ for (;;)
+ {
+ sleep (1);
+ }
+}
+
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+ gl_cv_pthread_rwlock_rdlock_prefer_writer=yes
+else
+ gl_cv_pthread_rwlock_rdlock_prefer_writer=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+ LIBS="$save_LIBS"
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $gl_cv_pthread_rwlock_rdlock_prefer_writer" >&5
+$as_echo "$gl_cv_pthread_rwlock_rdlock_prefer_writer" >&6; }
+ case "$gl_cv_pthread_rwlock_rdlock_prefer_writer" in
+ *yes)
+
+$as_echo "#define HAVE_PTHREAD_RWLOCK_RDLOCK_PREFER_WRITER 1" >>confdefs.h
+
+ ;;
+ esac
+
fi
# glibc defines PTHREAD_MUTEX_RECURSIVE as enum, not as a macro.
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-AC_REVISION([for Bash 5.0, version 5.007])dnl
+AC_REVISION([for Bash 5.0, version 5.008])dnl
define(bashvers, 5.0)
define(relstatus, maint)
m4_include([m4/po.m4])
m4_include([m4/printf-posix.m4])
m4_include([m4/progtest.m4])
+m4_include([m4/pthread_rwlock_rdlock.m4])
m4_include([m4/size_max.m4])
m4_include([m4/stdint_h.m4])
m4_include([m4/threadlib.m4])
@noindent
Associative arrays are created using
@example
-declare -A @var{name}.
+declare -A @var{name}
@end example
Attributes may be
if (original_pgrp >= 0 && terminal_pgrp != original_pgrp)
give_terminal_to (original_pgrp, 1);
- if (original_pgrp >= 0)
- setpgid (0, original_pgrp);
+ if (original_pgrp >= 0 && setpgid (0, original_pgrp) == 0)
+ shell_pgrp = original_pgrp;
}
/* Restart job control by closing shell tty and reinitializing. This is
--- /dev/null
+# pthread_rwlock_rdlock.m4 serial 2
+dnl Copyright (C) 2017-2019 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+dnl From Bruno Haible.
+dnl Inspired by
+dnl https://github.com/linux-test-project/ltp/blob/master/testcases/open_posix_testsuite/conformance/interfaces/pthread_rwlock_rdlock/2-2.c
+dnl by Intel Corporation.
+
+dnl Test whether in a situation where
+dnl - an rwlock is taken by a reader and has a writer waiting,
+dnl - an additional reader requests the lock,
+dnl - the waiting writer and the requesting reader threads have the same
+dnl priority,
+dnl the requesting reader thread gets blocked, so that at some point the
+dnl waiting writer can acquire the lock.
+dnl Without such a guarantee, when there a N readers and each of the readers
+dnl spends more than 1/Nth of the time with the lock held, there is a high
+dnl probability that the waiting writer will not get the lock in a given finite
+dnl time, a phenomenon called "writer starvation".
+dnl Without such a guarantee, applications have a hard time avoiding writer
+dnl starvation.
+dnl
+dnl POSIX:2017 makes this requirement only for implementations that support TPS
+dnl (Thread Priority Scheduling) and only for the scheduling policies SCHED_FIFO
+dnl and SCHED_RR, see
+dnl http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_rwlock_rdlock.html
+dnl but this test verifies the guarantee regardless of TPS and regardless of
+dnl scheduling policy.
+dnl Glibc currently does not provide this guarantee, see
+dnl https://sourceware.org/bugzilla/show_bug.cgi?id=13701
+AC_DEFUN([gl_PTHREAD_RWLOCK_RDLOCK_PREFER_WRITER],
+[
+ AC_REQUIRE([gl_THREADLIB_EARLY])
+ AC_CACHE_CHECK([whether pthread_rwlock_rdlock prefers a writer to a reader],
+ [gl_cv_pthread_rwlock_rdlock_prefer_writer],
+ [save_LIBS="$LIBS"
+ LIBS="$LIBS $LIBMULTITHREAD"
+ AC_RUN_IFELSE(
+ [AC_LANG_SOURCE([[
+#include <errno.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#define SUCCEED() exit (0)
+#define FAILURE() exit (1)
+#define UNEXPECTED(n) (exit (10 + (n)))
+
+/* The main thread creates the waiting writer and the requesting reader threads
+ in the default way; this guarantees that they have the same priority.
+ We can reuse the main thread as first reader thread. */
+
+static pthread_rwlock_t lock;
+static pthread_t reader1;
+static pthread_t writer;
+static pthread_t reader2;
+static pthread_t timer;
+/* Used to pass control from writer to reader2 and from reader2 to timer,
+ as in a relay race.
+ Passing control from one running thread to another running thread
+ is most likely faster than to create the second thread. */
+static pthread_mutex_t baton;
+
+static void *
+timer_func (void *ignored)
+{
+ /* Step 13 (can be before or after step 12):
+ The timer thread takes the baton, then waits a moment to make sure
+ it can tell whether the second reader thread is blocked at step 12. */
+ if (pthread_mutex_lock (&baton))
+ UNEXPECTED (13);
+ usleep (100000);
+ /* By the time we get here, it's clear that the second reader thread is
+ blocked at step 12. This is the desired behaviour. */
+ SUCCEED ();
+}
+
+static void *
+reader2_func (void *ignored)
+{
+ int err;
+
+ /* Step 8 (can be before or after step 7):
+ The second reader thread takes the baton, then waits a moment to make sure
+ the writer thread has reached step 7. */
+ if (pthread_mutex_lock (&baton))
+ UNEXPECTED (8);
+ usleep (100000);
+ /* Step 9: The second reader thread requests the lock. */
+ err = pthread_rwlock_tryrdlock (&lock);
+ if (err == 0)
+ FAILURE ();
+ else if (err != EBUSY)
+ UNEXPECTED (9);
+ /* Step 10: Launch a timer, to test whether the next call blocks. */
+ if (pthread_create (&timer, NULL, timer_func, NULL))
+ UNEXPECTED (10);
+ /* Step 11: Release the baton. */
+ if (pthread_mutex_unlock (&baton))
+ UNEXPECTED (11);
+ /* Step 12: The second reader thread requests the lock. */
+ err = pthread_rwlock_rdlock (&lock);
+ if (err == 0)
+ FAILURE ();
+ else
+ UNEXPECTED (12);
+}
+
+static void *
+writer_func (void *ignored)
+{
+ /* Step 4: Take the baton, so that the second reader thread does not go ahead
+ too early. */
+ if (pthread_mutex_lock (&baton))
+ UNEXPECTED (4);
+ /* Step 5: Create the second reader thread. */
+ if (pthread_create (&reader2, NULL, reader2_func, NULL))
+ UNEXPECTED (5);
+ /* Step 6: Release the baton. */
+ if (pthread_mutex_unlock (&baton))
+ UNEXPECTED (6);
+ /* Step 7: The writer thread requests the lock. */
+ if (pthread_rwlock_wrlock (&lock))
+ UNEXPECTED (7);
+ return NULL;
+}
+
+int
+main ()
+{
+ reader1 = pthread_self ();
+
+ /* Step 1: The main thread initializes the lock and the baton. */
+ if (pthread_rwlock_init (&lock, NULL))
+ UNEXPECTED (1);
+ if (pthread_mutex_init (&baton, NULL))
+ UNEXPECTED (1);
+ /* Step 2: The main thread acquires the lock as a reader. */
+ if (pthread_rwlock_rdlock (&lock))
+ UNEXPECTED (2);
+ /* Step 3: Create the writer thread. */
+ if (pthread_create (&writer, NULL, writer_func, NULL))
+ UNEXPECTED (3);
+ /* Job done. Go to sleep. */
+ for (;;)
+ {
+ sleep (1);
+ }
+}
+]])],
+ [gl_cv_pthread_rwlock_rdlock_prefer_writer=yes],
+ [gl_cv_pthread_rwlock_rdlock_prefer_writer=no],
+ [gl_cv_pthread_rwlock_rdlock_prefer_writer="guessing yes"])
+ LIBS="$save_LIBS"
+ ])
+ case "$gl_cv_pthread_rwlock_rdlock_prefer_writer" in
+ *yes)
+ AC_DEFINE([HAVE_PTHREAD_RWLOCK_RDLOCK_PREFER_WRITER], [1],
+ [Define if the 'pthread_rwlock_rdlock' function prefers a writer to a reader.])
+ ;;
+ esac
+])
f &= ~W_COMPASSIGN;
fprintf (stderr, "W_COMPASSIGN%s", f ? "|" : "");
}
- if (f & W_NOEXPAND)
+ if (f & W_EXPANDRHS)
{
- f &= ~W_NOEXPAND;
- fprintf (stderr, "W_NOEXPAND%s", f ? "|" : "");
+ f &= ~W_EXPANDRHS;
+ fprintf (stderr, "W_EXPANDRHS%s", f ? "|" : "");
}
if (f & W_ITILDE)
{
in Posix bug 1129 */
old_nosplit = expand_no_split_dollar_star;
expand_no_split_dollar_star = (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT)) || op == '=' || ifs_is_null == 0; /* XXX - was 1 */
- td.flags = W_NOSPLIT2; /* no splitting, remove "" and '' */
+ td.flags = W_EXPANDRHS; /* expanding RHS of ${paramOPword */
+ td.flags |= W_NOSPLIT2; /* no splitting, remove "" and '' */
if (pflags & PF_ASSIGNRHS) /* pass through */
td.flags |= W_ASSIGNRHS;
if (op == '=')
this is when we are going to be performing word splitting,
since we have to preserve a null argument if the next character
will cause word splitting. */
+ if (temp == 0 && quoted_state == PARTIALLY_QUOTED && quoted == 0 && (word->flags & W_NOSPLIT) == 0 && (word->flags & W_EXPANDRHS))
+ {
+ c = CTLNUL;
+ sindex--;
+ had_quoted_null = 1;
+ goto add_character;
+ }
if (temp == 0 && quoted_state == PARTIALLY_QUOTED && (word->flags & (W_NOSPLIT|W_NOSPLIT2)))
continue;
/* We do not want to add quoted nulls to strings that are only
partially quoted; such nulls are discarded. See above for the
exception, which is when the string is going to be split.
- Posix interp 888 */
+ Posix interp 888/1129 */
+ if (temp == 0 && quoted_state == PARTIALLY_QUOTED && quoted == 0 && (word->flags & W_NOSPLIT) == 0 && (word->flags & W_EXPANDRHS))
+ {
+ c = CTLNUL;
+ sindex--;
+ goto add_character;
+ }
+
if (temp == 0 && (quoted_state == PARTIALLY_QUOTED) && (word->flags & (W_NOSPLIT|W_NOSPLIT2)))
continue;
tword->flags |= W_NOGLOB; /* XXX */
if (word->flags & W_NOBRACE)
tword->flags |= W_NOBRACE; /* XXX */
- if (word->flags & W_NOEXPAND)
- tword->flags |= W_NOEXPAND; /* XXX */
if (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES))
tword->flags |= W_QUOTED;
list = make_word_list (tword, (WORD_LIST *)NULL);
tword->flags |= W_NOGLOB;
if (word->flags & W_NOBRACE)
tword->flags |= W_NOBRACE;
- if (word->flags & W_NOEXPAND)
- tword->flags |= W_NOEXPAND;
list = make_word_list (tword, (WORD_LIST *)NULL);
}
}
null string with unquoted $@
argv[1] = <>
null string with quoted $@
+argv[1] = <>
assignment
argv[1] = <>
variable
argv[1] = <>
argv[1] = <>
argv[1] = <>
+argv[2] = <>
argv[1] = <>
+argv[2] = <>
argv[1] = <>
=====
argv[1] = <>
argv[1] = <>
argv[1] = <>
argv[1] = <>
+argv[2] = <>
argv[1] = <>
argv[1] = <>
argv[1] = <>
+argv[2] = <>
=====
argv[1] = <>
argv[1] = <>
argv[1] = <>
argv[1] = <>
argv[1] = <>
+argv[2] = <>
argv[1] = <>
+argv[2] = <>
argv[1] = <>
=====
argv[1] = <>
argv[1] = <>
argv[1] = <>
argv[1] = <>
+argv[2] = <>
argv[1] = <>
+argv[2] = <>
argv[1] = <>