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