From 8edbe90dd289153acba4948df918f00ecfba54a1 Mon Sep 17 00:00:00 2001
From: Jon Griffiths <jon_p_griffiths@yahoo.com>
Date: Sat, 19 Jul 2003 03:08:29 +0000
Subject: [PATCH] Correctly spot forwards. Various improvements and fixes to
 the output, add index for html.

---
 tools/c2man.pl | 333 ++++++++++++++++++++++++++++++++++++++++---------
 1 file changed, 274 insertions(+), 59 deletions(-)

diff --git a/tools/c2man.pl b/tools/c2man.pl
index 3c78b9b5814..5798ae2f952 100755
--- a/tools/c2man.pl
+++ b/tools/c2man.pl
@@ -22,13 +22,20 @@
 # TODO
 #  SGML gurus - feel free to smarten up the SGML.
 #  Add any other relevant information for the dll - imports etc
-#  Read .spec flags such as -i386,-noname and add to docs appropriately
-#  Generate an alpabetical listing of every API call, with links
 #  Should we have a special output mode for WineHQ?
 
 use strict;
 use bytes;
 
+# Function flags. most of these come from the spec flags
+my $FLAG_DOCUMENTED = 1;
+my $FLAG_NONAME     = 2;
+my $FLAG_I386       = 4;
+my $FLAG_REGISTER   = 8;
+my $FLAG_APAIR      = 16; # The A version of a matching W function
+my $FLAG_WPAIR      = 32; # The W version of a matching A function
+
+
 # Options
 my $opt_output_directory = "man3w"; # All default options are for nroff (man pages)
 my $opt_manual_section = "3w";
@@ -45,13 +52,15 @@ my @opt_source_file_list = ();
 my %spec_files;
 # All the collected details about all the source files being processed
 my %source_files;
+# All documented functions that are to be placed in the index
+my @index_entries_list = ();
 
 # useful globals
 my $pwd = `pwd`."/";
 $pwd =~ s/\n//;
 my @datetime = localtime;
 my @months = ( "Jan", "Feb", "Mar", "Apr", "May", "Jun",
-            "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" );
+               "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" );
 my $year = $datetime[5] + 1900;
 my $date = "$months[$datetime[4]] $year";
 
@@ -94,10 +103,12 @@ while(defined($_ = shift @ARGV))
       /^e$/  && do { $opt_output_empty = 1;               last; };
       /^L$/  && do { last; };
       /^w$/  && do { @opt_spec_file_list = (@opt_spec_file_list, shift @ARGV); last; };
-      s/^I// && do { my $include = $_."/*.h";
-                     $include =~ s/\/\//\//g;
-                     my $have_headers = `ls $include >/dev/null 2>&1`;
-                     if ($? >> 8 == 0) { @opt_header_file_list = (@opt_header_file_list, $include); }
+      s/^I// && do { if ($_ ne ".") {
+                       my $include = $_."/*.h";
+                       $include =~ s/\/\//\//g;
+                       my $have_headers = `ls $include >/dev/null 2>&1`;
+                       if ($? >> 8 == 0) { @opt_header_file_list = (@opt_header_file_list, $include); }
+                     }
                      last;
                    };
       s/^R// && do { if ($_ =~ /^\//) { $opt_wine_root_dir = $_; }
@@ -216,10 +227,24 @@ sub process_spec_file
     s/\s+/ /g;           # Strip multiple tabs & spaces to a single space
     s/\s*#.*//;          # Strip comments
     s/\(.*\)/ /;         # Strip arguments
-    s/ \-[a-z0-9]+//g;   # Strip modifiers
     s/\s+/ /g;           # Strip multiple tabs & spaces to a single space (again)
     s/\n$//;             # Strip newline
 
