c2man: Fix a typo.
[wine/multimedia.git] / tools / c2man.pl
blobbc256838d5d8e729622e0934300fbe2dee011543
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 push (@{$comment->{PROTOTYPE}},$_);
423 # Strip comments from the line before checking for ')'
424 my $stripped_line = $_;
425 $stripped_line =~ s/ *(\/\* *)(.*?)( *\*\/ *)//;
426 if ( $stripped_line =~ /\)/ )
428 # Strip a blank last line
429 my $last_line = pop(@{$comment->{TEXT}});
430 if (defined($last_line) && $last_line ne "")
432 # Put it back
433 push (@{$comment->{TEXT}},$last_line);
436 if ($opt_output_empty != 0 && @{$comment->{TEXT}} == 0)
438 # Create a 'not implemented' comment
439 @{$comment->{TEXT}} = ("fixme: This function has not yet been documented.");
441 $parse_state = 5;
445 if ($parse_state == 5) # Processing the comment
447 # Process it, if it has any text
448 if (@{$comment->{TEXT}} > 0)
450 if ($extra_comment == 1)
452 process_extra_comment($comment);
454 else
456 @{$comment->{TEXT}} = ("DESCRIPTION", @{$comment->{TEXT}});
457 process_comment($comment);
460 elsif ($opt_verbose > 1 && $opt_output_empty == 0)
462 print "Info: Function '",$comment->{COMMENT_NAME},"' has no documentation.\n";
464 $parse_state = 0;
467 close(SOURCE_FILE);
470 # Standardise a comments text for consistency
471 sub process_comment_text($)
473 my $comment = shift;
474 my $in_params = 0;
475 my @tmp_list = ();
476 my $i = 0;
478 for (@{$comment->{TEXT}})
480 my $line = $_;
482 if ( /^\s*$/ || /^[A-Z]+$/ || /^-/ )
484 $in_params = 0;
486 if ( $in_params > 0 && !/\[/ && !/\]/ )
488 # Possibly a continuation of the parameter description
489 my $last_line = pop(@tmp_list);
490 if ( $last_line =~ /\[/ && $last_line =~ /\]/ )
492 $line = $last_line." ".$_;
494 else
496 $in_params = 0;
497 push (@tmp_list, $last_line);
500 if ( /^(PARAMS|MEMBERS)$/ )
502 $in_params = 1;
504 push (@tmp_list, $line);
507 @{$comment->{TEXT}} = @tmp_list;
509 for (@{$comment->{TEXT}})
511 if (! /^\|/ )
513 # Map I/O values. These come in too many formats to standardise now....
514 s/\[I\]|\[i\]|\[in\]|\[IN\]/\[In\] /g;
515 s/\[O\]|\[o\]|\[out\]|\[OUT\]/\[Out\]/g;
516 s/\[I\/O\]|\[I\,O\]|\[i\/o\]|\[in\/out\]|\[IN\/OUT\]/\[In\/Out\]/g;
517 # TRUE/FALSE/NULL are defines, capitilise them
518 s/True|true/TRUE/g;
519 s/False|false/FALSE/g;
520 s/Null|null/NULL/g;
521 # Preferred capitalisations
522 s/ wine| WINE/ Wine/g;
523 s/ API | api / Api /g;
524 s/ DLL | Dll / dll /g;
525 s/ URL | url / Url /g;
526 s/WIN16|win16/Win16/g;
527 s/WIN32|win32/Win32/g;
528 s/WIN64|win64/Win64/g;
529 s/ ID | id / Id /g;
530 # Grammar
531 s/([a-z])\.([A-Z])/$1\. $2/g; # Space after full stop
532 s/ \:/\:/g; # Colons to the left
533 s/ \;/\;/g; # Semi-colons too
534 # Common idioms
535 s/^See ([A-Za-z0-9_]+)\.$/See $1\(\)\./; # Referring to A version from W
536 s/^Unicode version of ([A-Za-z0-9_]+)\.$/See $1\(\)\./; # Ditto
537 s/^64\-bit version of ([A-Za-z0-9_]+)\.$/See $1\(\)\./; # Referring to 32 bit version from 64
538 s/^PARAMETERS$/PARAMS/; # Name of parameter section should be 'PARAMS'
539 # Trademarks
540 s/( |\.)(M\$|MS|Microsoft|microsoft|micro\$oft|Micro\$oft)( |\.)/$1Microsoft\(tm\)$3/g;
541 s/( |\.)(Windows|windows|windoze|winblows)( |\.)/$1Windows\(tm\)$3/g;
542 s/( |\.)(DOS|dos|msdos)( |\.)/$1MS-DOS\(tm\)$3/g;
543 s/( |\.)(UNIX|unix)( |\.)/$1Unix\(tm\)$3/g;
544 s/( |\.)(LINIX|linux)( |\.)/$1Linux\(tm\)$3/g;
545 # Abbreviations
546 s/( char )/ character /g;
547 s/( chars )/ characters /g;
548 s/( info )/ information /g;
549 s/( app )/ application /g;
550 s/( apps )/ applications /g;
551 s/( exe )/ executable /g;
552 s/( ptr )/ pointer /g;
553 s/( obj )/ object /g;
554 s/( err )/ error /g;
555 s/( bool )/ boolean /g;
556 s/( no\. )/ number /g;
557 s/( No\. )/ Number /g;
558 # Punctuation
559 if ( /\[I|\[O/ && ! /\.$/ )
561 $_ = $_."."; # Always have a full stop at the end of parameter desc.
563 elsif ($i > 0 && /^[A-Z]*$/ &&
564 !(@{$comment->{TEXT}}[$i-1] =~ /\.$/) &&
565 !(@{$comment->{TEXT}}[$i-1] =~ /\:$/))
568 if (!(@{$comment->{TEXT}}[$i-1] =~ /^[A-Z]*$/))
570 # Paragraphs always end with a full stop
571 @{$comment->{TEXT}}[$i-1] = @{$comment->{TEXT}}[$i-1].".";
575 $i++;
579 # Standardise our comment and output it if it is suitable.
580 sub process_comment($)
582 my $comment = shift;
584 # Don't process this comment if the function isn't exported
585 my $spec_details = $spec_files{$comment->{DLL_NAME}}[0];
587 if (!defined($spec_details))
589 if ($opt_verbose > 2)
591 print "Warning: Function '".$comment->{COMMENT_NAME}."' belongs to '".
592 $comment->{DLL_NAME}."' (not passed with -w): not processing it.\n";
594 return;
597 if ($comment->{COMMENT_NAME} eq "@")
599 my $found = 0;
601 # Find the name from the .spec file
602 for (@{$spec_details->{EXPORTS}})
604 if (@$_[$EXPORT_ORDINAL] eq $comment->{ORDINAL})
606 $comment->{COMMENT_NAME} = @$_[$EXPORT_EXPNAME];
607 $found = 1;
611 if ($found == 0)
613 # Create an implementation name
614 $comment->{COMMENT_NAME} = $comment->{DLL_NAME}."_".$comment->{ORDINAL};
618 my $exported_names = $spec_details->{EXPORTED_NAMES};
619 my $export_index = $exported_names->{$comment->{COMMENT_NAME}};
620 my $implementation_names = $spec_details->{IMPLEMENTATION_NAMES};
622 if (!defined($export_index))
624 # Perhaps the comment uses the implementation name?
625 $export_index = $implementation_names->{$comment->{COMMENT_NAME}};
627 if (!defined($export_index))
629 # This function doesn't appear to be exported. hmm.
630 if ($opt_verbose > 2)
632 print "Warning: Function '".$comment->{COMMENT_NAME}."' claims to belong to '".
633 $comment->{DLL_NAME}."' but is not exported by it: not processing it.\n";
635 return;
638 # When the function is exported twice we have the second name below the first
639 # (you see this a lot in ntdll, but also in some other places).
640 my $first_line = ${$comment->{TEXT}}[1];
642 if ( $first_line =~ /^(@|[A-Za-z0-9_]+) +(\(|\[)([A-Za-z0-9_]+)\.(([0-9]+)|@)(\)|\])$/ )
644 # Found a second name - mark it as documented
645 my $alt_index = $exported_names->{$1};
646 if (defined($alt_index))
648 if ($opt_verbose > 2)
650 print "Info: Found alternate name '",$1,"\n";
652 my $alt_export = @{$spec_details->{EXPORTS}}[$alt_index];
653 @$alt_export[$EXPORT_FLAGS] |= $FLAG_DOCUMENTED;
654 $spec_details->{NUM_DOCS}++;
655 ${$comment->{TEXT}}[1] = "";
659 if (@{$spec_details->{CURRENT_EXTRA}})
661 # We have an extra comment that might be related to this one
662 my $current_comment = ${$spec_details->{CURRENT_EXTRA}}[0];
663 my $current_name = $current_comment->{COMMENT_NAME};
664 if ($comment->{COMMENT_NAME} =~ /^$current_name/ && $comment->{COMMENT_NAME} ne $current_name)
666 if ($opt_verbose > 2)
668 print "Linking ",$comment->{COMMENT_NAME}," to $current_name\n";
670 # Add a reference to this comment to our extra comment
671 push (@{$current_comment->{TEXT}}, $comment->{COMMENT_NAME}."()","");
675 # We want our docs generated using the implementation name, so they are unique
676 my $export = @{$spec_details->{EXPORTS}}[$export_index];
677 $comment->{COMMENT_NAME} = @$export[$EXPORT_IMPNAME];
678 $comment->{ALT_NAME} = @$export[$EXPORT_EXPNAME];
680 # Mark the function as documented
681 $spec_details->{NUM_DOCS}++;
682 @$export[$EXPORT_FLAGS] |= $FLAG_DOCUMENTED;
684 # This file is used by the DLL - Make sure we get our contributors right
685 push (@{$spec_details->{SOURCES}},$comment->{FILE});
687 # If we have parameter comments in the prototype, extract them
688 my @parameter_comments;
689 for (@{$comment->{PROTOTYPE}})
691 s/ *\, */\,/g; # Strip spaces from around commas
693 if ( s/ *(\/\* *)(.*?)( *\*\/ *)// ) # Strip out comment
695 my $parameter_comment = $2;
696 if (!$parameter_comment =~ /^\[/ )
698 # Add [IO] markers so we format the comment correctly
699 $parameter_comment = "[fixme] ".$parameter_comment;
701 if ( /( |\*)([A-Za-z_]{1}[A-Za-z_0-9]*)(\,|\))/ )
703 # Add the parameter name
704 $parameter_comment = $2." ".$parameter_comment;
706 push (@parameter_comments, $parameter_comment);
710 # If we extracted any prototype comments, add them to the comment text.
711 if (@parameter_comments)
713 @parameter_comments = ("PARAMS", @parameter_comments);
714 my @new_comment = ();
715 my $inserted_params = 0;
717 for (@{$comment->{TEXT}})
719 if ( $inserted_params == 0 && /^[A-Z]+$/ )
721 # Found a section header, so this is where we insert
722 push (@new_comment, @parameter_comments);
723 $inserted_params = 1;
725 push (@new_comment, $_);
727 if ($inserted_params == 0)
729 # Add them to the end
730 push (@new_comment, @parameter_comments);
732 $comment->{TEXT} = [@new_comment];
735 if ($opt_fussy == 1 && $opt_output_empty == 0)
737 # Reject any comment that doesn't have a description or a RETURNS section.
738 # This is the default for now, 'coz many comments aren't suitable.
739 my $found_returns = 0;
740 my $found_description_text = 0;
741 my $in_description = 0;
742 for (@{$comment->{TEXT}})
744 if ( /^RETURNS$/ )
746 $found_returns = 1;
747 $in_description = 0;
749 elsif ( /^DESCRIPTION$/ )
751 $in_description = 1;
753 elsif ($in_description == 1)
755 if ( !/^[A-Z]+$/ )
757 # Don't reject comments that refer to another doc (e.g. A/W)
758 if ( /^See ([A-Za-z0-9_]+)\.$/ )
760 if ($comment->{COMMENT_NAME} =~ /W$/ )
762 # This is probably a Unicode version of an Ascii function.
763 # Create the Ascii name and see if its been documented
764 my $ascii_name = $comment->{COMMENT_NAME};
765 $ascii_name =~ s/W$/A/;
767 my $ascii_export_index = $exported_names->{$ascii_name};
769 if (!defined($ascii_export_index))
771 $ascii_export_index = $implementation_names->{$ascii_name};
773 if (!defined($ascii_export_index))
775 if ($opt_verbose > 2)
777 print "Warning: Function '".$comment->{COMMENT_NAME}."' is not an A/W pair.\n";
780 else
782 my $ascii_export = @{$spec_details->{EXPORTS}}[$ascii_export_index];
783 if (@$ascii_export[$EXPORT_FLAGS] & $FLAG_DOCUMENTED)
785 # Flag these functions as an A/W pair
786 @$ascii_export[$EXPORT_FLAGS] |= $FLAG_APAIR;
787 @$export[$EXPORT_FLAGS] |= $FLAG_WPAIR;
791 $found_returns = 1;
793 elsif ( /^Unicode version of ([A-Za-z0-9_]+)\.$/ )
795 @$export[$EXPORT_FLAGS] |= $FLAG_WPAIR; # Explicitly marked as W version
796 $found_returns = 1;
798 elsif ( /^64\-bit version of ([A-Za-z0-9_]+)\.$/ )
800 @$export[$EXPORT_FLAGS] |= $FLAG_64PAIR; # Explicitly marked as 64 bit version
801 $found_returns = 1;
803 $found_description_text = 1;
805 else
807 $in_description = 0;
811 if ($found_returns == 0 || $found_description_text == 0)
813 if ($opt_verbose > 2)
815 print "Info: Function '",$comment->{COMMENT_NAME},"' has no ",
816 "description and/or RETURNS section, skipping\n";
818 $spec_details->{NUM_DOCS}--;
819 @$export[$EXPORT_FLAGS] &= ~$FLAG_DOCUMENTED;
820 return;
824 process_comment_text($comment);
826 # Strip the prototypes return value, call convention, name and brackets
827 # (This leaves it as a list of types and names, or empty for void functions)
828 my $prototype = join(" ", @{$comment->{PROTOTYPE}});
829 $prototype =~ s/ / /g;
831 if ( $prototype =~ /(WINAPIV|WINAPI|__cdecl|PASCAL|CALLBACK|FARPROC16)/ )
833 $prototype =~ s/^(.*?)\s+(WINAPIV|WINAPI|__cdecl|PASCAL|CALLBACK|FARPROC16)\s+(.*?)\(\s*(.*)/$4/;
834 $comment->{RETURNS} = $1;
836 else
838 $prototype =~ s/^(.*?)([A-Za-z0-9_]+)\s*\(\s*(.*)/$3/;
839 $comment->{RETURNS} = $1;
842 $prototype =~ s/ *\).*//; # Strip end bracket
843 $prototype =~ s/ *\* */\*/g; # Strip space around pointers
844 $prototype =~ s/ *\, */\,/g; # Strip space around commas
845 $prototype =~ s/^(void|VOID)$//; # If void, leave blank
846 $prototype =~ s/\*([A-Za-z_])/\* $1/g; # Separate pointers from parameter name
847 @{$comment->{PROTOTYPE}} = split ( /,/ ,$prototype);
849 # FIXME: If we have no parameters, make sure we have a PARAMS: None. section
851 # Find header file
852 my $h_file = "";
853 if (@$export[$EXPORT_FLAGS] & $FLAG_NONAME)
855 $h_file = "Exported by ordinal only. Use GetProcAddress() to obtain a pointer to the function.";
857 else
859 if ($comment->{COMMENT_NAME} ne "")
861 my $tmp = "grep -s -l $comment->{COMMENT_NAME} @opt_header_file_list 2>/dev/null";
862 $tmp = `$tmp`;
863 my $exit_value = $? >> 8;
864 if ($exit_value == 0)
866 $tmp =~ s/\n.*//g;
867 if ($tmp ne "")
869 $h_file = "$tmp";
870 $h_file =~ s|^.*/\./||;
874 elsif ($comment->{ALT_NAME} ne "")
876 my $tmp = "grep -s -l $comment->{ALT_NAME} @opt_header_file_list"." 2>/dev/null";
877 $tmp = `$tmp`;
878 my $exit_value = $? >> 8;
879 if ($exit_value == 0)
881 $tmp =~ s/\n.*//g;
882 if ($tmp ne "")
884 $h_file = "$tmp";
885 $h_file =~ s|^.*/\./||;
889 $h_file =~ s/^ *//;
890 $h_file =~ s/\n//;
891 if ($h_file eq "")
893 $h_file = "Not declared in a Wine header. The function is either undocumented, or missing from Wine."
895 else
897 $h_file = "Declared in \"".$h_file."\".";
901 # Find source file
902 my $c_file = $comment->{FILE};
903 if ($opt_wine_root_dir ne "")
905 my $cfile = $pwd."/".$c_file; # Current dir + file
906 $cfile =~ s/(.+)(\/.*$)/$1/; # Strip the filename
907 $cfile = `cd $cfile && pwd`; # Strip any relative parts (e.g. "../../")
908 $cfile =~ s/\n//; # Strip newline
909 my $newfile = $c_file;
910 $newfile =~ s/(.+)(\/.*$)/$2/; # Strip all but the filename
911 $cfile = $cfile."/".$newfile; # Append filename to base path
912 $cfile =~ s/$opt_wine_root_dir//; # Get rid of the root directory
913 $cfile =~ s/\/\//\//g; # Remove any double slashes
914 $cfile =~ s/^\/+//; # Strip initial directory slash
915 $c_file = $cfile;
917 $c_file = "Implemented in \"".$c_file."\".";
919 # Add the implementation details
920 push (@{$comment->{TEXT}}, "IMPLEMENTATION","",$h_file,"",$c_file);
922 if (@$export[$EXPORT_FLAGS] & $FLAG_I386)
924 push (@{$comment->{TEXT}}, "", "Available on x86 platforms only.");
926 if (@$export[$EXPORT_FLAGS] & $FLAG_REGISTER)
928 push (@{$comment->{TEXT}}, "", "This function passes one or more arguments in registers. ",
929 "For more details, please read the source code.");
931 my $source_details = $source_files{$comment->{FILE}}[0];
932 if ($source_details->{DEBUG_CHANNEL} ne "")
934 push (@{$comment->{TEXT}}, "", "Debug channel \"".$source_details->{DEBUG_CHANNEL}."\".");
937 # Write out the documentation for the API
938 output_comment($comment)
941 # process our extra comment and output it if it is suitable.
942 sub process_extra_comment($)
944 my $comment = shift;
946 my $spec_details = $spec_files{$comment->{DLL_NAME}}[0];
948 if (!defined($spec_details))
950 if ($opt_verbose > 2)
952 print "Warning: Extra comment '".$comment->{COMMENT_NAME}."' belongs to '".
953 $comment->{DLL_NAME}."' (not passed with -w): not processing it.\n";
955 return;
958 # Check first to see if this is documentation for the DLL.
959 if ($comment->{COMMENT_NAME} eq $comment->{DLL_NAME})
961 if ($opt_verbose > 2)
963 print "Info: Found DLL documentation\n";
965 for (@{$comment->{TEXT}})
967 push (@{$spec_details->{DESCRIPTION}}, $_);
969 return;
972 # Add the comment to the DLL page as a link
973 push (@{$spec_details->{EXTRA_COMMENTS}},$comment->{COMMENT_NAME});
975 # If we have a prototype, process as a regular comment
976 if (@{$comment->{PROTOTYPE}})
978 $comment->{ORDINAL} = "@";
980 # Add an index for the comment name
981 $spec_details->{EXPORTED_NAMES}{$comment->{COMMENT_NAME}} = $spec_details->{NUM_EXPORTS};
983 # Add a fake exported entry
984 $spec_details->{NUM_EXPORTS}++;
985 my ($ordinal, $call_convention, $exported_name, $implementation_name, $documented) =
986 ("@", "fake", $comment->{COMMENT_NAME}, $comment->{COMMENT_NAME}, 0);
987 my @export = ($ordinal, $call_convention, $exported_name, $implementation_name, $documented);
988 push (@{$spec_details->{EXPORTS}},[@export]);
989 @{$comment->{TEXT}} = ("DESCRIPTION", @{$comment->{TEXT}});
990 process_comment($comment);
991 return;
994 if ($opt_verbose > 0)
996 print "Processing ",$comment->{COMMENT_NAME},"\n";
999 if (@{$spec_details->{CURRENT_EXTRA}})
1001 my $current_comment = ${$spec_details->{CURRENT_EXTRA}}[0];
1003 if ($opt_verbose > 0)
1005 print "Processing old current: ",$current_comment->{COMMENT_NAME},"\n";
1007 # Output the current comment
1008 process_comment_text($current_comment);
1009 output_open_api_file($current_comment->{COMMENT_NAME});
1010 output_api_header($current_comment);
1011 output_api_name($current_comment);
1012 output_api_comment($current_comment);
1013 output_api_footer($current_comment);
1014 output_close_api_file();
1017 if ($opt_verbose > 2)
1019 print "Setting current to ",$comment->{COMMENT_NAME},"\n";
1022 my $comment_copy =
1024 FILE => $comment->{FILE},
1025 COMMENT_NAME => $comment->{COMMENT_NAME},
1026 ALT_NAME => $comment->{ALT_NAME},
1027 DLL_NAME => $comment->{DLL_NAME},
1028 ORDINAL => $comment->{ORDINAL},
1029 RETURNS => $comment->{RETURNS},
1030 PROTOTYPE => [],
1031 TEXT => [],
1034 for (@{$comment->{TEXT}})
1036 push (@{$comment_copy->{TEXT}}, $_);
1038 # Set this comment to be the current extra comment
1039 @{$spec_details->{CURRENT_EXTRA}} = ($comment_copy);
1042 # Write a standardised comment out in the appropriate format
1043 sub output_comment($)
1045 my $comment = shift;
1047 if ($opt_verbose > 0)
1049 print "Processing ",$comment->{COMMENT_NAME},"\n";
1052 if ($opt_verbose > 4)
1054 print "--PROTO--\n";
1055 for (@{$comment->{PROTOTYPE}})
1057 print "'".$_."'\n";
1060 print "--COMMENT--\n";
1061 for (@{$comment->{TEXT} })
1063 print $_."\n";
1067 output_open_api_file($comment->{COMMENT_NAME});
1068 output_api_header($comment);
1069 output_api_name($comment);
1070 output_api_synopsis($comment);
1071 output_api_comment($comment);
1072 output_api_footer($comment);
1073 output_close_api_file();
1076 # Write out an index file for each .spec processed
1077 sub process_index_files()
1079 foreach my $spec_file (keys %spec_files)
1081 my $spec_details = $spec_files{$spec_file}[0];
1082 if (defined ($spec_details->{DLL_NAME}))
1084 if (@{$spec_details->{CURRENT_EXTRA}})
1086 # We have an unwritten extra comment, write it
1087 my $current_comment = ${$spec_details->{CURRENT_EXTRA}}[0];
1088 process_extra_comment($current_comment);
1089 @{$spec_details->{CURRENT_EXTRA}} = ();
1091 output_spec($spec_details);
1096 # Write a spec files documentation out in the appropriate format
1097 sub output_spec($)
1099 my $spec_details = shift;
1101 if ($opt_verbose > 2)
1103 print "Writing:",$spec_details->{DLL_NAME},"\n";
1106 # Use the comment output functions for consistency
1107 my $comment =
1109 FILE => $spec_details->{DLL_NAME},
1110 COMMENT_NAME => $spec_details->{DLL_NAME}.".".$spec_details->{DLL_EXT},
1111 ALT_NAME => $spec_details->{DLL_NAME},
1112 DLL_NAME => "",
1113 ORDINAL => "",
1114 RETURNS => "",
1115 PROTOTYPE => [],
1116 TEXT => [],
1118 my $total_implemented = $spec_details->{NUM_FORWARDS} + $spec_details->{NUM_VARS} +
1119 $spec_details->{NUM_FUNCS};
1120 my $percent_implemented = 0;
1121 if ($total_implemented)
1123 $percent_implemented = $total_implemented /
1124 ($total_implemented + $spec_details->{NUM_STUBS}) * 100;
1126 $percent_implemented = int($percent_implemented);
1127 my $percent_documented = 0;
1128 if ($spec_details->{NUM_DOCS})
1130 # Treat forwards and data as documented funcs for statistics
1131 $percent_documented = $spec_details->{NUM_DOCS} / $spec_details->{NUM_FUNCS} * 100;
1132 $percent_documented = int($percent_documented);
1135 # Make a list of the contributors to this DLL. Do this only for the source
1136 # files that make up the DLL, because some directories specify multiple dlls.
1137 my @contributors;
1139 for (@{$spec_details->{SOURCES}})
1141 my $source_details = $source_files{$_}[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;