1 /*
2  pblisam.c - isam file library implementation
3 
4  Copyright (C) 2002 - 2007   Peter Graf
5 
6    This file is part of PBL - The Program Base Library.
7    PBL is free software.
8 
9     This library is free software; you can redistribute it and/or
10     modify it under the terms of the GNU Lesser General Public
11     License as published by the Free Software Foundation; either
12     version 2.1 of the License, or (at your option) any later version.
13 
14     This library is distributed in the hope that it will be useful,
15     but WITHOUT ANY WARRANTY; without even the implied warranty of
16     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17     Lesser General Public License for more details.
18 
19     You should have received a copy of the GNU Lesser General Public
20     License along with this library; if not, write to the Free Software
21     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22 
23    For more information on the Program Base Library or Peter Graf,
24    please see: http://www.mission-base.com/.
25 
26     $Log: pblisam.c,v $
27     Revision 1.16  2010/05/30 20:06:45  peter
28     Removed warnings found by 'Microsoft Visual C++ 2010'.
29 
30     Revision 1.15  2009/03/08 20:56:50  peter
31     port to gcc (Ubuntu 4.3.2-1ubuntu12) 4.3.2.
32     Exposing the hash set and tree set interfaces.
33 
34     Revision 1.14  2009/02/03 16:40:14  peter
35     PBL vesion 1.04, optimizations,
36     MAC OS X port, port to Microsoft Visual C++ 2008 Express Edition,
37     exposing the array list and the linked list interface
38 
39 
40     Revision 1.2  2003/02/19 22:19:39  peter
41     fixed a bug related to finding non existing
42     duplicated keys
43 
44     bug was reported by Csaba P�los
45 
46 */
47 
48 /*
49  * make sure "strings <exe> | grep Id | sort -u" shows the source file versions
50  */
51 char * pblisam_c_id = "$Id: pblisam.c,v 1.16 2010/05/30 20:06:45 peter Exp $";
52 
53 #include <stdio.h>
54 #include <stdlib.h>
55 #include <string.h>
56 
57 #include "pbl.h"                  /* program base library                     */
58 
59 /******************************************************************************/
60 /* #defines                                                                   */
61 /******************************************************************************/
62 
63 /******************************************************************************/
64 /* typedefs                                                                   */
65 /******************************************************************************/
66 
67 /*
68  * PBL ISAM FILE DESCRIPTOR
69  */
70 typedef struct PBLISAMFILE_s
71 {
72     char          * magic;        /* magic string pointing to file descriptor */
73 
74     pblKeyFile_t  * mainfile;     /* file desriptor of main isam file         */
75     int             update;       /* flag: file open for update               */
76 
77     int             transactions; /* number of transactions active for file   */
78     int             rollback;     /* next commit should lead to a rollback    */
79 
80     int             nkeys;        /* number of key files of file              */
81     pblKeyFile_t ** keyfiles;     /* file descriptors of key files            */
82 
83     int           * keydup;       /* flag array does the key allow duplicates */
84     void         ** keycompare;   /* compare functions for keyfile            */
85 
86 } PBLISAMFILE_t;
87 
88 /******************************************************************************/
89 /* globals                                                                    */
90 /******************************************************************************/
91 static int (*pblkeycompare)( void * left, size_t llen,
92                              void * right, size_t rlen );
93 
94 /******************************************************************************/
95 /* functions                                                                  */
96 /******************************************************************************/
97 
98 /*
99  * conversion between keys of the main file and reference keys
100  *
101  * main file keys are 8 byte unsigned string numbers
102  * between "00000000"          and "ffffffff"
103  * or 17 byte unsigned string numbers
104  * between "g0000000100000000" and "gffffffffffffffff"
105  * this implements a 64 bit key.
106  *
107  * reference keys are compressed binary representations
108  * of the same values.
109  *
110  * function pblRKey2MainKey converts from the binary reference key
111  * to the unsigned string main key representing the same 64 bit value
112  *
113  * function pblMainKey2RKey converts from the unsigned string
114  * representation to the compressed representation
115  *
116  * reference keys are used as data for index records of index
117  * keyfiles for non duplicate keys
118  * and as key postfixes for index records of index
119  * keyfiles for keys allowing duplicates
120  *
121  * they provide the reference from the index records back
122  * to the main file records containing the data
123  */
pblRKey2MainKey(unsigned char * rkey,int rkeylen,unsigned char * okey)124 static int pblRKey2MainKey(
125 unsigned char * rkey,
126 int             rkeylen,
127 unsigned char * okey
128 )
129 {
130     unsigned long   keyhigh = 0;
131     unsigned long   keylow = 0;
132     int             hkeylen = 0;
133     int             lkeylen = 0;
134     int             len;
135 
136     /*
137      * at least two bytes are needed, one for the length and one for
138      * the value of lowkey
139      */
140     if( rkeylen < 2 )
141     {
142         pbl_errno = PBL_ERROR_BAD_FILE;
143         return( -1 );
144     }
145 
146     /*
147      * read the length of the compressed data from the end of the rkey
148      */
149     lkeylen = 0xff & rkey[ rkeylen - 1 ];
150 
151     /*
152      * the upper halfbyte of the length stores the length of the highkey
153      */
154     hkeylen = lkeylen >> 4;
155 
156     /*
157      * the lower halfbyte of the length stores the length of the lowkey
158      */
159     lkeylen &= 0x0f;
160 
161     /*
162      * the length of a the low key variable string must be between 1 and 5
163      */
164     if( lkeylen < 1 || lkeylen > 5 )
165     {
166         pbl_errno = PBL_ERROR_BAD_FILE;
167         return( -1 );
168     }
169 
170     /*
171      * additional to the length byte, lkeylen bytes are needed
172      */
173     if( rkeylen < 1 + lkeylen )
174     {
175         pbl_errno = PBL_ERROR_BAD_FILE;
176         return( -1 );
177     }
178 
179     /*
180      * read the value of the low key from the end of the key
181      */
182     len = pbl_VarBufToLong( rkey + rkeylen - ( lkeylen + 1 ), &keylow );
183     if( len != lkeylen )
184     {
185         pbl_errno = PBL_ERROR_BAD_FILE;
186         return( -1 );
187     }
188 
189     /*
190      * if there is no high key, just return the low key as a string
191      */
192     if( hkeylen < 1 )
193     {
194         if( okey )
195         {
196             pbl_LongToHexString( okey, keylow );
197         }
198 
199         /*
200          * return the number of bytes of the rkey parsed
201          */
202         return( lkeylen + 1 );
203     }
204 
205     /*
206      * the length of a the high key variable string cannot be greater than 5
207      */
208     if( hkeylen > 5 )
209     {
210         pbl_errno = PBL_ERROR_BAD_FILE;
211         return( -1 );
212     }
213 
214     /*
215      * additional to the length byte, lkeylen + hkeylen bytes are needed
216      */
217     if( rkeylen < 1 + lkeylen + hkeylen )
218     {
219         pbl_errno = PBL_ERROR_BAD_FILE;
220         return( -1 );
221     }
222 
223     /*
224      * read the value of the high key from the end of the key
225      */
226     len = pbl_VarBufToLong( rkey + rkeylen - (hkeylen + lkeylen + 1), &keyhigh);
227     if( len != lkeylen )
228     {
229         pbl_errno = PBL_ERROR_BAD_FILE;
230         return( -1 );
231     }
232 
233     /*
234      * keyhigh must have a positive value, otherwise it would not have been
235      * stored at all
236      */
237     if( !keyhigh )
238     {
239         pbl_errno = PBL_ERROR_BAD_FILE;
240         return( -1 );
241     }
242 
243     /*
244      * return highkey and lowkey as one string
245      */
246     if( okey )
247     {
248         *okey = 'g';
249         pbl_LongToHexString( okey + 1, keyhigh );
250         pbl_LongToHexString( okey + 9, keylow );
251     }
252 
253     /*
254      * return the number of bytes of the rkey parsed
255      */
256     return( hkeylen + lkeylen + 1 );
257 }
258 
pblLongs2RKey(unsigned long keylow,unsigned long keyhigh,unsigned char * rkey)259 static int pblLongs2RKey(
260 unsigned long   keylow,
261 unsigned long   keyhigh,
262 unsigned char * rkey
263 )
264 {
265     int             hkeylen = 0;
266     int             lkeylen = 0;
267     int             len;
268 
269     if( keyhigh )
270     {
271         /*
272          * only store the higher four byte value if it is not 0
273          */
274         hkeylen = pbl_LongToVarBuf( rkey, keyhigh );
275     }
276 
277     /*
278      * store the low 4 bytes
279      */
280     lkeylen = pbl_LongToVarBuf( rkey + hkeylen, keylow );
281 
282     /*
283      * store the length of both 4 bytes values in one byte at the end
284      * the upper halfbyte of the length stores the length of the highkey
285      * the lower halfbyte of the length stores the length of the lowkey
286      */
287     len = ( hkeylen << 4 ) | lkeylen;
288     rkey[ hkeylen + lkeylen ] = 0xff & len;
289 
290     /*
291      * return the bytes used for the rkey
292      */
293     return( hkeylen + lkeylen + 1 );
294 }
295 
296 
pblMainKey2RKey(unsigned char * okey,int okeylen,unsigned char * rkey)297 static int pblMainKey2RKey(
298 unsigned char * okey,
299 int             okeylen,
300 unsigned char * rkey
301 )
302 {
303     unsigned long   keylow;
304     unsigned long   keyhigh;
305     int             len;
306 
307     if( okeylen > PBLKEYLENGTH - 1 )
308     {
309         pbl_errno = PBL_ERROR_PARAM_KEYLEN;
310         return( -1 );
311     }
312 
313     /*
314      * copy the input key, because the parsing destroys it
315      */
316     memcpy( rkey, okey, okeylen );
317     if( *rkey == 'g' )
318     {
319         rkey[ 17 ] = 0;
320         keylow = strtoul( (char*)rkey + 9, 0, 16 );
321         rkey[ 9 ] = 0;
322         keyhigh = strtoul( (char*)rkey + 1, 0, 16 );
323     }
324     else
325     {
326         rkey[ 8 ] = 0;
327         keylow = strtoul( (char*)rkey, 0, 16 );
328         keyhigh = 0;
329     }
330 
331     /*
332      * store both long values as variable length byte buffers
333      */
334     len = pblLongs2RKey( keylow, keyhigh, rkey );
335 
336     return( len );
337 }
338 
339 /*
340  * return the length a duplicate index key without the
341  * reference postfix
342  */
pblIsamDupKeyLen(unsigned char * fkey,int fkeylen)343 static int pblIsamDupKeyLen( unsigned char * fkey, int fkeylen )
344 {
345     int  len;
346 
347     /*
348      * parse the reference from the end of the key
349      * this calculates the length of the reference needed below
350      */
351     len = pblRKey2MainKey( fkey, fkeylen, NULL );
352     if( len < 0 )
353     {
354         pbl_errno = PBL_ERROR_BAD_FILE;
355         return( -1 );
356     }
357 
358     return( fkeylen - len );
359 }
360 
361 /*
362  * compare two duplicate keys
363  */
pblIsamDupKeyCompare(void * left,size_t llen,void * right,size_t rlen)364 static int pblIsamDupKeyCompare(
365 void * left,    /** first buffer for compare               */
366 size_t llen,    /** length of that buffer                  */
367 void * right,   /** second buffer for compare              */
368 size_t rlen     /** length of that buffer                  */
369 )
370 {
371     int  rc;
372 
373     unsigned char lkey[ PBLKEYLENGTH ];
374     unsigned char rkey[ PBLKEYLENGTH ];
375 
376     int  leftlen;
377     int  rightlen;
378 
379     /*
380      * a buffer with a length 0 is logically smaller than any other buffer
381      */
382     if( !llen )
383     {
384         if( !rlen )
385         {
386             return( 0 );
387         }
388         return( -1 );
389     }
390     if( !rlen )
391     {
392         return( 1 );
393     }
394 
395     leftlen  = pblRKey2MainKey( left, llen, lkey );
396     rightlen = pblRKey2MainKey( right, rlen, rkey );
397 
398     if( leftlen < 1 || rightlen < 1 )
399     {
400         pbl_errno = PBL_ERROR_BAD_FILE;
401         return( -1 );
402     }
403 
404     if( leftlen >= (int)llen || rightlen >= (int)rlen )
405     {
406         pbl_errno = PBL_ERROR_BAD_FILE;
407         return( -1 );
408     }
409 
410     if( pblkeycompare )
411     {
412         /*
413          * use the default key compare function
414          */
415         rc = (*pblkeycompare)( left, llen - leftlen,
416                                right, rlen - rightlen );
417     }
418     else
419     {
420         /*
421          * use the default key compare function
422          */
423         rc = pbl_memcmp( left, llen - leftlen,
424                          right, rlen - rightlen );
425     }
426 
427     if( !rc )
428     {
429         rc = strcmp( (char*)lkey, (char*)rkey );
430     }
431 
432     return( rc );
433 }
434 
435 /*
436 ------------------------------------------------------------------------------
437   FUNCTION:     pblIsamStartTransOnFile
438 
439   DESCRIPTION:  start a transaction on a single ISAM file
440 
441   RESTRICTIONS: transactions can be nested
442 
443   RETURNS:      int rc == 0: the transaction was started successfully
444                 int rc >  0: the transaction was started
445                              but another transaction has resulted in
446                              a rollback request on the file already
447 ------------------------------------------------------------------------------
448 */
pblIsamStartTransOnFile(PBLISAMFILE_t * isam)449 static int pblIsamStartTransOnFile( PBLISAMFILE_t * isam )
450 {
451     int n;
452 
453     /*
454      * if there is no transaction active for the file
455      */
456     if( isam->transactions < 1 )
457     {
458         isam->transactions = 1;
459         isam->rollback = 0;
460     }
461     else
462     {
463         isam->transactions++;
464     }
465 
466     /*
467      * start a transaction on the main file
468      */
469     if( pblKfStartTransaction( isam->mainfile ) > 0 )
470     {
471         isam->rollback = 1;
472     }
473 
474     /*
475      * start transactions for all keyfiles
476      */
477     for( n = 0; n < isam->nkeys; n++ )
478     {
479         if( pblKfStartTransaction( isam->keyfiles[ n ] ) > 0 )
480         {
481             isam->rollback = 1;
482         }
483     }
484 
485     return( isam->rollback );
486 }
487 
488 
489 /**
490  * start a transaction on a set of ISAM files
491  *
492  * transactions can be nested
493  *
494  * @return int rc == 0: the transaction was started successfully
495  * @return int rc >  0: the transaction was started
496  *                      but another transaction has resulted in
497  *                      a rollback request on the file already
498  */
499 
pblIsamStartTransaction(int nfiles,pblIsamFile_t ** isamfiles)500 int pblIsamStartTransaction(
501 int nfiles,                  /** number of files in ISAM file list      */
502 pblIsamFile_t ** isamfiles   /** ISAM file list to start transaction on */
503 )
504 {
505     PBLISAMFILE_t ** files = ( PBLISAMFILE_t ** ) isamfiles;
506     int n;
507     int rollback = 0;
508 
509     for( n = 0; n < nfiles; n++ )
510     {
511         if( pblIsamStartTransOnFile( files[ n ] ) > 0 )
512         {
513             rollback = 1;
514         }
515     }
516 
517     return( rollback );
518 }
519 
520 
521 
522 /*
523 ------------------------------------------------------------------------------
524   FUNCTION:     pblIsamCommitFile
525 
526   DESCRIPTION:  commit or rollback changes done during a transaction
527                 on a single ISAM file
528 
529   RESTRICTIONS: transactions can be nested, if so the commit
530                 only happens when the outermost transaction
531                 calls a commit.
532 
533                 the commit only happens to process space buffer cache,
534                 call pblIsamFlush() after pblIsamCommitFile() if you want to
535                 flush to kernel space buffer cache.
536 
537   RETURNS:      int rc == 0: the commit went ok
538                 int rc >  0: a rollback happened,
539                              either because the caller requested it
540                              or because an inner transaction
541                              resulted in a rollback
542                 int rc <  0: an error see pbl_errno
543 ------------------------------------------------------------------------------
544 */
pblIsamCommitFile(PBLISAMFILE_t * isam,int rollback)545 static int pblIsamCommitFile( PBLISAMFILE_t * isam, int rollback )
546 {
547     int n;
548 
549     /*
550      * remember if this is a rollback
551      */
552     if( rollback )
553     {
554         isam->rollback = 1;
555     }
556     else
557     {
558         /*
559          * find out if any of the keyfiles needs a rollback
560          * do this by starting another transaction on the ISAM file
561          * if any of the index files have a rollback isam->rollback is set!
562          */
563         pblIsamStartTransOnFile( isam );
564 
565         /*
566          * commit the main file transaction started above
567          */
568         pblKfCommit( isam->mainfile, isam->rollback );
569     }
570 
571     /*
572      * commit the outer transaction on the main file
573      */
574     pblKfCommit( isam->mainfile, isam->rollback );
575 
576     /*
577      * do the rollback or commit on all key files
578      */
579     for( n = 0; n < isam->nkeys; n++ )
580     {
581         if( !rollback )
582         {
583             /*
584              * commit the transaction started above
585              */
586             pblKfCommit( isam->keyfiles[ n ], isam->rollback );
587         }
588 
589         /*
590          * commit the outer transaction
591          */
592         pblKfCommit( isam->keyfiles[ n ], isam->rollback );
593     }
594 
595     if( !rollback )
596     {
597         /*
598          * count the transaction started above
599          */
600         isam->transactions -= 1;
601     }
602 
603     /*
604      * we have one transaction less on the ISAM file
605      */
606     isam->transactions -= 1;
607 
608     return( isam->rollback );
609 }
610 
611 /**
612  * commit or rollback changes done during a transaction
613  *
614  * transactions can be nested, if so the commit
615  * only happens when the outermost transaction
616  * calls a commit.
617  *
618  * the commit only happens to process space buffer cache,
619  * call \Ref{pblIsamFlush}() after \Ref{pblIsamCommit}() if you want to
620  * flush to kernel space buffer cache.
621  *
622  * @return int rc == 0: the commit went ok
623  * @return int rc >  0: a rollback happened, either because the caller
624  *                      requested it or because an inner transaction resulted
625  *                      in a rollback
626  * @return int rc <  0: some error, see pbl_errno
627  */
628 
pblIsamCommit(int nfiles,pblIsamFile_t ** isamfiles,int rollback)629 int pblIsamCommit(
630 int nfiles,                  /** number of files in ISAM file list      */
631 pblIsamFile_t ** isamfiles,  /** ISAM file list to commit changes of    */
632 int rollback       /** != 0: roll back the changes, == 0: commit the changes  */
633 )
634 {
635     PBLISAMFILE_t ** files = ( PBLISAMFILE_t ** ) isamfiles;
636 
637     int n;
638     int dorollback = rollback;
639 
640     if( !rollback )
641     {
642         /*
643          * find out if any of the files needs a rollback
644          * do this by starting another transaction on the file set
645          * if any of the ISAM files have a rollback dorollback is set!
646          */
647         if( pblIsamStartTransaction( nfiles, isamfiles ) > 0 )
648         {
649             dorollback = 1;
650         }
651     }
652 
653     /*
654      * commit or rollback all files in the set
655      */
656     for( n = 0; n < nfiles; n++ )
657     {
658         if( !rollback )
659         {
660             /*
661              * commit the transaction done above
662              */
663             pblIsamCommitFile( files[ n ], dorollback );
664         }
665 
666         /*
667          * commit the outer transaction
668          */
669         pblIsamCommitFile( files[ n ], dorollback );
670     }
671 
672     return( dorollback );
673 }
674 
675 /**
676  * open an ISAM file, creates the file if necessary
677  *
678  * if update is 0, the ISAM file is opened for read access only,
679  * if update is not 0 the ISAM file is opened for reading and writing
680  *
681  * a file set tag can be attached to the ISAM file,
682  * if a file having a non NULL file set tag is flushed
683  * to disk all files having the same file set tag attached
684  * are flushed as well.
685  *
686  * @return  pblIsamFile_t * retptr == NULL: an error occured, see pbl_errno
687  * @return  pblIsamFile_t * retptr != NULL: a pointer to an ISAM file descriptor
688  */
689 
pblIsamOpen(char * path,int update,void * filesettag,int nkeys,char ** keyfilenames,int * keydup)690 pblIsamFile_t * pblIsamOpen(
691 char * path,         /** path of file to create                               */
692 int    update,       /** flag: should file be opened for update?              */
693 void * filesettag,   /** filesettag, for flushing multiple files consistently */
694 int    nkeys,        /** number of key files to create                        */
695 char **keyfilenames, /** list of names of key index files to create           */
696 int  * keydup        /** flaglist: is the i'th index key a duplicate key?     */
697 )
698 {
699     char          * ptr;
700     char          * keyfile;
701 
702     PBLISAMFILE_t * isam;
703     int             i;
704 
705     /*
706      * create the descriptor
707      */
708     isam = (PBLISAMFILE_t *)pbl_malloc0( "pblIsamOpen ISAMFILE", sizeof( PBLISAMFILE_t ));
709     if( !isam )
710     {
711         return( 0 );
712     }
713 
714     /*
715      * if the user did not specify an external file set tag
716      * the isam file descriptor is used in order to make sure
717      * all key files of the isam file are flushed at the same time
718      */
719     if( update && !filesettag )
720     {
721         filesettag = isam;
722     }
723 
724     isam->nkeys = nkeys;
725     if( isam->nkeys )
726     {
727         /*
728          * create space for pointers to key file descriptors
729          */
730         isam->keyfiles = (pblKeyFile_t **)pbl_malloc0( "pblIsamOpen keyfiles",
731                                       nkeys * sizeof( pblKeyFile_t * ));
732         if( !isam->keyfiles )
733         {
734             PBL_FREE( isam );
735             return( 0 );
736         }
737 
738         /*
739          * save the duplicate key flags for all keys
740          */
741         isam->keydup = (int *)pbl_memdup( "pblIsamOpen keydup",
742                                    keydup, nkeys * sizeof( int * ));
743         if( !isam->keydup )
744         {
745             PBL_FREE( isam->keyfiles );
746             PBL_FREE( isam );
747             pbl_errno = PBL_ERROR_OUT_OF_MEMORY;
748             return( 0 );
749         }
750 
751         /*
752          * create the array of keycompare functions for all keys
753          */
754         isam->keycompare = (void **)pbl_malloc0( "pblIsamOpen keycompare",
755                                         nkeys * sizeof( void * ));
756         if( !isam->keycompare )
757         {
758             PBL_FREE( isam->keydup );
759             PBL_FREE( isam->keyfiles );
760             PBL_FREE( isam );
761             pbl_errno = PBL_ERROR_OUT_OF_MEMORY;
762             return( 0 );
763         }
764     }
765 
766     /*
767      * open the main file
768      */
769     if( update )
770     {
771         /*
772          * try to create
773          */
774         isam->mainfile = pblKfCreate( path, filesettag );
775     }
776 
777     if( !isam->mainfile )
778     {
779         /*
780          * try to open
781          */
782         isam->mainfile = pblKfOpen( path, update, filesettag );
783 
784         /*
785          * if the main file is not open
786          */
787         if( !isam->mainfile )
788         {
789             PBL_FREE( isam->keycompare );
790             PBL_FREE( isam->keydup );
791             PBL_FREE( isam->keyfiles );
792             PBL_FREE( isam );
793             return( 0 );
794         }
795     }
796 
797     /*
798      * if the name of the main file has a directory part
799      * and the names of the index files do not
800      * we prepend the directory part of the main file to
801      * the names of the index files
802      *
803      * get a pointer to last / or \ in path
804      */
805     ptr = strrchr( path, '/' );
806     keyfile = strrchr( path, '\\' );
807 
808     if( ptr )
809     {
810         if( keyfile > ptr )
811         {
812             ptr = keyfile;
813         }
814     }
815     else
816     {
817         ptr = keyfile;
818     }
819     if( ptr )
820     {
821         /*
822          * set pointer to the character after the slash
823          */
824         ptr++;
825     }
826 
827     /*
828      * open all key files
829      */
830     for( i = 0; i < nkeys; i++ )
831     {
832         /*
833          * if the path contains a directory part
834          * and the name of the keyfile does not
835          */
836         if( ptr
837          && !strchr( keyfilenames[ i ], '/' )
838          && !strchr( keyfilenames[ i ], '\\' ))
839         {
840             /*
841              * build the the path to the keyfile
842              */
843             keyfile = pbl_mem2dup( "pblIsamOpen keyfile",
844                                    path, ptr - path,
845                                    keyfilenames[ i ],
846                                    strlen( keyfilenames[ i ] ) + 1 );
847         }
848         else
849         {
850             /*
851              * use keyfile name as given
852              */
853             keyfile = strdup( keyfilenames[ i ] );
854         }
855 
856         if( !keyfile )
857         {
858             pblKfClose( isam->mainfile );
859             PBL_FREE( isam->keycompare );
860             PBL_FREE( isam->keydup );
861             PBL_FREE( isam->keyfiles );
862             PBL_FREE( isam );
863             pbl_errno = PBL_ERROR_OUT_OF_MEMORY;
864             return( 0 );
865         }
866 
867         if( update )
868         {
869             /*
870              * try create
871              */
872             isam->keyfiles[ i ] = pblKfCreate( keyfile, filesettag );
873         }
874 
875         if( !isam->keyfiles[ i ] )
876         {
877             /*
878              * try open
879              */
880             isam->keyfiles[ i ] = pblKfOpen( keyfile, update, filesettag );
881         }
882 
883         if( !isam->keyfiles[ i ] )
884         {
885             int j;
886 
887             for( j = 0; j < i; j++ )
888             {
889                 pblKfClose( isam->keyfiles[ j ] );
890             }
891 
892             pblKfClose( isam->mainfile );
893             PBL_FREE( keyfile );
894             PBL_FREE( isam->keycompare );
895             PBL_FREE( isam->keydup );
896             PBL_FREE( isam->keyfiles );
897             PBL_FREE( isam );
898             return( 0 );
899         }
900 
901         /*
902          * set our custom compare function for keyfiles allowing
903          * duplicate keys
904          */
905         if( isam->keydup[ i ] )
906         {
907             pblKfSetCompareFunction( isam->keyfiles[ i ], pblIsamDupKeyCompare);
908         }
909 
910         /*
911          * the key file is open, we don't need its name anymore
912          */
913         PBL_FREE( keyfile );
914     }
915 
916     isam->magic = pblisam_c_id;
917     return( ( pblIsamFile_t * )isam );
918 }
919 
920 /**
921  * close an ISAM file
922  *
923  * all changes are flushed to disk before,
924  * all memory allocated for the file is released.
925  *
926  * @return int rc == 0: call went ok, file is closed
927  * @return int rc != 0: some error, see pbl_errno
928  */
929 
pblIsamClose(pblIsamFile_t * isamfile)930 int pblIsamClose(
931 pblIsamFile_t * isamfile           /** ISAM file to close */
932 )
933 {
934     PBLISAMFILE_t * isam = ( PBLISAMFILE_t * ) isamfile;
935     int             rc = 0;
936     int             i;
937     int             saveerrno = 0;
938 
939     /*
940      * close all the keyfiles
941      */
942     for( i = 0; i < isam->nkeys; i++ )
943     {
944         if( pblKfClose( isam->keyfiles[ i ] ))
945         {
946             saveerrno = pbl_errno;
947             rc = -1;
948         }
949     }
950 
951     /*
952      * close the main file
953      */
954     if( pblKfClose( isam->mainfile ))
955     {
956         saveerrno = pbl_errno;
957         rc = -1;
958     }
959 
960     PBL_FREE( isam->keycompare );
961     PBL_FREE( isam->keydup );
962     PBL_FREE( isam->keyfiles );
963     PBL_FREE( isam );
964 
965     if( rc )
966     {
967         pbl_errno = saveerrno;
968     }
969 
970     return( rc );
971 }
972 
973 /**
974  * flush an ISAM file
975  *
976  * all changes are flushed to disk,
977  *
978  * @return int rc == 0: call went ok
979  * @return int rc != 0: some error, see pbl_errno
980  */
981 
pblIsamFlush(pblIsamFile_t * isamfile)982 int pblIsamFlush(
983 pblIsamFile_t * isamfile           /** ISAM file to flush */
984 )
985 {
986     PBLISAMFILE_t * isam = ( PBLISAMFILE_t * ) isamfile;
987     int             rc = 0;
988     int             i;
989     int             saveerrno = 0;
990 
991     /*
992      * flush all the keyfiles
993      */
994     for( i = 0; i < isam->nkeys; i++ )
995     {
996         if( pblKfFlush( isam->keyfiles[ i ] ))
997         {
998             saveerrno = pbl_errno;
999             rc = -1;
1000         }
1001     }
1002 
1003     /*
1004      * flush the main file
1005      */
1006     if( pblKfFlush( isam->mainfile ))
1007     {
1008         saveerrno = pbl_errno;
1009         rc = -1;
1010     }
1011 
1012     if( rc )
1013     {
1014         pbl_errno = saveerrno;
1015     }
1016 
1017     return( rc );
1018 }
1019 
1020 /*
1021  * set the current record of the main file
1022  * used after deletes of records in the main file
1023  */
pblIsamSetCurrentRecord(PBLISAMFILE_t * isam)1024 static int pblIsamSetCurrentRecord( PBLISAMFILE_t * isam )
1025 {
1026     long          datalen;
1027     unsigned char okey[ PBLKEYLENGTH ];
1028     size_t        okeylen;
1029     int           saveerrno = pbl_errno;
1030 
1031     /*
1032      * read the key of the current record in the main file
1033      */
1034     datalen = pblKfThis( isam->mainfile, okey, &okeylen );
1035     if( datalen >= 0 )
1036     {
1037         if( okeylen == 9 )
1038         {
1039             okeylen = 8;
1040         }
1041         else if( okeylen == 18 )
1042         {
1043             okeylen = 17;
1044         }
1045         okey[ okeylen ] = 0;
1046 
1047         /*
1048          * position the current record of the main file to the allkeys record
1049          */
1050         datalen = pblKfFind( isam->mainfile, PBLFI, okey, okeylen, 0, 0 );
1051         if( datalen < 0 )
1052         {
1053             pbl_errno = PBL_ERROR_BAD_FILE;
1054             return( -1 );
1055         }
1056     }
1057 
1058     pbl_errno = saveerrno;
1059     return( 0 );
1060 }
1061 
1062 /**
1063  * insert a new record with the given keys and data into the isam file,
1064  *
1065  * the current record of the file will be set to the new record
1066  *
1067  * <P>
1068  * <B>RESTRICTIONS</B>:
1069  * <BR> - the file must be open for update,
1070  * <BR> - allkeys must point to the keys to be inserted,
1071  * <BR> - allkeyslen must be bigger than 0 and smaller than 1024,
1072  * <BR> - data must point to the data be inserted,
1073  * <BR> - datalen must not be negative,
1074  * <BR> - if datalen == 0, the pointer data is not evaluated at all
1075  *
1076  * Parameter <I>allkeys</I> must contain all values for all keys
1077  * of the record. The values have to be prepended by one byte giving
1078  * the length of the following value. All values have to be concatenated
1079  * into one string.
1080  *
1081  * Example:
1082  * <PRE> 4isam4file3key </PRE>
1083  * with the numbers as binary values and the letters ascii,
1084  * specifies three keys with the values "isam", "file" and "key".
1085  *
1086  * @return int rc == 0: call went ok
1087  * @return int rc != 0: some error occured, see pbl_errno
1088  */
1089 
pblIsamInsert(pblIsamFile_t * isamfile,void * allkeys,size_t allkeyslen,void * data,size_t datalen)1090 int pblIsamInsert(
1091 pblIsamFile_t * isamfile,   /** ISAM file to insert to                    */
1092 void          * allkeys,    /** pointers to all keys to insert            */
1093 size_t          allkeyslen, /** total length of all keys to insert        */
1094 void          * data,       /** data to insert                            */
1095 size_t          datalen     /** length of the data                        */
1096 )
1097 {
1098     PBLISAMFILE_t * isam = ( PBLISAMFILE_t * ) isamfile;
1099     unsigned long   lastkeyhigh = 0;
1100     unsigned long   lastkeylow = 0;
1101     unsigned char   rkey[ PBLKEYLENGTH ];
1102     int             rkeylen;
1103     unsigned char   okey[ PBLKEYLENGTH ];
1104     size_t          okeylen;
1105     int             ndatarecords = 0;
1106     int             n = 0;
1107     int             rc;
1108     unsigned char * ldata;
1109     long            ldatalen;
1110     unsigned char * key;
1111     int             keylen;
1112 
1113     /*
1114      * start a transaction
1115      */
1116     pblIsamStartTransaction( 1, &isamfile );
1117 
1118     /*
1119      * the sum of the length of all keys
1120      * cannot be longer than a single data record
1121      */
1122     if( allkeyslen > PBLDATALENGTH )
1123     {
1124         /*
1125          * rollback all changes
1126          */
1127         pblIsamCommit( 1, &isamfile, 1 );
1128         pbl_errno = PBL_ERROR_PARAM_KEYLEN;
1129         return( -1 );
1130     }
1131 
1132     /*
1133      * find out the last key used in the main file
1134      */
1135     if( pblKfLast( isam->mainfile, okey, &okeylen ) < 0 )
1136     {
1137         if( pbl_errno != PBL_ERROR_NOT_FOUND )
1138         {
1139             /*
1140              * rollback all changes
1141              */
1142             pblIsamCommit( 1, &isamfile, 1 );
1143             return( -1 );
1144         }
1145 
1146         /*
1147          * 00000000 is the smallest key we use in the main file
1148          */
1149         strcpy( (char*)okey, "00000000" );
1150         okeylen = 8;
1151     }
1152 
1153     /*
1154      * generate the next key, a 64 bit logic is used in order to make
1155      * sure we never run out of new keys
1156      */
1157     if( *okey == 'g' )
1158     {
1159         /*
1160          * values over unsigned 0xffffffff are represented
1161          * as 17 byte strings with a preceding "g"
1162          */
1163         okey[ 17 ] = 0;
1164         lastkeylow = strtoul( (char*)okey + 9, 0, 16 );
1165         okey[ 9 ] = 0;
1166         lastkeyhigh = strtoul( (char*)okey + 1, 0, 16 );
1167     }
1168     else
1169     {
1170         /*
1171          * values below unsigned 0xffffffff are represented
1172          * as 8 byte strings with no prefix
1173          */
1174         okey[ 8 ] = 0;
1175         lastkeylow = strtoul( (char*)okey, 0, 16 );
1176     }
1177 
1178     if( lastkeylow == 0xffffffff )
1179     {
1180         /*
1181          * 32 bit overflow
1182          */
1183         lastkeylow = 0;
1184         lastkeyhigh += 1;
1185     }
1186     else
1187     {
1188         lastkeylow += 1;
1189     }
1190 
1191     if( lastkeyhigh )
1192     {
1193         snprintf( (char*)okey, PBLKEYLENGTH, "g%08lx%08lx", lastkeyhigh, lastkeylow );
1194     }
1195     else
1196     {
1197         snprintf( (char*)okey, PBLKEYLENGTH, "%08lx", lastkeylow );
1198     }
1199     okeylen = strlen( (char*)okey );
1200 
1201     /*
1202      * create the reference key used as link to the main file
1203      */
1204     rkeylen = pblMainKey2RKey( okey, okeylen, rkey );
1205     if( rkeylen < 1 )
1206     {
1207         /*
1208          * rollback all changes
1209          */
1210         pblIsamCommit( 1, &isamfile, 1 );
1211         return( -1 );
1212     }
1213 
1214     /*
1215      * insert all the keys into the key files
1216      */
1217     for( key = allkeys, n = 0; n < isam->nkeys; n++ )
1218     {
1219         keylen = 0xff & *key++;
1220         if( keylen < 1 )
1221         {
1222             /*
1223              * non duplicate keys cannot be empty
1224              */
1225             if( !isam->keydup[ n ] )
1226             {
1227                 /*
1228                  * rollback all changes
1229                  */
1230                 pblIsamCommit( 1, &isamfile, 1 );
1231 
1232                 pbl_errno = PBL_ERROR_PARAM_KEY;
1233                 return( -1 );
1234             }
1235         }
1236 
1237         /*
1238          * check the sanity of the allkeys record given
1239          */
1240         if( key + keylen > ((unsigned char*)allkeys) + allkeyslen )
1241         {
1242             /*
1243              * rollback all changes
1244              */
1245             pblIsamCommit( 1, &isamfile, 1 );
1246 
1247             pbl_errno = PBL_ERROR_PARAM_KEY;
1248             return( -1 );
1249         }
1250 
1251         /*
1252          * if the key is allowing duplicates
1253          */
1254         if( isam->keydup[ n ] )
1255         {
1256             unsigned char ikey[ PBLKEYLENGTH ];
1257 
1258             /*
1259              * create a unique key for the insert
1260              */
1261             if( keylen + rkeylen > PBLKEYLENGTH )
1262             {
1263                 /*
1264                  * rollback all changes
1265                  */
1266                 pblIsamCommit( 1, &isamfile, 1 );
1267 
1268                 pbl_errno = PBL_ERROR_PARAM_KEYLEN;
1269                 return( -1 );
1270             }
1271 
1272             /*
1273              * concatenate the key and the reference key
1274              * the reference key is used as postfix of the index key
1275              */
1276             if( keylen )
1277             {
1278                 memcpy( ikey, key, keylen );
1279             }
1280             memcpy( ikey + keylen, rkey, rkeylen );
1281 
1282             /*
1283              * make sure any user defined key compare function gets used
1284              */
1285             pblkeycompare = isam->keycompare[ n ];
1286 
1287             /*
1288              * search for the key in the file
1289              */
1290             if( pblKfFind( isam->keyfiles[ n ],
1291                            PBLEQ, ikey, keylen + rkeylen, 0, 0 ) >= 0 )
1292             {
1293                 /*
1294                  * rollback all changes
1295                  */
1296                 pblIsamCommit( 1, &isamfile, 1 );
1297 
1298                 pbl_errno = PBL_ERROR_EXISTS;
1299                 return( -1 );
1300             }
1301 
1302             /*
1303              * insert the key to the file
1304              */
1305             if( pblKfInsert( isam->keyfiles[ n ],
1306                              ikey, keylen + rkeylen, 0, 0 ))
1307             {
1308                 int saveerrno = pbl_errno;
1309 
1310                 /*
1311                  * rollback all changes
1312                  */
1313                 pblIsamCommit( 1, &isamfile, 1 );
1314 
1315                 pbl_errno = saveerrno;
1316                 return( -1 );
1317             }
1318         }
1319         else
1320         {
1321             /*
1322              * search for the key in the file
1323              */
1324             if( pblKfFind( isam->keyfiles[ n ],
1325                            PBLEQ, key, keylen, 0, 0 ) >= 0 )
1326             {
1327                 /*
1328                  * rollback all changes
1329                  */
1330                 pblIsamCommit( 1, &isamfile, 1 );
1331 
1332                 pbl_errno = PBL_ERROR_EXISTS;
1333                 return( -1 );
1334             }
1335 
1336             /*
1337              * insert the key to the file
1338              * the reference key is the data of the record in the index
1339              */
1340             if( pblKfInsert( isam->keyfiles[ n ],
1341                              key, keylen, rkey, rkeylen ))
1342             {
1343                 int saveerrno = pbl_errno;
1344 
1345                 /*
1346                  * rollback all changes
1347                  */
1348                 pblIsamCommit( 1, &isamfile, 1 );
1349 
1350                 pbl_errno = saveerrno;
1351                 return( -1 );
1352             }
1353         }
1354 
1355         /*
1356          * move the key along the record
1357          */
1358         key += keylen;
1359     }
1360 
1361     /*
1362      * insert the data records into the main file
1363      */
1364     for( ldata = data, ndatarecords = 0; ; ndatarecords++ )
1365     {
1366         ldatalen = datalen - ndatarecords * PBLDATALENGTH;
1367         if( ldatalen < 0 )
1368         {
1369             break;
1370         }
1371 
1372         if( ldatalen > PBLDATALENGTH )
1373         {
1374             ldatalen = PBLDATALENGTH;
1375         }
1376 
1377         /*
1378          * the data records in the main file use a 9/18 byte
1379          * version of the main key string, including the trailing \0
1380          * we use the trailing \0 of the main key in order to differentiate
1381          * between the allkeys record and the data records
1382          */
1383         if( pblKfInsert( isam->mainfile, okey, okeylen + 1, ldata, ldatalen ))
1384         {
1385             int saveerrno = pbl_errno;
1386 
1387             /*
1388              * rollback all changes
1389              */
1390             pblIsamCommit( 1, &isamfile, 1 );
1391 
1392             pbl_errno = saveerrno;
1393             return( -1 );
1394         }
1395 
1396         if( ldatalen < PBLDATALENGTH )
1397         {
1398             break;
1399         }
1400 
1401         ldata += ldatalen;
1402     }
1403 
1404     /*
1405      * insert the record for the keys into the main file
1406      * the "allkeys" record in the main file uses the 8/17 byte main key
1407      */
1408     rc = pblKfInsert( isam->mainfile, okey, okeylen, allkeys, allkeyslen );
1409     if( rc )
1410     {
1411         int saveerrno = pbl_errno;
1412 
1413         /*
1414          * rollback all changes
1415          */
1416         pblIsamCommit( 1, &isamfile, 1 );
1417 
1418         pbl_errno = saveerrno;
1419         return( -1 );
1420     }
1421 
1422     /*
1423      * commit all changes
1424      */
1425     if( pblIsamCommit( 1, &isamfile, 0 ))
1426     {
1427         return( -1 );
1428     }
1429 
1430     return( 0 );
1431 }
1432 
1433 /**
1434  * delete the current record of the ISAM file.
1435  *
1436  * the current record of the file is set to the next record or
1437  * if the last record is deleted, to the previous record,
1438  *
1439  * if there are no more records in the file after the delete
1440  * the current record is of course unpositioned
1441  *
1442  * <P>
1443  * <B>RESTRICTIONS</B>:
1444  * <BR> - the file must be open for update,
1445  *
1446  * @return int rc == 0: call went ok
1447  * @return int rc != 0: some error occured, see pbl_errno
1448  */
1449 
pblIsamDelete(pblIsamFile_t * isamfile)1450 int pblIsamDelete(
1451 pblIsamFile_t * isamfile    /** ISAM file to delete from                  */
1452 )
1453 {
1454     PBLISAMFILE_t * isam = ( PBLISAMFILE_t * ) isamfile;
1455     unsigned char   okey[ PBLKEYLENGTH ];
1456     size_t          okeylen;
1457     unsigned char   rkey[ PBLKEYLENGTH ];
1458     int             rkeylen = -1;
1459     unsigned char * key;
1460     int             keylen;
1461     unsigned char   data[ PBLDATALENGTH ];
1462     long            datalen;
1463     long            rc;
1464     int             n;
1465     int             retval = 0;
1466 
1467     /*
1468      * start a transaction
1469      */
1470     pblIsamStartTransaction( 1, &isamfile );
1471 
1472     /*
1473      * read the key of the current record in the main file
1474      */
1475     datalen = pblKfThis( isam->mainfile, okey, &okeylen );
1476     if( datalen < 0 )
1477     {
1478         pblIsamCommit( 1, &isamfile, 1 );
1479         return( -1 );
1480     }
1481 
1482     /*
1483      * length 8 or 17 is for allkeys records
1484      * if the length is 9 or 18, the record is a data record
1485      */
1486     if( okeylen != 8 && okeylen != 17 )
1487     {
1488         pblIsamCommit( 1, &isamfile, 1 );
1489         pbl_errno = PBL_ERROR_POSITION;
1490         return( -1 );
1491     }
1492     okey[ okeylen ] = 0;
1493 
1494     /*
1495      * the allkeys record can not be longer than a single data record
1496      */
1497     if( datalen > PBLDATALENGTH )
1498     {
1499         pblIsamCommit( 1, &isamfile, 1 );
1500         pbl_errno = PBL_ERROR_BAD_FILE;
1501         return( -1 );
1502     }
1503 
1504     /*
1505      * read all the keys
1506      */
1507     rc = pblKfRead( isam->mainfile, data, datalen );
1508     if( rc < 0 )
1509     {
1510         pblIsamCommit( 1, &isamfile, 1 );
1511         return( -1 );
1512     }
1513     else if( rc != datalen )
1514     {
1515         pblIsamCommit( 1, &isamfile, 1 );
1516         pbl_errno = PBL_ERROR_BAD_FILE;
1517         return( -1 );
1518     }
1519 
1520     /*
1521      * delete all the keys from the key files
1522      */
1523     for( key = data, n = 0; n < isam->nkeys; n++ )
1524     {
1525         keylen = 0xff & *key++;
1526 
1527         /*
1528          * check the sanity of the allkeys record given
1529          */
1530         if( key + keylen > data + datalen )
1531         {
1532             pbl_errno = PBL_ERROR_BAD_FILE;
1533             retval = -1;
1534             continue;
1535         }
1536 
1537         /*
1538          * if the key is allowing duplicates
1539          */
1540         if( isam->keydup[ n ] )
1541         {
1542             unsigned char ikey[ PBLKEYLENGTH ];
1543 
1544             /*
1545              * a non unique key is deleted, we need the reference key
1546              */
1547             if( rkeylen < 0 )
1548             {
1549                 rkeylen = pblMainKey2RKey( okey, okeylen, rkey );
1550             }
1551             if( rkeylen < 0 )
1552             {
1553                 pbl_errno = PBL_ERROR_BAD_FILE;
1554                 retval = -1;
1555                 continue;
1556             }
1557 
1558             /*
1559              * the key in the index record has the reference key
1560              * as a postfix appended to it
1561              *
1562              * create a unique key for the delete
1563              */
1564             if( keylen + rkeylen > PBLKEYLENGTH )
1565             {
1566                 pbl_errno = PBL_ERROR_BAD_FILE;
1567                 retval = -1;
1568                 continue;
1569             }
1570 
1571             /*
1572              * concatenate the key and the reference
1573              */
1574             memcpy( ikey, key, keylen );
1575             memcpy( ikey + keylen, rkey, rkeylen );
1576 
1577             /*
1578              * make sure any user defined key compare function gets used
1579              */
1580             pblkeycompare = isam->keycompare[ n ];
1581 
1582             /*
1583              * delete the key from the file
1584              */
1585             if( pblKfFind( isam->keyfiles[ n ],
1586                            PBLEQ, ikey, keylen + rkeylen, 0, 0 ) < 0 )
1587             {
1588                 pbl_errno = PBL_ERROR_BAD_FILE;
1589                 retval = -1;
1590                 continue;
1591             }
1592 
1593             if( pblKfDelete( isam->keyfiles[ n ] ))
1594             {
1595                 pbl_errno = PBL_ERROR_BAD_FILE;
1596                 retval = -1;
1597                 continue;
1598             }
1599         }
1600         else
1601         {
1602             /*
1603              * directly use the key as stored in the allkeys record
1604              * of the main file
1605              *
1606              * delete the key from the index file
1607              */
1608             if( pblKfFind( isam->keyfiles[ n ],
1609                            PBLEQ, key, keylen, 0, 0 ) < 0 )
1610             {
1611                 pbl_errno = PBL_ERROR_BAD_FILE;
1612                 retval = -1;
1613                 continue;
1614             }
1615 
1616             if( pblKfDelete( isam->keyfiles[ n ] ))
1617             {
1618                 pbl_errno = PBL_ERROR_BAD_FILE;
1619                 retval = -1;
1620                 continue;
1621             }
1622         }
1623 
1624         /*
1625          * move the key along the keys
1626          */
1627         key += keylen;
1628     }
1629 
1630     /*
1631      * delete all records from the main file
1632      * this deletes the "allkeys" record having a keylength of 8/17 bytes
1633      */
1634     while( pblKfFind( isam->mainfile, PBLEQ, okey, okeylen, 0, 0 ) >= 0 )
1635     {
1636         if( pblKfDelete( isam->mainfile ))
1637         {
1638             pbl_errno = PBL_ERROR_BAD_FILE;
1639             retval = -1;
1640         }
1641     }
1642 
1643     /*
1644      * this deletes the data records having a keylength of 9/18 bytes
1645      */
1646     while( pblKfFind( isam->mainfile, PBLEQ, okey, okeylen + 1, 0, 0 ) >= 0 )
1647     {
1648         if( pblKfDelete( isam->mainfile ))
1649         {
1650             pbl_errno = PBL_ERROR_BAD_FILE;
1651             retval = -1;
1652         }
1653     }
1654 
1655     /*
1656      * position the current record of the main file to the
1657      * allkeys record of another entry of the main file
1658      */
1659     pblIsamSetCurrentRecord( isam );
1660 
1661     if( retval < 0 )
1662     {
1663         pblIsamCommit( 1, &isamfile, 1 );
1664         return( -1 );
1665     }
1666 
1667     if( pblIsamCommit( 1, &isamfile, 0 ))
1668     {
1669         return( -1 );
1670     }
1671     return( retval );
1672 }
1673 
1674 /*
1675  * get the main key from a given index key
1676  *
1677  * for non duplicate index keys this assumes
1678  * that the index keyfile is positioned
1679  * on the record who's key is given as skey
1680  *
1681  * for index keys allowing duplicates this assumes
1682  * that the skey given is the "long" version,
1683  * with the reference key postfix attached.
1684  *
1685  * the current record of the main file is
1686  * positioned on the allkeys record having the key
1687  */
pblIsamGetMainKey(PBLISAMFILE_t * isam,int index,void * skey,size_t skeylen,void * okey)1688 static int pblIsamGetMainKey(
1689 PBLISAMFILE_t  * isam,
1690 int              index,
1691 void           * skey,
1692 size_t           skeylen,
1693 void           * okey
1694 )
1695 {
1696     long            rc;
1697     unsigned char   key[ PBLKEYLENGTH ];
1698     int             keylen;
1699     long            datalen;
1700 
1701     /*
1702      * make sure the index is in bounds
1703      */
1704     if( index >= isam->nkeys )
1705     {
1706         pbl_errno = PBL_ERROR_PARAM_INDEX;
1707         return( -1 );
1708     }
1709 
1710     /*
1711      * if the key has duplicates
1712      */
1713     if( isam->keydup[ index ] )
1714     {
1715         /*
1716          * the main key is a postfix of the referece key
1717          * read the key from there and convert it to an unsigned string
1718          */
1719         keylen = pblRKey2MainKey( skey, skeylen, okey );
1720         if( keylen < 0 )
1721         {
1722             return( -1 );
1723         }
1724     }
1725     else
1726     {
1727         /*
1728          * read the reference, this assumes that the record is positioned!
1729          */
1730         rc = pblKfRead( isam->keyfiles[ index ], key, sizeof( key ) );
1731         if( rc < 0 )
1732         {
1733             return( -1 );
1734         }
1735         keylen = rc;
1736 
1737         /*
1738          * get the key used in the main file
1739          */
1740         if( pblRKey2MainKey( key, keylen, okey ) < 0 )
1741         {
1742             return( -1 );
1743         }
1744     }
1745 
1746     /*
1747      * get the length of the key
1748      */
1749     keylen = strlen( (char*)okey );
1750 
1751     /*
1752      * position the current record of the main file to the allkeys record
1753      */
1754     datalen = pblKfFind( isam->mainfile, PBLFI, okey, keylen, 0, 0 );
1755     if( datalen < 0 )
1756     {
1757         pbl_errno = PBL_ERROR_BAD_FILE;
1758         return( -1 );
1759     }
1760 
1761     return( keylen );
1762 }
1763 
1764 
1765 /*
1766  * find for index keys with possible duplicates
1767  *
1768  * writes the index key found with the reference postfix attached
1769  * to the supplied buffer rkey
1770  *
1771  * returns the length of that key excluding the reference postfix
1772  *
1773  * sets the length of that key including the reference postfix to rkeylen
1774  */
pblIsamFindDupKey(PBLISAMFILE_t * isam,int which,int index,unsigned char * skey,size_t skeylen,unsigned char * rkey,size_t * rkeylen)1775 static int pblIsamFindDupKey(
1776 PBLISAMFILE_t  * isam,
1777 int              which,
1778 int              index,
1779 unsigned char  * skey,
1780 size_t           skeylen,
1781 unsigned char  * rkey,
1782 size_t         * rkeylen
1783 )
1784 {
1785     long            datalen;
1786     unsigned char   fkey[ PBLKEYLENGTH ];
1787     size_t          fkeylen = 0;
1788     unsigned char   ikey[ PBLKEYLENGTH ];
1789     size_t          ikeylen = 0;
1790     int             keylen;
1791     int             lwhich = PBLLT;
1792     int             rc;
1793 
1794     /*
1795      * the search key needs to leave space for the reference
1796      */
1797     if( skeylen > PBLKEYLENGTH - 2 )
1798     {
1799         pbl_errno = PBL_ERROR_PARAM_KEYLEN;
1800         return( -1 );
1801     }
1802 
1803     switch( which )
1804     {
1805       case PBLLT:
1806         /*
1807          * create a reference key smaller than all real ones
1808          */
1809         fkeylen = pblLongs2RKey( 0, 0, fkey );
1810 
1811         /*
1812          * search for key lower than the one created
1813          */
1814         lwhich = PBLLT;
1815         break;
1816 
1817       case PBLLE:
1818 
1819         /*
1820          * the lower equal case is treated as FIRST or LOWER THAN
1821          */
1822         rc = pblIsamFindDupKey( isam, PBLFI, index,
1823                                 skey, skeylen, rkey, rkeylen );
1824         if( rc > 0 )
1825         {
1826             return( rc );
1827         }
1828 
1829         rc = pblIsamFindDupKey( isam, PBLLT, index,
1830                                      skey, skeylen, rkey, rkeylen );
1831         return( rc );
1832         break;
1833 
1834       case PBLFI:
1835       case PBLEQ:
1836         /*
1837          * create a reference key smaller than all real ones
1838          */
1839         fkeylen = pblLongs2RKey( 0, 0, fkey );
1840 
1841         /*
1842          * search for key greater than the one created
1843          */
1844         lwhich = PBLGT;
1845         break;
1846 
1847       case PBLLA:
1848         /*
1849          * create a reference key bigger than all real ones
1850          */
1851         fkeylen = pblLongs2RKey( 0xffffffff, 0xffffffff, fkey );
1852 
1853         /*
1854          * search for a key lower than the one created
1855          */
1856         lwhich = PBLLT;
1857         break;
1858 
1859       case PBLGE:
1860         /*
1861          * the lower equal case is treated as LAST or GREATER THAN
1862          */
1863         rc = pblIsamFindDupKey( isam, PBLLA, index,
1864                                      skey, skeylen, rkey, rkeylen );
1865         if( rc > 0 )
1866         {
1867             return( rc );
1868         }
1869 
1870         rc = pblIsamFindDupKey( isam, PBLGT, index,
1871                                 skey, skeylen, rkey, rkeylen );
1872         return( rc );
1873         break;
1874 
1875       default: /* PBLGT */
1876         /*
1877          * create a reference key bigger than all real ones
1878          */
1879         fkeylen = pblLongs2RKey( 0xffffffff, 0xffffffff, fkey );
1880 
1881         /*
1882          * search for a key greater than the one created
1883          */
1884         lwhich = PBLGT;
1885         break;
1886     }
1887 
1888     /*
1889      * create the key for the search
1890      */
1891     if( fkeylen + skeylen >= PBLKEYLENGTH )
1892     {
1893         pbl_errno = PBL_ERROR_PARAM_KEYLEN;
1894         return( -1 );
1895     }
1896 
1897     /*
1898      * concatenate the key and the reference key
1899      * the reference key is used as postfix of the index key
1900      */
1901     if( skeylen )
1902     {
1903         memcpy( ikey, skey, skeylen );
1904     }
1905     memcpy( ikey + skeylen, fkey, fkeylen );
1906     ikeylen = skeylen + fkeylen;
1907 
1908     /*
1909      * find the record in the key file
1910      */
1911     datalen = pblKfFind( isam->keyfiles[ index ], lwhich,
1912                          ikey, ikeylen, fkey, &fkeylen );
1913     if( datalen < 0 )
1914     {
1915         return( -1 );
1916     }
1917 
1918     /*
1919      * calculate the length of the key without the reference
1920      */
1921     keylen = pblIsamDupKeyLen( fkey, fkeylen );
1922     if( keylen < 0 )
1923     {
1924         pbl_errno = PBL_ERROR_BAD_FILE;
1925         return( -1 );
1926     }
1927 
1928     /*
1929      * in the FIRST, EQUAL and the LAST case the key has to match
1930      */
1931     if( which == PBLFI || which == PBLEQ || which == PBLLA )
1932     {
1933         /*
1934          * see whether the key matches the searchkey
1935          */
1936         if( skeylen != keylen || memcmp( skey, fkey, skeylen ))
1937         {
1938             pbl_errno = PBL_ERROR_NOT_FOUND;
1939             return( -1 );
1940         }
1941     }
1942 
1943     /*
1944      * save the key including the reference as return value
1945      */
1946     memcpy( rkey, fkey, fkeylen );
1947     *rkeylen = fkeylen;
1948 
1949     return( keylen );
1950 }
1951 
1952 
1953 /**
1954  * find a record in an ISAM file, set the current record
1955  *
1956  * parameter which specifies which record to find relative
1957  * to the search key specified by skey and skeylen.
1958  * the following values for which are possible
1959  *
1960  * <BR><B> PBLEQ </B> - find a record whose key is equal to skey
1961  * <BR><B> PBLFI </B> - find the first record that is equal
1962  * <BR><B> PBLLA </B> - find the last record that is equal
1963  * <BR><B> PBLGE </B> - find the last record that is equal or the smallest
1964  *                      record that is greater
1965  * <BR><B> PBLGT </B> - find the smallest record that is greater
1966  * <BR><B> PBLLE </B> - find the first record that is equal or the biggest
1967  *                      record that is smaller
1968  * <BR><B> PBLLT </B> - find the biggest record that is smaller
1969  *
1970  * parameter index specifies which of the keys to use
1971  *
1972  * <P>
1973  * <B>RESTRICTIONS</B>:
1974  * <BR> - the out parameter okey must point to a memory area that is
1975  *        big enough to hold any possible key, i.e 255 bytes
1976  *
1977  * @return int rc >= 0:
1978  * <UL>
1979  * <LI>                  call went ok,
1980  *                       the value returned is the length
1981  *                       of the key of the record found,
1982  * <LI>                  the key of the record is copied to okey,
1983  * <LI>                  the current record of the file is set to the
1984  *                       record found
1985  * </UL>
1986  *
1987  * @return int rc <  0:
1988  * <UL>
1989  * <LI>                  some error occured, see pbl_errno
1990  *                       especially PBL_ERROR_NOT_FOUND, if there is no
1991  *                       matching record
1992  * </UL>
1993  */
1994 
pblIsamFind(pblIsamFile_t * isamfile,int which,int index,void * skey,size_t skeylen,void * okey)1995 int pblIsamFind(
1996 pblIsamFile_t  * isamfile,  /** ISAM file to search in                    */
1997 int              which,     /** mode to use for search                    */
1998 int              index,     /** index of key to use for search            */
1999 void           * skey,      /** key to use for search                     */
2000 size_t           skeylen,   /** length of search key                      */
2001 void           * okey       /** buffer for result key                     */
2002 )
2003 {
2004     PBLISAMFILE_t * isam = ( PBLISAMFILE_t * ) isamfile;
2005     long            datalen;
2006     unsigned char   key[ PBLKEYLENGTH ];
2007     int             keylen;
2008     size_t          okeylen;
2009 
2010     /*
2011      * make sure the index is in bounds
2012      */
2013     if( index >= isam->nkeys )
2014     {
2015         pbl_errno = PBL_ERROR_PARAM_INDEX;
2016         return( -1 );
2017     }
2018 
2019     /*
2020      * if the key has duplicates
2021      */
2022     if( isam->keydup[ index ] )
2023     {
2024         /*
2025          * make sure any user defined key compare function gets used
2026          */
2027         pblkeycompare = isam->keycompare[ index ];
2028 
2029         /*
2030          * search the duplicate key
2031          */
2032         keylen = pblIsamFindDupKey( isam, which, index,
2033                                     skey, skeylen, okey, &okeylen );
2034         if( keylen < 0 )
2035         {
2036             return( keylen );
2037         }
2038     }
2039     else
2040     {
2041         /*
2042          * find the record in the key file
2043          */
2044         datalen = pblKfFind( isam->keyfiles[ index ], which,
2045                              skey, skeylen, okey, &okeylen );
2046         if( datalen < 0 )
2047         {
2048             return( datalen );
2049         }
2050 
2051         if( datalen < 2 || datalen > PBLKEYLENGTH )
2052         {
2053             pbl_errno = PBL_ERROR_BAD_FILE;
2054             return( -1 );
2055         }
2056 
2057         /*
2058          * set the return value
2059          */
2060         keylen = okeylen;
2061     }
2062 
2063     /*
2064      * position current record of the main file on that key
2065      */
2066     if( pblIsamGetMainKey( isam, index, okey, okeylen, key ) < 1 )
2067     {
2068         return( -1 );
2069     }
2070 
2071     if( isam->keydup[ index ] )
2072     {
2073         ((char*)okey)[ keylen ] = 0;
2074     }
2075 
2076     return( keylen );
2077 }
2078 
2079 /*
2080  * read a key from the allkeys record of an entry in the main file
2081  *
2082  * if the reference is requested it is appended to the key
2083  */
pblIsamThisKey(PBLISAMFILE_t * isam,int index,int reference,unsigned char * okey)2084 static int pblIsamThisKey(
2085 PBLISAMFILE_t  * isam,
2086 int              index,
2087 int              reference,
2088 unsigned char  * okey
2089 )
2090 {
2091     int             okeylen = -1;
2092     unsigned char   rkey[ PBLKEYLENGTH ];
2093     int             rkeylen;
2094     unsigned char   fkey[ PBLKEYLENGTH ];
2095     size_t          fkeylen;
2096     unsigned char   data[ PBLDATALENGTH ];
2097     long            datalen;
2098     long            rc;
2099     unsigned char * key;
2100     int             n;
2101 
2102     /*
2103      * make sure the index is in bounds
2104      */
2105     if( index >= isam->nkeys )
2106     {
2107         pbl_errno = PBL_ERROR_PARAM_INDEX;
2108         return( -1 );
2109     }
2110 
2111     /*
2112      * read the key of the current record in the main file
2113      */
2114     datalen = pblKfThis( isam->mainfile, fkey, &fkeylen );
2115     if( datalen < 0 )
2116     {
2117         return( -1 );
2118     }
2119 
2120     /*
2121      * length 8 or 17 is for allkeys records
2122      * if the length is 9 or 18, the record is a data record
2123      */
2124     if( fkeylen != 8 && fkeylen != 17 )
2125     {
2126         pbl_errno = PBL_ERROR_POSITION;
2127         return( -1 );
2128     }
2129     fkey[ fkeylen ] = 0;
2130 
2131     /*
2132      * the allkeys record can not be longer than a single data record
2133      */
2134     if( datalen > PBLDATALENGTH )
2135     {
2136         pbl_errno = PBL_ERROR_BAD_FILE;
2137         return( -1 );
2138     }
2139 
2140     /*
2141      * read all the keys
2142      */
2143     rc = pblKfRead( isam->mainfile, data, datalen );
2144     if( rc < 0 )
2145     {
2146         return( -1 );
2147     }
2148     else if( rc != datalen )
2149     {
2150         pbl_errno = PBL_ERROR_BAD_FILE;
2151         return( -1 );
2152     }
2153 
2154     /*
2155      * get the key
2156      */
2157     for( key = data, n = 0; n <= index; n++ )
2158     {
2159         okeylen = 0xff & *key++;
2160 
2161         if( key + okeylen > data + datalen )
2162         {
2163             pbl_errno = PBL_ERROR_BAD_FILE;
2164             return( -1 );
2165         }
2166 
2167         if( n < index )
2168         {
2169             /*
2170              * move the key along the keys
2171              */
2172             key += okeylen;
2173             continue;
2174         }
2175 
2176         memcpy( okey, key, okeylen );
2177 
2178         /*
2179          * if the reference of a duplicate key is requested
2180          */
2181         if( reference && isam->keydup[ n ] )
2182         {
2183             /*
2184              * create the reference postfix for the key
2185              */
2186             rkeylen = pblMainKey2RKey( fkey, fkeylen, rkey );
2187             if( rkeylen < 0 )
2188             {
2189                 pbl_errno = PBL_ERROR_BAD_FILE;
2190                 return( -1 );
2191             }
2192 
2193             /*
2194              * concatenate the key and the reference
2195              */
2196             if( okeylen + rkeylen > PBLKEYLENGTH )
2197             {
2198                 pbl_errno = PBL_ERROR_BAD_FILE;
2199                 return( -1 );
2200             }
2201 
2202             memcpy( okey + okeylen, rkey, rkeylen );
2203             okeylen += rkeylen;
2204         }
2205     }
2206 
2207     return( okeylen );
2208 }
2209 
2210 /**
2211  * get the key and keylen of a record
2212  *
2213  * parameter which specifies which record to get relative
2214  * to the search key specified by index.
2215  * the following values for which are possible
2216  *
2217  * <BR><B> PBLTHIS  </B> - get key and keylen of current record
2218  * <BR><B> PBLNEXT  </B> - get key and keylen of next record
2219  * <BR><B> PBLPREV  </B> - get key and keylen of previous record
2220  * <BR><B> PBLFIRST </B> - get key and keylen of first record
2221  * <BR><B> PBLLAST  </B> - get key and keylen of last record
2222  *
2223  * parameter index specifies which of the keys to get,
2224  * the pseudo index value -1 can be used in order to access the file
2225  * sequentially in the order the records were inserted.
2226  * okey is not set in this case, a keylength of 0 is returned in case
2227  * the call went ok.
2228  *
2229  * <P>
2230  * <B>RESTRICTIONS</B>:
2231  * <BR> - the out parameter okey must point to a memory area that is
2232  *        big enough to hold any possible key, i.e 255 bytes
2233  *
2234  * @return int rc >= 0:
2235  * <UL>
2236  * <LI>                  call went ok,
2237  *                       the value returned is the length
2238  *                       of the key of the record found,
2239  * <LI>                  the key of the record is copied to okey,
2240  * <LI>                  the current record of the file is set to the
2241  *                       record found
2242  * </UL>
2243  *
2244  * @return int rc <  0:
2245  * <UL>
2246  * <LI>                  some error occured, see pbl_errno
2247  * </UL>
2248  */
2249 
pblIsamGet(pblIsamFile_t * isamfile,int which,int index,void * okey)2250 int pblIsamGet(
2251 pblIsamFile_t  * isamfile,  /** ISAM file to read in                      */
2252 int              which,     /** mode to use for read                      */
2253 int              index,     /** index of key to use for read              */
2254 void           * okey       /** buffer for result key                     */
2255 )
2256 {
2257     PBLISAMFILE_t * isam = ( PBLISAMFILE_t * ) isamfile;
2258     unsigned char   rkey[ PBLKEYLENGTH ];
2259     size_t          rkeylen = 0;
2260     size_t          okeylen;
2261     long            datalen;
2262     int             rc;
2263 
2264     /*
2265      * make sure the index is in bounds
2266      */
2267     if(( index != -1 ) && ( index >= isam->nkeys ))
2268     {
2269         pbl_errno = PBL_ERROR_PARAM_INDEX;
2270         return( -1 );
2271     }
2272 
2273     if( index >= 0 && isam->keydup[ index ] )
2274     {
2275         /*
2276          * make sure any user defined key compare function gets used
2277          */
2278         pblkeycompare = isam->keycompare[ index ];
2279     }
2280 
2281     switch( which )
2282     {
2283       case PBLNEXT:
2284         if( index == -1 )
2285         {
2286             /*
2287              * save the current position of the main file
2288              */
2289             if( pblKfSavePosition( isam->mainfile ))
2290             {
2291                 return( -1 );
2292             }
2293         }
2294 
2295         while( index == -1 )
2296         {
2297             /*
2298              * read the next entry in the main file
2299              */
2300             datalen = pblKfNext( isam->mainfile, rkey, &rkeylen );
2301             if( datalen < 0 )
2302             {
2303                 pblKfRestorePosition( isam->mainfile );
2304                 return( -1 );
2305             }
2306 
2307             /*
2308              * length 8 or 17 is for allkeys records
2309              * if the length is 9 or 18, the record is a data record
2310              */
2311             if( rkeylen != 8 && rkeylen != 17 )
2312             {
2313                 /*
2314                  * found a datarecord, continue reading records
2315                  */
2316                 continue;
2317             }
2318 
2319             return( 0 );
2320         }
2321 
2322         /*
2323          * position to next record in the index file
2324          */
2325         datalen = pblKfNext( isam->keyfiles[ index ], okey, &okeylen );
2326         if( datalen < 0 )
2327         {
2328             return( -1 );
2329         }
2330         break;
2331 
2332       case PBLPREV:
2333         if( index == -1 )
2334         {
2335             /*
2336              * save the current position of the main file
2337              */
2338             if( pblKfSavePosition( isam->mainfile ))
2339             {
2340                 return( -1 );
2341             }
2342         }
2343 
2344         while( index == -1 )
2345         {
2346             /*
2347              * read the previous entry in the main file
2348              */
2349             datalen = pblKfPrev( isam->mainfile, rkey, &rkeylen );
2350             if( datalen < 0 )
2351             {
2352                 pblKfRestorePosition( isam->mainfile );
2353                 return( -1 );
2354             }
2355 
2356             /*
2357              * length 8 or 17 is for allkeys records
2358              * if the length is 9 or 18, the record is a data record
2359              */
2360             if( rkeylen != 8 && rkeylen != 17 )
2361             {
2362                 /*
2363                  * found a datarecord, continue reading records
2364                  */
2365                 continue;
2366             }
2367 
2368             return( 0 );
2369         }
2370 
2371         /*
2372          * position to previous record in the index file
2373          */
2374         datalen = pblKfPrev( isam->keyfiles[ index ], okey, &okeylen );
2375         if( datalen < 0 )
2376         {
2377             return( -1 );
2378         }
2379         break;
2380 
2381       case PBLFIRST:
2382         if( index == -1 )
2383         {
2384             datalen = pblKfFirst( isam->mainfile, 0, 0 );
2385             if( datalen < 0 )
2386             {
2387                 return( -1 );
2388             }
2389             return( 0 );
2390         }
2391 
2392         datalen = pblKfFirst( isam->keyfiles[ index ], okey, &okeylen );
2393         if( datalen < 0 )
2394         {
2395             return( -1 );
2396         }
2397         break;
2398 
2399       case PBLLAST:
2400         if( index == -1 )
2401         {
2402             datalen = pblKfLast( isam->mainfile, rkey, &rkeylen );
2403             if( datalen < 0 )
2404             {
2405                 return( -1 );
2406             }
2407             if( rkeylen == 8 || rkeylen == 17 )
2408             {
2409                 return( 0 );
2410             }
2411         }
2412 
2413         while( index == -1 )
2414         {
2415             /*
2416              * read the next entry in the main file
2417              */
2418             datalen = pblKfPrev( isam->mainfile, rkey, &rkeylen );
2419             if( datalen < 0 )
2420             {
2421                 return( -1 );
2422             }
2423 
2424             /*
2425              * length 8 or 17 is for allkeys records
2426              * if the length is 9 or 18, the record is a data record
2427              */
2428             if( rkeylen != 8 && rkeylen != 17 )
2429             {
2430                 /*
2431                  * found a datarecord, continue reading records
2432                  */
2433                 continue;
2434             }
2435 
2436             return( 0 );
2437         }
2438 
2439         datalen = pblKfLast( isam->keyfiles[ index ], okey, &okeylen );
2440         if( datalen < 0 )
2441         {
2442             return( -1 );
2443         }
2444         break;
2445 
2446       case PBLTHIS:
2447         if( index == -1 )
2448         {
2449             datalen = pblKfThis( isam->mainfile, 0, 0 );
2450             if( datalen < 0 )
2451             {
2452                 return( -1 );
2453             }
2454             return( 0 );
2455         }
2456 
2457         /*
2458          * read the key with the reference
2459          */
2460         rc = pblIsamThisKey( isam, index, 1, okey );
2461         if( rc <= 0 )
2462         {
2463             return( rc );
2464         }
2465         okeylen = rc;
2466 
2467         /*
2468          * find the record in the key file
2469          * position the current record in the index file
2470          */
2471         datalen = pblKfFind( isam->keyfiles[ index ],
2472                              PBLEQ, okey, okeylen, 0, 0 );
2473         if( datalen < 0 )
2474         {
2475             return( -1 );
2476         }
2477         break;
2478 
2479       default:
2480         pbl_errno = PBL_ERROR_PARAM_MODE;
2481         return( -1 );
2482     }
2483 
2484     /*
2485      * no need to position the current record of the main file in this case
2486      */
2487     if( which != PBLTHIS )
2488     {
2489         /*
2490          * position the current record of the main file
2491          */
2492         rc = pblIsamGetMainKey( isam, index, okey, okeylen, rkey );
2493         if( rc < 0 )
2494         {
2495             return( -1 );
2496         }
2497         rkeylen = rc;
2498     }
2499 
2500     /*
2501      * if the key has duplicates
2502      */
2503     if( isam->keydup[ index ] )
2504     {
2505         /*
2506          * calculate the length of the key without the reference postfix
2507          */
2508         rc = pblIsamDupKeyLen( okey, okeylen );
2509         if( rc < 0 )
2510         {
2511             return( -1 );
2512         }
2513         okeylen = rc;
2514 
2515         ((char*)okey)[ okeylen ] = 0;
2516     }
2517 
2518     return( okeylen );
2519 }
2520 
2521 /**
2522  * read the key and keylen of the current record
2523  *
2524  * parameter index specifies which of the keys to read
2525  *
2526  * <P>
2527  * <B>RESTRICTIONS</B>:
2528  * <BR> - the out parameter okey must point to a memory area that is
2529  *        big enough to hold any possible key, i.e 255 bytes
2530  *
2531  * @return int rc >= 0:
2532  * <UL>
2533  * <LI>                  call went ok,
2534  *                       the value returned is the length of the key
2535  * <LI>                  the key of the record is copied to okey,
2536  * <LI>                  the current record of the file is not affected
2537  *                       by this function
2538  * </UL>
2539  * @return int rc <  0:
2540  * <UL>
2541  * <LI>                  some error occured, see pbl_errno
2542  * </UL>
2543  */
2544 
pblIsamReadKey(pblIsamFile_t * isamfile,int index,void * okey)2545 int pblIsamReadKey(
2546 pblIsamFile_t  * isamfile,  /** ISAM file to read in                      */
2547 int              index,     /** index of key read                         */
2548 void           * okey       /** buffer for result key                     */
2549 )
2550 {
2551     PBLISAMFILE_t * isam = ( PBLISAMFILE_t * ) isamfile;
2552     int             okeylen;
2553 
2554     /*
2555      * make sure the index is in bounds
2556      */
2557     if( index >= isam->nkeys )
2558     {
2559         pbl_errno = PBL_ERROR_PARAM_INDEX;
2560         return( -1 );
2561     }
2562 
2563     /*
2564      * read the key without the reference
2565      */
2566     okeylen = pblIsamThisKey( isam, index, 0, okey );
2567 
2568     return( okeylen );
2569 }
2570 
2571 /**
2572  * read the datalen of the current record
2573  *
2574  * @return long rc >= 0: call went ok, the value returned is the length
2575  *                       of the data of the record,
2576  * @return long rc <  0: some error occured, see pbl_errno
2577  */
2578 
pblIsamReadDatalen(pblIsamFile_t * isamfile)2579 long pblIsamReadDatalen(
2580 pblIsamFile_t * isamfile   /** ISAM file to read length of data from */
2581 )
2582 {
2583     PBLISAMFILE_t * isam = ( PBLISAMFILE_t * ) isamfile;
2584     unsigned char   rkey[ PBLKEYLENGTH ];
2585     size_t          rkeylen;
2586     unsigned char   fkey[ PBLKEYLENGTH ];
2587     size_t          fkeylen;
2588     long            datalen = 0;
2589     long            rc = 0;
2590 
2591     /*
2592      * read the key of the current record in the main file
2593      */
2594     rc = pblKfThis( isam->mainfile, fkey, &fkeylen );
2595     if( rc < 0 )
2596     {
2597         return( -1 );
2598     }
2599 
2600     /*
2601      * length 8 or 17 is for allkeys records
2602      * if the length is 9 or 18, the record is a data record
2603      */
2604     if( fkeylen != 8 && fkeylen != 17 )
2605     {
2606         pbl_errno = PBL_ERROR_POSITION;
2607         return( -1 );
2608     }
2609     fkey[ fkeylen ] = 0;
2610 
2611     /*
2612      * save the current position of the main file
2613      */
2614     if( pblKfSavePosition( isam->mainfile ))
2615     {
2616         return( -1 );
2617     }
2618 
2619     /*
2620      * calculate the datalength of the record
2621      */
2622     for( ;; )
2623     {
2624         rc = pblKfNext( isam->mainfile, rkey, &rkeylen );
2625         if( rc < 0 )
2626         {
2627             if( pbl_errno == PBL_ERROR_NOT_FOUND )
2628             {
2629                 pbl_errno = 0;
2630                 break;
2631             }
2632             datalen = -1;
2633             break;
2634         }
2635 
2636         /*
2637          * if we are now positioned on an entry with a different main key
2638          */
2639         if(( fkeylen != rkeylen - 1 ) || memcmp( fkey, rkey, fkeylen ))
2640         {
2641             break;
2642         }
2643 
2644         datalen += rc;
2645     }
2646 
2647     /*
2648      * restore the current position of the main file
2649      */
2650     if( pblKfRestorePosition( isam->mainfile ))
2651     {
2652         return( -1 );
2653     }
2654 
2655     return( datalen );
2656 }
2657 
2658 /**
2659  * read the data of the current record
2660  *
2661  * parameter bufferlen specifies how many bytes to read
2662  *
2663  * @return long rc >= 0: call went ok, the value returned is the length
2664  *                       of the data copied
2665  * @return long rc <  0: some error occured, see pbl_errno
2666  */
2667 
pblIsamReadData(pblIsamFile_t * isamfile,void * buffer,size_t bufferlen)2668 long pblIsamReadData(
2669 pblIsamFile_t * isamfile,  /** ISAM file to read from                      */
2670 void          * buffer,    /** buffer to read to                           */
2671 size_t          bufferlen  /** length of that buffer                       */
2672 )
2673 {
2674     PBLISAMFILE_t * isam = ( PBLISAMFILE_t * ) isamfile;
2675     unsigned char   rkey[ PBLKEYLENGTH ];
2676     size_t          rkeylen;
2677     unsigned char   fkey[ PBLKEYLENGTH ];
2678     size_t          fkeylen;
2679     long            dataread = 0;
2680     long            rc;
2681 
2682     /*
2683      * read the key of the current record in the main file
2684      */
2685     rc = pblKfThis( isam->mainfile, fkey, &fkeylen );
2686     if( rc < 0 )
2687     {
2688         return( -1 );
2689     }
2690 
2691     /*
2692      * length 8 or 17 is for allkeys records
2693      * if the length is 9 or 18, the record is a data record
2694      */
2695     if( fkeylen != 8 && fkeylen != 17 )
2696     {
2697         pbl_errno = PBL_ERROR_POSITION;
2698         return( -1 );
2699     }
2700     fkey[ fkeylen ] = 0;
2701 
2702     /*
2703      * save the current position of the main file
2704      */
2705     if( pblKfSavePosition( isam->mainfile ))
2706     {
2707         return( -1 );
2708     }
2709 
2710     /*
2711      * read the data of the record
2712      */
2713     while( bufferlen > 0 )
2714     {
2715         rc = pblKfNext( isam->mainfile, rkey, &rkeylen );
2716         if( rc < 0 )
2717         {
2718             if( pbl_errno == PBL_ERROR_NOT_FOUND )
2719             {
2720                 pbl_errno = 0;
2721                 break;
2722             }
2723             dataread = -1;
2724             break;
2725         }
2726 
2727         /*
2728          * if we are now positioned on an entry with a different main key
2729          */
2730         if(( fkeylen != rkeylen - 1 ) || memcmp( fkey, rkey, fkeylen ))
2731         {
2732             break;
2733         }
2734 
2735         if( rc > (long)bufferlen )
2736         {
2737             rc = bufferlen;
2738         }
2739 
2740         if( rc > 0 )
2741         {
2742             rc = pblKfRead( isam->mainfile, ((char*)buffer) + dataread, rc );
2743             if( rc < 0 )
2744             {
2745                 dataread = -1;
2746                 break;
2747             }
2748             bufferlen -= rc;
2749             dataread  += rc;
2750         }
2751     }
2752 
2753     /*
2754      * restore the current position of the main file
2755      */
2756     if( pblKfRestorePosition( isam->mainfile ))
2757     {
2758         return( -1 );
2759     }
2760 
2761     return( dataread );
2762 }
2763 
2764 /**
2765  * update the data of the current record
2766  *
2767  * parameter datalen specifies how many bytes to write
2768  *
2769  * the file must be open for update
2770  *
2771  * @return long rc >= 0: call went ok, the value returned is the length
2772  *                       of the data copied
2773  * @return long rc <  0: some error occured, see pbl_errno
2774  */
2775 
pblIsamUpdateData(pblIsamFile_t * isamfile,void * data,size_t datalen)2776 long pblIsamUpdateData(
2777 pblIsamFile_t * isamfile,  /** ISAM file to update                       */
2778 void          * data,      /** data to write                             */
2779 size_t          datalen    /** length of that data                       */
2780 )
2781 {
2782     PBLISAMFILE_t * isam = ( PBLISAMFILE_t * ) isamfile;
2783     unsigned char   rkey[ PBLKEYLENGTH ];
2784     size_t          rkeylen;
2785     unsigned char   fkey[ PBLKEYLENGTH ];
2786     size_t          fkeylen;
2787     long            nwritten = 0;
2788     long            n;
2789     long            rc;
2790     long            olddatalen;
2791 
2792     /*
2793      * start a transaction
2794      */
2795     pblIsamStartTransaction( 1, &isamfile );
2796 
2797     /*
2798      * read the key of the current record in the main file
2799      */
2800     rc = pblKfThis( isam->mainfile, fkey, &fkeylen );
2801     if( rc < 0 )
2802     {
2803         /*
2804          * rollback all changes
2805          */
2806         pblIsamCommit( 1, &isamfile, 1 );
2807         return( -1 );
2808     }
2809 
2810     /*
2811      * read the old datalen of the records
2812      */
2813     olddatalen = pblIsamReadDatalen( isamfile );
2814 
2815     /*
2816      * length 8 or 17 is for allkeys records
2817      * if the length is 9 or 18, the record is a data record
2818      */
2819     if( fkeylen != 8 && fkeylen != 17 )
2820     {
2821         /*
2822          * rollback all changes
2823          */
2824         pblIsamCommit( 1, &isamfile, 1 );
2825         pbl_errno = PBL_ERROR_POSITION;
2826         return( -1 );
2827     }
2828     fkey[ fkeylen ] = 0;
2829 
2830     /*
2831      * when the datalen gets shorter we first delete records
2832      */
2833     while( (long)datalen < olddatalen )
2834     {
2835         /*
2836          * position to the last record that has data
2837          */
2838         rc = pblKfFind( isam->mainfile, PBLLA, fkey, fkeylen + 1, 0, 0 );
2839         if( rc < 0 )
2840         {
2841             break;
2842         }
2843 
2844         /*
2845          * we shorten the data of the record
2846          */
2847         olddatalen -= rc;
2848 
2849         /*
2850          * delete the record
2851          */
2852         rc = pblKfDelete( isam->mainfile );
2853         if( rc < 0 )
2854         {
2855             /*
2856              * rollback all changes
2857              */
2858             pblIsamCommit( 1, &isamfile, 1 );
2859 
2860             return( -1 );
2861         }
2862 
2863         if( (long)datalen < olddatalen )
2864         {
2865             /*
2866              * we need to delete more records from the end
2867              */
2868             continue;
2869         }
2870 
2871         /*
2872          * position the current record of the main file to the allkeys record
2873          */
2874         rc = pblKfFind( isam->mainfile, PBLFI, fkey, fkeylen, 0, 0 );
2875         if( rc < 0 )
2876         {
2877             /*
2878              * rollback all changes
2879              */
2880             pblIsamCommit( 1, &isamfile, 1 );
2881 
2882             pbl_errno = PBL_ERROR_BAD_FILE;
2883             return( -1 );
2884         }
2885         break;
2886     }
2887 
2888     /*
2889      * update existing records
2890      */
2891     while( datalen > 0 )
2892     {
2893         rc = pblKfNext( isam->mainfile, rkey, &rkeylen );
2894         if( rc < 0 )
2895         {
2896             if( pbl_errno == PBL_ERROR_NOT_FOUND )
2897             {
2898                 pbl_errno = 0;
2899                 break;
2900             }
2901             nwritten = -1;
2902             break;
2903         }
2904 
2905         /*
2906          * if we are now positioned on an entry with a different main key
2907          */
2908         if(( fkeylen != rkeylen - 1 ) || memcmp( fkey, rkey, fkeylen ))
2909         {
2910             break;
2911         }
2912 
2913         n = datalen;
2914         if( n > PBLDATALENGTH )
2915         {
2916             n = PBLDATALENGTH;
2917         }
2918 
2919         /*
2920          * udpate the data of the record
2921          */
2922         if( n > 0 )
2923         {
2924             rc = pblKfUpdate( isam->mainfile, ((char*)data) + nwritten, n );
2925             if( rc < 0 )
2926             {
2927                 nwritten = -1;
2928                 break;
2929             }
2930             datalen -= n;
2931             nwritten  += n;
2932         }
2933     }
2934 
2935     /*
2936      * append new records
2937      */
2938     while( datalen > 0 && nwritten >= 0 )
2939     {
2940         n = datalen;
2941         if( n > PBLDATALENGTH )
2942         {
2943             n = PBLDATALENGTH;
2944         }
2945 
2946         if( n > 0 )
2947         {
2948             rc = pblKfInsert( isam->mainfile, fkey, fkeylen + 1,
2949                               ((char*)data) + nwritten, n );
2950             if( rc < 0 )
2951             {
2952                 nwritten = -1;
2953                 break;
2954             }
2955             datalen -= n;
2956             nwritten  += n;
2957         }
2958     }
2959 
2960     /*
2961      * position the current record of the main file to the allkeys record
2962      */
2963     rc = pblKfFind( isam->mainfile, PBLFI, fkey, fkeylen, 0, 0 );
2964     if( rc < 0 )
2965     {
2966         /*
2967          * rollback all changes
2968          */
2969         pblIsamCommit( 1, &isamfile, 1 );
2970 
2971         pbl_errno = PBL_ERROR_BAD_FILE;
2972         return( -1 );
2973     }
2974 
2975     if( nwritten < 0 )
2976     {
2977         /*
2978          * rollback all changes
2979          */
2980         pblIsamCommit( 1, &isamfile, 1 );
2981         return( -1 );
2982     }
2983 
2984     /*
2985      * commit all changes
2986      */
2987     if( pblIsamCommit( 1, &isamfile, 0 ))
2988     {
2989         return( -1 );
2990     }
2991 
2992     return( nwritten );
2993 }
2994 
2995 /**
2996  * update a key of the current record of the ISAM file
2997  *
2998  * parameter index specifies which of the keys to update
2999  *
3000  * the file must be open for update
3001  *
3002  * @return int rc == 0: call went ok
3003  * @return int rc != 0: some error occured, see pbl_errno
3004  */
3005 
pblIsamUpdateKey(pblIsamFile_t * isamfile,int index,void * ukey,size_t ukeylen)3006 int pblIsamUpdateKey(
3007 pblIsamFile_t  * isamfile,   /** ISAM file to update key for                */
3008 int              index,      /** index of key to update                     */
3009 void           * ukey,       /** new value for the key to update            */
3010 size_t           ukeylen     /** length of that value                       */
3011 )
3012 {
3013     PBLISAMFILE_t * isam = ( PBLISAMFILE_t * ) isamfile;
3014     unsigned char   okey[ PBLKEYLENGTH ];
3015     size_t          okeylen;
3016     unsigned char   rkey[ PBLKEYLENGTH ];
3017     int             rkeylen = -1;
3018     unsigned char   fkey[ PBLKEYLENGTH ];
3019     int             fkeylen;
3020     unsigned char * key;
3021     size_t          keylen;
3022     unsigned char   data[ PBLDATALENGTH ];
3023     long            datalen;
3024     long            rc;
3025     int             n;
3026     unsigned char   newdata[ 2 * PBLDATALENGTH ];
3027     long            newdatalen = 0;
3028     unsigned char * newkey = newdata;
3029 
3030     /*
3031      * start a transaction
3032      */
3033     pblIsamStartTransaction( 1, &isamfile );
3034 
3035     /*
3036      * position the current record of the keyfile in question
3037      */
3038     fkeylen = pblIsamGet( isamfile, PBLTHIS, index, fkey );
3039     if( fkeylen < 0 )
3040     {
3041         /*
3042          * rollback all changes
3043          */
3044         pblIsamCommit( 1, &isamfile, 1 );
3045         return( -1 );
3046     }
3047 
3048     /*
3049      * read the key of the current record in the main file
3050      */
3051     datalen = pblKfThis( isam->mainfile, okey, &okeylen );
3052     if( datalen < 0 )
3053     {
3054         /*
3055          * rollback all changes
3056          */
3057         pblIsamCommit( 1, &isamfile, 1 );
3058         return( -1 );
3059     }
3060 
3061     /*
3062      * length 8 or 17 is for allkeys records
3063      * if the length is 9 or 18, the record is a data record
3064      */
3065     if( okeylen != 8 && okeylen != 17 )
3066     {
3067         /*
3068          * rollback all changes
3069          */
3070         pblIsamCommit( 1, &isamfile, 1 );
3071         pbl_errno = PBL_ERROR_POSITION;
3072         return( -1 );
3073     }
3074     okey[ okeylen ] = 0;
3075 
3076     /*
3077      * the allkeys record can not be longer than a single data record
3078      */
3079     if( datalen > PBLDATALENGTH )
3080     {
3081         /*
3082          * rollback all changes
3083          */
3084         pblIsamCommit( 1, &isamfile, 1 );
3085         pbl_errno = PBL_ERROR_BAD_FILE;
3086         return( -1 );
3087     }
3088 
3089     /*
3090      * read all the keys
3091      */
3092     rc = pblKfRead( isam->mainfile, data, datalen );
3093     if( rc < 0 )
3094     {
3095         /*
3096          * rollback all changes
3097          */
3098         pblIsamCommit( 1, &isamfile, 1 );
3099         return( -1 );
3100     }
3101     else if( rc != datalen )
3102     {
3103         /*
3104          * rollback all changes
3105          */
3106         pblIsamCommit( 1, &isamfile, 1 );
3107         pbl_errno = PBL_ERROR_BAD_FILE;
3108         return( -1 );
3109     }
3110 
3111     /*
3112      * make sure the key given is ok
3113      */
3114     if(( ukeylen > PBLKEYLENGTH ) || ( ukeylen < 1 && !isam->keydup[ index ] ))
3115     {
3116         /*
3117          * rollback all changes
3118          */
3119         pblIsamCommit( 1, &isamfile, 1 );
3120         pbl_errno = PBL_ERROR_PARAM_KEYLEN;
3121         return( -1 );
3122     }
3123 
3124     /*
3125      * copy all the keys, update the new one
3126      */
3127     for( key = data, n = 0; n < isam->nkeys; n++ )
3128     {
3129         if( n == index )
3130         {
3131             keylen = 0xff & *key++;
3132             if( keylen == ukeylen && !memcmp( key, ukey, ukeylen ))
3133             {
3134                 /*
3135                  * the key has not changed
3136                  */
3137                 pblIsamCommit( 1, &isamfile, 0 );
3138                 return( 0 );
3139             }
3140 
3141             /*
3142              * copy the new key into the allkeys array
3143              */
3144             *newkey++ = 0xff & ukeylen;
3145             if( 0xff & ukeylen )
3146             {
3147                 memcpy( newkey, ukey, 0xff & ukeylen );
3148                 newkey += 0xff & ukeylen;
3149             }
3150             key += keylen;
3151         }
3152         else
3153         {
3154             *newkey++ = keylen = 0xff & *key++;
3155 
3156             /*
3157              * copy the old key
3158              */
3159             if( keylen )
3160             {
3161                 memcpy( newkey, key, keylen );
3162                 newkey += keylen;
3163                 key    += keylen;
3164             }
3165         }
3166 
3167         /*
3168          * check whether the new allkeys array is in bounds
3169          */
3170         if( newkey - newdata > PBLDATALENGTH )
3171         {
3172             /*
3173              * rollback all changes
3174              */
3175             pblIsamCommit( 1, &isamfile, 1 );
3176             pbl_errno = PBL_ERROR_PARAM_KEYLEN;
3177             return( -1 );
3178         }
3179     }
3180     newdatalen = newkey - newdata;
3181 
3182     /*
3183      * update the entry in the index file
3184      * if the key is allowing duplicates
3185      */
3186     if( isam->keydup[ index ] )
3187     {
3188         unsigned char ikey[ PBLKEYLENGTH ];
3189 
3190         /*
3191          * create the reference key
3192          */
3193         rkeylen = pblMainKey2RKey( okey, okeylen, rkey );
3194         if( rkeylen < 0 )
3195         {
3196             /*
3197              * rollback all changes
3198              */
3199             pblIsamCommit( 1, &isamfile, 1 );
3200             pbl_errno = PBL_ERROR_BAD_FILE;
3201             return( -1 );
3202         }
3203 
3204         /*
3205          * create a unique key for the insert
3206          */
3207         if( ukeylen + rkeylen > PBLKEYLENGTH )
3208         {
3209             /*
3210              * rollback all changes
3211              */
3212             pblIsamCommit( 1, &isamfile, 1 );
3213             pbl_errno = PBL_ERROR_PARAM_KEYLEN;
3214             return( -1 );
3215         }
3216 
3217         /*
3218          * concatenate the key and the reference
3219          */
3220         memcpy( ikey, ukey, ukeylen );
3221         memcpy( ikey + ukeylen, rkey, rkeylen );
3222 
3223         /*
3224          * make sure any user defined key compare function gets used
3225          */
3226         pblkeycompare = isam->keycompare[ index ];
3227 
3228         /*
3229          * search for the new key in the file
3230          */
3231         if( pblKfFind( isam->keyfiles[ index ],
3232                        PBLEQ, ikey, ukeylen + rkeylen, 0, 0 ) >= 0 )
3233         {
3234             /*
3235              * rollback all changes
3236              */
3237             pblIsamCommit( 1, &isamfile, 1 );
3238             pbl_errno = PBL_ERROR_EXISTS;
3239             return( -1 );
3240         }
3241 
3242         /*
3243          * delete the old record
3244          */
3245         if( pblKfDelete( isam->keyfiles[ index ] ))
3246         {
3247             /*
3248              * rollback all changes
3249              */
3250             pblIsamCommit( 1, &isamfile, 1 );
3251             return( -1 );
3252         }
3253 
3254         /*
3255          * insert the key to the file
3256          */
3257         if( pblKfInsert( isam->keyfiles[ index ],
3258                          ikey, ukeylen + rkeylen,
3259                          0, 0 ))
3260         {
3261             /*
3262              * rollback all changes
3263              */
3264             pblIsamCommit( 1, &isamfile, 1 );
3265             return( -1 );
3266         }
3267     }
3268     else
3269     {
3270         /*
3271          * search for the new key in the file
3272          */
3273         if( pblKfFind( isam->keyfiles[ index ],
3274                        PBLEQ, ukey, ukeylen, 0, 0 ) >= 0 )
3275         {
3276             /*
3277              * rollback all changes
3278              */
3279             pblIsamCommit( 1, &isamfile, 1 );
3280             pbl_errno = PBL_ERROR_EXISTS;
3281             return( -1 );
3282         }
3283 
3284         /*
3285          * create the reference key
3286          */
3287         rkeylen = pblMainKey2RKey( okey, okeylen, rkey );
3288         if( rkeylen < 0 )
3289         {
3290             /*
3291              * rollback all changes
3292              */
3293             pblIsamCommit( 1, &isamfile, 1 );
3294             pbl_errno = PBL_ERROR_BAD_FILE;
3295             return( -1 );
3296         }
3297 
3298         /*
3299          * delete the old record
3300          */
3301         if( pblKfDelete( isam->keyfiles[ index ] ))
3302         {
3303             /*
3304              * rollback all changes
3305              */
3306             pblIsamCommit( 1, &isamfile, 1 );
3307             return( -1 );
3308         }
3309 
3310         /*
3311          * insert the key to the file
3312          */
3313         if( pblKfInsert( isam->keyfiles[ index ],
3314                          ukey, ukeylen,
3315                          rkey, rkeylen ))
3316         {
3317             /*
3318              * rollback all changes
3319              */
3320             pblIsamCommit( 1, &isamfile, 1 );
3321             return( -1 );
3322         }
3323     }
3324 
3325     /*
3326      * update the allkeys record
3327      */
3328     rc = pblKfUpdate( isam->mainfile, newdata, newdatalen );
3329     if( rc < 0 )
3330     {
3331         /*
3332          * rollback all changes
3333          */
3334         pblIsamCommit( 1, &isamfile, 1 );
3335         return( -1 );
3336     }
3337 
3338     /*
3339      * commit all changes
3340      */
3341     if( pblIsamCommit( 1, &isamfile, 0 ))
3342     {
3343         return( -1 );
3344     }
3345 
3346     return( rc );
3347 }
3348 
3349 /**
3350  * set an application specific compare function for a key of an ISAM file
3351  *
3352  * parameter index specifies which of the indices to set
3353  * the compare function for
3354  *
3355  * an application specific compare function can be used in order to
3356  * implement special orderings of the values of an index, e.g.
3357  * because of the use of european "umlauts" in names
3358  *
3359  * the default compare function is the c-library memcmp function
3360  * the keycompare function should behave like memcmp
3361  *
3362  * @return int rc == 0: call went ok
3363  * @return int rc != 0: some error occured, see pbl_errno
3364  */
3365 
pblIsamSetCompareFunction(pblIsamFile_t * isamfile,int index,int (* keycompare)(void * left,size_t llen,void * right,size_t rlen))3366 int pblIsamSetCompareFunction(
3367 pblIsamFile_t  * isamfile,    /** ISAM file to set function for         */
3368 int              index,       /** index of key to set function for      */
3369 int ( *keycompare )           /** compare function to set               */
3370     (
3371                 void* left,   /** "left" buffer for compare             */
3372                 size_t llen,  /** length of that buffer                 */
3373                 void* right,  /** "right" buffer for compare            */
3374                 size_t rlen   /** length of that buffer                 */
3375     )
3376 )
3377 {
3378     PBLISAMFILE_t * isam = ( PBLISAMFILE_t * ) isamfile;
3379 
3380     /*
3381      * make sure the index is in bounds
3382      */
3383     if( index >= isam->nkeys )
3384     {
3385         pbl_errno = PBL_ERROR_PARAM_INDEX;
3386         return( -1 );
3387     }
3388 
3389     /*
3390      * if the key has duplicates
3391      */
3392     if( isam->keydup[ index ] )
3393     {
3394         /*
3395          * we need to remember the compare function in the ISAM layer
3396          * for use in the pblIsamDupKeyCompare function
3397          */
3398         isam->keycompare[ index ] = keycompare;
3399     }
3400     else
3401     {
3402         /*
3403          * set the custom compare function to the KF layer
3404          */
3405         pblKfSetCompareFunction( isam->keyfiles[ index ], keycompare );
3406 
3407     }
3408 
3409     return( 0 );
3410 }
3411 
3412