Fix to allow multiple NONE libraries in mkerr.pl .
[openssl.git] / util / mkerr.pl
1 #!/usr/local/bin/perl -w
2
3 my $config = "crypto/err/openssl.ec";
4 my $debug = 0;
5 my $rebuild = 0;
6 my $static = 1;
7 my $recurse = 0;
8 my $reindex = 0;
9 my $dowrite = 0;
10
11
12 while (@ARGV) {
13         my $arg = $ARGV[0];
14         if($arg eq "-conf") {
15                 shift @ARGV;
16                 $config = shift @ARGV;
17         } elsif($arg eq "-debug") {
18                 $debug = 1;
19                 shift @ARGV;
20         } elsif($arg eq "-rebuild") {
21                 $rebuild = 1;
22                 shift @ARGV;
23         } elsif($arg eq "-recurse") {
24                 $recurse = 1;
25                 shift @ARGV;
26         } elsif($arg eq "-reindex") {
27                 $reindex = 1;
28                 shift @ARGV;
29         } elsif($arg eq "-nostatic") {
30                 $static = 0;
31                 shift @ARGV;
32         } elsif($arg eq "-write") {
33                 $dowrite = 1;
34                 shift @ARGV;
35         } else {
36                 last;
37         }
38 }
39
40 if($recurse) {
41         @source = (<crypto/*.c>, <crypto/*/*.c>, <ssl/*.c>);
42 } else {
43         @source = @ARGV;
44 }
45
46 # Read in the config file
47
48 open(IN, "<$config") || die "Can't open config file $config";
49
50 # Parse config file
51
52 while(<IN>)
53 {
54         if(/^L\s+(\S+)\s+(\S+)\s+(\S+)/) {
55                 $hinc{$1} = $2;
56                 $libinc{$2} = $1;
57                 $cskip{$3} = $1;
58                 if($3 ne "NONE") {
59                         $csrc{$1} = $3;
60                         $fmax{$1} = 99;
61                         $rmax{$1} = 99;
62                         $fnew{$1} = 0;
63                         $rnew{$1} = 0;
64                 }
65         } elsif (/^F\s+(\S+)/) {
66         # Add extra function with $1
67         } elsif (/^R\s+(\S+)\s+(\S+)/) {
68                 $rextra{$1} = $2;
69                 $rcodes{$1} = $2;
70         }
71 }
72
73 close IN;
74
75 # Scan each header file in turn and make a list of error codes
76 # and function names
77
78 while (($hdr, $lib) = each %libinc)
79 {
80         next if($hdr eq "NONE");
81         print STDERR "Scanning header file $hdr\n" if $debug; 
82         open(IN, "<$hdr") || die "Can't open Header file $hdr\n";
83         my $line = "", $def= "", $linenr = 0;
84         while(<IN>) {
85             $linenr++;
86             print STDERR "line: $linenr\r" if $debug;
87
88             last if(/BEGIN\s+ERROR\s+CODES/);
89             if ($line ne '') {
90                 $_ = $line . $_;
91                 $line = '';
92             }
93
94             if (/\\$/) {
95                 $line = $_;
96                 next;
97             }
98
99             $cpp = 1 if /^#.*ifdef.*cplusplus/;  # skip "C" declaration
100             if ($cpp) {
101                 $cpp = 0 if /^#.*endif/;
102                 next;
103             }
104
105             next if (/^#/);                      # skip preprocessor directives
106
107             s/\/\*.*?\*\///gs;                   # ignore comments
108             s/{[^{}]*}//gs;                      # ignore {} blocks
109
110             if (/{|\/\*/) { # Add a } so editor works...
111                 $line = $_;
112             } else {
113                 $def .= $_;
114             }
115         }
116
117         print STDERR "                                  \r" if $debug;
118         $defnr = 0;
119         foreach (split /;/, $def) {
120             $defnr++;
121             print STDERR "def: $defnr\r" if $debug;
122
123             s/^[\n\s]*//g;
124             s/[\n\s]*$//g;
125             next if(/typedef\W/);
126             if (/\(\*(\w*)\([^\)]+/) {
127                 my $name = $1;
128                 $name =~ tr/[a-z]/[A-Z]/;
129                 $ftrans{$name} = $1;
130             } elsif (/\w+\W+(\w+)\W*\(\s*\)$/s){
131                 # K&R C
132                 next ;
133             } elsif (/\w+\W+\w+\W*\(.*\)$/s) {
134                 while (not /\(\)$/s) {
135                     s/[^\(\)]*\)$/\)/s;
136                     s/\([^\(\)]*\)\)$/\)/s;
137                 }
138                 s/\(void\)//;
139                 /(\w+)\W*\(\)/s;
140                 my $name = $1;
141                 $name =~ tr/[a-z]/[A-Z]/;
142                 $ftrans{$name} = $1;
143             } elsif (/\(/ and not (/=/ or /DECLARE_STACK/)) {
144                 print STDERR "Header $hdr: cannot parse: $_;\n";
145             }
146         }
147
148         print STDERR "                                  \r" if $debug;
149
150         next if $reindex;
151
152         # Scan function and reason codes and store them: keep a note of the
153         # maximum code used.
154
155         while(<IN>) {
156                 if(/^#define\s+(\S+)\s+(\S+)/) {
157                         $name = $1;
158                         $code = $2;
159                         unless($name =~ /^${lib}_([RF])_(\w+)$/) {
160                                 print STDERR "Invalid error code $name\n";
161                                 next;
162                         }
163                         if($1 eq "R") {
164                                 $rcodes{$name} = $code;
165                                 if(!(exists $rextra{$name}) &&
166                                          ($code > $rmax{$lib}) ) {
167                                         $rmax{$lib} = $code;
168                                 }
169                         } else {
170                                 if($code > $fmax{$lib}) {
171                                         $fmax{$lib} = $code;
172                                 }
173                                 $fcodes{$name} = $code;
174                         }
175                 }
176         }
177         close IN;
178 }
179
180 # Scan each C source file and look for function and reason codes
181 # This is done by looking for strings that "look like" function or
182 # reason codes: basically anything consisting of all upper case and
183 # numerics which has _F_ or _R_ in it and which has the name of an
184 # error library at the start. This seems to work fine except for the
185 # oddly named structure BIO_F_CTX which needs to be ignored.
186 # If a code doesn't exist in list compiled from headers then mark it
187 # with the value "X" as a place holder to give it a value later.
188 # Store all function and reason codes found in %ufcodes and %urcodes
189 # so all those unreferenced can be printed out.
190
191
192 foreach $file (@source) {
193         # Don't parse the error source file.
194         next if exists $cskip{$file};
195         open(IN, "<$file") || die "Can't open source file $file\n";
196         while(<IN>) {
197                 if(/(([A-Z0-9]+)_F_([A-Z0-9_]+))/) {
198                         next unless exists $csrc{$2};
199                         next if($1 eq "BIO_F_BUFFER_CTX");
200                         $ufcodes{$1} = 1;
201                         if(!exists $fcodes{$1}) {
202                                 $fcodes{$1} = "X";
203                                 $fnew{$2}++;
204                         }
205                         $notrans{$1} = 1 unless exists $ftrans{$3};
206                 }
207                 if(/(([A-Z0-9]+)_R_[A-Z0-9_]+)/) {
208                         next unless exists $csrc{$2};
209                         $urcodes{$1} = 1;
210                         if(!exists $rcodes{$1}) {
211                                 $rcodes{$1} = "X";
212                                 $rnew{$2}++;
213                         }
214                 } 
215         }
216         close IN;
217 }
218
219 # Now process each library in turn.
220
221 foreach $lib (keys %csrc)
222 {
223         my $hfile = $hinc{$lib};
224         my $cfile = $csrc{$lib};
225         if(!$fnew{$lib} && !$rnew{$lib}) {
226                 print STDERR "$lib:\t\tNo new error codes\n";
227                 next unless $rebuild;
228         } else {
229                 print STDERR "$lib:\t\t$fnew{$lib} New Functions,";
230                 print STDERR " $rnew{$lib} New Reasons.\n";
231                 next unless $dowrite;
232         }
233
234         # If we get here then we have some new error codes so we
235         # need to rebuild the header file and C file.
236
237         # Make a sorted list of error and reason codes for later use.
238
239         my @function = sort grep(/^${lib}_/,keys %fcodes);
240         my @reasons = sort grep(/^${lib}_/,keys %rcodes);
241
242         # Rewrite the header file
243
244         open(IN, "<$hfile") || die "Can't Open Header File $hfile\n";
245
246         # Copy across the old file
247         while(<IN>) {
248                 push @out, $_;
249                 last if (/BEGIN ERROR CODES/);
250         }
251         close IN;
252
253         open (OUT, ">$hfile") || die "Can't Open File $hfile for writing\n";
254
255         print OUT @out;
256         undef @out;
257         print OUT <<"EOF";
258 /* The following lines are auto generated by the script mkerr.pl. Any changes
259  * made after this point may be overwritten when the script is next run.
260  */
261 void ERR_load_${lib}_strings(void);
262
263 /* Error codes for the $lib functions. */
264
265 /* Function codes. */
266 EOF
267
268         foreach $i (@function) {
269                 $z=6-int(length($i)/8);
270                 if($fcodes{$i} eq "X") {
271                         $fcodes{$i} = ++$fmax{$lib};
272                         print STDERR "New Function code $i\n" if $debug;
273                 }
274                 printf OUT "#define $i%s $fcodes{$i}\n","\t" x $z;
275         }
276
277         print OUT "\n/* Reason codes. */\n";
278
279         foreach $i (@reasons) {
280                 $z=6-int(length($i)/8);
281                 if($rcodes{$i} eq "X") {
282                         $rcodes{$i} = ++$rmax{$lib};
283                         print STDERR "New Reason code   $i\n" if $debug;
284                 }
285                 printf OUT "#define $i%s $rcodes{$i}\n","\t" x $z;
286         }
287         print OUT <<"EOF";
288
289 #ifdef  __cplusplus
290 }
291 #endif
292 #endif
293 EOF
294         close OUT;
295
296         # Rewrite the C source file containing the error details.
297
298         # First, read any existing reason string definitions:
299         my %err_reason_strings;
300         if (open(IN,"<$cfile")) {
301                 while (<IN>) {
302                         if (/\b(${lib}_R_\w*)\b.*\"(.*)\"/) {
303                                 $err_reason_strings{$1} = $2;
304                         }
305                 }
306                 close(IN);
307         }
308
309         my $hincf;
310         if($static) {
311                 $hfile =~ /([^\/]+)$/;
312                 $hincf = "<openssl/$1>";
313         } else {
314                 $hincf = "\"$hfile\"";
315         }
316
317
318         open (OUT,">$cfile") || die "Can't open $cfile for writing";
319
320         print OUT <<"EOF";
321 /* $cfile */
322 /* ====================================================================
323  * Copyright (c) 1999 The OpenSSL Project.  All rights reserved.
324  *
325  * Redistribution and use in source and binary forms, with or without
326  * modification, are permitted provided that the following conditions
327  * are met:
328  *
329  * 1. Redistributions of source code must retain the above copyright
330  *    notice, this list of conditions and the following disclaimer. 
331  *
332  * 2. Redistributions in binary form must reproduce the above copyright
333  *    notice, this list of conditions and the following disclaimer in
334  *    the documentation and/or other materials provided with the
335  *    distribution.
336  *
337  * 3. All advertising materials mentioning features or use of this
338  *    software must display the following acknowledgment:
339  *    "This product includes software developed by the OpenSSL Project
340  *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
341  *
342  * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
343  *    endorse or promote products derived from this software without
344  *    prior written permission. For written permission, please contact
345  *    openssl-core\@OpenSSL.org.
346  *
347  * 5. Products derived from this software may not be called "OpenSSL"
348  *    nor may "OpenSSL" appear in their names without prior written
349  *    permission of the OpenSSL Project.
350  *
351  * 6. Redistributions of any form whatsoever must retain the following
352  *    acknowledgment:
353  *    "This product includes software developed by the OpenSSL Project
354  *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
355  *
356  * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
357  * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
358  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
359  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
360  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
361  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
362  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
363  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
364  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
365  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
366  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
367  * OF THE POSSIBILITY OF SUCH DAMAGE.
368  * ====================================================================
369  *
370  * This product includes cryptographic software written by Eric Young
371  * (eay\@cryptsoft.com).  This product includes software written by Tim
372  * Hudson (tjh\@cryptsoft.com).
373  *
374  */
375
376 /* NOTE: this file was auto generated by the mkerr.pl script: any changes
377  * made to it will be overwritten when the script next updates this file,
378  * only reason strings will be preserved.
379  */
380
381 #include <stdio.h>
382 #include <openssl/err.h>
383 #include $hincf
384
385 /* BEGIN ERROR CODES */
386 #ifndef OPENSSL_NO_ERR
387 static ERR_STRING_DATA ${lib}_str_functs[]=
388         {
389 EOF
390         # Add each function code: if a function name is found then use it.
391         foreach $i (@function) {
392                 my $fn;
393                 $i =~ /^${lib}_F_(\S+)$/;
394                 $fn = $1;
395                 if(exists $ftrans{$fn}) {
396                         $fn = $ftrans{$fn};
397                 }
398                 print OUT "{ERR_PACK(0,$i,0),\t\"$fn\"},\n";
399         }
400         print OUT <<"EOF";
401 {0,NULL}
402         };
403
404 static ERR_STRING_DATA ${lib}_str_reasons[]=
405         {
406 EOF
407         # Add each reason code.
408         foreach $i (@reasons) {
409                 my $rn;
410                 my $nspc = 0;
411                 if (exists $err_reason_strings{$i}) {
412                         $rn = $err_reason_strings{$i};
413                 } else {
414                         $i =~ /^${lib}_R_(\S+)$/;
415                         $rn = $1;
416                         $rn =~ tr/_[A-Z]/ [a-z]/;
417                 }
418                 $nspc = 40 - length($i) unless length($i) > 40;
419                 $nspc = " " x $nspc;
420                 print OUT "{${i}${nspc},\"$rn\"},\n";
421         }
422 if($static) {
423         print OUT <<"EOF";
424 {0,NULL}
425         };
426
427 #endif
428
429 void ERR_load_${lib}_strings(void)
430         {
431         static int init=1;
432
433         if (init)
434                 {
435                 init=0;
436 #ifndef OPENSSL_NO_ERR
437                 ERR_load_strings(ERR_LIB_${lib},${lib}_str_functs);
438                 ERR_load_strings(ERR_LIB_${lib},${lib}_str_reasons);
439 #endif
440
441                 }
442         }
443 EOF
444 } else {
445         print OUT <<"EOF";
446 {0,NULL}
447         };
448
449 #endif
450
451 #ifdef ${lib}_LIB_NAME
452 static ERR_STRING_DATA ${lib}_lib_name[]=
453         {
454 {0      ,${lib}_LIB_NAME},
455 {0,NULL}
456         };
457 #endif
458
459
460 int ${lib}_lib_error_code=0;
461
462 void ERR_load_${lib}_strings(void)
463         {
464         static int init=1;
465
466         if (${lib}_lib_error_code == 0)
467                 ${lib}_lib_error_code=ERR_get_next_error_library();
468
469         if (init)
470                 {
471                 init=0;
472 #ifndef OPENSSL_NO_ERR
473                 ERR_load_strings(${lib}_lib_error_code,${lib}_str_functs);
474                 ERR_load_strings(${lib}_lib_error_code,${lib}_str_reasons);
475 #endif
476
477 #ifdef ${lib}_LIB_NAME
478                 ${lib}_lib_name->error = ERR_PACK(${lib}_lib_error_code,0,0);
479                 ERR_load_strings(0,${lib}_lib_name);
480 #endif
481                 }
482         }
483
484 void ERR_${lib}_error(int function, int reason, char *file, int line)
485         {
486         if (${lib}_lib_error_code == 0)
487                 ${lib}_lib_error_code=ERR_get_next_error_library();
488         ERR_PUT_error(${lib}_lib_error_code,function,reason,file,line);
489         }
490 EOF
491
492 }
493
494         close OUT;
495         undef %err_reason_strings;
496 }
497
498 if($debug && defined(%notrans)) {
499         print STDERR "The following function codes were not translated:\n";
500         foreach(sort keys %notrans)
501         {
502                 print STDERR "$_\n";
503         }
504 }
505
506 # Make a list of unreferenced function and reason codes
507
508 foreach (keys %fcodes) {
509         push (@funref, $_) unless exists $ufcodes{$_};
510 }
511
512 foreach (keys %rcodes) {
513         push (@runref, $_) unless exists $urcodes{$_};
514 }
515
516 if($debug && defined(@funref) ) {
517         print STDERR "The following function codes were not referenced:\n";
518         foreach(sort @funref)
519         {
520                 print STDERR "$_\n";
521         }
522 }
523
524 if($debug && defined(@runref) ) {
525         print STDERR "The following reason codes were not referenced:\n";
526         foreach(sort @runref)
527         {
528                 print STDERR "$_\n";
529         }
530 }