17 pod2usage(1) if $help;
18 pod2usage(-exitstatus => 0, -verbose => 2) if $man;
19 pod2usage(2) if (scalar @ARGV < 2 || scalar @ARGV > 3);
21 my ($file_in, $file_out, $file_exceptions) = @ARGV;
31 require Data::Dumper if ($debug);
34 # read the file and get identifiers
39 open IN, $file_in or die "Can't open $file_in";
45 $ln =~ s,/\*.*(\*/),,g;
47 $is_comment = 1 if ($ln =~ s,/\*.*,,);
49 if ($ln =~ s,^(.*\*/),,) {
56 if ($is_enum && $ln =~ m/^\s*([_\w][\w\d_]+)\s*[\,=]?/) {
62 $enum_symbols{$s} = "\\ :ref:`$s <$n>`\\ ";
64 $is_enum = 0 if ($is_enum && m/\}/);
67 $is_enum = 0 if ($is_enum && m/\}/);
69 if ($ln =~ m/^\s*#\s*define\s+([_\w][\w\d_]+)\s+_IO/) {
74 $ioctls{$s} = "\\ :ref:`$s <$n>`\\ ";
78 if ($ln =~ m/^\s*#\s*define\s+([_\w][\w\d_]+)\s+/) {
84 $defines{$s} = "\\ :ref:`$s <$n>`\\ ";
88 if ($ln =~ m/^\s*typedef\s+([_\w][\w\d_]+)\s+(.*)\s+([_\w][\w\d_]+);/) {
92 $typedefs{$n} = "\\ :c:type:`$n <$s>`\\ ";
95 if ($ln =~ m/^\s*enum\s+([_\w][\w\d_]+)\s+\{/
96 || $ln =~ m/^\s*enum\s+([_\w][\w\d_]+)$/
97 || $ln =~ m/^\s*typedef\s*enum\s+([_\w][\w\d_]+)\s+\{/
98 || $ln =~ m/^\s*typedef\s*enum\s+([_\w][\w\d_]+)$/) {
101 $enums{$s} = "enum :c:type:`$s`\\ ";
106 if ($ln =~ m/^\s*struct\s+([_\w][\w\d_]+)\s+\{/
107 || $ln =~ m/^\s*struct\s+([[_\w][\w\d_]+)$/
108 || $ln =~ m/^\s*typedef\s*struct\s+([_\w][\w\d_]+)\s+\{/
109 || $ln =~ m/^\s*typedef\s*struct\s+([[_\w][\w\d_]+)$/
113 $structs{$s} = "struct :c:type:`$s`\\ ";
120 # Handle multi-line typedefs
123 my @matches = ($data =~ m/typedef\s+struct\s+\S+?\s*\{[^\}]+\}\s*(\S+)\s*\;/g,
124 $data =~ m/typedef\s+enum\s+\S+?\s*\{[^\}]+\}\s*(\S+)\s*\;/g,);
125 foreach my $m (@matches) {
128 $typedefs{$s} = "\\ :c:type:`$s`\\ ";
133 # Handle exceptions, if any
140 "typedef" => ":c:type",
142 "struct" => ":c:type",
145 if ($file_exceptions) {
146 open IN, $file_exceptions or die "Can't read $file_exceptions";
148 next if (m/^\s*$/ || m/^\s*#/);
150 # Parsers to ignore a symbol
152 if (m/^ignore\s+ioctl\s+(\S+)/) {
153 delete $ioctls{$1} if (exists($ioctls{$1}));
156 if (m/^ignore\s+define\s+(\S+)/) {
157 delete $defines{$1} if (exists($defines{$1}));
160 if (m/^ignore\s+typedef\s+(\S+)/) {
161 delete $typedefs{$1} if (exists($typedefs{$1}));
164 if (m/^ignore\s+enum\s+(\S+)/) {
165 delete $enums{$1} if (exists($enums{$1}));
168 if (m/^ignore\s+struct\s+(\S+)/) {
169 delete $structs{$1} if (exists($structs{$1}));
172 if (m/^ignore\s+symbol\s+(\S+)/) {
173 delete $enum_symbols{$1} if (exists($enum_symbols{$1}));
177 # Parsers to replace a symbol
178 my ($type, $old, $new, $reftype);
180 if (m/^replace\s+(\S+)\s+(\S+)\s+(\S+)/) {
185 die "Can't parse $file_exceptions: $_";
188 if ($new =~ m/^\:c\:(data|func|macro|type)\:\`(.+)\`/) {
191 } elsif ($new =~ m/\:ref\:\`(.+)\`/) {
195 $reftype = $def_reftype{$type};
197 $new = "$reftype:`$old <$new>`";
199 if ($type eq "ioctl") {
200 $ioctls{$old} = $new if (exists($ioctls{$old}));
203 if ($type eq "define") {
204 $defines{$old} = $new if (exists($defines{$old}));
207 if ($type eq "symbol") {
208 $enum_symbols{$old} = $new if (exists($enum_symbols{$old}));
211 if ($type eq "typedef") {
212 $typedefs{$old} = $new if (exists($typedefs{$old}));
215 if ($type eq "enum") {
216 $enums{$old} = $new if (exists($enums{$old}));
219 if ($type eq "struct") {
220 $structs{$old} = $new if (exists($structs{$old}));
224 die "Can't parse $file_exceptions: $_";
229 print Data::Dumper->Dump([\%ioctls], [qw(*ioctls)]) if (%ioctls);
230 print Data::Dumper->Dump([\%typedefs], [qw(*typedefs)]) if (%typedefs);
231 print Data::Dumper->Dump([\%enums], [qw(*enums)]) if (%enums);
232 print Data::Dumper->Dump([\%structs], [qw(*structs)]) if (%structs);
233 print Data::Dumper->Dump([\%defines], [qw(*defines)]) if (%defines);
234 print Data::Dumper->Dump([\%enum_symbols], [qw(*enum_symbols)]) if (%enum_symbols);
240 $data = expand($data);
243 $data =~ s/\n\s+$/\n/g;
244 $data =~ s/\n\s+\n/\n\n/g;
247 # Add escape codes for special characters
249 $data =~ s,([\_\`\*\<\>\&\\\\:\/\|\%\$\#\{\}\~\^]),\\$1,g;
251 $data =~ s,DEPRECATED,**DEPRECATED**,g;
257 my $start_delim = "[ \n\t\(\=\*\@]";
258 my $end_delim = "(\\s|,|\\\\=|\\\\:|\\;|\\\)|\\}|\\{)";
260 foreach my $r (keys %ioctls) {
263 $r =~ s,([\_\`\*\<\>\&\\\\:\/]),\\\\$1,g;
265 print "$r -> $s\n" if ($debug);
267 $data =~ s/($start_delim)($r)$end_delim/$1$s$3/g;
270 foreach my $r (keys %defines) {
271 my $s = $defines{$r};
273 $r =~ s,([\_\`\*\<\>\&\\\\:\/]),\\\\$1,g;
275 print "$r -> $s\n" if ($debug);
277 $data =~ s/($start_delim)($r)$end_delim/$1$s$3/g;
280 foreach my $r (keys %enum_symbols) {
281 my $s = $enum_symbols{$r};
283 $r =~ s,([\_\`\*\<\>\&\\\\:\/]),\\\\$1,g;
285 print "$r -> $s\n" if ($debug);
287 $data =~ s/($start_delim)($r)$end_delim/$1$s$3/g;
290 foreach my $r (keys %enums) {
293 $r =~ s,([\_\`\*\<\>\&\\\\:\/]),\\\\$1,g;
295 print "$r -> $s\n" if ($debug);
297 $data =~ s/enum\s+($r)$end_delim/$s$2/g;
300 foreach my $r (keys %structs) {
301 my $s = $structs{$r};
303 $r =~ s,([\_\`\*\<\>\&\\\\:\/]),\\\\$1,g;
305 print "$r -> $s\n" if ($debug);
307 $data =~ s/struct\s+($r)$end_delim/$s$2/g;
310 foreach my $r (keys %typedefs) {
311 my $s = $typedefs{$r};
313 $r =~ s,([\_\`\*\<\>\&\\\\:\/]),\\\\$1,g;
315 print "$r -> $s\n" if ($debug);
316 $data =~ s/($start_delim)($r)$end_delim/$1$s$3/g;
319 $data =~ s/\\ ([\n\s])/\1/g;
322 # Generate output file
325 my $title = $file_in;
328 open OUT, "> $file_out" or die "Can't open $file_out";
329 print OUT ".. -*- coding: utf-8; mode: rst -*-\n\n";
330 print OUT "$title\n";
331 print OUT "=" x length($title);
332 print OUT "\n\n.. parsed-literal::\n\n";
340 parse_headers.pl - parse a C file, in order to identify functions, structs,
341 enums and defines and create cross-references to a Sphinx book.
345 B<parse_headers.pl> [<options>] <C_FILE> <OUT_FILE> [<EXCEPTIONS_FILE>]
347 Where <options> can be: --debug, --help or --man.
355 Put the script in verbose mode, useful for debugging.
359 Prints a brief help message and exits.
363 Prints the manual page and exits.
369 Convert a C header or source file (C_FILE), into a ReStructured Text
370 included via ..parsed-literal block with cross-references for the
371 documentation files that describe the API. It accepts an optional
372 EXCEPTIONS_FILE with describes what elements will be either ignored or
373 be pointed to a non-default reference.
375 The output is written at the (OUT_FILE).
377 It is capable of identifying defines, functions, structs, typedefs,
378 enums and enum symbols and create cross-references for all of them.
379 It is also capable of distinguish #define used for specifying a Linux
382 The EXCEPTIONS_FILE contain two types of statements: B<ignore> or B<replace>.
384 The syntax for the ignore tag is:
388 ignore B<type> B<name>
392 The B<ignore> means that it won't generate cross references for a
393 B<name> symbol of type B<type>.
395 The syntax for the replace tag is:
399 replace B<type> B<name> B<new_value>
403 The B<replace> means that it will generate cross references for a
404 B<name> symbol of type B<type>, but, instead of using the default
405 replacement rule, it will use B<new_value>.
407 For both statements, B<type> can be either one of the following:
413 The ignore or replace statement will apply to ioctl definitions like:
415 #define VIDIOC_DBG_S_REGISTER _IOW('V', 79, struct v4l2_dbg_register)
419 The ignore or replace statement will apply to any other #define found
424 The ignore or replace statement will apply to typedef statements at C_FILE.
428 The ignore or replace statement will apply to the name of struct statements
433 The ignore or replace statement will apply to the name of enum statements
438 The ignore or replace statement will apply to the name of enum statements
442 For replace statements, B<new_value> will automatically use :c:type:
443 references for B<typedef>, B<enum> and B<struct> types. It will use :ref:
444 for B<ioctl>, B<define> and B<symbol> types. The type of reference can
445 also be explicitly defined at the replace statement.
451 ignore define _VIDEODEV2_H
456 Ignore a #define _VIDEODEV2_H at the C_FILE.
460 ignore symbol PRIVATE
466 enum foo { BAR1, BAR2, PRIVATE };
468 It won't generate cross-references for B<PRIVATE>.
472 replace symbol BAR1 :c:type:`foo`
473 replace symbol BAR2 :c:type:`foo`
479 enum foo { BAR1, BAR2, PRIVATE };
481 It will make the BAR1 and BAR2 enum symbols to cross reference the foo
482 symbol at the C domain.
488 Report bugs to Mauro Carvalho Chehab <mchehab@s-opensource.com>
492 Copyright (c) 2016 by Mauro Carvalho Chehab <mchehab@s-opensource.com>.
494 License GPLv2: GNU GPL version 2 <http://gnu.org/licenses/gpl.html>.
496 This is free software: you are free to change and redistribute it.
497 There is NO WARRANTY, to the extent permitted by law.