+    my $flags = 0;
+    if( /\-noname/ )
+    {
+      $flags |= $FLAG_NONAME;
+    }
+    if( /\-i386/ )
+    {
+      $flags |= $FLAG_I386;
+    }
+    if( /\-register/ )
+    {
+      $flags |= $FLAG_REGISTER;
+    }
+    s/ \-[a-z0-9]+//g;   # Strip flags
+
     if( /^(([0-9]+)|@) / )
     {
       # This line contains an exported symbol
@@ -254,6 +279,14 @@ sub process_spec_file
       {
         $implementation_name = $exported_name;
       }
+
+      if ($implementation_name =~ /(.*?)\./)
+      {
+        $call_convention = "forward"; # Referencing a function from another dll
+        $spec_details->{NUM_FUNCS}--;
+        $spec_details->{NUM_FORWARDS}++;
+      }
+
       # Add indices for the exported and implementation names
       $spec_details->{EXPORTED_NAMES}{$exported_name} = $spec_details->{NUM_EXPORTS};
       if ($implementation_name ne $exported_name)
@@ -263,8 +296,7 @@ sub process_spec_file
 
       # Add the exported entry
       $spec_details->{NUM_EXPORTS}++;
-      my $documented = 0;
-      my @export = ($ordinal, $call_convention, $exported_name, $implementation_name, $documented);
+      my @export = ($ordinal, $call_convention, $exported_name, $implementation_name, $flags);
       push (@{$spec_details->{EXPORTS}},[@export]);
     }
   }
@@ -515,6 +547,7 @@ sub process_source_file
 sub process_comment_text
 {
   my $comment = shift(@_);
+  my $i = 0;
 
   for (@{$comment->{TEXT}})
   {
@@ -522,7 +555,7 @@ sub process_comment_text
     {
       # Map I/O values. These come in too many formats to standardise now....
       s/\[I\]|\[i\]|\[in\]|\[IN\]/\[In\] /g;
-      s/\[O\]|\[o\]|\[out\]\[OUT\]/\[Out\]/g;
+      s/\[O\]|\[o\]|\[out\]|\[OUT\]/\[Out\]/g;
       s/\[I\/O\]|\[I\,O\]|\[i\/o\]|\[in\/out\]|\[IN\/OUT\]/\[In\/Out\]/g;
       # TRUE/FALSE/NULL are defines, capitilise them
       s/True|true/TRUE/g;
@@ -531,7 +564,7 @@ sub process_comment_text
       # Preferred capitalisations
       s/ wine| WINE/ Wine/g;
       s/ API | api / Api /g;
-      s/DLL| Dll /dll /g;
+      s/DLL|Dll/dll /g;
       s/ URL | url / Url /g;
       s/WIN16|win16/Win16/g;
       s/WIN32|win32/Win32/g;
@@ -551,11 +584,34 @@ sub process_comment_text
       s/( |\.)(DOS|dos|msdos)( |\.)/$1MS-DOS\(tm\)$3/g;
       s/( |\.)(UNIX|Unix|unix)( |\.)/$1Unix\(tm\)$3/g;
       # Abbreviations
-      s/( chars)/characters/g;
+      s/( char )/ character /g;
+      s/( chars )/ characters /g;
       s/( info )/ information /g;
       s/( app )/ application /g;
       s/( apps )/ applications /g;
+      s/( exe )/ executable /g;
+      s/( ptr )/ pointer /g;
+      s/( obj )/ object /g;
+      s/( err )/ error /g;
+      s/( bool )/ boolean /g;
+      # Punctuation
+      if ( /\[I|\[O/ && ! /\.$/ )
+      {
+        $_ = $_."."; # Always have a full stop at the end of parameter desc.
+      }
+      elsif ($i > 0 && /^[A-Z]*$/  &&
+               !(@{$comment->{TEXT}}[$i-1] =~ /\.$/) &&
+               !(@{$comment->{TEXT}}[$i-1] =~ /\:$/))
+      {
+
+        if (!(@{$comment->{TEXT}}[$i-1] =~ /^[A-Z]*$/))
+        {
+          # Paragraphs always end with a full stop
+          @{$comment->{TEXT}}[$i-1] = @{$comment->{TEXT}}[$i-1].".";
+        }
+      }
     }
+    $i++;
   }
 }
 
@@ -585,11 +641,11 @@ sub process_comment
 
   my $exported_names = $spec_details->{EXPORTED_NAMES};
   my $export_index = $exported_names->{$comment->{COMMENT_NAME}};
+  my $implementation_names = $spec_details->{IMPLEMENTATION_NAMES};
 
   if (!defined($export_index))
   {
     # Perhaps the comment uses the implementation name?
-    my $implementation_names = $spec_details->{IMPLEMENTATION_NAMES};
     $export_index = $implementation_names->{$comment->{COMMENT_NAME}};
   }
   if (!defined($export_index))
@@ -618,7 +674,7 @@ sub process_comment
         print "Info: Found alternate name '",$1,"\n";
       }
       my $alt_export = @{$spec_details->{EXPORTS}}[$alt_index];
-      @$alt_export[4] |= 1;
+      @$alt_export[4] |= $FLAG_DOCUMENTED;
       $spec_details->{NUM_DOCS}++;
       ${@{$comment->{TEXT}}}[1] = "";
     }
