File Coverage

blib/lib/FTN/Nodelist.pm
Criterion Covered Total %
statement 104 119 87.3
branch 55 72 76.3
condition 37 52 71.1
subroutine 9 9 100.0
pod 2 2 100.0
total 207 254 81.5


line stmt bran cond sub pod time code
1             # FTN/Nodelist.pm
2             #
3             # Copyright (c) 2005-2007 Serguei Trouchelle. All rights reserved.
4             #
5             # This program is free software; you can redistribute it and/or modify it
6             # under the same terms as Perl itself.
7              
8             # History:
9             # 1.07 2007/02/28 License added
10             # 1.06 2007/02/04 Quality update (Test::Pod, Test::Pod::Coverage)
11             # 1.05 2005/09/29 Fixed problem with non-existing node
12             # 1.04 2005/09/29 Fixed problem with missing nodelist
13             # 1.03 2005/02/25 Cache problem fixed
14             # 1.02 2005/02/22 Perl license added
15             # Pointlist processing added
16             # Documentation improved
17             # 1.01 2005/02/16 Initial revision
18              
19             =head1 NAME
20              
21             FTN::Nodelist - Process FTN nodelist
22              
23             =head1 SYNOPSIS
24              
25             my $ndl = new FTN::Nodelist(-file => '/fido/var/ndl/nodelist.*');
26             if (my $node = $ndl->getNode('2:550/4077')) {
27             print $node->sysop();
28             } else {
29             warn 'Cannot find node';
30             }
31              
32             =head1 DESCRIPTION
33              
34             C contains functions that can be used to process Fidonet
35             Technology Network nodelist and pointlist.
36              
37             =head1 METHODS
38              
39             =head2 new
40              
41             This method creates C object.
42             Can get following arguments:
43              
44             Nodelist file path:
45              
46             -file => '/path/to/nodelist'
47              
48             Path can point to definite file (ex.: C<'/var/ndl/nodelist.357'>) or contain
49             wildcard (.*) instead of digital extension. Maximum extension value will be
50             used to find exact nodelist (ex.: C<'/var/ndl/nodelist.*'>)
51              
52             Cacheable status:
53              
54             -cache => 0/1
55              
56             Default is 1. When cacheable status is set to 1, all search results are
57             stored in object cache. It saves resources when searching the same address,
58             but eats memory to store results. Choose appropriate behaviour depending on
59             your tasks.
60              
61             =head2 getNode( $addr )
62              
63             Takes FTN address as argument. Address can be feed in 3D or 4D style
64             (Zone:Net/Node, Zone:Net/Node.Point).
65              
66             If 4D style is specified, point address is searching.
67              
68             Returns C object if node can be found in nodelist.
69              
70             See L for details how these results can be used.
71              
72             Examples:
73              
74             my $node = $ndl->getNode('2:550/0');
75             my $node = $ndl->getNode('2:2/0');
76             my $node = $ndl->getNode('2:550/4077');
77             my $node = $ndl->getNode('2:550/4077.101');
78              
79             =head1 KNOWN ISSUES
80              
81             When using wildcard in nodelist path, maximum extension is taken into
82             account. It may bring to wrong results when there are many nodelist files
83             and current nodelist has lesser number (for example, C and
84             C).
85              
86             This issue may be resolved in next versions of C.
87              
88             =head1 AUTHORS
89              
90             Serguei Trouchelle EFE
91              
92             =head1 LICENSE
93              
94             This program is free software; you can redistribute it and/or modify it
95             under the same terms as Perl itself.
96              
97             =head1 COPYRIGHT
98              
99             Copyright (c) 2005-2007 Serguei Trouchelle. All rights reserved.
100              
101             =cut
102              
103             package FTN::Nodelist;
104              
105 6     6   60315 use FTN::Nodelist::Node;
  6         14  
  6         305  
106 6     6   5390 use FTN::Address;
  6         16993  
  6         317  
