1a8e1175bSopenharmony_ci#!/usr/bin/env perl
2a8e1175bSopenharmony_ci
3a8e1175bSopenharmony_ci# Generate error.c
4a8e1175bSopenharmony_ci#
5a8e1175bSopenharmony_ci# Usage: ./generate_errors.pl or scripts/generate_errors.pl without arguments,
6a8e1175bSopenharmony_ci# or generate_errors.pl include_dir data_dir error_file
7a8e1175bSopenharmony_ci#
8a8e1175bSopenharmony_ci# Copyright The Mbed TLS Contributors
9a8e1175bSopenharmony_ci# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
10a8e1175bSopenharmony_ci
11a8e1175bSopenharmony_ciuse strict;
12a8e1175bSopenharmony_ciuse warnings;
13a8e1175bSopenharmony_ci
14a8e1175bSopenharmony_cimy ($include_dir, $data_dir, $error_file);
15a8e1175bSopenharmony_ci
16a8e1175bSopenharmony_ciif( @ARGV ) {
17a8e1175bSopenharmony_ci    die "Invalid number of arguments" if scalar @ARGV != 3;
18a8e1175bSopenharmony_ci    ($include_dir, $data_dir, $error_file) = @ARGV;
19a8e1175bSopenharmony_ci
20a8e1175bSopenharmony_ci    -d $include_dir or die "No such directory: $include_dir\n";
21a8e1175bSopenharmony_ci    -d $data_dir or die "No such directory: $data_dir\n";
22a8e1175bSopenharmony_ci} else {
23a8e1175bSopenharmony_ci    $include_dir = 'include/mbedtls';
24a8e1175bSopenharmony_ci    $data_dir = 'scripts/data_files';
25a8e1175bSopenharmony_ci    $error_file = 'library/error.c';
26a8e1175bSopenharmony_ci
27a8e1175bSopenharmony_ci    unless( -d $include_dir && -d $data_dir ) {
28a8e1175bSopenharmony_ci        chdir '..' or die;
29a8e1175bSopenharmony_ci        -d $include_dir && -d $data_dir
30a8e1175bSopenharmony_ci            or die "Without arguments, must be run from root or scripts\n"
31a8e1175bSopenharmony_ci    }
32a8e1175bSopenharmony_ci}
33a8e1175bSopenharmony_ci
34a8e1175bSopenharmony_cimy $error_format_file = $data_dir.'/error.fmt';
35a8e1175bSopenharmony_ci
36a8e1175bSopenharmony_cimy @low_level_modules = qw( AES ARIA ASN1 BASE64 BIGNUM
37a8e1175bSopenharmony_ci                            CAMELLIA CCM CHACHA20 CHACHAPOLY CMAC CTR_DRBG DES
38a8e1175bSopenharmony_ci                            ENTROPY ERROR GCM HKDF HMAC_DRBG LMS MD5
39a8e1175bSopenharmony_ci                            NET OID PADLOCK PBKDF2 PLATFORM POLY1305 RIPEMD160
40a8e1175bSopenharmony_ci                            SHA1 SHA256 SHA512 SHA3 THREADING );
41a8e1175bSopenharmony_cimy @high_level_modules = qw( CIPHER DHM ECP MD
42a8e1175bSopenharmony_ci                             PEM PK PKCS12 PKCS5
43a8e1175bSopenharmony_ci                             RSA SSL X509 PKCS7 );
44a8e1175bSopenharmony_ci
45a8e1175bSopenharmony_ciundef $/;
46a8e1175bSopenharmony_ci
47a8e1175bSopenharmony_ciopen(FORMAT_FILE, '<:crlf', "$error_format_file") or die "Opening error format file '$error_format_file': $!";
48a8e1175bSopenharmony_cimy $error_format = <FORMAT_FILE>;
49a8e1175bSopenharmony_ciclose(FORMAT_FILE);
50a8e1175bSopenharmony_ci
51a8e1175bSopenharmony_cimy @files = glob qq("$include_dir/*.h");
52a8e1175bSopenharmony_cimy @necessary_include_files;
53a8e1175bSopenharmony_cimy @matches;
54a8e1175bSopenharmony_ciforeach my $file (@files) {
55a8e1175bSopenharmony_ci    open(FILE, '<:crlf', $file) or die("$0: $file: $!");
56a8e1175bSopenharmony_ci    my $content = <FILE>;
57a8e1175bSopenharmony_ci    close FILE;
58a8e1175bSopenharmony_ci    my $found = 0;
59a8e1175bSopenharmony_ci    while ($content =~ m[
60a8e1175bSopenharmony_ci            # Both the before-comment and the after-comment are optional.
61a8e1175bSopenharmony_ci            # Only the comment content is a regex capture group. The comment
62a8e1175bSopenharmony_ci            # start and end parts are outside the capture group.
63a8e1175bSopenharmony_ci            (?:/\*[*!](?!<)             # Doxygen before-comment start
64a8e1175bSopenharmony_ci                ((?:[^*]|\*+[^*/])*)    # $1: Comment content (no */ inside)
65a8e1175bSopenharmony_ci                \*/)?                   # Comment end
66a8e1175bSopenharmony_ci            \s*\#\s*define\s+(MBEDTLS_ERR_\w+)  # $2: name
67a8e1175bSopenharmony_ci            \s+\-(0[Xx][0-9A-Fa-f]+)\s*         # $3: value (without the sign)
68a8e1175bSopenharmony_ci            (?:/\*[*!]<                 # Doxygen after-comment start
69a8e1175bSopenharmony_ci                ((?:[^*]|\*+[^*/])*)    # $4: Comment content (no */ inside)
70a8e1175bSopenharmony_ci                \*/)?                   # Comment end
71a8e1175bSopenharmony_ci    ]gsx) {
72a8e1175bSopenharmony_ci        my ($before, $name, $value, $after) = ($1, $2, $3, $4);
73a8e1175bSopenharmony_ci        # Discard Doxygen comments that are coincidentally present before
74a8e1175bSopenharmony_ci        # an error definition but not attached to it. This is ad hoc, based
75a8e1175bSopenharmony_ci        # on what actually matters (or mattered at some point).
76a8e1175bSopenharmony_ci        undef $before if defined($before) && $before =~ /\s*\\name\s/s;
77a8e1175bSopenharmony_ci        die "Description neither before nor after $name in $file\n"
78a8e1175bSopenharmony_ci          if !defined($before) && !defined($after);
79a8e1175bSopenharmony_ci        die "Description both before and after $name in $file\n"
80a8e1175bSopenharmony_ci          if defined($before) && defined($after);
81a8e1175bSopenharmony_ci        my $description = (defined($before) ? $before : $after);
82a8e1175bSopenharmony_ci        $description =~ s/^\s+//;
83a8e1175bSopenharmony_ci        $description =~ s/\n( *\*)? */ /g;
84a8e1175bSopenharmony_ci        $description =~ s/\.?\s+$//;
85a8e1175bSopenharmony_ci        push @matches, [$name, $value, $description];
86a8e1175bSopenharmony_ci        ++$found;
87a8e1175bSopenharmony_ci    }
88a8e1175bSopenharmony_ci    if ($found) {
89a8e1175bSopenharmony_ci        my $include_name = $file;
90a8e1175bSopenharmony_ci        $include_name =~ s!.*/!!;
91a8e1175bSopenharmony_ci        push @necessary_include_files, $include_name;
92a8e1175bSopenharmony_ci    }
93a8e1175bSopenharmony_ci}
94a8e1175bSopenharmony_ci
95a8e1175bSopenharmony_cimy $ll_old_define = "";
96a8e1175bSopenharmony_cimy $hl_old_define = "";
97a8e1175bSopenharmony_ci
98a8e1175bSopenharmony_cimy $ll_code_check = "";
99a8e1175bSopenharmony_cimy $hl_code_check = "";
100a8e1175bSopenharmony_ci
101a8e1175bSopenharmony_cimy $headers = "";
102a8e1175bSopenharmony_cimy %included_headers;
103a8e1175bSopenharmony_ci
104a8e1175bSopenharmony_cimy %error_codes_seen;
105a8e1175bSopenharmony_ci
106a8e1175bSopenharmony_ciforeach my $match (@matches)
107a8e1175bSopenharmony_ci{
108a8e1175bSopenharmony_ci    my ($error_name, $error_code, $description) = @$match;
109a8e1175bSopenharmony_ci
110a8e1175bSopenharmony_ci    die "Duplicated error code: $error_code ($error_name)\n"
111a8e1175bSopenharmony_ci        if( $error_codes_seen{$error_code}++ );
112a8e1175bSopenharmony_ci
113a8e1175bSopenharmony_ci    $description =~ s/\\/\\\\/g;
114a8e1175bSopenharmony_ci
115a8e1175bSopenharmony_ci    my ($module_name) = $error_name =~ /^MBEDTLS_ERR_([^_]+)/;
116a8e1175bSopenharmony_ci
117a8e1175bSopenharmony_ci    # Fix faulty ones
118a8e1175bSopenharmony_ci    $module_name = "BIGNUM" if ($module_name eq "MPI");
119a8e1175bSopenharmony_ci    $module_name = "CTR_DRBG" if ($module_name eq "CTR");
120a8e1175bSopenharmony_ci    $module_name = "HMAC_DRBG" if ($module_name eq "HMAC");
121a8e1175bSopenharmony_ci
122a8e1175bSopenharmony_ci    my $define_name = $module_name;
123a8e1175bSopenharmony_ci    $define_name = "X509_USE,X509_CREATE" if ($define_name eq "X509");
124a8e1175bSopenharmony_ci    $define_name = "ASN1_PARSE" if ($define_name eq "ASN1");
125a8e1175bSopenharmony_ci    $define_name = "SSL_TLS" if ($define_name eq "SSL");
126a8e1175bSopenharmony_ci    $define_name = "PEM_PARSE,PEM_WRITE" if ($define_name eq "PEM");
127a8e1175bSopenharmony_ci    $define_name = "PKCS7" if ($define_name eq "PKCS7");
128a8e1175bSopenharmony_ci
129a8e1175bSopenharmony_ci    my $include_name = $module_name;
130a8e1175bSopenharmony_ci    $include_name =~ tr/A-Z/a-z/;
131a8e1175bSopenharmony_ci
132a8e1175bSopenharmony_ci    # Fix faulty ones
133a8e1175bSopenharmony_ci    $include_name = "net_sockets" if ($module_name eq "NET");
134a8e1175bSopenharmony_ci
135a8e1175bSopenharmony_ci    $included_headers{"${include_name}.h"} = $module_name;
136a8e1175bSopenharmony_ci
137a8e1175bSopenharmony_ci    my $found_ll = grep $_ eq $module_name, @low_level_modules;
138a8e1175bSopenharmony_ci    my $found_hl = grep $_ eq $module_name, @high_level_modules;
139a8e1175bSopenharmony_ci    if (!$found_ll && !$found_hl)
140a8e1175bSopenharmony_ci    {
141a8e1175bSopenharmony_ci        printf("Error: Do not know how to handle: $module_name\n");
142a8e1175bSopenharmony_ci        exit 1;
143a8e1175bSopenharmony_ci    }
144a8e1175bSopenharmony_ci
145a8e1175bSopenharmony_ci    my $code_check;
146a8e1175bSopenharmony_ci    my $old_define;
147a8e1175bSopenharmony_ci    my $white_space;
148a8e1175bSopenharmony_ci    my $first;
149a8e1175bSopenharmony_ci
150a8e1175bSopenharmony_ci    if ($found_ll)
151a8e1175bSopenharmony_ci    {
152a8e1175bSopenharmony_ci        $code_check = \$ll_code_check;
153a8e1175bSopenharmony_ci        $old_define = \$ll_old_define;
154a8e1175bSopenharmony_ci        $white_space = '        ';
155a8e1175bSopenharmony_ci    }
156a8e1175bSopenharmony_ci    else
157a8e1175bSopenharmony_ci    {
158a8e1175bSopenharmony_ci        $code_check = \$hl_code_check;
159a8e1175bSopenharmony_ci        $old_define = \$hl_old_define;
160a8e1175bSopenharmony_ci        $white_space = '        ';
161a8e1175bSopenharmony_ci    }
162a8e1175bSopenharmony_ci
163a8e1175bSopenharmony_ci    if ($define_name ne ${$old_define})
164a8e1175bSopenharmony_ci    {
165a8e1175bSopenharmony_ci        if (${$old_define} ne "")
166a8e1175bSopenharmony_ci        {
167a8e1175bSopenharmony_ci            ${$code_check} .= "#endif /* ";
168a8e1175bSopenharmony_ci            $first = 0;
169a8e1175bSopenharmony_ci            foreach my $dep (split(/,/, ${$old_define}))
170a8e1175bSopenharmony_ci            {
171a8e1175bSopenharmony_ci                ${$code_check} .= " || " if ($first++);
172a8e1175bSopenharmony_ci                ${$code_check} .= "MBEDTLS_${dep}_C";
173a8e1175bSopenharmony_ci            }
174a8e1175bSopenharmony_ci            ${$code_check} .= " */\n\n";
175a8e1175bSopenharmony_ci        }
176a8e1175bSopenharmony_ci
177a8e1175bSopenharmony_ci        ${$code_check} .= "#if ";
178a8e1175bSopenharmony_ci        $headers .= "#if " if ($include_name ne "");
179a8e1175bSopenharmony_ci        $first = 0;
180a8e1175bSopenharmony_ci        foreach my $dep (split(/,/, ${define_name}))
181a8e1175bSopenharmony_ci        {
182a8e1175bSopenharmony_ci            ${$code_check} .= " || " if ($first);
183a8e1175bSopenharmony_ci            $headers       .= " || " if ($first++);
184a8e1175bSopenharmony_ci
185a8e1175bSopenharmony_ci            ${$code_check} .= "defined(MBEDTLS_${dep}_C)";
186a8e1175bSopenharmony_ci            $headers       .= "defined(MBEDTLS_${dep}_C)" if
187a8e1175bSopenharmony_ci                                                    ($include_name ne "");
188a8e1175bSopenharmony_ci        }
189a8e1175bSopenharmony_ci        ${$code_check} .= "\n";
190a8e1175bSopenharmony_ci        $headers .= "\n#include \"mbedtls/${include_name}.h\"\n".
191a8e1175bSopenharmony_ci                    "#endif\n\n" if ($include_name ne "");
192a8e1175bSopenharmony_ci        ${$old_define} = $define_name;
193a8e1175bSopenharmony_ci    }
194a8e1175bSopenharmony_ci
195a8e1175bSopenharmony_ci    ${$code_check} .= "${white_space}case -($error_name):\n".
196a8e1175bSopenharmony_ci                      "${white_space}    return( \"$module_name - $description\" );\n"
197a8e1175bSopenharmony_ci};
198a8e1175bSopenharmony_ci
199a8e1175bSopenharmony_ciif ($ll_old_define ne "")
200a8e1175bSopenharmony_ci{
201a8e1175bSopenharmony_ci    $ll_code_check .= "#endif /* ";
202a8e1175bSopenharmony_ci    my $first = 0;
203a8e1175bSopenharmony_ci    foreach my $dep (split(/,/, $ll_old_define))
204a8e1175bSopenharmony_ci    {
205a8e1175bSopenharmony_ci        $ll_code_check .= " || " if ($first++);
206a8e1175bSopenharmony_ci        $ll_code_check .= "MBEDTLS_${dep}_C";
207a8e1175bSopenharmony_ci    }
208a8e1175bSopenharmony_ci    $ll_code_check .= " */\n";
209a8e1175bSopenharmony_ci}
210a8e1175bSopenharmony_ciif ($hl_old_define ne "")
211a8e1175bSopenharmony_ci{
212a8e1175bSopenharmony_ci    $hl_code_check .= "#endif /* ";
213a8e1175bSopenharmony_ci    my $first = 0;
214a8e1175bSopenharmony_ci    foreach my $dep (split(/,/, $hl_old_define))
215a8e1175bSopenharmony_ci    {
216a8e1175bSopenharmony_ci        $hl_code_check .= " || " if ($first++);
217a8e1175bSopenharmony_ci        $hl_code_check .= "MBEDTLS_${dep}_C";
218a8e1175bSopenharmony_ci    }
219a8e1175bSopenharmony_ci    $hl_code_check .= " */\n";
220a8e1175bSopenharmony_ci}
221a8e1175bSopenharmony_ci
222a8e1175bSopenharmony_ci$error_format =~ s/HEADER_INCLUDED\n/$headers/g;
223a8e1175bSopenharmony_ci$error_format =~ s/LOW_LEVEL_CODE_CHECKS\n/$ll_code_check/g;
224a8e1175bSopenharmony_ci$error_format =~ s/HIGH_LEVEL_CODE_CHECKS\n/$hl_code_check/g;
225a8e1175bSopenharmony_ci
226a8e1175bSopenharmony_ciopen(ERROR_FILE, ">$error_file") or die "Opening destination file '$error_file': $!";
227a8e1175bSopenharmony_ciprint ERROR_FILE $error_format;
228a8e1175bSopenharmony_ciclose(ERROR_FILE);
229a8e1175bSopenharmony_ci
230a8e1175bSopenharmony_cimy $errors = 0;
231a8e1175bSopenharmony_cifor my $include_name (@necessary_include_files)
232a8e1175bSopenharmony_ci{
233a8e1175bSopenharmony_ci    if (not $included_headers{$include_name})
234a8e1175bSopenharmony_ci    {
235a8e1175bSopenharmony_ci        print STDERR "The header file \"$include_name\" defines error codes but has not been included!\n";
236a8e1175bSopenharmony_ci        ++$errors;
237a8e1175bSopenharmony_ci    }
238a8e1175bSopenharmony_ci}
239a8e1175bSopenharmony_ci
240a8e1175bSopenharmony_ciexit !!$errors;
241