RAND_pseudo_bytes is good enough for encryption IVs,
[openssl.git] / util / mkdef.pl
index 5b2f355bb48ccee18d969dc64457f685ac29c614..06b9426169973dac7c9bdf8573e0c8a947a6b5c7 100755 (executable)
-#!/usr/local/bin/perl
+#!/usr/local/bin/perl -w
 #
 # generate a .def file
 #
 # It does this by parsing the header files and looking for the
-# non-prototyped functions.
+# prototyped functions: it then prunes the output.
 #
 
-$crypto_num="util/libeay.num";
-$ssl_num=   "util/ssleay.num";
-
-$NT=1;
-foreach (@ARGV)
+my $crypto_num="util/libeay.num";
+my $ssl_num=   "util/ssleay.num";
+
+my $do_update = 0;
+my $do_crypto = 0;
+my $do_ssl = 0;
+my $do_ctest = 0;
+my $rsaref = 0;
+
+my $W32=1;
+my $NT=0;
+# Set this to make typesafe STACK definitions appear in DEF
+my $safe_stack_def = 1;
+
+my $options="";
+open(IN,"<Makefile.ssl") || die "unable to open Makefile.ssl!\n";
+while(<IN>) {
+    $options=$1 if (/^OPTIONS=(.*)$/);
+}
+close(IN);
+
+# The following ciphers may be excluded (by Configure). This means functions
+# defined with ifndef(NO_XXX) are not included in the .def file, and everything
+# in directory xxx is ignored.
+my $no_rc2; my $no_rc4; my $no_rc5; my $no_idea; my $no_des; my $no_bf;
+my $no_cast; my $no_md2; my $no_md5; my $no_sha; my $no_ripemd; my $no_mdc2;
+my $no_rsa; my $no_dsa; my $no_dh; my $no_hmac=0;
+
+foreach (@ARGV, split(/ /, $options))
        {
-       $NT=1 if $_ eq "32";
-       $NT=0 if $_ eq "16";
+       $W32=1 if $_ eq "32";
+       $W32=0 if $_ eq "16";
+       if($_ eq "NT") {
+               $W32 = 1;
+               $NT = 1;
+       }
        $do_ssl=1 if $_ eq "ssleay";
+       $do_ssl=1 if $_ eq "ssl";
        $do_crypto=1 if $_ eq "libeay";
+       $do_crypto=1 if $_ eq "crypto";
+       $do_update=1 if $_ eq "update";
+       $do_ctest=1 if $_ eq "ctest";
+       $rsaref=1 if $_ eq "rsaref";
+
+       if    (/^no-rc2$/)      { $no_rc2=1; }
+       elsif (/^no-rc4$/)      { $no_rc4=1; }
+       elsif (/^no-rc5$/)      { $no_rc5=1; }
+       elsif (/^no-idea$/)     { $no_idea=1; }
+       elsif (/^no-des$/)      { $no_des=1; }
+       elsif (/^no-bf$/)       { $no_bf=1; }
+       elsif (/^no-cast$/)     { $no_cast=1; }
+       elsif (/^no-md2$/)      { $no_md2=1; }
+       elsif (/^no-md5$/)      { $no_md5=1; }
+       elsif (/^no-sha$/)      { $no_sha=1; }
+       elsif (/^no-ripemd$/)   { $no_ripemd=1; }
+       elsif (/^no-mdc2$/)     { $no_mdc2=1; }
+       elsif (/^no-rsa$/)      { $no_rsa=1; }
+       elsif (/^no-dsa$/)      { $no_dsa=1; }
+       elsif (/^no-dh$/)       { $no_dh=1; }
+       elsif (/^no-hmac$/)     { $no_hmac=1; }
        }
 
