Prevent Winhelp from crashing when it can't find the helpfile.
[wine] / tools / c2man.pl
1 #!/usr/bin/perl
2
3 #####################################################################################
4 #
5 # c2man.pl v0.1  Copyright (C) 2000 Mike McCormack
6 #
7 # Generates Documents from C source code.
8 #
9 # Input is source code with specially formatted comments, output
10 # is man pages. The functionality is meant to be similar to c2man.
11 # The following is an example provided in the Wine documentation.
12 #
13 # This library is free software; you can redistribute it and/or
14 # modify it under the terms of the GNU Lesser General Public
15 # License as published by the Free Software Foundation; either
16 # version 2.1 of the License, or (at your option) any later version.
17 #
18 # This library is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
21 # Lesser General Public License for more details.
22 #
23 # You should have received a copy of the GNU Lesser General Public
24 # License along with this library; if not, write to the Free Software
25 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
26 #
27 # TODO:
28 #  Write code to generate HTML output with the -Th option.
29 #  Need somebody who knows about TROFF to help touch up the man page generation.
30 #  Parse spec files passed with -w option and generate pages for the functions
31 #   in the spec files only.
32 #  Modify Makefiles to pass multiple C files to speed up man page generation.
33 #  Use nm on the shared libraries specified in the spec files to determine which
34 #   source files should be parsed, and only parse them.(requires wine to be compiled)
35 #
36 #####################################################################################
37 # Input from C source file:
38 #
39 # /******************************************************************
40 #  *         CopyMetaFile32A   (GDI32.23)
41 #  *
42 #  *  Copies the metafile corresponding to hSrcMetaFile to either
43 #  *  a disk file, if a filename is given, or to a new memory based
44 #  *  metafile, if lpFileName is NULL.
45 #  *
46 #  * RETURNS
47 #  *
48 #  *  Handle to metafile copy on success, NULL on failure.
49 #  *
50 #  * BUGS
51 #  *
52 #  *  Copying to disk returns NULL even if successful.
53 #  */
54 # HMETAFILE32 WINAPI CopyMetaFile32A(
55 #                    HMETAFILE32 hSrcMetaFile, /* handle of metafile to copy */
56 #                    LPCSTR lpFilename /* filename if copying to a file */
57 # ) { ... }
58 #
59 #####################################################################################
60 # Output after processing with nroff -man
61 #
62 # CopyMetaFileA(3w)                               CopyMetaFileA(3w)
63 #
64 #
65 # NAME
66 #        CopyMetaFileA - CopyMetaFile32A   (GDI32.23)
67 #
68 # SYNOPSIS
69 #        HMETAFILE32 CopyMetaFileA
70 #        (
71 #             HMETAFILE32 hSrcMetaFile,
72 #             LPCSTR lpFilename
73 #        );
74 #
75 # PARAMETERS
76 #        HMETAFILE32 hSrcMetaFile
77 #               Handle of metafile to copy.
78 #
79 #        LPCSTR lpFilename
80 #               Filename if copying to a file.
81 #
82 # DESCRIPTION
83 #        Copies  the  metafile  corresponding  to  hSrcMetaFile  to
84 #        either a disk file, if a filename is given, or  to  a  new
85 #        memory based metafile, if lpFileName is NULL.
86 #
87 # RETURNS
88 #        Handle to metafile copy on success, NULL on failure.
89 #
90 # BUGS
91 #        Copying to disk returns NULL even if successful.
92 #
93 # SEE ALSO
94 #        GetMetaFileA(3w),   GetMetaFileW(3w),   CopyMetaFileW(3w),
95 #        PlayMetaFile(3w),  SetMetaFileBitsEx(3w),  GetMetaFileBit-
96 #        sEx(3w)
97 #
98 #####################################################################################
99
100 sub output_manpage
101 {
102     my ($buffer,$apiref) = @_;
103     my $parameters;
104     my $desc;
105
106     # join all the lines of the description together and highlight the headings
107     for (@$buffer) {
108         s/\n//g;
109         s/^\s*//g;
110         s/\s*$//g;
111         if ( /^([A-Z]+)$/ ) {
112             $desc = $desc.".SH $1\n.PP\n";
113         }
114         elsif ( /^$/ ) {
115             $desc = "$desc\n";
116         }
117         else {
118             $desc = "$desc $_";
119         }
120     }
121
122     #seperate out all the parameters
123
124     $plist = join ( ' ', @$apiref );
125
126     $name_type = $plist;
127     $name_type =~ s/\n//g;         # remove newlines
128     $name_type =~ s/\(.*$//;
129     $name_type =~ s/WINAPI//;
130
131     #check that this is a function that we want
132     if ( $funcdb{$apiname."ORD"} eq "" ) { return; }
133     print "Generating $apiname.$section\n";
134
135     $plist =~ s/\n//g;         # remove newlines
136     $plist =~ s/^.*\(\s*//;       # remove leading bracket and before
137     $plist =~ s/\s*\).*$//;       # remove trailing bracket and leftovers
138     $plist =~ s/\s*,?\s*\/\*([^*]*)\*\// - $1,/g; # move the comma to the back
139     @params = split ( /,/ , $plist);  # split parameters
140     for(@params) {
141         s/^\s*//;
142         s/\s*$//;
143     }
144
145     # figure the month and the year
146     @datetime = localtime;
147     @months = ( "January", "Febuary", "March", "April", "May", "June",
148                 "July", "August", "September", "October", "November", "December" );
149     $date = "$months[$datetime[4]] $datetime[5]";
150
151     # create the manual page
152     $manfile = "$mandir/$apiname.$section";
153     open(MAN,">$manfile") || die "Couldn't create the man page file $manfile\n";
154     print MAN ".\\\" DO NOT MODIFY THIS FILE!  It was generated by gendoc 1.0.\n";
155     print MAN ".TH $apiname \"$section\" \"$date\" \"Wine API\" \"The Wine Project\"\n";
156     print MAN ".SH NAME\n";
157     print MAN "$apiname ($apientry)\n";
158     print MAN ".SH SYNOPSIS\n";
159     print MAN ".PP\n";
160     print MAN "$name_type\n";
161     print MAN " (\n";
162     for($i=0; $i<@params; $i++) {
163         $x = ($i == (@params-1)) ? "" : ",";
164         $c = $params[$i];
165         $c =~ s/-.*//;
166         print MAN "    $c$x\n";
167     }
168     print MAN " );\n";
169     print MAN ".SH PARAMETERS\n";
170     print MAN ".PP\n";
171     for($i=0; $i<@params; $i++) {
172         print MAN "    $params[$i]\n";
173     }
174     print MAN ".SH DESCRIPTION\n";
175     print MAN ".PP\n";
176     print MAN $desc;
177     close(MAN);
178 }
179
180 #
181 # extract the comments from source file
182 #
183 sub parse_source
184 {
185   my $file = $_[0];
186   print "Processing $file\n";
187
188   open(SOURCE,"<$file") || die "Couldn't open the source file $file\n";
189   $state = 0;
190   while(<SOURCE>) {
191     if($state == 0 ) {
192         if ( /^\/\**$/ ) {
193             # find the start of the comment /**************
194             $state = 3;
195             @buffer = ();
196         }
197     }
198     elsif ($state == 3) {
199         #extract the wine API name and DLLNAME.XXX string
200         if ( / *([A-Za-z_0-9]+) *\(([A-Za-z0-9_]+\.(([0-9]+)|@))\) *$/ ) {
201             $apiname = $1;
202             $apientry = $2;
203             $state = 1;
204         }
205         else {
206             $state = 0;
207         }
208     }
209     elsif ($state == 1) {
210         #save the comment text into buffer, removing leading astericks
211         if ( /^ \*\// ) {
212             $state = 2;
213         }
214         else {
215             # find the end of the comment
216             if ( s/^ \*// ) {
217                 @buffer = ( @buffer , $_ );
218             }
219             else {
220                 $state = 0;
221             }
222         }
223     }
224     elsif ($state == 2) {
225         # check that the comment is followed by the declaration of
226         # a WINAPI function.
227         if ( /WINAPI/ ) {
228             @apidef = ( $_ );
229             #check if the function's parameters end on this line
230             if( /\)/ ) {
231                 output_manpage(\@buffer, \@apidef);
232                 $state = 0;
233             }
234             else {
235                 $state = 4;
236             }
237         }
238         else {
239             $state = 0;
240         }
241     }
242     elsif ($state == 4) {
243         @apidef = ( @apidef , $_ );
244         #find the end of the parameters list
245         if( /\)/ ) {
246             output_manpage(\@buffer, \@apidef);
247             $state = 0;
248         }
249     }
250   }
251   close(SOURCE);
252 }
253
254 # generate a database of functions to have man pages created from the source
255 # creates funclist and funcdb
256 sub parse_spec
257 {
258     my $spec = $_[0];
259     my $name,$type,$ord,$func;
260
261     open(SPEC,"<$spec") || die "Couldn't open the spec file $spec\n";
262     while(<SPEC>)
263     {
264         if( /^#/ ) { next; }
265         if( /^name/ ) { next; }
266         if( /^type/ ) { next; }
267         if( /^init/ ) { next; }
268         if( /^rsrc/ ) { next; }
269         if( /^import/ ) { next; }
270         if( /^\s*$/ ) { next; }
271         if( /^\s*(([0-9]+)|@)/ ) {
272             s/\(.*\)//; #remove all the args
273             ($ord,$type,$name,$func) = split( /\s+/ );
274             if(( $type eq "stub" ) || ($type eq "forward")) {next;}
275             if( $func eq "" ) { next; }
276             @funclist = ( @funclist , $func );
277             $funcdb{$func."ORD"} = $ord;
278             $funcdb{$func."TYPE"} = $type;
279             $funcdb{$func."NAME"} = $name;
280             $funcdb{$func."SPEC"} = $spec;
281         }
282     }
283     close(SPEC);
284 }
285
286 ######################################################################
287
288 #main starts here
289
290 $mandir = "man3w";
291 $section = "3";
292
293 #process args
294 while(@ARGV) {
295     if($ARGV[0] eq "-o") {      # extract output directory
296         shift @ARGV;
297         $mandir = $ARGV[0];
298         shift @ARGV;
299         next;
300     }
301     if($ARGV[0] =~ s/^-S// ) {  # extract man section
302         $section = $ARGV[0];
303         shift @ARGV;
304         next;
305     }
306     if($ARGV[0] =~ s/^-w// ) {  # extract man section
307         shift @ARGV;
308         @specfiles = ( @specfiles , $ARGV[0] );
309         shift @ARGV;
310         next;
311     }
312     if($ARGV[0] =~ s/^-T// ) {
313         die "FIXME: Only NROFF supported\n";
314     }
315     if($ARGV[0] =~ s/^-[LDiI]// ) {  #compatible with C2MAN flags
316         shift @ARGV;
317         next;
318     }
319     last; # stop after there's no more flags
320 }
321
322 #print "manual section: $section\n";
323 #print "man directory : $mandir\n";
324 #print "input files   : @ARGV\n";
325 #print "spec files    : @specfiles\n";
326
327 while(@specfiles) {
328     parse_spec($specfiles[0]);
329     shift @specfiles;
330 }
331
332 #print "Functions: @funclist\n";
333
334 while(@ARGV) {
335     parse_source($ARGV[0]);
336     shift @ARGV;
337 }