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