doc/bash.1,doc/bashref.texi
- fc: documented new -D option
+
+ 12/19
+ -----
+lib/sh/strtrans.c
+ - ansicstr: handle int overflow in \x{...} and break out of the loop
+ if we overflow
+ Fuzzing report from Александр Ушаков <anushakov@edu.hse.ru>
+
+ 12/23
+ -----
+input.c
+ - duplicate_buffered_stream: if we are duplicating the current input
+ buffered stream, make sure to mark both the current and new
+ buffered streams as B_SHAREDBUF, since they both share the buffer
+ and underlying file descriptor
+ - sync_shared_buffers: new function, after syncing the underlying
+ file descriptor, if the buffered stream is marked B_SHAREDBUF, make
+ sure to update b_used and b_inputp on other (saved) buffered streams
+ - sync_buffered_stream: call sync_shared_buffers
+ - count_shared_buffers: return a count of how many other buffered
+ streams are sharing the same buffer with the buffered stream passed
+ as an argument
+ - unshare_shared_buffers: unset the B_SHAREDBUF flag on the other
+ buffered stream (the caller guarantees there is only 1) sharing a
+ buffer with the buffered stream passed as an argument
+ - save_bash_input,duplicate_buffered_stream,close_buffered_stream:
+ if the buffered stream we're going to free up is marked B_SHAREDBUF,
+ count the number of buffered streams sharing the buffer, and
+ if there is only one other buffered stream sharing it, unset the
+ B_SHAREDBUF flag on that buffered stream
+
+ From a fuzzing report from Александр Ушаков <anushakov@edu.hse.ru>
+
+builtins/evalstring.c
+ - parse_and_execute: save and restore currently_executing_command to
+ avoid pointer aliasing problems with local COMMAND variable
+ From a fuzzing report from Александр Ушаков <anushakov@edu.hse.ru>
+
+execute_cmd.c
+ - execute_command_internal: set currently_executing_command to NULL
+ in places where we return from this function
tests/history7.sub f
tests/history8.sub f
tests/history9.sub f
+tests/history10.sub f
tests/ifs.tests f
tests/ifs.right f
tests/ifs1.sub f
protects installed by the string we're evaluating, so
it will undo the current function scope. */
dispose_command (command);
+ currently_executing_command = NULL;
discard_unwind_frame ("pe_dispose");
}
else
begin_unwind_frame ("pe_dispose");
add_unwind_protect (uw_dispose_fd_bitmap, bitmap);
add_unwind_protect (uw_dispose_command, command); /* XXX */
+ unwind_protect_pointer (currently_executing_command);
global_command = (COMMAND *)NULL;
#endif
last_result = execute_command_internal
(command, 0, NO_PIPE, NO_PIPE, bitmap);
- dispose_command (command);
- dispose_fd_bitmap (bitmap);
- discard_unwind_frame ("pe_dispose");
+
+ run_unwind_frame ("pe_dispose");
/* If the global value didn't change, we restore what we had. */
if (((subshell_environment & SUBSHELL_COMSUB) || executing_funsub) && local_alflag == expaliases_flag)
# Shellmath
-Introducing decimal arithmetic libraries for the Bash shell, because
+Introducing floating-point arithmetic libraries for the Bash shell, because
they said it couldn't be done... and because:
.
The comment header in `faster_e_demo.sh` explains the optimization and shows
how to put this faster version to work for you.
-## Runtime efficiency competitive with awk and bc
+## Competitive with awk and bc in runtime efficiency
The file `timingData.txt` captures the results of some timing experiments that compare
`shellmath` against the GNU versions of the calculators `awk` and `bc`. The experiments
exercised each of the arithmetic operations and captured the results in a shell variable.
## Please see also:
[A short discussion on arbitrary precision and shellmath](https://github.com/clarity20/shellmath/wiki/Shellmath-and-arbitrary-precision-arithmetic)
+
+-- Michael Wood
jump_to_top_level (ERREXIT);
}
+ currently_executing_command = (COMMAND *)NULL;
return (last_command_exit_value);
}
else
run_pending_traps ();
+ currently_executing_command = (COMMAND *)NULL;
/* Posix 2013 2.9.3.1: "the exit status of an asynchronous list
shall be zero." */
last_command_exit_value = 0;
jump_to_top_level (ERREXIT);
}
}
+
+ currently_executing_command = (COMMAND *)NULL;
return (last_command_exit_value);
}
int bash_input_fd_changed;
+static inline int count_shared_buffers (BUFFERED_STREAM *);
+static inline void unshare_shared_buffers (BUFFERED_STREAM *);
+static inline void sync_shared_buffers (BUFFERED_STREAM *);;;;
+
/* This provides a way to map from a file descriptor to the buffer
associated with that file descriptor, rather than just the other
way around. This is needed so that buffers are managed properly
descriptor? Free up the buffer and report the error. */
internal_error (_("save_bash_input: buffer already exists for new fd %d"), nfd);
if (buffers[nfd]->b_flag & B_SHAREDBUF)
- buffers[nfd]->b_buffer = (char *)NULL;
+ {
+ /* If this buffer is shared by only one other buffered stream, mark
+ it as no longer shared. */
+ if (count_shared_buffers (buffers[nfd]) == 1)
+ unshare_shared_buffers (buffers[nfd]);
+ buffers[nfd]->b_buffer = (char *)NULL;
+ }
free_buffered_stream (buffers[nfd]);
}
/* If this buffer is shared with another fd, don't free the buffer */
else if (buffers[fd2]->b_flag & B_SHAREDBUF)
{
+ /* If this buffer is shared by only one other buffered stream, mark
+ it as no longer shared. */
+ if (count_shared_buffers (buffers[fd2]) == 1)
+ unshare_shared_buffers (buffers[fd2]);
buffers[fd2]->b_buffer = (char *)NULL;
free_buffered_stream (buffers[fd2]);
}
}
if (buffers[fd2] && (fd_is_bash_input (fd1) || (buffers[fd1] && (buffers[fd1]->b_flag & B_SHAREDBUF))))
- buffers[fd2]->b_flag |= B_SHAREDBUF;
+ {
+ /* Make sure both buffered streams are marked as sharing an input buffer */
+ buffers[fd1]->b_flag |= B_SHAREDBUF;
+ buffers[fd2]->b_flag |= B_SHAREDBUF;
+ }
return (fd2);
}
{
int fd;
- if (!bp)
+ if (bp == NULL)
return (0);
fd = bp->b_fd;
if (bp->b_flag & B_SHAREDBUF)
- bp->b_buffer = (char *)NULL;
+ {
+ /* If this buffer is shared by only one other buffered stream, mark
+ it as no longer shared. */
+ if (count_shared_buffers (buffers[fd]) == 1)
+ unshare_shared_buffers (buffers[fd]);
+ bp->b_buffer = (char *)NULL;
+ }
free_buffered_stream (bp);
return (close (fd));
}
return (c);
}
+/* Return the number of buffered streams sharing a buffer with BP */
+static inline int
+count_shared_buffers (BUFFERED_STREAM *bp)
+{
+ size_t i;
+ int n;
+
+ n = 0;
+ for (i = 0; i < nbuffers; i++)
+ if (buffers[i] && (buffers[i]->b_flag & B_SHAREDBUF) && buffers[i] != bp && buffers[i]->b_buffer == bp->b_buffer)
+ n++;
+ return n;
+}
+
+/* Mark the buffered stream sharing a buffer with BP as no longer B_SHAREDBUF */
+static inline void
+unshare_shared_buffers (BUFFERED_STREAM *bp)
+{
+ size_t i;
+
+ for (i = 0; i < nbuffers; i++)
+ if (buffers[i] && (buffers[i]->b_flag & B_SHAREDBUF) && buffers[i] != bp && buffers[i]->b_buffer == bp->b_buffer)
+ {
+ buffers[i]->b_flag &= ~B_SHAREDBUF;
+ return;
+ }
+}
+
+/* Sync the input and read offsets on all buffered streams that share a buffer
+ with BP */
+static inline void
+sync_shared_buffers (BUFFERED_STREAM *bp)
+{
+ size_t i;
+
+ for (i = 0; i < nbuffers; i++)
+ if (buffers[i] && (buffers[i]->b_flag & B_SHAREDBUF) && buffers[i] != bp && buffers[i]->b_buffer == bp->b_buffer)
+ {
+ buffers[i]->b_used = bp->b_used;
+ buffers[i]->b_inputp = bp->b_inputp;
+ }
+}
+
/* Seek backwards on file BFD to synchronize what we've read so far
with the underlying file pointer. */
int
if (chars_left)
lseek (bp->b_fd, -chars_left, SEEK_CUR);
bp->b_used = bp->b_inputp = 0;
+
+ /* If we are sharing the buffer between buffered streams, we must have
+ duplicated the file descriptor and called duplicate_buffered_stream().
+ In this case, the buffers share the underlying fd and all of them
+ need to be updated to force a read on the next call, since we've changed
+ the file offset. */
+ if (bp->b_flag & B_SHAREDBUF)
+ sync_shared_buffers (bp);
+
return (0);
}
c &= 0xFF;
break;
case 'x': /* Hex digit -- non-ANSI */
- if ((flags & 2) && *s == '{')
+ if ((flags & 2) && *s == '{') /* undocumented */
{
flags |= 16; /* internal flag value */
s++;
chars are consumed. */
if (flags & 16)
{
+ v = c;
for ( ; ISXDIGIT ((unsigned char)*s); s++)
- c = (c * 16) + HEXVALUE (*s);
+ {
+ v = (v * 16) + HEXVALUE (*s);
+ if (v > INT_MAX) /* abandon on overflow */
+ {
+ v &= INT_MAX;
+ break;
+ }
+ }
+ c = v;
flags &= ~16;
if (*s == '}')
s++;
5 echo three
+ history10.sub
+ 1 echo first
+ 2 echo one
+ 3 echo two
+ 4 echo three
+ 5 echo x
+ 6 echo y
+ 7 echo z
+ 8 echo last
+echo one append
+one append
+echo two append
+two append
+echo three append
+three append
+ 1 echo first
+ 2 echo x
+ 3 echo y
+ 4 echo z
+ 5 echo last
+ 6 echo one append
+ 7 echo two append
+ 8 echo three append
+edit
+echo edit append
+edit append
+ 1 echo first
+ 2 echo x
+ 3 echo y
+ 4 echo z
+ 5 echo last
+ 6 echo one append
+ 7 echo two append
+ 8 echo three append
+ 9 # simulate readline edit_and_execute_command
+ 10 echo edit append
test_runsub ./history7.sub
test_runsub ./history8.sub
test_runsub ./history9.sub
+test_runsub ./history10.sub
--- /dev/null
+#
+# Copyright 2025 Free Software Foundation, Inc.
+#
+# This program 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.
+#
+# This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+# basic test for fc -D
+: ${TMPDIR:=/tmp}
+
+stream()
+{
+ # something you can see
+ sed 's/$/ append/' < $1 >$TMPF && mv $TMPF $1
+ rm -f $TMPF
+}
+
+HISTFILE=$TMPDIR/fchist-$BASHPID
+TMPF=$TMPDIR/fctmp-$BASHPID
+
+trap 'rm -f "$HISTFILE" "$TMPF"' 0 1 2 3 6 15
+
+cat > $HISTFILE <<EOF
+echo first
+echo one
+echo two
+echo three
+echo x
+echo y
+echo z
+echo last
+EOF
+
+set -o history
+
+history
+fc -e stream -D 2 4
+history
+
+# simulate readline edit_and_execute_command
+echo edit
+fc -e stream -D
+history