rpcrt4: Add a wrapper for NdrClientCall2 to isolate calling convention differences.
[wine.git] / tools / c2man.pl
blob9c674885fc275764aa9cc48619b884b981d63460
1 #! /usr/bin/perl -w
3 # Generate API documentation. See http://www.winehq.org/docs/winedev-guide/api-docs for details.
5 # Copyright (C) 2000 Mike McCormack
6 # Copyright (C) 2003 Jon Griffiths
8 # This library is free software; you can redistribute it and/or
9 # modify it under the terms of the GNU Lesser General Public
10 # License as published by the Free Software Foundation; either
11 # version 2.1 of the License, or (at your option) any later version.
13 # This library is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 # Lesser General Public License for more details.
18 # You should have received a copy of the GNU Lesser General Public
19 # License along with this library; if not, write to the Free Software
20 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 # TODO
23 # Consolidate A+W pairs together, and only write one doc, without the suffix
24 # Implement automatic docs of structs/defines in headers
25 # SGML gurus - feel free to smarten up the SGML.
26 # Add any other relevant information for the dll - imports etc
27 # Should we have a special output mode for WineHQ?
29 use strict;
30 use bytes;
32 # Function flags. most of these come from the spec flags
33 my $FLAG_DOCUMENTED = 1;
34 my $FLAG_NONAME = 2;
35 my $FLAG_I386 = 4;
36 my $FLAG_REGISTER = 8;
37 my $FLAG_APAIR = 16; # The A version of a matching W function
38 my $FLAG_WPAIR = 32; # The W version of a matching A function
39 my $FLAG_64PAIR = 64; # The 64 bit version of a matching 32 bit function
41 # Export list slot labels.
42 my $EXPORT_ORDINAL = 0; # Ordinal.
43 my $EXPORT_CALL = 1; # Call type.
44 my $EXPORT_EXPNAME = 2; # Export name.
45 my $EXPORT_IMPNAME = 3; # Implementation name.
46 my $EXPORT_FLAGS = 4; # Flags - see above.
48 # Options
49 my $opt_output_directory = "man3w"; # All default options are for nroff (man pages)
50 my $opt_manual_section = "3w";
51 my $opt_source_dir = "";
52 my $opt_wine_root_dir = "";
53 my $opt_output_format = ""; # '' = nroff, 'h' = html, 's' = sgml, 'x' = xml
54 my $opt_output_empty = 0; # Non-zero = Create 'empty' comments (for every implemented function)
55 my $opt_fussy = 1; # Non-zero = Create only if we have a RETURNS section
56 my $opt_verbose = 0; # >0 = verbosity. Can be given multiple times (for debugging)
57 my @opt_header_file_list = ();
58 my @opt_spec_file_list = ();
59 my @opt_source_file_list = ();
61 # All the collected details about all the .spec files being processed
62 my %spec_files;
63 # All the collected details about all the source files being processed
64 my %source_files;
65 # All documented functions that are to be placed in the index
66 my @index_entries_list = ();
68 # useful globals
69 my $pwd = `pwd`."/";
70 $pwd =~ s/\n//;
71 my @datetime = localtime;
72 my @months = ( "Jan", "Feb", "Mar", "Apr", "May", "Jun",
73 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" );
74 my $year = $datetime[5] + 1900;
75 my $date = "$months[$datetime[4]] $year";
78 sub output_api_comment($);
79 sub output_api_footer($);
80 sub output_api_header($);
81 sub output_api_name($);
82 sub output_api_synopsis($);
83 sub output_close_api_file();
84 sub output_comment($);
85 sub output_html_index_files();
86 sub output_html_stylesheet();
87 sub output_open_api_file($);
88 sub output_sgml_dll_file($);
89 sub output_xml_dll_file($);
90 sub output_sgml_master_file($);
91 sub output_xml_master_file($);
92 sub output_spec($);
93 sub process_comment($);
94 sub process_extra_comment($);
97 # Generate the list of exported entries for the dll
98 sub process_spec_file($)
100 my $spec_name = shift;
101 my ($dll_name, $dll_ext) = split(/\./, $spec_name);
102 $dll_ext = "dll" if ( $dll_ext eq "spec" );
103 my $uc_dll_name = uc $dll_name;
105 my $spec_details =
107 NAME => $spec_name,
108 DLL_NAME => $dll_name,
109 DLL_EXT => $dll_ext,
110 NUM_EXPORTS => 0,
111 NUM_STUBS => 0,
112 NUM_FUNCS => 0,
113 NUM_FORWARDS => 0,
114 NUM_VARS => 0,
115 NUM_DOCS => 0,
116 CONTRIBUTORS => [ ],
117 SOURCES => [ ],
118 DESCRIPTION => [ ],
119 EXPORTS => [ ],
120 EXPORTED_NAMES => { },
121 IMPLEMENTATION_NAMES => { },
122 EXTRA_COMMENTS => [ ],
123 CURRENT_EXTRA => [ ] ,
126 if ($opt_verbose > 0)
128 print "Processing ".$spec_name."\n";
131 # We allow opening to fail just to cater for the peculiarities of
132 # the Wine build system. This doesn't hurt, in any case
133 open(SPEC_FILE, "<$spec_name")
134 || (($opt_source_dir ne "")
135 && open(SPEC_FILE, "<$opt_source_dir/$spec_name"))
136 || return;
138 while(<SPEC_FILE>)
140 s/^\s+//; # Strip leading space
141 s/\s+\n$/\n/; # Strip trailing space
142 s/\s+/ /g; # Strip multiple tabs & spaces to a single space
143 s/\s*#.*//; # Strip comments
144 s/\(.*\)/ /; # Strip arguments
145 s/\s+/ /g; # Strip multiple tabs & spaces to a single space (again)
146 s/\n$//; # Strip newline
148 my $flags = 0;
149 if( /\-noname/ )
151 $flags |= $FLAG_NONAME;
153 if( /\-i386/ )
155 $flags |= $FLAG_I386;
157 if( /\-register/ )
159 $flags |= $FLAG_REGISTER;
161 s/ \-[a-z0-9=_]+//g; # Strip flags
163 if( /^(([0-9]+)|@) / )
165 # This line contains an exported symbol
166 my ($ordinal, $call_convention, $exported_name, $implementation_name) = split(' ');
168 for ($call_convention)
170 /^(cdecl|stdcall|varargs|pascal)$/
171 && do { $spec_details->{NUM_FUNCS}++; last; };
172 /^(variable|equate)$/
173 && do { $spec_details->{NUM_VARS}++; last; };
174 /^(extern)$/
175 && do { $spec_details->{NUM_FORWARDS}++; last; };
176 /^stub$/ && do { $spec_details->{NUM_STUBS}++; last; };
177 if ($opt_verbose > 0)
179 print "Warning: didn't recognise convention \'",$call_convention,"'\n";
181 last;
184 # Convert ordinal only names so we can find them later
185 if ($exported_name eq "@")
187 $exported_name = $uc_dll_name.'_'.$ordinal;
189 if (!defined($implementation_name))
191 $implementation_name = $exported_name;
193 if ($implementation_name eq "")
195 $implementation_name = $exported_name;
198 if ($implementation_name =~ /(.*?)\./)
200 $call_convention = "forward"; # Referencing a function from another dll
201 $spec_details->{NUM_FUNCS}--;
202 $spec_details->{NUM_FORWARDS}++;
205 # Add indices for the exported and implementation names
206 $spec_details->{EXPORTED_NAMES}{$exported_name} = $spec_details->{NUM_EXPORTS};
207 if ($implementation_name ne $exported_name)
209 $spec_details->{IMPLEMENTATION_NAMES}{$exported_name} = $spec_details->{NUM_EXPORTS};
212 # Add the exported entry
213 $spec_details->{NUM_EXPORTS}++;
214 my @export = ($ordinal, $call_convention, $exported_name, $implementation_name, $flags);
215 push (@{$spec_details->{EXPORTS}},[@export]);
218 close(SPEC_FILE);
220 # Add this .spec files details to the list of .spec files
221 $spec_files{$uc_dll_name} = [$spec_details];
224 # Read each source file, extract comments, and generate API documentation if appropriate
225 sub process_source_file($)
227 my $source_file = shift;
228 my $source_details =
230 CONTRIBUTORS => [ ],
231 DEBUG_CHANNEL => "",
233 my $comment =
235 FILE => $source_file,
236 COMMENT_NAME => "",
237 ALT_NAME => "",
238 DLL_NAME => "",
239 DLL_EXT => "",
240 ORDINAL => "",
241 RETURNS => "",
242 PROTOTYPE => [],
243 TEXT => [],
245 my $parse_state = 0;
246 my $ignore_blank_lines = 1;
247 my $extra_comment = 0; # 1 if this is an extra comment, i.e its not a .spec export
249 if ($opt_verbose > 0)
251 print "Processing ".$source_file."\n";
253 open(SOURCE_FILE,"<$source_file")
254 || (($opt_source_dir ne "")
255 && open(SOURCE_FILE,"<$opt_source_dir/$source_file"))
256 || die "couldn't open ".$source_file."\n";
258 # Add this source file to the list of source files
259 $source_files{$source_file} = [$source_details];
261 while(<SOURCE_FILE>)
263 s/\n$//; # Strip newline
264 s/^\s+//; # Strip leading space
265 s/\s+$//; # Strip trailing space
266 if (! /^\*\|/ )
268 # Strip multiple tabs & spaces to a single space
269 s/\s+/ /g;
272 if ( / +Copyright *(\([Cc]\))*[0-9 \-\,\/]*([[:alpha:][:^ascii:] \.\-]+)/ )
274 # Extract a contributor to this file
275 my $contributor = $2;
276 $contributor =~ s/ *$//;
277 $contributor =~ s/^by //;
278 $contributor =~ s/\.$//;
279 $contributor =~ s/ (for .*)/ \($1\)/;
280 if ($contributor ne "")
282 if ($opt_verbose > 3)
284 print "Info: Found contributor:'".$contributor."'\n";
286 push (@{$source_details->{CONTRIBUTORS}},$contributor);
289 elsif ( /WINE_DEFAULT_DEBUG_CHANNEL\(([A-Za-z]*)\)/ )
291 # Extract the debug channel to use
292 if ($opt_verbose > 3)
294 print "Info: Found debug channel:'".$1."'\n";
296 $source_details->{DEBUG_CHANNEL} = $1;
299 if ($parse_state == 0) # Searching for a comment
301 if ( /^\/\**$/ )
303 # Found a comment start
304 $comment->{COMMENT_NAME} = "";
305 $comment->{ALT_NAME} = "";
306 $comment->{DLL_NAME} = "";
307 $comment->{ORDINAL} = "";
308 $comment->{RETURNS} = "";
309 $comment->{PROTOTYPE} = [];
310 $comment->{TEXT} = [];
311 $ignore_blank_lines = 1;
312 $extra_comment = 0;
313 $parse_state = 3;
316 elsif ($parse_state == 1) # Reading in a comment
318 if ( /^\**\// )
320 # Found the end of the comment
321 $parse_state = 2;
323 elsif ( s/^\*\|/\|/ )
325 # A line of comment not meant to be pre-processed
326 push (@{$comment->{TEXT}},$_); # Add the comment text
328 elsif ( s/^ *\** *// )
330 # A line of comment, starting with an asterisk
331 if ( /^[A-Z]+$/ || $_ eq "")
333 # This is a section start, so skip blank lines before and after it.
334 my $last_line = pop(@{$comment->{TEXT}});
335 if (defined($last_line) && $last_line ne "")
337 # Put it back
338 push (@{$comment->{TEXT}},$last_line);
340 if ( /^[A-Z]+$/ )
342 $ignore_blank_lines = 1;
344 else
346 $ignore_blank_lines = 0;
350 if ($ignore_blank_lines == 0 || $_ ne "")
352 push (@{$comment->{TEXT}},$_); # Add the comment text
355 else
357 # This isn't a well formatted comment: look for the next one
358 $parse_state = 0;
361 elsif ($parse_state == 2) # Finished reading in a comment
363 if ( /(WINAPIV|WINAPI|__cdecl|PASCAL|CALLBACK|FARPROC16)/ ||
364 /.*?\(/ )
366 # Comment is followed by a function definition
367 $parse_state = 4; # Fall through to read prototype
369 else
371 # Allow cpp directives and blank lines between the comment and prototype
372 if ($extra_comment == 1)
374 # An extra comment not followed by a function definition
375 $parse_state = 5; # Fall through to process comment
377 elsif (!/^\#/ && !/^ *$/ && !/^__ASM_GLOBAL_FUNC/)
379 # This isn't a well formatted comment: look for the next one
380 if ($opt_verbose > 1)
382 print "Info: Function '",$comment->{COMMENT_NAME},"' not followed by prototype.\n";
384 $parse_state = 0;
388 elsif ($parse_state == 3) # Reading in the first line of a comment
390 s/^ *\** *//;
391 if ( /^([\@A-Za-z0-9_]+) +(\(|\[)([A-Za-z0-9_]+)\.(([0-9]+)|@)(\)|\])\s*(.*)$/ )
393 # Found a correctly formed "ApiName (DLLNAME.Ordinal)" line.
394 if (defined ($7) && $7 ne "")
396 push (@{$comment->{TEXT}},$_); # Add the trailing comment text
398 $comment->{COMMENT_NAME} = $1;
399 $comment->{DLL_NAME} = uc $3;
400 $comment->{ORDINAL} = $4;
401 $comment->{DLL_NAME} =~ s/^KERNEL$/KRNL386/; # Too many of these to ignore, _old_ code
402 $parse_state = 1;
404 elsif ( /^([A-Za-z0-9_-]+) +\{([A-Za-z0-9_]+)\}$/ )
406 # Found a correctly formed "CommentTitle {DLLNAME}" line (extra documentation)
407 $comment->{COMMENT_NAME} = $1;
408 $comment->{DLL_NAME} = uc $2;
409 $comment->{ORDINAL} = "";
410 $extra_comment = 1;
411 $parse_state = 1;
413 else
415 # This isn't a well formatted comment: look for the next one
416 $parse_state = 0;
420 if ($parse_state == 4) # Reading in the function definition
422 # This file is used by the DLL - Make sure we get our contributors right
423 @{$spec_files{$comment->{DLL_NAME}}[0]->{CURRENT_EXTRA}} = ();
424 push (@{$spec_files{$comment->{DLL_NAME}}[0]->{SOURCES}},$comment->{FILE});
426 push (@{$comment->{PROTOTYPE}},$_);
427 # Strip comments from the line before checking for ')'
428 my $stripped_line = $_;
429 $stripped_line =~ s/ *(\/\* *)(.*?)( *\*\/ *)//;
430 if ( $stripped_line =~ /\)/ )
432 # Strip a blank last line
433 my $last_line = pop(@{$comment->{TEXT}});
434 if (defined($last_line) && $last_line ne "")
436 # Put it back
437 push (@{$comment->{TEXT}},$last_line);
440 if ($opt_output_empty != 0 && @{$comment->{TEXT}} == 0)
442 # Create a 'not implemented' comment
443 @{$comment->{TEXT}} = ("fixme: This function has not yet been documented.");
445 $parse_state = 5;
449 if ($parse_state == 5) # Processing the comment
451 # Process it, if it has any text
452 if (@{$comment->{TEXT}} > 0)
454 if ($extra_comment == 1)
456 process_extra_comment($comment);
458 else
460 @{$comment->{TEXT}} = ("DESCRIPTION", @{$comment->{TEXT}});
461 process_comment($comment);
464 elsif ($opt_verbose > 1 && $opt_output_empty == 0)
466 print "Info: Function '",$comment->{COMMENT_NAME},"' has no documentation.\n";
468 $parse_state = 0;
471 close(SOURCE_FILE);
474 # Standardise a comments text for consistency
475 sub process_comment_text($)
477 my $comment = shift;
478 my $in_params = 0;
479 my @tmp_list = ();
480 my $i = 0;
482 for (@{$comment->{TEXT}})
484 my $line = $_;
486 if ( /^\s*$/ || /^[A-Z]+$/ || /^-/ )
488 $in_params = 0;
490 if ( $in_params > 0 && !/\[/ && !/\]/ )
492 # Possibly a continuation of the parameter description
493 my $last_line = pop(@tmp_list);
494 if ( $last_line =~ /\[/ && $last_line =~ /\]/ )
496 $line = $last_line." ".$_;
498 else
500 $in_params = 0;
501 push (@tmp_list, $last_line);
504 if ( /^(PARAMS|MEMBERS)$/ )
506 $in_params = 1;
508 push (@tmp_list, $line);
511 @{$comment->{TEXT}} = @tmp_list;
513 for (@{$comment->{TEXT}})
515 if (! /^\|/ )
517 # Map I/O values. These come in too many formats to standardise now....
518 s/\[I\]|\[i\]|\[in\]|\[IN\]/\[In\] /g;
519 s/\[O\]|\[o\]|\[out\]|\[OUT\]/\[Out\]/g;
520 s/\[I\/O\]|\[I\,O\]|\[i\/o\]|\[in\/out\]|\[IN\/OUT\]/\[In\/Out\]/g;
521 # TRUE/FALSE/NULL are defines, capitilise them
522 s/True|true/TRUE/g;
523 s/False|false/FALSE/g;
524 s/Null|null/NULL/g;
525 # Preferred capitalisations
526 s/ wine| WINE/ Wine/g;
527 s/ API | api / Api /g;
528 s/ DLL | Dll / dll /g;
529 s/ URL | url / Url /g;
530 s/WIN16|win16/Win16/g;
531 s/WIN32|win32/Win32/g;
532 s/WIN64|win64/Win64/g;
533 s/ ID | id / Id /g;
534 # Grammar
535 s/([a-z])\.([A-Z])/$1\. $2/g; # Space after full stop
536 s/ \:/\:/g; # Colons to the left
537 s/ \;/\;/g; # Semi-colons too
538 # Common idioms
539 s/^See ([A-Za-z0-9_]+)\.$/See $1\(\)\./; # Referring to A version from W
540 s/^Unicode version of ([A-Za-z0-9_]+)\.$/See $1\(\)\./; # Ditto
541 s/^64\-bit version of ([A-Za-z0-9_]+)\.$/See $1\(\)\./; # Referring to 32 bit version from 64
542 s/^PARAMETERS$/PARAMS/; # Name of parameter section should be 'PARAMS'
543 # Trademarks
544 s/( |\.)(M\$|MS|Microsoft|microsoft|micro\$oft|Micro\$oft)( |\.)/$1Microsoft\(tm\)$3/g;
545 s/( |\.)(Windows|windows|windoze|winblows)( |\.)/$1Windows\(tm\)$3/g;
546 s/( |\.)(DOS|dos|msdos)( |\.)/$1MS-DOS\(tm\)$3/g;
547 s/( |\.)(UNIX|unix)( |\.)/$1Unix\(tm\)$3/g;
548 s/( |\.)(LINIX|linux)( |\.)/$1Linux\(tm\)$3/g;
549 # Abbreviations
550 s/( char )/ character /g;
551 s/( chars )/ characters /g;
552 s/( info )/ information /g;
553 s/( app )/ application /g;
554 s/( apps )/ applications /g;
555 s/( exe )/ executable /g;
556 s/( ptr )/ pointer /g;
557 s/( obj )/ object /g;
558 s/( err )/ error /g;
559 s/( bool )/ boolean /g;
560 s/( no\. )/ number /g;
561 s/( No\. )/ Number /g;
562 # Punctuation
563 if ( /\[I|\[O/ && ! /\.$/ )
565 $_ = $_."."; # Always have a full stop at the end of parameter desc.
567 elsif ($i > 0 && /^[A-Z]*$/ &&
568 !(@{$comment->{TEXT}}[$i-1] =~ /\.$/) &&
569 !(@{$comment->{TEXT}}[$i-1] =~ /\:$/))
572 if (!(@{$comment->{TEXT}}[$i-1] =~ /^[A-Z]*$/))
574 # Paragraphs always end with a full stop
575 @{$comment->{TEXT}}[$i-1] = @{$comment->{TEXT}}[$i-1].".";
579 $i++;
583 # Standardise our comment and output it if it is suitable.
584 sub process_comment($)
586 my $comment = shift;
588 # Don't process this comment if the function isn't exported
589 my $spec_details = $spec_files{$comment->{DLL_NAME}}[0];
591 if (!defined($spec_details))
593 if ($opt_verbose > 2)
595 print "Warning: Function '".$comment->{COMMENT_NAME}."' belongs to '".
596 $comment->{DLL_NAME}."' (not passed with -w): not processing it.\n";
598 return;
601 if ($comment->{COMMENT_NAME} eq "@")
603 my $found = 0;
605 # Find the name from the .spec file
606 for (@{$spec_details->{EXPORTS}})
608 if (@$_[$EXPORT_ORDINAL] eq $comment->{ORDINAL})
610 $comment->{COMMENT_NAME} = @$_[$EXPORT_EXPNAME];
611 $found = 1;
615 if ($found == 0)
617 # Create an implementation name
618 $comment->{COMMENT_NAME} = $comment->{DLL_NAME}."_".$comment->{ORDINAL};
622 my $exported_names = $spec_details->{EXPORTED_NAMES};
623 my $export_index = $exported_names->{$comment->{COMMENT_NAME}};
624 my $implementation_names = $spec_details->{IMPLEMENTATION_NAMES};
626 if (!defined($export_index))
628 # Perhaps the comment uses the implementation name?
629 $export_index = $implementation_names->{$comment->{COMMENT_NAME}};
631 if (!defined($export_index))
633 # This function doesn't appear to be exported. hmm.
634 if ($opt_verbose > 2)
636 print "Warning: Function '".$comment->{COMMENT_NAME}."' claims to belong to '".
637 $comment->{DLL_NAME}."' but is not exported by it: not processing it.\n";
639 return;
642 # When the function is exported twice we have the second name below the first
643 # (you see this a lot in ntdll, but also in some other places).
644 my $first_line = ${$comment->{TEXT}}[1];
646 if ( $first_line =~ /^(@|[A-Za-z0-9_]+) +(\(|\[)([A-Za-z0-9_]+)\.(([0-9]+)|@)(\)|\])$/ )
648 # Found a second name - mark it as documented
649 my $alt_index = $exported_names->{$1};
650 if (defined($alt_index))
652 if ($opt_verbose > 2)
654 print "Info: Found alternate name '",$1,"\n";
656 my $alt_export = @{$spec_details->{EXPORTS}}[$alt_index];
657 @$alt_export[$EXPORT_FLAGS] |= $FLAG_DOCUMENTED;
658 $spec_details->{NUM_DOCS}++;
659 ${$comment->{TEXT}}[1] = "";
663 if (@{$spec_details->{CURRENT_EXTRA}})
665 # We have an extra comment that might be related to this one
666 my $current_comment = ${$spec_details->{CURRENT_EXTRA}}[0];
667 my $current_name = $current_comment->{COMMENT_NAME};
668 if ($comment->{COMMENT_NAME} =~ /^$current_name/ && $comment->{COMMENT_NAME} ne $current_name)
670 if ($opt_verbose > 2)
672 print "Linking ",$comment->{COMMENT_NAME}," to $current_name\n";
674 # Add a reference to this comment to our extra comment
675 push (@{$current_comment->{TEXT}}, $comment->{COMMENT_NAME}."()","");
679 # We want our docs generated using the implementation name, so they are unique
680 my $export = @{$spec_details->{EXPORTS}}[$export_index];
681 $comment->{COMMENT_NAME} = @$export[$EXPORT_IMPNAME];
682 $comment->{ALT_NAME} = @$export[$EXPORT_EXPNAME];
684 # Mark the function as documented
685 $spec_details->{NUM_DOCS}++;
686 @$export[$EXPORT_FLAGS] |= $FLAG_DOCUMENTED;
688 # If we have parameter comments in the prototype, extract them
689 my @parameter_comments;
690 for (@{$comment->{PROTOTYPE}})
692 s/ *\, */\,/g; # Strip spaces from around commas
694 if ( s/ *(\/\* *)(.*?)( *\*\/ *)// ) # Strip out comment
696 my $parameter_comment = $2;
697 if (!$parameter_comment =~ /^\[/ )
699 # Add [IO] markers so we format the comment correctly
700 $parameter_comment = "[fixme] ".$parameter_comment;
702 if ( /( |\*)([A-Za-z_]{1}[A-Za-z_0-9]*)(\,|\))/ )
704 # Add the parameter name
705 $parameter_comment = $2." ".$parameter_comment;
707 push (@parameter_comments, $parameter_comment);
711 # If we extracted any prototype comments, add them to the comment text.
712 if (@parameter_comments)
714 @parameter_comments = ("PARAMS", @parameter_comments);
715 my @new_comment = ();
716 my $inserted_params = 0;
718 for (@{$comment->{TEXT}})
720 if ( $inserted_params == 0 && /^[A-Z]+$/ )
722 # Found a section header, so this is where we insert
723 push (@new_comment, @parameter_comments);
724 $inserted_params = 1;
726 push (@new_comment, $_);
728 if ($inserted_params == 0)
730 # Add them to the end
731 push (@new_comment, @parameter_comments);
733 $comment->{TEXT} = [@new_comment];
736 if ($opt_fussy == 1 && $opt_output_empty == 0)
738 # Reject any comment that doesn't have a description or a RETURNS section.
739 # This is the default for now, 'coz many comments aren't suitable.
740 my $found_returns = 0;
741 my $found_description_text = 0;
742 my $in_description = 0;
743 for (@{$comment->{TEXT}})
745 if ( /^RETURNS$/ )
747 $found_returns = 1;
748 $in_description = 0;
750 elsif ( /^DESCRIPTION$/ )
752 $in_description = 1;
754 elsif ($in_description == 1)
756 if ( !/^[A-Z]+$/ )
758 # Don't reject comments that refer to another doc (e.g. A/W)
759 if ( /^See ([A-Za-z0-9_]+)\.$/ )
761 if ($comment->{COMMENT_NAME} =~ /W$/ )
763 # This is probably a Unicode version of an Ascii function.
764 # Create the Ascii name and see if its been documented
765 my $ascii_name = $comment->{COMMENT_NAME};
766 $ascii_name =~ s/W$/A/;
768 my $ascii_export_index = $exported_names->{$ascii_name};
770 if (!defined($ascii_export_index))
772 $ascii_export_index = $implementation_names->{$ascii_name};
774 if (!defined($ascii_export_index))
776 if ($opt_verbose > 2)
778 print "Warning: Function '".$comment->{COMMENT_NAME}."' is not an A/W pair.\n";
781 else
783 my $ascii_export = @{$spec_details->{EXPORTS}}[$ascii_export_index];
784 if (@$ascii_export[$EXPORT_FLAGS] & $FLAG_DOCUMENTED)
786 # Flag these functions as an A/W pair
787 @$ascii_export[$EXPORT_FLAGS] |= $FLAG_APAIR;
788 @$export[$EXPORT_FLAGS] |= $FLAG_WPAIR;
792 $found_returns = 1;
794 elsif ( /^Unicode version of ([A-Za-z0-9_]+)\.$/ )
796 @$export[$EXPORT_FLAGS] |= $FLAG_WPAIR; # Explicitly marked as W version
797 $found_returns = 1;
799 elsif ( /^64\-bit version of ([A-Za-z0-9_]+)\.$/ )
801 @$export[$EXPORT_FLAGS] |= $FLAG_64PAIR; # Explicitly marked as 64 bit version
802 $found_returns = 1;
804 $found_description_text = 1;
806 else
808 $in_description = 0;
812 if ($found_returns == 0 || $found_description_text == 0)
814 if ($opt_verbose > 2)
816 print "Info: Function '",$comment->{COMMENT_NAME},"' has no ",
817 "description and/or RETURNS section, skipping\n";
819 $spec_details->{NUM_DOCS}--;
820 @$export[$EXPORT_FLAGS] &= ~$FLAG_DOCUMENTED;
821 return;
825 process_comment_text($comment);
827 # Strip the prototypes return value, call convention, name and brackets
828 # (This leaves it as a list of types and names, or empty for void functions)
829 my $prototype = join(" ", @{$comment->{PROTOTYPE}});
830 $prototype =~ s/ / /g;
832 if ( $prototype =~ /(WINAPIV|WINAPI|__cdecl|PASCAL|CALLBACK|FARPROC16)/ )
834 $prototype =~ s/^(.*?)\s+(WINAPIV|WINAPI|__cdecl|PASCAL|CALLBACK|FARPROC16)\s+(.*?)\(\s*(.*)/$4/;
835 $comment->{RETURNS} = $1;
837 else
839 $prototype =~ s/^(.*?)([A-Za-z0-9_]+)\s*\(\s*(.*)/$3/;
840 $comment->{RETURNS} = $1;
843 $prototype =~ s/ *\).*//; # Strip end bracket
844 $prototype =~ s/ *\* */\*/g; # Strip space around pointers
845 $prototype =~ s/ *\, */\,/g; # Strip space around commas
846 $prototype =~ s/^(void|VOID)$//; # If void, leave blank
847 $prototype =~ s/\*([A-Za-z_])/\* $1/g; # Separate pointers from parameter name
848 @{$comment->{PROTOTYPE}} = split ( /,/ ,$prototype);
850 # FIXME: If we have no parameters, make sure we have a PARAMS: None. section
852 # Find header file
853 my $h_file = "";
854 if (@$export[$EXPORT_FLAGS] & $FLAG_NONAME)
856 $h_file = "Exported by ordinal only. Use GetProcAddress() to obtain a pointer to the function.";
858 else
860 if ($comment->{COMMENT_NAME} ne "")
862 my $tmp = "grep -s -l $comment->{COMMENT_NAME} @opt_header_file_list 2>/dev/null";
863 $tmp = `$tmp`;
864 my $exit_value = $? >> 8;
865 if ($exit_value == 0)
867 $tmp =~ s/\n.*//g;
868 if ($tmp ne "")
870 $h_file = "$tmp";
871 $h_file =~ s|^.*/\./||;
875 elsif ($comment->{ALT_NAME} ne "")
877 my $tmp = "grep -s -l $comment->{ALT_NAME} @opt_header_file_list"." 2>/dev/null";
878 $tmp = `$tmp`;
879 my $exit_value = $? >> 8;
880 if ($exit_value == 0)
882 $tmp =~ s/\n.*//g;
883 if ($tmp ne "")
885 $h_file = "$tmp";
886 $h_file =~ s|^.*/\./||;
890 $h_file =~ s/^ *//;
891 $h_file =~ s/\n//;
892 if ($h_file eq "")
894 $h_file = "Not declared in a Wine header. The function is either undocumented, or missing from Wine."
896 else
898 $h_file = "Declared in \"".$h_file."\".";
902 # Find source file
903 my $c_file = $comment->{FILE};
904 if ($opt_wine_root_dir ne "")
906 my $cfile = $pwd."/".$c_file; # Current dir + file
907 $cfile =~ s/(.+)(\/.*$)/$1/; # Strip the filename
908 $cfile = `cd $cfile && pwd`; # Strip any relative parts (e.g. "../../")
909 $cfile =~ s/\n//; # Strip newline
910 my $newfile = $c_file;
911 $newfile =~ s/(.+)(\/.*$)/$2/; # Strip all but the filename
912 $cfile = $cfile."/".$newfile; # Append filename to base path
913 $cfile =~ s/$opt_wine_root_dir//; # Get rid of the root directory
914 $cfile =~ s/\/\//\//g; # Remove any double slashes
915 $cfile =~ s/^\/+//; # Strip initial directory slash
916 $c_file = $cfile;
918 $c_file = "Implemented in \"".$c_file."\".";
920 # Add the implementation details
921 push (@{$comment->{TEXT}}, "IMPLEMENTATION","",$h_file,"",$c_file);
923 if (@$export[$EXPORT_FLAGS] & $FLAG_I386)
925 push (@{$comment->{TEXT}}, "", "Available on x86 platforms only.");
927 if (@$export[$EXPORT_FLAGS] & $FLAG_REGISTER)
929 push (@{$comment->{TEXT}}, "", "This function passes one or more arguments in registers. ",
930 "For more details, please read the source code.");
932 my $source_details = $source_files{$comment->{FILE}}[0];
933 if ($source_details->{DEBUG_CHANNEL} ne "")
935 push (@{$comment->{TEXT}}, "", "Debug channel \"".$source_details->{DEBUG_CHANNEL}."\".");
938 # Write out the documentation for the API
939 output_comment($comment)
942 # process our extra comment and output it if it is suitable.
943 sub process_extra_comment($)
945 my $comment = shift;
947 my $spec_details = $spec_files{$comment->{DLL_NAME}}[0];
949 if (!defined($spec_details))
951 if ($opt_verbose > 2)
953 print "Warning: Extra comment '".$comment->{COMMENT_NAME}."' belongs to '".
954 $comment->{DLL_NAME}."' (not passed with -w): not processing it.\n";
956 return;
959 # Check first to see if this is documentation for the DLL.
960 if ($comment->{COMMENT_NAME} eq $comment->{DLL_NAME})
962 if ($opt_verbose > 2)
964 print "Info: Found DLL documentation\n";
966 for (@{$comment->{TEXT}})
968 push (@{$spec_details->{DESCRIPTION}}, $_);
970 return;
973 # Add the comment to the DLL page as a link
974 push (@{$spec_details->{EXTRA_COMMENTS}},$comment->{COMMENT_NAME});
976 # If we have a prototype, process as a regular comment
977 if (@{$comment->{PROTOTYPE}})
979 $comment->{ORDINAL} = "@";
981 # Add an index for the comment name
982 $spec_details->{EXPORTED_NAMES}{$comment->{COMMENT_NAME}} = $spec_details->{NUM_EXPORTS};
984 # Add a fake exported entry
985 $spec_details->{NUM_EXPORTS}++;
986 my ($ordinal, $call_convention, $exported_name, $implementation_name, $documented) =
987 ("@", "fake", $comment->{COMMENT_NAME}, $comment->{COMMENT_NAME}, 0);
988 my @export = ($ordinal, $call_convention, $exported_name, $implementation_name, $documented);
989 push (@{$spec_details->{EXPORTS}},[@export]);
990 @{$comment->{TEXT}} = ("DESCRIPTION", @{$comment->{TEXT}});
991 process_comment($comment);
992 return;
995 if ($opt_verbose > 0)
997 print "Processing ",$comment->{COMMENT_NAME},"\n";
1000 if (@{$spec_details->{CURRENT_EXTRA}})
1002 my $current_comment = ${$spec_details->{CURRENT_EXTRA}}[0];
1004 if ($opt_verbose > 0)
1006 print "Processing old current: ",$current_comment->{COMMENT_NAME},"\n";
1008 # Output the current comment
1009 process_comment_text($current_comment);
1010 output_open_api_file($current_comment->{COMMENT_NAME});
1011 output_api_header($current_comment);
1012 output_api_name($current_comment);
1013 output_api_comment($current_comment);
1014 output_api_footer($current_comment);
1015 output_close_api_file();
1018 if ($opt_verbose > 2)
1020 print "Setting current to ",$comment->{COMMENT_NAME},"\n";
1023 my $comment_copy =
1025 FILE => $comment->{FILE},
1026 COMMENT_NAME => $comment->{COMMENT_NAME},
1027 ALT_NAME => $comment->{ALT_NAME},
1028 DLL_NAME => $comment->{DLL_NAME},
1029 ORDINAL => $comment->{ORDINAL},
1030 RETURNS => $comment->{RETURNS},
1031 PROTOTYPE => [],
1032 TEXT => [],
1035 for (@{$comment->{TEXT}})
1037 push (@{$comment_copy->{TEXT}}, $_);
1039 # Set this comment to be the current extra comment
1040 @{$spec_details->{CURRENT_EXTRA}} = ($comment_copy);
1043 # Write a standardised comment out in the appropriate format
1044 sub output_comment($)
1046 my $comment = shift;
1048 if ($opt_verbose > 0)
1050 print "Processing ",$comment->{COMMENT_NAME},"\n";
1053 if ($opt_verbose > 4)
1055 print "--PROTO--\n";
1056 for (@{$comment->{PROTOTYPE}})
1058 print "'".$_."'\n";
1061 print "--COMMENT--\n";
1062 for (@{$comment->{TEXT} })
1064 print $_."\n";
1068 output_open_api_file($comment->{COMMENT_NAME});
1069 output_api_header($comment);
1070 output_api_name($comment);
1071 output_api_synopsis($comment);
1072 output_api_comment($comment);
1073 output_api_footer($comment);
1074 output_close_api_file();
1077 # Write out an index file for each .spec processed
1078 sub process_index_files()
1080 foreach my $spec_file (keys %spec_files)
1082 my $spec_details = $spec_files{$spec_file}[0];
1083 if (defined ($spec_details->{DLL_NAME}))
1085 if (@{$spec_details->{CURRENT_EXTRA}})
1087 # We have an unwritten extra comment, write it
1088 my $current_comment = ${$spec_details->{CURRENT_EXTRA}}[0];
1089 process_extra_comment($current_comment);
1090 @{$spec_details->{CURRENT_EXTRA}} = ();
1092 output_spec($spec_details);
1097 # Write a spec files documentation out in the appropriate format
1098 sub output_spec($)
1100 my $spec_details = shift;
1102 if ($opt_verbose > 2)
1104 print "Writing:",$spec_details->{DLL_NAME},"\n";
1107 # Use the comment output functions for consistency
1108 my $comment =
1110 FILE => $spec_details->{DLL_NAME},
1111 COMMENT_NAME => $spec_details->{DLL_NAME}.".".$spec_details->{DLL_EXT},
1112 ALT_NAME => $spec_details->{DLL_NAME},
1113 DLL_NAME => "",
1114 ORDINAL => "",
1115 RETURNS => "",
1116 PROTOTYPE => [],
1117 TEXT => [],
1119 my $total_implemented = $spec_details->{NUM_FORWARDS} + $spec_details->{NUM_VARS} +
1120 $spec_details->{NUM_FUNCS};
1121 my $percent_implemented = 0;
1122 if ($total_implemented)
1124 $percent_implemented = $total_implemented /
1125 ($total_implemented + $spec_details->{NUM_STUBS}) * 100;
1127 $percent_implemented = int($percent_implemented);
1128 my $percent_documented = 0;
1129 if ($spec_details->{NUM_DOCS})
1131 # Treat forwards and data as documented funcs for statistics
1132 $percent_documented = $spec_details->{NUM_DOCS} / $spec_details->{NUM_FUNCS} * 100;
1133 $percent_documented = int($percent_documented);
1136 # Make a list of the contributors to this DLL. Do this only for the source
1137 # files that make up the DLL, because some directories specify multiple dlls.
1138 my @contributors;
1140 for (@{$spec_details->{SOURCES}})
1142 my $source_details = $source_files{$_}[0];
1143 for (@{$source_details->{CONTRIBUTORS}})
1145 push (@contributors, $_);
1149 my %saw;
1150 @contributors = grep(!$saw{$_}++, @contributors); # remove dups, from perlfaq4 manpage
1151 @contributors = sort @contributors;
1153 # Remove duplicates and blanks
1154 for(my $i=0; $i<@contributors; $i++)
1156 if ($i > 0 && ($contributors[$i] =~ /$contributors[$i-1]/ || $contributors[$i-1] eq ""))
1158 $contributors[$i-1] = $contributors[$i];
1161 undef %saw;
1162 @contributors = grep(!$saw{$_}++, @contributors);
1164 if ($opt_verbose > 3)
1166 print "Contributors:\n";
1167 for (@contributors)
1169 print "'".$_."'\n";
1172 my $contribstring = join (", ", @contributors);
1174 # Create the initial comment text
1175 @{$comment->{TEXT}} = (
1176 "NAME",
1177 $comment->{COMMENT_NAME}
1180 # Add the description, if we have one
1181 if (@{$spec_details->{DESCRIPTION}})
1183 push (@{$comment->{TEXT}}, "DESCRIPTION");
1184 for (@{$spec_details->{DESCRIPTION}})
1186 push (@{$comment->{TEXT}}, $_);
1190 # Add the statistics and contributors
1191 push (@{$comment->{TEXT}},
1192 "STATISTICS",
1193 "Forwards: ".$spec_details->{NUM_FORWARDS},
1194 "Variables: ".$spec_details->{NUM_VARS},
1195 "Stubs: ".$spec_details->{NUM_STUBS},
1196 "Functions: ".$spec_details->{NUM_FUNCS},
1197 "Exports-Total: ".$spec_details->{NUM_EXPORTS},
1198 "Implemented-Total: ".$total_implemented." (".$percent_implemented."%)",
1199 "Documented-Total: ".$spec_details->{NUM_DOCS}." (".$percent_documented."%)",
1200 "CONTRIBUTORS",
1201 "The following people hold copyrights on the source files comprising this dll:",
1203 $contribstring,
1204 "Note: This list may not be complete.",
1205 "For a complete listing, see the Files \"AUTHORS\" and \"Changelog\" in the Wine source tree.",
1209 if ($opt_output_format eq "h")
1211 # Add the exports to the comment text
1212 push (@{$comment->{TEXT}},"EXPORTS");
1213 my $exports = $spec_details->{EXPORTS};
1214 for (@$exports)
1216 my $line = "";
1218 # @$_ => ordinal, call convention, exported name, implementation name, flags;
1219 if (@$_[$EXPORT_CALL] eq "forward")
1221 my $forward_dll = @$_[$EXPORT_IMPNAME];
1222 $forward_dll =~ s/\.(.*)//;
1223 $line = @$_[$EXPORT_EXPNAME]." (forward to ".$1."() in ".$forward_dll."())";
1225 elsif (@$_[$EXPORT_CALL] eq "extern")
1227 $line = @$_[$EXPORT_EXPNAME]." (extern)";
1229 elsif (@$_[$EXPORT_CALL] eq "stub")
1231 $line = @$_[$EXPORT_EXPNAME]." (stub)";
1233 elsif (@$_[$EXPORT_CALL] eq "fake")
1235 # Don't add this function here, it gets listed with the extra documentation
1236 if (!(@$_[$EXPORT_FLAGS] & $FLAG_WPAIR))
1238 # This function should be indexed
1239 push (@index_entries_list, @$_[$EXPORT_IMPNAME].",".@$_[$EXPORT_IMPNAME]);
1242 elsif (@$_[$EXPORT_CALL] eq "equate" || @$_[$EXPORT_CALL] eq "variable")
1244 $line = @$_[$EXPORT_EXPNAME]." (data)";
1246 else
1248 # A function
1249 if (@$_[$EXPORT_FLAGS] & $FLAG_DOCUMENTED)
1251 # Documented
1252 $line = @$_[$EXPORT_EXPNAME]." (implemented as ".@$_[$EXPORT_IMPNAME]."())";
1253 if (@$_[$EXPORT_EXPNAME] ne @$_[$EXPORT_IMPNAME])
1255 $line = @$_[$EXPORT_EXPNAME]." (implemented as ".@$_[$EXPORT_IMPNAME]."())";
1257 else
1259 $line = @$_[$EXPORT_EXPNAME]."()";
1261 if (!(@$_[$EXPORT_FLAGS] & $FLAG_WPAIR))
1263 # This function should be indexed
1264 push (@index_entries_list, @$_[$EXPORT_EXPNAME].",".@$_[$EXPORT_IMPNAME]);
1267 else
1269 $line = @$_[$EXPORT_EXPNAME]." (not documented)";
1272 if ($line ne "")
1274 push (@{$comment->{TEXT}}, $line, "");
1278 # Add links to the extra documentation
1279 if (@{$spec_details->{EXTRA_COMMENTS}})
1281 push (@{$comment->{TEXT}}, "SEE ALSO");
1282 my %htmp;
1283 @{$spec_details->{EXTRA_COMMENTS}} = grep(!$htmp{$_}++, @{$spec_details->{EXTRA_COMMENTS}});
1284 for (@{$spec_details->{EXTRA_COMMENTS}})
1286 push (@{$comment->{TEXT}}, $_."()", "");
1290 # The dll entry should also be indexed
1291 push (@index_entries_list, $spec_details->{DLL_NAME}.",".$spec_details->{DLL_NAME});
1293 # Write out the document
1294 output_open_api_file($spec_details->{DLL_NAME});
1295 output_api_header($comment);
1296 output_api_comment($comment);
1297 output_api_footer($comment);
1298 output_close_api_file();
1300 # Add this dll to the database of dll names
1301 my $output_file = $opt_output_directory."/dlls.db";
1303 # Append the dllname to the output db of names
1304 open(DLLDB,">>$output_file") || die "Couldn't create $output_file\n";
1305 print DLLDB $spec_details->{DLL_NAME},"\n";
1306 close(DLLDB);
1308 if ($opt_output_format eq "s")
1310 output_sgml_dll_file($spec_details);
1311 return;
1314 if ($opt_output_format eq "x")
1316 output_xml_dll_file($spec_details);
1317 return;
1323 # OUTPUT FUNCTIONS
1324 # ----------------
1325 # Only these functions know anything about formatting for a specific
1326 # output type. The functions above work only with plain text.
1327 # This is to allow new types of output to be added easily.
1329 # Open the api file
1330 sub output_open_api_file($)
1332 my $output_name = shift;
1333 $output_name = $opt_output_directory."/".$output_name;
1335 if ($opt_output_format eq "h")
1337 $output_name = $output_name.".html";
1339 elsif ($opt_output_format eq "s")
1341 $output_name = $output_name.".sgml";
1343 elsif ($opt_output_format eq "x")
1345 $output_name = $output_name.".xml";
1347 else
1349 $output_name = $output_name.".".$opt_manual_section;
1351 open(OUTPUT,">$output_name") || die "Couldn't create file '$output_name'\n";
1354 # Close the api file
1355 sub output_close_api_file()
1357 close (OUTPUT);
1360 # Output the api file header
1361 sub output_api_header($)
1363 my $comment = shift;
1365 if ($opt_output_format eq "h")
1367 print OUTPUT "<!-- Generated file - DO NOT EDIT! -->\n";
1368 print OUTPUT "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n";
1369 print OUTPUT "<HTML>\n<HEAD>\n";
1370 print OUTPUT "<LINK REL=\"StyleSheet\" href=\"apidoc.css\" type=\"text/css\">\n";
1371 print OUTPUT "<META NAME=\"GENERATOR\" CONTENT=\"tools/c2man.pl\">\n";
1372 print OUTPUT "<META NAME=\"keywords\" CONTENT=\"Win32,Wine,API,$comment->{COMMENT_NAME}\">\n";
1373 print OUTPUT "<TITLE>Wine API: $comment->{COMMENT_NAME}</TITLE>\n</HEAD>\n<BODY>\n";
1375 elsif ($opt_output_format eq "s" || $opt_output_format eq "x")
1377 print OUTPUT "<!-- Generated file - DO NOT EDIT! -->\n",
1378 "<sect1>\n",
1379 "<title>$comment->{COMMENT_NAME}</title>\n";
1381 else
1383 print OUTPUT ".\\\" -*- nroff -*-\n.\\\" Generated file - DO NOT EDIT!\n".
1384 ".TH ",$comment->{COMMENT_NAME}," ",$opt_manual_section," \"",$date,"\" \"".
1385 "Wine API\" \"Wine API\"\n";
1389 sub output_api_footer($)
1391 if ($opt_output_format eq "h")
1393 print OUTPUT "<hr><p><i class=\"copy\">Copyright &copy ".$year." The Wine Project.".
1394 " All trademarks are the property of their respective owners.".
1395 " Visit <a href=\"http://www.winehq.org\">WineHQ</a> for license details.".
1396 " Generated $date.</i></p>\n</body>\n</html>\n";
1398 elsif ($opt_output_format eq "s" || $opt_output_format eq "x")
1400 print OUTPUT "</sect1>\n";
1401 return;
1403 else
1408 sub output_api_section_start($$)
1410 my $comment = shift;
1411 my $section_name = shift;
1413 if ($opt_output_format eq "h")
1415 print OUTPUT "\n<h2 class=\"section\">",$section_name,"</h2>\n";
1417 elsif ($opt_output_format eq "s" || $opt_output_format eq "x")
1419 print OUTPUT "<bridgehead>",$section_name,"</bridgehead>\n";
1421 else
1423 print OUTPUT "\n\.SH ",$section_name,"\n";
1427 sub output_api_section_end()
1429 # Not currently required by any output formats
1432 sub output_api_name($)
1434 my $comment = shift;
1435 my $readable_name = $comment->{COMMENT_NAME};
1436 $readable_name =~ s/-/ /g; # make section names more readable
1438 output_api_section_start($comment,"NAME");
1441 my $dll_ordinal = "";
1442 if ($comment->{ORDINAL} ne "")
1444 $dll_ordinal = "(".$comment->{DLL_NAME}.".".$comment->{ORDINAL}.")";
1446 if ($opt_output_format eq "h")
1448 print OUTPUT "<p><b class=\"func_name\">",$readable_name,
1449 "</b>&nbsp;&nbsp;<i class=\"dll_ord\">",
1450 ,$dll_ordinal,"</i></p>\n";
1452 elsif ($opt_output_format eq "s" || $opt_output_format eq "x")
1454 print OUTPUT "<para>\n <command>",$readable_name,"</command> <emphasis>",
1455 $dll_ordinal,"</emphasis>\n</para>\n";
1457 else
1459 print OUTPUT "\\fB",$readable_name,"\\fR ",$dll_ordinal;
1462 output_api_section_end();
1465 sub output_api_synopsis($)
1467 my $comment = shift;
1468 my @fmt;
1470 output_api_section_start($comment,"SYNOPSIS");
1472 if ($opt_output_format eq "h")
1474 print OUTPUT "<pre class=\"proto\">\n ", $comment->{RETURNS}," ",$comment->{COMMENT_NAME},"\n (\n";
1475 @fmt = ("", "\n", "<tt class=\"param\">", "</tt>");
1477 elsif ($opt_output_format eq "s" || $opt_output_format eq "x")
1479 print OUTPUT "<screen>\n ",$comment->{RETURNS}," ",$comment->{COMMENT_NAME},"\n (\n";
1480 @fmt = ("", "\n", "<emphasis>", "</emphasis>");
1482 else
1484 print OUTPUT $comment->{RETURNS}," ",$comment->{COMMENT_NAME},"\n (\n";
1485 @fmt = ("", "\n", "\\fI", "\\fR");
1488 # Since our prototype is output in a pre-formatted block, line up the
1489 # parameters and parameter comments in the same column.
1491 # First caluculate where the columns should start
1492 my $biggest_length = 0;
1493 for(my $i=0; $i < @{$comment->{PROTOTYPE}}; $i++)
1495 my $line = ${$comment->{PROTOTYPE}}[$i];
1496 if ($line =~ /(.+?)([A-Za-z_][A-Za-z_0-9]*)$/)
1498 my $length = length $1;
1499 if ($length > $biggest_length)
1501 $biggest_length = $length;
1506 # Now pad the string with blanks
1507 for(my $i=0; $i < @{$comment->{PROTOTYPE}}; $i++)
1509 my $line = ${$comment->{PROTOTYPE}}[$i];
1510 if ($line =~ /(.+?)([A-Za-z_][A-Za-z_0-9]*)$/)
1512 my $pad_len = $biggest_length - length $1;
1513 my $padding = " " x ($pad_len);
1514 ${$comment->{PROTOTYPE}}[$i] = $1.$padding.$2;
1518 for(my $i=0; $i < @{$comment->{PROTOTYPE}}; $i++)
1520 # Format the parameter name
1521 my $line = ${$comment->{PROTOTYPE}}[$i];
1522 my $comma = ($i == @{$comment->{PROTOTYPE}}-1) ? "" : ",";
1523 $line =~ s/(.+?)([A-Za-z_][A-Za-z_0-9]*)$/ $fmt[0]$1$fmt[2]$2$fmt[3]$comma$fmt[1]/;
1524 print OUTPUT $line;
1527 if ($opt_output_format eq "h")
1529 print OUTPUT " )\n</pre>\n";
1531 elsif ($opt_output_format eq "s" || $opt_output_format eq "x")
1533 print OUTPUT " )\n</screen>\n";
1535 else
1537 print OUTPUT " )\n";
1540 output_api_section_end();
1543 sub output_api_comment($)
1545 my $comment = shift;
1546 my $open_paragraph = 0;
1547 my $open_raw = 0;
1548 my $param_docs = 0;
1549 my @fmt;
1551 if ($opt_output_format eq "h")
1553 @fmt = ("<p>", "</p>\n", "<tt class=\"const\">", "</tt>", "<b class=\"emp\">", "</b>",
1554 "<tt class=\"coderef\">", "</tt>", "<tt class=\"param\">", "</tt>",
1555 "<i class=\"in_out\">", "</i>", "<pre class=\"raw\">\n", "</pre>\n",
1556 "<table class=\"tab\"><colgroup><col><col><col></colgroup><tbody>\n",
1557 "</tbody></table>\n","<tr><td>","</td></tr>\n","</td>","</td><td>");
1559 elsif ($opt_output_format eq "s" || $opt_output_format eq "x")
1561 @fmt = ("<para>\n","\n</para>\n","<constant>","</constant>","<emphasis>","</emphasis>",
1562 "<command>","</command>","<constant>","</constant>","<emphasis>","</emphasis>",
1563 "<screen>\n","</screen>\n",
1564 "<informaltable frame=\"none\">\n<tgroup cols=\"3\">\n<tbody>\n",
1565 "</tbody>\n</tgroup>\n</informaltable>\n","<row><entry>","</entry></row>\n",
1566 "</entry>","</entry><entry>");
1568 else
1570 @fmt = ("\.PP\n", "\n", "\\fB", "\\fR", "\\fB", "\\fR", "\\fB", "\\fR", "\\fI", "\\fR",
1571 "\\fB", "\\fR ", "", "", "", "","","\n.PP\n","","");
1574 # Extract the parameter names
1575 my @parameter_names;
1576 for (@{$comment->{PROTOTYPE}})
1578 if ( /(.+?)([A-Za-z_][A-Za-z_0-9]*)$/ )
1580 push (@parameter_names, $2);
1584 for (@{$comment->{TEXT}})
1586 if ($opt_output_format eq "h" || $opt_output_format eq "s" || $opt_output_format eq "x")
1588 # Map special characters
1589 s/\&/\&amp;/g;
1590 s/\</\&lt;/g;
1591 s/\>/\&gt;/g;
1592 s/\([Cc]\)/\&copy;/g;
1593 s/\(tm\)/&#174;/;
1596 if ( s/^\|// )
1598 # Raw output
1599 if ($open_raw == 0)
1601 if ($open_paragraph == 1)
1603 # Close the open paragraph
1604 print OUTPUT $fmt[1];
1605 $open_paragraph = 0;
1607 # Start raw output
1608 print OUTPUT $fmt[12];
1609 $open_raw = 1;
1611 if ($opt_output_format eq "")
1613 print OUTPUT ".br\n"; # Prevent 'man' running these lines together
1615 print OUTPUT $_,"\n";
1617 else
1619 if ($opt_output_format eq "h")
1621 # Link to the file in WineHQ cvs
1622 s/^(Implemented in \")(.+?)(\"\.)/$1$2$3 http:\/\/source.winehq.org\/source\/$2/g;
1623 s/^(Declared in \")(.+?)(\"\.)/$1$2$3 http:\/\/source.winehq.org\/source\/include\/$2/g;
1625 # Highlight strings
1626 s/(\".+?\")/$fmt[2]$1$fmt[3]/g;
1627 # Highlight literal chars
1628 s/(\'.\')/$fmt[2]$1$fmt[3]/g;
1629 s/(\'.{2}\')/$fmt[2]$1$fmt[3]/g;
1630 # Highlight numeric constants
1631 s/( |\-|\+|\.|\()([0-9\-\.]+)( |\-|$|\.|\,|\*|\?|\))/$1$fmt[2]$2$fmt[3]$3/g;
1633 # Leading cases ("xxxx:","-") start new paragraphs & are emphasised
1634 # FIXME: Using bullet points for leading '-' would look nicer.
1635 if ($open_paragraph == 1 && $param_docs == 0)
1637 s/^(\-)/$fmt[1]$fmt[0]$fmt[4]$1$fmt[5]/;
1638 s/^([[A-Za-z\-]+\:)/$fmt[1]$fmt[0]$fmt[4]$1$fmt[5]/;
1640 else
1642 s/^(\-)/$fmt[4]$1$fmt[5]/;
1643 s/^([[A-Za-z\-]+\:)/$fmt[4]$1$fmt[5]/;
1646 if ($opt_output_format eq "h")
1648 # Html uses links for API calls
1649 while ( /([A-Za-z_]+[A-Za-z_0-9-]+)(\(\))/)
1651 my $link = $1;
1652 my $readable_link = $1;
1653 $readable_link =~ s/-/ /g;
1655 s/([A-Za-z_]+[A-Za-z_0-9-]+)(\(\))/<a href\=\"$link\.html\">$readable_link<\/a>/;
1657 # Index references
1658 s/\{\{(.*?)\}\}\{\{(.*?)\}\}/<a href\=\"$2\.html\">$1<\/a>/g;
1659 s/ ([A-Z_])(\(\))/<a href\=\"$1\.html\">$1<\/a>/g;
1660 # And references to COM objects (hey, they'll get documented one day)
1661 s/ (I[A-Z]{1}[A-Za-z0-9_]+) (Object|object|Interface|interface)/ <a href\=\"$1\.html\">$1<\/a> $2/g;
1662 # Convert any web addresses to real links
1663 s/(http\:\/\/)(.+?)($| )/<a href\=\"$1$2\">$2<\/a>$3/g;
1665 else
1667 if ($opt_output_format eq "")
1669 # Give the man section for API calls
1670 s/ ([A-Za-z_]+[A-Za-z_0-9-]+)\(\)/ $fmt[6]$1\($opt_manual_section\)$fmt[7]/g;
1672 else
1674 # Highlight API calls
1675 s/ ([A-Za-z_]+[A-Za-z_0-9-]+\(\))/ $fmt[6]$1$fmt[7]/g;
1678 # And references to COM objects
1679 s/ (I[A-Z]{1}[A-Za-z0-9_]+) (Object|object|Interface|interface)/ $fmt[6]$1$fmt[7] $2/g;
1682 if ($open_raw == 1)
1684 # Finish the raw output
1685 print OUTPUT $fmt[13];
1686 $open_raw = 0;
1689 if ( /^[A-Z]+$/ || /^SEE ALSO$/ )
1691 # Start of a new section
1692 if ($open_paragraph == 1)
1694 if ($param_docs == 1)
1696 print OUTPUT $fmt[17],$fmt[15];
1697 $param_docs = 0;
1699 else
1701 print OUTPUT $fmt[1];
1703 $open_paragraph = 0;
1705 output_api_section_start($comment,$_);
1706 if ( /^PARAMS$/ || /^MEMBERS$/ )
1708 print OUTPUT $fmt[14];
1709 $param_docs = 1;
1711 else
1713 #print OUTPUT $fmt[15];
1714 #$param_docs = 0;
1717 elsif ( /^$/ )
1719 # Empty line, indicating a new paragraph
1720 if ($open_paragraph == 1)
1722 if ($param_docs == 0)
1724 print OUTPUT $fmt[1];
1725 $open_paragraph = 0;
1729 else
1731 if ($param_docs == 1)
1733 if ($open_paragraph == 1)
1735 # For parameter docs, put each parameter into a new paragraph/table row
1736 print OUTPUT $fmt[17];
1737 $open_paragraph = 0;
1739 s/(\[.+\])( *)/$fmt[19]$fmt[10]$1$fmt[11]$fmt[19] /; # Format In/Out
1741 else
1743 # Within paragraph lines, prevent lines running together
1744 $_ = $_." ";
1747 # Format parameter names where they appear in the comment
1748 for my $parameter_name (@parameter_names)
1750 s/(^|[ \.\,\(\-\*])($parameter_name)($|[ \.\)\,\-\/]|(\=[^"]))/$1$fmt[8]$2$fmt[9]$3/g;
1752 # Structure dereferences include the dereferenced member
1753 s/(\-\>[A-Za-z_]+)/$fmt[8]$1$fmt[9]/g;
1754 s/(\-\&gt\;[A-Za-z_]+)/$fmt[8]$1$fmt[9]/g;
1756 if ($open_paragraph == 0)
1758 if ($param_docs == 1)
1760 print OUTPUT $fmt[16];
1762 else
1764 print OUTPUT $fmt[0];
1766 $open_paragraph = 1;
1768 # Anything in all uppercase on its own gets emphasised
1769 s/(^|[ \.\,\(\[\|\=])([A-Z]+?[A-Z0-9_]+)($|[ \.\,\*\?\|\)\=\'])/$1$fmt[6]$2$fmt[7]$3/g;
1771 print OUTPUT $_;
1775 if ($open_raw == 1)
1777 print OUTPUT $fmt[13];
1779 if ($param_docs == 1 && $open_paragraph == 1)
1781 print OUTPUT $fmt[17];
1782 $open_paragraph = 0;
1784 if ($param_docs == 1)
1786 print OUTPUT $fmt[15];
1788 if ($open_paragraph == 1)
1790 print OUTPUT $fmt[1];
1794 # Create the master index file
1795 sub output_master_index_files()
1797 if ($opt_output_format eq "")
1799 return; # No master index for man pages
1802 if ($opt_output_format eq "h")
1804 # Append the index entries to the output db of index entries
1805 my $output_file = $opt_output_directory."/index.db";
1806 open(INDEXDB,">>$output_file") || die "Couldn't create $output_file\n";
1807 for (@index_entries_list)
1809 $_ =~ s/A\,/\,/;
1810 print INDEXDB $_."\n";
1812 close(INDEXDB);
1815 # Use the comment output functions for consistency
1816 my $comment =
1818 FILE => "",
1819 COMMENT_NAME => "The Wine Api Guide",
1820 ALT_NAME => "The Wine Api Guide",
1821 DLL_NAME => "",
1822 ORDINAL => "",
1823 RETURNS => "",
1824 PROTOTYPE => [],
1825 TEXT => [],
1828 if ($opt_output_format eq "s" || $opt_output_format eq "x")
1830 $comment->{COMMENT_NAME} = "Introduction";
1831 $comment->{ALT_NAME} = "Introduction",
1833 elsif ($opt_output_format eq "h")
1835 @{$comment->{TEXT}} = (
1836 "NAME",
1837 $comment->{COMMENT_NAME},
1838 "INTRODUCTION",
1842 # Create the initial comment text
1843 push (@{$comment->{TEXT}},
1844 "This document describes the Api calls made available",
1845 "by Wine. They are grouped by the dll that exports them.",
1847 "Please do not edit this document, since it is generated automatically",
1848 "from the Wine source code tree. Details on updating this documentation",
1849 "are given in the \"Wine Developers Guide\".",
1850 "CONTRIBUTORS",
1851 "Api documentation is generally written by the person who ",
1852 "implements a given Api call. Authors of each dll are listed in the overview ",
1853 "section for that dll. Additional contributors who have updated source files ",
1854 "but have not entered their names in a copyright statement are noted by an ",
1855 "entry in the file \"Changelog\" from the Wine source code distribution.",
1859 # Read in all dlls from the database of dll names
1860 my $input_file = $opt_output_directory."/dlls.db";
1861 my @dlls = `cat $input_file|sort|uniq`;
1863 if ($opt_output_format eq "h")
1865 # HTML gets a list of all the dlls and an index. For docbook the index creates this for us
1866 push (@{$comment->{TEXT}},
1867 "INDEX",
1868 "For an alphabetical listing of the functions available, please click the ",
1869 "first letter of the functions name below:","",
1870 "[ _(), A(), B(), C(), D(), E(), F(), G(), H(), ".
1871 "I(), J(), K(), L(), M(), N(), O(), P(), Q(), ".
1872 "R(), S(), T(), U(), V(), W(), X(), Y(), Z() ]", "",
1873 "DLLS",
1874 "Each dll provided by Wine is documented individually. The following dlls are provided :",
1877 # Add the dlls to the comment
1878 for (@dlls)
1880 $_ =~ s/(\..*)?\n/\(\)/;
1881 push (@{$comment->{TEXT}}, $_, "");
1883 output_open_api_file("index");
1885 elsif ($opt_output_format eq "s" || $opt_output_format eq "x")
1887 # Just write this as the initial blurb, with a chapter heading
1888 output_open_api_file("blurb");
1889 print OUTPUT "<chapter id =\"blurb\">\n<title>Introduction to The Wine Api Guide</title>\n"
1892 # Write out the document
1893 output_api_header($comment);
1894 output_api_comment($comment);
1895 output_api_footer($comment);
1896 if ($opt_output_format eq "s" || $opt_output_format eq "x")
1898 print OUTPUT "</chapter>\n" # finish the chapter
1900 output_close_api_file();
1902 if ($opt_output_format eq "s")
1904 output_sgml_master_file(\@dlls);
1905 return;
1907 if ($opt_output_format eq "x")
1909 output_xml_master_file(\@dlls);
1910 return;
1912 if ($opt_output_format eq "h")
1914 output_html_index_files();
1915 output_html_stylesheet();
1916 return;
1920 # Write the master wine-api.xml, linking it to each dll.
1921 sub output_xml_master_file($)
1923 my $dlls = shift;
1925 output_open_api_file("wine-api");
1926 print OUTPUT "<?xml version='1.0'?>";
1927 print OUTPUT "<!-- Generated file - DO NOT EDIT! -->\n";
1928 print OUTPUT "<!DOCTYPE book PUBLIC \"-//OASIS//DTD DocBook V5.0/EN\" ";
1929 print OUTPUT " \"http://www.docbook.org/xml/5.0/dtd/docbook.dtd\" [\n\n";
1930 print OUTPUT "<!ENTITY blurb SYSTEM \"blurb.xml\">\n";
1932 # List the entities
1933 for (@$dlls)
1935 $_ =~ s/(\..*)?\n//;
1936 print OUTPUT "<!ENTITY ",$_," SYSTEM \"",$_,".xml\">\n"
1939 print OUTPUT "]>\n\n<book id=\"index\">\n<bookinfo><title>The Wine Api Guide</title></bookinfo>\n\n";
1940 print OUTPUT " &blurb;\n";
1942 for (@$dlls)
1944 print OUTPUT " &",$_,";\n"
1946 print OUTPUT "\n\n</book>\n";
1948 output_close_api_file();
1951 # Write the master wine-api.sgml, linking it to each dll.
1952 sub output_sgml_master_file($)
1954 my $dlls = shift;
1956 output_open_api_file("wine-api");
1957 print OUTPUT "<!-- Generated file - DO NOT EDIT! -->\n";
1958 print OUTPUT "<!doctype book PUBLIC \"-//OASIS//DTD DocBook V3.1//EN\" [\n\n";
1959 print OUTPUT "<!entity blurb SYSTEM \"blurb.sgml\">\n";
1961 # List the entities
1962 for (@$dlls)
1964 $_ =~ s/(\..*)?\n//;
1965 print OUTPUT "<!entity ",$_," SYSTEM \"",$_,".sgml\">\n"
1968 print OUTPUT "]>\n\n<book id=\"index\">\n<bookinfo><title>The Wine Api Guide</title></bookinfo>\n\n";
1969 print OUTPUT " &blurb;\n";
1971 for (@$dlls)
1973 print OUTPUT " &",$_,";\n"
1975 print OUTPUT "\n\n</book>\n";
1977 output_close_api_file();
1980 # Produce the sgml for the dll chapter from the generated files
1981 sub output_sgml_dll_file($)
1983 my $spec_details = shift;
1985 # Make a list of all the documentation files to include
1986 my $exports = $spec_details->{EXPORTS};
1987 my @source_files = ();
1988 for (@$exports)
1990 # @$_ => ordinal, call convention, exported name, implementation name, documented;
1991 if (@$_[$EXPORT_CALL] ne "forward" && @$_[$EXPORT_CALL] ne "extern" &&
1992 @$_[$EXPORT_CALL] ne "stub" && @$_[$EXPORT_CALL] ne "equate" &&
1993 @$_[$EXPORT_CALL] ne "variable" && @$_[$EXPORT_CALL] ne "fake" &&
1994 @$_[$EXPORT_FLAGS] & $FLAG_DOCUMENTED)
1996 # A documented function
1997 push (@source_files,@$_[$EXPORT_IMPNAME]);
2001 push (@source_files,@{$spec_details->{EXTRA_COMMENTS}});
2003 @source_files = sort @source_files;
2005 # create a new chapter for this dll
2006 my $tmp_name = $opt_output_directory."/".$spec_details->{DLL_NAME}.".tmp";
2007 open(OUTPUT,">$tmp_name") || die "Couldn't create $tmp_name\n";
2008 print OUTPUT "<chapter>\n<title>$spec_details->{DLL_NAME}</title>\n";
2009 output_close_api_file();
2011 # Add the sorted documentation, cleaning up as we go
2012 `cat $opt_output_directory/$spec_details->{DLL_NAME}.sgml >>$tmp_name`;
2013 for (@source_files)
2015 `cat $opt_output_directory/$_.sgml >>$tmp_name`;
2016 `rm -f $opt_output_directory/$_.sgml`;
2019 # close the chapter, and overwite the dll source
2020 open(OUTPUT,">>$tmp_name") || die "Couldn't create $tmp_name\n";
2021 print OUTPUT "</chapter>\n";
2022 close OUTPUT;
2023 `mv $tmp_name $opt_output_directory/$spec_details->{DLL_NAME}.sgml`;
2026 # Produce the xml for the dll chapter from the generated files
2027 sub output_xml_dll_file($)
2029 my $spec_details = shift;
2031 # Make a list of all the documentation files to include
2032 my $exports = $spec_details->{EXPORTS};
2033 my @source_files = ();
2034 for (@$exports)
2036 # @$_ => ordinal, call convention, exported name, implementation name, documented;
2037 if (@$_[$EXPORT_CALL] ne "forward" && @$_[$EXPORT_CALL] ne "extern" &&
2038 @$_[$EXPORT_CALL] ne "stub" && @$_[$EXPORT_CALL] ne "equate" &&
2039 @$_[$EXPORT_CALL] ne "variable" && @$_[$EXPORT_CALL] ne "fake" &&
2040 @$_[$EXPORT_FLAGS] & $FLAG_DOCUMENTED)
2042 # A documented function
2043 push (@source_files,@$_[$EXPORT_IMPNAME]);
2047 push (@source_files,@{$spec_details->{EXTRA_COMMENTS}});
2049 @source_files = sort @source_files;
2051 # create a new chapter for this dll
2052 my $tmp_name = $opt_output_directory."/".$spec_details->{DLL_NAME}.".tmp";
2053 open(OUTPUT,">$tmp_name") || die "Couldn't create $tmp_name\n";
2054 print OUTPUT "<?xml version='1.0' encoding='UTF-8'?>\n<chapter>\n<title>$spec_details->{DLL_NAME}</title>\n";
2055 output_close_api_file();
2057 # Add the sorted documentation, cleaning up as we go
2058 `cat $opt_output_directory/$spec_details->{DLL_NAME}.xml >>$tmp_name`;
2059 for (@source_files)
2061 `cat $opt_output_directory/$_.xml >>$tmp_name`;
2062 `rm -f $opt_output_directory/$_.xml`;
2065 # close the chapter, and overwite the dll source
2066 open(OUTPUT,">>$tmp_name") || die "Couldn't create $tmp_name\n";
2067 print OUTPUT "</chapter>\n";
2068 close OUTPUT;
2069 `mv $tmp_name $opt_output_directory/$spec_details->{DLL_NAME}.xml`;
2072 # Write the html index files containing the function names
2073 sub output_html_index_files()
2075 if ($opt_output_format ne "h")
2077 return;
2080 my @letters = ('_', 'A' .. 'Z');
2082 # Read in all functions
2083 my $input_file = $opt_output_directory."/index.db";
2084 my @funcs = `cat $input_file|sort|uniq`;
2086 for (@letters)
2088 my $letter = $_;
2089 my $comment =
2091 FILE => "",
2092 COMMENT_NAME => "",
2093 ALT_NAME => "",
2094 DLL_NAME => "",
2095 ORDINAL => "",
2096 RETURNS => "",
2097 PROTOTYPE => [],
2098 TEXT => [],
2101 $comment->{COMMENT_NAME} = $letter." Functions";
2102 $comment->{ALT_NAME} = $letter." Functions";
2104 push (@{$comment->{TEXT}},
2105 "NAME",
2106 $comment->{COMMENT_NAME},
2107 "FUNCTIONS"
2110 # Add the functions to the comment
2111 for (@funcs)
2113 my $first_char = substr ($_, 0, 1);
2114 $first_char = uc $first_char;
2116 if ($first_char eq $letter)
2118 my $name = $_;
2119 my $file;
2120 $name =~ s/(^.*?)\,(.*?)\n/$1/;
2121 $file = $2;
2122 push (@{$comment->{TEXT}}, "{{".$name."}}{{".$file."}}","");
2126 # Write out the document
2127 output_open_api_file($letter);
2128 output_api_header($comment);
2129 output_api_comment($comment);
2130 output_api_footer($comment);
2131 output_close_api_file();
2135 # Output the stylesheet for HTML output
2136 sub output_html_stylesheet()
2138 if ($opt_output_format ne "h")
2140 return;
2143 my $css;
2144 ($css = <<HERE_TARGET) =~ s/^\s+//gm;
2146 * Default styles for Wine HTML Documentation.
2148 * This style sheet should be altered to suit your needs/taste.
2150 BODY { /* Page body */
2151 background-color: white;
2152 color: black;
2153 font-family: Tahoma,sans-serif;
2154 font-style: normal;
2155 font-size: 10pt;
2157 a:link { color: #4444ff; } /* Links */
2158 a:visited { color: #333377 }
2159 a:active { color: #0000dd }
2160 H2.section { /* Section Headers */
2161 font-family: sans-serif;
2162 color: #777777;
2163 background-color: #F0F0FE;
2164 margin-left: 0.2in;
2165 margin-right: 1.0in;
2167 b.func_name { /* Function Name */
2168 font-size: 10pt;
2169 font-style: bold;
2171 i.dll_ord { /* Italicised DLL+ordinal */
2172 color: #888888;
2173 font-family: sans-serif;
2174 font-size: 8pt;
2176 p { /* Paragraphs */
2177 margin-left: 0.5in;
2178 margin-right: 0.5in;
2180 table { /* tables */
2181 margin-left: 0.5in;
2182 margin-right: 0.5in;
2184 pre.proto /* API Function prototype */
2186 border-style: solid;
2187 border-width: 1px;
2188 border-color: #777777;
2189 background-color: #F0F0BB;
2190 color: black;
2191 font-size: 10pt;
2192 vertical-align: top;
2193 margin-left: 0.5in;
2194 margin-right: 1.0in;
2196 pre.raw { /* Raw text output */
2197 margin-left: 0.6in;
2198 margin-right: 1.1in;
2199 background-color: #8080DC;
2201 tt.param { /* Parameter name */
2202 font-style: italic;
2203 color: blue;
2205 tt.const { /* Constant */
2206 color: red;
2208 i.in_out { /* In/Out */
2209 font-size: 8pt;
2210 color: grey;
2212 tt.coderef { /* Code in description text */
2213 color: darkgreen;
2215 b.emp /* Emphasis */ {
2216 font-style: bold;
2217 color: darkblue;
2219 i.footer { /* Footer */
2220 font-family: sans-serif;
2221 font-size: 6pt;
2222 color: darkgrey;
2224 HERE_TARGET
2226 my $output_file = "$opt_output_directory/apidoc.css";
2227 open(CSS,">$output_file") || die "Couldn't create the file $output_file\n";
2228 print CSS $css;
2229 close(CSS);
2233 sub usage()
2235 print "\nCreate API Documentation from Wine source code.\n\n",
2236 "Usage: c2man.pl [options] {-w <spec>} {-I <include>} {<source>}\n",
2237 "Where: <spec> is a .spec file giving a DLL's exports.\n",
2238 " <include> is an include directory used by the DLL.\n",
2239 " <source> is a source file of the DLL.\n",
2240 " The above can be given multiple times on the command line, as appropriate.\n",
2241 "Options:\n",
2242 " -Th : Output HTML instead of a man page\n",
2243 " -Ts : Output SGML (Docbook source) instead of a man page\n",
2244 " -C <dir> : Source directory, to find source files if they are not found in the\n",
2245 " current directory. Default is \"",$opt_source_dir,"\"\n",
2246 " -R <dir> : Root of build directory, default is \"",$opt_wine_root_dir,"\"\n",
2247 " -o <dir> : Create output in <dir>, default is \"",$opt_output_directory,"\"\n",
2248 " -s <sect>: Set manual section to <sect>, default is ",$opt_manual_section,"\n",
2249 " -e : Output \"FIXME\" documentation from empty comments.\n",
2250 " -v : Verbosity. Can be given more than once for more detail.\n";
2255 # Main
2258 # Print usage if we're called with no args
2259 if( @ARGV == 0)
2261 usage();
2264 # Process command line options
2265 while(defined($_ = shift @ARGV))
2267 if( s/^-// )
2269 # An option.
2270 for ($_)
2272 /^o$/ && do { $opt_output_directory = shift @ARGV; last; };
2273 s/^S// && do { $opt_manual_section = $_; last; };
2274 /^Th$/ && do { $opt_output_format = "h"; last; };
2275 /^Ts$/ && do { $opt_output_format = "s"; last; };
2276 /^Tx$/ && do { $opt_output_format = "x"; last; };
2277 /^v$/ && do { $opt_verbose++; last; };
2278 /^e$/ && do { $opt_output_empty = 1; last; };
2279 /^L$/ && do { last; };
2280 /^w$/ && do { @opt_spec_file_list = (@opt_spec_file_list, shift @ARGV); last; };
2281 s/^I// && do { if ($_ ne ".") {
2282 foreach my $include (`find $_/./ -type d ! -name tests`) {
2283 $include =~ s/\n//;
2284 $include = $include."/*.h";
2285 $include =~ s/\/\//\//g;
2286 my $have_headers = `ls $include >/dev/null 2>&1`;
2287 if ($? >> 8 == 0) { @opt_header_file_list = (@opt_header_file_list, $include); }
2290 last;
2292 s/^C// && do {
2293 if ($_ ne "") { $opt_source_dir = $_; }
2294 last;
2296 s/^R// && do { if ($_ =~ /^\//) { $opt_wine_root_dir = $_; }
2297 else { $opt_wine_root_dir = `cd $pwd/$_ && pwd`; }
2298 $opt_wine_root_dir =~ s/\n//;
2299 $opt_wine_root_dir =~ s/\/\//\//g;
2300 if (! $opt_wine_root_dir =~ /\/$/ ) { $opt_wine_root_dir = $opt_wine_root_dir."/"; };
2301 last;
2303 die "Unrecognised option $_\n";
2306 else
2308 # A source file.
2309 push (@opt_source_file_list, $_);
2313 # Remove duplicate include directories
2314 my %htmp;
2315 @opt_header_file_list = grep(!$htmp{$_}++, @opt_header_file_list);
2317 if ($opt_verbose > 3)
2319 print "Output dir:'".$opt_output_directory."'\n";
2320 print "Section :'".$opt_manual_section."'\n";
2321 print "Format :'".$opt_output_format."'\n";
2322 print "Source dir:'".$opt_source_dir."'\n";
2323 print "Root :'".$opt_wine_root_dir."'\n";
2324 print "Spec files:'@opt_spec_file_list'\n";
2325 print "Includes :'@opt_header_file_list'\n";
2326 print "Sources :'@opt_source_file_list'\n";
2329 if (@opt_spec_file_list == 0)
2331 exit 0; # Don't bother processing non-dll files
2334 # Make sure the output directory exists
2335 unless (-d $opt_output_directory)
2337 mkdir $opt_output_directory or die "Cannot create directory $opt_output_directory\n";
2340 # Read in each .spec files exports and other details
2341 while(my $spec_file = shift @opt_spec_file_list)
2343 process_spec_file($spec_file);
2346 if ($opt_verbose > 3)
2348 foreach my $spec_file ( keys %spec_files )
2350 print "in '$spec_file':\n";
2351 my $spec_details = $spec_files{$spec_file}[0];
2352 my $exports = $spec_details->{EXPORTS};
2353 for (@$exports)
2355 print @$_[$EXPORT_ORDINAL].",".@$_[$EXPORT_CALL].", ".
2356 @$_[$EXPORT_EXPNAME].",".@$_[$EXPORT_IMPNAME]."\n";
2361 # Extract and output the comments from each source file
2362 while(defined($_ = shift @opt_source_file_list))
2364 process_source_file($_);
2367 # Write the index files for each spec
2368 process_index_files();
2370 # Write the master index file
2371 output_master_index_files();
2373 exit 0;