mshtml: Removed duplicated includes.
[wine/wine-gecko.git] / tools / c2man.pl
blobce8a4447e92f23751968e540e9809f0e18d642d1
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_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
62 my %spec_files;
63 # All the collected details about all the source files being processed
64 my %source_files;
65 # All documented functions that are to be placed in the index
66 my @index_entries_list = ();
68 # useful globals
69 my $pwd = `pwd`."/";
70 $pwd =~ s/\n//;
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($);
92 sub output_spec($);
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;
105 my $spec_details =
107 NAME => $spec_name,
108 DLL_NAME => $dll_name,
109 DLL_EXT => $dll_ext,
110 NUM_EXPORTS => 0,
111 NUM_STUBS => 0,
112 NUM_FUNCS => 0,
113 NUM_FORWARDS => 0,
114 NUM_VARS => 0,
115 NUM_DOCS => 0,
116 CONTRIBUTORS => [ ],
117 SOURCES => [ ],
118 DESCRIPTION => [ ],
119 EXPORTS => [ ],
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"))
136 || return;
138 while(<SPEC_FILE>)
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
148 my $flags = 0;
149 if( /\-noname/ )
151 $flags |= $FLAG_NONAME;
153 if( /\-i386/ )
155 $flags |= $FLAG_I386;
157 if( /\-register/ )
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; };
174 /^(extern)$/
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";
181 last;
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]);
218 close(SPEC_FILE);
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;
228 my $source_details =
230 CONTRIBUTORS => [ ],
231 DEBUG_CHANNEL => "",
233 my $comment =
235 FILE => $source_file,
236 COMMENT_NAME => "",
237 ALT_NAME => "",
238 DLL_NAME => "",
239 DLL_EXT => "",
240 ORDINAL => "",
241 RETURNS => "",
242 PROTOTYPE => [],
243 TEXT => [],
245 my $parse_state = 0;
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];
261 while(<SOURCE_FILE>)
263 s/\n$//; # Strip newline
264 s/^\s+//; # Strip leading space
265 s/\s+$//; # Strip trailing space
266 if (! /^\*\|/ )
268 # Strip multiple tabs & spaces to a single space
269 s/\s+/ /g;
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
301 if ( /^\/\**$/ )
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;
312 $extra_comment = 0;
313 $parse_state = 3;
316 elsif ($parse_state == 1) # Reading in a comment
318 if ( /^\**\// )
320 # Found the end of the comment
321 $parse_state = 2;
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 "")
337 # Put it back
338 push (@{$comment->{TEXT}},$last_line);
340 if ( /^[A-Z]+$/ )
342 $ignore_blank_lines = 1;
344 else
346 $ignore_blank_lines = 0;
350 if ($ignore_blank_lines == 0 || $_ ne "")
352 push (@{$comment->{TEXT}},$_); # Add the comment text
355 else
357 # This isn't a well formatted comment: look for the next one
358 $parse_state = 0;
361 elsif ($parse_state == 2) # Finished reading in a comment
363 if ( /(WINAPIV|WINAPI|__cdecl|PASCAL|CALLBACK|FARPROC16)/ ||
364 /.*?\(/ )
366 # Comment is followed by a function definition
367 $parse_state = 4; # Fall through to read prototype
369 else
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";
384 $parse_state = 0;
388 elsif ($parse_state == 3) # Reading in the first line of a comment
390 s/^ *\** *//;
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
402 $parse_state = 1;
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} = "";
410 $extra_comment = 1;
411 $parse_state = 1;
413 else
415 # This isn't a well formatted comment: look for the next one
416 $parse_state = 0;
420 if ($parse_state == 4) # Reading in the function definition
422 # This file is used by the DLL - Make sure we get our contributors right
423 @{$spec_files{$comment->{DLL_NAME}}[0]->{CURRENT_EXTRA}} = ();
424 push (@{$spec_files{$comment->{DLL_NAME}}[0]->{SOURCES}},$comment->{FILE});
426 push (@{$comment->{PROTOTYPE}},$_);
427 # Strip comments from the line before checking for ')'
428 my $stripped_line = $_;
429 $stripped_line =~ s/ *(\/\* *)(.*?)( *\*\/ *)//;
430 if ( $stripped_line =~ /\)/ )
432 # Strip a blank last line
433 my $last_line = pop(@{$comment->{TEXT}});
434 if (defined($last_line) && $last_line ne "")
436 # Put it back
437 push (@{$comment->{TEXT}},$last_line);
440 if ($opt_output_empty != 0 && @{$comment->{TEXT}} == 0)
442 # Create a 'not implemented' comment
443 @{$comment->{TEXT}} = ("fixme: This function has not yet been documented.");
445 $parse_state = 5;
449 if ($parse_state == 5) # Processing the comment
451 # Process it, if it has any text
452 if (@{$comment->{TEXT}} > 0)
454 if ($extra_comment == 1)
456 process_extra_comment($comment);
458 else
460 @{$comment->{TEXT}} = ("DESCRIPTION", @{$comment->{TEXT}});
461 process_comment($comment);
464 elsif ($opt_verbose > 1 && $opt_output_empty == 0)
466 print "Info: Function '",$comment->{COMMENT_NAME},"' has no documentation.\n";
468 $parse_state = 0;
471 close(SOURCE_FILE);
474 # Standardise a comments text for consistency
475 sub process_comment_text($)
477 my $comment = shift;
478 my $in_params = 0;
479 my @tmp_list = ();
480 my $i = 0;
482 for (@{$comment->{TEXT}})
484 my $line = $_;
486 if ( /^\s*$/ || /^[A-Z]+$/ || /^-/ )
488 $in_params = 0;
490 if ( $in_params > 0 && !/\[/ && !/\]/ )
492 # Possibly a continuation of the parameter description
493 my $last_line = pop(@tmp_list);
494 if ( $last_line =~ /\[/ && $last_line =~ /\]/ )
496 $line = $last_line." ".$_;
498 else
500 $in_params = 0;
501 push (@tmp_list, $last_line);
504 if ( /^(PARAMS|MEMBERS)$/ )
506 $in_params = 1;
508 push (@tmp_list, $line);
511 @{$comment->{TEXT}} = @tmp_list;
513 for (@{$comment->{TEXT}})
515 if (! /^\|/ )
517 # Map I/O values. These come in too many formats to standardise now....
518 s/\[I\]|\[i\]|\[in\]|\[IN\]/\[In\] /g;
519 s/\[O\]|\[o\]|\[out\]|\[OUT\]/\[Out\]/g;
520 s/\[I\/O\]|\[I\,O\]|\[i\/o\]|\[in\/out\]|\[IN\/OUT\]/\[In\/Out\]/g;
521 # TRUE/FALSE/NULL are defines, capitilise them
522 s/True|true/TRUE/g;
523 s/False|false/FALSE/g;
524 s/Null|null/NULL/g;
525 # Preferred capitalisations
526 s/ wine| WINE/ Wine/g;
527 s/ API | api / Api /g;
528 s/ DLL | Dll / dll /g;
529 s/ URL | url / Url /g;
530 s/WIN16|win16/Win16/g;
531 s/WIN32|win32/Win32/g;
532 s/WIN64|win64/Win64/g;
533 s/ ID | id / Id /g;
534 # Grammar
535 s/([a-z])\.([A-Z])/$1\. $2/g; # Space after full stop
536 s/ \:/\:/g; # Colons to the left
537 s/ \;/\;/g; # Semi-colons too
538 # Common idioms
539 s/^See ([A-Za-z0-9_]+)\.$/See $1\(\)\./; # Referring to A version from W
540 s/^Unicode version of ([A-Za-z0-9_]+)\.$/See $1\(\)\./; # Ditto
541 s/^64\-bit version of ([A-Za-z0-9_]+)\.$/See $1\(\)\./; # Referring to 32 bit version from 64
542 s/^PARAMETERS$/PARAMS/; # Name of parameter section should be 'PARAMS'
543 # Trademarks
544 s/( |\.)(M\$|MS|Microsoft|microsoft|micro\$oft|Micro\$oft)( |\.)/$1Microsoft\(tm\)$3/g;
545 s/( |\.)(Windows|windows|windoze|winblows)( |\.)/$1Windows\(tm\)$3/g;
546 s/( |\.)(DOS|dos|msdos)( |\.)/$1MS-DOS\(tm\)$3/g;
547 s/( |\.)(UNIX|unix)( |\.)/$1Unix\(tm\)$3/g;
548 s/( |\.)(LINIX|linux)( |\.)/$1Linux\(tm\)$3/g;
549 # Abbreviations
550 s/( char )/ character /g;
551 s/( chars )/ characters /g;
552 s/( info )/ information /g;
553 s/( app )/ application /g;
554 s/( apps )/ applications /g;
555 s/( exe )/ executable /g;
556 s/( ptr )/ pointer /g;
557 s/( obj )/ object /g;
558 s/( err )/ error /g;
559 s/( bool )/ boolean /g;
560 s/( no\. )/ number /g;
561 s/( No\. )/ Number /g;
562 # Punctuation
563 if ( /\[I|\[O/ && ! /\.$/ )
565 $_ = $_."."; # Always have a full stop at the end of parameter desc.
567 elsif ($i > 0 && /^[A-Z]*$/ &&
568 !(@{$comment->{TEXT}}[$i-1] =~ /\.$/) &&
569 !(@{$comment->{TEXT}}[$i-1] =~ /\:$/))
572 if (!(@{$comment->{TEXT}}[$i-1] =~ /^[A-Z]*$/))
574 # Paragraphs always end with a full stop
575 @{$comment->{TEXT}}[$i-1] = @{$comment->{TEXT}}[$i-1].".";
579 $i++;
583 # Standardise our comment and output it if it is suitable.
584 sub process_comment($)
586 my $comment = shift;
588 # Don't process this comment if the function isn't exported
589 my $spec_details = $spec_files{$comment->{DLL_NAME}}[0];
591 if (!defined($spec_details))
593 if ($opt_verbose > 2)
595 print "Warning: Function '".$comment->{COMMENT_NAME}."' belongs to '".
596 $comment->{DLL_NAME}."' (not passed with -w): not processing it.\n";
598 return;
601 if ($comment->{COMMENT_NAME} eq "@")
603 my $found = 0;
605 # Find the name from the .spec file
606 for (@{$spec_details->{EXPORTS}})
608 if (@$_[$EXPORT_ORDINAL] eq $comment->{ORDINAL})
610 $comment->{COMMENT_NAME} = @$_[$EXPORT_EXPNAME];
611 $found = 1;
615 if ($found == 0)
617 # Create an implementation name
618 $comment->{COMMENT_NAME} = $comment->{DLL_NAME}."_".$comment->{ORDINAL};
622 my $exported_names = $spec_details->{EXPORTED_NAMES};
623 my $export_index = $exported_names->{$comment->{COMMENT_NAME}};
624 my $implementation_names = $spec_details->{IMPLEMENTATION_NAMES};
626 if (!defined($export_index))
628 # Perhaps the comment uses the implementation name?
629 $export_index = $implementation_names->{$comment->{COMMENT_NAME}};
631 if (!defined($export_index))
633 # This function doesn't appear to be exported. hmm.
634 if ($opt_verbose > 2)
636 print "Warning: Function '".$comment->{COMMENT_NAME}."' claims to belong to '".
637 $comment->{DLL_NAME}."' but is not exported by it: not processing it.\n";
639 return;
642 # When the function is exported twice we have the second name below the first
643 # (you see this a lot in ntdll, but also in some other places).
644 my $first_line = ${$comment->{TEXT}}[1];
646 if ( $first_line =~ /^(@|[A-Za-z0-9_]+) +(\(|\[)([A-Za-z0-9_]+)\.(([0-9]+)|@)(\)|\])$/ )
648 # Found a second name - mark it as documented
649 my $alt_index = $exported_names->{$1};
650 if (defined($alt_index))
652 if ($opt_verbose > 2)
654 print "Info: Found alternate name '",$1,"\n";
656 my $alt_export = @{$spec_details->{EXPORTS}}[$alt_index];
657 @$alt_export[$EXPORT_FLAGS] |= $FLAG_DOCUMENTED;
658 $spec_details->{NUM_DOCS}++;
659 ${$comment->{TEXT}}[1] = "";
663 if (@{$spec_details->{CURRENT_EXTRA}})
665 # We have an extra comment that might be related to this one
666 my $current_comment = ${$spec_details->{CURRENT_EXTRA}}[0];
667 my $current_name = $current_comment->{COMMENT_NAME};
668 if ($comment->{COMMENT_NAME} =~ /^$current_name/ && $comment->{COMMENT_NAME} ne $current_name)
670 if ($opt_verbose > 2)
672 print "Linking ",$comment->{COMMENT_NAME}," to $current_name\n";
674 # Add a reference to this comment to our extra comment
675 push (@{$current_comment->{TEXT}}, $comment->{COMMENT_NAME}."()","");
679 # We want our docs generated using the implementation name, so they are unique
680 my $export = @{$spec_details->{EXPORTS}}[$export_index];
681 $comment->{COMMENT_NAME} = @$export[$EXPORT_IMPNAME];
682 $comment->{ALT_NAME} = @$export[$EXPORT_EXPNAME];
684 # Mark the function as documented
685 $spec_details->{NUM_DOCS}++;
686 @$export[$EXPORT_FLAGS] |= $FLAG_DOCUMENTED;
688 # If we have parameter comments in the prototype, extract them
689 my @parameter_comments;
690 for (@{$comment->{PROTOTYPE}})
692 s/ *\, */\,/g; # Strip spaces from around commas
694 if ( s/ *(\/\* *)(.*?)( *\*\/ *)// ) # Strip out comment
696 my $parameter_comment = $2;
697 if (!$parameter_comment =~ /^\[/ )
699 # Add [IO] markers so we format the comment correctly
700 $parameter_comment = "[fixme] ".$parameter_comment;
702 if ( /( |\*)([A-Za-z_]{1}[A-Za-z_0-9]*)(\,|\))/ )
704 # Add the parameter name
705 $parameter_comment = $2." ".$parameter_comment;
707 push (@parameter_comments, $parameter_comment);
711 # If we extracted any prototype comments, add them to the comment text.
712 if (@parameter_comments)
714 @parameter_comments = ("PARAMS", @parameter_comments);
715 my @new_comment = ();
716 my $inserted_params = 0;
718 for (@{$comment->{TEXT}})
720 if ( $inserted_params == 0 && /^[A-Z]+$/ )
722 # Found a section header, so this is where we insert
723 push (@new_comment, @parameter_comments);
724 $inserted_params = 1;
726 push (@new_comment, $_);
728 if ($inserted_params == 0)
730 # Add them to the end
731 push (@new_comment, @parameter_comments);
733 $comment->{TEXT} = [@new_comment];
736 if ($opt_fussy == 1 && $opt_output_empty == 0)
738 # Reject any comment that doesn't have a description or a RETURNS section.
739 # This is the default for now, 'coz many comments aren't suitable.
740 my $found_returns = 0;
741 my $found_description_text = 0;
742 my $in_description = 0;
743 for (@{$comment->{TEXT}})
745 if ( /^RETURNS$/ )
747 $found_returns = 1;
748 $in_description = 0;
750 elsif ( /^DESCRIPTION$/ )
752 $in_description = 1;
754 elsif ($in_description == 1)
756 if ( !/^[A-Z]+$/ )
758 # Don't reject comments that refer to another doc (e.g. A/W)
759 if ( /^See ([A-Za-z0-9_]+)\.$/ )
761 if ($comment->{COMMENT_NAME} =~ /W$/ )
763 # This is probably a Unicode version of an Ascii function.
764 # Create the Ascii name and see if its been documented
765 my $ascii_name = $comment->{COMMENT_NAME};
766 $ascii_name =~ s/W$/A/;
768 my $ascii_export_index = $exported_names->{$ascii_name};
770 if (!defined($ascii_export_index))
772 $ascii_export_index = $implementation_names->{$ascii_name};
774 if (!defined($ascii_export_index))
776 if ($opt_verbose > 2)
778 print "Warning: Function '".$comment->{COMMENT_NAME}."' is not an A/W pair.\n";
781 else
783 my $ascii_export = @{$spec_details->{EXPORTS}}[$ascii_export_index];
784 if (@$ascii_export[$EXPORT_FLAGS] & $FLAG_DOCUMENTED)
786 # Flag these functions as an A/W pair
787 @$ascii_export[$EXPORT_FLAGS] |= $FLAG_APAIR;
788 @$export[$EXPORT_FLAGS] |= $FLAG_WPAIR;
792 $found_returns = 1;
794 elsif ( /^Unicode version of ([A-Za-z0-9_]+)\.$/ )
796 @$export[$EXPORT_FLAGS] |= $FLAG_WPAIR; # Explicitly marked as W version
797 $found_returns = 1;
799 elsif ( /^64\-bit version of ([A-Za-z0-9_]+)\.$/ )
801 @$export[$EXPORT_FLAGS] |= $FLAG_64PAIR; # Explicitly marked as 64 bit version
802 $found_returns = 1;
804 $found_description_text = 1;
806 else
808 $in_description = 0;
812 if ($found_returns == 0 || $found_description_text == 0)
814 if ($opt_verbose > 2)
816 print "Info: Function '",$comment->{COMMENT_NAME},"' has no ",
817 "description and/or RETURNS section, skipping\n";
819 $spec_details->{NUM_DOCS}--;
820 @$export[$EXPORT_FLAGS] &= ~$FLAG_DOCUMENTED;
821 return;
825 process_comment_text($comment);
827 # Strip the prototypes return value, call convention, name and brackets
828 # (This leaves it as a list of types and names, or empty for void functions)
829 my $prototype = join(" ", @{$comment->{PROTOTYPE}});
830 $prototype =~ s/ / /g;
832 if ( $prototype =~ /(WINAPIV|WINAPI|__cdecl|PASCAL|CALLBACK|FARPROC16)/ )
834 $prototype =~ s/^(.*?)\s+(WINAPIV|WINAPI|__cdecl|PASCAL|CALLBACK|FARPROC16)\s+(.*?)\(\s*(.*)/$4/;
835 $comment->{RETURNS} = $1;
837 else
839 $prototype =~ s/^(.*?)([A-Za-z0-9_]+)\s*\(\s*(.*)/$3/;
840 $comment->{RETURNS} = $1;
843 $prototype =~ s/ *\).*//; # Strip end bracket
844 $prototype =~ s/ *\* */\*/g; # Strip space around pointers
845 $prototype =~ s/ *\, */\,/g; # Strip space around commas
846 $prototype =~ s/^(void|VOID)$//; # If void, leave blank
847 $prototype =~ s/\*([A-Za-z_])/\* $1/g; # Separate pointers from parameter name
848 @{$comment->{PROTOTYPE}} = split ( /,/ ,$prototype);
850 # FIXME: If we have no parameters, make sure we have a PARAMS: None. section
852 # Find header file
853 my $h_file = "";
854 if (@$export[$EXPORT_FLAGS] & $FLAG_NONAME)
856 $h_file = "Exported by ordinal only. Use GetProcAddress() to obtain a pointer to the function.";
858 else
860 if ($comment->{COMMENT_NAME} ne "")
862 my $tmp = "grep -s -l $comment->{COMMENT_NAME} @opt_header_file_list 2>/dev/null";
863 $tmp = `$tmp`;
864 my $exit_value = $? >> 8;
865 if ($exit_value == 0)
867 $tmp =~ s/\n.*//g;
868 if ($tmp ne "")
870 $h_file = "$tmp";
871 $h_file =~ s|^.*/\./||;
875 elsif ($comment->{ALT_NAME} ne "")
877 my $tmp = "grep -s -l $comment->{ALT_NAME} @opt_header_file_list"." 2>/dev/null";
878 $tmp = `$tmp`;
879 my $exit_value = $? >> 8;
880 if ($exit_value == 0)
882 $tmp =~ s/\n.*//g;
883 if ($tmp ne "")
885 $h_file = "$tmp";
886 $h_file =~ s|^.*/\./||;
890 $h_file =~ s/^ *//;
891 $h_file =~ s/\n//;
892 if ($h_file eq "")
894 $h_file = "Not declared in a Wine header. The function is either undocumented, or missing from Wine."
896 else
898 $h_file = "Declared in \"".$h_file."\".";
902 # Find source file
903 my $c_file = $comment->{FILE};
904 if ($opt_wine_root_dir ne "")
906 my $cfile = $pwd."/".$c_file; # Current dir + file
907 $cfile =~ s/(.+)(\/.*$)/$1/; # Strip the filename
908 $cfile = `cd $cfile && pwd`; # Strip any relative parts (e.g. "../../")
909 $cfile =~ s/\n//; # Strip newline
910 my $newfile = $c_file;
911 $newfile =~ s/(.+)(\/.*$)/$2/; # Strip all but the filename
912 $cfile = $cfile."/".$newfile; # Append filename to base path
913 $cfile =~ s/$opt_wine_root_dir//; # Get rid of the root directory
914 $cfile =~ s/\/\//\//g; # Remove any double slashes
915 $cfile =~ s/^\/+//; # Strip initial directory slash
916 $c_file = $cfile;
918 $c_file = "Implemented in \"".$c_file."\".";
920 # Add the implementation details
921 push (@{$comment->{TEXT}}, "IMPLEMENTATION","",$h_file,"",$c_file);
923 if (@$export[$EXPORT_FLAGS] & $FLAG_I386)
925 push (@{$comment->{TEXT}}, "", "Available on x86 platforms only.");
927 if (@$export[$EXPORT_FLAGS] & $FLAG_REGISTER)
929 push (@{$comment->{TEXT}}, "", "This function passes one or more arguments in registers. ",
930 "For more details, please read the source code.");
932 my $source_details = $source_files{$comment->{FILE}}[0];
933 if ($source_details->{DEBUG_CHANNEL} ne "")
935 push (@{$comment->{TEXT}}, "", "Debug channel \"".$source_details->{DEBUG_CHANNEL}."\".");
938 # Write out the documentation for the API
939 output_comment($comment)
942 # process our extra comment and output it if it is suitable.
943 sub process_extra_comment($)
945 my $comment = shift;
947 my $spec_details = $spec_files{$comment->{DLL_NAME}}[0];
949 if (!defined($spec_details))
951 if ($opt_verbose > 2)
953 print "Warning: Extra comment '".$comment->{COMMENT_NAME}."' belongs to '".
954 $comment->{DLL_NAME}."' (not passed with -w): not processing it.\n";
956 return;
959 # Check first to see if this is documentation for the DLL.
960 if ($comment->{COMMENT_NAME} eq $comment->{DLL_NAME})
962 if ($opt_verbose > 2)
964 print "Info: Found DLL documentation\n";
966 for (@{$comment->{TEXT}})
968 push (@{$spec_details->{DESCRIPTION}}, $_);
970 return;
973 # Add the comment to the DLL page as a link
974 push (@{$spec_details->{EXTRA_COMMENTS}},$comment->{COMMENT_NAME});
976 # If we have a prototype, process as a regular comment
977 if (@{$comment->{PROTOTYPE}})
979 $comment->{ORDINAL} = "@";
981 # Add an index for the comment name
982 $spec_details->{EXPORTED_NAMES}{$comment->{COMMENT_NAME}} = $spec_details->{NUM_EXPORTS};
984 # Add a fake exported entry
985 $spec_details->{NUM_EXPORTS}++;
986 my ($ordinal, $call_convention, $exported_name, $implementation_name, $documented) =
987 ("@", "fake", $comment->{COMMENT_NAME}, $comment->{COMMENT_NAME}, 0);
988 my @export = ($ordinal, $call_convention, $exported_name, $implementation_name, $documented);
989 push (@{$spec_details->{EXPORTS}},[@export]);
990 @{$comment->{TEXT}} = ("DESCRIPTION", @{$comment->{TEXT}});
991 process_comment($comment);
992 return;
995 if ($opt_verbose > 0)
997 print "Processing ",$comment->{COMMENT_NAME},"\n";
1000 if (@{$spec_details->{CURRENT_EXTRA}})
1002 my $current_comment = ${$spec_details->{CURRENT_EXTRA}}[0];
1004 if ($opt_verbose > 0)
1006 print "Processing old current: ",$current_comment->{COMMENT_NAME},"\n";
1008 # Output the current comment
1009 process_comment_text($current_comment);
1010 output_open_api_file($current_comment->{COMMENT_NAME});
1011 output_api_header($current_comment);
1012 output_api_name($current_comment);
1013 output_api_comment($current_comment);
1014 output_api_footer($current_comment);
1015 output_close_api_file();
1018 if ($opt_verbose > 2)
1020 print "Setting current to ",$comment->{COMMENT_NAME},"\n";
1023 my $comment_copy =
1025 FILE => $comment->{FILE},
1026 COMMENT_NAME => $comment->{COMMENT_NAME},
1027 ALT_NAME => $comment->{ALT_NAME},
1028 DLL_NAME => $comment->{DLL_NAME},
1029 ORDINAL => $comment->{ORDINAL},
1030 RETURNS => $comment->{RETURNS},
1031 PROTOTYPE => [],
1032 TEXT => [],
1035 for (@{$comment->{TEXT}})
1037 push (@{$comment_copy->{TEXT}}, $_);
1039 # Set this comment to be the current extra comment
1040 @{$spec_details->{CURRENT_EXTRA}} = ($comment_copy);
1043 # Write a standardised comment out in the appropriate format
1044 sub output_comment($)
1046 my $comment = shift;
1048 if ($opt_verbose > 0)
1050 print "Processing ",$comment->{COMMENT_NAME},"\n";
1053 if ($opt_verbose > 4)
1055 print "--PROTO--\n";
1056 for (@{$comment->{PROTOTYPE}})
1058 print "'".$_."'\n";
1061 print "--COMMENT--\n";
1062 for (@{$comment->{TEXT} })
1064 print $_."\n";
1068 output_open_api_file($comment->{COMMENT_NAME});
1069 output_api_header($comment);
1070 output_api_name($comment);
1071 output_api_synopsis($comment);
1072 output_api_comment($comment);
1073 output_api_footer($comment);
1074 output_close_api_file();
1077 # Write out an index file for each .spec processed
1078 sub process_index_files()
1080 foreach my $spec_file (keys %spec_files)
1082 my $spec_details = $spec_files{$spec_file}[0];
1083 if (defined ($spec_details->{DLL_NAME}))
1085 if (@{$spec_details->{CURRENT_EXTRA}})
1087 # We have an unwritten extra comment, write it
1088 my $current_comment = ${$spec_details->{CURRENT_EXTRA}}[0];
1089 process_extra_comment($current_comment);
1090 @{$spec_details->{CURRENT_EXTRA}} = ();
1092 output_spec($spec_details);
1097 # Write a spec files documentation out in the appropriate format
1098 sub output_spec($)
1100 my $spec_details = shift;
1102 if ($opt_verbose > 2)
1104 print "Writing:",$spec_details->{DLL_NAME},"\n";
1107 # Use the comment output functions for consistency
1108 my $comment =
1110 FILE => $spec_details->{DLL_NAME},
1111 COMMENT_NAME => $spec_details->{DLL_NAME}.".".$spec_details->{DLL_EXT},
1112 ALT_NAME => $spec_details->{DLL_NAME},
1113 DLL_NAME => "",
1114 ORDINAL => "",
1115 RETURNS => "",
1116 PROTOTYPE => [],
1117 TEXT => [],
1119 my $total_implemented = $spec_details->{NUM_FORWARDS} + $spec_details->{NUM_VARS} +
1120 $spec_details->{NUM_FUNCS};
1121 my $percent_implemented = 0;
1122 if ($total_implemented)
1124 $percent_implemented = $total_implemented /
1125 ($total_implemented + $spec_details->{NUM_STUBS}) * 100;
1127 $percent_implemented = int($percent_implemented);
1128 my $percent_documented = 0;
1129 if ($spec_details->{NUM_DOCS})
1131 # Treat forwards and data as documented funcs for statistics
1132 $percent_documented = $spec_details->{NUM_DOCS} / $spec_details->{NUM_FUNCS} * 100;
1133 $percent_documented = int($percent_documented);
1136 # Make a list of the contributors to this DLL.
1137 my @contributors;
1139 foreach my $source_file (keys %source_files)
1141 my $source_details = $source_files{$source_file}[0];
1142 for (@{$source_details->{CONTRIBUTORS}})
1144 push (@contributors, $_);
1148 my %saw;
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];
1160 undef %saw;
1161 @contributors = grep(!$saw{$_}++, @contributors);
1163 if ($opt_verbose > 3)
1165 print "Contributors:\n";
1166 for (@contributors)
1168 print "'".$_."'\n";
1171 my $contribstring = join (", ", @contributors);
1173 # Create the initial comment text
1174 @{$comment->{TEXT}} = (
1175 "NAME",
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}},
1191 "STATISTICS",
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."%)",
1199 "CONTRIBUTORS",
1200 "The following people hold copyrights on the source files comprising this dll:",
1202 $contribstring,
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};
1213 for (@$exports)
1215 my $line = "";
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)";
1245 else
1247 # A function
1248 if (@$_[$EXPORT_FLAGS] & $FLAG_DOCUMENTED)
1250 # Documented
1251 $line = @$_[$EXPORT_EXPNAME]." (implemented as ".@$_[$EXPORT_IMPNAME]."())";
1252 if (@$_[$EXPORT_EXPNAME] ne @$_[$EXPORT_IMPNAME])
1254 $line = @$_[$EXPORT_EXPNAME]." (implemented as ".@$_[$EXPORT_IMPNAME]."())";
1256 else
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]);
1266 else
1268 $line = @$_[$EXPORT_EXPNAME]." (not documented)";
1271 if ($line ne "")
1273 push (@{$comment->{TEXT}}, $line, "");
1277 # Add links to the extra documentation
1278 if (@{$spec_details->{EXTRA_COMMENTS}})
1280 push (@{$comment->{TEXT}}, "SEE ALSO");
1281 my %htmp;
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";
1305 close(DLLDB);
1307 if ($opt_output_format eq "s")
1309 output_sgml_dll_file($spec_details);
1310 return;
1313 if ($opt_output_format eq "x")
1315 output_xml_dll_file($spec_details);
1316 return;
1322 # OUTPUT FUNCTIONS
1323 # ----------------
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.
1328 # Open the api file
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";
1346 else
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()
1356 close (OUTPUT);
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",
1377 "<sect1>\n",
1378 "<title>$comment->{COMMENT_NAME}</title>\n";
1380 else
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 &copy ".$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";
1400 return;
1402 else
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";
1420 else
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>&nbsp;&nbsp;<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";
1456 else
1458 print OUTPUT "\\fB",$readable_name,"\\fR ",$dll_ordinal;
1461 output_api_section_end();
1464 sub output_api_synopsis($)
1466 my $comment = shift;
1467 my @fmt;
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>");
1481 else
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]/;
1523 print OUTPUT $line;
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";
1534 else
1536 print OUTPUT " )\n";
1539 output_api_section_end();
1542 sub output_api_comment($)
1544 my $comment = shift;
1545 my $open_paragraph = 0;
1546 my $open_raw = 0;
1547 my $param_docs = 0;
1548 my @fmt;
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>");
1567 else
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
1588 s/\&/\&amp;/g;
1589 s/\</\&lt;/g;
1590 s/\>/\&gt;/g;
1591 s/\([Cc]\)/\&copy;/g;
1592 s/\(tm\)/&#174;/;
1595 if ( s/^\|// )
1597 # Raw output
1598 if ($open_raw == 0)
1600 if ($open_paragraph == 1)
1602 # Close the open paragraph
1603 print OUTPUT $fmt[1];
1604 $open_paragraph = 0;
1606 # Start raw output
1607 print OUTPUT $fmt[12];
1608 $open_raw = 1;
1610 if ($opt_output_format eq "")
1612 print OUTPUT ".br\n"; # Prevent 'man' running these lines together
1614 print OUTPUT $_,"\n";
1616 else
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;
1624 # Highlight strings
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]/;
1639 else
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-]+)(\(\))/)
1650 my $link = $1;
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>/;
1656 # Index references
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;
1664 else
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;
1671 else
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;
1681 if ($open_raw == 1)
1683 # Finish the raw output
1684 print OUTPUT $fmt[13];
1685 $open_raw = 0;
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];
1696 $param_docs = 0;
1698 else
1700 print OUTPUT $fmt[1];
1702 $open_paragraph = 0;
1704 output_api_section_start($comment,$_);
1705 if ( /^PARAMS$/ || /^MEMBERS$/ )
1707 print OUTPUT $fmt[14];
1708 $param_docs = 1;
1710 else
1712 #print OUTPUT $fmt[15];
1713 #$param_docs = 0;
1716 elsif ( /^$/ )
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;
1728 else
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
1740 else
1742 # Within paragraph lines, prevent lines running together
1743 $_ = $_." ";
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/(\-\&gt\;[A-Za-z_]+)/$fmt[8]$1$fmt[9]/g;
1755 if ($open_paragraph == 0)
1757 if ($param_docs == 1)
1759 print OUTPUT $fmt[16];
1761 else
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;
1770 print OUTPUT $_;
1774 if ($open_raw == 1)
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)
1808 $_ =~ s/A\,/\,/;
1809 print INDEXDB $_."\n";
1811 close(INDEXDB);
1814 # Use the comment output functions for consistency
1815 my $comment =
1817 FILE => "",
1818 COMMENT_NAME => "The Wine Api Guide",
1819 ALT_NAME => "The Wine Api Guide",
1820 DLL_NAME => "",
1821 ORDINAL => "",
1822 RETURNS => "",
1823 PROTOTYPE => [],
1824 TEXT => [],
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}} = (
1835 "NAME",
1836 $comment->{COMMENT_NAME},
1837 "INTRODUCTION",
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\".",
1849 "CONTRIBUTORS",
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}},
1866 "INDEX",
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() ]", "",
1872 "DLLS",
1873 "Each dll provided by Wine is documented individually. The following dlls are provided :",
1876 # Add the dlls to the comment
1877 for (@dlls)
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);
1904 return;
1906 if ($opt_output_format eq "x")
1908 output_xml_master_file(\@dlls);
1909 return;
1911 if ($opt_output_format eq "h")
1913 output_html_index_files();
1914 output_html_stylesheet();
1915 return;
1919 # Write the master wine-api.xml, linking it to each dll.
1920 sub output_xml_master_file($)
1922 my $dlls = shift;
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";
1931 # List the entities
1932 for (@$dlls)
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";
1941 for (@$dlls)
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($)
1953 my $dlls = shift;
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";
1960 # List the entities
1961 for (@$dlls)
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";
1970 for (@$dlls)
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 = ();
1987 for (@$exports)
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`;
2012 for (@source_files)
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";
2021 close OUTPUT;
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 = ();
2033 for (@$exports)
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`;
2058 for (@source_files)
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";
2067 close OUTPUT;
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")
2076 return;
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`;
2085 for (@letters)
2087 my $letter = $_;
2088 my $comment =
2090 FILE => "",
2091 COMMENT_NAME => "",
2092 ALT_NAME => "",
2093 DLL_NAME => "",
2094 ORDINAL => "",
2095 RETURNS => "",
2096 PROTOTYPE => [],
2097 TEXT => [],
2100 $comment->{COMMENT_NAME} = $letter." Functions";
2101 $comment->{ALT_NAME} = $letter." Functions";
2103 push (@{$comment->{TEXT}},
2104 "NAME",
2105 $comment->{COMMENT_NAME},
2106 "FUNCTIONS"
2109 # Add the functions to the comment
2110 for (@funcs)
2112 my $first_char = substr ($_, 0, 1);
2113 $first_char = uc $first_char;
2115 if ($first_char eq $letter)
2117 my $name = $_;
2118 my $file;
2119 $name =~ s/(^.*?)\,(.*?)\n/$1/;
2120 $file = $2;
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")
2139 return;
2142 my $css;
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;
2151 color: black;
2152 font-family: Tahoma,sans-serif;
2153 font-style: normal;
2154 font-size: 10pt;
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;
2161 color: #777777;
2162 background-color: #F0F0FE;
2163 margin-left: 0.2in;
2164 margin-right: 1.0in;
2166 b.func_name { /* Function Name */
2167 font-size: 10pt;
2168 font-style: bold;
2170 i.dll_ord { /* Italicised DLL+ordinal */
2171 color: #888888;
2172 font-family: sans-serif;
2173 font-size: 8pt;
2175 p { /* Paragraphs */
2176 margin-left: 0.5in;
2177 margin-right: 0.5in;
2179 table { /* tables */
2180 margin-left: 0.5in;
2181 margin-right: 0.5in;
2183 pre.proto /* API Function prototype */
2185 border-style: solid;
2186 border-width: 1px;
2187 border-color: #777777;
2188 background-color: #F0F0BB;
2189 color: black;
2190 font-size: 10pt;
2191 vertical-align: top;
2192 margin-left: 0.5in;
2193 margin-right: 1.0in;
2195 pre.raw { /* Raw text output */
2196 margin-left: 0.6in;
2197 margin-right: 1.1in;
2198 background-color: #8080DC;
2200 tt.param { /* Parameter name */
2201 font-style: italic;
2202 color: blue;
2204 tt.const { /* Constant */
2205 color: red;
2207 i.in_out { /* In/Out */
2208 font-size: 8pt;
2209 color: grey;
2211 tt.coderef { /* Code in description text */
2212 color: darkgreen;
2214 b.emp /* Emphasis */ {
2215 font-style: bold;
2216 color: darkblue;
2218 i.footer { /* Footer */
2219 font-family: sans-serif;
2220 font-size: 6pt;
2221 color: darkgrey;
2223 HERE_TARGET
2225 my $output_file = "$opt_output_directory/apidoc.css";
2226 open(CSS,">$output_file") || die "Couldn't create the file $output_file\n";
2227 print CSS $css;
2228 close(CSS);
2232 sub usage()
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",
2240 "Options:\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";
2254 # Main
2257 # Print usage if we're called with no args
2258 if( @ARGV == 0)
2260 usage();
2263 # Process command line options
2264 while(defined($_ = shift @ARGV))
2266 if( s/^-// )
2268 # An option.
2269 for ($_)
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`) {
2282 $include =~ s/\n//;
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); }
2289 last;
2291 s/^C// && do {
2292 if ($_ ne "") { $opt_source_dir = $_; }
2293 last;
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."/"; };
2300 last;
2302 die "Unrecognised option $_\n";
2305 else
2307 # A source file.
2308 push (@opt_source_file_list, $_);
2312 # Remove duplicate include directories
2313 my %htmp;
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};
2352 for (@$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();
2372 exit 0;