summaryrefslogtreecommitdiffstats
path: root/tests/csplit
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 16:58:41 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 16:58:41 +0000
commite1908ae95dd4c9d19ee4dfabfc8bf8a7f85943fe (patch)
treef5cc731bedcac0fb7fe14d952e4581e749f8bb87 /tests/csplit
parentInitial commit. (diff)
downloadcoreutils-upstream.tar.xz
coreutils-upstream.zip
Adding upstream version 9.4.upstream/9.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'tests/csplit')
-rwxr-xr-xtests/csplit/csplit-1000.sh29
-rwxr-xr-xtests/csplit/csplit-heap.sh32
-rwxr-xr-xtests/csplit/csplit-io-err.sh92
-rwxr-xr-xtests/csplit/csplit-suppress-matched.pl218
-rwxr-xr-xtests/csplit/csplit.sh103
5 files changed, 474 insertions, 0 deletions
diff --git a/tests/csplit/csplit-1000.sh b/tests/csplit/csplit-1000.sh
new file mode 100755
index 0000000..bc34a24
--- /dev/null
+++ b/tests/csplit/csplit-1000.sh
@@ -0,0 +1,29 @@
+#!/bin/sh
+# cause a 1-byte heap buffer overrun
+
+# Copyright (C) 2010-2023 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 <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ csplit
+
+# Before coreutils-8.7, this would overrun the 6-byte filename_space buffer.
+# It's hard to detect that without using valgrind, so here, we simply
+# run the demonstrator.
+seq 1000 | csplit - '/./' '{*}' || fail=1
+test -f xx1000 || fail=1
+test -f xx1001 && fail=1
+
+Exit $fail
diff --git a/tests/csplit/csplit-heap.sh b/tests/csplit/csplit-heap.sh
new file mode 100755
index 0000000..36b286b
--- /dev/null
+++ b/tests/csplit/csplit-heap.sh
@@ -0,0 +1,32 @@
+#!/bin/sh
+# ensure that csplit uses a bounded amount of memory
+
+# Copyright (C) 2010-2023 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 <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ csplit
+
+# Determine basic amount of memory needed.
+{ echo y; echo n; } > f || framework_failure_
+vm=$(get_min_ulimit_v_ csplit -z f %n%1) \
+ || skip_ "this shell lacks ulimit support"
+
+(
+ ulimit -v $(($vm + 4000)) \
+ && { yes | head -n2500000; echo n; } | csplit -z - %n%1
+) || fail=1
+
+Exit $fail
diff --git a/tests/csplit/csplit-io-err.sh b/tests/csplit/csplit-io-err.sh
new file mode 100755
index 0000000..04c0ce0
--- /dev/null
+++ b/tests/csplit/csplit-io-err.sh
@@ -0,0 +1,92 @@
+#!/bin/sh
+# Ensure we handle i/o errors correctly in csplit
+
+# Copyright (C) 2015-2023 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 <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ csplit
+require_gcc_shared_
+
+if ! test -w /dev/full || ! test -c /dev/full; then
+ skip_ '/dev/full is required'
+fi
+
+# Ensure error messages are in English
+LC_ALL=C
+export LC_ALL
+
+# Replace fwrite and ferror, always returning an error
+cat > k.c <<'EOF' || framework_failure_
+#include <stdio.h>
+#include <errno.h>
+
+#undef fwrite
+#undef fwrite_unlocked
+
+size_t
+fwrite (const void *ptr, size_t size, size_t nitems, FILE *stream)
+{
+ if (stream == stderr)
+ {
+ /* Perform the normal operation of fwrite. */
+ const char *p = ptr;
+ size_t count = size * nitems;
+ size_t i;
+ for (i = 0; i < count; i++)
+ if (putc ((unsigned char) *p++, stream) == EOF)
+ break;
+ return i / size;
+ }
+ else
+ {
+ fclose (fopen ("preloaded","w")); /* marker for preloaded interception */
+ errno = ENOSPC;
+ return 0;
+ }
+}
+
+size_t
+fwrite_unlocked (const void *ptr, size_t size, size_t nitems, FILE *stream)
+{
+ return fwrite (ptr, size, nitems, stream);
+}
+EOF
+
+# Get the wording of the OS-dependent ENOSPC message
+returns_ 1 seq 1 >/dev/full 2>msgt || framework_failure_
+sed 's/seq: write error: //' msgt > msg || framework_failure_
+
+# Create the expected error message
+{ printf "%s" "csplit: write error for 'xx01': " ; cat msg ; } > exp \
+ || framework_failure_
+
+# compile/link the interception shared library:
+gcc_shared_ k.c k.so \
+ || skip_ 'failed to build forced-fwrite-failure shared library'
+
+# Split the input, and force fwrite() failure -
+# the 'csplit' command should fail with exit code 1
+# (checked with 'returns_ 1 ... || fail=1')
+seq 10 |
+(export LD_PRELOAD=$LD_PRELOAD:./k.so
+ returns_ 1 csplit - 1 4 2>out) || fail=1
+
+test -e preloaded || skip_ 'LD_PRELOAD interception failed'
+
+# Ensure we got the expected error message
+compare exp out || fail=1
+
+Exit $fail
diff --git a/tests/csplit/csplit-suppress-matched.pl b/tests/csplit/csplit-suppress-matched.pl
new file mode 100755
index 0000000..80db7fc
--- /dev/null
+++ b/tests/csplit/csplit-suppress-matched.pl
@@ -0,0 +1,218 @@
+#!/usr/bin/perl
+
+# Copyright (C) 2013-2023 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 <https://www.gnu.org/licenses/>.
+
+use strict;
+
+my $limits = getlimits ();
+
+my $prog = 'csplit';
+
+# Turn off localization of executable's output.
+@ENV{qw(LANGUAGE LANG LC_ALL)} = ('C') x 3;
+
+# Input from 'seq 6'
+my $IN_SEQ_6 =<<EOF;
+1
+2
+3
+4
+5
+6
+EOF
+
+# Input from a possible run of 'uniq --group'
+# (groups separated by empty lines)
+my $IN_UNIQ =<<EOF;
+a
+a
+YY
+
+XX
+b
+b
+YY
+
+XX
+c
+YY
+
+XX
+d
+d
+d
+EOF
+
+# Standard Coreutils::run_tests() structure, except the addition of
+# "OUTPUTS" array, containing the expected content of the output files.
+# See code below for conversion into PRE/CMP/POST checks.
+my @csplit_tests =
+(
+ # without --suppress-matched,
+ # the newline (matched line) appears in the output files
+ ["re-base", "-q - '/^\$/' '{*}'", {IN_PIPE => $IN_UNIQ},
+ {OUTPUTS => [ "a\na\nYY\n", "\nXX\nb\nb\nYY\n","\nXX\nc\nYY\n",
+ "\nXX\nd\nd\nd\n" ] }],
+
+ # the newline (matched line) does not appear in the output files
+ ["re-1", " --suppress-matched -q - '/^\$/' '{*}'", {IN_PIPE => $IN_UNIQ},
+ {OUTPUTS => ["a\na\nYY\n", "XX\nb\nb\nYY\n", "XX\nc\nYY\n",
+ "XX\nd\nd\nd\n"]}],
+
+ # the 'XX' (matched line + offset 1) does not appear in the output files.
+ # the newline appears in the files (before each split, at the end of the file)
+ ["re-2", "--suppress-matched -q - '/^\$/1' '{*}'", {IN_PIPE => $IN_UNIQ},
+ {OUTPUTS => ["a\na\nYY\n\n","b\nb\nYY\n\n","c\nYY\n\n","d\nd\nd\n"]}],
+
+ # the 'YY' (matched line + offset of -1) does not appear in the output files
+ # the newline appears in the files (as the first line of the new split)
+ ["re-3", " --suppress-matched -q - '/^\$/-1' '{*}'", {IN_PIPE => $IN_UNIQ},
+ {OUTPUTS => ["a\na\n", "\nXX\nb\nb\n", "\nXX\nc\n", "\nXX\nd\nd\nd\n"]}],
+
+ # the last matched line for a non infinite match repetition is suppressed.
+ # Up to and including coreutils 8.32, the last match was output.
+ ["re-4", " --suppress-matched -q - '/^\$/' '{2}'", {IN_PIPE => $IN_UNIQ},
+ {OUTPUTS => ["a\na\nYY\n", "XX\nb\nb\nYY\n", "XX\nc\nYY\n",
+ "XX\nd\nd\nd\n"]}],
+
+ # Test two consecutive matched lines
+ # without suppress-matched, the second file should contain a single newline.
+ ["re-4.1", "-q - '/^\$/' '{*}'", {IN_PIPE => "a\n\n\nb\n"},
+ {OUTPUTS => [ "a\n", "\n", "\nb\n" ]}],
+ # suppress-matched will cause the second file to be empty.
+ ["re-4.2", "--suppress-match -q - '/^\$/' '{*}'", {IN_PIPE => "a\n\n\nb\n"},
+ {OUTPUTS => [ "a\n", "", "b\n" ]}],
+ # suppress-matched + elide-empty should output just two files.
+ ["re-4.3", "--suppress-match -zq - '/^\$/' '{*}'", {IN_PIPE => "a\n\n\nb\n"},
+ {OUTPUTS => [ "a\n", "b\n" ]}],
+
+
+ # Test a matched-line as the last line
+ # default: last file with newline should be created.
+ ["re-5.1", "-q - '/^\$/' '{*}'", {IN_PIPE => "a\n\nb\n\n"},
+ {OUTPUTS => [ "a\n", "\nb\n", "\n" ]}],
+ # suppress-matched - last empty files should be created.
+ ["re-5.2", "--suppress-match -q - '/^\$/' '{*}'", {IN_PIPE => "a\n\nb\n\n"},
+ {OUTPUTS => [ "a\n", "b\n", "" ]}],
+ # suppress-matched + elide-empty: just two files should be created.
+ ["re-5.3", "--suppress-match -zq - '/^\$/' '{*}'", {IN_PIPE => "a\n\nb\n\n"},
+ {OUTPUTS => [ "a\n", "b\n" ]}],
+
+ # without suppress-matched,
+ # the matched lines (2/4/6) appears in the output files
+ ["int-base", '-q - 2 4 6', {IN_PIPE => $IN_SEQ_6},
+ {OUTPUTS => [ "1\n", "2\n3\n", "4\n5\n", "6\n" ]}],
+ # suppress matched - the matching lines (2/4/6) should not appear.
+ ["int-1", '--suppress-matched -q - 2 4 6', {IN_PIPE => $IN_SEQ_6},
+ {OUTPUTS => [ "1\n", "3\n", "5\n", "" ]}],
+ # suppress matched + elide-empty
+ ["int-2", '--suppress-matched -zq - 2 4 6', {IN_PIPE => $IN_SEQ_6},
+ {OUTPUTS => [ "1\n", "3\n", "5\n" ]}],
+);
+
+
+
+=pod
+The following loop translate the above @Tests to a Coreutils::run_tests()
+compatible structure. It converts "OUTPUTS" key into "CMP" + "POST" keys:
+1. Each element in the OUTPUTS key is expected to be an output file
+ from csplit (named xx00, xx01, xx02...)
+ create a "CMP" key for each one, with the output and the filename.
+2. Add a "POST" key, ensuring no extra files have been created.
+ (e.g. if there are 4 expected outputs, xx00 to xx03,
+ ensure xx04 doesn't exist).
+3. Add a "PRE" key, deleting all existing 'xx*' files.
+
+Example:
+
+Before conversion:
+ my @csplit_tests =
+ (
+ ["1", '-z -q - 2 4 6',
+ {IN_PIPE => "1\n2\n3\n4\n5\n6\n"},
+ {OUTPUTS => [ "1\n", "2\n3\n", "4\n5\n", "6\n" ],
+ ]
+ )
+
+After conversion:
+
+ my @csplit_tests =
+ (
+ ["1", '-z -q - 2 4 6',
+ {IN_PIPE => "1\n2\n3\n4\n5\n6\n"},
+ {PRE => sub { unlink glob './xx??' ; }},
+ {CMP => ["1\n", {'xx00'=> undef}]},
+ {CMP => ["2\n3\n", {'xx01'=> undef}]},
+ {CMP => ["4\n5\n", {'xx02'=> undef}]},
+ {CMP => ["6\n", {'xx03'=> undef}]},
+ {POST => sub { die "extra file" if -e 'xx04'}},
+ ],
+ );
+=cut
+my @Tests;
+foreach my $t (@csplit_tests)
+ {
+ my ($test_name, $cmdline, @others) = @$t;
+ my $new_ent = [$test_name, $cmdline];
+
+ my $out_file_num = 0 ;
+
+ foreach my $e (@others)
+ {
+ die "Internal error: expecting a hash (e.g. IN_PIPE/OUTPUTS/ERR)" .
+ "in test '$test_name', got $e"
+ unless ref $e && (ref $e eq 'HASH');
+
+ my ($key, $value) = each %$e;
+ if ($key eq 'OUTPUTS')
+ {
+ # Convert each expected OUTPUT to a 'CMP' key.
+ foreach my $output (@$value)
+ {
+ my $filename = sprintf("xx%02d",$out_file_num++);
+ my $cmp = {CMP => [ $output, { $filename => undef}]};
+ push @$new_ent, $cmp;
+ }
+
+ # Add a 'POST' check
+ # Ensure no extra files have been created.
+ my $filename = sprintf("xx%02d",$out_file_num++);
+ my $post = { POST => sub { die "Test failed: an extraneous file " .
+ "'$filename' has been created\n"
+ if -e $filename; } } ;
+ push @$new_ent, $post;
+
+ # before running each test, cleanup the 'xx00' files
+ # from previous runs.
+ my $pre = { PRE => sub { unlink glob "./xx??"; } };
+ push @$new_ent, $pre;
+ }
+ else
+ {
+ # pass other entities as-is (e.g. OUT, ERR, OUT_SUBST, EXIT)
+ # run_tests() will know how to handle them.
+ push @$new_ent, $e;
+ }
+ }
+
+ push @Tests, $new_ent;
+ }
+
+my $save_temps = $ENV{DEBUG};
+my $verbose = $ENV{VERBOSE};
+
+my $fail = run_tests ($prog, $prog, \@Tests, $save_temps, $verbose);
+exit $fail;
diff --git a/tests/csplit/csplit.sh b/tests/csplit/csplit.sh
new file mode 100755
index 0000000..a27f61e
--- /dev/null
+++ b/tests/csplit/csplit.sh
@@ -0,0 +1,103 @@
+#!/bin/sh
+# various csplit tests
+
+# Copyright (C) 2001-2023 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 <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ csplit
+
+
+# csplit could get a failed assertion to 2.0.17
+(echo a; echo; echo) > in
+csplit in '/^$/' 2 > out || fail=1
+cat <<EOF > exp
+2
+0
+2
+EOF
+compare exp out || fail=1
+rm -f in out exp
+
+# Ensure that xx02 contains just two newlines.
+# This would fail due to reading from freed buffer with coreutils-5.0.91.
+printf '\n\n' > exp
+cp xx02 out || fail=1
+compare exp out || fail=1
+rm -f in out exp
+
+# csplit would infloop
+(echo; echo a) > in
+csplit in '/a/-1' '{*}' > out || fail=1
+cat <<EOF > exp
+0
+3
+EOF
+compare exp out || fail=1
+rm -f in out exp
+
+# 'echo |csplit - 1 1' used to abort.
+echo > in
+csplit in 1 1 > out 2> err || fail=1
+cat <<EOF > exp
+0
+0
+1
+EOF
+compare exp out || fail=1
+cat <<\EOF > experr
+csplit: warning: line number '1' is the same as preceding line number
+EOF
+compare experr err || fail=1
+rm -f in out exp err experr
+
+# 'echo | csplit -b '%0#6.3x' - 1' incorrectly warned about the format
+# up through coreutils 8.6.
+echo > in
+csplit -b '%0#6.3x' in 1 > out 2> err || fail=1
+cat <<EOF > exp
+0
+1
+EOF
+compare exp out || fail=1
+touch experr
+compare experr err || fail=1
+compare 'xx 000' experr || fail=1
+compare 'xx 0x001' in || fail=1
+rm -f in out exp err experr xx*
+
+# make sure 'csplit FILE 0' fails.
+echo > in
+csplit in 0 > out 2> err && fail=1
+csplit in 2 1 > out 2>> err && fail=1
+csplit in 3 3 > out 2>> err && fail=1
+cat <<\EOF > experr
+csplit: 0: line number must be greater than zero
+csplit: line number '1' is smaller than preceding line number, 2
+csplit: warning: line number '3' is the same as preceding line number
+csplit: '3': line number out of range
+EOF
+compare experr err || fail=1
+
+# Ensure that lines longer than the initial buffer length don't cause
+# trouble (e.g. reading from freed memory, resulting in corrupt output).
+# This test failed at least in coreutils-5.2.1 and 5.3.0, and was fixed
+# in 5.3.1.
+rm -f in out exp err experr xx??
+printf 'x%8199s\nx\n%8199s\nx\n' x x > in
+csplit in '/x\{1\}/' '{*}' > /dev/null || fail=1
+cat xx?? | compare - in || fail=1
+
+Exit $fail