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