@@ -647,7 +703,7 @@ sub process_comment
 
   # Mark the function as documented
   $spec_details->{NUM_DOCS}++;
-  @$export[4] |= 1;
+  @$export[4] |= $FLAG_DOCUMENTED;
 
   # This file is used by the DLL - Make sure we get our contributors right
   push (@{$spec_details->{SOURCES}},$comment->{FILE});
@@ -722,9 +778,45 @@ sub process_comment
       {
         if ( !/^[A-Z]+$/ )
         {
-          if ( /^See ([A-Za-z0-9_]+)\.$/ || /^Unicode version of ([A-Za-z0-9_]+)\.$/)
+          # Dont reject comments that refer to another doc (e.g. A/W)
+          if ( /^See ([A-Za-z0-9_]+)\.$/ )
+          {
+            if ($comment->{COMMENT_NAME} =~ /W$/ )
+            {
+              # This is probably a Unicode version of an Ascii function.
+              # Create the Ascii name and see if its been documented
+              my $ascii_name = $comment->{COMMENT_NAME};
+              $ascii_name =~ s/W$/A/;
+
+              my $ascii_export_index = $exported_names->{$ascii_name};
+
+              if (!defined($ascii_export_index))
+              {
+                $ascii_export_index = $implementation_names->{$ascii_name};
+              }
+              if (!defined($ascii_export_index))
+              {
+                if ($opt_verbose > 2)
+                {
+                  print "Warning: Function '".$comment->{COMMENT_NAME}."' is not an A/W pair.\n";
+                }
+              }
+              else
+              {
+                my $ascii_export = @{$spec_details->{EXPORTS}}[$ascii_export_index];
+                if (@$ascii_export[4] & $FLAG_DOCUMENTED)
+                {
+                  # Flag these functions as an A/W pair
+                  @$ascii_export[4] |= $FLAG_APAIR;
+                  @$export[4] |= $FLAG_WPAIR;
+                }
+              }
+            }
+            $found_returns = 1;
+          }
+          elsif ( /^Unicode version of ([A-Za-z0-9_]+)\.$/ )
           {
-            # Dont reject comments that refer to their A/W couterpart
+            @$export[4] |= $FLAG_WPAIR; # Explicitly marked as W version
             $found_returns = 1;
           }
           $found_description_text = 1;
@@ -743,7 +835,7 @@ sub process_comment
               "description and/or RETURNS section, skipping\n";
       }
       $spec_details->{NUM_DOCS}--;
-      @$export[4] &= ~1;
+      @$export[4] &= ~$FLAG_DOCUMENTED;
       return;
     }
   }
@@ -767,43 +859,50 @@ sub process_comment
 
   # Find header file
   my $h_file = "";
