home *** CD-ROM | disk | FTP | other *** search
/ PC Professionell 2004 December / PCpro_2004_12.ISO / files / webserver / xampp / xampp-perl-addon-1.4.9-installer.exe / ParseSource.pm < prev    next >
Encoding:
Perl POD Document  |  2004-08-12  |  15.5 KB  |  619 lines

  1. # Copyright 2001-2004 The Apache Software Foundation
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. #     http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. #
  15. package Apache::ParseSource;
  16.  
  17. use strict;
  18. use warnings FATAL => 'all';
  19.  
  20. use Apache::Build ();
  21. use Config;
  22. use File::Basename;
  23. use File::Spec::Functions qw(catdir);
  24.  
  25. our $VERSION = '0.02';
  26.  
  27. sub new {
  28.     my $class = shift;
  29.  
  30.     my $self = bless {
  31.         config => Apache::Build->build_config,
  32.         @_,
  33.     }, $class;
  34.  
  35.     my $prefixes = join '|', @{ $self->{prefixes} || [qw(ap_ apr_)] };
  36.     $self->{prefix_re} = qr{^($prefixes)};
  37.  
  38.     $Apache::Build::APXS ||= $self->{apxs};
  39.  
  40.     $self;
  41. }
  42.  
  43. sub config {
  44.     shift->{config};
  45. }
  46.  
  47. sub parse {
  48.     my $self = shift;
  49.  
  50.     $self->{scan_filename} = $self->generate_cscan_file;
  51.  
  52.     $self->{c} = $self->scan;
  53. }
  54.  
  55. sub DESTROY {
  56.     my $self = shift;
  57.     unlink $self->{scan_filename}
  58. }
  59.  
  60. {
  61.     package Apache::ParseSource::Scan;
  62.  
  63.     our @ISA = qw(ModPerl::CScan);
  64.  
  65.     sub get {
  66.         local $SIG{__DIE__} = \&Carp::confess;
  67.         shift->SUPER::get(@_);
  68.     }
  69. }
  70.  
  71. my @c_scan_defines = (
  72.     'CORE_PRIVATE',   #so we get all of apache
  73.     'MP_SOURCE_SCAN', #so we can avoid some c-scan barfing
  74.     '_NETINET_TCP_H', #c-scan chokes on netinet/tcp.h
  75.  #   'APR_OPTIONAL_H', #c-scan chokes on apr_optional.h
  76.     'apr_table_do_callback_fn_t=void', #c-scan chokes on function pointers
  77. );
  78.  
  79.  
  80. # some types c-scan failing to resolve
  81. push @c_scan_defines, map { "$_=void" } 
  82.     qw(PPADDR_t PerlExitListEntry modperl_tipool_vtbl_t);
  83.  
  84. sub scan {
  85.     require ModPerl::CScan;
  86.     ModPerl::CScan->VERSION(0.75);
  87.     require Carp;
  88.  
  89.     my $self = shift;
  90.  
  91.     my $c = ModPerl::CScan->new(filename => $self->{scan_filename});
  92.  
  93.     my $includes = $self->includes;
  94.  
  95.     # where to find perl headers, but we don't want to parse them otherwise
  96.     my $perl_core_path = catdir $Config{installarchlib}, "CORE";
  97.     push @$includes, $perl_core_path;
  98.  
  99.     $c->set(includeDirs => $includes);
  100.  
  101.     my @defines = @c_scan_defines;
  102.  
  103.     unless ($Config{useithreads} and $Config{useithreads} eq 'define') {
  104.         #fake -DITHREADS so function tables are the same for
  105.         #vanilla and ithread perls, that is,
  106.         #make sure THX and friends are always expanded
  107.         push @defines, 'MP_SOURCE_SCAN_NEED_ITHREADS';
  108.     }
  109.  
  110.     $c->set(Defines => join ' ', map "-D$_", @defines);
  111.  
  112.     bless $c, 'Apache::ParseSource::Scan';
  113. }
  114.  
  115. sub include_dirs {
  116.     my $self = shift;
  117.     ($self->config->apxs('-q' => 'INCLUDEDIR'),
  118.      $self->config->mp_include_dir);
  119. }
  120.  
  121. sub includes { shift->config->includes }
  122.  
  123. sub find_includes {
  124.     my $self = shift;
  125.  
  126.     return $self->{includes} if $self->{includes};
  127.  
  128.     require File::Find;
  129.  
  130.     my @includes = ();
  131.     # don't pick preinstalled mod_perl headers if any, but pick the rest
  132.     {
  133.         my @dirs = $self->include_dirs;
  134.         die "could not find include directory (build the project first)"
  135.             unless -d $dirs[0];
  136.  
  137.         my $unwanted = join '|', qw(ap_listen internal version
  138.                                     apr_optional mod_include mod_cgi
  139.                                     mod_proxy mod_ssl ssl_ apr_anylock
  140.                                     apr_rmm ap_config mod_log_config
  141.                                     mod_perl modperl_ apreq);
  142.         $unwanted = qr|^$unwanted|;
  143.         my $wanted = '';
  144.  
  145.         push @includes, find_includes_wanted($wanted, $unwanted, @dirs);
  146.     }
  147.  
  148.     # now add the live mod_perl headers (to make sure that we always
  149.     # work against the latest source)
  150.     {
  151.         my @dirs = map { catdir $self->config->{cwd}, $_ }
  152.             catdir(qw(src modules perl)), 'xs';
  153.  
  154.         my $unwanted = '';
  155.         my $wanted = join '|', qw(mod_perl modperl_);
  156.         $wanted = qr|^$wanted|;
  157.  
  158.         push @includes, find_includes_wanted($wanted, $unwanted, @dirs);
  159.     }
  160.  
  161.     # now reorg the header files list, so the fragile scan won't choke
  162.     my @apr = ();
  163.     my @mp = ();
  164.     my @rest = ();
  165.     for (@includes) {
  166.         if (/mod_perl.h$/) {
  167.             # mod_perl.h needs to be included before other mod_perl
  168.             # headers
  169.             unshift @mp, $_;
  170.         }
  171.         elsif (/modperl_\w+.h$/) {
  172.             push @mp, $_;
  173.         }
  174.         elsif (/apr_\w+\.h$/ ) {
  175.             # apr headers need to be included first
  176.             push @apr, $_;
  177.         }
  178.         else {
  179.             push @rest, $_;
  180.         }
  181.     }
  182.     @includes = (@apr, @rest, @mp);
  183.  
  184.     return $self->{includes} = \@includes;
  185. }
  186.  
  187. sub find_includes_wanted {
  188.     my($wanted, $unwanted, @dirs) = @_;
  189.     my @includes = ();
  190.     for my $dir (@dirs) {
  191.         File::Find::finddepth({
  192.                                wanted => sub {
  193.                                    return unless /\.h$/;
  194.  
  195.                                    if ($wanted) {
  196.                                        return unless /$wanted/;
  197.                                    }
  198.                                    else {
  199.                                        return if /$unwanted/;
  200.                                    }
  201.  
  202.                                    my $dir = $File::Find::dir;
  203.                                    push @includes, "$dir/$_";
  204.                                },
  205.                                (Apache::Build::WIN32 ? '' : follow => 1),
  206.                               }, $dir);
  207.     }
  208.     return @includes;
  209. }
  210.  
  211. sub generate_cscan_file {
  212.     my $self = shift;
  213.  
  214.     my $includes = $self->find_includes;
  215.  
  216.     my $filename = '.apache_includes';
  217.     open my $fh, '>', $filename or die "can't open $filename: $!";
  218.  
  219.     for my $path (@$includes) {
  220.         my $filename = basename $path;
  221.         print $fh qq(\#include "$path"\n);
  222.     }
  223.  
  224.     close $fh;
  225.  
  226.     return $filename;
  227. }
  228.  
  229. my $filemode = join '|',
  230.   qw{READ WRITE CREATE APPEND TRUNCATE BINARY EXCL BUFFERED DELONCLOSE};
  231.  
  232. my %defines_wanted = (
  233.     Apache => {
  234.         common     => [qw{OK DECLINED DONE}],
  235.         config     => [qw{DECLINE_CMD}],
  236.         context    => [qw(NOT_IN_ GLOBAL_ONLY)],
  237.         http       => [qw{HTTP_}],
  238.         log        => [qw(APLOG_)],
  239.         methods    => [qw{M_ METHODS}],
  240.         mpmq       => [qw{AP_MPMQ_}],
  241.         options    => [qw{OPT_}],
  242.         override   => [qw{OR_ ACCESS_CONF RSRC_CONF}],
  243.         platform   => [qw{CRLF CR LF}],
  244.         remotehost => [qw{REMOTE_}],
  245.         satisfy    => [qw{SATISFY_}],
  246.         types      => [qw{DIR_MAGIC_TYPE}],
  247.     },
  248.     APR => {
  249.         common    => [qw{APR_SUCCESS}],
  250.         error     => [qw{APR_E}],
  251.         filemode  => ["APR_($filemode)"],
  252.         filepath  => [qw{APR_FILEPATH_}],
  253.         fileperms => [qw{APR_\w(READ|WRITE|EXECUTE)}],
  254.         finfo     => [qw{APR_FINFO_}],
  255.         flock     => [qw{APR_FLOCK_}],
  256.         hook      => [qw{APR_HOOK_}],
  257.         limit     => [qw{APR_LIMIT}],
  258.         poll      => [qw{APR_POLL}],
  259.         socket    => [qw{APR_SO_}],
  260.         status    => [qw{APR_TIMEUP}],
  261.         table     => [qw{APR_OVERLAP_TABLES_}],
  262.         uri       => [qw{APR_URI_}],
  263.     },
  264.    ModPerl => {
  265.         common    => [qw{MODPERL_RC_}],
  266.    }
  267. );
  268.  
  269. my %defines_wanted_re;
  270. while (my($class, $groups) = each %defines_wanted) {
  271.     while (my($group, $wanted) = each %$groups) {
  272.         my $pat = join '|', @$wanted;
  273.         $defines_wanted_re{$class}->{$group} = $pat; #qr{^($pat)};
  274.     }
  275. }
  276.  
  277. my %enums_wanted = (
  278.     Apache => { map { $_, 1 } qw(cmd_how input_mode filter_type conn_keepalive) },
  279.     APR => { map { $_, 1 } qw(apr_shutdown_how apr_read_type apr_lockmech apr_filetype) },
  280. );
  281.  
  282. my $defines_unwanted = join '|', qw{
  283. HTTP_VERSION APR_EOL_STR APLOG_MARK APLOG_NOERRNO APR_SO_TIMEOUT
  284. };
  285.  
  286. sub get_constants {
  287.     my($self) = @_;
  288.  
  289.     my $includes = $self->find_includes;
  290.     my(%constants, %seen);
  291.  
  292.     for my $file (@$includes) {
  293.         open my $fh, $file or die "open $file: $!";
  294.         while (<$fh>) {
  295.             if (s/^\#define\s+(\w+)\s+.*/$1/) {
  296.                 chomp;
  297.                 next if /_H$/;
  298.                 next if $seen{$_}++;
  299.                 $self->handle_constant(\%constants);
  300.             }
  301.             elsif (m/enum[^\{]+\{/) {
  302.                 $self->handle_enum($fh, \%constants);
  303.             }
  304.         }
  305.         close $fh;
  306.     }
  307.  
  308.     #maintain a few handy shortcuts from 1.xx
  309.     #aliases are defined in ModPerl::Code
  310.     push @{ $constants{'Apache'}->{common} },
  311.       qw(NOT_FOUND FORBIDDEN AUTH_REQUIRED SERVER_ERROR REDIRECT);
  312.  
  313.     return \%constants;
  314. }
  315.  
  316. sub handle_constant {
  317.     my($self, $constants) = @_;
  318.     my $keys = keys %defines_wanted_re; #XXX broken bleedperl ?
  319.  
  320.     return if /^($defines_unwanted)/o;
  321.  
  322.     while (my($class, $groups) = each %defines_wanted_re) {
  323.         my $keys = keys %$groups; #XXX broken bleedperl ?
  324.  
  325.         while (my($group, $re) = each %$groups) {
  326.             next unless /^($re)/;
  327.             push @{ $constants->{$class}->{$group} }, $_;
  328.             return;
  329.         }
  330.     }
  331. }
  332.  
  333. sub handle_enum {
  334.     my($self, $fh, $constants) = @_;
  335.  
  336.     my($name, $e) = $self->parse_enum($fh);
  337.     return unless $name;
  338.  
  339.     $name =~ s/^ap_//;
  340.     $name =~ s/_(e|t)$//;
  341.  
  342.     my $class;
  343.     for (keys %enums_wanted) {
  344.         next unless $enums_wanted{$_}->{$name};
  345.         $class = $_;
  346.     }
  347.  
  348.     return unless $class;
  349.     $name =~ s/^apr_//;
  350.  
  351.     push @{ $constants->{$class}->{$name} }, @$e if $e;
  352. }
  353.  
  354. #this should win an award for worlds lamest parser
  355. sub parse_enum {
  356.     my($self, $fh) = @_;
  357.     my $code = $_;
  358.     my @e;
  359.  
  360.     unless ($code =~ /;\s*$/) {
  361.         local $_;
  362.         while (<$fh>) {
  363.             $code .= $_;
  364.             last if /;\s*$/;
  365.         }
  366.     }
  367.  
  368.     my $name;
  369.     if ($code =~ s/^\s*enum\s+(\w*)\s*//) {
  370.         $name = $1;
  371.     }
  372.     elsif ($code =~ s/^\s*typedef\s+enum\s+//) {
  373.         $code =~ s/\s*(\w+)\s*;\s*$//;
  374.         $name = $1;
  375.     }
  376.  
  377.     $code =~ s:/\*.*?\*/::sg;
  378.     $code =~ s/\s*=\s*\w+//g;
  379.     $code =~ s/^[^\{]*\{//s;
  380.     $code =~ s/\}[^;]*;?//s;
  381.     $code =~ s/^\s*\n//gm;
  382.  
  383.     while ($code =~ /\b(\w+)\b,?/g) {
  384.         push @e, $1;
  385.     }
  386.  
  387.     return ($name, \@e);
  388. }
  389.  
  390. sub wanted_functions  { shift->{prefix_re} }
  391. sub wanted_structures { shift->{prefix_re} }
  392.  
  393. sub get_functions {
  394.     my $self = shift;
  395.  
  396.     my $key = 'parsed_fdecls';
  397.     return $self->{$key} if $self->{$key};
  398.  
  399.     my $c = $self->{c};
  400.  
  401.     my $fdecls = $c->get($key);
  402.  
  403.     my %seen;
  404.     my $wanted = $self->wanted_functions;
  405.  
  406.     my @functions;
  407.  
  408.     for my $entry (@$fdecls) {
  409.         my($rtype, $name, $args) = @$entry;
  410.         next unless $name =~ $wanted;
  411.         next if $seen{$name}++;
  412.         my @attr;
  413.  
  414.         for (qw(static __inline__)) {
  415.             if ($rtype =~ s/^($_)\s+//) {
  416.                 push @attr, $1;
  417.             }
  418.         }
  419.  
  420.         #XXX: working around ModPerl::CScan confusion here
  421.         #macro defines ap_run_error_log causes
  422.         #cpp filename:linenumber to be included as part of the type
  423.         for (@$args) {
  424.             next unless $_->[0];
  425.             $_->[0] =~ s/^\#.*?\"\s+//;
  426.             $_->[0] =~ s/^register //;
  427.         }
  428.  
  429.         my $func = {
  430.            name => $name,
  431.            return_type => $rtype,
  432.            args => [map {
  433.                { type => $_->[0], name => $_->[1] }
  434.            } @$args],
  435.         };
  436.  
  437.         $func->{attr} = \@attr if @attr;
  438.  
  439.         push @functions, $func;
  440.     }
  441.  
  442.     # sort the functions by the 'name' attribute to ensure a
  443.     # consistent output on different systems.
  444.     $self->{$key} = [sort { $a->{name} cmp $b->{name} } @functions];
  445. }
  446.  
  447. sub get_structs {
  448.     my $self = shift;
  449.  
  450.     my $key = 'typedef_structs';
  451.     return $self->{$key} if $self->{$key};
  452.  
  453.     my $c = $self->{c};
  454.  
  455.     my $typedef_structs = $c->get($key);
  456.  
  457.     my %seen;
  458.     my $wanted = $self->wanted_structures;
  459.     my $other  = join '|', qw(_rec module
  460.                               piped_log uri_t htaccess_result
  461.                               cmd_parms cmd_func cmd_how);
  462.  
  463.     my @structures;
  464.     my $sx = qr(^struct\s+);
  465.  
  466.     while (my($type, $elts) = each %$typedef_structs) {
  467.         next unless $type =~ $wanted or $type =~ /($other)$/o;
  468.  
  469.         $type =~ s/$sx//;
  470.  
  471.         next if $seen{$type}++;
  472.  
  473.         my $struct = {
  474.            type => $type,
  475.            elts => [map {
  476.                my $type = $_->[0];
  477.                $type =~ s/$sx//;
  478.                $type .= $_->[1] if $_->[1];
  479.                $type =~ s/:\d+$//; #unsigned:1
  480.                { type => $type, name => $_->[2] }
  481.            } @$elts],
  482.         };
  483.  
  484.         push @structures, $struct;
  485.     }
  486.  
  487.     # sort the structs by the 'type' attribute to ensure a consistent
  488.     # output on different systems.
  489.     $self->{$key} = [sort { $a->{type} cmp $b->{type} } @structures];
  490. }
  491.  
  492. sub write_functions_pm {
  493.     my $self = shift;
  494.     my $file = shift || 'FunctionTable.pm';
  495.     my $name = shift || 'Apache::FunctionTable';
  496.  
  497.     $self->write_pm($file, $name, $self->get_functions);
  498. }
  499.  
  500. sub write_structs_pm {
  501.     my $self = shift;
  502.     my $file = shift || 'StructureTable.pm';
  503.     my $name = shift || 'Apache::StructureTable';
  504.  
  505.     $self->write_pm($file, $name, $self->get_structs);
  506. }
  507.  
  508. sub write_constants_pm {
  509.     my $self = shift;
  510.     my $file = shift || 'ConstantsTable.pm';
  511.     my $name = shift || 'Apache::ConstantsTable';
  512.  
  513.     $self->write_pm($file, $name, $self->get_constants);
  514. }
  515.  
  516. sub write_pm {
  517.     my($self, $file, $name, $data) = @_;
  518.  
  519.     require Data::Dumper;
  520.     local $Data::Dumper::Indent = 1;
  521.  
  522.     my($subdir) = (split '::', $name)[0];
  523.  
  524.     my $tdir = 'xs/tables/current';
  525.     if (-d "$tdir/$subdir") {
  526.         $file = "$tdir/$subdir/$file";
  527.     }
  528.  
  529.     # sort the hashes (including nested ones) for a consistent dump
  530.     canonsort(\$data);
  531.  
  532.     my $dump = Data::Dumper->new([$data],
  533.                                  [$name])->Dump;
  534.  
  535.     my $package = ref($self) || $self;
  536.     my $version = $self->VERSION;
  537.     my $date = scalar localtime;
  538.  
  539.     my $new_content = << "EOF";
  540. package $name;
  541.  
  542. # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  543. # ! WARNING: generated by $package/$version
  544. # !          $date
  545. # !          do NOT edit, any changes will be lost !
  546. # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  547.  
  548. $dump
  549.  
  550. 1;
  551. EOF
  552.  
  553.     my $old_content = '';
  554.     if (-e $file) {
  555.         open my $pm, '<', $file or die "open $file: $!";
  556.         local $/ = undef; # slurp the file
  557.         $old_content = <$pm>;
  558.         close $pm;
  559.     }
  560.  
  561.     my $overwrite = 1;
  562.     if ($old_content) {
  563.         # strip the date line, which will never be the same before
  564.         # comparing
  565.         my $table_header = qr{^\#\s!.*};
  566.         (my $old = $old_content) =~ s/$table_header//mg;
  567.         (my $new = $new_content) =~ s/$table_header//mg;
  568.         $overwrite = 0 if $old eq $new;
  569.     }
  570.  
  571.     if ($overwrite) {
  572.         open my $pm, '>', $file or die "open $file: $!";
  573.         print $pm $new_content;
  574.         close $pm;
  575.     }
  576.  
  577. }
  578.  
  579. # canonsort(\$data);
  580. # sort nested hashes in the data structure.
  581. # the data structure itself gets modified
  582.  
  583. sub canonsort {
  584.     my $ref = shift;
  585.     my $type = ref $$ref;
  586.  
  587.     return unless $type;
  588.  
  589.     require Tie::IxHash;
  590.  
  591.     my $data = $$ref;
  592.  
  593.     if ($type eq 'ARRAY') {
  594.         for (@$data) {
  595.             canonsort(\$_);
  596.         }
  597.     }
  598.     elsif ($type eq 'HASH') {
  599.         for (keys %$data) {
  600.             canonsort(\$data->{$_});
  601.         }
  602.  
  603.         tie my %ixhash, 'Tie::IxHash';
  604.  
  605.         # reverse sort so we get the order of:
  606.         # return_type, name, args { type, name } for functions
  607.         # type, elts { type, name } for structures
  608.  
  609.         for (sort { $b cmp $a } keys %$data) {
  610.             $ixhash{$_} = $data->{$_};
  611.         }
  612.  
  613.         $$ref = \%ixhash;
  614.     }
  615. }
  616.  
  617. 1;
  618. __END__
  619.