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