wpp: Add explicit casts when mixing signed and unsigned integers.
[wine/wine-gecko.git] / tools / c2man.pl
blobdb9f25f5226a5fccc8f60f98fc308d719a98e6b5
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
42 # Options
43 my $opt_output_directory = "man3w"; # All default options are for nroff (man pages)
44 my $opt_manual_section = "3w";
45 my $opt_source_dir = "";
46 my $opt_wine_root_dir = "";
47 my $opt_output_format = ""; # '' = nroff, 'h' = html, 's' = sgml, 'x' = xml
48 my $opt_output_empty = 0; # Non-zero = Create 'empty' comments (for every implemented function)
49 my $opt_fussy = 1; # Non-zero = Create only if we have a RETURNS section
50 my $opt_verbose = 0; # >0 = verbosity. Can be given multiple times (for debugging)
51 my @opt_header_file_list = ();
52 my @opt_spec_file_list = ();
53 my @opt_source_file_list = ();
55 # All the collected details about all the .spec files being processed
56 my %spec_files;
57 # All the collected details about all the source files being processed
58 my %source_files;
59 # All documented functions that are to be placed in the index
60 my @index_entries_list = ();
62 # useful globals
63 my $pwd = `pwd`."/";
64 $pwd =~ s/\n//;
65 my @datetime = localtime;
66 my @months = ( "Jan", "Feb", "Mar", "Apr", "May", "Jun",
67 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" );
68 my $year = $datetime[5] + 1900;
69 my $date = "$months[$datetime[4]] $year";
72 sub output_api_comment($);
73 sub output_api_footer($);
74 sub output_api_header($);
75 sub output_api_name($);
76 sub output_api_synopsis($);
77 sub output_close_api_file();
78 sub output_comment($);
79 sub output_html_index_files();
80 sub output_html_stylesheet();
81 sub output_open_api_file($);
82 sub output_sgml_dll_file($);
83 sub output_xml_dll_file($);
84 sub output_sgml_master_file($);
85 sub output_xml_master_file($);
86 sub output_spec($);
87 sub process_comment($);
88 sub process_extra_comment($);
91 # Generate the list of exported entries for the dll
92 sub process_spec_file($)
94 my $spec_name = shift;
95 my ($dll_name, $dll_ext) = split(/\./, $spec_name);
96 $dll_ext = "dll" if ( $dll_ext eq "spec" );
97 my $uc_dll_name = uc $dll_name;
99 my $spec_details =
101 NAME => $spec_name,
102 DLL_NAME => $dll_name,
103 DLL_EXT => $dll_ext,
104 NUM_EXPORTS => 0,
105 NUM_STUBS => 0,
106 NUM_FUNCS => 0,
107 NUM_FORWARDS => 0,
108 NUM_VARS => 0,
109 NUM_DOCS => 0,
110 CONTRIBUTORS => [ ],
111 SOURCES => [ ],
112 DESCRIPTION => [ ],
113 EXPORTS => [ ],
114 EXPORTED_NAMES => { },
115 IMPLEMENTATION_NAMES => { },
116 EXTRA_COMMENTS => [ ],
117 CURRENT_EXTRA => [ ] ,
120 if ($opt_verbose > 0)
122 print "Processing ".$spec_name."\n";
125 # We allow opening to fail just to cater for the peculiarities of
126 # the Wine build system. This doesn't hurt, in any case
127 open(SPEC_FILE, "<$spec_name")
128 || (($opt_source_dir ne "")
129 && open(SPEC_FILE, "<$opt_source_dir/$spec_name"))
130 || return;
132 while(<SPEC_FILE>)
134 s/^\s+//; # Strip leading space
135 s/\s+\n$/\n/; # Strip trailing space
136 s/\s+/ /g; # Strip multiple tabs & spaces to a single space
137 s/\s*#.*//; # Strip comments
138 s/\(.*\)/ /; # Strip arguments
139 s/\s+/ /g; # Strip multiple tabs & spaces to a single space (again)
140 s/\n$//; # Strip newline
142 my $flags = 0;
143 if( /\-noname/ )
145 $flags |= $FLAG_NONAME;
147 if( /\-i386/ )
149 $flags |= $FLAG_I386;
151 if( /\-register/ )
153 $flags |= $FLAG_REGISTER;
155 s/ \-[a-z0-9]+//g; # Strip flags
157 if( /^(([0-9]+)|@) / )
159 # This line contains an exported symbol
160 my ($ordinal, $call_convention, $exported_name, $implementation_name) = split(' ');
162 for ($call_convention)
164 /^(cdecl|stdcall|varargs|pascal)$/
165 && do { $spec_details->{NUM_FUNCS}++; last; };
166 /^(variable|equate)$/
167 && do { $spec_details->{NUM_VARS}++; last; };
168 /^(extern)$/
169 && do { $spec_details->{NUM_FORWARDS}++; last; };
170 /^stub$/ && do { $spec_details->{NUM_STUBS}++; last; };
171 if ($opt_verbose > 0)
173 print "Warning: didn't recognise convention \'",$call_convention,"'\n";
175 last;
178 # Convert ordinal only names so we can find them later
179 if ($exported_name eq "@")
181 $exported_name = $uc_dll_name.'_'.$ordinal;
183 if (!defined($implementation_name))
185 $implementation_name = $exported_name;
187 if ($implementation_name eq "")
189 $implementation_name = $exported_name;
192 if ($implementation_name =~ /(.*?)\./)
194 $call_convention = "forward"; # Referencing a function from another dll
195 $spec_details->{NUM_FUNCS}--;
196 $spec_details->{NUM_FORWARDS}++;
199 # Add indices for the exported and implementation names
200 $spec_details->{EXPORTED_NAMES}{$exported_name} = $spec_details->{NUM_EXPORTS};
201 if ($implementation_name ne $exported_name)
203 $spec_details->{IMPLEMENTATION_NAMES}{$exported_name} = $spec_details->{NUM_EXPORTS};
206 # Add the exported entry
207 $spec_details->{NUM_EXPORTS}++;
208 my @export = ($ordinal, $call_convention, $exported_name, $implementation_name, $flags);
209 push (@{$spec_details->{EXPORTS}},[@export]);
212 close(SPEC_FILE);
214 # Add this .spec files details to the list of .spec files
215 $spec_files{$uc_dll_name} = [$spec_details];
218 # Read each source file, extract comments, and generate API documentation if appropriate
219 sub process_source_file($)
221 my $source_file = shift;
222 my $source_details =
224 CONTRIBUTORS => [ ],
225 DEBUG_CHANNEL => "",
227 my $comment =
229 FILE => $source_file,
230 COMMENT_NAME => "",
231 ALT_NAME => "",
232 DLL_NAME => "",
233 DLL_EXT => "",
234 ORDINAL => "",
235 RETURNS => "",
236 PROTOTYPE => [],
237 TEXT => [],
239 my $parse_state = 0;
240 my $ignore_blank_lines = 1;
241 my $extra_comment = 0; # 1 if this is an extra comment, i.e its not a .spec export
243 if ($opt_verbose > 0)
245 print "Processing ".$source_file."\n";
247 open(SOURCE_FILE,"<$source_file")
248 || (($opt_source_dir ne "")
249 && open(SOURCE_FILE,"<$opt_source_dir/$source_file"))
250 || die "couldn't open ".$source_file."\n";
252 # Add this source file to the list of source files
253 $source_files{$source_file} = [$source_details];
255 while(<SOURCE_FILE>)
257 s/\n$//; # Strip newline
258 s/^\s+//; # Strip leading space
259 s/\s+$//; # Strip trailing space
260 if (! /^\*\|/ )
262 # Strip multiple tabs & spaces to a single space
263 s/\s+/ /g;
266 if ( / +Copyright *(\([Cc]\))*[0-9 \-\,\/]*([[:alpha:][:^ascii:] \.\-]+)/ )
268 # Extract a contributor to this file
269 my $contributor = $2;
270 $contributor =~ s/ *$//;
271 $contributor =~ s/^by //;
272 $contributor =~ s/\.$//;
273 $contributor =~ s/ (for .*)/ \($1\)/;
274 if ($contributor ne "")
276 if ($opt_verbose > 3)
278 print "Info: Found contributor:'".$contributor."'\n";
280 push (@{$source_details->{CONTRIBUTORS}},$contributor);
283 elsif ( /WINE_DEFAULT_DEBUG_CHANNEL\(([A-Za-z]*)\)/ )
285 # Extract the debug channel to use
286 if ($opt_verbose > 3)
288 print "Info: Found debug channel:'".$1."'\n";
290 $source_details->{DEBUG_CHANNEL} = $1;
293 if ($parse_state == 0) # Searching for a comment
295 if ( /^\/\**$/ )
297 # Found a comment start
298 $comment->{COMMENT_NAME} = "";
299 $comment->{ALT_NAME} = "";
300 $comment->{DLL_NAME} = "";
301 $comment->{ORDINAL} = "";
302 $comment->{RETURNS} = "";
303 $comment->{PROTOTYPE} = [];
304 $comment->{TEXT} = [];
305 $ignore_blank_lines = 1;
306 $extra_comment = 0;
307 $parse_state = 3;
310 elsif ($parse_state == 1) # Reading in a comment
312 if ( /^\**\// )
314 # Found the end of the comment
315 $parse_state = 2;
317 elsif ( s/^\*\|/\|/ )
319 # A line of comment not meant to be pre-processed
320 push (@{$comment->{TEXT}},$_); # Add the comment text
322 elsif ( s/^ *\** *// )
324 # A line of comment, starting with an asterisk
325 if ( /^[A-Z]+$/ || $_ eq "")
327 # This is a section start, so skip blank lines before and after it.
328 my $last_line = pop(@{$comment->{TEXT}});
329 if (defined($last_line) && $last_line ne "")
331 # Put it back
332 push (@{$comment->{TEXT}},$last_line);
334 if ( /^[A-Z]+$/ )
336 $ignore_blank_lines = 1;
338 else
340 $ignore_blank_lines = 0;
344 if ($ignore_blank_lines == 0 || $_ ne "")
346 push (@{$comment->{TEXT}},$_); # Add the comment text
349 else
351 # This isn't a well formatted comment: look for the next one
352 $parse_state = 0;
355 elsif ($parse_state == 2) # Finished reading in a comment
357 if ( /(WINAPIV|WINAPI|__cdecl|PASCAL|CALLBACK|FARPROC16)/ ||
358 /.*?\(/ )
360 # Comment is followed by a function definition
361 $parse_state = 4; # Fall through to read prototype
363 else
365 # Allow cpp directives and blank lines between the comment and prototype
366 if ($extra_comment == 1)
368 # An extra comment not followed by a function definition
369 $parse_state = 5; # Fall through to process comment
371 elsif (!/^\#/ && !/^ *$/ && !/^__ASM_GLOBAL_FUNC/)
373 # This isn't a well formatted comment: look for the next one
374 if ($opt_verbose > 1)
376 print "Info: Function '",$comment->{COMMENT_NAME},"' not followed by prototype.\n";
378 $parse_state = 0;
382 elsif ($parse_state == 3) # Reading in the first line of a comment
384 s/^ *\** *//;
385 if ( /^([\@A-Za-z0-9_]+) +(\(|\[)([A-Za-z0-9_]+)\.(([0-9]+)|@)(\)|\])\s*(.*)$/ )
387 # Found a correctly formed "ApiName (DLLNAME.Ordinal)" line.
388 if (defined ($7) && $7 ne "")
390 push (@{$comment->{TEXT}},$_); # Add the trailing comment text
392 $comment->{COMMENT_NAME} = $1;
393 $comment->{DLL_NAME} = uc $3;
394 $comment->{ORDINAL} = $4;
395 $comment->{DLL_NAME} =~ s/^KERNEL$/KRNL386/; # Too many of these to ignore, _old_ code
396 $parse_state = 1;
398 elsif ( /^([A-Za-z0-9_-]+) +\{([A-Za-z0-9_]+)\}$/ )
400 # Found a correctly formed "CommentTitle {DLLNAME}" line (extra documentation)
401 $comment->{COMMENT_NAME} = $1;
402 $comment->{DLL_NAME} = uc $2;
403 $comment->{ORDINAL} = "";
404 $extra_comment = 1;
405 $parse_state = 1;
407 else
409 # This isn't a well formatted comment: look for the next one
410 $parse_state = 0;
414 if ($parse_state == 4) # Reading in the function definition
416 push (@{$comment->{PROTOTYPE}},$_);
417 # Strip comments from the line before checking for ')'
418 my $stripped_line = $_;
419 $stripped_line =~ s/ *(\/\* *)(.*?)( *\*\/ *)//;
420 if ( $stripped_line =~ /\)/ )
422 # Strip a blank last line
423 my $last_line = pop(@{$comment->{TEXT}});
424 if (defined($last_line) && $last_line ne "")
426 # Put it back
427 push (@{$comment->{TEXT}},$last_line);
430 if ($opt_output_empty != 0 && @{$comment->{TEXT}} == 0)
432 # Create a 'not implemented' comment
433 @{$comment->{TEXT}} = ("fixme: This function has not yet been documented.");
435 $parse_state = 5;
439 if ($parse_state == 5) # Processing the comment
441 # Process it, if it has any text
442 if (@{$comment->{TEXT}} > 0)
444 if ($extra_comment == 1)
446 process_extra_comment($comment);
448 else
450 @{$comment->{TEXT}} = ("DESCRIPTION", @{$comment->{TEXT}});
451 process_comment($comment);
454 elsif ($opt_verbose > 1 && $opt_output_empty == 0)
456 print "Info: Function '",$comment->{COMMENT_NAME},"' has no documentation.\n";
458 $parse_state = 0;
461 close(SOURCE_FILE);
464 # Standardise a comments text for consistency
465 sub process_comment_text($)
467 my $comment = shift;
468 my $in_params = 0;
469 my @tmp_list = ();
470 my $i = 0;
472 for (@{$comment->{TEXT}})
474 my $line = $_;
476 if ( /^\s*$/ || /^[A-Z]+$/ || /^-/ )
478 $in_params = 0;
480 if ( $in_params > 0 && !/\[/ && !/\]/ )
482 # Possibly a continuation of the parameter description
483 my $last_line = pop(@tmp_list);
484 if ( $last_line =~ /\[/ && $last_line =~ /\]/ )
486 $line = $last_line." ".$_;
488 else
490 $in_params = 0;
491 push (@tmp_list, $last_line);
494 if ( /^(PARAMS|MEMBERS)$/ )
496 $in_params = 1;
498 push (@tmp_list, $line);
501 @{$comment->{TEXT}} = @tmp_list;
503 for (@{$comment->{TEXT}})
505 if (! /^\|/ )
507 # Map I/O values. These come in too many formats to standardise now....
508 s/\[I\]|\[i\]|\[in\]|\[IN\]/\[In\] /g;
509 s/\[O\]|\[o\]|\[out\]|\[OUT\]/\[Out\]/g;
510 s/\[I\/O\]|\[I\,O\]|\[i\/o\]|\[in\/out\]|\[IN\/OUT\]/\[In\/Out\]/g;
511 # TRUE/FALSE/NULL are defines, capitilise them
512 s/True|true/TRUE/g;
513 s/False|false/FALSE/g;
514 s/Null|null/NULL/g;
515 # Preferred capitalisations
516 s/ wine| WINE/ Wine/g;
517 s/ API | api / Api /g;
518 s/ DLL | Dll / dll /g;
519 s/ URL | url / Url /g;
520 s/WIN16|win16/Win16/g;
521 s/WIN32|win32/Win32/g;
522 s/WIN64|win64/Win64/g;
523 s/ ID | id / Id /g;
524 # Grammar
525 s/([a-z])\.([A-Z])/$1\. $2/g; # Space after full stop
526 s/ \:/\:/g; # Colons to the left
527 s/ \;/\;/g; # Semi-colons too
528 # Common idioms
529 s/^See ([A-Za-z0-9_]+)\.$/See $1\(\)\./; # Referring to A version from W
530 s/^Unicode version of ([A-Za-z0-9_]+)\.$/See $1\(\)\./; # Ditto
531 s/^64\-bit version of ([A-Za-z0-9_]+)\.$/See $1\(\)\./; # Referring to 32 bit version from 64
532 s/^PARAMETERS$/PARAMS/; # Name of parameter section should be 'PARAMS'
533 # Trademarks
534 s/( |\.)(M\$|MS|Microsoft|microsoft|micro\$oft|Micro\$oft)( |\.)/$1Microsoft\(tm\)$3/g;
535 s/( |\.)(Windows|windows|windoze|winblows)( |\.)/$1Windows\(tm\)$3/g;
536 s/( |\.)(DOS|dos|msdos)( |\.)/$1MS-DOS\(tm\)$3/g;
537 s/( |\.)(UNIX|unix)( |\.)/$1Unix\(tm\)$3/g;
538 s/( |\.)(LINIX|linux)( |\.)/$1Linux\(tm\)$3/g;
539 # Abbreviations
540 s/( char )/ character /g;
541 s/( chars )/ characters /g;
542 s/( info )/ information /g;
543 s/( app )/ application /g;
544 s/( apps )/ applications /g;
545 s/( exe )/ executable /g;
546 s/( ptr )/ pointer /g;
547 s/( obj )/ object /g;
548 s/( err )/ error /g;
549 s/( bool )/ boolean /g;
550 s/( no\. )/ number /g;
551 s/( No\. )/ Number /g;
552 # Punctuation
553 if ( /\[I|\[O/ && ! /\.$/ )
555 $_ = $_."."; # Always have a full stop at the end of parameter desc.
557 elsif ($i > 0 && /^[A-Z]*$/ &&
558 !(@{$comment->{TEXT}}[$i-1] =~ /\.$/) &&
559 !(@{$comment->{TEXT}}[$i-1] =~ /\:$/))
562 if (!(@{$comment->{TEXT}}[$i-1] =~ /^[A-Z]*$/))
564 # Paragraphs always end with a full stop
565 @{$comment->{TEXT}}[$i-1] = @{$comment->{TEXT}}[$i-1].".";
569 $i++;
573 # Standardise our comment and output it if it is suitable.
574 sub process_comment($)
576 my $comment = shift;
578 # Don't process this comment if the function isn't exported
579 my $spec_details = $spec_files{$comment->{DLL_NAME}}[0];
581 if (!defined($spec_details))
583 if ($opt_verbose > 2)
585 print "Warning: Function '".$comment->{COMMENT_NAME}."' belongs to '".
586 $comment->{DLL_NAME}."' (not passed with -w): not processing it.\n";
588 return;
591 if ($comment->{COMMENT_NAME} eq "@")
593 my $found = 0;
595 # Find the name from the .spec file
596 for (@{$spec_details->{EXPORTS}})
598 if (@$_[0] eq $comment->{ORDINAL})
600 $comment->{COMMENT_NAME} = @$_[2];
601 $found = 1;
605 if ($found == 0)
607 # Create an implementation name
608 $comment->{COMMENT_NAME} = $comment->{DLL_NAME}."_".$comment->{ORDINAL};
612 my $exported_names = $spec_details->{EXPORTED_NAMES};
613 my $export_index = $exported_names->{$comment->{COMMENT_NAME}};
614 my $implementation_names = $spec_details->{IMPLEMENTATION_NAMES};
616 if (!defined($export_index))
618 # Perhaps the comment uses the implementation name?
619 $export_index = $implementation_names->{$comment->{COMMENT_NAME}};
621 if (!defined($export_index))
623 # This function doesn't appear to be exported. hmm.
624 if ($opt_verbose > 2)
626 print "Warning: Function '".$comment->{COMMENT_NAME}."' claims to belong to '".
627 $comment->{DLL_NAME}."' but is not exported by it: not processing it.\n";
629 return;
632 # When the function is exported twice we have the second name below the first
633 # (you see this a lot in ntdll, but also in some other places).
634 my $first_line = ${$comment->{TEXT}}[1];
636 if ( $first_line =~ /^(@|[A-Za-z0-9_]+) +(\(|\[)([A-Za-z0-9_]+)\.(([0-9]+)|@)(\)|\])$/ )
638 # Found a second name - mark it as documented
639 my $alt_index = $exported_names->{$1};
640 if (defined($alt_index))
642 if ($opt_verbose > 2)
644 print "Info: Found alternate name '",$1,"\n";
646 my $alt_export = @{$spec_details->{EXPORTS}}[$alt_index];
647 @$alt_export[4] |= $FLAG_DOCUMENTED;
648 $spec_details->{NUM_DOCS}++;
649 ${$comment->{TEXT}}[1] = "";
653 if (@{$spec_details->{CURRENT_EXTRA}})
655 # We have an extra comment that might be related to this one
656 my $current_comment = ${$spec_details->{CURRENT_EXTRA}}[0];
657 my $current_name = $current_comment->{COMMENT_NAME};
658 if ($comment->{COMMENT_NAME} =~ /^$current_name/ && $comment->{COMMENT_NAME} ne $current_name)
660 if ($opt_verbose > 2)
662 print "Linking ",$comment->{COMMENT_NAME}," to $current_name\n";
664 # Add a reference to this comment to our extra comment
665 push (@{$current_comment->{TEXT}}, $comment->{COMMENT_NAME}."()","");
669 # We want our docs generated using the implementation name, so they are unique
670 my $export = @{$spec_details->{EXPORTS}}[$export_index];
671 $comment->{COMMENT_NAME} = @$export[3];
672 $comment->{ALT_NAME} = @$export[2];
674 # Mark the function as documented
675 $spec_details->{NUM_DOCS}++;
676 @$export[4] |= $FLAG_DOCUMENTED;
678 # This file is used by the DLL - Make sure we get our contributors right
679 push (@{$spec_details->{SOURCES}},$comment->{FILE});
681 # If we have parameter comments in the prototype, extract them
682 my @parameter_comments;
683 for (@{$comment->{PROTOTYPE}})
685 s/ *\, */\,/g; # Strip spaces from around commas
687 if ( s/ *(\/\* *)(.*?)( *\*\/ *)// ) # Strip out comment
689 my $parameter_comment = $2;
690 if (!$parameter_comment =~ /^\[/ )
692 # Add [IO] markers so we format the comment correctly
693 $parameter_comment = "[fixme] ".$parameter_comment;
695 if ( /( |\*)([A-Za-z_]{1}[A-Za-z_0-9]*)(\,|\))/ )
697 # Add the parameter name
698 $parameter_comment = $2." ".$parameter_comment;
700 push (@parameter_comments, $parameter_comment);
704 # If we extracted any prototype comments, add them to the comment text.
705 if (@parameter_comments)
707 @parameter_comments = ("PARAMS", @parameter_comments);
708 my @new_comment = ();
709 my $inserted_params = 0;
711 for (@{$comment->{TEXT}})
713 if ( $inserted_params == 0 && /^[A-Z]+$/ )
715 # Found a section header, so this is where we insert
716 push (@new_comment, @parameter_comments);
717 $inserted_params = 1;
719 push (@new_comment, $_);
721 if ($inserted_params == 0)
723 # Add them to the end
724 push (@new_comment, @parameter_comments);
726 $comment->{TEXT} = [@new_comment];
729 if ($opt_fussy == 1 && $opt_output_empty == 0)
731 # Reject any comment that doesn't have a description or a RETURNS section.
732 # This is the default for now, 'coz many comments aren't suitable.
733 my $found_returns = 0;
734 my $found_description_text = 0;
735 my $in_description = 0;
736 for (@{$comment->{TEXT}})
738 if ( /^RETURNS$/ )
740 $found_returns = 1;
741 $in_description = 0;
743 elsif ( /^DESCRIPTION$/ )
745 $in_description = 1;
747 elsif ($in_description == 1)
749 if ( !/^[A-Z]+$/ )
751 # Don't reject comments that refer to another doc (e.g. A/W)
752 if ( /^See ([A-Za-z0-9_]+)\.$/ )
754 if ($comment->{COMMENT_NAME} =~ /W$/ )
756 # This is probably a Unicode version of an Ascii function.
757 # Create the Ascii name and see if its been documented
758 my $ascii_name = $comment->{COMMENT_NAME};
759 $ascii_name =~ s/W$/A/;
761 my $ascii_export_index = $exported_names->{$ascii_name};
763 if (!defined($ascii_export_index))
765 $ascii_export_index = $implementation_names->{$ascii_name};
767 if (!defined($ascii_export_index))
769 if ($opt_verbose > 2)
771 print "Warning: Function '".$comment->{COMMENT_NAME}."' is not an A/W pair.\n";
774 else
776 my $ascii_export = @{$spec_details->{EXPORTS}}[$ascii_export_index];
777 if (@$ascii_export[4] & $FLAG_DOCUMENTED)
779 # Flag these functions as an A/W pair
780 @$ascii_export[4] |= $FLAG_APAIR;
781 @$export[4] |= $FLAG_WPAIR;
785 $found_returns = 1;
787 elsif ( /^Unicode version of ([A-Za-z0-9_]+)\.$/ )
789 @$export[4] |= $FLAG_WPAIR; # Explicitly marked as W version
790 $found_returns = 1;
792 elsif ( /^64\-bit version of ([A-Za-z0-9_]+)\.$/ )
794 @$export[4] |= $FLAG_64PAIR; # Explicitly marked as 64 bit version
795 $found_returns = 1;
797 $found_description_text = 1;
799 else
801 $in_description = 0;
805 if ($found_returns == 0 || $found_description_text == 0)
807 if ($opt_verbose > 2)
809 print "Info: Function '",$comment->{COMMENT_NAME},"' has no ",
810 "description and/or RETURNS section, skipping\n";
812 $spec_details->{NUM_DOCS}--;
813 @$export[4] &= ~$FLAG_DOCUMENTED;
814 return;
818 process_comment_text($comment);
820 # Strip the prototypes return value, call convention, name and brackets
821 # (This leaves it as a list of types and names, or empty for void functions)
822 my $prototype = join(" ", @{$comment->{PROTOTYPE}});
823 $prototype =~ s/ / /g;
825 if ( $prototype =~ /(WINAPIV|WINAPI|__cdecl|PASCAL|CALLBACK|FARPROC16)/ )
827 $prototype =~ s/^(.*?)\s+(WINAPIV|WINAPI|__cdecl|PASCAL|CALLBACK|FARPROC16)\s+(.*?)\(\s*(.*)/$4/;
828 $comment->{RETURNS} = $1;
830 else
832 $prototype =~ s/^(.*?)([A-Za-z0-9_]+)\s*\(\s*(.*)/$3/;
833 $comment->{RETURNS} = $1;
836 $prototype =~ s/ *\).*//; # Strip end bracket
837 $prototype =~ s/ *\* */\*/g; # Strip space around pointers
838 $prototype =~ s/ *\, */\,/g; # Strip space around commas
839 $prototype =~ s/^(void|VOID)$//; # If void, leave blank
840 $prototype =~ s/\*([A-Za-z_])/\* $1/g; # Separate pointers from parameter name
841 @{$comment->{PROTOTYPE}} = split ( /,/ ,$prototype);
843 # FIXME: If we have no parameters, make sure we have a PARAMS: None. section
845 # Find header file
846 my $h_file = "";
847 if (@$export[4] & $FLAG_NONAME)
849 $h_file = "Exported by ordinal only. Use GetProcAddress() to obtain a pointer to the function.";
851 else
853 if ($comment->{COMMENT_NAME} ne "")
855 my $tmp = "grep -s -l $comment->{COMMENT_NAME} @opt_header_file_list 2>/dev/null";
856 $tmp = `$tmp`;
857 my $exit_value = $? >> 8;
858 if ($exit_value == 0)
860 $tmp =~ s/\n.*//g;
861 if ($tmp ne "")
863 $h_file = "$tmp";
864 $h_file =~ s|^.*/\./||;
868 elsif ($comment->{ALT_NAME} ne "")
870 my $tmp = "grep -s -l $comment->{ALT_NAME} @opt_header_file_list"." 2>/dev/null";
871 $tmp = `$tmp`;
872 my $exit_value = $? >> 8;
873 if ($exit_value == 0)
875 $tmp =~ s/\n.*//g;
876 if ($tmp ne "")
878 $h_file = "$tmp";
879 $h_file =~ s|^.*/\./||;
883 $h_file =~ s/^ *//;
884 $h_file =~ s/\n//;
885 if ($h_file eq "")
887 $h_file = "Not declared in a Wine header. The function is either undocumented, or missing from Wine."
889 else
891 $h_file = "Declared in \"".$h_file."\".";
895 # Find source file
896 my $c_file = $comment->{FILE};
897 if ($opt_wine_root_dir ne "")
899 my $cfile = $pwd."/".$c_file; # Current dir + file
900 $cfile =~ s/(.+)(\/.*$)/$1/; # Strip the filename
901 $cfile = `cd $cfile && pwd`; # Strip any relative parts (e.g. "../../")
902 $cfile =~ s/\n//; # Strip newline
903 my $newfile = $c_file;
904 $newfile =~ s/(.+)(\/.*$)/$2/; # Strip all but the filename
905 $cfile = $cfile."/".$newfile; # Append filename to base path
906 $cfile =~ s/$opt_wine_root_dir//; # Get rid of the root directory
907 $cfile =~ s/\/\//\//g; # Remove any double slashes
908 $cfile =~ s/^\/+//; # Strip initial directory slash
909 $c_file = $cfile;
911 $c_file = "Implemented in \"".$c_file."\".";
913 # Add the implementation details
914 push (@{$comment->{TEXT}}, "IMPLEMENTATION","",$h_file,"",$c_file);
916 if (@$export[4] & $FLAG_I386)
918 push (@{$comment->{TEXT}}, "", "Available on x86 platforms only.");
920 if (@$export[4] & $FLAG_REGISTER)
922 push (@{$comment->{TEXT}}, "", "This function passes one or more arguments in registers. ",
923 "For more details, please read the source code.");
925 my $source_details = $source_files{$comment->{FILE}}[0];
926 if ($source_details->{DEBUG_CHANNEL} ne "")
928 push (@{$comment->{TEXT}}, "", "Debug channel \"".$source_details->{DEBUG_CHANNEL}."\".");
931 # Write out the documentation for the API
932 output_comment($comment)
935 # process our extra comment and output it if it is suitable.
936 sub process_extra_comment($)
938 my $comment = shift;
940 my $spec_details = $spec_files{$comment->{DLL_NAME}}[0];
942 if (!defined($spec_details))
944 if ($opt_verbose > 2)
946 print "Warning: Extra comment '".$comment->{COMMENT_NAME}."' belongs to '".
947 $comment->{DLL_NAME}."' (not passed with -w): not processing it.\n";
949 return;
952 # Check first to see if this is documentation for the DLL.
953 if ($comment->{COMMENT_NAME} eq $comment->{DLL_NAME})
955 if ($opt_verbose > 2)
957 print "Info: Found DLL documentation\n";
959 for (@{$comment->{TEXT}})
961 push (@{$spec_details->{DESCRIPTION}}, $_);
963 return;
966 # Add the comment to the DLL page as a link
967 push (@{$spec_details->{EXTRA_COMMENTS}},$comment->{COMMENT_NAME});
969 # If we have a prototype, process as a regular comment
970 if (@{$comment->{PROTOTYPE}})
972 $comment->{ORDINAL} = "@";
974 # Add an index for the comment name
975 $spec_details->{EXPORTED_NAMES}{$comment->{COMMENT_NAME}} = $spec_details->{NUM_EXPORTS};
977 # Add a fake exported entry
978 $spec_details->{NUM_EXPORTS}++;
979 my ($ordinal, $call_convention, $exported_name, $implementation_name, $documented) =
980 ("@", "fake", $comment->{COMMENT_NAME}, $comment->{COMMENT_NAME}, 0);
981 my @export = ($ordinal, $call_convention, $exported_name, $implementation_name, $documented);
982 push (@{$spec_details->{EXPORTS}},[@export]);
983 @{$comment->{TEXT}} = ("DESCRIPTION", @{$comment->{TEXT}});
984 process_comment($comment);
985 return;
988 if ($opt_verbose > 0)
990 print "Processing ",$comment->{COMMENT_NAME},"\n";
993 if (@{$spec_details->{CURRENT_EXTRA}})
995 my $current_comment = ${$spec_details->{CURRENT_EXTRA}}[0];
997 if ($opt_verbose > 0)
999 print "Processing old current: ",$current_comment->{COMMENT_NAME},"\n";
1001 # Output the current comment
1002 process_comment_text($current_comment);
1003 output_open_api_file($current_comment->{COMMENT_NAME});
1004 output_api_header($current_comment);
1005 output_api_name($current_comment);
1006 output_api_comment($current_comment);
1007 output_api_footer($current_comment);
1008 output_close_api_file();
1011 if ($opt_verbose > 2)
1013 print "Setting current to ",$comment->{COMMENT_NAME},"\n";
1016 my $comment_copy =
1018 FILE => $comment->{FILE},
1019 COMMENT_NAME => $comment->{COMMENT_NAME},
1020 ALT_NAME => $comment->{ALT_NAME},
1021 DLL_NAME => $comment->{DLL_NAME},
1022 ORDINAL => $comment->{ORDINAL},
1023 RETURNS => $comment->{RETURNS},
1024 PROTOTYPE => [],
1025 TEXT => [],
1028 for (@{$comment->{TEXT}})
1030 push (@{$comment_copy->{TEXT}}, $_);
1032 # Set this comment to be the current extra comment
1033 @{$spec_details->{CURRENT_EXTRA}} = ($comment_copy);
1036 # Write a standardised comment out in the appropriate format
1037 sub output_comment($)
1039 my $comment = shift;
1041 if ($opt_verbose > 0)
1043 print "Processing ",$comment->{COMMENT_NAME},"\n";
1046 if ($opt_verbose > 4)
1048 print "--PROTO--\n";
1049 for (@{$comment->{PROTOTYPE}})
1051 print "'".$_."'\n";
1054 print "--COMMENT--\n";
1055 for (@{$comment->{TEXT} })
1057 print $_."\n";
1061 output_open_api_file($comment->{COMMENT_NAME});
1062 output_api_header($comment);
1063 output_api_name($comment);
1064 output_api_synopsis($comment);
1065 output_api_comment($comment);
1066 output_api_footer($comment);
1067 output_close_api_file();
1070 # Write out an index file for each .spec processed
1071 sub process_index_files()
1073 foreach my $spec_file (keys %spec_files)
1075 my $spec_details = $spec_files{$spec_file}[0];
1076 if (defined ($spec_details->{DLL_NAME}))
1078 if (@{$spec_details->{CURRENT_EXTRA}})
1080 # We have an unwritten extra comment, write it
1081 my $current_comment = ${$spec_details->{CURRENT_EXTRA}}[0];
1082 process_extra_comment($current_comment);
1083 @{$spec_details->{CURRENT_EXTRA}} = ();
1085 output_spec($spec_details);
1090 # Write a spec files documentation out in the appropriate format
1091 sub output_spec($)
1093 my $spec_details = shift;
1095 if ($opt_verbose > 2)
1097 print "Writing:",$spec_details->{DLL_NAME},"\n";
1100 # Use the comment output functions for consistency
1101 my $comment =
1103 FILE => $spec_details->{DLL_NAME},
1104 COMMENT_NAME => $spec_details->{DLL_NAME}.".".$spec_details->{DLL_EXT},
1105 ALT_NAME => $spec_details->{DLL_NAME},
1106 DLL_NAME => "",
1107 ORDINAL => "",
1108 RETURNS => "",
1109 PROTOTYPE => [],
1110 TEXT => [],
1112 my $total_implemented = $spec_details->{NUM_FORWARDS} + $spec_details->{NUM_VARS} +
1113 $spec_details->{NUM_FUNCS};
1114 my $percent_implemented = 0;
1115 if ($total_implemented)
1117 $percent_implemented = $total_implemented /
1118 ($total_implemented + $spec_details->{NUM_STUBS}) * 100;
1120 $percent_implemented = int($percent_implemented);
1121 my $percent_documented = 0;
1122 if ($spec_details->{NUM_DOCS})
1124 # Treat forwards and data as documented funcs for statistics
1125 $percent_documented = $spec_details->{NUM_DOCS} / $spec_details->{NUM_FUNCS} * 100;
1126 $percent_documented = int($percent_documented);
1129 # Make a list of the contributors to this DLL. Do this only for the source
1130 # files that make up the DLL, because some directories specify multiple dlls.
1131 my @contributors;
1133 for (@{$spec_details->{SOURCES}})
1135 my $source_details = $source_files{$_}[0];
1136 for (@{$source_details->{CONTRIBUTORS}})
1138 push (@contributors, $_);
1142 my %saw;
1143 @contributors = grep(!$saw{$_}++, @contributors); # remove dups, from perlfaq4 manpage
1144 @contributors = sort @contributors;
1146 # Remove duplicates and blanks
1147 for(my $i=0; $i<@contributors; $i++)
1149 if ($i > 0 && ($contributors[$i] =~ /$contributors[$i-1]/ || $contributors[$i-1] eq ""))
1151 $contributors[$i-1] = $contributors[$i];
1154 undef %saw;
1155 @contributors = grep(!$saw{$_}++, @contributors);
1157 if ($opt_verbose > 3)
1159 print "Contributors:\n";
1160 for (@contributors)
1162 print "'".$_."'\n";
1165 my $contribstring = join (", ", @contributors);
1167 # Create the initial comment text
1168 @{$comment->{TEXT}} = (
1169 "NAME",
1170 $comment->{COMMENT_NAME}
1173 # Add the description, if we have one
1174 if (@{$spec_details->{DESCRIPTION}})
1176 push (@{$comment->{TEXT}}, "DESCRIPTION");
1177 for (@{$spec_details->{DESCRIPTION}})
1179 push (@{$comment->{TEXT}}, $_);
1183 # Add the statistics and contributors
1184 push (@{$comment->{TEXT}},
1185 "STATISTICS",
1186 "Forwards: ".$spec_details->{NUM_FORWARDS},
1187 "Variables: ".$spec_details->{NUM_VARS},
1188 "Stubs: ".$spec_details->{NUM_STUBS},
1189 "Functions: ".$spec_details->{NUM_FUNCS},
1190 "Exports-Total: ".$spec_details->{NUM_EXPORTS},
1191 "Implemented-Total: ".$total_implemented." (".$percent_implemented."%)",
1192 "Documented-Total: ".$spec_details->{NUM_DOCS}." (".$percent_documented."%)",
1193 "CONTRIBUTORS",
1194 "The following people hold copyrights on the source files comprising this dll:",
1196 $contribstring,
1197 "Note: This list may not be complete.",
1198 "For a complete listing, see the Files \"AUTHORS\" and \"Changelog\" in the Wine source tree.",
1202 if ($opt_output_format eq "h")
1204 # Add the exports to the comment text
1205 push (@{$comment->{TEXT}},"EXPORTS");
1206 my $exports = $spec_details->{EXPORTS};
1207 for (@$exports)
1209 my $line = "";
1211 # @$_ => ordinal, call convention, exported name, implementation name, flags;
1212 if (@$_[1] eq "forward")
1214 my $forward_dll = @$_[3];
1215 $forward_dll =~ s/\.(.*)//;
1216 $line = @$_[2]." (forward to ".$1."() in ".$forward_dll."())";
1218 elsif (@$_[1] eq "extern")
1220 $line = @$_[2]." (extern)";
1222 elsif (@$_[1] eq "stub")
1224 $line = @$_[2]." (stub)";
1226 elsif (@$_[1] eq "fake")
1228 # Don't add this function here, it gets listed with the extra documentation
1229 if (!(@$_[4] & $FLAG_WPAIR))
1231 # This function should be indexed
1232 push (@index_entries_list, @$_[3].",".@$_[3]);
1235 elsif (@$_[1] eq "equate" || @$_[1] eq "variable")
1237 $line = @$_[2]." (data)";
1239 else
1241 # A function
1242 if (@$_[4] & $FLAG_DOCUMENTED)
1244 # Documented
1245 $line = @$_[2]." (implemented as ".@$_[3]."())";
1246 if (@$_[2] ne @$_[3])
1248 $line = @$_[2]." (implemented as ".@$_[3]."())";
1250 else
1252 $line = @$_[2]."()";
1254 if (!(@$_[4] & $FLAG_WPAIR))
1256 # This function should be indexed
1257 push (@index_entries_list, @$_[2].",".@$_[3]);
1260 else
1262 $line = @$_[2]." (not documented)";
1265 if ($line ne "")
1267 push (@{$comment->{TEXT}}, $line, "");
1271 # Add links to the extra documentation
1272 if (@{$spec_details->{EXTRA_COMMENTS}})
1274 push (@{$comment->{TEXT}}, "SEE ALSO");
1275 my %htmp;
1276 @{$spec_details->{EXTRA_COMMENTS}} = grep(!$htmp{$_}++, @{$spec_details->{EXTRA_COMMENTS}});
1277 for (@{$spec_details->{EXTRA_COMMENTS}})
1279 push (@{$comment->{TEXT}}, $_."()", "");
1283 # The dll entry should also be indexed
1284 push (@index_entries_list, $spec_details->{DLL_NAME}.",".$spec_details->{DLL_NAME});
1286 # Write out the document
1287 output_open_api_file($spec_details->{DLL_NAME});
1288 output_api_header($comment);
1289 output_api_comment($comment);
1290 output_api_footer($comment);
1291 output_close_api_file();
1293 # Add this dll to the database of dll names
1294 my $output_file = $opt_output_directory."/dlls.db";
1296 # Append the dllname to the output db of names
1297 open(DLLDB,">>$output_file") || die "Couldn't create $output_file\n";
1298 print DLLDB $spec_details->{DLL_NAME},"\n";
1299 close(DLLDB);
1301 if ($opt_output_format eq "s")
1303 output_sgml_dll_file($spec_details);
1304 return;
1307 if ($opt_output_format eq "x")
1309 output_xml_dll_file($spec_details);
1310 return;
1316 # OUTPUT FUNCTIONS
1317 # ----------------
1318 # Only these functions know anything about formatting for a specific
1319 # output type. The functions above work only with plain text.
1320 # This is to allow new types of output to be added easily.
1322 # Open the api file
1323 sub output_open_api_file($)
1325 my $output_name = shift;
1326 $output_name = $opt_output_directory."/".$output_name;
1328 if ($opt_output_format eq "h")
1330 $output_name = $output_name.".html";
1332 elsif ($opt_output_format eq "s")
1334 $output_name = $output_name.".sgml";
1336 elsif ($opt_output_format eq "x")
1338 $output_name = $output_name.".xml";
1340 else
1342 $output_name = $output_name.".".$opt_manual_section;
1344 open(OUTPUT,">$output_name") || die "Couldn't create file '$output_name'\n";
1347 # Close the api file
1348 sub output_close_api_file()
1350 close (OUTPUT);
1353 # Output the api file header
1354 sub output_api_header($)
1356 my $comment = shift;
1358 if ($opt_output_format eq "h")
1360 print OUTPUT "<!-- Generated file - DO NOT EDIT! -->\n";
1361 print OUTPUT "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n";
1362 print OUTPUT "<HTML>\n<HEAD>\n";
1363 print OUTPUT "<LINK REL=\"StyleSheet\" href=\"apidoc.css\" type=\"text/css\">\n";
1364 print OUTPUT "<META NAME=\"GENERATOR\" CONTENT=\"tools/c2man.pl\">\n";
1365 print OUTPUT "<META NAME=\"keywords\" CONTENT=\"Win32,Wine,API,$comment->{COMMENT_NAME}\">\n";
1366 print OUTPUT "<TITLE>Wine API: $comment->{COMMENT_NAME}</TITLE>\n</HEAD>\n<BODY>\n";
1368 elsif ($opt_output_format eq "s" || $opt_output_format eq "x")
1370 print OUTPUT "<!-- Generated file - DO NOT EDIT! -->\n",
1371 "<sect1>\n",
1372 "<title>$comment->{COMMENT_NAME}</title>\n";
1374 else
1376 print OUTPUT ".\\\" -*- nroff -*-\n.\\\" Generated file - DO NOT EDIT!\n".
1377 ".TH ",$comment->{COMMENT_NAME}," ",$opt_manual_section," \"",$date,"\" \"".
1378 "Wine API\" \"Wine API\"\n";
1382 sub output_api_footer($)
1384 if ($opt_output_format eq "h")
1386 print OUTPUT "<hr><p><i class=\"copy\">Copyright &copy ".$year." The Wine Project.".
1387 " All trademarks are the property of their respective owners.".
1388 " Visit <a href=\"http://www.winehq.org\">WineHQ</a> for license details.".
1389 " Generated $date.</i></p>\n</body>\n</html>\n";
1391 elsif ($opt_output_format eq "s" || $opt_output_format eq "x")
1393 print OUTPUT "</sect1>\n";
1394 return;
1396 else
1401 sub output_api_section_start($$)
1403 my $comment = shift;
1404 my $section_name = shift;
1406 if ($opt_output_format eq "h")
1408 print OUTPUT "\n<h2 class=\"section\">",$section_name,"</h2>\n";
1410 elsif ($opt_output_format eq "s" || $opt_output_format eq "x")
1412 print OUTPUT "<bridgehead>",$section_name,"</bridgehead>\n";
1414 else
1416 print OUTPUT "\n\.SH ",$section_name,"\n";
1420 sub output_api_section_end()
1422 # Not currently required by any output formats
1425 sub output_api_name($)
1427 my $comment = shift;
1428 my $readable_name = $comment->{COMMENT_NAME};
1429 $readable_name =~ s/-/ /g; # make section names more readable
1431 output_api_section_start($comment,"NAME");
1434 my $dll_ordinal = "";
1435 if ($comment->{ORDINAL} ne "")
1437 $dll_ordinal = "(".$comment->{DLL_NAME}.".".$comment->{ORDINAL}.")";
1439 if ($opt_output_format eq "h")
1441 print OUTPUT "<p><b class=\"func_name\">",$readable_name,
1442 "</b>&nbsp;&nbsp;<i class=\"dll_ord\">",
1443 ,$dll_ordinal,"</i></p>\n";
1445 elsif ($opt_output_format eq "s" || $opt_output_format eq "x")
1447 print OUTPUT "<para>\n <command>",$readable_name,"</command> <emphasis>",
1448 $dll_ordinal,"</emphasis>\n</para>\n";
1450 else
1452 print OUTPUT "\\fB",$readable_name,"\\fR ",$dll_ordinal;
1455 output_api_section_end();
1458 sub output_api_synopsis($)
1460 my $comment = shift;
1461 my @fmt;
1463 output_api_section_start($comment,"SYNOPSIS");
1465 if ($opt_output_format eq "h")
1467 print OUTPUT "<pre class=\"proto\">\n ", $comment->{RETURNS}," ",$comment->{COMMENT_NAME},"\n (\n";
1468 @fmt = ("", "\n", "<tt class=\"param\">", "</tt>");
1470 elsif ($opt_output_format eq "s" || $opt_output_format eq "x")
1472 print OUTPUT "<screen>\n ",$comment->{RETURNS}," ",$comment->{COMMENT_NAME},"\n (\n";
1473 @fmt = ("", "\n", "<emphasis>", "</emphasis>");
1475 else
1477 print OUTPUT $comment->{RETURNS}," ",$comment->{COMMENT_NAME},"\n (\n";
1478 @fmt = ("", "\n", "\\fI", "\\fR");
1481 # Since our prototype is output in a pre-formatted block, line up the
1482 # parameters and parameter comments in the same column.
1484 # First caluculate where the columns should start
1485 my $biggest_length = 0;
1486 for(my $i=0; $i < @{$comment->{PROTOTYPE}}; $i++)
1488 my $line = ${$comment->{PROTOTYPE}}[$i];
1489 if ($line =~ /(.+?)([A-Za-z_][A-Za-z_0-9]*)$/)
1491 my $length = length $1;
1492 if ($length > $biggest_length)
1494 $biggest_length = $length;
1499 # Now pad the string with blanks
1500 for(my $i=0; $i < @{$comment->{PROTOTYPE}}; $i++)
1502 my $line = ${$comment->{PROTOTYPE}}[$i];
1503 if ($line =~ /(.+?)([A-Za-z_][A-Za-z_0-9]*)$/)
1505 my $pad_len = $biggest_length - length $1;
1506 my $padding = " " x ($pad_len);
1507 ${$comment->{PROTOTYPE}}[$i] = $1.$padding.$2;
1511 for(my $i=0; $i < @{$comment->{PROTOTYPE}}; $i++)
1513 # Format the parameter name
1514 my $line = ${$comment->{PROTOTYPE}}[$i];
1515 my $comma = ($i == @{$comment->{PROTOTYPE}}-1) ? "" : ",";
1516 $line =~ s/(.+?)([A-Za-z_][A-Za-z_0-9]*)$/ $fmt[0]$1$fmt[2]$2$fmt[3]$comma$fmt[1]/;
1517 print OUTPUT $line;
1520 if ($opt_output_format eq "h")
1522 print OUTPUT " )\n</pre>\n";
1524 elsif ($opt_output_format eq "s" || $opt_output_format eq "x")
1526 print OUTPUT " )\n</screen>\n";
1528 else
1530 print OUTPUT " )\n";
1533 output_api_section_end();
1536 sub output_api_comment($)
1538 my $comment = shift;
1539 my $open_paragraph = 0;
1540 my $open_raw = 0;
1541 my $param_docs = 0;
1542 my @fmt;
1544 if ($opt_output_format eq "h")
1546 @fmt = ("<p>", "</p>\n", "<tt class=\"const\">", "</tt>", "<b class=\"emp\">", "</b>",
1547 "<tt class=\"coderef\">", "</tt>", "<tt class=\"param\">", "</tt>",
1548 "<i class=\"in_out\">", "</i>", "<pre class=\"raw\">\n", "</pre>\n",
1549 "<table class=\"tab\"><colgroup><col><col><col></colgroup><tbody>\n",
1550 "</tbody></table>\n","<tr><td>","</td></tr>\n","</td>","</td><td>");
1552 elsif ($opt_output_format eq "s" || $opt_output_format eq "x")
1554 @fmt = ("<para>\n","\n</para>\n","<constant>","</constant>","<emphasis>","</emphasis>",
1555 "<command>","</command>","<constant>","</constant>","<emphasis>","</emphasis>",
1556 "<screen>\n","</screen>\n",
1557 "<informaltable frame=\"none\">\n<tgroup cols=\"3\">\n<tbody>\n",
1558 "</tbody>\n</tgroup>\n</informaltable>\n","<row><entry>","</entry></row>\n",
1559 "</entry>","</entry><entry>");
1561 else
1563 @fmt = ("\.PP\n", "\n", "\\fB", "\\fR", "\\fB", "\\fR", "\\fB", "\\fR", "\\fI", "\\fR",
1564 "\\fB", "\\fR ", "", "", "", "","","\n.PP\n","","");
1567 # Extract the parameter names
1568 my @parameter_names;
1569 for (@{$comment->{PROTOTYPE}})
1571 if ( /(.+?)([A-Za-z_][A-Za-z_0-9]*)$/ )
1573 push (@parameter_names, $2);
1577 for (@{$comment->{TEXT}})
1579 if ($opt_output_format eq "h" || $opt_output_format eq "s" || $opt_output_format eq "x")
1581 # Map special characters
1582 s/\&/\&amp;/g;
1583 s/\</\&lt;/g;
1584 s/\>/\&gt;/g;
1585 s/\([Cc]\)/\&copy;/g;
1586 s/\(tm\)/&#174;/;
1589 if ( s/^\|// )
1591 # Raw output
1592 if ($open_raw == 0)
1594 if ($open_paragraph == 1)
1596 # Close the open paragraph
1597 print OUTPUT $fmt[1];
1598 $open_paragraph = 0;
1600 # Start raw output
1601 print OUTPUT $fmt[12];
1602 $open_raw = 1;
1604 if ($opt_output_format eq "")
1606 print OUTPUT ".br\n"; # Prevent 'man' running these lines together
1608 print OUTPUT $_,"\n";
1610 else
1612 if ($opt_output_format eq "h")
1614 # Link to the file in WineHQ cvs
1615 s/^(Implemented in \")(.+?)(\"\.)/$1$2$3 http:\/\/source.winehq.org\/source\/$2/g;
1616 s/^(Declared in \")(.+?)(\"\.)/$1$2$3 http:\/\/source.winehq.org\/source\/include\/$2/g;
1618 # Highlight strings
1619 s/(\".+?\")/$fmt[2]$1$fmt[3]/g;
1620 # Highlight literal chars
1621 s/(\'.\')/$fmt[2]$1$fmt[3]/g;
1622 s/(\'.{2}\')/$fmt[2]$1$fmt[3]/g;
1623 # Highlight numeric constants
1624 s/( |\-|\+|\.|\()([0-9\-\.]+)( |\-|$|\.|\,|\*|\?|\))/$1$fmt[2]$2$fmt[3]$3/g;
1626 # Leading cases ("xxxx:","-") start new paragraphs & are emphasised
1627 # FIXME: Using bullet points for leading '-' would look nicer.
1628 if ($open_paragraph == 1 && $param_docs == 0)
1630 s/^(\-)/$fmt[1]$fmt[0]$fmt[4]$1$fmt[5]/;
1631 s/^([[A-Za-z\-]+\:)/$fmt[1]$fmt[0]$fmt[4]$1$fmt[5]/;
1633 else
1635 s/^(\-)/$fmt[4]$1$fmt[5]/;
1636 s/^([[A-Za-z\-]+\:)/$fmt[4]$1$fmt[5]/;
1639 if ($opt_output_format eq "h")
1641 # Html uses links for API calls
1642 while ( /([A-Za-z_]+[A-Za-z_0-9-]+)(\(\))/)
1644 my $link = $1;
1645 my $readable_link = $1;
1646 $readable_link =~ s/-/ /g;
1648 s/([A-Za-z_]+[A-Za-z_0-9-]+)(\(\))/<a href\=\"$link\.html\">$readable_link<\/a>/;
1650 # Index references
1651 s/\{\{(.*?)\}\}\{\{(.*?)\}\}/<a href\=\"$2\.html\">$1<\/a>/g;
1652 s/ ([A-Z_])(\(\))/<a href\=\"$1\.html\">$1<\/a>/g;
1653 # And references to COM objects (hey, they'll get documented one day)
1654 s/ (I[A-Z]{1}[A-Za-z0-9_]+) (Object|object|Interface|interface)/ <a href\=\"$1\.html\">$1<\/a> $2/g;
1655 # Convert any web addresses to real links
1656 s/(http\:\/\/)(.+?)($| )/<a href\=\"$1$2\">$2<\/a>$3/g;
1658 else
1660 if ($opt_output_format eq "")
1662 # Give the man section for API calls
1663 s/ ([A-Za-z_]+[A-Za-z_0-9-]+)\(\)/ $fmt[6]$1\($opt_manual_section\)$fmt[7]/g;
1665 else
1667 # Highlight API calls
1668 s/ ([A-Za-z_]+[A-Za-z_0-9-]+\(\))/ $fmt[6]$1$fmt[7]/g;
1671 # And references to COM objects
1672 s/ (I[A-Z]{1}[A-Za-z0-9_]+) (Object|object|Interface|interface)/ $fmt[6]$1$fmt[7] $2/g;
1675 if ($open_raw == 1)
1677 # Finish the raw output
1678 print OUTPUT $fmt[13];
1679 $open_raw = 0;
1682 if ( /^[A-Z]+$/ || /^SEE ALSO$/ )
1684 # Start of a new section
1685 if ($open_paragraph == 1)
1687 if ($param_docs == 1)
1689 print OUTPUT $fmt[17],$fmt[15];
1690 $param_docs = 0;
1692 else
1694 print OUTPUT $fmt[1];
1696 $open_paragraph = 0;
1698 output_api_section_start($comment,$_);
1699 if ( /^PARAMS$/ || /^MEMBERS$/ )
1701 print OUTPUT $fmt[14];
1702 $param_docs = 1;
1704 else
1706 #print OUTPUT $fmt[15];
1707 #$param_docs = 0;
1710 elsif ( /^$/ )
1712 # Empty line, indicating a new paragraph
1713 if ($open_paragraph == 1)
1715 if ($param_docs == 0)
1717 print OUTPUT $fmt[1];
1718 $open_paragraph = 0;
1722 else
1724 if ($param_docs == 1)
1726 if ($open_paragraph == 1)
1728 # For parameter docs, put each parameter into a new paragraph/table row
1729 print OUTPUT $fmt[17];
1730 $open_paragraph = 0;
1732 s/(\[.+\])( *)/$fmt[19]$fmt[10]$1$fmt[11]$fmt[19] /; # Format In/Out
1734 else
1736 # Within paragraph lines, prevent lines running together
1737 $_ = $_." ";
1740 # Format parameter names where they appear in the comment
1741 for my $parameter_name (@parameter_names)
1743 s/(^|[ \.\,\(\-\*])($parameter_name)($|[ \.\)\,\-\/]|(\=[^"]))/$1$fmt[8]$2$fmt[9]$3/g;
1745 # Structure dereferences include the dereferenced member
1746 s/(\-\>[A-Za-z_]+)/$fmt[8]$1$fmt[9]/g;
1747 s/(\-\&gt\;[A-Za-z_]+)/$fmt[8]$1$fmt[9]/g;
1749 if ($open_paragraph == 0)
1751 if ($param_docs == 1)
1753 print OUTPUT $fmt[16];
1755 else
1757 print OUTPUT $fmt[0];
1759 $open_paragraph = 1;
1761 # Anything in all uppercase on its own gets emphasised
1762 s/(^|[ \.\,\(\[\|\=])([A-Z]+?[A-Z0-9_]+)($|[ \.\,\*\?\|\)\=\'])/$1$fmt[6]$2$fmt[7]$3/g;
1764 print OUTPUT $_;
1768 if ($open_raw == 1)
1770 print OUTPUT $fmt[13];
1772 if ($param_docs == 1 && $open_paragraph == 1)
1774 print OUTPUT $fmt[17];
1775 $open_paragraph = 0;
1777 if ($param_docs == 1)
1779 print OUTPUT $fmt[15];
1781 if ($open_paragraph == 1)
1783 print OUTPUT $fmt[1];
1787 # Create the master index file
1788 sub output_master_index_files()
1790 if ($opt_output_format eq "")
1792 return; # No master index for man pages
1795 if ($opt_output_format eq "h")
1797 # Append the index entries to the output db of index entries
1798 my $output_file = $opt_output_directory."/index.db";
1799 open(INDEXDB,">>$output_file") || die "Couldn't create $output_file\n";
1800 for (@index_entries_list)
1802 $_ =~ s/A\,/\,/;
1803 print INDEXDB $_."\n";
1805 close(INDEXDB);
1808 # Use the comment output functions for consistency
1809 my $comment =
1811 FILE => "",
1812 COMMENT_NAME => "The Wine Api Guide",
1813 ALT_NAME => "The Wine Api Guide",
1814 DLL_NAME => "",
1815 ORDINAL => "",
1816 RETURNS => "",
1817 PROTOTYPE => [],
1818 TEXT => [],
1821 if ($opt_output_format eq "s" || $opt_output_format eq "x")
1823 $comment->{COMMENT_NAME} = "Introduction";
1824 $comment->{ALT_NAME} = "Introduction",
1826 elsif ($opt_output_format eq "h")
1828 @{$comment->{TEXT}} = (
1829 "NAME",
1830 $comment->{COMMENT_NAME},
1831 "INTRODUCTION",
1835 # Create the initial comment text
1836 push (@{$comment->{TEXT}},
1837 "This document describes the Api calls made available",
1838 "by Wine. They are grouped by the dll that exports them.",
1840 "Please do not edit this document, since it is generated automatically",
1841 "from the Wine source code tree. Details on updating this documentation",
1842 "are given in the \"Wine Developers Guide\".",
1843 "CONTRIBUTORS",
1844 "Api documentation is generally written by the person who ",
1845 "implements a given Api call. Authors of each dll are listed in the overview ",
1846 "section for that dll. Additional contributors who have updated source files ",
1847 "but have not entered their names in a copyright statement are noted by an ",
1848 "entry in the file \"Changelog\" from the Wine source code distribution.",
1852 # Read in all dlls from the database of dll names
1853 my $input_file = $opt_output_directory."/dlls.db";
1854 my @dlls = `cat $input_file|sort|uniq`;
1856 if ($opt_output_format eq "h")
1858 # HTML gets a list of all the dlls and an index. For docbook the index creates this for us
1859 push (@{$comment->{TEXT}},
1860 "INDEX",
1861 "For an alphabetical listing of the functions available, please click the ",
1862 "first letter of the functions name below:","",
1863 "[ _(), A(), B(), C(), D(), E(), F(), G(), H(), ".
1864 "I(), J(), K(), L(), M(), N(), O(), P(), Q(), ".
1865 "R(), S(), T(), U(), V(), W(), X(), Y(), Z() ]", "",
1866 "DLLS",
1867 "Each dll provided by Wine is documented individually. The following dlls are provided :",
1870 # Add the dlls to the comment
1871 for (@dlls)
1873 $_ =~ s/(\..*)?\n/\(\)/;
1874 push (@{$comment->{TEXT}}, $_, "");
1876 output_open_api_file("index");
1878 elsif ($opt_output_format eq "s" || $opt_output_format eq "x")
1880 # Just write this as the initial blurb, with a chapter heading
1881 output_open_api_file("blurb");
1882 print OUTPUT "<chapter id =\"blurb\">\n<title>Introduction to The Wine Api Guide</title>\n"
1885 # Write out the document
1886 output_api_header($comment);
1887 output_api_comment($comment);
1888 output_api_footer($comment);
1889 if ($opt_output_format eq "s" || $opt_output_format eq "x")
1891 print OUTPUT "</chapter>\n" # finish the chapter
1893 output_close_api_file();
1895 if ($opt_output_format eq "s")
1897 output_sgml_master_file(\@dlls);
1898 return;
1900 if ($opt_output_format eq "x")
1902 output_xml_master_file(\@dlls);
1903 return;
1905 if ($opt_output_format eq "h")
1907 output_html_index_files();
1908 output_html_stylesheet();
1909 return;
1913 # Write the master wine-api.xml, linking it to each dll.
1914 sub output_xml_master_file($)
1916 my $dlls = shift;
1918 output_open_api_file("wine-api");
1919 print OUTPUT "<?xml version='1.0'?>";
1920 print OUTPUT "<!-- Generated file - DO NOT EDIT! -->\n";
1921 print OUTPUT "<!DOCTYPE book PUBLIC \"-//OASIS//DTD DocBook V5.0/EN\" ";
1922 print OUTPUT " \"http://www.docbook.org/xml/5.0/dtd/docbook.dtd\" [\n\n";
1923 print OUTPUT "<!ENTITY blurb SYSTEM \"blurb.xml\">\n";
1925 # List the entities
1926 for (@$dlls)
1928 $_ =~ s/(\..*)?\n//;
1929 print OUTPUT "<!ENTITY ",$_," SYSTEM \"",$_,".xml\">\n"
1932 print OUTPUT "]>\n\n<book id=\"index\">\n<bookinfo><title>The Wine Api Guide</title></bookinfo>\n\n";
1933 print OUTPUT " &blurb;\n";
1935 for (@$dlls)
1937 print OUTPUT " &",$_,";\n"
1939 print OUTPUT "\n\n</book>\n";
1941 output_close_api_file();
1944 # Write the master wine-api.sgml, linking it to each dll.
1945 sub output_sgml_master_file($)
1947 my $dlls = shift;
1949 output_open_api_file("wine-api");
1950 print OUTPUT "<!-- Generated file - DO NOT EDIT! -->\n";
1951 print OUTPUT "<!doctype book PUBLIC \"-//OASIS//DTD DocBook V3.1//EN\" [\n\n";
1952 print OUTPUT "<!entity blurb SYSTEM \"blurb.sgml\">\n";
1954 # List the entities
1955 for (@$dlls)
1957 $_ =~ s/(\..*)?\n//;
1958 print OUTPUT "<!entity ",$_," SYSTEM \"",$_,".sgml\">\n"
1961 print OUTPUT "]>\n\n<book id=\"index\">\n<bookinfo><title>The Wine Api Guide</title></bookinfo>\n\n";
1962 print OUTPUT " &blurb;\n";
1964 for (@$dlls)
1966 print OUTPUT " &",$_,";\n"
1968 print OUTPUT "\n\n</book>\n";
1970 output_close_api_file();
1973 # Produce the sgml for the dll chapter from the generated files
1974 sub output_sgml_dll_file($)
1976 my $spec_details = shift;
1978 # Make a list of all the documentation files to include
1979 my $exports = $spec_details->{EXPORTS};
1980 my @source_files = ();
1981 for (@$exports)
1983 # @$_ => ordinal, call convention, exported name, implementation name, documented;
1984 if (@$_[1] ne "forward" && @$_[1] ne "extern" && @$_[1] ne "stub" && @$_[1] ne "equate" &&
1985 @$_[1] ne "variable" && @$_[1] ne "fake" && @$_[4] & 1)
1987 # A documented function
1988 push (@source_files,@$_[3]);
1992 push (@source_files,@{$spec_details->{EXTRA_COMMENTS}});
1994 @source_files = sort @source_files;
1996 # create a new chapter for this dll
1997 my $tmp_name = $opt_output_directory."/".$spec_details->{DLL_NAME}.".tmp";
1998 open(OUTPUT,">$tmp_name") || die "Couldn't create $tmp_name\n";
1999 print OUTPUT "<chapter>\n<title>$spec_details->{DLL_NAME}</title>\n";
2000 output_close_api_file();
2002 # Add the sorted documentation, cleaning up as we go
2003 `cat $opt_output_directory/$spec_details->{DLL_NAME}.sgml >>$tmp_name`;
2004 for (@source_files)
2006 `cat $opt_output_directory/$_.sgml >>$tmp_name`;
2007 `rm -f $opt_output_directory/$_.sgml`;
2010 # close the chapter, and overwite the dll source
2011 open(OUTPUT,">>$tmp_name") || die "Couldn't create $tmp_name\n";
2012 print OUTPUT "</chapter>\n";
2013 close OUTPUT;
2014 `mv $tmp_name $opt_output_directory/$spec_details->{DLL_NAME}.sgml`;
2017 # Produce the xml for the dll chapter from the generated files
2018 sub output_xml_dll_file($)
2020 my $spec_details = shift;
2022 # Make a list of all the documentation files to include
2023 my $exports = $spec_details->{EXPORTS};
2024 my @source_files = ();
2025 for (@$exports)
2027 # @$_ => ordinal, call convention, exported name, implementation name, documented;
2028 if (@$_[1] ne "forward" && @$_[1] ne "extern" && @$_[1] ne "stub" && @$_[1] ne "equate" &&
2029 @$_[1] ne "variable" && @$_[1] ne "fake" && @$_[4] & 1)
2031 # A documented function
2032 push (@source_files,@$_[3]);
2036 push (@source_files,@{$spec_details->{EXTRA_COMMENTS}});
2038 @source_files = sort @source_files;
2040 # create a new chapter for this dll
2041 my $tmp_name = $opt_output_directory."/".$spec_details->{DLL_NAME}.".tmp";
2042 open(OUTPUT,">$tmp_name") || die "Couldn't create $tmp_name\n";
2043 print OUTPUT "<?xml version='1.0' encoding='UTF-8'?>\n<chapter>\n<title>$spec_details->{DLL_NAME}</title>\n";
2044 output_close_api_file();
2046 # Add the sorted documentation, cleaning up as we go
2047 `cat $opt_output_directory/$spec_details->{DLL_NAME}.xml >>$tmp_name`;
2048 for (@source_files)
2050 `cat $opt_output_directory/$_.xml >>$tmp_name`;
2051 `rm -f $opt_output_directory/$_.xml`;
2054 # close the chapter, and overwite the dll source
2055 open(OUTPUT,">>$tmp_name") || die "Couldn't create $tmp_name\n";
2056 print OUTPUT "</chapter>\n";
2057 close OUTPUT;
2058 `mv $tmp_name $opt_output_directory/$spec_details->{DLL_NAME}.xml`;
2061 # Write the html index files containing the function names
2062 sub output_html_index_files()
2064 if ($opt_output_format ne "h")
2066 return;
2069 my @letters = ('_', 'A' .. 'Z');
2071 # Read in all functions
2072 my $input_file = $opt_output_directory."/index.db";
2073 my @funcs = `cat $input_file|sort|uniq`;
2075 for (@letters)
2077 my $letter = $_;
2078 my $comment =
2080 FILE => "",
2081 COMMENT_NAME => "",
2082 ALT_NAME => "",
2083 DLL_NAME => "",
2084 ORDINAL => "",
2085 RETURNS => "",
2086 PROTOTYPE => [],
2087 TEXT => [],
2090 $comment->{COMMENT_NAME} = $letter." Functions";
2091 $comment->{ALT_NAME} = $letter." Functions";
2093 push (@{$comment->{TEXT}},
2094 "NAME",
2095 $comment->{COMMENT_NAME},
2096 "FUNCTIONS"
2099 # Add the functions to the comment
2100 for (@funcs)
2102 my $first_char = substr ($_, 0, 1);
2103 $first_char = uc $first_char;
2105 if ($first_char eq $letter)
2107 my $name = $_;
2108 my $file;
2109 $name =~ s/(^.*?)\,(.*?)\n/$1/;
2110 $file = $2;
2111 push (@{$comment->{TEXT}}, "{{".$name."}}{{".$file."}}","");
2115 # Write out the document
2116 output_open_api_file($letter);
2117 output_api_header($comment);
2118 output_api_comment($comment);
2119 output_api_footer($comment);
2120 output_close_api_file();
2124 # Output the stylesheet for HTML output
2125 sub output_html_stylesheet()
2127 if ($opt_output_format ne "h")
2129 return;
2132 my $css;
2133 ($css = <<HERE_TARGET) =~ s/^\s+//gm;
2135 * Default styles for Wine HTML Documentation.
2137 * This style sheet should be altered to suit your needs/taste.
2139 BODY { /* Page body */
2140 background-color: white;
2141 color: black;
2142 font-family: Tahoma,sans-serif;
2143 font-style: normal;
2144 font-size: 10pt;
2146 a:link { color: #4444ff; } /* Links */
2147 a:visited { color: #333377 }
2148 a:active { color: #0000dd }
2149 H2.section { /* Section Headers */
2150 font-family: sans-serif;
2151 color: #777777;
2152 background-color: #F0F0FE;
2153 margin-left: 0.2in;
2154 margin-right: 1.0in;
2156 b.func_name { /* Function Name */
2157 font-size: 10pt;
2158 font-style: bold;
2160 i.dll_ord { /* Italicised DLL+ordinal */
2161 color: #888888;
2162 font-family: sans-serif;
2163 font-size: 8pt;
2165 p { /* Paragraphs */
2166 margin-left: 0.5in;
2167 margin-right: 0.5in;
2169 table { /* tables */
2170 margin-left: 0.5in;
2171 margin-right: 0.5in;
2173 pre.proto /* API Function prototype */
2175 border-style: solid;
2176 border-width: 1px;
2177 border-color: #777777;
2178 background-color: #F0F0BB;
2179 color: black;
2180 font-size: 10pt;
2181 vertical-align: top;
2182 margin-left: 0.5in;
2183 margin-right: 1.0in;
2185 pre.raw { /* Raw text output */
2186 margin-left: 0.6in;
2187 margin-right: 1.1in;
2188 background-color: #8080DC;
2190 tt.param { /* Parameter name */
2191 font-style: italic;
2192 color: blue;
2194 tt.const { /* Constant */
2195 color: red;
2197 i.in_out { /* In/Out */
2198 font-size: 8pt;
2199 color: grey;
2201 tt.coderef { /* Code in description text */
2202 color: darkgreen;
2204 b.emp /* Emphasis */ {
2205 font-style: bold;
2206 color: darkblue;
2208 i.footer { /* Footer */
2209 font-family: sans-serif;
2210 font-size: 6pt;
2211 color: darkgrey;
2213 HERE_TARGET
2215 my $output_file = "$opt_output_directory/apidoc.css";
2216 open(CSS,">$output_file") || die "Couldn't create the file $output_file\n";
2217 print CSS $css;
2218 close(CSS);
2222 sub usage()
2224 print "\nCreate API Documentation from Wine source code.\n\n",
2225 "Usage: c2man.pl [options] {-w <spec>} {-I <include>} {<source>}\n",
2226 "Where: <spec> is a .spec file giving a DLL's exports.\n",
2227 " <include> is an include directory used by the DLL.\n",
2228 " <source> is a source file of the DLL.\n",
2229 " The above can be given multiple times on the command line, as appropriate.\n",
2230 "Options:\n",
2231 " -Th : Output HTML instead of a man page\n",
2232 " -Ts : Output SGML (Docbook source) instead of a man page\n",
2233 " -C <dir> : Source directory, to find source files if they are not found in the\n",
2234 " current directory. Default is \"",$opt_source_dir,"\"\n",
2235 " -R <dir> : Root of build directory, default is \"",$opt_wine_root_dir,"\"\n",
2236 " -o <dir> : Create output in <dir>, default is \"",$opt_output_directory,"\"\n",
2237 " -s <sect>: Set manual section to <sect>, default is ",$opt_manual_section,"\n",
2238 " -e : Output \"FIXME\" documentation from empty comments.\n",
2239 " -v : Verbosity. Can be given more than once for more detail.\n";
2244 # Main
2247 # Print usage if we're called with no args
2248 if( @ARGV == 0)
2250 usage();
2253 # Process command line options
2254 while(defined($_ = shift @ARGV))
2256 if( s/^-// )
2258 # An option.
2259 for ($_)
2261 /^o$/ && do { $opt_output_directory = shift @ARGV; last; };
2262 s/^S// && do { $opt_manual_section = $_; last; };
2263 /^Th$/ && do { $opt_output_format = "h"; last; };
2264 /^Ts$/ && do { $opt_output_format = "s"; last; };
2265 /^Tx$/ && do { $opt_output_format = "x"; last; };
2266 /^v$/ && do { $opt_verbose++; last; };
2267 /^e$/ && do { $opt_output_empty = 1; last; };
2268 /^L$/ && do { last; };
2269 /^w$/ && do { @opt_spec_file_list = (@opt_spec_file_list, shift @ARGV); last; };
2270 s/^I// && do { if ($_ ne ".") {
2271 foreach my $include (`find $_/./ -type d ! -name tests`) {
2272 $include =~ s/\n//;
2273 $include = $include."/*.h";
2274 $include =~ s/\/\//\//g;
2275 my $have_headers = `ls $include >/dev/null 2>&1`;
2276 if ($? >> 8 == 0) { @opt_header_file_list = (@opt_header_file_list, $include); }
2279 last;
2281 s/^C// && do {
2282 if ($_ ne "") { $opt_source_dir = $_; }
2283 last;
2285 s/^R// && do { if ($_ =~ /^\//) { $opt_wine_root_dir = $_; }
2286 else { $opt_wine_root_dir = `cd $pwd/$_ && pwd`; }
2287 $opt_wine_root_dir =~ s/\n//;
2288 $opt_wine_root_dir =~ s/\/\//\//g;
2289 if (! $opt_wine_root_dir =~ /\/$/ ) { $opt_wine_root_dir = $opt_wine_root_dir."/"; };
2290 last;
2292 die "Unrecognised option $_\n";
2295 else
2297 # A source file.
2298 push (@opt_source_file_list, $_);
2302 # Remove duplicate include directories
2303 my %htmp;
2304 @opt_header_file_list = grep(!$htmp{$_}++, @opt_header_file_list);
2306 if ($opt_verbose > 3)
2308 print "Output dir:'".$opt_output_directory."'\n";
2309 print "Section :'".$opt_manual_section."'\n";
2310 print "Format :'".$opt_output_format."'\n";
2311 print "Source dir:'".$opt_source_dir."'\n";
2312 print "Root :'".$opt_wine_root_dir."'\n";
2313 print "Spec files:'@opt_spec_file_list'\n";
2314 print "Includes :'@opt_header_file_list'\n";
2315 print "Sources :'@opt_source_file_list'\n";
2318 if (@opt_spec_file_list == 0)
2320 exit 0; # Don't bother processing non-dll files
2323 # Make sure the output directory exists
2324 unless (-d $opt_output_directory)
2326 mkdir $opt_output_directory or die "Cannot create directory $opt_output_directory\n";
2329 # Read in each .spec files exports and other details
2330 while(my $spec_file = shift @opt_spec_file_list)
2332 process_spec_file($spec_file);
2335 if ($opt_verbose > 3)
2337 foreach my $spec_file ( keys %spec_files )
2339 print "in '$spec_file':\n";
2340 my $spec_details = $spec_files{$spec_file}[0];
2341 my $exports = $spec_details->{EXPORTS};
2342 for (@$exports)
2344 print @$_[0].",".@$_[1].",".@$_[2].",".@$_[3]."\n";
2349 # Extract and output the comments from each source file
2350 while(defined($_ = shift @opt_source_file_list))
2352 process_source_file($_);
2355 # Write the index files for each spec
2356 process_index_files();
2358 # Write the master index file
2359 output_master_index_files();
2361 exit 0;