The library provides two separate interface methods.
The base API uses allocated file descriptors (similar to the system fopen/fwrite calls) to manage isam files, and it will be this interface that is described here.
The wrap (CISAM™ standard) interface is covered here.
All modules which make core API calls should include isbase.h
In all of the following, isfd will be used to refer to an open isam file descriptor, as returned by isOpen or isBuild, and declared as:
struct IsamFile *isfd; /* the formal way */ IsFile *isfd; /* typedef shortcut */
With the exception of isBuild, isOpen and a few others, the base will return a boolean ISTRUE or ISFALSE. ISTRUE and ISFALSE are defined in the isbase.h header as 1 and 0 respectively.
When ISFALSE is returned you will find the system or isam error code in isfd->iserrno, where isfd is an open file descriptor. ISAM error codes start at 500 (refer to ISERRBASE in [DISAM_INSTALL] if you need to change this) and are listed under error codes below. System error codes have their usual values for the target system.
If iserrno is less than 500 (ie a system error) isErrio( isfd ) (or isfd->iserrio) will provide a little more detail as to where and why, as shown under [Error Codes] below.
Please note the original value for ISERRBASE, while compatible with CISAM, is no longer high enough to encompass all the normal system errors, however if you are willing to deal with the confusion which may result, there should be no other harm in re-setting ISERRBASE to the original value of 100.
In situations where no file descriptor is available, such as when isBuild or isOpen fail, or after isClose, isErase or isRename, the call will return the error code in the global errno variable.
The library includes the following series of calls to provide a means of generating data that will be portable to different machines, across the endian divide.
Use of these is only required if you need such portability, since the library will accept native formats without complaint.
int ldInt( char *pad ); int stInt( int value, char *pad ); long ldLong( char *pad ); int stLong( long value, char *pad ); int stChar( char *pad, char *str, int len ); int ldChar( char *pad, int len, char *str ); double ldFloat( char *pad ); int stFloat( double value, char *pad ); double ldDbl( char *pad ); int stDbl( double value, char *pad ); int stFltNull( double value, char *pad, short *null ); double ldFltNull( char *pad, short *null ); int stDblNull( double value, char *pad, short *null ); double ldDblNull( char *pad, short *null );
In all of the above, st identifies calls that will place a value in the specified location, which can then be retrieved by the matching ld call.
The Null suffix calls will load the variable passed (by address) in the last parameter with a boolean ISTRUE if the associated value is zero, or ISFALSE if not.
All pads referred to should be the size of the associated data type, with the exception of ldInt and stInt, which refer to a two byte integer, regardless of the native word size, and ldLong and stLong, which refer to four bytes.
These (cisam derived) function names are based on old world (16 bit native word size) realities, where int was 2 bytes (short) and long meant 4.
The Float and Double calls do not perform any actual data conversion, and are provided as a means of avoiding data alignment problems only.
The Int and Long calls will perform byte order reversal (sometimes referred to as swabbing) on platforms where the least significant byte is found at the beginning of the field.
ldInt and stInt operate on short (2 byte) integers, although the value and return are passed as int, which ought to be big enough in all models, including 16 bit.
Since the 64 bit model suddenly makes long twice as big as will fit, ldLong and stLong now operate on int32_t as defined in stdint.h. See Standard (C99) Integers for more.
ISAM indexes are defined/described by means of the keydesc structure.
The following is an example of the basic procedure, and describes an index that allows duplicates, has full compression, and consists of two fields - a machine independent integer taken from the first two bytes of the record, and a character string from the seventh through sixteenth bytes.
struct keydesc key; key.k_flags = ISDUPS+ISCOMPRESS; /* see index flags */ key.k_nparts = 2; /* number of fields involved */ key.k_part[0].kp_start = 0; /* offset in data record */ key.k_part[0].kp_leng = INTSIZE; /* length */ key.k_part[0].kp_type = INTTYPE; /* type */ key.k_part[1].kp_start = 7; /* offset in data record */ key.k_part[1].kp_leng = 10 * CHARSIZE; /* length */ key.k_part[1].kp_type = CHARTYPE; /* type */
This structure is then passed to isBuild, isAddIndex, isStart, etc.
ISNODUPS no duplicates permitted ISDUPS duplicates permitted DCOMPRESS compress duplicates LCOMPRESS leading compression TCOMPRESS trailing space compression COMPRESS full compression TNULL compress on zero instead of space in TCOMPRESS
CHARTYPE array of bytes/characters INTTYPE two byte (short) integer LONGTYPE four byte (long) integer DOUBLETYPE ieee double floating point FLOATTYPE ieee single floating point MINTTYPE machine (native) short MLONGTYPE machine (native) long STRINGTYPE null terminated string ISDESC add to any above for descending order
It is generally advisable to provide some callable means of loading your key descriptions into a keydesc structure, or to provide some other means of easy access, since they will be needed for selecting indexes via isStart.
You will also need to exercise caution when using CHARTYPE fields as the library does not recognise null terminated strings. For example, if you're referring to a 10 byte wide character field, and it contains a seven byte string, a null terminator and 2 garbage bytes following the null, the library will build a key 10 bytes in length that includes the null and trailing garbage. This key might prove difficult to locate later.
The officially approved solution is to use the included stchar() and ldchar() calls, which will pad the string passed with spaces on store and replace the null terminator on load.
Note also that cisam standard trailing compression works on strings of trailing spaces, so the common practice of terminating strings with a zero (and optionally padding the remainder of the field with zeros) will result in inefficient operation when trailing compression is expected.
STRINGTYPE has been added, to extend the standard, and this causes terminated strings to be correctly indexed without extra padding.
TNULL has also been added, to trigger trailing compression on zero values, rather than spaces.
There are two different groups of open modes used by the isOpen and isBuild calls. The first group describes the operations which the current process will be allowed to perform, while the latter controls what forms of external access will be permitted.
Locking Method (mandatory)
ISMANULOCK record locks placed manually ISAUTOLOCK record locks placed automatically on read ISEXCLLOCK exclusive access to index and data ISRDONLY read only - no locking
I/O Modes
ISINPUT open file for reading only (default) ISOUTPUT open file for writing only ISINOUT open file for both reading and writing
Variable Length - see Variable Length Records.
ISVARLEN variable length records - see varlen.ref ISVARCMP RLE compressed varlen data ISNOCARE open either fixed or variable length
Transaction Processing - see Transaction Processing.
ISTRANS transaction processing ISNOLOG disable logging
Other Options
ISMASKED index masking option - see Index Masking ISSYNCWR all writes flushed to disk before return
Please note that ISSYNCWR is only active for systems that permit O_SYNC in the file open call. This precludes most DOS flavoured systems.
all isRead operations act on the currently selected index.
ISFIRST select the first record in the index ISLAST select the last record in the index ISEQUAL search for and return the first exact match, or error ISGTEQ search for first exact match, or next greater ISGREAT search for the next greatest key in the index ISNEXT skip to the next key in the index ISPREV skip to the previous key in the index ISCURR reread the current location.
ISNEXT and ISPREV behave differently when used in isRead immediately after in isStart call in that they will return the current rather than the next/previous record.
This is a feature, not a bug. The intention is to allow isStart to function as an initialisation call preceding a read next/previous loop, as in the following:
isStart( isfd, &secondary_index, 0, NULL, ISFIRST ); while( isRead( isfd, data_pointer, ISNEXT ) ) display_data( data_pointer );
The above will read the contents of isfd from the top in secondary_index order.
These are added to the search mode in isRead to lock or wait for the release of the selected record.
ISLOCK lock the selected record against external update ISWAIT wait until the selected record has been released ISLCKW wait for and unlock the selected record
ISLOCK will return an ELOCKED error if the selected record has been locked, but will still return the selected record image. this allows the process to read an externally locked record, but warns that updates will not be permitted.
ISWAIT and ISLCKW do no deadlock processing, and will wait forever, so applications should be coded appropriately.
The isLock, isUnLock, isRelease, isRelRec and isRelCurr group of functions provide further control over record locking, and you can also use ISEXCLLOCK to lock other processes out of a file completely.