Back to home page

OSCL-LXR

 
 

    


0001 #!/usr/bin/env perl
0002 use strict;
0003 use Text::Tabs;
0004 use Getopt::Long;
0005 use Pod::Usage;
0006 
0007 my $debug;
0008 my $help;
0009 my $man;
0010 
0011 GetOptions(
0012     "debug" => \$debug,
0013     'usage|?' => \$help,
0014     'help' => \$man
0015 ) or pod2usage(2);
0016 
0017 pod2usage(1) if $help;
0018 pod2usage(-exitstatus => 0, -verbose => 2) if $man;
0019 pod2usage(2) if (scalar @ARGV < 2 || scalar @ARGV > 3);
0020 
0021 my ($file_in, $file_out, $file_exceptions) = @ARGV;
0022 
0023 my $data;
0024 my %ioctls;
0025 my %defines;
0026 my %typedefs;
0027 my %enums;
0028 my %enum_symbols;
0029 my %structs;
0030 
0031 require Data::Dumper if ($debug);
0032 
0033 #
0034 # read the file and get identifiers
0035 #
0036 
0037 my $is_enum = 0;
0038 my $is_comment = 0;
0039 open IN, $file_in or die "Can't open $file_in";
0040 while (<IN>) {
0041     $data .= $_;
0042 
0043     my $ln = $_;
0044     if (!$is_comment) {
0045         $ln =~ s,/\*.*(\*/),,g;
0046 
0047         $is_comment = 1 if ($ln =~ s,/\*.*,,);
0048     } else {
0049         if ($ln =~ s,^(.*\*/),,) {
0050             $is_comment = 0;
0051         } else {
0052             next;
0053         }
0054     }
0055 
0056     if ($is_enum && $ln =~ m/^\s*([_\w][\w\d_]+)\s*[\,=]?/) {
0057         my $s = $1;
0058         my $n = $1;
0059         $n =~ tr/A-Z/a-z/;
0060         $n =~ tr/_/-/;
0061 
0062         $enum_symbols{$s} =  "\\ :ref:`$s <$n>`\\ ";
0063 
0064         $is_enum = 0 if ($is_enum && m/\}/);
0065         next;
0066     }
0067     $is_enum = 0 if ($is_enum && m/\}/);
0068 
0069     if ($ln =~ m/^\s*#\s*define\s+([_\w][\w\d_]+)\s+_IO/) {
0070         my $s = $1;
0071         my $n = $1;
0072         $n =~ tr/A-Z/a-z/;
0073 
0074         $ioctls{$s} = "\\ :ref:`$s <$n>`\\ ";
0075         next;
0076     }
0077 
0078     if ($ln =~ m/^\s*#\s*define\s+([_\w][\w\d_]+)\s+/) {
0079         my $s = $1;
0080         my $n = $1;
0081         $n =~ tr/A-Z/a-z/;
0082         $n =~ tr/_/-/;
0083 
0084         $defines{$s} = "\\ :ref:`$s <$n>`\\ ";
0085         next;
0086     }
0087 
0088     if ($ln =~ m/^\s*typedef\s+([_\w][\w\d_]+)\s+(.*)\s+([_\w][\w\d_]+);/) {
0089         my $s = $2;
0090         my $n = $3;
0091 
0092         $typedefs{$n} = "\\ :c:type:`$n <$s>`\\ ";
0093         next;
0094     }
0095     if ($ln =~ m/^\s*enum\s+([_\w][\w\d_]+)\s+\{/
0096         || $ln =~ m/^\s*enum\s+([_\w][\w\d_]+)$/
0097         || $ln =~ m/^\s*typedef\s*enum\s+([_\w][\w\d_]+)\s+\{/
0098         || $ln =~ m/^\s*typedef\s*enum\s+([_\w][\w\d_]+)$/) {
0099         my $s = $1;
0100 
0101         $enums{$s} =  "enum :c:type:`$s`\\ ";
0102 
0103         $is_enum = $1;
0104         next;
0105     }
0106     if ($ln =~ m/^\s*struct\s+([_\w][\w\d_]+)\s+\{/
0107         || $ln =~ m/^\s*struct\s+([[_\w][\w\d_]+)$/
0108         || $ln =~ m/^\s*typedef\s*struct\s+([_\w][\w\d_]+)\s+\{/
0109         || $ln =~ m/^\s*typedef\s*struct\s+([[_\w][\w\d_]+)$/
0110         ) {
0111         my $s = $1;
0112 
0113         $structs{$s} = "struct $s\\ ";
0114         next;
0115     }
0116 }
0117 close IN;
0118 
0119 #
0120 # Handle multi-line typedefs
0121 #
0122 
0123 my @matches = ($data =~ m/typedef\s+struct\s+\S+?\s*\{[^\}]+\}\s*(\S+)\s*\;/g,
0124            $data =~ m/typedef\s+enum\s+\S+?\s*\{[^\}]+\}\s*(\S+)\s*\;/g,);
0125 foreach my $m (@matches) {
0126     my $s = $m;
0127 
0128     $typedefs{$s} = "\\ :c:type:`$s`\\ ";
0129     next;
0130 }
0131 
0132 #
0133 # Handle exceptions, if any
0134 #
0135 
0136 my %def_reftype = (
0137     "ioctl"   => ":ref",
0138     "define"  => ":ref",
0139     "symbol"  => ":ref",
0140     "typedef" => ":c:type",
0141     "enum"    => ":c:type",
0142     "struct"  => ":c:type",
0143 );
0144 
0145 if ($file_exceptions) {
0146     open IN, $file_exceptions or die "Can't read $file_exceptions";
0147     while (<IN>) {
0148         next if (m/^\s*$/ || m/^\s*#/);
0149 
0150         # Parsers to ignore a symbol
0151 
0152         if (m/^ignore\s+ioctl\s+(\S+)/) {
0153             delete $ioctls{$1} if (exists($ioctls{$1}));
0154             next;
0155         }
0156         if (m/^ignore\s+define\s+(\S+)/) {
0157             delete $defines{$1} if (exists($defines{$1}));
0158             next;
0159         }
0160         if (m/^ignore\s+typedef\s+(\S+)/) {
0161             delete $typedefs{$1} if (exists($typedefs{$1}));
0162             next;
0163         }
0164         if (m/^ignore\s+enum\s+(\S+)/) {
0165             delete $enums{$1} if (exists($enums{$1}));
0166             next;
0167         }
0168         if (m/^ignore\s+struct\s+(\S+)/) {
0169             delete $structs{$1} if (exists($structs{$1}));
0170             next;
0171         }
0172         if (m/^ignore\s+symbol\s+(\S+)/) {
0173             delete $enum_symbols{$1} if (exists($enum_symbols{$1}));
0174             next;
0175         }
0176 
0177         # Parsers to replace a symbol
0178         my ($type, $old, $new, $reftype);
0179 
0180         if (m/^replace\s+(\S+)\s+(\S+)\s+(\S+)/) {
0181             $type = $1;
0182             $old = $2;
0183             $new = $3;
0184         } else {
0185             die "Can't parse $file_exceptions: $_";
0186         }
0187 
0188         if ($new =~ m/^\:c\:(data|func|macro|type)\:\`(.+)\`/) {
0189             $reftype = ":c:$1";
0190             $new = $2;
0191         } elsif ($new =~ m/\:ref\:\`(.+)\`/) {
0192             $reftype = ":ref";
0193             $new = $1;
0194         } else {
0195             $reftype = $def_reftype{$type};
0196         }
0197         $new = "$reftype:`$old <$new>`";
0198 
0199         if ($type eq "ioctl") {
0200             $ioctls{$old} = $new if (exists($ioctls{$old}));
0201             next;
0202         }
0203         if ($type eq "define") {
0204             $defines{$old} = $new if (exists($defines{$old}));
0205             next;
0206         }
0207         if ($type eq "symbol") {
0208             $enum_symbols{$old} = $new if (exists($enum_symbols{$old}));
0209             next;
0210         }
0211         if ($type eq "typedef") {
0212             $typedefs{$old} = $new if (exists($typedefs{$old}));
0213             next;
0214         }
0215         if ($type eq "enum") {
0216             $enums{$old} = $new if (exists($enums{$old}));
0217             next;
0218         }
0219         if ($type eq "struct") {
0220             $structs{$old} = $new if (exists($structs{$old}));
0221             next;
0222         }
0223 
0224         die "Can't parse $file_exceptions: $_";
0225     }
0226 }
0227 
0228 if ($debug) {
0229     print Data::Dumper->Dump([\%ioctls], [qw(*ioctls)]) if (%ioctls);
0230     print Data::Dumper->Dump([\%typedefs], [qw(*typedefs)]) if (%typedefs);
0231     print Data::Dumper->Dump([\%enums], [qw(*enums)]) if (%enums);
0232     print Data::Dumper->Dump([\%structs], [qw(*structs)]) if (%structs);
0233     print Data::Dumper->Dump([\%defines], [qw(*defines)]) if (%defines);
0234     print Data::Dumper->Dump([\%enum_symbols], [qw(*enum_symbols)]) if (%enum_symbols);
0235 }
0236 
0237 #
0238 # Align block
0239 #
0240 $data = expand($data);
0241 $data = "    " . $data;
0242 $data =~ s/\n/\n    /g;
0243 $data =~ s/\n\s+$/\n/g;
0244 $data =~ s/\n\s+\n/\n\n/g;
0245 
0246 #
0247 # Add escape codes for special characters
0248 #
0249 $data =~ s,([\_\`\*\<\>\&\\\\:\/\|\%\$\#\{\}\~\^]),\\$1,g;
0250 
0251 $data =~ s,DEPRECATED,**DEPRECATED**,g;
0252 
0253 #
0254 # Add references
0255 #
0256 
0257 my $start_delim = "[ \n\t\(\=\*\@]";
0258 my $end_delim = "(\\s|,|\\\\=|\\\\:|\\;|\\\)|\\}|\\{)";
0259 
0260 foreach my $r (keys %ioctls) {
0261     my $s = $ioctls{$r};
0262 
0263     $r =~ s,([\_\`\*\<\>\&\\\\:\/]),\\\\$1,g;
0264 
0265     print "$r -> $s\n" if ($debug);
0266 
0267     $data =~ s/($start_delim)($r)$end_delim/$1$s$3/g;
0268 }
0269 
0270 foreach my $r (keys %defines) {
0271     my $s = $defines{$r};
0272 
0273     $r =~ s,([\_\`\*\<\>\&\\\\:\/]),\\\\$1,g;
0274 
0275     print "$r -> $s\n" if ($debug);
0276 
0277     $data =~ s/($start_delim)($r)$end_delim/$1$s$3/g;
0278 }
0279 
0280 foreach my $r (keys %enum_symbols) {
0281     my $s = $enum_symbols{$r};
0282 
0283     $r =~ s,([\_\`\*\<\>\&\\\\:\/]),\\\\$1,g;
0284 
0285     print "$r -> $s\n" if ($debug);
0286 
0287     $data =~ s/($start_delim)($r)$end_delim/$1$s$3/g;
0288 }
0289 
0290 foreach my $r (keys %enums) {
0291     my $s = $enums{$r};
0292 
0293     $r =~ s,([\_\`\*\<\>\&\\\\:\/]),\\\\$1,g;
0294 
0295     print "$r -> $s\n" if ($debug);
0296 
0297     $data =~ s/enum\s+($r)$end_delim/$s$2/g;
0298 }
0299 
0300 foreach my $r (keys %structs) {
0301     my $s = $structs{$r};
0302 
0303     $r =~ s,([\_\`\*\<\>\&\\\\:\/]),\\\\$1,g;
0304 
0305     print "$r -> $s\n" if ($debug);
0306 
0307     $data =~ s/struct\s+($r)$end_delim/$s$2/g;
0308 }
0309 
0310 foreach my $r (keys %typedefs) {
0311     my $s = $typedefs{$r};
0312 
0313     $r =~ s,([\_\`\*\<\>\&\\\\:\/]),\\\\$1,g;
0314 
0315     print "$r -> $s\n" if ($debug);
0316     $data =~ s/($start_delim)($r)$end_delim/$1$s$3/g;
0317 }
0318 
0319 $data =~ s/\\ ([\n\s])/\1/g;
0320 
0321 #
0322 # Generate output file
0323 #
0324 
0325 my $title = $file_in;
0326 $title =~ s,.*/,,;
0327 
0328 open OUT, "> $file_out" or die "Can't open $file_out";
0329 print OUT ".. -*- coding: utf-8; mode: rst -*-\n\n";
0330 print OUT "$title\n";
0331 print OUT "=" x length($title);
0332 print OUT "\n\n.. parsed-literal::\n\n";
0333 print OUT $data;
0334 close OUT;
0335 
0336 __END__
0337 
0338 =head1 NAME
0339 
0340 parse_headers.pl - parse a C file, in order to identify functions, structs,
0341 enums and defines and create cross-references to a Sphinx book.
0342 
0343 =head1 SYNOPSIS
0344 
0345 B<parse_headers.pl> [<options>] <C_FILE> <OUT_FILE> [<EXCEPTIONS_FILE>]
0346 
0347 Where <options> can be: --debug, --help or --usage.
0348 
0349 =head1 OPTIONS
0350 
0351 =over 8
0352 
0353 =item B<--debug>
0354 
0355 Put the script in verbose mode, useful for debugging.
0356 
0357 =item B<--usage>
0358 
0359 Prints a brief help message and exits.
0360 
0361 =item B<--help>
0362 
0363 Prints a more detailed help message and exits.
0364 
0365 =back
0366 
0367 =head1 DESCRIPTION
0368 
0369 Convert a C header or source file (C_FILE), into a ReStructured Text
0370 included via ..parsed-literal block with cross-references for the
0371 documentation files that describe the API. It accepts an optional
0372 EXCEPTIONS_FILE with describes what elements will be either ignored or
0373 be pointed to a non-default reference.
0374 
0375 The output is written at the (OUT_FILE).
0376 
0377 It is capable of identifying defines, functions, structs, typedefs,
0378 enums and enum symbols and create cross-references for all of them.
0379 It is also capable of distinguish #define used for specifying a Linux
0380 ioctl.
0381 
0382 The EXCEPTIONS_FILE contain two rules to allow ignoring a symbol or
0383 to replace the default references by a custom one.
0384 
0385 Please read Documentation/doc-guide/parse-headers.rst at the Kernel's
0386 tree for more details.
0387 
0388 =head1 BUGS
0389 
0390 Report bugs to Mauro Carvalho Chehab <mchehab@kernel.org>
0391 
0392 =head1 COPYRIGHT
0393 
0394 Copyright (c) 2016 by Mauro Carvalho Chehab <mchehab+samsung@kernel.org>.
0395 
0396 License GPLv2: GNU GPL version 2 <https://gnu.org/licenses/gpl.html>.
0397 
0398 This is free software: you are free to change and redistribute it.
0399 There is NO WARRANTY, to the extent permitted by law.
0400 
0401 =cut