Major hack of mkdef.pl. There should be no more need to redo the
[openssl.git] / util / mkdef.pl
1 #!/usr/local/bin/perl -w
2 #
3 # generate a .def file
4 #
5 # It does this by parsing the header files and looking for the
6 # prototyped functions: it then prunes the output.
7 #
8 # Intermediary files are created, call libeay.num and ssleay.num,...
9 # Previously, they had the following format:
10 #
11 #       routine-name    nnnn
12 #
13 # But that isn't enough for a number of reasons, the first on being that
14 # this format is (needlessly) very Win32-centric, and even then...
15 # One of the biggest problems is that there's no information about what
16 # routines should actually be used, which varies with what crypto algorithms
17 # are disabled.  Also, some operating systems (for example VMS with VAX C)
18 # need to keep track of the global variables as well as the functions.
19 #
20 # So, a remake of this script is done so as to include information on the
21 # kind of symbol it is (function or variable) and what algorithms they're
22 # part of.  This will allow easy translating to .def files or the corresponding
23 # file in other operating systems (a .opt file for VMS, possibly with a .mar
24 # file).
25 #
26 # The format now becomes:
27 #
28 #       routine-name    nnnn    info
29 #
30 # and the "info" part is actually a colon-separated string of fields with
31 # the following meaning:
32 #
33 #       existence:platform:kind:algorithms
34 #
35 # - "existence" can be "EXIST" or "NOEXIST" depending on if the symbol is
36 #   found somewhere in the source, 
37 # - "platforms" is empty if it exists on all platforms, otherwise it contains
38 #   comma-separated list of the platform, just as they are if the symbol exists
39 #   for those platforms, or prepended with a "!" if not.  This helps resolve
40 #   symbol name replacements for platforms where the names are too long for the
41 #   compiler or linker, or if the systems is case insensitive and there is a
42 #   clash.  This script assumes those redefinitions are place in the file
43 #   crypto/idhacks.h.
44 # - "kind" is "FUNCTION" or "VARIABLE".  The meaning of that is obvious.
45 # - "algorithms" is a comma-separated list of algorithm names.  This helps
46 #   exclude symbols that are part of an algorithm that some user wants to
47 #   exclude.
48 #
49
50 my $crypto_num= "util/libeay.num";
51 my $ssl_num=    "util/ssleay.num";
52
53 my $do_update = 0;
54 my $do_rewrite = 0;
55 my $do_crypto = 0;
56 my $do_ssl = 0;
57 my $do_ctest = 0;
58 my $do_ctestall = 0;
59 my $rsaref = 0;
60
61 my $VMS=0;
62 my $W32=0;
63 my $W16=0;
64 my $NT=0;
65 # Set this to make typesafe STACK definitions appear in DEF
66 my $safe_stack_def = 0;
67
68 my @known_platforms = ( "__FreeBSD__", "VMS", "WIN16", "WIN32",
69                         "WINNT", "PERL5", "NeXT" );
70 my @known_algorithms = ( "RC2", "RC4", "RC5", "IDEA", "DES", "BF",
71                          "CAST", "MD2", "MD4", "MD5", "SHA", "RIPEMD",
72                          "MDC2", "RSA", "DSA", "DH", "HMAC", "FP_API" );
73
74 my $options="";
75 open(IN,"<Makefile.ssl") || die "unable to open Makefile.ssl!\n";
76 while(<IN>) {
77     $options=$1 if (/^OPTIONS=(.*)$/);
78 }
79 close(IN);
80
81 # The following ciphers may be excluded (by Configure). This means functions
82 # defined with ifndef(NO_XXX) are not included in the .def file, and everything
83 # in directory xxx is ignored.
84 my $no_rc2; my $no_rc4; my $no_rc5; my $no_idea; my $no_des; my $no_bf;
85 my $no_cast;
86 my $no_md2; my $no_md4; my $no_md5; my $no_sha; my $no_ripemd; my $no_mdc2;
87 my $no_rsa; my $no_dsa; my $no_dh; my $no_hmac=0;
88 my $no_fp_api;
89
90 foreach (@ARGV, split(/ /, $options))
91         {
92         $W32=1 if $_ eq "32";
93         $W16=1 if $_ eq "16";
94         if($_ eq "NT") {
95                 $W32 = 1;
96                 $NT = 1;
97         }
98         $VMS=1 if $_ eq "VMS";
99         $do_ssl=1 if $_ eq "ssleay";
100         $do_ssl=1 if $_ eq "ssl";
101         $do_crypto=1 if $_ eq "libeay";
102         $do_crypto=1 if $_ eq "crypto";
103         $do_update=1 if $_ eq "update";
104         $do_rewrite=1 if $_ eq "rewrite";
105         $do_ctest=1 if $_ eq "ctest";
106         $do_ctestall=1 if $_ eq "ctestall";
107         $rsaref=1 if $_ eq "rsaref";
108         #$safe_stack_def=1 if $_ eq "-DDEBUG_SAFESTACK";
109
110         if    (/^no-rc2$/)      { $no_rc2=1; }
111         elsif (/^no-rc4$/)      { $no_rc4=1; }
112         elsif (/^no-rc5$/)      { $no_rc5=1; }
113         elsif (/^no-idea$/)     { $no_idea=1; }
114         elsif (/^no-des$/)      { $no_des=1; }
115         elsif (/^no-bf$/)       { $no_bf=1; }
116         elsif (/^no-cast$/)     { $no_cast=1; }
117         elsif (/^no-md2$/)      { $no_md2=1; }
118         elsif (/^no-md4$/)      { $no_md4=1; }
119         elsif (/^no-md5$/)      { $no_md5=1; }
120         elsif (/^no-sha$/)      { $no_sha=1; }
121         elsif (/^no-ripemd$/)   { $no_ripemd=1; }
122         elsif (/^no-mdc2$/)     { $no_mdc2=1; }
123         elsif (/^no-rsa$/)      { $no_rsa=1; }
124         elsif (/^no-dsa$/)      { $no_dsa=1; }
125         elsif (/^no-dh$/)       { $no_dh=1; }
126         elsif (/^no-hmac$/)     { $no_hmac=1; }
127         }
128
129
130 # If no platform is given, assume WIN32
131 if ($W32 + $W16 + $VMS == 0) {
132         $W32 = 1;
133 }
134
135 # Add extra knowledge
136 if ($W16) {
137         $no_fp_api=1;
138 }
139
140 if (!$do_ssl && !$do_crypto)
141         {
142         print STDERR "usage: $0 ( ssl | crypto ) [ 16 | 32 | NT ] [rsaref]\n";
143         exit(1);
144         }
145
146 %ssl_list=&load_numbers($ssl_num);
147 $max_ssl = $max_num;
148 %crypto_list=&load_numbers($crypto_num);
149 $max_crypto = $max_num;
150
151 my $ssl="ssl/ssl.h";
152
153 my $crypto ="crypto/crypto.h";
154 $crypto.=" crypto/des/des.h" unless $no_des;
155 $crypto.=" crypto/idea/idea.h" unless $no_idea;
156 $crypto.=" crypto/rc4/rc4.h" unless $no_rc4;
157 $crypto.=" crypto/rc5/rc5.h" unless $no_rc5;
158 $crypto.=" crypto/rc2/rc2.h" unless $no_rc2;
159 $crypto.=" crypto/bf/blowfish.h" unless $no_bf;
160 $crypto.=" crypto/cast/cast.h" unless $no_cast;
161 $crypto.=" crypto/md2/md2.h" unless $no_md2;
162 $crypto.=" crypto/md4/md4.h" unless $no_md4;
163 $crypto.=" crypto/md5/md5.h" unless $no_md5;
164 $crypto.=" crypto/mdc2/mdc2.h" unless $no_mdc2;
165 $crypto.=" crypto/sha/sha.h" unless $no_sha;
166 $crypto.=" crypto/ripemd/ripemd.h" unless $no_ripemd;
167
168 $crypto.=" crypto/bn/bn.h";
169 $crypto.=" crypto/rsa/rsa.h" unless $no_rsa;
170 $crypto.=" crypto/dsa/dsa.h" unless $no_dsa;
171 $crypto.=" crypto/dh/dh.h" unless $no_dh;
172 $crypto.=" crypto/hmac/hmac.h" unless $no_hmac;
173
174 $crypto.=" crypto/stack/stack.h";
175 $crypto.=" crypto/buffer/buffer.h";
176 $crypto.=" crypto/bio/bio.h";
177 $crypto.=" crypto/dso/dso.h";
178 $crypto.=" crypto/lhash/lhash.h";
179 $crypto.=" crypto/conf/conf.h";
180 $crypto.=" crypto/txt_db/txt_db.h";
181
182 $crypto.=" crypto/evp/evp.h";
183 $crypto.=" crypto/objects/objects.h";
184 $crypto.=" crypto/pem/pem.h";
185 #$crypto.=" crypto/meth/meth.h";
186 $crypto.=" crypto/asn1/asn1.h";
187 $crypto.=" crypto/asn1/asn1_mac.h";
188 $crypto.=" crypto/err/err.h";
189 $crypto.=" crypto/pkcs7/pkcs7.h";
190 $crypto.=" crypto/pkcs12/pkcs12.h";
191 $crypto.=" crypto/x509/x509.h";
192 $crypto.=" crypto/x509/x509_vfy.h";
193 $crypto.=" crypto/x509v3/x509v3.h";
194 $crypto.=" crypto/rand/rand.h";
195 $crypto.=" crypto/comp/comp.h";
196 $crypto.=" crypto/tmdiff.h";
197
198 my $symhacks="crypto/symhacks.h";
199
200 my @ssl_symbols = &do_defs("SSLEAY", $ssl, $symhacks);
201 my @crypto_symbols = &do_defs("LIBEAY", $crypto, $symhacks);
202
203 if ($do_update) {
204
205 if ($do_ssl == 1) {
206
207         &maybe_add_info("SSLEAY",*ssl_list,@ssl_symbols);
208         if ($do_rewrite == 1) {
209                 open(OUT, ">$ssl_num");
210                 &rewrite_numbers(*OUT,"SSLEAY",*ssl_list,@ssl_symbols);
211                 close OUT;
212         } else {
213                 open(OUT, ">>$ssl_num");
214         }
215         &update_numbers(*OUT,"SSLEAY",*ssl_list,$max_ssl,@ssl_symbols);
216         close OUT;
217 }
218
219 if($do_crypto == 1) {
220
221         &maybe_add_info("LIBEAY",*crypto_list,@crypto_symbols);
222         if ($do_rewrite == 1) {
223                 open(OUT, ">$crypto_num");
224                 &rewrite_numbers(*OUT,"LIBEAY",*crypto_list,@crypto_symbols);
225         } else {
226                 open(OUT, ">>$crypto_num");
227         }
228         &update_numbers(*OUT,"LIBEAY",*crypto_list,$max_crypto,@crypto_symbols);
229         close OUT;
230
231
232 } elsif ($do_ctest || $do_ctestall) {
233
234         print <<"EOF";
235
236 /* Test file to check all DEF file symbols are present by trying
237  * to link to all of them. This is *not* intended to be run!
238  */
239
240 int main()
241 {
242 EOF
243         &print_test_file(*STDOUT,"SSLEAY",*ssl_list,$do_ctestall,@ssl_symbols)
244                 if $do_ssl == 1;
245
246         &print_test_file(*STDOUT,"LIBEAY",*crypto_list,$do_ctestall,@crypto_symbols)
247                 if $do_crypto == 1;
248
249         print "}\n";
250
251 } else {
252
253         &print_def_file(*STDOUT,"SSLEAY",*ssl_list,@ssl_symbols)
254                 if $do_ssl == 1;
255
256         &print_def_file(*STDOUT,"LIBEAY",*crypto_list,@crypto_symbols)
257                 if $do_crypto == 1;
258
259 }
260
261
262 sub do_defs
263 {
264         my($name,$files,$symhacksfile)=@_;
265         my $file;
266         my @ret;
267         my %syms;
268         my %platform;           # For anything undefined, we assume ""
269         my %kind;               # For anything undefined, we assume "FUNCTION"
270         my %algorithm;          # For anything undefined, we assume ""
271         my %rename;
272         my $cpp;
273
274         foreach $file (split(/\s+/,$symhacksfile." ".$files))
275                 {
276                 open(IN,"<$file") || die "unable to open $file:$!\n";
277                 my $line = "", my $def= "";
278                 my %tag = (
279                         (map { $_ => 0 } @known_platforms),
280                         (map { "NO_".$_ => 0 } @known_algorithms),
281                         NOPROTO         => 0,
282                         PERL5           => 0,
283                         _WINDLL         => 0,
284                         CONST_STRICT    => 0,
285                         TRUE            => 1,
286                 );
287                 my $symhacking = $file eq $symhacksfile;
288                 while(<IN>) {
289                         last if (/BEGIN ERROR CODES/);
290                         if ($line ne '') {
291                                 $_ = $line . $_;
292                                 $line = '';
293                         }
294
295                         if (/\\$/) {
296                                 $line = $_;
297                                 next;
298                         }
299
300                         $cpp = 1 if /^\#.*ifdef.*cplusplus/;
301                         if ($cpp) {
302                                 $cpp = 0 if /^\#.*endif/;
303                                 next;
304                         }
305
306                         s/\/\*.*?\*\///gs;                   # ignore comments
307                         s/{[^{}]*}//gs;                      # ignore {} blocks
308                         if (/^\#\s*ifndef (.*)/) {
309                                 push(@tag,$1);
310                                 $tag{$1}=-1;
311                         } elsif (/^\#\s*if !defined\(([^\)]+)\)/) {
312                                 push(@tag,$1);
313                                 $tag{$1}=-1;
314                         } elsif (/^\#\s*ifdef (.*)/) {
315                                 push(@tag,$1);
316                                 $tag{$1}=1;
317                         } elsif (/^\#\s*if defined\(([^\)]+)\)/) {
318                                 push(@tag,$1);
319                                 $tag{$1}=1;
320                         } elsif (/^\#\s*error\s+(\w+) is disabled\./) {
321                                 if ($tag[$#tag] eq "NO_".$1) {
322                                         $tag{$tag[$#tag]}=2;
323                                 }
324                         } elsif (/^\#\s*endif/) {
325                                 if ($tag{$tag[$#tag]}==2) {
326                                         $tag{$tag[$#tag]}=-1;
327                                 } else {
328                                         $tag{$tag[$#tag]}=0;
329                                 }
330                                 pop(@tag);
331                         } elsif (/^\#\s*else/) {
332                                 my $t=$tag[$#tag];
333                                 $tag{$t}= -$tag{$t};
334                         } elsif (/^\#\s*if\s+1/) {
335                                 # Dummy tag
336                                 push(@tag,"TRUE");
337                                 $tag{"TRUE"}=1;
338                         } elsif (/^\#\s*if\s+0/) {
339                                 # Dummy tag
340                                 push(@tag,"TRUE");
341                                 $tag{"TRUE"}=-1;
342                         } elsif (/^\#\s*define\s+(\w+)\s+(\w+)/
343                                  && $symhacking) {
344                                 my $s = $1;
345                                 my $a =
346                                     $2.":".join(",", grep(!/^$/,
347                                                           map { $tag{$_} == 1 ?
348                                                                     $_ : "" }
349                                                           @known_platforms));
350                                 $rename{$s} = $a;
351                         }
352                         if (/^\#/) {
353                                 my @p = grep(!/^$/,
354                                              map { $tag{$_} == 1 ? $_ :
355                                                        $tag{$_} == -1 ? "!".$_  : "" }
356                                              @known_platforms);
357                                 my @a = grep(!/^$/,
358                                              map { $tag{"NO_".$_} == -1 ? $_ : "" }
359                                              @known_algorithms);
360                                 $def .= "#INFO:".join(',',@p).":".join(',',@a).";";
361                                 next;
362                         }
363                         if (/^\s*DECLARE_STACK_OF\s*\(\s*(\w*)\s*\)/) {
364                                 next;
365                         } elsif (/^\s*DECLARE_PKCS12_STACK_OF\s*\(\s*(\w*)\s*\)/) {
366                                 next;
367                         } elsif (/^\s*DECLARE_ASN1_SET_OF\s*\(\s*(\w*)\s*\)/) {
368                                 next;
369                         } elsif (/^DECLARE_PEM_rw\s*\(\s*(\w*)\s*,/ ||
370                                  /^DECLARE_PEM_rw_cb\s*\(\s*(\w*)\s*,/ ) {
371                                 # Things not in Win16
372                                 $syms{"PEM_read_${1}"} = 1;
373                                 $platform{"PEM_read_${1}"} = "!WIN16";
374                                 $syms{"PEM_write_${1}"} = 1;
375                                 $platform{"PEM_write_${1}"} = "!WIN16";
376                                 # Things that are everywhere
377                                 $syms{"PEM_read_bio_${1}"} = 1;
378                                 $syms{"PEM_write_bio_${1}"} = 1;
379                                 if ($1 eq "RSAPrivateKey" ||
380                                     $1 eq "RSAPublicKey" ||
381                                     $1 eq "RSA_PUBKEY") {
382                                         $algorithm{"PEM_read_${1}"} = "RSA";
383                                         $algorithm{"PEM_write_${1}"} = "RSA";
384                                         $algorithm{"PEM_read_bio_${1}"} = "RSA";
385                                         $algorithm{"PEM_write_bio_${1}"} = "RSA";
386                                 }
387                                 elsif ($1 eq "DSAPrivateKey" ||
388                                        $1 eq "DSAparams" ||
389                                        $1 eq "RSA_PUBKEY") {
390                                         $algorithm{"PEM_read_${1}"} = "DSA";
391                                         $algorithm{"PEM_write_${1}"} = "DSA";
392                                         $algorithm{"PEM_read_bio_${1}"} = "DSA";
393                                         $algorithm{"PEM_write_bio_${1}"} = "DSA";
394                                 }
395                                 elsif ($1 eq "DHparams") {
396                                         $algorithm{"PEM_read_${1}"} = "DH";
397                                         $algorithm{"PEM_write_${1}"} = "DH";
398                                         $algorithm{"PEM_read_bio_${1}"} = "DH";
399                                         $algorithm{"PEM_write_bio_${1}"} = "DH";
400                                 }
401                         } elsif (/^DECLARE_PEM_write\s*\(\s*(\w*)\s*,/ ||
402                                      /^DECLARE_PEM_write_cb\s*\(\s*(\w*)\s*,/ ) {
403                                 # Things not in Win16
404                                 $syms{"PEM_write_${1}"} = 1;
405                                 $platform{"PEM_write_${1}"} .= ",!WIN16";
406                                 # Things that are everywhere
407                                 $syms{"PEM_write_bio_${1}"} = 1;
408                                 if ($1 eq "RSAPrivateKey" ||
409                                     $1 eq "RSAPublicKey" ||
410                                     $1 eq "RSA_PUBKEY") {
411                                         $algorithm{"PEM_write_${1}"} = "RSA";
412                                         $algorithm{"PEM_write_bio_${1}"} = "RSA";
413                                 }
414                                 elsif ($1 eq "DSAPrivateKey" ||
415                                        $1 eq "DSAparams" ||
416                                        $1 eq "RSA_PUBKEY") {
417                                         $algorithm{"PEM_write_${1}"} = "DSA";
418                                         $algorithm{"PEM_write_bio_${1}"} = "DSA";
419                                 }
420                                 elsif ($1 eq "DHparams") {
421                                         $algorithm{"PEM_write_${1}"} = "DH";
422                                         $algorithm{"PEM_write_bio_${1}"} = "DH";
423                                 }
424                         } elsif (/^DECLARE_PEM_read\s*\(\s*(\w*)\s*,/ ||
425                                      /^DECLARE_PEM_read_cb\s*\(\s*(\w*)\s*,/ ) {
426                                 # Things not in Win16
427                                 $syms{"PEM_read_${1}"} = 1;
428                                 $platform{"PEM_read_${1}"} .= ",!WIN16";
429                                 # Things that are everywhere
430                                 $syms{"PEM_read_bio_${1}"} = 1;
431                         } elsif (
432                                 ($tag{'TRUE'} != -1)
433                                 && ($tag{'CONST_STRICT'} != 1)
434                                  )
435                                 {
436                                         if (/\{|\/\*|\([^\)]*$/) {
437                                                 $line = $_;
438                                         } else {
439                                                 $def .= $_;
440                                         }
441                                 }
442                         }
443                 close(IN);
444
445                 my $algs;
446                 my $plays;
447
448                 foreach (split /;/, $def) {
449                         my $s; my $k = "FUNCTION"; my $p; my $a;
450                         s/^[\n\s]*//g;
451                         s/[\n\s]*$//g;
452                         next if(/\#undef/);
453                         next if(/typedef\W/);
454                         next if(/\#define/);
455
456                         if (/^\#INFO:([^:]*):(.*)$/) {
457                                 $plats = $1;
458                                 $algs = $2;
459                                 next;
460                         } elsif (/^\s*OPENSSL_EXTERN\s.*?(\w+)(\[[0-9]*\])*\s*$/) {
461                                 $s = $1;
462                                 $k = "VARIABLE";
463                         } elsif (/\(\*(\w*)\([^\)]+/) {
464                                 $s = $1;
465                         } elsif (/\w+\W+(\w+)\W*\(\s*\)$/s) {
466                                 # K&R C
467                                 next;
468                         } elsif (/\w+\W+\w+\W*\(.*\)$/s) {
469                                 while (not /\(\)$/s) {
470                                         s/[^\(\)]*\)$/\)/s;
471                                         s/\([^\(\)]*\)\)$/\)/s;
472                                 }
473                                 s/\(void\)//;
474                                 /(\w+)\W*\(\)/s;
475                                 $s = $1;
476                         } elsif (/\(/ and not (/=/)) {
477                                 print STDERR "File $file: cannot parse: $_;\n";
478                                 next;
479                         } else {
480                                 next;
481                         }
482
483                         $syms{$s} = 1;
484                         $kind{$s} = $k;
485
486                         $p = $plats;
487                         $a = $algs;
488                         $a .= ",BF" if($s =~ /EVP_bf/);
489                         $a .= ",CAST" if($s =~ /EVP_cast/);
490                         $a .= ",DES" if($s =~ /EVP_des/);
491                         $a .= ",DSA" if($s =~ /EVP_dss/);
492                         $a .= ",IDEA" if($s =~ /EVP_idea/);
493                         $a .= ",MD2" if($s =~ /EVP_md2/);
494                         $a .= ",MD4" if($s =~ /EVP_md4/);
495                         $a .= ",MD5" if($s =~ /EVP_md5/);
496                         $a .= ",RC2" if($s =~ /EVP_rc2/);
497                         $a .= ",RC4" if($s =~ /EVP_rc4/);
498                         $a .= ",RC5" if($s =~ /EVP_rc5/);
499                         $a .= ",RIPEMD" if($s =~ /EVP_ripemd/);
500                         $a .= ",SHA" if($s =~ /EVP_sha/);
501                         $a .= ",RSA" if($s =~ /EVP_(Open|Seal)(Final|Init)/);
502                         $a .= ",RSA" if($s =~ /PEM_Seal(Final|Init|Update)/);
503                         $a .= ",RSA" if($s =~ /RSAPrivateKey/);
504                         $a .= ",RSA" if($s =~ /SSLv23?_((client|server)_)?method/);
505
506                         $platform{$s} .= ','.$p;
507                         $algorithm{$s} .= ','.$a;
508
509                         if (defined($rename{$s})) {
510                                 (my $r, my $p) = split(/:/,$rename{$s});
511                                 my @ip = map { /^!(.*)$/ ? $1 : "!".$_ } split /,/, $p;
512                                 $syms{$r} = 1;
513                                 $kind{$r} = $kind{$s}."(".$s.")";
514                                 $algorithm{$r} = $algorithm{$s};
515                                 $platform{$r} = $platform{$s}.",".$p;
516                                 $platform{$s} .= ','.join(',', @ip).','.join(',', @ip);
517                         }
518                 }
519         }
520
521         # Prune the returned symbols
522
523         $platform{"crypt"} .= ",!PERL5,!__FreeBSD__,!NeXT";
524
525         delete $syms{"SSL_add_dir_cert_subjects_to_stack"};
526         delete $syms{"bn_dump1"};
527
528         $platform{"BIO_s_file_internal"} .= ",WIN16";
529         $platform{"BIO_new_file_internal"} .= ",WIN16";
530         $platform{"BIO_new_fp_internal"} .= ",WIN16";
531
532         $platform{"BIO_s_file"} .= ",!WIN16";
533         $platform{"BIO_new_file"} .= ",!WIN16";
534         $platform{"BIO_new_fp"} .= ",!WIN16";
535
536         $platform{"BIO_s_log"} .= ",!WIN32,!WIN16,!macintosh";
537
538         if(exists $syms{"ERR_load_CRYPTO_strings"}) {
539                 $platform{"ERR_load_CRYPTO_strings"} .= ",!VMS,!WIN16";
540                 $syms{"ERR_load_CRYPTOlib_strings"} = 1;
541                 $platform{"ERR_load_CRYPTOlib_strings"} .= ",VMS,WIN16";
542         }
543
544         # Info we know about
545
546         $platform{"RSA_PKCS1_RSAref"} = "RSAREF";
547         $algorithm{"RSA_PKCS1_RSAref"} = "RSA";
548
549         push @ret, map { $_."\\".&info_string($_,"EXIST",
550                                               $platform{$_},
551                                               $kind{$_},
552                                               $algorithm{$_}) } keys %syms;
553
554         return(@ret);
555 }
556
557 sub info_string {
558         (my $symbol, my $exist, my $platforms, my $kind, my $algorithms) = @_;
559
560         my %a = defined($algorithms) ?
561             map { $_ => 1 } split /,/, $algorithms : ();
562         my $pl = defined($platforms) ? $platforms : "";
563         my %p = map { $_ => 0 } split /,/, $pl;
564         my $k = defined($kind) ? $kind : "FUNCTION";
565         my $ret;
566
567         # We do this, because if there's code like the following, it really
568         # means the function exists in all cases and should therefore be
569         # everywhere.  By increasing and decreasing, we may attain 0:
570         #
571         # ifndef WIN16
572         #    int foo();
573         # else
574         #    int _fat foo();
575         # endif
576         foreach $platform (split /,/, $pl) {
577                 if ($platform =~ /^!(.*)$/) {
578                         $p{$1}--;
579                 } else {
580                         $p{$platform}++;
581                 }
582         }
583         foreach $platform (keys %p) {
584                 if ($p{$platform} == 0) { delete $p{$platform}; }
585         }
586
587         delete $p{""};
588         delete $a{""};
589
590         $ret = $exist;
591         $ret .= ":".join(',',map { $p{$_} < 0 ? "!".$_ : $_ } keys %p);
592         $ret .= ":".$k;
593         $ret .= ":".join(',',keys %a);
594         return $ret;
595 }
596
597 sub maybe_add_info {
598         (my $name, *nums, my @symbols) = @_;
599         my $sym;
600         my $new_info = 0;
601
602         print STDERR "Updating $name info\n";
603         foreach $sym (@symbols) {
604                 (my $s, my $i) = split /\\/, $sym;
605                 $i =~ s/^(.*?:.*?:\w+)(\(\w+\))?/$1/;
606                 if (defined($nums{$s})) {
607                         (my $n, my $dummy) = split /\\/, $nums{$s};
608                         if (!defined($dummy) || $i ne $dummy) {
609                                 $nums{$s} = $n."\\".$i;
610                                 $new_info++;
611                                 #print STDERR "DEBUG: maybe_add_info for $s: \"$dummy\" => \"$i\"\n";
612                         }
613                 }
614         }
615         if ($new_info) {
616                 print STDERR "$new_info old symbols got an info update\n";
617         } else {
618                 print STDERR "No old symbols needed info update\n";
619         }
620 }
621
622 sub print_test_file
623 {
624         (*OUT,my $name,*nums,my @symbols)=@_;
625         my $n = 1; my @e; my @r;
626         my $sym; my $prev = ""; my $prefSSLeay;
627
628         (@e)=grep(/^SSLeay\\.*?:.*?:FUNCTION/,@symbols);
629         (@r)=grep(/^\w+\\.*?:.*?:FUNCTION/ && !/^SSLeay\\.*?:.*?:FUNCTION/,@symbols);
630         @symbols=((sort @e),(sort @r));
631
632         foreach $sym (@symbols) {
633                 (my $s, my $i) = $sym =~ /^(.*?)\\(.*)$/;
634                 if ($s ne $prev) {
635                         if (!defined($nums{$sym})) {
636                                 printf STDERR "Warning: $sym does not have a number assigned\n"
637                                                 if(!$do_update);
638                         } else {
639                                 $n=$nums{$s};
640                                 print OUT "\t$s();\n";
641                         }
642                 }
643                 $prev = $s;     # To avoid duplicates...
644         }
645 }
646
647 sub print_def_file
648 {
649         (*OUT,my $name,*nums,my @symbols)=@_;
650         my $n = 1; my @e; my @r;
651
652         if ($W32)
653                 { $name.="32"; }
654         else
655                 { $name.="16"; }
656
657         print OUT <<"EOF";
658 ;
659 ; Definition file for the DLL version of the $name library from OpenSSL
660 ;
661
662 LIBRARY         $name
663
664 DESCRIPTION     'OpenSSL $name - http://www.openssl.org/'
665
666 EOF
667
668         if (!$W32) {
669                 print <<"EOF";
670 CODE            PRELOAD MOVEABLE
671 DATA            PRELOAD MOVEABLE SINGLE
672
673 EXETYPE         WINDOWS
674
675 HEAPSIZE        4096
676 STACKSIZE       8192
677
678 EOF
679         }
680
681         print "EXPORTS\n";
682
683         (@e)=grep(/^SSLeay\\.*?:.*?:FUNCTION/,@symbols);
684         (@r)=grep(/^\w+\\.*?:.*?:FUNCTION/ && !/^SSLeay\\.*?:.*?:FUNCTION/,@symbols);
685         @symbols=((sort @e),(sort @r));
686
687
688         foreach $sym (@symbols) {
689                 (my $s, my $i) = $sym =~ /^(.*?)\\(.*)$/;
690                 if (!defined($nums{$s})) {
691                         printf STDERR "Warning: $s does not have a number assigned\n"
692                                         if(!$do_update);
693                 } else {
694                         (my $n, my $i) = split /\\/, $nums{$s};
695                         my @p = split(/,/, ($i =~ /^.*?:(.*?):/,$1));
696                         printf OUT "    %s%-40s@%d\n",($W32)?"":"_",$s,$n
697                             # It is very important to check NT before W32
698                             if ($NT && (!@p || (grep(/^WINNT$/,@p)
699                                                 && !grep(/^!WINNT$/,@p)))
700                                 || $W32 && (!@p || (grep(/^WIN32$/,@p)
701                                                     && !grep(/^!WIN32$/,@p)))
702                                 || $W16 && (!@p || (grep(/^WIN16$/,@p)
703                                                     && !grep(/^!WIN16$/,@p))));
704                 }
705         }
706         printf OUT "\n";
707 }
708
709 sub load_numbers
710 {
711         my($name)=@_;
712         my(@a,%ret);
713
714         $max_num = 0;
715         $num_noinfo = 0;
716         $prev = "";
717
718         open(IN,"<$name") || die "unable to open $name:$!\n";
719         while (<IN>) {
720                 chop;
721                 s/#.*$//;
722                 next if /^\s*$/;
723                 @a=split;
724                 if (defined $ret{$a[0]}) {
725                         print STDERR "Warning: Symbol '",$a[0],"' redefined. old=",$ret{$a[0]},", new=",$a[1],"\n";
726                 }
727                 if ($max_num > $a[1]) {
728                         print STDERR "Warning: Number decreased from ",$max_num," to ",$a[1],"\n";
729                 }
730                 if ($max_num == $a[1]) {
731                         # This is actually perfectly OK
732                         #print STDERR "Warning: Symbol ",$a[0]," has same number as previous ",$prev,": ",$a[1],"\n";
733                 }
734                 if ($#a < 2) {
735                         # Existence will be proven later, in do_defs
736                         $ret{$a[0]}=$a[1];
737                         $num_noinfo++;
738                 } else {
739                         $ret{$a[0]}=$a[1]."\\".$a[2]; # \\ is a special marker
740                 }
741                 $max_num = $a[1] if $a[1] > $max_num;
742                 $prev=$a[0];
743         }
744         if ($num_noinfo) {
745                 print STDERR "Warning: $num_noinfo symbols were without info.";
746                 if ($do_rewrite) {
747                         printf STDERR "  The rewrite will fix this.\n";
748                 } else {
749                         printf STDERR "  You should do a rewrite to fix this.\n";
750                 }
751         }
752         close(IN);
753         return(%ret);
754 }
755
756 sub parse_number
757 {
758         (my $str, my $what) = @_;
759         (my $n, my $i) = split(/\\/,$str);
760         if ($what eq "n") {
761                 return $n;
762         } else {
763                 return $i;
764         }
765 }
766
767 sub rewrite_numbers
768 {
769         (*OUT,$name,*nums,@symbols)=@_;
770         my $thing;
771
772         print STDERR "Rewriting $name\n";
773
774         my @r = grep(/^\w+\\.*?:.*?:\w+\(\w+\)/,@symbols);
775         my $r; my %r; my %rsyms;
776         foreach $r (@r) {
777                 (my $s, my $i) = split /\\/, $r;
778                 my $a = $1 if $i =~ /^.*?:.*?:\w+\((\w+)\)/;
779                 $i =~ s/^(.*?:.*?:\w+)\(\w+\)/$1/;
780                 $r{$a} = $s."\\".$i;
781                 $rsyms{$s} = 1;
782         }
783
784         my @s=sort { &parse_number($nums{$a},"n") <=> &parse_number($nums{$b},"n") } keys %nums;
785         foreach $sym (@s) {
786                 (my $n, my $i) = split /\\/, $nums{$sym};
787                 next if defined($i) && $i =~ /^.*?:.*?:\w+\(\w+\)/;
788                 next if defined($rsyms{$sym});
789                 $i="NOEXIST::FUNCTION:" if !defined($i) || $i eq "";
790                 printf OUT "%s%-40s%d\t%s\n","",$sym,$n,$i;
791                 if (exists $r{$sym}) {
792                         (my $s, $i) = split /\\/,$r{$sym};
793                         printf OUT "%s%-40s%d\t%s\n","",$s,$n,$i;
794                 }
795         }
796 }
797
798 sub update_numbers
799 {
800         (*OUT,$name,*nums,my $start_num, my @symbols)=@_;
801         my $new_syms = 0;
802
803         print STDERR "Updating $name numbers\n";
804
805         my @r = grep(/^\w+\\.*?:.*?:\w+\(\w+\)/,@symbols);
806         my $r; my %r; my %rsyms;
807         foreach $r (@r) {
808                 (my $s, my $i) = split /\\/, $r;
809                 my $a = $1 if $i =~ /^.*?:.*?:\w+\((\w+)\)/;
810                 $i =~ s/^(.*?:.*?:\w+)\(\w+\)/$1/;
811                 $r{$a} = $s."\\".$i;
812                 $rsyms{$s} = 1;
813         }
814
815         foreach $sym (@symbols) {
816                 (my $s, my $i) = $sym =~ /^(.*?)\\(.*)$/;
817                 next if $i =~ /^.*?:.*?:\w+\(\w+\)/;
818                 next if defined($rsyms{$sym});
819                 die "ERROR: Symbol $sym had no info attached to it."
820                     if $i eq "";
821                 if (!exists $nums{$s}) {
822                         $new_syms++;
823                         printf OUT "%s%-40s%d\t%s\n","",$s, ++$start_num,$i;
824                         if (exists $r{$s}) {
825                                 ($s, $i) = split /\\/,$r{$sym};
826                                 printf OUT "%s%-40s%d\t%s\n","",$s, $start_num,$i;
827                         }
828                 }
829         }
830         if($new_syms) {
831                 print STDERR "$new_syms New symbols added\n";
832         } else {
833                 print STDERR "No New symbols Added\n";
834         }
835 }
836
837 sub check_existing
838 {
839         (*nums, my @symbols)=@_;
840         my %existing; my @remaining;
841         @remaining=();
842         foreach $sym (@symbols) {
843                 (my $s, my $i) = $sym =~ /^(.*?)\\(.*)$/;
844                 $existing{$s}=1;
845         }
846         foreach $sym (keys %nums) {
847                 if (!exists $existing{$sym}) {
848                         push @remaining, $sym;
849                 }
850         }
851         if(@remaining) {
852                 print STDERR "The following symbols do not seem to exist:\n";
853                 foreach $sym (@remaining) {
854                         print STDERR "\t",$sym,"\n";
855                 }
856         }
857 }
858