home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / blt2_214.zip / bullet2.inf (.txt) < prev    next >
OS/2 Help File  |  1996-11-23  |  280KB  |  11,498 lines

  1.  
  2. ΓòÉΓòÉΓòÉ 1. Copyright ΓòÉΓòÉΓòÉ
  3.  
  4. 23-Nov-1996 
  5.  
  6.           BULLET is Copyright 1992-96, and is owned by the author, Cornel Huth, 
  7.           and is protected by United States copyright laws and international 
  8.           treaty provisions.  License restrictions apply. 
  9.  
  10.  
  11. ΓòÉΓòÉΓòÉ 2. License Agreement ΓòÉΓòÉΓòÉ
  12.  
  13. Before using this software, BULLET you must agree to the following: 
  14.  
  15.    1. A BULLET license grants you the right to use the BULLET library code on a 
  16.       royalty-free basis according to the terms of this License Agreement. 
  17.  
  18.    2. You are not permitted to operate more than one copy of this software 
  19.       package at one time per license.  For example, if you have ten 
  20.       programmers that have access to the BULLET package at the same time, you 
  21.       are required to have ten BULLET licenses. 
  22.  
  23.    3. There is no restriction on the number of users you may support, and no 
  24.       restriction on the number of different end-user programs you may 
  25.       distribute that use BULLET.  You may allow any number of simultaneous 
  26.       users to use your end-user program. 
  27.  
  28.    4. The dynamic link library, BULLET*.DLL, may be distributed with your 
  29.       end-user program.  No other BULLET product may be distributed without 
  30.       permission (for example, you may not distribute Bullet's import library, 
  31.       BULLET*I.LIB). 
  32.  
  33.    5. You are not permitted to distribute non-executable code containing BULLET 
  34.       code.  This means that may not redistribute BULLET with your program if 
  35.       your program can be used by other programmers to develop executable code. 
  36.       BULLET must be part of an end-user product only.  This means that you 
  37.       cannot provide an overlay or other such external code containing BULLET 
  38.       code if that code is to be used as a programming library for other 
  39.       programmers, from which the other programmers can create programs.  If 
  40.       you require distributing a non-end-user package containing BULLET, you 
  41.       must obtain written permission from the author.  This limitation pertains 
  42.       to the distribution of BULLET library code.  You may, however, develop 
  43.       and distribute your programmer package (ie non-end-user) as you wish, but 
  44.       you may not distribute BULLET library code with that package without 
  45.       written permission.  For example, you may develop class libraries that 
  46.       use the BULLET library code, and distribute those tools that you have 
  47.       written, but you may not include BULLET library code, or BULLET 
  48.       activation methods, in that package.  The programmer using your package 
  49.       would need a BULLET license to make use of your package. 
  50.  
  51.    6. The static link library, BULLET*.LIB, may not be distributed except in 
  52.       executable form as a component in your executable program (EXE). 
  53.       BULLET*.LIB may not be placed into a DLL.  BULLET*.LIB, if part of your 
  54.       license/Option, may only be linked directly to your end-user program. 
  55.       BULLET*.LIB is available with Option C licenses only. 
  56.  
  57.    7. Shareware use is limited to 28 days, and for the sole purpose of 
  58.       evaluating the software.  The BULLET library code may not be distributed 
  59.       in any form without a registered BULLET license.  A BULLET license is 
  60.       obtained only with purchase of a BULLET package, purchased from an 
  61.       authorized BULLET distributor. 
  62.  
  63.    8. A BULLET license is specific to the option level purchased.  License 
  64.       holders with a lower Option may not use any higher level Option code. 
  65.       For example, if you find another product using the Option C DLL, and you 
  66.       have an Option A license, you are not permitted to use the Option C DLL 
  67.       in your development, nor may you distribute any code that is not part of 
  68.       your Option level. 
  69.  
  70.    9. Your end-user program using the BULLET DLL is required to be a 
  71.       copyrighted work, and must contain a valid copyright notice in the form, 
  72.       'Program-name Copyright (C)Year Your-Name', or similar.  No notice of 
  73.       BULLET's copyright need be further specified in your program (in other 
  74.       words, you don't need to mention BULLET, but you may if you wish).  This 
  75.       applies only if you distribute the BULLET DLL with your product. 
  76.       Programs that are linked using the BULLET*.LIB static link library need 
  77.       not display a copyright notice.  In other words, if you must distribute 
  78.       to the Public Domain, where no copyright is desired for your program, you 
  79.       must link using the static link library, and not the DLL. 
  80.  
  81.   10. BULLET is owned by the author, Cornel Huth, and is protected by United 
  82.       States copyright laws and international treaty provisions.  You are not 
  83.       permitted to make copies of this software except for archival purposes. 
  84.  
  85.   11. You may not rent or lease BULLET. You may not transfer this license 
  86.       without the written permission of the author.  If this software is an 
  87.       update or upgrade, you may not sell or give away previous versions. 
  88.  
  89.   12. You may not reverse engineer, decompile, or disassemble this software if 
  90.       the intent or result is to alter the software. 
  91.  
  92.   13. You may not reproduce the printed manual. 
  93.  
  94.   14. There are no expressed or implied warranties with this software. 
  95.  
  96.   15. All liabilities in the use of this software rest with the user. 
  97.  
  98.   16. U.S. Government Restricted Rights.  This software is provided with 
  99.       restricted rights.  Use, duplication, or disclosure by the Government is 
  100.       subject to restrictions as set forth in subparagraph (c)(1)(ii) of the 
  101.       Rights in Technical Data and Computer Software clause at 52.227-7013. The 
  102.       software is owned by Cornel Huth/6402 Ingram Rd/San Antonio Texas 
  103.       78238/USA.  This agreement is governed by the laws of the Great State of 
  104.       Texas, the United States of America, and all other countries of Earth. 
  105.  
  106.  Any questions concerning this License Agreement should be directed to Product 
  107.  Support. 
  108.  
  109.  Failure to comply with any part of this License Agreement may result in 
  110.  license revocation. 
  111.  
  112.  
  113. ΓòÉΓòÉΓòÉ 3. Installation ΓòÉΓòÉΓòÉ
  114.  
  115. Installation instructions are located in the README text file included with 
  116. your package. 
  117.  
  118.  
  119. ΓòÉΓòÉΓòÉ 4. Product Support ΓòÉΓòÉΓòÉ
  120.  
  121. Technical support in the use of Bullet is available for licensed users at the 
  122. 40th Floor BBS or by way of the Internet. 
  123.  
  124. 40th Floor BBS: 
  125. +1(210)684-8065  N-8-1 
  126.  
  127. Internet: 
  128. support@40th.com 
  129. http://www.40th.com 
  130.  
  131. Response time is usually within 24 hours by internet e-mail or if you leave a 
  132. message at the support BBS.  Alternatively, you may post a letter to Cornel 
  133. Huth/6402 Ingram Rd/San Antonio TX 78238/USA. 
  134.  
  135. The latest in-version (2.x) release of BULLET 2 is available for download from 
  136. the BBS by registered users, or $5 by postal mail, or from the web site (ask 
  137. for instructions, if needed).  Re-createable bugs are fixed immediately. 
  138. Report a new program bug and get the fix shipped free (include your mailing 
  139. address with your report, even if it has not changed).  Past bugs are listed at 
  140. the BBS and the web site.  Contact support if you have any questions or 
  141. requests. 
  142.  
  143. For everyone else, or for general information, send e-mail to: 
  144. info@40th.com 
  145.  
  146.  
  147. ΓòÉΓòÉΓòÉ 4.1. Bug Report Form ΓòÉΓòÉΓòÉ
  148.  
  149. When requesting support for possible bug(s) use the following as a guideline: 
  150.  
  151.    1. Include a complete problem description. 
  152.    2. Include sample source of the problem, if necessary (small is best). 
  153.    3. Include necessary data files, include files, etc. 
  154.    4. Include step-by-step procedure to follow in order to recreate the 
  155.       problem. 
  156.  
  157.  Once done, send it to support by way of the BBS, e-mail, or postal mail to 
  158.  addresses listed in Product Support. 
  159.  
  160.  
  161. ΓòÉΓòÉΓòÉ 5. Ordering Information ΓòÉΓòÉΓòÉ
  162.  
  163. To order Bullet by check, bank check, money-order, or cash, use form 
  164. !ORDER.FRM.  For credit card order, use form !ORDER.CC. 
  165.  
  166. Payment Options 
  167.  
  168. Γûá Check, money-order, cash 
  169.  
  170. Your funds have to be in US Dollars and, if by check, have to be drawn on or 
  171. payable through a US bank.  If sending currency, use Registered AirMail. 
  172. Personal checks may require 10 working days to clear.  Most major non-American 
  173. banks have branch banks in the US.  Contact your bank for details.  Direct 
  174. wire-transfer is available: enquire to sales@40th.com. 
  175.  
  176. To order, send payment and the order form !ORDER.FRM to: 
  177.  
  178.    Cornel Huth
  179.    6402 Ingram Rd
  180.    San Antonio, Texas 78238-3915
  181.    USA
  182.  
  183. Γûá Credit card, Eurochecks in DM, other 
  184.  
  185. For credit card orders or for orders that cannot use the Direct-To-Author order 
  186. form, use the special Bullet/BMT Micro order form, or see the file !ORDER.CC in 
  187. the distribution package. 
  188.  
  189. Order Options 
  190.  
  191. Prices are: 
  192.  
  193. Option A is $ 99 for 2-process/100 open files per process, DLL 
  194. Option B is $149 for 32-process/250 open files per process, DLL 
  195. Option C is $199 for unlimited-process/1024 open files per process, DLL and LIB 
  196. Option E is $249 for DOSX32, OS/2, and Win32 versions of Bullet, all in one 
  197. box, at Option C level. 
  198.  
  199. All Bullet 2 versions include a printed manual and standard AirMail shipping at 
  200. no extra charge.  All OS/2 versions include a personal-use REXX interface to 
  201. Bullet at no extra charge. 
  202.  
  203. The BULLET*.DLL may be distributed with your end-user programs.  There are no 
  204. run-time fees and there are no royalty fees.  Refer to the LICENSE AGREEMENT 
  205. for limitations on distributing BULLET*.DLL with a non-end-user package. 
  206.  
  207. Γûá Option A permits two processes to use the Bullet DLL at the same time.  This 
  208. is not a user limit, but a process limit.  This two-process limit means that 
  209. you can start two different programs that use BULLET*.DLL, and have them both 
  210. run at the same time, on the same machine.  Before starting a third program 
  211. that uses BULLET*.DLL, you must end one or both of the previous two.  Most 
  212. programmers should need only 1 Bullet process active at a time.  There's an 
  213. extra process should you need it.  The total number of Bullet DBF and index 
  214. files open at any one time may be up to 100 files per process, total (sum of 
  215. data and index; memo files are not counted against this).  Most programmers 
  216. should not need more than 100 files open at the same time. 
  217.  
  218. Γûá Option B permits 32 processes to use the Bullet DLL at the same time.  The 
  219. total number of Bullet DBF and index files open at any one time may be up to 
  220. 250 files per process, total (sum of data and index; memo files are not counted 
  221. against this). 
  222.  
  223. Γûá Option C permits an unlimited number of processes (limited only by the OS 
  224. itself) to use the Bullet DLL at the same time.  In addition to the BULLET 
  225. dynamic link library (BULLET*.DLL), Option C provides the BULLET static link 
  226. library (BULLET*.LIB), which may be directly linked into your end-user 
  227. executable program so as to not require BULLET*.DLL.  The total number of 
  228. Bullet DBF and index files open at any one time may be up to 1024 files per 
  229. process, total (sum of data and index; memo files are not counted against 
  230. this).  Special-order versions with more than 1024 files are available. 
  231.  
  232. Γûá Option E is as option C, but includes Bullet for DOSX32, OS/2, and Win32 
  233. platforms, all in one box. 
  234.  
  235. Delivery Options 
  236.  
  237. Standard AirMail to all destinations is available at no extra charge.  Shipping 
  238. weight is approximately 2lbs (1kg) and 10x12x1" (25x30x3cm). 
  239.  
  240. Updates 
  241.  
  242. Updates are available for $5 (to cover disk and shipping costs) for any 2.x 
  243. version update at your option level.  Updates may be downloaded from the Bullet 
  244. support BBS at no charge.  Contact Product Support for details, or just send $5 
  245. along with a photocopy of your receipt (for proof of purchase) to the author, 
  246. Cornel Huth. 
  247.  
  248. Note:  All shipments outside of the US may go through your country's Customs. 
  249. Each package is valued at the total price of the order, less any shipping 
  250. costs, if any, unless pre-arranged otherwise.  Your Customs Office may delay 
  251. mail delivery if duty is required. 
  252.  
  253. Click here [Γûá] for the check, money-order, cash order form. 
  254.  
  255. Click here [Γûá] for the credit card order from. 
  256.  
  257.  
  258. ΓòÉΓòÉΓòÉ 5.1. Order Form for Check, Money-order, or Cash ΓòÉΓòÉΓòÉ
  259.  
  260. Printing this section from here may not fit on a single page.  Use the file 
  261. !ORDER.FRM instead, or select the 'Services' menu item from here (upper-left), 
  262. and select 'Copy to file' to print this to .\TEXT.TMP. 
  263.  
  264. Direct-To-Author Order Form.
  265. Bullet 2.1 for OS/2 Order Form for Check, Money-order, or Cash
  266. Details on Options A-C,E are in 'Ordering Information' in the documentation.
  267.  
  268.                                     Cost Per    Number of
  269.                                     License     Licenses    Extended
  270.  --------------------------------------------------------------------
  271.  Option A.  Single-license, 2 processes/100 files per process DLL
  272.                                     $  99.00  x          = $
  273.  -----------------------------------            -------     ---------
  274.  Option B.  Single-license, 32 processes/250 files per process DLL
  275.                                     $ 149.00  x          =
  276.  -----------------------------------            -------     ---------
  277.  Option C.  Single-license, unlimited processes/1024 files per process
  278.             DLL and static link library
  279.                                     $ 199.00  x          =
  280.  -----------------------------------           --------     ---------
  281.  Option E.  Single-license, unlimited processes/1024 files per process
  282.             DLL and static link libraries for DOSX32, OS/2, and Win32
  283.                                     $ 249.00  x          =
  284.  -----------------------------------           --------     ---------
  285.  
  286.                                                  TOTAL  = $
  287.                                                             =========
  288.  
  289. Send TOTAL payment on check (US Dollars/US bank), money-order, or cash to
  290.  
  291.         CORNEL HUTH                       Always endorse your letter
  292.         6402 INGRAM RD                             AirMail
  293.         SAN ANTONIO TX 78238-3915          if you are not in the USA
  294.         USA
  295.  
  296.    PLEASE PRINT - For comments add a separate page and mark here [  ]
  297.  
  298. Your Name>
  299.            -------------------------------------------------- (required)
  300.  
  301.   Company>
  302.            -------------------------------------------------- (optional)
  303.  
  304.   Address>
  305.            -------------------------------------------------------------
  306.  
  307.  
  308.            -------------------------------------------------------------
  309.  
  310.  
  311.            -------------------------------------------------------------
  312.  
  313.     E-mail>                                               Disk
  314.            ---------------------------------------------- Size> --------
  315.  
  316.  Telephone                             Fax
  317.    Number> --------------------------- No.> ----------------------------
  318.  
  319.                                               Today's Date>
  320.            ----------------------------------               ---/----/---
  321.  
  322.  
  323. ΓòÉΓòÉΓòÉ 5.2. Order Form for Credit Card ΓòÉΓòÉΓòÉ
  324.  
  325. Printing this section from here will not fit on a single page.  Use the file 
  326. !ORDER.CC instead, or select the 'Services' menu item from here (upper-left), 
  327. and select 'Copy to file' to print this to .\TEXT.TMP.  Cut-and-paste as 
  328. needed. 
  329.  
  330.    To order Bullet by credit card (or Eurochecks in DM) use this form,
  331.    payable to BMT Micro.  All other orders should use the Direct-To-
  332.    Author order form.  All shipping is done by the author direct to
  333.    you, and is always the current release of Bullet.  All Bullet 2
  334.    versions include a printed manual and include free shipping to all
  335.    destinations (AirMail).
  336.  
  337.                   Mail Orders To: BMT Micro
  338.                                   PO Box 15016
  339.                                   WILMINGTON NC 28408
  340.                                   USA
  341.  
  342.                     Voice Orders: 800AM - 700PM EST (-5 GMT)
  343.                                   (800) 414-4268 (orders only)
  344.                                   (910) 791-7052
  345.  
  346.                       Fax Orders: (910) 350-2937  24 hours / 7 Days
  347.                                   (800) 346-1672  24 hours / 7 Days
  348.  
  349.          Online Orders via modem: (910) 350-8061  10 lines, all 14.4K
  350.                                   (910) 799-0923  Direct 28.8K line
  351.  
  352.      Ordering and general ordering questions:
  353.                          Via AOL: bmtmicro
  354.                          via MSN: bmtmicro
  355.                      Via Prodigy: HNGP66D
  356.                   via Compuserve: 74031,307
  357.                     via Internet: orders@bmtmicro.com
  358.                                   telnet@bmtmicro.com
  359.                                   www.bmtmicro.com
  360.  
  361.  
  362.      We accept Visa, Mastercard, Discover, American Express, Diners
  363.      Club, Carte Blanche, Cashiers Check, Personal Check.   Personal
  364.      checks are subject to clearance.  Eurochecks in DM are welcome.
  365.      DM, Sterling, and US Currency is welcome but send only by
  366.      registered mail, return reciept requested.   We cannot be liable
  367.      for lost cash sent through the mail.
  368.  
  369.      Purchase orders are welcome, subject to approval.   The minimum
  370.      amount is $250.00.
  371.  
  372.      Information for our German customers is explained in the last
  373.      paragraph of this order form.
  374.  
  375.      -------------------------------cut here-----------------------------
  376.      Name:
  377.           ------------------------------------------------------(required)
  378.      Company:
  379.              ---------------------------------------------------(optional)
  380.      Address:
  381.              -------------------------------------------------------------
  382.  
  383.              -------------------------------------------------------------
  384.      City:                                State/Province:
  385.           --------------------------------               -----------------
  386.      Postal/ZIP Code:                     Country:
  387.                      ---------------------        ------------------------
  388.      Phone:
  389.            ---------------------------------------------------------------
  390.      Fax:
  391.          -----------------------------------------------------------------
  392.      E-Mail #1
  393.               ------------------------------------------------------------
  394.      E-Mail #2
  395.               ------------------------------------------------------------
  396.                                         Cost Per    Number of
  397.        ORDERING:  BULLET 2 for OS/2     License     Licenses    Extended
  398.      -----------------------------------            --------    --------
  399.      Option A.  Single-license, 2 processes/100 files per process DLL
  400.                                         $  99.00  x          = $
  401.      -----------------------------------            -------     ---------
  402.      Option B.  Single-license, 32 processes/250 files per process DLL
  403.                                         $ 149.00  x          =
  404.      -----------------------------------            -------     ---------
  405.      Option C.  Single-license, unlimited processes/1024 files per process
  406.                 DLL and static link library
  407.                                         $ 199.00  x          =
  408.      -----------------------------------           --------     ---------
  409.      Option E.  Single-license, unlimited processes/1024 files per process
  410.                 DLL and static link libraries for DOSX32, OS/2, and Win32
  411.                                         $ 249.00  x          =
  412.      -----------------------------------           --------     ---------
  413.                                                  SUB-TOTAL  = $
  414.                                                                 ---------
  415.                  North Carolina Residents add 6% Sales Tax  =  +
  416.                                                                 ---------
  417.                                            BULLET 2  TOTAL  = $
  418.                                                                 =========
  419.      ---------------------------------------------------------------------
  420.      |                                                                   |
  421.      | For credit card payment only                                      |
  422.      |                                                                   |
  423.      | Circle one: VISA / Master / Discover / American Express / Diners  |
  424.      |                                                                   |
  425.      | Credit card number:                                               |
  426.      |                     --------------------------------------------- |
  427.      | Expiration date:                                                  |
  428.      |                  ------------------------------------------------ |
  429.      |                                                                   |
  430.      | Authorization signature:                                          |
  431.      |                          ---------------------------------------- |
  432.      ---------------------------------------------------------------------
  433.      -------------------------------cut here-----------------------------
  434.  
  435.  
  436.                    ORDERING FROM INSIDE GERMANY ONLY
  437.                    =================================
  438.  
  439. Persons in Germany wishing to order shareware may also transfer funds
  440. into our account with Deutsche Bank.   Once the money is deposited you
  441. may either fax a confirmation to us with proof of deposit or wait until
  442. Deutsche Bank notifies us of the transaction (usually 10-18 business days).
  443. Account information is as follows:
  444.  
  445. Deutsche Bank / Frankfurt Branch
  446. Empf╨önger:  Thomas Bradford / BMT Micro
  447. Konto-Nummer: 0860221
  448. Bankleitzahl: 500-700-10
  449.  
  450. When you make the transfer, be sure to put your name and the program you
  451. are registering on the transfer.
  452.  
  453. Current exchange rates can be obtained by sending an email to
  454. dm_to_us@bmtmicro.com.   An automated reply will return todays exchange
  455. rates.
  456.  
  457. It is very important that you send us a completed order form by
  458. either email or fax if you deposit money into this account for a
  459. registration.  Fill the order form out as usual except in the credit
  460. card number field put "DEUTSCHE BANK".   We will file the order and
  461. use it to match against the deposit information we receive from the
  462. bank.
  463.  
  464.                                IMPORTANT!
  465.                                ----------
  466. When you email us your order form, we will reply with an
  467. acknowledgement.   If you do not get an acknowledgement within 24 hours
  468. please send your order again in case it was lost.  This extra bit of
  469. caution can save a lot of confusion.
  470.  
  471. If you are concerned that your order is taking too long to process, feel
  472. free to check with us about the status of your order.   It's important
  473. to all of us that you feel safe doing business with our company and
  474. please feel free to suggest ways we can improve our service to you.
  475.  
  476.  
  477. ΓòÉΓòÉΓòÉ 6. Tutorial ΓòÉΓòÉΓòÉ
  478.  
  479. This tutorial describes how to set up a basic database.  It describes file 
  480. layout and how to use Bullet to enter into and read from the database.  It does 
  481. not describe aspects that do not relate to Bullet, such as how to load list 
  482. boxes, create dialogs, etc.  This tutorial creates a music CD collection 
  483. database. 
  484.  
  485. The tutorial follows. 
  486.  
  487.  
  488. ΓòÉΓòÉΓòÉ 6.1. Tutorial: Output Requirements ΓòÉΓòÉΓòÉ
  489.  
  490. The goal of this tutorial is to create a database for a music CD collection. 
  491. The method used is a suggested method; it is by no means the only way to 
  492. develop a database, and only touches the surface of database programming.  That 
  493. said... 
  494.  
  495. The most important thing to do is to know what output you want.  From that, you 
  496. know what input is needed.  For the 'CD database', I've drawn up a list of 
  497. things that I want: 
  498.  
  499. Title, artist, and year.  Must have those.  Track name (the song name), track 
  500. number, and play time would be essential, too.  How about who was playing on 
  501. the track and instruments they used?  Put that on your list -- it's not on this 
  502. one. 
  503.  
  504. All these could be entered into a single file as a single database record, but 
  505. that would be a pretty poor design.  A single data file may seem to be the 
  506. obvious way, but in the long run, it's not very good at all.  This database 
  507. uses two data files.  This concept -- multiple data files -- is especially 
  508. useful when the database is more complex.  For example, an accounting system 
  509. would employ many physical data files.  It could be done in one, sure, but the 
  510. maintenance would be very difficult. 
  511.  
  512. Two tables are used:  The Title-Artist-Year table (Title/CTAY), and the 
  513. TrackNumber-TrackName-Time table (Tracks/CTTT).  A table is a file composed of 
  514. rows and columns.  A row is a record; a column is a field.  No two rows may be 
  515. exactly alike (what would be the point?).  In addition to the fields listed 
  516. (the Title, Artist, and Year, for instance), each table includes a Code field. 
  517. TrackNumber is included as a field value in the second table since the physical 
  518. ordering of rows cannot be used for this purpose (it could, technically, but it 
  519. is unwise to depend on row order being preserved since there is nothing 
  520. enforcing the physical ordering of records). 
  521.  
  522. The Code field for the Title table (CTAY) is generated by the program and is 
  523. composed of the first 4 characters of the Title, plus the first 4 characters of 
  524. the Artist, plus the year (all four characters of it).  This code is also used 
  525. in the Tracks table (CTTT).  This serves two purposes:  First, it ensures that 
  526. each Track record is unique (with pretty good certainty; without it, it is 
  527. possible that TrackNumber-TrackName-Time alone may occur more than once), and 
  528. second, it can be used as the primary key when being accessed through the 
  529. foreign key in the Title table (see Record Layout Diagrams).  By searching CTAY 
  530. (the Title Table) for a desired title, or artist, or even year, and using the 
  531. Code field from that row, you can match it to the Code in CTTT (Track Table) to 
  532. find the tracks that belong to that title row (or artist, or year -- whatever 
  533. the search was based on).  To speed up searching, indexes are maintained and 
  534. used for lookups.  For a very large CD collection (say, several thousand CD 
  535. titles, and tens of thousands of tracks), the database can still be very 
  536. quickly queried for whatever information you need (and have programmed). 
  537.  
  538.  
  539. ΓòÉΓòÉΓòÉ 6.2. Tutorial: Record Layout ΓòÉΓòÉΓòÉ
  540.  
  541. The Code field is generated by your program.  Other fields are input data.  The 
  542. following are the data record layouts of the DBF files (tag fields not shown). 
  543. The time field may be created as a text field with the form "mm:ss", or could 
  544. even be made into a 'seconds' field.  This database uses the more direct 
  545. "mm:ss" since that's how it's read off the CD. Note that this information is 
  546. already stored digitally on the CD itself (running time), but that would 
  547. require additional coding and is not relevant to this tutorial (but oh, how 
  548. sweet it could be). 
  549.  
  550.  CTAY TABLE
  551. ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
  552. Γöé   CODE     Γöé      TITLE      Γöé     ARTIST      ΓöéYEAR Γöé
  553. Γöé            Γöé                 Γöé                 Γöé     Γöé
  554. ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
  555.  MexiConc1993  Mexican Moon      Concrete Blonde  1993
  556.  No NCran1994  No Need to Argue  Cranberries, The 1994
  557.  MachDeep1972  Machine Head      Deep Purple      1972
  558.  
  559.  
  560.               CTTT TABLE
  561.              ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
  562.              Γöé    CODE    ΓöéTRKΓöé     TRACKNAME      ΓöéTIME Γöé
  563.              Γöé            Γöé   Γöé                    Γöé     Γöé
  564.              ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
  565.               MexiConc1993  01 Jenny I Read         05:18
  566.               MexiConc1993  02 Mexican Moon         05:03
  567.               MexiConc1993  03 Heal It Up           04:21
  568.               MexiConc1993  04 Jonestown            06:06
  569.               MexiConc1993  05 Rain                 03:28
  570.               MexiConc1993  06 I Call It Love       05:15
  571.               MexiConc1993  07 Jesus Forgive Me     05:17
  572.               MexiConc1993  08 When You Smile       04:18
  573.               MexiConc1993  09 Close To Home        03:31
  574.               MexiConc1993  10 One Of My Kind       03:55
  575.               MexiConc1993  11 End Of The Line      04:39
  576.               MexiConc1993  12 Blind Ambition       06:10
  577.               MexiConc1993  13 Bajo La Lune         05:07
  578.               No NCran1994  01 Ode To My Family     04:30
  579.               No NCran1994  02 I Can't Be With You  03:07
  580.               No NCran1994  03 Twenty One           03:08
  581.               No NCran1994  04 Zombie               05:06
  582.               No NCran1994  05 Empty                03:26
  583.               No NCran1994  06 Everything I Said    03:53
  584.               No NCran1994  07 The Icicle Melts     02:54
  585.               No NCran1994  08 Disappointment       04:14
  586.               No NCran1994  09 Ridiculous Thoughts  04:31
  587.               No NCran1994  10 Dreaming My Dreams   03:37
  588.               No NCran1994  11 Yeat's Grave         02:59
  589.               No NCran1994  12 Daffodil Lament      06:09
  590.               No NCran1994  13 No Need To Argue     02:56
  591.               MachDeep1972  01 Highway Star         06:05
  592.               MachDeep1972  02 Maybe I'm A Leo      04:51
  593.               MachDeep1972  03 Pictures Of Home     05:03
  594.               MachDeep1972  04 Never Before         03:56
  595.               MachDeep1972  05 Smoke On The Water   05:40
  596.               MachDeep1972  06 Lazy                 07:19
  597.               MachDeep1972  07 Space Truckin'       04:31
  598.  
  599.  
  600. ΓòÉΓòÉΓòÉ 6.3. Tutorial: Index Files Used ΓòÉΓòÉΓòÉ
  601.  
  602. The index files created for this database are, for CTAY: Title+Artist and 
  603. Artist+Title (the + indicates a compound key made up of more than one field); 
  604. for CTTT: Code+TrackName and TrackName+Code.  With these indexes, searching can 
  605. be made on CD title, by Artist, or by TrackName.  If searching by year is 
  606. required, an additional index could be used, such as Year+Artist+Title (Title 
  607. being used as part of the key in case an Artist releases more than one title in 
  608. a year). 
  609.  
  610. To search by CD title, a title is used as the key.  Even a partial title can be 
  611. used during the actual lookup.  For example, 'Mexican'.  From this, a search is 
  612. made in the Title+Artist index.  The first key starting with 'Mexican' is 
  613. returned.  The index can then be traversed to find any other keys also starting 
  614. with this (by getting the next key).  Since Bullet has high-level access 
  615. routines (GET_FIRST_XB, for example), where a key search returns the data 
  616. record, once a key is found its data is already in memory (the record is 
  617. typically read directly into a structure variable you have set up).  This lets 
  618. you move through the CTAY file getting each record in key order, one at a time 
  619. (one key and record per call to Bullet). 
  620.  
  621. With the data record in memory, the Code field in the record (CTAY table) is 
  622. used as a foreign key into CTTT.  A new search needs to be done, this time on 
  623. the Code+TrackName index of the CTTT table.  With the search key set to 
  624. 'MexiConc1993' (the foreign key field in CTAY of the record that first matched 
  625. 'Mexican'), the Code+TrackName index for CTTT is searched.  The first key 
  626. starting with 'MexiConc1993' is returned, and its data record.  In that record 
  627. is the track name, track number, and track time.  When done with that record, 
  628. the index can be tranversed in-order (forward, or even backward), and, so long 
  629. as the first 12 characters of the Code+TrackName key match 'MexiConc1993', that 
  630. key's record is used.  When the match is no longer true in those first 12 
  631. characters, which in the example data occurs after 13 tracks have been read, it 
  632. means that all matching keys in that index file have been found, and there are 
  633. no others matching the criterion. 
  634.  
  635. The TrackName part of Code+TrackName (the part of the key after the first 12 
  636. characters) is not needed as a match criterion in this case; TrackName is being 
  637. used solely for the purpose of making the key unique to that particular CD 
  638. Title (all Code fields are the same for each trackname on the CD). 
  639.  
  640. A possible additional index file would be a key based on Code, for CTAY (there 
  641. is such an index already for CTTT).  This would allow indexed access to the 
  642. CTAY table when looking for the Title of a track name.  (In other words, you 
  643. have a list of track names of all CDs in the database, in track name order, and 
  644. want to know the CD Title for each track you see.)  Even without an index this 
  645. can be done by using the Code value in CTTT as a sequential search lookup value 
  646. in CTAY (searching for the matching Code value in CTAY).  This additional index 
  647. is not used here. 
  648.  
  649.  
  650. ΓòÉΓòÉΓòÉ 6.4. Tutorial: Creating the Data Files ΓòÉΓòÉΓòÉ
  651.  
  652. Now that the output requirements are known, and the layout of the data records 
  653. of the two data files constructed, and the index access methods designed, the 
  654. database can be created.  Bullet provides routines to create the files of the 
  655. database according to specifications that you supply. 
  656.  
  657. The CTAY (Code-Title-Artist-Year) and CTTT (Code-Track-TrackName-Time) tables 
  658. are to be in this format: 
  659.  
  660.   CTAY TABLE
  661.  ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
  662.  Γöé   CODE     Γöé      TITLE      Γöé     ARTIST      ΓöéYEAR Γöé
  663.  ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
  664.   MexiConc1993  Mexican Moon      Concrete Blonde  1993   (sample record)
  665.      :
  666.  
  667.  CODE      character data, 12 bytes
  668.  TITLE     character data, 32 bytes
  669.  ARTIST    character data, 35 bytes
  670.  YEAR      character data, 4 bytes
  671.  
  672.  
  673.   CTTT TABLE
  674.  ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
  675.  Γöé    CODE    ΓöéTRKΓöé     TRACKNAME      ΓöéTIME Γöé
  676.  ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
  677.   MexiConc1993  01 Jenny I Read         05:18   (sample record)
  678.       :
  679.  
  680.  CODE      character data, 12 bytes
  681.  TRK       character data, 2 bytes
  682.  TRACKNAME character data, 40 bytes
  683.  TIME      character data, 5 bytes
  684.  
  685. The fields are sized primarily on expected need.  Since each DBF record has a 
  686. 1-byte tag field, the sum of the above record layouts, including the tag byte, 
  687. is aligned to an even 4-byte size.  An admirable goal, but not a primary design 
  688. consideration.  CTAY is 84 bytes per record; CTTT is 60 bytes per record. 
  689.  
  690. Data is not entered during the create process.  That comes later.  The create 
  691. is only to make the physical files.  The following is the Bullet code required 
  692. to build the two data files.  The index files are covered later. 
  693.  
  694. typedef struct _CTAY {
  695.  CHAR tag;
  696.  CHAR code[12];
  697.  CHAR title[32];
  698.  CHAR artist[35];
  699.  CHAR year[4];
  700. } CTAY;         // (total CTAY record length is 84 bytes)
  701. CTAY ctayRec;
  702.  
  703. CHAR ctayName[] = "CTAY.DBF";
  704. ULONG ctayID=0;                             // handle of CTAY file
  705. FIELDDESCTYPE ctayFieldList[4];             // 4 fields used by the record
  706. memset(ctayFieldList,0,sizeof(ctayFieldList));  // init unused bytes to 0
  707.  
  708. typedef struct _CTTT {
  709.  CHAR tag;
  710.  CHAR code[12];
  711.  CHAR trk[2];
  712.  CHAR trackName[40];
  713.  CHAR time[5];
  714. } CTTT;         // (total CTTT record length is 60 bytes)
  715. CTTT ctttRec;
  716.  
  717. CHAR ctttName[] = "CTTT.DBF";
  718. ULONG ctttID=0;
  719. FIELDDESCTYPE ctttFieldList[4];
  720. memset(ctttFieldList,0,sizeof(ctttFieldList));
  721.  
  722. // field descriptor info for CTAY
  723.  
  724. strcpy(ctayFieldList[0].fieldName, "CODE");
  725. ctayFieldList[0].fieldType = 'C';
  726. ctayFieldList[0].fieldLen = 12;
  727. ctayFieldList[0].fieldDC = 0;
  728.  
  729. strcpy(ctayFieldList[1].fieldName, "TITLE");
  730. ctayFieldList[1].fieldType = 'C';
  731. ctayFieldList[1].fieldLen = 32;
  732. ctayFieldList[1].fieldDC = 0;
  733.  
  734. strcpy(ctayFieldList[2].fieldName, "ARTIST");
  735. ctayFieldList[2].fieldType = 'C';
  736. ctayFieldList[2].fieldLen = 35;
  737. ctayFieldList[2].fieldDC = 0;
  738.  
  739. strcpy(ctayFieldList[3].fieldName, "YEAR");
  740. ctayFieldList[3].fieldType = 'C';
  741. ctayFieldList[3].fieldLen = 4;
  742. ctayFieldList[3].fieldDC = 0;
  743.  
  744. // field descriptor info for CTTT
  745.  
  746. strcpy(ctttFieldList[0].fieldName, "CODE");
  747. ctttFieldList[0].fieldType = 'C';
  748. ctttFieldList[0].fieldLen = 12;
  749. ctttFieldList[0].fieldDC = 0;
  750.  
  751. strcpy(ctttFieldList[1].fieldName, "TRACK");
  752. ctttFieldList[1].fieldType = 'C';
  753. ctttFieldList[1].fieldLen = 2;
  754. ctttFieldList[1].fieldDC = 0;
  755.  
  756. strcpy(ctttFieldList[2].fieldName, "TRACKNAME");
  757. ctttFieldList[2].fieldType = 'C';
  758. ctttFieldList[2].fieldLen = 40;
  759. ctttFieldList[2].fieldDC = 0;
  760.  
  761. strcpy(ctttFieldList[3].fieldName, "TIME");
  762. ctttFieldList[3].fieldType = 'C';
  763. ctttFieldList[3].fieldLen = 5;
  764. ctttFieldList[3].fieldDC = 0;
  765.  
  766. // create the CTAY file
  767.  
  768. CDP.func = CREATE_DATA_XB;
  769. CDP.filenamePtr = ctayName;
  770. CDP.noFields = 4;
  771. CDP.fieldListPtr = ctayFieldList;
  772. CDP.fileID = 3;
  773. rez = BULLET(&CDP);
  774. if (rez) {
  775.    printf("Failed CTAY create.  Err: %d\n",rez);
  776.    return(rez);
  777. }
  778.  
  779. // create the CTTT file
  780.  
  781. CDP.func = CREATE_DATA_XB;
  782. CDP.filenamePtr = ctttName;
  783. CDP.noFields = 4;
  784. CDP.fieldListPtr = ctttFieldList;
  785. CDP.fileID = 3;
  786. rez = BULLET(&CDP);
  787. if (rez) {
  788.    printf("Failed CTTT create.  Err: %d\n",rez);
  789.    return(rez);
  790. }
  791.  
  792.  
  793. ΓòÉΓòÉΓòÉ 6.5. Tutorial: Opening the Data Files ΓòÉΓòÉΓòÉ
  794.  
  795. Before using a data file it must be opened.  Any DBF file can be opened, 
  796. whether created by Bullet or another program.  Before creating indexes for the 
  797. data files created here, the data files must be open.  The handles of the open 
  798. data files are used in the index file create process. 
  799.  
  800.  
  801. // open CTAY and store handle to ctayID
  802.  
  803. OP.func = OPEN_DATA_XB;
  804. OP.filenamePtr = ctayData;
  805. OP.asMode = READWRITE | DENYNONE;
  806. rez = BULLET(&OP);
  807. if (rez) {
  808.    printf("Failed CTAY file open.  Err: %d\n",rez);
  809.    return(rez);
  810. }
  811. ctayID = OP.handle;
  812.  
  813. // open CTTT and store handle to ctttID
  814.  
  815. OP.func = OPEN_DATA_XB;
  816. OP.filenamePtr = ctttData;
  817. OP.asMode = READWRITE | DENYNONE;
  818. rez = BULLET(&OP);
  819. if (rez) {
  820.    printf("Failed CTTT file open.  Err: %d\n",rez);
  821.    return(rez);
  822. }
  823. ctttID = OP.handle;
  824.  
  825.  
  826. ΓòÉΓòÉΓòÉ 6.6. Tutorial: Creating the Index Files ΓòÉΓòÉΓòÉ
  827.  
  828. Two pairs of index files are used.  For CTAY, Title+Artist and Artist+Title; 
  829. for CTTT, Code+TrackName and TrackName+Code.  These are expected to be unique, 
  830. and so the index files are specified to not accept duplicate keys.  Also, 
  831. rather than using the full field sizes for key expressions, substrings are 
  832. specified.  Long keys affect performance, both in index file size and general 
  833. access speed (smaller is usually better), and using the entire field data does 
  834. little to differentiate the key (for the uniqueness requirement).  If 
  835. DUPS_ALLOWED were specified, even shorter keys could be used, though duplicates 
  836. generally are not desirable.  If there comes a record whose generated key is 
  837. not unique (i.e., it already belongs to another already-added record), either 
  838. modify the data entered so that its key is different, if possible, or redo your 
  839. index files to allow duplicates.  You could also play if 'safe' and permit 
  840. duplicates keys to occur from the outset, knowing that not many duplicates will 
  841. occur, where if they do, you can continue and let Bullet managed the duplicate 
  842. key with an enumerator.  If many duplicates of keys are possible, re-evaluate 
  843. your key expression to make it more unique.  This is done by using a compound 
  844. key with more field components.  Still, the fewer components in a key, and the 
  845. shorter, the better the key is. 
  846.  
  847. Index file creation: 
  848.  
  849. CHAR xTitleName[] = "TITLE.IX3";
  850. CHAR xTitleExp[] = "SUBSTR(TITLE,1,8)+SUBSTR(ARTIST,1,8)";
  851. CHAT xTitleBuffer[16];          // key buffer for later use
  852.                                 // note that if DUPS_ALLOWED, the key buffer must
  853.                                 // provide room for the enumerator (2 bytes more)
  854.  
  855. CHAR xArtistName[] = "ARTIST.IX3";
  856. CHAR xArtistExp[] = "SUBSTR(ARTIST,1,8)+SUBSTR(TITLE,1,8)";
  857. CHAT xArtistBuffer[16];
  858.  
  859. CHAR xCodeName[] = "CODETRK.IX3";
  860. CHAR xCodeExp[] = "CODE+SUBSTR(TRACKNAME,1,12)";
  861. CHAT xCodeBuffer[24];
  862.  
  863. CHAR xTrackName[] = "TRKNAME.IX3";
  864. CHAR xTrackExp[] = "SUBSTR(TRACKNAME,1,12)+CODE";
  865. CHAT xTrackBuffer[24];
  866.  
  867. // create two index files for CTAY
  868.  
  869. CIP.func = CREATE_INDEX_XB;
  870. CIP.filenamePtr = xTitleName;
  871. CIP.keyExpPtr = xTitleExp;
  872. CIP.xbLink = ctayID;            // the handle of the open CTAY file
  873. CIP.sortFunction = NLS_SORT;    // sort key by NLS for proper mixed-case order
  874. CIP.codePage = 0;               // use OS-default code page
  875. CIP.countryCode = 0;            // use OS-default country code
  876. CIP.collatePtr = NULL;          // no need for a special collate table
  877. CIP.nodeSize = 512;             // 512-byte node size (or 1024, 2048 bytes)
  878. rez = BULLET(&CIP);
  879. if (rez) {
  880.    printf("Failed Title index create.  Err: %d\n",rez);
  881.    return(rez);
  882. }
  883.  
  884. CIP.filenamePtr = xArtistName;
  885. CIP.keyExpPtr = xArtistExp;     // other values still valid from above
  886. rez = BULLET(&CIP);
  887. if (rez) {
  888.    printf("Failed Artist index create.  Err: %d\n",rez);
  889.    return(rez);
  890. }
  891.  
  892. // create two index files for CTTT
  893.  
  894. CIP.func = CREATE_INDEX_XB;
  895. CIP.filenamePtr = xCodeName;
  896. CIP.keyExpPtr = xCodeExp;
  897. CIP.xbLink = ctttID;            // as above...
  898. CIP.sortFunction = NLS_SORT;
  899. CIP.codePage = 0;
  900. CIP.countryCode = 0;
  901. CIP.collatePtr = NULL;
  902. CIP.nodeSize = 512;
  903. rez = BULLET(&CIP);
  904. if (rez) {
  905.    printf("Failed Code index create.  Err: %d\n",rez);
  906.    return(rez);
  907. }
  908.  
  909. CIP.filenamePtr = xTrackName;
  910. CIP.keyExpPtr = xTrackExp;      // other values still valid from above
  911. rez = BULLET(&CIP);
  912. if (rez) {
  913.    printf("Failed TrackName index create.  Err: %d\n",rez);
  914.    return(rez);
  915. }
  916.  
  917.  
  918. ΓòÉΓòÉΓòÉ 6.7. Tutorial: Opening the Index Files ΓòÉΓòÉΓòÉ
  919.  
  920. Before using an index file it must be opened.  Only Bullet index files can be 
  921. opened. 
  922.  
  923. // open Title index and store handle to xTitleID
  924.  
  925. OP.func = OPEN_INDEX_XB;
  926. OP.filenamePtr = xTitleName;
  927. OP.asMode = READWRITE | DENYNONE;
  928. OP.xbLink = ctayID;                     // note xbLink
  929. rez = BULLET(&OP);
  930. if (rez) {
  931.    printf("Failed Title index open.  Err: %d\n",rez);
  932.    return(rez);
  933. }
  934. xTitleID = OP.handle;
  935.  
  936. // open Artist index and store handle to artistID
  937.  
  938. OP.func = OPEN_INDEX_XB;
  939. OP.filenamePtr = xArtistName;
  940. OP.asMode = READWRITE | DENYNONE;
  941. OP.xbLink = ctayID;
  942. rez = BULLET(&OP);
  943. if (rez) {
  944.    printf("Failed Artist index open.  Err: %d\n",rez);
  945.    return(rez);
  946. }
  947. xArtistID = OP.handle;
  948.  
  949. // open Code index and store handle to xCodeID
  950.  
  951. OP.func = OPEN_INDEX_XB;
  952. OP.filenamePtr = xCodeName;
  953. OP.asMode = READWRITE | DENYNONE;
  954. OP.xbLink = ctttID;
  955. rez = BULLET(&OP);
  956. if (rez) {
  957.    printf("Failed Code index open.  Err: %d\n",rez);
  958.    return(rez);
  959. }
  960. xCodeID = OP.handle;
  961.  
  962. // open TrackName index and store handle to xTrackNameID
  963.  
  964. OP.func = OPEN_INDEX_XB;
  965. OP.filenamePtr = xTrackName;
  966. OP.asMode = READWRITE | DENYNONE;
  967. OP.xbLink = ctttID;
  968. rez = BULLET(&OP);
  969. if (rez) {
  970.    printf("Failed TrackName index open.  Err: %d\n",rez);
  971.    return(rez);
  972. }
  973. xTrackNameID = OP.handle;
  974.  
  975.  
  976. ΓòÉΓòÉΓòÉ 6.8. Tutorial: Inserting CD Title Data ΓòÉΓòÉΓòÉ
  977.  
  978. The files have been created, and are open.  Data may now inserted into the 
  979. database.  The term 'inserted' is used in contrast to 'added' since Bullet 
  980. 'adds' data only, while it 'inserts' data and key information together (and 
  981. especially, the key is inserted, in order).  The CD data entry would typically 
  982. be done via the keyboard.  This tutorial assumes that this has already been 
  983. done, and that the data items are in the following structure variables: 
  984. ctayRec for CD title data; ctttRec for track data. 
  985.  
  986. First, the CD title record is added to CTAY with a key inserted into the Title 
  987. index and another key into Artist index.  Data entered by the user consists of 
  988. CD title, artist, and year.  The Code field is generated by the program code, 
  989. shown below. 
  990.  
  991. // ctayRec has CD title, artist, and year -- generate Code field data
  992.  
  993. strncpy(ctayRec.code  ,ctayRec.title,4);   // first 4 of title
  994. strncpy(ctayRec.code+4,ctayRec.artist,4);  // first 4 of artist
  995. strncpy(ctayRec.code+8,ctayRec.year,4);    // and 4 of year make the Code field
  996.  
  997. // insert CD title data (one data record added, two index files inserted into)
  998.  
  999. AP[0].func = INSERT_XB;
  1000. AP[0].handle = xTitleID;        // handle of Title index
  1001. AP[0].recNo = 0;                // required
  1002. AP[0].recPtr = &ctayRec;        // the CD title record
  1003. AP[0].keyPtr = xTitleBuffer;    // key buffer
  1004. AP[0].nextPtr = &AP[1];         // since two index files, point to next
  1005. AP[1].handle = xArtistID:       // handle of Artist index
  1006. AP[1].recNo = 0x80000000;       // required
  1007. AP[1].recPtr = &ctayRec;        // the CD title record
  1008. AP[1].keyPtr = xArtistBuffer;   // key buffer
  1009. AP[1].nextPtr = NULL;
  1010.  
  1011. rez = BULLET(&AP[0]);
  1012. if (rez) {                      // rez is not error, but pack index of error
  1013.    rc = AP[abs(rez)-1].stat;
  1014.    printf("Insert failed, pack: %d, err: %d\n",rez,rc);
  1015.    return(rc);
  1016. }
  1017.  
  1018. A CD title has been entered.  That's the first part of the data entry.  Next, 
  1019. each track on that CD needs to be entered.  It needs the Code field data 
  1020. generated above, along with the general track data (track name, etc.). 
  1021.  
  1022.  
  1023. ΓòÉΓòÉΓòÉ 6.9. Tutorial: Inserting CD Track Data ΓòÉΓòÉΓòÉ
  1024.  
  1025. During the CD title insert, a Code field value was generated.  That field value 
  1026. is used for each of the track entries inserted into database for this CD.  The 
  1027. actual track data has already been placed in the ctttRec structure variable (by 
  1028. the main program code, not shown here), and only the Code field of ctttRec 
  1029. needs to be set.  Once done, the track record is inserted, this time into the 
  1030. CTTT data file and the Code and Track index files. 
  1031.  
  1032. //
  1033. // this code section is repeated for each track on the CD
  1034. //
  1035.  
  1036. // copy Code field data from CTAY
  1037.  
  1038. strncpy(ctttRec.code,ctayRec.code,12); // use Title's Code for each Track entry
  1039.  
  1040. // insert track data (one data record added, two index files inserted into)
  1041.  
  1042. AP[0].func = INSERT_XB;
  1043. AP[0].handle = xCodeID;         // handle of Code index
  1044. AP[0].recNo = 0;                // required
  1045. AP[0].recPtr = &ctttRec;        // the track record
  1046. AP[0].keyPtr = xCodeBuffer;     // key buffer
  1047. AP[0].nextPtr = &AP[1];         // since two index files, point to next
  1048. AP[1].handle = xTrackID:        // handle of TrackName index
  1049. AP[1].recNo = 0x80000000;       // required
  1050. AP[1].recPtr = &ctttRec;        // the track record
  1051. AP[1].keyPtr = xTrackBuffer;    // key buffer
  1052. AP[1].nextPtr = NULL;
  1053.  
  1054. rez = BULLET(&AP[0]);
  1055. if (rez) {                      // rez is not error, but pack index of error
  1056.    rc = AP[abs(rez)-1].stat;
  1057.    printf("Insert failed, pack: %d, err: %d\n",rez,rc);
  1058.    return(rc);
  1059. }
  1060.  
  1061. // and repeat for each track on the CD
  1062.  
  1063. The database now contains a complete entry for the one CD.  This process is 
  1064. repeated for each CD that is to entered into the database.  This is the data 
  1065. entry process.  Data retrieval is covered next. 
  1066.  
  1067.  
  1068. ΓòÉΓòÉΓòÉ 6.10. Tutorial: Retrieving Data ΓòÉΓòÉΓòÉ
  1069.  
  1070. The database can be accessed in several different ways.  For indexed access, 
  1071. any index file can be used.  Alternatively, direct access without an index 
  1072. could also be used.  Generally, since there's no guarantee that the physical 
  1073. order of rows in the database is in any order at all, index access is usually 
  1074. desired.  Possible retrievals are by: 
  1075.  
  1076. 1. CD title, in alphabetical order, and, from information from the CD title, 
  1077. all tracks on the CD.  The tracks are read into an array and sorted by track 
  1078. number.  Alternatively, a separate index could be used to order track entries 
  1079. by TrackNo+TrackName(+etc.), but since there are so few tracks per CD 
  1080. (generally less than 20), it's much more efficient to simply get all tracks for 
  1081. a CD title into an array, and then sort that array for presentation. 
  1082. Otherwise, an index file would need to be maintained on a permanent basis, and 
  1083. for something that can easily be done at run-time (and then discarded). 
  1084.  
  1085. 2. Artist, in alpha order.  Access is similar to CD title, above, since it uses 
  1086. the same data file (CTAY).  As above, tracks for each artist's title can then 
  1087. be retrieved.  The only difference between this and the CD title access is that 
  1088. this lists CDs by artist, rather than by title. 
  1089.  
  1090. 3. Track name, in alphabetical order.  This lists all tracks by track name, in 
  1091. order, with different tracks intermixed with CD titles.  In other words, if 
  1092. three CDs had the track called "In the Summer Time", then each of those tracks 
  1093. would appear together, one after the other.  There is no index available in 
  1094. this database to access the CD on which the track name appears, so you cannot 
  1095. go from trackname alone to CD title (by index).  However, since the Code field 
  1096. is available, and is common to each data file, the Code field value of a track 
  1097. record can be used to sequentially search the Title records for a match.  This 
  1098. is a non-indexed search.  Access is slower than if an index were available, but 
  1099. unless you created indexes for all possible search methods, some sequential 
  1100. (read: slow) searching may be required.  For smaller files (less than 10,000 
  1101. records), or where only a few such searches occur, this may be perfectly 
  1102. acceptable. 
  1103.  
  1104. An index on Code exists (for CTTT, but not CTAY), and you may find a use for 
  1105. this other than for use as a foreign key lookup.  Also, you may create ad hoc 
  1106. index files whenever you need to, and these can be deleted when no longer 
  1107. required.  Since Bullet can create an index for even a large file in just a few 
  1108. seconds, this is a viable option; the few seconds needed to create an index may 
  1109. be many times repaid in the time saved in sequential searches on a non-indexed 
  1110. access. 
  1111.  
  1112. The first retrieval mentioned is shown next. 
  1113.  
  1114. // for each CD title in the database, display all its tracks
  1115.  
  1116.                         // ctayRec already defined
  1117. CTTT cttyRecs[59];      // store each Track record for later sorting by track#
  1118.  
  1119. // This would be repeated for each CD title (only the first is shown).
  1120. // Note: For the next CD title, GET_NEXT_XB would be used.  If necessary,
  1121. // the key just accessed could be stored (preserved), then laster reaccessed
  1122. // using GET_EQUAL_XB again, and then immediately followed with a GET_NEXT_XB.
  1123. // This way, you can stop processing completely, and restart up where you left
  1124. // off.
  1125.  
  1126. // files are locked as required (e.g., full-lock, shared)
  1127.  
  1128. AP.func = GET_FIRST_XB;
  1129. AP.handle = xTitleID;
  1130. AP.recPtr = &ctayRec;
  1131. AP.keyPtr = xTitleBuffer;
  1132. rez = BULLET(&AP);
  1133. while (rez==0) {
  1134.    printf("Title: %s  Artist: %s  Year: %s\n",
  1135.            ctayRec.title,
  1136.            ctayRec.artist,
  1137.            ctayRec.year);
  1138.  
  1139.    // get each track belonging to this CD title (same Code values)
  1140.  
  1141.    int trk=0;                            // counter
  1142.    memset(xCodeBuffer,0,sizeof(xCodeBuffer)); // clear it to 0
  1143.    strncpy(xCodeBuffer,ctayRec.code,12); // copy Code to search-for buffer
  1144.  
  1145.    AP.func = GET_EQUAL_XB;
  1146.    AP.handle = xCodeID;
  1147.    AP.recPtr = &ctttRecs[trk];
  1148.    AP.keyPtr = xCodeBuffer;             // find Code
  1149.    rez = BULLET(&AP);
  1150.  
  1151.    // rather than print each when gotten, collect them, sort, then display
  1152.    // if rez==0 here then Code matched exactly, no strncmp needed, otherwise,
  1153.    // there's no match on the first 12 characters of Code
  1154.  
  1155.    AP.func = GET_NEXT_XB;               // continue getting while same Code
  1156.    while (rez==0) {
  1157.       trk++;                            // limit to array size...
  1158.       AP.recPtr = &ctttRecs[trk];       // read into next array record
  1159.       rez = BULLET(&AP);
  1160.       if (rez==0) {
  1161.  
  1162.          // if code field in CTTT no longer the same as CTAY, then have them all
  1163.  
  1164.          if (strncmp(ctttRecs[trk].code,ctayRec.code,12)!=0) {
  1165.             rez = 1;
  1166.             break;                      // different Code fields
  1167.          }
  1168.       }
  1169.    }
  1170.  
  1171.    // if rez==1 or at end of file then no error, sort and display --
  1172.    // trk at this point is number of tracks that matched Code (1-based)
  1173.    // DoSortOf... routine sorts the ctttRecs array with trk count of elements
  1174.  
  1175.    if ((rez==1) | (rez==EXB_END_OF_FILE)) {
  1176.       DoSortOfTheTrackRecordsByTrackNumber(&ctttRecs[0],trk);
  1177.  
  1178.       // print each track record, now sorted by track number
  1179.  
  1180.       for (i=0;i < trk;i++) {
  1181.          printf("Trk: %s  Name: %s   Time: %s\n",
  1182.                  ctttRecs[i].trk,
  1183.                  ctttRecs[i].trackName,
  1184.                  ctttRecs[i].time);
  1185.       }
  1186.    else {
  1187.        printf("failed, rez: %d\n",rez);
  1188.        return(rez);
  1189.    }
  1190. }
  1191.  
  1192.  
  1193. ΓòÉΓòÉΓòÉ 6.11. Tutorial: Preparing to End Your Program ΓòÉΓòÉΓòÉ
  1194.  
  1195. It's proper programming practice to perform an orderly shutdown of Bullet when 
  1196. ending your program.  This also extends to files, too, where even if you are 
  1197. not ending your program, but are finished using a file, close that file so that 
  1198. you release the resources allocated for it. 
  1199.  
  1200. In the event that an abnormal termination occurs, and you are unable to release 
  1201. outstanding locks and close all files, you may choose to call EXIT_XB and allow 
  1202. that to close files (the OS releases locks when the file is closed).  If you 
  1203. are unable to call even EXIT_XB, there is still another method that gets 
  1204. Bullet's attention.  During Bullet initialization, the EXIT_XB routine is 
  1205. registered with the OS so that whenever your program terminates (or is 
  1206. terminated), EXIT_XB is called automatically.  This ensures that headers are 
  1207. properly written in all but the most severe of abnormal terminations (abend), 
  1208. such as a lockup. 
  1209.  
  1210. // all outstanding locks should have been released immediately after use
  1211.  
  1212. // close each file that was opened
  1213.  
  1214. HP.func = CLOSE_DATA_XB
  1215. HP.handle = xTitleID;
  1216. if (HP.handle) rez = BULLET(&HP);
  1217. if (rez) printf("close failed, err: %d\n",rez);
  1218.  
  1219. HP.handle = xArtistID;
  1220. if (HP.handle) rez = BULLET(&HP);
  1221. if (rez) printf("close failed, err: %d\n",rez);
  1222.  
  1223. HP.handle = xCodeID;
  1224. if (HP.handle) rez = BULLET(&HP);
  1225. if (rez) printf("close failed, err: %d\n",rez);
  1226.  
  1227. HP.handle = xTrackID;
  1228. if (HP.handle) rez = BULLET(&HP);
  1229. if (rez) printf("close failed, err: %d\n",rez);
  1230.  
  1231. EP.func = EXIT_XB;
  1232. rez = BULLET(&EP);
  1233. if (rez) printf("exit failed, err: %d\n",rez);
  1234.  
  1235. // Bullet is now deinitialized, and must be INIT_XB'ed before further use
  1236.  
  1237.  
  1238. ΓòÉΓòÉΓòÉ 7. History of Changes ΓòÉΓòÉΓòÉ
  1239.  
  1240. Changes Made 
  1241.  
  1242. 2.13 23-Nov-96: 
  1243.  
  1244.    1. Added EQUAL_OR_GREATER_KEY/LESSER_KEY for fuzzy key access (atomic). 
  1245.    2. Added GET_EQUAL_OR_GREATER/LESSER for fuzzy key+record access (atomic). 
  1246.    3. Increased maximum fields from 255 to 1024 for dBASE 5 DBFs. 
  1247.    4. Update of data record would not set DBF dirty flag, though effect was 
  1248.       minimal (last update timestamp in DBF header not set). 
  1249.    5. Fixed Bullet/2 REXX interface DLL dispatcher. 
  1250.  
  1251.  2.12 6-Nov-96: 
  1252.  
  1253.    1. New, lower prices!  Order direct-from-author or from BMT Micro. 
  1254.    2. New source example, compact.c, to compact DBFs with attached DBT memo 
  1255.       file. 
  1256.    3. Release 2.12.  Code version is 2.11. 
  1257.  
  1258.  2.11 16-Oct-96: 
  1259.  
  1260.    1. New order option, option E, includes DOSX32, OS/2 and Win32 versions of 
  1261.       Bullet all in one box. 
  1262.  
  1263.  2.105 11-Oct-96: 
  1264.  
  1265.    1. Added OS function override revectoring for all OS calls made, and added 
  1266.       supporting query/set routines, QUERY/SET_VECTORS_XB. 
  1267.    2. Added USE_ANSI_SET flag for index file collate sequence table (esp. for 
  1268.       Windows), in addition to standard OEM character set, for use at 
  1269.       CREATE_INDEX_XB. 
  1270.    3. Added shared/exclusive region locking mode for NT. 
  1271.    4. UPDATE_XB efficiency improved, esp. with large number of index files. 
  1272.    5. Internal sort efficiency improved (even faster). 
  1273.    6. Improved demo program samples; also added compile option for generating 
  1274.       console or windowed app. 
  1275.    7. Consolidated Bullet header files into one, for all platforms, with 
  1276.       minimal conditionals; the file is named bullet_2.h. 
  1277.  
  1278.  2.103 4-Oct-1996: 
  1279.  
  1280.    1. Win32 version would fail under NT (and Win32s) because of un-saved 
  1281.       register into consecutive kernel calls. 
  1282.    2. Win32s now supported. 
  1283.    3. Win32 version now in a single DLL (supports Watcom (old and new) and VC4, 
  1284.       both, and both Win32 and Win32s). 
  1285.    4. CREATE_INDEX_XB was always using OS-supplied codepage/country code. 
  1286.    5. Added callback support in reindex and pack records, esp. useful for 
  1287.       DOSX32 and Win32s. 
  1288.    6. UPPER() in key expression would fail. 
  1289.  
  1290.  2.101 28-Aug-1996: 
  1291.  
  1292.    1. REINDEX_XB would fail on certain key sizes, causing an access 
  1293.       violation/GPF. 
  1294.  
  1295.  2.100 3-Aug-1996: 
  1296.  
  1297.    1. REINDEX_XB and GET_LAST_XB could fail if DUPS_ALLOWED. 
  1298.    2. Sort functions 1, 2, and 3 were operating as expected but 4-6 were using 
  1299.       3. 
  1300.    3. Node scanning was using the wrong sort-compare function (1 was using 3, 2 
  1301.       was using 1, and 3 was using 2). 
  1302.    4. UPDATE_XB was returning failed pack's AP[].stat=1 instead of true error 
  1303.       code if the update failed when inserting a new key. 
  1304.    5. BUILD_KEY_XB was not issuing skip tag warning, and was not passing 
  1305.       .recPtr to external build key function. 
  1306.    6. PACK_RECORDS_XB and DEBUMP_RECORD_XB values in bullet2.h were 
  1307.       interchanged: Pack is 47; Debump is 46. 
  1308.    7. DEBUMP_RECORD_XB requires FLUSH_DATA_HEADER_XB call prior to use. 
  1309.    8. Documentation errata: GET_DESCRIPTOR_XB uses DP.handle for IN; includes 
  1310.       DP.FD.altFieldLength for OUT (DP.fieldOffset noted previously) 
  1311.    9. GET_DESCRIPTOR_XB was not returning info in DP.FD.* when accessed by 
  1312.       fieldName. 
  1313.   10. SDP.lastUpdate year was returned with year-256 (1996 was 1740). 
  1314.   11. GET_ERROR_CLASS_XB was not returning useful info. 
  1315.   12. ZAP_INDEX_HEADER_XB had been making index data start at 16KB size. 
  1316.   13. DosExitList() handler is now dropped after calling EXIT_XB. 
  1317.  
  1318.  2.051 24-Apr-96: 
  1319.  
  1320.    1. Fixes to transaction routines InsertXB and UpdateXB, including Insert 
  1321.       rollback fix with multiple packs. 
  1322.    2. Update pack number return value fix. 
  1323.    3. Update w/ multiple packs fix, and an Update rollback fix. 
  1324.    4. Fix of GetKeyForRecordXB when DUPS_ALLOWED. 
  1325.  
  1326.  2.050 26-Feb-96: 
  1327.  
  1328.    1. Shareware refresh released. 
  1329.  
  1330.  2.044 31-Jan-96: 
  1331.  
  1332.    1. Fixed DUPS_ALLOWED bug at INSERT_XB where error EXB_TOP_OF_FILE or error 
  1333.       EXB_TOO_MANY_DUPLICATES would be returned. 
  1334.  
  1335.  2.043  3-Jan-96: 
  1336.  
  1337.    1. Added atomic key access for NEXT_KEY, PREV_KEY, GET_NEXT, and GET_PREV 
  1338.       for simpler use in multi-threaded programs. 
  1339.    2. Changed EXB_SHARED_LOCK_ON in BULLET2.H (was ERR_ rather than EXB_). 
  1340.  
  1341.  2.042 20-Dec-95: 
  1342.  
  1343.    1. MAKE_DIR_DOS was expecting directory name in DFP.bufferPtr.  Changed to 
  1344.       expect path in DFP.filenamePtr. 
  1345.  
  1346.  2.040 28-Oct-95: 
  1347.  
  1348.    1. BACKUP_FILE_XB extended to back up related memo file on DBF backup. 
  1349.    2. [2.033] Fixed memo file problems. 
  1350.    3. [2.032] Win95 version available. 
  1351.    4. [2.031] DOSX32 version available. 
  1352.  
  1353.  2.031 11-Sep-95: 
  1354.  
  1355.    1. Added DP.fieldOffset to documentation for GET_DESCRIPTOR_XB. 
  1356.    2. Instance tracker purified of DosEnterCritSec need. 
  1357.  
  1358.  2.030 9-Sep-95: 
  1359.  
  1360.    1. Removed erroneous '...header reload...' in online docs of RELOCK_DATA_XB. 
  1361.    2. Default maximum filesizes set to 2047 MB from 2048 MB (absolute max is 
  1362.       4095 MB). 
  1363.    3. Fixed REINDEX_XB so that it actually uses re-evaluted key expression. 
  1364.    4. ATEXIT_XB is obsolete. 
  1365.    5. BREAK_XB is obsolete. 
  1366.  
  1367.  2.020 31-Aug-95: 
  1368.  
  1369.    1. Locality and other cache-related options can be set at file open. 
  1370.    2. IP.versionBullet changed to *1000 from *100 (2020 is version 2.020). 
  1371.    3. Instance tracker corrected. 
  1372.  
  1373.  2.01  22-Aug-95: 
  1374.  
  1375.    1. Changed index header to 1024 bytes from 768 for sector-alignment. 
  1376.    2. Changed header ID to '31ch' from '30ch'. 
  1377.  
  1378.  2.00  20-Aug-95: 
  1379.  
  1380.  Preliminary release. 
  1381.  
  1382.  
  1383. ΓòÉΓòÉΓòÉ 8. Bullet Include File ΓòÉΓòÉΓòÉ
  1384.  
  1385. The Bullet/2 C/C++ header file, BULLET_2.H: 
  1386.  
  1387.  
  1388. /* BULLET_2.H    23-Nov-96-chh
  1389.  *
  1390.  *  Bullet header for 32-bit C/C++ (DOSX32, OS/2, and Win32s/Win32)
  1391.  *  Bullet call numbers, parameter packs, and error number equates
  1392.  *
  1393.  *  Requires PLATFORM defined and set to ON_DOSX32 (3), ON_OS2 (4),
  1394.  *  or ON_WIN32 (5) before getting here.  For example:
  1395.  *    #define PLATFORM ON_DOSX32  (ON_DOSX32 defined as 3)
  1396.  *
  1397.  */
  1398.  
  1399. #ifndef __BULLET_H
  1400. #define __BULLET_H
  1401.  
  1402. /*
  1403.  * The #pragma pack(1)/#pragma pack() is no longer required since all
  1404.  * structure members in this header will align properly -- all are
  1405.  * 32-bit size except for the structure "FieldDescType", but fieldDA
  1406.  * member (a LONG) is at a 32-bit alignment already (at byte offset +12).
  1407.  * The altFieldLength member, same structure, is also already at
  1408.  * proper alignment for a 16-bit value (at byte offset +18).  If, for
  1409.  * some reason, your compiler aligns the members differently, then you
  1410.  * must use the appropriate compiler pragma to prevent this -- the
  1411.  * FieldDescType size is 32 bytes exactly.  It is not likely that any
  1412.  * conforming compiler will alter this structure, but, now you know what
  1413.  * to do if it does.
  1414.  *
  1415.  * #pragma pack(1)
  1416.  *
  1417.  * NOTE: In your program source code, when you layout your record buffer
  1418.  * structure, you must use the #pragma pack(1)/#pragma pack() directives
  1419.  * around it since it will be, most likely, modified.  The reason is that
  1420.  * this structure MUST start with the implicit TAG field (a BYTE), and so,
  1421.  * unless you use only BYTE/CHAR members in your structure (Bullet can use
  1422.  * binary field values), or take special care to align the record layout
  1423.  * so no padding is performed by the compiler, then you will need to use
  1424.  * the pack(1) pragma.
  1425.  *
  1426.  * #pragma pack()
  1427.  */
  1428.  
  1429. /* Re: Bullet/X for DOSX32:
  1430.  * (refer to ccdosfn.c for more)
  1431.  * Bullet ccdosfn.c provides no OS call support to determine the
  1432.  * system country code and code page ID.  This can be coded in
  1433.  * ccdosfn.c, but it is quite compiler- and extender-dependent.
  1434.  * The current state is to supply a collate sequence table for
  1435.  * country code=1 and code page=437 (the table is statically coded).
  1436.  * For other sort tables, modify as required.  If support for this
  1437.  * at the system level is made available at run-time, you may
  1438.  * want to change the following to 0, for both CTRYCODE and CODEPAGE.
  1439.  * Until this is so coded, you cannot use 0 here (as with the Win32
  1440.  * and OS/2 versions), else error EXB_216501 (8251) is the result.
  1441.  * This table is added to each index file (if NLS or a user sort).
  1442.  */
  1443.  
  1444. #ifndef PLATFORM
  1445.  #error No PLATFORM specified
  1446.  #error ---------------------
  1447. #endif
  1448.  
  1449. #ifndef ON_DOSX32
  1450.  #define ON_DOSX32 3
  1451.  #define ON_OS2    4
  1452.  #define ON_WIN32  5
  1453. #endif
  1454.  
  1455. #if PLATFORM == ON_DOSX32
  1456.  #define CTRYCODE 1     /* 0 signifies default country code (at index create) */
  1457.  #define CODEPAGE 437   /* 0 signifies default code page (at index create) */
  1458.                         /* but DOS extender may not support OS call to get info */
  1459.                         /* see ccdosfn.c for making changes for DOSX32 platform */
  1460.  #define RELOCK_AVAIL 0 /* relock not supported */
  1461.  
  1462.  #define VOID void      /* these are already defined if Win32 or OS/2, but not DOS */
  1463.  #define SHORT short
  1464.  #define LONG long
  1465.  #define CHAR char
  1466.  
  1467.  typedef unsigned char BYTE;
  1468.  typedef unsigned short USHORT;
  1469.  typedef unsigned long ULONG;
  1470.  typedef unsigned char *PSZ;
  1471.  typedef VOID *PVOID;
  1472.  
  1473.  #define APIENTRY __cdecl
  1474.  
  1475. #elif PLATFORM == ON_OS2
  1476.  #define CTRYCODE 0
  1477.  #define CODEPAGE 0
  1478.  #define RELOCK_AVAIL 1    /* relock is supported */
  1479.  
  1480.  /* above types are assumed defined in os2def.h */
  1481.  
  1482. #elif PLATFORM == ON_WIN32
  1483.  #define CTRYCODE 0
  1484.  #define CODEPAGE 0     /* may be ANSI or OEM code page value, depending on USE*CHARSET flag */
  1485.  #define RELOCK_AVAIL 0 /* relock not supported */
  1486.  
  1487.  /* above types are assume defined in windef.h and winnt.h */
  1488.  
  1489. #else
  1490.  #error No PLATFORM specified
  1491.  #error ---------------------
  1492.  
  1493. #endif
  1494.  
  1495. #ifndef __BLT_DYNA    // define this if using run-time loading of BULLET*.DLL
  1496.                       // via LoadLibrary(Win32) or DosLoadModule(OS/2)
  1497.  #ifdef __cplusplus
  1498.   extern "C" LONG APIENTRY BULLET(PVOID datapack);
  1499.  #else
  1500.   extern LONG APIENTRY BULLET(PVOID datapack);
  1501.  #endif
  1502.  
  1503. #else
  1504.  
  1505.   LONG (* APIENTRY BULLET)(PVOID datapack);
  1506.  
  1507. #endif
  1508.  
  1509.  
  1510.  
  1511. /* The following on mutex-semaphore protection does not apply to Bullet/X */
  1512. /* unless the semaphore routines (in ccdosfn.c) are coded to do something */
  1513.  
  1514. /* All Bullet routines are mutex-semaphore protected except the following:
  1515.  *
  1516.  * MEMORY_XB            STAT_HANDLE_XB          GET_ERROR_CLASS_XB
  1517.  * QUERY_SYSVARS_XB     QUERY_VECTORS_XB        CHECK_REMOTE_XB
  1518.  * STAT_DATA_XB         STAT_INDEX_XB
  1519.  *
  1520.  * This means that any thread can call the above routines at any time.  All
  1521.  * other calls in the current process block until the previous thread exits
  1522.  * BULLET.  The default mutex wait is 0 milliseconds, and can be set via
  1523.  * SET_SYSVARS_XB using the MUTEX_SEM_TIMEOUT index.  In the case of
  1524.  * STAT_DATA_XB and STAT_INDEX_XB, these should be used only when there
  1525.  * is no chance that another thread may close that file handle while the
  1526.  * routine is working.
  1527.  *
  1528.  */
  1529.  
  1530.  
  1531.  
  1532. /* ************************************************************************
  1533.  *
  1534.  * xxx.func call numbers
  1535.  *
  1536.  * ************************************************************************/
  1537.  
  1538. #define GEN_ERR_XB              0
  1539. #define INIT_XB                 1  /* system */
  1540. #define EXIT_XB                 2
  1541. #define MEMORY_XB               4
  1542. #define BACKUP_FILE_XB          6
  1543. #define STAT_HANDLE_XB          7
  1544. #define GET_ERROR_CLASS_XB      8
  1545.  
  1546. #define QUERY_SYSVARS_XB        10 /* advanced system */
  1547. #define SET_SYSVARS_XB          11
  1548. #define SET_DVMON_XB            12 /* reserved */
  1549. #define QUERY_VECTORS_XB        13
  1550. #define SET_VECTORS_XB          14
  1551.  
  1552. #define CREATE_DATA_XB          20 /* data control mid-level */
  1553. #define OPEN_DATA_XB            21
  1554. #define CLOSE_DATA_XB           22
  1555. #define STAT_DATA_XB            23
  1556. #define READ_DATA_HEADER_XB     24
  1557. #define FLUSH_DATA_HEADER_XB    25
  1558. #define COPY_DATA_HEADER_XB     26
  1559. #define ZAP_DATA_HEADER_XB      27
  1560.  
  1561. #define CREATE_INDEX_XB         30 /* key control mid-level */
  1562. #define OPEN_INDEX_XB           31
  1563. #define CLOSE_INDEX_XB          32
  1564. #define STAT_INDEX_XB           33
  1565. #define READ_INDEX_HEADER_XB    34
  1566. #define FLUSH_INDEX_HEADER_XB   35
  1567. #define COPY_INDEX_HEADER_XB    36
  1568. #define ZAP_INDEX_HEADER_XB     37
  1569.  
  1570. #define GET_DESCRIPTOR_XB       40 /* data access mid-level */
  1571. #define GET_RECORD_XB           41
  1572. #define ADD_RECORD_XB           42
  1573. #define UPDATE_RECORD_XB        43
  1574. #define DELETE_RECORD_XB        44
  1575. #define UNDELETE_RECORD_XB      45
  1576. #define DEBUMP_RECORD_XB        46
  1577. #define PACK_RECORDS_XB         47
  1578.  
  1579. #define GET_MEMO_SIZE_XB        50 /* memo access mid-level */
  1580. #define GET_MEMO_XB             51
  1581. #define ADD_MEMO_XB             52
  1582. #define UPDATE_MEMO_XB          53
  1583. #define DELETE_MEMO_XB          54
  1584. #define MEMO_BYPASS_XB          59 /* see below for bypass ordinals */
  1585.  
  1586. #define BYPASS_CREATE_MEMO       1 /* The bypass routines are automatically */
  1587. #define BYPASS_OPEN_MEMO         2 /* performed by BULLET but can be done */
  1588. #define BYPASS_CLOSE_MEMO        3 /* manually, if needed - these numbers are */
  1589. #define BYPASS_READ_MEMO_HEADER  4 /* put in MDP.memoBypass, with MDP.func */
  1590. #define BYPASS_FLUSH_MEMO_HEADER 5 /* set to MEMO_BYPASS_XB */
  1591.  
  1592.  
  1593.  
  1594. #define FIRST_KEY_XB            60 /* key access mid-level */
  1595. #define EQUAL_KEY_XB            61
  1596. #define EQUAL_OR_GREATER_KEY_XB 110
  1597. #define EQUAL_OR_LESSER_KEY_XB  111
  1598. #define NEXT_KEY_XB             62
  1599. #define PREV_KEY_XB             63
  1600. #define LAST_KEY_XB             64
  1601. #define STORE_KEY_XB            65
  1602. #define DELETE_KEY_XB           66
  1603. #define BUILD_KEY_XB            67
  1604. #define GET_CURRENT_KEY_XB      68
  1605. #define GET_KEY_FOR_RECORD_XB   69
  1606.  
  1607. #define GET_FIRST_XB            70 /* key and data access high-level */
  1608. #define GET_EQUAL_XB            71
  1609. #define GET_EQUAL_OR_GREATER_XB 112
  1610. #define GET_EQUAL_OR_LESSER_XB  113
  1611. #define GET_NEXT_XB             72
  1612. #define GET_PREV_XB             73
  1613. #define GET_LAST_XB             74
  1614. #define INSERT_XB               75
  1615. #define UPDATE_XB               76
  1616. #define REINDEX_XB              77
  1617.  
  1618. #define LOCK_XB                 80 /* network control */
  1619. #define UNLOCK_XB               81
  1620. #define LOCK_INDEX_XB           82
  1621. #define UNLOCK_INDEX_XB         83
  1622. #define LOCK_DATA_XB            84
  1623. #define UNLOCK_DATA_XB          85
  1624. #define CHECK_REMOTE_XB         86
  1625. #define RELOCK_XB               87
  1626. #define RELOCK_INDEX_XB         88
  1627. #define RELOCK_DATA_XB          89
  1628.  
  1629. #define DELETE_FILE_DOS         90 /* DOS file I/O low-level */
  1630. #define RENAME_FILE_DOS         91
  1631. #define CREATE_FILE_DOS         92
  1632. #define OPEN_FILE_DOS           93
  1633. #define SEEK_FILE_DOS           94
  1634. #define READ_FILE_DOS           95
  1635. #define WRITE_FILE_DOS          96
  1636. #define CLOSE_FILE_DOS          97
  1637. #define ACCESS_FILE_DOS         98
  1638. #define EXPAND_FILE_DOS         99
  1639. #define MAKE_DIR_DOS            100
  1640. #define COMMIT_FILE_DOS         101
  1641.  
  1642. /* ************************************************************************
  1643.  *
  1644.  * operating system file I/O equates
  1645.  *
  1646.  * ************************************************************************/
  1647.  
  1648. #define READONLY        0x00000000 /* std file access mode */
  1649. #define WRITEONLY       0x00000001 /* no underscore used for std equates */
  1650. #define READWRITE       0x00000002
  1651.  
  1652. #define DENYREADWRITE   0x00000010 /* std file share mode, cannot be 0 */
  1653. #define DENYWRITE       0x00000020
  1654. #define DENYREAD        0x00000030
  1655. #define DENYNONE        0x00000040
  1656. #define NOINHERIT       0x00000080
  1657.  
  1658. #define NO_LOCALITY     0x00000000 /* optional cache modes */
  1659. #define SEQ_LOCALITY    0x00010000
  1660. #define RND_LOCALITY    0x00020000
  1661. #define MIX_LOCALITY    0x00030000
  1662. #define SKIP_CACHE      0x00100000 /* not inherited by child process */
  1663. #define WRITE_THROUGH   0x00400000 /* not inherited by child process */
  1664.  
  1665.  
  1666. #define LOCK_SHARED      1         /* a read-only lock, OS/2 and NT only */
  1667. #define LOCK_EXCLUSIVE   0         /* default */
  1668.  
  1669. /* ************************************************************************
  1670.  *
  1671.  * .sortFunction IDs, Query/SetSysVars|Vectors item IDs
  1672.  *
  1673.  * ************************************************************************/
  1674.  
  1675. // SORT_SET flag:  USE_*_CHARSET defaults to OEM character set, and is used
  1676. // (OEM or ANSI) if the .sortFunction is NLS or a custom sort-compare.
  1677.  
  1678. #define USE_OEM_CHARSET  (0 << 17) /* for DOSX32, OS/2, and Windows */
  1679. #define USE_ANSI_CHARSET (1 << 17) /* for Windows (.sortFunction flag) */
  1680.  
  1681. #define DUPS_ALLOWED (1 << 16) /* allow duplicate keys (.sortFunction flag) */
  1682.  
  1683. /* All Bullet system vars set to default values at INIT_XB */
  1684. /* Sorts 1-19 also used as CIP.sortFunction (can be OR'ed with DUPS_ALLOWED) */
  1685. /* Intrinsic sorts (1-6) are read-only (R-O) */
  1686.  
  1687. #define ASCII_SORT 1    /* sort by: ASCII value (R-O) */
  1688. #define NLS_SORT   2    /* NLS (R-O) */
  1689. #define S16_SORT   3    /* 16-bit signed integer (R-O) */
  1690. #define U16_SORT   4    /* 16-bit unsigned integer (R-O) */
  1691. #define S32_SORT   5    /* 32-bit signed integer (R-O) */
  1692. #define U32_SORT   6    /* 32-bit unsigned integer (R-O) */
  1693.  
  1694. /* sorts 7 to 9 are reserved */
  1695. /* Custom sort-compare functions are from 10 to 19 */
  1696.  
  1697. #define BUILD_KEY_FUNC  20      /* key build function ptr */
  1698. #define PARSER_FUNC     21      /* key expression parser function ptr */
  1699.  
  1700. #define MUTEX_SEM_HANDLE     29 /* handle of Bullet's mutex semaphore (R-O) */
  1701. #define LOCK_TIMEOUT         30 /* lock-wait timeout (default=0, no wait)*/
  1702. #define MUTEX_SEM_TIMEOUT    31 /* mutex semaphore-wait timeout (def=0,none) */
  1703. #define PACK_BUFFER_SIZE     32 /* pack buffer size (def=0, min autosize) */
  1704. #define REINDEX_BUFFER_SIZE  33 /* reindex buffer size (def=0, min autosize) */
  1705. #define REINDEX_PACK_PCT     34 /* reindex node pack % (default=100, max) */
  1706. #define TMP_PATH_PTR         35 /* temporary file path ptr (default=NULL) */
  1707. #define REINDEX_SKIP_TAG     36 /* index skip tag select (default=0, none) */
  1708. #define COMMIT_AT_EACH       37 /* commit each insert/update in pack (def=0) */
  1709. #define MEMO_BLOCKSIZE       38 /* memo block size (default=512 bytes) */
  1710. #define MEMO_EXTENSION       39 /* memo filename extension (default='DBT\0') */
  1711. #define MAX_DATAFILE_SIZE    40 /* max data size (default=0x7FEFFFFF=2095MB) */
  1712. #define MAX_INDEXFILE_SIZE   41 /* max index size (default=0x7FEFFFFF=2095MB)*/
  1713. #define ATOMIC_MODE          42 /* bit0=1 atomic next/prev key access (def=0)*/
  1714. #define CALLBACK_PTR         43 /* callback at reindex/pack (def=0, none) */
  1715.  
  1716. /* ************************************************************************
  1717.  *
  1718.  * Query/SetVectors vector IDs
  1719.  *
  1720.  * ************************************************************************/
  1721.  
  1722. #define VECTOR_CLOSE_FILE           2
  1723. #define VECTOR_CREATE_DIR           3
  1724. #define VECTOR_CREATE_FILE          4
  1725. #define VECTOR_CREATE_UNIQUE_FILE   5
  1726. #define VECTOR_DELETE_FILE          6
  1727. #define VECTOR_LENGTH_FILE          7
  1728. #define VECTOR_MOVE_FILE            8
  1729. #define VECTOR_OPEN_FILE            9
  1730. #define VECTOR_READ_FILE           10
  1731. #define VECTOR_SEEK_FILE           11
  1732. #define VECTOR_UPDATE_DIR_ENTRY    12
  1733. #define VECTOR_WRITE_FILE          13
  1734. #define VECTOR_LOCK_FILE           14
  1735. #define VECTOR_IS_DRIVE_REMOTE     15
  1736. #define VECTOR_IS_FILE_REMOTE      16
  1737. #define VECTOR_EXITLIST            17
  1738. #define VECTOR_REMOVE_EXITLIST     18
  1739. #define VECTOR_FREE                19
  1740. #define VECTOR_GET_SORT_TABLE      20
  1741. #define VECTOR_GET_COUNTRY_INFO    21
  1742. #define VECTOR_GET_ERROR_CLASS     22
  1743. #define VECTOR_GET_MEMORY          23
  1744. #define VECTOR_GET_TMP_DIR         24
  1745. #define VECTOR_GET_VERSION         25
  1746. #define VECTOR_MALLOC              26
  1747. #define VECTOR_SET_HANDLE_COUNT    27
  1748. #define VECTOR_GET_TIME_INFO       28
  1749. #define VECTOR_UPPERCASE           29
  1750. #define VECTOR_CLOSE_MUTEX_SEM     30
  1751. #define VECTOR_CREATE_MUTEX_SEM    31
  1752. #define VECTOR_RELEASE_MUTEX_SEM   32
  1753. #define VECTOR_REQUEST_MUTEX_SEM   33
  1754.  
  1755.  
  1756. /* ************************************************************************
  1757.  *
  1758.  * Parameter pack structures, typedefs
  1759.  *
  1760.  * ************************************************************************/
  1761.  
  1762. /* AP, CP, CDP, etc., are suggested variable names */
  1763.  
  1764. typedef struct _ACCESSPACK {
  1765. ULONG func;
  1766. ULONG stat;
  1767. ULONG handle;         /* I, handle of Bullet file to access */
  1768. LONG  recNo;          /* IO, record number */
  1769. PVOID recPtr;         /* I, programmer's record buffer */
  1770. PVOID keyPtr;         /* I, programmer's key buffer */
  1771. PVOID nextPtr;        /* I, NULL if not xaction, else next AP in list */
  1772. } ACCESSPACK; /* AP */
  1773. typedef ACCESSPACK *PACCESSPACK;
  1774.  
  1775. /* CBP is the structure received by the callback procedure */
  1776. /* structure members are filled in by Bullet */
  1777.  
  1778. typedef struct _CALLBACKPACK {
  1779. ULONG sizeIs;         /* structure size (current 16 bytes) */
  1780. ULONG callMode;       /* 0=from reindex; 1=from DBF pack */
  1781. ULONG handle;         /* file handle */
  1782. ULONG data1;          /* for callMode=0/1: progress percent (1-99,0) */
  1783. } CALLBACKPACK; /* CBP */
  1784. typedef CALLBACKPACK *PCALLBACKPACK;
  1785.  
  1786. typedef struct _COPYPACK {
  1787. ULONG func;
  1788. ULONG stat;
  1789. ULONG handle;         /* I, handle of Bullet file to copy */
  1790. PSZ   filenamePtr;    /* I, filename to use (drv+path must exist if used) */
  1791. } COPYPACK; /* CP */
  1792. typedef COPYPACK *PCOPYPACK;
  1793.  
  1794. typedef struct _CREATEDATAPACK {
  1795. ULONG func;
  1796. ULONG stat;
  1797. PSZ   filenamePtr;    /* I, filename to use */
  1798. ULONG noFields;       /* I, 1 to 1024 */
  1799. PVOID fieldListPtr;   /* I, descriptor list, 1 per field */
  1800. ULONG fileID;         /* I, 0x03 for standard DBF, 0x8B if memo file also */
  1801. } CREATEDATAPACK; /* CDP */
  1802. typedef CREATEDATAPACK *PCREATEDATAPACK;
  1803.  
  1804. typedef struct _CREATEINDEXPACK {
  1805. ULONG func;
  1806. ULONG stat;
  1807. PSZ   filenamePtr;    /* I, filename to use */
  1808. PSZ   keyExpPtr;      /* I, e.g., "SUBSTR(LNAME,1,4)+SSN" */
  1809. LONG  xbLink;         /* I, opened data file handle this indexes */
  1810. ULONG sortFunction;   /* I, 1-9 system, 10-19 custom */
  1811. ULONG codePage;       /* I, 0=use process default */
  1812. ULONG countryCode;    /* I, 0=use process default */
  1813. PVOID collatePtr;     /* I, NULL=use cc/cp else use passed table for sort */
  1814. ULONG nodeSize;       /* I, 512, 1024, or 2048 */
  1815. } CREATEINDEXPACK; /* CIP */
  1816. typedef CREATEINDEXPACK *PCREATEINDEXPACK;
  1817.  
  1818. typedef struct _FIELDDESCTYPE {
  1819. BYTE  fieldName[11];  /* IO, upper A-Z and _; 1-10 chars, 0-filled, 0-term */
  1820. BYTE  fieldType;      /* IO, C,D,L,N, or M */
  1821. LONG  fieldDA;        /* x, offset within record (run-time storage option) */
  1822. BYTE  fieldLen;       /* IO, C=1-255,D=8,L=1,N=1-19,M=10 */
  1823. BYTE  fieldDC;        /* IO, fieldType=N then 0-15 else 0 */
  1824. USHORT altFieldLength;/* IO, 0 */
  1825. BYTE  filler[12];     /* I, 0 */
  1826. } FIELDDESCTYPE; /* nested in _DESCRIPTORPACK */
  1827. typedef FIELDDESCTYPE *PFIELDDESCTYPE;
  1828.  
  1829. typedef struct _DESCRIPTORPACK {
  1830. ULONG func;
  1831. ULONG stat;
  1832. ULONG handle;         /* I, handle of DBF file */
  1833. ULONG fieldNumber;    /* IO, first field is 1 */
  1834. ULONG fieldOffset;    /* O, offset of field within record (tag=offset 0) */
  1835. FIELDDESCTYPE FD;     /* IO FD.fieldName only, O for rest of FD */
  1836. } DESCRIPTORPACK; /* DP */
  1837. typedef DESCRIPTORPACK *PDESCRIPTORPACK;
  1838.  
  1839. typedef struct _DOSFILEPACK {
  1840. ULONG func;
  1841. ULONG stat;
  1842. PSZ   filenamePtr;    /* I, filename to use */
  1843. ULONG handle;         /* IO, handle of open file */
  1844. ULONG asMode;         /* I, access-sharing mode */
  1845. ULONG bytes;          /* IO, bytes to read, write, length of */
  1846. LONG  seekTo;         /* IO, seek to offset, current offset */
  1847. ULONG method;         /* I, seek method (0=start of file, 1=current, 2=end) */
  1848. PVOID bufferPtr;      /* I, buffer to read into or write from */
  1849. ULONG attr;           /* I, attribute to create file with */
  1850. PSZ   newNamePtr;     /* I, name to use on rename */
  1851. } DOSFILEPACK; /* DFP */
  1852. typedef DOSFILEPACK *PDOSFILEPACK;
  1853.  
  1854. typedef struct _EXITPACK {
  1855. ULONG func;
  1856. ULONG stat;
  1857. } EXITPACK; /* EP */
  1858. typedef EXITPACK *PEXITPACK;
  1859.  
  1860. typedef struct _HANDLEPACK {
  1861. ULONG func;
  1862. ULONG stat;
  1863. ULONG handle;         /* I, handle of Bullet file */
  1864. } HANDLEPACK; /* HP */
  1865. typedef HANDLEPACK *PHANDLEPACK;
  1866.  
  1867. typedef struct _INITPACK {
  1868. ULONG func;
  1869. ULONG stat;
  1870. ULONG JFTsize;        /* I, max opened files (20-1024+) */
  1871. ULONG versionDOS;     /* O, e.g., 230 for 2.30 */
  1872. ULONG versionBullet;  /* O, e.g., 2019 for 2.019 */
  1873. ULONG versionOS;      /* O, e.g., 4=OS/2 32-bit */
  1874. PVOID exitPtr;        /* O, function pointer to EXIT_XB routine */
  1875. } INITPACK; /* IP */
  1876. typedef INITPACK *PINITPACK;
  1877.  
  1878. typedef struct _LOCKPACK {
  1879. ULONG func;
  1880. ULONG stat;
  1881. ULONG handle;         /* I, handle of Bullet file to lock */
  1882. ULONG xlMode;         /* I, index lock mode (0=exclusive, 1=shared) */
  1883. ULONG dlMode;         /* I, data lock mode (0=exclusive, 1=shared) */
  1884. LONG  recStart;       /* I, if data, first record # to lock, or 0 for all */
  1885. ULONG recCount;       /* I, if data and recStart!=0, # records to lock */
  1886. PVOID nextPtr;        /* I, NULL if not xaction, else next LP in list */
  1887. } LOCKPACK; /* LP */
  1888. typedef LOCKPACK *PLOCKPACK;
  1889.  
  1890. typedef struct _MEMODATAPACK {
  1891. ULONG func;
  1892. ULONG stat;
  1893. ULONG dbfHandle;      /* I, handle of DBF file to which this memo file belongs */
  1894. ULONG memoBypass;     /* I, memo bypass function to do, if any */
  1895. PVOID memoPtr;        /* I, ptr to memo record buffer */
  1896. ULONG memoNo;         /* IO, memo record number (aka block number) */
  1897. ULONG memoOffset;     /* I, position within record to start read/update */
  1898. ULONG memoBytes;      /* IO, number of bytes to read/update */
  1899. } MEMODATAPACK; /* MDP */
  1900. typedef MEMODATAPACK *PMEMODATAPACK;
  1901.  
  1902. typedef struct _MEMORYPACK {
  1903. ULONG func;
  1904. ULONG stat;
  1905. ULONG memory;         /* O, not used in OS/2 */
  1906. } MEMORYPACK; /* MP */
  1907. typedef MEMORYPACK *PMEMORYPACK;
  1908.  
  1909. typedef struct _OPENPACK {
  1910. ULONG func;
  1911. ULONG stat;
  1912. ULONG handle;         /* O, handle of file opened */
  1913. PSZ   filenamePtr;    /* I, Bullet file to open */
  1914. ULONG asMode;         /* I, access-sharing-cache mode */
  1915. LONG  xbLink;         /* I, if index open, xbLink=handle of its opened DBF */
  1916. } OPENPACK; /* OP */
  1917. typedef OPENPACK *POPENPACK;
  1918.  
  1919. typedef struct _QUERYSETPACK {
  1920. ULONG func;
  1921. ULONG stat;
  1922. ULONG item;           /* I, Bullet sysvar item to get/set */
  1923. ULONG itemValue;      /* IO, current/new value */
  1924. } QUERYSETPACK; /* QSP */
  1925. typedef QUERYSETPACK *PQUERYSETPACK;
  1926.  
  1927. typedef struct _REMOTEPACK {
  1928. ULONG func;
  1929. ULONG stat;
  1930. ULONG handle;         /* I, handle of file, or if 0, use RP.drive */
  1931. ULONG drive;          /* I, drive (1=A,2=B,3=C,...0=current) to check */
  1932. ULONG isRemote;       /* O, =1 of handle/drive is remote, =0 if local */
  1933. ULONG flags;          /* O, 0 */
  1934. ULONG isShare;        /* O, 1 */
  1935. } REMOTEPACK; /* RP */
  1936. typedef REMOTEPACK *PREMOTEPACK;
  1937.  
  1938. typedef struct _STATDATAPACK {
  1939. ULONG func;
  1940. ULONG stat;
  1941. ULONG handle;         /* I, handle to check */
  1942. ULONG fileType;       /* O, bit0=1 data file */
  1943. ULONG flags;          /* O, bit0=1 dirty, bit1=1 full-lock, bit2=1 shared */
  1944. ULONG progress;       /* O, 0,1-99% pack progress */
  1945. PVOID morePtr;        /* O, 0 */
  1946. ULONG fields;         /* O, fields per record */
  1947. ULONG asMode;         /* O, access-sharing-cache mode */
  1948. PSZ   filenamePtr;    /* O, filename used in open */
  1949. ULONG fileID;         /* O, first byte of DBF file */
  1950. ULONG lastUpdate;     /* O, high word=year,low byte=day, high byte=month */
  1951. ULONG records;        /* O, data records (including "deleted") */
  1952. ULONG recordLength;   /* O, record length */
  1953. ULONG xactionFlag;    /* O, 0 */
  1954. ULONG encryptFlag;    /* O, 0 */
  1955. PVOID herePtr;        /* O, this file's control address */
  1956. ULONG memoHandle;     /* O, handle of open memo file (0 if none) */
  1957. ULONG memoBlockSize;  /* O, memo file block size */
  1958. ULONG memoFlags;      /* O, bit0=1 dirty */
  1959. ULONG memoLastRecord; /* O, last accessed memo record (0 if none) */
  1960. ULONG memoLastSize;   /* O, size of last accessed memo record (in bytes, +8) */
  1961. ULONG lockCount;      /* O, number of full-locks in force */
  1962. } STATDATAPACK; /* SDP */
  1963. typedef STATDATAPACK *PSTATDATAPACK;
  1964.  
  1965. typedef struct _STATHANDLEPACK {
  1966. ULONG func;
  1967. ULONG stat;
  1968. ULONG handle;         /* I, handle to check */
  1969. LONG  ID;             /* O, bit0=1 data file, bit0=1 index file */
  1970. } STATHANDLEPACK; /* SHP */
  1971. typedef STATHANDLEPACK *PSTATHANDLEPACK;
  1972.  
  1973. typedef struct _STATINDEXPACK {
  1974. ULONG func;
  1975. ULONG stat;
  1976. ULONG handle;         /* I, handle to check */
  1977. ULONG fileType;       /* O, bit0=0 index file */
  1978. ULONG flags;          /* O, bit0=1 dirty, bit1=1 full-lock, bit2=1 shared */
  1979. ULONG progress;       /* O, 0,1-99% reindex progress */
  1980. PVOID morePtr;        /* O, 0 */
  1981. ULONG xbLink;         /* O, XB file link handle */
  1982. ULONG asMode;         /* O, access-sharing-cache mode */
  1983. PSZ   filenamePtr;    /* O, pointer to filename used in open */
  1984. ULONG fileID;         /* O, "31ch" */
  1985. PSZ   keyExpPtr;      /* O, pointer to key expression */
  1986. ULONG keys;           /* O, keys in file */
  1987. ULONG keyLength;      /* O, key length */
  1988. ULONG keyRecNo;       /* O, record number of current key */
  1989. PVOID keyPtr;         /* O, ptr to current key value (valid to keyLength) */
  1990. PVOID herePtr;        /* O, this file's control address */
  1991. ULONG codePage;       /* O, code page at create time */
  1992. ULONG countryCode;    /* O, country code at create time */
  1993. PVOID CTptr;          /* O, collate table ptr, NULL=no collate table present */
  1994. ULONG nodeSize;       /* O, node size */
  1995. ULONG sortFunction;   /* O, sort function ID */
  1996. ULONG lockCount;      /* O, number of full-locks in force */
  1997. } STATINDEXPACK; /* SIP */
  1998. typedef STATINDEXPACK *PSTATINDEXPACK;
  1999.  
  2000. typedef struct _XERRORPACK {
  2001. ULONG func;
  2002. ULONG stat;           /* I, error to check */
  2003. ULONG errClass;       /* O, class of error */
  2004. ULONG action;         /* O, action recommended for error */
  2005. ULONG location;       /* O, location of error */
  2006. } XERRORPACK; /* XEP */
  2007. typedef XERRORPACK *PXERRORPACK;
  2008.  
  2009.  
  2010. /* ************************************************************************
  2011.  *
  2012.  * Error codes
  2013.  *
  2014.  * ************************************************************************/
  2015.  
  2016. #define EXB_NOT_ENOUGH_MEMORY   8  /* cannot get memory requested */
  2017. #define EXB_INVALID_DRIVE       15 /* not a valid drive letter */
  2018. #define EXB_UNEXPECTED_EOF      38 /* unexpect EOF (bytes read != bytes asked) */
  2019. #define EXB_DISK_FULL           39 /* disk full on WriteFile */
  2020. #define EXB_FILE_EXISTS         80 /* cannot create file since it already exists */
  2021. #define EXB_SEM_OWNER_DIED      105 /* in place of Win32 error 80h (mutex) */
  2022. #define EXB_TIMEOUT             640 /* in place of Win32 error 102h (mutex) */
  2023.  
  2024. /* Other operating system errors are as returned by OS itself */
  2025.  
  2026. /* System/general error codes */
  2027.  
  2028. #define EXB_OR_WITH_FAULTS      8192 /* 8192+1 to +4, close-type errors */
  2029.  
  2030.                                      /* ERR_216501/6 are for Bullet/x only */
  2031. #define EXB_216501              8251 /* INT21/6501h not supported by DOS extender */
  2032.                                      /* (do not use default cc/cp) */
  2033. #define EXB_216506              8256 /* INT21/6506h not supported by DOS extender */
  2034.                                      /* (provide a custom collate table) */
  2035.  
  2036. #define EXB_ILLEGAL_CMD         8300 /* function not allowed */
  2037. #define EXB_OLD_DOS             8301 /* OS version < MIN_DOS_NEEDED */
  2038. #define EXB_NOT_INITIALIZED     8302 /* init not active, must do INIT_XB */
  2039. #define EXB_ALREADY_INITIALIZED 8303 /* init already active, must do EXIT_XB */
  2040. #define EXB_TOO_MANY_HANDLES    8304 /* more than 1024 opens requested */
  2041. #define EXB_SYSTEM_HANDLE       8305 /* Bullet won't use or close handles 0-2 */
  2042. #define EXB_FILE_NOT_OPEN       8306 /* file not open (not Bullet handle, including xbLink) */
  2043. #define EXB_FILE_IS_DIRTY       8307 /* tried to reload header but current still dirty */
  2044. #define EXB_BAD_FILETYPE        8308 /* tried key op on non-key file, data op on non... */
  2045. #define EXB_TOO_MANY_PACKS      8309 /* too many INSERT,UPDATE,REINDEX,LOCK_XB packs */
  2046. #define EXB_NULL_RECPTR         8310 /* null record pointer passed to Bullet */
  2047. #define EXB_NULL_KEYPTR         8311 /* null key pointer passed to Bullet */
  2048. #define EXB_NULL_MEMOPTR        8312 /* null memo pointer passed to Bullet */
  2049. #define EXB_EXPIRED             8313 /* evaluation time period has expired */
  2050. #define EXB_BAD_INDEX           8314 /* Query/SetSysVars index beyond last one */
  2051. #define EXB_RO_INDEX            8315 /* SetSysVar index item is read-only */
  2052. #define EXB_FILE_BOUNDS         8316 /* file size > 4GB, or > system var sets */
  2053. #define EXB_FORCE_ROLLBACK      8397 /* rollback test completed (internal use) */
  2054. #define EXB_INVALID_DLL         8398 /* DLL seems to be invalid, 8399 same */
  2055.  
  2056. /* Multi-access error codes */
  2057.  
  2058. #define EXB_BAD_LOCK_MODE       8401 /* lock mode (LP) not valid */
  2059. #define EXB_NOTHING_TO_RELOCK   8402 /* cannot relock without existing full-lock */
  2060. #define EXB_SHARED_LOCK_ON      8403 /* write access needed but lock is shared (flush on backup) */
  2061.  
  2062. /* Index error codes */
  2063.  
  2064. #define EXB_KEY_NOT_FOUND       8501 /* exact match of key not found */
  2065. #define EXB_KEY_EXISTS          8502 /* key exists already and dups not allowed */
  2066. #define EXB_END_OF_FILE         8503 /* already at last index order */
  2067. #define EXB_TOP_OF_FILE         8504 /* already at first index order */
  2068. #define EXB_EMPTY_FILE          8505 /* nothing to do since no keys */
  2069. #define EXB_CANNOT_GET_LAST     8506 /* cannot locate last key */
  2070. #define EXB_BAD_INDEX_STACK     8507 /* index file is corrupt */
  2071. #define EXB_BAD_INDEX_READ0     8508 /* index file is corrupt */
  2072. #define EXB_BAD_INDEX_WRITE0    8509 /* index file is corrupt */
  2073.  
  2074. #define EXB_OLD_INDEX           8521 /* old index, run through ReindexOld to update */
  2075. #define EXB_UNKNOWN_INDEX       8522 /* not a Bullet index file */
  2076. #define EXB_KEY_TOO_LONG        8523 /* keylength > 62 (or 64 if unique), or is 0 */
  2077.  
  2078. #define EXB_PARSER_NULL         8531 /* parser function pointer is NULL */
  2079. #define EXB_BUILDER_NULL        8532 /* build key function pointer is NULL */
  2080. #define EXB_BAD_SORT_FUNC       8533 /* CIP.sortFunction not valid */
  2081. #define EXB_BAD_NODE_SIZE       8534 /* CIP.nodeSize is not 512, 1024, or 2048 */
  2082. #define EXB_FILENAME_TOO_LONG   8535 /* CIP.filenamePtr->pathname > max path length */
  2083.  
  2084. #define EXB_KEYX_NULL           8541 /* expression is effectively NULL */
  2085. #define EXB_KEYX_TOO_LONG       8542 /* CIP.keyExpPtr->expression > 159 */
  2086. #define EXB_KEYX_SYM_TOO_LONG   8543 /* fieldname/funcname in expression > 10 chars */
  2087. #define EXB_KEYX_SYM_UNKNOWN    8544 /* fieldname/funcname in expression unknown */
  2088. #define EXB_KEYX_TOO_MANY_SYMS  8545 /* too many symbols/fields used in expression */
  2089. #define EXB_KEYX_BAD_SUBSTR     8546 /* invalid SUBSTR() operand in expression */
  2090. #define EXB_KEYX_BAD_SUBSTR_SZ  8547 /* SUBSTR() exceeds field's size */
  2091. #define EXB_KEYX_BAD_FORM       8548 /* didn't match expected symbol in expression */
  2092.  
  2093. #define EXB_NO_READS_FOR_RUN    8551 /* unlikely, use different reindex buffer size */
  2094. #define EXB_TOO_MANY_RUNS       8552 /* unlikely, too many runs (64K or more runs) */
  2095. #define EXB_TOO_MANY_RUNS_FOR_BUFFER 8553 /* unlikely, too many runs for run buffer */
  2096. #define EXB_TOO_MANY_DUPLICATES 8554 /* more than 64K "identical" keys */
  2097.  
  2098. #define EXB_INSERT_RECNO_BAD    8561 /* AP.recNo cannot be > 0 if inserting */
  2099. #define EXB_PREV_APPEND_EMPTY   8562 /* no prev append for insert yet AP.recNo==80000000h */
  2100. #define EXB_PREV_APPEND_MISMATCH 8563 /* prev append's xbLink does not match this */
  2101. #define EXB_INSERT_KBO_FAILED   8564 /* could not back out key at INSERT_XB */
  2102. #define EXB_INSERT_DBO_FAILED   8565 /* could not back out data records at INSERT_XB */
  2103.  
  2104. #define WRN_NOTHING_TO_UPDATE   8571 /* all AP.recNo=0 at UPDATE_XB */
  2105. #define EXB_INTERNAL_UPDATE     8572 /* internal error UPDATE_XB, not in hdl/rec# list */
  2106.  
  2107. #define EXB_FAILED_DATA_RESTORE 8573 /* could not restore original data record (*) */
  2108. #define EXB_FAILED_KEY_DELETE   8574 /* could not remove new key (*) */
  2109. #define EXB_FAILED_KEY_RESTORE  8575 /* could not restore original key(*) */
  2110. /* *original error, which forced a back-out, has been replaced by this error */
  2111. /* this error is always returned in the first AP.stat (-1 on data, 1 on index) */
  2112.  
  2113. /* Data error codes */
  2114.  
  2115. #define EXB_EXT_XBLINK          8601 /* xbLink handle is not internal DBF (is -1) */
  2116. #define EXB_FIELDNAME_TOO_LONG  8602 /* fieldname is > 10 characters */
  2117. #define EXB_RECORD_TOO_LONG     8603 /* record length is > 64K */
  2118. #define EXB_FIELD_NOT_FOUND     8604 /* fieldname not found in descriptor info */
  2119. #define EXB_BAD_FIELD_COUNT     8605 /* fields <= 0 or >= MAX_FIELDS (Init,Open) */
  2120.                                      /* and also GetDescriptor by field number */
  2121. #define EXB_BAD_HEADER          8606 /* bad header (reclen=0, etc., from LocateTo, Flush) */
  2122. #define EXB_BUFFER_TOO_SMALL    8607 /* buffer too small (pack buffer < reclen in pack) */
  2123. #define EXB_INTERNAL_PACK       8608 /* internal error in PackRecords */
  2124. #define EXB_BAD_RECNO           8609 /* record number=0 or > records in data file hdr */
  2125.                                      /* or Pack on empty data file */
  2126. #define WRN_RECORD_TAGGED       8610 /* record's tag field matches skip tag */
  2127.  
  2128. /* Memo error codes */
  2129.  
  2130. #define WRN_CANNOT_OPEN_MEMO    8701 /* DBF says memo file but memo open fails */
  2131. #define EXB_MEMO_NOT_OPEN       8702 /* no open memo file for operation */
  2132. #define EXB_BAD_BLOCKSIZE       8703 /* memo blocksize must be at least 24 bytes */
  2133. #define EXB_MEMO_DELETED        8704 /* memo is deleted */
  2134. #define EXB_MEMO_PAST_END       8705 /* memo data requested is past end of record */
  2135. #define EXB_BAD_MEMONO          8706 /* memo number is not valid */
  2136. #define EXB_MEMO_IN_USE         8707 /* memo add encountered likely corrupt memo file */
  2137. #define EXB_BAD_AVAIL_LINK      8708 /* memo avail link cannot be valid (is 0) */
  2138. #define EXB_MEMO_ZERO_SIZE      8709 /* memo data has no size */
  2139. #define EXB_MEMO_IS_SMALLER     8710 /* memo attempt to shrink but already <= size */
  2140.  
  2141. #endif /* ifndef __BULLET_H */
  2142.  
  2143.  
  2144. ΓòÉΓòÉΓòÉ 9. Bullet Functions ΓòÉΓòÉΓòÉ
  2145.  
  2146. System level                    Advanced system-level 
  2147.  
  2148.  INIT_XB                        QUERY_SYSVARS_XB 
  2149.  EXIT_XB                        SET_SYSVARS_XB 
  2150.  MEMORY_XB                      SET_DVMON_XB 
  2151.  BACKUP_FILE_XB                 QUERY_VECTORS_XB 
  2152.  STAT_HANDLE_XB                 SET_VECTORS_XB 
  2153.  GET_ERROR_CLASS_XB 
  2154.  
  2155.  Data low-level                 Data mid-level                     Memo 
  2156.                                                                    mid-level 
  2157.  
  2158.  CREATE_DATA_XB                 GET_DESCRIPTOR_XB 
  2159.                                                                    GET_MEMO_SIZE_XB 
  2160.  OPEN_DATA_XB                   GET_RECORD_XB                      GET_MEMO_XB 
  2161.  CLOSE_DATA_XB                  ADD_RECORD_XB                      ADD_MEMO_XB 
  2162.  STAT_DATA_XB                   UPDATE_RECORD_XB 
  2163.                                                                    UPDATE_MEMO_XB 
  2164.  READ_DATA_HEADER_XB            DELETE_RECORD_XB 
  2165.                                                                    DELETE_MEMO_XB 
  2166.  FLUSH_DATA_HEADER_XB           UNDELETE_RECORD_XB 
  2167.                                                                    MEMO_BYPASS_XB 
  2168.  COPY_DATA_HEADER_XB            DEBUMP_RECORD_XB 
  2169.  ZAP_DATA_HEADER_XB             PACK_RECORDS_XB 
  2170.  
  2171.  Index low-level                Index mid-level                    Index 
  2172.                                                                    high-level 
  2173.  
  2174.  CREATE_INDEX_XB                FIRST_KEY_XB                       GET_FIRST_XB 
  2175.  OPEN_INDEX_XB                  EQUAL_KEY_XB                       GET_EQUAL_XB 
  2176.  CLOSE_INDEX                    EQUAL_OR_GREATER_KEY_XB 
  2177.                                                                    GET_EQUAL_OR_GREATER_XB 
  2178.  STAT_INDEX_XB                  EQUAL_OR_LESSER_KEY_XB 
  2179.                                                                    GET_EQUAL_OR_LESSER_XB 
  2180.  READ_INDEX_HEADER_XB           NEXT_KEY_XB                        GET_NEXT_XB 
  2181.  FLUSH_INDEX_HEADER_XB          PREV_KEY_XB                        GET_PREV_XB 
  2182.  COPY_INDEX_HEADER_XB           LAST_KEY_XB                        GET_LAST_XB 
  2183.  ZAP_INDEX_HEADER_XB            STORE_KEY_XB                       INSERT_XB 
  2184.                                 DELETE_KEY_XB                      UPDATE_XB 
  2185.                                 BUILD_KEY_XB                       REINDEX_XB 
  2186.                                 GET_CURRENT_KEY_XB 
  2187.                                 GET_KEY_FOR_RECORD_XB 
  2188.  
  2189.  Network level                  CP level 
  2190.  
  2191.  LOCK_XB                        DELETE_FILE_DOS 
  2192.  UNLOCK_XB                      RENAME_FILE_DOS 
  2193.  LOCK_INDEX_XB                  CREATE_FILE_DOS 
  2194.  UNLOCK_INDEX_XB                OPEN_FILE_DOS 
  2195.  LOCK_DATA_XB                   SEEK_FILE_DOS 
  2196.  UNLOCK_DATA_XB                 READ_FILE_DOS 
  2197.  CHECK_REMOTE_XB                WRITE_FILE_DOS 
  2198.  RELOCK_XB                      CLOSE_FILE_DOS 
  2199.  RELOCK_INDEX_XB                ACCESS_FILE_DOS 
  2200.  RELOCK_DATA_XB                 EXPAND_FILE_DOS 
  2201.                                 MAKE_DIR_DOS 
  2202.                                 COMMIT_FILE_DOS 
  2203.  
  2204.  
  2205. ΓòÉΓòÉΓòÉ 9.1. INIT_XB ΓòÉΓòÉΓòÉ
  2206.  
  2207. Pack: INITPACK                 Source Example 
  2208.  
  2209.      IN               OUT
  2210.    IP.func          IP.stat
  2211.    IP.JFTsize       IP.versionDOS
  2212.                     IP.versionBullet
  2213.                     IP.versionOS
  2214.                     IP.exitPtr
  2215.  
  2216.  This must be the first routine called (except for SET_VECTORS_XB).  If it has 
  2217.  already been called the system variables are restored to their defaults, and 
  2218.  an error is returned.  Otherwise, the entire Bullet system is initialized, and 
  2219.  EXIT_XB is registered with the OS ExitList handler (DosExitList). 
  2220.  
  2221.  For more than the default open files (generally 20), set IP.JFTsize to the 
  2222.  total number of concurrently open files you need.  Depending on your version, 
  2223.  Bullet manages up to 1024 Bullet files per process (total data and index; memo 
  2224.  files are not counted against this total).  Setting this less than 20 does 
  2225.  nothing.  This number is for Bullet files, your files, pipes -- anything using 
  2226.  a handle.  If you need to account for handles that you are managing, you 
  2227.  should add those to IP.JFTsize.  For example, if you need 10 data files, each 
  2228.  with a memo file, and 2 index files per data file, that is 40 total Bullet 
  2229.  files.  If you need to use 15 other handles, for whatever use, add that number 
  2230.  to the 40 Bullet files, for a total setting of 55.  The OS also uses 3 handles 
  2231.  for itself, so, for all these, IP.JFTsize=58 would be the minimum.  You can 
  2232.  set it higher, but unused handles are wasted handles. In addition, if the 
  2233.  current process has fewer total handles available than the number you 
  2234.  specified in IP.JFTsize, Bullet sets the total available handles to IP.JFTsize 
  2235.  (as the absolute number of handles required).  If the current process already 
  2236.  has more total handles than IP.JFTsize, no action is taken. 
  2237.  
  2238.  On return (where no error occurred), the operating system version is in 
  2239.  IP.versionDOS (*100) and the Bullet version (*1000) in IP.versionBullet. 
  2240.  IP.versionOS return is based on the following table: 
  2241.  
  2242.       Bullet Platform   IP.versionOS
  2243.          MS-DOS 16-bit      0
  2244.           Win3x 16-bit      1
  2245.          DOSX32 32-bit      3
  2246.            OS/2 32-bit      4
  2247.           Win32 32-bit      5
  2248.  
  2249.  IP.exitPtr returns with the function pointer to the EXIT_XB routine. This 
  2250.  function pointer is redundant unless specifically mentioned as being required 
  2251.  for your platform.  It is not needed in OS/2, but is useful for other 
  2252.  platforms, for example, for use with atexit(), part of C's standard library. 
  2253.  
  2254.  Win32s, as run in Windows 3.1, returns IP.versionDOS equal to its level.  For 
  2255.  example, if running Win31, using win32s version 1.25, IP.versionDOS returns as 
  2256.  125, not 310 as might be expected. 
  2257.  
  2258.  Note:  References under OUT using *AP.keyPtr or similar (note then *) are used 
  2259.  throughout this manual and indicate that Bullet updates the contents at 
  2260.  AP.keyPtr with data (i.e., Bullet filled the buffer). 
  2261.  
  2262.  
  2263. ΓòÉΓòÉΓòÉ 9.2. EXIT_XB ΓòÉΓòÉΓòÉ
  2264.  
  2265. Pack: EXITPACK                 Source Example 
  2266.  
  2267.      IN               OUT
  2268.    EP.func          EP.stat
  2269.  
  2270.  Call EXIT_XB before ending your program to release any remaining resources 
  2271.  back to the OS.  Open files should be closed by using CLOSE_DATA_XB and 
  2272.  CLOSE_INDEX_XB. EXIT_XB closes any Bullet files that are still open. 
  2273.  
  2274.  This routine is registered with the operating system and so is called 
  2275.  automatically when your program terminates in OS/2, or with the C startup code 
  2276.  if atexit() is used. 
  2277.  
  2278.  
  2279. ΓòÉΓòÉΓòÉ 9.3. ATEXIT_XB ΓòÉΓòÉΓòÉ
  2280.  
  2281. This routine is obsolete.  In OS/2, the EXIT_XB shutdown procedure is 
  2282. registered with the operating system.  For those systems that do not offer this 
  2283. feature in the OS, the compiler run-time routine atexit() is used immediately 
  2284. after calling INIT_XB, using IP.exitPtr as the function pointer for atexit(). 
  2285.  
  2286.  
  2287. ΓòÉΓòÉΓòÉ 9.4. MEMORY_XB ΓòÉΓòÉΓòÉ
  2288.  
  2289. Pack: MEMORYPACK               Source Example 
  2290.  
  2291.      IN               OUT
  2292.    MP.func          MP.stat
  2293.                     MP.memory
  2294.  
  2295.  MP.memory returns with the number of bytes in the private memory arena 
  2296.  allocatable by the process according to DosQuerySysInfo for QSV_MAXPRMEM, or 
  2297.  as provided by the OS.  It is for informational use only. 
  2298.  
  2299.  Note:  This routine is not mutex-protected. 
  2300.  
  2301.  
  2302. ΓòÉΓòÉΓòÉ 9.5. BACKUP_FILE_XB ΓòÉΓòÉΓòÉ
  2303.  
  2304. Pack: COPYPACK                 Source Example 
  2305.  
  2306.      IN               OUT
  2307.    CP.func          CP.stat
  2308.    CP.handle
  2309.    CP.filenamePtr
  2310.  
  2311.  Copy an open BULLET index file or data/memo files.  BULLET repacks and 
  2312.  reindexes files in place, requiring less disk space to perform the function. 
  2313.  This routine allows a file to be safely copied for a possible later restore. 
  2314.  
  2315.  This function is recommended prior to packing a data file with 
  2316.  PACK_RECORDS_XB.  For index files, COPY_INDEX_HEADER_XB is sufficient since 
  2317.  index files are easily recreated so long as you have the data file along with 
  2318.  the index file header. 
  2319.  
  2320.  A full-lock should be in force before copying.  A shared lock may be used. 
  2321.  
  2322.  If CP.handle belongs to a DBF data file, and if a memo file is attached, the 
  2323.  memo file backup name is as CP.filenamePtr, the backup DBF pathname, but the 
  2324.  extension is always set to "._BT".  For example, if CP.handle is for a DBF 
  2325.  that has a DBT memo file attached, then the current state of the DBF file is 
  2326.  copied to CP.filenamePtr, say, "\CURRBACK\ACCT.DBF", and, in this case, the 
  2327.  DBT memo file is copied to "\CURRBACK\ACCT._BT".  The name of the original 
  2328.  DBF/DBT does not matter.  If MEMO_EXTENSION of SET_SYSVARS_XB has changed the 
  2329.  default, then that extension is used on the memo copy, with '_' replacing its 
  2330.  first character. 
  2331.  
  2332.  To prevent the backing up of a DBT memo file when backing up a DBF data file, 
  2333.  set CP.handle = -CP.handle (i.e., negative CP.handle).  This way, the DBT memo 
  2334.  file is not copied.  To backup only a DBT file, close the DBF and copy the DBT 
  2335.  by some other means. 
  2336.  
  2337.  
  2338. ΓòÉΓòÉΓòÉ 9.6. STAT_HANDLE_XB ΓòÉΓòÉΓòÉ
  2339.  
  2340. Pack: STATHANDLEPACK           Source Example 
  2341.  
  2342.      IN               OUT
  2343.    SHP.func         SHP.stat
  2344.    SHP.handle       SHP.ID
  2345.  
  2346.  Get information on a file handle number to determine if it is a BULLET file, 
  2347.  and if so, its type:  index or data. 
  2348.  
  2349.       SHP.ID   File type
  2350.          0     index, IX3  use STAT_INDEX_XB for file stats
  2351.          1     data, DBF   use STAT_DATA_XB for file stats
  2352.         -1     unknown
  2353.  
  2354.  Only bit0 of SHP.ID is significant if not -1.  So, if bit0=0 then the handle 
  2355.  belongs to an index file.  If bit0=1 then it's a data file. 
  2356.  
  2357.  Memo file handles return as unknown.  A DBF file's memo file handle is stored 
  2358.  in the DBF file's data area, and is returned by STAT_DATA_XB in 
  2359.  SDP.memoHandle. 
  2360.  
  2361.  Note:  This routine is not mutex-protected. 
  2362.  
  2363.  
  2364. ΓòÉΓòÉΓòÉ 9.7. GET_ERROR_CLASS_XB ΓòÉΓòÉΓòÉ
  2365.  
  2366. Pack: XERRORPACK               Source Example 
  2367.  
  2368.      IN               OUT
  2369.    XEP.func         XEP.errClass
  2370.    XEP.stat         XEP.action
  2371.                     XEP.location
  2372.  
  2373.  Get the extended error information for the code passed in XEP.stat. This 
  2374.  information includes the error classification, recommended action, and origin 
  2375.  of the error. 
  2376.  
  2377.  Any system error code can be specified, not necessarily the one that last 
  2378.  occurred.  If a return code is not a BULLET code, then it is a system error 
  2379.  code (from the CP, DosXXX routines). 
  2380.  
  2381.  The ERRCLASS, ERRACT, and ERRLOC items below are OS/2 values, names and 
  2382.  descriptions for DosErrClass(). 
  2383.  
  2384.   Error Classification
  2385.  
  2386.     Value   Name                Description
  2387.       1    ERRCLASS_OUTRES     Out of resources
  2388.       2    ERRCLASS_TEMPSIT    Temporary situation
  2389.       3    ERRCLASS_AUTH       Authorization failed
  2390.       4    ERRCLASS_INTRN      Internal error
  2391.       5    ERRCLASS_HRDFAIL    Device hardware failure
  2392.       6    ERRCLASS_SYSFAIL    System failure
  2393.       7    ERRCLASS_APPEAR     Probably application error
  2394.       8    ERRCLASS_NOTFND     Item not located
  2395.       9    ERRCLASS_BADFMT     Bad format for function or data
  2396.      10    ERRCLASS_LOCKED     Resource or data locked
  2397.      11    ERRCLASS_MEDIA      Incorrect media, CRC error
  2398.      12    ERRCLASS_ALREADY    Action already taken or done, or resource already exists
  2399.      13    ERRCLASS_UNK        Unclassified
  2400.      14    ERRCLASS_CANT       Cannot perform requested action
  2401.      15    ERRCLASS_TIME       Timeout
  2402.  
  2403.  
  2404.   Recommended Action
  2405.  
  2406.     Value   Name                Description
  2407.       1    ERRACT_RETRY        Retry immediately
  2408.       2    ERRACT_DLYRET       Delay and retry
  2409.       3    ERRACT_USER         Bad user input - get new values
  2410.       4    ERRACT_ABORT        Terminate in an orderly manner
  2411.       5    ERRACT_PANIC        Terminate immediately
  2412.       6    ERRACT_IGNORE       Ignore error
  2413.       7    ERRACT_INTRET       Retry after user intervention
  2414.  
  2415.  
  2416.   Origin
  2417.  
  2418.     Value   Name                Description
  2419.       1    ERRLOC_UNK          Unknown
  2420.       2    ERRLOC_DISK         Disk
  2421.       3    ERRLOC_NET          Network
  2422.       4    ERRLOC_SERDEV       Serial device
  2423.       5    ERRLOC_MEM          Memory
  2424.  
  2425.  Note:  This routine is not mutex-protected. 
  2426.  
  2427.  
  2428. ΓòÉΓòÉΓòÉ 9.8. QUERY_SYSVARS_XB ΓòÉΓòÉΓòÉ
  2429.  
  2430. Pack: QUERYSETPACK             Source Example 
  2431.  
  2432.      IN               OUT
  2433.    QSP.func         QSP.stat
  2434.    QSP.item         QSP.itemValue
  2435.  
  2436.  Query a BULLET system variable. 
  2437.  
  2438.  To get the function pointers to the sort-compare functions, use: 
  2439.  
  2440.    QSP.item           FuncPtr To
  2441.    ASCII_SORT        ASCII sort compare
  2442.    NLS_SORT          NLS sort compare
  2443.    S16_SORT          16-bit signed integer
  2444.    U16_SORT          16-bit unsigned integer
  2445.    S32_SORT          32-bit signed integer
  2446.    U32_SORT          32-bit unsigned integer
  2447.  
  2448.  All intrinsic sort compares (1-6) point to the same function. They cannot be 
  2449.  called except by BULLET itself.  The integer compare routines are based on 
  2450.  Intel byte order.  For Motorola byte order, ASCII sort can be used for 
  2451.  all-positive numbers, otherwise a custom sort-compare should be used. 
  2452.  
  2453.     QSP.item          FuncPtr To
  2454.      10-19           Custom sort-compare functions
  2455.  
  2456.  Before creating or opening an index file with a custom sort-compare function 
  2457.  (which is specified during CREATE_INDEX_XB), that function's address must 
  2458.  first be sent to BULLET using SET_SYSVARS_XB. Thereafter, that function must 
  2459.  be available whenever that index file is accessed.  See Custom Sort-Compare 
  2460.  Function for creating custom sort-compare functions. 
  2461.  
  2462.  To get the function pointers to the build key and expression parser routines, 
  2463.  use: 
  2464.  
  2465.     QSP.item          FuncPtr To
  2466.    BUILD_KEY_FUNC    Build key routine
  2467.    PARSER_FUNC       Key expression parser routine
  2468.  
  2469.  Before creating or opening an index file with a custom build key or expression 
  2470.  parser routine (which is specified at any time, but must be used in a 
  2471.  consistent manner), that routine's address must first be sent to BULLET using 
  2472.  SET_SYSVARS_XB.  Thereafter, that routine should be available since it may be 
  2473.  required again.  See Custom Build-Key Routine for creating a custom build-key 
  2474.  routine and Custom Expression Parser Routine for creating a custom key 
  2475.  expression parser. 
  2476.  
  2477.  To get the BULLET system variables' values, use: 
  2478.  
  2479.     QSP.item             Value To
  2480.    26 (read-only)        low-word: number of xb$Malloc(), high-word: number of xb$Free()
  2481.    27 (read-only)        Max instances (2, 32, 999)
  2482.    28 (read-only)        Max files (100, 250, 1024)
  2483.    29 (read-only)        Handle of Bullet's mutex semaphore
  2484.  
  2485.    LOCK_TIMEOUT          Lock file bytes timeout, in milliseconds (default=0)
  2486.    MUTEX_SEM_TIMEOUT     Mutex semaphore request timeout, in milliseconds (default=0)
  2487.    PACK_BUFFER_SIZE      Pack buffer size, in bytes (default=0: autosize)
  2488.    REINDEX_BUFFER_SIZE   Reindex buffer size, in bytes (default=0: autosize)
  2489.    REINDEX_PACK_PCT      Reindex node pack percentage, 50-100% (default=100)
  2490.    TMP_PATH_PTR          Temporary file path pointer (default=NULL, where TMP= used, then .\)
  2491.    REINDEX_SKIP_TAG      Reindex tag field character to skip (default=0, no skip)
  2492.    COMMIT_AT_EACH        Commit each individual file during INSERT/UPDATE_XB (default=0, defer until flush)
  2493.    MEMO_BLOCKSIZE        Memo file block size (default=512 bytes; minimum is 24 bytes)
  2494.    MEMO_EXTENSION        Memo file extension (default is "DBT\0")
  2495.    MAX_DATAFILE_SIZE     Max data file size-1 (default=2047MB, absolute max is 4095MB)
  2496.    MAX_INDEXFILE_SIZE    Max index file size-1 (default=2047MB, absolute max is 4095MB)
  2497.    ATOMIC_MODE           Atomic mode (bit0=1 then atomic Next and Prev access, default=0)
  2498.    CALLBACK_PTR          Callback routine at reindex/pack (def=0, none)
  2499.  
  2500.  The timeout values determine if the kernel should wait for a pre-determined 
  2501.  time before returning an error if the resource cannot be obtained.  The lock 
  2502.  timeout specifies how long to wait for a lock to be obtained in case some 
  2503.  other process has a lock on the same resource.  The mutex timeout specifies 
  2504.  how long to wait for access to BULLET in case some other thread in this 
  2505.  process is in BULLET.  Multiple processes can access BULLET at the same time, 
  2506.  but only one thread in each process can be inside BULLET at any one time. 
  2507.  
  2508.  The buffer sizes, when 0, default to a minimum reasonable size.  Performance 
  2509.  is acceptable at these sizes.  For best performance, provide as much real 
  2510.  memory as possible, up to 512KB.  Larger buffers can be used. 
  2511.  
  2512.  The reindex node pack percentage determines how many keys are packed on a 
  2513.  node.  100% forces as many keys as possible, minus 1. 
  2514.  
  2515.  If the temporary file path pointer is NULL (the default), then the TMP= 
  2516.  environment variable is used to locate any temporary files created by BULLET, 
  2517.  or if that is not found, then the current directory is used.  The pointer 
  2518.  supplied, if any, should be to a string containing an existing path (drive 
  2519.  should be included; a trailing '\' is optional, but recommended).  See 
  2520.  REINDEX_XB for size requirements. 
  2521.  
  2522.  The reindex skip tag character, if encountered in the DBF record's tag field 
  2523.  (the first byte of each record), causes the reindex routine to not place that 
  2524.  record's key value into the index file.  Also, BUILD_KEY_XB returns a warning 
  2525.  if the record supplied has a matching tag character. To disable skip tag 
  2526.  processing, set it to 0. 
  2527.  
  2528.  Inserts and Updates, by default, do not commit each file when that pack is 
  2529.  processed.  Instead, it is left to the programmer to issue a FLUSH_XB to 
  2530.  commit.  To force a commit after each pack file is processed, set CommitAtEach 
  2531.  to 1.  This is not one single commit, but a commit for each file in the pack, 
  2532.  after that file has been processed, but before the next file in the pack is. 
  2533.  This will not prevent a roll-back should it be needed. 
  2534.  
  2535.  A memo file can have at most 589,822 blocks.  At the default 512 bytes per 
  2536.  block, that equates to about 288MB.  If you need more memo space, increase the 
  2537.  block size.  The memo extension default is "DBT\0".  Generally, it's a good 
  2538.  idea to leave it at this. 
  2539.  
  2540.  The maximum file sizes are enforced when adding to or reading from DBF files, 
  2541.  and when inserting into or reading from index files.  The default is 2047 MB 
  2542.  (0x7FEFFFFF).  If your file system permits 4GB files, set the values to 4095 
  2543.  MB (0xFFEFFFFF). 
  2544.  
  2545.  The Atomic mode flag determines how key access is handled.  When bit0=0, the 
  2546.  default, the key routines, NEXT_KEY_XB, PREV_KEY_XB, and GET_NEXT_XB, 
  2547.  GET_PREV_XB, use the internal position of the last gotten key as their 
  2548.  starting point.  In multi-threaded code, it's possible that another thread has 
  2549.  since accessed the same file handle and altered the last gotten key.  By 
  2550.  setting bit0=1, key access (next or previous) can now specify a starting point 
  2551.  (typically already set up in AP.keyPtr), rather than starting at the last 
  2552.  accessed key for that handle (which may have been changed by another thread). 
  2553.  
  2554.  The Callback routine receives a pointer to a CALLBACK structure on the stack. 
  2555.  The callback routine may clean the stack (e.g., a _syscall routine).  It may 
  2556.  also leave it for the caller to clean (e.g., a _stdcall or __cdecl routine). 
  2557.  See bd_rix.c on the distribution disk for an example.  When the CALLBACK_PTR 
  2558.  == NULL (default), no callback is made.  Currently, the callback is made 
  2559.  during REINDEX_XB and PACK_RECORDS calls, at a rate dependent on the 
  2560.  *_BUFFER_SIZE. 
  2561.  
  2562.  Note:  This routine is not mutex-protected. 
  2563.  
  2564.  
  2565. ΓòÉΓòÉΓòÉ 9.9. SET_SYSVARS_XB ΓòÉΓòÉΓòÉ
  2566.  
  2567. Pack: QUERYSETPACK             Source Example 
  2568.  
  2569.      IN               OUT
  2570.    QSP.func         QSP.stat
  2571.    QSP.item         QSP.itemValue
  2572.    QSP.itemValue
  2573.  
  2574.  Set a BULLET system variable, returning the previous value. 
  2575.  
  2576.  To use, set QSP.item to the item to set, and QSP.itemValue with the value to 
  2577.  use (function's address, variable's timeout value, etc., whatever the case may 
  2578.  be).  On return, QSP.itemValue is the previous value that QSP.item was set to. 
  2579.  
  2580.    QSP.item           FuncPtr To
  2581.    ASCII_SORT        ASCII sort compare
  2582.    NLS_SORT          NLS sort compare
  2583.    S16_SORT          16-bit signed integer
  2584.    U16_SORT          16-bit unsigned integer
  2585.    S32_SORT          32-bit signed integer
  2586.    U32_SORT          32-bit unsigned integer
  2587.  
  2588.  All intrinsic sort compares (1-6) point to the same function. They cannot be 
  2589.  called except by BULLET itself.  They should not be overloaded with custom 
  2590.  functions.  If you have a custom sort-compare, use one of the custom slots. 
  2591.  The integer compare routines are based on Intel byte order.  For Motorola byte 
  2592.  order, ASCII sort can be used for all-positive numbers, otherwise a custom 
  2593.  sort-compare should be used. 
  2594.  
  2595.     QSP.item          FuncPtr To
  2596.      10-19           Custom sort-compare functions
  2597.  
  2598.  Before creating or opening an index file with a custom sort-compare function 
  2599.  (which is specified during CREATE_INDEX_XB), that function's address must 
  2600.  first be sent to BULLET using this routine. Thereafter, that function must be 
  2601.  available whenever that index file is accessed.  See Custom Sort-Compare 
  2602.  Function for creating custom sort-compare functions. 
  2603.  
  2604.  To set the function pointers to the build key and expression parser routines, 
  2605.  use: 
  2606.  
  2607.     QSP.item          FuncPtr To
  2608.    BUILD_KEY_FUNC    Build key routine
  2609.    PARSER_FUNC       Key expression parser routine
  2610.  
  2611.  Before creating or opening an index file with a custom build key or expression 
  2612.  parser routine (which is specified at any time, but must be used in a 
  2613.  consistent manner), that routine's address must first be sent to BULLET using 
  2614.  this routine.  Thereafter, that routine should always be ready (in a callable 
  2615.  state) since it may be required again.  See Custom Build-Key Routine for 
  2616.  creating a custom build-key routine and Custom Expression Parser Routine for 
  2617.  creating a custom key expression parser. 
  2618.  
  2619.  To set the BULLET system variables' values, use: 
  2620.  
  2621.     QSP.item             Value To
  2622.    LOCK_TIMEOUT          Lock file bytes timeout, in milliseconds (default=0)
  2623.    MUTEX_SEM_TIMEOUT     Mutex semaphore request timeout, in milliseconds (default=0)
  2624.    PACK_BUFFER_SIZE      Pack buffer size, in bytes (default=0: autosize)
  2625.    REINDEX_BUFFER_SIZE   Reindex buffer size, in bytes (default=0: autosize)
  2626.    REINDEX_PACK_PCT      Reindex node pack percentage, 50-100% (default=100)
  2627.    TMP_PATH_PTR          Temporary file path pointer (default=NULL, where TMP= used, then .\)
  2628.    REINDEX_SKIP_TAG      Reindex tag field character to skip (default=0, no skip)
  2629.    COMMIT_AT_EACH        Commit each individual file during INSERT/UPDATE_XB (default=0, defer until flush)
  2630.    MEMO_BLOCKSIZE        Memo file block size (default=512 bytes; minimum is 24 bytes)
  2631.    MEMO_EXTENSION        Memo file extension (default is "DBT\0")
  2632.    MAX_DATAFILE_SIZE     Max data file size-1 (default=2047MB, absolute max is 4095MB)
  2633.    MAX_INDEXFILE_SIZE    Max index file size-1 (default=2047MB, absolute max is 4095MB)
  2634.    ATOMIC_MODE           Atomic mode (bit0=1 then atomic Next and Prev access, default=0)
  2635.    CALLBACK_PTR          Callback routine at reindex/pack (def=0, none)
  2636.  
  2637.  The timeout values determine if the kernel should wait for a pre-determined 
  2638.  time before returning an error if the resource cannot be obtained.  The lock 
  2639.  timeout specifies how long to wait for a lock to be obtained in case some 
  2640.  other process has a lock on the same resource.  The mutex timeout specifies 
  2641.  how long to wait for access to BULLET in case some other thread in this 
  2642.  process is in BULLET.  Multiple processes can access BULLET at the same time, 
  2643.  but only one thread in each process can be inside BULLET at any one time. 
  2644.  
  2645.  The buffer sizes, when 0, default to a minimum reasonable size.  Performance 
  2646.  is acceptable at these sizes.  For best performance, provide as much real 
  2647.  memory as possible, up to 512KB.  Larger buffers can be used. 
  2648.  
  2649.  The reindex node pack percentage determines how many keys are packed on a 
  2650.  node.  100% forces as many keys as possible, minus 1. 
  2651.  
  2652.  If the temporary file path pointer is NULL (the default), then the TMP= 
  2653.  environment variable is used to locate any temporary files created by BULLET, 
  2654.  or if that is not found, then the current directory is used.  The pointer 
  2655.  supplied, if any, should be to a string containing an existing path (drive 
  2656.  should be included; a trailing '\' is optional, but recommended).  See 
  2657.  REINDEX_XB for size requirements. 
  2658.  
  2659.  The reindex skip tag character, if encountered in the DBF record's tag field 
  2660.  (the first byte of each record), causes the reindex routine to not place that 
  2661.  record's key value into the index file.  Also, BUILD_KEY_XB returns a warning 
  2662.  if the record supplied has a matching tag character. To disable skip tag 
  2663.  processing, set it to 0. 
  2664.  
  2665.  Inserts and Updates, by default, do not commit each file when that pack is 
  2666.  processed.  Instead, it is left to the programmer to issue a FLUSH_XB to 
  2667.  commit.  To force a commit after each pack file is processed, set CommitAtEach 
  2668.  to 1.  This is not one single commit, but a commit for each file in the pack, 
  2669.  after that file has been processed, but before the next file in the pack is. 
  2670.  This will not prevent a roll-back should it be needed. 
  2671.  
  2672.  A memo file can have at most 589,822 blocks.  At the default 512 bytes per 
  2673.  block, that equates to about 288MB.  If you need more memo space, increase the 
  2674.  block size.  The memo extension default is "DBT\0".  Generally, it's a good 
  2675.  idea to leave it at this. 
  2676.  
  2677.  The maximum file sizes are enforced when adding to or reading from DBF files, 
  2678.  and when inserting into or reading from index files.  The default is 2047 MB 
  2679.  (0x7FEFFFFF).  If your file system permits 4GB files, set the values to 4095 
  2680.  MB (0xFFEFFFFF). 
  2681.  
  2682.  The Atomic mode flag determines how key access is handled.  When bit0=0, the 
  2683.  default, the key routines, NEXT_KEY_XB, PREV_KEY_XB, and GET_NEXT_XB, 
  2684.  GET_PREV_XB, use the internal position of the last gotten key as their 
  2685.  starting point.  In multi-threaded code, it's possible that another thread has 
  2686.  since accessed the same file handle and altered the last gotten key.  By 
  2687.  setting bit0=1, key access (next or previous) can now specify a starting point 
  2688.  (typically already set up in AP.keyPtr), rather than starting at the last 
  2689.  accessed key for that handle (which may have been changed by another thread). 
  2690.  
  2691.  The Callback routine receives a pointer to a CALLBACK structure on the stack. 
  2692.  The callback routine may clean the stack (e.g., a _syscall routine).  It may 
  2693.  also leave it for the caller to clean (e.g., a _stdcall or __cdecl routine). 
  2694.  See bd_rix.c on the distribution disk for an example.  When the CALLBACK_PTR 
  2695.  == NULL (default), no callback is made.  Currently, the callback is made 
  2696.  during REINDEX_XB and PACK_RECORDS calls, at a rate dependent on the 
  2697.  *_BUFFER_SIZE. 
  2698.  
  2699.  Note:  Issuing INIT_XB restores all system variables (those setable via this 
  2700.  routine) and function pointers, but not vectors, to their default values. 
  2701.  This is done even if INIT_XB returns an error that BULLET has already been 
  2702.  initialized. 
  2703.  
  2704.  
  2705. ΓòÉΓòÉΓòÉ 9.10. SET_DVMON_XB ΓòÉΓòÉΓòÉ
  2706.  
  2707. This routine is not currently used. 
  2708.  
  2709.  
  2710. ΓòÉΓòÉΓòÉ 9.11. QUERY_VECTORS_XB ΓòÉΓòÉΓòÉ
  2711.  
  2712. Pack: QUERYSETPACK             Source Example 
  2713.  
  2714.      IN               OUT
  2715.    QSP.func         QSP.stat
  2716.    QSP.item         QSP.itemValue
  2717.  
  2718.  Query a BULLET OS API vector. 
  2719.  
  2720.  To get the vectors that Bullet makes to access OS API calls, set QSP.item to 
  2721.  the desired VECTOR_* constant below.  On return, QSP.itemValue has the 
  2722.  function pointer for that item. 
  2723.  
  2724.        QSP.item
  2725.    VECTOR_CLOSE_FILE
  2726.    VECTOR_CREATE_DIR
  2727.    VECTOR_CREATE_FILE
  2728.    VECTOR_CREATE_UNIQUE_FILE
  2729.    VECTOR_DELETE_FILE
  2730.    VECTOR_LENGTH_FILE
  2731.    VECTOR_MOVE_FILE
  2732.    VECTOR_OPEN_FILE
  2733.    VECTOR_READ_FILE
  2734.    VECTOR_SEEK_FILE
  2735.    VECTOR_UPDATE_DIR_ENTRY
  2736.    VECTOR_WRITE_FILE
  2737.    VECTOR_LOCK_FILE
  2738.    VECTOR_IS_DRIVE_REMOTE
  2739.    VECTOR_IS_FILE_REMOTE
  2740.    VECTOR_EXITLIST
  2741.    VECTOR_REMOVE_EXITLIST
  2742.    VECTOR_FREE
  2743.    VECTOR_GET_SORT_TABLE
  2744.    VECTOR_GET_COUNTRY_INFO
  2745.    VECTOR_GET_ERROR_CLASS
  2746.    VECTOR_GET_MEMORY
  2747.    VECTOR_GET_TMP_DIR
  2748.    VECTOR_GET_VERSION
  2749.    VECTOR_MALLOC
  2750.    VECTOR_SET_HANDLE_COUNT
  2751.    VECTOR_GET_TIME_INFO
  2752.    VECTOR_UPPERCASE
  2753.    VECTOR_CLOSE_MUTEX_SEM
  2754.    VECTOR_CREATE_MUTEX_SEM
  2755.    VECTOR_RELEASE_MUTEX_SEM
  2756.    VECTOR_REQUEST_MUTEX_SEM
  2757.  
  2758.  If QSP.itemValue is returned NULL, the default Bullet OS API call is being 
  2759.  used. 
  2760.  
  2761.  Note:  This routine is not mutex-protected. 
  2762.  
  2763.  
  2764. ΓòÉΓòÉΓòÉ 9.12. SET_VECTORS_XB ΓòÉΓòÉΓòÉ
  2765.  
  2766. Pack: QUERYSETPACK             Source Example 
  2767.  
  2768.      IN               OUT
  2769.    QSP.func         QSP.stat
  2770.    QSP.item         QSP.itemValue
  2771.    QSP.itemValue
  2772.  
  2773.  Set a BULLET OS API vector. 
  2774.  
  2775.  To set the vectors that Bullet makes to access OS API calls, set QSP.item to 
  2776.  the desired VECTOR_* constant below, and QSP.itemValue to its replacement's 
  2777.  address.  On return, QSP.itemValue has the previous function pointer for that 
  2778.  item. 
  2779.  
  2780.        QSP.item
  2781.    VECTOR_CLOSE_FILE
  2782.    VECTOR_CREATE_DIR
  2783.    VECTOR_CREATE_FILE
  2784.    VECTOR_CREATE_UNIQUE_FILE
  2785.    VECTOR_DELETE_FILE
  2786.    VECTOR_LENGTH_FILE
  2787.    VECTOR_MOVE_FILE
  2788.    VECTOR_OPEN_FILE
  2789.    VECTOR_READ_FILE
  2790.    VECTOR_SEEK_FILE
  2791.    VECTOR_UPDATE_DIR_ENTRY
  2792.    VECTOR_WRITE_FILE
  2793.    VECTOR_LOCK_FILE
  2794.    VECTOR_IS_DRIVE_REMOTE
  2795.    VECTOR_IS_FILE_REMOTE
  2796.    VECTOR_EXITLIST
  2797.    VECTOR_REMOVE_EXITLIST
  2798.    VECTOR_FREE
  2799.    VECTOR_GET_SORT_TABLE
  2800.    VECTOR_GET_COUNTRY_INFO
  2801.    VECTOR_GET_ERROR_CLASS
  2802.    VECTOR_GET_MEMORY
  2803.    VECTOR_GET_TMP_DIR
  2804.    VECTOR_GET_VERSION
  2805.    VECTOR_MALLOC
  2806.    VECTOR_SET_HANDLE_COUNT
  2807.    VECTOR_GET_TIME_INFO
  2808.    VECTOR_UPPERCASE
  2809.    VECTOR_CLOSE_MUTEX_SEM
  2810.    VECTOR_CREATE_MUTEX_SEM
  2811.    VECTOR_RELEASE_MUTEX_SEM
  2812.    VECTOR_REQUEST_MUTEX_SEM
  2813.  
  2814.  Example replacement routines are in ccdosfn.c.  Many of the routines in 
  2815.  ccdosfn.c are standard C, but some are OS-specific and must be updated for the 
  2816.  OS being used. 
  2817.  
  2818.  If QSP.itemValue is set to NULL, the default Bullet OS API call is used. 
  2819.  
  2820.  On return from a successful call, QSP.itemValue is the value of the previous 
  2821.  function pointer for that item, which may be returned NULL, indicating that 
  2822.  the default Bullet OS API call was being used. 
  2823.  
  2824.  
  2825. ΓòÉΓòÉΓòÉ 9.13. CREATE_DATA_XB ΓòÉΓòÉΓòÉ
  2826.  
  2827. Pack: CREATEDATAPACK           Source Example 
  2828.  
  2829.      IN               OUT
  2830.    CDP.func         CDP.stat
  2831.    CDP.filenamePtr
  2832.    CDP.noFields
  2833.    CDP.fieldListPtr
  2834.    CDP.fileID
  2835.  
  2836.  Create a new BULLET DBF data file with the name at CDP.filenamePtr, and an 
  2837.  optional DBT memo file. 
  2838.  
  2839.  Before using this routine, allocate an array of field descriptors of type 
  2840.  FIELDDESCTYPE, one for each field in the record (number of fields as set in 
  2841.  CDP.noFields). It is recommended  that this allocation be zeroed before use 
  2842.  since fieldnames and reserved entries must be 0-filled: 
  2843.  
  2844.      FIELDDESCTYPE fieldList[12];  // 12 fields used in data record
  2845.        :
  2846.      memset(fieldList,0,sizeof(fieldList)); // init unused bytes to 0 (required)
  2847.  
  2848.  Filename 
  2849.  
  2850.  The drive and path must exist if used as part of the filename. Long filenames 
  2851.  may be used if supported by the file system in use. As with all text strings 
  2852.  used by Bullet, the filename must end in a '\0'. 
  2853.  
  2854.  Number of Fields 
  2855.  
  2856.  The number of descriptors in the array, described next.  Each field has a 
  2857.  descriptor.  The tag field is not a formal field, and so has no descriptor, 
  2858.  and is not counted in the number of fields.  The maximum number of fields is 
  2859.  254 according to the DBF standard.  Bullet allows 1024, but 254 should be used 
  2860.  if creating a standard DBF III Plus file. 
  2861.  
  2862.  Field Descriptors 
  2863.  
  2864.  For each field, a descriptor is used to identify and type it.  These 
  2865.  descriptors are assigned to an array; the pointer to that array is assigned to 
  2866.  CDP.fieldListPtr.  The format of the descriptor follows, with a physical 
  2867.  format listed in DBF File Format. 
  2868.  
  2869.  Γûá Fieldname 
  2870.  
  2871.  10 characters plus null byte terminator.  Valid fieldname characters are ASCII 
  2872.  A-Z (upper-case) and the underscore (ASCII 95).  All bytes after the fieldname 
  2873.  must be null bytes.  E.g., if the fieldname is "LNAME", five characters, the 
  2874.  following six bytes (including the 11th byte) are set to 0. The eleventh byte 
  2875.  is always a null byte since 10 characters is the maximum fieldname length. 
  2876.  Extended ASCII characters (above 127) should not be used. 
  2877.  
  2878.   fieldList[0].fieldname = "ANYNAME";   // see memset() above
  2879.  
  2880.  Γûá Field type and size 
  2881.  
  2882.  Standard Xbase field types are C, D, L, M, and N: 
  2883.  
  2884.  Type   Description 
  2885.    C    Character field, any code page character, 1 to 255 characters. 
  2886.         Null bytes are not desirable except as a string terminator.  There is 
  2887.         no requirement that field data be terminated with a '\0'.  The field 
  2888.         data should be left-justified within the field, but this is not 
  2889.         required (in which case use leading spaces, not 0 bytes). 
  2890.  
  2891.                        fieldList[0].fieldType = 'C';
  2892.                        fieldList[0].fieldLen = 25;
  2893.                        fieldList[0].fieldDC = 0;
  2894.  
  2895.    D    Date field, valid ASCII digits for date, 8 characters. 
  2896.         The physical format is YYYYMMDD, where YYYY is the year (1999), MM is 
  2897.         the month (1-12), and DD the day (1-31).  The date field is always 8 
  2898.         bytes long, and is in ASCII digits ('19991231').  If no date, set to 
  2899.         all spaces. 
  2900.  
  2901.                        fieldList[0].fieldType = 'D';
  2902.                        fieldList[0].fieldLen = 8;
  2903.                        fieldList[0].fieldDC = 0;
  2904.  
  2905.    L    Logical field, < SPACE> Y N T F y n t f, 1 character. 
  2906.         A single-byte field.  When not yet initialized the value will be a 
  2907.         <SPACE> (ASCII 32).  This is typically displayed as a '?' to the user, 
  2908.         indicating that the field has not been initialized.  Initialized values 
  2909.         are variations of yes, no, true, false ('Y', 'y', etc.). 
  2910.  
  2911.                        fieldList[0].fieldType = 'L';
  2912.                        fieldList[0].fieldLen = 1;
  2913.                        fieldList[0].fieldDC = 0;
  2914.  
  2915.    M    Memo field, 10 ASCII digits, 10 characters. 
  2916.         Field data is used as the block number in the corresponding DBT memo 
  2917.         file.  Each block is typically 512 bytes, with the first block (block 
  2918.         #0) used as the memo file header.  If no block is used in the .DBT by 
  2919.         this record, the field is set to <SPACES>.  The first memo block is 
  2920.         stored as "0000000001".  (This description is valid for dBASE IV and 
  2921.         later memo files, as created and used by BULLET.) Some Xbase versions 
  2922.         use field types B and G as variations of memo files.  They are as M, 
  2923.         but contain general data (as in anything), while memo files contain 
  2924.         only text. BULLET supports any type data in its memo files, and you may 
  2925.         use the CDP.fieldType of 'B' or 'G'. 
  2926.  
  2927.         More than one memo field per record is permitted.  For example, you may 
  2928.         need a memo for the printable address, where the address is free-form 
  2929.         rather than in separate fields (i.e., you have both forms), and another 
  2930.         memo for general notes, and yet a third for problem reports, and so on. 
  2931.         All these, and all memos for the rest of the DBF file, are stored in 
  2932.         the same DBT memo file. 
  2933.  
  2934.         Note:  BULLET does not use the fieldType with regard to identifying 
  2935.         memo field type; it is the programmer's responsibility to check the 
  2936.         fieldType and act on it accordingly, such as adding memo numbers. 
  2937.  
  2938.                        fieldList[0].fieldType = 'M';
  2939.                        fieldList[0].fieldLen = 10;
  2940.                        fieldList[0].fieldDC = 0;
  2941.  
  2942.    N    Numeric field, ASCII digits, 19 digits maximum (see below). 
  2943.         All standard Xbase data is stored in ASCII form (for universal 
  2944.         exchange).  Numeric fields are to be right-justified, with leading 
  2945.         spaces, and an aligned decimal point, if any (relative this field in 
  2946.         other records).  Do not end the field with a null byte. 
  2947.  
  2948.         The total size of the numeric field is specified in .fieldLen, which 
  2949.         includes any leading sign, the decimal point, and decimal digits to the 
  2950.         right of the decimal point (if any decimal point). The maximum total 
  2951.         size is 19 places.  If a decimal point, then the number of digits to 
  2952.         the right may be from 1 to 15 digits, but must be no more than the 
  2953.         total-2. 
  2954.  
  2955.                    FieldLen.FieldDC    Example
  2956.                            8.2          " 2345.78"
  2957.                            8.2          "12345.78"
  2958.                            8.2          "-2345.78"
  2959.                            8.1          "123456.8"
  2960.                            8.0          "12345678"
  2961.                            5.3          "2.235"
  2962.                            5.4          (not valid)
  2963.  
  2964.                        fieldList[0].fieldType = 'N';
  2965.                        fieldList[0].fieldLen = 8;
  2966.                        fieldList[0].fieldDC = 2;
  2967.  
  2968.  Although not dBASE compatible, you may use binary fields in your data records. 
  2969.  The Xbase standard always has ASCII data in the data fields, even if the field 
  2970.  is numeric.  For example, an 'N' type field of 8.2 (total 
  2971.  length.decimal-count) is stored as an ASCII text string in the data record, 
  2972.  say, a string like " 1100.55".  If you want dBASE compatibility your field 
  2973.  data must also be ASCII.  However, if you can forgo this requirement, you can 
  2974.  use binary values in the fields. 
  2975.  
  2976.  To do this you must specify a field type of 'Y' (actually, anything but an 
  2977.  'N') and, if it is to be used as a key field, also set the sort function to 
  2978.  the appropriate type (S16_SORT, etc.).  The field length 
  2979.  (fieldList[x].fieldLen) for a 'Y' field type is 2 if 16-bit, and 4 if 32-bit. 
  2980.  Also possible is floating-point (with a custom sort-compare function).  A 
  2981.  likely field type marker for this would be 'F'.  Note that both 'Y' and 'F' 
  2982.  are completely non-standard Xbase types, and only your programs will 
  2983.  understand them. 
  2984.  
  2985.  Note:  'B' should not be used as a binary field type marker since dBASE V uses 
  2986.  'B' to signify a binary-data memo file field.  Bullet makes no distinction in 
  2987.  its memo file data; anything can be placed in them.  Typically, your memo 
  2988.  fields are marked as 'M' in Bullet, but could also be 'B' or 'G'. 
  2989.  
  2990.  File ID 
  2991.  
  2992.  Conventional dBASE DBF files have a CDP.fileID=3.  To create a memo file (DBT, 
  2993.  dBASE IV compatible), set CDP.fileID=x8B.  For the DBT to be created, both 
  2994.  bits 3 and 7 (0x88) must be set.  The other bits may be anything, and are not 
  2995.  checked.  In creating your DBF files, specify CDP.fileID=3 to ensure 
  2996.  compatibility across Xbase versions, and limit record length to 4000 
  2997.  characters.  If creating a non-standard DBF (e.g., non-standard field types, 
  2998.  extended field lengths, etc.) it's recommended to use CDP.fileID=0 or 
  2999.  CDP.fileID=1.  For a standard DBF file with a memo file (dBASE IV or later), 
  3000.  use CDP.fileID=0x8B (eight-bee). 
  3001.  
  3002.  Generally, field data is space-filled.  String terminators are allowed in 
  3003.  C-haracter field types, but should not be used in other fields. 
  3004.  
  3005.  Memo File Creation 
  3006.  
  3007.  If bits 3 and 7 are set in CDP.fileID, a memo file is created for the DBF. The 
  3008.  memo filename will be the same as the DBF name except the extension. The memo 
  3009.  file is created after the DBF, with a block size of 512 bytes, and filename 
  3010.  extension of ".DBT".  The default block size and extension can be overridden 
  3011.  (see SET_SYSVARS_XB) prior to calling this routine. 
  3012.  
  3013.  Note:  A simple way to check that your record description in the field list 
  3014.  matches your source code structure is to compare the number of bytes used by 
  3015.  all fields in the field list (+1 for the delete tag byte) with the size of 
  3016.  your program's structure.  They must equal.  Also verify each field's size to 
  3017.  make sure they match.  By doing this you prevent the problem where data on 
  3018.  disk does not match the data in your record structure. 
  3019.  
  3020.  
  3021. ΓòÉΓòÉΓòÉ 9.14. OPEN_DATA_XB ΓòÉΓòÉΓòÉ
  3022.  
  3023. Pack: OPENPACK                 Source Example 
  3024.  
  3025.      IN               OUT
  3026.    OP.func          OP.stat
  3027.    OP.filenamePtr   OP.handle
  3028.    OP.asMode
  3029.  
  3030.  Open an existing DBF data file for use.  For DBF opens, two parameters are 
  3031.  specified:  the filename and the access-sharing mode.  The OP.xbLink parameter 
  3032.  is used only for index opens, and so is not used here. 
  3033.  
  3034.  The OP.asMode has optional cache mode settings.  The caching modes cover 
  3035.  locality, write-through, and skip cache.  Locality is typically mostly random 
  3036.  (RND_LOCALITY), but may be mostly sequential if the data file has been sorted 
  3037.  and the index file recently reindexed and processing is mostly in-order (first 
  3038.  to last, rather than random).  Locality is used to tune the cache.  Also, 
  3039.  normally, data is written to the cache with control returning immediately to 
  3040.  the program before the disk is written (an asynchronous write).  To force the 
  3041.  write to take place before control is returned (a synchronous write), use the 
  3042.  WRITE_THROUGH mode.  To skip the cache completely, use the SKIP_CACHE mode. 
  3043.  This, as all OP.asMode settings, affects this file handle only. 
  3044.  
  3045.  On a successful open, the file handle is returned.  Use this handle for all 
  3046.  further access to this file.  If the DBF was created with a compatible memo 
  3047.  file, it is also opened.  The handle of the memo file is available via 
  3048.  STAT_DATA_XB, but all access to the memo file is made with the handle of the 
  3049.  memo file's master DBF (the handle returned by this routine in OP.handle). 
  3050.  The memo file is opened using the same OP.asMode. 
  3051.  
  3052.  Note:  FoxPro DBF files with Fox memo files (FPT) use an ID of 0xFF.  Bullet 
  3053.  does not support FoxPro memo files, and so opening a FoxPro DBF with a Fox 
  3054.  memo file returns the warning message, WRN_CANNOT_OPEN_MEMO.  The DBF file is 
  3055.  opened, and the warning can be ignored. 
  3056.  
  3057.  Once open, you can get information on the data file by using STAT_DATA_XB. 
  3058.  
  3059.  Each DBF data file opened allocates and commits at least 4K bytes for internal 
  3060.  use: 
  3061.  
  3062.    Number of Fields     Memory
  3063.         1 to 121            4KB
  3064.       122 to 249            8KB
  3065.       each 128 fields       4KB more
  3066.  
  3067.  This memory is released when you close the file with CLOSE_DATA_XB. or issue 
  3068.  EXIT_XB. 
  3069.  
  3070.  Note:  You must open the data file before you can open or create any of its 
  3071.  index files. 
  3072.  
  3073.  When BULLET creates a DBF, it forces all fieldnames to upper-case (it's a DBF 
  3074.  requirement) and 0-fills them as well.  On data file opens (OPEN_DATA_XB), it 
  3075.  also does this, and so any header copy (COPY_DATA_HEADER_XB) will have 
  3076.  upper-cased fieldnames (the original file is not changed).  To prevent BULLET 
  3077.  from mapping the fieldnames to upper-case (NLS mapping, though fieldnames 
  3078.  should be standard ASCII characters only), set bit31 of OP.asMode to 1 
  3079.  (0x80000042, for example).  This skips the case mapping.  Zero-filling always 
  3080.  takes place, and starts after the first '\0' byte in the fieldname. 
  3081.  
  3082.  
  3083. ΓòÉΓòÉΓòÉ 9.15. CLOSE_DATA_XB ΓòÉΓòÉΓòÉ
  3084.  
  3085. Pack: HANDLEPACK               Source Example 
  3086.  
  3087.      IN               OUT
  3088.    HP.func          HP.stat
  3089.    HP.handle
  3090.  
  3091.  Close an existing data file. 
  3092.  
  3093.  Closing the file updates the file header and releases the memory used by the 
  3094.  file.  Any associated memo file is closed, too.  Any outstanding locks should 
  3095.  be unlocked before calling this routine. 
  3096.  
  3097.  Note:  Remaining locks belonging to this handle are released by the OS upon 
  3098.  the successful close. 
  3099.  
  3100.  
  3101. ΓòÉΓòÉΓòÉ 9.16. STAT_DATA_XB ΓòÉΓòÉΓòÉ
  3102.  
  3103. Pack: STATDATAPACK             Source Example 
  3104.  
  3105.      IN               OUT
  3106.    SDP.func         SDP.stat         SDP.recordLength
  3107.    SDP.handle       SDP.fileType     SDP.xactionFlag
  3108.                     SDP.flags        SDP.encryptFlag
  3109.                     SDP.progress     SDP.herePtr
  3110.                     SDP.morePtr      SDP.memoHandle
  3111.                     SDP.fields       SDP.memoBlockSize
  3112.                     SDP.asMode       SDP.memoFlags
  3113.                     SDP.filenamePtr  SDP.memoLastRecord
  3114.                     SDP.fileID       SDP.memoLastSize
  3115.                     SDP.lastUpdate   SDP.lockCount
  3116.                     SDP.records
  3117.  
  3118.  Return information BULLET has on the DBF data file specified by SDP.handle. 
  3119.  
  3120.    Item             Description 
  3121.  stat               Return code of operation 
  3122.  fileType           1 for DBF 
  3123.  flags              Bit0=1 if file has changed since last flush (dirty) 
  3124.                     Bit1=1 if the file has its entire region locked (full lock) 
  3125.                     Bit2=1 if the file has a shared lock in use (cannot write 
  3126.                     to it if so) 
  3127.  progress           Percentage of pack operation completed, 1-99, or 0 if done 
  3128.  morePtr            Always 0 
  3129.  fields             Number of fields per record (does not included implicit tag 
  3130.                     field) 
  3131.  asMode             Access-sharing-cache mode as specified at open (excludes 
  3132.                     NoCaseMap bit31) 
  3133.  filenamePtr        Pointer to the filename as used in OPEN_DATA_XB 
  3134.  fileID             ID byte used when the DBF was created (the first byte of 
  3135.                     the file) 
  3136.  lastUpdate         Date of last change (binary: high word=year (1999), low 
  3137.                     byte=day, high byte=month) 
  3138.  records            Number of records in the DBF (includes any delete-tagged 
  3139.                     records) 
  3140.  recordLength       Total length of a data record, including tag field 
  3141.  xactionFlag        Not currently used 
  3142.  encryptFlag        Not currently used 
  3143.  herePtr            Pointer to the internal data control area for this file 
  3144.                     handle 
  3145.  memoHandle         Handle of open memo file (0 if none) 
  3146.  memoBlockSize      Memo file block size (512 is typical, 24 is minimum) 
  3147.  memoFlags          Bit0=1 dirty 
  3148.  memoLastRecord     Last accessed memo record (0 if none; same as 'block 
  3149.                     number') 
  3150.  memoLastSize       Size of last accessed memo record (in bytes, including 8 
  3151.                     bytes overhead) 
  3152.  lockCount          Number of full-locks in force (locked on first, unlocked on 
  3153.                     last) 
  3154.  
  3155.  Typically, your program tracks whether a particular handle belongs to an index 
  3156.  file or data file.  In cases where this is not possible, call the 
  3157.  STAT_HANDLE_XB. routine to determine what file type a handle is. 
  3158.  
  3159.  Note:  In network environments, you should have an exclusive lock on the data 
  3160.  file (and implicitly, therefore, the memo file, if any) before using this 
  3161.  routine to ensure that the information is current.  This also applies to 
  3162.  multi-process environments on a single machine.  This routine is not 
  3163.  mutex-protected.  During the call, the file handle must not be closed by 
  3164.  another thread. 
  3165.  
  3166.  
  3167. ΓòÉΓòÉΓòÉ 9.17. READ_DATA_HEADER_XB ΓòÉΓòÉΓòÉ
  3168.  
  3169. Pack: HANDLEPACK               Source Example 
  3170.  
  3171.      IN               OUT
  3172.    HP.func          HP.stat
  3173.    HP.handle
  3174.  
  3175.  Reload the disk copy of the data header for the opened DBF data file handle, 
  3176.  refreshing the in-memory copy.  Any associated memo file is refreshed, too. 
  3177.  
  3178.  Normally, this routine is not called directly but rather is done automatically 
  3179.  when you full-lock the file (LOCK_XB).  This routine does not refresh the 
  3180.  header if the current state is dirty (SDP.flags, bit0=1); it returns an error 
  3181.  if tried. Since it is recommended that a full-lock be in force before using 
  3182.  this routine (shared or exclusive), and since a full-lock always reloads the 
  3183.  header anyway, calling this routine should never be required.  If ever there 
  3184.  is a reason to use this routine without having a full-lock in force, then, of 
  3185.  course, you may need to.  However, it is not wise to reload the header without 
  3186.  a full-lock (which locks the header).  If you are using your own lock 
  3187.  routines, this call will be very useful. 
  3188.  
  3189.  In single-user, single-tasking systems this routine is not needed.  However, 
  3190.  in a multi-user or multi-tasking system it's possible, and desirable, for two 
  3191.  or more programs to use the same data file.  Consider this scenario: A data 
  3192.  file has 100 records.  Two programs access this data file, both opening it. 
  3193.  Program 1 locks the file, adds a new record, then flushes and unlocks the 
  3194.  file.  Program 1 knows that there are now 101 records in the file.  However, 
  3195.  Program 2 is not aware of the changes that Program 1 made--it thinks that 
  3196.  there are still 100 records in the file.  This out-of-sync situation is easily 
  3197.  remedied by having Program 2 reload the data header from the file on disk. 
  3198.  
  3199.  How does Program 2 know that it needs to reload the header? It doesn't. 
  3200.  Instead, BULLET uses a simple, yet effective, approach when dealing with this. 
  3201.  When BULLET full-locks a file, BULLET automatically reloads the header by 
  3202.  using this routine.  When removing the full-lock, BULLET automatically flushes 
  3203.  the header using FLUSH_DATA_HEADER_XB (unless the current lock is a shared 
  3204.  lock (SDP.flags bit2=1)). 
  3205.  
  3206.  
  3207. ΓòÉΓòÉΓòÉ 9.18. FLUSH_DATA_HEADER_XB ΓòÉΓòÉΓòÉ
  3208.  
  3209. Pack: HANDLEPACK               Source Example 
  3210.  
  3211.      IN               OUT
  3212.    HP.func          HP.stat
  3213.    HP.handle
  3214.  
  3215.  Write the in-memory copy of the data header for the opened DBF data file 
  3216.  handle to disk.  The actual write occurs only if the header has been changed 
  3217.  (the dirty bit is set).  Any associated memo file is flushed, too.  This 
  3218.  routine ensures that the data header on disk matches exactly the data header 
  3219.  that is being maintained by BULLET. 
  3220.  
  3221.  Normally, this routine is not called directly but rather is done automatically 
  3222.  when you unlock the file (UNLOCK_XB).  This routine does not write out the 
  3223.  header if the current lock state is shared (SDP.flags, bit2=1); it returns an 
  3224.  error if tried.  Unlocking a full-lock performs a flush automatically, and so 
  3225.  you may never need to explicitly call this routine.  Also, when relocking from 
  3226.  an exclusive full-lock to a shared full-lock, an automatic flush is performed. 
  3227.  
  3228.  Assume the following: A data file with 100 records.  Your program opens the 
  3229.  data file and adds 1 record.  Physically, there are 101 records on disk. 
  3230.  However, the header image of the data file on disk still reads 100 records. 
  3231.  This isn't a problem, BULLET uses its internal copy of the data header and the 
  3232.  internal copy does read 101 records.  But, if there were a system failure now, 
  3233.  the image of the header would not get updated since the disk image is written 
  3234.  only on a CLOSE_ or FLUSH_DATA_XB, or on EXIT_XB (and also prior to 
  3235.  PACK_RECORDS_XB).  After the system restarts, BULLET opens the file, reads the 
  3236.  header and thinks that there are 100 records.  You lost a record.  Now, if 
  3237.  after that record add your program issues FLUSH_DATA_HEADER_XB, the header on 
  3238.  disk is refreshed with the in-memory copy, keeping the two in sync.  This 
  3239.  routine also updates the directory entry for the file, keeping things neat 
  3240.  there (file size).  Still, it doesn't come without cost: flushing takes 
  3241.  additional time, therefore, you may elect to flush periodically, or whenever 
  3242.  the system is idle. 
  3243.  
  3244.  Note:  You should have a full-lock on the file before using this routine. 
  3245.  
  3246.  
  3247. ΓòÉΓòÉΓòÉ 9.19. COPY_DATA_HEADER_XB ΓòÉΓòÉΓòÉ
  3248.  
  3249. Pack: COPYPACK                 Source Example 
  3250.  
  3251.      IN               OUT
  3252.    CP.func          CP.stat
  3253.    CP.handle
  3254.    CP.filenamePtr
  3255.  
  3256.  Copy the DBF file structure of an open data file to a new file. 
  3257.  
  3258.  This routine makes it easy for you to duplicate the structure of an existing 
  3259.  DBF file without having to specify all the information needed by 
  3260.  CREATE_DATA_XB. The resultant DBF will be exactly like the source, including 
  3261.  number of fields and field descriptions, and an empty memo file, if 
  3262.  applicable. It contains 0 records.  It may be opened as a regular Bullet data 
  3263.  file. 
  3264.  
  3265.  A typical use for this is to create a work file, where only a subset of 
  3266.  records is required.  For example:  You want to process all records of those 
  3267.  whose last name starts with A.  Copy the header to a work file, use GET_XB 
  3268.  routines to get records meeting the criterion, writing those that fit the 
  3269.  criterion to the work file (using either Add/Reindex, or Insert).  A new index 
  3270.  can be specified, or an existing index can be copied using 
  3271.  COPY_INDEX_HEADER_XB. 
  3272.  
  3273.  
  3274. ΓòÉΓòÉΓòÉ 9.20. ZAP_DATA_HEADER_XB ΓòÉΓòÉΓòÉ
  3275.  
  3276. Pack: HANDLEPACK               Source Example 
  3277.  
  3278.      IN               OUT
  3279.    HP.func          HP.stat
  3280.    HP.handle
  3281.  
  3282.  Delete all records in a DBF data file. 
  3283.  
  3284.  This routine is similar to COPY_DATA_HEADER_XB except for one major 
  3285.  difference: All data records in the source file are physically deleted.  No 
  3286.  action is performed on the DBF's memo file, if any. 
  3287.  
  3288.  If you have a DBF file with 100 records and use ZAP_DATA_HEADER_XB on it, all 
  3289.  100 records will be physically deleted and the file truncated as if no records 
  3290.  were ever in the file.  All data records are lost forever. 
  3291.  
  3292.  
  3293. ΓòÉΓòÉΓòÉ 9.21. CREATE_INDEX_XB ΓòÉΓòÉΓòÉ
  3294.  
  3295. Pack: CREATEINDEXPACK          Source Example 
  3296.  
  3297.      IN               OUT
  3298.    CIP.func         CIP.stat
  3299.    CIP.filenamePtr
  3300.    CIP.keyExpPtr
  3301.    CIP.xbLink
  3302.    CIP.sortFunction
  3303.    CIP.codePage
  3304.    CIP.countryCode
  3305.    CIP.collatePtr
  3306.    CIP.nodeSize
  3307.  
  3308.  Create a new BULLET index file. 
  3309.  
  3310.  Before you can create an index file, you must first have opened (and have 
  3311.  created if necessary) the BULLET DBF data file that it is to index. To open 
  3312.  the data file, use OPEN_DATA_XB.  To create the index file, you need to 
  3313.  provide the name to use, the key expression, the DBF file link handle 
  3314.  (obtained from the OPEN_DATA_XB call), sort function/flags, and optionally, 
  3315.  the code page, country code, and collate table.  There's also a node size 
  3316.  parameter.  Select 512, 1024, or 2048 bytes. 
  3317.  
  3318.  Note:  BULLET has an optional external data mode where only indexing is done 
  3319.  -- no data file link is used.  In this mode, BULLET manages the index files of 
  3320.  the key and key data you provide (key data is any 32-bit item, e.g., a record 
  3321.  number, offset, etc.).  This would be useful for indexing non-DBF files, even 
  3322.  files with variable-length records. 
  3323.  
  3324.  Filename 
  3325.  
  3326.  The drive and path must exist if used as part of the filename. Long filenames 
  3327.  may be used if supported by the file system in use. 
  3328.  
  3329.  Key Expression 
  3330.  
  3331.  The key expression is an ASCIIZ string composed of the elements that are to 
  3332.  make up this index file's key.  The key can be composed of any or all of the 
  3333.  fields in the DBF data record, or sub-strings within any of those fields. Up 
  3334.  to 16 component parts can be used in the expression. 
  3335.  
  3336.  Two functions are supported in evaluating a key expression. These are SUBSTR() 
  3337.  and UPPER(): 
  3338.  
  3339.  SUBSTR() extracts part of a field's data starting at a particular position for 
  3340.  x number of characters.  The first position is 1. 
  3341.  
  3342.  UPPER() converts all lower-case letters to their upper-case equivalent.  Since 
  3343.  BULLET supports NLS, UPPER() conversion is not required for proper sorting of 
  3344.  mixed-case text strings. 
  3345.  
  3346.  Any name used in the key expression must be a valid field name in the DBF data 
  3347.  file.  Below are a few sample key expressions for the given data file 
  3348.  structure: 
  3349.  
  3350.      Name  Type Len DC
  3351.     FNAME    C   25  0
  3352.     LNAME    C   25  0
  3353.     SSN      C    9  0
  3354.     DEPT     N    5  0
  3355.  
  3356.  A few example key expression strings for this structure: 
  3357.  
  3358.     keyExpression[]="LNAME";
  3359.     keyExpression[]="LNAME+FNAME";
  3360.     keyExpression[]="SUBSTR(LNAME,1,4)+SUBSTR(FNAME,1,1)+SUBSTR(SSN,6,4)";
  3361.     keyExpression[]="UPPER(LNAME+FNAME)";  // for ASCII sort function only
  3362.     keyExpression[]="DEPT+SSN";
  3363.  
  3364.  In the last example above, even though DEPT is a numeric field type (N), it 
  3365.  can still be used as a component of a multi-part character key with SSN (whose 
  3366.  type is set to character).  This because numeric fields in dBASE DBF data 
  3367.  files are ASCII digits, not binary values, and are sorted according to the 
  3368.  ASCII value or NLS weight. 
  3369.  
  3370.  The key expression is parsed when the index file is created (this routine) and 
  3371.  also when reindexed (REINDEX_XB.). The parser() function, which parses the key 
  3372.  expression, may be replaced by a programmer-supplied function if additional 
  3373.  functionality is needed.  See Custom Expression Parser Routine for details. 
  3374.  
  3375.  DBF File Link Handle (xbLink) 
  3376.  
  3377.  Since BULLET evaluates the key expression when the file is created (this 
  3378.  routine) or during reindex, it must have access to the DBF file to verify that 
  3379.  the key expression is valid.  You must, therefore, supply the OS file handle 
  3380.  of the opened DBF data file.  If you later change the structure of the DBF 
  3381.  data file (add new fields, remove others, etc.), you must use the reindex 
  3382.  routine to re-evaluate the key expression.  If the key expression is no longer 
  3383.  valid after the data file changes (key field has changed names, etc.), then 
  3384.  you must create a brand new index file with this routine, supplying the new 
  3385.  key expression, rather than reindexing. 
  3386.  
  3387.  Note:  Handles 0-2 are reserved handles and should never be used for any 
  3388.  BULLET routine.  Also, .xbLink of -1 is reserved by BULLET to indicate an 
  3389.  external data index for index create and open routines. 
  3390.  
  3391.  Sort Function 
  3392.  
  3393.  The sort function specifies the sort method for the index file. Essentially, 
  3394.  this defines the compare function used by the access methods employed by 
  3395.  BULLET when doing any type of key access (reading and writing).  There are six 
  3396.  intrinsic sort compare functions available, with an additional 10 sort compare 
  3397.  functions that can be specified by the programmer (see Custom Sort-Compare 
  3398.  Functions). 
  3399.  
  3400.  While not recommended, duplicate key values are supported and managed by 
  3401.  BULLET.  The flag DUPS_ALLOWED is OR'ed with the sort function value to 
  3402.  specify this.  Generally, it is not acceptable to allow duplicate keys for an 
  3403.  index; there should be one key identifying one record without any further 
  3404.  investigation needed to determine if the key is indeed for that record.  This 
  3405.  is not possible, not consistently so, when duplicate keys exist.  It is much 
  3406.  simpler to define your key so that duplicates are not generated, than it is to 
  3407.  deal with duplicate keys once you have them.  If an attempt to insert a key 
  3408.  that already exists in the index file is made, and DUPS_ALLOWED was not 
  3409.  specified when the index file was created, the insert fails (either a 
  3410.  STORE_KEY_XB, an INSERT_XB, or a REINDEX_XB operation), and error 
  3411.  EXB_KEY_EXISTS is returned. 
  3412.  
  3413.  For Windows, the sort function flag, USE_ANSI_CHARSET may be specified. This 
  3414.  instructs Bullet to use the ANSI character set (i.e., Windows character set) 
  3415.  for the current system code page.  The default is USE_OEM_CHARSET, which is 
  3416.  used by MS-DOS and OS/2. 
  3417.  
  3418.  Only data contained within a record should be used to build a key. The 
  3419.  physical record number is not part of the data of a record since it can change 
  3420.  at any time without you knowing about it (during a pack, for example).  Do not 
  3421.  use the record number in an attempt to generate unique keys.  Only use what is 
  3422.  available in the data record itself, so that the key can be built, or rebuilt, 
  3423.  at any time. 
  3424.  
  3425.  The intrinsic sort compare functions of BULLET are: 
  3426.  
  3427.  ASCII_SORT    1 - ASCII  (up to 16 key components) 
  3428.  NLS_SORT      2 - NLS   (up to 16 key components) 
  3429.  S16_SORT      3 - 16-bit signed integer (single component) 
  3430.  U16_SORT      4 - 16-bit unsigned integer (single component) 
  3431.  S32_SORT      5 - 32-bit signed integer (single component) 
  3432.  U32_SORT      6 - 32-bit unsigned integer (single component) 
  3433.  
  3434.  To expand on the basic functionality provided by BULLET, you can supply your 
  3435.  own parser, build, and sort compare routines, and have BULLET use them 
  3436.  instead.  With your own routines in place, you can have BULLET do just about 
  3437.  anything with regard to the index file, including evaluating the key 
  3438.  expression dynamically; using more components; allowing multi-part binary 
  3439.  keys; and more. 
  3440.  
  3441.  Generally, character data (type C) is left-justified, and unused space is 
  3442.  padded with the SPACE character (ASCII 32).  It is permissible to use C-type 
  3443.  strings, or to 0-fill unused space. 
  3444.  
  3445.  Numeric data (type N) is right-justified, with leading space to be padded with 
  3446.  the SPACE character.  It is not permissible to use 0-fill leading bytes 
  3447.  (literal '0' can used, however).  Since the field is right-justified, it is 
  3448.  not generally desirable to terminate the field with a 0 byte, either.  If a 
  3449.  decimal count is specified (not 0),the decimal point location is to be the 
  3450.  same for all entries in this field.  The field description must match the 
  3451.  actual data:  If the field length and field decimal count was specified as 
  3452.  10.2 (10 total bytes, 2 digits to the right of the decimal, then the data is 
  3453.  to be formatted so that '-234567.90' is the longest data that is to be entered 
  3454.  in that field.  All entries in all records for this field must be of the same 
  3455.  format.  For example, " 987654.21", or "   23.01", or "   -1.99" (note the 
  3456.  leading spaces).  Numeric data is indexed as ASCII values (i.e., the key 
  3457.  remains character digits) unless a binary sort function is specified. 
  3458.  
  3459.  Using one of the binary integer sort compare functions requires the following: 
  3460.  
  3461.  1.  Single component expression. 
  3462.  2.  Field type must be N if the field has ASCII digits, or if the data is 
  3463.      binary, then the field type must be Y (actually, anything but N). 
  3464.  3.  If ASCII digits, the value must fit into the function size (-32768 to 
  3465.      32767 or 0-65535 for signed/unsigned 16-bit; 2,147,483,647 to 
  3466.      -2,147,483,648 or 0-4,294,967,295 for 32-bit signed/unsigned values). 
  3467.  
  3468.  Although not dBASE compatible, you may use binary fields in your data records. 
  3469.  The Xbase standard always has ASCII data in the data fields, even if the field 
  3470.  is numeric.  For example, an 'N' type field of 8.2 (total 
  3471.  length.decimal-count) is stored as an ASCII text string in the data record, 
  3472.  say, a string like " 1100.55" (there is no \0 string terminator).  If you want 
  3473.  dBASE compatibility, your field data must be ASCII.  However, if you can forgo 
  3474.  this requirement, you can use binary values in the fields. 
  3475.  
  3476.  To do this you must specify a field type of 'Y' (actually, anything but an 
  3477.  'N') and, if it is to be used as a key field, also set the sort function to 
  3478.  the appropriate type (S16_SORT, etc.).  The field length 
  3479.  (fieldList[x].fieldLen) for a 'Y' field type is 2 if 16-bit, and 4 if 32-bit. 
  3480.  For 64-bit integers, a custom sort-copmare function is required since there is 
  3481.  no intrinsic 64-bit function available. 
  3482.  
  3483.  Note:  'B' should not be used as a binary field type marker since dBASE V uses 
  3484.  'B' to signify a binary-data memo file field.  Bullet makes no distinction in 
  3485.  its memo file data; anything can be place in them.  Typically, your memo 
  3486.  fields are marked as 'M' in Bullet, but could also be 'B' or 'G'. 
  3487.  
  3488.  The key expression string you specify may be up to 159 characters, and 
  3489.  evaluate out to 64 bytes (62 bytes if DUPS_ALLOWED is specified).  The 
  3490.  expression string must be 0-terminated, as are all strings used by BULLET 
  3491.  itself (filenames, etc.). 
  3492.  
  3493.  National Language Support (NLS) 
  3494.  
  3495.  National Language Support is available to properly sort most languages' 
  3496.  alphabets.  BULLET uses NLS to build the collate sequence table (sort table) 
  3497.  that it uses to ensure proper sorting of mixed-case keys as well as the 
  3498.  sorting of foreign language alphabets which use extended-ASCII.  In order for 
  3499.  BULLET to use the proper collate table, it must know what code page and 
  3500.  country code to use.  If not supplied, Bullet gets this information directly 
  3501.  from the OS, which provides the cc/cp for the current process.  If you supply 
  3502.  cc/cp, the code page must be loaded or an error is returned (see OS/2's CHCP 
  3503.  command).  The collate table generated is made part of the index file so that 
  3504.  all subsequent access to the index file maintains the original sort order, 
  3505.  even if, say, the MIS shop is moved to another location/computer system using 
  3506.  another country code/code page.  These three items are discussed below. 
  3507.  
  3508.  Code Page 
  3509.  
  3510.  To use the default code page of the current process, specify a code page of 0. 
  3511.  The OS is queried for the current code page and this code page is then used. 
  3512.  Any valid and available code page can be specified. This is used only if a 
  3513.  custom sort-compare or NLS sort is specified. 
  3514.  
  3515.  Country Code 
  3516.  
  3517.  To use the default country code of the current process, specify a country code 
  3518.  of 0.  The OS is queried for the current country code and this code is then 
  3519.  used.  Any valid country code can be specified.  This is used only if a custom 
  3520.  sort-compare or NLS sort is specified. 
  3521.  
  3522.  Custom Collate Table 
  3523.  
  3524.  If a null-pointer is specified, and a custom sort-compare or NLS sort is 
  3525.  specified, BULLET queries the OS for the collate sequence table to use based 
  3526.  on the code page and country code specified.  Otherwise, the supplied table is 
  3527.  used.  Intrinsic sorts other than NLS use no collate table, and the country 
  3528.  code or code page are not used, either. 
  3529.  
  3530.  To use a sort weight table of your own choosing, supply a non-NULL pointer to 
  3531.  this parameter.  If non-NULL, the passed table is used for sort compares.  The 
  3532.  table is composed of 256 weight values, one per character.  For example, table 
  3533.  position 65 ('A') and table position 97 ('a') could both be weighted 65, so 
  3534.  that that each are considered equal when sorted.  If a custom sort-compare 
  3535.  function was specified, this sort table may, or may not, be used -- it depends 
  3536.  on whether the sort compare function uses the table (it's all up to the custom 
  3537.  sort-compare function's logic). 
  3538.  
  3539.  Typically, you set both the code page and country code = 0, and the collate 
  3540.  table pointer to NULL. 
  3541.  
  3542.  Node Size 
  3543.  
  3544.  The index file is read and written in node-size chunks.  The larger the node 
  3545.  size, the more keys are read or written per chunk.  Generally, a smaller node 
  3546.  size offers better random key access, while a larger node size offers better 
  3547.  sequential key access. 
  3548.  
  3549.  Typically, an average node utilizes 66% of the node space for keys (a very 
  3550.  small number may contain only a few keys, while some may be filled 
  3551.  completely).  In a 512-byte node file, for a key length of 8, there is room 
  3552.  for (512-5)/(keylength+8) nodes, or 31 keys.  Since a typical node is filled 
  3553.  to 66%, that means about 20 keys per node.  For a 2048-byte node file, same 
  3554.  parameters, there is room for (2048-5)/(keylength+8), or 127 keys.  At the 
  3555.  standard 66% load, there are typically 83 keys per 2K node.  That's 3 more 
  3556.  keys per 2K of disk than the 512-byte node gives for 4 nodes (20 keys*4), The 
  3557.  trade-off is that each node is 4 times as large, and so requires 4 times more 
  3558.  searching.  Actual performance differences may be minimal, or may be great. 
  3559.  Run tests on expected data to determine the best for the data and access used. 
  3560.  
  3561.  
  3562. ΓòÉΓòÉΓòÉ 9.22. OPEN_INDEX_XB ΓòÉΓòÉΓòÉ
  3563.  
  3564. Pack: OPENPACK                 Source Example 
  3565.  
  3566.      IN               OUT
  3567.    OP.func          OP.stat
  3568.    OP.filenamePtr   OP.handle
  3569.    OP.asMode
  3570.    OP.xbLink
  3571.  
  3572.  Open an existing index file for use.  For index opens, three parameters are 
  3573.  specified:  the filename, the access-sharing mode, and the handle of the open 
  3574.  DBF file that this file indexes.  It is required to open the data file before 
  3575.  you can open its related index file. 
  3576.  
  3577.  Note:  Handles 0-2 are reserved handles and should never be used for any 
  3578.  BULLET routine.  Also, .xbLink of -1 is reserved by BULLET to indicate an 
  3579.  external data index for index create and open routines. 
  3580.  
  3581.  On a successful open, the file handle is returned.  Use this handle for all 
  3582.  further access to this file. 
  3583.  
  3584.  Once open, you can get information on the index file by using STAT_INDEX_XB. 
  3585.  
  3586.  Each index file that you open allocates and commits 4K bytes for internal use 
  3587.  (this will vary if SET_VECTORS_XB with VECTOR_MALLOC is used).  This memory is 
  3588.  released when you close the file with CLOSE_INDEX_XB or issue EXIT_XB, or your 
  3589.  program terminates. 
  3590.  
  3591.  The OP.asMode has optional cache mode settings.  The caching modes cover 
  3592.  locality, write-through, and skip cache.  Locality is typically mostly random 
  3593.  (RND_LOCALITY), but may be mostly sequential if the data file has been sorted 
  3594.  and the index file recently reindexed and processing is mostly in-order (first 
  3595.  to last, rather than random).  Locality is used to tune the cache.  Also, 
  3596.  normally, data is written to the cache with control returning immediately to 
  3597.  the program before the disk is written (an asynchronous write).  To force the 
  3598.  write to take place before control is returned (a synchronous write), use the 
  3599.  WRITE_THROUGH mode.  To skip the cache completely, use the SKIP_CACHE mode. 
  3600.  This, as all OP.asMode settings, affects this file handle only. 
  3601.  
  3602.  BULLET has an optional external data mode where only indexing is done -- no 
  3603.  data file link is used.  In this mode, BULLET manages the index files of the 
  3604.  key and key data you provide (key data is any 32-bit item, e.g., a record 
  3605.  number, offset, etc.).  This would be useful for indexing non-DBF files, even 
  3606.  files with variable-length records.  Only those routines that do not access 
  3607.  the data file may be used (any routine using AP.recPtr, for example INSERT_XB, 
  3608.  could not be used, but NEXT_KEY_XB, STORE_KEY, etc., may). 
  3609.  
  3610.  
  3611. ΓòÉΓòÉΓòÉ 9.23. CLOSE_INDEX_XB ΓòÉΓòÉΓòÉ
  3612.  
  3613. Pack: HANDLEPACK               Source Example 
  3614.  
  3615.      IN               OUT
  3616.    HP.func          HP.stat
  3617.    HP.handle
  3618.  
  3619.  Close an open index file. 
  3620.  
  3621.  Closing the file updates the file header and releases the memory used by the 
  3622.  file.  Any outstanding locks should be unlocked before calling this routine. 
  3623.  
  3624.  Note:  Remaining locks belonging to this handle are released by the OS upon 
  3625.  the successful close. 
  3626.  
  3627.  
  3628. ΓòÉΓòÉΓòÉ 9.24. STAT_INDEX_XB ΓòÉΓòÉΓòÉ
  3629.  
  3630. Pack: STATINDEXPACK            Source Example 
  3631.  
  3632.      IN               OUT
  3633.    SIP.func         SIP.stat         SIP.keyLength
  3634.    SIP.handle       SIP.fileType     SIP.keyRecNo
  3635.                     SIP.flags        SIP.keyPtr
  3636.                     SIP.progress     SIP.herePtr
  3637.                     SIP.morePtr      SIP.codePage
  3638.                     SIP.xbLink       SIP.countryCode
  3639.                     SIP.asMode       SIP.CTptr
  3640.                     SIP.filenamePtr  SIP.nodeSize
  3641.                     SIP.fileID       SIP.sortFunction
  3642.                     SIP.keyExpPtr    SIP.lockCount
  3643.                     SIP.keys
  3644.  
  3645.  Return information BULLET has on the index file specified by SIP.handle. 
  3646.  
  3647.    Item             Description 
  3648.  stat               Return code of operation 
  3649.  fileType           0 for index, IX3 
  3650.  flags              Bit0=1 if file has changed since last flush (dirty) 
  3651.                     Bit1=1 if the file has its entire region locked (full lock) 
  3652.                     Bit2=1 if the file has a shared full-lock in use (cannot 
  3653.                     write to it if so) 
  3654.  progress           Percentage of reindex operation completed, 1-99, or 0 if 
  3655.                     done 
  3656.  morePtr            Always 0 
  3657.  xbLink             handle of the open DBF file this file indexes 
  3658.  asMode             Access-sharing-cache mode as specified at open 
  3659.  filenamePtr        Pointer to the filename as used in OPEN_INDEX_XB 
  3660.  fileID             '31ch' (the first four bytes of the file) 
  3661.  keyExpPtr          Pointer to the key expression used when the index was 
  3662.                     created 
  3663.  keys               Number of keys in the index file 
  3664.  keyLength          Length of the key, including 2-byte enumerator if 
  3665.                     DUPS_ALLOWED 
  3666.  keyRecNo           The DBF record number that the last accessed key indexes 
  3667.  keyPtr             Pointer to the last accessed key (valid for keyLength 
  3668.                     bytes) 
  3669.  herePtr            Pointer to the internal data control for this file 
  3670.  codePage           Code page used when the index file was created (the actual 
  3671.                     code page) 
  3672.  countryCode        Country code used when the index file was created (the 
  3673.                     actual country code) 
  3674.  CTptr              Pointer to the collate sequence table used for NLS sorting 
  3675.                     (each index file has its own sequence table) or NULL if not 
  3676.                     an NLS index 
  3677.  nodeSize           Size of an index node, 512, 1024, or 2048 bytes, as 
  3678.                     specified during create 
  3679.  sortFunction       The index sort-compare method (low word) and the sort flags 
  3680.                     (high word), with sort-compare values 1-9 being intrinsic, 
  3681.                     and 10-19 being custom functions; the DUPS_ALLOWED flag is 
  3682.                     bit0 of the high word (allowed if set) 
  3683.  lockCount          Number of full-locks in force (locked on first, unlocked on 
  3684.                     last) 
  3685.  
  3686.  Typically, your program tracks whether a particular handle belongs to an index 
  3687.  file or a data file.  In cases where this is not possible, call the 
  3688.  STAT_HANDLE_XB routine to determine what file type a handle is. 
  3689.  
  3690.  Note:  In network environments, you should have an exclusive lock on the index 
  3691.  file before using this routine to ensure that the information is current. 
  3692.  This routine is not mutex-protected.  During the call, the file handle must 
  3693.  not be closed by another thread. 
  3694.  
  3695.  
  3696. ΓòÉΓòÉΓòÉ 9.25. READ_INDEX_HEADER_XB ΓòÉΓòÉΓòÉ
  3697.  
  3698. Pack: HANDLEPACK               Source Example 
  3699.  
  3700.      IN               OUT
  3701.    HP.func          HP.stat
  3702.    HP.handle
  3703.  
  3704.  Reload the disk copy of the index header for the opened index file handle, 
  3705.  refreshing the in-memory copy. 
  3706.  
  3707.  Normally, this routine is not called directly but rather is done automatically 
  3708.  when you full-lock the file (LOCK_XB).  This routine does not refresh the 
  3709.  header if the current state is dirty (SIP.flags, bit0=1); it returns an error 
  3710.  if tried. Since it is recommended that a full-lock be in force before using 
  3711.  this routine (shared or exclusive), and since a full-lock always reloads the 
  3712.  header anyway, calling this routine should never be required.  If ever there 
  3713.  is a reason to use this routine without having a full-lock in force, then, of 
  3714.  course, you may need to.  However, it is not wise to reload the header without 
  3715.  a full-lock (which locks the header).  If you are using your own lock 
  3716.  routines, this call will be very useful. 
  3717.  
  3718.  In single-user, single-tasking systems this routine is not needed.  However, 
  3719.  in a multi-user or multi-tasking system it's possible, and desirable, for two 
  3720.  or more programs to use the same data file.  Consider this scenario: An index 
  3721.  file has 100 keys.  Two programs access this index file, both opening it. 
  3722.  Program 1 locks the file, adds a new key, then flushes and unlocks the file. 
  3723.  Program 1 knows that there are now 101 keys in the file.  However, Program 2 
  3724.  is not aware of the changes that Program 1 made--it thinks that there are 
  3725.  still 100 keys in the file.  This out-of-sync situation is easily remedied by 
  3726.  having Program 2 reload the index header from the file on disk. 
  3727.  
  3728.  How does Program 2 know that it needs to reload the header? It doesn't. 
  3729.  Instead, BULLET uses a simple, yet effective, approach when dealing with this. 
  3730.  When BULLET full-locks a file, it automatically reloads the header using this 
  3731.  routine.  When removing the full-lock, BULLET automatically flushes the header 
  3732.  using FLUSH_INDEX_HEADER_XB (unless the current lock is a shared lock 
  3733.  (SIP.flags bit2=1)). 
  3734.  
  3735.  
  3736. ΓòÉΓòÉΓòÉ 9.26. FLUSH_INDEX_HEADER_XB ΓòÉΓòÉΓòÉ
  3737.  
  3738. Pack: HANDLEPACK               Source Example 
  3739.  
  3740.      IN               OUT
  3741.    HP.func          HP.stat
  3742.    HP.handle
  3743.  
  3744.  Write the in-memory copy of the index header for the opened index file handle 
  3745.  to disk.  The actual write occurs only if the header has been changed. This 
  3746.  ensures that the index header on disk matches exactly the index header that is 
  3747.  being maintained by BULLET. 
  3748.  
  3749.  Normally, this routine is not called directly but rather is done automatically 
  3750.  when you unlock the file (UNLOCK_XB).  This routine does not write out the 
  3751.  header if the current lock state is shared (SIP.flags, bit2=1); it returns an 
  3752.  error if tried.  Unlocking a full-lock performs a flush automatically, and so 
  3753.  you may never need to explicitly call this routine.  Also, when relocking from 
  3754.  an exclusive full-lock to a shared full-lock, an automatic flush is performed. 
  3755.  
  3756.  Assume the following: An index file with 100 keys.  Your program opens the 
  3757.  index file and adds 1 key.  Physically, there are 101 keys on disk.  However, 
  3758.  the header image of the index file on disk still reads 100 keys.  This isn't a 
  3759.  problem; BULLET uses its in-memory copy of the index header and the in-memory 
  3760.  copy does read 101 keys.  But, if there were a system failure after the key 
  3761.  add, the disk image of the header would not get updated since the disk image 
  3762.  is written only on a CLOSE_ or FLUSH_INDEX_XB, or on EXIT_XB (and also prior 
  3763.  to REINDEX_XB).  After the system restarts, BULLET opens the file, reads the 
  3764.  header and thinks that there are 100 keys.  You lost a key.  Now, if after 
  3765.  that key was added, your program issues a FLUSH_INDEX_HEADER_XB, the header on 
  3766.  disk is refreshed with the in-memory copy, keeping the two in sync.  The 
  3767.  routine updates the directory entry, keeping things neat there as well (file 
  3768.  size).  Still, it doesn't come without cost: flushing will take additional 
  3769.  time, therefore, you may elect to flush periodically, or whenever the system 
  3770.  is idle. 
  3771.  
  3772.  Note:  You should have a full-lock on the file before using this routine. 
  3773.  
  3774.  
  3775. ΓòÉΓòÉΓòÉ 9.27. COPY_INDEX_HEADER_XB ΓòÉΓòÉΓòÉ
  3776.  
  3777. Pack: COPYPACK                 Source Example 
  3778.  
  3779.      IN               OUT
  3780.    CP.func          CP.stat
  3781.    CP.handle
  3782.    CP.filenamePtr
  3783.  
  3784.  Copy the index file structure of an open index file to another file. 
  3785.  
  3786.  This routine duplicates the structure of an existing index file without having 
  3787.  to re-specify the information needed by CREATE_INDEX_XB. The resultant index 
  3788.  file will be exactly like the source, including sort function and key 
  3789.  expression.  It contains 0 keys. 
  3790.  
  3791.  
  3792. ΓòÉΓòÉΓòÉ 9.28. ZAP_INDEX_HEADER_XB ΓòÉΓòÉΓòÉ
  3793.  
  3794. Pack: HANDLEPACK               Source Example 
  3795.  
  3796.      IN               OUT
  3797.    HP.func          HP.stat
  3798.    HP.handle
  3799.  
  3800.  Delete all keys from an index file. 
  3801.  
  3802.  This routine is similar to COPY_INDEX_HEADER_XB except for one major 
  3803.  difference: All keys in the source file are physically deleted. 
  3804.  
  3805.  If you have an index file with 100 keys and issue ZAP_INDEX_HEADER_XB, all 100 
  3806.  keys will be physically deleted and the file truncated to 0 keys. REINDEX_XB 
  3807.  can be used to rebuild the index file. 
  3808.  
  3809.  Note:  Since BULLET reindexes in place, the use of ZAP is not typically 
  3810.  needed. 
  3811.  
  3812.  
  3813. ΓòÉΓòÉΓòÉ 9.29. GET_DESCRIPTOR_XB ΓòÉΓòÉΓòÉ
  3814.  
  3815. Pack: DESCRIPTORPACK           Source Example 
  3816.  
  3817.      IN               OUT
  3818.    DP.func          DP.stat
  3819.    DP.handle        DP.fieldNumber
  3820.    DP.fieldNumber   DP.fieldOffset
  3821.         -or-        DP.FD.fieldName
  3822.    DP.FD.fieldName  DP.FD.fieldType
  3823.                     DP.FD.fieldLen
  3824.                     DP.FD.fieldDC
  3825.                     DP.FD.altFieldLength
  3826.  
  3827.  Get the field descriptor information for a field by fieldname or by field 
  3828.  position. 
  3829.  
  3830.  To get descriptor info by fieldname, set DP.fieldNumber=0 and set the 
  3831.  DP.FD.fieldname member to the fieldname string.  Fieldnames must be 
  3832.  0-terminated and 0-filled, and must be upper-case, with A-Z and _ valid 
  3833.  fieldname characters.  If the string matches a fieldname in the DBF descriptor 
  3834.  area, that field's descriptor info is returned in DP.FD, (FD is 
  3835.  FIELDDESCTYPE), and its position is returned in FD.fieldNumber and 
  3836.  FD.fieldOffset. 
  3837.  
  3838.  To get descriptor info by field position (i.e., field number), set 
  3839.  DP.fieldNumber to the field's position.  The first is field #1. The "delete 
  3840.  tag" field is not considered a field.  If the position is valid (i.e., greater 
  3841.  than 0 and not beyond the last field),that field's descriptor info is returned 
  3842.  in DP.FD. 
  3843.  
  3844.  This routine lets you work with unknown DBF files -- those created by another 
  3845.  program.  By reading each field descriptor, by number, from 1 to number of 
  3846.  fields (SDP.noFields), you can generate a run-time layout of the DBF file. 
  3847.  Alternatively, you can get input from your user for a fieldname, and locate 
  3848.  the descriptor by name. 
  3849.  
  3850.  Note:  If you need to add or delete a field, be sure to reindex all related 
  3851.  index files so that their key expressions can be re-evaluated. To do this, you 
  3852.  need to create a new data file and build it as you build any other new data 
  3853.  file.  Then, copy record-by-record from the old DBF to the new, using the old 
  3854.  record layout for reads, and the new record layout for writes.  After this, 
  3855.  reindex any index file related to the DBF file.  The old DBF file can then be 
  3856.  deleted. 
  3857.  
  3858.  If non-standard fields are used (i.e., non-char structure members to match 
  3859.  non-ASCII data fields in your non-standard DBF), then be aware that your 
  3860.  compiler more than likely will add padding to align on member-size boundaries. 
  3861.  This will result in a mis-match between your compiler structure and your DBF 
  3862.  structure (as described in fieldList[]).  To prevent this, place #pragma 
  3863.  pack(1) / #pragma pack() around your structures that BULLET uses.  Consult 
  3864.  your particular compiler for alternate methods if it does not support #pragma 
  3865.  pack. 
  3866.  
  3867.  
  3868. ΓòÉΓòÉΓòÉ 9.30. GET_RECORD_XB ΓòÉΓòÉΓòÉ
  3869.  
  3870. Pack: ACCESSPACK               Source Example 
  3871.  
  3872.      IN               OUT
  3873.    AP.func          AP.stat
  3874.    AP.handle        *AP.recPtr
  3875.    AP.recNo
  3876.    AP.recPtr
  3877.  
  3878.  Get the physical record from the data file into a data buffer. 
  3879.  
  3880.  The data buffer, pointed to by AP.recPtr, is typically a struct variable 
  3881.  defined as the DBF record itself is defined.  For example, if the DBF record 
  3882.  has 2 fields, LNAME and FNAME, each 25 characters, then then variable would be 
  3883.  typed as: 
  3884.  
  3885.   struct rectype {
  3886.    CHAR  tag;             /* The Xbase DBF delete tag (must be included) */
  3887.    CHAR  lastName[25];    /* same length as first field's descriptor fieldLen */
  3888.    CHAR  firstName[25];   /* same length as second field's descriptor fieldLen */
  3889.   }; /* 51 */
  3890.   struct rectype recbuff;
  3891.  
  3892.  The first record is at AP.recNo=1.  The last is SDP.records, determined by 
  3893.  STAT_DATA_XB.  The buffer must be at least as large as the record length 
  3894.  (SDP.recordLength). 
  3895.  
  3896.  This method of accessing the data file does not use any indexing.  Generally, 
  3897.  this access method is not used except for special purposes (sequential 
  3898.  processing where order is not required).  The preferred method to access the 
  3899.  data is by one of the keyed GET_XB routines. 
  3900.  
  3901.  If non-standard fields are used (i.e., non-char structure members to match 
  3902.  non-ASCII data fields in your non-standard DBF), then be aware that your 
  3903.  compiler more than likely will add padding to align on member-size boundaries. 
  3904.  This will result in a mis-match between your compiler structure (rectype 
  3905.  above) and your DBF structure (as described in fieldList[]).  To prevent this, 
  3906.  place #pragma pack(1) / #pragma pack() around your structures that BULLET 
  3907.  uses.  Consult your particular compiler for alternate methods if it does not 
  3908.  support #pragma pack. 
  3909.  
  3910.  
  3911. ΓòÉΓòÉΓòÉ 9.31. ADD_RECORD_XB ΓòÉΓòÉΓòÉ
  3912.  
  3913. Pack: ACCESSPACK               Source Example 
  3914.  
  3915.      IN               OUT
  3916.    AP.func          AP.stat
  3917.    AP.handle        AP.recNo
  3918.    AP.recPtr
  3919.  
  3920.  Append the data record in the data buffer to the end of the DBF file. 
  3921.  
  3922.  This method of adding a record involves no indexing.  It is typically used to 
  3923.  build a data file en masse.  Indexing is deferred until all records have been 
  3924.  added, and then quickly indexed using REINDEX_XB. 
  3925.  
  3926.  Since ADD_RECORD_XB is extremely fast, if you have several thousand data 
  3927.  records to be added at once, appending records with this routine and 
  3928.  reindexing when all have been added using REINDEX_XB is often faster than 
  3929.  using INSERT_XB for each record to add. 
  3930.  
  3931.  The record number assigned to the record appended is determined by BULLET, and 
  3932.  that record number is returned in AP.recNo. 
  3933.  
  3934.  If non-standard fields are used (i.e., non-char structure members to match 
  3935.  non-ASCII data fields in your non-standard DBF), then be aware that your 
  3936.  compiler more than likely will adding padding to align on member-size 
  3937.  boundaries.  This will result in a mis-match between your compiler structure 
  3938.  and your DBF structure (as described in fieldList[]).  To prevent this, place 
  3939.  #pragma pack(1) / #pragma pack() around your structures that BULLET uses. 
  3940.  Consult your particular compiler for alternate methods if it does not support 
  3941.  #pragma pack. 
  3942.  
  3943.  
  3944. ΓòÉΓòÉΓòÉ 9.32. UPDATE_RECORD_XB ΓòÉΓòÉΓòÉ
  3945.  
  3946. Pack: ACCESSPACK               Source Example 
  3947.  
  3948.      IN               OUT
  3949.    AP.func          AP.stat
  3950.    AP.handle
  3951.    AP.recNo
  3952.    AP.recPtr
  3953.  
  3954.  Write the updated data record to the physical record number. 
  3955.  
  3956.  This method of updating a data record must not be used if any field being used 
  3957.  as a key field (i.e., part of the key expression) is changed. 
  3958.  
  3959.  This method of updating a record is very fast if you know that that update is 
  3960.  not going to alter any field used as a key in any index file that uses it. You 
  3961.  must, of course, first get the data record into the record buffer.  You can 
  3962.  then change it, and write the update out to disk using this routine. 
  3963.  
  3964.  If you need to change a field that is used as a key field, or part of one 
  3965.  (e.g., SUBSTR()), use the UPDATE_XB routine. 
  3966.  
  3967.  If you plan on reindexing with REINDEX_XB immediately after using this 
  3968.  routine, you may elect to update the data file using this method even if 
  3969.  changing any field used as a key, rather than UPDATE_XB.  This since UPDATE_XB 
  3970.  is very disk intensive.  However, if transaction support is needed (i.e., 
  3971.  updates are dependent on other updates), then UPDATE_XB should be used. 
  3972.  
  3973.  
  3974. ΓòÉΓòÉΓòÉ 9.33. DELETE_RECORD_XB ΓòÉΓòÉΓòÉ
  3975.  
  3976. Pack: ACCESSPACK               Source Example 
  3977.  
  3978.      IN               OUT
  3979.    AP.func          AP.stat
  3980.    AP.handle
  3981.    AP.recNo
  3982.  
  3983.  Tag the record at the physical record number as being deleted. 
  3984.  
  3985.  This does not tag any in-memory copies of the record so be sure to mark any 
  3986.  such copies as being deleted yourself. 
  3987.  
  3988.  The first byte of every DBF record is reserved for the tag field.  This tag is 
  3989.  a space (ASCII 32) if the record is normal, or a * (ASCII 42) if it's marked 
  3990.  as being deleted.  This delete tag is a reserved field in the DBF record and 
  3991.  as such is not defined as a formal field with a descriptor.  Make sure that 
  3992.  you define your in-memory buffers to reserve the first byte for the delete 
  3993.  tag. 
  3994.  
  3995.  The Xbase DBF standard doesn't physically remove records marked as deleted 
  3996.  from the data file.  It doesn't mark them as available/reusable either.  To 
  3997.  physically remove records marked as deleted use PACK_RECORDS_XB 
  3998.  
  3999.  Records can be temporarily marked as deleted during processing and then 
  4000.  recalled to normal status when completed, useful for flagging a record as 
  4001.  having been processed (for example, mass updating using UPDATE_XB).  The 
  4002.  GET_XB routines return the record number associated with a key (in AP.recNo), 
  4003.  and that record number can be used for this routine. 
  4004.  
  4005.  While the DELETE_RECORD_XB and UNDELETE_RECORD_XB routines provided in BULLET 
  4006.  use the * and SPACE characters only, you can use whatever character you want 
  4007.  in the tag field when you fill your record buffer structure's data.  Normally, 
  4008.  you set the tag field to SPACE (x.tag = ' ';), but, for example, if you want 
  4009.  to implement your own, program-level locking you can use the tag field as a 
  4010.  marker to indicate the record is locked (by using an 'L' character, or ID with 
  4011.  bit7=1, or whatever you can think of) and use the very fast UPDATE_RECORD_XB 
  4012.  to set it.  Another possibility is set to aside a field to be used as this, 
  4013.  say, along with the user ID of the lock owner. 
  4014.  
  4015.  The SKIP_TAG_SELECT item in SET_SYSVARS_XB can be set to have the REINDEX_XB 
  4016.  routine not place a key value into the index file if a record has a matching 
  4017.  tag field.  This may be useful if you want to, say, generate an ad hoc index 
  4018.  for only undeleted records. 
  4019.  
  4020.  
  4021. ΓòÉΓòÉΓòÉ 9.34. UNDELETE_RECORD_XB ΓòÉΓòÉΓòÉ
  4022.  
  4023. Pack: ACCESSPACK               Source Example 
  4024.  
  4025.      IN               OUT
  4026.    AP.func          AP.stat
  4027.    AP.handle
  4028.    AP.recNo
  4029.  
  4030.  Tag the record at the physical record number as being normal (not deleted). 
  4031.  
  4032.  This does not tag any in-memory copies of the record so be sure to mark any 
  4033.  such copies as being normal. 
  4034.  
  4035.  This routine removes the * character, as put there by DELETE_RECORD_XB, in the 
  4036.  tag field and replaces it with a SPACE.  The tag field is always overwritten 
  4037.  with a SPACE, regardless of what it was. 
  4038.  
  4039.  
  4040. ΓòÉΓòÉΓòÉ 9.35. DEBUMP_RECORD_XB ΓòÉΓòÉΓòÉ
  4041.  
  4042. Pack: ACCESSPACK               Source Example 
  4043.  
  4044.      IN               OUT
  4045.    AP.func          AP.stat
  4046.    AP.handle
  4047.    AP.recNo
  4048.  
  4049.  Remove the record identified by AP.recNo from the data file if and only if the 
  4050.  record is the last in the file.  The file is automatically flushed before 
  4051.  debumping. 
  4052.  
  4053.  Unlike DELETE_RECORD_XB, this routine physically removes a data record from 
  4054.  the DBF file, provided that the record to delete is the last. STAT_DATA_XB can 
  4055.  be used to identify the last record number (SDP.records is the last).  This, 
  4056.  when used after deleting any and all keys in all index files referencing this 
  4057.  record (see DELETE_KEY_XB), is useful if you are managing a transaction log 
  4058.  and need to back out changes made, beyond what BULLET performs. 
  4059.  
  4060.  If the record is not the last, alternate methods must be used. The simplest, 
  4061.  and often equally as good as physically deleting the record, is to just mark 
  4062.  the record as deleted using DELETE_RECORD_XB and let it remain in the file 
  4063.  until the next PACK_RECORDS_XB.  Another option is to overwrite the record's 
  4064.  data with SPACES, or other appropriate field data (such as HIGH-VALUES, and 
  4065.  use UPDATE_XB), if necessary.  This routine is the only method available to 
  4066.  physically remove a record from the file, short of using PACK_RECORDS_XB. 
  4067.  Removing a record with active keys referencing that record will result in an 
  4068.  access error (ERR_UNEXPECTED_EOF) when accessing that key with GET_XB 
  4069.  routines, or will generate stale results.  Remove any keys that reference this 
  4070.  record before deleting it. 
  4071.  
  4072.  
  4073. ΓòÉΓòÉΓòÉ 9.36. PACK_RECORDS_XB ΓòÉΓòÉΓòÉ
  4074.  
  4075. Pack: ACCESSPACK               Source Example 
  4076.  
  4077.      IN               OUT
  4078.    AP.func          AP.stat
  4079.    AP.handle
  4080.  
  4081.  Rebuild the open DBF file by physically removing all records marked as 
  4082.  deleted. 
  4083.  
  4084.  Packing occurs in place using the existing file.  It is recommended that you 
  4085.  use BACKUP_FILE_XB to copy the current DBF file before using this routine in 
  4086.  case of a failure during the pack process. 
  4087.  
  4088.  The newly packed file is truncated to reflect the current, actual size.  All 
  4089.  records with the tag field set to * are removed from the file. 
  4090.  
  4091.  If there are index files for this DBF file, they must be reindexed after the 
  4092.  pack process by using REINDEX_XB. 
  4093.  
  4094.  Memo files are not affected by this routine.  Before packing, it is 
  4095.  recommended that you traverse the data file to be packed, and for records that 
  4096.  are to be deleted, check to see if there is a memo record. If there is, delete 
  4097.  the memo.  Do this for each such occurrence.  This way, orphaned memo records 
  4098.  will not take up permanent space in the memo file. 
  4099.  
  4100.  
  4101. ΓòÉΓòÉΓòÉ 9.37. GET_MEMO_SIZE_XB ΓòÉΓòÉΓòÉ
  4102.  
  4103. Pack: MEMODATAPACK             Source Example 
  4104.  
  4105.      IN               OUT
  4106.    MDP.func         MDP.stat
  4107.    MDP.dbfHandle    MDP.memoBytes
  4108.    MDP.memoNo
  4109.  
  4110.  Get the number of bytes used by the memo at MDP.memoNo. 
  4111.  
  4112.  Memo file allocation is made in blocks, typically of 512 bytes each. 
  4113.  Therefore, a memo of 10 bytes uses 1 allocation block, as would a 500-byte 
  4114.  memo.  This size is stored with each memo record, and can be retrieved. 
  4115.  Before accessing a memo record, it's a good idea to retrieve the current size 
  4116.  of the memo so you know how large a buffer you may need if you intend to read 
  4117.  it all in, at one time, or even to just know how much to read, in total, 
  4118.  reading parts of it at a time. 
  4119.  
  4120.  The first memo is at MDP.memoNo=1.  The last memo number cannot be easily 
  4121.  determined, but generally this does not need to be known.  The memo number 
  4122.  identifying the memo's location is stored in the memo field area of the DBF 
  4123.  record.  It is stored as a text string (e.g., "0000000001").  This is not a C 
  4124.  string; there is no zero terminator so sprintf() should be used.  This number 
  4125.  is the physical block number at which the memo starts.  Memos are always 
  4126.  stored in consecutive blocks, if more than a single block is needed.  For 
  4127.  example, a memo of 513 bytes uses two blocks, say, #1 and #2.  The next memo 
  4128.  added would use memo #3 (if #3 is available), rather than #2 since #2 was used 
  4129.  by the first memo.  Memo numbers may be reassigned (see UPDATE_MEMO_XB).  The 
  4130.  highest possible memo number is 589,822 (0x8FFFE).  With the standard 512-byte 
  4131.  block size, this allows a memo file to be up to 288MB.  If more memo data 
  4132.  space is needed, use a larger block size (e.g., 2KB block size allows over 1GB 
  4133.  per memo file). 
  4134.  
  4135.  Notice For All Memo Routines 
  4136.  
  4137.  In multitasking environments you should have a full-lock on the DBF file that 
  4138.  owns this memo file, or at least a record lock on the record that owns the 
  4139.  memo number.  In BULLET, locking is not performed on the memo file.  Instead, 
  4140.  the lock is implied when the lock is made on the DBF file.  This because a 
  4141.  memo file is for one DBF file alone, and so if you have a lock on the DBF 
  4142.  before accessing the memo file (for whatever reason), then no other process 
  4143.  may lock the DBF and also access the memo. 
  4144.  
  4145.  This works only if you restrict your access to the memo file if you have a 
  4146.  lock on the DBF master file (the DBF that this DBT memo file belongs to) or on 
  4147.  the DBF record.  For this routine, which only requires access to this memo 
  4148.  record, a record lock is sufficient since no writing is performed.  Further, a 
  4149.  shared lock is all that is required.  This because all that is required to 
  4150.  keep from stepping on other process's toes is that it be known that the 
  4151.  current memo header info (for this memo record), as known to this process, is 
  4152.  the current state of this memo.  In other words, it must be true that the memo 
  4153.  file state on disk exactly matches the memo file state in memory.  With a lock 
  4154.  in place, no other process may gain write access to change this memo, "out 
  4155.  from under you".  A shared lock does allow the other process to read this 
  4156.  memo, and that may be used if no writing is needed. 
  4157.  
  4158.  Each memo routine following states its lock requirements (exclusive full lock, 
  4159.  shared full lock, exclusive record lock, or shared record lock). 
  4160.  
  4161.  
  4162. ΓòÉΓòÉΓòÉ 9.38. GET_MEMO_XB ΓòÉΓòÉΓòÉ
  4163.  
  4164. Pack: MEMODATAPACK             Source Example 
  4165.  
  4166.      IN               OUT
  4167.    MDP.func         MDP.stat
  4168.    MDP.dbfHandle    *MDP.memoPtr
  4169.    MDP.memoNo       MDP.memoBytes
  4170.    MDP.memoPtr
  4171.    MDP.memoOffset
  4172.    MDP.memoBytes
  4173.  
  4174.  Read the specified number of bytes of the memo, starting at the offset, into 
  4175.  the buffer.  The actual number of bytes read is returned. 
  4176.  
  4177.  Use GET_MEMO_SIZE_XB to determine that total number of bytes you may need to 
  4178.  read.  With that, you can allocate a buffer of that size to read the entire 
  4179.  memo into.  Or, you can read chunks of the memo, a chunk at a time, up to the 
  4180.  number of bytes in the memo. 
  4181.  
  4182.  The number of bytes actually read (and stored starting at MDP.memoPtr) is 
  4183.  returned in MDP.memoBytes (overwriting the value you placed there).  If the 
  4184.  number of bytes requested is not the same as the number of bytes returned, you 
  4185.  attempted to read beyond the end of the memo.  BULLET does not return an error 
  4186.  if you try this, which is SOP for file reads, so check the two if you need to 
  4187.  verify this.  An error is returned, however, if you attempt to read at a 
  4188.  starting offset beyond the end of the actual memo data (i.e., MDP.memoOffset > 
  4189.  memo's data size).  The first byte of the memo data is at .memoOffset=0. 
  4190.  
  4191.  It's recommended that a lock be in force on either the DBF (full-lock) or on 
  4192.  the record that this memo belongs to.  A shared lock is okay since no writing 
  4193.  is done. 
  4194.  
  4195.  
  4196. ΓòÉΓòÉΓòÉ 9.39. ADD_MEMO_XB ΓòÉΓòÉΓòÉ
  4197.  
  4198. Pack: MEMODATAPACK             Source Example 
  4199.  
  4200.      IN               OUT
  4201.    MDP.func         MDP.stat
  4202.    MDP.dbfHandle    MDP.memoNo
  4203.    MDP.memoPtr
  4204.    MDP.memoBytes
  4205.  
  4206.  Add the data from the buffer for the specified bytes to a new memo.  The memo 
  4207.  number used is returned. 
  4208.  
  4209.  Any data can be stored in a memo.  The memo number returned can be any value; 
  4210.  it can even be less than the previous add's memo number.  The reason for this 
  4211.  is that an avail-list is kept for the memo file, and any deleted or otherwise 
  4212.  freed blocks become available for re-use.  The memo is stored in the first 
  4213.  contiguous group of free blocks large enough to satisfy the request.  For 
  4214.  example, if MDP.memoBytes is from 1 to (blockSize-8) bytes, the first 
  4215.  available block is used.  If the size needed is greater than 1 block, then the 
  4216.  avail-list is walked and the first contiguous group large enough to satisfy 
  4217.  the request is used.  If none of the avail-list groups is large enough, 
  4218.  ultimately, the new memo data is appended to the end of the file.  This is 
  4219.  also done if there are no avail-list items at all, such as in a memo file that 
  4220.  has never had deletes or updates. 
  4221.  
  4222.  The returned memo is a binary block number (ULONG).  This value should be 
  4223.  converted into an ASCII string (sprintf can be used) and stored in the DBF 
  4224.  data record, in the memo field.  The string should be of the form, 
  4225.  "0000000001" (for MDP.memoNo=1), with leading zeros, but no zero terminator 
  4226.  (exactly 10 bytes in size).  This data record should then be written to disk 
  4227.  using UPDATE_RECORD_XB. Since BULLET can be used in non-standard Xbase mode, 
  4228.  where binary field values can be used, you can omit the conversion from binary 
  4229.  to ASCII if a standard DBF is not required.  Likewise, when accessing a memo, 
  4230.  the conversion of the memo block number from ASCII to binary would not be 
  4231.  required. 
  4232.  
  4233.  It's recommended that a lock be in force on the DBF (full-lock).  A shared 
  4234.  lock may not be used since writing to the memo file, and the DBF record, is 
  4235.  required.  A full lock is required since the memo file header is read and 
  4236.  written. 
  4237.  
  4238.  
  4239. ΓòÉΓòÉΓòÉ 9.40. UPDATE_MEMO_XB ΓòÉΓòÉΓòÉ
  4240.  
  4241. Pack: MEMODATAPACK             Source Example 
  4242.  
  4243.      IN               OUT
  4244.    MDP.func         MDP.stat
  4245.    MDP.dbfHandle    MDP.memoNo
  4246.    MDP.memoNo
  4247.    MDP.memoPtr
  4248.    MDP.memoOffset
  4249.    MDP.memoBytes
  4250.  
  4251.  Update an existing memo.  The update can overwrite current data, append new 
  4252.  data extending the current size, or it can shrink the current size. 
  4253.  
  4254.  Appending data so that the memo is extended may result in a new memo number 
  4255.  returned.  The original memo blocks are made available for reuse (deleted). 
  4256.  Shrinking will not change the memo number, but unused blocks from the shrink 
  4257.  are made available for reuse. 
  4258.  
  4259.  If you want to change anything in the memo at MDP.memoNo, locate its position 
  4260.  within the memo with MDP.memoOffset and set the size in MDP.memoBytes. The 
  4261.  first data byte of a memo is located at MDP.offset=0.  There are 8 bytes of 
  4262.  overhead per memo record (any number of blocks still has only the 8 bytes of 
  4263.  overhead), but these are transparent to any memo access you do. The bytes at 
  4264.  MDP.memoPtr overwrite the current memo data at the position specified.  For 
  4265.  example, if you want to change the first 5 bytes of the first memo, set 
  4266.  MDP.memoNo=1, MDP.memoPtr=yourNewData, MDP.memoOffset=0, and MDP.memoBytes=5. 
  4267.  On return, MDP.memoNo is going to be the same as it was before the update, 
  4268.  since you are not extending the memo size in this example.  Nothing further 
  4269.  needs to be done; the memo is updated. 
  4270.  
  4271.  If you want to add new memo data to an existing memo at MDP.memoNo, such as 
  4272.  adding another line item, or problem report paragraph, etc., set 
  4273.  MDP.memoOffset=theCurrentMemoSize (this locates to the end of the current memo 
  4274.  data), MDP.memoBytes=bytesYouWantToAppend, and MDP.memoPtr=yourDataToAppend. 
  4275.  If the old data size plus your newly added data still fits inside the last 
  4276.  memo block previously used, MDP.memoNo is returned the same as it was on 
  4277.  entry.  However, if the new data requires that more blocks be allocated, the 
  4278.  entire memo is relocated to the next contiguous block group that is large 
  4279.  enough to store the data.  That new block number is returned in MDP.memoNo, 
  4280.  and the old block number and all its blocks are placed on the top of the 
  4281.  avail-list. 
  4282.  
  4283.  If you want to shrink the size as reported by GET_MEMO_SIZE_XB from an 
  4284.  existing memo at MDP.memoNo, set MDP.memoBytes=newSizeYouWant, and 
  4285.  MDP.memoPtr=NULL.  This means that you should have, before making this shrink 
  4286.  call, updated the memo data that occurs within this new size to be the data 
  4287.  size you want to be in the memo.  For example, if you have 10 line items, say, 
  4288.  each 60 bytes long, and want to remove line item #5, you could do it by 
  4289.  reading all 10 line items to memory, moving line items #6 to 10 down one (so 
  4290.  they are now line items #5 to 9, effectively removing old line item #5), and 
  4291.  update the memo (by using memoOffset=0 and memoBytes=9*60).  After this, 
  4292.  though, you still have 10*60 bytes as the memo size (old line item #10 is now 
  4293.  at #9 and still at #10).  Since you want the size to reflect the real data in 
  4294.  the memo, set MDP.memoBytes=90, MDP.memoPtr=NULL, and update this memo number. 
  4295.  Only the memo's size is affected by this particular update.  The size 
  4296.  specified must be smaller than the original size, or an error is returned. 
  4297.  
  4298.  It's recommended that a lock be in force on the DBF (full-lock).  A record 
  4299.  lock should not be used if the update may result in blocks being moved, or the 
  4300.  memo being shrunk by a full block or more.  A shared lock may not be used 
  4301.  since writing to the memo file, and to the DBF record if MDP.memoNo is new, is 
  4302.  required. 
  4303.  
  4304.  
  4305. ΓòÉΓòÉΓòÉ 9.41. DELETE_MEMO_XB ΓòÉΓòÉΓòÉ
  4306.  
  4307. Pack: MEMODATAPACK             Source Example 
  4308.  
  4309.      IN               OUT
  4310.    MDP.func         MDP.stat
  4311.    MDP.dbfHandle
  4312.    MDP.memoNo
  4313.  
  4314.  The memo and all its blocks are made available for reuse. 
  4315.  
  4316.  Before using PACK_RECORDS_XB, you should run through all DBF records and check 
  4317.  for those records that are deleted (record.tag='*') to be sure that any memo 
  4318.  belong to those records are deleted from the memo file.  If this is not done, 
  4319.  orphaned memo records -- those that do not have a DBF record memo field 
  4320.  pointing to it, may be left in the memo file (forever!). 
  4321.  
  4322.  After deleting a memo record, update the DBF record's memo field by writing 
  4323.  <SPACES> (ASCII 32) to the memo field member.  Update this to disk with 
  4324.  UPDATE_RECORD_XB as soon as possible (and before unlocking).  A memo field 
  4325.  with no current memo record is indicated by spaces ("0000000000" should not be 
  4326.  used). 
  4327.  
  4328.  It's recommended that a lock be in force on the DBF (full-lock).  Neither a 
  4329.  record lock nor a shared lock may be used since writing to the memo file 
  4330.  header and the DBF record is required. 
  4331.  
  4332.  
  4333. ΓòÉΓòÉΓòÉ 9.42. MEMO_BYPASS_XB ΓòÉΓòÉΓòÉ
  4334.  
  4335. Pack: MEMODATAPACK             Source Example 
  4336.  
  4337.      IN               OUT
  4338.    MDP.func         MDP.stat
  4339.    MDP.dbfHandle
  4340.    MDP.memoBypass
  4341.  
  4342.  Memo files are created, opened, closed, and flushed/reloaded by their 
  4343.  corresponding DBF data file action.  To perform these tasks asynchronously, 
  4344.  this routine is used.  Bypass routines are: 
  4345.  
  4346.      MDP.memoBypass      Value
  4347.    BYPASS_CREATE_MEMO        1
  4348.    BYPASS_OPEN_MEMO          2
  4349.    BYPASS_CLOSE_MEMO         3
  4350.    BYPASS_READ_MEMO_HEADER   4
  4351.    BYPASS_FLUSH_MEMO_HEADER  5
  4352.  
  4353.  All bypass routines require the handle of the DBF file that this memo is for. 
  4354.  Nothing is returned here, except the result code.  The memo handle from the 
  4355.  open is stored internally, but is available by using STAT_DATA_XB and checking 
  4356.  SDP.memoHandle.  However, none of the BULLET memo routines use the memo handle 
  4357.  directly; all access to the memo file is through the master DBF file handle. 
  4358.  
  4359.  No data is required for input other than the DBF handle and memo bypass 
  4360.  routine to perform (see table above).  All required info is obtained from the 
  4361.  DBF file's information.  You may use an alternate block size, as set via 
  4362.  SET_SYSVARS_XB. 
  4363.  
  4364.  Generally, there is no need to call these routines using this bypass. However, 
  4365.  if you need to create a memo file anew (say, after the initial DBF was 
  4366.  created), and then open it, using these routines is the easiest way to 
  4367.  proceed. 
  4368.  
  4369.  Note:  When creating a memo via the bypass method, the file ID is altered to 
  4370.  indicate that the DBF has a DBT memo file.  The file ID is the first byte of 
  4371.  the DBF file.  The ID is changed by OR'ing 0x88h with the current file ID 
  4372.  value.  The next flush or close updates the disk image of the DBF with the new 
  4373.  file ID.  The next DBF open, then, also opens the DBT memo file created here. 
  4374.  Be sure to always keep the DBT and DBF pairs in the same directory, if moved. 
  4375.  
  4376.  Since the DBF file is already open (and must be to use any of these routines), 
  4377.  you must use the open bypass routine to open the memo if you plan on using it. 
  4378.  Either that, or close the DBF after you've create the memo file, and simply 
  4379.  re-open the DBF, which also, now, opens the DBT memo file. 
  4380.  
  4381.  The other available bypass routines: close, read, and flush, typically will 
  4382.  not be used from this bypass routine.  These operations are done automatically 
  4383.  when their corresponding DBF action is performed, and have little 
  4384.  functionality used on their own. 
  4385.  
  4386.  Before using BYPASS_READ_MEMO_HEADER or BYPASS_FLUSH_MEMO_HEADER, it's 
  4387.  recommended that a lock be in force on the DBF (full-lock). A shared lock can 
  4388.  be used for BYPASS_READ_MEMO_HEADER, but it must be a full lock. 
  4389.  
  4390.  
  4391. ΓòÉΓòÉΓòÉ 9.43. FIRST_KEY_XB ΓòÉΓòÉΓòÉ
  4392.  
  4393. Pack: ACCESSPACK               Source Example 
  4394.  
  4395.      IN               OUT
  4396.    AP.func          AP.stat
  4397.    AP.handle        AP.recNo
  4398.    AP.keyPtr        *AP.keyPtr
  4399.  
  4400.  Retrieve the first key in index order from the index file. 
  4401.  
  4402.  This routine does not access the DBF file and so does not retrieve the data 
  4403.  record.  What it does do is locate the first logical key of the index file, 
  4404.  returning it, and also returning the record number within the DBF that the key 
  4405.  indexes. 
  4406.  
  4407.  To retrieve the data record you can use the GET_RECORD_XB routine.  The 
  4408.  preferred method, however, is to use GET_FIRST_XB, which combines these 
  4409.  operations. 
  4410.  
  4411.  The key returned includes an enumerator if the index file allows duplicate 
  4412.  keys. 
  4413.  
  4414.  This routine is typically used to position the index file to the first key so 
  4415.  as to allow forward in-order access to the keys by using NEXT_KEY_XB. 
  4416.  
  4417.  If an external data file was specified in CREATE_INDEX_XB, the record number 
  4418.  returned by this routine does not refer to a DBF record, but rather is the 
  4419.  value supplied when the key was stored.  This permits index access to your 
  4420.  data files (data files which are not maintained by BULLET, but by you). 
  4421.  
  4422.  
  4423. ΓòÉΓòÉΓòÉ 9.44. EQUAL_KEY_XB ΓòÉΓòÉΓòÉ
  4424.  
  4425. Pack: ACCESSPACK               Source Example 
  4426.  
  4427.      IN               OUT
  4428.    AP.func          AP.stat
  4429.    AP.handle        AP.recNo
  4430.    AP.keyPtr
  4431.  
  4432.  Search for the exact key in the index file. 
  4433.  
  4434.  This routine does not access the DBF file and so does not retrieve the data 
  4435.  record.  What it does do is search for the key in the index, and if found, 
  4436.  returns the record number within the DBF that the key indexes.  The key must 
  4437.  be an exact match, including the enumerator word if the index file is using 
  4438.  non-unique keys. 
  4439.  
  4440.  To retrieve the data record you can use the GET_RECORD_XB routine.  The 
  4441.  preferred method, however, is to use GET_EQUAL_XB, which combines these 
  4442.  operations. 
  4443.  
  4444.  This routine returns no key in *keyPtr since, by definition, you already have 
  4445.  the key in the key buffer if this routine succeeds. 
  4446.  
  4447.  This routine finds only an exact match to the specified key (including the 
  4448.  enumerator if applicable). 
  4449.  
  4450.  
  4451. ΓòÉΓòÉΓòÉ 9.45. EQUAL_OR_GREATER_KEY_XB ΓòÉΓòÉΓòÉ
  4452.  
  4453. Pack: ACCESSPACK               Source Example 
  4454.  
  4455.      IN               OUT
  4456.    AP.func          AP.stat
  4457.    AP.handle        AP.recNo
  4458.    AP.keyPtr        *AP.keyPtr
  4459.  
  4460.  Search for the exact key in the index file and, if not found, get the key that 
  4461.  would have followed it. 
  4462.  
  4463.  This routine is similar to EQUAL_KEY_XB except that this routine returns a key 
  4464.  in *keyPtr (either the same as on entry, or if that is not found, then the 
  4465.  next greater key). 
  4466.  
  4467.  The main benefit of this routine is that it is an atomic operation. It differs 
  4468.  from setting the atomic mode flag of SET_SYSVARS_XB in that this routine 
  4469.  allows a fuzzy starting point:  the key in AP.keyPtr, on entry (IN), need not 
  4470.  exist. 
  4471.  
  4472.  
  4473. ΓòÉΓòÉΓòÉ 9.46. EQUAL_OR_LESSER_KEY_XB ΓòÉΓòÉΓòÉ
  4474.  
  4475. Pack: ACCESSPACK               Source Example 
  4476.  
  4477.      IN               OUT
  4478.    AP.func          AP.stat
  4479.    AP.handle        AP.recNo
  4480.    AP.keyPtr        *AP.keyPtr
  4481.  
  4482.  Search for the exact key in the index file and, if not found, get the key that 
  4483.  would have come before it. 
  4484.  
  4485.  This routine is similar to EQUAL_KEY_XB except that this routine returns a key 
  4486.  in *keyPtr (either the same as on entry, or if that is not found, then the 
  4487.  previous, lesser key). 
  4488.  
  4489.  The main benefit of this routine is that it is an atomic operation. It differs 
  4490.  from setting the atomic mode flag of SET_SYSVARS_XB in that this routine 
  4491.  allows a fuzzy starting point:  the key in AP.keyPtr, on entry (IN), need not 
  4492.  exist. 
  4493.  
  4494.  
  4495. ΓòÉΓòÉΓòÉ 9.47. NEXT_KEY_XB ΓòÉΓòÉΓòÉ
  4496.  
  4497. Pack: ACCESSPACK               Source Example 
  4498.  
  4499.      IN               OUT
  4500.    AP.func          AP.stat
  4501.    AP.handle        AP.recNo
  4502.    AP.keyPtr        *AP.keyPtr
  4503.  
  4504.  Retrieve the next key in index order from the index file. 
  4505.  
  4506.  This routine does not access the DBF file and so does not retrieve the data 
  4507.  record.  What it does do is retrieve the next key of the index, returning it, 
  4508.  and also returning the record number within the DBF that the key indexes. 
  4509.  
  4510.  To retrieve the data record you can use the GET_RECORD_XB routine.  The 
  4511.  preferred method, however, is to use GET_NEXT_XB, which combines these 
  4512.  operations. 
  4513.  
  4514.  The key returned includes an enumerator if the index file allows duplicates. 
  4515.  
  4516.  This routine is typically called after the index file has first been 
  4517.  positioned to a known key using either FIRST_KEY_XB or EQUAL_KEY_XB, or after 
  4518.  a previous NEXT_KEY_XB or even PREV_KEY_XB.  What it basically does is get the 
  4519.  key following the current key, and then makes that key the new current key. ________________________________________________
  4520.  
  4521.  If bit0 of the atomic mode flag of SET_SYSVARS_XB is set to 1, key access is 
  4522.  based on a given starting point.  This simplifies index access in 
  4523.  multi-threaded code, where another thread may have altered the last key 
  4524.  accessed in the index file.  This mode lets you set a starting point for the 
  4525.  operation by supplying in AP.keyPtr the key value to start at. 
  4526.  
  4527.  For example, say you use GET_FIRST_XB.  On return, AP.keyPtr has the the very 
  4528.  first key.  Say elsewhere in your multi-threaded program, another operation 
  4529.  accesses that same index file handle, and performs some other access, where 
  4530.  the last accessed key is no longer the same (i.e., not the first key).  Your 
  4531.  first thread is expecting that a GET_NEXT_XB would get the second key, 
  4532.  however, it very likely won't since the second thread has altered the last 
  4533.  accessed key for that file handle.  By using the atomic mode for key access, 
  4534.  your first thread, which has the first key value in its AP.keyPtr, can do a 
  4535.  call to GET_NEXT_XB and get expected results, since the NEXT operation first 
  4536.  positions to the value in AP.keyPtr and then follows up with a GET_NEXT 
  4537.  operation.  This is performed within the Bullet kernel, and so won't be 
  4538.  interrupted by another thread (i.e., it is an atomic operation).  For this to 
  4539.  work, you must ensure that the AP.keyPtr value is set to the value of the last 
  4540.  accessed key.  This will always be the case unless uninitialized, or you are 
  4541.  using global variables for your threads' AP (AccessPack).  On return from the 
  4542.  operation, AP.keyPtr will once again be set up for another atomic operation. 
  4543.  
  4544.  Note:  You must supply a valid key value for this atomic access mode. 
  4545.  AP.keyPtr must be at least as large as the key length in all cases, and is to 
  4546.  have the starting point for the operation (i.e., the last accessed key).  You 
  4547.  may, alternatively, set the first byte of the key buffer to 0 (but not 
  4548.  AP.keyPtr itself to NULL).  This disables atomic mode for that access, and 
  4549.  reverts to the internally-stored last key accessed as the starting point. 
  4550.  
  4551.  
  4552. ΓòÉΓòÉΓòÉ 9.48. PREV_KEY_XB ΓòÉΓòÉΓòÉ
  4553.  
  4554. Pack: ACCESSPACK               Source Example 
  4555.  
  4556.      IN               OUT
  4557.    AP.func          AP.stat
  4558.    AP.handle        AP.recNo
  4559.    AP.keyPtr        *AP.keyPtr
  4560.  
  4561.  Retrieve the previous key in index order from the index file. 
  4562.  
  4563.  This routine does not access the DBF file and so does not retrieve the data 
  4564.  record.  What it does do is retrieve the previous key of the index, returning 
  4565.  it and also returning the record number within the DBF that the key indexes. 
  4566.  
  4567.  To retrieve the data record you can use the GET_RECORD_XB routine.  The 
  4568.  preferred method, however, is to use GET_PREV_XB, which combines these 
  4569.  operations. 
  4570.  
  4571.  The key returned includes an enumerator if the index file allows duplicates. 
  4572.  
  4573.  This routine is typically called after the index file has first been 
  4574.  positioned to a known key using either LAST_KEY_XB or EQUAL_KEY_XB, or after a 
  4575.  previous PREV_KEY_XB or even NEXT_KEY_XB.  What it basically does is to get 
  4576.  the key previous the current key, and then make that key the new current key. ________________________________________________
  4577.  
  4578.  If bit0 of the atomic mode flag of SET_SYSVARS_XB is set to 1, key access is 
  4579.  based on a given starting point.  This simplifies index access in 
  4580.  multi-threaded code, where another thread may have altered the last key 
  4581.  accessed in the index file.  This mode lets you set a starting point for the 
  4582.  operation by supplying in AP.keyPtr the key value to start at. 
  4583.  
  4584.  For example, say you use GET_FIRST_XB.  On return, AP.keyPtr has the the very 
  4585.  first key.  Say elsewhere in your multi-threaded program, another operation 
  4586.  accesses that same index file handle, and performs some other access, where 
  4587.  the last accessed key is no longer the same (i.e., not the first key).  Your 
  4588.  first thread is expecting that a GET_NEXT_XB would get the second key, 
  4589.  however, it very likely won't since the second thread has altered the last 
  4590.  accessed key for that file handle.  By using the atomic mode for key access, 
  4591.  your first thread, which has the first key value in its AP.keyPtr, can do a 
  4592.  call to GET_NEXT_XB and get expected results, since the NEXT operation first 
  4593.  positions to the value in AP.keyPtr and then follows up with a GET_NEXT 
  4594.  operation.  This is performed within the Bullet kernel, and so won't be 
  4595.  interrupted by another thread (i.e., it is an atomic operation).  For this to 
  4596.  work, you must ensure that the AP.keyPtr value is set to the value of the last 
  4597.  accessed key.  This will always be the case unless uninitialized, or you are 
  4598.  using global variables for your threads' AP (AccessPack).  On return from the 
  4599.  operation, AP.keyPtr will once again be set up for another atomic operation. 
  4600.  
  4601.  Note:  You must supply a valid key value for this atomic access mode. 
  4602.  AP.keyPtr must be at least as large as the key length in all cases, and is to 
  4603.  have the starting point for the operation (i.e., the last accessed key).  You 
  4604.  may, alternatively, set the first byte of the key buffer to 0 (but not 
  4605.  AP.keyPtr itself to NULL).  This disables atomic mode for that access, and 
  4606.  reverts to the internally-stored last key accessed as the starting point. 
  4607.  
  4608.  
  4609. ΓòÉΓòÉΓòÉ 9.49. LAST_KEY_XB ΓòÉΓòÉΓòÉ
  4610.  
  4611. Pack: ACCESSPACK               Source Example 
  4612.  
  4613.      IN               OUT
  4614.    AP.func          AP.stat
  4615.    AP.handle        AP.recNo
  4616.    AP.keyPtr        *AP.keyPtr
  4617.  
  4618.  Retrieve the last key in index order from the index file. 
  4619.  
  4620.  This routine does not access the DBF file and so does not retrieve the data 
  4621.  record.  What it does do is locate the last key of the index, returning it, 
  4622.  and also returning the record number within the DBF that the key indexes. 
  4623.  
  4624.  To retrieve the data record you can use the GET_RECORD_XB routine.  The 
  4625.  preferred method, however, is to use GET_LAST_XB, which combines these 
  4626.  operations. 
  4627.  
  4628.  The key returned includes an enumerator if the index file allows duplicates. 
  4629.  
  4630.  This routine is typically used to position the index file to the last key so 
  4631.  as to allow reverse in-order access to the keys by using PREV_KEY_XB. 
  4632.  
  4633.  
  4634. ΓòÉΓòÉΓòÉ 9.50. STORE_KEY_XB ΓòÉΓòÉΓòÉ
  4635.  
  4636. Pack: ACCESSPACK               Source Example 
  4637.  
  4638.      IN               OUT
  4639.    AP.func          AP.stat
  4640.    AP.handle
  4641.    AP.recNo
  4642.    AP.keyPtr
  4643.  
  4644.  Insert the key into the index file in proper key order. 
  4645.  
  4646.  This routine does not add the data record to the DBF file.  It only inserts 
  4647.  the key and record number into the index file.  Use INSERT_XB instead. 
  4648.  
  4649.  To do a complete data record and key insert, use ADD_RECORD_XB to add the data 
  4650.  record to the DBF, BUILD_KEY_XB to construct the key, then STORE_KEY_XB to 
  4651.  insert the key and record number information into the index file.  If that key 
  4652.  already exists and the file allows duplicate keys, attach the proper 
  4653.  enumerator word and retry STORE_KEY_XB.  INSERT_XB does this automatically. 
  4654.  
  4655.  
  4656. ΓòÉΓòÉΓòÉ 9.51. DELETE_KEY_XB ΓòÉΓòÉΓòÉ
  4657.  
  4658. Pack: ACCESSPACK               Source Example 
  4659.  
  4660.      IN               OUT
  4661.    AP.func          AP.stat
  4662.    AP.handle
  4663.    AP.keyPtr
  4664.  
  4665.  Physically remove the specified key from the index file. 
  4666.  
  4667.  This routine requires an exact key match for all bytes of the key, including 
  4668.  the enumerator word if duplicate keys are allowed. 
  4669.  
  4670.  Typically, this routine would seldom be used since deleted DBF data records 
  4671.  are only physically deleted during a PACK_RECORDS_XB operation, after which a 
  4672.  REINDEX_XB is done. It is useful if you are managing a transaction log and 
  4673.  need to back out changes made, beyond what BULLET performs.  Also see 
  4674.  DEBUMP_RECORD_XB. If you have non-unique keys (where DUPS_ALLOWED is true), 
  4675.  you may have several keys that match your criterion, and only differ in their 
  4676.  enumerator.  To identify which key, then, goes to a particular DBF record, 
  4677.  compare that key's AP.recNo with the number of your DBF record.  If they are 
  4678.  the same, then this key belongs to that record.  Use either the KEY_XB or the 
  4679.  GET_XB routines, then, before using this routine.  In other words, use this 
  4680.  routine only after you have identified exactly the key to delete, and for the 
  4681.  exact data record.  Once you have the record number, you can locate its key by 
  4682.  using GET_KEY_FOR_RECORD_XB. 
  4683.  
  4684.  
  4685. ΓòÉΓòÉΓòÉ 9.52. BUILD_KEY_XB ΓòÉΓòÉΓòÉ
  4686.  
  4687. Pack: ACCESSPACK               Source Example 
  4688.  
  4689.      IN               OUT
  4690.    AP.func          AP.stat
  4691.    AP.handle        *AP.keyPtr
  4692.    AP.recPtr
  4693.    AP.keyPtr
  4694.  
  4695.  Build the key for the specified data record based on the key expression for 
  4696.  the index file.  If the index file allows duplicate keys, a 0-value enumerator 
  4697.  is attached. 
  4698.  
  4699.  This routine, like most of the mid-level routines, typically would not be used 
  4700.  since the high-level access routines take care of this detail automatically. 
  4701.  If used, it normally would be used prior to STORE_KEY_XB. 
  4702.  
  4703.  This routine can be replaced.  See Custom Build-Key Routine. 
  4704.  
  4705.  Note:  If DUPS_ALLOWED, this routine always sets the enumerator to \0\0. 
  4706.  Enumerator management, which is used to guarantee a unique key, is performed 
  4707.  only when the INSERT_XB routine is used. 
  4708.  
  4709.  
  4710. ΓòÉΓòÉΓòÉ 9.53. GET_CURRENT_KEY_XB ΓòÉΓòÉΓòÉ
  4711.  
  4712. Pack: ACCESSPACK               Source Example 
  4713.  
  4714.      IN               OUT
  4715.    AP.func          AP.stat
  4716.    AP.handle        AP.recNo
  4717.    AP.keyPtr        *AP.keyPtr
  4718.  
  4719.  Retrieve the current key value for the specified index file handle and also 
  4720.  the data record number that it indexes. The key value includes the enumerator 
  4721.  if applicable. 
  4722.  
  4723.  This routine is useful in that it retrieves, on demand, the actual key value 
  4724.  of the last accessed key in the index file (and the data record number 
  4725.  associated with that key). STAT_INDEX_XB returns this information, too. 
  4726.  
  4727.  
  4728. ΓòÉΓòÉΓòÉ 9.54. GET_KEY_FOR_RECORD_XB ΓòÉΓòÉΓòÉ
  4729.  
  4730. Pack: ACCESSPACK               Source Example 
  4731.  
  4732.      IN               OUT
  4733.    AP.func          AP.stat
  4734.    AP.handle        *AP.keyPtr
  4735.    AP.recNo
  4736.    AP.recPtr
  4737.    AP.keyPtr
  4738.  
  4739.  Retrieve the key for the record/record number pair. 
  4740.  
  4741.  This routine would typically be used prior to using DELETE_KEY_XB and 
  4742.  DEBUMP_RECORD_XB.  The key returned includes the enumerator if applicable. 
  4743.  This routine sifts through any duplicate keys (if DUPS_ALLOWED) for the key 
  4744.  that matches the record/record number pair, and so requires both the actual 
  4745.  data record along with its physical record number (even if dups are not 
  4746.  allowed). 
  4747.  
  4748.  Typically this routine is extraneous; the key is available with a GET_XB 
  4749.  routine and so can be deleted from the information provided through normal 
  4750.  access. 
  4751.  
  4752.  This routine builds a key based on the supplied record at AP.recPtr and 
  4753.  searches the index for that key proper.  If found, and if DUPS_ALLOWED, each 
  4754.  key matching the key proper has its record number compared to the record 
  4755.  number in AP.recNo.  If that matches, too, then that is the exact key being 
  4756.  sought. 
  4757.  
  4758.  
  4759. ΓòÉΓòÉΓòÉ 9.55. GET_FIRST_XB ΓòÉΓòÉΓòÉ
  4760.  
  4761. Pack: ACCESSPACK               Source Example 
  4762.  
  4763.      IN               OUT
  4764.    AP.func          AP.stat
  4765.    AP.handle        AP.recNo
  4766.    AP.recPtr        *AP.recPtr
  4767.    AP.keyPtr        *AP.keyPtr
  4768.  
  4769.  Retrieve the first indexed key and its data record. 
  4770.  
  4771.  The key returned includes an enumerator if the index file uses non-unique keys 
  4772.  (DUPS_ALLOWED). 
  4773.  
  4774.  This routine is typically used to process a database in index order starting 
  4775.  at the first ordered key (and its data record).  After processing this first 
  4776.  entry, subsequent in-order access of the database is achieved by using 
  4777.  GET_NEXT_XB, until the end of the database is reached, at which point an error 
  4778.  is returned. 
  4779.  
  4780.  This routine, like all the high-level GET_XB routines, fills in the AP.recNo 
  4781.  of the record accessed.  In this case, it fills AP.recNo with the record 
  4782.  number pointed to by the first key. Since this is done upon each GET_XB 
  4783.  access, the AP pack is primed for an UPDATE_XB 
  4784.  
  4785.  
  4786. ΓòÉΓòÉΓòÉ 9.56. GET_EQUAL_XB ΓòÉΓòÉΓòÉ
  4787.  
  4788. Pack: ACCESSPACK               Source Example 
  4789.  
  4790.      IN               OUT
  4791.    AP.func          AP.stat
  4792.    AP.handle        AP.recNo
  4793.    AP.recPtr        *AP.recPtr
  4794.    AP.keyPtr
  4795.  
  4796.  Search for the exact key in the index file and return its data record. 
  4797.  
  4798.  This routine finds only an exact match to the specified key (including the 
  4799.  enumerator if applicable). 
  4800.  
  4801.  This routine, like all the high-level GET_XB routines, fills in the AP.recNo 
  4802.  of the record accessed.  In this case, it fills AP.recNo with the record 
  4803.  number pointed to by the matching key. Since this is done upon each GET_XB 
  4804.  access, the AP pack is primed for an UPDATE_XB 
  4805.  
  4806.  
  4807. ΓòÉΓòÉΓòÉ 9.57. GET_EQUAL_OR_GREATER_XB ΓòÉΓòÉΓòÉ
  4808.  
  4809. Pack: ACCESSPACK               Source Example 
  4810.  
  4811.      IN               OUT
  4812.    AP.func          AP.stat
  4813.    AP.handle        AP.recNo
  4814.    AP.recPtr        *AP.recPtr
  4815.    AP.keyPtr        *AP.keyPtr
  4816.  
  4817.  Search for the exact key in the index file and return its data record, or if 
  4818.  not found, get the key and record that would have followed it. 
  4819.  
  4820.  This routine is similar to GET_EQUAL_XB except that this routine returns a key 
  4821.  in *keyPtr (either the same as on entry, or if that is not found, then the 
  4822.  next greater key). 
  4823.  
  4824.  The main benefit of this routine is that it is an atomic operation. It differs 
  4825.  from setting the atomic mode flag of SET_SYSVARS_XB in that this routine 
  4826.  allows a fuzzy starting point:  the key in AP.keyPtr, on entry (IN), need not 
  4827.  exist. 
  4828.  
  4829.  
  4830. ΓòÉΓòÉΓòÉ 9.58. GET_EQUAL_OR_LESSER_XB ΓòÉΓòÉΓòÉ
  4831.  
  4832. Pack: ACCESSPACK               Source Example 
  4833.  
  4834.      IN               OUT
  4835.    AP.func          AP.stat
  4836.    AP.handle        AP.recNo
  4837.    AP.recPtr        *AP.recPtr
  4838.    AP.keyPtr        *AP.keyPtr
  4839.  
  4840.  Search for the exact key in the index file and return its data record, or if 
  4841.  not found, get the key and record that would have come before it. 
  4842.  
  4843.  This routine is similar to GET_EQUAL_XB except that this routine returns a key 
  4844.  in *keyPtr (either the same as on entry, or if that is not found, then the 
  4845.  previous, lesser key). 
  4846.  
  4847.  The main benefit of this routine is that it is an atomic operation. It differs 
  4848.  from setting the atomic mode flag of SET_SYSVARS_XB in that this routine 
  4849.  allows a fuzzy starting point:  the key in AP.keyPtr, on entry (IN), need not 
  4850.  exist. 
  4851.  
  4852.  
  4853. ΓòÉΓòÉΓòÉ 9.59. GET_NEXT_XB ΓòÉΓòÉΓòÉ
  4854.  
  4855. Pack: ACCESSPACK               Source Example 
  4856.  
  4857.      IN               OUT
  4858.    AP.func          AP.stat
  4859.    AP.handle        AP.recNo
  4860.    AP.recPtr        *AP.recPtr
  4861.    AP.keyPtr        *AP.keyPtr
  4862.  
  4863.  Retrieve the next indexed key and its data record. 
  4864.  
  4865.  The key returned includes an enumerator if the index file uses non-unique keys 
  4866.  (DUPS_ALLOWED). 
  4867.  
  4868.  This routine is typically called after the index file has first been 
  4869.  positioned to a known key using either GET_FIRST_XB or GET_EQUAL_XB, or after 
  4870.  a previous GET_NEXT_XB or even GET_PREV_XB.  What it basically does is get the 
  4871.  key and data record following the current key, and then makes that key the new 
  4872.  current key. 
  4873.  
  4874.  This routine, like all the high-level GET_XB routines, fills in the AP.recNo 
  4875.  of the record accessed.  In this case, it fills AP.recNo with the record 
  4876.  number pointed to by the next key (now the current key). Since this is done 
  4877.  upon each GET_XB access, the AP pack is primed for an UPDATE_XB. ________________________________________________
  4878.  
  4879.  If bit0 of the atomic mode flag of SET_SYSVARS_XB is set to 1, key access is 
  4880.  based on a given starting point.  This simplifies index access in 
  4881.  multi-threaded code, where another thread may have altered the last key 
  4882.  accessed in the index file.  This mode lets you set a starting point for the 
  4883.  operation by supplying in AP.keyPtr the key value to start at. 
  4884.  
  4885.  For example, say you use GET_FIRST_XB.  On return, AP.keyPtr has the the very 
  4886.  first key.  Say elsewhere in your multi-threaded program, another operation 
  4887.  accesses that same index file handle, and performs some other access, where 
  4888.  the last accessed key is no longer the same (i.e., not the first key).  Your 
  4889.  first thread is expecting that a GET_NEXT_XB would get the second key, 
  4890.  however, it very likely won't since the second thread has altered the last 
  4891.  accessed key for that file handle.  By using the atomic mode for key access, 
  4892.  your first thread, which has the first key value in its AP.keyPtr, can do a 
  4893.  call to GET_NEXT_XB and get expected results, since the NEXT operation first 
  4894.  positions to the value in AP.keyPtr and then follows up with a GET_NEXT 
  4895.  operation.  This is performed within the Bullet kernel, and so won't be 
  4896.  interrupted by another thread (i.e., it is an atomic operation).  For this to 
  4897.  work, you must ensure that the AP.keyPtr value is set to the value of the last 
  4898.  accessed key.  This will always be the case unless uninitialized, or you are 
  4899.  using global variables for your threads' AP (AccessPack).  On return from the 
  4900.  operation, AP.keyPtr will once again be set up for another atomic operation. 
  4901.  
  4902.  Note:  You must supply a valid key value for this atomic access mode. 
  4903.  AP.keyPtr must be at least as large as the key length in all cases, and is to 
  4904.  have the starting point for the operation (i.e., the last accessed key).  You 
  4905.  may, alternatively, set the first byte of the key buffer to 0 (but not 
  4906.  AP.keyPtr itself to NULL).  This disables atomic mode for that access, and 
  4907.  reverts to the internally-stored last key accessed as the starting point. 
  4908.  
  4909.  
  4910. ΓòÉΓòÉΓòÉ 9.60. GET_PREV_XB ΓòÉΓòÉΓòÉ
  4911.  
  4912. Pack: ACCESSPACK               Source Example 
  4913.  
  4914.      IN               OUT
  4915.    AP.func          AP.stat
  4916.    AP.handle        AP.recNo
  4917.    AP.recPtr        *AP.recPtr
  4918.    AP.keyPtr        *AP.keyPtr
  4919.  
  4920.  Retrieve the previous indexed key and its data record. 
  4921.  
  4922.  The key returned includes an enumerator if the index file uses non-unique keys 
  4923.  (DUPS_ALLOWED). 
  4924.  
  4925.  This routine is typically called after the index file has first been 
  4926.  positioned to a known key using either GET_LAST_XB or GET_EQUAL_XB, or after a 
  4927.  previous GET_PREV_XB or even GET_NEXT_XB.  What it basically does is get the 
  4928.  key and data record preceding the current key, and then makes that key the new 
  4929.  current key. 
  4930.  
  4931.  This routine, like all the high-level GET_XB routines, fills in the AP.recNo 
  4932.  of the record accessed.  In this case, it fills AP.recNo with the record 
  4933.  number pointed to by the previous key (now the current key). Since this is 
  4934.  done upon each GET_XB access, the AP pack is primed for an UPDATE_XB. ________________________________________________
  4935.  
  4936.  If bit0 of the atomic mode flag of SET_SYSVARS_XB is set to 1, key access is 
  4937.  based on a given starting point.  This simplifies index access in 
  4938.  multi-threaded code, where another thread may have altered the last key 
  4939.  accessed in the index file.  This mode lets you set a starting point for the 
  4940.  operation by supplying in AP.keyPtr the key value to start at. 
  4941.  
  4942.  For example, say you use GET_FIRST_XB.  On return, AP.keyPtr has the the very 
  4943.  first key.  Say elsewhere in your multi-threaded program, another operation 
  4944.  accesses that same index file handle, and performs some other access, where 
  4945.  the last accessed key is no longer the same (i.e., not the first key).  Your 
  4946.  first thread is expecting that a GET_NEXT_XB would get the second key, 
  4947.  however, it very likely won't since the second thread has altered the last 
  4948.  accessed key for that file handle.  By using the atomic mode for key access, 
  4949.  your first thread, which has the first key value in its AP.keyPtr, can do a 
  4950.  call to GET_NEXT_XB and get expected results, since the NEXT operation first 
  4951.  positions to the value in AP.keyPtr and then follows up with a GET_NEXT 
  4952.  operation.  This is performed within the Bullet kernel, and so won't be 
  4953.  interrupted by another thread (i.e., it is an atomic operation).  For this to 
  4954.  work, you must ensure that the AP.keyPtr value is set to the value of the last 
  4955.  accessed key.  This will always be the case unless uninitialized, or you are 
  4956.  using global variables for your threads' AP (AccessPack).  On return from the 
  4957.  operation, AP.keyPtr will once again be set up for another atomic operation. 
  4958.  
  4959.  Note:  You must supply a valid key value for this atomic access mode. 
  4960.  AP.keyPtr must be at least as large as the key length in all cases, and is to 
  4961.  have the starting point for the operation (i.e., the last accessed key).  You 
  4962.  may, alternatively, set the first byte of the key buffer to 0 (but not 
  4963.  AP.keyPtr itself to NULL).  This disables atomic mode for that access, and 
  4964.  reverts to the internally-stored last key accessed as the starting point. 
  4965.  
  4966.  
  4967. ΓòÉΓòÉΓòÉ 9.61. GET_LAST_XB ΓòÉΓòÉΓòÉ
  4968.  
  4969. Pack: ACCESSPACK               Source Example 
  4970.  
  4971.      IN               OUT
  4972.    AP.func          AP.stat
  4973.    AP.handle        AP.recNo
  4974.    AP.recPtr        *AP.recPtr
  4975.    AP.keyPtr        *AP.keyPtr
  4976.  
  4977.  Retrieve the last indexed key and its data record. 
  4978.  
  4979.  The key returned includes an enumerator if the index file uses non-unique keys 
  4980.  (DUPS_ALLOWED). 
  4981.  
  4982.  This routine is typically used to process a database in reverse index order 
  4983.  starting at the last ordered key (and its data record).  After processing this 
  4984.  last entry, subsequent reverse-order access of the database is achieved by 
  4985.  using GET_PREV_XB, until the top of the database is reached, at which point an 
  4986.  error is returned. 
  4987.  
  4988.  This routine, like all the high-level GET_XB routines, fills in the AP.recNo 
  4989.  of the record accessed.  In this case, it fills AP.recNo with the record 
  4990.  number pointed to by the last key. Since this is done upon each GET_XB access, 
  4991.  the AP pack is primed for an UPDATE_XB 
  4992.  
  4993.  
  4994. ΓòÉΓòÉΓòÉ 9.62. INSERT_XB ΓòÉΓòÉΓòÉ
  4995.  
  4996. Pack: ACCESSPACK               Source Example 
  4997.  
  4998.      IN               OUT
  4999.    AP.func          AP.stat
  5000.    AP.handle        AP.recNo
  5001.    AP.recNo         *AP.keyPtr
  5002.    AP.recPtr
  5003.    AP.keyPtr
  5004.    AP.nextPtr
  5005.  
  5006.  Append the data records to data files and build and insert the related keys 
  5007.  into all linked index files.  (Alternate forms are possible.) 
  5008.  
  5009.  This routine is used to add new entries into a database.  Up to 256 index 
  5010.  files may be inserted into per call, with up to 256 data files being added, 
  5011.  too, for a total of 512 files managed per single INSERT_XB call. 
  5012.  
  5013.  Note:  Bullet comes in 100, 250, and 1024-file versions and so this routine is 
  5014.  able to use as many files as handles are still available. If non-standard 
  5015.  fields are used (i.e., non-char structure members to match non-ASCII data 
  5016.  fields in your non-standard DBF), then be aware that your compiler more than 
  5017.  likely will add padding to align on member-size boundaries.  This will result 
  5018.  in a mis-match between your compiler structure and your DBF structure (as 
  5019.  described in fieldList[]).  To prevent this, place #pragma pack(1) / #pragma 
  5020.  pack() around your structures that BULLET uses.  Consult your particular 
  5021.  compiler for alternate methods if it does not support #pragma pack. 
  5022.  
  5023.  Only index handles are listed in AP.handle.  Each index file has associated 
  5024.  with it a data file, known internally to BULLET (the xbLink from OPEN_XB). 
  5025.  There may be more than one index file for a data file, but there is always one 
  5026.  data file per index handle specified in the list.  In other words, you can 
  5027.  list five index files, each indexing the same xbLink data file, and have 
  5028.  BULLET perform an atomic insert of that list.  Or, another possibility is that 
  5029.  you have a single index file, indexing a single data file.  Or, you can list 
  5030.  256 index files, each indexing a single data file (512 total files). 
  5031.  
  5032.  This and several other routines are transaction-list-based.  This means that 
  5033.  if a failure occurs prior to the routine's completion, all changes made to the 
  5034.  database by the routine will be backed-out, and the database (data and index 
  5035.  files) effectively restored to its original state. 
  5036.  
  5037.  If the routine failed to complete, the function return value is the number 
  5038.  (1-based) of the pack that caused the failure.  A positive number indicates 
  5039.  the failure was from an index operation; a negative number indicates the 
  5040.  failure was from a data operation.  In each case, the absolute value of the 
  5041.  return code is the list item that failed (the pack index).  For example, if 
  5042.  five index handles are in the list (AP[0] to AP[4]), and an error occurred on 
  5043.  the last pack's index file, the return code would be positive 5, indicating 
  5044.  the fifth pack (AP[4]) failed.  Since it was a positive 5, the index file was 
  5045.  being processed when the error occurred.  Being processed means not only 
  5046.  physical access, but verification, etc.  If the return code was -5, then 
  5047.  again, the error was in the fifth pack, but since it is negative, the error 
  5048.  occurred while processing the data file.  In either case, upon return, the 
  5049.  database is effectively restored to the way it was before the INSERT_XB call 
  5050.  was made.  Remedy the error, if possible, and INSERT_XB again. 
  5051.  
  5052.  Each pack must include a separate key buffer.  You must not share a common key 
  5053.  buffer.  Doing so disables any chance of recovering the index files in case of 
  5054.  error, since it is in these buffers that BULLET places the newly built keys, 
  5055.  and it is from these that BULLET, upon an error condition, deletes the keys 
  5056.  (required for roll-back). 
  5057.  
  5058.  The enumerator is automatically set up by this routine, if required 
  5059.  (DUPS_ALLOWED and the key already exists with enumerator 0).  It does this by 
  5060.  seeking the last possible enumerator value (0xFFFF) and then backing up to the 
  5061.  previous key.  That key's enumerator is evaluated and incremented, and used as 
  5062.  this key's. 
  5063.  
  5064.  Specifying Files 
  5065.  
  5066.  As mentioned, only the index file handles are specified in AP.handle. Data 
  5067.  files are implicitly specified by their links to the index files, as specified 
  5068.  when the index file was opened (OP.xbLink).  INSERT_XB can process up to 256 
  5069.  index files per call.  Since each index file requires a data file, this means 
  5070.  that up to 256 data files can be processed per call, as well.  Also possible 
  5071.  is that all 256 index handles refer to the same, single data file.  Yet 
  5072.  another possibility is that there is 1 index file, and so 1 data file.  The 
  5073.  possibilities can include those and anything in between. 
  5074.  
  5075.  Example: Specifying a single index file 
  5076.  
  5077.  The simplest form is where a single index handle is specified.  This implies a 
  5078.  single data file, too.  AccessPack setup for this is: 
  5079.  
  5080.   AP.func = INSERT_XB;
  5081.   AP.handle = indexHandle;
  5082.   AP.recNo = 0;
  5083.   AP.recPtr = &recordStruct; // contents referred to below as *recordStruct
  5084.   AP.keyPtr = keyBuffer;
  5085.   AP.nextPtr = NULL;
  5086.  
  5087.  A call to BULLET with the above does the following: 
  5088.  
  5089.    1. The data in *recordStruct is used as a new record that is appended to the 
  5090.       data file.  The data file was linked to this index during the index open, 
  5091.       in OP.xbLink. 
  5092.  
  5093.    2. A key is built by BULLET, based on the data in *recordStruct, and that 
  5094.       key is inserted into the index file (AP.handle).  Stored with the key is 
  5095.       the record number of the record added above. 
  5096.  
  5097.  Note:  AP.recNo must be set to 0 prior to the call.  Any positive number 
  5098.  results in an error (0x80000000, and negative numbers, may be used when more 
  5099.  than one AP pack is used - see below). 
  5100.  
  5101.  Upon return, if no error, the return code is 0.  AP.recNo is set to the 
  5102.  physical record number in the data file that *recordStruct was placed.  The 
  5103.  key that was stored, including any enumerator, is in *keyBuffer. 
  5104.  
  5105.  Upon return, and there was an error, the return code is either -1 or 1. If -1, 
  5106.  the error was caused during processing of the data file portion, and the error 
  5107.  code itself is in AP.stat.  If +1, the error was caused during processing of 
  5108.  the index file, and the error code itself is in AP.stat, as well.  The return 
  5109.  code is, as in all BULLET transaction-list routines, an index of the AP pack 
  5110.  that generated the error -- negative if a data file error, positive if an 
  5111.  index file error.  Since this example has only the single pack, only a -1 or 
  5112.  +1 could be returned, or 0. 
  5113.  
  5114.  Note:  If an error occurred after any part of the database had changed (during 
  5115.  this particular call), then any and all changes that were made are backed-out, 
  5116.  and the files restored to the same state as before the call. 
  5117.  
  5118.  Example: Specifying two index files for a single data file 
  5119.  
  5120.  Two index files, related to the same data file, would set AccessPack to: 
  5121.  
  5122.   AP[0].func = INSERT_XB;
  5123.   AP[0].handle = indexHandle_0;
  5124.   AP[0].recNo = 0;
  5125.   AP[0].recPtr = &recordStruct;
  5126.   AP[0].keyPtr = keyBuffer_0;
  5127.   AP[0].nextPtr = AP[1];
  5128.  
  5129.   AP[1].handle = indexHandle_1;
  5130.   AP[1].recNo = 0x80000000;
  5131.   AP[1].recPtr = &recordStruct;
  5132.   AP[1].keyPtr = keyBuffer_1;
  5133.   AP[1].nextPtr = NULL;
  5134.  
  5135.  A call to BULLET with the above does the following: 
  5136.  
  5137.    1. The data in *recordStruct is used as a new record that is appended 
  5138.       (added) to the data file. 
  5139.  
  5140.    2. A key is built by BULLET, based on the data in *recordStruct, and that 
  5141.       key is inserted into the index file (AP[0].handle).  Stored with the key 
  5142.       is the record number of the record added above. 
  5143.  
  5144.    3. A second key is built by BULLET, based on the data in *recordStruct, and 
  5145.       that key is inserted into the second index file (AP[1].handle).  Stored 
  5146.       with the key is the record number of the record added above. 
  5147.  
  5148.  Note:  The 0x80000000 in AP[1].recNo signifies that AP[1] is using the same 
  5149.  data record that was appended during processing of AP[0].  This results in 
  5150.  just the one data record being added.  AP[1].recPtr must still, however, point 
  5151.  to the same data as AP[0].recPtr does. 
  5152.  
  5153.  Upon return, if no error, the return code is 0.  AP[0].recNo is set to the 
  5154.  physical record number in the data file that *recordStruct was placed.  The 
  5155.  key that was stored for the first index, including any enumerator, is in the 
  5156.  buffer at AP[0].keyPtr.  AP[1].recNo is set to the same physical record number 
  5157.  as AP[0].recNo, except that the record number is negative: For example, if 
  5158.  AP[0].recNo is 22 on return, AP[1].recNo is -22 (the original 0x80000000 value 
  5159.  is overwritten).  The key that was stored for the second index, including any 
  5160.  enumerator, is in the buffer at AP[1].keyPtr. 
  5161.  
  5162.  Upon return, and there was an error, the return code can be -2, -1, 1, or 2. 
  5163.  If negative, the error was caused during processing of that AP pack's data 
  5164.  file portion, and the error code itself is in AP[abs(rez)-1].stat (where rez 
  5165.  is the return code, and -1 since C arrays start at 0).  If the return code was 
  5166.  positive, the error was caused during processing of that AP pack's index file, 
  5167.  and the error code itself is in AP[rez-1].stat, as well.  The return code is, 
  5168.  as in all BULLET transaction-list routines, an index of the AP pack that 
  5169.  generated the error -- negative if a data file error, positive if an index 
  5170.  file error. 
  5171.  
  5172.  Note:  If an error occurred after any part of the database had changed (during 
  5173.  this particular call), then any and all changes that were made are backed-out, 
  5174.  and the files restored to the same state as before the call. 
  5175.  
  5176.  Example: Specifying two index files for each of two different data files 
  5177.  
  5178.  Four total files: two index files related to one data file, and two other 
  5179.  index files related to another data file, would set AccessPack to: 
  5180.  
  5181.   AP[0].func = INSERT_XB;
  5182.   AP[0].handle = indexHandle_0;
  5183.   AP[0].recNo = 0;
  5184.   AP[0].recPtr = &recordStruct_0;
  5185.   AP[0].keyPtr = keyBuffer_0;
  5186.   AP[0].nextPtr = AP[1];
  5187.  
  5188.   AP[1].handle = indexHandle_1;
  5189.   AP[1].recNo = 0x80000000;
  5190.   AP[1].recPtr = &recordStruct_0;
  5191.   AP[1].keyPtr = keyBuffer_1;
  5192.   AP[1].nextPtr = AP[2];
  5193.  
  5194.   AP[2].handle = indexHandle_2;
  5195.   AP[2].recNo = 0;
  5196.   AP[2].recPtr = &recordStruct_1;
  5197.   AP[2].keyPtr = keyBuffer_2;
  5198.   AP[2].nextPtr = AP[3];
  5199.  
  5200.   AP[3].handle = indexHandle_3;
  5201.   AP[3].recNo = 0x80000000;
  5202.   AP[3].recPtr = &recordStruct_1;
  5203.   AP[3].keyPtr = keyBuffer_3;
  5204.   AP[3].nextPtr = NULL;
  5205.  
  5206.  A call to BULLET with the above does the following: 
  5207.  
  5208.    1. The data in *recordStruct_0 is used as a new record that is appended to 
  5209.       the data file linked to the index file in AP[0].handle. 
  5210.  
  5211.    2. A key is built by BULLET, based on the data in *recordStruct_0, and that 
  5212.       key is inserted into the index file (AP[0].handle).  Stored with the key 
  5213.       is the record number of the record added above, for _0. 
  5214.  
  5215.    3. A second key is built by BULLET, based on the data in *recordStruct_0, 
  5216.       and that key is inserted into the second index file (AP[1].handle). 
  5217.       Stored with the key is the record number of the record added above, using 
  5218.       *recordStruct_0. 
  5219.  
  5220.    4. The data in *recordStruct_1 is used as a new record that is appended to 
  5221.       the data file linked to the index file in AP[2].handle. 
  5222.  
  5223.    5. A third key is built by BULLET, based on the data in *recordStruct_1, and 
  5224.       that key is inserted into the index file (AP[2].handle).  Stored with the 
  5225.       key is the record number of the record added above, for _1. 
  5226.  
  5227.    6. A fourth key is built by BULLET, based on the data in *recordStruct_1, 
  5228.       and that key is inserted into the fourth index file (AP[3].handle). 
  5229.       Stored with the key is the record number of the record added above, using 
  5230.       *recordStruct_1. 
  5231.  
  5232.  Note:  The 0x80000000 in AP[1].recNo signifies that AP[1] is using the same 
  5233.  data record that was appended during processing of AP[0].  This results in 
  5234.  just the one data record being added.  AP[1].recPtr must still, however, point 
  5235.  to the same data as AP[0].recPtr does.  The same applies to AP[2] and AP[3] 
  5236.  (though different values, of course). 
  5237.  
  5238.  Upon return, if no error, the return code is 0.  AP[0].recNo is set to the 
  5239.  physical record number in the data file that *recordStruct_0 was placed. The 
  5240.  key that was stored for the first index, including any enumerator, is in the 
  5241.  buffer at AP[0].keyPtr.  AP[1].recNo is set to the same physical record number 
  5242.  as AP[0].recNo, except that the record number is negative: For example, if 
  5243.  AP[0].recNo is 22 on return, AP[1].recNo is -22 (the original 0x80000000 value 
  5244.  is overwritten).  The key that was stored for the second index, including any 
  5245.  enumerator, is in the buffer at AP[1].keyPtr.  AP[2].recNo is set to the 
  5246.  physical record number in the data file that *recordStruct_1 was placed.  The 
  5247.  key that was stored for the third index, including any enumerator, is in the 
  5248.  buffer at AP[2].keyPtr.  AP[3].recNo is set to the same physical record number 
  5249.  as AP[2].recNo, except that the record number is negative: For example, if 
  5250.  AP[2].recNo is 74 on return, AP[3].recNo is -74 (the original 0x80000000 value 
  5251.  is overwritten).  The key that was stored for the fourth index, including any 
  5252.  enumerator, is in the buffer at AP[3].keyPtr. 
  5253.  
  5254.  Upon return, and there was an error, the return code can be -4 to -1, or 1 to 
  5255.  4.  If negative, the error was caused during processing of that AP pack's data 
  5256.  file portion, and the error code itself is in AP[abs(rez)-1].stat (where rez 
  5257.  is the return code, and -1 since C arrays start at 0).  If the return code was 
  5258.  positive, the error was caused during processing of that AP pack's index file, 
  5259.  and the error code itself is in AP[rez-1].stat, as well.  The return code is, 
  5260.  as in all BULLET transaction-list routines, an index of the AP pack that 
  5261.  generated the error -- negative if a data file error, positive if an index 
  5262.  file error. 
  5263.  
  5264.  Note:  If an error occurred after any part of the database had changed (during 
  5265.  this particular call), then any and all changes that were made are backed-out, 
  5266.  and the files restored to the same state as before the call. 
  5267.  
  5268.  Example: Specifying two index files for two records in the same data file 
  5269.  
  5270.  Three files: two index files related to one data file, where two data records 
  5271.  are to be appended, would set AccessPack to: 
  5272.  
  5273.   AP[0].func = INSERT_XB;
  5274.   AP[0].handle = indexHandle_0;
  5275.   AP[0].recNo = 0;
  5276.   AP[0].recPtr = &recordStruct_0;
  5277.   AP[0].keyPtr = keyBuffer_0;
  5278.   AP[0].nextPtr = AP[1];
  5279.  
  5280.   AP[1].handle = indexHandle_1;
  5281.   AP[1].recNo = 0x80000000;
  5282.   AP[1].recPtr = &recordStruct_0;
  5283.   AP[1].keyPtr = keyBuffer_1;
  5284.   AP[1].nextPtr = AP[2];
  5285.  
  5286.   AP[2].handle = indexHandle_0;
  5287.   AP[2].recNo = 0;
  5288.   AP[2].recPtr = &recordStruct_1;
  5289.   AP[2].keyPtr = keyBuffer_2;
  5290.   AP[2].nextPtr = AP[3];
  5291.  
  5292.   AP[3].handle = indexHandle_1;
  5293.   AP[3].recNo = 0x80000000;
  5294.   AP[3].recPtr = &recordStruct_1;
  5295.   AP[3].keyPtr = keyBuffer_3;
  5296.   AP[3].nextPtr = NULL;
  5297.  
  5298.  A call to BULLET with the above does the following: 
  5299.  
  5300.    1. The data in *recordStruct_0 is used as a new record that is appended to 
  5301.       the data file linked to the index file in AP[0].handle. 
  5302.  
  5303.    2. A key is built by BULLET, based on the data in *recordStruct_0, and that 
  5304.       key is inserted into the index file (AP[0].handle).  Stored with the key 
  5305.       is the record number of the record added above, for _0. 
  5306.  
  5307.    3. A second key is built by BULLET, based on the data in *recordStruct_0, 
  5308.       and that key is inserted into the second index file (AP[1].handle). 
  5309.       Stored with the key is the record number of the record added above, using 
  5310.       *recordStruct_0. 
  5311.  
  5312.    4. The data in *recordStruct_1 is used as a new record that is appended to 
  5313.       the data file linked to the index file in AP[2].handle.  Since 
  5314.       AP[2].handle is the same index file as that of AP[0].handle, this means 
  5315.       it's also the same data file as was just operated on above -- a second 
  5316.       data record is appended to the data file.  The net effect of this 
  5317.       operation is to call INSERT_XB twice, once for one insert, then again for 
  5318.       the second.  The difference is that the operation is atomic -- if one 
  5319.       fails, the other is not committed; it's an "all or nothing" operation. 
  5320.  
  5321.    5. A third key is built by BULLET, based on the data in *recordStruct_1, and 
  5322.       that key is inserted into the index file (AP[2].handle).  Stored with the 
  5323.       key is the record number of the record added directly above, for _1. Note 
  5324.       that this index file is the same as specified in AP[0].handle. 
  5325.  
  5326.    6. A fourth key is built by BULLET, based on the data in *recordStruct_1, 
  5327.       and that key is inserted into the fourth index file (AP[3].handle). 
  5328.       Stored with the key is the record number of the record added above, using 
  5329.       *recordStruct_1. 
  5330.  The return ritual is as described above, for "Specifying two index files each 
  5331.  for two different data files". 
  5332.  
  5333.  Example: Specifying a single index file for a previously added data record 
  5334.  
  5335.  This form lets you insert a key without adding a data record.  This would be 
  5336.  required if you were, for example, creating a temporary index of select 
  5337.  records in a data file (i.e., the data records already exist, you just want to 
  5338.  index them).  AccessPack setup for this is: 
  5339.  
  5340.   AP.func = INSERT_XB;
  5341.   AP.handle = indexHandle;
  5342.   AP.recNo = -recordNumberOfExistingRecord;
  5343.   AP.recPtr = &recordStruct;
  5344.   AP.keyPtr = keyBuffer;
  5345.   AP.nextPtr = NULL;
  5346.  
  5347.  A call to BULLET with the above does the following: 
  5348.  
  5349.    1. A key is built by BULLET, based on the data in *recordStruct, and that 
  5350.       key is inserted into the index file (AP.handle).  Stored with the key is 
  5351.       the absolute value of the record number specified in AP.recNo (which is 
  5352.       set to negative record number). 
  5353.  
  5354.  Upon return, if no error, the return code is 0.  AP.recNo is changed to 
  5355.  abs(AP.recNo).  The key that was stored, including any enumerator, is in 
  5356.  *keyBuffer.  No data file access is made. 
  5357.  
  5358.  Upon return, and there was an error, the return code is either -1 or 1. If -1, 
  5359.  the error was caused during processing of the data file portion, and the error 
  5360.  code itself is in AP.stat.  If +1, the error was caused during processing of 
  5361.  the index file, and the error code itself is in AP.stat, as well.  The return 
  5362.  code is, as in all BULLET transaction-list routines, an index of the AP pack 
  5363.  that generated the error -- negative if a data file error, positive if an 
  5364.  index file error.  Since this example has only the single pack, only a -1 or 
  5365.  +1 could be returned. 
  5366.  
  5367.  Note:  If an error occurred after any part of the database had changed (during 
  5368.  this particular call), then any and all changes that were made are backed-out, 
  5369.  and the files restored to the same state as before the call. 
  5370.  
  5371.  An example use of this INSERT_XB feature is to create an ad hoc index of, say, 
  5372.  records marked as deleted.  To do this, create a new index file (say, with a 
  5373.  key of NAME).  Get each data record, by record number using GET_RECORD_XB (for 
  5374.  records 1 to number-of-records), and check the .tag byte.  If '*', call 
  5375.  INSERT_XB with the negative value of AP.recNo.  Do this for every such marked 
  5376.  record.  After all records are processed, you have an index of all deleted 
  5377.  records in the data file.  Delete the index when no longer needed.  That's 
  5378.  just one example. 
  5379.  
  5380.  
  5381. ΓòÉΓòÉΓòÉ 9.63. UPDATE_XB ΓòÉΓòÉΓòÉ
  5382.  
  5383. Pack: ACCESSPACK               Source Example 
  5384.  
  5385.      IN               OUT
  5386.    AP.func          AP.stat
  5387.    AP.handle        *AP.keyPtr
  5388.    AP.recNo
  5389.    AP.recPtr
  5390.    AP.keyPtr
  5391.    AP.nextPtr
  5392.  
  5393.  Update any and all files in the transaction list if necessary, including both 
  5394.  index and data files. 
  5395.  
  5396.  This routine is used to update data records while also updating the index 
  5397.  files if a key field has changed due to data record updates.  Up to 256 index 
  5398.  files may be updated per call, as well as 256 data files, too, for a total of 
  5399.  512 files managed per single UPDATE_XB call. 
  5400.  
  5401.  Only index handles are listed in AP.handle.  Each index file has associated 
  5402.  with it a data file, known internally to BULLET (the xbLink from OPEN_XB). 
  5403.  There may be more than one index file for a data file, but there is always one 
  5404.  data file per index handle specified in the list.  In other words, you can 
  5405.  list five index files, each indexing the same xbLink data file, and have 
  5406.  BULLET perform an atomic update of that list. Or, another possibility is that 
  5407.  you have a single index file, indexing a single data file.  Or, you can list 
  5408.  256 index files, each indexing a single data file (512 total files). 
  5409.  
  5410.  This and several other routines are transaction-list-based.  This means that 
  5411.  if a failure occurs prior to the routine's completion, all changes made to the 
  5412.  database by the routine will be backed-out, and the database (data and index 
  5413.  files) effectively restored to its original state. 
  5414.  
  5415.  If the routine failed to complete, the function return value is the number 
  5416.  (1-based) of the pack that caused the failure.  A positive number indicates 
  5417.  the failure was from an index operation; a negative number indicates the 
  5418.  failure was from a data operation.  In each case, the absolute value of the 
  5419.  return code is the list item that failed (the pack index).  For example, if 
  5420.  five index handles are in the list(AP[0] to AP[4]), and an error occurred on 
  5421.  the last pack's index file, the return code would be positive 5, indicating 
  5422.  the fifth pack (AP[4]) failed.  Since it was a positive 5, the index file was 
  5423.  being processed when the error occurred.  Being processed means not only 
  5424.  physical access, but verification, etc.  If the return code was -5, then 
  5425.  again, the error was in the fifth pack, but since it is negative, the error 
  5426.  occurred while processing the data file.  In either case, upon return, the 
  5427.  database is restored to the way it was before the UPDATE_XB call was made. 
  5428.  Remedy the error, if possible, and UPDATE_XB again. 
  5429.  
  5430.  Each pack must include a separate key buffer.  You must not share a common key 
  5431.  buffer.  Doing so disables any chance of recovering the index files in case of 
  5432.  error, since it is in these buffers that BULLET places any newly built keys, 
  5433.  and it is from these that BULLET, upon an error condition, deletes these keys 
  5434.  (required for roll-back). 
  5435.  
  5436.  The enumerator is automatically maintained by this routine, if required 
  5437.  (DUPS_ALLOWED and the key already exists with enumerator 0).  The process is 
  5438.  the same as INSERT_XB's. 
  5439.  
  5440.  How an update works 
  5441.  
  5442.  All data records specified in the list are read from disk into memory, except 
  5443.  those with AP.recNo=0.  Therefore, a memory allocation large enough to store 
  5444.  all unique data records is made upon entry to this routine (and released at 
  5445.  exit).  For example, if the list includes two implicit data files, and the 
  5446.  record lengths of those two data files are 2048 and 4096 bytes, an allocation 
  5447.  of 6K is made.  In addition, 40KB more is allocated for workspace.  So, for 
  5448.  this example, 46K is allocated (rounded up to 48KB, the next 4KB page 
  5449.  boundary).  Since up to 256 unique records are possible, where a unique record 
  5450.  is identified by handle/record number, be aware of the memory requirements if 
  5451.  you are updating very large databases (e.g., 256 unique records, each 4KB in 
  5452.  length, would have UPDATE_XB allocate a bit over 1MB of memory for this call). 
  5453.  
  5454.  After the data records have been read from disk, each list-item is processed, 
  5455.  in order.  The disk record image previously read is compared with the record 
  5456.  image at AP.recPtr.  If the same, that item is skipped, and the next item in 
  5457.  the list is processed.  If you know beforehand that that record is the same, 
  5458.  set that item's AP.recNo=0 so you can avoid having its disk image read and 
  5459.  stored (or do not include it in the list at all).  If the images differ, 
  5460.  BULLET creates a key for the index file being processed, for each record image 
  5461.  (the original and the one in AP.recPtr).  If the keys generated are the same, 
  5462.  no index file update is needed.  If different, the original key for that 
  5463.  record is deleted from that index file, and the new key inserted. Finally, the 
  5464.  new record replaces the old, the new directly overwriting the original.  Note 
  5465.  that the actual sequence of the update event differs somewhat from this 
  5466.  description in order to optimize the process. 
  5467.  
  5468.  Specifying Files 
  5469.  
  5470.  As mentioned, only the index file handles are specified in AP.handle. Data 
  5471.  files are implicitly specified by their links to the index files, as specified 
  5472.  when the index file was opened (OP.xbLink).  UPDATE_XB can process up to 256 
  5473.  index files per call.  Since each index file requires a data file, this means 
  5474.  that up to 256 data files can be processed per call as well.  Also possible is 
  5475.  that all 256 index handles refer to the same, single data file.  Yet another 
  5476.  possibility is that there is 1 index file, and so 1 data file.  The 
  5477.  possibilities can include those and anything in between. 
  5478.  
  5479.  Example: Specifying a single index file 
  5480.  
  5481.  The simplest form is where a single index handle is specified.  This implies a 
  5482.  single data file, too.  AccessPack setup for this is: 
  5483.  
  5484.   AP.func = UPDATE_XB;
  5485.   AP.handle = indexHandle;
  5486.   AP.recNo = recordToUpdate;
  5487.   AP.recPtr = &recordStruct;
  5488.   AP.keyPtr = keyBuffer;
  5489.   AP.nextPtr = NULL;
  5490.  
  5491.  A call to BULLET with the above does the following: 
  5492.  
  5493.    1. The data in *recordStruct is used as the new record that is to replace 
  5494.       the data record at AP.recNo.  The data file was linked to this index file 
  5495.       (AP.handle) during the index open, in OP.xbLink. 
  5496.  
  5497.    2. If the record data in *recordStruct is the same as the original disk 
  5498.       record, nothing is done.  If the data is new, the key fields are compared 
  5499.       to that belonging to the original disk record, and if the same, only the 
  5500.       record data is updated.  If the new record's key differs from the 
  5501.       original's, the original key for this record is removed from the index, 
  5502.       and the new key inserted. 
  5503.  
  5504.  AP.recNo must be set to the record number that you are updating. Any GET_XB 
  5505.  routine (GET_EQUAL_XB, etc.) may be used to identify the number of a data 
  5506.  record.  Key access has the obvious advantage of knowing the record number of 
  5507.  a specific key (for example, Betty Barbar's data). Any record number, from 1 
  5508.  to number of records in the data file, can be used.  In addition, a negative 
  5509.  record number can be used.  This is treated exactly the same as a positive 
  5510.  record number (the absolute value is used). The reason this is allowed is 
  5511.  because INSERT_XB replaces 0x80000000 record numbers with the negative value 
  5512.  of the previous insert. 
  5513.  
  5514.  Upon return, if no error, the return code is 0.  If the record data was new, 
  5515.  the key for that data record, including any enumerator, is in *keyBuffer. 
  5516.  This is so even if key fields had not changed. 
  5517.  
  5518.  Upon return, and there was an error, the return code is either -1 or 1. If -1, 
  5519.  the error was caused during processing of the data file portion, and the error 
  5520.  code itself is in AP.stat.  If +1, the error was caused during processing of 
  5521.  the index file, and the error code itself is in AP.stat, as well.  The return 
  5522.  code is, as in all BULLET transaction-list routines, an index of the AP pack 
  5523.  that generated the error -- negative if a data file error, positive if an 
  5524.  index file error.  Since this example has only the single pack, only a -1 or 
  5525.  +1 could be returned. 
  5526.  
  5527.  Note:  If an error occurred after any part of the database had changed (during 
  5528.  this particular call), then any and all changes that were made are backed-out, 
  5529.  and the files restored to the same state as before the call. 
  5530.  
  5531.  Example: Specifying two index files for a single data file 
  5532.  
  5533.  Two index files, related to the same data file, would set AccessPack to: 
  5534.  
  5535.   AP[0].func = UPDATE_XB;
  5536.   AP[0].handle = indexHandle_0;
  5537.   AP[0].recNo = recordToUpdate;
  5538.   AP[0].recPtr = &recordStruct;
  5539.   AP[0].keyPtr = keyBuffer_0;
  5540.   AP[0].nextPtr = AP[1];
  5541.  
  5542.   AP[1].handle = indexHandle_1;
  5543.   AP[1].recNo = recordToUpdate;
  5544.   AP[1].recPtr = &recordStruct;
  5545.   AP[1].keyPtr = keyBuffer_1;
  5546.   AP[1].nextPtr = NULL;
  5547.  
  5548.  A call to BULLET with the above does the following: 
  5549.  
  5550.    1. The data in *recordStruct is used as the new record that is to replace 
  5551.       the data record at AP.recNo.  The data file was linked to this index file 
  5552.       (AP.handle) during the index open, in OP.xbLink. 
  5553.  
  5554.    2. If the record data in *recordStruct is the same as the original disk 
  5555.       record, nothing is done.  If the data is new, the key fields are compared 
  5556.       to that belonging to the original disk record, and if the same, only the 
  5557.       record data is updated.  If the new record's key differs from the 
  5558.       original's, the original key for this record is removed from the index, 
  5559.       and the new key inserted. 
  5560.  
  5561.    3. The operation performed directly above is repeated, this time for the 
  5562.       second index file.  The new record data, and the record number to update 
  5563.       are, for this particular example, the same. 
  5564.  
  5565.  AP.recNo must be set to the record number that you are updating. Each 
  5566.  AP[].recNo must be set to a valid record number, even if the record number is 
  5567.  the same as the previous AP[] pack's (the case where you have more than one 
  5568.  index file for a data file).  BULLET knows if the record number duplicates a 
  5569.  number in a previous AP pack, and allocates resources for only the first 
  5570.  encounter of the data record.  Subsequent encounters refer to the first. 
  5571.  
  5572.  Upon return, if no error, the return code is 0.  If the new and original data 
  5573.  records differ, the key for the new data record, including any enumerator, is 
  5574.  in the buffer at AP[0].keyPtr.  This even if the key fields did not change. 
  5575.  The same applies to the second index, with the new data key in AP[1].keyPtr. 
  5576.  
  5577.  Upon return, and there was an error, the return code can be -2, -1, 1, or 2. 
  5578.  If negative, the error was caused during processing of that AP pack's data 
  5579.  file portion, and the error code itself is in AP[abs(rez)-1].stat (where rez 
  5580.  is the return code, and -1 since C arrays start at 0).  If the return code was 
  5581.  positive, the error was caused during processing of that AP pack's index file, 
  5582.  and the error code itself is in AP[rez-1].stat, as well.  The return code is, 
  5583.  as in all BULLET transaction-list routines, an index of the AP pack that 
  5584.  generated the error -- negative if a data file error, positive if an index 
  5585.  file error. 
  5586.  
  5587.  Note:  If an error occurred after any part of the database had changed (during 
  5588.  this particular call), then any and all changes that were made are backed-out, 
  5589.  and the files restored to the same state as before the call. 
  5590.  
  5591.  Example: Specifying two index files for each of two different data files 
  5592.  
  5593.  Four total files: two index files related to one data file, and two other 
  5594.  index files related to another data file, would set AccessPack to: 
  5595.  
  5596.   AP[0].func = UPDATE_XB;
  5597.   AP[0].handle = indexHandle_0;
  5598.   AP[0].recNo = recordToUpdate_0;
  5599.   AP[0].recPtr = &recordStruct_0;
  5600.   AP[0].keyPtr = keyBuffer_0;
  5601.   AP[0].nextPtr = AP[1];
  5602.  
  5603.   AP[1].handle = indexHandle_1;
  5604.   AP[1].recNo = recordToUpdate_0;
  5605.   AP[1].recPtr = &recordStruct_0;
  5606.   AP[1].keyPtr = keyBuffer_1;
  5607.   AP[1].nextPtr = AP[2];
  5608.  
  5609.   AP[2].handle = indexHandle_2;
  5610.   AP[2].recNo = recordToUpdate_1;
  5611.   AP[2].recPtr = &recordStruct_1;
  5612.   AP[2].keyPtr = keyBuffer_2;
  5613.   AP[2].nextPtr = AP[3];
  5614.  
  5615.   AP[3].handle = indexHandle_3;
  5616.   AP[3].recNo = recordToUpdate_1;
  5617.   AP[3].recPtr = &recordStruct_1;
  5618.   AP[3].keyPtr = keyBuffer_3;
  5619.   AP[3].nextPtr = NULL;
  5620.  
  5621.  A call to BULLET with the above does the following: 
  5622.  
  5623.    1. The data in *recordStruct_0 is used as the new record that is to replace 
  5624.       the data record at AP[0].recNo in the data file linked to the index file 
  5625.       in AP[0].handle. 
  5626.  
  5627.    2. If the record data in *recordStruct_0 is the same as the original disk 
  5628.       record, nothing is done.  If the data is new, the key fields are compared 
  5629.       to that belonging to the original disk record, and if the same, only the 
  5630.       record data is updated.  If the new record's key differs from the 
  5631.       original's, the original key for this record is removed from the index, 
  5632.       and the new key inserted. 
  5633.  
  5634.    3. The operation performed directly above is repeated, this time for the 
  5635.       second index file.  The new record data, and the record number to update, 
  5636.       are for this particular example, the same. 
  5637.  
  5638.    4. The data in *recordStruct_1 is used as the new record that is to replace 
  5639.       the data record at AP[2].recNo in the data file linked to the index file 
  5640.       in AP[2].handle. 
  5641.  
  5642.    5. If the record data in *recordStruct_1 is the same as the original disk 
  5643.       record, nothing is done.  If the data is new, the key fields are compared 
  5644.       to that belonging to the original disk record, and if the same, only the 
  5645.       record data is updated.  If the new record's key differs from the 
  5646.       original's, the original key for this record is removed from the index, 
  5647.       and the new key inserted. 
  5648.  
  5649.    6. The operation performed directly above is repeated, this time for the 
  5650.       fourth index file.  The new record data, and the record number to update 
  5651.       are, for this particular example, the same. 
  5652.  
  5653.  Upon return, if no error, the return code is 0.  If the new and original data 
  5654.  records differ, the keys for the new data records, including any enumerators, 
  5655.  are in the buffers at AP[0].keyPtr to AP[3].keyPtr.  This even if the key 
  5656.  fields did not change.  If one, or all, of the new data records matched the 
  5657.  original data record, nothing is placed in *keyBuffer for that index. 
  5658.  
  5659.  Upon return, and there was an error, the return code can be -4 to -1, or 1 to 
  5660.  4.  If negative, the error was caused during processing of that AP pack's data 
  5661.  file portion, and the error code itself is in AP[abs(rez)-1].stat (where rez 
  5662.  is the return code, and rez-1 since C arrays start at 0).  If the return code 
  5663.  was positive, the error was caused during processing of that AP pack's index 
  5664.  file, and the error code itself is in AP[rez-1].stat, as well.  The return 
  5665.  code is, as in all BULLET transaction-list routines, an index of the AP pack 
  5666.  that generated the error -- negative if a data file error, positive if an 
  5667.  index file error. 
  5668.  
  5669.  Note:  If an error occurred after any part of the database had changed (during 
  5670.  this particular call), then any and all changes that were made are backed-out, 
  5671.  and the files restored to the same state as before the call. 
  5672.  
  5673.  Example: Specifying two index files for two records in the same data file 
  5674.  
  5675.  Three files: two index files related to one data file, where two data records 
  5676.  are to be updated, would set AccessPack to: 
  5677.  
  5678.   AP[0].func = UPDATE_XB;
  5679.   AP[0].handle = indexHandle_0;
  5680.   AP[0].recNo = recordToUpdate_0;
  5681.   AP[0].recPtr = &recordStruct_0;
  5682.   AP[0].keyPtr = keyBuffer_0;
  5683.   AP[0].nextPtr = AP[1];
  5684.  
  5685.   AP[1].handle = indexHandle_1;
  5686.   AP[1].recNo = recordToUpdate_0;
  5687.   AP[1].recPtr = &recordStruct_0;
  5688.   AP[1].keyPtr = keyBuffer_1;
  5689.   AP[1].nextPtr = AP[2];
  5690.  
  5691.   AP[2].handle = indexHandle_0;
  5692.   AP[2].recNo = recordToUpdate_1;
  5693.   AP[2].recPtr = &recordStruct_1;
  5694.   AP[2].keyPtr = keyBuffer_2;
  5695.   AP[2].nextPtr = AP[3];
  5696.  
  5697.   AP[3].handle = indexHandle_1;
  5698.   AP[3].recNo = recordToUpdate_1;
  5699.   AP[3].recPtr = &recordStruct_1;
  5700.   AP[3].keyPtr = keyBuffer_3;
  5701.   AP[3].nextPtr = NULL;
  5702.  
  5703.  A call to BULLET with the above does the following: 
  5704.  
  5705.    1. The data in *recordStruct_0 is used as the new record that is to replace 
  5706.       the data record at AP[0].recNo in the data file linked to the index file 
  5707.       in AP[0].handle. 
  5708.  
  5709.    2. If the record data in *recordStruct_0 is the same as the original disk 
  5710.       record, nothing is done.  If the data is new, the key fields are compared 
  5711.       to that belonging to the original disk record, and if the same, only the 
  5712.       record data is updated.  If the new record's key differs from the 
  5713.       original's, the original key for this record is removed from the index, 
  5714.       and the new key inserted. 
  5715.  
  5716.    3. The operation performed directly above is repeated, this time for the 
  5717.       second index file.  The new record data, and the record number to update, 
  5718.       are for this particular example, the same. 
  5719.  
  5720.    4. The data in *recordStruct_1 is used as the new record that is to replace 
  5721.       the data record at AP[2].recNo in the data file linked to the index file 
  5722.       in AP[2].handle.  This is the same index file as the first AP pack, and 
  5723.       also the same data file.  However, this is a different record number. 
  5724.  
  5725.    5. If the record data in *recordStruct_1 is the same as the original disk 
  5726.       record, nothing is done.  If the data is new, the key fields are compared 
  5727.       to that belonging to the original disk record, and if the same, only the 
  5728.       record data is updated.  If the new record's key differs from the 
  5729.       original's, the original key for this record is removed from the index, 
  5730.       and the new key inserted. 
  5731.  
  5732.    6. The operation performed directly above is repeated, this time for the 
  5733.       fourth index file. The new record data, and the record number to update 
  5734.       are, for this particular example, the same. 
  5735.  The return ritual is as described above, for "Specifying two index files each 
  5736.  for two different data files". 
  5737.  
  5738.  
  5739. ΓòÉΓòÉΓòÉ 9.64. REINDEX_XB ΓòÉΓòÉΓòÉ
  5740.  
  5741. Pack: ACCESSPACK               Source Example 
  5742.  
  5743.      IN               OUT
  5744.    AP.func          AP.stat
  5745.    AP.handle        AP.recNo
  5746.    AP.keyPtr        *AP.keyPtr
  5747.    AP.nextPtr
  5748.  
  5749.  Reindex all files in the transaction list, re-evaluating the key expression in 
  5750.  the process. 
  5751.  
  5752.  This routine is used to reindex up to 256 index files per call. The index 
  5753.  files must already exist and be open.  Any existing key values are overwritten 
  5754.  by new key data.  In other words, if you have a 100MB index file, REINDEX_XB 
  5755.  uses the same file space, building new keys over old.  This results in a less 
  5756.  fragmented disk and also minimizes disk space needed.  You can also create a 
  5757.  new, empty index file and reindex to that.  This would be useful, for 
  5758.  instance, if you needed to create a temporary index file -- something that 
  5759.  you'd use for a report, say, then delete after the report.  Another use for 
  5760.  creating a new index file and reindexing to that is to, after creating it 
  5761.  (COPY_INDEX_HEADER_XB can be used), use EXPAND_FILE_DOS and expand it to the 
  5762.  expected size.  This has the benefit of ensuring that this file allocation is 
  5763.  as contiguous as the file system allows (without relying on OS/API-specific 
  5764.  calls). 
  5765.  
  5766.  If the routine failed to complete, the function return value is the number 
  5767.  (1-based) of the pack that caused the failure.  A positive number indicates 
  5768.  the failure was from an index operation; a negative number indicates the 
  5769.  failure was from a data operation (reading the data file).  In each case, the 
  5770.  absolute value of the return code is the list item that failed (the pack 
  5771.  index).  For example, if five index handles are in the list(AP[0] to AP[4]), 
  5772.  and an error occurred on the last pack's index file, the return code would be 
  5773.  positive 5, indicating the fifth pack (AP[4]) failed.  Since it was a positive 
  5774.  5, the index file was being processed when the error occurred.  Being 
  5775.  processed means not only physical access, but verification, etc.  If the 
  5776.  return code was -5, then again, the error was in the fifth pack, but since it 
  5777.  is negative, the error occurred while processing the data file. 
  5778.  
  5779.  Unlike INSERT_XB and UPDATE_XB, each pack need not include a separate key 
  5780.  buffer; you may share a common key buffer.  If duplicate keys are generated in 
  5781.  the reindex process and the sort function does not flag DUPS_ALLOWED, an error 
  5782.  is returned.  The duplicate key is in *AP.keyPtr and the record number it was 
  5783.  generated from in AP.recNo (both these are valid only on error).  Since no 
  5784.  roll-back is performed, there is only a real need for a single key buffer. 
  5785.  You may use separate ones, too. 
  5786.  
  5787.  This routine creates a temporary work file in either the current directory or, 
  5788.  if the environment variable TMP is defined, in the directory pointed to by 
  5789.  TMP=.  The path used for this temporary file may also be specified at run-time 
  5790.  by using the TMP_PATH_PTR item for SET_SYSVARS_XB.  If TMP_PATH_PTR is NULL 
  5791.  (default), then TMP= is used, or if that is not found, then the current 
  5792.  directory is used.  The size of this temporary file is, in bytes, 
  5793.  approximately (keylength+4) * number of records in the data file.  The 
  5794.  resultant index files are, by default, optimized for minimum size and maximum 
  5795.  retrieval speed.  This full-node packing leaves one empty key per node, which 
  5796.  means b-tree splitting will occur almost immediately upon inserting data (with 
  5797.  INSERT_XB or STORE_KEY_XB). 
  5798.  
  5799.  This behaviour can be modified with the REINDEX_PACK_PCT item for 
  5800.  SET_SYSVARS_XB so that less packing is done.  Less packing would improve 
  5801.  subsequent INSERT_XB performance since all nodes are not almost full as they 
  5802.  are with a full pack.  File size and retrieval times increase, though, but 
  5803.  perhaps not noticeably. 
  5804.  
  5805.  During the reindex process, each record is checked for a matching skip-tag 
  5806.  value, as set in SET_SYSVARS_XB.  The skip-tag is set to 0 by default, where 
  5807.  no check is done and keys for all records in the data file are inserted into 
  5808.  the index file. 
  5809.  
  5810.  
  5811. ΓòÉΓòÉΓòÉ 9.65. LOCK_XB ΓòÉΓòÉΓòÉ
  5812.  
  5813. Pack: LOCKPACK                 Source Example 
  5814.  
  5815.      IN               OUT
  5816.    LP.func          LP.stat
  5817.    LP.handle
  5818.    LP.xlMode
  5819.    LP.dlMode
  5820.    LP.recStart=0
  5821.    LP.nextPtr
  5822.  
  5823.  Lock all bytes of the files in the list for exclusive use by the current 
  5824.  process, and reload file headers from disk.  LP.recStart must be 0 for each 
  5825.  pack. 
  5826.  
  5827.  This routine is used to lock the database for either exclusive use by this 
  5828.  process, or shared access (allowing any process to read, but not write, to the 
  5829.  files).  Up to 256 index files may be locked per call, as well as 256 data 
  5830.  files, too, for a total of 512 files per single LOCK_XB call.  Shared-access 
  5831.  locking prevents all processes from writing to the file while a shared lock is 
  5832.  in force, including this process.  To relock in exclusive lock mode, without 
  5833.  unlocking first, use: RELOCK_XB. 
  5834.  
  5835.  Only index handles are listed in LP.handle.  Each index file has associated 
  5836.  with it a data file, known internally to BULLET (the xbLink from OPEN_XB). 
  5837.  There may be more than one index file for a data file, but there is always one 
  5838.  data file per index handle specified in the list.  For example, you can list 
  5839.  five index files, each indexing the same xbLink data file, and have BULLET 
  5840.  perform an atomic lock of that list. 
  5841.  
  5842.  LP.xlMode is set to 1 to perform a shared lock on the index file.  Set to 0 
  5843.  for an exclusive lock.  A shared lock allows only reading. 
  5844.  
  5845.  LP.dlMode is set to 1 to perform a shared lock on the data file.  Set to 0 for 
  5846.  an exclusive lock.  A shared lock allows only reading. 
  5847.  
  5848.  The lock mode (shared <-> exclusive) can be changed using RELOCK_XB. 
  5849.  
  5850.  Bullet maintains a per-handle lock count, and does a physical region lock only 
  5851.  upon the first lock request (or on a relock request).  It is only on this 
  5852.  first lock request that the header is reloaded.  When the lock count returns 
  5853.  to 0 (from UNLOCK_XB calls), it is at that time the header is flushed, if 
  5854.  required.  Only full-locks are maintained in this way.  The number of 
  5855.  outstanding locks can be determined from the SIP and SDP structures, from the 
  5856.  STAT_XB routines.  Note that individual LOCK_INDEX_XB and UNLOCK_INDEX_XB 
  5857.  routines, as well as the data ones, also act upon this lock count.  Therefore, 
  5858.  you can lock a file 100 times in a row, but only on the first lock are any 
  5859.  operations actually performed, and only on the last unlock are any performed. 
  5860.  Other lock/unlock calls (other than the first lock or last unlock) simply 
  5861.  increment or decrement the lock count for that handle. 
  5862.  
  5863.  This and several other routines are transaction-list-based.  This means that 
  5864.  if a failure occurs prior to the routine's completion, all locks made to the 
  5865.  database by this routine will be unlocked. 
  5866.  
  5867.  If the routine failed to complete, the function return value is the number 
  5868.  (1-based) of the pack that caused the failure.  A positive number indicates 
  5869.  the failure was from an index operation; a negative number indicates the 
  5870.  failure was from a data operation.  In each case, the absolute value of the 
  5871.  return code is the list item that failed (the pack index).  For example, if 
  5872.  five index handles are in the list(AP[0] to AP[4]), and an error occurred on 
  5873.  the last pack's index file, the return code would be positive 5, indicating 
  5874.  the fifth pack (AP[4]) failed.  Since it was a positive 5, the index file was 
  5875.  being processed when the error occurred.  Being processed means not only 
  5876.  physical access, but verification, etc.  If the return code was -5, then 
  5877.  again, the error was in the fifth pack, but since it is negative, the error 
  5878.  occurred while processing the data file.  In either case, upon return, any 
  5879.  files locked during this call are unlocked before returning. 
  5880.  
  5881.  The advantage of using region locks (LOCK_XB locks entire file regions) to 
  5882.  control file access is that the file does not need to be opened/closed using 
  5883.  the Deny Read/Write sharing attribute.  Opening the file for Deny None, and 
  5884.  controlling subsequent access with region locks, allows for faster processing 
  5885.  since files do not need to be constantly opened and closed, as they would if 
  5886.  access were controlled by opening with Deny Read/Write. 
  5887.  
  5888.  Using the operating system to control access also prevents other processes 
  5889.  from accessing the files.  Other methods, such as internal locking (such as 
  5890.  using 'L' in the tag field as a program-aware in-use flag), work fine so long 
  5891.  as each process accessing the files knows about this internal "locking". 
  5892.  However, since it's proprietary, other processes may not know about it.  Any 
  5893.  locking system that is not commonly shared throughout the system is not 
  5894.  effective when it comes to preventing other processes from corrupting files. 
  5895.  
  5896.  Note:  Region locking prevents other processes from both writing and reading 
  5897.  the locked file.  For operating systems that do not provide shared locks, and 
  5898.  read-access is required at all times, you may specify this type access with 
  5899.  the access-sharing mode when the BULLET file is opened.  Once opened for this 
  5900.  (R/W, DenyWrite) then only the current process can write to the file until it 
  5901.  is closed.  Other processes must open the file for Read-Only access.  For 
  5902.  small networks (two or three nodes), this may be suitable.  Otherwise, region 
  5903.  locking is much preferred, and very much faster, since files do not have to be 
  5904.  opened and closed every time the access state needs to change. 
  5905.  
  5906.  
  5907. ΓòÉΓòÉΓòÉ 9.66. UNLOCK_XB ΓòÉΓòÉΓòÉ
  5908.  
  5909. Pack: LOCKPACK                 Source Example 
  5910.  
  5911.      IN               OUT
  5912.    LP.func          LP.stat
  5913.    LP.handle
  5914.    LP.recStart=0
  5915.    LP.nextPtr
  5916.  
  5917.  Unlock all bytes in the specified files (previously locked) and flush the 
  5918.  files' headers to disk (the flush is done before the locks are released). Also 
  5919.  unlock all bytes in the related data file and flush the data file header to 
  5920.  disk.  LP.recStart must be 0 for each pack. 
  5921.  
  5922.  Note:  If a shared-lock is active for this handle (as set by this process), 
  5923.  the flush is not performed.  This because writing to the locked region is not 
  5924.  permitted (nor is the flush required since nothing could have been changed). 
  5925.  
  5926.  If the routine failed to complete, the function return value is the number 
  5927.  (1-based) of the pack that caused the failure.  A positive number indicates 
  5928.  the failure was from an index operation; a negative number indicates the 
  5929.  failure was from a data operation.  In each case, the absolute value of the 
  5930.  return code is the list item that failed (the pack index).  For example, if 
  5931.  five index handles are in the list(AP[0] to AP[4]), and an error occurred on 
  5932.  the last pack's index file, the return code would be positive 5, indicating 
  5933.  the fifth pack (AP[4]) failed.  Since it was a positive 5, the index file was 
  5934.  being processed when the error occurred.  Being processed means not only 
  5935.  physical access, but verification, etc.  If the return code was -5, then 
  5936.  again, the error was in the fifth pack, but since it is negative, the error 
  5937.  occurred while processing the data file. 
  5938.  
  5939.  This routine does not attempt to re-lock those files unlocked successfully if 
  5940.  an error occurs in the transaction.  If an error does occur (unlikely), the 
  5941.  remaining files must be manually unlocked with the UNLOCK_KEY_XB and 
  5942.  UNLOCK_DATA_XB routines. 
  5943.  
  5944.  Bullet maintains a per-handle lock count, and does a physical region lock only 
  5945.  upon the first lock request (or on a relock request).  It is only on this 
  5946.  first lock request that the header is reloaded.  When the lock count returns 
  5947.  to 0 (from UNLOCK_XB calls), it is at that time the header is flushed, if 
  5948.  required.  Only full-locks are maintained in this way.  The number of 
  5949.  outstanding locks can be determined from the SIP and SDP structures, from the 
  5950.  STAT_XB routines.  Note that individual LOCK_INDEX_XB and UNLOCK_INDEX_XB 
  5951.  routines, as well as the data ones, also act upon this lock count.  Therefore, 
  5952.  you can lock a file 100 times in a row, but only on the first lock are any 
  5953.  operations actually performed, and only on the last unlock are any performed. 
  5954.  Other lock/unlock calls (other than the first lock or last unlock) simply 
  5955.  increment or decrement the lock count for that handle. 
  5956.  
  5957.  
  5958. ΓòÉΓòÉΓòÉ 9.67. LOCK_INDEX_XB ΓòÉΓòÉΓòÉ
  5959.  
  5960. Pack: LOCKPACK                 Source Example 
  5961.  
  5962.      IN               OUT
  5963.    LP.func          LP.stat
  5964.    LP.handle
  5965.    LP.xlMode
  5966.  
  5967.  Lock all bytes of the index file for exclusive use by the current process and 
  5968.  reload the index file's header from disk. 
  5969.  
  5970.  LP.xlMode is set to 1 to perform a shared lock.  Set to 0 for an exclusive 
  5971.  lock.  A shared lock allows only reading.  The lock mode (shared <-> 
  5972.  exclusive) can be changed using RELOCK_INDEX_XB. 
  5973.  
  5974.  Bullet maintains a per-handle lock count, and does a physical region lock only 
  5975.  upon the first lock request (or on a relock request).  It is only on this 
  5976.  first lock request that the header is reloaded.  When the lock count returns 
  5977.  to 0 (from UNLOCK_XB calls), it is at that time the header is flushed, if 
  5978.  required.  Only full-locks are maintained in this way.  The number of 
  5979.  outstanding locks can be determined from the SIP and SDP structures, from the 
  5980.  STAT_XB routines.  Note that individual LOCK_INDEX_XB and UNLOCK_INDEX_XB 
  5981.  routines, as well as the data ones, also act upon this lock count.  Therefore, 
  5982.  you can lock a file 100 times in a row, but only on the first lock are any 
  5983.  operations actually performed, and only on the last unlock are any performed. 
  5984.  Other lock/unlock calls (other than the first lock or last unlock) simply 
  5985.  increment or decrement the lock count for that handle. 
  5986.  
  5987.  
  5988. ΓòÉΓòÉΓòÉ 9.68. UNLOCK_INDEX_XB ΓòÉΓòÉΓòÉ
  5989.  
  5990. Pack: LOCKPACK                 Source Example 
  5991.  
  5992.      IN               OUT
  5993.    LP.func          LP.stat
  5994.    LP.handle
  5995.  
  5996.  Unlock all bytes in the specified file (previously locked) and flush the 
  5997.  file's header to disk (the flush is done before the locks are released). 
  5998.  
  5999.  If the current lock state is shared, the flush is not performed. 
  6000.  
  6001.  Bullet maintains a per-handle lock count, and does a physical region lock only 
  6002.  upon the first lock request (or on a relock request).  It is only on this 
  6003.  first lock request that the header is reloaded.  When the lock count returns 
  6004.  to 0 (from UNLOCK_XB calls), it is at that time the header is flushed, if 
  6005.  required.  Only full-locks are maintained in this way.  The number of 
  6006.  outstanding locks can be determined from the SIP and SDP structures, from the 
  6007.  STAT_XB routines.  Note that individual LOCK_INDEX_XB and UNLOCK_INDEX_XB 
  6008.  routines, as well as the data ones, also act upon this lock count.  Therefore, 
  6009.  you can lock a file 100 times in a row, but only on the first lock are any 
  6010.  operations actually performed, and only on the last unlock are any performed. 
  6011.  Other lock/unlock calls (other than the first lock or last unlock) simply 
  6012.  increment or decrement the lock count for that handle. 
  6013.  
  6014.  
  6015. ΓòÉΓòÉΓòÉ 9.69. LOCK_DATA_XB ΓòÉΓòÉΓòÉ
  6016.  
  6017. Pack: LOCKPACK                 Source Example 
  6018.  
  6019.      IN               OUT
  6020.    LP.func          LP.stat
  6021.    LP.handle
  6022.    LP.dlMode
  6023.    LP.recStart
  6024.    LP.recCount
  6025.  
  6026.  Lock all bytes of the data file specified in LP.handle for exclusive use by 
  6027.  the current process.  It also reloads the data file header from disk. You must 
  6028.  set LP.recStart=0 to lock all bytes.  To lock a single record, or a set of 
  6029.  contiguous records, set LP.recStart to the first record to lock and 
  6030.  LP.recCount to the number of records to lock. 
  6031.  
  6032.  Header re-loading is performed only if locking all bytes. 
  6033.  
  6034.  If LP.recStart is not 0, be aware that the header is not locked, nor is it 
  6035.  re-loaded.  Also, maintaining a lock on the single record prevents any other 
  6036.  process from doing a full lock on that data file, thereby preventing write 
  6037.  access to the file from any other BULLET process using LOCK_XB, but not 
  6038.  necessarily preventing other applications from writing to that file.  That may 
  6039.  or may not be good.  It does not prevent other BULLET processes from reading 
  6040.  that file (except for that locked record). 
  6041.  
  6042.  Multiple single records are allowed, but each must then be unlocked 
  6043.  individually, in the same format (start, count) as the lock. 
  6044.  
  6045.  LP.dlMode is set to 1 to perform a shared lock.  Set to 0 for an exclusive 
  6046.  lock.  A shared lock allows only reading.  The lock mode (shared <-> 
  6047.  exclusive) can be changed using RELOCK_DATA_XB. 
  6048.  
  6049.  Bullet maintains a per-handle lock count, and does a physical region lock only 
  6050.  upon the first lock request (or on a relock request).  It is only on this 
  6051.  first lock request that the header is reloaded.  When the lock count returns 
  6052.  to 0 (from UNLOCK_XB calls), it is at that time the header is flushed, if 
  6053.  required.  Only full-locks are maintained in this way.  The number of 
  6054.  outstanding locks can be determined from the SIP and SDP structures, from the 
  6055.  STAT_XB routines.  Note that individual LOCK_INDEX_XB and UNLOCK_INDEX_XB 
  6056.  routines, as well as the data ones if a full lock, also act upon this lock 
  6057.  count.  Therefore, you can lock a file 100 times in a row, but only on the 
  6058.  first lock are any operations actually performed, and only on the last unlock 
  6059.  are any performed.  Other lock/unlock calls (other than the first lock or last 
  6060.  unlock) simply increment or decrement the lock count for that handle. 
  6061.  
  6062.  
  6063. ΓòÉΓòÉΓòÉ 9.70. UNLOCK_DATA_XB ΓòÉΓòÉΓòÉ
  6064.  
  6065. Pack: LOCKPACK                 Source Example 
  6066.  
  6067.      IN               OUT
  6068.    LP.func          LP.stat
  6069.    LP.handle
  6070.    LP.recStart
  6071.    LP.recCount
  6072.  
  6073.  Unlock all bytes in the specified file handle (previously locked) and flush 
  6074.  the data file header to disk (the flush is done before the lock is released). 
  6075.  You must set LP.recStart=0 to unlock all bytes.  To unlock a single record, or 
  6076.  a set of contiguous records, set LP.recStart to the first record to unlock and 
  6077.  LP.recCount to the number of records to unlock. 
  6078.  
  6079.  Header flushing is performed only if unlocking a full lock. 
  6080.  
  6081.  An unlock must exactly mimic its corresponding lock.  This means if you lock 
  6082.  several records singly, you must unlock each of those records -- you cannot 
  6083.  use LP.recStart=0 to unlock singly-locked records. 
  6084.  
  6085.  Bullet maintains a per-handle lock count, and does a physical region lock only 
  6086.  upon the first lock request (or on a relock request).  It is only on this 
  6087.  first lock request that the header is reloaded.  When the lock count returns 
  6088.  to 0 (from UNLOCK_XB calls), it is at that time the header is flushed, if 
  6089.  required.  Only full-locks are maintained in this way.  The number of 
  6090.  outstanding locks can be determined from the SIP and SDP structures, from the 
  6091.  STAT_XB routines.  Note that individual LOCK_INDEX_XB and UNLOCK_INDEX_XB 
  6092.  routines, as well as the data ones if a full lock, also act upon this lock 
  6093.  count.  Therefore, you can lock a file 100 times in a row, but only on the 
  6094.  first lock are any operations actually performed, and only on the last unlock 
  6095.  are any performed.  Other lock/unlock calls (other than the first lock or last 
  6096.  unlock) simply increment or decrement the lock count for that handle. 
  6097.  
  6098.  
  6099. ΓòÉΓòÉΓòÉ 9.71. CHECK_REMOTE_XB ΓòÉΓòÉΓòÉ
  6100.  
  6101. Pack: REMOTEPACK               Source Example 
  6102.  
  6103.      IN               OUT
  6104.    RP.func          RP.stat
  6105.    RP.handle        RP.isRemote
  6106.       -or-          RP.flags=0
  6107.    RP.drive         RP.isShare=1
  6108.  
  6109.  If RP.handle is non-zero, determine if the specified handle of a file or 
  6110.  device is remote.  If the handle is local (e.g., not a network file or 
  6111.  device), RP.isRemote returns 0, otherwise it is remote.  RP.flags=0 and 
  6112.  RP.isShare=1 on return for either a handle or drive check under OS/2. 
  6113.  
  6114.  If RP.handle is zero, determine if the drive specified in RP.drive is remote. 
  6115.  Drive A: is 1, B: is 2, C: is 3, and so on.  To check the default drive use 0 
  6116.  (the default drive is the current drive).  If the drive is local (e.g., not a 
  6117.  network drive), RP.isRemote returns 0, otherwise it is remote. 
  6118.  
  6119.  The significance of the remote-ness is less important in a multitasking 
  6120.  environment since sharing of resources (files, in particular) must always be 
  6121.  managed, compared to single-tasking environments where, typically, sharing 
  6122.  (locking mechanisms) need only be performed when the resource is able to be 
  6123.  accessed by another process (ie is a 'network' drive).  Note that the resource 
  6124.  need not be located elsewhere to be classified as remote: Drives or devices or 
  6125.  files on the same machine may be classified as remote if the network software 
  6126.  is redirecting local access (such as on a server). 
  6127.  
  6128.  Note:  This routine is not mutex-protected. 
  6129.  
  6130.  
  6131. ΓòÉΓòÉΓòÉ 9.72. RELOCK_XB ΓòÉΓòÉΓòÉ
  6132.  
  6133. Pack: LOCKPACK                 Source Example 
  6134.  
  6135.      IN               OUT
  6136.    LP.func          LP.stat
  6137.    LP.handle
  6138.    LP.xlMode
  6139.    LP.dlMode
  6140.    LP.recStart=0
  6141.    LP.nextPtr
  6142.  
  6143.  Relock all bytes of the index files for the mode specified in LP.xlMode (index 
  6144.  files) and LP.dlMode (data files).  Also relock all bytes in the related data 
  6145.  file.  LP.recStart must be 0 for each pack. 
  6146.  
  6147.  Set LP.xlMode=1 to select a shared lock for the index file; set to 0 for an 
  6148.  exclusive lock.  Set LP.dlMode=1 to select a shared lock for the data file; 
  6149.  set to 0 for an exclusive lock. 
  6150.  
  6151.  If the lock mode is from exclusive to shared, the file is flushed before the 
  6152.  shared state is set.  BULLET maintains the current lock state and knows which 
  6153.  direction the lock is going in.  The lock state (shared or exclusive) can be 
  6154.  determined by the SIP and SDP structures from the STAT_XB routines.  This 
  6155.  routine does not affect the lock count, nor are the headers reloaded (nor 
  6156.  should they be). 
  6157.  
  6158.  The lock state is on a file handle basis, not on an LP[] pack basis. This 
  6159.  means the file, as identified by the handle, is in the lock state last set. 
  6160.  
  6161.  Note:  The lock switch is made atomic:  Rather than unlocking, and then 
  6162.  locking again in the new state, this performs all operations without the 
  6163.  possibility that another process can grab the lock away, if supported by the 
  6164.  OS.  If relock is not supported by the OS, and shared (read-only) locks are 
  6165.  supported, the relock will be performed by Bullet rather than the OS.  If 
  6166.  Bullet performs the relock, another, non-Bullet process may be able to steal 
  6167.  away the lock since it is no longer guaranteed to be atomic. 
  6168.  
  6169.  
  6170. ΓòÉΓòÉΓòÉ 9.73. RELOCK_INDEX_XB ΓòÉΓòÉΓòÉ
  6171.  
  6172. Pack: LOCKPACK                 Source Example 
  6173.  
  6174.      IN               OUT
  6175.    LP.func          LP.stat
  6176.    LP.handle
  6177.    LP.xlMode
  6178.  
  6179.  Relock all bytes of the index file for the mode specified in LP.xlMode and 
  6180.  reload the header. 
  6181.  
  6182.  Set LP.xlMode=1 to select a shared lock; set to 0 for an exclusive lock. If 
  6183.  the lock mode is from exclusive to shared, the file is flushed before the 
  6184.  shared state is set.  BULLET maintains the current lock state and knows which 
  6185.  direction the lock is going in.  The lock state (shared or exclusive) can be 
  6186.  determined by the SIP structure from the STAT_INDEX_XB routine.  This routine 
  6187.  does not affect the lock count, nor is the header reloaded (nor should it be). 
  6188.  
  6189.  Note:  The lock switch is made atomic:  Rather than unlocking, and then 
  6190.  locking again in the new state, this performs all operations without the 
  6191.  possibility that another process can grab the lock away, if supported by the 
  6192.  OS.  If relock is not supported by the OS, and shared (read-only) locks are 
  6193.  supported, the relock will be performed by Bullet rather than the OS.  If 
  6194.  Bullet performs the relock, another, non-Bullet process may be able to steal 
  6195.  away the lock since it is no longer guaranteed to be atomic. 
  6196.  
  6197.  
  6198. ΓòÉΓòÉΓòÉ 9.74. RELOCK_DATA_XB ΓòÉΓòÉΓòÉ
  6199.  
  6200. Pack: LOCKPACK                 Source Example 
  6201.  
  6202.      IN               OUT
  6203.    LP.func          LP.stat
  6204.    LP.handle
  6205.    LP.dlMode
  6206.    LP.recStart
  6207.    LP.recCount
  6208.  
  6209.  Relock all bytes of the data file for the mode specified in LP.dlMode and 
  6210.  reload the header, unless LP.recStart is non-zero. 
  6211.  
  6212.  If the lock mode is from exclusive to shared, the file is flushed before the 
  6213.  shared state is set.  BULLET maintains the current lock state and knows which 
  6214.  direction the lock is going in.  The lock state (shared or exclusive) can be 
  6215.  determined by the SDP structure from the STAT_DATA_XB routine.  This routine 
  6216.  does not affect the lock count, nor is the header reloaded (nor should it be). 
  6217.  
  6218.  You must set LP.recStart=0 to relock all bytes.  To relock a single record, or 
  6219.  set of contiguous records, set LP.recStart=record# to relock and LP.recCount 
  6220.  to the number of records to relock. 
  6221.  
  6222.  Note:  The lock switch is made atomic:  Rather than unlocking, and then 
  6223.  locking again in the new state, this performs all operations without the 
  6224.  possibility that another process can grab the lock away, if supported by the 
  6225.  OS.  If relock is not supported by the OS, and shared (read-only) locks are 
  6226.  supported, the relock will be performed by Bullet rather than the OS.  If 
  6227.  Bullet performs the relock, another, non-Bullet process may be able to steal 
  6228.  away the lock since it is no longer guaranteed to be atomic. 
  6229.  
  6230.  
  6231. ΓòÉΓòÉΓòÉ 9.75. DELETE_FILE_DOS ΓòÉΓòÉΓòÉ
  6232.  
  6233. Pack: DOSFILEPACK              Source Example 
  6234.  
  6235.      IN               OUT
  6236.    DFP.func         DFP.stat
  6237.    DFP.filenamePtr
  6238.  
  6239.  Delete the specified file. 
  6240.  
  6241.  Note:  OS/2 DosForceDelete is used so the file is not recoverable with the 
  6242.  UNDELETE command. 
  6243.  
  6244.  
  6245. ΓòÉΓòÉΓòÉ 9.76. RENAME_FILE_DOS ΓòÉΓòÉΓòÉ
  6246.  
  6247. Pack: DOSFILEPACK              Source Example 
  6248.  
  6249.      IN               OUT
  6250.    DFP.func         DFP.stat
  6251.    DFP.filenamePtr
  6252.    DFP.newNamePtr
  6253.  
  6254.  Rename a file.  May also be used to move the file to a new directory within 
  6255.  the partition. 
  6256.  
  6257.  If the specified directory differs from the file's directory, the file's 
  6258.  directory entry is moved to the new directory. 
  6259.  
  6260.  For example, if the filenamePtr filename is /LP100/PROJ94A.INF and the 
  6261.  newFilenamePtr filename is /ARCH/PROJ93A.INA, the file is essentially renamed 
  6262.  and also moved to the /ARCH directory. 
  6263.  
  6264.  
  6265. ΓòÉΓòÉΓòÉ 9.77. CREATE_FILE_DOS ΓòÉΓòÉΓòÉ
  6266.  
  6267. Pack: DOSFILEPACK              Source Example 
  6268.  
  6269.      IN               OUT
  6270.    DFP.func         DFP.stat
  6271.    DFP.filenamePtr
  6272.    DFP.attr
  6273.  
  6274.  Create a new file. 
  6275.  
  6276.  The specified filename/pathname must not already exist. 
  6277.  
  6278.  The file created is not left open.  You must OPEN_FILE_DOS to use it. 
  6279.  
  6280.  The attribute used during the create can be: 
  6281.  
  6282.    Attribute  Value   Meaning
  6283.     Normal       0      normal access permitted to file
  6284.     Read-Only    1      read-only access permitted to file
  6285.     Hidden       2      file does not appear in directory listing
  6286.     System       4      file is a system file
  6287.     SubDir      10h     FILENAME is a subdirectory
  6288.     Archive     20h     file is marked for archiving
  6289.  
  6290.  Note:  Use MAKE_DIR_DOS to create a subdirectory. 
  6291.  
  6292.  
  6293. ΓòÉΓòÉΓòÉ 9.78. OPEN_FILE_DOS ΓòÉΓòÉΓòÉ
  6294.  
  6295. Pack: DOSFILEPACK              Source Example 
  6296.  
  6297.      IN               OUT
  6298.    DFP.func         DFP.stat
  6299.    DFP.filenamePtr  DFP.handle
  6300.    DFP.asMode
  6301.  
  6302.  Open the file with the access-sharing mode, returning the handle on success. 
  6303.  
  6304.  
  6305. ΓòÉΓòÉΓòÉ 9.79. SEEK_FILE_DOS ΓòÉΓòÉΓòÉ
  6306.  
  6307. Pack: DOSFILEPACK              Source Example 
  6308.  
  6309.      IN               OUT
  6310.    DFP.func         DFP.stat
  6311.    DFP.handle       DFP.seekTo
  6312.    DFP.seekTo
  6313.    DFP.method
  6314.  
  6315.  Position the file pointer of the file to the seekTo position based on the 
  6316.  method specified. 
  6317.  
  6318.  The position is a 32-bit value and is relative to either the start of the 
  6319.  file, the current file pointer position, or the end of the file. 
  6320.  
  6321.   Method   Meaning
  6322.     0      start move from the start of file (offset is a 32-bit unsigned value)
  6323.     1      start move at the current position (offset a signed value)
  6324.     2      start move at the end of file (offset a signed value)
  6325.  For example, to move to the last byte of a sector (512th byte, but offset 
  6326.  511), set the offset value to 511 and use Method 0.  On return, the absolute 
  6327.  offset value of the new position is returned.  This return value is useful 
  6328.  with Method 2 since you can specify an offset of 0 and have the file length 
  6329.  returned.  To move to the start of the file, use method 0, offset 0.  To move 
  6330.  to the first byte of the second sector, use offset 512. 
  6331.  
  6332.  Note:  Never position the file pointer to before the start of file. 
  6333.  
  6334.  
  6335. ΓòÉΓòÉΓòÉ 9.80. READ_FILE_DOS ΓòÉΓòÉΓòÉ
  6336.  
  6337. Pack: DOSFILEPACK              Source Example 
  6338.  
  6339.      IN               OUT
  6340.    DFP.func         DFP.stat
  6341.    DFP.handle       DFP.bytes
  6342.    DFP.bytes
  6343.    DFP.bufferPtr
  6344.  
  6345.  Read from the file or device the specified number of bytes into a buffer. 
  6346.  
  6347.  On block devices (such as disks) input starts at the current file position and 
  6348.  the file pointer is repositioned to the last byte read +1. 
  6349.  
  6350.  It is possible to read less than the bytes specified without an error being 
  6351.  generated.  Compare the bytes to read with the returned bytes read value.  If 
  6352.  less then end of file was reached during the read.  If 0 then file was already 
  6353.  at EOF. 
  6354.  
  6355.  
  6356. ΓòÉΓòÉΓòÉ 9.81. WRITE_FILE_DOS ΓòÉΓòÉΓòÉ
  6357.  
  6358. Pack: DOSFILEPACK              Source Example 
  6359.  
  6360.      IN               OUT
  6361.    DFP.func         DFP.stat
  6362.    DFP.handle       DFP.bytes
  6363.    DFP.bytes
  6364.    DFP.bufferPtr
  6365.  
  6366.  Write to the file or device the specified number of bytes from a buffer. 
  6367.  
  6368.  If the number of bytes written is less than the specified bytes, this routine 
  6369.  returns an error. 
  6370.  
  6371.  On block devices (such as disk) output starts at the current file position, 
  6372.  and the file pointer is repositioned to the last byte written +1. 
  6373.  
  6374.  Note:  If the specified bytes to write is 0, the file is truncated at the 
  6375.  current file pointer position. 
  6376.  
  6377.  
  6378. ΓòÉΓòÉΓòÉ 9.82. CLOSE_FILE_DOS ΓòÉΓòÉΓòÉ
  6379.  
  6380. Pack: DOSFILEPACK              Source Example 
  6381.  
  6382.      IN               OUT
  6383.    DFP.func         DFP.stat
  6384.    DFP.handle
  6385.  
  6386.  Close the file flushing any internal buffers, releasing any locked regions, 
  6387.  and updating the directory entry to the correct size, date, and time. 
  6388.  
  6389.  
  6390. ΓòÉΓòÉΓòÉ 9.83. ACCESS_FILE_DOS ΓòÉΓòÉΓòÉ
  6391.  
  6392. Pack: DOSFILEPACK              Source Example 
  6393.  
  6394.      IN               OUT
  6395.    DFP.func         DFP.stat
  6396.    DFP.filenamePtr
  6397.    DFP.asMode
  6398.  
  6399.  Determine if the specified file can be accessed with the specified 
  6400.  access-sharing mode. 
  6401.  
  6402.  Basically, a Does-File-Exist routine.  It uses the specified access-sharing 
  6403.  mode when trying to open the file.  For example, if you specify DFP.attr = 
  6404.  0x0042 (R/W access + Deny None sharing) and issue ACCESS_FILE_DOS on a 
  6405.  Read-Only file, an error is returned.  A sharing mode must be specified; it 
  6406.  cannot be left 0. 
  6407.  
  6408.  
  6409. ΓòÉΓòÉΓòÉ 9.84. EXPAND_FILE_DOS ΓòÉΓòÉΓòÉ
  6410.  
  6411. Pack: DOSFILEPACK              Source Example 
  6412.  
  6413.      IN               OUT
  6414.    DFP.func         DFP.stat
  6415.    DFP.handle
  6416.    DFP.bytes
  6417.  
  6418.  Expands the file by the number of bytes beyond its current size. 
  6419.  
  6420.  This routine is useful in pre-allocating disk space.  By reserving disk space 
  6421.  in advance you can guarantee that enough disk space will be available for a 
  6422.  future operation (especially if more than 1 process is running). You'll also 
  6423.  be able ensure that the disk space that a file does use is as contiguous as 
  6424.  possible. 
  6425.  
  6426.  Database systems are dynamic and their files typically allocate new space on 
  6427.  an as-needed basis.  This dynamic allocation can cause parts of a file to be 
  6428.  located throughout the disk system, possibly affecting performance 
  6429.  drastically.  By pre-allocating the disk space you can be assured of 
  6430.  consistent throughput performance since the file is contiguous. 
  6431.  
  6432.  Note:  The file space is not initialized. 
  6433.  
  6434.  * --------------------------------------------------------------------------- 
  6435.  
  6436.  
  6437. ΓòÉΓòÉΓòÉ 9.85. MAKE_DIR_DOS ΓòÉΓòÉΓòÉ
  6438.  
  6439. Pack: DOSFILEPACK              Source Example 
  6440.  
  6441.      IN               OUT
  6442.    DFP.func         DFP.stat
  6443.    DFP.filenamePtr
  6444.  
  6445.  Create a new subdirectory. 
  6446.  
  6447.  
  6448. ΓòÉΓòÉΓòÉ 9.86. COMMIT_FILE_DOS ΓòÉΓòÉΓòÉ
  6449.  
  6450. Pack: DOSFILEPACK              Source Example 
  6451.  
  6452.      IN               OUT
  6453.    DFP.func         DFP.stat
  6454.    DFP.handle
  6455.  
  6456.  Flushes the OS system buffers for the handle, and updates the directory entry 
  6457.  for size. 
  6458.  
  6459.  
  6460. ΓòÉΓòÉΓòÉ 10. Bullet Source Examples ΓòÉΓòÉΓòÉ
  6461.  
  6462. Bullet source example excerpts for the C language follow.  Complete program 
  6463. source examples are provide on the included disk. 
  6464.  
  6465. In the examples, consider that 
  6466.  
  6467.         CHAR keyBuffer[64];
  6468.         AP.keyPtr = keyBuffer;
  6469.  
  6470. provides the compiler with a pointer whenever 'keyBuffer' is referenced, hence 
  6471. no need to use &keyBuffer.  Contrast this with a structure definition 
  6472.  
  6473.         struct yourRecordLayout yourRecord;
  6474.         AP.recPtr = &yourRecord;
  6475.  
  6476. where &yourRecord is required. 
  6477.  
  6478. Be aware of how you use zero-terminated strings, especially for fields that are 
  6479. not to be 0T'ed (DATE and NUMERIC fields, for example).  Use sscanf() and 
  6480. sprintf(), as required. 
  6481.  
  6482.  
  6483. ΓòÉΓòÉΓòÉ 10.1. Bullet Initialization ΓòÉΓòÉΓòÉ
  6484.  
  6485. /* All example source uses minimal error checking/handling throughout */
  6486. // comment marks are used throughout
  6487.  
  6488. // To simplify the source examples, error handling is typically of the form:
  6489. //
  6490. //   if (rez) goto ErrorHandler;
  6491. //
  6492. // Use suitable coding as you would normally handle the error.
  6493.  
  6494. #include "bullet_2.h"
  6495.  
  6496. INITPACK IP;                    // packs used here
  6497.  
  6498. IP.func = INIT_XB;              // start up Bullet
  6499. IP.JFTsize = 30;                // allow at least 30 handles to be open
  6500. rez = BULLET(&IP);
  6501. if (rez) return(rez);
  6502.  
  6503.  
  6504. ΓòÉΓòÉΓòÉ 10.2. Bullet Shutdown ΓòÉΓòÉΓòÉ
  6505.  
  6506. #include "bullet_2.h"
  6507.  
  6508. EXITPACK EP;                    // packs used here
  6509.  
  6510. EP.func = EXIT_XB;              // shutdown Bullet
  6511. rez = BULLET(&EP);
  6512. if (rez) return(rez);
  6513.  
  6514.  
  6515. ΓòÉΓòÉΓòÉ 10.3. Memory Available ΓòÉΓòÉΓòÉ
  6516.  
  6517. #include "bullet_2.h"
  6518.  
  6519. MEMORYPACK EP;                  // packs used here
  6520.  
  6521. MP.func = MEMORY_XB;
  6522. rez = BULLET(&MP);
  6523. if (rez==0) {
  6524.    printf("Private memory arena space right now is %d bytes\n",MP.memory);
  6525. }
  6526.  
  6527.  
  6528. ΓòÉΓòÉΓòÉ 10.4. File Backup Procedure ΓòÉΓòÉΓòÉ
  6529.  
  6530. #include "bullet_2.h"
  6531.  
  6532. // code determines file type of handle, and selects appropriate backup method
  6533. // this type of work (copy/backup) would be a prelude to reindexing an index
  6534. // file or performing a pack on a data file since both of those routines
  6535. // overwrite existing data
  6536.  
  6537. COPYPACK CP;
  6538. LOCKPACK LP;
  6539. STATHANDLEPACK SHP;
  6540. STATDATAPACK SDP;
  6541. STATINDEXPACK SIP;
  6542.  
  6543. CHAR CopyOfIndexHdrName[260];
  6544. CHAR BackupDataName[260];
  6545.  
  6546. // Given a handle, find out its type and, if index, generate a copy
  6547. // of the index file's header, or if data, backup the entire data file.
  6548. // Files are locked to ensure that access if permissible.
  6549.  
  6550. SHP.func = STAT_HANDLE_XB;
  6551. SHP.handle = passedHandle;
  6552. rez = BULLET(&SHP);
  6553. if (SHP.ID==-1)
  6554.    puts("Handle is not a Bullet data or index file.  Use DosCopy() API call.");
  6555. else {
  6556.    if (SHP.ID==0) {
  6557.  
  6558.       // Since the copy takes place on open files, ensure a full-lock is in place
  6559.       // A shared lock is okay since this file is only read
  6560.  
  6561.       // locking the entire file also RELOADS the index header
  6562.  
  6563.       LP.func = LOCK_INDEX_XB;
  6564.       LP.handle = passedHandle;
  6565.       LP.xlMode = LOCK_SHARED;          // in bullet_2.h
  6566.       rez = BULLET(&LP);
  6567.       if (rez) goto ErrorHandler;
  6568.  
  6569.       SIP.func = STAT_INDEX_XB;
  6570.       SIP.handle = passedHandle;
  6571.       rez = BULLET(&SIP);
  6572.       if (rez) goto ErrorHandler;       // must unlock file in handler
  6573.  
  6574.       // SIP.filenamePtr -> pathname of this handle, from which you can
  6575.       // derive a suitable name for the index header written next -- in
  6576.       // case it's not obvious, this function is not part of Bullet
  6577.  
  6578.       DeriveSuitableName(SIP.filenamePtr,CopyOfIndexHdrName);
  6579.  
  6580.       CP.func = COPY_INDEX_HEADER_XB;
  6581.       CP.handle = passedHandle;
  6582.       CP.filenamePtr = CopyOfIndexHdrName;
  6583.       rez = BULLET(&CP);
  6584.       if (rez) goto ErrorHandler;       // must unlock file in handler
  6585.  
  6586.       // unlocking also flushes the index header if not LOCK_SHARED (and if required)
  6587.  
  6588.       LP.func = UNLOCK_INDEX_XB;
  6589.       LP.handle = passedHandle;
  6590.       rez = BULLET(&LP);
  6591.       if (rez) goto ErrorHandler;
  6592.    }
  6593.    else {
  6594.  
  6595.       // locking the entire file also RELOADS the data header
  6596.  
  6597.       LP.func = LOCK_DATA_XB;
  6598.       LP.handle = passedHandle;
  6599.       LP.dlMode = LOCK_SHARED;
  6600.       LP.startRec = 0;                  // lock entire data file
  6601.       rez = BULLET(&LP);
  6602.       if (rez) goto ErrorHandler;
  6603.  
  6604.       SDP.func = STAT_DATA_XB;
  6605.       SDP.handle = passedHandle;
  6606.       rez = BULLET(&SDP);
  6607.       if (rez) goto ErrorHandler;       // must unlock file in handler
  6608.  
  6609.       DeriveSuitableName(SDP.filenamePtr,BackupDataName);
  6610.  
  6611.       CP.func = BACKUP_FILE_XB;
  6612.       CP.handle = passedHandle;         // set to -passedHandle to skip memo backup
  6613.       CP.filenamePtr = BackupDataName;
  6614.       rez = BULLET(&CP);
  6615.       if (rez) goto ErrorHandler;       // must unlock file in handler
  6616.  
  6617.       // unlocking also flushes the data header if not LOCK_SHARED (and if required)
  6618.  
  6619.       LP.func = UNLOCK_DATA_XB;
  6620.       LP.handle = passedHandle;
  6621.       rez = BULLET(&LP);
  6622.       if (rez) goto ErrorHandler;
  6623.    }
  6624. }
  6625.  
  6626.  
  6627. ΓòÉΓòÉΓòÉ 10.5. Get Error Class ΓòÉΓòÉΓòÉ
  6628.  
  6629. #include "bullet_2.h"
  6630.  
  6631. XERRORPACK XEP;                 // packs used here
  6632.  
  6633. // Bullet errors range from 8192 to 8999, any other return code
  6634. // indicates the error number was generated by OS/2 itself
  6635.  
  6636. if (rez < 8192) | (rez > 8999) {
  6637.    XEP.func = GET_ERROR_CLASS_XB;
  6638.    XEP.stat = rez;
  6639.    rez = BULLET(&XEP);
  6640.  
  6641.    // here XEP.errClass, .action, and .location are set
  6642.    // this call is the same as OS/2 API DosErrClass()
  6643.  
  6644.  
  6645. ΓòÉΓòÉΓòÉ 10.6. Query System Variables ΓòÉΓòÉΓòÉ
  6646.  
  6647. #include "bullet_2.h"
  6648.  
  6649. QUERYSETPACK  QSP;              // packs used here
  6650.  
  6651. // Query a Bullet system variable
  6652.  
  6653. QSP.func = QUERY_SYSVARS_XB;
  6654. QSP.item = MUTEX_SEM_HANDLE;    // in bullet_2.h
  6655. rez = BULLET(&QSP);
  6656. if (rez==0)
  6657.    printf("Bullet/2 mutex handle is %d\n",QSP.itemValue);
  6658.  
  6659.  
  6660. ΓòÉΓòÉΓòÉ 10.7. Set System Variables ΓòÉΓòÉΓòÉ
  6661.  
  6662. #include "bullet_2.h"
  6663.  
  6664. QUERYSETPACK  QSP;              // packs used here
  6665.  
  6666. // Set a Bullet system variable
  6667.  
  6668. QSP.func = SET_SYSVARS_XB;
  6669. QSP.item = REINDEX_BUFFER_SIZE; // in bullet_2.h
  6670. QSP.itemValue = 384*1024;       // set to 384KB
  6671. rez = BULLET(&QSP);
  6672. if (rez==0) {
  6673.    printf("Reindex buffer sized changed to 384K.\n");
  6674.    printf("Previous setting was %d.\n",QSP.itemValue);
  6675.  
  6676.    // For REINDEX_BUFFER_SIZE, a value of 0 represents 'autosize', for which
  6677.    // Bullet selects a minimum usuable size (often 144KB).  The minimum size
  6678.    // that you can use for REINDEX_BUFFER_SIZE is 48KB.
  6679.  
  6680.  
  6681. ΓòÉΓòÉΓòÉ 10.8. Set Dual-video Monitor ΓòÉΓòÉΓòÉ
  6682.  
  6683.  
  6684. // SET_DVMON_XB is not currently used
  6685.  
  6686.  
  6687. ΓòÉΓòÉΓòÉ 10.9. Query Vectors ΓòÉΓòÉΓòÉ
  6688.  
  6689. #include "bullet_2.h"
  6690.  
  6691. QUERYSETPACK  QSP;              // packs used here
  6692.  
  6693. // Query a Bullet vector
  6694.  
  6695. QSP.func = QUERY_VECTORS_XB;
  6696. QSP.item = VECTOR_GET_SORT_TABLE;  // in bullet_2.h
  6697. rez = BULLET(&QSP);
  6698. if (rez==0)
  6699.    printf("Bullet get-sort-table vector is %x\n",QSP.itemValue);
  6700.  
  6701.  
  6702. ΓòÉΓòÉΓòÉ 10.10. Set Vectors ΓòÉΓòÉΓòÉ
  6703.  
  6704. #include "bullet_2.h"
  6705.  
  6706. QUERYSETPACK  QSP;              // packs used here
  6707.  
  6708. // Set a Bullet vector
  6709.  
  6710. // external routine (in ccdosfn.c, for example)
  6711. LONG __cdecl BulletMalloc(ULONG bytes, VOID **basePtr);
  6712.  
  6713. QSP.func = SET_VECTORS_XB;
  6714. QSP.item = VECTOR_MALLOC;
  6715. QSP.itemValue = (ULONG) &BulletMalloc;
  6716. rez = BULLET(&QSP);
  6717. if (rez==0) {
  6718.    printf("Memory allocation changed to new routine.\n");
  6719.    printf("Previous routine's address was %x.\n",QSP.itemValue);
  6720.  
  6721.  
  6722. ΓòÉΓòÉΓòÉ 10.11. Create, Open, and Close Data and Index Files ΓòÉΓòÉΓòÉ
  6723.  
  6724. #include "bullet_2.h"
  6725.  
  6726. CREATEDATAPACK  CDP;
  6727. CREATEINDEXPACK CIP;
  6728. OPENPACK        OP;
  6729. HANDLEPACK      HP;             // packs used here
  6730.  
  6731. // create the data file
  6732.  
  6733. #pragma pack(1)         // ensure compiler does not pad Bullet-used structures
  6734.                         // (not needed here since all members are CHAR)
  6735.  
  6736. // generally, only left-justified strings should be 0-terminated, numbers or
  6737. // date fields should not be 0-terminated
  6738.  
  6739. typedef struct _RECTYPE {
  6740.  CHAR tag;              // record tag, init to SPACE, '*' means deleted
  6741.  CHAR userSSN[9];       // first field in DBF (not 0-terminated in this case)
  6742.  CHAR userScore[6];     // second field (also not 0T)
  6743. } RECTYPE;              // (total record length is 16 bytes)
  6744. RECTYPE ourRecord;
  6745.  
  6746. #pragma pack()
  6747.  
  6748. CHAR nameIX3[] = "INDEX.IX3";   // index pathname
  6749. CHAR keyExpression[] = "SSN";   // key is built from field named 'SSN'
  6750. ULONG indexID=0;                // handle of opened index file
  6751. CHAR keyBuffer[68];             // buffer to store/receive key values
  6752.  
  6753. CHAR nameData[] = "DATA.DBF";   // data pathname
  6754. ULONG dataID=0;                 // handle of opened data file
  6755. FIELDDESCTYPE fieldList[2];     // 2 fields used in data record (SSN and SCORE)
  6756.  
  6757. // the field descriptor info must have unused entries set to 0
  6758.  
  6759. memset(fieldList,0,sizeof(fieldList));  // init unused bytes to 0 (required)
  6760.  
  6761. // fill in the field descriptor info for the data file you want to create
  6762. // this descriptor must match the layout of ourRecord, above (ourRecord.tag
  6763. // is implicit and is not a formal field, and so is not in fieldList[])
  6764.  
  6765. strcpy(fieldList[0].fieldName, "SSN");  // field names must be upper-case
  6766. fieldList[0].fieldType = 'C';           // field types must be upper-case
  6767. fieldList[0].fieldLen = 9;              // * Note that the .fieldname here
  6768. fieldList[0].fieldDC = 0;               // * matches the keyExpression
  6769.  
  6770. strcpy(fieldList[1].fieldName, "SCORE");
  6771. fieldList[1].fieldType = 'C';
  6772. fieldList[1].fieldLen = 6;              // 6 is total size of field
  6773. fieldList[1].fieldDC = 0;
  6774.  
  6775. // Create the data file as defined in fieldList above
  6776. // To create only a DBF, set CDP.fileID=3
  6777. // To create both a DBF and a DBT memo file, set CDP.fileID=0x8B
  6778.  
  6779. CDP.func = CREATE_DATA_XB;
  6780. CDP.filenamePtr = nameData;
  6781. CDP.noFields = 2;
  6782. CDP.fieldListPtr = fieldList;
  6783. CDP.fileID = 3;
  6784. rez = BULLET(&CDP);
  6785. if (rez) {
  6786.    printf("Failed data file create.  Err: %d\n",rez);
  6787.    return(rez);
  6788. }
  6789.  
  6790. // Open the data file (required before creating an index file for it)
  6791.  
  6792. OP.func = OPEN_DATA_XB;
  6793. OP.filenamePtr = nameData;
  6794. OP.asMode = READWRITE | DENYNONE;
  6795. rez = BULLET(&OP);
  6796. if (rez) {
  6797.    printf("Failed data file open.  Err: %d\n",rez);
  6798.    return(rez);
  6799. }
  6800. dataID = OP.handle;
  6801.  
  6802. // Create an index file for the data file opened above.
  6803. // This example uses a simple primary key: the SSN field.
  6804. // Since it is assumed to be unique, DUPS_ALLOWED is not
  6805. // OR'ed with the .sortFunction.
  6806.  
  6807. CIP.func = CREATE_INDEX_XB;
  6808. CIP.filenamePtr = nameIX3;
  6809. CIP.keyExpPtr = keyExpression;
  6810. CIP.xbLink = dataID;            // the handle of the data file
  6811. CIP.sortFunction = ASCII_SORT;  // sort key by ASCII (fine for SSN ordering)
  6812. CIP.codePage = 0;               // use OS-default code page
  6813. CIP.countryCode = 0;            // use OS-default country code
  6814. CIP.collatePtr = NULL;          // no need for a special collate table
  6815. CIP.nodeSize = 512;             // 512-byte node size (or 1024, 2048 bytes)
  6816. rez = BULLET(&CIP);
  6817. if (rez) {
  6818.    printf("Failed index file create.  Err: %d\n",rez);
  6819.    return(rez);
  6820. }
  6821.  
  6822. // Open the index file (what we just created above).
  6823. // As with the index-create, the index-open requires the handle of the data
  6824. // file which this index file indexes.
  6825.  
  6826. OP.func = OPEN_INDEX_XB;
  6827. OP.filenamePtr = nameIX3;
  6828. OP.asMode = READWRITE | DENYNONE;
  6829. OP.xbLink = dataID;
  6830. rez = BULLET(&OP);
  6831. if (rez) {
  6832.    printf("Failed index file open.  Err: %d\n",rez);
  6833.    return(rez);
  6834. }
  6835. indexID = OP.handle;
  6836.  
  6837. // at this point, both the data and index files are open and accessible
  6838.  
  6839. // |
  6840. // | do work as required, then, when done, close them
  6841. // |
  6842.  
  6843. if (indexID) {
  6844.    HP.func = CLOSE_INDEX_XB;
  6845.    HP.handle = indexID;
  6846.    rez = BULLET(&HP);
  6847.    if (rez)
  6848.       printf("Failed index file close.  Err: %d\n",rez);
  6849. }
  6850.  
  6851. if (dataID) {
  6852.    HP.func = CLOSE_DATA_XB;
  6853.    HP.handle = dataID;
  6854.    rez = BULLET(&HP);
  6855.    if (rez)
  6856.       printf("Failed data file close.  Err: %d\n",rez);
  6857. }
  6858.  
  6859.  
  6860. ΓòÉΓòÉΓòÉ 10.12. Read Data and Index Header ΓòÉΓòÉΓòÉ
  6861.  
  6862. #include "bullet_2.h"
  6863.  
  6864. HANDLEPACK HP;                  // packs used here
  6865.  
  6866. // Since it is recommended that a full-lock be in force before reloading
  6867. // data or index headers, and since performing a full lock from BULLET does
  6868. // itself reload the header, this routine would not normally be used.
  6869. // However, if you are doing your own locking, then you need to call this.
  6870.  
  6871. rez = YourExternalControlLockRoutine;  // so you have your own locks...
  6872.  
  6873. if (rez==0) {
  6874.  
  6875.    if (youWantDataReload) {
  6876.       HP.func = READ_DATA_HEADER_XB;
  6877.       HP.func = dataID;
  6878.    }
  6879.    else {
  6880.       HP.func = READ_INDEX_HEADER_XB;
  6881.       HP.func = indexID;
  6882.    }
  6883.    rez = BULLET(&HP);
  6884.  
  6885.    // since locked, release lock regardless rez value
  6886.  
  6887.    rez2 = YourExternalControlUnlockRoutine;
  6888. }
  6889.  
  6890. // Be sure to read the explanation above
  6891.  
  6892.  
  6893. ΓòÉΓòÉΓòÉ 10.13. Flush Data and Index Header ΓòÉΓòÉΓòÉ
  6894.  
  6895. #include "bullet_2.h"
  6896.  
  6897. HANDLEPACK HP;                  // packs used here
  6898.  
  6899. // While BULLET automatically flushes data and index info whenever
  6900. // a BULLET file is unlocked from a full-lock (and if it
  6901. // is needed), you may to flush more frequently.
  6902.  
  6903. // It is recommended that you have a full-lock on the file before
  6904. // flushing.  In normal use, you would always have a full-lock when
  6905. // writing to a file (the only time you need to flush is if the file
  6906. // has been written to).  If you are, for whatever reason, keeping
  6907. // the file locked, and are updating it repeatedly, and have no
  6908. // intention of unlocking (and thereby flushing it) any time soon,
  6909. // you may do a manual flush to ensure that the disk image matches
  6910. // the memory image (in case the power goes out and you have no UPS)
  6911.  
  6912. if (youWantDataFlush) {
  6913.    HP.func = FLUSH_DATA_HEADER_XB;
  6914.    HP.func = dataID;
  6915. }
  6916. else {
  6917.    HP.func = FLUSH_INDEX_HEADER_XB;
  6918.    HP.func = indexID;
  6919. }
  6920. rez = BULLET(&HP);
  6921.  
  6922. // Only if the file has been changed does a flush actually write to disk.
  6923. // You should have an exclusive full-lock; a shared full-lock cannot be
  6924. // used since a shared lock does not allow writing to the file.
  6925.  
  6926.  
  6927. ΓòÉΓòÉΓòÉ 10.14. Copy (Add) a Subset of Records to a New File ΓòÉΓòÉΓòÉ
  6928.  
  6929. #include "bullet_2.h"
  6930.  
  6931. ACCESSPACK APo;                 // used when accessing original
  6932. ACCESSPACK APn;                 // used when accessing new
  6933. COPYPACK   CP;
  6934. HANDLEPACK HP;
  6935. OPENPACK   OP;                  // packs used here
  6936.  
  6937. RECTYPE ourRecord;              // as defined in Create example
  6938.  
  6939. // Assume an open, locked DBF data file, handle in dataID, and that you
  6940. // want to copy those records that are marked as deleted, one at a time, to a
  6941. // new file, prior to packing the database.  An extension to this procedure is
  6942. // to access each record in key order, and to copy each record to the new
  6943. // file, thereby giving a SORTED data file (also known as a clustered file).
  6944. // The lock must be a full-lock, but may be a shared full-lock.  A ZAP is done
  6945. // on the new file if the copy did not complete as expected.
  6946.  
  6947. // No indexed access is used in this example.
  6948.  
  6949. CP.func = COPY_DATA_HEADER_XB;
  6950. CP.handle = dataID;             // dataID is the original data file handle
  6951. CP.filenamePtr = "DELRECS.DBF"; // new file for deleted records
  6952. rez = BULLET(&CP);
  6953. if (rez) {
  6954.    printf("Failed header copy.  Err: %d\n",rez);
  6955.    return(rez);
  6956. }
  6957.  
  6958. // DELRECS.DBF now is exactly like the original, but has 0 records.  Since
  6959. // we're building this file, open it for exclusive use (DENYREADWRTE) --
  6960. // this is different from using the LOCK_XB routines since the lock is done
  6961. // at the file open level (no other process may even open it), whereas
  6962. // the LOCK_XB routines are at the access level (other processes may
  6963. // open the file, but may or may not access it).  You could instead use
  6964. // LOCK_DATA_XB for an exclusive full-lock, but this is simpler.
  6965.  
  6966. OP.func = OPEN_DATA_XB;
  6967. OP.filenamePtr = "DELRECS.DBF";         // open it
  6968. OP.asMode = READWRITE | DENYREADWRITE;  // for exclusive use
  6969. rez = BULLET(&OP);
  6970. if (rez) {
  6971.    printf("Failed new file open.  Err: %d\n",rez);
  6972.    return(rez);
  6973. }
  6974.  
  6975. // Now have two files open:  the original, with all the records, and
  6976. // the new, with no records.  The procedure here is to get all original
  6977. // records, check each for being deleted, if so, add that record (copy it)
  6978. // to the new file.  After copying, the original file is packed so that
  6979. // all deleted records are removed.  Then an index to it is reindexed.
  6980.  
  6981. // most BULLET pack members will be invariant within loops... (low overhead!)
  6982.  
  6983. APn.func = ADD_RECORD_XB;       // AccessPack for the new file
  6984. APn.handle = OP.handle;         // the handle just opened above
  6985. APn.recPtr = &ourRecord;
  6986.  
  6987. APo.func = GET_RECORD_XB;       // AccessPack for the original file
  6988. APo.handle = dataID;
  6989. APo.recNo = 1;                  // start at the first record
  6990. APo.recPtr = &ourRecord;        // read data into this
  6991. rez = BULLET(&APo);             // read the first data record
  6992.  
  6993. while (rez==0) {
  6994.  
  6995.    // check if this record is deleted, if so copy it else continue
  6996.  
  6997.    if (ourRecord.tag = '*') {
  6998.       rez = BULLET(&APn);       // add it to the new file
  6999.       if (rez) break;
  7000.       // here APn.recNo is the record number used for the just-added record
  7001.    }
  7002.  
  7003.    // This while() loop could have been a for() loop if we had used
  7004.    // STAT_DATA_XB to get the number of records, but in this example,
  7005.    // the original file is read until EXB_BAD_RECNO is returned,
  7006.    // indicating that the last record has been passed.
  7007.  
  7008.    APo.recNo++;                 // get next original record
  7009.    rez = BULLET(&APo);          // everything else is already setup
  7010. }
  7011.  
  7012. // the expected rez here is EXB_BAD_RECNO, any other then quit
  7013.  
  7014. if (rez != EXB_BAD_RECNO) {
  7015.     printf("Failed the while() loop, ZAPing new file.  Err: %d\n",rez);
  7016.  
  7017.     // As an example, if the copy failed to complete as expected, the
  7018.     // new file is ZAP'ed, removing any records that may have been copied
  7019.     // up to the point that the error occurred -- investigate failure and
  7020.     // restart process (you may want to just delete, rather than ZAP/CLOSE).
  7021.  
  7022.     HP.func = ZAP_DATA_HEADER_XB;
  7023.     HP.handle = APn.handle;     // the new file handle
  7024.     rez2 = BULLET(&HP);         // using 'rez2' to preserve initial error code
  7025.     if (rez2)
  7026.        printf("Failed ZAP!  Err: %d\n",rez2);
  7027.  
  7028.     HP.func = CLOSE_DATA_XB;    // close
  7029.     rez2 = BULLET(&HP);
  7030.     if (rez2)
  7031.        printf("Failed CLOSE!  Err: %d\n",rez2);
  7032.  
  7033.     return(rez);                // return initial error code
  7034. }
  7035.  
  7036. // done with the new file
  7037. // it contains all the records in the original marked as deleted
  7038.  
  7039. HP.func = CLOSE_DATA_XB;        // always use the correct pack for the routine
  7040. HP.handle = APn.handle;         // -- do not try to use AP when closing a file
  7041. rez = BULLET(&HP);
  7042. if (rez) return(rez);
  7043.  
  7044. // Pack the original data file, now that we've "saved" the delete-marked recs.
  7045. // Note: You may want to use BACKUP_XB before using this next routine
  7046. // in case a serious error occurs.  BULLET packs in place!  Also, before
  7047. // packing, memos belonging to records about to be deleted should have
  7048. // have been deleted before packing.  Neither is shown here.
  7049.  
  7050. APo.func = PACK_RECORDS_XB;
  7051. rez = BULLET(&APo);
  7052. if (rez==0) {
  7053.  
  7054.    // After a pack, you must reindex any related index files.
  7055.    // Assume here one index file, handle in indexID
  7056.  
  7057.    APo.func = REINDEX_XB;
  7058.    APo.handle = indexID;
  7059.    rez = BULLET(&APo);  // rez is not errc, but is pack # than failed
  7060.    if (rez)
  7061.       printf("Failed reindex.  Err: %d\n",APo.stat);
  7062. }
  7063. else {
  7064.    printf("Failed packed!  Recommend restore from backup.  Err: %d\n",rez);
  7065.    printf("Note: Unless error is known to not be severe.\n");
  7066. }
  7067.  
  7068. // Here and rez==0 then everything went as planned.
  7069.  
  7070. return(rez);
  7071.  
  7072.  
  7073. ΓòÉΓòÉΓòÉ 10.15. Zap Index File ΓòÉΓòÉΓòÉ
  7074.  
  7075. #include "bullet_2.h"
  7076.  
  7077. HANDLEPACK HP;                  // packs used here
  7078.  
  7079. // Since BULLET reindexes in place, there's typically no need to use
  7080. // ZAP to reduce disk requirements (there won't be two separate file
  7081. // spaces since the reindex copies right over the old key data).
  7082.  
  7083. HP.func = ZAP_INDEX_HEADER_XB;
  7084. HP.handle = indexToZap;
  7085. rez = BULLET(&HP);
  7086. if (rez) return(rez);
  7087.  
  7088.  
  7089. ΓòÉΓòÉΓòÉ 10.16. Get Descriptor, Using a DBF Not Created by Bullet ΓòÉΓòÉΓòÉ
  7090.  
  7091. #include <os2.h>
  7092. #include <stdio.h>
  7093. #include <stdlib.h>
  7094. #include <string.h>
  7095.  
  7096. #include "bullet_2.h"
  7097.  
  7098. int main(int argc,char *argv[]) {
  7099.  
  7100. INITPACK IP;
  7101. EXITPACK EP;
  7102. DESCRIPTORPACK DP;
  7103. OPENPACK OP;
  7104. HANDLEPACK HP;
  7105. STATDATAPACK SDP;
  7106. ACCESSPACK AP;          // packs used here
  7107.  
  7108. PFIELDDESCTYPE fieldDescPtr;    // pointer to field descriptor base allocation
  7109. PFIELDDESCTYPE fdPtr;           // roving pointer to any field's descriptor
  7110.  
  7111. CHAR dataRec[8192];     // 'unknown' record layout since reading "any" DBF
  7112. CHAR  fmt[32];          // printf() fmt string for on-the-fly formatting
  7113. ULONG dataID;           // handle of DBF
  7114. LONG  recNo;            // loop counter
  7115. BYTE  fldNo;            // loop counter2
  7116. int   rez;              // primary op return code
  7117. int   rez2;             // secondary op return code (so to perserve primary rc)
  7118.  
  7119. if (argc < 2) {
  7120.    puts("Use: C>progname anyfile.dbf");
  7121.    return(1);
  7122. }
  7123.  
  7124. // init Bullet
  7125.  
  7126. IP.func = INIT_XB;
  7127. IP.JFTsize = 20;                // 20 handles is all we need here
  7128. rez = BULLET(&IP);
  7129. if (rez!=0) {
  7130.    printf("INIT_XB failed: %d\n",rez);
  7131.    return(1);
  7132. }
  7133.  
  7134. // open existing DBF as named in command line
  7135.  
  7136. OP.func = OPEN_DATA_XB;
  7137. OP.filenamePtr = argv[1];
  7138. OP.asMode = READWRITE | DENYNONE;
  7139. rez = BULLET(&OP);
  7140. if (rez==0) {
  7141.  
  7142.    dataID = OP.handle;
  7143.  
  7144.    SDP.func = STAT_DATA_XB;
  7145.    SDP.handle = dataID;
  7146.    rez = BULLET(&SDP);
  7147.    if (rez==0) {
  7148.  
  7149.       // allocate field descriptors needed (SDP.fields is number needed)
  7150.       // calloc() used since 0-filled storage is required
  7151.  
  7152.       fieldDescPtr = calloc(SDP.fields,sizeof(FIELDDESCTYPE));
  7153.  
  7154.       if (fieldDescPtr != NULL) {
  7155.  
  7156.          fdPtr = fieldDescPtr;          // fdPtr->each descriptor
  7157.  
  7158.          // read each field descriptor from Bullet, storing to our program
  7159.          // show each for display
  7160.  
  7161.          //      1234567890-123456789-123456789-12345
  7162.          printf("FLD#   FIELDNAME  T  LEN.DEC  OFFSET\n");
  7163.  
  7164.          DP.func = GET_DESCRIPTOR_XB;
  7165.          DP.handle = dataID;
  7166.          for (fldNo=1;fldNo <= SDP.fields;fldNo++) {
  7167.  
  7168.             DP.fieldNumber = fldNo;
  7169.             rez = BULLET(&DP);
  7170.             if (rez==0) {
  7171.  
  7172.                strcpy(fdPtr->fieldName, DP.FD.fieldName);
  7173.                fdPtr->fieldType = DP.FD.fieldType;
  7174.                fdPtr->fieldLen = DP.FD.fieldLen;
  7175.                fdPtr->fieldDC = DP.FD.fieldDC;
  7176.                fdPtr->fieldDA = DP.fieldOffset;
  7177.                printf("%3u   %-10s  %c  %3u.%1u     %4u\n",
  7178.                   fldNo,
  7179.                   fdPtr->fieldName,
  7180.                   fdPtr->fieldType,
  7181.                   (ULONG) fdPtr->fieldLen,
  7182.                   (ULONG) fdPtr->fieldDC,
  7183.                   fdPtr->fieldDA);
  7184.                fdPtr++;                 // next field descriptor
  7185.             }
  7186.             else
  7187.                break;
  7188.          }
  7189.  
  7190.          // An interesting item above is where fdPtr->fieldDA is set to
  7191.          // DP.fieldOffset.  fieldDA is a run-time storage area that in
  7192.          // dBASE is used to directly access the field (DA="direct access").
  7193.          // It has no meaning except for that particular run (it is a memory
  7194.          // address). In this program example I use it to store the offset
  7195.          // of the field, relative the start of the record buffer (where the
  7196.          // tag byte = offset 0).  You could just as easily use some of the
  7197.          // 12 reserved bytes left over in the descriptor, as I do for the
  7198.          // alternate field length.  But, since fieldDA is already there, and
  7199.          // not used otherwise, it makes sense to use it.
  7200.  
  7201.          // Now have all we need to know about the DBF fields, having just
  7202.          // read and stored the field descriptors.  For this example, we
  7203.          // grab the first nine records and spit them out, by field, in record
  7204.          // number order (no indexing used).
  7205.  
  7206.          if (SDP.records != 0) {
  7207.  
  7208.             AP.func = GET_RECORD_XB;
  7209.             AP.handle = dataID;
  7210.             AP.recPtr = &dataRec;
  7211.  
  7212.             for (recNo=1;recNo <= 9; recNo++) {
  7213.  
  7214.                printf("\nrecNo %u: ",recNo);    // show line number
  7215.                AP.recNo = recNo;                // get this record #...
  7216.                rez = BULLET(&AP);               // ...to dataRec buffer
  7217.                if (rez==0) {
  7218.                   printf("%.1s ",(CHAR *) dataRec); // show if deleted or not
  7219.  
  7220.                   fdPtr = fieldDescPtr; // fdPtr->first field descriptor
  7221.  
  7222.                   for (fldNo=1;fldNo <= SDP.fields;fldNo++) {
  7223.  
  7224.                      // No special formatting is done on this output for this
  7225.                      // example -- since standard DBF data is always in pure
  7226.                      // ASCII form, all is printable.
  7227.  
  7228.                      switch (fdPtr->fieldType) {
  7229.                      case 'C':  // text
  7230.                      case 'D':  // date, show as-is
  7231.                      case 'L':  // logical, show as-is
  7232.                      case 'M':  // memo field (block number in ASCII)
  7233.                      case 'N':  // numeric (ASCII)
  7234.  
  7235.                         // make fmt[] string like this: "%xx.xxs"
  7236.                         // where xx is field length for this field
  7237.  
  7238.                         sprintf(fmt,"%%-%i.%is ",
  7239.                                 fdPtr->fieldLen,
  7240.                                 fdPtr->fieldLen);
  7241.  
  7242.                         // fdPtr->fieldDA=offset of the field within the record
  7243.                         // so it plus dataRec (buffer base) results in the
  7244.                         // offset of the current field we are processing
  7245.  
  7246.                         printf(fmt,dataRec+fdPtr->fieldDA);
  7247.                         break;
  7248.                      default:
  7249.                         printf("\nUnknown field type: %c\n",fdPtr->fieldType);
  7250.                      } // switch
  7251.  
  7252.                      fdPtr += 1;                  // next field's descriptor
  7253.  
  7254.                   } // for fields
  7255.                } // if record read
  7256.  
  7257.                else {
  7258.                   if (rez==EXB_BAD_RECNO)       // if < for-count records in DBF
  7259.                      rez=0;                     // then would get this error
  7260.                   else
  7261.                      printf("Failed GET_RECORD_XB, err: %d\n",rez);
  7262.                   break;                        // break for any ELSE case
  7263.                }
  7264.  
  7265.             } // for records
  7266.             if (rez==0) printf("\nDone.\n"); // all FOR recs done
  7267.          }
  7268.          else
  7269.             printf("No records in file\n");
  7270.  
  7271.          free(fieldDescPtr);
  7272.       }
  7273.       else
  7274.          printf("calloc failed!\n");
  7275.    }
  7276.    else
  7277.       printf("STAT_DATA_XB failed: %d\n",rez);
  7278.  
  7279.    HP.func = CLOSE_DATA_XB;
  7280.    HP.handle = dataID;
  7281.    rez2 = BULLET(&HP);
  7282. }
  7283. else
  7284.    printf("OPEN_DATA_XB failed: %d\n",rez);
  7285.  
  7286. EP.func = EXIT_XB;
  7287. rez2=BULLET(&EP);
  7288.  
  7289. printf("\nPress ENTER to exit");
  7290. getchar();
  7291. if (rez==0) rez=rez2;  // rez is more important, but if 0 use rez2 result
  7292. return(rez);
  7293. }
  7294.  
  7295. The above is a complete program.  Running it against a sample DBF results in 
  7296. the following output: 
  7297.  
  7298. FLD#   FIELDNAME  T  LEN.DEC  OFFSET
  7299.   1   SSN         C    9.0        1
  7300.   2   LNAME       C   16.0       10
  7301.   3   FNAME       C   16.0       26
  7302.   4   HIRED       D    8.0       42
  7303.   5   DEPT_ID     C    6.0       50
  7304.  
  7305. recNo 1:   465309999 Que              Barbie           19900131 BOSS
  7306. recNo 2:   445038888 Stewart          Jackie           19910228 ACC
  7307. recNo 3:   760443232 Whitman          Kelly            19920414 HUM
  7308. recNo 4:   845309944 Beatty           Leslie           19940122 PRG
  7309. recNo 5:   555033388 Jasper           Amy              19930230 PRG
  7310. recNo 6:   430443222 Hauntos          Poco             19920414 PRG
  7311. recNo 7:   365502949 Hopkins          Lisa             19910121 PRG
  7312. recNo 8:   685733868 Leonard          Rosina           19850218 PRG
  7313. recNo 9:   500945242 Morton           Holly            19950406 PHY
  7314. Done.
  7315.  
  7316. Press ENTER to exit
  7317.  
  7318.  
  7319. ΓòÉΓòÉΓòÉ 10.17. Update a Data Record ΓòÉΓòÉΓòÉ
  7320.  
  7321. #include "bullet_2.h"
  7322.  
  7323. ACCESSPACK AP;                  // packs used here
  7324.  
  7325. typedef struct _RECTYPE {
  7326.  CHAR tag;              // record tag, init to SPACE, '*' means deleted
  7327.  CHAR userSSN[9];       // first field in DBF (not 0-terminated in this case)
  7328.  CHAR userScore[6];     // second field (also not 0T)
  7329. } RECTYPE;              // (total record length is 16 bytes)
  7330. RECTYPE ourRecord;
  7331.  
  7332. // This excerpt demonstrates how to update (change) a record.
  7333. // The idea is to get the current contents of a record (by record
  7334. // number since no index is used for this update routine), change
  7335. // what needs changing, then write it back.  Under no circumstances
  7336. // should you change any field that is used as a key by an index, or
  7337. // as a foreign key, or in any other way removes the referential
  7338. // integrity of the database.  If you need to change a key field, then
  7339. // you must use UPDATE_XB.
  7340.  
  7341. AP.func = GET_RECORD_XB;
  7342. AP.handle = dataID;
  7343. AP.recNo = recordToUpdate;
  7344. AP.recPtr = &ourRecord;
  7345. rez = BULLET(&AP);
  7346. if (rez) return(rez);
  7347.  
  7348. // ourRecord has data stored at record number recordToUpdate -- since
  7349. // userScore is not used as a key field, this routine may be used to
  7350. // modify the contents of that field.  Since numbers are stored as ASCII
  7351. // text in compatible DBF files, must convert to binary, perform needed
  7352. // math, then convert back to text:
  7353.  
  7354. //t = atol(ourRecord.userScore);  // use scanf() since not 0T
  7355. sscanf(t,"%6u",&ourRecord.userScore);
  7356. t = t + ClassCurve;             // increase each score by curve value
  7357. sprintf(ourRecord.userScore,"%6.6u",t)
  7358.  
  7359. AP.func = UPDATE_RECORD_XB;     // other AP values already set up from GET
  7360. rez = BULLET(&AP);              // write out the record with the new score
  7361. if (rez) return(rez);
  7362.  
  7363.  
  7364. ΓòÉΓòÉΓòÉ 10.18. Delete, Undelete, 'Debump' a Data Record ΓòÉΓòÉΓòÉ
  7365.  
  7366. #include "bullet_2.h"
  7367.  
  7368. ACCESSPACK AP;                  // packs used here
  7369.  
  7370. // delete or undelete or remove (debump)
  7371.  
  7372. switch(*requestMsg) {
  7373. case 'delete':
  7374.    AP.func = DELETE_RECORD_XB;  // places a '*' in .tag byte
  7375.    break;
  7376. case 'undelete':
  7377.    AP.func = UNDELETE_RECORD_XB; // places a SPACE in .tag byte
  7378.    break;
  7379. case 'debump':
  7380.    AP.func = DEBUMP_RECORD_XB;  // physically removes record from file, but
  7381.    break;                       // fails if AP.recNo is not last record number
  7382. }
  7383.  
  7384. AP.handle = dataID;             // same for all three
  7385. AP.recNo = recordNumber;
  7386. rez = BULLET(&AP);
  7387. return(rez);
  7388.  
  7389.  
  7390. ΓòÉΓòÉΓòÉ 10.19. Reading Memo Records ΓòÉΓòÉΓòÉ
  7391.  
  7392. #include "bullet_2.h"
  7393.  
  7394. ACCESSPACK AP;
  7395. LOCKPACK LP;
  7396. MEMODATAPACK MDP;       // packs used here
  7397.  
  7398. typedef struct _RECTYPE {
  7399.  CHAR tag;              // record tag, init to SPACE, '*' means deleted
  7400.  CHAR userSSN[9];       // first field in DBF (not 0-terminated in this case)
  7401.  CHAR userMemo[10];     // second field (also not 0T), is memo field type
  7402. } RECTYPE;
  7403. RECTYPE someRecord;
  7404.  
  7405. // a minimum shared record lock is required on the DBF record that owns
  7406. // memoNumber in this example, since only reading of the single memo is done
  7407.  
  7408. LP.func = LOCK_DATA_XB;
  7409. LP.handle = dataID;
  7410. LP.dlMode = LOCK_SHARED;        // only reading here, shared record lock is okay
  7411. LP.recStart = recordToGet;      // lock this record
  7412. LP.recCount = 1;                // and only this record
  7413. rez = BULLET(&LP);
  7414. if (rez) return(rez);
  7415.  
  7416. // hereafter, must unlock before exiting
  7417.  
  7418. AP.func = GET_RECORD_XB;
  7419. AP.handle = dataID;
  7420. AP.recNo = recordToGet;
  7421. AP.recPtr = &someRecord;        // load someRecord with data on disk
  7422. rez = BULLET(&AP);
  7423. if (rez) return(rez);           // UNLOCK! before doing this exit
  7424.  
  7425. // memoNumber = atol(someRecord.userMemo); // the memo number (0 if none)
  7426. sscanf(memoNumber,"%10u",&someRecord.userMemo);
  7427.  
  7428. // this code reads the number of data bytes in the memo and allocates a
  7429. // run-time buffer to read that memo into
  7430.  
  7431. MDP.func = GET_MEMO_SIZE_XB;
  7432. MDP.dbfHandle = dataID;         // handle of the DBF this memo belongs to
  7433. MDP.memoNo = memoNumber;        // memo number to get size of (1=first)
  7434. rez = BULLET(&MDP);             // (returns an error if memoNumber is 0)
  7435. if (rez==0) {
  7436.  
  7437.    // BULLET does maintain 0-sized memo records, so you may want to check
  7438.    // if MDP.memoBytes from GET_MEMO_SIZE_XB is 0, and skip processing if so
  7439.  
  7440.    memoBytesToRead = MDP.memoBytes;         // since overwritten by next call
  7441.    memoBufferPtr = malloc(memoBytesToRead); // assuming you want it all at once
  7442.  
  7443.    if (memoBufferPtr) {
  7444.  
  7445.       MDP.func = GET_MEMO_XB;
  7446.       MDP.dbfHandle = dataID;      // same as before, as is .memoNo
  7447.       MDP.memoNo = memoNumber;
  7448.       MDP.memoPtr = memoBufferPtr; // memo disk data is loaded into this buffer
  7449.       MDP.memoOffset = 0;          // read from very first memo data byte
  7450.       MDP.memoBytes = memoBytesToRead;
  7451.  
  7452.       // MDP.memoBytes is already set to the total data size -- you may read
  7453.       // fewer bytes, and you may use .offset to move through the memo data
  7454.       // chunks at a time, rather than all at once -- the above simply reloads
  7455.       // it with the same count, since here memoBytesToRead==MDP.memoBytes
  7456.  
  7457.       rez = BULLET(&MDP);          // returns with MDP.memoBytes= bytes read
  7458.       if (rez==0) {
  7459.  
  7460.          // if (MDP.memoBytes != memoBytesToRead)
  7461.          //   printf("Could not read all bytes requested - probably at end of memo\n");
  7462.          // above would not happen in this case since the exact size was requested
  7463.  
  7464.          // process as required (here passes buffer ptr and bytes actually read)
  7465.  
  7466.          DoWhatYouWillWithThisMemoData(memoBufferPtr,MDP.memoBytes);
  7467.       }
  7468.       free(memoBufferPtr);
  7469.    }
  7470.    else rez=8;  // malloc failed, return 8=not enough memory
  7471. }
  7472.  
  7473. LP.func = UNLOCK_DATA_XB;
  7474. LP.handle = dataID;
  7475. LP.recStart = recordToGet;      // unlock this record
  7476. LP.recCount = 1;                // and only this record
  7477. rez2 = BULLET(&LP);
  7478.  
  7479. if (rez==0) rez=rez2;
  7480. return(rez);
  7481.  
  7482.  
  7483. ΓòÉΓòÉΓòÉ 10.20. Add, Update, Delete a Memo Record ΓòÉΓòÉΓòÉ
  7484.  
  7485. #include "bullet_2.h"
  7486.  
  7487. ACCESSPACK AP;
  7488. LOCKPACK LP;
  7489. MEMODATAPACK MDP;       // packs used here
  7490.  
  7491. typedef struct _RECTYPE {
  7492.  CHAR tag;
  7493.  CHAR userSSN[9];
  7494.  CHAR userMemo[10];
  7495. } RECTYPE;
  7496. RECTYPE someRecord;
  7497.  
  7498. // an exclusive lock is required on the DBF file that owns this memo file
  7499. // since writing is done to the memo file (memo file header, especially)
  7500.  
  7501. LP.func = LOCK_DATA_XB;
  7502. LP.handle = dataID;
  7503. LP.dlMode = LOCK_EXCLUSIVE;     // writing here, exclusive full lock required
  7504. LP.recStart = 0;                // lock all
  7505. rez = BULLET(&LP);
  7506. if (rez) return(rez);
  7507.  
  7508. // hereafter, must unlock before exiting
  7509.  
  7510. AP.func = GET_RECORD_XB;
  7511. AP.handle = dataID;
  7512. AP.recNo = recordToGet;
  7513. AP.recPtr = &someRecord;        // load someRecord with data on disk
  7514. rez = BULLET(&AP);
  7515. if (rez) return(rez);           // UNLOCK! before doing this exit
  7516.  
  7517. // memoNumber = atol(someRecord.userMemo); // the memo number (0 if none)
  7518. sscanf(memoNumber,"%10u",&someRecord.userMemo);
  7519.  
  7520. // if there is no current memo, this example adds one
  7521. // if there is, this example updates it by:
  7522. //   - changing the first 16 bytes to "Kilroy was here."
  7523. //   - adding the text "Was updated." to the end of the current memo data
  7524. // the example then deletes the memo
  7525.  
  7526. CHAR kilroyStr[] = "Kilroy was here.";
  7527. CHAR updateStr[] = "Was updated.";
  7528. CHAR newStr[] = "New.";
  7529.  
  7530. if (memoNumber) {
  7531.  
  7532.    // modify the current memo -- first, the text "Kilroy was here." is
  7533.    // placed at the start of the memo, then the memo size is gotten (in
  7534.    // case the original memo size were less than the size of "Kilroy...").
  7535.  
  7536.    MDP.func = UPDATE_MEMO_XB;
  7537.    MDP.dbfHandle = dataID;      // handle of the DBF this memo belongs to
  7538.    MDP.memoNo = memoNumber;     // memo number to update (1=first)
  7539.    MDP.memoPtr = kilroyStr;     // data to write
  7540.    MDP.memoOffset = 0;          // start write at first byte of memo
  7541.    MDP.memoBytes = strlen(kilroyStr); // bytes to write
  7542.    rez = BULLET(&MDP);
  7543.  
  7544.       // the first 16 bytes of the memo now say kilroyStr (overwrote what was there)
  7545.       // the memo size changes only if the original memo was < 16 bytes, in
  7546.       // which case the size is now 16
  7547.  
  7548.    if (rez==0) {
  7549.       printf("%s overwrote first 16 bytes\n",kilroyStr);
  7550.  
  7551.       // must check if the update resulted in a new memo block being
  7552.       // used (if the update required more allocation blocks), and if
  7553.       // so must update the DBF field storing the memo number
  7554.       // MDP.memoNo is the memo number returned, memoNumber the original number
  7555.  
  7556.       // IT CAN BE ASSUMED IN THIS PARTICULAR example that this will never
  7557.       // be needed since the update modified the first 16 bytes of the
  7558.       // memo only, and so would never have required any more blocks --
  7559.       // however, unless you know before-hand that the update will not need
  7560.       // more allocation blocks (not difficult, if you know the block size,
  7561.       // overhead bytes (8), and your offset and bytes to write), it should
  7562.       // be checked -- hint: as with all Bullet routines, the idea is to
  7563.       // wrap up these separate operations into nice, neat callable routines
  7564.       // that take care of your particular need; there are no doubt 1000s of
  7565.       // variations -- pick one you can deal with.
  7566.  
  7567.       if (MDP.memoNo != memoNumber) {
  7568.          sprintf(someRecord.userMemo,"%10.10u",MDP.memoNo)
  7569.          memoNumber = MDP.memoNo;       // set original to new for next update
  7570.  
  7571.          // rather than updating here, and possibly again below, you
  7572.          // may elect to set a flag and then do the DBF update at the end, once
  7573.          // again -- this updates the _data_ record, in the DBF file:
  7574.  
  7575.          AP.func = UPDATE_RECORD_XB;    // this updates the data record only
  7576.          AP.handle = dataID;
  7577.          AP.recNo = recordToGet;
  7578.          AP.recPtr = &someRecord;       // write the new data
  7579.          rez = BULLET(&AP);
  7580.          if (rez) return(rez);          // UNLOCK! before doing this exit
  7581.       }
  7582.  
  7583.       MDP.func = GET_MEMO_SIZE_XB;  // other MDP members already set above
  7584.       rez = BULLET(&MDP);
  7585.       if (rez=0) {              // size of memo is at least 16 (from kilroyStr)
  7586.                                 // but may be bigger if was bigger before
  7587.          MDP.func = UPDATE_MEMO_XB;
  7588.          MDP.memoPtr = updateStr;
  7589.  
  7590.          // the current memo size is used as the offset for the appending of
  7591.          // the updateStr bytes (offset is 0-based, so using MDP.memoBytes
  7592.          // results in the offset being the current size + 1)
  7593.  
  7594.          MDP.memoOffset = MDP.memoBytes;
  7595.          MDP.memoBytes = strlen(updateStr);
  7596.          rez = BULLET(&MDP);   // returns with MDP.memoNo
  7597.                                // bytes written==MDP.memoBytes always
  7598.          if (rez==0)
  7599.  
  7600.             printf("%s appended to memo\n",updateStr);
  7601.             // _minimum_ memo contents now is kilroyStr plus updateStr
  7602.             // more if original memo was > 16 bytes
  7603.  
  7604.             if (MDP.memoNo != memoNumber) {
  7605.                sprintf(someRecord.userMemo,"%10.10u",MDP.memoNo)
  7606.                AP.func = UPDATE_RECORD_XB;         //
  7607.                AP.handle = dataID;                 // as explained above
  7608.                AP.recNo = recordToGet;             //
  7609.                AP.recPtr = &someRecord;
  7610.                rez = BULLET(&AP);
  7611.                if (rez) return(rez);    // actually must unlock before exit!
  7612.             }
  7613.          } // memo update #2 failed
  7614.       } // memo size failed
  7615.    }
  7616.    else
  7617.       printf("update failed, err: %d\n",rez); // disk full probably
  7618. }
  7619. else {
  7620.  
  7621.    // no current memo, add one
  7622.  
  7623.    MDP.func = ADD_MEMO_XB;
  7624.    MDP.dbfHandle = dataID;      // handle of the DBF this memo belongs to
  7625.    MDP.memoPtr = newStr;        // data to write
  7626.    MDP.memoBytes = strlen(newStr); // bytes to write
  7627.    rez = BULLET(&MDP);
  7628.    if (rez==0) {
  7629.       sprintf(someRecord.userMemo,"%10.10u",MDP.memoNo)
  7630.       AP.func = UPDATE_RECORD_XB;         //
  7631.       AP.handle = dataID;                 // as explained above
  7632.       AP.recNo = recordToGet;             //
  7633.       AP.recPtr = &someRecord;
  7634.       rez = BULLET(&AP);
  7635.    }
  7636.    else
  7637.      printf("add failed, err: %d\n",rez);
  7638. }
  7639.  
  7640. // delete the memo just operated on
  7641.  
  7642. if (rez==0) {
  7643.  
  7644.    MDP.func = DELETE_MEMO_XB;
  7645.    // MDP.memoNo is already set
  7646.    rez = BULLET(&MDP);
  7647.    if (rez==0) {
  7648.       strcpy(someRecord.userMemo,"          ");// DBF memo field to all spaces
  7649.       AP.func = UPDATE_RECORD_XB;         //
  7650.       AP.handle = dataID;                 // as explained above
  7651.       AP.recNo = recordToGet;             //
  7652.       AP.recPtr = &someRecord;
  7653.       rez = BULLET(&AP);
  7654.    }
  7655.    else
  7656.      printf("delete failed, err: %d\n",rez);
  7657. }
  7658.  
  7659. LP.func = UNLOCK_DATA_XB;
  7660. LP.handle = dataID;
  7661. LP.recStart = 0;                // unlock all
  7662. rez2 = BULLET(&LP);
  7663.  
  7664. if (rez==0) rez=rez2;
  7665. return(rez);
  7666.  
  7667.  
  7668. ΓòÉΓòÉΓòÉ 10.21. Memo Bypass (Memo Create, Open, Close, Read/Flush Header) ΓòÉΓòÉΓòÉ
  7669.  
  7670. #include "bullet_2.h"
  7671.  
  7672. MEMODATAPACK MDP;               // packs used here
  7673.  
  7674.  
  7675. //  These five routines are normally performed automatically, as described
  7676. //  in the main documentation, and seldom would need to be called directly.
  7677.  
  7678.  
  7679. MDP.func = MEMO_BYPASS_XB;
  7680. MDP.dbfHandle = dataID;         // handle of DBF
  7681. MDP.memoBypass = BypassRoutineToDo;
  7682.  
  7683. // where BypassRoutineToDo is one of the following:
  7684. //
  7685. //   BYPASS_CREATE_MEMO
  7686. //   BYPASS_OPEN_MEMO
  7687. //   BYPASS_CLOSE_MEMO
  7688. //   BYPASS_READ_MEMO_HEADER
  7689. //   BYPASS_FLUSH_MEMO_HEADER
  7690.  
  7691. rez = BULLET(&MDP);
  7692. if (rez) return(rez);
  7693.  
  7694.  
  7695. ΓòÉΓòÉΓòÉ 10.22. Key Access Without Data Record Read ΓòÉΓòÉΓòÉ
  7696.  
  7697. #include "bullet_2.h"
  7698.  
  7699. CHAR keyBuffer[64];             // enough for the largest possible key
  7700.  
  7701. ACCESSPACK AP;                  // packs used here
  7702.  
  7703. // -----------------------------------------------------------
  7704. // this section starts at the first in-order key and reads all
  7705. // keys in the index file in order
  7706.  
  7707. AP.func = FIRST_KEY_XB;
  7708. AP.handle = indexID;
  7709. AP.keyPtr = keyBuffer;
  7710. rez = BULLET(&AP);
  7711. while (rez==0) {
  7712.  
  7713.    // show first 8 bytes of key, and the record number the key is for
  7714.    printf("%8.8s  %9.9lu\r", keyBuffer, AP.recNo);
  7715.  
  7716.    AP.func = NEXT_KEY_XB;       // and get the next key...
  7717.    rez = BULLET(&AP);           // until all keys accessed
  7718. };
  7719. if (rez!=EXB_END_OF_FILE) return(rez); // expected rez is EXB_END_OF_FILE
  7720.  
  7721.  
  7722. // ----------------------------------------------------------
  7723. // this section starts at the last in-order key and reads all
  7724. // keys in the index file in reverse order
  7725.  
  7726. AP.func = LAST_KEY_XB;
  7727. AP.handle = indexID;
  7728. AP.keyPtr = keyBuffer;
  7729. rez = BULLET(&AP);
  7730. while (rez==0) {
  7731.  
  7732.    // show first 8 bytes of key, and the record number the key is for
  7733.    printf("%8.8s  %9.9lu\r", keyBuffer, AP.recNo);
  7734.  
  7735.    AP.func = PREV_KEY_XB;       // and get the previous key...
  7736.    rez = BULLET(&AP);           // until all keys accessed
  7737. };
  7738. if (rez!=EXB_TOP_OF_FILE) return(rez); // expected rez is EXB_TOP_OF_FILE
  7739.  
  7740. // this section starts at the first key that starts with "SM", or if
  7741. // no keys do, the first key after "SM", and gets that key
  7742. // keys in the index file in reverse order
  7743.  
  7744. memset(keyBuffer,0,64);         // ensure remaining bytes are \0 (required)
  7745. strcpy(keyBuffer,"SM");         // get key of "SM", if present (not likely)
  7746. AP.func = EQUAL_KEY_XB;
  7747. AP.handle = indexID;
  7748. AP.keyPtr = keyBuffer;
  7749. rez = BULLET(&AP);
  7750.  
  7751. // since "SM" is only being used as a partial key search criterion,
  7752. // it won't be found (though, of course, it is a valid key) in this
  7753. // example -- however, by using NEXT_KEY_XB, the next key is accessed,
  7754. // say, for example, "SMITH"...  It could also be "TIMBU" if there were
  7755. // no keys with values greater than "SM" and less than "TIMBU".
  7756.  
  7757. // Be aware that if another thread in this process (repeat:
  7758. // this process!) is accessing this index file (it's not likely that
  7759. // you will write your could so that this would happen), then you
  7760. // can no longer rely on any multi-call key access to be an atomic
  7761. // operation.  If you need to have more than one thread access the
  7762. // same index (and you require NEXT_KEY_XB or PREV_KEY_XB), then you
  7763. // must semaphore protect your code so you don't try to access the same
  7764. // index file.
  7765.  
  7766. if (rez!=EXB_KEY_NOT_FOUND) {   // expected
  7767.    AP.func = NEXT_KEY_XB;       // so get the first key after "SM"
  7768.    AP.handle = indexID;
  7769.    AP.keyPtr = keyBuffer;
  7770.    rez = BULLET(&AP);
  7771.    if (rez==0)
  7772.       printf("The first key >= SM is %s\n",AP.keyBuffer);
  7773. }
  7774.  
  7775.  
  7776. ΓòÉΓòÉΓòÉ 10.23. Building and Storing Raw Key ΓòÉΓòÉΓòÉ
  7777.  
  7778. #include "bullet_2.h"
  7779.  
  7780. ACCESSPACK AP;                  // packs used here
  7781.  
  7782. // this example shows a simple database insert process
  7783. // INSERT_XB should be used instead since it does all this and then some
  7784. // files should be locked (not shown)
  7785.  
  7786. AP.func = ADD_RECORD_XB;
  7787. AP.handle = dataID;
  7788. AP.recPtr = &yourRecord;
  7789. rez = BULLET(&AP);
  7790. if (rez) return(rez);
  7791.  
  7792. // AP.recNo is returned by Bullet and will be used later
  7793.  
  7794. AP.func = BUILD_KEY_XB;
  7795. AP.handle = indexID;
  7796. AP.recPtr = &yourRecord;
  7797. AP.keyPtr = keyBuffer;          // CHAR keyBuffer[64];
  7798. rez = BULLET(&AP);
  7799. if (rez) return(rez);
  7800.  
  7801. // keyBuffer filled with key to store
  7802. // a \0\0 enumerator is attached if the index file was created with DUPS_ALLOWED
  7803.  
  7804. AP.func = STORE_KEY_XB;
  7805. AP.handle = indexID;
  7806. // AP.recNo is already set from the ADD_RECORD_XB call
  7807. AP.keyPtr = keyBuffer;
  7808. rez = BULLET(&AP);
  7809. if (rez) return(rez);
  7810.  
  7811. // if no error, the key was inserted in the index file and the record
  7812. // number was associated with that key -- next time you access that
  7813. // key, the record number is returned (along with the key itself) --
  7814. // and with that record number you access the data file
  7815.  
  7816. // when using this routine, you must check the error for a EXB_KEY_EXISTS
  7817. // error and if DUPS_ALLOWED, you must manage your own enumerator --
  7818. // INSERT_XB is the only routine that does this automatically so unless
  7819. // you have a real desire to manage this yourself (among other things)
  7820. // use INSERT_XB instead of all this.
  7821.  
  7822.  
  7823. ΓòÉΓòÉΓòÉ 10.24. Getting Current Key, Key for Rec/RecNo Pair, Deleting Key ΓòÉΓòÉΓòÉ
  7824.  
  7825. #include "bullet_2.h"
  7826.  
  7827. ACCESSPACK AP;
  7828. LOCKPACK LP;                    // packs used here
  7829.  
  7830. AP.func = GET_CURRENT_KEY_XB;
  7831. AP.handle = indexID;
  7832. AP.keyPtr = keyBuffer;          // current key placed here by Bullet
  7833. rez = BULLET(&AP);
  7834. if (rez) return(rez);
  7835. printf("The last accessed key for indexID is in keyBuffer, including any enumerator\n");
  7836.  
  7837. // This next example assumes that you are maintaining your own method of
  7838. // transaction rollback, and are about to delete the last item added to the
  7839. // database: in this case, the last data record is removed from the one
  7840. // DBF data file, and the key for that record is removed from the index file
  7841. // -- normally, you wouldn't do this, but if you have the need...
  7842.  
  7843. rez = InsertToDatabaseHoweverYouDoIt(yourPtr);
  7844.  
  7845. // Assume the above called succeeded, but you've decided, for whatever reason,
  7846. // that you want to backout the insert... normally, if you used INSERT_XB to
  7847. // insert a record/key into a database, you'd already have the record number
  7848. // used AND the key used for the record (for each if more than one) -- but for
  7849. // this example, assume that only the record number is known.  Steps done
  7850. // to remove the record and key for this would be:
  7851. //
  7852. //  1. Exclusive full-lock files
  7853. //  2. Get the record data at the record number (GET_RECORD_XB)
  7854. //  3. Get the key for this record/recNo pair (GET_KEY_FOR_RECORD_XB)
  7855. //  4. Delete the key (DELETE_KEY_XB) (delete the key before deleting the record)
  7856. //  5. Delete the record data (DEBUMP_RECORD_XB) (record must be last record in file)
  7857. //  6. Unlock files
  7858.  
  7859. // lock files being processed
  7860.  
  7861. LP.func = LOCK_XB;
  7862. LP.handle = indexID;            // also locks indexID's owner (its DBF)
  7863. LP.xlMode = LOCK_EXCLUSIVE;     // exclusive lock for index
  7864. LP.dlMode = LOCK_EXCLUSIVE;     // exclusive lock for data
  7865. LP.nextPtr = NULL;              // only one pack
  7866. rez = BULLET(&LP);
  7867. if (rez) return(LP.stat);       // rez for transaction-list is NOT the error
  7868.  
  7869. // get actual data record for record number
  7870.  
  7871. AP.func = GET_RECORD_XB;
  7872. AP.handle = dataID;
  7873. AP.recNo = recNoToDelete;       // get this record to...
  7874. AP.recPtr = &yourRecord;        // ...this data record buffer (a structure var)
  7875. rez = BULLET(&AP);
  7876. if (rez) goto MustAlwaysUnlock;
  7877.  
  7878. // now have the data record/data number pair --
  7879. // it must be the last physical record in the data file -- if this cannot
  7880. // be known, use STAT_DATA_XB to verify that recNoToDelete==SDP.records
  7881.  
  7882. AP.func = GET_KEY_FOR_RECORD_XB;
  7883. AP.handle = indexID;
  7884. AP.recNo = recNoToDelete;
  7885. AP.recPtr = &yourRecord;
  7886. AP.keyPtr = keyBuffer;          // CHAR keyBuffer[64]; key returned here
  7887. rez = BULLET(&AP);
  7888. if (rez) goto MustAlwaysUnlock;
  7889.  
  7890. // now have the key (with any attached enumerator) in keyBuffer, delete it
  7891.  
  7892. AP.func = DELETE_KEY_XB;
  7893. AP.handle = indexID;
  7894. AP.keyPtr = keyBuffer;          // was set with key by GET_KEY_FOR_RECORD_XB
  7895. rez = BULLET(&AP);
  7896. if (rez) goto MustAlwaysUnlock;
  7897.  
  7898. // and delete (physically remove) the data record
  7899.  
  7900. AP.func = DEBUMP_RECORD_XB;
  7901. AP.handle = dataID;
  7902. AP.recNo = recNoToDelete;
  7903. rez = BULLET(&AP);
  7904. // if (rez) goto MustAlwaysUnlock;
  7905.  
  7906. MustAlwaysUnlock:   // unlock ALWAYS required if lock succeeded
  7907.  
  7908. LP.func = UNLOCK_XB;
  7909. LP.handle = indexID;
  7910. LP.nextPtr = NULL;
  7911. rez2 = BULLET(&LP);
  7912. if (rez2) rez2=LP.stat; // rez for transaction-list is NOT the error
  7913.  
  7914. if (rez==0) rez=rez2;
  7915. return(rez);
  7916.  
  7917.  
  7918. ΓòÉΓòÉΓòÉ 10.25. Get Data by Key Order ΓòÉΓòÉΓòÉ
  7919.  
  7920. #include "bullet_2.h"
  7921.  
  7922. ACCESSPACK AP;                  // packs used here
  7923.  
  7924. // First example starts at first in-order data and moves through to last
  7925.  
  7926. AP.func = GET_FIRST_XB;
  7927. AP.handle = indexID;
  7928. AP.recPtr = &yourRecord;        // struct yourStructure yourRecord;
  7929. AP.keyPtr = keyBuffer;          // CHAR keyBuffer[64];
  7930. rez = BULLET(&AP);
  7931. if (rez) return(rez);
  7932. printf("The first in-order key is in keyBuffer and its record in yourRecord\n");
  7933.  
  7934. AP.func = GET_NEXT_XB;          // other parm same as set above
  7935. while (rez==0) {
  7936.    rez = BULLET(&AP);
  7937.    if (rez) break;
  7938.    printf("The next in-order key is in keyBuffer and its record in yourRecord\n");
  7939. }
  7940. if (rez==EXB_END_OF_FILE) rez==0; // expected rez after end of file
  7941. if (rez) return(rez);
  7942.  
  7943.  
  7944. // Second example starts at last in-order data and moves through to first
  7945. // note: since above already is past last, the call to GET_LAST_XB in
  7946. //       this example would not be necessary -- a call to GET_PREV_XB
  7947. //       could have been made directly -- however, it doesn't matter
  7948.  
  7949. AP.func = GET_LAST_XB;
  7950. AP.handle = indexID;
  7951. AP.recPtr = &yourRecord;
  7952. AP.keyPtr = keyBuffer;
  7953. rez = BULLET(&AP);
  7954. if (rez) return(rez);
  7955. printf("The last in-order key is in keyBuffer and its record in yourRecord\n");
  7956.  
  7957. AP.func = GET_PREV_XB;          // other parms same as set above
  7958. while (rez==0) {
  7959.    rez = BULLET(&AP);
  7960.    if (rez) break;
  7961.    printf("The previous in-order key is in keyBuffer and its record in yourRecord\n");
  7962. }
  7963. if (rez==EXB_TOP_OF_FILE) rez==0; // expected rez before at beginning of file
  7964. if (rez) return(rez);
  7965.  
  7966.  
  7967. // Third example performs a GET_EQUAL_OR_GREATER operation, typically
  7968. // used to locate to a key based on a partial search criterion
  7969.  
  7970. AP.func = GET_EQUAL_XB;
  7971. AP.handle = indexID;
  7972. AP.recPtr = &yourRecord;    // to be filled on return, if found
  7973.  
  7974. memset(keyBuffer,0,sizeof(keyBuffer);   // clear it out (required)
  7975. strcpy(keyBuffer,"KING");               // find first key starting with 'KING'
  7976. AP.keyPtr = keyBuffer;
  7977.  
  7978. rez = BULLET(&AP);
  7979. if (rez==0)
  7980.    printf("Matched search key in keyBuffer EXACTLY -- its record is in yourRecord\n");
  7981. else if (rez==EXB_KEY_NOT_FOUND) {
  7982.  
  7983.    // since not found, get the following in-order one (say, 'KINGSTON')
  7984.  
  7985.    AP.func = GET_NEXT_XB;
  7986.    rez = BULLET(&AP);
  7987.    if (rez) return(rez);
  7988.  
  7989.    printf("Not exact, but next greater key is in keyBuffer and its record is in yourRecord\n");
  7990. }
  7991. else
  7992.    return(rez);
  7993.  
  7994.  
  7995. ΓòÉΓòÉΓòÉ 10.26. Insert Data Record with Key Into Database ΓòÉΓòÉΓòÉ
  7996.  
  7997. #include "bullet_2.h"
  7998.  
  7999. ACCESSPACK AP;
  8000. LOCKPACK LP;                    // packs used here
  8001.  
  8002. LP.func = LOCK_XB;
  8003. LP.handle = indexID;            // also locks indexID's owner (its DBF)
  8004. LP.xlMode = LOCK_EXCLUSIVE;     // exclusive lock for index
  8005. LP.dlMode = LOCK_EXCLUSIVE;     // exclusive lock for data
  8006. LP.nextPtr = NULL;              // only one pack
  8007. rez = BULLET(&LP);
  8008. if (rez) return(LP.stat);       // rez for transaction-list is NOT the error
  8009.  
  8010. AP.func = INSERT_XB;
  8011. AP.handle = indexID;
  8012. AP.recNo = 0;                   // must be zero
  8013. AP.recPtr = &yourRecord;        // contains data record
  8014. AP.keyPtr = keyBuffer;          // empty, on return has key stored
  8015. AP.nextPtr = NULL;              // only the single pack
  8016. rez = BULLET(&AP);
  8017.  
  8018. // on return, as on all transaction-list routines, rez is not the return
  8019. // code but is the pack item that failed (neg if data, pos if index)
  8020.  
  8021. if (rez==0)
  8022.    printf("okay\n");
  8023. else if (rez < 0)
  8024.    printf("insert failed with data, err: %d\n",AP.stat);
  8025. else
  8026.    printf("insert failed with index, err: %d\n",AP.stat);
  8027.  
  8028.  
  8029. // if locked, MUST unlock!
  8030.  
  8031. LP.func = UNLOCK_XB;
  8032. LP.handle = indexID;
  8033. LP.nextPtr = NULL;
  8034. rez2 = BULLET(&LP);
  8035. if (rez2) rez2=LP.stat; // rez for transaction-list is NOT the error
  8036. if (rez==0) rez=rez2    // return the most interesting error, if any
  8037.  
  8038.  
  8039. ΓòÉΓòÉΓòÉ 10.27. Update Data Record with Key Update ΓòÉΓòÉΓòÉ
  8040.  
  8041. #include "bullet_2.h"
  8042.  
  8043. ACCESSPACK AP;
  8044. LOCKPACK LP;                    // packs used here
  8045.  
  8046. LP.func = LOCK_XB;
  8047. LP.handle = indexID;            // also locks indexID's owner (its DBF)
  8048. LP.xlMode = LOCK_SHARED;        // shared lock for index
  8049. LP.dlMode = LOCK_SHARED;        // shared lock for data
  8050. LP.nextPtr = NULL;              // only one pack
  8051. rez = BULLET(&LP);
  8052. if (rez) return(LP.stat);       // rez for transaction-list is NOT the error
  8053.  
  8054. AP.func = GET_FIRST_XB;         // get first key's data record (and its recNo)
  8055. AP.handle = indexID;
  8056. AP.recPtr = &yourRecord;
  8057. AP.keyPtr = keyBuffer;
  8058. rez = BULLET(&AP);
  8059. if (rez==0) {
  8060.  
  8061.    // assume that we want to change this entry now (as if we weren't sure
  8062.    // that we wanted to initially, hence the initial shared lock) --
  8063.    // this requires write access so relock to allow write access
  8064.  
  8065.    LP.func = RELOCK_XB;
  8066.    LP.handle = indexID;
  8067.    LP.xlMode = LOCK_EXCLUSIVE;  // exclusive lock for index
  8068.    LP.dlMode = LOCK_EXCLUSIVE;  // exclusive lock for data
  8069.    LP.recStart = 0;
  8070.    LP.nextPtr = NULL;
  8071.    rez = BULLET(&LP);
  8072.    if (rez) rez=LP.stat         // xaction-list routine so use LP.stat
  8073.    if (rez==0) {
  8074.  
  8075.       strcpy(yourRecord.someField,"new field data");
  8076.  
  8077.       // AP.recNo has been set by Bullet GET_XB call above
  8078.  
  8079.       AP.func = UPDATE_XB;
  8080.       AP.nextPtr = NULL;        // all other AP members set above
  8081.       rez = BULLET(&AP);
  8082.  
  8083.       // on return, as on all transaction-list routines, rez is not the return
  8084.       // code but is the pack item that failed (neg if data, pos if index)
  8085.  
  8086.       if (rez==0)
  8087.          printf("okay\n");
  8088.       else if (rez < 0)
  8089.          printf("update failed with data, err: %d\n",AP.stat);
  8090.       else
  8091.          printf("update failed with index, err: %d\n",AP.stat);
  8092.    }
  8093.    else
  8094.       printf("relock failed, rez: %d  err: %d\n",rez,LP.stat);
  8095. }
  8096.  
  8097. // if locked, MUST unlock!
  8098.  
  8099. LP.func = UNLOCK_XB;
  8100. LP.handle = indexID;
  8101. LP.nextPtr = NULL;
  8102. rez2 = BULLET(&LP);
  8103. if (rez2) rez2=LP.stat; // rez for transaction-list is NOT the error
  8104. if (rez==0) rez=rez2    // return the most interesting error, if any
  8105.  
  8106.  
  8107. ΓòÉΓòÉΓòÉ 10.28. Remote Drive, File/Device Check ΓòÉΓòÉΓòÉ
  8108.  
  8109. #include "bullet_2.h"
  8110.  
  8111. REMOTEPACK RP;                  // packs used here
  8112.  
  8113. // check if file in handle (or device handle) is on a 'network' drive
  8114.  
  8115. RP.func = CHECK_REMOTE_XB;
  8116. RP.handle = indexID;            // check if indexID handle is on a network
  8117. rez = BULLET(&RP);
  8118. if (rez) return(rez);
  8119. if (RP.isRemote)
  8120.    printf("handle is on a network drive\n");
  8121.  
  8122. // check if drive is a 'network' drive
  8123.  
  8124. RP.func = CHECK_REMOTE_XB;
  8125. RP.handle = 0;                  // set RP.handle to 0 to check drive
  8126. RP.drive = 0;                   // check if current drive is network drive
  8127. rez = BULLET(&RP);              // to check drive C:, set RP.drive=3
  8128. if (rez) return(rez);           // D: is RP.drive=4, and so on
  8129. if (RP.isRemote)
  8130.    printf("current drive is a network drive\n");
  8131.  
  8132.  
  8133. ΓòÉΓòÉΓòÉ 10.29. Relock Individual File ΓòÉΓòÉΓòÉ
  8134.  
  8135. #include "bullet_2.h"
  8136.  
  8137. LOCKPACK LP;
  8138. STATHANDLEPACK SHP;
  8139. STATDATAPACK SDP;
  8140. STATINDEXPACK SIP;              // packs used here
  8141.  
  8142. // given a handle, determine if it's DBF or index
  8143. // check if currently locked
  8144. // if locked, determine if shared or exclusive lock
  8145. // if exclusive lock, relock to shared
  8146. // if not locked, do nothing
  8147.  
  8148. SHP.func = STAT_HANDLE_XB;
  8149. SHP.handle = passedHandle;
  8150. rez = BULLET(&SHP);
  8151. if (SHP.ID==-1)
  8152.    puts("Handle is not a Bullet data or index file\n");
  8153. else {
  8154.    if (SHP.ID==0) {
  8155.  
  8156.       // normally, you lock before calling this routine but since
  8157.       // SIP.lockCount and SIP.flags are all that is being checked,
  8158.       // and since nothing is to be done if the handle is not locked,
  8159.       // it's okay in this instance to use this routine without
  8160.       // first explicitly locking the handle
  8161.  
  8162.       SIP.func = STAT_INDEX_XB;
  8163.       SIP.handle = passedHandle;
  8164.       rez = BULLET(&SIP);
  8165.       if (rez) goto ErrorHandler;
  8166.  
  8167.       // if locked, check if the lock is exclusive
  8168.  
  8169.       if (SIP.lockCount) {              // count of active full locks
  8170.  
  8171.          if ((SIP.flags & 4)==0) {      // bit2=0 means lock is not shared
  8172.  
  8173.             // currently exclusive, make it shared
  8174.  
  8175.             LP.func = RELOCK_INDEX_XB;
  8176.             LP.handle = passedHandle;
  8177.             LP.xlMode = LOCK_SHARED;
  8178.             rez = BULLET(&LP);
  8179.             if (rez) goto ErrorHandler;
  8180.          }
  8181.       }
  8182.    }
  8183.    else {
  8184.  
  8185.       SDP.func = STAT_DATA_XB;
  8186.       SDP.handle = passedHandle;
  8187.       rez = BULLET(&SDP);
  8188.       if (rez) goto ErrorHandler;
  8189.  
  8190.       if (SDP.lockCount) {              // count of active full locks
  8191.  
  8192.          if ((SDP.flags & 4)==0) {      // bit2=0 means lock is not shared
  8193.  
  8194.             // currently exclusive, make it shared
  8195.  
  8196.             LP.func = RELOCK_DATA_XB;
  8197.             LP.handle = passedHandle;
  8198.             LP.dlMode = LOCK_SHARED;
  8199.             LP.startRec = 0;            // entire file
  8200.             rez = BULLET(&LP);
  8201.             if (rez) goto ErrorHandler;
  8202.          }
  8203.       }
  8204.    }
  8205. }
  8206.  
  8207.  
  8208. ΓòÉΓòÉΓòÉ 10.30. DOS Disk Routines Through Bullet ΓòÉΓòÉΓòÉ
  8209.  
  8210. #include "bullet_2.h"
  8211.  
  8212. DOSFILEPACK DFP;                // packs used here
  8213.  
  8214. CHAR dataDirname[] = "under";
  8215. CHAR dataFilename[]= "under\\data.dbf";
  8216. CHAR newFilename[] = "under\\newdata.dbf";
  8217.  
  8218. // check if pathame can be access for read/write denynone (expected not here)
  8219.  
  8220. DFP.func = ACCESS_FILE_DOS;
  8221. DFP.filenamePtr = dataFilename;
  8222. DFP.asMode = 0x42;
  8223. rez = BULLET(&DFP);
  8224. if (rez==0) return(0);          // file already exists
  8225. if (rez==5) return(5);          // access denied (exists but in use)
  8226. // other errors possible, too, do the Make and Create and go by those results
  8227.  
  8228. // make directory for file
  8229.  
  8230. DFP.func = MAKE_DIR_DOS;
  8231. DFP.filenamePtr = dataDirname;
  8232. rez = BULLET(&DFP);
  8233. if (rez) rez=0;                 // can't create subdirectory (already exists?)
  8234.  
  8235. // create file
  8236.  
  8237. DFP.func = CREATE_FILE_DOS;
  8238. DFP.filenamePtr = dataFilename;
  8239. DFP.attr = 0;                   // 'normal' attributes
  8240. rez = BULLET(&DFP);
  8241. if (rez) return(rez);           // can't create file
  8242.  
  8243. // open file
  8244.  
  8245. DFP.func = OPEN_FILE_DOS;
  8246. DFP.filenamePtr = dataFilename;
  8247. DFP.asMode = 0x42;
  8248. rez = BULLET(&DFP);
  8249. if (rez) return(rez);           // can't open file
  8250.  
  8251. // DFP.handle is set by OPEN_FILE_DOS call above
  8252. // pre-allocate file space
  8253.  
  8254. DFP.func = EXPAND_FILE_DOS;
  8255. // DFP.handle already set
  8256. // DFP.asMode already set
  8257. DFP.bytes = 128*1024;           // set filesize to 128KB
  8258. rez = BULLET(&DFP);
  8259. if (rez) return(rez);           // can't do it (close file before return)
  8260.  
  8261. CHAR writeStuff[]="Write this string to offset 888";
  8262.  
  8263. DFP.func = SEEK_FILE_DOS;
  8264. // DFP.handle already set
  8265. DFP.seekTo = 888;
  8266. DFP.method = 0;                 // from start of file
  8267. rez = BULLET(&DFP);
  8268. if (rez) return(rez);           // can't do it (close...)
  8269.  
  8270. // write the string to disk
  8271.  
  8272. DFP.func = WRITE_FILE_DOS;
  8273. // DFP.handle already set
  8274. DFP.bytes = strlen(writeStuff);
  8275. DFP.bufferPtr = writeStuff;
  8276. rez = BULLET(&DFP);
  8277. if (rez) return(rez);
  8278.  
  8279. // commit to the deep
  8280.  
  8281. DFP.func = COMMIT_FILE_DOS;
  8282. // DFP.handle already set
  8283. rez = BULLET(&DFP);
  8284. if (rez) return(rez);
  8285.  
  8286. // reposition to where write started so can read what was written
  8287.  
  8288. DFP.func = SEEK_FILE_DOS;
  8289. // DFP.handle already set
  8290. DFP.seekTo = 888;
  8291. DFP.method = 0;
  8292. rez = BULLET(&DFP);
  8293. if (rez) return(rez);
  8294.  
  8295. CHAR readBuffer[128];
  8296.  
  8297. DFP.func = READ_FILE_DOS;
  8298. // DFP.handle already set
  8299. DFP.bytes = strlen(writeStuff); // read it back
  8300. DFP.bufferPtr = readBuffer;
  8301. rez = BULLET(&DFP);
  8302. if (rez) return(rez);
  8303. if (DFP.bytes != strlen(writeStuff))
  8304.    printf("read came up short!\n");
  8305.  
  8306. DFP.func = CLOSE_FILE_DOS;
  8307. // DFP.handle already set
  8308. rez = BULLET(&DFP);
  8309. if (rez) return(rez);
  8310. DFP.handle = 0;                 // it's gone (closed, anyway)
  8311.  
  8312. // rename the file
  8313.  
  8314. DFP.func = RENAME_FILE_DOS;
  8315. DFP.filenamePtr = dataFilename;
  8316. DFP.newFilenamePtr = newFilename;
  8317. rez = BULLET(&DFP);
  8318. if (rez) return(rez);
  8319.  
  8320. // and get rid of it (known now as newFilename)
  8321.  
  8322. DFP.func = DELETE_FILE_DOS;
  8323. DFP.filenamePtr = newFilename;
  8324. rez = BULLET(&DFP);
  8325. return(rez);
  8326.  
  8327. // Why the DosXXX routines when they're standard API?  Not all
  8328. // language tools are able to directly call the API, but can
  8329. // call them indirectly through the Bullet DLL.
  8330.  
  8331.  
  8332. ΓòÉΓòÉΓòÉ 11. Bullet Errors ΓòÉΓòÉΓòÉ
  8333.  
  8334. Bullet error codes are numbered so that they do not overlap OS error codes. 
  8335. The first general Bullet error number is 8193, but Bullet also returns select 
  8336. system error code numbers instead of duplicating system codes (those listed 
  8337. below less than 8192).  In addition, if the error is reported by the OS (for 
  8338. example, attempting to access a locked file), then the OS error in returned by 
  8339. Bullet.  For a list of OS errors, see OS/2 Dos API Errors. 
  8340.  
  8341. System Error Codes
  8342.  
  8343.    8    EXB_NOT_ENOUGH_MEMORY
  8344.         cannot get memory requested
  8345.  
  8346.   15    EXB_INVALID_DRIVE
  8347.         not a valid drive letter
  8348.  
  8349.   38    EXB_UNEXPECTED_EOF
  8350.         unexpected end-of-file where bytes requested for read exceeded EOF
  8351.  
  8352.   39    EXB_DISK_FULL
  8353.         disk full on WriteFile
  8354.  
  8355.   80    EXB_FILE_EXISTS
  8356.         cannot create file since it already exists
  8357.  
  8358.  105    EXB_SEM_OWNER_DIED
  8359.         owner of mutex semaphore died  (used in place of Win32 80h)
  8360.  
  8361.  640    EXB_TIMEOUT
  8362.         mutex semaphore timed out (used in place of Win32 102h)
  8363.  
  8364.  
  8365. 8192+   EXB_OR_WITH_FAULTS
  8366.         1=flush failed on handle close
  8367.         2=free memory failed on handle close
  8368.         4=failed memo handle close (data handle only)
  8369.  
  8370.         During a CLOSE_XB routine, the close process continues
  8371.         regardless of errors, and so the errors are accumulated.
  8372.         For example, 8193 means the flush failed, and 8195 means
  8373.         both the flush and the free failed (8192+1+2=8195).
  8374.         If the error occurred in the actual DosClose() API call,
  8375.         only that error is returned (it will be an OS error code).
  8376.  
  8377. 8251    EXB_216501
  8378.         INT21/6501h not supported by DOS extender (see ccdosfn.c)
  8379.  
  8380. 8256    EXB_216506
  8381.         INT21/6506h not supported by DOS extender (see ccdosfn.c)
  8382.  
  8383.  
  8384. 8300    EXB_ILLEGAL_CMD
  8385.         function not allowed
  8386.  
  8387. 8301    EXB_OLD_DOS
  8388.         OS version < MIN_DOS_NEEDED
  8389.  
  8390. 8302    EXB_NOT_INITIALIZED
  8391.         init not active, must do INIT_XB before using Bullet
  8392.  
  8393. 8303    EXB_ALREADY_INITIALIZED
  8394.         init already active, must do EXIT_XB first
  8395.  
  8396. 8304    EXB_TOO_MANY_HANDLES
  8397.         more than 1024 opens requested,
  8398.         or more than license permits (100, 250, 1024)
  8399.  
  8400. 8305    EXB_SYSTEM_HANDLE
  8401.         Bullet won't use or close handles 0-2
  8402.  
  8403. 8306    EXB_FILE_NOT_OPEN
  8404.         the handle is not a Bullet handle, including the
  8405.         handle supplied in OP.xbLink
  8406.  
  8407. 8307    EXB_FILE_IS_DIRTY
  8408.         tried to reload header but current still dirty;
  8409.         flush the file before reloading the header
  8410.  
  8411. 8308    EXB_BAD_FILETYPE
  8412.         attempted to do a key file operation on non-key file,
  8413.         or a data operation on a non-data file
  8414.  
  8415. 8309    EXB_TOO_MANY_PACKS
  8416.         too many INSERT, UPDATE, REINDEX, LOCK_XB packs (more than 256)
  8417.  
  8418. 8310    EXB_NULL_RECPTR
  8419.         null record pointer passed to Bullet (.recPtr==NULL)
  8420.  
  8421. 8311    EXB_NULL_KEYPTR
  8422.         null key pointer passed to Bullet (.keyPtr==NULL)
  8423.  
  8424. 8312    EXB_NULL_MEMOPTR
  8425.         null memo pointer passed to Bullet (.memoPtr==NULL)
  8426.  
  8427. 8313    EXB_EXPIRED
  8428.         evaluation time period has expired, reinstall if time remaining
  8429.  
  8430. 8314    EXB_BAD_INDEX
  8431.         Query/SetSysVars index selection is beyond the last one
  8432.  
  8433. 8315    EXB_RO_INDEX
  8434.         SetSysVars index item is read-only
  8435.  
  8436. 8316    EXB_FILE_BOUNDS
  8437.         file size > 4GB, or greater than the SetSysVars value
  8438.  
  8439.  
  8440. 8397    EXB_FORCE_ROLLBACK
  8441.         rollback test completed (last AP[].nextPtr=-1 for Insert/Update) (test-use only)
  8442.  
  8443. 8398    EXB_INVALID_DLL
  8444.         DLL seems to be invalid (8399 similar)
  8445.  
  8446.  
  8447. Multi-access Error Codes
  8448.  
  8449. 8401    EXB_BAD_LOCK_MODE
  8450.         lock mode (LP) not valid, must be 0 or 1
  8451.  
  8452. 8402    EXB_NOTHING_TO_RELOCK
  8453.         cannot relock without existing full-lock
  8454.  
  8455. 8403    EXB_SHARED_LOCK_ON
  8456.         unlikely error, write access needed for flush, but lock is shared
  8457.  
  8458.  
  8459. Index Error Codes
  8460.  
  8461. 8501    EXB_KEY_NOT_FOUND
  8462.         exact match of key not found
  8463.  
  8464. 8502    EXB_KEY_EXISTS
  8465.         key exists already and dups not allowed
  8466.  
  8467. 8503    EXB_END_OF_FILE
  8468.         already at last index order
  8469.  
  8470. 8504    EXB_TOP_OF_FILE
  8471.         already at first index order
  8472.  
  8473. 8505    EXB_EMPTY_FILE
  8474.         nothing to do since no keys
  8475.  
  8476. 8506    EXB_CANNOT_GET_LAST
  8477.         cannot locate last key
  8478.  
  8479. 8507    EXB_BAD_INDEX_STACK
  8480.         index file is corrupt
  8481.  
  8482. 8508    EXB_BAD_INDEX_READ0
  8483.         index file is corrupt
  8484.  
  8485. 8509    EXB_BAD_INDEX_WRITE0
  8486.         index file is corrupt
  8487.  
  8488.  
  8489. 8521    EXB_OLD_INDEX
  8490.         incompatible Bullet index, use ReindexOld subroutine, if available
  8491.  
  8492. 8522    EXB_UNKNOWN_INDEX
  8493.         not a Bullet index file
  8494.  
  8495. 8523    EXB_KEY_TOO_LONG
  8496.         keylength > 62 (or 64 if unique), or is 0
  8497.  
  8498.  
  8499. 8531    EXB_PARSER_NULL
  8500.         parser function pointer is NULL
  8501.  
  8502. 8532    EXB_BUILDER_NULL
  8503.         build key function pointer is NULL
  8504.  
  8505. 8533    EXB_BAD_SORT_FUNC
  8506.         CIP.sortFunction not valid (not 1-6, or a custom sort-compare)
  8507.  
  8508. 8534    EXB_BAD_NODE_SIZE
  8509.         CIP.nodeSize is not 512, 1024, or 2048
  8510.  
  8511. 8535    EXB_FILENAME_TOO_LONG
  8512.         CIP.filenamePtr->pathname greater than file system allows
  8513.  
  8514.         This error is detected only for the file system installed,
  8515.         and does not detect using pathnames greater than 80 on a FAT
  8516.         system if HPFS is installed.  The OS returns its own error
  8517.         in this case, after the fact.
  8518.  
  8519.  
  8520. 8541    EXB_KEYX_NULL
  8521.         key expression is effectively NULL
  8522.  
  8523. 8542    EXB_KEYX_TOO_LONG
  8524.         CIP.keyExpPtr->expression is greater than 159 bytes
  8525.  
  8526. 8543    EXB_KEYX_SYM_TOO_LONG
  8527.         fieldname/funcname in expression is longer than 10 chars
  8528.  
  8529. 8544    EXB_KEYX_SYM_UNKNOWN
  8530.         fieldname/funcname in expression is unknown or misspelled
  8531.  
  8532. 8545    EXB_KEYX_TOO_MANY_SYMS
  8533.         too many symbols/fields used in expression (16 max)
  8534.  
  8535. 8546    EXB_KEYX_BAD_SUBSTR
  8536.         invalid SUBSTR() operand in expression
  8537.  
  8538. 8547    EXB_KEYX_BAD_SUBSTR_SZ
  8539.         SUBSTR() exceeds field's size
  8540.  
  8541. 8548    EXB_KEYX_BAD_FORM
  8542.         didn't match expected symbol in expression (missing paren, etc.)
  8543.  
  8544.  
  8545. 8551    EXB_NO_READS_FOR_RUN
  8546.         unlikely error, use different reindex buffer size to fix
  8547.  
  8548. 8552    EXB_TOO_MANY_RUNS
  8549.         unlikely error, too many runs (64K or more runs)
  8550.  
  8551. 8553    EXB_TOO_MANY_RUNS_FOR_BUFFER
  8552.         unlikely error, too many runs for run buffer
  8553.  
  8554. 8554    EXB_TOO_MANY_DUPLICATES
  8555.         more than 64K "identical" keys since the last enumerator used
  8556.         was 0xFFFF -- if ever you have this error, REINDEX_XB should
  8557.         be used to resequence the enumerators
  8558.  
  8559.  
  8560. 8561    EXB_INSERT_RECNO_BAD
  8561.         AP.recNo cannot be > 0 if inserting with INSERT_XB
  8562.  
  8563. 8562    EXB_PREV_APPEND_EMPTY
  8564.         no previous append for INSERT_XB yet AP.recNo==0x80000000
  8565.  
  8566. 8563    EXB_PREV_APPEND_MISMATCH
  8567.         previous append's xbLink does not match this
  8568.         -- if this pack's AP.recNo=0x80000000 then this pack's AP.handle
  8569.         must be the same handle as that of the last pack that added a record
  8570.  
  8571. 8564    EXB_INSERT_KBO_FAILED
  8572.         could not back out key at INSERT_XB
  8573.  
  8574. 8565    EXB_INSERT_DBO_FAILED
  8575.         could not back out data records at INSERT_XB
  8576.  
  8577.  
  8578. 8571    WRN_NOTHING_TO_UPDATE
  8579.         all AP.recNo=0 at UPDATE_XB so nothing to do
  8580.  
  8581. 8572    EXB_INTERNAL_UPDATE
  8582.         internal error UPDATE_XB, not in handle/record# list
  8583.  
  8584. 8573    EXB_FAILED_DATA_RESTORE
  8585.         could not restore original data record (*)
  8586.  
  8587. 8574    EXB_FAILED_KEY_DELETE
  8588.         could not remove new key (*)
  8589.  
  8590. 8575    EXB_FAILED_KEY_RESTORE
  8591.         could not restore original key(*)
  8592.  
  8593.         (*) original error, which forced a back-out, has been
  8594.         replaced by this error -- this error is always returned
  8595.         in the first AP.stat (-1 on data, 1 on index)
  8596.  
  8597.  
  8598. Data Error Codes
  8599.  
  8600. 8601    EXB_EXT_XBLINK
  8601.         xbLink handle is not an internal DBF, as was specified during the
  8602.         index file's creation -- the Bullet routine called requires a Bullet
  8603.         DBF data file (instead use index-only access methods like NEXT_KEY_XB).
  8604.  
  8605. 8602    EXB_FIELDNAME_TOO_LONG
  8606.         fieldname is > 10 characters
  8607.  
  8608. 8603    EXB_RECORD_TOO_LONG
  8609.         record length is > 64K
  8610.  
  8611. 8604    EXB_FIELD_NOT_FOUND
  8612.         fieldname not found in descriptor info
  8613.  
  8614. 8605    EXB_BAD_FIELD_COUNT
  8615.         fields <= 0 or >= MAX_FIELDS; also use of a field number which
  8616.         is beyond the last field
  8617.  
  8618. 8606    EXB_BAD_HEADER
  8619.         bad header (reclen=0, etc.)
  8620.  
  8621. 8607    EXB_BUFFER_TOO_SMALL
  8622.         buffer too small (pack buffer < record length)
  8623.  
  8624. 8608    EXB_INTERNAL_PACK
  8625.         internal error in PackRecords
  8626.  
  8627. 8609    EXB_BAD_RECNO
  8628.         record number=0 or > records in data file header, or
  8629.         pack attempt on empty data file
  8630.  
  8631. 8610    WRN_RECORD_TAGGED
  8632.         record's tag field matches skip tag
  8633.  
  8634.  
  8635. Memo Error Codes
  8636.  
  8637. 8701    WRN_CANNOT_OPEN_MEMO
  8638.         the DBF header has bits 3 & 7 set, which indicates that a memo
  8639.         file is attached to this DBF, but the DBT memo file failed to open
  8640.         -- the DBF open continues, with this warning code returned
  8641.  
  8642. 8702    EXB_MEMO_NOT_OPEN
  8643.         no open memo file for operation
  8644.  
  8645. 8703    EXB_BAD_BLOCKSIZE
  8646.         memo blocksize must be at least 24 bytes
  8647.  
  8648. 8704    EXB_MEMO_DELETED
  8649.         memo is deleted
  8650.  
  8651. 8705    EXB_MEMO_PAST_END
  8652.         memo data requested is past end of record
  8653.  
  8654. 8706    EXB_BAD_MEMONO
  8655.         memo number is not valid
  8656.  
  8657. 8707    EXB_MEMO_IN_USE
  8658.         memo add encountered likely corrupt memo file
  8659.         -- avail list indicates this memo record is deleted, but the memoAvail
  8660.         link for the memo indicates it is use (memoAvail link==0x8FFFF)
  8661.  
  8662. 8708    EXB_BAD_AVAIL_LINK
  8663.         memo avail link cannot be valid (e.g., memoAvail==0)
  8664.  
  8665. 8709    EXB_MEMO_ZERO_SIZE
  8666.         memo data has no size (size is 0)
  8667.  
  8668. 8710    EXB_MEMO_IS_SMALLER
  8669.         memo attempt to shrink but memo size is already <= size requested
  8670.  
  8671.  
  8672. ΓòÉΓòÉΓòÉ 12. OS/2 Dos API Errors ΓòÉΓòÉΓòÉ
  8673.  
  8674.  
  8675.   0   NO_ERROR
  8676.           No error occurred.
  8677.  
  8678.   1   ERROR_INVALID_FUNCTION
  8679.           Invalid function number.
  8680.  
  8681.   2   ERROR_FILE_NOT_FOUND
  8682.           File not found.
  8683.  
  8684.   3   ERROR_PATH_NOT_FOUND
  8685.           Path not found.
  8686.  
  8687.   4   ERROR_TOO_MANY_OPEN_FILES
  8688.           Too many open files (no handles left).
  8689.  
  8690.   5   ERROR_ACCESS_DENIED
  8691.           Access denied.
  8692.  
  8693.   6   ERROR_INVALID_HANDLE
  8694.           Invalid handle.
  8695.  
  8696.   7   ERROR_ARENA_TRASHED
  8697.           Memory control blocks destroyed.
  8698.  
  8699.   8   ERROR_NOT_ENOUGH_MEMORY
  8700.           Insufficient memory.
  8701.  
  8702.   9   ERROR_INVALID_BLOCK
  8703.           Invalid memory-block address.
  8704.  
  8705.   10   ERROR_BAD_ENVIRONMENT
  8706.           Invalid environment.
  8707.  
  8708.   11   ERROR_BAD_FORMAT
  8709.           Invalid format.
  8710.  
  8711.   12   ERROR_INVALID_ACCESS
  8712.           Invalid access code.
  8713.  
  8714.   13   ERROR_INVALID_DATA
  8715.           Invalid data.
  8716.  
  8717.   14   Reserved.
  8718.  
  8719.  
  8720.   15   ERROR_INVALID_DRIVE
  8721.           Invalid drive specified.
  8722.  
  8723.   16   ERROR_CURRENT_DIRECTORY
  8724.           Attempting to remove current directory.
  8725.  
  8726.   17   ERROR_NOT_SAME_DEVICE
  8727.           Not same device.
  8728.  
  8729.   18   ERROR_NO_MORE_FILES
  8730.           No more files.
  8731.  
  8732.   19   ERROR_WRITE_PROTECT
  8733.           Attempt to write on write-protected diskette.
  8734.  
  8735.   20   ERROR_BAD_UNIT
  8736.           Unknown unit.
  8737.  
  8738.   21   ERROR_NOT_READY
  8739.           Drive not ready.
  8740.  
  8741.   22   ERROR_BAD_COMMAND
  8742.           Unknown command.
  8743.  
  8744.   23   ERROR_CRC
  8745.           Data error - cyclic redundancy check.
  8746.  
  8747.   24   ERROR_BAD_LENGTH
  8748.           Invalid request structure length.
  8749.  
  8750.   25   ERROR_SEEK
  8751.           Seek error.
  8752.  
  8753.   26   ERROR_NOT_DOS_DISK
  8754.           Unknown media type.
  8755.  
  8756.   27   ERROR_SECTOR_NOT_FOUND
  8757.           Sector not found.
  8758.  
  8759.   28   ERROR_OUT_OF_PAPER
  8760.           Printer is out of paper.
  8761.  
  8762.   29   ERROR_WRITE FAULT
  8763.           Write fault.
  8764.  
  8765.   30   ERROR_READ_FAULT
  8766.           Read fault.
  8767.  
  8768.   31   ERROR_GEN_FAILURE
  8769.           General failure.
  8770.  
  8771.   32   ERROR_SHARING_VIOLATION
  8772.           Sharing violation.
  8773.  
  8774.   33   ERROR_LOCK_VIOLATION
  8775.           Lock violation.
  8776.  
  8777.   34   ERROR_WRONG_DISK
  8778.           Invalid disk change.
  8779.  
  8780.   35   ERROR_FCB_UNAVAILABLE
  8781.           FCB unavailable.
  8782.  
  8783.   36   ERROR_SHARING_BUFFER_EXCEEDED
  8784.           Sharing buffer overflow.
  8785.  
  8786.   37   ERROR_CODE_PAGE_MISMATCHED
  8787.           Code page does not match.
  8788.  
  8789.   38   ERROR_HANDLE_EOF
  8790.           End of file reached.
  8791.  
  8792.   39   ERROR_HANDLE_DISK_FULL
  8793.           Disk is full.
  8794.  
  8795.   40-49   Reserved.
  8796.  
  8797.  
  8798.   50   ERROR_NOT_SUPPORTED
  8799.           Network request not supported.
  8800.  
  8801.   51   ERROR_REM_NOT_LIST
  8802.           Remote network node is not online.
  8803.  
  8804.   52   ERROR_DUP_NAME
  8805.           Duplicate file name in network.
  8806.  
  8807.   53   ERROR_BAD_NETPATH
  8808.           Network path not found.
  8809.  
  8810.   54   ERROR_NETWORK_BUSY
  8811.           Network is busy.
  8812.  
  8813.   55   ERROR_DEV_NOT_EXIST
  8814.           Device is not installed in network.
  8815.  
  8816.   56   ERROR_TOO_MANY_CMDS
  8817.           Network command limit reached.
  8818.  
  8819.   57   ERROR_ADAP_HDW_ERR
  8820.           Network adapter hardware error.
  8821.  
  8822.   58   ERROR_BAD_NET_RESP
  8823.           Incorrect response in network.
  8824.  
  8825.   59   ERROR_UNEXP_NET_ERR
  8826.           Unexpected error in network.
  8827.  
  8828.   60   ERROR_BAD_REM_ADAP
  8829.           Remote network adapter error.
  8830.  
  8831.   61   ERROR_PRINTQ_FULL
  8832.           Network printer queue is full.
  8833.  
  8834.   62   ERROR_NO_SPOOL_SPACE
  8835.           No space in print spool file.
  8836.  
  8837.   63   ERROR_PRINT_CANCELLED
  8838.           Print spool file deleted.
  8839.  
  8840.   64   ERROR_NETNAME_DELETED
  8841.           Network name deleted.
  8842.  
  8843.   65   ERROR_NETWORK_ACCESS_DENIED
  8844.           Access to network denied.
  8845.  
  8846.   66   ERROR_BAD_DEV_TYPE
  8847.           Device type invalid for network.
  8848.  
  8849.   67   ERROR_BAD_NET_NAME
  8850.           Network name not found.
  8851.  
  8852.   68   ERROR_TOO_MANY_NAMES
  8853.           Network name limit exceeded.
  8854.  
  8855.   69   ERROR_TOO_MANY_SESS
  8856.           Network session limit exceeded.
  8857.  
  8858.   70   ERROR_SHARING_PAUSED
  8859.           Temporary pause in network.
  8860.  
  8861.   71   ERROR_REQ_NOT_ACCEP
  8862.           Network request denied.
  8863.  
  8864.   72   ERROR_REDIR_PAUSED
  8865.           Pause in network print disk redirection.
  8866.  
  8867.   73   ERROR_SBCS_ATT_WRITE_PROT
  8868.           Attempted write on protected disk.
  8869.  
  8870.   74   ERROR_SBCS_GENERAL_FAILURE
  8871.           General failure, single-byte character set.
  8872.  
  8873.   75-79   Reserved.
  8874.  
  8875.  
  8876.   80   ERROR_FILE_EXISTS
  8877.           File exists.
  8878.  
  8879.   81   ERROR_DUP_FCB
  8880.           Reserved.
  8881.  
  8882.   82   ERROR_CANNOT_MAKE
  8883.           Cannot make directory entry.
  8884.  
  8885.   83   ERROR_FAIL_I24
  8886.           Failure on INT 24.
  8887.  
  8888.   84   ERROR_OUT_OF_STRUCTURES
  8889.           Too many redirections.
  8890.  
  8891.   85   ERROR_ALREADY_ASSIGNED
  8892.           Duplicate redirection.
  8893.  
  8894.   86   ERROR_INVALID_PASSWORD
  8895.           Invalid password.
  8896.  
  8897.   87   ERROR_INVALID_PARAMETER
  8898.           Invalid parameter.
  8899.  
  8900.   88   ERROR_NET_WRITE_FAULT
  8901.           Network device fault.
  8902.  
  8903.   89   ERROR_NO_PROC_SLOTS
  8904.           No process slots available.
  8905.  
  8906.   90   ERROR_NOT_FROZEN
  8907.           System error.
  8908.  
  8909.   91   ERR_TSTOVFL
  8910.           Timer service table overflow.
  8911.  
  8912.   92   ERR_TSTDUP
  8913.           Timer service table duplicate.
  8914.  
  8915.   93   ERROR_NO_ITEMS
  8916.           No items to work on.
  8917.  
  8918.   95   ERROR_INTERRUPT
  8919.           Interrupted system call.
  8920.  
  8921.   99   ERROR_DEVICE_IN_USE
  8922.           Device in use.
  8923.  
  8924.   100   ERROR_TOO_MANY_SEMAPHORES
  8925.           User/system open semaphore limit reached.
  8926.  
  8927.   101   ERROR_EXCL_SEM_ALREADY_OWNED
  8928.           Exclusive semaphore already owned.
  8929.  
  8930.   102   ERROR_SEM_IS_SET
  8931.           DosCloseSem found semaphore set.
  8932.  
  8933.   103   ERROR_TOO_MANY_SEM_REQUESTS
  8934.           Too many exclusive semaphore requests.
  8935.  
  8936.   104   ERROR_INVALID_AT_INTERRUPT_TIME
  8937.           Operation invalid at interrupt time.
  8938.  
  8939.   105   ERROR_SEM_OWNER_DIED
  8940.           Previous semaphore owner terminated without freeing semaphore.
  8941.  
  8942.   106   ERROR_SEM_USER_LIMIT
  8943.           Semaphore limit exceeded.
  8944.  
  8945.   107   ERROR_DISK_CHANGE
  8946.           Insert drive B disk into drive A.
  8947.  
  8948.   108   ERROR_DRIVE_LOCKED
  8949.           Drive locked by another process.
  8950.  
  8951.   109   ERROR_BROKEN_PIPE
  8952.           Write on pipe with no reader.
  8953.  
  8954.   110   ERROR_OPEN_FAILED
  8955.           Open/create failed due to explicit fail command.
  8956.  
  8957.   111   ERROR_BUFFER_OVERFLOW
  8958.           Buffer passed to system call too small to hold return data.
  8959.  
  8960.   112   ERROR_DISK_FULL
  8961.           Not enough space on the disk.
  8962.  
  8963.   113   ERROR_NO_MORE_SEARCH_HANDLES
  8964.           Cannot allocate another search structure and handle.
  8965.  
  8966.   114   ERROR_INVALID_TARGET_HANDLE
  8967.           Target handle in DosDupHandle invalid.
  8968.  
  8969.   115   ERROR_PROTECTION_VIOLATION
  8970.           Invalid user virtual address.
  8971.  
  8972.   116   ERROR_VIOKBD_REQUEST
  8973.           Error on display write or keyboard read.
  8974.  
  8975.   117   ERROR_INVALID_CATEGORY
  8976.           Category for DevIOCtl not defined.
  8977.  
  8978.   118   ERROR_INVALID_VERIFY_SWITCH
  8979.           Invalid value passed for verify flag.
  8980.  
  8981.   119   ERROR_BAD_DRIVER_LEVEL
  8982.           Level four driver not found.
  8983.  
  8984.   120   ERROR_CALL_NOT_IMPLEMENTED
  8985.           Invalid function called.
  8986.  
  8987.   121   ERROR_SEM_TIMEOUT
  8988.           Time-out occurred from semaphore API function.
  8989.  
  8990.   122   ERROR_INSUFFICIENT_BUFFER
  8991.           Data buffer too small.
  8992.  
  8993.   123   ERROR_INVALID_NAME
  8994.           Illegal character or invalid file-system name.
  8995.  
  8996.   124   ERROR_INVALID_LEVEL
  8997.           Non-implemented level for information retrieval or setting.
  8998.  
  8999.   125   ERROR_NO_VOLUME_LABEL
  9000.           No volume label found with DosQueryFSInfo function.
  9001.  
  9002.   126   ERROR_MOD_NOT_FOUND
  9003.           Module handle not found with DosQueryProcAddr(),
  9004.           DosQueryModAddr().
  9005.  
  9006.   127   ERROR_PROC_NOT_FOUND
  9007.           Procedure address not found with DosQueryProcAddr().
  9008.  
  9009.   128   ERROR_WAIT_NO_CHILDREN
  9010.           DosWaitChild finds no children.
  9011.  
  9012.   129   ERROR_CHILD_NOT_COMPLETE
  9013.           DosWaitChild children not terminated.
  9014.  
  9015.   130   ERROR_DIRECT_ACCESS_HANDLE
  9016.           Handle operation invalid for direct disk-access handles.
  9017.  
  9018.   131   ERROR_NEGATIVE_SEEK
  9019.           Attempting seek to negative offset.
  9020.  
  9021.   132   ERROR_SEEK_ON_DEVICE
  9022.           Application trying to seek on device or pipe.
  9023.  
  9024.   133   ERROR_IS_JOIN_TARGET
  9025.           Drive has previously joined drives.
  9026.  
  9027.   134   ERROR_IS_JOINED
  9028.           Drive is already joined.
  9029.  
  9030.   135   ERROR_IS_SUBSTED
  9031.           Drive is already substituted.
  9032.  
  9033.   136   ERROR_NOT_JOINED
  9034.           Cannot delete drive that is not joined.
  9035.  
  9036.   137   ERROR_NOT_SUBSTED
  9037.           Cannot delete drive that is not substituted.
  9038.  
  9039.   138   ERROR_JOIN_TO_JOIN
  9040.           Cannot join to a joined drive.
  9041.  
  9042.   139   ERROR_SUBST_TO_SUBST
  9043.           Cannot substitute to a substituted drive.
  9044.  
  9045.   140   ERROR_JOIN_TO_SUBST
  9046.           Cannot join to a substituted drive.
  9047.  
  9048.   141   ERROR_SUBST_TO_JOIN
  9049.           Cannot substitute to a joined drive.
  9050.  
  9051.   142   ERROR_BUSY_DRIVE
  9052.           Specified drive is busy.
  9053.  
  9054.   143   ERROR_SAME_DRIVE
  9055.           Cannot join or substitute a drive to a directory on the same drive.
  9056.  
  9057.   144   ERROR_DIR_NOT_ROOT
  9058.           Directory must be a subdirectory of the root.
  9059.  
  9060.   145   ERROR_DIR_NOT_EMPTY
  9061.           Directory must be empty to use join command.
  9062.  
  9063.   146   ERROR_IS_SUBST_PATH
  9064.           Path specified is being used in a substitute.
  9065.  
  9066.   147   ERROR_IS_JOIN_PATH
  9067.           Path specified is being used in a join.
  9068.  
  9069.   148   ERROR_PATH_BUSY
  9070.           Path specified is being used by another process.
  9071.  
  9072.   149   ERROR_IS_SUBST_TARGET
  9073.           Cannot join or substitute a drive that has a directory that is the
  9074.           target of a previous substitute.
  9075.  
  9076.   150   ERROR_SYSTEM_TRACE
  9077.           System trace error.
  9078.  
  9079.   151   ERROR_INVALID_EVENT_COUNT
  9080.           DosWaitMuxWaitSem errors.
  9081.  
  9082.   152   ERROR_TOO_MANY_MUXWAITERS
  9083.           System limit of 100 entries reached.
  9084.  
  9085.   153   ERROR_INVALID_LIST_FORMAT
  9086.           Invalid list format.
  9087.  
  9088.   154   ERROR_LABEL_TOO_LONG
  9089.           Volume label too big.
  9090.  
  9091.   155   ERROR_TOO_MANY_TCBS
  9092.           Cannot create another TCB.
  9093.  
  9094.   156   ERROR_SIGNAL_REFUSED
  9095.           Signal refused.
  9096.  
  9097.   157   ERROR_DISCARDED
  9098.           Segment is discarded.
  9099.  
  9100.   158   ERROR_NOT_LOCKED
  9101.           Segment is not locked.
  9102.  
  9103.   159   ERROR_BAD_THREADID_ADDR
  9104.           Invalid thread-identity address.
  9105.  
  9106.   160   ERROR_BAD_ARGUMENTS
  9107.           Invalid environment pointer.
  9108.  
  9109.   161   ERROR_BAD_PATHNAME
  9110.           Invalid path name passed to exec.
  9111.  
  9112.   162   ERROR_SIGNAL_PENDING
  9113.           Signal already pending.
  9114.  
  9115.   163   ERROR_UNCERTAIN_MEDIA
  9116.           Error with INT 24 mapping.
  9117.  
  9118.   164   ERROR_MAX_THRDS_REACHED
  9119.           No more process slots.
  9120.  
  9121.   165   ERROR_MONITORS_NOT_SUPPORTED
  9122.           Error with INT 24 mapping.
  9123.  
  9124.   166   ERROR_UNC_DRIVER_NOT_INSTALLED
  9125.           Default redirection return code.
  9126.  
  9127.   167   ERROR_LOCK_FAILED
  9128.           Locking failed.
  9129.  
  9130.   168   ERROR_SWAPIO_FAILED
  9131.           Swap I/O failed.
  9132.  
  9133.   169   ERROR_SWAPIN_FAILED
  9134.           Swap in failed.
  9135.  
  9136.   170   ERROR_BUSY
  9137.           Segment is busy.
  9138.  
  9139.   171-172   Reserved.
  9140.  
  9141.  
  9142.   173   ERROR_CANCEL_VIOLATION
  9143.           A lock request is not outstanding for the specified file range, or the
  9144.           range length is zero.
  9145.  
  9146.   174   ERROR_ATOMIC_LOCK_NOT_SUPPORTED
  9147.           The file-system driver (FSD) does not support atomic lock operations.
  9148.           Versions of OS/2 prior to version 2.00 do not support atomic lock
  9149.           operations.
  9150.  
  9151.   175   ERROR_READ_LOCKS_NOT_SUPPORTED
  9152.           The file system driver (FSD) does not support shared read locks.
  9153.  
  9154.   176-179   Reserved.
  9155.  
  9156.  
  9157.   180   ERROR_INVALID_SEGMENT_NUMBER
  9158.           Invalid segment number.
  9159.  
  9160.   181   ERROR_INVALID_CALLGATE
  9161.           Invalid call gate.
  9162.  
  9163.   182   ERROR_INVALID_ORDINAL
  9164.           Invalid ordinal.
  9165.  
  9166.   183   ERROR_ALREADY_EXISTS
  9167.           Shared segment already exists.
  9168.  
  9169.   184   ERROR_NO_CHILD_PROCESS
  9170.           No child process to wait for.
  9171.  
  9172.   185   ERROR_CHILD_ALIVE_NOWAIT
  9173.           NoWait specified and child alive.
  9174.  
  9175.   186   ERROR_INVALID_FLAG_NUMBER
  9176.           Invalid flag number.
  9177.  
  9178.   187   ERROR_SEM_NOT_FOUND
  9179.           Semaphore does not exist.
  9180.  
  9181.   188   ERROR_INVALID_STARTING_CODESEG
  9182.           Invalid starting code segment, incorrect END (label) directive.
  9183.  
  9184.   189   ERROR_INVALID_STACKSEG
  9185.           Invalid stack segment.
  9186.  
  9187.   190   ERROR_INVALID_MODULETYPE
  9188.           Invalid module type - dynamic-link library file cannot be used as an
  9189.           application. Application cannot be used as a dynamic-link library.
  9190.  
  9191.   191   ERROR_INVALID_EXE_SIGNATURE
  9192.           Invalid EXE signature - file is a DOS mode program or an improper
  9193.           program.
  9194.  
  9195.   192   ERROR_EXE_MARKED_INVALID
  9196.           EXE marked invalid - link detected errors when the application was
  9197.           created.
  9198.  
  9199.   193   ERROR_BAD_EXE_FORMAT
  9200.           Invalid EXE format - file is a DOS mode program or an improper
  9201.           program.
  9202.  
  9203.   194   ERROR_ITERATED_DATA_EXCEEDS_64k
  9204.           Iterated data exceeds 64KB - there is more than 64KB of data in one
  9205.           of the segments of the file.
  9206.  
  9207.   195   ERROR_INVALID_MINALLOCSIZE
  9208.           Invalid minimum allocation size - the size is specified to be less than
  9209.           the size of the segment data in the file.
  9210.  
  9211.   196   ERROR_DYNLINK_FROM_INVALID_RING
  9212.           Dynamic link from invalid privilege level - privilege level 2 routine
  9213.           cannot link to dynamic-link libraries.
  9214.  
  9215.   197   ERROR_IOPL_NOT_ENABLED
  9216.           IOPL not enabled - IOPL set to NO in CONFIG.SYS.
  9217.  
  9218.   198   ERROR_INVALID_SEGDPL
  9219.           Invalid segment descriptor privilege level - can only have privilege
  9220.           levels of 2 and 3.
  9221.  
  9222.   199   ERROR_AUTODATASEG_EXCEEDS_64k
  9223.           Automatic data segment exceeds 64KB.
  9224.  
  9225.   200   ERROR_RING2SEG_MUST_BE_MOVABLE
  9226.           Privilege level 2 segment must be movable.
  9227.  
  9228.   201   ERROR_RELOC_CHAIN_XEEDS_SEGLIM
  9229.           Relocation chain exceeds segment limit.
  9230.  
  9231.   202   ERROR_INFLOOP_IN_RELOC_CHAIN
  9232.           Infinite loop in relocation chain segment.
  9233.  
  9234.   203   ERROR_ENVVAR_NOT_FOUND
  9235.           Environment variable not found.
  9236.  
  9237.   204   ERROR_NOT_CURRENT_CTRY
  9238.           Not current country.
  9239.  
  9240.   205   ERROR_NO_SIGNAL_SENT
  9241.           No signal sent - no process in the command subtree has a signal
  9242.           handler.
  9243.  
  9244.   206   ERROR_FILENAME_EXCED_RANGE
  9245.           File name or extension is greater than 8.3 characters.
  9246.  
  9247.   207   ERROR_RING2_STACK_IN_USE
  9248.           Privilege level 2 stack is in use.
  9249.  
  9250.   208   ERROR_META_EXPANSION_TOO_LONG
  9251.           Meta (global) expansion is too long.
  9252.  
  9253.   209   ERROR_INVALID_SIGNAL_NUMBER
  9254.           Invalid signal number.
  9255.  
  9256.   210   ERROR_THREAD_1_INACTIVE
  9257.           Inactive thread.
  9258.  
  9259.   211   ERROR_INFO_NOT_AVAIL
  9260.           File system information is not available for this file.
  9261.  
  9262.   212   ERROR_LOCKED
  9263.           Locked error.
  9264.  
  9265.   213   ERROR_BAD_DYNALINK
  9266.           Attempted to execute a non-family API in DOS mode.
  9267.  
  9268.   214   ERROR_TOO_MANY_MODULES
  9269.           Too many modules.
  9270.  
  9271.   215   ERROR_NESTING_NOT_ALLOWED
  9272.           Nesting is not allowed.
  9273.  
  9274.   217   ERROR_ZOMBIE_PROCESS
  9275.           Zombie process.
  9276.  
  9277.   218   ERROR_STACK_IN_HIGH_MEMORY
  9278.           Stack is in high memory.
  9279.  
  9280.   219   ERROR_INVALID_EXITROUTINE_RING
  9281.           Invalid exit routine ring.
  9282.  
  9283.   220   ERROR_GETBUF_FAILED
  9284.           Get buffer failed.
  9285.  
  9286.   221   ERROR_FLUSHBUF_FAILED
  9287.           Flush buffer failed.
  9288.  
  9289.   222   ERROR_TRANSFER_TOO_LONG
  9290.           Transfer is too long.
  9291.  
  9292.   224   ERROR_SMG_NO_TARGET_WINDOW
  9293.           The application window was created without the FCF_TASKLIST
  9294.           style, or the application window not yet been created or has already
  9295.           been destroyed.
  9296.  
  9297.   228   ERROR_NO_CHILDREN
  9298.           No child process.
  9299.  
  9300.   229   ERROR_INVALID_SCREEN_GROUP
  9301.           Invalid session.
  9302.  
  9303.   230   ERROR_BAD_PIPE
  9304.           Non-existent pipe or invalid operation.
  9305.  
  9306.   231   ERROR_PIPE_BUSY
  9307.           Pipe is busy.
  9308.  
  9309.   232   ERROR_NO_DATA
  9310.           No data available on non-blocking read.
  9311.  
  9312.   233   ERROR_PIPE_NOT_CONNECTED
  9313.           Pipe was disconnected by server.
  9314.  
  9315.   234   ERROR_MORE_DATA
  9316.           More data is available.
  9317.  
  9318.   240   ERROR_VC_DISCONNECTED
  9319.           Session was dropped due to errors.
  9320.  
  9321.   250   ERROR_CIRCULARITY_REQUESTED
  9322.           Renaming a directory that would cause a circularity problem.
  9323.  
  9324.   251   ERROR_DIRECTORY_IN_CDS
  9325.           Renaming a directory that is in use.
  9326.  
  9327.   252   ERROR_INVALID_FSD_NAME
  9328.           Trying to access nonexistent FSD.
  9329.  
  9330.   253   ERROR_INVALID_PATH
  9331.           Invalid pseudo device.
  9332.  
  9333.   254   ERROR_INVALID_EA_NAME
  9334.           Invalid character in name, or invalid cbName.
  9335.  
  9336.   255   ERROR_EA_LIST_INCONSISTENT
  9337.           List does not match its size, or there are invalid EAs in the list.
  9338.  
  9339.   256   ERROR_EA_LIST_TOO_LONG
  9340.           FEAList is longer than 64K-1 bytes.
  9341.  
  9342.   257   ERROR_NO_META_MATCH
  9343.           String does not match expression.
  9344.  
  9345.   259   ERROR_NO_MORE_ITEMS
  9346.           DosQueryFSAttach ordinal query.
  9347.  
  9348.   260   ERROR_SEARCH_STRUC_REUSED
  9349.           DOS mode findfirst/next search structure reused.
  9350.  
  9351.   261   ERROR_CHAR_NOT_FOUND
  9352.           Character not found.
  9353.  
  9354.   262   ERROR_TOO_MUCH_STACK
  9355.           Stack request exceeds system limit.
  9356.  
  9357.   263   ERROR_INVALID_ATTR
  9358.           Invalid attribute.
  9359.  
  9360.   264   ERROR_INVALID_STARTING_RING
  9361.           Invalid starting ring.
  9362.  
  9363.   265   ERROR_INVALID_DLL_INIT_RING
  9364.           Invalid DLL INIT ring.
  9365.  
  9366.   266   ERROR_CANNOT_COPY
  9367.           Cannot copy.
  9368.  
  9369.   267   ERROR_DIRECTORY
  9370.           Used by DOSCOPY in doscall1.
  9371.  
  9372.   268   ERROR_OPLOCKED_FILE
  9373.           Oplocked file.
  9374.  
  9375.   269   ERROR_OPLOCK_THREAD_EXISTS
  9376.           Oplock thread exists.
  9377.  
  9378.   270   ERROR_VOLUME_CHANGED
  9379.           Volume changed.
  9380.  
  9381.   271-273   Reserved.
  9382.  
  9383.  
  9384.   274   ERROR_ALREADY_SHUTDOWN
  9385.           System is already shut down.
  9386.  
  9387.   275   ERROR_EAS_DIDNT_FIT
  9388.           Buffer is not big enough to hold the EAs.
  9389.  
  9390.   276   ERROR_EA_FILE_CORRUPT
  9391.           EA file has been damaged.
  9392.  
  9393.   277   ERROR_EA_TABLE_FULL
  9394.           EA table is full.
  9395.  
  9396.   278   ERROR_INVALID_EA_HANDLE
  9397.           EA handle is invalid.
  9398.  
  9399.   279   ERROR_NO_CLUSTER
  9400.           No cluster.
  9401.  
  9402.   280   ERROR_CREATE_EA_FILE
  9403.           Cannot create the EA file.
  9404.  
  9405.   281   ERROR_CANNOT_OPEN_EA_FILE
  9406.           Cannot open the EA file.
  9407.  
  9408.   282   ERROR_EAS_NOT_SUPPORTED
  9409.           Destination file system does not support EAs.
  9410.  
  9411.   283   ERROR_NEED_EAS_FOUND
  9412.           Destination file system does not support EAs, and the source file's
  9413.           EAs contain a need EA.
  9414.  
  9415.   284   ERROR_DUPLICATE_HANDLE
  9416.           The handle already exists.
  9417.  
  9418.   285   ERROR_DUPLICATE_NAME
  9419.           The name already exists.
  9420.  
  9421.   286   ERROR_EMPTY_MUXWAIT
  9422.           The list of semaphores in a muxwait semaphore is empty.
  9423.  
  9424.   287   ERROR_MUTEX_OWNED
  9425.           The calling thread owns one or more of the mutex semaphores in the
  9426.           list.
  9427.  
  9428.   288   ERROR_NOT_OWNER
  9429.           Caller does not own the semaphore.
  9430.  
  9431.   289   ERROR_PARAM_TOO_SMALL
  9432.           Parameter is not large enough to contain all of the semaphore
  9433.           records in the muxwait semaphore.
  9434.  
  9435.   290   ERROR_TOO_MANY_HANDLES
  9436.           Limit reached for number of handles.
  9437.  
  9438.   291   ERROR_TOO_MANY_OPENS
  9439.           There are too many files or semaphores open.
  9440.  
  9441.   292   ERROR_WRONG_TYPE
  9442.           Attempted to create wrong type of semaphore.
  9443.  
  9444.   293   ERROR_UNUSED_CODE
  9445.           Code is not used.
  9446.  
  9447.   294   ERROR_THREAD_NOT_TERMINATED
  9448.           Thread has not terminated.
  9449.  
  9450.   295   ERROR_INIT_ROUTINE_FAILED
  9451.           Initialization routine failed.
  9452.  
  9453.   296   ERROR_MODULE_IN_USE
  9454.           Module is in use.
  9455.  
  9456.   297   ERROR_NOT_ENOUGH_WATCHPOINTS
  9457.           There are not enough watchpoints.
  9458.  
  9459.   298   ERROR_TOO_MANY_POSTS
  9460.           Post count limit was reached for an event semaphore.
  9461.  
  9462.   299   ERROR_ALREADY_POSTED
  9463.           Event semaphore is already posted.
  9464.  
  9465.   300   ERROR_ALREADY_RESET
  9466.           Event semaphore is already reset.
  9467.  
  9468.   301   ERROR_SEM_BUSY
  9469.           Semaphore is busy.
  9470.  
  9471.   302   Reserved
  9472.  
  9473.  
  9474.   303   ERROR_INVALID_PROCID
  9475.           Invalid process identity.
  9476.  
  9477.   304   ERROR_INVALID_PDELTA
  9478.           Invalid priority delta.
  9479.  
  9480.   305   ERROR_NOT_DESCENDANT
  9481.           Not descendant.
  9482.  
  9483.   306   ERROR_NOT_SESSION_MANAGER
  9484.           Requestor not session manager.
  9485.  
  9486.   307   ERROR_INVALID_PCLASS
  9487.           Invalid P class.
  9488.  
  9489.   308   ERROR_INVALID_SCOPE
  9490.           Invalid scope.
  9491.  
  9492.   309   ERROR_INVALID_THREADID
  9493.           Invalid thread identity.
  9494.  
  9495.   310   ERROR_DOSSUB_SHRINK
  9496.           Cannot shrink segment - DosSubSetMem.
  9497.  
  9498.   311   ERROR_DOSSUB_NOMEM
  9499.           No memory to satisfy request - DosSubAllocMem.
  9500.  
  9501.   312   ERROR_DOSSUB_OVERLAP
  9502.           Overlap of the specified block with a block of allocated memory -
  9503.           DosSubFreeMem.
  9504.  
  9505.   313   ERROR_DOSSUB_BADSIZE
  9506.           Invalid size parameter - DosSubAllocMem or DosSubFreeMem.
  9507.  
  9508.   314   ERROR_DOSSUB_BADFLAG
  9509.           Invalid flag parameter - DosSubSetMem.
  9510.  
  9511.   315   ERROR_DOSSUB_BADSELECTOR
  9512.           Invalid segment selector.
  9513.  
  9514.   316   ERROR_MR_MSG_TOO_LONG
  9515.           Message too long for buffer.
  9516.  
  9517.   317   ERROR_MR_MID_NOT_FOUND
  9518.           Message identity number not found.
  9519.  
  9520.   318   ERROR_MR_UN_ACC_MSGF
  9521.           Unable to access message file.
  9522.  
  9523.   319   ERROR_MR_INV_MSGF_FORMAT
  9524.           Invalid message file format.
  9525.  
  9526.   320   ERROR_MR_INV_IVCOUNT
  9527.           Invalid insertion variable count.
  9528.  
  9529.   321   ERROR_MR_UN_PERFORM
  9530.           Unable to perform function.
  9531.  
  9532.   322   ERROR_TS_WAKEUP
  9533.           Unable to wake up.
  9534.  
  9535.   323   ERROR_TS_SEMHANDLE
  9536.           Invalid system semaphore.
  9537.  
  9538.   324   ERROR_TS_NOTIMER
  9539.           No timers available.
  9540.  
  9541.   326   ERROR_TS_HANDLE
  9542.           Invalid timer handle.
  9543.  
  9544.   327   ERROR_TS_DATETIME
  9545.           Date or time invalid.
  9546.  
  9547.   328   ERROR_SYS_INTERNAL
  9548.           Internal system error.
  9549.  
  9550.   329   ERROR_QUE_CURRENT_NAME
  9551.           Current queue name does not exist.
  9552.  
  9553.   330   ERROR_QUE_PROC_NOT_OWNED
  9554.           Current process does not own queue.
  9555.  
  9556.   331   ERROR_QUE_PROC_OWNED
  9557.           Current process owns queue.
  9558.  
  9559.   332   ERROR_QUE_DUPLICATE
  9560.           Duplicate queue name.
  9561.  
  9562.   333   ERROR_QUE_ELEMENT_NOT_EXIST
  9563.           Queue element does not exist.
  9564.  
  9565.   334   ERROR_QUE_NO_MEMORY
  9566.           Inadequate queue memory.
  9567.  
  9568.   335   ERROR_QUE_INVALID_NAME
  9569.           Invalid queue name.
  9570.  
  9571.   336   ERROR_QUE_INVALID_PRIORITY
  9572.           Invalid queue priority parameter.
  9573.  
  9574.   337   ERROR_QUE_INVALID_HANDLE
  9575.           Invalid queue handle.
  9576.  
  9577.   338   ERROR_QUE_LINK_NOT_FOUND
  9578.           Queue link not found.
  9579.  
  9580.   339   ERROR_QUE_MEMORY_ERROR
  9581.           Queue memory error.
  9582.  
  9583.   340   ERROR_QUE_PREV_AT_END
  9584.           Previous queue element was at end of queue.
  9585.  
  9586.   341   ERROR_QUE_PROC_NO_ACCESS
  9587.           Process does not have access to queues.
  9588.  
  9589.   342   ERROR_QUE_EMPTY
  9590.           Queue is empty.
  9591.  
  9592.   343   ERROR_QUE_NAME_NOT_EXIST
  9593.           Queue name does not exist.
  9594.  
  9595.   344   ERROR_QUE_NOT_INITIALIZED
  9596.           Queues not initialized.
  9597.  
  9598.   345   ERROR_QUE_UNABLE_TO_ACCESS
  9599.           Unable to access queues.
  9600.  
  9601.   346   ERROR_QUE_UNABLE_TO_ADD
  9602.           Unable to add new queue.
  9603.  
  9604.   347   ERROR_QUE_UNABLE_TO_INIT
  9605.           Unable to initialize queues.
  9606.  
  9607.   349   ERROR_VIO_INVALID_MASK
  9608.           Invalid function replaced.
  9609.  
  9610.   350   ERROR_VIO_PTR
  9611.           Invalid pointer to parameter.
  9612.  
  9613.   351   ERROR_VIO_APTR
  9614.           Invalid pointer to attribute.
  9615.  
  9616.   352   ERROR_VIO_RPTR
  9617.           Invalid pointer to row.
  9618.  
  9619.   353   ERROR_VIO_CPTR
  9620.           Invalid pointer to column.
  9621.  
  9622.   354   ERROR_VIO_LPTR
  9623.           Invalid pointer to length.
  9624.  
  9625.   355   ERROR_VIO_MODE
  9626.           Unsupported screen mode.
  9627.  
  9628.   356   ERROR_VIO_WIDTH
  9629.           Invalid cursor width value.
  9630.  
  9631.   357   ERROR_VIO_ATTR
  9632.           Invalid cursor attribute value.
  9633.  
  9634.   358   ERROR_VIO_ROW
  9635.           Invalid row value.
  9636.  
  9637.   359   ERROR_VIO_COL
  9638.           Invalid column value.
  9639.  
  9640.   360   ERROR_VIO_TOPROW
  9641.           Invalid TopRow value.
  9642.  
  9643.   361   ERROR_VIO_BOTROW
  9644.           Invalid BotRow value.
  9645.  
  9646.   362   ERROR_VIO_RIGHTCOL
  9647.           Invalid right column value.
  9648.  
  9649.   363   ERROR_VIO_LEFTCOL
  9650.           Invalid left column value.
  9651.  
  9652.   364   ERROR_SCS_CALL
  9653.           Call issued by other than session manager.
  9654.  
  9655.   365   ERROR_SCS_VALUE
  9656.           Value is not for save or restore.
  9657.  
  9658.   366   ERROR_VIO_WAIT_FLAG
  9659.           Invalid wait flag setting.
  9660.  
  9661.   367   ERROR_VIO_UNLOCK
  9662.           Screen not previously locked.
  9663.  
  9664.   368   ERROR_SGS_NOT_SESSION_MGR
  9665.           Caller not session manager.
  9666.  
  9667.   369   ERROR_SMG_INVALID_SGID
  9668.           Invalid session identity.
  9669.  
  9670.   369   ERROR_SMG_INVALID_SESSION_ID
  9671.           Invalid session ID.
  9672.  
  9673.   370   ERROR_SMG_NOSG
  9674.           No sessions available.
  9675.  
  9676.   370   ERROR_SMG_NO_SESSIONS
  9677.           No sessions available.
  9678.  
  9679.   371   ERROR_SMG_GRP_NOT_FOUND
  9680.           Session not found.
  9681.  
  9682.   371   ERROR_SMG_SESSION_NOT_FOUND
  9683.           Session not found.
  9684.  
  9685.   372   ERROR_SMG_SET_TITLE
  9686.           Title sent by shell or parent cannot be changed.
  9687.  
  9688.   373   ERROR_KBD_PARAMETER
  9689.           Invalid parameter to keyboard.
  9690.  
  9691.   374   ERROR_KBD_NO_DEVICE
  9692.           No device.
  9693.  
  9694.   375   ERROR_KBD_INVALID_IOWAIT
  9695.           Invalid I/O wait specified.
  9696.  
  9697.   376   ERROR_KBD_INVALID_LENGTH
  9698.           Invalid length for keyboard.
  9699.  
  9700.   377   ERROR_KBD_INVALID_ECHO_MASK
  9701.           Invalid echo mode mask.
  9702.  
  9703.   378   ERROR_KBD_INVALID_INPUT_MASK
  9704.           Invalid input mode mask.
  9705.  
  9706.   379   ERROR_MON_INVALID_PARMS
  9707.           Invalid parameters to DosMon.
  9708.  
  9709.   380   ERROR_MON_INVALID_DEVNAME
  9710.           Invalid device name string.
  9711.  
  9712.   381   ERROR_MON_INVALID_HANDLE
  9713.           Invalid device handle.
  9714.  
  9715.   382   ERROR_MON_BUFFER_TOO_SMALL
  9716.           Buffer too small.
  9717.  
  9718.   383   ERROR_MON_BUFFER_EMPTY
  9719.           Buffer is empty.
  9720.  
  9721.   384   ERROR_MON_DATA_TOO_LARGE
  9722.           Data record is too large.
  9723.  
  9724.   385   ERROR_MOUSE_NO_DEVICE
  9725.           Mouse device closed (invalid device handle).
  9726.  
  9727.   386   ERROR_MOUSE_INV_HANDLE
  9728.           Mouse device closed (invalid device handle).
  9729.  
  9730.   387   ERROR_MOUSE_INV_PARMS
  9731.           Parameters invalid for display mode.
  9732.  
  9733.   388   ERROR_MOUSE_CANT_RESET
  9734.           Function assigned and cannot be reset.
  9735.  
  9736.   389   ERROR_MOUSE_DISPLAY_PARMS
  9737.           Parameters invalid for display mode.
  9738.  
  9739.   390   ERROR_MOUSE_INV_MODULE
  9740.           Module not valid.
  9741.  
  9742.   391   ERROR_MOUSE_INV_ENTRY_PT
  9743.           Entry point not valid.
  9744.  
  9745.   392   ERROR_MOUSE_INV_MASK
  9746.           Function mask invalid.
  9747.  
  9748.   393   NO_ERROR_MOUSE_NO_DATA
  9749.           No valid data.
  9750.  
  9751.   394   NO_ERROR_MOUSE_PTR_DRAWN
  9752.           Pointer drawn.
  9753.  
  9754.   395   ERROR_INVALID_FREQUENCY
  9755.           Invalid frequency for beep.
  9756.  
  9757.   396   ERROR_NLS_NO_COUNTRY_FILE
  9758.           Cannot find COUNTRY.SYS file.
  9759.  
  9760.   397   ERROR_NLS_OPEN_FAILED
  9761.           Cannot open COUNTRY.SYS file.
  9762.  
  9763.   398   ERROR_NLS_NO_CTRY_CODE
  9764.           Country code not found.
  9765.  
  9766.   398   ERROR_NO_COUNTRY_OR_CODEPAGE
  9767.           Country code not found.
  9768.  
  9769.   399   ERROR_NLS_TABLE_TRUNCATED
  9770.           Table returned information truncated, buffer is too small.
  9771.  
  9772.   400   ERROR_NLS_BAD_TYPE
  9773.           Selected type does not exist.
  9774.  
  9775.   401   ERROR_NLS_TYPE_NOT_FOUND
  9776.           Selected type is not in file.
  9777.  
  9778.   402   ERROR_VIO_SMG_ONLY
  9779.           Valid from session manager only.
  9780.  
  9781.   403   ERROR_VIO_INVALID_ASCIIZ
  9782.           Invalid ASCIIZ length.
  9783.  
  9784.   404   ERROR_VIO_DEREGISTER
  9785.           VioDeRegister not allowed.
  9786.  
  9787.   405   ERROR_VIO_NO_POPUP
  9788.           Pop-up window not allocated.
  9789.  
  9790.   406   ERROR_VIO_EXISTING_POPUP
  9791.           Pop-up window on screen (NoWait).
  9792.  
  9793.   407   ERROR_KBD_SMG_ONLY
  9794.           Valid from session manager only.
  9795.  
  9796.   408   ERROR_KBD_INVALID_ASCIIZ
  9797.           Invalid ASCIIZ length.
  9798.  
  9799.   409   ERROR_KBD_INVALID_MASK
  9800.           Invalid replacement mask.
  9801.  
  9802.   410   ERROR_KBD_REGISTER
  9803.           KbdRegister not allowed.
  9804.  
  9805.   411   ERROR_KBD_DEREGISTER
  9806.           KbdDeRegister not allowed.
  9807.  
  9808.   412   ERROR_MOUSE_SMG_ONLY
  9809.           Valid from session manager only.
  9810.  
  9811.   413   ERROR_MOUSE_INVALID_ASCIIZ
  9812.           Invalid ASCIIZ length.
  9813.  
  9814.   414   ERROR_MOUSE_INVALID_MASK
  9815.           Invalid replacement mask.
  9816.  
  9817.   415   ERROR_MOUSE_REGISTER
  9818.           Mouse register not allowed.
  9819.  
  9820.   416   ERROR_MOUSE_DEREGISTER
  9821.           Mouse deregister not allowed.
  9822.  
  9823.   417   ERROR_SMG_BAD_ACTION
  9824.           Invalid action specified.
  9825.  
  9826.   418   ERROR_SMG_INVALID_CALL
  9827.           INIT called more than once, or invalid session identity.
  9828.  
  9829.   419   ERROR_SCS_SG_NOTFOUND
  9830.           New session number.
  9831.  
  9832.   420   ERROR_SCS_NOT_SHELL
  9833.           Caller is not shell.
  9834.  
  9835.   421   ERROR_VIO_INVALID_PARMS
  9836.           Invalid parameters passed.
  9837.  
  9838.   422   ERROR_VIO_FUNCTION_OWNED
  9839.           Save/restore already owned.
  9840.  
  9841.   423   ERROR_VIO_RETURN
  9842.           Non-destruct return (undo).
  9843.  
  9844.   424   ERROR_SCS_INVALID_FUNCTION
  9845.           Caller invalid function.
  9846.  
  9847.   425   ERROR_SCS_NOT_SESSION_MGR
  9848.           Caller not session manager.
  9849.  
  9850.   426   ERROR_VIO_REGISTER
  9851.           Vio register not allowed.
  9852.  
  9853.   427   ERROR_VIO_NO_MODE_THREAD
  9854.           No mode restore thread in SG.
  9855.  
  9856.   428   ERROR_VIO_NO_SAVE_RESTORE_THD
  9857.           No save/restore thread in SG.
  9858.  
  9859.   429   ERROR_VIO_IN_BG
  9860.           Function invalid in background.
  9861.  
  9862.   430   ERROR_VIO_ILLEGAL_DURING_POPUP
  9863.           Function not allowed during pop-up window.
  9864.  
  9865.   431   ERROR_SMG_NOT_BASESHELL
  9866.           Caller is not the base shell.
  9867.  
  9868.   432   ERROR_SMG_BAD_STATUSREQ
  9869.           Invalid status requested.
  9870.  
  9871.   433   ERROR_QUE_INVALID_WAIT
  9872.           NoWait parameter out of bounds.
  9873.  
  9874.   434   ERROR_VIO_LOCK
  9875.           Error returned from Scroll Lock.
  9876.  
  9877.   435   ERROR_MOUSE_INVALID_IOWAIT
  9878.           Invalid parameters for IOWait.
  9879.  
  9880.   436   ERROR_VIO_INVALID_HANDLE
  9881.           Invalid VIO handle.
  9882.  
  9883.   437   ERROR_VIO_ILLEGAL_DURING_LOCK
  9884.           Function not allowed during screen lock.
  9885.  
  9886.   438   ERROR_VIO_INVALID_LENGTH
  9887.           Invalid VIO length.
  9888.  
  9889.   439   ERROR_KBD_INVALID_HANDLE
  9890.           Invalid KBD handle.
  9891.  
  9892.   440   ERROR_KBD_NO_MORE_HANDLE
  9893.           Ran out of handles.
  9894.  
  9895.   441   ERROR_KBD_CANNOT_CREATE_KCB
  9896.           Unable to create kcb.
  9897.  
  9898.   442   ERROR_KBD_CODEPAGE_LOAD_INCOMPL
  9899.           Unsuccessful code-page load.
  9900.  
  9901.   443   ERROR_KBD_INVALID_CODEPAGE_ID
  9902.           Invalid code-page identity.
  9903.  
  9904.   444   ERROR_KBD_NO_CODEPAGE_SUPPORT
  9905.           No code page support.
  9906.  
  9907.   445   ERROR_KBD_FOCUS_REQUIRED
  9908.           Keyboard focus required.
  9909.  
  9910.   446   ERROR_KBD_FOCUS_ALREADY_ACTIVE
  9911.           Calling thread has an outstanding focus.
  9912.  
  9913.   447   ERROR_KBD_KEYBOARD_BUSY
  9914.           Keyboard is busy.
  9915.  
  9916.   448   ERROR_KBD_INVALID_CODEPAGE
  9917.           Invalid code page.
  9918.  
  9919.   449   ERROR_KBD_UNABLE_TO_FOCUS
  9920.           Focus attempt failed.
  9921.  
  9922.   450   ERROR_SMG_SESSION_NON_SELECT
  9923.           Session is not selectable.
  9924.  
  9925.   451   ERROR_SMG_SESSION_NOT_FOREGRND
  9926.           Parent/child session is not foreground.
  9927.  
  9928.   452   ERROR_SMG_SESSION_NOT_PARENT
  9929.           Not parent of requested child.
  9930.  
  9931.   453   ERROR_SMG_INVALID_START_MODE
  9932.           Invalid session start mode.
  9933.  
  9934.   454   ERROR_SMG_INVALID_RELATED_OPT
  9935.           Invalid session start related option.
  9936.  
  9937.   455   ERROR_SMG_INVALID_BOND_OPTION
  9938.           Invalid session bond option.
  9939.  
  9940.   456   ERROR_SMG_INVALID_SELECT_OPT
  9941.           Invalid session select option.
  9942.  
  9943.   457   ERROR_SMG_START_IN_BACKGROUND
  9944.           Session started in background.
  9945.  
  9946.   458   ERROR_SMG_INVALID_STOP_OPTION
  9947.           Invalid session stop option.
  9948.  
  9949.   459   ERROR_SMG_BAD_RESERVE
  9950.           Reserved parameters are not zero.
  9951.  
  9952.   460   ERROR_SMG_PROCESS_NOT_PARENT
  9953.           Session parent process already exists.
  9954.  
  9955.   461   ERROR_SMG_INVALID_DATA_LENGTH
  9956.           Invalid data length.
  9957.  
  9958.   462   ERROR_SMG_NOT_BOUND
  9959.           Parent is not bound.
  9960.  
  9961.   463   ERROR_SMG_RETRY_SUB_ALLOC
  9962.           Retry request block allocation.
  9963.  
  9964.   464   ERROR_KBD_DETACHED
  9965.           This call is not allowed for a detached PID.
  9966.  
  9967.   465   ERROR_VIO_DETACHED
  9968.           This call is not allowed for a detached PID.
  9969.  
  9970.   466   ERROR_MOU_DETACHED
  9971.           This call is not allowed for a detached PID.
  9972.  
  9973.   467   ERROR_VIO_FONT
  9974.           No font is available to support the mode.
  9975.  
  9976.   468   ERROR_VIO_USER_FONT
  9977.           User font is active.
  9978.  
  9979.   469   ERROR_VIO_BAD_CP
  9980.           Invalid code page specified.
  9981.  
  9982.   470   ERROR_VIO_NO_CP
  9983.           System displays do not support code page.
  9984.  
  9985.   471   ERROR_VIO_NA_CP
  9986.           Current display does not support code page.
  9987.  
  9988.   472   ERROR_INVALID_CODE_PAGE
  9989.           Invalid code page.
  9990.  
  9991.   473   ERROR_CPLIST_TOO_SMALL
  9992.           Code page list is too small.
  9993.  
  9994.   474   ERROR_CP_NOT_MOVED
  9995.           Code page was not moved.
  9996.  
  9997.   475   ERROR_MODE_SWITCH_INIT
  9998.           Mode switch initialization error.
  9999.  
  10000.   476   ERROR_CODE_PAGE_NOT_FOUND
  10001.           Code page was not found.
  10002.  
  10003.   477   ERROR_UNEXPECTED_SLOT_RETURNED
  10004.           Internal error.
  10005.  
  10006.   478   ERROR_SMG_INVALID_TRACE_OPTION
  10007.           Invalid start session trace indicator.
  10008.  
  10009.   479   ERROR_VIO_INTERNAL_RESOURCE
  10010.           VIO internal resource error.
  10011.  
  10012.   480   ERROR_VIO_SHELL_INIT
  10013.           VIO shell initialization error.
  10014.  
  10015.   481   ERROR_SMG_NO_HARD_ERRORS
  10016.           No session manager hard errors.
  10017.  
  10018.   482   ERROR_CP_SWITCH_INCOMPLETE
  10019.           DosSetProcessCp is unable to set a KBD or VIO code page.
  10020.  
  10021.   483   ERROR_VIO_TRANSPARENT_POPUP
  10022.           Error during VIO pop-up window.
  10023.  
  10024.   484   ERROR_CRITSEC_OVERFLOW
  10025.           Critical section overflow.
  10026.  
  10027.   485   ERROR_CRITSEC_UNDERFLOW
  10028.           Critical section underflow.
  10029.  
  10030.   486   ERROR_VIO_BAD_RESERVE
  10031.           Reserved parameter is not zero.
  10032.  
  10033.   487   ERROR_INVALID_ADDRESS
  10034.           Invalid physical address.
  10035.  
  10036.   488   ERROR_ZERO_SELECTORS_REQUESTED
  10037.           At least one selector must be requested.
  10038.  
  10039.   489   ERROR_NOT_ENOUGH_SELECTORS_AVA
  10040.           Not enough GDT selectors to satisfy request.
  10041.  
  10042.   490   ERROR_INVALID_SELECTOR
  10043.           Not a GDT selector.
  10044.  
  10045.   491   ERROR_SMG_INVALID_PROGRAM_TYPE
  10046.           Invalid program type.
  10047.  
  10048.   492   ERROR_SMG_INVALID_PGM_CONTROL
  10049.           Invalid program control.
  10050.  
  10051.   493   ERROR_SMG_INVALID_INHERIT_OPT
  10052.           Invalid inherit option.
  10053.  
  10054.   494   ERROR_VIO_EXTENDED_SG
  10055.  
  10056.  
  10057.   495   ERROR_VIO_NOT_PRES_MGR_SG
  10058.  
  10059.  
  10060.   496   ERROR_VIO_SHIELD_OWNED
  10061.  
  10062.  
  10063.   497   ERROR_VIO_NO_MORE_HANDLES
  10064.  
  10065.  
  10066.   498   ERROR_VIO_SEE_ERROR_LOG
  10067.  
  10068.  
  10069.   499   ERROR_VIO_ASSOCIATED_DC
  10070.  
  10071.  
  10072.   500   ERROR_KBD_NO_CONSOLE
  10073.  
  10074.  
  10075.   501   ERROR_MOUSE_NO_CONSOLE
  10076.  
  10077.  
  10078.   502   ERROR_MOUSE_INVALID_HANDLE
  10079.  
  10080.  
  10081.   503   ERROR_SMG_INVALID_DEBUG_PARMS
  10082.  
  10083.  
  10084.   504   ERROR_KBD_EXTENDED_SG
  10085.  
  10086.  
  10087.   505   ERROR_MOU_EXTENDED_SG
  10088.  
  10089.  
  10090.   506   ERROR_SMG_INVALID_ICON_FILE
  10091.  
  10092.  
  10093.   507   ERROR_TRC_PID_NON_EXISTENT
  10094.  
  10095.  
  10096.   508   ERROR_TRC_COUNT_ACTIVE
  10097.  
  10098.  
  10099.   509   ERROR_TRC_SUSPENDED_BY_COUNT
  10100.  
  10101.  
  10102.   510   ERROR_TRC_COUNT_INACTIVE
  10103.  
  10104.  
  10105.   511   ERROR_TRC_COUNT_REACHED
  10106.  
  10107.  
  10108.   512   ERROR_NO_MC_TRACE
  10109.  
  10110.  
  10111.   513   ERROR_MC_TRACE
  10112.  
  10113.  
  10114.   514   ERROR_TRC_COUNT_ZERO
  10115.  
  10116.  
  10117.   515   ERROR_SMG_TOO_MANY_DDS
  10118.  
  10119.  
  10120.   516   ERROR_SMG_INVALID_NOTIFICATION
  10121.  
  10122.  
  10123.   517   ERROR_LF_INVALID_FUNCTION
  10124.  
  10125.  
  10126.   518   ERROR_LF_NOT_AVAIL
  10127.  
  10128.  
  10129.   519   ERROR_LF_SUSPENDED
  10130.  
  10131.  
  10132.   520   ERROR_LF_BUF_TOO_SMALL
  10133.  
  10134.  
  10135.   521   ERROR_LF_BUFFER_CORRUPTED
  10136.  
  10137.  
  10138.   521   ERROR_LF_BUFFER_FULL
  10139.  
  10140.  
  10141.   522   ERROR_LF_INVALID_DAEMON
  10142.  
  10143.  
  10144.   522   ERROR_LF_INVALID_RECORD
  10145.  
  10146.  
  10147.   523   ERROR_LF_INVALID_TEMPL
  10148.  
  10149.  
  10150.   523   ERROR_LF_INVALID_SERVICE
  10151.  
  10152.  
  10153.   524   ERROR_LF_GENERAL_FAILURE
  10154.  
  10155.  
  10156.   525   ERROR_LF_INVALID_ID
  10157.  
  10158.  
  10159.   526   ERROR_LF_INVALID_HANDLE
  10160.  
  10161.  
  10162.   527   ERROR_LF_NO_ID_AVAIL
  10163.  
  10164.  
  10165.   528   ERROR_LF_TEMPLATE_AREA_FULL
  10166.  
  10167.  
  10168.   529   ERROR_LF_ID_IN_USE
  10169.  
  10170.  
  10171.   530   ERROR_MOU_NOT_INITIALIZED
  10172.  
  10173.  
  10174.   531   ERROR_MOUINITREAL_DONE
  10175.  
  10176.  
  10177.   532   ERROR_DOSSUB_CORRUPTED
  10178.  
  10179.  
  10180.   533   ERROR_MOUSE_CALLER_NOT_SUBSYS
  10181.  
  10182.  
  10183.   534   ERROR_ARITHMETIC_OVERFLOW
  10184.  
  10185.  
  10186.   535   ERROR_TMR_NO_DEVICE
  10187.  
  10188.  
  10189.   536   ERROR_TMR_INVALID_TIME
  10190.  
  10191.  
  10192.   537   ERROR_PVW_INVALID_ENTITY
  10193.  
  10194.  
  10195.   538   ERROR_PVW_INVALID_ENTITY_TYPE
  10196.  
  10197.  
  10198.   539   ERROR_PVW_INVALID_SPEC
  10199.  
  10200.  
  10201.   540   ERROR_PVW_INVALID_RANGE_TYPE
  10202.  
  10203.  
  10204.   541   ERROR_PVW_INVALID_COUNTER_BLK
  10205.  
  10206.  
  10207.   542   ERROR_PVW_INVALID_TEXT_BLK
  10208.  
  10209.  
  10210.   543   ERROR_PRF_NOT_INITIALIZED
  10211.  
  10212.  
  10213.   544   ERROR_PRF_ALREADY_INITIALIZED
  10214.  
  10215.  
  10216.   545   ERROR_PRF_NOT_STARTED
  10217.  
  10218.  
  10219.   546   ERROR_PRF_ALREADY_STARTED
  10220.  
  10221.  
  10222.   547   ERROR_PRF_TIMER_OUT_OF_RANGE
  10223.  
  10224.  
  10225.   548   ERROR_PRF_TIMER_RESET
  10226.  
  10227.  
  10228.   639   ERROR_VDD_LOCK_USEAGE_DENIED
  10229.  
  10230.  
  10231.   640   ERROR_TIMEOUT
  10232.  
  10233.  
  10234.   641   ERROR_VDM_DOWN
  10235.  
  10236.  
  10237.   642   ERROR_VDM_LIMIT
  10238.  
  10239.  
  10240.   643   ERROR_VDD_NOT_FOUND
  10241.  
  10242.  
  10243.   644   ERROR_INVALID_CALLER
  10244.  
  10245.  
  10246.   645   ERROR_PID_MISMATCH
  10247.  
  10248.  
  10249.   646   ERROR_INVALID_VDD_HANDLE
  10250.  
  10251.  
  10252.   647   ERROR_VLPT_NO_SPOOLER
  10253.  
  10254.  
  10255.   648   ERROR_VCOM_DEVICE_BUSY
  10256.  
  10257.  
  10258.   649   ERROR_VLPT_DEVICE_BUSY
  10259.  
  10260.  
  10261.   650   ERROR_NESTING_TOO_DEEP
  10262.  
  10263.  
  10264.   651   ERROR_VDD_MISSING
  10265.  
  10266.  
  10267.   691   ERROR_IMP_INVALID_PARM
  10268.  
  10269.  
  10270.   692   ERROR_IMP_INVALID_LENGTH
  10271.  
  10272.  
  10273.   693   MSG_HPFS_DISK_ERROR_WARN
  10274.  
  10275.  
  10276.   730   ERROR_MON_BAD_BUFFER
  10277.  
  10278.  
  10279.   731   ERROR_MODULE_CORRUPTED
  10280.  
  10281.  
  10282.   2055   ERROR_LF_TIMEOUT
  10283.  
  10284.  
  10285.   2057   ERROR_LF_SUSPEND_SUCCESS
  10286.  
  10287.  
  10288.   2058   ERROR_LF_RESUME_SUCCESS
  10289.  
  10290.  
  10291.   2059   ERROR_LF_REDIRECT_SUCCESS
  10292.  
  10293.  
  10294.   2060   ERROR_LF_REDIRECT_FAILURE
  10295.  
  10296.  
  10297.   32768   ERROR_SWAPPER_NOT_ACTIVE
  10298.  
  10299.  
  10300.   32769   ERROR_INVALID_SWAPID
  10301.  
  10302.  
  10303.   32770   ERROR_IOERR_SWAP_FILE
  10304.  
  10305.  
  10306.   32771   ERROR_SWAP_TABLE_FULL
  10307.  
  10308.  
  10309.   32772   ERROR_SWAP_FILE_FULL
  10310.  
  10311.  
  10312.   32773   ERROR_CANT_INIT_SWAPPER
  10313.  
  10314.  
  10315.   32774   ERROR_SWAPPER_ALREADY_INIT
  10316.  
  10317.  
  10318.   32775   ERROR_PMM_INSUFFICIENT_MEMORY
  10319.  
  10320.  
  10321.   32776   ERROR_PMM_INVALID_FLAGS
  10322.  
  10323.  
  10324.   32777   ERROR_PMM_INVALID_ADDRESS
  10325.  
  10326.  
  10327.   32778   ERROR_PMM_LOCK_FAILED
  10328.  
  10329.  
  10330.   32779   ERROR_PMM_UNLOCK_FAILED
  10331.  
  10332.  
  10333.   32780   ERROR_PMM_MOVE_INCOMPLETE
  10334.  
  10335.  
  10336.   32781   ERROR_UCOM_DRIVE_RENAMED
  10337.  
  10338.  
  10339.   32782   ERROR_UCOM_FILENAME_TRUNCATED
  10340.  
  10341.  
  10342.   32783   ERROR_UCOM_BUFFER_LENGTH
  10343.  
  10344.  
  10345.   32784   ERROR_MON_CHAIN_HANDLE
  10346.  
  10347.  
  10348.   32785   ERROR_MON_NOT_REGISTERED
  10349.  
  10350.  
  10351.   32786   ERROR_SMG_ALREADY_TOP
  10352.  
  10353.  
  10354.   32787   ERROR_PMM_ARENA_MODIFIED
  10355.  
  10356.  
  10357.   32788   ERROR_SMG_PRINTER_OPEN
  10358.  
  10359.  
  10360.   32789   ERROR_PMM_SET_FLAGS_FAILED
  10361.  
  10362.  
  10363.   32790   ERROR_INVALID_DOS_DD
  10364.  
  10365.  
  10366.   32791   ERROR_BLOCKED
  10367.  
  10368.  
  10369.   32792   ERROR_NOBLOCK
  10370.  
  10371.  
  10372.   32793   ERROR_INSTANCE_SHARED
  10373.  
  10374.  
  10375.   32794   ERROR_NO_OBJECT
  10376.  
  10377.  
  10378.   32795   ERROR_PARTIAL_ATTACH
  10379.  
  10380.  
  10381.   32796   ERROR_INCACHE
  10382.  
  10383.  
  10384.   32797   ERROR_SWAP_IO_PROBLEMS
  10385.  
  10386.  
  10387.   32798   ERROR_CROSSES_OBJECT_BOUNDARY
  10388.  
  10389.  
  10390.   32799   ERROR_LONGLOCK
  10391.  
  10392.  
  10393.   32800   ERROR_SHORTLOCK
  10394.  
  10395.  
  10396.   32801   ERROR_UVIRTLOCK
  10397.  
  10398.  
  10399.   32802   ERROR_ALIASLOCK
  10400.  
  10401.  
  10402.   32803   ERROR_ALIAS
  10403.  
  10404.  
  10405.   32804   ERROR_NO_MORE_HANDLES
  10406.  
  10407.  
  10408.   32805   ERROR_SCAN_TERMINATED
  10409.  
  10410.  
  10411.   32806   ERROR_TERMINATOR_NOT_FOUND
  10412.  
  10413.  
  10414.   32807   ERROR_NOT_DIRECT_CHILD
  10415.  
  10416.  
  10417.   32808   ERROR_DELAY_FREE
  10418.  
  10419.  
  10420.   32809   ERROR_GUARDPAGE
  10421.  
  10422.  
  10423.   32900   ERROR_SWAPERROR
  10424.  
  10425.  
  10426.   32901   ERROR_LDRERROR
  10427.  
  10428.  
  10429.   32902   ERROR_NOMEMORY
  10430.  
  10431.  
  10432.   32903   ERROR_NOACCESS
  10433.  
  10434.  
  10435.   32904   ERROR_NO_DLL_TERM
  10436.  
  10437.  
  10438.   65026   ERROR_CPSIO_CODE_PAGE_INVALID
  10439.  
  10440.  
  10441.   65027   ERROR_CPSIO_NO_SPOOLER
  10442.  
  10443.  
  10444.   65028   ERROR_CPSIO_FONT_ID_INVALID
  10445.  
  10446.  
  10447.   65033   ERROR_CPSIO_INTERNAL_ERROR
  10448.  
  10449.  
  10450.   65034   ERROR_CPSIO_INVALID_PTR_NAME
  10451.  
  10452.  
  10453.   65037   ERROR_CPSIO_NOT_ACTIVE
  10454.  
  10455.  
  10456.   65039   ERROR_CPSIO_PID_FULL
  10457.  
  10458.  
  10459.   65040   ERROR_CPSIO_PID_NOT_FOUND
  10460.  
  10461.  
  10462.   65043   ERROR_CPSIO_READ_CTL_SEQ
  10463.  
  10464.  
  10465.   65045   ERROR_CPSIO_READ_FNT_DEF
  10466.  
  10467.  
  10468.   65047   ERROR_CPSIO_WRITE_ERROR
  10469.  
  10470.  
  10471.   65048   ERROR_CPSIO_WRITE_FULL_ERROR
  10472.  
  10473.  
  10474.   65049   ERROR_CPSIO_WRITE_HANDLE_BAD
  10475.  
  10476.  
  10477.   65074   ERROR_CPSIO_SWIT_LOAD
  10478.  
  10479.  
  10480.   65077   ERROR_CPSIO_INV_COMMAND
  10481.  
  10482.  
  10483.   65078   ERROR_CPSIO_NO_FONT_SWIT
  10484.  
  10485.  
  10486.   65079   ERROR_ENTRY_IS_CALLGATE
  10487.  
  10488.  
  10489. ΓòÉΓòÉΓòÉ 13. Specifications ΓòÉΓòÉΓòÉ
  10490.  
  10491. Specifications covered: 
  10492.  
  10493. Γûá API Calls Made 
  10494.  
  10495. Γûá Memory Usage 
  10496.  
  10497. Γûá IX3 File Format 
  10498.  
  10499. Γûá DBF File Format 
  10500.  
  10501. Γûá DBT File Format 
  10502.  
  10503. Γûá Custom Sort-Compare Function 
  10504.  
  10505. Γûá Custom Build-Key 
  10506.  
  10507. Γûá Custom Expression Parser 
  10508.  
  10509.  
  10510. ΓòÉΓòÉΓòÉ 13.1. OS/2 API Calls Made ΓòÉΓòÉΓòÉ
  10511.  
  10512. The following are the API calls made by Bullet. 
  10513.  
  10514.  DosAllocMem             DosClose                 DosCopy 
  10515.  DosCloseMutexSem        DosCreateDir             DosCreateMutexSem 
  10516.  DosDelete (*)           DosErrClass              DosExitList 
  10517.  DosForceDelete          DosFreeMem               DosGetDateTime 
  10518.  DosMapCase              DosOpen                  DosQueryCollate 
  10519.  DosQueryCp              DosQueryCtryInfo         DosQueryCurrentDisk 
  10520.  DosQueryFSAttach        DosQueryHType            DosQuerySysInfo 
  10521.  DosMove                 DosRead                  DosReleaseMutexSem 
  10522.  DosRequestMutexSem      DosResetBuffer           DosScanEnv 
  10523.  DosSetFileLocks         DosSetFilePtr            DosSetFileSize 
  10524.  DosSetMaxFH             DosSetRelMaxFH           DosWrite 
  10525.  
  10526.  (*) DosForceDelete is used in favor of DosDelete. 
  10527.  
  10528.  
  10529. ΓòÉΓòÉΓòÉ 13.2. Bullet Memory Usage ΓòÉΓòÉΓòÉ
  10530.  
  10531. Memory is committed when allocated, using the PAG_COMMIT and the PAG_WRITE 
  10532. flags.  This is memory allocated by Bullet itself.  Additional memory needs are 
  10533. made by your code, such as parameter pack data, key buffers, and data record 
  10534. buffers. 
  10535.  
  10536. Code 
  10537.  
  10538. Bullet uses 8 pages for code, or less than 32KB. 
  10539.  
  10540. Data 
  10541.  
  10542. Γûá  Shared Data 
  10543.  
  10544. A single page of shared memory is used by all Bullet processes. 
  10545.  
  10546. Γûá  Instance Data 
  10547.  
  10548. One page of private memory is used by each Bullet processes. 
  10549.  
  10550. Γûá  Handle Data 
  10551.  
  10552. One page of private memory is used by each open Bullet index file.  For open 
  10553. data files, one page of private memory is used for files with 121 or fewer 
  10554. fields.  Two pages are used for files with 249 or fewer fields. Thereafter, one 
  10555. page for each additional 128 fields, up to 1024 max fields. 
  10556.  
  10557. For example, if one machine is running 2 Bullet processes, each with 10 open 
  10558. data files with 12 fields each, and 10 index files (one for each data file), 
  10559. its total memory usage is: 
  10560.  
  10561.      Total code is 32KB. 
  10562.      Shared data is 4KB. 
  10563.      Instance data is 2 processes * 4KB, or 8KB (plus INIT_XB use shown 
  10564.       below). 
  10565.      DBF file data is 2 * 10 files * 4KB, or 80KB. 
  10566.      Index file data is 2 * 10 files * 4KB, or 80KB. 
  10567.  
  10568.  Total memory committed by Bullet for the above is 200KB, plus code and data of 
  10569.  your two applications (or your single application, if the same application is 
  10570.  being run twice).  With no files open, for example when starting your program, 
  10571.  only 40KB is committed.  Thereafter, 4KB per open file.  The memory is freed 
  10572.  when the file is closed. 
  10573.  
  10574.  Γûá  Temporary Data 
  10575.  
  10576.  Additional memory is allocated on a temporary basis, where the allocation is 
  10577.  requested on entry to, and is freed upon exiting from, the routine called. 
  10578.  INIT_XB's allocation can be considered permanent since its allocations are not 
  10579.  released until the program has ended or EXIT_XB is issued.  The following are 
  10580.  the routines and the single requested amount: 
  10581.  
  10582.      Routine          Memory Allocated, in KB
  10583.  
  10584.   INIT_XB             8 for 1024 MAX_FILES version; 4KB for 100 and 250 MAX_FILES versions
  10585.   BACKUP_FILE_XB      8
  10586.   CREATE_DATA_XB      4 for 1-121 fields, 8 for 122-249 fields, ...
  10587.   CREATE_INDEX_XB     4
  10588.   PACK_RECORDS_XB     adjustable, 128 default (less if file is smaller)
  10589.   REINDEX_XB          adjustable, 144 default (minimum size is 48KB)
  10590.   UPDATE_XB           varies:      40 + sum of record lengths where AP[].recNo!=0
  10591.  
  10592.  Γûá  Stack Data 
  10593.  
  10594.  Stack requirements are 8KB minimum for Bullet.  No single stack allocation 
  10595.  requests more than 4KB at a time (ie all changes to ESP (the CPU stack 
  10596.  pointer) are less than 4KB at any one time), but some routines nest and 
  10597.  require up to the minimum 8KB in total.  The minimum recommended stack size 
  10598.  for your Bullet application is 16KB.  It's likely that you need to use a much 
  10599.  larger size for your main program's stack use.  If you have any doubt about 
  10600.  stack space, double it, twice even. 
  10601.  
  10602.  
  10603. ΓòÉΓòÉΓòÉ 13.3. IX3 File Format ΓòÉΓòÉΓòÉ
  10604.  
  10605. The IX3 index file is composed of a header followed by node data.  The header 
  10606. layout is detailed below, followed by the node format. 
  10607.  
  10608. Index Header 
  10609.  
  10610. // nnn, where nnn is the offset of that item relative to the start of the file
  10611.  
  10612. CHAR fileID[4];     // 000 file id = '31ch'
  10613. ULONG nodeSize;     // 004 size of a node, in bytes
  10614. ULONG rootNode;     // 008 root node (1-based)
  10615. ULONG noKeys;       // 012 total number of keys
  10616. ULONG availNode;    // 016 next available node (link to, 0 if none, 1-based)
  10617. ULONG freeNode;     // 020 next free node
  10618. ULONG keyLength;    // 024 length of key
  10619. ULONG maxKeys;      // 028 maximum number of keys on a node
  10620. ULONG codePage;     // 032 code page from CreateIndexFile
  10621. ULONG countryCode;  // 036 country code from CreateIndexFile
  10622. ULONG sortFunction; // 040 system (1-9) or custom (10-19)
  10623.                     //     high word has flags: bit0=1 dups allowed
  10624.                     //                          bit1-15 rez
  10625.  
  10626. // Translated key expression as done by Parser during CreateIndex and Reindex.
  10627. // More details on this is in the Custom Expression Parser Specifications.
  10628. // For each key part in KH.expression a 4-byte structure is used in XLATEX:
  10629.  
  10630. typedef struct _XLATEX {
  10631.  CHAR  ftype;   // field type (C,N,L, etc.),if bit7=1 and C then do UPPER key
  10632.  CHAR  length;  // bytes to use starting at offset (never > 64)
  10633.  SHORT offset;  // byte offset into data record that length bytes are to be used
  10634. } XLATEX;
  10635.  
  10636. ULONG xlateCount;         // 044 number of key fields (64/4=16 max fields)
  10637. XLATEX xlateExpression[16]; // 048 key construct info (16 dword's worth)
  10638. CHAR  miscWorkspace[236]; // 112-347 B-tree workspace
  10639. CHAR  expression[160];    // 348 key expression, user (0-Terminated)
  10640. ULONG CTsize;             // 508 size of collate table following
  10641. CHAR  collateTable[256];  // 512 collate table, fill at CreateIndexXB
  10642. CHAR  rez[256];           // 768 to 1023 reserved (header size=1024 bytes)
  10643.  
  10644. Node Data 
  10645.  
  10646. Directly after the header the node data starts.  Each node is either 512, 1024, 
  10647. or 2048 bytes long.  Each node contains a key count, indicating the number of 
  10648. active keys on the node, followed by key data. 
  10649.  
  10650. // nnn, where nnn is the offset of that item relative to the start of the node
  10651.  
  10652. CHAR  keyCount;         // 000 1 to maxKeys (in header above)
  10653. ULONG backNode;         // 001 previous node page, or 0 if this node is a leaf
  10654. XNODE node[maxKeys];    // 005...
  10655.  
  10656. For each key on the node: 
  10657.  
  10658. typedef struct _XNODE {
  10659. CHAR  keyValue[keyLength]; // 005   actual key   (keyLength from header)
  10660. ULONG recordNo;            // 005+keyLength   record number for key
  10661. ULONG fwdNode;             // 005+keyLength+4   next node page, or 0 if leaf
  10662. } XNODE;
  10663.  
  10664. backNode and fwdNode are node numbers.  The first node is 1, and is located 
  10665. directly after the header.  The last node used is at header:freeNode-1.  Each 
  10666. fwdNode of a key is also the next key's backNode.  If the node has had all keys 
  10667. removed, its node number is placed on the top of the header:availNode list, and 
  10668. the first 4 bytes of the node are used as a link to the previous list top. 
  10669.  
  10670.  
  10671. ΓòÉΓòÉΓòÉ 13.4. DBF File Format ΓòÉΓòÉΓòÉ
  10672.  
  10673. The DBF data file is composed of a header, field descriptors, one per field, 
  10674. and the actual record data.  The header layout is detailed below, followed by 
  10675. the field descriptor layout and then the description of the data record. The 
  10676. standard DBF file has a record length limit of 4000 bytes.  Creating record 
  10677. lengths greater than 4000 is allowed in Bullet, but these files may not be 
  10678. recognized by other applications if you do so. 
  10679.  
  10680. DBF Header 
  10681.  
  10682. // nnn, where nnn is the offset of that item relative to the start of the file
  10683.  
  10684. CHAR  fileID;           // 000 file id byte
  10685. CHAR  lastUpdateYR;     // 001 binary year-1900
  10686. CHAR  lastUpdateMO;     // 002 binary month (1-12)
  10687. CHAR  lastUpdateDA;     // 003 binary day (1-31)
  10688. ULONG noRecords;        // 004 total number of records
  10689. SHORT headerLength;     // 008 length of data header
  10690. SHORT recordLength;     // 010 record length
  10691. SHORT nada;             // 012 reserved
  10692. CHAR  xactionFlag;      // 014 flag indicating incomplete dBASE transaction (n/a)
  10693. CHAR  encryptFlag;      // 015 flag indicating encrypted (n/a)
  10694. CHAR  filler[16];       // 016 fill to 32 bytes
  10695.  
  10696. Field Descriptors 
  10697.  
  10698. For each field, a descriptor is stored in the DBF.  The first descriptor starts 
  10699. directly after the header, at file offset 32 (the 33rd byte).  Each descriptor 
  10700. is 32 bytes.  After the last descriptor, a byte with ASCII value 13 (0x0D) is 
  10701. stored.  Following this byte, the record data starts. 
  10702.  
  10703. // nnn, where nnn is offset of item relative to the start of the descriptor
  10704.  
  10705. CHAR  fieldName[11];    // 000 ASCII, UPPER, underscore, zero-filled, (0T)
  10706. CHAR  fieldType;        // 011 UPPER C,N,D,L,M
  10707. ULONG fieldDA;          // 012 not used
  10708. CHAR  fieldLength;      // 016 1-255 bytes, depending on fieldType
  10709. CHAR  fieldDC;          // 017 places right of decimal point
  10710. SHORT altFieldLength;   // 018 alternate field length when fieldLength==0
  10711. CHAR  filler[12];       // 020 not used
  10712.  
  10713. // altFieldLength is proprietary to Bullet, and can be used if Xbase
  10714. // compatibility is not required and fields need to be larger than 255 bytes.
  10715. // To use it, set fieldLength=0 and altFieldLength to > 255 bytes.
  10716.  
  10717. Record Data 
  10718.  
  10719. The DBF data are free-form, fixed-length records.  Each data record starts with 
  10720. a one-byte 'tag' field, which is implicitly defined for all records (hence, it 
  10721. is not a formal field and has no descriptor).  Following the tag field is the 
  10722. first field of the record, and following that field (whose length is described 
  10723. in the field's descriptor) is the next field, and so on.  No separators are 
  10724. used between fields.  After the very last data record in the file, DBF 
  10725. specification dictates that an end of file marker be placed, so at the end of 
  10726. the file is a byte of value ASCII 26 (0x1A). 
  10727.  
  10728. Record layout is as you define in your application.  It must match the layout
  10729. as described in the field descriptors, byte-for-byte.
  10730.  
  10731. Increasing DBF Performance 
  10732.  
  10733. Records are stored in the order they were written.  To improve performance, 
  10734. especially indexed-sequential access, the data file may be sorted, or 
  10735. clustered, by reading each record in primary key order, then writing that 
  10736. record to a new DBF data file.  Repeat for each record.  After all records have 
  10737. been written, reindex the newly created DBF data file (and all related index 
  10738. files).  After this, delete the old files (data and index), and rename the new 
  10739. ones to the filenames required.  This technique maximizes cache efficiency, and 
  10740. can easily offer 10x performance increase in access speed. 
  10741.  
  10742.  
  10743. ΓòÉΓòÉΓòÉ 13.5. DBT File Format ΓòÉΓòÉΓòÉ
  10744.  
  10745. The DBT memo file is composed of a header followed by memo data, stored in one 
  10746. or more blocks.  The header layout is detailed below, followed by the memo 
  10747. record. 
  10748.  
  10749. DBT Header 
  10750.  
  10751. // nnn, where nnn is the offset of that item relative to the start of the file
  10752.  
  10753. ULONG memoAvailBlock;   // 000 next available block (header is block 0)
  10754. ULONG memoRez;          // 004 not used
  10755. CHAR  memoFilename[8];  // 008 filename proper (first 8 of filename proper)
  10756. ULONG memoRez2;         // 016 not used (apparently)
  10757. ULONG memoBlockSize;    // 020 block size, must be at least 24
  10758.  
  10759. // the rest of the header block (to block size bytes) is unused
  10760.  
  10761. Memo Record 
  10762.  
  10763. // nnn, where nnn is offset of item relative to the start of the memo record
  10764.  
  10765. ULONG memoAvail;        // 000 next available link
  10766. ULONG memoSize;         // 004 size of data (including this and memoAvail)
  10767. CHAR  memoData;         // 008 for as many bytes as memoSize, less 8
  10768.  
  10769. A memo may use one or more blocks (each block is a fixed size), but allocations 
  10770. are always contiguous.  Unused bytes after the memo data (to the end of the 
  10771. last block allocated to that memo record) are undefined.  memoAvail is 0x8FFFF 
  10772. for all active memo records.  For deleted memo records, memoAvail is used as a 
  10773. link in the memoAvail list.  memoSize is the total bytes used by the memo, 
  10774. including the memoAvail and memoSize data, so it is the size of the real data + 
  10775. 8 bytes. 
  10776.  
  10777. Removing Memo Records 
  10778.  
  10779. A similar technique to that above could be used to remove memo records from a 
  10780. DBT.  Instead of using PACK_RECORDS_XB, read each DBF record in sequence, and 
  10781. if not to be deleted, write that record to a new DBF data file and its memo 
  10782. records to a new DBT memo file.  If the record read is to be deleted, skip it 
  10783. (and its memo record(s)) and continue with the next DBF data record.  Repeat 
  10784. while more records. Delete or archive the old DBF/DBT pair, and use the new 
  10785. files in their place.  Reindex.  Also see compact.c on the distribution disk 
  10786. for an example technique of compacting a database. 
  10787.  
  10788.  
  10789. ΓòÉΓòÉΓòÉ 13.6. Custom Sort-Compare Function ΓòÉΓòÉΓòÉ
  10790.  
  10791. Bullet provides 10 custom sort-compare functions, in addition to the 6 
  10792. intrinsic sort-compare functions (ASCII, NLS, and the four integer compares). 
  10793. The custom function you supply is not actually a sort function, as the name 
  10794. implies, but a compare function.  Basically, two strings are supplied and your 
  10795. function determines string1's relation to string2 (<, >, or ==). 
  10796.  
  10797. The strings supplied (via pointers) are not C strings, and they are not 
  10798. (necessarily) 0-terminated.  A count value is passed, indicating the number of 
  10799. bytes to compare.  The handle of the index file for which this compare is being 
  10800. done is also supplied, so that you can interrogate the index file state 
  10801. (STAT_INDEX_XB) for any additional information required. 
  10802.  
  10803. In addition to the compare function this routine performs, a special-case call 
  10804. is made to this routine requesting a pointer to a string of HIGH-VALUES for 
  10805. this sort compare.  The pointer must be to a static memory area that exists for 
  10806. as long as the index file is open, and must be at least as long as count.  This 
  10807. special-case call is indicated by both string pointers==NULL. 
  10808.  
  10809. To use a custom sort-compare function, first use SET_SYSVARS_XB to assign the 
  10810. custom sort ID (10 to 19) with the function's address pointer.  Once assigned, 
  10811. an index file may be created with its CIP.sortFunction set to the sort ID 
  10812. (10-19).  Also, any previously created index file with a custom sort ID may now 
  10813. be opened (but only after you used SET_SYSVARS_XB to assign the sort-compare 
  10814. function pointer).  During the index file create, the sort ID you specified for 
  10815. the create is stored in the index file.  When that index file is later opened, 
  10816. that same sort ID is used, and so requires that the custom sort-compare 
  10817. function already be assigned (with SET_SYSVARS_XB) before opening the index 
  10818. file.  This means that you need to be consistent in your custom sort ID 
  10819. numbering, since each index created forever uses that sort ID you specified. 
  10820.  
  10821. It's simple to create a custom sort-compare function.  The calling convention 
  10822. is APIENTRY (DOSX32 = __cdecl, OS/2= _syscall, Win32=_stdcall), and the 
  10823. parameters are passed to your function on the stack (by Bullet).  A sample 
  10824. prototype for a custom sort-compare function follows: 
  10825.  
  10826. LONG APIENTRY YourCustomSort10(PVOID str1,
  10827.                                PVOID str2,
  10828.                                ULONG count,
  10829.                                ULONG handle);
  10830.  
  10831. If the pointers are not NULL, your routine is to compare str1 to str2, for 
  10832. count bytes, and is to return: 
  10833.  
  10834.         -1 if str1 is less than str2
  10835.          0 if str1 is equal to str2
  10836.          1 if str1 is greater than str2
  10837.  
  10838. str is not a C string, but is of type void.  Cast as required,
  10839. depending on the data expected.
  10840.  
  10841. If str1 and str2 are both NULL, your routine must return a pointer to a static 
  10842. object that contains HIGH-VALUES for the object type.  For example, if the 
  10843. sort-compare is for IEEE floating-point, then the function is to return a 
  10844. pointer to a static data area filled with the highest floating-point value. 
  10845. Depending on your sort-compare routine's functionality, you may need just a 
  10846. single high-value, or multiple high-values, one after the other (e.g., if you 
  10847. are supporting compound key values for binary keys).  The count parameter 
  10848. indicates the total bytes needed, so divide by the object size to get the 
  10849. number of objects required.  Be aware that the object size (in count) is +2 
  10850. bytes for the enumerator if DUPS_ALLOWED was specified when the index file was 
  10851. created.  This high-values object is used in the REINDEX_XB routine, and also 
  10852. the LAST_KEY_XB and GET_LAST_XB routines. 
  10853.  
  10854.  
  10855. ΓòÉΓòÉΓòÉ 13.7. Custom Build-Key ΓòÉΓòÉΓòÉ
  10856.  
  10857. Bullet provides an internal build-key routine that constructs the key from the 
  10858. data record supplied.  The internal routine can be overloaded by your custom 
  10859. build-key routine if you need additional functionality.  It may be used in 
  10860. conjunction with a custom sort-compare function, or an intrinsic Bullet 
  10861. sort-compare. 
  10862.  
  10863. Developing a custom build-key routine requires delving into the internal Bullet 
  10864. data structures.  It is more complicated than a custom sort-compare function, 
  10865. but not really any more complex.  The handle of the index file is passed, and 
  10866. using this, STAT_INDEX_XB is called to get the SIP.herePtr pointer.  This is 
  10867. the pointer to the internal Bullet data structure for this index file.  What 
  10868. needs to be accessed in this structure is the translated key expression.  From 
  10869. this, you have the starting offset in the data record, and the byte count to 
  10870. use, for each key component (up to 16 components per key).  The offset value as 
  10871. stored in the XLATEX structure does not include the tag field byte.  Therefore, 
  10872. to locate to the correct offset, add 1 to the value in offset.  For example, 
  10873. XLATEX.offset=0 means to use the first field, which is the first byte after the 
  10874. tag field byte, but the physical offset, as referenced to recPtr, is not at 
  10875. offset=0, but is at offset=1. 
  10876.  
  10877. This translated key expression structure is: 
  10878.  
  10879. // (This is an excerpt from the IX3 header format)
  10880.  
  10881. // Translated key expression as done by Parser during CreateIndex and Reindex.
  10882. // More details on this is in the Custom Expression Parser Specifications.
  10883. // For each key part in KH.expression a 4-byte structure is used:
  10884.  
  10885. typedef struct _XLATEX {
  10886.  CHAR  ftype;   // field type (C,N,L, etc.),if bit7=1 and C then do UPPER key
  10887.  CHAR  length;  // bytes to use starting at offset (never > 64)
  10888.  SHORT offset;  // byte offset into data record that length bytes are to be used
  10889. } XLATEX;       // (note that offset does not count tag field byte)
  10890.  
  10891. ULONG xlateCount;           // 044 number of key fields (64/4=16 max fields)
  10892. XLATEX xlateExpression[16]; // 048 key construct info (16 dword's worth)
  10893.  
  10894. xlateExpression is at offset +48 relative the IX3 index header.  However, 
  10895. SIP.herePtr points to -384 relative the IX3 index header start.  Therefore, to 
  10896. locate to xlateExpression, you must add 384 to 48.  This means that 
  10897. xlateExpression[0].ftype is located at SIP.herePtr+432.  The number of valid 
  10898. key components in xlateExpression is stored in xlateCount (at SIP.herePtr+428). 
  10899.  
  10900. The calling convention for your custom build-key function is APIENTRY (or 
  10901. _System, or __syscall for some compilers), and the parameters are passed to 
  10902. your function on the stack (by Bullet).  A sample prototype for a build-key 
  10903. function follows: 
  10904.  
  10905. ULONG APIENTRY YourBuildKey(ULONG handle,
  10906.                             PVOID recordPtr,
  10907.                             PVOID keyPtr,
  10908.                             PULONG keyLenPtr
  10909.                             PULONG sortFuncPtr);
  10910.  
  10911. Using the data from xlateExpression, you are to build a key from the data 
  10912. record located at the passed pointer, recordPtr, and are store the built key in 
  10913. the buffer located at keyPtr.  For each key component, you copy from the data 
  10914. record xlateExpression[].length bytes starting at xlateExpression[].offset+1 
  10915. (given the 1-byte tag field which is not accounted for otherwise), and build 
  10916. other key components after previously build parts.  If the index file allows 
  10917. duplicate keys (DUPS_ALLOWED is flagged in SIP.sortFunction), then append an 
  10918. enumerator to the end of the key proper.  The handle of the index file is 
  10919. passed, which is used when calling STAT_INDEX_XB (to get SIP.herePtr).  The 
  10920. return is 0 if successful, or an appropriate Bullet error code (EXB_) should be 
  10921. used.  In addition, the key length is placed in the ULONG data pointed to by 
  10922. keyLenPtr (SIP.keyLength may be used), and the sort-compare function is placed 
  10923. in the ULONG data pointed to by sortFuncPtr (SIP.sortFunction may be used). 
  10924.  
  10925. The routine is also to check if the tag field of the data record matches the 
  10926. skip-tag value, as set by SET_SYSVARS_XB.  If the tag field matches, 
  10927. WRN_SKIP_KEY is to be returned as the 'error' code.  The key is built 
  10928. regardless of a match. 
  10929.  
  10930.  
  10931. ΓòÉΓòÉΓòÉ 13.8. Custom Expression Parser ΓòÉΓòÉΓòÉ
  10932.  
  10933. Bullet provides an internal key expression parser routine that constructs the 
  10934. translated key expression stored in the index file header.  The internal 
  10935. routine can be overloaded by your custom expression parser routine if you need 
  10936. additional functionality.  It may be used in conjunction with a custom 
  10937. sort-compare function, with a custom build-key routine, or with an intrinsic 
  10938. Bullet sort-compare. 
  10939.  
  10940. Developing a custom expression parser routine requires delving into the 
  10941. internal Bullet data structures.  It is more complicated than a custom 
  10942. sort-compare function, and it is also much more complex.  Unlike the custom 
  10943. sort-compare and build-key functions, no handle is passed to the parser.  This 
  10944. is because, rather than using the handle to get the SIP.herePtr, this pointer 
  10945. is passed directly to this routine.  This is the pointer to the internal Bullet 
  10946. data structure for this index file.  What needs to be accessed in this 
  10947. structure is the translated key expression location, as well as the text 
  10948. version of the key expression, as supplied by the programmer/user.  To the 
  10949. XLATEX data you place the starting offset in the data record, and the byte 
  10950. count to use, for each key component you parse from the key expression (up to 
  10951. 16 components per key).  The offset value as stored in the XLATEX structure 
  10952. does not include the tag field byte.  Therefore, the correct offset to store is 
  10953. the physical offset within the record, minus 1.  For example, XLATEX.offset=0 
  10954. should be used for the offset of the first field, which is the first byte after 
  10955. the tag field byte.  For each component parsed, an XLATEX data structure is 
  10956. added to the xlateExpression data area (up to 16).  Unused XLATEX components 
  10957. must be set to 0.  When all components have been stored, the xlateCount value 
  10958. is set to the number of key components stored. 
  10959.  
  10960. DHDptr is the data header pointer.  It is -352 bytes relative the DBF data 
  10961. header.  However, rather than using absolute addressing to locate field 
  10962. descriptor data (needed for parsing), it's recommended that the DBF handle be 
  10963. obtained from the KHptr structure.  Since no file handles are passed, you must 
  10964. read the xbLink handle value from the index file header.  The xbLink handle is 
  10965. stored at KHptr+12.  With this handle, you call the GET_DESCRIPTOR_XB routine 
  10966. to obtain field descriptor info for each field. 
  10967.  
  10968. This translated key expression structure, and text expression location, is at: 
  10969.  
  10970. // (This is an excerpt from the IX3 header format)
  10971.  
  10972. // Translated key expression as done by Parser during CreateIndex and Reindex.
  10973. // For each key part in KH.expression a 4-byte structure is used:
  10974.  
  10975. typedef struct _XLATEX {
  10976.  CHAR  ftype;   // field type (C,N,L, etc.),if bit7=1 and C then do UPPER key
  10977.  CHAR  length;  // bytes to use starting at offset (never > 64)
  10978.  SHORT offset;  // byte offset into data record that length bytes are to be used
  10979. } XLATEX;       // (note that offset does not count tag field byte)
  10980.  
  10981. ULONG xlateCount;           // 044 number of key fields (64/4=16 max fields)
  10982. XLATEX xlateExpression[16]; // 048 key construct info (16 dword's worth)
  10983.       :                     // 112-347   :
  10984. CHAR  expression[160];      // 348 key expression, user (0-Terminated)
  10985.  
  10986. xlateExpression is at offset +48 relative the IX3 index header.  However, 
  10987. KHptr, passed to this routine, points to -384 relative the IX3 index header 
  10988. start.  Therefore, to locate to xlateExpression, you must add 384 to 48.  This 
  10989. means that xlateExpression[0].ftype is located at KHptr+432.  The number of 
  10990. valid key components in xlateExpression is stored in xlateCount (at KHptr+428). 
  10991. To text key expression string, which you are to parse, is located at KHptr+732. 
  10992. This is identical to the expression passed during CREATE_INDEX_XB (and it is 
  10993. CREATE_INDEX_XB that calls this parser routine). 
  10994.  
  10995. The calling convention for your custom key expression parser function is 
  10996. APIENTRY (or _System, or __syscall for some compilers), and the parameters are 
  10997. passed to your function on the stack (by Bullet).  A sample prototype for a 
  10998. build-key function follows: 
  10999.  
  11000. ULONG APIENTRY YourKeyExpressionParser(PVOID DHDptr,
  11001.                                        PVOID KHptr,
  11002.                                        PULONG keyLenPtr);
  11003.  
  11004. You are to parse the text key expression at KHptr+732 and store the key 
  11005. component XLATEX structure values to the XLATEX structure, one for each key 
  11006. component parsed.  In addition, the key length (the sum of the XLATEX.length 
  11007. fields) is placed in the ULONG data pointed to by keyLenPtr.  The keylength may 
  11008. not exceed 64 bytes.  If DUPS_ALLOWED is flagged, add two to the sum of the 
  11009. XLATEX.length fields for the enumerator word. 
  11010.  
  11011. Note:  The key expression has been mapped to upper-case and 0-filled by the 
  11012. time this routine is called. 
  11013.  
  11014. This is probably the most difficult part of customizing Bullet.  However, the 
  11015. difficulty lies not with Bullet, but how you parse.  The idea is simple -- you 
  11016. are to generate a xlateCount value, and for each key component (ie 
  11017. non-contiguous, non-same-type run in the data record), an XLATEX variable 
  11018. describing the method to build that key component out of the data record (type, 
  11019. length, and starting offset) is stored.  The text key expression is available 
  11020. in the index header, and the destination to write to is there, also.  You do 
  11021. need to read the index header at KHptr+12 (ULONG) to obtain the DBF handle for 
  11022. this index file before you can parse the expression.  This because you need to 
  11023. know about the record field names, types, and lengths before you can parse the 
  11024. key expression.  The matter not covered here is that of parsing the expression, 
  11025. which is left to the programmer.  Any lexical parser algorithm may be used, or 
  11026. you may even do no parsing at all, and simply hard-code values into the XLATEX 
  11027. structures. 
  11028.  
  11029. If you've gotten this far, you may find the following data structures useful. 
  11030. The numbers at // nnn are offsets relative the SIP.herePtr and SDP.herePtr 
  11031. pointers.  For example, at SIP.herePtr+352 is a ULONG of the number of key 
  11032. searches requested.  These could be monitored in a separate thread. 
  11033.  
  11034. Relative SIP.herePtr: 
  11035.  
  11036. ULONG fType;            // 000 bit0=0 for index file, btree
  11037. ULONG flags;            // 004 bit0=1 is dirty
  11038.                         //     bit1=1 full lock (count stored in KH.lockCount)
  11039.                         //     bit2=1 shared lock (if bit1=1)
  11040.                         //     bit3-14 reserved (=0)
  11041.                         //     bit15=1 no coalesce on key delete
  11042.                         // 006 BYTE, progress of reindex (0,1-99)
  11043.                         // 007 BYTE, 0
  11044. PVOID morePtr;          // 008 ptr to additional header info, if ever needed
  11045. ULONG xbLink;           // 012 related XB data file handle
  11046. ULONG asMode;           // 016 access-sharing-cache mode of open
  11047. CHAR  filename[260];    // 020 filename at open (0T)
  11048. ULONG currKeyRecNo;     // 280 current rec number assigned to KH.currKey
  11049. CHAR  currKey[64];      // 284 current key value
  11050. ULONG rez0;             // 348 allow for 0-terminated string
  11051. ULONG searches;         // 352 keys searched for
  11052. ULONG seeks;            // 356 nodes seeked
  11053. ULONG hits;             // 360 seeks satisfied without disk access
  11054. ULONG keysDeleted;      // 364 keys deleted since last zeroed
  11055. ULONG keysStored;       // 368 keys added since last zeroed
  11056. ULONG nodesSplit;       // 372 splits needed on insert since last zeroed
  11057. ULONG nodesMadeAvail;   // 376 nodes made available from deleting keys
  11058. ULONG lockCount;        // 380 active full-lock count
  11059.  
  11060. // the IX3 index header follows at 384+
  11061.  
  11062. Relative SDP.herePtr: 
  11063.  
  11064. ULONG fType;            // 000 bit0=1 for DBF data file, XB
  11065. ULONG flags;            // 004 bit0=1 is dirty
  11066.                         //     bit1=1 full lock
  11067.                         //     bit2=1 shared lock (if bit1=1)
  11068.                         //     bit3-15 reserved (=0)
  11069.                         // 006 BYTE, progress of pack (0,1-99)
  11070.                         // 007 BYTE, 0
  11071. PVOID morePtr;          // 008 ptr to additional header info, if ever needed
  11072. ULONG noFields;         // 012 number of fields in this data file
  11073. ULONG asMode;           // 016 access-sharing-cache mode of open
  11074. CHAR  filename[260];    // 020 filename at open (0T)
  11075. ULONG lockCount;        // 280 only when dec'ed to 0 do full unlock
  11076. ULONG memoAvailBlock;   // 284 next available block (header is block 0)
  11077. ULONG memoUnk1;         // 288 not used
  11078. CHAR  memoFilename[8];  // 292 filename proper (first 8 of filename proper)
  11079. ULONG memoUnk2;         // 300 not used (apparently)
  11080. ULONG memoBlockSize;    // 304 block size, must be at least 24 to cover header!
  11081. ULONG memoHandle;       // 308 handle of open memo file
  11082. ULONG memoFlags;        // 312 bit0=1 is dirty
  11083. ULONG memoLastNo;       // 316 last accessed memo number (if not 0)
  11084. ULONG memoLastLink;     // 320 link data for last accessed memo
  11085. ULONG memoLastSize;     // 324 size of last accessed memo (in bytes, w/OH)
  11086. ULONG align32[6];       // 328 (align to even32)
  11087.  
  11088. // the DBF data header follows at +352
  11089.  
  11090.  
  11091. ΓòÉΓòÉΓòÉ 14. Bullet Is... ΓòÉΓòÉΓòÉ
  11092.  
  11093. Bullet is a thread-safe, multi-process capable database engine toolkit for 
  11094. OS/2, DOSX32, and Win32.  It provides pre-built and tested access methods to 
  11095. data and index files for application programmers.  It is not an end-user 
  11096. Database Management System (DBMS), but it is a tool that could be used to 
  11097. develop one.  Bullet is compact, efficient, and very fast.  It can be 
  11098. configured to use custom key-build, sort-compare functions, and expression 
  11099. parser routines to extend the built-in functionality, and even 
  11100. programmer-supplied OS API calls to replace the internal ones. 
  11101.  
  11102. The standard data format is DBF (dBASE 3+ and later).  The supported memo 
  11103. format is DBT (dBASE 4 and later).  Index-only support can be enabled and with 
  11104. this any data file format may be used (the data maintained by the programmer 
  11105. then).  Also, the DBF standard may be extended by using binary field values and 
  11106. fields larger than 255 bytes.  Index files are NLS-compatible and use an 
  11107. efficient b-tree structure.  Files may be any size supported by the OS, up to 
  11108. 4GB.  Up to 1024 files may be opened and in use by any one process, with any 
  11109. number of processes active. 
  11110.  
  11111. The Bullet API consists of a wide assortment of routines, from low-level OS 
  11112. calls to high-level transaction-list routines that can process hundreds of 
  11113. files per transaction, with roll-back on error.  Network support is included, 
  11114. and makes use of operating system features such as atomic re-locking, and 
  11115. shared locks that allow other processes read-access to region-locked files. 
  11116.  
  11117. Bullet is simple to use, and may easily be modified by using function wrappers 
  11118. around groups of related Bullet routines.  If you don't like working with the 
  11119. parameter packs, use a wrapper and call as you like.  Bullet works the way you 
  11120. are used to working. 
  11121.  
  11122. This online manual is a complete introduction and programmer reference for 
  11123. Bullet.  Sample code is included, with more still on disk. 
  11124.  
  11125.  
  11126. ΓòÉΓòÉΓòÉ <hidden>  ΓòÉΓòÉΓòÉ
  11127.  
  11128. A foreign key is a column value in one table used as a primary key in a second 
  11129. table.  For example, if one table has two fields:  employee name (primary key) 
  11130. and department code, and a second table has two fields:  department code 
  11131. (primary key) and department name, then department code in the first table is 
  11132. considered a foreign key, since it may be used as the primary key value when 
  11133. searching the second table.  Joins and views make use of foreign keys. 
  11134.  
  11135.  
  11136. ΓòÉΓòÉΓòÉ <hidden>  ΓòÉΓòÉΓòÉ
  11137.  
  11138. Null pack
  11139.  
  11140.  
  11141. ΓòÉΓòÉΓòÉ <hidden>  ΓòÉΓòÉΓòÉ
  11142.  
  11143. typedef struct _ACCESSPACK {
  11144. ULONG func;
  11145. ULONG stat;
  11146. ULONG handle;         /* I, handle of Bullet file to access */
  11147. LONG  recNo;          /* IO, record number */
  11148. PVOID recPtr;         /* I, programmer's record buffer */
  11149. PVOID keyPtr;         /* I, programmer's key buffer */
  11150. PVOID nextPtr;        /* I, NULL if not xaction, else next AP in list */
  11151. } ACCESSPACK; /* AP */
  11152. typedef ACCESSPACK *PACCESSPACK;
  11153.  
  11154.  
  11155. ΓòÉΓòÉΓòÉ <hidden>  ΓòÉΓòÉΓòÉ
  11156.  
  11157. typedef struct _CALLBACKPACK {
  11158. ULONG sizeIs;         /* structure size (current 16 bytes) */
  11159. ULONG callMode;       /* 0=from reindex; 1=from DBF pack */
  11160. ULONG handle;         /* file handle */
  11161. ULONG data1;          /* for callMode=0/1: progress percent (1-99,0) */
  11162. } CALLBACKPACK; /* CBP */
  11163. typedef CALLBACKPACK *PCALLBACKPACK;
  11164.  
  11165.  
  11166. ΓòÉΓòÉΓòÉ <hidden>  ΓòÉΓòÉΓòÉ
  11167.  
  11168. typedef struct _COPYPACK {
  11169. ULONG func;
  11170. ULONG stat;
  11171. ULONG handle;         /* I, handle of Bullet file to copy */
  11172. PSZ   filenamePtr;    /* I, filename to use (drv:path must exist if used) */
  11173. } COPYPACK; /* CP */
  11174. typedef COPYPACK *PCOPYPACK;
  11175.  
  11176.  
  11177. ΓòÉΓòÉΓòÉ <hidden>  ΓòÉΓòÉΓòÉ
  11178.  
  11179. typedef struct _CREATEDATAPACK {
  11180. ULONG func;
  11181. ULONG stat;
  11182. PSZ   filenamePtr;    /* I, filename to use */
  11183. ULONG noFields;       /* I, 1 to 254 */
  11184. PFIELDDESCTYPE fieldListPtr;   /* I, descriptor list, 1 per field */
  11185. ULONG fileID;         /* I, 0x03 for std DBF, 0x8B for DBF+DBT */
  11186. } CREATEDATAPACK; /* CDP */
  11187. typedef CREATEDATAPACK *PCREATEDATAPACK;
  11188.  
  11189.  
  11190. ΓòÉΓòÉΓòÉ <hidden>  ΓòÉΓòÉΓòÉ
  11191.  
  11192. typedef struct _CREATEINDEXPACK {
  11193. ULONG func;
  11194. ULONG stat;
  11195. PSZ   filenamePtr;    /* I, filename to use */
  11196. PSZ   keyExpPtr;      /* I, e.g., "SUBSTR(LNAME,1,4)+SSN" */
  11197. LONG  xbLink;         /* I, opened data file handle this indexes */
  11198. ULONG sortFunction;   /* I, 1-9 system, 10-19 custom */
  11199. ULONG codePage;       /* I, 0=use process default */
  11200. ULONG countryCode;    /* I, 0=use process default */
  11201. PVOID collatePtr;     /* I, NULL=use cc/cp else use passed table for sort */
  11202. ULONG nodeSize;       /* I, 512, 1024, or 2048 */
  11203. } CREATEINDEXPACK; /* CIP */
  11204. typedef CREATEINDEXPACK *PCREATEINDEXPACK;
  11205.  
  11206.  
  11207. ΓòÉΓòÉΓòÉ <hidden>  ΓòÉΓòÉΓòÉ
  11208.  
  11209. typedef struct _FIELDDESCTYPE {
  11210. BYTE  fieldName[11];  /* IO, upper A-Z and _; 1-10 chars, 0-filled, 0-term */
  11211. BYTE  fieldType;      /* IO, C,D,L,N, or M */
  11212. LONG  fieldDA;        /* x, offset within record (run-time storage option) */
  11213. BYTE  fieldLen;       /* IO, C=1-255,D=8,L=1,N=1-19,M=10 */
  11214. BYTE  fieldDC;        /* IO, fieldType=N then 0-15 else 0 */
  11215. USHORT altFieldLength;/* IO, 0 */
  11216. BYTE  filler[12];     /* I, 0 */
  11217. } FIELDDESCTYPE; /* nested in DESCRIPTORPACK */
  11218. typedef FIELDDESCTYPE *PFIELDDESCTYPE;
  11219.  
  11220.  
  11221. ΓòÉΓòÉΓòÉ <hidden>  ΓòÉΓòÉΓòÉ
  11222.  
  11223. typedef struct _DESCRIPTORPACK {
  11224. ULONG func;
  11225. ULONG stat;
  11226. ULONG handle;         /* I, handle of DBF file */
  11227. ULONG fieldNumber;    /* IO, first field is 1 */
  11228. ULONG fieldOffset;    /* O, offset of field within record (delete tag=offset 0) */
  11229. FIELDDESCTYPE FD;  /* IO FD.fieldName, O rest of FD */
  11230. } DESCRIPTORPACK; /* DP */
  11231. typedef DESCRIPTORPACK *PDESCRIPTORPACK;
  11232. };
  11233.  
  11234.  
  11235. ΓòÉΓòÉΓòÉ <hidden>  ΓòÉΓòÉΓòÉ
  11236.  
  11237. typedef struct _DOSFILEPACK {
  11238. ULONG func;
  11239. ULONG stat;
  11240. PSZ   filenamePtr;    /* I, filename to use */
  11241. ULONG handle;         /* IO, handle of open file */
  11242. ULONG asMode;         /* I, access-sharing-cache mode */
  11243. ULONG bytes;          /* IO, bytes to read, write, length of */
  11244. LONG  seekTo;         /* IO, seek to offset, current offset */
  11245. ULONG method;         /* I, seek method (0=start of file, 1=current, 2=end) */
  11246. PVOID bufferPtr;      /* I, buffer to read into or write from */
  11247. ULONG attr;           /* I, attribute to create file with */
  11248. PSZ   newNamePtr;     /* I, name to use on rename */
  11249. } DOSFILEPACK; /* DFP */
  11250. typedef DOSFILEPACK *PDOSFILEPACK;
  11251.  
  11252.  
  11253. ΓòÉΓòÉΓòÉ <hidden>  ΓòÉΓòÉΓòÉ
  11254.  
  11255. typedef struct _EXITPACK {
  11256. ULONG func;
  11257. ULONG stat;
  11258. } EXITPACK; /* EP */
  11259. typedef EXITPACK *PEXITPACK;
  11260.  
  11261.  
  11262. ΓòÉΓòÉΓòÉ <hidden>  ΓòÉΓòÉΓòÉ
  11263.  
  11264. typedef struct _HANDLEPACK {
  11265. ULONG func;
  11266. ULONG stat;
  11267. ULONG handle;         /* I, handle of Bullet file */
  11268. } HANDLEPACK; /* HP */
  11269. typedef HANDLEPACK *PHANDLEPACK;
  11270.  
  11271.  
  11272. ΓòÉΓòÉΓòÉ <hidden>  ΓòÉΓòÉΓòÉ
  11273.  
  11274. typedef struct _INITPACK {
  11275. ULONG func;
  11276. ULONG stat;
  11277. ULONG JFTsize;        /* I, max opened files (20-1024+) */
  11278. ULONG versionDOS;     /* O, e.g., 230 for 2.30 */
  11279. ULONG versionBullet;  /* O, e.g., 2019 for 2.019 */
  11280. ULONG versionOS;      /* O, e.g., 4=OS/2 32-bit */
  11281. PVOID exitPtr;        /* O, function pointer to EXIT_XB routine */
  11282. } INITPACK; /* IP */
  11283. typedef INITPACK *PINITPACK;
  11284.  
  11285.  
  11286. ΓòÉΓòÉΓòÉ <hidden>  ΓòÉΓòÉΓòÉ
  11287.  
  11288. typedef struct _LOCKPACK {
  11289. ULONG func;
  11290. ULONG stat;
  11291. ULONG handle;         /* I, handle of Bullet file to lock */
  11292. ULONG xlMode;         /* I, index lock mode (0=exclusive, 1=shared) */
  11293. ULONG dlMode;         /* I, data lock mode (0=exclusive, 1=shared) */
  11294. LONG  recStart;       /* I, if data, first record # to lock, or 0 for all */
  11295. ULONG recCount;       /* I, if data and recStart!=0, # records to lock */
  11296. PVOID nextPtr;        /* I, NULL if not xaction, else next LP in list */
  11297. } LOCKPACK; /* LP */
  11298. typedef LOCKPACK *PLOCKPACK;
  11299.  
  11300.  
  11301. ΓòÉΓòÉΓòÉ <hidden>  ΓòÉΓòÉΓòÉ
  11302.  
  11303. typedef struct _MEMODATAPACK {
  11304. ULONG func;
  11305. ULONG stat;
  11306. ULONG dbfHandle;      /* I, handle of DBF file to which this memo file belongs */
  11307. ULONG memoBypass;     /* I, memo bypass function to do, if any */
  11308. PVOID memoPtr;        /* I, ptr to memo record buffer */
  11309. ULONG memoNo;         /* IO, memo record number (aka block number) */
  11310. ULONG memoOffset;     /* I, position within record to start read/update */
  11311. ULONG memoBytes;      /* IO, number of bytes to read/update */
  11312. } MEMODATAPACK; /* MDP */
  11313. typedef MEMODATAPACK *PMEMODATAPACK;
  11314.  
  11315.  
  11316. ΓòÉΓòÉΓòÉ <hidden>  ΓòÉΓòÉΓòÉ
  11317.  
  11318. typedef struct _MEMORYPACK {
  11319. ULONG func;
  11320. ULONG stat;
  11321. ULONG memory;         /* O, not used in OS/2 */
  11322. } MEMORYPACK; /* MP */
  11323. typedef MEMORYPACK *PMEMORYPACK;
  11324.  
  11325.  
  11326. ΓòÉΓòÉΓòÉ <hidden>  ΓòÉΓòÉΓòÉ
  11327.  
  11328. typedef struct _OPENPACK {
  11329. ULONG func;
  11330. ULONG stat;
  11331. ULONG handle;         /* O, handle of file opened */
  11332. PSZ   filenamePtr;    /* I, Bullet file to open */
  11333. ULONG asMode;         /* I, access-sharing-cache mode */
  11334. LONG  xbLink;         /* I, if index open, xbLink=handle of its opened DBF */
  11335. } OPENPACK; /* OP */
  11336. typedef OPENPACK *POPENPACK;
  11337.  
  11338.  
  11339. ΓòÉΓòÉΓòÉ <hidden>  ΓòÉΓòÉΓòÉ
  11340.  
  11341. typedef struct _QUERYSETPACK {
  11342. ULONG func;
  11343. ULONG stat;
  11344. ULONG item;           /* I, Bullet sysvar item to get/set */
  11345. ULONG itemValue;      /* IO, current/new value */
  11346. } QUERYSETPACK; /* QSP */
  11347. typedef QUERYSETPACK *PQUERYSETPACK;
  11348.  
  11349.  
  11350. ΓòÉΓòÉΓòÉ <hidden>  ΓòÉΓòÉΓòÉ
  11351.  
  11352. typedef struct _REMOTEPACK {
  11353. ULONG func;
  11354. ULONG stat;
  11355. ULONG handle;         /* I, handle of file, or if 0, use RP.drive */
  11356. ULONG drive;          /* I, drive (1=A,2=B,3=C,...0=current) to check */
  11357. ULONG isRemote;       /* O, =1 of handle/drive is remote, =0 if local */
  11358. ULONG flags;          /* O, 0 */
  11359. ULONG isShare;        /* O, 1 */
  11360. } REMOTEPACK; /* RP */
  11361. typedef REMOTEPACK *PREMOTEPACK;
  11362.  
  11363.  
  11364. ΓòÉΓòÉΓòÉ <hidden>  ΓòÉΓòÉΓòÉ
  11365.  
  11366. struct StatDataPack {
  11367. typedef struct _STATDATAPACK {
  11368. ULONG func;
  11369. ULONG stat;
  11370. ULONG handle;         /* I, handle to check */
  11371. ULONG fileType;       /* O, bit0=1 data file */
  11372. ULONG flags;          /* O, bit0=1 dirty, bit1=1 full-lock, bit2=1 shared */
  11373. ULONG progress;       /* O, 0,1-99% pack progress */
  11374. PVOID morePtr;        /* O, 0 */
  11375. ULONG fields;         /* O, fields per record */
  11376. ULONG asMode;         /* O, access-sharing-cache mode */
  11377. PSZ   filenamePtr;    /* O, filename used in open */
  11378. ULONG fileID;         /* O, first byte of DBF file */
  11379. ULONG lastUpdate;     /* O, high word=year,low byte=day, high byte=month */
  11380. ULONG records;        /* O, data records (including "deleted") */
  11381. ULONG recordLength;   /* O, record length */
  11382. ULONG xactionFlag;    /* O, 0 */
  11383. ULONG encryptFlag;    /* O, 0 */
  11384. PVOID herePtr;        /* O, this file's control address */
  11385. ULONG memoHandle;     /* O, handle of open memo file (0 if none) */
  11386. ULONG memoBlockSize;  /* O, memo file block size */
  11387. ULONG memoFlags;      /* O, bit0=1 dirty */
  11388. ULONG memoLastRecord; /* O, last accessed memo record (0 if none) */
  11389. ULONG memoLastSize;   /* O, size of last accessed memo record (in bytes, +8) */
  11390. ULONG lockCount;      /* O, number of full-locks in force */
  11391. } STATDATAPACK; /* SDP */
  11392. typedef STATDATAPACK *PSTATDATAPACK;
  11393.  
  11394.  
  11395. ΓòÉΓòÉΓòÉ <hidden>  ΓòÉΓòÉΓòÉ
  11396.  
  11397. typedef struct _STATHANDLEPACK {
  11398. ULONG func;
  11399. ULONG stat;
  11400. ULONG handle;         /* I, handle to check */
  11401. LONG  ID;             /* O, bit0=1 data file, bit0=1 index file */
  11402. } STATHANDLEPACK; /* SHP */
  11403. typedef STATHANDLEPACK *PSTATHANDLEPACK;
  11404.  
  11405.  
  11406. ΓòÉΓòÉΓòÉ <hidden>  ΓòÉΓòÉΓòÉ
  11407.  
  11408. typedef struct _STATINDEXPACK {
  11409. ULONG func;
  11410. ULONG stat;
  11411. ULONG handle;         /* I, handle to check */
  11412. ULONG fileType;       /* O, bit0=0 index file */
  11413. ULONG flags;          /* O, bit0=1 dirty, bit1=1 full-lock, bit2=1 shared */
  11414. ULONG progress;       /* O, 0,1-99% reindex progress */
  11415. PVOID morePtr;        /* O, 0 */
  11416. ULONG xbLink;         /* O, XB file link handle */
  11417. ULONG asMode;         /* O, access-sharing-cache mode */
  11418. PSZ   filenamePtr;    /* O, pointer to filename used in open */
  11419. ULONG fileID;         /* O, "31ch" */
  11420. PSZ   keyExpPtr;      /* O, pointer to key expression */
  11421. ULONG keys;           /* O, keys in file */
  11422. ULONG keyLength;      /* O, key length */
  11423. ULONG keyRecNo;       /* O, record number of current key */
  11424. PVOID keyPtr;         /* O, ptr to current key value (valid to keyLength) */
  11425. PVOID herePtr;        /* O, this file's control address */
  11426. ULONG codePage;       /* O, code page at create time */
  11427. ULONG countryCode;    /* O, country code at create time */
  11428. PVOID CTptr;          /* O, collate table ptr, NULL=no collate table present */
  11429. ULONG nodeSize;       /* O, node size */
  11430. ULONG sortFunction;   /* O, sort function ID */
  11431. ULONG lockCount;      /* O, number of full-locks in force */
  11432. } STATINDEXPACK; /* SIP */
  11433. typedef STATINDEXPACK *PSTATINDEXPACK;
  11434.  
  11435.  
  11436. ΓòÉΓòÉΓòÉ <hidden>  ΓòÉΓòÉΓòÉ
  11437.  
  11438. typedef struct _XERRORPACK {
  11439. ULONG func;
  11440. ULONG stat;           /* I, error to check */
  11441. ULONG errClass;       /* O, class of error */
  11442. ULONG action;         /* O, action recommended for error */
  11443. ULONG location;       /* O, location of error */
  11444. } XERRORPACK; /* XEP */
  11445. typedef XERRORPACK *PXERRORPACK;
  11446.  
  11447.  
  11448. ΓòÉΓòÉΓòÉ <hidden>  ΓòÉΓòÉΓòÉ
  11449.  
  11450.  Access-Mode (required)
  11451.   READONLY        0x00000000   read-only access open
  11452.   WRITEONLY       0x00000001   write-only access open
  11453.   READWRITE       0x00000002   read/write access open
  11454.  
  11455.  Share-Mode (required)
  11456.   DENYREADWRITE   0x00000010   no other process may share file
  11457.   DENYWRITE       0x00000020   no other process may share file for write
  11458.   DENYREAD        0x00000030   no other process may share file for read
  11459.   DENYNONE        0x00000040   any process may share file
  11460.  
  11461.  Inherit
  11462.   NOINHERIT       0x00000080   child process does not inherit file handles
  11463.  
  11464.  Cache
  11465.   NO_LOCALITY     0x00000000  locality is not known
  11466.   SEQ_LOCALITY    0x00010000  access will be mainly sequential
  11467.   RND_LOCALITY    0x00020000  access will be mainly random
  11468.   MIX_LOCALITY    0x00030000  access will be random with some sequential
  11469.   SKIP_CACHE      0x00100000  I/O is not to go through the cache
  11470.   WRITE_THROUGH   0x00400000  control returns only after disk is written to
  11471.  
  11472. Access- and Share-Mode values not explicitly listed are not valid.  The file 
  11473. access mode is a combination of ACCESS + SHARE + INHERIT + CACHE.  Typical data 
  11474. and index asMode is 0x00000042, though locality may be set accordingly (e.g., 
  11475. 0x00020042 for mostly random access to the file).  All values are mapped to the 
  11476. appropriate flags for the OS being used. 
  11477.  
  11478. The Cache mode options are valid for OPEN_DATA_XB and OPEN_INDEX_XB only; for 
  11479. OPEN_FILE_DOS, the Cache values must be right-shifted by 8.  The 'Skip Cache' 
  11480. and 'Write Through' options are not inherited. 
  11481.  
  11482.  
  11483. ΓòÉΓòÉΓòÉ <hidden>  ΓòÉΓòÉΓòÉ
  11484.  
  11485. The enumerator is a big-endian 16-bit value that serves to differentiate up to 
  11486. 65536 "identical", non- unique keys.  It is attached to all keys of 
  11487. DUPS_ALLOWED flagged index files (set at CREATE_INDEX_XB), and occupies the 
  11488. last two bytes of the key.  The first key of the type uses \0\0, the second 
  11489. uses \0\1, and so on.  This ordering of bytes is the reverse of x86 Intel 
  11490. words, which uses little-endian format. 
  11491.  
  11492.  
  11493. ΓòÉΓòÉΓòÉ <hidden>  ΓòÉΓòÉΓòÉ
  11494.  
  11495. HIGH-VALUES signifies a sort value that is the highest possible (sorts last). 
  11496. HIGH-VALUES for a character key would be 0xFF for each byte, or the 256th byte 
  11497. of the collate-sequence table if an NLS sort (which is 0xFF also).  For 16-bit 
  11498. signed integer values, 0x7FFF is the highest.  And so on...