get_abi.pl 22.2 KB
Newer Older
1
#!/usr/bin/env perl
2
# SPDX-License-Identifier: GPL-2.0
3 4

use strict;
5
use warnings;
6
use utf8;
7 8 9 10
use Pod::Usage;
use Getopt::Long;
use File::Find;
use Fcntl ':mode';
11
use Cwd 'abs_path';
12
use Data::Dumper;
13

14
my $help = 0;
15
my $hint = 0;
16 17 18
my $man = 0;
my $debug = 0;
my $enable_lineno = 0;
19
my $show_warnings = 1;
20
my $prefix="Documentation/ABI";
21
my $sysfs_prefix="/sys";
22
my $search_string;
23

24 25 26 27 28
# Debug options
my $dbg_what_parsing = 1;
my $dbg_what_open = 2;
my $dbg_dump_abi_structs = 4;

29 30 31
#
# If true, assumes that the description is formatted with ReST
#
32
my $description_is_rst = 1;
33

34
GetOptions(
35
	"debug=i" => \$debug,
36
	"enable-lineno" => \$enable_lineno,
37
	"rst-source!" => \$description_is_rst,
38
	"dir=s" => \$prefix,
39
	'help|?' => \$help,
40
	"show-hints" => \$hint,
41
	"search-string=s" => \$search_string,
42 43 44 45 46 47
	man => \$man
) or pod2usage(2);

pod2usage(1) if $help;
pod2usage(-exitstatus => 0, -verbose => 2) if $man;

48
pod2usage(2) if (scalar @ARGV < 1 || @ARGV > 2);
49

50 51
my ($cmd, $arg) = @ARGV;

52
pod2usage(2) if ($cmd ne "search" && $cmd ne "rest" && $cmd ne "validate" && $cmd ne "undefined");
53
pod2usage(2) if ($cmd eq "search" && !$arg);
54

55
require Data::Dumper if ($debug & $dbg_dump_abi_structs);
56 57

my %data;
58
my %symbols;
59 60 61 62 63 64 65

#
# Displays an error message, printing file name and line
#
sub parse_error($$$$) {
	my ($file, $ln, $msg, $data) = @_;

66 67
	return if (!$show_warnings);

68 69 70 71 72 73 74 75 76
	$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";
	}
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
}

#
# 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,.*/,,;

92 93 94 95
	my $fn = $file;
	$fn =~ s,Documentation/ABI/,,;

	my $nametag = "File $fn";
96 97 98
	$data{$nametag}->{what} = "File $name";
	$data{$nametag}->{type} = "File";
	$data{$nametag}->{file} = $name;
99
	$data{$nametag}->{filepath} = $file;
100
	$data{$nametag}->{is_file} = 1;
101
	$data{$nametag}->{line_no} = 1;
102

103 104 105 106 107
	my $type = $file;
	$type =~ s,.*/(.*)/.*,$1,;

	my $what;
	my $new_what;
108
	my $tag = "";
109
	my $ln;
110
	my $xrefs;
111
	my $space;
112
	my @labels;
113
	my $label = "";
114

115
	print STDERR "Opening $file\n" if ($debug & $dbg_what_open);
