1862 lines
55 KiB
Plaintext
Executable File
1862 lines
55 KiB
Plaintext
Executable File
ISAM REFERENCE
|
||
|
||
|
||
Version 2.10
|
||
|
||
|
||
February 3, 1995
|
||
|
||
|
||
(C) Copyright Psion PLC 1990-95
|
||
|
||
|
||
All rights reserved. This manual and the programs referred to herein are copyrighted works of Psion
|
||
PLC, London, England. Reproduction in whole or in part, including utilization in machines capable of
|
||
reproduction or retrieval, without express written permission of Psion PLC, is prohibited. Reverse
|
||
engineering is also prohibited.
|
||
|
||
|
||
The information in this document is subject to change without notice.
|
||
|
||
|
||
Psion and the Psion logo are registered trademarks, and Psion, Psion MC, Psion HC, Psion Series 3,
|
||
Psion Series 3a and Psion Workabout are trademarks of Psion PLC.
|
||
|
||
|
||
TopSpeed is a registered trademark of Clarion Software Corporation. IBM, IBM XT and IBM AT are
|
||
registered trademarks of International Business Machines Corp. Microsoft and MS-DOS are registered
|
||
trademarks of Microsoft Corporation. Apple and Macintosh are registered trademarks of Apple Computer
|
||
Inc. VAX and VMS are registered trademarks of Digital Equipment Corporation. Brief is a registered
|
||
trademark of Underware Inc. Psion PLC acknowledges that some other names referred to are registered
|
||
trademarks.
|
||
|
||
|
||
Ea Se eee
|
||
|
||
|
||
Contents
|
||
|
||
|
||
OO eee
|
||
|
||
|
||
DV TREODUCHON os. cre ciacsesdcztestsavis besksace sabteeentce ce ic thee es ca i 1
|
||
OVER VICW Pres cite ccc vapciu part tetne sacra see trt narescet em vennescattes’ ieitins dike one vessdaice 1
|
||
MPS TSAIM NR ALY wigiie ven tersestode Gaavaaabsiavenssniavedincy ss wersaye Secseevoearcceucokiacheeks 1
|
||
DBF IGS aed drauu gar tha nis det ane oe di dewey oenueatgtiad tenes Grateiunew ne ules honttcat Nt 2
|
||
PICS arsdeecceantd veunasarcwacmecuransoisnagteusias seve canueee paises ioc Seales: Staab ected 2
|
||
NUMBER OR FECORES oo ceca. ailoc feel awesseopeissavvapiaveréuecs taibesctbiacteesGshealdleccs 2
|
||
Bethe ee ES: 3550 sins wands cocuesuestn fa catpeiaaile Uened tamale noes davik ov caus tstdewonc wie orasc oe 2
|
||
BIOCK DUNERIMG i cnac acces anstyes cuyinassuestiea Madeausccauan innate es ded cuhaesiontiecses ee 2
|
||
ICY CES CHE OM ernst ceceatecsatovissanasdelsadendsastaccs dbversducenccesageiner foxtevente osha. 2
|
||
DIZELOMBaEG MNOS ic ascaveusdacsadeacensetarasrsuaiiooievdediduamesbestiuree eicanacaniclncs 3
|
||
Interface to the ISAM library.............sscssssscecceccssseccccessccccusessceseeceecscccesees 3
|
||
BOAO NG ION Esai neecestans avsseccnensaguatvcsewaseoss iiaceebataoaeetsvaeeickecuicenicet 3
|
||
Creating am (SAM :OB)OCE si. 5cc.iscsnssasteecisadeiWevevasevisesss aide keaseaeavedeaesiseeécck 4
|
||
Calling (ISAM MUNCLONS 4.iscissye2sedoacasuedsaseerdeweesecedansdssesesahuocdcvercecsa covers 4
|
||
Destroying the ISAM object..............sssssssccssssscceceseccccenesecsececsueecceceuces 4
|
||
|
||
UV PUG ASE: si seca lane vcuuwetded osanes des adynnsvauseuremettleauon secon Meraeloecate cic ccs 4
|
||
EXAMPIG PROGQHANTS « wosascredececslsoueiaavecadatheuade nee nckass ws aeer sea tee laieclbeccsseecaninl 5
|
||
Building a complete (dense) index.............cssssscccsssssccssssecececsesseueceneesees 5
|
||
Building a selective (sparse) index .............csssescccssecccsasscececcescccecceseeese 7
|
||
|
||
|
||
@ ISOM FUNCTIONS sve sestes seo choc 05 Secudeutce se buscdvscaudbasduvacbecvuaceacnnssd sata lice hedabeseae ek 9
|
||
|
||
GE MSN al MU MCHONS vases connec cu seiisenladeubuaycatedonsouauueedisoves ss cusSaceavlncsiea oo licetaas 9
|
||
|
||
Initialise an ISAM object (O_IS_INIT)...........ssssssecesscscccceecececccaueseccesecees 9
|
||
Destroy an ISAM object (O_IS DESTROY) ............ccccecsesessessssecececeesesees 10
|
||
Set the field definition (O_IS SET FIELDDEF) .............cccccsscccccesesseuseneees 10
|
||
Get the field definition (O_IS_GET_FIELDDEF) Sevestecdaied sida oeeRhuecdacvesieekeest 11
|
||
Set the key definition (O_IS SET _KEYDEPF) .............ccccsscsssseecccceeesecccuees 11
|
||
Get the key definition (O_IS_GET KEYDEP).............ccccsesssseccccesesessueecees 13
|
||
Put a value into a field (fom IS PUT CSRIEUD) 5 in sbavecsuedessscecdsscssevesceccetestacies 13
|
||
Get a value from a field (Oi IS GET GEIBUID). cccsisanseescecincsacoansovsseosantoeecs 13
|
||
Get the type of a field (0 _[S GET TYPE) au incnetecnrenecmestntsesetireecuoeroeieces 14
|
||
Get the address of the record buffer (0. IS GET_RBUP)..............cccccesees 14
|
||
Set the radix for number conversion (0 _| AS “SET RADIX) eiseaaiWwanstanvasettaxns 14
|
||
Set the text format for DOUBLES (O_ IS SET _ RADIX) daacaduece sbaveteveeauasaess 14
|
||
Add ancventry (O° 1S ADD) ssccs er cecccais sts Sons Cissvepwes doves sevavievec bevdeccecetovecess 14
|
||
Erase an entry (O IS ERASE).......ccsssccsssssccssscsssscsssssscersonecsececesssessens 14
|
||
Update an entry (O_IS_UPDATE)..............cccsseceeseccccsscceccercecseuececeeevecs 15
|
||
ata HIG FUMCNONS tise ssranii scp aes terebeeaiasdaxedtagd touted aleuumcvensuenteccteeccni dfs 15
|
||
Open a data file (O_IS DOPEN)..............ccccsssccssstceseconsesccenseneccessceeeesce 15
|
||
Flush data file buffers {0 IS DFELUSH) ic scctsacsckiv cesta dtsnsberscxevseovacdescs vases 15
|
||
Get sizeof data Tile (0! IS SIZE) 0. sis.ssccsstersssivacnssavenerenesassieccdeieedsc se 15
|
||
Read first data record (O_IS DFIRST)...........cccccssescsessescecstessecsccescesscs 15
|
||
Read next data record (O_ Is” DN EXT) idavedeves dense de deeocsedeeses csavadetectictis 16
|
||
Index file FUNCTIONS ...........ccccvesseccccccscccoretsnscseresstesceetaccsecesconsevscececeecceccee 16
|
||
Open: aniimdex: (OISIOREN) esl 5Ge inf Gscws toecsshasasinea sau celeeesadeneaaceeer ghee ecs 16
|
||
Close an index (O_ [SISO SEP cicaesterncaveastyt.temadlen eae basal ea, 16
|
||
Flush index file buffers (OCIS MBLUSH) 5 secs fea iee sicnadeavesseidebdeecnedeaathersoes 17
|
||
Set and get index flags (O_ Is” SIREAGS)ocacsetets cates docuisicdvsntsevese Sensesheons 17
|
||
Specify a filter method (O_ is PANELISTS estbiadt 2s coe bt os ssi hind vaneatee tocnaeannee: 17
|
||
Specify a duplicates method (om ISSIDUP) 23. oss tacdesrvceacs vavanand each catceeeivee 18
|
||
Bulle an: index: (OIS | IRONED). s.0 ies idcubeeexdssvacecanederecvesacadecaverlinetlccacesecs 18
|
||
Build an index from sorted data (OMS *IQBUIED) 2. cits seccovedecesssaciceeees ecees 18
|
||
Add an index entry (O_ ISBIA DD) Seth nett se create ante ata, «St ee 19
|
||
|
||
|
||
ISAM REFERENCE
|
||
|
||
|
||
i
|
||
|
||
|
||
Erase an index entry (O_IS_IERASE).............cssscsssscccevscorsvecesesecesscconees 19
|
||
Erase all index entries (O_ is wa IERASEALL) wok. ssscscccecatintees .cmresceterreeees 19
|
||
Get the number of index entries (OBISTIGOUNT) ciciiniressccnteecces ceStesceieees 20
|
||
Get size of an index file (O_IS | ISIZE) T Sawa case cdces salyseuieess se coe acbeascMeereeneeee 20
|
||
Find a record using an index (ORISAIFIND) 6.25.0 c.uscucsstih ce ee cee: 20
|
||
Read next indexed record (O_ is_ _INEXT) G Saline deen ve sovaatedaw asus eoeeeieteceeanete 21
|
||
Read previous indexed record (O4 ISSIBACK) & ia. cckscc codes cei acteatettectseoes 21
|
||
Read first indexed record (O_IS IFIRST) Le teet eats te cectatrauacat et ecetscaseeereones 21
|
||
Read last indexed record (O_ 1s ILAST) SCOOT TET ESTE EC CCLEER SET eee 21
|
||
Read current indexed record (O_ ISSIGURRENT) oa cs escscaseisdccccscenstesceuests« 21
|
||
|
||
|
||
CHAPTER 1
|
||
|
||
|
||
INTRODUCTION
|
||
|
||
|
||
SSS_—— SSS ee eae ee a ee ey
|
||
|
||
|
||
Overview
|
||
|
||
|
||
This manual describes the Indexed Sequential Access Method (ISAM) dynamic library for SIBO
|
||
machines (HC, $3, S3a and MC).
|
||
|
||
|
||
The ISAM library provides a powerful set of functions for rapid and efficient access of Database files
|
||
(DBF files). Such files consist of multiple records containing one or more structured fields. They can be
|
||
created, maintained and accessed from OPL programs and database applications on the HC, S3, S3a and
|
||
MC (see the Database Files chapter of the PLIB Reference manual).
|
||
|
||
|
||
The ISAM library uses B-tree index files to provide rapid and efficient indexed and sequential access to
|
||
DBF files. It is the most widely used form of index file simply because it is the most efficient general
|
||
method of accessing database files. In brief, B-tree index files minimise the number of disk accesses per
|
||
retrieval - manipulating data already in memory is much faster than accessing the disk - whilst ensuring a
|
||
well balanced index i.e. no one retrieval requires an inordinately large number of disk accesses (for
|
||
details see for example Structures, by Michael J.Folk and Bill Zoellick, published by Addison-Wesley
|
||
Publishing Company).
|
||
|
||
|
||
The ISAM library
|
||
The ISAM library functions provide the following:
|
||
|
||
|
||
= Fast record retrieval on a key. For example a DBF file can be searched for all records containing
|
||
the key "Smith".
|
||
|
||
|
||
= Very little degradation of the speed of retrieval as the number of records increases - even large
|
||
DBF files can be manipulated with relative ease.
|
||
|
||
|
||
= Sequential access to records that are ordered by the key (that is, next, back, first and last). For
|
||
example a record with key "Smalley" that precedes a current record with key "Smith" can be
|
||
rapidly found using the 0_I1s_1BACK function.
|
||
|
||
|
||
= The ability to see a selection of the records in the file (by constructing a selective or Sparse
|
||
index). For example all records containing the key "Smith" can be viewed.
|
||
|
||
|
||
= Access to record fields with automatic conversion from numbers to text (and vice-versa) if
|
||
required.
|
||
|
||
|
||
= The ability to make powerful key definitions that facilitate access ordered on combinations of up
|
||
to eight fields. For example all records with key fields "Smith" and "Slough" can be found with
|
||
"Smith" given the higher priority.
|
||
|
||
|
||
= The opening of up to 31 index files for a DBF file at any one time. For example, a DBF file
|
||
containing customer details can have two indexes, one for searching on customer occupation and
|
||
related details and the other for searching on customer location and related details.
|
||
|
||
|
||
= The adding, erasing, and updating of records with the appropriate updating of all associated
|
||
index files carried out automatically thus greatly facilitating the task of maintaining an index.
|
||
|
||
|
||
ISAM REFERENCE
|
||
|
||
|
||
SSS SS ee ee ee er Ty
|
||
DBF files
|
||
|
||
|
||
Fields
|
||
The following field types are available:
|
||
|
||
|
||
BYTE, UBYTE, WORD, UWORD, LONG, ULONG, DOUBLE, STRING.
|
||
|
||
|
||
However, if compatibility with existing OPL or PLIB DBF functions is required, the field types must be
|
||
restricted to:
|
||
|
||
|
||
WORD, LONG, DOUBLE, STRING
|
||
|
||
|
||
Number of records
|
||
The maximum number of records that can be handled by the PLIB DBF functions is 65534.
|
||
|
||
|
||
However, in ISAM the maximum is 2147483647.
|
||
|
||
|
||
SSS SSS a a a ee ea eee
|
||
B-tree index files
|
||
|
||
|
||
The ISAM library uses B-tree index files to provide much more powerful indexed and sequential access to
|
||
DBF files than would otherwise be possible.
|
||
|
||
|
||
In general B-tree index files should not be constructed or maintained on a Flash SSD. There is however
|
||
no reason not to read an index file from Flash SSD.
|
||
|
||
|
||
Block buffering
|
||
|
||
|
||
B-tree index files are block structured files having a block size of 1SAM_BLOCK_SIZE (512) bytes (in the
|
||
literature these blocks are sometimes referred to as pages - the terms are interchangeable).
|
||
|
||
|
||
Index files are read using a least-recently-used (LRU) block buffering scheme - blocks that are repeatedly
|
||
accessed are kept in memory (typically these are blocks near the root of the B-tree index). LRU block
|
||
buffering has such an impact on the performance of the B-tree algorithm that it is not reasonable to do
|
||
without it.
|
||
|
||
|
||
The ISAM library implements a fixed-size LRU block pool per process regardless of the number of open
|
||
index files. The size is fixed by calling the ISAM function o_1s_1Nn1T and specifying zero causes the
|
||
default of 20 blocks (10K bytes) to be used. If more than one ISAM object is created, the same block
|
||
pool is used; if a new size is specified in 0_!s_INIT, it is ignored if there are any index files still open.
|
||
The new size is used once all of the original index files are closed.
|
||
|
||
|
||
The LRU block pool is implemented in an external memory segment and does not, therefore, detract
|
||
from the process data segment. The segment name will be BLKSnnnn.BLK where nnnn is the process ID in
|
||
hex.
|
||
|
||
|
||
Key description
|
||
|
||
|
||
A key consists of up to eight fields with the constituent fields prioritised according to their order - the
|
||
first having the highest priority - subject to an overall maximum key size of 64 bytes. For example, keys
|
||
can be constructed from eight pous-e fields or, as a further example, the first 63 characters of a single
|
||
text field (stored as a leading byte count string).
|
||
|
||
|
||
The field types: BYTE, UBYTE, WORD, UWORD, LONG, ULONG and DOUBLE are compared numerically whereas
|
||
STRING fields are compared lexically using a given number of characters at a given offset from the start.
|
||
The comparison is subject to a programmer defined collation table.
|
||
|
||
|
||
The specification of the key is stored in the header of the B-tree index file and the structure is defined by
|
||
ISAM_KEYDEF:
|
||
|
||
|
||
1 INTRODUCTION
|
||
|
||
|
||
——___-__—————_-—j
|
||
|
||
|
||
typedef struct
|
||
|
||
|
||
€
|
||
|
||
WORD field; /* Field number for key */
|
||
|
||
UBYTE type; /* Type of field in key */
|
||
|
||
UBYTE flags; /* TSAM_FIELDFLAG_ASCEND or ISAM_FIELDFLAG_DESCEND */
|
||
UBYTE offset; /* Offset for start of comparison */
|
||
|
||
UBYTE len; /* Number of characters to compare */
|
||
|
||
WORD convert; /* Convert flag or collate table */
|
||
|
||
|
||
> ISAM_KEY_FIELD;
|
||
|
||
|
||
typedef struct
|
||
€
|
||
WORD nKeyFields; /* Number of key fields following... */
|
||
ISAM_KEY_FIELD keyField[ISAM_MAX_KEYFIELDS];
|
||
> ISAM_KEYDEF;
|
||
|
||
|
||
The convert field can take one of the following values:
|
||
|
||
|
||
ISAM_CONVERT_NOFOLD
|
||
ISAM_CONVERT_FOLD
|
||
ISAM_CONVERT_UPPER
|
||
ISAM_CONVERT_LOWER
|
||
|
||
|
||
which range from 0 - 3, or it can be the address of a user defined 256 byte collate table.
|
||
|
||
|
||
The priority for the field comparisons is the order they are specified with the first one having highest
|
||
priority.
|
||
Size of B-tree files
|
||
|
||
|
||
The size of a B-tree file depends upon the key size; therefore, it is good practice to keep the key as
|
||
compact as possible. A worst-case B-tree file is only 50% full so the maximum size of a B-tree file is
|
||
approximately:
|
||
|
||
|
||
2*(number of records)*(key length + 6)
|
||
|
||
|
||
For example, an index file with an eight byte key for a 1000 record file would require approximately
|
||
28K bytes of storage.
|
||
|
||
|
||
However, under typical use, a B-tree will be approximately 67% full and with the ISAM_MINIMISE flag set
|
||
(see the o_Is_IFLAGs function) this is increased to approximately 86%. Note also that B-trees built from
|
||
sorted data using 0_1S_1QBUILD or O_IS_IQADD are significantly more compact, typically 98% or over for
|
||
large numbers of records.
|
||
|
||
|
||
SSS SaaS Se eee eee eee eee ee
|
||
Interface to the ISAM library
|
||
|
||
|
||
The ISAM library is written using object-oriented programming (OOP). A class hierarchy has been
|
||
implemented such that OOP programmers can subclass the library to handle record files other than DBF
|
||
files and to use index files other than B-tree files.
|
||
|
||
|
||
Most of the ISAM functions can be called using conventional C (using the PLIB p_send OF p_entersend
|
||
functions) and the library is compatible with any combination of CLIB and PLIB libraries.
|
||
|
||
|
||
The rest of this section describes how to use the ISAM library from conventional C. In any program
|
||
using ISAM, the following steps will be involved:
|
||
|
||
|
||
= Load the ISAM DYL.
|
||
|
||
= Create an ISAM object
|
||
|
||
= Call the ISAM functions
|
||
= Destroy the ISAM object
|
||
|
||
|
||
Loading the DYL
|
||
|
||
|
||
The ISAM library is supplied as one file 1sam.pYL. Before any ISAM functions can be called, this must be
|
||
loaded. This will get a category handle which is needed for an ISAM object to be created. The loading
|
||
must be done in one of two ways depending on whether it has been linked into a multiple DYL file
|
||
(normally an executable) or not.
|
||
|
||
|
||
ISAM REFERENCE
|
||
|
||
|
||
® If not linked to a multiple DYL file, the PLIB function p_toad! ib must be used, for example:
|
||
HANDLE isamCat;
|
||
p_loadl ib("ISAM.DYL", &isamCat, TRUE);
|
||
|
||
|
||
will load 1sam.pyt from the current directory and write the category handle to isamCat.
|
||
|
||
|
||
= If linked to a multiple DYL, the two PLIB functions p_opent ib and p_loadfilelib must be used,
|
||
for example, if ISAM.DYL has been linked as the first DYL in an executable:
|
||
|
||
|
||
HANDLE isamCat;
|
||
VOID *chan;
|
||
|
||
|
||
chan=NULL;
|
||
if (p_openlib(&chan,DatCommandPtr)==0)
|
||
p_loadfilelib(chan,0,&isamCat, TRUE);
|
||
p_close(chan);
|
||
will load IsaM.bYL from the current executable and write the category handle to isamCat.
|
||
|
||
|
||
Creating an ISAM object
|
||
|
||
|
||
Once the ISAM library has been loaded, an ISAM object can be created. The class of the object required
|
||
is defined in isam.g as c_ptTbBF, which is an ISAM object using B-trees to access DBF files (ISAM may
|
||
be subclassed to produce objects accessing other types of file).
|
||
|
||
|
||
The PLIB functions p_newlibh or f_newl ibh can be used to create the object, for example:
|
||
|
||
|
||
VOID *pIsam;
|
||
|
||
|
||
plsam=p_newl ibh(isamCat,C_BIDBF);
|
||
|
||
|
||
Since there is always some initialisation to be carried out, it is generally more convenient to use the PLIB
|
||
function f_newlibhsend, for example:
|
||
|
||
|
||
VOID *plsam;
|
||
|
||
|
||
plsam=f_newl ibhsend( isamCat,C_BTDBF,O_IS_INIT,4096,0);
|
||
|
||
|
||
will create an ISAM object and then call the function 0_1s_INIT to initialise a record buffer of 4096 bytes
|
||
and the default index buffer (see the ISAM Functions chapter for the details of the 0_1s_1NIT function).
|
||
|
||
|
||
Calling ISAM functions
|
||
|
||
|
||
The ISAM functions are called by sending a message to an ISAM object using the PLIB functions p_send
|
||
OI p_entersend. The messages are defined in isam.g and take the form 0_1s_xxx where xxx is the name of
|
||
the function. Since all ISAM functions which can fail return zero for success or leave with a negative
|
||
error number, p_entersend can easily be used to catch the error or p_send can be used if the error handling
|
||
is done at a higher level. For example:
|
||
|
||
|
||
INT c;
|
||
|
||
|
||
if ((c=p_entersend4(plsam,O_IS_DOPEN, "TEST .DBF",P_FOPEN))<0)
|
||
p_printfC"Open data file failed with error %d",c);
|
||
|
||
|
||
catches the error whereas:
|
||
p_send4(plsam,O_1S DOPEN, "TEST .DBF",P_FOPEN);
|
||
passes any error to a higher level.
|
||
|
||
|
||
Destroying the ISAM object
|
||
|
||
|
||
The ISAM function 0_1s_DEsTROY will close the data file and all open index files and free all memory
|
||
used and does not leave or return errors. However, since data is buffered, it could fail. Carefully written
|
||
applications should therefore make sure that all data has been flushed and any error dealt with before
|
||
calling 0_I1s_DEsTROY. The functions 0_1s_DFLUSH and 0_IS_IFLUSH must be used.
|
||
|
||
|
||
Typical use
|
||
There are many ISAM functions available; here is a sample in the order that they are typically used:
|
||
|
||
|
||
= Create and initialise an ISAM object, use PLIB function f_newl ibhsend.
|
||
|
||
|
||
1 INTRODUCTION
|
||
|
||
|
||
SS eee
|
||
|
||
|
||
= Set the field definition for the data file, use 0_1S_SET_FIELDDEF.
|
||
|
||
= Open or create a data file, use 0_1S_DOPEN.
|
||
|
||
= Set the key definition for an index file, use 0_1S_SET_KEYDEF.
|
||
|
||
= Open or create one or more index files, use 0_1S_I0PEN.
|
||
|
||
= Build an index file, use 0_Is_IBUILD.
|
||
|
||
= Add or erase records, use 0_IS_ADD Or 0_IS_ERASE.
|
||
|
||
= Access ordered data with keys, use 0_IS_IFIND Or 0_IS_INEXT etc.
|
||
= Flush buffers, use o_1s_DFLUSH and 0_IS_IFLUSH.
|
||
|
||
= Destroy the ISAM object, use 0_1S_DESTROY.
|
||
|
||
|
||
Example programs
|
||
Note that the following two examples do not include complete error handling.
|
||
|
||
|
||
Building a complete (dense) index
|
||
|
||
|
||
This program creates a DBF file called ExaMPLE.oBF with one field of type Lone and appends 100 random
|
||
numbers to it. It then creates and builds an index file called Ex1.BTx of these numbers in ascending order.
|
||
|
||
|
||
Note that in the argument list for the setFieldDef and setkeyDef subroutines, the TopSpeed C compiler
|
||
understands the three dots to indicate an undefined number of arguments of undefined type.
|
||
|
||
|
||
#include <p_std.h>
|
||
#include <p_file.h>
|
||
#include <p_sys.h>
|
||
|
||
|
||
#include <epoc.h>
|
||
#include <isam.g>
|
||
|
||
|
||
GLREF_C TEXT *DatCommandPtr;
|
||
|
||
|
||
LOCAL_C HANDLE isamCat;
|
||
LOCAL_C VOID *plsam;
|
||
|
||
|
||
LOCAL_C VOID pErrCUBYTE *mess, INT err)
|
||
¢
|
||
UBYTE buf (E_MAX_ERROR_TEXT_SIZE];
|
||
|
||
|
||
p_errs(&buf [0] ,err);
|
||
|
||
P_printf("%s failed - %s",mess, &buf [0] );
|
||
p_getch();
|
||
|
||
p_exit( TRUE);
|
||
|
||
}
|
||
|
||
|
||
LOCAL_C VOID loadIsam(VOID)
|
||
€
|
||
INT c;
|
||
TEXT dylname[P_FNAMESIZE];
|
||
|
||
|
||
p_fparse("ISAM.DYL", &dylname[0} ,NULL);
|
||
|
||
if ((c=p_loadl ib(&dylname [0] , ,&isamCat, TRUE) )<0)
|
||
pErr("Load ISAM.DYL",c);
|
||
|
||
}
|
||
|
||
|
||
LOCAL_C VOID createlsam(VOID)
|
||
{
|
||
|
||
|
||
plsam=f_newl ibhsend(isamCat,C_BTDBF,O_IS_INIT,4096,0);
|
||
>
|
||
|
||
|
||
LOCAL_C VOID setFieldDef(INT nFields,...)
|
||
{
|
||
|
||
|
||
p_send4(plIsam,0_IS_SET_FIELDDEF,nFields,&nFields+1);
|
||
|
||
|
||
_- Oro
|
||
|
||
|
||
ISAM REFERENCE
|
||
|
||
|
||
LOCAL_C VOID setKeyDef(INT nKeyFields,...)
|
||
€
|
||
|
||
|
||
p_send4(pIsam,O_IS_SET_KEYDEF ,nKeyFields, &nKeyFields+1);
|
||
>
|
||
|
||
|
||
LOCAL_C VOID openData(TEXT *name,UINT mode)
|
||
€
|
||
INT c;
|
||
|
||
|
||
if ((c=p_entersend4(pIsam,0_IS_DOPEN,name,mode))<0)
|
||
pErr("Open data file",c);
|
||
>
|
||
|
||
|
||
LOCAL_C INT openIndex(TEXT *name,UINT mode)
|
||
{
|
||
INT ¢;
|
||
|
||
|
||
if ((c=p_entersend4(pIsam,O_IS_IOPEN,name,mode) )<0)
|
||
pErr("Open index file",c);
|
||
|
||
return(c);
|
||
|
||
>
|
||
|
||
|
||
GLDEF_C VOID main(VOID)
|
||
{
|
||
ULONG seed;
|
||
LONG lL;
|
||
INT nRecords;
|
||
INT indexid;
|
||
INT i;
|
||
|
||
|
||
loadIsam();
|
||
|
||
createlIsam();
|
||
setFieldDef(1,ISAM_FIELDTYPE_LONG);
|
||
|
||
openData( "EXAMPLE .DBF",P_FREPLACE |P_FUPDATE);
|
||
seed=0L;
|
||
|
||
nRecords=100;
|
||
|
||
|
||
p_printf("Generating %u records",nRecords);
|
||
for (i=0;i<nRecords; i++)
|
||
€
|
||
l=p_randl (&seed);
|
||
p_sendS(pIsam,O_IS_PUT_FIELD,0,&t, ISAM_FIELDTYPE_LONG);
|
||
p_send2(pIsam,0_IS_ADD);
|
||
>
|
||
|
||
|
||
p_printf ("Building index");
|
||
|
||
setKeyDef(1,0, 1SAM_FIELDTYPE_LONG, ISAM_FIELDFLAG_ASCEND);
|
||
index Id=open!I ndex("EX1.BTX", P_FREPLACE |P_FUPDATE);
|
||
p_send3(pIsam,O_IS_IBUILD, indexId);
|
||
|
||
|
||
p_printf("Sorted data:");
|
||
p_send3(pIsam,O_IS_IFIRST, indexId);
|
||
for (i=0;i<nRecords; i++)
|
||
€
|
||
p_send5(pisam,O_IS GET_FIELD,0,&L,1SAM_FIELDTYPE_LONG);
|
||
p_printf "4d", 1);
|
||
p_send3(pIsam,OQ_IS_INEXT, indexId);
|
||
>
|
||
p_send2(pIsam,O_IS_ DESTROY);
|
||
p_getch();
|
||
p_exit(0);
|
||
>
|
||
|
||
|
||
1 INTRODUCTION
|
||
SS See
|
||
|
||
|
||
Building a selective (sparse) index
|
||
|
||
|
||
The following routine opens the DBF file called ExaMPLE DBF which was created in the previous example.
|
||
It then creates and builds an index file called ex2.8Tx of those numbers which are divisible by ten in
|
||
ascending order.
|
||
|
||
|
||
LOCAL_C VOID divi0¢VOID)
|
||
{
|
||
LONG Ll;
|
||
INT indexId;
|
||
INT eof;
|
||
|
||
|
||
createlsam();
|
||
openData("EXAMPLE.DBF",P_FOPEN);
|
||
p_printf("Build index of numbers divisible by 10");
|
||
setKeyDef(1,0, ISAM_FIELDTYPE_LONG, ISAM_FIELDFLAG_ ASCEND);
|
||
indexId=openIndex("EX2.BTX", P_FREPLACE|P_FUPDATE);
|
||
eof=p_send2(pIsam,O_IS_DFIRST);
|
||
while (eof==FALSE)
|
||
€
|
||
p_send5(pIsam,O_IS_GET_FIELD,0,&1,1SAM_FIELDTYPE_LONG);
|
||
if (¢1%10)==0)
|
||
p_send3(pisam,O_IS_IADD, indexId);
|
||
eof=p_send2(plIsam,0_IS_DNEXT);
|
||
>
|
||
p_printf("Sorted data:");
|
||
eof=p_send3(plsam,0_IS_IFIRST, indexId);
|
||
while (eof==FALSE)
|
||
€
|
||
p_send5(pIsam,0_IS_GET_FIELD,0,&l,ISAM_FIELDTYPE_LONG);
|
||
p_printt¢("%ld",L);
|
||
eof=p_send3(pIsam,O_IS_INEXT, indexId);
|
||
>
|
||
p_send2(pIsam,0_IS_DESTROY);
|
||
>
|
||
|
||
|
||
CHAPTER 2
|
||
|
||
|
||
ISAM FUNCTIONS
|
||
|
||
|
||
ISAM functions are called using either p_send or p_entersend to the ISAM object created with (for
|
||
example) the PLIB function p_newlibh.
|
||
|
||
|
||
Most functions which can fail will return zero or a positive number for success or leave with a negative
|
||
error number, giving the caller the option of catching the error using p_entersend or leaving the error
|
||
handling to a higher level by calling p_send.
|
||
|
||
|
||
Functions that can generate E_FILE_EoF (for example, 0_1S_INEXT), will return this negative error rather
|
||
than leave.
|
||
|
||
|
||
All functions can be called from standard C but o_1s_IFILTER and 0_1S_IpuP are only applicable when
|
||
using object oriented programming (OOP).
|
||
|
||
|
||
There are three types of functions:
|
||
= General functions
|
||
s Data file functions
|
||
= Index file functions
|
||
|
||
|
||
SSS a a a a ae eee
|
||
General functions
|
||
This section describes functions to perform the following:
|
||
|
||
= Initialise and destroy an ISAM object.
|
||
|
||
= Define and read the field definition for a data file.
|
||
|
||
= Define and read the key definition for an index file.
|
||
|
||
= Read and write data to the record buffer.
|
||
|
||
® Set the format for conversion of numbers to text.
|
||
|
||
|
||
= Add, erase and update records in the data file with appropriate updates to all associated index
|
||
files.
|
||
|
||
|
||
SINT eee
|
||
|
||
|
||
VOID p_send4(VOID *pIsam, O_IS_INIT, INT rBufSize, INT iBufBlocks);
|
||
|
||
|
||
Initialise the ISAM object with a record buffer of length raufLen and an index buffer in an external
|
||
memory segment with isufBlocks blocks (a block is 1SAM_BLOCK_sIZE (512) bytes long). If iBufBlocks is
|
||
zero, the default number of blocks, 8L_DEFAULT BLOCKS (20) is used.
|
||
|
||
|
||
Performs the following initialisation:
|
||
= Allocates rbufLen bytes for the record buffer.
|
||
= Sets the field definition to the default of 32 string fields.
|
||
= Sets the key definition to the default of the first eight characters of the first string field.
|
||
|
||
|
||
ISAM REFERENCE
|
||
TO
|
||
|
||
|
||
= Sets the radix for number conversion to the default of 10.
|
||
|
||
|
||
= Sets the format for doubles converted to text to the default of p_pTOB_GENERAL with a width of 20
|
||
and a decimal point character of '.'.
|
||
|
||
|
||
rBufLen sets the size of the record buffer and must be in the range 1SAM_MIN_RECORD_BUFFER (512) to
|
||
ISAM_MAX_RECORD_BUFFER (16384). If it is not, p_panic will be called. The record buffer is used to read
|
||
records from the data file and must be at least as long as the longest record to be read. A length of
|
||
ISAM_MAX_RECORD_LENGTH+2 (4096) is guaranteed to be large enough for all records. When an index file is
|
||
being built, data is read from the file in blocks as large as the record buffer; the bigger the buffer, the
|
||
faster the build will be.
|
||
|
||
|
||
iBufBlocks must be less than or equal to ISAM_MAX_SEG_BLOCKS (1024). Any value greater than this will
|
||
cause p_panic to be called.
|
||
|
||
|
||
Leaves with E_GEN_NOMEMoRY if there is insufficient memory to initialise everything.
|
||
|
||
|
||
Typically, this function would be called when the object is created and can be combined into one call
|
||
using (for example) the PLIB function f_newl ibhsend:
|
||
|
||
|
||
plsam=f_newl ibhsend( isamCatNum, C_BTDBF,O_IS_INIT,4096,0);
|
||
|
||
|
||
This will create an ISAM object and initialise it with a record buffer of 4096 bytes and the default
|
||
number of index buffer blocks.
|
||
|
||
|
||
Creating an ISAM object is explained further in the Introduction chapter.
|
||
|
||
|
||
VOID p_send2(VOID *pIsam, O_IS_DESTROY);
|
||
Destroy the ISAM object. The data file and all open index files are closed and all memory is freed.
|
||
|
||
|
||
Note that if more than one ISAM object is created in the same process, the external segment memory
|
||
used for buffering the indexes is shared and will only be freed when the last ISAM object is destroyed.
|
||
|
||
|
||
O_IS_DESTROY can fail due to one or more write operations but will always succeed in destroying the
|
||
ISAM object (freeing all memory used). No error is ever returned and the function will not leave.
|
||
|
||
|
||
Carefully written applications avoid this problem by using o_1s_pFLUSH and 0_IS_IFLUSH, to flush all
|
||
buffered data (and taking appropriate action if this fails) before destroying the object without risk of
|
||
failure.
|
||
|
||
|
||
INT p_send4(VOID *pisam, O_IS_SET_FIELDDEF, INT nFields, VOID *pArgs);
|
||
|
||
|
||
Set the field definition to contain nFietds with types in the list at pargs. This field definition will be used
|
||
when a data file is created.
|
||
|
||
|
||
Alternatively pargs can point to an ISAM_FIELDDEF structure containing the field definition while nfields is
|
||
passed as zero.
|
||
|
||
|
||
The ISAM_FIELDDEF structure is defined in isam.g as:
|
||
|
||
|
||
typedef struct
|
||
€
|
||
WORD nFields;
|
||
UBYTE type[ISAM_MAX_DEFINED_FIELDS] ;
|
||
> ISAM_FIELDDEF
|
||
|
||
|
||
For example:
|
||
|
||
|
||
LOCAL_C VOID defineFields(VOID)
|
||
€
|
||
ISAM_FIELDDEF fieldDef;
|
||
|
||
|
||
fieldDef .nFields=2;
|
||
fieldDef.type[0]=ISAM_FIELDTYPE_WORD;
|
||
fieldDef.typel1]=ISAM_FIELDTYPE_LONG;
|
||
p_send4(pisam,0_IS_SET_FIELDDEF,0,&fieldDef);
|
||
>
|
||
|
||
|
||
SS ee SS ee
|
||
10
|
||
|
||
|
||
2 ISAM FUNCTIONS
|
||
—_ See
|
||
|
||
|
||
is equivalent to:
|
||
|
||
|
||
LOCAL_C VOID setFieldDef(INT nFields,...)
|
||
€
|
||
|
||
|
||
p_send4(pIsam,O_IS_SET_FIELDDEF ,nFields,&nFields+1));
|
||
>
|
||
|
||
|
||
LOCAL_C VOID defineFields(VOID)
|
||
€
|
||
|
||
|
||
setFieldDef(2,1SAM_FIELDTYPE_WORD, ISAM_FIELDTYPE_LONG);
|
||
>
|
||
|
||
|
||
and both define the field structure of the data file to be two fields, the first a woRD field, the second a
|
||
LONG.
|
||
|
||
|
||
The maximum number of fields that can be specified is 1SAM_MAX_DEFINED_FIELDS (32) and each field type
|
||
must be one of the following types:
|
||
|
||
|
||
ISAM_FIELDTYPE_BYTE for a signed Byte field.
|
||
ISAM_FIELDTYPE_UBYTE for an unsigned usyTE field.
|
||
ISAM_FIELDTYPE_WORD for a signed worp field.
|
||
ISAM_FIELDTYPE_UWORD for an unsigned uworp field.
|
||
ISAM_FIELDTYPE_LONG for a signed tone field.
|
||
ISAM_FIELDTYPE_ULONG for an unsigned uLonc field.
|
||
ISAM_FIELDTYPE_DOUBLE for a DOUBLE field.
|
||
ISAM_FIELDTYPE_STRING for a leading byte count string field.
|
||
|
||
|
||
More fields than are specified in the field definition can be accessed, but they are always assumed to be
|
||
of type ISAM_FIELDTYPE_STRING.
|
||
|
||
|
||
Note that this specifies the field types to be used in the actual data file; however, conversion to and from
|
||
I1SAM_FIELDTYPE_STRING is performed automatically by the functions 0_1S_PUT_FIELD and O_IS_GET_FIELD.
|
||
See the descriptions of these two functions for further details.
|
||
|
||
|
||
Similarly, index files may specify field types which differ from those specified for data file fields. The
|
||
field types will be converted automatically, provided the conversion is to or from ISAM_FIELDTYPE_STRING.
|
||
See the description of 0_1$_SET_KEYDEF.
|
||
|
||
|
||
Retums zero if successful or leaves with &_GEN_ARG if the given field definition is invalid.
|
||
|
||
|
||
ISAM_FIELDDEF *p_send2(VOID *pIsam, O_IS GET_FIELDDEF);
|
||
|
||
|
||
Get the current field definition, returning a pointer to an 1SAM_FIELDDEF structure.
|
||
|
||
|
||
INT p_send4(VOID *plsam, O_IS_SET_KEYDEF, INT nKeyFields, VOID *pArgs);
|
||
|
||
|
||
Set the key definition (to be used for the next created index file) to contain nkeyFields with the
|
||
parameters for each key field specified in the list at pArgs. This key definition will be used for the next
|
||
index file created.
|
||
|
||
|
||
Alternatively pArgs can point to an ISAM_KEYDEF structure containing the key definition while nFields is
|
||
passed as zero.
|
||
|
||
|
||
The 1SAM_KEYDEF structure is defined in isam.g by:
|
||
|
||
|
||
11
|
||
|
||
|
||
ISAM REFERENCE
|
||
|
||
|
||
typedef struct
|
||
|
||
|
||
€
|
||
|
||
WORD field; /* Field number for key */
|
||
|
||
UBYTE type; /* Type of field in key */
|
||
|
||
UBYTE flags; /* ISAM_FIELDFLAG_ASCEND or ISAM_FIELDTYPE_DESCEND */
|
||
UBYTE offset; /* Offset for string comparison */
|
||
|
||
UBYTE len: /* Length for string comparison */
|
||
|
||
WORD convert; /* Convert flag or collate table */
|
||
|
||
|
||
} ISAM_KEY_FIELD;
|
||
|
||
|
||
typedef struct
|
||
|
||
|
||
€
|
||
|
||
WORD nkKeyFields;
|
||
|
||
ISAM_KEY_FIELD keyField[ISAM_MAX_KEYFIELDS];
|
||
} ISAM_KEYDEF;
|
||
|
||
|
||
For example:
|
||
|
||
|
||
LOCAL_C VOID defineKey(VOID)
|
||
|
||
|
||
€
|
||
ISAM_KEYDEF keyDef;
|
||
|
||
|
||
keyDef .nKeyFields=2;
|
||
|
||
keyDef .keyField[0] .field=3;
|
||
|
||
keyDef .keyField[0] . type=ISAM_FIELDTYPE_STRING;
|
||
keyDef .keyField[0] . flags=1SAM_FIELDFLAG_ASCEND;
|
||
keyDef .keyField[0] .offset=0;
|
||
|
||
keyDef .keyField[0] . Len=8;
|
||
|
||
keyDef .keyField[0] .convert=1SAM_CONVERT_NOFOLD;
|
||
keyDef .keyField[1] .field=1;
|
||
keyDef.keyField[1] . type=ISAM_FIELDTYPE_LONG;
|
||
keyDef .keyField[1] .flags=1SAM_FIELDFLAG_ASCEND;
|
||
p_send4(pIsam,O_IS_SET_KEYDEF ,0,&keyDef);
|
||
|
||
3
|
||
|
||
|
||
is equivalent to:
|
||
|
||
|
||
LOCAL_C VOID setKeyDefCINT nKeyFields,...)
|
||
|
||
|
||
{€
|
||
|
||
|
||
p_send4(pIsam,0_IS_SET_KEYDEF ,nKeyFields, &nKeyFields+1);
|
||
>
|
||
|
||
|
||
LOCAL_C VOID defineKey(VOID)
|
||
|
||
|
||
€
|
||
|
||
|
||
setKeyDef(2,3, 1SAM_FIELOTYPE_STRING, ISAM_FIELDFLAG_ASCEND,0,8,1SAM_CONVERT_NOFOLD, 1,
|
||
|
||
|
||
TSAM_FIELDTYPE_LONG, ISAM_FIELDTYPE_ASCEND);
|
||
|
||
|
||
>
|
||
|
||
|
||
The fields: offset, len and convert must only be supplied if the type is 1SAM_FIELDTYPE_STRING.
|
||
|
||
|
||
The key definition is subject to the following validation:
|
||
|
||
|
||
The number of key fields must be in the range 1 to 1SAM_MAX_KEYFIELDS (8).
|
||
The field number for each key field must be in the range 0 to 1SAM_MAX_RECORD_FIELDS (4094).
|
||
|
||
|
||
The type of each key field (the type actually stored in the index file) must be valid and must be
|
||
either the same as the type specified in the current field definition or be a conversion from or to
|
||
an ISAM_FIELDTYPE_STRING,
|
||
|
||
|
||
The length of a key produced by this definition must be no greater than ISAM_MAX_KEY_LENGTH
|
||
(64).
|
||
|
||
|
||
Returns the length of a key resulting from this definition if successful or leaves with €_GEN_aRG if the key
|
||
definition is invalid.
|
||
|
||
|
||
12
|
||
|
||
|
||
2 ISAM FUNCTIONS
|
||
|
||
|
||
ISAM_KEYDEF “p_send3(VOID *plsam, O_IS_GET_KEYDEF, INT indexId);
|
||
|
||
|
||
Get the key definition for the index indexid, returning a pointer to an 1SAM_KEYDEF structure. The value
|
||
indexid is returned when an index is opened (see 0_1S_I10PEN below).
|
||
|
||
|
||
INT p_send5(VOID “plsam, O_IS_PUT_FIELD, INT field, VOID *pData, INT type);
|
||
|
||
|
||
Put the data at address pata into the record buffer at field number given by field. If any preceding fields
|
||
have not been set, they will be automatically set to contain zeros.
|
||
|
||
|
||
The type parameter is the type of the data pointed to by pdata. If type is ISAM_FIELDTYPE_STRING, the data
|
||
at pData must be in the form of a leading byte string.
|
||
|
||
|
||
If type is different from that specified for field number field (as reported by 0_IS_GET_FIELDDEF) and
|
||
either of these two types is ISAM_FIELDTYPE_STRING, the field data will be converted between numeric and
|
||
string types as necessary according to the current radix (as set by 0_IS_SET_RADIX).
|
||
|
||
|
||
For example, if the field definition is set to 32 strings and the current radix is decimal (the defaults), the
|
||
following code will put the value "31" into field zero in the record buffer as a leading byte count string.
|
||
|
||
|
||
WORD value;
|
||
|
||
|
||
value=31;
|
||
p_send5(pIsam,0_IS_PUT_FIELD,0,&value, ISAM_FIELDTYPE_WORD);
|
||
|
||
|
||
Returns zero if successful or leaves with one of the following negative error numbers:
|
||
|
||
|
||
E_FILE_RECORD the assignment to the field would make the total record length greater than
|
||
ISAM_MAX_RECORD_LENGTH (4094) or the size of the buffer specified in 0_1S_INIT.
|
||
|
||
E_GEN_ARG the type conversion required is not to or from ISAM_FIELDTYPE_STRING.
|
||
|
||
E_GEN_FAIL the type conversion failed because the string could not be recognised as a
|
||
number.
|
||
|
||
E_GEN_OVER the type conversion failed because the number is out of range.
|
||
|
||
|
||
INT p_sendS(VOID *pisam, O_IS_GET FIELD, INT field, VOID *pData, INT t e@);
|
||
- eT YP!
|
||
|
||
|
||
Get the data from field in the record buffer into the buffer at poata.
|
||
|
||
|
||
The type parameter is the type of the data required at ppata. If type is ISAM_FIELDTYPE_STRING, the data
|
||
will be written to poata in the form of a leading byte string.
|
||
|
||
|
||
If type is different from that specified for field number field (as reported by 0_IS_GET_FIELDDEF) and
|
||
either of these two types is 1SAM_FIELDTYPE_STRING, the field data will be converted between numeric and
|
||
string types as necessary according to the current radix (as set by 0_IS_SET_RADIX).
|
||
|
||
|
||
For example, if the field definition is set to 32 strings (the default), the following code will get the value
|
||
from field 10 in the record buffer and convert it to a LONG.
|
||
|
||
|
||
LONG lValue;
|
||
|
||
|
||
p_send5(pIsam,O_IS_GET_FIELD,10,&lValue, ISAM_FIELDTYPE_LONG);
|
||
|
||
|
||
Retums zero if successful or leaves with one of the following negative error numbers:
|
||
|
||
|
||
E_GEN_ARG the type conversion required is not to or from 1SAM_FIELDTYPE_STRING.
|
||
|
||
E_GEN_FAIL the type conversion failed because the string could not be recognised as a
|
||
number.
|
||
|
||
£_GEN_OVER the type conversion failed because the number is out of range.
|
||
|
||
|
||
13
|
||
|
||
|
||
INT p_send3(VOID *pIsam, O_IS_GET_TYPE, INT field);
|
||
Return the actual type of field in the data file.
|
||
|
||
|
||
This provides the same functionality as calling 0_1s_GET_FIELDDEF and then inspecting the required field.
|
||
It is included for convenience.
|
||
|
||
|
||
ISAM_RECORDBUF *p_send2(VOID *plsam, 0_IS_GET_RBUF);
|
||
|
||
|
||
Get the actual address of the record buffer allocated in 0_1s_1NIT, returning a pointer to an ISAM_RECORDBUF
|
||
structure. This struct is not defined in any header file - users of this method function should declare the
|
||
struct in their own code as:
|
||
|
||
|
||
typedef struct
|
||
{
|
||
WORD Len; /* Length of record */
|
||
UBYTE data[2]; /* len bytes of data... */
|
||
|
||
|
||
} ISAM_RECORDBUF;
|
||
|
||
|
||
This function is provided to allow direct access to the current record in the buffer, if needed. Typically,
|
||
it will be more convenient to use 0_IS_GET_FIELD and ©_IS_PUT_FIELD.
|
||
|
||
|
||
VOID p_send3(VOID “pIsam, C_IS_SET_RADIX, INT radix);
|
||
Set the base radix for number conversion to radix. By default this is set to 10.
|
||
|
||
|
||
VOID p_send3(VOID *pIsam, O_IS_SET_DFORMAT, P_DTOB *dFormat);
|
||
Set the format for pousLEs converted to text to be that specified by dFormat.
|
||
|
||
|
||
INT p_send2(VOID *pIsam, O_IS_ADD);
|
||
|
||
|
||
Add the record currently in the record buffer to the data file and attempt to add entries to all open index
|
||
files which do not have 1SAM_FLAG_MANUAL set (see 0_IS_IFLAGS).
|
||
|
||
|
||
One or more of the index files may refuse to actually add the entry for any of the following reasons:
|
||
= A filter method returns FALSE (see 0_1S_IFILTER).
|
||
|
||
|
||
= The entry matches an existing entry in the index and a duplicate method returns FALSE (see
|
||
O_IS_IDUP).
|
||
|
||
|
||
" The entry matches an existing entry in the index and I1SAM_FLAG_ALLOWDUP is not set (see
|
||
O_IS_IFLAGS).
|
||
|
||
|
||
but no error will be given.
|
||
|
||
|
||
Returns FALSE if successful or leaves with one of the negative errors returned by the PLIB function
|
||
p_write.
|
||
|
||
|
||
s
|
||
|
||
|
||
INT p_send2(VOID *pIsam, O_IS_ ERASE);
|
||
|
||
|
||
Erase the record currently in the record buffer from the data file and from all open index files which do
|
||
not have ISAM_FLAG_MANUAL set (see O_IS_IFLAGS).
|
||
|
||
|
||
The record buffer must contain a record read by a successful call to one of the following functions:
|
||
O_IS_DFIRST, O_1S_DNEXT, O_IS_IFIND, O_IS_INEXT, O_IS_IBACK, O_IS_IFIRST, O_IS_ILAST, O_IS_ICURRENT.
|
||
|
||
|
||
SSS SS Se ee eee
|
||
14
|
||
|
||
|
||
2 ISAM FUNCTIONS
|
||
—. EES
|
||
|
||
|
||
Retums FALSE if successful or €_FILE_EoF if the current record is end-of-file or leaves with one of the
|
||
negative errors retumed by the PLIB function p write.
|
||
|
||
|
||
INT p_send2(VOID *pisam, O_IS_ UPDATE);
|
||
|
||
|
||
Replace the record which was read by a successful call to one of the following functions: 0_1s_DFIRST,
|
||
O_IS_DNEXT, O_IS_IFIND, O_IS_INEXT, O_IS_IBACK, O_IS_IFIRST, O_IS_ILAST, O_IS_ICURRENT, by the new
|
||
contents of the record buffer in the data file and all open index files which do not have 1SAM_FLAG_MANUAL
|
||
set (see O_IS_IFLAGS).
|
||
|
||
|
||
Returns FALSE if successful or €_FILE_Eor if the current record is end-of-file or leaves with one of the
|
||
negative errors returned by the PLIB function p_write.
|
||
|
||
|
||
SS ea en ee eS]
|
||
Data file functions
|
||
This section describes functions to perform the following:
|
||
|
||
|
||
= Open or create a data file.
|
||
|
||
= Flush the buffers used for the data file.
|
||
= Get the current size of the data file.
|
||
|
||
= Sequentially read the data file.
|
||
|
||
|
||
INT p_send4(VOID *plsam, O_IS_DOPEN, TEXT *name, UINT mode);
|
||
Open a data file specified by the zero terminated file specification name.
|
||
|
||
|
||
The mode parameter is the same as for the PLIB function p_open(P_FSTREAM). If P_FCREATE OF P_FREPLACE is
|
||
specified, the data file is created with the current field definition (see 0_1S_SET_FIELDDEF). If an existing
|
||
data file is opened, the current field definition is set to that of the file opened.
|
||
|
||
|
||
Only one data file can be opened per ISAM object. If an attempt is made to open more than one, P_panic
|
||
will be called.
|
||
|
||
|
||
Retums zero if successful or leaves with one of the negative errors returned by p_open(P_FSTREAM).
|
||
|
||
|
||
INT p_send2(VOID *pisam, O_IS_DFLUSH);
|
||
Flush al! data written to the data file and write the file's modification date.
|
||
|
||
|
||
Returns zero if successful or leaves with one of the negative errors returned by the PLIB function
|
||
P_write.
|
||
|
||
|
||
See the PLIB function p_iow(P_FFLUSK) for further details on flushing.
|
||
|
||
|
||
Sra
|
||
|
||
|
||
INT p_send3(VOID *pIsam, O_IS DSIZE, LONG *size);
|
||
Write the size (in bytes) of the open data file to size.
|
||
|
||
|
||
Retums zero if successful or leaves with one of the negative errors returned by the PLIB function p_seek.
|
||
|
||
|
||
INT p_send2(VOID *plsam, O_IS_DFIRST);
|
||
Read the first record in the data file into the record buffer.
|
||
|
||
|
||
15
|
||
|
||
|
||
ISAM REFERENCE
|
||
|
||
|
||
Returns zero if successful or £_FILE_EoF if there are no records or leaves with one of the negative errors
|
||
returned by the PLIB function p_read.
|
||
|
||
|
||
Read the next record in the data file into the record buffer.
|
||
|
||
|
||
Should be used in conjunctions with o_1s_DFIRST to provide sequential access to the data file records in
|
||
the order they are stored without the need for an index file.
|
||
|
||
|
||
Returns zero if successful or E_FILE_EOF if the current record is the last record in the file or leaves with
|
||
one of the negative errors returned by the PLIB function p_read.
|
||
|
||
|
||
a a a a a a ed
|
||
Index file functions
|
||
This section describes functions to perform the following:
|
||
= Open or create an index file.
|
||
= Flush the buffers used for the index file.
|
||
® Build a complete (dense) index file from a sorted or unsorted data file.
|
||
= Build an index for a selection of records (sparse) by one of two mechanisms:
|
||
1) Specify a function which will be called during the building of the index (OOP only).
|
||
2) Add and erase entries from the index directly.
|
||
= Get the size and number of entries in an index.
|
||
= Provide fast indexed access to records in the data file from a given index and match key.
|
||
= Provide sequential access to records in the data file in the order defined by an index.
|
||
|
||
|
||
The function 0_I1S_IOPEN returns an indexId which is then passed as a parameter to other index functions.
|
||
|
||
|
||
INT p_send4(VOID *pIsam, O_IS_IOPEN, TEXT “name, UINT mode);
|
||
|
||
|
||
Open an index file specified by the zero terminated file specification name.
|
||
|
||
|
||
The mode parameter has the same meaning as for the PLIB function p_opencP_FSTREAM), except that
|
||
P_FAPPEND should not be specified. If P_FCREATE or P_FREPLACE is specified, the data file is created with the
|
||
current key definition (see 0_1S_SET_KEYDEF above). If an existing index file is opened (with a mode of
|
||
P_FOPEN, optionally ored with P_FUPDATE) its key definition can be obtaining using 0_1s_GET_KEYDEF.
|
||
|
||
|
||
Note that the key definition is stored in the index file itself and cannot be changed once the index file has
|
||
been created.
|
||
|
||
|
||
Retums an indexid from 1 to 31 if successful or leaves with one of the negative errors returned by
|
||
P_open(P_FSTREAM) Or E_GEN_FAIL if an attempt is made to open more than 31 indexes.
|
||
|
||
|
||
INT p_send3(VOID *plsam, O_IS_ICLOSE, INT indexId);
|
||
Close the index specified by indexid and return zero.
|
||
|
||
|
||
0_IS_ICLOSE can fail due to one or more write operations but will always succeed in closing the channel
|
||
(the indexid should not be used subsequently). No error is ever returned and the function will not leave.
|
||
|
||
|
||
Carefully written applications avoid this problem by using 0_!s_IFLUSH to flush all buffered data (and
|
||
taking appropriate action it this fails) before closing the channel without risk of failure.
|
||
|
||
|
||
16
|
||
|
||
|
||
2 ISAM FUNCTIONS
|
||
|
||
|
||
INT p_send3(VOID *plsam, O_IS_IFLUSH, INT indexld);
|
||
Flush all buffers used for index indextd and write the index file's modification date.
|
||
|
||
|
||
Returns zero if successful or leaves with one of the negative errors returned by the PLIB function
|
||
p_write.
|
||
|
||
|
||
See the PLIB function p_iow¢P_FFLUSH) for further details on flushing.
|
||
|
||
|
||
INT p_sendS(VOID *plsam, O_IS_IFLAGS, INT indexId, INT mask, INT value);
|
||
|
||
|
||
Modify the flag bits given by mask to the state (set or clear) given by value in index indextd.
|
||
Returns the new value of the flags; passing mask as Zero just reads the current flag settings.
|
||
The flags available are:
|
||
|
||
|
||
ISAM_FLAG_ALLOWDUP allow duplicate entries in the index. If this flag is set, duplicate entries will be
|
||
added when the index is built using 0_I1s_BUILD or 0_IS_QBUILD or when entries
|
||
are added with 0_1S_ADD or 0_IS_IADD.
|
||
|
||
|
||
1SAM_FLAG_MANUAL the index will not be automatically updated by calls to the general functions
|
||
0_1S_ADD, O_IS_ERASE or 0_IS_UPDATE. The index functions 0_1S_1ADD and
|
||
O_IS_IERASE must be used to add or erase entries explicitly; typically, it is used
|
||
to build an index to a selection of records only (a sparse index).
|
||
|
||
|
||
ISAM_FLAG_MINIMIZE Minimize the storage requirement of the index by altering the way keys are
|
||
inserted. The index is not compressed when the flag is set but any future
|
||
additions to the index are inserted using the minimize algorithm. With the flag
|
||
set, the actual insertion will be slower but the index produced will be smaller
|
||
and hence a greater portion of it can be held in internal buffers which means
|
||
finding the insertion point is faster.
|
||
|
||
|
||
Whether it is worth setting this flag is clearly application dependent and the
|
||
best way to decide is by trial and error.
|
||
|
||
|
||
The flag can be set or cleared at any stage without harming the index structure.
|
||
By default, all the above flags are not set.
|
||
|
||
|
||
Note that these settings are stored in the index file but can be modified at any time. For example, if
|
||
1SAM_FLAG_ALLOWDUP is not set, it does not necessarily mean there are no duplicate entries in the index. It
|
||
just means that the current intention is to not add duplicates.
|
||
|
||
|
||
Example
|
||
|
||
|
||
testId=p_send4(pIsam,0_IS_IOPEN, "TEST. INX", P_FREPLACE|P_FUPDATE);
|
||
p_send5(pIsam,O_IS_IFLAGS, testId, ISAM_FLAG_ALLOWDUP, ISAM_FLAG_ALLOWDUP);
|
||
|
||
|
||
creates an index file in which duplicate entries will be allowed.
|
||
|
||
|
||
_ Specify a filter method
|
||
VOID p_sendS(VOID *pIsam, O_IS_IFILTER, INT indexid, VOID *pObj, INT method);
|
||
This function is only applicable when using object oriented programming (OOP).
|
||
|
||
|
||
Specify the object at p0bj to be called with method whenever entries are added to index indextd, to
|
||
facilitate the building of an index to a selection of records (a sparse index).
|
||
|
||
|
||
The method is called with a pointer to the key that is being inserted and the address of a LoNG which is the
|
||
data file reference. The method must return FALSE if the entry should be omitted from the index or TRUE if
|
||
it should be added.
|
||
|
||
|
||
To remove the filter method, pobj must be passed as NULL.
|
||
|
||
|
||
17
|
||
|
||
|
||
ISAM REFERENCE
|
||
|
||
|
||
pil
|
||
VOID p_send5(VOID *plsam, O_IS_IDUP, INT indexId, VOID *pObj, INT method);
|
||
|
||
This function is only applicable when using object oriented programming (OOP).
|
||
Specify the object at pobj to be called with method whenever a duplicate entry is added to index indexId.
|
||
|
||
|
||
The method is called with a pointer to the key that is being inserted and the address of a Lonc which is the
|
||
data file reference. The method must return FALSE if the duplicate entry should be omitted from the index
|
||
or TRUE if it should be added anyway. There is no regard for the setting of the flag 1SAM_FLAG_ALLOWDUP in
|
||
either case, ie the method called has higher priority.
|
||
|
||
|
||
To remove the duplicates method, pobj must be passed as NULL.
|
||
|
||
|
||
INT p_send3(VOID *plsam, O_IS_IBUILD, INT indexId);
|
||
|
||
|
||
Build the index specified by indexid using its key definition to extract keys from the records in the data
|
||
file.
|
||
|
||
|
||
If the data file is already ordered by the key for this index, 0_1s_1aBuILD should be used to build the
|
||
index much faster.
|
||
|
||
|
||
The index is always built from scratch; any existing index entries are erased first.
|
||
|
||
|
||
Returns FALSE if the complete build was successful and there were no duplicate entries; returns TRUE if
|
||
successful but there were duplicates or leaves with one of the negative errors returned by the PLIB
|
||
function p_write in which case all entries made so far will be erased.
|
||
|
||
|
||
Duplicate entries
|
||
|
||
|
||
The value TRUE, returned by 0_1$_IBUILD is simply to inform the caller that one or more keys extracted
|
||
from the data during the build were identical. It does not, however, indicate whether more than one copy
|
||
of the key has been stored in the index or not. This is controlled by the following:
|
||
|
||
|
||
= Ifa duplicates method has been specified by 0_1s_1pup, its return value of TRUE or FALSE
|
||
determines whether to insert or not. (This is applicable to OOP only.)
|
||
|
||
|
||
= If there is no duplicates method, the setting of the flag 1SAM_FLAG_ALLOWDUP is used.
|
||
|
||
|
||
INT p_send3(VOID *plIsam, O_IS_IQBUILD, INT indexId);
|
||
|
||
|
||
Build the index specified by indexid using its key definition to extract keys from the records in the
|
||
(sorted) data file.
|
||
|
||
|
||
For this function to work, the records in the data file must be in the correct order defined by the key
|
||
definition for this index.
|
||
|
||
|
||
There are two advantages of this function over 0_Is_1BUILD:
|
||
|
||
|
||
1) It is much faster for a large number of records. So much faster, in fact, that it is often quicker to
|
||
sort the data file (using, for example, a quick-sort algorithm) and then call 0_1s_1aBuILb to build
|
||
an index than to call 0_1s_1BUILD with an unsorted data file.
|
||
|
||
|
||
2) The index file produced will generally be smaller (more compressed) than that produced using
|
||
O_1S_IBUILD. Note that if the flag ISAM_FLAG_MINIMIZE is set before the call to 0_IS_QBUILD, an
|
||
even smaller index file will be produced at a small cost to the time taken (see the 0_1S_IFLAGS
|
||
function, above).
|
||
|
||
|
||
The index is always built from scratch, so any existing index entries are erased first.
|
||
|
||
|
||
Returns FALSE if the complete build is successful or leaves with one of the negative errors returned by the
|
||
PLIB function p write in which case all entries made so far will be erased.
|
||
|
||
|
||
Note that there is no checking for duplicate entries as there is with the o_1s_1BUILD function.
|
||
|
||
|
||
18
|
||
|
||
|
||
2 ISAM FUNCTIONS
|
||
|
||
|
||
INT p_send3(VOID *plsam, O_IS_IADD, INT indexId);
|
||
|
||
|
||
Add an entry to the index indexid using its key definition to extract a key from the record currently in the
|
||
record buffer.
|
||
|
||
|
||
The entry is added to the given index only, unlike the function 0_1s_app which adds entries to all
|
||
appropriate indexes automatically. This provides, therefore, the mechanism for building and maintaining
|
||
a selective (sparse) index:
|
||
|
||
|
||
= Read records into the record buffer using, for example, 0_1S_DNEXT, Or O_IS_IFIND, 0_IS_INEXT
|
||
with another index.
|
||
|
||
|
||
= Selectively add entries to this index if certain conditions are met.
|
||
|
||
|
||
This method of building an index will typically be slower than building a sparse index by calling
|
||
O_IS_IBUILD Or 0_IS_IQBUILD after specifying a filter method but does not require OOP.
|
||
|
||
|
||
If the entries are to be added in key order, the function 0_1s_1aapp should be used to add the entries
|
||
faster.
|
||
|
||
|
||
Retumis FALSE if successful or TRUE if successful but the entry is a duplicate or leaves with one of the
|
||
negative errors returned by the PLIB function p_write.
|
||
|
||
|
||
The decision whether to add a duplicate entry is made in exactly the same way as with 0_IS_IBUILD.
|
||
|
||
|
||
INT p_send4(VOID *pIsam, O_IS_IQADD, INT indexId, INT finish);
|
||
|
||
|
||
Add an entry to the index indexid in order using its key definition to extract a key from the record
|
||
currently in the record buffer.
|
||
|
||
|
||
The entries must be added in the correct order given by the key for this index and the parameter finish
|
||
should be passed as FALsE for all entries except the last entry, when it should be passed as TRUE.
|
||
|
||
|
||
The index should contain no entries before the first 0_1s_1aapp is called.
|
||
The advantages of this function over 0_1s_1apD are the same as 0_IS_IQBUILD over O_IS_IBUILD.
|
||
|
||
|
||
Returms FALSE if successful or leaves with one of the negative errors returned by the PLIB function
|
||
P_write.
|
||
|
||
|
||
Note that there is no checking for duplicate entries as there is with the 0_1s_1app function.
|
||
|
||
|
||
1S_IEF
|
||
|
||
|
||
INT p_send3(VOID *pIsam, O_IS_IERASE, INT indexId);
|
||
|
||
|
||
Erase an entry from the index indexid, which corresponds to the record currently in the record buffer.
|
||
|
||
|
||
The entry is erased from the given index only, unlike the function 0_1s_ERASE which erases entries from
|
||
all appropriate indexes automatically. This provides, therefore, the mechanism for maintaining a selective
|
||
(sparse) index.
|
||
|
||
|
||
Returns FALSE if successful or TRUE if there is no entry in the index which corresponds to the record in the
|
||
record buffer or leaves with one of the negative errors returned by the PLIB function p_write.
|
||
|
||
|
||
INT p_send3(VOID *pisam, O_IS_IERASEALL, INT indexId);
|
||
|
||
|
||
Erase all entries in the index indextd.
|
||
|
||
|
||
Returns zero if successful or leaves with one of the negative errors returned by the PLIB function
|
||
p_write.
|
||
|
||
|
||
19
|
||
|
||
|
||
INT p_send4(VOID *pIsam, O_IS_ICOUNT, INT indexId, LONG *count);
|
||
Write the number of entries in the open index file indexId to count.
|
||
|
||
|
||
Returns zero if successful or leaves with one of the negative errors returned by the PLIB function p_seek.
|
||
|
||
|
||
INT p_send4(VOID *pIsam, O_IS_DSIZE, INT indexId, LONG *size);
|
||
|
||
|
||
Write the size (in bytes) of the open index file indexid to size.
|
||
|
||
|
||
Returns zero if successful or leaves with one of the negative errors returned by the PLIB function p_seek.
|
||
|
||
|
||
INT p_send4(VOID *pisam, O_IS_IFIND, INT indexId, VOID **pArgs);
|
||
|
||
|
||
Find an entry in the index indexid which matches the key generated by the arguments at pargs and read
|
||
the corresponding record from the data file into the record buffer.
|
||
|
||
|
||
pArgs is the address of an array of pointers to key fields to be matched, terminated by NULL if fewer key
|
||
fields are supplied than are specified in the key definition for this index.
|
||
|
||
|
||
Returns the following:
|
||
|
||
FALSE if a matching entry is found and the corresponding record is read into the
|
||
record buffer.
|
||
|
||
TRUE if no match is found and the record corresponding to the following index entry
|
||
is read into the buffer.
|
||
|
||
E_FILE_EOF if no match is found and there is no following index entry (no record is read
|
||
|
||
|
||
into the record buffer).
|
||
or leaves with a negative error returned from the PLIB function p_read.
|
||
For example:
|
||
|
||
|
||
LOCAL_C INT findEntryCINT indexId,...)
|
||
{
|
||
|
||
|
||
return(p_send4(pisam,O_IS_IFIND, indexId, &indexId+1));
|
||
>
|
||
|
||
|
||
LOCAL_C VOID find(VoID)
|
||
{
|
||
UBYTE str[16];
|
||
LONG lL;
|
||
|
||
|
||
L=99999L ;
|
||
|
||
str [0J=3;
|
||
|
||
str[ij="A';
|
||
|
||
str[2]="B';
|
||
|
||
str(3]='C';
|
||
|
||
if (findEntry(index1,&l,&str [0] ,NULL)==FALSE)
|
||
p_printf ("Found") ;
|
||
|
||
else
|
||
p_printf("Not found");
|
||
|
||
>
|
||
|
||
|
||
will search index’ for the first entry with field zero equal to 999991 and field one equal to asc. Note that
|
||
the field types passed must be the same as those specified in the key definition of the index.
|
||
|
||
|
||
If there is more than one entry in the index which matches the given fields, 0_1s_IFIND will always read
|
||
the first one. To read any others, use 0_IS_INEXT.
|
||
|
||
|
||
20
|
||
|
||
|
||
2 ISAM FUNCTIONS
|
||
|
||
|
||
INT p_send3(VOID *pIsam, O_IS_INEXT, INT indexId);
|
||
Read the record corresponding to the next entry in index indexid into the record buffer.
|
||
|
||
|
||
Returns zero if successful or £_FILE_EoF if the current index entry is the last one or leaves with one of the
|
||
negative errors returned by the PLIB function p_read.
|
||
|
||
|
||
If two or more indexes are open at the same time, this method function may return E_FILE_EOF
|
||
erroneously. If you use two or more indexes simultaneously, you should access this method via a utility
|
||
function of the following form:
|
||
|
||
|
||
GLDEF_C INT DoInext(VOID *pIsam, INT indexId)
|
||
€
|
||
INT ret;
|
||
|
||
|
||
ret=p_send3(pIsam,O_IS_INEXT, indexId);
|
||
|
||
if (ret==E_FILE_EOF)
|
||
{
|
||
p_send3(pIsam,0_IS_IBACK, indexId);
|
||
p_send3(pIsam,O_IS_INEXT, indexId);
|
||
ret=p_send3(pIsam,O_IS_INEXT, indexId);
|
||
>
|
||
|
||
return(ret);
|
||
|
||
>
|
||
|
||
|
||
INT p_send3(VOID *plsam, O_IS_IBACK, INT indexId);
|
||
|
||
|
||
Read the record corresponding to the next entry in index index!d into the record buffer.
|
||
|
||
|
||
Retums zero if successful or €_FILE_E0F if the current index entry is the first one or leaves with one of the
|
||
negative errors returned by the PLIB function p_read.
|
||
|
||
|
||
INT p_send3(VOID *pIsam, O_IS_IFIRST, INT indexId);
|
||
Read the record corresponding to the first entry in index indexid into the record buffer.
|
||
|
||
|
||
Returns zero if successful or €_F1L€_EOF if there are no entries in the index or leaves with one of the
|
||
negative errors returned by the PLIB function p_read.
|
||
|
||
|
||
INT p_send3(VOID *pIsam, O_IS_ILAST, INT indexId);
|
||
|
||
|
||
Read the record corresponding to the last entry in index indexid into the record buffer.
|
||
|
||
|
||
Returns zero if successful or €_F1LE_E0F if there are no entries in the index or leaves with one of the
|
||
negative errors returned by the PLIB function p_read.
|
||
|
||
|
||
INT p_send3(VOID *pIsam, O_IS_ICURRENT, INT indexId);
|
||
|
||
|
||
Read the record corresponding to the current entry in index indexid into the record buffer.
|
||
|
||
|
||
Retums zero if successful or €_F1LE€_E0F if the current index entry is set to end-of-file or leaves with one
|
||
of the negative errors returned by the PLIB function p_read.
|
||
|
||
|
||
21
|
||
|
||
|
||
INDEX
|
||
|
||
|
||
IS ADD 14
|
||
-18 DESTROY 10
|
||
DFIRST 15
|
||
“DFLUSH 15
|
||
|
||
|
||
ERASE 14
|
||
GET FIELD 13
|
||
GET_FIELDDEF 11
|
||
GET_KEYDEF 13
|
||
GET_RBUF 14
|
||
GET_TYPE 14
|
||
“IADD 19
|
||
“IBACK 21
|
||
IBUILD 18
|
||
ICLOSE 16
|
||
“ICOUNT 20
|
||
“ICURRENT 21
|
||
“IDUP 18
|
||
IERASE 19
|
||
“IERASEALL 19
|
||
TIFILTER 17
|
||
“IFIND 20
|
||
IFIRST 21
|
||
IFLAGS 17
|
||
“IFLUSH 17
|
||
ILAST 21
|
||
“INEXT 21
|
||
|
||
INIT 9
|
||
|
||
“IOPEN 16
|
||
“IQADD 19
|
||
“IOBUILD 18
|
||
“ISIZE 20
|
||
|
||
“PUT FIELD 13
|
||
"SET_DFORMAT 14
|
||
SET_FIELDDEF 10
|
||
~SET_KEYDEF 11
|
||
"SET_RADIX 14
|
||
"UPDATE 15
|
||
|
||
|
||
1111919919191 9, 92,9, 9,2,2,9,9,9,9,2,9,9,9,0,9,0.0
|
||
|
||
|
||
FIAVAIIRAA AAD ADD BADAADARARA DADO RBBB AAD D
|
||
|
||
|