1 #!/usr/bin/env perl 2 use strict; 3 use Text::Tabs; 4 use Getopt::Long; 5 use Pod::Usage; 6 7 my $debug; 8 my $help; 9 my $man; 10 11 GetOptions( 12 "debug" => \$debug, 13 'usage|?' => \$help, 14 'help' => \$man 15 ) or pod2usage(2); 16 17 pod2usage(1) if $help; 18 pod2usage(-exitstatus => 0, -verbose => 2) if $man; 19 pod2usage(2) if (scalar @ARGV < 2 || scalar @ARGV > 3); 20 21 my ($file_in, $file_out, $file_exceptions) = @ARGV; 22 23 my $data; 24 my %ioctls; 25 my %defines; 26 my %typedefs; 27 my %enums; 28 my %enum_symbols; 29 my %structs; 30 31 require Data::Dumper if ($debug); 32 33 # 34 # read the file and get identifiers 35 # 36 37 my $is_enum = 0; 38 my $is_comment = 0; 39 open IN, $file_in or die "Can't open $file_in"; 40 while (<IN>) { 41 $data .= $_; 42 43 my $ln = $_; 44 if (!$is_comment) { 45 $ln =~ s,/\*.*(\*/),,g; 46 47 $is_comment = 1 if ($ln =~ s,/\*.*,,); 48 } else { 49 if ($ln =~ s,^(.*\*/),,) { 50 $is_comment = 0; 51 } else { 52 next; 53 } 54 } 55 56 if ($is_enum && $ln =~ m/^\s*([_\w][\w\d_]+)\s*[\,=]?/) { 57 my $s = $1; 58 my $n = $1; 59 $n =~ tr/A-Z/a-z/; 60 $n =~ tr/_/-/; 61 62 $enum_symbols{$s} = "\\ :ref:`$s <$n>`\\ "; 63 64 $is_enum = 0 if ($is_enum && m/\}/); 65 next; 66 } 67 $is_enum = 0 if ($is_enum && m/\}/); 68 69 if ($ln =~ m/^\s*#\s*define\s+([_\w][\w\d_]+)\s+_IO/) { 70 my $s = $1; 71 my $n = $1; 72 $n =~ tr/A-Z/a-z/; 73 74 $ioctls{$s} = "\\ :ref:`$s <$n>`\\ "; 75 next; 76 } 77 78 if ($ln =~ m/^\s*#\s*define\s+([_\w][\w\d_]+)\s+/) { 79 my $s = $1; 80 my $n = $1; 81 $n =~ tr/A-Z/a-z/; 82 $n =~ tr/_/-/; 83 84 $defines{$s} = "\\ :ref:`$s <$n>`\\ "; 85 next; 86 } 87 88 if ($ln =~ m/^\s*typedef\s+([_\w][\w\d_]+)\s+(.*)\s+([_\w][\w\d_]+);/) { 89 my $s = $2; 90 my $n = $3; 91 92 $typedefs{$n} = "\\ :c:type:`$n <$s>`\\ "; 93 next; 94 } 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_]+)$/) { 99 my $s = $1; 100 101 $enums{$s} = "enum :c:type:`$s`\\ "; 102 103 $is_enum = $1; 104 next; 105 } 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_]+)$/ 110 ) { 111 my $s = $1; 112 113 $structs{$s} = "struct $s\\ "; 114 next; 115 } 116 } 117 close IN; 118 119 # 120 # Handle multi-line typedefs 121 # 122 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) { 126 my $s = $m; 127 128 $typedefs{$s} = "\\ :c:type:`$s`\\ "; 129 next; 130 } 131 132 # 133 # Handle exceptions, if any 134 # 135 136 my %def_reftype = ( 137 "ioctl" => ":ref", 138 "define" => ":ref", 139 "symbol" => ":ref", 140 "typedef" => ":c:type", 141 "enum" => ":c:type", 142 "struct" => ":c:type", 143 ); 144 145 if ($file_exceptions) { 146 open IN, $file_exceptions or die "Can't read $file_exceptions"; 147 while (<IN>) { 148 next if (m/^\s*$/ || m/^\s*#/); 149 150 # Parsers to ignore a symbol 151 152 if (m/^ignore\s+ioctl\s+(\S+)/) { 153 delete $ioctls{$1} if (exists($ioctls{$1})); 154 next; 155 } 156 if (m/^ignore\s+define\s+(\S+)/) { 157 delete $defines{$1} if (exists($defines{$1})); 158 next; 159 } 160 if (m/^ignore\s+typedef\s+(\S+)/) { 161 delete $typedefs{$1} if (exists($typedefs{$1})); 162 next; 163 } 164 if (m/^ignore\s+enum\s+(\S+)/) { 165 delete $enums{$1} if (exists($enums{$1})); 166 next; 167 } 168 if (m/^ignore\s+struct\s+(\S+)/) { 169 delete $structs{$1} if (exists($structs{$1})); 170 next; 171 } 172 if (m/^ignore\s+symbol\s+(\S+)/) { 173 delete $enum_symbols{$1} if (exists($enum_symbols{$1})); 174 next; 175 } 176 177 # Parsers to replace a symbol 178 my ($type, $old, $new, $reftype); 179 180 if (m/^replace\s+(\S+)\s+(\S+)\s+(\S+)/) { 181 $type = $1; 182 $old = $2; 183 $new = $3; 184 } else { 185 die "Can't parse $file_exceptions: $_"; 186 } 187 188 if ($new =~ m/^\:c\:(data|func|macro|type)\:\`(.+)\`/) { 189 $reftype = ":c:$1"; 190 $new = $2; 191 } elsif ($new =~ m/\:ref\:\`(.+)\`/) { 192 $reftype = ":ref"; 193 $new = $1; 194 } else { 195 $reftype = $def_reftype{$type}; 196 } 197 $new = "$reftype:`$old <$new>`"; 198 199 if ($type eq "ioctl") { 200 $ioctls{$old} = $new if (exists($ioctls{$old})); 201 next; 202 } 203 if ($type eq "define") { 204 $defines{$old} = $new if (exists($defines{$old})); 205 next; 206 } 207 if ($type eq "symbol") { 208 $enum_symbols{$old} = $new if (exists($enum_symbols{$old})); 209 next; 210 } 211 if ($type eq "typedef") { 212 $typedefs{$old} = $new if (exists($typedefs{$old})); 213 next; 214 } 215 if ($type eq "enum") { 216 $enums{$old} = $new if (exists($enums{$old})); 217 next; 218 } 219 if ($type eq "struct") { 220 $structs{$old} = $new if (exists($structs{$old})); 221 next; 222 } 223 224 die "Can't parse $file_exceptions: $_"; 225 } 226 } 227 228 if ($debug) { 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); 235 } 236 237 # 238 # Align block 239 # 240 $data = expand($data); 241 $data = " " . $data; 242 $data =~ s/\n/\n /g; 243 $data =~ s/\n\s+$/\n/g; 244 $data =~ s/\n\s+\n/\n\n/g; 245 246 # 247 # Add escape codes for special characters 248 # 249 $data =~ s,([\_\`\*\<\>\&\\\\:\/\|\%\$\#\{\}\~\^]),\\$1,g; 250 251 $data =~ s,DEPRECATED,**DEPRECATED**,g; 252 253 # 254 # Add references 255 # 256 257 my $start_delim = "[ \n\t\(\=\*\@]"; 258 my $end_delim = "(\\s|,|\\\\=|\\\\:|\\;|\\\)|\\}|\\{)"; 259 260 foreach my $r (keys %ioctls) { 261 my $s = $ioctls{$r}; 262 263 $r =~ s,([\_\`\*\<\>\&\\\\:\/]),\\\\$1,g; 264 265 print "$r -> $s\n" if ($debug); 266 267 $data =~ s/($start_delim)($r)$end_delim/$1$s$3/g; 268 } 269 270 foreach my $r (keys %defines) { 271 my $s = $defines{$r}; 272 273 $r =~ s,([\_\`\*\<\>\&\\\\:\/]),\\\\$1,g; 274 275 print "$r -> $s\n" if ($debug); 276 277 $data =~ s/($start_delim)($r)$end_delim/$1$s$3/g; 278 } 279 280 foreach my $r (keys %enum_symbols) { 281 my $s = $enum_symbols{$r}; 282 283 $r =~ s,([\_\`\*\<\>\&\\\\:\/]),\\\\$1,g; 284 285 print "$r -> $s\n" if ($debug); 286 287 $data =~ s/($start_delim)($r)$end_delim/$1$s$3/g; 288 } 289 290 foreach my $r (keys %enums) { 291 my $s = $enums{$r}; 292 293 $r =~ s,([\_\`\*\<\>\&\\\\:\/]),\\\\$1,g; 294 295 print "$r -> $s\n" if ($debug); 296 297 $data =~ s/enum\s+($r)$end_delim/$s$2/g; 298 } 299 300 foreach my $r (keys %structs) { 301 my $s = $structs{$r}; 302 303 $r =~ s,([\_\`\*\<\>\&\\\\:\/]),\\\\$1,g; 304 305 print "$r -> $s\n" if ($debug); 306 307 $data =~ s/struct\s+($r)$end_delim/$s$2/g; 308 } 309 310 foreach my $r (keys %typedefs) { 311 my $s = $typedefs{$r}; 312 313 $r =~ s,([\_\`\*\<\>\&\\\\:\/]),\\\\$1,g; 314 315 print "$r -> $s\n" if ($debug); 316 $data =~ s/($start_delim)($r)$end_delim/$1$s$3/g; 317 } 318 319 $data =~ s/\\ ([\n\s])/\1/g; 320 321 # 322 # Generate output file 323 # 324 325 my $title = $file_in; 326 $title =~ s,.*/,,; 327 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"; 333 print OUT $data; 334 close OUT; 335 336 __END__ 337 338 =head1 NAME 339 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. 342 343 =head1 SYNOPSIS 344 345 B<parse_headers.pl> [<options>] <C_FILE> <OUT_FILE> [<EXCEPTIONS_FILE>] 346 347 Where <options> can be: --debug, --help or --usage. 348 349 =head1 OPTIONS 350 351 =over 8 352 353 =item B<--debug> 354 355 Put the script in verbose mode, useful for debugging. 356 357 =item B<--usage> 358 359 Prints a brief help message and exits. 360 361 =item B<--help> 362 363 Prints a more detailed help message and exits. 364 365 =back 366 367 =head1 DESCRIPTION 368 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. 374 375 The output is written at the (OUT_FILE). 376 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 380 ioctl. 381 382 The EXCEPTIONS_FILE contain two rules to allow ignoring a symbol or 383 to replace the default references by a custom one. 384 385 Please read Documentation/doc-guide/parse-headers.rst at the Kernel's 386 tree for more details. 387 388 =head1 BUGS 389 390 Report bugs to Mauro Carvalho Chehab <mchehab@kernel.org> 391 392 =head1 COPYRIGHT 393 394 Copyright (c) 2016 by Mauro Carvalho Chehab <mchehab+samsung@kernel.org>. 395 396 License GPLv2: GNU GPL version 2 <https://gnu.org/licenses/gpl.html>. 397 398 This is free software: you are free to change and redistribute it. 399 There is NO WARRANTY, to the extent permitted by law. 400 401 =cut
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.