Commit | Line | Data |
---|---|---|
bbc249f2 MCC |
1 | #!/usr/bin/perl |
2 | ||
3 | use strict; | |
4 | use Pod::Usage; | |
5 | use Getopt::Long; | |
6 | use File::Find; | |
7 | use Fcntl ':mode'; | |
8 | ||
9 | my $help; | |
10 | my $man; | |
11 | my $debug; | |
12 | ||
13 | GetOptions( | |
14 | "debug|d+" => \$debug, | |
15 | 'help|?' => \$help, | |
16 | man => \$man | |
17 | ) or pod2usage(2); | |
18 | ||
19 | pod2usage(1) if $help; | |
20 | pod2usage(-exitstatus => 0, -verbose => 2) if $man; | |
21 | ||
22 | pod2usage(2) if (scalar @ARGV != 1); | |
23 | ||
24 | my ($prefix) = @ARGV; | |
25 | ||
26 | require Data::Dumper if ($debug); | |
27 | ||
28 | my %data; | |
29 | ||
30 | # | |
31 | # Displays an error message, printing file name and line | |
32 | # | |
33 | sub 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 | # | |
42 | sub 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 | ||
52 | my $type = $file; | |
53 | $type =~ s,.*/(.*)/.*,$1,; | |
54 | ||
55 | my $what; | |
56 | my $new_what; | |
57 | my $tag; | |
6619c661 | 58 | my $label; |
bbc249f2 | 59 | my $ln; |
6619c661 MCC |
60 | my $has_file; |
61 | my $xrefs; | |
4e6a6234 | 62 | my $space; |
bbc249f2 MCC |
63 | |
64 | print STDERR "Opening $file\n" if ($debug > 1); | |
65 | open IN, $file; | |
66 | while(<IN>) { | |
67 | $ln++; | |
4e6a6234 | 68 | if (m/^(\S+)(:\s*)(.*)/i) { |
bbc249f2 | 69 | my $new_tag = lc($1); |
4e6a6234 MCC |
70 | my $sep = $2; |
71 | my $content = $3; | |
bbc249f2 MCC |
72 | |
73 | if (!($new_tag =~ m/(what|date|kernelversion|contact|description|users)/)) { | |
74 | if ($tag eq "description") { | |
4e6a6234 MCC |
75 | # New "tag" is actually part of |
76 | # description. Don't consider it a tag | |
77 | $new_tag = ""; | |
bbc249f2 MCC |
78 | } else { |
79 | parse_error($file, $ln, "tag '$tag' is invalid", $_); | |
80 | } | |
81 | } | |
82 | ||
83 | if ($new_tag =~ m/what/) { | |
4e6a6234 | 84 | $space = ""; |
bbc249f2 MCC |
85 | if ($tag =~ m/what/) { |
86 | $what .= ", " . $content; | |
87 | } else { | |
4e6a6234 MCC |
88 | parse_error($file, $ln, "What '$what' doesn't have a description", "") if ($what && !$data{$what}->{description}); |
89 | ||
bbc249f2 MCC |
90 | $what = $content; |
91 | $new_what = 1; | |
92 | } | |
93 | $tag = $new_tag; | |
6619c661 MCC |
94 | |
95 | if ($has_file) { | |
96 | $label = "abi_" . $content . " "; | |
97 | $label =~ tr/A-Z/a-z/; | |
98 | ||
99 | # Convert special chars to "_" | |
100 | $label =~s/[\x00-\x2f]+/_/g; | |
101 | $label =~s/[\x3a-\x40]+/_/g; | |
102 | $label =~s/[\x7b-\xff]+/_/g; | |
103 | $label =~ s,_+,_,g; | |
104 | $label =~ s,_$,,; | |
105 | ||
106 | $data{$what}->{label} .= $label; | |
107 | ||
108 | # Escape special chars from content | |
109 | $content =~s/([\x00-\x1f\x21-\x2f\x3a-\x40\x7b-\xff])/\\$1/g; | |
110 | ||
111 | $xrefs .= "- :ref:`$content <$label>`\n\n"; | |
112 | } | |
bbc249f2 MCC |
113 | next; |
114 | } | |
115 | ||
4e6a6234 MCC |
116 | if ($new_tag) { |
117 | $tag = $new_tag; | |
bbc249f2 | 118 | |
4e6a6234 MCC |
119 | if ($new_what) { |
120 | $new_what = 0; | |
bbc249f2 | 121 | |
4e6a6234 MCC |
122 | $data{$what}->{type} = $type; |
123 | $data{$what}->{file} = $name; | |
124 | print STDERR "\twhat: $what\n" if ($debug > 1); | |
125 | } | |
bbc249f2 | 126 | |
4e6a6234 MCC |
127 | if (!$what) { |
128 | parse_error($file, $ln, "'What:' should come first:", $_); | |
129 | next; | |
130 | } | |
131 | if ($tag eq "description") { | |
132 | next if ($content =~ m/^\s*$/); | |
133 | if ($content =~ m/^(\s*)(.*)/) { | |
134 | my $new_content = $2; | |
135 | $space = $new_tag . $sep . $1; | |
136 | while ($space =~ s/\t+/' ' x (length($&) * 8 - length($`) % 8)/e) {} | |
137 | $space =~ s/./ /g; | |
138 | $data{$what}->{$tag} .= "$new_content\n"; | |
139 | } | |
140 | } else { | |
141 | $data{$what}->{$tag} = $content; | |
142 | } | |
bbc249f2 MCC |
143 | next; |
144 | } | |
bbc249f2 MCC |
145 | } |
146 | ||
4e6a6234 | 147 | # Store any contents before tags at the database |
6619c661 MCC |
148 | if (!$tag) { |
149 | next if (/^\n/); | |
150 | ||
151 | my $my_what = "File $name"; | |
152 | $data{$my_what}->{what} = "File $name"; | |
153 | $data{$my_what}->{type} = "File"; | |
154 | $data{$my_what}->{file} = $name; | |
155 | $data{$my_what}->{description} .= $_; | |
156 | $has_file = 1; | |
157 | next; | |
158 | } | |
bbc249f2 | 159 | |
4e6a6234 MCC |
160 | if ($tag eq "description") { |
161 | if (!$data{$what}->{description}) { | |
162 | next if (m/^\s*\n/); | |
163 | if (m/^(\s*)(.*)/) { | |
164 | $space = $1; | |
165 | while ($space =~ s/\t+/' ' x (length($&) * 8 - length($`) % 8)/e) {} | |
166 | $data{$what}->{$tag} .= "$2\n"; | |
167 | } | |
168 | } else { | |
169 | my $content = $_; | |
170 | if (m/^\s*\n/) { | |
171 | $data{$what}->{$tag} .= $content; | |
172 | next; | |
173 | } | |
174 | ||
175 | while ($content =~ s/\t+/' ' x (length($&) * 8 - length($`) % 8)/e) {} | |
176 | $space = "" if (!($content =~ s/^($space)//)); | |
177 | ||
178 | # Compress spaces with tabs | |
179 | $content =~ s<^ {8}> <\t>; | |
180 | $content =~ s<^ {1,7}\t> <\t>; | |
181 | $content =~ s< {1,7}\t> <\t>; | |
182 | $data{$what}->{$tag} .= $content; | |
183 | } | |
184 | next; | |
185 | } | |
bbc249f2 MCC |
186 | if (m/^\s*(.*)/) { |
187 | $data{$what}->{$tag} .= "\n$1"; | |
188 | $data{$what}->{$tag} =~ s/\n+$//; | |
189 | next; | |
190 | } | |
191 | ||
192 | # Everything else is error | |
193 | parse_error($file, $ln, "Unexpected line:", $_); | |
194 | } | |
195 | close IN; | |
6619c661 MCC |
196 | |
197 | if ($has_file) { | |
198 | my $my_what = "File $name"; | |
199 | $data{$my_what}->{xrefs} = $xrefs; | |
200 | } | |
bbc249f2 MCC |
201 | } |
202 | ||
203 | # Outputs the output on ReST format | |
204 | sub output_rest { | |
205 | foreach my $what (sort keys %data) { | |
206 | my $type = $data{$what}->{type}; | |
207 | my $file = $data{$what}->{file}; | |
208 | ||
209 | my $w = $what; | |
210 | $w =~ s/([\(\)\_\-\*\=\^\~\\])/\\$1/g; | |
211 | ||
4e6a6234 MCC |
212 | my $bar = $w; |
213 | $bar =~ s/./-/g; | |
214 | ||
6619c661 MCC |
215 | if ($data{$what}->{label}) { |
216 | my @labels = split(/\s/, $data{$what}->{label}); | |
217 | foreach my $label (@labels) { | |
218 | printf ".. _%s:\n\n", $label; | |
219 | } | |
220 | } | |
221 | ||
4e6a6234 | 222 | print "$w\n$bar\n\n"; |
6619c661 MCC |
223 | |
224 | print "- defined on file $file (type: $type)\n\n" if ($type ne "File"); | |
bbc249f2 MCC |
225 | |
226 | my $desc = $data{$what}->{description}; | |
227 | $desc =~ s/^\s+//; | |
228 | ||
229 | # Remove title markups from the description, as they won't work | |
230 | $desc =~ s/\n[\-\*\=\^\~]+\n/\n/g; | |
231 | ||
4e6a6234 MCC |
232 | if (!($desc =~ /^\s*$/)) { |
233 | if ($desc =~ m/\:\n/ || $desc =~ m/\n[\t ]+/ || $desc =~ m/[\x00-\x08\x0b-\x1f\x7b-\xff]/) { | |
234 | # put everything inside a code block | |
235 | $desc =~ s/\n/\n /g; | |
bbc249f2 | 236 | |
4e6a6234 MCC |
237 | print "::\n\n"; |
238 | print " $desc\n\n"; | |
239 | } else { | |
240 | # Escape any special chars from description | |
241 | $desc =~s/([\x00-\x08\x0b-\x1f\x21-\x2a\x2d\x2f\x3c-\x40\x5c\x5e-\x60\x7b-\xff])/\\$1/g; | |
bbc249f2 | 242 | |
4e6a6234 MCC |
243 | print "$desc\n\n"; |
244 | } | |
bbc249f2 | 245 | } else { |
4e6a6234 | 246 | print "DESCRIPTION MISSING for $what\n\n"; |
bbc249f2 | 247 | } |
6619c661 MCC |
248 | |
249 | printf "Has the following ABI:\n\n%s", $data{$what}->{xrefs} if ($data{$what}->{xrefs}); | |
bbc249f2 MCC |
250 | } |
251 | } | |
252 | ||
253 | # | |
254 | # Parses all ABI files located at $prefix dir | |
255 | # | |
256 | find({wanted =>\&parse_abi, no_chdir => 1}, $prefix); | |
257 | ||
258 | print STDERR Data::Dumper->Dump([\%data], [qw(*data)]) if ($debug); | |
259 | ||
260 | # | |
261 | # Outputs an ReST file with the ABI contents | |
262 | # | |
263 | output_rest | |
264 | ||
265 | ||
266 | __END__ | |
267 | ||
268 | =head1 NAME | |
269 | ||
270 | abi_book.pl - parse the Linux ABI files and produce a ReST book. | |
271 | ||
272 | =head1 SYNOPSIS | |
273 | ||
274 | B<abi_book.pl> [--debug] <ABI_DIR>] | |
275 | ||
276 | =head1 OPTIONS | |
277 | ||
278 | =over 8 | |
279 | ||
280 | =item B<--debug> | |
281 | ||
282 | Put the script in verbose mode, useful for debugging. Can be called multiple | |
283 | times, to increase verbosity. | |
284 | ||
285 | =item B<--help> | |
286 | ||
287 | Prints a brief help message and exits. | |
288 | ||
289 | =item B<--man> | |
290 | ||
291 | Prints the manual page and exits. | |
292 | ||
293 | =back | |
294 | ||
295 | =head1 DESCRIPTION | |
296 | ||
297 | Parse the Linux ABI files from ABI DIR (usually located at Documentation/ABI) | |
298 | and produce a ReST book containing the Linux ABI. | |
299 | ||
300 | =head1 BUGS | |
301 | ||
302 | Report bugs to Mauro Carvalho Chehab <mchehab@s-opensource.com> | |
303 | ||
304 | =head1 COPYRIGHT | |
305 | ||
306 | Copyright (c) 2016 by Mauro Carvalho Chehab <mchehab@s-opensource.com>. | |
307 | ||
308 | License GPLv2: GNU GPL version 2 <http://gnu.org/licenses/gpl.html>. | |
309 | ||
310 | This is free software: you are free to change and redistribute it. | |
311 | There is NO WARRANTY, to the extent permitted by law. | |
312 | ||
313 | =cut |