+
 if (!$do_ssl && !$do_crypto)
        {
-       print STDERR "usage: $0 ( ssl | crypto ) [ 16 | 32 ]\n";
+       print STDERR "usage: $0 ( ssl | crypto ) [ 16 | 32 | NT ] [rsaref]\n";
        exit(1);
        }
 
 %ssl_list=&load_numbers($ssl_num);
+$max_ssl = $max_num;
 %crypto_list=&load_numbers($crypto_num);
-
-$ssl="ssl/ssl.h";
-
-$crypto ="crypto/crypto.h";
-$crypto.=" crypto/des/des.h";
-$crypto.=" crypto/idea/idea.h";
-$crypto.=" crypto/rc4/rc4.h";
-$crypto.=" crypto/rc5/rc5.h";
-$crypto.=" crypto/rc2/rc2.h";
-$crypto.=" crypto/bf/blowfish.h";
-$crypto.=" crypto/cast/cast.h";
-$crypto.=" crypto/md2/md2.h";
-$crypto.=" crypto/md5/md5.h";
-$crypto.=" crypto/mdc2/mdc2.h";
-$crypto.=" crypto/sha/sha.h";
-$crypto.=" crypto/ripemd/ripemd.h";
+$max_crypto = $max_num;
+
+my $ssl="ssl/ssl.h";
+
+my $crypto ="crypto/crypto.h";
+$crypto.=" crypto/des/des.h" unless $no_des;
+$crypto.=" crypto/idea/idea.h" unless $no_idea;
+$crypto.=" crypto/rc4/rc4.h" unless $no_rc4;
+$crypto.=" crypto/rc5/rc5.h" unless $no_rc5;
+$crypto.=" crypto/rc2/rc2.h" unless $no_rc2;
+$crypto.=" crypto/bf/blowfish.h" unless $no_bf;
+$crypto.=" crypto/cast/cast.h" unless $no_cast;
+$crypto.=" crypto/md2/md2.h" unless $no_md2;
+$crypto.=" crypto/md5/md5.h" unless $no_md5;
+$crypto.=" crypto/mdc2/mdc2.h" unless $no_mdc2;
+$crypto.=" crypto/sha/sha.h" unless $no_sha;
+$crypto.=" crypto/ripemd/ripemd.h" unless $no_ripemd;
 
 $crypto.=" crypto/bn/bn.h";
-$crypto.=" crypto/rsa/rsa.h";
-$crypto.=" crypto/dsa/dsa.h";
-$crypto.=" crypto/dh/dh.h";
+$crypto.=" crypto/rsa/rsa.h" unless $no_rsa;
+$crypto.=" crypto/dsa/dsa.h" unless $no_dsa;
+$crypto.=" crypto/dh/dh.h" unless $no_dh;
+$crypto.=" crypto/hmac/hmac.h" unless $no_hmac;
 
 $crypto.=" crypto/stack/stack.h";
 $crypto.=" crypto/buffer/buffer.h";
@@ -63,168 +117,318 @@ $crypto.=" crypto/asn1/asn1.h";
 $crypto.=" crypto/asn1/asn1_mac.h";
 $crypto.=" crypto/err/err.h";
 $crypto.=" crypto/pkcs7/pkcs7.h";
+$crypto.=" crypto/pkcs12/pkcs12.h";
 $crypto.=" crypto/x509/x509.h";
 $crypto.=" crypto/x509/x509_vfy.h";
 $crypto.=" crypto/x509v3/x509v3.h";
 $crypto.=" crypto/rand/rand.h";
-$crypto.=" crypto/hmac/hmac.h";
 $crypto.=" crypto/comp/comp.h";
 $crypto.=" crypto/tmdiff.h";
 
