catch the one $class instance missed in less.pm
[p5sagit/p5-mst-13.2.git] / lib / Search / Dict.pm
CommitLineData
a0d0e21e 1package Search::Dict;
2require 5.000;
3require Exporter;
4
b75c8c73 5use strict;
6
6e372064 7our $VERSION = '1.02';
b75c8c73 8our @ISA = qw(Exporter);
9our @EXPORT = qw(look);
a0d0e21e 10
5be1dfc7 11=head1 NAME
a0d0e21e 12
5be1dfc7 13Search::Dict, look - search for key in dictionary file
14
15=head1 SYNOPSIS
16
17 use Search::Dict;
6e372064 18 look *FILEHANDLE, $key, $dict, $fold;
19
20 use Search::Dict;
21 look *FILEHANDLE, $params;
5be1dfc7 22
23=head1 DESCRIPTION
24
25Sets file position in FILEHANDLE to be first line greater than or equal
26(stringwise) to I<$key>. Returns the new file position, or -1 if an error
27occurs.
28
29The flags specify dictionary order and case folding:
30
31If I<$dict> is true, search by dictionary order (ignore anything but word
37ef5c3b 32characters and whitespace). The default is honour all characters.
5be1dfc7 33
37ef5c3b 34If I<$fold> is true, ignore case. The default is to honour case.
35
37ef5c3b 36If there are only three arguments and the third argument is a hash
37reference, the keys of that hash can have values C<dict>, C<fold>, and
6e372064 38C<comp> or C<xfrm> (see below), and their correponding values will be
39used as the parameters.
40
41If a comparison subroutine (comp) is defined, it must return less than zero,
42zero, or greater than zero, if the first comparand is less than,
43equal, or greater than the second comparand.
44
45If a transformation subroutine (xfrm) is defined, its value is used to
46transform the lines read from the filehandle before their comparison.
5be1dfc7 47
48=cut
a0d0e21e 49
50sub look {
6e372064 51 my($fh,$key,$dict,$fold) = @_;
52 my ($comp, $xfrm);
37ef5c3b 53 if (@_ == 3 && ref $dict eq 'HASH') {
6e372064 54 my $params = $dict;
37ef5c3b 55 $dict = 0;
6e372064 56 $dict = $params->{dict} if exists $params->{dict};
57 $fold = $params->{fold} if exists $params->{fold};
58 $comp = $params->{comp} if exists $params->{comp};
59 $xfrm = $params->{xfrm} if exists $params->{xfrm};
37ef5c3b 60 }
61 $comp = sub { $_[0] cmp $_[1] } unless defined $comp;
5be1dfc7 62 local($_);
b75c8c73 63 my(@stat) = stat($fh)
5be1dfc7 64 or return -1;
65 my($size, $blksize) = @stat[7,11];
66 $blksize ||= 8192;
a0d0e21e 67 $key =~ s/[^\w\s]//g if $dict;
37ef5c3b 68 $key = lc $key if $fold;
69 # find the right block
70 my($min, $max) = (0, int($size / $blksize));
71 my $mid;
a0d0e21e 72 while ($max - $min > 1) {
73 $mid = int(($max + $min) / 2);
b75c8c73 74 seek($fh, $mid * $blksize, 0)
5be1dfc7 75 or return -1;
b75c8c73 76 <$fh> if $mid; # probably a partial line
77 $_ = <$fh>;
6e372064 78 $_ = $xfrm->($_) if defined $xfrm;
37ef5c3b 79 chomp;
a0d0e21e 80 s/[^\w\s]//g if $dict;
37ef5c3b 81 $_ = lc $_ if $fold;
82 if (defined($_) && $comp->($_, $key) < 0) {
a0d0e21e 83 $min = $mid;
84 }
85 else {
86 $max = $mid;
87 }
88 }
37ef5c3b 89 # find the right line
a0d0e21e 90 $min *= $blksize;
b75c8c73 91 seek($fh,$min,0)
5be1dfc7 92 or return -1;
b75c8c73 93 <$fh> if $min;
5be1dfc7 94 for (;;) {
b75c8c73 95 $min = tell($fh);
96 defined($_ = <$fh>)
5be1dfc7 97 or last;
6e372064 98 $_ = $xfrm->($_) if defined $xfrm;
37ef5c3b 99 chomp;
a0d0e21e 100 s/[^\w\s]//g if $dict;
37ef5c3b 101 $_ = lc $_ if $fold;
102 last if $comp->($_, $key) >= 0;
a0d0e21e 103 }
b75c8c73 104 seek($fh,$min,0);
a0d0e21e 105 $min;
106}
107
1081;