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