Advertisement
Guest User

Perl Script to Convert IP2Location LITE to GeoIP

a guest
Mar 10th, 2014
506
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Perl 19.41 KB | None | 0 0
  1. #!/usr/bin/perl
  2. ###################################################################################
  3. #                                                                                
  4. # Description : Converts IP2Location LITE DB1 into MaxMind GeoLite2 Country data or
  5. #               Converts IP2Location LITE DB11 into MaxMind GeoLite2 City data
  6. #                                                                                
  7. # Usages      : perl convert.pl IP2LOCATION-LITE-DB1.CSV
  8. #               perl convert.pl IP2LOCATION-LITE-DB11.CSV
  9. #                                                                                
  10. # Note        : Continent, geonames id, confidence, accuracy radius, metro code,
  11. #               time zone, registered country, and represented country fields are
  12. #               not supported. Only English names are supported at present.
  13. #
  14. # Author      : Антон Владимиревич <anton.vladimir@europe.com>
  15. #
  16. # Disclaimer  : IP2Location and Maxmind are trademark of respective owners.
  17. #               The IP2Location and GeoLite2 data structure is based on the
  18. #               public specification published by the respective owners.
  19. #
  20. ###################################################################################
  21. use strict;
  22. use Socket;
  23.  
  24. if (($#ARGV + 1) != 1) {
  25.     print "Usage: perl convert.pl <IP2Location LITE DB1 or DB11 CSV file>\n";
  26.     exit();
  27. }
  28.  
  29. my $filename = $ARGV[0];
  30. my $filename2 = $filename . ".MMDB";
  31.  
  32. my %sortbylength;
  33. my %btree;
  34. my @data;
  35. my @cidrdata;
  36. my $csvdata = "";
  37. my @csvdataarray;
  38. my %tokens = ("country" => 0, "iso_code" => 0, "names" => 0, "en" => 0, "-" => 0);
  39. my %tokens2 = ("city" => 0, "location" => 0, "postal" => 0, "latitude" => 0, "longitude" => 0, "code" => 0, "subdivisions" => 0);
  40.  
  41. my %countryoffset;
  42. my %cityoffset;
  43. my %countries;
  44. my %cities;
  45. my %latlongs;
  46. my $filetype = "";
  47. my $datastartmarker = &print_byte(0) x 16;
  48. my $datastartmarkerlength = length($datastartmarker);
  49.  
  50. {
  51.     open IN, "<$filename" or die;
  52.     local $/ = undef;
  53.     $csvdata = <IN>;
  54.     close IN;
  55. }
  56.  
  57. @csvdataarray = split(/[\r\n]+/, $csvdata);
  58. $csvdata = "";
  59.  
  60. foreach my $csvline (@csvdataarray) {
  61.     my $therest = '';
  62.     my @array = &splitcsv($csvline);
  63.     if ($filetype eq '') {
  64.         if (scalar(@array) == 10)   {
  65.             $filetype = "city";
  66.         }
  67.         else {
  68.             $filetype = "country";
  69.         }
  70.     }
  71.     if ($filetype eq "city") {
  72.         pop(@array);
  73.         $tokens{$array[2]} = 0;
  74.         $tokens{$array[3]} = 0;
  75.         $tokens{$array[4]} = 0;
  76.         $tokens{$array[5]} = 0;
  77.         $latlongs{$array[6]} = 0;
  78.         $latlongs{$array[7]} = 0;
  79.         $tokens{$array[8]} = 0;
  80.        
  81.         $cities{$array[2] . "|" . $array[3] . "|" . $array[4] . "|" . $array[5] . "|" . $array[6] . "|" . $array[7] . "|" . $array[8]} = 0;
  82.         $therest = $array[2] . "|" . $array[4] . "|" . $array[5] . "|" . $array[6] . "|" . $array[7] . "|" . $array[8];
  83.     }   elsif ($filetype eq "country") {
  84.         $countries{$array[2]} = $array[3];
  85.         $therest = $array[2];
  86.     }
  87.     my $fromip = &no2ip($array[0]);
  88.     my $toip = &no2ip($array[1]);
  89.     my @ar = &range2cidr("$fromip-$toip");
  90.     foreach my $a (sort ipnumber_sort @ar) {
  91.         push (@cidrdata, '"' . $a . '",' . $therest);
  92.     }
  93. }
  94.  
  95. undef(@csvdataarray);
  96.  
  97. sub range2cidr {
  98.     my @r = @_;
  99.     my $i;
  100.     my @c;
  101.  
  102.     for ($i=0; $i <= $#r; $i++) {
  103.         $r[$i] =~ s/\s//g;
  104.         if ($r[$i] =~ /\//) {
  105.             push @c, $r[$i];
  106.             next;
  107.         }
  108.        
  109.         $r[$i]="$r[$i]-$r[$i]" unless $r[$i] =~ /(.*)-(.*)/;
  110.         $r[$i] =~ /(.*)-(.*)/;
  111.        
  112.         my ($a,$b)=($1,$2);
  113.         my @a=split(/\.+/, $a);
  114.         my @b=split(/\.+/, $b);
  115.        
  116.         return unless $#a == $#b;
  117.         my @cc=_range2cidr(\@a, \@b);
  118.        
  119.         while ($#cc >= 0)   {
  120.             $a=shift @cc;
  121.             $b=shift @cc;
  122.             push @c, "$a/$b";
  123.         }
  124.     }
  125.    
  126.     return @c unless(1==@r && 1==@c && !wantarray());
  127.     return $c[0];
  128. }
  129.  
  130. sub _range2cidr {
  131.     my $a=shift;
  132.     my $b=shift;
  133.  
  134.     my @a=@$a;
  135.     my @b=@$b;
  136.  
  137.     $a=shift @a;
  138.     $b=shift @b;
  139.  
  140.     return _range2cidr8($a, $b) if $#a < 0;
  141.  
  142.     die unless $a >= 0 && $a <= 255 && $a =~ /^[0-9]+$/;
  143.     die unless $b >= 0 && $b <= 255 && $b =~ /^[0-9]+$/ && $b >= $a;
  144.  
  145.     my @c;
  146.  
  147.     if ($a == $b)   {
  148.         my @cc= _range2cidr(\@a, \@b);
  149.        
  150.         while ($#cc >= 0)   {
  151.             my $c=shift @cc;
  152.         push @c, "$a.$c";
  153.         $c=shift @cc;
  154.         push @c, $c+8;
  155.         }
  156.         return @c;
  157.     }
  158.  
  159.     my $start0=1;
  160.     my $end255=1;
  161.  
  162.     grep { $start0=0 unless $_ == 0; } @a;
  163.     grep { $end255=0 unless $_ == 255; } @b;
  164.  
  165.     if (! $start0) {
  166.         my @bcopy=@b;
  167.         grep { $_=255 } @bcopy;
  168.         my @cc= _range2cidr(\@a, \@bcopy);
  169.         while ($#cc >= 0)   {
  170.             my $c=shift @cc;
  171.             push @c, "$a.$c";
  172.             $c=shift @cc;
  173.             push @c, $c + 8;
  174.         }
  175.         ++$a;
  176.   }
  177.  
  178.     if (! $end255) {
  179.         my @acopy=@a;
  180.         grep { $_=0 } @acopy;
  181.         my @cc= _range2cidr(\@acopy, \@b);
  182.         while ($#cc >= 0)   {
  183.             my $c=shift @cc;
  184.             push @c, "$b.$c";
  185.             $c=shift @cc;
  186.             push @c, $c + 8;
  187.         }
  188.         --$b;
  189.     }
  190.  
  191.     if ($a <= $b) {
  192.         grep { $_=0 } @a;
  193.         my $pfix=join(".", @a);
  194.         my @cc= _range2cidr8($a, $b);
  195.         while ($#cc >= 0)   {
  196.             my $c=shift @cc;
  197.             push @c, "$c.$pfix";
  198.             $c=shift @cc;
  199.         push @c, $c;
  200.         }
  201.     }
  202.     return @c;
  203. }
  204.  
  205. sub _range2cidr8
  206. {
  207.     my @c;
  208.     my @r = @_;
  209.    
  210.     while ($#r >= 0) {
  211.         my $a=shift @r;
  212.         my $b=shift @r;
  213.        
  214.         die unless $a >= 0 && $a <= 255 && $a =~ /^[0-9]+$/;
  215.         die unless $b >= 0 && $b <= 255 && $b =~ /^[0-9]+$/ && $b >= $a;
  216.        
  217.         ++$b;
  218.        
  219.         while ($a < $b) {
  220.             my $i=0;
  221.             my $n=1;
  222.            
  223.             while ( ($n & $a) == 0) {
  224.                 ++$i;
  225.                 $n <<= 1;
  226.                 last if $i >= 8;
  227.             }
  228.             while ($i && $n + $a > $b)  {
  229.                 --$i;
  230.                 $n >>= 1;
  231.             }
  232.             push @c, $a;
  233.             push @c, 8-$i;
  234.             $a += $n;
  235.         }
  236.     }
  237.     return @c;
  238. }
  239.  
  240. sub no2ip {
  241.     my $no = shift(@_);
  242.     return inet_ntoa(pack("N", $no));
  243. }
  244.  
  245. sub ip2no {
  246.     my $ip = shift(@_);
  247.     return unpack("N",inet_aton($ip));
  248. }
  249.  
  250. sub ipnumber_sort {
  251.     my @a_val = split('/', $a);
  252.     my @b_val = split('/', $b);
  253.     return &ip2no($a_val[0]) <=> &ip2no($b_val[0]);
  254. }
  255.  
  256. sub splitcsv() {
  257.     my $line = shift (@_);
  258.     my $sep = ',';
  259.     return () unless $line;
  260.     my @cells;
  261.     $line =~ s/\r?\n$//;
  262.     my $re = qr/(?:^|$sep)(?:\"([^\"]*)\"|([^$sep]*))/;
  263.     while($line =~ /$re/g) {
  264.         my $value = defined $1 ? $1 : $2;
  265.         push @cells, (defined $value ? $value : '');
  266.     }
  267.     return @cells;
  268. }
  269.  
  270. while (my $line = shift(@cidrdata)) {
  271.     if ($line =~ /^"([\d\.]+)\/(\d+)",(.*)$/)   {
  272.         my $ip = $1;
  273.         my $cidr = $2;
  274.         $line = $3;
  275.        
  276.         my @iparr = split(/\./, $ip);
  277.         my @binary = map { sprintf("%08b", $_) } @iparr;
  278.         my $binarystr = join("", @binary);
  279.         my $binarystrcidr = substr($binarystr, 0, $cidr);
  280.        
  281.         $sortbylength{"GG" . $binarystrcidr} = $line;
  282.     }
  283. }
  284.  
  285. undef(@cidrdata);
  286.  
  287. my $datasection = "";
  288. my $stringtype = 2 << 5;
  289. my $maptype = 7 << 5;
  290. my $pointertype = 1 << 5;
  291. my $uint16type = 5 << 5;
  292. my $uint32type = 6 << 5;
  293. my $uint64type = 9 - 7;
  294. my $arraytype = 11 - 7;
  295. my $extendedtype = 0;
  296. my $doubletype = 3 << 5;
  297.  
  298. if ($filetype eq "city") {
  299.     my %newHash = (%tokens, %tokens2);
  300.     %tokens = %newHash;
  301. }
  302.  
  303. foreach my $token (sort keys(%tokens)) {
  304.     $tokens{$token} = length($datasection);
  305.     my $tokenlength = length($token);
  306.     my $controlbyte = $stringtype | $tokenlength;
  307.    
  308.     $datasection .= &print_byte($controlbyte) . &print_str($token, $tokenlength);
  309. }
  310.  
  311. foreach my $latlong (sort keys(%latlongs)) {
  312.     $latlongs{$latlong} = length($datasection);
  313.     my $controlbyte = $doubletype | 8;
  314.    
  315.     $datasection .= &print_byte($controlbyte) . &print_double($latlong);
  316. }
  317.  
  318. if ($filetype eq "country") {
  319.     foreach my $countrycode (sort keys(%countries)) {
  320.         $countryoffset{$countrycode} = length($datasection);
  321.        
  322.         my $controlbyte = $maptype | 1;
  323.         $datasection .= &print_byte($controlbyte);
  324.         $datasection .= &print_pointer($tokens{"country"});
  325.        
  326.         $controlbyte = $maptype | 2;
  327.         $datasection .= &print_byte($controlbyte);
  328.         $datasection .= &print_pointer($tokens{"iso_code"});
  329.        
  330.         my $tokenlength = length($countrycode);
  331.         $controlbyte = $stringtype | $tokenlength;
  332.         $datasection .= &print_byte($controlbyte) . &print_str($countrycode, $tokenlength);
  333.         $datasection .= &print_pointer($tokens{"names"});
  334.        
  335.         $controlbyte = $maptype | 1;
  336.         $datasection .= &print_byte($controlbyte);
  337.         $datasection .= &print_pointer($tokens{"en"});
  338.        
  339.         my $countryname = $countries{$countrycode};
  340.         $tokenlength = length($countryname);
  341.         $controlbyte = $stringtype | $tokenlength;
  342.         $datasection .= &print_byte($controlbyte) . &print_str($countryname, $tokenlength);
  343.     }
  344.     undef(%countries);
  345. }
  346. elsif ($filetype eq "city")
  347. {
  348.     foreach my $stuff (sort keys(%cities))  {
  349.         my @array = split(/\|/, $stuff);
  350.         my $countrycode = $array[0];
  351.         my $countryname = $array[1];
  352.         my $statename = $array[2];
  353.         my $cityname = $array[3];
  354.         my $latitude = $array[4];
  355.         my $longitude = $array[5];
  356.         my $postcode = $array[6];
  357.        
  358.         $cityoffset{$countrycode . "|" . $statename . "|" . $cityname . "|" . $latitude . "|" . $longitude . "|" . $postcode} = length($datasection);
  359.        
  360.         my $controlbyte = $maptype | 5;
  361.         $datasection .= &print_byte($controlbyte);
  362.        
  363.         $datasection .= &print_pointer($tokens{"city"});
  364.         $controlbyte = $maptype | 1;
  365.         $datasection .= &print_byte($controlbyte);
  366.         $datasection .= &print_pointer($tokens{"names"});
  367.         $controlbyte = $maptype | 1;
  368.         $datasection .= &print_byte($controlbyte);
  369.         $datasection .= &print_pointer($tokens{"en"});
  370.         $datasection .= &print_pointer($tokens{$cityname});
  371.        
  372.         $datasection .= &print_pointer($tokens{"country"});
  373.         $controlbyte = $maptype | 2;
  374.         $datasection .= &print_byte($controlbyte);
  375.         $datasection .= &print_pointer($tokens{"iso_code"});
  376.         $datasection .= &print_pointer($tokens{$countrycode});
  377.         $datasection .= &print_pointer($tokens{"names"});
  378.         $controlbyte = $maptype | 1;
  379.         $datasection .= &print_byte($controlbyte);
  380.         $datasection .= &print_pointer($tokens{"en"});
  381.         $datasection .= &print_pointer($tokens{$countryname});
  382.        
  383.         $datasection .= &print_pointer($tokens{"location"});
  384.         $controlbyte = $maptype | 2;
  385.         $datasection .= &print_byte($controlbyte);
  386.         $datasection .= &print_pointer($tokens{"latitude"});
  387.         $datasection .= &print_pointer($latlongs{$latitude});
  388.         $datasection .= &print_pointer($tokens{"longitude"});
  389.         $datasection .= &print_pointer($latlongs{$longitude});
  390.        
  391.         $datasection .= &print_pointer($tokens{"postal"});
  392.         $controlbyte = $maptype | 1;
  393.         $datasection .= &print_byte($controlbyte);
  394.         $datasection .= &print_pointer($tokens{"code"});
  395.         $datasection .= &print_pointer($tokens{$postcode});
  396.        
  397.         $datasection .= &print_pointer($tokens{"subdivisions"});
  398.         my $myint = 1;
  399.         $controlbyte = $extendedtype | $myint;
  400.         my $typebyte = $arraytype;
  401.         $datasection .= &print_byte($controlbyte) . &print_byte($typebyte);
  402.         $controlbyte = $maptype | 1;
  403.         $datasection .= &print_byte($controlbyte);
  404.         $datasection .= &print_pointer($tokens{"names"});
  405.         my $controlbyte = $maptype | 1;
  406.         $datasection .= &print_byte($controlbyte);
  407.         $datasection .= &print_pointer($tokens{"en"});
  408.         $datasection .= &print_pointer($tokens{$statename});
  409.     }
  410.     undef(%cities);
  411. }
  412.  
  413. foreach my $binarystrcidr (sort keys(%sortbylength))
  414. {
  415.     my $tmp = $binarystrcidr;
  416.     $tmp =~ s/GG//;
  417.     my @myarr = split(//, $tmp);
  418.  
  419.     my $code = '$btree';
  420.     foreach (@myarr) {
  421.         $code .= '{"x' . $_ . '"}';
  422.     }
  423.     $code .= ' = "' . $sortbylength{$binarystrcidr} . '";';
  424.  
  425.     eval($code);
  426.     warn $@ if $@;
  427. }
  428.  
  429. undef(%sortbylength);
  430.  
  431. travtree(\%btree, 0, '');
  432.  
  433. undef(%btree);
  434.  
  435. my $totalnodes = 0;
  436. my @offsetnodes;
  437.  
  438. foreach my $x (0..$#data) {
  439.     my $nodes = @{$data[$x]};
  440.     $totalnodes += $nodes;
  441.     $offsetnodes[$x] = $totalnodes;
  442. }
  443.  
  444. open OUT, ">$filename2" or die;
  445. binmode OUT;
  446.  
  447. foreach my $x (0..$#data)
  448. {
  449.     my @datalevel = @{$data[$x]};
  450.    
  451.     foreach my $y (0..$#datalevel) {
  452.         my $nodedata = $datalevel[$y];
  453.        
  454.         if ($nodedata =~ /^(.*)\#(.*)$/)        {
  455.             my $left = $1;
  456.             my $right = $2;
  457.             my $leftdata = 0;
  458.             my $rightdata = 0;
  459.            
  460.             if ($left =~ /^\d+$/)   {
  461.                 $left += $offsetnodes[$x];
  462.                 $leftdata = $left;
  463.             }   else {
  464.                 if ($filetype eq 'country') {
  465.                     $leftdata = $countryoffset{$left} + $datastartmarkerlength + $totalnodes;
  466.                 }   elsif ($filetype eq 'city') {
  467.                     $leftdata = $cityoffset{$left} + $datastartmarkerlength + $totalnodes;
  468.                 }
  469.             }
  470.             if ($right =~ /^\d+$/) {
  471.                 $right += $offsetnodes[$x];
  472.                 $rightdata = $right;
  473.             }   else {
  474.                 if ($filetype eq 'country') {
  475.                     $rightdata = $countryoffset{$right} + $datastartmarkerlength + $totalnodes;
  476.                 }   elsif ($filetype eq 'city') {
  477.                     $rightdata = $cityoffset{$right} + $datastartmarkerlength + $totalnodes;
  478.                 }
  479.             }
  480.             print OUT &print_node($leftdata, $rightdata);
  481.         }
  482.     }
  483. }
  484.  
  485. undef(@data);
  486.  
  487. print OUT $datastartmarker;
  488. print OUT $datasection;
  489. print OUT &print_hex("ABCDEF4D61784D696E642E636F6D");
  490. my $controlbyte = $maptype | 9;
  491. print OUT &print_byte($controlbyte);
  492. my $field = "binary_format_major_version";
  493. my $fieldlength = length($field);
  494. $controlbyte = $stringtype | $fieldlength;
  495. print OUT &print_byte($controlbyte) . &print_str($field, $fieldlength);
  496. my $myint = 2;
  497. $myint = &print_uint($myint);
  498. my $intbytes = length($myint);
  499. $controlbyte = $uint16type | $intbytes;
  500. print OUT &print_byte($controlbyte) . $myint;
  501. $field = "binary_format_minor_version";
  502. $fieldlength = length($field);
  503. $controlbyte = $stringtype | $fieldlength;
  504. print OUT &print_byte($controlbyte) . &print_str($field, $fieldlength);
  505. my $myint = 0;
  506. $myint = &print_uint($myint);
  507. my $intbytes = length($myint);
  508. $controlbyte = $uint16type | $intbytes;
  509. print OUT &print_byte($controlbyte) . $myint;
  510. $field = "build_epoch";
  511. $fieldlength = length($field);
  512. $controlbyte = $stringtype | $fieldlength;
  513. print OUT &print_byte($controlbyte) . &print_str($field, $fieldlength);
  514. $myint = time;
  515. $myint = &print_uint($myint);
  516. my $intbytes = length($myint);
  517. $controlbyte = $extendedtype | $intbytes;
  518. my $typebyte = $uint64type;
  519. print OUT &print_byte($controlbyte) . &print_byte($typebyte) . $myint;
  520. $field = "database_type";
  521. $fieldlength = length($field);
  522. $controlbyte = $stringtype | $fieldlength;
  523. print OUT &print_byte($controlbyte) . &print_str($field, $fieldlength);
  524. $field = ($filetype eq 'country') ? "IP2LITE-Country" : "IP2LITE-City";
  525. $fieldlength = length($field);
  526. $controlbyte = $stringtype | $fieldlength;
  527. print OUT &print_byte($controlbyte) . &print_str($field, $fieldlength);
  528. $field = "description";
  529. $fieldlength = length($field);
  530. $controlbyte = $stringtype | $fieldlength;
  531. print OUT &print_byte($controlbyte) . &print_str($field, $fieldlength);
  532. my $controlbyte = $maptype | 1;
  533. print OUT &print_byte($controlbyte);
  534. $field = "en";
  535. $fieldlength = length($field);
  536. $controlbyte = $stringtype | $fieldlength;
  537. print OUT &print_byte($controlbyte) . &print_str($field, $fieldlength);
  538. $field = ($filetype eq 'country') ? "IP2LITE-Country database" : "IP2LITE-City database";
  539. $fieldlength = length($field);
  540. $controlbyte = $stringtype | $fieldlength;
  541. print OUT &print_byte($controlbyte) . &print_str($field, $fieldlength);
  542. $field = "ip_version";
  543. $fieldlength = length($field);
  544. $controlbyte = $stringtype | $fieldlength;
  545. print OUT &print_byte($controlbyte) . &print_str($field, $fieldlength);
  546. my $myint = 4;
  547. $myint = &print_uint($myint);
  548. my $intbytes = length($myint);
  549. $controlbyte = $uint16type | $intbytes;
  550. print OUT &print_byte($controlbyte) . $myint;
  551. $field = "languages";
  552. $fieldlength = length($field);
  553. $controlbyte = $stringtype | $fieldlength;
  554. print OUT &print_byte($controlbyte) . &print_str($field, $fieldlength);
  555. $myint = 1;
  556. $controlbyte = $extendedtype | $myint;
  557. my $typebyte = $arraytype;
  558. print OUT &print_byte($controlbyte) . &print_byte($typebyte);
  559. $field = "en";
  560. $fieldlength = length($field);
  561. $controlbyte = $stringtype | $fieldlength;
  562. print OUT &print_byte($controlbyte) . &print_str($field, $fieldlength);
  563. $field = "node_count";
  564. $fieldlength = length($field);
  565. $controlbyte = $stringtype | $fieldlength;
  566. print OUT &print_byte($controlbyte) . &print_str($field, $fieldlength);
  567. $myint = $totalnodes;
  568. $myint = &print_uint($myint);
  569. my $intbytes = length($myint);
  570. $controlbyte = $uint32type | $intbytes;
  571. print OUT &print_byte($controlbyte) . $myint;
  572. $field = "record_size";
  573. $fieldlength = length($field);
  574. $controlbyte = $stringtype | $fieldlength;
  575. print OUT &print_byte($controlbyte) . &print_str($field, $fieldlength);
  576. $myint = ($filetype eq 'country') ? 24 : 28;
  577. $myint = &print_uint($myint);
  578. my $intbytes = length($myint);
  579. $controlbyte = $uint32type | $intbytes;
  580. print OUT &print_byte($controlbyte) . $myint;
  581.  
  582. close OUT;
  583.  
  584. print "You have successfully converted $filename to $filename2.\n";
  585. print "You can now use the $filename2 with any MaxMind API which supports the GeoLite2 format.\n";
  586.  
  587. sub travtree {
  588.     my ($hash, $level, $trace) = @_;
  589.     my $leftval = -1;
  590.     my $rightval = -1;
  591.     my $leftleaf = 0;
  592.     my $rightleaf = 0;
  593.     while (my ($key, $value) = each %$hash) {
  594.         my $key2 = $key;
  595.         my $trace2 = $trace . $key2;
  596.        
  597.         if ('SCALAR' eq ref(\$value)) {
  598.             if ($key eq 'x0')   {
  599.                 $leftval = $value;
  600.                 $leftleaf = 1;
  601.             }   elsif ($key eq 'x1') {
  602.                 $rightval = $value;
  603.                 $rightleaf = 1;
  604.             }
  605.         } elsif ('REF' eq ref(\$value)) {
  606.             my $tmp = &travtree(\%{$value}, $level + 1, $trace2);
  607.            
  608.             if ($key eq 'x0')   {
  609.                 $leftval = $tmp;
  610.             }   elsif ($key eq 'x1')    {
  611.                 $rightval = $tmp;
  612.             }
  613.         }
  614.     }
  615.  
  616.     my $ownoffset = 0;
  617.     if (defined($data[$level])) {
  618.         $ownoffset = @{$data[$level]};
  619.     }   else {
  620.         $data[$level] = ();
  621.     }
  622.    
  623.     $data[$level][$ownoffset] = $leftval . "#" . $rightval;
  624.     return $ownoffset;
  625. }
  626.  
  627. sub print_double {
  628.     my $num = shift;
  629.     my $s = pack('d>', $num);
  630.     return $s;
  631. }
  632.  
  633. sub print_uint {
  634.     my $num = shift;
  635.     my $s = "";
  636.     while ($num > 0) {
  637.         my $num2 = $num & 0xFF;
  638.         $s = &print_byte($num2) . $s;
  639.         $num = $num >> 8;
  640.     }
  641.     return $s;
  642. }
  643.  
  644. sub print_str {
  645.     my $value = shift;
  646.     my $x = shift;
  647.     my $s = pack('A' . $x, $value);
  648.     return $s;
  649. }
  650.  
  651. sub print_byte {
  652.     my $num = shift;
  653.     my $s = pack('C', $num);
  654.     return $s;
  655. }
  656.  
  657. sub print_hex {
  658.     my $num = shift;
  659.     my $s = pack('H*', $num);
  660.     return $s;
  661. }
  662.  
  663. sub print_node {
  664.     my $leftdata = shift;
  665.     my $rightdata = shift;
  666.     my @mybytes;
  667.    
  668.     if ($filetype eq 'country') {
  669.         my @leftbytes = &get_byte_array($leftdata, 3);
  670.         my @rightbytes = &get_byte_array($rightdata, 3);
  671.         @mybytes = (@leftbytes, @rightbytes);
  672.     }   elsif ($filetype eq 'city') {
  673.         my @leftbytes = &get_byte_array($leftdata, 4);
  674.         my @rightbytes = &get_byte_array($rightdata, 4);
  675.         my $midbyte = ($leftbytes[0] << 4) ^ $rightbytes[0];
  676.         shift(@leftbytes);
  677.         shift(@rightbytes);
  678.         push(@leftbytes, $midbyte);
  679.         @mybytes = (@leftbytes, @rightbytes);
  680.     }
  681.    
  682.     my $s = "";
  683.     foreach (@mybytes) {
  684.         $s .= &print_byte($_);
  685.     }
  686.     return $s;
  687. }
  688.  
  689. sub print_pointer
  690. {
  691.     my $num = shift;
  692.     my $pointersize = -1;
  693.     my $threebits = 0;
  694.     my @balance;
  695.    
  696.     if ($num <= 2047)   {
  697.         $pointersize = 0;
  698.         $threebits = $num >> 8;
  699.         @balance = &get_byte_array($num, 1);
  700.     }   elsif ($num <= 526335) {
  701.         $pointersize = 1;
  702.         $num = $num - 2048;
  703.         $threebits = $num >> 16;
  704.         @balance = &get_byte_array($num, 2);
  705.     }   elsif ($num <= 134744063)   {
  706.         $pointersize = 2;
  707.         $num = $num - 526336;
  708.         $threebits = $num >> 24;
  709.         @balance = &get_byte_array($num, 3);
  710.     }   elsif ($num <= 4294967295) {
  711.         $pointersize = 3;
  712.         $threebits = 0;
  713.         @balance = &get_byte_array($num, 4);
  714.     } else {
  715.         die "Pointer value too large.\n";
  716.     }
  717.    
  718.     $pointersize = $pointersize << 3;
  719.    
  720.     my $controlbyte = $pointertype | $pointersize | $threebits;
  721.    
  722.     my $s = &print_byte($controlbyte);
  723.    
  724.     foreach (@balance)  {
  725.         $s .= &print_byte($_);
  726.     }
  727.    
  728.     return $s;
  729. }
  730.  
  731. sub get_byte_array {
  732.     my $num = shift;
  733.     my $bytes = shift;
  734.     my @bytesarr;
  735.     my $tmp;
  736.    
  737.     foreach (1..$bytes) {
  738.         $tmp = $num & 0xFF;
  739.         $num = $num >> 8;
  740.         unshift(@bytesarr, $tmp);
  741.     }
  742.     return @bytesarr;
  743. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement