Rework util/find-doc-nits to distinguish internal documentation
[openssl.git] / util / perl / OpenSSL / Util / Pod.pm
1 # Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
2 #
3 # Licensed under the Apache License 2.0 (the "License").  You may not use
4 # this file except in compliance with the License.  You can obtain a copy
5 # in the file LICENSE in the source distribution or at
6 # https://www.openssl.org/source/license.html
7
8 package OpenSSL::Util::Pod;
9
10 use strict;
11 use warnings;
12
13 use Exporter;
14 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
15 $VERSION = "0.1";
16 @ISA = qw(Exporter);
17 @EXPORT = qw(extract_pod_info);
18 @EXPORT_OK = qw();
19
20 =head1 NAME
21
22 OpenSSL::Util::Pod - utilities to manipulate .pod files
23
24 =head1 SYNOPSIS
25
26   use OpenSSL::Util::Pod;
27
28   my %podinfo = extract_pod_info("foo.pod");
29
30   # or if the file is already opened...  Note that this consumes the
31   # remainder of the file.
32
33   my %podinfo = extract_pod_info(\*STDIN);
34
35 =head1 DESCRIPTION
36
37 =over
38
39 =item B<extract_pod_info "FILENAME", HASHREF>
40
41 =item B<extract_pod_info "FILENAME">
42
43 =item B<extract_pod_info GLOB, HASHREF>
44
45 =item B<extract_pod_info GLOB>
46
47 Extracts information from a .pod file, given a STRING (file name) or a
48 GLOB (a file handle).  The result is given back as a hash table.
49
50 The additional hash is for extra parameters:
51
52 =over
53
54 =item B<section =E<gt> N>
55
56 The value MUST be a number, and will be the man section number
57 to be used with the given .pod file.
58
59 =item B<debug =E<gt> 0|1>
60
61 If set to 1, extra debug text will be printed on STDERR
62
63 =back
64
65 =back
66
67 =head1 RETURN VALUES
68
69 =over
70
71 =item B<extract_pod_info> returns a hash table with the following
72 items:
73
74 =over
75
76 =item B<section =E<gt> N>
77
78 The man section number this .pod file belongs to.  Often the same as
79 was given as input.
80
81 =item B<names =E<gt> [ "name", ... ]>
82
83 All the names extracted from the NAME section.
84
85 =item B<contents =E<gt> "...">
86
87 The whole contents of the .pod file.
88
89 =back
90
91 =back
92
93 =cut
94
95 sub extract_pod_info {
96     my $input = shift;
97     my $defaults_ref = shift || {};
98     my %defaults = ( debug => 0, section => 0, %$defaults_ref );
99     my $fh = undef;
100     my $filename = undef;
101     my $contents;
102
103     # If not a file handle, then it's assume to be a file path (a string)
104     if (ref $input eq "") {
105         $filename = $input;
106         open $fh, $input or die "Trying to read $filename: $!\n";
107         print STDERR "DEBUG: Reading $input\n" if $defaults{debug};
108         $input = $fh;
109     }
110     if (ref $input eq "GLOB") {
111         local $/ = undef;
112         $contents = <$input>;
113     } else {
114         die "Unknown input type";
115     }
116
117     my @invisible_names = ();
118     my %podinfo = ( section => $defaults{section});
119
120     # Regexp to split a text into paragraphs found at
121     # https://www.perlmonks.org/?node_id=584367
122     # Most of all, \G (continue at last match end) and /g (anchor
123     # this match for \G) are significant
124     foreach (map { /\G((?:(?!\n\n).)*\n+|.+\z)/sg } $contents) {
125         # Remove as many line endings as possible from the end of the paragraph
126         while (s|\R$||) {}
127
128         print STDERR "DEBUG: Paragraph:\n$_\n"
129             if $defaults{debug};
130
131         # Stop reading when we have reached past the NAME section.
132         last if (m|^=head1|
133                  && defined $podinfo{lastsect}
134                  && $podinfo{lastsect} eq "NAME");
135
136         # Collect the section name
137         if (m|^=head1\s*(.*)|) {
138             $podinfo{lastsect} = $1;
139             $podinfo{lastsect} =~ s/\s+$//;
140             print STDERR "DEBUG: Found new pod section $1\n"
141                 if $defaults{debug};
142             print STDERR "DEBUG: Clearing pod section text\n"
143                 if $defaults{debug};
144             $podinfo{lastsecttext} = "";
145         }
146
147         # Add invisible names
148         if (m|^=for\s+openssl\s+names:\s*(.*)|s) {
149             my $x = $1;
150             my @tmp = map { map { s/\s+//g; $_ } split(/,/, $_) } $x;
151             print STDERR
152                 "DEBUG: Found invisible names: ", join(', ', @tmp), "\n"
153                 if $defaults{debug};
154             push @invisible_names, @tmp;
155         }
156
157         next if (m|^=| || m|^\s*$|);
158
159         # Collect the section text
160         print STDERR "DEBUG: accumulating pod section text \"$_\"\n"
161             if $defaults{debug};
162         $podinfo{lastsecttext} .= " " if $podinfo{lastsecttext};
163         $podinfo{lastsecttext} .= $_;
164     }
165
166
167     if (defined $fh) {
168         close $fh;
169         print STDERR "DEBUG: Done reading $filename\n" if $defaults{debug};
170     }
171
172     $podinfo{lastsecttext} =~ s|\s+-\s+.*$||s;
173
174     my @names =
175         map { s/^\s+//g;        # Trim prefix blanks
176               s/\s+$//g;        # Trim suffix blanks
177               s|/|-|g;          # Treat slash as dash
178               $_ }
179         split(m|,|, $podinfo{lastsecttext});
180
181     print STDERR
182         "DEBUG: Collected names are: ",
183         join(', ', @names, @invisible_names), "\n"
184         if $defaults{debug};
185
186     return ( section => $podinfo{section},
187              names => [ @names, @invisible_names ],
188              contents => $contents,
189              filename => $filename );
190 }
191
192 1;