ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/cvsroot/TDB_FileX/TDB_FileX.pm
Revision: 1.26
Committed: Mon May 5 10:17:01 2025 UTC (16 months, 2 weeks ago) by root
Branch: MAIN
CVS Tags: HEAD
Changes since 1.25: +13 -4 lines
Error occurred while calculating annotation data.
Log Message:
*** empty log message ***

File Contents

# Content
1 package TDB_FileX;
2
3 use common::sense;
4
5 use Exporter ();
6 use XSLoader ();
7
8 our @ISA = qw(Exporter);
9
10 # Items to export into callers namespace by default. Note: do not export
11 # names by default without a very good reason. Use EXPORT_OK instead.
12 # Do not simply export all your public functions/methods/constants.
13
14 our %EXPORT_TAGS = (
15 flags => [qw(
16 ALLOW_NESTING
17 BIGENDIAN
18 CLEAR_IF_FIRST
19 CONVERT
20 DEFAULT
21 DISALLOW_NESTING
22 INCOMPATIBLE_HASH
23 INTERNAL
24 MUTEX_LOCKING
25 NOLOCK
26 NOMMAP
27 NOSYNC
28 SEQNUM
29 VOLATILE
30 )],
31 insert => [qw(
32 INSERT
33 MODIFY
34 REPLACE
35 )],
36 error => [qw(
37 SUCCESS
38 ERR_CORRUPT
39 ERR_EXISTS
40 ERR_IO
41 ERR_LOCK
42 ERR_LOCK_TIMEOUT
43 ERR_NOEXIST
44 ERR_NOLOCK
45 ERR_OOM
46 ERR_EINVAL
47 ERR_RDONLY
48 )],
49 debug => [qw(
50 DEBUG_FATAL
51 DEBUG_ERROR
52 DEBUG_WARNING
53 DEBUG_TRACE}
54 )],
55 );
56
57 our @EXPORT_OK;
58
59 Exporter::export_ok_tags qw(flags insert error debug);
60
61 $EXPORT_TAGS{all} = \@EXPORT_OK;
62
63 our $VERSION = '0.97';
64
65 XSLoader::load __PACKAGE__, $VERSION;
66
67 1;
68 __END__
69
70 =head1 NAME
71
72 TDB_FileX - Perl access to the trivial database library
73
74 =head1 SYNOPSIS
75
76 use TDB_FileX;
77
78 # tie interface
79 tie %hash, TDB_FileX => $filename,
80 hash_size => 8000,
81 mutex => 1,
82 ;
83 $hash{key} = 'value';
84 while (my ($k, $v) = each %hash) { print "$k -> $v\n" }
85
86 # OO interface
87 my $tdb = TDB_FileX->open ($filename, flags => TDB_FileX::CLEAR_IF_FIRST)
88 or die $!;
89
90 $tdb->store (key => 'value') or die $tdb->errorstr;
91 $tdb->traverse (sub { print "$_[0] -> $_[1]\n" });
92
93 =head1 DESCRIPTION
94
95 TDB is a simple database similar to GDBM, but allows multiple simultaneous
96 writers. It's main drawback is the need to manually configure a hash table
97 size in advance - see the C<hash_size> option for C<open>.
98
99 TDB_FileX provides a simple C<tie> interface, similar to DB_File and
100 friends; and an object-oriented interface, which provides access to most
101 of the functions in the TDB library.
102
103 =head2 COMPARISON TO OTHER DBMS
104
105 TDB stands for trivial database - and indeed, the database structure is
106 very simple, requiring manual sizing at creation time, and being limited
107 to 4GB size.
108
109 But otherwise, TDB has features that no other simple DBM has -
110 fine-grained locking, multiprocess database access and transactions.
111
112 GDBM_File for example has a single per-process lock, so only one process
113 can write to the database. Most others have no locking and will happily
114 corrupt your database. TDB_FileX is safe to use from multiple processes.
115
116 As for data safety, GDBM databases easily corrupt (in current version of
117 GDBM), and while GDBM databases can be 30% more compact than equivalent
118 TDB databases, they tend to grow over time (I regularly find 10GB GDBM
119 databases (actual disk usage) that reorganize to 300MB files and have
120 never stored more than that). TDB files do not usually have such bad
121 growth behaviour, but also have no reorganize function to shrink the
122 database again.
123
124 So, if you want safe access from multiple proceses and have a good idea
125 of how many keys to store, TDB_FileX is the thing. If you only want
126 protectioon against obvious data corruption and processes can wait, GDBM
127 is a probably a better choice.
128
129 =head1 ERROR HANDLKING
130
131 The TDB C API is not well designed - among other things, error handling is
132 a bit erratic. To compensate, many TDB functions that normally succeed are
133 marked with a [CROAK] - these will throw an exception on "unusual" error
134 conditions, the exception string being the error message. C<$!> is set to
135 the numerical error value in that case (and will stringify into the wrong
136 OS-error string, so don't do that!).
137
138 This is so that you can concentrate on the important parts without having
139 to check for an error status on every call, while there are still no
140 silent unexpected errors, as TDB_FileX will croak for you.
141
142 The other functions will not generally croak - check their description for
143 details on how errors are handled.
144
145 Functions that are not part of the TDB API (such as
146 C<register_hash_function>) may croak on errors without being marked as
147 such. The above only refers to TDB API functions, as they cannot throw
148 Perl exceptions themselves.
149
150 =head2 FUNCTIONS
151
152 =over 4
153
154 =item $tdb = tie %hash, TDB_FileX => $path[ , key => value...]
155
156 =item $tdb = TDB_FileX->open ($path[, key => value...])
157
158 TDB_FileX constructor (same as C<TIE>). Opens $path and returns a
159 TDB_FileX object. The same arguments may be passed to the C<tie>
160 function. On error, C<undef> is returned.
161
162 You should consider specifying at least C<log_cb> and C<hash_size> (and
163 possibly C<mutex>), everything else has sensible defaults.
164
165 To open a tdb file that is created and used by another application
166 (maximising compatibility):
167
168 my $tdb = TDB_FileX->open ($path, log_cb => sub { warn $_[1] })
169 or die "$path: failed to open\n";
170
171 To successfully open another database, you might have to duplicate some of
172 the settings, e.g. whether mutex locking is used or the hash function. The
173 C<log_cb> output usually will tell you what's wrong.
174
175 To open a tdb file with good performance for many thousands of large keys,
176 maybe not compatible to other programs:
177
178 my $tdb = TDB_FileX->open ($path,
179 hash_size => 10000,
180 hash => "xxh3",
181 mutex => 1,
182 nocow => 1,
183 log_cb => sub { warn $_[1] },
184 ) or die "$path: failed to open\n";
185
186 The following key-value pairs are understood:
187
188 =over
189
190 =item tdb_flags => $flags (default: C<TDB_FileX::DEFAULT>)
191
192 A set of flags that influence the behaviour and format of the database.
193
194 =over
195
196 =item C<DEFAULT>
197
198 Same as C<0> - no flags.
199
200 =item C<CLEAR_IF_FIRST>
201
202 If this is the first open, wipe the db.
203
204 =item C<INTERNAL>
205
206 In-memory database only, path will be ignored.
207
208 =item C<NOLOCK>
209
210 Don't do any locking.
211
212 =item C<NOMMAP>
213
214 Don't use mmap.
215
216 =item C<NOSYNC>
217
218 Don't use synchronous transactions.
219
220 =item C<SEQNUM>
221
222 Maintain a sequence number.
223
224 =item C<VOLATILE>
225
226 Activate the per-hashchain freelist, default 5. (Same as calling
227 C<set_max_dead> with C<5> instead of the default C<0>).
228
229 =item C<ALLOW_NESTING>
230
231 Allow transactions to nest.
232
233 =item C<DISALLOW_NESTING>
234
235 Disallow transactions to nest.
236
237 =item C<INCOMPATIBLE_HASH>
238
239 Better default hash function, but can't be opened by tdb < 1.2.6.
240
241 =item C<MUTEX_LOCKING>
242
243 Optimized locking using robust mutexes if supported,
244
245 =back
246
247 =item open_flags => $flags (default: C<Fcntl::O_RDWR | Fcntl::O_CREAT>)
248
249 Standard open flags, as used in C<sysopen>.
250
251 =item mode => $mode (default: C<0666>)
252
253 Standard file open mode, as used in C<sysopen>. Only used when creating
254 the database file.
255
256 =item hash_size => $int (default: internal to libtdb, but normally C<131>)
257
258 The size of the internal hash table - only used when creating the
259 database. The default is usually very low and only good for a few thousand
260 keys. As a rule of thumb, it should be at least one percent of the number
261 of keys you plan to store, e.g. for C<800000> keys you should use around a
262 size of C<8000>.
263
264 Every hash entry is only 4 octets, so usually it isn't an issue to make the
265 hash table too large.
266
267 To give you an idea of the performance, I inserted about 600000 records,
268 3GB of data, into a TDB file, using different hash sizes.
269
270 size time
271 131 160s
272 256 85s
273 1024 12s
274 4096 5s
275 8192 4s
276
277 As you can see, the default hash table size for this case caused it to use
278 40 times the time to insert than a larger hash table, the optimum being
279 around 75 keys per hash entry. But this database was easily cached in
280 memory - if that is not the case, you might want to consider a hash table
281 that is larger than the number of keys you want to store - each hash slot
282 only uses 4 octets.
283
284 =item log_cb => $cb->($level, $msg)
285
286 Specifies a code reference that is called with a message and a log level
287 (lower means more important, there are C<DEBUG_FATAL>, C<DEBUG_ERROR>,
288 C<DEBUG_WARNING> and C<DEBUG_TRACE}>). Unlike the "debug" in the name
289 might indicate, if you want to find out why, for instance, you could not
290 open a database, you need to use a logging callback.
291
292 =item hash => $hash (default: C<undef>)
293
294 Selects a hash function to use.
295
296 =over
297
298 =item C<"default"> or C<undef>
299
300 Use autodetection and use either C<"jenkins"> or the original tdb hash function.
301
302 =item C<"jenkins">
303
304 The jenkins hash function. Relatively fast, and recommended for modern tdb databases
305 that need to be interoperable between implementations.
306
307 =item C<"fnv1ax">
308
309 The FNV1-A hash with 32 bit post-mixing. Pretty good and very fast for
310 keys up to 10-20 octets.
311
312 =item C<"xxh3">
313
314 The XXH3 hash. Very good and fast especially for long keys.
315
316 =item a value returned by C<register_hash_function>
317
318 Use a custom hash function registered via C<register_hash_function>.
319
320 =back
321
322 =item mutex => $bool (default: C<0>)
323
324 TDB can take advantage of fast interprocess mutexes, which can be orders
325 of magnitude faster than the syscall-based locking used by default, but
326 only work on the same machine (the default C<fcntl>-based locking works
327 e.g. over NFS).
328
329 Normally, you need to call C<TDB_FileX::runtime_check_for_robust_mutexes>
330 and set the C<MUTEX_LOCKING> flag if support is indicated.
331
332 This option, when true, enables C<MUTEX_LOCKING> if it is supported
333 by the platform - which involves forking the process when opening the
334 database. When false, it will remove the flag.
335
336 It is recommended to keep this on, but enabling this changes the format
337 of the database, so might not be an option if interoperability with other
338 programs is required.
339
340 =item nocow => $bool (default: C<0>)
341
342 Set the no-copy-on-write flag I<iff> this flag is true, C<open_flags>
343 contains C<O_CREAT>, C<tdb_flags> do not contain C<INTERNAL> and this is
344 supported on the platform and filesystem. Copy-on-write filesystems have
345 to make a copy of every block written to, which can both be costly and can
346 cause massive fragmentation of the database file.
347
348 Setting the no-copy-on-write flag (same as C<chattr +C>) disables this,
349 usually at the expense of data protection (checksumming), reducing the
350 safety to the level of a normal filesystem such as ext4.
351
352 This is best effort and usually only takes effect when the
353 database is initially created. If it fails, TDB_FileX will simply
354 continue. Correctness should not be affected either way.
355
356 =back
357
358 There is no explicit close function. The database is closed implicitly
359 when there are no remaining references.
360
361 =item $tdb->set_logging_function ($cb->($level, $msg))
362
363 Set the logging function to use when this database object encounters
364 errors.
365
366 $cb is called with the severity level (an integer) and the message (a
367 string).
368
369 =item $code = $tdb->error
370
371 Returns the current error state of the C<$tdb> object. See the list of
372 error codes given in L<EXPORTS>.
373
374 =item $mesage = $tdb->errorstr
375
376 Returns a printable string that describes the error state of the
377 database.
378
379 =item $tdb->store ($key, $value[, $flag=REPLACE]) [CROAK]
380
381 Store $value in the database with key w$key. The $flag defaults to
382 C<REPLACE>, but can also be C<INSERT> or C<MODIFY>, see tdb_store(3) for
383 details.
384
385 =item $tdb->append ($key, $value) [CROAK]
386
387 Appends $data to the data already stored for $key, or creates a new entry with it.
388
389 =item $data = $tdb->fetch ($key) [CROAK]
390
391 Fetch the value associated with $key, or C<undef> if it is not found (or
392 on any error).
393
394 =item $tdb->delete ($key) [CROAK]
395
396 Delete the value associated with $key.
397
398 =item $bool = $tdb->exists ($key)
399
400 Return true if the $key is found, false otherwise.
401
402 =item $key = $tdb->firstkey
403
404 Return the key of the first value in the database. Returns C<undef> on
405 failure or if there are no keys in the database. See tdb_firstkey(3)
406 for details.
407
408 =item $key = $tdb->nextkey ($lastkey)
409
410 Return the next key in the database after $lastkey. Returns C<undef> on
411 failure or if there are no more keys in the database. See tdb_nextkey(3)
412 for details.
413
414 =item $tdb->reopen [CROAK]
415
416 Closes and reopens the database. Required after a
417 L<fork|perlfunc/fork>, if both processes wish to use the database.
418
419 B<NB:> If C<reopen> fails, then it is unsafe to call any further methods
420 on C<$tdb>. Thus, the only way to find out I<why> C<reopen> failed is to
421 use a logging function.
422
423 =item TDB_FileX::reopen_all [CROAK]
424
425 Closes and reopens all open databases. See C<reopen>.
426
427 B<NB:> If C<reopen_all> fails, there is no indication of I<which> C<$tdb>
428 objects failed or why. If you have to survive failures, you may wish to do
429 your own C<reopen> loop instead.
430
431 =item $tdb->traverse ($cb->($key, $data)) [CROAK]
432
433 Call $cb for each entry in the database. The callback should return false
434 to continue, or a true value to abort the traversal.
435
436 The callback is called with the key and value as arguments and should
437 return a false value if you wish to continue traversal, and a true value
438 if the traversal should be aborted.
439
440 C<traverse> returns the number of elements traversed. If $cb is C<undef>,
441 then this function simply counts the number of elements.
442
443 =item $tdb->traverse_read ($cb->($key, $data)) [CROAK]
444
445 Like C<traverse>, but only acquires a read lock.
446
447 =item $tdb->lockall [CROAK]
448
449 Lock an entire database with an exclusive write lock, returning false on
450 error. The purpose of this call is to avoid locking overhead for many
451 operations, but the database has to be unlocked manually when done.
452
453 =item $tdb->unlockall [CROAK]
454
455 Unlock an entire database previously locked with
456 C<lockall>.
457
458 =item $tdb->lockall_read [CROAK]
459
460 Same as C<lockall>, but uses a shared read lock instead of a writer lock.
461
462 =item $tdb->unlockall_read [CROAK]
463
464 Opposite of C<lockall_read>.
465
466 =item $tdb->lockall_mark [CROAK]
467
468 =item $tdb->lockall_unmark [CROAK]
469
470 These apparently mark and unmark locks internally, but do not actually do
471 locking. Probably you should not use this, but feel free to tell me when
472 these are useful.
473
474 =item $tdb->lockall_nonblock [CROAK]
475
476 =item $success = $tdb->lockall_read_nonblock [CROAK]
477
478 Try to lock, but instead of waiting, fail if the lock could not
479 be acquired. Returns true if the lock could be acquired, false
480 otherwise. Croaks on all other errors.
481
482 =item $tdb->transaction_start [CROAK]
483
484 Starts a transaction - all operations will be queued, but not applied
485 to the database, until the transaction is either committed C<transaction_commit>
486 or aborted/thrown away, with C<transaction_cancel>.
487
488 =item $tdb->transaction_start_nonblock [CROAK]
489
490 Tries to start a transaction, but instead of waiting, fail if the lock
491 could not be acquired. Returns true if the transaction could be started,
492 false otherwise. Croaks on all other errors.
493
494 Please tell me what this does.
495
496 =item $tdb->transaction_commit [CROAK]
497
498 Applies all changes in the transaction.
499
500 =item $tdb->transaction_cancel [CROAK]
501
502 Throws away all changes in the transaction.
503
504 =item $tdb->transaction_prepare_commit [CROAK]
505
506 Instead of calling C<transaction_commit> you can do the commit in two
507 phases by calling this method before C<transaction_commit>, which does the
508 expensive steps first.
509
510 =item $bool = $tdb->transaction_active
511
512 Returns true if a transaction is currently active.
513
514 =item $tdb->enable_seqnum
515
516 Enables sequence number generation support for the database.
517
518 =item $seq = $tdb->get_seqnum
519
520 Returns the current sequence number (a 32 bit unsigned integer).
521
522 =item $tdb->increment_seqnum_nonblock
523
524 Increments the sequence number. Note that the sequence number is also
525 incremented by TDB itself on many operations.
526
527 =item $size = $tdb->hash_size
528
529 Returns the size of the hash table. The size cannot be changed other than
530 by recreating the database.
531
532 =item $octets = $tdb->map_size
533
534 Returns the current mmap size for the database.
535
536 =item $flags = $tdb->get_flags
537
538 Returns the flags for the database (the same as the C<tdb_flags> in C<open>).
539
540 =item $tdb->add_flags ($flag)
541
542 Tried to add flags to the database - yes, the parameter says C<$flag> (singular)
543 but the documentation and the code say I<flags> (plural).
544
545 =item $tdb->remove_flags ($flag)
546
547 Attempts to remove flags from the database.
548
549 =item $tdb->set_max_dead ($max_dead)
550
551 Set the maximum number of dead records per hash chain.
552
553 =item $fileno = $tdb->fd
554
555 Returns the file descriptor (not file handle) for the underlying database file.
556
557 =item $path = $tdb->name
558
559 Returns the path used to open the database.
560
561 =item $tdb->wipe_all [CROAK]
562
563 Efficientlly deletes all entries in the database. This does not shrink the
564 file itself.
565
566 =item $tdb->repack [CROAK]
567
568 Tries to improve layout of the database by copying all items into a
569 temporary in-memory database, wiping the database, and copying all items
570 back. Yes, everything must fit into memory.
571
572 =item $bool = $tdb->check ([$cb->($key, $value)])
573
574 Does extensive checks on the database, optionally (if not C<undef> or
575 missing) calling a check function for each pair, which must return true if
576 the data is valid.
577
578 Returns a boolean indicating whether the database was found healthy and
579 all the calls to the callback returned true.
580
581 =item $bool = $tdb->rescue ($cb->($key, $value))
582
583 Tries to recover some or all key-value pairs from a potentially damanged
584 database file. For each recovered pair it calls the given callback.
585
586 Returns a boolean indicating whether the database was found healthy and
587 all the calls to the callback returned true.
588
589 =item $bool = TDB_FileX::runtime_check_for_robust_mutexes
590
591 Tests whether robust mutexes are available for locking. This involves
592 forking the process, so it can be costly and problematic. This function
593 needs to be called before using the C<MUTEX_LOCKING> flag. But see the
594 C<mutex> parametrer to C<open> for an alternative.
595
596 =item $tdb->dump_all
597
598 Dump the records and freelist to standard output in an almost human
599 readable form.
600
601 =item $summary = $tdb->summary
602
603 Return a textual summary of the database. The format isn't documented,
604 but for some random database, I got this string:
605
606 Size of file/data: 325001216/238324906
607 Header offset/logical size: 4001792/320999424
608 Number of records: 117209
609 Incompatible hash: no
610 Active/supported feature flags: 0x00000001/0x00000001
611 Robust mutexes locking: yes
612 Smallest/average/largest keys: 11/36/102
613 Smallest/average/largest data: 10/1997/2921430
614 Smallest/average/largest padding: 9/530/749374
615 Number of dead records: 0
616 Smallest/average/largest dead records: 0/0/0
617 Number of free records: 1126
618 Smallest/average/largest free records: 12/15312/14681136
619 Number of hash chains: 100000
620 Smallest/average/largest hash chains: 0/1/8
621 Number of uncoalesced records: 0
622 Smallest/average/largest uncoalesced runs: 0/0/0
623 Percentage keys/data/padding/free/dead/rechdrs&tailers/hashes: 1/72/19/5/0/1/0
624
625 =item $octets = $tdb->freelist_size
626
627 Returns the total number of free (unused) octets in the file.
628
629 =item $tdb->printfreelist
630
631 Dump the freelist to STDOUT.
632
633 =item $num_entries = $tdb->validate_freelist [CROAK]
634
635 Verifies consistency of freelist and return the number of entries. This
636 loads the whole freelist into memory, using an in-memory tdb database,
637 which did strike Jeremy as extremely clever.
638
639 =item $func_id = TDB_FileX::register_hash_function $callback->($key)
640
641 Registers a custom hash function. The callback should take a key and
642 return a 32 bit integer hash of it.
643
644 Returns a value that is suitable to be used as the name of a hash function
645 (C<hash> argument to C<open>).
646
647 A maximum of four custom hash functions can be registered.
648
649 =item TDB_FileX::unregister_hash_function $func_id
650
651 Frees the hash function registered by a previous call to
652 C<register_hash_function>. Note that you I<MUST NOT> unregister a function
653 that is still in use.
654
655 =item $uint = TDB_FileX::fnv1ax $string
656
657 Returns the result of the FNV-1A hash with postmixing - the internal fnv1ax hash.
658
659 =item $uint = TDB_FileX::xxh3 $string
660
661 Returns the result of xxh3_64bits, converted to an unsigned int - the
662 internal xxh3 hash.
663
664 =back
665
666 =head2 EXPORTS
667
668 The tag C<:all> exports all of the constants. Nothing is exported by
669 default.
670
671 Individually or with the tag C<:flags>:
672
673 DEFAULT
674 CLEAR_IF_FIRST
675 INTERNAL
676 NOLOCK
677 NOMMAP
678 CONVERT
679 BIGENDIAN
680 NOSYNC
681 SEQNUM
682 VOLATILE
683 ALLOW_NESTING
684 DISALLOW_NESTING
685 INCOMPATIBLE_HASH
686 MUTEX_LOCKING
687
688 Individually or with the tag C<:insert>:
689
690 REPLACE
691 INSERT
692 MODIFY
693
694 Individually or with the tag C<:error>:
695
696 SUCCESS
697 ERR_CORRUPT
698 ERR_IO
699 ERR_LOCK
700 ERR_OOM
701 ERR_EXISTS
702 ERR_NOLOCK
703 ERR_LOCK_TIMEOUT
704 ERR_NOEXIST
705 ERR_EINVAL
706 ERR_RDONLY
707
708 Individually or with the tag C<:debug>:
709
710 DEBUG_FATAL
711 DEBUG_ERROR
712 DEBUG_WARNING
713 DEBUG_TRACE
714
715 =head1 UNICODE HANDLING
716
717 TDB databases can only store octet strings. Unlike most other database
718 interfaces, TDB_FileX will safely handle Perl strings by downgrading
719 them. Perl will warn about strings that cannot be downgraded.
720
721 =head1 DISK USAGE
722
723 TDB databses use 24 octets for every key-value pair, plus the octet size
724 of the key and sata, e.g. the pair "key" => "value" takes up 24+3+5 octets
725 on disk.
726
727 The database header is 168 octets (if I haven't miscounted).
728
729 Each hashtable entry is 4 octets, and one more than the hash size is
730 allocated, so the hash table size is (hash_size + 1) * 4.
731
732 =head1 LIMITATIONS
733
734 =head2 Database Size
735
736 As far as I can see, TDB databases are limited to 4 GB.
737
738 =head2 Hash Functions
739
740 Hash functioons need to be set globally - they are limited to a maximum of
741 4. This can be easily extended, but requires source code editing. This
742 is due to a limitation of the TDB C API.
743
744 =head2 No recovery after failed C<reopen_all>
745
746 There is no way to survive an error during C<reopen_all>.
747 Unfortunately this is a limitation in the TDB C API.
748
749 =head2 No way to resize hash table
750
751 Performance degrades majorly when the databse grows larger then
752 accomodated by the hash table size, and the hash table size needs to be
753 set at database creation time and cannot be resized later.
754
755 =head1 SEE ALSO
756
757 tdb(3), L<perltie>.
758
759 =head1 AUTHOR
760
761 Angus Lees, E<lt>gus@inodes.org>
762
763 Currently maintained by Marc A. Lehmann <schmorp@schmorp.de>
764 http://home.schmorp.de/
765
766 =cut