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 # Boundaries are punct characters, spaces and end-of-line 311 my $start = qr {(^|\s|\() }x; 286 my $start = qr {(^|\s|\() }x; 312 my $bondary = qr { ([,.:;\)\s]|\z) }x; 287 my $bondary = qr { ([,.:;\)\s]|\z) }x; 313 my $xref_match = qr { $start(\/(sys|config|pro 288 my $xref_match = qr { $start(\/(sys|config|proc|dev|kvd)\/[^,.:;\)\s]+)$bondary }x; 314 my $symbols = qr { ([\x01-\x08\x0e-\x1f\x21-\x 289 my $symbols = qr { ([\x01-\x08\x0e-\x1f\x21-\x2f\x3a-\x40\x7b-\xff]) }x; 315 290 316 sub output_rest { 291 sub output_rest { 317 create_labels(); 292 create_labels(); 318 293 319 my $part = ""; 294 my $part = ""; 320 295 321 foreach my $what (sort { 296 foreach my $what (sort { 322 ($data{$a}->{t 297 ($data{$a}->{type} eq "File") cmp ($data{$b}->{type} eq "File") || 323 $a cmp $b 298 $a cmp $b 324 } keys %data) { 299 } keys %data) { 325 my $type = $data{$what}->{type 300 my $type = $data{$what}->{type}; 326 301 327 my @file = split / /, $data{$w 302 my @file = split / /, $data{$what}->{file}; 328 my @filepath = split / /, $dat 303 my @filepath = split / /, $data{$what}->{filepath}; 329 304 330 if ($enable_lineno) { 305 if ($enable_lineno) { 331 printf ".. LINENO %s%s !! 306 printf "#define LINENO %s%s#%s\n\n", 332 $prefix, $file[ 307 $prefix, $file[0], 333 $data{$what}->{ 308 $data{$what}->{line_no}; 334 } 309 } 335 310 336 my $w = $what; 311 my $w = $what; 337 312 338 if ($type ne "File") { 313 if ($type ne "File") { 339 my $cur_part = $what; 314 my $cur_part = $what; 340 if ($what =~ '/') { 315 if ($what =~ '/') { 341 if ($what =~ m 316 if ($what =~ m#^(\/?(?:[\w\-]+\/?){1,2})#) { 342 $cur_p 317 $cur_part = "Symbols under $1"; 343 $cur_p 318 $cur_part =~ s,/$,,; 344 } 319 } 345 } 320 } 346 321 347 if ($cur_part ne "" && 322 if ($cur_part ne "" && $part ne $cur_part) { 348 $part = $cur_part; 323 $part = $cur_part; 349 my $bar = $part; 324 my $bar = $part; 350 $bar =~ s/./-/g; 325 $bar =~ s/./-/g; 351 print "$part\n$bar 326 print "$part\n$bar\n\n"; 352 } 327 } 353 328 354 printf ".. _%s:\n\n", 329 printf ".. _%s:\n\n", $data{$what}->{label}; 355 330 356 my @names = split /\xa !! 331 my @names = split /, /,$w; 357 my $len = 0; 332 my $len = 0; 358 333 359 foreach my $name (@nam 334 foreach my $name (@names) { 360 $name =~ s/$sy 335 $name =~ s/$symbols/\\$1/g; 361 $name = "**$na 336 $name = "**$name**"; 362 $len = length( 337 $len = length($name) if (length($name) > $len); 363 } 338 } 364 339 365 print "+-" . "-" x $le 340 print "+-" . "-" x $len . "-+\n"; 366 foreach my $name (@nam 341 foreach my $name (@names) { 367 printf "| %s", 342 printf "| %s", $name . " " x ($len - length($name)) . " |\n"; 368 print "+-" . " 343 print "+-" . "-" x $len . "-+\n"; 369 } 344 } 370 345 371 print "\n"; 346 print "\n"; 372 } 347 } 373 348 374 for (my $i = 0; $i < scalar(@f 349 for (my $i = 0; $i < scalar(@filepath); $i++) { 375 my $path = $filepath[$ 350 my $path = $filepath[$i]; 376 my $f = $file[$i]; 351 my $f = $file[$i]; 377 352 378 $path =~ s,.*/(.*/.*), 353 $path =~ s,.*/(.*/.*),$1,;; 379 $path =~ s,[/\-],_,g;; 354 $path =~ s,[/\-],_,g;; 380 my $fileref = "abi_fil 355 my $fileref = "abi_file_".$path; 381 356 382 if ($type eq "File") { 357 if ($type eq "File") { 383 print ".. _$fi 358 print ".. _$fileref:\n\n"; 384 } else { 359 } else { 385 print "Defined 360 print "Defined on file :ref:`$f <$fileref>`\n\n"; 386 } 361 } 387 } 362 } 388 363 389 if ($type eq "File") { 364 if ($type eq "File") { 390 my $bar = $w; 365 my $bar = $w; 391 $bar =~ s/./-/g; 366 $bar =~ s/./-/g; 392 print "$w\n$bar\n\n"; 367 print "$w\n$bar\n\n"; 393 } 368 } 394 369 395 my $desc = ""; 370 my $desc = ""; 396 $desc = $data{$what}->{descrip 371 $desc = $data{$what}->{description} if (defined($data{$what}->{description})); 397 $desc =~ s/\s+$/\n/; 372 $desc =~ s/\s+$/\n/; 398 373 399 if (!($desc =~ /^\s*$/)) { 374 if (!($desc =~ /^\s*$/)) { 400 if ($description_is_rs 375 if ($description_is_rst) { 401 # Remove title 376 # Remove title markups from the description 402 # Having title 377 # Having titles inside ABI files will only work if extra 403 # care would b 378 # care would be taken in order to strictly follow the same 404 # level order 379 # level order for each markup. 405 $desc =~ s/\n[ 380 $desc =~ s/\n[\-\*\=\^\~]+\n/\n\n/g; 406 381 407 # Enrich text 382 # Enrich text by creating cross-references 408 383 409 my $new_desc = 384 my $new_desc = ""; 410 my $init_inden 385 my $init_indent = -1; 411 my $literal_in 386 my $literal_indent = -1; 412 387 413 open(my $fh, " 388 open(my $fh, "+<", \$desc); 414 while (my $d = 389 while (my $d = <$fh>) { 415 my $in 390 my $indent = $d =~ m/^(\s+)/; 416 my $sp 391 my $spaces = length($indent); 417 $init_ 392 $init_indent = $indent if ($init_indent < 0); 418 if ($l 393 if ($literal_indent >= 0) { 419 394 if ($spaces > $literal_indent) { 420 395 $new_desc .= $d; 421 396 next; 422 397 } else { 423 398 $literal_indent = -1; 424 399 } 425 } else 400 } else { 426 401 if ($d =~ /()::$/ && !($d =~ /^\s*\.\./)) { 427 402 $literal_indent = $spaces; 428 403 } 429 } 404 } 430 405 431 $d =~ 406 $d =~ s,Documentation/(?!devicetree)(\S+)\.rst,:doc:`/$1`,g; 432 407 433 my @ma 408 my @matches = $d =~ m,Documentation/ABI/([\w\/\-]+),g; 434 foreac 409 foreach my $f (@matches) { 435 410 my $xref = $f; 436 411 my $path = $f; 437 412 $path =~ s,.*/(.*/.*),$1,;; 438 413 $path =~ s,[/\-],_,g;; 439 414 $xref .= " <abi_file_" . $path . ">"; 440 415 $d =~ s,\bDocumentation/ABI/$f\b,:ref:`$xref`,g; 441 } 416 } 442 417 443 # Seek 418 # Seek for cross reference symbols like /sys/... 444 @match 419 @matches = $d =~ m/$xref_match/g; 445 420 446 foreac 421 foreach my $s (@matches) { 447 422 next if (!($s =~ m,/,)); 448 423 if (defined($data{$s}) && defined($data{$s}->{label})) { 449 424 my $xref = $s; 450 425 451 426 $xref =~ s/$symbols/\\$1/g; 452 427 $xref = ":ref:`$xref <" . $data{$s}->{label} . ">`"; 453 428 454 429 $d =~ s,$start$s$bondary,$1$xref$2,g; 455 430 } 456 } 431 } 457 $new_d 432 $new_desc .= $d; 458 } 433 } 459 close $fh; 434 close $fh; 460 435 461 436 462 print "$new_de 437 print "$new_desc\n\n"; 463 } else { 438 } else { 464 $desc =~ s/^\s 439 $desc =~ s/^\s+//; 465 440 466 # Remove title 441 # Remove title markups from the description, as they won't work 467 $desc =~ s/\n[ 442 $desc =~ s/\n[\-\*\=\^\~]+\n/\n\n/g; 468 443 469 if ($desc =~ m 444 if ($desc =~ m/\:\n/ || $desc =~ m/\n[\t ]+/ || $desc =~ m/[\x00-\x08\x0b-\x1f\x7b-\xff]/) { 470 # put 445 # put everything inside a code block 471 $desc 446 $desc =~ s/\n/\n /g; 472 447 473 print 448 print "::\n\n"; 474 print 449 print " $desc\n\n"; 475 } else { 450 } else { 476 # Esca 451 # Escape any special chars from description 477 $desc 452 $desc =~s/([\x00-\x08\x0b-\x1f\x21-\x2a\x2d\x2f\x3c-\x40\x5c\x5e-\x60\x7b-\xff])/\\$1/g; 478 print 453 print "$desc\n\n"; 479 } 454 } 480 } 455 } 481 } else { 456 } else { 482 print "DESCRIPTION MIS 457 print "DESCRIPTION MISSING for $what\n\n" if (!$data{$what}->{is_file}); 483 } 458 } 484 459 485 if ($data{$what}->{symbols}) { 460 if ($data{$what}->{symbols}) { 486 printf "Has the follow 461 printf "Has the following ABI:\n\n"; 487 462 488 foreach my $content(@{ 463 foreach my $content(@{$data{$what}->{symbols}}) { 489 my $label = $d 464 my $label = $data{$symbols{$content}->{xref}}->{label}; 490 465 491 # Escape speci 466 # Escape special chars from content 492 $content =~s/( 467 $content =~s/([\x00-\x1f\x21-\x2f\x3a-\x40\x7b-\xff])/\\$1/g; 493 468 494 print "- :ref: 469 print "- :ref:`$content <$label>`\n\n"; 495 } 470 } 496 } 471 } 497 472 498 if (defined($data{$what}->{use 473 if (defined($data{$what}->{users})) { 499 my $users = $data{$wha 474 my $users = $data{$what}->{users}; 500 475 501 $users =~ s/\n/\n\t/g; 476 $users =~ s/\n/\n\t/g; 502 printf "Users:\n\t%s\n 477 printf "Users:\n\t%s\n\n", $users if ($users ne ""); 503 } 478 } 504 479 505 } 480 } 506 } 481 } 507 482 508 # 483 # 509 # Searches for ABI symbols 484 # Searches for ABI symbols 510 # 485 # 511 sub search_symbols { 486 sub search_symbols { 512 foreach my $what (sort keys %data) { 487 foreach my $what (sort keys %data) { 513 next if (!($what =~ m/($arg)/) 488 next if (!($what =~ m/($arg)/)); 514 489 515 my $type = $data{$what}->{type 490 my $type = $data{$what}->{type}; 516 next if ($type eq "File"); 491 next if ($type eq "File"); 517 492 518 my $file = $data{$what}->{file 493 my $file = $data{$what}->{filepath}; 519 494 520 $what =~ s/\xac/, /g; << 521 my $bar = $what; 495 my $bar = $what; 522 $bar =~ s/./-/g; 496 $bar =~ s/./-/g; 523 497 524 print "\n$what\n$bar\n\n"; 498 print "\n$what\n$bar\n\n"; 525 499 526 my $kernelversion = $data{$wha 500 my $kernelversion = $data{$what}->{kernelversion} if (defined($data{$what}->{kernelversion})); 527 my $contact = $data{$what}->{c 501 my $contact = $data{$what}->{contact} if (defined($data{$what}->{contact})); 528 my $users = $data{$what}->{use 502 my $users = $data{$what}->{users} if (defined($data{$what}->{users})); 529 my $date = $data{$what}->{date 503 my $date = $data{$what}->{date} if (defined($data{$what}->{date})); 530 my $desc = $data{$what}->{desc 504 my $desc = $data{$what}->{description} if (defined($data{$what}->{description})); 531 505 532 $kernelversion =~ s/^\s+// if 506 $kernelversion =~ s/^\s+// if ($kernelversion); 533 $contact =~ s/^\s+// if ($cont 507 $contact =~ s/^\s+// if ($contact); 534 if ($users) { 508 if ($users) { 535 $users =~ s/^\s+//; 509 $users =~ s/^\s+//; 536 $users =~ s/\n//g; 510 $users =~ s/\n//g; 537 } 511 } 538 $date =~ s/^\s+// if ($date); 512 $date =~ s/^\s+// if ($date); 539 $desc =~ s/^\s+// if ($desc); 513 $desc =~ s/^\s+// if ($desc); 540 514 541 printf "Kernel version:\t\t%s\ 515 printf "Kernel version:\t\t%s\n", $kernelversion if ($kernelversion); 542 printf "Date:\t\t\t%s\n", $dat 516 printf "Date:\t\t\t%s\n", $date if ($date); 543 printf "Contact:\t\t%s\n", $co 517 printf "Contact:\t\t%s\n", $contact if ($contact); 544 printf "Users:\t\t\t%s\n", $us 518 printf "Users:\t\t\t%s\n", $users if ($users); 545 print "Defined on file(s):\t$f 519 print "Defined on file(s):\t$file\n\n"; 546 print "Description:\n\n$desc"; 520 print "Description:\n\n$desc"; 547 } 521 } 548 } 522 } 549 523 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 524 # Ensure that the prefix will always end with a slash 944 # While this is not needed for find, it makes 525 # While this is not needed for find, it makes the patch nicer 945 # with --enable-lineno 526 # with --enable-lineno 946 $prefix =~ s,/?$,/,; 527 $prefix =~ s,/?$,/,; 947 528 948 if ($cmd eq "undefined" || $cmd eq "search") { << 949 $show_warnings = 0; << 950 } << 951 # 529 # 952 # Parses all ABI files located at $prefix dir 530 # Parses all ABI files located at $prefix dir 953 # 531 # 954 find({wanted =>\&parse_abi, no_chdir => 1}, $p 532 find({wanted =>\&parse_abi, no_chdir => 1}, $prefix); 955 533 956 print STDERR Data::Dumper->Dump([\%data], [qw( !! 534 print STDERR Data::Dumper->Dump([\%data], [qw(*data)]) if ($debug); 957 535 958 # 536 # 959 # Handles the command 537 # Handles the command 960 # 538 # 961 if ($cmd eq "undefined") { !! 539 if ($cmd eq "search") { 962 undefined_symbols; << 963 } elsif ($cmd eq "search") { << 964 search_symbols; 540 search_symbols; 965 } else { 541 } else { 966 if ($cmd eq "rest") { 542 if ($cmd eq "rest") { 967 output_rest; 543 output_rest; 968 } 544 } 969 545 970 # Warn about duplicated ABI entries 546 # Warn about duplicated ABI entries 971 foreach my $what(sort keys %symbols) { 547 foreach my $what(sort keys %symbols) { 972 my @files = @{$symbols{$what}- 548 my @files = @{$symbols{$what}->{file}}; 973 549 974 next if (scalar(@files) == 1); 550 next if (scalar(@files) == 1); 975 551 976 printf STDERR "Warning: $what 552 printf STDERR "Warning: $what is defined %d times: @files\n", 977 scalar(@files); 553 scalar(@files); 978 } 554 } 979 } 555 } 980 556 981 __END__ 557 __END__ 982 558 983 =head1 NAME 559 =head1 NAME 984 560 985 get_abi.pl - parse the Linux ABI files and pro !! 561 abi_book.pl - parse the Linux ABI files and produce a ReST book. 986 562 987 =head1 SYNOPSIS 563 =head1 SYNOPSIS 988 564 989 B<get_abi.pl> [--debug <level>] [--enable-line !! 565 B<abi_book.pl> [--debug] [--enable-lineno] [--man] [--help] 990 [--(no-)rst-source] [--dir=<dir !! 566 [--(no-)rst-source] [--dir=<dir>] <COMAND> [<ARGUMENT>] 991 [--search-string <regex>] << 992 <COMMAND> [<ARGUMENT>] << 993 567 994 Where B<COMMAND> can be: !! 568 Where <COMMAND> can be: 995 569 996 =over 8 570 =over 8 997 571 998 B<search> I<SEARCH_REGEX> - search for I<SEARC !! 572 B<search> [SEARCH_REGEX] - search for [SEARCH_REGEX] inside ABI 999 573 1000 B<rest> - output the ABI in !! 574 B<rest> - output the ABI in ReST markup language 1001 575 1002 B<validate> - validate the ABI !! 576 B<validate> - validate the ABI contents 1003 << 1004 B<undefined> - existing symbols << 1005 defined at Docume << 1006 577 1007 =back 578 =back 1008 579 1009 =head1 OPTIONS 580 =head1 OPTIONS 1010 581 1011 =over 8 582 =over 8 1012 583 1013 =item B<--dir> 584 =item B<--dir> 1014 585 1015 Changes the location of the ABI search. By de 586 Changes the location of the ABI search. By default, it uses 1016 the Documentation/ABI directory. 587 the Documentation/ABI directory. 1017 588 1018 =item B<--rst-source> and B<--no-rst-source> 589 =item B<--rst-source> and B<--no-rst-source> 1019 590 1020 The input file may be using ReST syntax or no 591 The input file may be using ReST syntax or not. Those two options allow 1021 selecting between a rst-compliant source ABI !! 592 selecting between a rst-compliant source ABI (--rst-source), or a 1022 plain text that may be violating ReST spec, s 593 plain text that may be violating ReST spec, so it requres some escaping 1023 logic (B<--no-rst-source>). !! 594 logic (--no-rst-source). 1024 595 1025 =item B<--enable-lineno> 596 =item B<--enable-lineno> 1026 597 1027 Enable output of .. LINENO lines. !! 598 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 599 1043 =item B<--search-string> I<regex string> !! 600 =item B<--debug> 1044 601 1045 Show only occurences that match a search stri !! 602 Put the script in verbose mode, useful for debugging. Can be called multiple 1046 Used only when B<undefined>. !! 603 times, to increase verbosity. 1047 604 1048 =item B<--help> 605 =item B<--help> 1049 606 1050 Prints a brief help message and exits. 607 Prints a brief help message and exits. 1051 608 1052 =item B<--man> 609 =item B<--man> 1053 610 1054 Prints the manual page and exits. 611 Prints the manual page and exits. 1055 612 1056 =back 613 =back 1057 614 1058 =head1 DESCRIPTION 615 =head1 DESCRIPTION 1059 616 1060 Parse the Linux ABI files from ABI DIR (usual 617 Parse the Linux ABI files from ABI DIR (usually located at Documentation/ABI), 1061 allowing to search for ABI symbols or to prod 618 allowing to search for ABI symbols or to produce a ReST book containing 1062 the Linux ABI documentation. 619 the Linux ABI documentation. 1063 620 1064 =head1 EXAMPLES 621 =head1 EXAMPLES 1065 622 1066 Search for all stable symbols with the word " 623 Search for all stable symbols with the word "usb": 1067 624 1068 =over 8 625 =over 8 1069 626 1070 $ scripts/get_abi.pl search usb --dir Documen 627 $ scripts/get_abi.pl search usb --dir Documentation/ABI/stable 1071 628 1072 =back 629 =back 1073 630 1074 Search for all symbols that match the regex e 631 Search for all symbols that match the regex expression "usb.*cap": 1075 632 1076 =over 8 633 =over 8 1077 634 1078 $ scripts/get_abi.pl search usb.*cap 635 $ scripts/get_abi.pl search usb.*cap 1079 636 1080 =back 637 =back 1081 638 1082 Output all obsoleted symbols in ReST format 639 Output all obsoleted symbols in ReST format 1083 640 1084 =over 8 641 =over 8 1085 642 1086 $ scripts/get_abi.pl rest --dir Documentation 643 $ scripts/get_abi.pl rest --dir Documentation/ABI/obsolete 1087 644 1088 =back 645 =back 1089 646 1090 =head1 BUGS 647 =head1 BUGS 1091 648 1092 Report bugs to Mauro Carvalho Chehab <mchehab+ !! 649 Report bugs to Mauro Carvalho Chehab <mchehab+samsung@kernel.org> 1093 650 1094 =head1 COPYRIGHT 651 =head1 COPYRIGHT 1095 652 1096 Copyright (c) 2016-2021 by Mauro Carvalho Che< !! 653 Copyright (c) 2016-2019 by Mauro Carvalho Chehab <mchehab+samsung@kernel.org>. 1097 654 1098 License GPLv2: GNU GPL version 2 <http://gnu. 655 License GPLv2: GNU GPL version 2 <http://gnu.org/licenses/gpl.html>. 1099 656 1100 This is free software: you are free to change 657 This is free software: you are free to change and redistribute it. 1101 There is NO WARRANTY, to the extent permitted 658 There is NO WARRANTY, to the extent permitted by law. 1102 659 1103 =cut 660 =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.