-$match{'NOPROTO'}=1;
-$match2{'PERL5'}=1;
+my @ssl_func = &do_defs("SSLEAY", $ssl);
+my @crypto_func = &do_defs("LIBEAY", $crypto);
+
+
+if ($do_update) {
+
+if ($do_ssl == 1) {
+       open(OUT, ">>$ssl_num");
+       &update_numbers(*OUT,"SSLEAY",*ssl_list,$max_ssl, @ssl_func);
+       close OUT;
+}
+
+if($do_crypto == 1) {
+       open(OUT, ">>$crypto_num");
+       &update_numbers(*OUT,"LIBEAY",*crypto_list,$max_crypto, @crypto_func);
+       close OUT;
+} 
+
+} elsif ($do_ctest) {
+
+       print <<"EOF";
 
-&print_def_file(*STDOUT,"SSLEAY",*ssl_list,&do_defs("SSLEAY",$ssl))
-       if $do_ssl == 1;
+/* Test file to check all DEF file symbols are present by trying
+ * to link to all of them. This is *not* intended to be run!
+ */
+
+int main()
+{
+EOF
+       &print_test_file(*STDOUT,"SSLEAY",*ssl_list,@ssl_func)
+               if $do_ssl == 1;
+
+       &print_test_file(*STDOUT,"LIBEAY",*crypto_list,@crypto_func)
+               if $do_crypto == 1;
+
+       print "}\n";
+
+} else {
+
+       &print_def_file(*STDOUT,"SSLEAY",*ssl_list,@ssl_func)
+               if $do_ssl == 1;
+
+       &print_def_file(*STDOUT,"LIBEAY",*crypto_list,@crypto_func)
+               if $do_crypto == 1;
+
+}
 
-&print_def_file(*STDOUT,"LIBEAY",*crypto_list,&do_defs("LIBEAY",$crypto))
-       if $do_crypto == 1;
 
 sub do_defs
-       {
-       local($name,$files)=@_;
-       local(@ret);
+{
+       my($name,$files)=@_;
+       my $file;
+       my @ret;
+       my %funcs;
+       my $cpp;
 
-       $off=-1;
        foreach $file (split(/\s+/,$files))
                {
-#              print STDERR "reading $file\n";
                open(IN,"<$file") || die "unable to open $file:$!\n";
-               $depth=0;
-               $pr=-1;
-               @np="";
-               $/=undef;
-               $a=<IN>;
-               while (($i=index($a,"/*")) >= 0)
-                       {
-                       $j=index($a,"*/");
-                       break unless ($j >= 0);
-                       $a=substr($a,0,$i).substr($a,$j+2);
-               #       print "$i $j\n";
+               my $line = "", my $def= "";
+               my %tag = (
+                       FreeBSD         => 0,
+                       NOPROTO         => 0,
+                       WIN16           => 0,
+                       PERL5           => 0,
+                       _WINDLL         => 0,
+                       NO_FP_API       => 0,
+                       CONST_STRICT    => 0,
+                       TRUE            => 1,
+                       NO_RC2          => 0,
+                       NO_RC4          => 0,
+                       NO_RC5          => 0,
+                       NO_IDEA         => 0,
+                       NO_DES          => 0,
+                       NO_BF           => 0,
+                       NO_CAST         => 0,
+                       NO_MD2          => 0,
+                       NO_MD5          => 0,
+                       NO_SHA          => 0,
+                       NO_RIPEMD       => 0,
+                       NO_MDC2         => 0,
+                       NO_RSA          => 0,
+                       NO_DSA          => 0,
+                       NO_DH           => 0,
+                       NO_HMAC         => 0,
+               );
+               while(<IN>) {
+                       last if (/BEGIN ERROR CODES/);
+                       if ($line ne '') {
+                               $_ = $line . $_;
+                               $line = '';
                        }
-               foreach (split("\n",$a))
-                       {
-                       if (/^\#\s*ifndef (.*)/)
-                               {
+
+                       if (/\\$/) {
+                               $line = $_;
+                               next;
+                       }
+
+                       $cpp = 1 if /^#.*ifdef.*cplusplus/;
+                       if ($cpp) {
+                               $cpp = 0 if /^#.*endif/;
+                               next;
+                       }
+
+                       s/\/\*.*?\*\///gs;                   # ignore comments
+                       s/{[^{}]*}//gs;                      # ignore {} blocks
+                       if (/^\#\s*ifndef (.*)/) {
                                push(@tag,$1);
                                $tag{$1}=-1;
                                next;
-                               }
-                       elsif (/^\#\s*if !defined\(([^\)]+)\)/)
-                               {
+                       } elsif (/^\#\s*if !defined\(([^\)]+)\)/) {
                                push(@tag,$1);
                                $tag{$1}=-1;
                                next;
-                               }
-                       elsif (/^\#\s*ifdef (.*)/)
-                               {
+                       } elsif (/^\#\s*ifdef (.*)/) {
                                push(@tag,$1);
                                $tag{$1}=1;
                                next;
-                               }
-                       elsif (/^\#\s*if defined(.*)/)
-                               {
+                       } elsif (/^\#\s*if defined(.*)/) {
                                push(@tag,$1);
                                $tag{$1}=1;
                                next;
-                               }
-                       elsif (/^\#\s*endif/)
-                               {
+                       } elsif (/^\#\s*endif/) {
                                $tag{$tag[$#tag]}=0;
                                pop(@tag);
                                next;
-                               }
-                       elsif (/^\#\s*else/)
-                               {
-                               $t=$tag[$#tag];
+                       } elsif (/^\#\s*else/) {
+                               my $t=$tag[$#tag];
                                $tag{$t}= -$tag{$t};
                                next;
+                       } elsif (/^\#\s*if\s+1/) {
+                               # Dummy tag
+                               push(@tag,"TRUE");
+                               $tag{"TRUE"}=1;
+                               next;
+                       } elsif (/^\#\s*if\s+0/) {
+                               # Dummy tag
+                               push(@tag,"TRUE");
+                               $tag{"TRUE"}=-1;
+                               next;
+                       } elsif (/^\#/) {
+                               next;
+                       }
+                       if ($safe_stack_def &&
+                               /^\s*DECLARE_STACK_OF\s*\(\s*(\w*)\s*\)/) {
+                               $funcs{"sk_${1}_new"} = 1;
+                               $funcs{"sk_${1}_new_null"} = 1;
+                               $funcs{"sk_${1}_free"} = 1;
+                               $funcs{"sk_${1}_num"} = 1;
+                               $funcs{"sk_${1}_value"} = 1;
+                               $funcs{"sk_${1}_set"} = 1;
+                               $funcs{"sk_${1}_zero"} = 1;
+                               $funcs{"sk_${1}_push"} = 1;
+                               $funcs{"sk_${1}_unshift"} = 1;
+                               $funcs{"sk_${1}_find"} = 1;
+                               $funcs{"sk_${1}_delete"} = 1;
+                               $funcs{"sk_${1}_delete_ptr"} = 1;
+                               $funcs{"sk_${1}_insert"} = 1;
+                               $funcs{"sk_${1}_set_cmp_func"} = 1;
+                               $funcs{"sk_${1}_dup"} = 1;
+                               $funcs{"sk_${1}_pop_free"} = 1;
+                               $funcs{"sk_${1}_shift"} = 1;
+                               $funcs{"sk_${1}_pop"} = 1;
+                               $funcs{"sk_${1}_sort"} = 1;
+                       } elsif ($safe_stack_def &&
+                               /^\s*DECLARE_ASN1_SET_OF\s*\(\s*(\w*)\s*\)/) {
+                               $funcs{"d2i_ASN1_SET_OF_${1}"} = 1;
+                               $funcs{"i2d_ASN1_SET_OF_${1}"} = 1;
+                       } elsif (/^DECLARE_PEM_rw\s*\(\s*(\w*)\s*,/ ||
+                                    /^DECLARE_PEM_rw_cb\s*\(\s*(\w*)\s*,/ ) {
+                               if($W32) {
+                                       $funcs{"PEM_read_${1}"} = 1;
+                                       $funcs{"PEM_write_${1}"} = 1;
                                }
-#printf STDERR "$_\n%2d %2d %2d %2d %2d $NT\n",
-#$tag{'NOPROTO'},$tag{'FreeBSD'},$tag{'WIN16'},$tag{'PERL5'},$tag{'NO_FP_API'};
-
-                       $t=undef;
-                       if (/^extern .*;$/)
-                               { $t=&do_extern($name,$_); }
-                       elsif ( ($tag{'NOPROTO'} == 1) &&
+                               $funcs{"PEM_read_bio_${1}"} = 1;
+                               $funcs{"PEM_write_bio_${1}"} = 1;
+                       } elsif (
+                               ($tag{'TRUE'} != -1) &&
                                ($tag{'FreeBSD'} != 1) &&
-                               (($NT && ($tag{'WIN16'} != 1)) ||
-                                (!$NT && ($tag{'WIN16'} != -1))) &&
+                               ($tag{'CONST_STRICT'} != 1) &&
+                               (($W32 && ($tag{'WIN16'} != 1)) ||
+                                (!$W32 && ($tag{'WIN16'} != -1))) &&
                                ($tag{'PERL5'} != 1) &&
 #                              ($tag{'_WINDLL'} != -1) &&
-                               ((!$NT && $tag{'_WINDLL'} != -1) ||
-                                ($NT && $tag{'_WINDLL'} != 1)) &&
-                               ((($tag{'NO_FP_API'} != 1) && $NT) ||
-                                (($tag{'NO_FP_API'} != -1) && !$NT)))
-                               { $t=&do_line($name,$_); }
-                       else
-                               { $t=undef; }
-                       if (($t ne undef) && (!$done{$name,$t}))
+                               ((!$W32 && $tag{'_WINDLL'} != -1) ||
+                                ($W32 && $tag{'_WINDLL'} != 1)) &&
+                               ((($tag{'NO_FP_API'} != 1) && $W32) ||
+                                (($tag{'NO_FP_API'} != -1) && !$W32)) &&
+                               ($tag{'NO_RC2'} == 0  || !$no_rc2) &&
+                               ($tag{'NO_RC4'} == 0  || !$no_rc4) &&
+                               ($tag{'NO_RC5'} == 0  || !$no_rc5) &&
+                               ($tag{'NO_IDEA'} == 0 || !$no_idea) &&
+                               ($tag{'NO_DES'} == 0  || !$no_des) &&
+                               ($tag{'NO_BF'} == 0   || !$no_bf) &&
+                               ($tag{'NO_CAST'} == 0 || !$no_cast) &&
+                               ($tag{'NO_MD2'} == 0  || !$no_md2) &&
+                               ($tag{'NO_MD5'} == 0  || !$no_md5) &&
+                               ($tag{'NO_SHA'} == 0  || !$no_sha) &&
+                               ($tag{'NO_RIPEMD'} == 0 || !$no_ripemd) &&
+                               ($tag{'NO_MDC2'} == 0 || !$no_mdc2) &&
+                               ($tag{'NO_RSA'} == 0  || !$no_rsa) &&
+                               ($tag{'NO_DSA'} == 0  || !$no_dsa) &&
+                               ($tag{'NO_DH'} == 0   || !$no_dh) &&
+                               ($tag{'NO_HMAC'} == 0 || !$no_hmac))
                                {
-                               $done{$name,$t}++;
-                               push(@ret,$t);
-#printf STDERR "one:$t\n" if $t =~ /BIO_/;
+                                       if (/{|\/\*/) { # }
+                                               $line = $_;
+                                       } else {
+                                               $def .= $_;
+                                       }
                                }
                        }
                close(IN);
+
+               foreach (split /;/, $def) {
+                       s/^[\n\s]*//g;
+                       s/[\n\s]*$//g;
+                       next if(/typedef\W/);
+                       next if(/EVP_bf/ and $no_bf);
+                       next if(/EVP_cast/ and $no_cast);
+                       next if(/EVP_des/ and $no_des);
+                       next if(/EVP_dss/ and $no_dsa);
+                       next if(/EVP_idea/ and $no_idea);
+                       next if(/EVP_md2/ and $no_md2);
+                       next if(/EVP_md5/ and $no_md5);
+                       next if(/EVP_rc2/ and $no_rc2);
+                       next if(/EVP_rc4/ and $no_rc4);
+                       next if(/EVP_rc5/ and $no_rc5);
+                       next if(/EVP_ripemd/ and $no_ripemd);
+                       next if(/EVP_sha/ and $no_sha);
+                       if (/\(\*(\w*)\([^\)]+/) {
+                               $funcs{$1} = 1;
+                       } elsif (/\w+\W+(\w+)\W*\(\s*\)$/s) {
+                               # K&R C
+                               next;
+                       } elsif (/\w+\W+\w+\W*\(.*\)$/s) {
+                               while (not /\(\)$/s) {
+                                       s/[^\(\)]*\)$/\)/s;
+                                       s/\([^\(\)]*\)\)$/\)/s;
+                               }
+                               s/\(void\)//;
+                               /(\w+)\W*\(\)/s;
+                               $funcs{$1} = 1;
+                       } elsif (/\(/ and not (/=/)) {
+                               print STDERR "File $file: cannot parse: $_;\n";
+                       }
                }
-       return(@ret);
        }
 
-sub do_line
-       {
-       local($file,$_)=@_;
-       local($n);
-
-       return(undef) if /^$/;
-       return(undef) if /^\s/;
-#printf STDERR "two:$_\n" if $_ =~ /BIO_/;
-       if (/(CRYPTO_get_locking_callback)/)
-               { return($1); }
-       elsif (/(CRYPTO_get_id_callback)/)
-               { return($1); }
-       elsif (/(CRYPTO_get_add_lock_callback)/)
-               { return($1); }
-       elsif (/(SSL_CTX_get_verify_callback)/)
-               { return($1); }
-       elsif (/(SSL_get_info_callback)/)
-               { return($1); }
-       elsif ((!$NT) && /(ERR_load_CRYPTO_strings)/)
-               { return("ERR_load_CRYPTOlib_strings"); }
-       elsif (!$NT && /BIO_s_file/)
-               { return(undef); }
-       elsif (!$NT && /BIO_new_file/)
-               { return(undef); }
-       elsif (!$NT && /BIO_new_fp/)
-               { return(undef); }
-       elsif ($NT && /BIO_s_file_internal/)
-               { return(undef); }
-       elsif ($NT && /BIO_new_file_internal/)
-               { return(undef); }
-       elsif ($NT && /BIO_new_fp_internal/)
-               { return(undef); }
-       else
-               {
-               /\s\**(\S+)\s*\(/;
-               return($1);
+       # Prune the returned functions
+
+        delete $funcs{"SSL_add_dir_cert_subjects_to_stack"};
+        delete $funcs{"des_crypt"};
+        delete $funcs{"RSA_PKCS1_RSAref"} unless $rsaref;
+
+       if($W32) {
+               delete $funcs{"BIO_s_file_internal"};
+               delete $funcs{"BIO_new_file_internal"};
+               delete $funcs{"BIO_new_fp_internal"};
+       } else {
+               if(exists $funcs{"ERR_load_CRYPTO_strings"}) {
+                       delete $funcs{"ERR_load_CRYPTO_strings"};
+                       $funcs{"ERR_load_CRYPTOlib_strings"} = 1;
                }
+               delete $funcs{"BIO_s_file"};
+               delete $funcs{"BIO_new_file"};
+               delete $funcs{"BIO_new_fp"};
+       }
+       if (!$NT) {
+               delete $funcs{"BIO_s_log"};
        }
 
-sub do_extern
-       {
-       local($file,$_)=@_;
-       local($n);
+       push @ret, keys %funcs;
+
+       return(@ret);
+}
+
+sub print_test_file
+{
+       (*OUT,my $name,*nums,my @functions)=@_;
+       my $n = 1; my @e; my @r;
+       my $func;
 
-       /\s\**(\S+);$/;
-       return($1);
+       (@e)=grep(/^SSLeay/,@functions);
+       (@r)=grep(!/^SSLeay/,@functions);
+       @functions=((sort @e),(sort @r));
+
+       foreach $func (@functions) {
+               if (!defined($nums{$func})) {
+                       printf STDERR "$func does not have a number assigned\n"
+                                       if(!$do_update);
+               } else {
+                       $n=$nums{$func};
+                       print OUT "\t$func();\n";
+               }
        }
+}
 
 sub print_def_file
-       {
-       local(*OUT,$name,*nums,@functions)=@_;
-       local($n)=1;
+{
+       (*OUT,my $name,*nums,my @functions)=@_;
+       my $n = 1; my @e; my @r;
 
-       if ($NT)
+       if ($W32)
                { $name.="32"; }
        else
                { $name.="16"; }
@@ -240,8 +444,7 @@ DESCRIPTION     'OpenSSL $name - http://www.openssl.org/'
 
 EOF
 
-       if (!$NT)
-               {
+       if (!$W32) {
                print <<"EOF";
 CODE            PRELOAD MOVEABLE
 DATA            PRELOAD MOVEABLE SINGLE
@@ -252,7 +455,7 @@ HEAPSIZE    4096
 STACKSIZE      8192
 
 EOF
-               }
+       }
 
        print "EXPORTS\n";
 
@@ -261,35 +464,52 @@ EOF
        (@r)=grep(!/^SSLeay/,@functions);
        @functions=((sort @e),(sort @r));
 
-       foreach $func (@functions)
-               {
-               if (!defined($nums{$func}))
-                       {
-                       printf STDERR "$func does not have a number assigned\n";
-                       }
-               else
-                       {
+       foreach $func (@functions) {
+               if (!defined($nums{$func})) {
+                       printf STDERR "$func does not have a number assigned\n"
+                                       if(!$do_update);
+               } else {
                        $n=$nums{$func};
-                       printf OUT "    %s%-40s@%d\n",($NT)?"":"_",$func,$n;
-                       }
+                       printf OUT "    %s%-40s@%d\n",($W32)?"":"_",$func,$n;
                }
-       printf OUT "\n";
        }
+       printf OUT "\n";
+}
 
 sub load_numbers
-       {
-       local($name)=@_;
-       local($j,@a,%ret);
+{
+       my($name)=@_;
+       my(@a,%ret);
+
+       $max_num = 0;
 
        open(IN,"<$name") || die "unable to open $name:$!\n";
-       while (<IN>)
-               {
+       while (<IN>) {
                chop;
                s/#.*$//;
                next if /^\s*$/;
                @a=split;
                $ret{$a[0]}=$a[1];
-               }
+               $max_num = $a[1] if $a[1] > $max_num;
+       }
        close(IN);
        return(%ret);
+}
+
+sub update_numbers
+{
+       (*OUT,$name,*nums,my $start_num, my @functions)=@_;
+       my $new_funcs = 0;
+       print STDERR "Updating $name\n";
+       foreach $func (@functions) {
+               if (!exists $nums{$func}) {
+                       $new_funcs++;
+                       printf OUT "%s%-40s%d\n","",$func, ++$start_num;
+               }
+       }
+       if($new_funcs) {
+               print STDERR "$new_funcs New Functions added\n";
+       } else {
+               print STDERR "No New Functions Added\n";
        }
+}