1#!/usr/bin/perl 2# Test "cut". 3 4# Copyright (C) 2006-2023 Free Software Foundation, Inc. 5 6# This program is free software: you can redistribute it and/or modify 7# it under the terms of the GNU General Public License as published by 8# the Free Software Foundation, either version 3 of the License, or 9# (at your option) any later version. 10 11# This program is distributed in the hope that it will be useful, 12# but WITHOUT ANY WARRANTY; without even the implied warranty of 13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14# GNU General Public License for more details. 15 16# You should have received a copy of the GNU General Public License 17# along with this program. If not, see <https://www.gnu.org/licenses/>. 18 19use strict; 20 21(my $ME = $0) =~ s|.*/||; 22 23# Turn off localization of executable's output. 24@ENV{qw(LANGUAGE LANG LC_ALL)} = ('C') x 3; 25 26my $mb_locale = $ENV{LOCALE_FR_UTF8}; 27! defined $mb_locale || $mb_locale eq 'none' 28 and $mb_locale = 'C'; 29 30my $prog = 'cut'; 31my $try = "Try '$prog --help' for more information.\n"; 32my $from_field1 = "$prog: fields are numbered from 1\n$try"; 33my $from_pos1 = "$prog: byte/character positions are numbered from 1\n$try"; 34my $inval_fld = "$prog: invalid field range\n$try"; 35my $inval_pos = "$prog: invalid byte or character range\n$try"; 36my $no_endpoint = "$prog: invalid range with no endpoint: -\n$try"; 37my $nofield = "$prog: an input delimiter may be specified only when " . 38 "operating on fields\n$try"; 39 40my @Tests = 41 ( 42 # Provoke a double-free in cut from coreutils-6.7. 43 ['dbl-free', '-f2-', {IN=>{f=>'x'}}, {IN=>{g=>'y'}}, {OUT=>"x\ny\n"}], 44 45 # This failed (as it should) even before coreutils-6.9.90, 46 # but cut from 6.9.90 produces a more useful diagnostic. 47 ['zero-1', '-b0', {ERR=>$from_pos1}, {EXIT => 1} ], 48 49 # Up to coreutils-6.9, specifying a range of 0-2 was not an error. 50 # It was treated just like "-2". 51 ['zero-2', '-f0-2', {ERR=>$from_field1}, {EXIT => 1} ], 52 53 # Up to coreutils-8.20, specifying a range of 0- was not an error. 54 ['zero-3b', '-b0-', {ERR=>$from_pos1}, {EXIT => 1} ], 55 ['zero-3c', '-c0-', {ERR=>$from_pos1}, {EXIT => 1} ], 56 ['zero-3f', '-f0-', {ERR=>$from_field1}, {EXIT => 1} ], 57 58 ['1', '-d:', '-f1,3-', {IN=>"a:b:c\n"}, {OUT=>"a:c\n"}], 59 ['2', '-d:', '-f1,3-', {IN=>"a:b:c\n"}, {OUT=>"a:c\n"}], 60 ['3', qw(-d: -f2-), {IN=>"a:b:c\n"}, {OUT=>"b:c\n"}], 61 ['4', qw(-d: -f4), {IN=>"a:b:c\n"}, {OUT=>"\n"}], 62 ['5', qw(-d: -f4), {IN=>""}, {OUT=>""}], 63 ['6', '-c4', {IN=>"123\n"}, {OUT=>"\n"}], 64 ['7', '-c4', {IN=>"123"}, {OUT=>"\n"}], 65 ['8', '-c4', {IN=>"123\n1"}, {OUT=>"\n\n"}], 66 ['9', '-c4', {IN=>""}, {OUT=>""}], 67 ['a', qw(-s -d:), '-f3-', {IN=>"a:b:c\n"}, {OUT=>"c\n"}], 68 ['b', qw(-s -d:), '-f2,3', {IN=>"a:b:c\n"}, {OUT=>"b:c\n"}], 69 ['c', qw(-s -d:), '-f1,3', {IN=>"a:b:c\n"}, {OUT=>"a:c\n"}], 70 # Trailing colon should not be output 71 ['d', qw(-s -d:), '-f1,3', {IN=>"a:b:c:\n"}, {OUT=>"a:c\n"}], 72 ['e', qw(-s -d:), '-f3-', {IN=>"a:b:c:\n"}, {OUT=>"c:\n"}], 73 ['f', qw(-s -d:), '-f3-4', {IN=>"a:b:c:\n"}, {OUT=>"c:\n"}], 74 ['g', qw(-s -d:), '-f3,4', {IN=>"a:b:c:\n"}, {OUT=>"c:\n"}], 75 # Make sure -s suppresses non-delimited lines 76 ['h', qw(-s -d:), '-f2,3', {IN=>"abc\n"}, {OUT=>""}], 77 # 78 ['i', qw(-d: -f1-3), {IN=>":::\n"}, {OUT=>"::\n"}], 79 ['j', qw(-d: -f1-4), {IN=>":::\n"}, {OUT=>":::\n"}], 80 ['k', qw(-d: -f2-3), {IN=>":::\n"}, {OUT=>":\n"}], 81 ['l', qw(-d: -f2-4), {IN=>":::\n"}, {OUT=>"::\n"}], 82 ['m', qw(-s -d: -f1-3), {IN=>":::\n"}, {OUT=>"::\n"}], 83 ['n', qw(-s -d: -f1-4), {IN=>":::\n"}, {OUT=>":::\n"}], 84 ['o', qw(-s -d: -f2-3), {IN=>":::\n"}, {OUT=>":\n"}], 85 ['p', qw(-s -d: -f2-4), {IN=>":::\n"}, {OUT=>"::\n"}], 86 ['q', qw(-s -d: -f2-4), {IN=>":::\n:\n"}, {OUT=>"::\n\n"}], 87 ['r', qw(-s -d: -f2-4), {IN=>":::\n:1\n"}, {OUT=>"::\n1\n"}], 88 ['s', qw(-s -d: -f1-4), {IN=>":::\n:a\n"}, {OUT=>":::\n:a\n"}], 89 ['t', qw(-s -d: -f3-), {IN=>":::\n:1\n"}, {OUT=>":\n\n"}], 90 # Make sure it handles empty input properly, with and without -s. 91 ['u', qw(-s -f3-), {IN=>""}, {OUT=>""}], 92 ['v', '-f3-', {IN=>""}, {OUT=>""}], 93 # Make sure it handles empty input properly. 94 ['w', qw(-b 1), {IN=>""}, {OUT=>""}], 95 ['x', qw(-s -d: -f2-4), {IN=>":\n"}, {OUT=>"\n"}], 96 # Errors 97 # -s may be used only with -f 98 ['y', qw(-s -b4), {IN=>":\n"}, {OUT=>""}, {EXIT=>1}, 99 {ERR=>"$prog: suppressing non-delimited lines makes sense\n" 100 . "\tonly when operating on fields\n$try"}], 101 # You must specify bytes or fields (or chars) 102 ['z', '', {IN=>":\n"}, {OUT=>""}, {EXIT=>1}, 103 {ERR=>"$prog: you must specify a list of bytes, characters, or fields\n$try"} 104 ], 105 # Empty field list 106 ['empty-fl', qw(-f ''), {IN=>":\n"}, {OUT=>""}, {EXIT=>1}, 107 {ERR=>$from_field1}], 108 # Missing field list 109 ['missing-fl', qw(-f --), {IN=>":\n"}, {OUT=>""}, {EXIT=>1}, 110 {ERR=>$inval_fld}], 111 # Empty byte list 112 ['empty-bl', qw(-b ''), {IN=>":\n"}, {OUT=>""}, {EXIT=>1}, {ERR=>$from_pos1}], 113 # Missing byte list 114 ['missing-bl', qw(-b --), {IN=>":\n"}, {OUT=>""}, {EXIT=>1}, 115 {ERR=>$inval_pos}], 116 117 # This test fails with cut from textutils-1.22. 118 ['empty-f1', '-f1', {IN=>""}, {OUT=>""}], 119 120 ['empty-f2', '-f2', {IN=>""}, {OUT=>""}], 121 122 ['o-delim', qw(-d: --out=_), '-f2,3', {IN=>"a:b:c\n"}, {OUT=>"b_c\n"}], 123 ['nul-idelim', qw(-d '' --out=_), '-f2,3', {IN=>"a\0b\0c\n"}, {OUT=>"b_c\n"}], 124 ['nul-odelim', qw(-d: --out=), '-f2,3', {IN=>"a:b:c\n"}, {OUT=>"b\0c\n"}], 125 ['multichar-od', qw(-d: --out=_._), '-f2,3', {IN=>"a:b:c\n"}, 126 {OUT=>"b_._c\n"}], 127 128 # Ensure delim is not allowed without a field 129 # Prior to 8.21, a NUL delim was allowed without a field 130 ['delim-no-field1', qw(-d ''), '-b1', {EXIT=>1}, {ERR=>$nofield}], 131 ['delim-no-field2', qw(-d:), '-b1', {EXIT=>1}, {ERR=>$nofield}], 132 133 # Prior to 1.22i, you couldn't use a delimiter that would sign-extend. 134 ['8bit-delim', '-d', "\255", '--out=_', '-f2,3', {IN=>"a\255b\255c\n"}, 135 {OUT=>"b_c\n"}], 136 137 # newline processing for fields 138 ['newline-1', '-f1-', {IN=>"a\nb"}, {OUT=>"a\nb\n"}], 139 ['newline-2', '-f1-', {IN=>""}, {OUT=>""}], 140 ['newline-3', '-d:', '-f1', {IN=>"a:1\nb:2\n"}, {OUT=>"a\nb\n"}], 141 ['newline-4', '-d:', '-f1', {IN=>"a:1\nb:2"}, {OUT=>"a\nb\n"}], 142 ['newline-5', '-d:', '-f2', {IN=>"a:1\nb:2\n"}, {OUT=>"1\n2\n"}], 143 ['newline-6', '-d:', '-f2', {IN=>"a:1\nb:2"}, {OUT=>"1\n2\n"}], 144 ['newline-7', '-s', '-d:', '-f1', {IN=>"a:1\nb:2"}, {OUT=>"a\nb\n"}], 145 ['newline-8', '-s', '-d:', '-f1', {IN=>"a:1\nb:2\n"}, {OUT=>"a\nb\n"}], 146 ['newline-9', '-s', '-d:', '-f1', {IN=>"a1\nb2"}, {OUT=>""}], 147 ['newline-10', '-s', '-d:', '-f1,2', {IN=>"a:1\nb:2"}, {OUT=>"a:1\nb:2\n"}], 148 ['newline-11', '-s', '-d:', '-f1,2', {IN=>"a:1\nb:2\n"}, {OUT=>"a:1\nb:2\n"}], 149 ['newline-12', '-s', '-d:', '-f1', {IN=>"a:1\nb:"}, {OUT=>"a\nb\n"}], 150 ['newline-13', '-d:', '-f1-', {IN=>"a1:\n:"}, {OUT=>"a1:\n:\n"}], 151 # newline processing for fields when -d == '\n' 152 ['newline-14', "-d'\n'", '-f1', {IN=>"a:1\nb:"}, {OUT=>"a:1\n"}], 153 ['newline-15', '-s', "-d'\n'", '-f1', {IN=>"a:1\nb:"}, {OUT=>"a:1\n"}], 154 ['newline-16', '-s', "-d'\n'", '-f2', {IN=>"\nb"}, {OUT=>"b\n"}], 155 ['newline-17', '-s', "-d'\n'", '-f1', {IN=>"\nb"}, {OUT=>"\n"}], 156 ['newline-18', "-d'\n'", '-f2', {IN=>"\nb"}, {OUT=>"b\n"}], 157 ['newline-19', "-d'\n'", '-f1', {IN=>"\nb"}, {OUT=>"\n"}], 158 ['newline-20', '-s', "-d'\n'", '-f1-', {IN=>"\n"}, {OUT=>"\n"}], 159 ['newline-21', '-s', "-d'\n'", '-f1-', {IN=>"\nb"}, {OUT=>"\nb\n"}], 160 ['newline-22', "-d'\n'", '-f1-', {IN=>"\nb"}, {OUT=>"\nb\n"}], 161 ['newline-23', "-d'\n'", '-f1-', '--ou=:', {IN=>"a\nb\n"}, {OUT=>"a:b\n"}], 162 ['newline-24', "-d'\n'", '-f1,2', '--ou=:', {IN=>"a\nb\n"}, {OUT=>"a:b\n"}], 163 164 # --zero-terminated 165 ['zerot-1', "-z", '-c1', {IN=>"ab\0cd\0"}, {OUT=>"a\0c\0"}], 166 ['zerot-2', "-z", '-c1', {IN=>"ab\0cd"}, {OUT=>"a\0c\0"}], 167 ['zerot-3', '-z -f1-', {IN=>""}, {OUT=>""}], 168 ['zerot-4', '-z -d:', '-f1', {IN=>"a:1\0b:2"}, {OUT=>"a\0b\0"}], 169 ['zerot-5', '-z -d:', '-f1-', {IN=>"a1:\0:"}, {OUT=>"a1:\0:\0"}], 170 ['zerot-6', "-z -d ''", '-f1,2', '--ou=:', {IN=>"a\0b\0"}, {OUT=>"a:b\0"}], 171 172 # New functionality: 173 ['out-delim1', '-c1-3,5-', '--output-d=:', {IN=>"abcdefg\n"}, 174 {OUT=>"abc:efg\n"}], 175 # A totally overlapped field shouldn't change anything: 176 ['out-delim2', '-c1-3,2,5-', '--output-d=:', {IN=>"abcdefg\n"}, 177 {OUT=>"abc:efg\n"}], 178 # Partial overlap: index '2' is not at the start of a range. 179 ['out-delim3', '-c1-3,2-4,6', '--output-d=:', {IN=>"abcdefg\n"}, 180 {OUT=>"abcd:f\n"}], 181 ['out-delim3a', '-c1-3,2-4,6-', '--output-d=:', {IN=>"abcdefg\n"}, 182 {OUT=>"abcd:fg\n"}], 183 # Ensure that the following two commands produce the same output. 184 # Before an off-by-1 fix, the output from the former would not contain a ':'. 185 ['out-delim4', '-c4-,2-3', '--output-d=:', 186 {IN=>"abcdefg\n"}, {OUT=>"bc:defg\n"}], 187 ['out-delim5', '-c2-3,4-', '--output-d=:', 188 {IN=>"abcdefg\n"}, {OUT=>"bc:defg\n"}], 189 # This test would fail for cut from coreutils-5.0.1 and earlier. 190 ['out-delim6', '-c2,1-3', '--output-d=:', {IN=>"abc\n"}, {OUT=>"abc\n"}], 191 # 192 ['od-abut', '-b1-2,3-4', '--output-d=:', {IN=>"abcd\n"}, {OUT=>"ab:cd\n"}], 193 ['od-overlap', '-b1-2,2', '--output-d=:', {IN=>"abc\n"}, {OUT=>"ab\n"}], 194 ['od-overlap2', '-b1-2,2-', '--output-d=:', {IN=>"abc\n"}, {OUT=>"abc\n"}], 195 ['od-overlap3', '-b1-3,2-', '--output-d=:', {IN=>"abcd\n"}, {OUT=>"abcd\n"}], 196 ['od-overlap4', '-b1-3,2-3', '--output-d=:', {IN=>"abcd\n"}, {OUT=>"abc\n"}], 197 ['od-overlap5', '-b1-3,1-4', '--output-d=:', 198 {IN=>"abcde\n"}, {OUT=>"abcd\n"}], 199 200 # None of the following invalid ranges provoked an error up to coreutils-6.9. 201 ['inval1', qw(-f 2-0), {IN=>''}, {OUT=>''}, {EXIT=>1}, 202 {ERR=>"$prog: invalid decreasing range\n$try"}], 203 ['inval2', qw(-f -), {IN=>''}, {OUT=>''}, {EXIT=>1}, {ERR=>$no_endpoint}], 204 ['inval3', '-f', '4,-', {IN=>''}, {OUT=>''}, {EXIT=>1}, {ERR=>$no_endpoint}], 205 ['inval4', '-f', '1-2,-', {IN=>''}, {OUT=>''}, {EXIT=>1}, 206 {ERR=>$no_endpoint}], 207 ['inval5', '-f', '1-,-', {IN=>''}, {OUT=>''}, {EXIT=>1}, {ERR=>$no_endpoint}], 208 ['inval6', '-f', '-1,-', {IN=>''}, {OUT=>''}, {EXIT=>1}, {ERR=>$no_endpoint}], 209 # This would evoke a segfault from 5.3.0..8.10 210 ['big-unbounded-b', '--output-d=:', '-b1234567890-', {IN=>''}, {OUT=>''}], 211 ['big-unbounded-b2a', '--output-d=:', '-b1,9-', {IN=>'123456789'}, 212 {OUT=>"1:9\n"}], 213 ['big-unbounded-b2b', '--output-d=:', '-b1,1234567890-', {IN=>''}, {OUT=>''}], 214 ['big-unbounded-c', '--output-d=:', '-c1234567890-', {IN=>''}, {OUT=>''}], 215 ['big-unbounded-f', '--output-d=:', '-f1234567890-', {IN=>''}, {OUT=>''}], 216 217 ['overlapping-unbounded-1', '-b3-,2-', {IN=>"1234\n"}, {OUT=>"234\n"}], 218 ['overlapping-unbounded-2', '-b2-,3-', {IN=>"1234\n"}, {OUT=>"234\n"}], 219 220 # When printing output delimiters, and with one or more ranges subsumed 221 # by a to-EOL range, cut 8.20 and earlier would print extraneous delimiters. 222 ['EOL-subsumed-1', '--output-d=: -b2-,3,4-4,5', 223 {IN=>"123456\n"}, {OUT=>"23456\n"}], 224 ['EOL-subsumed-2', '--output-d=: -b3,4-4,5,2-', 225 {IN=>"123456\n"}, {OUT=>"23456\n"}], 226 ['EOL-subsumed-3', '--complement -b3,4-4,5,2-', 227 {IN=>"123456\n"}, {OUT=>"1\n"}], 228 ['EOL-subsumed-4', '--output-d=: -b1-2,2-3,3-', 229 {IN=>"1234\n"}, {OUT=>"1234\n"}], 230 ); 231 232if ($mb_locale ne 'C') 233 { 234 # Duplicate each test vector, appending "-mb" to the test name and 235 # inserting {ENV => "LC_ALL=$mb_locale"} in the copy, so that we 236 # provide coverage for the distro-added multi-byte code paths. 237 my @new; 238 foreach my $t (@Tests) 239 { 240 my @new_t = @$t; 241 my $test_name = shift @new_t; 242 243 push @new, ["$test_name-mb", @new_t, {ENV => "LC_ALL=$mb_locale"}]; 244 } 245 push @Tests, @new; 246 } 247 248 249@Tests = triple_test \@Tests; 250 251my $save_temps = $ENV{DEBUG}; 252my $verbose = $ENV{VERBOSE}; 253 254my $fail = run_tests ($ME, $prog, \@Tests, $save_temps, $verbose); 255exit $fail; 256