advapi32/tests: Mark a test result on Windows 2000 as broken.
[wine/multimedia.git] / tools / c2man.pl
bloba9c4150ea1e054acdd60d1157c61ad346889406d
1 #! /usr/bin/perl -w
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
22 # TODO
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?
29 use strict;
30 use bytes;
32 # Function flags. most of these come from the spec flags
33 my $FLAG_DOCUMENTED = 1;
34 my $FLAG_NONAME = 2;
35 my $FLAG_I386 = 4;
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.
48 # Options
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_parent_dir = "";
53 my $opt_wine_root_dir = "";
54 my $opt_output_format = ""; # '' = nroff, 'h' = html, 's' = sgml, 'x' = xml
55 my $opt_output_empty = 0; # Non-zero = Create 'empty' comments (for every implemented function)
56 my $opt_fussy = 1; # Non-zero = Create only if we have a RETURNS section
57 my $opt_verbose = 0; # >0 = verbosity. Can be given multiple times (for debugging)
58 my @opt_header_file_list = ();
59 my @opt_spec_file_list = ();
60 my @opt_source_file_list = ();
62 # All the collected details about all the .spec files being processed
63 my %spec_files;
64 # All the collected details about all the source files being processed
65 my %source_files;
66 # All documented functions that are to be placed in the index
67 my @index_entries_list = ();
69 # useful globals
70 my $pwd = `pwd`."/";
71 $pwd =~ s/\n//;
72 my @datetime = localtime;
73 my @months = ( "Jan", "Feb", "Mar", "Apr", "May", "Jun",
74 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" );
75 my $year = $datetime[5] + 1900;
76 my $date = "$months[$datetime[4]] $year";
79 sub output_api_comment($);
80 sub output_api_footer($);
81 sub output_api_header($);
82 sub output_api_name($);
83 sub output_api_synopsis($);
84 sub output_close_api_file();
85 sub output_comment($);
86 sub output_html_index_files();
87 sub output_html_stylesheet();
88 sub output_open_api_file($);
89 sub output_sgml_dll_file($);
90 sub output_xml_dll_file($);
91 sub output_sgml_master_file($);
92 sub output_xml_master_file($);
93 sub output_spec($);
94 sub process_comment($);
95 sub process_extra_comment($);
98 # Generate the list of exported entries for the dll
99 sub process_spec_file($)
101 my $spec_name = shift;
102 my ($dll_name, $dll_ext) = split(/\./, $spec_name);
103 $dll_ext = "dll" if ( $dll_ext eq "spec" );
104 my $uc_dll_name = uc $dll_name;
106 my $spec_details =
108 NAME => $spec_name,
109 DLL_NAME => $dll_name,
110 DLL_EXT => $dll_ext,
111 NUM_EXPORTS => 0,
112 NUM_STUBS => 0,
113 NUM_FUNCS => 0,
114 NUM_FORWARDS => 0,
115 NUM_VARS => 0,
116 NUM_DOCS => 0,
117 CONTRIBUTORS => [ ],
118 SOURCES => [ ],
119 DESCRIPTION => [ ],
120 EXPORTS => [ ],
121 EXPORTED_NAMES => { },
122 IMPLEMENTATION_NAMES => { },
123 EXTRA_COMMENTS => [ ],
124 CURRENT_EXTRA => [ ] ,
127 if ($opt_verbose > 0)
129 print "Processing ".$spec_name."\n";
132 # We allow opening to fail just to cater for the peculiarities of
133 # the Wine build system. This doesn't hurt, in any case
134 open(SPEC_FILE, "<$spec_name")
135 || (($opt_source_dir ne "")
136 && open(SPEC_FILE, "<$opt_source_dir/$spec_name"))
137 || return;
139 while(<SPEC_FILE>)
141 s/^\s+//; # Strip leading space
142 s/\s+\n$/\n/; # Strip trailing space
143 s/\s+/ /g; # Strip multiple tabs & spaces to a single space
144 s/\s*#.*//; # Strip comments
145 s/\(.*\)/ /; # Strip arguments
146 s/\s+/ /g; # Strip multiple tabs & spaces to a single space (again)
147 s/\n$//; # Strip newline
149 my $flags = 0;
150 if( /\-noname/ )
152 $flags |= $FLAG_NONAME;
154 if( /\-i386/ )
156 $flags |= $FLAG_I386;
158 if( /\-register/ )
160 $flags |= $FLAG_REGISTER;
162 s/ \-[a-z0-9=_]+//g; # Strip flags
164 if( /^(([0-9]+)|@) / )
166 # This line contains an exported symbol
167 my ($ordinal, $call_convention, $exported_name, $implementation_name) = split(' ');
169 for ($call_convention)
171 /^(cdecl|stdcall|varargs|pascal)$/
172 && do { $spec_details->{NUM_FUNCS}++; last; };
173 /^(variable|equate)$/
174 && do { $spec_details->{NUM_VARS}++; last; };
175 /^(extern)$/
176 && do { $spec_details->{NUM_FORWARDS}++; last; };
177 /^stub$/ && do { $spec_details->{NUM_STUBS}++; last; };
178 if ($opt_verbose > 0)
180 print "Warning: didn't recognise convention \'",$call_convention,"'\n";
182 last;
185 # Convert ordinal only names so we can find them later
186 if ($exported_name eq "@")
188 $exported_name = $uc_dll_name.'_'.$ordinal;
190 if (!defined($implementation_name))
192 $implementation_name = $exported_name;
194 if ($implementation_name eq "")
196 $implementation_name = $exported_name;
199 if ($implementation_name =~ /(.*?)\./)
201 $call_convention = "forward"; # Referencing a function from another dll
202 $spec_details->{NUM_FUNCS}--;
203 $spec_details->{NUM_FORWARDS}++;
206 # Add indices for the exported and implementation names
207 $spec_details->{EXPORTED_NAMES}{$exported_name} = $spec_details->{NUM_EXPORTS};
208 if ($implementation_name ne $exported_name)
210 $spec_details->{IMPLEMENTATION_NAMES}{$exported_name} = $spec_details->{NUM_EXPORTS};
213 # Add the exported entry
214 $spec_details->{NUM_EXPORTS}++;
215 my @export = ($ordinal, $call_convention, $exported_name, $implementation_name, $flags);
216 push (@{$spec_details->{EXPORTS}},[@export]);
219 close(SPEC_FILE);
221 # Add this .spec files details to the list of .spec files
222 $spec_files{$uc_dll_name} = [$spec_details];
225 # Read each source file, extract comments, and generate API documentation if appropriate
226 sub process_source_file($)
228 my $source_file = shift;
229 my $source_details =
231 CONTRIBUTORS => [ ],
232 DEBUG_CHANNEL => "",
234 my $comment =
236 FILE => $source_file,
237 COMMENT_NAME => "",
238 ALT_NAME => "",
239 DLL_NAME => "",
240 DLL_EXT => "",
241 ORDINAL => "",
242 RETURNS => "",
243 PROTOTYPE => [],
244 TEXT => [],
246 my $parse_state = 0;
247 my $ignore_blank_lines = 1;
248 my $extra_comment = 0; # 1 if this is an extra comment, i.e its not a .spec export
250 if ($opt_verbose > 0)
252 print "Processing ".$source_file."\n";
254 open(SOURCE_FILE,"<$source_file")
255 || (($opt_source_dir ne ".")
256 && open(SOURCE_FILE,"<$opt_source_dir/$source_file"))
257 || (($opt_parent_dir ne "")
258 && open(SOURCE_FILE,"<$opt_source_dir/$opt_parent_dir/$source_file"))
259 || die "couldn't open ".$source_file."\n";
261 # Add this source file to the list of source files
262 $source_files{$source_file} = [$source_details];
264 while(<SOURCE_FILE>)
266 s/\n$//; # Strip newline
267 s/^\s+//; # Strip leading space
268 s/\s+$//; # Strip trailing space
269 if (! /^\*\|/ )
271 # Strip multiple tabs & spaces to a single space
272 s/\s+/ /g;
275 if ( / +Copyright *(\([Cc]\))*[0-9 \-\,\/]*([[:alpha:][:^ascii:] \.\-]+)/ )
277 # Extract a contributor to this file
278 my $contributor = $2;
279 $contributor =~ s/ *$//;
280 $contributor =~ s/^by //;
281 $contributor =~ s/\.$//;
282 $contributor =~ s/ (for .*)/ \($1\)/;
283 if ($contributor ne "")
285 if ($opt_verbose > 3)
287 print "Info: Found contributor:'".$contributor."'\n";
289 push (@{$source_details->{CONTRIBUTORS}},$contributor);
292 elsif ( /WINE_DEFAULT_DEBUG_CHANNEL\(([A-Za-z]*)\)/ )
294 # Extract the debug channel to use
295 if ($opt_verbose > 3)
297 print "Info: Found debug channel:'".$1."'\n";
299 $source_details->{DEBUG_CHANNEL} = $1;
302 if ($parse_state == 0) # Searching for a comment
304 if ( /^\/\**$/ )
306 # This file is used by the DLL - Make sure we get our contributors right
307 @{$spec_files{$comment->{DLL_NAME}}[0]->{CURRENT_EXTRA}} = ();
308 push (@{$spec_files{$comment->{DLL_NAME}}[0]->{SOURCES}},$comment->{FILE});
309 # Found a comment start
310 $comment->{COMMENT_NAME} = "";
311 $comment->{ALT_NAME} = "";
312 $comment->{DLL_NAME} = "";
313 $comment->{ORDINAL} = "";
314 $comment->{RETURNS} = "";
315 $comment->{PROTOTYPE} = [];
316 $comment->{TEXT} = [];
317 $ignore_blank_lines = 1;
318 $extra_comment = 0;
319 $parse_state = 3;
322 elsif ($parse_state == 1) # Reading in a comment
324 if ( /^\**\// )
326 # Found the end of the comment
327 $parse_state = 2;
329 elsif ( s/^\*\|/\|/ )
331 # A line of comment not meant to be pre-processed
332 push (@{$comment->{TEXT}},$_); # Add the comment text
334 elsif ( s/^ *\** *// )
336 # A line of comment, starting with an asterisk
337 if ( /^[A-Z]+$/ || $_ eq "")
339 # This is a section start, so skip blank lines before and after it.
340 my $last_line = pop(@{$comment->{TEXT}});
341 if (defined($last_line) && $last_line ne "")
343 # Put it back
344 push (@{$comment->{TEXT}},$last_line);
346 if ( /^[A-Z]+$/ )
348 $ignore_blank_lines = 1;
350 else
352 $ignore_blank_lines = 0;
356 if ($ignore_blank_lines == 0 || $_ ne "")
358 push (@{$comment->{TEXT}},$_); # Add the comment text
361 else
363 # This isn't a well formatted comment: look for the next one
364 $parse_state = 0;
367 elsif ($parse_state == 2) # Finished reading in a comment
369 if ( /(WINAPIV|WINAPI|__cdecl|PASCAL|CALLBACK|FARPROC16)/ ||
370 /.*?\(/ )
372 # Comment is followed by a function definition
373 $parse_state = 4; # Fall through to read prototype
375 else
377 # Allow cpp directives and blank lines between the comment and prototype
378 if ($extra_comment == 1)
380 # An extra comment not followed by a function definition
381 $parse_state = 5; # Fall through to process comment
383 elsif (!/^\#/ && !/^ *$/ && !/^__ASM_GLOBAL_FUNC/)
385 # This isn't a well formatted comment: look for the next one
386 if ($opt_verbose > 1)
388 print "Info: Function '",$comment->{COMMENT_NAME},"' not followed by prototype.\n";
390 $parse_state = 0;
394 elsif ($parse_state == 3) # Reading in the first line of a comment
396 s/^ *\** *//;
397 if ( /^([\@A-Za-z0-9_]+) +(\(|\[)([A-Za-z0-9_]+)\.(([0-9]+)|@)(\)|\])\s*(.*)$/ )
399 # Found a correctly formed "ApiName (DLLNAME.Ordinal)" line.
400 if (defined ($7) && $7 ne "")
402 push (@{$comment->{TEXT}},$_); # Add the trailing comment text
404 $comment->{COMMENT_NAME} = $1;
405 $comment->{DLL_NAME} = uc $3;
406 $comment->{ORDINAL} = $4;
407 $comment->{DLL_NAME} =~ s/^KERNEL$/KRNL386/; # Too many of these to ignore, _old_ code
408 $parse_state = 1;
410 elsif ( /^([A-Za-z0-9_-]+) +\{([A-Za-z0-9_]+)\}$/ )
412 # Found a correctly formed "CommentTitle {DLLNAME}" line (extra documentation)
413 $comment->{COMMENT_NAME} = $1;
414 $comment->{DLL_NAME} = uc $2;
415 $comment->{ORDINAL} = "";
416 $extra_comment = 1;
417 $parse_state = 1;
419 else
421 # This isn't a well formatted comment: look for the next one
422 $parse_state = 0;
426 if ($parse_state == 4) # Reading in the function definition
428 push (@{$comment->{PROTOTYPE}},$_);
429 # Strip comments from the line before checking for ')'
430 my $stripped_line = $_;
431 $stripped_line =~ s/ *(\/\* *)(.*?)( *\*\/ *)//;
432 if ( $stripped_line =~ /\)/ )
434 # Strip a blank last line
435 my $last_line = pop(@{$comment->{TEXT}});
436 if (defined($last_line) && $last_line ne "")
438 # Put it back
439 push (@{$comment->{TEXT}},$last_line);
442 if ($opt_output_empty != 0 && @{$comment->{TEXT}} == 0)
444 # Create a 'not implemented' comment
445 @{$comment->{TEXT}} = ("fixme: This function has not yet been documented.");
447 $parse_state = 5;
451 if ($parse_state == 5) # Processing the comment
453 # Process it, if it has any text
454 if (@{$comment->{TEXT}} > 0)
456 if ($extra_comment == 1)
458 process_extra_comment($comment);
460 else
462 @{$comment->{TEXT}} = ("DESCRIPTION", @{$comment->{TEXT}});
463 process_comment($comment);
466 elsif ($opt_verbose > 1 && $opt_output_empty == 0)
468 print "Info: Function '",$comment->{COMMENT_NAME},"' has no documentation.\n";
470 $parse_state = 0;
473 close(SOURCE_FILE);
476 # Standardise a comments text for consistency
477 sub process_comment_text($)
479 my $comment = shift;
480 my $in_params = 0;
481 my @tmp_list = ();
482 my $i = 0;
484 for (@{$comment->{TEXT}})
486 my $line = $_;
488 if ( /^\s*$/ || /^[A-Z]+$/ || /^-/ )
490 $in_params = 0;
492 if ( $in_params > 0 && !/\[/ && !/\]/ )
494 # Possibly a continuation of the parameter description
495 my $last_line = pop(@tmp_list);
496 if ( $last_line =~ /\[/ && $last_line =~ /\]/ )
498 $line = $last_line." ".$_;
500 else
502 $in_params = 0;
503 push (@tmp_list, $last_line);
506 if ( /^(PARAMS|MEMBERS)$/ )
508 $in_params = 1;
510 push (@tmp_list, $line);
513 @{$comment->{TEXT}} = @tmp_list;
515 for (@{$comment->{TEXT}})
517 if (! /^\|/ )
519 # Map I/O values. These come in too many formats to standardise now....
520 s/\[I\]|\[i\]|\[in\]|\[IN\]/\[In\] /g;
521 s/\[O\]|\[o\]|\[out\]|\[OUT\]/\[Out\]/g;
522 s/\[I\/O\]|\[I\,O\]|\[i\/o\]|\[in\/out\]|\[IN\/OUT\]/\[In\/Out\]/g;
523 # TRUE/FALSE/NULL are defines, capitilise them
524 s/True|true/TRUE/g;
525 s/False|false/FALSE/g;
526 s/Null|null/NULL/g;
527 # Preferred capitalisations
528 s/ wine| WINE/ Wine/g;
529 s/ API | api / Api /g;
530 s/ DLL | Dll / dll /g;
531 s/ URL | url / Url /g;
532 s/WIN16|win16/Win16/g;
533 s/WIN32|win32/Win32/g;
534 s/WIN64|win64/Win64/g;
535 s/ ID | id / Id /g;
536 # Grammar
537 s/([a-z])\.([A-Z])/$1\. $2/g; # Space after full stop
538 s/ \:/\:/g; # Colons to the left
539 s/ \;/\;/g; # Semi-colons too
540 # Common idioms
541 s/^See ([A-Za-z0-9_]+)\.$/See $1\(\)\./; # Referring to A version from W
542 s/^Unicode version of ([A-Za-z0-9_]+)\.$/See $1\(\)\./; # Ditto
543 s/^64\-bit version of ([A-Za-z0-9_]+)\.$/See $1\(\)\./; # Referring to 32 bit version from 64
544 s/^PARAMETERS$/PARAMS/; # Name of parameter section should be 'PARAMS'
545 # Trademarks
546 s/( |\.)(M\$|MS|Microsoft|microsoft|micro\$oft|Micro\$oft)( |\.)/$1Microsoft\(tm\)$3/g;
547 s/( |\.)(Windows|windows|windoze|winblows)( |\.)/$1Windows\(tm\)$3/g;
548 s/( |\.)(DOS|dos|msdos)( |\.)/$1MS-DOS\(tm\)$3/g;
549 s/( |\.)(UNIX|unix)( |\.)/$1Unix\(tm\)$3/g;
550 s/( |\.)(LINIX|linux)( |\.)/$1Linux\(tm\)$3/g;
551 # Abbreviations
552 s/( char )/ character /g;
553 s/( chars )/ characters /g;
554 s/( info )/ information /g;
555 s/( app )/ application /g;
556 s/( apps )/ applications /g;
557 s/( exe )/ executable /g;
558 s/( ptr )/ pointer /g;
559 s/( obj )/ object /g;
560 s/( err )/ error /g;
561 s/( bool )/ boolean /g;
562 s/( no\. )/ number /g;
563 s/( No\. )/ Number /g;
564 # Punctuation
565 if ( /\[I|\[O/ && ! /\.$/ )
567 $_ = $_."."; # Always have a full stop at the end of parameter desc.
569 elsif ($i > 0 && /^[A-Z]*$/ &&
570 !(@{$comment->{TEXT}}[$i-1] =~ /\.$/) &&
571 !(@{$comment->{TEXT}}[$i-1] =~ /\:$/))
574 if (!(@{$comment->{TEXT}}[$i-1] =~ /^[A-Z]*$/))
576 # Paragraphs always end with a full stop
577 @{$comment->{TEXT}}[$i-1] = @{$comment->{TEXT}}[$i-1].".";
581 $i++;
585 # Standardise our comment and output it if it is suitable.
586 sub process_comment($)
588 my $comment = shift;
590 # Don't process this comment if the function isn't exported
591 my $spec_details = $spec_files{$comment->{DLL_NAME}}[0];
593 if (!defined($spec_details))
595 if ($opt_verbose > 2)
597 print "Warning: Function '".$comment->{COMMENT_NAME}."' belongs to '".
598 $comment->{DLL_NAME}."' (not passed with -w): not processing it.\n";
600 return;
603 if ($comment->{COMMENT_NAME} eq "@")
605 my $found = 0;
607 # Find the name from the .spec file
608 for (@{$spec_details->{EXPORTS}})
610 if (@$_[$EXPORT_ORDINAL] eq $comment->{ORDINAL})
612 $comment->{COMMENT_NAME} = @$_[$EXPORT_EXPNAME];
613 $found = 1;
617 if ($found == 0)
619 # Create an implementation name
620 $comment->{COMMENT_NAME} = $comment->{DLL_NAME}."_".$comment->{ORDINAL};
624 my $exported_names = $spec_details->{EXPORTED_NAMES};
625 my $export_index = $exported_names->{$comment->{COMMENT_NAME}};
626 my $implementation_names = $spec_details->{IMPLEMENTATION_NAMES};
628 if (!defined($export_index))
630 # Perhaps the comment uses the implementation name?
631 $export_index = $implementation_names->{$comment->{COMMENT_NAME}};
633 if (!defined($export_index))
635 # This function doesn't appear to be exported. hmm.
636 if ($opt_verbose > 2)
638 print "Warning: Function '".$comment->{COMMENT_NAME}."' claims to belong to '".
639 $comment->{DLL_NAME}."' but is not exported by it: not processing it.\n";
641 return;
644 # When the function is exported twice we have the second name below the first
645 # (you see this a lot in ntdll, but also in some other places).
646 my $first_line = ${$comment->{TEXT}}[1];
648 if ( $first_line =~ /^(@|[A-Za-z0-9_]+) +(\(|\[)([A-Za-z0-9_]+)\.(([0-9]+)|@)(\)|\])$/ )
650 # Found a second name - mark it as documented
651 my $alt_index = $exported_names->{$1};
652 if (defined($alt_index))
654 if ($opt_verbose > 2)
656 print "Info: Found alternate name '",$1,"\n";
658 my $alt_export = @{$spec_details->{EXPORTS}}[$alt_index];
659 @$alt_export[$EXPORT_FLAGS] |= $FLAG_DOCUMENTED;
660 $spec_details->{NUM_DOCS}++;
661 ${$comment->{TEXT}}[1] = "";
665 if (@{$spec_details->{CURRENT_EXTRA}})
667 # We have an extra comment that might be related to this one
668 my $current_comment = ${$spec_details->{CURRENT_EXTRA}}[0];
669 my $current_name = $current_comment->{COMMENT_NAME};
670 if ($comment->{COMMENT_NAME} =~ /^$current_name/ && $comment->{COMMENT_NAME} ne $current_name)
672 if ($opt_verbose > 2)
674 print "Linking ",$comment->{COMMENT_NAME}," to $current_name\n";
676 # Add a reference to this comment to our extra comment
677 push (@{$current_comment->{TEXT}}, $comment->{COMMENT_NAME}."()","");
681 # We want our docs generated using the implementation name, so they are unique
682 my $export = @{$spec_details->{EXPORTS}}[$export_index];
683 $comment->{COMMENT_NAME} = @$export[$EXPORT_IMPNAME];
684 $comment->{ALT_NAME} = @$export[$EXPORT_EXPNAME];
686 # Mark the function as documented
687 $spec_details->{NUM_DOCS}++;
688 @$export[$EXPORT_FLAGS] |= $FLAG_DOCUMENTED;
690 # If we have parameter comments in the prototype, extract them
691 my @parameter_comments;
692 for (@{$comment->{PROTOTYPE}})
694 s/ *\, */\,/g; # Strip spaces from around commas
696 if ( s/ *(\/\* *)(.*?)( *\*\/ *)// ) # Strip out comment
698 my $parameter_comment = $2;
699 if (!$parameter_comment =~ /^\[/ )
701 # Add [IO] markers so we format the comment correctly
702 $parameter_comment = "[fixme] ".$parameter_comment;
704 if ( /( |\*)([A-Za-z_]{1}[A-Za-z_0-9]*)(\,|\))/ )
706 # Add the parameter name
707 $parameter_comment = $2." ".$parameter_comment;
709 push (@parameter_comments, $parameter_comment);
713 # If we extracted any prototype comments, add them to the comment text.
714 if (@parameter_comments)
716 @parameter_comments = ("PARAMS", @parameter_comments);
717 my @new_comment = ();
718 my $inserted_params = 0;
720 for (@{$comment->{TEXT}})
722 if ( $inserted_params == 0 && /^[A-Z]+$/ )
724 # Found a section header, so this is where we insert
725 push (@new_comment, @parameter_comments);
726 $inserted_params = 1;
728 push (@new_comment, $_);
730 if ($inserted_params == 0)
732 # Add them to the end
733 push (@new_comment, @parameter_comments);
735 $comment->{TEXT} = [@new_comment];
738 if ($opt_fussy == 1 && $opt_output_empty == 0)
740 # Reject any comment that doesn't have a description or a RETURNS section.
741 # This is the default for now, 'coz many comments aren't suitable.
742 my $found_returns = 0;
743 my $found_description_text = 0;
744 my $in_description = 0;
745 for (@{$comment->{TEXT}})
747 if ( /^RETURNS$/ )
749 $found_returns = 1;
750 $in_description = 0;
752 elsif ( /^DESCRIPTION$/ )
754 $in_description = 1;
756 elsif ($in_description == 1)
758 if ( !/^[A-Z]+$/ )
760 # Don't reject comments that refer to another doc (e.g. A/W)
761 if ( /^See ([A-Za-z0-9_]+)\.$/ )
763 if ($comment->{COMMENT_NAME} =~ /W$/ )
765 # This is probably a Unicode version of an Ascii function.
766 # Create the Ascii name and see if its been documented
767 my $ascii_name = $comment->{COMMENT_NAME};
768 $ascii_name =~ s/W$/A/;
770 my $ascii_export_index = $exported_names->{$ascii_name};
772 if (!defined($ascii_export_index))
774 $ascii_export_index = $implementation_names->{$ascii_name};
776 if (!defined($ascii_export_index))
778 if ($opt_verbose > 2)
780 print "Warning: Function '".$comment->{COMMENT_NAME}."' is not an A/W pair.\n";
783 else
785 my $ascii_export = @{$spec_details->{EXPORTS}}[$ascii_export_index];
786 if (@$ascii_export[$EXPORT_FLAGS] & $FLAG_DOCUMENTED)
788 # Flag these functions as an A/W pair
789 @$ascii_export[$EXPORT_FLAGS] |= $FLAG_APAIR;
790 @$export[$EXPORT_FLAGS] |= $FLAG_WPAIR;
794 $found_returns = 1;
796 elsif ( /^Unicode version of ([A-Za-z0-9_]+)\.$/ )
798 @$export[$EXPORT_FLAGS] |= $FLAG_WPAIR; # Explicitly marked as W version
799 $found_returns = 1;
801 elsif ( /^64\-bit version of ([A-Za-z0-9_]+)\.$/ )
803 @$export[$EXPORT_FLAGS] |= $FLAG_64PAIR; # Explicitly marked as 64 bit version
804 $found_returns = 1;
806 $found_description_text = 1;
808 else
810 $in_description = 0;
814 if ($found_returns == 0 || $found_description_text == 0)
816 if ($opt_verbose > 2)
818 print "Info: Function '",$comment->{COMMENT_NAME},"' has no ",
819 "description and/or RETURNS section, skipping\n";
821 $spec_details->{NUM_DOCS}--;
822 @$export[$EXPORT_FLAGS] &= ~$FLAG_DOCUMENTED;
823 return;
827 process_comment_text($comment);
829 # Strip the prototypes return value, call convention, name and brackets
830 # (This leaves it as a list of types and names, or empty for void functions)
831 my $prototype = join(" ", @{$comment->{PROTOTYPE}});
832 $prototype =~ s/ / /g;
834 if ( $prototype =~ /(WINAPIV|WINAPI|__cdecl|PASCAL|CALLBACK|FARPROC16)/ )
836 $prototype =~ s/^(.*?)\s+(WINAPIV|WINAPI|__cdecl|PASCAL|CALLBACK|FARPROC16)\s+(.*?)\(\s*(.*)/$4/;
837 $comment->{RETURNS} = $1;
839 else
841 $prototype =~ s/^(.*?)([A-Za-z0-9_]+)\s*\(\s*(.*)/$3/;
842 $comment->{RETURNS} = $1;
845 $prototype =~ s/ *\).*//; # Strip end bracket
846 $prototype =~ s/ *\* */\*/g; # Strip space around pointers
847 $prototype =~ s/ *\, */\,/g; # Strip space around commas
848 $prototype =~ s/^(void|VOID)$//; # If void, leave blank
849 $prototype =~ s/\*([A-Za-z_])/\* $1/g; # Separate pointers from parameter name
850 @{$comment->{PROTOTYPE}} = split ( /,/ ,$prototype);
852 # FIXME: If we have no parameters, make sure we have a PARAMS: None. section
854 # Find header file
855 my $h_file = "";
856 if (@$export[$EXPORT_FLAGS] & $FLAG_NONAME)
858 $h_file = "Exported by ordinal only. Use GetProcAddress() to obtain a pointer to the function.";
860 else
862 if ($comment->{COMMENT_NAME} ne "")
864 my $tmp = "grep -s -l $comment->{COMMENT_NAME} @opt_header_file_list 2>/dev/null";
865 $tmp = `$tmp`;
866 my $exit_value = $? >> 8;
867 if ($exit_value == 0)
869 $tmp =~ s/\n.*//g;
870 if ($tmp ne "")
872 $h_file = "$tmp";
873 $h_file =~ s|^.*/\./||;
877 elsif ($comment->{ALT_NAME} ne "")
879 my $tmp = "grep -s -l $comment->{ALT_NAME} @opt_header_file_list"." 2>/dev/null";
880 $tmp = `$tmp`;
881 my $exit_value = $? >> 8;
882 if ($exit_value == 0)
884 $tmp =~ s/\n.*//g;
885 if ($tmp ne "")
887 $h_file = "$tmp";
888 $h_file =~ s|^.*/\./||;
892 $h_file =~ s/^ *//;
893 $h_file =~ s/\n//;
894 if ($h_file eq "")
896 $h_file = "Not declared in a Wine header. The function is either undocumented, or missing from Wine."
898 else
900 $h_file = "Declared in \"".$h_file."\".";
904 # Find source file
905 my $c_file = $comment->{FILE};
906 if ($opt_wine_root_dir ne "")
908 my $cfile = $pwd."/".$c_file; # Current dir + file
909 $cfile =~ s/(.+)(\/.*$)/$1/; # Strip the filename
910 $cfile = `cd $cfile && pwd`; # Strip any relative parts (e.g. "../../")
911 $cfile =~ s/\n//; # Strip newline
912 my $newfile = $c_file;
913 $newfile =~ s/(.+)(\/.*$)/$2/; # Strip all but the filename
914 $cfile = $cfile."/".$newfile; # Append filename to base path
915 $cfile =~ s/$opt_wine_root_dir//; # Get rid of the root directory
916 $cfile =~ s/\/\//\//g; # Remove any double slashes
917 $cfile =~ s/^\/+//; # Strip initial directory slash
918 $c_file = $cfile;
920 $c_file = "Implemented in \"".$c_file."\".";
922 # Add the implementation details
923 push (@{$comment->{TEXT}}, "IMPLEMENTATION","",$h_file,"",$c_file);
925 if (@$export[$EXPORT_FLAGS] & $FLAG_I386)
927 push (@{$comment->{TEXT}}, "", "Available on x86 platforms only.");
929 if (@$export[$EXPORT_FLAGS] & $FLAG_REGISTER)
931 push (@{$comment->{TEXT}}, "", "This function passes one or more arguments in registers. ",
932 "For more details, please read the source code.");
934 my $source_details = $source_files{$comment->{FILE}}[0];
935 if ($source_details->{DEBUG_CHANNEL} ne "")
937 push (@{$comment->{TEXT}}, "", "Debug channel \"".$source_details->{DEBUG_CHANNEL}."\".");
940 # Write out the documentation for the API
941 output_comment($comment)
944 # process our extra comment and output it if it is suitable.
945 sub process_extra_comment($)
947 my $comment = shift;
949 my $spec_details = $spec_files{$comment->{DLL_NAME}}[0];
951 if (!defined($spec_details))
953 if ($opt_verbose > 2)
955 print "Warning: Extra comment '".$comment->{COMMENT_NAME}."' belongs to '".
956 $comment->{DLL_NAME}."' (not passed with -w): not processing it.\n";
958 return;
961 # Check first to see if this is documentation for the DLL.
962 if ($comment->{COMMENT_NAME} eq $comment->{DLL_NAME})
964 if ($opt_verbose > 2)
966 print "Info: Found DLL documentation\n";
968 for (@{$comment->{TEXT}})
970 push (@{$spec_details->{DESCRIPTION}}, $_);
972 return;
975 # Add the comment to the DLL page as a link
976 push (@{$spec_details->{EXTRA_COMMENTS}},$comment->{COMMENT_NAME});
978 # If we have a prototype, process as a regular comment
979 if (@{$comment->{PROTOTYPE}})
981 $comment->{ORDINAL} = "@";
983 # Add an index for the comment name
984 $spec_details->{EXPORTED_NAMES}{$comment->{COMMENT_NAME}} = $spec_details->{NUM_EXPORTS};
986 # Add a fake exported entry
987 $spec_details->{NUM_EXPORTS}++;
988 my ($ordinal, $call_convention, $exported_name, $implementation_name, $documented) =
989 ("@", "fake", $comment->{COMMENT_NAME}, $comment->{COMMENT_NAME}, 0);
990 my @export = ($ordinal, $call_convention, $exported_name, $implementation_name, $documented);
991 push (@{$spec_details->{EXPORTS}},[@export]);
992 @{$comment->{TEXT}} = ("DESCRIPTION", @{$comment->{TEXT}});
993 process_comment($comment);
994 return;
997 if ($opt_verbose > 0)
999 print "Processing ",$comment->{COMMENT_NAME},"\n";
1002 if (@{$spec_details->{CURRENT_EXTRA}})
1004 my $current_comment = ${$spec_details->{CURRENT_EXTRA}}[0];
1006 if ($opt_verbose > 0)
1008 print "Processing old current: ",$current_comment->{COMMENT_NAME},"\n";
1010 # Output the current comment
1011 process_comment_text($current_comment);
1012 output_open_api_file($current_comment->{COMMENT_NAME});
1013 output_api_header($current_comment);
1014 output_api_name($current_comment);
1015 output_api_comment($current_comment);
1016 output_api_footer($current_comment);
1017 output_close_api_file();
1020 if ($opt_verbose > 2)
1022 print "Setting current to ",$comment->{COMMENT_NAME},"\n";
1025 my $comment_copy =
1027 FILE => $comment->{FILE},
1028 COMMENT_NAME => $comment->{COMMENT_NAME},
1029 ALT_NAME => $comment->{ALT_NAME},
1030 DLL_NAME => $comment->{DLL_NAME},
1031 ORDINAL => $comment->{ORDINAL},
1032 RETURNS => $comment->{RETURNS},
1033 PROTOTYPE => [],
1034 TEXT => [],
1037 for (@{$comment->{TEXT}})
1039 push (@{$comment_copy->{TEXT}}, $_);
1041 # Set this comment to be the current extra comment
1042 @{$spec_details->{CURRENT_EXTRA}} = ($comment_copy);
1045 # Write a standardised comment out in the appropriate format
1046 sub output_comment($)
1048 my $comment = shift;
1050 if ($opt_verbose > 0)
1052 print "Processing ",$comment->{COMMENT_NAME},"\n";
1055 if ($opt_verbose > 4)
1057 print "--PROTO--\n";
1058 for (@{$comment->{PROTOTYPE}})
1060 print "'".$_."'\n";
1063 print "--COMMENT--\n";
1064 for (@{$comment->{TEXT} })
1066 print $_."\n";
1070 output_open_api_file($comment->{COMMENT_NAME});
1071 output_api_header($comment);
1072 output_api_name($comment);
1073 output_api_synopsis($comment);
1074 output_api_comment($comment);
1075 output_api_footer($comment);
1076 output_close_api_file();
1079 # Write out an index file for each .spec processed
1080 sub process_index_files()
1082 foreach my $spec_file (keys %spec_files)
1084 my $spec_details = $spec_files{$spec_file}[0];
1085 if (defined ($spec_details->{DLL_NAME}))
1087 if (@{$spec_details->{CURRENT_EXTRA}})
1089 # We have an unwritten extra comment, write it
1090 my $current_comment = ${$spec_details->{CURRENT_EXTRA}}[0];
1091 process_extra_comment($current_comment);
1092 @{$spec_details->{CURRENT_EXTRA}} = ();
1094 output_spec($spec_details);
1099 # Write a spec files documentation out in the appropriate format
1100 sub output_spec($)
1102 my $spec_details = shift;
1104 if ($opt_verbose > 2)
1106 print "Writing:",$spec_details->{DLL_NAME},"\n";
1109 # Use the comment output functions for consistency
1110 my $comment =
1112 FILE => $spec_details->{DLL_NAME},
1113 COMMENT_NAME => $spec_details->{DLL_NAME}.".".$spec_details->{DLL_EXT},
1114 ALT_NAME => $spec_details->{DLL_NAME},
1115 DLL_NAME => "",
1116 ORDINAL => "",
1117 RETURNS => "",
1118 PROTOTYPE => [],
1119 TEXT => [],
1121 my $total_implemented = $spec_details->{NUM_FORWARDS} + $spec_details->{NUM_VARS} +
1122 $spec_details->{NUM_FUNCS};
1123 my $percent_implemented = 0;
1124 if ($total_implemented)
1126 $percent_implemented = $total_implemented /
1127 ($total_implemented + $spec_details->{NUM_STUBS}) * 100;
1129 $percent_implemented = int($percent_implemented);
1130 my $percent_documented = 0;
1131 if ($spec_details->{NUM_DOCS})
1133 # Treat forwards and data as documented funcs for statistics
1134 $percent_documented = $spec_details->{NUM_DOCS} / $spec_details->{NUM_FUNCS} * 100;
1135 $percent_documented = int($percent_documented);
1138 # Make a list of the contributors to this DLL.
1139 my @contributors;
1141 foreach my $source_file (keys %source_files)
1143 my $source_details = $source_files{$source_file}[0];
1144 for (@{$source_details->{CONTRIBUTORS}})
1146 push (@contributors, $_);
1150 my %saw;
1151 @contributors = grep(!$saw{$_}++, @contributors); # remove dups, from perlfaq4 manpage
1152 @contributors = sort @contributors;
1154 # Remove duplicates and blanks
1155 for(my $i=0; $i<@contributors; $i++)
1157 if ($i > 0 && ($contributors[$i] =~ /$contributors[$i-1]/ || $contributors[$i-1] eq ""))
1159 $contributors[$i-1] = $contributors[$i];
1162 undef %saw;
1163 @contributors = grep(!$saw{$_}++, @contributors);
1165 if ($opt_verbose > 3)
1167 print "Contributors:\n";
1168 for (@contributors)
1170 print "'".$_."'\n";
1173 my $contribstring = join (", ", @contributors);
1175 # Create the initial comment text
1176 @{$comment->{TEXT}} = (
1177 "NAME",
1178 $comment->{COMMENT_NAME}
1181 # Add the description, if we have one
1182 if (@{$spec_details->{DESCRIPTION}})
1184 push (@{$comment->{TEXT}}, "DESCRIPTION");
1185 for (@{$spec_details->{DESCRIPTION}})
1187 push (@{$comment->{TEXT}}, $_);
1191 # Add the statistics and contributors
1192 push (@{$comment->{TEXT}},
1193 "STATISTICS",
1194 "Forwards: ".$spec_details->{NUM_FORWARDS},
1195 "Variables: ".$spec_details->{NUM_VARS},
1196 "Stubs: ".$spec_details->{NUM_STUBS},
1197 "Functions: ".$spec_details->{NUM_FUNCS},
1198 "Exports-Total: ".$spec_details->{NUM_EXPORTS},
1199 "Implemented-Total: ".$total_implemented." (".$percent_implemented."%)",
1200 "Documented-Total: ".$spec_details->{NUM_DOCS}." (".$percent_documented."%)",
1201 "CONTRIBUTORS",
1202 "The following people hold copyrights on the source files comprising this dll:",
1204 $contribstring,
1205 "Note: This list may not be complete.",
1206 "For a complete listing, see the git commit logs and the File \"AUTHORS\" in the Wine source tree.",
1210 if ($opt_output_format eq "h")
1212 # Add the exports to the comment text
1213 push (@{$comment->{TEXT}},"EXPORTS");
1214 my $exports = $spec_details->{EXPORTS};
1215 for (@$exports)
1217 my $line = "";
1219 # @$_ => ordinal, call convention, exported name, implementation name, flags;
1220 if (@$_[$EXPORT_CALL] eq "forward")
1222 my $forward_dll = @$_[$EXPORT_IMPNAME];
1223 $forward_dll =~ s/\.(.*)//;
1224 $line = @$_[$EXPORT_EXPNAME]." (forward to ".$1."() in ".$forward_dll."())";
1226 elsif (@$_[$EXPORT_CALL] eq "extern")
1228 $line = @$_[$EXPORT_EXPNAME]." (extern)";
1230 elsif (@$_[$EXPORT_CALL] eq "stub")
1232 $line = @$_[$EXPORT_EXPNAME]." (stub)";
1234 elsif (@$_[$EXPORT_CALL] eq "fake")
1236 # Don't add this function here, it gets listed with the extra documentation
1237 if (!(@$_[$EXPORT_FLAGS] & $FLAG_WPAIR))
1239 # This function should be indexed
1240 push (@index_entries_list, @$_[$EXPORT_IMPNAME].",".@$_[$EXPORT_IMPNAME]);
1243 elsif (@$_[$EXPORT_CALL] eq "equate" || @$_[$EXPORT_CALL] eq "variable")
1245 $line = @$_[$EXPORT_EXPNAME]." (data)";
1247 else
1249 # A function
1250 if (@$_[$EXPORT_FLAGS] & $FLAG_DOCUMENTED)
1252 # Documented
1253 $line = @$_[$EXPORT_EXPNAME]." (implemented as ".@$_[$EXPORT_IMPNAME]."())";
1254 if (@$_[$EXPORT_EXPNAME] ne @$_[$EXPORT_IMPNAME])
1256 $line = @$_[$EXPORT_EXPNAME]." (implemented as ".@$_[$EXPORT_IMPNAME]."())";
1258 else
1260 $line = @$_[$EXPORT_EXPNAME]."()";
1262 if (!(@$_[$EXPORT_FLAGS] & $FLAG_WPAIR))
1264 # This function should be indexed
1265 push (@index_entries_list, @$_[$EXPORT_EXPNAME].",".@$_[$EXPORT_IMPNAME]);
1268 else
1270 $line = @$_[$EXPORT_EXPNAME]." (not documented)";
1273 if ($line ne "")
1275 push (@{$comment->{TEXT}}, $line, "");
1279 # Add links to the extra documentation
1280 if (@{$spec_details->{EXTRA_COMMENTS}})
1282 push (@{$comment->{TEXT}}, "SEE ALSO");
1283 my %htmp;
1284 @{$spec_details->{EXTRA_COMMENTS}} = grep(!$htmp{$_}++, @{$spec_details->{EXTRA_COMMENTS}});
1285 for (@{$spec_details->{EXTRA_COMMENTS}})
1287 push (@{$comment->{TEXT}}, $_."()", "");
1291 # The dll entry should also be indexed
1292 push (@index_entries_list, $spec_details->{DLL_NAME}.",".$spec_details->{DLL_NAME});
1294 # Write out the document
1295 output_open_api_file($spec_details->{DLL_NAME});
1296 output_api_header($comment);
1297 output_api_comment($comment);
1298 output_api_footer($comment);
1299 output_close_api_file();
1301 # Add this dll to the database of dll names
1302 my $output_file = $opt_output_directory."/dlls.db";
1304 # Append the dllname to the output db of names
1305 open(DLLDB,">>$output_file") || die "Couldn't create $output_file\n";
1306 print DLLDB $spec_details->{DLL_NAME},"\n";
1307 close(DLLDB);
1309 if ($opt_output_format eq "s")
1311 output_sgml_dll_file($spec_details);
1312 return;
1315 if ($opt_output_format eq "x")
1317 output_xml_dll_file($spec_details);
1318 return;
1324 # OUTPUT FUNCTIONS
1325 # ----------------
1326 # Only these functions know anything about formatting for a specific
1327 # output type. The functions above work only with plain text.
1328 # This is to allow new types of output to be added easily.
1330 # Open the api file
1331 sub output_open_api_file($)
1333 my $output_name = shift;
1334 $output_name = $opt_output_directory."/".$output_name;
1336 if ($opt_output_format eq "h")
1338 $output_name = $output_name.".html";
1340 elsif ($opt_output_format eq "s")
1342 $output_name = $output_name.".sgml";
1344 elsif ($opt_output_format eq "x")
1346 $output_name = $output_name.".xml";
1348 else
1350 $output_name = $output_name.".".$opt_manual_section;
1352 open(OUTPUT,">$output_name") || die "Couldn't create file '$output_name'\n";
1355 # Close the api file
1356 sub output_close_api_file()
1358 close (OUTPUT);
1361 # Output the api file header
1362 sub output_api_header($)
1364 my $comment = shift;
1366 if ($opt_output_format eq "h")
1368 print OUTPUT "<!-- Generated file - DO NOT EDIT! -->\n";
1369 print OUTPUT "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n";
1370 print OUTPUT "<HTML>\n<HEAD>\n";
1371 print OUTPUT "<LINK REL=\"StyleSheet\" href=\"apidoc.css\" type=\"text/css\">\n";
1372 print OUTPUT "<META NAME=\"GENERATOR\" CONTENT=\"tools/c2man.pl\">\n";
1373 print OUTPUT "<META NAME=\"keywords\" CONTENT=\"Win32,Wine,API,$comment->{COMMENT_NAME}\">\n";
1374 print OUTPUT "<TITLE>Wine API: $comment->{COMMENT_NAME}</TITLE>\n</HEAD>\n<BODY>\n";
1376 elsif ($opt_output_format eq "s" || $opt_output_format eq "x")
1378 print OUTPUT "<!-- Generated file - DO NOT EDIT! -->\n",
1379 "<sect1>\n",
1380 "<title>$comment->{COMMENT_NAME}</title>\n";
1382 else
1384 print OUTPUT ".\\\" -*- nroff -*-\n.\\\" Generated file - DO NOT EDIT!\n".
1385 ".TH ",$comment->{COMMENT_NAME}," ",$opt_manual_section," \"",$date,"\" \"".
1386 "Wine API\" \"Wine API\"\n";
1390 sub output_api_footer($)
1392 if ($opt_output_format eq "h")
1394 print OUTPUT "<hr><p><i class=\"copy\">Copyright &copy ".$year." The Wine Project.".
1395 " All trademarks are the property of their respective owners.".
1396 " Visit <a href=\"http://www.winehq.org\">WineHQ</a> for license details.".
1397 " Generated $date.</i></p>\n</body>\n</html>\n";
1399 elsif ($opt_output_format eq "s" || $opt_output_format eq "x")
1401 print OUTPUT "</sect1>\n";
1402 return;
1404 else
1409 sub output_api_section_start($$)
1411 my $comment = shift;
1412 my $section_name = shift;
1414 if ($opt_output_format eq "h")
1416 print OUTPUT "\n<h2 class=\"section\">",$section_name,"</h2>\n";
1418 elsif ($opt_output_format eq "s" || $opt_output_format eq "x")
1420 print OUTPUT "<bridgehead>",$section_name,"</bridgehead>\n";
1422 else
1424 print OUTPUT "\n\.SH ",$section_name,"\n";
1428 sub output_api_section_end()
1430 # Not currently required by any output formats
1433 sub output_api_name($)
1435 my $comment = shift;
1436 my $readable_name = $comment->{COMMENT_NAME};
1437 $readable_name =~ s/-/ /g; # make section names more readable
1439 output_api_section_start($comment,"NAME");
1442 my $dll_ordinal = "";
1443 if ($comment->{ORDINAL} ne "")
1445 $dll_ordinal = "(".$comment->{DLL_NAME}.".".$comment->{ORDINAL}.")";
1447 if ($opt_output_format eq "h")
1449 print OUTPUT "<p><b class=\"func_name\">",$readable_name,
1450 "</b>&nbsp;&nbsp;<i class=\"dll_ord\">",
1451 ,$dll_ordinal,"</i></p>\n";
1453 elsif ($opt_output_format eq "s" || $opt_output_format eq "x")
1455 print OUTPUT "<para>\n <command>",$readable_name,"</command> <emphasis>",
1456 $dll_ordinal,"</emphasis>\n</para>\n";
1458 else
1460 print OUTPUT "\\fB",$readable_name,"\\fR ",$dll_ordinal;
1463 output_api_section_end();
1466 sub output_api_synopsis($)
1468 my $comment = shift;
1469 my @fmt;
1471 output_api_section_start($comment,"SYNOPSIS");
1473 if ($opt_output_format eq "h")
1475 print OUTPUT "<pre class=\"proto\">\n ", $comment->{RETURNS}," ",$comment->{COMMENT_NAME},"\n (\n";
1476 @fmt = ("", "\n", "<tt class=\"param\">", "</tt>");
1478 elsif ($opt_output_format eq "s" || $opt_output_format eq "x")
1480 print OUTPUT "<screen>\n ",$comment->{RETURNS}," ",$comment->{COMMENT_NAME},"\n (\n";
1481 @fmt = ("", "\n", "<emphasis>", "</emphasis>");
1483 else
1485 print OUTPUT $comment->{RETURNS}," ",$comment->{COMMENT_NAME},"\n (\n";
1486 @fmt = ("", "\n", "\\fI", "\\fR");
1489 # Since our prototype is output in a pre-formatted block, line up the
1490 # parameters and parameter comments in the same column.
1492 # First calculate where the columns should start
1493 my $biggest_length = 0;
1494 for(my $i=0; $i < @{$comment->{PROTOTYPE}}; $i++)
1496 my $line = ${$comment->{PROTOTYPE}}[$i];
1497 if ($line =~ /(.+?)([A-Za-z_][A-Za-z_0-9]*)$/)
1499 my $length = length $1;
1500 if ($length > $biggest_length)
1502 $biggest_length = $length;
1507 # Now pad the string with blanks
1508 for(my $i=0; $i < @{$comment->{PROTOTYPE}}; $i++)
1510 my $line = ${$comment->{PROTOTYPE}}[$i];
1511 if ($line =~ /(.+?)([A-Za-z_][A-Za-z_0-9]*)$/)
1513 my $pad_len = $biggest_length - length $1;
1514 my $padding = " " x ($pad_len);
1515 ${$comment->{PROTOTYPE}}[$i] = $1.$padding.$2;
1519 for(my $i=0; $i < @{$comment->{PROTOTYPE}}; $i++)
1521 # Format the parameter name
1522 my $line = ${$comment->{PROTOTYPE}}[$i];
1523 my $comma = ($i == @{$comment->{PROTOTYPE}}-1) ? "" : ",";
1524 $line =~ s/(.+?)([A-Za-z_][A-Za-z_0-9]*)$/ $fmt[0]$1$fmt[2]$2$fmt[3]$comma$fmt[1]/;
1525 print OUTPUT $line;
1528 if ($opt_output_format eq "h")
1530 print OUTPUT " )\n</pre>\n";
1532 elsif ($opt_output_format eq "s" || $opt_output_format eq "x")
1534 print OUTPUT " )\n</screen>\n";
1536 else
1538 print OUTPUT " )\n";
1541 output_api_section_end();
1544 sub output_api_comment($)
1546 my $comment = shift;
1547 my $open_paragraph = 0;
1548 my $open_raw = 0;
1549 my $param_docs = 0;
1550 my @fmt;
1552 if ($opt_output_format eq "h")
1554 @fmt = ("<p>", "</p>\n", "<tt class=\"const\">", "</tt>", "<b class=\"emp\">", "</b>",
1555 "<tt class=\"coderef\">", "</tt>", "<tt class=\"param\">", "</tt>",
1556 "<i class=\"in_out\">", "</i>", "<pre class=\"raw\">\n", "</pre>\n",
1557 "<table class=\"tab\"><colgroup><col><col><col></colgroup><tbody>\n",
1558 "</tbody></table>\n","<tr><td>","</td></tr>\n","</td>","</td><td>");
1560 elsif ($opt_output_format eq "s" || $opt_output_format eq "x")
1562 @fmt = ("<para>\n","\n</para>\n","<constant>","</constant>","<emphasis>","</emphasis>",
1563 "<command>","</command>","<constant>","</constant>","<emphasis>","</emphasis>",
1564 "<screen>\n","</screen>\n",
1565 "<informaltable frame=\"none\">\n<tgroup cols=\"3\">\n<tbody>\n",
1566 "</tbody>\n</tgroup>\n</informaltable>\n","<row><entry>","</entry></row>\n",
1567 "</entry>","</entry><entry>");
1569 else
1571 @fmt = ("\.PP\n", "\n", "\\fB", "\\fR", "\\fB", "\\fR", "\\fB", "\\fR", "\\fI", "\\fR",
1572 "\\fB", "\\fR ", "", "", "", "","","\n.PP\n","","");
1575 # Extract the parameter names
1576 my @parameter_names;
1577 for (@{$comment->{PROTOTYPE}})
1579 if ( /(.+?)([A-Za-z_][A-Za-z_0-9]*)$/ )
1581 push (@parameter_names, $2);
1585 for (@{$comment->{TEXT}})
1587 if ($opt_output_format eq "h" || $opt_output_format eq "s" || $opt_output_format eq "x")
1589 # Map special characters
1590 s/\&/\&amp;/g;
1591 s/\</\&lt;/g;
1592 s/\>/\&gt;/g;
1593 s/\([Cc]\)/\&copy;/g;
1594 s/\(tm\)/&#174;/;
1597 if ( s/^\|// )
1599 # Raw output
1600 if ($open_raw == 0)
1602 if ($open_paragraph == 1)
1604 # Close the open paragraph
1605 print OUTPUT $fmt[1];
1606 $open_paragraph = 0;
1608 # Start raw output
1609 print OUTPUT $fmt[12];
1610 $open_raw = 1;
1612 if ($opt_output_format eq "")
1614 print OUTPUT ".br\n"; # Prevent 'man' running these lines together
1616 print OUTPUT $_,"\n";
1618 else
1620 if ($opt_output_format eq "h")
1622 # Link to the file in WineHQ cvs
1623 s/^(Implemented in \")(.+?)(\"\.)/$1$2$3 http:\/\/source.winehq.org\/source\/$2/g;
1624 s/^(Declared in \")(.+?)(\"\.)/$1$2$3 http:\/\/source.winehq.org\/source\/include\/$2/g;
1626 # Highlight strings
1627 s/(\".+?\")/$fmt[2]$1$fmt[3]/g;
1628 # Highlight literal chars
1629 s/(\'.\')/$fmt[2]$1$fmt[3]/g;
1630 s/(\'.{2}\')/$fmt[2]$1$fmt[3]/g;
1631 # Highlight numeric constants
1632 s/( |\-|\+|\.|\()([0-9\-\.]+)( |\-|$|\.|\,|\*|\?|\))/$1$fmt[2]$2$fmt[3]$3/g;
1634 # Leading cases ("xxxx:","-") start new paragraphs & are emphasised
1635 # FIXME: Using bullet points for leading '-' would look nicer.
1636 if ($open_paragraph == 1 && $param_docs == 0)
1638 s/^(\-)/$fmt[1]$fmt[0]$fmt[4]$1$fmt[5]/;
1639 s/^([[A-Za-z\-]+\:)/$fmt[1]$fmt[0]$fmt[4]$1$fmt[5]/;
1641 else
1643 s/^(\-)/$fmt[4]$1$fmt[5]/;
1644 s/^([[A-Za-z\-]+\:)/$fmt[4]$1$fmt[5]/;
1647 if ($opt_output_format eq "h")
1649 # Html uses links for API calls
1650 while ( /([A-Za-z_]+[A-Za-z_0-9-]+)(\(\))/)
1652 my $link = $1;
1653 my $readable_link = $1;
1654 $readable_link =~ s/-/ /g;
1656 s/([A-Za-z_]+[A-Za-z_0-9-]+)(\(\))/<a href\=\"$link\.html\">$readable_link<\/a>/;
1658 # Index references
1659 s/\{\{(.*?)\}\}\{\{(.*?)\}\}/<a href\=\"$2\.html\">$1<\/a>/g;
1660 s/ ([A-Z_])(\(\))/<a href\=\"$1\.html\">$1<\/a>/g;
1661 # And references to COM objects (hey, they'll get documented one day)
1662 s/ (I[A-Z]{1}[A-Za-z0-9_]+) (Object|object|Interface|interface)/ <a href\=\"$1\.html\">$1<\/a> $2/g;
1663 # Convert any web addresses to real links
1664 s/(http\:\/\/)(.+?)($| )/<a href\=\"$1$2\">$2<\/a>$3/g;
1666 else
1668 if ($opt_output_format eq "")
1670 # Give the man section for API calls
1671 s/ ([A-Za-z_]+[A-Za-z_0-9-]+)\(\)/ $fmt[6]$1\($opt_manual_section\)$fmt[7]/g;
1673 else
1675 # Highlight API calls
1676 s/ ([A-Za-z_]+[A-Za-z_0-9-]+\(\))/ $fmt[6]$1$fmt[7]/g;
1679 # And references to COM objects
1680 s/ (I[A-Z]{1}[A-Za-z0-9_]+) (Object|object|Interface|interface)/ $fmt[6]$1$fmt[7] $2/g;
1683 if ($open_raw == 1)
1685 # Finish the raw output
1686 print OUTPUT $fmt[13];
1687 $open_raw = 0;
1690 if ( /^[A-Z]+$/ || /^SEE ALSO$/ )
1692 # Start of a new section
1693 if ($open_paragraph == 1)
1695 if ($param_docs == 1)
1697 print OUTPUT $fmt[17],$fmt[15];
1698 $param_docs = 0;
1700 else
1702 print OUTPUT $fmt[1];
1704 $open_paragraph = 0;
1706 output_api_section_start($comment,$_);
1707 if ( /^PARAMS$/ || /^MEMBERS$/ )
1709 print OUTPUT $fmt[14];
1710 $param_docs = 1;
1712 else
1714 #print OUTPUT $fmt[15];
1715 #$param_docs = 0;
1718 elsif ( /^$/ )
1720 # Empty line, indicating a new paragraph
1721 if ($open_paragraph == 1)
1723 if ($param_docs == 0)
1725 print OUTPUT $fmt[1];
1726 $open_paragraph = 0;
1730 else
1732 if ($param_docs == 1)
1734 if ($open_paragraph == 1)
1736 # For parameter docs, put each parameter into a new paragraph/table row
1737 print OUTPUT $fmt[17];
1738 $open_paragraph = 0;
1740 s/(\[.+\])( *)/$fmt[19]$fmt[10]$1$fmt[11]$fmt[19] /; # Format In/Out
1742 else
1744 # Within paragraph lines, prevent lines running together
1745 $_ = $_." ";
1748 # Format parameter names where they appear in the comment
1749 for my $parameter_name (@parameter_names)
1751 s/(^|[ \.\,\(\-\*])($parameter_name)($|[ \.\)\,\-\/]|(\=[^"]))/$1$fmt[8]$2$fmt[9]$3/g;
1753 # Structure dereferences include the dereferenced member
1754 s/(\-\>[A-Za-z_]+)/$fmt[8]$1$fmt[9]/g;
1755 s/(\-\&gt\;[A-Za-z_]+)/$fmt[8]$1$fmt[9]/g;
1757 if ($open_paragraph == 0)
1759 if ($param_docs == 1)
1761 print OUTPUT $fmt[16];
1763 else
1765 print OUTPUT $fmt[0];
1767 $open_paragraph = 1;
1769 # Anything in all uppercase on its own gets emphasised
1770 s/(^|[ \.\,\(\[\|\=])([A-Z]+?[A-Z0-9_]+)($|[ \.\,\*\?\|\)\=\'])/$1$fmt[6]$2$fmt[7]$3/g;
1772 print OUTPUT $_;
1776 if ($open_raw == 1)
1778 print OUTPUT $fmt[13];
1780 if ($param_docs == 1 && $open_paragraph == 1)
1782 print OUTPUT $fmt[17];
1783 $open_paragraph = 0;
1785 if ($param_docs == 1)
1787 print OUTPUT $fmt[15];
1789 if ($open_paragraph == 1)
1791 print OUTPUT $fmt[1];
1795 # Create the master index file
1796 sub output_master_index_files()
1798 if ($opt_output_format eq "")
1800 return; # No master index for man pages
1803 if ($opt_output_format eq "h")
1805 # Append the index entries to the output db of index entries
1806 my $output_file = $opt_output_directory."/index.db";
1807 open(INDEXDB,">>$output_file") || die "Couldn't create $output_file\n";
1808 for (@index_entries_list)
1810 $_ =~ s/A\,/\,/;
1811 print INDEXDB $_."\n";
1813 close(INDEXDB);
1816 # Use the comment output functions for consistency
1817 my $comment =
1819 FILE => "",
1820 COMMENT_NAME => "The Wine API Guide",
1821 ALT_NAME => "The Wine API Guide",
1822 DLL_NAME => "",
1823 ORDINAL => "",
1824 RETURNS => "",
1825 PROTOTYPE => [],
1826 TEXT => [],
1829 if ($opt_output_format eq "s" || $opt_output_format eq "x")
1831 $comment->{COMMENT_NAME} = "Introduction";
1832 $comment->{ALT_NAME} = "Introduction",
1834 elsif ($opt_output_format eq "h")
1836 @{$comment->{TEXT}} = (
1837 "NAME",
1838 $comment->{COMMENT_NAME},
1839 "INTRODUCTION",
1843 # Create the initial comment text
1844 push (@{$comment->{TEXT}},
1845 "This document describes the API calls made available",
1846 "by Wine. They are grouped by the dll that exports them.",
1848 "Please do not edit this document, since it is generated automatically",
1849 "from the Wine source code tree. Details on updating this documentation",
1850 "are given in the \"Wine Developers Guide\".",
1851 "CONTRIBUTORS",
1852 "API documentation is generally written by the person who ",
1853 "implements a given API call. Authors of each dll are listed in the overview ",
1854 "section for that dll. Additional contributors who have updated source files ",
1855 "but have not entered their names in a copyright statement are noted by an ",
1856 "entry in the git commit logs.",
1860 # Read in all dlls from the database of dll names
1861 my $input_file = $opt_output_directory."/dlls.db";
1862 my @dlls = `cat $input_file|sort|uniq`;
1864 if ($opt_output_format eq "h")
1866 # HTML gets a list of all the dlls and an index. For docbook the index creates this for us
1867 push (@{$comment->{TEXT}},
1868 "INDEX",
1869 "For an alphabetical listing of the functions available, please click the ",
1870 "first letter of the functions name below:","",
1871 "[ _(), A(), B(), C(), D(), E(), F(), G(), H(), ".
1872 "I(), J(), K(), L(), M(), N(), O(), P(), Q(), ".
1873 "R(), S(), T(), U(), V(), W(), X(), Y(), Z() ]", "",
1874 "DLLS",
1875 "Each dll provided by Wine is documented individually. The following dlls are provided :",
1878 # Add the dlls to the comment
1879 for (@dlls)
1881 $_ =~ s/(\..*)?\n/\(\)/;
1882 push (@{$comment->{TEXT}}, $_, "");
1884 output_open_api_file("index");
1886 elsif ($opt_output_format eq "s" || $opt_output_format eq "x")
1888 # Just write this as the initial blurb, with a chapter heading
1889 output_open_api_file("blurb");
1890 print OUTPUT "<chapter id =\"blurb\">\n<title>Introduction to The Wine API Guide</title>\n"
1893 # Write out the document
1894 output_api_header($comment);
1895 output_api_comment($comment);
1896 output_api_footer($comment);
1897 if ($opt_output_format eq "s" || $opt_output_format eq "x")
1899 print OUTPUT "</chapter>\n" # finish the chapter
1901 output_close_api_file();
1903 if ($opt_output_format eq "s")
1905 output_sgml_master_file(\@dlls);
1906 return;
1908 if ($opt_output_format eq "x")
1910 output_xml_master_file(\@dlls);
1911 return;
1913 if ($opt_output_format eq "h")
1915 output_html_index_files();
1916 output_html_stylesheet();
1917 return;
1921 # Write the master wine-api.xml, linking it to each dll.
1922 sub output_xml_master_file($)
1924 my $dlls = shift;
1926 output_open_api_file("wine-api");
1927 print OUTPUT "<?xml version='1.0'?>";
1928 print OUTPUT "<!-- Generated file - DO NOT EDIT! -->\n";
1929 print OUTPUT "<!DOCTYPE book PUBLIC \"-//OASIS//DTD DocBook V5.0/EN\" ";
1930 print OUTPUT " \"http://www.docbook.org/xml/5.0/dtd/docbook.dtd\" [\n\n";
1931 print OUTPUT "<!ENTITY blurb SYSTEM \"blurb.xml\">\n";
1933 # List the entities
1934 for (@$dlls)
1936 $_ =~ s/(\..*)?\n//;
1937 print OUTPUT "<!ENTITY ",$_," SYSTEM \"",$_,".xml\">\n"
1940 print OUTPUT "]>\n\n<book id=\"index\">\n<bookinfo><title>The Wine API Guide</title></bookinfo>\n\n";
1941 print OUTPUT " &blurb;\n";
1943 for (@$dlls)
1945 print OUTPUT " &",$_,";\n"
1947 print OUTPUT "\n\n</book>\n";
1949 output_close_api_file();
1952 # Write the master wine-api.sgml, linking it to each dll.
1953 sub output_sgml_master_file($)
1955 my $dlls = shift;
1957 output_open_api_file("wine-api");
1958 print OUTPUT "<!-- Generated file - DO NOT EDIT! -->\n";
1959 print OUTPUT "<!doctype book PUBLIC \"-//OASIS//DTD DocBook V3.1//EN\" [\n\n";
1960 print OUTPUT "<!entity blurb SYSTEM \"blurb.sgml\">\n";
1962 # List the entities
1963 for (@$dlls)
1965 $_ =~ s/(\..*)?\n//;
1966 print OUTPUT "<!entity ",$_," SYSTEM \"",$_,".sgml\">\n"
1969 print OUTPUT "]>\n\n<book id=\"index\">\n<bookinfo><title>The Wine API Guide</title></bookinfo>\n\n";
1970 print OUTPUT " &blurb;\n";
1972 for (@$dlls)
1974 print OUTPUT " &",$_,";\n"
1976 print OUTPUT "\n\n</book>\n";
1978 output_close_api_file();
1981 # Produce the sgml for the dll chapter from the generated files
1982 sub output_sgml_dll_file($)
1984 my $spec_details = shift;
1986 # Make a list of all the documentation files to include
1987 my $exports = $spec_details->{EXPORTS};
1988 my @source_files = ();
1989 for (@$exports)
1991 # @$_ => ordinal, call convention, exported name, implementation name, documented;
1992 if (@$_[$EXPORT_CALL] ne "forward" && @$_[$EXPORT_CALL] ne "extern" &&
1993 @$_[$EXPORT_CALL] ne "stub" && @$_[$EXPORT_CALL] ne "equate" &&
1994 @$_[$EXPORT_CALL] ne "variable" && @$_[$EXPORT_CALL] ne "fake" &&
1995 @$_[$EXPORT_FLAGS] & $FLAG_DOCUMENTED)
1997 # A documented function
1998 push (@source_files,@$_[$EXPORT_IMPNAME]);
2002 push (@source_files,@{$spec_details->{EXTRA_COMMENTS}});
2004 @source_files = sort @source_files;
2006 # create a new chapter for this dll
2007 my $tmp_name = $opt_output_directory."/".$spec_details->{DLL_NAME}.".tmp";
2008 open(OUTPUT,">$tmp_name") || die "Couldn't create $tmp_name\n";
2009 print OUTPUT "<chapter>\n<title>$spec_details->{DLL_NAME}</title>\n";
2010 output_close_api_file();
2012 # Add the sorted documentation, cleaning up as we go
2013 `cat $opt_output_directory/$spec_details->{DLL_NAME}.sgml >>$tmp_name`;
2014 for (@source_files)
2016 `cat $opt_output_directory/$_.sgml >>$tmp_name`;
2017 `rm -f $opt_output_directory/$_.sgml`;
2020 # close the chapter, and overwite the dll source
2021 open(OUTPUT,">>$tmp_name") || die "Couldn't create $tmp_name\n";
2022 print OUTPUT "</chapter>\n";
2023 close OUTPUT;
2024 `mv $tmp_name $opt_output_directory/$spec_details->{DLL_NAME}.sgml`;
2027 # Produce the xml for the dll chapter from the generated files
2028 sub output_xml_dll_file($)
2030 my $spec_details = shift;
2032 # Make a list of all the documentation files to include
2033 my $exports = $spec_details->{EXPORTS};
2034 my @source_files = ();
2035 for (@$exports)
2037 # @$_ => ordinal, call convention, exported name, implementation name, documented;
2038 if (@$_[$EXPORT_CALL] ne "forward" && @$_[$EXPORT_CALL] ne "extern" &&
2039 @$_[$EXPORT_CALL] ne "stub" && @$_[$EXPORT_CALL] ne "equate" &&
2040 @$_[$EXPORT_CALL] ne "variable" && @$_[$EXPORT_CALL] ne "fake" &&
2041 @$_[$EXPORT_FLAGS] & $FLAG_DOCUMENTED)
2043 # A documented function
2044 push (@source_files,@$_[$EXPORT_IMPNAME]);
2048 push (@source_files,@{$spec_details->{EXTRA_COMMENTS}});
2050 @source_files = sort @source_files;
2052 # create a new chapter for this dll
2053 my $tmp_name = $opt_output_directory."/".$spec_details->{DLL_NAME}.".tmp";
2054 open(OUTPUT,">$tmp_name") || die "Couldn't create $tmp_name\n";
2055 print OUTPUT "<?xml version='1.0' encoding='UTF-8'?>\n<chapter>\n<title>$spec_details->{DLL_NAME}</title>\n";
2056 output_close_api_file();
2058 # Add the sorted documentation, cleaning up as we go
2059 `cat $opt_output_directory/$spec_details->{DLL_NAME}.xml >>$tmp_name`;
2060 for (@source_files)
2062 `cat $opt_output_directory/$_.xml >>$tmp_name`;
2063 `rm -f $opt_output_directory/$_.xml`;
2066 # close the chapter, and overwite the dll source
2067 open(OUTPUT,">>$tmp_name") || die "Couldn't create $tmp_name\n";
2068 print OUTPUT "</chapter>\n";
2069 close OUTPUT;
2070 `mv $tmp_name $opt_output_directory/$spec_details->{DLL_NAME}.xml`;
2073 # Write the html index files containing the function names
2074 sub output_html_index_files()
2076 if ($opt_output_format ne "h")
2078 return;
2081 my @letters = ('_', 'A' .. 'Z');
2083 # Read in all functions
2084 my $input_file = $opt_output_directory."/index.db";
2085 my @funcs = `cat $input_file|sort|uniq`;
2087 for (@letters)
2089 my $letter = $_;
2090 my $comment =
2092 FILE => "",
2093 COMMENT_NAME => "",
2094 ALT_NAME => "",
2095 DLL_NAME => "",
2096 ORDINAL => "",
2097 RETURNS => "",
2098 PROTOTYPE => [],
2099 TEXT => [],
2102 $comment->{COMMENT_NAME} = $letter." Functions";
2103 $comment->{ALT_NAME} = $letter." Functions";
2105 push (@{$comment->{TEXT}},
2106 "NAME",
2107 $comment->{COMMENT_NAME},
2108 "FUNCTIONS"
2111 # Add the functions to the comment
2112 for (@funcs)
2114 my $first_char = substr ($_, 0, 1);
2115 $first_char = uc $first_char;
2117 if ($first_char eq $letter)
2119 my $name = $_;
2120 my $file;
2121 $name =~ s/(^.*?)\,(.*?)\n/$1/;
2122 $file = $2;
2123 push (@{$comment->{TEXT}}, "{{".$name."}}{{".$file."}}","");
2127 # Write out the document
2128 output_open_api_file($letter);
2129 output_api_header($comment);
2130 output_api_comment($comment);
2131 output_api_footer($comment);
2132 output_close_api_file();
2136 # Output the stylesheet for HTML output
2137 sub output_html_stylesheet()
2139 if ($opt_output_format ne "h")
2141 return;
2144 my $css;
2145 ($css = <<HERE_TARGET) =~ s/^\s+//gm;
2147 * Default styles for Wine HTML Documentation.
2149 * This style sheet should be altered to suit your needs/taste.
2151 BODY { /* Page body */
2152 background-color: white;
2153 color: black;
2154 font-family: Tahoma,sans-serif;
2155 font-style: normal;
2156 font-size: 10pt;
2158 a:link { color: #4444ff; } /* Links */
2159 a:visited { color: #333377 }
2160 a:active { color: #0000dd }
2161 H2.section { /* Section Headers */
2162 font-family: sans-serif;
2163 color: #777777;
2164 background-color: #F0F0FE;
2165 margin-left: 0.2in;
2166 margin-right: 1.0in;
2168 b.func_name { /* Function Name */
2169 font-size: 10pt;
2170 font-style: bold;
2172 i.dll_ord { /* Italicised DLL+ordinal */
2173 color: #888888;
2174 font-family: sans-serif;
2175 font-size: 8pt;
2177 p { /* Paragraphs */
2178 margin-left: 0.5in;
2179 margin-right: 0.5in;
2181 table { /* tables */
2182 margin-left: 0.5in;
2183 margin-right: 0.5in;
2185 pre.proto /* API Function prototype */
2187 border-style: solid;
2188 border-width: 1px;
2189 border-color: #777777;
2190 background-color: #F0F0BB;
2191 color: black;
2192 font-size: 10pt;
2193 vertical-align: top;
2194 margin-left: 0.5in;
2195 margin-right: 1.0in;
2197 pre.raw { /* Raw text output */
2198 margin-left: 0.6in;
2199 margin-right: 1.1in;
2200 background-color: #8080DC;
2202 tt.param { /* Parameter name */
2203 font-style: italic;
2204 color: blue;
2206 tt.const { /* Constant */
2207 color: red;
2209 i.in_out { /* In/Out */
2210 font-size: 8pt;
2211 color: grey;
2213 tt.coderef { /* Code in description text */
2214 color: darkgreen;
2216 b.emp /* Emphasis */ {
2217 font-style: bold;
2218 color: darkblue;
2220 i.footer { /* Footer */
2221 font-family: sans-serif;
2222 font-size: 6pt;
2223 color: darkgrey;
2225 HERE_TARGET
2227 my $output_file = "$opt_output_directory/apidoc.css";
2228 open(CSS,">$output_file") || die "Couldn't create the file $output_file\n";
2229 print CSS $css;
2230 close(CSS);
2234 sub usage()
2236 print "\nCreate API Documentation from Wine source code.\n\n",
2237 "Usage: c2man.pl [options] {-w <spec>} {-I <include>} {<source>}\n",
2238 "Where: <spec> is a .spec file giving a DLL's exports.\n",
2239 " <include> is an include directory used by the DLL.\n",
2240 " <source> is a source file of the DLL.\n",
2241 " The above can be given multiple times on the command line, as appropriate.\n",
2242 "Options:\n",
2243 " -Th : Output HTML instead of a man page\n",
2244 " -Ts : Output SGML (DocBook source) instead of a man page\n",
2245 " -C <dir> : Source directory, to find source files if they are not found in the\n",
2246 " current directory. Default is \"",$opt_source_dir,"\"\n",
2247 " -P <dir> : Parent source directory.\n",
2248 " -R <dir> : Root of build directory.\n",
2249 " -o <dir> : Create output in <dir>, default is \"",$opt_output_directory,"\"\n",
2250 " -s <sect>: Set manual section to <sect>, default is ",$opt_manual_section,"\n",
2251 " -e : Output \"FIXME\" documentation from empty comments.\n",
2252 " -v : Verbosity. Can be given more than once for more detail.\n";
2257 # Main
2260 # Print usage if we're called with no args
2261 if( @ARGV == 0)
2263 usage();
2266 # Process command line options
2267 while(defined($_ = shift @ARGV))
2269 if( s/^-// )
2271 # An option.
2272 for ($_)
2274 /^o$/ && do { $opt_output_directory = shift @ARGV; last; };
2275 s/^S// && do { $opt_manual_section = $_; last; };
2276 /^Th$/ && do { $opt_output_format = "h"; last; };
2277 /^Ts$/ && do { $opt_output_format = "s"; last; };
2278 /^Tx$/ && do { $opt_output_format = "x"; last; };
2279 /^v$/ && do { $opt_verbose++; last; };
2280 /^e$/ && do { $opt_output_empty = 1; last; };
2281 /^L$/ && do { last; };
2282 /^w$/ && do { @opt_spec_file_list = (@opt_spec_file_list, shift @ARGV); last; };
2283 s/^I// && do { if ($_ ne ".") {
2284 foreach my $include (`find $_/./ -type d ! -name tests`) {
2285 $include =~ s/\n//;
2286 $include = $include."/*.h";
2287 $include =~ s/\/\//\//g;
2288 my $have_headers = `ls $include >/dev/null 2>&1`;
2289 if ($? >> 8 == 0) { @opt_header_file_list = (@opt_header_file_list, $include); }
2292 last;
2294 s/^C// && do {
2295 if ($_ ne "") { $opt_source_dir = $_; }
2296 last;
2298 s/^P// && do {
2299 if ($_ ne "") { $opt_parent_dir = $_; }
2300 last;
2302 s/^R// && do { if ($_ =~ /^\//) { $opt_wine_root_dir = $_; }
2303 else { $opt_wine_root_dir = `cd $pwd/$_ && pwd`; }
2304 $opt_wine_root_dir =~ s/\n//;
2305 $opt_wine_root_dir =~ s/\/\//\//g;
2306 if (! $opt_wine_root_dir =~ /\/$/ ) { $opt_wine_root_dir = $opt_wine_root_dir."/"; };
2307 last;
2309 die "Unrecognised option $_\n";
2312 else
2314 # A source file.
2315 push (@opt_source_file_list, $_);
2319 # Remove duplicate include directories
2320 my %htmp;
2321 @opt_header_file_list = grep(!$htmp{$_}++, @opt_header_file_list);
2323 if ($opt_verbose > 3)
2325 print "Output dir:'".$opt_output_directory."'\n";
2326 print "Section :'".$opt_manual_section."'\n";
2327 print "Format :'".$opt_output_format."'\n";
2328 print "Source dir:'".$opt_source_dir."'\n";
2329 print "Root :'".$opt_wine_root_dir."'\n";
2330 print "Spec files:'@opt_spec_file_list'\n";
2331 print "Includes :'@opt_header_file_list'\n";
2332 print "Sources :'@opt_source_file_list'\n";
2335 if (@opt_spec_file_list == 0)
2337 exit 0; # Don't bother processing non-dll files
2340 # Make sure the output directory exists
2341 unless (-d $opt_output_directory)
2343 mkdir $opt_output_directory or die "Cannot create directory $opt_output_directory\n";
2346 # Read in each .spec files exports and other details
2347 while(my $spec_file = shift @opt_spec_file_list)
2349 process_spec_file($spec_file);
2352 if ($opt_verbose > 3)
2354 foreach my $spec_file ( keys %spec_files )
2356 print "in '$spec_file':\n";
2357 my $spec_details = $spec_files{$spec_file}[0];
2358 my $exports = $spec_details->{EXPORTS};
2359 for (@$exports)
2361 print @$_[$EXPORT_ORDINAL].",".@$_[$EXPORT_CALL].", ".
2362 @$_[$EXPORT_EXPNAME].",".@$_[$EXPORT_IMPNAME]."\n";
2367 # Extract and output the comments from each source file
2368 while(defined($_ = shift @opt_source_file_list))
2370 process_source_file($_);
2373 # Write the index files for each spec
2374 process_index_files();
2376 # Write the master index file
2377 output_master_index_files();
2379 exit 0;