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