1 #!/usr/bin/env perl !! 1 #!/usr/bin/perl 2 # SPDX-License-Identifier: GPL-2.0 2 # SPDX-License-Identifier: GPL-2.0 3 3 4 BEGIN { $Pod::Usage::Formatter = 'Pod::Text::T << 5 << 6 use strict; 4 use strict; 7 use warnings; !! 5 use Pod::Usage; 8 use utf8; << 9 use Pod::Usage qw(pod2usage); << 10 use Getopt::Long; 6 use Getopt::Long; 11 use File::Find; 7 use File::Find; 12 use IO::Handle; << 13 use Fcntl ':mode'; 8 use Fcntl ':mode'; 14 use Cwd 'abs_path'; << 15 use Data::Dumper; << 16 9 17 my $help = 0; !! 10 my $help; 18 my $hint = 0; !! 11 my $man; 19 my $man = 0; !! 12 my $debug; 20 my $debug = 0; << 21 my $enable_lineno = 0; << 22 my $show_warnings = 1; << 23 my $prefix="Documentation/ABI"; 13 my $prefix="Documentation/ABI"; 24 my $sysfs_prefix="/sys"; << 25 my $search_string; << 26 << 27 # Debug options << 28 my $dbg_what_parsing = 1; << 29 my $dbg_what_open = 2; << 30 my $dbg_dump_abi_structs = 4; << 31 my $dbg_undefined = 8; << 32 << 33 $Data::Dumper::Indent = 1; << 34 $Data::Dumper::Terse = 1; << 35 << 36 # << 37 # If true, assumes that the description is for << 38 # << 39 my $description_is_rst = 1; << 40 14 41 GetOptions( 15 GetOptions( 42 "debug=i" => \$debug, !! 16 "debug|d+" => \$debug, 43 "enable-lineno" => \$enable_lineno, << 44 "rst-source!" => \$description_is_rst, << 45 "dir=s" => \$prefix, 17 "dir=s" => \$prefix, 46 'help|?' => \$help, 18 'help|?' => \$help, 47 "show-hints" => \$hint, << 48 "search-string=s" => \$search_string, << 49 man => \$man 19 man => \$man 50 ) or pod2usage(2); 20 ) or pod2usage(2); 51 21 52 pod2usage(1) if $help; 22 pod2usage(1) if $help; 53 pod2usage(-exitstatus => 0, -noperldoc, -verbo !! 23 pod2usage(-exitstatus => 0, -verbose => 2) if $man; 54 24 55 pod2usage(2) if (scalar @ARGV < 1 || @ARGV > 2) 25 pod2usage(2) if (scalar @ARGV < 1 || @ARGV > 2); 56 26 57 my ($cmd, $arg) = @ARGV; 27 my ($cmd, $arg) = @ARGV; 58 28 59 pod2usage(2) if ($cmd ne "search" && $cmd ne " !! 29 pod2usage(2) if ($cmd ne "search" && $cmd ne "rest" && $cmd ne "validate"); 60 pod2usage(2) if ($cmd eq "search" && !$arg); 30 pod2usage(2) if ($cmd eq "search" && !$arg); 61 31 62 require Data::Dumper if ($debug & $dbg_dump_ab !! 32 require Data::Dumper if ($debug); 63 33 64 my %data; 34 my %data; 65 my %symbols; << 66 35 67 # 36 # 68 # Displays an error message, printing file nam 37 # Displays an error message, printing file name and line 69 # 38 # 70 sub parse_error($$$$) { 39 sub parse_error($$$$) { 71 my ($file, $ln, $msg, $data) = @_; 40 my ($file, $ln, $msg, $data) = @_; 72 41 73 return if (!$show_warnings); !! 42 print STDERR "file $file#$ln: $msg at\n\t$data"; 74 << 75 $data =~ s/\s+$/\n/; << 76 << 77 print STDERR "Warning: file $file#$ln: << 78 << 79 if ($data ne "") { << 80 print STDERR ". Line\n\t\t$dat << 81 } else { << 82 print STDERR "\n"; << 83 } << 84 } 43 } 85 44 86 # 45 # 87 # Parse an ABI file, storing its contents at % 46 # Parse an ABI file, storing its contents at %data 88 # 47 # 89 sub parse_abi { 48 sub parse_abi { 90 my $file = $File::Find::name; 49 my $file = $File::Find::name; 91 50 92 my $mode = (stat($file))[2]; 51 my $mode = (stat($file))[2]; 93 return if ($mode & S_IFDIR); 52 return if ($mode & S_IFDIR); 94 return if ($file =~ m,/README,); 53 return if ($file =~ m,/README,); 95 return if ($file =~ m,/\.,); << 96 return if ($file =~ m,\.(rej|org|orig| << 97 54 98 my $name = $file; 55 my $name = $file; 99 $name =~ s,.*/,,; 56 $name =~ s,.*/,,; 100 57 101 my $fn = $file; !! 58 my $nametag = "File $name"; 102 $fn =~ s,.*Documentation/ABI/,,; << 103 << 104 my $nametag = "File $fn"; << 105 $data{$nametag}->{what} = "File $name" 59 $data{$nametag}->{what} = "File $name"; 106 $data{$nametag}->{type} = "File"; 60 $data{$nametag}->{type} = "File"; 107 $data{$nametag}->{file} = $name; 61 $data{$nametag}->{file} = $name; 108 $data{$nametag}->{filepath} = $file; 62 $data{$nametag}->{filepath} = $file; 109 $data{$nametag}->{is_file} = 1; 63 $data{$nametag}->{is_file} = 1; 110 $data{$nametag}->{line_no} = 1; << 111 64 112 my $type = $file; 65 my $type = $file; 113 $type =~ s,.*/(.*)/.*,$1,; 66 $type =~ s,.*/(.*)/.*,$1,; 114 67 115 my $what; 68 my $what; 116 my $new_what; 69 my $new_what; 117 my $tag = ""; !! 70 my $tag; 118 my $ln; 71 my $ln; 119 my $xrefs; 72 my $xrefs; 120 my $space; 73 my $space; 121 my @labels; 74 my @labels; 122 my $label = ""; !! 75 my $label; 123 76 124 print STDERR "Opening $file\n" if ($de !! 77 print STDERR "Opening $file\n" if ($debug > 1); 125 open IN, $file; 78 open IN, $file; 126 while(<IN>) { 79 while(<IN>) { 127 $ln++; 80 $ln++; 128 if (m/^(\S+)(:\s*)(.*)/i) { 81 if (m/^(\S+)(:\s*)(.*)/i) { 129 my $new_tag = lc($1); 82 my $new_tag = lc($1); 130 my $sep = $2; 83 my $sep = $2; 131 my $content = $3; 84 my $content = $3; 132 85 133 if (!($new_tag =~ m/(w 86 if (!($new_tag =~ m/(what|where|date|kernelversion|contact|description|users)/)) { 134 if ($tag eq "d 87 if ($tag eq "description") { 135 # New 88 # New "tag" is actually part of 136 # desc 89 # description. Don't consider it a tag 137 $new_t 90 $new_tag = ""; 138 } elsif ($tag 91 } elsif ($tag ne "") { 139 parse_ 92 parse_error($file, $ln, "tag '$tag' is invalid", $_); 140 } 93 } 141 } 94 } 142 95 143 # Invalid, but it is a 96 # Invalid, but it is a common mistake 144 if ($new_tag eq "where 97 if ($new_tag eq "where") { 145 parse_error($f !! 98 parse_error($file, $ln, "tag 'Where' is invalid. Should be 'What:' instead", $_); 146 $new_tag = "wh 99 $new_tag = "what"; 147 } 100 } 148 101 149 if ($new_tag =~ m/what 102 if ($new_tag =~ m/what/) { 150 $space = ""; 103 $space = ""; 151 $content =~ s/ << 152 << 153 push @{$symbol << 154 << 155 if ($tag =~ m/ 104 if ($tag =~ m/what/) { 156 $what !! 105 $what .= ", " . $content; 157 } else { 106 } else { 158 if ($w !! 107 parse_error($file, $ln, "What '$what' doesn't have a description", "") if ($what && !$data{$what}->{description}); 159 << 160 << 161 << 162 << 163 << 164 } << 165 108 166 $what 109 $what = $content; 167 $label 110 $label = $content; 168 $new_w 111 $new_what = 1; 169 } 112 } 170 push @labels, 113 push @labels, [($content, $label)]; 171 $tag = $new_ta 114 $tag = $new_tag; 172 115 173 push @{$data{$ !! 116 push @{$data{$nametag}->{xrefs}}, [($content, $label)] if ($data{$nametag}->{what}); 174 next; 117 next; 175 } 118 } 176 119 177 if ($tag ne "" && $new 120 if ($tag ne "" && $new_tag) { 178 $tag = $new_ta 121 $tag = $new_tag; 179 122 180 if ($new_what) 123 if ($new_what) { 181 @{$dat !! 124 @{$data{$what}->{label}} = @labels if ($data{$nametag}->{what}); 182 @label 125 @labels = (); 183 $label 126 $label = ""; 184 $new_w 127 $new_what = 0; 185 128 186 $data{ 129 $data{$what}->{type} = $type; 187 if (!d !! 130 $data{$what}->{file} = $name; 188 !! 131 $data{$what}->{filepath} = $file; 189 !! 132 print STDERR "\twhat: $what\n" if ($debug > 1); 190 } else << 191 << 192 << 193 << 194 << 195 << 196 } << 197 print << 198 $data{ << 199 } else { << 200 $data{ << 201 } 133 } 202 134 203 if (!$what) { 135 if (!$what) { 204 parse_ 136 parse_error($file, $ln, "'What:' should come first:", $_); 205 next; 137 next; 206 } 138 } 207 if ($new_tag e !! 139 if ($tag eq "description") { 208 $sep = !! 140 next if ($content =~ m/^\s*$/); 209 $conte !! 141 if ($content =~ m/^(\s*)(.*)/) { 210 while !! 142 my $new_content = $2; 211 if ($c !! 143 $space = $new_tag . $sep . $1; 212 !! 144 while ($space =~ s/\t+/' ' x (length($&) * 8 - length($`) % 8)/e) {} 213 !! 145 $space =~ s/./ /g; 214 !! 146 $data{$what}->{$tag} .= "$new_content\n"; 215 << 216 } else << 217 << 218 } 147 } 219 << 220 } else { 148 } else { 221 $data{ 149 $data{$what}->{$tag} = $content; 222 } 150 } 223 next; 151 next; 224 } 152 } 225 } 153 } 226 154 227 # Store any contents before ta 155 # Store any contents before tags at the database 228 if (!$tag && $data{$nametag}-> 156 if (!$tag && $data{$nametag}->{what}) { 229 $data{$nametag}->{desc 157 $data{$nametag}->{description} .= $_; 230 next; 158 next; 231 } 159 } 232 160 233 if ($tag eq "description") { 161 if ($tag eq "description") { 234 my $content = $_; !! 162 if (!$data{$what}->{description}) { 235 while ($content =~ s/\ !! 163 next if (m/^\s*\n/); 236 if (m/^\s*\n/) { !! 164 if (m/^(\s*)(.*)/) { 237 $data{$what}-> << 238 next; << 239 } << 240 << 241 if (!defined($space)) << 242 # Preserve ini << 243 if ($content = << 244 $space 165 $space = $1; 245 $conte !! 166 while ($space =~ s/\t+/' ' x (length($&) * 8 - length($`) % 8)/e) {} >> 167 $data{$what}->{$tag} .= "$2\n"; 246 } 168 } 247 } else { 169 } else { >> 170 my $content = $_; >> 171 if (m/^\s*\n/) { >> 172 $data{$what}->{$tag} .= $content; >> 173 next; >> 174 } >> 175 >> 176 while ($content =~ s/\t+/' ' x (length($&) * 8 - length($`) % 8)/e) {} 248 $space = "" if 177 $space = "" if (!($content =~ s/^($space)//)); 249 } << 250 $data{$what}->{$tag} . << 251 178 >> 179 # Compress spaces with tabs >> 180 $content =~ s<^ {8}> <\t>; >> 181 $content =~ s<^ {1,7}\t> <\t>; >> 182 $content =~ s< {1,7}\t> <\t>; >> 183 $data{$what}->{$tag} .= $content; >> 184 } 252 next; 185 next; 253 } 186 } 254 if (m/^\s*(.*)/) { 187 if (m/^\s*(.*)/) { 255 $data{$what}->{$tag} . 188 $data{$what}->{$tag} .= "\n$1"; 256 $data{$what}->{$tag} = 189 $data{$what}->{$tag} =~ s/\n+$//; 257 next; 190 next; 258 } 191 } 259 192 260 # Everything else is error 193 # Everything else is error 261 parse_error($file, $ln, "Unexp !! 194 parse_error($file, $ln, "Unexpected line:", $_); 262 } << 263 $data{$nametag}->{description} =~ s/^\ << 264 if ($what) { << 265 parse_error($file, $ln, "What << 266 << 267 foreach my $w(split /\xac/,$wh << 268 $symbols{$w}->{xref} = << 269 }; << 270 } 195 } >> 196 $data{$nametag}->{description} =~ s/^\n+//; 271 close IN; 197 close IN; 272 } 198 } 273 199 274 sub create_labels { !! 200 # 275 my %labels; !! 201 # Outputs the book on ReST format >> 202 # >> 203 >> 204 my %labels; >> 205 >> 206 sub output_rest { >> 207 foreach my $what (sort { >> 208 ($data{$a}->{type} eq "File") cmp ($data{$b}->{type} eq "File") || >> 209 $a cmp $b >> 210 } keys %data) { >> 211 my $type = $data{$what}->{type}; >> 212 my $file = $data{$what}->{file}; >> 213 my $filepath = $data{$what}->{filepath}; >> 214 >> 215 my $w = $what; >> 216 $w =~ s/([\(\)\_\-\*\=\^\~\\])/\\$1/g; 276 217 277 foreach my $what (keys %data) { << 278 next if ($data{$what}->{file} << 279 218 280 foreach my $p (@{$data{$what}- !! 219 foreach my $p (@{$data{$what}->{label}}) { 281 my ($content, $label) 220 my ($content, $label) = @{$p}; 282 $label = "abi_" . $lab 221 $label = "abi_" . $label . " "; 283 $label =~ tr/A-Z/a-z/; 222 $label =~ tr/A-Z/a-z/; 284 223 285 # Convert special char 224 # Convert special chars to "_" 286 $label =~s/([\x00-\x2f 225 $label =~s/([\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\xff])/_/g; 287 $label =~ s,_+,_,g; 226 $label =~ s,_+,_,g; 288 $label =~ s,_$,,; 227 $label =~ s,_$,,; 289 228 290 # Avoid duplicated lab 229 # Avoid duplicated labels 291 while (defined($labels 230 while (defined($labels{$label})) { 292 my @chars = ("A".. 231 my @chars = ("A".."Z", "a".."z"); 293 $label .= $chars[r 232 $label .= $chars[rand @chars]; 294 } 233 } 295 $labels{$label} = 1; 234 $labels{$label} = 1; 296 235 297 $data{$what}->{label} !! 236 $data{$what}->{label} .= $label; >> 237 >> 238 printf ".. _%s:\n\n", $label; 298 239 299 # only one label is en 240 # only one label is enough 300 last; 241 last; 301 } 242 } 302 } << 303 } << 304 243 305 # << 306 # Outputs the book on ReST format << 307 # << 308 244 309 # \b doesn't work well with paths. So, we need !! 245 $filepath =~ s,.*/(.*/.*),\1,;; 310 # Boundaries are punct characters, spaces and !! 246 $filepath =~ s,[/\-],_,g;; 311 my $start = qr {(^|\s|\() }x; !! 247 my $fileref = "abi_file_".$filepath; 312 my $bondary = qr { ([,.:;\)\s]|\z) }x; << 313 my $xref_match = qr { $start(\/(sys|config|pro << 314 my $symbols = qr { ([\x01-\x08\x0e-\x1f\x21-\x << 315 248 316 sub output_rest { !! 249 if ($type eq "File") { 317 create_labels(); !! 250 my $bar = $w; 318 !! 251 $bar =~ s/./-/g; 319 my $part = ""; << 320 << 321 foreach my $what (sort { << 322 ($data{$a}->{t << 323 $a cmp $b << 324 } keys %data) { << 325 my $type = $data{$what}->{type << 326 << 327 my @file = split / /, $data{$w << 328 my @filepath = split / /, $dat << 329 << 330 if ($enable_lineno) { << 331 printf ".. LINENO %s%s << 332 $prefix, $file[ << 333 $data{$what}->{ << 334 } << 335 << 336 my $w = $what; << 337 << 338 if ($type ne "File") { << 339 my $cur_part = $what; << 340 if ($what =~ '/') { << 341 if ($what =~ m << 342 $cur_p << 343 $cur_p << 344 } << 345 } << 346 << 347 if ($cur_part ne "" && << 348 $part = $cur_part; << 349 my $bar = $part; << 350 $bar =~ s/./-/g; << 351 print "$part\n$bar << 352 } << 353 252 354 printf ".. _%s:\n\n", !! 253 print ".. _$fileref:\n\n"; >> 254 print "$w\n$bar\n\n"; >> 255 } else { >> 256 my @names = split /\s*,\s*/,$w; 355 257 356 my @names = split /\xa << 357 my $len = 0; 258 my $len = 0; 358 259 359 foreach my $name (@nam 260 foreach my $name (@names) { 360 $name =~ s/$sy << 361 $name = "**$na << 362 $len = length( 261 $len = length($name) if (length($name) > $len); 363 } 262 } 364 263 >> 264 print "What:\n\n"; >> 265 365 print "+-" . "-" x $le 266 print "+-" . "-" x $len . "-+\n"; 366 foreach my $name (@nam 267 foreach my $name (@names) { 367 printf "| %s", 268 printf "| %s", $name . " " x ($len - length($name)) . " |\n"; 368 print "+-" . " 269 print "+-" . "-" x $len . "-+\n"; 369 } 270 } 370 << 371 print "\n"; 271 print "\n"; 372 } 272 } 373 273 374 for (my $i = 0; $i < scalar(@f !! 274 print "Defined on file :ref:`$file <$fileref>`\n\n" if ($type ne "File"); 375 my $path = $filepath[$ << 376 my $f = $file[$i]; << 377 << 378 $path =~ s,.*/(.*/.*), << 379 $path =~ s,[/\-],_,g;; << 380 my $fileref = "abi_fil << 381 275 382 if ($type eq "File") { !! 276 my $desc = $data{$what}->{description}; 383 print ".. _$fi !! 277 $desc =~ s/^\s+//; 384 } else { << 385 print "Defined << 386 } << 387 } << 388 << 389 if ($type eq "File") { << 390 my $bar = $w; << 391 $bar =~ s/./-/g; << 392 print "$w\n$bar\n\n"; << 393 } << 394 278 395 my $desc = ""; !! 279 # Remove title markups from the description, as they won't work 396 $desc = $data{$what}->{descrip !! 280 $desc =~ s/\n[\-\*\=\^\~]+\n/\n/g; 397 $desc =~ s/\s+$/\n/; << 398 281 399 if (!($desc =~ /^\s*$/)) { 282 if (!($desc =~ /^\s*$/)) { 400 if ($description_is_rs !! 283 if ($desc =~ m/\:\n/ || $desc =~ m/\n[\t ]+/ || $desc =~ m/[\x00-\x08\x0b-\x1f\x7b-\xff]/) { 401 # Remove title !! 284 # put everything inside a code block 402 # Having title !! 285 $desc =~ s/\n/\n /g; 403 # care would b << 404 # level order << 405 $desc =~ s/\n[ << 406 << 407 # Enrich text << 408 << 409 my $new_desc = << 410 my $init_inden << 411 my $literal_in << 412 << 413 open(my $fh, " << 414 while (my $d = << 415 my $in << 416 my $sp << 417 $init_ << 418 if ($l << 419 << 420 << 421 << 422 << 423 << 424 << 425 } else << 426 << 427 << 428 << 429 } << 430 << 431 $d =~ << 432 << 433 my @ma << 434 foreac << 435 << 436 << 437 << 438 << 439 << 440 << 441 } << 442 << 443 # Seek << 444 @match << 445 << 446 foreac << 447 << 448 << 449 << 450 << 451 << 452 << 453 << 454 << 455 << 456 } << 457 $new_d << 458 } << 459 close $fh; << 460 << 461 286 462 print "$new_de !! 287 print "::\n\n"; >> 288 print " $desc\n\n"; 463 } else { 289 } else { 464 $desc =~ s/^\s !! 290 # Escape any special chars from description >> 291 $desc =~s/([\x00-\x08\x0b-\x1f\x21-\x2a\x2d\x2f\x3c-\x40\x5c\x5e-\x60\x7b-\xff])/\\$1/g; 465 292 466 # Remove title !! 293 print "$desc\n\n"; 467 $desc =~ s/\n[ << 468 << 469 if ($desc =~ m << 470 # put << 471 $desc << 472 << 473 print << 474 print << 475 } else { << 476 # Esca << 477 $desc << 478 print << 479 } << 480 } 294 } 481 } else { 295 } else { 482 print "DESCRIPTION MIS 296 print "DESCRIPTION MISSING for $what\n\n" if (!$data{$what}->{is_file}); 483 } 297 } 484 298 485 if ($data{$what}->{symbols}) { !! 299 if ($data{$what}->{xrefs}) { 486 printf "Has the follow 300 printf "Has the following ABI:\n\n"; 487 301 488 foreach my $content(@{ !! 302 foreach my $p(@{$data{$what}->{xrefs}}) { 489 my $label = $d !! 303 my ($content, $label) = @{$p}; >> 304 $label = "abi_" . $label . " "; >> 305 $label =~ tr/A-Z/a-z/; >> 306 >> 307 # Convert special chars to "_" >> 308 $label =~s/([\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\xff])/_/g; >> 309 $label =~ s,_+,_,g; >> 310 $label =~ s,_$,,; 490 311 491 # Escape speci 312 # Escape special chars from content 492 $content =~s/( 313 $content =~s/([\x00-\x1f\x21-\x2f\x3a-\x40\x7b-\xff])/\\$1/g; 493 314 494 print "- :ref: 315 print "- :ref:`$content <$label>`\n\n"; 495 } 316 } 496 } 317 } 497 << 498 if (defined($data{$what}->{use << 499 my $users = $data{$wha << 500 << 501 $users =~ s/\n/\n\t/g; << 502 printf "Users:\n\t%s\n << 503 } << 504 << 505 } 318 } 506 } 319 } 507 320 508 # 321 # 509 # Searches for ABI symbols 322 # Searches for ABI symbols 510 # 323 # 511 sub search_symbols { 324 sub search_symbols { 512 foreach my $what (sort keys %data) { 325 foreach my $what (sort keys %data) { 513 next if (!($what =~ m/($arg)/) 326 next if (!($what =~ m/($arg)/)); 514 327 515 my $type = $data{$what}->{type 328 my $type = $data{$what}->{type}; 516 next if ($type eq "File"); 329 next if ($type eq "File"); 517 330 518 my $file = $data{$what}->{file 331 my $file = $data{$what}->{filepath}; 519 332 520 $what =~ s/\xac/, /g; << 521 my $bar = $what; 333 my $bar = $what; 522 $bar =~ s/./-/g; 334 $bar =~ s/./-/g; 523 335 524 print "\n$what\n$bar\n\n"; 336 print "\n$what\n$bar\n\n"; 525 337 526 my $kernelversion = $data{$wha !! 338 my $kernelversion = $data{$what}->{kernelversion}; 527 my $contact = $data{$what}->{c !! 339 my $contact = $data{$what}->{contact}; 528 my $users = $data{$what}->{use !! 340 my $users = $data{$what}->{users}; 529 my $date = $data{$what}->{date !! 341 my $date = $data{$what}->{date}; 530 my $desc = $data{$what}->{desc !! 342 my $desc = $data{$what}->{description}; 531 !! 343 $kernelversion =~ s/^\s+//; 532 $kernelversion =~ s/^\s+// if !! 344 $contact =~ s/^\s+//; 533 $contact =~ s/^\s+// if ($cont !! 345 $users =~ s/^\s+//; 534 if ($users) { !! 346 $users =~ s/\n//g; 535 $users =~ s/^\s+//; !! 347 $date =~ s/^\s+//; 536 $users =~ s/\n//g; !! 348 $desc =~ s/^\s+//; 537 } << 538 $date =~ s/^\s+// if ($date); << 539 $desc =~ s/^\s+// if ($desc); << 540 349 541 printf "Kernel version:\t\t%s\ 350 printf "Kernel version:\t\t%s\n", $kernelversion if ($kernelversion); 542 printf "Date:\t\t\t%s\n", $dat 351 printf "Date:\t\t\t%s\n", $date if ($date); 543 printf "Contact:\t\t%s\n", $co 352 printf "Contact:\t\t%s\n", $contact if ($contact); 544 printf "Users:\t\t\t%s\n", $us 353 printf "Users:\t\t\t%s\n", $users if ($users); 545 print "Defined on file(s):\t$f !! 354 print "Defined on file:\t$file\n\n"; 546 print "Description:\n\n$desc"; 355 print "Description:\n\n$desc"; 547 } 356 } 548 } 357 } 549 358 550 # Exclude /sys/kernel/debug and /sys/kernel/tr << 551 sub dont_parse_special_attributes { << 552 if (($File::Find::dir =~ m,^/sys/kerne << 553 return grep {!/(debug|tracing) << 554 } << 555 << 556 if (($File::Find::dir =~ m,^/sys/fs,)) << 557 return grep {!/(pstore|bpf|fus << 558 } << 559 359 560 return @_ << 561 } << 562 << 563 my %leaf; << 564 my %aliases; << 565 my @files; << 566 my %root; << 567 << 568 sub graph_add_file { << 569 my $file = shift; << 570 my $type = shift; << 571 << 572 my $dir = $file; << 573 $dir =~ s,^(.*/).*,$1,; << 574 $file =~ s,.*/,,; << 575 << 576 my $name; << 577 my $file_ref = \%root; << 578 foreach my $edge(split "/", $dir) { << 579 $name .= "$edge/"; << 580 if (!defined ${$file_ref}{$edg << 581 ${$file_ref}{$edge} = << 582 } << 583 $file_ref = \%{$$file_ref{$edg << 584 ${$file_ref}{"__name"} = [ $na << 585 } << 586 $name .= "$file"; << 587 ${$file_ref}{$file} = { << 588 "__name" => [ $name ] << 589 }; << 590 << 591 return \%{$$file_ref{$file}}; << 592 } << 593 << 594 sub graph_add_link { << 595 my $file = shift; << 596 my $link = shift; << 597 << 598 # Traverse graph to find the reference << 599 my $file_ref = \%root; << 600 foreach my $edge(split "/", $file) { << 601 $file_ref = \%{$$file_ref{$edg << 602 } << 603 << 604 # do a BFS << 605 << 606 my @queue; << 607 my %seen; << 608 my $st; << 609 << 610 push @queue, $file_ref; << 611 $seen{$start}++; << 612 << 613 while (@queue) { << 614 my $v = shift @queue; << 615 my @child = keys(%{$v}); << 616 << 617 foreach my $c(@child) { << 618 next if $seen{$$v{$c}} << 619 next if ($c eq "__name << 620 << 621 if (!defined($$v{$c}{" << 622 printf STDERR << 623 print STDERR D << 624 exit; << 625 } << 626 << 627 # Add new name << 628 my $name = @{$$v{$c}{" << 629 if ($name =~ s#^$file/ << 630 push @{$$v{$c} << 631 } << 632 # Add child to the que << 633 push @queue, $$v{$c}; << 634 $seen{$c}++; << 635 } << 636 } << 637 } << 638 << 639 my $escape_symbols = qr { ([\x01-\x08\x0e-\x1f << 640 sub parse_existing_sysfs { << 641 my $file = $File::Find::name; << 642 << 643 my $mode = (lstat($file))[2]; << 644 my $abs_file = abs_path($file); << 645 << 646 my @tmp; << 647 push @tmp, $file; << 648 push @tmp, $abs_file if ($abs_file ne << 649 << 650 foreach my $f(@tmp) { << 651 # Ignore cgroup, as this is bi << 652 return if ($f =~ m#^/sys/fs/cg << 653 << 654 # Ignore firmware as it is doc << 655 # Either ACPI or under Documen << 656 return if ($f =~ m#^/sys/firmw << 657 << 658 # Ignore some sysfs nodes that << 659 return if ($f =~ m#/sections|n << 660 << 661 # Would need to check at << 662 # Documentation/admin-guide/ke << 663 # is not easily parseable. << 664 return if ($f =~ m#/parameters << 665 } << 666 << 667 if (S_ISLNK($mode)) { << 668 $aliases{$file} = $abs_file; << 669 return; << 670 } << 671 << 672 return if (S_ISDIR($mode)); << 673 << 674 # Trivial: file is defined exactly the << 675 return if (defined($data{$file})); << 676 return if (defined($data{$abs_file})); << 677 << 678 push @files, graph_add_file($abs_file, << 679 } << 680 << 681 sub get_leave($) << 682 { << 683 my $what = shift; << 684 my $leave; << 685 << 686 my $l = $what; << 687 my $stop = 1; << 688 << 689 $leave = $l; << 690 $leave =~ s,/$,,; << 691 $leave =~ s,.*/,,; << 692 $leave =~ s/[\(\)]//g; << 693 << 694 # $leave is used to improve search per << 695 # check_undefined_symbols, as the algo << 696 # for a small number of "what". It als << 697 # hint about a leave with the same nam << 698 # However, there are a few occurences << 699 # either a wildcard or a number. Just << 700 # altogether. << 701 if ($leave =~ m/\.\*/ || $leave eq "" << 702 $leave = "others"; << 703 } << 704 << 705 return $leave; << 706 } << 707 << 708 my @not_found; << 709 << 710 sub check_file($$) << 711 { << 712 my $file_ref = shift; << 713 my $names_ref = shift; << 714 my @names = @{$names_ref}; << 715 my $file = $names[0]; << 716 << 717 my $found_string; << 718 << 719 my $leave = get_leave($file); << 720 if (!defined($leaf{$leave})) { << 721 $leave = "others"; << 722 } << 723 my @expr = @{$leaf{$leave}->{expr}}; << 724 die ("\rmissing rules for $leave") if << 725 << 726 my $path = $file; << 727 $path =~ s,(.*/).*,$1,; << 728 << 729 if ($search_string) { << 730 return if (!($file =~ m#$searc << 731 $found_string = 1; << 732 } << 733 << 734 for (my $i = 0; $i < @names; $i++) { << 735 if ($found_string && $hint) { << 736 if (!$i) { << 737 print STDERR " << 738 } else { << 739 print STDERR " << 740 } << 741 } << 742 foreach my $re (@expr) { << 743 print STDERR "$names[$ << 744 if ($names[$i] =~ $re) << 745 return; << 746 } << 747 } << 748 } << 749 << 750 if ($leave ne "others") { << 751 my @expr = @{$leaf{"others"}-> << 752 for (my $i = 0; $i < @names; $ << 753 foreach my $re (@expr) << 754 print STDERR " << 755 if ($names[$i] << 756 return << 757 } << 758 } << 759 } << 760 } << 761 << 762 push @not_found, $file if (!$search_st << 763 << 764 if ($hint && (!$search_string || $foun << 765 my $what = $leaf{$leave}->{wha << 766 $what =~ s/\xac/\n\t/g; << 767 if ($leave ne "others") { << 768 print STDERR "\r mo << 769 } else { << 770 print STDERR "\r te << 771 } << 772 } << 773 } << 774 << 775 sub check_undefined_symbols { << 776 my $num_files = scalar @files; << 777 my $next_i = 0; << 778 my $start_time = times; << 779 << 780 @files = sort @files; << 781 << 782 my $last_time = $start_time; << 783 << 784 # When either debug or hint is enabled << 785 # progress, as the progress will be ov << 786 if ($hint || ($debug && $dbg_undefined << 787 $next_i = $num_files; << 788 } << 789 << 790 my $is_console; << 791 $is_console = 1 if (-t STDERR); << 792 << 793 for (my $i = 0; $i < $num_files; $i++) << 794 my $file_ref = $files[$i]; << 795 my @names = @{$$file_ref{"__na << 796 << 797 check_file($file_ref, \@names) << 798 << 799 my $cur_time = times; << 800 << 801 if ($i == $next_i || $cur_time << 802 my $percent = $i * 100 << 803 << 804 my $tm = $cur_time - $ << 805 my $time = sprintf "%d << 806 << 807 printf STDERR "\33[2K\ << 808 printf STDERR "%s: pro << 809 printf STDERR "\n", if << 810 STDERR->flush(); << 811 << 812 $next_i = int (($perce << 813 $last_time = $cur_time << 814 } << 815 } << 816 << 817 my $cur_time = times; << 818 my $tm = $cur_time - $start_time; << 819 my $time = sprintf "%d:%02d", int($tm) << 820 << 821 printf STDERR "\33[2K\r", if ($is_cons << 822 printf STDERR "%s: processing sysfs fi << 823 << 824 foreach my $file (@not_found) { << 825 print "$file not found.\n"; << 826 } << 827 } << 828 << 829 sub undefined_symbols { << 830 print STDERR "Reading $sysfs_prefix di << 831 find({ << 832 wanted =>\&parse_existing_sysf << 833 preprocess =>\&dont_parse_spec << 834 no_chdir => 1 << 835 }, $sysfs_prefix); << 836 print STDERR "done.\n"; << 837 << 838 $leaf{"others"}->{what} = ""; << 839 << 840 print STDERR "Converting ABI What fiel << 841 foreach my $w (sort keys %data) { << 842 foreach my $what (split /\xac/ << 843 next if (!($what =~ m/ << 844 << 845 # Convert what into re << 846 << 847 # Escape dot character << 848 $what =~ s/\./\xf6/g; << 849 << 850 # Temporarily change [ << 851 $what =~ s/\[0\-9\]\+/ << 852 << 853 # Temporarily change [ << 854 $what =~ s/\[0\-\d+\]/ << 855 $what =~ s/\[(\d+)\]/\ << 856 << 857 # Temporarily change [ << 858 $what =~ s/\[(\d)\-(\d << 859 << 860 # Handle multiple opti << 861 $what =~ s/[\{\<\[]([\ << 862 << 863 # Handle wildcards << 864 $what =~ s,\*,.*,g; << 865 $what =~ s,/\xf6..,/.* << 866 $what =~ s/\<[^\>]+\>/ << 867 $what =~ s/\{[^\}]+\}/ << 868 $what =~ s/\[[^\]]+\]/ << 869 << 870 $what =~ s/[XYZ]/.*/g; << 871 << 872 # Recover [0-9] type o << 873 $what =~ s/\xf4/[/g; << 874 $what =~ s/\xf5/]/g; << 875 << 876 # Remove duplicated sp << 877 $what =~ s/\s+/ /g; << 878 << 879 # Special case: this A << 880 $what =~ s/sqrt\(x^2\+ << 881 << 882 # Special case: drop c << 883 # What: foo = <s << 884 # (this happens on a f << 885 $what =~ s,\s*\=.*$,,; << 886 << 887 # Escape all other sym << 888 $what =~ s/$escape_sym << 889 $what =~ s/\\\\/\\/g; << 890 $what =~ s/\\([\[\]\(\ << 891 $what =~ s/(\d+)\\(-\d << 892 << 893 $what =~ s/\xff/\\d+/g << 894 << 895 # Special case: IIO AB << 896 $what =~ s/sqrt(.*)/sq << 897 << 898 # Simplify regexes wit << 899 $what =~ s#(?:\.\*){2, << 900 # $what =~ s#\.\*/\.\*#. << 901 << 902 # Recover dot characte << 903 $what =~ s/\xf6/\./g; << 904 << 905 my $leave = get_leave( << 906 << 907 my $added = 0; << 908 foreach my $l (split / << 909 if (defined($l << 910 next i << 911 $leaf{ << 912 $added << 913 } else { << 914 $leaf{ << 915 $added << 916 } << 917 } << 918 if ($search_string && << 919 print STDERR " << 920 } << 921 << 922 } << 923 } << 924 # Compile regexes << 925 foreach my $l (sort keys %leaf) { << 926 my @expr; << 927 foreach my $w(sort split /\xac << 928 push @expr, qr /^$w$/; << 929 } << 930 $leaf{$l}->{expr} = \@expr; << 931 } << 932 << 933 # Take links into account << 934 foreach my $link (sort keys %aliases) << 935 my $abs_file = $aliases{$link} << 936 graph_add_link($abs_file, $lin << 937 } << 938 print STDERR "done.\n"; << 939 << 940 check_undefined_symbols; << 941 } << 942 << 943 # Ensure that the prefix will always end with << 944 # While this is not needed for find, it makes << 945 # with --enable-lineno << 946 $prefix =~ s,/?$,/,; << 947 << 948 if ($cmd eq "undefined" || $cmd eq "search") { << 949 $show_warnings = 0; << 950 } << 951 # 360 # 952 # Parses all ABI files located at $prefix dir 361 # Parses all ABI files located at $prefix dir 953 # 362 # 954 find({wanted =>\&parse_abi, no_chdir => 1}, $p 363 find({wanted =>\&parse_abi, no_chdir => 1}, $prefix); 955 364 956 print STDERR Data::Dumper->Dump([\%data], [qw( !! 365 print STDERR Data::Dumper->Dump([\%data], [qw(*data)]) if ($debug); 957 366 958 # 367 # 959 # Handles the command 368 # Handles the command 960 # 369 # 961 if ($cmd eq "undefined") { !! 370 if ($cmd eq "rest") { 962 undefined_symbols; !! 371 output_rest; 963 } elsif ($cmd eq "search") { 372 } elsif ($cmd eq "search") { 964 search_symbols; 373 search_symbols; 965 } else { << 966 if ($cmd eq "rest") { << 967 output_rest; << 968 } << 969 << 970 # Warn about duplicated ABI entries << 971 foreach my $what(sort keys %symbols) { << 972 my @files = @{$symbols{$what}- << 973 << 974 next if (scalar(@files) == 1); << 975 << 976 printf STDERR "Warning: $what << 977 scalar(@files); << 978 } << 979 } 374 } 980 375 >> 376 981 __END__ 377 __END__ 982 378 983 =head1 NAME 379 =head1 NAME 984 380 985 get_abi.pl - parse the Linux ABI files and pro !! 381 abi_book.pl - parse the Linux ABI files and produce a ReST book. 986 382 987 =head1 SYNOPSIS 383 =head1 SYNOPSIS 988 384 989 B<get_abi.pl> [--debug <level>] [--enable-line !! 385 B<abi_book.pl> [--debug] [--man] [--help] [--dir=<dir>] <COMAND> [<ARGUMENT>] 990 [--(no-)rst-source] [--dir=<dir << 991 [--search-string <regex>] << 992 <COMMAND> [<ARGUMENT>] << 993 386 994 Where B<COMMAND> can be: !! 387 Where <COMMAND> can be: 995 388 996 =over 8 389 =over 8 997 390 998 B<search> I<SEARCH_REGEX> - search for I<SEARC !! 391 B<search> [SEARCH_REGEX] - search for [SEARCH_REGEX] inside ABI 999 << 1000 B<rest> - output the ABI in << 1001 392 1002 B<validate> - validate the ABI !! 393 B<rest> - output the ABI in ReST markup language 1003 394 1004 B<undefined> - existing symbols !! 395 B<validate> - validate the ABI contents 1005 defined at Docume << 1006 396 1007 =back 397 =back 1008 398 1009 =head1 OPTIONS 399 =head1 OPTIONS 1010 400 1011 =over 8 401 =over 8 1012 402 1013 =item B<--dir> 403 =item B<--dir> 1014 404 1015 Changes the location of the ABI search. By de 405 Changes the location of the ABI search. By default, it uses 1016 the Documentation/ABI directory. 406 the Documentation/ABI directory. 1017 407 1018 =item B<--rst-source> and B<--no-rst-source> !! 408 =item B<--debug> 1019 << 1020 The input file may be using ReST syntax or no << 1021 selecting between a rst-compliant source ABI << 1022 plain text that may be violating ReST spec, s << 1023 logic (B<--no-rst-source>). << 1024 << 1025 =item B<--enable-lineno> << 1026 << 1027 Enable output of .. LINENO lines. << 1028 << 1029 =item B<--debug> I<debug level> << 1030 << 1031 Print debug information according with the le << 1032 following bitmask: << 1033 << 1034 - 1: Debug parsing What entries from ABI << 1035 - 2: Shows what files are opened from AB << 1036 - 4: Dump the structs used to store the << 1037 << 1038 =item B<--show-hints> << 1039 << 1040 Show hints about possible definitions for the << 1041 Used only when B<undefined>. << 1042 << 1043 =item B<--search-string> I<regex string> << 1044 409 1045 Show only occurences that match a search stri !! 410 Put the script in verbose mode, useful for debugging. Can be called multiple 1046 Used only when B<undefined>. !! 411 times, to increase verbosity. 1047 412 1048 =item B<--help> 413 =item B<--help> 1049 414 1050 Prints a brief help message and exits. 415 Prints a brief help message and exits. 1051 416 1052 =item B<--man> 417 =item B<--man> 1053 418 1054 Prints the manual page and exits. 419 Prints the manual page and exits. 1055 420 1056 =back 421 =back 1057 422 1058 =head1 DESCRIPTION 423 =head1 DESCRIPTION 1059 424 1060 Parse the Linux ABI files from ABI DIR (usual 425 Parse the Linux ABI files from ABI DIR (usually located at Documentation/ABI), 1061 allowing to search for ABI symbols or to prod 426 allowing to search for ABI symbols or to produce a ReST book containing 1062 the Linux ABI documentation. 427 the Linux ABI documentation. 1063 428 1064 =head1 EXAMPLES 429 =head1 EXAMPLES 1065 430 1066 Search for all stable symbols with the word " 431 Search for all stable symbols with the word "usb": 1067 432 1068 =over 8 433 =over 8 1069 434 1070 $ scripts/get_abi.pl search usb --dir Documen 435 $ scripts/get_abi.pl search usb --dir Documentation/ABI/stable 1071 436 1072 =back 437 =back 1073 438 1074 Search for all symbols that match the regex e 439 Search for all symbols that match the regex expression "usb.*cap": 1075 440 1076 =over 8 441 =over 8 1077 442 1078 $ scripts/get_abi.pl search usb.*cap 443 $ scripts/get_abi.pl search usb.*cap 1079 444 1080 =back 445 =back 1081 446 1082 Output all obsoleted symbols in ReST format 447 Output all obsoleted symbols in ReST format 1083 448 1084 =over 8 449 =over 8 1085 450 1086 $ scripts/get_abi.pl rest --dir Documentation 451 $ scripts/get_abi.pl rest --dir Documentation/ABI/obsolete 1087 452 1088 =back 453 =back 1089 454 1090 =head1 BUGS 455 =head1 BUGS 1091 456 1092 Report bugs to Mauro Carvalho Chehab <mchehab+ !! 457 Report bugs to Mauro Carvalho Chehab <mchehab+samsung@kernel.org> 1093 458 1094 =head1 COPYRIGHT 459 =head1 COPYRIGHT 1095 460 1096 Copyright (c) 2016-2021 by Mauro Carvalho Che< !! 461 Copyright (c) 2016-2019 by Mauro Carvalho Chehab <mchehab+samsung@kernel.org>. 1097 462 1098 License GPLv2: GNU GPL version 2 <http://gnu. 463 License GPLv2: GNU GPL version 2 <http://gnu.org/licenses/gpl.html>. 1099 464 1100 This is free software: you are free to change 465 This is free software: you are free to change and redistribute it. 1101 There is NO WARRANTY, to the extent permitted 466 There is NO WARRANTY, to the extent permitted by law. 1102 467 1103 =cut 468 =cut
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.