Update winapi and c2man so they know about -ret16. Remove support for
[wine] / tools / c2man.pl
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21 #
22 # TODO
23 #  SGML gurus - feel free to smarten up the SGML.
24 #  Add any other relevant information for the dll - imports etc
25 #  Should we have a special output mode for WineHQ?
26
27 use strict;
28 use bytes;
29
30 # Function flags. most of these come from the spec flags
31 my $FLAG_DOCUMENTED = 1;
32 my $FLAG_NONAME     = 2;
33 my $FLAG_I386       = 4;
34 my $FLAG_REGISTER   = 8;
35 my $FLAG_APAIR      = 16; # The A version of a matching W function
36 my $FLAG_WPAIR      = 32; # The W version of a matching A function
37
38
39 # Options
40 my $opt_output_directory = "man3w"; # All default options are for nroff (man pages)
41 my $opt_manual_section = "3w";
42 my $opt_wine_root_dir = "";
43 my $opt_output_format = "";         # '' = nroff, 'h' = html, 's' = sgml
44 my $opt_output_empty = 0;           # Non-zero = Create 'empty' comments (for every implemented function)
45 my $opt_fussy = 1;                  # Non-zero = Create only if we have a RETURNS section
46 my $opt_verbose = 0;                # >0 = verbosity. Can be given multiple times (for debugging)
47 my @opt_header_file_list = ();
48 my @opt_spec_file_list = ();
49 my @opt_source_file_list = ();
50
51 # All the collected details about all the .spec files being processed
52 my %spec_files;
53 # All the collected details about all the source files being processed
54 my %source_files;
55 # All documented functions that are to be placed in the index
56 my @index_entries_list = ();
57
58 # useful globals
59 my $pwd = `pwd`."/";
60 $pwd =~ s/\n//;
61 my @datetime = localtime;
62 my @months = ( "Jan", "Feb", "Mar", "Apr", "May", "Jun",
63                "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" );
64 my $year = $datetime[5] + 1900;
65 my $date = "$months[$datetime[4]] $year";
66
67 sub usage
68 {
69   print "\nCreate API Documentation from Wine source code.\n\n",
70         "Usage: c2man.pl [options] {-w <spec>} {-I <include>} {<source>}\n",
71         "Where: <spec> is a .spec file giving a DLL's exports.\n",
72         "       <include> is an include directory used by the DLL.\n",
73         "       <source> is a source file of the DLL.\n",
74         "       The above can be given multiple times on the command line, as appropriate.\n",
75         "Options:\n",
76         " -Th      : Output HTML instead of a man page\n",
77         " -Ts      : Output SGML (Docbook source) instead of a man page\n",
78         " -o <dir> : Create output in <dir>, default is \"",$opt_output_directory,"\"\n",
79         " -s <sect>: Set manual section to <sect>, default is ",$opt_manual_section,"\n",
80         " -e       : Output \"FIXME\" documentation from empty comments.\n",
81         " -v       : Verbosity. Can be given more than once for more detail.\n";
82 }
83
84 # Print usage if we're called with no args
85 if( @ARGV == 0)
86 {
87   usage();
88 }
89
90 # Process command line options
91 while(defined($_ = shift @ARGV))
92 {
93   if( s/^-// )
94   {
95     # An option.
96     for ($_)
97     {
98       /^o$/  && do { $opt_output_directory = shift @ARGV; last; };
99       s/^S// && do { $opt_manual_section   = $_;          last; };
100       /^Th$/ && do { $opt_output_format  = "h";           last; };
101       /^Ts$/ && do { $opt_output_format  = "s";           last; };
102       /^v$/  && do { $opt_verbose++;                      last; };
103       /^e$/  && do { $opt_output_empty = 1;               last; };
104       /^L$/  && do { last; };
105       /^w$/  && do { @opt_spec_file_list = (@opt_spec_file_list, shift @ARGV); last; };
106       s/^I// && do { if ($_ ne ".") {
107                        my $include = $_."/*.h";
108                        $include =~ s/\/\//\//g;
109                        my $have_headers = `ls $include >/dev/null 2>&1`;
110                        if ($? >> 8 == 0) { @opt_header_file_list = (@opt_header_file_list, $include); }
111                      }
112                      last;
113                    };
114       s/^R// && do { if ($_ =~ /^\//) { $opt_wine_root_dir = $_; }
115                      else { $opt_wine_root_dir = `cd $pwd/$_ && pwd`; }
116                      $opt_wine_root_dir =~ s/\n//;
117                      $opt_wine_root_dir =~ s/\/\//\//g;
118                      if (! $opt_wine_root_dir =~ /\/$/ ) { $opt_wine_root_dir = $opt_wine_root_dir."/"; };
119                      last;
120              };
121       die "Unrecognised option $_\n";
122     }
123   }
124   else
125   {
126     # A source file.
127     push (@opt_source_file_list, $_);
128   }
129 }
130
131 # Remove duplicate include directories
132 my %htmp;
133 @opt_header_file_list = grep(!$htmp{$_}++, @opt_header_file_list);
134
135 if ($opt_verbose > 3)
136 {
137   print "Output dir:'".$opt_output_directory."'\n";
138   print "Section   :'".$opt_manual_section."'\n";
139   print "Format  :'".$opt_output_format."'\n";
140   print "Root    :'".$opt_wine_root_dir."'\n";
141   print "Spec files:'@opt_spec_file_list'\n";
142   print "Includes  :'@opt_header_file_list'\n";
143   print "Sources   :'@opt_source_file_list'\n";
144 }
145
146 if (@opt_spec_file_list == 0)
147 {
148   exit 0; # Don't bother processing non-dll files
149 }
150
151 # Read in each .spec files exports and other details
152 while(my $spec_file = shift @opt_spec_file_list)
153 {
154   process_spec_file($spec_file);
155 }
156
157 if ($opt_verbose > 3)
158 {
159     foreach my $spec_file ( keys %spec_files )
160     {
161         print "in '$spec_file':\n";
162         my $spec_details = $spec_files{$spec_file}[0];
163         my $exports = $spec_details->{EXPORTS};
164         for (@$exports)
165         {
166            print @$_[0].",".@$_[1].",".@$_[2].",".@$_[3]."\n";
167         }
168     }
169 }
170
171 # Extract and output the comments from each source file
172 while(defined($_ = shift @opt_source_file_list))
173 {
174   process_source_file($_);
175 }
176
177 # Write the index files for each spec
178 process_index_files();
179
180 # Write the master index file
181 output_master_index_files();
182
183 exit 0;
184
185
186 # Generate the list of exported entries for the dll
187 sub process_spec_file
188 {
189   my $spec_name = shift(@_);
190   my $dll_name  = $spec_name;
191   $dll_name =~ s/\..*//;       # Strip the file extension
192   my $uc_dll_name  = uc $dll_name;
193
194   my $spec_details =
195   {
196     NAME => $spec_name,
197     DLL_NAME => $dll_name,
198     NUM_EXPORTS => 0,
199     NUM_STUBS => 0,
200     NUM_FUNCS => 0,
201     NUM_FORWARDS => 0,
202     NUM_VARS => 0,
203     NUM_DOCS => 0,
204     CONTRIBUTORS => [ ],
205     SOURCES => [ ],
206     DESCRIPTION => [ ],
207     EXPORTS => [ ],
208     EXPORTED_NAMES => { },
209     IMPLEMENTATION_NAMES => { },
210     EXTRA_COMMENTS => [ ],
211     CURRENT_EXTRA => [ ] ,
212   };
213
214   if ($opt_verbose > 0)
215   {
216     print "Processing ".$spec_name."\n";
217   }
218
219   # We allow opening to fail just to cater for the peculiarities of
220   # the Wine build system. This doesn't hurt, in any case
221   open(SPEC_FILE, "<$spec_name") || return;
222
223   while(<SPEC_FILE>)
224   {
225     s/^\s+//;            # Strip leading space
226     s/\s+\n$/\n/;        # Strip trailing space
227     s/\s+/ /g;           # Strip multiple tabs & spaces to a single space
228     s/\s*#.*//;          # Strip comments
229     s/\(.*\)/ /;         # Strip arguments
230     s/\s+/ /g;           # Strip multiple tabs & spaces to a single space (again)
231     s/\n$//;             # Strip newline
232
233     my $flags = 0;
234     if( /\-noname/ )
235     {
236       $flags |= $FLAG_NONAME;
237     }
238     if( /\-i386/ )
239     {
240       $flags |= $FLAG_I386;
241     }
242     if( /\-register/ )
243     {
244       $flags |= $FLAG_REGISTER;
245     }
246     s/ \-[a-z0-9]+//g;   # Strip flags
247
248     if( /^(([0-9]+)|@) / )
249     {
250       # This line contains an exported symbol
251       my ($ordinal, $call_convention, $exported_name, $implementation_name) = split(' ');
252
253       for ($call_convention)
254       {
255         /^(cdecl|stdcall|varargs|pascal)$/
256                  && do { $spec_details->{NUM_FUNCS}++;    last; };
257         /^(variable|equate)$/
258                  && do { $spec_details->{NUM_VARS}++;     last; };
259         /^(extern)$/
260                  && do { $spec_details->{NUM_FORWARDS}++; last; };
261         /^stub$/ && do { $spec_details->{NUM_STUBS}++;    last; };
262         if ($opt_verbose > 0)
263         {
264           print "Warning: didn't recognise convention \'",$call_convention,"'\n";
265         }
266         last;
267       }
268
269       # Convert ordinal only names so we can find them later
270       if ($exported_name eq "@")
271       {
272         $exported_name = $uc_dll_name.'_'.$ordinal;
273       }
274       if (!defined($implementation_name))
275       {
276         $implementation_name = $exported_name;
277       }
278       if ($implementation_name eq "")
279       {
280         $implementation_name = $exported_name;
281       }
282
283       if ($implementation_name =~ /(.*?)\./)
284       {
285         $call_convention = "forward"; # Referencing a function from another dll
286         $spec_details->{NUM_FUNCS}--;
287         $spec_details->{NUM_FORWARDS}++;
288       }
289
290       # Add indices for the exported and implementation names
291       $spec_details->{EXPORTED_NAMES}{$exported_name} = $spec_details->{NUM_EXPORTS};
292       if ($implementation_name ne $exported_name)
293       {
294         $spec_details->{IMPLEMENTATION_NAMES}{$exported_name} = $spec_details->{NUM_EXPORTS};
295       }
296
297       # Add the exported entry
298       $spec_details->{NUM_EXPORTS}++;
299       my @export = ($ordinal, $call_convention, $exported_name, $implementation_name, $flags);
300       push (@{$spec_details->{EXPORTS}},[@export]);
301     }
302   }
303   close(SPEC_FILE);
304
305   # Add this .spec files details to the list of .spec files
306   $spec_files{$uc_dll_name} = [$spec_details];
307 }
308
309 # Read each source file, extract comments, and generate API documentation if appropriate
310 sub process_source_file
311 {
312   my $source_file = shift(@_);
313   my $source_details =
314   {
315     CONTRIBUTORS => [ ],
316     DEBUG_CHANNEL => "",
317   };
318   my $comment =
319   {
320     FILE => $source_file,
321     COMMENT_NAME => "",
322     ALT_NAME => "",
323     DLL_NAME => "",
324     ORDINAL => "",
325     RETURNS => "",
326     PROTOTYPE => [],
327     TEXT => [],
328   };
329   my $parse_state = 0;
330   my $ignore_blank_lines = 1;
331   my $extra_comment = 0; # 1 if this is an extra comment, i.e its not a .spec export
332
333   if ($opt_verbose > 0)
334   {
335     print "Processing ".$source_file."\n";
336   }
337   open(SOURCE_FILE,"<$source_file") || die "couldn't open ".$source_file."\n";
338
339   # Add this source file to the list of source files
340   $source_files{$source_file} = [$source_details];
341
342   while(<SOURCE_FILE>)
343   {
344     s/\n$//;   # Strip newline
345     s/^\s+//;  # Strip leading space
346     s/\s+$//;  # Strip trailing space
347     if (! /^\*\|/ )
348     {
349       # Strip multiple tabs & spaces to a single space
350       s/\s+/ /g;
351     }
352
353     if ( / +Copyright *(\([Cc]\))*[0-9 \-\,\/]*([[:alpha:][:^ascii:] \.\-]+)/ )
354     {
355       # Extract a contributor to this file
356       my $contributor = $2;
357       $contributor =~ s/ *$//;
358       $contributor =~ s/^by //;
359       $contributor =~ s/\.$//;
360       $contributor =~ s/ (for .*)/ \($1\)/;
361       if ($contributor ne "")
362       {
363         if ($opt_verbose > 3)
364         {
365           print "Info: Found contributor:'".$contributor."'\n";
366         }
367         push (@{$source_details->{CONTRIBUTORS}},$contributor);
368       }
369     }
370     elsif ( /WINE_DEFAULT_DEBUG_CHANNEL\(([A-Za-z]*)\)/ )
371     {
372       # Extract the debug channel to use
373       if ($opt_verbose > 3)
374       {
375         print "Info: Found debug channel:'".$1."'\n";
376       }
377       $source_details->{DEBUG_CHANNEL} = $1;
378     }
379
380     if ($parse_state == 0) # Searching for a comment
381     {
382       if ( /^\/\**$/ )
383       {
384         # Found a comment start
385         $comment->{COMMENT_NAME} = "";
386         $comment->{ALT_NAME} = "";
387         $comment->{DLL_NAME} = "";
388         $comment->{ORDINAL} = "";
389         $comment->{RETURNS} = "";
390         $comment->{PROTOTYPE} = [];
391         $comment->{TEXT} = [];
392         $ignore_blank_lines = 1;
393         $extra_comment = 0;
394         $parse_state = 3;
395       }
396     }
397     elsif ($parse_state == 1) # Reading in a comment
398     {
399       if ( /^\**\// )
400       {
401         # Found the end of the comment
402         $parse_state = 2;
403       }
404       elsif ( s/^\*\|/\|/ )
405       {
406         # A line of comment not meant to be pre-processed
407         push (@{$comment->{TEXT}},$_); # Add the comment text
408       }
409       elsif ( s/^ *\** *// )
410       {
411         # A line of comment, starting with an asterisk
412         if ( /^[A-Z]+$/ || $_ eq "")
413         {
414           # This is a section start, so skip blank lines before and after it.
415           my $last_line = pop(@{$comment->{TEXT}});
416           if (defined($last_line) && $last_line ne "")
417           {
418             # Put it back
419             push (@{$comment->{TEXT}},$last_line);
420           }
421           if ( /^[A-Z]+$/ )
422           {
423             $ignore_blank_lines = 1;
424           }
425           else
426           {
427             $ignore_blank_lines = 0;
428           }
429         }
430
431         if ($ignore_blank_lines == 0 || $_ ne "")
432         {
433           push (@{$comment->{TEXT}},$_); # Add the comment text
434         }
435       }
436       else
437       {
438         # This isn't a well formatted comment: look for the next one
439         $parse_state = 0;
440       }
441     }
442     elsif ($parse_state == 2) # Finished reading in a comment
443     {
444       if ( /(WINAPI|__cdecl|PASCAL|CALLBACK|FARPROC16)/ )
445       {
446         # Comment is followed by a function definition
447         $parse_state = 4; # Fall through to read prototype
448       }
449       else
450       {
451         # Allow cpp directives and blank lines between the comment and prototype
452         if ($extra_comment == 1)
453         {
454           # An extra comment not followed by a function definition
455           $parse_state = 5; # Fall through to process comment
456         }
457         elsif (!/^\#/ && !/^ *$/ && !/^__ASM_GLOBAL_FUNC/)
458         {
459           # This isn't a well formatted comment: look for the next one
460           if ($opt_verbose > 1)
461           {
462             print "Info: Function '",$comment->{COMMENT_NAME},"' not followed by prototype.\n";
463           }
464           $parse_state = 0;
465         }
466       }
467     }
468     elsif ($parse_state == 3) # Reading in the first line of a comment
469     {
470       s/^ *\** *//;
471       if ( /^(@|[A-Za-z0-9_]+) +(\(|\[)([A-Za-z0-9_]+)\.(([0-9]+)|@)(\)|\])$/ )
472       {
473         # Found a correctly formed "ApiName (DLLNAME.Ordinal)" line.
474         $comment->{COMMENT_NAME} = $1;
475         $comment->{DLL_NAME} = uc $3;
476         $comment->{ORDINAL} = $4;
477         $comment->{DLL_NAME} =~ s/^KERNEL$/KRNL386/; # Too many of these to ignore, _old_ code
478         $parse_state = 1;
479       }
480       elsif ( /^([A-Za-z0-9_]+) +\{([A-Za-z0-9_]+)\}$/ )
481       {
482         # Found a correctly formed "CommentTitle {DLLNAME}" line (extra documentation)
483         $comment->{COMMENT_NAME} = $1;
484         $comment->{DLL_NAME} = uc $2;
485         $comment->{ORDINAL} = "";
486         $extra_comment = 1;
487         $parse_state = 1;
488       }
489       else
490       {
491         # This isn't a well formatted comment: look for the next one
492         $parse_state = 0;
493       }
494     }
495
496     if ($parse_state == 4) # Reading in the function definition
497     {
498       push (@{$comment->{PROTOTYPE}},$_);
499       # Strip comments from the line before checking for ')'
500       my $stripped_line = $_;
501       $stripped_line =~ s/ *(\/\* *)(.*?)( *\*\/ *)//;
502       if ( $stripped_line =~ /\)/ )
503       {
504         # Strip a blank last line
505         my $last_line = pop(@{$comment->{TEXT}});
506         if (defined($last_line) && $last_line ne "")
507         {
508           # Put it back
509           push (@{$comment->{TEXT}},$last_line);
510         }
511
512         if ($opt_output_empty != 0 && @{$comment->{TEXT}} == 0)
513         {
514           # Create a 'not implemented' comment
515           @{$comment->{TEXT}} = ("fixme: This function has not yet been documented.");
516         }
517         $parse_state = 5;
518       }
519     }
520
521     if ($parse_state == 5) # Processing the comment
522     {
523       # Process it, if it has any text
524       if (@{$comment->{TEXT}} > 0)
525       {
526         if ($extra_comment == 1)
527         {
528           process_extra_comment($comment);
529         }
530         else
531         {
532           @{$comment->{TEXT}} = ("DESCRIPTION", @{$comment->{TEXT}});
533           process_comment($comment);
534         }
535       }
536       elsif ($opt_verbose > 1 && $opt_output_empty == 0)
537       {
538         print "Info: Function '",$comment->{COMMENT_NAME},"' has no documentation.\n";
539       }
540       $parse_state = 0;
541     }
542   }
543   close(SOURCE_FILE);
544 }
545
546 # Standardise a comments text for consistency
547 sub process_comment_text
548 {
549   my $comment = shift(@_);
550   my $i = 0;
551
552   for (@{$comment->{TEXT}})
553   {
554     if (! /^\|/ )
555     {
556       # Map I/O values. These come in too many formats to standardise now....
557       s/\[I\]|\[i\]|\[in\]|\[IN\]/\[In\] /g;
558       s/\[O\]|\[o\]|\[out\]|\[OUT\]/\[Out\]/g;
559       s/\[I\/O\]|\[I\,O\]|\[i\/o\]|\[in\/out\]|\[IN\/OUT\]/\[In\/Out\]/g;
560       # TRUE/FALSE/NULL are defines, capitilise them
561       s/True|true/TRUE/g;
562       s/False|false/FALSE/g;
563       s/Null|null/NULL/g;
564       # Preferred capitalisations
565       s/ wine| WINE/ Wine/g;
566       s/ API | api / Api /g;
567       s/DLL|Dll/dll /g;
568       s/ URL | url / Url /g;
569       s/WIN16|win16/Win16/g;
570       s/WIN32|win32/Win32/g;
571       s/WIN64|win64/Win64/g;
572       s/ ID | id / Id /g;
573       # Grammar
574       s/([a-z])\.([A-Z])/$1\. $2/g; # Space after full stop
575       s/ \:/\:/g;                   # Colons to the left
576       s/ \;/\;/g;                   # Semi-colons too
577       # Common idioms
578       s/^See ([A-Za-z0-9_]+)\.$/See $1\(\)\./;                # Referring to A version from W
579       s/^Unicode version of ([A-Za-z0-9_]+)\.$/See $1\(\)\./; # Ditto
580       s/^PARAMETERS$/PARAMS/;  # Name of parameter section should be 'PARAMS'
581       # Trademarks
582       s/( |\.)(MS|Microsoft|microsoft)( |\.)/$1Microsoft\(tm\)$3/g;
583       s/( |\.)(Windows|windows|windoze|winblows)( |\.)/$1Windows\(tm\)$3/g;
584       s/( |\.)(DOS|dos|msdos)( |\.)/$1MS-DOS\(tm\)$3/g;
585       s/( |\.)(UNIX|Unix|unix)( |\.)/$1Unix\(tm\)$3/g;
586       # Abbreviations
587       s/( char )/ character /g;
588       s/( chars )/ characters /g;
589       s/( info )/ information /g;
590       s/( app )/ application /g;
591       s/( apps )/ applications /g;
592       s/( exe )/ executable /g;
593       s/( ptr )/ pointer /g;
594       s/( obj )/ object /g;
595       s/( err )/ error /g;
596       s/( bool )/ boolean /g;
597       # Punctuation
598       if ( /\[I|\[O/ && ! /\.$/ )
599       {
600         $_ = $_."."; # Always have a full stop at the end of parameter desc.
601       }
602       elsif ($i > 0 && /^[A-Z]*$/  &&
603                !(@{$comment->{TEXT}}[$i-1] =~ /\.$/) &&
604                !(@{$comment->{TEXT}}[$i-1] =~ /\:$/))
605       {
606
607         if (!(@{$comment->{TEXT}}[$i-1] =~ /^[A-Z]*$/))
608         {
609           # Paragraphs always end with a full stop
610           @{$comment->{TEXT}}[$i-1] = @{$comment->{TEXT}}[$i-1].".";
611         }
612       }
613     }
614     $i++;
615   }
616 }
617
618 # Standardise our comment and output it if it is suitable.
619 sub process_comment
620 {
621   my $comment = shift(@_);
622
623   # Don't process this comment if the function isnt exported
624   my $spec_details = $spec_files{$comment->{DLL_NAME}}[0];
625
626   if (!defined($spec_details))
627   {
628     if ($opt_verbose > 2)
629     {
630       print "Warning: Function '".$comment->{COMMENT_NAME}."' belongs to '".
631             $comment->{DLL_NAME}."' (not passed with -w): not processing it.\n";
632     }
633     return;
634   }
635
636   if ($comment->{COMMENT_NAME} eq "@")
637   {
638     # Create an implementation name
639     $comment->{COMMENT_NAME} = $comment->{DLL_NAME}."_".$comment->{ORDINAL};
640   }
641
642   my $exported_names = $spec_details->{EXPORTED_NAMES};
643   my $export_index = $exported_names->{$comment->{COMMENT_NAME}};
644   my $implementation_names = $spec_details->{IMPLEMENTATION_NAMES};
645
646   if (!defined($export_index))
647   {
648     # Perhaps the comment uses the implementation name?
649     $export_index = $implementation_names->{$comment->{COMMENT_NAME}};
650   }
651   if (!defined($export_index))
652   {
653     # This function doesn't appear to be exported. hmm.
654     if ($opt_verbose > 2)
655     {
656       print "Warning: Function '".$comment->{COMMENT_NAME}."' claims to belong to '".
657             $comment->{DLL_NAME}."' but is not exported by it: not processing it.\n";
658     }
659     return;
660   }
661
662   # When the function is exported twice we have the second name below the first
663   # (you see this a lot in ntdll, but also in some other places).
664   my $first_line = ${@{$comment->{TEXT}}}[1];
665
666   if ( $first_line =~ /^(@|[A-Za-z0-9_]+) +(\(|\[)([A-Za-z0-9_]+)\.(([0-9]+)|@)(\)|\])$/ )
667   {
668     # Found a second name - mark it as documented
669     my $alt_index = $exported_names->{$1};
670     if (defined($alt_index))
671     {
672       if ($opt_verbose > 2)
673       {
674         print "Info: Found alternate name '",$1,"\n";
675       }
676       my $alt_export = @{$spec_details->{EXPORTS}}[$alt_index];
677       @$alt_export[4] |= $FLAG_DOCUMENTED;
678       $spec_details->{NUM_DOCS}++;
679       ${@{$comment->{TEXT}}}[1] = "";
680     }
681   }
682
683   if (@{$spec_details->{CURRENT_EXTRA}})
684   {
685     # We have an extra comment that might be related to this one
686     my $current_comment = ${@{$spec_details->{CURRENT_EXTRA}}}[0];
687     my $current_name = $current_comment->{COMMENT_NAME};
688     if ($comment->{COMMENT_NAME} =~ /^$current_name/ && $comment->{COMMENT_NAME} ne $current_name)
689     {
690       if ($opt_verbose > 2)
691       {
692         print "Linking ",$comment->{COMMENT_NAME}," to $current_name\n";
693       }
694       # Add a reference to this comment to our extra comment
695       push (@{$current_comment->{TEXT}}, $comment->{COMMENT_NAME}."()","");
696     }
697   }
698
699   # We want our docs generated using the implementation name, so they are unique
700   my $export = @{$spec_details->{EXPORTS}}[$export_index];
701   $comment->{COMMENT_NAME} = @$export[3];
702   $comment->{ALT_NAME} = @$export[2];
703
704   # Mark the function as documented
705   $spec_details->{NUM_DOCS}++;
706   @$export[4] |= $FLAG_DOCUMENTED;
707
708   # This file is used by the DLL - Make sure we get our contributors right
709   push (@{$spec_details->{SOURCES}},$comment->{FILE});
710
711   # If we have parameter comments in the prototype, extract them
712   my @parameter_comments;
713   for (@{$comment->{PROTOTYPE}})
714   {
715     s/ *\, */\,/g; # Strip spaces from around commas
716
717     if ( s/ *(\/\* *)(.*?)( *\*\/ *)// ) # Strip out comment
718     {
719       my $parameter_comment = $2;
720       if (!$parameter_comment =~ /^\[/ )
721       {
722         # Add [IO] markers so we format the comment correctly
723         $parameter_comment = "[fixme] ".$parameter_comment;
724       }
725       if ( /( |\*)([A-Za-z_]{1}[A-Za-z_0-9]*)(\,|\))/ )
726       {
727         # Add the parameter name
728         $parameter_comment = $2." ".$parameter_comment;
729       }
730       push (@parameter_comments, $parameter_comment);
731     }
732   }
733
734   # If we extracted any prototype comments, add them to the comment text.
735   if (@parameter_comments)
736   {
737     @parameter_comments = ("PARAMS", @parameter_comments);
738     my @new_comment = ();
739     my $inserted_params = 0;
740
741     for (@{$comment->{TEXT}})
742     {
743       if ( $inserted_params == 0 && /^[A-Z]+$/ )
744       {
745         # Found a section header, so this is where we insert
746         push (@new_comment, @parameter_comments);
747         $inserted_params = 1;
748       }
749       push (@new_comment, $_);
750     }
751     if ($inserted_params == 0)
752     {
753       # Add them to the end
754       push (@new_comment, @parameter_comments);
755     }
756     $comment->{TEXT} = [@new_comment];
757   }
758
759   if ($opt_fussy == 1 && $opt_output_empty == 0)
760   {
761     # Reject any comment that doesn't have a description or a RETURNS section.
762     # This is the default for now, 'coz many comments aren't suitable.
763     my $found_returns = 0;
764     my $found_description_text = 0;
765     my $in_description = 0;
766     for (@{$comment->{TEXT}})
767     {
768       if ( /^RETURNS$/ )
769       {
770         $found_returns = 1;
771         $in_description = 0;
772       }
773       elsif ( /^DESCRIPTION$/ )
774       {
775         $in_description = 1;
776       }
777       elsif ($in_description == 1)
778       {
779         if ( !/^[A-Z]+$/ )
780         {
781           # Dont reject comments that refer to another doc (e.g. A/W)
782           if ( /^See ([A-Za-z0-9_]+)\.$/ )
783           {
784             if ($comment->{COMMENT_NAME} =~ /W$/ )
785             {
786               # This is probably a Unicode version of an Ascii function.
787               # Create the Ascii name and see if its been documented
788               my $ascii_name = $comment->{COMMENT_NAME};
789               $ascii_name =~ s/W$/A/;
790
791               my $ascii_export_index = $exported_names->{$ascii_name};
792
793               if (!defined($ascii_export_index))
794               {
795                 $ascii_export_index = $implementation_names->{$ascii_name};
796               }
797               if (!defined($ascii_export_index))
798               {
799                 if ($opt_verbose > 2)
800                 {
801                   print "Warning: Function '".$comment->{COMMENT_NAME}."' is not an A/W pair.\n";
802                 }
803               }
804               else
805               {
806                 my $ascii_export = @{$spec_details->{EXPORTS}}[$ascii_export_index];
807                 if (@$ascii_export[4] & $FLAG_DOCUMENTED)
808                 {
809                   # Flag these functions as an A/W pair
810                   @$ascii_export[4] |= $FLAG_APAIR;
811                   @$export[4] |= $FLAG_WPAIR;
812                 }
813               }
814             }
815             $found_returns = 1;
816           }
817           elsif ( /^Unicode version of ([A-Za-z0-9_]+)\.$/ )
818           {
819             @$export[4] |= $FLAG_WPAIR; # Explicitly marked as W version
820             $found_returns = 1;
821           }
822           $found_description_text = 1;
823         }
824         else
825         {
826           $in_description = 0;
827         }
828       }
829     }
830     if ($found_returns == 0 || $found_description_text == 0)
831     {
832       if ($opt_verbose > 2)
833       {
834         print "Info: Function '",$comment->{COMMENT_NAME},"' has no ",
835               "description and/or RETURNS section, skipping\n";
836       }
837       $spec_details->{NUM_DOCS}--;
838       @$export[4] &= ~$FLAG_DOCUMENTED;
839       return;
840     }
841   }
842
843   process_comment_text($comment);
844
845   # Strip the prototypes return value, call convention, name and brackets
846   # (This leaves it as a list of types and names, or empty for void functions)
847   my $prototype = join(" ", @{$comment->{PROTOTYPE}});
848   $prototype =~ s/  / /g;
849   $prototype =~ s/^(.*?) (WINAPIV|WINAPI|__cdecl|PASCAL|CALLBACK|FARPROC16) (.*?)\( *(.*)/$4/;
850   $comment->{RETURNS} = $1;
851   $prototype =~ s/ *\).*//;        # Strip end bracket
852   $prototype =~ s/ *\* */\*/g;     # Strip space around pointers
853   $prototype =~ s/ *\, */\,/g;     # Strip space around commas
854   $prototype =~ s/^(void|VOID)$//; # If void, leave blank
855   $prototype =~ s/\*([A-Za-z_])/\* $1/g; # Separate pointers from parameter name
856   @{$comment->{PROTOTYPE}} = split ( /,/ ,$prototype);
857
858   # FIXME: If we have no parameters, make sure we have a PARAMS: None. section
859
860   # Find header file
861   my $h_file = "";
862   if (@$export[4] & $FLAG_NONAME)
863   {
864     $h_file = "Exported by ordinal only. Use GetProcAddress() to obtain a pointer to the function.";
865   }
866   else
867   {
868     if ($comment->{COMMENT_NAME} ne "")
869     {
870       my $tmp = "grep -s -l $comment->{COMMENT_NAME} @opt_header_file_list 2>/dev/null";
871       $tmp = `$tmp`;
872       my $exit_value  = $? >> 8;
873       if ($exit_value == 0)
874       {
875         $tmp =~ s/\n.*//g;
876         if ($tmp ne "")
877         {
878           $h_file = `basename $tmp`;
879         }
880       }
881     }
882     elsif ($comment->{ALT_NAME} ne "")
883     {
884       my $tmp = "grep -s -l $comment->{ALT_NAME} @opt_header_file_list"." 2>/dev/null";
885       $tmp = `$tmp`;
886       my $exit_value  = $? >> 8;
887       if ($exit_value == 0)
888       {
889         $tmp =~ s/\n.*//g;
890         if ($tmp ne "")
891         {
892           $h_file = `basename $tmp`;
893         }
894       }
895     }
896     $h_file =~ s/^ *//;
897     $h_file =~ s/\n//;
898     if ($h_file eq "")
899     {
900       $h_file = "Not defined in a Wine header. The function is either undocumented, or missing from Wine."
901     }
902     else
903     {
904       $h_file = "Defined in \"".$h_file."\".";
905     }
906   }
907
908   # Find source file
909   my $c_file = $comment->{FILE};
910   if ($opt_wine_root_dir ne "")
911   {
912     my $cfile = $pwd."/".$c_file;     # Current dir + file
913     $cfile =~ s/(.+)(\/.*$)/$1/;      # Strip the filename
914     $cfile = `cd $cfile && pwd`;      # Strip any relative parts (e.g. "../../")
915     $cfile =~ s/\n//;                 # Strip newline
916     my $newfile = $c_file;
917     $newfile =~ s/(.+)(\/.*$)/$2/;    # Strip all but the filename
918     $cfile = $cfile."/".$newfile;     # Append filename to base path
919     $cfile =~ s/$opt_wine_root_dir//; # Get rid of the root directory
920     $cfile =~ s/\/\//\//g;            # Remove any double slashes
921     $cfile =~ s/^\/+//;               # Strip initial directory slash
922     $c_file = $cfile;
923   }
924   $c_file = "Implemented in \"".$c_file."\".";
925
926   # Add the implementation details
927   push (@{$comment->{TEXT}}, "IMPLEMENTATION","",$h_file,"",$c_file);
928
929   if (@$export[4] & $FLAG_I386)
930   {
931     push (@{$comment->{TEXT}}, "", "Available on x86 platforms only.");
932   }
933   if (@$export[4] & $FLAG_REGISTER)
934   {
935     push (@{$comment->{TEXT}}, "", "This function passes one or more arguments in registers. ",
936           "For more details, please read the source code.");
937   }
938   my $source_details = $source_files{$comment->{FILE}}[0];
939   if ($source_details->{DEBUG_CHANNEL} ne "")
940   {
941     push (@{$comment->{TEXT}}, "", "Debug channel \"".$source_details->{DEBUG_CHANNEL}."\".");
942   }
943
944   # Write out the documentation for the API
945   output_comment($comment)
946 }
947
948 # process our extra comment and output it if it is suitable.
949 sub process_extra_comment
950 {
951   my $comment = shift(@_);
952
953   my $spec_details = $spec_files{$comment->{DLL_NAME}}[0];
954
955   if (!defined($spec_details))
956   {
957     if ($opt_verbose > 2)
958     {
959       print "Warning: Extra comment '".$comment->{COMMENT_NAME}."' belongs to '".
960             $comment->{DLL_NAME}."' (not passed with -w): not processing it.\n";
961     }
962     return;
963   }
964
965   # Check first to see if this is documentation for the DLL.
966   if ($comment->{COMMENT_NAME} eq $comment->{DLL_NAME})
967   {
968     if ($opt_verbose > 2)
969     {
970       print "Info: Found DLL documentation\n";
971     }
972     for (@{$comment->{TEXT}})
973     {
974       push (@{$spec_details->{DESCRIPTION}}, $_);
975     }
976     return;
977   }
978
979   # Add the comment to the DLL page as a link
980   push (@{$spec_details->{EXTRA_COMMENTS}},$comment->{COMMENT_NAME});
981
982   # If we have a prototype, process as a regular comment
983   if (@{$comment->{PROTOTYPE}})
984   {
985     $comment->{ORDINAL} = "@";
986
987     # Add an index for the comment name
988     $spec_details->{EXPORTED_NAMES}{$comment->{COMMENT_NAME}} = $spec_details->{NUM_EXPORTS};
989
990     # Add a fake exported entry
991     $spec_details->{NUM_EXPORTS}++;
992     my ($ordinal, $call_convention, $exported_name, $implementation_name, $documented) =
993      ("@", "fake", $comment->{COMMENT_NAME}, $comment->{COMMENT_NAME}, 0);
994     my @export = ($ordinal, $call_convention, $exported_name, $implementation_name, $documented);
995     push (@{$spec_details->{EXPORTS}},[@export]);
996     @{$comment->{TEXT}} = ("DESCRIPTION", @{$comment->{TEXT}});
997     process_comment($comment);
998     return;
999   }
1000
1001   if ($opt_verbose > 0)
1002   {
1003     print "Processing ",$comment->{COMMENT_NAME},"\n";
1004   }
1005
1006   if (@{$spec_details->{CURRENT_EXTRA}})
1007   {
1008     my $current_comment = ${@{$spec_details->{CURRENT_EXTRA}}}[0];
1009
1010     if ($opt_verbose > 0)
1011     {
1012       print "Processing old current: ",$current_comment->{COMMENT_NAME},"\n";
1013     }
1014     # Output the current comment
1015     process_comment_text($current_comment);
1016     output_open_api_file($current_comment->{COMMENT_NAME});
1017     output_api_header($current_comment);
1018     output_api_name($current_comment);
1019     output_api_comment($current_comment);
1020     output_api_footer($current_comment);
1021     output_close_api_file();
1022   }
1023
1024   if ($opt_verbose > 2)
1025   {
1026     print "Setting current to ",$comment->{COMMENT_NAME},"\n";
1027   }
1028
1029   my $comment_copy =
1030   {
1031     FILE => $comment->{FILE},
1032     COMMENT_NAME => $comment->{COMMENT_NAME},
1033     ALT_NAME => $comment->{ALT_NAME},
1034     DLL_NAME => $comment->{DLL_NAME},
1035     ORDINAL => $comment->{ORDINAL},
1036     RETURNS => $comment->{RETURNS},
1037     PROTOTYPE => [],
1038     TEXT => [],
1039   };
1040
1041   for (@{$comment->{TEXT}})
1042   {
1043     push (@{$comment_copy->{TEXT}}, $_);
1044   }
1045   # Set this comment to be the current extra comment
1046   @{$spec_details->{CURRENT_EXTRA}} = ($comment_copy);
1047 }
1048
1049 # Write a standardised comment out in the appropriate format
1050 sub output_comment
1051 {
1052   my $comment = shift(@_);
1053
1054   if ($opt_verbose > 0)
1055   {
1056     print "Processing ",$comment->{COMMENT_NAME},"\n";
1057   }
1058
1059   if ($opt_verbose > 4)
1060   {
1061     print "--PROTO--\n";
1062     for (@{$comment->{PROTOTYPE}})
1063     {
1064       print "'".$_."'\n";
1065     }
1066
1067     print "--COMMENT--\n";
1068     for (@{$comment->{TEXT} })
1069     {
1070       print $_."\n";
1071     }
1072   }
1073
1074   output_open_api_file($comment->{COMMENT_NAME});
1075   output_api_header($comment);
1076   output_api_name($comment);
1077   output_api_synopsis($comment);
1078   output_api_comment($comment);
1079   output_api_footer($comment);
1080   output_close_api_file();
1081 }
1082
1083 # Write out an index file for each .spec processed
1084 sub process_index_files
1085 {
1086   foreach my $spec_file (keys %spec_files)
1087   {
1088     my $spec_details = $spec_files{$spec_file}[0];
1089     if (defined ($spec_details->{DLL_NAME}))
1090     {
1091       if (@{$spec_details->{CURRENT_EXTRA}})
1092       {
1093         # We have an unwritten extra comment, write it
1094         my $current_comment = ${@{$spec_details->{CURRENT_EXTRA}}}[0];
1095         process_extra_comment($current_comment);
1096         @{$spec_details->{CURRENT_EXTRA}} = ();
1097        }
1098        output_spec($spec_details);
1099     }
1100   }
1101 }
1102
1103 # Write a spec files documentation out in the appropriate format
1104 sub output_spec
1105 {
1106   my $spec_details = shift(@_);
1107
1108   if ($opt_verbose > 2)
1109   {
1110     print "Writing:",$spec_details->{DLL_NAME},"\n";
1111   }
1112
1113   # Use the comment output functions for consistency
1114   my $comment =
1115   {
1116     FILE => $spec_details->{DLL_NAME},
1117     COMMENT_NAME => $spec_details->{DLL_NAME}.".dll",
1118     ALT_NAME => $spec_details->{DLL_NAME},
1119     DLL_NAME => "",
1120     ORDINAL => "",
1121     RETURNS => "",
1122     PROTOTYPE => [],
1123     TEXT => [],
1124   };
1125   my $total_implemented = $spec_details->{NUM_FORWARDS} + $spec_details->{NUM_VARS} +
1126      $spec_details->{NUM_FUNCS};
1127   my $percent_implemented = 0;
1128   if ($total_implemented)
1129   {
1130     $percent_implemented = $total_implemented /
1131      ($total_implemented + $spec_details->{NUM_STUBS}) * 100;
1132   }
1133   $percent_implemented = int($percent_implemented);
1134   my $percent_documented = 0;
1135   if ($spec_details->{NUM_DOCS})
1136   {
1137     # Treat forwards and data as documented funcs for statistics
1138     $percent_documented = $spec_details->{NUM_DOCS} / $spec_details->{NUM_FUNCS} * 100;
1139     $percent_documented = int($percent_documented);
1140   }
1141
1142   # Make a list of the contributors to this DLL. Do this only for the source
1143   # files that make up the DLL, because some directories specify multiple dlls.
1144   my @contributors;
1145
1146   for (@{$spec_details->{SOURCES}})
1147   {
1148     my $source_details = $source_files{$_}[0];
1149     for (@{$source_details->{CONTRIBUTORS}})
1150     {
1151       push (@contributors, $_);
1152     }
1153   }
1154
1155   my %saw;
1156   @contributors = grep(!$saw{$_}++, @contributors); # remove dups, from perlfaq4 manpage
1157   @contributors = sort @contributors;
1158
1159   # Remove duplicates and blanks
1160   for(my $i=0; $i<@contributors; $i++)
1161   {
1162     if ($i > 0 && ($contributors[$i] =~ /$contributors[$i-1]/ || $contributors[$i-1] eq ""))
1163     {
1164       $contributors[$i-1] = $contributors[$i];
1165     }
1166   }
1167   undef %saw;
1168   @contributors = grep(!$saw{$_}++, @contributors);
1169
1170   if ($opt_verbose > 3)
1171   {
1172     print "Contributors:\n";
1173     for (@contributors)
1174     {
1175       print "'".$_."'\n";
1176     }
1177   }
1178   my $contribstring = join (", ", @contributors);
1179
1180   # Create the initial comment text
1181   @{$comment->{TEXT}} = (
1182     "NAME",
1183     $comment->{COMMENT_NAME}
1184   );
1185
1186   # Add the description, if we have one
1187   if (@{$spec_details->{DESCRIPTION}})
1188   {
1189     push (@{$comment->{TEXT}}, "DESCRIPTION");
1190     for (@{$spec_details->{DESCRIPTION}})
1191     {
1192       push (@{$comment->{TEXT}}, $_);
1193     }
1194   }
1195
1196   # Add the statistics and contributors
1197   push (@{$comment->{TEXT}},
1198     "STATISTICS",
1199     "Forwards: ".$spec_details->{NUM_FORWARDS},
1200     "Variables: ".$spec_details->{NUM_VARS},
1201     "Stubs: ".$spec_details->{NUM_STUBS},
1202     "Functions: ".$spec_details->{NUM_FUNCS},
1203     "Exports-Total: ".$spec_details->{NUM_EXPORTS},
1204     "Implemented-Total: ".$total_implemented." (".$percent_implemented."%)",
1205     "Documented-Total: ".$spec_details->{NUM_DOCS}." (".$percent_documented."%)",
1206     "CONTRIBUTORS",
1207     "The following people hold copyrights on the source files comprising this dll:",
1208     "",
1209     $contribstring,
1210     "Note: This list may not be complete.",
1211     "For a complete listing, see the Files \"AUTHORS\" and \"Changelog\" in the Wine source tree.",
1212     "",
1213   );
1214
1215   if ($opt_output_format eq "h")
1216   {
1217     # Add the exports to the comment text
1218     push (@{$comment->{TEXT}},"EXPORTS");
1219     my $exports = $spec_details->{EXPORTS};
1220     for (@$exports)
1221     {
1222       my $line = "";
1223
1224       # @$_ => ordinal, call convention, exported name, implementation name, flags;
1225       if (@$_[1] eq "forward")
1226       {
1227         my $forward_dll = @$_[3];
1228         $forward_dll =~ s/\.(.*)//;
1229         $line = @$_[2]." (forward to ".$1."() in ".$forward_dll."())";
1230       }
1231       elsif (@$_[1] eq "extern")
1232       {
1233         $line = @$_[2]." (extern)";
1234       }
1235       elsif (@$_[1] eq "stub")
1236       {
1237         $line = @$_[2]." (stub)";
1238       }
1239       elsif (@$_[1] eq "fake")
1240       {
1241         # Don't add this function here, it gets listed with the extra documentation
1242         if (!(@$_[4] & $FLAG_WPAIR))
1243         {
1244           # This function should be indexed
1245           push (@index_entries_list, @$_[3].",".@$_[3]);
1246         }
1247       }
1248       elsif (@$_[1] eq "equate" || @$_[1] eq "variable")
1249       {
1250         $line = @$_[2]." (data)";
1251       }
1252       else
1253       {
1254         # A function
1255         if (@$_[4] & $FLAG_DOCUMENTED)
1256         {
1257           # Documented
1258           $line = @$_[2]." (implemented as ".@$_[3]."())";
1259           if (@$_[2] ne @$_[3])
1260           {
1261             $line = @$_[2]." (implemented as ".@$_[3]."())";
1262           }
1263           else
1264           {
1265             $line = @$_[2]."()";
1266           }
1267           if (!(@$_[4] & $FLAG_WPAIR))
1268           {
1269             # This function should be indexed
1270             push (@index_entries_list, @$_[2].",".@$_[3]);
1271           }
1272         }
1273         else
1274         {
1275           $line = @$_[2]." (not documented)";
1276         }
1277       }
1278       if ($line ne "")
1279       {
1280         push (@{$comment->{TEXT}}, $line, "");
1281       }
1282     }
1283
1284     # Add links to the extra documentation
1285     if (@{$spec_details->{EXTRA_COMMENTS}})
1286     {
1287       push (@{$comment->{TEXT}}, "SEE ALSO");
1288       my %htmp;
1289       @{$spec_details->{EXTRA_COMMENTS}} = grep(!$htmp{$_}++, @{$spec_details->{EXTRA_COMMENTS}});
1290       for (@{$spec_details->{EXTRA_COMMENTS}})
1291       {
1292         push (@{$comment->{TEXT}}, $_."()", "");
1293       }
1294     }
1295   }
1296   # The dll entry should also be indexed
1297   push (@index_entries_list, $spec_details->{DLL_NAME}.",".$spec_details->{DLL_NAME});
1298
1299   # Write out the document
1300   output_open_api_file($spec_details->{DLL_NAME});
1301   output_api_header($comment);
1302   output_api_comment($comment);
1303   output_api_footer($comment);
1304   output_close_api_file();
1305
1306   # Add this dll to the database of dll names
1307   my $output_file = $opt_output_directory."/dlls.db";
1308
1309   # Append the dllname to the output db of names
1310   open(DLLDB,">>$output_file") || die "Couldn't create $output_file\n";
1311   print DLLDB $spec_details->{DLL_NAME},"\n";
1312   close(DLLDB);
1313
1314   if ($opt_output_format eq "s")
1315   {
1316     output_sgml_dll_file($spec_details);
1317     return;
1318   }
1319 }
1320
1321 #
1322 # OUTPUT FUNCTIONS
1323 # ----------------
1324 # Only these functions know anything about formatting for a specific
1325 # output type. The functions above work only with plain text.
1326 # This is to allow new types of output to be added easily.
1327
1328 # Open the api file
1329 sub output_open_api_file
1330 {
1331   my $output_name = shift(@_);
1332   $output_name = $opt_output_directory."/".$output_name;
1333
1334   if ($opt_output_format eq "h")
1335   {
1336     $output_name = $output_name.".html";
1337   }
1338   elsif ($opt_output_format eq "s")
1339   {
1340     $output_name = $output_name.".sgml";
1341   }
1342   else
1343   {
1344     $output_name = $output_name.".".$opt_manual_section;
1345   }
1346   open(OUTPUT,">$output_name") || die "Couldn't create file '$output_name'\n";
1347 }
1348
1349 # Close the api file
1350 sub output_close_api_file
1351 {
1352   close (OUTPUT);
1353 }
1354
1355 # Output the api file header
1356 sub output_api_header
1357 {
1358   my $comment = shift(@_);
1359
1360   if ($opt_output_format eq "h")
1361   {
1362       print OUTPUT "<!-- Generated file - DO NOT EDIT! -->\n";
1363       print OUTPUT "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n";
1364       print OUTPUT "<HTML>\n<HEAD>\n";
1365       print OUTPUT "<LINK REL=\"StyleSheet\" href=\"apidoc.css\" type=\"text/css\">\n";
1366       print OUTPUT "<META NAME=\"GENERATOR\" CONTENT=\"tools/c2man.pl\">\n";
1367       print OUTPUT "<META NAME=\"keywords\" CONTENT=\"Win32,Wine,API,$comment->{COMMENT_NAME}\">\n";
1368       print OUTPUT "<TITLE>Wine API: $comment->{COMMENT_NAME}</TITLE>\n</HEAD>\n<BODY>\n";
1369   }
1370   elsif ($opt_output_format eq "s")
1371   {
1372       print OUTPUT "<!-- Generated file - DO NOT EDIT! -->\n",
1373                    "<sect1>\n",
1374                    "<title>$comment->{COMMENT_NAME}</title>\n";
1375   }
1376   else
1377   {
1378       print OUTPUT ".\\\" -*- nroff -*-\n.\\\" Generated file - DO NOT EDIT!\n".
1379                    ".TH ",$comment->{COMMENT_NAME}," ",$opt_manual_section," \"",$date,"\" \"".
1380                    "Wine API\" \"Wine API\"\n";
1381   }
1382 }
1383
1384 sub output_api_footer
1385 {
1386   if ($opt_output_format eq "h")
1387   {
1388       print OUTPUT "<hr><p><i class=\"copy\">Copyright &copy ".$year." The Wine Project.".
1389                    " All trademarks are the property of their respective owners.".
1390                    " Visit <a HREF=http://www.winehq.org>WineHQ</a> for license details.".
1391                    " Generated $date.</i></p>\n</body>\n</html>\n";
1392   }
1393   elsif ($opt_output_format eq "s")
1394   {
1395       print OUTPUT "</sect1>\n";
1396       return;
1397   }
1398   else
1399   {
1400   }
1401 }
1402
1403 sub output_api_section_start
1404 {
1405   my $comment = shift(@_);
1406   my $section_name = shift(@_);
1407
1408   if ($opt_output_format eq "h")
1409   {
1410     print OUTPUT "\n<p><h2 class=\"section\">",$section_name,"</h2></p>\n";
1411   }
1412   elsif ($opt_output_format eq "s")
1413   {
1414     print OUTPUT "<bridgehead>",$section_name,"</bridgehead>\n";
1415   }
1416   else
1417   {
1418     print OUTPUT "\n\.SH ",$section_name,"\n";
1419   }
1420 }
1421
1422 sub output_api_section_end
1423 {
1424   # Not currently required by any output formats
1425 }
1426
1427 sub output_api_name
1428 {
1429   my $comment = shift(@_);
1430
1431   output_api_section_start($comment,"NAME");
1432
1433   my $dll_ordinal = "";
1434   if ($comment->{ORDINAL} ne "")
1435   {
1436     $dll_ordinal = "(".$comment->{DLL_NAME}.".".$comment->{ORDINAL}.")";
1437   }
1438   if ($opt_output_format eq "h")
1439   {
1440     print OUTPUT "<p><b class=\"func_name\">",$comment->{COMMENT_NAME},
1441                  "</b>&nbsp;&nbsp;<i class=\"dll_ord\">",
1442                  ,$dll_ordinal,"</i></p>\n";
1443   }
1444   elsif ($opt_output_format eq "s")
1445   {
1446     print OUTPUT "<para>\n  <command>",$comment->{COMMENT_NAME},"</command>  <emphasis>",
1447                  $dll_ordinal,"</emphasis>\n</para>\n";
1448   }
1449   else
1450   {
1451     print OUTPUT "\\fB",$comment->{COMMENT_NAME},"\\fR ",$dll_ordinal;
1452   }
1453
1454   output_api_section_end();
1455 }
1456
1457 sub output_api_synopsis
1458 {
1459   my $comment = shift(@_);
1460   my @fmt;
1461
1462   output_api_section_start($comment,"SYNOPSIS");
1463
1464   if ($opt_output_format eq "h")
1465   {
1466     print OUTPUT "<p><pre class=\"proto\">\n ", $comment->{RETURNS}," ",$comment->{COMMENT_NAME},"\n (\n";
1467     @fmt = ("", "\n", "<tt class=\"param\">", "</tt>");
1468   }
1469   elsif ($opt_output_format eq "s")
1470   {
1471     print OUTPUT "<screen>\n ",$comment->{RETURNS}," ",$comment->{COMMENT_NAME},"\n (\n";
1472     @fmt = ("", "\n", "<emphasis>", "</emphasis>");
1473   }
1474   else
1475   {
1476     print OUTPUT $comment->{RETURNS}," ",$comment->{COMMENT_NAME},"\n (\n";
1477     @fmt = ("", "\n", "\\fI", "\\fR");
1478   }
1479
1480   # Since our prototype is output in a pre-formatted block, line up the
1481   # parameters and parameter comments in the same column.
1482
1483   # First caluculate where the columns should start
1484   my $biggest_length = 0;
1485   for(my $i=0; $i < @{$comment->{PROTOTYPE}}; $i++)
1486   {
1487     my $line = ${@{$comment->{PROTOTYPE}}}[$i];
1488     if ($line =~ /(.+?)([A-Za-z_][A-Za-z_0-9]*)$/)
1489     {
1490       my $length = length $1;
1491       if ($length > $biggest_length)
1492       {
1493         $biggest_length = $length;
1494       }
1495     }
1496   }
1497
1498   # Now pad the string with blanks
1499   for(my $i=0; $i < @{$comment->{PROTOTYPE}}; $i++)
1500   {
1501     my $line = ${@{$comment->{PROTOTYPE}}}[$i];
1502     if ($line =~ /(.+?)([A-Za-z_][A-Za-z_0-9]*)$/)
1503     {
1504       my $pad_len = $biggest_length - length $1;
1505       my $padding = " " x ($pad_len);
1506       ${@{$comment->{PROTOTYPE}}}[$i] = $1.$padding.$2;
1507     }
1508   }
1509
1510   for(my $i=0; $i < @{$comment->{PROTOTYPE}}; $i++)
1511   {
1512     # Format the parameter name
1513     my $line = ${@{$comment->{PROTOTYPE}}}[$i];
1514     my $comma = ($i == @{$comment->{PROTOTYPE}}-1) ? "" : ",";
1515     $line =~ s/(.+?)([A-Za-z_][A-Za-z_0-9]*)$/  $fmt[0]$1$fmt[2]$2$fmt[3]$comma$fmt[1]/;
1516     print OUTPUT $line;
1517   }
1518
1519   if ($opt_output_format eq "h")
1520   {
1521     print OUTPUT " )\n\n</pre></p>\n";
1522   }
1523   elsif ($opt_output_format eq "s")
1524   {
1525     print OUTPUT " )\n</screen>\n";
1526   }
1527   else
1528   {
1529     print OUTPUT " )\n";
1530   }
1531
1532   output_api_section_end();
1533 }
1534
1535 sub output_api_comment
1536 {
1537   my $comment = shift(@_);
1538   my $open_paragraph = 0;
1539   my $open_raw = 0;
1540   my $param_docs = 0;
1541   my @fmt;
1542
1543   if ($opt_output_format eq "h")
1544   {
1545     @fmt = ("<p>", "</p>\n", "<tt class=\"const\">", "</tt>", "<b class=\"emp\">", "</b>",
1546             "<tt class=\"coderef\">", "</tt>", "<tt class=\"param\">", "</tt>",
1547             "<i class=\"in_out\">", "</i>", "<pre class=\"raw\">\n", "</pre>\n",
1548             "<table class=\"tab\"><colgroup><col><col><col></colgroup><tbody>\n",
1549             "</tbody></table>\n","<tr><td>","</td></tr>\n","</td>","</td><td>");
1550   }
1551   elsif ($opt_output_format eq "s")
1552   {
1553     @fmt = ("<para>\n","\n</para>\n","<constant>","</constant>","<emphasis>","</emphasis>",
1554             "<command>","</command>","<constant>","</constant>","<emphasis>","</emphasis>",
1555             "<screen>\n","</screen>\n",
1556             "<informaltable frame=\"none\">\n<tgroup cols=\"3\">\n<tbody>\n",
1557             "</tbody>\n</tgroup>\n</informaltable>\n","<row><entry>","</entry></row>\n",
1558             "</entry>","</entry><entry>");
1559   }
1560   else
1561   {
1562     @fmt = ("\.PP\n", "\n", "\\fB", "\\fR", "\\fB", "\\fR", "\\fB", "\\fR", "\\fI", "\\fR",
1563             "\\fB", "\\fR ", "", "", "", "","","\n.PP\n","","");
1564   }
1565
1566   # Extract the parameter names
1567   my @parameter_names;
1568   for (@{$comment->{PROTOTYPE}})
1569   {
1570     if ( /(.+?)([A-Za-z_][A-Za-z_0-9]*)$/ )
1571     {
1572       push (@parameter_names, $2);
1573     }
1574   }
1575
1576   for (@{$comment->{TEXT}})
1577   {
1578     if ($opt_output_format eq "h" || $opt_output_format eq "s")
1579     {
1580       # Map special characters
1581       s/\&/\&amp;/g;
1582       s/\</\&lt;/g;
1583       s/\>/\&gt;/g;
1584       s/\([Cc]\)/\&copy;/g;
1585       s/\(tm\)/&#174;/;
1586     }
1587
1588     if ( s/^\|// )
1589     {
1590       # Raw output
1591       if ($open_raw == 0)
1592       {
1593         if ($open_paragraph == 1)
1594         {
1595           # Close the open paragraph
1596           print OUTPUT $fmt[1];
1597           $open_paragraph = 0;
1598         }
1599         # Start raw output
1600         print OUTPUT $fmt[12];
1601         $open_raw = 1;
1602       }
1603       if ($opt_output_format eq "")
1604       {
1605         print OUTPUT ".br\n"; # Prevent 'man' running these lines together
1606       }
1607       print OUTPUT $_,"\n";
1608     }
1609     else
1610     {
1611       # Highlight strings
1612       s/(\".+?\")/$fmt[2]$1$fmt[3]/g;
1613       # Highlight literal chars
1614       s/(\'.\')/$fmt[2]$1$fmt[3]/g;
1615       s/(\'.{2}\')/$fmt[2]$1$fmt[3]/g;
1616       # Highlight numeric constants
1617       s/( |\-|\+|\.|\()([0-9\-\.]+)( |\-|$|\.|\,|\*|\?|\))/$1$fmt[2]$2$fmt[3]$3/g;
1618
1619       # Leading cases ("xxxx:","-") start new paragraphs & are emphasised
1620       # FIXME: Using bullet points for leading '-' would look nicer.
1621       if ($open_paragraph == 1)
1622       {
1623         s/^(\-)/$fmt[1]$fmt[0]$fmt[4]$1$fmt[5]/;
1624         s/^([[A-Za-z\-]+\:)/$fmt[1]$fmt[0]$fmt[4]$1$fmt[5]/;
1625       }
1626       else
1627       {
1628         s/^(\-)/$fmt[4]$1$fmt[5]/;
1629         s/^([[A-Za-z\-]+\:)/$fmt[4]$1$fmt[5]/;
1630       }
1631
1632       if ($opt_output_format eq "h")
1633       {
1634         # Html uses links for API calls
1635         s/([A-Za-z_]+[A-Za-z_0-9]+)(\(\))/<a href\=\"$1\.html\">$1<\/a>/g;
1636         # Index references
1637         s/\{\{(.*?)\}\}\{\{(.*?)\}\}/<a href\=\"$2\.html\">$1<\/a>/g;
1638         s/ ([A-Z_])(\(\))/<a href\=\"$1\.html\">$1<\/a>/g;
1639         # And references to COM objects (hey, they'll get documented one day)
1640         s/ (I[A-Z]{1}[A-Za-z0-9_]+) (Object|object|Interface|interface)/ <a href\=\"$1\.html\">$1<\/a> $2/g;
1641         # Convert any web addresses to real links
1642         s/(http\:\/\/)(.+?)($| )/<a href\=\"$1$2\">$2<\/a>$3/g;
1643       }
1644       else
1645       {
1646         if ($opt_output_format eq "")
1647         {
1648           # Give the man section for API calls
1649           s/ ([A-Za-z_]+[A-Za-z_0-9]+)\(\)/ $fmt[6]$1\($opt_manual_section\)$fmt[7]/g;
1650         }
1651         else
1652         {
1653           # Highlight API calls
1654           s/ ([A-Za-z_]+[A-Za-z_0-9]+\(\))/ $fmt[6]$1$fmt[7]/g;
1655         }
1656
1657         # And references to COM objects
1658         s/ (I[A-Z]{1}[A-Za-z0-9_]+) (Object|object|Interface|interface)/ $fmt[6]$1$fmt[7] $2/g;
1659       }
1660
1661       if ($open_raw == 1)
1662       {
1663         # Finish the raw output
1664         print OUTPUT $fmt[13];
1665         $open_raw = 0;
1666       }
1667
1668       if ( /^[A-Z]+$/ || /^SEE ALSO$/ )
1669       {
1670         # Start of a new section
1671         if ($open_paragraph == 1)
1672         {
1673           if ($param_docs == 1)
1674           {
1675             print OUTPUT $fmt[17],$fmt[15];
1676           }
1677           else
1678           {
1679             print OUTPUT $fmt[1];
1680           }
1681           $open_paragraph = 0;
1682         }
1683         output_api_section_start($comment,$_);
1684         if ( /^PARAMS$/ )
1685         {
1686           print OUTPUT $fmt[14];
1687           $param_docs = 1;
1688         }
1689         else
1690         {
1691           #print OUTPUT $fmt[15];
1692           $param_docs = 0;
1693         }
1694       }
1695       elsif ( /^$/ )
1696       {
1697         # Empty line, indicating a new paragraph
1698         if ($open_paragraph == 1)
1699         {
1700           if ($param_docs == 0)
1701           {
1702             print OUTPUT $fmt[1];
1703             $open_paragraph = 0;
1704           }
1705         }
1706       }
1707       else
1708       {
1709         if ($param_docs == 1)
1710         {
1711           if ($open_paragraph == 1)
1712           {
1713             # For parameter docs, put each parameter into a new paragraph/table row
1714             print OUTPUT $fmt[17];
1715             $open_paragraph = 0;
1716           }
1717           s/(\[.+\])( *)/$fmt[19]$fmt[10]$1$fmt[11]$fmt[19] /; # Format In/Out
1718         }
1719         else
1720         {
1721           # Within paragraph lines, prevent lines running together
1722           $_ = $_." ";
1723         }
1724
1725         # Format parameter names where they appear in the comment
1726         for my $parameter_name (@parameter_names)
1727         {
1728           s/(^|[ \.\,\(\-\*])($parameter_name)($|[ \.\)\,\-\=\/])/$1$fmt[8]$2$fmt[9]$3/g;
1729         }
1730         # Structure dereferences include the dereferenced member
1731         s/(\-\>[A-Za-z_]+)/$fmt[8]$1$fmt[9]/g;
1732         s/(\-\&gt\;[A-Za-z_]+)/$fmt[8]$1$fmt[9]/g;
1733
1734         if ($open_paragraph == 0)
1735         {
1736           if ($param_docs == 1)
1737           {
1738             print OUTPUT $fmt[16];
1739           }
1740           else
1741           {
1742             print OUTPUT $fmt[0];
1743           }
1744           $open_paragraph = 1;
1745         }
1746         # Anything in all uppercase on its own gets emphasised
1747         s/(^|[ \.\,\(\[\|\=])([A-Z]+?[A-Z0-9_]+)($|[ \.\,\*\?\|\)\=\'])/$1$fmt[6]$2$fmt[7]$3/g;
1748
1749         print OUTPUT $_;
1750       }
1751     }
1752   }
1753   if ($open_raw == 1)
1754   {
1755     print OUTPUT $fmt[13];
1756   }
1757   if ($open_paragraph == 1)
1758   {
1759     print OUTPUT $fmt[1];
1760   }
1761 }
1762
1763 # Create the master index file
1764 sub output_master_index_files
1765 {
1766   if ($opt_output_format eq "")
1767   {
1768     return; # No master index for man pages
1769   }
1770
1771   if ($opt_output_format eq "h")
1772   {
1773     # Append the index entries to the output db of index entries
1774     my $output_file = $opt_output_directory."/index.db";
1775     open(INDEXDB,">>$output_file") || die "Couldn't create $output_file\n";
1776     for (@index_entries_list)
1777     {
1778       $_ =~ s/A\,/\,/;
1779       print INDEXDB $_."\n";
1780     }
1781     close(INDEXDB);
1782   }
1783
1784   # Use the comment output functions for consistency
1785   my $comment =
1786   {
1787     FILE => "",
1788     COMMENT_NAME => "The Wine Api Guide",
1789     ALT_NAME => "The Wine Api Guide",
1790     DLL_NAME => "",
1791     ORDINAL => "",
1792     RETURNS => "",
1793     PROTOTYPE => [],
1794     TEXT => [],
1795   };
1796
1797   if ($opt_output_format eq "s")
1798   {
1799     $comment->{COMMENT_NAME} = "Introduction";
1800     $comment->{ALT_NAME} = "Introduction",
1801   }
1802   elsif ($opt_output_format eq "h")
1803   {
1804     @{$comment->{TEXT}} = (
1805       "NAME",
1806        $comment->{COMMENT_NAME},
1807        "INTRODUCTION",
1808     );
1809   }
1810
1811   # Create the initial comment text
1812   push (@{$comment->{TEXT}},
1813     "This document describes the Api calls made available",
1814     "by Wine. They are grouped by the dll that exports them.",
1815     "",
1816     "Please do not edit this document, since it is generated automatically",
1817     "from the Wine source code tree. Details on updating this documentation",
1818     "are given in the \"Wine Developers Guide\".",
1819     "CONTRIBUTORS",
1820     "Api documentation is generally written by the person who ",
1821     "implements a given Api call. Authors of each dll are listed in the overview ",
1822     "section for that dll. Additional contributors who have updated source files ",
1823     "but have not entered their names in a copyright statement are noted by an ",
1824     "entry in the file \"Changelog\" from the Wine source code distribution.",
1825       ""
1826   );
1827
1828   # Read in all dlls from the database of dll names
1829   my $input_file = $opt_output_directory."/dlls.db";
1830   my @dlls = `cat $input_file|sort|uniq`;
1831
1832   if ($opt_output_format eq "h")
1833   {
1834     # HTML gets a list of all the dlls and an index. For docbook the index creates this for us
1835     push (@{$comment->{TEXT}},
1836       "INDEX",
1837       "For an alphabetical listing of the functions available, please click the ",
1838       "first letter of the functions name below:","",
1839       "[ _(), A(), B(), C(), D(), E(), F(), G(), H(), ".
1840       "I(), J(), K(), L(), M(), N(), O(), P(), Q() ".
1841       "R(), S(), T(), U(), V(), W(), X(), Y(), Z() ]", "",
1842       "DLLS",
1843       "Each dll provided by Wine is documented individually. The following dlls are provided :",
1844       ""
1845     );
1846     # Add the dlls to the comment
1847     for (@dlls)
1848     {
1849       $_ =~ s/(\..*)?\n/\(\)/;
1850       push (@{$comment->{TEXT}}, $_, "");
1851     }
1852     output_open_api_file("index");
1853   }
1854   elsif ($opt_output_format eq "s")
1855   {
1856     # Just write this as the initial blurb, with a chapter heading
1857     output_open_api_file("blurb");
1858     print OUTPUT "<chapter id =\"blurb\">\n<title>Introduction to The Wine Api Guide</title>\n"
1859   }
1860
1861   # Write out the document
1862   output_api_header($comment);
1863   output_api_comment($comment);
1864   output_api_footer($comment);
1865   if ($opt_output_format eq "s")
1866   {
1867     print OUTPUT "</chapter>\n" # finish the chapter
1868   }
1869   output_close_api_file();
1870
1871   if ($opt_output_format eq "s")
1872   {
1873     output_sgml_master_file(\@dlls);
1874     return;
1875   }
1876   if ($opt_output_format eq "h")
1877   {
1878     output_html_index_files();
1879     output_html_stylesheet();
1880     return;
1881   }
1882 }
1883
1884 # Write the master wine-api.sgml, linking it to each dll.
1885 sub output_sgml_master_file
1886 {
1887   my $dlls = shift(@_);
1888
1889   output_open_api_file("wine-api");
1890   print OUTPUT "<!-- Generated file - DO NOT EDIT! -->\n";
1891   print OUTPUT "<!doctype book PUBLIC \"-//OASIS//DTD DocBook V3.1//EN\" [\n\n";
1892   print OUTPUT "<!entity blurb SYSTEM \"blurb.sgml\">\n";
1893
1894   # List the entities
1895   for (@$dlls)
1896   {
1897     $_ =~ s/(\..*)?\n//;
1898     print OUTPUT "<!entity ",$_," SYSTEM \"",$_,".sgml\">\n"
1899   }
1900
1901   print OUTPUT "]>\n\n<book id=\"index\">\n<bookinfo><title>The Wine Api Guide</title></bookinfo>\n\n";
1902   print OUTPUT "  &blurb;\n";
1903
1904   for (@$dlls)
1905   {
1906     print OUTPUT "  &",$_,";\n"
1907   }
1908   print OUTPUT "\n\n</book>\n";
1909
1910   output_close_api_file();
1911 }
1912
1913 # Produce the sgml for the dll chapter from the generated files
1914 sub output_sgml_dll_file
1915 {
1916   my $spec_details = shift(@_);
1917
1918   # Make a list of all the documentation files to include
1919   my $exports = $spec_details->{EXPORTS};
1920   my @source_files = ();
1921   for (@$exports)
1922   {
1923     # @$_ => ordinal, call convention, exported name, implementation name, documented;
1924     if (@$_[1] ne "forward" && @$_[1] ne "extern" && @$_[1] ne "stub" && @$_[1] ne "equate" &&
1925         @$_[1] ne "variable" && @$_[1] ne "fake" && @$_[4] & 1)
1926     {
1927       # A documented function
1928       push (@source_files,@$_[3]);
1929     }
1930   }
1931
1932   push (@source_files,@{$spec_details->{EXTRA_COMMENTS}});
1933
1934   @source_files = sort @source_files;
1935
1936   # create a new chapter for this dll
1937   my $tmp_name = $opt_output_directory."/".$spec_details->{DLL_NAME}.".tmp";
1938   open(OUTPUT,">$tmp_name") || die "Couldn't create $tmp_name\n";
1939   print OUTPUT "<chapter>\n<title>$spec_details->{DLL_NAME}</title>\n";
1940   output_close_api_file();
1941
1942   # Add the sorted documentation, cleaning up as we go
1943   `cat $opt_output_directory/$spec_details->{DLL_NAME}.sgml >>$tmp_name`;
1944   for (@source_files)
1945   {
1946     `cat $opt_output_directory/$_.sgml >>$tmp_name`;
1947     `rm -f $opt_output_directory/$_.sgml`;
1948   }
1949
1950   # close the chapter, and overwite the dll source
1951   open(OUTPUT,">>$tmp_name") || die "Couldn't create $tmp_name\n";
1952   print OUTPUT "</chapter>\n";
1953   close OUTPUT;
1954   `mv $tmp_name $opt_output_directory/$spec_details->{DLL_NAME}.sgml`;
1955 }
1956
1957 # Write the html index files containing the function names
1958 sub output_html_index_files
1959 {
1960   if ($opt_output_format ne "h")
1961   {
1962     return;
1963   }
1964
1965   my @letters = ('_', 'A' .. 'Z');
1966
1967   # Read in all functions
1968   my $input_file = $opt_output_directory."/index.db";
1969   my @funcs = `cat $input_file|sort|uniq`;
1970
1971   for (@letters)
1972   {
1973     my $letter = $_;
1974     my $comment =
1975     {
1976       FILE => "",
1977       COMMENT_NAME => "",
1978       ALT_NAME => "",
1979       DLL_NAME => "",
1980       ORDINAL => "",
1981       RETURNS => "",
1982       PROTOTYPE => [],
1983       TEXT => [],
1984     };
1985
1986     $comment->{COMMENT_NAME} = $letter." Functions";
1987     $comment->{ALT_NAME} = $letter." Functions";
1988
1989     push (@{$comment->{TEXT}},
1990       "NAME",
1991       $comment->{COMMENT_NAME},
1992       "FUNCTIONS"
1993     );
1994
1995     # Add the functions to the comment
1996     for (@funcs)
1997     {
1998       my $first_char = substr ($_, 0, 1);
1999       $first_char = uc $first_char;
2000
2001       if ($first_char eq $letter)
2002       {
2003         my $name = $_;
2004         my $file;
2005         $name =~ s/(^.*?)\,(.*?)\n/$1/;
2006         $file = $2;
2007         push (@{$comment->{TEXT}}, "{{".$name."}}{{".$file."}}","");
2008       }
2009     }
2010
2011     # Write out the document
2012     output_open_api_file($letter);
2013     output_api_header($comment);
2014     output_api_comment($comment);
2015     output_api_footer($comment);
2016     output_close_api_file();
2017   }
2018 }
2019
2020 # Output the stylesheet for HTML output
2021 sub output_html_stylesheet
2022 {
2023   if ($opt_output_format ne "h")
2024   {
2025     return;
2026   }
2027
2028   my $css;
2029   ($css = <<HERE_TARGET) =~ s/^\s+//gm;
2030 /*
2031  * Default styles for Wine HTML Documentation.
2032  *
2033  * This style sheet should be altered to suit your needs/taste.
2034  */
2035 BODY { /* Page body */
2036 background-color: white;
2037 color: black;
2038 font-family: Tahoma,sans-serif;
2039 font-style: normal;
2040 font-size: 10pt;
2041 }
2042 a:link { color: #4444ff; } /* Links */
2043 a:visited { color: #333377 }
2044 a:active { color: #0000dd }
2045 H2.section { /* Section Headers */
2046 font-family: sans-serif;
2047 color: #777777;
2048 background-color: #F0F0FE;
2049 margin-left: 0.2in;
2050 margin-right: 1.0in;
2051 }
2052 b.func_name { /* Function Name */
2053 font-size: 10pt;
2054 font-style: bold;
2055 }
2056 i.dll_ord { /* Italicised DLL+ordinal */
2057 color: #888888;
2058 font-family: sans-serif;
2059 font-size: 8pt;
2060 }
2061 p { /* Paragraphs */
2062 margin-left: 0.5in;
2063 margin-right: 0.5in;
2064 }
2065 table { /* tables */
2066 margin-left: 0.5in;
2067 margin-right: 0.5in;
2068 }
2069 pre.proto /* API Function prototype */
2070 {
2071 border-style: solid;
2072 border-width: 1px;
2073 border-color: #777777;
2074 background-color: #F0F0BB;
2075 color: black;
2076 font-size: 10pt;
2077 vertical-align: top;
2078 margin-left: 0.5in;
2079 margin-right: 1.0in;
2080 }
2081 pre.raw { /* Raw text output */
2082 margin-left: 0.6in;
2083 margin-right: 1.1in;
2084 background-color: #8080DC;
2085 }
2086 tt.param { /* Parameter name */
2087 font-style: italic;
2088 color: blue;
2089 }
2090 tt.const { /* Constant */
2091 color: red;
2092 }
2093 i.in_out { /* In/Out */
2094 font-size: 8pt;
2095 color: grey;
2096 }
2097 tt.coderef { /* Code in description text */
2098 color: darkgreen;
2099 }
2100 b.emp /* Emphasis */ {
2101 font-style: bold;
2102 color: darkblue;
2103 }
2104 i.footer { /* Footer */
2105 font-family: sans-serif;
2106 font-size: 6pt;
2107 color: darkgrey;
2108 }
2109 HERE_TARGET
2110
2111   my $output_file = "$opt_output_directory/apidoc.css";
2112   open(CSS,">$output_file") || die "Couldn't create the file $output_file\n";
2113   print CSS $css;
2114   close(CSS);
2115 }