File Coverage

blib/lib/IO/Socket/TIPC/Sockaddr.pm
Criterion Covered Total %
statement 163 193 84.4
branch 84 128 65.6
condition n/a
subroutine 18 19 94.7
pod 5 14 35.7
total 270 354 76.2


line stmt bran cond sub pod time code
1             package IO::Socket::TIPC::Sockaddr;
2 8     8   42 use strict;
  8         15  
  8         269  
3 8     8   37 use warnings;
  8         15  
  8         181  
4 8     8   44 use Carp;
  8         11  
  8         818  
5 8     8   47 use Scalar::Util qw(looks_like_number);
  8         13  
  8         924  
6 8     8   40 use Exporter;
  8         13  
  8         63359  
7              
8             our @ISA = qw(Exporter);
9              
10             =head1 NAME
11              
12             IO::Socket::TIPC::Sockaddr - struct sockaddr_tipc class
13              
14             =head1 SYNOPSIS
15              
16             use IO::Socket::TIPC::Sockaddr;
17              
18              
19             =head1 DESCRIPTION
20              
21             TIPC Sockaddrs are used with TIPC sockets, to specify local or remote
22             endpoints for communication. They are used in the B(),
23             B(), B() and B() calls.
24              
25             Sockaddrs can be broken down into 3 address-types, I<"name">,
26             I<"nameseq"> and I<"id">. the I (linked to in
27             B) explains the details much better than I ever could, I suggest
28             reading it before trying to use this module. Also, the B section
29             is useful for getting a feel for how this module works.
30              
31             =cut
32              
33              
34             # Virtually this whole file is just hand-holding for the caller's benefit.
35             #
36             # You can pass it strings like Id => "", or Nameseq => "{a,b,c}".
37             # You can pass it the pieces, like AddrType => 'name', Type => 4242, Instance => 1.
38             # You can pass it a mixture of the two, like Id => "", Ref => 8295.
39             # You can even omit the AddrType parameter, it'll guess from the other args.
40              
41             # Passing the pieces (and specifying the AddrType) is the most efficient way to
42             # use this module, but not the most convenient, so other options exist.
43              
44              
45             sub divine_address_type {
46 3     3 0 5 my $args = shift;
47             # try to figure out what type of address this is.
48 3 50       13 if(exists($$args{Type})) {
    0          
49 3 100       11 if(exists($$args{Instance})) {
    50          
    0          
50 1         4 $$args{AddrType} = 'name';
51             }
52             elsif(exists($$args{Lower})) {
53 2         5 $$args{AddrType} = 'nameseq';
54 2 100       7 $$args{Upper} = $$args{Lower}
55             unless exists $$args{Upper};
56             }
57             elsif(exists($$args{Upper})) {
58 0         0 $$args{AddrType} = 'nameseq';
59 0 0       0 $$args{Lower} = $$args{Upper}
60             unless exists $$args{Lower};
61             }
62             } elsif(exists($$args{Ref})) {
63 0         0 $$args{AddrType} = 'id';
64             } else {
65 0         0 croak("could not guess AddrType - please specify it");
66             }
67 3         10 return 1;
68             }
69              
70             my %valid_args = (
71             'AddrType' => [qw(id name nameseq)], # 'id', 'name', or 'nameseq'
72             'Scope' => [qw( name nameseq)], # TIPC_*_SCOPE, for binding, how far to advertise a name
73             'Ref' => [qw(id )], #
74             'Id' => [qw(id )], # (string or uint32) or (string)
75             'Zone' => [qw(id )], #
76             'Cluster' => [qw(id )], #
77             'Node' => [qw(id )], #
78             'Name' => [qw( name )], # {A,B} (string)
79             'Type' => [qw( name nameseq)], # {A,b} or {A,b,c}
80             'Instance' => [qw( name )], # {a,B}
81             'Domain' => [qw( name )], # tipc_addr, connect/sendto, how far to search for a name
82             'Lower' => [qw( nameseq)], # {a,B,c}
83             'Upper' => [qw( nameseq)], # {a,b,C}
84             'Nameseq' => [qw( nameseq)], # {A,B,C} (string)
85             );
86            
87             sub validate_args_for_address_type {
88 25     25 0 30 my $args = shift;
89 25         32 my $addrtype = $$args{AddrType};
90             # Validate hash-key arguments for this address type
91 25         82 foreach my $key (sort keys %$args) {
92 108         127 my $ref = $valid_args{$key};
93 108 50       171 die "got here ($key)" unless defined $ref;
94 108         134 my %valid = map { $_ => 1 } (@$ref);
  184         393  
95 108 50       305 croak("argument $key not valid for AddrType $addrtype")
96             unless exists($valid{$addrtype});
97             }
98 25         75 return 1;
99             }
100              
101             sub fixup_hash_names {
102 25     25 0 31 my $args = shift;
103             # Validate hash-key arguments to IO::Socket::TIPC::Sockaddr->new()
104 25         107 foreach my $key (sort keys %$args) {
105 87 50       234 if(!exists($valid_args{$key})) {
106             # This key needs to be fixed up. Search for it.
107 0         0 my $lckey = lc($key);
108 0         0 my $fixed = 0;
109 0         0 foreach my $goodkey (sort keys %valid_args) {
110 0 0       0 if($lckey eq lc($goodkey)) {
111             # Found it. Fix it up.
112 0         0 $$args{$goodkey} = $$args{$key};
113 0         0 delete($$args{$key});
114 0         0 $fixed = 1;
115 0         0 last;
116             }
117             }
118 0 0       0 croak("unknown argument $key")
119             unless $fixed;
120             }
121             }
122 25         74 return 1;
123             }
124              
125             sub string_parsing_stuff {
126 25     25 0 26 my $args = shift;
127 25         34 my %details;
128 25 100       101 if(exists($$args{Id})) {
    50          
    50          
129             # just in case the user did Id => '<1.2.3>', Ref => 4, pass in the Ref
130 4 100       11 $details{Ref} = $$args{Ref} if exists $$args{Ref};
131 4 50       14 return undef unless tipc_parse_string(\%details,$$args{Id});
132 4         10 $$args{Zone} = $details{Zone};
133 4         7 $$args{Cluster} = $details{Cluster};
134 4         6 $$args{Node} = $details{Node};
135 4 50       14 $$args{Ref} = $details{Ref} if exists($details{Ref});
136             } elsif(exists($$args{Name})) {
137 0 0       0 return undef unless tipc_parse_string(\%details,$$args{Name});
138 0         0 $$args{Type} = $details{Type};
139 0         0 $$args{Instance} = $details{Instance};
140             } elsif(exists($$args{Nameseq})) {
141 0 0       0 return undef unless tipc_parse_string(\%details,$$args{Nameseq});
142 0         0 $$args{Type} = $details{Type};
143 0         0 $$args{Lower} = $details{Lower};
144 0         0 $$args{Upper} = $details{Upper};
145             }
146 25 100       47 if(exists($details{AddrType})) {
147 4 100       14 $$args{AddrType} = $details{AddrType} unless exists $$args{AddrType};
148             }
149 25         70 return 1;
150             }
151              
152             my %addr_prereqs = (
153             'id' => [qw(Zone Cluster Node Ref)],
154             'name' => [qw(Scope Type Instance)],
155             'nameseq' => [qw(Scope Type Lower Upper)],
156             );
157              
158             sub check_prereqs_for_address_type {
159 25     25 0 30 my $args = shift;
160 25         33 my $addrtype = $$args{AddrType};
161 25         42 my $ref = $addr_prereqs{$addrtype};
162 25 50       47 croak "got here ($addrtype)" unless defined $ref;
163 25         42 foreach my $key (@$ref) {
164 87 50       180 croak "addrtype $addrtype requires a $key value"
165             unless exists($$args{$key});
166             }
167 25         80 1;
168             }
169              
170              
171              
172             =head1 CONSTRUCTOR
173              
174             ...->new ( "string", key=>value, key=>value... )
175             ...->new ( key=>value, key=>value... )
176             ...->new_from_data ( $binary_data )
177              
178             Creates an "IO::Socket::TIPC::Sockaddr" object, which is really just a
179             bunch of fluff to manage C "struct sockaddr_tipc" values easily.
180              
181             Use the B constructor if you want to wrap this class
182             around some sockaddr_tipc data you obtained from somewhere else.
183             (for instance, from the B builtin.)
184              
185             Use the B() constructor to create a new sockaddr object. It
186             optionally takes a string as its first argument. Any other arguments
187             are in the form of Key => Value pairs.
188              
189             =head2 Initial String Argument (optional)
190              
191             You can pass any type of TIPC address as a string, to fill in most of
192             the below values for you. This is a very useful way to save lots of
193             typing, and keeps it more readable. Here is a list of possible string
194             arguments, and their hash-parameter equivalents:
195              
196             "<1.2.3:4>" is equivalent to:
197             AddrType => TIPC_ADDR_ID,
198             Zone => 1,
199             Cluster => 2,
200             Node => 3,
201             Ref => 4
202              
203             "{1, 2}" is equivalent to:
204             AddrType => TIPC_ADDR_NAME,
205             Type => 1,
206             Instance => 2
207              
208             "{1, 2, 3}" is equivalent to:
209             AddrType => TIPC_ADDR_NAMESEQ,
210             Type => 1,
211             Lower => 2,
212             Upper => 3
213              
214             Of course, noone B to spell the fields out in such excruciating
215             detail (you can pass the same strings in I/I/I
216             parameters), but it illustrates my point nicely.
217              
218             The string does not define everything useful about the address...
219             consider specifying the I parameter for arguments to B,
220             and the I parameter for Is you plan to B to.
221              
222              
223             =head2 AddrType
224              
225             This tells Sockaddr whether to create an I, I or I
226             address. The default is guessed from the other arguments it was
227             given; pass the I argument to make it explicit. In
228             practice, this is rarely (never?) needed.
229              
230             If the right constants were imported, you can pass the following
231             arguments: I, I, I,
232             or I (which is an alias for I).
233             Otherwise, you can just say I<"id">, I<"name"> or I<"nameseq">, these
234             will work equally well.
235              
236              
237             =head2 Scope
238              
239             Valid for I and I addresses. Specifies how loudly to
240             advertise the name/nameseq, to the rest of the network. The default
241             is I.
242              
243             If the right constants were imported, you can pass the following
244             arguments: I, I, or
245             I. Otherwise, you can just say I<"zone">,
246             I<"cluster"> or I<"node">, which will work equally well.
247              
248              
249             =head2 Id
250              
251             Defines an I address. An I address has the format
252             "". With the I parameter, you can specify
253             the "" portion of that address, either with a
254             string (like "<1.2.3>") or as an unsigned 32-bit integer.
255             Alternately, you can define the whole thing, Ref included, as a
256             string (like "<1.2.3:4>"). This is a useful way to avoid having to
257             specify the I, I, I, and I parameters
258             individually.
259              
260              
261             =head2 Ref
262              
263             Valid for I addresses. This 32-bit field is usually assigned
264             randomly by the operating system, and only needs to be set when you
265             are attempting to connect to someone else.
266              
267              
268             =head2 Zone
269              
270             Valid for I addresses. This 8-bit field defines the I
271             portion of the Id address. See the I parameter.
272              
273              
274             =head2 Cluster
275              
276             Valid for I addresses. This 12-bit field defines the
277             I portion of the Id address. See the I parameter.
278              
279              
280             =head2 Node
281              
282             Valid for I addresses. This 12-bit field defines the I
283             portion of the Id address. See the I parameter.
284              
285              
286             =head2 Name
287              
288             Defines a I address. A I address comprises two fields,
289             I and I, 32 bits each. It has the format
290             "{Name, Instance}". Name addresses also have a I flag,
291             which is used in Bing, to specify where to start looking
292             for the server.
293              
294             The I parameter is useful for defining a name address all in
295             one go (minus the I). Pass it a string, like "{1, 2}",
296             to avoid having to specify the I and I parameters
297             individually.
298              
299              
300             =head2 Type
301              
302             Required for I and I addresses. This 32-bit field
303             defines the I portion of the address.
304              
305              
306             =head2 Instance
307              
308             Required for I addresses. This 32-bit field defines the
309             I portion of the address.
310              
311              
312             =head2 Domain
313              
314             Valid for I addresses. This 32-bit field defines the starting
315             point, when searching for a server by name. You can pass it an
316             integer, or a TIPC address string, of the form "<1.2.3>".
317              
318              
319             =head2 Nameseq
320              
321             Defines a I address. A I address comprises three
322             fields, I, I and I, 32 bits each. The I
323             and I attributes define a range of I values (see
324             I).
325              
326             I addresses have the format "{Type, Lower, Upper}".
327              
328             The I parameter is useful for defining a nameseq address
329             all in one go. Pass it a string, like "{1, 2, 3}", to avoid having
330             to specify the I, I and I parameters
331             individually.
332              
333              
334             =head2 Lower
335              
336             Required for I addresses. This 32-bit field defines the
337             lower end of an I range. If unspecified, it defaults to
338             I, resulting in a "range" of 1.
339              
340              
341             =head2 Upper
342              
343             Required for I addresses. This 32-bit field defines the
344             upper end of an I range. If unspecified, it defaults to
345             I, resulting in a "range" of 1.
346              
347             =cut
348              
349             sub new {
350 25     25 0 27927 my $package = shift;
351 25         49 my %args = ();
352 25 50       80 if(@_) {
353 25 100       90 if(scalar @_ & 1) {
354 5 50       19 return undef unless tipc_parse_string(\%args, shift);
355             }
356 25         114 %args = (%args, @_);
357             }
358             # sanity-check input, correct capitalization, make sure all keys are valid
359 25 50       60 return undef unless fixup_hash_names(\%args);
360             # handle things like Id => '<1.2.3:4>'
361 25 50       65 return undef unless string_parsing_stuff(\%args);
362 25 100       55 unless(exists($args{AddrType})) {
363 3 50       10 return undef unless divine_address_type(\%args);
364             }
365             # check that we don't have any extra values. (like Name, for an "id" addr)
366 25 50       47 return undef unless validate_args_for_address_type(\%args);
367             # fill in some optional stuff
368 25 100       63 if($args{AddrType} eq 'name') {
369 13 100       26 if(exists($args{Domain})) {
370 4 100       20 unless(looks_like_number($args{Domain})) {
371 2         5 my $href = {};
372 2         8 tipc_parse_string($href,$args{Domain});
373 2 50       6 croak "Domain string should be an id!"
374             unless $$href{AddrType} eq 'id';
375 2         7 $args{Domain} = tipc_addr(@$href{'Zone','Cluster','Node'});
376             }
377             } else {
378 9         21 $args{Domain} = 0;
379             }
380             }
381 25 100       55 if(exists($args{Scope})) {
382 7         11 my $scope = $args{Scope};
383 7         24 my %valid_scopes = (
384             IO::Socket::TIPC::TIPC_ZONE_SCOPE() => 1,
385             IO::Socket::TIPC::TIPC_CLUSTER_SCOPE() => 1,
386             IO::Socket::TIPC::TIPC_NODE_SCOPE() => 1,
387             );
388 7         19 my %scope_values = (
389             zone => IO::Socket::TIPC::TIPC_ZONE_SCOPE(),
390             cluster => IO::Socket::TIPC::TIPC_CLUSTER_SCOPE(),
391             node => IO::Socket::TIPC::TIPC_NODE_SCOPE(),
392             );
393 7 100       28 unless(exists($valid_scopes{$scope})) {
394 4 50       19 $args{Scope} = $scope_values{lc($scope)}
395             if exists $scope_values{lc($scope)};
396             }
397 7         10 $scope = $args{Scope};
398 7 50       25 croak("invalid Scope $scope")
399             unless exists $valid_scopes{$scope};
400             } else {
401 18         58 $args{Scope} = IO::Socket::TIPC::TIPC_NODE_SCOPE();
402             }
403              
404             # check that we do have the arguments we need.
405 25 50       60 return undef unless check_prereqs_for_address_type(\%args);
406 25         133 my $sockaddr = _tipc_create();
407 25         60 _tipc_fill_common($sockaddr, $args{Scope});
408 25 100       75 if($args{AddrType} eq 'id') {
    100          
    50          
409 6         24 _tipc_fill_id_pieces($sockaddr, @args{"Ref","Zone","Cluster","Node"});
410             } elsif($args{AddrType} eq 'name') {
411 13         54 _tipc_fill_name($sockaddr, @args{"Type","Instance","Domain"});
412             } elsif($args{AddrType} eq 'nameseq') {
413 6         16 _tipc_fill_nameseq($sockaddr, @args{"Type","Lower","Upper"});
414             } else {
415 0         0 croak("invalid AddrType $args{AddrType}");
416             }
417 25         86 return $sockaddr;
418             }
419              
420             sub new_from_data {
421 0     0 0 0 my ($package, $data) = @_;
422 0         0 get_family(\$data); # this calls _sanity_check
423 0         0 return bless(\$data, $package);
424             }
425              
426              
427             =head1 METHODS
428              
429             =head2 stringify()
430              
431             B returns a string representing the sockaddr. These
432             strings are the same as the ones used in the TIPC documentation,
433             see I (linked to in B). Depending
434             on the address type, it will return something that looks like one of:
435              
436             "<1.2.3:4>" # ID, addr = 1.2.3, ref = 4
437             "{4242, 100}" # NAME, type = 4242, instance = 100
438             "{4242, 100, 101}" # NAMESEQ, type = 4242, range 100-101
439              
440             Note that these strings are intended for use as shorthand, with
441             someone familiar with TIPC. They do not include all the fields of
442             the sockaddr structure, and sometimes the hidden fields are important.
443             In particular, they are missing the I and I fields,
444             which affect how far away binding/connecting may occur for Is and
445             Is. If you need to store an address for reuse, you are better
446             off reusing the Sockaddr object itself, rather than storing one of
447             these strings.
448              
449             =head2 get/set routines
450              
451             The C structure looks like this (minor edits for clarity):
452              
453             struct sockaddr_tipc {
454             unsigned short family;
455             unsigned char addrtype;
456             signed char scope;
457             union {
458             struct {
459             __u32 ref;
460             __u32 node;
461             } id;
462             struct {
463             __u32 type;
464             __u32 lower;
465             __u32 upper;
466             } nameseq;
467             struct {
468             struct {
469             __u32 type;
470             __u32 instance;
471             } name;
472             __u32 domain;
473             } name;
474             } addr;
475             };
476              
477             Each of these fields has methods to get and set it. The only
478             exception is "family", which is always set to I, and
479             has very good reasons for being read-only.
480              
481             An exhaustive list of these methods follows. All functions return
482             integers, "val" means an unsigned integer argument, "<1.2.3>" means a
483             string-address argument (obviously).
484              
485             =over
486              
487             =item global stuff
488              
489             get_family()
490             get_addrtype() set_addrtype(val)
491             get_scope() set_scope(val)
492              
493             =item TIPC_ADDR_ID stuff
494              
495             get_ref() set_ref(val)
496             get_id() set_id(val) or set_id("<1.2.3>")
497             get_zone() set_zone(val)
498             get_cluster() set_cluster(val)
499             get_node() set_node(val)
500              
501             NOTE: for id-style addresses, direct access to the address as a whole (id) is
502             allowed, as well as its constituent components (zone, cluster, and node).
503             This may cause confusion, since the whole address is called "node" in the C
504             structure, but "node" refers to only a portion of the address here.
505              
506              
507             =item TIPC_ADDR_NAME stuff
508              
509             get_ntype() set_ntype(val)
510             get_instance() set_instance(val)
511             get_domain() set_domain(val) or set_domain("<1.2.3>")
512              
513             =item TIPC_ADDR_NAMESEQ stuff
514              
515             get_stype() set_stype(val)
516             get_lower() set_lower(val)
517             get_upper() set_upper(val)
518              
519             =item Type helpers
520              
521             get_type() set_type(arg)
522              
523             The B/B functions call either B/B,
524             or B/B, depending on whether the I is I
525             or I.
526              
527             =back
528              
529             =cut
530              
531             # NOTE: Most of the above accessor calls go straight to XS code. The
532             # following subroutines are wrappers, to handle cases where I want to
533             # parse a string or something before it goes down to the XS layer.
534              
535             # wrap set_domain: accept string-address arguments
536             sub set_domain {
537 2     2 0 5 my ($self, $addr) = @_;
538 2 100       13 unless(looks_like_number($addr)) {
539 1         3 my $components = {};
540 1         6 tipc_parse_string($components, $addr);
541 1 50       5 croak "'domain' is an address field."
542             unless $$components{AddrType} eq 'id';
543 1         44 $addr = tipc_addr(@$components{'Zone', 'Cluster', 'Node'});
544             }
545 2         9 return $self->_tipc_set_domain($addr);
546             }
547              
548             # wrap set_id: accept string-address arguments
549             sub set_id {
550 2     2 0 3521 my ($self, $addr) = @_;
551 2 100       15 unless(looks_like_number($addr)) {
552 1         2 my $components = {};
553 1         5 tipc_parse_string($components, $addr);
554 1 50       5 croak "'id' is an address field."
555             unless $$components{AddrType} eq 'id';
556 1         7 $addr = tipc_addr(@$components{'Zone', 'Cluster', 'Node'});
557             }
558 2         8 return $self->_tipc_set_id($addr);
559             }
560              
561              
562             =head1 SUBROUTINES (non-methods)
563              
564             =head2 tipc_zone(int)
565              
566             Unpacks the Zone from a TIPC address (integer). You can also pass it a string
567             address, like "<1.2.3>". Returns the zone as an integer. Example below.
568              
569             =cut
570              
571             sub tipc_zone {
572 3     3 1 8 my ($addr) = @_;
573 3 100       14 unless(looks_like_number($addr)) {
574 1         2 my $components = {};
575 1         4 tipc_parse_string($components, $addr);
576 1 50       4 croak "'zone' is an 'id' address field."
577             unless $$components{AddrType} eq 'id';
578 1         4 $addr = tipc_addr(@$components{'Zone', 'Cluster', 'Node'});
579             }
580 3         15 return _tipc_zone($addr);
581             }
582              
583             =head2 tipc_cluster(int)
584              
585             Unpacks the Cluster from a TIPC address (integer). You can also pass it a
586             string address, like "<1.2.3>". Returns the cluster as an integer.
587              
588             my $zone = tipc_zone(0x01002003); # $zone is now set to 1
589             my $cluster = tipc_zone(0x01002003); # $cluster is now set to 2
590             my $node = tipc_zone(0x01002003); # $node is now set to 3
591             printf("<%i.%i.%i>\n",
592             $zone, $cluster, $node); # prints <1.2.3>
593              
594             =cut
595              
596             sub tipc_cluster {
597 3     3 1 7 my ($addr) = @_;
598 3 100       14 unless(looks_like_number($addr)) {
599 1         3 my $components = {};
600 1         3 tipc_parse_string($components, $addr);
601 1 50       5 croak "'cluster' is an 'id' address field."
602             unless $$components{AddrType} eq 'id';
603 1         5 $addr = tipc_addr(@$components{'Zone', 'Cluster', 'Node'});
604             }
605 3         20 return _tipc_cluster($addr);
606             }
607              
608             =head2 tipc_node(int)
609              
610             Unpacks the Node from a TIPC address (integer). You can also pass it a string
611             address, like "<1.2.3>". Returns the node as an integer. Example above.
612              
613             =cut
614              
615             sub tipc_node {
616 3     3 1 8 my ($addr) = @_;
617 3 100       11 unless(looks_like_number($addr)) {
618 1         3 my $components = {};
619 1         4 tipc_parse_string($components, $addr);
620 1 50       12 croak "'node' is an 'id' address field."
621             unless $$components{AddrType} eq 'id';
622 1         4 $addr = tipc_addr(@$components{'Zone', 'Cluster', 'Node'});
623             }
624 3         15 return _tipc_node($addr);
625             }
626              
627              
628             =head2 tipc_addr(int)
629              
630             Packs a zone, cluster and node into a tipc address. You can also pass it
631             a "<1.2.3>" string address.
632              
633             my $addr = tipc_addr($zone, $cluster, $node);
634             printf("0x%x\n", $addr); # prints 0x01002003
635              
636             =cut
637              
638             sub tipc_addr {
639 9     9 1 1915 my ($zone, $cluster, $node) = @_;
640 9 100       35 unless(looks_like_number($zone)) {
641 1         2 my $addr = $zone;
642 1         2 my $components = {};
643 1         3 tipc_parse_string($components, $addr);
644 1 50       4 croak "this is not an 'id' address."
645             unless $$components{AddrType} eq 'id';
646 1         9 return _tipc_addr(@$components{'Zone', 'Cluster', 'Node'});
647             }
648 8         45 return _tipc_addr($zone, $cluster, $node);
649             }
650              
651              
652             =head2 tipc_parse_string(hashref, string)
653              
654             Given a string that looks like "<1.2.3:4>", "<1.2.3>", "{1, 2}", or
655             "{1, 2, 3}", chop it into its components. Puts the components into
656             appropriately named keys in hashref, like I, I,
657             I, I, I, I, I, I. It also
658             guesses the I of the string you passed. Returns 1 on
659             success, croaks on error.
660              
661             my $href = {};
662             tipc_parse_string($href, "<1.2.3:4>");
663             printf("Address <%i.%i.%i:%i> is of type %s\n",
664             @$href{"Zone", "Cluster", "Node", "Ref", "AddrType"});
665             # prints "Address <1.2.3:4> is of type id\n"
666              
667             This is a function which B() uses internally, to turn user
668             provided garbage into some values it can actually use. There is
669             no need to call it directly, unless you want to use the same parser
670             for some other reason, like input checking.
671              
672             =cut
673              
674             sub tipc_parse_string {
675 17     17 1 29 my ($args, $string) = @_;
676             # we got a string. we accept the following types of string:
677             # ID: '' (REF=0)
678             # ID (dec): '12345' (REF=0)
679             # ID (hex): '0x01002003' (REF=0)
680             # ID+REF: ''
681             # NAME: '{a,b}'
682             # NAMESEQ: '{a,b,c}'
683 17         22 my $valid = 0;
684             # handle string ID+REF or string ID
685 17 100       92 if($string =~ /^<(\d+)\.(\d+)\.(\d+)(:(\d+))?>$/) {
686 12         26 $$args{AddrType} = 'id';
687 12         39 $$args{Zone} = $1;
688 12         29 $$args{Cluster} = $2;
689 12         25 $$args{Node} = $3;
690 12 100       35 $$args{Ref} = $5 if defined $5;
691 12 100       39 $$args{Ref} = 0 unless defined $$args{Ref};
692 12         47 $valid = 1;
693             }
694             # handle decimal ID
695 17 50       67 if($string =~ /^(\d+)$/) {
696 0         0 $$args{Zone} = tipc_zone($1);
697 0         0 $$args{Cluster} = tipc_cluster($1);
698 0         0 $$args{Node} = tipc_node($1);
699 0         0 printf(STDERR "dec: <%i.%i.%i>\n",@$args{'Zone','Cluster','Node'});
700 0         0 $$args{AddrType} = 'id';
701 0         0 $valid = 1;
702             }
703             # handle hex ID
704 17 100       45 if($string =~ /^0x([0-9a-fA-F]{1,8})$/) {
705 1         7 $$args{Zone} = tipc_zone(hex($1));
706 1         6 $$args{Cluster} = tipc_cluster(hex($1));
707 1         6 $$args{Node} = tipc_node(hex($1));
708 1         3 $$args{AddrType} = 'id';
709 1         1 $valid = 1;
710             }
711            
712             # handle string NAME
713 17 100       47 if($string =~ /^\{(\d+),\s*(\d+)\}$/) {
714 2         6 $$args{AddrType} = 'name';
715 2         9 $$args{Type} = $1;
716 2         5 $$args{Instance} = $2;
717 2         4 $valid = 1;
718             }
719             # handle string NAMESEQ
720 17 100       42 if($string =~ /^\{(\d+),\s*(\d+),\s*(\d+)\}$/) {
721 2         4 $$args{AddrType} = 'nameseq';
722 2         5 $$args{Type} = $1;
723 2         5 $$args{Lower} = $2;
724 2         3 $$args{Upper} = $3;
725 2         4 $valid = 1;
726             }
727 17 50       31 croak("string argument '$string' is not a valid TIPC address.")
728             unless($valid);
729 17         35 return 1;
730             }
731              
732             =head1 EXPORT
733              
734             None by default.
735              
736             =head2 Exportable subroutines
737              
738             tipc_addr
739             tipc_zone
740             tipc_cluster
741             tipc_node
742             tipc_parse_string
743              
744             =cut
745              
746             our @EXPORT = qw();
747             our @EXPORT_OK = qw();
748              
749             our %EXPORT_TAGS = (
750             'all' => [ qw(
751             tipc_addr tipc_zone tipc_cluster tipc_node tipc_parse_string
752             ) ]
753             );
754             Exporter::export_ok_tags('all');
755              
756             1;
757             __END__