X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=utils%2Fh2ph.PL;h=9082e143619a445304952a33cf7c5305e143f709;hb=9df548ee6c72d2540cc8aac7e738eff1202a7278;hp=731360e7fa265c14574182f09f63b129aa74c19d;hpb=5d42aa7bc9bf791efe4c2cf03a5b696d88d51458;p=p5sagit%2Fp5-mst-13.2.git diff --git a/utils/h2ph.PL b/utils/h2ph.PL index 731360e..9082e14 100644 --- a/utils/h2ph.PL +++ b/utils/h2ph.PL @@ -36,13 +36,16 @@ $Config{startperl} print OUT <<'!NO!SUBS!'; +use strict; + use Config; use File::Path qw(mkpath); use Getopt::Std; getopts('Dd:rlhaQ'); +use vars qw($opt_D $opt_d $opt_r $opt_l $opt_h $opt_a $opt_Q); die "-r and -a options are mutually exclusive\n" if ($opt_r and $opt_a); -@inc_dirs = inc_dirs() if $opt_a; +my @inc_dirs = inc_dirs() if $opt_a; my $Exit = 0; @@ -50,7 +53,7 @@ my $Dest_dir = $opt_d || $Config{installsitearch}; die "Destination directory $Dest_dir doesn't exist or isn't a directory\n" unless -d $Dest_dir; -@isatype = split(' ',<$Dest_dir/$outfile") || die "Can't create $outfile: $!\n"; } - while () { - chop; - while (/\\$/) { - chop; - $_ .= ; - chop; - } - print OUT "# $_\n" if $opt_D; - if (s:/\*:\200:g) { - s:\*/:\201:g; - s/\200[^\201]*\201//g; # delete single line comments - if (s/\200.*//) { # begin multi-line comment? - $_ .= '/*'; - $_ .= ; - redo; - } - } + + print OUT "require '_h2ph_pre.ph';\n\n"; + while (defined (local $_ = next_line())) { if (s/^\s*\#\s*//) { if (s/^define\s+(\w+)//) { $name = $1; @@ -124,7 +119,7 @@ while (defined ($file = next_file())) { my $proto = '() '; if ($args ne '') { $proto = ''; - foreach $arg (split(/,\s*/,$args)) { + foreach my $arg (split(/,\s*/,$args)) { $arg =~ s/^\s*([^\s].*[^\s])\s*$/$1/; $curargs{$arg} = 1; } @@ -158,6 +153,7 @@ while (defined ($file = next_file())) { $args = reindent($args); if ($t ne '') { $new =~ s/(['\\])/\\$1/g; #']); + if ($opt_h) { print OUT $t,"eval \"\\n#line $eval_index $outfile\\n\" . 'sub $name () {",$new,";}' unless defined(\&$name);\n"; $eval_index++; @@ -165,6 +161,9 @@ while (defined ($file = next_file())) { print OUT $t,"eval 'sub $name () {",$new,";}' unless defined(\&$name);\n"; } } else { + # Shunt around such directives as `#define FOO FOO': + next if " \&$name" eq $new; + print OUT $t,"unless(defined(\&$name)) {\n sub $name () {\t",$new,";}\n}\n"; } } @@ -230,6 +229,8 @@ while (defined ($file = next_file())) { print OUT $t,"}\n"; } elsif(/^undef\s+(\w+)/) { print OUT $t, "undef(&$1) if defined(&$1);\n"; + } elsif(/^error\s+(".*")/) { + print OUT $t, "die($1);\n"; } elsif(/^error\s+(.*)/) { print OUT $t, "die(\"", quotemeta($1), "\");\n"; } elsif(/^warning\s+(.*)/) { @@ -237,20 +238,24 @@ while (defined ($file = next_file())) { } elsif(/^ident\s+(.*)/) { print OUT $t, "# $1\n"; } - } elsif(/^\s*(typedef\s*)?enum\s*(\s+[a-zA-Z_]\w*\s*)?\{/) { - until(/\}.*?;/) { - chomp($next = ); + } elsif(/^\s*(typedef\s*)?enum\s*(\s+[a-zA-Z_]\w*\s*)?/) { + until(/\{[^}]*\}.*;/ || /;/) { + last unless defined ($next = ); + chomp $next; + # drop "#define FOO FOO" in enums + $next =~ s/^\s*#\s*define\s+(\w+)\s+\1\s*$//; $_ .= $next; print OUT "# $next\n" if $opt_D; } + s/#\s*if.*?#\s*endif//g; # drop #ifdefs s@/\*.*?\*/@@g; s/\s+/ /g; - /^\s?(typedef\s?)?enum\s?([a-zA-Z_]\w*)?\s?\{(.*)\}\s?([a-zA-Z_]\w*)?\s?;/; - ($enum_subs = $3) =~ s/\s//g; - @enum_subs = split(/,/, $enum_subs); - $enum_val = -1; - for $enum (@enum_subs) { - ($enum_name, $enum_value) = $enum =~ /^([a-zA-Z_]\w*)(=.+)?$/; + next unless /^\s?(typedef\s?)?enum\s?([a-zA-Z_]\w*)?\s?\{(.*)\}\s?([a-zA-Z_]\w*)?\s?;/; + (my $enum_subs = $3) =~ s/\s//g; + my @enum_subs = split(/,/, $enum_subs); + my $enum_val = -1; + foreach my $enum (@enum_subs) { + my ($enum_name, $enum_value) = $enum =~ /^([a-zA-Z_]\w*)(=.+)?$/; $enum_value =~ s/^=//; $enum_val = (length($enum_value) ? $enum_value : $enum_val + 1); if ($opt_h) { @@ -269,12 +274,13 @@ while (defined ($file = next_file())) { } print OUT "1;\n"; - $is_converted{$file} = 1; + $Is_converted{$file} = 1; queue_includes_from($file) if ($opt_a); } exit $Exit; + sub reindent($) { my($text) = shift; $text =~ s/\n/\n /g; @@ -282,9 +288,11 @@ sub reindent($) { $text; } + sub expr { + my $joined_args; if(keys(%curargs)) { - my($joined_args) = join('|', keys(%curargs)); + $joined_args = join('|', keys(%curargs)); } while ($_ ne '') { s/^\&\&// && do { $new .= " &&"; next;}; # handle && operator @@ -336,7 +344,7 @@ sub expr { }; # struct/union member, including arrays: s/^([_A-Z]\w*(\[[^\]]+\])?((\.|->)[_A-Z]\w*(\[[^\]]+\])?)+)//i && do { - $id = $1; + my $id = $1; $id =~ s/(\.|(->))([^\.\-]*)/->\{$3\}/g; $id =~ s/\b([^\$])($joined_args)/$1\$$2/g if length($joined_args); while($id =~ /\[\s*([^\$\&\d\]]+)\]/) { @@ -352,7 +360,7 @@ sub expr { $new .= " (\$$id)"; }; s/^([_a-zA-Z]\w*)// && do { - $id = $1; + my $id = $1; if ($id eq 'struct') { s/^\s+(\w+)//; $id .= ' ' . $1; @@ -366,8 +374,8 @@ sub expr { $new .= '->' if /^[\[\{]/; } elsif ($id eq 'defined') { $new .= 'defined'; - } elsif (/^\(/) { - s/^\((\w),/("$1",/ if $id =~ /^_IO[WR]*$/i; # cheat + } elsif (/^\s*\(/) { + s/^\s*\((\w),/("$1",/ if $id =~ /^_IO[WR]*$/i; # cheat $new .= " &$id"; } elsif ($isatype{$id}) { if ($new =~ /{\s*$/) { @@ -394,6 +402,66 @@ sub expr { } +sub next_line +{ + my ($in, $out); + my $pre_sub_tri_graphs = 1; + + READ: while (not eof IN) { + $in .= ; + chomp $in; + next unless length $in; + + while (length $in) { + if ($pre_sub_tri_graphs) { + # Preprocess all tri-graphs + # including things stuck in quoted string constants. + $in =~ s/\?\?=/#/g; # | ??=| #| + $in =~ s/\?\?\!/|/g; # | ??!| || + $in =~ s/\?\?'/^/g; # | ??'| ^| + $in =~ s/\?\?\(/[/g; # | ??(| [| + $in =~ s/\?\?\)/]/g; # | ??)| ]| + $in =~ s/\?\?\-/~/g; # | ??-| ~| + $in =~ s/\?\?\//\\/g; # | ??/| \| + $in =~ s/\?\?/}/g; # | ??>| }| + } + if ($in =~ s/\\$//) { # \-newline + $out .= ' '; + next READ; + } elsif ($in =~ s/^([^"'\\\/]+)//) { # Passthrough + $out .= $1; + } elsif ($in =~ s/^(\\.)//) { # \... + $out .= $1; + } elsif ($in =~ s/^('(\\.|[^'\\])*')//) { # '... + $out .= $1; + } elsif ($in =~ s/^("(\\.|[^"\\])*")//) { # "... + $out .= $1; + } elsif ($in =~ s/^\/\/.*//) { # //... + last READ; + } elsif ($in =~ m/^\/\*/) { # /*... + # C comment removal adapted from perlfaq6: + if ($in =~ s/^\/\*[^*]*\*+([^\/*][^*]*\*+)*\///) { + $out .= ' '; + } else { # Incomplete /* */ + next READ; + } + } elsif ($in =~ s/^(\/)//) { # /... + $out .= $1; + } elsif ($in =~ s/^([^\'\"\\\/]+)//) { + $out .= $1; + } else { + die "Cannot parse:\n$in\n"; + } + } + + last READ; + } + + return $out; +} + + # Handle recursive subdirectories without getting a grotesquely big stack. # Could this be implemented using File::Find? sub next_file @@ -494,7 +562,7 @@ sub queue_includes_from } if ($line =~ /^#\s*include\s+<(.*?)>/) { - push(@ARGV, $1) unless $is_converted{$1}; + push(@ARGV, $1) unless $Is_converted{$1}; } } close HEADER; @@ -512,6 +580,74 @@ sub inc_dirs } +# Create "_h2ph_pre.ph", if it doesn't exist or was built by a different +# version of h2ph. +sub build_preamble_if_necessary +{ + # Increment $VERSION every time this function is modified: + my $VERSION = 2; + my $preamble = "$Dest_dir/_h2ph_pre.ph"; + + # Can we skip building the preamble file? + if (-r $preamble) { + # Extract version number from first line of preamble: + open PREAMBLE, $preamble or die "Cannot open $preamble: $!"; + my $line = ; + $line =~ /(\b\d+\b)/; + close PREAMBLE or die "Cannot close $preamble: $!"; + + # Don't build preamble if a compatible preamble exists: + return if $1 == $VERSION; + } + + my (%define) = _extract_cc_defines(); + + open PREAMBLE, ">$preamble" or die "Cannot open $preamble: $!"; + print PREAMBLE "# This file was created by h2ph version $VERSION\n"; + + foreach (sort keys %define) { + if ($opt_D) { + print PREAMBLE "# $_=$define{$_}\n"; + } + + if ($define{$_} =~ /^(\d+)U?L{0,2}$/i) { + print PREAMBLE + "unless (defined &$_) { sub $_() { $1 } }\n\n"; + } elsif ($define{$_} =~ /^\w+$/) { + print PREAMBLE + "unless (defined &$_) { sub $_() { &$define{$_} } }\n\n"; + } else { + print PREAMBLE + "unless (defined &$_) { sub $_() { \"", + quotemeta($define{$_}), "\" } }\n\n"; + } + } + close PREAMBLE or die "Cannot close $preamble: $!"; +} + + +# %Config contains information on macros that are pre-defined by the +# system's compiler. We need this information to make the .ph files +# function with perl as the .h files do with cc. +sub _extract_cc_defines +{ + my %define; + my $allsymbols = join " ", + @Config{'ccsymbols', 'cppsymbols', 'cppccsymbols'}; + + # Split compiler pre-definitions into `key=value' pairs: + foreach (split /\s+/, $allsymbols) { + /(.+?)=(.+)/ and $define{$1} = $2; + + if ($opt_D) { + print STDERR "$_: $1 -> $2\n"; + } + } + + return %define; +} + + 1; ############################################################################## @@ -590,6 +726,10 @@ However, the B<.ph> files almost double in size when built using B<-h>. Include the code from the B<.h> file as a comment in the B<.ph> file. This is primarily used for debugging I. +=item -Q + +``Quiet'' mode; don't print out the names of the files being converted. + =back =head1 ENVIRONMENT @@ -626,6 +766,22 @@ that it can translate. It's only intended as a rough tool. You may need to dicker with the files produced. +You have to run this program by hand; it's not run as part of the Perl +installation. + +Doesn't handle complicated expressions built piecemeal, a la: + + enum { + FIRST_VALUE, + SECOND_VALUE, + #ifdef ABC + THIRD_VALUE + #endif + }; + +Doesn't necessarily locate all of your C compiler's internally-defined +symbols. + =cut !NO!SUBS!