116 117 118
	open IN, $file;
	while(<IN>) {
		$ln++;
119
		if (m/^(\S+)(:\s*)(.*)/i) {
120
			my $new_tag = lc($1);
121 122
			my $sep = $2;
			my $content = $3;
123

124
			if (!($new_tag =~ m/(what|where|date|kernelversion|contact|description|users)/)) {
125
				if ($tag eq "description") {
126 127 128
					# New "tag" is actually part of
					# description. Don't consider it a tag
					$new_tag = "";
129
				} elsif ($tag ne "") {
130 131 132 133
					parse_error($file, $ln, "tag '$tag' is invalid", $_);
				}
			}

134 135
			# Invalid, but it is a common mistake
			if ($new_tag eq "where") {
136
				parse_error($file, $ln, "tag 'Where' is invalid. Should be 'What:' instead", "");
137 138 139
				$new_tag = "what";
			}

140
			if ($new_tag =~ m/what/) {
141
				$space = "";
142 143
				$content =~ s/[,.;]$//;

144 145
				push @{$symbols{$content}->{file}}, " $file:" . ($ln - 1);

146
				if ($tag =~ m/what/) {
147
					$what .= "\xac" . $content;
148
				} else {
149 150 151
					if ($what) {
						parse_error($file, $ln, "What '$what' doesn't have a description", "") if (!$data{$what}->{description});

152
						foreach my $w(split /\xac/, $what) {
153
							$symbols{$w}->{xref} = $what;
154 155
						};
					}
156

157
					$what = $content;
158
					$label = $content;
159 160
					$new_what = 1;
				}
161
				push @labels, [($content, $label)];
162
				$tag = $new_tag;
163

164
				push @{$data{$nametag}->{symbols}}, $content if ($data{$nametag}->{what});
165 166 167
				next;
			}

168
			if ($tag ne "" && $new_tag) {
169
				$tag = $new_tag;
170

171
				if ($new_what) {
172
					@{$data{$what}->{label_list}} = @labels if ($data{$nametag}->{what});
173 174
					@labels = ();
					$label = "";
175
					$new_what = 0;
176

177
					$data{$what}->{type} = $type;
178 179 180 181 182 183 184 185 186
					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;
						}
					}
187
					print STDERR "\twhat: $what\n" if ($debug & $dbg_what_parsing);
188 189 190
					$data{$what}->{line_no} = $ln;
				} else {
					$data{$what}->{line_no} = $ln if (!defined($data{$what}->{line_no}));
191
				}
192

193 194 195 196
				if (!$what) {
					parse_error($file, $ln, "'What:' should come first:", $_);
					next;
				}
197 198
				if ($new_tag eq "description") {
					$sep =~ s,:, ,;
199
					$content = ' ' x length($new_tag) . $sep . $content;
200 201 202
					while ($content =~ s/\t+/' ' x (length($&) * 8 - length($`) % 8)/e) {}
					if ($content =~ m/^(\s*)(\S.*)$/) {
						# Preserve initial spaces for the first line
203
						$space = $1;
204 205 206 207
						$content = "$2\n";
						$data{$what}->{$tag} .= $content;
					} else {
						undef($space);
208
					}
209

210 211 212
				} else {
					$data{$what}->{$tag} = $content;
				}
213 214 215 216
				next;
			}
		}

217
		# Store any contents before tags at the database
218 219
		if (!$tag && $data{$nametag}->{what}) {
			$data{$nametag}->{description} .= $_;
220 221
			next;
		}
222

223
		if ($tag eq "description") {
224 225
			my $content = $_;
			while ($content =~ s/\t+/' ' x (length($&) * 8 - length($`) % 8)/e) {}
226 227 228 229 230 231
			if (m/^\s*\n/) {
				$data{$what}->{$tag} .= "\n";
				next;
			}

			if (!defined($space)) {
232
				# Preserve initial spaces for the first line
233
				if ($content =~ m/^(\s*)(\S.*)$/) {
234
					$space = $1;
235
					$content = "$2\n";
236 237 238 239
				}
			} else {
				$space = "" if (!($content =~ s/^($space)//));
			}
240 241
			$data{$what}->{$tag} .= $content;

242 243
			next;
		}
244 245 246 247 248 249 250
		if (m/^\s*(.*)/) {
			$data{$what}->{$tag} .= "\n$1";
			$data{$what}->{$tag} =~ s/\n+$//;
			next;
		}

		# Everything else is error
251
		parse_error($file, $ln, "Unexpected content", $_);
252
	}
253 254 255 256
	$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});

257
		foreach my $w(split /\xac/,$what) {
258
			$symbols{$w}->{xref} = $what;
259 260
		};
	}
261 262 263
	close IN;
}

