2020-11-23 17:15:33 +03:00
#!/usr/bin/env perl
2019-06-20 20:23:10 +03:00
# SPDX-License-Identifier: GPL-2.0
2019-06-20 20:22:55 +03:00
use strict ;
2020-10-30 10:40:24 +03:00
use warnings ;
2020-10-30 10:40:29 +03:00
use utf8 ;
2019-06-20 20:22:55 +03:00
use Pod::Usage ;
use Getopt::Long ;
use File::Find ;
use Fcntl ':mode' ;
2020-10-30 10:40:24 +03:00
my $ help = 0 ;
my $ man = 0 ;
my $ debug = 0 ;
my $ enable_lineno = 0 ;
2021-09-18 12:52:12 +03:00
my $ show_warnings = 1 ;
2019-06-20 20:22:59 +03:00
my $ prefix = "Documentation/ABI" ;
2021-09-18 12:52:12 +03:00
my $ sysfs_prefix = "/sys" ;
2019-06-20 20:22:55 +03:00
2020-10-30 10:40:20 +03:00
#
# If true, assumes that the description is formatted with ReST
#
2020-10-30 10:40:58 +03:00
my $ description_is_rst = 1 ;
2020-10-30 10:40:20 +03:00
2019-06-20 20:22:55 +03:00
GetOptions (
"debug|d+" = > \ $ debug ,
2020-10-30 10:40:22 +03:00
"enable-lineno" = > \ $ enable_lineno ,
2020-10-30 10:40:20 +03:00
"rst-source!" = > \ $ description_is_rst ,
2019-06-20 20:22:59 +03:00
"dir=s" = > \ $ prefix ,
2019-06-20 20:22:55 +03:00
'help|?' = > \ $ help ,
man = > \ $ man
) or pod2usage ( 2 ) ;
pod2usage ( 1 ) if $ help ;
pod2usage ( - exitstatus = > 0 , - verbose = > 2 ) if $ man ;
2019-06-20 20:22:59 +03:00
pod2usage ( 2 ) if ( scalar @ ARGV < 1 || @ ARGV > 2 ) ;
2019-06-20 20:22:55 +03:00
2019-06-20 20:22:59 +03:00
my ( $ cmd , $ arg ) = @ ARGV ;
2021-09-18 12:52:12 +03:00
pod2usage ( 2 ) if ( $ cmd ne "search" && $ cmd ne "rest" && $ cmd ne "validate" && $ cmd ne "undefined" ) ;
2019-06-20 20:22:59 +03:00
pod2usage ( 2 ) if ( $ cmd eq "search" && ! $ arg ) ;
2019-06-20 20:22:55 +03:00
require Data::Dumper if ( $ debug ) ;
my % data ;
2020-10-30 10:40:24 +03:00
my % symbols ;
2019-06-20 20:22:55 +03:00
#
# Displays an error message, printing file name and line
#
sub parse_error ($$$$) {
my ( $ file , $ ln , $ msg , $ data ) = @ _ ;
2021-09-18 12:52:12 +03:00
return if ( ! $ show_warnings ) ;
2020-10-30 10:40:45 +03:00
$ data =~ s/\s+$/\n/ ;
print STDERR "Warning: file $file#$ln:\n\t$msg" ;
if ( $ data ne "" ) {
print STDERR ". Line\n\t\t$data" ;
} else {
print STDERR "\n" ;
}
2019-06-20 20:22:55 +03:00
}
#
# Parse an ABI file, storing its contents at %data
#
sub parse_abi {
my $ file = $ File:: Find:: name ;
my $ mode = ( stat ( $ file ) ) [ 2 ] ;
return if ( $ mode & S_IFDIR ) ;
return if ( $ file =~ m , / README , ) ;
my $ name = $ file ;
$ name =~ s , . * / , , ;
2020-10-30 10:40:27 +03:00
my $ fn = $ file ;
$ fn =~ s , Documentation /ABI/ , , ;
my $ nametag = "File $fn" ;
2019-06-20 20:22:58 +03:00
$ data { $ nametag } - > { what } = "File $name" ;
$ data { $ nametag } - > { type } = "File" ;
$ data { $ nametag } - > { file } = $ name ;
2019-06-20 20:22:59 +03:00
$ data { $ nametag } - > { filepath } = $ file ;
2019-06-20 20:22:58 +03:00
$ data { $ nametag } - > { is_file } = 1 ;
2020-10-30 10:40:22 +03:00
$ data { $ nametag } - > { line_no } = 1 ;
2019-06-20 20:22:58 +03:00
2019-06-20 20:22:55 +03:00
my $ type = $ file ;
$ type =~ s , . * /(.*)/ . * , $ 1 , ;
my $ what ;
my $ new_what ;
2020-10-30 10:40:24 +03:00
my $ tag = "" ;
2019-06-20 20:22:55 +03:00
my $ ln ;
2019-06-20 20:22:56 +03:00
my $ xrefs ;
2019-06-20 20:22:57 +03:00
my $ space ;
2019-06-20 20:22:58 +03:00
my @ labels ;
2020-10-30 10:40:24 +03:00
my $ label = "" ;
2019-06-20 20:22:55 +03:00
print STDERR "Opening $file\n" if ( $ debug > 1 ) ;
open IN , $ file ;
while ( <IN> ) {
$ ln + + ;
2019-06-20 20:22:57 +03:00
if ( m/^(\S+)(:\s*)(.*)/i ) {
2019-06-20 20:22:55 +03:00
my $ new_tag = lc ( $ 1 ) ;
2019-06-20 20:22:57 +03:00
my $ sep = $ 2 ;
my $ content = $ 3 ;
2019-06-20 20:22:55 +03:00
2019-06-20 20:23:04 +03:00
if ( ! ( $ new_tag =~ m/(what|where|date|kernelversion|contact|description|users)/ ) ) {
2019-06-20 20:22:55 +03:00
if ( $ tag eq "description" ) {
2019-06-20 20:22:57 +03:00
# New "tag" is actually part of
# description. Don't consider it a tag
$ new_tag = "" ;
2019-06-20 20:23:01 +03:00
} elsif ( $ tag ne "" ) {
2019-06-20 20:22:55 +03:00
parse_error ( $ file , $ ln , "tag '$tag' is invalid" , $ _ ) ;
}
}
2019-06-20 20:23:03 +03:00
# Invalid, but it is a common mistake
if ( $ new_tag eq "where" ) {
2020-10-30 10:40:45 +03:00
parse_error ( $ file , $ ln , "tag 'Where' is invalid. Should be 'What:' instead" , "" ) ;
2019-06-20 20:23:03 +03:00
$ new_tag = "what" ;
}
2019-06-20 20:22:55 +03:00
if ( $ new_tag =~ m/what/ ) {
2019-06-20 20:22:57 +03:00
$ space = "" ;
2020-10-30 10:40:24 +03:00
$ content =~ s/[,.;]$// ;
2020-10-30 10:40:25 +03:00
push @ { $ symbols { $ content } - > { file } } , " $file:" . ( $ ln - 1 ) ;
2019-06-20 20:22:55 +03:00
if ( $ tag =~ m/what/ ) {
2021-09-18 12:52:11 +03:00
$ what . = "\xac" . $ content ;
2019-06-20 20:22:55 +03:00
} else {
2020-10-30 10:40:24 +03:00
if ( $ what ) {
parse_error ( $ file , $ ln , "What '$what' doesn't have a description" , "" ) if ( ! $ data { $ what } - > { description } ) ;
2021-09-18 12:52:11 +03:00
foreach my $ w ( split /\xac/ , $ what ) {
2020-10-30 10:40:25 +03:00
$ symbols { $ w } - > { xref } = $ what ;
2020-10-30 10:40:24 +03:00
} ;
}
2019-06-20 20:22:57 +03:00
2019-06-20 20:22:55 +03:00
$ what = $ content ;
2019-06-20 20:22:58 +03:00
$ label = $ content ;
2019-06-20 20:22:55 +03:00
$ new_what = 1 ;
}
2019-06-20 20:22:58 +03:00
push @ labels , [ ( $ content , $ label ) ] ;
2019-06-20 20:22:55 +03:00
$ tag = $ new_tag ;
2019-06-20 20:22:56 +03:00
2020-10-30 10:40:24 +03:00
push @ { $ data { $ nametag } - > { symbols } } , $ content if ( $ data { $ nametag } - > { what } ) ;
2019-06-20 20:22:55 +03:00
next ;
}
2019-06-20 20:23:01 +03:00
if ( $ tag ne "" && $ new_tag ) {
2019-06-20 20:22:57 +03:00
$ tag = $ new_tag ;
2019-06-20 20:22:55 +03:00
2019-06-20 20:22:57 +03:00
if ( $ new_what ) {
2020-10-30 10:40:24 +03:00
@ { $ data { $ what } - > { label_list } } = @ labels if ( $ data { $ nametag } - > { what } ) ;
2019-06-20 20:22:58 +03:00
@ labels = ( ) ;
$ label = "" ;
2019-06-20 20:22:57 +03:00
$ new_what = 0 ;
2019-06-20 20:22:55 +03:00
2019-06-20 20:22:57 +03:00
$ data { $ what } - > { type } = $ type ;
2020-10-30 10:40:25 +03:00
if ( ! defined ( $ data { $ what } - > { file } ) ) {
$ data { $ what } - > { file } = $ name ;
$ data { $ what } - > { filepath } = $ file ;
} else {
if ( $ name ne $ data { $ what } - > { file } ) {
$ data { $ what } - > { file } . = " " . $ name ;
$ data { $ what } - > { filepath } . = " " . $ file ;
}
}
2019-06-20 20:22:57 +03:00
print STDERR "\twhat: $what\n" if ( $ debug > 1 ) ;
2020-10-30 10:40:25 +03:00
$ data { $ what } - > { line_no } = $ ln ;
} else {
$ data { $ what } - > { line_no } = $ ln if ( ! defined ( $ data { $ what } - > { line_no } ) ) ;
2019-06-20 20:22:57 +03:00
}
2019-06-20 20:22:55 +03:00
2019-06-20 20:22:57 +03:00
if ( ! $ what ) {
parse_error ( $ file , $ ln , "'What:' should come first:" , $ _ ) ;
next ;
}
2020-10-30 10:40:23 +03:00
if ( $ new_tag eq "description" ) {
$ sep =~ s , : , , ;
2020-10-30 10:40:20 +03:00
$ content = ' ' x length ( $ new_tag ) . $ sep . $ content ;
2020-10-30 10:40:23 +03:00
while ( $ content =~ s/\t+/' ' x (length($&) * 8 - length($`) % 8)/e ) { }
if ( $ content =~ m/^(\s*)(\S.*)$/ ) {
# Preserve initial spaces for the first line
2020-10-30 10:40:20 +03:00
$ space = $ 1 ;
2020-10-30 10:40:23 +03:00
$ content = "$2\n" ;
$ data { $ what } - > { $ tag } . = $ content ;
} else {
undef ( $ space ) ;
2019-06-20 20:22:57 +03:00
}
2020-10-30 10:40:21 +03:00
2019-06-20 20:22:57 +03:00
} else {
$ data { $ what } - > { $ tag } = $ content ;
}
2019-06-20 20:22:55 +03:00
next ;
}
}
2019-06-20 20:22:57 +03:00
# Store any contents before tags at the database
2019-06-20 20:22:58 +03:00
if ( ! $ tag && $ data { $ nametag } - > { what } ) {
$ data { $ nametag } - > { description } . = $ _ ;
2019-06-20 20:22:56 +03:00
next ;
}
2019-06-20 20:22:55 +03:00
2019-06-20 20:22:57 +03:00
if ( $ tag eq "description" ) {
2020-10-30 10:40:21 +03:00
my $ content = $ _ ;
while ( $ content =~ s/\t+/' ' x (length($&) * 8 - length($`) % 8)/e ) { }
2020-10-30 10:40:23 +03:00
if ( m/^\s*\n/ ) {
$ data { $ what } - > { $ tag } . = "\n" ;
next ;
}
if ( ! defined ( $ space ) ) {
2020-10-30 10:40:21 +03:00
# Preserve initial spaces for the first line
2020-10-30 10:40:23 +03:00
if ( $ content =~ m/^(\s*)(\S.*)$/ ) {
2020-10-30 10:40:21 +03:00
$ space = $ 1 ;
2020-10-30 10:40:23 +03:00
$ content = "$2\n" ;
2019-06-20 20:22:57 +03:00
}
} else {
$ space = "" if ( ! ( $ content =~ s/^($space)// ) ) ;
}
2020-10-30 10:40:23 +03:00
$ data { $ what } - > { $ tag } . = $ content ;
2019-06-20 20:22:57 +03:00
next ;
}
2019-06-20 20:22:55 +03:00
if ( m/^\s*(.*)/ ) {
$ data { $ what } - > { $ tag } . = "\n$1" ;
$ data { $ what } - > { $ tag } =~ s/\n+$// ;
next ;
}
# Everything else is error
2020-10-30 10:40:45 +03:00
parse_error ( $ file , $ ln , "Unexpected content" , $ _ ) ;
2019-06-20 20:22:55 +03:00
}
2020-10-30 10:40:24 +03:00
$ data { $ nametag } - > { description } =~ s/^\n+// if ( $ data { $ nametag } - > { description } ) ;
if ( $ what ) {
parse_error ( $ file , $ ln , "What '$what' doesn't have a description" , "" ) if ( ! $ data { $ what } - > { description } ) ;
2021-09-18 12:52:11 +03:00
foreach my $ w ( split /\xac/ , $ what ) {
2020-10-30 10:40:25 +03:00
$ symbols { $ w } - > { xref } = $ what ;
2020-10-30 10:40:24 +03:00
} ;
}
2019-06-20 20:22:55 +03:00
close IN ;
}
2020-10-30 10:40:24 +03:00
sub create_labels {
my % labels ;
2019-06-20 20:22:55 +03:00
2020-10-30 10:40:24 +03:00
foreach my $ what ( keys % data ) {
next if ( $ data { $ what } - > { file } eq "File" ) ;
2019-06-20 20:22:57 +03:00
2020-10-30 10:40:24 +03:00
foreach my $ p ( @ { $ data { $ what } - > { label_list } } ) {
2019-06-20 20:22:58 +03:00
my ( $ content , $ label ) = @ { $ p } ;
$ label = "abi_" . $ label . " " ;
$ label =~ tr /A-Z/ a - z / ;
# Convert special chars to "_"
$ label =~ s/([\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\xff])/_/g ;
$ label =~ s , _ + , _ , g ;
$ label =~ s , _ $, , ;
2019-06-20 20:23:02 +03:00
# Avoid duplicated labels
while ( defined ( $ labels { $ label } ) ) {
my @ chars = ( "A" .. "Z" , "a" .. "z" ) ;
$ label . = $ chars [ rand @ chars ] ;
}
$ labels { $ label } = 1 ;
2020-10-30 10:40:24 +03:00
$ data { $ what } - > { label } = $ label ;
2019-06-20 20:22:58 +03:00
# only one label is enough
last ;
2019-06-20 20:22:56 +03:00
}
2020-10-30 10:40:24 +03:00
}
}
#
# Outputs the book on ReST format
#
2021-03-25 13:38:24 +03:00
# \b doesn't work well with paths. So, we need to define something else:
# Boundaries are punct characters, spaces and end-of-line
my $ start = qr {(^|\s|\() } x ;
my $ bondary = qr { ( [ , . : ; \ ) \ s ] | \ z ) } x ;
2021-03-25 13:38:25 +03:00
my $ xref_match = qr { $ start ( \ / ( sys | config | proc | dev | kvd ) \ / [ ^ , . : ; \ ) \ s ] + ) $ bondary } x ;
2021-03-25 13:38:22 +03:00
my $ symbols = qr { ( [ \ x01 - \ x08 \ x0e - \ x1f \ x21 - \ x2f \ x3a - \ x40 \ x7b - \ xff ] ) } x ;
2020-10-30 10:40:29 +03:00
2020-10-30 10:40:24 +03:00
sub output_rest {
create_labels ( ) ;
2020-11-02 13:32:16 +03:00
my $ part = "" ;
2020-10-30 10:40:24 +03:00
foreach my $ what ( sort {
( $ data { $ a } - > { type } eq "File" ) cmp ( $ data { $ b } - > { type } eq "File" ) ||
$ a cmp $ b
} keys % data ) {
my $ type = $ data { $ what } - > { type } ;
2020-10-30 10:40:25 +03:00
my @ file = split / / , $ data { $ what } - > { file } ;
my @ filepath = split / / , $ data { $ what } - > { filepath } ;
2020-10-30 10:40:24 +03:00
if ( $ enable_lineno ) {
printf "#define LINENO %s%s#%s\n\n" ,
2020-10-30 10:40:25 +03:00
$ prefix , $ file [ 0 ] ,
2020-10-30 10:40:24 +03:00
$ data { $ what } - > { line_no } ;
}
2019-06-20 20:22:56 +03:00
2020-10-30 10:40:24 +03:00
my $ w = $ what ;
2019-06-20 20:22:56 +03:00
2020-10-30 10:40:25 +03:00
if ( $ type ne "File" ) {
2020-11-02 13:32:16 +03:00
my $ cur_part = $ what ;
if ( $ what =~ '/' ) {
if ( $ what =~ m #^(\/?(?:[\w\-]+\/?){1,2})#) {
$ cur_part = "Symbols under $1" ;
$ cur_part =~ s , / $, , ;
}
}
if ( $ cur_part ne "" && $ part ne $ cur_part ) {
$ part = $ cur_part ;
my $ bar = $ part ;
$ bar =~ s/./-/g ;
print "$part\n$bar\n\n" ;
}
2020-10-30 10:40:24 +03:00
printf ".. _%s:\n\n" , $ data { $ what } - > { label } ;
2019-06-20 20:23:00 +03:00
2021-09-18 12:52:11 +03:00
my @ names = split /\xac/ , $ w ;
2019-06-20 20:23:00 +03:00
my $ len = 0 ;
foreach my $ name ( @ names ) {
2021-03-25 13:38:22 +03:00
$ name =~ s/$symbols/\\$1/g ;
2020-10-30 10:40:28 +03:00
$ name = "**$name**" ;
2019-06-20 20:23:00 +03:00
$ len = length ( $ name ) if ( length ( $ name ) > $ len ) ;
}
print "+-" . "-" x $ len . "-+\n" ;
foreach my $ name ( @ names ) {
printf "| %s" , $ name . " " x ( $ len - length ( $ name ) ) . " |\n" ;
print "+-" . "-" x $ len . "-+\n" ;
}
2020-10-30 10:40:25 +03:00
print "\n" ;
}
for ( my $ i = 0 ; $ i < scalar ( @ filepath ) ; $ i + + ) {
my $ path = $ filepath [ $ i ] ;
my $ f = $ file [ $ i ] ;
$ path =~ s , . * /(.*/ . * ) , $ 1 , ; ;
$ path =~ s , [ / \ - ] , _ , g ; ;
my $ fileref = "abi_file_" . $ path ;
if ( $ type eq "File" ) {
print ".. _$fileref:\n\n" ;
} else {
print "Defined on file :ref:`$f <$fileref>`\n\n" ;
}
2020-10-30 10:40:24 +03:00
}
2019-06-20 20:22:55 +03:00
2020-10-30 10:40:27 +03:00
if ( $ type eq "File" ) {
my $ bar = $ w ;
$ bar =~ s/./-/g ;
print "$w\n$bar\n\n" ;
}
2020-10-30 10:40:24 +03:00
my $ desc = "" ;
$ desc = $ data { $ what } - > { description } if ( defined ( $ data { $ what } - > { description } ) ) ;
$ desc =~ s/\s+$/\n/ ;
2019-06-20 20:22:55 +03:00
2019-06-20 20:22:57 +03:00
if ( ! ( $ desc =~ /^\s*$/ ) ) {
2020-10-30 10:40:20 +03:00
if ( $ description_is_rst ) {
2020-11-02 13:32:15 +03:00
# Remove title markups from the description
# Having titles inside ABI files will only work if extra
# care would be taken in order to strictly follow the same
# level order for each markup.
$ desc =~ s/\n[\-\*\=\^\~]+\n/\n\n/g ;
2020-10-30 10:40:29 +03:00
# Enrich text by creating cross-references
2021-03-25 13:38:26 +03:00
my $ new_desc = "" ;
2021-03-25 13:38:27 +03:00
my $ init_indent = - 1 ;
my $ literal_indent = - 1 ;
2021-03-25 13:38:26 +03:00
open ( my $ fh , "+<" , \ $ desc ) ;
while ( my $ d = <$fh> ) {
2021-03-25 13:38:27 +03:00
my $ indent = $ d =~ m/^(\s+)/ ;
my $ spaces = length ( $ indent ) ;
$ init_indent = $ indent if ( $ init_indent < 0 ) ;
if ( $ literal_indent >= 0 ) {
if ( $ spaces > $ literal_indent ) {
$ new_desc . = $ d ;
next ;
} else {
$ literal_indent = - 1 ;
}
} else {
if ( $ d =~ /()::$/ && ! ( $ d =~ /^\s*\.\./ ) ) {
$ literal_indent = $ spaces ;
}
}
2021-03-25 13:38:26 +03:00
$ d =~ s , Documentation /(?!devicetree)(\S+)\.rst,:doc:`/ $ 1 ` , g ;
my @ matches = $ d =~ m , Documentation /ABI/ ( [ \ w \ / \ - ] + ) , g ;
foreach my $ f ( @ matches ) {
my $ xref = $ f ;
my $ path = $ f ;
$ path =~ s , . * /(.*/ . * ) , $ 1 , ; ;
$ path =~ s , [ / \ - ] , _ , g ; ;
$ xref . = " <abi_file_" . $ path . ">" ;
$ d =~ s , \ bDocumentation /ABI/ $ f \ b , : ref : `$xref` , g ;
}
2020-10-30 10:40:29 +03:00
2021-03-25 13:38:26 +03:00
# Seek for cross reference symbols like /sys/...
@ matches = $ d =~ m/$xref_match/g ;
2020-10-30 10:40:29 +03:00
2021-03-25 13:38:26 +03:00
foreach my $ s ( @ matches ) {
next if ( ! ( $ s =~ m , / , ) ) ;
if ( defined ( $ data { $ s } ) && defined ( $ data { $ s } - > { label } ) ) {
my $ xref = $ s ;
2020-10-30 10:40:29 +03:00
2021-03-25 13:38:26 +03:00
$ xref =~ s/$symbols/\\$1/g ;
$ xref = ":ref:`$xref <" . $ data { $ s } - > { label } . ">`" ;
2020-10-30 10:40:29 +03:00
2021-03-25 13:38:26 +03:00
$ d =~ s , $ start $ s $ bondary , $ 1 $ xref $ 2 , g ;
}
2020-10-30 10:40:29 +03:00
}
2021-03-25 13:38:26 +03:00
$ new_desc . = $ d ;
2020-10-30 10:40:29 +03:00
}
2021-03-25 13:38:26 +03:00
close $ fh ;
2020-10-30 10:40:29 +03:00
2021-03-25 13:38:26 +03:00
print "$new_desc\n\n" ;
2019-06-20 20:22:57 +03:00
} else {
2020-10-30 10:40:20 +03:00
$ desc =~ s/^\s+// ;
2019-06-20 20:22:55 +03:00
2020-10-30 10:40:20 +03:00
# Remove title markups from the description, as they won't work
$ desc =~ s/\n[\-\*\=\^\~]+\n/\n\n/g ;
if ( $ desc =~ m/\:\n/ || $ desc =~ m/\n[\t ]+/ || $ desc =~ m/[\x00-\x08\x0b-\x1f\x7b-\xff]/ ) {
# put everything inside a code block
$ desc =~ s/\n/\n /g ;
print "::\n\n" ;
print " $desc\n\n" ;
} else {
# Escape any special chars from description
$ desc =~ s/([\x00-\x08\x0b-\x1f\x21-\x2a\x2d\x2f\x3c-\x40\x5c\x5e-\x60\x7b-\xff])/\\$1/g ;
print "$desc\n\n" ;
}
2019-06-20 20:22:57 +03:00
}
2019-06-20 20:22:55 +03:00
} else {
2019-06-20 20:22:58 +03:00
print "DESCRIPTION MISSING for $what\n\n" if ( ! $ data { $ what } - > { is_file } ) ;
2019-06-20 20:22:55 +03:00
}
2019-06-20 20:22:56 +03:00
2020-10-30 10:40:24 +03:00
if ( $ data { $ what } - > { symbols } ) {
2019-06-20 20:22:58 +03:00
printf "Has the following ABI:\n\n" ;
2020-10-30 10:40:24 +03:00
foreach my $ content ( @ { $ data { $ what } - > { symbols } } ) {
2020-10-30 10:40:25 +03:00
my $ label = $ data { $ symbols { $ content } - > { xref } } - > { label } ;
2019-06-20 20:22:58 +03:00
# Escape special chars from content
$ content =~ s/([\x00-\x1f\x21-\x2f\x3a-\x40\x7b-\xff])/\\$1/g ;
print "- :ref:`$content <$label>`\n\n" ;
}
}
2020-10-30 10:40:26 +03:00
if ( defined ( $ data { $ what } - > { users } ) ) {
my $ users = $ data { $ what } - > { users } ;
$ users =~ s/\n/\n\t/g ;
printf "Users:\n\t%s\n\n" , $ users if ( $ users ne "" ) ;
}
2019-06-20 20:22:55 +03:00
}
}
2019-06-20 20:22:59 +03:00
#
# Searches for ABI symbols
#
sub search_symbols {
foreach my $ what ( sort keys % data ) {
next if ( ! ( $ what =~ m/($arg)/ ) ) ;
my $ type = $ data { $ what } - > { type } ;
next if ( $ type eq "File" ) ;
my $ file = $ data { $ what } - > { filepath } ;
my $ bar = $ what ;
$ bar =~ s/./-/g ;
print "\n$what\n$bar\n\n" ;
2020-10-30 10:40:24 +03:00
my $ kernelversion = $ data { $ what } - > { kernelversion } if ( defined ( $ data { $ what } - > { kernelversion } ) ) ;
my $ contact = $ data { $ what } - > { contact } if ( defined ( $ data { $ what } - > { contact } ) ) ;
my $ users = $ data { $ what } - > { users } if ( defined ( $ data { $ what } - > { users } ) ) ;
my $ date = $ data { $ what } - > { date } if ( defined ( $ data { $ what } - > { date } ) ) ;
my $ desc = $ data { $ what } - > { description } if ( defined ( $ data { $ what } - > { description } ) ) ;
$ kernelversion =~ s/^\s+// if ( $ kernelversion ) ;
$ contact =~ s/^\s+// if ( $ contact ) ;
if ( $ users ) {
$ users =~ s/^\s+// ;
$ users =~ s/\n//g ;
}
$ date =~ s/^\s+// if ( $ date ) ;
$ desc =~ s/^\s+// if ( $ desc ) ;
2019-06-20 20:22:59 +03:00
printf "Kernel version:\t\t%s\n" , $ kernelversion if ( $ kernelversion ) ;
printf "Date:\t\t\t%s\n" , $ date if ( $ date ) ;
printf "Contact:\t\t%s\n" , $ contact if ( $ contact ) ;
printf "Users:\t\t\t%s\n" , $ users if ( $ users ) ;
2020-10-30 10:40:25 +03:00
print "Defined on file(s):\t$file\n\n" ;
2019-06-20 20:22:59 +03:00
print "Description:\n\n$desc" ;
}
}
2021-09-18 12:52:12 +03:00
# Exclude /sys/kernel/debug and /sys/kernel/tracing from the search path
sub skip_debugfs {
if ( ( $ File:: Find:: dir =~ m , ^ /sys/ kernel , ) ) {
return grep { ! /(debug|tracing)/ } @ _ ;
}
if ( ( $ File:: Find:: dir =~ m , ^ /sys/ fs , ) ) {
return grep { ! /(pstore|bpf|fuse)/ } @ _ ;
}
return @ _
}
my % leaf ;
my $ escape_symbols = qr { ( [ \ x01 - \ x08 \ x0e - \ x1f \ x21 - \ x29 \ x2b - \ x2d \ x3a - \ x40 \ x7b - \ xff ] ) } x ;
sub parse_existing_sysfs {
my $ file = $ File:: Find:: name ;
my $ mode = ( stat ( $ file ) ) [ 2 ] ;
return if ( $ mode & S_IFDIR ) ;
my $ leave = $ file ;
$ leave =~ s , . * / , , ;
if ( defined ( $ leaf { $ leave } ) ) {
# FIXME: need to check if the path makes sense
my $ what = $ leaf { $ leave } ;
$ what =~ s/,/ /g ;
$ what =~ s/\<[^\>]+\>/.*/g ;
$ what =~ s/\{[^\}]+\}/.*/g ;
$ what =~ s/\[[^\]]+\]/.*/g ;
$ what =~ s , /\.\.\./ , /.*/ , g ;
$ what =~ s , /\*/ , /.*/ , g ;
$ what =~ s/\s+/ /g ;
# Escape all other symbols
$ what =~ s/$escape_symbols/\\$1/g ;
foreach my $ i ( split / / , $ what ) {
if ( $ file =~ m #^$i$#) {
# print "$file: $i: OK!\n";
return ;
}
}
print "$file: $leave is defined at $what\n" ;
return ;
}
print "$file not found.\n" ;
}
sub undefined_symbols {
foreach my $ w ( sort keys % data ) {
foreach my $ what ( split /\xac / , $ w ) {
my $ leave = $ what ;
$ leave =~ s , . * / , , ;
if ( defined ( $ leaf { $ leave } ) ) {
$ leaf { $ leave } . = " " . $ what ;
} else {
$ leaf { $ leave } = $ what ;
}
}
}
find ( { wanted = > \ & parse_existing_sysfs , preprocess = > \ & skip_debugfs , no_chdir = > 1 } , $ sysfs_prefix ) ;
}
2020-10-30 10:40:22 +03:00
# Ensure that the prefix will always end with a slash
# While this is not needed for find, it makes the patch nicer
# with --enable-lineno
$ prefix =~ s , /?$,/ , ;
2019-06-20 20:22:59 +03:00
2021-09-18 12:52:12 +03:00
if ( $ cmd eq "undefined" || $ cmd eq "search" ) {
$ show_warnings = 0 ;
}
2019-06-20 20:22:55 +03:00
#
# Parses all ABI files located at $prefix dir
#
find ( { wanted = > \ & parse_abi , no_chdir = > 1 } , $ prefix ) ;
print STDERR Data::Dumper - > Dump ( [ \ % data ] , [ qw( *data ) ] ) if ( $ debug ) ;
#
2019-06-20 20:22:59 +03:00
# Handles the command
2019-06-20 20:22:55 +03:00
#
2021-09-18 12:52:12 +03:00
if ( $ cmd eq "undefined" ) {
undefined_symbols ;
} elsif ( $ cmd eq "search" ) {
2019-06-20 20:22:59 +03:00
search_symbols ;
2020-10-30 10:40:25 +03:00
} else {
if ( $ cmd eq "rest" ) {
output_rest ;
}
# Warn about duplicated ABI entries
foreach my $ what ( sort keys % symbols ) {
my @ files = @ { $ symbols { $ what } - > { file } } ;
next if ( scalar ( @ files ) == 1 ) ;
2019-06-20 20:22:55 +03:00
2020-10-30 10:40:25 +03:00
printf STDERR "Warning: $what is defined %d times: @files\n" ,
scalar ( @ files ) ;
}
}
2019-06-20 20:22:55 +03:00
__END__
= head1 NAME
abi_book . pl - parse the Linux ABI files and produce a ReST book .
= head1 SYNOPSIS
2020-10-30 10:40:22 +03:00
B <abi_book.pl> [ - - debug ] [ - - enable - lineno ] [ - - man ] [ - - help ]
[ - - ( no - ) rst - source ] [ - - dir = <dir> ] <COMAND> [ <ARGUMENT> ]
2019-06-20 20:22:59 +03:00
Where <COMMAND> can be:
= over 8
B <search> [ SEARCH_REGEX ] - search for [ SEARCH_REGEX ] inside ABI
2019-06-20 20:23:04 +03:00
B <rest> - output the ABI in ReST markup language
B <validate> - validate the ABI contents
2019-06-20 20:22:59 +03:00
2021-09-18 12:52:12 +03:00
B <undefined> - existing symbols at the system that aren ' t
defined at Documentation / ABI
2019-06-20 20:22:59 +03:00
= back
2019-06-20 20:22:55 +03:00
= head1 OPTIONS
= over 8
2019-06-20 20:22:59 +03:00
= item B <--dir>
Changes the location of the ABI search . By default , it uses
the Documentation / ABI directory .
2020-10-30 10:40:20 +03:00
= item B <--rst-source> and B <--no-rst-source>
The input file may be using ReST syntax or not . Those two options allow
selecting between a rst - compliant source ABI ( - - rst - source ) , or a
plain text that may be violating ReST spec , so it requres some escaping
logic ( - - no - rst - source ) .
2020-10-30 10:40:22 +03:00
= item B <--enable-lineno>
Enable output of #define LINENO lines.
2019-06-20 20:22:55 +03:00
= item B <--debug>
Put the script in verbose mode , useful for debugging . Can be called multiple
times , to increase verbosity .
= item B <--help>
Prints a brief help message and exits .
= item B <--man>
Prints the manual page and exits .
= back
= head1 DESCRIPTION
2019-06-20 20:22:59 +03:00
Parse the Linux ABI files from ABI DIR ( usually located at Documentation / ABI ) ,
allowing to search for ABI symbols or to produce a ReST book containing
the Linux ABI documentation .
= head1 EXAMPLES
Search for all stable symbols with the word "usb" :
= over 8
$ scripts /get_abi.pl search usb --dir Documentation/ ABI / stable
= back
Search for all symbols that match the regex expression "usb.*cap" :
= over 8
$ scripts / get_abi . pl search usb . * cap
= back
Output all obsoleted symbols in ReST format
= over 8
$ scripts /get_abi.pl rest --dir Documentation/ ABI / obsolete
= back
2019-06-20 20:22:55 +03:00
= head1 BUGS
2019-06-20 20:23:04 +03:00
Report bugs to Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
2019-06-20 20:22:55 +03:00
= head1 COPYRIGHT
2019-06-20 20:23:04 +03:00
Copyright ( c ) 2016 - 2019 by Mauro Carvalho Chehab <mchehab+samsung@kernel.org> .
2019-06-20 20:22:55 +03:00
License GPLv2: GNU GPL version 2 <http://gnu.org/licenses/gpl.html> .
This is free software: you are free to change and redistribute it .
There is NO WARRANTY , to the extent permitted by law .
= cut