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