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