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