~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Wine Cross Reference
wine/tools/c2man.pl

Version: ~ [ wine-1.1.4 ] ~ [ wine-1.1.3 ] ~ [ wine-1.1.2 ] ~ [ wine-1.1.1 ] ~ [ wine-1.1.0 ] ~ [ wine-1.0 ] ~ [ wine-1.0-rc5 ] ~ [ wine-1.0-rc4 ] ~ [ wine-1.0-rc3 ] ~ [ wine-1.0-rc2 ] ~ [ wine-1.0-rc1 ] ~ [ wine-0.9.61 ] ~ [ wine-0.9.60 ] ~ [ wine-0.9.59 ] ~ [ wine-0.9.58 ] ~ [ wine-0.9.57 ] ~ [ wine-0.9.56 ] ~ [ wine-0.9.55 ] ~ [ wine-0.9.54 ] ~ [ wine-0.9.53 ] ~ [ wine-0.9.52 ] ~ [ wine-0.9.51 ] ~ [ wine-0.9.50 ] ~ [ wine-0.9.49 ] ~ [ wine-0.9.48 ] ~ [ wine-0.9.47 ] ~ [ wine-0.9.46 ] ~ [ wine-0.9.45 ] ~ [ wine-0.9.44 ] ~ [ wine-0.9.43 ] ~ [ wine-0.9.42 ] ~ [ wine-0.9.41 ] ~ [ wine-0.9.40 ] ~ [ wine-0.9.39 ] ~ [ wine-0.9.38 ] ~ [ wine-0.9.37 ] ~ [ wine-0.9.36 ] ~ [ wine-0.9.35 ] ~ [ wine-0.9.34 ] ~ [ wine-0.9.33 ] ~ [ wine-0.9.32 ] ~ [ wine-0.9.31 ] ~ [ wine-0.9.30 ] ~ [ wine-0.9.29 ] ~ [ wine-0.9.28 ] ~ [ wine-0.9.27 ] ~ [ wine-0.9.26 ] ~ [ wine-0.9.25 ] ~ [ wine-0.9.24 ] ~ [ wine-0.9.23 ] ~ [ wine-0.9.22 ] ~ [ wine-0.9.21 ] ~ [ wine-0.9.20 ] ~ [ wine-0.9.19 ] ~ [ wine-0.9.18 ] ~ [ wine-0.9.17 ] ~ [ wine-0.9.16 ] ~ [ wine-0.9.15 ] ~ [ wine-0.9.14 ] ~ [ wine-0.9.13 ] ~ [ wine-0.9.12 ] ~ [ wine-0.9.11 ] ~ [ wine-0.9.10 ] ~ [ wine-0.9.9 ] ~ [ wine-0.9.8 ] ~ [ wine-0.9.7 ] ~ [ wine-0.9.6 ] ~ [ wine-0.9.5 ] ~ [ wine-0.9.4 ] ~ [ wine-0.9.3 ] ~ [ wine-0.9.2 ] ~ [ wine-0.9.1 ] ~ [ wine-0.9 ] ~ [ wine20050930 ] ~ [ wine20050830 ] ~ [ wine20050725 ] ~ [ wine20050628 ] ~ [ wine20050524 ] ~ [ wine20050419 ] ~ [ wine20050310 ] ~ [ wine20050211 ] ~ [ wine20050111 ] ~ [ wine20041201 ] ~ [ wine20041019 ] ~ [ wine20040914 ] ~ [ wine20040813 ] ~ [ wine20040716 ] ~ [ wine20040615 ] ~ [ wine20040505 ] ~ [ wine20040408 ] ~ [ wine20040309 ] ~ [ wine20040213 ] ~ [ wine20040121 ] ~ [ wine20031212 ] ~ [ wine20031118 ] ~ [ wine20031016 ] ~ [ wine20030911 ] ~ [ wine20030813 ] ~ [ wine20030709 ] ~ [ wine20030618 ] ~ [ wine20030508 ] ~ [ wine20030408 ] ~ [ wine20030318 ] ~ [ wine20030219 ] ~ [ wine20030115 ] ~ [ wine20021219 ] ~ [ wine20021125 ] ~ [ wine20021031 ] ~ [ wine20021007 ] ~ [ wine20020904 ] ~ [ wine20020804 ] ~ [ wine20020710 ] ~ [ wine20020605 ] ~ [ wine20020509 ] ~ [ wine20020411 ] ~ [ wine20020310 ] ~ [ wine20020228 ] ~ [ wine20011226 ] ~ [ wine20011108 ] ~ [ wine20011004 ] ~ [ wine20010824 ] ~ [ wine20010731 ] ~ [ wine20010629 ] ~ [ wine20010510 ] ~ [ wine20010418 ] ~ [ wine20010326 ] ~ [ wine20010305 ] ~ [ wine20010216 ] ~ [ wine20010112 ] ~ [ wine20001222 ] ~ [ wine20001202 ] ~ [ wine20001026 ] ~ [ wine20001002 ] ~ [ wine20000909 ] ~ [ wine20000821 ] ~ [ wine20000801 ] ~ [ wine20000716 ] ~ [ wine20000326 ] ~ [ wine20000227 ] ~ [ wine20000130 ] ~ [ wine20000109 ] ~

  1 #! /usr/bin/perl -w
  2 #
  3 # Generate API documentation. See documentation/documentation.sgml for details.
  4 #
  5 # Copyright (C) 2000 Mike McCormack
  6 # Copyright (C) 2003 Jon Griffiths
  7 #
  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.
 12 #
 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.
 17 #
 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
 21 #
 22 # TODO
 23 #  Consolidate A+W pairs together, and only write one doc, without the suffix
 24 #  Implement automatic docs fo 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?
 28 
 29 use strict;
 30 use bytes;
 31 
 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
 40 
 41 
 42 # Options
 43 my $opt_output_directory = "man3w"; # All default options are for nroff (man pages)
 44 my $opt_manual_section = "3w";
 45 my $opt_source_dir = "";
 46 my $opt_wine_root_dir = "";
 47 my $opt_output_format = "";         # '' = nroff, 'h' = html, 's' = sgml
 48 my $opt_output_empty = 0;           # Non-zero = Create 'empty' comments (for every implemented function)
 49 my $opt_fussy = 1;                  # Non-zero = Create only if we have a RETURNS section
 50 my $opt_verbose = 0;                # >0 = verbosity. Can be given multiple times (for debugging)
 51 my @opt_header_file_list = ();
 52 my @opt_spec_file_list = ();
 53 my @opt_source_file_list = ();
 54 
 55 # All the collected details about all the .spec files being processed
 56 my %spec_files;
 57 # All the collected details about all the source files being processed
 58 my %source_files;
 59 # All documented functions that are to be placed in the index
 60 my @index_entries_list = ();
 61 
 62 # useful globals
 63 my $pwd = `pwd`."/";
 64 $pwd =~ s/\n//;
 65 my @datetime = localtime;
 66 my @months = ( "Jan", "Feb", "Mar", "Apr", "May", "Jun",
 67                "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" );
 68 my $year = $datetime[5] + 1900;
 69 my $date = "$months[$datetime[4]] $year";
 70 
 71 
 72 sub output_api_comment($);
 73 sub output_api_footer($);
 74 sub output_api_header($);
 75 sub output_api_name($);
 76 sub output_api_synopsis($);
 77 sub output_close_api_file();
 78 sub output_comment($);
 79 sub output_html_index_files();
 80 sub output_html_stylesheet();
 81 sub output_open_api_file($);
 82 sub output_sgml_dll_file($);
 83 sub output_sgml_master_file($);
 84 sub output_spec($);
 85 sub process_comment($);
 86 sub process_extra_comment($);
 87 
 88 
 89 # Generate the list of exported entries for the dll
 90 sub process_spec_file($)
 91 {
 92   my $spec_name = shift;
 93   my ($dll_name, $dll_ext)  = split(/\./, $spec_name);
 94   $dll_ext = "dll" if ( $dll_ext eq "spec" );
 95   my $uc_dll_name  = uc $dll_name;
 96 
 97   my $spec_details =
 98   {
 99     NAME => $spec_name,
100     DLL_NAME => $dll_name,
101     DLL_EXT => $dll_ext,
102     NUM_EXPORTS => 0,
103     NUM_STUBS => 0,
104     NUM_FUNCS => 0,
105     NUM_FORWARDS => 0,
106     NUM_VARS => 0,
107     NUM_DOCS => 0,
108     CONTRIBUTORS => [ ],
109     SOURCES => [ ],
110     DESCRIPTION => [ ],
111     EXPORTS => [ ],
112     EXPORTED_NAMES => { },
113     IMPLEMENTATION_NAMES => { },
114     EXTRA_COMMENTS => [ ],
115     CURRENT_EXTRA => [ ] ,
116   };
117 
118   if ($opt_verbose > 0)
119   {
120     print "Processing ".$spec_name."\n";
121   }
122 
123   # We allow opening to fail just to cater for the peculiarities of
124   # the Wine build system. This doesn't hurt, in any case
125   open(SPEC_FILE, "<$spec_name")
126   || (($opt_source_dir ne "")
127       && open(SPEC_FILE, "<$opt_source_dir/$spec_name"))
128   || return;
129 
130   while(<SPEC_FILE>)
131   {
132     s/^\s+//;            # Strip leading space
133     s/\s+\n$/\n/;        # Strip trailing space
134     s/\s+/ /g;           # Strip multiple tabs & spaces to a single space
135     s/\s*#.*//;          # Strip comments
136     s/\(.*\)/ /;         # Strip arguments
137     s/\s+/ /g;           # Strip multiple tabs & spaces to a single space (again)
138     s/\n$//;             # Strip newline
139 
140     my $flags = 0;
141     if( /\-noname/ )
142     {
143       $flags |= $FLAG_NONAME;
144     }
145     if( /\-i386/ )
146     {
147       $flags |= $FLAG_I386;
148     }
149     if( /\-register/ )
150     {
151       $flags |= $FLAG_REGISTER;
152     }
153     s/ \-[a-z0-9]+//g;   # Strip flags
154 
155     if( /^(([0-9]+)|@) / )
156     {
157       # This line contains an exported symbol
158       my ($ordinal, $call_convention, $exported_name, $implementation_name) = split(' ');
159 
160       for ($call_convention)
161       {
162         /^(cdecl|stdcall|varargs|pascal)$/
163                  && do { $spec_details->{NUM_FUNCS}++;    last; };
164         /^(variable|equate)$/
165                  && do { $spec_details->{NUM_VARS}++;     last; };
166         /^(extern)$/
167                  && do { $spec_details->{NUM_FORWARDS}++; last; };
168         /^stub$/ && do { $spec_details->{NUM_STUBS}++;    last; };
169         if ($opt_verbose > 0)
170         {
171           print "Warning: didn't recognise convention \'",$call_convention,"'\n";
172         }
173         last;
174       }
175 
176       # Convert ordinal only names so we can find them later
177       if ($exported_name eq "@")
178       {
179         $exported_name = $uc_dll_name.'_'.$ordinal;
180       }
181       if (!defined($implementation_name))
182       {
183         $implementation_name = $exported_name;
184       }
185       if ($implementation_name eq "")
186       {
187         $implementation_name = $exported_name;
188       }
189 
190       if ($implementation_name =~ /(.*?)\./)
191       {
192         $call_convention = "forward"; # Referencing a function from another dll
193         $spec_details->{NUM_FUNCS}--;
194         $spec_details->{NUM_FORWARDS}++;
195       }
196 
197       # Add indices for the exported and implementation names
198       $spec_details->{EXPORTED_NAMES}{$exported_name} = $spec_details->{NUM_EXPORTS};
199       if ($implementation_name ne $exported_name)
200       {
201         $spec_details->{IMPLEMENTATION_NAMES}{$exported_name} = $spec_details->{NUM_EXPORTS};
202       }
203 
204       # Add the exported entry
205       $spec_details->{NUM_EXPORTS}++;
206       my @export = ($ordinal, $call_convention, $exported_name, $implementation_name, $flags);
207       push (@{$spec_details->{EXPORTS}},[@export]);
208     }
209   }
210   close(SPEC_FILE);
211 
212   # Add this .spec files details to the list of .spec files
213   $spec_files{$uc_dll_name} = [$spec_details];
214 }
215 
216 # Read each source file, extract comments, and generate API documentation if appropriate
217 sub process_source_file($)
218 {
219   my $source_file = shift;
220   my $source_details =
221   {
222     CONTRIBUTORS => [ ],
223     DEBUG_CHANNEL => "",
224   };
225   my $comment =
226   {
227     FILE => $source_file,
228     COMMENT_NAME => "",
229     ALT_NAME => "",
230     DLL_NAME => "",
231     DLL_EXT => "",
232     ORDINAL => "",
233     RETURNS => "",
234     PROTOTYPE => [],
235     TEXT => [],
236   };
237   my $parse_state = 0;
238   my $ignore_blank_lines = 1;
239   my $extra_comment = 0; # 1 if this is an extra comment, i.e its not a .spec export
240 
241   if ($opt_verbose > 0)
242   {
243     print "Processing ".$source_file."\n";
244   }
245   open(SOURCE_FILE,"<$source_file")
246   || (($opt_source_dir ne "")
247       && open(SOURCE_FILE,"<$opt_source_dir/$source_file"))
248   || die "couldn't open ".$source_file."\n";
249 
250   # Add this source file to the list of source files
251   $source_files{$source_file} = [$source_details];
252 
253   while(<SOURCE_FILE>)
254   {
255     s/\n$//;   # Strip newline
256     s/^\s+//;  # Strip leading space
257     s/\s+$//;  # Strip trailing space
258     if (! /^\*\|/ )
259     {
260       # Strip multiple tabs & spaces to a single space
261       s/\s+/ /g;
262     }
263 
264     if ( / +Copyright *(\([Cc]\))*[0-9 \-\,\/]*([[:alpha:][:^ascii:] \.\-]+)/ )
265     {
266       # Extract a contributor to this file
267       my $contributor = $2;
268       $contributor =~ s/ *$//;
269       $contributor =~ s/^by //;
270       $contributor =~ s/\.$//;
271       $contributor =~ s/ (for .*)/ \($1\)/;
272       if ($contributor ne "")
273       {
274         if ($opt_verbose > 3)
275         {
276           print "Info: Found contributor:'".$contributor."'\n";
277         }
278         push (@{$source_details->{CONTRIBUTORS}},$contributor);
279       }
280     }
281     elsif ( /WINE_DEFAULT_DEBUG_CHANNEL\(([A-Za-z]*)\)/ )
282     {
283       # Extract the debug channel to use
284       if ($opt_verbose > 3)
285       {
286         print "Info: Found debug channel:'".$1."'\n";
287       }
288       $source_details->{DEBUG_CHANNEL} = $1;
289     }
290 
291     if ($parse_state == 0) # Searching for a comment
292     {
293       if ( /^\/\**$/ )
294       {
295         # Found a comment start
296         $comment->{COMMENT_NAME} = "";
297         $comment->{ALT_NAME} = "";
298         $comment->{DLL_NAME} = "";
299         $comment->{ORDINAL} = "";
300         $comment->{RETURNS} = "";
301         $comment->{PROTOTYPE} = [];
302         $comment->{TEXT} = [];
303         $ignore_blank_lines = 1;
304         $extra_comment = 0;
305         $parse_state = 3;
306       }
307     }
308     elsif ($parse_state == 1) # Reading in a comment
309     {
310       if ( /^\**\// )
311       {
312         # Found the end of the comment
313         $parse_state = 2;
314       }
315       elsif ( s/^\*\|/\|/ )
316       {
317         # A line of comment not meant to be pre-processed
318         push (@{$comment->{TEXT}},$_); # Add the comment text
319       }
320       elsif ( s/^ *\** *// )
321       {
322         # A line of comment, starting with an asterisk
323         if ( /^[A-Z]+$/ || $_ eq "")
324         {
325           # This is a section start, so skip blank lines before and after it.
326           my $last_line = pop(@{$comment->{TEXT}});
327           if (defined($last_line) && $last_line ne "")
328           {
329             # Put it back
330             push (@{$comment->{TEXT}},$last_line);
331           }
332           if ( /^[A-Z]+$/ )
333           {
334             $ignore_blank_lines = 1;
335           }
336           else
337           {
338             $ignore_blank_lines = 0;
339           }
340         }
341 
342         if ($ignore_blank_lines == 0 || $_ ne "")
343         {
344           push (@{$comment->{TEXT}},$_); # Add the comment text
345         }
346       }
347       else
348       {
349         # This isn't a well formatted comment: look for the next one
350         $parse_state = 0;
351       }
352     }
353     elsif ($parse_state == 2) # Finished reading in a comment
354     {
355       if ( /(WINAPIV|WINAPI|__cdecl|PASCAL|CALLBACK|FARPROC16)/ ||
356            /.*?\(/ )
357       {
358         # Comment is followed by a function definition
359         $parse_state = 4; # Fall through to read prototype
360       }
361       else
362       {
363         # Allow cpp directives and blank lines between the comment and prototype
364         if ($extra_comment == 1)
365         {
366           # An extra comment not followed by a function definition
367           $parse_state = 5; # Fall through to process comment
368         }
369         elsif (!/^\#/ && !/^ *$/ && !/^__ASM_GLOBAL_FUNC/)
370         {
371           # This isn't a well formatted comment: look for the next one
372           if ($opt_verbose > 1)
373           {
374             print "Info: Function '",$comment->{COMMENT_NAME},"' not followed by prototype.\n";
375           }
376           $parse_state = 0;
377         }
378       }
379     }
380     elsif ($parse_state == 3) # Reading in the first line of a comment
381     {
382       s/^ *\** *//;
383       if ( /^([\@A-Za-z0-9_]+) +(\(|\[)([A-Za-z0-9_]+)\.(([0-9]+)|@)(\)|\])\s*(.*)$/ )
384       {
385         # Found a correctly formed "ApiName (DLLNAME.Ordinal)" line.
386         if (defined ($7) && $7 ne "")
387         {
388           push (@{$comment->{TEXT}},$_); # Add the trailing comment text
389         }
390         $comment->{COMMENT_NAME} = $1;
391         $comment->{DLL_NAME} = uc $3;
392         $comment->{ORDINAL} = $4;
393         $comment->{DLL_NAME} =~ s/^KERNEL$/KRNL386/; # Too many of these to ignore, _old_ code
394         $parse_state = 1;
395       }
396       elsif ( /^([A-Za-z0-9_-]+) +\{([A-Za-z0-9_]+)\}$/ )
397       {
398         # Found a correctly formed "CommentTitle {DLLNAME}" line (extra documentation)
399         $comment->{COMMENT_NAME} = $1;
400         $comment->{DLL_NAME} = uc $2;
401         $comment->{ORDINAL} = "";
402         $extra_comment = 1;
403         $parse_state = 1;
404       }
405       else
406       {
407         # This isn't a well formatted comment: look for the next one
408         $parse_state = 0;
409       }
410     }
411 
412     if ($parse_state == 4) # Reading in the function definition
413     {
414       push (@{$comment->{PROTOTYPE}},$_);
415       # Strip comments from the line before checking for ')'
416       my $stripped_line = $_;
417       $stripped_line =~ s/ *(\/\* *)(.*?)( *\*\/ *)//;
418       if ( $stripped_line =~ /\)/ )
419       {
420         # Strip a blank last line
421         my $last_line = pop(@{$comment->{TEXT}});
422         if (defined($last_line) && $last_line ne "")
423         {
424           # Put it back
425           push (@{$comment->{TEXT}},$last_line);
426         }
427 
428         if ($opt_output_empty != 0 && @{$comment->{TEXT}} == 0)
429         {
430           # Create a 'not implemented' comment
431           @{$comment->{TEXT}} = ("fixme: This function has not yet been documented.");
432         }
433         $parse_state = 5;
434       }
435     }
436 
437     if ($parse_state == 5) # Processing the comment
438     {
439       # Process it, if it has any text
440       if (@{$comment->{TEXT}} > 0)
441       {
442         if ($extra_comment == 1)
443         {
444           process_extra_comment($comment);
445         }
446         else
447         {
448           @{$comment->{TEXT}} = ("DESCRIPTION", @{$comment->{TEXT}});
449           process_comment($comment);
450         }
451       }
452       elsif ($opt_verbose > 1 && $opt_output_empty == 0)
453       {
454         print "Info: Function '",$comment->{COMMENT_NAME},"' has no documentation.\n";
455       }
456       $parse_state = 0;
457     }
458   }
459   close(SOURCE_FILE);
460 }
461 
462 # Standardise a comments text for consistency
463 sub process_comment_text($)
464 {
465   my $comment = shift;
466   my $in_params = 0;
467   my @tmp_list = ();
468   my $i = 0;
469 
470   for (@{$comment->{TEXT}})
471   {
472     my $line = $_;
473 
474     if ( /^\s*$/ || /^[A-Z]+$/ || /^-/ )
475     {
476       $in_params = 0;
477     }
478     if ( $in_params > 0 && !/\[/ && !/\]/ )
479     {
480       # Possibly a continuation of the parameter description
481       my $last_line = pop(@tmp_list);
482       if ( $last_line =~ /\[/ && $last_line =~ /\]/ )
483       {
484         $line = $last_line." ".$_;
485       }
486       else
487       {
488         $in_params = 0;
489         push (@tmp_list, $last_line);
490       }
491     }
492     if ( /^(PARAMS|MEMBERS)$/ )
493     {
494       $in_params = 1;
495     }
496     push (@tmp_list, $line);
497   }
498 
499   @{$comment->{TEXT}} = @tmp_list;
500 
501   for (@{$comment->{TEXT}})
502   {
503     if (! /^\|/ )
504     {
505       # Map I/O values. These come in too many formats to standardise now....
506       s/\[I\]|\[i\]|\[in\]|\[IN\]/\[In\] /g;
507       s/\[O\]|\[o\]|\[out\]|\[OUT\]/\[Out\]/g;
508       s/\[I\/O\]|\[I\,O\]|\[i\/o\]|\[in\/out\]|\[IN\/OUT\]/\[In\/Out\]/g;
509       # TRUE/FALSE/NULL are defines, capitilise them
510       s/True|true/TRUE/g;
511       s/False|false/FALSE/g;
512       s/Null|null/NULL/g;
513       # Preferred capitalisations
514       s/ wine| WINE/ Wine/g;
515       s/ API | api / Api /g;
516       s/ DLL | Dll / dll /g;
517       s/ URL | url / Url /g;
518       s/WIN16|win16/Win16/g;
519       s/WIN32|win32/Win32/g;
520       s/WIN64|win64/Win64/g;
521       s/ ID | id / Id /g;
522       # Grammar
523       s/([a-z])\.([A-Z])/$1\. $2/g; # Space after full stop
524       s/ \:/\:/g;                   # Colons to the left
525       s/ \;/\;/g;                   # Semi-colons too
526       # Common idioms
527       s/^See ([A-Za-z0-9_]+)\.$/See $1\(\)\./;                # Referring to A version from W
528       s/^Unicode version of ([A-Za-z0-9_]+)\.$/See $1\(\)\./; # Ditto
529       s/^64\-bit version of ([A-Za-z0-9_]+)\.$/See $1\(\)\./; # Referring to 32 bit version from 64
530       s/^PARAMETERS$/PARAMS/;  # Name of parameter section should be 'PARAMS'
531       # Trademarks
532       s/( |\.)(M\$|MS|Microsoft|microsoft|micro\$oft|Micro\$oft)( |\.)/$1Microsoft\(tm\)$3/g;
533       s/( |\.)(Windows|windows|windoze|winblows)( |\.)/$1Windows\(tm\)$3/g;
534       s/( |\.)(DOS|dos|msdos)( |\.)/$1MS-DOS\(tm\)$3/g;
535       s/( |\.)(UNIX|unix)( |\.)/$1Unix\(tm\)$3/g;
536       s/( |\.)(LINIX|linux)( |\.)/$1Linux\(tm\)$3/g;
537       # Abbreviations
538       s/( char )/ character /g;
539       s/( chars )/ characters /g;
540       s/( info )/ information /g;
541       s/( app )/ application /g;
542       s/( apps )/ applications /g;
543       s/( exe )/ executable /g;
544       s/( ptr )/ pointer /g;
545       s/( obj )/ object /g;
546       s/( err )/ error /g;
547       s/( bool )/ boolean /g;
548       s/( no\. )/ number /g;
549       s/( No\. )/ Number /g;
550       # Punctuation
551       if ( /\[I|\[O/ && ! /\.$/ )
552       {
553         $_ = $_."."; # Always have a full stop at the end of parameter desc.
554       }
555       elsif ($i > 0 && /^[A-Z]*$/  &&
556                !(@{$comment->{TEXT}}[$i-1] =~ /\.$/) &&
557                !(@{$comment->{TEXT}}[$i-1] =~ /\:$/))
558       {
559 
560         if (!(@{$comment->{TEXT}}[$i-1] =~ /^[A-Z]*$/))
561         {
562           # Paragraphs always end with a full stop
563           @{$comment->{TEXT}}[$i-1] = @{$comment->{TEXT}}[$i-1].".";
564         }
565       }
566     }
567     $i++;
568   }
569 }
570 
571 # Standardise our comment and output it if it is suitable.
572 sub process_comment($)
573 {
574   my $comment = shift;
575 
576   # Don't process this comment if the function isn't exported
577   my $spec_details = $spec_files{$comment->{DLL_NAME}}[0];
578 
579   if (!defined($spec_details))
580   {
581     if ($opt_verbose > 2)
582     {
583       print "Warning: Function '".$comment->{COMMENT_NAME}."' belongs to '".
584             $comment->{DLL_NAME}."' (not passed with -w): not processing it.\n";
585     }
586     return;
587   }
588 
589   if ($comment->{COMMENT_NAME} eq "@")
590   {
591     my $found = 0;
592 
593     # Find the name from the .spec file
594     for (@{$spec_details->{EXPORTS}})
595     {
596       if (@$_[0] eq $comment->{ORDINAL})
597       {
598         $comment->{COMMENT_NAME} = @$_[2];
599         $found = 1;
600       }
601     }
602 
603     if ($found == 0)
604     {
605       # Create an implementation name
606       $comment->{COMMENT_NAME} = $comment->{DLL_NAME}."_".$comment->{ORDINAL};
607     }
608   }
609 
610   my $exported_names = $spec_details->{EXPORTED_NAMES};
611   my $export_index = $exported_names->{$comment->{COMMENT_NAME}};
612   my $implementation_names = $spec_details->{IMPLEMENTATION_NAMES};
613 
614   if (!defined($export_index))
615   {
616     # Perhaps the comment uses the implementation name?
617     $export_index = $implementation_names->{$comment->{COMMENT_NAME}};
618   }
619   if (!defined($export_index))
620   {
621     # This function doesn't appear to be exported. hmm.
622     if ($opt_verbose > 2)
623     {
624       print "Warning: Function '".$comment->{COMMENT_NAME}."' claims to belong to '".
625             $comment->{DLL_NAME}."' but is not exported by it: not processing it.\n";
626     }
627     return;
628   }
629 
630   # When the function is exported twice we have the second name below the first
631   # (you see this a lot in ntdll, but also in some other places).
632   my $first_line = ${@{$comment->{TEXT}}}[1];
633 
634   if ( $first_line =~ /^(@|[A-Za-z0-9_]+) +(\(|\[)([A-Za-z0-9_]+)\.(([0-9]+)|@)(\)|\])$/ )
635   {
636     # Found a second name - mark it as documented
637     my $alt_index = $exported_names->{$1};
638     if (defined($alt_index))
639     {
640       if ($opt_verbose > 2)
641       {
642         print "Info: Found alternate name '",$1,"\n";
643       }
644       my $alt_export = @{$spec_details->{EXPORTS}}[$alt_index];
645       @$alt_export[4] |= $FLAG_DOCUMENTED;
646       $spec_details->{NUM_DOCS}++;
647       ${@{$comment->{TEXT}}}[1] = "";
648     }
649   }
650 
651   if (@{$spec_details->{CURRENT_EXTRA}})
652   {
653     # We have an extra comment that might be related to this one
654     my $current_comment = ${@{$spec_details->{CURRENT_EXTRA}}}[0];
655     my $current_name = $current_comment->{COMMENT_NAME};
656     if ($comment->{COMMENT_NAME} =~ /^$current_name/ && $comment->{COMMENT_NAME} ne $current_name)
657     {
658       if ($opt_verbose > 2)
659       {
660         print "Linking ",$comment->{COMMENT_NAME}," to $current_name\n";
661       }
662       # Add a reference to this comment to our extra comment
663       push (@{$current_comment->{TEXT}}, $comment->{COMMENT_NAME}."()","");
664     }
665   }
666 
667   # We want our docs generated using the implementation name, so they are unique
668   my $export = @{$spec_details->{EXPORTS}}[$export_index];
669   $comment->{COMMENT_NAME} = @$export[3];
670   $comment->{ALT_NAME} = @$export[2];
671 
672   # Mark the function as documented
673   $spec_details->{NUM_DOCS}++;
674   @$export[4] |= $FLAG_DOCUMENTED;
675 
676   # This file is used by the DLL - Make sure we get our contributors right
677   push (@{$spec_details->{SOURCES}},$comment->{FILE});
678 
679   # If we have parameter comments in the prototype, extract them
680   my @parameter_comments;
681   for (@{$comment->{PROTOTYPE}})
682   {
683     s/ *\, */\,/g; # Strip spaces from around commas
684 
685     if ( s/ *(\/\* *)(.*?)( *\*\/ *)// ) # Strip out comment
686     {
687       my $parameter_comment = $2;
688       if (!$parameter_comment =~ /^\[/ )
689       {
690         # Add [IO] markers so we format the comment correctly
691         $parameter_comment = "[fixme] ".$parameter_comment;
692       }
693       if ( /( |\*)([A-Za-z_]{1}[A-Za-z_0-9]*)(\,|\))/ )
694       {
695         # Add the parameter name
696         $parameter_comment = $2." ".$parameter_comment;
697       }
698       push (@parameter_comments, $parameter_comment);
699     }
700   }
701 
702   # If we extracted any prototype comments, add them to the comment text.
703   if (@parameter_comments)
704   {
705     @parameter_comments = ("PARAMS", @parameter_comments);
706     my @new_comment = ();
707     my $inserted_params = 0;
708 
709     for (@{$comment->{TEXT}})
710     {
711       if ( $inserted_params == 0 && /^[A-Z]+$/ )
712       {
713         # Found a section header, so this is where we insert
714         push (@new_comment, @parameter_comments);
715         $inserted_params = 1;
716       }
717       push (@new_comment, $_);
718     }
719     if ($inserted_params == 0)
720     {
721       # Add them to the end
722       push (@new_comment, @parameter_comments);
723     }
724     $comment->{TEXT} = [@new_comment];
725   }
726 
727   if ($opt_fussy == 1 && $opt_output_empty == 0)
728   {
729     # Reject any comment that doesn't have a description or a RETURNS section.
730     # This is the default for now, 'coz many comments aren't suitable.
731     my $found_returns = 0;
732     my $found_description_text = 0;
733     my $in_description = 0;
734     for (@{$comment->{TEXT}})
735     {
736       if ( /^RETURNS$/ )
737       {
738         $found_returns = 1;
739         $in_description = 0;
740       }
741       elsif ( /^DESCRIPTION$/ )
742       {
743         $in_description = 1;
744       }
745       elsif ($in_description == 1)
746       {
747         if ( !/^[A-Z]+$/ )
748         {
749           # Don't reject comments that refer to another doc (e.g. A/W)
750           if ( /^See ([A-Za-z0-9_]+)\.$/ )
751           {
752             if ($comment->{COMMENT_NAME} =~ /W$/ )
753             {
754               # This is probably a Unicode version of an Ascii function.
755               # Create the Ascii name and see if its been documented
756               my $ascii_name = $comment->{COMMENT_NAME};
757               $ascii_name =~ s/W$/A/;
758 
759               my $ascii_export_index = $exported_names->{$ascii_name};
760 
761               if (!defined($ascii_export_index))
762               {
763                 $ascii_export_index = $implementation_names->{$ascii_name};
764               }
765               if (!defined($ascii_export_index))
766               {
767                 if ($opt_verbose > 2)
768                 {
769                   print "Warning: Function '".$comment->{COMMENT_NAME}."' is not an A/W pair.\n";
770                 }
771               }
772               else
773               {
774                 my $ascii_export = @{$spec_details->{EXPORTS}}[$ascii_export_index];
775                 if (@$ascii_export[4] & $FLAG_DOCUMENTED)
776                 {
777                   # Flag these functions as an A/W pair
778                   @$ascii_export[4] |= $FLAG_APAIR;
779                   @$export[4] |= $FLAG_WPAIR;
780                 }
781               }
782             }
783             $found_returns = 1;
784           }
785           elsif ( /^Unicode version of ([A-Za-z0-9_]+)\.$/ )
786           {
787             @$export[4] |= $FLAG_WPAIR; # Explicitly marked as W version
788             $found_returns = 1;
789           }
790           elsif ( /^64\-bit version of ([A-Za-z0-9_]+)\.$/ )
791           {
792             @$export[4] |= $FLAG_64PAIR; # Explicitly marked as 64 bit version
793             $found_returns = 1;
794           }
795           $found_description_text = 1;
796         }
797         else
798         {
799           $in_description = 0;
800         }
801       }
802     }
803     if ($found_returns == 0 || $found_description_text == 0)
804     {
805       if ($opt_verbose > 2)
806       {
807         print "Info: Function '",$comment->{COMMENT_NAME},"' has no ",
808               "description and/or RETURNS section, skipping\n";
809       }
810       $spec_details->{NUM_DOCS}--;
811       @$export[4] &= ~$FLAG_DOCUMENTED;
812       return;
813     }
814   }
815 
816   process_comment_text($comment);
817 
818   # Strip the prototypes return value, call convention, name and brackets
819   # (This leaves it as a list of types and names, or empty for void functions)
820   my $prototype = join(" ", @{$comment->{PROTOTYPE}});
821   $prototype =~ s/  / /g;
822 
823   if ( $prototype =~ /(WINAPIV|WINAPI|__cdecl|PASCAL|CALLBACK|FARPROC16)/ )
824   {
825     $prototype =~ s/^(.*?) (WINAPIV|WINAPI|__cdecl|PASCAL|CALLBACK|FARPROC16) (.*?)\( *(.*)/$4/;
826     $comment->{RETURNS} = $1;
827   }
828   else
829   {
830     $prototype =~ s/^(.*?)([A-Za-z0-9_]+)\( *(.*)/$3/;
831     $comment->{RETURNS} = $1;
832   }
833 
834   $prototype =~ s/ *\).*//;        # Strip end bracket
835   $prototype =~ s/ *\* */\*/g;     # Strip space around pointers
836   $prototype =~ s/ *\, */\,/g;     # Strip space around commas
837   $prototype =~ s/^(void|VOID)$//; # If void, leave blank
838   $prototype =~ s/\*([A-Za-z_])/\* $1/g; # Separate pointers from parameter name
839   @{$comment->{PROTOTYPE}} = split ( /,/ ,$prototype);
840 
841   # FIXME: If we have no parameters, make sure we have a PARAMS: None. section
842 
843   # Find header file
844   my $h_file = "";
845   if (@$export[4] & $FLAG_NONAME)
846   {
847     $h_file = "Exported by ordinal only. Use GetProcAddress() to obtain a pointer to the function.";
848   }
849   else
850   {
851     if ($comment->{COMMENT_NAME} ne "")
852     {
853       my $tmp = "grep -s -l $comment->{COMMENT_NAME} @opt_header_file_list 2>/dev/null";
854       $tmp = `$tmp`;
855       my $exit_value  = $? >> 8;
856       if ($exit_value == 0)
857       {
858         $tmp =~ s/\n.*//g;
859         if ($tmp ne "")
860         {
861           $h_file = `basename $tmp`;
862         }
863       }
864     }
865     elsif ($comment->{ALT_NAME} ne "")
866     {
867       my $tmp = "grep -s -l $comment->{ALT_NAME} @opt_header_file_list"." 2>/dev/null";
868       $tmp = `$tmp`;
869       my $exit_value  = $? >> 8;
870       if ($exit_value == 0)
871       {
872         $tmp =~ s/\n.*//g;
873         if ($tmp ne "")
874         {
875           $h_file = `basename $tmp`;
876         }
877       }
878     }
879     $h_file =~ s/^ *//;
880     $h_file =~ s/\n//;
881     if ($h_file eq "")
882     {
883       $h_file = "Not defined in a Wine header. The function is either undocumented, or missing from Wine."
884     }
885     else
886     {
887       $h_file = "Defined in \"".$h_file."\".";
888     }
889   }
890 
891   # Find source file
892   my $c_file = $comment->{FILE};
893   if ($opt_wine_root_dir ne "")
894   {
895     my $cfile = $pwd."/".$c_file;     # Current dir + file
896     $cfile =~ s/(.+)(\/.*$)/$1/;      # Strip the filename
897     $cfile = `cd $cfile && pwd`;      # Strip any relative parts (e.g. "../../")
898     $cfile =~ s/\n//;                 # Strip newline
899     my $newfile = $c_file;
900     $newfile =~ s/(.+)(\/.*$)/$2/;    # Strip all but the filename
901     $cfile = $cfile."/".$newfile;     # Append filename to base path
902     $cfile =~ s/$opt_wine_root_dir//; # Get rid of the root directory
903     $cfile =~ s/\/\//\//g;            # Remove any double slashes
904     $cfile =~ s/^\/+//;               # Strip initial directory slash
905     $c_file = $cfile;
906   }
907   $c_file = "Implemented in \"".$c_file."\".";
908 
909   # Add the implementation details
910   push (@{$comment->{TEXT}}, "IMPLEMENTATION","",$h_file,"",$c_file);
911 
912   if (@$export[4] & $FLAG_I386)
913   {
914     push (@{$comment->{TEXT}}, "", "Available on x86 platforms only.");
915   }
916   if (@$export[4] & $FLAG_REGISTER)
917   {
918     push (@{$comment->{TEXT}}, "", "This function passes one or more arguments in registers. ",
919           "For more details, please read the source code.");
920   }
921   my $source_details = $source_files{$comment->{FILE}}[0];
922   if ($source_details->{DEBUG_CHANNEL} ne "")
923   {
924     push (@{$comment->{TEXT}}, "", "Debug channel \"".$source_details->{DEBUG_CHANNEL}."\".");
925   }
926 
927   # Write out the documentation for the API
928   output_comment($comment)
929 }
930 
931 # process our extra comment and output it if it is suitable.
932 sub process_extra_comment($)
933 {
934   my $comment = shift;
935 
936   my $spec_details = $spec_files{$comment->{DLL_NAME}}[0];
937 
938   if (!defined($spec_details))
939   {
940     if ($opt_verbose > 2)
941     {
942       print "Warning: Extra comment '".$comment->{COMMENT_NAME}."' belongs to '".
943             $comment->{DLL_NAME}."' (not passed with -w): not processing it.\n";
944     }
945     return;
946   }
947 
948   # Check first to see if this is documentation for the DLL.
949   if ($comment->{COMMENT_NAME} eq $comment->{DLL_NAME})
950   {
951     if ($opt_verbose > 2)
952     {
953       print "Info: Found DLL documentation\n";
954     }
955     for (@{$comment->{TEXT}})
956     {
957       push (@{$spec_details->{DESCRIPTION}}, $_);
958     }
959     return;
960   }
961 
962   # Add the comment to the DLL page as a link
963   push (@{$spec_details->{EXTRA_COMMENTS}},$comment->{COMMENT_NAME});
964 
965   # If we have a prototype, process as a regular comment
966   if (@{$comment->{PROTOTYPE}})
967   {
968     $comment->{ORDINAL} = "@";
969 
970     # Add an index for the comment name
971     $spec_details->{EXPORTED_NAMES}{$comment->{COMMENT_NAME}} = $spec_details->{NUM_EXPORTS};
972 
973     # Add a fake exported entry
974     $spec_details->{NUM_EXPORTS}++;
975     my ($ordinal, $call_convention, $exported_name, $implementation_name, $documented) =
976      ("@", "fake", $comment->{COMMENT_NAME}, $comment->{COMMENT_NAME}, 0);
977     my @export = ($ordinal, $call_convention, $exported_name, $implementation_name, $documented);
978     push (@{$spec_details->{EXPORTS}},[@export]);
979     @{$comment->{TEXT}} = ("DESCRIPTION", @{$comment->{TEXT}});
980     process_comment($comment);
981     return;
982   }
983 
984   if ($opt_verbose > 0)
985   {
986     print "Processing ",$comment->{COMMENT_NAME},"\n";
987   }
988 
989   if (@{$spec_details->{CURRENT_EXTRA}})
990   {
991     my $current_comment = ${@{$spec_details->{CURRENT_EXTRA}}}[0];
992 
993     if ($opt_verbose > 0)
994     {
995       print "Processing old current: ",$current_comment->{COMMENT_NAME},"\n";
996     }
997     # Output the current comment
998     process_comment_text($current_comment);
999     output_open_api_file($current_comment->{COMMENT_NAME});
1000     output_api_header($current_comment);
1001     output_api_name($current_comment);
1002     output_api_comment($current_comment);
1003     output_api_footer($current_comment);
1004     output_close_api_file();
1005   }
1006 
1007   if ($opt_verbose > 2)
1008   {
1009     print "Setting current to ",$comment->{COMMENT_NAME},"\n";
1010   }
1011 
1012   my $comment_copy =
1013   {
1014     FILE => $comment->{FILE},
1015     COMMENT_NAME => $comment->{COMMENT_NAME},
1016     ALT_NAME => $comment->{ALT_NAME},
1017     DLL_NAME => $comment->{DLL_NAME},
1018     ORDINAL => $comment->{ORDINAL},
1019     RETURNS => $comment->{RETURNS},
1020     PROTOTYPE => [],
1021     TEXT => [],
1022   };
1023 
1024   for (@{$comment->{TEXT}})
1025   {
1026     push (@{$comment_copy->{TEXT}}, $_);
1027   }
1028   # Set this comment to be the current extra comment
1029   @{$spec_details->{CURRENT_EXTRA}} = ($comment_copy);
1030 }
1031 
1032 # Write a standardised comment out in the appropriate format
1033 sub output_comment($)
1034 {
1035   my $comment = shift;
1036 
1037   if ($opt_verbose > 0)
1038   {
1039     print "Processing ",$comment->{COMMENT_NAME},"\n";
1040   }
1041 
1042   if ($opt_verbose > 4)
1043   {
1044     print "--PROTO--\n";
1045     for (@{$comment->{PROTOTYPE}})
1046     {
1047       print "'".$_."'\n";
1048     }
1049 
1050     print "--COMMENT--\n";
1051     for (@{$comment->{TEXT} })
1052     {
1053       print $_."\n";
1054     }
1055   }
1056 
1057   output_open_api_file($comment->{COMMENT_NAME});
1058   output_api_header($comment);
1059   output_api_name($comment);
1060   output_api_synopsis($comment);
1061   output_api_comment($comment);
1062   output_api_footer($comment);
1063   output_close_api_file();
1064 }
1065 
1066 # Write out an index file for each .spec processed
1067 sub process_index_files()
1068 {
1069   foreach my $spec_file (keys %spec_files)
1070   {
1071     my $spec_details = $spec_files{$spec_file}[0];
1072     if (defined ($spec_details->{DLL_NAME}))
1073     {
1074       if (@{$spec_details->{CURRENT_EXTRA}})
1075       {
1076         # We have an unwritten extra comment, write it
1077         my $current_comment = ${@{$spec_details->{CURRENT_EXTRA}}}[0];
1078         process_extra_comment($current_comment);
1079         @{$spec_details->{CURRENT_EXTRA}} = ();
1080        }
1081        output_spec($spec_details);
1082     }
1083   }
1084 }
1085 
1086 # Write a spec files documentation out in the appropriate format
1087 sub output_spec($)
1088 {
1089   my $spec_details = shift;
1090 
1091   if ($opt_verbose > 2)
1092   {
1093     print "Writing:",$spec_details->{DLL_NAME},"\n";
1094   }
1095 
1096   # Use the comment output functions for consistency
1097   my $comment =
1098   {
1099     FILE => $spec_details->{DLL_NAME},
1100     COMMENT_NAME => $spec_details->{DLL_NAME}.".".$spec_details->{DLL_EXT},
1101     ALT_NAME => $spec_details->{DLL_NAME},
1102     DLL_NAME => "",
1103     ORDINAL => "",
1104     RETURNS => "",
1105     PROTOTYPE => [],
1106     TEXT => [],
1107   };
1108   my $total_implemented = $spec_details->{NUM_FORWARDS} + $spec_details->{NUM_VARS} +
1109      $spec_details->{NUM_FUNCS};
1110   my $percent_implemented = 0;
1111   if ($total_implemented)
1112   {
1113     $percent_implemented = $total_implemented /
1114      ($total_implemented + $spec_details->{NUM_STUBS}) * 100;
1115   }
1116   $percent_implemented = int($percent_implemented);
1117   my $percent_documented = 0;
1118   if ($spec_details->{NUM_DOCS})
1119   {
1120     # Treat forwards and data as documented funcs for statistics
1121     $percent_documented = $spec_details->{NUM_DOCS} / $spec_details->{NUM_FUNCS} * 100;
1122     $percent_documented = int($percent_documented);
1123   }
1124 
1125   # Make a list of the contributors to this DLL. Do this only for the source
1126   # files that make up the DLL, because some directories specify multiple dlls.
1127   my @contributors;
1128 
1129   for (@{$spec_details->{SOURCES}})
1130   {
1131     my $source_details = $source_files{$_}[0];
1132     for (@{$source_details->{CONTRIBUTORS}})
1133     {
1134       push (@contributors, $_);
1135     }
1136   }
1137 
1138   my %saw;
1139   @contributors = grep(!$saw{$_}++, @contributors); # remove dups, from perlfaq4 manpage
1140   @contributors = sort @contributors;
1141 
1142   # Remove duplicates and blanks
1143   for(my $i=0; $i<@contributors; $i++)
1144   {
1145     if ($i > 0 && ($contributors[$i] =~ /$contributors[$i-1]/ || $contributors[$i-1] eq ""))
1146     {
1147       $contributors[$i-1] = $contributors[$i];
1148     }
1149   }
1150   undef %saw;
1151   @contributors = grep(!$saw{$_}++, @contributors);
1152 
1153   if ($opt_verbose > 3)
1154   {
1155     print "Contributors:\n";
1156     for (@contributors)
1157     {
1158       print "'".$_."'\n";
1159     }
1160   }
1161   my $contribstring = join (", ", @contributors);
1162 
1163   # Create the initial comment text
1164   @{$comment->{TEXT}} = (
1165     "NAME",
1166     $comment->{COMMENT_NAME}
1167   );
1168 
1169   # Add the description, if we have one
1170   if (@{$spec_details->{DESCRIPTION}})
1171   {
1172     push (@{$comment->{TEXT}}, "DESCRIPTION");
1173     for (@{$spec_details->{DESCRIPTION}})
1174     {
1175       push (@{$comment->{TEXT}}, $_);
1176     }
1177   }
1178 
1179   # Add the statistics and contributors
1180   push (@{$comment->{TEXT}},
1181     "STATISTICS",
1182     "Forwards: ".$spec_details->{NUM_FORWARDS},
1183     "Variables: ".$spec_details->{NUM_VARS},
1184     "Stubs: ".$spec_details->{NUM_STUBS},
1185     "Functions: ".$spec_details->{NUM_FUNCS},
1186     "Exports-Total: ".$spec_details->{NUM_EXPORTS},
1187     "Implemented-Total: ".$total_implemented." (".$percent_implemented."%)",
1188     "Documented-Total: ".$spec_details->{NUM_DOCS}." (".$percent_documented."%)",
1189     "CONTRIBUTORS",
1190     "The following people hold copyrights on the source files comprising this dll:",
1191     "",
1192     $contribstring,
1193     "Note: This list may not be complete.",
1194     "For a complete listing, see the Files \"AUTHORS\" and \"Changelog\" in the Wine source tree.",
1195     "",
1196   );
1197 
1198   if ($opt_output_format eq "h")
1199   {
1200     # Add the exports to the comment text
1201     push (@{$comment->{TEXT}},"EXPORTS");
1202     my $exports = $spec_details->{EXPORTS};
1203     for (@$exports)
1204     {
1205       my $line = "";
1206 
1207       # @$_ => ordinal, call convention, exported name, implementation name, flags;
1208       if (@$_[1] eq "forward")
1209       {
1210         my $forward_dll = @$_[3];
1211         $forward_dll =~ s/\.(.*)//;
1212         $line = @$_[2]." (forward to ".$1."() in ".$forward_dll."())";
1213       }
1214       elsif (@$_[1] eq "extern")
1215       {
1216         $line = @$_[2]." (extern)";
1217       }
1218       elsif (@$_[1] eq "stub")
1219       {
1220         $line = @$_[2]." (stub)";
1221       }
1222       elsif (@$_[1] eq "fake")
1223       {
1224         # Don't add this function here, it gets listed with the extra documentation
1225         if (!(@$_[4] & $FLAG_WPAIR))
1226         {
1227           # This function should be indexed
1228           push (@index_entries_list, @$_[3].",".@$_[3]);
1229         }
1230       }
1231       elsif (@$_[1] eq "equate" || @$_[1] eq "variable")
1232       {
1233         $line = @$_[2]." (data)";
1234       }
1235       else
1236       {
1237         # A function
1238         if (@$_[4] & $FLAG_DOCUMENTED)
1239         {
1240           # Documented
1241           $line = @$_[2]." (implemented as ".@$_[3]."())";
1242           if (@$_[2] ne @$_[3])
1243           {
1244             $line = @$_[2]." (implemented as ".@$_[3]."())";
1245           }
1246           else
1247           {
1248             $line = @$_[2]."()";
1249           }
1250           if (!(@$_[4] & $FLAG_WPAIR))
1251           {
1252             # This function should be indexed
1253             push (@index_entries_list, @$_[2].",".@$_[3]);
1254           }
1255         }
1256         else
1257         {
1258           $line = @$_[2]." (not documented)";
1259         }
1260       }
1261       if ($line ne "")
1262       {
1263         push (@{$comment->{TEXT}}, $line, "");
1264       }
1265     }
1266 
1267     # Add links to the extra documentation
1268     if (@{$spec_details->{EXTRA_COMMENTS}})
1269     {
1270       push (@{$comment->{TEXT}}, "SEE ALSO");
1271       my %htmp;
1272       @{$spec_details->{EXTRA_COMMENTS}} = grep(!$htmp{$_}++, @{$spec_details->{EXTRA_COMMENTS}});
1273       for (@{$spec_details->{EXTRA_COMMENTS}})
1274       {
1275         push (@{$comment->{TEXT}}, $_."()", "");
1276       }
1277     }
1278   }
1279   # The dll entry should also be indexed
1280   push (@index_entries_list, $spec_details->{DLL_NAME}.",".$spec_details->{DLL_NAME});
1281 
1282   # Write out the document
1283   output_open_api_file($spec_details->{DLL_NAME});
1284   output_api_header($comment);
1285   output_api_comment($comment);
1286   output_api_footer($comment);
1287   output_close_api_file();
1288 
1289   # Add this dll to the database of dll names
1290   my $output_file = $opt_output_directory."/dlls.db";
1291 
1292   # Append the dllname to the output db of names
1293   open(DLLDB,">>$output_file") || die "Couldn't create $output_file\n";
1294   print DLLDB $spec_details->{DLL_NAME},"\n";
1295   close(DLLDB);
1296 
1297   if ($opt_output_format eq "s")
1298   {
1299     output_sgml_dll_file($spec_details);
1300     return;
1301   }
1302 }
1303 
1304 #
1305 # OUTPUT FUNCTIONS
1306 # ----------------
1307 # Only these functions know anything about formatting for a specific
1308 # output type. The functions above work only with plain text.
1309 # This is to allow new types of output to be added easily.
1310 
1311 # Open the api file
1312 sub output_open_api_file($)
1313 {
1314   my $output_name = shift;
1315   $output_name = $opt_output_directory."/".$output_name;
1316 
1317   if ($opt_output_format eq "h")
1318   {
1319     $output_name = $output_name.".html";
1320   }
1321   elsif ($opt_output_format eq "s")
1322   {
1323     $output_name = $output_name.".sgml";
1324   }
1325   else
1326   {
1327     $output_name = $output_name.".".$opt_manual_section;
1328   }
1329   open(OUTPUT,">$output_name") || die "Couldn't create file '$output_name'\n";
1330 }
1331 
1332 # Close the api file
1333 sub output_close_api_file()
1334 {
1335   close (OUTPUT);
1336 }
1337 
1338 # Output the api file header
1339 sub output_api_header($)
1340 {
1341   my $comment = shift;
1342 
1343   if ($opt_output_format eq "h")
1344   {
1345       print OUTPUT "<!-- Generated file - DO NOT EDIT! -->\n";
1346       print OUTPUT "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n";
1347       print OUTPUT "<HTML>\n<HEAD>\n";
1348       print OUTPUT "<LINK REL=\"StyleSheet\" href=\"apidoc.css\" type=\"text/css\">\n";
1349       print OUTPUT "<META NAME=\"GENERATOR\" CONTENT=\"tools/c2man.pl\">\n";
1350       print OUTPUT "<META NAME=\"keywords\" CONTENT=\"Win32,Wine,API,$comment->{COMMENT_NAME}\">\n";
1351       print OUTPUT "<TITLE>Wine API: $comment->{COMMENT_NAME}</TITLE>\n</HEAD>\n<BODY>\n";
1352   }
1353   elsif ($opt_output_format eq "s")
1354   {
1355       print OUTPUT "<!-- Generated file - DO NOT EDIT! -->\n",
1356                    "<sect1>\n",
1357                    "<title>$comment->{COMMENT_NAME}</title>\n";
1358   }
1359   else