]> dxcluster.net Git - spider.git/blob - perl/Route/Node.pm
fix cf broadcasts
[spider.git] / perl / Route / Node.pm
1 #
2 # Node routing routines
3 #
4 # Copyright (c) 2001 Dirk Koopman G1TLH
5 #
6 # $Id$
7
8
9 package Route::Node;
10
11 use DXDebug;
12 use Route;
13 use Route::User;
14
15 use strict;
16
17 use vars qw($VERSION $BRANCH);
18
19 main::mkver($VERSION = q$Revision$);
20
21 use vars qw(%list %valid @ISA $max $filterdef);
22 @ISA = qw(Route);
23
24 %valid = (
25                   parent => '0,Parent Calls,parray',
26                   nodes => '0,Nodes,parray',
27                   users => '0,Users,parray',
28                   usercount => '0,User Count',
29                   version => '0,Version',
30                   build => '0,Build',
31                   sw => '0,Software',
32                   np => '0,Using New Prot,yesno',
33 );
34
35 $filterdef = $Route::filterdef;
36 %list = ();
37 $max = 0;
38
39 sub count
40 {
41         my $n = scalar (keys %list);
42         $max = $n if $n > $max;
43         return $n;
44 }
45
46 sub max
47 {
48         count();
49         return $max;
50 }
51
52 #
53 # this routine handles the possible adding of an entry in the routing
54 # table. It will only add an entry if it is new. It may have all sorts of
55 # other side effects which may include fixing up other links.
56 #
57 # It will return a node object if (and only if) it is a completely new
58 # object with that callsign. The upper layers are expected to do something
59 # sensible with this!
60 #
61 # called as $parent->add(call, version, flags) 
62 #
63
64 sub add
65 {
66         my $parent = shift;
67         my $call = uc shift;
68         confess "Route::add trying to add $call to myself" if $call eq $parent->{call};
69         my $version = shift;
70         my $here = shift;
71         
72         my $self = get($call);
73         if ($self) {
74                 $self->_addparent($parent);
75                 $parent->_addnode($self);
76                 if ($self->{version} != $version || $self->{flags} != $here) {
77                         $self->{version} = $version;
78                         $self->{flags} = $here;
79                         return $self;
80                 }
81                 return undef;
82         }
83         $self = $parent->new($call, $version, $here);
84         $parent->_addnode($self);
85         return $self;
86 }
87
88 #
89 # this routine is the opposite of 'add' above.
90 #
91 # It will return an object if (and only if) this 'del' will remove
92 # this object completely
93 #
94
95 sub del
96 {
97         my $self = shift;
98         my $pref = shift;
99
100         # delete parent from this call's parent list
101         $pref->_delnode($self);
102     $self->_delparent($pref);
103         my @nodes;
104         my $ncall = $self->{call};
105         
106         # is this the last connection, I have no parents anymore?
107         unless (@{$self->{parent}}) {
108                 foreach my $rcall (@{$self->{nodes}}) {
109                         next if grep $rcall eq $_, @_;
110                         my $r = Route::Node::get($rcall);
111                         push @nodes, $r->del($self, $ncall, @_) if $r;
112                 }
113                 $self->_del_users;
114                 delete $list{$self->{call}};
115                 push @nodes, $self;
116         }
117         return @nodes;
118 }
119
120 sub del_nodes
121 {
122         my $parent = shift;
123         my @out;
124         foreach my $rcall (@{$parent->{nodes}}) {
125                 my $r = get($rcall);
126                 push @out, $r->del($parent, $parent->{call}, @_) if $r;
127         }
128         return @out;
129 }
130
131 sub _del_users
132 {
133         my $self = shift;
134         for (@{$self->{users}}) {
135                 my $ref = Route::User::get($_);
136                 $ref->del($self) if $ref;
137         }
138         $self->{users} = [];
139 }
140
141 # add a user to this node
142 sub add_user
143 {
144         my $self = shift;
145         my $ucall = shift;
146
147         confess "Trying to add NULL User call to routing tables" unless $ucall;
148
149         my $uref = Route::User::get($ucall);
150         my @out;
151         if ($uref) {
152                 push @out, $uref->addparent($self);
153         } else {
154                 $uref = Route::User->new($ucall, $self->{call}, @_);
155                 push @out, $uref;
156         }
157         $self->_adduser($uref);
158         $self->{usercount} = scalar @{$self->{users}};
159
160         return @out;
161 }
162
163 # delete a user from this node
164 sub del_user
165 {
166         my $self = shift;
167         my $ref = shift;
168         my @out;
169         
170         if ($ref) {
171                 @out = $self->_deluser($ref);
172                 $ref->del($self);
173         } else {
174                 confess "tried to delete non-existant $ref->{call} from $self->{call}";
175         }
176         $self->{usercount} = scalar @{$self->{users}};
177         return @out;
178 }
179
180 sub usercount
181 {
182         my $self = shift;
183         if (@_ && @{$self->{users}} == 0) {
184                 $self->{usercount} = shift;
185         }
186         return $self->{usercount};
187 }
188
189 sub users
190 {
191         my $self = shift;
192         return @{$self->{users}};
193 }
194
195 sub nodes
196 {
197         my $self = shift;
198         return @{$self->{nodes}};
199 }
200
201 sub parents
202 {
203         my $self = shift;
204         return @{$self->{parent}};
205 }
206
207 sub rnodes
208 {
209         my $self = shift;
210         my @out;
211         foreach my $call (@{$self->{nodes}}) {
212                 next if grep $call eq $_, @_;
213                 push @out, $call;
214                 my $r = get($call);
215                 push @out, $r->rnodes($call, @_) if $r;
216         }
217         return @out;
218 }
219
220 # return the differences in nodes between what we currently have and
221 # the list proffered. Returns two refs one to a list of nodes to remove and  
222 # the other a list of nodes to add
223
224 # input is a list of callsigns (not refs)
225 sub diff_nodes
226 {
227         my $self = shift;
228         my $in = ref $_[0] ? shift : \@_;
229         my %del = map {($_, 1)} nodes($self);
230         my %in = map {($_, 1)} @$in;
231         
232         # remove all the calls that are in both lists
233         for (@$in) {
234                 delete $in{$_} if delete $del{$_};
235         }
236         return ([keys %del], [keys %in]);
237 }
238
239 # same as above but for users
240 sub diff_users
241 {
242         my $self = shift;
243         my $in = ref $_[0] ? shift : \@_;
244         my %del = map {($_, 1)} users($self);
245         my %in = map {($_, 1)} @$in;
246         
247         # remove all the calls that are in both lists
248         for (@$in) {
249                 delete $in{$_} if delete $del{$_};
250         }
251         return ([keys %del], [keys %in]);
252 }
253
254 sub new
255 {
256         my $pkg = shift;
257         my $call = uc shift;
258         
259         confess "already have $call in $pkg" if $list{$call};
260         
261         my $self = $pkg->SUPER::new($call);
262         $self->{parent} = ref $pkg ? [ $pkg->{call} ] : [ ];
263         $self->{version} = 0 || shift;
264         $self->{flags} = 0 || shift;
265         $self->{users} = [];
266         $self->{nodes} = [];
267         $self->{lid} = 0;
268         
269         $list{$call} = $self;
270         
271         return $self;
272 }
273
274 sub get
275 {
276         my $call = shift;
277         $call = shift if ref $call;
278         my $ref = $list{uc $call};
279         dbg("Failed to get Node $call" ) if !$ref && isdbg('routerr');
280         return $ref;
281 }
282
283 sub get_all
284 {
285         return values %list;
286 }
287
288 sub newid
289 {
290         my $self = shift;
291         my $id = shift;
292         
293         return 0 if $id == $self->{lid};
294         if ($id > $self->{lid}) {
295                 $self->{lid} = $id;
296                 return 1;
297         } elsif ($self->{lid} - $id > 500) {
298                 $self->{id} = $id;
299                 return 1;
300         }
301         return 0;
302 }
303
304 sub _addparent
305 {
306         my $self = shift;
307     return $self->_addlist('parent', @_);
308 }
309
310 sub _delparent
311 {
312         my $self = shift;
313     return $self->_dellist('parent', @_);
314 }
315
316
317 sub _addnode
318 {
319         my $self = shift;
320     return $self->_addlist('nodes', @_);
321 }
322
323 sub _delnode
324 {
325         my $self = shift;
326     return $self->_dellist('nodes', @_);
327 }
328
329
330 sub _adduser
331 {
332         my $self = shift;
333     return $self->_addlist('users', @_);
334 }
335
336 sub _deluser
337 {
338         my $self = shift;
339     return $self->_dellist('users', @_);
340 }
341
342 #
343 # generic AUTOLOAD for accessors
344 #
345
346 sub AUTOLOAD
347 {
348         no strict;
349         my $name = $AUTOLOAD;
350         return if $name =~ /::DESTROY$/;
351         $name =~ s/^.*:://o;
352   
353         confess "Non-existant field '$AUTOLOAD'" unless $valid{$name} || $Route::valid{$name};
354
355         # this clever line of code creates a subroutine which takes over from autoload
356         # from OO Perl - Conway
357         *$AUTOLOAD = sub {$_[0]->{$name} = $_[1] if @_ > 1; return $_[0]->{$name}};
358         goto &$AUTOLOAD;
359 }
360
361 1;
362