parse-headers.pl 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401
  1. #!/usr/bin/env perl
  2. use strict;
  3. use Text::Tabs;
  4. use Getopt::Long;
  5. use Pod::Usage;
  6. my $debug;
  7. my $help;
  8. my $man;
  9. GetOptions(
  10. "debug" => \$debug,
  11. 'usage|?' => \$help,
  12. 'help' => \$man
  13. ) or pod2usage(2);
  14. pod2usage(1) if $help;
  15. pod2usage(-exitstatus => 0, -verbose => 2) if $man;
  16. pod2usage(2) if (scalar @ARGV < 2 || scalar @ARGV > 3);
  17. my ($file_in, $file_out, $file_exceptions) = @ARGV;
  18. my $data;
  19. my %ioctls;
  20. my %defines;
  21. my %typedefs;
  22. my %enums;
  23. my %enum_symbols;
  24. my %structs;
  25. require Data::Dumper if ($debug);
  26. #
  27. # read the file and get identifiers
  28. #
  29. my $is_enum = 0;
  30. my $is_comment = 0;
  31. open IN, $file_in or die "Can't open $file_in";
  32. while (<IN>) {
  33. $data .= $_;
  34. my $ln = $_;
  35. if (!$is_comment) {
  36. $ln =~ s,/\*.*(\*/),,g;
  37. $is_comment = 1 if ($ln =~ s,/\*.*,,);
  38. } else {
  39. if ($ln =~ s,^(.*\*/),,) {
  40. $is_comment = 0;
  41. } else {
  42. next;
  43. }
  44. }
  45. if ($is_enum && $ln =~ m/^\s*([_\w][\w\d_]+)\s*[\,=]?/) {
  46. my $s = $1;
  47. my $n = $1;
  48. $n =~ tr/A-Z/a-z/;
  49. $n =~ tr/_/-/;
  50. $enum_symbols{$s} = "\\ :ref:`$s <$n>`\\ ";
  51. $is_enum = 0 if ($is_enum && m/\}/);
  52. next;
  53. }
  54. $is_enum = 0 if ($is_enum && m/\}/);
  55. if ($ln =~ m/^\s*#\s*define\s+([_\w][\w\d_]+)\s+_IO/) {
  56. my $s = $1;
  57. my $n = $1;
  58. $n =~ tr/A-Z/a-z/;
  59. $ioctls{$s} = "\\ :ref:`$s <$n>`\\ ";
  60. next;
  61. }
  62. if ($ln =~ m/^\s*#\s*define\s+([_\w][\w\d_]+)\s+/) {
  63. my $s = $1;
  64. my $n = $1;
  65. $n =~ tr/A-Z/a-z/;
  66. $n =~ tr/_/-/;
  67. $defines{$s} = "\\ :ref:`$s <$n>`\\ ";
  68. next;
  69. }
  70. if ($ln =~ m/^\s*typedef\s+([_\w][\w\d_]+)\s+(.*)\s+([_\w][\w\d_]+);/) {
  71. my $s = $2;
  72. my $n = $3;
  73. $typedefs{$n} = "\\ :c:type:`$n <$s>`\\ ";
  74. next;
  75. }
  76. if ($ln =~ m/^\s*enum\s+([_\w][\w\d_]+)\s+\{/
  77. || $ln =~ m/^\s*enum\s+([_\w][\w\d_]+)$/
  78. || $ln =~ m/^\s*typedef\s*enum\s+([_\w][\w\d_]+)\s+\{/
  79. || $ln =~ m/^\s*typedef\s*enum\s+([_\w][\w\d_]+)$/) {
  80. my $s = $1;
  81. $enums{$s} = "enum :c:type:`$s`\\ ";
  82. $is_enum = $1;
  83. next;
  84. }
  85. if ($ln =~ m/^\s*struct\s+([_\w][\w\d_]+)\s+\{/
  86. || $ln =~ m/^\s*struct\s+([[_\w][\w\d_]+)$/
  87. || $ln =~ m/^\s*typedef\s*struct\s+([_\w][\w\d_]+)\s+\{/
  88. || $ln =~ m/^\s*typedef\s*struct\s+([[_\w][\w\d_]+)$/
  89. ) {
  90. my $s = $1;
  91. $structs{$s} = "struct $s\\ ";
  92. next;
  93. }
  94. }
  95. close IN;
  96. #
  97. # Handle multi-line typedefs
  98. #
  99. my @matches = ($data =~ m/typedef\s+struct\s+\S+?\s*\{[^\}]+\}\s*(\S+)\s*\;/g,
  100. $data =~ m/typedef\s+enum\s+\S+?\s*\{[^\}]+\}\s*(\S+)\s*\;/g,);
  101. foreach my $m (@matches) {
  102. my $s = $m;
  103. $typedefs{$s} = "\\ :c:type:`$s`\\ ";
  104. next;
  105. }
  106. #
  107. # Handle exceptions, if any
  108. #
  109. my %def_reftype = (
  110. "ioctl" => ":ref",
  111. "define" => ":ref",
  112. "symbol" => ":ref",
  113. "typedef" => ":c:type",
  114. "enum" => ":c:type",
  115. "struct" => ":c:type",
  116. );
  117. if ($file_exceptions) {
  118. open IN, $file_exceptions or die "Can't read $file_exceptions";
  119. while (<IN>) {
  120. next if (m/^\s*$/ || m/^\s*#/);
  121. # Parsers to ignore a symbol
  122. if (m/^ignore\s+ioctl\s+(\S+)/) {
  123. delete $ioctls{$1} if (exists($ioctls{$1}));
  124. next;
  125. }
  126. if (m/^ignore\s+define\s+(\S+)/) {
  127. delete $defines{$1} if (exists($defines{$1}));
  128. next;
  129. }
  130. if (m/^ignore\s+typedef\s+(\S+)/) {
  131. delete $typedefs{$1} if (exists($typedefs{$1}));
  132. next;
  133. }
  134. if (m/^ignore\s+enum\s+(\S+)/) {
  135. delete $enums{$1} if (exists($enums{$1}));
  136. next;
  137. }
  138. if (m/^ignore\s+struct\s+(\S+)/) {
  139. delete $structs{$1} if (exists($structs{$1}));
  140. next;
  141. }
  142. if (m/^ignore\s+symbol\s+(\S+)/) {
  143. delete $enum_symbols{$1} if (exists($enum_symbols{$1}));
  144. next;
  145. }
  146. # Parsers to replace a symbol
  147. my ($type, $old, $new, $reftype);
  148. if (m/^replace\s+(\S+)\s+(\S+)\s+(\S+)/) {
  149. $type = $1;
  150. $old = $2;
  151. $new = $3;
  152. } else {
  153. die "Can't parse $file_exceptions: $_";
  154. }
  155. if ($new =~ m/^\:c\:(data|func|macro|type)\:\`(.+)\`/) {
  156. $reftype = ":c:$1";
  157. $new = $2;
  158. } elsif ($new =~ m/\:ref\:\`(.+)\`/) {
  159. $reftype = ":ref";
  160. $new = $1;
  161. } else {
  162. $reftype = $def_reftype{$type};
  163. }
  164. $new = "$reftype:`$old <$new>`";
  165. if ($type eq "ioctl") {
  166. $ioctls{$old} = $new if (exists($ioctls{$old}));
  167. next;
  168. }
  169. if ($type eq "define") {
  170. $defines{$old} = $new if (exists($defines{$old}));
  171. next;
  172. }
  173. if ($type eq "symbol") {
  174. $enum_symbols{$old} = $new if (exists($enum_symbols{$old}));
  175. next;
  176. }
  177. if ($type eq "typedef") {
  178. $typedefs{$old} = $new if (exists($typedefs{$old}));
  179. next;
  180. }
  181. if ($type eq "enum") {
  182. $enums{$old} = $new if (exists($enums{$old}));
  183. next;
  184. }
  185. if ($type eq "struct") {
  186. $structs{$old} = $new if (exists($structs{$old}));
  187. next;
  188. }
  189. die "Can't parse $file_exceptions: $_";
  190. }
  191. }
  192. if ($debug) {
  193. print Data::Dumper->Dump([\%ioctls], [qw(*ioctls)]) if (%ioctls);
  194. print Data::Dumper->Dump([\%typedefs], [qw(*typedefs)]) if (%typedefs);
  195. print Data::Dumper->Dump([\%enums], [qw(*enums)]) if (%enums);
  196. print Data::Dumper->Dump([\%structs], [qw(*structs)]) if (%structs);
  197. print Data::Dumper->Dump([\%defines], [qw(*defines)]) if (%defines);
  198. print Data::Dumper->Dump([\%enum_symbols], [qw(*enum_symbols)]) if (%enum_symbols);
  199. }
  200. #
  201. # Align block
  202. #
  203. $data = expand($data);
  204. $data = " " . $data;
  205. $data =~ s/\n/\n /g;
  206. $data =~ s/\n\s+$/\n/g;
  207. $data =~ s/\n\s+\n/\n\n/g;
  208. #
  209. # Add escape codes for special characters
  210. #
  211. $data =~ s,([\_\`\*\<\>\&\\\\:\/\|\%\$\#\{\}\~\^]),\\$1,g;
  212. $data =~ s,DEPRECATED,**DEPRECATED**,g;
  213. #
  214. # Add references
  215. #
  216. my $start_delim = "[ \n\t\(\=\*\@]";
  217. my $end_delim = "(\\s|,|\\\\=|\\\\:|\\;|\\\)|\\}|\\{)";
  218. foreach my $r (keys %ioctls) {
  219. my $s = $ioctls{$r};
  220. $r =~ s,([\_\`\*\<\>\&\\\\:\/]),\\\\$1,g;
  221. print "$r -> $s\n" if ($debug);
  222. $data =~ s/($start_delim)($r)$end_delim/$1$s$3/g;
  223. }
  224. foreach my $r (keys %defines) {
  225. my $s = $defines{$r};
  226. $r =~ s,([\_\`\*\<\>\&\\\\:\/]),\\\\$1,g;
  227. print "$r -> $s\n" if ($debug);
  228. $data =~ s/($start_delim)($r)$end_delim/$1$s$3/g;
  229. }
  230. foreach my $r (keys %enum_symbols) {
  231. my $s = $enum_symbols{$r};
  232. $r =~ s,([\_\`\*\<\>\&\\\\:\/]),\\\\$1,g;
  233. print "$r -> $s\n" if ($debug);
  234. $data =~ s/($start_delim)($r)$end_delim/$1$s$3/g;
  235. }
  236. foreach my $r (keys %enums) {
  237. my $s = $enums{$r};
  238. $r =~ s,([\_\`\*\<\>\&\\\\:\/]),\\\\$1,g;
  239. print "$r -> $s\n" if ($debug);
  240. $data =~ s/enum\s+($r)$end_delim/$s$2/g;
  241. }
  242. foreach my $r (keys %structs) {
  243. my $s = $structs{$r};
  244. $r =~ s,([\_\`\*\<\>\&\\\\:\/]),\\\\$1,g;
  245. print "$r -> $s\n" if ($debug);
  246. $data =~ s/struct\s+($r)$end_delim/$s$2/g;
  247. }
  248. foreach my $r (keys %typedefs) {
  249. my $s = $typedefs{$r};
  250. $r =~ s,([\_\`\*\<\>\&\\\\:\/]),\\\\$1,g;
  251. print "$r -> $s\n" if ($debug);
  252. $data =~ s/($start_delim)($r)$end_delim/$1$s$3/g;
  253. }
  254. $data =~ s/\\ ([\n\s])/\1/g;
  255. #
  256. # Generate output file
  257. #
  258. my $title = $file_in;
  259. $title =~ s,.*/,,;
  260. open OUT, "> $file_out" or die "Can't open $file_out";
  261. print OUT ".. -*- coding: utf-8; mode: rst -*-\n\n";
  262. print OUT "$title\n";
  263. print OUT "=" x length($title);
  264. print OUT "\n\n.. parsed-literal::\n\n";
  265. print OUT $data;
  266. close OUT;
  267. __END__
  268. =head1 NAME
  269. parse_headers.pl - parse a C file, in order to identify functions, structs,
  270. enums and defines and create cross-references to a Sphinx book.
  271. =head1 SYNOPSIS
  272. B<parse_headers.pl> [<options>] <C_FILE> <OUT_FILE> [<EXCEPTIONS_FILE>]
  273. Where <options> can be: --debug, --help or --usage.
  274. =head1 OPTIONS
  275. =over 8
  276. =item B<--debug>
  277. Put the script in verbose mode, useful for debugging.
  278. =item B<--usage>
  279. Prints a brief help message and exits.
  280. =item B<--help>
  281. Prints a more detailed help message and exits.
  282. =back
  283. =head1 DESCRIPTION
  284. Convert a C header or source file (C_FILE), into a ReStructured Text
  285. included via ..parsed-literal block with cross-references for the
  286. documentation files that describe the API. It accepts an optional
  287. EXCEPTIONS_FILE with describes what elements will be either ignored or
  288. be pointed to a non-default reference.
  289. The output is written at the (OUT_FILE).
  290. It is capable of identifying defines, functions, structs, typedefs,
  291. enums and enum symbols and create cross-references for all of them.
  292. It is also capable of distinguish #define used for specifying a Linux
  293. ioctl.
  294. The EXCEPTIONS_FILE contain two rules to allow ignoring a symbol or
  295. to replace the default references by a custom one.
  296. Please read Documentation/doc-guide/parse-headers.rst at the Kernel's
  297. tree for more details.
  298. =head1 BUGS
  299. Report bugs to Mauro Carvalho Chehab <mchehab@kernel.org>
  300. =head1 COPYRIGHT
  301. Copyright (c) 2016 by Mauro Carvalho Chehab <mchehab+samsung@kernel.org>.
  302. License GPLv2: GNU GPL version 2 <https://gnu.org/licenses/gpl.html>.
  303. This is free software: you are free to change and redistribute it.
  304. There is NO WARRANTY, to the extent permitted by law.
  305. =cut