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