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

Wine Cross Reference
wine/tools/c2man.pl

Version: ~ [ wine-1.1.33 ] ~ [ wine-1.1.32 ] ~ [ wine-1.1.31 ] ~ [ wine-1.1.30 ] ~ [ wine-1.1.29 ] ~ [ wine-1.1.28 ] ~ [ wine-1.1.27 ] ~ [ wine-1.1.26 ] ~ [ wine-1.1.25 ] ~ [ wine-1.1.24 ] ~ [ wine-1.1.23 ] ~ [ wine-1.1.22 ] ~ [ wine-1.1.21 ] ~ [ wine-1.1.20 ] ~ [ wine-1.1.19 ] ~ [ wine-1.1.18 ] ~ [ wine-1.1.17 ] ~ [ wine-1.1.16 ] ~ [ wine-1.1.15 ] ~ [ wine-1.1.14 ] ~ [ wine-1.1.13 ] ~ [ wine-1.1.12 ] ~ [ wine-1.1.11 ] ~ [ wine-1.1.10 ] ~ [ wine-1.1.9 ] ~ [ wine-1.1.8 ] ~ [ wine-1.1.7 ] ~ [ wine-1.0.1 ] ~ [ wine-1.1.6 ] ~ [ wine-1.1.5 ] ~ [ wine-1.1.4 ] ~ [ wine-1.1.3 ] ~ [ wine-1.1.2 ] ~ [ wine-1.1.1 ] ~ [ wine-1.1.0 ] ~ [ wine-1.0 ] ~

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

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

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.