| #! /usr/bin/perl -w | |
| # | |
| # Generate API documentation. See documentation/documentation.sgml for details. | |
| # | |
| # Copyright (C) 2000 Mike McCormack | |
| # Copyright (C) 2003 Jon Griffiths | |
| # | |
| # This library is free software; you can redistribute it and/or | |
| # modify it under the terms of the GNU Lesser General Public | |
| # License as published by the Free Software Foundation; either | |
| # version 2.1 of the License, or (at your option) any later version. | |
| # | |
| # This library is distributed in the hope that it will be useful, | |
| # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| # Lesser General Public License for more details. | |
| # | |
| # You should have received a copy of the GNU Lesser General Public | |
| # License along with this library; if not, write to the Free Software | |
| # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
| # | |
| # TODO | |
| # SGML gurus - feel free to smarten up the SGML. | |
| # Add any other relevant information for the dll - imports etc | |
| # 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 | |
| my $FLAG_64PAIR = 64; # The 64 bit version of a matching 32 bit function | |
| # Options | |
| my $opt_output_directory = "man3w"; # All default options are for nroff (man pages) | |
| my $opt_manual_section = "3w"; | |
| my $opt_wine_root_dir = ""; | |
| my $opt_output_format = ""; # '' = nroff, 'h' = html, 's' = sgml | |
| my $opt_output_empty = 0; # Non-zero = Create 'empty' comments (for every implemented function) | |
| my $opt_fussy = 1; # Non-zero = Create only if we have a RETURNS section | |
| my $opt_verbose = 0; # >0 = verbosity. Can be given multiple times (for debugging) | |
| my @opt_header_file_list = (); | |
| my @opt_spec_file_list = (); | |
| my @opt_source_file_list = (); | |
| # All the collected details about all the .spec files being processed | |
| 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" ); | |
| my $year = $datetime[5] + 1900; | |
| my $date = "$months[$datetime[4]] $year"; | |
| # Generate the list of exported entries for the dll | |
| sub process_spec_file($) | |
| { | |
| my $spec_name = shift; | |
| my $dll_name = $spec_name; | |
| $dll_name =~ s/\..*//; # Strip the file extension | |
| my $uc_dll_name = uc $dll_name; | |
| my $spec_details = | |
| { | |
| NAME => $spec_name, | |
| DLL_NAME => $dll_name, | |
| NUM_EXPORTS => 0, | |
| NUM_STUBS => 0, | |
| NUM_FUNCS => 0, | |
| NUM_FORWARDS => 0, | |
| NUM_VARS => 0, | |
| NUM_DOCS => 0, | |
| CONTRIBUTORS => [ ], | |
| SOURCES => [ ], | |
| DESCRIPTION => [ ], | |
| EXPORTS => [ ], | |
| EXPORTED_NAMES => { }, | |
| IMPLEMENTATION_NAMES => { }, | |
| EXTRA_COMMENTS => [ ], | |
| CURRENT_EXTRA => [ ] , | |
| }; | |
| if ($opt_verbose > 0) | |
| { | |
| print "Processing ".$spec_name."\n"; | |
| } | |
| # We allow opening to fail just to cater for the peculiarities of | |
| # the Wine build system. This doesn't hurt, in any case | |
| open(SPEC_FILE, "<$spec_name") || return; | |
| while(<SPEC_FILE>) | |
| { | |
| s/^\s+//; # Strip leading space | |
| s/\s+\n$/\n/; # Strip trailing space | |
| s/\s+/ /g; # Strip multiple tabs & spaces to a single space | |
| s/\s*#.*//; # Strip comments | |
| s/\(.*\)/ /; # Strip arguments | |
| 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 | |
| my ($ordinal, $call_convention, $exported_name, $implementation_name) = split(' '); | |
| for ($call_convention) | |
| { | |
| /^(cdecl|stdcall|varargs|pascal)$/ | |
| && do { $spec_details->{NUM_FUNCS}++; last; }; | |
| /^(variable|equate)$/ | |
| && do { $spec_details->{NUM_VARS}++; last; }; | |
| /^(extern)$/ | |
| && do { $spec_details->{NUM_FORWARDS}++; last; }; | |
| /^stub$/ && do { $spec_details->{NUM_STUBS}++; last; }; | |
| if ($opt_verbose > 0) | |
| { | |
| print "Warning: didn't recognise convention \'",$call_convention,"'\n"; | |
| } | |
| last; | |
| } | |
| # Convert ordinal only names so we can find them later | |
| if ($exported_name eq "@") | |
| { | |
| $exported_name = $uc_dll_name.'_'.$ordinal; | |
| } | |
| if (!defined($implementation_name)) | |
| { | |
| $implementation_name = $exported_name; | |
| } | |
| if ($implementation_name eq "") | |
| { | |
| $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) | |
| { | |
| $spec_details->{IMPLEMENTATION_NAMES}{$exported_name} = $spec_details->{NUM_EXPORTS}; | |
| } | |
| # Add the exported entry | |
| $spec_details->{NUM_EXPORTS}++; | |
| my @export = ($ordinal, $call_convention, $exported_name, $implementation_name, $flags); | |
| push (@{$spec_details->{EXPORTS}},[@export]); | |
| } | |
| } | |
| close(SPEC_FILE); | |
| # Add this .spec files details to the list of .spec files | |
| $spec_files{$uc_dll_name} = [$spec_details]; | |
| } | |
| # Read each source file, extract comments, and generate API documentation if appropriate | |
| sub process_source_file($) | |
| { | |
| my $source_file = shift; | |
| my $source_details = | |
| { | |
| CONTRIBUTORS => [ ], | |
| DEBUG_CHANNEL => "", | |
| }; | |
| my $comment = | |
| { | |
| FILE => $source_file, | |
| COMMENT_NAME => "", | |
| ALT_NAME => "", | |
| DLL_NAME => "", | |
| ORDINAL => "", | |
| RETURNS => "", | |
| PROTOTYPE => [], | |
| TEXT => [], | |
| }; | |
| my $parse_state = 0; | |
| my $ignore_blank_lines = 1; | |
| my $extra_comment = 0; # 1 if this is an extra comment, i.e its not a .spec export | |
| if ($opt_verbose > 0) | |
| { | |
| print "Processing ".$source_file."\n"; | |
| } | |
| open(SOURCE_FILE,"<$source_file") || die "couldn't open ".$source_file."\n"; | |
| # Add this source file to the list of source files | |
| $source_files{$source_file} = [$source_details]; | |
| while(<SOURCE_FILE>) | |
| { | |
| s/\n$//; # Strip newline | |
| s/^\s+//; # Strip leading space | |
| s/\s+$//; # Strip trailing space | |
| if (! /^\*\|/ ) | |
| { | |
| # Strip multiple tabs & spaces to a single space | |
| s/\s+/ /g; | |
| } | |
| if ( / +Copyright *(\([Cc]\))*[0-9 \-\,\/]*([[:alpha:][:^ascii:] \.\-]+)/ ) | |
| { | |
| # Extract a contributor to this file | |
| my $contributor = $2; | |
| $contributor =~ s/ *$//; | |
| $contributor =~ s/^by //; | |
| $contributor =~ s/\.$//; | |
| $contributor =~ s/ (for .*)/ \($1\)/; | |
| if ($contributor ne "") | |
| { | |
| if ($opt_verbose > 3) | |
| { | |
| print "Info: Found contributor:'".$contributor."'\n"; | |
| } | |
| push (@{$source_details->{CONTRIBUTORS}},$contributor); | |
| } | |
| } | |
| elsif ( /WINE_DEFAULT_DEBUG_CHANNEL\(([A-Za-z]*)\)/ ) | |
| { | |
| # Extract the debug channel to use | |
| if ($opt_verbose > 3) | |
| { | |
| print "Info: Found debug channel:'".$1."'\n"; | |
| } | |
| $source_details->{DEBUG_CHANNEL} = $1; | |
| } | |
| if ($parse_state == 0) # Searching for a comment | |
| { | |
| if ( /^\/\**$/ ) | |
| { | |
| # Found a comment start | |
| $comment->{COMMENT_NAME} = ""; | |
| $comment->{ALT_NAME} = ""; | |
| $comment->{DLL_NAME} = ""; | |
| $comment->{ORDINAL} = ""; | |
| $comment->{RETURNS} = ""; | |
| $comment->{PROTOTYPE} = []; | |
| $comment->{TEXT} = []; | |
| $ignore_blank_lines = 1; | |
| $extra_comment = 0; | |
| $parse_state = 3; | |
| } | |
| } | |
| elsif ($parse_state == 1) # Reading in a comment | |
| { | |
| if ( /^\**\// ) | |
| { | |
| # Found the end of the comment | |
| $parse_state = 2; | |
| } | |
| elsif ( s/^\*\|/\|/ ) | |
| { | |
| # A line of comment not meant to be pre-processed | |
| push (@{$comment->{TEXT}},$_); # Add the comment text | |
| } | |
| elsif ( s/^ *\** *// ) | |
| { | |
| # A line of comment, starting with an asterisk | |
| if ( /^[A-Z]+$/ || $_ eq "") | |
| { | |
| # This is a section start, so skip blank lines before and after it. | |
| my $last_line = pop(@{$comment->{TEXT}}); | |
| if (defined($last_line) && $last_line ne "") | |
| { | |
| # Put it back | |
| push (@{$comment->{TEXT}},$last_line); | |
| } | |
| if ( /^[A-Z]+$/ ) | |
| { | |
| $ignore_blank_lines = 1; | |
| } | |
| else | |
| { | |
| $ignore_blank_lines = 0; | |
| } | |
| } | |
| if ($ignore_blank_lines == 0 || $_ ne "") | |
| { | |
| push (@{$comment->{TEXT}},$_); # Add the comment text | |
| } | |
| } | |
| else | |
| { | |
| # This isn't a well formatted comment: look for the next one | |
| $parse_state = 0; | |
| } | |
| } | |
| elsif ($parse_state == 2) # Finished reading in a comment | |
| { | |
| if ( /(WINAPIV|WINAPI|__cdecl|PASCAL|CALLBACK|FARPROC16)/ || | |
| /.*?\(/ ) | |
| { | |
| # Comment is followed by a function definition | |
| $parse_state = 4; # Fall through to read prototype | |
| } | |
| else | |
| { | |
| # Allow cpp directives and blank lines between the comment and prototype | |
| if ($extra_comment == 1) | |
| { | |
| # An extra comment not followed by a function definition | |
| $parse_state = 5; # Fall through to process comment | |
| } | |
| elsif (!/^\#/ && !/^ *$/ && !/^__ASM_GLOBAL_FUNC/) | |
| { | |
| # This isn't a well formatted comment: look for the next one | |
| if ($opt_verbose > 1) | |
| { | |
| print "Info: Function '",$comment->{COMMENT_NAME},"' not followed by prototype.\n"; | |
| } | |
| $parse_state = 0; | |
| } | |
| } | |
| } | |
| elsif ($parse_state == 3) # Reading in the first line of a comment | |
| { | |
| s/^ *\** *//; | |
| if ( /^([\@A-Za-z0-9_]+) +(\(|\[)([A-Za-z0-9_]+)\.(([0-9]+)|@)(\)|\])$/ ) | |
| { | |
| # Found a correctly formed "ApiName (DLLNAME.Ordinal)" line. | |
| $comment->{COMMENT_NAME} = $1; | |
| $comment->{DLL_NAME} = uc $3; | |
| $comment->{ORDINAL} = $4; | |
| $comment->{DLL_NAME} =~ s/^KERNEL$/KRNL386/; # Too many of these to ignore, _old_ code | |
| $parse_state = 1; | |
| } | |
| elsif ( /^([A-Za-z0-9_]+) +\{([A-Za-z0-9_]+)\}$/ ) | |
| { | |
| # Found a correctly formed "CommentTitle {DLLNAME}" line (extra documentation) | |
| $comment->{COMMENT_NAME} = $1; | |
| $comment->{DLL_NAME} = uc $2; | |
| $comment->{ORDINAL} = ""; | |
| $extra_comment = 1; | |
| $parse_state = 1; | |
| } | |
| else | |
| { | |
| # This isn't a well formatted comment: look for the next one | |
| $parse_state = 0; | |
| } | |
| } | |
| if ($parse_state == 4) # Reading in the function definition | |
| { | |
| push (@{$comment->{PROTOTYPE}},$_); | |
| # Strip comments from the line before checking for ')' | |
| my $stripped_line = $_; | |
| $stripped_line =~ s/ *(\/\* *)(.*?)( *\*\/ *)//; | |
| if ( $stripped_line =~ /\)/ ) | |
| { | |
| # Strip a blank last line | |
| my $last_line = pop(@{$comment->{TEXT}}); | |
| if (defined($last_line) && $last_line ne "") | |
| { | |
| # Put it back | |
| push (@{$comment->{TEXT}},$last_line); | |
| } | |
| if ($opt_output_empty != 0 && @{$comment->{TEXT}} == 0) | |
| { | |
| # Create a 'not implemented' comment | |
| @{$comment->{TEXT}} = ("fixme: This function has not yet been documented."); | |
| } | |
| $parse_state = 5; | |
| } | |
| } | |
| if ($parse_state == 5) # Processing the comment | |
| { | |
| # Process it, if it has any text | |
| if (@{$comment->{TEXT}} > 0) | |
| { | |
| if ($extra_comment == 1) | |
| { | |
| process_extra_comment($comment); | |
| } | |
| else | |
| { | |
| @{$comment->{TEXT}} = ("DESCRIPTION", @{$comment->{TEXT}}); | |
| process_comment($comment); | |
| } | |
| } | |
| elsif ($opt_verbose > 1 && $opt_output_empty == 0) | |
| { | |
| print "Info: Function '",$comment->{COMMENT_NAME},"' has no documentation.\n"; | |
| } | |
| $parse_state = 0; | |
| } | |
| } | |
| close(SOURCE_FILE); | |
| } | |
| # Standardise a comments text for consistency | |
| sub process_comment_text($) | |
| { | |
| my $comment = shift; | |
| my $i = 0; | |
| for (@{$comment->{TEXT}}) | |
| { | |
| if (! /^\|/ ) | |
| { | |
| # 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/\[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; | |
| s/False|false/FALSE/g; | |
| s/Null|null/NULL/g; | |
| # Preferred capitalisations | |
| s/ wine| WINE/ Wine/g; | |
| s/ API | api / Api /g; | |
| s/DLL|Dll/dll /g; | |
| s/ URL | url / Url /g; | |
| s/WIN16|win16/Win16/g; | |
| s/WIN32|win32/Win32/g; | |
| s/WIN64|win64/Win64/g; | |
| s/ ID | id / Id /g; | |
| # Grammar | |
| s/([a-z])\.([A-Z])/$1\. $2/g; # Space after full stop | |
| s/ \:/\:/g; # Colons to the left | |
| s/ \;/\;/g; # Semi-colons too | |
| # Common idioms | |
| s/^See ([A-Za-z0-9_]+)\.$/See $1\(\)\./; # Referring to A version from W | |
| s/^Unicode version of ([A-Za-z0-9_]+)\.$/See $1\(\)\./; # Ditto | |
| s/^64\-bit version of ([A-Za-z0-9_]+)\.$/See $1\(\)\./; # Referring to 32 bit version from 64 | |
| s/^PARAMETERS$/PARAMS/; # Name of parameter section should be 'PARAMS' | |
| # Trademarks | |
| s/( |\.)(MS|Microsoft|microsoft)( |\.)/$1Microsoft\(tm\)$3/g; | |
| s/( |\.)(Windows|windows|windoze|winblows)( |\.)/$1Windows\(tm\)$3/g; | |
| s/( |\.)(DOS|dos|msdos)( |\.)/$1MS-DOS\(tm\)$3/g; | |
| s/( |\.)(UNIX|Unix|unix)( |\.)/$1Unix\(tm\)$3/g; | |
| # Abbreviations | |
| 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++; | |
| } | |
| } | |
| # Standardise our comment and output it if it is suitable. | |
| sub process_comment($) | |
| { | |
| my $comment = shift; | |
| # Don't process this comment if the function isn't exported | |
| my $spec_details = $spec_files{$comment->{DLL_NAME}}[0]; | |
| if (!defined($spec_details)) | |
| { | |
| if ($opt_verbose > 2) | |
| { | |
| print "Warning: Function '".$comment->{COMMENT_NAME}."' belongs to '". | |
| $comment->{DLL_NAME}."' (not passed with -w): not processing it.\n"; | |
| } | |
| return; | |
| } | |
| if ($comment->{COMMENT_NAME} eq "@") | |
| { | |
| my $found = 0; | |
| # Find the name from the .spec file | |
| for (@{$spec_details->{EXPORTS}}) | |
| { | |
| if (@$_[0] eq $comment->{ORDINAL}) | |
| { | |
| $comment->{COMMENT_NAME} = @$_[2]; | |
| $found = 1; | |
| } | |
| } | |
| if ($found == 0) | |
| { | |
| # Create an implementation name | |
| $comment->{COMMENT_NAME} = $comment->{DLL_NAME}."_".$comment->{ORDINAL}; | |
| } | |
| } | |
| 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? | |
| $export_index = $implementation_names->{$comment->{COMMENT_NAME}}; | |
| } | |
| if (!defined($export_index)) | |
| { | |
| # This function doesn't appear to be exported. hmm. | |
| if ($opt_verbose > 2) | |
| { | |
| print "Warning: Function '".$comment->{COMMENT_NAME}."' claims to belong to '". | |
| $comment->{DLL_NAME}."' but is not exported by it: not processing it.\n"; | |
| } | |
| return; | |
| } | |
| # When the function is exported twice we have the second name below the first | |
| # (you see this a lot in ntdll, but also in some other places). | |
| my $first_line = ${@{$comment->{TEXT}}}[1]; | |
| if ( $first_line =~ /^(@|[A-Za-z0-9_]+) +(\(|\[)([A-Za-z0-9_]+)\.(([0-9]+)|@)(\)|\])$/ ) | |
| { | |
| # Found a second name - mark it as documented | |
| my $alt_index = $exported_names->{$1}; | |
| if (defined($alt_index)) | |
| { | |
| if ($opt_verbose > 2) | |
| { | |
| print "Info: Found alternate name '",$1,"\n"; | |
| } | |
| my $alt_export = @{$spec_details->{EXPORTS}}[$alt_index]; | |
| @$alt_export[4] |= $FLAG_DOCUMENTED; | |
| $spec_details->{NUM_DOCS}++; | |
| ${@{$comment->{TEXT}}}[1] = ""; | |
| } | |
| } | |
| if (@{$spec_details->{CURRENT_EXTRA}}) | |
| { | |
| # We have an extra comment that might be related to this one | |
| my $current_comment = ${@{$spec_details->{CURRENT_EXTRA}}}[0]; | |
| my $current_name = $current_comment->{COMMENT_NAME}; | |
| if ($comment->{COMMENT_NAME} =~ /^$current_name/ && $comment->{COMMENT_NAME} ne $current_name) | |
| { | |
| if ($opt_verbose > 2) | |
| { | |
| print "Linking ",$comment->{COMMENT_NAME}," to $current_name\n"; | |
| } | |
| # Add a reference to this comment to our extra comment | |
| push (@{$current_comment->{TEXT}}, $comment->{COMMENT_NAME}."()",""); | |
| } | |
| } | |
| # We want our docs generated using the implementation name, so they are unique | |
| my $export = @{$spec_details->{EXPORTS}}[$export_index]; | |
| $comment->{COMMENT_NAME} = @$export[3]; | |
| $comment->{ALT_NAME} = @$export[2]; | |
| # Mark the function as documented | |
| $spec_details->{NUM_DOCS}++; | |
| @$export[4] |= $FLAG_DOCUMENTED; | |
| # This file is used by the DLL - Make sure we get our contributors right | |
| push (@{$spec_details->{SOURCES}},$comment->{FILE}); | |
| # If we have parameter comments in the prototype, extract them | |
| my @parameter_comments; | |
| for (@{$comment->{PROTOTYPE}}) | |
| { | |
| s/ *\, */\,/g; # Strip spaces from around commas | |
| if ( s/ *(\/\* *)(.*?)( *\*\/ *)// ) # Strip out comment | |
| { | |
| my $parameter_comment = $2; | |
| if (!$parameter_comment =~ /^\[/ ) | |
| { | |
| # Add [IO] markers so we format the comment correctly | |
| $parameter_comment = "[fixme] ".$parameter_comment; | |
| } | |
| if ( /( |\*)([A-Za-z_]{1}[A-Za-z_0-9]*)(\,|\))/ ) | |
| { | |
| # Add the parameter name | |
| $parameter_comment = $2." ".$parameter_comment; | |
| } | |
| push (@parameter_comments, $parameter_comment); | |
| } | |
| } | |
| # If we extracted any prototype comments, add them to the comment text. | |
| if (@parameter_comments) | |
| { | |
| @parameter_comments = ("PARAMS", @parameter_comments); | |
| my @new_comment = (); | |
| my $inserted_params = 0; | |
| for (@{$comment->{TEXT}}) | |
| { | |
| if ( $inserted_params == 0 && /^[A-Z]+$/ ) | |
| { | |
| # Found a section header, so this is where we insert | |
| push (@new_comment, @parameter_comments); | |
| $inserted_params = 1; | |
| } | |
| push (@new_comment, $_); | |
| } | |
| if ($inserted_params == 0) | |
| { | |
| # Add them to the end | |
| push (@new_comment, @parameter_comments); | |
| } | |
| $comment->{TEXT} = [@new_comment]; | |
| } | |
| if ($opt_fussy == 1 && $opt_output_empty == 0) | |
| { | |
| # Reject any comment that doesn't have a description or a RETURNS section. | |
| # This is the default for now, 'coz many comments aren't suitable. | |
| my $found_returns = 0; | |
| my $found_description_text = 0; | |
| my $in_description = 0; | |
| for (@{$comment->{TEXT}}) | |
| { | |
| if ( /^RETURNS$/ ) | |
| { | |
| $found_returns = 1; | |
| $in_description = 0; | |
| } | |
| elsif ( /^DESCRIPTION$/ ) | |
| { | |
| $in_description = 1; | |
| } | |
| elsif ($in_description == 1) | |
| { | |
| if ( !/^[A-Z]+$/ ) | |
| { | |
| # Don't 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_]+)\.$/ ) | |
| { | |
| @$export[4] |= $FLAG_WPAIR; # Explicitly marked as W version | |
| $found_returns = 1; | |
| } | |
| elsif ( /^64\-bit version of ([A-Za-z0-9_]+)\.$/ ) | |
| { | |
| @$export[4] |= $FLAG_64PAIR; # Explicitly marked as 64 bit version | |
| $found_returns = 1; | |
| } | |
| $found_description_text = 1; | |
| } | |
| else | |
| { | |
| $in_description = 0; | |
| } | |
| } | |
| } | |
| if ($found_returns == 0 || $found_description_text == 0) | |
| { | |
| if ($opt_verbose > 2) | |
| { | |
| print "Info: Function '",$comment->{COMMENT_NAME},"' has no ", | |
| "description and/or RETURNS section, skipping\n"; | |
| } | |
| $spec_details->{NUM_DOCS}--; | |
| @$export[4] &= ~$FLAG_DOCUMENTED; | |
| return; | |
| } | |
| } | |
| process_comment_text($comment); | |
| # Strip the prototypes return value, call convention, name and brackets | |
| # (This leaves it as a list of types and names, or empty for void functions) | |
| my $prototype = join(" ", @{$comment->{PROTOTYPE}}); | |
| $prototype =~ s/ / /g; | |
| if ( $prototype =~ /(WINAPIV|WINAPI|__cdecl|PASCAL|CALLBACK|FARPROC16)/ ) | |
| { | |
| $prototype =~ s/^(.*?) (WINAPIV|WINAPI|__cdecl|PASCAL|CALLBACK|FARPROC16) (.*?)\( *(.*)/$4/; | |
| $comment->{RETURNS} = $1; | |
| } | |
| else | |
| { | |
| $prototype =~ s/^(.*?)([A-Za-z0-9_]+)\( *(.*)/$3/; | |
| $comment->{RETURNS} = $1; | |
| } | |
| $prototype =~ s/ *\).*//; # Strip end bracket | |
| $prototype =~ s/ *\* */\*/g; # Strip space around pointers | |
| $prototype =~ s/ *\, */\,/g; # Strip space around commas | |
| $prototype =~ s/^(void|VOID)$//; # If void, leave blank | |
| $prototype =~ s/\*([A-Za-z_])/\* $1/g; # Separate pointers from parameter name | |
| @{$comment->{PROTOTYPE}} = split ( /,/ ,$prototype); | |
| # FIXME: If we have no parameters, make sure we have a PARAMS: None. section | |
| # Find header file | |
| my $h_file = ""; | |
| if (@$export[4] & $FLAG_NONAME) | |
| { | |
| $h_file = "Exported by ordinal only. Use GetProcAddress() to obtain a pointer to the function."; | |
| } | |
| else | |
| { | |
| if ($comment->{COMMENT_NAME} 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) | |
| { | |
| $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) | |
| { | |
| $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. The function is either undocumented, or missing from Wine." | |
| } | |
| else | |
| { | |
| $h_file = "Defined in \"".$h_file."\"."; | |
| } | |
| } | |
| # Find source file | |
| my $c_file = $comment->{FILE}; | |
| if ($opt_wine_root_dir ne "") | |
| { | |
| my $cfile = $pwd."/".$c_file; # Current dir + file | |
| $cfile =~ s/(.+)(\/.*$)/$1/; # Strip the filename | |
| $cfile = `cd $cfile && pwd`; # Strip any relative parts (e.g. "../../") | |
| $cfile =~ s/\n//; # Strip newline | |
| my $newfile = $c_file; | |
| $newfile =~ s/(.+)(\/.*$)/$2/; # Strip all but the filename | |
| $cfile = $cfile."/".$newfile; # Append filename to base path | |
| $cfile =~ s/$opt_wine_root_dir//; # Get rid of the root directory | |
| $cfile =~ s/\/\//\//g; # Remove any double slashes | |
| $cfile =~ s/^\/+//; # Strip initial directory slash | |
| $c_file = $cfile; | |
| } | |
| $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}."\"."); | |
| } | |
| # Write out the documentation for the API | |
| output_comment($comment) | |
| } | |
| # process our extra comment and output it if it is suitable. | |
| sub process_extra_comment($) | |
| { | |
| my $comment = shift; | |
| my $spec_details = $spec_files{$comment->{DLL_NAME}}[0]; | |
| if (!defined($spec_details)) | |
| { | |
| if ($opt_verbose > 2) | |
| { | |
| print "Warning: Extra comment '".$comment->{COMMENT_NAME}."' belongs to '". | |
| $comment->{DLL_NAME}."' (not passed with -w): not processing it.\n"; | |
| } | |
| return; | |
| } | |
| # Check first to see if this is documentation for the DLL. | |
| if ($comment->{COMMENT_NAME} eq $comment->{DLL_NAME}) | |
| { | |
| if ($opt_verbose > 2) | |
| { | |
| print "Info: Found DLL documentation\n"; | |
| } | |
| for (@{$comment->{TEXT}}) | |
| { | |
| push (@{$spec_details->{DESCRIPTION}}, $_); | |
| } | |
| return; | |
| } | |
| # Add the comment to the DLL page as a link | |
| push (@{$spec_details->{EXTRA_COMMENTS}},$comment->{COMMENT_NAME}); | |
| # If we have a prototype, process as a regular comment | |
| if (@{$comment->{PROTOTYPE}}) | |
| { | |
| $comment->{ORDINAL} = "@"; | |
| # Add an index for the comment name | |
| $spec_details->{EXPORTED_NAMES}{$comment->{COMMENT_NAME}} = $spec_details->{NUM_EXPORTS}; | |
| # Add a fake exported entry | |
| $spec_details->{NUM_EXPORTS}++; | |
| my ($ordinal, $call_convention, $exported_name, $implementation_name, $documented) = | |
| ("@", "fake", $comment->{COMMENT_NAME}, $comment->{COMMENT_NAME}, 0); | |
| my @export = ($ordinal, $call_convention, $exported_name, $implementation_name, $documented); | |
| push (@{$spec_details->{EXPORTS}},[@export]); | |
| @{$comment->{TEXT}} = ("DESCRIPTION", @{$comment->{TEXT}}); | |
| process_comment($comment); | |
| return; | |
| } | |
| if ($opt_verbose > 0) | |
| { | |
| print "Processing ",$comment->{COMMENT_NAME},"\n"; | |
| } | |
| if (@{$spec_details->{CURRENT_EXTRA}}) | |
| { | |
| my $current_comment = ${@{$spec_details->{CURRENT_EXTRA}}}[0]; | |
| if ($opt_verbose > 0) | |
| { | |
| print "Processing old current: ",$current_comment->{COMMENT_NAME},"\n"; | |
| } | |
| # Output the current comment | |
| process_comment_text($current_comment); | |
| output_open_api_file($current_comment->{COMMENT_NAME}); | |
| output_api_header($current_comment); | |
| output_api_name($current_comment); | |
| output_api_comment($current_comment); | |
| output_api_footer($current_comment); | |
| output_close_api_file(); | |
| } | |
| if ($opt_verbose > 2) | |
| { | |
| print "Setting current to ",$comment->{COMMENT_NAME},"\n"; | |
| } | |
| my $comment_copy = | |
| { | |
| FILE => $comment->{FILE}, | |
| COMMENT_NAME => $comment->{COMMENT_NAME}, | |
| ALT_NAME => $comment->{ALT_NAME}, | |
| DLL_NAME => $comment->{DLL_NAME}, | |
| ORDINAL => $comment->{ORDINAL}, | |
| RETURNS => $comment->{RETURNS}, | |
| PROTOTYPE => [], | |
| TEXT => [], | |
| }; | |
| for (@{$comment->{TEXT}}) | |
| { | |
| push (@{$comment_copy->{TEXT}}, $_); | |
| } | |
| # Set this comment to be the current extra comment | |
| @{$spec_details->{CURRENT_EXTRA}} = ($comment_copy); | |
| } | |
| # Write a standardised comment out in the appropriate format | |
| sub output_comment($) | |
| { | |
| my $comment = shift; | |
| if ($opt_verbose > 0) | |
| { | |
| print "Processing ",$comment->{COMMENT_NAME},"\n"; | |
| } | |
| if ($opt_verbose > 4) | |
| { | |
| print "--PROTO--\n"; | |
| for (@{$comment->{PROTOTYPE}}) | |
| { | |
| print "'".$_."'\n"; | |
| } | |
| print "--COMMENT--\n"; | |
| for (@{$comment->{TEXT} }) | |
| { | |
| print $_."\n"; | |
| } | |
| } | |
| output_open_api_file($comment->{COMMENT_NAME}); | |
| output_api_header($comment); | |
| output_api_name($comment); | |
| output_api_synopsis($comment); | |
| output_api_comment($comment); | |
| output_api_footer($comment); | |
| output_close_api_file(); | |
| } | |
| # Write out an index file for each .spec processed | |
| sub process_index_files() | |
| { | |
| foreach my $spec_file (keys %spec_files) | |
| { | |
| my $spec_details = $spec_files{$spec_file}[0]; | |
| if (defined ($spec_details->{DLL_NAME})) | |
| { | |
| if (@{$spec_details->{CURRENT_EXTRA}}) | |
| { | |
| # We have an unwritten extra comment, write it | |
| my $current_comment = ${@{$spec_details->{CURRENT_EXTRA}}}[0]; | |
| process_extra_comment($current_comment); | |
| @{$spec_details->{CURRENT_EXTRA}} = (); | |
| } | |
| output_spec($spec_details); | |
| } | |
| } | |
| } | |
| # Write a spec files documentation out in the appropriate format | |
| sub output_spec($) | |
| { | |
| my $spec_details = shift; | |
| if ($opt_verbose > 2) | |
| { | |
| print "Writing:",$spec_details->{DLL_NAME},"\n"; | |
| } | |
| # Use the comment output functions for consistency | |
| my $comment = | |
| { | |
| FILE => $spec_details->{DLL_NAME}, | |
| COMMENT_NAME => $spec_details->{DLL_NAME}.".dll", | |
| ALT_NAME => $spec_details->{DLL_NAME}, | |
| DLL_NAME => "", | |
| ORDINAL => "", | |
| RETURNS => "", | |
| PROTOTYPE => [], | |
| TEXT => [], | |
| }; | |
| my $total_implemented = $spec_details->{NUM_FORWARDS} + $spec_details->{NUM_VARS} + | |
| $spec_details->{NUM_FUNCS}; | |
| my $percent_implemented = 0; | |
| if ($total_implemented) | |
| { | |
| $percent_implemented = $total_implemented / | |
| ($total_implemented + $spec_details->{NUM_STUBS}) * 100; | |
| } | |
| $percent_implemented = int($percent_implemented); | |
| my $percent_documented = 0; | |
| if ($spec_details->{NUM_DOCS}) | |
| { | |
| # Treat forwards and data as documented funcs for statistics | |
| $percent_documented = $spec_details->{NUM_DOCS} / $spec_details->{NUM_FUNCS} * 100; | |
| $percent_documented = int($percent_documented); | |
| } | |
| # Make a list of the contributors to this DLL. Do this only for the source | |
| # files that make up the DLL, because some directories specify multiple dlls. | |
| my @contributors; | |
| for (@{$spec_details->{SOURCES}}) | |
| { | |
| my $source_details = $source_files{$_}[0]; | |
| for (@{$source_details->{CONTRIBUTORS}}) | |
| { | |
| push (@contributors, $_); | |
| } | |
| } | |
| my %saw; | |
| @contributors = grep(!$saw{$_}++, @contributors); # remove dups, from perlfaq4 manpage | |
| @contributors = sort @contributors; | |
| # Remove duplicates and blanks | |
| for(my $i=0; $i<@contributors; $i++) | |
| { | |
| if ($i > 0 && ($contributors[$i] =~ /$contributors[$i-1]/ || $contributors[$i-1] eq "")) | |
| { | |
| $contributors[$i-1] = $contributors[$i]; | |
| } | |
| } | |
| undef %saw; | |
| @contributors = grep(!$saw{$_}++, @contributors); | |
| if ($opt_verbose > 3) | |
| { | |
| print "Contributors:\n"; | |
| for (@contributors) | |
| { | |
| print "'".$_."'\n"; | |
| } | |
| } | |
| my $contribstring = join (", ", @contributors); | |
| # Create the initial comment text | |
| @{$comment->{TEXT}} = ( | |
| "NAME", | |
| $comment->{COMMENT_NAME} | |
| ); | |
| # Add the description, if we have one | |
| if (@{$spec_details->{DESCRIPTION}}) | |
| { | |
| push (@{$comment->{TEXT}}, "DESCRIPTION"); | |
| for (@{$spec_details->{DESCRIPTION}}) | |
| { | |
| push (@{$comment->{TEXT}}, $_); | |
| } | |
| } | |
| # Add the statistics and contributors | |
| push (@{$comment->{TEXT}}, | |
| "STATISTICS", | |
| "Forwards: ".$spec_details->{NUM_FORWARDS}, | |
| "Variables: ".$spec_details->{NUM_VARS}, | |
| "Stubs: ".$spec_details->{NUM_STUBS}, | |
| "Functions: ".$spec_details->{NUM_FUNCS}, | |
| "Exports-Total: ".$spec_details->{NUM_EXPORTS}, | |
| "Implemented-Total: ".$total_implemented." (".$percent_implemented."%)", | |
| "Documented-Total: ".$spec_details->{NUM_DOCS}." (".$percent_documented."%)", | |
| "CONTRIBUTORS", | |
| "The following people hold copyrights on the source files comprising this dll:", | |
| "", | |
| $contribstring, | |
| "Note: This list may not be complete.", | |
| "For a complete listing, see the Files \"AUTHORS\" and \"Changelog\" in the Wine source tree.", | |
| "", | |
| ); | |
| if ($opt_output_format eq "h") | |
| { | |
| # Add the exports to the comment text | |
| push (@{$comment->{TEXT}},"EXPORTS"); | |
| my $exports = $spec_details->{EXPORTS}; | |
| for (@$exports) | |
| { | |
| my $line = ""; | |
| # @$_ => ordinal, call convention, exported name, implementation name, flags; | |
| if (@$_[1] eq "forward") | |
| { | |
| my $forward_dll = @$_[3]; | |
| $forward_dll =~ s/\.(.*)//; | |
| $line = @$_[2]." (forward to ".$1."() in ".$forward_dll."())"; | |
| } | |
| elsif (@$_[1] eq "extern") | |
| { | |
| $line = @$_[2]." (extern)"; | |
| } | |
| elsif (@$_[1] eq "stub") | |
| { | |
| $line = @$_[2]." (stub)"; | |
| } | |
| 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") | |
| { | |
| $line = @$_[2]." (data)"; | |
| } | |
| else | |
| { | |
| # A function | |
| if (@$_[4] & $FLAG_DOCUMENTED) | |
| { | |
| # Documented | |
| $line = @$_[2]." (implemented as ".@$_[3]."())"; | |
| if (@$_[2] ne @$_[3]) | |
| { | |
| $line = @$_[2]." (implemented as ".@$_[3]."())"; | |
| } | |
| else | |
| { | |
| $line = @$_[2]."()"; | |
| } | |
| if (!(@$_[4] & $FLAG_WPAIR)) | |
| { | |
| # This function should be indexed | |
| push (@index_entries_list, @$_[2].",".@$_[3]); | |
| } | |
| } | |
| else | |
| { | |
| $line = @$_[2]." (not documented)"; | |
| } | |
| } | |
| if ($line ne "") | |
| { | |
| push (@{$comment->{TEXT}}, $line, ""); | |
| } | |
| } | |
| # Add links to the extra documentation | |
| if (@{$spec_details->{EXTRA_COMMENTS}}) | |
| { | |
| push (@{$comment->{TEXT}}, "SEE ALSO"); | |
| my %htmp; | |
| @{$spec_details->{EXTRA_COMMENTS}} = grep(!$htmp{$_}++, @{$spec_details->{EXTRA_COMMENTS}}); | |
| for (@{$spec_details->{EXTRA_COMMENTS}}) | |
| { | |
| push (@{$comment->{TEXT}}, $_."()", ""); | |
| } | |
| } | |
| } | |
| # 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); | |
| output_api_comment($comment); | |
| output_api_footer($comment); | |
| output_close_api_file(); | |
| # Add this dll to the database of dll names | |
| my $output_file = $opt_output_directory."/dlls.db"; | |
| # Append the dllname to the output db of names | |
| open(DLLDB,">>$output_file") || die "Couldn't create $output_file\n"; | |
| print DLLDB $spec_details->{DLL_NAME},"\n"; | |
| close(DLLDB); | |
| if ($opt_output_format eq "s") | |
| { | |
| output_sgml_dll_file($spec_details); | |
| return; | |
| } | |
| } | |
| # | |
| # OUTPUT FUNCTIONS | |
| # ---------------- | |
| # Only these functions know anything about formatting for a specific | |
| # output type. The functions above work only with plain text. | |
| # This is to allow new types of output to be added easily. | |
| # Open the api file | |
| sub output_open_api_file($) | |
| { | |
| my $output_name = shift; | |
| $output_name = $opt_output_directory."/".$output_name; | |
| if ($opt_output_format eq "h") | |
| { | |
| $output_name = $output_name.".html"; | |
| } | |
| elsif ($opt_output_format eq "s") | |
| { | |
| $output_name = $output_name.".sgml"; | |
| } | |
| else | |
| { | |
| $output_name = $output_name.".".$opt_manual_section; | |
| } | |
| open(OUTPUT,">$output_name") || die "Couldn't create file '$output_name'\n"; | |
| } | |
| # Close the api file | |
| sub output_close_api_file() | |
| { | |
| close (OUTPUT); | |
| } | |
| # Output the api file header | |
| sub output_api_header($) | |
| { | |
| my $comment = shift; | |
| if ($opt_output_format eq "h") | |
| { | |
| print OUTPUT "<!-- Generated file - DO NOT EDIT! -->\n"; | |
| print OUTPUT "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n"; | |
| print OUTPUT "<HTML>\n<HEAD>\n"; | |
| print OUTPUT "<LINK REL=\"StyleSheet\" href=\"apidoc.css\" type=\"text/css\">\n"; | |
| print OUTPUT "<META NAME=\"GENERATOR\" CONTENT=\"tools/c2man.pl\">\n"; | |
| print OUTPUT "<META NAME=\"keywords\" CONTENT=\"Win32,Wine,API,$comment->{COMMENT_NAME}\">\n"; | |
| print OUTPUT "<TITLE>Wine API: $comment->{COMMENT_NAME}</TITLE>\n</HEAD>\n<BODY>\n"; | |
| } | |
| elsif ($opt_output_format eq "s") | |
| { | |
| print OUTPUT "<!-- Generated file - DO NOT EDIT! -->\n", | |
| "<sect1>\n", | |
| "<title>$comment->{COMMENT_NAME}</title>\n"; | |
| } | |
| else | |
| { | |
| print OUTPUT ".\\\" -*- nroff -*-\n.\\\" Generated file - DO NOT EDIT!\n". | |
| ".TH ",$comment->{COMMENT_NAME}," ",$opt_manual_section," \"",$date,"\" \"". | |
| "Wine API\" \"Wine API\"\n"; | |
| } | |
| } | |
| sub output_api_footer($) | |
| { | |
| if ($opt_output_format eq "h") | |
| { | |
| print OUTPUT "<hr><p><i class=\"copy\">Copyright © ".$year." The Wine Project.". | |
| " 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") | |
| { | |
| print OUTPUT "</sect1>\n"; | |
| return; | |
| } | |
| else | |
| { | |
| } | |
| } | |
| sub output_api_section_start($$) | |
| { | |
| my $comment = shift; | |
| my $section_name = shift; | |
| if ($opt_output_format eq "h") | |
| { | |
| print OUTPUT "\n<p><h2 class=\"section\">",$section_name,"</h2></p>\n"; | |
| } | |
| elsif ($opt_output_format eq "s") | |
| { | |
| print OUTPUT "<bridgehead>",$section_name,"</bridgehead>\n"; | |
| } | |
| else | |
| { | |
| print OUTPUT "\n\.SH ",$section_name,"\n"; | |
| } | |
| } | |
| sub output_api_section_end() | |
| { | |
| # Not currently required by any output formats | |
| } | |
| sub output_api_name($) | |
| { | |
| my $comment = shift; | |
| output_api_section_start($comment,"NAME"); | |
| my $dll_ordinal = ""; | |
| if ($comment->{ORDINAL} ne "") | |
| { | |
| $dll_ordinal = "(".$comment->{DLL_NAME}.".".$comment->{ORDINAL}.")"; | |
| } | |
| if ($opt_output_format eq "h") | |
| { | |
| print OUTPUT "<p><b class=\"func_name\">",$comment->{COMMENT_NAME}, | |
| "</b> <i class=\"dll_ord\">", | |
| ,$dll_ordinal,"</i></p>\n"; | |
| } | |
| elsif ($opt_output_format eq "s") | |
| { | |
| print OUTPUT "<para>\n <command>",$comment->{COMMENT_NAME},"</command> <emphasis>", | |
| $dll_ordinal,"</emphasis>\n</para>\n"; | |
| } | |
| else | |
| { | |
| print OUTPUT "\\fB",$comment->{COMMENT_NAME},"\\fR ",$dll_ordinal; | |
| } | |
| output_api_section_end(); | |
| } | |
| sub output_api_synopsis($) | |
| { | |
| my $comment = shift; | |
| my @fmt; | |
| output_api_section_start($comment,"SYNOPSIS"); | |
| if ($opt_output_format eq "h") | |
| { | |
| print OUTPUT "<p><pre class=\"proto\">\n ", $comment->{RETURNS}," ",$comment->{COMMENT_NAME},"\n (\n"; | |
| @fmt = ("", "\n", "<tt class=\"param\">", "</tt>"); | |
| } | |
| elsif ($opt_output_format eq "s") | |
| { | |
| print OUTPUT "<screen>\n ",$comment->{RETURNS}," ",$comment->{COMMENT_NAME},"\n (\n"; | |
| @fmt = ("", "\n", "<emphasis>", "</emphasis>"); | |
| } | |
| else | |
| { | |
| print OUTPUT $comment->{RETURNS}," ",$comment->{COMMENT_NAME},"\n (\n"; | |
| @fmt = ("", "\n", "\\fI", "\\fR"); | |
| } | |
| # Since our prototype is output in a pre-formatted block, line up the | |
| # parameters and parameter comments in the same column. | |
| # First caluculate where the columns should start | |
| my $biggest_length = 0; | |
| for(my $i=0; $i < @{$comment->{PROTOTYPE}}; $i++) | |
| { | |
| my $line = ${@{$comment->{PROTOTYPE}}}[$i]; | |
| if ($line =~ /(.+?)([A-Za-z_][A-Za-z_0-9]*)$/) | |
| { | |
| my $length = length $1; | |
| if ($length > $biggest_length) | |
| { | |
| $biggest_length = $length; | |
| } | |
| } | |
| } | |
| # Now pad the string with blanks | |
| for(my $i=0; $i < @{$comment->{PROTOTYPE}}; $i++) | |
| { | |
| my $line = ${@{$comment->{PROTOTYPE}}}[$i]; | |
| if ($line =~ /(.+?)([A-Za-z_][A-Za-z_0-9]*)$/) | |
| { | |
| my $pad_len = $biggest_length - length $1; | |
| my $padding = " " x ($pad_len); | |
| ${@{$comment->{PROTOTYPE}}}[$i] = $1.$padding.$2; | |
| } | |
| } | |
| for(my $i=0; $i < @{$comment->{PROTOTYPE}}; $i++) | |
| { | |
| # Format the parameter name | |
| my $line = ${@{$comment->{PROTOTYPE}}}[$i]; | |
| my $comma = ($i == @{$comment->{PROTOTYPE}}-1) ? "" : ","; | |
| $line =~ s/(.+?)([A-Za-z_][A-Za-z_0-9]*)$/ $fmt[0]$1$fmt[2]$2$fmt[3]$comma$fmt[1]/; | |
| print OUTPUT $line; | |
| } | |
| if ($opt_output_format eq "h") | |
| { | |
| print OUTPUT " )\n\n</pre></p>\n"; | |
| } | |
| elsif ($opt_output_format eq "s") | |
| { | |
| print OUTPUT " )\n</screen>\n"; | |
| } | |
| else | |
| { | |
| print OUTPUT " )\n"; | |
| } | |
| output_api_section_end(); | |
| } | |
| sub output_api_comment($) | |
| { | |
| my $comment = shift; | |
| my $open_paragraph = 0; | |
| my $open_raw = 0; | |
| my $param_docs = 0; | |
| my @fmt; | |
| if ($opt_output_format eq "h") | |
| { | |
| @fmt = ("<p>", "</p>\n", "<tt class=\"const\">", "</tt>", "<b class=\"emp\">", "</b>", | |
| "<tt class=\"coderef\">", "</tt>", "<tt class=\"param\">", "</tt>", | |
| "<i class=\"in_out\">", "</i>", "<pre class=\"raw\">\n", "</pre>\n", | |
| "<table class=\"tab\"><colgroup><col><col><col></colgroup><tbody>\n", | |
| "</tbody></table>\n","<tr><td>","</td></tr>\n","</td>","</td><td>"); | |
| } | |
| elsif ($opt_output_format eq "s") | |
| { | |
| @fmt = ("<para>\n","\n</para>\n","<constant>","</constant>","<emphasis>","</emphasis>", | |
| "<command>","</command>","<constant>","</constant>","<emphasis>","</emphasis>", | |
| "<screen>\n","</screen>\n", | |
| "<informaltable frame=\"none\">\n<tgroup cols=\"3\">\n<tbody>\n", | |
| "</tbody>\n</tgroup>\n</informaltable>\n","<row><entry>","</entry></row>\n", | |
| "</entry>","</entry><entry>"); | |
| } | |
| else | |
| { | |
| @fmt = ("\.PP\n", "\n", "\\fB", "\\fR", "\\fB", "\\fR", "\\fB", "\\fR", "\\fI", "\\fR", | |
| "\\fB", "\\fR ", "", "", "", "","","\n.PP\n","",""); | |
| } | |
| # Extract the parameter names | |
| my @parameter_names; | |
| for (@{$comment->{PROTOTYPE}}) | |
| { | |
| if ( /(.+?)([A-Za-z_][A-Za-z_0-9]*)$/ ) | |
| { | |
| push (@parameter_names, $2); | |
| } | |
| } | |
| for (@{$comment->{TEXT}}) | |
| { | |
| if ($opt_output_format eq "h" || $opt_output_format eq "s") | |
| { | |
| # Map special characters | |
| s/\&/\&/g; | |
| s/\</\</g; | |
| s/\>/\>/g; | |
| s/\([Cc]\)/\©/g; | |
| s/\(tm\)/®/; | |
| } | |
| if ( s/^\|// ) | |
| { | |
| # Raw output | |
| if ($open_raw == 0) | |
| { | |
| if ($open_paragraph == 1) | |
| { | |
| # Close the open paragraph | |
| print OUTPUT $fmt[1]; | |
| $open_paragraph = 0; | |
| } | |
| # Start raw output | |
| print OUTPUT $fmt[12]; | |
| $open_raw = 1; | |
| } | |
| if ($opt_output_format eq "") | |
| { | |
| print OUTPUT ".br\n"; # Prevent 'man' running these lines together | |
| } | |
| print OUTPUT $_,"\n"; | |
| } | |
| else | |
| { | |
| if ($opt_output_format eq "h") | |
| { | |
| # Link to the file in WineHQ cvs | |
| s/^(Implemented in \")(.+?)(\"\.)/$1$2$3 http:\/\/source.winehq.org\/source\/$2/g; | |
| } | |
| # Highlight strings | |
| s/(\".+?\")/$fmt[2]$1$fmt[3]/g; | |
| # Highlight literal chars | |
| s/(\'.\')/$fmt[2]$1$fmt[3]/g; | |
| s/(\'.{2}\')/$fmt[2]$1$fmt[3]/g; | |
| # Highlight numeric constants | |
| s/( |\-|\+|\.|\()([0-9\-\.]+)( |\-|$|\.|\,|\*|\?|\))/$1$fmt[2]$2$fmt[3]$3/g; | |
| # Leading cases ("xxxx:","-") start new paragraphs & are emphasised | |
| # FIXME: Using bullet points for leading '-' would look nicer. | |
| if ($open_paragraph == 1) | |
| { | |
| s/^(\-)/$fmt[1]$fmt[0]$fmt[4]$1$fmt[5]/; | |
| s/^([[A-Za-z\-]+\:)/$fmt[1]$fmt[0]$fmt[4]$1$fmt[5]/; | |
| } | |
| else | |
| { | |
| s/^(\-)/$fmt[4]$1$fmt[5]/; | |
| s/^([[A-Za-z\-]+\:)/$fmt[4]$1$fmt[5]/; | |
| } | |
| if ($opt_output_format eq "h") | |
| { | |
| # 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 | |
| s/(http\:\/\/)(.+?)($| )/<a href\=\"$1$2\">$2<\/a>$3/g; | |
| } | |
| else | |
| { | |
| if ($opt_output_format eq "") | |
| { | |
| # Give the man section for API calls | |
| s/ ([A-Za-z_]+[A-Za-z_0-9]+)\(\)/ $fmt[6]$1\($opt_manual_section\)$fmt[7]/g; | |
| } | |
| else | |
| { | |
| # Highlight API calls | |
| s/ ([A-Za-z_]+[A-Za-z_0-9]+\(\))/ $fmt[6]$1$fmt[7]/g; | |
| } | |
| # And references to COM objects | |
| s/ (I[A-Z]{1}[A-Za-z0-9_]+) (Object|object|Interface|interface)/ $fmt[6]$1$fmt[7] $2/g; | |
| } | |
| if ($open_raw == 1) | |
| { | |
| # Finish the raw output | |
| print OUTPUT $fmt[13]; | |
| $open_raw = 0; | |
| } | |
| if ( /^[A-Z]+$/ || /^SEE ALSO$/ ) | |
| { | |
| # Start of a new section | |
| if ($open_paragraph == 1) | |
| { | |
| if ($param_docs == 1) | |
| { | |
| print OUTPUT $fmt[17],$fmt[15]; | |
| } | |
| else | |
| { | |
| print OUTPUT $fmt[1]; | |
| } | |
| $open_paragraph = 0; | |
| } | |
| output_api_section_start($comment,$_); | |
| if ( /^PARAMS$/ ) | |
| { | |
| print OUTPUT $fmt[14]; | |
| $param_docs = 1; | |
| } | |
| else | |
| { | |
| #print OUTPUT $fmt[15]; | |
| $param_docs = 0; | |
| } | |
| } | |
| elsif ( /^$/ ) | |
| { | |
| # Empty line, indicating a new paragraph | |
| if ($open_paragraph == 1) | |
| { | |
| if ($param_docs == 0) | |
| { | |
| print OUTPUT $fmt[1]; | |
| $open_paragraph = 0; | |
| } | |
| } | |
| } | |
| else | |
| { | |
| if ($param_docs == 1) | |
| { | |
| if ($open_paragraph == 1) | |
| { | |
| # For parameter docs, put each parameter into a new paragraph/table row | |
| print OUTPUT $fmt[17]; | |
| $open_paragraph = 0; | |
| } | |
| s/(\[.+\])( *)/$fmt[19]$fmt[10]$1$fmt[11]$fmt[19] /; # Format In/Out | |
| } | |
| else | |
| { | |
| # Within paragraph lines, prevent lines running together | |
| $_ = $_." "; | |
| } | |
| # 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; | |
| } | |
| # Structure dereferences include the dereferenced member | |
| s/(\-\>[A-Za-z_]+)/$fmt[8]$1$fmt[9]/g; | |
| s/(\-\>\;[A-Za-z_]+)/$fmt[8]$1$fmt[9]/g; | |
| if ($open_paragraph == 0) | |
| { | |
| if ($param_docs == 1) | |
| { | |
| print OUTPUT $fmt[16]; | |
| } | |
| else | |
| { | |
| print OUTPUT $fmt[0]; | |
| } | |
| $open_paragraph = 1; | |
| } | |
| # Anything in all uppercase on its own gets emphasised | |
| s/(^|[ \.\,\(\[\|\=])([A-Z]+?[A-Z0-9_]+)($|[ \.\,\*\?\|\)\=\'])/$1$fmt[6]$2$fmt[7]$3/g; | |
| print OUTPUT $_; | |
| } | |
| } | |
| } | |
| if ($open_raw == 1) | |
| { | |
| print OUTPUT $fmt[13]; | |
| } | |
| if ($open_paragraph == 1) | |
| { | |
| print OUTPUT $fmt[1]; | |
| } | |
| } | |
| # Create the master index file | |
| sub output_master_index_files() | |
| { | |
| if ($opt_output_format eq "") | |
| { | |
| 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 = | |
| { | |
| FILE => "", | |
| COMMENT_NAME => "The Wine Api Guide", | |
| ALT_NAME => "The Wine Api Guide", | |
| DLL_NAME => "", | |
| ORDINAL => "", | |
| RETURNS => "", | |
| PROTOTYPE => [], | |
| TEXT => [], | |
| }; | |
| if ($opt_output_format eq "s") | |
| { | |
| $comment->{COMMENT_NAME} = "Introduction"; | |
| $comment->{ALT_NAME} = "Introduction", | |
| } | |
| elsif ($opt_output_format eq "h") | |
| { | |
| @{$comment->{TEXT}} = ( | |
| "NAME", | |
| $comment->{COMMENT_NAME}, | |
| "INTRODUCTION", | |
| ); | |
| } | |
| # Create the initial comment text | |
| push (@{$comment->{TEXT}}, | |
| "This document describes the Api calls made available", | |
| "by Wine. They are grouped by the dll that exports them.", | |
| "", | |
| "Please do not edit this document, since it is generated automatically", | |
| "from the Wine source code tree. Details on updating this documentation", | |
| "are given in the \"Wine Developers Guide\".", | |
| "CONTRIBUTORS", | |
| "Api documentation is generally written by the person who ", | |
| "implements a given Api call. Authors of each dll are listed in the overview ", | |
| "section for that dll. Additional contributors who have updated source files ", | |
| "but have not entered their names in a copyright statement are noted by an ", | |
| "entry in the file \"Changelog\" from the Wine source code distribution.", | |
| "" | |
| ); | |
| # Read in all dlls from the database of dll names | |
| my $input_file = $opt_output_directory."/dlls.db"; | |
| my @dlls = `cat $input_file|sort|uniq`; | |
| if ($opt_output_format eq "h") | |
| { | |
| # 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", | |
| "Each dll provided by Wine is documented individually. The following dlls are provided :", | |
| "" | |
| ); | |
| # Add the dlls to the comment | |
| for (@dlls) | |
| { | |
| $_ =~ s/(\..*)?\n/\(\)/; | |
| push (@{$comment->{TEXT}}, $_, ""); | |
| } | |
| output_open_api_file("index"); | |
| } | |
| elsif ($opt_output_format eq "s") | |
| { | |
| # Just write this as the initial blurb, with a chapter heading | |
| output_open_api_file("blurb"); | |
| print OUTPUT "<chapter id =\"blurb\">\n<title>Introduction to The Wine Api Guide</title>\n" | |
| } | |
| # Write out the document | |
| output_api_header($comment); | |
| output_api_comment($comment); | |
| output_api_footer($comment); | |
| if ($opt_output_format eq "s") | |
| { | |
| print OUTPUT "</chapter>\n" # finish the chapter | |
| } | |
| output_close_api_file(); | |
| if ($opt_output_format eq "s") | |
| { | |
| output_sgml_master_file(\@dlls); | |
| return; | |
| } | |
| if ($opt_output_format eq "h") | |
| { | |
| output_html_index_files(); | |
| output_html_stylesheet(); | |
| return; | |
| } | |
| } | |
| # Write the master wine-api.sgml, linking it to each dll. | |
| sub output_sgml_master_file($) | |
| { | |
| my $dlls = shift; | |
| output_open_api_file("wine-api"); | |
| print OUTPUT "<!-- Generated file - DO NOT EDIT! -->\n"; | |
| print OUTPUT "<!doctype book PUBLIC \"-//OASIS//DTD DocBook V3.1//EN\" [\n\n"; | |
| print OUTPUT "<!entity blurb SYSTEM \"blurb.sgml\">\n"; | |
| # List the entities | |
| for (@$dlls) | |
| { | |
| $_ =~ s/(\..*)?\n//; | |
| print OUTPUT "<!entity ",$_," SYSTEM \"",$_,".sgml\">\n" | |
| } | |
| print OUTPUT "]>\n\n<book id=\"index\">\n<bookinfo><title>The Wine Api Guide</title></bookinfo>\n\n"; | |
| print OUTPUT " &blurb;\n"; | |
| for (@$dlls) | |
| { | |
| print OUTPUT " &",$_,";\n" | |
| } | |
| print OUTPUT "\n\n</book>\n"; | |
| output_close_api_file(); | |
| } | |
| # Produce the sgml for the dll chapter from the generated files | |
| sub output_sgml_dll_file($) | |
| { | |
| my $spec_details = shift; | |
| # Make a list of all the documentation files to include | |
| my $exports = $spec_details->{EXPORTS}; | |
| my @source_files = (); | |
| for (@$exports) | |
| { | |
| # @$_ => ordinal, call convention, exported name, implementation name, documented; | |
| if (@$_[1] ne "forward" && @$_[1] ne "extern" && @$_[1] ne "stub" && @$_[1] ne "equate" && | |
| @$_[1] ne "variable" && @$_[1] ne "fake" && @$_[4] & 1) | |
| { | |
| # A documented function | |
| push (@source_files,@$_[3]); | |
| } | |
| } | |
| push (@source_files,@{$spec_details->{EXTRA_COMMENTS}}); | |
| @source_files = sort @source_files; | |
| # create a new chapter for this dll | |
| my $tmp_name = $opt_output_directory."/".$spec_details->{DLL_NAME}.".tmp"; | |
| open(OUTPUT,">$tmp_name") || die "Couldn't create $tmp_name\n"; | |
| print OUTPUT "<chapter>\n<title>$spec_details->{DLL_NAME}</title>\n"; | |
| output_close_api_file(); | |
| # Add the sorted documentation, cleaning up as we go | |
| `cat $opt_output_directory/$spec_details->{DLL_NAME}.sgml >>$tmp_name`; | |
| for (@source_files) | |
| { | |
| `cat $opt_output_directory/$_.sgml >>$tmp_name`; | |
| `rm -f $opt_output_directory/$_.sgml`; | |
| } | |
| # close the chapter, and overwite the dll source | |
| open(OUTPUT,">>$tmp_name") || die "Couldn't create $tmp_name\n"; | |
| print OUTPUT "</chapter>\n"; | |
| close OUTPUT; | |
| `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() | |
| { | |
| if ($opt_output_format ne "h") | |
| { | |
| return; | |
| } | |
| my $css; | |
| ($css = <<HERE_TARGET) =~ s/^\s+//gm; | |
| /* | |
| * Default styles for Wine HTML Documentation. | |
| * | |
| * This style sheet should be altered to suit your needs/taste. | |
| */ | |
| BODY { /* Page body */ | |
| background-color: white; | |
| color: black; | |
| font-family: Tahoma,sans-serif; | |
| font-style: normal; | |
| font-size: 10pt; | |
| } | |
| a:link { color: #4444ff; } /* Links */ | |
| a:visited { color: #333377 } | |
| a:active { color: #0000dd } | |
| H2.section { /* Section Headers */ | |
| font-family: sans-serif; | |
| color: #777777; | |
| background-color: #F0F0FE; | |
| margin-left: 0.2in; | |
| margin-right: 1.0in; | |
| } | |
| b.func_name { /* Function Name */ | |
| font-size: 10pt; | |
| font-style: bold; | |
| } | |
| i.dll_ord { /* Italicised DLL+ordinal */ | |
| color: #888888; | |
| font-family: sans-serif; | |
| font-size: 8pt; | |
| } | |
| p { /* Paragraphs */ | |
| margin-left: 0.5in; | |
| margin-right: 0.5in; | |
| } | |
| table { /* tables */ | |
| margin-left: 0.5in; | |
| margin-right: 0.5in; | |
| } | |
| pre.proto /* API Function prototype */ | |
| { | |
| border-style: solid; | |
| border-width: 1px; | |
| border-color: #777777; | |
| background-color: #F0F0BB; | |
| color: black; | |
| font-size: 10pt; | |
| 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; | |
| } | |
| tt.const { /* Constant */ | |
| color: red; | |
| } | |
| i.in_out { /* In/Out */ | |
| font-size: 8pt; | |
| color: grey; | |
| } | |
| tt.coderef { /* Code in description text */ | |
| color: darkgreen; | |
| } | |
| b.emp /* Emphasis */ { | |
| font-style: bold; | |
| color: darkblue; | |
| } | |
| i.footer { /* Footer */ | |
| font-family: sans-serif; | |
| font-size: 6pt; | |
| color: darkgrey; | |
| } | |
| HERE_TARGET | |
| my $output_file = "$opt_output_directory/apidoc.css"; | |
| open(CSS,">$output_file") || die "Couldn't create the file $output_file\n"; | |
| print CSS $css; | |
| close(CSS); | |
| } | |
| sub usage() | |
| { | |
| print "\nCreate API Documentation from Wine source code.\n\n", | |
| "Usage: c2man.pl [options] {-w <spec>} {-I <include>} {<source>}\n", | |
| "Where: <spec> is a .spec file giving a DLL's exports.\n", | |
| " <include> is an include directory used by the DLL.\n", | |
| " <source> is a source file of the DLL.\n", | |
| " The above can be given multiple times on the command line, as appropriate.\n", | |
| "Options:\n", | |
| " -Th : Output HTML instead of a man page\n", | |
| " -Ts : Output SGML (Docbook source) instead of a man page\n", | |
| " -o <dir> : Create output in <dir>, default is \"",$opt_output_directory,"\"\n", | |
| " -s <sect>: Set manual section to <sect>, default is ",$opt_manual_section,"\n", | |
| " -e : Output \"FIXME\" documentation from empty comments.\n", | |
| " -v : Verbosity. Can be given more than once for more detail.\n"; | |
| } | |
| # | |
| # Main | |
| # | |
| # Print usage if we're called with no args | |
| if( @ARGV == 0) | |
| { | |
| usage(); | |
| } | |
| # Process command line options | |
| while(defined($_ = shift @ARGV)) | |
| { | |
| if( s/^-// ) | |
| { | |
| # An option. | |
| for ($_) | |
| { | |
| /^o$/ && do { $opt_output_directory = shift @ARGV; last; }; | |
| s/^S// && do { $opt_manual_section = $_; last; }; | |
| /^Th$/ && do { $opt_output_format = "h"; last; }; | |
| /^Ts$/ && do { $opt_output_format = "s"; last; }; | |
| /^v$/ && do { $opt_verbose++; last; }; | |
| /^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 { 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 = $_; } | |
| else { $opt_wine_root_dir = `cd $pwd/$_ && pwd`; } | |
| $opt_wine_root_dir =~ s/\n//; | |
| $opt_wine_root_dir =~ s/\/\//\//g; | |
| if (! $opt_wine_root_dir =~ /\/$/ ) { $opt_wine_root_dir = $opt_wine_root_dir."/"; }; | |
| last; | |
| }; | |
| die "Unrecognised option $_\n"; | |
| } | |
| } | |
| else | |
| { | |
| # A source file. | |
| push (@opt_source_file_list, $_); | |
| } | |
| } | |
| # Remove duplicate include directories | |
| my %htmp; | |
| @opt_header_file_list = grep(!$htmp{$_}++, @opt_header_file_list); | |
| if ($opt_verbose > 3) | |
| { | |
| print "Output dir:'".$opt_output_directory."'\n"; | |
| print "Section :'".$opt_manual_section."'\n"; | |
| print "Format :'".$opt_output_format."'\n"; | |
| print "Root :'".$opt_wine_root_dir."'\n"; | |
| print "Spec files:'@opt_spec_file_list'\n"; | |
| print "Includes :'@opt_header_file_list'\n"; | |
| print "Sources :'@opt_source_file_list'\n"; | |
| } | |
| if (@opt_spec_file_list == 0) | |
| { | |
| exit 0; # Don't bother processing non-dll files | |
| } | |
| # Read in each .spec files exports and other details | |
| while(my $spec_file = shift @opt_spec_file_list) | |
| { | |
| process_spec_file($spec_file); | |
| } | |
| if ($opt_verbose > 3) | |
| { | |
| foreach my $spec_file ( keys %spec_files ) | |
| { | |
| print "in '$spec_file':\n"; | |
| my $spec_details = $spec_files{$spec_file}[0]; | |
| my $exports = $spec_details->{EXPORTS}; | |
| for (@$exports) | |
| { | |
| print @$_[0].",".@$_[1].",".@$_[2].",".@$_[3]."\n"; | |
| } | |
| } | |
| } | |
| # Extract and output the comments from each source file | |
| while(defined($_ = shift @opt_source_file_list)) | |
| { | |
| process_source_file($_); | |
| } | |
| # Write the index files for each spec | |
| process_index_files(); | |
| # Write the master index file | |
| output_master_index_files(); | |
| exit 0; |