#!./perl -wT BEGIN { chdir 't' if -d 't'; unshift @INC, '../lib'; unshift @INC, '.'; require Config; import Config; if (!$Config{d_setlocale} || $Config{ccflags} =~ /\bD?NO_LOCALE\b/) { print "1..0\n"; exit; } } use strict; my $debug = 1; sub debug { print @_ if $debug; } sub debugf { printf @_ if $debug; } my $have_setlocale = 0; eval { require POSIX; import POSIX ':locale_h'; $have_setlocale++; }; # Visual C's CRT goes silly on strings of the form "en_US.ISO8859-1" # and mingw32 uses said silly CRT $have_setlocale = 0 if $^O eq 'MSWin32' && $Config{cc} =~ /^(cl|gcc)/i; print "1..", ($have_setlocale ? 115 : 98), "\n"; use vars qw(&LC_ALL); my $a = 'abc %'; sub ok { my ($n, $result) = @_; print 'not ' unless ($result); print "ok $n\n"; } # First we'll do a lot of taint checking for locales. # This is the easiest to test, actually, as any locale, # even the default locale will taint under 'use locale'. sub is_tainted { # hello, camel two. local $^W; # no warnings 'undef' my $dummy; not eval { $dummy = join("", @_), kill 0; 1 } } sub check_taint ($$) { ok $_[0], is_tainted($_[1]); } sub check_taint_not ($$) { ok $_[0], not is_tainted($_[1]); } use locale; # engage locale and therefore locale taint. check_taint_not 1, $a; check_taint 2, uc($a); check_taint 3, "\U$a"; check_taint 4, ucfirst($a); check_taint 5, "\u$a"; check_taint 6, lc($a); check_taint 7, "\L$a"; check_taint 8, lcfirst($a); check_taint 9, "\l$a"; check_taint_not 10, sprintf('%e', 123.456); check_taint_not 11, sprintf('%f', 123.456); check_taint_not 12, sprintf('%g', 123.456); check_taint_not 13, sprintf('%d', 123.456); check_taint_not 14, sprintf('%x', 123.456); $_ = $a; # untaint $_ $_ = uc($a); # taint $_ check_taint 15, $_; /(\w)/; # taint $&, $`, $', $+, $1. check_taint 16, $&; check_taint 17, $`; check_taint 18, $'; check_taint 19, $+; check_taint 20, $1; check_taint_not 21, $2; /(.)/; # untaint $&, $`, $', $+, $1. check_taint_not 22, $&; check_taint_not 23, $`; check_taint_not 24, $'; check_taint_not 25, $+; check_taint_not 26, $1; check_taint_not 27, $2; /(\W)/; # taint $&, $`, $', $+, $1. check_taint 28, $&; check_taint 29, $`; check_taint 30, $'; check_taint 31, $+; check_taint 32, $1; check_taint_not 33, $2; /(\s)/; # taint $&, $`, $', $+, $1. check_taint 34, $&; check_taint 35, $`; check_taint 36, $'; check_taint 37, $+; check_taint 38, $1; check_taint_not 39, $2; /(\S)/; # taint $&, $`, $', $+, $1. check_taint 40, $&; check_taint 41, $`; check_taint 42, $'; check_taint 43, $+; check_taint 44, $1; check_taint_not 45, $2; $_ = $a; # untaint $_ check_taint_not 46, $_; /(b)/; # this must not taint check_taint_not 47, $&; check_taint_not 48, $`; check_taint_not 49, $'; check_taint_not 50, $+; check_taint_not 51, $1; check_taint_not 52, $2; $_ = $a; # untaint $_ check_taint_not 53, $_; $b = uc($a); # taint $b s/(.+)/$b/; # this must taint only the $_ check_taint 54, $_; check_taint_not 55, $&; check_taint_not 56, $`; check_taint_not 57, $'; check_taint_not 58, $+; check_taint_not 59, $1; check_taint_not 60, $2; $_ = $a; # untaint $_ s/(.+)/b/; # this must not taint check_taint_not 61, $_; check_taint_not 62, $&; check_taint_not 63, $`; check_taint_not 64, $'; check_taint_not 65, $+; check_taint_not 66, $1; check_taint_not 67, $2; $b = $a; # untaint $b ($b = $a) =~ s/\w/$&/; check_taint 68, $b; # $b should be tainted. check_taint_not 69, $a; # $a should be not. $_ = $a; # untaint $_ s/(\w)/\l$1/; # this must taint check_taint 70, $_; check_taint 71, $&; check_taint 72, $`; check_taint 73, $'; check_taint 74, $+; check_taint 75, $1; check_taint_not 76, $2; $_ = $a; # untaint $_ s/(\w)/\L$1/; # this must taint check_taint 77, $_; check_taint 78, $&; check_taint 79, $`; check_taint 80, $'; check_taint 81, $+; check_taint 82, $1; check_taint_not 83, $2; $_ = $a; # untaint $_ s/(\w)/\u$1/; # this must taint check_taint 84, $_; check_taint 85, $&; check_taint 86, $`; check_taint 87, $'; check_taint 88, $+; check_taint 89, $1; check_taint_not 90, $2; $_ = $a; # untaint $_ s/(\w)/\U$1/; # this must taint check_taint 91, $_; check_taint 92, $&; check_taint 93, $`; check_taint 94, $'; check_taint 95, $+; check_taint 96, $1; check_taint_not 97, $2; # After all this tainting $a should be cool. check_taint_not 98, $a; # I think we've seen quite enough of taint. # Let us do some *real* locale work now, # unless setlocale() is missing (i.e. minitest). exit unless $have_setlocale; # Find locales. debug "# Scanning for locales...\n"; # Note that it's okay that some languages have their native names # capitalized here even though that's not "right". They are lowercased # anyway later during the scanning process (and besides, some clueless # vendor might have them capitalized errorneously anyway). my $locales = < $#Alnum_); $lesser = join('', @Alnum_[$from..$to]); # Select a slice one character on. $from++; $to++; $to = $#Alnum_ if ($to > $#Alnum_); $greater = join('', @Alnum_[$from..$to]); ($yes, $no, $sign) = ($lesser lt $greater ? (" ", "not ", 1) : ("not ", " ", -1)); # all these tests should FAIL (return 0). # Exact lt or gt cannot be tested because # in some locales, say, eacute and E may test equal. @test = ( $no.' ($lesser le $greater)', # 1 'not ($lesser ne $greater)', # 2 ' ($lesser eq $greater)', # 3 $yes.' ($lesser ge $greater)', # 4 $yes.' ($lesser ge $greater)', # 5 $yes.' ($greater le $lesser )', # 7 'not ($greater ne $lesser )', # 8 ' ($greater eq $lesser )', # 9 $no.' ($greater ge $lesser )', # 10 'not (($lesser cmp $greater) == -$sign)' # 12 ); @test{@test} = 0 x @test; $test = 0; for my $ti (@test) { $test{$ti} = eval $ti; $test ||= $test{$ti} } tryneoalpha($Locale, 102, $test == 0); if ($test) { debug "# lesser = '$lesser'\n"; debug "# greater = '$greater'\n"; debug "# lesser cmp greater = ", $lesser cmp $greater, "\n"; debug "# greater cmp lesser = ", $greater cmp $lesser, "\n"; debug "# (greater) from = $from, to = $to\n"; for my $ti (@test) { debugf("# %-40s %-4s", $ti, $test{$ti} ? 'FAIL' : 'ok'); if ($ti =~ /\(\.*(\$.+ +cmp +\$[^\)]+)\.*\)/) { debugf("(%s == %4d)", $1, eval $1); } debug "\n#"; } last; } } } } use locale; my ($x, $y) = (1.23, 1.23); my $a = "$x"; printf ''; # printf used to reset locale to "C" my $b = "$y"; debug "# 103..107: a = $a, b = $b, Locale = $Locale\n"; tryneoalpha($Locale, 103, $a eq $b); my $c = "$x"; my $z = sprintf ''; # sprintf used to reset locale to "C" my $d = "$y"; debug "# 104..107: c = $c, d = $d, Locale = $Locale\n"; tryneoalpha($Locale, 104, $c eq $d); { my $w = 0; local $SIG{__WARN__} = sub { $w++ }; local $^W = 1; # the == (among other ops) used to warn for locales # that had something else than "." as the radix character tryneoalpha($Locale, 105, $c == 1.23); tryneoalpha($Locale, 106, $c == $x); tryneoalpha($Locale, 107, $c == $d); { no locale; my $e = "$x"; debug "# 108..110: e = $e, Locale = $Locale\n"; tryneoalpha($Locale, 108, $e == 1.23); tryneoalpha($Locale, 109, $e == $x); tryneoalpha($Locale, 110, $e == $c); } tryneoalpha($Locale, 111, $w == 0); my $f = "1.23"; debug "# 112..114: f = $f, locale = $Locale\n"; tryneoalpha($Locale, 112, $f == 1.23); tryneoalpha($Locale, 113, $f == $x); tryneoalpha($Locale, 114, $f == $c); } debug "# testing 115 with locale '$Locale'\n"; { use locale; sub lcA { my $lc0 = lc $_[0]; my $lc1 = lc $_[1]; return $lc0 cmp $lc1; } sub lcB { return lc($_[0]) cmp lc($_[1]); } my $x = "ab"; my $y = "aa"; my $z = "AB"; tryneoalpha($Locale, 115, lcA($x, $y) == 1 && lcB($x, $y) == 1 || lcA($x, $z) == 0 && lcB($x, $z) == 0); } } # Recount the errors. foreach (99..115) { if ($Problem{$_} || !defined $Okay{$_} || !@{$Okay{$_}}) { if ($_ == 102) { print "# The failure of test 102 is not necessarily fatal.\n"; print "# It usually indicates a problem in the enviroment,\n"; print "# not in Perl itself.\n"; } print "not "; } print "ok $_\n"; } # Give final advice. my $didwarn = 0; foreach (99..115) { if ($Problem{$_}) { my @f = sort keys %{ $Problem{$_} }; my $f = join(" ", @f); $f =~ s/(.{50,60}) /$1\n#\t/g; print "#\n", "# The locale ", (@f == 1 ? "definition" : "definitions"), "\n#\n", "#\t", $f, "\n#\n", "# on your system may have errors because the locale test $_\n", "# failed in ", (@f == 1 ? "that locale" : "those locales"), ".\n"; print <