264 265
sub create_labels {
	my %labels;
266

267 268
	foreach my $what (keys %data) {
		next if ($data{$what}->{file} eq "File");
269

270
		foreach my $p (@{$data{$what}->{label_list}}) {
271 272 273 274 275 276 277 278 279
			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,_$,,;

280 281 282 283 284 285 286
			# Avoid duplicated labels
			while (defined($labels{$label})) {
			    my @chars = ("A".."Z", "a".."z");
			    $label .= $chars[rand @chars];
			}
			$labels{$label} = 1;

287
			$data{$what}->{label} = $label;
288 289 290

			# only one label is enough
			last;
291
		}
292 293 294 295 296 297 298
	}
}

#
# Outputs the book on ReST format
#

299 300 301 302
# \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;
303
my $xref_match = qr { $start(\/(sys|config|proc|dev|kvd)\/[^,.:;\)\s]+)$bondary }x;
304
my $symbols = qr { ([\x01-\x08\x0e-\x1f\x21-\x2f\x3a-\x40\x7b-\xff]) }x;
305

306 307 308
sub output_rest {
	create_labels();

309 310
	my $part = "";

311 312 313 314 315
	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};
316 317 318

		my @file = split / /, $data{$what}->{file};
		my @filepath = split / /, $data{$what}->{filepath};
319 320 321

		if ($enable_lineno) {
			printf "#define LINENO %s%s#%s\n\n",
322
			       $prefix, $file[0],
323 324
			       $data{$what}->{line_no};
		}
325

326
		my $w = $what;
327

328
		if ($type ne "File") {
329 330 331 332 333 334 335 336 337 338 339 340 341 342 343
			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";
			}

344
			printf ".. _%s:\n\n", $data{$what}->{label};
345

346
			my @names = split /\xac/,$w;
347 348 349
			my $len = 0;

			foreach my $name (@names) {
350
				$name =~ s/$symbols/\\$1/g;
351
				$name = "**$name**";
352 353 354 355 356 357 358 359 360
				$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";
			}

361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376
			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";
			}
377
		}
378

379 380 381 382 383 384
		if ($type eq "File") {
			my $bar = $w;
			$bar =~ s/./-/g;
			print "$w\n$bar\n\n";
		}

385 386 387
		my $desc = "";
		$desc = $data{$what}->{description} if (defined($data{$what}->{description}));
		$desc =~ s/\s+$/\n/;
388

389
		if (!($desc =~ /^\s*$/)) {
390
			if ($description_is_rst) {
391 392 393 394 395 396
				# 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;

397 398
				# Enrich text by creating cross-references

399
				my $new_desc = "";
400 401 402
				my $init_indent = -1;
				my $literal_indent = -1;

403 404
				open(my $fh, "+<", \$desc);
				while (my $d = <$fh>) {
405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420
					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;
						}
					}

421 422 423 424 425 426 427 428 429 430 431
					$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;
					}
432

433 434
					# Seek for cross reference symbols like /sys/...
					@matches = $d =~ m/$xref_match/g;
435

436 437 438 439
					foreach my $s (@matches) {
						next if (!($s =~ m,/,));
						if (defined($data{$s}) && defined($data{$s}->{label})) {
							my $xref = $s;
440

441 442
							$xref =~ s/$symbols/\\$1/g;
							$xref = ":ref:`$xref <" . $data{$s}->{label} . ">`";
443

444 445
							$d =~ s,$start$s$bondary,$1$xref$2,g;
						}
446
					}
447
					$new_desc .= $d;
448
				}
449 450
				close $fh;

451

452
				print "$new_desc\n\n";
453
			} else {
454
				$desc =~ s/^\s+//;
455

456 457 458 459 460 461 462 463 464 465 466 467 468 469
				# 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";
				}
470
			}
471
		} else {
472
			print "DESCRIPTION MISSING for $what\n\n" if (!$data{$what}->{is_file});
473
		}
474

475
		if ($data{$what}->{symbols}) {
476 477
			printf "Has the following ABI:\n\n";

478
			foreach my $content(@{$data{$what}->{symbols}}) {
479
				my $label = $data{$symbols{$content}->{xref}}->{label};
480 481 482 483 484 485 486

				# Escape special chars from content
				$content =~s/([\x00-\x1f\x21-\x2f\x3a-\x40\x7b-\xff])/\\$1/g;

				print "- :ref:`$content <$label>`\n\n";
			}
		}
