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