home *** CD-ROM | disk | FTP | other *** search
/ Frostbyte's 1980s DOS Shareware Collection / floppyshareware.zip / floppyshareware / DOOG / CBASE09.ZIP / GUIDE.TXT < prev    next >
Text File  |  1989-09-01  |  40KB  |  1,041 lines

  1.                       CBASE
  2.  
  3.              The C Database Library
  4.  
  5.  
  6.  
  7.  
  8.  
  9.                      Citadel
  10.                Brookville, Indiana Copyright (c) 1989 by Citadel.  All rights reserved.
  11.  
  12. Citadel
  13. 241 East Eleventh Street
  14. Brookville, IN 47012
  15. 317-647-4720
  16. Bulletin Board  317-647-2403
  17.  
  18. This manual is protected by United States copyright law.  No part of it
  19. may be reproduced without the express written permission of Citadel.
  20.  
  21. Technical Support
  22. Technical support is available between 10 a.m. and 4 p.m. EST.  When
  23. calling for technical support, please have ready the following
  24. information:
  25.  
  26.           . product name, version, and serial number
  27.           . operating system and version number
  28.           . C compiler and version number
  29.           . computer brand and model
  30.  
  31. The Citadel BBS is available 24 hours a day.  Users of Citadel
  32. products are encouraged to make use of this resource.
  33.  
  34.  
  35.  
  36.  
  37.  
  38.  
  39.  
  40.  
  41.  
  42.  
  43.  
  44.  
  45.  
  46.  
  47.  
  48.  
  49.  
  50.  
  51.  
  52.  
  53.  
  54.  
  55.  
  56. UNIX is a trademark of AT&T.  MS-DOS is a trademark of Microsoft
  57. Corporation.
  58.  
  59.  
  60.  
  61.  
  62.                                        Contents
  63.  
  64.  
  65. Chapter 1.  Introduction                      1
  66.  
  67. Chapter 2.  Basic Database Concepts           3
  68.      2.1  The Linked Sequential File
  69.      2.2  Inverted Files
  70.      2.3  The B-tree
  71.  
  72. Chapter 3.  cbase Library Functions           7
  73.      3.1  File Access Functions
  74.      3.2  Record Cursor Positioning Functions
  75.      3.3  Key Cursor Positioning Functions
  76.      3.4  Input/Output Functions
  77.      3.5  Lock Functions
  78.  
  79. Chapter 4.  An Example Program               15
  80.      4.1  Data Definition
  81.      4.2  Header Files
  82.      4.3  Opening
  83.      4.4  General Programming Guidelines
  84.      4.5  Closing
  85.  
  86. Appendix A.  Installation Instructions       21
  87.      A1   The blkio Library
  88.      A2   The btree Library
  89.      A3   The cbase library
  90.      A4   The lseq Library
  91.  
  92. Appendix B.  Adding New Data Types           25
  93.  
  94. Appendix C.  Porting to a New Operating System27
  95.  
  96. Bibliography                                 29
  97.  
  98.  
  99.  
  100.  
  101.                        Chapter 1:  Introduction
  102.  
  103.  
  104.      cbase is a C database file management library.  Records may be
  105. accessed both randomly and sequentially through indexes stored in  B+-
  106. trees.  Records may also be accessed sequentially in the order in which
  107. they are stored. Multiuser access is supported under any operating
  108. system with file locking capabilities.  Strict adherence to the ANSI C
  109. standard has been made, making cbase extremely portable.  Certain
  110. operations such as file locking, however, are very operating system
  111. dependent.  All operating system specific code has been isolated,
  112. though, and porting to a system not currently supported should require
  113. only a very minimal effort.
  114.  
  115.      Many of the operations performed by cbase internally represent
  116. independently useful tools, and the software has been designed with this
  117. in mind.  cbase actually comprises four individual libraries, each
  118. complete and independently accessible.  Figure 1.1 shows these libraries
  119. and their relationships.
  120.  
  121.        ┌─────────────────────────────────┐
  122.        │              cbase              │
  123.        └───────┬─────────────────┬───────┘
  124.        ┌───────┴───────┐ ┌───────┴───────┐
  125.        │     lseq      │ │     btree     │
  126.        └───────┬───────┘ └───────┬───────┘
  127.        ┌───────┴─────────────────┴───────┐
  128.        │              blkio              │
  129.        └─────────────────────────────────┘
  130.  
  131.    Figure 1.1:  cbase and Underlying Libraries
  132.  
  133.      At the foundation of cbase is the blkio (block buffered
  134. input/output) library.  blkio is a buffered input/output library very
  135. similar to stdio but based on a file model more appropriate for
  136. structured files such as used in database software.  While stdio models
  137. a file as an unstructured stream of characters, blkio models a file as a
  138. collection of blocks made up of fields.  All file access and buffering is
  139. done using blkio.  All operating system specific code is isolated within
  140. a small portion of this library.
  141.  
  142.      The lseq (linked sequential file) library provides all the facilities
  143. necessary for the creation and manipulation of doubly linked sequential
  144. files.  The btree (B-tree) library provides the same for B+-tree files. 
  145. The cbase library uses lseq and btree to perform all structured file
  146. management operations.  The lseq library is used for record storage and
  147. the btree library for inverted file key storage.
  148.  
  149.      When using a particular library, all operations are performed with
  150. functions provided by that library.  No references need be made to
  151. underlying libraries.  (There is one exception; see bexit in the blkio
  152. reference manual.)  This is not to say that the libraries may not be used
  153. simultaneously.
  154.  
  155.  
  156.  
  157.  
  158.             Chapter 2:  Basic Database Concepts
  159.  
  160.  
  161.      This chapter summarizes the fundamental database concepts used
  162. by cbase.  It is intended to be a brief and conveniently accessible
  163. introduction.  The information presented here is not necessary for using
  164. cbase and can be easily skipped by one already familiar with topics
  165. named in the section headings.
  166.  
  167.  
  168. 2.1  The Linked Sequential File
  169.  
  170.      One of the simplest file organizations is the physical sequential
  171. file.  In this organization, records are simply written one after the other
  172. and sequential access is done in the order in which the records are
  173. physically stored.
  174.  
  175.      The physical sequential file works very well in cases where the
  176. data is static, but it is extremely inefficient when the data is dynamic. 
  177. Consider a file containing 100,000 records stored in sorted order, then
  178. the problem of inserting a new record at the beginning of that file. 
  179. This single insertion would result in 100,000 records being moved to
  180. make room at the beginning of the file for the new record.
  181.  
  182.      This identical problem also occurs when large ordered lists are
  183. stored in memory, and the same solution, the linked list, can be used
  184. here (see pp. 106-12 of [HORO76] for an explanation of linked lists in
  185. memory).  In a linked list, the record order is determined by pointers
  186. stored with each record.  In a singly linked list each record has only a
  187. pointer to the next record and so the file can be traversed in just one
  188. direction.  In a doubly linked list, each record has both a next and a
  189. previous pointer, allowing bidirectional access.
  190.  
  191.  
  192. 2.2  Inverted Files
  193.  
  194.      Assume a data file containing member records for an organization,
  195. and that the record format for this file is:
  196.  
  197.           typedef struct {
  198.                long number;
  199.                char name[24];
  200.                char address[81];
  201.                char city[24];
  202.                char state[3];
  203.                char zip[11];
  204.           } member_t;
  205.  
  206. Assume also that these records are stored in the file in physical
  207. sequential order sorted by the member number, therefore a binary
  208. search may be performed to quickly find a record with a given number
  209. field.  The field on which the records are sorted is called the primary
  210. index.  The problem arises when a query is made on another field
  211. besides primary index, in which case a search of the entire data file is
  212. required.
  213.  
  214.     The problem of being able to perform queries efficiently on more
  215. than one field can be resolved through the use of inverted files.  The
  216. inverted file for the name field, for example, would have entries
  217. containing the name field and record position for each record in the
  218. data file, and these would be sorted by the name field.  Fields stored in
  219. inverted files are referred to as secondary indexes.
  220.  
  221.      For a concrete example of an inverted file, just pick up a textbook
  222. and look at the index.  The index consists of terms from the text paired
  223. with the pages on which they are located, which is an inverted file
  224. relative to the text, which would correspond to the data file.
  225.  
  226.  
  227. 2.3  The B-tree
  228.  
  229.      As discussed above, the physical sequential file provides
  230. unacceptable performance for dynamic data.  For static data, however, it
  231. provides efficient sequential as well as random access (using a binary
  232. search).  The linked sequential file solves the problem of performance
  233. for dynamic data while preserving efficient sequential access, but the
  234. ability to randomly access records is lost.  The need to store dynamic
  235. data which may be randomly accessed led to the development of the B-
  236. tree.
  237.  
  238.      As the name implies, the B-tree stores data in a tree structure,
  239. which allows random access.  And since the tree nodes are connected
  240. by pointers, the update problem is solved in much the same way as by
  241. the linked sequential file organization.  A B-tree does not provide very
  242. efficient sequential access, however.  This led to a B-tree variant called
  243. the B+-tree, which provides both efficient random and sequential access.
  244.  
  245.      While the B+-tree does come close to providing the best of all
  246. worlds, it has two important drawbacks.  First, there is significantly
  247. more storage overhead for a B+-tree than for a sequential file.  Every
  248. entry in a B+-tree is stored in a leaf node, and the rest of the tree is
  249. simply scaffolding used when the tree is searched.  Second, each entry
  250. in any type of B-tree must be unique.  These shortcomings make the B-
  251. tree file organization inappropriate for data files containing records,
  252. which may be both large and duplicated.  But for inverted files, which
  253. generally have shorter entries that are by their very nature unique (the
  254. same term and page number would not be listed twice in an index), the
  255. B+-tree is an ideal choice.  Therefore cbase uses the linked sequential
  256. file organization for record storage and B+-trees for inverted files.
  257.  
  258.      A discussion of the inner workings of B-trees is beyond the scope
  259. of this chapter.  A good discussion of the B-tree and its major variants
  260. may be found in COME79.
  261.  
  262.  
  263.  
  264.  
  265.             Chapter 3:  cbase Library Functions
  266.  
  267.  
  268. 3.1  File Access Functions
  269.  
  270.      The cbcreate function is used to create a new cbase.
  271.  
  272.      int cbcreate(char *cbname, size_t recsize,     
  273.                  cbfield_t *fields, size_t fldcnt);
  274.  
  275. cbname points to a character string which is the name of the cbase. 
  276. This name is used as the name of the data file which holds the records
  277. in the cbase.  recsize specifies the record size to be used.  fields
  278. points to an array of structures containing the data definition
  279. information for the cbase.  Each structure in the array contains the
  280. definition for one field, the number of fields being specified by
  281. fldcnt.  The field definitions must be in the order in which the fields
  282. occur in the record.  The field definition structure type cbfield_t is
  283. defined as:
  284.  
  285.      typedef struct {     /* field definition */
  286.           size_t offset;       /* field offset */
  287.           size_t size;         /* size of field */
  288.           int type;            /* type of field */
  289.           int flags;           /* flags */
  290.           char filename[FILENAME_MAX];
  291.      } cbfield_t;
  292.  
  293. offset is the location of the field within the record and size is the
  294. size of the field.  type specifies the field data type, legal values for
  295. which are shown in Table 3.1.  flags values are constructed by
  296. ORing together flags from the following list:
  297.  
  298.           CBFKEY         Field is to be a key.
  299.           CBFUNIQ        Only for use with CBFKEY.
  300.                          Indicates that key is
  301.                          constrained to be unique.
  302.  
  303. FILENAME_MAX is an ANSI C definition indicating the maximum
  304. length of a filename string.  It is found in <stdio.h>.
  305.  
  306.           t_char      signed character
  307.           t_uchar     unsigned character
  308.           t_short     signed short integer
  309.           t_ushort    unsigned short integer
  310.           t_int       signed integer
  311.           t_uint      unsigned integer
  312.           t_long      signed long integer
  313.           t_ulong     unsigned long integer
  314.           t_float     floating point
  315.           t_double    double precision floating point
  316.           t_pointer   pointer
  317.           t_string    character string
  318.           t_binary    block of binary data (e.g., graphics)
  319.  
  320.           Table 3.1:  cbase Data Types
  321.  
  322.      Before a cbase that has been created may be used, it must be
  323. opened using the following function:
  324.  
  325.      cbase_t *cbopen(char *cbname, char *type,
  326.                   cbfield_t fields, size_t fldcnt);
  327.  
  328. cbname, fields, and fldcnt serve the same functions as for
  329. cbcreate and should be given the same values as when the cbase
  330. was created.  type points to a character string specifying the type of
  331. operations for which the cbase is to be opened (as for the stdio
  332. function fopen).  Legal values for type are:
  333.  
  334.           "r"       open for reading
  335.           "r+"      open for update (reading and writing)
  336.  
  337. cbopen returns a pointer to the open cbase.
  338.  
  339.  
  340.      The cbsync function causes any buffered data for a cbase to be
  341. written out.
  342.  
  343.      int cbsync(cbase_t *cbp);
  344.  
  345. The cbase remains open and the buffers retain their contents.
  346.  
  347.      After processing is completed on an open cbase, it must be closed
  348. using the following function:
  349.  
  350.      int cbclose(cbase_t *cbp);
  351.  
  352. The cbclose function causes any buffered data for the cbase to be
  353. written out, unlocks it, closes it, and frees the cbase pointer.
  354.  
  355.  
  356. 3.2  Record Cursor Positioning Functions
  357.  
  358.      Each open cbase has a record cursor.  At any given time the
  359. record cursor is either positioned on a record in that cbase or on a
  360. special position called null.  The record the cursor is on is referred to
  361. as the current record.  The operations performed by most cbase
  362. functions are either on or relative to the current record, so the initial
  363. step in a transaction on a cbase is usually to position the record cursor
  364. on the desired record.  When accessing the records in a cbase in the
  365. order in which they are stored (described in section 2.1), the following
  366. functions are used to move the record cursor:
  367.  
  368.      int cbrecfirst(cbase_t *cbp);
  369.      int cbreclast(cbase_t *cbp);
  370.      int cbrecnext(cbase_t *cbp);
  371.      int cbrecprev(cbase_t *cbp);
  372.  
  373. The cbrecfirst function positions the record cursor to the first
  374. record, and cbreclast to the last record.  Before calling either of
  375. these functions cbreccnt should be used to test if the cbase is empty.
  376.  
  377.      size_t cbreccnt(cbase_t *cbp);
  378.  
  379. If the cbase is empty, there is no first or last record and so these
  380. functions would return an error.  The cbrecnext function advances
  381. the record cursor to the succeeding record, and cbrecprev retreats it
  382. to the preceding record.  In the record ordering, null is located before
  383. the first record and after the last.
  384.  
  385.      There are also functions for saving the current position of the
  386. record cursor and resetting it to that position:
  387.  
  388.    int cbgetrcur(cbase_t *cbp, cbrpos_t *cbrpos_p);
  389.    int cbsetrcur(cbase_t *cbp, cbrpos_t *cbrpos_p);
  390.  
  391. The cbgetrcur function gets the current position of the record cursor
  392. and saves it in the variable pointed to by cbrpos_p.  cbrpos_t is
  393. the cbase record position type.  cbsetrcur can then be used later to
  394. set the record cursor back to that position.  The record cursor can be
  395. positioned on null by passing cbsetrcur the NULL pointer rather
  396. than a pointer to a variable.  Other than this special case, cbsetrcur
  397. should only be called with record cursor positions previously saved with
  398. cbgetrcur.
  399.  
  400.      The cbrcursor macro is used to test if the record cursor for a
  401. cbase is positioned on a record or on null.
  402.  
  403.      void *cbrcursor(cbase_t *cbp);
  404.  
  405. If the record cursor of the cbase pointed to by cbp is positioned on
  406. null, cbrcursor returns the NULL pointer.  If it is on a record,
  407. cbrcursor returns a value not equal to the NULL pointer.  This
  408. function is useful for loops which need to test when the last (or first)
  409. record has been processed.
  410.  
  411.  
  412. 3.3  Key Cursor Positioning Functions
  413.  
  414.      Each open cbase also has a key cursor for each key (index)
  415. defined for that cbase.  As for the record cursor, a key cursor is either
  416. positioned on a record in that cbase or on null.  To access a cbase in
  417. the sort order of a certain key, a key cursor is used instead of the
  418. record cursor.  Each key cursor moves independently of the others, but
  419. whenever a key cursor position is set, the record cursor is moved to the
  420. same record.  The key cursors are not affected by moving the record
  421. cursor.  The following functions are used to move a key cursor:
  422.  
  423.      int cbkeyfirst(cbase_t *cbp, int field);
  424.      int cbkeylast(cbase_t *cbp, int field);
  425.      int cbkeynext(cbase_t *cbp, int field);
  426.      int cbkeyprev(cbase_t *cbp, int field);
  427.  
  428. These perform as do the corresponding functions for the record cursor.
  429.  
  430.      The following function is used to search for a key of a certain 
  431. value:
  432.  
  433.  int cbkeysrch(cbase_t *cbp, int field, void *buf);
  434.  
  435. buf points to the key value to search for.  If a key of that value is not
  436. found, the key (and record) cursor is positioned to the record (possibly
  437. null) which would follow a record with that key value.
  438.  
  439.      Since the key cursors do not automatically follow the record
  440. cursor, the situation sometimes occurs where the record cursor is
  441. positioned to the desired record, but the cursor for the key to be used is
  442. not.  The cbkeyalign function is used to align a specified key cursor
  443. with the record cursor.
  444.  
  445.      int cbkeyalign(cbase_t *cbp, int field);
  446.  
  447. The reason the key cursors are not updated every time the record cursor
  448. moves is not because it would be in any way difficult to do so, but
  449. because this would increase the overhead enormously.  And since only
  450. one key cursor is normally used at a time, this extra overhead would
  451. rarely yield anything in return.
  452.  
  453.      As for the record cursor, a key cursor position can be tested using
  454. the following macro:
  455.  
  456.      void *cbkcursor(cbase_t *cbp, int field);
  457.  
  458. If the key cursor specified by field of the cbase pointed to by cbp is
  459. positioned on null, cbkcursor returns the NULL pointer.  If it is on a
  460. record, cbkcursor returns a value not equal to the NULL pointer.
  461.  
  462.  
  463. 3.4  Input/Output Functions
  464.  
  465.      The functions for reading from a cbase are:
  466.  
  467.    int cbgetr(cbase_t *cbp, void *buf);
  468.    int cbgetrf(cbase_t *cbp, int field, void *buf);
  469.  
  470. where cbp is a pointer to an open cbase and buf points to the storage
  471. area to receive the data read from the cbase.  The cbgetr function
  472. reads the current record into buf.  cbgetrf reads a field from the
  473. current record into buf.
  474.  
  475.      The functions for inserting a record into a cbase are:
  476.  
  477.  
  478.      int cbinscur(cbase_t *cbp, void *buf);
  479.      int cbinsert(cbase_t *cbp, void *buf);
  480.  
  481. where buf points to the record to be inserted.  The cbinscur
  482. function inserts a record after the current record.  cbinsert inserts a
  483. record as the last record in the cbase.  In both cases the record cursor
  484. is positioned on the inserted record.  Note that the position at which a
  485. record is inserted affects only the record cursor functions and not the
  486. key cursor functions.
  487.  
  488.      The cbdelcur function is used to delete a record.
  489.  
  490.      int cbdelcur(cbase_t *cbp);
  491.  
  492. The record cursor must first be positioned on the record to delete, then
  493. cbdelcur called to delete the current record.
  494.  
  495.      The cbputr function writes over an existing record, which is
  496. equivalent to deleting a record and inserting a new one in the same
  497. position in the file.
  498.  
  499.      int cbputr(cbase_t *cbp, void *buf);
  500.  
  501. buf points to the new record contents.
  502.  
  503.  
  504. 3.5  Lock Functions
  505.  
  506.      Before an open cbase can be accessed, it must be locked.  The
  507. function to control the lock status of a cbase is:
  508.  
  509.      int cblock(cbase_t *cbp, int l_type);
  510.  
  511. where cbp is a pointer to an open cbase and l_type is the type of
  512. lock to place on the cbase.  The legal values for l_type are:
  513.  
  514.           CB_RDLCK  lock cbase for reading
  515.           CB_WRLCK  lock cbase for reading and writing
  516.           CB_RDLKW  lock cbase for reading (wait)
  517.           CB_WRLKW  lock cbase for reading and writing (wait)
  518.           CB_UNLCK  unlock cbase
  519.  
  520. If l_type is CB_RDLCK and the cbase is currently write locked by
  521. another process, or if l_type is CB_WRLCK and the cbase is currently
  522. read or write locked by another process, cblock will fail and set
  523. errno to EAGAIN.  For the lock types which wait, cblock will not
  524. return until the lock is available.
  525.  
  526.      A general rule for locking in database applications is to retain
  527. locks for the smallest amount of time possible.  Therefore all locks
  528. should normally be released before pausing for user input.  Also, a
  529. write lock should not be used when a read lock will do.
  530.  
  531.      The cbgetlck function reports the lock status held by the calling
  532. process on a cbase.
  533.  
  534.      int cbgetlck(cbase_t *cbp);
  535.  
  536. It returns one of the legal values for the l_type argument in the
  537. cblock function.
  538.  
  539.  
  540.  
  541.  
  542.                  Chapter 4:  An Example Program
  543.  
  544.  
  545.      Included with cbase is a small example program to illustrate the
  546. basic use of the library.  This program, rolo, is a skeletal program for
  547. storing business cards.  To allow it to be compiled without requiring
  548. any additional libraries for displays, and because the purpose of the
  549. program is purely instructional, the program has not been given a real
  550. user interface.
  551.  
  552.  
  553. 4.1  Data Definition
  554.  
  555.      The first step in writing a program using cbase is to define the
  556. data to be stored.  This should be done in a separate header file for
  557. each record type to be used.   Listing 4.1 shows rolo.h, the header
  558. for the business card record type used by rolo.
  559.  
  560.     Starting at the top, the first thing to note is the definition of a
  561. macro to prevent multiple includes.  This is a general practice
  562. applicable to any header file, cbase or otherwise, whose purpose is to
  563. allow a header to be specified for inclusion multiple times in a file
  564. while ensuring that the definitions within the file are processed only
  565. once.  For a header containing nothing but macro definitions, the only
  566. effect of preventing multiple includes will be reduced compile times. 
  567. But for a header containing, for instance, type definitions, multiple
  568. includes would produce compiler errors. Notice how the include macro
  569. is related to the header file name; the file name is converted to upper
  570. case and the period replaced by an underscore ('.' is not a valid
  571. character for C identifiers).  Be consistent in naming these macros so
  572. you will not accidentally use one elsewhere.
  573.  
  574.      The first cbase definition is the cbase name.  This character string
  575. is the name used for the record file.  Again, note the relation of the
  576. macro name to the header name; the dot extension is dropped from the
  577. file name and the remaining characters converted to upper case.  This
  578. macro is to be used for the cbname argument when calling
  579. cbcreate and cbopen.
  580.  
  581.  
  582.  
  583.   #ifndef ROLO_H  /* prevent multiple includes*/
  584.   #define ROLO_H
  585.  
  586.   /* cbase name */
  587.   #define ROLO("rolo.dat")
  588.  
  589.   /* record definition */
  590.   typedef struct {
  591.      char rl_contact[81];   /* contact name */
  592.      char rl_title[81];     /* contact job title */
  593.      char rl_company[81];   /* company name */
  594.      char rl_addr[4][81];   /* company address */
  595.      char rl_phone[11];     /* phone number */
  596.      char rl_fax[11];       /* fax number */
  597.      char rl_notes[5][81];  /* notes */
  598.   } rolo_t;
  599.  
  600.   /* field numbers */
  601.   #define RL_CONTACT   (1)
  602.   #define RL_TITLE     (2)
  603.   #define RL_COMPANY   (3)
  604.   #define RL_ADDR      (4)
  605.   #define RL_PHONE     (5)
  606.   #define RL_FAX       (6)
  607.   #define RL_NOTES     (7)
  608.   #define RL_FLDCNT    (7)
  609.  
  610.   /* field definitions */
  611.   cbfield_t rl_fields[] = {
  612.      {offsetof(rolo_t, rl_contact[0]),
  613.       sizeofm(rolo_t, rl_contact),
  614.       t_string, CBFKEY | CBFUNIQ, "rl_cont.key"},
  615.      {offsetof(rolo_t, rl_title[0]),
  616.       sizeofm(rolo_t, rl_title),
  617.       t_string, 0, ""},
  618.      {offsetof(rolo_t, rl_company[0]),
  619.       sizeofm(rolo_t, rl_company),
  620.       t_string, CBFKEY, "rl_comp.key"},
  621.      {offsetof(rolo_t, rl_addr[0][0]),
  622.       sizeofm(rolo_t, rl_addr),
  623.       t_string, 0, ""},
  624.      {offsetof(rolo_t, rl_phone[0]),
  625.       sizeofm(rolo_t, rl_phone),
  626.       t_string, 0, ""},
  627.      {offsetof(rolo_t, rl_fax[0]),
  628.       sizeofm(rolo_t, rl_fax),
  629.       t_string, 0, ""},
  630.      {offsetof(rolo_t, rl_notes[0][0]),
  631.       sizeofm(rolo_t, rl_notes),
  632.       t_string, 0, ""}
  633.   };
  634.  
  635.   #endif/* #ifdef ROLO_H */
  636.  
  637.               Listing 4.1:  rolo.h
  638.  
  639.      Next is the type definition for the record being defined.  Each
  640. field in the record (member in the structure) begins with a character
  641. sequence derived from the cbase name, usually two characters followed
  642. by an underscore.  This character sequence should be unique across all
  643. the record types used by a given program.  The type name to be used
  644. is constructed by dropping the dot extension from the header name and
  645. appending _t to indicate a type definition.  This type is for use when
  646. declaring record variables.  Variables of this type should be used for
  647. functions such as cbgetr which require a pointer to a record.
  648.  
  649.      Following the record type definition is a list of macros used to
  650. identify a field when calling cbase functions.  The names of these
  651. macros are constructed by taking the field names from the record type
  652. definition and converting them to uppercase.  This is why the field
  653. names are made unique.  Each field name macro must be an integer
  654. indicating the number of the field in the record type definition, starting
  655. at one, not zero.  A macro for the total number of fields is also
  656. defined.  This macro is to be used for the fldcnt argument when
  657. calling cbcreate and cbopen.
  658.  
  659.      The field definition array specifies, for each field in the record, its
  660. offset in the record, its size, its type, some flags, and a filename to be
  661. used if the field is to be a key.  The offsetof macro should be used
  662. to specify the offset.  A macro sizeofm (size of member) is defined
  663. in cbase.h to give the size of a member of a structure.
  664.  
  665.      size_t sizeofm(struct_t, member);
  666.  
  667. The arguments to sizeofm are the same as for offsetof.  The size
  668. cannot be calculated from the difference between field offsets because
  669. padding characters may be added between structure members to
  670. maintain proper alignment (see Kernighan, 1988).
  671.  
  672.      flags is used to indicate which fields are keys, and which keys
  673. must be unique.  Note that every record type should normally have at
  674. least one unique key.  If the field is a key, filename is used as the
  675. name of the inverted file to contain the keys.
  676.  
  677.  
  678. 4.2  Header Files
  679.  
  680.      Looking now at the source file rolo.c, in addition to the data
  681. definition header rolo.h, the following header files are included:
  682.  
  683.           #include <blkio.h>
  684.           #include <cbase.h>
  685.           #include <errno.h>
  686.           #include <stdio.h>
  687.           #include <stdlib.h>
  688.  
  689. The header files listed above are normally included by all programs
  690. using the cbase library.  blkio.h is the header for the blkio library. 
  691. It is included to provide the definition of the NULL pointer and the
  692. declaration of the function bexit.  bexit is for use in place of
  693. exit with any library built upon the blkio library, as cbase is.  It
  694. writes out any buffered data for any open block file then calls exit
  695. (see bexit in the blkio reference manual).  cbase.h is the cbase
  696. header file containing all the constant and type definitions and function
  697. declarations for using the cbase library.  <errno.h> contains the
  698. definition of the errno macro as well as definitions of error code
  699. values.  <stdio.h> is the header for the standard buffered
  700. input/output library, and is included for the declarations of functions
  701. such as printf and scanf.  <stdlib.h> is included for the
  702. macros EXIT_SUCCESS and EXIT_FAILURE, which are
  703. implementation defined success and failure codes to be used as the
  704. argument to exit.  <string.h> is included for declarations of the
  705. string and memory functions (e.g., strcpy and memset).
  706.  
  707.  
  708. 4.3 Opening
  709.  
  710.      The first step in accessing an existing cbase is to open it.  Listing
  711. 4.2 shows the code from rolo.c to open the business card cbase. 
  712. rolo is opened with a type argument of "r+" to allow both reading
  713. and writing.  The other arguments are all taken from rolo.h.  On
  714. error cbopen returns the NULL pointer.  If the error was that the
  715. named cbase does not exist, errno will be set to ENOENT.  Here, if
  716. the rolo cbase did not exist it is created and the program continues as
  717. normal.  Note that the cbase must still be opened after it is created. 
  718. For this program there is only one cbase, but most programs would
  719. have multiple cbases to open.
  720.  
  721.   /* open cbase */
  722.   cbp = cbopen(ROLO, "r+", rl_fields, RL_FLDCNT);
  723.   if (cbp == NULL) {
  724.     if (errno == ENOENT) {
  725.       printf("The card file does not exist.
  726.            Creating.\n");
  727.         rs = cbcreate(ROLO, sizeof(rolo_t),
  728.                              rl_fields, RL_FLDCNT);
  729.         if (rs == -1) {
  730.           fprintf(stderr, "*** Error creating rolo.
  731.                Error number %d.\n",  errno);
  732.           bexit(EXIT_FAILURE);
  733.         }
  734.         cbp = cbopen(ROLO, "r+", rl_fields,         
  735.     RL_FLDCNT);
  736.         if (cbp == NULL) {
  737.           printf("*** Error opening rolo.
  738.                Error number %d.\n", errno);
  739.           bexit(EXIT_FAILURE);
  740.         }
  741.       } else {
  742.       printf("*** Error opening rolo.
  743.            Error number %d.\n", errno);
  744.       bexit(EXIT_FAILURE);
  745.     }
  746.   }
  747.  
  748.           Listing 4.2:  Opening a cbase
  749.  
  750.  
  751. 4.4 General Programming Guidelines
  752.  
  753.      The rolo program contains examples of most of the general
  754. concepts needed to use the cbase library.  First is how to use of the
  755. contents of the data definition header files.  Second, when using
  756. locking, before the program pauses for user input all locks are released
  757. to allow other processes to access the cbase.  Also note that the
  758. functions cbgetrcur and cbsetrcur are not used to reposition the
  759. record cursor after the cbase has been unlocked then locked again. 
  760. This is because during the time in which the cbase was unlocked it
  761. could have been modified by another process; the record position saved
  762. before might contain another record or be empty.
  763.  
  764.  
  765. 4.5 Closing
  766.  
  767.      The last step is to close all open cbases.  Listing 4.3 shows this
  768. code from rolo.c.
  769.  
  770.      /* close cbase */
  771.      rs = cbclose(cbp);
  772.      if (rs == -1) {
  773.           printf("*** Error closing rolo.
  774.                Error number %d.\n", errno);
  775.           bexit(EXIT_FAILURE);
  776.      }
  777.  
  778.           Listing 4.3:  Closing a cbase
  779.  
  780.  
  781.  
  782.  
  783.          Appendix A:  Installation Instructions
  784.  
  785.  
  786.      The cbase library is distributed in MS-DOS format on two 5¼
  787. 360K diskettes or one 3½ 720K diskette.  All the files for each library
  788. are contained within a directory bearing the name of that library.
  789.  
  790.      The initial steps for installing each library are the same.  First, a
  791. directory on the target disk must be created for each library to receive
  792. the files for that library.  Second, the files are copied from each
  793. library's directory on a distribution diskette into the appropriate
  794. directory on the target disk.  If an operating system besides MS-DOS is
  795. being used, the systems file converson facilities will need to be used. 
  796. For VP/ix under UNIX, for instance, the copy command would be
  797. used with the /u option to convert the files from MS-DOS text format
  798. to UNIX text format.
  799.  
  800.      The remaining installation instructions for each library, which
  801. should be performed in the directory containing that library, are given
  802. below.  The batch files provided for MS-DOS installation are written
  803. for Borland Turbo C, and because there is so little uniformity among
  804. MS-DOS C compilers, these will have to be modified for use with
  805. another compiler.  The necessary modifications are straightforward; they
  806. are listed at the beginning of the blkio installation batch file.  Also, if a
  807. make utility is available, the UNIX makefiles may be adapted.
  808.  
  809.      As mentioned in chapter 1, cbase is written in strict compliance
  810. with the ANSI C standard.  But since K&R C compilers are still in
  811. wide use, temporary patches have been made to allow compilation
  812. under both ANSI and K&R C.  These alterations are all very minor and
  813. they should cause no inconvenience for the ANSI C user.
  814.  
  815.      Before proceeding to install the libraries, the manx utility should
  816. be compiled and placed in a directory in the path.  manx is used to
  817. generate the reference manuals.
  818.  
  819.  
  820. A1.  The blkio Library
  821.  
  822.                       UNIX
  823.  
  824.      1. Install the boolean header file.
  825.              $ su
  826.              # cp bool.h /usr/include
  827.              # ^d
  828.      2. Build the blkio library.  Examine the makerec file
  829.         afterward for warnings.
  830.              $ make blkio > makerec
  831.      3. Install the blkio library.  This will copy the blkio header
  832.         file blkio.h to /usr/include and the blkio library archive
  833.         to /usr/lib.
  834.              $ su
  835.              # make install
  836.              # ^d
  837.  
  838.  
  839.                      MS-DOS
  840.  
  841.      1. If necessary, modify install.bat (and blkio.rsp) for the
  842.         C compiler being used.
  843.      2. Install the blkio library.
  844.              > install
  845.  
  846.  
  847. A2.  The btree Library
  848.  
  849.                                 UNIX
  850.  
  851.      1. First install the blkio library.
  852.      2. Build the btree library.  Examine the makerec file
  853.         afterward for warnings or errors.
  854.              $ make btree > makerec
  855.      3. Install the btree library.  This will copy btree.h to
  856.         /usr/include and the btree library archive to
  857.         /usr/lib/libbtree.a.
  858.              $ su
  859.              # make install
  860.              # ^d
  861.      4. Make the utilities and example programs.
  862.              $ make util
  863.  
  864.  
  865.                                MS-DOS
  866.  
  867.      1. First install the blkio library.
  868.      2. If necessary, modify install.bat (and btree.rsp) for the
  869.         C compiler being used.
  870.      3. Install the btree library.
  871.              > install
  872.  
  873.  
  874. A3.  The cbase library
  875.  
  876.                                 UNIX
  877.  
  878.      1. First install the blkio, btree, and lseq libraries.
  879.      2. Build the cbase library.  Examine the makerec file
  880.         afterward for warnings or errors.
  881.              $ make cbase > makerec
  882.      3. Install the cbase library.  This will copy cbase.h to
  883.         /usr/include and the cbase library archive to
  884.         /usr/lib/libcbase.a.
  885.              $ su
  886.              # make install
  887.              # ^d
  888.      4. Make the utilities and example programs.
  889.              $ make util
  890.  
  891.  
  892.                                MS-DOS
  893.  
  894.      1. First install the blkio, btree, and lseq libraries.
  895.      2. If necessary, modify install.bat (and cbase.rsp) for the
  896.         C compiler being used.
  897.      3. Install the cbase library.
  898.              > install
  899.  
  900.  
  901. A4.  The lseq Library
  902.  
  903.                                 UNIX
  904.  
  905.      1. First install the blkio library.
  906.      2. Build the lseq library.  Examine the makerec file
  907.         afterward for warnings or errors.
  908.              $ make lseq > makerec
  909.      3. Install the lseq library.  This will copy lseq.h to
  910.         /usr/include and the lseq library archive to
  911.         /usr/lib/liblseq.a.
  912.              $ su
  913.              # make install
  914.              # ^d
  915.      4. Make the utilities and example programs.
  916.              $ make util
  917.  
  918.  
  919.                                MS-DOS
  920.  
  921.      1. First install the blkio library.
  922.      2. If necessary, modify install.bat (and lseq.rsp) for the
  923.         C compiler being used.
  924.      3. Install the lseq library.
  925.              > install
  926.  
  927.  
  928.  
  929.  
  930.  
  931.              Appendix B:  Adding New Data Types
  932.  
  933.  
  934.      Each cbase data type has an associated function used to compare
  935. two data items, thus defining that type.  Pointers to these comparison
  936. functions are stored in an array through which they are accessed by
  937. cbase internally.  The field type (e.g., t_int, t_string) is an index
  938. into this array.  Therefore to add a new data type to cbase, the
  939. comparison function defining that type must be added as well as a type
  940. name macro defined to specify that type.  The steps necessary to add a
  941. new type are as follows:
  942.  
  943.      1. Add the New Comparison Function.
  944.              The file cbcmp.c contains each cbase data type's        
  945.         comparison function.  Add the new comparison function
  946.         in this file.  Comparison functions must be of the format:
  947.  
  948.    int f(const void *p1, const void *p2, size_t n);
  949.  
  950.         p1 and p2 are pointers to the two data items to be compared,
  951.         and n is the length of the data items.  The value returned
  952.         by the function must be less than, equal to, or greater than
  953.         zero if the data item pointed to by p1 is less than, equal to,
  954.         or greater than, respectively, that pointed to by p2.  The C
  955.         standard library function memcmp, for example, fills all these
  956.         requirements.  This function is used only by cbase internally,
  957.         and then only through the comparison function pointer array,
  958.         so all comparison functions should be declared static.
  959.         cbcmp_p is the comparison function pointer array.  Add a
  960.         pointer to the new function as the new last element in this
  961.         array.
  962.  
  963.      2. Add the New Type Name.
  964.              In cbase.h, find the list of cbase data type macros
  965.         (t_char, t_int, t_string, etc.).  These are numbered
  966.         sequentially starting at 1.  Add the new type following
  967.         the last in the list.  Subtracting 1 from any of these
  968.         macros gives the index into the array cbcmp_p for its
  969.         associated comparison function.
  970.  
  971.      3. Increment the Type Count Macro.
  972.              cbase_.h is the private header included only by cbase
  973.         modules.  The macro TYPECNT is defined as the number of
  974.         functions in the cbcmp_p array.  This must be incremented
  975.         by one for each new type added.
  976.  
  977.  
  978.  
  979.  
  980.  Appendix C:  Porting to a New Operating System
  981.  
  982.  
  983.      As stated in chapter 1, while cbase is extremely portable, certain
  984. necessary operations are very operating system independent.  All system
  985. specific code is contained in the blkio library and is further isolated to
  986. only two files within that library.  The steps necessary to port to a new
  987. system are as follows:
  988.  
  989.      1. The Host Macro.
  990.              In blkio_.h, find where the HOST macro is defined.
  991.         Add the new system to the list of supported systems, then
  992.         change the definition of HOST to the new system.
  993.  
  994.      2. The File Descriptor Type.
  995.              In blkio.h, the file descriptor type fd_t is defined
  996.         as a union.  Look at this union to find the member with the
  997.         correct type for the new system.  The name of this member
  998.         will be needed for steps 3 and 4.  If a member with the
  999.         correct type is not included in the union, add a new member.
  1000.  
  1001.      3. Code for File Access.
  1002.              In buops.c, search for every occurrence of the HOST
  1003.         macro.  Each occurence will resemble the following:
  1004.  
  1005.              #if HOST == UNIX
  1006.              .
  1007.              .
  1008.              .
  1009.              #elif HOST == MSDOS
  1010.              .
  1011.              .
  1012.              .
  1013.              #endif
  1014.  
  1015.         For each of these, add another #elif for the new operating
  1016.         system followed by the required code.  buops.c contains all
  1017.         code for opening, closing, seeking, reading, and writing
  1018.         files.  A simple and direct translation to the new system
  1019.         calls should be all that is required.
  1020.  
  1021.      4. Code for File Locking.
  1022.              In lockb.c, do the same as for buops.c.  lockb.c
  1023.         contains the code for file record locking.  If the new
  1024.         operating system does not support file locking lockb.c
  1025.         does not need to be altered.
  1026.  
  1027.  
  1028.  
  1029.  
  1030.                                    Bibliography
  1031.  
  1032.  
  1033. COME79    Comer, D. The Ubiquitous B-tree. ACM Computing
  1034.           Surveys, June 1979.
  1035.  
  1036. HORO76    Horowitz, E. and S. Sahni. Fundamentals of Data
  1037.            Structures. Rockville, MD: Computer Science Press, 1976.
  1038.  
  1039. KERN88    Kernighan, B. and D. Ritchie. The C Programming
  1040.            Language 2nd ed. Englewood Cliffs, NJ: Prentice Hall, 1988.
  1041.