15759b3d2Safresh1#!perl 25759b3d2Safresh1# 35759b3d2Safresh1# This auxiliary script makes five header files 45759b3d2Safresh1# used for building XSUB of Unicode::Normalize. 55759b3d2Safresh1# 65759b3d2Safresh1# Usage: 75759b3d2Safresh1# <do 'mkheader'> in perl, or <perl mkheader> in command line 85759b3d2Safresh1# 95759b3d2Safresh1# Input files: 105759b3d2Safresh1# unicore/CombiningClass.pl (or unicode/CombiningClass.pl) 115759b3d2Safresh1# unicore/Decomposition.pl (or unicode/Decomposition.pl) 125759b3d2Safresh1# 135759b3d2Safresh1# Output files: 145759b3d2Safresh1# unfcan.h 155759b3d2Safresh1# unfcpt.h 165759b3d2Safresh1# unfcmb.h 175759b3d2Safresh1# unfcmp.h 185759b3d2Safresh1# unfexc.h 195759b3d2Safresh1# 205759b3d2Safresh1use 5.006; 215759b3d2Safresh1use strict; 225759b3d2Safresh1use warnings; 235759b3d2Safresh1use Carp; 245759b3d2Safresh1use File::Spec; 255759b3d2Safresh1use SelectSaver; 265759b3d2Safresh1 275759b3d2Safresh1our $PACKAGE = 'Unicode::Normalize, mkheader'; 285759b3d2Safresh1 295759b3d2Safresh1our $prefix = "UNF_"; 305759b3d2Safresh1our $structname = "${prefix}complist"; 315759b3d2Safresh1 325759b3d2Safresh1# Starting in v5.20, the tables in lib/unicore are built using the platform's 33*256a93a4Safresh1# native character set for code points 0-255. But in v5.35, pack U stopped 34*256a93a4Safresh1# trying to compensate 35*256a93a4Safresh1*pack_U = ($] ge 5.020 && $] lt 5.035) 36*256a93a4Safresh1 ? sub { return pack('U*', map { utf8::unicode_to_native($_) } @_); } 375759b3d2Safresh1 : sub { return pack('U*', @_); }; 385759b3d2Safresh1 395759b3d2Safresh1# %Canon and %Compat will be ($codepoint => $hexstring) after _U_stringify() 405759b3d2Safresh1our %Comp1st; # $codepoint => $listname : may be composed with a next char. 415759b3d2Safresh1our %CompList; # $listname,$2nd => $codepoint : composite 425759b3d2Safresh1 435759b3d2Safresh1##### The below part is common to mkheader and PP ##### 445759b3d2Safresh1 455759b3d2Safresh1our %Combin; # $codepoint => $number : combination class 465759b3d2Safresh1our %Canon; # $codepoint => \@codepoints : canonical decomp. 475759b3d2Safresh1our %Compat; # $codepoint => \@codepoints : compat. decomp. 485759b3d2Safresh1our %Compos; # $1st,$2nd => $codepoint : composite 495759b3d2Safresh1our %Exclus; # $codepoint => 1 : composition exclusions 505759b3d2Safresh1our %Single; # $codepoint => 1 : singletons 515759b3d2Safresh1our %NonStD; # $codepoint => 1 : non-starter decompositions 525759b3d2Safresh1our %Comp2nd; # $codepoint => 1 : may be composed with a prev char. 535759b3d2Safresh1 545759b3d2Safresh1# from core Unicode database 555759b3d2Safresh1our $Combin = do "unicore/CombiningClass.pl" 565759b3d2Safresh1 || do "unicode/CombiningClass.pl" 575759b3d2Safresh1 || croak "$PACKAGE: CombiningClass.pl not found"; 585759b3d2Safresh1our $Decomp = do "unicore/Decomposition.pl" 595759b3d2Safresh1 || do "unicode/Decomposition.pl" 605759b3d2Safresh1 || croak "$PACKAGE: Decomposition.pl not found"; 615759b3d2Safresh1 625759b3d2Safresh1# CompositionExclusions.txt since Unicode 3.2.0. If this ever changes, it 635759b3d2Safresh1# would be better to get the values from Unicode::UCD rather than hard-code 645759b3d2Safresh1# them here, as that will protect from having to make fixes for future 655759b3d2Safresh1# changes. 665759b3d2Safresh1our @CompEx = qw( 675759b3d2Safresh1 0958 0959 095A 095B 095C 095D 095E 095F 09DC 09DD 09DF 0A33 0A36 685759b3d2Safresh1 0A59 0A5A 0A5B 0A5E 0B5C 0B5D 0F43 0F4D 0F52 0F57 0F5C 0F69 0F76 695759b3d2Safresh1 0F78 0F93 0F9D 0FA2 0FA7 0FAC 0FB9 FB1D FB1F FB2A FB2B FB2C FB2D 705759b3d2Safresh1 FB2E FB2F FB30 FB31 FB32 FB33 FB34 FB35 FB36 FB38 FB39 FB3A FB3B 715759b3d2Safresh1 FB3C FB3E FB40 FB41 FB43 FB44 FB46 FB47 FB48 FB49 FB4A FB4B FB4C 725759b3d2Safresh1 FB4D FB4E 2ADC 1D15E 1D15F 1D160 1D161 1D162 1D163 1D164 1D1BB 735759b3d2Safresh1 1D1BC 1D1BD 1D1BE 1D1BF 1D1C0 745759b3d2Safresh1); 755759b3d2Safresh1 765759b3d2Safresh1# definition of Hangul constants 775759b3d2Safresh1use constant SBase => 0xAC00; 785759b3d2Safresh1use constant SFinal => 0xD7A3; # SBase -1 + SCount 795759b3d2Safresh1use constant SCount => 11172; # LCount * NCount 805759b3d2Safresh1use constant NCount => 588; # VCount * TCount 815759b3d2Safresh1use constant LBase => 0x1100; 825759b3d2Safresh1use constant LFinal => 0x1112; 835759b3d2Safresh1use constant LCount => 19; 845759b3d2Safresh1use constant VBase => 0x1161; 855759b3d2Safresh1use constant VFinal => 0x1175; 865759b3d2Safresh1use constant VCount => 21; 875759b3d2Safresh1use constant TBase => 0x11A7; 885759b3d2Safresh1use constant TFinal => 0x11C2; 895759b3d2Safresh1use constant TCount => 28; 905759b3d2Safresh1 915759b3d2Safresh1sub decomposeHangul { 925759b3d2Safresh1 my $sindex = $_[0] - SBase; 935759b3d2Safresh1 my $lindex = int( $sindex / NCount); 945759b3d2Safresh1 my $vindex = int(($sindex % NCount) / TCount); 955759b3d2Safresh1 my $tindex = $sindex % TCount; 965759b3d2Safresh1 my @ret = ( 975759b3d2Safresh1 LBase + $lindex, 985759b3d2Safresh1 VBase + $vindex, 995759b3d2Safresh1 $tindex ? (TBase + $tindex) : (), 1005759b3d2Safresh1 ); 1015759b3d2Safresh1 return wantarray ? @ret : pack_U(@ret); 1025759b3d2Safresh1} 1035759b3d2Safresh1 1045759b3d2Safresh1########## getting full decomposition ########## 1055759b3d2Safresh1 1065759b3d2Safresh1## converts string "hhhh hhhh hhhh" to a numeric list 1075759b3d2Safresh1## (hex digits separated by spaces) 1085759b3d2Safresh1sub _getHexArray { map hex, $_[0] =~ /\G *([0-9A-Fa-f]+)/g } 1095759b3d2Safresh1 1105759b3d2Safresh1while ($Combin =~ /(.+)/g) { 1115759b3d2Safresh1 my @tab = split /\t/, $1; 1125759b3d2Safresh1 my $ini = hex $tab[0]; 1135759b3d2Safresh1 if ($tab[1] eq '') { 1145759b3d2Safresh1 $Combin{$ini} = $tab[2]; 1155759b3d2Safresh1 } else { 1165759b3d2Safresh1 $Combin{$_} = $tab[2] foreach $ini .. hex($tab[1]); 1175759b3d2Safresh1 } 1185759b3d2Safresh1} 1195759b3d2Safresh1 1205759b3d2Safresh1while ($Decomp =~ /(.+)/g) { 1215759b3d2Safresh1 my @tab = split /\t/, $1; 1225759b3d2Safresh1 my $compat = $tab[2] =~ s/<[^>]+>//; 1235759b3d2Safresh1 my $dec = [ _getHexArray($tab[2]) ]; # decomposition 1245759b3d2Safresh1 my $ini = hex($tab[0]); # initial decomposable character 1255759b3d2Safresh1 my $end = $tab[1] eq '' ? $ini : hex($tab[1]); 1265759b3d2Safresh1 # ($ini .. $end) is the range of decomposable characters. 1275759b3d2Safresh1 1285759b3d2Safresh1 foreach my $u ($ini .. $end) { 1295759b3d2Safresh1 $Compat{$u} = $dec; 1305759b3d2Safresh1 $Canon{$u} = $dec if ! $compat; 1315759b3d2Safresh1 } 1325759b3d2Safresh1} 1335759b3d2Safresh1 1345759b3d2Safresh1for my $s (@CompEx) { 1355759b3d2Safresh1 my $u = hex $s; 1365759b3d2Safresh1 next if !$Canon{$u}; # not assigned 1375759b3d2Safresh1 next if $u == 0xFB1D && !$Canon{0x1D15E}; # 3.0.1 before Corrigendum #2 1385759b3d2Safresh1 $Exclus{$u} = 1; 1395759b3d2Safresh1} 1405759b3d2Safresh1 1415759b3d2Safresh1foreach my $u (keys %Canon) { 1425759b3d2Safresh1 my $dec = $Canon{$u}; 1435759b3d2Safresh1 1445759b3d2Safresh1 if (@$dec == 2) { 1455759b3d2Safresh1 if ($Combin{ $dec->[0] }) { 1465759b3d2Safresh1 $NonStD{$u} = 1; 1475759b3d2Safresh1 } else { 1485759b3d2Safresh1 $Compos{ $dec->[0] }{ $dec->[1] } = $u; 1495759b3d2Safresh1 $Comp2nd{ $dec->[1] } = 1 if ! $Exclus{$u}; 1505759b3d2Safresh1 } 1515759b3d2Safresh1 } elsif (@$dec == 1) { 1525759b3d2Safresh1 $Single{$u} = 1; 1535759b3d2Safresh1 } else { 1545759b3d2Safresh1 my $h = sprintf '%04X', $u; 1555759b3d2Safresh1 croak("Weird Canonical Decomposition of U+$h"); 1565759b3d2Safresh1 } 1575759b3d2Safresh1} 1585759b3d2Safresh1 1595759b3d2Safresh1# modern HANGUL JUNGSEONG and HANGUL JONGSEONG jamo 1605759b3d2Safresh1foreach my $j (0x1161..0x1175, 0x11A8..0x11C2) { 1615759b3d2Safresh1 $Comp2nd{$j} = 1; 1625759b3d2Safresh1} 1635759b3d2Safresh1 1645759b3d2Safresh1sub getCanonList { 1655759b3d2Safresh1 my @src = @_; 1665759b3d2Safresh1 my @dec = map { 1675759b3d2Safresh1 (SBase <= $_ && $_ <= SFinal) ? decomposeHangul($_) 1685759b3d2Safresh1 : $Canon{$_} ? @{ $Canon{$_} } : $_ 1695759b3d2Safresh1 } @src; 1705759b3d2Safresh1 return join(" ",@src) eq join(" ",@dec) ? @dec : getCanonList(@dec); 1715759b3d2Safresh1 # condition @src == @dec is not ok. 1725759b3d2Safresh1} 1735759b3d2Safresh1 1745759b3d2Safresh1sub getCompatList { 1755759b3d2Safresh1 my @src = @_; 1765759b3d2Safresh1 my @dec = map { 1775759b3d2Safresh1 (SBase <= $_ && $_ <= SFinal) ? decomposeHangul($_) 1785759b3d2Safresh1 : $Compat{$_} ? @{ $Compat{$_} } : $_ 1795759b3d2Safresh1 } @src; 1805759b3d2Safresh1 return join(" ",@src) eq join(" ",@dec) ? @dec : getCompatList(@dec); 1815759b3d2Safresh1 # condition @src == @dec is not ok. 1825759b3d2Safresh1} 1835759b3d2Safresh1 1845759b3d2Safresh1# exhaustive decomposition 1855759b3d2Safresh1foreach my $key (keys %Canon) { 1865759b3d2Safresh1 $Canon{$key} = [ getCanonList($key) ]; 1875759b3d2Safresh1} 1885759b3d2Safresh1 1895759b3d2Safresh1# exhaustive decomposition 1905759b3d2Safresh1foreach my $key (keys %Compat) { 1915759b3d2Safresh1 $Compat{$key} = [ getCompatList($key) ]; 1925759b3d2Safresh1} 1935759b3d2Safresh1 1945759b3d2Safresh1##### The above part is common to mkheader and PP ##### 1955759b3d2Safresh1 1965759b3d2Safresh1foreach my $comp1st (keys %Compos) { 1975759b3d2Safresh1 my $listname = sprintf("${structname}_%06x", $comp1st); 1985759b3d2Safresh1 # %04x is bad since it'd place _3046 after _1d157. 1995759b3d2Safresh1 $Comp1st{$comp1st} = $listname; 2005759b3d2Safresh1 my $rh1st = $Compos{$comp1st}; 2015759b3d2Safresh1 2025759b3d2Safresh1 foreach my $comp2nd (keys %$rh1st) { 2035759b3d2Safresh1 my $uc = $rh1st->{$comp2nd}; 2045759b3d2Safresh1 $CompList{$listname}{$comp2nd} = $uc; 2055759b3d2Safresh1 } 2065759b3d2Safresh1} 2075759b3d2Safresh1 2085759b3d2Safresh1sub split_into_char { 2095759b3d2Safresh1 use bytes; 2105759b3d2Safresh1 my $uni = shift; 2115759b3d2Safresh1 my $len = length($uni); 2125759b3d2Safresh1 my @ary; 2135759b3d2Safresh1 for(my $i = 0; $i < $len; ++$i) { 2145759b3d2Safresh1 push @ary, ord(substr($uni,$i,1)); 2155759b3d2Safresh1 } 2165759b3d2Safresh1 return @ary; 2175759b3d2Safresh1} 2185759b3d2Safresh1 2195759b3d2Safresh1sub _U_stringify { 2205759b3d2Safresh1 sprintf '"%s"', join '', 2215759b3d2Safresh1 map sprintf("\\x%02x", $_), split_into_char(pack_U(@_)); 2225759b3d2Safresh1} 2235759b3d2Safresh1 2245759b3d2Safresh1foreach my $hash (\%Canon, \%Compat) { 2255759b3d2Safresh1 foreach my $key (keys %$hash) { 2265759b3d2Safresh1 $hash->{$key} = _U_stringify( @{ $hash->{$key} } ); 2275759b3d2Safresh1 } 2285759b3d2Safresh1} 2295759b3d2Safresh1 2305759b3d2Safresh1########## writing header files ########## 2315759b3d2Safresh1 2325759b3d2Safresh1my @boolfunc = ( 2335759b3d2Safresh1 { 2345759b3d2Safresh1 name => "Exclusion", 2355759b3d2Safresh1 type => "bool", 2365759b3d2Safresh1 hash => \%Exclus, 2375759b3d2Safresh1 }, 2385759b3d2Safresh1 { 2395759b3d2Safresh1 name => "Singleton", 2405759b3d2Safresh1 type => "bool", 2415759b3d2Safresh1 hash => \%Single, 2425759b3d2Safresh1 }, 2435759b3d2Safresh1 { 2445759b3d2Safresh1 name => "NonStDecomp", 2455759b3d2Safresh1 type => "bool", 2465759b3d2Safresh1 hash => \%NonStD, 2475759b3d2Safresh1 }, 2485759b3d2Safresh1 { 2495759b3d2Safresh1 name => "Comp2nd", 2505759b3d2Safresh1 type => "bool", 2515759b3d2Safresh1 hash => \%Comp2nd, 2525759b3d2Safresh1 }, 2535759b3d2Safresh1); 2545759b3d2Safresh1 2555759b3d2Safresh1my $orig_fh = SelectSaver->new; 2565759b3d2Safresh1{ 2575759b3d2Safresh1 2585759b3d2Safresh1my $file = "unfexc.h"; 2595759b3d2Safresh1open FH, ">$file" or croak "$PACKAGE: $file can't be made"; 2605759b3d2Safresh1binmode FH; select FH; 2615759b3d2Safresh1 2625759b3d2Safresh1 print << 'EOF'; 2635759b3d2Safresh1/* 2645759b3d2Safresh1 * This file is auto-generated by mkheader. 2655759b3d2Safresh1 * Any changes here will be lost! 2665759b3d2Safresh1 */ 2675759b3d2Safresh1EOF 2685759b3d2Safresh1 2695759b3d2Safresh1foreach my $tbl (@boolfunc) { 2705759b3d2Safresh1 my @temp = sort {$a <=> $b} keys %{$tbl->{hash}}; 2715759b3d2Safresh1 my $type = $tbl->{type}; 2725759b3d2Safresh1 my $name = $tbl->{name}; 2735759b3d2Safresh1 print "$type is$name (UV uv)\n{\nreturn\n\t"; 2745759b3d2Safresh1 2755759b3d2Safresh1 while (@temp) { 2765759b3d2Safresh1 my $cur = shift @temp; 2775759b3d2Safresh1 if (@temp && $cur + 1 == $temp[0]) { 2785759b3d2Safresh1 print "($cur <= uv && uv <= "; 2795759b3d2Safresh1 while (@temp && $cur + 1 == $temp[0]) { 2805759b3d2Safresh1 $cur = shift @temp; 2815759b3d2Safresh1 } 2825759b3d2Safresh1 print "$cur)"; 2835759b3d2Safresh1 print "\n\t|| " if @temp; 2845759b3d2Safresh1 } else { 2855759b3d2Safresh1 print "uv == $cur"; 2865759b3d2Safresh1 print "\n\t|| " if @temp; 2875759b3d2Safresh1 } 2885759b3d2Safresh1 } 2895759b3d2Safresh1 print "\n\t? TRUE : FALSE;\n}\n\n"; 2905759b3d2Safresh1} 2915759b3d2Safresh1 2925759b3d2Safresh1close FH; 2935759b3d2Safresh1 2945759b3d2Safresh1#################################### 2955759b3d2Safresh1 2965759b3d2Safresh1my $compinit = 2975759b3d2Safresh1 "typedef struct { UV nextchar; UV composite; } $structname;\n\n"; 2985759b3d2Safresh1 2995759b3d2Safresh1foreach my $i (sort keys %CompList) { 3005759b3d2Safresh1 $compinit .= "$structname $i [] = {\n"; 3015759b3d2Safresh1 $compinit .= join ",\n", 3025759b3d2Safresh1 map sprintf("\t{ %d, %d }", $_, $CompList{$i}{$_}), 3035759b3d2Safresh1 sort {$a <=> $b } keys %{ $CompList{$i} }; 3045759b3d2Safresh1 $compinit .= ",\n{0,0}\n};\n\n"; # with sentinel 3055759b3d2Safresh1} 3065759b3d2Safresh1 3075759b3d2Safresh1my @tripletable = ( 3085759b3d2Safresh1 { 3095759b3d2Safresh1 file => "unfcmb", 3105759b3d2Safresh1 name => "combin", 3115759b3d2Safresh1 type => "STDCHAR", 3125759b3d2Safresh1 hash => \%Combin, 3135759b3d2Safresh1 null => 0, 3145759b3d2Safresh1 }, 3155759b3d2Safresh1 { 3165759b3d2Safresh1 file => "unfcan", 3175759b3d2Safresh1 name => "canon", 3185759b3d2Safresh1 type => "char*", 3195759b3d2Safresh1 hash => \%Canon, 3205759b3d2Safresh1 null => "NULL", 3215759b3d2Safresh1 }, 3225759b3d2Safresh1 { 3235759b3d2Safresh1 file => "unfcpt", 3245759b3d2Safresh1 name => "compat", 3255759b3d2Safresh1 type => "char*", 3265759b3d2Safresh1 hash => \%Compat, 3275759b3d2Safresh1 null => "NULL", 3285759b3d2Safresh1 }, 3295759b3d2Safresh1 { 3305759b3d2Safresh1 file => "unfcmp", 3315759b3d2Safresh1 name => "compos", 3325759b3d2Safresh1 type => "$structname *", 3335759b3d2Safresh1 hash => \%Comp1st, 3345759b3d2Safresh1 null => "NULL", 3355759b3d2Safresh1 init => $compinit, 3365759b3d2Safresh1 }, 3375759b3d2Safresh1); 3385759b3d2Safresh1 3395759b3d2Safresh1foreach my $tbl (@tripletable) { 3405759b3d2Safresh1 my $file = "$tbl->{file}.h"; 3415759b3d2Safresh1 my $head = "${prefix}$tbl->{name}"; 3425759b3d2Safresh1 my $type = $tbl->{type}; 3435759b3d2Safresh1 my $hash = $tbl->{hash}; 3445759b3d2Safresh1 my $null = $tbl->{null}; 3455759b3d2Safresh1 my $init = $tbl->{init}; 3465759b3d2Safresh1 3475759b3d2Safresh1 open FH, ">$file" or croak "$PACKAGE: $file can't be made"; 3485759b3d2Safresh1 binmode FH; select FH; 3495759b3d2Safresh1 my %val; 3505759b3d2Safresh1 3515759b3d2Safresh1 print FH << 'EOF'; 3525759b3d2Safresh1/* 3535759b3d2Safresh1 * This file is auto-generated by mkheader. 3545759b3d2Safresh1 * Any changes here will be lost! 3555759b3d2Safresh1 */ 3565759b3d2Safresh1EOF 3575759b3d2Safresh1 3585759b3d2Safresh1 print $init if defined $init; 3595759b3d2Safresh1 3605759b3d2Safresh1 foreach my $uv (keys %$hash) { 3615759b3d2Safresh1 croak sprintf("a Unicode code point 0x%04X over 0x10FFFF.", $uv) 3625759b3d2Safresh1 unless $uv <= 0x10FFFF; 3635759b3d2Safresh1 my @c = unpack 'CCCC', pack 'N', $uv; 3645759b3d2Safresh1 $val{ $c[1] }{ $c[2] }{ $c[3] } = $hash->{$uv}; 3655759b3d2Safresh1 } 3665759b3d2Safresh1 3675759b3d2Safresh1 foreach my $p (sort { $a <=> $b } keys %val) { 3685759b3d2Safresh1 next if ! $val{ $p }; 3695759b3d2Safresh1 for (my $r = 0; $r < 256; $r++) { 3705759b3d2Safresh1 next if ! $val{ $p }{ $r }; 3715759b3d2Safresh1 printf "static $type ${head}_%02x_%02x [256] = {\n", $p, $r; 3725759b3d2Safresh1 for (my $c = 0; $c < 256; $c++) { 3735759b3d2Safresh1 print "\t", defined $val{$p}{$r}{$c} 3745759b3d2Safresh1 ? "($type)".$val{$p}{$r}{$c} 3755759b3d2Safresh1 : $null; 3765759b3d2Safresh1 print ',' if $c != 255; 3775759b3d2Safresh1 print "\n" if $c % 8 == 7; 3785759b3d2Safresh1 } 3795759b3d2Safresh1 print "};\n\n"; 3805759b3d2Safresh1 } 3815759b3d2Safresh1 } 3825759b3d2Safresh1 foreach my $p (sort { $a <=> $b } keys %val) { 3835759b3d2Safresh1 next if ! $val{ $p }; 3845759b3d2Safresh1 printf "static $type* ${head}_%02x [256] = {\n", $p; 3855759b3d2Safresh1 for (my $r = 0; $r < 256; $r++) { 3865759b3d2Safresh1 print $val{ $p }{ $r } 3875759b3d2Safresh1 ? sprintf("${head}_%02x_%02x", $p, $r) 3885759b3d2Safresh1 : "NULL"; 3895759b3d2Safresh1 print ',' if $r != 255; 3905759b3d2Safresh1 print "\n" if $val{ $p }{ $r } || ($r+1) % 8 == 0; 3915759b3d2Safresh1 } 3925759b3d2Safresh1 print "};\n\n"; 3935759b3d2Safresh1 } 3945759b3d2Safresh1 print "static $type** $head [] = {\n"; 3955759b3d2Safresh1 for (my $p = 0; $p <= 0x10; $p++) { 3965759b3d2Safresh1 print $val{ $p } ? sprintf("${head}_%02x", $p) : "NULL"; 3975759b3d2Safresh1 print ',' if $p != 0x10; 3985759b3d2Safresh1 print "\n"; 3995759b3d2Safresh1 } 4005759b3d2Safresh1 print "};\n\n"; 4015759b3d2Safresh1 close FH; 4025759b3d2Safresh1} 4035759b3d2Safresh1 4045759b3d2Safresh1} # End of block for SelectSaver 4055759b3d2Safresh1 4065759b3d2Safresh11; 4075759b3d2Safresh1__END__ 408