3 # Generate API documentation. See http://www.winehq.org/docs/winedev-guide/api-docs for details.
5 # Copyright (C) 2000 Mike McCormack
6 # Copyright (C) 2003 Jon Griffiths
8 # This library is free software; you can redistribute it and/or
9 # modify it under the terms of the GNU Lesser General Public
10 # License as published by the Free Software Foundation; either
11 # version 2.1 of the License, or (at your option) any later version.
13 # This library is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 # Lesser General Public License for more details.
18 # You should have received a copy of the GNU Lesser General Public
19 # License along with this library; if not, write to the Free Software
20 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 # Consolidate A+W pairs together, and only write one doc, without the suffix
24 # Implement automatic docs of structs/defines in headers
25 # SGML gurus - feel free to smarten up the SGML.
26 # Add any other relevant information for the dll - imports etc
27 # Should we have a special output mode for WineHQ?
32 # Function flags. most of these come from the spec flags
33 my $FLAG_DOCUMENTED = 1;
36 my $FLAG_REGISTER = 8;
37 my $FLAG_APAIR = 16; # The A version of a matching W function
38 my $FLAG_WPAIR = 32; # The W version of a matching A function
39 my $FLAG_64PAIR = 64; # The 64 bit version of a matching 32 bit function
41 # Export list slot labels.
42 my $EXPORT_ORDINAL = 0; # Ordinal.
43 my $EXPORT_CALL = 1; # Call type.
44 my $EXPORT_EXPNAME = 2; # Export name.
45 my $EXPORT_IMPNAME = 3; # Implementation name.
46 my $EXPORT_FLAGS = 4; # Flags - see above.
49 my $opt_output_directory = "man3w"; # All default options are for nroff (man pages)
50 my $opt_manual_section = "3w";
51 my $opt_source_dir = "";
52 my $opt_wine_root_dir = "";
53 my $opt_output_format = ""; # '' = nroff, 'h' = html, 's' = sgml, 'x' = xml
54 my $opt_output_empty = 0; # Non-zero = Create 'empty' comments (for every implemented function)
55 my $opt_fussy = 1; # Non-zero = Create only if we have a RETURNS section
56 my $opt_verbose = 0; # >0 = verbosity. Can be given multiple times (for debugging)
57 my @opt_header_file_list = ();
58 my @opt_spec_file_list = ();
59 my @opt_source_file_list = ();
61 # All the collected details about all the .spec files being processed
63 # All the collected details about all the source files being processed
65 # All documented functions that are to be placed in the index
66 my @index_entries_list = ();
71 my @datetime = localtime;
72 my @months = ( "Jan", "Feb", "Mar", "Apr", "May", "Jun",
73 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" );
74 my $year = $datetime[5] + 1900;
75 my $date = "$months[$datetime[4]] $year";
78 sub output_api_comment($);
79 sub output_api_footer($);
80 sub output_api_header($);
81 sub output_api_name($);
82 sub output_api_synopsis($);
83 sub output_close_api_file();
84 sub output_comment($);
85 sub output_html_index_files();
86 sub output_html_stylesheet();
87 sub output_open_api_file($);
88 sub output_sgml_dll_file($);
89 sub output_xml_dll_file($);
90 sub output_sgml_master_file($);
91 sub output_xml_master_file($);
93 sub process_comment($);
94 sub process_extra_comment($);
97 # Generate the list of exported entries for the dll
98 sub process_spec_file($)
100 my $spec_name = shift;
101 my ($dll_name, $dll_ext) = split(/\./, $spec_name);
102 $dll_ext = "dll" if ( $dll_ext eq "spec" );
103 my $uc_dll_name = uc $dll_name;
108 DLL_NAME => $dll_name,
120 EXPORTED_NAMES => { },
121 IMPLEMENTATION_NAMES => { },
122 EXTRA_COMMENTS => [ ],
123 CURRENT_EXTRA => [ ] ,
126 if ($opt_verbose > 0)
128 print "Processing ".$spec_name."\n";
131 # We allow opening to fail just to cater for the peculiarities of
132 # the Wine build system. This doesn't hurt, in any case
133 open(SPEC_FILE, "<$spec_name")
134 || (($opt_source_dir ne "")
135 && open(SPEC_FILE, "<$opt_source_dir/$spec_name"))
140 s/^\s+//; # Strip leading space
141 s/\s+\n$/\n/; # Strip trailing space
142 s/\s+/ /g; # Strip multiple tabs & spaces to a single space
143 s/\s*#.*//; # Strip comments
144 s/\(.*\)/ /; # Strip arguments
145 s/\s+/ /g; # Strip multiple tabs & spaces to a single space (again)
146 s/\n$//; # Strip newline
151 $flags |= $FLAG_NONAME;
155 $flags |= $FLAG_I386;
159 $flags |= $FLAG_REGISTER;
161 s/ \-[a-z0-9=_]+//g; # Strip flags
163 if( /^(([0-9]+)|@) / )
165 # This line contains an exported symbol
166 my ($ordinal, $call_convention, $exported_name, $implementation_name) = split(' ');
168 for ($call_convention)
170 /^(cdecl|stdcall|varargs|pascal)$/
171 && do { $spec_details->{NUM_FUNCS}++; last; };
172 /^(variable|equate)$/
173 && do { $spec_details->{NUM_VARS}++; last; };
175 && do { $spec_details->{NUM_FORWARDS}++; last; };
176 /^stub$/ && do { $spec_details->{NUM_STUBS}++; last; };
177 if ($opt_verbose > 0)
179 print "Warning: didn't recognise convention \'",$call_convention,"'\n";
184 # Convert ordinal only names so we can find them later
185 if ($exported_name eq "@")
187 $exported_name = $uc_dll_name.'_'.$ordinal;
189 if (!defined($implementation_name))
191 $implementation_name = $exported_name;
193 if ($implementation_name eq "")
195 $implementation_name = $exported_name;
198 if ($implementation_name =~ /(.*?)\./)
200 $call_convention = "forward"; # Referencing a function from another dll
201 $spec_details->{NUM_FUNCS}--;
202 $spec_details->{NUM_FORWARDS}++;
205 # Add indices for the exported and implementation names
206 $spec_details->{EXPORTED_NAMES}{$exported_name} = $spec_details->{NUM_EXPORTS};
207 if ($implementation_name ne $exported_name)
209 $spec_details->{IMPLEMENTATION_NAMES}{$exported_name} = $spec_details->{NUM_EXPORTS};
212 # Add the exported entry
213 $spec_details->{NUM_EXPORTS}++;
214 my @export = ($ordinal, $call_convention, $exported_name, $implementation_name, $flags);
215 push (@{$spec_details->{EXPORTS}},[@export]);
220 # Add this .spec files details to the list of .spec files
221 $spec_files{$uc_dll_name} = [$spec_details];
224 # Read each source file, extract comments, and generate API documentation if appropriate
225 sub process_source_file($)
227 my $source_file = shift;
235 FILE => $source_file,
246 my $ignore_blank_lines = 1;
247 my $extra_comment = 0; # 1 if this is an extra comment, i.e its not a .spec export
249 if ($opt_verbose > 0)
251 print "Processing ".$source_file."\n";
253 open(SOURCE_FILE,"<$source_file")
254 || (($opt_source_dir ne "")
255 && open(SOURCE_FILE,"<$opt_source_dir/$source_file"))
256 || die "couldn't open ".$source_file."\n";
258 # Add this source file to the list of source files
259 $source_files{$source_file} = [$source_details];
263 s/\n$//; # Strip newline
264 s/^\s+//; # Strip leading space
265 s/\s+$//; # Strip trailing space
268 # Strip multiple tabs & spaces to a single space
272 if ( / +Copyright *(\([Cc]\))*[0-9 \-\,\/]*([[:alpha:][:^ascii:] \.\-]+)/ )
274 # Extract a contributor to this file
275 my $contributor = $2;
276 $contributor =~ s/ *$//;
277 $contributor =~ s/^by //;
278 $contributor =~ s/\.$//;
279 $contributor =~ s/ (for .*)/ \($1\)/;
280 if ($contributor ne "")
282 if ($opt_verbose > 3)
284 print "Info: Found contributor:'".$contributor."'\n";
286 push (@{$source_details->{CONTRIBUTORS}},$contributor);
289 elsif ( /WINE_DEFAULT_DEBUG_CHANNEL\(([A-Za-z]*)\)/ )
291 # Extract the debug channel to use
292 if ($opt_verbose > 3)
294 print "Info: Found debug channel:'".$1."'\n";
296 $source_details->{DEBUG_CHANNEL} = $1;
299 if ($parse_state == 0) # Searching for a comment
303 # Found a comment start
304 $comment->{COMMENT_NAME} = "";
305 $comment->{ALT_NAME} = "";
306 $comment->{DLL_NAME} = "";
307 $comment->{ORDINAL} = "";
308 $comment->{RETURNS} = "";
309 $comment->{PROTOTYPE} = [];
310 $comment->{TEXT} = [];
311 $ignore_blank_lines = 1;
316 elsif ($parse_state == 1) # Reading in a comment
320 # Found the end of the comment
323 elsif ( s/^\*\|/\|/ )
325 # A line of comment not meant to be pre-processed
326 push (@{$comment->{TEXT}},$_); # Add the comment text
328 elsif ( s/^ *\** *// )
330 # A line of comment, starting with an asterisk
331 if ( /^[A-Z]+$/ || $_ eq "")
333 # This is a section start, so skip blank lines before and after it.
334 my $last_line = pop(@{$comment->{TEXT}});
335 if (defined($last_line) && $last_line ne "")
338 push (@{$comment->{TEXT}},$last_line);
342 $ignore_blank_lines = 1;
346 $ignore_blank_lines = 0;
350 if ($ignore_blank_lines == 0 || $_ ne "")
352 push (@{$comment->{TEXT}},$_); # Add the comment text
357 # This isn't a well formatted comment: look for the next one
361 elsif ($parse_state == 2) # Finished reading in a comment
363 if ( /(WINAPIV|WINAPI|__cdecl|PASCAL|CALLBACK|FARPROC16)/ ||
366 # Comment is followed by a function definition
367 $parse_state = 4; # Fall through to read prototype
371 # Allow cpp directives and blank lines between the comment and prototype
372 if ($extra_comment == 1)
374 # An extra comment not followed by a function definition
375 $parse_state = 5; # Fall through to process comment
377 elsif (!/^\#/ && !/^ *$/ && !/^__ASM_GLOBAL_FUNC/)
379 # This isn't a well formatted comment: look for the next one
380 if ($opt_verbose > 1)
382 print "Info: Function '",$comment->{COMMENT_NAME},"' not followed by prototype.\n";
388 elsif ($parse_state == 3) # Reading in the first line of a comment
391 if ( /^([\@A-Za-z0-9_]+) +(\(|\[)([A-Za-z0-9_]+)\.(([0-9]+)|@)(\)|\])\s*(.*)$/ )
393 # Found a correctly formed "ApiName (DLLNAME.Ordinal)" line.
394 if (defined ($7) && $7 ne "")
396 push (@{$comment->{TEXT}},$_); # Add the trailing comment text
398 $comment->{COMMENT_NAME} = $1;
399 $comment->{DLL_NAME} = uc $3;
400 $comment->{ORDINAL} = $4;
401 $comment->{DLL_NAME} =~ s/^KERNEL$/KRNL386/; # Too many of these to ignore, _old_ code
404 elsif ( /^([A-Za-z0-9_-]+) +\{([A-Za-z0-9_]+)\}$/ )
406 # Found a correctly formed "CommentTitle {DLLNAME}" line (extra documentation)
407 $comment->{COMMENT_NAME} = $1;
408 $comment->{DLL_NAME} = uc $2;
409 $comment->{ORDINAL} = "";
415 # This isn't a well formatted comment: look for the next one
420 if ($parse_state == 4) # Reading in the function definition
422 push (@{$comment->{PROTOTYPE}},$_);
423 # Strip comments from the line before checking for ')'
424 my $stripped_line = $_;
425 $stripped_line =~ s/ *(\/\* *)(.*?)( *\*\/ *)//;
426 if ( $stripped_line =~ /\)/ )
428 # Strip a blank last line
429 my $last_line = pop(@{$comment->{TEXT}});
430 if (defined($last_line) && $last_line ne "")
433 push (@{$comment->{TEXT}},$last_line);
436 if ($opt_output_empty != 0 && @{$comment->{TEXT}} == 0)
438 # Create a 'not implemented' comment
439 @{$comment->{TEXT}} = ("fixme: This function has not yet been documented.");
445 if ($parse_state == 5) # Processing the comment
447 # Process it, if it has any text
448 if (@{$comment->{TEXT}} > 0)
450 if ($extra_comment == 1)
452 process_extra_comment($comment);
456 @{$comment->{TEXT}} = ("DESCRIPTION", @{$comment->{TEXT}});
457 process_comment($comment);
460 elsif ($opt_verbose > 1 && $opt_output_empty == 0)
462 print "Info: Function '",$comment->{COMMENT_NAME},"' has no documentation.\n";
470 # Standardise a comments text for consistency
471 sub process_comment_text($)
478 for (@{$comment->{TEXT}})
482 if ( /^\s*$/ || /^[A-Z]+$/ || /^-/ )
486 if ( $in_params > 0 && !/\[/ && !/\]/ )
488 # Possibly a continuation of the parameter description
489 my $last_line = pop(@tmp_list);
490 if ( $last_line =~ /\[/ && $last_line =~ /\]/ )
492 $line = $last_line." ".$_;
497 push (@tmp_list, $last_line);
500 if ( /^(PARAMS|MEMBERS)$/ )
504 push (@tmp_list, $line);
507 @{$comment->{TEXT}} = @tmp_list;
509 for (@{$comment->{TEXT}})
513 # Map I/O values. These come in too many formats to standardise now....
514 s/\[I\]|\[i\]|\[in\]|\[IN\]/\[In\] /g;
515 s/\[O\]|\[o\]|\[out\]|\[OUT\]/\[Out\]/g;
516 s/\[I\/O\]|\[I\,O\]|\[i\/o\]|\[in\/out\]|\[IN\/OUT\]/\[In\/Out\]/g;
517 # TRUE/FALSE/NULL are defines, capitilise them
519 s/False|false/FALSE/g;
521 # Preferred capitalisations
522 s/ wine| WINE/ Wine/g;
523 s/ API | api / Api /g;
524 s/ DLL | Dll / dll /g;
525 s/ URL | url / Url /g;
526 s/WIN16|win16/Win16/g;
527 s/WIN32|win32/Win32/g;
528 s/WIN64|win64/Win64/g;
531 s/([a-z])\.([A-Z])/$1\. $2/g; # Space after full stop
532 s/ \:/\:/g; # Colons to the left
533 s/ \;/\;/g; # Semi-colons too
535 s/^See ([A-Za-z0-9_]+)\.$/See $1\(\)\./; # Referring to A version from W
536 s/^Unicode version of ([A-Za-z0-9_]+)\.$/See $1\(\)\./; # Ditto
537 s/^64\-bit version of ([A-Za-z0-9_]+)\.$/See $1\(\)\./; # Referring to 32 bit version from 64
538 s/^PARAMETERS$/PARAMS/; # Name of parameter section should be 'PARAMS'
540 s/( |\.)(M\$|MS|Microsoft|microsoft|micro\$oft|Micro\$oft)( |\.)/$1Microsoft\(tm\)$3/g;
541 s/( |\.)(Windows|windows|windoze|winblows)( |\.)/$1Windows\(tm\)$3/g;
542 s/( |\.)(DOS|dos|msdos)( |\.)/$1MS-DOS\(tm\)$3/g;
543 s/( |\.)(UNIX|unix)( |\.)/$1Unix\(tm\)$3/g;
544 s/( |\.)(LINIX|linux)( |\.)/$1Linux\(tm\)$3/g;
546 s/( char )/ character /g;
547 s/( chars )/ characters /g;
548 s/( info )/ information /g;
549 s/( app )/ application /g;
550 s/( apps )/ applications /g;
551 s/( exe )/ executable /g;
552 s/( ptr )/ pointer /g;
553 s/( obj )/ object /g;
555 s/( bool )/ boolean /g;
556 s/( no\. )/ number /g;
557 s/( No\. )/ Number /g;
559 if ( /\[I|\[O/ && ! /\.$/ )
561 $_ = $_."."; # Always have a full stop at the end of parameter desc.
563 elsif ($i > 0 && /^[A-Z]*$/ &&
564 !(@{$comment->{TEXT}}[$i-1] =~ /\.$/) &&
565 !(@{$comment->{TEXT}}[$i-1] =~ /\:$/))
568 if (!(@{$comment->{TEXT}}[$i-1] =~ /^[A-Z]*$/))
570 # Paragraphs always end with a full stop
571 @{$comment->{TEXT}}[$i-1] = @{$comment->{TEXT}}[$i-1].".";
579 # Standardise our comment and output it if it is suitable.
580 sub process_comment($)
584 # Don't process this comment if the function isn't exported
585 my $spec_details = $spec_files{$comment->{DLL_NAME}}[0];
587 if (!defined($spec_details))
589 if ($opt_verbose > 2)
591 print "Warning: Function '".$comment->{COMMENT_NAME}."' belongs to '".
592 $comment->{DLL_NAME}."' (not passed with -w): not processing it.\n";
597 if ($comment->{COMMENT_NAME} eq "@")
601 # Find the name from the .spec file
602 for (@{$spec_details->{EXPORTS}})
604 if (@$_[$EXPORT_ORDINAL] eq $comment->{ORDINAL})
606 $comment->{COMMENT_NAME} = @$_[$EXPORT_EXPNAME];
613 # Create an implementation name
614 $comment->{COMMENT_NAME} = $comment->{DLL_NAME}."_".$comment->{ORDINAL};
618 my $exported_names = $spec_details->{EXPORTED_NAMES};
619 my $export_index = $exported_names->{$comment->{COMMENT_NAME}};
620 my $implementation_names = $spec_details->{IMPLEMENTATION_NAMES};
622 if (!defined($export_index))
624 # Perhaps the comment uses the implementation name?
625 $export_index = $implementation_names->{$comment->{COMMENT_NAME}};
627 if (!defined($export_index))
629 # This function doesn't appear to be exported. hmm.
630 if ($opt_verbose > 2)
632 print "Warning: Function '".$comment->{COMMENT_NAME}."' claims to belong to '".
633 $comment->{DLL_NAME}."' but is not exported by it: not processing it.\n";
638 # When the function is exported twice we have the second name below the first
639 # (you see this a lot in ntdll, but also in some other places).
640 my $first_line = ${$comment->{TEXT}}[1];
642 if ( $first_line =~ /^(@|[A-Za-z0-9_]+) +(\(|\[)([A-Za-z0-9_]+)\.(([0-9]+)|@)(\)|\])$/ )
644 # Found a second name - mark it as documented
645 my $alt_index = $exported_names->{$1};
646 if (defined($alt_index))
648 if ($opt_verbose > 2)
650 print "Info: Found alternate name '",$1,"\n";
652 my $alt_export = @{$spec_details->{EXPORTS}}[$alt_index];
653 @$alt_export[$EXPORT_FLAGS] |= $FLAG_DOCUMENTED;
654 $spec_details->{NUM_DOCS}++;
655 ${$comment->{TEXT}}[1] = "";
659 if (@{$spec_details->{CURRENT_EXTRA}})
661 # We have an extra comment that might be related to this one
662 my $current_comment = ${$spec_details->{CURRENT_EXTRA}}[0];
663 my $current_name = $current_comment->{COMMENT_NAME};
664 if ($comment->{COMMENT_NAME} =~ /^$current_name/ && $comment->{COMMENT_NAME} ne $current_name)
666 if ($opt_verbose > 2)
668 print "Linking ",$comment->{COMMENT_NAME}," to $current_name\n";
670 # Add a reference to this comment to our extra comment
671 push (@{$current_comment->{TEXT}}, $comment->{COMMENT_NAME}."()","");
675 # We want our docs generated using the implementation name, so they are unique
676 my $export = @{$spec_details->{EXPORTS}}[$export_index];
677 $comment->{COMMENT_NAME} = @$export[$EXPORT_IMPNAME];
678 $comment->{ALT_NAME} = @$export[$EXPORT_EXPNAME];
680 # Mark the function as documented
681 $spec_details->{NUM_DOCS}++;
682 @$export[$EXPORT_FLAGS] |= $FLAG_DOCUMENTED;
684 # This file is used by the DLL - Make sure we get our contributors right
685 push (@{$spec_details->{SOURCES}},$comment->{FILE});
687 # If we have parameter comments in the prototype, extract them
688 my @parameter_comments;
689 for (@{$comment->{PROTOTYPE}})
691 s/ *\, */\,/g; # Strip spaces from around commas
693 if ( s/ *(\/\* *)(.*?)( *\*\/ *)// ) # Strip out comment
695 my $parameter_comment = $2;
696 if (!$parameter_comment =~ /^\[/ )
698 # Add [IO] markers so we format the comment correctly
699 $parameter_comment = "[fixme] ".$parameter_comment;
701 if ( /( |\*)([A-Za-z_]{1}[A-Za-z_0-9]*)(\,|\))/ )
703 # Add the parameter name
704 $parameter_comment = $2." ".$parameter_comment;
706 push (@parameter_comments, $parameter_comment);
710 # If we extracted any prototype comments, add them to the comment text.
711 if (@parameter_comments)
713 @parameter_comments = ("PARAMS", @parameter_comments);
714 my @new_comment = ();
715 my $inserted_params = 0;
717 for (@{$comment->{TEXT}})
719 if ( $inserted_params == 0 && /^[A-Z]+$/ )
721 # Found a section header, so this is where we insert
722 push (@new_comment, @parameter_comments);
723 $inserted_params = 1;
725 push (@new_comment, $_);
727 if ($inserted_params == 0)
729 # Add them to the end
730 push (@new_comment, @parameter_comments);
732 $comment->{TEXT} = [@new_comment];
735 if ($opt_fussy == 1 && $opt_output_empty == 0)
737 # Reject any comment that doesn't have a description or a RETURNS section.
738 # This is the default for now, 'coz many comments aren't suitable.
739 my $found_returns = 0;
740 my $found_description_text = 0;
741 my $in_description = 0;
742 for (@{$comment->{TEXT}})
749 elsif ( /^DESCRIPTION$/ )
753 elsif ($in_description == 1)
757 # Don't reject comments that refer to another doc (e.g. A/W)
758 if ( /^See ([A-Za-z0-9_]+)\.$/ )
760 if ($comment->{COMMENT_NAME} =~ /W$/ )
762 # This is probably a Unicode version of an Ascii function.
763 # Create the Ascii name and see if its been documented
764 my $ascii_name = $comment->{COMMENT_NAME};
765 $ascii_name =~ s/W$/A/;
767 my $ascii_export_index = $exported_names->{$ascii_name};
769 if (!defined($ascii_export_index))
771 $ascii_export_index = $implementation_names->{$ascii_name};
773 if (!defined($ascii_export_index))
775 if ($opt_verbose > 2)
777 print "Warning: Function '".$comment->{COMMENT_NAME}."' is not an A/W pair.\n";
782 my $ascii_export = @{$spec_details->{EXPORTS}}[$ascii_export_index];
783 if (@$ascii_export[$EXPORT_FLAGS] & $FLAG_DOCUMENTED)
785 # Flag these functions as an A/W pair
786 @$ascii_export[$EXPORT_FLAGS] |= $FLAG_APAIR;
787 @$export[$EXPORT_FLAGS] |= $FLAG_WPAIR;
793 elsif ( /^Unicode version of ([A-Za-z0-9_]+)\.$/ )
795 @$export[$EXPORT_FLAGS] |= $FLAG_WPAIR; # Explicitly marked as W version
798 elsif ( /^64\-bit version of ([A-Za-z0-9_]+)\.$/ )
800 @$export[$EXPORT_FLAGS] |= $FLAG_64PAIR; # Explicitly marked as 64 bit version
803 $found_description_text = 1;
811 if ($found_returns == 0 || $found_description_text == 0)
813 if ($opt_verbose > 2)
815 print "Info: Function '",$comment->{COMMENT_NAME},"' has no ",
816 "description and/or RETURNS section, skipping\n";
818 $spec_details->{NUM_DOCS}--;
819 @$export[$EXPORT_FLAGS] &= ~$FLAG_DOCUMENTED;
824 process_comment_text($comment);
826 # Strip the prototypes return value, call convention, name and brackets
827 # (This leaves it as a list of types and names, or empty for void functions)
828 my $prototype = join(" ", @{$comment->{PROTOTYPE}});
829 $prototype =~ s/ / /g;
831 if ( $prototype =~ /(WINAPIV|WINAPI|__cdecl|PASCAL|CALLBACK|FARPROC16)/ )
833 $prototype =~ s/^(.*?)\s+(WINAPIV|WINAPI|__cdecl|PASCAL|CALLBACK|FARPROC16)\s+(.*?)\(\s*(.*)/$4/;
834 $comment->{RETURNS} = $1;
838 $prototype =~ s/^(.*?)([A-Za-z0-9_]+)\s*\(\s*(.*)/$3/;
839 $comment->{RETURNS} = $1;
842 $prototype =~ s/ *\).*//; # Strip end bracket
843 $prototype =~ s/ *\* */\*/g; # Strip space around pointers
844 $prototype =~ s/ *\, */\,/g; # Strip space around commas
845 $prototype =~ s/^(void|VOID)$//; # If void, leave blank
846 $prototype =~ s/\*([A-Za-z_])/\* $1/g; # Separate pointers from parameter name
847 @{$comment->{PROTOTYPE}} = split ( /,/ ,$prototype);
849 # FIXME: If we have no parameters, make sure we have a PARAMS: None. section
853 if (@$export[$EXPORT_FLAGS] & $FLAG_NONAME)
855 $h_file = "Exported by ordinal only. Use GetProcAddress() to obtain a pointer to the function.";
859 if ($comment->{COMMENT_NAME} ne "")
861 my $tmp = "grep -s -l $comment->{COMMENT_NAME} @opt_header_file_list 2>/dev/null";
863 my $exit_value = $? >> 8;
864 if ($exit_value == 0)
870 $h_file =~ s|^.*/\./||;
874 elsif ($comment->{ALT_NAME} ne "")
876 my $tmp = "grep -s -l $comment->{ALT_NAME} @opt_header_file_list"." 2>/dev/null";
878 my $exit_value = $? >> 8;
879 if ($exit_value == 0)
885 $h_file =~ s|^.*/\./||;
893 $h_file = "Not declared in a Wine header. The function is either undocumented, or missing from Wine."
897 $h_file = "Declared in \"".$h_file."\".";
902 my $c_file = $comment->{FILE};
903 if ($opt_wine_root_dir ne "")
905 my $cfile = $pwd."/".$c_file; # Current dir + file
906 $cfile =~ s/(.+)(\/.*$)/$1/; # Strip the filename
907 $cfile = `cd $cfile && pwd`; # Strip any relative parts (e.g. "../../")
908 $cfile =~ s/\n//; # Strip newline
909 my $newfile = $c_file;
910 $newfile =~ s/(.+)(\/.*$)/$2/; # Strip all but the filename
911 $cfile = $cfile."/".$newfile; # Append filename to base path
912 $cfile =~ s/$opt_wine_root_dir//; # Get rid of the root directory
913 $cfile =~ s/\/\//\//g; # Remove any double slashes
914 $cfile =~ s/^\/+//; # Strip initial directory slash
917 $c_file = "Implemented in \"".$c_file."\".";
919 # Add the implementation details
920 push (@{$comment->{TEXT}}, "IMPLEMENTATION","",$h_file,"",$c_file);
922 if (@$export[$EXPORT_FLAGS] & $FLAG_I386)
924 push (@{$comment->{TEXT}}, "", "Available on x86 platforms only.");
926 if (@$export[$EXPORT_FLAGS] & $FLAG_REGISTER)
928 push (@{$comment->{TEXT}}, "", "This function passes one or more arguments in registers. ",
929 "For more details, please read the source code.");
931 my $source_details = $source_files{$comment->{FILE}}[0];
932 if ($source_details->{DEBUG_CHANNEL} ne "")
934 push (@{$comment->{TEXT}}, "", "Debug channel \"".$source_details->{DEBUG_CHANNEL}."\".");
937 # Write out the documentation for the API
938 output_comment($comment)
941 # process our extra comment and output it if it is suitable.
942 sub process_extra_comment($)
946 my $spec_details = $spec_files{$comment->{DLL_NAME}}[0];
948 if (!defined($spec_details))
950 if ($opt_verbose > 2)
952 print "Warning: Extra comment '".$comment->{COMMENT_NAME}."' belongs to '".
953 $comment->{DLL_NAME}."' (not passed with -w): not processing it.\n";
958 # Check first to see if this is documentation for the DLL.
959 if ($comment->{COMMENT_NAME} eq $comment->{DLL_NAME})
961 if ($opt_verbose > 2)
963 print "Info: Found DLL documentation\n";
965 for (@{$comment->{TEXT}})
967 push (@{$spec_details->{DESCRIPTION}}, $_);
972 # Add the comment to the DLL page as a link
973 push (@{$spec_details->{EXTRA_COMMENTS}},$comment->{COMMENT_NAME});
975 # If we have a prototype, process as a regular comment
976 if (@{$comment->{PROTOTYPE}})
978 $comment->{ORDINAL} = "@";
980 # Add an index for the comment name
981 $spec_details->{EXPORTED_NAMES}{$comment->{COMMENT_NAME}} = $spec_details->{NUM_EXPORTS};
983 # Add a fake exported entry
984 $spec_details->{NUM_EXPORTS}++;
985 my ($ordinal, $call_convention, $exported_name, $implementation_name, $documented) =
986 ("@", "fake", $comment->{COMMENT_NAME}, $comment->{COMMENT_NAME}, 0);
987 my @export = ($ordinal, $call_convention, $exported_name, $implementation_name, $documented);
988 push (@{$spec_details->{EXPORTS}},[@export]);
989 @{$comment->{TEXT}} = ("DESCRIPTION", @{$comment->{TEXT}});
990 process_comment($comment);
994 if ($opt_verbose > 0)
996 print "Processing ",$comment->{COMMENT_NAME},"\n";
999 if (@{$spec_details->{CURRENT_EXTRA}})
1001 my $current_comment = ${$spec_details->{CURRENT_EXTRA}}[0];
1003 if ($opt_verbose > 0)
1005 print "Processing old current: ",$current_comment->{COMMENT_NAME},"\n";
1007 # Output the current comment
1008 process_comment_text($current_comment);
1009 output_open_api_file($current_comment->{COMMENT_NAME});
1010 output_api_header($current_comment);
1011 output_api_name($current_comment);
1012 output_api_comment($current_comment);
1013 output_api_footer($current_comment);
1014 output_close_api_file();
1017 if ($opt_verbose > 2)
1019 print "Setting current to ",$comment->{COMMENT_NAME},"\n";
1024 FILE => $comment->{FILE},
1025 COMMENT_NAME => $comment->{COMMENT_NAME},
1026 ALT_NAME => $comment->{ALT_NAME},
1027 DLL_NAME => $comment->{DLL_NAME},
1028 ORDINAL => $comment->{ORDINAL},
1029 RETURNS => $comment->{RETURNS},
1034 for (@{$comment->{TEXT}})
1036 push (@{$comment_copy->{TEXT}}, $_);
1038 # Set this comment to be the current extra comment
1039 @{$spec_details->{CURRENT_EXTRA}} = ($comment_copy);
1042 # Write a standardised comment out in the appropriate format
1043 sub output_comment($)
1045 my $comment = shift;
1047 if ($opt_verbose > 0)
1049 print "Processing ",$comment->{COMMENT_NAME},"\n";
1052 if ($opt_verbose > 4)
1054 print "--PROTO--\n";
1055 for (@{$comment->{PROTOTYPE}})
1060 print "--COMMENT--\n";
1061 for (@{$comment->{TEXT} })
1067 output_open_api_file($comment->{COMMENT_NAME});
1068 output_api_header($comment);
1069 output_api_name($comment);
1070 output_api_synopsis($comment);
1071 output_api_comment($comment);
1072 output_api_footer($comment);
1073 output_close_api_file();
1076 # Write out an index file for each .spec processed
1077 sub process_index_files()
1079 foreach my $spec_file (keys %spec_files)
1081 my $spec_details = $spec_files{$spec_file}[0];
1082 if (defined ($spec_details->{DLL_NAME}))
1084 if (@{$spec_details->{CURRENT_EXTRA}})
1086 # We have an unwritten extra comment, write it
1087 my $current_comment = ${$spec_details->{CURRENT_EXTRA}}[0];
1088 process_extra_comment($current_comment);
1089 @{$spec_details->{CURRENT_EXTRA}} = ();
1091 output_spec($spec_details);
1096 # Write a spec files documentation out in the appropriate format
1099 my $spec_details = shift;
1101 if ($opt_verbose > 2)
1103 print "Writing:",$spec_details->{DLL_NAME},"\n";
1106 # Use the comment output functions for consistency
1109 FILE => $spec_details->{DLL_NAME},
1110 COMMENT_NAME => $spec_details->{DLL_NAME}.".".$spec_details->{DLL_EXT},
1111 ALT_NAME => $spec_details->{DLL_NAME},
1118 my $total_implemented = $spec_details->{NUM_FORWARDS} + $spec_details->{NUM_VARS} +
1119 $spec_details->{NUM_FUNCS};
1120 my $percent_implemented = 0;
1121 if ($total_implemented)
1123 $percent_implemented = $total_implemented /
1124 ($total_implemented + $spec_details->{NUM_STUBS}) * 100;
1126 $percent_implemented = int($percent_implemented);
1127 my $percent_documented = 0;
1128 if ($spec_details->{NUM_DOCS})
1130 # Treat forwards and data as documented funcs for statistics
1131 $percent_documented = $spec_details->{NUM_DOCS} / $spec_details->{NUM_FUNCS} * 100;
1132 $percent_documented = int($percent_documented);
1135 # Make a list of the contributors to this DLL. Do this only for the source
1136 # files that make up the DLL, because some directories specify multiple dlls.
1139 for (@{$spec_details->{SOURCES}})
1141 my $source_details = $source_files{$_}[0];
1142 for (@{$source_details->{CONTRIBUTORS}})
1144 push (@contributors, $_);
1149 @contributors = grep(!$saw{$_}++, @contributors); # remove dups, from perlfaq4 manpage
1150 @contributors = sort @contributors;
1152 # Remove duplicates and blanks
1153 for(my $i=0; $i<@contributors; $i++)
1155 if ($i > 0 && ($contributors[$i] =~ /$contributors[$i-1]/ || $contributors[$i-1] eq ""))
1157 $contributors[$i-1] = $contributors[$i];
1161 @contributors = grep(!$saw{$_}++, @contributors);
1163 if ($opt_verbose > 3)
1165 print "Contributors:\n";
1171 my $contribstring = join (", ", @contributors);
1173 # Create the initial comment text
1174 @{$comment->{TEXT}} = (
1176 $comment->{COMMENT_NAME}
1179 # Add the description, if we have one
1180 if (@{$spec_details->{DESCRIPTION}})
1182 push (@{$comment->{TEXT}}, "DESCRIPTION");
1183 for (@{$spec_details->{DESCRIPTION}})
1185 push (@{$comment->{TEXT}}, $_);
1189 # Add the statistics and contributors
1190 push (@{$comment->{TEXT}},
1192 "Forwards: ".$spec_details->{NUM_FORWARDS},
1193 "Variables: ".$spec_details->{NUM_VARS},
1194 "Stubs: ".$spec_details->{NUM_STUBS},
1195 "Functions: ".$spec_details->{NUM_FUNCS},
1196 "Exports-Total: ".$spec_details->{NUM_EXPORTS},
1197 "Implemented-Total: ".$total_implemented." (".$percent_implemented."%)",
1198 "Documented-Total: ".$spec_details->{NUM_DOCS}." (".$percent_documented."%)",
1200 "The following people hold copyrights on the source files comprising this dll:",
1203 "Note: This list may not be complete.",
1204 "For a complete listing, see the Files \"AUTHORS\" and \"Changelog\" in the Wine source tree.",
1208 if ($opt_output_format eq "h")
1210 # Add the exports to the comment text
1211 push (@{$comment->{TEXT}},"EXPORTS");
1212 my $exports = $spec_details->{EXPORTS};
1217 # @$_ => ordinal, call convention, exported name, implementation name, flags;
1218 if (@$_[$EXPORT_CALL] eq "forward")
1220 my $forward_dll = @$_[$EXPORT_IMPNAME];
1221 $forward_dll =~ s/\.(.*)//;
1222 $line = @$_[$EXPORT_EXPNAME]." (forward to ".$1."() in ".$forward_dll."())";
1224 elsif (@$_[$EXPORT_CALL] eq "extern")
1226 $line = @$_[$EXPORT_EXPNAME]." (extern)";
1228 elsif (@$_[$EXPORT_CALL] eq "stub")
1230 $line = @$_[$EXPORT_EXPNAME]." (stub)";
1232 elsif (@$_[$EXPORT_CALL] eq "fake")
1234 # Don't add this function here, it gets listed with the extra documentation
1235 if (!(@$_[$EXPORT_FLAGS] & $FLAG_WPAIR))
1237 # This function should be indexed
1238 push (@index_entries_list, @$_[$EXPORT_IMPNAME].",".@$_[$EXPORT_IMPNAME]);
1241 elsif (@$_[$EXPORT_CALL] eq "equate" || @$_[$EXPORT_CALL] eq "variable")
1243 $line = @$_[$EXPORT_EXPNAME]." (data)";
1248 if (@$_[$EXPORT_FLAGS] & $FLAG_DOCUMENTED)
1251 $line = @$_[$EXPORT_EXPNAME]." (implemented as ".@$_[$EXPORT_IMPNAME]."())";
1252 if (@$_[$EXPORT_EXPNAME] ne @$_[$EXPORT_IMPNAME])
1254 $line = @$_[$EXPORT_EXPNAME]." (implemented as ".@$_[$EXPORT_IMPNAME]."())";
1258 $line = @$_[$EXPORT_EXPNAME]."()";
1260 if (!(@$_[$EXPORT_FLAGS] & $FLAG_WPAIR))
1262 # This function should be indexed
1263 push (@index_entries_list, @$_[$EXPORT_EXPNAME].",".@$_[$EXPORT_IMPNAME]);
1268 $line = @$_[$EXPORT_EXPNAME]." (not documented)";
1273 push (@{$comment->{TEXT}}, $line, "");
1277 # Add links to the extra documentation
1278 if (@{$spec_details->{EXTRA_COMMENTS}})
1280 push (@{$comment->{TEXT}}, "SEE ALSO");
1282 @{$spec_details->{EXTRA_COMMENTS}} = grep(!$htmp{$_}++, @{$spec_details->{EXTRA_COMMENTS}});
1283 for (@{$spec_details->{EXTRA_COMMENTS}})
1285 push (@{$comment->{TEXT}}, $_."()", "");
1289 # The dll entry should also be indexed
1290 push (@index_entries_list, $spec_details->{DLL_NAME}.",".$spec_details->{DLL_NAME});
1292 # Write out the document
1293 output_open_api_file($spec_details->{DLL_NAME});
1294 output_api_header($comment);
1295 output_api_comment($comment);
1296 output_api_footer($comment);
1297 output_close_api_file();
1299 # Add this dll to the database of dll names
1300 my $output_file = $opt_output_directory."/dlls.db";
1302 # Append the dllname to the output db of names
1303 open(DLLDB,">>$output_file") || die "Couldn't create $output_file\n";
1304 print DLLDB $spec_details->{DLL_NAME},"\n";
1307 if ($opt_output_format eq "s")
1309 output_sgml_dll_file($spec_details);
1313 if ($opt_output_format eq "x")
1315 output_xml_dll_file($spec_details);
1324 # Only these functions know anything about formatting for a specific
1325 # output type. The functions above work only with plain text.
1326 # This is to allow new types of output to be added easily.
1329 sub output_open_api_file($)
1331 my $output_name = shift;
1332 $output_name = $opt_output_directory."/".$output_name;
1334 if ($opt_output_format eq "h")
1336 $output_name = $output_name.".html";
1338 elsif ($opt_output_format eq "s")
1340 $output_name = $output_name.".sgml";
1342 elsif ($opt_output_format eq "x")
1344 $output_name = $output_name.".xml";
1348 $output_name = $output_name.".".$opt_manual_section;
1350 open(OUTPUT,">$output_name") || die "Couldn't create file '$output_name'\n";
1353 # Close the api file
1354 sub output_close_api_file()
1359 # Output the api file header
1360 sub output_api_header($)
1362 my $comment = shift;
1364 if ($opt_output_format eq "h")
1366 print OUTPUT "<!-- Generated file - DO NOT EDIT! -->\n";
1367 print OUTPUT "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n";
1368 print OUTPUT "<HTML>\n<HEAD>\n";
1369 print OUTPUT "<LINK REL=\"StyleSheet\" href=\"apidoc.css\" type=\"text/css\">\n";
1370 print OUTPUT "<META NAME=\"GENERATOR\" CONTENT=\"tools/c2man.pl\">\n";
1371 print OUTPUT "<META NAME=\"keywords\" CONTENT=\"Win32,Wine,API,$comment->{COMMENT_NAME}\">\n";
1372 print OUTPUT "<TITLE>Wine API: $comment->{COMMENT_NAME}</TITLE>\n</HEAD>\n<BODY>\n";
1374 elsif ($opt_output_format eq "s" || $opt_output_format eq "x")
1376 print OUTPUT "<!-- Generated file - DO NOT EDIT! -->\n",
1378 "<title>$comment->{COMMENT_NAME}</title>\n";
1382 print OUTPUT ".\\\" -*- nroff -*-\n.\\\" Generated file - DO NOT EDIT!\n".
1383 ".TH ",$comment->{COMMENT_NAME}," ",$opt_manual_section," \"",$date,"\" \"".
1384 "Wine API\" \"Wine API\"\n";
1388 sub output_api_footer($)
1390 if ($opt_output_format eq "h")
1392 print OUTPUT "<hr><p><i class=\"copy\">Copyright © ".$year." The Wine Project.".
1393 " All trademarks are the property of their respective owners.".
1394 " Visit <a href=\"http://www.winehq.org\">WineHQ</a> for license details.".
1395 " Generated $date.</i></p>\n</body>\n</html>\n";
1397 elsif ($opt_output_format eq "s" || $opt_output_format eq "x")
1399 print OUTPUT "</sect1>\n";
1407 sub output_api_section_start($$)
1409 my $comment = shift;
1410 my $section_name = shift;
1412 if ($opt_output_format eq "h")
1414 print OUTPUT "\n<h2 class=\"section\">",$section_name,"</h2>\n";
1416 elsif ($opt_output_format eq "s" || $opt_output_format eq "x")
1418 print OUTPUT "<bridgehead>",$section_name,"</bridgehead>\n";
1422 print OUTPUT "\n\.SH ",$section_name,"\n";
1426 sub output_api_section_end()
1428 # Not currently required by any output formats
1431 sub output_api_name($)
1433 my $comment = shift;
1434 my $readable_name = $comment->{COMMENT_NAME};
1435 $readable_name =~ s/-/ /g; # make section names more readable
1437 output_api_section_start($comment,"NAME");
1440 my $dll_ordinal = "";
1441 if ($comment->{ORDINAL} ne "")
1443 $dll_ordinal = "(".$comment->{DLL_NAME}.".".$comment->{ORDINAL}.")";
1445 if ($opt_output_format eq "h")
1447 print OUTPUT "<p><b class=\"func_name\">",$readable_name,
1448 "</b> <i class=\"dll_ord\">",
1449 ,$dll_ordinal,"</i></p>\n";
1451 elsif ($opt_output_format eq "s" || $opt_output_format eq "x")
1453 print OUTPUT "<para>\n <command>",$readable_name,"</command> <emphasis>",
1454 $dll_ordinal,"</emphasis>\n</para>\n";
1458 print OUTPUT "\\fB",$readable_name,"\\fR ",$dll_ordinal;
1461 output_api_section_end();
1464 sub output_api_synopsis($)
1466 my $comment = shift;
1469 output_api_section_start($comment,"SYNOPSIS");
1471 if ($opt_output_format eq "h")
1473 print OUTPUT "<pre class=\"proto\">\n ", $comment->{RETURNS}," ",$comment->{COMMENT_NAME},"\n (\n";
1474 @fmt = ("", "\n", "<tt class=\"param\">", "</tt>");
1476 elsif ($opt_output_format eq "s" || $opt_output_format eq "x")
1478 print OUTPUT "<screen>\n ",$comment->{RETURNS}," ",$comment->{COMMENT_NAME},"\n (\n";
1479 @fmt = ("", "\n", "<emphasis>", "</emphasis>");
1483 print OUTPUT $comment->{RETURNS}," ",$comment->{COMMENT_NAME},"\n (\n";
1484 @fmt = ("", "\n", "\\fI", "\\fR");
1487 # Since our prototype is output in a pre-formatted block, line up the
1488 # parameters and parameter comments in the same column.
1490 # First caluculate where the columns should start
1491 my $biggest_length = 0;
1492 for(my $i=0; $i < @{$comment->{PROTOTYPE}}; $i++)
1494 my $line = ${$comment->{PROTOTYPE}}[$i];
1495 if ($line =~ /(.+?)([A-Za-z_][A-Za-z_0-9]*)$/)
1497 my $length = length $1;
1498 if ($length > $biggest_length)
1500 $biggest_length = $length;
1505 # Now pad the string with blanks
1506 for(my $i=0; $i < @{$comment->{PROTOTYPE}}; $i++)
1508 my $line = ${$comment->{PROTOTYPE}}[$i];
1509 if ($line =~ /(.+?)([A-Za-z_][A-Za-z_0-9]*)$/)
1511 my $pad_len = $biggest_length - length $1;
1512 my $padding = " " x ($pad_len);
1513 ${$comment->{PROTOTYPE}}[$i] = $1.$padding.$2;
1517 for(my $i=0; $i < @{$comment->{PROTOTYPE}}; $i++)
1519 # Format the parameter name
1520 my $line = ${$comment->{PROTOTYPE}}[$i];
1521 my $comma = ($i == @{$comment->{PROTOTYPE}}-1) ? "" : ",";
1522 $line =~ s/(.+?)([A-Za-z_][A-Za-z_0-9]*)$/ $fmt[0]$1$fmt[2]$2$fmt[3]$comma$fmt[1]/;
1526 if ($opt_output_format eq "h")
1528 print OUTPUT " )\n</pre>\n";
1530 elsif ($opt_output_format eq "s" || $opt_output_format eq "x")
1532 print OUTPUT " )\n</screen>\n";
1536 print OUTPUT " )\n";
1539 output_api_section_end();
1542 sub output_api_comment($)
1544 my $comment = shift;
1545 my $open_paragraph = 0;
1550 if ($opt_output_format eq "h")
1552 @fmt = ("<p>", "</p>\n", "<tt class=\"const\">", "</tt>", "<b class=\"emp\">", "</b>",
1553 "<tt class=\"coderef\">", "</tt>", "<tt class=\"param\">", "</tt>",
1554 "<i class=\"in_out\">", "</i>", "<pre class=\"raw\">\n", "</pre>\n",
1555 "<table class=\"tab\"><colgroup><col><col><col></colgroup><tbody>\n",
1556 "</tbody></table>\n","<tr><td>","</td></tr>\n","</td>","</td><td>");
1558 elsif ($opt_output_format eq "s" || $opt_output_format eq "x")
1560 @fmt = ("<para>\n","\n</para>\n","<constant>","</constant>","<emphasis>","</emphasis>",
1561 "<command>","</command>","<constant>","</constant>","<emphasis>","</emphasis>",
1562 "<screen>\n","</screen>\n",
1563 "<informaltable frame=\"none\">\n<tgroup cols=\"3\">\n<tbody>\n",
1564 "</tbody>\n</tgroup>\n</informaltable>\n","<row><entry>","</entry></row>\n",
1565 "</entry>","</entry><entry>");
1569 @fmt = ("\.PP\n", "\n", "\\fB", "\\fR", "\\fB", "\\fR", "\\fB", "\\fR", "\\fI", "\\fR",
1570 "\\fB", "\\fR ", "", "", "", "","","\n.PP\n","","");
1573 # Extract the parameter names
1574 my @parameter_names;
1575 for (@{$comment->{PROTOTYPE}})
1577 if ( /(.+?)([A-Za-z_][A-Za-z_0-9]*)$/ )
1579 push (@parameter_names, $2);
1583 for (@{$comment->{TEXT}})
1585 if ($opt_output_format eq "h" || $opt_output_format eq "s" || $opt_output_format eq "x")
1587 # Map special characters
1591 s/\([Cc]\)/\©/g;
1600 if ($open_paragraph == 1)
1602 # Close the open paragraph
1603 print OUTPUT $fmt[1];
1604 $open_paragraph = 0;
1607 print OUTPUT $fmt[12];
1610 if ($opt_output_format eq "")
1612 print OUTPUT ".br\n"; # Prevent 'man' running these lines together
1614 print OUTPUT $_,"\n";
1618 if ($opt_output_format eq "h")
1620 # Link to the file in WineHQ cvs
1621 s/^(Implemented in \")(.+?)(\"\.)/$1$2$3 http:\/\/source.winehq.org\/source\/$2/g;
1622 s/^(Declared in \")(.+?)(\"\.)/$1$2$3 http:\/\/source.winehq.org\/source\/include\/$2/g;
1625 s/(\".+?\")/$fmt[2]$1$fmt[3]/g;
1626 # Highlight literal chars
1627 s/(\'.\')/$fmt[2]$1$fmt[3]/g;
1628 s/(\'.{2}\')/$fmt[2]$1$fmt[3]/g;
1629 # Highlight numeric constants
1630 s/( |\-|\+|\.|\()([0-9\-\.]+)( |\-|$|\.|\,|\*|\?|\))/$1$fmt[2]$2$fmt[3]$3/g;
1632 # Leading cases ("xxxx:","-") start new paragraphs & are emphasised
1633 # FIXME: Using bullet points for leading '-' would look nicer.
1634 if ($open_paragraph == 1 && $param_docs == 0)
1636 s/^(\-)/$fmt[1]$fmt[0]$fmt[4]$1$fmt[5]/;
1637 s/^([[A-Za-z\-]+\:)/$fmt[1]$fmt[0]$fmt[4]$1$fmt[5]/;
1641 s/^(\-)/$fmt[4]$1$fmt[5]/;
1642 s/^([[A-Za-z\-]+\:)/$fmt[4]$1$fmt[5]/;
1645 if ($opt_output_format eq "h")
1647 # Html uses links for API calls
1648 while ( /([A-Za-z_]+[A-Za-z_0-9-]+)(\(\))/)
1651 my $readable_link = $1;
1652 $readable_link =~ s/-/ /g;
1654 s/([A-Za-z_]+[A-Za-z_0-9-]+)(\(\))/<a href\=\"$link\.html\">$readable_link<\/a>/;
1657 s/\{\{(.*?)\}\}\{\{(.*?)\}\}/<a href\=\"$2\.html\">$1<\/a>/g;
1658 s/ ([A-Z_])(\(\))/<a href\=\"$1\.html\">$1<\/a>/g;
1659 # And references to COM objects (hey, they'll get documented one day)
1660 s/ (I[A-Z]{1}[A-Za-z0-9_]+) (Object|object|Interface|interface)/ <a href\=\"$1\.html\">$1<\/a> $2/g;
1661 # Convert any web addresses to real links
1662 s/(http\:\/\/)(.+?)($| )/<a href\=\"$1$2\">$2<\/a>$3/g;
1666 if ($opt_output_format eq "")
1668 # Give the man section for API calls
1669 s/ ([A-Za-z_]+[A-Za-z_0-9-]+)\(\)/ $fmt[6]$1\($opt_manual_section\)$fmt[7]/g;
1673 # Highlight API calls
1674 s/ ([A-Za-z_]+[A-Za-z_0-9-]+\(\))/ $fmt[6]$1$fmt[7]/g;
1677 # And references to COM objects
1678 s/ (I[A-Z]{1}[A-Za-z0-9_]+) (Object|object|Interface|interface)/ $fmt[6]$1$fmt[7] $2/g;
1683 # Finish the raw output
1684 print OUTPUT $fmt[13];
1688 if ( /^[A-Z]+$/ || /^SEE ALSO$/ )
1690 # Start of a new section
1691 if ($open_paragraph == 1)
1693 if ($param_docs == 1)
1695 print OUTPUT $fmt[17],$fmt[15];
1700 print OUTPUT $fmt[1];
1702 $open_paragraph = 0;
1704 output_api_section_start($comment,$_);
1705 if ( /^PARAMS$/ || /^MEMBERS$/ )
1707 print OUTPUT $fmt[14];
1712 #print OUTPUT $fmt[15];
1718 # Empty line, indicating a new paragraph
1719 if ($open_paragraph == 1)
1721 if ($param_docs == 0)
1723 print OUTPUT $fmt[1];
1724 $open_paragraph = 0;
1730 if ($param_docs == 1)
1732 if ($open_paragraph == 1)
1734 # For parameter docs, put each parameter into a new paragraph/table row
1735 print OUTPUT $fmt[17];
1736 $open_paragraph = 0;
1738 s/(\[.+\])( *)/$fmt[19]$fmt[10]$1$fmt[11]$fmt[19] /; # Format In/Out
1742 # Within paragraph lines, prevent lines running together
1746 # Format parameter names where they appear in the comment
1747 for my $parameter_name (@parameter_names)
1749 s/(^|[ \.\,\(\-\*])($parameter_name)($|[ \.\)\,\-\/]|(\=[^"]))/$1$fmt[8]$2$fmt[9]$3/g;
1751 # Structure dereferences include the dereferenced member
1752 s/(\-\>[A-Za-z_]+)/$fmt[8]$1$fmt[9]/g;
1753 s/(\-\>\;[A-Za-z_]+)/$fmt[8]$1$fmt[9]/g;
1755 if ($open_paragraph == 0)
1757 if ($param_docs == 1)
1759 print OUTPUT $fmt[16];
1763 print OUTPUT $fmt[0];
1765 $open_paragraph = 1;
1767 # Anything in all uppercase on its own gets emphasised
1768 s/(^|[ \.\,\(\[\|\=])([A-Z]+?[A-Z0-9_]+)($|[ \.\,\*\?\|\)\=\'])/$1$fmt[6]$2$fmt[7]$3/g;
1776 print OUTPUT $fmt[13];
1778 if ($param_docs == 1 && $open_paragraph == 1)
1780 print OUTPUT $fmt[17];
1781 $open_paragraph = 0;
1783 if ($param_docs == 1)
1785 print OUTPUT $fmt[15];
1787 if ($open_paragraph == 1)
1789 print OUTPUT $fmt[1];
1793 # Create the master index file
1794 sub output_master_index_files()
1796 if ($opt_output_format eq "")
1798 return; # No master index for man pages
1801 if ($opt_output_format eq "h")
1803 # Append the index entries to the output db of index entries
1804 my $output_file = $opt_output_directory."/index.db";
1805 open(INDEXDB,">>$output_file") || die "Couldn't create $output_file\n";
1806 for (@index_entries_list)
1809 print INDEXDB $_."\n";
1814 # Use the comment output functions for consistency
1818 COMMENT_NAME => "The Wine Api Guide",
1819 ALT_NAME => "The Wine Api Guide",
1827 if ($opt_output_format eq "s" || $opt_output_format eq "x")
1829 $comment->{COMMENT_NAME} = "Introduction";
1830 $comment->{ALT_NAME} = "Introduction",
1832 elsif ($opt_output_format eq "h")
1834 @{$comment->{TEXT}} = (
1836 $comment->{COMMENT_NAME},
1841 # Create the initial comment text
1842 push (@{$comment->{TEXT}},
1843 "This document describes the Api calls made available",
1844 "by Wine. They are grouped by the dll that exports them.",
1846 "Please do not edit this document, since it is generated automatically",
1847 "from the Wine source code tree. Details on updating this documentation",
1848 "are given in the \"Wine Developers Guide\".",
1850 "Api documentation is generally written by the person who ",
1851 "implements a given Api call. Authors of each dll are listed in the overview ",
1852 "section for that dll. Additional contributors who have updated source files ",
1853 "but have not entered their names in a copyright statement are noted by an ",
1854 "entry in the file \"Changelog\" from the Wine source code distribution.",
1858 # Read in all dlls from the database of dll names
1859 my $input_file = $opt_output_directory."/dlls.db";
1860 my @dlls = `cat $input_file|sort|uniq`;
1862 if ($opt_output_format eq "h")
1864 # HTML gets a list of all the dlls and an index. For docbook the index creates this for us
1865 push (@{$comment->{TEXT}},
1867 "For an alphabetical listing of the functions available, please click the ",
1868 "first letter of the functions name below:","",
1869 "[ _(), A(), B(), C(), D(), E(), F(), G(), H(), ".
1870 "I(), J(), K(), L(), M(), N(), O(), P(), Q(), ".
1871 "R(), S(), T(), U(), V(), W(), X(), Y(), Z() ]", "",
1873 "Each dll provided by Wine is documented individually. The following dlls are provided :",
1876 # Add the dlls to the comment
1879 $_ =~ s/(\..*)?\n/\(\)/;
1880 push (@{$comment->{TEXT}}, $_, "");
1882 output_open_api_file("index");
1884 elsif ($opt_output_format eq "s" || $opt_output_format eq "x")
1886 # Just write this as the initial blurb, with a chapter heading
1887 output_open_api_file("blurb");
1888 print OUTPUT "<chapter id =\"blurb\">\n<title>Introduction to The Wine Api Guide</title>\n"
1891 # Write out the document
1892 output_api_header($comment);
1893 output_api_comment($comment);
1894 output_api_footer($comment);
1895 if ($opt_output_format eq "s" || $opt_output_format eq "x")
1897 print OUTPUT "</chapter>\n" # finish the chapter
1899 output_close_api_file();
1901 if ($opt_output_format eq "s")
1903 output_sgml_master_file(\@dlls);
1906 if ($opt_output_format eq "x")
1908 output_xml_master_file(\@dlls);
1911 if ($opt_output_format eq "h")
1913 output_html_index_files();
1914 output_html_stylesheet();
1919 # Write the master wine-api.xml, linking it to each dll.
1920 sub output_xml_master_file($)
1924 output_open_api_file("wine-api");
1925 print OUTPUT "<?xml version='1.0'?>";
1926 print OUTPUT "<!-- Generated file - DO NOT EDIT! -->\n";
1927 print OUTPUT "<!DOCTYPE book PUBLIC \"-//OASIS//DTD DocBook V5.0/EN\" ";
1928 print OUTPUT " \"http://www.docbook.org/xml/5.0/dtd/docbook.dtd\" [\n\n";
1929 print OUTPUT "<!ENTITY blurb SYSTEM \"blurb.xml\">\n";
1934 $_ =~ s/(\..*)?\n//;
1935 print OUTPUT "<!ENTITY ",$_," SYSTEM \"",$_,".xml\">\n"
1938 print OUTPUT "]>\n\n<book id=\"index\">\n<bookinfo><title>The Wine Api Guide</title></bookinfo>\n\n";
1939 print OUTPUT " &blurb;\n";
1943 print OUTPUT " &",$_,";\n"
1945 print OUTPUT "\n\n</book>\n";
1947 output_close_api_file();
1950 # Write the master wine-api.sgml, linking it to each dll.
1951 sub output_sgml_master_file($)
1955 output_open_api_file("wine-api");
1956 print OUTPUT "<!-- Generated file - DO NOT EDIT! -->\n";
1957 print OUTPUT "<!doctype book PUBLIC \"-//OASIS//DTD DocBook V3.1//EN\" [\n\n";
1958 print OUTPUT "<!entity blurb SYSTEM \"blurb.sgml\">\n";
1963 $_ =~ s/(\..*)?\n//;
1964 print OUTPUT "<!entity ",$_," SYSTEM \"",$_,".sgml\">\n"
1967 print OUTPUT "]>\n\n<book id=\"index\">\n<bookinfo><title>The Wine Api Guide</title></bookinfo>\n\n";
1968 print OUTPUT " &blurb;\n";
1972 print OUTPUT " &",$_,";\n"
1974 print OUTPUT "\n\n</book>\n";
1976 output_close_api_file();
1979 # Produce the sgml for the dll chapter from the generated files
1980 sub output_sgml_dll_file($)
1982 my $spec_details = shift;
1984 # Make a list of all the documentation files to include
1985 my $exports = $spec_details->{EXPORTS};
1986 my @source_files = ();
1989 # @$_ => ordinal, call convention, exported name, implementation name, documented;
1990 if (@$_[$EXPORT_CALL] ne "forward" && @$_[$EXPORT_CALL] ne "extern" &&
1991 @$_[$EXPORT_CALL] ne "stub" && @$_[$EXPORT_CALL] ne "equate" &&
1992 @$_[$EXPORT_CALL] ne "variable" && @$_[$EXPORT_CALL] ne "fake" &&
1993 @$_[$EXPORT_FLAGS] & $FLAG_DOCUMENTED)
1995 # A documented function
1996 push (@source_files,@$_[$EXPORT_IMPNAME]);
2000 push (@source_files,@{$spec_details->{EXTRA_COMMENTS}});
2002 @source_files = sort @source_files;
2004 # create a new chapter for this dll
2005 my $tmp_name = $opt_output_directory."/".$spec_details->{DLL_NAME}.".tmp";
2006 open(OUTPUT,">$tmp_name") || die "Couldn't create $tmp_name\n";
2007 print OUTPUT "<chapter>\n<title>$spec_details->{DLL_NAME}</title>\n";
2008 output_close_api_file();
2010 # Add the sorted documentation, cleaning up as we go
2011 `cat $opt_output_directory/$spec_details->{DLL_NAME}.sgml >>$tmp_name`;
2014 `cat $opt_output_directory/$_.sgml >>$tmp_name`;
2015 `rm -f $opt_output_directory/$_.sgml`;
2018 # close the chapter, and overwite the dll source
2019 open(OUTPUT,">>$tmp_name") || die "Couldn't create $tmp_name\n";
2020 print OUTPUT "</chapter>\n";
2022 `mv $tmp_name $opt_output_directory/$spec_details->{DLL_NAME}.sgml`;
2025 # Produce the xml for the dll chapter from the generated files
2026 sub output_xml_dll_file($)
2028 my $spec_details = shift;
2030 # Make a list of all the documentation files to include
2031 my $exports = $spec_details->{EXPORTS};
2032 my @source_files = ();
2035 # @$_ => ordinal, call convention, exported name, implementation name, documented;
2036 if (@$_[$EXPORT_CALL] ne "forward" && @$_[$EXPORT_CALL] ne "extern" &&
2037 @$_[$EXPORT_CALL] ne "stub" && @$_[$EXPORT_CALL] ne "equate" &&
2038 @$_[$EXPORT_CALL] ne "variable" && @$_[$EXPORT_CALL] ne "fake" &&
2039 @$_[$EXPORT_FLAGS] & $FLAG_DOCUMENTED)
2041 # A documented function
2042 push (@source_files,@$_[$EXPORT_IMPNAME]);
2046 push (@source_files,@{$spec_details->{EXTRA_COMMENTS}});
2048 @source_files = sort @source_files;
2050 # create a new chapter for this dll
2051 my $tmp_name = $opt_output_directory."/".$spec_details->{DLL_NAME}.".tmp";
2052 open(OUTPUT,">$tmp_name") || die "Couldn't create $tmp_name\n";
2053 print OUTPUT "<?xml version='1.0' encoding='UTF-8'?>\n<chapter>\n<title>$spec_details->{DLL_NAME}</title>\n";
2054 output_close_api_file();
2056 # Add the sorted documentation, cleaning up as we go
2057 `cat $opt_output_directory/$spec_details->{DLL_NAME}.xml >>$tmp_name`;
2060 `cat $opt_output_directory/$_.xml >>$tmp_name`;
2061 `rm -f $opt_output_directory/$_.xml`;
2064 # close the chapter, and overwite the dll source
2065 open(OUTPUT,">>$tmp_name") || die "Couldn't create $tmp_name\n";
2066 print OUTPUT "</chapter>\n";
2068 `mv $tmp_name $opt_output_directory/$spec_details->{DLL_NAME}.xml`;
2071 # Write the html index files containing the function names
2072 sub output_html_index_files()
2074 if ($opt_output_format ne "h")
2079 my @letters = ('_', 'A' .. 'Z');
2081 # Read in all functions
2082 my $input_file = $opt_output_directory."/index.db";
2083 my @funcs = `cat $input_file|sort|uniq`;
2100 $comment->{COMMENT_NAME} = $letter." Functions";
2101 $comment->{ALT_NAME} = $letter." Functions";
2103 push (@{$comment->{TEXT}},
2105 $comment->{COMMENT_NAME},
2109 # Add the functions to the comment
2112 my $first_char = substr ($_, 0, 1);
2113 $first_char = uc $first_char;
2115 if ($first_char eq $letter)
2119 $name =~ s/(^.*?)\,(.*?)\n/$1/;
2121 push (@{$comment->{TEXT}}, "{{".$name."}}{{".$file."}}","");
2125 # Write out the document
2126 output_open_api_file($letter);
2127 output_api_header($comment);
2128 output_api_comment($comment);
2129 output_api_footer($comment);
2130 output_close_api_file();
2134 # Output the stylesheet for HTML output
2135 sub output_html_stylesheet()
2137 if ($opt_output_format ne "h")
2143 ($css = <<HERE_TARGET) =~ s/^\s+//gm;
2145 * Default styles for Wine HTML Documentation.
2147 * This style sheet should be altered to suit your needs/taste.
2149 BODY { /* Page body */
2150 background-color: white;
2152 font-family: Tahoma,sans-serif;
2156 a:link { color: #4444ff; } /* Links */
2157 a:visited { color: #333377 }
2158 a:active { color: #0000dd }
2159 H2.section { /* Section Headers */
2160 font-family: sans-serif;
2162 background-color: #F0F0FE;
2164 margin-right: 1.0in;
2166 b.func_name { /* Function Name */
2170 i.dll_ord { /* Italicised DLL+ordinal */
2172 font-family: sans-serif;
2175 p { /* Paragraphs */
2177 margin-right: 0.5in;
2179 table { /* tables */
2181 margin-right: 0.5in;
2183 pre.proto /* API Function prototype */
2185 border-style: solid;
2187 border-color: #777777;
2188 background-color: #F0F0BB;
2191 vertical-align: top;
2193 margin-right: 1.0in;
2195 pre.raw { /* Raw text output */
2197 margin-right: 1.1in;
2198 background-color: #8080DC;
2200 tt.param { /* Parameter name */
2204 tt.const { /* Constant */
2207 i.in_out { /* In/Out */
2211 tt.coderef { /* Code in description text */
2214 b.emp /* Emphasis */ {
2218 i.footer { /* Footer */
2219 font-family: sans-serif;
2225 my $output_file = "$opt_output_directory/apidoc.css";
2226 open(CSS,">$output_file") || die "Couldn't create the file $output_file\n";
2234 print "\nCreate API Documentation from Wine source code.\n\n",
2235 "Usage: c2man.pl [options] {-w <spec>} {-I <include>} {<source>}\n",
2236 "Where: <spec> is a .spec file giving a DLL's exports.\n",
2237 " <include> is an include directory used by the DLL.\n",
2238 " <source> is a source file of the DLL.\n",
2239 " The above can be given multiple times on the command line, as appropriate.\n",
2241 " -Th : Output HTML instead of a man page\n",
2242 " -Ts : Output SGML (Docbook source) instead of a man page\n",
2243 " -C <dir> : Source directory, to find source files if they are not found in the\n",
2244 " current directory. Default is \"",$opt_source_dir,"\"\n",
2245 " -R <dir> : Root of build directory, default is \"",$opt_wine_root_dir,"\"\n",
2246 " -o <dir> : Create output in <dir>, default is \"",$opt_output_directory,"\"\n",
2247 " -s <sect>: Set manual section to <sect>, default is ",$opt_manual_section,"\n",
2248 " -e : Output \"FIXME\" documentation from empty comments.\n",
2249 " -v : Verbosity. Can be given more than once for more detail.\n";
2257 # Print usage if we're called with no args
2263 # Process command line options
2264 while(defined($_ = shift @ARGV))
2271 /^o$/ && do { $opt_output_directory = shift @ARGV; last; };
2272 s/^S// && do { $opt_manual_section = $_; last; };
2273 /^Th$/ && do { $opt_output_format = "h"; last; };
2274 /^Ts$/ && do { $opt_output_format = "s"; last; };
2275 /^Tx$/ && do { $opt_output_format = "x"; last; };
2276 /^v$/ && do { $opt_verbose++; last; };
2277 /^e$/ && do { $opt_output_empty = 1; last; };
2278 /^L$/ && do { last; };
2279 /^w$/ && do { @opt_spec_file_list = (@opt_spec_file_list, shift @ARGV); last; };
2280 s/^I// && do { if ($_ ne ".") {
2281 foreach my $include (`find $_/./ -type d ! -name tests`) {
2283 $include = $include."/*.h";
2284 $include =~ s/\/\//\//g;
2285 my $have_headers = `ls $include >/dev/null 2>&1`;
2286 if ($? >> 8 == 0) { @opt_header_file_list = (@opt_header_file_list, $include); }
2292 if ($_ ne "") { $opt_source_dir = $_; }
2295 s/^R// && do { if ($_ =~ /^\//) { $opt_wine_root_dir = $_; }
2296 else { $opt_wine_root_dir = `cd $pwd/$_ && pwd`; }
2297 $opt_wine_root_dir =~ s/\n//;
2298 $opt_wine_root_dir =~ s/\/\//\//g;
2299 if (! $opt_wine_root_dir =~ /\/$/ ) { $opt_wine_root_dir = $opt_wine_root_dir."/"; };
2302 die "Unrecognised option $_\n";
2308 push (@opt_source_file_list, $_);
2312 # Remove duplicate include directories
2314 @opt_header_file_list = grep(!$htmp{$_}++, @opt_header_file_list);
2316 if ($opt_verbose > 3)
2318 print "Output dir:'".$opt_output_directory."'\n";
2319 print "Section :'".$opt_manual_section."'\n";
2320 print "Format :'".$opt_output_format."'\n";
2321 print "Source dir:'".$opt_source_dir."'\n";
2322 print "Root :'".$opt_wine_root_dir."'\n";
2323 print "Spec files:'@opt_spec_file_list'\n";
2324 print "Includes :'@opt_header_file_list'\n";
2325 print "Sources :'@opt_source_file_list'\n";
2328 if (@opt_spec_file_list == 0)
2330 exit 0; # Don't bother processing non-dll files
2333 # Make sure the output directory exists
2334 unless (-d $opt_output_directory)
2336 mkdir $opt_output_directory or die "Cannot create directory $opt_output_directory\n";
2339 # Read in each .spec files exports and other details
2340 while(my $spec_file = shift @opt_spec_file_list)
2342 process_spec_file($spec_file);
2345 if ($opt_verbose > 3)
2347 foreach my $spec_file ( keys %spec_files )
2349 print "in '$spec_file':\n";
2350 my $spec_details = $spec_files{$spec_file}[0];
2351 my $exports = $spec_details->{EXPORTS};
2354 print @$_[$EXPORT_ORDINAL].",".@$_[$EXPORT_CALL].", ".
2355 @$_[$EXPORT_EXPNAME].",".@$_[$EXPORT_IMPNAME]."\n";
2360 # Extract and output the comments from each source file
2361 while(defined($_ = shift @opt_source_file_list))
2363 process_source_file($_);
2366 # Write the index files for each spec
2367 process_index_files();
2369 # Write the master index file
2370 output_master_index_files();