home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Hall of Fame
/
HallofFameCDROM.cdr
/
proglc
/
index.lzh
/
INDEX.DOC
< prev
next >
Wrap
Text File
|
1988-02-02
|
27KB
|
670 lines
INDEX.DOC Version 1.03 February 2, 1988
Page 1
This documentation and the software it describes are Copyright 1987, 1988 by
Jim Mischel. You are free to use this software providing that the following
conditions are met:
1) Any distributed versions contain the my copyright notice and are
accompanied by documentation and source code. The package doesn't do
anybody any good if they don't know how to use it.
2) You notify me before using it in any commercial application. I won't
ask for any money, I'd just like to know about it. Also, if you
notify me I may be able to get you a current version and/or notify
you of any bug fixes.
This package was developed using Turbo C version 1.0. It should port to other
compilers and operating systems with very little modification.
Questions, comments, problems should be addressed via EMAIL to Jim Mischel,
CIS id number 73717,1355. I'd be more than happy to reply to inquiries.
INDEX.DOC Version 1.03 February 2, 1988
Page 2
DESCRIPTION
INDEX is a collection of routines designed to provide single-key indexed file
access to C programs. They are modeled after the ISAM features supplied in
standard COBOL implementations, with some additions. Keys can be of any type,
including float, double, and user-defined key types.
This version of the package uses a threaded binary tree to maintain the index.
This may change in future versions. I chose the threaded tree because it is
relatively simple to implement, uses little memory, and is acceptable for
small and medium-sized data sets. It is not the fastest method available, and
I make no claims as to the speed of record access, though it should be
acceptable. Currently, no balancing is performed when adding or deleting
records from the file. In many cases, this can result in a severely un-
balanced tree and horribly slow access times. There is, however, a rebuild
function that will remove deleted records and balance the index tree.
Following are descriptions of the user-accessable routines. These are the
only routines that should be called by applications programs. Calling other
routines in the package will produce unpredictable results and could very
possibly destroy the database.
The examples provided build on previous examples. For example, the example
used with iclose() uses data types defined in the example provided with
iopen().
INDEX.DOC Version 1.03 February 2, 1988
Page 3
IOPEN - open a file for indexed I/O
void *iopen(char *fname, unsigned recsiz, unsigned offset,
char dupflag, int (*cmp_rtn)());
iopen opens an indexed file for reading/writing. If the file to be opened
does not exist, iopen attempts to create it. Upon successful completion,
iopen will return a pointer to an index control record. This pointer should
not be modified by the application program, nor should the fields in the index
control record be changed by the application. The data in the record is used
by the index routines and is at best marginally useful to the application.
Modifying either the returned pointer or the data pointed to will produce
unpredictable results and could very likely make the database unuseable. If
iopen can not open the file, NULL is returned and the global variable ierrno
is set to identify the cause of the error.
Arguments passed to iopen()
fname A string containing the name of the file to be opened. This
name should be without an extension. iopen will append the
extension '.DAT' for the data file and the extension '.INX' for
the index file.
recsiz Size of the data record in characters.
offset The offset (in characters) from the beginning of the record to
the first byte of the key.
dupflag Controls whether duplicate keys are allowed. If dupflag is 0,
duplicate keys are not allowed, and an attempt to add a
duplicate key will return an error (see error codes below). If
dupflag is 1, duplicate keys will be allowed.
cmp_rtn Is a pointer to a user-defined key comparison routine.
The index routines use this routine for key comparisons
The declaration of the routine must be:
int routine_name(void *arg1, void *arg2);
This routine accepts pointers to the operands and returns
-1 if arg1 < arg2
0 if arg1 == arg2
1 if arg1 > arg2
INDEX.DOC Version 1.03 February 2, 1988
Page 4
IOPEN - open a file for indexed I/O
Example:
#include <stdio.h>
#include "index.h"
int cmpr_rtn(void *arg1, void *arg2); /* for string comparisons */
main (int argc, char *argv[]) {
struct {
char first_name[25],
last_name[25];
} name_rec;
char *name_file; /* pointer to control record */
unsigned offset;
offset = &name_rec.last_name - &name_rec; /* compute key offset */
if ((name_file = iopen(argv[1],sizeof(name_rec),offset,1,cmpr_rtn) ==
NULL) {
printf("Error opening file %s\n",argv[1]);
printf("Error code is %X\n",ierrno);
return;
}
...
...
...
int cmpr_rtn(void *arg1, void *arg2)
{
int r;
return(((r = strcmp((char *)arg1, (char *)arg2)) == 0) ? 0 :
(r > 0) ? 1 : -1);
}
INDEX.DOC Version 1.03 February 2, 1988
Page 5
ICLOSE - close files and free memory
void iclose(void *db_control);
iclose closes the index and data files assigned to the index control record,
and frees the memory used. iclose does not return status.
IMPORTANT NOTE:
All files opened with iopen that have had records added or deleted MUST be
closed with iclose. Failure to do so may result in losing data or destroying
the integrity of the index file.
Arguments passed to iclose()
db_control The pointer returned when the file was opened by iopen.
Example: (building on the one above)
iclose(name_file);
INDEX.DOC Version 1.03 February 2, 1988
Page 6
IREAD - read a record from the file
int iread(void *db_control, void *destin);
iread reads a record and places it in memory at destin. iread assumes there
is sufficient space at destin to hold the entire data record. Place the key
value to be searched for at the key position in the record at destin and call
iread. iread returns 0 if the record was found, I_NOREC if not, and error
status if there was an I/O error. On error conditions, the data at destin is
not changed. Using iread does not change the pointer used by the sequential
read functions iread_next and iread_prev.
Arguments passed to iread()
db_control The pointer returned when the file was opened by iopen.
destin A pointer to the structure that will receive the data record.
Example:
name_rec.last_name = "Mischel";
if (iread(name_file,&name_rec)) {
printf("\007Record key %s not found in file %s\n",
name_rec.last_name,argv[1]);
printf("Error code is %X\n",ierrno);
}
else
printf("Record found\n");
INDEX.DOC Version 1.03 February 2, 1988
Page 7
ISTART - position file for sequential access
int istart(void *db_control, char cond, void *source);
istart positions the file pointer for sequential file access. This function
must be called before attempting to sequentially access the file through
iread_next or iread_prev. Place the key value to be searched for at the key
position in source and call istart. No data is transferred by this function.
istart will return 0 if the file pointer was positioned successfully, EOF if
no record meeting key and cond could be found, and one of the standard error
codes on error.
Arguments passed to istart()
db_control The pointer returned when the file was opened by iopen.
cond One of the conditions defined in INDEX.H:
START_FILE Start at beginning of file, source is ignored
LT Start at the key less than the key in source
LE Start at key less then or equal to key in
source. If a record exists with key, it will
start there.
EQ Start at key equal to the key in source.
GE Start at key greater than or equal to key in
source. If a record exists with key, it will
start there.
GT Start at key greater than key in source.
END_FILE Start at end of file. This is useful for
reading the entire file backwards.
Example:
/*
* to position the file at the first record with a key greater than or equal
* to Sm.
*/
name_rec.last_name = "Sm";
if (istart(db_control,GE,&name_rec)) {
printf("Couldn't position file\n");
printf("Error code is %X\n",ierrno);
}
else {
/*
* read the file sequentially forward or backward. See examples for
* iread_next and iread_prev.
*/
}
INDEX.DOC Version 1.03 February 2, 1988
Page 8
IREAD_NEXT - Sequentially read the next record
int iread_next(void *db_control, void *destin);
Read the next record in sequence into destin. iread_next assumes there is
room in destin for the entire record. Returns 0 if successful, EOF at end of
file, standard error code on error. On error conditions, the data at destin
is not changed.
Arguments passed to iread_next()
db_control The pointer returned when the file was opened by iopen.
destin A pointer to the structure to receive the data record.
Example:
/*
* read the file sequentially forward (assuming it was started as above)
*/
while (!iread_next(db_control,&name_rec))
printf("%s %s\n",name_rec.first_name,name_rec.last_name);
INDEX.DOC Version 1.03 February 2, 1988
Page 9
IREAD_PREV - sequentially read the previous record
int iread_prev(void *db_control, void *destin);
read the previous record in sequence into destin. Assumes there is room in
destin for the entire record. Returns 0 if successful, EOF at top of file,
standard error code on error. On error conditions, the data at destin is not
changed.
Arguments passed to iread_prev()
db_control The pointer returned when the file was opened by iopen.
destin A pointer to the structure to receive the data record.
Example:
/*
* read the file sequentially backward (assuming it was started as above)
*/
while (!iread_prev(db_control,&name_rec))
printf("%s %s\n",name_rec.first_name,name_rec.last_name);
INDEX.DOC Version 1.03 February 2, 1988
Page 10
IWRITE - add a new record
int iwrite(void *db_control, void *source);
Write the record from source to the data file. Data records are always added
to the end of the file. Returns 0 if successful, I_INVKEY if duplicates are
not permitted and an attempt was made to add a duplicate record, standard
error code otherwise.
Arguments passed to iwrite()
db_control The pointer returned when the file was opened by iopen.
source A pointer to the structure to be written.
Example:
name_rec.first_name = "Jim";
name_rec.last_name = "Mischel";
switch (iwrite(db_control,&name_rec)) {
case 0 :
printf("Record written\n");
break;
case I_INVKEY :
printf("Duplicate key %s\n",name_rec.last_name);
break;
default :
printf("Write error. Error code is %X\n",ierrno);
}
INDEX.DOC Version 1.03 February 2, 1988
Page 11
IREWRITE - update an existing record
int irewrite(void *db_control, void *source);
Update the current record in the file. The record must have been read using
one of the read routines, or written using iwrite, and must still be in the
buffer. It is not possible to change the key using irewrite. Use idelete to
remove the record and iwrite to add the record after the key has been changed.
Returns 0 on success, I_INVKEY if attempt to rewrite a record that isn't in
the buffer, standard error code otherwise.
Arguments passed to irewrite()
db_control The pointer returned when the file was opened by iopen.
source A pointer to the structure to be written.
Example:
/* first we have to read the record */
name_rec.last_name = "Smith";
if (iread(name_file,&name_rec)) {
printf("\007Record key %s not found in file %s\n",
name_rec.last_name,argv[1]);
printf("Error code is %X\n",ierrno);
}
else {
name_rec.first_name = "John";
switch (irewrite(db_control,&name_rec)) {
case 0 :
printf("Success\n");
break;
case I_INVKEY :
printf("Invalid rewrite attempted\n");
break;
default :
printf("Rewrite error. Error code is %X\n",ierrno);
}
INDEX.DOC Version 1.03 February 2, 1988
Page 12
IDELETE - delete a record
int idelete(void *d, void *source);
Delete the record currently in the buffer. Record must have been read using
one of the read routines, or written using iwrite, and still be in the buffer.
Returns 0 on success, I_INVKEY if attempt to delete a record that isn't in the
buffer, standard error code otherwise.
When a record is deleted, it is not removed from the data file and its index
pointer is not removed from the index file. What happens is that the index
record is flagged as deleted and the read routines treat the record as if it
didn't exist. This has at least two major side affects:
1) If the data file is scanned by a program that doesn't use the index
routines, deleted records will be picked up along with current records.
2) In a file that has a large turnover rate, there will be a considerable
amount of wasted space taken up by the deleted records.
Beginning with Version 1.03 (02/02/88), the function irebuild() will remove
deleted records from a data file and balance the index tree for optimum
performance. This takes care of the wasted space in the data file.
The first problem can be solved by using the irebuild() function before
scanning the data file, or you can add a deleted record flag to the data
record. Your program will have to manipulate this flag, setting it before the
record is deleted.
Arguments passed to idelete()
db_control The pointer returned when the file was opened by iopen.
source A pointer to the structure containing the key name to be
deleted.
INDEX.DOC Version 1.03 February 2, 1988
Page 13
IDELETE - delete a record
Example:
/* first read the record */
name_rec.last_name = "Smith";
if (iread(name_file,&name_rec)) {
printf("\007Record key %s not found in file %s\n",
name_rec.last_name,argv[1]);
printf("Error code is %X\n",ierrno);
}
else {
switch (idelete(db_control,&name_rec)) {
case 0 :
printf("Record deleted\n");
break;
case I_INVKEY :
printf("Invalid delete attempted\n");
break;
default :
printf("Delete error. Error code is %X\n",ierrno);
}
INDEX.DOC Version 1.03 February 2, 1988
Page 14
IREBUILD - rebuild data and index files
int irebuild(char *old_fname, unsigned recsiz, unsigned offset,
char dupflag, int (*cmp_rtn)(), char *new_fname);
Copy the file old_fname to the file new_fname, removing deleted records and
balancing the index tree. old_fname must be the name of an existing index
file (without the extension). new_fname must be the name of the new file
(without extension), and it must not exist. If a file with new_fname already
exists, the function returns with an error.
The routine returns 0 if the rebuild is successful. A non-zero return
signifies error. If the routine exits with an error, it does not clean up its
workspace. It may leave files open and temporary memory buffers allocated.
If the routine exits with an error, it is a good idea to abort the calling
program.
irebuild() uses a temporary file called !REBLD!.$$$ to hold the data records
while it is balancing the index tree. If the function exits with an error,
this file will remain. If you break out of the program (using Control-C)
while the file is being re-built, the original file (old_fname) will not be
harmed. However, the new file (new_fname) will most likely not contain valid
data.
Arguments passed to irebuild()
old_fname A string containing the name of the file to be rebuilt. This
name must be without an extension. irebuild will append the
extension '.DAT' for the data file and the extension '.INX' for
the index file.
recsiz Size of the data record in characters.
offset The offset (in characters) from the beginning of the record to
the first byte of the key.
dupflag Controls whether duplicate keys are allowed. If dupflag is 0,
duplicate keys are not allowed, and an attempt to add a
duplicate key will return an error (see error codes below). If
dupflag is 1, duplicate keys will be allowed.
cmp_rtn Is a pointer to a user-defined key comparison routine.
The index routines use this routine for key comparisons
The declaration of the routine must be:
int routine_name(void *arg1, void *arg2);
This routine accepts pointers to the operands and returns
-1 if arg1 < arg2
0 if arg1 == arg2
1 if arg1 > arg2
INDEX.DOC Version 1.03 February 2, 1988
Page 15
IREBUILD - rebuild data and index files
new_fname A string containing the name of the file to be created. This
file will contain the packed data and balanced index files.
This name must be without an extension. irebuild will append
the extension '.DAT' for the data file and the extension '.INX'
for the index file.
Example:
#include <stdio.h>
#include "index.h"
int cmpr_rtn(void *arg1, void *arg2); /* for string comparisons */
main (int argc, char *argv[]) {
struct {
char first_name[25],
last_name[25];
} name_rec;
char *name_file; /* pointer to control record */
unsigned offset;
offset = &name_rec.last_name - &name_rec; /* compute key offset */
if (irebuild(argv[1],sizeof(name_rec),offset,1,cmpr_rtn,argv[2])) {
printf("Error rebuilding file %s\n",argv[1]);
printf("Error code is %X\n",ierrno);
}
return;
} /* main */
int cmpr_rtn(void *arg1, void *arg2)
{
int r;
return(((r = strcmp((char *)arg1, (char *)arg2)) == 0) ? 0 :
(r > 0) ? 1 : -1);
}
INDEX.DOC Version 1.03 February 2, 1988
Page 16
ERROR CODES
The global variable ierrno will contain the results of the last I/O operation.
Possible values are:
Code Value Description
---- ----- -----------
I_NODAT 0x10 - couldn't open data file (iopen)
I_DATRD 0x11 - I/O error attempting to read data file (any)
I_DATWT 0x12 - I/O error attempting to write to data file (any)
I_NOINX 0x20 - couldn't open index file (iopen)
I_INXRD 0x21 - I/O error attempting to read index file (any)
I_INXWT 0x22 - I/O error attempting to write to index file (any)
I_INVKEY 0x80 - Attempt to add duplicate key when duplicates not permitted
(iwrite)
Attempt to rewrite deleted record, or record not in buffer
(irewrite)
Attempt to delete deleted record, or record not in buffer
(idelete)
I_NOMEM 0x81 - out of memory (iopen)
I_NOREC 0x82 - record not found (iread)
I_NOCMP 0x83 - no comparison routine (iopen)
INDEX.DOC Version 1.03 February 2, 1988
Page 17
NOTES
I have included a two sample programs that illustrate the use of several of
the routines in this package. The first program, ADR.C, is a simple
address/phone book that is marginally useful as a real application, but does
show how to use the routines. The second program, IBLD.C, illustrates the use
of the irebuild() function.
MAKE FILE
I have included a make file that can be used to re-compile the package. This
file includes commands to rebuild the library (INDEX.LIB), using either the
QLIB program available on BPROGB DL 4, or Microsoft's LIB. The supplied
library was build with MS LIB and is compatible with TLINK and MS LINK.
Remember, if you use QLIB, the resulting library will not be compatible with
Microsoft LINK.
KNOWN BUGS
The current version (1.03) fixes all reported bugs. If you run accross any,
please drop me a note.
REVISION HISTORY
This package started as a "I wonder how that's done" type of project. The
original program used a primitive hashing algorithm to store and retrieve the
keys. The program (written in Pascal) did work, but was a terrible kludge and
I threw it away. The current version was written for two reasons: 1) I
wanted to learn C, and 2) I needed something like this for a project I was
planning.
Version 1.00 August 13, 1987
Original release version.
Version 1.01 August 21, 1987
1) Fixed bugs in iwrite() and irewrite() that prevented the data buffer
from being updated when the record was written. This bug caused
irewrite() and idelete() to erroneously return I_INVKEY.
2) Updated documentation. Added examples and cleaned things up quite a
bit.
3) Split the package into multiple source files and added a make file
that creates a Turbo C compatible library.
4) Add calls to fflush() in the write routines. This slows things down a
bit (a lot?), but does help keep data file integrity.
Version 1.02 November 4, 1987
1) Removed the 'keytyp' parameter from the iopen() call. Also removed
default key comparison routines. All key comparison routines are now
user-defined. Added new error code, I_NOCMP, returned by iopen() when
the 'cmp_rtn' parameter is NULL.
INDEX.DOC Version 1.03 February 2, 1988
Page 18
NOTES
Version 1.03 February 2, 1988
1) There are some possible conflicts when using sequential and random I/O
at the same time. These can only occur when adding or deleting
records while sequentially scanning the file. In general, the only
problems involve deleting a record that is to be the next one read, or
adding a record between the current and next records. In the delete
case, the deleted record will still be read, and in the add case, the
new record will not be read. These problems have been fixed in the
current release.
2) There were some serious problems with the idelete() function when
deleting the root node, which caused backward pointers to get munged.
This problem has been fixed in the current release.
3) Add irebuild() function. This function removes deleted records from
the data file and balances the index tree. Use it often.
4) Include IBLD.C program to illustrate use of irebuild() function.
5) Modify make file (INDEX.MAK) to allow for use of either QLIB or
Microsoft's LIB.
INDEX.DOC Version 1.03 February 2, 1988
Page 19
NOTES
Files that make up the index package
ADR.C - demonstration program for index package
ADR.MAK - make file for ADR
ARCIT.BAT - batch file to build the archive
IBLD.C - demonstration program for irebuild() function
IBLD.MAK - make file for IBLD
IDELETE.C - delete record routine
INAMES - name file for QLIB program
INXRTNS.C - support functions for index package
INDEX.DOC - index package documentation, rough but useable
INDEX.H - header file included by application programs
INDEX.LIB - index routines library (useable only by TLINK, not by MS LINK)
INDEX.MAK - make file for index routines
INXDEFS.H - header file for index routines
IOPEN.C - open and close functions
IREADN.C - readnext function
IREADP.C - readprevious function
IREADR.C - read random function
IREWRITE.C- rewrite (update) record function
IREBUILD.C- rebuild file (remove deleted records and balance tree)
ISTART.C - start function
IWRITE.C - write record function
READ.ME - Latest revision notes