Don't export util/mktar.sh
[openssl.git] / util / mkerr.pl
old mode 100644 (file)
new mode 100755 (executable)
index 96c705e..0ea0296
-#!/usr/local/bin/perl -w
-
-my $config = "crypto/err/openssl.ec";
-my $hprefix = "openssl/";
-my $debug = 0;
-my $rebuild = 0;
-my $static = 1;
-my $recurse = 0;
-my $reindex = 0;
-my $dowrite = 0;
-my $staticloader = "";
-
-my $pack_errcode;
-my $load_errcode;
-
-my $errcount;
-my $year = (localtime)[5] + 1900;
-
-while (@ARGV) {
-       my $arg = $ARGV[0];
-       if($arg eq "-conf") {
-               shift @ARGV;
-               $config = shift @ARGV;
-       } elsif($arg eq "-hprefix") {
-               shift @ARGV;
-               $hprefix = shift @ARGV;
-       } elsif($arg eq "-debug") {
-               $debug = 1;
-               shift @ARGV;
-       } elsif($arg eq "-rebuild") {
-               $rebuild = 1;
-               shift @ARGV;
-       } elsif($arg eq "-recurse") {
-               $recurse = 1;
-               shift @ARGV;
-       } elsif($arg eq "-reindex") {
-               $reindex = 1;
-               shift @ARGV;
-       } elsif($arg eq "-nostatic") {
-               $static = 0;
-               shift @ARGV;
-       } elsif($arg eq "-staticloader") {
-               $staticloader = "static ";
-               shift @ARGV;
-       } elsif($arg eq "-write") {
-               $dowrite = 1;
-               shift @ARGV;
-       } elsif($arg eq "-help" || $arg eq "-h" || $arg eq "-?" || $arg eq "--help") {
-               print STDERR <<"EOF";
-mkerr.pl [options] ...
+#! /usr/bin/env perl
+# Copyright 1999-2018 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+use strict;
+use warnings;
+
+use lib ".";
+use configdata;
+
+my $config       = "crypto/err/openssl.ec";
+my $debug        = 0;
+my $internal     = 0;
+my $nowrite      = 0;
+my $rebuild      = 0;
+my $reindex      = 0;
+my $static       = 0;
+my $unref        = 0;
+my %modules         = ();
+
+my $errors       = 0;
+my @t            = localtime();
+my $YEAR         = $t[5] + 1900;
+
+sub phase
+{
+    my $text = uc(shift);
+    print STDERR "\n---\n$text\n" if $debug;
+}
+
+sub help
+{
+    print STDERR <<"EOF";
+mkerr.pl [options] [files...]
 
 Options:
 
-  -conf F       Use the config file F instead of the default one:
-                  crypto/err/openssl.ec
-
-  -hprefix P    Prepend the filenames in generated #include <header>
-                statements with prefix P. Default: 'openssl/' (without
-                the quotes, naturally)
-
-  -debug        Turn on debugging verbose output on stderr.
-
-  -rebuild      Rebuild all header and C source files, irrespective of the
-                fact if any error or function codes have been added/removed.
-                Default: only update files for libraries which saw change
-                         (of course, this requires '-write' as well, or no
-                          files will be touched!)
-
-  -recurse      scan a preconfigured set of directories / files for error and
-                function codes:
-                  (<crypto/*.c>, <crypto/*/*.c>, <ssl/*.c>, <apps/*.c>)
-                When this option is NOT specified, the filelist is taken from
-                the commandline instead. Here, wildcards may be embedded. (Be
-                sure to escape those to prevent the shell from expanding them
-                for you when you wish mkerr.pl to do so instead.)
-                Default: take file list to scan from the command line.
-
-  -reindex      Discard the numeric values previously assigned to the error
-                and function codes as extracted from the scanned header files;
-                instead renumber all of them starting from 100. (Note that
-                the numbers assigned through 'R' records in the config file
-                remain intact.)
-                Default: keep previously assigned numbers. (You are warned
-                         when collisions are detected.)
-
-  -nostatic     Generates a different source code, where these additional 
-                functions are generated for each library specified in the
-                config file:
-                  void ERR_load_<LIB>_strings(void);
-                  void ERR_unload_<LIB>_strings(void);
-                  void ERR_<LIB>_error(int f, int r, char *fn, int ln);
-                  #define <LIB>err(f,r) ERR_<LIB>_error(f,r,__FILE__,__LINE__)
-                while the code facilitates the use of these in an environment
-                where the error support routines are dynamically loaded at 
-                runtime.
-                Default: 'static' code generation.
-
-  -staticloader Prefix generated functions with the 'static' scope modifier.
-                Default: don't write any scope modifier prefix.
-
-  -write        Actually (over)write the generated code to the header and C 
-                source files as assigned to each library through the config 
-                file.
-                Default: don't write.
-
-  -help / -h / -? / --help            Show this help text.
-
-  ...           Additional arguments are added to the file list to scan,
-                assuming '-recurse' was NOT specified on the command line.
+    -conf FILE  Use the named config file FILE instead of the default.
+
+    -debug      Verbose output debugging on stderr.
+
+    -internal   Generate code that is to be built as part of OpenSSL itself.
+                Also scans internal list of files.
+
+    -module M   Only useful with -internal!
+                Only write files for library module M.  Whether files are
+                actually written or not depends on other options, such as
+                -rebuild.
+                Note: this option is cumulative.  If not given at all, all
+                internal modules will be considered.
+
+    -nowrite    Do not write the header/source files, even if changed.
+
+    -rebuild    Rebuild all header and C source files, even if there
+                were no changes.
+
+    -reindex    Ignore previously assigned values (except for R records in
+                the config file) and renumber everything starting at 100.
+
+    -static     Make the load/unload functions static.
+
+    -unref      List all unreferenced function and reason codes on stderr;
+                implies -nowrite.
+
+    -help       Show this help text.
+
+    ...         Additional arguments are added to the file list to scan,
+                if '-internal' was NOT specified on the command line.
 
 EOF
-               exit 1;
-       } else {
-               last;
-       }
 }
 
-if($recurse) {
-       @source = ( <crypto/*.c>, <crypto/*/*.c>, <ssl/*.c> )
-} else {
-       @source = @ARGV;
+while ( @ARGV ) {
+    my $arg = $ARGV[0];
+    last unless $arg =~ /-.*/;
+    $arg = $1 if $arg =~ /-(-.*)/;
+    if ( $arg eq "-conf" ) {
+        $config = $ARGV[1];
+        shift @ARGV;
+    } elsif ( $arg eq "-debug" ) {
+        $debug = 1;
+        $unref = 1;
+    } elsif ( $arg eq "-internal" ) {
+        $internal = 1;
+    } elsif ( $arg eq "-nowrite" ) {
+        $nowrite = 1;
+    } elsif ( $arg eq "-rebuild" ) {
+        $rebuild = 1;
+    } elsif ( $arg eq "-reindex" ) {
+        $reindex = 1;
+    } elsif ( $arg eq "-static" ) {
+        $static = 1;
+    } elsif ( $arg eq "-unref" ) {
+        $unref = 1;
+        $nowrite = 1;
+    } elsif ( $arg eq "-module" ) {
+        shift @ARGV;
+        $modules{uc $ARGV[0]} = 1;
+    } elsif ( $arg =~ /-*h(elp)?/ ) {
+        &help();
+        exit;
+    } elsif ( $arg =~ /-.*/ ) {
+        die "Unknown option $arg; use -h for help.\n";
+    }
+    shift @ARGV;
 }
 
-# Read in the config file
-
-open(IN, "<$config") || die "Can't open config file $config";
+my @source;
+if ( $internal ) {
+    die "Cannot mix -internal and -static\n" if $static;
+    die "Extra parameters given.\n" if @ARGV;
+    @source = ( glob('crypto/*.c'), glob('crypto/*/*.c'),
+                glob('ssl/*.c'), glob('ssl/*/*.c') );
+} else {
+    die "-module isn't useful without -internal\n" if scalar keys %modules > 0;
+    @source = @ARGV;
+}
 
-# Parse config file
+# Data parsed out of the config and state files.
+my %hinc;       # lib -> header
+my %libinc;     # header -> lib
+my %cskip;      # error_file -> lib
+my %errorfile;  # lib -> error file name
+my %fmax;       # lib -> max assigned function code
+my %rmax;       # lib -> max assigned reason code
+my %fassigned;  # lib -> colon-separated list of assigned function codes
+my %rassigned;  # lib -> colon-separated list of assigned reason codes
+my %fnew;       # lib -> count of new function codes
+my %rnew;       # lib -> count of new reason codes
+my %rextra;     # "extra" reason code -> lib
+my %rcodes;     # reason-name -> value
+my %ftrans;     # old name -> #define-friendly name (all caps)
+my %fcodes;     # function-name -> value
+my $statefile;  # state file with assigned reason and function codes
+my %strings;    # define -> text
+
+# Read and parse the config file
+open(IN, "$config") || die "Can't open config file $config, $!,";
+while ( <IN> ) {
+    next if /^#/ || /^$/;
+    if ( /^L\s+(\S+)\s+(\S+)\s+(\S+)/ ) {
+        my $lib = $1;
+        my $hdr = $2;
+        my $err = $3;
+        $hinc{$lib}   = $hdr;
+        $libinc{$hdr} = $lib;
+        $cskip{$err}  = $lib;
+        next if $err eq 'NONE';
+        $errorfile{$lib} = $err;
+        $fmax{$lib}      = 100;
+        $rmax{$lib}      = 100;
+        $fassigned{$lib} = ":";
+        $rassigned{$lib} = ":";
+        $fnew{$lib}      = 0;
+        $rnew{$lib}      = 0;
+    } elsif ( /^R\s+(\S+)\s+(\S+)/ ) {
+        $rextra{$1} = $2;
+        $rcodes{$1} = $2;
+    } elsif ( /^S\s+(\S+)/ ) {
+        $statefile = $1;
+    } else {
+        die "Illegal config line $_\n";
+    }
+}
+close IN;
 
-while(<IN>)
-{
-       if(/^L\s+(\S+)\s+(\S+)\s+(\S+)/) {
-               $hinc{$1} = $2;
-               $libinc{$2} = $1;
-               $cskip{$3} = $1;
-               if($3 ne "NONE") {
-                       $csrc{$1} = $3;
-                       $fmax{$1} = 100;
-                       $rmax{$1} = 100;
-                       $fassigned{$1} = ":";
-                       $rassigned{$1} = ":";
-                       $fnew{$1} = 0;
-                       $rnew{$1} = 0;
-               }
-       } elsif (/^F\s+(\S+)/) {
-       # Add extra function with $1
-       } elsif (/^R\s+(\S+)\s+(\S+)/) {
-               $rextra{$1} = $2;
-               $rcodes{$1} = $2;
-       }
+if ( ! $statefile ) {
+    $statefile = $config;
+    $statefile =~ s/.ec/.txt/;
 }
 
-close IN;
+# The statefile has all the previous assignments.
+&phase("Reading state");
+my $skippedstate = 0;
+if ( ! $reindex && $statefile ) {
+    open(STATE, "<$statefile") || die "Can't open $statefile, $!";
+
+    # Scan function and reason codes and store them: keep a note of the
+    # maximum code used.
+    while ( <STATE> ) {
+        next if /^#/ || /^$/;
+        my $name;
+        my $code;
+        if ( /^(.+):(\d+):\\$/ ) {
+            $name = $1;
+            $code = $2;
+            my $next = <STATE>;
+            $next =~ s/^\s*(.*)\s*$/$1/;
+            die "Duplicate define $name" if exists $strings{$name};
+            $strings{$name} = $next;
+        } elsif ( /^(\S+):(\d+):(.*)$/ ) {
+            $name = $1;
+            $code = $2;
+            die "Duplicate define $name" if exists $strings{$name};
+            $strings{$name} = $3;
+        } else {
+            die "Bad line in $statefile:\n$_\n";
+        }
+        my $lib = $name;
+        $lib =~ s/^((?:OSSL_|OPENSSL_)?[^_]{2,}).*$/$1/;
+        $lib = "SSL" if $lib =~ /TLS/;
+        if ( !defined $errorfile{$lib} ) {
+            print "Skipping $_";
+            $skippedstate++;
+            next;
+        }
+        if ( $name =~ /^(?:OSSL_|OPENSSL_)?[A-Z0-9]{2,}_R_/ ) {
+            die "$lib reason code $code collision at $name\n"
+                if $rassigned{$lib} =~ /:$code:/;
+            $rassigned{$lib} .= "$code:";
+            if ( !exists $rextra{$name} ) {
+                $rmax{$lib} = $code if $code > $rmax{$lib};
+            }
+            $rcodes{$name} = $code;
+        } elsif ( $name =~ /^(?:OSSL_|OPENSSL_)?[A-Z0-9]{2,}_F_/ ) {
+            die "$lib function code $code collision at $name\n"
+                if $fassigned{$lib} =~ /:$code:/;
+            $fassigned{$lib} .= "$code:";
+            $fmax{$lib} = $code if $code > $fmax{$lib};
+            $fcodes{$name} = $code;
+        } else {
+            die "Bad line in $statefile:\n$_\n";
+        }
+    }
+    close(STATE);
+
+    if ( $debug ) {
+        foreach my $lib ( sort keys %rmax ) {
+            print STDERR "Reason codes for ${lib}:\n";
+            if ( $rassigned{$lib} =~ m/^:(.*):$/ ) {
+                my @rassigned = sort { $a <=> $b } split( ":", $1 );
+                print STDERR "  ", join(' ', @rassigned), "\n";
+            } else {
+                print STDERR "  --none--\n";
+            }
+        }
+        print STDERR "\n";
+        foreach my $lib ( sort keys %fmax ) {
+            print STDERR "Function codes for ${lib}:\n";
+            if ( $fassigned{$lib} =~ m/^:(.*):$/ ) {
+                my @fassigned = sort { $a <=> $b } split( ":", $1 );
+                print STDERR "  ", join(' ', @fassigned), "\n";
+            } else {
+                print STDERR "  --none--\n";
+            }
+        }
+    }
+}
 
-# Scan each header file in turn and make a list of error codes
+# Scan each header file and make a list of error codes
 # and function names
+&phase("Scanning headers");
+while ( ( my $hdr, my $lib ) = each %libinc ) {
+    next if $hdr eq "NONE";
+    print STDERR " ." if $debug;
+    my $line = "";
+    my $def = "";
+    my $linenr = 0;
+    my $cpp = 0;
+
+    open(IN, "<$hdr") || die "Can't open $hdr, $!,";
+    while ( <IN> ) {
+        $linenr++;
+
+        if ( $line ne '' ) {
+            $_    = $line . $_;
+            $line = '';
+        }
+
+        if ( /\\$/ ) {
+            $line = $_;
+            next;
+        }
+
+        if ( /\/\*/ ) {
+            if ( not /\*\// ) {    # multiline comment...
+                $line = $_;        # ... just accumulate
+                next;
+            } else {
+                s/\/\*.*?\*\///gs;    # wipe it
+            }
+        }
+
+        if ( $cpp ) {
+            $cpp++ if /^#\s*if/;
+            $cpp-- if /^#\s*endif/;
+            next;
+        }
+        $cpp = 1 if /^#.*ifdef.*cplusplus/;    # skip "C" declaration
+
+        next if /^\#/;    # skip preprocessor directives
+
+        s/{[^{}]*}//gs;     # ignore {} blocks
+
+        if ( /\{|\/\*/ ) {    # Add a so editor works...
+            $line = $_;
+        } else {
+            $def .= $_;
+        }
+    }
 
-while (($hdr, $lib) = each %libinc)
-{
-       next if($hdr eq "NONE");
-       print STDERR "Scanning header file $hdr\n" if $debug; 
-       my $line = "", $def= "", $linenr = 0, $gotfile = 0;
-       if (open(IN, "<$hdr")) {
-           $gotfile = 1;
-           while(<IN>) {
-               $linenr++;
-               print STDERR "line: $linenr\r" if $debug;
-
-               last if(/BEGIN\s+ERROR\s+CODES/);
-               if ($line ne '') {
-                   $_ = $line . $_;
-                   $line = '';
-               }
-
-               if (/\\$/) {
-                   $line = $_;
-                   next;
-               }
-
-               if(/\/\*/) {
-                   if (not /\*\//) {           # multiline comment...
-                       $line = $_;             # ... just accumulate
-                       next; 
-                   } else {
-                       s/\/\*.*?\*\///gs;      # wipe it
-                   }
-               }
-
-               if ($cpp) {
-                   $cpp++ if /^#\s*if/;
-                   $cpp-- if /^#\s*endif/;
-                   next;
-               }
-               $cpp = 1 if /^#.*ifdef.*cplusplus/;  # skip "C" declaration
-
-               next if (/^\#/);                      # skip preprocessor directives
-
-               s/{[^{}]*}//gs;                      # ignore {} blocks
-
-               if (/\{|\/\*/) { # Add a } so editor works...
-                   $line = $_;
-               } else {
-                   $def .= $_;
-               }
-           }
-       }
-
-       print STDERR "                                  \r" if $debug;
-        $defnr = 0;
-       # Delete any DECLARE_ macros
-       $def =~ s/DECLARE_\w+\([\w,\s]+\)//gs;
-       foreach (split /;/, $def) {
-           $defnr++;
-           print STDERR "def: $defnr\r" if $debug;
-
-           # The goal is to collect function names from function declarations.
-
-           s/^[\n\s]*//g;
-           s/[\n\s]*$//g;
-
-           # Skip over recognized non-function declarations
-           next if(/typedef\W/ or /DECLARE_STACK_OF/ or /TYPEDEF_.*_OF/);
-
-           # Remove STACK_OF(foo)
-           s/STACK_OF\(\w+\)/void/;
-
-           # Reduce argument lists to empty ()
-           # fold round brackets recursively: (t(*v)(t),t) -> (t{}{},t) -> {}
-           while(/\(.*\)/s) {
-               s/\([^\(\)]+\)/\{\}/gs;
-               s/\(\s*\*\s*(\w+)\s*\{\}\s*\)/$1/gs;    #(*f{}) -> f
-           }
-           # pretend as we didn't use curly braces: {} -> ()
-           s/\{\}/\(\)/gs;
-
-           if (/(\w+)\s*\(\).*/s) {    # first token prior [first] () is
-               my $name = $1;          # a function name!
-               $name =~ tr/[a-z]/[A-Z]/;
-               $ftrans{$name} = $1;
-           } elsif (/[\(\)]/ and not (/=/)) {
-               print STDERR "Header $hdr: cannot parse: $_;\n";
-           }
-       }
-
-       print STDERR "                                  \r" if $debug;
-
-       next if $reindex;
-
-       # Scan function and reason codes and store them: keep a note of the
-       # maximum code used.
-
-       if ($gotfile) {
-         while(<IN>) {
-               if(/^\#\s*define\s+(\S+)\s+(\S+)/) {
-                       $name = $1;
-                       $code = $2;
-                       next if $name =~ /^${lib}err/;
-                       unless($name =~ /^${lib}_([RF])_(\w+)$/) {
-                               print STDERR "Invalid error code $name\n";
-                               next;
-                       }
-                       if($1 eq "R") {
-                               $rcodes{$name} = $code;
-                               if ($rassigned{$lib} =~ /:$code:/) {
-                                       print STDERR "!! ERROR: $lib reason code $code assigned twice (collision at $name)\n";
-                                       ++$errcount;
-                               }
-                               $rassigned{$lib} .= "$code:";
-                               if(!(exists $rextra{$name}) &&
-                                        ($code > $rmax{$lib}) ) {
-                                       $rmax{$lib} = $code;
-                               }
-                       } else {
-                               if ($fassigned{$lib} =~ /:$code:/) {
-                                       print STDERR "!! ERROR: $lib function code $code assigned twice (collision at $name)\n";
-                                       ++$errcount;
-                               }
-                               $fassigned{$lib} .= "$code:";
-                               if($code > $fmax{$lib}) {
-                                       $fmax{$lib} = $code;
-                               }
-                               $fcodes{$name} = $code;
-                       }
-               }
-         }
-       }
-
-       if ($debug) {
-               if (defined($fmax{$lib})) {
-                       print STDERR "Max function code fmax" . "{" . "$lib" . "} = $fmax{$lib}\n";
-                       $fassigned{$lib} =~ m/^:(.*):$/;
-                       @fassigned = sort {$a <=> $b} split(":", $1);
-                       print STDERR "  @fassigned\n";
-               }
-               if (defined($rmax{$lib})) {
-                       print STDERR "Max reason code rmax" . "{" . "$lib" . "} = $rmax{$lib}\n";
-                       $rassigned{$lib} =~ m/^:(.*):$/;
-                       @rassigned = sort {$a <=> $b} split(":", $1);
-                       print STDERR "  @rassigned\n";
-               }
-       }
-
-       if ($lib eq "SSL") {
-               if ($rmax{$lib} >= 1000) {
-                       print STDERR "!! ERROR: SSL error codes 1000+ are reserved for alerts.\n";
-                       print STDERR "!!        Any new alerts must be added to $config.\n";
-                       ++$errcount;
-                       print STDERR "\n";
-               }
-       }
-       close IN;
+    # Delete any DECLARE_ macros
+    my $defnr = 0;
+    $def =~ s/DECLARE_\w+\([\w,\s]+\)//gs;
+    foreach ( split /;/, $def ) {
+        $defnr++;
+        # The goal is to collect function names from function declarations.
+
+        s/^[\n\s]*//g;
+        s/[\n\s]*$//g;
+
+        # Skip over recognized non-function declarations
+        next if /typedef\W/ or /DECLARE_STACK_OF/ or /TYPEDEF_.*_OF/;
+
+        # Remove STACK_OF(foo)
+        s/STACK_OF\(\w+\)/void/;
+
+        # Reduce argument lists to empty ()
+        # fold round brackets recursively: (t(*v)(t),t) -> (t{}{},t) -> {}
+        while ( /\(.*\)/s ) {
+            s/\([^\(\)]+\)/\{\}/gs;
+            s/\(\s*\*\s*(\w+)\s*\{\}\s*\)/$1/gs;    #(*f{}) -> f
+        }
+
+        # pretend as we didn't use curly braces: {} -> ()
+        s/\{\}/\(\)/gs;
+
+        # Last token just before the first () is a function name.
+        if ( /(\w+)\s*\(\).*/s ) {
+            my $name = $1;
+            $name =~ tr/[a-z]/[A-Z]/;
+            $ftrans{$name} = $1;
+        } elsif ( /[\(\)]/ and not(/=/) ) {
+            print STDERR "Header $hdr: cannot parse: $_;\n";
+        }
+    }
+
+    next if $reindex;
+
+    if ( $lib eq "SSL" && $rmax{$lib} >= 1000 ) {
+        print STDERR "SSL error codes 1000+ are reserved for alerts.\n";
+        print STDERR "Any new alerts must be added to $config.\n";
+        $errors++;
+    }
+    close IN;
 }
+print STDERR "\n" if $debug;
 
 # Scan each C source file and look for function and reason codes
 # This is done by looking for strings that "look like" function or
 # reason codes: basically anything consisting of all upper case and
 # numerics which has _F_ or _R_ in it and which has the name of an
-# error library at the start. This seems to work fine except for the
+# error library at the start.  This seems to work fine except for the
 # oddly named structure BIO_F_CTX which needs to be ignored.
 # If a code doesn't exist in list compiled from headers then mark it
 # with the value "X" as a place holder to give it a value later.
-# Store all function and reason codes found in %ufcodes and %urcodes
+# Store all function and reason codes found in %usedfuncs and %usedreasons
 # so all those unreferenced can be printed out.
-
-
-foreach $file (@source) {
-       # Don't parse the error source file.
-       next if exists $cskip{$file};
-       print STDERR "File loaded: ".$file."\r" if $debug;
-       open(IN, "<$file") || die "Can't open source file $file\n";
-       while(<IN>) {
-               # skip obsoleted source files entirely!
-               last if(/^#error\s+obsolete/);
-
-               if(/(([A-Z0-9]+)_F_([A-Z0-9_]+))/) {
-                       next unless exists $csrc{$2};
-                       next if($1 eq "BIO_F_BUFFER_CTX");
-                       $ufcodes{$1} = 1;
-                       if(!exists $fcodes{$1}) {
-                               $fcodes{$1} = "X";
-                               $fnew{$2}++;
-                       }
-                       $notrans{$1} = 1 unless exists $ftrans{$3};
-                       print STDERR "Function: $1\t= $fcodes{$1} (lib: $2, name: $3)\n" if $debug; 
-               }
-               if(/(([A-Z0-9]+)_R_[A-Z0-9_]+)/) {
-                       next unless exists $csrc{$2};
-                       $urcodes{$1} = 1;
-                       if(!exists $rcodes{$1}) {
-                               $rcodes{$1} = "X";
-                               $rnew{$2}++;
-                       }
-                       print STDERR "Reason: $1\t= $rcodes{$1} (lib: $2)\n" if $debug; 
-               } 
-       }
-       close IN;
+&phase("Scanning source");
+my %usedfuncs;
+my %usedreasons;
+foreach my $file ( @source ) {
+    # Don't parse the error source file.
+    next if exists $cskip{$file};
+    open( IN, "<$file" ) || die "Can't open $file, $!,";
+    my $func;
+    my $linenr = 0;
+    print STDERR "$file:\n" if $debug;
+    while ( <IN> ) {
+
+        # skip obsoleted source files entirely!
+        last if /^#error\s+obsolete/;
+        $linenr++;
+        if ( !/;$/ && /^\**([a-zA-Z_].*[\s*])?([A-Za-z_0-9]+)\(.*([),]|$)/ ) {
+            /^([^()]*(\([^()]*\)[^()]*)*)\(/;
+            $1 =~ /([A-Za-z_0-9]*)$/;
+            $func = $1;
+        }
+
+        if ( /(((?:OSSL_|OPENSSL_)?[A-Z0-9]{2,})_F_([A-Z0-9_]+))/ ) {
+            next unless exists $errorfile{$2};
+            next if $1 eq "BIO_F_BUFFER_CTX";
+            $usedfuncs{$1} = 1;
+            if ( !exists $fcodes{$1} ) {
+                print STDERR "  New function $1\n" if $debug;
+                $fcodes{$1} = "X";
+                $fnew{$2}++;
+            }
+            $ftrans{$3} = $func unless exists $ftrans{$3};
+            if ( uc($func) ne $3 ) {
+                print STDERR "ERROR: mismatch $file:$linenr $func:$3\n";
+                $errors++;
+            }
+            print STDERR "  Function $1 = $fcodes{$1}\n"
+              if $debug;
+        }
+        if ( /(((?:OSSL_|OPENSSL_)?[A-Z0-9]{2,})_R_[A-Z0-9_]+)/ ) {
+            next unless exists $errorfile{$2};
+            $usedreasons{$1} = 1;
+            if ( !exists $rcodes{$1} ) {
+                print STDERR "  New reason $1\n" if $debug;
+                $rcodes{$1} = "X";
+                $rnew{$2}++;
+            }
+            print STDERR "  Reason $1 = $rcodes{$1}\n" if $debug;
+        }
+    }
+    close IN;
 }
-print STDERR "                                  \n" if $debug;
+print STDERR "\n" if $debug;
 
 # Now process each library in turn.
+&phase("Writing files");
+my $newstate = 0;
+foreach my $lib ( keys %errorfile ) {
+    if ( ! $fnew{$lib} && ! $rnew{$lib} ) {
+        next unless $rebuild;
+    }
+    next if scalar keys %modules > 0 && !$modules{$lib};
+    next if $nowrite;
+    print STDERR "$lib: $fnew{$lib} new functions\n" if $fnew{$lib};
+    print STDERR "$lib: $rnew{$lib} new reasons\n" if $rnew{$lib};
+    $newstate = 1;
 
-foreach $lib (keys %csrc)
-{
-       my $hfile = $hinc{$lib};
-       my $cfile = $csrc{$lib};
-       if(!$fnew{$lib} && !$rnew{$lib}) {
-               print STDERR "$lib:\t\tNo new error codes\n";
-               next unless $rebuild;
-       } else {
-               print STDERR "$lib:\t\t$fnew{$lib} New Functions,";
-               print STDERR " $rnew{$lib} New Reasons.\n";
-               next unless $dowrite;
-       }
-
-       # If we get here then we have some new error codes so we
-       # need to rebuild the header file and C file.
-
-       # Make a sorted list of error and reason codes for later use.
-
-       my @function = sort grep(/^${lib}_/,keys %fcodes);
-       my @reasons = sort grep(/^${lib}_/,keys %rcodes);
-
-       # Rewrite the header file
-
-       if (open(IN, "<$hfile")) {
-           # Copy across the old file
-           while(<IN>) {
-               push @out, $_;
-               last if (/BEGIN ERROR CODES/);
-           }
-           close IN;
-       } else {
-           push @out,
-"/* ====================================================================\n",
-" * Copyright (c) 2001-$year The OpenSSL Project.  All rights reserved.\n",
-" *\n",
-" * Redistribution and use in source and binary forms, with or without\n",
-" * modification, are permitted provided that the following conditions\n",
-" * are met:\n",
-" *\n",
-" * 1. Redistributions of source code must retain the above copyright\n",
-" *    notice, this list of conditions and the following disclaimer. \n",
-" *\n",
-" * 2. Redistributions in binary form must reproduce the above copyright\n",
-" *    notice, this list of conditions and the following disclaimer in\n",
-" *    the documentation and/or other materials provided with the\n",
-" *    distribution.\n",
-" *\n",
-" * 3. All advertising materials mentioning features or use of this\n",
-" *    software must display the following acknowledgment:\n",
-" *    \"This product includes software developed by the OpenSSL Project\n",
-" *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)\"\n",
-" *\n",
-" * 4. The names \"OpenSSL Toolkit\" and \"OpenSSL Project\" must not be used to\n",
-" *    endorse or promote products derived from this software without\n",
-" *    prior written permission. For written permission, please contact\n",
-" *    openssl-core\@openssl.org.\n",
-" *\n",
-" * 5. Products derived from this software may not be called \"OpenSSL\"\n",
-" *    nor may \"OpenSSL\" appear in their names without prior written\n",
-" *    permission of the OpenSSL Project.\n",
-" *\n",
-" * 6. Redistributions of any form whatsoever must retain the following\n",
-" *    acknowledgment:\n",
-" *    \"This product includes software developed by the OpenSSL Project\n",
-" *    for use in the OpenSSL Toolkit (http://www.openssl.org/)\"\n",
-" *\n",
-" * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY\n",
-" * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n",
-" * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n",
-" * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR\n",
-" * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n",
-" * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\n",
-" * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n",
-" * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n",
-" * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,\n",
-" * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n",
-" * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED\n",
-" * OF THE POSSIBILITY OF SUCH DAMAGE.\n",
-" * ====================================================================\n",
-" *\n",
-" * This product includes cryptographic software written by Eric Young\n",
-" * (eay\@cryptsoft.com).  This product includes software written by Tim\n",
-" * Hudson (tjh\@cryptsoft.com).\n",
-" *\n",
-" */\n",
-"\n",
-"#ifndef HEADER_${lib}_ERR_H\n",
-"#define HEADER_${lib}_ERR_H\n",
-"\n",
-"#ifdef  __cplusplus\n",
-"extern \"C\" {\n",
-"#endif\n",
-"\n",
-"/* BEGIN ERROR CODES */\n";
-       }
-       open (OUT, ">$hfile") || die "Can't Open File $hfile for writing\n";
-
-       print OUT @out;
-       undef @out;
-       print OUT <<"EOF";
+    # If we get here then we have some new error codes so we
+    # need to rebuild the header file and C file.
+
+    # Make a sorted list of error and reason codes for later use.
+    my @function = sort grep( /^${lib}_/, keys %fcodes );
+    my @reasons  = sort grep( /^${lib}_/, keys %rcodes );
+
+    # indent level for innermost preprocessor lines
+    my $indent = " ";
+
+    # Rewrite the header file
+
+    my $hfile = $hinc{$lib};
+    $hfile =~ s/.h$/err.h/ if $internal;
+    open( OUT, ">$hfile" ) || die "Can't write to $hfile, $!,";
+    print OUT <<"EOF";
 /*
- * The following lines are auto generated by the script mkerr.pl. Any changes
- * made after this point may be overwritten when the script is next run.
+ * Generated by util/mkerr.pl DO NOT EDIT
+ * Copyright 1995-$YEAR The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the \"License\").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
  */
-EOF
-       if($static) {
-               print OUT <<"EOF";
-${staticloader}void ERR_load_${lib}_strings(void);
 
-EOF
-       } else {
-               print OUT <<"EOF";
-${staticloader}void ERR_load_${lib}_strings(void);
-${staticloader}void ERR_unload_${lib}_strings(void);
-${staticloader}void ERR_${lib}_error(int function, int reason, char *file, int line);
-# define ${lib}err(f,r) ERR_${lib}_error((f),(r),__FILE__,__LINE__)
+#ifndef HEADER_${lib}ERR_H
+# define HEADER_${lib}ERR_H
 
 EOF
-       }
-       print OUT <<"EOF";
-/* Error codes for the $lib functions. */
+    if ( $internal ) {
+        # Declare the load function because the generate C file
+        # includes "fooerr.h" not "foo.h"
+        if ($lib ne "SSL" && $lib ne "ASYNC"
+                && grep { $lib eq uc $_ } @disablables) {
+            print OUT <<"EOF";
+# include <openssl/opensslconf.h>
+
+# ifndef OPENSSL_NO_${lib}
 
-/* Function codes. */
 EOF
+            $indent = "  ";
+        }
+        print OUT <<"EOF";
+#${indent}ifdef  __cplusplus
+extern \"C\"
+#${indent}endif
+int ERR_load_${lib}_strings(void);
+EOF
+    } else {
+        print OUT <<"EOF";
+# define ${lib}err(f, r) ERR_${lib}_error((f), (r), OPENSSL_FILE, OPENSSL_LINE)
 
-       foreach $i (@function) {
-               $z=48 - length($i);
-               if($fcodes{$i} eq "X") {
-                       $fassigned{$lib} =~ m/^:([^:]*):/;
-                       $findcode = $1;
-                       if (!defined($findcode)) {
-                               $findcode = $fmax{$lib};
-                       }
-                       while ($fassigned{$lib} =~ m/:$findcode:/) {
-                               $findcode++;
-                       }
-                       $fcodes{$i} = $findcode;
-                       $fassigned{$lib} .= "$findcode:";
-                       print STDERR "New Function code $i\n" if $debug;
-               }
-               printf OUT "# define $i%s $fcodes{$i}\n"," " x $z;
-       }
-
-       print OUT "\n/* Reason codes. */\n";
-
-       foreach $i (@reasons) {
-               $z=48 - length($i);
-               if($rcodes{$i} eq "X") {
-                       $rassigned{$lib} =~ m/^:([^:]*):/;
-                       $findcode = $1;
-                       if (!defined($findcode)) {
-                               $findcode = $rmax{$lib};
-                       }
-                       while ($rassigned{$lib} =~ m/:$findcode:/) {
-                               $findcode++;
-                       }
-                       $rcodes{$i} = $findcode;
-                       $rassigned{$lib} .= "$findcode:";
-                       print STDERR "New Reason code   $i\n" if $debug;
-               }
-               printf OUT "# define $i%s $rcodes{$i}\n"," " x $z;
-       }
-       print OUT <<"EOF";
-
-#ifdef  __cplusplus
+EOF
+        if ( ! $static ) {
+            print OUT <<"EOF";
+
+# ifdef  __cplusplus
+extern \"C\" {
+# endif
+int ERR_load_${lib}_strings(void);
+void ERR_unload_${lib}_strings(void);
+void ERR_${lib}_error(int function, int reason, char *file, int line);
+# ifdef  __cplusplus
 }
-#endif
-#endif
+# endif
 EOF
-       close OUT;
-
-       # Rewrite the C source file containing the error details.
-
-       # First, read any existing reason string definitions:
-       my %err_reason_strings;
-       if (open(IN,"<$cfile")) {
-               while (<IN>) {
-                       if (/\b(${lib}_R_\w*)\b.*\"(.*)\"/) {
-                               $err_reason_strings{$1} = $2;
-                       }
-                       if (/\b${lib}_F_(\w*)\b.*\"(.*)\"/) {
-                               if (!exists $ftrans{$1} && ($1 ne $2)) {
-                                       print STDERR "WARNING: Mismatched function string $2\n";
-                                       $ftrans{$1} = $2;
-                               }
-                       }
-               }
-               close(IN);
-       }
-
-
-       my $hincf;
-       if($static) {
-               $hfile =~ /([^\/]+)$/;
-               $hincf = "<${hprefix}$1>";
-       } else {
-               $hincf = "\"$hfile\"";
-       }
-
-       # If static we know the error code at compile time so use it
-       # in error definitions.
-
-       if ($static)
-               {
-               $pack_errcode = "ERR_LIB_${lib}";
-               $load_errcode = "0";
-               }
-       else
-               {
-               $pack_errcode = "0";
-               $load_errcode = "ERR_LIB_${lib}";
-               }
-
-
-       open (OUT,">$cfile") || die "Can't open $cfile for writing";
-
-       print OUT <<"EOF";
-/* $cfile */
-/* ====================================================================
- * Copyright (c) 1999-$year The OpenSSL Project.  All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *
- * 3. All advertising materials mentioning features or use of this
- *    software must display the following acknowledgment:
- *    "This product includes software developed by the OpenSSL Project
- *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
- *
- * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
- *    endorse or promote products derived from this software without
- *    prior written permission. For written permission, please contact
- *    openssl-core\@OpenSSL.org.
- *
- * 5. Products derived from this software may not be called "OpenSSL"
- *    nor may "OpenSSL" appear in their names without prior written
- *    permission of the OpenSSL Project.
- *
- * 6. Redistributions of any form whatsoever must retain the following
- *    acknowledgment:
- *    "This product includes software developed by the OpenSSL Project
- *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
- *
- * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
- * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
- * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
- * OF THE POSSIBILITY OF SUCH DAMAGE.
- * ====================================================================
- *
- * This product includes cryptographic software written by Eric Young
- * (eay\@cryptsoft.com).  This product includes software written by Tim
- * Hudson (tjh\@cryptsoft.com).
- *
- */
+        }
+    }
+
+    print OUT "\n/*\n * $lib function codes.\n */\n";
+    foreach my $i ( @function ) {
+        my $z = 48 - length($i);
+        $z = 0 if $z < 0;
+        if ( $fcodes{$i} eq "X" ) {
+            $fassigned{$lib} =~ m/^:([^:]*):/;
+            my $findcode = $1;
+            $findcode = $fmax{$lib} if !defined $findcode;
+            while ( $fassigned{$lib} =~ m/:$findcode:/ ) {
+                $findcode++;
+            }
+            $fcodes{$i} = $findcode;
+            $fassigned{$lib} .= "$findcode:";
+            print STDERR "New Function code $i\n" if $debug;
+        }
+        printf OUT "#${indent}define $i%s $fcodes{$i}\n", " " x $z;
+    }
+
+    print OUT "\n/*\n * $lib reason codes.\n */\n";
+    foreach my $i ( @reasons ) {
+        my $z = 48 - length($i);
+        $z = 0 if $z < 0;
+        if ( $rcodes{$i} eq "X" ) {
+            $rassigned{$lib} =~ m/^:([^:]*):/;
+            my $findcode = $1;
+            $findcode = $rmax{$lib} if !defined $findcode;
+            while ( $rassigned{$lib} =~ m/:$findcode:/ ) {
+                $findcode++;
+            }
+            $rcodes{$i} = $findcode;
+            $rassigned{$lib} .= "$findcode:";
+            print STDERR "New Reason code $i\n" if $debug;
+        }
+        printf OUT "#${indent}define $i%s $rcodes{$i}\n", " " x $z;
+    }
+    print OUT "\n";
+
+    while (length($indent) > 0) {
+        $indent = substr $indent, 0, -1;
+        print OUT "#${indent}endif\n";
+    }
+
+    # Rewrite the C source file containing the error details.
+
+    # First, read any existing reason string definitions:
+    my $cfile = $errorfile{$lib};
+    my $pack_lib = $internal ? "ERR_LIB_${lib}" : "0";
+    my $hincf = $hfile;
+    $hincf =~ s|.*include/||;
+    if ( $hincf =~ m|^openssl/| ) {
+        $hincf = "<${hincf}>";
+    } else {
+        $hincf = "\"${hincf}\"";
+    }
 
+    open( OUT, ">$cfile" )
+        || die "Can't open $cfile for writing, $!, stopped";
+
+    my $const = $internal ? 'const ' : '';
+
+    print OUT <<"EOF";
 /*
- * NOTE: this file was auto generated by the mkerr.pl script: any changes
- * made to it will be overwritten when the script next updates this file,
- * only reason strings will be preserved.
+ * Generated by util/mkerr.pl DO NOT EDIT
+ * Copyright 1995-$YEAR The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
  */
 
-#include <stdio.h>
 #include <openssl/err.h>
 #include $hincf
 
-/* BEGIN ERROR CODES */
 #ifndef OPENSSL_NO_ERR
 
-# define ERR_FUNC(func) ERR_PACK($pack_errcode,func,0)
-# define ERR_REASON(reason) ERR_PACK($pack_errcode,0,reason)
-
-static ERR_STRING_DATA ${lib}_str_functs[] = {
+static ${const}ERR_STRING_DATA ${lib}_str_functs[] = {
 EOF
-       # Add each function code: if a function name is found then use it.
-       foreach $i (@function) {
-               my $fn;
-               $i =~ /^${lib}_F_(\S+)$/;
-               $fn = $1;
-               if(exists $ftrans{$fn}) {
-                       $fn = $ftrans{$fn};
-               }
-#              print OUT "{ERR_PACK($pack_errcode,$i,0),\t\"$fn\"},\n";
-               if(length($i) + length($fn) > 58) {
-                       print OUT "    {ERR_FUNC($i),\n     \"$fn\"},\n";
-               } else {
-                       print OUT "    {ERR_FUNC($i), \"$fn\"},\n";
-               }
-       }
-       print OUT <<"EOF";
+
+    # Add each function code: if a function name is found then use it.
+    foreach my $i ( @function ) {
+        my $fn;
+        if ( exists $strings{$i} and $strings{$i} ne '' ) {
+            $fn = $strings{$i};
+            $fn = "" if $fn eq '*';
+        } else {
+            $i =~ /^${lib}_F_(\S+)$/;
+            $fn = $1;
+            $fn = $ftrans{$fn} if exists $ftrans{$fn};
+            $strings{$i} = $fn;
+        }
+        my $short = "    {ERR_PACK($pack_lib, $i, 0), \"$fn\"},";
+        if ( length($short) <= 80 ) {
+            print OUT "$short\n";
+        } else {
+            print OUT "    {ERR_PACK($pack_lib, $i, 0),\n     \"$fn\"},\n";
+        }
+    }
+    print OUT <<"EOF";
     {0, NULL}
 };
 
-static ERR_STRING_DATA ${lib}_str_reasons[] = {
+static ${const}ERR_STRING_DATA ${lib}_str_reasons[] = {
 EOF
-       # Add each reason code.
-       foreach $i (@reasons) {
-               my $rn;
-               my $rstr = "ERR_REASON($i)";
-               if (exists $err_reason_strings{$i}) {
-                       $rn = $err_reason_strings{$i};
-               } else {
-                       $i =~ /^${lib}_R_(\S+)$/;
-                       $rn = $1;
-                       $rn =~ tr/_[A-Z]/ [a-z]/;
-               }
-               if(length($i) + length($rn) > 56) {
-                       print OUT "    {${rstr},\n     \"$rn\"},\n";
-               } else {
-                       print OUT "    {${rstr}, \"$rn\"},\n";
-               }
-       }
-if($static) {
-       print OUT <<"EOF";
+
+    # Add each reason code.
+    foreach my $i ( @reasons ) {
+        my $rn;
+        if ( exists $strings{$i} ) {
+            $rn = $strings{$i};
+            $rn = "" if $rn eq '*';
+        } else {
+            $i =~ /^${lib}_R_(\S+)$/;
+            $rn = $1;
+            $rn =~ tr/_[A-Z]/ [a-z]/;
+            $strings{$i} = $rn;
+        }
+        my $short = "    {ERR_PACK($pack_lib, 0, $i), \"$rn\"},";
+        if ( length($short) <= 80 ) {
+            print OUT "$short\n";
+        } else {
+            print OUT "    {ERR_PACK($pack_lib, 0, $i),\n    \"$rn\"},\n";
+        }
+    }
+    print OUT <<"EOF";
     {0, NULL}
 };
 
 #endif
+EOF
+    if ( $internal ) {
+        print OUT <<"EOF";
 
-${staticloader}void ERR_load_${lib}_strings(void)
+int ERR_load_${lib}_strings(void)
 {
 #ifndef OPENSSL_NO_ERR
-
     if (ERR_func_error_string(${lib}_str_functs[0].error) == NULL) {
-        ERR_load_strings($load_errcode, ${lib}_str_functs);
-        ERR_load_strings($load_errcode, ${lib}_str_reasons);
+        ERR_load_strings_const(${lib}_str_functs);
+        ERR_load_strings_const(${lib}_str_reasons);
     }
 #endif
+    return 1;
 }
 EOF
-} else {
-       print OUT <<"EOF";
-    {0, NULL}
-};
-
-#endif
+    } else {
+        my $st = $static ? "static " : "";
+        print OUT <<"EOF";
 
-#ifdef ${lib}_LIB_NAME
-static ERR_STRING_DATA ${lib}_lib_name[] = {
-    {0, ${lib}_LIB_NAME},
-    {0, NULL}
-};
-#endif
+static int lib_code = 0;
+static int error_loaded = 0;
 
-static int ${lib}_lib_error_code = 0;
-static int ${lib}_error_init = 1;
-
-${staticloader}void ERR_load_${lib}_strings(void)
+${st}int ERR_load_${lib}_strings(void)
 {
-    if (${lib}_lib_error_code == 0)
-        ${lib}_lib_error_code = ERR_get_next_error_library();
+    if (lib_code == 0)
+        lib_code = ERR_get_next_error_library();
 
-    if (${lib}_error_init) {
-        ${lib}_error_init = 0;
+    if (!error_loaded) {
 #ifndef OPENSSL_NO_ERR
-        ERR_load_strings(${lib}_lib_error_code, ${lib}_str_functs);
-        ERR_load_strings(${lib}_lib_error_code, ${lib}_str_reasons);
-#endif
-
-#ifdef ${lib}_LIB_NAME
-        ${lib}_lib_name->error = ERR_PACK(${lib}_lib_error_code, 0, 0);
-        ERR_load_strings(0, ${lib}_lib_name);
+        ERR_load_strings(lib_code, ${lib}_str_functs);
+        ERR_load_strings(lib_code, ${lib}_str_reasons);
 #endif
+        error_loaded = 1;
     }
+    return 1;
 }
 
-${staticloader}void ERR_unload_${lib}_strings(void)
+${st}void ERR_unload_${lib}_strings(void)
 {
-    if (${lib}_error_init == 0) {
+    if (error_loaded) {
 #ifndef OPENSSL_NO_ERR
-        ERR_unload_strings(${lib}_lib_error_code, ${lib}_str_functs);
-        ERR_unload_strings(${lib}_lib_error_code, ${lib}_str_reasons);
+        ERR_unload_strings(lib_code, ${lib}_str_functs);
+        ERR_unload_strings(lib_code, ${lib}_str_reasons);
 #endif
-
-#ifdef ${lib}_LIB_NAME
-        ERR_unload_strings(0, ${lib}_lib_name);
-#endif
-        ${lib}_error_init = 1;
+        error_loaded = 0;
     }
 }
 
-${staticloader}void ERR_${lib}_error(int function, int reason, char *file, int line)
+${st}void ERR_${lib}_error(int function, int reason, char *file, int line)
 {
-    if (${lib}_lib_error_code == 0)
-        ${lib}_lib_error_code = ERR_get_next_error_library();
-    ERR_PUT_error(${lib}_lib_error_code, function, reason, file, line);
+    if (lib_code == 0)
+        lib_code = ERR_get_next_error_library();
+    ERR_PUT_error(lib_code, function, reason, file, line);
 }
 EOF
 
-}
-
-       close OUT;
-       undef %err_reason_strings;
-}
+    }
 
-if($debug && %notrans) {
-       print STDERR "The following function codes were not translated:\n";
-       foreach(sort keys %notrans)
-       {
-               print STDERR "$_\n";
-       }
+    close OUT;
 }
 
+&phase("Ending");
 # Make a list of unreferenced function and reason codes
-
-foreach (keys %fcodes) {
-       push (@funref, $_) unless exists $ufcodes{$_};
-}
-
-foreach (keys %rcodes) {
-       push (@runref, $_) unless exists $urcodes{$_};
-}
-
-if($debug && @funref) {
-       print STDERR "The following function codes were not referenced:\n";
-       foreach(sort @funref)
-       {
-               print STDERR "$_\n";
-       }
-}
-
-if($debug && @runref) {
-       print STDERR "The following reason codes were not referenced:\n";
-       foreach(sort @runref)
-       {
-               print STDERR "$_\n";
-       }
+if ( $unref ) {
+    my @funref;
+    foreach ( keys %fcodes ) {
+        push( @funref, $_ ) unless exists $usedfuncs{$_};
+    }
+    my @runref;
+    foreach ( keys %rcodes ) {
+        push( @runref, $_ ) unless exists $usedreasons{$_};
+    }
+    if ( @funref ) {
+        print STDERR "The following function codes were not referenced:\n";
+        foreach ( sort @funref ) {
+            print STDERR "  $_\n";
+        }
+    }
+    if ( @runref ) {
+        print STDERR "The following reason codes were not referenced:\n";
+        foreach ( sort @runref ) {
+            print STDERR "  $_\n";
+        }
+    }
 }
 
-if($errcount) {
-       print STDERR "There were errors, failing...\n\n";
-       exit $errcount;
+die "Found $errors errors, quitting" if $errors;
+
+# Update the state file
+if ( $newstate )  {
+    open(OUT, ">$statefile.new")
+        || die "Can't write $statefile.new, $!";
+    print OUT <<"EOF";
+# Copyright 1999-$YEAR The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+EOF
+    print OUT "\n# Function codes\n";
+    foreach my $i ( sort keys %fcodes ) {
+        my $short = "$i:$fcodes{$i}:";
+        my $t = exists $strings{$i} ? $strings{$i} : "";
+        $t = "\\\n\t" . $t if length($short) + length($t) > 80;
+        print OUT "$short$t\n";
+    }
+    print OUT "\n#Reason codes\n";
+    foreach my $i ( sort keys %rcodes ) {
+        my $short = "$i:$rcodes{$i}:";
+        my $t = exists $strings{$i} ? "$strings{$i}" : "";
+        $t = "\\\n\t" . $t if length($short) + length($t) > 80;
+        print OUT "$short$t\n" if !exists $rextra{$i};
+    }
+    close(OUT);
+    if ( $skippedstate ) {
+        print "Skipped state, leaving update in $statefile.new";
+    } else {
+        rename "$statefile", "$statefile.old"
+            || die "Can't backup $statefile to $statefile.old, $!";
+        rename "$statefile.new", "$statefile"
+            || die "Can't rename $statefile to $statefile.new, $!";
+    }
 }
 
+exit;