1 #!/usr/bin/env perl 1 #!/usr/bin/env 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 warnings; 8 use utf8; 6 use utf8; 9 use Pod::Usage qw(pod2usage); !! 7 use Pod::Usage; 10 use Getopt::Long; 8 use Getopt::Long; 11 use File::Find; 9 use File::Find; 12 use IO::Handle; << 13 use Fcntl ':mode'; 10 use Fcntl ':mode'; 14 use Cwd 'abs_path'; << 15 use Data::Dumper; << 16 11 17 my $help = 0; 12 my $help = 0; 18 my $hint = 0; << 19 my $man = 0; 13 my $man = 0; 20 my $debug = 0; 14 my $debug = 0; 21 my $enable_lineno = 0; 15 my $enable_lineno = 0; 22 my $show_warnings = 1; << 23 my $prefix="Documentation/ABI"; 16 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 17 36 # 18 # 37 # If true, assumes that the description is for 19 # If true, assumes that the description is formatted with ReST 38 # 20 # 39 my $description_is_rst = 1; 21 my $description_is_rst = 1; 40 22 41 GetOptions( 23 GetOptions( 42 "debug=i" => \$debug, !! 24 "debug|d+" => \$debug, 43 "enable-lineno" => \$enable_lineno, 25 "enable-lineno" => \$enable_lineno, 44 "rst-source!" => \$description_is_rst, 26 "rst-source!" => \$description_is_rst, 45 "dir=s" => \$prefix, 27 "dir=s" => \$prefix, 46 'help|?' => \$help, 28 'help|?' => \$help, 47 "show-hints" => \$hint, << 48 "search-string=s" => \$search_string, << 49 man => \$man 29 man => \$man 50 ) or pod2usage(2); 30 ) or pod2usage(2); 51 31 52 pod2usage(1) if $help; 32 pod2usage(1) if $help; 53 pod2usage(-exitstatus => 0, -noperldoc, -verbo !! 33 pod2usage(-exitstatus => 0, -verbose => 2) if $man; 54 34 55 pod2usage(2) if (scalar @ARGV < 1 || @ARGV > 2) 35 pod2usage(2) if (scalar @ARGV < 1 || @ARGV > 2); 56 36 57 my ($cmd, $arg) = @ARGV; 37 my ($cmd, $arg) = @ARGV; 58 38 59 pod2usage(2) if ($cmd ne "search" && $cmd ne " !! 39 pod2usage(2) if ($cmd ne "search" && $cmd ne "rest" && $cmd ne "validate"); 60 pod2usage(2) if ($cmd eq "search" && !$arg); 40 pod2usage(2) if ($cmd eq "search" && !$arg); 61 41 62 require Data::Dumper if ($debug & $dbg_dump_ab !! 42 require Data::Dumper if ($debug); 63 43 64 my %data; 44 my %data; 65 my %symbols; 45 my %symbols; 66 46 67 # 47 # 68 # Displays an error message, printing file nam 48 # Displays an error message, printing file name and line 69 # 49 # 70 sub parse_error($$$$) { 50 sub parse_error($$$$) { 71 my ($file, $ln, $msg, $data) = @_; 51 my ($file, $ln, $msg, $data) = @_; 72 52 73 return if (!$show_warnings); << 74 << 75 $data =~ s/\s+$/\n/; 53 $data =~ s/\s+$/\n/; 76 54 77 print STDERR "Warning: file $file#$ln: 55 print STDERR "Warning: file $file#$ln:\n\t$msg"; 78 56 79 if ($data ne "") { 57 if ($data ne "") { 80 print STDERR ". Line\n\t\t$dat 58 print STDERR ". Line\n\t\t$data"; 81 } else { 59 } else { 82 print STDERR "\n"; 60 print STDERR "\n"; 83 } 61 } 84 } 62 } 85 63 86 # 64 # 87 # Parse an ABI file, storing its contents at % 65 # Parse an ABI file, storing its contents at %data 88 # 66 # 89 sub parse_abi { 67 sub parse_abi { 90 my $file = $File::Find::name; 68 my $file = $File::Find::name; 91 69 92 my $mode = (stat($file))[2]; 70 my $mode = (stat($file))[2]; 93 return if ($mode & S_IFDIR); 71 return if ($mode & S_IFDIR); 94 return if ($file =~ m,/README,); 72 return if ($file =~ m,/README,); 95 return if ($file =~ m,/\.,); << 96 return if ($file =~ m,\.(rej|org|orig| << 97 73 98 my $name = $file; 74 my $name = $file; 99 $name =~ s,.*/,,; 75 $name =~ s,.*/,,; 100 76 101 my $fn = $file; 77 my $fn = $file; 102 $fn =~ s,.*Documentation/ABI/,,; 78 $fn =~ s,.*Documentation/ABI/,,; 103 79 104 my $nametag = "File $fn"; 80 my $nametag = "File $fn"; 105 $data{$nametag}->{what} = "File $name" 81 $data{$nametag}->{what} = "File $name"; 106 $data{$nametag}->{type} = "File"; 82 $data{$nametag}->{type} = "File"; 107 $data{$nametag}->{file} = $name; 83 $data{$nametag}->{file} = $name; 108 $data{$nametag}->{filepath} = $file; 84 $data{$nametag}->{filepath} = $file; 109 $data{$nametag}->{is_file} = 1; 85 $data{$nametag}->{is_file} = 1; 110 $data{$nametag}->{line_no} = 1; 86 $data{$nametag}->{line_no} = 1; 111 87 112 my $type = $file; 88 my $type = $file; 113 $type =~ s,.*/(.*)/.*,$1,; 89 $type =~ s,.*/(.*)/.*,$1,; 114 90 115 my $what; 91 my $what; 116 my $new_what; 92 my $new_what; 117 my $tag = ""; 93 my $tag = ""; 118 my $ln; 94 my $ln; 119 my $xrefs; 95 my $xrefs; 120 my $space; 96 my $space; 121 my @labels; 97 my @labels; 122 my $label = ""; 98 my $label = ""; 123 99 124 print STDERR "Opening $file\n" if ($de !! 100 print STDERR "Opening $file\n" if ($debug > 1); 125 open IN, $file; 101 open IN, $file; 126 while(<IN>) { 102 while(<IN>) { 127 $ln++; 103 $ln++; 128 if (m/^(\S+)(:\s*)(.*)/i) { 104 if (m/^(\S+)(:\s*)(.*)/i) { 129 my $new_tag = lc($1); 105 my $new_tag = lc($1); 130 my $sep = $2; 106 my $sep = $2; 131 my $content = $3; 107 my $content = $3; 132 108 133 if (!($new_tag =~ m/(w 109 if (!($new_tag =~ m/(what|where|date|kernelversion|contact|description|users)/)) { 134 if ($tag eq "d 110 if ($tag eq "description") { 135 # New 111 # New "tag" is actually part of 136 # desc 112 # description. Don't consider it a tag 137 $new_t 113 $new_tag = ""; 138 } elsif ($tag 114 } elsif ($tag ne "") { 139 parse_ 115 parse_error($file, $ln, "tag '$tag' is invalid", $_); 140 } 116 } 141 } 117 } 142 118 143 # Invalid, but it is a 119 # Invalid, but it is a common mistake 144 if ($new_tag eq "where 120 if ($new_tag eq "where") { 145 parse_error($f 121 parse_error($file, $ln, "tag 'Where' is invalid. Should be 'What:' instead", ""); 146 $new_tag = "wh 122 $new_tag = "what"; 147 } 123 } 148 124 149 if ($new_tag =~ m/what 125 if ($new_tag =~ m/what/) { 150 $space = ""; 126 $space = ""; 151 $content =~ s/ 127 $content =~ s/[,.;]$//; 152 128 153 push @{$symbol 129 push @{$symbols{$content}->{file}}, " $file:" . ($ln - 1); 154 130 155 if ($tag =~ m/ 131 if ($tag =~ m/what/) { 156 $what !! 132 $what .= ", " . $content; 157 } else { 133 } else { 158 if ($w 134 if ($what) { 159 135 parse_error($file, $ln, "What '$what' doesn't have a description", "") if (!$data{$what}->{description}); 160 136 161 !! 137 foreach my $w(split /, /, $what) { 162 138 $symbols{$w}->{xref} = $what; 163 139 }; 164 } 140 } 165 141 166 $what 142 $what = $content; 167 $label 143 $label = $content; 168 $new_w 144 $new_what = 1; 169 } 145 } 170 push @labels, 146 push @labels, [($content, $label)]; 171 $tag = $new_ta 147 $tag = $new_tag; 172 148 173 push @{$data{$ 149 push @{$data{$nametag}->{symbols}}, $content if ($data{$nametag}->{what}); 174 next; 150 next; 175 } 151 } 176 152 177 if ($tag ne "" && $new 153 if ($tag ne "" && $new_tag) { 178 $tag = $new_ta 154 $tag = $new_tag; 179 155 180 if ($new_what) 156 if ($new_what) { 181 @{$dat 157 @{$data{$what}->{label_list}} = @labels if ($data{$nametag}->{what}); 182 @label 158 @labels = (); 183 $label 159 $label = ""; 184 $new_w 160 $new_what = 0; 185 161 186 $data{ 162 $data{$what}->{type} = $type; 187 if (!d 163 if (!defined($data{$what}->{file})) { 188 164 $data{$what}->{file} = $name; 189 165 $data{$what}->{filepath} = $file; 190 } else 166 } else { 191 << 192 167 if ($name ne $data{$what}->{file}) { 193 168 $data{$what}->{file} .= " " . $name; 194 169 $data{$what}->{filepath} .= " " . $file; 195 170 } 196 } 171 } 197 print !! 172 print STDERR "\twhat: $what\n" if ($debug > 1); 198 $data{ 173 $data{$what}->{line_no} = $ln; 199 } else { 174 } else { 200 $data{ 175 $data{$what}->{line_no} = $ln if (!defined($data{$what}->{line_no})); 201 } 176 } 202 177 203 if (!$what) { 178 if (!$what) { 204 parse_ 179 parse_error($file, $ln, "'What:' should come first:", $_); 205 next; 180 next; 206 } 181 } 207 if ($new_tag e 182 if ($new_tag eq "description") { 208 $sep = 183 $sep =~ s,:, ,; 209 $conte 184 $content = ' ' x length($new_tag) . $sep . $content; 210 while 185 while ($content =~ s/\t+/' ' x (length($&) * 8 - length($`) % 8)/e) {} 211 if ($c 186 if ($content =~ m/^(\s*)(\S.*)$/) { 212 187 # Preserve initial spaces for the first line 213 188 $space = $1; 214 189 $content = "$2\n"; 215 190 $data{$what}->{$tag} .= $content; 216 } else 191 } else { 217 192 undef($space); 218 } 193 } 219 194 220 } else { 195 } else { 221 $data{ 196 $data{$what}->{$tag} = $content; 222 } 197 } 223 next; 198 next; 224 } 199 } 225 } 200 } 226 201 227 # Store any contents before ta 202 # Store any contents before tags at the database 228 if (!$tag && $data{$nametag}-> 203 if (!$tag && $data{$nametag}->{what}) { 229 $data{$nametag}->{desc 204 $data{$nametag}->{description} .= $_; 230 next; 205 next; 231 } 206 } 232 207 233 if ($tag eq "description") { 208 if ($tag eq "description") { 234 my $content = $_; 209 my $content = $_; 235 while ($content =~ s/\ 210 while ($content =~ s/\t+/' ' x (length($&) * 8 - length($`) % 8)/e) {} 236 if (m/^\s*\n/) { 211 if (m/^\s*\n/) { 237 $data{$what}-> 212 $data{$what}->{$tag} .= "\n"; 238 next; 213 next; 239 } 214 } 240 215 241 if (!defined($space)) 216 if (!defined($space)) { 242 # Preserve ini 217 # Preserve initial spaces for the first line 243 if ($content = 218 if ($content =~ m/^(\s*)(\S.*)$/) { 244 $space 219 $space = $1; 245 $conte 220 $content = "$2\n"; 246 } 221 } 247 } else { 222 } else { 248 $space = "" if 223 $space = "" if (!($content =~ s/^($space)//)); 249 } 224 } 250 $data{$what}->{$tag} . 225 $data{$what}->{$tag} .= $content; 251 226 252 next; 227 next; 253 } 228 } 254 if (m/^\s*(.*)/) { 229 if (m/^\s*(.*)/) { 255 $data{$what}->{$tag} . 230 $data{$what}->{$tag} .= "\n$1"; 256 $data{$what}->{$tag} = 231 $data{$what}->{$tag} =~ s/\n+$//; 257 next; 232 next; 258 } 233 } 259 234 260 # Everything else is error 235 # Everything else is error 261 parse_error($file, $ln, "Unexp 236 parse_error($file, $ln, "Unexpected content", $_); 262 } 237 } 263 $data{$nametag}->{description} =~ s/^\ 238 $data{$nametag}->{description} =~ s/^\n+// if ($data{$nametag}->{description}); 264 if ($what) { 239 if ($what) { 265 parse_error($file, $ln, "What 240 parse_error($file, $ln, "What '$what' doesn't have a description", "") if (!$data{$what}->{description}); 266 241 267 foreach my $w(split /\xac/,$wh !! 242 foreach my $w(split /, /,$what) { 268 $symbols{$w}->{xref} = 243 $symbols{$w}->{xref} = $what; 269 }; 244 }; 270 } 245 } 271 close IN; 246 close IN; 272 } 247 } 273 248 274 sub create_labels { 249 sub create_labels { 275 my %labels; 250 my %labels; 276 251 277 foreach my $what (keys %data) { 252 foreach my $what (keys %data) { 278 next if ($data{$what}->{file} 253 next if ($data{$what}->{file} eq "File"); 279 254 280 foreach my $p (@{$data{$what}- 255 foreach my $p (@{$data{$what}->{label_list}}) { 281 my ($content, $label) 256 my ($content, $label) = @{$p}; 282 $label = "abi_" . $lab 257 $label = "abi_" . $label . " "; 283 $label =~ tr/A-Z/a-z/; 258 $label =~ tr/A-Z/a-z/; 284 259 285 # Convert special char 260 # Convert special chars to "_" 286 $label =~s/([\x00-\x2f 261 $label =~s/([\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\xff])/_/g; 287 $label =~ s,_+,_,g; 262 $label =~ s,_+,_,g; 288 $label =~ s,_$,,; 263 $label =~ s,_$,,; 289 264 290 # Avoid duplicated lab 265 # Avoid duplicated labels 291 while (defined($labels 266 while (defined($labels{$label})) { 292 my @chars = ("A".. 267 my @chars = ("A".."Z", "a".."z"); 293 $label .= $chars[r 268 $label .= $chars[rand @chars]; 294 } 269 } 295 $labels{$label} = 1; 270 $labels{$label} = 1; 296 271 297 $data{$what}->{label} 272 $data{$what}->{label} = $label; 298 273 299 # only one label is en 274 # only one label is enough 300 last; 275 last; 301 } 276 } 302 } 277 } 303 } 278 } 304 279 305 # 280 # 306 # Outputs the book on ReST format 281 # Outputs the book on ReST format 307 # 282 # 308 283 309 # \b doesn't work well with paths. So, we need !! 284 # \b doesn't work well with paths. So, we need to define something else 310 # Boundaries are punct characters, spaces and !! 285 my $bondary = qr { (?<![\w\/\`\{])(?=[\w\/\`\{])|(?<=[\w\/\`\{])(?![\w\/\`\{]) }x; 311 my $start = qr {(^|\s|\() }x; << 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 286 316 sub output_rest { 287 sub output_rest { 317 create_labels(); 288 create_labels(); 318 289 319 my $part = ""; 290 my $part = ""; 320 291 321 foreach my $what (sort { 292 foreach my $what (sort { 322 ($data{$a}->{t 293 ($data{$a}->{type} eq "File") cmp ($data{$b}->{type} eq "File") || 323 $a cmp $b 294 $a cmp $b 324 } keys %data) { 295 } keys %data) { 325 my $type = $data{$what}->{type 296 my $type = $data{$what}->{type}; 326 297 327 my @file = split / /, $data{$w 298 my @file = split / /, $data{$what}->{file}; 328 my @filepath = split / /, $dat 299 my @filepath = split / /, $data{$what}->{filepath}; 329 300 330 if ($enable_lineno) { 301 if ($enable_lineno) { 331 printf ".. LINENO %s%s !! 302 printf "#define LINENO %s%s#%s\n\n", 332 $prefix, $file[ 303 $prefix, $file[0], 333 $data{$what}->{ 304 $data{$what}->{line_no}; 334 } 305 } 335 306 336 my $w = $what; 307 my $w = $what; >> 308 $w =~ s/([\(\)\_\-\*\=\^\~\\])/\\$1/g; 337 309 338 if ($type ne "File") { 310 if ($type ne "File") { 339 my $cur_part = $what; 311 my $cur_part = $what; 340 if ($what =~ '/') { 312 if ($what =~ '/') { 341 if ($what =~ m 313 if ($what =~ m#^(\/?(?:[\w\-]+\/?){1,2})#) { 342 $cur_p 314 $cur_part = "Symbols under $1"; 343 $cur_p 315 $cur_part =~ s,/$,,; 344 } 316 } 345 } 317 } 346 318 347 if ($cur_part ne "" && 319 if ($cur_part ne "" && $part ne $cur_part) { 348 $part = $cur_part; 320 $part = $cur_part; 349 my $bar = $part; 321 my $bar = $part; 350 $bar =~ s/./-/g; 322 $bar =~ s/./-/g; 351 print "$part\n$bar 323 print "$part\n$bar\n\n"; 352 } 324 } 353 325 354 printf ".. _%s:\n\n", 326 printf ".. _%s:\n\n", $data{$what}->{label}; 355 327 356 my @names = split /\xa !! 328 my @names = split /, /,$w; 357 my $len = 0; 329 my $len = 0; 358 330 359 foreach my $name (@nam 331 foreach my $name (@names) { 360 $name =~ s/$sy << 361 $name = "**$na 332 $name = "**$name**"; 362 $len = length( 333 $len = length($name) if (length($name) > $len); 363 } 334 } 364 335 365 print "+-" . "-" x $le 336 print "+-" . "-" x $len . "-+\n"; 366 foreach my $name (@nam 337 foreach my $name (@names) { 367 printf "| %s", 338 printf "| %s", $name . " " x ($len - length($name)) . " |\n"; 368 print "+-" . " 339 print "+-" . "-" x $len . "-+\n"; 369 } 340 } 370 341 371 print "\n"; 342 print "\n"; 372 } 343 } 373 344 374 for (my $i = 0; $i < scalar(@f 345 for (my $i = 0; $i < scalar(@filepath); $i++) { 375 my $path = $filepath[$ 346 my $path = $filepath[$i]; 376 my $f = $file[$i]; 347 my $f = $file[$i]; 377 348 378 $path =~ s,.*/(.*/.*), 349 $path =~ s,.*/(.*/.*),$1,;; 379 $path =~ s,[/\-],_,g;; 350 $path =~ s,[/\-],_,g;; 380 my $fileref = "abi_fil 351 my $fileref = "abi_file_".$path; 381 352 382 if ($type eq "File") { 353 if ($type eq "File") { 383 print ".. _$fi 354 print ".. _$fileref:\n\n"; 384 } else { 355 } else { 385 print "Defined 356 print "Defined on file :ref:`$f <$fileref>`\n\n"; 386 } 357 } 387 } 358 } 388 359 389 if ($type eq "File") { 360 if ($type eq "File") { 390 my $bar = $w; 361 my $bar = $w; 391 $bar =~ s/./-/g; 362 $bar =~ s/./-/g; 392 print "$w\n$bar\n\n"; 363 print "$w\n$bar\n\n"; 393 } 364 } 394 365 395 my $desc = ""; 366 my $desc = ""; 396 $desc = $data{$what}->{descrip 367 $desc = $data{$what}->{description} if (defined($data{$what}->{description})); 397 $desc =~ s/\s+$/\n/; 368 $desc =~ s/\s+$/\n/; 398 369 399 if (!($desc =~ /^\s*$/)) { 370 if (!($desc =~ /^\s*$/)) { 400 if ($description_is_rs 371 if ($description_is_rst) { 401 # Remove title 372 # Remove title markups from the description 402 # Having title 373 # Having titles inside ABI files will only work if extra 403 # care would b 374 # care would be taken in order to strictly follow the same 404 # level order 375 # level order for each markup. 405 $desc =~ s/\n[ 376 $desc =~ s/\n[\-\*\=\^\~]+\n/\n\n/g; 406 377 407 # Enrich text 378 # Enrich text by creating cross-references 408 379 409 my $new_desc = !! 380 $desc =~ s,Documentation/(?!devicetree)(\S+)\.rst,:doc:`/$1`,g; 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 381 433 my @ma !! 382 my @matches = $desc =~ m,Documentation/ABI/([\w\/\-]+),; 434 foreac !! 383 foreach my $f (@matches) { 435 !! 384 my $xref = $f; 436 !! 385 my $path = $f; 437 !! 386 $path =~ s,.*/(.*/.*),$1,;; 438 !! 387 $path =~ s,[/\-],_,g;; 439 !! 388 $xref .= " <abi_file_" . $path . ">"; 440 !! 389 $desc =~ s,\bDocumentation/ABI/$f\b,:ref:`$xref`,g; 441 } !! 390 } 442 391 443 # Seek !! 392 @matches = $desc =~ m,$bondary(/sys/[^\s\.\,\;\:\*\s\`\'\(\)]+)$bondary,; 444 @match << 445 393 446 foreac !! 394 foreach my $s (@matches) { 447 !! 395 if (defined($data{$s}) && defined($data{$s}->{label})) { 448 !! 396 my $xref = $s; 449 << 450 397 451 !! 398 $xref =~ s/([\x00-\x1f\x21-\x2f\x3a-\x40\x7b-\xff])/\\$1/g; 452 !! 399 $xref = ":ref:`$xref <" . $data{$s}->{label} . ">`"; 453 400 454 !! 401 $desc =~ s,$bondary$s$bondary,$xref,g; 455 << 456 } 402 } 457 $new_d << 458 } 403 } 459 close $fh; << 460 << 461 404 462 print "$new_de !! 405 print "$desc\n\n"; 463 } else { 406 } else { 464 $desc =~ s/^\s 407 $desc =~ s/^\s+//; 465 408 466 # Remove title 409 # Remove title markups from the description, as they won't work 467 $desc =~ s/\n[ 410 $desc =~ s/\n[\-\*\=\^\~]+\n/\n\n/g; 468 411 469 if ($desc =~ m 412 if ($desc =~ m/\:\n/ || $desc =~ m/\n[\t ]+/ || $desc =~ m/[\x00-\x08\x0b-\x1f\x7b-\xff]/) { 470 # put 413 # put everything inside a code block 471 $desc 414 $desc =~ s/\n/\n /g; 472 415 473 print 416 print "::\n\n"; 474 print 417 print " $desc\n\n"; 475 } else { 418 } else { 476 # Esca 419 # Escape any special chars from description 477 $desc 420 $desc =~s/([\x00-\x08\x0b-\x1f\x21-\x2a\x2d\x2f\x3c-\x40\x5c\x5e-\x60\x7b-\xff])/\\$1/g; 478 print 421 print "$desc\n\n"; 479 } 422 } 480 } 423 } 481 } else { 424 } else { 482 print "DESCRIPTION MIS 425 print "DESCRIPTION MISSING for $what\n\n" if (!$data{$what}->{is_file}); 483 } 426 } 484 427 485 if ($data{$what}->{symbols}) { 428 if ($data{$what}->{symbols}) { 486 printf "Has the follow 429 printf "Has the following ABI:\n\n"; 487 430 488 foreach my $content(@{ 431 foreach my $content(@{$data{$what}->{symbols}}) { 489 my $label = $d 432 my $label = $data{$symbols{$content}->{xref}}->{label}; 490 433 491 # Escape speci 434 # Escape special chars from content 492 $content =~s/( 435 $content =~s/([\x00-\x1f\x21-\x2f\x3a-\x40\x7b-\xff])/\\$1/g; 493 436 494 print "- :ref: 437 print "- :ref:`$content <$label>`\n\n"; 495 } 438 } 496 } 439 } 497 440 498 if (defined($data{$what}->{use 441 if (defined($data{$what}->{users})) { 499 my $users = $data{$wha 442 my $users = $data{$what}->{users}; 500 443 501 $users =~ s/\n/\n\t/g; 444 $users =~ s/\n/\n\t/g; 502 printf "Users:\n\t%s\n 445 printf "Users:\n\t%s\n\n", $users if ($users ne ""); 503 } 446 } 504 447 505 } 448 } 506 } 449 } 507 450 508 # 451 # 509 # Searches for ABI symbols 452 # Searches for ABI symbols 510 # 453 # 511 sub search_symbols { 454 sub search_symbols { 512 foreach my $what (sort keys %data) { 455 foreach my $what (sort keys %data) { 513 next if (!($what =~ m/($arg)/) 456 next if (!($what =~ m/($arg)/)); 514 457 515 my $type = $data{$what}->{type 458 my $type = $data{$what}->{type}; 516 next if ($type eq "File"); 459 next if ($type eq "File"); 517 460 518 my $file = $data{$what}->{file 461 my $file = $data{$what}->{filepath}; 519 462 520 $what =~ s/\xac/, /g; << 521 my $bar = $what; 463 my $bar = $what; 522 $bar =~ s/./-/g; 464 $bar =~ s/./-/g; 523 465 524 print "\n$what\n$bar\n\n"; 466 print "\n$what\n$bar\n\n"; 525 467 526 my $kernelversion = $data{$wha 468 my $kernelversion = $data{$what}->{kernelversion} if (defined($data{$what}->{kernelversion})); 527 my $contact = $data{$what}->{c 469 my $contact = $data{$what}->{contact} if (defined($data{$what}->{contact})); 528 my $users = $data{$what}->{use 470 my $users = $data{$what}->{users} if (defined($data{$what}->{users})); 529 my $date = $data{$what}->{date 471 my $date = $data{$what}->{date} if (defined($data{$what}->{date})); 530 my $desc = $data{$what}->{desc 472 my $desc = $data{$what}->{description} if (defined($data{$what}->{description})); 531 473 532 $kernelversion =~ s/^\s+// if 474 $kernelversion =~ s/^\s+// if ($kernelversion); 533 $contact =~ s/^\s+// if ($cont 475 $contact =~ s/^\s+// if ($contact); 534 if ($users) { 476 if ($users) { 535 $users =~ s/^\s+//; 477 $users =~ s/^\s+//; 536 $users =~ s/\n//g; 478 $users =~ s/\n//g; 537 } 479 } 538 $date =~ s/^\s+// if ($date); 480 $date =~ s/^\s+// if ($date); 539 $desc =~ s/^\s+// if ($desc); 481 $desc =~ s/^\s+// if ($desc); 540 482 541 printf "Kernel version:\t\t%s\ 483 printf "Kernel version:\t\t%s\n", $kernelversion if ($kernelversion); 542 printf "Date:\t\t\t%s\n", $dat 484 printf "Date:\t\t\t%s\n", $date if ($date); 543 printf "Contact:\t\t%s\n", $co 485 printf "Contact:\t\t%s\n", $contact if ($contact); 544 printf "Users:\t\t\t%s\n", $us 486 printf "Users:\t\t\t%s\n", $users if ($users); 545 print "Defined on file(s):\t$f 487 print "Defined on file(s):\t$file\n\n"; 546 print "Description:\n\n$desc"; 488 print "Description:\n\n$desc"; 547 } 489 } 548 } 490 } 549 491 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 << 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 492 # Ensure that the prefix will always end with a slash 944 # While this is not needed for find, it makes 493 # While this is not needed for find, it makes the patch nicer 945 # with --enable-lineno 494 # with --enable-lineno 946 $prefix =~ s,/?$,/,; 495 $prefix =~ s,/?$,/,; 947 496 948 if ($cmd eq "undefined" || $cmd eq "search") { << 949 $show_warnings = 0; << 950 } << 951 # 497 # 952 # Parses all ABI files located at $prefix dir 498 # Parses all ABI files located at $prefix dir 953 # 499 # 954 find({wanted =>\&parse_abi, no_chdir => 1}, $p 500 find({wanted =>\&parse_abi, no_chdir => 1}, $prefix); 955 501 956 print STDERR Data::Dumper->Dump([\%data], [qw( !! 502 print STDERR Data::Dumper->Dump([\%data], [qw(*data)]) if ($debug); 957 503 958 # 504 # 959 # Handles the command 505 # Handles the command 960 # 506 # 961 if ($cmd eq "undefined") { !! 507 if ($cmd eq "search") { 962 undefined_symbols; << 963 } elsif ($cmd eq "search") { << 964 search_symbols; 508 search_symbols; 965 } else { 509 } else { 966 if ($cmd eq "rest") { 510 if ($cmd eq "rest") { 967 output_rest; 511 output_rest; 968 } 512 } 969 513 970 # Warn about duplicated ABI entries 514 # Warn about duplicated ABI entries 971 foreach my $what(sort keys %symbols) { 515 foreach my $what(sort keys %symbols) { 972 my @files = @{$symbols{$what}- 516 my @files = @{$symbols{$what}->{file}}; 973 517 974 next if (scalar(@files) == 1); 518 next if (scalar(@files) == 1); 975 519 976 printf STDERR "Warning: $what 520 printf STDERR "Warning: $what is defined %d times: @files\n", 977 scalar(@files); 521 scalar(@files); 978 } 522 } 979 } 523 } 980 524 981 __END__ 525 __END__ 982 526 983 =head1 NAME 527 =head1 NAME 984 528 985 get_abi.pl - parse the Linux ABI files and pro !! 529 abi_book.pl - parse the Linux ABI files and produce a ReST book. 986 530 987 =head1 SYNOPSIS 531 =head1 SYNOPSIS 988 532 989 B<get_abi.pl> [--debug <level>] [--enable-line !! 533 B<abi_book.pl> [--debug] [--enable-lineno] [--man] [--help] 990 [--(no-)rst-source] [--dir=<dir !! 534 [--(no-)rst-source] [--dir=<dir>] <COMAND> [<ARGUMENT>] 991 [--search-string <regex>] << 992 <COMMAND> [<ARGUMENT>] << 993 535 994 Where B<COMMAND> can be: !! 536 Where <COMMAND> can be: 995 537 996 =over 8 538 =over 8 997 539 998 B<search> I<SEARCH_REGEX> - search for I<SEARC !! 540 B<search> [SEARCH_REGEX] - search for [SEARCH_REGEX] inside ABI 999 541 1000 B<rest> - output the ABI in !! 542 B<rest> - output the ABI in ReST markup language 1001 543 1002 B<validate> - validate the ABI !! 544 B<validate> - validate the ABI contents 1003 << 1004 B<undefined> - existing symbols << 1005 defined at Docume << 1006 545 1007 =back 546 =back 1008 547 1009 =head1 OPTIONS 548 =head1 OPTIONS 1010 549 1011 =over 8 550 =over 8 1012 551 1013 =item B<--dir> 552 =item B<--dir> 1014 553 1015 Changes the location of the ABI search. By de 554 Changes the location of the ABI search. By default, it uses 1016 the Documentation/ABI directory. 555 the Documentation/ABI directory. 1017 556 1018 =item B<--rst-source> and B<--no-rst-source> 557 =item B<--rst-source> and B<--no-rst-source> 1019 558 1020 The input file may be using ReST syntax or no 559 The input file may be using ReST syntax or not. Those two options allow 1021 selecting between a rst-compliant source ABI !! 560 selecting between a rst-compliant source ABI (--rst-source), or a 1022 plain text that may be violating ReST spec, s 561 plain text that may be violating ReST spec, so it requres some escaping 1023 logic (B<--no-rst-source>). !! 562 logic (--no-rst-source). 1024 563 1025 =item B<--enable-lineno> 564 =item B<--enable-lineno> 1026 565 1027 Enable output of .. LINENO lines. !! 566 Enable output of #define 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 567 1043 =item B<--search-string> I<regex string> !! 568 =item B<--debug> 1044 569 1045 Show only occurences that match a search stri !! 570 Put the script in verbose mode, useful for debugging. Can be called multiple 1046 Used only when B<undefined>. !! 571 times, to increase verbosity. 1047 572 1048 =item B<--help> 573 =item B<--help> 1049 574 1050 Prints a brief help message and exits. 575 Prints a brief help message and exits. 1051 576 1052 =item B<--man> 577 =item B<--man> 1053 578 1054 Prints the manual page and exits. 579 Prints the manual page and exits. 1055 580 1056 =back 581 =back 1057 582 1058 =head1 DESCRIPTION 583 =head1 DESCRIPTION 1059 584 1060 Parse the Linux ABI files from ABI DIR (usual 585 Parse the Linux ABI files from ABI DIR (usually located at Documentation/ABI), 1061 allowing to search for ABI symbols or to prod 586 allowing to search for ABI symbols or to produce a ReST book containing 1062 the Linux ABI documentation. 587 the Linux ABI documentation. 1063 588 1064 =head1 EXAMPLES 589 =head1 EXAMPLES 1065 590 1066 Search for all stable symbols with the word " 591 Search for all stable symbols with the word "usb": 1067 592 1068 =over 8 593 =over 8 1069 594 1070 $ scripts/get_abi.pl search usb --dir Documen 595 $ scripts/get_abi.pl search usb --dir Documentation/ABI/stable 1071 596 1072 =back 597 =back 1073 598 1074 Search for all symbols that match the regex e 599 Search for all symbols that match the regex expression "usb.*cap": 1075 600 1076 =over 8 601 =over 8 1077 602 1078 $ scripts/get_abi.pl search usb.*cap 603 $ scripts/get_abi.pl search usb.*cap 1079 604 1080 =back 605 =back 1081 606 1082 Output all obsoleted symbols in ReST format 607 Output all obsoleted symbols in ReST format 1083 608 1084 =over 8 609 =over 8 1085 610 1086 $ scripts/get_abi.pl rest --dir Documentation 611 $ scripts/get_abi.pl rest --dir Documentation/ABI/obsolete 1087 612 1088 =back 613 =back 1089 614 1090 =head1 BUGS 615 =head1 BUGS 1091 616 1092 Report bugs to Mauro Carvalho Chehab <mchehab+ !! 617 Report bugs to Mauro Carvalho Chehab <mchehab+samsung@kernel.org> 1093 618 1094 =head1 COPYRIGHT 619 =head1 COPYRIGHT 1095 620 1096 Copyright (c) 2016-2021 by Mauro Carvalho Che< !! 621 Copyright (c) 2016-2019 by Mauro Carvalho Chehab <mchehab+samsung@kernel.org>. 1097 622 1098 License GPLv2: GNU GPL version 2 <http://gnu. 623 License GPLv2: GNU GPL version 2 <http://gnu.org/licenses/gpl.html>. 1099 624 1100 This is free software: you are free to change 625 This is free software: you are free to change and redistribute it. 1101 There is NO WARRANTY, to the extent permitted 626 There is NO WARRANTY, to the extent permitted by law. 1102 627 1103 =cut 628 =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.