#!perl # # mkinc [ -Ipath1;path2;path3 ...] [ -1output1 ] [ -2output2 ] header.h... # # Looks for header.h, extracts all structures # from said header file, generates output1 and output2. # # Multiple headers can be supplied on the command line; they are all # combined to form a single output file. # # If the -I option is specified, it serves as a search path if the header # file cannot be found in the current directory. The search path is used # only for relative paths. # # If the -1 and -2 options are specified, they specify the targets # for the two include files. output1 includes all the headers and # output2 includes all the structures. output1 defaults to NUL and # output2 defaults to STDOUT. # Path helper functions sub PathIsAbsolute { my $path = shift; $path =~ /^\\\\/ || $path =~ /^[a-zA-Z]:\\/; } sub PathIsRelative { !PathIsAbsolute(shift); } sub PathCombine { my ($dir, $file) = @_; $dir =~ /\\$/ ? "$dir$file" : "$dir\\$file"; } # Unsubstitute %_NTBINDIR% if it appears at the start of the path. # This keeps output independent of the user's enlistment. sub PathPrettify { my $path = shift; my $bindir = $ENV{"_NTBINDIR"} . "\\"; if (uc substr($path, 0, length($bindir)) eq uc $bindir) { "%_NTBINDIR%\\" . substr($path, length($bindir)); } else { $path; } } sub PathEscapePath { my $path = shift; $path =~ s/\\/\\\\/g; $path; } @INC = (); # Add a semicolon-separated list to the INC list sub AddInc { for (split(/\s*;\s*/, shift)) { push(@INC, $_) if $_; } } # Open a file, searching on the path; returns the opened filename sub Open { my $file = shift; # See if it is fully qualified already if (open(I, $file)) { return $file; } # Don't look on path if it's already absolute return undef if PathIsAbsolute($file); my $dir; for $dir (@INC) { $path = PathCombine($dir, $file); if (open(I, $path)) { return $path; } } return undef; } sub SyntaxError { my ($file, $structname) = @_; print O2 "#error $file($.): wacky struct $structname syntax; cannot parse\n"; print STDOUT "$file($.) : error X0000: wacky struct $structname syntax; cannot parse\n"; } # ProcessFile - The file has been opened as sub ProcessFile { my $header = shift; my $structname, $endstruct; line: while () { chomp; s/\/\*.*?\*\///g; # Kill one-line C comment s/\/\/.*//; # Kill C++ comment s/\s*$//; # Kill trailing whitespace next unless /^\s*typedef\s+struct\s+/; next if /;/; # Forward declaration; ignore $tag = $'; if ($tag =~ /^(\w+)/) { $structname = $1; next line if $structname =~ /Vtbl$/; # Ignore MIDL Vtbls } else { $structname = ""; } # Look for the closing brace of the struct # The closing brace must be at the same indent level as the # line containing the opening brace if (!/\{/) { $_ = ; if (!/^\s*\{/) { SyntaxError($header, $structname); next line; } } /^(\s*)/; $endstruct = $1 . "}"; while (($_ = ) && substr($_, 0, length($endstruct)) ne $endstruct) { } if ($_ =~ /\}\s*(\w+)\s*[,;]/) { print O2 "_($1) /* line $. */\n"; } elsif ($_ eq $endstruct . "\n") { $_ = ; if ($_ =~ /^(\w+)\s*[,;]/) { print O2 "_($1) /* line $. */\n"; } else { SyntaxError($header, $structname); } } else { SyntaxError($header, $structname); } } } open(O2, ">&STDOUT"); while ($file = shift) { if ($file =~ /^-I/) { # a -I directive AddInc($'); } elsif ($file =~ /^-1/) { open(O1, ">$'") || die "Cannot create $'"; } elsif ($file =~ /^-2/) { open(O2, ">$'") || die "Cannot create $'"; } else { # a file if ($path = Open($file)) { print O1 "#include <$path>\n"; $pretty = PathPrettify($path); print O2 "// $pretty\n"; print O2 "#define H \"", PathEscapePath($pretty), "\"\n"; ProcessFile($path); print O2 "#undef H\n"; close(I); } else { print O2 "#error Cannot find file $file\n"; } } }