1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
|
#!/usr/bin/perl -w
# SPDX-License-Identifier: GPL-2.0-or-later
#
# Script to generate SMB1 error mapping tables.
#
# Copyright (C) 2026 KylinSoft Co., Ltd. All rights reserved.
# Author(s): Huiwen He <hehuiwen@kylinos.cn>
# ChenXiaoSong <chenxiaosong@kylinos.cn>
#
use strict;
if ($#ARGV != 1) {
print STDERR "Usage: $0 <in-file> <out-file>\n";
exit(2);
}
# Parse input parameters and extract filenames
my $in_file = $ARGV[0];
my $out_file = $ARGV[1];
my $input_name = (split m|/|, $in_file)[-1];
my $output_name = (split m|/|, $out_file)[-1];
my $script_name = (split m|/|, $0)[-1];
my @list = ();
my %seen = ();
my $current_class = "";
# Parse annotated entries from the input file
open(my $in, "<", $in_file) or die "Cannot open $in_file: $!";
if ($in_file =~ /nterr\.h$/) {
while (<$in>) {
# Handle backslash line continuation
$_ .= <$in> while s/\\\s*\n//;
# Match #define NT_STATUS_... followed by // CLASS, CODE or /* CLASS, CODE */
my $re = qr{^\s*#define\s+(NT_STATUS_[A-Za-z0-9_]+)\s+(.+?)\s*} .
qr{(?://\s*|/\*\s*)([A-Z0-9_]+)\s*,\s*([A-Za-z0-9_]+)};
if (/$re/) {
my ($name, $val_str, $class, $code) = ($1, $2, $3, $4);
# Skip duplicate macro names
next if $seen{$name}++;
# Clean up value string (remove parens, spaces)
$val_str =~ s/[\s\(\)]//g;
my $val = 0;
foreach my $part (split(/\|/, $val_str)) {
$val |= hex($part);
}
push @list, { val => $val, name => $name, class => $class, code => $code };
} elsif (/^\s*#define\s+NT_STATUS_.*(?:\/\/|\/\*)/) {
# Error if macro has a comment (// or /*) but fails mapping format
die "Error: Invalid mapping comment format in $in_file: $_";
}
}
} elsif ($in_file =~ /smberr\.h$/) {
while (<$in>) {
# Handle backslash line continuation
$_ .= <$in> while s/\\\s*\n//;
# Detect current error class from header comments (ERRDOS or ERRSRV)
if (/generated with the (\w+) error class/) {
$current_class = $1;
}
# Match #define ERR/Err_... <value> followed by // -POSIX_ERR or /* -POSIX_ERR */
if (/^\s*#define\s+((?:ERR|Err)[A-Za-z0-9_]+)\s+([0-9a-fA-FxX]+)\s*(?:\/\/|\/\*)\s*(-[A-Z0-9_]+)/) {
my ($name, $val_str, $error) = ($1, $2, $3);
my $val = ($val_str =~ /^0x/i) ? hex($val_str) : $val_str;
push @list, { val => $val, name => $name, error => $error, class => $current_class };
} elsif ($current_class && /^\s*#define\s+(?:ERR|Err).*?(?:\/\/|\/\*)/) {
# Error if macro has a comment (// or /*) but fails mapping format
die "Error: Invalid mapping comment format in $in_file: $_";
}
}
}
close($in);
# Fail if no entries were found to avoid broken builds
die "Error: No mapping entries found in $in_file\n" unless @list;
# Sort entries numerically by value
@list = sort { $a->{val} <=> $b->{val} } @list;
# Generate the C mapping table output file
open(my $out, ">", $out_file) or die "Cannot open $out_file: $!";
print $out "/* Autogenerated from $input_name by $script_name */\n\n";
if ($output_name eq "smb1_mapping_table.c") {
# Generate NT status -> DOS error mapping file
my $count = scalar @list;
my $full_names = "";
for (my $i = 0; $i < $count; $i++) {
my $e = $list[$i];
my $val = $e->{val};
$full_names .= $e->{name};
# Merge synonyms
if ($i < $count - 1 && $list[$i + 1]->{val} == $val) {
$full_names .= " or ";
next;
}
printf $out "\t{ %s, %s, 0x%08x, \"%s\" },\n", $e->{class}, $e->{code}, $val, $full_names;
$full_names = "";
}
} elsif ($output_name eq "smb1_err_dos_map.c" || $output_name eq "smb1_err_srv_map.c") {
# Generate SMB1 error -> POSIX error mapping file
# Filtered by exact output filename
my $filter = ($output_name eq "smb1_err_dos_map.c") ? "ERRDOS" : "ERRSRV";
foreach my $e (@list) {
if (!$filter || $e->{class} eq $filter) {
printf $out "\t{%s, %s},\n", $e->{name}, $e->{error};
}
}
} else {
die "Error: Unsupported output target: $output_name\n";
}
close($out);
|