9 use vars qw ($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
12 #Give a hoot don't pollute, do not export more than needed by default
21 IsisDB - Read CDS/ISIS database
26 my $isis = new IsisDB(
27 isisdb => './cds/cds',
32 This module will read CDS/ISIS databases and create hash values out of it.
33 It can be used as perl-only alternative to OpenIsis module.
39 # my $ORDN; # Nodes Order
40 # my $ORDF; # Leafs Order
41 # my $N; # Number of Memory buffers for nodes
42 # my $K; # Number of buffers for first level index
43 # my $LIV; # Current number of Index Levels
44 # my $POSRX; # Pointer to Root Record in N0x
45 # my $NMAXPOS; # Next Available position in N0x
46 # my $FMAXPOS; # Next available position in L0x
47 # my $ABNORMAL; # Formal BTree normality indicator
56 my $f = shift || die "Read32 needs file handle";
57 read($$f,$b,4) || die "can't read 4 bytes from $$f from position ".tell($f);
58 return unpack("l",$b);
63 Open CDS/ISIS database
65 my $isis = new IsisDB(
66 isisdb => './cds/cds',
71 Options are described below:
77 Prefix path to CDS/ISIS. It should contain full or relative path to database
78 and common prefix of C<.FDT>, C<.MST>, C<.CNT>, C<.XRF> and C<.MST> files.
82 Boolean flag to specify if field definition table should be read. It's off
87 Dump a C<lot> of debugging output.
91 It will also set C<$isis-E<gt>{'maxmfn'}> which is maximum MFN stored in database.
100 $self->{isisdb} = {@_}->{isisdb} || croak "new needs database name as argument!";
102 $self->{debug} = {@_}->{debug};
104 # if you want to read .FDT file use read_fdt argument when creating class!
105 if ({@_}->{read_fdt} && -e $self->{isisdb}.".FDT") {
107 # read the $db.FDT file for tags
110 open(fileFDT, $self->{isisdb}.".FDT") || croak "can't read '$self->{isisdb}.FDT': $!";
115 my $name=substr($_,0,30);
116 my $tag=substr($_,50,3);
121 $self->{'TagName'}->{$tag}=$name;
132 # Get the Maximum MFN from $db.MST
134 open(fileMST,$self->{isisdb}.".MST") || croak "can't read '$self->{isisdb}.MST': $!";
136 # MST format: (* = 32 bit signed)
138 # NXTMFN* MFN to be assigned to the next record created
139 # NXTMFB* last block allocated to master file
140 # NXTMFP offset to next available position in last block
141 # MFTYPE always 0 for user db file (1 for system)
143 $self->{'NXTMFN'}=$self->Read32(\*fileMST) || carp "NXTNFN is zero";
146 $self->{'maxmfn'} = $self->{'NXTMFN'} - 1;
150 # Get the index information from $db.CNT
152 open(fileCNT, $self->{isisdb}.".CNT") || croak "can't read '$self->{isisdb}.CNT': $!";
154 # There is two 26 Bytes fixed lenght records
156 # 0: IDTYPE BTree type 16
157 # 2: ORDN Nodes Order 16
158 # 4: ORDF Leafs Order 16
159 # 6: N Number of Memory buffers for nodes 16
160 # 8: K Number of buffers for first level index 16
161 # 10: LIV Current number of Index Levels 16
162 # 12: POSRX* Pointer to Root Record in N0x 32
163 # 16: NMAXPOS* Next Available position in N0x 32
164 # 20: FMAXPOS* Next available position in L0x 32
165 # 24: ABNORMAL Formal BTree normality indicator 16
171 my @flds = qw(ORDN ORDF N K LIV POSRX NMAXPOS FMAXPOS ABNORMAL);
173 my $buff = shift || return;
174 my @arr = unpack("ssssssllls", $buff);
176 my $IDTYPE = shift @arr;
178 $self->{$IDTYPE}->{$_} = abs(shift @arr);
183 read(fileCNT, $buff, 26);
184 $self->unpack_cnt($buff);
186 read(fileCNT, $buff, 26);
187 $self->unpack_cnt($buff);
192 print Dumper($self) if ($self->{debug});
194 # open files for later
195 open($self->{'fileXRF'}, $self->{isisdb}.".XRF") || croak "can't open '$self->{isisdb}.XRF': $!";
197 open($self->{'fileMST'}, $self->{isisdb}.".MST") || croak "can't open '$self->{isisdb}.MST': $!";
199 $self ? return $self : return undef;
204 Read record with selected MFN
206 my $rec = $isis->fetch(55);
208 Returns hash with keys which are field names and values are unpacked values
216 my $mfn = shift || croak "fetch needs MFN as argument!";
218 print "fetch: $mfn\n" if ($self->{debug});
221 my $mfnpos=($mfn+int(($mfn-1)/127))*4;
223 print "seeking to $mfnpos in file '$self->{isisdb}.XRF'\n" if ($self->{debug});
224 seek($self->{'fileXRF'},$mfnpos,0);
226 # read XRFMFB abd XRFMFP
227 my $pointer=$self->Read32(\*{$self->{'fileXRF'}});
229 my $XRFMFB = int($pointer/2048);
230 my $XRFMFP = $pointer - ($XRFMFB*2048);
232 print "XRFMFB: $XRFMFB XRFMFP: $XRFMFP\n" if ($self->{debug});
234 # XXX fix this to be more readable!!
235 # e.g. (XRFMFB - 1) * 512 + XRFMFP
237 my $offset = $pointer;
238 my $offset2=int($offset/2048)-1;
239 my $offset22=int($offset/4096);
240 my $offset3=$offset-($offset22*4096);
242 $offset3=$offset3-2048;
244 my $offset4=($offset2*512)+$offset3;
246 print "$offset - $offset2 - $offset3 - $offset4\n" if ($self->{debug});
248 # Get Record Information
250 seek($self->{'fileMST'},$offset4,0);
252 my $value=$self->Read32(\*{$self->{'fileMST'}});
255 print ("Error: The MFN:".$mfn." is not found in MST(".$value.")");
256 return -1; # XXX deleted record?
259 # $MFRL=$self->Read16($fileMST);
260 # $MFBWB=$self->Read32($fileMST);
261 # $MFBWP=$self->Read16($fileMST);
262 # $BASE=$self->Read16($fileMST);
263 # $NVF=$self->Read16($fileMST);
264 # $STATUS=$self->Read16($fileMST);
267 read($self->{'fileMST'}, $buff, 14);
269 my ($MFRL,$MFBWB,$MFBWP,$BASE,$NVF,$STATUS) = unpack("slssss", $buff);
271 print "MFRL: $MFRL MFBWB: $MFBWB MFBWP: $MFBWP BASE: $BASE NVF: $NVF STATUS: $STATUS\n" if ($self->{debug});
273 # Get Directory Format
279 for (my $i = 0 ; $i < $NVF ; $i++) {
281 # $TAG=$self->Read16($fileMST);
282 # $POS=$self->Read16($fileMST);
283 # $LEN=$self->Read16($fileMST);
285 read($self->{'fileMST'}, $buff, 6);
286 my ($TAG,$POS,$LEN) = unpack("sss", $buff);
288 print "TAG: $TAG POS: $POS LEN: $LEN\n" if ($self->{debug});
290 # The TAG does not exists in .FDT so we set it to 0.
292 # XXX This is removed from perl version; .FDT file is updated manually, so
293 # you will often have fields in .MST file which aren't in .FDT. On the other
294 # hand, IsisMarc doesn't use .FDT files at all!
296 #if (! $self->{TagName}->{$TAG}) {
305 # Get Variable Fields
307 delete $self->{record};
309 for (my $i = 0 ; $i < $NVF ; $i++) {
311 read($self->{'fileMST'},$rec,$FieldLEN[$i]);
312 push @{$self->{record}->{$FieldTAG[$i]}}, $rec;
316 # The record is marked for deletion
321 print Dumper($self) if ($self->{debug});
323 return $self->{'record'};
328 Dump ascii output of selected MFN
330 print $isis->to_ascii(55);
337 my $mfn = shift || croak "need MFN";
339 my $rec = $self->fetch($mfn);
343 foreach my $f (sort keys %{$rec}) {
344 $out .= "\n$f\t".join("\n$f\t",@{$self->{record}->{$f}});
353 # XXX porting from php left-over:
355 # do I *REALLY* need those methods, or should I use
356 # $self->{something} directly?
358 # Probably direct usage is better!
363 return $self->{TagName};
368 return $self->{NXTMFN};
375 This module has been very lightly tested. Use with caution and report bugs.
382 http://www.rot13.org/~dpavlin/
384 This module is based heavily on code from LIBISIS.PHP - Library to read ISIS files V0.1.1
385 written in php and (c) 2000 Franck Martin - <franck@sopac.org> released under LGPL.
389 This program is free software; you can redistribute
390 it and/or modify it under the same terms as Perl itself.
392 The full text of the license can be found in the
393 LICENSE file included with this module.
398 L<http://www.openisis.org|OpenIsis>, perl(1).