webservices: Fix reader movement for text field mappings.
[wine.git] / tools / c2man.pl
blobe4056849f73afd9908dd381c14fc8e4f8ab79503
1 #! /usr/bin/perl -w
3 # Generate API documentation. See http://www.winehq.org/docs/winedev-guide/api-docs for details.
5 # Copyright (C) 2000 Mike McCormack
6 # Copyright (C) 2003 Jon Griffiths
8 # This library is free software; you can redistribute it and/or
9 # modify it under the terms of the GNU Lesser General Public
10 # License as published by the Free Software Foundation; either
11 # version 2.1 of the License, or (at your option) any later version.
13 # This library is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 # Lesser General Public License for more details.
18 # You should have received a copy of the GNU Lesser General Public
19 # License along with this library; if not, write to the Free Software
20 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 # TODO
23 # Consolidate A+W pairs together, and only write one doc, without the suffix
24 # Implement automatic docs of structs/defines in headers
25 # SGML gurus - feel free to smarten up the SGML.
26 # Add any other relevant information for the dll - imports etc
27 # Should we have a special output mode for WineHQ?
29 use strict;
30 use bytes;
32 # Function flags. most of these come from the spec flags
33 my $FLAG_DOCUMENTED = 1;
34 my $FLAG_NONAME = 2;
35 my $FLAG_I386 = 4;
36 my $FLAG_REGISTER = 8;
37 my $FLAG_APAIR = 16; # The A version of a matching W function
38 my $FLAG_WPAIR = 32; # The W version of a matching A function
39 my $FLAG_64PAIR = 64; # The 64 bit version of a matching 32 bit function
41 # Export list slot labels.
42 my $EXPORT_ORDINAL = 0; # Ordinal.
43 my $EXPORT_CALL = 1; # Call type.
44 my $EXPORT_EXPNAME = 2; # Export name.
45 my $EXPORT_IMPNAME = 3; # Implementation name.
46 my $EXPORT_FLAGS = 4; # Flags - see above.
48 # Options
49 my $opt_output_directory = "man3w"; # All default options are for nroff (man pages)
50 my $opt_manual_section = "3w";
51 my $opt_source_dir = ".";
52 my $opt_parent_dir = "";
53 my $opt_wine_root_dir = "";
54 my $opt_output_format = ""; # '' = nroff, 'h' = html, 's' = sgml, 'x' = xml
55 my $opt_output_empty = 0; # Non-zero = Create 'empty' comments (for every implemented function)
56 my $opt_fussy = 1; # Non-zero = Create only if we have a RETURNS section
57 my $opt_verbose = 0; # >0 = verbosity. Can be given multiple times (for debugging)
58 my @opt_header_file_list = ();
59 my @opt_spec_file_list = ();
60 my @opt_source_file_list = ();
62 # All the collected details about all the .spec files being processed
63 my %spec_files;
64 # All the collected details about all the source files being processed
65 my %source_files;
66 # All documented functions that are to be placed in the index
67 my @index_entries_list = ();
69 # useful globals
70 my $pwd = `pwd`."/";
71 $pwd =~ s/\n//;
72 my @datetime = localtime;
73 my @months = ( "Jan", "Feb", "Mar", "Apr", "May", "Jun",
74 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" );
75 my $year = $datetime[5] + 1900;
76 my $date = "$months[$datetime[4]] $year";
79 sub output_api_comment($);
80 sub output_api_footer($);
81 sub output_api_header($);
82 sub output_api_name($);
83 sub output_api_synopsis($);
84 sub output_close_api_file();
85 sub output_comment($);
86 sub output_html_index_files();
87 sub output_html_stylesheet();
88 sub output_open_api_file($);
89 sub output_sgml_dll_file($);
90 sub output_xml_dll_file($);
91 sub output_sgml_master_file($);
92 sub output_xml_master_file($);
93 sub output_spec($);
94 sub process_comment($);
95 sub process_extra_comment($);
98 # Generate the list of exported entries for the dll
99 sub process_spec_file($)
101 my $spec_name = shift;
102 (my $basename = $spec_name) =~ s/.*\///;
103 my ($dll_name, $dll_ext) = split(/\./, $basename);
104 $dll_ext = "dll" if ( $dll_ext eq "spec" );
105 my $uc_dll_name = uc $dll_name;
107 my $spec_details =
109 NAME => $basename,
110 DLL_NAME => $dll_name,
111 DLL_EXT => $dll_ext,
112 NUM_EXPORTS => 0,
113 NUM_STUBS => 0,
114 NUM_FUNCS => 0,
115 NUM_FORWARDS => 0,
116 NUM_VARS => 0,
117 NUM_DOCS => 0,
118 CONTRIBUTORS => [ ],
119 SOURCES => [ ],
120 DESCRIPTION => [ ],
121 EXPORTS => [ ],
122 EXPORTED_NAMES => { },
123 IMPLEMENTATION_NAMES => { },
124 EXTRA_COMMENTS => [ ],
125 CURRENT_EXTRA => [ ] ,
128 if ($opt_verbose > 0)
130 print "Processing ".$spec_name."\n";
133 # We allow opening to fail just to cater for the peculiarities of
134 # the Wine build system. This doesn't hurt, in any case
135 open(SPEC_FILE, "<$spec_name")
136 || (($opt_source_dir ne "")
137 && open(SPEC_FILE, "<$opt_source_dir/$spec_name"))
138 || return;
140 while(<SPEC_FILE>)
142 s/^\s+//; # Strip leading space
143 s/\s+\n$/\n/; # Strip trailing space
144 s/\s+/ /g; # Strip multiple tabs & spaces to a single space
145 s/\s*#.*//; # Strip comments
146 s/\(.*\)/ /; # Strip arguments
147 s/\s+/ /g; # Strip multiple tabs & spaces to a single space (again)
148 s/\n$//; # Strip newline
150 my $flags = 0;
151 if( /\-noname/ )
153 $flags |= $FLAG_NONAME;
155 if( /\-i386/ )
157 $flags |= $FLAG_I386;
159 if( /\-register/ )
161 $flags |= $FLAG_REGISTER;
163 s/ \-[a-z0-9=_]+//g; # Strip flags
165 if( /^(([0-9]+)|@) / )
167 # This line contains an exported symbol
168 my ($ordinal, $call_convention, $exported_name, $implementation_name) = split(' ');
170 for ($call_convention)
172 /^(cdecl|stdcall|varargs|pascal)$/
173 && do { $spec_details->{NUM_FUNCS}++; last; };
174 /^(variable|equate)$/
175 && do { $spec_details->{NUM_VARS}++; last; };
176 /^(extern)$/
177 && do { $spec_details->{NUM_FORWARDS}++; last; };
178 /^stub$/ && do { $spec_details->{NUM_STUBS}++; last; };
179 if ($opt_verbose > 0)
181 print "Warning: didn't recognise convention \'",$call_convention,"'\n";
183 last;
186 # Convert ordinal only names so we can find them later
187 if ($exported_name eq "@")
189 $exported_name = $uc_dll_name.'_'.$ordinal;
191 if (!defined($implementation_name))
193 $implementation_name = $exported_name;
195 if ($implementation_name eq "")
197 $implementation_name = $exported_name;
200 if ($implementation_name =~ /(.*?)\./)
202 $call_convention = "forward"; # Referencing a function from another dll
203 $spec_details->{NUM_FUNCS}--;
204 $spec_details->{NUM_FORWARDS}++;
207 # Add indices for the exported and implementation names
208 $spec_details->{EXPORTED_NAMES}{$exported_name} = $spec_details->{NUM_EXPORTS};
209 if ($implementation_name ne $exported_name)
211 $spec_details->{IMPLEMENTATION_NAMES}{$exported_name} = $spec_details->{NUM_EXPORTS};
214 # Add the exported entry
215 $spec_details->{NUM_EXPORTS}++;
216 my @export = ($ordinal, $call_convention, $exported_name, $implementation_name, $flags);
217 push (@{$spec_details->{EXPORTS}},[@export]);
220 close(SPEC_FILE);
222 # Add this .spec files details to the list of .spec files
223 $spec_files{$uc_dll_name} = [$spec_details];
226 # Read each source file, extract comments, and generate API documentation if appropriate
227 sub process_source_file($)
229 my $source_file = shift;
230 my $source_details =
232 CONTRIBUTORS => [ ],
233 DEBUG_CHANNEL => "",
235 my $comment =
237 FILE => $source_file,
238 COMMENT_NAME => "",
239 ALT_NAME => "",
240 DLL_NAME => "",
241 DLL_EXT => "",
242 ORDINAL => "",
243 RETURNS => "",
244 PROTOTYPE => [],
245 TEXT => [],
247 my $parse_state = 0;
248 my $ignore_blank_lines = 1;
249 my $extra_comment = 0; # 1 if this is an extra comment, i.e it's not a .spec export
251 if ($opt_verbose > 0)
253 print "Processing ".$source_file."\n";
255 open(SOURCE_FILE,"<$source_file")
256 || (($opt_source_dir ne ".")
257 && open(SOURCE_FILE,"<$opt_source_dir/$source_file"))
258 || (($opt_parent_dir ne "")
259 && open(SOURCE_FILE,"<$opt_source_dir/$opt_parent_dir/$source_file"))
260 || die "couldn't open ".$source_file."\n";
262 # Add this source file to the list of source files
263 $source_files{$source_file} = [$source_details];
265 while(<SOURCE_FILE>)
267 s/\n$//; # Strip newline
268 s/^\s+//; # Strip leading space
269 s/\s+$//; # Strip trailing space
270 if (! /^\*\|/ )
272 # Strip multiple tabs & spaces to a single space
273 s/\s+/ /g;
276 if ( / +Copyright *(\([Cc]\))*[0-9 \-\,\/]*([[:alpha:][:^ascii:] \.\-]+)/ )
278 # Extract a contributor to this file
279 my $contributor = $2;
280 $contributor =~ s/ *$//;
281 $contributor =~ s/^by //;
282 $contributor =~ s/\.$//;
283 $contributor =~ s/ (for .*)/ \($1\)/;
284 if ($contributor ne "")
286 if ($opt_verbose > 3)
288 print "Info: Found contributor:'".$contributor."'\n";
290 push (@{$source_details->{CONTRIBUTORS}},$contributor);
293 elsif ( /WINE_DEFAULT_DEBUG_CHANNEL\(([A-Za-z]*)\)/ )
295 # Extract the debug channel to use
296 if ($opt_verbose > 3)
298 print "Info: Found debug channel:'".$1."'\n";
300 $source_details->{DEBUG_CHANNEL} = $1;
303 if ($parse_state == 0) # Searching for a comment
305 if ( /^\/\**$/ )
307 # This file is used by the DLL - Make sure we get our contributors right
308 @{$spec_files{$comment->{DLL_NAME}}[0]->{CURRENT_EXTRA}} = ();
309 push (@{$spec_files{$comment->{DLL_NAME}}[0]->{SOURCES}},$comment->{FILE});
310 # Found a comment start
311 $comment->{COMMENT_NAME} = "";
312 $comment->{ALT_NAME} = "";
313 $comment->{DLL_NAME} = "";
314 $comment->{ORDINAL} = "";
315 $comment->{RETURNS} = "";
316 $comment->{PROTOTYPE} = [];
317 $comment->{TEXT} = [];
318 $ignore_blank_lines = 1;
319 $extra_comment = 0;
320 $parse_state = 3;
323 elsif ($parse_state == 1) # Reading in a comment
325 if ( /^\**\// )
327 # Found the end of the comment
328 $parse_state = 2;
330 elsif ( s/^\*\|/\|/ )
332 # A line of comment not meant to be pre-processed
333 push (@{$comment->{TEXT}},$_); # Add the comment text
335 elsif ( s/^ *\** *// )
337 # A line of comment, starting with an asterisk
338 if ( /^[A-Z]+$/ || $_ eq "")
340 # This is a section start, so skip blank lines before and after it.
341 my $last_line = pop(@{$comment->{TEXT}});
342 if (defined($last_line) && $last_line ne "")
344 # Put it back
345 push (@{$comment->{TEXT}},$last_line);
347 if ( /^[A-Z]+$/ )
349 $ignore_blank_lines = 1;
351 else
353 $ignore_blank_lines = 0;
357 if ($ignore_blank_lines == 0 || $_ ne "")
359 push (@{$comment->{TEXT}},$_); # Add the comment text
362 else
364 # This isn't a well formatted comment: look for the next one
365 $parse_state = 0;
368 elsif ($parse_state == 2) # Finished reading in a comment
370 if ( /(WINAPIV|WINAPI|__cdecl|PASCAL|CALLBACK|FARPROC16)/ ||
371 /.*?\(/ )
373 # Comment is followed by a function definition
374 $parse_state = 4; # Fall through to read prototype
376 else
378 # Allow cpp directives and blank lines between the comment and prototype
379 if ($extra_comment == 1)
381 # An extra comment not followed by a function definition
382 $parse_state = 5; # Fall through to process comment
384 elsif (!/^\#/ && !/^ *$/ && !/^__ASM_GLOBAL_FUNC/)
386 # This isn't a well formatted comment: look for the next one
387 if ($opt_verbose > 1)
389 print "Info: Function '",$comment->{COMMENT_NAME},"' not followed by prototype.\n";
391 $parse_state = 0;
395 elsif ($parse_state == 3) # Reading in the first line of a comment
397 s/^ *\** *//;
398 if ( /^([\@A-Za-z0-9_]+) +(\(|\[)([A-Za-z0-9_]+)\.(([0-9]+)|@)(\)|\])\s*(.*)$/ )
400 # Found a correctly formed "ApiName (DLLNAME.Ordinal)" line.
401 if (defined ($7) && $7 ne "")
403 push (@{$comment->{TEXT}},$_); # Add the trailing comment text
405 $comment->{COMMENT_NAME} = $1;
406 $comment->{DLL_NAME} = uc $3;
407 $comment->{ORDINAL} = $4;
408 $comment->{DLL_NAME} =~ s/^KERNEL$/KRNL386/; # Too many of these to ignore, _old_ code
409 $parse_state = 1;
411 elsif ( /^([A-Za-z0-9_-]+) +\{([A-Za-z0-9_]+)\}$/ )
413 # Found a correctly formed "CommentTitle {DLLNAME}" line (extra documentation)
414 $comment->{COMMENT_NAME} = $1;
415 $comment->{DLL_NAME} = uc $2;
416 $comment->{ORDINAL} = "";
417 $extra_comment = 1;
418 $parse_state = 1;
420 else
422 # This isn't a well formatted comment: look for the next one
423 $parse_state = 0;
427 if ($parse_state == 4) # Reading in the function definition
429 push (@{$comment->{PROTOTYPE}},$_);
430 # Strip comments from the line before checking for ')'
431 my $stripped_line = $_;
432 $stripped_line =~ s/ *(\/\* *)(.*?)( *\*\/ *)//;
433 if ( $stripped_line =~ /\)/ )
435 # Strip a blank last line
436 my $last_line = pop(@{$comment->{TEXT}});
437 if (defined($last_line) && $last_line ne "")
439 # Put it back
440 push (@{$comment->{TEXT}},$last_line);
443 if ($opt_output_empty != 0 && @{$comment->{TEXT}} == 0)
445 # Create a 'not implemented' comment
446 @{$comment->{TEXT}} = ("fixme: This function has not yet been documented.");
448 $parse_state = 5;
452 if ($parse_state == 5) # Processing the comment
454 # Process it, if it has any text
455 if (@{$comment->{TEXT}} > 0)
457 if ($extra_comment == 1)
459 process_extra_comment($comment);
461 else
463 @{$comment->{TEXT}} = ("DESCRIPTION", @{$comment->{TEXT}});
464 process_comment($comment);
467 elsif ($opt_verbose > 1 && $opt_output_empty == 0)
469 print "Info: Function '",$comment->{COMMENT_NAME},"' has no documentation.\n";
471 $parse_state = 0;
474 close(SOURCE_FILE);
477 # Standardise a comments text for consistency
478 sub process_comment_text($)
480 my $comment = shift;
481 my $in_params = 0;
482 my @tmp_list = ();
483 my $i = 0;
485 for (@{$comment->{TEXT}})
487 my $line = $_;
489 if ( /^\s*$/ || /^[A-Z]+$/ || /^-/ )
491 $in_params = 0;
493 if ( $in_params > 0 && !/\[/ && !/\]/ )
495 # Possibly a continuation of the parameter description
496 my $last_line = pop(@tmp_list);
497 if ( $last_line =~ /\[/ && $last_line =~ /\]/ )
499 $line = $last_line." ".$_;
501 else
503 $in_params = 0;
504 push (@tmp_list, $last_line);
507 if ( /^(PARAMS|MEMBERS)$/ )
509 $in_params = 1;
511 push (@tmp_list, $line);
514 @{$comment->{TEXT}} = @tmp_list;
516 for (@{$comment->{TEXT}})
518 if (! /^\|/ )
520 # Map I/O values. These come in too many formats to standardise now....
521 s/\[I\]|\[i\]|\[in\]|\[IN\]/\[In\] /g;
522 s/\[O\]|\[o\]|\[out\]|\[OUT\]/\[Out\]/g;
523 s/\[I\/O\]|\[I\,O\]|\[i\/o\]|\[in\/out\]|\[IN\/OUT\]/\[In\/Out\]/g;
524 # TRUE/FALSE/NULL are defines, capitalise them
525 s/True|true/TRUE/g;
526 s/False|false/FALSE/g;
527 s/Null|null/NULL/g;
528 # Preferred capitalisations
529 s/ wine| WINE/ Wine/g;
530 s/ API | api / Api /g;
531 s/ DLL | Dll / dll /g;
532 s/ URL | url / Url /g;
533 s/WIN16|win16/Win16/g;
534 s/WIN32|win32/Win32/g;
535 s/WIN64|win64/Win64/g;
536 s/ ID | id / Id /g;
537 # Grammar
538 s/([a-z])\.([A-Z])/$1\. $2/g; # Space after full stop
539 s/ \:/\:/g; # Colons to the left
540 s/ \;/\;/g; # Semi-colons too
541 # Common idioms
542 s/^See ([A-Za-z0-9_]+)\.$/See $1\(\)\./; # Referring to A version from W
543 s/^Unicode version of ([A-Za-z0-9_]+)\.$/See $1\(\)\./; # Ditto
544 s/^64\-bit version of ([A-Za-z0-9_]+)\.$/See $1\(\)\./; # Referring to 32 bit version from 64
545 s/^PARAMETERS$/PARAMS/; # Name of parameter section should be 'PARAMS'
546 # Trademarks
547 s/( |\.)(M\$|MS|Microsoft|microsoft|micro\$oft|Micro\$oft)( |\.)/$1Microsoft\(tm\)$3/g;
548 s/( |\.)(Windows|windows|windoze|winblows)( |\.)/$1Windows\(tm\)$3/g;
549 s/( |\.)(DOS|dos|msdos)( |\.)/$1MS-DOS\(tm\)$3/g;
550 s/( |\.)(UNIX|unix)( |\.)/$1Unix\(tm\)$3/g;
551 s/( |\.)(LINIX|linux)( |\.)/$1Linux\(tm\)$3/g;
552 # Abbreviations
553 s/( char )/ character /g;
554 s/( chars )/ characters /g;
555 s/( info )/ information /g;
556 s/( app )/ application /g;
557 s/( apps )/ applications /g;
558 s/( exe )/ executable /g;
559 s/( ptr )/ pointer /g;
560 s/( obj )/ object /g;
561 s/( err )/ error /g;
562 s/( bool )/ boolean /g;
563 s/( no\. )/ number /g;
564 s/( No\. )/ Number /g;
565 # Punctuation
566 if ( /\[I|\[O/ && ! /\.$/ )
568 $_ = $_."."; # Always have a full stop at the end of parameter desc.
570 elsif ($i > 0 && /^[A-Z]*$/ &&
571 !(@{$comment->{TEXT}}[$i-1] =~ /\.$/) &&
572 !(@{$comment->{TEXT}}[$i-1] =~ /\:$/))
575 if (!(@{$comment->{TEXT}}[$i-1] =~ /^[A-Z]*$/))
577 # Paragraphs always end with a full stop
578 @{$comment->{TEXT}}[$i-1] = @{$comment->{TEXT}}[$i-1].".";
582 $i++;
586 # Standardise our comment and output it if it is suitable.
587 sub process_comment($)
589 my $comment = shift;
591 # Don't process this comment if the function isn't exported
592 my $spec_details = $spec_files{$comment->{DLL_NAME}}[0];
594 if (!defined($spec_details))
596 if ($opt_verbose > 2)
598 print "Warning: Function '".$comment->{COMMENT_NAME}."' belongs to '".
599 $comment->{DLL_NAME}."' (not passed with -w): not processing it.\n";
601 return;
604 if ($comment->{COMMENT_NAME} eq "@")
606 my $found = 0;
608 # Find the name from the .spec file
609 for (@{$spec_details->{EXPORTS}})
611 if (@$_[$EXPORT_ORDINAL] eq $comment->{ORDINAL})
613 $comment->{COMMENT_NAME} = @$_[$EXPORT_EXPNAME];
614 $found = 1;
618 if ($found == 0)
620 # Create an implementation name
621 $comment->{COMMENT_NAME} = $comment->{DLL_NAME}."_".$comment->{ORDINAL};
625 my $exported_names = $spec_details->{EXPORTED_NAMES};
626 my $export_index = $exported_names->{$comment->{COMMENT_NAME}};
627 my $implementation_names = $spec_details->{IMPLEMENTATION_NAMES};
629 if (!defined($export_index))
631 # Perhaps the comment uses the implementation name?
632 $export_index = $implementation_names->{$comment->{COMMENT_NAME}};
634 if (!defined($export_index))
636 # This function doesn't appear to be exported. hmm.
637 if ($opt_verbose > 2)
639 print "Warning: Function '".$comment->{COMMENT_NAME}."' claims to belong to '".
640 $comment->{DLL_NAME}."' but is not exported by it: not processing it.\n";
642 return;
645 # When the function is exported twice we have the second name below the first
646 # (you see this a lot in ntdll, but also in some other places).
647 my $first_line = ${$comment->{TEXT}}[1];
649 if ( $first_line =~ /^(@|[A-Za-z0-9_]+) +(\(|\[)([A-Za-z0-9_]+)\.(([0-9]+)|@)(\)|\])$/ )
651 # Found a second name - mark it as documented
652 my $alt_index = $exported_names->{$1};
653 if (defined($alt_index))
655 if ($opt_verbose > 2)
657 print "Info: Found alternate name '",$1,"\n";
659 my $alt_export = @{$spec_details->{EXPORTS}}[$alt_index];
660 @$alt_export[$EXPORT_FLAGS] |= $FLAG_DOCUMENTED;
661 $spec_details->{NUM_DOCS}++;
662 ${$comment->{TEXT}}[1] = "";
666 if (@{$spec_details->{CURRENT_EXTRA}})
668 # We have an extra comment that might be related to this one
669 my $current_comment = ${$spec_details->{CURRENT_EXTRA}}[0];
670 my $current_name = $current_comment->{COMMENT_NAME};
671 if ($comment->{COMMENT_NAME} =~ /^$current_name/ && $comment->{COMMENT_NAME} ne $current_name)
673 if ($opt_verbose > 2)
675 print "Linking ",$comment->{COMMENT_NAME}," to $current_name\n";
677 # Add a reference to this comment to our extra comment
678 push (@{$current_comment->{TEXT}}, $comment->{COMMENT_NAME}."()","");
682 # We want our docs generated using the implementation name, so they are unique
683 my $export = @{$spec_details->{EXPORTS}}[$export_index];
684 $comment->{COMMENT_NAME} = @$export[$EXPORT_IMPNAME];
685 $comment->{ALT_NAME} = @$export[$EXPORT_EXPNAME];
687 # Mark the function as documented
688 $spec_details->{NUM_DOCS}++;
689 @$export[$EXPORT_FLAGS] |= $FLAG_DOCUMENTED;
691 # If we have parameter comments in the prototype, extract them
692 my @parameter_comments;
693 for (@{$comment->{PROTOTYPE}})
695 s/ *\, */\,/g; # Strip spaces from around commas
697 if ( s/ *(\/\* *)(.*?)( *\*\/ *)// ) # Strip out comment
699 my $parameter_comment = $2;
700 if (!$parameter_comment =~ /^\[/ )
702 # Add [IO] markers so we format the comment correctly
703 $parameter_comment = "[fixme] ".$parameter_comment;
705 if ( /( |\*)([A-Za-z_]{1}[A-Za-z_0-9]*)(\,|\))/ )
707 # Add the parameter name
708 $parameter_comment = $2." ".$parameter_comment;
710 push (@parameter_comments, $parameter_comment);
714 # If we extracted any prototype comments, add them to the comment text.
715 if (@parameter_comments)
717 @parameter_comments = ("PARAMS", @parameter_comments);
718 my @new_comment = ();
719 my $inserted_params = 0;
721 for (@{$comment->{TEXT}})
723 if ( $inserted_params == 0 && /^[A-Z]+$/ )
725 # Found a section header, so this is where we insert
726 push (@new_comment, @parameter_comments);
727 $inserted_params = 1;
729 push (@new_comment, $_);
731 if ($inserted_params == 0)
733 # Add them to the end
734 push (@new_comment, @parameter_comments);
736 $comment->{TEXT} = [@new_comment];
739 if ($opt_fussy == 1 && $opt_output_empty == 0)
741 # Reject any comment that doesn't have a description or a RETURNS section.
742 # This is the default for now, 'coz many comments aren't suitable.
743 my $found_returns = 0;
744 my $found_description_text = 0;
745 my $in_description = 0;
746 for (@{$comment->{TEXT}})
748 if ( /^RETURNS$/ )
750 $found_returns = 1;
751 $in_description = 0;
753 elsif ( /^DESCRIPTION$/ )
755 $in_description = 1;
757 elsif ($in_description == 1)
759 if ( !/^[A-Z]+$/ )
761 # Don't reject comments that refer to another doc (e.g. A/W)
762 if ( /^See ([A-Za-z0-9_]+)\.$/ )
764 if ($comment->{COMMENT_NAME} =~ /W$/ )
766 # This is probably a Unicode version of an Ascii function.
767 # Create the Ascii name and see if it has been documented
768 my $ascii_name = $comment->{COMMENT_NAME};
769 $ascii_name =~ s/W$/A/;
771 my $ascii_export_index = $exported_names->{$ascii_name};
773 if (!defined($ascii_export_index))
775 $ascii_export_index = $implementation_names->{$ascii_name};
777 if (!defined($ascii_export_index))
779 if ($opt_verbose > 2)
781 print "Warning: Function '".$comment->{COMMENT_NAME}."' is not an A/W pair.\n";
784 else
786 my $ascii_export = @{$spec_details->{EXPORTS}}[$ascii_export_index];
787 if (@$ascii_export[$EXPORT_FLAGS] & $FLAG_DOCUMENTED)
789 # Flag these functions as an A/W pair
790 @$ascii_export[$EXPORT_FLAGS] |= $FLAG_APAIR;
791 @$export[$EXPORT_FLAGS] |= $FLAG_WPAIR;
795 $found_returns = 1;
797 elsif ( /^Unicode version of ([A-Za-z0-9_]+)\.$/ )
799 @$export[$EXPORT_FLAGS] |= $FLAG_WPAIR; # Explicitly marked as W version
800 $found_returns = 1;
802 elsif ( /^64\-bit version of ([A-Za-z0-9_]+)\.$/ )
804 @$export[$EXPORT_FLAGS] |= $FLAG_64PAIR; # Explicitly marked as 64 bit version
805 $found_returns = 1;
807 $found_description_text = 1;
809 else
811 $in_description = 0;
815 if ($found_returns == 0 || $found_description_text == 0)
817 if ($opt_verbose > 2)
819 print "Info: Function '",$comment->{COMMENT_NAME},"' has no ",
820 "description and/or RETURNS section, skipping\n";
822 $spec_details->{NUM_DOCS}--;
823 @$export[$EXPORT_FLAGS] &= ~$FLAG_DOCUMENTED;
824 return;
828 process_comment_text($comment);
830 # Strip the prototypes return value, call convention, name and brackets
831 # (This leaves it as a list of types and names, or empty for void functions)
832 my $prototype = join(" ", @{$comment->{PROTOTYPE}});
833 $prototype =~ s/ / /g;
835 if ( $prototype =~ /(WINAPIV|WINAPI|__cdecl|PASCAL|CALLBACK|FARPROC16)/ )
837 $prototype =~ s/^(.*?)\s+(WINAPIV|WINAPI|__cdecl|PASCAL|CALLBACK|FARPROC16)\s+(.*?)\(\s*(.*)/$4/;
838 $comment->{RETURNS} = $1;
840 else
842 $prototype =~ s/^(.*?)([A-Za-z0-9_]+)\s*\(\s*(.*)/$3/;
843 $comment->{RETURNS} = $1;
846 $prototype =~ s/ *\).*//; # Strip end bracket
847 $prototype =~ s/ *\* */\*/g; # Strip space around pointers
848 $prototype =~ s/ *\, */\,/g; # Strip space around commas
849 $prototype =~ s/^(void|VOID)$//; # If void, leave blank
850 $prototype =~ s/\*([A-Za-z_])/\* $1/g; # Separate pointers from parameter name
851 @{$comment->{PROTOTYPE}} = split ( /,/ ,$prototype);
853 # FIXME: If we have no parameters, make sure we have a PARAMS: None. section
855 # Find header file
856 my $h_file = "";
857 if (@$export[$EXPORT_FLAGS] & $FLAG_NONAME)
859 $h_file = "Exported by ordinal only. Use GetProcAddress() to obtain a pointer to the function.";
861 else
863 if ($comment->{COMMENT_NAME} ne "")
865 my $tmp = "grep -s -l $comment->{COMMENT_NAME} @opt_header_file_list 2>/dev/null";
866 $tmp = `$tmp`;
867 my $exit_value = $? >> 8;
868 if ($exit_value == 0)
870 $tmp =~ s/\n.*//g;
871 if ($tmp ne "")
873 $h_file = "$tmp";
874 $h_file =~ s|^.*/\./||;
878 elsif ($comment->{ALT_NAME} ne "")
880 my $tmp = "grep -s -l $comment->{ALT_NAME} @opt_header_file_list"." 2>/dev/null";
881 $tmp = `$tmp`;
882 my $exit_value = $? >> 8;
883 if ($exit_value == 0)
885 $tmp =~ s/\n.*//g;
886 if ($tmp ne "")
888 $h_file = "$tmp";
889 $h_file =~ s|^.*/\./||;
893 $h_file =~ s/^ *//;
894 $h_file =~ s/\n//;
895 if ($h_file eq "")
897 $h_file = "Not declared in a Wine header. The function is either undocumented, or missing from Wine."
899 else
901 $h_file = "Declared in \"".$h_file."\".";
905 # Find source file
906 my $c_file = $comment->{FILE};
907 if ($opt_wine_root_dir ne "")
909 my $cfile = $pwd."/".$c_file; # Current dir + file
910 $cfile =~ s/(.+)(\/.*$)/$1/; # Strip the filename
911 $cfile = `cd $cfile && pwd`; # Strip any relative parts (e.g. "../../")
912 $cfile =~ s/\n//; # Strip newline
913 my $newfile = $c_file;
914 $newfile =~ s/(.+)(\/.*$)/$2/; # Strip all but the filename
915 $cfile = $cfile."/".$newfile; # Append filename to base path
916 $cfile =~ s/$opt_wine_root_dir//; # Get rid of the root directory
917 $cfile =~ s/\/\//\//g; # Remove any double slashes
918 $cfile =~ s/^\/+//; # Strip initial directory slash
919 $c_file = $cfile;
921 $c_file = "Implemented in \"".$c_file."\".";
923 # Add the implementation details
924 push (@{$comment->{TEXT}}, "IMPLEMENTATION","",$h_file,"",$c_file);
926 if (@$export[$EXPORT_FLAGS] & $FLAG_I386)
928 push (@{$comment->{TEXT}}, "", "Available on x86 platforms only.");
930 if (@$export[$EXPORT_FLAGS] & $FLAG_REGISTER)
932 push (@{$comment->{TEXT}}, "", "This function passes one or more arguments in registers. ",
933 "For more details, please read the source code.");
935 my $source_details = $source_files{$comment->{FILE}}[0];
936 if ($source_details->{DEBUG_CHANNEL} ne "")
938 push (@{$comment->{TEXT}}, "", "Debug channel \"".$source_details->{DEBUG_CHANNEL}."\".");
941 # Write out the documentation for the API
942 output_comment($comment)
945 # process our extra comment and output it if it is suitable.
946 sub process_extra_comment($)
948 my $comment = shift;
950 my $spec_details = $spec_files{$comment->{DLL_NAME}}[0];
952 if (!defined($spec_details))
954 if ($opt_verbose > 2)
956 print "Warning: Extra comment '".$comment->{COMMENT_NAME}."' belongs to '".
957 $comment->{DLL_NAME}."' (not passed with -w): not processing it.\n";
959 return;
962 # Check first to see if this is documentation for the DLL.
963 if ($comment->{COMMENT_NAME} eq $comment->{DLL_NAME})
965 if ($opt_verbose > 2)
967 print "Info: Found DLL documentation\n";
969 for (@{$comment->{TEXT}})
971 push (@{$spec_details->{DESCRIPTION}}, $_);
973 return;
976 # Add the comment to the DLL page as a link
977 push (@{$spec_details->{EXTRA_COMMENTS}},$comment->{COMMENT_NAME});
979 # If we have a prototype, process as a regular comment
980 if (@{$comment->{PROTOTYPE}})
982 $comment->{ORDINAL} = "@";
984 # Add an index for the comment name
985 $spec_details->{EXPORTED_NAMES}{$comment->{COMMENT_NAME}} = $spec_details->{NUM_EXPORTS};
987 # Add a fake exported entry
988 $spec_details->{NUM_EXPORTS}++;
989 my ($ordinal, $call_convention, $exported_name, $implementation_name, $documented) =
990 ("@", "fake", $comment->{COMMENT_NAME}, $comment->{COMMENT_NAME}, 0);
991 my @export = ($ordinal, $call_convention, $exported_name, $implementation_name, $documented);
992 push (@{$spec_details->{EXPORTS}},[@export]);
993 @{$comment->{TEXT}} = ("DESCRIPTION", @{$comment->{TEXT}});
994 process_comment($comment);
995 return;
998 if ($opt_verbose > 0)
1000 print "Processing ",$comment->{COMMENT_NAME},"\n";
1003 if (@{$spec_details->{CURRENT_EXTRA}})
1005 my $current_comment = ${$spec_details->{CURRENT_EXTRA}}[0];
1007 if ($opt_verbose > 0)
1009 print "Processing old current: ",$current_comment->{COMMENT_NAME},"\n";
1011 # Output the current comment
1012 process_comment_text($current_comment);
1013 output_open_api_file($current_comment->{COMMENT_NAME});
1014 output_api_header($current_comment);
1015 output_api_name($current_comment);
1016 output_api_comment($current_comment);
1017 output_api_footer($current_comment);
1018 output_close_api_file();
1021 if ($opt_verbose > 2)
1023 print "Setting current to ",$comment->{COMMENT_NAME},"\n";
1026 my $comment_copy =
1028 FILE => $comment->{FILE},
1029 COMMENT_NAME => $comment->{COMMENT_NAME},
1030 ALT_NAME => $comment->{ALT_NAME},
1031 DLL_NAME => $comment->{DLL_NAME},
1032 ORDINAL => $comment->{ORDINAL},
1033 RETURNS => $comment->{RETURNS},
1034 PROTOTYPE => [],
1035 TEXT => [],
1038 for (@{$comment->{TEXT}})
1040 push (@{$comment_copy->{TEXT}}, $_);
1042 # Set this comment to be the current extra comment
1043 @{$spec_details->{CURRENT_EXTRA}} = ($comment_copy);
1046 # Write a standardised comment out in the appropriate format
1047 sub output_comment($)
1049 my $comment = shift;
1051 if ($opt_verbose > 0)
1053 print "Processing ",$comment->{COMMENT_NAME},"\n";
1056 if ($opt_verbose > 4)
1058 print "--PROTO--\n";
1059 for (@{$comment->{PROTOTYPE}})
1061 print "'".$_."'\n";
1064 print "--COMMENT--\n";
1065 for (@{$comment->{TEXT} })
1067 print $_."\n";
1071 output_open_api_file($comment->{COMMENT_NAME});
1072 output_api_header($comment);
1073 output_api_name($comment);
1074 output_api_synopsis($comment);
1075 output_api_comment($comment);
1076 output_api_footer($comment);
1077 output_close_api_file();
1080 # Write out an index file for each .spec processed
1081 sub process_index_files()
1083 foreach my $spec_file (keys %spec_files)
1085 my $spec_details = $spec_files{$spec_file}[0];
1086 if (defined ($spec_details->{DLL_NAME}))
1088 if (@{$spec_details->{CURRENT_EXTRA}})
1090 # We have an unwritten extra comment, write it
1091 my $current_comment = ${$spec_details->{CURRENT_EXTRA}}[0];
1092 process_extra_comment($current_comment);
1093 @{$spec_details->{CURRENT_EXTRA}} = ();
1095 output_spec($spec_details);
1100 # Write a spec files documentation out in the appropriate format
1101 sub output_spec($)
1103 my $spec_details = shift;
1105 if ($opt_verbose > 2)
1107 print "Writing:",$spec_details->{DLL_NAME},"\n";
1110 # Use the comment output functions for consistency
1111 my $comment =
1113 FILE => $spec_details->{DLL_NAME},
1114 COMMENT_NAME => $spec_details->{DLL_NAME}.".".$spec_details->{DLL_EXT},
1115 ALT_NAME => $spec_details->{DLL_NAME},
1116 DLL_NAME => "",
1117 ORDINAL => "",
1118 RETURNS => "",
1119 PROTOTYPE => [],
1120 TEXT => [],
1122 my $total_implemented = $spec_details->{NUM_FORWARDS} + $spec_details->{NUM_VARS} +
1123 $spec_details->{NUM_FUNCS};
1124 my $percent_implemented = 0;
1125 if ($total_implemented)
1127 $percent_implemented = $total_implemented /
1128 ($total_implemented + $spec_details->{NUM_STUBS}) * 100;
1130 $percent_implemented = int($percent_implemented);
1131 my $percent_documented = 0;
1132 if ($spec_details->{NUM_DOCS})
1134 # Treat forwards and data as documented funcs for statistics
1135 $percent_documented = $spec_details->{NUM_DOCS} / $spec_details->{NUM_FUNCS} * 100;
1136 $percent_documented = int($percent_documented);
1139 # Make a list of the contributors to this DLL.
1140 my @contributors;
1142 foreach my $source_file (keys %source_files)
1144 my $source_details = $source_files{$source_file}[0];
1145 for (@{$source_details->{CONTRIBUTORS}})
1147 push (@contributors, $_);
1151 my %saw;
1152 @contributors = grep(!$saw{$_}++, @contributors); # remove dups, from perlfaq4 manpage
1153 @contributors = sort @contributors;
1155 # Remove duplicates and blanks
1156 for(my $i=0; $i<@contributors; $i++)
1158 if ($i > 0 && ($contributors[$i] =~ /$contributors[$i-1]/ || $contributors[$i-1] eq ""))
1160 $contributors[$i-1] = $contributors[$i];
1163 undef %saw;
1164 @contributors = grep(!$saw{$_}++, @contributors);
1166 if ($opt_verbose > 3)
1168 print "Contributors:\n";
1169 for (@contributors)
1171 print "'".$_."'\n";
1174 my $contribstring = join (", ", @contributors);
1176 # Create the initial comment text
1177 @{$comment->{TEXT}} = (
1178 "NAME",
1179 $comment->{COMMENT_NAME}
1182 # Add the description, if we have one
1183 if (@{$spec_details->{DESCRIPTION}})
1185 push (@{$comment->{TEXT}}, "DESCRIPTION");
1186 for (@{$spec_details->{DESCRIPTION}})
1188 push (@{$comment->{TEXT}}, $_);
1192 # Add the statistics and contributors
1193 push (@{$comment->{TEXT}},
1194 "STATISTICS",
1195 "Forwards: ".$spec_details->{NUM_FORWARDS},
1196 "Variables: ".$spec_details->{NUM_VARS},
1197 "Stubs: ".$spec_details->{NUM_STUBS},
1198 "Functions: ".$spec_details->{NUM_FUNCS},
1199 "Exports-Total: ".$spec_details->{NUM_EXPORTS},
1200 "Implemented-Total: ".$total_implemented." (".$percent_implemented."%)",
1201 "Documented-Total: ".$spec_details->{NUM_DOCS}." (".$percent_documented."%)",
1202 "CONTRIBUTORS",
1203 "The following people hold copyrights on the source files comprising this dll:",
1205 $contribstring,
1206 "Note: This list may not be complete.",
1207 "For a complete listing, see the git commit logs and the File \"AUTHORS\" in the Wine source tree.",
1211 if ($opt_output_format eq "h")
1213 # Add the exports to the comment text
1214 push (@{$comment->{TEXT}},"EXPORTS");
1215 my $exports = $spec_details->{EXPORTS};
1216 for (@$exports)
1218 my $line = "";
1220 # @$_ => ordinal, call convention, exported name, implementation name, flags;
1221 if (@$_[$EXPORT_CALL] eq "forward")
1223 my $forward_dll = @$_[$EXPORT_IMPNAME];
1224 $forward_dll =~ s/\.(.*)//;
1225 $line = @$_[$EXPORT_EXPNAME]." (forward to ".$1."() in ".$forward_dll."())";
1227 elsif (@$_[$EXPORT_CALL] eq "extern")
1229 $line = @$_[$EXPORT_EXPNAME]." (extern)";
1231 elsif (@$_[$EXPORT_CALL] eq "stub")
1233 $line = @$_[$EXPORT_EXPNAME]." (stub)";
1235 elsif (@$_[$EXPORT_CALL] eq "fake")
1237 # Don't add this function here, it gets listed with the extra documentation
1238 if (!(@$_[$EXPORT_FLAGS] & $FLAG_WPAIR))
1240 # This function should be indexed
1241 push (@index_entries_list, @$_[$EXPORT_IMPNAME].",".@$_[$EXPORT_IMPNAME]);
1244 elsif (@$_[$EXPORT_CALL] eq "equate" || @$_[$EXPORT_CALL] eq "variable")
1246 $line = @$_[$EXPORT_EXPNAME]." (data)";
1248 else
1250 # A function
1251 if (@$_[$EXPORT_FLAGS] & $FLAG_DOCUMENTED)
1253 # Documented
1254 $line = @$_[$EXPORT_EXPNAME]." (implemented as ".@$_[$EXPORT_IMPNAME]."())";
1255 if (@$_[$EXPORT_EXPNAME] ne @$_[$EXPORT_IMPNAME])
1257 $line = @$_[$EXPORT_EXPNAME]." (implemented as ".@$_[$EXPORT_IMPNAME]."())";
1259 else
1261 $line = @$_[$EXPORT_EXPNAME]."()";
1263 if (!(@$_[$EXPORT_FLAGS] & $FLAG_WPAIR))
1265 # This function should be indexed
1266 push (@index_entries_list, @$_[$EXPORT_EXPNAME].",".@$_[$EXPORT_IMPNAME]);
1269 else
1271 $line = @$_[$EXPORT_EXPNAME]." (not documented)";
1274 if ($line ne "")
1276 push (@{$comment->{TEXT}}, $line, "");
1280 # Add links to the extra documentation
1281 if (@{$spec_details->{EXTRA_COMMENTS}})
1283 push (@{$comment->{TEXT}}, "SEE ALSO");
1284 my %htmp;
1285 @{$spec_details->{EXTRA_COMMENTS}} = grep(!$htmp{$_}++, @{$spec_details->{EXTRA_COMMENTS}});
1286 for (@{$spec_details->{EXTRA_COMMENTS}})
1288 push (@{$comment->{TEXT}}, $_."()", "");
1292 # The dll entry should also be indexed
1293 push (@index_entries_list, $spec_details->{DLL_NAME}.",".$spec_details->{DLL_NAME});
1295 # Write out the document
1296 output_open_api_file($spec_details->{DLL_NAME});
1297 output_api_header($comment);
1298 output_api_comment($comment);
1299 output_api_footer($comment);
1300 output_close_api_file();
1302 # Add this dll to the database of dll names
1303 my $output_file = $opt_output_directory."/dlls.db";
1305 # Append the dllname to the output db of names
1306 open(DLLDB,">>$output_file") || die "Couldn't create $output_file\n";
1307 print DLLDB $spec_details->{DLL_NAME},"\n";
1308 close(DLLDB);
1310 if ($opt_output_format eq "s")
1312 output_sgml_dll_file($spec_details);
1313 return;
1316 if ($opt_output_format eq "x")
1318 output_xml_dll_file($spec_details);
1319 return;
1325 # OUTPUT FUNCTIONS
1326 # ----------------
1327 # Only these functions know anything about formatting for a specific
1328 # output type. The functions above work only with plain text.
1329 # This is to allow new types of output to be added easily.
1331 # Open the api file
1332 sub output_open_api_file($)
1334 my $output_name = shift;
1335 $output_name = $opt_output_directory."/".$output_name;
1337 if ($opt_output_format eq "h")
1339 $output_name = $output_name.".html";
1341 elsif ($opt_output_format eq "s")
1343 $output_name = $output_name.".sgml";
1345 elsif ($opt_output_format eq "x")
1347 $output_name = $output_name.".xml";
1349 else
1351 $output_name = $output_name.".".$opt_manual_section;
1353 open(OUTPUT,">$output_name") || die "Couldn't create file '$output_name'\n";
1356 # Close the api file
1357 sub output_close_api_file()
1359 close (OUTPUT);
1362 # Output the api file header
1363 sub output_api_header($)
1365 my $comment = shift;
1367 if ($opt_output_format eq "h")
1369 print OUTPUT "<!-- Generated file - DO NOT EDIT! -->\n";
1370 print OUTPUT "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n";
1371 print OUTPUT "<HTML>\n<HEAD>\n";
1372 print OUTPUT "<LINK REL=\"StyleSheet\" href=\"apidoc.css\" type=\"text/css\">\n";
1373 print OUTPUT "<META NAME=\"GENERATOR\" CONTENT=\"tools/c2man.pl\">\n";
1374 print OUTPUT "<META NAME=\"keywords\" CONTENT=\"Win32,Wine,API,$comment->{COMMENT_NAME}\">\n";
1375 print OUTPUT "<TITLE>Wine API: $comment->{COMMENT_NAME}</TITLE>\n</HEAD>\n<BODY>\n";
1377 elsif ($opt_output_format eq "s" || $opt_output_format eq "x")
1379 print OUTPUT "<!-- Generated file - DO NOT EDIT! -->\n",
1380 "<sect1>\n",
1381 "<title>$comment->{COMMENT_NAME}</title>\n";
1383 else
1385 print OUTPUT ".\\\" -*- nroff -*-\n.\\\" Generated file - DO NOT EDIT!\n".
1386 ".TH ",$comment->{COMMENT_NAME}," ",$opt_manual_section," \"",$date,"\" \"".
1387 "Wine API\" \"Wine API\"\n";
1391 sub output_api_footer($)
1393 if ($opt_output_format eq "h")
1395 print OUTPUT "<hr><p><i class=\"copy\">Copyright &copy ".$year." The Wine Project.".
1396 " All trademarks are the property of their respective owners.".
1397 " Visit <a href=\"http://www.winehq.org\">WineHQ</a> for license details.".
1398 " Generated $date.</i></p>\n</body>\n</html>\n";
1400 elsif ($opt_output_format eq "s" || $opt_output_format eq "x")
1402 print OUTPUT "</sect1>\n";
1403 return;
1405 else
1410 sub output_api_section_start($$)
1412 my $comment = shift;
1413 my $section_name = shift;
1415 if ($opt_output_format eq "h")
1417 print OUTPUT "\n<h2 class=\"section\">",$section_name,"</h2>\n";
1419 elsif ($opt_output_format eq "s" || $opt_output_format eq "x")
1421 print OUTPUT "<bridgehead>",$section_name,"</bridgehead>\n";
1423 else
1425 print OUTPUT "\n\.SH ",$section_name,"\n";
1429 sub output_api_section_end()
1431 # Not currently required by any output formats
1434 sub output_api_name($)
1436 my $comment = shift;
1437 my $readable_name = $comment->{COMMENT_NAME};
1438 $readable_name =~ s/-/ /g; # make section names more readable
1440 output_api_section_start($comment,"NAME");
1443 my $dll_ordinal = "";
1444 if ($comment->{ORDINAL} ne "")
1446 $dll_ordinal = "(".$comment->{DLL_NAME}.".".$comment->{ORDINAL}.")";
1448 if ($opt_output_format eq "h")
1450 print OUTPUT "<p><b class=\"func_name\">",$readable_name,
1451 "</b>&nbsp;&nbsp;<i class=\"dll_ord\">",
1452 ,$dll_ordinal,"</i></p>\n";
1454 elsif ($opt_output_format eq "s" || $opt_output_format eq "x")
1456 print OUTPUT "<para>\n <command>",$readable_name,"</command> <emphasis>",
1457 $dll_ordinal,"</emphasis>\n</para>\n";
1459 else
1461 print OUTPUT "\\fB",$readable_name,"\\fR ",$dll_ordinal;
1464 output_api_section_end();
1467 sub output_api_synopsis($)
1469 my $comment = shift;
1470 my @fmt;
1472 output_api_section_start($comment,"SYNOPSIS");
1474 if ($opt_output_format eq "h")
1476 print OUTPUT "<pre class=\"proto\">\n ", $comment->{RETURNS}," ",$comment->{COMMENT_NAME},"\n (\n";
1477 @fmt = ("", "\n", "<tt class=\"param\">", "</tt>");
1479 elsif ($opt_output_format eq "s" || $opt_output_format eq "x")
1481 print OUTPUT "<screen>\n ",$comment->{RETURNS}," ",$comment->{COMMENT_NAME},"\n (\n";
1482 @fmt = ("", "\n", "<emphasis>", "</emphasis>");
1484 else
1486 print OUTPUT $comment->{RETURNS}," ",$comment->{COMMENT_NAME},"\n (\n";
1487 @fmt = ("", "\n", "\\fI", "\\fR");
1490 # Since our prototype is output in a pre-formatted block, line up the
1491 # parameters and parameter comments in the same column.
1493 # First calculate where the columns should start
1494 my $biggest_length = 0;
1495 for(my $i=0; $i < @{$comment->{PROTOTYPE}}; $i++)
1497 my $line = ${$comment->{PROTOTYPE}}[$i];
1498 if ($line =~ /(.+?)([A-Za-z_][A-Za-z_0-9]*)$/)
1500 my $length = length $1;
1501 if ($length > $biggest_length)
1503 $biggest_length = $length;
1508 # Now pad the string with blanks
1509 for(my $i=0; $i < @{$comment->{PROTOTYPE}}; $i++)
1511 my $line = ${$comment->{PROTOTYPE}}[$i];
1512 if ($line =~ /(.+?)([A-Za-z_][A-Za-z_0-9]*)$/)
1514 my $pad_len = $biggest_length - length $1;
1515 my $padding = " " x ($pad_len);
1516 ${$comment->{PROTOTYPE}}[$i] = $1.$padding.$2;
1520 for(my $i=0; $i < @{$comment->{PROTOTYPE}}; $i++)
1522 # Format the parameter name
1523 my $line = ${$comment->{PROTOTYPE}}[$i];
1524 my $comma = ($i == @{$comment->{PROTOTYPE}}-1) ? "" : ",";
1525 $line =~ s/(.+?)([A-Za-z_][A-Za-z_0-9]*)$/ $fmt[0]$1$fmt[2]$2$fmt[3]$comma$fmt[1]/;
1526 print OUTPUT $line;
1529 if ($opt_output_format eq "h")
1531 print OUTPUT " )\n</pre>\n";
1533 elsif ($opt_output_format eq "s" || $opt_output_format eq "x")
1535 print OUTPUT " )\n</screen>\n";
1537 else
1539 print OUTPUT " )\n";
1542 output_api_section_end();
1545 sub output_api_comment($)
1547 my $comment = shift;
1548 my $open_paragraph = 0;
1549 my $open_raw = 0;
1550 my $param_docs = 0;
1551 my @fmt;
1553 if ($opt_output_format eq "h")
1555 @fmt = ("<p>", "</p>\n", "<tt class=\"const\">", "</tt>", "<b class=\"emp\">", "</b>",
1556 "<tt class=\"coderef\">", "</tt>", "<tt class=\"param\">", "</tt>",
1557 "<i class=\"in_out\">", "</i>", "<pre class=\"raw\">\n", "</pre>\n",
1558 "<table class=\"tab\"><colgroup><col><col><col></colgroup><tbody>\n",
1559 "</tbody></table>\n","<tr><td>","</td></tr>\n","</td>","</td><td>");
1561 elsif ($opt_output_format eq "s" || $opt_output_format eq "x")
1563 @fmt = ("<para>\n","\n</para>\n","<constant>","</constant>","<emphasis>","</emphasis>",
1564 "<command>","</command>","<constant>","</constant>","<emphasis>","</emphasis>",
1565 "<screen>\n","</screen>\n",
1566 "<informaltable frame=\"none\">\n<tgroup cols=\"3\">\n<tbody>\n",
1567 "</tbody>\n</tgroup>\n</informaltable>\n","<row><entry>","</entry></row>\n",
1568 "</entry>","</entry><entry>");
1570 else
1572 @fmt = ("\.PP\n", "\n", "\\fB", "\\fR", "\\fB", "\\fR", "\\fB", "\\fR", "\\fI", "\\fR",
1573 "\\fB", "\\fR ", "", "", "", "","","\n.PP\n","","");
1576 # Extract the parameter names
1577 my @parameter_names;
1578 for (@{$comment->{PROTOTYPE}})
1580 if ( /(.+?)([A-Za-z_][A-Za-z_0-9]*)$/ )
1582 push (@parameter_names, $2);
1586 for (@{$comment->{TEXT}})
1588 if ($opt_output_format eq "h" || $opt_output_format eq "s" || $opt_output_format eq "x")
1590 # Map special characters
1591 s/\&/\&amp;/g;
1592 s/\</\&lt;/g;
1593 s/\>/\&gt;/g;
1594 s/\([Cc]\)/\&copy;/g;
1595 s/\(tm\)/&#174;/;
1598 if ( s/^\|// )
1600 # Raw output
1601 if ($open_raw == 0)
1603 if ($open_paragraph == 1)
1605 # Close the open paragraph
1606 print OUTPUT $fmt[1];
1607 $open_paragraph = 0;
1609 # Start raw output
1610 print OUTPUT $fmt[12];
1611 $open_raw = 1;
1613 if ($opt_output_format eq "")
1615 print OUTPUT ".br\n"; # Prevent 'man' running these lines together
1617 print OUTPUT $_,"\n";
1619 else
1621 if ($opt_output_format eq "h")
1623 # Link to the file in WineHQ cvs
1624 s/^(Implemented in \")(.+?)(\"\.)/$1$2$3 http:\/\/source.winehq.org\/source\/$2/g;
1625 s/^(Declared in \")(.+?)(\"\.)/$1$2$3 http:\/\/source.winehq.org\/source\/include\/$2/g;
1627 # Highlight strings
1628 s/(\".+?\")/$fmt[2]$1$fmt[3]/g;
1629 # Highlight literal chars
1630 s/(\'.\')/$fmt[2]$1$fmt[3]/g;
1631 s/(\'.{2}\')/$fmt[2]$1$fmt[3]/g;
1632 # Highlight numeric constants
1633 s/( |\-|\+|\.|\()([0-9\-\.]+)( |\-|$|\.|\,|\*|\?|\))/$1$fmt[2]$2$fmt[3]$3/g;
1635 # Leading cases ("xxxx:","-") start new paragraphs & are emphasised
1636 # FIXME: Using bullet points for leading '-' would look nicer.
1637 if ($open_paragraph == 1 && $param_docs == 0)
1639 s/^(\-)/$fmt[1]$fmt[0]$fmt[4]$1$fmt[5]/;
1640 s/^([[A-Za-z\-]+\:)/$fmt[1]$fmt[0]$fmt[4]$1$fmt[5]/;
1642 else
1644 s/^(\-)/$fmt[4]$1$fmt[5]/;
1645 s/^([[A-Za-z\-]+\:)/$fmt[4]$1$fmt[5]/;
1648 if ($opt_output_format eq "h")
1650 # Html uses links for API calls
1651 while ( /([A-Za-z_]+[A-Za-z_0-9-]+)(\(\))/)
1653 my $link = $1;
1654 my $readable_link = $1;
1655 $readable_link =~ s/-/ /g;
1657 s/([A-Za-z_]+[A-Za-z_0-9-]+)(\(\))/<a href\=\"$link\.html\">$readable_link<\/a>/;
1659 # Index references
1660 s/\{\{(.*?)\}\}\{\{(.*?)\}\}/<a href\=\"$2\.html\">$1<\/a>/g;
1661 s/ ([A-Z_])(\(\))/<a href\=\"$1\.html\">$1<\/a>/g;
1662 # And references to COM objects (hey, they'll get documented one day)
1663 s/ (I[A-Z]{1}[A-Za-z0-9_]+) (Object|object|Interface|interface)/ <a href\=\"$1\.html\">$1<\/a> $2/g;
1664 # Convert any web addresses to real links
1665 s/(http\:\/\/)(.+?)($| )/<a href\=\"$1$2\">$2<\/a>$3/g;
1667 else
1669 if ($opt_output_format eq "")
1671 # Give the man section for API calls
1672 s/ ([A-Za-z_]+[A-Za-z_0-9-]+)\(\)/ $fmt[6]$1\($opt_manual_section\)$fmt[7]/g;
1674 else
1676 # Highlight API calls
1677 s/ ([A-Za-z_]+[A-Za-z_0-9-]+\(\))/ $fmt[6]$1$fmt[7]/g;
1680 # And references to COM objects
1681 s/ (I[A-Z]{1}[A-Za-z0-9_]+) (Object|object|Interface|interface)/ $fmt[6]$1$fmt[7] $2/g;
1684 if ($open_raw == 1)
1686 # Finish the raw output
1687 print OUTPUT $fmt[13];
1688 $open_raw = 0;
1691 if ( /^[A-Z]+$/ || /^SEE ALSO$/ )
1693 # Start of a new section
1694 if ($open_paragraph == 1)
1696 if ($param_docs == 1)
1698 print OUTPUT $fmt[17],$fmt[15];
1699 $param_docs = 0;
1701 else
1703 print OUTPUT $fmt[1];
1705 $open_paragraph = 0;
1707 output_api_section_start($comment,$_);
1708 if ( /^PARAMS$/ || /^MEMBERS$/ )
1710 print OUTPUT $fmt[14];
1711 $param_docs = 1;
1713 else
1715 #print OUTPUT $fmt[15];
1716 #$param_docs = 0;
1719 elsif ( /^$/ )
1721 # Empty line, indicating a new paragraph
1722 if ($open_paragraph == 1)
1724 if ($param_docs == 0)
1726 print OUTPUT $fmt[1];
1727 $open_paragraph = 0;
1731 else
1733 if ($param_docs == 1)
1735 if ($open_paragraph == 1)
1737 # For parameter docs, put each parameter into a new paragraph/table row
1738 print OUTPUT $fmt[17];
1739 $open_paragraph = 0;
1741 s/(\[.+\])( *)/$fmt[19]$fmt[10]$1$fmt[11]$fmt[19] /; # Format In/Out
1743 else
1745 # Within paragraph lines, prevent lines running together
1746 $_ = $_." ";
1749 # Format parameter names where they appear in the comment
1750 for my $parameter_name (@parameter_names)
1752 s/(^|[ \.\,\(\-\*])($parameter_name)($|[ \.\)\,\-\/]|(\=[^"]))/$1$fmt[8]$2$fmt[9]$3/g;
1754 # Structure dereferences include the dereferenced member
1755 s/(\-\>[A-Za-z_]+)/$fmt[8]$1$fmt[9]/g;
1756 s/(\-\&gt\;[A-Za-z_]+)/$fmt[8]$1$fmt[9]/g;
1758 if ($open_paragraph == 0)
1760 if ($param_docs == 1)
1762 print OUTPUT $fmt[16];
1764 else
1766 print OUTPUT $fmt[0];
1768 $open_paragraph = 1;
1770 # Anything in all uppercase on its own gets emphasised
1771 s/(^|[ \.\,\(\[\|\=])([A-Z]+?[A-Z0-9_]+)($|[ \.\,\*\?\|\)\=\'])/$1$fmt[6]$2$fmt[7]$3/g;
1773 print OUTPUT $_;
1777 if ($open_raw == 1)
1779 print OUTPUT $fmt[13];
1781 if ($param_docs == 1 && $open_paragraph == 1)
1783 print OUTPUT $fmt[17];
1784 $open_paragraph = 0;
1786 if ($param_docs == 1)
1788 print OUTPUT $fmt[15];
1790 if ($open_paragraph == 1)
1792 print OUTPUT $fmt[1];
1796 # Create the master index file
1797 sub output_master_index_files()
1799 if ($opt_output_format eq "")
1801 return; # No master index for man pages
1804 if ($opt_output_format eq "h")
1806 # Append the index entries to the output db of index entries
1807 my $output_file = $opt_output_directory."/index.db";
1808 open(INDEXDB,">>$output_file") || die "Couldn't create $output_file\n";
1809 for (@index_entries_list)
1811 $_ =~ s/A\,/\,/;
1812 print INDEXDB $_."\n";
1814 close(INDEXDB);
1817 # Use the comment output functions for consistency
1818 my $comment =
1820 FILE => "",
1821 COMMENT_NAME => "The Wine API Guide",
1822 ALT_NAME => "The Wine API Guide",
1823 DLL_NAME => "",
1824 ORDINAL => "",
1825 RETURNS => "",
1826 PROTOTYPE => [],
1827 TEXT => [],
1830 if ($opt_output_format eq "s" || $opt_output_format eq "x")
1832 $comment->{COMMENT_NAME} = "Introduction";
1833 $comment->{ALT_NAME} = "Introduction",
1835 elsif ($opt_output_format eq "h")
1837 @{$comment->{TEXT}} = (
1838 "NAME",
1839 $comment->{COMMENT_NAME},
1840 "INTRODUCTION",
1844 # Create the initial comment text
1845 push (@{$comment->{TEXT}},
1846 "This document describes the API calls made available",
1847 "by Wine. They are grouped by the dll that exports them.",
1849 "Please do not edit this document, since it is generated automatically",
1850 "from the Wine source code tree. Details on updating this documentation",
1851 "are given in the \"Wine Developers Guide\".",
1852 "CONTRIBUTORS",
1853 "API documentation is generally written by the person who ",
1854 "implements a given API call. Authors of each dll are listed in the overview ",
1855 "section for that dll. Additional contributors who have updated source files ",
1856 "but have not entered their names in a copyright statement are noted by an ",
1857 "entry in the git commit logs.",
1861 # Read in all dlls from the database of dll names
1862 my $input_file = $opt_output_directory."/dlls.db";
1863 my @dlls = `cat $input_file|sort|uniq`;
1865 if ($opt_output_format eq "h")
1867 # HTML gets a list of all the dlls and an index. For docbook the index creates this for us
1868 push (@{$comment->{TEXT}},
1869 "INDEX",
1870 "For an alphabetical listing of the functions available, please click the ",
1871 "first letter of the functions name below:","",
1872 "[ _(), A(), B(), C(), D(), E(), F(), G(), H(), ".
1873 "I(), J(), K(), L(), M(), N(), O(), P(), Q(), ".
1874 "R(), S(), T(), U(), V(), W(), X(), Y(), Z() ]", "",
1875 "DLLS",
1876 "Each dll provided by Wine is documented individually. The following dlls are provided :",
1879 # Add the dlls to the comment
1880 for (@dlls)
1882 $_ =~ s/(\..*)?\n/\(\)/;
1883 push (@{$comment->{TEXT}}, $_, "");
1885 output_open_api_file("index");
1887 elsif ($opt_output_format eq "s" || $opt_output_format eq "x")
1889 # Just write this as the initial blurb, with a chapter heading
1890 output_open_api_file("blurb");
1891 print OUTPUT "<chapter id =\"blurb\">\n<title>Introduction to The Wine API Guide</title>\n"
1894 # Write out the document
1895 output_api_header($comment);
1896 output_api_comment($comment);
1897 output_api_footer($comment);
1898 if ($opt_output_format eq "s" || $opt_output_format eq "x")
1900 print OUTPUT "</chapter>\n" # finish the chapter
1902 output_close_api_file();
1904 if ($opt_output_format eq "s")
1906 output_sgml_master_file(\@dlls);
1907 return;
1909 if ($opt_output_format eq "x")
1911 output_xml_master_file(\@dlls);
1912 return;
1914 if ($opt_output_format eq "h")
1916 output_html_index_files();
1917 output_html_stylesheet();
1918 return;
1922 # Write the master wine-api.xml, linking it to each dll.
1923 sub output_xml_master_file($)
1925 my $dlls = shift;
1927 output_open_api_file("wine-api");
1928 print OUTPUT "<?xml version='1.0'?>";
1929 print OUTPUT "<!-- Generated file - DO NOT EDIT! -->\n";
1930 print OUTPUT "<!DOCTYPE book PUBLIC \"-//OASIS//DTD DocBook V5.0/EN\" ";
1931 print OUTPUT " \"http://www.docbook.org/xml/5.0/dtd/docbook.dtd\" [\n\n";
1932 print OUTPUT "<!ENTITY blurb SYSTEM \"blurb.xml\">\n";
1934 # List the entities
1935 for (@$dlls)
1937 $_ =~ s/(\..*)?\n//;
1938 print OUTPUT "<!ENTITY ",$_," SYSTEM \"",$_,".xml\">\n"
1941 print OUTPUT "]>\n\n<book id=\"index\">\n<bookinfo><title>The Wine API Guide</title></bookinfo>\n\n";
1942 print OUTPUT " &blurb;\n";
1944 for (@$dlls)
1946 print OUTPUT " &",$_,";\n"
1948 print OUTPUT "\n\n</book>\n";
1950 output_close_api_file();
1953 # Write the master wine-api.sgml, linking it to each dll.
1954 sub output_sgml_master_file($)
1956 my $dlls = shift;
1958 output_open_api_file("wine-api");
1959 print OUTPUT "<!-- Generated file - DO NOT EDIT! -->\n";
1960 print OUTPUT "<!doctype book PUBLIC \"-//OASIS//DTD DocBook V3.1//EN\" [\n\n";
1961 print OUTPUT "<!entity blurb SYSTEM \"blurb.sgml\">\n";
1963 # List the entities
1964 for (@$dlls)
1966 $_ =~ s/(\..*)?\n//;
1967 print OUTPUT "<!entity ",$_," SYSTEM \"",$_,".sgml\">\n"
1970 print OUTPUT "]>\n\n<book id=\"index\">\n<bookinfo><title>The Wine API Guide</title></bookinfo>\n\n";
1971 print OUTPUT " &blurb;\n";
1973 for (@$dlls)
1975 print OUTPUT " &",$_,";\n"
1977 print OUTPUT "\n\n</book>\n";
1979 output_close_api_file();
1982 # Produce the sgml for the dll chapter from the generated files
1983 sub output_sgml_dll_file($)
1985 my $spec_details = shift;
1987 # Make a list of all the documentation files to include
1988 my $exports = $spec_details->{EXPORTS};
1989 my @source_files = ();
1990 for (@$exports)
1992 # @$_ => ordinal, call convention, exported name, implementation name, documented;
1993 if (@$_[$EXPORT_CALL] ne "forward" && @$_[$EXPORT_CALL] ne "extern" &&
1994 @$_[$EXPORT_CALL] ne "stub" && @$_[$EXPORT_CALL] ne "equate" &&
1995 @$_[$EXPORT_CALL] ne "variable" && @$_[$EXPORT_CALL] ne "fake" &&
1996 @$_[$EXPORT_FLAGS] & $FLAG_DOCUMENTED)
1998 # A documented function
1999 push (@source_files,@$_[$EXPORT_IMPNAME]);
2003 push (@source_files,@{$spec_details->{EXTRA_COMMENTS}});
2005 @source_files = sort @source_files;
2007 # create a new chapter for this dll
2008 my $tmp_name = $opt_output_directory."/".$spec_details->{DLL_NAME}.".tmp";
2009 open(OUTPUT,">$tmp_name") || die "Couldn't create $tmp_name\n";
2010 print OUTPUT "<chapter>\n<title>$spec_details->{DLL_NAME}</title>\n";
2011 output_close_api_file();
2013 # Add the sorted documentation, cleaning up as we go
2014 `cat $opt_output_directory/$spec_details->{DLL_NAME}.sgml >>$tmp_name`;
2015 for (@source_files)
2017 `cat $opt_output_directory/$_.sgml >>$tmp_name`;
2018 `rm -f $opt_output_directory/$_.sgml`;
2021 # close the chapter, and overwrite the dll source
2022 open(OUTPUT,">>$tmp_name") || die "Couldn't create $tmp_name\n";
2023 print OUTPUT "</chapter>\n";
2024 close OUTPUT;
2025 `mv $tmp_name $opt_output_directory/$spec_details->{DLL_NAME}.sgml`;
2028 # Produce the xml for the dll chapter from the generated files
2029 sub output_xml_dll_file($)
2031 my $spec_details = shift;
2033 # Make a list of all the documentation files to include
2034 my $exports = $spec_details->{EXPORTS};
2035 my @source_files = ();
2036 for (@$exports)
2038 # @$_ => ordinal, call convention, exported name, implementation name, documented;
2039 if (@$_[$EXPORT_CALL] ne "forward" && @$_[$EXPORT_CALL] ne "extern" &&
2040 @$_[$EXPORT_CALL] ne "stub" && @$_[$EXPORT_CALL] ne "equate" &&
2041 @$_[$EXPORT_CALL] ne "variable" && @$_[$EXPORT_CALL] ne "fake" &&
2042 @$_[$EXPORT_FLAGS] & $FLAG_DOCUMENTED)
2044 # A documented function
2045 push (@source_files,@$_[$EXPORT_IMPNAME]);
2049 push (@source_files,@{$spec_details->{EXTRA_COMMENTS}});
2051 @source_files = sort @source_files;
2053 # create a new chapter for this dll
2054 my $tmp_name = $opt_output_directory."/".$spec_details->{DLL_NAME}.".tmp";
2055 open(OUTPUT,">$tmp_name") || die "Couldn't create $tmp_name\n";
2056 print OUTPUT "<?xml version='1.0' encoding='UTF-8'?>\n<chapter>\n<title>$spec_details->{DLL_NAME}</title>\n";
2057 output_close_api_file();
2059 # Add the sorted documentation, cleaning up as we go
2060 `cat $opt_output_directory/$spec_details->{DLL_NAME}.xml >>$tmp_name`;
2061 for (@source_files)
2063 `cat $opt_output_directory/$_.xml >>$tmp_name`;
2064 `rm -f $opt_output_directory/$_.xml`;
2067 # close the chapter, and overwrite the dll source
2068 open(OUTPUT,">>$tmp_name") || die "Couldn't create $tmp_name\n";
2069 print OUTPUT "</chapter>\n";
2070 close OUTPUT;
2071 `mv $tmp_name $opt_output_directory/$spec_details->{DLL_NAME}.xml`;
2074 # Write the html index files containing the function names
2075 sub output_html_index_files()
2077 if ($opt_output_format ne "h")
2079 return;
2082 my @letters = ('_', 'A' .. 'Z');
2084 # Read in all functions
2085 my $input_file = $opt_output_directory."/index.db";
2086 my @funcs = `cat $input_file|sort|uniq`;
2088 for (@letters)
2090 my $letter = $_;
2091 my $comment =
2093 FILE => "",
2094 COMMENT_NAME => "",
2095 ALT_NAME => "",
2096 DLL_NAME => "",
2097 ORDINAL => "",
2098 RETURNS => "",
2099 PROTOTYPE => [],
2100 TEXT => [],
2103 $comment->{COMMENT_NAME} = $letter." Functions";
2104 $comment->{ALT_NAME} = $letter." Functions";
2106 push (@{$comment->{TEXT}},
2107 "NAME",
2108 $comment->{COMMENT_NAME},
2109 "FUNCTIONS"
2112 # Add the functions to the comment
2113 for (@funcs)
2115 my $first_char = substr ($_, 0, 1);
2116 $first_char = uc $first_char;
2118 if ($first_char eq $letter)
2120 my $name = $_;
2121 my $file;
2122 $name =~ s/(^.*?)\,(.*?)\n/$1/;
2123 $file = $2;
2124 push (@{$comment->{TEXT}}, "{{".$name."}}{{".$file."}}","");
2128 # Write out the document
2129 output_open_api_file($letter);
2130 output_api_header($comment);
2131 output_api_comment($comment);
2132 output_api_footer($comment);
2133 output_close_api_file();
2137 # Output the stylesheet for HTML output
2138 sub output_html_stylesheet()
2140 if ($opt_output_format ne "h")
2142 return;
2145 my $css;
2146 ($css = <<HERE_TARGET) =~ s/^\s+//gm;
2148 * Default styles for Wine HTML Documentation.
2150 * This style sheet should be altered to suit your needs/taste.
2152 BODY { /* Page body */
2153 background-color: white;
2154 color: black;
2155 font-family: Tahoma,sans-serif;
2156 font-style: normal;
2157 font-size: 10pt;
2159 a:link { color: #4444ff; } /* Links */
2160 a:visited { color: #333377 }
2161 a:active { color: #0000dd }
2162 H2.section { /* Section Headers */
2163 font-family: sans-serif;
2164 color: #777777;
2165 background-color: #F0F0FE;
2166 margin-left: 0.2in;
2167 margin-right: 1.0in;
2169 b.func_name { /* Function Name */
2170 font-size: 10pt;
2171 font-style: bold;
2173 i.dll_ord { /* Italicised DLL+ordinal */
2174 color: #888888;
2175 font-family: sans-serif;
2176 font-size: 8pt;
2178 p { /* Paragraphs */
2179 margin-left: 0.5in;
2180 margin-right: 0.5in;
2182 table { /* tables */
2183 margin-left: 0.5in;
2184 margin-right: 0.5in;
2186 pre.proto /* API Function prototype */
2188 border-style: solid;
2189 border-width: 1px;
2190 border-color: #777777;
2191 background-color: #F0F0BB;
2192 color: black;
2193 font-size: 10pt;
2194 vertical-align: top;
2195 margin-left: 0.5in;
2196 margin-right: 1.0in;
2198 pre.raw { /* Raw text output */
2199 margin-left: 0.6in;
2200 margin-right: 1.1in;
2201 background-color: #8080DC;
2203 tt.param { /* Parameter name */
2204 font-style: italic;
2205 color: blue;
2207 tt.const { /* Constant */
2208 color: red;
2210 i.in_out { /* In/Out */
2211 font-size: 8pt;
2212 color: grey;
2214 tt.coderef { /* Code in description text */
2215 color: darkgreen;
2217 b.emp /* Emphasis */ {
2218 font-style: bold;
2219 color: darkblue;
2221 i.footer { /* Footer */
2222 font-family: sans-serif;
2223 font-size: 6pt;
2224 color: darkgrey;
2226 HERE_TARGET
2228 my $output_file = "$opt_output_directory/apidoc.css";
2229 open(CSS,">$output_file") || die "Couldn't create the file $output_file\n";
2230 print CSS $css;
2231 close(CSS);
2235 sub usage()
2237 print "\nCreate API Documentation from Wine source code.\n\n",
2238 "Usage: c2man.pl [options] {-w <spec>} {-I <include>} {<source>}\n",
2239 "Where: <spec> is a .spec file giving a DLL's exports.\n",
2240 " <include> is an include directory used by the DLL.\n",
2241 " <source> is a source file of the DLL.\n",
2242 " The above can be given multiple times on the command line, as appropriate.\n",
2243 "Options:\n",
2244 " -Th : Output HTML instead of a man page\n",
2245 " -Ts : Output SGML (DocBook source) instead of a man page\n",
2246 " -C <dir> : Source directory, to find source files if they are not found in the\n",
2247 " current directory. Default is \"",$opt_source_dir,"\"\n",
2248 " -P <dir> : Parent source directory.\n",
2249 " -R <dir> : Root of build directory.\n",
2250 " -o <dir> : Create output in <dir>, default is \"",$opt_output_directory,"\"\n",
2251 " -s <sect>: Set manual section to <sect>, default is ",$opt_manual_section,"\n",
2252 " -e : Output \"FIXME\" documentation from empty comments.\n",
2253 " -v : Verbosity. Can be given more than once for more detail.\n";
2258 # Main
2261 # Print usage if we're called with no args
2262 if( @ARGV == 0)
2264 usage();
2267 # Process command line options
2268 while(defined($_ = shift @ARGV))
2270 if( s/^-// )
2272 # An option.
2273 for ($_)
2275 /^o$/ && do { $opt_output_directory = shift @ARGV; last; };
2276 s/^S// && do { $opt_manual_section = $_; last; };
2277 /^Th$/ && do { $opt_output_format = "h"; last; };
2278 /^Ts$/ && do { $opt_output_format = "s"; last; };
2279 /^Tx$/ && do { $opt_output_format = "x"; last; };
2280 /^v$/ && do { $opt_verbose++; last; };
2281 /^e$/ && do { $opt_output_empty = 1; last; };
2282 /^L$/ && do { last; };
2283 /^w$/ && do { @opt_spec_file_list = (@opt_spec_file_list, shift @ARGV); last; };
2284 s/^I// && do { if ($_ ne ".") {
2285 foreach my $include (`find $_/./ -type d ! -name tests`) {
2286 $include =~ s/\n//;
2287 $include = $include."/*.h";
2288 $include =~ s/\/\//\//g;
2289 my $have_headers = `ls $include >/dev/null 2>&1`;
2290 if ($? >> 8 == 0) { @opt_header_file_list = (@opt_header_file_list, $include); }
2293 last;
2295 s/^C// && do {
2296 if ($_ ne "") { $opt_source_dir = $_; }
2297 last;
2299 s/^P// && do {
2300 if ($_ ne "") { $opt_parent_dir = $_; }
2301 last;
2303 s/^R// && do { if ($_ =~ /^\//) { $opt_wine_root_dir = $_; }
2304 else { $opt_wine_root_dir = `cd $pwd/$_ && pwd`; }
2305 $opt_wine_root_dir =~ s/\n//;
2306 $opt_wine_root_dir =~ s/\/\//\//g;
2307 if (! $opt_wine_root_dir =~ /\/$/ ) { $opt_wine_root_dir = $opt_wine_root_dir."/"; };
2308 last;
2310 die "Unrecognised option $_\n";
2313 else
2315 # A source file.
2316 push (@opt_source_file_list, $_);
2320 # Remove duplicate include directories
2321 my %htmp;
2322 @opt_header_file_list = grep(!$htmp{$_}++, @opt_header_file_list);
2324 if ($opt_verbose > 3)
2326 print "Output dir:'".$opt_output_directory."'\n";
2327 print "Section :'".$opt_manual_section."'\n";
2328 print "Format :'".$opt_output_format."'\n";
2329 print "Source dir:'".$opt_source_dir."'\n";
2330 print "Root :'".$opt_wine_root_dir."'\n";
2331 print "Spec files:'@opt_spec_file_list'\n";
2332 print "Includes :'@opt_header_file_list'\n";
2333 print "Sources :'@opt_source_file_list'\n";
2336 if (@opt_spec_file_list == 0)
2338 exit 0; # Don't bother processing non-dll files
2341 # Make sure the output directory exists
2342 unless (-d $opt_output_directory)
2344 mkdir $opt_output_directory or die "Cannot create directory $opt_output_directory\n";
2347 # Read in each .spec files exports and other details
2348 while(my $spec_file = shift @opt_spec_file_list)
2350 process_spec_file($spec_file);
2353 if ($opt_verbose > 3)
2355 foreach my $spec_file ( keys %spec_files )
2357 print "in '$spec_file':\n";
2358 my $spec_details = $spec_files{$spec_file}[0];
2359 my $exports = $spec_details->{EXPORTS};
2360 for (@$exports)
2362 print @$_[$EXPORT_ORDINAL].",".@$_[$EXPORT_CALL].", ".
2363 @$_[$EXPORT_EXPNAME].",".@$_[$EXPORT_IMPNAME]."\n";
2368 # Extract and output the comments from each source file
2369 while(defined($_ = shift @opt_source_file_list))
2371 process_source_file($_);
2374 # Write the index files for each spec
2375 process_index_files();
2377 # Write the master index file
2378 output_master_index_files();
2380 exit 0;