107              
108             require Exporter;
109 6     6   42 use Config;
  6         11  
  6         188  
110              
111 6     6   30 use strict;
  6         11  
  6         181  
112 6     6   74 use warnings;
  6         16  
  6         418  
113              
114             our @EXPORT_OK = qw//;
115             our %EXPORT_TAGS = ();
116             our @ISA = qw/Exporter/;
117              
118             $FTN::Nodelist::VERSION = "1.07";
119              
120 6     6   39 use File::Spec;
  6         11  
  6         159  
121 6     6   30 use File::Basename;
  6         34  
  6         11169  
122              
123             sub new {
124 5     5 1 82 my $self = shift;
125 5         27 my %attr = @_;
126 5         13 $self = {};
127              
128 5         20 my $ndlfile = $attr{'-file'};
129              
130 5 50       26 unless (defined $ndlfile) {
131 0         0 @$ = "No `-file' attribute specified, cannot find nodelist";
132 0         0 return undef;
133             }
134              
135 5 50       45 if ($ndlfile =~ /\.\*$/) { # wildmask used, find corresonding nodelist
136 5         375 my $directory = dirname($ndlfile);
137 5         156 my $filename = basename($ndlfile);
138              
139 5         29 $filename =~ s/\.\*$/.\\d\\d\\d/;
140              
141 5 50       320 if (opendir(DIR, $directory)) {
142 6 100       19 my ($ndl, @rest) = sort {$b cmp $a}
  80         598  
143 5         189 grep { /^$filename/ && -f "$directory/$_" }
144             readdir(DIR);
145 5         160 closedir DIR;
146 5 50       24 if (defined $ndl) {
147 5         172 $ndlfile = File::Spec->catfile($directory, $ndl);
148             } else {
149 0         0 $@ = 'Cannot find file ' . $ndlfile;
150 0         0 return undef;
151             }
152              
153             } else {
154             # failed to read directory
155 0         0 $@ = 'Cannot read directory ' . $directory;
156 0         0 return undef;
157             }
158             }
159              
160 5 50       161 unless (-e $ndlfile) {
161 0         0 $@ = 'Cannot find file ' . $ndlfile;
162 0         0 return undef;
163             }
164              
165 5         94 $self->{'__ndlfile'} = $ndlfile;
166              
167 5         16 $self->{'__cache'} = 1; # cache search results by default
168             # but may be overriden
169 5 50       22 $self->{'__cache'} = $attr{'-cache'} if exists $attr{'-cache'};
170              
171 5         9 bless $self ;
172 5         24 return $self;
173             }
174              
175             sub getNode {
176 27     27 1 6790 my $self = shift;
177 27         46 my $node = shift;
178              
179 27 50 33     203 if ($self->{'__cache'} and
180             $self->{'__nodes'}->{$node}) {
181             # Return cached copy
182 0         0 return $self->{'__nodes'}->{$node};
183             }
184              
185 27 50       117 if (my $addr = new FTN::Address($node)) {
186 27 100       818 if ($addr->{'p'}) {
187             # Points are not in nodelist
188             # Process boss/boss-point format pointlists...
189 12 50       421 if (open (F, '<' . $self->{'__ndlfile'})) {
190 12         16 my $found;
191              
192             PNT:
193 12         248 while() {
194 140 100       315 next if /^;/; # strip comments
195 116 100 66     616 if (m!^Boss,(\d+):(\d+)/(\d+)!
      66        
      66        
196             and $1 eq $addr->{'z'}
197             and $2 eq $addr->{'n'}
198             and $3 eq $addr->{'f'} ) {
199 8         29 while() {
200 100 50       169 next if /^;/; # strip comments
201 100 100 50     797 if (((/^,(\d+),/) or
      66        
202             (/^Point,(\d+),/) or
203             0
204             ) and ($addr->{'p'} == $1)) {
205 8         18 $found = $_;
206 8         18 last PNT;
207             }
208              
209 92 50       342 last PNT if /^Boss/; # Not found
210             }
211             }
212             }
213              
214 12         170 close(F);
215 12 100       29 if ($found) {
216 8         17 chomp $found;
217 8         42 my $node = new FTN::Nodelist::Node($addr, $found);
218             # cache result if needed
219 8 50       44 $self->{'__nodes'}->{$node->address()} = $node if $self->{'__cache'};
220 8         28 return $node;
221             } else {
222             # We will search point-format in nodelist
223             }
224             } else {
225 0         0 $@ = 'Cannot read nodelist ' . $@;
226 0         0 return undef;
227             }
228             }
229              
230             # Process nodelist
231              
232 19 50       647 if (open (F, '<' . $self->{'__ndlfile'})) {
233 19         25 my $found;
234              
235             NDL:
236              
237 19         318 while() {
238 76 100       325 next if /^;/; # strip comments
239 19 50 33     179 if ((/^Zone,(\d+),/) and ($addr->{'z'} == $1)) {
240 19 100 100     90 if ($addr->{'z'} eq $addr->{'n'} and $addr->{'f'} == 0) {
241 1         4 $found = $_;
242 1         3 last NDL;
243             }
244 18         22 my $reg;
245 18         66 while() {
246 122 100       290 next if /^;/; # strip comments
247 101 100       196 $reg = 1 if /^Region,/;
248 101 100 100     1385 if ((/^Region,(\d+),/ or
    100 100        
      100        
      66        
      100        
249             /^Host,(\d+),/
250             ) and ($addr->{'n'} == $1)) {
251              
252 13 100       39 if ($addr->{'f'} == 0) {
253 2         3 $found = $_;
254 2         5 last NDL;
255             }
256              
257 11         38 while() {
258 24 100       59 next if /^;/; # strip comments
259 20 100 33     140 last NDL if /^Zone,/ or
      66        
260             /^Region,/ or
261             /^Host,/;
262 16 100 50     238 if (((/^,(\d+),/) or
      100        
263             (/^Hub,(\d+),/) or
264             (/^Pvt,(\d+),/) or
265             (/^Hold,(\d+),/) or
266             (/^Down,(\d+),/) or
267             0
268             ) and ($addr->{'f'} == $1)) {
269 7         10 $found = $_;
270 7         14 last NDL;
271             }
272             }
273             } elsif (not $reg and $addr->{'z'} eq $addr->{'n'}
274             and /,(\d+)/ and $addr->{'f'} eq $1) {
275 5         10 $found = $_;
276 5         10 last NDL;
277             }
278             }
279             }
280             }
281              
282 19 100       49 if ($addr->{'p'}) {
283             # Search for point (point-format)
284 4         5 undef $found; # Don't need boss-node
285 4         15 while() {
286 8 50       17 next if /^;/; # strip comments
287 8 50       30 last if /^((Zone)|(Region)|(Host)|(Hub)|(Pvt)|(Hold)|(Down))?,/;
288             # Next node found
289 8 100 66     53 if (/^Point,(\d+),/
290             and $1 == $addr->{'p'}) {
291 4         4 $found = $_;
292 4         6 last;
293             }
294             }
295             }
296              
297 19         263 close(F);
298 19 100       44 if ($found) {
299 15         30 chomp $found;
300 15         67 my $node = new FTN::Nodelist::Node($addr, $found);
301             # cache result if needed
302 15 50       73 $self->{'__nodes'}->{$node->address()} = $node if $self->{'__cache'};
303 15         49 return $node;
304             } else {
305 4         11 return undef; # Not found
306             }
307             } else {
308 0           $@ = 'Cannot read nodelist ' . $@;
309 0           return undef;
310             }
311             } else {
312 0           $@ = 'Invalid address : ' . $node;
313 0           return undef;
314             }
315             }
316              
317             1;