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