1#!/usr/bin/perl 2# SPDX-License-Identifier: GPL-2.0 3 4use strict; 5use Pod::Usage; 6use Getopt::Long; 7use File::Find; 8use Fcntl ':mode'; 9 10my $help; 11my $man; 12my $debug; 13my $arch; 14my $feat; 15my $prefix="Documentation/features"; 16 17GetOptions( 18 "debug|d+" => \$debug, 19 "dir=s" => \$prefix, 20 'help|?' => \$help, 21 'arch=s' => \$arch, 22 'feat=s' => \$feat, 23 man => \$man 24) or pod2usage(2); 25 26pod2usage(1) if $help; 27pod2usage(-exitstatus => 0, -verbose => 2) if $man; 28 29pod2usage(2) if (scalar @ARGV < 1 || @ARGV > 2); 30 31my ($cmd, $arg) = @ARGV; 32 33pod2usage(2) if ($cmd ne "current" && $cmd ne "rest" && $cmd ne "validate"); 34 35require Data::Dumper if ($debug); 36 37my %data; 38my %archs; 39 40# 41# Displays an error message, printing file name and line 42# 43sub parse_error($$$$) { 44 my ($file, $ln, $msg, $data) = @_; 45 46 $data =~ s/\s+$/\n/; 47 48 print STDERR "Warning: file $file#$ln:\n\t$msg"; 49 50 if ($data ne "") { 51 print STDERR ". Line\n\t\t$data"; 52 } else { 53 print STDERR "\n"; 54 } 55} 56 57# 58# Parse a features file, storing its contents at %data 59# 60 61my $h_name = "Feature"; 62my $h_kconfig = "Kconfig"; 63my $h_description = "Description"; 64my $h_subsys = "Subsystem"; 65my $h_status = "Status"; 66my $h_arch = "Architecture"; 67 68my $max_size_name = length($h_name); 69my $max_size_kconfig = length($h_kconfig); 70my $max_size_description = length($h_description); 71my $max_size_subsys = length($h_subsys); 72my $max_size_status = length($h_status); 73my $max_size_arch = length($h_arch); 74 75sub parse_feat { 76 my $file = $File::Find::name; 77 78 my $mode = (stat($file))[2]; 79 return if ($mode & S_IFDIR); 80 return if ($file =~ m,($prefix)/arch-support.txt,); 81 return if (!($file =~ m,arch-support.txt$,)); 82 83 my $subsys = ""; 84 $subsys = $2 if ( m,.*($prefix)/([^/]+).*,); 85 86 if (length($subsys) > $max_size_subsys) { 87 $max_size_subsys = length($subsys); 88 } 89 90 my $name; 91 my $kconfig; 92 my $description; 93 my $comments = ""; 94 my $last_status; 95 my $ln; 96 my %arch_table; 97 98 print STDERR "Opening $file\n" if ($debug > 1); 99 open IN, $file; 100 101 while(<IN>) { 102 $ln++; 103 104 if (m/^\#\s+Feature\s+name:\s*(.*\S)/) { 105 $name = $1; 106 if (length($name) > $max_size_name) { 107 $max_size_name = length($name); 108 } 109 next; 110 } 111 if (m/^\#\s+Kconfig:\s*(.*\S)/) { 112 $kconfig = $1; 113 if (length($kconfig) > $max_size_kconfig) { 114 $max_size_kconfig = length($kconfig); 115 } 116 next; 117 } 118 if (m/^\#\s+description:\s*(.*\S)/) { 119 $description = $1; 120 if (length($description) > $max_size_description) { 121 $max_size_description = length($description); 122 } 123 next; 124 } 125 next if (m/^\\s*$/); 126 next if (m/^\s*\-+\s*$/); 127 next if (m/^\s*\|\s*arch\s*\|\s*status\s*\|\s*$/); 128 129 if (m/^\#\s*(.*)/) { 130 $comments .= "$1\n"; 131 next; 132 } 133 if (m/^\s*\|\s*(\S+):\s*\|\s*(\S+)\s*\|\s*$/) { 134 my $a = $1; 135 my $status = $2; 136 137 if (length($status) > $max_size_status) { 138 $max_size_status = length($status); 139 } 140 if (length($a) > $max_size_arch) { 141 $max_size_arch = length($a); 142 } 143 144 $status = "---" if ($status =~ m/^\.\.$/); 145 146 $archs{$a} = 1; 147 $arch_table{$a} = $status; 148 next; 149 } 150 151 #Everything else is an error 152 parse_error($file, $ln, "line is invalid", $_); 153 } 154 close IN; 155 156 if (!$name) { 157 parse_error($file, $ln, "Feature name not found", ""); 158 return; 159 } 160 161 parse_error($file, $ln, "Subsystem not found", "") if (!$subsys); 162 parse_error($file, $ln, "Kconfig not found", "") if (!$kconfig); 163 parse_error($file, $ln, "Description not found", "") if (!$description); 164 165 if (!%arch_table) { 166 parse_error($file, $ln, "Architecture table not found", ""); 167 return; 168 } 169 170 $data{$name}->{where} = $file; 171 $data{$name}->{subsys} = $subsys; 172 $data{$name}->{kconfig} = $kconfig; 173 $data{$name}->{description} = $description; 174 $data{$name}->{comments} = $comments; 175 $data{$name}->{table} = \%arch_table; 176} 177 178# 179# Output feature(s) for a given architecture 180# 181sub output_arch_table { 182 my $title = "Feature status on $arch architecture"; 183 184 print "=" x length($title) . "\n"; 185 print "$title\n"; 186 print "=" x length($title) . "\n\n"; 187 188 print "=" x $max_size_subsys; 189 print " "; 190 print "=" x $max_size_name; 191 print " "; 192 print "=" x $max_size_kconfig; 193 print " "; 194 print "=" x $max_size_status; 195 print " "; 196 print "=" x $max_size_description; 197 print "\n"; 198 printf "%-${max_size_subsys}s ", $h_subsys; 199 printf "%-${max_size_name}s ", $h_name; 200 printf "%-${max_size_kconfig}s ", $h_kconfig; 201 printf "%-${max_size_status}s ", $h_status; 202 printf "%-${max_size_description}s\n", $h_description; 203 print "=" x $max_size_subsys; 204 print " "; 205 print "=" x $max_size_name; 206 print " "; 207 print "=" x $max_size_kconfig; 208 print " "; 209 print "=" x $max_size_status; 210 print " "; 211 print "=" x $max_size_description; 212 print "\n"; 213 214 foreach my $name (sort { 215 ($data{$a}->{subsys} cmp $data{$b}->{subsys}) || 216 ($data{$a}->{name} cmp $data{$b}->{name}) 217 } keys %data) { 218 next if ($feat && $name ne $feat); 219 220 my %arch_table = %{$data{$name}->{table}}; 221 printf "%-${max_size_subsys}s ", $data{$name}->{subsys}; 222 printf "%-${max_size_name}s ", $name; 223 printf "%-${max_size_kconfig}s ", $data{$name}->{kconfig}; 224 printf "%-${max_size_status}s ", $arch_table{$arch}; 225 printf "%-${max_size_description}s\n", $data{$name}->{description}; 226 } 227 228 print "=" x $max_size_subsys; 229 print " "; 230 print "=" x $max_size_name; 231 print " "; 232 print "=" x $max_size_kconfig; 233 print " "; 234 print "=" x $max_size_status; 235 print " "; 236 print "=" x $max_size_description; 237 print "\n"; 238} 239 240# 241# Output a feature on all architectures 242# 243sub output_feature { 244 my $title = "Feature $feat"; 245 246 print "=" x length($title) . "\n"; 247 print "$title\n"; 248 print "=" x length($title) . "\n\n"; 249 250 print ":Subsystem: $data{$feat}->{subsys} \n" if ($data{$feat}->{subsys}); 251 print ":Kconfig: $data{$feat}->{kconfig} \n" if ($data{$feat}->{kconfig}); 252 253 my $desc = $data{$feat}->{description}; 254 $desc =~ s/^([a-z])/\U$1/; 255 $desc =~ s/\.?\s*//; 256 print "\n$desc.\n\n"; 257 258 my $com = $data{$feat}->{comments}; 259 $com =~ s/^\s+//; 260 $com =~ s/\s+$//; 261 if ($com) { 262 print "Comments\n"; 263 print "--------\n\n"; 264 print "$com\n\n"; 265 } 266 267 print "=" x $max_size_arch; 268 print " "; 269 print "=" x $max_size_status; 270 print "\n"; 271 272 printf "%-${max_size_arch}s ", $h_arch; 273 printf "%-${max_size_status}s", $h_status . "\n"; 274 275 print "=" x $max_size_arch; 276 print " "; 277 print "=" x $max_size_status; 278 print "\n"; 279 280 my %arch_table = %{$data{$feat}->{table}}; 281 foreach my $arch (sort keys %arch_table) { 282 printf "%-${max_size_arch}s ", $arch; 283 printf "%-${max_size_status}s\n", $arch_table{$arch}; 284 } 285 286 print "=" x $max_size_arch; 287 print " "; 288 print "=" x $max_size_status; 289 print "\n"; 290} 291 292# 293# Output all features for all architectures 294# 295 296sub matrix_lines { 297 print "=" x $max_size_subsys; 298 print " "; 299 print "=" x $max_size_name; 300 print " "; 301 302 foreach my $arch (sort keys %archs) { 303 my $len = $max_size_status; 304 305 $len = length($arch) if ($len < length($arch)); 306 307 print "=" x $len; 308 print " "; 309 } 310 print "=" x $max_size_kconfig; 311 print " "; 312 print "=" x $max_size_description; 313 print "\n"; 314} 315 316sub output_matrix { 317 318 my $title = "Feature List (feature x architecture)"; 319 320 print "=" x length($title) . "\n"; 321 print "$title\n"; 322 print "=" x length($title) . "\n\n"; 323 324 matrix_lines; 325 326 printf "%-${max_size_subsys}s ", $h_subsys; 327 printf "%-${max_size_name}s ", $h_name; 328 329 foreach my $arch (sort keys %archs) { 330 printf "%-${max_size_status}s ", $arch; 331 } 332 printf "%-${max_size_kconfig}s ", $h_kconfig; 333 printf "%-${max_size_description}s\n", $h_description; 334 335 matrix_lines; 336 337 foreach my $name (sort { 338 ($data{$a}->{subsys} cmp $data{$b}->{subsys}) || 339 ($data{$a}->{name} cmp $data{$b}->{name}) 340 } keys %data) { 341 printf "%-${max_size_subsys}s ", $data{$name}->{subsys}; 342 printf "%-${max_size_name}s ", $name; 343 344 my %arch_table = %{$data{$name}->{table}}; 345 346 foreach my $arch (sort keys %arch_table) { 347 my $len = $max_size_status; 348 349 $len = length($arch) if ($len < length($arch)); 350 351 printf "%-${len}s ", $arch_table{$arch}; 352 } 353 printf "%-${max_size_kconfig}s ", $data{$name}->{kconfig}; 354 printf "%-${max_size_description}s\n", $data{$name}->{description}; 355 } 356 357 matrix_lines; 358} 359 360 361# 362# Parses all feature files located at $prefix dir 363# 364find({wanted =>\&parse_feat, no_chdir => 1}, $prefix); 365 366print STDERR Data::Dumper->Dump([\%data], [qw(*data)]) if ($debug); 367 368# 369# Handles the command 370# 371if ($cmd eq "current") { 372 $arch = qx(uname -m | sed 's/x86_64/x86/' | sed 's/i386/x86/'); 373 $arch =~s/\s+$//; 374} 375 376if ($cmd ne "validate") { 377 if ($arch) { 378 output_arch_table; 379 } elsif ($feat) { 380 output_feature; 381 } else { 382 output_matrix; 383 } 384} 385 386__END__ 387 388=head1 NAME 389 390get_feat.pl - parse the Linux Feature files and produce a ReST book. 391 392=head1 SYNOPSIS 393 394B<get_feat.pl> [--debug] [--man] [--help] [--dir=<dir>] 395 [--arch=<arch>] [--feat=<feature>] <COMAND> [<ARGUMENT>] 396 397Where <COMMAND> can be: 398 399=over 8 400 401B<current> - output features for this machine's architecture 402 403B<rest> - output features in ReST markup language 404 405B<validate> - validate the feature contents 406 407=back 408 409=head1 OPTIONS 410 411=over 8 412 413=item B<--arch> 414 415Output features for an specific architecture, optionally filtering for 416a single specific feature. 417 418=item B<--feat> 419 420Output features for a single specific architecture. 421 422=item B<--dir> 423 424Changes the location of the Feature files. By default, it uses 425the Documentation/features directory. 426 427=item B<--debug> 428 429Put the script in verbose mode, useful for debugging. Can be called multiple 430times, to increase verbosity. 431 432=item B<--help> 433 434Prints a brief help message and exits. 435 436=item B<--man> 437 438Prints the manual page and exits. 439 440=back 441 442=head1 DESCRIPTION 443 444Parse the Linux feature files from Documentation/features (by default), 445optionally producing results at ReST format. 446 447It supports output data per architecture, per feature or a 448feature x arch matrix. 449 450When used with B<rest> command, it will use either one of the tree formats: 451 452If neither B<--arch> or B<--feature> arguments are used, it will output a 453matrix with features per architecture. 454 455If B<--arch> argument is used, it will output the features availability for 456a given architecture. 457 458If B<--feat> argument is used, it will output the content of the feature 459file using ReStructured Text markup. 460 461=head1 BUGS 462 463Report bugs to Mauro Carvalho Chehab <[email protected]> 464 465=head1 COPYRIGHT 466 467Copyright (c) 2019 by Mauro Carvalho Chehab <[email protected]>. 468 469License GPLv2: GNU GPL version 2 <http://gnu.org/licenses/gpl.html>. 470 471This is free software: you are free to change and redistribute it. 472There is NO WARRANTY, to the extent permitted by law. 473 474=cut 475