This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
7319e98afa171638802dd222625b18bd9372c639
[perl5.git] / lib / locale.t
1 #!./perl -wT
2
3 # This tests plain 'use locale' and adorned 'use locale ":not_characters"'
4 # Because these pragmas are compile time, and I (khw) am trying to test
5 # without using 'eval' as much as possible, which might cloud the issue,  the
6 # crucial parts of the code are duplicated in a block for each pragma.
7
8 # To make a TODO test, add the string 'TODO' to its %test_names value
9
10 binmode STDOUT, ':utf8';
11 binmode STDERR, ':utf8';
12
13 BEGIN {
14     chdir 't' if -d 't';
15     @INC = '../lib';
16     unshift @INC, '.';
17     require Config; import Config;
18     if (!$Config{d_setlocale} || $Config{ccflags} =~ /\bD?NO_LOCALE\b/) {
19         print "1..0\n";
20         exit;
21     }
22     $| = 1;
23 }
24
25 use strict;
26 use feature 'fc';
27
28 my $debug = $ENV{PERL_DEBUG_FULL_TEST} // 0;
29
30 # Certain tests have been shown to be problematical for a few locales.  Don't
31 # fail them unless at least this percentage of the tested locales fail.
32 # Some Windows machines are defective in every in every locale but the C,
33 # calling \t printable; superscripts to be digits, etc.  See
34 # http://markmail.org/message/5jwam4xsx4amsdnv
35 # (There aren't 1000 locales currently in existence, so 99.9 works)
36 my $acceptable_fold_failure_percentage = $^O eq 'MSWin32' ? 99.9 : 5;
37
38 use Dumpvalue;
39
40 my $dumper = Dumpvalue->new(
41                             tick => qq{"},
42                             quoteHighBit => 0,
43                             unctrl => "quote"
44                            );
45 sub debug {
46   return unless $debug;
47   my($mess) = join "", @_;
48   chop $mess;
49   print $dumper->stringify($mess,1), "\n";
50 }
51
52 sub debugf {
53     printf @_ if $debug;
54 }
55
56 my $have_setlocale = 0;
57 eval {
58     require POSIX;
59     import POSIX ':locale_h';
60     $have_setlocale++;
61 };
62
63 # Visual C's CRT goes silly on strings of the form "en_US.ISO8859-1"
64 # and mingw32 uses said silly CRT
65 # This doesn't seem to be an issue any more, at least on Windows XP,
66 # so re-enable the tests for Windows XP onwards.
67 my $winxp = ($^O eq 'MSWin32' && defined &Win32::GetOSVersion &&
68                 join('.', (Win32::GetOSVersion())[1..2]) >= 5.1);
69 $have_setlocale = 0 if ((($^O eq 'MSWin32' && !$winxp) || $^O eq 'NetWare') &&
70                 $Config{cc} =~ /^(cl|gcc)/i);
71
72 # UWIN seems to loop after taint tests, just skip for now
73 $have_setlocale = 0 if ($^O =~ /^uwin/);
74
75 $a = 'abc %';
76
77 my $test_num = 0;
78
79 sub ok {
80     my ($result, $message) = @_;
81     $message = "" unless defined $message;
82
83     print 'not ' unless ($result);
84     print "ok " . ++$test_num;
85     print " $message";
86     print "\n";
87 }
88
89 # First we'll do a lot of taint checking for locales.
90 # This is the easiest to test, actually, as any locale,
91 # even the default locale will taint under 'use locale'.
92
93 sub is_tainted { # hello, camel two.
94     no warnings 'uninitialized' ;
95     my $dummy;
96     local $@;
97     not eval { $dummy = join("", @_), kill 0; 1 }
98 }
99
100 sub check_taint ($;$) {
101     my $message_tail = $_[1] // "";
102     $message_tail = ": $message_tail" if $message_tail;
103     ok is_tainted($_[0]), "verify that is tainted$message_tail";
104 }
105
106 sub check_taint_not ($;$) {
107     my $message_tail = $_[1] // "";
108     $message_tail = ": $message_tail" if $message_tail;
109     ok((not is_tainted($_[0])), "verify that isn't tainted$message_tail");
110 }
111
112 "\tb\t" =~ /^m?(\s)(.*)\1$/;
113 check_taint_not   $&, "not tainted outside 'use locale'";
114 ;
115
116 use locale;     # engage locale and therefore locale taint.
117
118 check_taint_not   $a;
119
120 check_taint       uc($a);
121 check_taint       "\U$a";
122 check_taint       ucfirst($a);
123 check_taint       "\u$a";
124 check_taint       lc($a);
125 check_taint       fc($a);
126 check_taint       "\L$a";
127 check_taint       "\F$a";
128 check_taint       lcfirst($a);
129 check_taint       "\l$a";
130
131 check_taint_not  sprintf('%e', 123.456);
132 check_taint_not  sprintf('%f', 123.456);
133 check_taint_not  sprintf('%g', 123.456);
134 check_taint_not  sprintf('%d', 123.456);
135 check_taint_not  sprintf('%x', 123.456);
136
137 $_ = $a;        # untaint $_
138
139 $_ = uc($a);    # taint $_
140
141 check_taint      $_;
142
143 /(\w)/; # taint $&, $`, $', $+, $1.
144 check_taint      $&;
145 check_taint      $`;
146 check_taint      $';
147 check_taint      $+;
148 check_taint      $1;
149 check_taint_not  $2;
150
151 /(.)/;  # untaint $&, $`, $', $+, $1.
152 check_taint_not  $&;
153 check_taint_not  $`;
154 check_taint_not  $';
155 check_taint_not  $+;
156 check_taint_not  $1;
157 check_taint_not  $2;
158
159 /(\W)/; # taint $&, $`, $', $+, $1.
160 check_taint      $&;
161 check_taint      $`;
162 check_taint      $';
163 check_taint      $+;
164 check_taint      $1;
165 check_taint_not  $2;
166
167 /(\s)/; # taint $&, $`, $', $+, $1.
168 check_taint      $&;
169 check_taint      $`;
170 check_taint      $';
171 check_taint      $+;
172 check_taint      $1;
173 check_taint_not  $2;
174
175 /(\S)/; # taint $&, $`, $', $+, $1.
176 check_taint      $&;
177 check_taint      $`;
178 check_taint      $';
179 check_taint      $+;
180 check_taint      $1;
181 check_taint_not  $2;
182
183 $_ = $a;        # untaint $_
184
185 check_taint_not  $_;
186
187 /(b)/;          # this must not taint
188 check_taint_not  $&;
189 check_taint_not  $`;
190 check_taint_not  $';
191 check_taint_not  $+;
192 check_taint_not  $1;
193 check_taint_not  $2;
194
195 $_ = $a;        # untaint $_
196
197 check_taint_not  $_;
198
199 $b = uc($a);    # taint $b
200 s/(.+)/$b/;     # this must taint only the $_
201
202 check_taint      $_;
203 check_taint_not  $&;
204 check_taint_not  $`;
205 check_taint_not  $';
206 check_taint_not  $+;
207 check_taint_not  $1;
208 check_taint_not  $2;
209
210 $_ = $a;        # untaint $_
211
212 s/(.+)/b/;      # this must not taint
213 check_taint_not  $_;
214 check_taint_not  $&;
215 check_taint_not  $`;
216 check_taint_not  $';
217 check_taint_not  $+;
218 check_taint_not  $1;
219 check_taint_not  $2;
220
221 $b = $a;        # untaint $b
222
223 ($b = $a) =~ s/\w/$&/;
224 check_taint      $b;    # $b should be tainted.
225 check_taint_not  $a;    # $a should be not.
226
227 $_ = $a;        # untaint $_
228
229 s/(\w)/\l$1/;   # this must taint
230 check_taint      $_;
231 check_taint      $&;
232 check_taint      $`;
233 check_taint      $';
234 check_taint      $+;
235 check_taint      $1;
236 check_taint_not  $2;
237
238 $_ = $a;        # untaint $_
239
240 s/(\w)/\L$1/;   # this must taint
241 check_taint      $_;
242 check_taint      $&;
243 check_taint      $`;
244 check_taint      $';
245 check_taint      $+;
246 check_taint      $1;
247 check_taint_not  $2;
248
249 $_ = $a;        # untaint $_
250
251 s/(\w)/\u$1/;   # this must taint
252 check_taint      $_;
253 check_taint      $&;
254 check_taint      $`;
255 check_taint      $';
256 check_taint      $+;
257 check_taint      $1;
258 check_taint_not  $2;
259
260 $_ = $a;        # untaint $_
261
262 s/(\w)/\U$1/;   # this must taint
263 check_taint      $_;
264 check_taint      $&;
265 check_taint      $`;
266 check_taint      $';
267 check_taint      $+;
268 check_taint      $1;
269 check_taint_not  $2;
270
271 # After all this tainting $a should be cool.
272
273 check_taint_not  $a;
274
275 {   # This is just the previous tests copied here with a different
276     # compile-time pragma.
277
278     use locale ':not_characters'; # engage restricted locale with different
279                                   # tainting rules
280
281     check_taint_not   $a;
282
283     check_taint_not     uc($a);
284     check_taint_not     "\U$a";
285     check_taint_not     ucfirst($a);
286     check_taint_not     "\u$a";
287     check_taint_not     lc($a);
288     check_taint_not     fc($a);
289     check_taint_not     "\L$a";
290     check_taint_not     "\F$a";
291     check_taint_not     lcfirst($a);
292     check_taint_not     "\l$a";
293
294     check_taint_not  sprintf('%e', 123.456);
295     check_taint_not  sprintf('%f', 123.456);
296     check_taint_not  sprintf('%g', 123.456);
297     check_taint_not  sprintf('%d', 123.456);
298     check_taint_not  sprintf('%x', 123.456);
299
300     $_ = $a;    # untaint $_
301
302     $_ = uc($a);        # taint $_
303
304     check_taint_not     $_;
305
306     /(\w)/;     # taint $&, $`, $', $+, $1.
307     check_taint_not     $&;
308     check_taint_not     $`;
309     check_taint_not     $';
310     check_taint_not     $+;
311     check_taint_not     $1;
312     check_taint_not  $2;
313
314     /(.)/;      # untaint $&, $`, $', $+, $1.
315     check_taint_not  $&;
316     check_taint_not  $`;
317     check_taint_not  $';
318     check_taint_not  $+;
319     check_taint_not  $1;
320     check_taint_not  $2;
321
322     /(\W)/;     # taint $&, $`, $', $+, $1.
323     check_taint_not     $&;
324     check_taint_not     $`;
325     check_taint_not     $';
326     check_taint_not     $+;
327     check_taint_not     $1;
328     check_taint_not  $2;
329
330     /(\s)/;     # taint $&, $`, $', $+, $1.
331     check_taint_not     $&;
332     check_taint_not     $`;
333     check_taint_not     $';
334     check_taint_not     $+;
335     check_taint_not     $1;
336     check_taint_not  $2;
337
338     /(\S)/;     # taint $&, $`, $', $+, $1.
339     check_taint_not     $&;
340     check_taint_not     $`;
341     check_taint_not     $';
342     check_taint_not     $+;
343     check_taint_not     $1;
344     check_taint_not  $2;
345
346     $_ = $a;    # untaint $_
347
348     check_taint_not  $_;
349
350     /(b)/;              # this must not taint
351     check_taint_not  $&;
352     check_taint_not  $`;
353     check_taint_not  $';
354     check_taint_not  $+;
355     check_taint_not  $1;
356     check_taint_not  $2;
357
358     $_ = $a;    # untaint $_
359
360     check_taint_not  $_;
361
362     $b = uc($a);        # taint $b
363     s/(.+)/$b/; # this must taint only the $_
364
365     check_taint_not     $_;
366     check_taint_not  $&;
367     check_taint_not  $`;
368     check_taint_not  $';
369     check_taint_not  $+;
370     check_taint_not  $1;
371     check_taint_not  $2;
372
373     $_ = $a;    # untaint $_
374
375     s/(.+)/b/;  # this must not taint
376     check_taint_not  $_;
377     check_taint_not  $&;
378     check_taint_not  $`;
379     check_taint_not  $';
380     check_taint_not  $+;
381     check_taint_not  $1;
382     check_taint_not  $2;
383
384     $b = $a;    # untaint $b
385
386     ($b = $a) =~ s/\w/$&/;
387     check_taint_not     $b;     # $b should be tainted.
388     check_taint_not  $a;        # $a should be not.
389
390     $_ = $a;    # untaint $_
391
392     s/(\w)/\l$1/;       # this must taint
393     check_taint_not     $_;
394     check_taint_not     $&;
395     check_taint_not     $`;
396     check_taint_not     $';
397     check_taint_not     $+;
398     check_taint_not     $1;
399     check_taint_not  $2;
400
401     $_ = $a;    # untaint $_
402
403     s/(\w)/\L$1/;       # this must taint
404     check_taint_not     $_;
405     check_taint_not     $&;
406     check_taint_not     $`;
407     check_taint_not     $';
408     check_taint_not     $+;
409     check_taint_not     $1;
410     check_taint_not  $2;
411
412     $_ = $a;    # untaint $_
413
414     s/(\w)/\u$1/;       # this must taint
415     check_taint_not     $_;
416     check_taint_not     $&;
417     check_taint_not     $`;
418     check_taint_not     $';
419     check_taint_not     $+;
420     check_taint_not     $1;
421     check_taint_not  $2;
422
423     $_ = $a;    # untaint $_
424
425     s/(\w)/\U$1/;       # this must taint
426     check_taint_not     $_;
427     check_taint_not     $&;
428     check_taint_not     $`;
429     check_taint_not     $';
430     check_taint_not     $+;
431     check_taint_not     $1;
432     check_taint_not  $2;
433
434     # After all this tainting $a should be cool.
435
436     check_taint_not  $a;
437 }
438
439 # Here are in scope of 'use locale'
440
441 # I think we've seen quite enough of taint.
442 # Let us do some *real* locale work now,
443 # unless setlocale() is missing (i.e. minitest).
444
445 unless ($have_setlocale) {
446     print "1..$test_num\n";
447     exit;
448 }
449
450 # The test number before our first setlocale()
451 my $final_without_setlocale = $test_num;
452
453 # Find locales.
454
455 debug "# Scanning for locales...\n";
456
457 # Note that it's okay that some languages have their native names
458 # capitalized here even though that's not "right".  They are lowercased
459 # anyway later during the scanning process (and besides, some clueless
460 # vendor might have them capitalized erroneously anyway).
461
462 my $locales = <<EOF;
463 Afrikaans:af:za:1 15
464 Arabic:ar:dz eg sa:6 arabic8
465 Brezhoneg Breton:br:fr:1 15
466 Bulgarski Bulgarian:bg:bg:5
467 Chinese:zh:cn tw:cn.EUC eucCN eucTW euc.CN euc.TW Big5 GB2312 tw.EUC
468 Hrvatski Croatian:hr:hr:2
469 Cymraeg Welsh:cy:cy:1 14 15
470 Czech:cs:cz:2
471 Dansk Danish:da:dk:1 15
472 Nederlands Dutch:nl:be nl:1 15
473 English American British:en:au ca gb ie nz us uk zw:1 15 cp850
474 Esperanto:eo:eo:3
475 Eesti Estonian:et:ee:4 6 13
476 Suomi Finnish:fi:fi:1 15
477 Flamish::fl:1 15
478 Deutsch German:de:at be ch de lu:1 15
479 Euskaraz Basque:eu:es fr:1 15
480 Galego Galician:gl:es:1 15
481 Ellada Greek:el:gr:7 g8
482 Frysk:fy:nl:1 15
483 Greenlandic:kl:gl:4 6
484 Hebrew:iw:il:8 hebrew8
485 Hungarian:hu:hu:2
486 Indonesian:id:id:1 15
487 Gaeilge Irish:ga:IE:1 14 15
488 Italiano Italian:it:ch it:1 15
489 Nihongo Japanese:ja:jp:euc eucJP jp.EUC sjis
490 Korean:ko:kr:
491 Latine Latin:la:va:1 15
492 Latvian:lv:lv:4 6 13
493 Lithuanian:lt:lt:4 6 13
494 Macedonian:mk:mk:1 15
495 Maltese:mt:mt:3
496 Moldovan:mo:mo:2
497 Norsk Norwegian:no no\@nynorsk nb nn:no:1 15
498 Occitan:oc:es:1 15
499 Polski Polish:pl:pl:2
500 Rumanian:ro:ro:2
501 Russki Russian:ru:ru su ua:5 koi8 koi8r KOI8-R koi8u cp1251 cp866
502 Serbski Serbian:sr:yu:5
503 Slovak:sk:sk:2
504 Slovene Slovenian:sl:si:2
505 Sqhip Albanian:sq:sq:1 15
506 Svenska Swedish:sv:fi se:1 15
507 Thai:th:th:11 tis620
508 Turkish:tr:tr:9 turkish8
509 Yiddish:yi::1 15
510 EOF
511
512 if ($^O eq 'os390') {
513     # These cause heartburn.  Broken locales?
514     $locales =~ s/Svenska Swedish:sv:fi se:1 15\n//;
515     $locales =~ s/Thai:th:th:11 tis620\n//;
516 }
517
518 sub in_utf8 () { $^H & 0x08 || (${^OPEN} || "") =~ /:utf8/ }
519
520 if (in_utf8) {
521     require "lib/locale/utf8";
522 } else {
523     require "lib/locale/latin1";
524 }
525
526 my @Locale;
527 my $Locale;
528 my %posixes;
529
530 sub trylocale {
531     my $locale = shift;
532     return if grep { $locale eq $_ } @Locale;
533     return unless setlocale(&POSIX::LC_ALL, $locale);
534     my $badutf8;
535     {
536         local $SIG{__WARN__} = sub {
537             $badutf8 = $_[0] =~ /Malformed UTF-8/;
538         };
539         $Locale =~ /UTF-?8/i;
540     }
541
542     if ($badutf8) {
543         ok(0, "Locale name contains malformed utf8");
544         return;
545     }
546     push @Locale, $locale;
547 }
548
549 sub decode_encodings {
550     my @enc;
551
552     foreach (split(/ /, shift)) {
553         if (/^(\d+)$/) {
554             push @enc, "ISO8859-$1";
555             push @enc, "iso8859$1";     # HP
556             if ($1 eq '1') {
557                  push @enc, "roman8";   # HP
558             }
559         } else {
560             push @enc, $_;
561             push @enc, "$_.UTF-8";
562         }
563     }
564     if ($^O eq 'os390') {
565         push @enc, qw(IBM-037 IBM-819 IBM-1047);
566     }
567
568     return @enc;
569 }
570
571 trylocale("C");
572 trylocale("POSIX");
573 foreach (0..15) {
574     trylocale("ISO8859-$_");
575     trylocale("iso8859$_");
576     trylocale("iso8859-$_");
577     trylocale("iso_8859_$_");
578     trylocale("isolatin$_");
579     trylocale("isolatin-$_");
580     trylocale("iso_latin_$_");
581 }
582
583 # Sanitize the environment so that we can run the external 'locale'
584 # program without the taint mode getting grumpy.
585
586 # $ENV{PATH} is special in VMS.
587 delete $ENV{PATH} if $^O ne 'VMS' or $Config{d_setenv};
588
589 # Other subversive stuff.
590 delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
591
592 if (-x "/usr/bin/locale" && open(LOCALES, "/usr/bin/locale -a 2>/dev/null|")) {
593     while (<LOCALES>) {
594         # It seems that /usr/bin/locale steadfastly outputs 8 bit data, which
595         # ain't great when we're running this testPERL_UNICODE= so that utf8
596         # locales will cause all IO hadles to default to (assume) utf8
597         next unless utf8::valid($_);
598         chomp;
599         trylocale($_);
600     }
601     close(LOCALES);
602 } elsif ($^O eq 'VMS' && defined($ENV{'SYS$I18N_LOCALE'}) && -d 'SYS$I18N_LOCALE') {
603 # The SYS$I18N_LOCALE logical name search list was not present on
604 # VAX VMS V5.5-12, but was on AXP && VAX VMS V6.2 as well as later versions.
605     opendir(LOCALES, "SYS\$I18N_LOCALE:");
606     while ($_ = readdir(LOCALES)) {
607         chomp;
608         trylocale($_);
609     }
610     close(LOCALES);
611 } elsif (($^O eq 'openbsd' || $^O eq 'bitrig' ) && -e '/usr/share/locale') {
612
613    # OpenBSD doesn't have a locale executable, so reading /usr/share/locale
614    # is much easier and faster than the last resort method.
615
616     opendir(LOCALES, '/usr/share/locale');
617     while ($_ = readdir(LOCALES)) {
618         chomp;
619         trylocale($_);
620     }
621     close(LOCALES);
622 } else {
623
624     # This is going to be slow.
625
626     foreach my $locale (split(/\n/, $locales)) {
627         my ($locale_name, $language_codes, $country_codes, $encodings) =
628             split(/:/, $locale);
629         my @enc = decode_encodings($encodings);
630         foreach my $loc (split(/ /, $locale_name)) {
631             trylocale($loc);
632             foreach my $enc (@enc) {
633                 trylocale("$loc.$enc");
634             }
635             $loc = lc $loc;
636             foreach my $enc (@enc) {
637                 trylocale("$loc.$enc");
638             }
639         }
640         foreach my $lang (split(/ /, $language_codes)) {
641             trylocale($lang);
642             foreach my $country (split(/ /, $country_codes)) {
643                 my $lc = "${lang}_${country}";
644                 trylocale($lc);
645                 foreach my $enc (@enc) {
646                     trylocale("$lc.$enc");
647                 }
648                 my $lC = "${lang}_\U${country}";
649                 trylocale($lC);
650                 foreach my $enc (@enc) {
651                     trylocale("$lC.$enc");
652                 }
653             }
654         }
655     }
656 }
657
658 setlocale(&POSIX::LC_ALL, "C");
659
660 if ($^O eq 'darwin') {
661     # Darwin 8/Mac OS X 10.4 and 10.5 have bad Basque locales: perl bug #35895,
662     # Apple bug ID# 4139653. It also has a problem in Byelorussian.
663     (my $v) = $Config{osvers} =~ /^(\d+)/;
664     if ($v >= 8 and $v < 10) {
665         debug "# Skipping eu_ES, be_BY locales -- buggy in Darwin\n";
666         @Locale = grep ! m/^(eu_ES(?:\..*)?|be_BY\.CP1131)$/, @Locale;
667     } elsif ($v < 12) {
668         debug "# Skipping be_BY locales -- buggy in Darwin\n";
669         @Locale = grep ! m/^be_BY\.CP1131$/, @Locale;
670     }
671 }
672
673 @Locale = sort @Locale;
674
675 debug "# Locales =\n";
676 for ( @Locale ) {
677     debug "# $_\n";
678 }
679
680 my %Problem;
681 my %Okay;
682 my %Testing;
683 my @Added_alpha;   # Alphas that aren't in the C locale.
684 my %test_names;
685
686 sub display_characters {
687     # This returns a display string denoting the input parameter @_, each
688     # entry of which is a single character in the range 0-255.  The first part
689     # of the output is a string of the characters in @_ that are ASCII
690     # graphics, and hence unambiguously displayable.  They are given by code
691     # point order.  The second part is the remaining code points, the ordinals
692     # of which are each displayed as 2-digit hex.  Blanks are inserted so as
693     # to keep anything from the first part looking like a 2-digit hex number.
694
695     no locale;
696     my @chars = sort { ord $a <=> ord $b } @_;
697     my $output = "";
698     my $range_start;
699     my $start_class;
700     push @chars, chr(258);  # This sentinel simplifies the loop termination
701                             # logic
702     foreach my $i (0 .. @chars - 1) {
703         my $char = $chars[$i];
704         my $range_end;
705         my $class;
706
707         # We avoid using [:posix:] classes, as these are being tested in this
708         # file.  Each equivalence class below is for things that can appear in
709         # a range; those that can't be in a range have class -1.  0 for those
710         # which should be output in hex; and >0 for the other ranges
711         if ($char =~ /[A-Z]/) {
712             $class = 2;
713         }
714         elsif ($char =~ /[a-z]/) {
715             $class = 3;
716         }
717         elsif ($char =~ /[0-9]/) {
718             $class = 4;
719         }
720         # Uncomment to get literal punctuation displayed instead of hex
721         #elsif ($char =~ /[[\]!"#\$\%&\'()*+,.\/:\\;<=>?\@\^_`{|}~-]/) {
722         #    $class = -1;    # Punct never appears in a range
723         #}
724         else {
725             $class = 0;     # Output in hex
726         }
727
728         if (! defined $range_start) {
729             if ($class < 0) {
730                 $output .= " " . $char;
731             }
732             else {
733                 $range_start = ord $char;
734                 $start_class = $class;
735             }
736         } # A range ends if not consecutive, or the class-type changes
737         elsif (ord $char != ($range_end = ord($chars[$i-1])) + 1
738               || $class != $start_class)
739         {
740
741             # Here, the current character is not in the range.  This means the
742             # previous character must have been.  Output the range up through
743             # that one.
744             my $range_length = $range_end - $range_start + 1;
745             if ($start_class > 0) {
746                 $output .= " " . chr($range_start);
747                 $output .= "-" . chr($range_end) if $range_length > 1;
748             }
749             else {
750                 $output .= sprintf(" %02X", $range_start);
751                 $output .= sprintf("-%02X", $range_end) if $range_length > 1;
752             }
753
754             # Handle the new current character, as potentially beginning a new
755             # range
756             undef $range_start;
757             redo;
758         }
759     }
760
761     $output =~ s/^ //;
762     return $output;
763 }
764
765 sub report_result {
766     my ($Locale, $i, $pass_fail, $message) = @_;
767     $message //= "";
768     $message = "  ($message)" if $message;
769     unless ($pass_fail) {
770         $Problem{$i}{$Locale} = 1;
771         debug "# failed $i ($test_names{$i}) with locale '$Locale'$message\n";
772     } else {
773         push @{$Okay{$i}}, $Locale;
774     }
775 }
776
777 sub report_multi_result {
778     my ($Locale, $i, $results_ref) = @_;
779
780     # $results_ref points to an array, each element of which is a character that was
781     # in error for this test numbered '$i'.  If empty, the test passed
782
783     my $message = "";
784     if (@$results_ref) {
785         $message = join " ", "for", display_characters(@$results_ref);
786     }
787     report_result($Locale, $i, @$results_ref == 0, $message);
788 }
789
790 my $first_locales_test_number = $final_without_setlocale + 1;
791 my $locales_test_number;
792 my $not_necessarily_a_problem_test_number;
793 my $first_casing_test_number;
794 my $final_casing_test_number;
795 my %setlocale_failed;   # List of locales that setlocale() didn't work on
796
797 foreach $Locale (@Locale) {
798     $locales_test_number = $first_locales_test_number - 1;
799     debug "#\n";
800     debug "# Locale = $Locale\n";
801
802     unless (setlocale(&POSIX::LC_ALL, $Locale)) {
803         $setlocale_failed{$Locale} = $Locale;
804         next;
805     }
806
807     # We test UTF-8 locales only under ':not_characters'; otherwise they have
808     # documented deficiencies.  Non- UTF-8 locales are tested only under plain
809     # 'use locale', as otherwise we would have to convert everything in them
810     # to Unicode.
811     # The locale name doesn't necessarily have to have "utf8" in it to be a
812     # UTF-8 locale, but it works mostly.
813     my $is_utf8_locale = $Locale =~ /UTF-?8/i;
814
815     my %UPPER = ();     # All alpha X for which uc(X) == X and lc(X) != X
816     my %lower = ();     # All alpha X for which lc(X) == X and uc(X) != X
817     my %BoThCaSe = ();  # All alpha X for which uc(X) == lc(X) == X
818
819     if (! $is_utf8_locale) {
820         use locale;
821         @{$posixes{'word'}} = grep /\w/, map { chr } 0..255;
822         @{$posixes{'digit'}} = grep /\d/, map { chr } 0..255;
823         @{$posixes{'space'}} = grep /\s/, map { chr } 0..255;
824         @{$posixes{'alpha'}} = grep /[[:alpha:]]/, map {chr } 0..255;
825         @{$posixes{'alnum'}} = grep /[[:alnum:]]/, map {chr } 0..255;
826         @{$posixes{'ascii'}} = grep /[[:ascii:]]/, map {chr } 0..255;
827         @{$posixes{'blank'}} = grep /[[:blank:]]/, map {chr } 0..255;
828         @{$posixes{'cntrl'}} = grep /[[:cntrl:]]/, map {chr } 0..255;
829         @{$posixes{'graph'}} = grep /[[:graph:]]/, map {chr } 0..255;
830         @{$posixes{'lower'}} = grep /[[:lower:]]/, map {chr } 0..255;
831         @{$posixes{'print'}} = grep /[[:print:]]/, map {chr } 0..255;
832         @{$posixes{'punct'}} = grep /[[:punct:]]/, map {chr } 0..255;
833         @{$posixes{'upper'}} = grep /[[:upper:]]/, map {chr } 0..255;
834         @{$posixes{'xdigit'}} = grep /[[:xdigit:]]/, map {chr } 0..255;
835         @{$posixes{'cased'}} = grep /[[:upper:]]/i, map {chr } 0..255;
836
837         # Sieve the uppercase and the lowercase.
838
839         for (@{$posixes{'word'}}) {
840             if (/[^\d_]/) { # skip digits and the _
841                 if (uc($_) eq $_) {
842                     $UPPER{$_} = $_;
843                 }
844                 if (lc($_) eq $_) {
845                     $lower{$_} = $_;
846                 }
847             }
848         }
849     }
850     else {
851         use locale ':not_characters';
852         @{$posixes{'word'}} = grep /\w/, map { chr } 0..255;
853         @{$posixes{'digit'}} = grep /\d/, map { chr } 0..255;
854         @{$posixes{'space'}} = grep /\s/, map { chr } 0..255;
855         @{$posixes{'alpha'}} = grep /[[:alpha:]]/, map {chr } 0..255;
856         @{$posixes{'alnum'}} = grep /[[:alnum:]]/, map {chr } 0..255;
857         @{$posixes{'ascii'}} = grep /[[:ascii:]]/, map {chr } 0..255;
858         @{$posixes{'blank'}} = grep /[[:blank:]]/, map {chr } 0..255;
859         @{$posixes{'cntrl'}} = grep /[[:cntrl:]]/, map {chr } 0..255;
860         @{$posixes{'graph'}} = grep /[[:graph:]]/, map {chr } 0..255;
861         @{$posixes{'lower'}} = grep /[[:lower:]]/, map {chr } 0..255;
862         @{$posixes{'print'}} = grep /[[:print:]]/, map {chr } 0..255;
863         @{$posixes{'punct'}} = grep /[[:punct:]]/, map {chr } 0..255;
864         @{$posixes{'upper'}} = grep /[[:upper:]]/, map {chr } 0..255;
865         @{$posixes{'xdigit'}} = grep /[[:xdigit:]]/, map {chr } 0..255;
866         @{$posixes{'cased'}} = grep /[[:upper:]]/i, map {chr } 0..255;
867         for (@{$posixes{'word'}}) {
868             if (/[^\d_]/) { # skip digits and the _
869                 if (uc($_) eq $_) {
870                     $UPPER{$_} = $_;
871                 }
872                 if (lc($_) eq $_) {
873                     $lower{$_} = $_;
874                 }
875             }
876         }
877     }
878
879     # Ordered, where possible,  in groups of "this is a subset of the next
880     # one"
881     debug "# :upper:  = ", display_characters(@{$posixes{'upper'}}), "\n";
882     debug "# :lower:  = ", display_characters(@{$posixes{'lower'}}), "\n";
883     debug "# :cased:  = ", display_characters(@{$posixes{'cased'}}), "\n";
884     debug "# :alpha:  = ", display_characters(@{$posixes{'alpha'}}), "\n";
885     debug "# :alnum:  = ", display_characters(@{$posixes{'alnum'}}), "\n";
886     debug "#  w       = ", display_characters(@{$posixes{'word'}}), "\n";
887     debug "# :graph:  = ", display_characters(@{$posixes{'graph'}}), "\n";
888     debug "# :print:  = ", display_characters(@{$posixes{'print'}}), "\n";
889     debug "#  d       = ", display_characters(@{$posixes{'digit'}}), "\n";
890     debug "# :xdigit: = ", display_characters(@{$posixes{'xdigit'}}), "\n";
891     debug "# :blank:  = ", display_characters(@{$posixes{'blank'}}), "\n";
892     debug "#  s       = ", display_characters(@{$posixes{'space'}}), "\n";
893     debug "# :punct:  = ", display_characters(@{$posixes{'punct'}}), "\n";
894     debug "# :cntrl:  = ", display_characters(@{$posixes{'cntrl'}}), "\n";
895     debug "# :ascii:  = ", display_characters(@{$posixes{'ascii'}}), "\n";
896
897     foreach (keys %UPPER) {
898
899         $BoThCaSe{$_}++ if exists $lower{$_};
900     }
901     foreach (keys %lower) {
902         $BoThCaSe{$_}++ if exists $UPPER{$_};
903     }
904     foreach (keys %BoThCaSe) {
905         delete $UPPER{$_};
906         delete $lower{$_};
907     }
908
909     my %Unassigned;
910     foreach my $ord ( 0 .. 255 ) {
911         $Unassigned{chr $ord} = 1;
912     }
913     foreach my $class (keys %posixes) {
914         foreach my $char (@{$posixes{$class}}) {
915             delete $Unassigned{$char};
916         }
917     }
918
919     debug "# UPPER    = ", display_characters(keys %UPPER), "\n";
920     debug "# lower    = ", display_characters(keys %lower), "\n";
921     debug "# BoThCaSe = ", display_characters(keys %BoThCaSe), "\n";
922     debug "# Unassigned = ", display_characters(sort { ord $a <=> ord $b } keys %Unassigned), "\n";
923
924     my @failures;
925     my @fold_failures;
926     foreach my $x (sort keys %UPPER) {
927         my $ok;
928         my $fold_ok;
929         if ($is_utf8_locale) {
930             use locale ':not_characters';
931             $ok = $x =~ /[[:upper:]]/;
932             $fold_ok = $x =~ /[[:lower:]]/i;
933         }
934         else {
935             use locale;
936             $ok = $x =~ /[[:upper:]]/;
937             $fold_ok = $x =~ /[[:lower:]]/i;
938         }
939         push @failures, $x unless $ok;
940         push @fold_failures, $x unless $fold_ok;
941     }
942     $locales_test_number++;
943     $first_casing_test_number = $locales_test_number;
944     $test_names{$locales_test_number} = 'Verify that /[[:upper:]]/ matches all alpha X for which uc(X) == X and lc(X) != X';
945     report_multi_result($Locale, $locales_test_number, \@failures);
946
947     $locales_test_number++;
948
949     $test_names{$locales_test_number} = 'Verify that /[[:lower:]]/i matches all alpha X for which uc(X) == X and lc(X) != X';
950     report_multi_result($Locale, $locales_test_number, \@fold_failures);
951
952     undef @failures;
953     undef @fold_failures;
954
955     foreach my $x (sort keys %lower) {
956         my $ok;
957         my $fold_ok;
958         if ($is_utf8_locale) {
959             use locale ':not_characters';
960             $ok = $x =~ /[[:lower:]]/;
961             $fold_ok = $x =~ /[[:upper:]]/i;
962         }
963         else {
964             use locale;
965             $ok = $x =~ /[[:lower:]]/;
966             $fold_ok = $x =~ /[[:upper:]]/i;
967         }
968         push @failures, $x unless $ok;
969         push @fold_failures, $x unless $fold_ok;
970     }
971
972     $locales_test_number++;
973     $test_names{$locales_test_number} = 'Verify that /[[:lower:]]/ matches all alpha X for which lc(X) == X and uc(X) != X';
974     report_multi_result($Locale, $locales_test_number, \@failures);
975
976     $locales_test_number++;
977     $test_names{$locales_test_number} = 'Verify that /[[:upper:]]/i matches all alpha X for which lc(X) == X and uc(X) != X';
978     report_multi_result($Locale, $locales_test_number, \@fold_failures);
979
980     {   # Find the alphabetic characters that are not considered alphabetics
981         # in the default (C) locale.
982
983         no locale;
984
985         @Added_alpha = ();
986         for (keys %UPPER, keys %lower, keys %BoThCaSe) {
987             push(@Added_alpha, $_) if (/\W/);
988         }
989     }
990
991     @Added_alpha = sort @Added_alpha;
992
993     debug "# Added_alpha = ", display_characters(@Added_alpha), "\n";
994
995     # Cross-check the whole 8-bit character set.
996
997     ++$locales_test_number;
998     my @f;
999     $test_names{$locales_test_number} = 'Verify that \w and [:word:] are identical';
1000     for (map { chr } 0..255) {
1001         if ($is_utf8_locale) {
1002             use locale ':not_characters';
1003             push @f, $_ unless /[[:word:]]/ == /\w/;
1004         }
1005         else {
1006             push @f, $_ unless /[[:word:]]/ == /\w/;
1007         }
1008     }
1009     report_multi_result($Locale, $locales_test_number, \@f);
1010
1011     ++$locales_test_number;
1012     undef @f;
1013     $test_names{$locales_test_number} = 'Verify that \d and [:digit:] are identical';
1014     for (map { chr } 0..255) {
1015         if ($is_utf8_locale) {
1016             use locale ':not_characters';
1017             push @f, $_ unless /[[:digit:]]/ == /\d/;
1018         }
1019         else {
1020             push @f, $_ unless /[[:digit:]]/ == /\d/;
1021         }
1022     }
1023     report_multi_result($Locale, $locales_test_number, \@f);
1024
1025     ++$locales_test_number;
1026     undef @f;
1027     $test_names{$locales_test_number} = 'Verify that \s and [:space:] are identical';
1028     for (map { chr } 0..255) {
1029         if ($is_utf8_locale) {
1030             use locale ':not_characters';
1031             push @f, $_ unless /[[:space:]]/ == /\s/;
1032         }
1033         else {
1034             push @f, $_ unless /[[:space:]]/ == /\s/;
1035         }
1036     }
1037     report_multi_result($Locale, $locales_test_number, \@f);
1038
1039     ++$locales_test_number;
1040     undef @f;
1041     $test_names{$locales_test_number} = 'Verify that [:posix:] and [:^posix:] are mutually exclusive';
1042     for (map { chr } 0..255) {
1043         if ($is_utf8_locale) {
1044             use locale ':not_characters';
1045             push @f, $_ unless   (/[[:alpha:]]/ xor /[[:^alpha:]]/)   ||
1046                     (/[[:alnum:]]/ xor /[[:^alnum:]]/)   ||
1047                     (/[[:ascii:]]/ xor /[[:^ascii:]]/)   ||
1048                     (/[[:blank:]]/ xor /[[:^blank:]]/)   ||
1049                     (/[[:cntrl:]]/ xor /[[:^cntrl:]]/)   ||
1050                     (/[[:digit:]]/ xor /[[:^digit:]]/)   ||
1051                     (/[[:graph:]]/ xor /[[:^graph:]]/)   ||
1052                     (/[[:lower:]]/ xor /[[:^lower:]]/)   ||
1053                     (/[[:print:]]/ xor /[[:^print:]]/)   ||
1054                     (/[[:space:]]/ xor /[[:^space:]]/)   ||
1055                     (/[[:upper:]]/ xor /[[:^upper:]]/)   ||
1056                     (/[[:word:]]/  xor /[[:^word:]]/)    ||
1057                     (/[[:xdigit:]]/ xor /[[:^xdigit:]]/) ||
1058
1059                     # effectively is what [:cased:] would be if it existed.
1060                     (/[[:upper:]]/i xor /[[:^upper:]]/i);
1061         }
1062         else {
1063             push @f, $_ unless   (/[[:alpha:]]/ xor /[[:^alpha:]]/)   ||
1064                     (/[[:alnum:]]/ xor /[[:^alnum:]]/)   ||
1065                     (/[[:ascii:]]/ xor /[[:^ascii:]]/)   ||
1066                     (/[[:blank:]]/ xor /[[:^blank:]]/)   ||
1067                     (/[[:cntrl:]]/ xor /[[:^cntrl:]]/)   ||
1068                     (/[[:digit:]]/ xor /[[:^digit:]]/)   ||
1069                     (/[[:graph:]]/ xor /[[:^graph:]]/)   ||
1070                     (/[[:lower:]]/ xor /[[:^lower:]]/)   ||
1071                     (/[[:print:]]/ xor /[[:^print:]]/)   ||
1072                     (/[[:space:]]/ xor /[[:^space:]]/)   ||
1073                     (/[[:upper:]]/ xor /[[:^upper:]]/)   ||
1074                     (/[[:word:]]/  xor /[[:^word:]]/)    ||
1075                     (/[[:xdigit:]]/ xor /[[:^xdigit:]]/) ||
1076                     (/[[:upper:]]/i xor /[[:^upper:]]/i);
1077         }
1078     }
1079     report_multi_result($Locale, $locales_test_number, \@f);
1080
1081     # The rules for the relationships are given in:
1082     # http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap07.html
1083
1084
1085     ++$locales_test_number;
1086     undef @f;
1087     $test_names{$locales_test_number} = 'Verify that [:lower:] contains at least a-z';
1088     for ('a' .. 'z') {
1089         if ($is_utf8_locale) {
1090             use locale ':not_characters';
1091             push @f, $_  unless /[[:lower:]]/;
1092         }
1093         else {
1094             push @f, $_  unless /[[:lower:]]/;
1095         }
1096     }
1097     report_multi_result($Locale, $locales_test_number, \@f);
1098
1099     ++$locales_test_number;
1100     undef @f;
1101     $test_names{$locales_test_number} = 'Verify that [:lower:] is a subset of [:alpha:]';
1102     for (map { chr } 0..255) {
1103         if ($is_utf8_locale) {
1104             use locale ':not_characters';
1105             push @f, $_  if /[[:lower:]]/ and ! /[[:alpha:]]/;
1106         }
1107         else {
1108             push @f, $_  if /[[:lower:]]/ and ! /[[:alpha:]]/;
1109         }
1110     }
1111     report_multi_result($Locale, $locales_test_number, \@f);
1112
1113     ++$locales_test_number;
1114     undef @f;
1115     $test_names{$locales_test_number} = 'Verify that [:upper:] contains at least A-Z';
1116     for ('A' .. 'Z') {
1117         if ($is_utf8_locale) {
1118             use locale ':not_characters';
1119             push @f, $_  unless /[[:upper:]]/;
1120         }
1121         else {
1122             push @f, $_  unless /[[:upper:]]/;
1123         }
1124     }
1125     report_multi_result($Locale, $locales_test_number, \@f);
1126
1127     ++$locales_test_number;
1128     undef @f;
1129     $test_names{$locales_test_number} = 'Verify that [:upper:] is a subset of [:alpha:]';
1130     for (map { chr } 0..255) {
1131         if ($is_utf8_locale) {
1132             use locale ':not_characters';
1133             push @f, $_  if /[[:upper:]]/ and ! /[[:alpha:]]/;
1134         }
1135         else {
1136             push @f, $_ if /[[:upper:]]/  and ! /[[:alpha:]]/;
1137         }
1138     }
1139     report_multi_result($Locale, $locales_test_number, \@f);
1140
1141     ++$locales_test_number;
1142     undef @f;
1143     $test_names{$locales_test_number} = 'Verify that /[[:lower:]]/i is a subset of [:alpha:]';
1144     for (map { chr } 0..255) {
1145         if ($is_utf8_locale) {
1146             use locale ':not_characters';
1147             push @f, $_ if /[[:lower:]]/i  and ! /[[:alpha:]]/;
1148         }
1149         else {
1150             push @f, $_ if /[[:lower:]]/i  and ! /[[:alpha:]]/;
1151         }
1152     }
1153     report_multi_result($Locale, $locales_test_number, \@f);
1154
1155     ++$locales_test_number;
1156     undef @f;
1157     $test_names{$locales_test_number} = 'Verify that [:alpha:] is a subset of [:alnum:]';
1158     for (map { chr } 0..255) {
1159         if ($is_utf8_locale) {
1160             use locale ':not_characters';
1161             push @f, $_ if /[[:alpha:]]/  and ! /[[:alnum:]]/;
1162         }
1163         else {
1164             push @f, $_ if /[[:alpha:]]/  and ! /[[:alnum:]]/;
1165         }
1166     }
1167     report_multi_result($Locale, $locales_test_number, \@f);
1168
1169     ++$locales_test_number;
1170     undef @f;
1171     $test_names{$locales_test_number} = 'Verify that [:digit:] contains at least 0-9';
1172     for ('0' .. '9') {
1173         if ($is_utf8_locale) {
1174             use locale ':not_characters';
1175             push @f, $_  unless /[[:digit:]]/;
1176         }
1177         else {
1178             push @f, $_  unless /[[:digit:]]/;
1179         }
1180     }
1181     report_multi_result($Locale, $locales_test_number, \@f);
1182
1183     ++$locales_test_number;
1184     undef @f;
1185     $test_names{$locales_test_number} = 'Verify that [:digit:] is a subset of [:alnum:]';
1186     for (map { chr } 0..255) {
1187         if ($is_utf8_locale) {
1188             use locale ':not_characters';
1189             push @f, $_ if /[[:digit:]]/  and ! /[[:alnum:]]/;
1190         }
1191         else {
1192             push @f, $_ if /[[:digit:]]/  and ! /[[:alnum:]]/;
1193         }
1194     }
1195     report_multi_result($Locale, $locales_test_number, \@f);
1196
1197     ++$locales_test_number;
1198     undef @f;
1199     $test_names{$locales_test_number} = 'Verify that [:digit:] matches either 10 or 20 code points';
1200     report_result($Locale, $locales_test_number, @{$posixes{'digit'}} == 10 || @{$posixes{'digit'}} == 20);
1201
1202     ++$locales_test_number;
1203     undef @f;
1204     $test_names{$locales_test_number} = 'Verify that if there is a second set of digits in [:digit:], they are consecutive';
1205     if (@{$posixes{'digit'}} == 20) {
1206         my $previous_ord;
1207         for (map { chr } 0..255) {
1208             next unless /[[:digit:]]/;
1209             next if /[0-9]/;
1210             if (defined $previous_ord) {
1211                 if ($is_utf8_locale) {
1212                     use locale ':not_characters';
1213                     push @f, $_ if ord $_ != $previous_ord + 1;
1214                 }
1215                 else {
1216                     push @f, $_ if ord $_ != $previous_ord + 1;
1217                 }
1218             }
1219             $previous_ord = ord $_;
1220         }
1221     }
1222     report_multi_result($Locale, $locales_test_number, \@f);
1223
1224     ++$locales_test_number;
1225     undef @f;
1226     $test_names{$locales_test_number} = 'Verify that [:digit:] is a subset of [:xdigit:]';
1227     for (map { chr } 0..255) {
1228         if ($is_utf8_locale) {
1229             use locale ':not_characters';
1230             push @f, $_ if /[[:digit:]]/  and ! /[[:xdigit:]]/;
1231         }
1232         else {
1233             push @f, $_ if /[[:digit:]]/  and ! /[[:xdigit:]]/;
1234         }
1235     }
1236     report_multi_result($Locale, $locales_test_number, \@f);
1237
1238     ++$locales_test_number;
1239     undef @f;
1240     $test_names{$locales_test_number} = 'Verify that [:xdigit:] contains at least A-F, a-f';
1241     for ('A' .. 'F', 'a' .. 'f') {
1242         if ($is_utf8_locale) {
1243             use locale ':not_characters';
1244             push @f, $_  unless /[[:xdigit:]]/;
1245         }
1246         else {
1247             push @f, $_  unless /[[:xdigit:]]/;
1248         }
1249     }
1250     report_multi_result($Locale, $locales_test_number, \@f);
1251
1252     ++$locales_test_number;
1253     undef @f;
1254     $test_names{$locales_test_number} = 'Verify that any additional members of [:xdigit:], are in groups of 6 consecutive code points';
1255     my $previous_ord;
1256     my $count = 0;
1257     for (map { chr } 0..255) {
1258         next unless /[[:xdigit:]]/;
1259         next if /[[:digit:]]/;
1260         next if /[A-Fa-f]/;
1261         if (defined $previous_ord) {
1262             if ($is_utf8_locale) {
1263                 use locale ':not_characters';
1264                 push @f, $_ if ord $_ != $previous_ord + 1;
1265             }
1266             else {
1267                 push @f, $_ if ord $_ != $previous_ord + 1;
1268             }
1269         }
1270         $count++;
1271         if ($count == 6) {
1272             undef $previous_ord;
1273         }
1274         else {
1275             $previous_ord = ord $_;
1276         }
1277     }
1278     report_multi_result($Locale, $locales_test_number, \@f);
1279
1280     ++$locales_test_number;
1281     undef @f;
1282     $test_names{$locales_test_number} = 'Verify that [:xdigit:] is a subset of [:graph:]';
1283     for (map { chr } 0..255) {
1284         if ($is_utf8_locale) {
1285             use locale ':not_characters';
1286             push @f, $_ if /[[:xdigit:]]/  and ! /[[:graph:]]/;
1287         }
1288         else {
1289             push @f, $_ if /[[:xdigit:]]/  and ! /[[:graph:]]/;
1290         }
1291     }
1292     report_multi_result($Locale, $locales_test_number, \@f);
1293
1294     # Note that xdigit doesn't have to be a subset of alnum
1295
1296     ++$locales_test_number;
1297     undef @f;
1298     $test_names{$locales_test_number} = 'Verify that [:punct:] is a subset of [:graph:]';
1299     for (map { chr } 0..255) {
1300         if ($is_utf8_locale) {
1301             use locale ':not_characters';
1302             push @f, $_ if /[[:punct:]]/  and ! /[[:graph:]]/;
1303         }
1304         else {
1305             push @f, $_ if /[[:punct:]]/  and ! /[[:graph:]]/;
1306         }
1307     }
1308     report_multi_result($Locale, $locales_test_number, \@f);
1309
1310     ++$locales_test_number;
1311     undef @f;
1312     $test_names{$locales_test_number} = 'Verify that the space character is not in [:graph:]';
1313     if ($is_utf8_locale) {
1314         use locale ':not_characters';
1315         push @f, " " if " " =~ /[[:graph:]]/;
1316     }
1317     else {
1318         push @f, " " if " " =~ /[[:graph:]]/;
1319     }
1320     report_multi_result($Locale, $locales_test_number, \@f);
1321
1322     ++$locales_test_number;
1323     undef @f;
1324     $test_names{$locales_test_number} = 'Verify that [:space:] contains at least [\f\n\r\t\cK ]';
1325     for (' ', "\f", "\n", "\r", "\t", "\cK") {
1326         if ($is_utf8_locale) {
1327             use locale ':not_characters';
1328             push @f, $_  unless /[[:space:]]/;
1329         }
1330         else {
1331             push @f, $_  unless /[[:space:]]/;
1332         }
1333     }
1334     report_multi_result($Locale, $locales_test_number, \@f);
1335
1336     ++$locales_test_number;
1337     undef @f;
1338     $test_names{$locales_test_number} = 'Verify that [:blank:] contains at least [\t ]';
1339     for (' ', "\t") {
1340         if ($is_utf8_locale) {
1341             use locale ':not_characters';
1342             push @f, $_  unless /[[:blank:]]/;
1343         }
1344         else {
1345             push @f, $_  unless /[[:blank:]]/;
1346         }
1347     }
1348     report_multi_result($Locale, $locales_test_number, \@f);
1349
1350     ++$locales_test_number;
1351     undef @f;
1352     $test_names{$locales_test_number} = 'Verify that [:blank:] is a subset of [:space:]';
1353     for (map { chr } 0..255) {
1354         if ($is_utf8_locale) {
1355             use locale ':not_characters';
1356             push @f, $_ if /[[:blank:]]/  and ! /[[:space:]]/;
1357         }
1358         else {
1359             push @f, $_ if /[[:blank:]]/  and ! /[[:space:]]/;
1360         }
1361     }
1362     report_multi_result($Locale, $locales_test_number, \@f);
1363
1364     ++$locales_test_number;
1365     undef @f;
1366     $test_names{$locales_test_number} = 'Verify that [:graph:] is a subset of [:print:]';
1367     for (map { chr } 0..255) {
1368         if ($is_utf8_locale) {
1369             use locale ':not_characters';
1370             push @f, $_ if /[[:graph:]]/  and ! /[[:print:]]/;
1371         }
1372         else {
1373             push @f, $_ if /[[:graph:]]/  and ! /[[:print:]]/;
1374         }
1375     }
1376     report_multi_result($Locale, $locales_test_number, \@f);
1377
1378     ++$locales_test_number;
1379     undef @f;
1380     $test_names{$locales_test_number} = 'Verify that the space character is in [:print:]';
1381     if ($is_utf8_locale) {
1382         use locale ':not_characters';
1383         push @f, " " if " " !~ /[[:print:]]/;
1384     }
1385     else {
1386         push @f, " " if " " !~ /[[:print:]]/;
1387     }
1388     report_multi_result($Locale, $locales_test_number, \@f);
1389
1390     ++$locales_test_number;
1391     undef @f;
1392     $test_names{$locales_test_number} = 'Verify that isn\'t both [:cntrl:] and [:print:]';
1393     for (map { chr } 0..255) {
1394         if ($is_utf8_locale) {
1395             use locale ':not_characters';
1396             push @f, $_ if (/[[:print:]]/ and /[[:cntrl:]]/);
1397         }
1398         else {
1399             push @f, $_ if (/[[:print:]]/ and /[[:cntrl:]]/);
1400         }
1401     }
1402     report_multi_result($Locale, $locales_test_number, \@f);
1403
1404     ++$locales_test_number;
1405     undef @f;
1406     $test_names{$locales_test_number} = 'Verify that isn\'t both [:alpha:] and [:digit:]';
1407     for (map { chr } 0..255) {
1408         if ($is_utf8_locale) {
1409             use locale ':not_characters';
1410             push @f, $_ if /[[:alpha:]]/ and /[[:digit:]]/;
1411         }
1412         else {
1413             push @f, $_ if /[[:alpha:]]/ and /[[:digit:]]/;
1414         }
1415     }
1416     report_multi_result($Locale, $locales_test_number, \@f);
1417
1418     ++$locales_test_number;
1419     undef @f;
1420     $test_names{$locales_test_number} = 'Verify that isn\'t both [:alnum:] and [:punct:]';
1421     for (map { chr } 0..255) {
1422         if ($is_utf8_locale) {
1423             use locale ':not_characters';
1424             push @f, $_ if /[[:alnum:]]/ and /[[:punct:]]/;
1425         }
1426         else {
1427             push @f, $_ if /[[:alnum:]]/ and /[[:punct:]]/;
1428         }
1429     }
1430     report_multi_result($Locale, $locales_test_number, \@f);
1431
1432     ++$locales_test_number;
1433     undef @f;
1434     $test_names{$locales_test_number} = 'Verify that isn\'t both [:xdigit:] and [:punct:]';
1435     for (map { chr } 0..255) {
1436         if ($is_utf8_locale) {
1437             use locale ':not_characters';
1438             push @f, $_ if (/[[:punct:]]/ and /[[:xdigit:]]/);
1439         }
1440         else {
1441             push @f, $_ if (/[[:punct:]]/ and /[[:xdigit:]]/);
1442         }
1443     }
1444     report_multi_result($Locale, $locales_test_number, \@f);
1445
1446     ++$locales_test_number;
1447     undef @f;
1448     $test_names{$locales_test_number} = 'Verify that isn\'t both [:graph:] and [:space:]';
1449     for (map { chr } 0..255) {
1450         if ($is_utf8_locale) {
1451             use locale ':not_characters';
1452             push @f, $_ if (/[[:graph:]]/ and /[[:space:]]/);
1453         }
1454         else {
1455             push @f, $_ if (/[[:graph:]]/ and /[[:space:]]/);
1456         }
1457     }
1458     report_multi_result($Locale, $locales_test_number, \@f);
1459
1460     $final_casing_test_number = $locales_test_number;
1461
1462     # Test for read-only scalars' locale vs non-locale comparisons.
1463
1464     {
1465         no locale;
1466         my $ok;
1467         $a = "qwerty";
1468         if ($is_utf8_locale) {
1469             use locale ':not_characters';
1470             $ok = ($a cmp "qwerty") == 0;
1471         }
1472         else {
1473             use locale;
1474             $ok = ($a cmp "qwerty") == 0;
1475         }
1476         report_result($Locale, ++$locales_test_number, $ok);
1477         $test_names{$locales_test_number} = 'Verify that cmp works with a read-only scalar; no- vs locale';
1478     }
1479
1480     {
1481         my ($from, $to, $lesser, $greater,
1482             @test, %test, $test, $yes, $no, $sign);
1483
1484         ++$locales_test_number;
1485         $test_names{$locales_test_number} = 'Verify that "le", "ne", etc work';
1486         $not_necessarily_a_problem_test_number = $locales_test_number;
1487         for (0..9) {
1488             # Select a slice.
1489             $from = int(($_*@{$posixes{'word'}})/10);
1490             $to = $from + int(@{$posixes{'word'}}/10);
1491             $to = $#{$posixes{'word'}} if ($to > $#{$posixes{'word'}});
1492             $lesser  = join('', @{$posixes{'word'}}[$from..$to]);
1493             # Select a slice one character on.
1494             $from++; $to++;
1495             $to = $#{$posixes{'word'}} if ($to > $#{$posixes{'word'}});
1496             $greater = join('', @{$posixes{'word'}}[$from..$to]);
1497             if ($is_utf8_locale) {
1498                 use locale ':not_characters';
1499                 ($yes, $no, $sign) = ($lesser lt $greater
1500                                     ? ("    ", "not ", 1)
1501                                     : ("not ", "    ", -1));
1502             }
1503             else {
1504                 use locale;
1505                 ($yes, $no, $sign) = ($lesser lt $greater
1506                                     ? ("    ", "not ", 1)
1507                                     : ("not ", "    ", -1));
1508             }
1509             # all these tests should FAIL (return 0).  Exact lt or gt cannot
1510             # be tested because in some locales, say, eacute and E may test
1511             # equal.
1512             @test =
1513                 (
1514                     $no.'    ($lesser  le $greater)',  # 1
1515                     'not      ($lesser  ne $greater)', # 2
1516                     '         ($lesser  eq $greater)', # 3
1517                     $yes.'    ($lesser  ge $greater)', # 4
1518                     $yes.'    ($lesser  ge $greater)', # 5
1519                     $yes.'    ($greater le $lesser )', # 7
1520                     'not      ($greater ne $lesser )', # 8
1521                     '         ($greater eq $lesser )', # 9
1522                     $no.'     ($greater ge $lesser )', # 10
1523                     'not (($lesser cmp $greater) == -($sign))' # 11
1524                     );
1525             @test{@test} = 0 x @test;
1526             $test = 0;
1527             for my $ti (@test) {
1528                 if ($is_utf8_locale) {
1529                     use locale ':not_characters';
1530                     $test{$ti} = eval $ti;
1531                 }
1532                 else {
1533                     # Already in 'use locale';
1534                     $test{$ti} = eval $ti;
1535                 }
1536                 $test ||= $test{$ti}
1537             }
1538             report_result($Locale, $locales_test_number, $test == 0);
1539             if ($test) {
1540                 debug "# lesser  = '$lesser'\n";
1541                 debug "# greater = '$greater'\n";
1542                 debug "# lesser cmp greater = ",
1543                         $lesser cmp $greater, "\n";
1544                 debug "# greater cmp lesser = ",
1545                         $greater cmp $lesser, "\n";
1546                 debug "# (greater) from = $from, to = $to\n";
1547                 for my $ti (@test) {
1548                     debugf("# %-40s %-4s", $ti,
1549                             $test{$ti} ? 'FAIL' : 'ok');
1550                     if ($ti =~ /\(\.*(\$.+ +cmp +\$[^\)]+)\.*\)/) {
1551                         debugf("(%s == %4d)", $1, eval $1);
1552                     }
1553                     debug "\n#";
1554                 }
1555
1556                 last;
1557             }
1558         }
1559     }
1560
1561     my $ok1;
1562     my $ok2;
1563     my $ok3;
1564     my $ok4;
1565     my $ok5;
1566     my $ok6;
1567     my $ok7;
1568     my $ok8;
1569     my $ok9;
1570     my $ok10;
1571     my $ok11;
1572     my $ok12;
1573     my $ok13;
1574     my $ok14;
1575     my $ok15;
1576     my $ok16;
1577     my $ok17;
1578     my $ok18;
1579
1580     my $c;
1581     my $d;
1582     my $e;
1583     my $f;
1584     my $g;
1585     my $h;
1586     my $i;
1587     my $j;
1588
1589     if (! $is_utf8_locale) {
1590         use locale;
1591
1592         my ($x, $y) = (1.23, 1.23);
1593
1594         $a = "$x";
1595         printf ''; # printf used to reset locale to "C"
1596         $b = "$y";
1597         $ok1 = $a eq $b;
1598
1599         $c = "$x";
1600         my $z = sprintf ''; # sprintf used to reset locale to "C"
1601         $d = "$y";
1602         $ok2 = $c eq $d;
1603         {
1604
1605             use warnings;
1606             my $w = 0;
1607             local $SIG{__WARN__} =
1608                 sub {
1609                     print "# @_\n";
1610                     $w++;
1611                 };
1612
1613             # The == (among other ops) used to warn for locales
1614             # that had something else than "." as the radix character.
1615
1616             $ok3 = $c == 1.23;
1617             $ok4 = $c == $x;
1618             $ok5 = $c == $d;
1619             {
1620                 no locale;
1621
1622                 $e = "$x";
1623
1624                 $ok6 = $e == 1.23;
1625                 $ok7 = $e == $x;
1626                 $ok8 = $e == $c;
1627             }
1628
1629             $f = "1.23";
1630             $g = 2.34;
1631             $h = 1.5;
1632             $i = 1.25;
1633             $j = "$h:$i";
1634
1635             $ok9 = $f == 1.23;
1636             $ok10 = $f == $x;
1637             $ok11 = $f == $c;
1638             $ok12 = abs(($f + $g) - 3.57) < 0.01;
1639             $ok13 = $w == 0;
1640             $ok14 = $ok15 = $ok16 = 1;  # Skip for non-utf8 locales
1641         }
1642         {
1643             no locale;
1644             $ok17 = "1.5:1.25" eq sprintf("%g:%g", $h, $i);
1645         }
1646         $ok18 = $j eq sprintf("%g:%g", $h, $i);
1647     }
1648     else {
1649         use locale ':not_characters';
1650
1651         my ($x, $y) = (1.23, 1.23);
1652         $a = "$x";
1653         printf ''; # printf used to reset locale to "C"
1654         $b = "$y";
1655         $ok1 = $a eq $b;
1656
1657         $c = "$x";
1658         my $z = sprintf ''; # sprintf used to reset locale to "C"
1659         $d = "$y";
1660         $ok2 = $c eq $d;
1661         {
1662             use warnings;
1663             my $w = 0;
1664             local $SIG{__WARN__} =
1665                 sub {
1666                     print "# @_\n";
1667                     $w++;
1668                 };
1669             $ok3 = $c == 1.23;
1670             $ok4 = $c == $x;
1671             $ok5 = $c == $d;
1672             {
1673                 no locale;
1674                 $e = "$x";
1675
1676                 $ok6 = $e == 1.23;
1677                 $ok7 = $e == $x;
1678                 $ok8 = $e == $c;
1679             }
1680
1681             $f = "1.23";
1682             $g = 2.34;
1683             $h = 1.5;
1684             $i = 1.25;
1685             $j = "$h:$i";
1686
1687             $ok9 = $f == 1.23;
1688             $ok10 = $f == $x;
1689             $ok11 = $f == $c;
1690             $ok12 = abs(($f + $g) - 3.57) < 0.01;
1691             $ok13 = $w == 0;
1692
1693             # Look for non-ASCII error messages, and verify that the first
1694             # such is in UTF-8 (the others almost certainly will be like the
1695             # first).
1696             $ok14 = 1;
1697             foreach my $err (keys %!) {
1698                 use Errno;
1699                 $! = eval "&Errno::$err";   # Convert to strerror() output
1700                 my $strerror = "$!";
1701                 if ("$strerror" =~ /\P{ASCII}/) {
1702                     $ok14 = utf8::is_utf8($strerror);
1703                     last;
1704                 }
1705             }
1706
1707             # Similarly, we verify that a non-ASCII radix is in UTF-8.  This
1708             # also catches if there is a disparity between sprintf and
1709             # stringification.
1710
1711             my $string_g = "$g";
1712             my $sprintf_g = sprintf("%g", $g);
1713
1714             $ok15 = $string_g =~ / ^ \p{ASCII}+ $ /x || utf8::is_utf8($string_g);
1715             $ok16 = $sprintf_g eq $string_g;
1716         }
1717         {
1718             no locale;
1719             $ok17 = "1.5:1.25" eq sprintf("%g:%g", $h, $i);
1720         }
1721         $ok18 = $j eq sprintf("%g:%g", $h, $i);
1722     }
1723
1724     report_result($Locale, ++$locales_test_number, $ok1);
1725     $test_names{$locales_test_number} = 'Verify that an intervening printf doesn\'t change assignment results';
1726     my $first_a_test = $locales_test_number;
1727
1728     debug "# $first_a_test..$locales_test_number: \$a = $a, \$b = $b, Locale = $Locale\n";
1729
1730     report_result($Locale, ++$locales_test_number, $ok2);
1731     $test_names{$locales_test_number} = 'Verify that an intervening sprintf doesn\'t change assignment results';
1732
1733     my $first_c_test = $locales_test_number;
1734
1735     report_result($Locale, ++$locales_test_number, $ok3);
1736     $test_names{$locales_test_number} = 'Verify that a different locale radix works when doing "==" with a constant';
1737
1738     report_result($Locale, ++$locales_test_number, $ok4);
1739     $test_names{$locales_test_number} = 'Verify that a different locale radix works when doing "==" with a scalar';
1740
1741     report_result($Locale, ++$locales_test_number, $ok5);
1742     $test_names{$locales_test_number} = 'Verify that a different locale radix works when doing "==" with a scalar and an intervening sprintf';
1743
1744     debug "# $first_c_test..$locales_test_number: \$c = $c, \$d = $d, Locale = $Locale\n";
1745
1746     report_result($Locale, ++$locales_test_number, $ok6);
1747     $test_names{$locales_test_number} = 'Verify that can assign stringified under inner no-locale block';
1748     my $first_e_test = $locales_test_number;
1749
1750     report_result($Locale, ++$locales_test_number, $ok7);
1751     $test_names{$locales_test_number} = 'Verify that "==" with a scalar still works in inner no locale';
1752
1753     report_result($Locale, ++$locales_test_number, $ok8);
1754     $test_names{$locales_test_number} = 'Verify that "==" with a scalar and an intervening sprintf still works in inner no locale';
1755
1756     debug "# $first_e_test..$locales_test_number: \$e = $e, no locale\n";
1757
1758     report_result($Locale, ++$locales_test_number, $ok9);
1759     $test_names{$locales_test_number} = 'Verify that after a no-locale block, a different locale radix still works when doing "==" with a constant';
1760     my $first_f_test = $locales_test_number;
1761
1762     report_result($Locale, ++$locales_test_number, $ok10);
1763     $test_names{$locales_test_number} = 'Verify that after a no-locale block, a different locale radix still works when doing "==" with a scalar';
1764
1765     report_result($Locale, ++$locales_test_number, $ok11);
1766     $test_names{$locales_test_number} = 'Verify that after a no-locale block, a different locale radix still works when doing "==" with a scalar and an intervening sprintf';
1767
1768     report_result($Locale, ++$locales_test_number, $ok12);
1769     $test_names{$locales_test_number} = 'Verify that after a no-locale block, a different locale radix can participate in an addition and function call as numeric';
1770
1771     report_result($Locale, ++$locales_test_number, $ok13);
1772     $test_names{$locales_test_number} = 'Verify that don\'t get warning under "==" even if radix is not a dot';
1773
1774     report_result($Locale, ++$locales_test_number, $ok14);
1775     $test_names{$locales_test_number} = 'Verify that non-ASCII UTF-8 error messages are in UTF-8';
1776
1777     report_result($Locale, ++$locales_test_number, $ok15);
1778     $test_names{$locales_test_number} = 'Verify that a number with a UTF-8 radix has a UTF-8 stringification';
1779
1780     report_result($Locale, ++$locales_test_number, $ok16);
1781     $test_names{$locales_test_number} = 'Verify that a sprintf of a number with a UTF-8 radix yields UTF-8';
1782
1783     report_result($Locale, ++$locales_test_number, $ok17);
1784     $test_names{$locales_test_number} = 'Verify that a sprintf of a number outside locale scope uses a dot radix';
1785
1786     report_result($Locale, ++$locales_test_number, $ok18);
1787     $test_names{$locales_test_number} = 'Verify that a sprintf of a number back within locale scope uses locale radix';
1788
1789     debug "# $first_f_test..$locales_test_number: \$f = $f, \$g = $g, back to locale = $Locale\n";
1790
1791     # Does taking lc separately differ from taking
1792     # the lc "in-line"?  (This was the bug 19990704.002, change #3568.)
1793     # The bug was in the caching of the 'o'-magic.
1794     if (! $is_utf8_locale) {
1795         use locale;
1796
1797         sub lcA {
1798             my $lc0 = lc $_[0];
1799             my $lc1 = lc $_[1];
1800             return $lc0 cmp $lc1;
1801         }
1802
1803         sub lcB {
1804             return lc($_[0]) cmp lc($_[1]);
1805         }
1806
1807         my $x = "ab";
1808         my $y = "aa";
1809         my $z = "AB";
1810
1811         report_result($Locale, ++$locales_test_number,
1812                     lcA($x, $y) == 1 && lcB($x, $y) == 1 ||
1813                     lcA($x, $z) == 0 && lcB($x, $z) == 0);
1814     }
1815     else {
1816         use locale ':not_characters';
1817
1818         sub lcC {
1819             my $lc0 = lc $_[0];
1820             my $lc1 = lc $_[1];
1821             return $lc0 cmp $lc1;
1822         }
1823
1824         sub lcD {
1825             return lc($_[0]) cmp lc($_[1]);
1826         }
1827
1828         my $x = "ab";
1829         my $y = "aa";
1830         my $z = "AB";
1831
1832         report_result($Locale, ++$locales_test_number,
1833                     lcC($x, $y) == 1 && lcD($x, $y) == 1 ||
1834                     lcC($x, $z) == 0 && lcD($x, $z) == 0);
1835     }
1836     $test_names{$locales_test_number} = 'Verify "lc(foo) cmp lc(bar)" is the same as using intermediaries for the cmp';
1837
1838     # Does lc of an UPPER (if different from the UPPER) match
1839     # case-insensitively the UPPER, and does the UPPER match
1840     # case-insensitively the lc of the UPPER.  And vice versa.
1841     {
1842         use locale;
1843         no utf8;
1844         my $re = qr/[\[\(\{\*\+\?\|\^\$\\]/;
1845
1846         my @f = ();
1847         ++$locales_test_number;
1848         $test_names{$locales_test_number} = 'Verify case insensitive matching works';
1849         foreach my $x (sort keys %UPPER) {
1850             if (! $is_utf8_locale) {
1851                 my $y = lc $x;
1852                 next unless uc $y eq $x;
1853                 print "# UPPER $x lc $y ",
1854                         $x =~ /$y/i ? 1 : 0, " ",
1855                         $y =~ /$x/i ? 1 : 0, "\n" if 0;
1856                 #
1857                 # If $x and $y contain regular expression characters
1858                 # AND THEY lowercase (/i) to regular expression characters,
1859                 # regcomp() will be mightily confused.  No, the \Q doesn't
1860                 # help here (maybe regex engine internal lowercasing
1861                 # is done after the \Q?)  An example of this happening is
1862                 # the bg_BG (Bulgarian) locale under EBCDIC (OS/390 USS):
1863                 # the chr(173) (the "[") is the lowercase of the chr(235).
1864                 #
1865                 # Similarly losing EBCDIC locales include cs_cz, cs_CZ,
1866                 # el_gr, el_GR, en_us.IBM-037 (!), en_US.IBM-037 (!),
1867                 # et_ee, et_EE, hr_hr, hr_HR, hu_hu, hu_HU, lt_LT,
1868                 # mk_mk, mk_MK, nl_nl.IBM-037, nl_NL.IBM-037,
1869                 # pl_pl, pl_PL, ro_ro, ro_RO, ru_ru, ru_RU,
1870                 # sk_sk, sk_SK, sl_si, sl_SI, tr_tr, tr_TR.
1871                 #
1872                 # Similar things can happen even under (bastardised)
1873                 # non-EBCDIC locales: in many European countries before the
1874                 # advent of ISO 8859-x nationally customised versions of
1875                 # ISO 646 were devised, reusing certain punctuation
1876                 # characters for modified characters needed by the
1877                 # country/language.  For example, the "|" might have
1878                 # stood for U+00F6 or LATIN SMALL LETTER O WITH DIAERESIS.
1879                 #
1880                 if ($x =~ $re || $y =~ $re) {
1881                     print "# Regex characters in '$x' or '$y', skipping test $locales_test_number for locale '$Locale'\n";
1882                     next;
1883                 }
1884                 # With utf8 both will fail since the locale concept
1885                 # of upper/lower does not work well in Unicode.
1886                 push @f, $x unless $x =~ /$y/i == $y =~ /$x/i;
1887
1888                 # fc is not a locale concept, so Perl uses lc for it.
1889                 push @f, $x unless lc $x eq fc $x;
1890             }
1891             else {
1892                 use locale ':not_characters';
1893                 my $y = lc $x;
1894                 next unless uc $y eq $x;
1895                 print "# UPPER $x lc $y ",
1896                         $x =~ /$y/i ? 1 : 0, " ",
1897                         $y =~ /$x/i ? 1 : 0, "\n" if 0;
1898
1899                 # Here, we can fully test things, unlike plain 'use locale',
1900                 # because this form does work well with Unicode
1901                 push @f, $x unless $x =~ /$y/i && $y =~ /$x/i;
1902
1903                 # The places where Unicode's lc is different from fc are
1904                 # skipped here by virtue of the 'next unless uc...' line above
1905                 push @f, $x unless lc $x eq fc $x;
1906             }
1907         }
1908
1909         foreach my $x (sort keys %lower) {
1910             if (! $is_utf8_locale) {
1911                 my $y = uc $x;
1912                 next unless lc $y eq $x;
1913                 print "# lower $x uc $y ",
1914                     $x =~ /$y/i ? 1 : 0, " ",
1915                     $y =~ /$x/i ? 1 : 0, "\n" if 0;
1916                 if ($x =~ $re || $y =~ $re) { # See above.
1917                     print "# Regex characters in '$x' or '$y', skipping test $locales_test_number for locale '$Locale'\n";
1918                     next;
1919                 }
1920                 # With utf8 both will fail since the locale concept
1921                 # of upper/lower does not work well in Unicode.
1922                 push @f, $x unless $x =~ /$y/i == $y =~ /$x/i;
1923
1924                 push @f, $x unless lc $x eq fc $x;
1925             }
1926             else {
1927                 use locale ':not_characters';
1928                 my $y = uc $x;
1929                 next unless lc $y eq $x;
1930                 print "# lower $x uc $y ",
1931                         $x =~ /$y/i ? 1 : 0, " ",
1932                         $y =~ /$x/i ? 1 : 0, "\n" if 0;
1933                 push @f, $x unless $x =~ /$y/i && $y =~ /$x/i;
1934
1935                 push @f, $x unless lc $x eq fc $x;
1936             }
1937         }
1938         report_multi_result($Locale, $locales_test_number, \@f);
1939     }
1940
1941     # [perl #109318]
1942     {
1943         my @f = ();
1944         ++$locales_test_number;
1945         $test_names{$locales_test_number} = 'Verify atof with locale radix and negative exponent';
1946
1947         my $radix = POSIX::localeconv()->{decimal_point};
1948         my @nums = (
1949              "3.14e+9",  "3${radix}14e+9",  "3.14e-9",  "3${radix}14e-9",
1950             "-3.14e+9", "-3${radix}14e+9", "-3.14e-9", "-3${radix}14e-9",
1951         );
1952
1953         if (! $is_utf8_locale) {
1954             use locale;
1955             for my $num (@nums) {
1956                 push @f, $num
1957                     unless sprintf("%g", $num) =~ /3.+14/;
1958             }
1959         }
1960         else {
1961             use locale ':not_characters';
1962             for my $num (@nums) {
1963                 push @f, $num
1964                     unless sprintf("%g", $num) =~ /3.+14/;
1965             }
1966         }
1967
1968         report_result($Locale, $locales_test_number, @f == 0);
1969         if (@f) {
1970             print "# failed $locales_test_number locale '$Locale' numbers @f\n"
1971         }
1972     }
1973 }
1974
1975 my $final_locales_test_number = $locales_test_number;
1976
1977 # Recount the errors.
1978
1979 foreach ($first_locales_test_number..$final_locales_test_number) {
1980     if (%setlocale_failed) {
1981         print "not ";
1982     }
1983     elsif ($Problem{$_} || !defined $Okay{$_} || !@{$Okay{$_}}) {
1984         if (defined $not_necessarily_a_problem_test_number
1985             && $_ == $not_necessarily_a_problem_test_number)
1986         {
1987             print "# The failure of test $not_necessarily_a_problem_test_number is not necessarily fatal.\n";
1988             print "# It usually indicates a problem in the environment,\n";
1989             print "# not in Perl itself.\n";
1990         }
1991         if ($Okay{$_} && ($_ >= $first_casing_test_number
1992                           && $_ <= $final_casing_test_number))
1993         {
1994             # Round to nearest .1%
1995             my $percent_fail = (int(.5 + (1000 * scalar(keys $Problem{$_})
1996                                           / scalar(@Locale))))
1997                                / 10;
1998             if (! $debug && $percent_fail < $acceptable_fold_failure_percentage)
1999             {
2000                 $test_names{$_} .= 'TODO';
2001                 print "# ", 100 - $percent_fail, "% of locales pass the following test, so it is likely that the failures\n";
2002                 print "# are errors in the locale definitions.  The test is marked TODO, as the\n";
2003                 print "# problem is not likely to be Perl's\n";
2004             }
2005         }
2006         print "#\n";
2007         if ($debug) {
2008             print "# The code points that had this failure are given above.  Look for lines\n";
2009             print "# that match 'failed $_'\n";
2010         }
2011         else {
2012             print "# For more details, rerun, with environment variable PERL_DEBUG_FULL_TEST=1.\n";
2013             print "# Then look at that output for lines that match 'failed $_'\n";
2014         }
2015         print "not ";
2016     }
2017     print "ok $_";
2018     if (defined $test_names{$_}) {
2019         # If TODO is in the test name, make it thus
2020         my $todo = $test_names{$_} =~ s/TODO\s*//;
2021         print " $test_names{$_}";
2022         print " # TODO" if $todo;
2023     }
2024     print "\n";
2025 }
2026
2027 $test_num = $final_locales_test_number;
2028
2029 {   # perl #115808
2030     use warnings;
2031     my $warned = 0;
2032     local $SIG{__WARN__} = sub {
2033         $warned = $_[0] =~ /uninitialized/;
2034     };
2035     my $z = "y" . setlocale(&POSIX::LC_ALL, "xyzzy");
2036     ok($warned, "variable set to setlocale(BAD LOCALE) is considered uninitialized");
2037 }
2038
2039 # Test that tainting and case changing works on utf8 strings.  These tests are
2040 # placed last to avoid disturbing the hard-coded test numbers that existed at
2041 # the time these were added above this in this file.
2042 # This also tests that locale overrides unicode_strings in the same scope for
2043 # non-utf8 strings.
2044 setlocale(&POSIX::LC_ALL, "C");
2045 {
2046     use locale;
2047     use feature 'unicode_strings';
2048
2049     foreach my $function ("uc", "ucfirst", "lc", "lcfirst", "fc") {
2050         my @list;   # List of code points to test for $function
2051
2052         # Used to calculate the changed case for ASCII characters by using the
2053         # ord, instead of using one of the functions under test.
2054         my $ascii_case_change_delta;
2055         my $above_latin1_case_change_delta; # Same for the specific ords > 255
2056                                             # that we use
2057
2058         # We test an ASCII character, which should change case and be tainted;
2059         # a Latin1 character, which shouldn't change case under this C locale,
2060         #   and is tainted.
2061         # an above-Latin1 character that when the case is changed would cross
2062         #   the 255/256 boundary, so doesn't change case and isn't tainted
2063         # (the \x{149} is one of these, but changes into 2 characters, the
2064         #   first one of which doesn't cross the boundary.
2065         # the final one in each list is an above-Latin1 character whose case
2066         #   does change, and shouldn't be tainted.  The code below uses its
2067         #   position in its list as a marker to indicate that it, unlike the
2068         #   other code points above ASCII, has a successful case change
2069         if ($function =~ /^u/) {
2070             @list = ("", "a", "\xe0", "\xff", "\x{fb00}", "\x{149}", "\x{101}");
2071             $ascii_case_change_delta = -32;
2072             $above_latin1_case_change_delta = -1;
2073         }
2074         else {
2075             @list = ("", "A", "\xC0", "\x{17F}", "\x{100}");
2076             $ascii_case_change_delta = +32;
2077             $above_latin1_case_change_delta = +1;
2078         }
2079         foreach my $is_utf8_locale (0 .. 1) {
2080             foreach my $j (0 .. $#list) {
2081                 my $char = $list[$j];
2082
2083                 for my $encoded_in_utf8 (0 .. 1) {
2084                     my $should_be;
2085                     my $changed;
2086                     if (! $is_utf8_locale) {
2087                         $should_be = ($j == $#list)
2088                             ? chr(ord($char) + $above_latin1_case_change_delta)
2089                             : (length $char == 0 || ord($char) > 127)
2090                             ? $char
2091                             : chr(ord($char) + $ascii_case_change_delta);
2092
2093                         # This monstrosity is in order to avoid using an eval,
2094                         # which might perturb the results
2095                         $changed = ($function eq "uc")
2096                                     ? uc($char)
2097                                     : ($function eq "ucfirst")
2098                                       ? ucfirst($char)
2099                                       : ($function eq "lc")
2100                                         ? lc($char)
2101                                         : ($function eq "lcfirst")
2102                                           ? lcfirst($char)
2103                                           : ($function eq "fc")
2104                                             ? fc($char)
2105                                             : die("Unexpected function \"$function\"");
2106                     }
2107                     else {
2108                         {
2109                             no locale;
2110
2111                             # For utf8-locales the case changing functions
2112                             # should work just like they do outside of locale.
2113                             # Can use eval here because not testing it when
2114                             # not in locale.
2115                             $should_be = eval "$function('$char')";
2116                             die "Unexpected eval error $@ from 'eval \"$function('$char')\"'" if  $@;
2117
2118                         }
2119                         use locale ':not_characters';
2120                         $changed = ($function eq "uc")
2121                                     ? uc($char)
2122                                     : ($function eq "ucfirst")
2123                                       ? ucfirst($char)
2124                                       : ($function eq "lc")
2125                                         ? lc($char)
2126                                         : ($function eq "lcfirst")
2127                                           ? lcfirst($char)
2128                                           : ($function eq "fc")
2129                                             ? fc($char)
2130                                             : die("Unexpected function \"$function\"");
2131                     }
2132                     ok($changed eq $should_be,
2133                         "$function(\"$char\") in C locale "
2134                         . (($is_utf8_locale)
2135                             ? "(use locale ':not_characters'"
2136                             : "(use locale")
2137                         . (($encoded_in_utf8)
2138                             ? "; encoded in utf8)"
2139                             : "; not encoded in utf8)")
2140                         . " should be \"$should_be\", got \"$changed\"");
2141
2142                     # Tainting shouldn't happen for utf8 locales, empty
2143                     # strings, or those characters above 255.
2144                     (! $is_utf8_locale && length($char) > 0 && ord($char) < 256)
2145                     ? check_taint($changed)
2146                     : check_taint_not($changed);
2147
2148                     # Use UTF-8 next time through the loop
2149                     utf8::upgrade($char);
2150                 }
2151             }
2152         }
2153     }
2154 }
2155
2156 # Give final advice.
2157
2158 my $didwarn = 0;
2159
2160 foreach ($first_locales_test_number..$final_locales_test_number) {
2161     if ($Problem{$_}) {
2162         my @f = sort keys %{ $Problem{$_} };
2163         my $f = join(" ", @f);
2164         $f =~ s/(.{50,60}) /$1\n#\t/g;
2165         print
2166             "#\n",
2167             "# The locale ", (@f == 1 ? "definition" : "definitions"), "\n#\n",
2168             "#\t", $f, "\n#\n",
2169             "# on your system may have errors because the locale test $_\n",
2170             "# \"$test_names{$_}\"\n",
2171             "# failed in ", (@f == 1 ? "that locale" : "those locales"),
2172             ".\n";
2173         print <<EOW;
2174 #
2175 # If your users are not using these locales you are safe for the moment,
2176 # but please report this failure first to perlbug\@perl.com using the
2177 # perlbug script (as described in the INSTALL file) so that the exact
2178 # details of the failures can be sorted out first and then your operating
2179 # system supplier can be alerted about these anomalies.
2180 #
2181 EOW
2182         $didwarn = 1;
2183     }
2184 }
2185
2186 # Tell which locales were okay and which were not.
2187
2188 if ($didwarn) {
2189     my (@s, @F);
2190
2191     foreach my $l (@Locale) {
2192         my $p = 0;
2193         if ($setlocale_failed{$l}) {
2194             $p++;
2195         }
2196         else {
2197             foreach my $t
2198                         ($first_locales_test_number..$final_locales_test_number)
2199             {
2200                 $p++ if $Problem{$t}{$l};
2201             }
2202         }
2203         push @s, $l if $p == 0;
2204         push @F, $l unless $p == 0;
2205     }
2206
2207     if (@s) {
2208         my $s = join(" ", @s);
2209         $s =~ s/(.{50,60}) /$1\n#\t/g;
2210
2211         warn
2212             "# The following locales\n#\n",
2213             "#\t", $s, "\n#\n",
2214             "# tested okay.\n#\n",
2215     } else {
2216         warn "# None of your locales were fully okay.\n";
2217     }
2218
2219     if (@F) {
2220         my $F = join(" ", @F);
2221         $F =~ s/(.{50,60}) /$1\n#\t/g;
2222
2223         warn
2224           "# The following locales\n#\n",
2225           "#\t", $F, "\n#\n",
2226           "# had problems.\n#\n",
2227           "# For more details, rerun, with environment variable PERL_DEBUG_FULL_TEST=1.\n";
2228     } else {
2229         warn "# None of your locales were broken.\n";
2230     }
2231 }
2232
2233 print "1..$test_num\n";
2234
2235 # eof