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 >
Perl Script  |  2014-05-02  |  24KB  |  953 lines

  1. #!/usr/local/bin/perl
  2. #
  3. # $Id: doxyfilt.pl,v 1.23 2002/04/15 01:19:41 vhao Exp $
  4. #
  5. #/**
  6. # * @file
  7. # *
  8. # * This file provides the input filter for doxygen to pre-process files into the
  9. # * required format.
  10. # *
  11. # * doxyfilt.pl supports embedded comments in doxygen format in non-C
  12. # * code (asm, perl, shell, bat, vbs).
  13. # *
  14. # * doxyfilt.pl also translates K&R style definitions into ANSI-C definitions
  15. # * in a form suitable for processing by doxygen.
  16. # *
  17. # * @see doxytmpl.pl, doxyfilt.pl.
  18. # *
  19. # */
  20. #
  21. # doxygen requires that the input files be valid ANSI-C (i.e. with
  22. # prototypes) or C++
  23. #
  24. # This filter attempts to fixup K&R-style declarations to appear to
  25. # doxygen to be valid ANSI-C.
  26. #
  27. # Additionally, doxygen's handling of details split across files is
  28. # not flexible enough to allow public header files to be tersely
  29. # documented with additional details in separate files. This filter
  30. # looks for files with the extension 'dox' and automatically includes
  31. # them in the output for 'h' or 'c' files. If there are both 'c' and 'h'
  32. # files that match then only the 'h' file gets the 'dox' file auto-included
  33. #
  34. # The formats below were picked to be as close to the C-style as possible
  35. # without catching existing usage (which happened to often have a * as
  36. # the second column - or a terse format could have been used).
  37. #
  38. # 'asm' files are filtered looking for lines of the format
  39. # ;/*
  40. # ; *
  41. # ; */
  42. #
  43. # 'perl'|'pl','sh','cfg' files are filtered looking for lines of the form
  44. # #/*
  45. # # *
  46. # # */
  47. #
  48. # 'bat' files are filtered looking for lines of the form
  49. # rem /*
  50. # rem  *
  51. # rem  */
  52. #
  53. # 'vbs' files are filtered looking for lines of the form
  54. # '/*
  55. # ' *
  56. # ' */
  57. #
  58. # 'java' files - don't need anything?
  59. #
  60.  
  61. use Cwd;
  62. use File::Basename;
  63.  
  64. $debug=0;
  65.  
  66. $auto_filetag=0;
  67. $no_macro_functions=0;
  68.  
  69. # progname = lowercase program name without the .pl removed
  70. $progname = $0;
  71. $progname =~ s/\.pl$//;
  72. $progname = lc $progname;
  73.  
  74. # we need to know the current directory - as we add that into the
  75. # configuration file
  76. $currentdir=cwd();
  77. $basename=basename($currentdir);
  78. $dirname=dirname($currentdir);
  79.  
  80. foreach $arg (@ARGV) {
  81.   if ($arg =~ /^-d/) {
  82.     $debug=1;
  83.   } elsif ($arg =~ /^-auto_filetag/) {
  84.     $auto_filetag=1;
  85.   } elsif ($arg =~ /^-no_macro_functions/) {
  86.     $no_macro_functions=1;
  87.   } else {
  88.     $file_name = $arg;
  89.     $file_base = basename($file_name);
  90.     $file_type = $file_base;
  91.     $file_type =~ s/(.*)\.([^\/\\\.]+)/$2/;
  92.   }
  93. }
  94.  
  95. if ($debug) {
  96.   print STDERR "prog: $progname  - $pdir - $dirname $basename\n";
  97.   print STDERR "file: $file_name $file_base $file_type\n";
  98. }
  99.  
  100. $mode="generic";
  101. if ($file_type eq "asm") {
  102.   $mode="asm"
  103. } elsif ($file_type =~ m/^(c|cpp|h)$/) {
  104.   $mode="c";
  105. } elsif ($file_type =~ m/^(pl|pm)$/) {
  106.   $mode="perl";
  107. } elsif ($file_type =~ m/^cfg$/) {
  108.   $mode="cfg";
  109. } elsif ($file_type =~ m/^(sh|csh)$/) {
  110.   $mode="shell";
  111. } elsif ($file_type =~ m/^(bat|cmd)$/) {
  112.   $mode="dosbatch";
  113. } elsif ($file_type =~ m/^(txt|doc)$/) {
  114.   $mode="text";
  115. } elsif ($file_type =~ m/^(vbs|vb|bas|cls)$/) {
  116.   $mode="visualbasic";
  117. }
  118.  
  119. open(IN,"<$file_name") || &error_exit("unable to open $file_name - $!\n");
  120. if ($mode ne "generic") {
  121.   if ($debug) {
  122.     print STDERR "$progname:$mode-mode\n" if ($debug);
  123.   }
  124. }
  125.  
  126. # reset the knr2ansi filter - not strictly required
  127. if ($mode eq "c") {
  128.   &knr2ansi("",1);
  129. }
  130.  
  131. if ($mode eq "visualbasic") {
  132.   &bas2doxy("",1);
  133. }
  134.  
  135. #
  136. # note: in order to preserve line numbers cleanly all lines that are
  137. #       not extracted from the input file (i.e. don't match a doxygen-like
  138. #       format) are output as blank lines
  139. #
  140.  
  141. $first=1;
  142.  
  143. #
  144. # check to see if we have an @file tag anywhere in the input and if
  145. # we do then we will not automatically output one
  146. #
  147. $have_file_tag=0;
  148. @input_lines=<IN>;
  149. $have_file_tag=grep(/\@file /,@input_lines);
  150. $lineno=0;
  151.  
  152. $line_input_count=0;
  153. $line_output_count=0;
  154.  
  155. foreach (@input_lines) {
  156.   $lineno++;
  157.   #
  158.   # if we do not know what type of file we are dealing with then
  159.   # when reading the first line look for the unix #! intepreter
  160.   # indicator and figure it out off that
  161.   #
  162.   if ($first) {
  163.     $first=0;
  164.     #
  165.     # doxygen requires knowing the file to generate the right stuff
  166.     # in places - so we provide one always
  167.     #
  168.     # we may need to adjust this later
  169.     #
  170.     if (!$have_file_tag && ($mode ne "text")) {
  171.       if ($auto_filetag) {
  172.         print "/** \@file $file_name */\n";
  173.       }
  174.     }
  175.     if ($mode eq "generic") {
  176.       if (/^#!(.*)/) {
  177.     $interp=$1;
  178.     # remove whitespace
  179.     $interp =~ s/[\s\t]//g;
  180.     if ($interp =~ m/sh$/) {
  181.       $mode="shell";
  182.     } elsif ($interp =~ m/perl$/) {
  183.       $mode="perl";
  184.     }
  185.       }
  186.       if ($debug) {
  187.       print STDERR "$progname:$mode-mode\n" if ($debug);
  188.       }
  189.     }
  190.   }
  191.  
  192.   $line_input_count++;
  193.  
  194.   if ($mode eq "asm") {
  195.     # handle semi-colon followed by C-style comments as comments
  196.     if (/^;((\/\*| \*).*$)/) {
  197.       print "$1\n";
  198.     # handle semi-colon followed by equals as C-style code
  199.     } elsif (/^;=(.*$)/) {
  200.       print "$1\n";
  201.     } else {
  202.       print "\n";
  203.     }
  204.   } elsif ($mode eq "c") {
  205.     local(@junk,$linecnt);
  206.  
  207.     # knr2ansi conversion
  208.     $ret=&knr2ansi($_,0);
  209.  
  210.     # if we have just a blank line then it is a single
  211.     # line in the list
  212.     if ($ret eq "\n") {
  213.       $linecnt=1;
  214.     } else {
  215.       @junk=split(/\n/,$ret);
  216.       if (defined @junk) {
  217.         $linecnt=$#junk;
  218.         $linecnt++;
  219.       } else {
  220.         $linecnt=0;
  221.       }
  222.     }
  223.  
  224.     $line_output_count+=$linecnt;
  225.  
  226.     # if we got any output make sure we catchup in terms of
  227.     # number of lines we have written out
  228.     if ($linecnt>0) {
  229.       local($i,$replace_brace);
  230.  
  231.       #
  232.       # if we have prototype\n{\n then we want to insert any extra
  233.       # lines we need to preserve the output *before* the brace
  234.       #
  235.       if ($ret =~ m/^((.*)\n(.*)){\n$/ ) {
  236.     $ret=$1;
  237.     $replace_brace=1;
  238.       } else {
  239.     $replace_brace=0;
  240.       }
  241.  
  242.       for($i=0;$i<($line_input_count-$line_output_count);$i++) {
  243.     $ret="$ret\n";
  244.       }
  245.  
  246.       if ($replace_brace) {
  247.     $ret="$ret\{\n";
  248.       }
  249.  
  250.       $line_output_count=$line_input_count;
  251.     }
  252.  
  253.     print $ret;
  254.   } elsif (($mode eq "perl" || $mode eq "shell" || $mode eq "cfg")) {
  255.     # handle semi-colon followed by C-style comments as comments
  256.     if (/^\#((\/\*| \*).*$)/) {
  257.       print "$1\n";
  258.     # handle semi-colon followed by equals as C-style code
  259.     } elsif (/^\#=(.*$)/) {
  260.       print "$1\n";
  261.     } else {
  262.       print "\n";
  263.     }
  264.   } elsif ($mode eq "dosbatch") {
  265.     # handle rem followed by C-style comments as comments
  266.     if (/^rem ((\/\*| \*).*$)/) {
  267.       print "$1\n";
  268.     # handle semi-colon followed by equals as C-style code
  269.     } elsif (/^rem =(.*$)/) {
  270.       print "$1\n";
  271.     } else {
  272.       print "\n";
  273.     }
  274.   } elsif ($mode eq "visualbasic") {
  275.     # handle single quote followed by C-style comments as comments
  276.     $ret=&bas2doxy($_,0);
  277.     print $ret;
  278.   } else {
  279.     print $_;
  280.   }
  281.  
  282. }
  283. close(IN);
  284.  
  285. #print STDERR "LINEIN $line_input_count\nLINEOUT $line_output_count\n";
  286.  
  287. if ($mode eq "c") {
  288.   # flush any remaining data - we might not have got a complete
  289.   # block to process - so we have to explicitly ask for the remaining
  290.   # buffered data to be released
  291.   &knr2ansi("",2);
  292. }
  293.  
  294. if ($mode eq "visualbasic") {
  295.   &bas2doxy("",2);
  296. }
  297.  
  298. exit(0);
  299.  
  300. #
  301. # knr2ansi - look for ANSI-C prototypes and replace the K&R-style
  302. #            implementation with the ANSI-C equivalent
  303. #
  304. # This is a version of the knr2ansi filter that operates a line at
  305. # a time (i.e. input is trickled through it) so that the caller has
  306. # complete control over the origin of the input
  307. #
  308. # Convert possible K&R style function definitions into ANSI function
  309. # definitions
  310. #
  311. # Note: this tool
  312. #
  313. # - doesn't perform any pre-processing which can cause errors, if the
  314. #   pre-processor directives are around function definitions.
  315. #
  316. # - doesn't preserve all comments or their exact position in the code
  317. #
  318. # - is mainly used to convert code containing K&R style function
  319. #   definitions to ANSI format for use with 'doxygen'
  320. #
  321.  
  322. sub knr2ansi
  323. {
  324.   local($in_line,$reset)=@_;
  325.   local($ret)="";
  326.   local($line);
  327.   local(@prevlines,$pline);
  328.  
  329.   if ($reset eq 1) {
  330.     $comment=0;
  331.     $doxygen_comment=0;
  332.     $doxygen_comment_continuation=0;
  333.     $funcname="";
  334.     $expect_knr= 0;
  335.     $level= 0;
  336.     $concatenate=0;
  337.     $last_line="";
  338.  
  339.     @params=();
  340.     %knrparams=();
  341.     @knrpar_names=();
  342.     $have_func_count=0;
  343.     $doing_recursion=0;
  344.     $last_comment="";
  345.   }
  346.  
  347.   if ($doxygen_comment_continuation) {
  348.     if ($comment_debug) {
  349.       print STDERR "PRESTRIP $in_line\n";
  350.     }
  351.     $in_line =~ s/^[\s\*]*/ /;
  352.     if ($comment_debug) {
  353.       print STDERR "POSTSTRIP $in_line\n";
  354.     }
  355.     $doxygen_comment_continuation=0;
  356.   }
  357.  
  358.   # prepend the buffered line data from the last call
  359.   $in_line="$last_line$in_line";
  360.  
  361.   #
  362.   # we must process any buffered information before we handle the
  363.   # current line of data - so we call recursively appending the output
  364.   # until we have handled all the lines (including the current one)
  365.   #
  366.   @prevlines=split(/\n/,"$in_line\n");
  367.   $last_line="";
  368.   if (!$doing_recursion && ($#prevlines>0)) {
  369.     $doing_recursion=1;
  370.     foreach $pline (@prevlines) {
  371.       $ret.=&knr2ansi("$pline\n",$reset);
  372.     }
  373.     $doing_recursion=0;
  374.     return($ret);
  375.   }
  376.  
  377.   # if we are before or after the normal line processing then we flush
  378.   # whatever data we have buffered
  379.   if ($reset ne 0) {
  380.     return "$in_line$last_line";
  381.   }
  382.  
  383.   if ($debug) {
  384.     printf STDERR "in_line=$in_line";
  385.   }
  386.  
  387.   #
  388.   # we also have some C++ style (//) comments in our code base
  389.   # we just extract them out at the moment
  390.   #
  391.   if ($in_line =~ m/\/\//xg) {
  392.     $in_line=substr($in_line,0,index($in_line,"//"));
  393.   }
  394.  
  395.   #
  396.   # blank lines can be returned immediately
  397.   #
  398.   if ($in_line eq "") {
  399.     $ret=$in_line;
  400.     return($ret);
  401.   }
  402.   $line=$in_line;
  403.   if ($debug) {
  404.     printf STDERR "level=$level, line=$line\n";
  405.   }
  406.  
  407.   #
  408.   # check for line continuation character at the end
  409.   #
  410.   $linecont= 0;
  411.   if ($line =~ m/\\\s*$/xg) {
  412.     $last_line.=$line;
  413.     $linecont=1;
  414.     return($ret);
  415.   }
  416.  
  417.   #
  418.   # if we are in the middle of a comment block then we simply gather
  419.   # output until we hit the closing comment and then return the data
  420.   # as is - but leave the remainder of the line following the termination
  421.   # of the comment for processing.
  422.   #
  423.   if ($comment) {
  424.     if($line=~m/\*\//) {
  425.       local($cdata,$pos);
  426.  
  427.       if ($comment_debug) {
  428.         print STDERR "COMMENT END $line\n";
  429.       }
  430.       $pos=rindex($line, "*/");
  431.       $cdata=substr($line, 0, $pos+2);
  432.       $ret.=$cdata;
  433.       if ($doxygen_comment) {
  434.         $last_comment.=$cdata;
  435.       }
  436.       $line=substr($line, $pos + 2);
  437.       $comment=0;
  438.       $doxygen_comment=0;
  439.     } else {
  440.       if ($doxygen_comment) {
  441.     if ($line =~ m/\@fn\s+/) {
  442.       if ($line =~ /\)[;\s]*$/) {
  443.         if ($comment_debug) {
  444.           print STDERR "OKAY COMMENT $line\n";
  445.         }
  446.       } else {
  447.         if ($comment_debug) {
  448.           print STDERR "NOT OKAY COMMENT $line\n";
  449.         }
  450.         $doxygen_comment_continuation=1;
  451.         $line =~ s/\n$//;
  452.         $last_line.=$line;
  453.         $linecont=1;
  454.         return($ret);
  455.       }
  456.     }
  457.         $last_comment.=$line;
  458.       }
  459.       $ret.=$line;
  460.       return($ret);
  461.     }
  462.   }
  463.  
  464.   # if we have a comment block starting ...
  465.   if ($line=~m/\/\*/) {
  466.     $comment=1;
  467.     if ($line=~m/\/\*\*/) {
  468.       if ($comment_debug) {
  469.         print STDERR "DOXYGEN COMMENT START - $line\n";
  470.       }
  471.       $doxygen_comment=1;
  472.     }
  473.     if ($doxygen_comment) {
  474.       $last_comment=$line;
  475.     }
  476.   }
  477.  
  478.   # if we have a comment block finishing ...
  479.   if($line=~m/\*\//) {
  480.     if ($comment_debug) {
  481.       print STDERR "COMMENT END $line\n";
  482.     }
  483.     $comment=0;
  484.   }
  485.  
  486.   # if we have a #define statement we check to see if the last comment
  487.   # block contained a @fn line and if so then we output the @fn line
  488.   # and *not* the #define so things will be documented as though it
  489.   # was a function
  490.   if (!$no_macro_functions) {
  491.     if ($line =~ m/^\s*\#define\s+/) {
  492.       #
  493.       #
  494.       #
  495.       if ($comment_debug) {
  496.         print STDERR "HAVE MACRO USAGE - $line\n";
  497.         print STDERR "LAST COMMENT IS - $last_comment\n";
  498.       }
  499.       if ($last_comment =~ m/.*\@fn\s+(.*)\n.*/) {
  500.     local($prototype)=$1;
  501.  
  502.     if ($comment_debug) {
  503.       print STDERR "COMMENT BLOCK - proto $prototype\n$last_comment";
  504.     }
  505.     if (!($prototype =~ m/;/)) {
  506.       $prototype="$prototype;";
  507.     }
  508.     $ret.="$prototype\n";
  509.     return($ret);
  510.       }
  511.     }
  512.   }
  513.  
  514.   #
  515.   # If we see the start of a function body then we know at
  516.   # this point that we have gathered enough information that
  517.   # we can output an ANSI-style defn
  518.   #
  519.   if ($line=~m/{/) {
  520.     # we are entering a function body now
  521.     $level+=count_char($line, "{")- count_char($line, "}");
  522.     $expect_knr= 0;
  523.  
  524.     # if we just parsed a function definition then output it
  525.     if ($funcname && $level == 1) {
  526.  
  527. output_func:
  528.  
  529.       $have_func_count--;
  530.  
  531.       if ($debug) {
  532.     print STDERR "FUNC $funcname at level=1\n";
  533.     print STDERR print_array("params", ",");
  534.     print STDERR "\n";
  535.     print STDERR print_array("knrparams", ",");
  536.     print STDERR "\n";
  537.       }
  538.  
  539.       #
  540.       # if there are no knr style declarations, just output the
  541.       # definition as is.
  542.       #
  543.       if (scalar(@knrparams) == 0) {
  544.     # there were no k&r stype parameter definitions
  545.     # output the buffered function definition
  546.     $ret.="$func_def$line";
  547.     $funcname= "";
  548.     @params=();
  549.     @knrparams=();
  550.     @knrpar_names=();
  551.     if ($debug) {
  552.       print STDERR "RETURN as-is\n";
  553.     }
  554.     return($ret);
  555.       }
  556.  
  557.       #
  558.       # if the number of parameters matches,
  559.       # replace the ones in the parameter list, by the
  560.       # K&R declarations.
  561.       #
  562.       if (scalar(@knrparams) == scalar(@params)) {
  563.     $idx1= index($func_def, "(");
  564.     $idx2= rindex($func_def, ")");
  565.     $new_func_def= substr($func_def, 0, $idx1+1);
  566.     for ($kp= 0; $kp < scalar(@knrparams); $kp+=1) {
  567.       $new_func_def.= @knrparams[$kp];
  568.       $new_func_def.= "," if ($kp < (scalar(@knrparams)-1));
  569.     }
  570.     $new_func_def.= substr($func_def, $idx2);
  571.     $ret.=$new_func_def;
  572.       } else {
  573.     local($errmsg);
  574.  
  575.     $errmsg="FUNC $funcname at level=1\n";
  576.     $errmsg.=print_array("params", ",");
  577.     $errmsg.="\n";
  578.     $errmsg.=print_array("knrparams", ",");
  579.     $errmsg.="\n";
  580.     &error_exit("$file_name:$lineno: Mismatched number of parameter definitions and parameter list. $expect_knr - " . scalar(@knrparams) . " != " . scalar(@params) . "\n$line\n$errmsg");
  581.       }
  582.  
  583.       $funcname= "";
  584.       @params=();
  585.       @knrparams=();
  586.       @knrpar_names=();
  587.     }
  588.     $ret.=$line;
  589.     if ($debug) {
  590.       print STDERR "RETURN FUNC LINE $ret\n";
  591.     }
  592.     return($ret);
  593.   }
  594.  
  595.   #
  596.   # When we hit a a closing brace we decrease our block level
  597.   #
  598.   if ($line=~m/}/) {
  599.     $level-=1;
  600.     $ret.=$line;
  601.     return($ret);
  602.   }
  603.  
  604.   #if we are outside a function and not inside a comment
  605.   if($level==0 && !$comment) {
  606.     # strip out comments ( these can only be one liners if we got here)
  607.     $stripped_line= $line;
  608.     $stripped_line=~s/\/\*.*\*\///g;
  609.  
  610.     if ($stripped_line =~ m/^\s*\#/xg) {
  611.       #
  612.       # if we have a function and have gathered enough parameters
  613.       # to fixup the knr form then we do that now
  614.       #
  615.       if ($have_func_count == 1) {
  616.         if (scalar(@knrparams) == scalar(@params)) {
  617.       print STDERR "PREPROC: current line->lastline $line\n" if ($debug);
  618.       $last_line.="$line";
  619.       $line="";
  620.       $expect_knr=0;
  621.       $concatenate=0;
  622.       goto output_func;
  623.     }
  624.       }
  625.       # this looks like a pre-processor statement
  626.       # spit it out!
  627.       $ret.=$line;
  628.       return($ret);
  629.     }
  630.  
  631.     if ($debug) {
  632.       printf STDERR "stripped_line=$stripped_line\n";
  633.     }
  634.  
  635.     if (!$expect_knr && ($stripped_line =~ m/((?:\*\s*)*\w+)\s*\(/xg) ) {
  636.       # check for func_name(
  637.       # this looks like a function declaration
  638.       # check if its complete
  639.       my $paralevel= count_char($stripped_line, "(")- count_char($stripped_line, ")");
  640.       printf STDERR "paralevel= $paralevel\n" if ($debug);
  641.  
  642.       if ($paralevel==0) {
  643.     printf STDERR "complete definition:\n%s",$stripped_line if ($debug);
  644.  
  645.         $have_func_count++;
  646.  
  647.     # if we have an assignment statement then we are not
  648.     # interested in this line from the point of view of
  649.     # gathering details about parameters
  650.     if ($stripped_line =~ /.*=.*/) {
  651.       $ret.=$line;
  652.           return($ret);
  653.     }
  654.  
  655.     if ( $stripped_line =~ m/\)\s*\;/xg ||
  656.          ($stripped_line =~ m/\(\s*\*\s*\w+\s*\)\s*\(/xg)) {
  657.           $have_func_count--;
  658.       # check for (*)()
  659.       # this appears to be a prototype
  660.       $expect_knr= 0;
  661.       $concatenate=0;
  662.       $ret.=$line;
  663.       if ($debug) {
  664.         print STDERR "ALREADY prototyped\n";
  665.       }
  666.       return($ret);
  667.     } else {
  668.       if ($have_func_count == 2) {
  669.         $have_func_count--;
  670.         print STDERR "current line->lastline $line\n" if($debug);
  671.         $last_line.="$line";
  672.         $line="";
  673.             $expect_knr=0;
  674.         $concatenate=0;
  675.         goto output_func;
  676.       }
  677.       #printf STDERR "defn single $have_func_count $level:\n%s",$stripped_line;
  678.     }
  679.  
  680.     #
  681.     # now we can process the entire definition
  682.     #
  683.     $funcname= $1;
  684.     @params= $stripped_line=~ m/(\w+)\s*[\,)]/xg;
  685.     if ($debug) {
  686.       print STDERR print_array ("params", " ");
  687.       printf STDERR "\n";
  688.     }
  689.  
  690.     $func_def= $line;
  691.     $expect_knr= 1;
  692.     $concatenate=0;
  693.       } else {
  694.     if ($paralevel < 0) {
  695.       # this is an error ( more closing than opening paras
  696.       &error_exit("$file_name:$lineno: Error parsing function declaration or definition");
  697.     }
  698.     $concatenate=1;
  699.     #
  700.     # remove the trailing newline - we will join the next line to
  701.     # this line and see if it makes more sense
  702.     #
  703.     $line=~s/\n$//;
  704.         $last_line.=$line;
  705.       }
  706.       return($ret);
  707.     }
  708.  
  709.     if ($expect_knr) {
  710.       printf STDERR "\nhandling possible K&Rs: $stripped_line\n" if ($debug);
  711.  
  712.       # this could be  K&r style param definitions
  713.       # isolate the parameters (followed either by a , or ;)
  714.       @pardefs= $stripped_line =~ m/((?:\*\s*)*\(\s*\*\s*\w+\s*\)\s*\(.*\))\s*[,;]/xg;
  715.       @parnames= $stripped_line =~ m/(?:\*\s*)*\(\s*(\*\s*\w+)\s*\)\s*\(.*\)\s*[,;]/xg;
  716.  
  717.       if (scalar(@pardefs) == 0 ) {
  718.     @pardefs= $stripped_line =~ m/((?:\*\s*)*\w+)\s*(?:\[\s*\])*\s*[,;]/xg;
  719.     if (scalar(@pardefs) ==0) {
  720.       # we didn't get what we expected, redo;
  721.       # so try again (maybe a #if ... #else ...#endif
  722.       $expect_knr= 0;
  723.       $ret.=$stripped_line;
  724.       return($ret);
  725.     }
  726.     @parnames= @pardefs;
  727.       }
  728.  
  729.       if ($debug) {
  730.     printf STDERR print_array ("pardefs", " ");
  731.     printf STDERR "\n";
  732.       }
  733.  
  734.       @words= $stripped_line =~ m/((?:\*\s*)*\w+)/xg;
  735.       if ($debug) {
  736.     print STDERR print_array ("words", " ");
  737.     printf STDERR "\n";
  738.     print STDERR print_array ("parnames", " ");
  739.     printf STDERR "\n";
  740.       }
  741.  
  742.       $past_type= 0;
  743.       $type= "";
  744.       # find the type
  745.       foreach $w (@words) {
  746.     if ($w eq @parnames[0]) {
  747.       $past_type= 1;
  748.     } else {
  749.       $type.=$w." " if (!$past_type);
  750.     }
  751.       }
  752.  
  753.       chop($type);
  754.       printf STDERR "type= $type\n" if ($debug);
  755.       for ($l= 0; $l < @pardefs; $l+=1) {
  756.     $found= 0;
  757.     for ($j= 0; $j < scalar(@knrpar_names); $j++) {
  758.       printf STDERR "@knrpar_names[$j] =? @parnames[$l]\n" if ($debug);
  759.       if (@knrpar_names[$j] eq @parnames[$l]) {
  760.         $found= 1;
  761.       }
  762.     }
  763.     if ($found == 0) {
  764.       push(@knrpar_names, @parnames[$l]);
  765.       push(@knrparams, $type . " " . @pardefs[$l]);
  766.       printf STDERR "\nparam:".$type." ".@pardefs[$l]."\n" if ($debug);
  767.     } else {
  768.       printf STDERR "\nduplicate param:".$type." ".@pardefs[$l]."\n" if ($debug);
  769.     }
  770.       }
  771.       return($ret);
  772.     } else {
  773. #      if (!$concatenate) {
  774.     $ret.=$line;
  775.     return($ret);
  776. #      }
  777.     }
  778.   } else {
  779.     $ret.=$line;
  780.     return($ret);
  781.   }
  782. }
  783.  
  784. sub print_array
  785. {
  786.   my ($name, $sep, $dont_print_array_name)= @_;
  787.   my $ref= \$name;
  788.   my @array= ();
  789.   my $ne;
  790.   my $pa;
  791.   my $ret;
  792.  
  793.   $ret="";
  794.   @array= @$$ref;
  795.   $ne=scalar(@array);
  796.  
  797.   $ret.="$name:" if (!$dont_print_array_name);
  798.   for ($pa = 0; $pa < $ne; $pa+=1) {
  799.     $ret.=@array[$pa];
  800.     $ret.=$sep if $pa< $ne-1;
  801.   }
  802.   return($ret);
  803. }
  804.  
  805. sub count_char
  806. {
  807.   my ($s,$c)= @_;
  808.   my $count= 0;
  809.   my $idx= -1;
  810.  
  811.   while (($idx= index($s, $c, $idx)) > -1) {
  812.     $count++; $idx++;
  813.   }
  814.   return $count;
  815. }
  816.  
  817. sub separate_type_params
  818. {
  819.   my ($funcname, @elements)= @_;
  820.   my $past_func_name= 0;
  821.   #my (@params,@types);
  822.   my $e;
  823.  
  824.   @params=();
  825.   @types=();
  826.   foreach $e (@elements) {
  827.     if (($e eq $funcname) || ("*".$e eq $funcname)) {
  828.       $past_func_name= 1;
  829.     } else {
  830.       push(@params, $e) if ($past_func_name);
  831.       push(@types, $e) if (!$past_func_name);
  832.     }
  833.   }
  834.   return;
  835. }
  836.  
  837.  
  838. #
  839. # bas2doxy - convert basic style functions into 'doxygen' format
  840. #
  841. sub bas2doxy {
  842.   local($in_line,$reset)=@_;
  843.   local($ret)="";
  844.   local($line);
  845.   local(@prevlines);
  846.  
  847.   if ($reset eq 1) {
  848.     $comment=0;
  849.     $funcname="";
  850.     $level=0;
  851.     $concatenate=0;
  852.     $last_line="";
  853.     @params=();
  854.   }
  855.  
  856.   # prepend the buffered line data from the last call
  857.   $in_line="$last_line$in_line";
  858.  
  859.   #
  860.   # we must process any buffered information before we handle the
  861.   # current line of data - so we call recursively appending the output
  862.   # until we have handled all the lines (including the current one)
  863.   #
  864.   @prevlines=split(/\n/,"$in_line\n");
  865.   $last_line="";
  866.   if (!$doing_recursion && ($#prevlines>0)) {
  867.     $doing_recursion=1;
  868.     foreach $pline (@prevlines) {
  869.       $ret.=&bas2doxy("$pline\n",$reset);
  870.     }
  871.     $doing_recursion=0;
  872.     return($ret);
  873.   }
  874.  
  875.   # if we are before or after the normal line processing then we flush
  876.   # whatever data we have buffered
  877.   if ($reset ne 0) {
  878.     return "$in_line$last_line";
  879.   }
  880.  
  881.   if ($debug) {
  882.     printf STDERR "in_line=$in_line";
  883.   }
  884.  
  885.  
  886.   $line=$in_line;
  887.   if ($debug) {
  888.     printf STDERR "level=$level, line=$line\n";
  889.   }
  890.  
  891.   #
  892.   # check for line continuation character at the end
  893.   #
  894.   $linecont=0;
  895.   if ($line =~ m/_\s*$/xg) {
  896.     #print STDERR "CONT: $line\n";
  897.     $last_line.=$line;
  898.     $linecont=1;
  899.     return($ret);
  900.   }
  901.  
  902.   # handle embedded doxygen comments
  903.   if ($line =~ /^\'((\/\*| \*).*$)/) {
  904.     ##print STDERR "MATCH1: $in_line => $1\n";
  905.     $ret.="$1\n";
  906.     # handle single quote followed by equals as C-style code
  907.   } elsif ($line =~ /^\'=(.*$)/) {
  908.     ##print STDERR "MATCH2: $in_line => $1\n";
  909.     $ret.="$1\n";
  910.   } elsif ($line =~ /^(Public|Private|)\s*(Sub|Function)\s*(.*)/ ) {
  911.     local($pre,$scope,$type,$rest,$ftype);
  912.  
  913.     $scope=$1; $type=$2; $rest=$3;
  914.     if ($scope eq "Private") {
  915.       $pre="static ";
  916.     } else {
  917.       $pre="";
  918.     }
  919.     #print STDERR "MATCHSUB: $in_line => $1:$2:$3\n";
  920.     if ($type eq "Sub") {
  921.       $ret.="void $rest;\n";
  922.     } elsif ($type eq "Function") {
  923.       if ($rest =~ /^((.*)(\([^\)]*\)))\s+As\s+(.*)/) {
  924.         $ftype="$4";
  925.         $rest=$1;
  926.       } else {
  927.         $ftype="int";
  928.       }
  929.       $ret.="$pre$ftype $rest;\n";
  930.     } else {
  931.       $ret.="$line\n";
  932.     }
  933.   } else {
  934.     $ret.="\n";
  935.   }
  936.  
  937.   return($ret);
  938.  
  939. }
  940.  
  941. sub error_exit {
  942.   local($message)=@_;
  943.  
  944.   die <<EOF;
  945. ****************************************************************************
  946. * doxytmpl filter aborting - the output file will be invalid or incomplete *
  947. ****************************************************************************
  948. $message
  949. ****************************************************************************
  950. EOF
  951. }
  952.  
  953.