home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ftp.rsa.com
/
2014.05.ftp.rsa.com.tar
/
ftp.rsa.com
/
pub
/
dsg
/
public
/
doxygen
/
doxyfilt.pl
next >
Wrap
Perl Script
|
2014-05-02
|
24KB
|
953 lines
#!/usr/local/bin/perl
#
# $Id: doxyfilt.pl,v 1.23 2002/04/15 01:19:41 vhao Exp $
#
#/**
# * @file
# *
# * This file provides the input filter for doxygen to pre-process files into the
# * required format.
# *
# * doxyfilt.pl supports embedded comments in doxygen format in non-C
# * code (asm, perl, shell, bat, vbs).
# *
# * doxyfilt.pl also translates K&R style definitions into ANSI-C definitions
# * in a form suitable for processing by doxygen.
# *
# * @see doxytmpl.pl, doxyfilt.pl.
# *
# */
#
# doxygen requires that the input files be valid ANSI-C (i.e. with
# prototypes) or C++
#
# This filter attempts to fixup K&R-style declarations to appear to
# doxygen to be valid ANSI-C.
#
# Additionally, doxygen's handling of details split across files is
# not flexible enough to allow public header files to be tersely
# documented with additional details in separate files. This filter
# looks for files with the extension 'dox' and automatically includes
# them in the output for 'h' or 'c' files. If there are both 'c' and 'h'
# files that match then only the 'h' file gets the 'dox' file auto-included
#
# The formats below were picked to be as close to the C-style as possible
# without catching existing usage (which happened to often have a * as
# the second column - or a terse format could have been used).
#
# 'asm' files are filtered looking for lines of the format
# ;/*
# ; *
# ; */
#
# 'perl'|'pl','sh','cfg' files are filtered looking for lines of the form
# #/*
# # *
# # */
#
# 'bat' files are filtered looking for lines of the form
# rem /*
# rem *
# rem */
#
# 'vbs' files are filtered looking for lines of the form
# '/*
# ' *
# ' */
#
# 'java' files - don't need anything?
#
use Cwd;
use File::Basename;
$debug=0;
$auto_filetag=0;
$no_macro_functions=0;
# progname = lowercase program name without the .pl removed
$progname = $0;
$progname =~ s/\.pl$//;
$progname = lc $progname;
# we need to know the current directory - as we add that into the
# configuration file
$currentdir=cwd();
$basename=basename($currentdir);
$dirname=dirname($currentdir);
foreach $arg (@ARGV) {
if ($arg =~ /^-d/) {
$debug=1;
} elsif ($arg =~ /^-auto_filetag/) {
$auto_filetag=1;
} elsif ($arg =~ /^-no_macro_functions/) {
$no_macro_functions=1;
} else {
$file_name = $arg;
$file_base = basename($file_name);
$file_type = $file_base;
$file_type =~ s/(.*)\.([^\/\\\.]+)/$2/;
}
}
if ($debug) {
print STDERR "prog: $progname - $pdir - $dirname $basename\n";
print STDERR "file: $file_name $file_base $file_type\n";
}
$mode="generic";
if ($file_type eq "asm") {
$mode="asm"
} elsif ($file_type =~ m/^(c|cpp|h)$/) {
$mode="c";
} elsif ($file_type =~ m/^(pl|pm)$/) {
$mode="perl";
} elsif ($file_type =~ m/^cfg$/) {
$mode="cfg";
} elsif ($file_type =~ m/^(sh|csh)$/) {
$mode="shell";
} elsif ($file_type =~ m/^(bat|cmd)$/) {
$mode="dosbatch";
} elsif ($file_type =~ m/^(txt|doc)$/) {
$mode="text";
} elsif ($file_type =~ m/^(vbs|vb|bas|cls)$/) {
$mode="visualbasic";
}
open(IN,"<$file_name") || &error_exit("unable to open $file_name - $!\n");
if ($mode ne "generic") {
if ($debug) {
print STDERR "$progname:$mode-mode\n" if ($debug);
}
}
# reset the knr2ansi filter - not strictly required
if ($mode eq "c") {
&knr2ansi("",1);
}
if ($mode eq "visualbasic") {
&bas2doxy("",1);
}
#
# note: in order to preserve line numbers cleanly all lines that are
# not extracted from the input file (i.e. don't match a doxygen-like
# format) are output as blank lines
#
$first=1;
#
# check to see if we have an @file tag anywhere in the input and if
# we do then we will not automatically output one
#
$have_file_tag=0;
@input_lines=<IN>;
$have_file_tag=grep(/\@file /,@input_lines);
$lineno=0;
$line_input_count=0;
$line_output_count=0;
foreach (@input_lines) {
$lineno++;
#
# if we do not know what type of file we are dealing with then
# when reading the first line look for the unix #! intepreter
# indicator and figure it out off that
#
if ($first) {
$first=0;
#
# doxygen requires knowing the file to generate the right stuff
# in places - so we provide one always
#
# we may need to adjust this later
#
if (!$have_file_tag && ($mode ne "text")) {
if ($auto_filetag) {
print "/** \@file $file_name */\n";
}
}
if ($mode eq "generic") {
if (/^#!(.*)/) {
$interp=$1;
# remove whitespace
$interp =~ s/[\s\t]//g;
if ($interp =~ m/sh$/) {
$mode="shell";
} elsif ($interp =~ m/perl$/) {
$mode="perl";
}
}
if ($debug) {
print STDERR "$progname:$mode-mode\n" if ($debug);
}
}
}
$line_input_count++;
if ($mode eq "asm") {
# handle semi-colon followed by C-style comments as comments
if (/^;((\/\*| \*).*$)/) {
print "$1\n";
# handle semi-colon followed by equals as C-style code
} elsif (/^;=(.*$)/) {
print "$1\n";
} else {
print "\n";
}
} elsif ($mode eq "c") {
local(@junk,$linecnt);
# knr2ansi conversion
$ret=&knr2ansi($_,0);
# if we have just a blank line then it is a single
# line in the list
if ($ret eq "\n") {
$linecnt=1;
} else {
@junk=split(/\n/,$ret);
if (defined @junk) {
$linecnt=$#junk;
$linecnt++;
} else {
$linecnt=0;
}
}
$line_output_count+=$linecnt;
# if we got any output make sure we catchup in terms of
# number of lines we have written out
if ($linecnt>0) {
local($i,$replace_brace);
#
# if we have prototype\n{\n then we want to insert any extra
# lines we need to preserve the output *before* the brace
#
if ($ret =~ m/^((.*)\n(.*)){\n$/ ) {
$ret=$1;
$replace_brace=1;
} else {
$replace_brace=0;
}
for($i=0;$i<($line_input_count-$line_output_count);$i++) {
$ret="$ret\n";
}
if ($replace_brace) {
$ret="$ret\{\n";
}
$line_output_count=$line_input_count;
}
print $ret;
} elsif (($mode eq "perl" || $mode eq "shell" || $mode eq "cfg")) {
# handle semi-colon followed by C-style comments as comments
if (/^\#((\/\*| \*).*$)/) {
print "$1\n";
# handle semi-colon followed by equals as C-style code
} elsif (/^\#=(.*$)/) {
print "$1\n";
} else {
print "\n";
}
} elsif ($mode eq "dosbatch") {
# handle rem followed by C-style comments as comments
if (/^rem ((\/\*| \*).*$)/) {
print "$1\n";
# handle semi-colon followed by equals as C-style code
} elsif (/^rem =(.*$)/) {
print "$1\n";
} else {
print "\n";
}
} elsif ($mode eq "visualbasic") {
# handle single quote followed by C-style comments as comments
$ret=&bas2doxy($_,0);
print $ret;
} else {
print $_;
}
}
close(IN);
#print STDERR "LINEIN $line_input_count\nLINEOUT $line_output_count\n";
if ($mode eq "c") {
# flush any remaining data - we might not have got a complete
# block to process - so we have to explicitly ask for the remaining
# buffered data to be released
&knr2ansi("",2);
}
if ($mode eq "visualbasic") {
&bas2doxy("",2);
}
exit(0);
#
# knr2ansi - look for ANSI-C prototypes and replace the K&R-style
# implementation with the ANSI-C equivalent
#
# This is a version of the knr2ansi filter that operates a line at
# a time (i.e. input is trickled through it) so that the caller has
# complete control over the origin of the input
#
# Convert possible K&R style function definitions into ANSI function
# definitions
#
# Note: this tool
#
# - doesn't perform any pre-processing which can cause errors, if the
# pre-processor directives are around function definitions.
#
# - doesn't preserve all comments or their exact position in the code
#
# - is mainly used to convert code containing K&R style function
# definitions to ANSI format for use with 'doxygen'
#
sub knr2ansi
{
local($in_line,$reset)=@_;
local($ret)="";
local($line);
local(@prevlines,$pline);
if ($reset eq 1) {
$comment=0;
$doxygen_comment=0;
$doxygen_comment_continuation=0;
$funcname="";
$expect_knr= 0;
$level= 0;
$concatenate=0;
$last_line="";
@params=();
%knrparams=();
@knrpar_names=();
$have_func_count=0;
$doing_recursion=0;
$last_comment="";
}
if ($doxygen_comment_continuation) {
if ($comment_debug) {
print STDERR "PRESTRIP $in_line\n";
}
$in_line =~ s/^[\s\*]*/ /;
if ($comment_debug) {
print STDERR "POSTSTRIP $in_line\n";
}
$doxygen_comment_continuation=0;
}
# prepend the buffered line data from the last call
$in_line="$last_line$in_line";
#
# we must process any buffered information before we handle the
# current line of data - so we call recursively appending the output
# until we have handled all the lines (including the current one)
#
@prevlines=split(/\n/,"$in_line\n");
$last_line="";
if (!$doing_recursion && ($#prevlines>0)) {
$doing_recursion=1;
foreach $pline (@prevlines) {
$ret.=&knr2ansi("$pline\n",$reset);
}
$doing_recursion=0;
return($ret);
}
# if we are before or after the normal line processing then we flush
# whatever data we have buffered
if ($reset ne 0) {
return "$in_line$last_line";
}
if ($debug) {
printf STDERR "in_line=$in_line";
}
#
# we also have some C++ style (//) comments in our code base
# we just extract them out at the moment
#
if ($in_line =~ m/\/\//xg) {
$in_line=substr($in_line,0,index($in_line,"//"));
}
#
# blank lines can be returned immediately
#
if ($in_line eq "") {
$ret=$in_line;
return($ret);
}
$line=$in_line;
if ($debug) {
printf STDERR "level=$level, line=$line\n";
}
#
# check for line continuation character at the end
#
$linecont= 0;
if ($line =~ m/\\\s*$/xg) {
$last_line.=$line;
$linecont=1;
return($ret);
}
#
# if we are in the middle of a comment block then we simply gather
# output until we hit the closing comment and then return the data
# as is - but leave the remainder of the line following the termination
# of the comment for processing.
#
if ($comment) {
if($line=~m/\*\//) {
local($cdata,$pos);
if ($comment_debug) {
print STDERR "COMMENT END $line\n";
}
$pos=rindex($line, "*/");
$cdata=substr($line, 0, $pos+2);
$ret.=$cdata;
if ($doxygen_comment) {
$last_comment.=$cdata;
}
$line=substr($line, $pos + 2);
$comment=0;
$doxygen_comment=0;
} else {
if ($doxygen_comment) {
if ($line =~ m/\@fn\s+/) {
if ($line =~ /\)[;\s]*$/) {
if ($comment_debug) {
print STDERR "OKAY COMMENT $line\n";
}
} else {
if ($comment_debug) {
print STDERR "NOT OKAY COMMENT $line\n";
}
$doxygen_comment_continuation=1;
$line =~ s/\n$//;
$last_line.=$line;
$linecont=1;
return($ret);
}
}
$last_comment.=$line;
}
$ret.=$line;
return($ret);
}
}
# if we have a comment block starting ...
if ($line=~m/\/\*/) {
$comment=1;
if ($line=~m/\/\*\*/) {
if ($comment_debug) {
print STDERR "DOXYGEN COMMENT START - $line\n";
}
$doxygen_comment=1;
}
if ($doxygen_comment) {
$last_comment=$line;
}
}
# if we have a comment block finishing ...
if($line=~m/\*\//) {
if ($comment_debug) {
print STDERR "COMMENT END $line\n";
}
$comment=0;
}
# if we have a #define statement we check to see if the last comment
# block contained a @fn line and if so then we output the @fn line
# and *not* the #define so things will be documented as though it
# was a function
if (!$no_macro_functions) {
if ($line =~ m/^\s*\#define\s+/) {
#
#
#
if ($comment_debug) {
print STDERR "HAVE MACRO USAGE - $line\n";
print STDERR "LAST COMMENT IS - $last_comment\n";
}
if ($last_comment =~ m/.*\@fn\s+(.*)\n.*/) {
local($prototype)=$1;
if ($comment_debug) {
print STDERR "COMMENT BLOCK - proto $prototype\n$last_comment";
}
if (!($prototype =~ m/;/)) {
$prototype="$prototype;";
}
$ret.="$prototype\n";
return($ret);
}
}
}
#
# If we see the start of a function body then we know at
# this point that we have gathered enough information that
# we can output an ANSI-style defn
#
if ($line=~m/{/) {
# we are entering a function body now
$level+=count_char($line, "{")- count_char($line, "}");
$expect_knr= 0;
# if we just parsed a function definition then output it
if ($funcname && $level == 1) {
output_func:
$have_func_count--;
if ($debug) {
print STDERR "FUNC $funcname at level=1\n";
print STDERR print_array("params", ",");
print STDERR "\n";
print STDERR print_array("knrparams", ",");
print STDERR "\n";
}
#
# if there are no knr style declarations, just output the
# definition as is.
#
if (scalar(@knrparams) == 0) {
# there were no k&r stype parameter definitions
# output the buffered function definition
$ret.="$func_def$line";
$funcname= "";
@params=();
@knrparams=();
@knrpar_names=();
if ($debug) {
print STDERR "RETURN as-is\n";
}
return($ret);
}
#
# if the number of parameters matches,
# replace the ones in the parameter list, by the
# K&R declarations.
#
if (scalar(@knrparams) == scalar(@params)) {
$idx1= index($func_def, "(");
$idx2= rindex($func_def, ")");
$new_func_def= substr($func_def, 0, $idx1+1);
for ($kp= 0; $kp < scalar(@knrparams); $kp+=1) {
$new_func_def.= @knrparams[$kp];
$new_func_def.= "," if ($kp < (scalar(@knrparams)-1));
}
$new_func_def.= substr($func_def, $idx2);
$ret.=$new_func_def;
} else {
local($errmsg);
$errmsg="FUNC $funcname at level=1\n";
$errmsg.=print_array("params", ",");
$errmsg.="\n";
$errmsg.=print_array("knrparams", ",");
$errmsg.="\n";
&error_exit("$file_name:$lineno: Mismatched number of parameter definitions and parameter list. $expect_knr - " . scalar(@knrparams) . " != " . scalar(@params) . "\n$line\n$errmsg");
}
$funcname= "";
@params=();
@knrparams=();
@knrpar_names=();
}
$ret.=$line;
if ($debug) {
print STDERR "RETURN FUNC LINE $ret\n";
}
return($ret);
}
#
# When we hit a a closing brace we decrease our block level
#
if ($line=~m/}/) {
$level-=1;
$ret.=$line;
return($ret);
}
#if we are outside a function and not inside a comment
if($level==0 && !$comment) {
# strip out comments ( these can only be one liners if we got here)
$stripped_line= $line;
$stripped_line=~s/\/\*.*\*\///g;
if ($stripped_line =~ m/^\s*\#/xg) {
#
# if we have a function and have gathered enough parameters
# to fixup the knr form then we do that now
#
if ($have_func_count == 1) {
if (scalar(@knrparams) == scalar(@params)) {
print STDERR "PREPROC: current line->lastline $line\n" if ($debug);
$last_line.="$line";
$line="";
$expect_knr=0;
$concatenate=0;
goto output_func;
}
}
# this looks like a pre-processor statement
# spit it out!
$ret.=$line;
return($ret);
}
if ($debug) {
printf STDERR "stripped_line=$stripped_line\n";
}
if (!$expect_knr && ($stripped_line =~ m/((?:\*\s*)*\w+)\s*\(/xg) ) {
# check for func_name(
# this looks like a function declaration
# check if its complete
my $paralevel= count_char($stripped_line, "(")- count_char($stripped_line, ")");
printf STDERR "paralevel= $paralevel\n" if ($debug);
if ($paralevel==0) {
printf STDERR "complete definition:\n%s",$stripped_line if ($debug);
$have_func_count++;
# if we have an assignment statement then we are not
# interested in this line from the point of view of
# gathering details about parameters
if ($stripped_line =~ /.*=.*/) {
$ret.=$line;
return($ret);
}
if ( $stripped_line =~ m/\)\s*\;/xg ||
($stripped_line =~ m/\(\s*\*\s*\w+\s*\)\s*\(/xg)) {
$have_func_count--;
# check for (*)()
# this appears to be a prototype
$expect_knr= 0;
$concatenate=0;
$ret.=$line;
if ($debug) {
print STDERR "ALREADY prototyped\n";
}
return($ret);
} else {
if ($have_func_count == 2) {
$have_func_count--;
print STDERR "current line->lastline $line\n" if($debug);
$last_line.="$line";
$line="";
$expect_knr=0;
$concatenate=0;
goto output_func;
}
#printf STDERR "defn single $have_func_count $level:\n%s",$stripped_line;
}
#
# now we can process the entire definition
#
$funcname= $1;
@params= $stripped_line=~ m/(\w+)\s*[\,)]/xg;
if ($debug) {
print STDERR print_array ("params", " ");
printf STDERR "\n";
}
$func_def= $line;
$expect_knr= 1;
$concatenate=0;
} else {
if ($paralevel < 0) {
# this is an error ( more closing than opening paras
&error_exit("$file_name:$lineno: Error parsing function declaration or definition");
}
$concatenate=1;
#
# remove the trailing newline - we will join the next line to
# this line and see if it makes more sense
#
$line=~s/\n$//;
$last_line.=$line;
}
return($ret);
}
if ($expect_knr) {
printf STDERR "\nhandling possible K&Rs: $stripped_line\n" if ($debug);
# this could be K&r style param definitions
# isolate the parameters (followed either by a , or ;)
@pardefs= $stripped_line =~ m/((?:\*\s*)*\(\s*\*\s*\w+\s*\)\s*\(.*\))\s*[,;]/xg;
@parnames= $stripped_line =~ m/(?:\*\s*)*\(\s*(\*\s*\w+)\s*\)\s*\(.*\)\s*[,;]/xg;
if (scalar(@pardefs) == 0 ) {
@pardefs= $stripped_line =~ m/((?:\*\s*)*\w+)\s*(?:\[\s*\])*\s*[,;]/xg;
if (scalar(@pardefs) ==0) {
# we didn't get what we expected, redo;
# so try again (maybe a #if ... #else ...#endif
$expect_knr= 0;
$ret.=$stripped_line;
return($ret);
}
@parnames= @pardefs;
}
if ($debug) {
printf STDERR print_array ("pardefs", " ");
printf STDERR "\n";
}
@words= $stripped_line =~ m/((?:\*\s*)*\w+)/xg;
if ($debug) {
print STDERR print_array ("words", " ");
printf STDERR "\n";
print STDERR print_array ("parnames", " ");
printf STDERR "\n";
}
$past_type= 0;
$type= "";
# find the type
foreach $w (@words) {
if ($w eq @parnames[0]) {
$past_type= 1;
} else {
$type.=$w." " if (!$past_type);
}
}
chop($type);
printf STDERR "type= $type\n" if ($debug);
for ($l= 0; $l < @pardefs; $l+=1) {
$found= 0;
for ($j= 0; $j < scalar(@knrpar_names); $j++) {
printf STDERR "@knrpar_names[$j] =? @parnames[$l]\n" if ($debug);
if (@knrpar_names[$j] eq @parnames[$l]) {
$found= 1;
}
}
if ($found == 0) {
push(@knrpar_names, @parnames[$l]);
push(@knrparams, $type . " " . @pardefs[$l]);
printf STDERR "\nparam:".$type." ".@pardefs[$l]."\n" if ($debug);
} else {
printf STDERR "\nduplicate param:".$type." ".@pardefs[$l]."\n" if ($debug);
}
}
return($ret);
} else {
# if (!$concatenate) {
$ret.=$line;
return($ret);
# }
}
} else {
$ret.=$line;
return($ret);
}
}
sub print_array
{
my ($name, $sep, $dont_print_array_name)= @_;
my $ref= \$name;
my @array= ();
my $ne;
my $pa;
my $ret;
$ret="";
@array= @$$ref;
$ne=scalar(@array);
$ret.="$name:" if (!$dont_print_array_name);
for ($pa = 0; $pa < $ne; $pa+=1) {
$ret.=@array[$pa];
$ret.=$sep if $pa< $ne-1;
}
return($ret);
}
sub count_char
{
my ($s,$c)= @_;
my $count= 0;
my $idx= -1;
while (($idx= index($s, $c, $idx)) > -1) {
$count++; $idx++;
}
return $count;
}
sub separate_type_params
{
my ($funcname, @elements)= @_;
my $past_func_name= 0;
#my (@params,@types);
my $e;
@params=();
@types=();
foreach $e (@elements) {
if (($e eq $funcname) || ("*".$e eq $funcname)) {
$past_func_name= 1;
} else {
push(@params, $e) if ($past_func_name);
push(@types, $e) if (!$past_func_name);
}
}
return;
}
#
# bas2doxy - convert basic style functions into 'doxygen' format
#
sub bas2doxy {
local($in_line,$reset)=@_;
local($ret)="";
local($line);
local(@prevlines);
if ($reset eq 1) {
$comment=0;
$funcname="";
$level=0;
$concatenate=0;
$last_line="";
@params=();
}
# prepend the buffered line data from the last call
$in_line="$last_line$in_line";
#
# we must process any buffered information before we handle the
# current line of data - so we call recursively appending the output
# until we have handled all the lines (including the current one)
#
@prevlines=split(/\n/,"$in_line\n");
$last_line="";
if (!$doing_recursion && ($#prevlines>0)) {
$doing_recursion=1;
foreach $pline (@prevlines) {
$ret.=&bas2doxy("$pline\n",$reset);
}
$doing_recursion=0;
return($ret);
}
# if we are before or after the normal line processing then we flush
# whatever data we have buffered
if ($reset ne 0) {
return "$in_line$last_line";
}
if ($debug) {
printf STDERR "in_line=$in_line";
}
$line=$in_line;
if ($debug) {
printf STDERR "level=$level, line=$line\n";
}
#
# check for line continuation character at the end
#
$linecont=0;
if ($line =~ m/_\s*$/xg) {
#print STDERR "CONT: $line\n";
$last_line.=$line;
$linecont=1;
return($ret);
}
# handle embedded doxygen comments
if ($line =~ /^\'((\/\*| \*).*$)/) {
##print STDERR "MATCH1: $in_line => $1\n";
$ret.="$1\n";
# handle single quote followed by equals as C-style code
} elsif ($line =~ /^\'=(.*$)/) {
##print STDERR "MATCH2: $in_line => $1\n";
$ret.="$1\n";
} elsif ($line =~ /^(Public|Private|)\s*(Sub|Function)\s*(.*)/ ) {
local($pre,$scope,$type,$rest,$ftype);
$scope=$1; $type=$2; $rest=$3;
if ($scope eq "Private") {
$pre="static ";
} else {
$pre="";
}
#print STDERR "MATCHSUB: $in_line => $1:$2:$3\n";
if ($type eq "Sub") {
$ret.="void $rest;\n";
} elsif ($type eq "Function") {
if ($rest =~ /^((.*)(\([^\)]*\)))\s+As\s+(.*)/) {
$ftype="$4";
$rest=$1;
} else {
$ftype="int";
}
$ret.="$pre$ftype $rest;\n";
} else {
$ret.="$line\n";
}
} else {
$ret.="\n";
}
return($ret);
}
sub error_exit {
local($message)=@_;
die <<EOF;
****************************************************************************
* doxytmpl filter aborting - the output file will be invalid or incomplete *
****************************************************************************
$message
****************************************************************************
EOF
}