File Coverage

blib/lib/Pod/Links.pm
Criterion Covered Total %
statement 33 148 22.3
branch 7 66 10.6
condition 8 39 20.5
subroutine 8 22 36.3
pod 0 13 0.0
total 56 288 19.4


line stmt bran cond sub pod time code
1             package Pod::Links;
2 1     1   5 use strict;
  1         1  
  1         33  
3 1     1   5 use File::Basename;
  1         1  
  1         114  
4 1     1   7 use Carp;
  1         9  
  1         56  
5 1     1   4 use Pod::Parser;
  1         2  
  1         27  
6 1     1   3 use vars qw(@ISA $VERSION @EXPORT_OK);
  1         2  
  1         72  
7             $VERSION = '1.00';
8 1     1   4 use base qw(Exporter Pod::Parser);
  1         2  
  1         1387  
9             @EXPORT_OK = qw(link_parse);
10              
11             sub link_parse
12             {
13 2     2 0 3 my ($link,$sec) = @_;
14 2         3 my ($section,$remote,$category);
15 2         10 my $text = $link;
16 2 50       8 $text = $1 if $link =~ s/^([^|]+)\|(?=.)//;
17 2 50       7 return ($text,$link) if $link =~ m#[a-zA-Z]+://#;
18 2         9 $link =~ s/\s+/ /g;
19 2 50       7 $sec = {} unless defined $sec;
20 2 100 33     51 if ((exists($sec->{$link}) && $link =~ /^(.*)$/) ||
    50 33        
      66        
      33        
      66        
21             $link =~ /^"(.*)"$/ || $link =~ m#^/"?(.*?)"?$# ||
22             ($link !~ m#/# && $link =~ /^(.*\s.*)$/))
23             {
24 1   33     6 $section = $1 || $link;
25             }
26             elsif ($link =~ m#^([^/]+)(?:/"?(.*?)"?)?$#)
27             {
28 1         4 ($remote,$section) = ($1,$2);
29 1 50       6 $category = $2 if ($remote =~ s/(\w+)\s*\((.*)\)$/$1/);
30             # $section =~ s/\W+$// if defined $section;
31             }
32 2         10 return ($text,$section,$remote,$category);
33             }
34              
35             sub begin_pod
36             {
37 0     0 0   my $parser = shift;
38 0           $parser->{'links'} = {};
39 0           $parser->{'sections'} = {};
40 0           delete $parser->{'NAME'};
41             }
42              
43              
44             sub new
45             {
46 0     0 0   my $parser = shift->SUPER::new(@_);
47 0           $parser->{'documents'} = {};
48 0           return $parser;
49             }
50              
51             sub verbatim
52             {
53 0     0 0   my ($parser, $paragraph) = @_;
54 0 0         if ($parser->{'inNAME'})
55             {
56 0           warn $parser->input_file.": verbatim NAME section!\n";
57 0           $parser->{'NAME'} = $paragraph;
58 0           $parser->{'inNAME'} = 0;
59             }
60             }
61              
62             sub textblock
63             {
64 0     0 0   my ($parser, $paragraph) = @_;
65 0           my $expansion = $parser->interpolate($paragraph);
66 0 0         if ($parser->{'inNAME'})
67             {
68 0           $parser->{'NAME'} = $expansion;
69 0           $parser->{'inNAME'} = 0;
70             }
71             }
72              
73             sub command
74             {
75 0     0 0   my ($parser, $command, $paragraph) = @_;
76 0           my $expansion = $parser->interpolate($paragraph);
77 0           $expansion =~ s/(^\s+|\s+$)//g;
78 0           $expansion =~ s/[\s\n]+/ /g;
79 0 0 0       if ($command =~ /^(head\d)/ || ($command eq 'item' && $expansion !~ /^(\*|\d+\.)/))
      0        
80             {
81 0   0       $parser->{'inNAME'} = ($command eq 'head1' && $expansion eq 'NAME');
82 0 0 0       if ($command eq 'item' && $expansion =~ /\s/)
83             {
84 0           $parser->{'sections'}{$expansion} |= 1;
85 0           ($expansion) = split(/\s/,$expansion,2);
86             }
87 0           $parser->{'sections'}{$expansion} |= 1;
88             }
89             }
90              
91             sub interior_sequence
92             {
93 0     0 0   my ($parser, $seq_command, $seq_argument) = @_;
94 0 0         if ($seq_command eq 'L')
    0          
95             {
96 0           my $expansion = $seq_argument;
97 0           $expansion =~ s/(^\s+|\s+$)//g;
98 0           $expansion =~ s/^[^|]+\|\s*//;
99 0           $expansion =~ s/[\s\n]+/ /g;
100 0           $parser->{'links'}{$expansion} = 0;
101             }
102             elsif ($seq_command eq 'E')
103             {
104 0 0         return '>' if $seq_argument eq 'gt';
105 0 0         return '<' if $seq_argument eq 'lt';
106             }
107 0           return $seq_argument;
108             }
109              
110             sub documents
111             {
112 0     0 0   my ($parser) = @_;
113 0           return $parser->{'documents'};
114             }
115              
116             sub names
117             {
118 0     0 0   my ($parser) = @_;
119 0           return sort keys %{$parser->{'documents'}};
  0            
120             }
121              
122             sub url
123             {
124 0     0 0   my ($parser,$sec,$name,$cat) = @_;
125 0           my $url = '';
126 0 0 0       return $url unless $sec || $name;
127 0 0 0       if (defined($name) && length($name))
128             {
129 0           my $hash = $parser->{'documents'}{$name};
130 0 0         return undef unless defined $hash->{'link'};
131 0           $url .= $hash->{'link'};
132             }
133 0 0         if (defined $sec)
134             {
135 0           $sec =~ s/[^A-Z0-9_]+/_/ig;
136 0           $url .= "#$sec";
137             }
138 0           return $url;
139             }
140              
141             sub relative_url
142             {
143 0     0 0   require URI::URL;
144 0           my $parser = shift;
145 0           my $source = URI::URL->newlocal(shift)->abs;
146 0           my $url = shift;
147 0 0         if ($url)
148             {
149 0           my $uo = URI::URL->new($url,$source)->abs;
150 0           my $rel = $uo->rel->as_string;
151 0           $url = $rel;
152             }
153 0           return $url;
154             }
155              
156              
157             sub _attr
158             {
159 0     0     my ($parser,$key,$name,$val) = @_;
160 0           my $hash = $parser->{'documents'}{$name};
161 0 0         $hash->{$key} = $val if (@_ > 3);
162 0           return $hash->{$key};
163             }
164              
165             foreach my $field (qw[pod name title sections link])
166             {
167 1     1   6 no strict 'refs';
  1         1  
  1         762  
168 0     0     *{$field} = sub { shift->_attr($field,@_) };
169             }
170              
171             sub end_pod
172             {
173 0     0 0   my ($parser) = @_;
174 0           my $file = $parser->input_file();
175 0 0         warn "$file\n" if $parser->{'Verbose'};
176 0           my $name = $parser->{'NAME'};
177 0           my $links = delete $parser->{'links'};
178 0           my $sec = delete $parser->{'sections'};
179 0           my $documents = $parser->{'documents'};
180 0 0         if (defined $name)
181             {
182 0           my ($doc,$title) = $name =~ /^\s*(.+?)\s+-+\s+([\s\S]*?)\s*$/;
183 0 0         if (defined($doc))
184             {
185 0 0         ($doc) = split(/\s*,\s*/,$doc,2) if ($doc =~ /,/);
186 0           $title =~ s/\.\s[\s\S]*$//;
187 0 0         if (exists $documents->{$doc})
188             {
189 0           my $hash = $documents->{$doc};
190 0 0         if (exists $hash->{'pod'})
191             {
192 0           my $old = $hash->{'pod'};
193 0           warn "`$doc' in $old and $file\n";
194             }
195 0           foreach my $section (keys %{$hash->{'sections'}})
  0            
196             {
197 0 0         if (exists $sec->{$section})
198             {
199 0           $sec->{$section} |= $hash->{'sections'}{$section};
200             }
201             else
202             {
203 0           warn "No section '$section' in `$doc' $file\n";
204             }
205             }
206             }
207              
208 0           $documents->{$doc} = { name => $doc, title => $title, pod => $file, sections => $sec };
209              
210 0           foreach my $link (sort keys %$links)
211             {
212 0           my ($text,$section,$remote,$category) = link_parse($link,$sec);
213 0 0         if (defined $remote)
    0          
214             {
215 0 0         unless (exists $documents->{$remote})
216             {
217 0           $documents->{$remote} = {'sections' => {}, 'refsfrom' => {}};
218             }
219 0 0         $documents->{$remote}->{'sections'}{$section} |= 4 if defined $section;
220 0           $documents->{$remote}->{'refsfrom'}{$file}++;
221             }
222             elsif (defined $section)
223             {
224 0           $sec->{$section} |= 2; # local ref
225             }
226             else
227             {
228 0           warn "Strange link L<$link> in $file\n";
229             }
230             }
231             }
232             else
233             {
234 0           warn "Weird NAME '$name' in $file\n";
235             }
236             }
237             else
238             {
239 0           warn "No NAME in $file\n";
240             }
241             }
242              
243             sub check_links
244             {
245 0     0 0   my $parser = shift;
246 0           my $documents = $parser->{'documents'};
247 0           foreach my $doc (sort keys %$documents)
248             {
249 0           my $sec = $documents->{$doc}->{'sections'};
250 0 0         if (exists $sec->{'NAME'})
251             {
252 0           foreach my $section (sort keys %{$sec})
  0            
253             {
254 0           my $f = $sec->{$section};
255 0 0 0       if (($f & 4) && !($f & 1))
256             {
257 0           warn "Links to $doc/$section but never seen\n";
258             }
259             }
260             }
261             else
262             {
263 0           my $who = $documents->{$doc}->{'refsfrom'};
264 0           warn "Links to `$doc' but never seen\n";
265 0           foreach my $file (sort keys %$who)
266             {
267 0           printf STDERR "%3d $file\n",$who->{$file};
268             }
269             }
270             }
271             }
272              
273              
274              
275             1;
276             __END__