blob: f84d321a72bbafd82ca3b7b6588637c92bc1710e [file] [log] [blame]
Mauro Carvalho Chehabbbc249f2019-06-20 14:22:55 -03001#!/usr/bin/perl
2
3use strict;
4use Pod::Usage;
5use Getopt::Long;
6use File::Find;
7use Fcntl ':mode';
8
9my $help;
10my $man;
11my $debug;
12
13GetOptions(
14 "debug|d+" => \$debug,
15 'help|?' => \$help,
16 man => \$man
17) or pod2usage(2);
18
19pod2usage(1) if $help;
20pod2usage(-exitstatus => 0, -verbose => 2) if $man;
21
22pod2usage(2) if (scalar @ARGV != 1);
23
24my ($prefix) = @ARGV;
25
26require Data::Dumper if ($debug);
27
28my %data;
29
30#
31# Displays an error message, printing file name and line
32#
33sub parse_error($$$$) {
34 my ($file, $ln, $msg, $data) = @_;
35
36 print STDERR "file $file#$ln: $msg at\n\t$data";
37}
38
39#
40# Parse an ABI file, storing its contents at %data
41#
42sub parse_abi {
43 my $file = $File::Find::name;
44
45 my $mode = (stat($file))[2];
46 return if ($mode & S_IFDIR);
47 return if ($file =~ m,/README,);
48
49 my $name = $file;
50 $name =~ s,.*/,,;
51
Mauro Carvalho Chehabd0ebaf52019-06-20 14:22:58 -030052 my $nametag = "File $name";
53 $data{$nametag}->{what} = "File $name";
54 $data{$nametag}->{type} = "File";
55 $data{$nametag}->{file} = $name;
56 $data{$nametag}->{is_file} = 1;
57
Mauro Carvalho Chehabbbc249f2019-06-20 14:22:55 -030058 my $type = $file;
59 $type =~ s,.*/(.*)/.*,$1,;
60
61 my $what;
62 my $new_what;
63 my $tag;
64 my $ln;
Mauro Carvalho Chehab6619c662019-06-20 14:22:56 -030065 my $xrefs;
Mauro Carvalho Chehab4e6a6232019-06-20 14:22:57 -030066 my $space;
Mauro Carvalho Chehabd0ebaf52019-06-20 14:22:58 -030067 my @labels;
68 my $label;
Mauro Carvalho Chehabbbc249f2019-06-20 14:22:55 -030069
70 print STDERR "Opening $file\n" if ($debug > 1);
71 open IN, $file;
72 while(<IN>) {
73 $ln++;
Mauro Carvalho Chehab4e6a6232019-06-20 14:22:57 -030074 if (m/^(\S+)(:\s*)(.*)/i) {
Mauro Carvalho Chehabbbc249f2019-06-20 14:22:55 -030075 my $new_tag = lc($1);
Mauro Carvalho Chehab4e6a6232019-06-20 14:22:57 -030076 my $sep = $2;
77 my $content = $3;
Mauro Carvalho Chehabbbc249f2019-06-20 14:22:55 -030078
79 if (!($new_tag =~ m/(what|date|kernelversion|contact|description|users)/)) {
80 if ($tag eq "description") {
Mauro Carvalho Chehab4e6a6232019-06-20 14:22:57 -030081 # New "tag" is actually part of
82 # description. Don't consider it a tag
83 $new_tag = "";
Mauro Carvalho Chehabbbc249f2019-06-20 14:22:55 -030084 } else {
85 parse_error($file, $ln, "tag '$tag' is invalid", $_);
86 }
87 }
88
89 if ($new_tag =~ m/what/) {
Mauro Carvalho Chehab4e6a6232019-06-20 14:22:57 -030090 $space = "";
Mauro Carvalho Chehabbbc249f2019-06-20 14:22:55 -030091 if ($tag =~ m/what/) {
92 $what .= ", " . $content;
93 } else {
Mauro Carvalho Chehab4e6a6232019-06-20 14:22:57 -030094 parse_error($file, $ln, "What '$what' doesn't have a description", "") if ($what && !$data{$what}->{description});
95
Mauro Carvalho Chehabbbc249f2019-06-20 14:22:55 -030096 $what = $content;
Mauro Carvalho Chehabd0ebaf52019-06-20 14:22:58 -030097 $label = $content;
Mauro Carvalho Chehabbbc249f2019-06-20 14:22:55 -030098 $new_what = 1;
99 }
Mauro Carvalho Chehabd0ebaf52019-06-20 14:22:58 -0300100 push @labels, [($content, $label)];
Mauro Carvalho Chehabbbc249f2019-06-20 14:22:55 -0300101 $tag = $new_tag;
Mauro Carvalho Chehab6619c662019-06-20 14:22:56 -0300102
Mauro Carvalho Chehabd0ebaf52019-06-20 14:22:58 -0300103 push @{$data{$nametag}->{xrefs}}, [($content, $label)] if ($data{$nametag}->{what});
Mauro Carvalho Chehabbbc249f2019-06-20 14:22:55 -0300104 next;
105 }
106
Mauro Carvalho Chehab4e6a6232019-06-20 14:22:57 -0300107 if ($new_tag) {
108 $tag = $new_tag;
Mauro Carvalho Chehabbbc249f2019-06-20 14:22:55 -0300109
Mauro Carvalho Chehab4e6a6232019-06-20 14:22:57 -0300110 if ($new_what) {
Mauro Carvalho Chehabd0ebaf52019-06-20 14:22:58 -0300111 @{$data{$what}->{label}} = @labels if ($data{$nametag}->{what});
112 @labels = ();
113 $label = "";
Mauro Carvalho Chehab4e6a6232019-06-20 14:22:57 -0300114 $new_what = 0;
Mauro Carvalho Chehabbbc249f2019-06-20 14:22:55 -0300115
Mauro Carvalho Chehab4e6a6232019-06-20 14:22:57 -0300116 $data{$what}->{type} = $type;
117 $data{$what}->{file} = $name;
118 print STDERR "\twhat: $what\n" if ($debug > 1);
119 }
Mauro Carvalho Chehabbbc249f2019-06-20 14:22:55 -0300120
Mauro Carvalho Chehab4e6a6232019-06-20 14:22:57 -0300121 if (!$what) {
122 parse_error($file, $ln, "'What:' should come first:", $_);
123 next;
124 }
125 if ($tag eq "description") {
126 next if ($content =~ m/^\s*$/);
127 if ($content =~ m/^(\s*)(.*)/) {
128 my $new_content = $2;
129 $space = $new_tag . $sep . $1;
130 while ($space =~ s/\t+/' ' x (length($&) * 8 - length($`) % 8)/e) {}
131 $space =~ s/./ /g;
132 $data{$what}->{$tag} .= "$new_content\n";
133 }
134 } else {
135 $data{$what}->{$tag} = $content;
136 }
Mauro Carvalho Chehabbbc249f2019-06-20 14:22:55 -0300137 next;
138 }
Mauro Carvalho Chehabbbc249f2019-06-20 14:22:55 -0300139 }
140
Mauro Carvalho Chehab4e6a6232019-06-20 14:22:57 -0300141 # Store any contents before tags at the database
Mauro Carvalho Chehabd0ebaf52019-06-20 14:22:58 -0300142 if (!$tag && $data{$nametag}->{what}) {
143 $data{$nametag}->{description} .= $_;
Mauro Carvalho Chehab6619c662019-06-20 14:22:56 -0300144 next;
145 }
Mauro Carvalho Chehabbbc249f2019-06-20 14:22:55 -0300146
Mauro Carvalho Chehab4e6a6232019-06-20 14:22:57 -0300147 if ($tag eq "description") {
148 if (!$data{$what}->{description}) {
149 next if (m/^\s*\n/);
150 if (m/^(\s*)(.*)/) {
151 $space = $1;
152 while ($space =~ s/\t+/' ' x (length($&) * 8 - length($`) % 8)/e) {}
153 $data{$what}->{$tag} .= "$2\n";
154 }
155 } else {
156 my $content = $_;
157 if (m/^\s*\n/) {
158 $data{$what}->{$tag} .= $content;
159 next;
160 }
161
162 while ($content =~ s/\t+/' ' x (length($&) * 8 - length($`) % 8)/e) {}
163 $space = "" if (!($content =~ s/^($space)//));
164
165 # Compress spaces with tabs
166 $content =~ s<^ {8}> <\t>;
167 $content =~ s<^ {1,7}\t> <\t>;
168 $content =~ s< {1,7}\t> <\t>;
169 $data{$what}->{$tag} .= $content;
170 }
171 next;
172 }
Mauro Carvalho Chehabbbc249f2019-06-20 14:22:55 -0300173 if (m/^\s*(.*)/) {
174 $data{$what}->{$tag} .= "\n$1";
175 $data{$what}->{$tag} =~ s/\n+$//;
176 next;
177 }
178
179 # Everything else is error
180 parse_error($file, $ln, "Unexpected line:", $_);
181 }
Mauro Carvalho Chehabd0ebaf52019-06-20 14:22:58 -0300182 $data{$nametag}->{description} =~ s/^\n+//;
Mauro Carvalho Chehabbbc249f2019-06-20 14:22:55 -0300183 close IN;
184}
185
186# Outputs the output on ReST format
187sub output_rest {
188 foreach my $what (sort keys %data) {
189 my $type = $data{$what}->{type};
190 my $file = $data{$what}->{file};
191
192 my $w = $what;
193 $w =~ s/([\(\)\_\-\*\=\^\~\\])/\\$1/g;
194
Mauro Carvalho Chehab4e6a6232019-06-20 14:22:57 -0300195 my $bar = $w;
196 $bar =~ s/./-/g;
197
Mauro Carvalho Chehabd0ebaf52019-06-20 14:22:58 -0300198 foreach my $p (@{$data{$what}->{label}}) {
199 my ($content, $label) = @{$p};
200 $label = "abi_" . $label . " ";
201 $label =~ tr/A-Z/a-z/;
202
203 # Convert special chars to "_"
204 $label =~s/([\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\xff])/_/g;
205 $label =~ s,_+,_,g;
206 $label =~ s,_$,,;
207
208 $data{$what}->{label} .= $label;
209
210 printf ".. _%s:\n\n", $label;
211
212 # only one label is enough
213 last;
Mauro Carvalho Chehab6619c662019-06-20 14:22:56 -0300214 }
215
Mauro Carvalho Chehab4e6a6232019-06-20 14:22:57 -0300216 print "$w\n$bar\n\n";
Mauro Carvalho Chehab6619c662019-06-20 14:22:56 -0300217
218 print "- defined on file $file (type: $type)\n\n" if ($type ne "File");
Mauro Carvalho Chehabbbc249f2019-06-20 14:22:55 -0300219
220 my $desc = $data{$what}->{description};
221 $desc =~ s/^\s+//;
222
223 # Remove title markups from the description, as they won't work
224 $desc =~ s/\n[\-\*\=\^\~]+\n/\n/g;
225
Mauro Carvalho Chehabbbc249f2019-06-20 14:22:55 -0300226 if (!($desc =~ /^\s*$/)) {
Mauro Carvalho Chehab4e6a6232019-06-20 14:22:57 -0300227 if ($desc =~ m/\:\n/ || $desc =~ m/\n[\t ]+/ || $desc =~ m/[\x00-\x08\x0b-\x1f\x7b-\xff]/) {
228 # put everything inside a code block
229 $desc =~ s/\n/\n /g;
230
231 print "::\n\n";
232 print " $desc\n\n";
233 } else {
234 # Escape any special chars from description
235 $desc =~s/([\x00-\x08\x0b-\x1f\x21-\x2a\x2d\x2f\x3c-\x40\x5c\x5e-\x60\x7b-\xff])/\\$1/g;
236
237 print "$desc\n\n";
238 }
Mauro Carvalho Chehabbbc249f2019-06-20 14:22:55 -0300239 } else {
Mauro Carvalho Chehabd0ebaf52019-06-20 14:22:58 -0300240 print "DESCRIPTION MISSING for $what\n\n" if (!$data{$what}->{is_file});
Mauro Carvalho Chehabbbc249f2019-06-20 14:22:55 -0300241 }
Mauro Carvalho Chehab6619c662019-06-20 14:22:56 -0300242
Mauro Carvalho Chehabd0ebaf52019-06-20 14:22:58 -0300243 if ($data{$what}->{xrefs}) {
244 printf "Has the following ABI:\n\n";
245
246 foreach my $p(@{$data{$what}->{xrefs}}) {
247 my ($content, $label) = @{$p};
248 $label = "abi_" . $label . " ";
249 $label =~ tr/A-Z/a-z/;
250
251 # Convert special chars to "_"
252 $label =~s/([\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\xff])/_/g;
253 $label =~ s,_+,_,g;
254 $label =~ s,_$,,;
255
256 # Escape special chars from content
257 $content =~s/([\x00-\x1f\x21-\x2f\x3a-\x40\x7b-\xff])/\\$1/g;
258
259 print "- :ref:`$content <$label>`\n\n";
260 }
261 }
Mauro Carvalho Chehabbbc249f2019-06-20 14:22:55 -0300262 }
263}
264
265#
266# Parses all ABI files located at $prefix dir
267#
268find({wanted =>\&parse_abi, no_chdir => 1}, $prefix);
269
270print STDERR Data::Dumper->Dump([\%data], [qw(*data)]) if ($debug);
271
272#
273# Outputs an ReST file with the ABI contents
274#
275output_rest
276
277
278__END__
279
280=head1 NAME
281
282abi_book.pl - parse the Linux ABI files and produce a ReST book.
283
284=head1 SYNOPSIS
285
286B<abi_book.pl> [--debug] <ABI_DIR>]
287
288=head1 OPTIONS
289
290=over 8
291
292=item B<--debug>
293
294Put the script in verbose mode, useful for debugging. Can be called multiple
295times, to increase verbosity.
296
297=item B<--help>
298
299Prints a brief help message and exits.
300
301=item B<--man>
302
303Prints the manual page and exits.
304
305=back
306
307=head1 DESCRIPTION
308
309Parse the Linux ABI files from ABI DIR (usually located at Documentation/ABI)
310and produce a ReST book containing the Linux ABI.
311
312=head1 BUGS
313
314Report bugs to Mauro Carvalho Chehab <mchehab@s-opensource.com>
315
316=head1 COPYRIGHT
317
318Copyright (c) 2016 by Mauro Carvalho Chehab <mchehab@s-opensource.com>.
319
320License GPLv2: GNU GPL version 2 <http://gnu.org/licenses/gpl.html>.
321
322This is free software: you are free to change and redistribute it.
323There is NO WARRANTY, to the extent permitted by law.
324
325=cut