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