Add a tool that (semi)automatically created the API documentation
[openssl.git] / fips / tools / api_list.pl
1 #!/bin/env perl
2 #
3 # Quick and dirty utility to help assemble the mandated (but otherwise
4 # useless) API documentation. We get the list of external function
5 # symbols from fipscanister.o, pair those with the source file names
6 # (from ./fips/fipssyms.h), and map to the object file name containing
7 # them.
8
9 # Requires the "nm" and "find" utilities.
10 # Execure from the root of the FIPS module source code workarea
11
12 use HTML::Entities;
13 use File::Basename;
14
15 $here = dirname($0);
16 require "$here/api_fns.pm";
17
18 $_direction_question = ''; # Set to '?' to show "<-?", "<->?" for uncertain directions
19
20 print STDERR "Info: finding FIPS renames and reimplementations of OpenSSL symbols\n";
21 # Get mapping of old (source code) to new (live as renamed) symbols
22 foreach $file ("./fips/fipssyms.h") {
23     open(IN, $file) || die "Error opening $file";
24     # grab pairs until assembler symbols
25     my $buf = '';
26     my $reimplementations = 1;  # When 1, we're looking at reimplementations
27                                 # (not renames) of OpenSSL functions.  They
28                                 # still have to be saved to get the API.
29     while (<IN>) {
30         $reimplementations = 0 if m|^\s*/\*\sRename\ssymbols\s|;
31
32         if ($buf) {
33             $_ = $buf . $_;
34             $buf = '';
35         }
36         if (s/\\\n$//) {
37             $buf = $_;
38             next;
39         }
40         if (m/\(/) {
41             ($oldname, $newname) = m/#define\s+(\S+)\(.*\)\s+(\S+)\(.*\)/;
42         } else {
43             ($oldname, $newname) = m/#define\s+(\S+)\s+(\S+)/;
44         }
45
46         $oldname || next;
47         if (!$reimplementations) {
48             $oldname{$newname} = $oldname;
49         }
50         $oldimpl{$newname} = $oldname;
51         last if (/assembler/)
52     }
53     close(IN);
54     # %oldname is the mapping of new function names to old
55     print "<!-- Total of ", scalar(keys %oldname), " mapped symbols in $file -->\n";
56 }
57
58 print STDERR "Info: finding FIPS symbols in object files\n";
59 # generate list of external function names in fipscanister.o
60 $file = "./fips/fipscanister.o";
61 for (`nm -g --defined-only -p -o $file`) {
62     chomp;
63     s/^\S+ T // || next;
64     m/^fips_/ && next;
65     $fipssyms{$_}++;
66     $objname =~ s/\.o$/\.\[o\|c\]/;
67     $objname{$symname} = $objname;
68 }
69 # keys %fipssyms is the list of module functions
70 print "<!-- Total of ", scalar(keys %fipssyms), " functions in $file -->\n";
71
72 # grab filename to symbol name mapping, each line is of the format
73 #       ./fips/sha/fips_sha1_selftest.o:00000000 T FIPS_selftest_sha1
74 # discard the offset and type ":00000000 T".
75 for (`find . -name '*.o' \\! -name 'fipscanister.o' -exec nm -g --defined-only -p -o {} \\;`) {
76         ($objname, $symname) = m/^(\S+):\S+\s+T+\s+(\S+)/;
77         $objname || next;
78 #       $fipssyms{$symname} || next;
79         $objname =~ s/\.o$/\.\[o\|c\]/;
80         $objname{$symname} = $objname;
81         }
82 # %objname is the mapping of new symbol name to (source/object) file name
83 print "<!-- Total of ", scalar(keys %objname), " functions found in files -->\n";
84
85 print STDERR "Info: finding declarations in header files\n";
86
87 # grab filenames in include/openssl, run each of them through
88 # get_function_declarations_from_file (defined in api_fns.pl)
89 # and collect the result.
90 %declarations = ();
91 while (<include/openssl/*.h ./crypto/cryptlib.h>) {
92     my %decls = api_data::get_function_declaration_strings_from_file($_);
93     map { $declarations{$_} = $decls{$_} } keys %decls;
94 }
95 # %declarations is the mapping of old symbol name to their declaration
96 print "<!-- Total of ", scalar(keys %declarations), " declarations found in header files -->\n";
97
98 # Add the markers FIPS_text_start and FIPS_text_end
99 $declarations{FIPS_text_start} = "void *FIPS_text_start()";
100 $declarations{FIPS_text_end} = "void *FIPS_text_end()";
101
102
103 # Read list of API names obtained from edited "nm -g fipscanister.o"
104 $spill = 0;
105 sub printer {
106     foreach (@_) {
107         if ($_->{kind} >= 0) {
108             if ($spill) {
109                 print " " x $indent;
110                 print "kind:     ",$_->{kind} ? "function" : "variable","\n";
111                 print " " x $indent;
112                 print "sym:      ",$_->{sym},"\n";
113                 print " " x $indent;
114                 print "type:     ",$_->{type},"\n";
115             }
116             if ($_->{kind}) {
117                 $c = 0;
118                 map {
119                     if ($spill) {
120                         print " " x $indent;
121                         printf "param %d:\n", ++$c;
122                     }
123                     $indent += 2;
124                     printer($_);
125                     my $direction = $_->{direction};
126                     if (!$_direction_question) {
127                         $direction =~ s/<-\? <->\?/<->/;
128                         $direction =~ s/\?//g;
129                     }
130                     print " " x $indent,$direction," ",$_->{sym},"\n";
131                     $indent -= 2;
132                 } @{$_->{params}};
133                 if ($_->{type} !~ m/^\s*void\s*$/) {
134                     print " " x $indent;
135                     print "<- Return\n";
136                 }
137             }
138         } else {
139             if ($spill) {
140                 print " " x $indent;
141                 print "decl:     ",$_->{decl},"\n";
142             }
143         }
144     }
145 }
146
147 sub html_printer {
148     my $print_mode = shift;     # 0 = print declaration with symbol in bold,
149                                 #     call recursively with 1 for each parameter,
150                                 #     call recursively with 2 for each parameter
151                                 # 1 = print declaration with sym grey background,
152                                 #     call recursivelt with 3 for each parameter
153                                 # 2 = just print declaration
154     my $d = shift;              # Parsed declaration
155     my $s = '';
156
157     if ($print_mode == 0) {
158         $d->{sym} || return $s;
159         my $h = "<hr><br />\n";
160         $h .= $d->{sym} . ($d->{symcomment} ? " " . $d->{symcomment} : "");
161         $h .= " in file " . $d->{objfile} . "<br />\n<br />\n";
162
163         $s .= '<b>' . $d->{sym} . '</b>';
164         if ($d->{kind} == 1) {
165             $s .= '(';
166             $s .= join(', ',
167                        map {
168                            html_printer(1,$_);
169                        } @{$d->{params}});
170             $s .= ')';
171         }
172         my $t = $d->{type};
173         $t =~ s/\?/$s/;
174         $s = $t;
175         if ($d->{kind} == 1) {
176             map {
177                 my $direction = $_->{direction};
178                 if (!$_direction_question) {
179                     $direction =~ s/<-\? <->\?/<->/;
180                     $direction =~ s/\?//g;
181                 }
182                 $s .= "<br />\n";
183                 $s .= encode_entities($direction
184                                       . "\xA0" x (9 - length($direction)));
185                 $s .= $_->{sym};
186             } @{$d->{params}};
187         }
188         if ($d->{type} !~ m/^\s*void\s*\?$/) {
189             $s .= "<br />\n";
190             $s .= encode_entities('<-'.("\xA0" x 7).'Return');
191         }
192         $s = $h . $s;
193     } elsif ($print_mode == 1) {
194         $s .= '<span style="background: #c0c0c0">' . $d->{sym} . '</span>';
195         if ($d->{kind} == 1) {
196             $s .= '(';
197             $s .= join(', ',
198                        map {
199                            html_printer(3,$_);
200                        } @{$d->{params}});
201             $s .= ')';
202         }
203         my $t = $d->{type};
204         $t =~ s/\?/$s/;
205         $s = $t;
206     } elsif ($print_mode == 2) {
207         $s .= $d->{sym};
208         if ($d->{kind} == 1) {
209             $s .= '(';
210             $s .= join(', ',
211                        map {
212                            html_printer(2,$_);
213                        } @{$d->{params}});
214             $s .= ')';
215         }
216         my $t = $d->{type};
217         $t =~ s/\?/$s/;
218         $s = $t;
219     }
220     return $s;
221 }
222
223 print STDERR "Info: building/updating symbol information database\n";
224
225 $d = api_data->new();
226 if (-s "$here/declarations.dat") {
227     $d->read_declaration_db("$here/declarations.dat");
228 } else {
229     print STDERR "Warning: there was no file '$here/declarations.dat'.  A new one will be created\n";
230 }
231
232 for (sort keys %fipssyms) {
233     $newname = $_;
234     $namecomment = undef;
235     if ($oldname{$newname}) {
236         $oldname = $oldname{$newname};
237         $objname = $objname{$oldname} ? $objname{$oldname} : $objname{$newname};
238         $namecomment = "(renames $oldname)";
239     } else {
240         $objname = $objname{$newname};
241     }
242     if ($oldimpl{$newname}) {
243         $apisym = $oldimpl{$newname};
244         $namecomment = "(reimplements $apisym)" if !$namecomment;
245     } else {
246         $apisym = $newname;
247     }
248     $declaration = $declarations{$apisym};
249     print "<!--\n";
250     print "$newname\t\t$namecomment\tin file $objname:\n";
251     print "  ",$declaration,"\n  ";
252     $d->add_declaration($declaration,$newname,$objname,$namecomment);
253     print "-->\n";
254 }
255
256 $d->complete_directions();
257 $d->write_declaration_db("$here/declarations.dat");
258
259 print STDERR "Info: printing output\n";
260
261 $d->on_all_declarations(
262     sub {
263         my $decl = shift; 
264         #$indent = 2;
265         #print printer($decl);
266         print "<p>",html_printer(0,$decl),"</p>\n";
267     });