1#!/usr/bin/perl 2# Exercise expand. 3 4# Copyright (C) 2004-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 21my $limits = getlimits (); 22my $UINTMAX_OFLOW = $limits->{UINTMAX_OFLOW}; 23 24(my $program_name = $0) =~ s|.*/||; 25my $prog = 'expand'; 26 27# Turn off localization of executable's output. 28@ENV{qw(LANGUAGE LANG LC_ALL)} = ('C') x 3; 29 30my @Tests = 31 ( 32 ['t1', '--tabs=3', {IN=>"a\tb"}, {OUT=>"a b"}], 33 ['t2', '--tabs=3,6,9', {IN=>"a\tb\tc\td\te"}, {OUT=>"a b c d e"}], 34 ['t3', '--tabs="3 6 9"', {IN=>"a\tb\tc\td\te"}, {OUT=>"a b c d e"}], 35 # Leading space/commas are silently ignored; Mixing space/commas is allowed. 36 # (a side-effect of allowing direct "-3,9" parameter). 37 ['t4', '--tabs=", 3,6 9"', {IN=>"a\tb\tc\td\te"}, {OUT=>"a b c d e"}], 38 # tab stops parameter without values 39 ['t5', '--tabs=""', {IN=>"a\tb\tc"}, {OUT=>"a b c"}], 40 ['t6', '--tabs=","', {IN=>"a\tb\tc"}, {OUT=>"a b c"}], 41 ['t7', '--tabs=" "', {IN=>"a\tb\tc"}, {OUT=>"a b c"}], 42 ['t8', '--tabs="/"', {IN=>"a\tb\tc"}, {OUT=>"a b c"}], 43 44 # Input field wider than the specified tab list 45 ['if', '--tabs=6,9', {IN=>"a\tbbbbbbbbbbbbb\tc"}, 46 {OUT=>"a bbbbbbbbbbbbb c"}], 47 48 ['i1', '--tabs=3 -i', {IN=>"\ta\tb"}, {OUT=>" a\tb"}], 49 ['i2', '--tabs=3 -i', {IN=>" \ta\tb"}, {OUT=>" a\tb"}], 50 51 # Undocumented feature: 52 # treat "expand -7" as "expand --tabs 7" , 53 # and "expand -90" as "expand --tabs 90", 54 ['u1', '-3', {IN=>"a\tb\tc"}, {OUT=>"a b c"}], 55 ['u2', '-4 -9', {IN=>"a\tb\tc"}, {OUT=>"a b c"}], 56 ['u3', '-11', {IN=>"a\tb\tc"}, {OUT=>"a b c"}], 57 # Test all digits (for full code coverage) 58 ['u4', '-2 -6', {IN=>"a\tb\tc"}, {OUT=>"a b c"}], 59 ['u5', '-7', {IN=>"a\tb"}, {OUT=>"a b"}], 60 ['u6', '-8', {IN=>"a\tb"}, {OUT=>"a b"}], 61 # This syntax is handled internally as "-3, -9" 62 ['u7', '-3,9', {IN=>"a\tb\tc"}, {OUT=>"a b c"}], 63 64 # Multiple non-empty files 65 ['f1', '--tabs=4', 66 {IN=>{"in1" => "a\tb\n"}}, {IN=>{"in2" => "c\td\n"}}, 67 {OUT=>"a b\nc d\n"}], 68 # Multiple files, first file is empty 69 ['f2', '--tabs=4', 70 {IN=>{"in1" => ""}}, {IN=>{"in2" => "c\td\n"}}, 71 {OUT=>"c d\n"}], 72 # Multiple files, second file is empty 73 ['f3', '--tabs=4', 74 {IN=>{"in1" => "a\tb\n"}}, {IN=>{"in2" => ""}}, 75 {OUT=>"a b\n"}], 76 77 78 # Test '\b' (back-space) - subtract one column. 79 # 80 # Note: 81 # In a terminal window, 'expand' will appear to erase the 'a' characters 82 # due to overwriting them with spaces: 83 # 84 # $ printf 'aaa\b\b\bc\td\n' 85 # caa d 86 # $ printf 'aaa\b\b\bc\td\n' | expand 87 # c d 88 # 89 # However the characters are all printed: 90 # 91 # $ printf 'aaa\b\b\bc\td\n' | expand | od -An -ta 92 # a a a bs bs bs c sp sp sp sp sp sp sp d nl 93 # 94 # If users ever report a problem with these tests and just 95 # copy&paste from the terminal, their report will be confusing 96 # (the 'a' will not appear). 97 # 98 # To see an example, enable the 'b-confusing' test, and examine the 99 # reported log: 100 # 101 # expand.pl: test b-confusing: stdout mismatch 102 # *** b-confusing.2 Fri Jun 24 15:43:21 2016 103 # --- b-confusing.O Fri Jun 24 15:43:21 2016 104 # *************** 105 # *** 1 **** 106 # ! c d 107 # --- 1 ---- 108 # ! c d 109 # 110 # ['b-confusing','', {IN=>"aaa\b\b\bc\td\n"}, {OUT=>"c d\n"}], 111 112 ['b1','', {IN=>"aaa\b\b\bc\td\n"}, {OUT=>"aaa\b\b\bc d\n"}], 113 114 # \b as first character, when column is zero 115 ['b2','', {IN=>"\bc\td"}, {OUT=>"\bc d"}], 116 117 # Testing tab list adjusted due to backspaces 118 # ('b3' is the baseline without backspaces). 119 ['b3','--tabs 2,4,6,10', 120 {IN=>"1\t2\t3\t4\t5\n" . 121 "a\tb\tc\td\te\n"}, 122 {OUT=>"1 2 3 4 5\n" . 123 "a b c d e\n"}], 124 125 # On screen this will appear the same as 'b3' 126 ['b4','--tabs 2,4,6,10', 127 {IN=>"1\t2\t3\t4\t5\n" . 128 "a\tbHELLO\b\b\b\b\b\tc\td\te\n"}, 129 {OUT=>"1 2 3 4 5\n" . 130 "a bHELLO\b\b\b\b\b c d e\n"}], 131 132 # On screen on 'bHE' will appear (LLO overwritten by spaces), 133 # 'c' should align with 4, 'd' with 5: 134 # 1 2 3 4 5 135 # a bHE c d e 136 ['b5','--tabs 2,4,6,10', 137 {IN=>"1\t2\t3\t4\t5\n" . 138 "a\tbHELLO\b\b\b\tc\td\te\n"}, 139 {OUT=>"1 2 3 4 5\n" . 140 "a bHELLO\b\b\b c d e\n"}], 141 142 # Test the trailing '/' feature which specifies the 143 # tab size to use after the last specified stop 144 ['trail1', '--tabs=1,/5', {IN=>"\ta\tb\tc"}, {OUT=>" a b c"}], 145 ['trail2', '--tabs=2,/5', {IN=>"\ta\tb\tc"}, {OUT=>" a b c"}], 146 ['trail3', '--tabs=1,2,/5', {IN=>"\ta\tb\tc"}, {OUT=>" a b c"}], 147 ['trail4', '--tabs=/5', {IN=>"\ta\tb"}, {OUT=>" a b"}], 148 ['trail5', '--tabs=//5', {IN=>"\ta\tb"}, {OUT=>" a b"}], 149 ['trail5a','--tabs=+/5', {IN=>"\ta\tb"}, {OUT=>" a b"}], 150 ['trail6', '--tabs=/,/5', {IN=>"\ta\tb"}, {OUT=>" a b"}], 151 ['trail7', '--tabs=,/5', {IN=>"\ta\tb"}, {OUT=>" a b"}], 152 ['trail8', '--tabs=1 -t/5', {IN=>"\ta\tb\tc"}, {OUT=>" a b c"}], 153 ['trail9', '--tab=1,2 -t/5',{IN=>"\ta\tb\tc"}, {OUT=>" a b c"}], 154 155 # Test incremental trailing '+' feature which specifies that 156 # tab stops should continue every increment 157 ['incre0', '--tab=1,+5', {IN=>"+\t\ta\tb"}, {OUT=>"+ a b"}], 158 ['incre1', '--tabs=1,+5', {IN=>"\ta\tb\tc"}, {OUT=>" a b c"}], 159 ['incre2', '--tabs=2,+5', {IN=>"\ta\tb\tc"}, {OUT=>" a b c"}], 160 ['incre3', '--tabs=1,2,+5', {IN=>"\ta\tb\tc"}, {OUT=>" a b c"}], 161 ['incre4', '--tabs=+5', {IN=>"\ta\tb"}, {OUT=>" a b"}], 162 ['incre5', '--tabs=++5', {IN=>"\ta\tb"}, {OUT=>" a b"}], 163 ['incre5a','--tabs=/+5', {IN=>"\ta\tb"}, {OUT=>" a b"}], 164 ['incre6', '--tabs=+,+5', {IN=>"\ta\tb"}, {OUT=>" a b"}], 165 ['incre7', '--tabs=,+5', {IN=>"\ta\tb"}, {OUT=>" a b"}], 166 ['incre8', '--tabs=1 -t+5', {IN=>"\ta\tb\tc"}, {OUT=>" a b c"}], 167 ['incre9', '--tab=1,2 -t+5',{IN=>"\ta\tb\tc"}, {OUT=>" a b c"}], 168 169 170 # Test errors 171 ['e1', '--tabs="a"', {IN=>''}, {OUT=>''}, {EXIT=>1}, 172 {ERR => "$prog: tab size contains invalid character(s): 'a'\n"}], 173 ['e2', "-t $UINTMAX_OFLOW", {IN=>''}, {OUT=>''}, {EXIT=>1}, 174 {ERR => "$prog: tab stop is too large '$UINTMAX_OFLOW'\n"}], 175 ['e3', '--tabs=0', {IN=>''}, {OUT=>''}, {EXIT=>1}, 176 {ERR => "$prog: tab size cannot be 0\n"}], 177 ['e4', '--tabs=3,3', {IN=>''}, {OUT=>''}, {EXIT=>1}, 178 {ERR => "$prog: tab sizes must be ascending\n"}], 179 ['e5', '--tabs=/3,6,8', {IN=>''}, {OUT=>''}, {EXIT=>1}, 180 {ERR => "$prog: '/' specifier only allowed with the last value\n"}], 181 ['e6', '-t/3 -t/6', {IN=>''}, {OUT=>''}, {EXIT=>1}, 182 {ERR => "$prog: '/' specifier only allowed with the last value\n"}], 183 ['e7', '--tabs=3/', {IN=>''}, {OUT=>''}, {EXIT=>1}, 184 {ERR => "$prog: '/' specifier not at start of number: '/'\n"}], 185 ); 186 187my $save_temps = $ENV{DEBUG}; 188my $verbose = $ENV{VERBOSE}; 189 190my $fail = run_tests ($program_name, $prog, \@Tests, $save_temps, $verbose); 191exit $fail; 192