-  if ($comment->{COMMENT_NAME} ne "")
+  if (@$export[4] & $FLAG_NONAME)
+  {
+    $h_file = "Exported by ordinal only. Use GetProcAddress() to obtain a pointer to the function.";
+  }
+  else
   {
-    my $tmp = "grep -s -l $comment->{COMMENT_NAME} @opt_header_file_list 2>/dev/null";
-    $tmp = `$tmp`;
-    my $exit_value  = $? >> 8;
-    if ($exit_value == 0)
+    if ($comment->{COMMENT_NAME} ne "")
     {
-      $tmp =~ s/\n.*//g;
-      if ($tmp ne "")
+      my $tmp = "grep -s -l $comment->{COMMENT_NAME} @opt_header_file_list 2>/dev/null";
+      $tmp = `$tmp`;
+      my $exit_value  = $? >> 8;
+      if ($exit_value == 0)
       {
-        $h_file = `basename $tmp`;
+        $tmp =~ s/\n.*//g;
+        if ($tmp ne "")
+        {
+          $h_file = `basename $tmp`;
+        }
       }
     }
-  }
-  elsif ($comment->{ALT_NAME} ne "")
-  {
-    my $tmp = "grep -s -l $comment->{ALT_NAME} @opt_header_file_list"." 2>/dev/null";
-    $tmp = `$tmp`;
-    my $exit_value  = $? >> 8;
-    if ($exit_value == 0)
+    elsif ($comment->{ALT_NAME} ne "")
     {
-      $tmp =~ s/\n.*//g;
-      if ($tmp ne "")
+      my $tmp = "grep -s -l $comment->{ALT_NAME} @opt_header_file_list"." 2>/dev/null";
+      $tmp = `$tmp`;
+      my $exit_value  = $? >> 8;
+      if ($exit_value == 0)
       {
-        $h_file = `basename $tmp`;
+        $tmp =~ s/\n.*//g;
+        if ($tmp ne "")
+        {
+          $h_file = `basename $tmp`;
+        }
       }
     }
-  }
-  $h_file =~ s/^ *//;
-  $h_file =~ s/\n//;
-  if ($h_file eq "")
-  {
-    $h_file = "Not defined in a Wine header";
-  }
-  else
-  {
-    $h_file = "Defined in \"".$h_file."\"";
+    $h_file =~ s/^ *//;
+    $h_file =~ s/\n//;
+    if ($h_file eq "")
+    {
+      $h_file = "Not defined in a Wine header. The function is either undocumented, or missing from Wine."
+    }
+    else
+    {
+      $h_file = "Defined in \"".$h_file."\".";
+    }
   }
 
   # Find source file
@@ -822,15 +921,24 @@ sub process_comment
     $cfile =~ s/^\/+//;               # Strip initial directory slash
     $c_file = $cfile;
   }
-  $c_file = "Implemented in \"".$c_file."\"";
+  $c_file = "Implemented in \"".$c_file."\".";
 
   # Add the implementation details
   push (@{$comment->{TEXT}}, "IMPLEMENTATION","",$h_file,"",$c_file);
 
+  if (@$export[4] & $FLAG_I386)
+  {
+    push (@{$comment->{TEXT}}, "", "Available on x86 platforms only.");
+  }
+  if (@$export[4] & $FLAG_REGISTER)
+  {
+    push (@{$comment->{TEXT}}, "", "This function passes one or more arguments in registers. ",
+          "For more details, please read the source code.");
+  }
   my $source_details = $source_files{$comment->{FILE}}[0];
   if ($source_details->{DEBUG_CHANNEL} ne "")
   {
-    push (@{$comment->{TEXT}}, "", "Debug channel \"".$source_details->{DEBUG_CHANNEL}."\"");
+    push (@{$comment->{TEXT}}, "", "Debug channel \"".$source_details->{DEBUG_CHANNEL}."\".");
   }
 
   # Write out the documentation for the API
