jscript: Added jsstr_extract helper and use it in String.charCodeAt implementation.
[wine.git] / tools / c2man.pl
blob96bedd59ddc523e1a28ef2db12e85d6398453222
1 #! /usr/bin/perl -w
3 # Generate API documentation. See http://www.winehq.org/docs/winedev-guide/api-docs for details.
5 # Copyright (C) 2000 Mike McCormack
6 # Copyright (C) 2003 Jon Griffiths
8 # This library is free software; you can redistribute it and/or
9 # modify it under the terms of the GNU Lesser General Public
10 # License as published by the Free Software Foundation; either
11 # version 2.1 of the License, or (at your option) any later version.
13 # This library is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 # Lesser General Public License for more details.
18 # You should have received a copy of the GNU Lesser General Public
19 # License along with this library; if not, write to the Free Software
20 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 # TODO
23 # Consolidate A+W pairs together, and only write one doc, without the suffix
24 # Implement automatic docs of structs/defines in headers
25 # SGML gurus - feel free to smarten up the SGML.
26 # Add any other relevant information for the dll - imports etc
27 # Should we have a special output mode for WineHQ?
29 use strict;
30 use bytes;
32 # Function flags. most of these come from the spec flags
33 my $FLAG_DOCUMENTED = 1;
34 my $FLAG_NONAME = 2;
35 my $FLAG_I386 = 4;
36 my $FLAG_REGISTER = 8;
37 my $FLAG_APAIR = 16; # The A version of a matching W function
38 my $FLAG_WPAIR = 32; # The W version of a matching A function
39 my $FLAG_64PAIR = 64; # The 64 bit version of a matching 32 bit function
41 # Export list slot labels.
42 my $EXPORT_ORDINAL = 0; # Ordinal.
43 my $EXPORT_CALL = 1; # Call type.
44 my $EXPORT_EXPNAME = 2; # Export name.
45 my $EXPORT_IMPNAME = 3; # Implementation name.
46 my $EXPORT_FLAGS = 4; # Flags - see above.
48 # Options
49 my $opt_output_directory = "man3w"; # All default options are for nroff (man pages)
50 my $opt_manual_section = "3w";
51 my $opt_source_dir = "";
52 my $opt_wine_root_dir = "";
53 my $opt_output_format = ""; # '' = nroff, 'h' = html, 's' = sgml, 'x' = xml
54 my $opt_output_empty = 0; # Non-zero = Create 'empty' comments (for every implemented function)
55 my $opt_fussy = 1; # Non-zero = Create only if we have a RETURNS section
56 my $opt_verbose = 0; # >0 = verbosity. Can be given multiple times (for debugging)
57 my @opt_header_file_list = ();
58 my @opt_spec_file_list = ();
59 my @opt_source_file_list = ();
61 # All the collected details about all the .spec files being processed
62 my %spec_files;
63 # All the collected details about all the source files being processed
64 my %source_files;
65 # All documented functions that are to be placed in the index
66 my @index_entries_list = ();
68 # useful globals
69 my $pwd = `pwd`."/";
70 $pwd =~ s/\n//;
71 my @datetime = localtime;
72 my @months = ( "Jan", "Feb", "Mar", "Apr", "May", "Jun",
73 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" );
74 my $year = $datetime[5] + 1900;
75 my $date = "$months[$datetime[4]] $year";
78 sub output_api_comment($);
79 sub output_api_footer($);
80 sub output_api_header($);
81 sub output_api_name($);
82 sub output_api_synopsis($);
83 sub output_close_api_file();
84 sub output_comment($);
85 sub output_html_index_files();
86 sub output_html_stylesheet();
87 sub output_open_api_file($);
88 sub output_sgml_dll_file($);
89 sub output_xml_dll_file($);
90 sub output_sgml_master_file($);
91 sub output_xml_master_file($);
92 sub output_spec($);
93 sub process_comment($);
94 sub process_extra_comment($);
97 # Generate the list of exported entries for the dll
98 sub process_spec_file($)
100 my $spec_name = shift;
101 my ($dll_name, $dll_ext) = split(/\./, $spec_name);
102 $dll_ext = "dll" if ( $dll_ext eq "spec" );
103 my $uc_dll_name = uc $dll_name;
105 my $spec_details =
107 NAME => $spec_name,
108 DLL_NAME => $dll_name,
109 DLL_EXT => $dll_ext,
110 NUM_EXPORTS => 0,
111 NUM_STUBS => 0,
112 NUM_FUNCS => 0,
113 NUM_FORWARDS => 0,
114 NUM_VARS => 0,
115 NUM_DOCS => 0,
116 CONTRIBUTORS => [ ],
117 SOURCES => [ ],
118 DESCRIPTION => [ ],
119 EXPORTS => [ ],
120 EXPORTED_NAMES => { },
121 IMPLEMENTATION_NAMES => { },
122 EXTRA_COMMENTS => [ ],
123 CURRENT_EXTRA => [ ] ,
126 if ($opt_verbose > 0)
128 print "Processing ".$spec_name."\n";
131 # We allow opening to fail just to cater for the peculiarities of
132 # the Wine build system. This doesn't hurt, in any case
133 open(SPEC_FILE, "<$spec_name")
134 || (($opt_source_dir ne "")
135 && open(SPEC_FILE, "<$opt_source_dir/$spec_name"))
136 || return;
138 while(<SPEC_FILE>)
140 s/^\s+//; # Strip leading space
141 s/\s+\n$/\n/; # Strip trailing space
142 s/\s+/ /g; # Strip multiple tabs & spaces to a single space
143 s/\s*#.*//; # Strip comments
144 s/\(.*\)/ /; # Strip arguments
145 s/\s+/ /g; # Strip multiple tabs & spaces to a single space (again)
146 s/\n$//; # Strip newline
148 my $flags = 0;
149 if( /\-noname/ )
151 $flags |= $FLAG_NONAME;
153 if( /\-i386/ )
155 $flags |= $FLAG_I386;
157 if( /\-register/ )
159 $flags |= $FLAG_REGISTER;
161 s/ \-[a-z0-9=_]+//g; # Strip flags
163 if( /^(([0-9]+)|@) / )
165 # This line contains an exported symbol
166 my ($ordinal, $call_convention, $exported_name, $implementation_name) = split(' ');
168 for ($call_convention)
170 /^(cdecl|stdcall|varargs|pascal)$/
171 && do { $spec_details->{NUM_FUNCS}++; last; };
172 /^(variable|equate)$/
173 && do { $spec_details->{NUM_VARS}++; last; };
174 /^(extern)$/
175 && do { $spec_details->{NUM_FORWARDS}++; last; };
176 /^stub$/ && do { $spec_details->{NUM_STUBS}++; last; };
177 if ($opt_verbose > 0)
179 print "Warning: didn't recognise convention \'",$call_convention,"'\n";
181 last;
184 # Convert ordinal only names so we can find them later
185 if ($exported_name eq "@")
187 $exported_name = $uc_dll_name.'_'.$ordinal;
189 if (!defined($implementation_name))
191 $implementation_name = $exported_name;
193 if ($implementation_name eq "")
195 $implementation_name = $exported_name;
198 if ($implementation_name =~ /(.*?)\./)
200 $call_convention = "forward"; # Referencing a function from another dll
201 $spec_details->{NUM_FUNCS}--;
202 $spec_details->{NUM_FORWARDS}++;
205 # Add indices for the exported and implementation names
206 $spec_details->{EXPORTED_NAMES}{$exported_name} = $spec_details->{NUM_EXPORTS};
207 if ($implementation_name ne $exported_name)
209 $spec_details->{IMPLEMENTATION_NAMES}{$exported_name} = $spec_details->{NUM_EXPORTS};
212 # Add the exported entry
213 $spec_details->{NUM_EXPORTS}++;
214 my @export = ($ordinal, $call_convention, $exported_name, $implementation_name, $flags);
215 push (@{$spec_details->{EXPORTS}},[@export]);
218 close(SPEC_FILE);
220 # Add this .spec files details to the list of .spec files
221 $spec_files{$uc_dll_name} = [$spec_details];
224 # Read each source file, extract comments, and generate API documentation if appropriate
225 sub process_source_file($)
227 my $source_file = shift;
228 my $source_details =
230 CONTRIBUTORS => [ ],
231 DEBUG_CHANNEL => "",
233 my $comment =
235 FILE => $source_file,
236 COMMENT_NAME => "",
237 ALT_NAME => "",
238 DLL_NAME => "",
239 DLL_EXT => "",
240 ORDINAL => "",
241 RETURNS => "",
242 PROTOTYPE => [],
243 TEXT => [],
245 my $parse_state = 0;
246 my $ignore_blank_lines = 1;
247 my $extra_comment = 0; # 1 if this is an extra comment, i.e its not a .spec export
249 if ($opt_verbose > 0)
251 print "Processing ".$source_file."\n";
253 open(SOURCE_FILE,"<$source_file")
254 || (($opt_source_dir ne "")
255 && open(SOURCE_FILE,"<$opt_source_dir/$source_file"))
256 || die "couldn't open ".$source_file."\n";
258 # Add this source file to the list of source files
259 $source_files{$source_file} = [$source_details];
261 while(<SOURCE_FILE>)
263 s/\n$//; # Strip newline
264 s/^\s+//; # Strip leading space
265 s/\s+$//; # Strip trailing space
266 if (! /^\*\|/ )
268 # Strip multiple tabs & spaces to a single space
269 s/\s+/ /g;
272 if ( / +Copyright *(\([Cc]\))*[0-9 \-\,\/]*([[:alpha:][:^ascii:] \.\-]+)/ )
274 # Extract a contributor to this file
275 my $contributor = $2;
276 $contributor =~ s/ *$//;
277 $contributor =~ s/^by //;
278 $contributor =~ s/\.$//;
279 $contributor =~ s/ (for .*)/ \($1\)/;
280 if ($contributor ne "")
282 if ($opt_verbose > 3)
284 print "Info: Found contributor:'".$contributor."'\n";
286 push (@{$source_details->{CONTRIBUTORS}},$contributor);
289 elsif ( /WINE_DEFAULT_DEBUG_CHANNEL\(([A-Za-z]*)\)/ )
291 # Extract the debug channel to use
292 if ($opt_verbose > 3)
294 print "Info: Found debug channel:'".$1."'\n";
296 $source_details->{DEBUG_CHANNEL} = $1;
299 if ($parse_state == 0) # Searching for a comment
301 if ( /^\/\**$/ )
303 # This file is used by the DLL - Make sure we get our contributors right
304 @{$spec_files{$comment->{DLL_NAME}}[0]->{CURRENT_EXTRA}} = ();
305 push (@{$spec_files{$comment->{DLL_NAME}}[0]->{SOURCES}},$comment->{FILE});
306 # Found a comment start
307 $comment->{COMMENT_NAME} = "";
308 $comment->{ALT_NAME} = "";
309 $comment->{DLL_NAME} = "";
310 $comment->{ORDINAL} = "";
311 $comment->{RETURNS} = "";
312 $comment->{PROTOTYPE} = [];
313 $comment->{TEXT} = [];
314 $ignore_blank_lines = 1;
315 $extra_comment = 0;
316 $parse_state = 3;
319 elsif ($parse_state == 1) # Reading in a comment
321 if ( /^\**\// )
323 # Found the end of the comment
324 $parse_state = 2;
326 elsif ( s/^\*\|/\|/ )
328 # A line of comment not meant to be pre-processed
329 push (@{$comment->{TEXT}},$_); # Add the comment text
331 elsif ( s/^ *\** *// )
333 # A line of comment, starting with an asterisk
334 if ( /^[A-Z]+$/ || $_ eq "")
336 # This is a section start, so skip blank lines before and after it.
337 my $last_line = pop(@{$comment->{TEXT}});
338 if (defined($last_line) && $last_line ne "")
340 # Put it back
341 push (@{$comment->{TEXT}},$last_line);
343 if ( /^[A-Z]+$/ )
345 $ignore_blank_lines = 1;
347 else
349 $ignore_blank_lines = 0;
353 if ($ignore_blank_lines == 0 || $_ ne "")
355 push (@{$comment->{TEXT}},$_); # Add the comment text
358 else
360 # This isn't a well formatted comment: look for the next one
361 $parse_state = 0;
364 elsif ($parse_state == 2) # Finished reading in a comment
366 if ( /(WINAPIV|WINAPI|__cdecl|PASCAL|CALLBACK|FARPROC16)/ ||
367 /.*?\(/ )
369 # Comment is followed by a function definition
370 $parse_state = 4; # Fall through to read prototype
372 else
374 # Allow cpp directives and blank lines between the comment and prototype
375 if ($extra_comment == 1)
377 # An extra comment not followed by a function definition
378 $parse_state = 5; # Fall through to process comment
380 elsif (!/^\#/ && !/^ *$/ && !/^__ASM_GLOBAL_FUNC/)
382 # This isn't a well formatted comment: look for the next one
383 if ($opt_verbose > 1)
385 print "Info: Function '",$comment->{COMMENT_NAME},"' not followed by prototype.\n";
387 $parse_state = 0;
391 elsif ($parse_state == 3) # Reading in the first line of a comment
393 s/^ *\** *//;
394 if ( /^([\@A-Za-z0-9_]+) +(\(|\[)([A-Za-z0-9_]+)\.(([0-9]+)|@)(\)|\])\s*(.*)$/ )
396 # Found a correctly formed "ApiName (DLLNAME.Ordinal)" line.
397 if (defined ($7) && $7 ne "")
399 push (@{$comment->{TEXT}},$_); # Add the trailing comment text
401 $comment->{COMMENT_NAME} = $1;
402 $comment->{DLL_NAME} = uc $3;
403 $comment->{ORDINAL} = $4;
404 $comment->{DLL_NAME} =~ s/^KERNEL$/KRNL386/; # Too many of these to ignore, _old_ code
405 $parse_state = 1;
407 elsif ( /^([A-Za-z0-9_-]+) +\{([A-Za-z0-9_]+)\}$/ )
409 # Found a correctly formed "CommentTitle {DLLNAME}" line (extra documentation)
410 $comment->{COMMENT_NAME} = $1;
411 $comment->{DLL_NAME} = uc $2;
412 $comment->{ORDINAL} = "";
413 $extra_comment = 1;
414 $parse_state = 1;
416 else
418 # This isn't a well formatted comment: look for the next one
419 $parse_state = 0;
423 if ($parse_state == 4) # Reading in the function definition
425 push (@{$comment->{PROTOTYPE}},$_);
426 # Strip comments from the line before checking for ')'
427 my $stripped_line = $_;
428 $stripped_line =~ s/ *(\/\* *)(.*?)( *\*\/ *)//;
429 if ( $stripped_line =~ /\)/ )
431 # Strip a blank last line
432 my $last_line = pop(@{$comment->{TEXT}});
433 if (defined($last_line) && $last_line ne "")
435 # Put it back
436 push (@{$comment->{TEXT}},$last_line);
439 if ($opt_output_empty != 0 && @{$comment->{TEXT}} == 0)
441 # Create a 'not implemented' comment
442 @{$comment->{TEXT}} = ("fixme: This function has not yet been documented.");
444 $parse_state = 5;
448 if ($parse_state == 5) # Processing the comment
450 # Process it, if it has any text
451 if (@{$comment->{TEXT}} > 0)
453 if ($extra_comment == 1)
455 process_extra_comment($comment);
457 else
459 @{$comment->{TEXT}} = ("DESCRIPTION", @{$comment->{TEXT}});
460 process_comment($comment);
463 elsif ($opt_verbose > 1 && $opt_output_empty == 0)
465 print "Info: Function '",$comment->{COMMENT_NAME},"' has no documentation.\n";
467 $parse_state = 0;
470 close(SOURCE_FILE);
473 # Standardise a comments text for consistency
474 sub process_comment_text($)
476 my $comment = shift;
477 my $in_params = 0;
478 my @tmp_list = ();
479 my $i = 0;
481 for (@{$comment->{TEXT}})
483 my $line = $_;
485 if ( /^\s*$/ || /^[A-Z]+$/ || /^-/ )
487 $in_params = 0;
489 if ( $in_params > 0 && !/\[/ && !/\]/ )
491 # Possibly a continuation of the parameter description
492 my $last_line = pop(@tmp_list);
493 if ( $last_line =~ /\[/ && $last_line =~ /\]/ )
495 $line = $last_line." ".$_;
497 else
499 $in_params = 0;
500 push (@tmp_list, $last_line);
503 if ( /^(PARAMS|MEMBERS)$/ )
505 $in_params = 1;
507 push (@tmp_list, $line);
510 @{$comment->{TEXT}} = @tmp_list;
512 for (@{$comment->{TEXT}})
514 if (! /^\|/ )
516 # Map I/O values. These come in too many formats to standardise now....
517 s/\[I\]|\[i\]|\[in\]|\[IN\]/\[In\] /g;
518 s/\[O\]|\[o\]|\[out\]|\[OUT\]/\[Out\]/g;
519 s/\[I\/O\]|\[I\,O\]|\[i\/o\]|\[in\/out\]|\[IN\/OUT\]/\[In\/Out\]/g;
520 # TRUE/FALSE/NULL are defines, capitilise them
521 s/True|true/TRUE/g;
522 s/False|false/FALSE/g;
523 s/Null|null/NULL/g;
524 # Preferred capitalisations
525 s/ wine| WINE/ Wine/g;
526 s/ API | api / Api /g;
527 s/ DLL | Dll / dll /g;
528 s/ URL | url / Url /g;
529 s/WIN16|win16/Win16/g;
530 s/WIN32|win32/Win32/g;
531 s/WIN64|win64/Win64/g;
532 s/ ID | id / Id /g;
533 # Grammar
534 s/([a-z])\.([A-Z])/$1\. $2/g; # Space after full stop
535 s/ \:/\:/g; # Colons to the left
536 s/ \;/\;/g; # Semi-colons too
537 # Common idioms
538 s/^See ([A-Za-z0-9_]+)\.$/See $1\(\)\./; # Referring to A version from W
539 s/^Unicode version of ([A-Za-z0-9_]+)\.$/See $1\(\)\./; # Ditto
540 s/^64\-bit version of ([A-Za-z0-9_]+)\.$/See $1\(\)\./; # Referring to 32 bit version from 64
541 s/^PARAMETERS$/PARAMS/; # Name of parameter section should be 'PARAMS'
542 # Trademarks
543 s/( |\.)(M\$|MS|Microsoft|microsoft|micro\$oft|Micro\$oft)( |\.)/$1Microsoft\(tm\)$3/g;
544 s/( |\.)(Windows|windows|windoze|winblows)( |\.)/$1Windows\(tm\)$3/g;
545 s/( |\.)(DOS|dos|msdos)( |\.)/$1MS-DOS\(tm\)$3/g;
546 s/( |\.)(UNIX|unix)( |\.)/$1Unix\(tm\)$3/g;
547 s/( |\.)(LINIX|linux)( |\.)/$1Linux\(tm\)$3/g;
548 # Abbreviations
549 s/( char )/ character /g;
550 s/( chars )/ characters /g;
551 s/( info )/ information /g;
552 s/( app )/ application /g;
553 s/( apps )/ applications /g;
554 s/( exe )/ executable /g;
555 s/( ptr )/ pointer /g;
556 s/( obj )/ object /g;
557 s/( err )/ error /g;
558 s/( bool )/ boolean /g;
559 s/( no\. )/ number /g;
560 s/( No\. )/ Number /g;
561 # Punctuation
562 if ( /\[I|\[O/ && ! /\.$/ )
564 $_ = $_."."; # Always have a full stop at the end of parameter desc.
566 elsif ($i > 0 && /^[A-Z]*$/ &&
567 !(@{$comment->{TEXT}}[$i-1] =~ /\.$/) &&
568 !(@{$comment->{TEXT}}[$i-1] =~ /\:$/))
571 if (!(@{$comment->{TEXT}}[$i-1] =~ /^[A-Z]*$/))
573 # Paragraphs always end with a full stop
574 @{$comment->{TEXT}}[$i-1] = @{$comment->{TEXT}}[$i-1].".";
578 $i++;
582 # Standardise our comment and output it if it is suitable.
583 sub process_comment($)
585 my $comment = shift;
587 # Don't process this comment if the function isn't exported
588 my $spec_details = $spec_files{$comment->{DLL_NAME}}[0];
590 if (!defined($spec_details))
592 if ($opt_verbose > 2)
594 print "Warning: Function '".$comment->{COMMENT_NAME}."' belongs to '".
595 $comment->{DLL_NAME}."' (not passed with -w): not processing it.\n";
597 return;
600 if ($comment->{COMMENT_NAME} eq "@")
602 my $found = 0;
604 # Find the name from the .spec file
605 for (@{$spec_details->{EXPORTS}})
607 if (@$_[$EXPORT_ORDINAL] eq $comment->{ORDINAL})
609 $comment->{COMMENT_NAME} = @$_[$EXPORT_EXPNAME];
610 $found = 1;
614 if ($found == 0)
616 # Create an implementation name
617 $comment->{COMMENT_NAME} = $comment->{DLL_NAME}."_".$comment->{ORDINAL};
621 my $exported_names = $spec_details->{EXPORTED_NAMES};
622 my $export_index = $exported_names->{$comment->{COMMENT_NAME}};
623 my $implementation_names = $spec_details->{IMPLEMENTATION_NAMES};
625 if (!defined($export_index))
627 # Perhaps the comment uses the implementation name?
628 $export_index = $implementation_names->{$comment->{COMMENT_NAME}};
630 if (!defined($export_index))
632 # This function doesn't appear to be exported. hmm.
633 if ($opt_verbose > 2)
635 print "Warning: Function '".$comment->{COMMENT_NAME}."' claims to belong to '".
636 $comment->{DLL_NAME}."' but is not exported by it: not processing it.\n";
638 return;
641 # When the function is exported twice we have the second name below the first
642 # (you see this a lot in ntdll, but also in some other places).
643 my $first_line = ${$comment->{TEXT}}[1];
645 if ( $first_line =~ /^(@|[A-Za-z0-9_]+) +(\(|\[)([A-Za-z0-9_]+)\.(([0-9]+)|@)(\)|\])$/ )
647 # Found a second name - mark it as documented
648 my $alt_index = $exported_names->{$1};
649 if (defined($alt_index))
651 if ($opt_verbose > 2)
653 print "Info: Found alternate name '",$1,"\n";
655 my $alt_export = @{$spec_details->{EXPORTS}}[$alt_index];
656 @$alt_export[$EXPORT_FLAGS] |= $FLAG_DOCUMENTED;
657 $spec_details->{NUM_DOCS}++;
658 ${$comment->{TEXT}}[1] = "";
662 if (@{$spec_details->{CURRENT_EXTRA}})
664 # We have an extra comment that might be related to this one
665 my $current_comment = ${$spec_details->{CURRENT_EXTRA}}[0];
666 my $current_name = $current_comment->{COMMENT_NAME};
667 if ($comment->{COMMENT_NAME} =~ /^$current_name/ && $comment->{COMMENT_NAME} ne $current_name)
669 if ($opt_verbose > 2)
671 print "Linking ",$comment->{COMMENT_NAME}," to $current_name\n";
673 # Add a reference to this comment to our extra comment
674 push (@{$current_comment->{TEXT}}, $comment->{COMMENT_NAME}."()","");
678 # We want our docs generated using the implementation name, so they are unique
679 my $export = @{$spec_details->{EXPORTS}}[$export_index];
680 $comment->{COMMENT_NAME} = @$export[$EXPORT_IMPNAME];
681 $comment->{ALT_NAME} = @$export[$EXPORT_EXPNAME];
683 # Mark the function as documented
684 $spec_details->{NUM_DOCS}++;
685 @$export[$EXPORT_FLAGS] |= $FLAG_DOCUMENTED;
687 # If we have parameter comments in the prototype, extract them
688 my @parameter_comments;
689 for (@{$comment->{PROTOTYPE}})
691 s/ *\, */\,/g; # Strip spaces from around commas
693 if ( s/ *(\/\* *)(.*?)( *\*\/ *)// ) # Strip out comment
695 my $parameter_comment = $2;
696 if (!$parameter_comment =~ /^\[/ )
698 # Add [IO] markers so we format the comment correctly
699 $parameter_comment = "[fixme] ".$parameter_comment;
701 if ( /( |\*)([A-Za-z_]{1}[A-Za-z_0-9]*)(\,|\))/ )
703 # Add the parameter name
704 $parameter_comment = $2." ".$parameter_comment;
706 push (@parameter_comments, $parameter_comment);
710 # If we extracted any prototype comments, add them to the comment text.
711 if (@parameter_comments)
713 @parameter_comments = ("PARAMS", @parameter_comments);
714 my @new_comment = ();
715 my $inserted_params = 0;
717 for (@{$comment->{TEXT}})
719 if ( $inserted_params == 0 && /^[A-Z]+$/ )
721 # Found a section header, so this is where we insert
722 push (@new_comment, @parameter_comments);
723 $inserted_params = 1;
725 push (@new_comment, $_);
727 if ($inserted_params == 0)
729 # Add them to the end
730 push (@new_comment, @parameter_comments);
732 $comment->{TEXT} = [@new_comment];
735 if ($opt_fussy == 1 && $opt_output_empty == 0)
737 # Reject any comment that doesn't have a description or a RETURNS section.
738 # This is the default for now, 'coz many comments aren't suitable.
739 my $found_returns = 0;
740 my $found_description_text = 0;
741 my $in_description = 0;
742 for (@{$comment->{TEXT}})
744 if ( /^RETURNS$/ )
746 $found_returns = 1;
747 $in_description = 0;
749 elsif ( /^DESCRIPTION$/ )
751 $in_description = 1;
753 elsif ($in_description == 1)
755 if ( !/^[A-Z]+$/ )
757 # Don't reject comments that refer to another doc (e.g. A/W)
758 if ( /^See ([A-Za-z0-9_]+)\.$/ )
760 if ($comment->{COMMENT_NAME} =~ /W$/ )
762 # This is probably a Unicode version of an Ascii function.
763 # Create the Ascii name and see if its been documented
764 my $ascii_name = $comment->{COMMENT_NAME};
765 $ascii_name =~ s/W$/A/;
767 my $ascii_export_index = $exported_names->{$ascii_name};
769 if (!defined($ascii_export_index))
771 $ascii_export_index = $implementation_names->{$ascii_name};
773 if (!defined($ascii_export_index))
775 if ($opt_verbose > 2)
777 print "Warning: Function '".$comment->{COMMENT_NAME}."' is not an A/W pair.\n";
780 else
782 my $ascii_export = @{$spec_details->{EXPORTS}}[$ascii_export_index];
783 if (@$ascii_export[$EXPORT_FLAGS] & $FLAG_DOCUMENTED)
785 # Flag these functions as an A/W pair
786 @$ascii_export[$EXPORT_FLAGS] |= $FLAG_APAIR;
787 @$export[$EXPORT_FLAGS] |= $FLAG_WPAIR;
791 $found_returns = 1;
793 elsif ( /^Unicode version of ([A-Za-z0-9_]+)\.$/ )
795 @$export[$EXPORT_FLAGS] |= $FLAG_WPAIR; # Explicitly marked as W version
796 $found_returns = 1;
798 elsif ( /^64\-bit version of ([A-Za-z0-9_]+)\.$/ )
800 @$export[$EXPORT_FLAGS] |= $FLAG_64PAIR; # Explicitly marked as 64 bit version
801 $found_returns = 1;
803 $found_description_text = 1;
805 else
807 $in_description = 0;
811 if ($found_returns == 0 || $found_description_text == 0)
813 if ($opt_verbose > 2)
815 print "Info: Function '",$comment->{COMMENT_NAME},"' has no ",
816 "description and/or RETURNS section, skipping\n";
818 $spec_details->{NUM_DOCS}--;
819 @$export[$EXPORT_FLAGS] &= ~$FLAG_DOCUMENTED;
820 return;
824 process_comment_text($comment);
826 # Strip the prototypes return value, call convention, name and brackets
827 # (This leaves it as a list of types and names, or empty for void functions)
828 my $prototype = join(" ", @{$comment->{PROTOTYPE}});
829 $prototype =~ s/ / /g;
831 if ( $prototype =~ /(WINAPIV|WINAPI|__cdecl|PASCAL|CALLBACK|FARPROC16)/ )
833 $prototype =~ s/^(.*?)\s+(WINAPIV|WINAPI|__cdecl|PASCAL|CALLBACK|FARPROC16)\s+(.*?)\(\s*(.*)/$4/;
834 $comment->{RETURNS} = $1;
836 else
838 $prototype =~ s/^(.*?)([A-Za-z0-9_]+)\s*\(\s*(.*)/$3/;
839 $comment->{RETURNS} = $1;
842 $prototype =~ s/ *\).*//; # Strip end bracket
843 $prototype =~ s/ *\* */\*/g; # Strip space around pointers
844 $prototype =~ s/ *\, */\,/g; # Strip space around commas
845 $prototype =~ s/^(void|VOID)$//; # If void, leave blank
846 $prototype =~ s/\*([A-Za-z_])/\* $1/g; # Separate pointers from parameter name
847 @{$comment->{PROTOTYPE}} = split ( /,/ ,$prototype);
849 # FIXME: If we have no parameters, make sure we have a PARAMS: None. section
851 # Find header file
852 my $h_file = "";
853 if (@$export[$EXPORT_FLAGS] & $FLAG_NONAME)
855 $h_file = "Exported by ordinal only. Use GetProcAddress() to obtain a pointer to the function.";
857 else
859 if ($comment->{COMMENT_NAME} ne "")
861 my $tmp = "grep -s -l $comment->{COMMENT_NAME} @opt_header_file_list 2>/dev/null";
862 $tmp = `$tmp`;
863 my $exit_value = $? >> 8;
864 if ($exit_value == 0)
866 $tmp =~ s/\n.*//g;
867 if ($tmp ne "")
869 $h_file = "$tmp";
870 $h_file =~ s|^.*/\./||;
874 elsif ($comment->{ALT_NAME} ne "")
876 my $tmp = "grep -s -l $comment->{ALT_NAME} @opt_header_file_list"." 2>/dev/null";
877 $tmp = `$tmp`;
878 my $exit_value = $? >> 8;
879 if ($exit_value == 0)
881 $tmp =~ s/\n.*//g;
882 if ($tmp ne "")
884 $h_file = "$tmp";
885 $h_file =~ s|^.*/\./||;
889 $h_file =~ s/^ *//;
890 $h_file =~ s/\n//;
891 if ($h_file eq "")
893 $h_file = "Not declared in a Wine header. The function is either undocumented, or missing from Wine."
895 else
897 $h_file = "Declared in \"".$h_file."\".";
901 # Find source file
902 my $c_file = $comment->{FILE};
903 if ($opt_wine_root_dir ne "")
905 my $cfile = $pwd."/".$c_file; # Current dir + file
906 $cfile =~ s/(.+)(\/.*$)/$1/; # Strip the filename
907 $cfile = `cd $cfile && pwd`; # Strip any relative parts (e.g. "../../")
908 $cfile =~ s/\n//; # Strip newline
909 my $newfile = $c_file;
910 $newfile =~ s/(.+)(\/.*$)/$2/; # Strip all but the filename
911 $cfile = $cfile."/".$newfile; # Append filename to base path
912 $cfile =~ s/$opt_wine_root_dir//; # Get rid of the root directory
913 $cfile =~ s/\/\//\//g; # Remove any double slashes
914 $cfile =~ s/^\/+//; # Strip initial directory slash
915 $c_file = $cfile;
917 $c_file = "Implemented in \"".$c_file."\".";
919 # Add the implementation details
920 push (@{$comment->{TEXT}}, "IMPLEMENTATION","",$h_file,"",$c_file);
922 if (@$export[$EXPORT_FLAGS] & $FLAG_I386)
924 push (@{$comment->{TEXT}}, "", "Available on x86 platforms only.");
926 if (@$export[$EXPORT_FLAGS] & $FLAG_REGISTER)
928 push (@{$comment->{TEXT}}, "", "This function passes one or more arguments in registers. ",
929 "For more details, please read the source code.");
931 my $source_details = $source_files{$comment->{FILE}}[0];
932 if ($source_details->{DEBUG_CHANNEL} ne "")
934 push (@{$comment->{TEXT}}, "", "Debug channel \"".$source_details->{DEBUG_CHANNEL}."\".");
937 # Write out the documentation for the API
938 output_comment($comment)
941 # process our extra comment and output it if it is suitable.
942 sub process_extra_comment($)
944 my $comment = shift;
946 my $spec_details = $spec_files{$comment->{DLL_NAME}}[0];
948 if (!defined($spec_details))
950 if ($opt_verbose > 2)
952 print "Warning: Extra comment '".$comment->{COMMENT_NAME}."' belongs to '".
953 $comment->{DLL_NAME}."' (not passed with -w): not processing it.\n";
955 return;
958 # Check first to see if this is documentation for the DLL.
959 if ($comment->{COMMENT_NAME} eq $comment->{DLL_NAME})
961 if ($opt_verbose > 2)
963 print "Info: Found DLL documentation\n";
965 for (@{$comment->{TEXT}})
967 push (@{$spec_details->{DESCRIPTION}}, $_);
969 return;
972 # Add the comment to the DLL page as a link
973 push (@{$spec_details->{EXTRA_COMMENTS}},$comment->{COMMENT_NAME});
975 # If we have a prototype, process as a regular comment
976 if (@{$comment->{PROTOTYPE}})
978 $comment->{ORDINAL} = "@";
980 # Add an index for the comment name
981 $spec_details->{EXPORTED_NAMES}{$comment->{COMMENT_NAME}} = $spec_details->{NUM_EXPORTS};
983 # Add a fake exported entry
984 $spec_details->{NUM_EXPORTS}++;
985 my ($ordinal, $call_convention, $exported_name, $implementation_name, $documented) =
986 ("@", "fake", $comment->{COMMENT_NAME}, $comment->{COMMENT_NAME}, 0);
987 my @export = ($ordinal, $call_convention, $exported_name, $implementation_name, $documented);
988 push (@{$spec_details->{EXPORTS}},[@export]);
989 @{$comment->{TEXT}} = ("DESCRIPTION", @{$comment->{TEXT}});
990 process_comment($comment);
991 return;
994 if ($opt_verbose > 0)
996 print "Processing ",$comment->{COMMENT_NAME},"\n";
999 if (@{$spec_details->{CURRENT_EXTRA}})
1001 my $current_comment = ${$spec_details->{CURRENT_EXTRA}}[0];
1003 if ($opt_verbose > 0)
1005 print "Processing old current: ",$current_comment->{COMMENT_NAME},"\n";
1007 # Output the current comment
1008 process_comment_text($current_comment);
1009 output_open_api_file($current_comment->{COMMENT_NAME});
1010 output_api_header($current_comment);
1011 output_api_name($current_comment);
1012 output_api_comment($current_comment);
1013 output_api_footer($current_comment);
1014 output_close_api_file();
1017 if ($opt_verbose > 2)
1019 print "Setting current to ",$comment->{COMMENT_NAME},"\n";
1022 my $comment_copy =
1024 FILE => $comment->{FILE},
1025 COMMENT_NAME => $comment->{COMMENT_NAME},
1026 ALT_NAME => $comment->{ALT_NAME},
1027 DLL_NAME => $comment->{DLL_NAME},
1028 ORDINAL => $comment->{ORDINAL},
1029 RETURNS => $comment->{RETURNS},
1030 PROTOTYPE => [],
1031 TEXT => [],
1034 for (@{$comment->{TEXT}})
1036 push (@{$comment_copy->{TEXT}}, $_);
1038 # Set this comment to be the current extra comment
1039 @{$spec_details->{CURRENT_EXTRA}} = ($comment_copy);
1042 # Write a standardised comment out in the appropriate format
1043 sub output_comment($)
1045 my $comment = shift;
1047 if ($opt_verbose > 0)
1049 print "Processing ",$comment->{COMMENT_NAME},"\n";
1052 if ($opt_verbose > 4)
1054 print "--PROTO--\n";
1055 for (@{$comment->{PROTOTYPE}})
1057 print "'".$_."'\n";
1060 print "--COMMENT--\n";
1061 for (@{$comment->{TEXT} })
1063 print $_."\n";
1067 output_open_api_file($comment->{COMMENT_NAME});
1068 output_api_header($comment);
1069 output_api_name($comment);
1070 output_api_synopsis($comment);
1071 output_api_comment($comment);
1072 output_api_footer($comment);
1073 output_close_api_file();
1076 # Write out an index file for each .spec processed
1077 sub process_index_files()
1079 foreach my $spec_file (keys %spec_files)
1081 my $spec_details = $spec_files{$spec_file}[0];
1082 if (defined ($spec_details->{DLL_NAME}))
1084 if (@{$spec_details->{CURRENT_EXTRA}})
1086 # We have an unwritten extra comment, write it
1087 my $current_comment = ${$spec_details->{CURRENT_EXTRA}}[0];
1088 process_extra_comment($current_comment);
1089 @{$spec_details->{CURRENT_EXTRA}} = ();
1091 output_spec($spec_details);
1096 # Write a spec files documentation out in the appropriate format
1097 sub output_spec($)
1099 my $spec_details = shift;
1101 if ($opt_verbose > 2)
1103 print "Writing:",$spec_details->{DLL_NAME},"\n";
1106 # Use the comment output functions for consistency
1107 my $comment =
1109 FILE => $spec_details->{DLL_NAME},
1110 COMMENT_NAME => $spec_details->{DLL_NAME}.".".$spec_details->{DLL_EXT},
1111 ALT_NAME => $spec_details->{DLL_NAME},
1112 DLL_NAME => "",
1113 ORDINAL => "",
1114 RETURNS => "",
1115 PROTOTYPE => [],
1116 TEXT => [],
1118 my $total_implemented = $spec_details->{NUM_FORWARDS} + $spec_details->{NUM_VARS} +
1119 $spec_details->{NUM_FUNCS};
1120 my $percent_implemented = 0;
1121 if ($total_implemented)
1123 $percent_implemented = $total_implemented /
1124 ($total_implemented + $spec_details->{NUM_STUBS}) * 100;
1126 $percent_implemented = int($percent_implemented);
1127 my $percent_documented = 0;
1128 if ($spec_details->{NUM_DOCS})
1130 # Treat forwards and data as documented funcs for statistics
1131 $percent_documented = $spec_details->{NUM_DOCS} / $spec_details->{NUM_FUNCS} * 100;
1132 $percent_documented = int($percent_documented);
1135 # Make a list of the contributors to this DLL.
1136 my @contributors;
1138 foreach my $source_file (keys %source_files)
1140 my $source_details = $source_files{$source_file}[0];
1141 for (@{$source_details->{CONTRIBUTORS}})
1143 push (@contributors, $_);
1147 my %saw;
1148 @contributors = grep(!$saw{$_}++, @contributors); # remove dups, from perlfaq4 manpage
1149 @contributors = sort @contributors;
1151 # Remove duplicates and blanks
1152 for(my $i=0; $i<@contributors; $i++)
1154 if ($i > 0 && ($contributors[$i] =~ /$contributors[$i-1]/ || $contributors[$i-1] eq ""))
1156 $contributors[$i-1] = $contributors[$i];
1159 undef %saw;
1160 @contributors = grep(!$saw{$_}++, @contributors);
1162 if ($opt_verbose > 3)
1164 print "Contributors:\n";
1165 for (@contributors)
1167 print "'".$_."'\n";
1170 my $contribstring = join (", ", @contributors);
1172 # Create the initial comment text
1173 @{$comment->{TEXT}} = (
1174 "NAME",
1175 $comment->{COMMENT_NAME}
1178 # Add the description, if we have one
1179 if (@{$spec_details->{DESCRIPTION}})
1181 push (@{$comment->{TEXT}}, "DESCRIPTION");
1182 for (@{$spec_details->{DESCRIPTION}})
1184 push (@{$comment->{TEXT}}, $_);
1188 # Add the statistics and contributors
1189 push (@{$comment->{TEXT}},
1190 "STATISTICS",
1191 "Forwards: ".$spec_details->{NUM_FORWARDS},
1192 "Variables: ".$spec_details->{NUM_VARS},
1193 "Stubs: ".$spec_details->{NUM_STUBS},
1194 "Functions: ".$spec_details->{NUM_FUNCS},
1195 "Exports-Total: ".$spec_details->{NUM_EXPORTS},
1196 "Implemented-Total: ".$total_implemented." (".$percent_implemented."%)",
1197 "Documented-Total: ".$spec_details->{NUM_DOCS}." (".$percent_documented."%)",
1198 "CONTRIBUTORS",
1199 "The following people hold copyrights on the source files comprising this dll:",
1201 $contribstring,
1202 "Note: This list may not be complete.",
1203 "For a complete listing, see the Files \"AUTHORS\" and \"Changelog\" in the Wine source tree.",
1207 if ($opt_output_format eq "h")
1209 # Add the exports to the comment text
1210 push (@{$comment->{TEXT}},"EXPORTS");
1211 my $exports = $spec_details->{EXPORTS};
1212 for (@$exports)
1214 my $line = "";
1216 # @$_ => ordinal, call convention, exported name, implementation name, flags;
1217 if (@$_[$EXPORT_CALL] eq "forward")
1219 my $forward_dll = @$_[$EXPORT_IMPNAME];
1220 $forward_dll =~ s/\.(.*)//;
1221 $line = @$_[$EXPORT_EXPNAME]." (forward to ".$1."() in ".$forward_dll."())";
1223 elsif (@$_[$EXPORT_CALL] eq "extern")
1225 $line = @$_[$EXPORT_EXPNAME]." (extern)";
1227 elsif (@$_[$EXPORT_CALL] eq "stub")
1229 $line = @$_[$EXPORT_EXPNAME]." (stub)";
1231 elsif (@$_[$EXPORT_CALL] eq "fake")
1233 # Don't add this function here, it gets listed with the extra documentation
1234 if (!(@$_[$EXPORT_FLAGS] & $FLAG_WPAIR))
1236 # This function should be indexed
1237 push (@index_entries_list, @$_[$EXPORT_IMPNAME].",".@$_[$EXPORT_IMPNAME]);
1240 elsif (@$_[$EXPORT_CALL] eq "equate" || @$_[$EXPORT_CALL] eq "variable")
1242 $line = @$_[$EXPORT_EXPNAME]." (data)";
1244 else
1246 # A function
1247 if (@$_[$EXPORT_FLAGS] & $FLAG_DOCUMENTED)
1249 # Documented
1250 $line = @$_[$EXPORT_EXPNAME]." (implemented as ".@$_[$EXPORT_IMPNAME]."())";
1251 if (@$_[$EXPORT_EXPNAME] ne @$_[$EXPORT_IMPNAME])
1253 $line = @$_[$EXPORT_EXPNAME]." (implemented as ".@$_[$EXPORT_IMPNAME]."())";
1255 else
1257 $line = @$_[$EXPORT_EXPNAME]."()";
1259 if (!(@$_[$EXPORT_FLAGS] & $FLAG_WPAIR))
1261 # This function should be indexed
1262 push (@index_entries_list, @$_[$EXPORT_EXPNAME].",".@$_[$EXPORT_IMPNAME]);
1265 else
1267 $line = @$_[$EXPORT_EXPNAME]." (not documented)";
1270 if ($line ne "")
1272 push (@{$comment->{TEXT}}, $line, "");
1276 # Add links to the extra documentation
1277 if (@{$spec_details->{EXTRA_COMMENTS}})
1279 push (@{$comment->{TEXT}}, "SEE ALSO");
1280 my %htmp;
1281 @{$spec_details->{EXTRA_COMMENTS}} = grep(!$htmp{$_}++, @{$spec_details->{EXTRA_COMMENTS}});
1282 for (@{$spec_details->{EXTRA_COMMENTS}})
1284 push (@{$comment->{TEXT}}, $_."()", "");
1288 # The dll entry should also be indexed
1289 push (@index_entries_list, $spec_details->{DLL_NAME}.",".$spec_details->{DLL_NAME});
1291 # Write out the document
1292 output_open_api_file($spec_details->{DLL_NAME});
1293 output_api_header($comment);
1294 output_api_comment($comment);
1295 output_api_footer($comment);
1296 output_close_api_file();
1298 # Add this dll to the database of dll names
1299 my $output_file = $opt_output_directory."/dlls.db";
1301 # Append the dllname to the output db of names
1302 open(DLLDB,">>$output_file") || die "Couldn't create $output_file\n";
1303 print DLLDB $spec_details->{DLL_NAME},"\n";
1304 close(DLLDB);
1306 if ($opt_output_format eq "s")
1308 output_sgml_dll_file($spec_details);
1309 return;
1312 if ($opt_output_format eq "x")
1314 output_xml_dll_file($spec_details);
1315 return;
1321 # OUTPUT FUNCTIONS
1322 # ----------------
1323 # Only these functions know anything about formatting for a specific
1324 # output type. The functions above work only with plain text.
1325 # This is to allow new types of output to be added easily.
1327 # Open the api file
1328 sub output_open_api_file($)
1330 my $output_name = shift;
1331 $output_name = $opt_output_directory."/".$output_name;
1333 if ($opt_output_format eq "h")
1335 $output_name = $output_name.".html";
1337 elsif ($opt_output_format eq "s")
1339 $output_name = $output_name.".sgml";
1341 elsif ($opt_output_format eq "x")
1343 $output_name = $output_name.".xml";
1345 else
1347 $output_name = $output_name.".".$opt_manual_section;
1349 open(OUTPUT,">$output_name") || die "Couldn't create file '$output_name'\n";
1352 # Close the api file
1353 sub output_close_api_file()
1355 close (OUTPUT);
1358 # Output the api file header
1359 sub output_api_header($)
1361 my $comment = shift;
1363 if ($opt_output_format eq "h")
1365 print OUTPUT "<!-- Generated file - DO NOT EDIT! -->\n";
1366 print OUTPUT "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n";
1367 print OUTPUT "<HTML>\n<HEAD>\n";
1368 print OUTPUT "<LINK REL=\"StyleSheet\" href=\"apidoc.css\" type=\"text/css\">\n";
1369 print OUTPUT "<META NAME=\"GENERATOR\" CONTENT=\"tools/c2man.pl\">\n";
1370 print OUTPUT "<META NAME=\"keywords\" CONTENT=\"Win32,Wine,API,$comment->{COMMENT_NAME}\">\n";
1371 print OUTPUT "<TITLE>Wine API: $comment->{COMMENT_NAME}</TITLE>\n</HEAD>\n<BODY>\n";
1373 elsif ($opt_output_format eq "s" || $opt_output_format eq "x")
1375 print OUTPUT "<!-- Generated file - DO NOT EDIT! -->\n",
1376 "<sect1>\n",
1377 "<title>$comment->{COMMENT_NAME}</title>\n";
1379 else
1381 print OUTPUT ".\\\" -*- nroff -*-\n.\\\" Generated file - DO NOT EDIT!\n".
1382 ".TH ",$comment->{COMMENT_NAME}," ",$opt_manual_section," \"",$date,"\" \"".
1383 "Wine API\" \"Wine API\"\n";
1387 sub output_api_footer($)
1389 if ($opt_output_format eq "h")
1391 print OUTPUT "<hr><p><i class=\"copy\">Copyright &copy ".$year." The Wine Project.".
1392 " All trademarks are the property of their respective owners.".
1393 " Visit <a href=\"http://www.winehq.org\">WineHQ</a> for license details.".
1394 " Generated $date.</i></p>\n</body>\n</html>\n";
1396 elsif ($opt_output_format eq "s" || $opt_output_format eq "x")
1398 print OUTPUT "</sect1>\n";
1399 return;
1401 else
1406 sub output_api_section_start($$)
1408 my $comment = shift;
1409 my $section_name = shift;
1411 if ($opt_output_format eq "h")
1413 print OUTPUT "\n<h2 class=\"section\">",$section_name,"</h2>\n";
1415 elsif ($opt_output_format eq "s" || $opt_output_format eq "x")
1417 print OUTPUT "<bridgehead>",$section_name,"</bridgehead>\n";
1419 else
1421 print OUTPUT "\n\.SH ",$section_name,"\n";
1425 sub output_api_section_end()
1427 # Not currently required by any output formats
1430 sub output_api_name($)
1432 my $comment = shift;
1433 my $readable_name = $comment->{COMMENT_NAME};
1434 $readable_name =~ s/-/ /g; # make section names more readable
1436 output_api_section_start($comment,"NAME");
1439 my $dll_ordinal = "";
1440 if ($comment->{ORDINAL} ne "")
1442 $dll_ordinal = "(".$comment->{DLL_NAME}.".".$comment->{ORDINAL}.")";
1444 if ($opt_output_format eq "h")
1446 print OUTPUT "<p><b class=\"func_name\">",$readable_name,
1447 "</b>&nbsp;&nbsp;<i class=\"dll_ord\">",
1448 ,$dll_ordinal,"</i></p>\n";
1450 elsif ($opt_output_format eq "s" || $opt_output_format eq "x")
1452 print OUTPUT "<para>\n <command>",$readable_name,"</command> <emphasis>",
1453 $dll_ordinal,"</emphasis>\n</para>\n";
1455 else
1457 print OUTPUT "\\fB",$readable_name,"\\fR ",$dll_ordinal;
1460 output_api_section_end();
1463 sub output_api_synopsis($)
1465 my $comment = shift;
1466 my @fmt;
1468 output_api_section_start($comment,"SYNOPSIS");
1470 if ($opt_output_format eq "h")
1472 print OUTPUT "<pre class=\"proto\">\n ", $comment->{RETURNS}," ",$comment->{COMMENT_NAME},"\n (\n";
1473 @fmt = ("", "\n", "<tt class=\"param\">", "</tt>");
1475 elsif ($opt_output_format eq "s" || $opt_output_format eq "x")
1477 print OUTPUT "<screen>\n ",$comment->{RETURNS}," ",$comment->{COMMENT_NAME},"\n (\n";
1478 @fmt = ("", "\n", "<emphasis>", "</emphasis>");
1480 else
1482 print OUTPUT $comment->{RETURNS}," ",$comment->{COMMENT_NAME},"\n (\n";
1483 @fmt = ("", "\n", "\\fI", "\\fR");
1486 # Since our prototype is output in a pre-formatted block, line up the
1487 # parameters and parameter comments in the same column.
1489 # First calculate where the columns should start
1490 my $biggest_length = 0;
1491 for(my $i=0; $i < @{$comment->{PROTOTYPE}}; $i++)
1493 my $line = ${$comment->{PROTOTYPE}}[$i];
1494 if ($line =~ /(.+?)([A-Za-z_][A-Za-z_0-9]*)$/)
1496 my $length = length $1;
1497 if ($length > $biggest_length)
1499 $biggest_length = $length;
1504 # Now pad the string with blanks
1505 for(my $i=0; $i < @{$comment->{PROTOTYPE}}; $i++)
1507 my $line = ${$comment->{PROTOTYPE}}[$i];
1508 if ($line =~ /(.+?)([A-Za-z_][A-Za-z_0-9]*)$/)
1510 my $pad_len = $biggest_length - length $1;
1511 my $padding = " " x ($pad_len);
1512 ${$comment->{PROTOTYPE}}[$i] = $1.$padding.$2;
1516 for(my $i=0; $i < @{$comment->{PROTOTYPE}}; $i++)
1518 # Format the parameter name
1519 my $line = ${$comment->{PROTOTYPE}}[$i];
1520 my $comma = ($i == @{$comment->{PROTOTYPE}}-1) ? "" : ",";
1521 $line =~ s/(.+?)([A-Za-z_][A-Za-z_0-9]*)$/ $fmt[0]$1$fmt[2]$2$fmt[3]$comma$fmt[1]/;
1522 print OUTPUT $line;
1525 if ($opt_output_format eq "h")
1527 print OUTPUT " )\n</pre>\n";
1529 elsif ($opt_output_format eq "s" || $opt_output_format eq "x")
1531 print OUTPUT " )\n</screen>\n";
1533 else
1535 print OUTPUT " )\n";
1538 output_api_section_end();
1541 sub output_api_comment($)
1543 my $comment = shift;
1544 my $open_paragraph = 0;
1545 my $open_raw = 0;
1546 my $param_docs = 0;
1547 my @fmt;
1549 if ($opt_output_format eq "h")
1551 @fmt = ("<p>", "</p>\n", "<tt class=\"const\">", "</tt>", "<b class=\"emp\">", "</b>",
1552 "<tt class=\"coderef\">", "</tt>", "<tt class=\"param\">", "</tt>",
1553 "<i class=\"in_out\">", "</i>", "<pre class=\"raw\">\n", "</pre>\n",
1554 "<table class=\"tab\"><colgroup><col><col><col></colgroup><tbody>\n",
1555 "</tbody></table>\n","<tr><td>","</td></tr>\n","</td>","</td><td>");
1557 elsif ($opt_output_format eq "s" || $opt_output_format eq "x")
1559 @fmt = ("<para>\n","\n</para>\n","<constant>","</constant>","<emphasis>","</emphasis>",
1560 "<command>","</command>","<constant>","</constant>","<emphasis>","</emphasis>",
1561 "<screen>\n","</screen>\n",
1562 "<informaltable frame=\"none\">\n<tgroup cols=\"3\">\n<tbody>\n",
1563 "</tbody>\n</tgroup>\n</informaltable>\n","<row><entry>","</entry></row>\n",
1564 "</entry>","</entry><entry>");
1566 else
1568 @fmt = ("\.PP\n", "\n", "\\fB", "\\fR", "\\fB", "\\fR", "\\fB", "\\fR", "\\fI", "\\fR",
1569 "\\fB", "\\fR ", "", "", "", "","","\n.PP\n","","");
1572 # Extract the parameter names
1573 my @parameter_names;
1574 for (@{$comment->{PROTOTYPE}})
1576 if ( /(.+?)([A-Za-z_][A-Za-z_0-9]*)$/ )
1578 push (@parameter_names, $2);
1582 for (@{$comment->{TEXT}})
1584 if ($opt_output_format eq "h" || $opt_output_format eq "s" || $opt_output_format eq "x")
1586 # Map special characters
1587 s/\&/\&amp;/g;
1588 s/\</\&lt;/g;
1589 s/\>/\&gt;/g;
1590 s/\([Cc]\)/\&copy;/g;
1591 s/\(tm\)/&#174;/;
1594 if ( s/^\|// )
1596 # Raw output
1597 if ($open_raw == 0)
1599 if ($open_paragraph == 1)
1601 # Close the open paragraph
1602 print OUTPUT $fmt[1];
1603 $open_paragraph = 0;
1605 # Start raw output
1606 print OUTPUT $fmt[12];
1607 $open_raw = 1;
1609 if ($opt_output_format eq "")
1611 print OUTPUT ".br\n"; # Prevent 'man' running these lines together
1613 print OUTPUT $_,"\n";
1615 else
1617 if ($opt_output_format eq "h")
1619 # Link to the file in WineHQ cvs
1620 s/^(Implemented in \")(.+?)(\"\.)/$1$2$3 http:\/\/source.winehq.org\/source\/$2/g;
1621 s/^(Declared in \")(.+?)(\"\.)/$1$2$3 http:\/\/source.winehq.org\/source\/include\/$2/g;
1623 # Highlight strings
1624 s/(\".+?\")/$fmt[2]$1$fmt[3]/g;
1625 # Highlight literal chars
1626 s/(\'.\')/$fmt[2]$1$fmt[3]/g;
1627 s/(\'.{2}\')/$fmt[2]$1$fmt[3]/g;
1628 # Highlight numeric constants
1629 s/( |\-|\+|\.|\()([0-9\-\.]+)( |\-|$|\.|\,|\*|\?|\))/$1$fmt[2]$2$fmt[3]$3/g;
1631 # Leading cases ("xxxx:","-") start new paragraphs & are emphasised
1632 # FIXME: Using bullet points for leading '-' would look nicer.
1633 if ($open_paragraph == 1 && $param_docs == 0)
1635 s/^(\-)/$fmt[1]$fmt[0]$fmt[4]$1$fmt[5]/;
1636 s/^([[A-Za-z\-]+\:)/$fmt[1]$fmt[0]$fmt[4]$1$fmt[5]/;
1638 else
1640 s/^(\-)/$fmt[4]$1$fmt[5]/;
1641 s/^([[A-Za-z\-]+\:)/$fmt[4]$1$fmt[5]/;
1644 if ($opt_output_format eq "h")
1646 # Html uses links for API calls
1647 while ( /([A-Za-z_]+[A-Za-z_0-9-]+)(\(\))/)
1649 my $link = $1;
1650 my $readable_link = $1;
1651 $readable_link =~ s/-/ /g;
1653 s/([A-Za-z_]+[A-Za-z_0-9-]+)(\(\))/<a href\=\"$link\.html\">$readable_link<\/a>/;
1655 # Index references
1656 s/\{\{(.*?)\}\}\{\{(.*?)\}\}/<a href\=\"$2\.html\">$1<\/a>/g;
1657 s/ ([A-Z_])(\(\))/<a href\=\"$1\.html\">$1<\/a>/g;
1658 # And references to COM objects (hey, they'll get documented one day)
1659 s/ (I[A-Z]{1}[A-Za-z0-9_]+) (Object|object|Interface|interface)/ <a href\=\"$1\.html\">$1<\/a> $2/g;
1660 # Convert any web addresses to real links
1661 s/(http\:\/\/)(.+?)($| )/<a href\=\"$1$2\">$2<\/a>$3/g;
1663 else
1665 if ($opt_output_format eq "")
1667 # Give the man section for API calls
1668 s/ ([A-Za-z_]+[A-Za-z_0-9-]+)\(\)/ $fmt[6]$1\($opt_manual_section\)$fmt[7]/g;
1670 else
1672 # Highlight API calls
1673 s/ ([A-Za-z_]+[A-Za-z_0-9-]+\(\))/ $fmt[6]$1$fmt[7]/g;
1676 # And references to COM objects
1677 s/ (I[A-Z]{1}[A-Za-z0-9_]+) (Object|object|Interface|interface)/ $fmt[6]$1$fmt[7] $2/g;
1680 if ($open_raw == 1)
1682 # Finish the raw output
1683 print OUTPUT $fmt[13];
1684 $open_raw = 0;
1687 if ( /^[A-Z]+$/ || /^SEE ALSO$/ )
1689 # Start of a new section
1690 if ($open_paragraph == 1)
1692 if ($param_docs == 1)
1694 print OUTPUT $fmt[17],$fmt[15];
1695 $param_docs = 0;
1697 else
1699 print OUTPUT $fmt[1];
1701 $open_paragraph = 0;
1703 output_api_section_start($comment,$_);
1704 if ( /^PARAMS$/ || /^MEMBERS$/ )
1706 print OUTPUT $fmt[14];
1707 $param_docs = 1;
1709 else
1711 #print OUTPUT $fmt[15];
1712 #$param_docs = 0;
1715 elsif ( /^$/ )
1717 # Empty line, indicating a new paragraph
1718 if ($open_paragraph == 1)
1720 if ($param_docs == 0)
1722 print OUTPUT $fmt[1];
1723 $open_paragraph = 0;
1727 else
1729 if ($param_docs == 1)
1731 if ($open_paragraph == 1)
1733 # For parameter docs, put each parameter into a new paragraph/table row
1734 print OUTPUT $fmt[17];
1735 $open_paragraph = 0;
1737 s/(\[.+\])( *)/$fmt[19]$fmt[10]$1$fmt[11]$fmt[19] /; # Format In/Out
1739 else
1741 # Within paragraph lines, prevent lines running together
1742 $_ = $_." ";
1745 # Format parameter names where they appear in the comment
1746 for my $parameter_name (@parameter_names)
1748 s/(^|[ \.\,\(\-\*])($parameter_name)($|[ \.\)\,\-\/]|(\=[^"]))/$1$fmt[8]$2$fmt[9]$3/g;
1750 # Structure dereferences include the dereferenced member
1751 s/(\-\>[A-Za-z_]+)/$fmt[8]$1$fmt[9]/g;
1752 s/(\-\&gt\;[A-Za-z_]+)/$fmt[8]$1$fmt[9]/g;
1754 if ($open_paragraph == 0)
1756 if ($param_docs == 1)
1758 print OUTPUT $fmt[16];
1760 else
1762 print OUTPUT $fmt[0];
1764 $open_paragraph = 1;
1766 # Anything in all uppercase on its own gets emphasised
1767 s/(^|[ \.\,\(\[\|\=])([A-Z]+?[A-Z0-9_]+)($|[ \.\,\*\?\|\)\=\'])/$1$fmt[6]$2$fmt[7]$3/g;
1769 print OUTPUT $_;
1773 if ($open_raw == 1)
1775 print OUTPUT $fmt[13];
1777 if ($param_docs == 1 && $open_paragraph == 1)
1779 print OUTPUT $fmt[17];
1780 $open_paragraph = 0;
1782 if ($param_docs == 1)
1784 print OUTPUT $fmt[15];
1786 if ($open_paragraph == 1)
1788 print OUTPUT $fmt[1];
1792 # Create the master index file
1793 sub output_master_index_files()
1795 if ($opt_output_format eq "")
1797 return; # No master index for man pages
1800 if ($opt_output_format eq "h")
1802 # Append the index entries to the output db of index entries
1803 my $output_file = $opt_output_directory."/index.db";
1804 open(INDEXDB,">>$output_file") || die "Couldn't create $output_file\n";
1805 for (@index_entries_list)
1807 $_ =~ s/A\,/\,/;
1808 print INDEXDB $_."\n";
1810 close(INDEXDB);
1813 # Use the comment output functions for consistency
1814 my $comment =
1816 FILE => "",
1817 COMMENT_NAME => "The Wine API Guide",
1818 ALT_NAME => "The Wine API Guide",
1819 DLL_NAME => "",
1820 ORDINAL => "",
1821 RETURNS => "",
1822 PROTOTYPE => [],
1823 TEXT => [],
1826 if ($opt_output_format eq "s" || $opt_output_format eq "x")
1828 $comment->{COMMENT_NAME} = "Introduction";
1829 $comment->{ALT_NAME} = "Introduction",
1831 elsif ($opt_output_format eq "h")
1833 @{$comment->{TEXT}} = (
1834 "NAME",
1835 $comment->{COMMENT_NAME},
1836 "INTRODUCTION",
1840 # Create the initial comment text
1841 push (@{$comment->{TEXT}},
1842 "This document describes the API calls made available",
1843 "by Wine. They are grouped by the dll that exports them.",
1845 "Please do not edit this document, since it is generated automatically",
1846 "from the Wine source code tree. Details on updating this documentation",
1847 "are given in the \"Wine Developers Guide\".",
1848 "CONTRIBUTORS",
1849 "API documentation is generally written by the person who ",
1850 "implements a given API call. Authors of each dll are listed in the overview ",
1851 "section for that dll. Additional contributors who have updated source files ",
1852 "but have not entered their names in a copyright statement are noted by an ",
1853 "entry in the file \"Changelog\" from the Wine source code distribution.",
1857 # Read in all dlls from the database of dll names
1858 my $input_file = $opt_output_directory."/dlls.db";
1859 my @dlls = `cat $input_file|sort|uniq`;
1861 if ($opt_output_format eq "h")
1863 # HTML gets a list of all the dlls and an index. For docbook the index creates this for us
1864 push (@{$comment->{TEXT}},
1865 "INDEX",
1866 "For an alphabetical listing of the functions available, please click the ",
1867 "first letter of the functions name below:","",
1868 "[ _(), A(), B(), C(), D(), E(), F(), G(), H(), ".
1869 "I(), J(), K(), L(), M(), N(), O(), P(), Q(), ".
1870 "R(), S(), T(), U(), V(), W(), X(), Y(), Z() ]", "",
1871 "DLLS",
1872 "Each dll provided by Wine is documented individually. The following dlls are provided :",
1875 # Add the dlls to the comment
1876 for (@dlls)
1878 $_ =~ s/(\..*)?\n/\(\)/;
1879 push (@{$comment->{TEXT}}, $_, "");
1881 output_open_api_file("index");
1883 elsif ($opt_output_format eq "s" || $opt_output_format eq "x")
1885 # Just write this as the initial blurb, with a chapter heading
1886 output_open_api_file("blurb");
1887 print OUTPUT "<chapter id =\"blurb\">\n<title>Introduction to The Wine API Guide</title>\n"
1890 # Write out the document
1891 output_api_header($comment);
1892 output_api_comment($comment);
1893 output_api_footer($comment);
1894 if ($opt_output_format eq "s" || $opt_output_format eq "x")
1896 print OUTPUT "</chapter>\n" # finish the chapter
1898 output_close_api_file();
1900 if ($opt_output_format eq "s")
1902 output_sgml_master_file(\@dlls);
1903 return;
1905 if ($opt_output_format eq "x")
1907 output_xml_master_file(\@dlls);
1908 return;
1910 if ($opt_output_format eq "h")
1912 output_html_index_files();
1913 output_html_stylesheet();
1914 return;
1918 # Write the master wine-api.xml, linking it to each dll.
1919 sub output_xml_master_file($)
1921 my $dlls = shift;
1923 output_open_api_file("wine-api");
1924 print OUTPUT "<?xml version='1.0'?>";
1925 print OUTPUT "<!-- Generated file - DO NOT EDIT! -->\n";
1926 print OUTPUT "<!DOCTYPE book PUBLIC \"-//OASIS//DTD DocBook V5.0/EN\" ";
1927 print OUTPUT " \"http://www.docbook.org/xml/5.0/dtd/docbook.dtd\" [\n\n";
1928 print OUTPUT "<!ENTITY blurb SYSTEM \"blurb.xml\">\n";
1930 # List the entities
1931 for (@$dlls)
1933 $_ =~ s/(\..*)?\n//;
1934 print OUTPUT "<!ENTITY ",$_," SYSTEM \"",$_,".xml\">\n"
1937 print OUTPUT "]>\n\n<book id=\"index\">\n<bookinfo><title>The Wine API Guide</title></bookinfo>\n\n";
1938 print OUTPUT " &blurb;\n";
1940 for (@$dlls)
1942 print OUTPUT " &",$_,";\n"
1944 print OUTPUT "\n\n</book>\n";
1946 output_close_api_file();
1949 # Write the master wine-api.sgml, linking it to each dll.
1950 sub output_sgml_master_file($)
1952 my $dlls = shift;
1954 output_open_api_file("wine-api");
1955 print OUTPUT "<!-- Generated file - DO NOT EDIT! -->\n";
1956 print OUTPUT "<!doctype book PUBLIC \"-//OASIS//DTD DocBook V3.1//EN\" [\n\n";
1957 print OUTPUT "<!entity blurb SYSTEM \"blurb.sgml\">\n";
1959 # List the entities
1960 for (@$dlls)
1962 $_ =~ s/(\..*)?\n//;
1963 print OUTPUT "<!entity ",$_," SYSTEM \"",$_,".sgml\">\n"
1966 print OUTPUT "]>\n\n<book id=\"index\">\n<bookinfo><title>The Wine API Guide</title></bookinfo>\n\n";
1967 print OUTPUT " &blurb;\n";
1969 for (@$dlls)
1971 print OUTPUT " &",$_,";\n"
1973 print OUTPUT "\n\n</book>\n";
1975 output_close_api_file();
1978 # Produce the sgml for the dll chapter from the generated files
1979 sub output_sgml_dll_file($)
1981 my $spec_details = shift;
1983 # Make a list of all the documentation files to include
1984 my $exports = $spec_details->{EXPORTS};
1985 my @source_files = ();
1986 for (@$exports)
1988 # @$_ => ordinal, call convention, exported name, implementation name, documented;
1989 if (@$_[$EXPORT_CALL] ne "forward" && @$_[$EXPORT_CALL] ne "extern" &&
1990 @$_[$EXPORT_CALL] ne "stub" && @$_[$EXPORT_CALL] ne "equate" &&
1991 @$_[$EXPORT_CALL] ne "variable" && @$_[$EXPORT_CALL] ne "fake" &&
1992 @$_[$EXPORT_FLAGS] & $FLAG_DOCUMENTED)
1994 # A documented function
1995 push (@source_files,@$_[$EXPORT_IMPNAME]);
1999 push (@source_files,@{$spec_details->{EXTRA_COMMENTS}});
2001 @source_files = sort @source_files;
2003 # create a new chapter for this dll
2004 my $tmp_name = $opt_output_directory."/".$spec_details->{DLL_NAME}.".tmp";
2005 open(OUTPUT,">$tmp_name") || die "Couldn't create $tmp_name\n";
2006 print OUTPUT "<chapter>\n<title>$spec_details->{DLL_NAME}</title>\n";
2007 output_close_api_file();
2009 # Add the sorted documentation, cleaning up as we go
2010 `cat $opt_output_directory/$spec_details->{DLL_NAME}.sgml >>$tmp_name`;
2011 for (@source_files)
2013 `cat $opt_output_directory/$_.sgml >>$tmp_name`;
2014 `rm -f $opt_output_directory/$_.sgml`;
2017 # close the chapter, and overwite the dll source
2018 open(OUTPUT,">>$tmp_name") || die "Couldn't create $tmp_name\n";
2019 print OUTPUT "</chapter>\n";
2020 close OUTPUT;
2021 `mv $tmp_name $opt_output_directory/$spec_details->{DLL_NAME}.sgml`;
2024 # Produce the xml for the dll chapter from the generated files
2025 sub output_xml_dll_file($)
2027 my $spec_details = shift;
2029 # Make a list of all the documentation files to include
2030 my $exports = $spec_details->{EXPORTS};
2031 my @source_files = ();
2032 for (@$exports)
2034 # @$_ => ordinal, call convention, exported name, implementation name, documented;
2035 if (@$_[$EXPORT_CALL] ne "forward" && @$_[$EXPORT_CALL] ne "extern" &&
2036 @$_[$EXPORT_CALL] ne "stub" && @$_[$EXPORT_CALL] ne "equate" &&
2037 @$_[$EXPORT_CALL] ne "variable" && @$_[$EXPORT_CALL] ne "fake" &&
2038 @$_[$EXPORT_FLAGS] & $FLAG_DOCUMENTED)
2040 # A documented function
2041 push (@source_files,@$_[$EXPORT_IMPNAME]);
2045 push (@source_files,@{$spec_details->{EXTRA_COMMENTS}});
2047 @source_files = sort @source_files;
2049 # create a new chapter for this dll
2050 my $tmp_name = $opt_output_directory."/".$spec_details->{DLL_NAME}.".tmp";
2051 open(OUTPUT,">$tmp_name") || die "Couldn't create $tmp_name\n";
2052 print OUTPUT "<?xml version='1.0' encoding='UTF-8'?>\n<chapter>\n<title>$spec_details->{DLL_NAME}</title>\n";
2053 output_close_api_file();
2055 # Add the sorted documentation, cleaning up as we go
2056 `cat $opt_output_directory/$spec_details->{DLL_NAME}.xml >>$tmp_name`;
2057 for (@source_files)
2059 `cat $opt_output_directory/$_.xml >>$tmp_name`;
2060 `rm -f $opt_output_directory/$_.xml`;
2063 # close the chapter, and overwite the dll source
2064 open(OUTPUT,">>$tmp_name") || die "Couldn't create $tmp_name\n";
2065 print OUTPUT "</chapter>\n";
2066 close OUTPUT;
2067 `mv $tmp_name $opt_output_directory/$spec_details->{DLL_NAME}.xml`;
2070 # Write the html index files containing the function names
2071 sub output_html_index_files()
2073 if ($opt_output_format ne "h")
2075 return;
2078 my @letters = ('_', 'A' .. 'Z');
2080 # Read in all functions
2081 my $input_file = $opt_output_directory."/index.db";
2082 my @funcs = `cat $input_file|sort|uniq`;
2084 for (@letters)
2086 my $letter = $_;
2087 my $comment =
2089 FILE => "",
2090 COMMENT_NAME => "",
2091 ALT_NAME => "",
2092 DLL_NAME => "",
2093 ORDINAL => "",
2094 RETURNS => "",
2095 PROTOTYPE => [],
2096 TEXT => [],
2099 $comment->{COMMENT_NAME} = $letter." Functions";
2100 $comment->{ALT_NAME} = $letter." Functions";
2102 push (@{$comment->{TEXT}},
2103 "NAME",
2104 $comment->{COMMENT_NAME},
2105 "FUNCTIONS"
2108 # Add the functions to the comment
2109 for (@funcs)
2111 my $first_char = substr ($_, 0, 1);
2112 $first_char = uc $first_char;
2114 if ($first_char eq $letter)
2116 my $name = $_;
2117 my $file;
2118 $name =~ s/(^.*?)\,(.*?)\n/$1/;
2119 $file = $2;
2120 push (@{$comment->{TEXT}}, "{{".$name."}}{{".$file."}}","");
2124 # Write out the document
2125 output_open_api_file($letter);
2126 output_api_header($comment);
2127 output_api_comment($comment);
2128 output_api_footer($comment);
2129 output_close_api_file();
2133 # Output the stylesheet for HTML output
2134 sub output_html_stylesheet()
2136 if ($opt_output_format ne "h")
2138 return;
2141 my $css;
2142 ($css = <<HERE_TARGET) =~ s/^\s+//gm;
2144 * Default styles for Wine HTML Documentation.
2146 * This style sheet should be altered to suit your needs/taste.
2148 BODY { /* Page body */
2149 background-color: white;
2150 color: black;
2151 font-family: Tahoma,sans-serif;
2152 font-style: normal;
2153 font-size: 10pt;
2155 a:link { color: #4444ff; } /* Links */
2156 a:visited { color: #333377 }
2157 a:active { color: #0000dd }
2158 H2.section { /* Section Headers */
2159 font-family: sans-serif;
2160 color: #777777;
2161 background-color: #F0F0FE;
2162 margin-left: 0.2in;
2163 margin-right: 1.0in;
2165 b.func_name { /* Function Name */
2166 font-size: 10pt;
2167 font-style: bold;
2169 i.dll_ord { /* Italicised DLL+ordinal */
2170 color: #888888;
2171 font-family: sans-serif;
2172 font-size: 8pt;
2174 p { /* Paragraphs */
2175 margin-left: 0.5in;
2176 margin-right: 0.5in;
2178 table { /* tables */
2179 margin-left: 0.5in;
2180 margin-right: 0.5in;
2182 pre.proto /* API Function prototype */
2184 border-style: solid;
2185 border-width: 1px;
2186 border-color: #777777;
2187 background-color: #F0F0BB;
2188 color: black;
2189 font-size: 10pt;
2190 vertical-align: top;
2191 margin-left: 0.5in;
2192 margin-right: 1.0in;
2194 pre.raw { /* Raw text output */
2195 margin-left: 0.6in;
2196 margin-right: 1.1in;
2197 background-color: #8080DC;
2199 tt.param { /* Parameter name */
2200 font-style: italic;
2201 color: blue;
2203 tt.const { /* Constant */
2204 color: red;
2206 i.in_out { /* In/Out */
2207 font-size: 8pt;
2208 color: grey;
2210 tt.coderef { /* Code in description text */
2211 color: darkgreen;
2213 b.emp /* Emphasis */ {
2214 font-style: bold;
2215 color: darkblue;
2217 i.footer { /* Footer */
2218 font-family: sans-serif;
2219 font-size: 6pt;
2220 color: darkgrey;
2222 HERE_TARGET
2224 my $output_file = "$opt_output_directory/apidoc.css";
2225 open(CSS,">$output_file") || die "Couldn't create the file $output_file\n";
2226 print CSS $css;
2227 close(CSS);
2231 sub usage()
2233 print "\nCreate API Documentation from Wine source code.\n\n",
2234 "Usage: c2man.pl [options] {-w <spec>} {-I <include>} {<source>}\n",
2235 "Where: <spec> is a .spec file giving a DLL's exports.\n",
2236 " <include> is an include directory used by the DLL.\n",
2237 " <source> is a source file of the DLL.\n",
2238 " The above can be given multiple times on the command line, as appropriate.\n",
2239 "Options:\n",
2240 " -Th : Output HTML instead of a man page\n",
2241 " -Ts : Output SGML (DocBook source) instead of a man page\n",
2242 " -C <dir> : Source directory, to find source files if they are not found in the\n",
2243 " current directory. Default is \"",$opt_source_dir,"\"\n",
2244 " -R <dir> : Root of build directory, default is \"",$opt_wine_root_dir,"\"\n",
2245 " -o <dir> : Create output in <dir>, default is \"",$opt_output_directory,"\"\n",
2246 " -s <sect>: Set manual section to <sect>, default is ",$opt_manual_section,"\n",
2247 " -e : Output \"FIXME\" documentation from empty comments.\n",
2248 " -v : Verbosity. Can be given more than once for more detail.\n";
2253 # Main
2256 # Print usage if we're called with no args
2257 if( @ARGV == 0)
2259 usage();
2262 # Process command line options
2263 while(defined($_ = shift @ARGV))
2265 if( s/^-// )
2267 # An option.
2268 for ($_)
2270 /^o$/ && do { $opt_output_directory = shift @ARGV; last; };
2271 s/^S// && do { $opt_manual_section = $_; last; };
2272 /^Th$/ && do { $opt_output_format = "h"; last; };
2273 /^Ts$/ && do { $opt_output_format = "s"; last; };
2274 /^Tx$/ && do { $opt_output_format = "x"; last; };
2275 /^v$/ && do { $opt_verbose++; last; };
2276 /^e$/ && do { $opt_output_empty = 1; last; };
2277 /^L$/ && do { last; };
2278 /^w$/ && do { @opt_spec_file_list = (@opt_spec_file_list, shift @ARGV); last; };
2279 s/^I// && do { if ($_ ne ".") {
2280 foreach my $include (`find $_/./ -type d ! -name tests`) {
2281 $include =~ s/\n//;
2282 $include = $include."/*.h";
2283 $include =~ s/\/\//\//g;
2284 my $have_headers = `ls $include >/dev/null 2>&1`;
2285 if ($? >> 8 == 0) { @opt_header_file_list = (@opt_header_file_list, $include); }
2288 last;
2290 s/^C// && do {
2291 if ($_ ne "") { $opt_source_dir = $_; }
2292 last;
2294 s/^R// && do { if ($_ =~ /^\//) { $opt_wine_root_dir = $_; }
2295 else { $opt_wine_root_dir = `cd $pwd/$_ && pwd`; }
2296 $opt_wine_root_dir =~ s/\n//;
2297 $opt_wine_root_dir =~ s/\/\//\//g;
2298 if (! $opt_wine_root_dir =~ /\/$/ ) { $opt_wine_root_dir = $opt_wine_root_dir."/"; };
2299 last;
2301 die "Unrecognised option $_\n";
2304 else
2306 # A source file.
2307 push (@opt_source_file_list, $_);
2311 # Remove duplicate include directories
2312 my %htmp;
2313 @opt_header_file_list = grep(!$htmp{$_}++, @opt_header_file_list);
2315 if ($opt_verbose > 3)
2317 print "Output dir:'".$opt_output_directory."'\n";
2318 print "Section :'".$opt_manual_section."'\n";
2319 print "Format :'".$opt_output_format."'\n";
2320 print "Source dir:'".$opt_source_dir."'\n";
2321 print "Root :'".$opt_wine_root_dir."'\n";
2322 print "Spec files:'@opt_spec_file_list'\n";
2323 print "Includes :'@opt_header_file_list'\n";
2324 print "Sources :'@opt_source_file_list'\n";
2327 if (@opt_spec_file_list == 0)
2329 exit 0; # Don't bother processing non-dll files
2332 # Make sure the output directory exists
2333 unless (-d $opt_output_directory)
2335 mkdir $opt_output_directory or die "Cannot create directory $opt_output_directory\n";
2338 # Read in each .spec files exports and other details
2339 while(my $spec_file = shift @opt_spec_file_list)
2341 process_spec_file($spec_file);
2344 if ($opt_verbose > 3)
2346 foreach my $spec_file ( keys %spec_files )
2348 print "in '$spec_file':\n";
2349 my $spec_details = $spec_files{$spec_file}[0];
2350 my $exports = $spec_details->{EXPORTS};
2351 for (@$exports)
2353 print @$_[$EXPORT_ORDINAL].",".@$_[$EXPORT_CALL].", ".
2354 @$_[$EXPORT_EXPNAME].",".@$_[$EXPORT_IMPNAME]."\n";
2359 # Extract and output the comments from each source file
2360 while(defined($_ = shift @opt_source_file_list))
2362 process_source_file($_);
2365 # Write the index files for each spec
2366 process_index_files();
2368 # Write the master index file
2369 output_master_index_files();
2371 exit 0;