487 488 489 490 491 492 493 494

		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 "");
		}

495 496 497
	}
}

498 499 500 501 502 503 504 505 506 507 508 509
#
# 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};

510
		$what =~ s/\xac/, /g;
511 512 513 514 515
		my $bar = $what;
		$bar =~ s/./-/g;

		print "\n$what\n$bar\n\n";

516 517 518 519 520 521 522 523 524 525 526 527 528 529
		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);
530 531 532 533 534

		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);
535
		print "Defined on file(s):\t$file\n\n";
536 537 538 539
		print "Description:\n\n$desc";
	}
}

540
# Exclude /sys/kernel/debug and /sys/kernel/tracing from the search path
541
sub dont_parse_special_attributes {
542 543 544 545 546 547 548 549 550 551 552 553
	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;
554 555
my %aliases;
my @files;
556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622
my %root;

sub graph_add_file {
	my $file = shift;
	my $type = shift;

	my $dir = $file;
	$dir =~ s,^(.*/).*,$1,;
	$file =~ s,.*/,,;

	my $name;
	my $file_ref = \%root;
	foreach my $edge(split "/", $dir) {
		$name .= "$edge/";
		if (!defined ${$file_ref}{$edge}) {
			${$file_ref}{$edge} = { };
		}
		$file_ref = \%{$$file_ref{$edge}};
		${$file_ref}{"__name"} = [ $name ];
	}
	$name .= "$file";
	${$file_ref}{$file} = {
		"__name" => [ $name ]
	};

	return \%{$$file_ref{$file}};
}

sub graph_add_link {
	my $file = shift;
	my $link = shift;

	# Traverse graph to find the reference
	my $file_ref = \%root;
	foreach my $edge(split "/", $file) {
		$file_ref = \%{$$file_ref{$edge}} || die "Missing node!";
	}

	# do a BFS

	my @queue;
	my %seen;
	my $base_name;
	my $st;

	push @queue, $file_ref;
	$seen{$start}++;

	while (@queue) {
		my $v = shift @queue;
		my @child = keys(%{$v});

		foreach my $c(@child) {
			next if $seen{$$v{$c}};
			next if ($c eq "__name");

			# Add new name
			my $name = @{$$v{$c}{"__name"}}[0];
			if ($name =~ s#^$file/#$link/#) {
				push @{$$v{$c}{"__name"}}, $name;
			}
			# Add child to the queue and mark as seen
			push @queue, $$v{$c};
			$seen{$c}++;
		}
	}
}
623

624
my $escape_symbols = qr { ([\x01-\x08\x0e-\x1f\x21-\x29\x2b-\x2d\x3a-\x40\x7b-\xfe]) }x;
625 626
sub parse_existing_sysfs {
	my $file = $File::Find::name;
627 628 629 630

	# Ignore cgroup and firmware
	return if ($file =~ m#^/sys/(fs/cgroup|firmware)/#);

631 632 633 634 635 636 637 638
	# Ignore some sysfs nodes
	return if ($file =~ m#/(sections|notes)/#);

	# Would need to check at
	# Documentation/admin-guide/kernel-parameters.txt, but this
	# is not easily parseable.
	return if ($file =~ m#/parameters/#);

639 640
	my $mode = (lstat($file))[2];
	my $abs_file = abs_path($file);
641

642 643 644 645 646 647
	if (S_ISLNK($mode)) {
		$aliases{$file} = $abs_file;
		return;
	}

	return if (S_ISDIR($mode));
648

649 650 651
	# Trivial: file is defined exactly the same way at ABI What:
	return if (defined($data{$file}));
	return if (defined($data{$abs_file}));
652

653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675
	push @files, graph_add_file($abs_file, "file");
}

sub get_leave($)
{
	my $what = shift;
	my $leave;

	my $l = $what;
	my $stop = 1;

	$leave = $l;
	$leave =~ s,/$,,;
	$leave =~ s,.*/,,;
	$leave =~ s/[\(\)]//g;

	# $leave is used to improve search performance at
	# check_undefined_symbols, as the algorithm there can seek
	# for a small number of "what". It also allows giving a
	# hint about a leave with the same name somewhere else.
	# However, there are a few occurences where the leave is
	# either a wildcard or a number. Just group such cases
	# altogether.
676
	if ($leave =~ m/\.\*/ || $leave eq "" || $leave =~ /\\d/) {
677 678 679 680
		$leave = "others";
	}

	return $leave;
681
}
682

683
sub check_undefined_symbols {
684 685 686
	foreach my $file_ref (sort @files) {
		my @names = @{$$file_ref{"__name"}};
		my $file = $names[0];
687

688
		my $exact = 0;
689
		my $found_string;
690

691 692 693 694 695
		my $leave = get_leave($file);
		if (!defined($leaf{$leave})) {
			$leave = "others";
		}
		my $what = $leaf{$leave};
696 697 698 699

		my $path = $file;
		$path =~ s,(.*/).*,$1,;

700 701 702 703 704
		if ($search_string) {
			next if (!($file =~ m#$search_string#));
			$found_string = 1;
		}

705 706 707 708 709 710
		foreach my $a (@names) {
			print "--> $a\n" if ($found_string && $hint);
			foreach my $w (split /\xac/, $what) {
				if ($a =~ m#^$w$#) {
					$exact = 1;
					last;
711
				}
712
			}
713
			last if ($exact);
714
		}
715
		next if ($exact);
716

717
		if ($hint && (!$search_string || $found_string)) {
718 719 720 721 722 723
			$what =~ s/\xac/\n\t/g;
			if ($leave ne "others") {
				print "    more likely regexes:\n\t$what\n";
			} else {
				print "    tested regexes:\n\t$what\n";
			}
724 725
			next;
		}
726
		print "$file not found.\n" if (!$search_string || $found_string);
727
	}
728 729 730
}

sub undefined_symbols {
731 732 733 734 735 736
	find({
		wanted =>\&parse_existing_sysfs,
		preprocess =>\&dont_parse_special_attributes,
		no_chdir => 1
	     }, $sysfs_prefix);

737 738
	$leaf{"others"} = "";

739
	foreach my $w (sort keys %data) {
740
		foreach my $what (split /\xac/,$w) {
741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788
			next if (!($what =~ m/^$sysfs_prefix/));

			# Convert what into regular expressions

			$what =~ s,/\.\.\./,/*/,g;
			$what =~ s,\*,.*,g;

			# Temporarily change [0-9]+ type of patterns
			$what =~ s/\[0\-9\]\+/\xff/g;

			# Temporarily change [\d+-\d+] type of patterns
			$what =~ s/\[0\-\d+\]/\xff/g;
			$what =~ s/\[(\d+)\]/\xf4$1\xf5/g;

			# Temporarily change [0-9] type of patterns
			$what =~ s/\[(\d)\-(\d)\]/\xf4$1-$2\xf5/g;

			# Handle multiple option patterns
			$what =~ s/[\{\<\[]([\w_]+)(?:[,|]+([\w_]+)){1,}[\}\>\]]/($1|$2)/g;

			# Handle wildcards
			$what =~ s/\<[^\>]+\>/.*/g;
			$what =~ s/\{[^\}]+\}/.*/g;
			$what =~ s/\[[^\]]+\]/.*/g;

			$what =~ s/[XYZ]/.*/g;

			# Recover [0-9] type of patterns
			$what =~ s/\xf4/[/g;
			$what =~ s/\xf5/]/g;

			# Remove duplicated spaces
			$what =~ s/\s+/ /g;

			# Special case: this ABI has a parenthesis on it
			$what =~ s/sqrt\(x^2\+y^2\+z^2\)/sqrt\(x^2\+y^2\+z^2\)/;

			# Special case: drop comparition as in:
			#	What: foo = <something>
			# (this happens on a few IIO definitions)
			$what =~ s,\s*\=.*$,,;

			# Escape all other symbols
			$what =~ s/$escape_symbols/\\$1/g;
			$what =~ s/\\\\/\\/g;
			$what =~ s/\\([\[\]\(\)\|])/$1/g;
			$what =~ s/(\d+)\\(-\d+)/$1$2/g;

789 790 791 792 793
			$what =~ s/\xff/\\d+/g;

			# Special case: IIO ABI which a parenthesis.
			$what =~ s/sqrt(.*)/sqrt\(.*\)/;

794
			my $leave = get_leave($what);
795
			my $added = 0;
796 797
			foreach my $l (split /\|/, $leave) {
				if (defined($leaf{$l})) {
798 799
					next if ($leaf{$l} =~ m/\b$what\b/);
					$leaf{$l} .= "\xac" . $what;
800
					$added = 1;
801 802
				} else {
					$leaf{$l} = $what;
803
					$added = 1;
804
				}
805
			}
806 807 808 809
			if ($search_string && $added) {
				print "What: $what\n" if ($what =~ m#$search_string#);
			}

810 811
		}
	}
812 813 814 815 816
	# Take links into account
	foreach my $link (keys %aliases) {
		my $abs_file = $aliases{$link};
		graph_add_link($abs_file, $link);
	}
817
	check_undefined_symbols;
818 819
}

820 821 822 823
# 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,/?$,/,;
824

825 826 827
if ($cmd eq "undefined" || $cmd eq "search") {
	$show_warnings = 0;
}
828 829 830 831 832
#
# Parses all ABI files located at $prefix dir
#
find({wanted =>\&parse_abi, no_chdir => 1}, $prefix);

833
print STDERR Data::Dumper->Dump([\%data], [qw(*data)]) if ($debug & $dbg_dump_abi_structs);
834 835

#
836
# Handles the command
837
#
838 839 840
if ($cmd eq "undefined") {
	undefined_symbols;
} elsif ($cmd eq "search") {
841
	search_symbols;
842 843 844 845 846 847 848 849 850 851
} 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);
852

853 854 855 856
		printf STDERR "Warning: $what is defined %d times: @files\n",
		    scalar(@files);
	}
}
857 858 859 860 861 862 863 864 865

__END__

=head1 NAME

abi_book.pl - parse the Linux ABI files and produce a ReST book.

=head1 SYNOPSIS

866
B<abi_book.pl> [--debug <level>] [--enable-lineno] [--man] [--help]
867
	       [--(no-)rst-source] [--dir=<dir>] [--show-hints]
868
	       [--search-string <regex>]
869
	       <COMAND> [<ARGUMENT>]
870 871 872 873 874 875 876

Where <COMMAND> can be:

=over 8

B<search> [SEARCH_REGEX] - search for [SEARCH_REGEX] inside ABI

877 878 879
B<rest>                  - output the ABI in ReST markup language

B<validate>              - validate the ABI contents
880

881 882 883
B<undefined>             - existing symbols at the system that aren't
                           defined at Documentation/ABI

884
=back
885 886 887 888 889

=head1 OPTIONS

=over 8

890 891 892 893 894
=item B<--dir>

Changes the location of the ABI search. By default, it uses
the Documentation/ABI directory.

895 896 897 898 899 900 901
=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).

902 903 904 905
=item B<--enable-lineno>

Enable output of #define LINENO lines.

906 907 908 909
=item B<--debug> I<debug level>

Print debug information according with the level, which is given by the
following bitmask:
910

911 912 913
    -  1: Debug parsing What entries from ABI files;
    -  2: Shows what files are opened from ABI files;
    -  4: Dump the structs used to store the contents of the ABI files.
914

915 916 917 918 919
=item B<--show-hints>

Show hints about possible definitions for the missing ABI symbols.
Used only when B<undefined>.

920 921 922 923 924
=item B<--search-string> [regex string]

Show only occurences that match a search string.
Used only when B<undefined>.

925 926 927 928 929 930 931 932 933 934 935 936
=item B<--help>

Prints a brief help message and exits.

=item B<--man>

Prints the manual page and exits.

=back

=head1 DESCRIPTION

937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965
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
966 967 968

=head1 BUGS

969
Report bugs to Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
970 971 972

=head1 COPYRIGHT

973
Copyright (c) 2016-2019 by Mauro Carvalho Chehab <mchehab+samsung@kernel.org>.
974 975 976 977 978 979 980

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