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 © ".$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> <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/\&/\&/g;
1581 s/\</\</g;
1582 s/\>/\>/g;
1583 s/\([Cc]\)/\©/g;
1584 s/\(tm\)/®/;
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/(\-\>\;[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;
This page was automatically generated by the
LXR engine.
Visit the LXR main site for more
information.