@@ -1113,7 +1221,7 @@ sub output_spec
     {
       my $line = "";
 
-      # @$_ => ordinal, call convention, exported name, implementation name, documented;
+      # @$_ => ordinal, call convention, exported name, implementation name, flags;
       if (@$_[1] eq "forward")
       {
         my $forward_dll = @$_[3];
@@ -1131,6 +1239,11 @@ sub output_spec
       elsif (@$_[1] eq "fake")
       {
         # Don't add this function here, it gets listed with the extra documentation
+        if (!(@$_[4] & $FLAG_WPAIR))
+        {
+          # This function should be indexed
+          push (@index_entries_list, @$_[3].",".@$_[3]);
+        }
       }
       elsif (@$_[1] eq "equate" || @$_[1] eq "variable")
       {
@@ -1139,7 +1252,7 @@ sub output_spec
       else
       {
         # A function
-        if (@$_[4] & 1)
+        if (@$_[4] & $FLAG_DOCUMENTED)
         {
           # Documented
           $line = @$_[2]." (implemented as ".@$_[3]."())";
@@ -1151,6 +1264,11 @@ sub output_spec
           {
             $line = @$_[2]."()";
           }
+          if (!(@$_[4] & $FLAG_WPAIR))
+          {
+            # This function should be indexed
+            push (@index_entries_list, @$_[2].",".@$_[3]);
+          }
         }
         else
         {
@@ -1175,6 +1293,9 @@ sub output_spec
       }
     }
   }
+  # The dll entry should also be indexed
+  push (@index_entries_list, $spec_details->{DLL_NAME}.",".$spec_details->{DLL_NAME});
+
   # Write out the document
   output_open_api_file($spec_details->{DLL_NAME});
   output_api_header($comment);
@@ -1265,8 +1386,9 @@ sub output_api_footer
   if ($opt_output_format eq "h")
   {
       print OUTPUT "<hr><p><i class=\"copy\">Copyright &copy ".$year." The Wine Project.".
-                   "Visit <a HREF=http://www.winehq.org>WineHQ</a> for license details.".
-                   "Generated $date</i></p>\n</body>\n</html>\n";
+                   " All trademarks are the property of their respective owners.".
+                   " Visit <a HREF=http://www.winehq.org>WineHQ</a> for license details.".
+                   " Generated $date.</i></p>\n</body>\n</html>\n";
   }
   elsif ($opt_output_format eq "s")
   {
@@ -1511,6 +1633,9 @@ sub output_api_comment
       {
         # Html uses links for API calls
         s/([A-Za-z_]+[A-Za-z_0-9]+)(\(\))/<a href\=\"$1\.html\">$1<\/a>/g;
+        # Index references
+        s/\{\{(.*?)\}\}\{\{(.*?)\}\}/<a href\=\"$2\.html\">$1<\/a>/g;
+        s/ ([A-Z_])(\(\))/<a href\=\"$1\.html\">$1<\/a>/g;
         # And references to COM objects (hey, they'll get documented one day)
         s/ (I[A-Z]{1}[A-Za-z0-9_]+) (Object|object|Interface|interface)/ <a href\=\"$1\.html\">$1<\/a> $2/g;
         # Convert any web addresses to real links
@@ -1589,7 +1714,7 @@ sub output_api_comment
             print OUTPUT $fmt[17];
             $open_paragraph = 0;
           }
-          s/(\[.+\])( *)/$fmt[19]$fmt[10]$1$fmt[11]$fmt[19]/; # Format In/Out
+          s/(\[.+\])( *)/$fmt[19]$fmt[10]$1$fmt[11]$fmt[19] /; # Format In/Out
         }
         else
         {
@@ -1600,8 +1725,11 @@ sub output_api_comment
         # Format parameter names where they appear in the comment
         for my $parameter_name (@parameter_names)
         {
-          s/(^|[ \.\,\(\-])($parameter_name)($|[ \.\)\,\-])/$1$fmt[8]$2$fmt[9]$3/g;
+          s/(^|[ \.\,\(\-\*])($parameter_name)($|[ \.\)\,\-\=\/])/$1$fmt[8]$2$fmt[9]$3/g;
         }
+        # Structure dereferences include the dereferenced member
+        s/(\-\>[A-Za-z_]+)/$fmt[8]$1$fmt[9]/g;
+        s/(\-\&gt\;[A-Za-z_]+)/$fmt[8]$1$fmt[9]/g;
 
         if ($open_paragraph == 0)
         {
@@ -1640,6 +1768,19 @@ sub output_master_index_files
     return; # No master index for man pages
   }
 
+  if ($opt_output_format eq "h")
+  {
+    # Append the index entries to the output db of index entries
+    my $output_file = $opt_output_directory."/index.db";
+    open(INDEXDB,">>$output_file") || die "Couldn't create $output_file\n";
+    for (@index_entries_list)
+    {
+      $_ =~ s/A\,/\,/;
+      print INDEXDB $_."\n";
+    }
+    close(INDEXDB);
+  }
+
   # Use the comment output functions for consistency
   my $comment =
   {
@@ -1690,10 +1831,16 @@ sub output_master_index_files
 
   if ($opt_output_format eq "h")
   {
-    # HTML gets a list of all the dlls. For docbook the index creates this for us
+    # HTML gets a list of all the dlls and an index. For docbook the index creates this for us
     push (@{$comment->{TEXT}},
+      "INDEX",
+      "For an alphabetical listing of the functions available, please click the ",
+      "first letter of the functions name below:","",
+      "[ _(), A(), B(), C(), D(), E(), F(), G(), H(), ".
+      "I(), J(), K(), L(), M(), N(), O(), P(), Q() ".
+      "R(), S(), T(), U(), V(), W(), X(), Y(), Z() ]", "",
       "DLLS",
-      "The following dlls are provided by Wine:",
+      "Each dll provided by Wine is documented individually. The following dlls are provided :",
       ""
     );
     # Add the dlls to the comment
@@ -1728,8 +1875,8 @@ sub output_master_index_files
   }
   if ($opt_output_format eq "h")
   {
+    output_html_index_files();
     output_html_stylesheet();
-    # FIXME: Create an alphabetical index
     return;
   }
 }
@@ -1807,6 +1954,69 @@ sub output_sgml_dll_file
   `mv $tmp_name $opt_output_directory/$spec_details->{DLL_NAME}.sgml`;
 }
 
+# Write the html index files containing the function names
+sub output_html_index_files
+{
+  if ($opt_output_format ne "h")
+  {
+    return;
+  }
+
+  my @letters = ('_', 'A' .. 'Z');
+
+  # Read in all functions
+  my $input_file = $opt_output_directory."/index.db";
+  my @funcs = `cat $input_file|sort|uniq`;
+
+  for (@letters)
+  {
+    my $letter = $_;
+    my $comment =
+    {
+      FILE => "",
+      COMMENT_NAME => "",
+      ALT_NAME => "",
+      DLL_NAME => "",
+      ORDINAL => "",
+      RETURNS => "",
+      PROTOTYPE => [],
+      TEXT => [],
+    };
+
+    $comment->{COMMENT_NAME} = $letter." Functions";
+    $comment->{ALT_NAME} = $letter." Functions";
+
+    push (@{$comment->{TEXT}},
+      "NAME",
+      $comment->{COMMENT_NAME},
+      "FUNCTIONS"
+    );
+
+    # Add the functions to the comment
+    for (@funcs)
+    {
+      my $first_char = substr ($_, 0, 1);
+      $first_char = uc $first_char;
+
+      if ($first_char eq $letter)
+      {
+        my $name = $_;
+        my $file;
+        $name =~ s/(^.*?)\,(.*?)\n/$1/;
+        $file = $2;
+        push (@{$comment->{TEXT}}, "{{".$name."}}{{".$file."}}","");
+      }
+    }
+
+    # Write out the document
+    output_open_api_file($letter);
+    output_api_header($comment);
+    output_api_comment($comment);
+    output_api_footer($comment);
+    output_close_api_file();
+  }
+}
+
 # Output the stylesheet for HTML output
 sub output_html_stylesheet
 {
@@ -1825,7 +2035,7 @@ sub output_html_stylesheet
 BODY { /* Page body */
 background-color: white;
 color: black;
-font-family: Tahomsans-serif;
+font-family: Tahoma,sans-serif;
 font-style: normal;
 font-size: 10pt;
 }
@@ -1868,6 +2078,11 @@ vertical-align: top;
 margin-left: 0.5in;
 margin-right: 1.0in;
 }
+pre.raw { /* Raw text output */
+margin-left: 0.6in;
+margin-right: 1.1in;
+background-color: #8080DC;
+}
 tt.param { /* Parameter name */
 font-style: italic;
 color: blue;
-- 
GitLab