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; | |
bbc249f2 MCC |
62 | |
63 | print STDERR "Opening $file\n" if ($debug > 1); | |
64 | open IN, $file; | |
65 | while(<IN>) { | |
66 | $ln++; | |
67 | if (m/^(\S+):\s*(.*)/i) { | |
68 | my $new_tag = lc($1); | |
69 | my $content = $2; | |
70 | ||
71 | if (!($new_tag =~ m/(what|date|kernelversion|contact|description|users)/)) { | |
72 | if ($tag eq "description") { | |
6619c661 | 73 | $data{$what}->{$tag} .= "\n$content"; |
bbc249f2 MCC |
74 | $data{$what}->{$tag} =~ s/\n+$//; |
75 | next; | |
76 | } else { | |
77 | parse_error($file, $ln, "tag '$tag' is invalid", $_); | |
78 | } | |
79 | } | |
80 | ||
81 | if ($new_tag =~ m/what/) { | |
82 | if ($tag =~ m/what/) { | |
83 | $what .= ", " . $content; | |
84 | } else { | |
85 | $what = $content; | |
86 | $new_what = 1; | |
87 | } | |
88 | $tag = $new_tag; | |
6619c661 MCC |
89 | |
90 | if ($has_file) { | |
91 | $label = "abi_" . $content . " "; | |
92 | $label =~ tr/A-Z/a-z/; | |
93 | ||
94 | # Convert special chars to "_" | |
95 | $label =~s/[\x00-\x2f]+/_/g; | |
96 | $label =~s/[\x3a-\x40]+/_/g; | |
97 | $label =~s/[\x7b-\xff]+/_/g; | |
98 | $label =~ s,_+,_,g; | |
99 | $label =~ s,_$,,; | |
100 | ||
101 | $data{$what}->{label} .= $label; | |
102 | ||
103 | # Escape special chars from content | |
104 | $content =~s/([\x00-\x1f\x21-\x2f\x3a-\x40\x7b-\xff])/\\$1/g; | |
105 | ||
106 | $xrefs .= "- :ref:`$content <$label>`\n\n"; | |
107 | } | |
bbc249f2 MCC |
108 | next; |
109 | } | |
110 | ||
111 | $tag = $new_tag; | |
112 | ||
113 | if ($new_what) { | |
114 | $new_what = 0; | |
115 | ||
116 | $data{$what}->{type} = $type; | |
117 | $data{$what}->{file} = $name; | |
118 | print STDERR "\twhat: $what\n" if ($debug > 1); | |
119 | } | |
120 | ||
121 | if (!$what) { | |
122 | parse_error($file, $ln, "'What:' should come first:", $_); | |
123 | next; | |
124 | } | |
125 | $data{$what}->{$tag} = $content; | |
126 | next; | |
127 | } | |
128 | ||
6619c661 MCC |
129 | # Store any contents before the database |
130 | if (!$tag) { | |
131 | next if (/^\n/); | |
132 | ||
133 | my $my_what = "File $name"; | |
134 | $data{$my_what}->{what} = "File $name"; | |
135 | $data{$my_what}->{type} = "File"; | |
136 | $data{$my_what}->{file} = $name; | |
137 | $data{$my_what}->{description} .= $_; | |
138 | $has_file = 1; | |
139 | next; | |
140 | } | |
bbc249f2 MCC |
141 | |
142 | if (m/^\s*(.*)/) { | |
143 | $data{$what}->{$tag} .= "\n$1"; | |
144 | $data{$what}->{$tag} =~ s/\n+$//; | |
145 | next; | |
146 | } | |
147 | ||
148 | # Everything else is error | |
149 | parse_error($file, $ln, "Unexpected line:", $_); | |
150 | } | |
151 | close IN; | |
6619c661 MCC |
152 | |
153 | if ($has_file) { | |
154 | my $my_what = "File $name"; | |
155 | $data{$my_what}->{xrefs} = $xrefs; | |
156 | } | |
bbc249f2 MCC |
157 | } |
158 | ||
159 | # Outputs the output on ReST format | |
160 | sub output_rest { | |
161 | foreach my $what (sort keys %data) { | |
162 | my $type = $data{$what}->{type}; | |
163 | my $file = $data{$what}->{file}; | |
164 | ||
165 | my $w = $what; | |
166 | $w =~ s/([\(\)\_\-\*\=\^\~\\])/\\$1/g; | |
167 | ||
6619c661 MCC |
168 | if ($data{$what}->{label}) { |
169 | my @labels = split(/\s/, $data{$what}->{label}); | |
170 | foreach my $label (@labels) { | |
171 | printf ".. _%s:\n\n", $label; | |
172 | } | |
173 | } | |
174 | ||
bbc249f2 | 175 | print "$w\n\n"; |
6619c661 MCC |
176 | |
177 | print "- defined on file $file (type: $type)\n\n" if ($type ne "File"); | |
178 | print "::\n\n"; | |
bbc249f2 MCC |
179 | |
180 | my $desc = $data{$what}->{description}; | |
181 | $desc =~ s/^\s+//; | |
182 | ||
183 | # Remove title markups from the description, as they won't work | |
184 | $desc =~ s/\n[\-\*\=\^\~]+\n/\n/g; | |
185 | ||
186 | # put everything inside a code block | |
187 | $desc =~ s/\n/\n /g; | |
188 | ||
189 | ||
190 | if (!($desc =~ /^\s*$/)) { | |
191 | print " $desc\n\n"; | |
192 | } else { | |
6619c661 | 193 | print " DESCRIPTION MISSING for $what\n\n"; |
bbc249f2 | 194 | } |
6619c661 MCC |
195 | |
196 | printf "Has the following ABI:\n\n%s", $data{$what}->{xrefs} if ($data{$what}->{xrefs}); | |
197 | ||
bbc249f2 MCC |
198 | } |
199 | } | |
200 | ||
201 | # | |
202 | # Parses all ABI files located at $prefix dir | |
203 | # | |
204 | find({wanted =>\&parse_abi, no_chdir => 1}, $prefix); | |
205 | ||
206 | print STDERR Data::Dumper->Dump([\%data], [qw(*data)]) if ($debug); | |
207 | ||
208 | # | |
209 | # Outputs an ReST file with the ABI contents | |
210 | # | |
211 | output_rest | |
212 | ||
213 | ||
214 | __END__ | |
215 | ||
216 | =head1 NAME | |
217 | ||
218 | abi_book.pl - parse the Linux ABI files and produce a ReST book. | |
219 | ||
220 | =head1 SYNOPSIS | |
221 | ||
222 | B<abi_book.pl> [--debug] <ABI_DIR>] | |
223 | ||
224 | =head1 OPTIONS | |
225 | ||
226 | =over 8 | |
227 | ||
228 | =item B<--debug> | |
229 | ||
230 | Put the script in verbose mode, useful for debugging. Can be called multiple | |
231 | times, to increase verbosity. | |
232 | ||
233 | =item B<--help> | |
234 | ||
235 | Prints a brief help message and exits. | |
236 | ||
237 | =item B<--man> | |
238 | ||
239 | Prints the manual page and exits. | |
240 | ||
241 | =back | |
242 | ||
243 | =head1 DESCRIPTION | |
244 | ||
245 | Parse the Linux ABI files from ABI DIR (usually located at Documentation/ABI) | |
246 | and produce a ReST book containing the Linux ABI. | |
247 | ||
248 | =head1 BUGS | |
249 | ||
250 | Report bugs to Mauro Carvalho Chehab <mchehab@s-opensource.com> | |
251 | ||
252 | =head1 COPYRIGHT | |
253 | ||
254 | Copyright (c) 2016 by Mauro Carvalho Chehab <mchehab@s-opensource.com>. | |
255 | ||
256 | License GPLv2: GNU GPL version 2 <http://gnu.org/licenses/gpl.html>. | |
257 | ||
258 | This is free software: you are free to change and redistribute it. | |
259 | There is NO WARRANTY, to the extent permitted by law. | |
260 | ||
261 | =cut |